juju-core_1.18.1/0000755000015300001610000000000012321736016013443 5ustar jenkinsjenkinsjuju-core_1.18.1/src/0000755000015300001610000000000012321735653014240 5ustar jenkinsjenkinsjuju-core_1.18.1/src/labix.org/0000755000015300001610000000000012321735653016125 5ustar jenkinsjenkinsjuju-core_1.18.1/src/labix.org/v2/0000755000015300001610000000000012321735656016457 5ustar jenkinsjenkinsjuju-core_1.18.1/src/labix.org/v2/mgo/0000755000015300001610000000000012321736015017227 5ustar jenkinsjenkinsjuju-core_1.18.1/src/labix.org/v2/mgo/stats.go0000644000015300001610000000656212321735660020732 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo import ( "sync" ) var stats *Stats var statsMutex sync.Mutex func SetStats(enabled bool) { statsMutex.Lock() if enabled { if stats == nil { stats = &Stats{} } } else { stats = nil } statsMutex.Unlock() } func GetStats() (snapshot Stats) { statsMutex.Lock() snapshot = *stats statsMutex.Unlock() return } func ResetStats() { statsMutex.Lock() debug("Resetting stats") old := stats stats = &Stats{} // These are absolute values: stats.Clusters = old.Clusters stats.SocketsInUse = old.SocketsInUse stats.SocketsAlive = old.SocketsAlive stats.SocketRefs = old.SocketRefs statsMutex.Unlock() return } type Stats struct { Clusters int MasterConns int SlaveConns int SentOps int ReceivedOps int ReceivedDocs int SocketsAlive int SocketsInUse int SocketRefs int } func (stats *Stats) cluster(delta int) { if stats != nil { statsMutex.Lock() stats.Clusters += delta statsMutex.Unlock() } } func (stats *Stats) conn(delta int, master bool) { if stats != nil { statsMutex.Lock() if master { stats.MasterConns += delta } else { stats.SlaveConns += delta } statsMutex.Unlock() } } func (stats *Stats) sentOps(delta int) { if stats != nil { statsMutex.Lock() stats.SentOps += delta statsMutex.Unlock() } } func (stats *Stats) receivedOps(delta int) { if stats != nil { statsMutex.Lock() stats.ReceivedOps += delta statsMutex.Unlock() } } func (stats *Stats) receivedDocs(delta int) { if stats != nil { statsMutex.Lock() stats.ReceivedDocs += delta statsMutex.Unlock() } } func (stats *Stats) socketsInUse(delta int) { if stats != nil { statsMutex.Lock() stats.SocketsInUse += delta statsMutex.Unlock() } } func (stats *Stats) socketsAlive(delta int) { if stats != nil { statsMutex.Lock() stats.SocketsAlive += delta statsMutex.Unlock() } } func (stats *Stats) socketRefs(delta int) { if stats != nil { statsMutex.Lock() stats.SocketRefs += delta statsMutex.Unlock() } } juju-core_1.18.1/src/labix.org/v2/mgo/queue_test.go0000644000015300001610000000550712321735660021755 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo import ( "launchpad.net/gocheck" ) type QS struct{} var _ = gocheck.Suite(&QS{}) func (s *QS) TestSequentialGrowth(c *gocheck.C) { q := queue{} n := 2048 for i := 0; i != n; i++ { q.Push(i) } for i := 0; i != n; i++ { c.Assert(q.Pop(), gocheck.Equals, i) } } var queueTestLists = [][]int{ // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, // {8, 9, 10, 11, ... 2, 3, 4, 5, 6, 7} {0, 1, 2, 3, 4, 5, 6, 7, -1, -1, 8, 9, 10, 11}, // {8, 9, 10, 11, ... 2, 3, 4, 5, 6, 7} {0, 1, 2, 3, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11}, // {0, 1, 2, 3, 4, 5, 6, 7, 8} {0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8}, } func (s *QS) TestQueueTestLists(c *gocheck.C) { test := []int{} testi := 0 reset := func() { test = test[0:0] testi = 0 } push := func(i int) { test = append(test, i) } pop := func() (i int) { if testi == len(test) { return -1 } i = test[testi] testi++ return } for _, list := range queueTestLists { reset() q := queue{} for _, n := range list { if n == -1 { c.Assert(q.Pop(), gocheck.Equals, pop(), gocheck.Commentf("With list %#v", list)) } else { q.Push(n) push(n) } } for n := pop(); n != -1; n = pop() { c.Assert(q.Pop(), gocheck.Equals, n, gocheck.Commentf("With list %#v", list)) } c.Assert(q.Pop(), gocheck.Equals, nil, gocheck.Commentf("With list %#v", list)) } } juju-core_1.18.1/src/labix.org/v2/mgo/queue.go0000644000015300001610000000544612321735660020720 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo type queue struct { elems []interface{} nelems, popi, pushi int } func (q *queue) Len() int { return q.nelems } func (q *queue) Push(elem interface{}) { //debugf("Pushing(pushi=%d popi=%d cap=%d): %#v\n", // q.pushi, q.popi, len(q.elems), elem) if q.nelems == len(q.elems) { q.expand() } q.elems[q.pushi] = elem q.nelems++ q.pushi = (q.pushi + 1) % len(q.elems) //debugf(" Pushed(pushi=%d popi=%d cap=%d): %#v\n", // q.pushi, q.popi, len(q.elems), elem) } func (q *queue) Pop() (elem interface{}) { //debugf("Popping(pushi=%d popi=%d cap=%d)\n", // q.pushi, q.popi, len(q.elems)) if q.nelems == 0 { return nil } elem = q.elems[q.popi] q.elems[q.popi] = nil // Help GC. q.nelems-- q.popi = (q.popi + 1) % len(q.elems) //debugf(" Popped(pushi=%d popi=%d cap=%d): %#v\n", // q.pushi, q.popi, len(q.elems), elem) return elem } func (q *queue) expand() { curcap := len(q.elems) var newcap int if curcap == 0 { newcap = 8 } else if curcap < 1024 { newcap = curcap * 2 } else { newcap = curcap + (curcap / 4) } elems := make([]interface{}, newcap) if q.popi == 0 { copy(elems, q.elems) q.pushi = curcap } else { newpopi := newcap - (curcap - q.popi) copy(elems, q.elems[:q.popi]) copy(elems[newpopi:], q.elems[q.popi:]) q.popi = newpopi } for i := range q.elems { q.elems[i] = nil // Help GC. } q.elems = elems } juju-core_1.18.1/src/labix.org/v2/mgo/LICENSE0000644000015300001610000000252412321735660020244 0ustar jenkinsjenkinsmgo - MongoDB driver for Go Copyright (c) 2010-2013 - Gustavo Niemeyer All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. juju-core_1.18.1/src/labix.org/v2/mgo/export_test.go0000644000015300001610000000107612321735660022147 0ustar jenkinsjenkinspackage mgo import ( "time" ) func HackSocketsPerServer(newLimit int) (restore func()) { oldLimit := newLimit restore = func() { socketsPerServer = oldLimit } socketsPerServer = newLimit return } func HackPingDelay(newDelay time.Duration) (restore func()) { oldDelay := pingDelay restore = func() { pingDelay = oldDelay } pingDelay = newDelay return } func HackSyncSocketTimeout(newTimeout time.Duration) (restore func()) { oldTimeout := syncSocketTimeout restore = func() { syncSocketTimeout = oldTimeout } syncSocketTimeout = newTimeout return } juju-core_1.18.1/src/labix.org/v2/mgo/log.go0000644000015300001610000000557212321735660020355 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo import "fmt" // --------------------------------------------------------------------------- // Logging integration. // Avoid importing the log type information unnecessarily. There's a small cost // associated with using an interface rather than the type. Depending on how // often the logger is plugged in, it would be worth using the type instead. type log_Logger interface { Output(calldepth int, s string) error } var globalLogger log_Logger var globalDebug bool // Specify the *log.Logger object where log messages should be sent to. func SetLogger(logger log_Logger) { globalLogger = logger } // Enable the delivery of debug messages to the logger. Only meaningful // if a logger is also set. func SetDebug(debug bool) { globalDebug = debug } func log(v ...interface{}) { if globalLogger != nil { globalLogger.Output(2, fmt.Sprint(v...)) } } func logln(v ...interface{}) { if globalLogger != nil { globalLogger.Output(2, fmt.Sprintln(v...)) } } func logf(format string, v ...interface{}) { if globalLogger != nil { globalLogger.Output(2, fmt.Sprintf(format, v...)) } } func debug(v ...interface{}) { if globalDebug && globalLogger != nil { globalLogger.Output(2, fmt.Sprint(v...)) } } func debugln(v ...interface{}) { if globalDebug && globalLogger != nil { globalLogger.Output(2, fmt.Sprintln(v...)) } } func debugf(format string, v ...interface{}) { if globalDebug && globalLogger != nil { globalLogger.Output(2, fmt.Sprintf(format, v...)) } } juju-core_1.18.1/src/labix.org/v2/mgo/cluster_test.go0000644000015300001610000011537012321736015022305 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo_test import ( "fmt" "io" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" . "launchpad.net/gocheck" "net" "strings" "sync" "time" ) func (s *S) TestNewSession(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() // Do a dummy operation to wait for connection. coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"_id": 1}) c.Assert(err, IsNil) // Tweak safety and query settings to ensure other has copied those. session.SetSafe(nil) session.SetBatch(-1) other := session.New() defer other.Close() session.SetSafe(&mgo.Safe{}) // Clone was copied while session was unsafe, so no errors. otherColl := other.DB("mydb").C("mycoll") err = otherColl.Insert(M{"_id": 1}) c.Assert(err, IsNil) // Original session was made safe again. err = coll.Insert(M{"_id": 1}) c.Assert(err, NotNil) // With New(), each session has its own socket now. stats := mgo.GetStats() c.Assert(stats.MasterConns, Equals, 2) c.Assert(stats.SocketsInUse, Equals, 2) // Ensure query parameters were cloned. err = otherColl.Insert(M{"_id": 2}) c.Assert(err, IsNil) // Ping the database to ensure the nonce has been received already. c.Assert(other.Ping(), IsNil) mgo.ResetStats() iter := otherColl.Find(M{}).Iter() c.Assert(err, IsNil) m := M{} ok := iter.Next(m) c.Assert(ok, Equals, true) err = iter.Close() c.Assert(err, IsNil) // If Batch(-1) is in effect, a single document must have been received. stats = mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, 1) } func (s *S) TestCloneSession(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() // Do a dummy operation to wait for connection. coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"_id": 1}) c.Assert(err, IsNil) // Tweak safety and query settings to ensure clone is copying those. session.SetSafe(nil) session.SetBatch(-1) clone := session.Clone() defer clone.Close() session.SetSafe(&mgo.Safe{}) // Clone was copied while session was unsafe, so no errors. cloneColl := clone.DB("mydb").C("mycoll") err = cloneColl.Insert(M{"_id": 1}) c.Assert(err, IsNil) // Original session was made safe again. err = coll.Insert(M{"_id": 1}) c.Assert(err, NotNil) // With Clone(), same socket is shared between sessions now. stats := mgo.GetStats() c.Assert(stats.SocketsInUse, Equals, 1) c.Assert(stats.SocketRefs, Equals, 2) // Refreshing one of them should let the original socket go, // while preserving the safety settings. clone.Refresh() err = cloneColl.Insert(M{"_id": 1}) c.Assert(err, IsNil) // Must have used another connection now. stats = mgo.GetStats() c.Assert(stats.SocketsInUse, Equals, 2) c.Assert(stats.SocketRefs, Equals, 2) // Ensure query parameters were cloned. err = cloneColl.Insert(M{"_id": 2}) c.Assert(err, IsNil) // Ping the database to ensure the nonce has been received already. c.Assert(clone.Ping(), IsNil) mgo.ResetStats() iter := cloneColl.Find(M{}).Iter() c.Assert(err, IsNil) m := M{} ok := iter.Next(m) c.Assert(ok, Equals, true) err = iter.Close() c.Assert(err, IsNil) // If Batch(-1) is in effect, a single document must have been received. stats = mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, 1) } func (s *S) TestSetModeStrong(c *C) { session, err := mgo.Dial("localhost:40012") c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Monotonic, false) session.SetMode(mgo.Strong, false) c.Assert(session.Mode(), Equals, mgo.Strong) result := M{} cmd := session.DB("admin").C("$cmd") err = cmd.Find(M{"ismaster": 1}).One(&result) c.Assert(err, IsNil) c.Assert(result["ismaster"], Equals, true) coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) // Wait since the sync also uses sockets. for len(session.LiveServers()) != 3 { c.Log("Waiting for cluster sync to finish...") time.Sleep(5e8) } stats := mgo.GetStats() c.Assert(stats.MasterConns, Equals, 1) c.Assert(stats.SlaveConns, Equals, 2) c.Assert(stats.SocketsInUse, Equals, 1) session.SetMode(mgo.Strong, true) stats = mgo.GetStats() c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestSetModeMonotonic(c *C) { // Must necessarily connect to a slave, otherwise the // master connection will be available first. session, err := mgo.Dial("localhost:40012") c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Monotonic, false) c.Assert(session.Mode(), Equals, mgo.Monotonic) result := M{} cmd := session.DB("admin").C("$cmd") err = cmd.Find(M{"ismaster": 1}).One(&result) c.Assert(err, IsNil) c.Assert(result["ismaster"], Equals, false) coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) result = M{} err = cmd.Find(M{"ismaster": 1}).One(&result) c.Assert(err, IsNil) c.Assert(result["ismaster"], Equals, true) // Wait since the sync also uses sockets. for len(session.LiveServers()) != 3 { c.Log("Waiting for cluster sync to finish...") time.Sleep(5e8) } stats := mgo.GetStats() c.Assert(stats.MasterConns, Equals, 1) c.Assert(stats.SlaveConns, Equals, 2) c.Assert(stats.SocketsInUse, Equals, 2) session.SetMode(mgo.Monotonic, true) stats = mgo.GetStats() c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestSetModeMonotonicAfterStrong(c *C) { // Test that a strong session shifting to a monotonic // one preserves the socket untouched. session, err := mgo.Dial("localhost:40012") c.Assert(err, IsNil) defer session.Close() // Insert something to force a connection to the master. coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) session.SetMode(mgo.Monotonic, false) // Wait since the sync also uses sockets. for len(session.LiveServers()) != 3 { c.Log("Waiting for cluster sync to finish...") time.Sleep(5e8) } // Master socket should still be reserved. stats := mgo.GetStats() c.Assert(stats.SocketsInUse, Equals, 1) // Confirm it's the master even though it's Monotonic by now. result := M{} cmd := session.DB("admin").C("$cmd") err = cmd.Find(M{"ismaster": 1}).One(&result) c.Assert(err, IsNil) c.Assert(result["ismaster"], Equals, true) } func (s *S) TestSetModeStrongAfterMonotonic(c *C) { // Test that shifting from Monotonic to Strong while // using a slave socket will keep the socket reserved // until the master socket is necessary, so that no // switch over occurs unless it's actually necessary. // Must necessarily connect to a slave, otherwise the // master connection will be available first. session, err := mgo.Dial("localhost:40012") c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Monotonic, false) // Ensure we're talking to a slave, and reserve the socket. result := M{} err = session.Run("ismaster", &result) c.Assert(err, IsNil) c.Assert(result["ismaster"], Equals, false) // Switch to a Strong session. session.SetMode(mgo.Strong, false) // Wait since the sync also uses sockets. for len(session.LiveServers()) != 3 { c.Log("Waiting for cluster sync to finish...") time.Sleep(5e8) } // Slave socket should still be reserved. stats := mgo.GetStats() c.Assert(stats.SocketsInUse, Equals, 1) // But any operation will switch it to the master. result = M{} err = session.Run("ismaster", &result) c.Assert(err, IsNil) c.Assert(result["ismaster"], Equals, true) } func (s *S) TestSetModeMonotonicWriteOnIteration(c *C) { // Must necessarily connect to a slave, otherwise the // master connection will be available first. session, err := mgo.Dial("localhost:40012") c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Monotonic, false) c.Assert(session.Mode(), Equals, mgo.Monotonic) coll1 := session.DB("mydb").C("mycoll1") coll2 := session.DB("mydb").C("mycoll2") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { err := coll1.Insert(M{"n": n}) c.Assert(err, IsNil) } // Release master so we can grab a slave again. session.Refresh() // Wait until synchronization is done. for { n, err := coll1.Count() c.Assert(err, IsNil) if n == len(ns) { break } } iter := coll1.Find(nil).Batch(2).Iter() i := 0 m := M{} for iter.Next(&m) { i++ if i > 3 { err := coll2.Insert(M{"n": 47 + i}) c.Assert(err, IsNil) } } c.Assert(i, Equals, len(ns)) } func (s *S) TestSetModeEventual(c *C) { // Must necessarily connect to a slave, otherwise the // master connection will be available first. session, err := mgo.Dial("localhost:40012") c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Eventual, false) c.Assert(session.Mode(), Equals, mgo.Eventual) result := M{} err = session.Run("ismaster", &result) c.Assert(err, IsNil) c.Assert(result["ismaster"], Equals, false) coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) result = M{} err = session.Run("ismaster", &result) c.Assert(err, IsNil) c.Assert(result["ismaster"], Equals, false) // Wait since the sync also uses sockets. for len(session.LiveServers()) != 3 { c.Log("Waiting for cluster sync to finish...") time.Sleep(5e8) } stats := mgo.GetStats() c.Assert(stats.MasterConns, Equals, 1) c.Assert(stats.SlaveConns, Equals, 2) c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestSetModeEventualAfterStrong(c *C) { // Test that a strong session shifting to an eventual // one preserves the socket untouched. session, err := mgo.Dial("localhost:40012") c.Assert(err, IsNil) defer session.Close() // Insert something to force a connection to the master. coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) session.SetMode(mgo.Eventual, false) // Wait since the sync also uses sockets. for len(session.LiveServers()) != 3 { c.Log("Waiting for cluster sync to finish...") time.Sleep(5e8) } // Master socket should still be reserved. stats := mgo.GetStats() c.Assert(stats.SocketsInUse, Equals, 1) // Confirm it's the master even though it's Eventual by now. result := M{} cmd := session.DB("admin").C("$cmd") err = cmd.Find(M{"ismaster": 1}).One(&result) c.Assert(err, IsNil) c.Assert(result["ismaster"], Equals, true) session.SetMode(mgo.Eventual, true) stats = mgo.GetStats() c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestPrimaryShutdownStrong(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40021") c.Assert(err, IsNil) defer session.Close() // With strong consistency, this will open a socket to the master. result := &struct{ Host string }{} err = session.Run("serverStatus", result) c.Assert(err, IsNil) // Kill the master. host := result.Host s.Stop(host) // This must fail, since the connection was broken. err = session.Run("serverStatus", result) c.Assert(err, Equals, io.EOF) // With strong consistency, it fails again until reset. err = session.Run("serverStatus", result) c.Assert(err, Equals, io.EOF) session.Refresh() // Now we should be able to talk to the new master. // Increase the timeout since this may take quite a while. session.SetSyncTimeout(3 * time.Minute) err = session.Run("serverStatus", result) c.Assert(err, IsNil) c.Assert(result.Host, Not(Equals), host) // Insert some data to confirm it's indeed a master. err = session.DB("mydb").C("mycoll").Insert(M{"n": 42}) c.Assert(err, IsNil) } func (s *S) TestPrimaryHiccup(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40021") c.Assert(err, IsNil) defer session.Close() // With strong consistency, this will open a socket to the master. result := &struct{ Host string }{} err = session.Run("serverStatus", result) c.Assert(err, IsNil) // Establish a few extra sessions to create spare sockets to // the master. This increases a bit the chances of getting an // incorrect cached socket. var sessions []*mgo.Session for i := 0; i < 20; i++ { sessions = append(sessions, session.Copy()) err = sessions[len(sessions)-1].Run("serverStatus", result) c.Assert(err, IsNil) } for i := range sessions { sessions[i].Close() } // Kill the master, but bring it back immediatelly. host := result.Host s.Stop(host) s.StartAll() // This must fail, since the connection was broken. err = session.Run("serverStatus", result) c.Assert(err, Equals, io.EOF) // With strong consistency, it fails again until reset. err = session.Run("serverStatus", result) c.Assert(err, Equals, io.EOF) session.Refresh() // Now we should be able to talk to the new master. // Increase the timeout since this may take quite a while. session.SetSyncTimeout(3 * time.Minute) // Insert some data to confirm it's indeed a master. err = session.DB("mydb").C("mycoll").Insert(M{"n": 42}) c.Assert(err, IsNil) } func (s *S) TestPrimaryShutdownMonotonic(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40021") c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Monotonic, true) // Insert something to force a switch to the master. coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) // Wait a bit for this to be synchronized to slaves. time.Sleep(3 * time.Second) result := &struct{ Host string }{} err = session.Run("serverStatus", result) c.Assert(err, IsNil) // Kill the master. host := result.Host s.Stop(host) // This must fail, since the connection was broken. err = session.Run("serverStatus", result) c.Assert(err, Equals, io.EOF) // With monotonic consistency, it fails again until reset. err = session.Run("serverStatus", result) c.Assert(err, Equals, io.EOF) session.Refresh() // Now we should be able to talk to the new master. err = session.Run("serverStatus", result) c.Assert(err, IsNil) c.Assert(result.Host, Not(Equals), host) } func (s *S) TestPrimaryShutdownMonotonicWithSlave(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40021") c.Assert(err, IsNil) defer session.Close() ssresult := &struct{ Host string }{} imresult := &struct{ IsMaster bool }{} // Figure the master while still using the strong session. err = session.Run("serverStatus", ssresult) c.Assert(err, IsNil) err = session.Run("isMaster", imresult) c.Assert(err, IsNil) master := ssresult.Host c.Assert(imresult.IsMaster, Equals, true, Commentf("%s is not the master", master)) // Create new monotonic session with an explicit address to ensure // a slave is synchronized before the master, otherwise a connection // with the master may be used below for lack of other options. var addr string switch { case strings.HasSuffix(ssresult.Host, ":40021"): addr = "localhost:40022" case strings.HasSuffix(ssresult.Host, ":40022"): addr = "localhost:40021" case strings.HasSuffix(ssresult.Host, ":40023"): addr = "localhost:40021" default: c.Fatal("Unknown host: ", ssresult.Host) } session, err = mgo.Dial(addr) c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Monotonic, true) // Check the address of the socket associated with the monotonic session. c.Log("Running serverStatus and isMaster with monotonic session") err = session.Run("serverStatus", ssresult) c.Assert(err, IsNil) err = session.Run("isMaster", imresult) c.Assert(err, IsNil) slave := ssresult.Host c.Assert(imresult.IsMaster, Equals, false, Commentf("%s is not a slave", slave)) c.Assert(master, Not(Equals), slave) // Kill the master. s.Stop(master) // Session must still be good, since we were talking to a slave. err = session.Run("serverStatus", ssresult) c.Assert(err, IsNil) c.Assert(ssresult.Host, Equals, slave, Commentf("Monotonic session moved from %s to %s", slave, ssresult.Host)) // If we try to insert something, it'll have to hold until the new // master is available to move the connection, and work correctly. coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) // Must now be talking to the new master. err = session.Run("serverStatus", ssresult) c.Assert(err, IsNil) err = session.Run("isMaster", imresult) c.Assert(err, IsNil) c.Assert(imresult.IsMaster, Equals, true, Commentf("%s is not the master", master)) // ... which is not the old one, since it's still dead. c.Assert(ssresult.Host, Not(Equals), master) } func (s *S) TestPrimaryShutdownEventual(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40021") c.Assert(err, IsNil) defer session.Close() result := &struct{ Host string }{} err = session.Run("serverStatus", result) c.Assert(err, IsNil) master := result.Host session.SetMode(mgo.Eventual, true) // Should connect to the master when needed. coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) // Wait a bit for this to be synchronized to slaves. time.Sleep(3 * time.Second) // Kill the master. s.Stop(master) // Should still work, with the new master now. coll = session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) err = session.Run("serverStatus", result) c.Assert(err, IsNil) c.Assert(result.Host, Not(Equals), master) } func (s *S) TestPreserveSocketCountOnSync(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() stats := mgo.GetStats() for stats.MasterConns+stats.SlaveConns != 3 { stats = mgo.GetStats() c.Log("Waiting for all connections to be established...") time.Sleep(5e8) } c.Assert(stats.SocketsAlive, Equals, 3) // Kill the master (with rs1, 'a' is always the master). s.Stop("localhost:40011") // Wait for the logic to run for a bit and bring it back. go func() { time.Sleep(5e9) s.StartAll() }() // Do an action to kick the resync logic in, and also to // wait until the cluster recognizes the server is back. result := struct{ Ok bool }{} err = session.Run("getLastError", &result) c.Assert(err, IsNil) c.Assert(result.Ok, Equals, true) for i := 0; i != 20; i++ { stats = mgo.GetStats() if stats.SocketsAlive == 3 { break } c.Logf("Waiting for 3 sockets alive, have %d", stats.SocketsAlive) time.Sleep(5e8) } // Ensure the number of sockets is preserved after syncing. stats = mgo.GetStats() c.Assert(stats.SocketsAlive, Equals, 3) c.Assert(stats.SocketsInUse, Equals, 1) c.Assert(stats.SocketRefs, Equals, 1) } // Connect to the master of a deployment with a single server, // run an insert, and then ensure the insert worked and that a // single connection was established. func (s *S) TestTopologySyncWithSingleMaster(c *C) { // Use hostname here rather than IP, to make things trickier. session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1, "b": 2}) c.Assert(err, IsNil) // One connection used for discovery. Master socket recycled for // insert. Socket is reserved after insert. stats := mgo.GetStats() c.Assert(stats.MasterConns, Equals, 1) c.Assert(stats.SlaveConns, Equals, 0) c.Assert(stats.SocketsInUse, Equals, 1) // Refresh session and socket must be released. session.Refresh() stats = mgo.GetStats() c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestTopologySyncWithSlaveSeed(c *C) { // That's supposed to be a slave. Must run discovery // and find out master to insert successfully. session, err := mgo.Dial("localhost:40012") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"a": 1, "b": 2}) result := struct{ Ok bool }{} err = session.Run("getLastError", &result) c.Assert(err, IsNil) c.Assert(result.Ok, Equals, true) // One connection to each during discovery. Master // socket recycled for insert. stats := mgo.GetStats() c.Assert(stats.MasterConns, Equals, 1) c.Assert(stats.SlaveConns, Equals, 2) // Only one socket reference alive, in the master socket owned // by the above session. c.Assert(stats.SocketsInUse, Equals, 1) // Refresh it, and it must be gone. session.Refresh() stats = mgo.GetStats() c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestSyncTimeout(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() s.Stop("localhost:40001") timeout := 3 * time.Second session.SetSyncTimeout(timeout) started := time.Now() // Do something. result := struct{ Ok bool }{} err = session.Run("getLastError", &result) c.Assert(err, ErrorMatches, "no reachable servers") c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true) c.Assert(started.After(time.Now().Add(-timeout*2)), Equals, true) } func (s *S) TestDialWithTimeout(c *C) { if *fast { c.Skip("-fast") } timeout := 2 * time.Second started := time.Now() // 40009 isn't used by the test servers. session, err := mgo.DialWithTimeout("localhost:40009", timeout) if session != nil { session.Close() } c.Assert(err, ErrorMatches, "no reachable servers") c.Assert(session, IsNil) c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true) c.Assert(started.After(time.Now().Add(-timeout*2)), Equals, true) } func (s *S) TestSocketTimeout(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() s.Freeze("localhost:40001") timeout := 3 * time.Second session.SetSocketTimeout(timeout) started := time.Now() // Do something. result := struct{ Ok bool }{} err = session.Run("getLastError", &result) c.Assert(err, ErrorMatches, ".*: i/o timeout") c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true) c.Assert(started.After(time.Now().Add(-timeout*2)), Equals, true) } func (s *S) TestSocketTimeoutOnDial(c *C) { if *fast { c.Skip("-fast") } timeout := 1 * time.Second defer mgo.HackSyncSocketTimeout(timeout)() s.Freeze("localhost:40001") started := time.Now() session, err := mgo.DialWithTimeout("localhost:40001", timeout) c.Assert(err, ErrorMatches, "no reachable servers") c.Assert(session, IsNil) c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true) c.Assert(started.After(time.Now().Add(-20 * time.Second)), Equals, true) } func (s *S) TestSocketTimeoutOnInactiveSocket(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() timeout := 2 * time.Second session.SetSocketTimeout(timeout) // Do something that relies on the timeout and works. c.Assert(session.Ping(), IsNil) // Freeze and wait for the timeout to go by. s.Freeze("localhost:40001") time.Sleep(timeout + 500 * time.Millisecond) s.Thaw("localhost:40001") // Do something again. The timeout above should not have killed // the socket as there was nothing to be done. c.Assert(session.Ping(), IsNil) } func (s *S) TestDirect(c *C) { session, err := mgo.Dial("localhost:40012?connect=direct") c.Assert(err, IsNil) defer session.Close() // We know that server is a slave. session.SetMode(mgo.Monotonic, true) result := &struct{ Host string }{} err = session.Run("serverStatus", result) c.Assert(err, IsNil) c.Assert(strings.HasSuffix(result.Host, ":40012"), Equals, true) stats := mgo.GetStats() c.Assert(stats.SocketsAlive, Equals, 1) c.Assert(stats.SocketsInUse, Equals, 1) c.Assert(stats.SocketRefs, Equals, 1) // We've got no master, so it'll timeout. session.SetSyncTimeout(5e8 * time.Nanosecond) coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"test": 1}) c.Assert(err, ErrorMatches, "no reachable servers") // Writing to the local database is okay. coll = session.DB("local").C("mycoll") defer coll.RemoveAll(nil) id := bson.NewObjectId() err = coll.Insert(M{"_id": id}) c.Assert(err, IsNil) // Data was stored in the right server. n, err := coll.Find(M{"_id": id}).Count() c.Assert(err, IsNil) c.Assert(n, Equals, 1) // Server hasn't changed. result.Host = "" err = session.Run("serverStatus", result) c.Assert(err, IsNil) c.Assert(strings.HasSuffix(result.Host, ":40012"), Equals, true) } func (s *S) TestDirectToUnknownStateMember(c *C) { session, err := mgo.Dial("localhost:40041?connect=direct") c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Monotonic, true) result := &struct{ Host string }{} err = session.Run("serverStatus", result) c.Assert(err, IsNil) c.Assert(strings.HasSuffix(result.Host, ":40041"), Equals, true) // We've got no master, so it'll timeout. session.SetSyncTimeout(5e8 * time.Nanosecond) coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"test": 1}) c.Assert(err, ErrorMatches, "no reachable servers") // Slave is still reachable. result.Host = "" err = session.Run("serverStatus", result) c.Assert(err, IsNil) c.Assert(strings.HasSuffix(result.Host, ":40041"), Equals, true) } type OpCounters struct { Insert int Query int Update int Delete int GetMore int Command int } func getOpCounters(server string) (c *OpCounters, err error) { session, err := mgo.Dial(server + "?connect=direct") if err != nil { return nil, err } defer session.Close() session.SetMode(mgo.Monotonic, true) result := struct{ OpCounters }{} err = session.Run("serverStatus", &result) return &result.OpCounters, err } func (s *S) TestMonotonicSlaveOkFlagWithMongos(c *C) { session, err := mgo.Dial("localhost:40021") c.Assert(err, IsNil) defer session.Close() ssresult := &struct{ Host string }{} imresult := &struct{ IsMaster bool }{} // Figure the master while still using the strong session. err = session.Run("serverStatus", ssresult) c.Assert(err, IsNil) err = session.Run("isMaster", imresult) c.Assert(err, IsNil) master := ssresult.Host c.Assert(imresult.IsMaster, Equals, true, Commentf("%s is not the master", master)) // Collect op counters for everyone. opc21a, err := getOpCounters("localhost:40021") c.Assert(err, IsNil) opc22a, err := getOpCounters("localhost:40022") c.Assert(err, IsNil) opc23a, err := getOpCounters("localhost:40023") c.Assert(err, IsNil) // Do a SlaveOk query through MongoS mongos, err := mgo.Dial("localhost:40202") c.Assert(err, IsNil) defer mongos.Close() mongos.SetMode(mgo.Monotonic, true) coll := mongos.DB("mydb").C("mycoll") result := &struct{}{} for i := 0; i != 5; i++ { err := coll.Find(nil).One(result) c.Assert(err, Equals, mgo.ErrNotFound) } // Collect op counters for everyone again. opc21b, err := getOpCounters("localhost:40021") c.Assert(err, IsNil) opc22b, err := getOpCounters("localhost:40022") c.Assert(err, IsNil) opc23b, err := getOpCounters("localhost:40023") c.Assert(err, IsNil) masterPort := master[strings.Index(master, ":")+1:] var masterDelta, slaveDelta int switch masterPort { case "40021": masterDelta = opc21b.Query - opc21a.Query slaveDelta = (opc22b.Query - opc22a.Query) + (opc23b.Query - opc23a.Query) case "40022": masterDelta = opc22b.Query - opc22a.Query slaveDelta = (opc21b.Query - opc21a.Query) + (opc23b.Query - opc23a.Query) case "40023": masterDelta = opc23b.Query - opc23a.Query slaveDelta = (opc21b.Query - opc21a.Query) + (opc22b.Query - opc22a.Query) default: c.Fatal("Uh?") } c.Check(masterDelta, Equals, 0) // Just the counting itself. c.Check(slaveDelta, Equals, 5) // The counting for both, plus 5 queries above. } func (s *S) TestRemovalOfClusterMember(c *C) { if *fast { c.Skip("-fast") } master, err := mgo.Dial("localhost:40021") c.Assert(err, IsNil) defer master.Close() // Wait for cluster to fully sync up. for i := 0; i < 10; i++ { if len(master.LiveServers()) == 3 { break } time.Sleep(5e8) } if len(master.LiveServers()) != 3 { c.Fatalf("Test started with bad cluster state: %v", master.LiveServers()) } result := &struct { IsMaster bool Me string }{} slave := master.Copy() slave.SetMode(mgo.Monotonic, true) // Monotonic can hold a non-master socket persistently. err = slave.Run("isMaster", result) c.Assert(err, IsNil) c.Assert(result.IsMaster, Equals, false) slaveAddr := result.Me defer func() { master.Refresh() master.Run(bson.D{{"$eval", `rs.add("` + slaveAddr + `")`}}, nil) master.Close() slave.Close() }() c.Logf("========== Removing slave: %s ==========", slaveAddr) master.Run(bson.D{{"$eval", `rs.remove("` + slaveAddr + `")`}}, nil) err = master.Ping() c.Assert(err, Equals, io.EOF) master.Refresh() // Give the cluster a moment to catch up by doing a roundtrip to the master. err = master.Ping() c.Assert(err, IsNil) time.Sleep(3e9) // This must fail since the slave has been taken off the cluster. err = slave.Ping() c.Assert(err, NotNil) for i := 0; i < 15; i++ { if len(master.LiveServers()) == 2 { break } time.Sleep(time.Second) } live := master.LiveServers() if len(live) != 2 { c.Errorf("Removed server still considered live: %#s", live) } c.Log("========== Test succeeded. ==========") } func (s *S) TestSocketLimit(c *C) { if *fast { c.Skip("-fast") } const socketLimit = 64 restore := mgo.HackSocketsPerServer(socketLimit) defer restore() session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() stats := mgo.GetStats() for stats.MasterConns+stats.SlaveConns != 3 { stats = mgo.GetStats() c.Log("Waiting for all connections to be established...") time.Sleep(5e8) } c.Assert(stats.SocketsAlive, Equals, 3) // Consume the whole limit for the master. var master []*mgo.Session for i := 0; i < socketLimit; i++ { s := session.Copy() defer s.Close() err := s.Ping() c.Assert(err, IsNil) master = append(master, s) } before := time.Now() go func() { time.Sleep(3e9) master[0].Refresh() }() // Now a single ping must block, since it would need another // connection to the master, over the limit. Once the goroutine // above releases its socket, it should move on. session.Ping() delay := time.Now().Sub(before) c.Assert(delay > 3e9, Equals, true) c.Assert(delay < 6e9, Equals, true) } func (s *S) TestSetModeEventualIterBug(c *C) { session1, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session1.Close() session1.SetMode(mgo.Eventual, false) coll1 := session1.DB("mydb").C("mycoll") const N = 100 for i := 0; i < N; i++ { err = coll1.Insert(M{"_id": i}) c.Assert(err, IsNil) } c.Logf("Waiting until secondary syncs") for { n, err := coll1.Count() c.Assert(err, IsNil) if n == N { c.Logf("Found all") break } } session2, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session2.Close() session2.SetMode(mgo.Eventual, false) coll2 := session2.DB("mydb").C("mycoll") i := 0 iter := coll2.Find(nil).Batch(10).Iter() var result struct{} for iter.Next(&result) { i++ } c.Assert(iter.Close(), Equals, nil) c.Assert(i, Equals, N) } func (s *S) TestCustomDial(c *C) { dials := make(chan bool, 16) dial := func(addr net.Addr) (net.Conn, error) { tcpaddr, ok := addr.(*net.TCPAddr) if !ok { return nil, fmt.Errorf("unexpected address type: %T", addr) } dials <- true return net.DialTCP("tcp", nil, tcpaddr) } info := mgo.DialInfo{ Addrs: []string{"localhost:40012"}, Dial: dial, } // Use hostname here rather than IP, to make things trickier. session, err := mgo.DialWithInfo(&info) c.Assert(err, IsNil) defer session.Close() const N = 3 for i := 0; i < N; i++ { select { case <-dials: case <-time.After(5 * time.Second): c.Fatalf("expected %d dials, got %d", N, i) } } select { case <-dials: c.Fatalf("got more dials than expected") case <-time.After(100 * time.Millisecond): } } func (s *S) TestPrimaryShutdownOnAuthShard(c *C) { if *fast { c.Skip("-fast") } // Dial the shard. session, err := mgo.Dial("localhost:40203") c.Assert(err, IsNil) defer session.Close() // Login and insert something to make it more realistic. session.DB("admin").Login("root", "rapadura") coll := session.DB("mydb").C("mycoll") err = coll.Insert(bson.M{"n": 1}) c.Assert(err, IsNil) // Dial the replica set to figure the master out. rs, err := mgo.Dial("root:rapadura@localhost:40031") c.Assert(err, IsNil) defer rs.Close() // With strong consistency, this will open a socket to the master. result := &struct{ Host string }{} err = rs.Run("serverStatus", result) c.Assert(err, IsNil) // Kill the master. host := result.Host s.Stop(host) // This must fail, since the connection was broken. err = rs.Run("serverStatus", result) c.Assert(err, Equals, io.EOF) // This won't work because the master just died. err = coll.Insert(bson.M{"n": 2}) c.Assert(err, NotNil) // Refresh session and wait for re-election. session.Refresh() for i := 0; i < 60; i++ { err = coll.Insert(bson.M{"n": 3}) if err == nil { break } c.Logf("Waiting for replica set to elect a new master. Last error: %v", err) time.Sleep(500 * time.Millisecond) } c.Assert(err, IsNil) count, err := coll.Count() c.Assert(count > 1, Equals, true) } func (s *S) TestNearestSecondary(c *C) { defer mgo.HackPingDelay(3 * time.Second)() rs1a := "127.0.0.1:40011" rs1b := "127.0.0.1:40012" rs1c := "127.0.0.1:40013" s.Freeze(rs1b) session, err := mgo.Dial(rs1a) c.Assert(err, IsNil) defer session.Close() // Wait for the sync up to run through the first couple of servers. for len(session.LiveServers()) != 2 { c.Log("Waiting for two servers to be alive...") time.Sleep(100 * time.Millisecond) } // Extra delay to ensure the third server gets penalized. time.Sleep(500 * time.Millisecond) // Release third server. s.Thaw(rs1b) // Wait for it to come up. for len(session.LiveServers()) != 3 { c.Log("Waiting for all servers to be alive...") time.Sleep(100 * time.Millisecond) } session.SetMode(mgo.Monotonic, true) var result struct{ Host string } // See which slave picks the line, several times to avoid chance. for i := 0; i < 10; i++ { session.Refresh() err = session.Run("serverStatus", &result) c.Assert(err, IsNil) c.Assert(hostPort(result.Host), Equals, hostPort(rs1c)) } if *fast { // Don't hold back for several seconds. return } // Now hold the other server for long enough to penalize it. s.Freeze(rs1c) time.Sleep(5 * time.Second) s.Thaw(rs1c) // Wait for the ping to be processed. time.Sleep(500 * time.Millisecond) // Repeating the test should now pick the former server consistently. for i := 0; i < 10; i++ { session.Refresh() err = session.Run("serverStatus", &result) c.Assert(err, IsNil) c.Assert(hostPort(result.Host), Equals, hostPort(rs1b)) } } func (s *S) TestConnectCloseConcurrency(c *C) { restore := mgo.HackPingDelay(500 * time.Millisecond) defer restore() var wg sync.WaitGroup const n = 500 wg.Add(n) for i := 0; i < n; i++ { go func() { defer wg.Done() session, err := mgo.Dial("localhost:40001") if err != nil { c.Fatal(err) } time.Sleep(1) session.Close() }() } wg.Wait() } func (s *S) TestSelectServers(c *C) { if !s.versionAtLeast(2, 2) { c.Skip("read preferences introduced in 2.2") } session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Eventual, true) var result struct{ Host string } session.Refresh() session.SelectServers(bson.D{{"rs1", "b"}}) err = session.Run("serverStatus", &result) c.Assert(err, IsNil) c.Assert(hostPort(result.Host), Equals, "40012") session.Refresh() session.SelectServers(bson.D{{"rs1", "c"}}) err = session.Run("serverStatus", &result) c.Assert(err, IsNil) c.Assert(hostPort(result.Host), Equals, "40013") } func (s *S) TestSelectServersWithMongos(c *C) { if !s.versionAtLeast(2, 2) { c.Skip("read preferences introduced in 2.2") } session, err := mgo.Dial("localhost:40021") c.Assert(err, IsNil) defer session.Close() ssresult := &struct{ Host string }{} imresult := &struct{ IsMaster bool }{} // Figure the master while still using the strong session. err = session.Run("serverStatus", ssresult) c.Assert(err, IsNil) err = session.Run("isMaster", imresult) c.Assert(err, IsNil) master := ssresult.Host c.Assert(imresult.IsMaster, Equals, true, Commentf("%s is not the master", master)) var slave1, slave2 string switch hostPort(master) { case "40021": slave1, slave2 = "b", "c" case "40022": slave1, slave2 = "a", "c" case "40023": slave1, slave2 = "a", "b" } // Collect op counters for everyone. opc21a, err := getOpCounters("localhost:40021") c.Assert(err, IsNil) opc22a, err := getOpCounters("localhost:40022") c.Assert(err, IsNil) opc23a, err := getOpCounters("localhost:40023") c.Assert(err, IsNil) // Do a SlaveOk query through MongoS mongos, err := mgo.Dial("localhost:40202") c.Assert(err, IsNil) defer mongos.Close() mongos.SetMode(mgo.Monotonic, true) mongos.Refresh() mongos.SelectServers(bson.D{{"rs2", slave1}}) coll := mongos.DB("mydb").C("mycoll") result := &struct{}{} for i := 0; i != 5; i++ { err := coll.Find(nil).One(result) c.Assert(err, Equals, mgo.ErrNotFound) } mongos.Refresh() mongos.SelectServers(bson.D{{"rs2", slave2}}) coll = mongos.DB("mydb").C("mycoll") for i := 0; i != 7; i++ { err := coll.Find(nil).One(result) c.Assert(err, Equals, mgo.ErrNotFound) } // Collect op counters for everyone again. opc21b, err := getOpCounters("localhost:40021") c.Assert(err, IsNil) opc22b, err := getOpCounters("localhost:40022") c.Assert(err, IsNil) opc23b, err := getOpCounters("localhost:40023") c.Assert(err, IsNil) switch hostPort(master) { case "40021": c.Check(opc21b.Query - opc21a.Query, Equals, 0) c.Check(opc22b.Query - opc22a.Query, Equals, 5) c.Check(opc23b.Query - opc23a.Query, Equals, 7) case "40022": c.Check(opc21b.Query - opc21a.Query, Equals, 5) c.Check(opc22b.Query - opc22a.Query, Equals, 0) c.Check(opc23b.Query - opc23a.Query, Equals, 7) case "40023": c.Check(opc21b.Query - opc21a.Query, Equals, 5) c.Check(opc22b.Query - opc22a.Query, Equals, 7) c.Check(opc23b.Query - opc23a.Query, Equals, 0) default: c.Fatal("Uh?") } } juju-core_1.18.1/src/labix.org/v2/mgo/doc.go0000644000015300001610000000225212321736015020324 0ustar jenkinsjenkins// The mgo ("mango") rich MongoDB driver for Go. // // The mgo project (pronounced as "mango") is a rich MongoDB driver for // the Go language. High-level details about the project may be found // at its web page: // // http://labix.org/mgo // // Usage of the driver revolves around the concept of sessions. To // get started, obtain a session using the Dial function: // // session, err := mgo.Dial(url) // // This will establish one or more connections with the cluster of // servers defined by the url parameter. From then on, the cluster // may be queried with multiple consistency rules (see SetMode) and // documents retrieved with statements such as: // // c := session.DB(database).C(collection) // err := c.Find(query).One(&result) // // New sessions may be created by calling New, Copy, or Clone on an // initial session. These spawned sessions will share the same cluster // information and connection cache, and may be easily handed into other // methods and functions for organizing logic. Every session created // must have its Close method called at the end of its use. // // For more details, see the documentation for the types and methods. // package mgo juju-core_1.18.1/src/labix.org/v2/mgo/socket.go0000644000015300001610000004134212321736015021052 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo import ( "errors" "labix.org/v2/mgo/bson" "net" "sync" "time" ) type replyFunc func(err error, reply *replyOp, docNum int, docData []byte) type mongoSocket struct { sync.Mutex server *mongoServer // nil when cached conn net.Conn timeout time.Duration addr string // For debugging only. nextRequestId uint32 replyFuncs map[uint32]replyFunc references int auth []authInfo logout []authInfo cachedNonce string gotNonce sync.Cond dead error serverInfo *mongoServerInfo } type queryOpFlags uint32 const ( flagTailable queryOpFlags = 1 << 1 flagSlaveOk queryOpFlags = 1 << 2 flagLogReplay queryOpFlags = 1 << 3 flagAwaitData queryOpFlags = 1 << 5 ) type queryOp struct { collection string query interface{} skip int32 limit int32 selector interface{} flags queryOpFlags replyFunc replyFunc options queryWrapper hasOptions bool serverTags []bson.D } type queryWrapper struct { Query interface{} "$query" OrderBy interface{} "$orderby,omitempty" Hint interface{} "$hint,omitempty" Explain bool "$explain,omitempty" Snapshot bool "$snapshot,omitempty" ReadPreference bson.D "$readPreference,omitempty" } func (op *queryOp) finalQuery(socket *mongoSocket) interface{} { if op.flags&flagSlaveOk != 0 && len(op.serverTags) > 0 && socket.ServerInfo().Mongos { op.hasOptions = true op.options.ReadPreference = bson.D{{"mode", "secondaryPreferred"}, {"tags", op.serverTags}} } if op.hasOptions { if op.query == nil { var empty bson.D op.options.Query = empty } else { op.options.Query = op.query } debugf("final query is %#v\n", &op.options) return &op.options } return op.query } type getMoreOp struct { collection string limit int32 cursorId int64 replyFunc replyFunc } type replyOp struct { flags uint32 cursorId int64 firstDoc int32 replyDocs int32 } type insertOp struct { collection string // "database.collection" documents []interface{} // One or more documents to insert } type updateOp struct { collection string // "database.collection" selector interface{} update interface{} flags uint32 } type deleteOp struct { collection string // "database.collection" selector interface{} flags uint32 } type killCursorsOp struct { cursorIds []int64 } type requestInfo struct { bufferPos int replyFunc replyFunc } func newSocket(server *mongoServer, conn net.Conn, timeout time.Duration) *mongoSocket { socket := &mongoSocket{ conn: conn, addr: server.Addr, server: server, replyFuncs: make(map[uint32]replyFunc), } socket.gotNonce.L = &socket.Mutex if err := socket.InitialAcquire(server.Info(), timeout); err != nil { panic("newSocket: InitialAcquire returned error: " + err.Error()) } stats.socketsAlive(+1) debugf("Socket %p to %s: initialized", socket, socket.addr) socket.resetNonce() go socket.readLoop() return socket } // Server returns the server that the socket is associated with. // It returns nil while the socket is cached in its respective server. func (socket *mongoSocket) Server() *mongoServer { socket.Lock() server := socket.server socket.Unlock() return server } // ServerInfo returns details for the server at the time the socket // was initially acquired. func (socket *mongoSocket) ServerInfo() *mongoServerInfo { socket.Lock() serverInfo := socket.serverInfo socket.Unlock() return serverInfo } // InitialAcquire obtains the first reference to the socket, either // right after the connection is made or once a recycled socket is // being put back in use. func (socket *mongoSocket) InitialAcquire(serverInfo *mongoServerInfo, timeout time.Duration) error { socket.Lock() if socket.references > 0 { panic("Socket acquired out of cache with references") } if socket.dead != nil { socket.Unlock() return socket.dead } socket.references++ socket.serverInfo = serverInfo socket.timeout = timeout stats.socketsInUse(+1) stats.socketRefs(+1) socket.Unlock() return nil } // Acquire obtains an additional reference to the socket. // The socket will only be recycled when it's released as many // times as it's been acquired. func (socket *mongoSocket) Acquire() (info *mongoServerInfo) { socket.Lock() if socket.references == 0 { panic("Socket got non-initial acquire with references == 0") } // We'll track references to dead sockets as well. // Caller is still supposed to release the socket. socket.references++ stats.socketRefs(+1) serverInfo := socket.serverInfo socket.Unlock() return serverInfo } // Release decrements a socket reference. The socket will be // recycled once its released as many times as it's been acquired. func (socket *mongoSocket) Release() { socket.Lock() if socket.references == 0 { panic("socket.Release() with references == 0") } socket.references-- stats.socketRefs(-1) if socket.references == 0 { stats.socketsInUse(-1) server := socket.server socket.Unlock() socket.LogoutAll() // If the socket is dead server is nil. if server != nil { server.RecycleSocket(socket) } } else { socket.Unlock() } } // SetTimeout changes the timeout used on socket operations. func (socket *mongoSocket) SetTimeout(d time.Duration) { socket.Lock() socket.timeout = d socket.Unlock() } type deadlineType int const ( readDeadline deadlineType = 1 writeDeadline deadlineType = 2 ) func (socket *mongoSocket) updateDeadline(which deadlineType) { var when time.Time if socket.timeout > 0 { when = time.Now().Add(socket.timeout) } whichstr := "" switch which { case readDeadline | writeDeadline: whichstr = "read/write" socket.conn.SetDeadline(when) case readDeadline: whichstr = "read" socket.conn.SetReadDeadline(when) case writeDeadline: whichstr = "write" socket.conn.SetWriteDeadline(when) default: panic("invalid parameter to updateDeadline") } debugf("Socket %p to %s: updated %s deadline to %s ahead (%s)", socket, socket.addr, whichstr, socket.timeout, when) } // Close terminates the socket use. func (socket *mongoSocket) Close() { socket.kill(errors.New("Closed explicitly"), false) } func (socket *mongoSocket) kill(err error, abend bool) { socket.Lock() if socket.dead != nil { debugf("Socket %p to %s: killed again: %s (previously: %s)", socket, socket.addr, err.Error(), socket.dead.Error()) socket.Unlock() return } logf("Socket %p to %s: closing: %s (abend=%v)", socket, socket.addr, err.Error(), abend) socket.dead = err socket.conn.Close() stats.socketsAlive(-1) replyFuncs := socket.replyFuncs socket.replyFuncs = make(map[uint32]replyFunc) server := socket.server socket.server = nil socket.Unlock() for _, f := range replyFuncs { logf("Socket %p to %s: notifying replyFunc of closed socket: %s", socket, socket.addr, err.Error()) f(err, nil, -1, nil) } if abend { server.AbendSocket(socket) } } func (socket *mongoSocket) SimpleQuery(op *queryOp) (data []byte, err error) { var mutex sync.Mutex var replyData []byte var replyErr error mutex.Lock() op.replyFunc = func(err error, reply *replyOp, docNum int, docData []byte) { replyData = docData replyErr = err mutex.Unlock() } err = socket.Query(op) if err != nil { return nil, err } mutex.Lock() // Wait. if replyErr != nil { return nil, replyErr } return replyData, nil } func (socket *mongoSocket) Query(ops ...interface{}) (err error) { if lops := socket.flushLogout(); len(lops) > 0 { ops = append(lops, ops...) } buf := make([]byte, 0, 256) // Serialize operations synchronously to avoid interrupting // other goroutines while we can't really be sending data. // Also, record id positions so that we can compute request // ids at once later with the lock already held. requests := make([]requestInfo, len(ops)) requestCount := 0 for _, op := range ops { debugf("Socket %p to %s: serializing op: %#v", socket, socket.addr, op) start := len(buf) var replyFunc replyFunc switch op := op.(type) { case *updateOp: buf = addHeader(buf, 2001) buf = addInt32(buf, 0) // Reserved buf = addCString(buf, op.collection) buf = addInt32(buf, int32(op.flags)) debugf("Socket %p to %s: serializing selector document: %#v", socket, socket.addr, op.selector) buf, err = addBSON(buf, op.selector) if err != nil { return err } debugf("Socket %p to %s: serializing update document: %#v", socket, socket.addr, op.update) buf, err = addBSON(buf, op.update) if err != nil { return err } case *insertOp: buf = addHeader(buf, 2002) buf = addInt32(buf, 0) // Reserved buf = addCString(buf, op.collection) for _, doc := range op.documents { debugf("Socket %p to %s: serializing document for insertion: %#v", socket, socket.addr, doc) buf, err = addBSON(buf, doc) if err != nil { return err } } case *queryOp: buf = addHeader(buf, 2004) buf = addInt32(buf, int32(op.flags)) buf = addCString(buf, op.collection) buf = addInt32(buf, op.skip) buf = addInt32(buf, op.limit) buf, err = addBSON(buf, op.finalQuery(socket)) if err != nil { return err } if op.selector != nil { buf, err = addBSON(buf, op.selector) if err != nil { return err } } replyFunc = op.replyFunc case *getMoreOp: buf = addHeader(buf, 2005) buf = addInt32(buf, 0) // Reserved buf = addCString(buf, op.collection) buf = addInt32(buf, op.limit) buf = addInt64(buf, op.cursorId) replyFunc = op.replyFunc case *deleteOp: buf = addHeader(buf, 2006) buf = addInt32(buf, 0) // Reserved buf = addCString(buf, op.collection) buf = addInt32(buf, int32(op.flags)) debugf("Socket %p to %s: serializing selector document: %#v", socket, socket.addr, op.selector) buf, err = addBSON(buf, op.selector) if err != nil { return err } case *killCursorsOp: buf = addHeader(buf, 2007) buf = addInt32(buf, 0) // Reserved buf = addInt32(buf, int32(len(op.cursorIds))) for _, cursorId := range op.cursorIds { buf = addInt64(buf, cursorId) } default: panic("Internal error: unknown operation type") } setInt32(buf, start, int32(len(buf)-start)) if replyFunc != nil { request := &requests[requestCount] request.replyFunc = replyFunc request.bufferPos = start requestCount++ } } // Buffer is ready for the pipe. Lock, allocate ids, and enqueue. socket.Lock() if socket.dead != nil { socket.Unlock() debugf("Socket %p to %s: failing query, already closed: %s", socket, socket.addr, socket.dead.Error()) // XXX This seems necessary in case the session is closed concurrently // with a query being performed, but it's not yet tested: for i := 0; i != requestCount; i++ { request := &requests[i] if request.replyFunc != nil { request.replyFunc(socket.dead, nil, -1, nil) } } return socket.dead } wasWaiting := len(socket.replyFuncs) > 0 // Reserve id 0 for requests which should have no responses. requestId := socket.nextRequestId + 1 if requestId == 0 { requestId++ } socket.nextRequestId = requestId + uint32(requestCount) for i := 0; i != requestCount; i++ { request := &requests[i] setInt32(buf, request.bufferPos+4, int32(requestId)) socket.replyFuncs[requestId] = request.replyFunc requestId++ } debugf("Socket %p to %s: sending %d op(s) (%d bytes)", socket, socket.addr, len(ops), len(buf)) stats.sentOps(len(ops)) socket.updateDeadline(writeDeadline) _, err = socket.conn.Write(buf) if !wasWaiting && requestCount > 0 { socket.updateDeadline(readDeadline) } socket.Unlock() return err } func fill(r net.Conn, b []byte) error { l := len(b) n, err := r.Read(b) for n != l && err == nil { var ni int ni, err = r.Read(b[n:]) n += ni } return err } // Estimated minimum cost per socket: 1 goroutine + memory for the largest // document ever seen. func (socket *mongoSocket) readLoop() { p := make([]byte, 36) // 16 from header + 20 from OP_REPLY fixed fields s := make([]byte, 4) conn := socket.conn // No locking, conn never changes. for { // XXX Handle timeouts, , etc err := fill(conn, p) if err != nil { socket.kill(err, true) return } totalLen := getInt32(p, 0) responseTo := getInt32(p, 8) opCode := getInt32(p, 12) // Don't use socket.server.Addr here. socket is not // locked and socket.server may go away. debugf("Socket %p to %s: got reply (%d bytes)", socket, socket.addr, totalLen) _ = totalLen if opCode != 1 { socket.kill(errors.New("opcode != 1, corrupted data?"), true) return } reply := replyOp{ flags: uint32(getInt32(p, 16)), cursorId: getInt64(p, 20), firstDoc: getInt32(p, 28), replyDocs: getInt32(p, 32), } stats.receivedOps(+1) stats.receivedDocs(int(reply.replyDocs)) socket.Lock() replyFunc, replyFuncFound := socket.replyFuncs[uint32(responseTo)] socket.Unlock() if replyFunc != nil && reply.replyDocs == 0 { replyFunc(nil, &reply, -1, nil) } else { for i := 0; i != int(reply.replyDocs); i++ { err := fill(conn, s) if err != nil { socket.kill(err, true) return } b := make([]byte, int(getInt32(s, 0))) // copy(b, s) in an efficient way. b[0] = s[0] b[1] = s[1] b[2] = s[2] b[3] = s[3] err = fill(conn, b[4:]) if err != nil { socket.kill(err, true) return } if globalDebug && globalLogger != nil { m := bson.M{} if err := bson.Unmarshal(b, m); err == nil { debugf("Socket %p to %s: received document: %#v", socket, socket.addr, m) } } if replyFunc != nil { replyFunc(nil, &reply, i, b) } // XXX Do bound checking against totalLen. } } // Only remove replyFunc after iteration, so that kill() will see it. socket.Lock() if replyFuncFound { delete(socket.replyFuncs, uint32(responseTo)) } if len(socket.replyFuncs) == 0 { // Nothing else to read for now. Disable deadline. socket.conn.SetReadDeadline(time.Time{}) } else { socket.updateDeadline(readDeadline) } socket.Unlock() // XXX Do bound checking against totalLen. } } var emptyHeader = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} func addHeader(b []byte, opcode int) []byte { i := len(b) b = append(b, emptyHeader...) // Enough for current opcodes. b[i+12] = byte(opcode) b[i+13] = byte(opcode >> 8) return b } func addInt32(b []byte, i int32) []byte { return append(b, byte(i), byte(i>>8), byte(i>>16), byte(i>>24)) } func addInt64(b []byte, i int64) []byte { return append(b, byte(i), byte(i>>8), byte(i>>16), byte(i>>24), byte(i>>32), byte(i>>40), byte(i>>48), byte(i>>56)) } func addCString(b []byte, s string) []byte { b = append(b, []byte(s)...) b = append(b, 0) return b } func addBSON(b []byte, doc interface{}) ([]byte, error) { if doc == nil { return append(b, 5, 0, 0, 0, 0), nil } data, err := bson.Marshal(doc) if err != nil { return b, err } return append(b, data...), nil } func setInt32(b []byte, pos int, i int32) { b[pos] = byte(i) b[pos+1] = byte(i >> 8) b[pos+2] = byte(i >> 16) b[pos+3] = byte(i >> 24) } func getInt32(b []byte, pos int) int32 { return (int32(b[pos+0])) | (int32(b[pos+1]) << 8) | (int32(b[pos+2]) << 16) | (int32(b[pos+3]) << 24) } func getInt64(b []byte, pos int) int64 { return (int64(b[pos+0])) | (int64(b[pos+1]) << 8) | (int64(b[pos+2]) << 16) | (int64(b[pos+3]) << 24) | (int64(b[pos+4]) << 32) | (int64(b[pos+5]) << 40) | (int64(b[pos+6]) << 48) | (int64(b[pos+7]) << 56) } juju-core_1.18.1/src/labix.org/v2/mgo/server.go0000644000015300001610000002610112321736015021064 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo import ( "errors" "labix.org/v2/mgo/bson" "net" "sort" "sync" "time" ) // --------------------------------------------------------------------------- // Mongo server encapsulation. type mongoServer struct { sync.RWMutex Addr string ResolvedAddr string tcpaddr *net.TCPAddr unusedSockets []*mongoSocket liveSockets []*mongoSocket closed bool abended bool sync chan bool dial dialer pingValue time.Duration pingIndex int pingCount uint32 pingWindow [6]time.Duration info *mongoServerInfo } type dialer func(addr net.Addr) (net.Conn, error) type mongoServerInfo struct { Master bool Mongos bool Tags bson.D } var defaultServerInfo mongoServerInfo func newServer(addr string, tcpaddr *net.TCPAddr, sync chan bool, dial dialer) *mongoServer { server := &mongoServer{ Addr: addr, ResolvedAddr: tcpaddr.String(), tcpaddr: tcpaddr, sync: sync, dial: dial, info: &defaultServerInfo, } // Once so the server gets a ping value, then loop in background. server.pinger(false) go server.pinger(true) return server } var errSocketLimit = errors.New("per-server connection limit reached") var errServerClosed = errors.New("server was closed") // AcquireSocket returns a socket for communicating with the server. // This will attempt to reuse an old connection, if one is available. Otherwise, // it will establish a new one. The returned socket is owned by the call site, // and will return to the cache when the socket has its Release method called // the same number of times as AcquireSocket + Acquire were called for it. // If the limit argument is not zero, a socket will only be returned if the // number of sockets in use for this server is under the provided limit. func (server *mongoServer) AcquireSocket(limit int, timeout time.Duration) (socket *mongoSocket, abended bool, err error) { for { server.Lock() abended = server.abended if server.closed { server.Unlock() return nil, abended, errServerClosed } n := len(server.unusedSockets) if limit > 0 && len(server.liveSockets)-n >= limit { server.Unlock() return nil, false, errSocketLimit } if n > 0 { socket = server.unusedSockets[n-1] server.unusedSockets[n-1] = nil // Help GC. server.unusedSockets = server.unusedSockets[:n-1] info := server.info server.Unlock() err = socket.InitialAcquire(info, timeout) if err != nil { continue } } else { server.Unlock() socket, err = server.Connect(timeout) if err == nil { server.Lock() // We've waited for the Connect, see if we got // closed in the meantime if server.closed { server.Unlock() socket.Release() socket.Close() return nil, abended, errServerClosed } server.liveSockets = append(server.liveSockets, socket) server.Unlock() } } return } panic("unreachable") } // Connect establishes a new connection to the server. This should // generally be done through server.AcquireSocket(). func (server *mongoServer) Connect(timeout time.Duration) (*mongoSocket, error) { server.RLock() master := server.info.Master dial := server.dial server.RUnlock() logf("Establishing new connection to %s (timeout=%s)...", server.Addr, timeout) var conn net.Conn var err error if dial == nil { // Cannot do this because it lacks timeout support. :-( //conn, err = net.DialTCP("tcp", nil, server.tcpaddr) conn, err = net.DialTimeout("tcp", server.ResolvedAddr, timeout) } else { conn, err = dial(server.tcpaddr) } if err != nil { logf("Connection to %s failed: %v", server.Addr, err.Error()) return nil, err } logf("Connection to %s established.", server.Addr) stats.conn(+1, master) return newSocket(server, conn, timeout), nil } // Close forces closing all sockets that are alive, whether // they're currently in use or not. func (server *mongoServer) Close() { server.Lock() server.closed = true liveSockets := server.liveSockets unusedSockets := server.unusedSockets server.liveSockets = nil server.unusedSockets = nil server.Unlock() logf("Connections to %s closing (%d live sockets).", server.Addr, len(liveSockets)) for i, s := range liveSockets { s.Close() liveSockets[i] = nil } for i := range unusedSockets { unusedSockets[i] = nil } } // RecycleSocket puts socket back into the unused cache. func (server *mongoServer) RecycleSocket(socket *mongoSocket) { server.Lock() if !server.closed { server.unusedSockets = append(server.unusedSockets, socket) } server.Unlock() } func removeSocket(sockets []*mongoSocket, socket *mongoSocket) []*mongoSocket { for i, s := range sockets { if s == socket { copy(sockets[i:], sockets[i+1:]) n := len(sockets) - 1 sockets[n] = nil sockets = sockets[:n] break } } return sockets } // AbendSocket notifies the server that the given socket has terminated // abnormally, and thus should be discarded rather than cached. func (server *mongoServer) AbendSocket(socket *mongoSocket) { server.Lock() server.abended = true if server.closed { server.Unlock() return } server.liveSockets = removeSocket(server.liveSockets, socket) server.unusedSockets = removeSocket(server.unusedSockets, socket) server.Unlock() // Maybe just a timeout, but suggest a cluster sync up just in case. select { case server.sync <- true: default: } } func (server *mongoServer) SetInfo(info *mongoServerInfo) { server.Lock() server.info = info server.Unlock() } func (server *mongoServer) Info() *mongoServerInfo { server.Lock() info := server.info server.Unlock() return info } func (server *mongoServer) hasTags(serverTags []bson.D) bool { NextTagSet: for _, tags := range serverTags { NextReqTag: for _, req := range tags { for _, has := range server.info.Tags { if req.Name == has.Name { if req.Value == has.Value { continue NextReqTag } continue NextTagSet } } continue NextTagSet } return true } return false } var pingDelay = 5 * time.Second func (server *mongoServer) pinger(loop bool) { op := queryOp{ collection: "admin.$cmd", query: bson.D{{"ping", 1}}, flags: flagSlaveOk, limit: -1, } for { if loop { time.Sleep(pingDelay) } op := op socket, _, err := server.AcquireSocket(0, 3 * pingDelay) if err == nil { start := time.Now() _, _ = socket.SimpleQuery(&op) delay := time.Now().Sub(start) server.pingWindow[server.pingIndex] = delay server.pingIndex = (server.pingIndex + 1) % len(server.pingWindow) server.pingCount++ var max time.Duration for i := 0; i < len(server.pingWindow) && uint32(i) < server.pingCount; i++ { if server.pingWindow[i] > max { max = server.pingWindow[i] } } socket.Release() server.Lock() if server.closed { loop = false } server.pingValue = max server.Unlock() logf("Ping for %s is %d ms", server.Addr, max/time.Millisecond) } else if err == errServerClosed { return } if !loop { return } } } type mongoServerSlice []*mongoServer func (s mongoServerSlice) Len() int { return len(s) } func (s mongoServerSlice) Less(i, j int) bool { return s[i].ResolvedAddr < s[j].ResolvedAddr } func (s mongoServerSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s mongoServerSlice) Sort() { sort.Sort(s) } func (s mongoServerSlice) Search(resolvedAddr string) (i int, ok bool) { n := len(s) i = sort.Search(n, func(i int) bool { return s[i].ResolvedAddr >= resolvedAddr }) return i, i != n && s[i].ResolvedAddr == resolvedAddr } type mongoServers struct { slice mongoServerSlice } func (servers *mongoServers) Search(resolvedAddr string) (server *mongoServer) { if i, ok := servers.slice.Search(resolvedAddr); ok { return servers.slice[i] } return nil } func (servers *mongoServers) Add(server *mongoServer) { servers.slice = append(servers.slice, server) servers.slice.Sort() } func (servers *mongoServers) Remove(other *mongoServer) (server *mongoServer) { if i, found := servers.slice.Search(other.ResolvedAddr); found { server = servers.slice[i] copy(servers.slice[i:], servers.slice[i+1:]) n := len(servers.slice) - 1 servers.slice[n] = nil // Help GC. servers.slice = servers.slice[:n] } return } func (servers *mongoServers) Slice() []*mongoServer { return ([]*mongoServer)(servers.slice) } func (servers *mongoServers) Get(i int) *mongoServer { return servers.slice[i] } func (servers *mongoServers) Len() int { return len(servers.slice) } func (servers *mongoServers) Empty() bool { return len(servers.slice) == 0 } // BestFit returns the best guess of what would be the most interesting // server to perform operations on at this point in time. func (servers *mongoServers) BestFit(serverTags []bson.D) *mongoServer { var best *mongoServer for _, next := range servers.slice { if best == nil { best = next best.RLock() if serverTags != nil && !next.info.Mongos && !best.hasTags(serverTags) { best.RUnlock() best = nil } continue } next.RLock() swap := false switch { case serverTags != nil && !next.info.Mongos && !next.hasTags(serverTags): // Must have requested tags. case next.info.Master != best.info.Master: // Prefer slaves. swap = best.info.Master case absDuration(next.pingValue-best.pingValue) > 15*time.Millisecond: // Prefer nearest server. swap = next.pingValue < best.pingValue case len(next.liveSockets)-len(next.unusedSockets) < len(best.liveSockets)-len(best.unusedSockets): // Prefer servers with less connections. swap = true } if swap { best.RUnlock() best = next } else { next.RUnlock() } } if best != nil { best.RUnlock() } return best } func absDuration(d time.Duration) time.Duration { if d < 0 { return -d } return d } juju-core_1.18.1/src/labix.org/v2/mgo/gridfs_test.go0000644000015300001610000003343512321736015022103 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo_test import ( "io" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" . "launchpad.net/gocheck" "os" "time" ) func (s *S) TestGridFSCreate(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") before := bson.Now() gfs := db.GridFS("fs") file, err := gfs.Create("") c.Assert(err, IsNil) n, err := file.Write([]byte("some data")) c.Assert(err, IsNil) c.Assert(n, Equals, 9) err = file.Close() c.Assert(err, IsNil) after := bson.Now() // Check the file information. result := M{} err = db.C("fs.files").Find(nil).One(result) c.Assert(err, IsNil) fileId, ok := result["_id"].(bson.ObjectId) c.Assert(ok, Equals, true) c.Assert(fileId.Valid(), Equals, true) result["_id"] = "" ud, ok := result["uploadDate"].(time.Time) c.Assert(ok, Equals, true) c.Assert(ud.After(before) && ud.Before(after), Equals, true) result["uploadDate"] = "" expected := M{ "_id": "", "length": 9, "chunkSize": 262144, "uploadDate": "", "md5": "1e50210a0202497fb79bc38b6ade6c34", } c.Assert(result, DeepEquals, expected) // Check the chunk. result = M{} err = db.C("fs.chunks").Find(nil).One(result) c.Assert(err, IsNil) chunkId, ok := result["_id"].(bson.ObjectId) c.Assert(ok, Equals, true) c.Assert(chunkId.Valid(), Equals, true) result["_id"] = "" expected = M{ "_id": "", "files_id": fileId, "n": 0, "data": []byte("some data"), } c.Assert(result, DeepEquals, expected) // Check that an index was created. indexes, err := db.C("fs.chunks").Indexes() c.Assert(err, IsNil) c.Assert(len(indexes), Equals, 2) c.Assert(indexes[1].Key, DeepEquals, []string{"files_id", "n"}) } func (s *S) TestGridFSFileDetails(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") gfs := db.GridFS("fs") file, err := gfs.Create("myfile1.txt") c.Assert(err, IsNil) n, err := file.Write([]byte("some")) c.Assert(err, IsNil) c.Assert(n, Equals, 4) c.Assert(file.Size(), Equals, int64(4)) n, err = file.Write([]byte(" data")) c.Assert(err, IsNil) c.Assert(n, Equals, 5) c.Assert(file.Size(), Equals, int64(9)) id, _ := file.Id().(bson.ObjectId) c.Assert(id.Valid(), Equals, true) c.Assert(file.Name(), Equals, "myfile1.txt") c.Assert(file.ContentType(), Equals, "") var info interface{} err = file.GetMeta(&info) c.Assert(err, IsNil) c.Assert(info, IsNil) file.SetId("myid") file.SetName("myfile2.txt") file.SetContentType("text/plain") file.SetMeta(M{"any": "thing"}) c.Assert(file.Id(), Equals, "myid") c.Assert(file.Name(), Equals, "myfile2.txt") c.Assert(file.ContentType(), Equals, "text/plain") err = file.GetMeta(&info) c.Assert(err, IsNil) c.Assert(info, DeepEquals, bson.M{"any": "thing"}) err = file.Close() c.Assert(err, IsNil) c.Assert(file.MD5(), Equals, "1e50210a0202497fb79bc38b6ade6c34") ud := file.UploadDate() now := time.Now() c.Assert(ud.Before(now), Equals, true) c.Assert(ud.After(now.Add(-3*time.Second)), Equals, true) result := M{} err = db.C("fs.files").Find(nil).One(result) c.Assert(err, IsNil) result["uploadDate"] = "" expected := M{ "_id": "myid", "length": 9, "chunkSize": 262144, "uploadDate": "", "md5": "1e50210a0202497fb79bc38b6ade6c34", "filename": "myfile2.txt", "contentType": "text/plain", "metadata": M{"any": "thing"}, } c.Assert(result, DeepEquals, expected) } func (s *S) TestGridFSCreateWithChunking(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") gfs := db.GridFS("fs") file, err := gfs.Create("") c.Assert(err, IsNil) file.SetChunkSize(5) // Smaller than the chunk size. n, err := file.Write([]byte("abc")) c.Assert(err, IsNil) c.Assert(n, Equals, 3) // Boundary in the middle. n, err = file.Write([]byte("defg")) c.Assert(err, IsNil) c.Assert(n, Equals, 4) // Boundary at the end. n, err = file.Write([]byte("hij")) c.Assert(err, IsNil) c.Assert(n, Equals, 3) // Larger than the chunk size, with 3 chunks. n, err = file.Write([]byte("klmnopqrstuv")) c.Assert(err, IsNil) c.Assert(n, Equals, 12) err = file.Close() c.Assert(err, IsNil) // Check the file information. result := M{} err = db.C("fs.files").Find(nil).One(result) c.Assert(err, IsNil) fileId, _ := result["_id"].(bson.ObjectId) c.Assert(fileId.Valid(), Equals, true) result["_id"] = "" result["uploadDate"] = "" expected := M{ "_id": "", "length": 22, "chunkSize": 5, "uploadDate": "", "md5": "44a66044834cbe55040089cabfc102d5", } c.Assert(result, DeepEquals, expected) // Check the chunks. iter := db.C("fs.chunks").Find(nil).Sort("n").Iter() dataChunks := []string{"abcde", "fghij", "klmno", "pqrst", "uv"} for i := 0; ; i++ { result = M{} if !iter.Next(result) { if i != 5 { c.Fatalf("Expected 5 chunks, got %d", i) } break } c.Assert(iter.Close(), IsNil) result["_id"] = "" expected = M{ "_id": "", "files_id": fileId, "n": i, "data": []byte(dataChunks[i]), } c.Assert(result, DeepEquals, expected) } } func (s *S) TestGridFSOpenNotFound(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") gfs := db.GridFS("fs") file, err := gfs.OpenId("non-existent") c.Assert(err == mgo.ErrNotFound, Equals, true) c.Assert(file, IsNil) file, err = gfs.Open("non-existent") c.Assert(err == mgo.ErrNotFound, Equals, true) c.Assert(file, IsNil) } func (s *S) TestGridFSReadAll(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") gfs := db.GridFS("fs") file, err := gfs.Create("") c.Assert(err, IsNil) id := file.Id() file.SetChunkSize(5) n, err := file.Write([]byte("abcdefghijklmnopqrstuv")) c.Assert(err, IsNil) c.Assert(n, Equals, 22) err = file.Close() c.Assert(err, IsNil) file, err = gfs.OpenId(id) c.Assert(err, IsNil) b := make([]byte, 30) n, err = file.Read(b) c.Assert(n, Equals, 22) c.Assert(err, IsNil) n, err = file.Read(b) c.Assert(n, Equals, 0) c.Assert(err == io.EOF, Equals, true) err = file.Close() c.Assert(err, IsNil) } func (s *S) TestGridFSReadChunking(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") gfs := db.GridFS("fs") file, err := gfs.Create("") c.Assert(err, IsNil) id := file.Id() file.SetChunkSize(5) n, err := file.Write([]byte("abcdefghijklmnopqrstuv")) c.Assert(err, IsNil) c.Assert(n, Equals, 22) err = file.Close() c.Assert(err, IsNil) file, err = gfs.OpenId(id) c.Assert(err, IsNil) b := make([]byte, 30) // Smaller than the chunk size. n, err = file.Read(b[:3]) c.Assert(err, IsNil) c.Assert(n, Equals, 3) c.Assert(b[:3], DeepEquals, []byte("abc")) // Boundary in the middle. n, err = file.Read(b[:4]) c.Assert(err, IsNil) c.Assert(n, Equals, 4) c.Assert(b[:4], DeepEquals, []byte("defg")) // Boundary at the end. n, err = file.Read(b[:3]) c.Assert(err, IsNil) c.Assert(n, Equals, 3) c.Assert(b[:3], DeepEquals, []byte("hij")) // Larger than the chunk size, with 3 chunks. n, err = file.Read(b) c.Assert(err, IsNil) c.Assert(n, Equals, 12) c.Assert(b[:12], DeepEquals, []byte("klmnopqrstuv")) n, err = file.Read(b) c.Assert(n, Equals, 0) c.Assert(err == io.EOF, Equals, true) err = file.Close() c.Assert(err, IsNil) } func (s *S) TestGridFSOpen(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") gfs := db.GridFS("fs") file, err := gfs.Create("myfile.txt") c.Assert(err, IsNil) file.Write([]byte{'1'}) file.Close() file, err = gfs.Create("myfile.txt") c.Assert(err, IsNil) file.Write([]byte{'2'}) file.Close() file, err = gfs.Open("myfile.txt") c.Assert(err, IsNil) defer file.Close() var b [1]byte _, err = file.Read(b[:]) c.Assert(err, IsNil) c.Assert(string(b[:]), Equals, "2") } func (s *S) TestGridFSSeek(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") gfs := db.GridFS("fs") file, err := gfs.Create("") c.Assert(err, IsNil) id := file.Id() file.SetChunkSize(5) n, err := file.Write([]byte("abcdefghijklmnopqrstuv")) c.Assert(err, IsNil) c.Assert(n, Equals, 22) err = file.Close() c.Assert(err, IsNil) b := make([]byte, 5) file, err = gfs.OpenId(id) c.Assert(err, IsNil) o, err := file.Seek(3, os.SEEK_SET) c.Assert(err, IsNil) c.Assert(o, Equals, int64(3)) _, err = file.Read(b) c.Assert(err, IsNil) c.Assert(b, DeepEquals, []byte("defgh")) o, err = file.Seek(5, os.SEEK_CUR) c.Assert(err, IsNil) c.Assert(o, Equals, int64(13)) _, err = file.Read(b) c.Assert(err, IsNil) c.Assert(b, DeepEquals, []byte("nopqr")) o, err = file.Seek(-10, os.SEEK_END) c.Assert(err, IsNil) c.Assert(o, Equals, int64(12)) _, err = file.Read(b) c.Assert(err, IsNil) c.Assert(b, DeepEquals, []byte("mnopq")) o, err = file.Seek(8, os.SEEK_SET) c.Assert(err, IsNil) c.Assert(o, Equals, int64(8)) _, err = file.Read(b) c.Assert(err, IsNil) c.Assert(b, DeepEquals, []byte("ijklm")) // Trivial seek forward within same chunk. Already // got the data, shouldn't touch the database. sent := mgo.GetStats().SentOps o, err = file.Seek(1, os.SEEK_CUR) c.Assert(err, IsNil) c.Assert(o, Equals, int64(14)) c.Assert(mgo.GetStats().SentOps, Equals, sent) _, err = file.Read(b) c.Assert(err, IsNil) c.Assert(b, DeepEquals, []byte("opqrs")) // Try seeking past end of file. file.Seek(3, os.SEEK_SET) o, err = file.Seek(23, os.SEEK_SET) c.Assert(err, ErrorMatches, "Seek past end of file") c.Assert(o, Equals, int64(3)) } func (s *S) TestGridFSRemoveId(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") gfs := db.GridFS("fs") file, err := gfs.Create("myfile.txt") c.Assert(err, IsNil) file.Write([]byte{'1'}) file.Close() file, err = gfs.Create("myfile.txt") c.Assert(err, IsNil) file.Write([]byte{'2'}) id := file.Id() file.Close() err = gfs.RemoveId(id) c.Assert(err, IsNil) file, err = gfs.Open("myfile.txt") c.Assert(err, IsNil) defer file.Close() var b [1]byte _, err = file.Read(b[:]) c.Assert(err, IsNil) c.Assert(string(b[:]), Equals, "1") n, err := db.C("fs.chunks").Find(M{"files_id": id}).Count() c.Assert(err, IsNil) c.Assert(n, Equals, 0) } func (s *S) TestGridFSRemove(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") gfs := db.GridFS("fs") file, err := gfs.Create("myfile.txt") c.Assert(err, IsNil) file.Write([]byte{'1'}) file.Close() file, err = gfs.Create("myfile.txt") c.Assert(err, IsNil) file.Write([]byte{'2'}) file.Close() err = gfs.Remove("myfile.txt") c.Assert(err, IsNil) _, err = gfs.Open("myfile.txt") c.Assert(err == mgo.ErrNotFound, Equals, true) n, err := db.C("fs.chunks").Find(nil).Count() c.Assert(err, IsNil) c.Assert(n, Equals, 0) } func (s *S) TestGridFSOpenNext(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() db := session.DB("mydb") gfs := db.GridFS("fs") file, err := gfs.Create("myfile1.txt") c.Assert(err, IsNil) file.Write([]byte{'1'}) file.Close() file, err = gfs.Create("myfile2.txt") c.Assert(err, IsNil) file.Write([]byte{'2'}) file.Close() var f *mgo.GridFile var b [1]byte iter := gfs.Find(nil).Sort("-filename").Iter() ok := gfs.OpenNext(iter, &f) c.Assert(ok, Equals, true) c.Check(f.Name(), Equals, "myfile2.txt") _, err = f.Read(b[:]) c.Assert(err, IsNil) c.Assert(string(b[:]), Equals, "2") ok = gfs.OpenNext(iter, &f) c.Assert(ok, Equals, true) c.Check(f.Name(), Equals, "myfile1.txt") _, err = f.Read(b[:]) c.Assert(err, IsNil) c.Assert(string(b[:]), Equals, "1") ok = gfs.OpenNext(iter, &f) c.Assert(ok, Equals, false) c.Assert(iter.Close(), IsNil) c.Assert(f, IsNil) // Do it again with a more restrictive query to make sure // it's actually taken into account. iter = gfs.Find(bson.M{"filename": "myfile1.txt"}).Iter() ok = gfs.OpenNext(iter, &f) c.Assert(ok, Equals, true) c.Check(f.Name(), Equals, "myfile1.txt") ok = gfs.OpenNext(iter, &f) c.Assert(ok, Equals, false) c.Assert(iter.Close(), IsNil) c.Assert(f, IsNil) } juju-core_1.18.1/src/labix.org/v2/mgo/testdb/0000755000015300001610000000000012321735660020521 5ustar jenkinsjenkinsjuju-core_1.18.1/src/labix.org/v2/mgo/testdb/dropall.js0000644000015300001610000000206612321735660022520 0ustar jenkinsjenkins var ports = [40001, 40002, 40011, 40012, 40013, 40021, 40022, 40023, 40041, 40101, 40102, 40103, 40201, 40202, 40203] var auth = [40002, 40103, 40203, 40031] for (var i in ports) { var port = ports[i] var server = "localhost:" + port var mongo = new Mongo("localhost:" + port) var admin = mongo.getDB("admin") for (var j in auth) { if (auth[j] == port) { admin.auth("root", "rapadura") break } } var result = admin.runCommand({"listDatabases": 1}) // Why is the command returning undefined!? while (typeof result.databases == "undefined") { print("dropall.js: listing databases of :" + port + " got:", result) result = admin.runCommand({"listDatabases": 1}) } var dbs = result.databases for (var j = 0; j != dbs.length; j++) { var db = dbs[j] switch (db.name) { case "admin": case "local": case "config": break default: mongo.getDB(db.name).dropDatabase() } } } // vim:ts=4:sw=4:et juju-core_1.18.1/src/labix.org/v2/mgo/testdb/wait.js0000644000015300001610000000377012321735660022032 0ustar jenkinsjenkins// We know the master of the first set (pri=1), but not of the second. var settings = {} var rs1cfg = {_id: "rs1", members: [{_id: 1, host: "127.0.0.1:40011", priority: 1}, {_id: 2, host: "127.0.0.1:40012", priority: 0}, {_id: 3, host: "127.0.0.1:40013", priority: 0}]} var rs2cfg = {_id: "rs2", members: [{_id: 1, host: "127.0.0.1:40021", priority: 1}, {_id: 2, host: "127.0.0.1:40022", priority: 1}, {_id: 3, host: "127.0.0.1:40023", priority: 0}]} var rs3cfg = {_id: "rs3", members: [{_id: 1, host: "127.0.0.1:40031", priority: 1}, {_id: 2, host: "127.0.0.1:40032", priority: 1}, {_id: 3, host: "127.0.0.1:40033", priority: 1}], settings: settings} for (var i = 0; i != 60; i++) { try { rs1a = new Mongo("127.0.0.1:40011").getDB("admin") rs2a = new Mongo("127.0.0.1:40021").getDB("admin") rs3a = new Mongo("127.0.0.1:40031").getDB("admin") rs3a.auth("root", "rapadura") db1 = new Mongo("127.0.0.1:40001").getDB("admin") db2 = new Mongo("127.0.0.1:40002").getDB("admin") break } catch(err) { print("Can't connect yet...") } sleep(1000) } function countHealthy(rs) { var status = rs.runCommand({replSetGetStatus: 1}) var count = 0 if (typeof status.members != "undefined") { for (var i = 0; i != status.members.length; i++) { var m = status.members[i] if (m.health == 1 && (m.state == 1 || m.state == 2)) { count += 1 } } } return count } var totalRSMembers = rs1cfg.members.length + rs2cfg.members.length + rs3cfg.members.length for (var i = 0; i != 60; i++) { var count = countHealthy(rs1a) + countHealthy(rs2a) + countHealthy(rs3a) print("Replica sets have", count, "healthy nodes.") if (count == totalRSMembers) { quit(0) } sleep(1000) } print("Replica sets didn't sync up properly.") quit(12) juju-core_1.18.1/src/labix.org/v2/mgo/testdb/init.js0000644000015300001610000000652212321735660022027 0ustar jenkinsjenkins//var settings = {heartbeatSleep: 0.05, heartbeatTimeout: 0.5} var settings = {}; // We know the master of the first set (pri=1), but not of the second. var rs1cfg = {_id: "rs1", members: [{_id: 1, host: "127.0.0.1:40011", priority: 1, tags: {rs1: "a"}}, {_id: 2, host: "127.0.0.1:40012", priority: 0, tags: {rs1: "b"}}, {_id: 3, host: "127.0.0.1:40013", priority: 0, tags: {rs1: "c"}}], settings: settings} var rs2cfg = {_id: "rs2", members: [{_id: 1, host: "127.0.0.1:40021", priority: 1, tags: {rs2: "a"}}, {_id: 2, host: "127.0.0.1:40022", priority: 1, tags: {rs2: "b"}}, {_id: 3, host: "127.0.0.1:40023", priority: 1, tags: {rs2: "c"}}], settings: settings} var rs3cfg = {_id: "rs3", members: [{_id: 1, host: "127.0.0.1:40031", priority: 1, tags: {rs3: "a"}}, {_id: 2, host: "127.0.0.1:40032", priority: 1, tags: {rs3: "b"}}, {_id: 3, host: "127.0.0.1:40033", priority: 1, tags: {rs3: "c"}}], settings: settings} for (var i = 0; i != 60; i++) { try { db1 = new Mongo("127.0.0.1:40001").getDB("admin") db2 = new Mongo("127.0.0.1:40002").getDB("admin") rs1a = new Mongo("127.0.0.1:40011").getDB("admin") rs2a = new Mongo("127.0.0.1:40021").getDB("admin") rs3a = new Mongo("127.0.0.1:40031").getDB("admin") break } catch(err) { print("Can't connect yet...") } sleep(1000) } rs1a.runCommand({replSetInitiate: rs1cfg}) rs2a.runCommand({replSetInitiate: rs2cfg}) rs3a.runCommand({replSetInitiate: rs3cfg}) function configShards() { cfg1 = new Mongo("127.0.0.1:40201").getDB("admin") cfg1.runCommand({addshard: "127.0.0.1:40001"}) cfg1.runCommand({addshard: "rs1/127.0.0.1:40011"}) cfg2 = new Mongo("127.0.0.1:40202").getDB("admin") cfg2.runCommand({addshard: "rs2/127.0.0.1:40021"}) cfg3 = new Mongo("127.0.0.1:40203").getDB("admin") cfg3.runCommand({addshard: "rs3/127.0.0.1:40031"}) } function configAuth() { var addrs = ["127.0.0.1:40002", "127.0.0.1:40203", "127.0.0.1:40031"] for (var i in addrs) { db = new Mongo(addrs[i]).getDB("admin") db.addUser("root", "rapadura") db.auth("root", "rapadura") if (db.serverBuildInfo().versionArray >= [2, 4]) { db.addUser({user: "reader", pwd: "rapadura", roles: ["readAnyDatabase"]}) } else { db.addUser("reader", "rapadura", true) } } } function countHealthy(rs) { var status = rs.runCommand({replSetGetStatus: 1}) var count = 0 if (typeof status.members != "undefined") { for (var i = 0; i != status.members.length; i++) { var m = status.members[i] if (m.health == 1 && (m.state == 1 || m.state == 2)) { count += 1 } } } return count } var totalRSMembers = rs1cfg.members.length + rs2cfg.members.length + rs3cfg.members.length for (var i = 0; i != 60; i++) { var count = countHealthy(rs1a) + countHealthy(rs2a) + countHealthy(rs3a) print("Replica sets have", count, "healthy nodes.") if (count == totalRSMembers) { sleep(2000) configShards() configAuth() quit(0) } sleep(1000) } print("Replica sets didn't sync up properly.") quit(12) // vim:ts=4:sw=4:et juju-core_1.18.1/src/labix.org/v2/mgo/testdb/setup.sh0000755000015300001610000000236512321735660022226 0ustar jenkinsjenkins#!/bin/sh -e start() { mkdir _testdb cd _testdb mkdir db1 db2 rs1a rs1b rs1c rs2a rs2b rs2c rs3a rs3b rs3c rs4a cfg1 cfg2 cfg3 ln -s ../testdb/supervisord.conf supervisord.conf echo keyfile > keyfile chmod 600 keyfile echo "Running supervisord..." supervisord || ( echo "Supervisord failed executing ($?)" && exit 1 ) COUNT=$(grep '^\[program' supervisord.conf | wc -l) echo "Supervisord is up, starting $COUNT processes..." for i in $(seq 10); do RUNNING=$(supervisorctl status | grep RUNNING | wc -l) echo "$RUNNING processes running..." if [ x$COUNT = x$RUNNING ]; then echo "Running setup.js with mongo..." mongo --nodb ../testdb/init.js exit 0 fi sleep 1 done echo "Failed to start all processes. Check out what's up at $PWD now!" exit 1 } stop() { if [ -d _testdb ]; then echo "Shutting down test cluster..." (cd _testdb && supervisorctl shutdown) rm -rf _testdb fi } if [ ! -f suite_test.go ]; then echo "This script must be run from within the source directory." exit 1 fi case "$1" in start) start ;; stop) stop ;; esac # vim:ts=4:sw=4:et juju-core_1.18.1/src/labix.org/v2/mgo/testdb/supervisord.conf0000644000015300001610000000772612321735660023771 0ustar jenkinsjenkins[supervisord] logfile = %(here)s/supervisord.log pidfile = %(here)s/supervisord.pid directory = %(here)s #nodaemon = true [inet_http_server] port = 127.0.0.1:9001 [supervisorctl] serverurl = http://127.0.0.1:9001 [rpcinterface:supervisor] supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface [program:db1] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --dbpath %(here)s/db1 --bind_ip=127.0.0.1 --port 40001 [program:db2] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --dbpath %(here)s/db2 --bind_ip=127.0.0.1 --port 40002 --auth [program:rs1a] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs1 --dbpath %(here)s/rs1a --bind_ip=127.0.0.1 --port 40011 [program:rs1b] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs1 --dbpath %(here)s/rs1b --bind_ip=127.0.0.1 --port 40012 [program:rs1c] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs1 --dbpath %(here)s/rs1c --bind_ip=127.0.0.1 --port 40013 [program:rs2a] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs2 --dbpath %(here)s/rs2a --bind_ip=127.0.0.1 --port 40021 [program:rs2b] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs2 --dbpath %(here)s/rs2b --bind_ip=127.0.0.1 --port 40022 [program:rs2c] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs2 --dbpath %(here)s/rs2c --bind_ip=127.0.0.1 --port 40023 [program:rs3a] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs3 --dbpath %(here)s/rs3a --bind_ip=127.0.0.1 --port 40031 --auth --keyFile=%(here)s/keyfile [program:rs3b] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs3 --dbpath %(here)s/rs3b --bind_ip=127.0.0.1 --port 40032 --auth --keyFile=%(here)s/keyfile [program:rs3c] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs3 --dbpath %(here)s/rs3c --bind_ip=127.0.0.1 --port 40033 --auth --keyFile=%(here)s/keyfile [program:rs4a] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs4 --dbpath %(here)s/rs4a --bind_ip=127.0.0.1 --port 40041 [program:cfg1] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --configsvr --dbpath %(here)s/cfg1 --bind_ip=127.0.0.1 --port 40101 [program:cfg2] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --configsvr --dbpath %(here)s/cfg2 --bind_ip=127.0.0.1 --port 40102 [program:cfg3] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --configsvr --dbpath %(here)s/cfg3 --bind_ip=127.0.0.1 --port 40103 --auth --keyFile=%(here)s/keyfile [program:s1] command = mongos --configdb 127.0.0.1:40101 --bind_ip=127.0.0.1 --port 40201 --chunkSize 1 [program:s2] command = mongos --configdb 127.0.0.1:40102 --bind_ip=127.0.0.1 --port 40202 --chunkSize 1 [program:s3] command = mongos --configdb 127.0.0.1:40103 --bind_ip=127.0.0.1 --port 40203 --chunkSize 1 --keyFile=%(here)s/keyfile juju-core_1.18.1/src/labix.org/v2/mgo/suite_test.go0000644000015300001610000001263112321735660021756 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo_test import ( "errors" "flag" "fmt" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" . "launchpad.net/gocheck" "net" "os/exec" "strconv" "syscall" "testing" "time" ) var fast = flag.Bool("fast", false, "Skip slow tests") type M bson.M type cLogger C func (c *cLogger) Output(calldepth int, s string) error { ns := time.Now().UnixNano() t := float64(ns%100e9) / 1e9 ((*C)(c)).Logf("[LOG] %.05f %s", t, s) return nil } func TestAll(t *testing.T) { TestingT(t) } type S struct { session *mgo.Session stopped bool build mgo.BuildInfo frozen []string } func (s *S) versionAtLeast(v ...int) bool { for i := range v { if i == len(s.build.VersionArray) { return false } if s.build.VersionArray[i] < v[i] { return false } } return true } var _ = Suite(&S{}) func (s *S) SetUpSuite(c *C) { mgo.SetDebug(true) mgo.SetStats(true) s.StartAll() session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) s.build, err = session.BuildInfo() c.Check(err, IsNil) session.Close() } func (s *S) SetUpTest(c *C) { err := run("mongo --nodb testdb/dropall.js") if err != nil { panic(err.Error()) } mgo.SetLogger((*cLogger)(c)) mgo.ResetStats() } func (s *S) TearDownTest(c *C) { if s.stopped { s.StartAll() } for _, host := range s.frozen { if host != "" { s.Thaw(host) } } var stats mgo.Stats for i := 0; ; i++ { stats = mgo.GetStats() if stats.SocketsInUse == 0 && stats.SocketsAlive == 0 { break } if i == 20 { c.Fatal("Test left sockets in a dirty state") } c.Logf("Waiting for sockets to die: %d in use, %d alive", stats.SocketsInUse, stats.SocketsAlive) time.Sleep(500 * time.Millisecond) } for i := 0; ; i++ { stats = mgo.GetStats() if stats.Clusters == 0 { break } if i == 60 { c.Fatal("Test left clusters alive") } c.Logf("Waiting for clusters to die: %d alive", stats.Clusters) time.Sleep(1 * time.Second) } } func (s *S) Stop(host string) { // Give a moment for slaves to sync and avoid getting rollback issues. time.Sleep(2 * time.Second) err := run("cd _testdb && supervisorctl stop " + supvName(host)) if err != nil { panic(err) } s.stopped = true } func (s *S) pid(host string) int { output, err := exec.Command("lsof", "-iTCP:"+hostPort(host), "-sTCP:LISTEN", "-Fp").CombinedOutput() if err != nil { panic(err) } pidstr := string(output[1 : len(output)-1]) pid, err := strconv.Atoi(pidstr) if err != nil { panic("cannot convert pid to int: " + pidstr) } return pid } func (s *S) Freeze(host string) { err := syscall.Kill(s.pid(host), syscall.SIGSTOP) if err != nil { panic(err) } s.frozen = append(s.frozen, host) } func (s *S) Thaw(host string) { err := syscall.Kill(s.pid(host), syscall.SIGCONT) if err != nil { panic(err) } for i, frozen := range s.frozen { if frozen == host { s.frozen[i] = "" } } } func (s *S) StartAll() { // Restart any stopped nodes. run("cd _testdb && supervisorctl start all") err := run("cd testdb && mongo --nodb wait.js") if err != nil { panic(err) } s.stopped = false } func run(command string) error { output, err := exec.Command("/bin/sh", "-c", command).CombinedOutput() if err != nil { msg := fmt.Sprintf("Failed to execute: %s: %s\n%s", command, err.Error(), string(output)) return errors.New(msg) } return nil } var supvNames = map[string]string{ "40001": "db1", "40002": "db2", "40011": "rs1a", "40012": "rs1b", "40013": "rs1c", "40021": "rs2a", "40022": "rs2b", "40023": "rs2c", "40031": "rs3a", "40032": "rs3b", "40033": "rs3c", "40041": "rs4a", "40101": "cfg1", "40102": "cfg2", "40103": "cfg3", "40201": "s1", "40202": "s2", "40203": "s3", } // supvName returns the supervisord name for the given host address. func supvName(host string) string { host, port, err := net.SplitHostPort(host) if err != nil { panic(err) } name, ok := supvNames[port] if !ok { panic("Unknown host: " + host) } return name } func hostPort(host string) string { _, port, err := net.SplitHostPort(host) if err != nil { panic(err) } return port } juju-core_1.18.1/src/labix.org/v2/mgo/session.go0000644000015300001610000030246112321736015021247 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo import ( "crypto/md5" "encoding/hex" "errors" "fmt" "labix.org/v2/mgo/bson" "math" "net" "reflect" "sort" "strconv" "strings" "sync" "time" ) type mode int const ( Eventual mode = 0 Monotonic mode = 1 Strong mode = 2 ) // When changing the Session type, check if newSession and copySession // need to be updated too. type Session struct { m sync.RWMutex cluster_ *mongoCluster slaveSocket *mongoSocket masterSocket *mongoSocket slaveOk bool consistency mode queryConfig query safeOp *queryOp syncTimeout time.Duration sockTimeout time.Duration defaultdb string dialAuth *authInfo auth []authInfo } type Database struct { Session *Session Name string } type Collection struct { Database *Database Name string // "collection" FullName string // "db.collection" } type Query struct { m sync.Mutex session *Session query // Enables default settings in session. } type query struct { op queryOp prefetch float64 limit int32 } type getLastError struct { CmdName int "getLastError" W interface{} "w,omitempty" WTimeout int "wtimeout,omitempty" FSync bool "fsync,omitempty" J bool "j,omitempty" } type Iter struct { m sync.Mutex gotReply sync.Cond session *Session server *mongoServer docData queue err error op getMoreOp prefetch float64 limit int32 docsToReceive int docsBeforeMore int timeout time.Duration timedout bool } var ErrNotFound = errors.New("not found") const defaultPrefetch = 0.25 // Dial establishes a new session to the cluster identified by the given seed // server(s). The session will enable communication with all of the servers in // the cluster, so the seed servers are used only to find out about the cluster // topology. // // Dial will timeout after 10 seconds if a server isn't reached. The returned // session will timeout operations after one minute by default if servers // aren't available. To customize the timeout, see DialWithTimeout, // SetSyncTimeout, and SetSocketTimeout. // // This method is generally called just once for a given cluster. Further // sessions to the same cluster are then established using the New or Copy // methods on the obtained session. This will make them share the underlying // cluster, and manage the pool of connections appropriately. // // Once the session is not useful anymore, Close must be called to release the // resources appropriately. // // The seed servers must be provided in the following format: // // [mongodb://][user:pass@]host1[:port1][,host2[:port2],...][/database][?options] // // For example, it may be as simple as: // // localhost // // Or more involved like: // // mongodb://myuser:mypass@localhost:40001,otherhost:40001/mydb // // If the port number is not provided for a server, it defaults to 27017. // // The username and password provided in the URL will be used to authenticate // into the database named after the slash at the end of the host names, or // into the "admin" database if none is provided. The authentication information // will persist in sessions obtained through the New method as well. // // The following connection options are supported after the question mark: // // connect=direct // // This option will disable the automatic replica set server // discovery logic, and will only use the servers provided. // This enables forcing the communication with a specific // server or set of servers (even if they are slaves). Note // that to talk to a slave you'll need to relax the consistency // requirements using a Monotonic or Eventual mode via SetMode. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Connections // func Dial(url string) (*Session, error) { session, err := DialWithTimeout(url, 10 * time.Second) if err == nil { session.SetSyncTimeout(1 * time.Minute) session.SetSocketTimeout(1 * time.Minute) } return session, err } // DialWithTimeout works like Dial, but uses timeout as the amount of time to // wait for a server to respond when first connecting and also on follow up // operations in the session. If timeout is zero, the call may block // forever waiting for a connection to be made. // // See SetSyncTimeout for customizing the timeout for the session. func DialWithTimeout(url string, timeout time.Duration) (*Session, error) { uinfo, err := parseURL(url) if err != nil { return nil, err } direct := false for k, v := range uinfo.options { switch k { case "connect": if v == "direct" { direct = true break } if v == "replicaSet" { break } fallthrough default: return nil, errors.New("Unsupported connection URL option: " + k + "=" + v) } } info := DialInfo{ Addrs: uinfo.addrs, Direct: direct, Timeout: timeout, Username: uinfo.user, Password: uinfo.pass, Database: uinfo.db, } return DialWithInfo(&info) } // DialInfo holds options for establishing a session with a MongoDB cluster. // To use a URL, see the Dial function. type DialInfo struct { // Addrs holds the addresses for the seed servers. Addrs []string // Direct informs whether to establish connections only with the // specified seed servers, or to obtain information for the whole // cluster and establish connections with further servers too. Direct bool // Timeout is the amount of time to wait for a server to respond when // first connecting and on follow up operations in the session. If // timeout is zero, the call may block forever waiting for a connection // to be established. Timeout time.Duration // Database is the database name used during the initial authentication. // If set, the value is also returned as the default result from the // Session.DB method, in place of "test". Database string // Username and Password inform the credentials for the initial // authentication done against Database, if that is set, // or the "admin" database otherwise. See the Session.Login method too. Username string Password string // Dial optionally specifies the dial function for creating connections. // At the moment addr will have type *net.TCPAddr, but other types may // be provided in the future, so check and fail if necessary. Dial func(addr net.Addr) (net.Conn, error) } // DialWithInfo establishes a new session to the cluster identified by info. func DialWithInfo(info *DialInfo) (*Session, error) { addrs := make([]string, len(info.Addrs)) for i, addr := range info.Addrs { p := strings.LastIndexAny(addr, "]:") if p == -1 || addr[p] != ':' { // XXX This is untested. The test suite doesn't use the standard port. addr += ":27017" } addrs[i] = addr } cluster := newCluster(addrs, info.Direct, info.Dial) session := newSession(Eventual, cluster, info.Timeout) session.defaultdb = info.Database if session.defaultdb == "" { session.defaultdb = "test" } if info.Username != "" { db := info.Database if db == "" { db = "admin" } session.dialAuth = &authInfo{db, info.Username, info.Password} session.auth = []authInfo{*session.dialAuth} } cluster.Release() // People get confused when we return a session that is not actually // established to any servers yet (e.g. what if url was wrong). So, // ping the server to ensure there's someone there, and abort if it // fails. if err := session.Ping(); err != nil { session.Close() return nil, err } session.SetMode(Strong, true) return session, nil } func isOptSep(c rune) bool { return c == ';' || c == '&' } type urlInfo struct { addrs []string user string pass string db string options map[string]string } func parseURL(url string) (*urlInfo, error) { if strings.HasPrefix(url, "mongodb://") { url = url[10:] } info := &urlInfo{options: make(map[string]string)} if c := strings.Index(url, "?"); c != -1 { for _, pair := range strings.FieldsFunc(url[c+1:], isOptSep) { l := strings.SplitN(pair, "=", 2) if len(l) != 2 || l[0] == "" || l[1] == "" { return nil, errors.New("Connection option must be key=value: " + pair) } info.options[l[0]] = l[1] } url = url[:c] } if c := strings.Index(url, "@"); c != -1 { pair := strings.SplitN(url[:c], ":", 2) if len(pair) != 2 || pair[0] == "" { return nil, errors.New("Credentials must be provided as user:pass@host") } info.user = pair[0] info.pass = pair[1] url = url[c+1:] } if c := strings.Index(url, "/"); c != -1 { info.db = url[c+1:] url = url[:c] } info.addrs = strings.Split(url, ",") return info, nil } func newSession(consistency mode, cluster *mongoCluster, timeout time.Duration) (session *Session) { cluster.Acquire() session = &Session{cluster_: cluster, syncTimeout: timeout, sockTimeout: timeout} debugf("New session %p on cluster %p", session, cluster) session.SetMode(consistency, true) session.SetSafe(&Safe{}) session.queryConfig.prefetch = defaultPrefetch return session } func copySession(session *Session, keepAuth bool) (s *Session) { cluster := session.cluster() cluster.Acquire() if session.masterSocket != nil { session.masterSocket.Acquire() } if session.slaveSocket != nil { session.slaveSocket.Acquire() } var auth []authInfo if keepAuth { auth = make([]authInfo, len(session.auth)) copy(auth, session.auth) } else if session.dialAuth != nil { auth = []authInfo{*session.dialAuth} } scopy := *session scopy.m = sync.RWMutex{} scopy.auth = auth s = &scopy debugf("New session %p on cluster %p (copy from %p)", s, cluster, session) return s } // LiveServers returns a list of server addresses which are // currently known to be alive. func (s *Session) LiveServers() (addrs []string) { s.m.RLock() addrs = s.cluster().LiveServers() s.m.RUnlock() return addrs } // DB returns a value representing the named database. If name // is empty, the database name provided in the dialed URL is // used instead. If that is also empty, "test" is used as a // fallback in a way equivalent to the mongo shell. // // Creating this value is a very lightweight operation, and // involves no network communication. func (s *Session) DB(name string) *Database { if name == "" { name = s.defaultdb } return &Database{s, name} } // C returns a value representing the named collection. // // Creating this value is a very lightweight operation, and // involves no network communication. func (db *Database) C(name string) *Collection { return &Collection{db, name, db.Name + "." + name} } // With returns a copy of db that uses session s. func (db *Database) With(s *Session) *Database { newdb := *db newdb.Session = s return &newdb } // With returns a copy of c that uses session s. func (c *Collection) With(s *Session) *Collection { newdb := *c.Database newdb.Session = s newc := *c newc.Database = &newdb return &newc } // GridFS returns a GridFS value representing collections in db that // follow the standard GridFS specification. // The provided prefix (sometimes known as root) will determine which // collections to use, and is usually set to "fs" when there is a // single GridFS in the database. // // See the GridFS Create, Open, and OpenId methods for more details. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/GridFS // http://www.mongodb.org/display/DOCS/GridFS+Tools // http://www.mongodb.org/display/DOCS/GridFS+Specification // func (db *Database) GridFS(prefix string) *GridFS { return newGridFS(db, prefix) } // Run issues the provided command against the database and unmarshals // its result in the respective argument. The cmd argument may be either // a string with the command name itself, in which case an empty document of // the form bson.M{cmd: 1} will be used, or it may be a full command document. // // Note that MongoDB considers the first marshalled key as the command // name, so when providing a command with options, it's important to // use an ordering-preserving document, such as a struct value or an // instance of bson.D. For instance: // // db.Run(bson.D{{"create", "mycollection"}, {"size", 1024}}) // // For privilleged commands typically run against the "admin" database, see // the Run method in the Session type. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Commands // http://www.mongodb.org/display/DOCS/List+of+Database+CommandSkips // func (db *Database) Run(cmd interface{}, result interface{}) error { if name, ok := cmd.(string); ok { cmd = bson.D{{name, 1}} } return db.C("$cmd").Find(cmd).One(result) } // Login authenticates against MongoDB with the provided credentials. The // authentication is valid for the whole session and will stay valid until // Logout is explicitly called for the same database, or the session is // closed. // // Concurrent Login calls will work correctly. func (db *Database) Login(user, pass string) (err error) { session := db.Session dbname := db.Name socket, err := session.acquireSocket(true) if err != nil { return err } defer socket.Release() err = socket.Login(dbname, user, pass) if err != nil { return err } session.m.Lock() defer session.m.Unlock() for _, a := range session.auth { if a.db == dbname { a.user = user a.pass = pass return nil } } session.auth = append(session.auth, authInfo{dbname, user, pass}) return nil } func (s *Session) socketLogin(socket *mongoSocket) error { for _, a := range s.auth { if err := socket.Login(a.db, a.user, a.pass); err != nil { return err } } return nil } // Logout removes any established authentication credentials for the database. func (db *Database) Logout() { session := db.Session dbname := db.Name session.m.Lock() found := false for i, a := range session.auth { if a.db == dbname { copy(session.auth[i:], session.auth[i+1:]) session.auth = session.auth[:len(session.auth)-1] found = true break } } if found { if session.masterSocket != nil { session.masterSocket.Logout(dbname) } if session.slaveSocket != nil { session.slaveSocket.Logout(dbname) } } session.m.Unlock() } // LogoutAll removes all established authentication credentials for the session. func (s *Session) LogoutAll() { s.m.Lock() for _, a := range s.auth { if s.masterSocket != nil { s.masterSocket.Logout(a.db) } if s.slaveSocket != nil { s.slaveSocket.Logout(a.db) } } s.auth = s.auth[0:0] s.m.Unlock() } // User represents a MongoDB user. // // Relevant documentation: // // http://docs.mongodb.org/manual/reference/privilege-documents/ // http://docs.mongodb.org/manual/reference/user-privileges/ // type User struct { // Username is how the user identifies itself to the system. Username string `bson:"user"` // Password is the plaintext password for the user. If set, // the UpsertUser method will hash it into PasswordHash and // unset it before the user is added to the database. Password string `bson:",omitempty"` // PasswordHash is the MD5 hash of Username+":mongo:"+Password. PasswordHash string `bson:"pwd,omitempty"` // UserSource indicates where to look for this user's credentials. // It may be set to a database name, or to "$external" for // consulting an external resource such as Kerberos. UserSource // must not be set if Password or PasswordHash are present. UserSource string `bson:"userSource,omitempty"` // Roles indicates the set of roles the user will be provided. // See the Role constants. Roles []Role `bson:"roles"` // OtherDBRoles allows assigning roles in other databases from // user documents inserted in the admin database. This field // only works in the admin database. OtherDBRoles map[string][]Role `bson:"otherDBRoles,omitempty"` } type Role string const ( // Relevant documentation: // // http://docs.mongodb.org/manual/reference/user-privileges/ // RoleRead Role = "read" RoleReadAny Role = "readAnyDatabase" RoleReadWrite Role = "readWrite" RoleReadWriteAny Role = "readWriteAnyDatabase" RoleDBAdmin Role = "dbAdmin" RoleDBAdminAny Role = "dbAdminAnyDatabase" RoleUserAdmin Role = "userAdmin" RoleUserAdminAny Role = "UserAdminAnyDatabase" RoleClusterAdmin Role = "clusterAdmin" ) // UpsertUser updates the authentication credentials and the roles for // a MongoDB user within the db database. If the named user doesn't exist // it will be created. // // This method should only be used from MongoDB 2.4 and on. For older // MongoDB releases, use the obsolete AddUser method instead. // // Relevant documentation: // // http://docs.mongodb.org/manual/reference/user-privileges/ // http://docs.mongodb.org/manual/reference/privilege-documents/ // func (db *Database) UpsertUser(user *User) error { if user.Username == "" { return fmt.Errorf("user has no Username") } if user.Password != "" { psum := md5.New() psum.Write([]byte(user.Username + ":mongo:" + user.Password)) user.PasswordHash = hex.EncodeToString(psum.Sum(nil)) user.Password = "" } if user.PasswordHash != "" && user.UserSource != "" { return fmt.Errorf("user has both Password/PasswordHash and UserSource set") } if len(user.OtherDBRoles) > 0 && db.Name != "admin" { return fmt.Errorf("user with OtherDBRoles is only supported in admin database") } var unset bson.D if user.PasswordHash == "" { unset = append(unset, bson.DocElem{"pwd", 1}) } if user.UserSource == "" { unset = append(unset, bson.DocElem{"userSource", 1}) } // user.Roles is always sent, as it's the way MongoDB distinguishes // old-style documents from new-style documents. if len(user.OtherDBRoles) == 0 { unset = append(unset, bson.DocElem{"otherDBRoles", 1}) } c := db.C("system.users") _, err := c.Upsert(bson.D{{"user", user.Username}}, bson.D{{"$unset", unset}, {"$set", user}}) return err } // AddUser creates or updates the authentication credentials of user within // the db database. // // This method is obsolete and should only be used with MongoDB 2.2 or // earlier. For MongoDB 2.4 and on, use UpsertUser instead. func (db *Database) AddUser(user, pass string, readOnly bool) error { psum := md5.New() psum.Write([]byte(user + ":mongo:" + pass)) digest := hex.EncodeToString(psum.Sum(nil)) c := db.C("system.users") _, err := c.Upsert(bson.M{"user": user}, bson.M{"$set": bson.M{"user": user, "pwd": digest, "readOnly": readOnly}}) return err } // RemoveUser removes the authentication credentials of user from the database. func (db *Database) RemoveUser(user string) error { c := db.C("system.users") return c.Remove(bson.M{"user": user}) } type indexSpec struct { Name, NS string Key bson.D Unique bool ",omitempty" DropDups bool "dropDups,omitempty" Background bool ",omitempty" Sparse bool ",omitempty" Bits, Min, Max int ",omitempty" ExpireAfter int "expireAfterSeconds,omitempty" } type Index struct { Key []string // Index key fields; prefix name with dash (-) for descending order Unique bool // Prevent two documents from having the same index key DropDups bool // Drop documents with the same index key as a previously indexed one Background bool // Build index in background and return immediately Sparse bool // Only index documents containing the Key fields ExpireAfter time.Duration // Periodically delete docs with indexed time.Time older than that. Name string // Index name, computed by EnsureIndex Bits, Min, Max int // Properties for spatial indexes } func parseIndexKey(key []string) (name string, realKey bson.D, err error) { var order interface{} for _, field := range key { raw := field if name != "" { name += "_" } var kind string if field != "" { if field[0] == '$' { if c := strings.Index(field, ":"); c > 1 && c < len(field)-1 { kind = field[1:c] field = field[c+1:] name += field + "_" + kind } } switch field[0] { case '$': // Logic above failed. Reset and error. field = "" case '@': order = "2d" field = field[1:] // The shell used to render this field as key_ instead of key_2d, // and mgo followed suit. This has been fixed in recent server // releases, and mgo followed as well. name += field + "_2d" case '-': order = -1 field = field[1:] name += field + "_-1" case '+': field = field[1:] fallthrough default: if kind == "" { order = 1 name += field + "_1" } else { order = kind } } } if field == "" || kind != "" && order != kind { return "", nil, fmt.Errorf(`Invalid index key: want "[$:][-]", got %q`, raw) } realKey = append(realKey, bson.DocElem{field, order}) } if name == "" { return "", nil, errors.New("Invalid index key: no fields provided") } return } // EnsureIndexKey ensures an index with the given key exists, creating it // if necessary. // // This example: // // err := collection.EnsureIndexKey("a", "b") // // Is equivalent to: // // err := collection.EnsureIndex(mgo.Index{Key: []string{"a", "b"}}) // // See the EnsureIndex method for more details. func (c *Collection) EnsureIndexKey(key ...string) error { return c.EnsureIndex(Index{Key: key}) } // EnsureIndex ensures an index with the given key exists, creating it with // the provided parameters if necessary. // // Once EnsureIndex returns successfully, following requests for the same index // will not contact the server unless Collection.DropIndex is used to drop the // same index, or Session.ResetIndexCache is called. // // For example: // // index := Index{ // Key: []string{"lastname", "firstname"}, // Unique: true, // DropDups: true, // Background: true, // See notes. // Sparse: true, // } // err := collection.EnsureIndex(index) // // The Key value determines which fields compose the index. The index ordering // will be ascending by default. To obtain an index with a descending order, // the field name should be prefixed by a dash (e.g. []string{"-time"}). // // If Unique is true, the index must necessarily contain only a single // document per Key. With DropDups set to true, documents with the same key // as a previously indexed one will be dropped rather than an error returned. // // If Background is true, other connections will be allowed to proceed using // the collection without the index while it's being built. Note that the // session executing EnsureIndex will be blocked for as long as it takes for // the index to be built. // // If Sparse is true, only documents containing the provided Key fields will be // included in the index. When using a sparse index for sorting, only indexed // documents will be returned. // // If ExpireAfter is non-zero, the server will periodically scan the collection // and remove documents containing an indexed time.Time field with a value // older than ExpireAfter. See the documentation for details: // // http://docs.mongodb.org/manual/tutorial/expire-data // // Other kinds of indexes are also supported through that API. Here is an example: // // index := Index{ // Key: []string{"$2d:loc"}, // Bits: 26, // } // err := collection.EnsureIndex(index) // // The example above requests the creation of a "2d" index for the "loc" field. // // The 2D index bounds may be changed using the Min and Max attributes of the // Index value. The default bound setting of (-180, 180) is suitable for // latitude/longitude pairs. // // The Bits parameter sets the precision of the 2D geohash values. If not // provided, 26 bits are used, which is roughly equivalent to 1 foot of // precision for the default (-180, 180) index bounds. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Indexes // http://www.mongodb.org/display/DOCS/Indexing+Advice+and+FAQ // http://www.mongodb.org/display/DOCS/Indexing+as+a+Background+Operation // http://www.mongodb.org/display/DOCS/Geospatial+Indexing // http://www.mongodb.org/display/DOCS/Multikeys // func (c *Collection) EnsureIndex(index Index) error { name, realKey, err := parseIndexKey(index.Key) if err != nil { return err } session := c.Database.Session cacheKey := c.FullName + "\x00" + name if session.cluster().HasCachedIndex(cacheKey) { return nil } spec := indexSpec{ Name: name, NS: c.FullName, Key: realKey, Unique: index.Unique, DropDups: index.DropDups, Background: index.Background, Sparse: index.Sparse, Bits: index.Bits, Min: index.Min, Max: index.Max, ExpireAfter: int(index.ExpireAfter / time.Second), } session = session.Clone() defer session.Close() session.SetMode(Strong, false) session.EnsureSafe(&Safe{}) db := c.Database.With(session) err = db.C("system.indexes").Insert(&spec) if err == nil { session.cluster().CacheIndex(cacheKey, true) } session.Close() return err } // DropIndex removes the index with key from the collection. // // The key value determines which fields compose the index. The index ordering // will be ascending by default. To obtain an index with a descending order, // the field name should be prefixed by a dash (e.g. []string{"-time"}). // // For example: // // err := collection.DropIndex("lastname", "firstname") // // See the EnsureIndex method for more details on indexes. func (c *Collection) DropIndex(key ...string) error { name, _, err := parseIndexKey(key) if err != nil { return err } session := c.Database.Session cacheKey := c.FullName + "\x00" + name session.cluster().CacheIndex(cacheKey, false) session = session.Clone() defer session.Close() session.SetMode(Strong, false) db := c.Database.With(session) result := struct { ErrMsg string Ok bool }{} err = db.Run(bson.D{{"dropIndexes", c.Name}, {"index", name}}, &result) if err != nil { return err } if !result.Ok { return errors.New(result.ErrMsg) } return nil } // Indexes returns a list of all indexes for the collection. // // For example, this snippet would drop all available indexes: // // indexes, err := collection.Indexes() // if err != nil { // return err // } // for _, index := range indexes { // err = collection.DropIndex(index.Key...) // if err != nil { // return err // } // } // // See the EnsureIndex method for more details on indexes. func (c *Collection) Indexes() (indexes []Index, err error) { query := c.Database.C("system.indexes").Find(bson.M{"ns": c.FullName}) iter := query.Sort("name").Iter() for { var spec indexSpec if !iter.Next(&spec) { break } index := Index{ Name: spec.Name, Key: simpleIndexKey(spec.Key), Unique: spec.Unique, DropDups: spec.DropDups, Background: spec.Background, Sparse: spec.Sparse, ExpireAfter: time.Duration(spec.ExpireAfter) * time.Second, } indexes = append(indexes, index) } err = iter.Close() return } func simpleIndexKey(realKey bson.D) (key []string) { for i := range realKey { field := realKey[i].Name vi, ok := realKey[i].Value.(int) if !ok { vf, _ := realKey[i].Value.(float64) vi = int(vf) } if vi == 1 { key = append(key, field) continue } if vi == -1 { key = append(key, "-"+field) continue } if vs, ok := realKey[i].Value.(string); ok { key = append(key, "$"+vs+":"+field) continue } panic("Got unknown index key type for field " + field) } return } // ResetIndexCache() clears the cache of previously ensured indexes. // Following requests to EnsureIndex will contact the server. func (s *Session) ResetIndexCache() { s.cluster().ResetIndexCache() } // New creates a new session with the same parameters as the original // session, including consistency, batch size, prefetching, safety mode, // etc. The returned session will use sockets from the pool, so there's // a chance that writes just performed in another session may not yet // be visible. // // Login information from the original session will not be copied over // into the new session unless it was provided through the initial URL // for the Dial function. // // See the Copy and Clone methods. // func (s *Session) New() *Session { s.m.Lock() scopy := copySession(s, false) s.m.Unlock() scopy.Refresh() return scopy } // Copy works just like New, but preserves the exact authentication // information from the original session. func (s *Session) Copy() *Session { s.m.Lock() scopy := copySession(s, true) s.m.Unlock() scopy.Refresh() return scopy } // Clone works just like Copy, but also reuses the same socket as the original // session, in case it had already reserved one due to its consistency // guarantees. This behavior ensures that writes performed in the old session // are necessarily observed when using the new session, as long as it was a // strong or monotonic session. That said, it also means that long operations // may cause other goroutines using the original session to wait. func (s *Session) Clone() *Session { s.m.Lock() scopy := copySession(s, true) s.m.Unlock() return scopy } // Close terminates the session. It's a runtime error to use a session // after it has been closed. func (s *Session) Close() { s.m.Lock() if s.cluster_ != nil { debugf("Closing session %p", s) s.unsetSocket() s.cluster_.Release() s.cluster_ = nil } s.m.Unlock() } func (s *Session) cluster() *mongoCluster { if s.cluster_ == nil { panic("Session already closed") } return s.cluster_ } // Refresh puts back any reserved sockets in use and restarts the consistency // guarantees according to the current consistency setting for the session. func (s *Session) Refresh() { s.m.Lock() s.slaveOk = s.consistency != Strong s.unsetSocket() s.m.Unlock() } // SetMode changes the consistency mode for the session. // // In the Strong consistency mode reads and writes will always be made to // the master server using a unique connection so that reads and writes are // fully consistent, ordered, and observing the most up-to-date data. // This offers the least benefits in terms of distributing load, but the // most guarantees. See also Monotonic and Eventual. // // In the Monotonic consistency mode reads may not be entirely up-to-date, // but they will always see the history of changes moving forward, the data // read will be consistent across sequential queries in the same session, // and modifications made within the session will be observed in following // queries (read-your-writes). // // In practice, the Monotonic mode is obtained by performing initial reads // against a unique connection to an arbitrary slave, if one is available, // and once the first write happens, the session connection is switched over // to the master server. This manages to distribute some of the reading // load with slaves, while maintaining some useful guarantees. // // In the Eventual consistency mode reads will be made to any slave in the // cluster, if one is available, and sequential reads will not necessarily // be made with the same connection. This means that data may be observed // out of order. Writes will of course be issued to the master, but // independent writes in the same Eventual session may also be made with // independent connections, so there are also no guarantees in terms of // write ordering (no read-your-writes guarantees either). // // The Eventual mode is the fastest and most resource-friendly, but is // also the one offering the least guarantees about ordering of the data // read and written. // // If refresh is true, in addition to ensuring the session is in the given // consistency mode, the consistency guarantees will also be reset (e.g. // a Monotonic session will be allowed to read from slaves again). This is // equivalent to calling the Refresh function. // // Shifting between Monotonic and Strong modes will keep a previously // reserved connection for the session unless refresh is true or the // connection is unsuitable (to a slave server in a Strong session). func (s *Session) SetMode(consistency mode, refresh bool) { s.m.Lock() debugf("Session %p: setting mode %d with refresh=%v (master=%p, slave=%p)", s, consistency, refresh, s.masterSocket, s.slaveSocket) s.consistency = consistency if refresh { s.slaveOk = s.consistency != Strong s.unsetSocket() } else if s.consistency == Strong { s.slaveOk = false } else if s.masterSocket == nil { s.slaveOk = true } s.m.Unlock() } // Mode returns the current consistency mode for the session. func (s *Session) Mode() mode { s.m.RLock() mode := s.consistency s.m.RUnlock() return mode } // SetSyncTimeout sets the amount of time an operation with this session // will wait before returning an error in case a connection to a usable // server can't be established. Set it to zero to wait forever. The // default value is 7 seconds. func (s *Session) SetSyncTimeout(d time.Duration) { s.m.Lock() s.syncTimeout = d s.m.Unlock() } // SetSocketTimeout sets the amount of time to wait for a non-responding // socket to the database before it is forcefully closed. func (s *Session) SetSocketTimeout(d time.Duration) { s.m.Lock() s.sockTimeout = d if s.masterSocket != nil { s.masterSocket.SetTimeout(d) } if s.slaveSocket != nil { s.slaveSocket.SetTimeout(d) } s.m.Unlock() } // SetBatch sets the default batch size used when fetching documents from the // database. It's possible to change this setting on a per-query basis as // well, using the Query.Batch method. // // The default batch size is defined by the database itself. As of this // writing, MongoDB will use an initial size of min(100 docs, 4MB) on the // first batch, and 4MB on remaining ones. func (s *Session) SetBatch(n int) { if n == 1 { // Server interprets 1 as -1 and closes the cursor (!?) n = 2 } s.m.Lock() s.queryConfig.op.limit = int32(n) s.m.Unlock() } // SetPrefetch sets the default point at which the next batch of results will be // requested. When there are p*batch_size remaining documents cached in an // Iter, the next batch will be requested in background. For instance, when // using this: // // session.SetBatch(200) // session.SetPrefetch(0.25) // // and there are only 50 documents cached in the Iter to be processed, the // next batch of 200 will be requested. It's possible to change this setting on // a per-query basis as well, using the Prefetch method of Query. // // The default prefetch value is 0.25. func (s *Session) SetPrefetch(p float64) { s.m.Lock() s.queryConfig.prefetch = p s.m.Unlock() } // See SetSafe for details on the Safe type. type Safe struct { W int // Min # of servers to ack before success WMode string // Write mode for MongoDB 2.0+ (e.g. "majority") WTimeout int // Milliseconds to wait for W before timing out FSync bool // Should servers sync to disk before returning success J bool // Wait for next group commit if journaling; no effect otherwise } // Safe returns the current safety mode for the session. func (s *Session) Safe() (safe *Safe) { s.m.Lock() defer s.m.Unlock() if s.safeOp != nil { cmd := s.safeOp.query.(*getLastError) safe = &Safe{WTimeout: cmd.WTimeout, FSync: cmd.FSync, J: cmd.J} switch w := cmd.W.(type) { case string: safe.WMode = w case int: safe.W = w } } return } // SetSafe changes the session safety mode. // // If the safe parameter is nil, the session is put in unsafe mode, and writes // become fire-and-forget, without error checking. The unsafe mode is faster // since operations won't hold on waiting for a confirmation. // // If the safe parameter is not nil, any changing query (insert, update, ...) // will be followed by a getLastError command with the specified parameters, // to ensure the request was correctly processed. // // The safe.W parameter determines how many servers should confirm a write // before the operation is considered successful. If set to 0 or 1, the // command will return as soon as the master is done with the request. // If safe.WTimeout is greater than zero, it determines how many milliseconds // to wait for the safe.W servers to respond before returning an error. // // Starting with MongoDB 2.0.0 the safe.WMode parameter can be used instead // of W to request for richer semantics. If set to "majority" the server will // wait for a majority of members from the replica set to respond before // returning. Custom modes may also be defined within the server to create // very detailed placement schemas. See the data awareness documentation in // the links below for more details (note that MongoDB internally reuses the // "w" field name for WMode). // // If safe.FSync is true and journaling is disabled, the servers will be // forced to sync all files to disk immediately before returning. If the // same option is true but journaling is enabled, the server will instead // await for the next group commit before returning. // // Since MongoDB 2.0.0, the safe.J option can also be used instead of FSync // to force the server to wait for a group commit in case journaling is // enabled. The option has no effect if the server has journaling disabled. // // For example, the following statement will make the session check for // errors, without imposing further constraints: // // session.SetSafe(&mgo.Safe{}) // // The following statement will force the server to wait for a majority of // members of a replica set to return (MongoDB 2.0+ only): // // session.SetSafe(&mgo.Safe{WMode: "majority"}) // // The following statement, on the other hand, ensures that at least two // servers have flushed the change to disk before confirming the success // of operations: // // session.EnsureSafe(&mgo.Safe{W: 2, FSync: true}) // // The following statement, on the other hand, disables the verification // of errors entirely: // // session.SetSafe(nil) // // See also the EnsureSafe method. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/getLastError+Command // http://www.mongodb.org/display/DOCS/Verifying+Propagation+of+Writes+with+getLastError // http://www.mongodb.org/display/DOCS/Data+Center+Awareness // func (s *Session) SetSafe(safe *Safe) { s.m.Lock() s.safeOp = nil s.ensureSafe(safe) s.m.Unlock() } // EnsureSafe compares the provided safety parameters with the ones // currently in use by the session and picks the most conservative // choice for each setting. // // That is: // // - safe.WMode is always used if set. // - safe.W is used if larger than the current W and WMode is empty. // - safe.FSync is always used if true. // - safe.J is used if FSync is false. // - safe.WTimeout is used if set and smaller than the current WTimeout. // // For example, the following statement will ensure the session is // at least checking for errors, without enforcing further constraints. // If a more conservative SetSafe or EnsureSafe call was previously done, // the following call will be ignored. // // session.EnsureSafe(&mgo.Safe{}) // // See also the SetSafe method for details on what each option means. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/getLastError+Command // http://www.mongodb.org/display/DOCS/Verifying+Propagation+of+Writes+with+getLastError // http://www.mongodb.org/display/DOCS/Data+Center+Awareness // func (s *Session) EnsureSafe(safe *Safe) { s.m.Lock() s.ensureSafe(safe) s.m.Unlock() } func (s *Session) ensureSafe(safe *Safe) { if safe == nil { return } var w interface{} if safe.WMode != "" { w = safe.WMode } else if safe.W > 0 { w = safe.W } var cmd getLastError if s.safeOp == nil { cmd = getLastError{1, w, safe.WTimeout, safe.FSync, safe.J} } else { // Copy. We don't want to mutate the existing query. cmd = *(s.safeOp.query.(*getLastError)) if cmd.W == nil { cmd.W = w } else if safe.WMode != "" { cmd.W = safe.WMode } else if i, ok := cmd.W.(int); ok && safe.W > i { cmd.W = safe.W } if safe.WTimeout > 0 && safe.WTimeout < cmd.WTimeout { cmd.WTimeout = safe.WTimeout } if safe.FSync { cmd.FSync = true cmd.J = false } else if safe.J && !cmd.FSync { cmd.J = true } } s.safeOp = &queryOp{ query: &cmd, collection: "admin.$cmd", limit: -1, } } // Run issues the provided command against the "admin" database and // and unmarshals its result in the respective argument. The cmd // argument may be either a string with the command name itself, in // which case an empty document of the form bson.M{cmd: 1} will be used, // or it may be a full command document. // // Note that MongoDB considers the first marshalled key as the command // name, so when providing a command with options, it's important to // use an ordering-preserving document, such as a struct value or an // instance of bson.D. For instance: // // db.Run(bson.D{{"create", "mycollection"}, {"size", 1024}}) // // For commands against arbitrary databases, see the Run method in // the Database type. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Commands // http://www.mongodb.org/display/DOCS/List+of+Database+CommandSkips // func (s *Session) Run(cmd interface{}, result interface{}) error { return s.DB("admin").Run(cmd, result) } // SelectServers restricts communication to servers configured with the // given tags. For example, the following statement restricts servers // used for reading operations to those with both tag "disk" set to // "ssd" and tag "rack" set to 1: // // session.SelectSlaves(bson.D{{"disk", "ssd"}, {"rack", 1}}) // // Multiple sets of tags may be provided, in which case the used server // must match all tags within any one set. // // If a connection was previously assigned to the session due to the // current session mode (see Session.SetMode), the tag selection will // only be enforced after the session is refreshed. // // Relevant documentation: // // http://docs.mongodb.org/manual/tutorial/configure-replica-set-tag-sets // func (s *Session) SelectServers(tags ...bson.D) { s.m.Lock() s.queryConfig.op.serverTags = tags s.m.Unlock() } // Ping runs a trivial ping command just to get in touch with the server. func (s *Session) Ping() error { return s.Run("ping", nil) } // Fsync flushes in-memory writes to disk on the server the session // is established with. If async is true, the call returns immediately, // otherwise it returns after the flush has been made. func (s *Session) Fsync(async bool) error { return s.Run(bson.D{{"fsync", 1}, {"async", async}}, nil) } // FsyncLock locks all writes in the specific server the session is // established with and returns. Any writes attempted to the server // after it is successfully locked will block until FsyncUnlock is // called for the same server. // // This method works on slaves as well, preventing the oplog from being // flushed while the server is locked, but since only the server // connected to is locked, for locking specific slaves it may be // necessary to establish a connection directly to the slave (see // Dial's connect=direct option). // // As an important caveat, note that once a write is attempted and // blocks, follow up reads will block as well due to the way the // lock is internally implemented in the server. More details at: // // https://jira.mongodb.org/browse/SERVER-4243 // // FsyncLock is often used for performing consistent backups of // the database files on disk. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/fsync+Command // http://www.mongodb.org/display/DOCS/Backups // func (s *Session) FsyncLock() error { return s.Run(bson.D{{"fsync", 1}, {"lock", true}}, nil) } // FsyncUnlock releases the server for writes. See FsyncLock for details. func (s *Session) FsyncUnlock() error { return s.DB("admin").C("$cmd.sys.unlock").Find(nil).One(nil) // WTF? } // Find prepares a query using the provided document. The document may be a // map or a struct value capable of being marshalled with bson. The map // may be a generic one using interface{} for its key and/or values, such as // bson.M, or it may be a properly typed map. Providing nil as the document // is equivalent to providing an empty document such as bson.M{}. // // Further details of the query may be tweaked using the resulting Query value, // and then executed to retrieve results using methods such as One, For, // Iter, or Tail. // // In case the resulting document includes a field named $err or errmsg, which // are standard ways for MongoDB to return query errors, the returned err will // be set to a *QueryError value including the Err message and the Code. In // those cases, the result argument is still unmarshalled into with the // received document so that any other custom values may be obtained if // desired. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Querying // http://www.mongodb.org/display/DOCS/Advanced+Queries // func (c *Collection) Find(query interface{}) *Query { session := c.Database.Session session.m.RLock() q := &Query{session: session, query: session.queryConfig} session.m.RUnlock() q.op.query = query q.op.collection = c.FullName return q } // FindId is a convenience helper equivalent to: // // query := collection.Find(bson.M{"_id": id}) // // See the Find method for more details. func (c *Collection) FindId(id interface{}) *Query { return c.Find(bson.D{{"_id", id}}) } type Pipe struct { session *Session collection *Collection pipeline interface{} } // Pipe prepares a pipeline to aggregate. The pipeline document // must be a slice built in terms of the aggregation framework language. // // For example: // // pipe := collection.Pipe([]bson.M{{"$match": bson.M{"name": "Otavio"}}}) // iter := pipe.Iter() // // Relevant documentation: // // http://docs.mongodb.org/manual/reference/aggregation // http://docs.mongodb.org/manual/applications/aggregation // http://docs.mongodb.org/manual/tutorial/aggregation-examples // func (c *Collection) Pipe(pipeline interface{}) *Pipe { session := c.Database.Session return &Pipe{ session: session, collection: c, pipeline: pipeline, } } // Iter executes the pipeline and returns an iterator capable of going // over all the generated results. func (p *Pipe) Iter() *Iter { iter := &Iter{ session: p.session, timeout: -1, } iter.gotReply.L = &iter.m var result struct{ Result []bson.Raw } c := p.collection iter.err = c.Database.Run(bson.D{{"aggregate", c.Name}, {"pipeline", p.pipeline}}, &result) if iter.err != nil { return iter } for i := range result.Result { iter.docData.Push(result.Result[i].Data) } return iter } // All works like Iter.All. func (p *Pipe) All(result interface{}) error { return p.Iter().All(result) } // One executes the pipeline and unmarshals the first item from the // result set into the result parameter. // It returns ErrNotFound if no items are generated by the pipeline. func (p *Pipe) One(result interface{}) error { iter := p.Iter() if iter.Next(result) { return nil } if err := iter.Err(); err != nil { return err } return ErrNotFound } type LastError struct { Err string Code, N, Waited int FSyncFiles int `bson:"fsyncFiles"` WTimeout bool UpdatedExisting bool `bson:"updatedExisting"` UpsertedId interface{} `bson:"upserted"` } func (err *LastError) Error() string { return err.Err } type queryError struct { Err string "$err" ErrMsg string Assertion string Code int AssertionCode int "assertionCode" LastError *LastError "lastErrorObject" } type QueryError struct { Code int Message string Assertion bool } func (err *QueryError) Error() string { return err.Message } // IsDup returns whether err informs of a duplicate key error because // a primary key index or a secondary unique index already has an entry // with the given value. func IsDup(err error) bool { // Besides being handy, helps with https://jira.mongodb.org/browse/SERVER-7164 // What follows makes me sad. Hopefully conventions will be more clear over time. switch e := err.(type) { case *LastError: return e.Code == 11000 || e.Code == 11001 || e.Code == 12582 case *QueryError: return e.Code == 11000 || e.Code == 11001 || e.Code == 12582 } return false } // Insert inserts one or more documents in the respective collection. In // case the session is in safe mode (see the SetSafe method) and an error // happens while inserting the provided documents, the returned error will // be of type *LastError. func (c *Collection) Insert(docs ...interface{}) error { _, err := c.writeQuery(&insertOp{c.FullName, docs}) return err } // Update finds a single document matching the provided selector document // and modifies it according to the change document. // If the session is in safe mode (see SetSafe) a ErrNotFound error is // returned if a document isn't found, or a value of type *LastError // when some other error is detected. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Updating // http://www.mongodb.org/display/DOCS/Atomic+Operations // func (c *Collection) Update(selector interface{}, change interface{}) error { lerr, err := c.writeQuery(&updateOp{c.FullName, selector, change, 0}) if err == nil && lerr != nil && !lerr.UpdatedExisting { return ErrNotFound } return err } // UpdateId is a convenience helper equivalent to: // // err := collection.Update(bson.M{"_id": id}, change) // // See the Update method for more details. func (c *Collection) UpdateId(id interface{}, change interface{}) error { return c.Update(bson.D{{"_id", id}}, change) } // ChangeInfo holds details about the outcome of a change operation. type ChangeInfo struct { Updated int // Number of existing documents updated Removed int // Number of documents removed UpsertedId interface{} // Upserted _id field, when not explicitly provided } // UpdateAll finds all documents matching the provided selector document // and modifies them according to the change document. // If the session is in safe mode (see SetSafe) details of the executed // operation are returned in info or an error of type *LastError when // some problem is detected. It is not an error for the update to not be // applied on any documents because the selector doesn't match. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Updating // http://www.mongodb.org/display/DOCS/Atomic+Operations // func (c *Collection) UpdateAll(selector interface{}, change interface{}) (info *ChangeInfo, err error) { lerr, err := c.writeQuery(&updateOp{c.FullName, selector, change, 2}) if err == nil && lerr != nil { info = &ChangeInfo{Updated: lerr.N} } return info, err } // Upsert finds a single document matching the provided selector document // and modifies it according to the change document. If no document matching // the selector is found, the change document is applied to the selector // document and the result is inserted in the collection. // If the session is in safe mode (see SetSafe) details of the executed // operation are returned in info, or an error of type *LastError when // some problem is detected. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Updating // http://www.mongodb.org/display/DOCS/Atomic+Operations // func (c *Collection) Upsert(selector interface{}, change interface{}) (info *ChangeInfo, err error) { data, err := bson.Marshal(change) if err != nil { return nil, err } change = bson.Raw{0x03, data} lerr, err := c.writeQuery(&updateOp{c.FullName, selector, change, 1}) if err == nil && lerr != nil { info = &ChangeInfo{} if lerr.UpdatedExisting { info.Updated = lerr.N } else { info.UpsertedId = lerr.UpsertedId } } return info, err } // UpsertId is a convenience helper equivalent to: // // info, err := collection.Upsert(bson.M{"_id": id}, change) // // See the Upsert method for more details. func (c *Collection) UpsertId(id interface{}, change interface{}) (info *ChangeInfo, err error) { return c.Upsert(bson.D{{"_id", id}}, change) } // Remove finds a single document matching the provided selector document // and removes it from the database. // If the session is in safe mode (see SetSafe) a ErrNotFound error is // returned if a document isn't found, or a value of type *LastError // when some other error is detected. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Removing // func (c *Collection) Remove(selector interface{}) error { lerr, err := c.writeQuery(&deleteOp{c.FullName, selector, 1}) if err == nil && lerr != nil && lerr.N == 0 { return ErrNotFound } return err } // RemoveId is a convenience helper equivalent to: // // err := collection.Remove(bson.M{"_id": id}) // // See the Remove method for more details. func (c *Collection) RemoveId(id interface{}) error { return c.Remove(bson.D{{"_id", id}}) } // RemoveAll finds all documents matching the provided selector document // and removes them from the database. In case the session is in safe mode // (see the SetSafe method) and an error happens when attempting the change, // the returned error will be of type *LastError. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Removing // func (c *Collection) RemoveAll(selector interface{}) (info *ChangeInfo, err error) { lerr, err := c.writeQuery(&deleteOp{c.FullName, selector, 0}) if err == nil && lerr != nil { info = &ChangeInfo{Removed: lerr.N} } return info, err } // DropDatabase removes the entire database including all of its collections. func (db *Database) DropDatabase() error { return db.Run(bson.D{{"dropDatabase", 1}}, nil) } // DropCollection removes the entire collection including all of its documents. func (c *Collection) DropCollection() error { return c.Database.Run(bson.D{{"drop", c.Name}}, nil) } // The CollectionInfo type holds metadata about a collection. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/createCollection+Command // http://www.mongodb.org/display/DOCS/Capped+Collections // type CollectionInfo struct { // DisableIdIndex prevents the automatic creation of the index // on the _id field for the collection. DisableIdIndex bool // ForceIdIndex enforces the automatic creation of the index // on the _id field for the collection. Capped collections, // for example, do not have such an index by default. ForceIdIndex bool // If Capped is true new documents will replace old ones when // the collection is full. MaxBytes must necessarily be set // to define the size when the collection wraps around. // MaxDocs optionally defines the number of documents when it // wraps, but MaxBytes still needs to be set. Capped bool MaxBytes int MaxDocs int } // Create explicitly creates the c collection with details of info. // MongoDB creates collections automatically on use, so this method // is only necessary when creating collection with non-default // characteristics, such as capped collections. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/createCollection+Command // http://www.mongodb.org/display/DOCS/Capped+Collections // func (c *Collection) Create(info *CollectionInfo) error { cmd := make(bson.D, 0, 4) cmd = append(cmd, bson.DocElem{"create", c.Name}) if info.Capped { if info.MaxBytes < 1 { return fmt.Errorf("Collection.Create: with Capped, MaxBytes must also be set") } cmd = append(cmd, bson.DocElem{"capped", true}) cmd = append(cmd, bson.DocElem{"size", info.MaxBytes}) if info.MaxDocs > 0 { cmd = append(cmd, bson.DocElem{"max", info.MaxDocs}) } } if info.DisableIdIndex { cmd = append(cmd, bson.DocElem{"autoIndexId", false}) } if info.ForceIdIndex { cmd = append(cmd, bson.DocElem{"autoIndexId", true}) } return c.Database.Run(cmd, nil) } // Batch sets the batch size used when fetching documents from the database. // It's possible to change this setting on a per-session basis as well, using // the Batch method of Session. // // The default batch size is defined by the database itself. As of this // writing, MongoDB will use an initial size of min(100 docs, 4MB) on the // first batch, and 4MB on remaining ones. func (q *Query) Batch(n int) *Query { if n == 1 { // Server interprets 1 as -1 and closes the cursor (!?) n = 2 } q.m.Lock() q.op.limit = int32(n) q.m.Unlock() return q } // Prefetch sets the point at which the next batch of results will be requested. // When there are p*batch_size remaining documents cached in an Iter, the next // batch will be requested in background. For instance, when using this: // // query.Batch(200).Prefetch(0.25) // // and there are only 50 documents cached in the Iter to be processed, the // next batch of 200 will be requested. It's possible to change this setting on // a per-session basis as well, using the SetPrefetch method of Session. // // The default prefetch value is 0.25. func (q *Query) Prefetch(p float64) *Query { q.m.Lock() q.prefetch = p q.m.Unlock() return q } // Skip skips over the n initial documents from the query results. Note that // this only makes sense with capped collections where documents are naturally // ordered by insertion time, or with sorted results. func (q *Query) Skip(n int) *Query { q.m.Lock() q.op.skip = int32(n) q.m.Unlock() return q } // Limit restricts the maximum number of documents retrieved to n, and also // changes the batch size to the same value. Once n documents have been // returned by Next, the following call will return ErrNotFound. func (q *Query) Limit(n int) *Query { q.m.Lock() switch { case n == 1: q.limit = 1 q.op.limit = -1 case n == math.MinInt32: // -MinInt32 == -MinInt32 q.limit = math.MaxInt32 q.op.limit = math.MinInt32 + 1 case n < 0: q.limit = int32(-n) q.op.limit = int32(n) default: q.limit = int32(n) q.op.limit = int32(n) } q.m.Unlock() return q } // Select enables selecting which fields should be retrieved for the results // found. For example, the following query would only retrieve the name field: // // err := collection.Find(nil).Select(bson.M{"name": 1}).One(&result) // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields // func (q *Query) Select(selector interface{}) *Query { q.m.Lock() q.op.selector = selector q.m.Unlock() return q } // Sort asks the database to order returned documents according to the // provided field names. A field name may be prefixed by - (minus) for // it to be sorted in reverse order. // // For example: // // query1 := collection.Find(nil).Sort("firstname", "lastname") // query2 := collection.Find(nil).Sort("-age") // query3 := collection.Find(nil).Sort("$natural") // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order // func (q *Query) Sort(fields ...string) *Query { q.m.Lock() var order bson.D for _, field := range fields { n := 1 if field != "" { switch field[0] { case '+': field = field[1:] case '-': n = -1 field = field[1:] } } if field == "" { panic("Sort: empty field name") } order = append(order, bson.DocElem{field, n}) } q.op.options.OrderBy = order q.op.hasOptions = true q.m.Unlock() return q } // Explain returns a number of details about how the MongoDB server would // execute the requested query, such as the number of objects examined, // the number of time the read lock was yielded to allow writes to go in, // and so on. // // For example: // // m := bson.M{} // err := collection.Find(bson.M{"filename": name}).Explain(m) // if err == nil { // fmt.Printf("Explain: %#v\n", m) // } // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Optimization // http://www.mongodb.org/display/DOCS/Query+Optimizer // func (q *Query) Explain(result interface{}) error { q.m.Lock() clone := &Query{session: q.session, query: q.query} q.m.Unlock() clone.op.options.Explain = true clone.op.hasOptions = true if clone.op.limit > 0 { clone.op.limit = -q.op.limit } iter := clone.Iter() if iter.Next(result) { return nil } return iter.Close() } // Hint will include an explicit "hint" in the query to force the server // to use a specified index, potentially improving performance in some // situations. The provided parameters are the fields that compose the // key of the index to be used. For details on how the indexKey may be // built, see the EnsureIndex method. // // For example: // // query := collection.Find(bson.M{"firstname": "Joe", "lastname": "Winter"}) // query.Hint("lastname", "firstname") // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Optimization // http://www.mongodb.org/display/DOCS/Query+Optimizer // func (q *Query) Hint(indexKey ...string) *Query { q.m.Lock() _, realKey, err := parseIndexKey(indexKey) q.op.options.Hint = realKey q.op.hasOptions = true q.m.Unlock() if err != nil { panic(err) } return q } // Snapshot will force the performed query to make use of an available // index on the _id field to prevent the same document from being returned // more than once in a single iteration. This might happen without this // setting in situations when the document changes in size and thus has to // be moved while the iteration is running. // // Because snapshot mode traverses the _id index, it may not be used with // sorting or explicit hints. It also cannot use any other index for the // query. // // Even with snapshot mode, items inserted or deleted during the query may // or may not be returned; that is, this mode is not a true point-in-time // snapshot. // // The same effect of Snapshot may be obtained by using any unique index on // field(s) that will not be modified (best to use Hint explicitly too). // A non-unique index (such as creation time) may be made unique by // appending _id to the index when creating it. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/How+to+do+Snapshotted+Queries+in+the+Mongo+Database // func (q *Query) Snapshot() *Query { q.m.Lock() q.op.options.Snapshot = true q.op.hasOptions = true q.m.Unlock() return q } // LogReplay enables an option that optimizes queries that are typically // made against the MongoDB oplog for replaying it. This is an internal // implementation aspect and most likely uninteresting for other uses. // It has seen at least one use case, though, so it's exposed via the API. func (q *Query) LogReplay() *Query { q.m.Lock() q.op.flags |= flagLogReplay q.m.Unlock() return q } func checkQueryError(fullname string, d []byte) error { l := len(d) if l < 16 { return nil } if d[5] == '$' && d[6] == 'e' && d[7] == 'r' && d[8] == 'r' && d[9] == '\x00' && d[4] == '\x02' { goto Error } if len(fullname) < 5 || fullname[len(fullname)-5:] != ".$cmd" { return nil } for i := 0; i+8 < l; i++ { if d[i] == '\x02' && d[i+1] == 'e' && d[i+2] == 'r' && d[i+3] == 'r' && d[i+4] == 'm' && d[i+5] == 's' && d[i+6] == 'g' && d[i+7] == '\x00' { goto Error } } return nil Error: result := &queryError{} bson.Unmarshal(d, result) logf("queryError: %#v\n", result) if result.LastError != nil { return result.LastError } if result.Err == "" && result.ErrMsg == "" { return nil } if result.AssertionCode != 0 && result.Assertion != "" { return &QueryError{Code: result.AssertionCode, Message: result.Assertion, Assertion: true} } if result.Err != "" { return &QueryError{Code: result.Code, Message: result.Err} } return &QueryError{Code: result.Code, Message: result.ErrMsg} } // One executes the query and unmarshals the first obtained document into the // result argument. The result must be a struct or map value capable of being // unmarshalled into by gobson. This function blocks until either a result // is available or an error happens. For example: // // err := collection.Find(bson.M{"a", 1}).One(&result) // // In case the resulting document includes a field named $err or errmsg, which // are standard ways for MongoDB to return query errors, the returned err will // be set to a *QueryError value including the Err message and the Code. In // those cases, the result argument is still unmarshalled into with the // received document so that any other custom values may be obtained if // desired. // func (q *Query) One(result interface{}) (err error) { q.m.Lock() session := q.session op := q.op // Copy. q.m.Unlock() socket, err := session.acquireSocket(true) if err != nil { return err } defer socket.Release() op.flags |= session.slaveOkFlag() op.limit = -1 data, err := socket.SimpleQuery(&op) if err != nil { return err } if data == nil { return ErrNotFound } if result != nil { err = bson.Unmarshal(data, result) if err == nil { debugf("Query %p document unmarshaled: %#v", q, result) } else { debugf("Query %p document unmarshaling failed: %#v", q, err) return err } } return checkQueryError(op.collection, data) } // The DBRef type implements support for the database reference MongoDB // convention as supported by multiple drivers. This convention enables // cross-referencing documents between collections and databases using // a structure which includes a collection name, a document id, and // optionally a database name. // // See the FindRef methods on Session and on Database. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Database+References // type DBRef struct { Collection string `bson:"$ref"` Id interface{} `bson:"$id"` Database string `bson:"$db,omitempty"` } // NOTE: Order of fields for DBRef above does matter, per documentation. // FindRef returns a query that looks for the document in the provided // reference. If the reference includes the DB field, the document will // be retrieved from the respective database. // // See also the DBRef type and the FindRef method on Session. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Database+References // func (db *Database) FindRef(ref *DBRef) *Query { var c *Collection if ref.Database == "" { c = db.C(ref.Collection) } else { c = db.Session.DB(ref.Database).C(ref.Collection) } return c.FindId(ref.Id) } // FindRef returns a query that looks for the document in the provided // reference. For a DBRef to be resolved correctly at the session level // it must necessarily have the optional DB field defined. // // See also the DBRef type and the FindRef method on Database. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Database+References // func (s *Session) FindRef(ref *DBRef) *Query { if ref.Database == "" { panic(errors.New(fmt.Sprintf("Can't resolve database for %#v", ref))) } c := s.DB(ref.Database).C(ref.Collection) return c.FindId(ref.Id) } // CollectionNames returns the collection names present in database. func (db *Database) CollectionNames() (names []string, err error) { c := len(db.Name) + 1 iter := db.C("system.namespaces").Find(nil).Iter() var result *struct{ Name string } for iter.Next(&result) { if strings.Index(result.Name, "$") < 0 || strings.Index(result.Name, ".oplog.$") >= 0 { names = append(names, result.Name[c:]) } } if err := iter.Close(); err != nil { return nil, err } sort.Strings(names) return names, nil } type dbNames struct { Databases []struct { Name string Empty bool } } // DatabaseNames returns the names of non-empty databases present in the cluster. func (s *Session) DatabaseNames() (names []string, err error) { var result dbNames err = s.Run("listDatabases", &result) if err != nil { return nil, err } for _, db := range result.Databases { if !db.Empty { names = append(names, db.Name) } } sort.Strings(names) return names, nil } // Iter executes the query and returns an iterator capable of going over all // the results. Results will be returned in batches of configurable // size (see the Batch method) and more documents will be requested when a // configurable number of documents is iterated over (see the Prefetch method). func (q *Query) Iter() *Iter { q.m.Lock() session := q.session op := q.op prefetch := q.prefetch limit := q.limit q.m.Unlock() iter := &Iter{ session: session, prefetch: prefetch, limit: limit, timeout: -1, } iter.gotReply.L = &iter.m iter.op.collection = op.collection iter.op.limit = op.limit iter.op.replyFunc = iter.replyFunc() iter.docsToReceive++ op.replyFunc = iter.op.replyFunc op.flags |= session.slaveOkFlag() socket, err := session.acquireSocket(true) if err != nil { iter.err = err } else { iter.err = socket.Query(&op) iter.server = socket.Server() socket.Release() } return iter } // Tail returns a tailable iterator. Unlike a normal iterator, a // tailable iterator may wait for new values to be inserted in the // collection once the end of the current result set is reached, // A tailable iterator may only be used with capped collections. // // The timeout parameter indicates how long Next will block waiting // for a result before timing out. If set to -1, Next will not // timeout, and will continue waiting for a result for as long as // the cursor is valid and the session is not closed. If set to 0, // Next times out as soon as it reaches the end of the result set. // Otherwise, Next will wait for at least the given number of // seconds for a new document to be available before timing out. // // On timeouts, Next will unblock and return false, and the Timeout // method will return true if called. In these cases, Next may still // be called again on the same iterator to check if a new value is // available at the current cursor position, and again it will block // according to the specified timeoutSecs. If the cursor becomes // invalid, though, both Next and Timeout will return false and // the query must be restarted. // // The following example demonstrates timeout handling and query // restarting: // // iter := collection.Find(nil).Sort("$natural").Tail(5 * time.Second) // for { // for iter.Next(&result) { // fmt.Println(result.Id) // lastId = result.Id // } // if err := iter.Close(); err != nil { // return err // } // if iter.Timeout() { // continue // } // query := collection.Find(bson.M{"_id": bson.M{"$gt": lastId}}) // iter = query.Sort("$natural").Tail(5 * time.Second) // } // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Tailable+Cursors // http://www.mongodb.org/display/DOCS/Capped+Collections // http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order // func (q *Query) Tail(timeout time.Duration) *Iter { q.m.Lock() session := q.session op := q.op prefetch := q.prefetch q.m.Unlock() iter := &Iter{session: session, prefetch: prefetch} iter.gotReply.L = &iter.m iter.timeout = timeout iter.op.collection = op.collection iter.op.limit = op.limit iter.op.replyFunc = iter.replyFunc() iter.docsToReceive++ op.replyFunc = iter.op.replyFunc op.flags |= flagTailable | flagAwaitData | session.slaveOkFlag() socket, err := session.acquireSocket(true) if err != nil { iter.err = err } else { iter.err = socket.Query(&op) iter.server = socket.Server() socket.Release() } return iter } func (s *Session) slaveOkFlag() (flag queryOpFlags) { s.m.RLock() if s.slaveOk { flag = flagSlaveOk } s.m.RUnlock() return } // Err returns nil if no errors happened during iteration, or the actual // error otherwise. // // In case a resulting document included a field named $err or errmsg, which are // standard ways for MongoDB to report an improper query, the returned value has // a *QueryError type, and includes the Err message and the Code. func (iter *Iter) Err() error { iter.m.Lock() err := iter.err iter.m.Unlock() if err == ErrNotFound { return nil } return err } // Close kills the server cursor used by the iterator, if any, and returns // nil if no errors happened during iteration, or the actual error otherwise. // // Server cursors are automatically closed at the end of an iteration, which // means close will do nothing unless the iteration was interrupted before // the server finished sending results to the driver. If Close is not called // in such a situation, the cursor will remain available at the server until // the default cursor timeout period is reached. No further problems arise. // // Close is idempotent. That means it can be called repeatedly and will // return the same result every time. // // In case a resulting document included a field named $err or errmsg, which are // standard ways for MongoDB to report an improper query, the returned value has // a *QueryError type. func (iter *Iter) Close() error { iter.m.Lock() iter.killCursor() err := iter.err iter.m.Unlock() if err == ErrNotFound { return nil } return err } func (iter *Iter) killCursor() error { if iter.op.cursorId != 0 { socket, err := iter.acquireSocket() if err == nil { // TODO Batch kills. err = socket.Query(&killCursorsOp{[]int64{iter.op.cursorId}}) socket.Release() } if err != nil && (iter.err == nil || iter.err == ErrNotFound) { iter.err = err } iter.op.cursorId = 0 return err } return nil } // Timeout returns true if Next returned false due to a timeout of // a tailable cursor. In those cases, Next may be called again to continue // the iteration at the previous cursor position. func (iter *Iter) Timeout() bool { iter.m.Lock() result := iter.timedout iter.m.Unlock() return result } // Next retrieves the next document from the result set, blocking if necessary. // This method will also automatically retrieve another batch of documents from // the server when the current one is exhausted, or before that in background // if pre-fetching is enabled (see the Query.Prefetch and Session.SetPrefetch // methods). // // Next returns true if a document was successfully unmarshalled onto result, // and false at the end of the result set or if an error happened. // When Next returns false, the Err method should be called to verify if // there was an error during iteration. // // For example: // // iter := collection.Find(nil).Iter() // for iter.Next(&result) { // fmt.Printf("Result: %v\n", result.Id) // } // if err := iter.Close(); err != nil { // return err // } // func (iter *Iter) Next(result interface{}) bool { iter.m.Lock() iter.timedout = false timeout := time.Time{} for iter.err == nil && iter.docData.Len() == 0 && (iter.docsToReceive > 0 || iter.op.cursorId != 0) { if iter.docsToReceive == 0 { if iter.timeout >= 0 { if timeout.IsZero() { timeout = time.Now().Add(iter.timeout) } if time.Now().After(timeout) { iter.timedout = true iter.m.Unlock() return false } } iter.getMore() } iter.gotReply.Wait() } // Exhaust available data before reporting any errors. if docData, ok := iter.docData.Pop().([]byte); ok { if iter.limit > 0 { iter.limit-- if iter.limit == 0 { if iter.docData.Len() > 0 { panic(fmt.Errorf("data remains after limit exhausted: %d", iter.docData.Len())) } iter.err = ErrNotFound if iter.killCursor() != nil { return false } } } if iter.op.cursorId != 0 && iter.err == nil { if iter.docsBeforeMore == 0 { iter.getMore() } iter.docsBeforeMore-- // Goes negative. } iter.m.Unlock() err := bson.Unmarshal(docData, result) if err != nil { debugf("Iter %p document unmarshaling failed: %#v", iter, err) iter.err = err return false } debugf("Iter %p document unmarshaled: %#v", iter, result) // XXX Only have to check first document for a query error? err = checkQueryError(iter.op.collection, docData) if err != nil { iter.m.Lock() if iter.err == nil { iter.err = err } iter.m.Unlock() return false } return true } else if iter.err != nil { debugf("Iter %p returning false: %s", iter, iter.err) iter.m.Unlock() return false } else if iter.op.cursorId == 0 { iter.err = ErrNotFound debugf("Iter %p exhausted with cursor=0", iter) iter.m.Unlock() return false } panic("unreachable") } // All retrieves all documents from the result set into the provided slice // and closes the iterator. // // The result argument must necessarily be the address for a slice. The slice // may be nil or previously allocated. // // WARNING: Obviously, All must not be used with result sets that may be // potentially large, since it may consume all memory until the system // crashes. Consider building the query with a Limit clause to ensure the // result size is bounded. // // For instance: // // var result []struct{ Value int } // iter := collection.Find(nil).Limit(100).Iter() // err := iter.All(&result) // if err != nil { // return err // } // func (iter *Iter) All(result interface{}) error { resultv := reflect.ValueOf(result) if resultv.Kind() != reflect.Ptr || resultv.Elem().Kind() != reflect.Slice { panic("result argument must be a slice address") } slicev := resultv.Elem() slicev = slicev.Slice(0, slicev.Cap()) elemt := slicev.Type().Elem() i := 0 for { if slicev.Len() == i { elemp := reflect.New(elemt) if !iter.Next(elemp.Interface()) { break } slicev = reflect.Append(slicev, elemp.Elem()) slicev = slicev.Slice(0, slicev.Cap()) } else { if !iter.Next(slicev.Index(i).Addr().Interface()) { break } } i++ } resultv.Elem().Set(slicev.Slice(0, i)) return iter.Close() } // All works like Iter.All. func (q *Query) All(result interface{}) error { return q.Iter().All(result) } // The For method is obsolete and will be removed in a future release. // See Iter as an elegant replacement. func (q *Query) For(result interface{}, f func() error) error { return q.Iter().For(result, f) } // The For method is obsolete and will be removed in a future release. // See Iter as an elegant replacement. func (iter *Iter) For(result interface{}, f func() error) (err error) { valid := false v := reflect.ValueOf(result) if v.Kind() == reflect.Ptr { v = v.Elem() switch v.Kind() { case reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice: valid = v.IsNil() } } if !valid { panic("For needs a pointer to nil reference value. See the documentation.") } zero := reflect.Zero(v.Type()) for { v.Set(zero) if !iter.Next(result) { break } err = f() if err != nil { return err } } return iter.Err() } func (iter *Iter) acquireSocket() (*mongoSocket, error) { socket, err := iter.session.acquireSocket(true) if err != nil { return nil, err } if socket.Server() != iter.server { // Socket server changed during iteration. This may happen // with Eventual sessions, if a Refresh is done, or if a // monotonic session gets a write and shifts from secondary // to primary. Our cursor is in a specific server, though. iter.session.m.Lock() sockTimeout := iter.session.sockTimeout iter.session.m.Unlock() socket.Release() socket, _, err = iter.server.AcquireSocket(0, sockTimeout) if err != nil { return nil, err } err := iter.session.socketLogin(socket) if err != nil { socket.Release() return nil, err } } return socket, nil } func (iter *Iter) getMore() { socket, err := iter.acquireSocket() if err != nil { iter.err = err return } defer socket.Release() debugf("Iter %p requesting more documents", iter) if iter.limit > 0 { limit := iter.limit - int32(iter.docsToReceive) - int32(iter.docData.Len()) if limit < iter.op.limit { iter.op.limit = limit } } if err := socket.Query(&iter.op); err != nil { iter.err = err } iter.docsToReceive++ } type countCmd struct { Count string Query interface{} Limit int32 ",omitempty" Skip int32 ",omitempty" } // Count returns the total number of documents in the result set. func (q *Query) Count() (n int, err error) { q.m.Lock() session := q.session op := q.op limit := q.limit q.m.Unlock() c := strings.Index(op.collection, ".") if c < 0 { return 0, errors.New("Bad collection name: " + op.collection) } dbname := op.collection[:c] cname := op.collection[c+1:] result := struct{ N int }{} err = session.DB(dbname).Run(countCmd{cname, op.query, limit, op.skip}, &result) return result.N, err } // Count returns the total number of documents in the collection. func (c *Collection) Count() (n int, err error) { return c.Find(nil).Count() } type distinctCmd struct { Collection string "distinct" Key string Query interface{} ",omitempty" } // Distinct returns a list of distinct values for the given key within // the result set. The list of distinct values will be unmarshalled // in the "values" key of the provided result parameter. // // For example: // // var result []int // err := collection.Find(bson.M{"gender": "F"}).Distinct("age", &result) // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Aggregation // func (q *Query) Distinct(key string, result interface{}) error { q.m.Lock() session := q.session op := q.op // Copy. q.m.Unlock() c := strings.Index(op.collection, ".") if c < 0 { return errors.New("Bad collection name: " + op.collection) } dbname := op.collection[:c] cname := op.collection[c+1:] var doc struct{ Values bson.Raw } err := session.DB(dbname).Run(distinctCmd{cname, key, op.query}, &doc) if err != nil { return err } return doc.Values.Unmarshal(result) } type mapReduceCmd struct { Collection string "mapreduce" Map string ",omitempty" Reduce string ",omitempty" Finalize string ",omitempty" Limit int32 ",omitempty" Out interface{} Query interface{} ",omitempty" Sort interface{} ",omitempty" Scope interface{} ",omitempty" Verbose bool ",omitempty" } type mapReduceResult struct { Results bson.Raw Result bson.Raw TimeMillis int64 "timeMillis" Counts struct{ Input, Emit, Output int } Ok bool Err string Timing *MapReduceTime } type MapReduce struct { Map string // Map Javascript function code (required) Reduce string // Reduce Javascript function code (required) Finalize string // Finalize Javascript function code (optional) Out interface{} // Output collection name or document. If nil, results are inlined into the result parameter. Scope interface{} // Optional global scope for Javascript functions Verbose bool } type MapReduceInfo struct { InputCount int // Number of documents mapped EmitCount int // Number of times reduce called emit OutputCount int // Number of documents in resulting collection Database string // Output database, if results are not inlined Collection string // Output collection, if results are not inlined Time int64 // Time to run the job, in nanoseconds VerboseTime *MapReduceTime // Only defined if Verbose was true } type MapReduceTime struct { Total int64 // Total time, in nanoseconds Map int64 "mapTime" // Time within map function, in nanoseconds EmitLoop int64 "emitLoop" // Time within the emit/map loop, in nanoseconds } // MapReduce executes a map/reduce job for documents covered by the query. // That kind of job is suitable for very flexible bulk aggregation of data // performed at the server side via Javascript functions. // // Results from the job may be returned as a result of the query itself // through the result parameter in case they'll certainly fit in memory // and in a single document. If there's the possibility that the amount // of data might be too large, results must be stored back in an alternative // collection or even a separate database, by setting the Out field of the // provided MapReduce job. In that case, provide nil as the result parameter. // // These are some of the ways to set Out: // // nil // Inline results into the result parameter. // // bson.M{"replace": "mycollection"} // The output will be inserted into a collection which replaces any // existing collection with the same name. // // bson.M{"merge": "mycollection"} // This option will merge new data into the old output collection. In // other words, if the same key exists in both the result set and the // old collection, the new key will overwrite the old one. // // bson.M{"reduce": "mycollection"} // If documents exist for a given key in the result set and in the old // collection, then a reduce operation (using the specified reduce // function) will be performed on the two values and the result will be // written to the output collection. If a finalize function was // provided, this will be run after the reduce as well. // // bson.M{...., "db": "mydb"} // Any of the above options can have the "db" key included for doing // the respective action in a separate database. // // The following is a trivial example which will count the number of // occurrences of a field named n on each document in a collection, and // will return results inline: // // job := &mgo.MapReduce{ // Map: "function() { emit(this.n, 1) }", // Reduce: "function(key, values) { return Array.sum(values) }", // } // var result []struct { Id int "_id"; Value int } // _, err := collection.Find(nil).MapReduce(job, &result) // if err != nil { // return err // } // for _, item := range result { // fmt.Println(item.Value) // } // // This function is compatible with MongoDB 1.7.4+. // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/MapReduce // func (q *Query) MapReduce(job *MapReduce, result interface{}) (info *MapReduceInfo, err error) { q.m.Lock() session := q.session op := q.op // Copy. limit := q.limit q.m.Unlock() c := strings.Index(op.collection, ".") if c < 0 { return nil, errors.New("Bad collection name: " + op.collection) } dbname := op.collection[:c] cname := op.collection[c+1:] cmd := mapReduceCmd{ Collection: cname, Map: job.Map, Reduce: job.Reduce, Finalize: job.Finalize, Out: fixMROut(job.Out), Scope: job.Scope, Verbose: job.Verbose, Query: op.query, Sort: op.options.OrderBy, Limit: limit, } if cmd.Out == nil { cmd.Out = bson.M{"inline": 1} } var doc mapReduceResult err = session.DB(dbname).Run(&cmd, &doc) if err != nil { return nil, err } if doc.Err != "" { return nil, errors.New(doc.Err) } info = &MapReduceInfo{ InputCount: doc.Counts.Input, EmitCount: doc.Counts.Emit, OutputCount: doc.Counts.Output, Time: doc.TimeMillis * 1e6, } if doc.Result.Kind == 0x02 { err = doc.Result.Unmarshal(&info.Collection) info.Database = dbname } else if doc.Result.Kind == 0x03 { var v struct{ Collection, Db string } err = doc.Result.Unmarshal(&v) info.Collection = v.Collection info.Database = v.Db } if doc.Timing != nil { info.VerboseTime = doc.Timing info.VerboseTime.Total *= 1e6 info.VerboseTime.Map *= 1e6 info.VerboseTime.EmitLoop *= 1e6 } if err != nil { return nil, err } if result != nil { return info, doc.Results.Unmarshal(result) } return info, nil } // The "out" option in the MapReduce command must be ordered. This was // found after the implementation was accepting maps for a long time, // so rather than breaking the API, we'll fix the order if necessary. // Details about the order requirement may be seen in MongoDB's code: // // http://goo.gl/L8jwJX // func fixMROut(out interface{}) interface{} { outv := reflect.ValueOf(out) if outv.Kind() != reflect.Map || outv.Type().Key() != reflect.TypeOf("") { return out } outs := make(bson.D, outv.Len()) outTypeIndex := -1 for i, k := range outv.MapKeys() { ks := k.String() outs[i].Name = ks outs[i].Value = outv.MapIndex(k).Interface() switch ks { case "normal", "replace", "merge", "reduce", "inline": outTypeIndex = i } } if outTypeIndex > 0 { outs[0], outs[outTypeIndex] = outs[outTypeIndex], outs[0] } return outs } type Change struct { Update interface{} // The change document Upsert bool // Whether to insert in case the document isn't found Remove bool // Whether to remove the document found rather than updating ReturnNew bool // Should the modified document be returned rather than the old one } type findModifyCmd struct { Collection string "findAndModify" Query, Update, Sort, Fields interface{} ",omitempty" Upsert, Remove, New bool ",omitempty" } type valueResult struct { Value bson.Raw LastError LastError "lastErrorObject" } // Apply allows updating, upserting or removing a document matching a query // and atomically returning either the old version (the default) or the new // version of the document (when ReturnNew is true). If no objects are // found Apply returns ErrNotFound. // // The Sort and Select query methods affect the result of Apply. In case // multiple documents match the query, Sort enables selecting which document to // act upon by ordering it first. Select enables retrieving only a selection // of fields of the new or old document. // // This simple example increments a counter and prints its new value: // // change := mgo.Change{ // Update: bson.M{"$inc": bson.M{"n": 1}}, // ReturnNew: true, // } // info, err = col.Find(M{"_id": id}).Apply(change, &doc) // fmt.Println(doc.N) // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/findAndModify+Command // http://www.mongodb.org/display/DOCS/Updating // http://www.mongodb.org/display/DOCS/Atomic+Operations // func (q *Query) Apply(change Change, result interface{}) (info *ChangeInfo, err error) { q.m.Lock() session := q.session op := q.op // Copy. q.m.Unlock() c := strings.Index(op.collection, ".") if c < 0 { return nil, errors.New("bad collection name: " + op.collection) } dbname := op.collection[:c] cname := op.collection[c+1:] cmd := findModifyCmd{ Collection: cname, Update: change.Update, Upsert: change.Upsert, Remove: change.Remove, New: change.ReturnNew, Query: op.query, Sort: op.options.OrderBy, Fields: op.selector, } session = session.Clone() defer session.Close() session.SetMode(Strong, false) var doc valueResult err = session.DB(dbname).Run(&cmd, &doc) if err != nil { if qerr, ok := err.(*QueryError); ok && qerr.Message == "No matching object found" { return nil, ErrNotFound } return nil, err } if doc.LastError.N == 0 { return nil, ErrNotFound } if doc.Value.Kind != 0x0A { err = doc.Value.Unmarshal(result) if err != nil { return nil, err } } info = &ChangeInfo{} lerr := &doc.LastError if lerr.UpdatedExisting { info.Updated = lerr.N } else if change.Remove { info.Removed = lerr.N } else if change.Upsert { info.UpsertedId = lerr.UpsertedId } return info, nil } // The BuildInfo type encapsulates details about the running MongoDB server. // // Note that the VersionArray field was introduced in MongoDB 2.0+, but it is // internally assembled from the Version information for previous versions. // In both cases, VersionArray is guaranteed to have at least 4 entries. type BuildInfo struct { Version string VersionArray []int `bson:"versionArray"` // On MongoDB 2.0+; assembled from Version otherwise GitVersion string `bson:"gitVersion"` SysInfo string `bson:"sysInfo"` Bits int Debug bool MaxObjectSize int `bson:"maxBsonObjectSize"` } // BuildInfo retrieves the version and other details about the // running MongoDB server. func (s *Session) BuildInfo() (info BuildInfo, err error) { err = s.Run(bson.D{{"buildInfo", "1"}}, &info) if len(info.VersionArray) == 0 { for _, a := range strings.Split(info.Version, ".") { i, err := strconv.Atoi(a) if err != nil { break } info.VersionArray = append(info.VersionArray, i) } } for len(info.VersionArray) < 4 { info.VersionArray = append(info.VersionArray, 0) } return } // --------------------------------------------------------------------------- // Internal session handling helpers. func (s *Session) acquireSocket(slaveOk bool) (*mongoSocket, error) { // Read-only lock to check for previously reserved socket. s.m.RLock() if s.masterSocket != nil { socket := s.masterSocket socket.Acquire() s.m.RUnlock() return socket, nil } if s.slaveSocket != nil && s.slaveOk && slaveOk { socket := s.slaveSocket socket.Acquire() s.m.RUnlock() return socket, nil } s.m.RUnlock() // No go. We may have to request a new socket and change the session, // so try again but with an exclusive lock now. s.m.Lock() defer s.m.Unlock() if s.masterSocket != nil { s.masterSocket.Acquire() return s.masterSocket, nil } if s.slaveSocket != nil && s.slaveOk && slaveOk { s.slaveSocket.Acquire() return s.slaveSocket, nil } // Still not good. We need a new socket. sock, err := s.cluster().AcquireSocket(slaveOk && s.slaveOk, s.syncTimeout, s.sockTimeout, s.queryConfig.op.serverTags) if err != nil { return nil, err } // Authenticate the new socket. if err = s.socketLogin(sock); err != nil { sock.Release() return nil, err } // Keep track of the new socket, if necessary. // Note that, as a special case, if the Eventual session was // not refreshed (s.slaveSocket != nil), it means the developer // asked to preserve an existing reserved socket, so we'll // keep a master one around too before a Refresh happens. if s.consistency != Eventual || s.slaveSocket != nil { s.setSocket(sock) } // Switch over a Monotonic session to the master. if !slaveOk && s.consistency == Monotonic { s.slaveOk = false } return sock, nil } // setSocket binds socket to this section. func (s *Session) setSocket(socket *mongoSocket) { info := socket.Acquire() if info.Master { if s.masterSocket != nil { panic("setSocket(master) with existing master socket reserved") } s.masterSocket = socket } else { if s.slaveSocket != nil { panic("setSocket(slave) with existing slave socket reserved") } s.slaveSocket = socket } } // unsetSocket releases any slave and/or master sockets reserved. func (s *Session) unsetSocket() { if s.masterSocket != nil { s.masterSocket.Release() } if s.slaveSocket != nil { s.slaveSocket.Release() } s.masterSocket = nil s.slaveSocket = nil } func (iter *Iter) replyFunc() replyFunc { return func(err error, op *replyOp, docNum int, docData []byte) { iter.m.Lock() iter.docsToReceive-- if err != nil { iter.err = err debugf("Iter %p received an error: %s", iter, err.Error()) } else if docNum == -1 { debugf("Iter %p received no documents (cursor=%d).", iter, op.cursorId) if op != nil && op.cursorId != 0 { // It's a tailable cursor. iter.op.cursorId = op.cursorId } else { iter.err = ErrNotFound } } else { rdocs := int(op.replyDocs) if docNum == 0 { iter.docsToReceive += rdocs - 1 docsToProcess := iter.docData.Len() + rdocs if iter.limit == 0 || int32(docsToProcess) < iter.limit { iter.docsBeforeMore = docsToProcess - int(iter.prefetch*float64(rdocs)) } else { iter.docsBeforeMore = -1 } iter.op.cursorId = op.cursorId } // XXX Handle errors and flags. debugf("Iter %p received reply document %d/%d (cursor=%d)", iter, docNum+1, rdocs, op.cursorId) iter.docData.Push(docData) } iter.gotReply.Broadcast() iter.m.Unlock() } } // writeQuery runs the given modifying operation, potentially followed up // by a getLastError command in case the session is in safe mode. The // LastError result is made available in lerr, and if lerr.Err is set it // will also be returned as err. func (c *Collection) writeQuery(op interface{}) (lerr *LastError, err error) { s := c.Database.Session dbname := c.Database.Name socket, err := s.acquireSocket(dbname == "local") if err != nil { return nil, err } defer socket.Release() s.m.RLock() safeOp := s.safeOp s.m.RUnlock() if safeOp == nil { return nil, socket.Query(op) } else { var mutex sync.Mutex var replyData []byte var replyErr error mutex.Lock() query := *safeOp // Copy the data. query.collection = dbname + ".$cmd" query.replyFunc = func(err error, reply *replyOp, docNum int, docData []byte) { replyData = docData replyErr = err mutex.Unlock() } err = socket.Query(op, &query) if err != nil { return nil, err } mutex.Lock() // Wait. if replyErr != nil { return nil, replyErr // XXX TESTME } if hasErrMsg(replyData) { // Looks like getLastError itself failed. err = checkQueryError(query.collection, replyData) if err != nil { return nil, err } } result := &LastError{} bson.Unmarshal(replyData, &result) debugf("Result from writing query: %#v", result) if result.Err != "" { return result, result } return result, nil } panic("unreachable") } func hasErrMsg(d []byte) bool { l := len(d) for i := 0; i+8 < l; i++ { if d[i] == '\x02' && d[i+1] == 'e' && d[i+2] == 'r' && d[i+3] == 'r' && d[i+4] == 'm' && d[i+5] == 's' && d[i+6] == 'g' && d[i+7] == '\x00' { return true } } return false } juju-core_1.18.1/src/labix.org/v2/mgo/.bzrignore0000644000015300001610000000001512321735660021232 0ustar jenkinsjenkins_* [856].out juju-core_1.18.1/src/labix.org/v2/mgo/auth.go0000644000015300001610000001666612321736015020536 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo import ( "crypto/md5" "encoding/hex" "errors" "fmt" "labix.org/v2/mgo/bson" "sync" ) type authInfo struct { db, user, pass string } type authCmd struct { Authenticate int Nonce, User, Key string } type authResult struct { ErrMsg string Ok bool } type getNonceCmd struct { GetNonce int } type getNonceResult struct { Nonce string Err string "$err" Code int } type logoutCmd struct { Logout int } func (socket *mongoSocket) getNonce() (nonce string, err error) { socket.Lock() for socket.cachedNonce == "" && socket.dead == nil { debugf("Socket %p to %s: waiting for nonce", socket, socket.addr) socket.gotNonce.Wait() } if socket.cachedNonce == "mongos" { socket.Unlock() return "", errors.New("Can't authenticate with mongos; see http://j.mp/mongos-auth") } debugf("Socket %p to %s: got nonce", socket, socket.addr) nonce, err = socket.cachedNonce, socket.dead socket.cachedNonce = "" socket.Unlock() if err != nil { nonce = "" } return } func (socket *mongoSocket) resetNonce() { debugf("Socket %p to %s: requesting a new nonce", socket, socket.addr) op := &queryOp{} op.query = &getNonceCmd{GetNonce: 1} op.collection = "admin.$cmd" op.limit = -1 op.replyFunc = func(err error, reply *replyOp, docNum int, docData []byte) { if err != nil { socket.kill(errors.New("getNonce: "+err.Error()), true) return } result := &getNonceResult{} err = bson.Unmarshal(docData, &result) if err != nil { socket.kill(errors.New("Failed to unmarshal nonce: "+err.Error()), true) return } debugf("Socket %p to %s: nonce unmarshalled: %#v", socket, socket.addr, result) if result.Code == 13390 { // mongos doesn't yet support auth (see http://j.mp/mongos-auth) result.Nonce = "mongos" } else if result.Nonce == "" { var msg string if result.Err != "" { msg = fmt.Sprintf("Got an empty nonce: %s (%d)", result.Err, result.Code) } else { msg = "Got an empty nonce" } socket.kill(errors.New(msg), true) return } socket.Lock() if socket.cachedNonce != "" { socket.Unlock() panic("resetNonce: nonce already cached") } socket.cachedNonce = result.Nonce socket.gotNonce.Signal() socket.Unlock() } err := socket.Query(op) if err != nil { socket.kill(errors.New("resetNonce: "+err.Error()), true) } } func (socket *mongoSocket) Login(db string, user string, pass string) error { socket.Lock() for _, a := range socket.auth { if a.db == db && a.user == user && a.pass == pass { debugf("Socket %p to %s: login: db=%q user=%q (already logged in)", socket, socket.addr, db, user) socket.Unlock() return nil } } if auth, found := socket.dropLogout(db, user, pass); found { debugf("Socket %p to %s: login: db=%q user=%q (cached)", socket, socket.addr, db, user) socket.auth = append(socket.auth, auth) socket.Unlock() return nil } socket.Unlock() debugf("Socket %p to %s: login: db=%q user=%q", socket, socket.addr, db, user) // Note that this only works properly because this function is // synchronous, which means the nonce won't get reset while we're // using it and any other login requests will block waiting for a // new nonce provided in the defer call below. nonce, err := socket.getNonce() if err != nil { return err } defer socket.resetNonce() psum := md5.New() psum.Write([]byte(user + ":mongo:" + pass)) ksum := md5.New() ksum.Write([]byte(nonce + user)) ksum.Write([]byte(hex.EncodeToString(psum.Sum(nil)))) key := hex.EncodeToString(ksum.Sum(nil)) cmd := authCmd{Authenticate: 1, User: user, Nonce: nonce, Key: key} var mutex sync.Mutex var replyErr error mutex.Lock() op := queryOp{} op.query = &cmd op.collection = db + ".$cmd" op.limit = -1 op.replyFunc = func(err error, reply *replyOp, docNum int, docData []byte) { defer mutex.Unlock() if err != nil { replyErr = err return } // Must handle this within the read loop for the socket, so // that concurrent login requests are properly ordered. result := &authResult{} err = bson.Unmarshal(docData, result) if err != nil { replyErr = err return } if !result.Ok { replyErr = errors.New(result.ErrMsg) } socket.Lock() socket.dropAuth(db) socket.auth = append(socket.auth, authInfo{db, user, pass}) socket.Unlock() } err = socket.Query(&op) if err != nil { return err } mutex.Lock() // Wait. if replyErr != nil { debugf("Socket %p to %s: login error: %s", socket, socket.addr, replyErr) } else { debugf("Socket %p to %s: login successful", socket, socket.addr) } return replyErr } func (socket *mongoSocket) Logout(db string) { socket.Lock() auth, found := socket.dropAuth(db) if found { debugf("Socket %p to %s: logout: db=%q (flagged)", socket, socket.addr, db) socket.logout = append(socket.logout, auth) } socket.Unlock() } func (socket *mongoSocket) LogoutAll() { socket.Lock() if l := len(socket.auth); l > 0 { debugf("Socket %p to %s: logout all (flagged %d)", socket, socket.addr, l) socket.logout = append(socket.logout, socket.auth...) socket.auth = socket.auth[0:0] } socket.Unlock() } func (socket *mongoSocket) flushLogout() (ops []interface{}) { socket.Lock() if l := len(socket.logout); l > 0 { debugf("Socket %p to %s: logout all (flushing %d)", socket, socket.addr, l) for i := 0; i != l; i++ { op := queryOp{} op.query = &logoutCmd{1} op.collection = socket.logout[i].db + ".$cmd" op.limit = -1 ops = append(ops, &op) } socket.logout = socket.logout[0:0] } socket.Unlock() return } func (socket *mongoSocket) dropAuth(db string) (auth authInfo, found bool) { for i, a := range socket.auth { if a.db == db { copy(socket.auth[i:], socket.auth[i+1:]) socket.auth = socket.auth[:len(socket.auth)-1] return a, true } } return auth, false } func (socket *mongoSocket) dropLogout(db, user, pass string) (auth authInfo, found bool) { for i, a := range socket.logout { if a.db == db && a.user == user && a.pass == pass { copy(socket.logout[i:], socket.logout[i+1:]) socket.logout = socket.logout[:len(socket.logout)-1] return a, true } } return auth, false } juju-core_1.18.1/src/labix.org/v2/mgo/session_test.go0000644000015300001610000023417312321736015022312 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo_test import ( "flag" "fmt" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" . "launchpad.net/gocheck" "math" "reflect" "runtime" "sort" "strconv" "strings" "time" ) func (s *S) TestRunString(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() result := struct{ Ok int }{} err = session.Run("ping", &result) c.Assert(err, IsNil) c.Assert(result.Ok, Equals, 1) } func (s *S) TestRunValue(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() result := struct{ Ok int }{} err = session.Run(M{"ping": 1}, &result) c.Assert(err, IsNil) c.Assert(result.Ok, Equals, 1) } func (s *S) TestPing(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() // Just ensure the nonce has been received. result := struct{}{} err = session.Run("ping", &result) mgo.ResetStats() err = session.Ping() c.Assert(err, IsNil) // Pretty boring. stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 1) c.Assert(stats.ReceivedOps, Equals, 1) } func (s *S) TestURLSingle(c *C) { session, err := mgo.Dial("mongodb://localhost:40001/") c.Assert(err, IsNil) defer session.Close() result := struct{ Ok int }{} err = session.Run("ping", &result) c.Assert(err, IsNil) c.Assert(result.Ok, Equals, 1) } func (s *S) TestURLMany(c *C) { session, err := mgo.Dial("mongodb://localhost:40011,localhost:40012/") c.Assert(err, IsNil) defer session.Close() result := struct{ Ok int }{} err = session.Run("ping", &result) c.Assert(err, IsNil) c.Assert(result.Ok, Equals, 1) } func (s *S) TestURLParsing(c *C) { urls := []string{ "localhost:40001?foo=1&bar=2", "localhost:40001?foo=1;bar=2", } for _, url := range urls { session, err := mgo.Dial(url) if session != nil { session.Close() } c.Assert(err, ErrorMatches, "Unsupported connection URL option: (foo=1|bar=2)") } } func (s *S) TestInsertFindOne(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"a": 1, "b": 2}) result := struct{ A, B int }{} err = coll.Find(M{"a": 1}).One(&result) c.Assert(err, IsNil) c.Assert(result.A, Equals, 1) c.Assert(result.B, Equals, 2) } func (s *S) TestInsertFindOneNil(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Find(nil).One(nil) c.Assert(err, ErrorMatches, "unauthorized.*|not authorized.*") } func (s *S) TestInsertFindOneMap(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"a": 1, "b": 2}) result := make(M) err = coll.Find(M{"a": 1}).One(result) c.Assert(err, IsNil) c.Assert(result["a"], Equals, 1) c.Assert(result["b"], Equals, 2) } func (s *S) TestInsertFindAll(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"a": 1, "b": 2}) coll.Insert(M{"a": 3, "b": 4}) type R struct{ A, B int } var result []R assertResult := func() { c.Assert(len(result), Equals, 2) c.Assert(result[0].A, Equals, 1) c.Assert(result[0].B, Equals, 2) c.Assert(result[1].A, Equals, 3) c.Assert(result[1].B, Equals, 4) } // nil slice err = coll.Find(nil).Sort("a").All(&result) c.Assert(err, IsNil) assertResult() // Previously allocated slice allocd := make([]R, 5) result = allocd err = coll.Find(nil).Sort("a").All(&result) c.Assert(err, IsNil) assertResult() // Ensure result is backed by the originally allocated array c.Assert(&result[0], Equals, &allocd[0]) // Non-pointer slice error f := func() { coll.Find(nil).All(result) } c.Assert(f, Panics, "result argument must be a slice address") // Non-slice error f = func() { coll.Find(nil).All(new(int)) } c.Assert(f, Panics, "result argument must be a slice address") } func (s *S) TestFindRef(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() db1 := session.DB("db1") db1col1 := db1.C("col1") db2 := session.DB("db2") db2col1 := db2.C("col1") db1col1.Insert(M{"_id": 1, "n": 1}) db1col1.Insert(M{"_id": 2, "n": 2}) db2col1.Insert(M{"_id": 2, "n": 3}) result := struct{ N int }{} ref1 := &mgo.DBRef{Collection: "col1", Id: 1} ref2 := &mgo.DBRef{Collection: "col1", Id: 2, Database: "db2"} err = db1.FindRef(ref1).One(&result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 1) err = db1.FindRef(ref2).One(&result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 3) err = db2.FindRef(ref1).One(&result) c.Assert(err, Equals, mgo.ErrNotFound) err = db2.FindRef(ref2).One(&result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 3) err = session.FindRef(ref2).One(&result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 3) f := func() { session.FindRef(ref1).One(&result) } c.Assert(f, PanicMatches, "Can't resolve database for &mgo.DBRef{Collection:\"col1\", Id:1, Database:\"\"}") } func (s *S) TestDatabaseAndCollectionNames(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() db1 := session.DB("db1") db1col1 := db1.C("col1") db1col2 := db1.C("col2") db2 := session.DB("db2") db2col1 := db2.C("col3") db1col1.Insert(M{"_id": 1}) db1col2.Insert(M{"_id": 1}) db2col1.Insert(M{"_id": 1}) names, err := session.DatabaseNames() c.Assert(err, IsNil) if !reflect.DeepEqual(names, []string{"db1", "db2"}) { // 2.4+ has "local" as well. c.Assert(names, DeepEquals, []string{"db1", "db2", "local"}) } names, err = db1.CollectionNames() c.Assert(err, IsNil) c.Assert(names, DeepEquals, []string{"col1", "col2", "system.indexes"}) names, err = db2.CollectionNames() c.Assert(err, IsNil) c.Assert(names, DeepEquals, []string{"col3", "system.indexes"}) } func (s *S) TestSelect(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"a": 1, "b": 2}) result := struct{ A, B int }{} err = coll.Find(M{"a": 1}).Select(M{"b": 1}).One(&result) c.Assert(err, IsNil) c.Assert(result.A, Equals, 0) c.Assert(result.B, Equals, 2) } func (s *S) TestInlineMap(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") var v, result1 struct { A int M map[string]int ",inline" } v.A = 1 v.M = map[string]int{"b": 2} err = coll.Insert(v) c.Assert(err, IsNil) noId := M{"_id": 0} err = coll.Find(nil).Select(noId).One(&result1) c.Assert(err, IsNil) c.Assert(result1.A, Equals, 1) c.Assert(result1.M, DeepEquals, map[string]int{"b": 2}) var result2 M err = coll.Find(nil).Select(noId).One(&result2) c.Assert(err, IsNil) c.Assert(result2, DeepEquals, M{"a": 1, "b": 2}) } func (s *S) TestUpdate(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { err := coll.Insert(M{"k": n, "n": n}) c.Assert(err, IsNil) } err = coll.Update(M{"k": 42}, M{"$inc": M{"n": 1}}) c.Assert(err, IsNil) result := make(M) err = coll.Find(M{"k": 42}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 43) err = coll.Update(M{"k": 47}, M{"k": 47, "n": 47}) c.Assert(err, Equals, mgo.ErrNotFound) err = coll.Find(M{"k": 47}).One(result) c.Assert(err, Equals, mgo.ErrNotFound) } func (s *S) TestUpdateId(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { err := coll.Insert(M{"_id": n, "n": n}) c.Assert(err, IsNil) } err = coll.UpdateId(42, M{"$inc": M{"n": 1}}) c.Assert(err, IsNil) result := make(M) err = coll.FindId(42).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 43) err = coll.UpdateId(47, M{"k": 47, "n": 47}) c.Assert(err, Equals, mgo.ErrNotFound) err = coll.FindId(47).One(result) c.Assert(err, Equals, mgo.ErrNotFound) } func (s *S) TestUpdateNil(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"k": 42, "n": 42}) c.Assert(err, IsNil) err = coll.Update(nil, M{"$inc": M{"n": 1}}) c.Assert(err, IsNil) result := make(M) err = coll.Find(M{"k": 42}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 43) err = coll.Insert(M{"k": 45, "n": 45}) c.Assert(err, IsNil) _, err = coll.UpdateAll(nil, M{"$inc": M{"n": 1}}) c.Assert(err, IsNil) err = coll.Find(M{"k": 42}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 44) err = coll.Find(M{"k": 45}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 46) } func (s *S) TestUpsert(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { err := coll.Insert(M{"k": n, "n": n}) c.Assert(err, IsNil) } info, err := coll.Upsert(M{"k": 42}, M{"k": 42, "n": 24}) c.Assert(err, IsNil) c.Assert(info.Updated, Equals, 1) c.Assert(info.UpsertedId, IsNil) result := M{} err = coll.Find(M{"k": 42}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 24) // Insert with internally created id. info, err = coll.Upsert(M{"k": 47}, M{"k": 47, "n": 47}) c.Assert(err, IsNil) c.Assert(info.Updated, Equals, 0) c.Assert(info.UpsertedId, NotNil) err = coll.Find(M{"k": 47}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 47) result = M{} err = coll.Find(M{"_id": info.UpsertedId}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 47) // Insert with provided id. info, err = coll.Upsert(M{"k": 48}, M{"k": 48, "n": 48, "_id": 48}) c.Assert(err, IsNil) c.Assert(info.Updated, Equals, 0) c.Assert(info.UpsertedId, IsNil) // Unfortunate, but that's what Mongo gives us. err = coll.Find(M{"k": 48}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 48) } func (s *S) TestUpsertId(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { err := coll.Insert(M{"_id": n, "n": n}) c.Assert(err, IsNil) } info, err := coll.UpsertId(42, M{"n": 24}) c.Assert(err, IsNil) c.Assert(info.Updated, Equals, 1) c.Assert(info.UpsertedId, IsNil) result := M{} err = coll.FindId(42).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 24) info, err = coll.UpsertId(47, M{"_id": 47, "n": 47}) c.Assert(err, IsNil) c.Assert(info.Updated, Equals, 0) c.Assert(info.UpsertedId, IsNil) err = coll.FindId(47).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 47) } func (s *S) TestUpdateAll(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { err := coll.Insert(M{"k": n, "n": n}) c.Assert(err, IsNil) } info, err := coll.UpdateAll(M{"k": M{"$gt": 42}}, M{"$inc": M{"n": 1}}) c.Assert(err, IsNil) c.Assert(info.Updated, Equals, 4) result := make(M) err = coll.Find(M{"k": 42}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 42) err = coll.Find(M{"k": 43}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 44) err = coll.Find(M{"k": 44}).One(result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 45) info, err = coll.UpdateAll(M{"k": 47}, M{"k": 47, "n": 47}) c.Assert(err, Equals, nil) c.Assert(info.Updated, Equals, 0) } func (s *S) TestRemove(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { err := coll.Insert(M{"n": n}) c.Assert(err, IsNil) } err = coll.Remove(M{"n": M{"$gt": 42}}) c.Assert(err, IsNil) result := &struct{ N int }{} err = coll.Find(M{"n": 42}).One(result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 42) err = coll.Find(M{"n": 43}).One(result) c.Assert(err, Equals, mgo.ErrNotFound) err = coll.Find(M{"n": 44}).One(result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 44) } func (s *S) TestRemoveId(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"_id": 40}, M{"_id": 41}, M{"_id": 42}) c.Assert(err, IsNil) err = coll.RemoveId(41) c.Assert(err, IsNil) c.Assert(coll.FindId(40).One(nil), IsNil) c.Assert(coll.FindId(41).One(nil), Equals, mgo.ErrNotFound) c.Assert(coll.FindId(42).One(nil), IsNil) } func (s *S) TestRemoveAll(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { err := coll.Insert(M{"n": n}) c.Assert(err, IsNil) } info, err := coll.RemoveAll(M{"n": M{"$gt": 42}}) c.Assert(err, IsNil) c.Assert(info.Updated, Equals, 0) c.Assert(info.Removed, Equals, 4) c.Assert(info.UpsertedId, IsNil) result := &struct{ N int }{} err = coll.Find(M{"n": 42}).One(result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 42) err = coll.Find(M{"n": 43}).One(result) c.Assert(err, Equals, mgo.ErrNotFound) err = coll.Find(M{"n": 44}).One(result) c.Assert(err, Equals, mgo.ErrNotFound) } func (s *S) TestDropDatabase(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() db1 := session.DB("db1") db1.C("col").Insert(M{"_id": 1}) db2 := session.DB("db2") db2.C("col").Insert(M{"_id": 1}) err = db1.DropDatabase() c.Assert(err, IsNil) names, err := session.DatabaseNames() c.Assert(err, IsNil) if !reflect.DeepEqual(names, []string{"db2"}) { // 2.4+ has "local" as well. c.Assert(names, DeepEquals, []string{"db2", "local"}) } err = db2.DropDatabase() c.Assert(err, IsNil) names, err = session.DatabaseNames() c.Assert(err, IsNil) if !reflect.DeepEqual(names, []string(nil)) { // 2.4+ has "local" as well. c.Assert(names, DeepEquals, []string{"local"}) } } func (s *S) TestDropCollection(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() db := session.DB("db1") db.C("col1").Insert(M{"_id": 1}) db.C("col2").Insert(M{"_id": 1}) err = db.C("col1").DropCollection() c.Assert(err, IsNil) names, err := db.CollectionNames() c.Assert(err, IsNil) c.Assert(names, DeepEquals, []string{"col2", "system.indexes"}) err = db.C("col2").DropCollection() c.Assert(err, IsNil) names, err = db.CollectionNames() c.Assert(err, IsNil) c.Assert(names, DeepEquals, []string{"system.indexes"}) } func (s *S) TestCreateCollectionCapped(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") info := &mgo.CollectionInfo{ Capped: true, MaxBytes: 1024, MaxDocs: 3, } err = coll.Create(info) c.Assert(err, IsNil) ns := []int{1, 2, 3, 4, 5} for _, n := range ns { err := coll.Insert(M{"n": n}) c.Assert(err, IsNil) } n, err := coll.Find(nil).Count() c.Assert(err, IsNil) c.Assert(n, Equals, 3) } func (s *S) TestCreateCollectionNoIndex(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") info := &mgo.CollectionInfo{ DisableIdIndex: true, } err = coll.Create(info) c.Assert(err, IsNil) err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) indexes, err := coll.Indexes() c.Assert(indexes, HasLen, 0) } func (s *S) TestCreateCollectionForceIndex(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") info := &mgo.CollectionInfo{ ForceIdIndex: true, Capped: true, MaxBytes: 1024, } err = coll.Create(info) c.Assert(err, IsNil) err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) indexes, err := coll.Indexes() c.Assert(indexes, HasLen, 1) } func (s *S) TestIsDupValues(c *C) { c.Assert(mgo.IsDup(nil), Equals, false) c.Assert(mgo.IsDup(&mgo.LastError{Code: 1}), Equals, false) c.Assert(mgo.IsDup(&mgo.QueryError{Code: 1}), Equals, false) c.Assert(mgo.IsDup(&mgo.LastError{Code: 11000}), Equals, true) c.Assert(mgo.IsDup(&mgo.QueryError{Code: 11000}), Equals, true) c.Assert(mgo.IsDup(&mgo.LastError{Code: 11001}), Equals, true) c.Assert(mgo.IsDup(&mgo.QueryError{Code: 11001}), Equals, true) c.Assert(mgo.IsDup(&mgo.LastError{Code: 12582}), Equals, true) c.Assert(mgo.IsDup(&mgo.QueryError{Code: 12582}), Equals, true) } func (s *S) TestIsDupPrimary(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"_id": 1}) c.Assert(err, IsNil) err = coll.Insert(M{"_id": 1}) c.Assert(err, ErrorMatches, ".*duplicate key error.*") c.Assert(mgo.IsDup(err), Equals, true) } func (s *S) TestIsDupUnique(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() index := mgo.Index{ Key: []string{"a", "b"}, Unique: true, } coll := session.DB("mydb").C("mycoll") err = coll.EnsureIndex(index) c.Assert(err, IsNil) err = coll.Insert(M{"a": 1, "b": 1}) c.Assert(err, IsNil) err = coll.Insert(M{"a": 1, "b": 1}) c.Assert(err, ErrorMatches, ".*duplicate key error.*") c.Assert(mgo.IsDup(err), Equals, true) } func (s *S) TestIsDupCapped(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") info := &mgo.CollectionInfo{ ForceIdIndex: true, Capped: true, MaxBytes: 1024, } err = coll.Create(info) c.Assert(err, IsNil) err = coll.Insert(M{"_id": 1}) c.Assert(err, IsNil) err = coll.Insert(M{"_id": 1}) // Quite unfortunate that the error is different for capped collections. c.Assert(err, ErrorMatches, "duplicate key.*capped collection") // The issue is reduced by using IsDup. c.Assert(mgo.IsDup(err), Equals, true) } func (s *S) TestIsDupFindAndModify(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.EnsureIndex(mgo.Index{Key: []string{"n"}, Unique: true}) c.Assert(err, IsNil) err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) err = coll.Insert(M{"n": 2}) c.Assert(err, IsNil) _, err = coll.Find(M{"n": 1}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}}, bson.M{}) c.Assert(err, ErrorMatches, ".*duplicate key error.*") c.Assert(mgo.IsDup(err), Equals, true) } func (s *S) TestFindAndModify(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 42}) session.SetMode(mgo.Monotonic, true) result := M{} info, err := coll.Find(M{"n": 42}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}}, result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 42) c.Assert(info.Updated, Equals, 1) c.Assert(info.Removed, Equals, 0) c.Assert(info.UpsertedId, IsNil) result = M{} info, err = coll.Find(M{"n": 43}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}, ReturnNew: true}, result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 44) c.Assert(info.Updated, Equals, 1) c.Assert(info.Removed, Equals, 0) c.Assert(info.UpsertedId, IsNil) result = M{} info, err = coll.Find(M{"n": 50}).Apply(mgo.Change{Upsert: true, Update: M{"n": 51, "o": 52}}, result) c.Assert(err, IsNil) c.Assert(result["n"], IsNil) c.Assert(info.Updated, Equals, 0) c.Assert(info.Removed, Equals, 0) c.Assert(info.UpsertedId, NotNil) result = M{} info, err = coll.Find(nil).Sort("-n").Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}, ReturnNew: true}, result) c.Assert(err, IsNil) c.Assert(result["n"], Equals, 52) c.Assert(info.Updated, Equals, 1) c.Assert(info.Removed, Equals, 0) c.Assert(info.UpsertedId, IsNil) result = M{} info, err = coll.Find(M{"n": 52}).Select(M{"o": 1}).Apply(mgo.Change{Remove: true}, result) c.Assert(err, IsNil) c.Assert(result["n"], IsNil) c.Assert(result["o"], Equals, 52) c.Assert(info.Updated, Equals, 0) c.Assert(info.Removed, Equals, 1) c.Assert(info.UpsertedId, IsNil) result = M{} info, err = coll.Find(M{"n": 60}).Apply(mgo.Change{Remove: true}, result) c.Assert(err, Equals, mgo.ErrNotFound) c.Assert(len(result), Equals, 0) c.Assert(info, IsNil) } func (s *S) TestFindAndModifyBug997828(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": "not-a-number"}) result := make(M) _, err = coll.Find(M{"n": "not-a-number"}).Apply(mgo.Change{Update: M{"$inc": M{"n": 1}}}, result) c.Assert(err, ErrorMatches, `(exception: )?Cannot apply \$inc modifier to non-number`) if s.versionAtLeast(2, 1) { qerr, _ := err.(*mgo.QueryError) c.Assert(qerr, NotNil, Commentf("err: %#v", err)) c.Assert(qerr.Code, Equals, 10140) } else { lerr, _ := err.(*mgo.LastError) c.Assert(lerr, NotNil, Commentf("err: %#v", err)) c.Assert(lerr.Code, Equals, 10140) } } func (s *S) TestCountCollection(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42} for _, n := range ns { err := coll.Insert(M{"n": n}) c.Assert(err, IsNil) } n, err := coll.Count() c.Assert(err, IsNil) c.Assert(n, Equals, 3) } func (s *S) TestCountQuery(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42} for _, n := range ns { err := coll.Insert(M{"n": n}) c.Assert(err, IsNil) } n, err := coll.Find(M{"n": M{"$gt": 40}}).Count() c.Assert(err, IsNil) c.Assert(n, Equals, 2) } func (s *S) TestCountQuerySorted(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42} for _, n := range ns { err := coll.Insert(M{"n": n}) c.Assert(err, IsNil) } n, err := coll.Find(M{"n": M{"$gt": 40}}).Sort("n").Count() c.Assert(err, IsNil) c.Assert(n, Equals, 2) } func (s *S) TestCountSkipLimit(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44} for _, n := range ns { err := coll.Insert(M{"n": n}) c.Assert(err, IsNil) } n, err := coll.Find(nil).Skip(1).Limit(3).Count() c.Assert(err, IsNil) c.Assert(n, Equals, 3) n, err = coll.Find(nil).Skip(1).Limit(5).Count() c.Assert(err, IsNil) c.Assert(n, Equals, 4) } func (s *S) TestQueryExplain(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42} for _, n := range ns { err := coll.Insert(M{"n": n}) c.Assert(err, IsNil) } m := M{} query := coll.Find(nil).Limit(2) err = query.Explain(m) c.Assert(err, IsNil) c.Assert(m["cursor"], Equals, "BasicCursor") c.Assert(m["nscanned"], Equals, 2) c.Assert(m["n"], Equals, 2) n := 0 var result M iter := query.Iter() for iter.Next(&result) { n++ } c.Assert(iter.Close(), IsNil) c.Assert(n, Equals, 2) } func (s *S) TestQueryHint(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.EnsureIndexKey("a") m := M{} err = coll.Find(nil).Hint("a").Explain(m) c.Assert(err, IsNil) c.Assert(m["indexBounds"], NotNil) c.Assert(m["indexBounds"].(M)["a"], NotNil) } func (s *S) TestFindOneNotFound(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") result := struct{ A, B int }{} err = coll.Find(M{"a": 1}).One(&result) c.Assert(err, Equals, mgo.ErrNotFound) c.Assert(err, ErrorMatches, "not found") c.Assert(err == mgo.ErrNotFound, Equals, true) } func (s *S) TestFindNil(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) result := struct{ N int }{} err = coll.Find(nil).One(&result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 1) } func (s *S) TestFindId(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"_id": 41, "n": 41}) c.Assert(err, IsNil) err = coll.Insert(M{"_id": 42, "n": 42}) c.Assert(err, IsNil) result := struct{ N int }{} err = coll.FindId(42).One(&result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 42) } func (s *S) TestFindIterAll(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } session.Refresh() // Release socket. mgo.ResetStats() iter := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2).Iter() result := struct{ N int }{} for i := 2; i < 7; i++ { ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(result.N, Equals, ns[i]) if i == 1 { stats := mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, 2) } } ok := iter.Next(&result) c.Assert(ok, Equals, false) c.Assert(iter.Close(), IsNil) session.Refresh() // Release socket. stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP c.Assert(stats.ReceivedOps, Equals, 3) // and their REPLY_OPs. c.Assert(stats.ReceivedDocs, Equals, 5) c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestFindIterTwiceWithSameQuery(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for i := 40; i != 47; i++ { coll.Insert(M{"n": i}) } query := coll.Find(M{}).Sort("n") result1 := query.Skip(1).Iter() result2 := query.Skip(2).Iter() result := struct{ N int }{} ok := result2.Next(&result) c.Assert(ok, Equals, true) c.Assert(result.N, Equals, 42) ok = result1.Next(&result) c.Assert(ok, Equals, true) c.Assert(result.N, Equals, 41) } func (s *S) TestFindIterWithoutResults(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"n": 42}) iter := coll.Find(M{"n": 0}).Iter() result := struct{ N int }{} ok := iter.Next(&result) c.Assert(ok, Equals, false) c.Assert(iter.Close(), IsNil) c.Assert(result.N, Equals, 0) } func (s *S) TestFindIterLimit(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } session.Refresh() // Release socket. mgo.ResetStats() query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Limit(3) iter := query.Iter() result := struct{ N int }{} for i := 2; i < 5; i++ { ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(result.N, Equals, ns[i]) } ok := iter.Next(&result) c.Assert(ok, Equals, false) c.Assert(iter.Close(), IsNil) session.Refresh() // Release socket. stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 2) // 1*QUERY_OP + 1*KILL_CURSORS_OP c.Assert(stats.ReceivedOps, Equals, 1) // and its REPLY_OP c.Assert(stats.ReceivedDocs, Equals, 3) c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestTooManyItemsLimitBug(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU())) mgo.SetDebug(false) coll := session.DB("mydb").C("mycoll") words := strings.Split("foo bar baz", " ") for i := 0; i < 5; i++ { words = append(words, words...) } doc := bson.D{{"words", words}} inserts := 10000 limit := 5000 iters := 0 c.Assert(inserts > limit, Equals, true) for i := 0; i < inserts; i++ { err := coll.Insert(&doc) c.Assert(err, IsNil) } iter := coll.Find(nil).Limit(limit).Iter() for iter.Next(&doc) { if iters%100 == 0 { c.Logf("Seen %d docments", iters) } iters++ } c.Assert(iter.Close(), IsNil) c.Assert(iters, Equals, limit) } func serverCursorsOpen(session *mgo.Session) int { var result struct { Cursors struct { TotalOpen int `bson:"totalOpen"` TimedOut int `bson:"timedOut"` } } err := session.Run("serverStatus", &result) if err != nil { panic(err) } return result.Cursors.TotalOpen } func (s *S) TestFindIterLimitWithMore(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") // Insane amounts of logging otherwise due to the // amount of data being shuffled. mgo.SetDebug(false) defer mgo.SetDebug(true) // Should amount to more than 4MB bson payload, // the default limit per result chunk. const total = 4096 var d struct{ A [1024]byte } docs := make([]interface{}, total) for i := 0; i < total; i++ { docs[i] = &d } err = coll.Insert(docs...) c.Assert(err, IsNil) n, err := coll.Count() c.Assert(err, IsNil) c.Assert(n, Equals, total) // First, try restricting to a single chunk with a negative limit. nresults := 0 iter := coll.Find(nil).Limit(-total).Iter() var discard struct{} for iter.Next(&discard) { nresults++ } if nresults < total/2 || nresults >= total { c.Fatalf("Bad result size with negative limit: %d", nresults) } cursorsOpen := serverCursorsOpen(session) // Try again, with a positive limit. Should reach the end now, // using multiple chunks. nresults = 0 iter = coll.Find(nil).Limit(total).Iter() for iter.Next(&discard) { nresults++ } c.Assert(nresults, Equals, total) // Ensure the cursor used is properly killed. c.Assert(serverCursorsOpen(session), Equals, cursorsOpen) // Edge case, -MinInt == -MinInt. nresults = 0 iter = coll.Find(nil).Limit(math.MinInt32).Iter() for iter.Next(&discard) { nresults++ } if nresults < total/2 || nresults >= total { c.Fatalf("Bad result size with MinInt32 limit: %d", nresults) } } func (s *S) TestFindIterLimitWithBatch(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } // Ping the database to ensure the nonce has been received already. c.Assert(session.Ping(), IsNil) session.Refresh() // Release socket. mgo.ResetStats() query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Limit(3).Batch(2) iter := query.Iter() result := struct{ N int }{} for i := 2; i < 5; i++ { ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(result.N, Equals, ns[i]) if i == 3 { stats := mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, 2) } } ok := iter.Next(&result) c.Assert(ok, Equals, false) c.Assert(iter.Close(), IsNil) session.Refresh() // Release socket. stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 1*GET_MORE_OP + 1*KILL_CURSORS_OP c.Assert(stats.ReceivedOps, Equals, 2) // and its REPLY_OPs c.Assert(stats.ReceivedDocs, Equals, 3) c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestFindIterSortWithBatch(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } // Without this, the logic above breaks because Mongo refuses to // return a cursor with an in-memory sort. coll.EnsureIndexKey("n") // Ping the database to ensure the nonce has been received already. c.Assert(session.Ping(), IsNil) session.Refresh() // Release socket. mgo.ResetStats() query := coll.Find(M{"n": M{"$lte": 44}}).Sort("-n").Batch(2) iter := query.Iter() ns = []int{46, 45, 44, 43, 42, 41, 40} result := struct{ N int }{} for i := 2; i < len(ns); i++ { c.Logf("i=%d", i) ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(result.N, Equals, ns[i]) if i == 3 { stats := mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, 2) } } ok := iter.Next(&result) c.Assert(ok, Equals, false) c.Assert(iter.Close(), IsNil) session.Refresh() // Release socket. stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP c.Assert(stats.ReceivedOps, Equals, 3) // and its REPLY_OPs c.Assert(stats.ReceivedDocs, Equals, 5) c.Assert(stats.SocketsInUse, Equals, 0) } // Test tailable cursors in a situation where Next has to sleep to // respect the timeout requested on Tail. func (s *S) TestFindTailTimeoutWithSleep(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() cresult := struct{ ErrMsg string }{} db := session.DB("mydb") err = db.Run(bson.D{{"create", "mycoll"}, {"capped", true}, {"size", 1024}}, &cresult) c.Assert(err, IsNil) c.Assert(cresult.ErrMsg, Equals, "") coll := db.C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } session.Refresh() // Release socket. mgo.ResetStats() timeout := 3 * time.Second query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2) iter := query.Tail(timeout) n := len(ns) result := struct{ N int }{} for i := 2; i != n; i++ { ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(iter.Err(), IsNil) c.Assert(iter.Timeout(), Equals, false) c.Assert(result.N, Equals, ns[i]) if i == 3 { // The batch boundary. stats := mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, 2) } } mgo.ResetStats() // The following call to Next will block. go func() { // The internal AwaitData timing of MongoDB is around 2 seconds, // so this should force mgo to sleep at least once by itself to // respect the requested timeout. time.Sleep(timeout + 5e8*time.Nanosecond) session := session.New() defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"n": 47}) }() c.Log("Will wait for Next with N=47...") ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(iter.Err(), IsNil) c.Assert(iter.Timeout(), Equals, false) c.Assert(result.N, Equals, 47) c.Log("Got Next with N=47!") // The following may break because it depends a bit on the internal // timing used by MongoDB's AwaitData logic. If it does, the problem // will be observed as more GET_MORE_OPs than predicted: // 1*QUERY for nonce + 1*GET_MORE_OP on Next + 1*GET_MORE_OP on Next after sleep + // 1*INSERT_OP + 1*QUERY_OP for getLastError on insert of 47 stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 5) c.Assert(stats.ReceivedOps, Equals, 4) // REPLY_OPs for 1*QUERY_OP for nonce + 2*GET_MORE_OPs + 1*QUERY_OP c.Assert(stats.ReceivedDocs, Equals, 3) // nonce + N=47 result + getLastError response c.Log("Will wait for a result which will never come...") started := time.Now() ok = iter.Next(&result) c.Assert(ok, Equals, false) c.Assert(iter.Err(), IsNil) c.Assert(iter.Timeout(), Equals, true) c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true) c.Log("Will now reuse the timed out tail cursor...") coll.Insert(M{"n": 48}) ok = iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(iter.Close(), IsNil) c.Assert(iter.Timeout(), Equals, false) c.Assert(result.N, Equals, 48) } // Test tailable cursors in a situation where Next never gets to sleep once // to respect the timeout requested on Tail. func (s *S) TestFindTailTimeoutNoSleep(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() cresult := struct{ ErrMsg string }{} db := session.DB("mydb") err = db.Run(bson.D{{"create", "mycoll"}, {"capped", true}, {"size", 1024}}, &cresult) c.Assert(err, IsNil) c.Assert(cresult.ErrMsg, Equals, "") coll := db.C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } session.Refresh() // Release socket. mgo.ResetStats() timeout := 1 * time.Second query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2) iter := query.Tail(timeout) n := len(ns) result := struct{ N int }{} for i := 2; i != n; i++ { ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(iter.Err(), IsNil) c.Assert(iter.Timeout(), Equals, false) c.Assert(result.N, Equals, ns[i]) if i == 3 { // The batch boundary. stats := mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, 2) } } mgo.ResetStats() // The following call to Next will block. go func() { // The internal AwaitData timing of MongoDB is around 2 seconds, // so this item should arrive within the AwaitData threshold. time.Sleep(5e8) session := session.New() defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"n": 47}) }() c.Log("Will wait for Next with N=47...") ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(iter.Err(), IsNil) c.Assert(iter.Timeout(), Equals, false) c.Assert(result.N, Equals, 47) c.Log("Got Next with N=47!") // The following may break because it depends a bit on the internal // timing used by MongoDB's AwaitData logic. If it does, the problem // will be observed as more GET_MORE_OPs than predicted: // 1*QUERY_OP for nonce + 1*GET_MORE_OP on Next + // 1*INSERT_OP + 1*QUERY_OP for getLastError on insert of 47 stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 4) c.Assert(stats.ReceivedOps, Equals, 3) // REPLY_OPs for 1*QUERY_OP for nonce + 1*GET_MORE_OPs and 1*QUERY_OP c.Assert(stats.ReceivedDocs, Equals, 3) // nonce + N=47 result + getLastError response c.Log("Will wait for a result which will never come...") started := time.Now() ok = iter.Next(&result) c.Assert(ok, Equals, false) c.Assert(iter.Err(), IsNil) c.Assert(iter.Timeout(), Equals, true) c.Assert(started.Before(time.Now().Add(-timeout)), Equals, true) c.Log("Will now reuse the timed out tail cursor...") coll.Insert(M{"n": 48}) ok = iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(iter.Close(), IsNil) c.Assert(iter.Timeout(), Equals, false) c.Assert(result.N, Equals, 48) } // Test tailable cursors in a situation where Next never gets to sleep once // to respect the timeout requested on Tail. func (s *S) TestFindTailNoTimeout(c *C) { if *fast { c.Skip("-fast") } session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() cresult := struct{ ErrMsg string }{} db := session.DB("mydb") err = db.Run(bson.D{{"create", "mycoll"}, {"capped", true}, {"size", 1024}}, &cresult) c.Assert(err, IsNil) c.Assert(cresult.ErrMsg, Equals, "") coll := db.C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } session.Refresh() // Release socket. mgo.ResetStats() query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2) iter := query.Tail(-1) c.Assert(err, IsNil) n := len(ns) result := struct{ N int }{} for i := 2; i != n; i++ { ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(result.N, Equals, ns[i]) if i == 3 { // The batch boundary. stats := mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, 2) } } mgo.ResetStats() // The following call to Next will block. go func() { time.Sleep(5e8) session := session.New() defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"n": 47}) }() c.Log("Will wait for Next with N=47...") ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(iter.Err(), IsNil) c.Assert(iter.Timeout(), Equals, false) c.Assert(result.N, Equals, 47) c.Log("Got Next with N=47!") // The following may break because it depends a bit on the internal // timing used by MongoDB's AwaitData logic. If it does, the problem // will be observed as more GET_MORE_OPs than predicted: // 1*QUERY_OP for nonce + 1*GET_MORE_OP on Next + // 1*INSERT_OP + 1*QUERY_OP for getLastError on insert of 47 stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 4) c.Assert(stats.ReceivedOps, Equals, 3) // REPLY_OPs for 1*QUERY_OP for nonce + 1*GET_MORE_OPs and 1*QUERY_OP c.Assert(stats.ReceivedDocs, Equals, 3) // nonce + N=47 result + getLastError response c.Log("Will wait for a result which will never come...") gotNext := make(chan bool) go func() { ok := iter.Next(&result) gotNext <- ok }() select { case ok := <-gotNext: c.Fatalf("Next returned: %v", ok) case <-time.After(3e9): // Good. Should still be sleeping at that point. } // Closing the session should cause Next to return. session.Close() select { case ok := <-gotNext: c.Assert(ok, Equals, false) c.Assert(iter.Err(), ErrorMatches, "Closed explicitly") c.Assert(iter.Timeout(), Equals, false) case <-time.After(1e9): c.Fatal("Closing the session did not unblock Next") } } func (s *S) TestIterNextResetsResult(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{1, 2, 3} for _, n := range ns { coll.Insert(M{"n" + strconv.Itoa(n): n}) } query := coll.Find(nil).Sort("$natural") i := 0 var sresult *struct{ N1, N2, N3 int } iter := query.Iter() for iter.Next(&sresult) { switch i { case 0: c.Assert(sresult.N1, Equals, 1) c.Assert(sresult.N2+sresult.N3, Equals, 0) case 1: c.Assert(sresult.N2, Equals, 2) c.Assert(sresult.N1+sresult.N3, Equals, 0) case 2: c.Assert(sresult.N3, Equals, 3) c.Assert(sresult.N1+sresult.N2, Equals, 0) } i++ } c.Assert(iter.Close(), IsNil) i = 0 var mresult M iter = query.Iter() for iter.Next(&mresult) { delete(mresult, "_id") switch i { case 0: c.Assert(mresult, DeepEquals, M{"n1": 1}) case 1: c.Assert(mresult, DeepEquals, M{"n2": 2}) case 2: c.Assert(mresult, DeepEquals, M{"n3": 3}) } i++ } c.Assert(iter.Close(), IsNil) i = 0 var iresult interface{} iter = query.Iter() for iter.Next(&iresult) { mresult, ok := iresult.(bson.M) c.Assert(ok, Equals, true, Commentf("%#v", iresult)) delete(mresult, "_id") switch i { case 0: c.Assert(mresult, DeepEquals, bson.M{"n1": 1}) case 1: c.Assert(mresult, DeepEquals, bson.M{"n2": 2}) case 2: c.Assert(mresult, DeepEquals, bson.M{"n3": 3}) } i++ } c.Assert(iter.Close(), IsNil) } func (s *S) TestFindForOnIter(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } session.Refresh() // Release socket. mgo.ResetStats() query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2) iter := query.Iter() i := 2 var result *struct{ N int } err = iter.For(&result, func() error { c.Assert(i < 7, Equals, true) c.Assert(result.N, Equals, ns[i]) if i == 1 { stats := mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, 2) } i++ return nil }) c.Assert(err, IsNil) session.Refresh() // Release socket. stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP c.Assert(stats.ReceivedOps, Equals, 3) // and their REPLY_OPs. c.Assert(stats.ReceivedDocs, Equals, 5) c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestFindFor(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } session.Refresh() // Release socket. mgo.ResetStats() query := coll.Find(M{"n": M{"$gte": 42}}).Sort("$natural").Prefetch(0).Batch(2) i := 2 var result *struct{ N int } err = query.For(&result, func() error { c.Assert(i < 7, Equals, true) c.Assert(result.N, Equals, ns[i]) if i == 1 { stats := mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, 2) } i++ return nil }) c.Assert(err, IsNil) session.Refresh() // Release socket. stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 3) // 1*QUERY_OP + 2*GET_MORE_OP c.Assert(stats.ReceivedOps, Equals, 3) // and their REPLY_OPs. c.Assert(stats.ReceivedDocs, Equals, 5) c.Assert(stats.SocketsInUse, Equals, 0) } func (s *S) TestFindForStopOnError(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } query := coll.Find(M{"n": M{"$gte": 42}}) i := 2 var result *struct{ N int } err = query.For(&result, func() error { c.Assert(i < 4, Equals, true) c.Assert(result.N, Equals, ns[i]) if i == 3 { return fmt.Errorf("stop!") } i++ return nil }) c.Assert(err, ErrorMatches, "stop!") } func (s *S) TestFindForResetsResult(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{1, 2, 3} for _, n := range ns { coll.Insert(M{"n" + strconv.Itoa(n): n}) } query := coll.Find(nil).Sort("$natural") i := 0 var sresult *struct{ N1, N2, N3 int } err = query.For(&sresult, func() error { switch i { case 0: c.Assert(sresult.N1, Equals, 1) c.Assert(sresult.N2+sresult.N3, Equals, 0) case 1: c.Assert(sresult.N2, Equals, 2) c.Assert(sresult.N1+sresult.N3, Equals, 0) case 2: c.Assert(sresult.N3, Equals, 3) c.Assert(sresult.N1+sresult.N2, Equals, 0) } i++ return nil }) c.Assert(err, IsNil) i = 0 var mresult M err = query.For(&mresult, func() error { delete(mresult, "_id") switch i { case 0: c.Assert(mresult, DeepEquals, M{"n1": 1}) case 1: c.Assert(mresult, DeepEquals, M{"n2": 2}) case 2: c.Assert(mresult, DeepEquals, M{"n3": 3}) } i++ return nil }) c.Assert(err, IsNil) i = 0 var iresult interface{} err = query.For(&iresult, func() error { mresult, ok := iresult.(bson.M) c.Assert(ok, Equals, true, Commentf("%#v", iresult)) delete(mresult, "_id") switch i { case 0: c.Assert(mresult, DeepEquals, bson.M{"n1": 1}) case 1: c.Assert(mresult, DeepEquals, bson.M{"n2": 2}) case 2: c.Assert(mresult, DeepEquals, bson.M{"n3": 3}) } i++ return nil }) c.Assert(err, IsNil) } func (s *S) TestFindIterSnapshot(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() // Insane amounts of logging otherwise due to the // amount of data being shuffled. mgo.SetDebug(false) defer mgo.SetDebug(true) coll := session.DB("mydb").C("mycoll") var a [1024000]byte for n := 0; n < 10; n++ { err := coll.Insert(M{"_id": n, "n": n, "a1": &a}) c.Assert(err, IsNil) } query := coll.Find(M{"n": M{"$gt": -1}}).Batch(2).Prefetch(0) query.Snapshot() iter := query.Iter() seen := map[int]bool{} result := struct { Id int "_id" }{} for iter.Next(&result) { if len(seen) == 2 { // Grow all entries so that they have to move. // Backwards so that the order is inverted. for n := 10; n >= 0; n-- { _, err := coll.Upsert(M{"_id": n}, M{"$set": M{"a2": &a}}) c.Assert(err, IsNil) } } if seen[result.Id] { c.Fatalf("seen duplicated key: %d", result.Id) } seen[result.Id] = true } c.Assert(iter.Close(), IsNil) } func (s *S) TestSort(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"a": 1, "b": 1}) coll.Insert(M{"a": 2, "b": 2}) coll.Insert(M{"a": 2, "b": 1}) coll.Insert(M{"a": 0, "b": 1}) coll.Insert(M{"a": 2, "b": 0}) coll.Insert(M{"a": 0, "b": 2}) coll.Insert(M{"a": 1, "b": 2}) coll.Insert(M{"a": 0, "b": 0}) coll.Insert(M{"a": 1, "b": 0}) query := coll.Find(M{}) query.Sort("-a") // Should be ignored. query.Sort("-b", "a") iter := query.Iter() l := make([]int, 18) r := struct{ A, B int }{} for i := 0; i != len(l); i += 2 { ok := iter.Next(&r) c.Assert(ok, Equals, true) c.Assert(err, IsNil) l[i] = r.A l[i+1] = r.B } c.Assert(l, DeepEquals, []int{0, 2, 1, 2, 2, 2, 0, 1, 1, 1, 2, 1, 0, 0, 1, 0, 2, 0}) } func (s *S) TestSortWithBadArgs(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") f1 := func() { coll.Find(nil).Sort("") } f2 := func() { coll.Find(nil).Sort("+") } f3 := func() { coll.Find(nil).Sort("foo", "-") } for _, f := range []func(){f1, f2, f3} { c.Assert(f, PanicMatches, "Sort: empty field name") } } func (s *S) TestPrefetching(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") mgo.SetDebug(false) docs := make([]interface{}, 800) for i := 0; i != 600; i++ { docs[i] = bson.D{{"n", i}} } coll.Insert(docs...) for testi := 0; testi < 5; testi++ { mgo.ResetStats() var iter *mgo.Iter var beforeMore int switch testi { case 0: // The default session value. session.SetBatch(100) iter = coll.Find(M{}).Iter() beforeMore = 75 case 2: // Changing the session value. session.SetBatch(100) session.SetPrefetch(0.27) iter = coll.Find(M{}).Iter() beforeMore = 73 case 1: // Changing via query methods. iter = coll.Find(M{}).Prefetch(0.27).Batch(100).Iter() beforeMore = 73 case 3: // With prefetch on first document. iter = coll.Find(M{}).Prefetch(1.0).Batch(100).Iter() beforeMore = 0 case 4: // Without prefetch. iter = coll.Find(M{}).Prefetch(0).Batch(100).Iter() beforeMore = 100 } pings := 0 for batchi := 0; batchi < len(docs)/100-1; batchi++ { c.Logf("Iterating over %d documents on batch %d", beforeMore, batchi) var result struct{ N int } for i := 0; i < beforeMore; i++ { ok := iter.Next(&result) c.Assert(ok, Equals, true, Commentf("iter.Err: %v", iter.Err())) } beforeMore = 99 c.Logf("Done iterating.") session.Run("ping", nil) // Roundtrip to settle down. pings++ stats := mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, (batchi+1)*100+pings) c.Logf("Iterating over one more document on batch %d", batchi) ok := iter.Next(&result) c.Assert(ok, Equals, true, Commentf("iter.Err: %v", iter.Err())) c.Logf("Done iterating.") session.Run("ping", nil) // Roundtrip to settle down. pings++ stats = mgo.GetStats() c.Assert(stats.ReceivedDocs, Equals, (batchi+2)*100+pings) } } } func (s *S) TestSafeSetting(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() // Check the default safe := session.Safe() c.Assert(safe.W, Equals, 0) c.Assert(safe.WMode, Equals, "") c.Assert(safe.WTimeout, Equals, 0) c.Assert(safe.FSync, Equals, false) c.Assert(safe.J, Equals, false) // Tweak it session.SetSafe(&mgo.Safe{W: 1, WTimeout: 2, FSync: true}) safe = session.Safe() c.Assert(safe.W, Equals, 1) c.Assert(safe.WMode, Equals, "") c.Assert(safe.WTimeout, Equals, 2) c.Assert(safe.FSync, Equals, true) c.Assert(safe.J, Equals, false) // Reset it again. session.SetSafe(&mgo.Safe{}) safe = session.Safe() c.Assert(safe.W, Equals, 0) c.Assert(safe.WMode, Equals, "") c.Assert(safe.WTimeout, Equals, 0) c.Assert(safe.FSync, Equals, false) c.Assert(safe.J, Equals, false) // Ensure safety to something more conservative. session.SetSafe(&mgo.Safe{W: 5, WTimeout: 6, J: true}) safe = session.Safe() c.Assert(safe.W, Equals, 5) c.Assert(safe.WMode, Equals, "") c.Assert(safe.WTimeout, Equals, 6) c.Assert(safe.FSync, Equals, false) c.Assert(safe.J, Equals, true) // Ensure safety to something less conservative won't change it. session.EnsureSafe(&mgo.Safe{W: 4, WTimeout: 7}) safe = session.Safe() c.Assert(safe.W, Equals, 5) c.Assert(safe.WMode, Equals, "") c.Assert(safe.WTimeout, Equals, 6) c.Assert(safe.FSync, Equals, false) c.Assert(safe.J, Equals, true) // But to something more conservative will. session.EnsureSafe(&mgo.Safe{W: 6, WTimeout: 4, FSync: true}) safe = session.Safe() c.Assert(safe.W, Equals, 6) c.Assert(safe.WMode, Equals, "") c.Assert(safe.WTimeout, Equals, 4) c.Assert(safe.FSync, Equals, true) c.Assert(safe.J, Equals, false) // Even more conservative. session.EnsureSafe(&mgo.Safe{WMode: "majority", WTimeout: 2}) safe = session.Safe() c.Assert(safe.W, Equals, 0) c.Assert(safe.WMode, Equals, "majority") c.Assert(safe.WTimeout, Equals, 2) c.Assert(safe.FSync, Equals, true) c.Assert(safe.J, Equals, false) // WMode always overrides, whatever it is, but J doesn't. session.EnsureSafe(&mgo.Safe{WMode: "something", J: true}) safe = session.Safe() c.Assert(safe.W, Equals, 0) c.Assert(safe.WMode, Equals, "something") c.Assert(safe.WTimeout, Equals, 2) c.Assert(safe.FSync, Equals, true) c.Assert(safe.J, Equals, false) // EnsureSafe with nil does nothing. session.EnsureSafe(nil) safe = session.Safe() c.Assert(safe.W, Equals, 0) c.Assert(safe.WMode, Equals, "something") c.Assert(safe.WTimeout, Equals, 2) c.Assert(safe.FSync, Equals, true) c.Assert(safe.J, Equals, false) // Changing the safety of a cloned session doesn't touch the original. clone := session.Clone() defer clone.Close() clone.EnsureSafe(&mgo.Safe{WMode: "foo"}) safe = session.Safe() c.Assert(safe.WMode, Equals, "something") } func (s *S) TestSafeInsert(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") // Insert an element with a predefined key. err = coll.Insert(M{"_id": 1}) c.Assert(err, IsNil) mgo.ResetStats() // Session should be safe by default, so inserting it again must fail. err = coll.Insert(M{"_id": 1}) c.Assert(err, ErrorMatches, "E11000 duplicate.*") c.Assert(err.(*mgo.LastError).Code, Equals, 11000) // It must have sent two operations (INSERT_OP + getLastError QUERY_OP) stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 2) mgo.ResetStats() // If we disable safety, though, it won't complain. session.SetSafe(nil) err = coll.Insert(M{"_id": 1}) c.Assert(err, IsNil) // Must have sent a single operation this time (just the INSERT_OP) stats = mgo.GetStats() c.Assert(stats.SentOps, Equals, 1) } func (s *S) TestSafeParameters(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") // Tweak the safety parameters to something unachievable. session.SetSafe(&mgo.Safe{W: 4, WTimeout: 100}) err = coll.Insert(M{"_id": 1}) c.Assert(err, ErrorMatches, "timeout") c.Assert(err.(*mgo.LastError).WTimeout, Equals, true) } func (s *S) TestQueryErrorOne(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") result := struct { Err string "$err" }{} err = coll.Find(M{"a": 1}).Select(M{"a": M{"b": 1}}).One(&result) c.Assert(err, ErrorMatches, "Unsupported projection option: b") c.Assert(err.(*mgo.QueryError).Message, Matches, "Unsupported projection option: b") c.Assert(err.(*mgo.QueryError).Code, Equals, 13097) // The result should be properly unmarshalled with QueryError c.Assert(result.Err, Matches, "Unsupported projection option: b") } func (s *S) TestQueryErrorNext(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") result := struct { Err string "$err" }{} iter := coll.Find(M{"a": 1}).Select(M{"a": M{"b": 1}}).Iter() ok := iter.Next(&result) c.Assert(ok, Equals, false) err = iter.Close() c.Assert(err, ErrorMatches, "Unsupported projection option: b") c.Assert(err.(*mgo.QueryError).Message, Matches, "Unsupported projection option: b") c.Assert(err.(*mgo.QueryError).Code, Equals, 13097) c.Assert(iter.Err(), Equals, err) // The result should be properly unmarshalled with QueryError c.Assert(result.Err, Matches, "Unsupported projection option: b") } func (s *S) TestEnsureIndex(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() index1 := mgo.Index{ Key: []string{"a"}, Background: true, } index2 := mgo.Index{ Key: []string{"a", "-b"}, Unique: true, DropDups: true, } // Obsolete: index3 := mgo.Index{ Key: []string{"@loc_old"}, Min: -500, Max: 500, Bits: 32, } index4 := mgo.Index{ Key: []string{"$2d:loc"}, Min: -500, Max: 500, Bits: 32, } coll := session.DB("mydb").C("mycoll") for _, index := range []mgo.Index{index1, index2, index3, index4} { err = coll.EnsureIndex(index) c.Assert(err, IsNil) } sysidx := session.DB("mydb").C("system.indexes") result1 := M{} err = sysidx.Find(M{"name": "a_1"}).One(result1) c.Assert(err, IsNil) result2 := M{} err = sysidx.Find(M{"name": "a_1_b_-1"}).One(result2) c.Assert(err, IsNil) result3 := M{} err = sysidx.Find(M{"name": "loc_old_2d"}).One(result3) c.Assert(err, IsNil) result4 := M{} err = sysidx.Find(M{"name": "loc_2d"}).One(result4) c.Assert(err, IsNil) delete(result1, "v") expected1 := M{ "name": "a_1", "key": M{"a": 1}, "ns": "mydb.mycoll", "background": true, } c.Assert(result1, DeepEquals, expected1) delete(result2, "v") expected2 := M{ "name": "a_1_b_-1", "key": M{"a": 1, "b": -1}, "ns": "mydb.mycoll", "unique": true, "dropDups": true, } c.Assert(result2, DeepEquals, expected2) delete(result3, "v") expected3 := M{ "name": "loc_old_2d", "key": M{"loc_old": "2d"}, "ns": "mydb.mycoll", "min": -500, "max": 500, "bits": 32, } c.Assert(result3, DeepEquals, expected3) delete(result4, "v") expected4 := M{ "name": "loc_2d", "key": M{"loc": "2d"}, "ns": "mydb.mycoll", "min": -500, "max": 500, "bits": 32, } c.Assert(result4, DeepEquals, expected4) // Ensure the index actually works for real. err = coll.Insert(M{"a": 1, "b": 1}) c.Assert(err, IsNil) err = coll.Insert(M{"a": 1, "b": 1}) c.Assert(err, ErrorMatches, ".*duplicate key error.*") c.Assert(mgo.IsDup(err), Equals, true) } func (s *S) TestEnsureIndexWithBadInfo(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.EnsureIndex(mgo.Index{}) c.Assert(err, ErrorMatches, "Invalid index key:.*") err = coll.EnsureIndex(mgo.Index{Key: []string{""}}) c.Assert(err, ErrorMatches, "Invalid index key:.*") } func (s *S) TestEnsureIndexWithUnsafeSession(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() session.SetSafe(nil) coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) err = coll.Insert(M{"a": 1}) c.Assert(err, IsNil) // Should fail since there are duplicated entries. index := mgo.Index{ Key: []string{"a"}, Unique: true, } err = coll.EnsureIndex(index) c.Assert(err, ErrorMatches, ".*duplicate key error.*") } func (s *S) TestEnsureIndexKey(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.EnsureIndexKey("a") c.Assert(err, IsNil) err = coll.EnsureIndexKey("a", "-b") c.Assert(err, IsNil) sysidx := session.DB("mydb").C("system.indexes") result1 := M{} err = sysidx.Find(M{"name": "a_1"}).One(result1) c.Assert(err, IsNil) result2 := M{} err = sysidx.Find(M{"name": "a_1_b_-1"}).One(result2) c.Assert(err, IsNil) delete(result1, "v") expected1 := M{ "name": "a_1", "key": M{"a": 1}, "ns": "mydb.mycoll", } c.Assert(result1, DeepEquals, expected1) delete(result2, "v") expected2 := M{ "name": "a_1_b_-1", "key": M{"a": 1, "b": -1}, "ns": "mydb.mycoll", } c.Assert(result2, DeepEquals, expected2) } func (s *S) TestEnsureIndexDropIndex(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.EnsureIndexKey("a") c.Assert(err, IsNil) err = coll.EnsureIndexKey("-b") c.Assert(err, IsNil) err = coll.DropIndex("-b") c.Assert(err, IsNil) sysidx := session.DB("mydb").C("system.indexes") dummy := &struct{}{} err = sysidx.Find(M{"name": "a_1"}).One(dummy) c.Assert(err, IsNil) err = sysidx.Find(M{"name": "b_1"}).One(dummy) c.Assert(err, Equals, mgo.ErrNotFound) err = coll.DropIndex("a") c.Assert(err, IsNil) err = sysidx.Find(M{"name": "a_1"}).One(dummy) c.Assert(err, Equals, mgo.ErrNotFound) err = coll.DropIndex("a") c.Assert(err, ErrorMatches, "index not found") } func (s *S) TestEnsureIndexCaching(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.EnsureIndexKey("a") c.Assert(err, IsNil) mgo.ResetStats() // Second EnsureIndex should be cached and do nothing. err = coll.EnsureIndexKey("a") c.Assert(err, IsNil) stats := mgo.GetStats() c.Assert(stats.SentOps, Equals, 0) // Resetting the cache should make it contact the server again. session.ResetIndexCache() err = coll.EnsureIndexKey("a") c.Assert(err, IsNil) stats = mgo.GetStats() c.Assert(stats.SentOps, Equals, 2) // Dropping the index should also drop the cached index key. err = coll.DropIndex("a") c.Assert(err, IsNil) mgo.ResetStats() err = coll.EnsureIndexKey("a") c.Assert(err, IsNil) stats = mgo.GetStats() c.Assert(stats.SentOps, Equals, 2) } func (s *S) TestEnsureIndexGetIndexes(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.EnsureIndexKey("-b") c.Assert(err, IsNil) err = coll.EnsureIndexKey("a") c.Assert(err, IsNil) // Obsolete. err = coll.EnsureIndexKey("@c") c.Assert(err, IsNil) err = coll.EnsureIndexKey("$2d:d") c.Assert(err, IsNil) indexes, err := coll.Indexes() c.Assert(err, IsNil) c.Assert(indexes[0].Name, Equals, "_id_") c.Assert(indexes[1].Name, Equals, "a_1") c.Assert(indexes[1].Key, DeepEquals, []string{"a"}) c.Assert(indexes[2].Name, Equals, "b_-1") c.Assert(indexes[2].Key, DeepEquals, []string{"-b"}) c.Assert(indexes[3].Name, Equals, "c_2d") c.Assert(indexes[3].Key, DeepEquals, []string{"$2d:c"}) c.Assert(indexes[4].Name, Equals, "d_2d") c.Assert(indexes[4].Key, DeepEquals, []string{"$2d:d"}) } func (s *S) TestEnsureIndexEvalGetIndexes(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({b: -1})"}}, nil) c.Assert(err, IsNil) err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({a: 1})"}}, nil) c.Assert(err, IsNil) err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({c: '2d'})"}}, nil) c.Assert(err, IsNil) err = session.Run(bson.D{{"eval", "db.getSiblingDB('mydb').mycoll.ensureIndex({d: -1, e: 1})"}}, nil) c.Assert(err, IsNil) indexes, err := coll.Indexes() c.Assert(err, IsNil) c.Assert(indexes[0].Name, Equals, "_id_") c.Assert(indexes[1].Name, Equals, "a_1") c.Assert(indexes[1].Key, DeepEquals, []string{"a"}) c.Assert(indexes[2].Name, Equals, "b_-1") c.Assert(indexes[2].Key, DeepEquals, []string{"-b"}) c.Assert(indexes[3].Name, Equals, "c_2d") c.Assert(indexes[3].Key, DeepEquals, []string{"$2d:c"}) c.Assert(indexes[4].Name, Equals, "d_-1_e_1") c.Assert(indexes[4].Key, DeepEquals, []string{"-d", "e"}) } var testTTL = flag.Bool("test-ttl", false, "test TTL collections (may take 1 minute)") func (s *S) TestEnsureIndexExpireAfter(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() session.SetSafe(nil) coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1, "t": time.Now().Add(-120 * time.Second)}) c.Assert(err, IsNil) err = coll.Insert(M{"n": 2, "t": time.Now()}) c.Assert(err, IsNil) // Should fail since there are duplicated entries. index := mgo.Index{ Key: []string{"t"}, ExpireAfter: 1 * time.Minute, } err = coll.EnsureIndex(index) c.Assert(err, IsNil) indexes, err := coll.Indexes() c.Assert(err, IsNil) c.Assert(indexes[1].Name, Equals, "t_1") c.Assert(indexes[1].ExpireAfter, Equals, 1*time.Minute) if *testTTL { worked := false stop := time.Now().Add(70 * time.Second) for time.Now().Before(stop) { n, err := coll.Count() c.Assert(err, IsNil) if n == 1 { worked = true break } c.Assert(n, Equals, 2) c.Logf("Still has 2 entries...") time.Sleep(1 * time.Second) } if !worked { c.Fatalf("TTL index didn't work") } } } func (s *S) TestDistinct(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for _, i := range []int{1, 4, 6, 2, 2, 3, 4} { coll.Insert(M{"n": i}) } var result []int err = coll.Find(M{"n": M{"$gt": 2}}).Sort("n").Distinct("n", &result) sort.IntSlice(result).Sort() c.Assert(result, DeepEquals, []int{3, 4, 6}) } func (s *S) TestMapReduce(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for _, i := range []int{1, 4, 6, 2, 2, 3, 4} { coll.Insert(M{"n": i}) } job := &mgo.MapReduce{ Map: "function() { emit(this.n, 1); }", Reduce: "function(key, values) { return Array.sum(values); }", } var result []struct { Id int "_id" Value int } info, err := coll.Find(M{"n": M{"$gt": 2}}).MapReduce(job, &result) c.Assert(err, IsNil) c.Assert(info.InputCount, Equals, 4) c.Assert(info.EmitCount, Equals, 4) c.Assert(info.OutputCount, Equals, 3) c.Assert(info.Time > 1e6, Equals, true) c.Assert(info.Time < 1e9, Equals, true) c.Assert(info.VerboseTime, IsNil) expected := map[int]int{3: 1, 4: 2, 6: 1} for _, item := range result { c.Logf("Item: %#v", &item) c.Assert(item.Value, Equals, expected[item.Id]) expected[item.Id] = -1 } // Weak attempt of testing that Sort gets delivered. _, err = coll.Find(nil).Sort("-n").MapReduce(job, &result) _, isQueryError := err.(*mgo.QueryError) c.Assert(isQueryError, Equals, true) } func (s *S) TestMapReduceFinalize(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for _, i := range []int{1, 4, 6, 2, 2, 3, 4} { coll.Insert(M{"n": i}) } job := &mgo.MapReduce{ Map: "function() { emit(this.n, 1) }", Reduce: "function(key, values) { return Array.sum(values) }", Finalize: "function(key, count) { return {count: count} }", } var result []struct { Id int "_id" Value struct{ Count int } } _, err = coll.Find(nil).MapReduce(job, &result) c.Assert(err, IsNil) expected := map[int]int{1: 1, 2: 2, 3: 1, 4: 2, 6: 1} for _, item := range result { c.Logf("Item: %#v", &item) c.Assert(item.Value.Count, Equals, expected[item.Id]) expected[item.Id] = -1 } } func (s *S) TestMapReduceToCollection(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for _, i := range []int{1, 4, 6, 2, 2, 3, 4} { coll.Insert(M{"n": i}) } job := &mgo.MapReduce{ Map: "function() { emit(this.n, 1); }", Reduce: "function(key, values) { return Array.sum(values); }", Out: "mr", } info, err := coll.Find(nil).MapReduce(job, nil) c.Assert(err, IsNil) c.Assert(info.InputCount, Equals, 7) c.Assert(info.EmitCount, Equals, 7) c.Assert(info.OutputCount, Equals, 5) c.Assert(info.Time > 1e6, Equals, true) c.Assert(info.Time < 1e9, Equals, true) c.Assert(info.Collection, Equals, "mr") c.Assert(info.Database, Equals, "mydb") expected := map[int]int{1: 1, 2: 2, 3: 1, 4: 2, 6: 1} var item *struct { Id int "_id" Value int } mr := session.DB("mydb").C("mr") iter := mr.Find(nil).Iter() for iter.Next(&item) { c.Logf("Item: %#v", &item) c.Assert(item.Value, Equals, expected[item.Id]) expected[item.Id] = -1 } c.Assert(iter.Close(), IsNil) } func (s *S) TestMapReduceToOtherDb(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for _, i := range []int{1, 4, 6, 2, 2, 3, 4} { coll.Insert(M{"n": i}) } job := &mgo.MapReduce{ Map: "function() { emit(this.n, 1); }", Reduce: "function(key, values) { return Array.sum(values); }", Out: bson.D{{"replace", "mr"}, {"db", "otherdb"}}, } info, err := coll.Find(nil).MapReduce(job, nil) c.Assert(err, IsNil) c.Assert(info.InputCount, Equals, 7) c.Assert(info.EmitCount, Equals, 7) c.Assert(info.OutputCount, Equals, 5) c.Assert(info.Time > 1e6, Equals, true) c.Assert(info.Time < 2e9, Equals, true) c.Assert(info.Collection, Equals, "mr") c.Assert(info.Database, Equals, "otherdb") expected := map[int]int{1: 1, 2: 2, 3: 1, 4: 2, 6: 1} var item *struct { Id int "_id" Value int } mr := session.DB("otherdb").C("mr") iter := mr.Find(nil).Iter() for iter.Next(&item) { c.Logf("Item: %#v", &item) c.Assert(item.Value, Equals, expected[item.Id]) expected[item.Id] = -1 } c.Assert(iter.Close(), IsNil) } func (s *S) TestMapReduceOutOfOrder(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for _, i := range []int{1, 4, 6, 2, 2, 3, 4} { coll.Insert(M{"n": i}) } job := &mgo.MapReduce{ Map: "function() { emit(this.n, 1); }", Reduce: "function(key, values) { return Array.sum(values); }", Out: bson.M{"a": "a", "z": "z", "replace": "mr", "db": "otherdb", "b": "b", "y": "y"}, } info, err := coll.Find(nil).MapReduce(job, nil) c.Assert(err, IsNil) c.Assert(info.Collection, Equals, "mr") c.Assert(info.Database, Equals, "otherdb") } func (s *S) TestMapReduceScope(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"n": 1}) job := &mgo.MapReduce{ Map: "function() { emit(this.n, x); }", Reduce: "function(key, values) { return Array.sum(values); }", Scope: M{"x": 42}, } var result []bson.M _, err = coll.Find(nil).MapReduce(job, &result) c.Assert(len(result), Equals, 1) c.Assert(result[0]["value"], Equals, 42.0) } func (s *S) TestMapReduceVerbose(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"n": 1}) job := &mgo.MapReduce{ Map: "function() { emit(this.n, 1); }", Reduce: "function(key, values) { return Array.sum(values); }", Verbose: true, } info, err := coll.Find(nil).MapReduce(job, nil) c.Assert(err, IsNil) c.Assert(info.VerboseTime, NotNil) c.Assert(info.VerboseTime.Total > 1e6, Equals, true) c.Assert(info.VerboseTime.Total < 1e9, Equals, true) } func (s *S) TestMapReduceLimit(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for _, i := range []int{1, 4, 6, 2, 2, 3, 4} { coll.Insert(M{"n": i}) } job := &mgo.MapReduce{ Map: "function() { emit(this.n, 1); }", Reduce: "function(key, values) { return Array.sum(values); }", } var result []bson.M _, err = coll.Find(nil).Limit(3).MapReduce(job, &result) c.Assert(err, IsNil) c.Assert(len(result), Equals, 3) } func (s *S) TestBuildInfo(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() info, err := session.BuildInfo() c.Assert(err, IsNil) var v []int for i, a := range strings.Split(info.Version, ".") { for _, token := range []string{"-rc", "-pre"} { if i == 2 && strings.Contains(a, token) { a = a[:strings.Index(a, token)] info.VersionArray[len(info.VersionArray)-1] = 0 } } n, err := strconv.Atoi(a) c.Assert(err, IsNil) v = append(v, n) } for len(v) < 4 { v = append(v, 0) } c.Assert(info.VersionArray, DeepEquals, v) c.Assert(info.GitVersion, Matches, "[a-z0-9]+") c.Assert(info.SysInfo, Matches, ".*[0-9:]+.*") if info.Bits != 32 && info.Bits != 64 { c.Fatalf("info.Bits is %d", info.Bits) } if info.MaxObjectSize < 8192 { c.Fatalf("info.MaxObjectSize seems too small: %d", info.MaxObjectSize) } } func (s *S) TestZeroTimeRoundtrip(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() var d struct{ T time.Time } conn := session.DB("mydb").C("mycoll") err = conn.Insert(d) c.Assert(err, IsNil) var result bson.M err = conn.Find(nil).One(&result) c.Assert(err, IsNil) t, isTime := result["t"].(time.Time) c.Assert(isTime, Equals, true) c.Assert(t, Equals, time.Time{}) } func (s *S) TestFsyncLock(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() clone := session.Clone() defer clone.Close() err = session.FsyncLock() c.Assert(err, IsNil) done := make(chan time.Time) go func() { time.Sleep(3e9) now := time.Now() err := session.FsyncUnlock() c.Check(err, IsNil) done <- now }() err = clone.DB("mydb").C("mycoll").Insert(bson.M{"n": 1}) unlocked := time.Now() unlocking := <-done c.Assert(err, IsNil) c.Assert(unlocked.After(unlocking), Equals, true) c.Assert(unlocked.Sub(unlocking) < 1e9, Equals, true) } func (s *S) TestFsync(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() // Not much to do here. Just a smoke check. err = session.Fsync(false) c.Assert(err, IsNil) err = session.Fsync(true) c.Assert(err, IsNil) } func (s *S) TestPipeIter(c *C) { if !s.versionAtLeast(2, 1) { c.Skip("Pipe only works on 2.1+") } session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } iter := coll.Pipe([]M{{"$match": M{"n": M{"$gte": 42}}}}).Iter() result := struct{ N int }{} for i := 2; i < 7; i++ { ok := iter.Next(&result) c.Assert(ok, Equals, true) c.Assert(result.N, Equals, ns[i]) } c.Assert(iter.Next(&result), Equals, false) c.Assert(iter.Close(), IsNil) } func (s *S) TestPipeAll(c *C) { if !s.versionAtLeast(2, 1) { c.Skip("Pipe only works on 2.1+") } session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { err := coll.Insert(M{"n": n}) c.Assert(err, IsNil) } var result []struct{ N int } err = coll.Pipe([]M{{"$match": M{"n": M{"$gte": 42}}}}).All(&result) c.Assert(err, IsNil) for i := 2; i < 7; i++ { c.Assert(result[i-2].N, Equals, ns[i]) } } func (s *S) TestPipeOne(c *C) { if !s.versionAtLeast(2, 1) { c.Skip("Pipe only works on 2.1+") } session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") coll.Insert(M{"a": 1, "b": 2}) result := struct{ A, B int }{} pipe := coll.Pipe([]M{{"$project": M{"a": 1, "b": M{"$add": []interface{}{"$b", 1}}}}}) err = pipe.One(&result) c.Assert(err, IsNil) c.Assert(result.A, Equals, 1) c.Assert(result.B, Equals, 3) pipe = coll.Pipe([]M{{"$match": M{"a": 2}}}) err = pipe.One(&result) c.Assert(err, Equals, mgo.ErrNotFound) } func (s *S) TestBatch1Bug(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for i := 0; i < 3; i++ { err := coll.Insert(M{"n": i}) c.Assert(err, IsNil) } var ns []struct{ N int } err = coll.Find(nil).Batch(1).All(&ns) c.Assert(err, IsNil) c.Assert(len(ns), Equals, 3) session.SetBatch(1) err = coll.Find(nil).All(&ns) c.Assert(err, IsNil) c.Assert(len(ns), Equals, 3) } func (s *S) TestInterfaceIterBug(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for i := 0; i < 3; i++ { err := coll.Insert(M{"n": i}) c.Assert(err, IsNil) } var result interface{} i := 0 iter := coll.Find(nil).Sort("n").Iter() for iter.Next(&result) { c.Assert(result.(bson.M)["n"], Equals, i) i++ } c.Assert(iter.Close(), IsNil) } func (s *S) TestFindIterCloseKillsCursor(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() cursors := serverCursorsOpen(session) coll := session.DB("mydb").C("mycoll") ns := []int{40, 41, 42, 43, 44, 45, 46} for _, n := range ns { coll.Insert(M{"n": n}) } iter := coll.Find(nil).Batch(2).Iter() c.Assert(iter.Next(bson.M{}), Equals, true) c.Assert(iter.Close(), IsNil) c.Assert(serverCursorsOpen(session), Equals, cursors) } func (s *S) TestLogReplay(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") for i := 0; i < 5; i++ { coll.Insert(M{"ts": time.Now()}) } iter := coll.Find(nil).LogReplay().Iter() c.Assert(iter.Next(bson.M{}), Equals, false) c.Assert(iter.Err(), ErrorMatches, "no ts field in query") } juju-core_1.18.1/src/labix.org/v2/mgo/Makefile0000644000015300001610000000010112321735660020664 0ustar jenkinsjenkinsstartdb: @testdb/setup.sh start stopdb: @testdb/setup.sh stop juju-core_1.18.1/src/labix.org/v2/mgo/auth_test.go0000644000015300001610000005071512321736015021566 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo_test import ( "fmt" "labix.org/v2/mgo" . "launchpad.net/gocheck" "sync" "time" ) func (s *S) TestAuthLogin(c *C) { // Test both with a normal database and with an authenticated shard. for _, addr := range []string{"localhost:40002", "localhost:40203"} { session, err := mgo.Dial(addr) c.Assert(err, IsNil) defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*") admindb := session.DB("admin") err = admindb.Login("root", "wrong") c.Assert(err, ErrorMatches, "auth fails") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) } } func (s *S) TestAuthLoginLogout(c *C) { // Test both with a normal database and with an authenticated shard. for _, addr := range []string{"localhost:40002", "localhost:40203"} { session, err := mgo.Dial(addr) c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) admindb.Logout() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*") // Must have dropped auth from the session too. session = session.Copy() defer session.Close() coll = session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*") } } func (s *S) TestAuthLoginLogoutAll(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) session.LogoutAll() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*") // Must have dropped auth from the session too. session = session.Copy() defer session.Close() coll = session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized .*") } func (s *S) TestAuthUpsertUserErrors(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) mydb := session.DB("mydb") err = mydb.UpsertUser(&mgo.User{}) c.Assert(err, ErrorMatches, "user has no Username") err = mydb.UpsertUser(&mgo.User{Username: "user", Password: "pass", UserSource: "source"}) c.Assert(err, ErrorMatches, "user has both Password/PasswordHash and UserSource set") err = mydb.UpsertUser(&mgo.User{Username: "user", Password: "pass", OtherDBRoles: map[string][]mgo.Role{"db": nil}}) c.Assert(err, ErrorMatches, "user with OtherDBRoles is only supported in admin database") } func (s *S) TestAuthUpsertUser(c *C) { if !s.versionAtLeast(2, 4) { c.Skip("UpsertUser only works on 2.4+") } session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) mydb := session.DB("mydb") myotherdb := session.DB("myotherdb") ruser := &mgo.User{ Username: "myruser", Password: "mypass", Roles: []mgo.Role{mgo.RoleRead}, } rwuser := &mgo.User{ Username: "myrwuser", Password: "mypass", Roles: []mgo.Role{mgo.RoleReadWrite}, } rwuserother := &mgo.User{ Username: "myrwuser", UserSource: "mydb", Roles: []mgo.Role{mgo.RoleRead}, } err = mydb.UpsertUser(ruser) c.Assert(err, IsNil) err = mydb.UpsertUser(rwuser) c.Assert(err, IsNil) err = myotherdb.UpsertUser(rwuserother) c.Assert(err, IsNil) err = mydb.Login("myruser", "mypass") c.Assert(err, IsNil) admindb.Logout() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|not authorized .*") err = mydb.Login("myrwuser", "mypass") c.Assert(err, IsNil) err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) // Test indirection via UserSource: we can't write to it, because // the roles for myrwuser are different there. othercoll := myotherdb.C("myothercoll") err = othercoll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|not authorized .*") // Reading works, though. err = othercoll.Find(nil).One(nil) c.Assert(err, Equals, mgo.ErrNotFound) // Can't login directly into the database using UserSource, though. err = myotherdb.Login("myrwuser", "mypass") c.Assert(err, ErrorMatches, "auth fails") } func (s *S) TestAuthUpserUserOtherDBRoles(c *C) { if !s.versionAtLeast(2, 4) { c.Skip("UpsertUser only works on 2.4+") } session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) ruser := &mgo.User{ Username: "myruser", Password: "mypass", OtherDBRoles: map[string][]mgo.Role{"mydb": []mgo.Role{mgo.RoleRead}}, } err = admindb.UpsertUser(ruser) c.Assert(err, IsNil) defer admindb.RemoveUser("myruser") admindb.Logout() err = admindb.Login("myruser", "mypass") coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|not authorized .*") err = coll.Find(nil).One(nil) c.Assert(err, Equals, mgo.ErrNotFound) } func (s *S) TestAuthUpserUserUnsetFields(c *C) { if !s.versionAtLeast(2, 4) { c.Skip("UpsertUser only works on 2.4+") } session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) // Insert a user with most fields set. user := &mgo.User{ Username: "myruser", Password: "mypass", Roles: []mgo.Role{mgo.RoleRead}, OtherDBRoles: map[string][]mgo.Role{"mydb": []mgo.Role{mgo.RoleRead}}, } err = admindb.UpsertUser(user) c.Assert(err, IsNil) defer admindb.RemoveUser("myruser") // Now update the user with few things set. user = &mgo.User{ Username: "myruser", UserSource: "mydb", } err = admindb.UpsertUser(user) c.Assert(err, IsNil) // Everything that was unset must have been dropped. var userm M err = admindb.C("system.users").Find(M{"user": "myruser"}).One(&userm) c.Assert(err, IsNil) delete(userm, "_id") c.Assert(userm, DeepEquals, M{"user": "myruser", "userSource": "mydb", "roles": []interface{}{}}) // Now set password again... user = &mgo.User{ Username: "myruser", Password: "mypass", } err = admindb.UpsertUser(user) c.Assert(err, IsNil) // ... and assert that userSource has been dropped. err = admindb.C("system.users").Find(M{"user": "myruser"}).One(&userm) _, found := userm["userSource"] c.Assert(found, Equals, false) } func (s *S) TestAuthAddUser(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) mydb := session.DB("mydb") err = mydb.AddUser("myruser", "mypass", true) c.Assert(err, IsNil) err = mydb.AddUser("mywuser", "mypass", false) c.Assert(err, IsNil) err = mydb.Login("myruser", "mypass") c.Assert(err, IsNil) admindb.Logout() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|not authorized .*") err = mydb.Login("mywuser", "mypass") c.Assert(err, IsNil) err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) } func (s *S) TestAuthAddUserReplaces(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) mydb := session.DB("mydb") err = mydb.AddUser("myuser", "myoldpass", false) c.Assert(err, IsNil) err = mydb.AddUser("myuser", "mynewpass", true) c.Assert(err, IsNil) admindb.Logout() err = mydb.Login("myuser", "myoldpass") c.Assert(err, ErrorMatches, "auth fails") err = mydb.Login("myuser", "mynewpass") c.Assert(err, IsNil) // ReadOnly flag was changed too. err = mydb.C("mycoll").Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|not authorized .*") } func (s *S) TestAuthRemoveUser(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) mydb := session.DB("mydb") err = mydb.AddUser("myuser", "mypass", true) c.Assert(err, IsNil) err = mydb.RemoveUser("myuser") c.Assert(err, IsNil) err = mydb.Login("myuser", "mypass") c.Assert(err, ErrorMatches, "auth fails") } func (s *S) TestAuthLoginTwiceDoesNothing(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) oldStats := mgo.GetStats() err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) newStats := mgo.GetStats() c.Assert(newStats.SentOps, Equals, oldStats.SentOps) } func (s *S) TestAuthLoginLogoutLoginDoesNothing(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) oldStats := mgo.GetStats() admindb.Logout() err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) newStats := mgo.GetStats() c.Assert(newStats.SentOps, Equals, oldStats.SentOps) } func (s *S) TestAuthLoginSwitchUser(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) err = admindb.Login("reader", "rapadura") c.Assert(err, IsNil) // Can't write. err = coll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|not authorized .*") // But can read. result := struct{ N int }{} err = coll.Find(nil).One(&result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 1) } func (s *S) TestAuthLoginChangePassword(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) mydb := session.DB("mydb") err = mydb.AddUser("myuser", "myoldpass", false) c.Assert(err, IsNil) err = mydb.Login("myuser", "myoldpass") c.Assert(err, IsNil) err = mydb.AddUser("myuser", "mynewpass", true) c.Assert(err, IsNil) err = mydb.Login("myuser", "mynewpass") c.Assert(err, IsNil) admindb.Logout() // The second login must be in effect, which means read-only. err = mydb.C("mycoll").Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|not authorized .*") } func (s *S) TestAuthLoginCachingWithSessionRefresh(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) session.Refresh() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) } func (s *S) TestAuthLoginCachingWithSessionCopy(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) session = session.Copy() defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) } func (s *S) TestAuthLoginCachingWithSessionClone(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) session = session.Clone() defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, IsNil) } func (s *S) TestAuthLoginCachingWithNewSession(c *C) { session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) session = session.New() defer session.Close() coll := session.DB("mydb").C("mycoll") err = coll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|need to login|not authorized for .*") } func (s *S) TestAuthLoginCachingAcrossPool(c *C) { // Logins are cached even when the conenction goes back // into the pool. session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) // Add another user to test the logout case at the same time. mydb := session.DB("mydb") err = mydb.AddUser("myuser", "mypass", false) c.Assert(err, IsNil) err = mydb.Login("myuser", "mypass") c.Assert(err, IsNil) // Logout root explicitly, to test both cases. admindb.Logout() // Give socket back to pool. session.Refresh() // Brand new session, should use socket from the pool. other := session.New() defer other.Close() oldStats := mgo.GetStats() err = other.DB("admin").Login("root", "rapadura") c.Assert(err, IsNil) err = other.DB("mydb").Login("myuser", "mypass") c.Assert(err, IsNil) // Both logins were cached, so no ops. newStats := mgo.GetStats() c.Assert(newStats.SentOps, Equals, oldStats.SentOps) // And they actually worked. err = other.DB("mydb").C("mycoll").Insert(M{"n": 1}) c.Assert(err, IsNil) other.DB("admin").Logout() err = other.DB("mydb").C("mycoll").Insert(M{"n": 1}) c.Assert(err, IsNil) } func (s *S) TestAuthLoginCachingAcrossPoolWithLogout(c *C) { // Now verify that logouts are properly flushed if they // are not revalidated after leaving the pool. session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) // Add another user to test the logout case at the same time. mydb := session.DB("mydb") err = mydb.AddUser("myuser", "mypass", true) c.Assert(err, IsNil) err = mydb.Login("myuser", "mypass") c.Assert(err, IsNil) // Just some data to query later. err = session.DB("mydb").C("mycoll").Insert(M{"n": 1}) c.Assert(err, IsNil) // Give socket back to pool. session.Refresh() // Brand new session, should use socket from the pool. other := session.New() defer other.Close() oldStats := mgo.GetStats() err = other.DB("mydb").Login("myuser", "mypass") c.Assert(err, IsNil) // Login was cached, so no ops. newStats := mgo.GetStats() c.Assert(newStats.SentOps, Equals, oldStats.SentOps) // Can't write, since root has been implicitly logged out // when the collection went into the pool, and not revalidated. err = other.DB("mydb").C("mycoll").Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|not authorized .*") // But can read due to the revalidated myuser login. result := struct{ N int }{} err = other.DB("mydb").C("mycoll").Find(nil).One(&result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 1) } func (s *S) TestAuthEventual(c *C) { // Eventual sessions don't keep sockets around, so they are // an interesting test case. session, err := mgo.Dial("localhost:40002") c.Assert(err, IsNil) defer session.Close() admindb := session.DB("admin") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) err = session.DB("mydb").C("mycoll").Insert(M{"n": 1}) c.Assert(err, IsNil) var wg sync.WaitGroup wg.Add(20) for i := 0; i != 10; i++ { go func() { defer wg.Done() var result struct{ N int } err := session.DB("mydb").C("mycoll").Find(nil).One(&result) c.Assert(err, IsNil) c.Assert(result.N, Equals, 1) }() } for i := 0; i != 10; i++ { go func() { defer wg.Done() err := session.DB("mydb").C("mycoll").Insert(M{"n": 1}) c.Assert(err, IsNil) }() } wg.Wait() } func (s *S) TestAuthURL(c *C) { session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002/") c.Assert(err, IsNil) defer session.Close() err = session.DB("mydb").C("mycoll").Insert(M{"n": 1}) c.Assert(err, IsNil) } func (s *S) TestAuthURLWrongCredentials(c *C) { session, err := mgo.Dial("mongodb://root:wrong@localhost:40002/") if session != nil { session.Close() } c.Assert(err, ErrorMatches, "auth fails") c.Assert(session, IsNil) } func (s *S) TestAuthURLWithNewSession(c *C) { // When authentication is in the URL, the new session will // actually carry it on as well, even if logged out explicitly. session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002/") c.Assert(err, IsNil) defer session.Close() session.DB("admin").Logout() // Do it twice to ensure it passes the needed data on. session = session.New() defer session.Close() session = session.New() defer session.Close() err = session.DB("mydb").C("mycoll").Insert(M{"n": 1}) c.Assert(err, IsNil) } func (s *S) TestAuthURLWithDatabase(c *C) { session, err := mgo.Dial("mongodb://root:rapadura@localhost:40002") c.Assert(err, IsNil) defer session.Close() mydb := session.DB("mydb") err = mydb.AddUser("myruser", "mypass", true) c.Assert(err, IsNil) usession, err := mgo.Dial("mongodb://myruser:mypass@localhost:40002/mydb") c.Assert(err, IsNil) defer usession.Close() ucoll := usession.DB("mydb").C("mycoll") err = ucoll.FindId(0).One(nil) c.Assert(err, Equals, mgo.ErrNotFound) err = ucoll.Insert(M{"n": 1}) c.Assert(err, ErrorMatches, "unauthorized|not authorized .*") } func (s *S) TestDefaultDatabase(c *C) { tests := []struct{ url, db string }{ {"mongodb://root:rapadura@localhost:40002", "test"}, {"mongodb://root:rapadura@localhost:40002/admin", "admin"}, {"mongodb://localhost:40001", "test"}, {"mongodb://localhost:40001/", "test"}, {"mongodb://localhost:40001/mydb", "mydb"}, } for _, test := range tests { session, err := mgo.Dial(test.url) c.Assert(err, IsNil) defer session.Close() c.Logf("test: %#v", test) c.Assert(session.DB("").Name, Equals, test.db) scopy := session.Copy() c.Check(scopy.DB("").Name, Equals, test.db) scopy.Close() } } func (s *S) TestAuthDirect(c *C) { // Direct connections must work to the master and slaves. for _, port := range []string{"40031", "40032", "40033"} { url := fmt.Sprintf("mongodb://root:rapadura@localhost:%s/?connect=direct", port) session, err := mgo.Dial(url) c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Monotonic, true) var result struct{} err = session.DB("mydb").C("mycoll").Find(nil).One(&result) c.Assert(err, Equals, mgo.ErrNotFound) } } func (s *S) TestAuthDirectWithLogin(c *C) { // Direct connections must work to the master and slaves. for _, port := range []string{"40031", "40032", "40033"} { url := fmt.Sprintf("mongodb://localhost:%s/?connect=direct", port) session, err := mgo.Dial(url) c.Assert(err, IsNil) defer session.Close() session.SetMode(mgo.Monotonic, true) session.SetSyncTimeout(3 * time.Second) err = session.DB("admin").Login("root", "rapadura") c.Assert(err, IsNil) var result struct{} err = session.DB("mydb").C("mycoll").Find(nil).One(&result) c.Assert(err, Equals, mgo.ErrNotFound) } } juju-core_1.18.1/src/labix.org/v2/mgo/cluster.go0000644000015300001610000004022612321736015021243 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo import ( "errors" "labix.org/v2/mgo/bson" "net" "sync" "time" ) // --------------------------------------------------------------------------- // Mongo cluster encapsulation. // // A cluster enables the communication with one or more servers participating // in a mongo cluster. This works with individual servers, a replica set, // a replica pair, one or multiple mongos routers, etc. type mongoCluster struct { sync.RWMutex serverSynced sync.Cond userSeeds []string dynaSeeds []string servers mongoServers masters mongoServers references int syncing bool direct bool cachedIndex map[string]bool sync chan bool dial dialer } func newCluster(userSeeds []string, direct bool, dial dialer) *mongoCluster { cluster := &mongoCluster{ userSeeds: userSeeds, references: 1, direct: direct, dial: dial, } cluster.serverSynced.L = cluster.RWMutex.RLocker() cluster.sync = make(chan bool, 1) stats.cluster(+1) go cluster.syncServersLoop() return cluster } // Acquire increases the reference count for the cluster. func (cluster *mongoCluster) Acquire() { cluster.Lock() cluster.references++ debugf("Cluster %p acquired (refs=%d)", cluster, cluster.references) cluster.Unlock() } // Release decreases the reference count for the cluster. Once // it reaches zero, all servers will be closed. func (cluster *mongoCluster) Release() { cluster.Lock() if cluster.references == 0 { panic("cluster.Release() with references == 0") } cluster.references-- debugf("Cluster %p released (refs=%d)", cluster, cluster.references) if cluster.references == 0 { for _, server := range cluster.servers.Slice() { server.Close() } // Wake up the sync loop so it can die. cluster.syncServers() stats.cluster(-1) } cluster.Unlock() } func (cluster *mongoCluster) LiveServers() (servers []string) { cluster.RLock() for _, serv := range cluster.servers.Slice() { servers = append(servers, serv.Addr) } cluster.RUnlock() return servers } func (cluster *mongoCluster) removeServer(server *mongoServer) { cluster.Lock() cluster.masters.Remove(server) other := cluster.servers.Remove(server) cluster.Unlock() if other != nil { other.Close() log("Removed server ", server.Addr, " from cluster.") } server.Close() } type isMasterResult struct { IsMaster bool Secondary bool Primary string Hosts []string Passives []string Tags bson.D Msg string } func (cluster *mongoCluster) isMaster(socket *mongoSocket, result *isMasterResult) error { // Monotonic let's it talk to a slave and still hold the socket. session := newSession(Monotonic, cluster, 10*time.Second) session.setSocket(socket) err := session.Run("ismaster", result) session.Close() return err } type possibleTimeout interface { Timeout() bool } var syncSocketTimeout = 5 * time.Second func (cluster *mongoCluster) syncServer(server *mongoServer) (info *mongoServerInfo, hosts []string, err error) { addr := server.Addr log("SYNC Processing ", addr, "...") // Retry a few times to avoid knocking a server down for a hiccup. var result isMasterResult var tryerr error for retry := 0; ; retry++ { if retry == 3 { return nil, nil, tryerr } if retry > 0 { // Don't abuse the server needlessly if there's something actually wrong. if err, ok := tryerr.(possibleTimeout); ok && err.Timeout() { // Give a chance for waiters to timeout as well. cluster.serverSynced.Broadcast() } time.Sleep(500 * time.Millisecond) } // It's not clear what would be a good timeout here. Is it // better to wait longer or to retry? socket, _, err := server.AcquireSocket(0, syncSocketTimeout) if err != nil { tryerr = err logf("SYNC Failed to get socket to %s: %v", addr, err) continue } err = cluster.isMaster(socket, &result) socket.Release() if err != nil { tryerr = err logf("SYNC Command 'ismaster' to %s failed: %v", addr, err) continue } debugf("SYNC Result of 'ismaster' from %s: %#v", addr, result) break } if result.IsMaster { debugf("SYNC %s is a master.", addr) // Made an incorrect assumption above, so fix stats. stats.conn(-1, false) stats.conn(+1, true) } else if result.Secondary { debugf("SYNC %s is a slave.", addr) } else if cluster.direct { logf("SYNC %s in unknown state. Pretending it's a slave due to direct connection.", addr) } else { logf("SYNC %s is neither a master nor a slave.", addr) // Made an incorrect assumption above, so fix stats. stats.conn(-1, false) return nil, nil, errors.New(addr + " is not a master nor slave") } info = &mongoServerInfo{ Master: result.IsMaster, Mongos: result.Msg == "isdbgrid", Tags: result.Tags, } hosts = make([]string, 0, 1+len(result.Hosts)+len(result.Passives)) if result.Primary != "" { // First in the list to speed up master discovery. hosts = append(hosts, result.Primary) } hosts = append(hosts, result.Hosts...) hosts = append(hosts, result.Passives...) debugf("SYNC %s knows about the following peers: %#v", addr, hosts) return info, hosts, nil } type syncKind bool const ( completeSync syncKind = true partialSync syncKind = false ) func (cluster *mongoCluster) addServer(server *mongoServer, info *mongoServerInfo, syncKind syncKind) { cluster.Lock() current := cluster.servers.Search(server.ResolvedAddr) if current == nil { if syncKind == partialSync { cluster.Unlock() server.Close() log("SYNC Discarding unknown server ", server.Addr, " due to partial sync.") return } cluster.servers.Add(server) if info.Master { cluster.masters.Add(server) log("SYNC Adding ", server.Addr, " to cluster as a master.") } else { log("SYNC Adding ", server.Addr, " to cluster as a slave.") } } else { if server != current { panic("addServer attempting to add duplicated server") } if server.Info().Master != info.Master { if info.Master { log("SYNC Server ", server.Addr, " is now a master.") cluster.masters.Add(server) } else { log("SYNC Server ", server.Addr, " is now a slave.") cluster.masters.Remove(server) } } } server.SetInfo(info) debugf("SYNC Broadcasting availability of server %s", server.Addr) cluster.serverSynced.Broadcast() cluster.Unlock() } func (cluster *mongoCluster) getKnownAddrs() []string { cluster.RLock() max := len(cluster.userSeeds) + len(cluster.dynaSeeds) + cluster.servers.Len() seen := make(map[string]bool, max) known := make([]string, 0, max) add := func(addr string) { if _, found := seen[addr]; !found { seen[addr] = true known = append(known, addr) } } for _, addr := range cluster.userSeeds { add(addr) } for _, addr := range cluster.dynaSeeds { add(addr) } for _, serv := range cluster.servers.Slice() { add(serv.Addr) } cluster.RUnlock() return known } // syncServers injects a value into the cluster.sync channel to force // an iteration of the syncServersLoop function. func (cluster *mongoCluster) syncServers() { select { case cluster.sync <- true: default: } } // How long to wait for a checkup of the cluster topology if nothing // else kicks a synchronization before that. const syncServersDelay = 30 * time.Second // syncServersLoop loops while the cluster is alive to keep its idea of // the server topology up-to-date. It must be called just once from // newCluster. The loop iterates once syncServersDelay has passed, or // if somebody injects a value into the cluster.sync channel to force a // synchronization. A loop iteration will contact all servers in // parallel, ask them about known peers and their own role within the // cluster, and then attempt to do the same with all the peers // retrieved. func (cluster *mongoCluster) syncServersLoop() { for { debugf("SYNC Cluster %p is starting a sync loop iteration.", cluster) cluster.Lock() if cluster.references == 0 { cluster.Unlock() break } cluster.references++ // Keep alive while syncing. direct := cluster.direct cluster.Unlock() cluster.syncServersIteration(direct) // We just synchronized, so consume any outstanding requests. select { case <-cluster.sync: default: } cluster.Release() // Hold off before allowing another sync. No point in // burning CPU looking for down servers. time.Sleep(500 * time.Millisecond) cluster.Lock() if cluster.references == 0 { cluster.Unlock() break } // Poke all waiters so they have a chance to timeout or // restart syncing if they wish to. cluster.serverSynced.Broadcast() // Check if we have to restart immediately either way. restart := !direct && cluster.masters.Empty() || cluster.servers.Empty() cluster.Unlock() if restart { log("SYNC No masters found. Will synchronize again.") continue } debugf("SYNC Cluster %p waiting for next requested or scheduled sync.", cluster) // Hold off until somebody explicitly requests a synchronization // or it's time to check for a cluster topology change again. select { case <-cluster.sync: case <-time.After(syncServersDelay): } } debugf("SYNC Cluster %p is stopping its sync loop.", cluster) } func (cluster *mongoCluster) server(addr string, tcpaddr *net.TCPAddr) *mongoServer { cluster.RLock() server := cluster.servers.Search(tcpaddr.String()) cluster.RUnlock() if server != nil { return server } return newServer(addr, tcpaddr, cluster.sync, cluster.dial) } func resolveAddr(addr string) (*net.TCPAddr, error) { tcpaddr, err := net.ResolveTCPAddr("tcp", addr) if err != nil { log("SYNC Failed to resolve ", addr, ": ", err.Error()) return nil, err } if tcpaddr.String() != addr { debug("SYNC Address ", addr, " resolved as ", tcpaddr.String()) } return tcpaddr, nil } type pendingAdd struct { server *mongoServer info *mongoServerInfo } func (cluster *mongoCluster) syncServersIteration(direct bool) { log("SYNC Starting full topology synchronization...") var wg sync.WaitGroup var m sync.Mutex notYetAdded := make(map[string]pendingAdd) addIfFound := make(map[string]bool) seen := make(map[string]bool) syncKind := partialSync var spawnSync func(addr string, byMaster bool) spawnSync = func(addr string, byMaster bool) { wg.Add(1) go func() { defer wg.Done() tcpaddr, err := resolveAddr(addr) if err != nil { log("SYNC Failed to start sync of ", addr, ": ", err.Error()) return } resolvedAddr := tcpaddr.String() m.Lock() if byMaster { if pending, ok := notYetAdded[resolvedAddr]; ok { delete(notYetAdded, resolvedAddr) m.Unlock() cluster.addServer(pending.server, pending.info, completeSync) return } addIfFound[resolvedAddr] = true } if seen[resolvedAddr] { m.Unlock() return } seen[resolvedAddr] = true m.Unlock() server := cluster.server(addr, tcpaddr) info, hosts, err := cluster.syncServer(server) if err != nil { cluster.removeServer(server) return } m.Lock() add := direct || info.Master || addIfFound[resolvedAddr] if add { syncKind = completeSync } else { notYetAdded[resolvedAddr] = pendingAdd{server, info} } m.Unlock() if add { cluster.addServer(server, info, completeSync) } if !direct { for _, addr := range hosts { spawnSync(addr, info.Master) } } }() } knownAddrs := cluster.getKnownAddrs() for _, addr := range knownAddrs { spawnSync(addr, false) } wg.Wait() if syncKind == completeSync { logf("SYNC Synchronization was complete (got data from primary).") for _, pending := range notYetAdded { cluster.removeServer(pending.server) } } else { logf("SYNC Synchronization was partial (cannot talk to primary).") for _, pending := range notYetAdded { cluster.addServer(pending.server, pending.info, partialSync) } } cluster.Lock() ml := cluster.masters.Len() logf("SYNC Synchronization completed: %d master(s) and %d slave(s) alive.", ml, cluster.servers.Len()-ml) // Update dynamic seeds, but only if we have any good servers. Otherwise, // leave them alone for better chances of a successful sync in the future. if syncKind == completeSync { dynaSeeds := make([]string, cluster.servers.Len()) for i, server := range cluster.servers.Slice() { dynaSeeds[i] = server.Addr } cluster.dynaSeeds = dynaSeeds debugf("SYNC New dynamic seeds: %#v\n", dynaSeeds) } cluster.Unlock() } var socketsPerServer = 4096 // AcquireSocket returns a socket to a server in the cluster. If slaveOk is // true, it will attempt to return a socket to a slave server. If it is // false, the socket will necessarily be to a master server. func (cluster *mongoCluster) AcquireSocket(slaveOk bool, syncTimeout time.Duration, socketTimeout time.Duration, serverTags []bson.D) (s *mongoSocket, err error) { var started time.Time warnedLimit := false for { cluster.RLock() for { ml := cluster.masters.Len() sl := cluster.servers.Len() debugf("Cluster has %d known masters and %d known slaves.", ml, sl-ml) if ml > 0 || slaveOk && sl > 0 { break } if started.IsZero() { started = time.Now() // Initialize after fast path above. } else if syncTimeout != 0 && started.Before(time.Now().Add(-syncTimeout)) { cluster.RUnlock() return nil, errors.New("no reachable servers") } log("Waiting for servers to synchronize...") cluster.syncServers() // Remember: this will release and reacquire the lock. cluster.serverSynced.Wait() } var server *mongoServer if slaveOk { server = cluster.servers.BestFit(serverTags) } else { server = cluster.masters.BestFit(nil) } cluster.RUnlock() if server == nil { // Must have failed the requested tags. Sleep to avoid spinning. time.Sleep(1e8) continue } s, abended, err := server.AcquireSocket(socketsPerServer, socketTimeout) if err == errSocketLimit { if !warnedLimit { log("WARNING: Per-server connection limit reached.") } time.Sleep(1e8) continue } if err != nil { cluster.removeServer(server) cluster.syncServers() continue } if abended && !slaveOk { var result isMasterResult err := cluster.isMaster(s, &result) if err != nil || !result.IsMaster { logf("Cannot confirm server %s as master (%v)", server.Addr, err) s.Release() cluster.syncServers() time.Sleep(1e8) continue } } return s, nil } panic("unreached") } func (cluster *mongoCluster) CacheIndex(cacheKey string, exists bool) { cluster.Lock() if cluster.cachedIndex == nil { cluster.cachedIndex = make(map[string]bool) } if exists { cluster.cachedIndex[cacheKey] = true } else { delete(cluster.cachedIndex, cacheKey) } cluster.Unlock() } func (cluster *mongoCluster) HasCachedIndex(cacheKey string) (result bool) { cluster.RLock() if cluster.cachedIndex != nil { result = cluster.cachedIndex[cacheKey] } cluster.RUnlock() return } func (cluster *mongoCluster) ResetIndexCache() { cluster.Lock() cluster.cachedIndex = make(map[string]bool) cluster.Unlock() } juju-core_1.18.1/src/labix.org/v2/mgo/bson/0000755000015300001610000000000012321736015020170 5ustar jenkinsjenkinsjuju-core_1.18.1/src/labix.org/v2/mgo/bson/LICENSE0000644000015300001610000000251412321735660021204 0ustar jenkinsjenkinsBSON library for Go Copyright (c) 2010-2012 - Gustavo Niemeyer All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. juju-core_1.18.1/src/labix.org/v2/mgo/bson/bson.go0000644000015300001610000005235712321735661021502 0ustar jenkinsjenkins// BSON library for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Package bson is an implementation of the BSON specification for Go: // // http://bsonspec.org // // It was created as part of the mgo MongoDB driver for Go, but is standalone // and may be used on its own without the driver. package bson import ( "crypto/md5" "crypto/rand" "encoding/binary" "encoding/hex" "errors" "fmt" "io" "os" "reflect" "runtime" "strings" "sync" "sync/atomic" "time" ) // -------------------------------------------------------------------------- // The public API. // A value implementing the bson.Getter interface will have its GetBSON // method called when the given value has to be marshalled, and the result // of this method will be marshaled in place of the actual object. // // If GetBSON returns return a non-nil error, the marshalling procedure // will stop and error out with the provided value. type Getter interface { GetBSON() (interface{}, error) } // A value implementing the bson.Setter interface will receive the BSON // value via the SetBSON method during unmarshaling, and the object // itself will not be changed as usual. // // If setting the value works, the method should return nil or alternatively // bson.SetZero to set the respective field to its zero value (nil for // pointer types). If SetBSON returns a value of type bson.TypeError, the // BSON value will be omitted from a map or slice being decoded and the // unmarshalling will continue. If it returns any other non-nil error, the // unmarshalling procedure will stop and error out with the provided value. // // This interface is generally useful in pointer receivers, since the method // will want to change the receiver. A type field that implements the Setter // interface doesn't have to be a pointer, though. // // Unlike the usual behavior, unmarshalling onto a value that implements a // Setter interface will NOT reset the value to its zero state. This allows // the value to decide by itself how to be unmarshalled. // // For example: // // type MyString string // // func (s *MyString) SetBSON(raw bson.Raw) error { // return raw.Unmarshal(s) // } // type Setter interface { SetBSON(raw Raw) error } // SetZero may be returned from a SetBSON method to have the value set to // its respective zero value. When used in pointer values, this will set the // field to nil rather than to the pre-allocated value. var SetZero = errors.New("set to zero") // M is a convenient alias for a map[string]interface{} map, useful for // dealing with BSON in a native way. For instance: // // bson.M{"a": 1, "b": true} // // There's no special handling for this type in addition to what's done anyway // for an equivalent map type. Elements in the map will be dumped in an // undefined ordered. See also the bson.D type for an ordered alternative. type M map[string]interface{} // D represents a BSON document containing ordered elements. For example: // // bson.D{{"a", 1}, {"b", true}} // // In some situations, such as when creating indexes for MongoDB, the order in // which the elements are defined is important. If the order is not important, // using a map is generally more comfortable. See bson.M and bson.RawD. type D []DocElem // See the D type. type DocElem struct { Name string Value interface{} } // Map returns a map out of the ordered element name/value pairs in d. func (d D) Map() (m M) { m = make(M, len(d)) for _, item := range d { m[item.Name] = item.Value } return m } // The Raw type represents raw unprocessed BSON documents and elements. // Kind is the kind of element as defined per the BSON specification, and // Data is the raw unprocessed data for the respective element. // Using this type it is possible to unmarshal or marshal values partially. // // Relevant documentation: // // http://bsonspec.org/#/specification // type Raw struct { Kind byte Data []byte } // RawD represents a BSON document containing raw unprocessed elements. // This low-level representation may be useful when lazily processing // documents of uncertain content, or when manipulating the raw content // documents in general. type RawD []RawDocElem // See the RawD type. type RawDocElem struct { Name string Value Raw } // ObjectId is a unique ID identifying a BSON value. It must be exactly 12 bytes // long. MongoDB objects by default have such a property set in their "_id" // property. // // http://www.mongodb.org/display/DOCS/Object+IDs type ObjectId string // ObjectIdHex returns an ObjectId from the provided hex representation. // Calling this function with an invalid hex representation will // cause a runtime panic. See the IsObjectIdHex function. func ObjectIdHex(s string) ObjectId { d, err := hex.DecodeString(s) if err != nil || len(d) != 12 { panic(fmt.Sprintf("Invalid input to ObjectIdHex: %q", s)) } return ObjectId(d) } // IsObjectIdHex returns whether s is a valid hex representation of // an ObjectId. See the ObjectIdHex function. func IsObjectIdHex(s string) bool { if len(s) != 24 { return false } _, err := hex.DecodeString(s) return err == nil } // objectIdCounter is atomically incremented when generating a new ObjectId // using NewObjectId() function. It's used as a counter part of an id. var objectIdCounter uint32 = 0 // machineId stores machine id generated once and used in subsequent calls // to NewObjectId function. var machineId = readMachineId() // initMachineId generates machine id and puts it into the machineId global // variable. If this function fails to get the hostname, it will cause // a runtime error. func readMachineId() []byte { var sum [3]byte id := sum[:] hostname, err1 := os.Hostname() if err1 != nil { _, err2 := io.ReadFull(rand.Reader, id) if err2 != nil { panic(fmt.Errorf("cannot get hostname: %v; %v", err1, err2)) } return id } hw := md5.New() hw.Write([]byte(hostname)) copy(id, hw.Sum(nil)) return id } // NewObjectId returns a new unique ObjectId. // This function causes a runtime error if it fails to get the hostname // of the current machine. func NewObjectId() ObjectId { var b [12]byte // Timestamp, 4 bytes, big endian binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix())) // Machine, first 3 bytes of md5(hostname) b[4] = machineId[0] b[5] = machineId[1] b[6] = machineId[2] // Pid, 2 bytes, specs don't specify endianness, but we use big endian. pid := os.Getpid() b[7] = byte(pid >> 8) b[8] = byte(pid) // Increment, 3 bytes, big endian i := atomic.AddUint32(&objectIdCounter, 1) b[9] = byte(i >> 16) b[10] = byte(i >> 8) b[11] = byte(i) return ObjectId(b[:]) } // NewObjectIdWithTime returns a dummy ObjectId with the timestamp part filled // with the provided number of seconds from epoch UTC, and all other parts // filled with zeroes. It's not safe to insert a document with an id generated // by this method, it is useful only for queries to find documents with ids // generated before or after the specified timestamp. func NewObjectIdWithTime(t time.Time) ObjectId { var b [12]byte binary.BigEndian.PutUint32(b[:4], uint32(t.Unix())) return ObjectId(string(b[:])) } // String returns a hex string representation of the id. // Example: ObjectIdHex("4d88e15b60f486e428412dc9"). func (id ObjectId) String() string { return fmt.Sprintf(`ObjectIdHex("%x")`, string(id)) } // Hex returns a hex representation of the ObjectId. func (id ObjectId) Hex() string { return hex.EncodeToString([]byte(id)) } // MarshalJSON turns a bson.ObjectId into a json.Marshaller. func (id ObjectId) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf(`"%x"`, string(id))), nil } // UnmarshalJSON turns *bson.ObjectId into a json.Unmarshaller. func (id *ObjectId) UnmarshalJSON(data []byte) error { if len(data) != 26 || data[0] != '"' || data[25] != '"' { return errors.New(fmt.Sprintf("Invalid ObjectId in JSON: %s", string(data))) } var buf [12]byte _, err := hex.Decode(buf[:], data[1:25]) if err != nil { return errors.New(fmt.Sprintf("Invalid ObjectId in JSON: %s (%s)", string(data), err)) } *id = ObjectId(string(buf[:])) return nil } // Valid returns true if id is valid. A valid id must contain exactly 12 bytes. func (id ObjectId) Valid() bool { return len(id) == 12 } // byteSlice returns byte slice of id from start to end. // Calling this function with an invalid id will cause a runtime panic. func (id ObjectId) byteSlice(start, end int) []byte { if len(id) != 12 { panic(fmt.Sprintf("Invalid ObjectId: %q", string(id))) } return []byte(string(id)[start:end]) } // Time returns the timestamp part of the id. // It's a runtime error to call this method with an invalid id. func (id ObjectId) Time() time.Time { // First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch. secs := int64(binary.BigEndian.Uint32(id.byteSlice(0, 4))) return time.Unix(secs, 0) } // Machine returns the 3-byte machine id part of the id. // It's a runtime error to call this method with an invalid id. func (id ObjectId) Machine() []byte { return id.byteSlice(4, 7) } // Pid returns the process id part of the id. // It's a runtime error to call this method with an invalid id. func (id ObjectId) Pid() uint16 { return binary.BigEndian.Uint16(id.byteSlice(7, 9)) } // Counter returns the incrementing value part of the id. // It's a runtime error to call this method with an invalid id. func (id ObjectId) Counter() int32 { b := id.byteSlice(9, 12) // Counter is stored as big-endian 3-byte value return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])) } // The Symbol type is similar to a string and is used in languages with a // distinct symbol type. type Symbol string // Now returns the current time with millisecond precision. MongoDB stores // timestamps with the same precision, so a Time returned from this method // will not change after a roundtrip to the database. That's the only reason // why this function exists. Using the time.Now function also works fine // otherwise. func Now() time.Time { return time.Unix(0, time.Now().UnixNano()/1e6*1e6) } // MongoTimestamp is a special internal type used by MongoDB that for some // strange reason has its own datatype defined in BSON. type MongoTimestamp int64 type orderKey int64 // MaxKey is a special value that compares higher than all other possible BSON // values in a MongoDB database. var MaxKey = orderKey(1<<63 - 1) // MinKey is a special value that compares lower than all other possible BSON // values in a MongoDB database. var MinKey = orderKey(-1 << 63) type undefined struct{} // Undefined represents the undefined BSON value. var Undefined undefined // Binary is a representation for non-standard binary values. Any kind should // work, but the following are known as of this writing: // // 0x00 - Generic. This is decoded as []byte(data), not Binary{0x00, data}. // 0x01 - Function (!?) // 0x02 - Obsolete generic. // 0x03 - UUID // 0x05 - MD5 // 0x80 - User defined. // type Binary struct { Kind byte Data []byte } // RegEx represents a regular expression. The Options field may contain // individual characters defining the way in which the pattern should be // applied, and must be sorted. Valid options as of this writing are 'i' for // case insensitive matching, 'm' for multi-line matching, 'x' for verbose // mode, 'l' to make \w, \W, and similar be locale-dependent, 's' for dot-all // mode (a '.' matches everything), and 'u' to make \w, \W, and similar match // unicode. The value of the Options parameter is not verified before being // marshaled into the BSON format. type RegEx struct { Pattern string Options string } // JavaScript is a type that holds JavaScript code. If Scope is non-nil, it // will be marshaled as a mapping from identifiers to values that may be // used when evaluating the provided Code. type JavaScript struct { Code string Scope interface{} } const initialBufferSize = 64 func handleErr(err *error) { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { panic(r) } else if _, ok := r.(externalPanic); ok { panic(r) } else if s, ok := r.(string); ok { *err = errors.New(s) } else if e, ok := r.(error); ok { *err = e } else { panic(r) } } } // Marshal serializes the in value, which may be a map or a struct value. // In the case of struct values, only exported fields will be serialized. // The lowercased field name is used as the key for each exported field, // but this behavior may be changed using the respective field tag. // The tag may also contain flags to tweak the marshalling behavior for // the field. The tag formats accepted are: // // "[][,[,]]" // // `(...) bson:"[][,[,]]" (...)` // // The following flags are currently supported: // // omitempty Only include the field if it's not set to the zero // value for the type or to empty slices or maps. // // minsize Marshal an int64 value as an int32, if that's feasible // while preserving the numeric value. // // inline Inline the field, which must be a struct or a map, // causing all of its fields or keys to be processed as if // they were part of the outer struct. For maps, keys must // not conflict with the bson keys of other struct fields. // // Some examples: // // type T struct { // A bool // B int "myb" // C string "myc,omitempty" // D string `bson:",omitempty" json:"jsonkey"` // E int64 ",minsize" // F int64 "myf,omitempty,minsize" // } // func Marshal(in interface{}) (out []byte, err error) { defer handleErr(&err) e := &encoder{make([]byte, 0, initialBufferSize)} e.addDoc(reflect.ValueOf(in)) return e.out, nil } // Unmarshal deserializes data from in into the out value. The out value // must be a map, a pointer to a struct, or a pointer to a bson.D value. // The lowercased field name is used as the key for each exported field, // but this behavior may be changed using the respective field tag. // The tag may also contain flags to tweak the marshalling behavior for // the field. The tag formats accepted are: // // "[][,[,]]" // // `(...) bson:"[][,[,]]" (...)` // // The following flags are currently supported during unmarshal (see the // Marshal method for other flags): // // inline Inline the field, which must be a struct or a map. // Inlined structs are handled as if its fields were part // of the outer struct. An inlined map causes keys that do // not match any other struct field to be inserted in the // map rather than being discarded as usual. // // The target field or element types of out may not necessarily match // the BSON values of the provided data. The following conversions are // made automatically: // // - Numeric types are converted if at least the integer part of the // value would be preserved correctly // - Bools are converted to numeric types as 1 or 0 // - Numeric types are converted to bools as true if not 0 or false otherwise // - Binary and string BSON data is converted to a string, array or byte slice // // If the value would not fit the type and cannot be converted, it's // silently skipped. // // Pointer values are initialized when necessary. func Unmarshal(in []byte, out interface{}) (err error) { defer handleErr(&err) v := reflect.ValueOf(out) switch v.Kind() { case reflect.Map, reflect.Ptr: d := newDecoder(in) d.readDocTo(v) case reflect.Struct: return errors.New("Unmarshal can't deal with struct values. Use a pointer.") default: return errors.New("Unmarshal needs a map or a pointer to a struct.") } return nil } // Unmarshal deserializes raw into the out value. If the out value type // is not compatible with raw, a *bson.TypeError is returned. // // See the Unmarshal function documentation for more details on the // unmarshalling process. func (raw Raw) Unmarshal(out interface{}) (err error) { defer handleErr(&err) v := reflect.ValueOf(out) switch v.Kind() { case reflect.Ptr: v = v.Elem() fallthrough case reflect.Map: d := newDecoder(raw.Data) good := d.readElemTo(v, raw.Kind) if !good { return &TypeError{v.Type(), raw.Kind} } case reflect.Struct: return errors.New("Raw Unmarshal can't deal with struct values. Use a pointer.") default: return errors.New("Raw Unmarshal needs a map or a valid pointer.") } return nil } type TypeError struct { Type reflect.Type Kind byte } func (e *TypeError) Error() string { return fmt.Sprintf("BSON kind 0x%02x isn't compatible with type %s", e.Kind, e.Type.String()) } // -------------------------------------------------------------------------- // Maintain a mapping of keys to structure field indexes type structInfo struct { FieldsMap map[string]fieldInfo FieldsList []fieldInfo InlineMap int Zero reflect.Value } type fieldInfo struct { Key string Num int OmitEmpty bool MinSize bool Inline []int } var structMap = make(map[reflect.Type]*structInfo) var structMapMutex sync.RWMutex type externalPanic string func (e externalPanic) String() string { return string(e) } func getStructInfo(st reflect.Type) (*structInfo, error) { structMapMutex.RLock() sinfo, found := structMap[st] structMapMutex.RUnlock() if found { return sinfo, nil } n := st.NumField() fieldsMap := make(map[string]fieldInfo) fieldsList := make([]fieldInfo, 0, n) inlineMap := -1 for i := 0; i != n; i++ { field := st.Field(i) if field.PkgPath != "" { continue // Private field } info := fieldInfo{Num: i} tag := field.Tag.Get("bson") if tag == "" && strings.Index(string(field.Tag), ":") < 0 { tag = string(field.Tag) } if tag == "-" { continue } // XXX Drop this after a few releases. if s := strings.Index(tag, "/"); s >= 0 { recommend := tag[:s] for _, c := range tag[s+1:] { switch c { case 'c': recommend += ",omitempty" case 's': recommend += ",minsize" default: msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", string([]byte{uint8(c)}), tag, st) panic(externalPanic(msg)) } } msg := fmt.Sprintf("Replace tag %q in field %s of type %s by %q", tag, field.Name, st, recommend) panic(externalPanic(msg)) } inline := false fields := strings.Split(tag, ",") if len(fields) > 1 { for _, flag := range fields[1:] { switch flag { case "omitempty": info.OmitEmpty = true case "minsize": info.MinSize = true case "inline": inline = true default: msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st) panic(externalPanic(msg)) } } tag = fields[0] } if inline { switch field.Type.Kind() { case reflect.Map: if inlineMap >= 0 { return nil, errors.New("Multiple ,inline maps in struct " + st.String()) } if field.Type.Key() != reflect.TypeOf("") { return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) } inlineMap = info.Num case reflect.Struct: sinfo, err := getStructInfo(field.Type) if err != nil { return nil, err } for _, finfo := range sinfo.FieldsList { if _, found := fieldsMap[finfo.Key]; found { msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() return nil, errors.New(msg) } if finfo.Inline == nil { finfo.Inline = []int{i, finfo.Num} } else { finfo.Inline = append([]int{i}, finfo.Inline...) } fieldsMap[finfo.Key] = finfo fieldsList = append(fieldsList, finfo) } default: panic("Option ,inline needs a struct value or map field") } continue } if tag != "" { info.Key = tag } else { info.Key = strings.ToLower(field.Name) } if _, found = fieldsMap[info.Key]; found { msg := "Duplicated key '" + info.Key + "' in struct " + st.String() return nil, errors.New(msg) } fieldsList = append(fieldsList, info) fieldsMap[info.Key] = info } sinfo = &structInfo{ fieldsMap, fieldsList, inlineMap, reflect.New(st).Elem(), } structMapMutex.Lock() structMap[st] = sinfo structMapMutex.Unlock() return sinfo, nil } juju-core_1.18.1/src/labix.org/v2/mgo/bson/bson_test.go0000644000015300001610000012622012321736015022522 0ustar jenkinsjenkins// BSON library for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // gobson - BSON library for Go. package bson_test import ( "encoding/binary" "encoding/json" "errors" "labix.org/v2/mgo/bson" . "launchpad.net/gocheck" "net/url" "reflect" "testing" "time" ) func TestAll(t *testing.T) { TestingT(t) } type S struct{} var _ = Suite(&S{}) // Wrap up the document elements contained in data, prepending the int32 // length of the data, and appending the '\x00' value closing the document. func wrapInDoc(data string) string { result := make([]byte, len(data)+5) binary.LittleEndian.PutUint32(result, uint32(len(result))) copy(result[4:], []byte(data)) return string(result) } func makeZeroDoc(value interface{}) (zero interface{}) { v := reflect.ValueOf(value) t := v.Type() switch t.Kind() { case reflect.Map: mv := reflect.MakeMap(t) zero = mv.Interface() case reflect.Ptr: pv := reflect.New(v.Type().Elem()) zero = pv.Interface() case reflect.Slice: zero = reflect.New(t).Interface() default: panic("unsupported doc type") } return zero } func testUnmarshal(c *C, data string, obj interface{}) { zero := makeZeroDoc(obj) err := bson.Unmarshal([]byte(data), zero) c.Assert(err, IsNil) c.Assert(zero, DeepEquals, obj) } type testItemType struct { obj interface{} data string } // -------------------------------------------------------------------------- // Samples from bsonspec.org: var sampleItems = []testItemType{ {bson.M{"hello": "world"}, "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"}, {bson.M{"BSON": []interface{}{"awesome", float64(5.05), 1986}}, "1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" + "awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"}, } func (s *S) TestMarshalSampleItems(c *C) { for i, item := range sampleItems { data, err := bson.Marshal(item.obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, item.data, Commentf("Failed on item %d", i)) } } func (s *S) TestUnmarshalSampleItems(c *C) { for i, item := range sampleItems { value := bson.M{} err := bson.Unmarshal([]byte(item.data), value) c.Assert(err, IsNil) c.Assert(value, DeepEquals, item.obj, Commentf("Failed on item %d", i)) } } // -------------------------------------------------------------------------- // Every type, ordered by the type flag. These are not wrapped with the // length and last \x00 from the document. wrapInDoc() computes them. // Note that all of them should be supported as two-way conversions. var allItems = []testItemType{ {bson.M{}, ""}, {bson.M{"_": float64(5.05)}, "\x01_\x00333333\x14@"}, {bson.M{"_": "yo"}, "\x02_\x00\x03\x00\x00\x00yo\x00"}, {bson.M{"_": bson.M{"a": true}}, "\x03_\x00\x09\x00\x00\x00\x08a\x00\x01\x00"}, {bson.M{"_": []interface{}{true, false}}, "\x04_\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"}, {bson.M{"_": []byte("yo")}, "\x05_\x00\x02\x00\x00\x00\x00yo"}, {bson.M{"_": bson.Binary{0x80, []byte("udef")}}, "\x05_\x00\x04\x00\x00\x00\x80udef"}, {bson.M{"_": bson.Undefined}, // Obsolete, but still seen in the wild. "\x06_\x00"}, {bson.M{"_": bson.ObjectId("0123456789ab")}, "\x07_\x000123456789ab"}, {bson.M{"_": false}, "\x08_\x00\x00"}, {bson.M{"_": true}, "\x08_\x00\x01"}, {bson.M{"_": time.Unix(0, 258e6)}, // Note the NS <=> MS conversion. "\x09_\x00\x02\x01\x00\x00\x00\x00\x00\x00"}, {bson.M{"_": nil}, "\x0A_\x00"}, {bson.M{"_": bson.RegEx{"ab", "cd"}}, "\x0B_\x00ab\x00cd\x00"}, {bson.M{"_": bson.JavaScript{"code", nil}}, "\x0D_\x00\x05\x00\x00\x00code\x00"}, {bson.M{"_": bson.Symbol("sym")}, "\x0E_\x00\x04\x00\x00\x00sym\x00"}, {bson.M{"_": bson.JavaScript{"code", bson.M{"": nil}}}, "\x0F_\x00\x14\x00\x00\x00\x05\x00\x00\x00code\x00" + "\x07\x00\x00\x00\x0A\x00\x00"}, {bson.M{"_": 258}, "\x10_\x00\x02\x01\x00\x00"}, {bson.M{"_": bson.MongoTimestamp(258)}, "\x11_\x00\x02\x01\x00\x00\x00\x00\x00\x00"}, {bson.M{"_": int64(258)}, "\x12_\x00\x02\x01\x00\x00\x00\x00\x00\x00"}, {bson.M{"_": int64(258 << 32)}, "\x12_\x00\x00\x00\x00\x00\x02\x01\x00\x00"}, {bson.M{"_": bson.MaxKey}, "\x7F_\x00"}, {bson.M{"_": bson.MinKey}, "\xFF_\x00"}, } func (s *S) TestMarshalAllItems(c *C) { for i, item := range allItems { data, err := bson.Marshal(item.obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, wrapInDoc(item.data), Commentf("Failed on item %d: %#v", i, item)) } } func (s *S) TestUnmarshalAllItems(c *C) { for i, item := range allItems { value := bson.M{} err := bson.Unmarshal([]byte(wrapInDoc(item.data)), value) c.Assert(err, IsNil) c.Assert(value, DeepEquals, item.obj, Commentf("Failed on item %d: %#v", i, item)) } } func (s *S) TestUnmarshalRawAllItems(c *C) { for i, item := range allItems { if len(item.data) == 0 { continue } value := item.obj.(bson.M)["_"] if value == nil { continue } pv := reflect.New(reflect.ValueOf(value).Type()) raw := bson.Raw{item.data[0], []byte(item.data[3:])} c.Logf("Unmarshal raw: %#v, %#v", raw, pv.Interface()) err := raw.Unmarshal(pv.Interface()) c.Assert(err, IsNil) c.Assert(pv.Elem().Interface(), DeepEquals, value, Commentf("Failed on item %d: %#v", i, item)) } } func (s *S) TestUnmarshalRawIncompatible(c *C) { raw := bson.Raw{0x08, []byte{0x01}} // true err := raw.Unmarshal(&struct{}{}) c.Assert(err, ErrorMatches, "BSON kind 0x08 isn't compatible with type struct \\{\\}") } func (s *S) TestUnmarshalZeroesStruct(c *C) { data, err := bson.Marshal(bson.M{"b": 2}) c.Assert(err, IsNil) type T struct{ A, B int } v := T{A: 1} err = bson.Unmarshal(data, &v) c.Assert(err, IsNil) c.Assert(v.A, Equals, 0) c.Assert(v.B, Equals, 2) } func (s *S) TestUnmarshalZeroesMap(c *C) { data, err := bson.Marshal(bson.M{"b": 2}) c.Assert(err, IsNil) m := bson.M{"a": 1} err = bson.Unmarshal(data, &m) c.Assert(err, IsNil) c.Assert(m, DeepEquals, bson.M{"b": 2}) } func (s *S) TestUnmarshalNonNilInterface(c *C) { data, err := bson.Marshal(bson.M{"b": 2}) c.Assert(err, IsNil) m := bson.M{"a": 1} var i interface{} i = m err = bson.Unmarshal(data, &i) c.Assert(err, IsNil) c.Assert(i, DeepEquals, bson.M{"b": 2}) c.Assert(m, DeepEquals, bson.M{"a": 1}) } // -------------------------------------------------------------------------- // Some one way marshaling operations which would unmarshal differently. var oneWayMarshalItems = []testItemType{ // These are being passed as pointers, and will unmarshal as values. {bson.M{"": &bson.Binary{0x02, []byte("old")}}, "\x05\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"}, {bson.M{"": &bson.Binary{0x80, []byte("udef")}}, "\x05\x00\x04\x00\x00\x00\x80udef"}, {bson.M{"": &bson.RegEx{"ab", "cd"}}, "\x0B\x00ab\x00cd\x00"}, {bson.M{"": &bson.JavaScript{"code", nil}}, "\x0D\x00\x05\x00\x00\x00code\x00"}, {bson.M{"": &bson.JavaScript{"code", bson.M{"": nil}}}, "\x0F\x00\x14\x00\x00\x00\x05\x00\x00\x00code\x00" + "\x07\x00\x00\x00\x0A\x00\x00"}, // There's no float32 type in BSON. Will encode as a float64. {bson.M{"": float32(5.05)}, "\x01\x00\x00\x00\x00@33\x14@"}, // The array will be unmarshaled as a slice instead. {bson.M{"": [2]bool{true, false}}, "\x04\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"}, // The typed slice will be unmarshaled as []interface{}. {bson.M{"": []bool{true, false}}, "\x04\x00\r\x00\x00\x00\x080\x00\x01\x081\x00\x00\x00"}, // Will unmarshal as a []byte. {bson.M{"": bson.Binary{0x00, []byte("yo")}}, "\x05\x00\x02\x00\x00\x00\x00yo"}, {bson.M{"": bson.Binary{0x02, []byte("old")}}, "\x05\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"}, // No way to preserve the type information here. We might encode as a zero // value, but this would mean that pointer values in structs wouldn't be // able to correctly distinguish between unset and set to the zero value. {bson.M{"": (*byte)(nil)}, "\x0A\x00"}, // No int types smaller than int32 in BSON. Could encode this as a char, // but it would still be ambiguous, take more, and be awkward in Go when // loaded without typing information. {bson.M{"": byte(8)}, "\x10\x00\x08\x00\x00\x00"}, // There are no unsigned types in BSON. Will unmarshal as int32 or int64. {bson.M{"": uint32(258)}, "\x10\x00\x02\x01\x00\x00"}, {bson.M{"": uint64(258)}, "\x12\x00\x02\x01\x00\x00\x00\x00\x00\x00"}, {bson.M{"": uint64(258 << 32)}, "\x12\x00\x00\x00\x00\x00\x02\x01\x00\x00"}, // This will unmarshal as int. {bson.M{"": int32(258)}, "\x10\x00\x02\x01\x00\x00"}, // That's a special case. The unsigned value is too large for an int32, // so an int64 is used instead. {bson.M{"": uint32(1<<32 - 1)}, "\x12\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"}, {bson.M{"": uint(1<<32 - 1)}, "\x12\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"}, } func (s *S) TestOneWayMarshalItems(c *C) { for i, item := range oneWayMarshalItems { data, err := bson.Marshal(item.obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, wrapInDoc(item.data), Commentf("Failed on item %d", i)) } } // -------------------------------------------------------------------------- // Two-way tests for user-defined structures using the samples // from bsonspec.org. type specSample1 struct { Hello string } type specSample2 struct { BSON []interface{} "BSON" } var structSampleItems = []testItemType{ {&specSample1{"world"}, "\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00"}, {&specSample2{[]interface{}{"awesome", float64(5.05), 1986}}, "1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" + "awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"}, } func (s *S) TestMarshalStructSampleItems(c *C) { for i, item := range structSampleItems { data, err := bson.Marshal(item.obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, item.data, Commentf("Failed on item %d", i)) } } func (s *S) TestUnmarshalStructSampleItems(c *C) { for _, item := range structSampleItems { testUnmarshal(c, item.data, item.obj) } } func (s *S) Test64bitInt(c *C) { var i int64 = (1 << 31) if int(i) > 0 { data, err := bson.Marshal(bson.M{"i": int(i)}) c.Assert(err, IsNil) c.Assert(string(data), Equals, wrapInDoc("\x12i\x00\x00\x00\x00\x80\x00\x00\x00\x00")) var result struct{ I int } err = bson.Unmarshal(data, &result) c.Assert(err, IsNil) c.Assert(int64(result.I), Equals, i) } } // -------------------------------------------------------------------------- // Generic two-way struct marshaling tests. var bytevar = byte(8) var byteptr = &bytevar var structItems = []testItemType{ {&struct{ Ptr *byte }{nil}, "\x0Aptr\x00"}, {&struct{ Ptr *byte }{&bytevar}, "\x10ptr\x00\x08\x00\x00\x00"}, {&struct{ Ptr **byte }{&byteptr}, "\x10ptr\x00\x08\x00\x00\x00"}, {&struct{ Byte byte }{8}, "\x10byte\x00\x08\x00\x00\x00"}, {&struct{ Byte byte }{0}, "\x10byte\x00\x00\x00\x00\x00"}, {&struct { V byte "Tag" }{8}, "\x10Tag\x00\x08\x00\x00\x00"}, {&struct { V *struct { Byte byte } }{&struct{ Byte byte }{8}}, "\x03v\x00" + "\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00"}, {&struct{ priv byte }{}, ""}, // The order of the dumped fields should be the same in the struct. {&struct{ A, C, B, D, F, E *byte }{}, "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x0Ae\x00"}, {&struct{ V bson.Raw }{bson.Raw{0x03, []byte("\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00")}}, "\x03v\x00" + "\x0f\x00\x00\x00\x10byte\x00\b\x00\x00\x00\x00"}, {&struct{ V bson.Raw }{bson.Raw{0x10, []byte("\x00\x00\x00\x00")}}, "\x10v\x00" + "\x00\x00\x00\x00"}, // Byte arrays. {&struct{ V [2]byte }{[2]byte{'y', 'o'}}, "\x05v\x00\x02\x00\x00\x00\x00yo"}, } func (s *S) TestMarshalStructItems(c *C) { for i, item := range structItems { data, err := bson.Marshal(item.obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, wrapInDoc(item.data), Commentf("Failed on item %d", i)) } } func (s *S) TestUnmarshalStructItems(c *C) { for _, item := range structItems { testUnmarshal(c, wrapInDoc(item.data), item.obj) } } func (s *S) TestUnmarshalRawStructItems(c *C) { for i, item := range structItems { raw := bson.Raw{0x03, []byte(wrapInDoc(item.data))} zero := makeZeroDoc(item.obj) err := raw.Unmarshal(zero) c.Assert(err, IsNil) c.Assert(zero, DeepEquals, item.obj, Commentf("Failed on item %d: %#v", i, item)) } } func (s *S) TestUnmarshalRawNil(c *C) { // Regression test: shouldn't try to nil out the pointer itself, // as it's not settable. raw := bson.Raw{0x0A, []byte{}} err := raw.Unmarshal(&struct{}{}) c.Assert(err, IsNil) } // -------------------------------------------------------------------------- // One-way marshaling tests. type dOnIface struct { D interface{} } type ignoreField struct { Before string Ignore string `bson:"-"` After string } var marshalItems = []testItemType{ // Ordered document dump. Will unmarshal as a dictionary by default. {bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", nil}, {"f", nil}, {"e", true}}, "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x08e\x00\x01"}, {MyD{{"a", nil}, {"c", nil}, {"b", nil}, {"d", nil}, {"f", nil}, {"e", true}}, "\x0Aa\x00\x0Ac\x00\x0Ab\x00\x0Ad\x00\x0Af\x00\x08e\x00\x01"}, {&dOnIface{bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", true}}}, "\x03d\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x0Ab\x00\x08d\x00\x01")}, {bson.RawD{{"a", bson.Raw{0x0A, nil}}, {"c", bson.Raw{0x0A, nil}}, {"b", bson.Raw{0x08, []byte{0x01}}}}, "\x0Aa\x00" + "\x0Ac\x00" + "\x08b\x00\x01"}, {MyRawD{{"a", bson.Raw{0x0A, nil}}, {"c", bson.Raw{0x0A, nil}}, {"b", bson.Raw{0x08, []byte{0x01}}}}, "\x0Aa\x00" + "\x0Ac\x00" + "\x08b\x00\x01"}, {&dOnIface{bson.RawD{{"a", bson.Raw{0x0A, nil}}, {"c", bson.Raw{0x0A, nil}}, {"b", bson.Raw{0x08, []byte{0x01}}}}}, "\x03d\x00" + wrapInDoc("\x0Aa\x00" + "\x0Ac\x00" + "\x08b\x00\x01")}, {&ignoreField{"before", "ignore", "after"}, "\x02before\x00\a\x00\x00\x00before\x00\x02after\x00\x06\x00\x00\x00after\x00"}, // Marshalling a Raw document does nothing. {bson.Raw{0x03, []byte(wrapInDoc("anything"))}, "anything"}, {bson.Raw{Data: []byte(wrapInDoc("anything"))}, "anything"}, } func (s *S) TestMarshalOneWayItems(c *C) { for _, item := range marshalItems { data, err := bson.Marshal(item.obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, wrapInDoc(item.data)) } } // -------------------------------------------------------------------------- // One-way unmarshaling tests. var unmarshalItems = []testItemType{ // Field is private. Should not attempt to unmarshal it. {&struct{ priv byte }{}, "\x10priv\x00\x08\x00\x00\x00"}, // Wrong casing. Field names are lowercased. {&struct{ Byte byte }{}, "\x10Byte\x00\x08\x00\x00\x00"}, // Ignore non-existing field. {&struct{ Byte byte }{9}, "\x10boot\x00\x08\x00\x00\x00" + "\x10byte\x00\x09\x00\x00\x00"}, // Do not unmarshal on ignored field. {&ignoreField{"before", "", "after"}, "\x02before\x00\a\x00\x00\x00before\x00" + "\x02-\x00\a\x00\x00\x00ignore\x00" + "\x02after\x00\x06\x00\x00\x00after\x00"}, // Ignore unsuitable types silently. {map[string]string{"str": "s"}, "\x02str\x00\x02\x00\x00\x00s\x00" + "\x10int\x00\x01\x00\x00\x00"}, {map[string][]int{"array": []int{5, 9}}, "\x04array\x00" + wrapInDoc("\x100\x00\x05\x00\x00\x00"+"\x021\x00\x02\x00\x00\x00s\x00"+"\x102\x00\x09\x00\x00\x00")}, // Wrong type. Shouldn't init pointer. {&struct{ Str *byte }{}, "\x02str\x00\x02\x00\x00\x00s\x00"}, {&struct{ Str *struct{ Str string } }{}, "\x02str\x00\x02\x00\x00\x00s\x00"}, // Ordered document. {&struct{ bson.D }{bson.D{{"a", nil}, {"c", nil}, {"b", nil}, {"d", true}}}, "\x03d\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x0Ab\x00\x08d\x00\x01")}, // Raw document. {&bson.Raw{0x03, []byte(wrapInDoc("\x10byte\x00\x08\x00\x00\x00"))}, "\x10byte\x00\x08\x00\x00\x00"}, // RawD document. {&struct{ bson.RawD }{bson.RawD{{"a", bson.Raw{0x0A, []byte{}}}, {"c", bson.Raw{0x0A, []byte{}}}, {"b", bson.Raw{0x08, []byte{0x01}}}}}, "\x03rawd\x00" + wrapInDoc("\x0Aa\x00\x0Ac\x00\x08b\x00\x01")}, // Decode old binary. {bson.M{"_": []byte("old")}, "\x05_\x00\x07\x00\x00\x00\x02\x03\x00\x00\x00old"}, // Decode old binary without length. According to the spec, this shouldn't happen. {bson.M{"_": []byte("old")}, "\x05_\x00\x03\x00\x00\x00\x02old"}, } func (s *S) TestUnmarshalOneWayItems(c *C) { for _, item := range unmarshalItems { testUnmarshal(c, wrapInDoc(item.data), item.obj) } } func (s *S) TestUnmarshalNilInStruct(c *C) { // Nil is the default value, so we need to ensure it's indeed being set. b := byte(1) v := &struct{ Ptr *byte }{&b} err := bson.Unmarshal([]byte(wrapInDoc("\x0Aptr\x00")), v) c.Assert(err, IsNil) c.Assert(v, DeepEquals, &struct{ Ptr *byte }{nil}) } // -------------------------------------------------------------------------- // Marshalling error cases. type structWithDupKeys struct { Name byte Other byte "name" // Tag should precede. } var marshalErrorItems = []testItemType{ {bson.M{"": uint64(1 << 63)}, "BSON has no uint64 type, and value is too large to fit correctly in an int64"}, {bson.M{"": bson.ObjectId("tooshort")}, "ObjectIDs must be exactly 12 bytes long \\(got 8\\)"}, {int64(123), "Can't marshal int64 as a BSON document"}, {bson.M{"": 1i}, "Can't marshal complex128 in a BSON document"}, {&structWithDupKeys{}, "Duplicated key 'name' in struct bson_test.structWithDupKeys"}, {bson.Raw{0x0A, []byte{}}, "Attempted to unmarshal Raw kind 10 as a document"}, {&inlineCantPtr{&struct{ A, B int }{1, 2}}, "Option ,inline needs a struct value or map field"}, {&inlineDupName{1, struct{ A, B int }{2, 3}}, "Duplicated key 'a' in struct bson_test.inlineDupName"}, {&inlineDupMap{}, "Multiple ,inline maps in struct bson_test.inlineDupMap"}, {&inlineBadKeyMap{}, "Option ,inline needs a map with string keys in struct bson_test.inlineBadKeyMap"}, {&inlineMap{A: 1, M: map[string]interface{}{"a": 1}}, `Can't have key "a" in inlined map; conflicts with struct field`}, } func (s *S) TestMarshalErrorItems(c *C) { for _, item := range marshalErrorItems { data, err := bson.Marshal(item.obj) c.Assert(err, ErrorMatches, item.data) c.Assert(data, IsNil) } } // -------------------------------------------------------------------------- // Unmarshalling error cases. type unmarshalErrorType struct { obj interface{} data string error string } var unmarshalErrorItems = []unmarshalErrorType{ // Tag name conflicts with existing parameter. {&structWithDupKeys{}, "\x10name\x00\x08\x00\x00\x00", "Duplicated key 'name' in struct bson_test.structWithDupKeys"}, // Non-string map key. {map[int]interface{}{}, "\x10name\x00\x08\x00\x00\x00", "BSON map must have string keys. Got: map\\[int\\]interface \\{\\}"}, {nil, "\xEEname\x00", "Unknown element kind \\(0xEE\\)"}, {struct{ Name bool }{}, "\x10name\x00\x08\x00\x00\x00", "Unmarshal can't deal with struct values. Use a pointer."}, {123, "\x10name\x00\x08\x00\x00\x00", "Unmarshal needs a map or a pointer to a struct."}, } func (s *S) TestUnmarshalErrorItems(c *C) { for _, item := range unmarshalErrorItems { data := []byte(wrapInDoc(item.data)) var value interface{} switch reflect.ValueOf(item.obj).Kind() { case reflect.Map, reflect.Ptr: value = makeZeroDoc(item.obj) case reflect.Invalid: value = bson.M{} default: value = item.obj } err := bson.Unmarshal(data, value) c.Assert(err, ErrorMatches, item.error) } } type unmarshalRawErrorType struct { obj interface{} raw bson.Raw error string } var unmarshalRawErrorItems = []unmarshalRawErrorType{ // Tag name conflicts with existing parameter. {&structWithDupKeys{}, bson.Raw{0x03, []byte("\x10byte\x00\x08\x00\x00\x00")}, "Duplicated key 'name' in struct bson_test.structWithDupKeys"}, {&struct{}{}, bson.Raw{0xEE, []byte{}}, "Unknown element kind \\(0xEE\\)"}, {struct{ Name bool }{}, bson.Raw{0x10, []byte("\x08\x00\x00\x00")}, "Raw Unmarshal can't deal with struct values. Use a pointer."}, {123, bson.Raw{0x10, []byte("\x08\x00\x00\x00")}, "Raw Unmarshal needs a map or a valid pointer."}, } func (s *S) TestUnmarshalRawErrorItems(c *C) { for i, item := range unmarshalRawErrorItems { err := item.raw.Unmarshal(item.obj) c.Assert(err, ErrorMatches, item.error, Commentf("Failed on item %d: %#v\n", i, item)) } } var corruptedData = []string{ "\x04\x00\x00\x00\x00", // Shorter than minimum "\x06\x00\x00\x00\x00", // Not enough data "\x05\x00\x00", // Broken length "\x05\x00\x00\x00\xff", // Corrupted termination "\x0A\x00\x00\x00\x0Aooop\x00", // Unfinished C string // Array end past end of string (s[2]=0x07 is correct) wrapInDoc("\x04\x00\x09\x00\x00\x00\x0A\x00\x00"), // Array end within string, but past acceptable. wrapInDoc("\x04\x00\x08\x00\x00\x00\x0A\x00\x00"), // Document end within string, but past acceptable. wrapInDoc("\x03\x00\x08\x00\x00\x00\x0A\x00\x00"), // String with corrupted end. wrapInDoc("\x02\x00\x03\x00\x00\x00yo\xFF"), } func (s *S) TestUnmarshalMapDocumentTooShort(c *C) { for _, data := range corruptedData { err := bson.Unmarshal([]byte(data), bson.M{}) c.Assert(err, ErrorMatches, "Document is corrupted") err = bson.Unmarshal([]byte(data), &struct{}{}) c.Assert(err, ErrorMatches, "Document is corrupted") } } // -------------------------------------------------------------------------- // Setter test cases. var setterResult = map[string]error{} type setterType struct { received interface{} } func (o *setterType) SetBSON(raw bson.Raw) error { err := raw.Unmarshal(&o.received) if err != nil { panic("The panic:" + err.Error()) } if s, ok := o.received.(string); ok { if result, ok := setterResult[s]; ok { return result } } return nil } type ptrSetterDoc struct { Field *setterType "_" } type valSetterDoc struct { Field setterType "_" } func (s *S) TestUnmarshalAllItemsWithPtrSetter(c *C) { for _, item := range allItems { for i := 0; i != 2; i++ { var field *setterType if i == 0 { obj := &ptrSetterDoc{} err := bson.Unmarshal([]byte(wrapInDoc(item.data)), obj) c.Assert(err, IsNil) field = obj.Field } else { obj := &valSetterDoc{} err := bson.Unmarshal([]byte(wrapInDoc(item.data)), obj) c.Assert(err, IsNil) field = &obj.Field } if item.data == "" { // Nothing to unmarshal. Should be untouched. if i == 0 { c.Assert(field, IsNil) } else { c.Assert(field.received, IsNil) } } else { expected := item.obj.(bson.M)["_"] c.Assert(field, NotNil, Commentf("Pointer not initialized (%#v)", expected)) c.Assert(field.received, DeepEquals, expected) } } } } func (s *S) TestUnmarshalWholeDocumentWithSetter(c *C) { obj := &setterType{} err := bson.Unmarshal([]byte(sampleItems[0].data), obj) c.Assert(err, IsNil) c.Assert(obj.received, DeepEquals, bson.M{"hello": "world"}) } func (s *S) TestUnmarshalSetterOmits(c *C) { setterResult["2"] = &bson.TypeError{} setterResult["4"] = &bson.TypeError{} defer func() { delete(setterResult, "2") delete(setterResult, "4") }() m := map[string]*setterType{} data := wrapInDoc("\x02abc\x00\x02\x00\x00\x001\x00" + "\x02def\x00\x02\x00\x00\x002\x00" + "\x02ghi\x00\x02\x00\x00\x003\x00" + "\x02jkl\x00\x02\x00\x00\x004\x00") err := bson.Unmarshal([]byte(data), m) c.Assert(err, IsNil) c.Assert(m["abc"], NotNil) c.Assert(m["def"], IsNil) c.Assert(m["ghi"], NotNil) c.Assert(m["jkl"], IsNil) c.Assert(m["abc"].received, Equals, "1") c.Assert(m["ghi"].received, Equals, "3") } func (s *S) TestUnmarshalSetterErrors(c *C) { boom := errors.New("BOOM") setterResult["2"] = boom defer delete(setterResult, "2") m := map[string]*setterType{} data := wrapInDoc("\x02abc\x00\x02\x00\x00\x001\x00" + "\x02def\x00\x02\x00\x00\x002\x00" + "\x02ghi\x00\x02\x00\x00\x003\x00") err := bson.Unmarshal([]byte(data), m) c.Assert(err, Equals, boom) c.Assert(m["abc"], NotNil) c.Assert(m["def"], IsNil) c.Assert(m["ghi"], IsNil) c.Assert(m["abc"].received, Equals, "1") } func (s *S) TestDMap(c *C) { d := bson.D{{"a", 1}, {"b", 2}} c.Assert(d.Map(), DeepEquals, bson.M{"a": 1, "b": 2}) } func (s *S) TestUnmarshalSetterSetZero(c *C) { setterResult["foo"] = bson.SetZero defer delete(setterResult, "field") data, err := bson.Marshal(bson.M{"field": "foo"}) c.Assert(err, IsNil) m := map[string]*setterType{} err = bson.Unmarshal([]byte(data), m) c.Assert(err, IsNil) value, ok := m["field"] c.Assert(ok, Equals, true) c.Assert(value, IsNil) } // -------------------------------------------------------------------------- // Getter test cases. type typeWithGetter struct { result interface{} err error } func (t *typeWithGetter) GetBSON() (interface{}, error) { return t.result, t.err } type docWithGetterField struct { Field *typeWithGetter "_" } func (s *S) TestMarshalAllItemsWithGetter(c *C) { for i, item := range allItems { if item.data == "" { continue } obj := &docWithGetterField{} obj.Field = &typeWithGetter{result: item.obj.(bson.M)["_"]} data, err := bson.Marshal(obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, wrapInDoc(item.data), Commentf("Failed on item #%d", i)) } } func (s *S) TestMarshalWholeDocumentWithGetter(c *C) { obj := &typeWithGetter{result: sampleItems[0].obj} data, err := bson.Marshal(obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, sampleItems[0].data) } func (s *S) TestGetterErrors(c *C) { e := errors.New("oops") obj1 := &docWithGetterField{} obj1.Field = &typeWithGetter{sampleItems[0].obj, e} data, err := bson.Marshal(obj1) c.Assert(err, ErrorMatches, "oops") c.Assert(data, IsNil) obj2 := &typeWithGetter{sampleItems[0].obj, e} data, err = bson.Marshal(obj2) c.Assert(err, ErrorMatches, "oops") c.Assert(data, IsNil) } type intGetter int64 func (t intGetter) GetBSON() (interface{}, error) { return int64(t), nil } type typeWithIntGetter struct { V intGetter ",minsize" } func (s *S) TestMarshalShortWithGetter(c *C) { obj := typeWithIntGetter{42} data, err := bson.Marshal(obj) c.Assert(err, IsNil) m := bson.M{} err = bson.Unmarshal(data, m) c.Assert(m["v"], Equals, 42) } // -------------------------------------------------------------------------- // Cross-type conversion tests. type crossTypeItem struct { obj1 interface{} obj2 interface{} } type condStr struct { V string ",omitempty" } type condStrNS struct { V string `a:"A" bson:",omitempty" b:"B"` } type condBool struct { V bool ",omitempty" } type condInt struct { V int ",omitempty" } type condUInt struct { V uint ",omitempty" } type condFloat struct { V float64 ",omitempty" } type condIface struct { V interface{} ",omitempty" } type condPtr struct { V *bool ",omitempty" } type condSlice struct { V []string ",omitempty" } type condMap struct { V map[string]int ",omitempty" } type namedCondStr struct { V string "myv,omitempty" } type condTime struct { V time.Time ",omitempty" } type condStruct struct { V struct { A []int } ",omitempty" } type shortInt struct { V int64 ",minsize" } type shortUint struct { V uint64 ",minsize" } type shortIface struct { V interface{} ",minsize" } type shortPtr struct { V *int64 ",minsize" } type shortNonEmptyInt struct { V int64 ",minsize,omitempty" } type inlineInt struct { V struct{ A, B int } ",inline" } type inlineCantPtr struct { V *struct{ A, B int } ",inline" } type inlineDupName struct { A int V struct{ A, B int } ",inline" } type inlineMap struct { A int M map[string]interface{} ",inline" } type inlineMapInt struct { A int M map[string]int ",inline" } type inlineMapMyM struct { A int M MyM ",inline" } type inlineDupMap struct { M1 map[string]interface{} ",inline" M2 map[string]interface{} ",inline" } type inlineBadKeyMap struct { M map[int]int ",inline" } type ( MyBytes []byte MyBool bool MyD []bson.DocElem MyRawD []bson.RawDocElem MyM map[string]interface{} ) var ( truevar = true falsevar = false int64var = int64(42) int64ptr = &int64var intvar = int(42) intptr = &intvar ) func parseURL(s string) *url.URL { u, err := url.Parse(s) if err != nil { panic(err) } return u } // That's a pretty fun test. It will dump the first item, generate a zero // value equivalent to the second one, load the dumped data onto it, and then // verify that the resulting value is deep-equal to the untouched second value. // Then, it will do the same in the *opposite* direction! var twoWayCrossItems = []crossTypeItem{ // int<=>int {&struct{ I int }{42}, &struct{ I int8 }{42}}, {&struct{ I int }{42}, &struct{ I int32 }{42}}, {&struct{ I int }{42}, &struct{ I int64 }{42}}, {&struct{ I int8 }{42}, &struct{ I int32 }{42}}, {&struct{ I int8 }{42}, &struct{ I int64 }{42}}, {&struct{ I int32 }{42}, &struct{ I int64 }{42}}, // uint<=>uint {&struct{ I uint }{42}, &struct{ I uint8 }{42}}, {&struct{ I uint }{42}, &struct{ I uint32 }{42}}, {&struct{ I uint }{42}, &struct{ I uint64 }{42}}, {&struct{ I uint8 }{42}, &struct{ I uint32 }{42}}, {&struct{ I uint8 }{42}, &struct{ I uint64 }{42}}, {&struct{ I uint32 }{42}, &struct{ I uint64 }{42}}, // float32<=>float64 {&struct{ I float32 }{42}, &struct{ I float64 }{42}}, // int<=>uint {&struct{ I uint }{42}, &struct{ I int }{42}}, {&struct{ I uint }{42}, &struct{ I int8 }{42}}, {&struct{ I uint }{42}, &struct{ I int32 }{42}}, {&struct{ I uint }{42}, &struct{ I int64 }{42}}, {&struct{ I uint8 }{42}, &struct{ I int }{42}}, {&struct{ I uint8 }{42}, &struct{ I int8 }{42}}, {&struct{ I uint8 }{42}, &struct{ I int32 }{42}}, {&struct{ I uint8 }{42}, &struct{ I int64 }{42}}, {&struct{ I uint32 }{42}, &struct{ I int }{42}}, {&struct{ I uint32 }{42}, &struct{ I int8 }{42}}, {&struct{ I uint32 }{42}, &struct{ I int32 }{42}}, {&struct{ I uint32 }{42}, &struct{ I int64 }{42}}, {&struct{ I uint64 }{42}, &struct{ I int }{42}}, {&struct{ I uint64 }{42}, &struct{ I int8 }{42}}, {&struct{ I uint64 }{42}, &struct{ I int32 }{42}}, {&struct{ I uint64 }{42}, &struct{ I int64 }{42}}, // int <=> float {&struct{ I int }{42}, &struct{ I float64 }{42}}, // int <=> bool {&struct{ I int }{1}, &struct{ I bool }{true}}, {&struct{ I int }{0}, &struct{ I bool }{false}}, // uint <=> float64 {&struct{ I uint }{42}, &struct{ I float64 }{42}}, // uint <=> bool {&struct{ I uint }{1}, &struct{ I bool }{true}}, {&struct{ I uint }{0}, &struct{ I bool }{false}}, // float64 <=> bool {&struct{ I float64 }{1}, &struct{ I bool }{true}}, {&struct{ I float64 }{0}, &struct{ I bool }{false}}, // string <=> string and string <=> []byte {&struct{ S []byte }{[]byte("abc")}, &struct{ S string }{"abc"}}, {&struct{ S []byte }{[]byte("def")}, &struct{ S bson.Symbol }{"def"}}, {&struct{ S string }{"ghi"}, &struct{ S bson.Symbol }{"ghi"}}, // map <=> struct {&struct { A struct { B, C int } }{struct{ B, C int }{1, 2}}, map[string]map[string]int{"a": map[string]int{"b": 1, "c": 2}}}, {&struct{ A bson.Symbol }{"abc"}, map[string]string{"a": "abc"}}, {&struct{ A bson.Symbol }{"abc"}, map[string][]byte{"a": []byte("abc")}}, {&struct{ A []byte }{[]byte("abc")}, map[string]string{"a": "abc"}}, {&struct{ A uint }{42}, map[string]int{"a": 42}}, {&struct{ A uint }{42}, map[string]float64{"a": 42}}, {&struct{ A uint }{1}, map[string]bool{"a": true}}, {&struct{ A int }{42}, map[string]uint{"a": 42}}, {&struct{ A int }{42}, map[string]float64{"a": 42}}, {&struct{ A int }{1}, map[string]bool{"a": true}}, {&struct{ A float64 }{42}, map[string]float32{"a": 42}}, {&struct{ A float64 }{42}, map[string]int{"a": 42}}, {&struct{ A float64 }{42}, map[string]uint{"a": 42}}, {&struct{ A float64 }{1}, map[string]bool{"a": true}}, {&struct{ A bool }{true}, map[string]int{"a": 1}}, {&struct{ A bool }{true}, map[string]uint{"a": 1}}, {&struct{ A bool }{true}, map[string]float64{"a": 1}}, {&struct{ A **byte }{&byteptr}, map[string]byte{"a": 8}}, // url.URL <=> string {&struct{ URL *url.URL }{parseURL("h://e.c/p")}, map[string]string{"url": "h://e.c/p"}}, {&struct{ URL url.URL }{*parseURL("h://e.c/p")}, map[string]string{"url": "h://e.c/p"}}, // Slices {&struct{ S []int }{[]int{1, 2, 3}}, map[string][]int{"s": []int{1, 2, 3}}}, {&struct{ S *[]int }{&[]int{1, 2, 3}}, map[string][]int{"s": []int{1, 2, 3}}}, // Conditionals {&condBool{true}, map[string]bool{"v": true}}, {&condBool{}, map[string]bool{}}, {&condInt{1}, map[string]int{"v": 1}}, {&condInt{}, map[string]int{}}, {&condUInt{1}, map[string]uint{"v": 1}}, {&condUInt{}, map[string]uint{}}, {&condFloat{}, map[string]int{}}, {&condStr{"yo"}, map[string]string{"v": "yo"}}, {&condStr{}, map[string]string{}}, {&condStrNS{"yo"}, map[string]string{"v": "yo"}}, {&condStrNS{}, map[string]string{}}, {&condSlice{[]string{"yo"}}, map[string][]string{"v": []string{"yo"}}}, {&condSlice{}, map[string][]string{}}, {&condMap{map[string]int{"k": 1}}, bson.M{"v": bson.M{"k": 1}}}, {&condMap{}, map[string][]string{}}, {&condIface{"yo"}, map[string]string{"v": "yo"}}, {&condIface{""}, map[string]string{"v": ""}}, {&condIface{}, map[string]string{}}, {&condPtr{&truevar}, map[string]bool{"v": true}}, {&condPtr{&falsevar}, map[string]bool{"v": false}}, {&condPtr{}, map[string]string{}}, {&condTime{time.Unix(123456789, 123e6)}, map[string]time.Time{"v": time.Unix(123456789, 123e6)}}, {&condTime{}, map[string]string{}}, {&condStruct{struct{A []int}{[]int{1}}}, bson.M{"v": bson.M{"a": []interface{}{1}}}}, {&condStruct{struct{A []int}{}}, bson.M{}}, {&namedCondStr{"yo"}, map[string]string{"myv": "yo"}}, {&namedCondStr{}, map[string]string{}}, {&shortInt{1}, map[string]interface{}{"v": 1}}, {&shortInt{1 << 30}, map[string]interface{}{"v": 1 << 30}}, {&shortInt{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}}, {&shortUint{1 << 30}, map[string]interface{}{"v": 1 << 30}}, {&shortUint{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}}, {&shortIface{int64(1) << 31}, map[string]interface{}{"v": int64(1 << 31)}}, {&shortPtr{int64ptr}, map[string]interface{}{"v": intvar}}, {&shortNonEmptyInt{1}, map[string]interface{}{"v": 1}}, {&shortNonEmptyInt{1 << 31}, map[string]interface{}{"v": int64(1 << 31)}}, {&shortNonEmptyInt{}, map[string]interface{}{}}, {&inlineInt{struct{ A, B int }{1, 2}}, map[string]interface{}{"a": 1, "b": 2}}, {&inlineMap{A: 1, M: map[string]interface{}{"b": 2}}, map[string]interface{}{"a": 1, "b": 2}}, {&inlineMap{A: 1, M: nil}, map[string]interface{}{"a": 1}}, {&inlineMapInt{A: 1, M: map[string]int{"b": 2}}, map[string]int{"a": 1, "b": 2}}, {&inlineMapInt{A: 1, M: nil}, map[string]int{"a": 1}}, {&inlineMapMyM{A: 1, M: MyM{"b": MyM{"c": 3}}}, map[string]interface{}{"a": 1, "b": map[string]interface{}{"c": 3}}}, // []byte <=> MyBytes {&struct{ B MyBytes }{[]byte("abc")}, map[string]string{"b": "abc"}}, {&struct{ B MyBytes }{[]byte{}}, map[string]string{"b": ""}}, {&struct{ B MyBytes }{}, map[string]bool{}}, {&struct{ B []byte }{[]byte("abc")}, map[string]MyBytes{"b": []byte("abc")}}, // bool <=> MyBool {&struct{ B MyBool }{true}, map[string]bool{"b": true}}, {&struct{ B MyBool }{}, map[string]bool{"b": false}}, {&struct{ B MyBool }{}, map[string]string{}}, {&struct{ B bool }{}, map[string]MyBool{"b": false}}, // arrays {&struct{ V [2]int }{[...]int{1, 2}}, map[string][2]int{"v": [2]int{1, 2}}}, // zero time {&struct{ V time.Time }{}, map[string]interface{}{"v": time.Time{}}}, // zero time + 1 second + 1 millisecond; overflows int64 as nanoseconds {&struct{ V time.Time }{time.Unix(-62135596799, 1e6).Local()}, map[string]interface{}{"v": time.Unix(-62135596799, 1e6).Local()}}, // bson.D <=> []DocElem {&bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}, &bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}}, {&bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}, &MyD{{"a", MyD{{"b", 1}, {"c", 2}}}}}, // bson.RawD <=> []RawDocElem {&bson.RawD{{"a", bson.Raw{0x08, []byte{0x01}}}}, &bson.RawD{{"a", bson.Raw{0x08, []byte{0x01}}}}}, {&bson.RawD{{"a", bson.Raw{0x08, []byte{0x01}}}}, &MyRawD{{"a", bson.Raw{0x08, []byte{0x01}}}}}, // bson.M <=> map {bson.M{"a": bson.M{"b": 1, "c": 2}}, MyM{"a": MyM{"b": 1, "c": 2}}}, {bson.M{"a": bson.M{"b": 1, "c": 2}}, map[string]interface{}{"a": map[string]interface{}{"b": 1, "c": 2}}}, } // Same thing, but only one way (obj1 => obj2). var oneWayCrossItems = []crossTypeItem{ // map <=> struct {map[string]interface{}{"a": 1, "b": "2", "c": 3}, map[string]int{"a": 1, "c": 3}}, // inline map elides badly typed values {map[string]interface{}{"a": 1, "b": "2", "c": 3}, &inlineMapInt{A: 1, M: map[string]int{"c": 3}}}, // Can't decode int into struct. {bson.M{"a": bson.M{"b": 2}}, &struct{ A bool }{}}, // Would get decoded into a int32 too in the opposite direction. {&shortIface{int64(1) << 30}, map[string]interface{}{"v": 1 << 30}}, } func testCrossPair(c *C, dump interface{}, load interface{}) { c.Logf("Dump: %#v", dump) c.Logf("Load: %#v", load) zero := makeZeroDoc(load) data, err := bson.Marshal(dump) c.Assert(err, IsNil) c.Logf("Dumped: %#v", string(data)) err = bson.Unmarshal(data, zero) c.Assert(err, IsNil) c.Logf("Loaded: %#v", zero) c.Assert(zero, DeepEquals, load) } func (s *S) TestTwoWayCrossPairs(c *C) { for _, item := range twoWayCrossItems { testCrossPair(c, item.obj1, item.obj2) testCrossPair(c, item.obj2, item.obj1) } } func (s *S) TestOneWayCrossPairs(c *C) { for _, item := range oneWayCrossItems { testCrossPair(c, item.obj1, item.obj2) } } // -------------------------------------------------------------------------- // ObjectId hex representation test. func (s *S) TestObjectIdHex(c *C) { id := bson.ObjectIdHex("4d88e15b60f486e428412dc9") c.Assert(id.String(), Equals, `ObjectIdHex("4d88e15b60f486e428412dc9")`) c.Assert(id.Hex(), Equals, "4d88e15b60f486e428412dc9") } func (s *S) TestIsObjectIdHex(c *C) { test := []struct { id string valid bool }{ {"4d88e15b60f486e428412dc9", true}, {"4d88e15b60f486e428412dc", false}, {"4d88e15b60f486e428412dc9e", false}, {"4d88e15b60f486e428412dcx", false}, } for _, t := range test { c.Assert(bson.IsObjectIdHex(t.id), Equals, t.valid) } } // -------------------------------------------------------------------------- // ObjectId parts extraction tests. type objectIdParts struct { id bson.ObjectId timestamp int64 machine []byte pid uint16 counter int32 } var objectIds = []objectIdParts{ objectIdParts{ bson.ObjectIdHex("4d88e15b60f486e428412dc9"), 1300816219, []byte{0x60, 0xf4, 0x86}, 0xe428, 4271561, }, objectIdParts{ bson.ObjectIdHex("000000000000000000000000"), 0, []byte{0x00, 0x00, 0x00}, 0x0000, 0, }, objectIdParts{ bson.ObjectIdHex("00000000aabbccddee000001"), 0, []byte{0xaa, 0xbb, 0xcc}, 0xddee, 1, }, } func (s *S) TestObjectIdPartsExtraction(c *C) { for i, v := range objectIds { t := time.Unix(v.timestamp, 0) c.Assert(v.id.Time(), Equals, t, Commentf("#%d Wrong timestamp value", i)) c.Assert(v.id.Machine(), DeepEquals, v.machine, Commentf("#%d Wrong machine id value", i)) c.Assert(v.id.Pid(), Equals, v.pid, Commentf("#%d Wrong pid value", i)) c.Assert(v.id.Counter(), Equals, v.counter, Commentf("#%d Wrong counter value", i)) } } func (s *S) TestNow(c *C) { before := time.Now() time.Sleep(1e6) now := bson.Now() time.Sleep(1e6) after := time.Now() c.Assert(now.After(before) && now.Before(after), Equals, true, Commentf("now=%s, before=%s, after=%s", now, before, after)) } // -------------------------------------------------------------------------- // ObjectId generation tests. func (s *S) TestNewObjectId(c *C) { // Generate 10 ids ids := make([]bson.ObjectId, 10) for i := 0; i < 10; i++ { ids[i] = bson.NewObjectId() } for i := 1; i < 10; i++ { prevId := ids[i-1] id := ids[i] // Test for uniqueness among all other 9 generated ids for j, tid := range ids { if j != i { c.Assert(id, Not(Equals), tid, Commentf("Generated ObjectId is not unique")) } } // Check that timestamp was incremented and is within 30 seconds of the previous one secs := id.Time().Sub(prevId.Time()).Seconds() c.Assert((secs >= 0 && secs <= 30), Equals, true, Commentf("Wrong timestamp in generated ObjectId")) // Check that machine ids are the same c.Assert(id.Machine(), DeepEquals, prevId.Machine()) // Check that pids are the same c.Assert(id.Pid(), Equals, prevId.Pid()) // Test for proper increment delta := int(id.Counter() - prevId.Counter()) c.Assert(delta, Equals, 1, Commentf("Wrong increment in generated ObjectId")) } } func (s *S) TestNewObjectIdWithTime(c *C) { t := time.Unix(12345678, 0) id := bson.NewObjectIdWithTime(t) c.Assert(id.Time(), Equals, t) c.Assert(id.Machine(), DeepEquals, []byte{0x00, 0x00, 0x00}) c.Assert(int(id.Pid()), Equals, 0) c.Assert(int(id.Counter()), Equals, 0) } // -------------------------------------------------------------------------- // ObjectId JSON marshalling. type jsonType struct { Id *bson.ObjectId } func (s *S) TestObjectIdJSONMarshaling(c *C) { id := bson.ObjectIdHex("4d88e15b60f486e428412dc9") v := jsonType{Id: &id} data, err := json.Marshal(&v) c.Assert(err, IsNil) c.Assert(string(data), Equals, `{"Id":"4d88e15b60f486e428412dc9"}`) } func (s *S) TestObjectIdJSONUnmarshaling(c *C) { data := []byte(`{"Id":"4d88e15b60f486e428412dc9"}`) v := jsonType{} err := json.Unmarshal(data, &v) c.Assert(err, IsNil) c.Assert(*v.Id, Equals, bson.ObjectIdHex("4d88e15b60f486e428412dc9")) } func (s *S) TestObjectIdJSONUnmarshalingError(c *C) { v := jsonType{} err := json.Unmarshal([]byte(`{"Id":"4d88e15b60f486e428412dc9A"}`), &v) c.Assert(err, ErrorMatches, `Invalid ObjectId in JSON: "4d88e15b60f486e428412dc9A"`) err = json.Unmarshal([]byte(`{"Id":"4d88e15b60f486e428412dcZ"}`), &v) c.Assert(err, ErrorMatches, `Invalid ObjectId in JSON: "4d88e15b60f486e428412dcZ" .*`) } // -------------------------------------------------------------------------- // Some simple benchmarks. type BenchT struct { A, B, C, D, E, F string } func BenchmarkUnmarhsalStruct(b *testing.B) { v := BenchT{A: "A", D: "D", E: "E"} data, err := bson.Marshal(&v) if err != nil { panic(err) } b.ResetTimer() for i := 0; i < b.N; i++ { err = bson.Unmarshal(data, &v) } if err != nil { panic(err) } } func BenchmarkUnmarhsalMap(b *testing.B) { m := bson.M{"a": "a", "d": "d", "e": "e"} data, err := bson.Marshal(&m) if err != nil { panic(err) } b.ResetTimer() for i := 0; i < b.N; i++ { err = bson.Unmarshal(data, &m) } if err != nil { panic(err) } } juju-core_1.18.1/src/labix.org/v2/mgo/bson/encode.go0000644000015300001610000002561212321736015021762 0ustar jenkinsjenkins// BSON library for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // gobson - BSON library for Go. package bson import ( "fmt" "math" "net/url" "reflect" "strconv" "time" ) // -------------------------------------------------------------------------- // Some internal infrastructure. var ( typeBinary = reflect.TypeOf(Binary{}) typeObjectId = reflect.TypeOf(ObjectId("")) typeSymbol = reflect.TypeOf(Symbol("")) typeMongoTimestamp = reflect.TypeOf(MongoTimestamp(0)) typeOrderKey = reflect.TypeOf(MinKey) typeDocElem = reflect.TypeOf(DocElem{}) typeRawDocElem = reflect.TypeOf(RawDocElem{}) typeRaw = reflect.TypeOf(Raw{}) typeURL = reflect.TypeOf(url.URL{}) typeTime = reflect.TypeOf(time.Time{}) ) const itoaCacheSize = 32 var itoaCache []string func init() { itoaCache = make([]string, itoaCacheSize) for i := 0; i != itoaCacheSize; i++ { itoaCache[i] = strconv.Itoa(i) } } func itoa(i int) string { if i < itoaCacheSize { return itoaCache[i] } return strconv.Itoa(i) } // -------------------------------------------------------------------------- // Marshaling of the document value itself. type encoder struct { out []byte } func (e *encoder) addDoc(v reflect.Value) { for { if vi, ok := v.Interface().(Getter); ok { getv, err := vi.GetBSON() if err != nil { panic(err) } v = reflect.ValueOf(getv) continue } if v.Kind() == reflect.Ptr { v = v.Elem() continue } break } if v.Type() == typeRaw { raw := v.Interface().(Raw) if raw.Kind != 0x03 && raw.Kind != 0x00 { panic("Attempted to unmarshal Raw kind " + strconv.Itoa(int(raw.Kind)) + " as a document") } e.addBytes(raw.Data...) return } start := e.reserveInt32() switch v.Kind() { case reflect.Map: e.addMap(v) case reflect.Struct: e.addStruct(v) case reflect.Array, reflect.Slice: e.addSlice(v) default: panic("Can't marshal " + v.Type().String() + " as a BSON document") } e.addBytes(0) e.setInt32(start, int32(len(e.out)-start)) } func (e *encoder) addMap(v reflect.Value) { for _, k := range v.MapKeys() { e.addElem(k.String(), v.MapIndex(k), false) } } func (e *encoder) addStruct(v reflect.Value) { sinfo, err := getStructInfo(v.Type()) if err != nil { panic(err) } var value reflect.Value if sinfo.InlineMap >= 0 { m := v.Field(sinfo.InlineMap) if m.Len() > 0 { for _, k := range m.MapKeys() { ks := k.String() if _, found := sinfo.FieldsMap[ks]; found { panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", ks)) } e.addElem(ks, m.MapIndex(k), false) } } } for _, info := range sinfo.FieldsList { if info.Inline == nil { value = v.Field(info.Num) } else { value = v.FieldByIndex(info.Inline) } if info.OmitEmpty && isZero(value) { continue } e.addElem(info.Key, value, info.MinSize) } } func isZero(v reflect.Value) bool { switch v.Kind() { case reflect.String: return len(v.String()) == 0 case reflect.Ptr, reflect.Interface: return v.IsNil() case reflect.Slice: return v.Len() == 0 case reflect.Map: return v.Len() == 0 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Bool: return !v.Bool() case reflect.Struct: if v.Type() == typeTime { return v.Interface().(time.Time).IsZero() } for i := v.NumField()-1; i >= 0; i-- { if !isZero(v.Field(i)) { return false } } return true } return false } func (e *encoder) addSlice(v reflect.Value) { vi := v.Interface() if d, ok := vi.(D); ok { for _, elem := range d { e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) } return } if d, ok := vi.(RawD); ok { for _, elem := range d { e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) } return } l := v.Len() et := v.Type().Elem() if et == typeDocElem { for i := 0; i < l; i++ { elem := v.Index(i).Interface().(DocElem) e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) } return } if et == typeRawDocElem { for i := 0; i < l; i++ { elem := v.Index(i).Interface().(RawDocElem) e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) } return } for i := 0; i < l; i++ { e.addElem(itoa(i), v.Index(i), false) } } // -------------------------------------------------------------------------- // Marshaling of elements in a document. func (e *encoder) addElemName(kind byte, name string) { e.addBytes(kind) e.addBytes([]byte(name)...) e.addBytes(0) } func (e *encoder) addElem(name string, v reflect.Value, minSize bool) { if !v.IsValid() { e.addElemName('\x0A', name) return } if getter, ok := v.Interface().(Getter); ok { getv, err := getter.GetBSON() if err != nil { panic(err) } e.addElem(name, reflect.ValueOf(getv), minSize) return } switch v.Kind() { case reflect.Interface: e.addElem(name, v.Elem(), minSize) case reflect.Ptr: e.addElem(name, v.Elem(), minSize) case reflect.String: s := v.String() switch v.Type() { case typeObjectId: if len(s) != 12 { panic("ObjectIDs must be exactly 12 bytes long (got " + strconv.Itoa(len(s)) + ")") } e.addElemName('\x07', name) e.addBytes([]byte(s)...) case typeSymbol: e.addElemName('\x0E', name) e.addStr(s) default: e.addElemName('\x02', name) e.addStr(s) } case reflect.Float32, reflect.Float64: e.addElemName('\x01', name) e.addInt64(int64(math.Float64bits(v.Float()))) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: u := v.Uint() if int64(u) < 0 { panic("BSON has no uint64 type, and value is too large to fit correctly in an int64") } else if u <= math.MaxInt32 && (minSize || v.Kind() <= reflect.Uint32) { e.addElemName('\x10', name) e.addInt32(int32(u)) } else { e.addElemName('\x12', name) e.addInt64(int64(u)) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: switch v.Type() { case typeMongoTimestamp: e.addElemName('\x11', name) e.addInt64(v.Int()) case typeOrderKey: if v.Int() == int64(MaxKey) { e.addElemName('\x7F', name) } else { e.addElemName('\xFF', name) } default: i := v.Int() if (minSize || v.Type().Kind() != reflect.Int64) && i >= math.MinInt32 && i <= math.MaxInt32 { // It fits into an int32, encode as such. e.addElemName('\x10', name) e.addInt32(int32(i)) } else { e.addElemName('\x12', name) e.addInt64(i) } } case reflect.Bool: e.addElemName('\x08', name) if v.Bool() { e.addBytes(1) } else { e.addBytes(0) } case reflect.Map: e.addElemName('\x03', name) e.addDoc(v) case reflect.Slice: vt := v.Type() et := vt.Elem() if et.Kind() == reflect.Uint8 { e.addElemName('\x05', name) e.addBinary('\x00', v.Bytes()) } else if et == typeDocElem || et == typeRawDocElem { e.addElemName('\x03', name) e.addDoc(v) } else { e.addElemName('\x04', name) e.addDoc(v) } case reflect.Array: et := v.Type().Elem() if et.Kind() == reflect.Uint8 { e.addElemName('\x05', name) e.addBinary('\x00', v.Slice(0, v.Len()).Interface().([]byte)) } else { e.addElemName('\x04', name) e.addDoc(v) } case reflect.Struct: switch s := v.Interface().(type) { case Raw: kind := s.Kind if kind == 0x00 { kind = 0x03 } e.addElemName(kind, name) e.addBytes(s.Data...) case Binary: e.addElemName('\x05', name) e.addBinary(s.Kind, s.Data) case RegEx: e.addElemName('\x0B', name) e.addCStr(s.Pattern) e.addCStr(s.Options) case JavaScript: if s.Scope == nil { e.addElemName('\x0D', name) e.addStr(s.Code) } else { e.addElemName('\x0F', name) start := e.reserveInt32() e.addStr(s.Code) e.addDoc(reflect.ValueOf(s.Scope)) e.setInt32(start, int32(len(e.out)-start)) } case time.Time: // MongoDB handles timestamps as milliseconds. e.addElemName('\x09', name) e.addInt64(s.Unix() * 1000 + int64(s.Nanosecond() / 1e6)) case url.URL: e.addElemName('\x02', name) e.addStr(s.String()) case undefined: e.addElemName('\x06', name) default: e.addElemName('\x03', name) e.addDoc(v) } default: panic("Can't marshal " + v.Type().String() + " in a BSON document") } } // -------------------------------------------------------------------------- // Marshaling of base types. func (e *encoder) addBinary(subtype byte, v []byte) { if subtype == 0x02 { // Wonder how that brilliant idea came to life. Obsolete, luckily. e.addInt32(int32(len(v) + 4)) e.addBytes(subtype) e.addInt32(int32(len(v))) } else { e.addInt32(int32(len(v))) e.addBytes(subtype) } e.addBytes(v...) } func (e *encoder) addStr(v string) { e.addInt32(int32(len(v) + 1)) e.addCStr(v) } func (e *encoder) addCStr(v string) { e.addBytes([]byte(v)...) e.addBytes(0) } func (e *encoder) reserveInt32() (pos int) { pos = len(e.out) e.addBytes(0, 0, 0, 0) return pos } func (e *encoder) setInt32(pos int, v int32) { e.out[pos+0] = byte(v) e.out[pos+1] = byte(v >> 8) e.out[pos+2] = byte(v >> 16) e.out[pos+3] = byte(v >> 24) } func (e *encoder) addInt32(v int32) { u := uint32(v) e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24)) } func (e *encoder) addInt64(v int64) { u := uint64(v) e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24), byte(u>>32), byte(u>>40), byte(u>>48), byte(u>>56)) } func (e *encoder) addBytes(v ...byte) { e.out = append(e.out, v...) } juju-core_1.18.1/src/labix.org/v2/mgo/bson/decode.go0000644000015300001610000004261112321736015021746 0ustar jenkinsjenkins// BSON library for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // gobson - BSON library for Go. package bson import ( "fmt" "math" "net/url" "reflect" "sync" "time" ) type decoder struct { in []byte i int docType reflect.Type } var typeM = reflect.TypeOf(M{}) func newDecoder(in []byte) *decoder { return &decoder{in, 0, typeM} } // -------------------------------------------------------------------------- // Some helper functions. func corrupted() { panic("Document is corrupted") } func settableValueOf(i interface{}) reflect.Value { v := reflect.ValueOf(i) sv := reflect.New(v.Type()).Elem() sv.Set(v) return sv } // -------------------------------------------------------------------------- // Unmarshaling of documents. const ( setterUnknown = iota setterNone setterType setterAddr ) var setterStyle map[reflect.Type]int var setterIface reflect.Type var setterMutex sync.RWMutex func init() { var iface Setter setterIface = reflect.TypeOf(&iface).Elem() setterStyle = make(map[reflect.Type]int) } func getSetter(outt reflect.Type, out reflect.Value) Setter { setterMutex.RLock() style := setterStyle[outt] setterMutex.RUnlock() if style == setterNone { return nil } if style == setterUnknown { setterMutex.Lock() defer setterMutex.Unlock() if outt.Implements(setterIface) { setterStyle[outt] = setterType } else if reflect.PtrTo(outt).Implements(setterIface) { setterStyle[outt] = setterAddr } else { setterStyle[outt] = setterNone return nil } style = setterStyle[outt] } if style == setterAddr { if !out.CanAddr() { return nil } out = out.Addr() } else if outt.Kind() == reflect.Ptr && out.IsNil() { out.Set(reflect.New(outt.Elem())) } return out.Interface().(Setter) } func clearMap(m reflect.Value) { var none reflect.Value for _, k := range m.MapKeys() { m.SetMapIndex(k, none) } } func (d *decoder) readDocTo(out reflect.Value) { var elemType reflect.Type outt := out.Type() outk := outt.Kind() for { if outk == reflect.Ptr && out.IsNil() { out.Set(reflect.New(outt.Elem())) } if setter := getSetter(outt, out); setter != nil { var raw Raw d.readDocTo(reflect.ValueOf(&raw)) err := setter.SetBSON(raw) if _, ok := err.(*TypeError); err != nil && !ok { panic(err) } return } if outk == reflect.Ptr { out = out.Elem() outt = out.Type() outk = out.Kind() continue } break } var fieldsMap map[string]fieldInfo var inlineMap reflect.Value start := d.i origout := out if outk == reflect.Interface { if d.docType.Kind() == reflect.Map { mv := reflect.MakeMap(d.docType) out.Set(mv) out = mv } else { dv := reflect.New(d.docType).Elem() out.Set(dv) out = dv } outt = out.Type() outk = outt.Kind() } docType := d.docType switch outk { case reflect.Map: if outt.Key().Kind() != reflect.String { panic("BSON map must have string keys. Got: " + outt.String()) } elemType = outt.Elem() if elemType == typeIface { d.docType = outt } if out.IsNil() { out.Set(reflect.MakeMap(out.Type())) } else if out.Len() > 0 { clearMap(out) } case reflect.Struct: if outt != typeRaw { sinfo, err := getStructInfo(out.Type()) if err != nil { panic(err) } fieldsMap = sinfo.FieldsMap out.Set(sinfo.Zero) if sinfo.InlineMap != -1 { inlineMap = out.Field(sinfo.InlineMap) if !inlineMap.IsNil() && inlineMap.Len() > 0 { clearMap(inlineMap) } elemType = inlineMap.Type().Elem() if elemType == typeIface { d.docType = inlineMap.Type() } } } case reflect.Slice: switch outt.Elem() { case typeDocElem: origout.Set(d.readDocElems(outt)) return case typeRawDocElem: origout.Set(d.readRawDocElems(outt)) return } fallthrough default: panic("Unsupported document type for unmarshalling: " + out.Type().String()) } end := int(d.readInt32()) end += d.i - 4 if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { corrupted() } for d.in[d.i] != '\x00' { kind := d.readByte() name := d.readCStr() if d.i >= end { corrupted() } switch outk { case reflect.Map: e := reflect.New(elemType).Elem() if d.readElemTo(e, kind) { out.SetMapIndex(reflect.ValueOf(name), e) } case reflect.Struct: if outt == typeRaw { d.dropElem(kind) } else { if info, ok := fieldsMap[name]; ok { if info.Inline == nil { d.readElemTo(out.Field(info.Num), kind) } else { d.readElemTo(out.FieldByIndex(info.Inline), kind) } } else if inlineMap.IsValid() { if inlineMap.IsNil() { inlineMap.Set(reflect.MakeMap(inlineMap.Type())) } e := reflect.New(elemType).Elem() if d.readElemTo(e, kind) { inlineMap.SetMapIndex(reflect.ValueOf(name), e) } } else { d.dropElem(kind) } } case reflect.Slice: } if d.i >= end { corrupted() } } d.i++ // '\x00' if d.i != end { corrupted() } d.docType = docType if outt == typeRaw { out.Set(reflect.ValueOf(Raw{0x03, d.in[start:d.i]})) } } func (d *decoder) readArrayDocTo(out reflect.Value) { end := int(d.readInt32()) end += d.i - 4 if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { corrupted() } i := 0 l := out.Len() for d.in[d.i] != '\x00' { if i >= l { panic("Length mismatch on array field") } kind := d.readByte() for d.i < end && d.in[d.i] != '\x00' { d.i++ } if d.i >= end { corrupted() } d.i++ d.readElemTo(out.Index(i), kind) if d.i >= end { corrupted() } i++ } if i != l { panic("Length mismatch on array field") } d.i++ // '\x00' if d.i != end { corrupted() } } func (d *decoder) readSliceDoc(t reflect.Type) interface{} { tmp := make([]reflect.Value, 0, 8) elemType := t.Elem() end := int(d.readInt32()) end += d.i - 4 if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { corrupted() } for d.in[d.i] != '\x00' { kind := d.readByte() for d.i < end && d.in[d.i] != '\x00' { d.i++ } if d.i >= end { corrupted() } d.i++ e := reflect.New(elemType).Elem() if d.readElemTo(e, kind) { tmp = append(tmp, e) } if d.i >= end { corrupted() } } d.i++ // '\x00' if d.i != end { corrupted() } n := len(tmp) slice := reflect.MakeSlice(t, n, n) for i := 0; i != n; i++ { slice.Index(i).Set(tmp[i]) } return slice.Interface() } var typeSlice = reflect.TypeOf([]interface{}{}) var typeIface = typeSlice.Elem() func (d *decoder) readDocElems(typ reflect.Type) reflect.Value { docType := d.docType d.docType = typ slice := make([]DocElem, 0, 8) d.readDocWith(func(kind byte, name string) { e := DocElem{Name: name} v := reflect.ValueOf(&e.Value) if d.readElemTo(v.Elem(), kind) { slice = append(slice, e) } }) slicev := reflect.New(typ).Elem() slicev.Set(reflect.ValueOf(slice)) d.docType = docType return slicev } func (d *decoder) readRawDocElems(typ reflect.Type) reflect.Value { docType := d.docType d.docType = typ slice := make([]RawDocElem, 0, 8) d.readDocWith(func(kind byte, name string) { e := RawDocElem{Name: name} v := reflect.ValueOf(&e.Value) if d.readElemTo(v.Elem(), kind) { slice = append(slice, e) } }) slicev := reflect.New(typ).Elem() slicev.Set(reflect.ValueOf(slice)) d.docType = docType return slicev } func (d *decoder) readDocWith(f func(kind byte, name string)) { end := int(d.readInt32()) end += d.i - 4 if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { corrupted() } for d.in[d.i] != '\x00' { kind := d.readByte() name := d.readCStr() if d.i >= end { corrupted() } f(kind, name) if d.i >= end { corrupted() } } d.i++ // '\x00' if d.i != end { corrupted() } } // -------------------------------------------------------------------------- // Unmarshaling of individual elements within a document. var blackHole = settableValueOf(struct{}{}) func (d *decoder) dropElem(kind byte) { d.readElemTo(blackHole, kind) } // Attempt to decode an element from the document and put it into out. // If the types are not compatible, the returned ok value will be // false and out will be unchanged. func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) { start := d.i if kind == '\x03' { // Special case for documents. Delegate to readDocTo(). switch out.Kind() { case reflect.Interface, reflect.Ptr, reflect.Struct, reflect.Map: d.readDocTo(out) default: switch out.Interface().(type) { case D: out.Set(d.readDocElems(out.Type())) case RawD: out.Set(d.readRawDocElems(out.Type())) default: d.readDocTo(blackHole) } } return true } var in interface{} switch kind { case 0x01: // Float64 in = d.readFloat64() case 0x02: // UTF-8 string in = d.readStr() case 0x03: // Document panic("Can't happen. Handled above.") case 0x04: // Array outt := out.Type() for outt.Kind() == reflect.Ptr { outt = outt.Elem() } switch outt.Kind() { case reflect.Array: d.readArrayDocTo(out) return true case reflect.Slice: in = d.readSliceDoc(outt) default: in = d.readSliceDoc(typeSlice) } case 0x05: // Binary b := d.readBinary() if b.Kind == 0x00 || b.Kind == 0x02 { in = b.Data } else { in = b } case 0x06: // Undefined (obsolete, but still seen in the wild) in = Undefined case 0x07: // ObjectId in = ObjectId(d.readBytes(12)) case 0x08: // Bool in = d.readBool() case 0x09: // Timestamp // MongoDB handles timestamps as milliseconds. i := d.readInt64() if i == -62135596800000 { in = time.Time{} // In UTC for convenience. } else { in = time.Unix(i/1e3, i%1e3*1e6) } case 0x0A: // Nil in = nil case 0x0B: // RegEx in = d.readRegEx() case 0x0D: // JavaScript without scope in = JavaScript{Code: d.readStr()} case 0x0E: // Symbol in = Symbol(d.readStr()) case 0x0F: // JavaScript with scope d.i += 4 // Skip length js := JavaScript{d.readStr(), make(M)} d.readDocTo(reflect.ValueOf(js.Scope)) in = js case 0x10: // Int32 in = int(d.readInt32()) case 0x11: // Mongo-specific timestamp in = MongoTimestamp(d.readInt64()) case 0x12: // Int64 in = d.readInt64() case 0x7F: // Max key in = MaxKey case 0xFF: // Min key in = MinKey default: panic(fmt.Sprintf("Unknown element kind (0x%02X)", kind)) } outt := out.Type() if outt == typeRaw { out.Set(reflect.ValueOf(Raw{kind, d.in[start:d.i]})) return true } if setter := getSetter(outt, out); setter != nil { err := setter.SetBSON(Raw{kind, d.in[start:d.i]}) if err == SetZero { out.Set(reflect.Zero(outt)) return true } if err == nil { return true } if _, ok := err.(*TypeError); !ok { panic(err) } return false } if in == nil { out.Set(reflect.Zero(outt)) return true } outk := outt.Kind() // Dereference and initialize pointer if necessary. first := true for outk == reflect.Ptr { if !out.IsNil() { out = out.Elem() } else { elem := reflect.New(outt.Elem()) if first { // Only set if value is compatible. first = false defer func(out, elem reflect.Value) { if good { out.Set(elem) } }(out, elem) } else { out.Set(elem) } out = elem } outt = out.Type() outk = outt.Kind() } inv := reflect.ValueOf(in) if outt == inv.Type() { out.Set(inv) return true } switch outk { case reflect.Interface: out.Set(inv) return true case reflect.String: switch inv.Kind() { case reflect.String: out.SetString(inv.String()) return true case reflect.Slice: if b, ok := in.([]byte); ok { out.SetString(string(b)) return true } } case reflect.Slice, reflect.Array: // Remember, array (0x04) slices are built with the correct // element type. If we are here, must be a cross BSON kind // conversion (e.g. 0x05 unmarshalling on string). if outt.Elem().Kind() != reflect.Uint8 { break } switch inv.Kind() { case reflect.String: slice := []byte(inv.String()) out.Set(reflect.ValueOf(slice)) return true case reflect.Slice: switch outt.Kind() { case reflect.Array: reflect.Copy(out, inv) case reflect.Slice: out.SetBytes(inv.Bytes()) } return true } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: switch inv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: out.SetInt(inv.Int()) return true case reflect.Float32, reflect.Float64: out.SetInt(int64(inv.Float())) return true case reflect.Bool: if inv.Bool() { out.SetInt(1) } else { out.SetInt(0) } return true case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: panic("Can't happen. No uint types in BSON?") } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: switch inv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: out.SetUint(uint64(inv.Int())) return true case reflect.Float32, reflect.Float64: out.SetUint(uint64(inv.Float())) return true case reflect.Bool: if inv.Bool() { out.SetUint(1) } else { out.SetUint(0) } return true case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: panic("Can't happen. No uint types in BSON.") } case reflect.Float32, reflect.Float64: switch inv.Kind() { case reflect.Float32, reflect.Float64: out.SetFloat(inv.Float()) return true case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: out.SetFloat(float64(inv.Int())) return true case reflect.Bool: if inv.Bool() { out.SetFloat(1) } else { out.SetFloat(0) } return true case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: panic("Can't happen. No uint types in BSON?") } case reflect.Bool: switch inv.Kind() { case reflect.Bool: out.SetBool(inv.Bool()) return true case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: out.SetBool(inv.Int() != 0) return true case reflect.Float32, reflect.Float64: out.SetBool(inv.Float() != 0) return true case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: panic("Can't happen. No uint types in BSON?") } case reflect.Struct: if outt == typeURL && inv.Kind() == reflect.String { u, err := url.Parse(inv.String()) if err != nil { panic(err) } out.Set(reflect.ValueOf(u).Elem()) return true } } return false } // -------------------------------------------------------------------------- // Parsers of basic types. func (d *decoder) readRegEx() RegEx { re := RegEx{} re.Pattern = d.readCStr() re.Options = d.readCStr() return re } func (d *decoder) readBinary() Binary { l := d.readInt32() b := Binary{} b.Kind = d.readByte() b.Data = d.readBytes(l) if b.Kind == 0x02 && len(b.Data) >= 4 { // Weird obsolete format with redundant length. b.Data = b.Data[4:] } return b } func (d *decoder) readStr() string { l := d.readInt32() b := d.readBytes(l - 1) if d.readByte() != '\x00' { corrupted() } return string(b) } func (d *decoder) readCStr() string { start := d.i end := start l := len(d.in) for ; end != l; end++ { if d.in[end] == '\x00' { break } } d.i = end + 1 if d.i > l { corrupted() } return string(d.in[start:end]) } func (d *decoder) readBool() bool { if d.readByte() == 1 { return true } return false } func (d *decoder) readFloat64() float64 { return math.Float64frombits(uint64(d.readInt64())) } func (d *decoder) readInt32() int32 { b := d.readBytes(4) return int32((uint32(b[0]) << 0) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24)) } func (d *decoder) readInt64() int64 { b := d.readBytes(8) return int64((uint64(b[0]) << 0) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24) | (uint64(b[4]) << 32) | (uint64(b[5]) << 40) | (uint64(b[6]) << 48) | (uint64(b[7]) << 56)) } func (d *decoder) readByte() byte { i := d.i d.i++ if d.i > len(d.in) { corrupted() } return d.in[i] } func (d *decoder) readBytes(length int32) []byte { start := d.i d.i += int(length) if d.i > len(d.in) { corrupted() } return d.in[start : start+int(length)] } juju-core_1.18.1/src/labix.org/v2/mgo/txn/0000755000015300001610000000000012321736015020040 5ustar jenkinsjenkinsjuju-core_1.18.1/src/labix.org/v2/mgo/txn/txn_test.go0000644000015300001610000002132412321736015022241 0ustar jenkinsjenkinspackage txn_test import ( "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" . "launchpad.net/gocheck" "testing" ) func TestAll(t *testing.T) { TestingT(t) } type S struct { MgoSuite db *mgo.Database tc, sc *mgo.Collection accounts *mgo.Collection runner *txn.Runner } var _ = Suite(&S{}) type M map[string]interface{} func (s *S) SetUpTest(c *C) { txn.SetChaos(txn.Chaos{}) txn.SetLogger(c) txn.SetDebug(true) s.MgoSuite.SetUpTest(c) s.db = s.session.DB("test") s.tc = s.db.C("tc") s.sc = s.db.C("tc.stash") s.accounts = s.db.C("accounts") s.runner = txn.NewRunner(s.tc) } type Account struct { Id int `bson:"_id"` Balance int } func (s *S) TestDocExists(c *C) { err := s.accounts.Insert(M{"_id": 0, "balance": 300}) c.Assert(err, IsNil) exists := []txn.Op{{ C: "accounts", Id: 0, Assert: txn.DocExists, }} missing := []txn.Op{{ C: "accounts", Id: 0, Assert: txn.DocMissing, }} err = s.runner.Run(exists, "", nil) c.Assert(err, IsNil) err = s.runner.Run(missing, "", nil) c.Assert(err, Equals, txn.ErrAborted) err = s.accounts.RemoveId(0) c.Assert(err, IsNil) err = s.runner.Run(exists, "", nil) c.Assert(err, Equals, txn.ErrAborted) err = s.runner.Run(missing, "", nil) c.Assert(err, IsNil) } func (s *S) TestInsert(c *C) { err := s.accounts.Insert(M{"_id": 0, "balance": 300}) c.Assert(err, IsNil) ops := []txn.Op{{ C: "accounts", Id: 0, Insert: M{"balance": 200}, }} err = s.runner.Run(ops, "", nil) c.Assert(err, IsNil) var account Account err = s.accounts.FindId(0).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 300) ops[0].Id = 1 err = s.runner.Run(ops, "", nil) c.Assert(err, IsNil) err = s.accounts.FindId(1).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 200) } func (s *S) TestRemove(c *C) { err := s.accounts.Insert(M{"_id": 0, "balance": 300}) c.Assert(err, IsNil) ops := []txn.Op{{ C: "accounts", Id: 0, Remove: true, }} err = s.runner.Run(ops, "", nil) c.Assert(err, IsNil) err = s.accounts.FindId(0).One(nil) c.Assert(err, Equals, mgo.ErrNotFound) err = s.runner.Run(ops, "", nil) c.Assert(err, IsNil) } func (s *S) TestUpdate(c *C) { var err error err = s.accounts.Insert(M{"_id": 0, "balance": 200}) c.Assert(err, IsNil) err = s.accounts.Insert(M{"_id": 1, "balance": 200}) c.Assert(err, IsNil) ops := []txn.Op{{ C: "accounts", Id: 0, Update: M{"$inc": M{"balance": 100}}, }} err = s.runner.Run(ops, "", nil) c.Assert(err, IsNil) var account Account err = s.accounts.FindId(0).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 300) ops[0].Id = 1 err = s.accounts.FindId(1).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 200) } func (s *S) TestInsertUpdate(c *C) { ops := []txn.Op{{ C: "accounts", Id: 0, Insert: M{"_id": 0, "balance": 200}, }, { C: "accounts", Id: 0, Update: M{"$inc": M{"balance": 100}}, }} err := s.runner.Run(ops, "", nil) c.Assert(err, IsNil) var account Account err = s.accounts.FindId(0).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 300) err = s.runner.Run(ops, "", nil) c.Assert(err, IsNil) err = s.accounts.FindId(0).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 400) } func (s *S) TestUpdateInsert(c *C) { ops := []txn.Op{{ C: "accounts", Id: 0, Update: M{"$inc": M{"balance": 100}}, }, { C: "accounts", Id: 0, Insert: M{"_id": 0, "balance": 200}, }} err := s.runner.Run(ops, "", nil) c.Assert(err, IsNil) var account Account err = s.accounts.FindId(0).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 200) err = s.runner.Run(ops, "", nil) c.Assert(err, IsNil) err = s.accounts.FindId(0).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 300) } func (s *S) TestInsertRemoveInsert(c *C) { ops := []txn.Op{{ C: "accounts", Id: 0, Insert: M{"_id": 0, "balance": 200}, }, { C: "accounts", Id: 0, Remove: true, }, { C: "accounts", Id: 0, Insert: M{"_id": 0, "balance": 300}, }} err := s.runner.Run(ops, "", nil) c.Assert(err, IsNil) var account Account err = s.accounts.FindId(0).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 300) } func (s *S) TestQueueStashing(c *C) { txn.SetChaos(txn.Chaos{ KillChance: 1, Breakpoint: "set-applying", }) opses := [][]txn.Op{{{ C: "accounts", Id: 0, Insert: M{"balance": 100}, }}, {{ C: "accounts", Id: 0, Remove: true, }}, {{ C: "accounts", Id: 0, Insert: M{"balance": 200}, }}, {{ C: "accounts", Id: 0, Update: M{"$inc": M{"balance": 100}}, }}} var last bson.ObjectId for _, ops := range opses { last = bson.NewObjectId() err := s.runner.Run(ops, last, nil) c.Assert(err, Equals, txn.ErrChaos) } txn.SetChaos(txn.Chaos{}) err := s.runner.Resume(last) c.Assert(err, IsNil) var account Account err = s.accounts.FindId(0).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 300) } func (s *S) TestInfo(c *C) { ops := []txn.Op{{ C: "accounts", Id: 0, Assert: txn.DocMissing, }} id := bson.NewObjectId() err := s.runner.Run(ops, id, M{"n": 42}) c.Assert(err, IsNil) var t struct{ I struct{ N int } } err = s.tc.FindId(id).One(&t) c.Assert(err, IsNil) c.Assert(t.I.N, Equals, 42) } func (s *S) TestErrors(c *C) { doc := bson.M{"foo": 1} tests := []txn.Op{{ C: "c", Id: 0, }, { C: "c", Id: 0, Insert: doc, Remove: true, }, { C: "c", Id: 0, Insert: doc, Update: doc, }, { C: "c", Id: 0, Update: doc, Remove: true, }, { C: "c", Assert: doc, }, { Id: 0, Assert: doc, }} txn.SetChaos(txn.Chaos{KillChance: 1.0}) for _, op := range tests { c.Logf("op: %v", op) err := s.runner.Run([]txn.Op{op}, "", nil) c.Assert(err, ErrorMatches, "error in transaction op 0: .*") } } func (s *S) TestAssertNestedOr(c *C) { // Assert uses $or internally. Ensure nesting works. err := s.accounts.Insert(M{"_id": 0, "balance": 300}) c.Assert(err, IsNil) ops := []txn.Op{{ C: "accounts", Id: 0, Assert: bson.D{{"$or", []bson.D{{{"balance", 100}}, {{"balance", 300}}}}}, Update: bson.D{{"$inc", bson.D{{"balance", 100}}}}, }} err = s.runner.Run(ops, "", nil) c.Assert(err, IsNil) var account Account err = s.accounts.FindId(0).One(&account) c.Assert(err, IsNil) c.Assert(account.Balance, Equals, 400) } func (s *S) TestVerifyFieldOrdering(c *C) { // Used to have a map in certain operations, which means // the ordering of fields would be messed up. fields := bson.D{{"a", 1}, {"b", 2}, {"c", 3}} ops := []txn.Op{{ C: "accounts", Id: 0, Insert: fields, }} err := s.runner.Run(ops, "", nil) c.Assert(err, IsNil) var d bson.D err = s.accounts.FindId(0).One(&d) c.Assert(err, IsNil) var filtered bson.D for _, e := range d { switch e.Name { case "a", "b", "c": filtered = append(filtered, e) } } c.Assert(filtered, DeepEquals, fields) } func (s *S) TestChangeLog(c *C) { chglog := s.db.C("chglog") s.runner.ChangeLog(chglog) ops := []txn.Op{{ C: "debts", Id: 0, Assert: txn.DocMissing, }, { C: "accounts", Id: 0, Insert: M{"balance": 300}, }, { C: "accounts", Id: 1, Insert: M{"balance": 300}, }, { C: "people", Id: "joe", Insert: M{"accounts": []int64{0, 1}}, }} id := bson.NewObjectId() err := s.runner.Run(ops, id, nil) c.Assert(err, IsNil) type IdList []interface{} type Log struct { Docs IdList "d" Revnos []int64 "r" } var m map[string]*Log err = chglog.FindId(id).One(&m) c.Assert(err, IsNil) c.Assert(m["accounts"], DeepEquals, &Log{IdList{0, 1}, []int64{2, 2}}) c.Assert(m["people"], DeepEquals, &Log{IdList{"joe"}, []int64{2}}) c.Assert(m["debts"], IsNil) ops = []txn.Op{{ C: "accounts", Id: 0, Update: M{"$inc": M{"balance": 100}}, }, { C: "accounts", Id: 1, Update: M{"$inc": M{"balance": 100}}, }} id = bson.NewObjectId() err = s.runner.Run(ops, id, nil) c.Assert(err, IsNil) m = nil err = chglog.FindId(id).One(&m) c.Assert(err, IsNil) c.Assert(m["accounts"], DeepEquals, &Log{IdList{0, 1}, []int64{3, 3}}) c.Assert(m["people"], IsNil) ops = []txn.Op{{ C: "accounts", Id: 0, Remove: true, }, { C: "people", Id: "joe", Remove: true, }} id = bson.NewObjectId() err = s.runner.Run(ops, id, nil) c.Assert(err, IsNil) m = nil err = chglog.FindId(id).One(&m) c.Assert(err, IsNil) c.Assert(m["accounts"], DeepEquals, &Log{IdList{0}, []int64{-4}}) c.Assert(m["people"], DeepEquals, &Log{IdList{"joe"}, []int64{-3}}) } juju-core_1.18.1/src/labix.org/v2/mgo/txn/chaos.go0000644000015300001610000000263312321735660021475 0ustar jenkinsjenkinspackage txn import ( mrand "math/rand" "time" ) var chaosEnabled = false var chaosSetting Chaos // Chaos holds parameters for the failure injection mechanism. type Chaos struct { // KillChance is the 0.0 to 1.0 chance that a given checkpoint // within the algorithm will raise an interruption that will // stop the procedure. KillChance float64 // SlowdownChance is the 0.0 to 1.0 chance that a given checkpoint // within the algorithm will be delayed by Slowdown before // continuing. SlowdownChance float64 Slowdown time.Duration // If Breakpoint is set, the above settings will only affect the // named breakpoint. Breakpoint string } // SetChaos sets the failure injection parameters to c. func SetChaos(c Chaos) { chaosSetting = c chaosEnabled = c.KillChance > 0 || c.SlowdownChance > 0 } func chaos(bpname string) { if !chaosEnabled { return } switch chaosSetting.Breakpoint { case "", bpname: kc := chaosSetting.KillChance if kc > 0 && mrand.Intn(1000) < int(kc*1000) { panic(chaosError{}) } if bpname == "insert" { return } sc := chaosSetting.SlowdownChance if sc > 0 && mrand.Intn(1000) < int(kc*1000) { time.Sleep(chaosSetting.Slowdown) } } } type chaosError struct{} func (f *flusher) handleChaos(err *error) { v := recover() if v == nil { return } if _, ok := v.(chaosError); ok { f.debugf("Killed by chaos!") *err = ErrChaos return } panic(v) } juju-core_1.18.1/src/labix.org/v2/mgo/txn/txn.go0000644000015300001610000003041012321736015021176 0ustar jenkinsjenkins// The txn package implements support for multi-document transactions. // // For details check the following blog post: // // http://blog.labix.org/2012/08/22/multi-doc-transactions-for-mongodb // package txn import ( "encoding/binary" "fmt" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "reflect" "sort" "sync" crand "crypto/rand" mrand "math/rand" ) type state int const ( tpreparing state = 1 // One or more documents not prepared tprepared state = 2 // Prepared but not yet ready to run taborting state = 3 // Assertions failed, cleaning up tapplying state = 4 // Changes are in progress taborted state = 5 // Pre-conditions failed, nothing done tapplied state = 6 // All changes applied ) func (s state) String() string { switch s { case tpreparing: return "preparing" case tprepared: return "prepared" case taborting: return "aborting" case tapplying: return "applying" case taborted: return "aborted" case tapplied: return "applied" } panic(fmt.Errorf("unknown state: %d", s)) } var rand *mrand.Rand var randmu sync.Mutex func init() { var seed int64 err := binary.Read(crand.Reader, binary.BigEndian, &seed) if err != nil { panic(err) } rand = mrand.New(mrand.NewSource(seed)) } type transaction struct { Id bson.ObjectId `bson:"_id"` State state `bson:"s"` Info interface{} `bson:"i,omitempty"` Ops []Op `bson:"o"` Nonce string `bson:"n,omitempty"` Revnos []int64 `bson:"r,omitempty"` docKeysCached docKeys } func (t *transaction) String() string { if t.Nonce == "" { return t.Id.Hex() } return string(t.token()) } func (t *transaction) done() bool { return t.State == tapplied || t.State == taborted } func (t *transaction) token() token { if t.Nonce == "" { panic("transaction has no nonce") } return tokenFor(t) } func (t *transaction) docKeys() docKeys { if t.docKeysCached != nil { return t.docKeysCached } dkeys := make(docKeys, 0, len(t.Ops)) NextOp: for _, op := range t.Ops { dkey := op.docKey() for i := range dkeys { if dkey == dkeys[i] { continue NextOp } } dkeys = append(dkeys, dkey) } sort.Sort(dkeys) t.docKeysCached = dkeys return dkeys } // tokenFor returns a unique transaction token that // is composed by t's id and a nonce. If t already has // a nonce assigned to it, it will be used, otherwise // a new nonce will be generated. func tokenFor(t *transaction) token { nonce := t.Nonce if nonce == "" { nonce = newNonce() } return token(t.Id.Hex() + "_" + nonce) } func newNonce() string { randmu.Lock() r := rand.Uint32() randmu.Unlock() n := make([]byte, 8) for i := uint(0); i < 8; i++ { n[i] = "0123456789abcdef"[(r>>(4*i))&0xf] } return string(n) } type token string func (tt token) id() bson.ObjectId { return bson.ObjectIdHex(string(tt[:24])) } func (tt token) nonce() string { return string(tt[25:]) } // Op represents an operation to a single document that may be // applied as part of a transaction with other operations. type Op struct { // C and Id identify the collection and document this operation // refers to. Id is matched against the "_id" document field. C string `bson:"c"` Id interface{} `bson:"d"` // Assert optionally holds a query document that is used to // test the operation document at the time the transaction is // going to be applied. The assertions for all operations in // a transaction are tested before any changes take place, // and the transaction is entirely aborted if any of them // fails. This is also the only way to prevent a transaction // from being being applied (the transaction continues despite // the outcome of Insert, Update, and Remove). Assert interface{} `bson:"a,omitempty"` // The Insert, Update and Remove fields describe the mutation // intended by the operation. At most one of them may be set // per operation. If none are set, Assert must be set and the // operation becomes a read-only test. // // Insert holds the document to be inserted at the time the // transaction is applied. The Id field will be inserted // into the document automatically as its _id field. The // transaction will continue even if the document already // exists. Use Assert with txn.DocMissing if the insertion is // required. // // Update holds the update document to be applied at the time // the transaction is applied. The transaction will continue // even if a document with Id is missing. Use Assert to // test for the document presence or its contents. // // Remove indicates whether to remove the document with Id. // The transaction continues even if the document doesn't yet // exist at the time the transaction is applied. Use Assert // with txn.DocExists to make sure it will be removed. Insert interface{} `bson:"i,omitempty"` Update interface{} `bson:"u,omitempty"` Remove bool `bson:"r,omitempty"` } func (op *Op) isChange() bool { return op.Update != nil || op.Insert != nil || op.Remove } func (op *Op) docKey() docKey { return docKey{op.C, op.Id} } func (op *Op) name() string { switch { case op.Update != nil: return "update" case op.Insert != nil: return "insert" case op.Remove: return "remove" case op.Assert != nil: return "assert" } return "none" } const ( // DocExists and DocMissing may be used on an operation's // Assert value to assert that the document with the given // Id exists or does not exist, respectively. DocExists = "d+" DocMissing = "d-" ) // A Runner applies operations as part of a transaction onto any number // of collections within a database. See the Run method for details. type Runner struct { tc *mgo.Collection // txns sc *mgo.Collection // stash lc *mgo.Collection // log } // NewRunner returns a new transaction runner that uses tc to hold its // transactions. // // Multiple transaction collections may exist in a single database, but // all collections that are touched by operations in a given transaction // collection must be handled exclusively by it. // // A second collection with the same name of tc but suffixed by ".stash" // will be used for implementing the transactional behavior of insert // and remove operations. func NewRunner(tc *mgo.Collection) *Runner { return &Runner{tc, tc.Database.C(tc.Name + ".stash"), nil} } var ErrAborted = fmt.Errorf("transaction aborted") // Run creates a new transaction with ops and runs it immediately. // The id parameter specifies the transaction id, and may be written // down ahead of time to later verify the success of the change and // resume it, when the procedure is interrupted for any reason. If // empty, a random id will be generated. // The info parameter, if not nil, is included under the "i" // field of the transaction document. // // Operations across documents are not atomically applied, but are // guaranteed to be eventually all applied in the order provided or // all aborted, as long as the affected documents are only modified // through transactions. If documents are simultaneously modified // by transactions and out of transactions the behavior is undefined. // // If Run returns no errors, all operations were applied successfully. // If it returns ErrAborted, one or more operations can't be applied // and the transaction was entirely aborted with no changes performed. // Otherwise, if the transaction is interrupted while running for any // reason, it may be resumed explicitly or by attempting to apply // another transaction on any of the documents targeted by ops, as // long as the interruption was made after the transaction document // itself was inserted. Run Resume with the obtained transaction id // to confirm whether the transaction was applied or not. // // Any number of transactions may be run concurrently, with one // runner or many. func (r *Runner) Run(ops []Op, id bson.ObjectId, info interface{}) (err error) { const efmt = "error in transaction op %d: %s" for i := range ops { op := &ops[i] if op.C == "" || op.Id == nil { return fmt.Errorf(efmt, i, "C or Id missing") } changes := 0 if op.Insert != nil { changes++ } if op.Update != nil { changes++ } if op.Remove { changes++ } if changes > 1 { return fmt.Errorf(efmt, i, "more than one of Insert/Update/Remove set") } if changes == 0 && op.Assert == nil { return fmt.Errorf(efmt, i, "none of Assert/Insert/Update/Remove set") } } if id == "" { id = bson.NewObjectId() } // Insert transaction sooner rather than later, to stay on the safer side. t := transaction{ Id: id, Ops: ops, State: tpreparing, Info: info, } if err = r.tc.Insert(&t); err != nil { return err } if err = flush(r, &t); err != nil { return err } if t.State == taborted { return ErrAborted } else if t.State != tapplied { panic(fmt.Errorf("invalid state for %s after flush: %q", &t, t.State)) } return nil } // ResumeAll resumes all pending transactions. All ErrAborted errors // from individual transactions are ignored. func (r *Runner) ResumeAll() (err error) { debugf("Resuming all unfinished transactions") iter := r.tc.Find(bson.D{{"s", bson.D{{"$in", []state{tpreparing, tprepared, tapplying}}}}}).Iter() var t transaction for iter.Next(&t) { if t.State == tapplied || t.State == taborted { continue } debugf("Resuming %s from %q", t.Id, t.State) if err := flush(r, &t); err != nil { return err } if !t.done() { panic(fmt.Errorf("invalid state for %s after flush: %q", &t, t.State)) } } return nil } // Resume resumes the transaction with id. It returns mgo.ErrNotFound // if the transaction is not found. Otherwise, it has the same semantics // of the Run method after the transaction is inserted. func (r *Runner) Resume(id bson.ObjectId) (err error) { t, err := r.load(id) if err != nil { return err } if !t.done() { debugf("Resuming %s from %q", t, t.State) if err := flush(r, t); err != nil { return err } } if t.State == taborted { return ErrAborted } else if t.State != tapplied { panic(fmt.Errorf("invalid state for %s after flush: %q", t, t.State)) } return nil } // ChangeLog enables logging of changes to the given collection // every time a transaction that modifies content is done being // applied. // // Saved documents are in the format: // // {"_id": , : {"d": [, ...], "r": [, ...]}} // // The document revision is the value of the txn-revno field after // the change has been applied. Negative values indicate the document // was not present in the collection. Revisions will not change when // updates or removes are applied to missing documents or inserts are // attempted when the document isn't present. func (r *Runner) ChangeLog(logc *mgo.Collection) { r.lc = logc } func (r *Runner) load(id bson.ObjectId) (*transaction, error) { var t transaction err := r.tc.FindId(id).One(&t) if err == mgo.ErrNotFound { return nil, fmt.Errorf("cannot find transaction %s", id) } else if err != nil { return nil, err } return &t, nil } type docKey struct { C string Id interface{} } type docKeys []docKey func (ks docKeys) Len() int { return len(ks) } func (ks docKeys) Swap(i, j int) { ks[i], ks[j] = ks[j], ks[i] } func (ks docKeys) Less(i, j int) bool { a, b := ks[i], ks[j] if a.C != b.C { return a.C < b.C } av, an := valueNature(a.Id) bv, bn := valueNature(b.Id) if an != bn { return an < bn } switch an { case natureString: return av.(string) < bv.(string) case natureInt: return av.(int64) < bv.(int64) case natureFloat: return av.(float64) < bv.(float64) case natureBool: return !av.(bool) && bv.(bool) } panic("unreachable") } type typeNature int const ( // The order of these values matters. Transactions // from applications using different ordering will // be incompatible with each other. _ typeNature = iota natureString natureInt natureFloat natureBool ) func valueNature(v interface{}) (value interface{}, nature typeNature) { rv := reflect.ValueOf(v) switch rv.Kind() { case reflect.String: return rv.String(), natureString case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return rv.Int(), natureInt case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return int64(rv.Uint()), natureInt case reflect.Float32, reflect.Float64: return rv.Float(), natureFloat case reflect.Bool: return rv.Bool(), natureBool } panic("document id type unsupported by txn: " + rv.Kind().String()) } juju-core_1.18.1/src/labix.org/v2/mgo/txn/debug.go0000644000015300001610000000360212321736015021456 0ustar jenkinsjenkinspackage txn import ( "bytes" "fmt" "labix.org/v2/mgo/bson" "sort" "sync/atomic" ) var ( debugEnabled bool logger log_Logger ) type log_Logger interface { Output(calldepth int, s string) error } // Specify the *log.Logger where logged messages should be sent to. func SetLogger(l log_Logger) { logger = l } // SetDebug enables or disables debugging. func SetDebug(debug bool) { debugEnabled = debug } var ErrChaos = fmt.Errorf("interrupted by chaos") var debugId uint32 func debugPrefix() string { d := atomic.AddUint32(&debugId, 1) - 1 s := make([]byte, 0, 10) for i := uint(0); i < 8; i++ { s = append(s, "abcdefghijklmnop"[(d>>(4*i))&0xf]) if d>>(4*(i+1)) == 0 { break } } s = append(s, ')', ' ') return string(s) } func debugf(format string, args ...interface{}) { if !debugEnabled || logger == nil { return } for i, arg := range args { switch v := arg.(type) { case bson.ObjectId: args[i] = v.Hex() case []bson.ObjectId: lst := make([]string, len(v)) for j, id := range v { lst[j] = id.Hex() } args[i] = lst case map[docKey][]bson.ObjectId: buf := &bytes.Buffer{} var dkeys docKeys for dkey := range v { dkeys = append(dkeys, dkey) } sort.Sort(dkeys) for i, dkey := range dkeys { if i > 0 { buf.WriteByte(' ') } buf.WriteString(fmt.Sprintf("%v: {", dkey)) for j, id := range v[dkey] { if j > 0 { buf.WriteByte(' ') } buf.WriteString(id.Hex()) } buf.WriteByte('}') } args[i] = buf.String() case map[docKey][]int64: buf := &bytes.Buffer{} var dkeys docKeys for dkey := range v { dkeys = append(dkeys, dkey) } sort.Sort(dkeys) for i, dkey := range dkeys { if i > 0 { buf.WriteByte(' ') } buf.WriteString(fmt.Sprintf("%v: %v", dkey, v[dkey])) } args[i] = buf.String() } } logger.Output(2, fmt.Sprintf(format, args...)) } juju-core_1.18.1/src/labix.org/v2/mgo/txn/mgo_test.go0000644000015300001610000000352112321735660022216 0ustar jenkinsjenkinspackage txn_test import ( "bytes" "labix.org/v2/mgo" . "launchpad.net/gocheck" "os/exec" "time" ) // ---------------------------------------------------------------------------- // The mgo test suite type MgoSuite struct { output bytes.Buffer server *exec.Cmd session *mgo.Session } var mgoaddr = "127.0.0.1:50017" func (s *MgoSuite) SetUpSuite(c *C) { //mgo.SetDebug(true) mgo.SetStats(true) dbdir := c.MkDir() args := []string{ "--dbpath", dbdir, "--bind_ip", "127.0.0.1", "--port", "50017", "--nssize", "1", "--noprealloc", "--smallfiles", "--nojournal", "-vvvvv", } s.server = exec.Command("mongod", args...) s.server.Stdout = &s.output s.server.Stderr = &s.output err := s.server.Start() if err != nil { panic(err) } } func (s *MgoSuite) TearDownSuite(c *C) { s.server.Process.Kill() s.server.Process.Wait() } func (s *MgoSuite) SetUpTest(c *C) { err := DropAll(mgoaddr) if err != nil { panic(err) } mgo.SetLogger(c) mgo.ResetStats() s.session, err = mgo.Dial(mgoaddr) c.Assert(err, IsNil) } func (s *MgoSuite) TearDownTest(c *C) { if s.session != nil { s.session.Close() } for i := 0; ; i++ { stats := mgo.GetStats() if stats.SocketsInUse == 0 && stats.SocketsAlive == 0 { break } if i == 20 { c.Fatal("Test left sockets in a dirty state") } c.Logf("Waiting for sockets to die: %d in use, %d alive", stats.SocketsInUse, stats.SocketsAlive) time.Sleep(500 * time.Millisecond) } } func DropAll(mongourl string) (err error) { session, err := mgo.Dial(mongourl) if err != nil { return err } defer session.Close() names, err := session.DatabaseNames() if err != nil { return err } for _, name := range names { switch name { case "admin", "local", "config": default: err = session.DB(name).DropDatabase() if err != nil { return err } } } return nil } juju-core_1.18.1/src/labix.org/v2/mgo/txn/sim_test.go0000644000015300001610000002133112321735660022223 0ustar jenkinsjenkinspackage txn_test import ( "flag" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" . "launchpad.net/gocheck" "math/rand" "time" ) var ( duration = flag.Duration("duration", 200*time.Millisecond, "duration for each simulation") seed = flag.Int64("seed", 0, "seed for rand") ) type params struct { killChance float64 slowdownChance float64 slowdown time.Duration unsafe bool workers int accounts int changeHalf bool reinsertCopy bool reinsertZeroed bool changelog bool changes int } func (s *S) TestSim1Worker(c *C) { simulate(c, params{ workers: 1, accounts: 4, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, }) } func (s *S) TestSim4WorkersDense(c *C) { simulate(c, params{ workers: 4, accounts: 2, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, }) } func (s *S) TestSim4WorkersSparse(c *C) { simulate(c, params{ workers: 4, accounts: 10, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, }) } func (s *S) TestSimHalf1Worker(c *C) { simulate(c, params{ workers: 1, accounts: 4, changeHalf: true, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, }) } func (s *S) TestSimHalf4WorkersDense(c *C) { simulate(c, params{ workers: 4, accounts: 2, changeHalf: true, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, }) } func (s *S) TestSimHalf4WorkersSparse(c *C) { simulate(c, params{ workers: 4, accounts: 10, changeHalf: true, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, }) } func (s *S) TestSimReinsertCopy1Worker(c *C) { simulate(c, params{ workers: 1, accounts: 10, reinsertCopy: true, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, }) } func (s *S) TestSimReinsertCopy4Workers(c *C) { simulate(c, params{ workers: 4, accounts: 10, reinsertCopy: true, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, }) } func (s *S) TestSimReinsertZeroed1Worker(c *C) { simulate(c, params{ workers: 1, accounts: 10, reinsertZeroed: true, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, }) } func (s *S) TestSimReinsertZeroed4Workers(c *C) { simulate(c, params{ workers: 4, accounts: 10, reinsertZeroed: true, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, }) } func (s *S) TestSimChangeLog(c *C) { simulate(c, params{ workers: 4, accounts: 10, killChance: 0.01, slowdownChance: 0.3, slowdown: 100 * time.Millisecond, changelog: true, }) } type balanceChange struct { id bson.ObjectId origin int target int amount int } func simulate(c *C, params params) { seed := *seed if seed == 0 { seed = time.Now().UnixNano() } rand.Seed(seed) c.Logf("Seed: %v", seed) txn.SetChaos(txn.Chaos{ KillChance: params.killChance, SlowdownChance: params.slowdownChance, Slowdown: params.slowdown, }) defer txn.SetChaos(txn.Chaos{}) session, err := mgo.Dial(mgoaddr) c.Assert(err, IsNil) defer session.Close() db := session.DB("test") tc := db.C("tc") runner := txn.NewRunner(tc) tclog := db.C("tc.log") if params.changelog { info := mgo.CollectionInfo{ Capped: true, MaxBytes: 1000000, } err := tclog.Create(&info) c.Assert(err, IsNil) runner.ChangeLog(tclog) } accounts := db.C("accounts") for i := 0; i < params.accounts; i++ { err := accounts.Insert(M{"_id": i, "balance": 300}) c.Assert(err, IsNil) } var stop time.Time if params.changes <= 0 { stop = time.Now().Add(*duration) } max := params.accounts if params.reinsertCopy || params.reinsertZeroed { max = int(float64(params.accounts) * 1.5) } changes := make(chan balanceChange, 1024) //session.SetMode(mgo.Eventual, true) for i := 0; i < params.workers; i++ { go func() { n := 0 for { if n > 0 && n == params.changes { break } if !stop.IsZero() && time.Now().After(stop) { break } change := balanceChange{ id: bson.NewObjectId(), origin: rand.Intn(max), target: rand.Intn(max), amount: 100, } var old Account var oldExists bool if params.reinsertCopy || params.reinsertZeroed { if err := accounts.FindId(change.origin).One(&old); err != mgo.ErrNotFound { c.Check(err, IsNil) change.amount = old.Balance oldExists = true } } var ops []txn.Op switch { case params.reinsertCopy && oldExists: ops = []txn.Op{{ C: "accounts", Id: change.origin, Assert: M{"balance": change.amount}, Remove: true, }, { C: "accounts", Id: change.target, Assert: txn.DocMissing, Insert: M{"balance": change.amount}, }} case params.reinsertZeroed && oldExists: ops = []txn.Op{{ C: "accounts", Id: change.target, Assert: txn.DocMissing, Insert: M{"balance": 0}, }, { C: "accounts", Id: change.origin, Assert: M{"balance": change.amount}, Remove: true, }, { C: "accounts", Id: change.target, Assert: txn.DocExists, Update: M{"$inc": M{"balance": change.amount}}, }} case params.changeHalf: ops = []txn.Op{{ C: "accounts", Id: change.origin, Assert: M{"balance": M{"$gte": change.amount}}, Update: M{"$inc": M{"balance": -change.amount / 2}}, }, { C: "accounts", Id: change.target, Assert: txn.DocExists, Update: M{"$inc": M{"balance": change.amount / 2}}, }, { C: "accounts", Id: change.origin, Update: M{"$inc": M{"balance": -change.amount / 2}}, }, { C: "accounts", Id: change.target, Update: M{"$inc": M{"balance": change.amount / 2}}, }} default: ops = []txn.Op{{ C: "accounts", Id: change.origin, Assert: M{"balance": M{"$gte": change.amount}}, Update: M{"$inc": M{"balance": -change.amount}}, }, { C: "accounts", Id: change.target, Assert: txn.DocExists, Update: M{"$inc": M{"balance": change.amount}}, }} } err = runner.Run(ops, change.id, nil) if err != nil && err != txn.ErrAborted && err != txn.ErrChaos { c.Check(err, IsNil) } n++ changes <- change } changes <- balanceChange{} }() } alive := params.workers changeLog := make([]balanceChange, 0, 1024) for alive > 0 { change := <-changes if change.id == "" { alive-- } else { changeLog = append(changeLog, change) } } c.Check(len(changeLog), Not(Equals), 0, Commentf("No operations were even attempted.")) txn.SetChaos(txn.Chaos{}) err = runner.ResumeAll() c.Assert(err, IsNil) n, err := accounts.Count() c.Check(err, IsNil) c.Check(n, Equals, params.accounts, Commentf("Number of accounts has changed.")) n, err = accounts.Find(M{"balance": M{"$lt": 0}}).Count() c.Check(err, IsNil) c.Check(n, Equals, 0, Commentf("There are %d accounts with negative balance.", n)) globalBalance := 0 iter := accounts.Find(nil).Iter() account := Account{} for iter.Next(&account) { globalBalance += account.Balance } c.Check(iter.Close(), IsNil) c.Check(globalBalance, Equals, params.accounts*300, Commentf("Total amount of money should be constant.")) // Compute and verify the exact final state of all accounts. balance := make(map[int]int) for i := 0; i < params.accounts; i++ { balance[i] += 300 } var applied, aborted int for _, change := range changeLog { err := runner.Resume(change.id) if err == txn.ErrAborted { aborted++ continue } else if err != nil { c.Fatalf("resuming %s failed: %v", change.id, err) } balance[change.origin] -= change.amount balance[change.target] += change.amount applied++ } iter = accounts.Find(nil).Iter() for iter.Next(&account) { c.Assert(account.Balance, Equals, balance[account.Id]) } c.Check(iter.Close(), IsNil) c.Logf("Total transactions: %d (%d applied, %d aborted)", len(changeLog), applied, aborted) if params.changelog { n, err := tclog.Count() c.Assert(err, IsNil) // Check if the capped collection is full. dummy := make([]byte, 1024) tclog.Insert(M{"_id": bson.NewObjectId(), "dummy": dummy}) m, err := tclog.Count() c.Assert(err, IsNil) if m == n+1 { // Wasn't full, so it must have seen it all. c.Assert(err, IsNil) c.Assert(n, Equals, applied) } } } juju-core_1.18.1/src/labix.org/v2/mgo/txn/tarjan.go0000644000015300001610000000437412321735660021663 0ustar jenkinsjenkinspackage txn import ( "labix.org/v2/mgo/bson" "sort" ) func tarjanSort(successors map[bson.ObjectId][]bson.ObjectId) [][]bson.ObjectId { // http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm data := &tarjanData{ successors: successors, nodes: make([]tarjanNode, 0, len(successors)), index: make(map[bson.ObjectId]int, len(successors)), } // Sort all nodes to stabilize the logic. var all []string for id := range successors { all = append(all, string(id)) } sort.Strings(all) for _, strid := range all { id := bson.ObjectId(strid) if _, seen := data.index[id]; !seen { data.strongConnect(id) } } return data.output } type tarjanData struct { successors map[bson.ObjectId][]bson.ObjectId output [][]bson.ObjectId nodes []tarjanNode stack []bson.ObjectId index map[bson.ObjectId]int } type tarjanNode struct { lowlink int stacked bool } type idList []bson.ObjectId func (l idList) Len() int { return len(l) } func (l idList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l idList) Less(i, j int) bool { return l[i] < l[j] } func (data *tarjanData) strongConnect(id bson.ObjectId) *tarjanNode { index := len(data.nodes) data.index[id] = index data.stack = append(data.stack, id) data.nodes = append(data.nodes, tarjanNode{index, true}) node := &data.nodes[index] // Sort to stabilize the algorithm. succids := idList(data.successors[id]) sort.Sort(succids) for _, succid := range succids { succindex, seen := data.index[succid] if !seen { succnode := data.strongConnect(succid) if succnode.lowlink < node.lowlink { node.lowlink = succnode.lowlink } } else if data.nodes[succindex].stacked { // Part of the current strongly-connected component. if succindex < node.lowlink { node.lowlink = succindex } } } if node.lowlink == index { // Root node; pop stack and output new // strongly-connected component. var scc []bson.ObjectId i := len(data.stack) - 1 for { stackid := data.stack[i] stackindex := data.index[stackid] data.nodes[stackindex].stacked = false scc = append(scc, stackid) if stackindex == index { break } i-- } data.stack = data.stack[:i] data.output = append(data.output, scc) } return node } juju-core_1.18.1/src/labix.org/v2/mgo/txn/flusher.go0000644000015300001610000006460512321735660022057 0ustar jenkinsjenkinspackage txn import ( "fmt" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "sort" ) func flush(r *Runner, t *transaction) error { f := &flusher{ Runner: r, goal: t, goalKeys: make(map[docKey]bool), queue: make(map[docKey][]token), debugId: debugPrefix(), } for _, dkey := range f.goal.docKeys() { f.goalKeys[dkey] = true } return f.run() } type flusher struct { *Runner goal *transaction goalKeys map[docKey]bool queue map[docKey][]token debugId string } func (f *flusher) run() (err error) { if chaosEnabled { defer f.handleChaos(&err) } f.debugf("Processing %s", f.goal) seen := make(map[bson.ObjectId]*transaction) if err := f.recurse(f.goal, seen); err != nil { return err } if f.goal.done() { return nil } // Sparse workloads will generally be managed entirely by recurse. // Getting here means one or more transactions have dependencies // and perhaps cycles. // Build successors data for Tarjan's sort. Must consider // that entries in txn-queue are not necessarily valid. successors := make(map[bson.ObjectId][]bson.ObjectId) ready := true for _, dqueue := range f.queue { NextPair: for i := 0; i < len(dqueue); i++ { pred := dqueue[i] predid := pred.id() predt := seen[predid] if predt == nil || predt.Nonce != pred.nonce() { continue } predsuccids, ok := successors[predid] if !ok { successors[predid] = nil } for j := i + 1; j < len(dqueue); j++ { succ := dqueue[j] succid := succ.id() succt := seen[succid] if succt == nil || succt.Nonce != succ.nonce() { continue } if _, ok := successors[succid]; !ok { successors[succid] = nil } // Found a valid pred/succ pair. i = j - 1 for _, predsuccid := range predsuccids { if predsuccid == succid { continue NextPair } } successors[predid] = append(predsuccids, succid) if succid == f.goal.Id { // There are still pre-requisites to handle. ready = false } continue NextPair } } } f.debugf("Queues: %v", f.queue) f.debugf("Successors: %v", successors) if ready { f.debugf("Goal %s has no real pre-requisites", f.goal) return f.advance(f.goal, nil, true) } // Robert Tarjan's algorithm for detecting strongly-connected // components is used for topological sorting and detecting // cycles at once. The order in which transactions are applied // in commonly affected documents must be a global agreement. sorted := tarjanSort(successors) if debugEnabled { f.debugf("Tarjan output: %v", sorted) } pull := make(map[bson.ObjectId]*transaction) for i := len(sorted) - 1; i >= 0; i-- { scc := sorted[i] f.debugf("Flushing %v", scc) if len(scc) == 1 { pull[scc[0]] = seen[scc[0]] } for _, id := range scc { if err := f.advance(seen[id], pull, true); err != nil { return err } } if len(scc) > 1 { for _, id := range scc { pull[id] = seen[id] } } } return nil } func (f *flusher) recurse(t *transaction, seen map[bson.ObjectId]*transaction) error { seen[t.Id] = t err := f.advance(t, nil, false) if err != errPreReqs { return err } for _, dkey := range t.docKeys() { for _, dtt := range f.queue[dkey] { id := dtt.id() if seen[id] != nil { continue } qt, err := f.load(id) if err != nil { return err } err = f.recurse(qt, seen) if err != nil { return err } } } return nil } func (f *flusher) advance(t *transaction, pull map[bson.ObjectId]*transaction, force bool) error { for { switch t.State { case tpreparing, tprepared: revnos, err := f.prepare(t, force) if err != nil { return err } if t.State != tprepared { continue } if err = f.assert(t, revnos, pull); err != nil { return err } if t.State != tprepared { continue } if err = f.checkpoint(t, revnos); err != nil { return err } case tapplying: return f.apply(t, pull) case taborting: return f.abortOrReload(t, nil, pull) case tapplied, taborted: return nil default: panic(fmt.Errorf("transaction in unknown state: %q", t.State)) } } panic("unreachable") } type stash string const ( stashStable stash = "" stashInsert stash = "insert" stashRemove stash = "remove" ) type txnInfo struct { Queue []token `bson:"txn-queue"` Revno int64 `bson:"txn-revno,omitempty"` Insert bson.ObjectId `bson:"txn-insert,omitempty"` Remove bson.ObjectId `bson:"txn-remove,omitempty"` } type stashState string const ( stashNew stashState = "" stashInserting stashState = "inserting" ) var txnFields = bson.D{{"txn-queue", 1}, {"txn-revno", 1}, {"txn-remove", 1}, {"txn-insert", 1}} var errPreReqs = fmt.Errorf("transaction has pre-requisites and force is false") // prepare injects t's id onto txn-queue for all affected documents // and collects the current txn-queue and txn-revno values during // the process. If the prepared txn-queue indicates that there are // pre-requisite transactions to be applied and the force parameter // is false, errPreReqs will be returned. Otherwise, the current // tip revision numbers for all the documents are returned. func (f *flusher) prepare(t *transaction, force bool) (revnos []int64, err error) { if t.State != tpreparing { return f.rescan(t, force) } f.debugf("Preparing %s", t) // Iterate in a stable way across all runners. This isn't // strictly required, but reduces the chances of cycles. dkeys := t.docKeys() sort.Sort(dkeys) revno := make(map[docKey]int64) info := txnInfo{} tt := tokenFor(t) NextDoc: for _, dkey := range dkeys { change := mgo.Change{ Update: bson.D{{"$addToSet", bson.D{{"txn-queue", tt}}}}, ReturnNew: true, } c := f.tc.Database.C(dkey.C) cquery := c.FindId(dkey.Id).Select(txnFields) RetryDoc: change.Upsert = false chaos("") if _, err := cquery.Apply(change, &info); err == nil { if info.Remove == "" { // Fast path, unless workload is insert/remove heavy. revno[dkey] = info.Revno f.queue[dkey] = info.Queue f.debugf("[A] Prepared document %v with revno %d and queue: %v", dkey, info.Revno, info.Queue) continue NextDoc } else { // Handle remove in progress before preparing it. if err := f.loadAndApply(info.Remove); err != nil { return nil, err } goto RetryDoc } } else if err != mgo.ErrNotFound { return nil, err } // Document missing. Use stash collection. change.Upsert = true chaos("") _, err := f.sc.FindId(dkey).Apply(change, &info) if err != nil { return nil, err } if info.Insert != "" { // Handle insert in progress before preparing it. if err := f.loadAndApply(info.Insert); err != nil { return nil, err } goto RetryDoc } // Must confirm stash is still in use and is the same one // prepared, since applying a remove overwrites the stash. docFound := false stashFound := false if err = c.FindId(dkey.Id).Select(txnFields).One(&info); err == nil { docFound = true } else if err != mgo.ErrNotFound { return nil, err } else if err = f.sc.FindId(dkey).One(&info); err == nil { stashFound = true if info.Revno == 0 { // Missing revno in the stash only happens when it // has been upserted, in which case it defaults to -1. // Txn-inserted documents get revno -1 while in the stash // for the first time, and -revno-1 == 2 when they go live. info.Revno = -1 } } else if err != mgo.ErrNotFound { return nil, err } if docFound && info.Remove == "" || stashFound && info.Insert == "" { for _, dtt := range info.Queue { if dtt != tt { continue } // Found tt properly prepared. if stashFound { f.debugf("[B] Prepared document %v on stash with revno %d and queue: %v", dkey, info.Revno, info.Queue) } else { f.debugf("[B] Prepared document %v with revno %d and queue: %v", dkey, info.Revno, info.Queue) } revno[dkey] = info.Revno f.queue[dkey] = info.Queue continue NextDoc } } // The stash wasn't valid and tt got overwriten. Try again. f.unstashToken(tt, dkey) goto RetryDoc } // Save the prepared nonce onto t. nonce := tt.nonce() qdoc := bson.D{{"_id", t.Id}, {"s", tpreparing}} udoc := bson.D{{"$set", bson.D{{"s", tprepared}, {"n", nonce}}}} chaos("set-prepared") err = f.tc.Update(qdoc, udoc) if err == nil { t.State = tprepared t.Nonce = nonce } else if err == mgo.ErrNotFound { f.debugf("Can't save nonce of %s: LOST RACE", tt) if err := f.reload(t); err != nil { return nil, err } else if t.State == tpreparing { panic("can't save nonce yet transaction is still preparing") } else if t.State != tprepared { return t.Revnos, nil } tt = t.token() } else if err != nil { return nil, err } prereqs, found := f.hasPreReqs(tt, dkeys) if !found { // Must only happen when reloading above. return f.rescan(t, force) } else if prereqs && !force { f.debugf("Prepared queue with %s [has prereqs & not forced].", tt) return nil, errPreReqs } for _, op := range t.Ops { dkey := op.docKey() revnos = append(revnos, revno[dkey]) drevno := revno[dkey] switch { case op.Insert != nil && drevno < 0: revno[dkey] = -drevno+1 case op.Update != nil && drevno >= 0: revno[dkey] = drevno+1 case op.Remove && drevno >= 0: revno[dkey] = -drevno-1 } } if !prereqs { f.debugf("Prepared queue with %s [no prereqs]. Revnos: %v", tt, revnos) } else { f.debugf("Prepared queue with %s [forced] Revnos: %v", tt, revnos) } return revnos, nil } func (f *flusher) unstashToken(tt token, dkey docKey) error { qdoc := bson.D{{"_id", dkey}, {"txn-queue", tt}} udoc := bson.D{{"$pull", bson.D{{"txn-queue", tt}}}} chaos("") if err := f.sc.Update(qdoc, udoc); err == nil { chaos("") err = f.sc.Remove(bson.D{{"_id", dkey}, {"txn-queue", bson.D{}}}) } else if err != mgo.ErrNotFound { return err } return nil } func (f *flusher) rescan(t *transaction, force bool) (revnos []int64, err error) { f.debugf("Rescanning %s", t) if t.State != tprepared { panic(fmt.Errorf("rescanning transaction in invalid state: %q", t.State)) } // Iterate in a stable way across all runners. This isn't // strictly required, but reduces the chances of cycles. dkeys := t.docKeys() sort.Sort(dkeys) tt := t.token() if !force { prereqs, found := f.hasPreReqs(tt, dkeys) if found && prereqs { // It's state is already known. return nil, errPreReqs } } revno := make(map[docKey]int64) info := txnInfo{} for _, dkey := range dkeys { retry := 0 RetryDoc: c := f.tc.Database.C(dkey.C) if err := c.FindId(dkey.Id).Select(txnFields).One(&info); err == mgo.ErrNotFound { // Document is missing. Look in stash. if err := f.sc.FindId(dkey).One(&info); err == mgo.ErrNotFound { // Stash also doesn't exist. Maybe someone applied it. if err := f.reload(t); err != nil { return nil, err } else if t.State != tprepared { return t.Revnos, err } // Not applying either. retry++ if retry < 3 { // Retry since there might be an insert/remove race. goto RetryDoc } // Neither the doc nor the stash seem to exist. return nil, fmt.Errorf("cannot find document %v for applying transaction %s", dkey, t) } else if err != nil { return nil, err } // Stash found. if info.Insert != "" { // Handle insert in progress before assuming ordering is good. if err := f.loadAndApply(info.Insert); err != nil { return nil, err } goto RetryDoc } if info.Revno == 0 { // Missing revno in the stash means -1. info.Revno = -1 } } else if err != nil { return nil, err } else if info.Remove != "" { // Handle remove in progress before assuming ordering is good. if err := f.loadAndApply(info.Remove); err != nil { return nil, err } goto RetryDoc } revno[dkey] = info.Revno found := false for _, id := range info.Queue { if id == tt { found = true break } } f.queue[dkey] = info.Queue if !found { // Previously set txn-queue was popped by someone. // Transaction is being/has been applied elsewhere. f.debugf("Rescanned document %v misses %s in queue: %v", dkey, tt, info.Queue) err := f.reload(t) if t.State == tpreparing || t.State == tprepared { panic("rescanned document misses transaction in queue") } return t.Revnos, err } } prereqs, found := f.hasPreReqs(tt, dkeys) if !found { panic("rescanning loop guarantees that this can't happen") } else if prereqs && !force { f.debugf("Rescanned queue with %s: has prereqs, not forced", tt) return nil, errPreReqs } for _, op := range t.Ops { dkey := op.docKey() revnos = append(revnos, revno[dkey]) if op.isChange() { revno[dkey] += 1 } } if !prereqs { f.debugf("Rescanned queue with %s: no prereqs, revnos: %v", tt, revnos) } else { f.debugf("Rescanned queue with %s: has prereqs, forced, revnos: %v", tt, revnos) } return revnos, nil } func (f *flusher) hasPreReqs(tt token, dkeys docKeys) (prereqs, found bool) { found = true NextDoc: for _, dkey := range dkeys { for _, dtt := range f.queue[dkey] { if dtt == tt { continue NextDoc } else if dtt.id() != tt.id() { prereqs = true } } found = false } return } func (f *flusher) reload(t *transaction) error { var newt transaction query := f.tc.FindId(t.Id) query.Select(bson.D{{"s", 1}, {"n", 1}, {"r", 1}}) if err := query.One(&newt); err != nil { return fmt.Errorf("failed to reload transaction: %v", err) } t.State = newt.State t.Nonce = newt.Nonce t.Revnos = newt.Revnos f.debugf("Reloaded %s: %q", t, t.State) return nil } func (f *flusher) loadAndApply(id bson.ObjectId) error { t, err := f.load(id) if err != nil { return err } return f.advance(t, nil, true) } // assert verifies that all assertions in t match the content that t // will be applied upon. If an assertion fails, the transaction state // is changed to aborted. func (f *flusher) assert(t *transaction, revnos []int64, pull map[bson.ObjectId]*transaction) error { f.debugf("Asserting %s with revnos %v", t, revnos) if t.State != tprepared { panic(fmt.Errorf("asserting transaction in invalid state: %q", t.State)) } qdoc := make(bson.D, 3) revno := make(map[docKey]int64) for i, op := range t.Ops { dkey := op.docKey() if _, ok := revno[dkey]; !ok { revno[dkey] = revnos[i] } if op.Assert == nil { continue } if op.Assert == DocMissing { if revnos[i] >= 0 { return f.abortOrReload(t, revnos, pull) } continue } if op.Insert != nil { return fmt.Errorf("Insert can only Assert txn.DocMissing", op.Assert) } // if revnos[i] < 0 { abort }? qdoc = append(qdoc[:0], bson.DocElem{"_id", op.Id}) if op.Assert != DocMissing { var revnoq interface{} if n := revno[dkey]; n == 0 { revnoq = bson.D{{"$exists", false}} } else { revnoq = n } // XXX Add tt to the query here, once we're sure it's all working. // Not having it increases the chances of breaking on bad logic. qdoc = append(qdoc, bson.DocElem{"txn-revno", revnoq}) if op.Assert != DocExists { qdoc = append(qdoc, bson.DocElem{"$or", []interface{}{op.Assert}}) } } c := f.tc.Database.C(op.C) if err := c.Find(qdoc).Select(bson.D{{"_id", 1}}).One(nil); err == mgo.ErrNotFound { // Assertion failed or someone else started applying. return f.abortOrReload(t, revnos, pull) } else if err != nil { return err } } f.debugf("Asserting %s succeeded", t) return nil } func (f *flusher) abortOrReload(t *transaction, revnos []int64, pull map[bson.ObjectId]*transaction) (err error) { f.debugf("Aborting or reloading %s (was %q)", t, t.State) if t.State == tprepared { qdoc := bson.D{{"_id", t.Id}, {"s", tprepared}} udoc := bson.D{{"$set", bson.D{{"s", taborting}}}} chaos("set-aborting") if err = f.tc.Update(qdoc, udoc); err == nil { t.State = taborting } else if err == mgo.ErrNotFound { if err = f.reload(t); err != nil || t.State != taborting { f.debugf("Won't abort %s. Reloaded state: %q", t, t.State) return err } } else { return err } } else if t.State != taborting { panic(fmt.Errorf("aborting transaction in invalid state: %q", t.State)) } if len(revnos) > 0 { if pull == nil { pull = map[bson.ObjectId]*transaction{t.Id: t} } seen := make(map[docKey]bool) for i, op := range t.Ops { dkey := op.docKey() if seen[op.docKey()] { continue } seen[dkey] = true pullAll := tokensToPull(f.queue[dkey], pull, "") if len(pullAll) == 0 { continue } udoc := bson.D{{"$pullAll", bson.D{{"txn-queue", pullAll}}}} chaos("") if revnos[i] < 0 { err = f.sc.UpdateId(dkey, udoc) } else { c := f.tc.Database.C(dkey.C) err = c.UpdateId(dkey.Id, udoc) } if err != nil && err != mgo.ErrNotFound { return err } } } udoc := bson.D{{"$set", bson.D{{"s", taborted}}}} chaos("set-aborted") if err := f.tc.UpdateId(t.Id, udoc); err != nil && err != mgo.ErrNotFound { return err } t.State = taborted f.debugf("Aborted %s", t) return nil } func (f *flusher) checkpoint(t *transaction, revnos []int64) error { var debugRevnos map[docKey][]int64 if debugEnabled { debugRevnos = make(map[docKey][]int64) for i, op := range t.Ops { dkey := op.docKey() debugRevnos[dkey] = append(debugRevnos[dkey], revnos[i]) } f.debugf("Ready to apply %s. Saving revnos %v", t, debugRevnos) } // Save in t the txn-revno values the transaction must run on. qdoc := bson.D{{"_id", t.Id}, {"s", tprepared}} udoc := bson.D{{"$set", bson.D{{"s", tapplying}, {"r", revnos}}}} chaos("set-applying") err := f.tc.Update(qdoc, udoc) if err == nil { t.State = tapplying t.Revnos = revnos f.debugf("Ready to apply %s. Saving revnos %v: DONE", t, debugRevnos) } else if err == mgo.ErrNotFound { f.debugf("Ready to apply %s. Saving revnos %v: LOST RACE", t, debugRevnos) return f.reload(t) } return nil } func (f *flusher) apply(t *transaction, pull map[bson.ObjectId]*transaction) error { f.debugf("Applying transaction %s", t) if t.State != tapplying { panic(fmt.Errorf("applying transaction in invalid state: %q", t.State)) } if pull == nil { pull = map[bson.ObjectId]*transaction{t.Id: t} } // Compute the operation in which t's id may be pulled // out of txn-queue. That's on its last change, or the // first assertion. pullOp := make(map[docKey]int) for i := range t.Ops { op := &t.Ops[i] dkey := op.docKey() if _, ok := pullOp[dkey]; !ok || op.isChange() { pullOp[dkey] = i } } logRevnos := append([]int64(nil), t.Revnos...) logDoc := bson.D{{"_id", t.Id}} tt := tokenFor(t) for i := range t.Ops { op := &t.Ops[i] dkey := op.docKey() dqueue := f.queue[dkey] revno := t.Revnos[i] var opName string if debugEnabled { opName = op.name() f.debugf("Applying %s op %d (%s) on %v with txn-revno %d", t, i, opName, dkey, revno) } c := f.tc.Database.C(op.C) qdoc := bson.D{{"_id", dkey.Id}, {"txn-revno", revno}, {"txn-queue", tt}} if op.Insert != nil { qdoc[0].Value = dkey if revno == -1 { qdoc[1].Value = bson.D{{"$exists", false}} } } else if revno == 0 { // There's no document with revno 0. The only way to see it is // when an existent document participates in a transaction the // first time. Txn-inserted documents get revno -1 while in the // stash for the first time, and -revno-1 == 2 when they go live. qdoc[1].Value = bson.D{{"$exists", false}} } dontPull := tt isPullOp := pullOp[dkey] == i if isPullOp { dontPull = "" } pullAll := tokensToPull(dqueue, pull, dontPull) var d bson.D var outcome string var err error switch { case op.Update != nil: if revno < 0 { err = mgo.ErrNotFound f.debugf("Won't try to apply update op; negative revision means the document is missing or stashed"); } else { newRevno := revno + 1 logRevnos[i] = newRevno if d, err = objToDoc(op.Update); err != nil { return err } if d, err = addToDoc(d, "$pullAll", bson.D{{"txn-queue", pullAll}}); err != nil { return err } if d, err = addToDoc(d, "$set", bson.D{{"txn-revno", newRevno}}); err != nil { return err } chaos("") err = c.Update(qdoc, d) } case op.Remove: if revno < 0 { err = mgo.ErrNotFound } else { newRevno := -revno - 1 logRevnos[i] = newRevno nonce := newNonce() stash := txnInfo{} change := mgo.Change{ Update: bson.D{{"$push", bson.D{{"n", nonce}}}}, Upsert: true, ReturnNew: true, } if _, err = f.sc.FindId(dkey).Apply(change, &stash); err != nil { return err } change = mgo.Change{ Update: bson.D{{"$set", bson.D{{"txn-remove", t.Id}}}}, ReturnNew: true, } var info txnInfo if _, err = c.Find(qdoc).Apply(change, &info); err == nil { // The document still exists so the stash previously // observed was either out of date or necessarily // contained the token being applied. f.debugf("Marked document %v to be removed on revno %d with queue: %v", dkey, info.Revno, info.Queue) updated := false if !hasToken(stash.Queue, tt) { var set, unset bson.D if revno == 0 { // Missing revno in stash means -1. set = bson.D{{"txn-queue", info.Queue}} unset = bson.D{{"n", 1}, {"txn-revno", 1}} } else { set = bson.D{{"txn-queue", info.Queue}, {"txn-revno", newRevno}} unset = bson.D{{"n", 1}} } qdoc := bson.D{{"_id", dkey}, {"n", nonce}} udoc := bson.D{{"$set", set}, {"$unset", unset}} if err = f.sc.Update(qdoc, udoc); err == nil { updated = true } else if err != mgo.ErrNotFound { return err } } if updated { f.debugf("Updated stash for document %v with revno %d and queue: %v", dkey, newRevno, info.Queue) } else { f.debugf("Stash for document %v was up-to-date", dkey) } err = c.Remove(qdoc) } } case op.Insert != nil: if revno >= 0 { err = mgo.ErrNotFound } else { newRevno := -revno + 1 logRevnos[i] = newRevno if d, err = objToDoc(op.Insert); err != nil { return err } change := mgo.Change{ Update: bson.D{{"$set", bson.D{{"txn-insert", t.Id}}}}, ReturnNew: true, } chaos("") var info txnInfo if _, err = f.sc.Find(qdoc).Apply(change, &info); err == nil { f.debugf("Stash for document %v has revno %d and queue: %v", dkey, info.Revno, info.Queue) d = setInDoc(d, bson.D{{"_id", op.Id}, {"txn-revno", newRevno}, {"txn-queue", info.Queue}}) // Unlikely yet unfortunate race in here if this gets seriously // delayed. If someone inserts+removes meanwhile, this will // reinsert, and there's no way to avoid that while keeping the // collection clean or compromising sharding. applyOps can solve // the former, but it can't shard (SERVER-1439). chaos("insert") err = c.Insert(d) if err == nil || mgo.IsDup(err) { if err == nil { f.debugf("New document %v inserted with revno %d and queue: %v", dkey, info.Revno, info.Queue) } else { f.debugf("Document %v already existed", dkey) } chaos("") if err = f.sc.Remove(qdoc); err == nil { f.debugf("Stash for document %v removed", dkey) } } if pullOp[dkey] == i && len(pullAll) > 0 { _ = f.sc.UpdateId(dkey, bson.D{{"$pullAll", bson.D{{"txn-queue", pullAll}}}}) } } } case op.Assert != nil: // TODO pullAll if pullOp[dkey] == i } if err == nil { outcome = "DONE" } else if err == mgo.ErrNotFound || mgo.IsDup(err) { outcome = "MISS" err = nil } else { outcome = err.Error() } if debugEnabled { f.debugf("Applying %s op %d (%s) on %v with txn-revno %d: %s", t, i, opName, dkey, revno, outcome) } if err != nil { return err } if f.lc != nil && op.isChange() { // Add change to the log document. var dr bson.D for li := range logDoc { elem := &logDoc[li] if elem.Name == op.C { dr = elem.Value.(bson.D) break } } if dr == nil { logDoc = append(logDoc, bson.DocElem{op.C, bson.D{{"d", []interface{}{}}, {"r", []int64{}}}}) dr = logDoc[len(logDoc)-1].Value.(bson.D) } dr[0].Value = append(dr[0].Value.([]interface{}), op.Id) dr[1].Value = append(dr[1].Value.([]int64), logRevnos[i]) } } t.State = tapplied if f.lc != nil { // Insert log document into the changelog collection. f.debugf("Inserting %s into change log", t) err := f.lc.Insert(logDoc) if err != nil && !mgo.IsDup(err) { return err } } // It's been applied, so errors are ignored here. It's fine for someone // else to win the race and mark it as applied, and it's also fine for // it to remain pending until a later point when someone will perceive // it has been applied and mark it at such. f.debugf("Marking %s as applied", t) chaos("set-applied") f.tc.Update(bson.D{{"_id", t.Id}, {"s", tapplying}}, bson.D{{"$set", bson.D{{"s", tapplied}}}}) return nil } func tokensToPull(dqueue []token, pull map[bson.ObjectId]*transaction, dontPull token) []token { var result []token for j := len(dqueue) - 1; j >= 0; j-- { dtt := dqueue[j] if dt, ok := pull[dtt.id()]; ok { if dt.Nonce == dtt.nonce() { // It's valid and is being pulled out, so everything // preceding it must have been handled already. if dtt == dontPull { // Not time to pull this one out yet. j-- } result = append(result, dqueue[:j+1]...) break } // It was handled before and this is a leftover invalid // nonce in the queue. Cherry-pick it out. result = append(result, dtt) } } return result } func objToDoc(obj interface{}) (d bson.D, err error) { data, err := bson.Marshal(obj) if err != nil { return nil, err } err = bson.Unmarshal(data, &d) if err != nil { return nil, err } return d, err } func addToDoc(doc bson.D, key string, add bson.D) (bson.D, error) { for i := range doc { elem := &doc[i] if elem.Name != key { continue } if old, ok := elem.Value.(bson.D); ok { elem.Value = append(old, add...) return doc, nil } else { return nil, fmt.Errorf("invalid %q value in change document: %#v", key, elem.Value) } } return append(doc, bson.DocElem{key, add}), nil } func setInDoc(doc bson.D, set bson.D) bson.D { dlen := len(doc) NextS: for s := range set { sname := set[s].Name for d := 0; d < dlen; d++ { if doc[d].Name == sname { doc[d].Value = set[s].Value continue NextS } } doc = append(doc, set[s]) } return doc } func hasToken(tokens []token, tt token) bool { for _, ttt := range tokens { if ttt == tt { return true } } return false } func (f *flusher) debugf(format string, args ...interface{}) { if !debugEnabled { return } debugf(f.debugId+format, args...) } juju-core_1.18.1/src/labix.org/v2/mgo/txn/tarjan_test.go0000644000015300001610000000135012321735660022711 0ustar jenkinsjenkinspackage txn import ( "fmt" "labix.org/v2/mgo/bson" . "launchpad.net/gocheck" ) type TarjanSuite struct{} var _ = Suite(TarjanSuite{}) func bid(n int) bson.ObjectId { return bson.ObjectId(fmt.Sprintf("%024d", n)) } func bids(ns ...int) (ids []bson.ObjectId) { for _, n := range ns { ids = append(ids, bid(n)) } return } func (TarjanSuite) TestExample(c *C) { successors := map[bson.ObjectId][]bson.ObjectId{ bid(1): bids(2), bid(2): bids(1, 5), bid(3): bids(4), bid(4): bids(3, 5), bid(5): bids(6), bid(6): bids(7), bid(7): bids(8), bid(8): bids(6, 9), bid(9): bids(), } c.Assert(tarjanSort(successors), DeepEquals, [][]bson.ObjectId{ bids(9), bids(8, 7, 6), bids(5), bids(2, 1), bids(4, 3), }) } juju-core_1.18.1/src/labix.org/v2/mgo/gridfs.go0000644000015300001610000004700612321736015021043 0ustar jenkinsjenkins// mgo - MongoDB driver for Go // // Copyright (c) 2010-2012 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package mgo import ( "crypto/md5" "encoding/hex" "errors" "hash" "io" "labix.org/v2/mgo/bson" "os" "sync" "time" ) type GridFS struct { Files *Collection Chunks *Collection } type gfsFileMode int const ( gfsClosed gfsFileMode = 0 gfsReading gfsFileMode = 1 gfsWriting gfsFileMode = 2 ) type GridFile struct { m sync.Mutex c sync.Cond gfs *GridFS mode gfsFileMode err error chunk int offset int64 wpending int wbuf []byte wsum hash.Hash rbuf []byte rcache *gfsCachedChunk doc gfsFile } type gfsFile struct { Id interface{} "_id" ChunkSize int "chunkSize" UploadDate time.Time "uploadDate" Length int64 ",minsize" MD5 string Filename string ",omitempty" ContentType string "contentType,omitempty" Metadata *bson.Raw ",omitempty" } type gfsChunk struct { Id interface{} "_id" FilesId interface{} "files_id" N int Data []byte } type gfsCachedChunk struct { wait sync.Mutex n int data []byte err error } func newGridFS(db *Database, prefix string) *GridFS { return &GridFS{db.C(prefix + ".files"), db.C(prefix + ".chunks")} } func (gfs *GridFS) newFile() *GridFile { file := &GridFile{gfs: gfs} file.c.L = &file.m //runtime.SetFinalizer(file, finalizeFile) return file } func finalizeFile(file *GridFile) { file.Close() } // Create creates a new file with the provided name in the GridFS. If the file // name already exists, a new version will be inserted with an up-to-date // uploadDate that will cause it to be atomically visible to the Open and // OpenId methods. If the file name is not important, an empty name may be // provided and the file Id used instead. // // It's important to Close files whether they are being written to // or read from, and to check the err result to ensure the operation // completed successfully. // // A simple example inserting a new file: // // func check(err os.Error) { // if err != nil { // panic(err.String()) // } // } // file, err := db.GridFS("fs").Create("myfile.txt") // check(err) // n, err := file.Write([]byte("Hello world!") // check(err) // err = file.Close() // check(err) // fmt.Printf("%d bytes written\n", n) // // The io.Writer interface is implemented by *GridFile and may be used to // help on the file creation. For example: // // file, err := db.GridFS("fs").Create("myfile.txt") // check(err) // messages, err := os.Open("/var/log/messages") // check(err) // defer messages.Close() // err = io.Copy(file, messages) // check(err) // err = file.Close() // check(err) // func (gfs *GridFS) Create(name string) (file *GridFile, err error) { file = gfs.newFile() file.mode = gfsWriting file.wsum = md5.New() file.doc = gfsFile{Id: bson.NewObjectId(), ChunkSize: 256 * 1024, Filename: name} return } // OpenId returns the file with the provided id, for reading. // If the file isn't found, err will be set to mgo.ErrNotFound. // // It's important to Close files whether they are being written to // or read from, and to check the err result to ensure the operation // completed successfully. // // The following example will print the first 8192 bytes from the file: // // func check(err os.Error) { // if err != nil { // panic(err.String()) // } // } // file, err := db.GridFS("fs").OpenId(objid) // check(err) // b := make([]byte, 8192) // n, err := file.Read(b) // check(err) // fmt.Println(string(b)) // check(err) // err = file.Close() // check(err) // fmt.Printf("%d bytes read\n", n) // // The io.Reader interface is implemented by *GridFile and may be used to // deal with it. As an example, the following snippet will dump the whole // file into the standard output: // // file, err := db.GridFS("fs").OpenId(objid) // check(err) // err = io.Copy(os.Stdout, file) // check(err) // err = file.Close() // check(err) // func (gfs *GridFS) OpenId(id interface{}) (file *GridFile, err error) { var doc gfsFile err = gfs.Files.Find(bson.M{"_id": id}).One(&doc) if err != nil { return } file = gfs.newFile() file.mode = gfsReading file.doc = doc return } // Open returns the most recently uploaded file with the provided // name, for reading. If the file isn't found, err will be set // to mgo.ErrNotFound. // // It's important to Close files whether they are being written to // or read from, and to check the err result to ensure the operation // completed successfully. // // The following example will print the first 8192 bytes from the file: // // file, err := db.GridFS("fs").Open("myfile.txt") // check(err) // b := make([]byte, 8192) // n, err := file.Read(b) // check(err) // fmt.Println(string(b)) // check(err) // err = file.Close() // check(err) // fmt.Printf("%d bytes read\n", n) // // The io.Reader interface is implemented by *GridFile and may be used to // deal with it. As an example, the following snippet will dump the whole // file into the standard output: // // file, err := db.GridFS("fs").Open("myfile.txt") // check(err) // err = io.Copy(os.Stdout, file) // check(err) // err = file.Close() // check(err) // func (gfs *GridFS) Open(name string) (file *GridFile, err error) { var doc gfsFile err = gfs.Files.Find(bson.M{"filename": name}).Sort("-uploadDate").One(&doc) if err != nil { return } file = gfs.newFile() file.mode = gfsReading file.doc = doc return } // OpenNext opens the next file from iter for reading, sets *file to it, // and returns true on the success case. If no more documents are available // on iter or an error occurred, *file is set to nil and the result is false. // Errors will be available via iter.Err(). // // The iter parameter must be an iterator on the GridFS files collection. // Using the GridFS.Find method is an easy way to obtain such an iterator, // but any iterator on the collection will work. // // If the provided *file is non-nil, OpenNext will close it before attempting // to iterate to the next element. This means that in a loop one only // has to worry about closing files when breaking out of the loop early // (break, return, or panic). // // For example: // // gfs := db.GridFS("fs") // query := gfs.Find(nil).Sort("filename") // iter := query.Iter() // var f *mgo.GridFile // for gfs.OpenNext(iter, &f) { // fmt.Printf("Filename: %s\n", f.Name()) // } // if iter.Close() != nil { // panic(iter.Close()) // } // func (gfs *GridFS) OpenNext(iter *Iter, file **GridFile) bool { if *file != nil { // Ignoring the error here shouldn't be a big deal // as we're reading the file and the loop iteration // for this file is finished. _ = (*file).Close() } var doc gfsFile if !iter.Next(&doc) { *file = nil return false } f := gfs.newFile() f.mode = gfsReading f.doc = doc *file = f return true } // Find runs query on GridFS's files collection and returns // the resulting Query. // // This logic: // // gfs := db.GridFS("fs") // iter := gfs.Find(nil).Iter() // // Is equivalent to: // // files := db.C("fs" + ".files") // iter := files.Find(nil).Iter() // func (gfs *GridFS) Find(query interface{}) *Query { return gfs.Files.Find(query) } // RemoveId deletes the file with the provided id from the GridFS. func (gfs *GridFS) RemoveId(id interface{}) error { err := gfs.Files.Remove(bson.M{"_id": id}) if err != nil { return err } _, err = gfs.Chunks.RemoveAll(bson.M{"files_id": id}) return err } type gfsDocId struct { Id interface{} "_id" } // Remove deletes all files with the provided name from the GridFS. func (gfs *GridFS) Remove(name string) (err error) { iter := gfs.Files.Find(bson.M{"filename": name}).Select(bson.M{"_id": 1}).Iter() var doc gfsDocId for iter.Next(&doc) { if e := gfs.RemoveId(doc.Id); e != nil { err = e } } if err == nil { err = iter.Close() } return err } func (file *GridFile) assertMode(mode gfsFileMode) { switch file.mode { case mode: return case gfsWriting: panic("GridFile is open for writing") case gfsReading: panic("GridFile is open for reading") case gfsClosed: panic("GridFile is closed") default: panic("Internal error: missing GridFile mode") } } // SetChunkSize sets size of saved chunks. Once the file is written to, it // will be split in blocks of that size and each block saved into an // independent chunk document. The default chunk size is 256kb. // // It is a runtime error to call this function once the file has started // being written to. func (file *GridFile) SetChunkSize(bytes int) { file.assertMode(gfsWriting) debugf("GridFile %p: setting chunk size to %d", file, bytes) file.m.Lock() file.doc.ChunkSize = bytes file.m.Unlock() } // Id returns the current file Id. func (file *GridFile) Id() interface{} { return file.doc.Id } // SetId changes the current file Id. It is a runtime // // It is a runtime error to call this function once the file has started // being written to, or when the file is not open for writing. func (file *GridFile) SetId(id interface{}) { file.assertMode(gfsWriting) file.m.Lock() file.doc.Id = id file.m.Unlock() } // Name returns the optional file name. An empty string will be returned // in case it is unset. func (file *GridFile) Name() string { return file.doc.Filename } // SetName changes the optional file name. An empty string may be used to // unset it. // // It is a runtime error to call this function when the file is not open // for writing. func (file *GridFile) SetName(name string) { file.assertMode(gfsWriting) file.m.Lock() file.doc.Filename = name file.m.Unlock() } // ContentType returns the optional file content type. An empty string will be // returned in case it is unset. func (file *GridFile) ContentType() string { return file.doc.ContentType } // ContentType changes the optional file content type. An empty string may be // used to unset it. // // It is a runtime error to call this function when the file is not open // for writing. func (file *GridFile) SetContentType(ctype string) { file.assertMode(gfsWriting) file.m.Lock() file.doc.ContentType = ctype file.m.Unlock() } // GetMeta unmarshals the optional "metadata" field associated with the // file into the result parameter. The meaning of keys under that field // is user-defined. For example: // // result := struct{ INode int }{} // err = file.GetMeta(&result) // if err != nil { // panic(err.String()) // } // fmt.Printf("inode: %d\n", result.INode) // func (file *GridFile) GetMeta(result interface{}) (err error) { file.m.Lock() if file.doc.Metadata != nil { err = bson.Unmarshal(file.doc.Metadata.Data, result) } file.m.Unlock() return } // SetMeta changes the optional "metadata" field associated with the // file. The meaning of keys under that field is user-defined. // For example: // // file.SetMeta(bson.M{"inode": inode}) // // It is a runtime error to call this function when the file is not open // for writing. func (file *GridFile) SetMeta(metadata interface{}) { file.assertMode(gfsWriting) data, err := bson.Marshal(metadata) file.m.Lock() if err != nil && file.err == nil { file.err = err } else { file.doc.Metadata = &bson.Raw{Data: data} } file.m.Unlock() } // Size returns the file size in bytes. func (file *GridFile) Size() (bytes int64) { file.m.Lock() bytes = file.doc.Length file.m.Unlock() return } // MD5 returns the file MD5 as a hex-encoded string. func (file *GridFile) MD5() (md5 string) { return file.doc.MD5 } // UploadDate returns the file upload time. func (file *GridFile) UploadDate() time.Time { return file.doc.UploadDate } // Close flushes any pending changes in case the file is being written // to, waits for any background operations to finish, and closes the file. // // It's important to Close files whether they are being written to // or read from, and to check the err result to ensure the operation // completed successfully. func (file *GridFile) Close() (err error) { file.m.Lock() defer file.m.Unlock() if file.mode == gfsWriting { if len(file.wbuf) > 0 { file.insertChunk(file.wbuf) file.wbuf = file.wbuf[0:0] } file.insertFile() } else if file.mode == gfsReading && file.rcache != nil { file.rcache.wait.Lock() file.rcache = nil } file.mode = gfsClosed debugf("GridFile %p: closed", file) return file.err } // Write writes the provided data to the file and returns the // number of bytes written and an error in case something // wrong happened. // // The file will internally cache the data so that all but the last // chunk sent to the database have the size defined by SetChunkSize. // This also means that errors may be deferred until a future call // to Write or Close. // // The parameters and behavior of this function turn the file // into an io.Writer. func (file *GridFile) Write(data []byte) (n int, err error) { file.assertMode(gfsWriting) file.m.Lock() debugf("GridFile %p: writing %d bytes", file, len(data)) defer file.m.Unlock() if file.err != nil { return 0, file.err } n = len(data) file.doc.Length += int64(n) chunkSize := file.doc.ChunkSize if len(file.wbuf)+len(data) < chunkSize { file.wbuf = append(file.wbuf, data...) return } // First, flush file.wbuf complementing with data. if len(file.wbuf) > 0 { missing := chunkSize - len(file.wbuf) if missing > len(data) { missing = len(data) } file.wbuf = append(file.wbuf, data[:missing]...) data = data[missing:] file.insertChunk(file.wbuf) file.wbuf = file.wbuf[0:0] } // Then, flush all chunks from data without copying. for len(data) > chunkSize { size := chunkSize if size > len(data) { size = len(data) } file.insertChunk(data[:size]) data = data[size:] } // And append the rest for a future call. file.wbuf = append(file.wbuf, data...) return n, file.err } func (file *GridFile) insertChunk(data []byte) { n := file.chunk file.chunk++ debugf("GridFile %p: adding to checksum: %q", file, string(data)) file.wsum.Write(data) for file.doc.ChunkSize*file.wpending >= 1024*1024 { // Hold on.. we got a MB pending. file.c.Wait() if file.err != nil { return } } file.wpending++ debugf("GridFile %p: inserting chunk %d with %d bytes", file, n, len(data)) // We may not own the memory of data, so rather than // simply copying it, we'll marshal the document ahead of time. data, err := bson.Marshal(gfsChunk{bson.NewObjectId(), file.doc.Id, n, data}) if err != nil { file.err = err return } go func() { err := file.gfs.Chunks.Insert(bson.Raw{Data: data}) file.m.Lock() file.wpending-- if err != nil && file.err == nil { file.err = err } file.c.Broadcast() file.m.Unlock() }() } func (file *GridFile) insertFile() { hexsum := hex.EncodeToString(file.wsum.Sum(nil)) for file.wpending > 0 { debugf("GridFile %p: waiting for %d pending chunks to insert file", file, file.wpending) file.c.Wait() } if file.err == nil { file.doc.UploadDate = bson.Now() file.doc.MD5 = hexsum file.err = file.gfs.Files.Insert(file.doc) file.gfs.Chunks.EnsureIndexKey("files_id", "n") } } // Seek sets the offset for the next Read or Write on file to // offset, interpreted according to whence: 0 means relative to // the origin of the file, 1 means relative to the current offset, // and 2 means relative to the end. It returns the new offset and // an Error, if any. func (file *GridFile) Seek(offset int64, whence int) (pos int64, err error) { file.m.Lock() debugf("GridFile %p: seeking for %s (whence=%d)", file, offset, whence) defer file.m.Unlock() switch whence { case os.SEEK_SET: case os.SEEK_CUR: offset += file.offset case os.SEEK_END: offset += file.doc.Length default: panic("Unsupported whence value") } if offset > file.doc.Length { return file.offset, errors.New("Seek past end of file") } chunk := int(offset / int64(file.doc.ChunkSize)) if chunk+1 == file.chunk && offset >= file.offset { file.rbuf = file.rbuf[int(offset-file.offset):] file.offset = offset return file.offset, nil } file.offset = offset file.chunk = chunk file.rbuf = nil file.rbuf, err = file.getChunk() if err == nil { file.rbuf = file.rbuf[int(file.offset-int64(chunk)*int64(file.doc.ChunkSize)):] } return file.offset, err } // Read reads into b the next available data from the file and // returns the number of bytes written and an error in case // something wrong happened. At the end of the file, n will // be zero and err will be set to os.EOF. // // The parameters and behavior of this function turn the file // into an io.Reader. func (file *GridFile) Read(b []byte) (n int, err error) { file.assertMode(gfsReading) file.m.Lock() debugf("GridFile %p: reading at offset %d into buffer of length %d", file, file.offset, len(b)) defer file.m.Unlock() if file.offset == file.doc.Length { return 0, io.EOF } for err == nil { i := copy(b, file.rbuf) n += i file.offset += int64(i) file.rbuf = file.rbuf[i:] if i == len(b) || file.offset == file.doc.Length { break } b = b[i:] file.rbuf, err = file.getChunk() } return n, err } func (file *GridFile) getChunk() (data []byte, err error) { cache := file.rcache file.rcache = nil if cache != nil && cache.n == file.chunk { debugf("GridFile %p: Getting chunk %d from cache", file, file.chunk) cache.wait.Lock() data, err = cache.data, cache.err } else { debugf("GridFile %p: Fetching chunk %d", file, file.chunk) var doc gfsChunk err = file.gfs.Chunks.Find(bson.D{{"files_id", file.doc.Id}, {"n", file.chunk}}).One(&doc) data = doc.Data } file.chunk++ if int64(file.chunk)*int64(file.doc.ChunkSize) < file.doc.Length { // Read the next one in background. cache = &gfsCachedChunk{n: file.chunk} cache.wait.Lock() debugf("GridFile %p: Scheduling chunk %d for background caching", file, file.chunk) // Clone the session to avoid having it closed in between. chunks := file.gfs.Chunks session := chunks.Database.Session.Clone() go func(id interface{}, n int) { defer session.Close() chunks = chunks.With(session) var doc gfsChunk cache.err = chunks.Find(bson.D{{"files_id", id}, {"n", n}}).One(&doc) cache.data = doc.Data cache.wait.Unlock() }(file.doc.Id, file.chunk) file.rcache = cache } debugf("Returning err: %#v", err) return } juju-core_1.18.1/src/code.google.com/0000755000015300001610000000000012321735644017202 5ustar jenkinsjenkinsjuju-core_1.18.1/src/code.google.com/p/0000755000015300001610000000000012321735647017444 5ustar jenkinsjenkinsjuju-core_1.18.1/src/code.google.com/p/go.net/0000755000015300001610000000000012321736013020622 5ustar jenkinsjenkinsjuju-core_1.18.1/src/code.google.com/p/go.net/codereview.cfg0000644000015300001610000000014612321735652023450 0ustar jenkinsjenkinsdefaultcc: golang-codereviews@googlegroups.com contributors: http://go.googlecode.com/hg/CONTRIBUTORS juju-core_1.18.1/src/code.google.com/p/go.net/dict/0000755000015300001610000000000012321735652021555 5ustar jenkinsjenkinsjuju-core_1.18.1/src/code.google.com/p/go.net/dict/dict.go0000644000015300001610000001071212321735652023030 0ustar jenkinsjenkins// Copyright 2010 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 dict implements the Dictionary Server Protocol // as defined in RFC 2229. package dict import ( "net/textproto" "strconv" "strings" ) // A Client represents a client connection to a dictionary server. type Client struct { text *textproto.Conn } // Dial returns a new client connected to a dictionary server at // addr on the given network. func Dial(network, addr string) (*Client, error) { text, err := textproto.Dial(network, addr) if err != nil { return nil, err } _, _, err = text.ReadCodeLine(220) if err != nil { text.Close() return nil, err } return &Client{text: text}, nil } // Close closes the connection to the dictionary server. func (c *Client) Close() error { return c.text.Close() } // A Dict represents a dictionary available on the server. type Dict struct { Name string // short name of dictionary Desc string // long description } // Dicts returns a list of the dictionaries available on the server. func (c *Client) Dicts() ([]Dict, error) { id, err := c.text.Cmd("SHOW DB") if err != nil { return nil, err } c.text.StartResponse(id) defer c.text.EndResponse(id) _, _, err = c.text.ReadCodeLine(110) if err != nil { return nil, err } lines, err := c.text.ReadDotLines() if err != nil { return nil, err } _, _, err = c.text.ReadCodeLine(250) dicts := make([]Dict, len(lines)) for i := range dicts { d := &dicts[i] a, _ := fields(lines[i]) if len(a) < 2 { return nil, textproto.ProtocolError("invalid dictionary: " + lines[i]) } d.Name = a[0] d.Desc = a[1] } return dicts, err } // A Defn represents a definition. type Defn struct { Dict Dict // Dict where definition was found Word string // Word being defined Text []byte // Definition text, typically multiple lines } // Define requests the definition of the given word. // The argument dict names the dictionary to use, // the Name field of a Dict returned by Dicts. // // The special dictionary name "*" means to look in all the // server's dictionaries. // The special dictionary name "!" means to look in all the // server's dictionaries in turn, stopping after finding the word // in one of them. func (c *Client) Define(dict, word string) ([]*Defn, error) { id, err := c.text.Cmd("DEFINE %s %q", dict, word) if err != nil { return nil, err } c.text.StartResponse(id) defer c.text.EndResponse(id) _, line, err := c.text.ReadCodeLine(150) if err != nil { return nil, err } a, _ := fields(line) if len(a) < 1 { return nil, textproto.ProtocolError("malformed response: " + line) } n, err := strconv.Atoi(a[0]) if err != nil { return nil, textproto.ProtocolError("invalid definition count: " + a[0]) } def := make([]*Defn, n) for i := 0; i < n; i++ { _, line, err = c.text.ReadCodeLine(151) if err != nil { return nil, err } a, _ := fields(line) if len(a) < 3 { // skip it, to keep protocol in sync i-- n-- def = def[0:n] continue } d := &Defn{Word: a[0], Dict: Dict{a[1], a[2]}} d.Text, err = c.text.ReadDotBytes() if err != nil { return nil, err } def[i] = d } _, _, err = c.text.ReadCodeLine(250) return def, err } // Fields returns the fields in s. // Fields are space separated unquoted words // or quoted with single or double quote. func fields(s string) ([]string, error) { var v []string i := 0 for { for i < len(s) && (s[i] == ' ' || s[i] == '\t') { i++ } if i >= len(s) { break } if s[i] == '"' || s[i] == '\'' { q := s[i] // quoted string var j int for j = i + 1; ; j++ { if j >= len(s) { return nil, textproto.ProtocolError("malformed quoted string") } if s[j] == '\\' { j++ continue } if s[j] == q { j++ break } } v = append(v, unquote(s[i+1:j-1])) i = j } else { // atom var j int for j = i; j < len(s); j++ { if s[j] == ' ' || s[j] == '\t' || s[j] == '\\' || s[j] == '"' || s[j] == '\'' { break } } v = append(v, s[i:j]) i = j } if i < len(s) { c := s[i] if c != ' ' && c != '\t' { return nil, textproto.ProtocolError("quotes not on word boundaries") } } } return v, nil } func unquote(s string) string { if strings.Index(s, "\\") < 0 { return s } b := []byte(s) w := 0 for r := 0; r < len(b); r++ { c := b[r] if c == '\\' { r++ c = b[r] } b[w] = c w++ } return string(b[0:w]) } juju-core_1.18.1/src/code.google.com/p/go.net/PATENTS0000644000015300001610000000242712321735652021700 0ustar jenkinsjenkinsAdditional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. juju-core_1.18.1/src/code.google.com/p/go.net/LICENSE0000644000015300001610000000270712321735652021645 0ustar jenkinsjenkinsCopyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. juju-core_1.18.1/src/code.google.com/p/go.net/html/0000755000015300001610000000000012321735652021576 5ustar jenkinsjenkinsjuju-core_1.18.1/src/code.google.com/p/go.net/html/const.go0000644000015300001610000000441212321735652023254 0ustar jenkinsjenkins// 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 html // Section 12.2.3.2 of the HTML5 specification says "The following elements // have varying levels of special parsing rules". // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements var isSpecialElementMap = map[string]bool{ "address": true, "applet": true, "area": true, "article": true, "aside": true, "base": true, "basefont": true, "bgsound": true, "blockquote": true, "body": true, "br": true, "button": true, "caption": true, "center": true, "col": true, "colgroup": true, "command": true, "dd": true, "details": true, "dir": true, "div": true, "dl": true, "dt": true, "embed": true, "fieldset": true, "figcaption": true, "figure": true, "footer": true, "form": true, "frame": true, "frameset": true, "h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true, "head": true, "header": true, "hgroup": true, "hr": true, "html": true, "iframe": true, "img": true, "input": true, "isindex": true, "li": true, "link": true, "listing": true, "marquee": true, "menu": true, "meta": true, "nav": true, "noembed": true, "noframes": true, "noscript": true, "object": true, "ol": true, "p": true, "param": true, "plaintext": true, "pre": true, "script": true, "section": true, "select": true, "style": true, "summary": true, "table": true, "tbody": true, "td": true, "textarea": true, "tfoot": true, "th": true, "thead": true, "title": true, "tr": true, "ul": true, "wbr": true, "xmp": true, } func isSpecialElement(element *Node) bool { switch element.Namespace { case "", "html": return isSpecialElementMap[element.Data] case "svg": return element.Data == "foreignObject" } return false } juju-core_1.18.1/src/code.google.com/p/go.net/html/example_test.go0000644000015300001610000000151212321735652024616 0ustar jenkinsjenkins// 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. // This example demonstrates parsing HTML data and walking the resulting tree. package html_test import ( "fmt" "log" "strings" "code.google.com/p/go.net/html" ) func ExampleParse() { s := `

Links:

` doc, err := html.Parse(strings.NewReader(s)) if err != nil { log.Fatal(err) } var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.ElementNode && n.Data == "a" { for _, a := range n.Attr { if a.Key == "href" { fmt.Println(a.Val) break } } } for c := n.FirstChild; c != nil; c = c.NextSibling { f(c) } } f(doc) // Output: // foo // /bar/baz } juju-core_1.18.1/src/code.google.com/p/go.net/html/doctype.go0000644000015300001610000001147512321735652023604 0ustar jenkinsjenkins// 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 html import ( "strings" ) // parseDoctype parses the data from a DoctypeToken into a name, // public identifier, and system identifier. It returns a Node whose Type // is DoctypeNode, whose Data is the name, and which has attributes // named "system" and "public" for the two identifiers if they were present. // quirks is whether the document should be parsed in "quirks mode". func parseDoctype(s string) (n *Node, quirks bool) { n = &Node{Type: DoctypeNode} // Find the name. space := strings.IndexAny(s, whitespace) if space == -1 { space = len(s) } n.Data = s[:space] // The comparison to "html" is case-sensitive. if n.Data != "html" { quirks = true } n.Data = strings.ToLower(n.Data) s = strings.TrimLeft(s[space:], whitespace) if len(s) < 6 { // It can't start with "PUBLIC" or "SYSTEM". // Ignore the rest of the string. return n, quirks || s != "" } key := strings.ToLower(s[:6]) s = s[6:] for key == "public" || key == "system" { s = strings.TrimLeft(s, whitespace) if s == "" { break } quote := s[0] if quote != '"' && quote != '\'' { break } s = s[1:] q := strings.IndexRune(s, rune(quote)) var id string if q == -1 { id = s s = "" } else { id = s[:q] s = s[q+1:] } n.Attr = append(n.Attr, Attribute{Key: key, Val: id}) if key == "public" { key = "system" } else { key = "" } } if key != "" || s != "" { quirks = true } else if len(n.Attr) > 0 { if n.Attr[0].Key == "public" { public := strings.ToLower(n.Attr[0].Val) switch public { case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html": quirks = true default: for _, q := range quirkyIDs { if strings.HasPrefix(public, q) { quirks = true break } } } // The following two public IDs only cause quirks mode if there is no system ID. if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") || strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) { quirks = true } } if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" && strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" { quirks = true } } return n, quirks } // quirkyIDs is a list of public doctype identifiers that cause a document // to be interpreted in quirks mode. The identifiers should be in lower case. var quirkyIDs = []string{ "+//silmaril//dtd html pro v0r11 19970101//", "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", "-//as//dtd html 3.0 aswedit + extensions//", "-//ietf//dtd html 2.0 level 1//", "-//ietf//dtd html 2.0 level 2//", "-//ietf//dtd html 2.0 strict level 1//", "-//ietf//dtd html 2.0 strict level 2//", "-//ietf//dtd html 2.0 strict//", "-//ietf//dtd html 2.0//", "-//ietf//dtd html 2.1e//", "-//ietf//dtd html 3.0//", "-//ietf//dtd html 3.2 final//", "-//ietf//dtd html 3.2//", "-//ietf//dtd html 3//", "-//ietf//dtd html level 0//", "-//ietf//dtd html level 1//", "-//ietf//dtd html level 2//", "-//ietf//dtd html level 3//", "-//ietf//dtd html strict level 0//", "-//ietf//dtd html strict level 1//", "-//ietf//dtd html strict level 2//", "-//ietf//dtd html strict level 3//", "-//ietf//dtd html strict//", "-//ietf//dtd html//", "-//metrius//dtd metrius presentational//", "-//microsoft//dtd internet explorer 2.0 html strict//", "-//microsoft//dtd internet explorer 2.0 html//", "-//microsoft//dtd internet explorer 2.0 tables//", "-//microsoft//dtd internet explorer 3.0 html strict//", "-//microsoft//dtd internet explorer 3.0 html//", "-//microsoft//dtd internet explorer 3.0 tables//", "-//netscape comm. corp.//dtd html//", "-//netscape comm. corp.//dtd strict html//", "-//o'reilly and associates//dtd html 2.0//", "-//o'reilly and associates//dtd html extended 1.0//", "-//o'reilly and associates//dtd html extended relaxed 1.0//", "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", "-//spyglass//dtd html 2.0 extended//", "-//sq//dtd html 2.0 hotmetal + extensions//", "-//sun microsystems corp.//dtd hotjava html//", "-//sun microsystems corp.//dtd hotjava strict html//", "-//w3c//dtd html 3 1995-03-24//", "-//w3c//dtd html 3.2 draft//", "-//w3c//dtd html 3.2 final//", "-//w3c//dtd html 3.2//", "-//w3c//dtd html 3.2s draft//", "-//w3c//dtd html 4.0 frameset//", "-//w3c//dtd html 4.0 transitional//", "-//w3c//dtd html experimental 19960712//", "-//w3c//dtd html experimental 970421//", "-//w3c//dtd w3 html//", "-//w3o//dtd w3 html 3.0//", "-//webtechs//dtd mozilla html 2.0//", "-//webtechs//dtd mozilla html//", } juju-core_1.18.1/src/code.google.com/p/go.net/html/entity_test.go0000644000015300001610000000177612321735652024513 0ustar jenkinsjenkins// Copyright 2010 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 html import ( "testing" "unicode/utf8" ) func TestEntityLength(t *testing.T) { // We verify that the length of UTF-8 encoding of each value is <= 1 + len(key). // The +1 comes from the leading "&". This property implies that the length of // unescaped text is <= the length of escaped text. for k, v := range entity { if 1+len(k) < utf8.RuneLen(v) { t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v)) } if len(k) > longestEntityWithoutSemicolon && k[len(k)-1] != ';' { t.Errorf("entity name %s is %d characters, but longestEntityWithoutSemicolon=%d", k, len(k), longestEntityWithoutSemicolon) } } for k, v := range entity2 { if 1+len(k) < utf8.RuneLen(v[0])+utf8.RuneLen(v[1]) { t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v[0]) + string(v[1])) } } } juju-core_1.18.1/src/code.google.com/p/go.net/html/doc.go0000644000015300001610000000647712321735652022710 0ustar jenkinsjenkins// Copyright 2010 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 html implements an HTML5-compliant tokenizer and parser. Tokenization is done by creating a Tokenizer for an io.Reader r. It is the caller's responsibility to ensure that r provides UTF-8 encoded HTML. z := html.NewTokenizer(r) Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(), which parses the next token and returns its type, or an error: for { tt := z.Next() if tt == html.ErrorToken { // ... return ... } // Process the current token. } There are two APIs for retrieving the current token. The high-level API is to call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs allow optionally calling Raw after Next but before Token, Text, TagName, or TagAttr. In EBNF notation, the valid call sequence per token is: Next {Raw} [ Token | Text | TagName {TagAttr} ] Token returns an independent data structure that completely describes a token. Entities (such as "<") are unescaped, tag names and attribute keys are lower-cased, and attributes are collected into a []Attribute. For example: for { if z.Next() == html.ErrorToken { // Returning io.EOF indicates success. return z.Err() } emitToken(z.Token()) } The low-level API performs fewer allocations and copies, but the contents of the []byte values returned by Text, TagName and TagAttr may change on the next call to Next. For example, to extract an HTML page's anchor text: depth := 0 for { tt := z.Next() switch tt { case ErrorToken: return z.Err() case TextToken: if depth > 0 { // emitBytes should copy the []byte it receives, // if it doesn't process it immediately. emitBytes(z.Text()) } case StartTagToken, EndTagToken: tn, _ := z.TagName() if len(tn) == 1 && tn[0] == 'a' { if tt == StartTagToken { depth++ } else { depth-- } } } } Parsing is done by calling Parse with an io.Reader, which returns the root of the parse tree (the document element) as a *Node. It is the caller's responsibility to ensure that the Reader provides UTF-8 encoded HTML. For example, to process each anchor node in depth-first order: doc, err := html.Parse(r) if err != nil { // ... } var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.ElementNode && n.Data == "a" { // Do something with n... } for c := n.FirstChild; c != nil; c = c.NextSibling { f(c) } } f(doc) The relevant specifications include: http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html and http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html */ package html // The tokenization algorithm implemented by this package is not a line-by-line // transliteration of the relatively verbose state-machine in the WHATWG // specification. A more direct approach is used instead, where the program // counter implies the state, such as whether it is tokenizing a tag or a text // node. Specification compliance is verified by checking expected and actual // outputs over a test suite rather than aiming for algorithmic fidelity. // TODO(nigeltao): Does a DOM API belong in this package or a separate one? // TODO(nigeltao): How does parsing interact with a JavaScript engine? juju-core_1.18.1/src/code.google.com/p/go.net/html/escape_test.go0000644000015300001610000000353512321735652024432 0ustar jenkinsjenkins// 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 html import "testing" type unescapeTest struct { // A short description of the test case. desc string // The HTML text. html string // The unescaped text. unescaped string } var unescapeTests = []unescapeTest{ // Handle no entities. { "copy", "A\ttext\nstring", "A\ttext\nstring", }, // Handle simple named entities. { "simple", "& > <", "& > <", }, // Handle hitting the end of the string. { "stringEnd", "& &", "& &", }, // Handle entities with two codepoints. { "multiCodepoint", "text ⋛︀ blah", "text \u22db\ufe00 blah", }, // Handle decimal numeric entities. { "decimalEntity", "Delta = Δ ", "Delta = Δ ", }, // Handle hexadecimal numeric entities. { "hexadecimalEntity", "Lambda = λ = λ ", "Lambda = λ = λ ", }, // Handle numeric early termination. { "numericEnds", "&# &#x €43 © = ©f = ©", "&# &#x €43 © = ©f = ©", }, // Handle numeric ISO-8859-1 entity replacements. { "numericReplacements", "Footnote‡", "Footnote‡", }, } func TestUnescape(t *testing.T) { for _, tt := range unescapeTests { unescaped := UnescapeString(tt.html) if unescaped != tt.unescaped { t.Errorf("TestUnescape %s: want %q, got %q", tt.desc, tt.unescaped, unescaped) } } } func TestUnescapeEscape(t *testing.T) { ss := []string{ ``, `abc def`, `a & b`, `a&b`, `a & b`, `"`, `"`, `"<&>"`, `"<&>"`, `3&5==1 && 0<1, "0<1", a+acute=á`, `The special characters are: <, >, &, ' and "`, } for _, s := range ss { if got := UnescapeString(EscapeString(s)); got != s { t.Errorf("got %q want %q", got, s) } } } juju-core_1.18.1/src/code.google.com/p/go.net/html/parse_test.go0000644000015300001610000002364512321735652024310 0ustar jenkinsjenkins// Copyright 2010 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 html import ( "bufio" "bytes" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "runtime" "sort" "strings" "testing" "code.google.com/p/go.net/html/atom" ) // readParseTest reads a single test case from r. func readParseTest(r *bufio.Reader) (text, want, context string, err error) { line, err := r.ReadSlice('\n') if err != nil { return "", "", "", err } var b []byte // Read the HTML. if string(line) != "#data\n" { return "", "", "", fmt.Errorf(`got %q want "#data\n"`, line) } for { line, err = r.ReadSlice('\n') if err != nil { return "", "", "", err } if line[0] == '#' { break } b = append(b, line...) } text = strings.TrimSuffix(string(b), "\n") b = b[:0] // Skip the error list. if string(line) != "#errors\n" { return "", "", "", fmt.Errorf(`got %q want "#errors\n"`, line) } for { line, err = r.ReadSlice('\n') if err != nil { return "", "", "", err } if line[0] == '#' { break } } if string(line) == "#document-fragment\n" { line, err = r.ReadSlice('\n') if err != nil { return "", "", "", err } context = strings.TrimSpace(string(line)) line, err = r.ReadSlice('\n') if err != nil { return "", "", "", err } } // Read the dump of what the parse tree should be. if string(line) != "#document\n" { return "", "", "", fmt.Errorf(`got %q want "#document\n"`, line) } inQuote := false for { line, err = r.ReadSlice('\n') if err != nil && err != io.EOF { return "", "", "", err } trimmed := bytes.Trim(line, "| \n") if len(trimmed) > 0 { if line[0] == '|' && trimmed[0] == '"' { inQuote = true } if trimmed[len(trimmed)-1] == '"' && !(line[0] == '|' && len(trimmed) == 1) { inQuote = false } } if len(line) == 0 || len(line) == 1 && line[0] == '\n' && !inQuote { break } b = append(b, line...) } return text, string(b), context, nil } func dumpIndent(w io.Writer, level int) { io.WriteString(w, "| ") for i := 0; i < level; i++ { io.WriteString(w, " ") } } type sortedAttributes []Attribute func (a sortedAttributes) Len() int { return len(a) } func (a sortedAttributes) Less(i, j int) bool { if a[i].Namespace != a[j].Namespace { return a[i].Namespace < a[j].Namespace } return a[i].Key < a[j].Key } func (a sortedAttributes) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func dumpLevel(w io.Writer, n *Node, level int) error { dumpIndent(w, level) switch n.Type { case ErrorNode: return errors.New("unexpected ErrorNode") case DocumentNode: return errors.New("unexpected DocumentNode") case ElementNode: if n.Namespace != "" { fmt.Fprintf(w, "<%s %s>", n.Namespace, n.Data) } else { fmt.Fprintf(w, "<%s>", n.Data) } attr := sortedAttributes(n.Attr) sort.Sort(attr) for _, a := range attr { io.WriteString(w, "\n") dumpIndent(w, level+1) if a.Namespace != "" { fmt.Fprintf(w, `%s %s="%s"`, a.Namespace, a.Key, a.Val) } else { fmt.Fprintf(w, `%s="%s"`, a.Key, a.Val) } } case TextNode: fmt.Fprintf(w, `"%s"`, n.Data) case CommentNode: fmt.Fprintf(w, "", n.Data) case DoctypeNode: fmt.Fprintf(w, "") case scopeMarkerNode: return errors.New("unexpected scopeMarkerNode") default: return errors.New("unknown node type") } io.WriteString(w, "\n") for c := n.FirstChild; c != nil; c = c.NextSibling { if err := dumpLevel(w, c, level+1); err != nil { return err } } return nil } func dump(n *Node) (string, error) { if n == nil || n.FirstChild == nil { return "", nil } var b bytes.Buffer for c := n.FirstChild; c != nil; c = c.NextSibling { if err := dumpLevel(&b, c, 0); err != nil { return "", err } } return b.String(), nil } const testDataDir = "testdata/webkit/" func TestParser(t *testing.T) { testFiles, err := filepath.Glob(testDataDir + "*.dat") if err != nil { t.Fatal(err) } for _, tf := range testFiles { f, err := os.Open(tf) if err != nil { t.Fatal(err) } defer f.Close() r := bufio.NewReader(f) for i := 0; ; i++ { text, want, context, err := readParseTest(r) if err == io.EOF { break } if err != nil { t.Fatal(err) } err = testParseCase(text, want, context) if err != nil { t.Errorf("%s test #%d %q, %s", tf, i, text, err) } } } } // testParseCase tests one test case from the test files. If the test does not // pass, it returns an error that explains the failure. // text is the HTML to be parsed, want is a dump of the correct parse tree, // and context is the name of the context node, if any. func testParseCase(text, want, context string) (err error) { defer func() { if x := recover(); x != nil { switch e := x.(type) { case error: err = e default: err = fmt.Errorf("%v", e) } } }() var doc *Node if context == "" { doc, err = Parse(strings.NewReader(text)) if err != nil { return err } } else { contextNode := &Node{ Type: ElementNode, DataAtom: atom.Lookup([]byte(context)), Data: context, } nodes, err := ParseFragment(strings.NewReader(text), contextNode) if err != nil { return err } doc = &Node{ Type: DocumentNode, } for _, n := range nodes { doc.AppendChild(n) } } if err := checkTreeConsistency(doc); err != nil { return err } got, err := dump(doc) if err != nil { return err } // Compare the parsed tree to the #document section. if got != want { return fmt.Errorf("got vs want:\n----\n%s----\n%s----", got, want) } if renderTestBlacklist[text] || context != "" { return nil } // Check that rendering and re-parsing results in an identical tree. pr, pw := io.Pipe() go func() { pw.CloseWithError(Render(pw, doc)) }() doc1, err := Parse(pr) if err != nil { return err } got1, err := dump(doc1) if err != nil { return err } if got != got1 { return fmt.Errorf("got vs got1:\n----\n%s----\n%s----", got, got1) } return nil } // Some test input result in parse trees are not 'well-formed' despite // following the HTML5 recovery algorithms. Rendering and re-parsing such a // tree will not result in an exact clone of that tree. We blacklist such // inputs from the render test. var renderTestBlacklist = map[string]bool{ // The second will be reparented to the first 's parent. This // results in an whose parent is an , which is not 'well-formed'. `
XCY`: true, // The same thing with a

: `

`: true, // More cases of being reparented: `aba
brx
aoe`: true, `

`: true, `
`: true, // A similar reparenting situation involving : `123`: true, // A element is reparented, putting it before a table. // A <plaintext> element can't have anything after it in HTML. `<table><plaintext><td>`: true, `<!doctype html><table><plaintext></plaintext>`: true, `<!doctype html><table><tbody><plaintext></plaintext>`: true, `<!doctype html><table><tbody><tr><plaintext></plaintext>`: true, // A form inside a table inside a form doesn't work either. `<!doctype html><form><table></form><form></table></form>`: true, // A script that ends at EOF may escape its own closing tag when rendered. `<!doctype html><script><!--<script `: true, `<!doctype html><script><!--<script <`: true, `<!doctype html><script><!--<script <a`: true, `<!doctype html><script><!--<script </`: true, `<!doctype html><script><!--<script </s`: true, `<!doctype html><script><!--<script </script`: true, `<!doctype html><script><!--<script </scripta`: true, `<!doctype html><script><!--<script -`: true, `<!doctype html><script><!--<script -a`: true, `<!doctype html><script><!--<script -<`: true, `<!doctype html><script><!--<script --`: true, `<!doctype html><script><!--<script --a`: true, `<!doctype html><script><!--<script --<`: true, `<script><!--<script `: true, `<script><!--<script <a`: true, `<script><!--<script </script`: true, `<script><!--<script </scripta`: true, `<script><!--<script -`: true, `<script><!--<script -a`: true, `<script><!--<script --`: true, `<script><!--<script --a`: true, `<script><!--<script <`: true, `<script><!--<script </`: true, `<script><!--<script </s`: true, // Reconstructing the active formatting elements results in a <plaintext> // element that contains an <a> element. `<!doctype html><p><a><plaintext>b`: true, } func TestNodeConsistency(t *testing.T) { // inconsistentNode is a Node whose DataAtom and Data do not agree. inconsistentNode := &Node{ Type: ElementNode, DataAtom: atom.Frameset, Data: "table", } _, err := ParseFragment(strings.NewReader("<p>hello</p>"), inconsistentNode) if err == nil { t.Errorf("got nil error, want non-nil") } } func BenchmarkParser(b *testing.B) { buf, err := ioutil.ReadFile("testdata/go1.html") if err != nil { b.Fatalf("could not read testdata/go1.html: %v", err) } b.SetBytes(int64(len(buf))) runtime.GC() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { Parse(bytes.NewBuffer(buf)) } } �������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/atom/��������������������������������������������0000755�0000153�0000161�00000000000�12321735652�022536� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/atom/gen.go��������������������������������������0000644�0000153�0000161�00000023733�12321735652�023646� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ignore package main // This program generates table.go and table_test.go. // Invoke as // // go run gen.go |gofmt >table.go // go run gen.go -test |gofmt >table_test.go import ( "flag" "fmt" "math/rand" "os" "sort" "strings" ) // identifier converts s to a Go exported identifier. // It converts "div" to "Div" and "accept-charset" to "AcceptCharset". func identifier(s string) string { b := make([]byte, 0, len(s)) cap := true for _, c := range s { if c == '-' { cap = true continue } if cap && 'a' <= c && c <= 'z' { c -= 'a' - 'A' } cap = false b = append(b, byte(c)) } return string(b) } var test = flag.Bool("test", false, "generate table_test.go") func main() { flag.Parse() var all []string all = append(all, elements...) all = append(all, attributes...) all = append(all, eventHandlers...) all = append(all, extra...) sort.Strings(all) if *test { fmt.Printf("// generated by go run gen.go -test; DO NOT EDIT\n\n") fmt.Printf("package atom\n\n") fmt.Printf("var testAtomList = []string{\n") for _, s := range all { fmt.Printf("\t%q,\n", s) } fmt.Printf("}\n") return } // uniq - lists have dups // compute max len too maxLen := 0 w := 0 for _, s := range all { if w == 0 || all[w-1] != s { if maxLen < len(s) { maxLen = len(s) } all[w] = s w++ } } all = all[:w] // Find hash that minimizes table size. var best *table for i := 0; i < 1000000; i++ { if best != nil && 1<<(best.k-1) < len(all) { break } h := rand.Uint32() for k := uint(0); k <= 16; k++ { if best != nil && k >= best.k { break } var t table if t.init(h, k, all) { best = &t break } } } if best == nil { fmt.Fprintf(os.Stderr, "failed to construct string table\n") os.Exit(1) } // Lay out strings, using overlaps when possible. layout := append([]string{}, all...) // Remove strings that are substrings of other strings for changed := true; changed; { changed = false for i, s := range layout { if s == "" { continue } for j, t := range layout { if i != j && t != "" && strings.Contains(s, t) { changed = true layout[j] = "" } } } } // Join strings where one suffix matches another prefix. for { // Find best i, j, k such that layout[i][len-k:] == layout[j][:k], // maximizing overlap length k. besti := -1 bestj := -1 bestk := 0 for i, s := range layout { if s == "" { continue } for j, t := range layout { if i == j { continue } for k := bestk + 1; k <= len(s) && k <= len(t); k++ { if s[len(s)-k:] == t[:k] { besti = i bestj = j bestk = k } } } } if bestk > 0 { layout[besti] += layout[bestj][bestk:] layout[bestj] = "" continue } break } text := strings.Join(layout, "") atom := map[string]uint32{} for _, s := range all { off := strings.Index(text, s) if off < 0 { panic("lost string " + s) } atom[s] = uint32(off<<8 | len(s)) } // Generate the Go code. fmt.Printf("// generated by go run gen.go; DO NOT EDIT\n\n") fmt.Printf("package atom\n\nconst (\n") for _, s := range all { fmt.Printf("\t%s Atom = %#x\n", identifier(s), atom[s]) } fmt.Printf(")\n\n") fmt.Printf("const hash0 = %#x\n\n", best.h0) fmt.Printf("const maxAtomLen = %d\n\n", maxLen) fmt.Printf("var table = [1<<%d]Atom{\n", best.k) for i, s := range best.tab { if s == "" { continue } fmt.Printf("\t%#x: %#x, // %s\n", i, atom[s], s) } fmt.Printf("}\n") datasize := (1 << best.k) * 4 fmt.Printf("const atomText =\n") textsize := len(text) for len(text) > 60 { fmt.Printf("\t%q +\n", text[:60]) text = text[60:] } fmt.Printf("\t%q\n\n", text) fmt.Fprintf(os.Stderr, "%d atoms; %d string bytes + %d tables = %d total data\n", len(all), textsize, datasize, textsize+datasize) } type byLen []string func (x byLen) Less(i, j int) bool { return len(x[i]) > len(x[j]) } func (x byLen) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byLen) Len() int { return len(x) } // fnv computes the FNV hash with an arbitrary starting value h. func fnv(h uint32, s string) uint32 { for i := 0; i < len(s); i++ { h ^= uint32(s[i]) h *= 16777619 } return h } // A table represents an attempt at constructing the lookup table. // The lookup table uses cuckoo hashing, meaning that each string // can be found in one of two positions. type table struct { h0 uint32 k uint mask uint32 tab []string } // hash returns the two hashes for s. func (t *table) hash(s string) (h1, h2 uint32) { h := fnv(t.h0, s) h1 = h & t.mask h2 = (h >> 16) & t.mask return } // init initializes the table with the given parameters. // h0 is the initial hash value, // k is the number of bits of hash value to use, and // x is the list of strings to store in the table. // init returns false if the table cannot be constructed. func (t *table) init(h0 uint32, k uint, x []string) bool { t.h0 = h0 t.k = k t.tab = make([]string, 1<<k) t.mask = 1<<k - 1 for _, s := range x { if !t.insert(s) { return false } } return true } // insert inserts s in the table. func (t *table) insert(s string) bool { h1, h2 := t.hash(s) if t.tab[h1] == "" { t.tab[h1] = s return true } if t.tab[h2] == "" { t.tab[h2] = s return true } if t.push(h1, 0) { t.tab[h1] = s return true } if t.push(h2, 0) { t.tab[h2] = s return true } return false } // push attempts to push aside the entry in slot i. func (t *table) push(i uint32, depth int) bool { if depth > len(t.tab) { return false } s := t.tab[i] h1, h2 := t.hash(s) j := h1 + h2 - i if t.tab[j] != "" && !t.push(j, depth+1) { return false } t.tab[j] = s return true } // The lists of element names and attribute keys were taken from // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-index.html // as of the "HTML Living Standard - Last Updated 30 May 2012" version. var elements = []string{ "a", "abbr", "address", "area", "article", "aside", "audio", "b", "base", "bdi", "bdo", "blockquote", "body", "br", "button", "canvas", "caption", "cite", "code", "col", "colgroup", "command", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "img", "input", "ins", "kbd", "keygen", "label", "legend", "li", "link", "map", "mark", "menu", "meta", "meter", "nav", "noscript", "object", "ol", "optgroup", "option", "output", "p", "param", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "small", "source", "span", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "u", "ul", "var", "video", "wbr", } var attributes = []string{ "accept", "accept-charset", "accesskey", "action", "alt", "async", "autocomplete", "autofocus", "autoplay", "border", "challenge", "charset", "checked", "cite", "class", "cols", "colspan", "command", "content", "contenteditable", "contextmenu", "controls", "coords", "crossorigin", "data", "datetime", "default", "defer", "dir", "dirname", "disabled", "download", "draggable", "dropzone", "enctype", "for", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget", "headers", "height", "hidden", "high", "href", "hreflang", "http-equiv", "icon", "id", "inert", "ismap", "itemid", "itemprop", "itemref", "itemscope", "itemtype", "keytype", "kind", "label", "lang", "list", "loop", "low", "manifest", "max", "maxlength", "media", "mediagroup", "method", "min", "multiple", "muted", "name", "novalidate", "open", "optimum", "pattern", "ping", "placeholder", "poster", "preload", "radiogroup", "readonly", "rel", "required", "reversed", "rows", "rowspan", "sandbox", "spellcheck", "scope", "scoped", "seamless", "selected", "shape", "size", "sizes", "span", "src", "srcdoc", "srclang", "start", "step", "style", "tabindex", "target", "title", "translate", "type", "typemustmatch", "usemap", "value", "width", "wrap", } var eventHandlers = []string{ "onabort", "onafterprint", "onbeforeprint", "onbeforeunload", "onblur", "oncancel", "oncanplay", "oncanplaythrough", "onchange", "onclick", "onclose", "oncontextmenu", "oncuechange", "ondblclick", "ondrag", "ondragend", "ondragenter", "ondragleave", "ondragover", "ondragstart", "ondrop", "ondurationchange", "onemptied", "onended", "onerror", "onfocus", "onhashchange", "oninput", "oninvalid", "onkeydown", "onkeypress", "onkeyup", "onload", "onloadeddata", "onloadedmetadata", "onloadstart", "onmessage", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onmousewheel", "onoffline", "ononline", "onpagehide", "onpageshow", "onpause", "onplay", "onplaying", "onpopstate", "onprogress", "onratechange", "onreset", "onresize", "onscroll", "onseeked", "onseeking", "onselect", "onshow", "onstalled", "onstorage", "onsubmit", "onsuspend", "ontimeupdate", "onunload", "onvolumechange", "onwaiting", } // extra are ad-hoc values not covered by any of the lists above. var extra = []string{ "align", "annotation", "annotation-xml", "applet", "basefont", "bgsound", "big", "blink", "center", "color", "desc", "face", "font", "foreignObject", // HTML is case-insensitive, but SVG-embedded-in-HTML is case-sensitive. "foreignobject", "frame", "frameset", "image", "isindex", "listing", "malignmark", "marquee", "math", "mglyph", "mi", "mn", "mo", "ms", "mtext", "nobr", "noembed", "noframes", "plaintext", "prompt", "public", "spacer", "strike", "svg", "system", "tt", "xmp", } �������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/atom/atom.go�������������������������������������0000644�0000153�0000161�00000004352�12321735652�024031� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 atom provides integer codes (also known as atoms) for a fixed set of // frequently occurring HTML strings: tag names and attribute keys such as "p" // and "id". // // Sharing an atom's name between all elements with the same tag can result in // fewer string allocations when tokenizing and parsing HTML. Integer // comparisons are also generally faster than string comparisons. // // The value of an atom's particular code is not guaranteed to stay the same // between versions of this package. Neither is any ordering guaranteed: // whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to // be dense. The only guarantees are that e.g. looking up "div" will yield // atom.Div, calling atom.Div.String will return "div", and atom.Div != 0. package atom // Atom is an integer code for a string. The zero value maps to "". type Atom uint32 // String returns the atom's name. func (a Atom) String() string { start := uint32(a >> 8) n := uint32(a & 0xff) if start+n > uint32(len(atomText)) { return "" } return atomText[start : start+n] } func (a Atom) string() string { return atomText[a>>8 : a>>8+a&0xff] } // fnv computes the FNV hash with an arbitrary starting value h. func fnv(h uint32, s []byte) uint32 { for i := range s { h ^= uint32(s[i]) h *= 16777619 } return h } func match(s string, t []byte) bool { for i, c := range t { if s[i] != c { return false } } return true } // Lookup returns the atom whose name is s. It returns zero if there is no // such atom. The lookup is case sensitive. func Lookup(s []byte) Atom { if len(s) == 0 || len(s) > maxAtomLen { return 0 } h := fnv(hash0, s) if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { return a } if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { return a } return 0 } // String returns a string whose contents are equal to s. In that sense, it is // equivalent to string(s) but may be more efficient. func String(s []byte) string { if a := Lookup(s); a != 0 { return a.String() } return string(s) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/atom/table.go������������������������������������0000644�0000153�0000161�00000052011�12321735652�024153� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// generated by go run gen.go; DO NOT EDIT package atom const ( A Atom = 0x1 Abbr Atom = 0x4 Accept Atom = 0x2106 AcceptCharset Atom = 0x210e Accesskey Atom = 0x3309 Action Atom = 0x21b06 Address Atom = 0x5d507 Align Atom = 0x1105 Alt Atom = 0x4503 Annotation Atom = 0x18d0a AnnotationXml Atom = 0x18d0e Applet Atom = 0x2d106 Area Atom = 0x31804 Article Atom = 0x39907 Aside Atom = 0x4f05 Async Atom = 0x9305 Audio Atom = 0xaf05 Autocomplete Atom = 0xd50c Autofocus Atom = 0xe109 Autoplay Atom = 0x10c08 B Atom = 0x101 Base Atom = 0x11404 Basefont Atom = 0x11408 Bdi Atom = 0x1a03 Bdo Atom = 0x12503 Bgsound Atom = 0x13807 Big Atom = 0x14403 Blink Atom = 0x14705 Blockquote Atom = 0x14c0a Body Atom = 0x2f04 Border Atom = 0x15606 Br Atom = 0x202 Button Atom = 0x15c06 Canvas Atom = 0x4b06 Caption Atom = 0x1e007 Center Atom = 0x2df06 Challenge Atom = 0x23e09 Charset Atom = 0x2807 Checked Atom = 0x33f07 Cite Atom = 0x9704 Class Atom = 0x3d905 Code Atom = 0x16f04 Col Atom = 0x17603 Colgroup Atom = 0x17608 Color Atom = 0x18305 Cols Atom = 0x18804 Colspan Atom = 0x18807 Command Atom = 0x19b07 Content Atom = 0x42c07 Contenteditable Atom = 0x42c0f Contextmenu Atom = 0x3480b Controls Atom = 0x1ae08 Coords Atom = 0x1ba06 Crossorigin Atom = 0x1c40b Data Atom = 0x44304 Datalist Atom = 0x44308 Datetime Atom = 0x25b08 Dd Atom = 0x28802 Default Atom = 0x5207 Defer Atom = 0x17105 Del Atom = 0x4d603 Desc Atom = 0x4804 Details Atom = 0x6507 Dfn Atom = 0x8303 Dialog Atom = 0x1b06 Dir Atom = 0x9d03 Dirname Atom = 0x9d07 Disabled Atom = 0x10008 Div Atom = 0x10703 Dl Atom = 0x13e02 Download Atom = 0x40908 Draggable Atom = 0x1a109 Dropzone Atom = 0x3a208 Dt Atom = 0x4e402 Em Atom = 0x7f02 Embed Atom = 0x7f05 Enctype Atom = 0x23007 Face Atom = 0x2dd04 Fieldset Atom = 0x1d508 Figcaption Atom = 0x1dd0a Figure Atom = 0x1f106 Font Atom = 0x11804 Footer Atom = 0x5906 For Atom = 0x1fd03 ForeignObject Atom = 0x1fd0d Foreignobject Atom = 0x20a0d Form Atom = 0x21704 Formaction Atom = 0x2170a Formenctype Atom = 0x22c0b Formmethod Atom = 0x2470a Formnovalidate Atom = 0x2510e Formtarget Atom = 0x2660a Frame Atom = 0x8705 Frameset Atom = 0x8708 H1 Atom = 0x13602 H2 Atom = 0x29602 H3 Atom = 0x2c502 H4 Atom = 0x30e02 H5 Atom = 0x4e602 H6 Atom = 0x27002 Head Atom = 0x2fa04 Header Atom = 0x2fa06 Headers Atom = 0x2fa07 Height Atom = 0x27206 Hgroup Atom = 0x27a06 Hidden Atom = 0x28606 High Atom = 0x29304 Hr Atom = 0x13102 Href Atom = 0x29804 Hreflang Atom = 0x29808 Html Atom = 0x27604 HttpEquiv Atom = 0x2a00a I Atom = 0x601 Icon Atom = 0x42b04 Id Atom = 0x5102 Iframe Atom = 0x2b406 Image Atom = 0x2ba05 Img Atom = 0x2bf03 Inert Atom = 0x4c105 Input Atom = 0x3f605 Ins Atom = 0x1cd03 Isindex Atom = 0x2c707 Ismap Atom = 0x2ce05 Itemid Atom = 0x9806 Itemprop Atom = 0x57e08 Itemref Atom = 0x2d707 Itemscope Atom = 0x2e509 Itemtype Atom = 0x2ef08 Kbd Atom = 0x1903 Keygen Atom = 0x3906 Keytype Atom = 0x51207 Kind Atom = 0xfd04 Label Atom = 0xba05 Lang Atom = 0x29c04 Legend Atom = 0x1a806 Li Atom = 0x1202 Link Atom = 0x14804 List Atom = 0x44704 Listing Atom = 0x44707 Loop Atom = 0xbe04 Low Atom = 0x13f03 Malignmark Atom = 0x100a Manifest Atom = 0x5b608 Map Atom = 0x2d003 Mark Atom = 0x1604 Marquee Atom = 0x5f207 Math Atom = 0x2f704 Max Atom = 0x30603 Maxlength Atom = 0x30609 Media Atom = 0xa205 Mediagroup Atom = 0xa20a Menu Atom = 0x34f04 Meta Atom = 0x45604 Meter Atom = 0x26105 Method Atom = 0x24b06 Mglyph Atom = 0x2c006 Mi Atom = 0x9b02 Min Atom = 0x31003 Mn Atom = 0x25402 Mo Atom = 0x47a02 Ms Atom = 0x2e802 Mtext Atom = 0x31305 Multiple Atom = 0x32108 Muted Atom = 0x32905 Name Atom = 0xa004 Nav Atom = 0x3e03 Nobr Atom = 0x7404 Noembed Atom = 0x7d07 Noframes Atom = 0x8508 Noscript Atom = 0x28b08 Novalidate Atom = 0x2550a Object Atom = 0x21106 Ol Atom = 0xcd02 Onabort Atom = 0x16007 Onafterprint Atom = 0x1e50c Onbeforeprint Atom = 0x21f0d Onbeforeunload Atom = 0x5c90e Onblur Atom = 0x3e206 Oncancel Atom = 0xb308 Oncanplay Atom = 0x12709 Oncanplaythrough Atom = 0x12710 Onchange Atom = 0x3b808 Onclick Atom = 0x2ad07 Onclose Atom = 0x32e07 Oncontextmenu Atom = 0x3460d Oncuechange Atom = 0x3530b Ondblclick Atom = 0x35e0a Ondrag Atom = 0x36806 Ondragend Atom = 0x36809 Ondragenter Atom = 0x3710b Ondragleave Atom = 0x37c0b Ondragover Atom = 0x3870a Ondragstart Atom = 0x3910b Ondrop Atom = 0x3a006 Ondurationchange Atom = 0x3b010 Onemptied Atom = 0x3a709 Onended Atom = 0x3c007 Onerror Atom = 0x3c707 Onfocus Atom = 0x3ce07 Onhashchange Atom = 0x3e80c Oninput Atom = 0x3f407 Oninvalid Atom = 0x3fb09 Onkeydown Atom = 0x40409 Onkeypress Atom = 0x4110a Onkeyup Atom = 0x42107 Onload Atom = 0x43b06 Onloadeddata Atom = 0x43b0c Onloadedmetadata Atom = 0x44e10 Onloadstart Atom = 0x4640b Onmessage Atom = 0x46f09 Onmousedown Atom = 0x4780b Onmousemove Atom = 0x4830b Onmouseout Atom = 0x48e0a Onmouseover Atom = 0x49b0b Onmouseup Atom = 0x4a609 Onmousewheel Atom = 0x4af0c Onoffline Atom = 0x4bb09 Ononline Atom = 0x4c608 Onpagehide Atom = 0x4ce0a Onpageshow Atom = 0x4d90a Onpause Atom = 0x4e807 Onplay Atom = 0x4f206 Onplaying Atom = 0x4f209 Onpopstate Atom = 0x4fb0a Onprogress Atom = 0x5050a Onratechange Atom = 0x5190c Onreset Atom = 0x52507 Onresize Atom = 0x52c08 Onscroll Atom = 0x53a08 Onseeked Atom = 0x54208 Onseeking Atom = 0x54a09 Onselect Atom = 0x55308 Onshow Atom = 0x55d06 Onstalled Atom = 0x56609 Onstorage Atom = 0x56f09 Onsubmit Atom = 0x57808 Onsuspend Atom = 0x58809 Ontimeupdate Atom = 0x1190c Onunload Atom = 0x59108 Onvolumechange Atom = 0x5990e Onwaiting Atom = 0x5a709 Open Atom = 0x58404 Optgroup Atom = 0xc008 Optimum Atom = 0x5b007 Option Atom = 0x5c506 Output Atom = 0x49506 P Atom = 0xc01 Param Atom = 0xc05 Pattern Atom = 0x6e07 Ping Atom = 0xab04 Placeholder Atom = 0xc70b Plaintext Atom = 0xf109 Poster Atom = 0x17d06 Pre Atom = 0x27f03 Preload Atom = 0x27f07 Progress Atom = 0x50708 Prompt Atom = 0x5bf06 Public Atom = 0x42706 Q Atom = 0x15101 Radiogroup Atom = 0x30a Readonly Atom = 0x31908 Rel Atom = 0x28003 Required Atom = 0x1f508 Reversed Atom = 0x5e08 Rows Atom = 0x7704 Rowspan Atom = 0x7707 Rp Atom = 0x1eb02 Rt Atom = 0x16502 Ruby Atom = 0xd104 S Atom = 0x2c01 Samp Atom = 0x6b04 Sandbox Atom = 0xe907 Scope Atom = 0x2e905 Scoped Atom = 0x2e906 Script Atom = 0x28d06 Seamless Atom = 0x33308 Section Atom = 0x3dd07 Select Atom = 0x55506 Selected Atom = 0x55508 Shape Atom = 0x1b505 Size Atom = 0x53004 Sizes Atom = 0x53005 Small Atom = 0x1bf05 Source Atom = 0x1cf06 Spacer Atom = 0x30006 Span Atom = 0x7a04 Spellcheck Atom = 0x33a0a Src Atom = 0x3d403 Srcdoc Atom = 0x3d406 Srclang Atom = 0x41a07 Start Atom = 0x39705 Step Atom = 0x5bc04 Strike Atom = 0x50e06 Strong Atom = 0x53406 Style Atom = 0x5db05 Sub Atom = 0x57a03 Summary Atom = 0x5e007 Sup Atom = 0x5e703 Svg Atom = 0x5ea03 System Atom = 0x5ed06 Tabindex Atom = 0x45c08 Table Atom = 0x43605 Target Atom = 0x26a06 Tbody Atom = 0x2e05 Td Atom = 0x4702 Textarea Atom = 0x31408 Tfoot Atom = 0x5805 Th Atom = 0x13002 Thead Atom = 0x2f905 Time Atom = 0x11b04 Title Atom = 0x8e05 Tr Atom = 0xf902 Track Atom = 0xf905 Translate Atom = 0x16609 Tt Atom = 0x7002 Type Atom = 0x23304 Typemustmatch Atom = 0x2330d U Atom = 0xb01 Ul Atom = 0x5602 Usemap Atom = 0x4ec06 Value Atom = 0x4005 Var Atom = 0x10903 Video Atom = 0x2a905 Wbr Atom = 0x14103 Width Atom = 0x4e205 Wrap Atom = 0x56204 Xmp Atom = 0xef03 ) const hash0 = 0xc17da63e const maxAtomLen = 16 var table = [1 << 9]Atom{ 0x1: 0x4830b, // onmousemove 0x2: 0x5a709, // onwaiting 0x4: 0x5bf06, // prompt 0x7: 0x5b007, // optimum 0x8: 0x1604, // mark 0xa: 0x2d707, // itemref 0xb: 0x4d90a, // onpageshow 0xc: 0x55506, // select 0xd: 0x1a109, // draggable 0xe: 0x3e03, // nav 0xf: 0x19b07, // command 0x11: 0xb01, // u 0x14: 0x2fa07, // headers 0x15: 0x44308, // datalist 0x17: 0x6b04, // samp 0x1a: 0x40409, // onkeydown 0x1b: 0x53a08, // onscroll 0x1c: 0x17603, // col 0x20: 0x57e08, // itemprop 0x21: 0x2a00a, // http-equiv 0x22: 0x5e703, // sup 0x24: 0x1f508, // required 0x2b: 0x27f07, // preload 0x2c: 0x21f0d, // onbeforeprint 0x2d: 0x3710b, // ondragenter 0x2e: 0x4e402, // dt 0x2f: 0x57808, // onsubmit 0x30: 0x13102, // hr 0x31: 0x3460d, // oncontextmenu 0x33: 0x2ba05, // image 0x34: 0x4e807, // onpause 0x35: 0x27a06, // hgroup 0x36: 0xab04, // ping 0x37: 0x55308, // onselect 0x3a: 0x10703, // div 0x40: 0x9b02, // mi 0x41: 0x33308, // seamless 0x42: 0x2807, // charset 0x43: 0x5102, // id 0x44: 0x4fb0a, // onpopstate 0x45: 0x4d603, // del 0x46: 0x5f207, // marquee 0x47: 0x3309, // accesskey 0x49: 0x5906, // footer 0x4a: 0x2d106, // applet 0x4b: 0x2ce05, // ismap 0x51: 0x34f04, // menu 0x52: 0x2f04, // body 0x55: 0x8708, // frameset 0x56: 0x52507, // onreset 0x57: 0x14705, // blink 0x58: 0x8e05, // title 0x59: 0x39907, // article 0x5b: 0x13002, // th 0x5d: 0x15101, // q 0x5e: 0x58404, // open 0x5f: 0x31804, // area 0x61: 0x43b06, // onload 0x62: 0x3f605, // input 0x63: 0x11404, // base 0x64: 0x18807, // colspan 0x65: 0x51207, // keytype 0x66: 0x13e02, // dl 0x68: 0x1d508, // fieldset 0x6a: 0x31003, // min 0x6b: 0x10903, // var 0x6f: 0x2fa06, // header 0x70: 0x16502, // rt 0x71: 0x17608, // colgroup 0x72: 0x25402, // mn 0x74: 0x16007, // onabort 0x75: 0x3906, // keygen 0x76: 0x4bb09, // onoffline 0x77: 0x23e09, // challenge 0x78: 0x2d003, // map 0x7a: 0x30e02, // h4 0x7b: 0x3c707, // onerror 0x7c: 0x30609, // maxlength 0x7d: 0x31305, // mtext 0x7e: 0x5805, // tfoot 0x7f: 0x11804, // font 0x80: 0x100a, // malignmark 0x81: 0x45604, // meta 0x82: 0x9305, // async 0x83: 0x2c502, // h3 0x84: 0x28802, // dd 0x85: 0x29804, // href 0x86: 0xa20a, // mediagroup 0x87: 0x1ba06, // coords 0x88: 0x41a07, // srclang 0x89: 0x35e0a, // ondblclick 0x8a: 0x4005, // value 0x8c: 0xb308, // oncancel 0x8e: 0x33a0a, // spellcheck 0x8f: 0x8705, // frame 0x91: 0x14403, // big 0x94: 0x21b06, // action 0x95: 0x9d03, // dir 0x97: 0x31908, // readonly 0x99: 0x43605, // table 0x9a: 0x5e007, // summary 0x9b: 0x14103, // wbr 0x9c: 0x30a, // radiogroup 0x9d: 0xa004, // name 0x9f: 0x5ed06, // system 0xa1: 0x18305, // color 0xa2: 0x4b06, // canvas 0xa3: 0x27604, // html 0xa5: 0x54a09, // onseeking 0xac: 0x1b505, // shape 0xad: 0x28003, // rel 0xae: 0x12710, // oncanplaythrough 0xaf: 0x3870a, // ondragover 0xb1: 0x1fd0d, // foreignObject 0xb3: 0x7704, // rows 0xb6: 0x44707, // listing 0xb7: 0x49506, // output 0xb9: 0x3480b, // contextmenu 0xbb: 0x13f03, // low 0xbc: 0x1eb02, // rp 0xbd: 0x58809, // onsuspend 0xbe: 0x15c06, // button 0xbf: 0x4804, // desc 0xc1: 0x3dd07, // section 0xc2: 0x5050a, // onprogress 0xc3: 0x56f09, // onstorage 0xc4: 0x2f704, // math 0xc5: 0x4f206, // onplay 0xc7: 0x5602, // ul 0xc8: 0x6e07, // pattern 0xc9: 0x4af0c, // onmousewheel 0xca: 0x36809, // ondragend 0xcb: 0xd104, // ruby 0xcc: 0xc01, // p 0xcd: 0x32e07, // onclose 0xce: 0x26105, // meter 0xcf: 0x13807, // bgsound 0xd2: 0x27206, // height 0xd4: 0x101, // b 0xd5: 0x2ef08, // itemtype 0xd8: 0x1e007, // caption 0xd9: 0x10008, // disabled 0xdc: 0x5ea03, // svg 0xdd: 0x1bf05, // small 0xde: 0x44304, // data 0xe0: 0x4c608, // ononline 0xe1: 0x2c006, // mglyph 0xe3: 0x7f05, // embed 0xe4: 0xf902, // tr 0xe5: 0x4640b, // onloadstart 0xe7: 0x3b010, // ondurationchange 0xed: 0x12503, // bdo 0xee: 0x4702, // td 0xef: 0x4f05, // aside 0xf0: 0x29602, // h2 0xf1: 0x50708, // progress 0xf2: 0x14c0a, // blockquote 0xf4: 0xba05, // label 0xf5: 0x601, // i 0xf7: 0x7707, // rowspan 0xfb: 0x4f209, // onplaying 0xfd: 0x2bf03, // img 0xfe: 0xc008, // optgroup 0xff: 0x42c07, // content 0x101: 0x5190c, // onratechange 0x103: 0x3e80c, // onhashchange 0x104: 0x6507, // details 0x106: 0x40908, // download 0x109: 0xe907, // sandbox 0x10b: 0x42c0f, // contenteditable 0x10d: 0x37c0b, // ondragleave 0x10e: 0x2106, // accept 0x10f: 0x55508, // selected 0x112: 0x2170a, // formaction 0x113: 0x2df06, // center 0x115: 0x44e10, // onloadedmetadata 0x116: 0x14804, // link 0x117: 0x11b04, // time 0x118: 0x1c40b, // crossorigin 0x119: 0x3ce07, // onfocus 0x11a: 0x56204, // wrap 0x11b: 0x42b04, // icon 0x11d: 0x2a905, // video 0x11e: 0x3d905, // class 0x121: 0x5990e, // onvolumechange 0x122: 0x3e206, // onblur 0x123: 0x2e509, // itemscope 0x124: 0x5db05, // style 0x127: 0x42706, // public 0x129: 0x2510e, // formnovalidate 0x12a: 0x55d06, // onshow 0x12c: 0x16609, // translate 0x12d: 0x9704, // cite 0x12e: 0x2e802, // ms 0x12f: 0x1190c, // ontimeupdate 0x130: 0xfd04, // kind 0x131: 0x2660a, // formtarget 0x135: 0x3c007, // onended 0x136: 0x28606, // hidden 0x137: 0x2c01, // s 0x139: 0x2470a, // formmethod 0x13a: 0x44704, // list 0x13c: 0x27002, // h6 0x13d: 0xcd02, // ol 0x13e: 0x3530b, // oncuechange 0x13f: 0x20a0d, // foreignobject 0x143: 0x5c90e, // onbeforeunload 0x145: 0x3a709, // onemptied 0x146: 0x17105, // defer 0x147: 0xef03, // xmp 0x148: 0xaf05, // audio 0x149: 0x1903, // kbd 0x14c: 0x46f09, // onmessage 0x14d: 0x5c506, // option 0x14e: 0x4503, // alt 0x14f: 0x33f07, // checked 0x150: 0x10c08, // autoplay 0x152: 0x202, // br 0x153: 0x2550a, // novalidate 0x156: 0x7d07, // noembed 0x159: 0x2ad07, // onclick 0x15a: 0x4780b, // onmousedown 0x15b: 0x3b808, // onchange 0x15e: 0x3fb09, // oninvalid 0x15f: 0x2e906, // scoped 0x160: 0x1ae08, // controls 0x161: 0x32905, // muted 0x163: 0x4ec06, // usemap 0x164: 0x1dd0a, // figcaption 0x165: 0x36806, // ondrag 0x166: 0x29304, // high 0x168: 0x3d403, // src 0x169: 0x17d06, // poster 0x16b: 0x18d0e, // annotation-xml 0x16c: 0x5bc04, // step 0x16d: 0x4, // abbr 0x16e: 0x1b06, // dialog 0x170: 0x1202, // li 0x172: 0x47a02, // mo 0x175: 0x1fd03, // for 0x176: 0x1cd03, // ins 0x178: 0x53004, // size 0x17a: 0x5207, // default 0x17b: 0x1a03, // bdi 0x17c: 0x4ce0a, // onpagehide 0x17d: 0x9d07, // dirname 0x17e: 0x23304, // type 0x17f: 0x21704, // form 0x180: 0x4c105, // inert 0x181: 0x12709, // oncanplay 0x182: 0x8303, // dfn 0x183: 0x45c08, // tabindex 0x186: 0x7f02, // em 0x187: 0x29c04, // lang 0x189: 0x3a208, // dropzone 0x18a: 0x4110a, // onkeypress 0x18b: 0x25b08, // datetime 0x18c: 0x18804, // cols 0x18d: 0x1, // a 0x18e: 0x43b0c, // onloadeddata 0x191: 0x15606, // border 0x192: 0x2e05, // tbody 0x193: 0x24b06, // method 0x195: 0xbe04, // loop 0x196: 0x2b406, // iframe 0x198: 0x2fa04, // head 0x19e: 0x5b608, // manifest 0x19f: 0xe109, // autofocus 0x1a0: 0x16f04, // code 0x1a1: 0x53406, // strong 0x1a2: 0x32108, // multiple 0x1a3: 0xc05, // param 0x1a6: 0x23007, // enctype 0x1a7: 0x2dd04, // face 0x1a8: 0xf109, // plaintext 0x1a9: 0x13602, // h1 0x1aa: 0x56609, // onstalled 0x1ad: 0x28d06, // script 0x1ae: 0x30006, // spacer 0x1af: 0x52c08, // onresize 0x1b0: 0x49b0b, // onmouseover 0x1b1: 0x59108, // onunload 0x1b2: 0x54208, // onseeked 0x1b4: 0x2330d, // typemustmatch 0x1b5: 0x1f106, // figure 0x1b6: 0x48e0a, // onmouseout 0x1b7: 0x27f03, // pre 0x1b8: 0x4e205, // width 0x1bb: 0x7404, // nobr 0x1be: 0x7002, // tt 0x1bf: 0x1105, // align 0x1c0: 0x3f407, // oninput 0x1c3: 0x42107, // onkeyup 0x1c6: 0x1e50c, // onafterprint 0x1c7: 0x210e, // accept-charset 0x1c8: 0x9806, // itemid 0x1cb: 0x50e06, // strike 0x1cc: 0x57a03, // sub 0x1cd: 0xf905, // track 0x1ce: 0x39705, // start 0x1d0: 0x11408, // basefont 0x1d6: 0x1cf06, // source 0x1d7: 0x1a806, // legend 0x1d8: 0x2f905, // thead 0x1da: 0x2e905, // scope 0x1dd: 0x21106, // object 0x1de: 0xa205, // media 0x1df: 0x18d0a, // annotation 0x1e0: 0x22c0b, // formenctype 0x1e2: 0x28b08, // noscript 0x1e4: 0x53005, // sizes 0x1e5: 0xd50c, // autocomplete 0x1e6: 0x7a04, // span 0x1e7: 0x8508, // noframes 0x1e8: 0x26a06, // target 0x1e9: 0x3a006, // ondrop 0x1ea: 0x3d406, // srcdoc 0x1ec: 0x5e08, // reversed 0x1f0: 0x2c707, // isindex 0x1f3: 0x29808, // hreflang 0x1f5: 0x4e602, // h5 0x1f6: 0x5d507, // address 0x1fa: 0x30603, // max 0x1fb: 0xc70b, // placeholder 0x1fc: 0x31408, // textarea 0x1fe: 0x4a609, // onmouseup 0x1ff: 0x3910b, // ondragstart } const atomText = "abbradiogrouparamalignmarkbdialogaccept-charsetbodyaccesskey" + "genavaluealtdescanvasidefaultfootereversedetailsampatternobr" + "owspanoembedfnoframesetitleasyncitemidirnamediagroupingaudio" + "ncancelabelooptgrouplaceholderubyautocompleteautofocusandbox" + "mplaintextrackindisabledivarautoplaybasefontimeupdatebdoncan" + "playthrough1bgsoundlowbrbigblinkblockquoteborderbuttonabortr" + "anslatecodefercolgroupostercolorcolspannotation-xmlcommandra" + "ggablegendcontrolshapecoordsmallcrossoriginsourcefieldsetfig" + "captionafterprintfigurequiredforeignObjectforeignobjectforma" + "ctionbeforeprintformenctypemustmatchallengeformmethodformnov" + "alidatetimeterformtargeth6heightmlhgroupreloadhiddenoscripth" + "igh2hreflanghttp-equivideonclickiframeimageimglyph3isindexis" + "mappletitemrefacenteritemscopeditemtypematheaderspacermaxlen" + "gth4minmtextareadonlymultiplemutedoncloseamlesspellcheckedon" + "contextmenuoncuechangeondblclickondragendondragenterondragle" + "aveondragoverondragstarticleondropzonemptiedondurationchange" + "onendedonerroronfocusrcdoclassectionbluronhashchangeoninputo" + "ninvalidonkeydownloadonkeypressrclangonkeyupublicontentedita" + "bleonloadeddatalistingonloadedmetadatabindexonloadstartonmes" + "sageonmousedownonmousemoveonmouseoutputonmouseoveronmouseupo" + "nmousewheelonofflinertononlineonpagehidelonpageshowidth5onpa" + "usemaponplayingonpopstateonprogresstrikeytypeonratechangeonr" + "esetonresizestrongonscrollonseekedonseekingonselectedonshowr" + "aponstalledonstorageonsubmitempropenonsuspendonunloadonvolum" + "echangeonwaitingoptimumanifestepromptoptionbeforeunloaddress" + "tylesummarysupsvgsystemarquee" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/atom/table_test.go�������������������������������0000644�0000153�0000161�00000007505�12321735652�025222� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// generated by go run gen.go -test; DO NOT EDIT package atom var testAtomList = []string{ "a", "abbr", "accept", "accept-charset", "accesskey", "action", "address", "align", "alt", "annotation", "annotation-xml", "applet", "area", "article", "aside", "async", "audio", "autocomplete", "autofocus", "autoplay", "b", "base", "basefont", "bdi", "bdo", "bgsound", "big", "blink", "blockquote", "body", "border", "br", "button", "canvas", "caption", "center", "challenge", "charset", "checked", "cite", "cite", "class", "code", "col", "colgroup", "color", "cols", "colspan", "command", "command", "content", "contenteditable", "contextmenu", "controls", "coords", "crossorigin", "data", "data", "datalist", "datetime", "dd", "default", "defer", "del", "desc", "details", "dfn", "dialog", "dir", "dirname", "disabled", "div", "dl", "download", "draggable", "dropzone", "dt", "em", "embed", "enctype", "face", "fieldset", "figcaption", "figure", "font", "footer", "for", "foreignObject", "foreignobject", "form", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget", "frame", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "headers", "height", "hgroup", "hidden", "high", "hr", "href", "hreflang", "html", "http-equiv", "i", "icon", "id", "iframe", "image", "img", "inert", "input", "ins", "isindex", "ismap", "itemid", "itemprop", "itemref", "itemscope", "itemtype", "kbd", "keygen", "keytype", "kind", "label", "label", "lang", "legend", "li", "link", "list", "listing", "loop", "low", "malignmark", "manifest", "map", "mark", "marquee", "math", "max", "maxlength", "media", "mediagroup", "menu", "meta", "meter", "method", "mglyph", "mi", "min", "mn", "mo", "ms", "mtext", "multiple", "muted", "name", "nav", "nobr", "noembed", "noframes", "noscript", "novalidate", "object", "ol", "onabort", "onafterprint", "onbeforeprint", "onbeforeunload", "onblur", "oncancel", "oncanplay", "oncanplaythrough", "onchange", "onclick", "onclose", "oncontextmenu", "oncuechange", "ondblclick", "ondrag", "ondragend", "ondragenter", "ondragleave", "ondragover", "ondragstart", "ondrop", "ondurationchange", "onemptied", "onended", "onerror", "onfocus", "onhashchange", "oninput", "oninvalid", "onkeydown", "onkeypress", "onkeyup", "onload", "onloadeddata", "onloadedmetadata", "onloadstart", "onmessage", "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onmousewheel", "onoffline", "ononline", "onpagehide", "onpageshow", "onpause", "onplay", "onplaying", "onpopstate", "onprogress", "onratechange", "onreset", "onresize", "onscroll", "onseeked", "onseeking", "onselect", "onshow", "onstalled", "onstorage", "onsubmit", "onsuspend", "ontimeupdate", "onunload", "onvolumechange", "onwaiting", "open", "optgroup", "optimum", "option", "output", "p", "param", "pattern", "ping", "placeholder", "plaintext", "poster", "pre", "preload", "progress", "prompt", "public", "q", "radiogroup", "readonly", "rel", "required", "reversed", "rows", "rowspan", "rp", "rt", "ruby", "s", "samp", "sandbox", "scope", "scoped", "script", "seamless", "section", "select", "selected", "shape", "size", "sizes", "small", "source", "spacer", "span", "span", "spellcheck", "src", "srcdoc", "srclang", "start", "step", "strike", "strong", "style", "style", "sub", "summary", "sup", "svg", "system", "tabindex", "table", "target", "tbody", "td", "textarea", "tfoot", "th", "thead", "time", "title", "title", "tr", "track", "translate", "tt", "type", "typemustmatch", "u", "ul", "usemap", "value", "var", "video", "wbr", "width", "wrap", "xmp", } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/atom/atom_test.go��������������������������������0000644�0000153�0000161�00000004162�12321735652�025067� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 atom import ( "sort" "testing" ) func TestKnown(t *testing.T) { for _, s := range testAtomList { if atom := Lookup([]byte(s)); atom.String() != s { t.Errorf("Lookup(%q) = %#x (%q)", s, uint32(atom), atom.String()) } } } func TestHits(t *testing.T) { for _, a := range table { if a == 0 { continue } got := Lookup([]byte(a.String())) if got != a { t.Errorf("Lookup(%q) = %#x, want %#x", a.String(), uint32(got), uint32(a)) } } } func TestMisses(t *testing.T) { testCases := []string{ "", "\x00", "\xff", "A", "DIV", "Div", "dIV", "aa", "a\x00", "ab", "abb", "abbr0", "abbr ", " abbr", " a", "acceptcharset", "acceptCharset", "accept_charset", "h0", "h1h2", "h7", "onClick", "λ", // The following string has the same hash (0xa1d7fab7) as "onmouseover". "\x00\x00\x00\x00\x00\x50\x18\xae\x38\xd0\xb7", } for _, tc := range testCases { got := Lookup([]byte(tc)) if got != 0 { t.Errorf("Lookup(%q): got %d, want 0", tc, got) } } } func TestForeignObject(t *testing.T) { const ( afo = Foreignobject afO = ForeignObject sfo = "foreignobject" sfO = "foreignObject" ) if got := Lookup([]byte(sfo)); got != afo { t.Errorf("Lookup(%q): got %#v, want %#v", sfo, got, afo) } if got := Lookup([]byte(sfO)); got != afO { t.Errorf("Lookup(%q): got %#v, want %#v", sfO, got, afO) } if got := afo.String(); got != sfo { t.Errorf("Atom(%#v).String(): got %q, want %q", afo, got, sfo) } if got := afO.String(); got != sfO { t.Errorf("Atom(%#v).String(): got %q, want %q", afO, got, sfO) } } func BenchmarkLookup(b *testing.B) { sortedTable := make([]string, 0, len(table)) for _, a := range table { if a != 0 { sortedTable = append(sortedTable, a.String()) } } sort.Strings(sortedTable) x := make([][]byte, 1000) for i := range x { x[i] = []byte(sortedTable[i%len(sortedTable)]) } b.ResetTimer() for i := 0; i < b.N; i++ { for _, s := range x { Lookup(s) } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/token_test.go������������������������������������0000644�0000153�0000161�00000034365�12321735652�024317� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 html import ( "bytes" "io" "io/ioutil" "reflect" "runtime" "strings" "testing" ) type tokenTest struct { // A short description of the test case. desc string // The HTML to parse. html string // The string representations of the expected tokens, joined by '$'. golden string } var tokenTests = []tokenTest{ { "empty", "", "", }, // A single text node. The tokenizer should not break text nodes on whitespace, // nor should it normalize whitespace within a text node. { "text", "foo bar", "foo bar", }, // An entity. { "entity", "one &lt; two", "one &lt; two", }, // A start, self-closing and end tag. The tokenizer does not care if the start // and end tokens don't match; that is the job of the parser. { "tags", "<a>b<c/>d</e>", "<a>$b$<c/>$d$</e>", }, // Angle brackets that aren't a tag. { "not a tag #0", "<", "&lt;", }, { "not a tag #1", "</", "&lt;/", }, { "not a tag #2", "</>", "<!---->", }, { "not a tag #3", "a</>b", "a$<!---->$b", }, { "not a tag #4", "</ >", "<!-- -->", }, { "not a tag #5", "</.", "<!--.-->", }, { "not a tag #6", "</.>", "<!--.-->", }, { "not a tag #7", "a < b", "a &lt; b", }, { "not a tag #8", "<.>", "&lt;.&gt;", }, { "not a tag #9", "a<<<b>>>c", "a&lt;&lt;$<b>$&gt;&gt;c", }, { "not a tag #10", "if x<0 and y < 0 then x*y>0", "if x&lt;0 and y &lt; 0 then x*y&gt;0", }, // EOF in a tag name. { "tag name eof #0", "<a", "", }, { "tag name eof #1", "<a ", "", }, { "tag name eof #2", "a<b", "a", }, { "tag name eof #3", "<a><b", "<a>", }, { "tag name eof #4", `<a x`, ``, }, // Some malformed tags that are missing a '>'. { "malformed tag #0", `<p</p>`, `<p< p="">`, }, { "malformed tag #1", `<p </p>`, `<p <="" p="">`, }, { "malformed tag #2", `<p id`, ``, }, { "malformed tag #3", `<p id=`, ``, }, { "malformed tag #4", `<p id=>`, `<p id="">`, }, { "malformed tag #5", `<p id=0`, ``, }, { "malformed tag #6", `<p id=0</p>`, `<p id="0&lt;/p">`, }, { "malformed tag #7", `<p id="0</p>`, ``, }, { "malformed tag #8", `<p id="0"</p>`, `<p id="0" <="" p="">`, }, { "malformed tag #9", `<p></p id`, `<p>`, }, // Raw text and RCDATA. { "basic raw text", "<script><a></b></script>", "<script>$&lt;a&gt;&lt;/b&gt;$</script>", }, { "unfinished script end tag", "<SCRIPT>a</SCR", "<script>$a&lt;/SCR", }, { "broken script end tag", "<SCRIPT>a</SCR ipt>", "<script>$a&lt;/SCR ipt&gt;", }, { "EOF in script end tag", "<SCRIPT>a</SCRipt", "<script>$a&lt;/SCRipt", }, { "scriptx end tag", "<SCRIPT>a</SCRiptx", "<script>$a&lt;/SCRiptx", }, { "' ' completes script end tag", "<SCRIPT>a</SCRipt ", "<script>$a", }, { "'>' completes script end tag", "<SCRIPT>a</SCRipt>", "<script>$a$</script>", }, { "self-closing script end tag", "<SCRIPT>a</SCRipt/>", "<script>$a$</script>", }, { "nested script tag", "<SCRIPT>a</SCRipt<script>", "<script>$a&lt;/SCRipt&lt;script&gt;", }, { "script end tag after unfinished", "<SCRIPT>a</SCRipt</script>", "<script>$a&lt;/SCRipt$</script>", }, { "script/style mismatched tags", "<script>a</style>", "<script>$a&lt;/style&gt;", }, { "style element with entity", "<style>&apos;", "<style>$&amp;apos;", }, { "textarea with tag", "<textarea><div></textarea>", "<textarea>$&lt;div&gt;$</textarea>", }, { "title with tag and entity", "<title><b>K&amp;R C</b></title>", "<title>$&lt;b&gt;K&amp;R C&lt;/b&gt;$</title>", }, // DOCTYPE tests. { "Proper DOCTYPE", "<!DOCTYPE html>", "<!DOCTYPE html>", }, { "DOCTYPE with no space", "<!doctypehtml>", "<!DOCTYPE html>", }, { "DOCTYPE with two spaces", "<!doctype html>", "<!DOCTYPE html>", }, { "looks like DOCTYPE but isn't", "<!DOCUMENT html>", "<!--DOCUMENT html-->", }, { "DOCTYPE at EOF", "<!DOCtype", "<!DOCTYPE >", }, // XML processing instructions. { "XML processing instruction", "<?xml?>", "<!--?xml?-->", }, // Comments. { "comment0", "abc<b><!-- skipme --></b>def", "abc$<b>$<!-- skipme -->$</b>$def", }, { "comment1", "a<!-->z", "a$<!---->$z", }, { "comment2", "a<!--->z", "a$<!---->$z", }, { "comment3", "a<!--x>-->z", "a$<!--x>-->$z", }, { "comment4", "a<!--x->-->z", "a$<!--x->-->$z", }, { "comment5", "a<!>z", "a$<!---->$z", }, { "comment6", "a<!->z", "a$<!----->$z", }, { "comment7", "a<!---<>z", "a$<!---<>z-->", }, { "comment8", "a<!--z", "a$<!--z-->", }, { "comment9", "a<!--z-", "a$<!--z-->", }, { "comment10", "a<!--z--", "a$<!--z-->", }, { "comment11", "a<!--z---", "a$<!--z--->", }, { "comment12", "a<!--z----", "a$<!--z---->", }, { "comment13", "a<!--x--!>z", "a$<!--x-->$z", }, // An attribute with a backslash. { "backslash", `<p id="a\"b">`, `<p id="a\" b"="">`, }, // Entities, tag name and attribute key lower-casing, and whitespace // normalization within a tag. { "tricky", "<p \t\n iD=\"a&quot;B\" foo=\"bar\"><EM>te&lt;&amp;;xt</em></p>", `<p id="a&#34;B" foo="bar">$<em>$te&lt;&amp;;xt$</em>$</p>`, }, // A nonexistent entity. Tokenizing and converting back to a string should // escape the "&" to become "&amp;". { "noSuchEntity", `<a b="c&noSuchEntity;d">&lt;&alsoDoesntExist;&`, `<a b="c&amp;noSuchEntity;d">$&lt;&amp;alsoDoesntExist;&amp;`, }, { "entity without semicolon", `&notit;&notin;<a b="q=z&amp=5&notice=hello&not;=world">`, `¬it;∉$<a b="q=z&amp;amp=5&amp;notice=hello¬=world">`, }, { "entity with digits", "&frac12;", "½", }, // Attribute tests: // http://dev.w3.org/html5/spec/Overview.html#attributes-0 { "Empty attribute", `<input disabled FOO>`, `<input disabled="" foo="">`, }, { "Empty attribute, whitespace", `<input disabled FOO >`, `<input disabled="" foo="">`, }, { "Unquoted attribute value", `<input value=yes FOO=BAR>`, `<input value="yes" foo="BAR">`, }, { "Unquoted attribute value, spaces", `<input value = yes FOO = BAR>`, `<input value="yes" foo="BAR">`, }, { "Unquoted attribute value, trailing space", `<input value=yes FOO=BAR >`, `<input value="yes" foo="BAR">`, }, { "Single-quoted attribute value", `<input value='yes' FOO='BAR'>`, `<input value="yes" foo="BAR">`, }, { "Single-quoted attribute value, trailing space", `<input value='yes' FOO='BAR' >`, `<input value="yes" foo="BAR">`, }, { "Double-quoted attribute value", `<input value="I'm an attribute" FOO="BAR">`, `<input value="I&#39;m an attribute" foo="BAR">`, }, { "Attribute name characters", `<meta http-equiv="content-type">`, `<meta http-equiv="content-type">`, }, { "Mixed attributes", `a<P V="0 1" w='2' X=3 y>z`, `a$<p v="0 1" w="2" x="3" y="">$z`, }, { "Attributes with a solitary single quote", `<p id=can't><p id=won't>`, `<p id="can&#39;t">$<p id="won&#39;t">`, }, } func TestTokenizer(t *testing.T) { loop: for _, tt := range tokenTests { z := NewTokenizer(strings.NewReader(tt.html)) if tt.golden != "" { for i, s := range strings.Split(tt.golden, "$") { if z.Next() == ErrorToken { t.Errorf("%s token %d: want %q got error %v", tt.desc, i, s, z.Err()) continue loop } actual := z.Token().String() if s != actual { t.Errorf("%s token %d: want %q got %q", tt.desc, i, s, actual) continue loop } } } z.Next() if z.Err() != io.EOF { t.Errorf("%s: want EOF got %q", tt.desc, z.Err()) } } } 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()), "<tttt"; got != want { t.Fatalf("buffered before overflow: got: %q want: %q", got, want) } } func TestMaxBufferReconstruction(t *testing.T) { // Exceeding the maximum buffer size at any point while tokenizing permits // reconstructing the original input. tests: for _, test := range tokenTests { for maxBuf := 1; ; maxBuf++ { r := strings.NewReader(test.html) z := NewTokenizer(r) z.SetMaxBuf(maxBuf) var tokenized bytes.Buffer for { tt := z.Next() tokenized.Write(z.Raw()) if tt == ErrorToken { if err := z.Err(); err != io.EOF && err != ErrBufferExceeded { t.Errorf("%s: unexpected error: %v", test.desc, err) } break } } // Anything tokenized along with untokenized input or data left in the reader. assembled, err := ioutil.ReadAll(io.MultiReader(&tokenized, bytes.NewReader(z.Buffered()), r)) if err != nil { t.Errorf("%s: ReadAll: %v", test.desc, err) continue tests } if got, want := string(assembled), test.html; got != want { t.Errorf("%s: reassembled html:\n got: %q\nwant: %q", test.desc, got, want) continue tests } // EOF indicates that we completed tokenization and hence found the max // maxBuf that generates ErrBufferExceeded, so continue to the next test. if z.Err() == io.EOF { break } } // buffer sizes } // tests } func TestPassthrough(t *testing.T) { // Accumulating the raw output for each parse event should reconstruct the // original input. for _, test := range tokenTests { z := NewTokenizer(strings.NewReader(test.html)) var parsed bytes.Buffer for { tt := z.Next() parsed.Write(z.Raw()) if tt == ErrorToken { break } } if got, want := parsed.String(), test.html; got != want { t.Errorf("%s: parsed output:\n got: %q\nwant: %q", test.desc, got, want) } } } func TestBufAPI(t *testing.T) { s := "0<a>1</a>2<b>3<a>4<a>5</a>6</b>7</a>8<a/>9" z := NewTokenizer(bytes.NewBufferString(s)) var result bytes.Buffer depth := 0 loop: for { tt := z.Next() switch tt { case ErrorToken: if z.Err() != io.EOF { t.Error(z.Err()) } break loop case TextToken: if depth > 0 { result.Write(z.Text()) } case StartTagToken, EndTagToken: tn, _ := z.TagName() if len(tn) == 1 && tn[0] == 'a' { if tt == StartTagToken { depth++ } else { depth-- } } } } u := "14567" v := string(result.Bytes()) if u != v { t.Errorf("TestBufAPI: want %q got %q", u, v) } } func TestConvertNewlines(t *testing.T) { testCases := map[string]string{ "Mac\rDOS\r\nUnix\n": "Mac\nDOS\nUnix\n", "Unix\nMac\rDOS\r\n": "Unix\nMac\nDOS\n", "DOS\r\nDOS\r\nDOS\r\n": "DOS\nDOS\nDOS\n", "": "", "\n": "\n", "\n\r": "\n\n", "\r": "\n", "\r\n": "\n", "\r\n\n": "\n\n", "\r\n\r": "\n\n", "\r\n\r\n": "\n\n", "\r\r": "\n\n", "\r\r\n": "\n\n", "\r\r\n\n": "\n\n\n", "\r\r\r\n": "\n\n\n", "\r \n": "\n \n", "xyz": "xyz", } for in, want := range testCases { if got := string(convertNewlines([]byte(in))); got != want { t.Errorf("input %q: got %q, want %q", in, got, want) } } } func TestReaderEdgeCases(t *testing.T) { const s = "<p>An io.Reader can return (0, nil) or (n, io.EOF).</p>" testCases := []io.Reader{ &zeroOneByteReader{s: s}, &eofStringsReader{s: s}, &stuckReader{}, } for i, tc := range testCases { got := []TokenType{} z := NewTokenizer(tc) for { tt := z.Next() if tt == ErrorToken { break } got = append(got, tt) } if err := z.Err(); err != nil && err != io.EOF { if err != io.ErrNoProgress { t.Errorf("i=%d: %v", i, err) } continue } want := []TokenType{ StartTagToken, TextToken, EndTagToken, } if !reflect.DeepEqual(got, want) { t.Errorf("i=%d: got %v, want %v", i, got, want) continue } } } // zeroOneByteReader is like a strings.Reader that alternates between // returning 0 bytes and 1 byte at a time. type zeroOneByteReader struct { s string n int } func (r *zeroOneByteReader) Read(p []byte) (int, error) { if len(p) == 0 { return 0, nil } if len(r.s) == 0 { return 0, io.EOF } r.n++ if r.n%2 != 0 { return 0, nil } p[0], r.s = r.s[0], r.s[1:] return 1, nil } // eofStringsReader is like a strings.Reader but can return an (n, err) where // n > 0 && err != nil. type eofStringsReader struct { s string } func (r *eofStringsReader) Read(p []byte) (int, error) { n := copy(p, r.s) r.s = r.s[n:] if r.s != "" { return n, nil } return n, io.EOF } // stuckReader is an io.Reader that always returns no data and no error. type stuckReader struct{} func (*stuckReader) Read(p []byte) (int, error) { return 0, nil } const ( rawLevel = iota lowLevel highLevel ) func benchmarkTokenizer(b *testing.B, level int) { buf, err := ioutil.ReadFile("testdata/go1.html") if err != nil { b.Fatalf("could not read testdata/go1.html: %v", err) } b.SetBytes(int64(len(buf))) runtime.GC() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { z := NewTokenizer(bytes.NewBuffer(buf)) for { tt := z.Next() if tt == ErrorToken { if err := z.Err(); err != nil && err != io.EOF { b.Fatalf("tokenizer error: %v", err) } break } switch level { case rawLevel: // Calling z.Raw just returns the raw bytes of the token. It does // not unescape &lt; to <, or lower-case tag names and attribute keys. z.Raw() case lowLevel: // Caling z.Text, z.TagName and z.TagAttr returns []byte values // whose contents may change on the next call to z.Next. switch tt { case TextToken, CommentToken, DoctypeToken: z.Text() case StartTagToken, SelfClosingTagToken: _, more := z.TagName() for more { _, _, more = z.TagAttr() } case EndTagToken: z.TagName() } case highLevel: // Calling z.Token converts []byte values to strings whose validity // extend beyond the next call to z.Next. z.Token() } } } } func BenchmarkRawLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, rawLevel) } func BenchmarkLowLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, lowLevel) } func BenchmarkHighLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, highLevel) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/�����������������������������������������0000755�0000153�0000161�00000000000�12321735652�023227� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/gen.go�����������������������������������0000644�0000153�0000161�00000005255�12321735652�024336� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// +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", } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/charset_test.go��������������������������0000644�0000153�0000161�00000015500�12321735652�026247� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������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) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/table.go���������������������������������0000644�0000153�0000161�00000032154�12321735652�024652� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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"}, } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/charset.go�������������������������������0000644�0000153�0000161�00000011722�12321735652�025212� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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"}, } ����������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/��������������������������������0000755�0000153�0000161�00000000000�12321735652�025040� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-charset.html���������������0000644�0000153�0000161�00000005071�12321735652�030137� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html lang="en" > <head> <title>HTTP charset</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <link rel="stylesheet" type="text/css" href="./generatedtests.css"> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <meta name="assert" content="The character encoding of a page can be set using the HTTP header charset declaration."> <style type='text/css'> .test div { width: 50px; }</style> <link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css"> </head> <body> <p class='title'>HTTP charset</p> <div id='log'></div> <div class='test'><div id='box' class='ýäè'>&#xA0;</div></div> <div class='description'> <p class="assertion" title="Assertion">The character encoding of a page can be set using the HTTP header charset declaration.</p> <div class="notes"><p><p>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 <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. 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.</p><p>The only character encoding declaration for this HTML file is in the HTTP header, which sets the encoding to ISO 8859-15.</p></p> </div> </div> <div class="nexttest"><div><a href="generate?test=the-input-byte-stream-003">Next test</a></div><div class="doctype">HTML5</div> <p class="jump">the-input-byte-stream-001<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-001" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p> <div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li> <li>The test is read from a server that supports HTTP.</li></ul></div> </div> <script> test(function() { assert_equals(document.getElementById('box').offsetWidth, 100); }, " "); </script> </body> </html> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/meta-charset-attribute.html�����0000644�0000153�0000161�00000005214�12321735652�032306� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html lang="en" > <head> <meta charset="iso-8859-15"> <title>meta charset attribute</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <link rel="stylesheet" type="text/css" href="./generatedtests.css"> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <meta name="assert" content="The character encoding of the page can be set by a meta element with charset attribute."> <style type='text/css'> .test div { width: 50px; }</style> <link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css"> </head> <body> <p class='title'>meta charset attribute</p> <div id='log'></div> <div class='test'><div id='box' class='ýäè'>&#xA0;</div></div> <div class='description'> <p class="assertion" title="Assertion">The character encoding of the page can be set by a meta element with charset attribute.</p> <div class="notes"><p><p>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.</p><p>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 <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. 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.</p></p> </div> </div> <div class="nexttest"><div><a href="generate?test=the-input-byte-stream-015">Next test</a></div><div class="doctype">HTML5</div> <p class="jump">the-input-byte-stream-009<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-009" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p> <div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li> <li>The test is read from a server that supports HTTP.</li></ul></div> </div> <script> test(function() { assert_equals(document.getElementById('box').offsetWidth, 100); }, " "); </script> </body> </html> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/UTF-16LE-BOM.html���������������0000644�0000153�0000161�00000005172�12321735652�027451� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ÿþ<�!�D�O�C�T�Y�P�E� �h�t�m�l�>� � �<�h�t�m�l� � �l�a�n�g�=�"�e�n�"� �>� � �<�h�e�a�d�>� � �<�t�i�t�l�e�>�U�T�F�-�1�6�L�E� �B�O�M�<�/�t�i�t�l�e�>� � �<�l�i�n�k� �r�e�l�=�'�a�u�t�h�o�r�'� �t�i�t�l�e�=�'�R�i�c�h�a�r�d� �I�s�h�i�d�a�'� �h�r�e�f�=�'�m�a�i�l�t�o�:�i�s�h�i�d�a�@�w�3�.�o�r�g�'�>� � �<�l�i�n�k� �r�e�l�=�'�h�e�l�p�'� �h�r�e�f�=�'�h�t�t�p�:�/�/�w�w�w�.�w�3�.�o�r�g�/�T�R�/�h�t�m�l�5�/�s�y�n�t�a�x�.�h�t�m�l�#�t�h�e�-�i�n�p�u�t�-�b�y�t�e�-�s�t�r�e�a�m�'�>� � �<�s�c�r�i�p�t� �s�r�c�=�"�h�t�t�p�:�/�/�w�3�c�-�t�e�s�t�.�o�r�g�/�r�e�s�o�u�r�c�e�s�/�t�e�s�t�h�a�r�n�e�s�s�.�j�s�"�>�<�/�s�c�r�i�p�t�>� � �<�s�c�r�i�p�t� �s�r�c�=�"�h�t�t�p�:�/�/�w�3�c�-�t�e�s�t�.�o�r�g�/�r�e�s�o�u�r�c�e�s�/�t�e�s�t�h�a�r�n�e�s�s�r�e�p�o�r�t�.�j�s�"�>�<�/�s�c�r�i�p�t�>� � �<�m�e�t�a� �n�a�m�e�=�'�f�l�a�g�s�'� �c�o�n�t�e�n�t�=�'�h�t�t�p�'�>� � �<�s�t�y�l�e� �t�y�p�e�=�'�t�e�x�t�/�c�s�s�'�>� � �.�t�e�s�t� �d�i�v� �{� �w�i�d�t�h�:� �5�0�p�x�;� �}� � �<�/�s�t�y�l�e�>� � � � �<�l�i�n�k� �r�e�l�=�"�s�t�y�l�e�s�h�e�e�t�"� �t�y�p�e�=�"�t�e�x�t�/�c�s�s�"� �h�r�e�f�=�"�e�n�c�o�d�i�n�g�t�e�s�t�s�-�1�5�.�c�s�s�"�>� � �<�/�h�e�a�d�>� � �<�b�o�d�y�>� � � � �<�d�i�v� �c�l�a�s�s�=�'�t�e�s�t�'�>�<�d�i�v� �i�d�=�'�b�o�x�'� �c�l�a�s�s�=�'�Ã�SÃ�¬ Ã�a'�>�&�#�x�A�0�;�<�/�d�i�v�>�<�/�d�i�v�>� � � � �<�!�-�-� �N�o�t�e�s�:� � � �N�o� �e�n�c�o�d�i�n�g� �i�n�f�o�r�m�a�t�i�o�n� �i�s� �d�e�c�l�a�r�e�d� �i�n� �t�h�e� �H�T�T�P� �h�e�a�d�e�r� �o�r� �i�n�s�i�d�e� �t�h�e� �d�o�c�u�m�e�n�t�,� �o�t�h�e�r� �t�h�a�n� �i�n� �t�h�e� �B�O�M�.� �T�h�e� �t�e�x�t� �o�f� �a� �c�l�a�s�s� �n�a�m�e� �i�n� �t�h�e� �t�e�s�t� �c�o�n�t�a�i�n�s� �t�h�e� �f�o�l�l�o�w�i�n�g� �s�e�q�u�e�n�c�e� �o�f� �b�y�t�e�s�:� �0�x�C�3� �0�x�c�0� �0�x�5�3� �0�x�c�1� �0�x�C�3� �0�x�c�0� �0�x�A�C� �0�x�c�2�0� �0�x�C�3� �0�x�c�0� �0�x�6�1� �0�x�c�1�.� �T�h�e� �e�x�t�e�r�n�a�l�,� �U�T�F�-�8�-�e�n�c�o�d�e�d� �s�t�y�l�e�s�h�e�e�t� �c�o�n�t�a�i�n�s� �a� �s�e�l�e�c�t�o�r� �w�i�t�h� �a� �s�e�q�u�e�n�c�e� �o�f� �c�h�a�r�a�c�t�e�r�s� �t�h�a�t� �w�i�l�l� �o�n�l�y� �m�a�t�c�h� �t�h�e� �c�l�a�s�s� �n�a�m�e� �i�n� �t�h�e� �H�T�M�L� �i�f� �t�h�e� �p�a�g�e� �i�s� �r�e�a�d� �a�s� �U�T�F�-�1�6�B�E�.� � �-�-�>� � � � �<�s�c�r�i�p�t�>� � � �t�e�s�t�(�f�u�n�c�t�i�o�n� �(�)� �{� � � � � � �a�s�s�e�r�t�_�e�q�u�a�l�s�(�d�o�c�u�m�e�n�t�.�g�e�t�E�l�e�m�e�n�t�B�y�I�d�(�'�b�o�x�'�)�.�o�f�f�s�e�t�W�i�d�t�h�,� �1�0�0�)�;� � � �}�,� �'�A� �p�a�g�e� �w�i�t�h� �n�o� �e�n�c�o�d�i�n�g� �d�e�c�l�a�r�a�t�i�o�n�s�,� �b�u�t� �w�i�t�h� �a� �U�T�F�-�1�6� �l�i�t�t�l�e�-�e�n�d�i�a�n� �B�O�M� �w�i�l�l� �b�e� �r�e�c�o�g�n�i�z�e�d� �a�s� �U�T�F�-�1�6�.�'�)�;� � �<�/�s�c�r�i�p�t�>� � � � �<�d�i�v� �i�d�=�"�l�o�g�"�>�<�/�d�i�v�>� � � � �<�/�b�o�d�y�>� � �<�/�h�t�m�l�>� � �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/No-encoding-declaration.html����0000644�0000153�0000161�00000004615�12321735652�032357� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html lang="en" > <head> <title>No encoding declaration</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <link rel="stylesheet" type="text/css" href="./generatedtests.css"> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <meta name="assert" content="A page with no encoding information in HTTP, BOM, XML declaration or meta element will be treated as UTF-8."> <style type='text/css'> .test div { width: 50px; }</style> <link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css"> </head> <body> <p class='title'>No encoding declaration</p> <div id='log'></div> <div class='test'><div id='box' class='ýäè'>&#xA0;</div></div> <div class='description'> <p class="assertion" title="Assertion">A page with no encoding information in HTTP, BOM, XML declaration or meta element will be treated as UTF-8.</p> <div class="notes"><p><p>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 <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. 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.</p></p> </div> </div> <div class="nexttest"><div><a href="generate?test=the-input-byte-stream-034">Next test</a></div><div class="doctype">HTML5</div> <p class="jump">the-input-byte-stream-015<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-015" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p> <div class='prereq'>Assumptions: <ul><li>The test is read from a server that supports HTTP.</li></ul></div> </div> <script> test(function() { assert_equals(document.getElementById('box').offsetWidth, 100); }, " "); </script> </body> </html> �������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/README��������������������������0000644�0000153�0000161�00000000154�12321735652�025720� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������These test cases come from http://www.w3.org/International/tests/html5/the-input-byte-stream/results-basics ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-meta-charset.html�������0000644�0000153�0000161�00000005350�12321735652�031511� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html lang="en" > <head> <meta charset="iso-8859-1" > <title>HTTP vs meta charset</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <link rel="stylesheet" type="text/css" href="./generatedtests.css"> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <meta name="assert" content="The HTTP header has a higher precedence than an encoding declaration in a meta charset attribute."> <style type='text/css'> .test div { width: 50px; }.test div { width: 90px; } </style> <link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css"> </head> <body> <p class='title'>HTTP vs meta charset</p> <div id='log'></div> <div class='test'><div id='box' class='ýäè'>&#xA0;</div></div> <div class='description'> <p class="assertion" title="Assertion">The HTTP header has a higher precedence than an encoding declaration in a meta charset attribute.</p> <div class="notes"><p><p>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.</p><p>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 <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. 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.</p></p> </div> </div> <div class="nexttest"><div><a href="generate?test=the-input-byte-stream-037">Next test</a></div><div class="doctype">HTML5</div> <p class="jump">the-input-byte-stream-018<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-018" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p> <div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li> <li>The test is read from a server that supports HTTP.</li></ul></div> </div> <script> test(function() { assert_equals(document.getElementById('box').offsetWidth, 100); }, " "); </script> </body> </html> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/UTF-8-BOM-vs-meta-charset.html��0000644�0000153�0000161�00000005347�12321735652�032156� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html lang="en" > <head> <meta charset="iso-8859-15"> <title>UTF-8 BOM vs meta charset</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <link rel="stylesheet" type="text/css" href="./generatedtests.css"> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <meta name="assert" content="A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta charset attribute declares a different encoding."> <style type='text/css'> .test div { width: 50px; }.test div { width: 90px; } </style> <link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css"> </head> <body> <p class='title'>UTF-8 BOM vs meta charset</p> <div id='log'></div> <div class='test'><div id='box' class='ýäè'>&#xA0;</div></div> <div class='description'> <p class="assertion" title="Assertion">A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta charset attribute declares a different encoding.</p> <div class="notes"><p><p>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.</p><p>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 <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. 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.</p></p> </div> </div> <div class="nexttest"><div><a href="generate?test=the-input-byte-stream-024">Next test</a></div><div class="doctype">HTML5</div> <p class="jump">the-input-byte-stream-038<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-038" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p> <div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li> <li>The test is read from a server that supports HTTP.</li></ul></div> </div> <script> test(function() { assert_equals(document.getElementById('box').offsetWidth, 100); }, " "); </script> </body> </html> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/meta-content-attribute.html�����0000644�0000153�0000161�00000005331�12321735652�032327� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html lang="en" > <head> <meta http-equiv="content-type" content="text/html; charset=iso-8859-15"> <title>meta content attribute</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <link rel="stylesheet" type="text/css" href="./generatedtests.css"> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <meta name="assert" content="The character encoding of the page can be set by a meta element with http-equiv and content attributes."> <style type='text/css'> .test div { width: 50px; }</style> <link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css"> </head> <body> <p class='title'>meta content attribute</p> <div id='log'></div> <div class='test'><div id='box' class='ýäè'>&#xA0;</div></div> <div class='description'> <p class="assertion" title="Assertion">The character encoding of the page can be set by a meta element with http-equiv and content attributes.</p> <div class="notes"><p><p>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.</p><p>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 <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. 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.</p></p> </div> </div> <div class="nexttest"><div><a href="generate?test=the-input-byte-stream-009">Next test</a></div><div class="doctype">HTML5</div> <p class="jump">the-input-byte-stream-007<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#basics" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-007" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p> <div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li> <li>The test is read from a server that supports HTTP.</li></ul></div> </div> <script> test(function() { assert_equals(document.getElementById('box').offsetWidth, 100); }, " "); </script> </body> </html> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-meta-content.html�������0000644�0000153�0000161�00000005424�12321735652�031534� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html lang="en" > <head> <meta http-equiv="content-type" content="text/html;charset=iso-8859-1" > <title>HTTP vs meta content</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <link rel="stylesheet" type="text/css" href="./generatedtests.css"> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <meta name="assert" content="The HTTP header has a higher precedence than an encoding declaration in a meta content attribute."> <style type='text/css'> .test div { width: 50px; }.test div { width: 90px; } </style> <link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-15.css"> </head> <body> <p class='title'>HTTP vs meta content</p> <div id='log'></div> <div class='test'><div id='box' class='ýäè'>&#xA0;</div></div> <div class='description'> <p class="assertion" title="Assertion">The HTTP header has a higher precedence than an encoding declaration in a meta content attribute.</p> <div class="notes"><p><p>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.</p><p>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 <code>.test div.&#x00C3;&#x0153;&#x00C3;&#x20AC;&#x00C3;&#x0161;</code>. 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.</p></p> </div> </div> <div class="nexttest"><div><a href="generate?test=the-input-byte-stream-018">Next test</a></div><div class="doctype">HTML5</div> <p class="jump">the-input-byte-stream-016<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-016" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p> <div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li> <li>The test is read from a server that supports HTTP.</li></ul></div> </div> <script> test(function() { assert_equals(document.getElementById('box').offsetWidth, 100); }, " "); </script> </body> </html> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/HTTP-vs-UTF-8-BOM.html����������0000644�0000153�0000161�00000005406�12321735652�030354� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html lang="en" > <head> <title>HTTP vs UTF-8 BOM</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <link rel="stylesheet" type="text/css" href="./generatedtests.css"> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <meta name="assert" content="A character encoding set in the HTTP header has lower precedence than the UTF-8 signature."> <style type='text/css'> .test div { width: 50px; }</style> <link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css"> </head> <body> <p class='title'>HTTP vs UTF-8 BOM</p> <div id='log'></div> <div class='test'><div id='box' class='ýäè'>&#xA0;</div></div> <div class='description'> <p class="assertion" title="Assertion">A character encoding set in the HTTP header has lower precedence than the UTF-8 signature.</p> <div class="notes"><p><p>The HTTP header attempts to set the character encoding to ISO 8859-15. The page starts with a UTF-8 signature.</p><p>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 <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. 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.</p><p>If the test is unsuccessful, the characters &#x00EF;&#x00BB;&#x00BF; 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.</p></p> </div> </div> <div class="nexttest"><div><a href="generate?test=the-input-byte-stream-022">Next test</a></div><div class="doctype">HTML5</div> <p class="jump">the-input-byte-stream-034<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-034" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p> <div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li> <li>The test is read from a server that supports HTTP.</li></ul></div> </div> <script> test(function() { assert_equals(document.getElementById('box').offsetWidth, 100); }, " "); </script> </body> </html> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/UTF-16BE-BOM.html���������������0000644�0000153�0000161�00000005156�12321735652�027441� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������þÿ�<�!�D�O�C�T�Y�P�E� �h�t�m�l�>� � �<�h�t�m�l� � �l�a�n�g�=�"�e�n�"� �>� � �<�h�e�a�d�>� � �<�t�i�t�l�e�>�U�T�F�-�1�6�B�E� �B�O�M�<�/�t�i�t�l�e�>� � �<�l�i�n�k� �r�e�l�=�'�a�u�t�h�o�r�'� �t�i�t�l�e�=�'�R�i�c�h�a�r�d� �I�s�h�i�d�a�'� �h�r�e�f�=�'�m�a�i�l�t�o�:�i�s�h�i�d�a�@�w�3�.�o�r�g�'�>� � �<�l�i�n�k� �r�e�l�=�'�h�e�l�p�'� �h�r�e�f�=�'�h�t�t�p�:�/�/�w�w�w�.�w�3�.�o�r�g�/�T�R�/�h�t�m�l�5�/�s�y�n�t�a�x�.�h�t�m�l�#�t�h�e�-�i�n�p�u�t�-�b�y�t�e�-�s�t�r�e�a�m�'�>� � �<�s�c�r�i�p�t� �s�r�c�=�"�h�t�t�p�:�/�/�w�3�c�-�t�e�s�t�.�o�r�g�/�r�e�s�o�u�r�c�e�s�/�t�e�s�t�h�a�r�n�e�s�s�.�j�s�"�>�<�/�s�c�r�i�p�t�>� � �<�s�c�r�i�p�t� �s�r�c�=�"�h�t�t�p�:�/�/�w�3�c�-�t�e�s�t�.�o�r�g�/�r�e�s�o�u�r�c�e�s�/�t�e�s�t�h�a�r�n�e�s�s�r�e�p�o�r�t�.�j�s�"�>�<�/�s�c�r�i�p�t�>� � �<�m�e�t�a� �n�a�m�e�=�'�f�l�a�g�s�'� �c�o�n�t�e�n�t�=�'�h�t�t�p�'�>� � �<�s�t�y�l�e� �t�y�p�e�=�'�t�e�x�t�/�c�s�s�'�>� � �.�t�e�s�t� �d�i�v� �{� �w�i�d�t�h�:� �5�0�p�x�;� �}� � �<�/�s�t�y�l�e�>� � � � �<�l�i�n�k� �r�e�l�=�"�s�t�y�l�e�s�h�e�e�t�"� �t�y�p�e�=�"�t�e�x�t�/�c�s�s�"� �h�r�e�f�=�"�e�n�c�o�d�i�n�g�t�e�s�t�s�-�1�5�.�c�s�s�"�>� � �<�/�h�e�a�d�>� � �<�b�o�d�y�>� � � � �<�d�i�v� �c�l�a�s�s�=�'�t�e�s�t�'�>�<�d�i�v� �i�d�=�'�b�o�x�'� �c�l�a�s�s�=�'�ÃS�à ¬�Ãa�'�>�&�#�x�A�0�;�<�/�d�i�v�>�<�/�d�i�v�>� � � � �<�!�-�-� �N�o�t�e�s�:� � � �N�o� �e�n�c�o�d�i�n�g� �i�n�f�o�r�m�a�t�i�o�n� �i�s� �d�e�c�l�a�r�e�d� �i�n� �t�h�e� �H�T�T�P� �h�e�a�d�e�r� �o�r� �i�n�s�i�d�e� �t�h�e� �d�o�c�u�m�e�n�t�,� �o�t�h�e�r� �t�h�a�n� �i�n� �t�h�e� �B�O�M�.� �T�h�e� �t�e�x�t� �o�f� �a� �c�l�a�s�s� �n�a�m�e� �i�n� �t�h�e� �t�e�s�t� �c�o�n�t�a�i�n�s� �t�h�e� �f�o�l�l�o�w�i�n�g� �s�e�q�u�e�n�c�e� �o�f� �b�y�t�e�s�:� �0�x�C�3� �0�x�c�0� �0�x�5�3� �0�x�c�1� �0�x�C�3� �0�x�c�0� �0�x�A�C� �0�x�c�2�0� �0�x�C�3� �0�x�c�0� �0�x�6�1� �0�x�c�1�.� �T�h�e� �e�x�t�e�r�n�a�l�,� �U�T�F�-�8�-�e�n�c�o�d�e�d� �s�t�y�l�e�s�h�e�e�t� �c�o�n�t�a�i�n�s� �a� �s�e�l�e�c�t�o�r� �w�i�t�h� �a� �s�e�q�u�e�n�c�e� �o�f� �c�h�a�r�a�c�t�e�r�s� �t�h�a�t� �w�i�l�l� �o�n�l�y� �m�a�t�c�h� �t�h�e� �c�l�a�s�s� �n�a�m�e� �i�n� �t�h�e� �H�T�M�L� �i�f� �t�h�e� �p�a�g�e� �i�s� �r�e�a�d� �a�s� �U�T�F�-�1�6�B�E�.� � �-�-�>� � � � �<�s�c�r�i�p�t�>� � �t�e�s�t�(�f�u�n�c�t�i�o�n� �(�)� �{� � � �a�s�s�e�r�t�_�e�q�u�a�l�s�(�d�o�c�u�m�e�n�t�.�g�e�t�E�l�e�m�e�n�t�B�y�I�d�(�'�b�o�x�'�)�.�o�f�f�s�e�t�W�i�d�t�h�,� �1�0�0�)�;� � � �}�,� �'�A� �p�a�g�e� �w�i�t�h� �n�o� �e�n�c�o�d�i�n�g� �d�e�c�l�a�r�a�t�i�o�n�s�,� �b�u�t� �w�i�t�h� �a� �U�T�F�-�1�6� �l�i�t�t�l�e�-�e�n�d�i�a�n� �B�O�M� �w�i�l�l� �b�e� �r�e�c�o�g�n�i�z�e�d� �a�s� �U�T�F�-�1�6�.�'�)�;� � �<�/�s�c�r�i�p�t�>� � � � �<�d�i�v� �i�d�=�l�o�g�>�<�/�d�i�v�>� � � � �<�/�b�o�d�y�>� � �<�/�h�t�m�l�>� � ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/charset/testdata/UTF-8-BOM-vs-meta-content.html��0000644�0000153�0000161�00000005371�12321735652�032174� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html lang="en" > <head> <meta http-equiv="content-type" content="text/html; charset=iso-8859-15"> <title>UTF-8 BOM vs meta content</title> <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'> <link rel='help' href='http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream'> <link rel="stylesheet" type="text/css" href="./generatedtests.css"> <script src="http://w3c-test.org/resources/testharness.js"></script> <script src="http://w3c-test.org/resources/testharnessreport.js"></script> <meta name='flags' content='http'> <meta name="assert" content="A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta content attribute declares a different encoding."> <style type='text/css'> .test div { width: 50px; }</style> <link rel="stylesheet" type="text/css" href="the-input-byte-stream/support/encodingtests-utf8.css"> </head> <body> <p class='title'>UTF-8 BOM vs meta content</p> <div id='log'></div> <div class='test'><div id='box' class='ýäè'>&#xA0;</div></div> <div class='description'> <p class="assertion" title="Assertion">A page with a UTF-8 BOM will be recognized as UTF-8 even if the meta content attribute declares a different encoding.</p> <div class="notes"><p><p>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.</p><p>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 <code>.test div.&#x00FD;&#x00E4;&#x00E8;</code>. 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.</p></p> </div> </div> <div class="nexttest"><div><a href="generate?test=the-input-byte-stream-038">Next test</a></div><div class="doctype">HTML5</div> <p class="jump">the-input-byte-stream-037<br /><a href="/International/tests/html5/the-input-byte-stream/results-basics#precedence" target="_blank">Result summary &amp; related tests</a><br /><a href="http://w3c-test.org/framework/details/i18n-html5/the-input-byte-stream-037" target="_blank">Detailed results for this test</a><br/> <a href="http://www.w3.org/TR/html5/syntax.html#the-input-byte-stream" target="_blank">Link to spec</a></p> <div class='prereq'>Assumptions: <ul><li>The default encoding for the browser you are testing is not set to ISO 8859-15.</li> <li>The test is read from a server that supports HTTP.</li></ul></div> </div> <script> test(function() { assert_equals(document.getElementById('box').offsetWidth, 100); }, " "); </script> </body> </html> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/parse.go�����������������������������������������0000644�0000153�0000161�00000137755�12321735652�023261� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 html import ( "errors" "fmt" "io" "strings" a "code.google.com/p/go.net/html/atom" ) // A parser implements the HTML5 parsing algorithm: // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#tree-construction type parser struct { // tokenizer provides the tokens for the parser. tokenizer *Tokenizer // tok is the most recently read token. tok Token // Self-closing tags like <hr/> are treated as start tags, except that // hasSelfClosingToken is set while they are being processed. hasSelfClosingToken bool // doc is the document root element. doc *Node // The stack of open elements (section 12.2.3.2) and active formatting // elements (section 12.2.3.3). oe, afe nodeStack // Element pointers (section 12.2.3.4). head, form *Node // Other parsing state flags (section 12.2.3.5). scripting, framesetOK bool // im is the current insertion mode. im insertionMode // originalIM is the insertion mode to go back to after completing a text // or inTableText insertion mode. originalIM insertionMode // fosterParenting is whether new elements should be inserted according to // the foster parenting rules (section 12.2.5.3). fosterParenting bool // quirks is whether the parser is operating in "quirks mode." quirks bool // fragment is whether the parser is parsing an HTML fragment. fragment bool // context is the context element when parsing an HTML fragment // (section 12.4). context *Node } func (p *parser) top() *Node { if n := p.oe.top(); n != nil { return n } return p.doc } // Stop tags for use in popUntil. These come from section 12.2.3.2. var ( defaultScopeStopTags = map[string][]a.Atom{ "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object}, "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, "svg": {a.Desc, a.ForeignObject, a.Title}, } ) type scope int const ( defaultScope scope = iota listItemScope buttonScope tableScope tableRowScope tableBodyScope selectScope ) // popUntil pops the stack of open elements at the highest element whose tag // is in matchTags, provided there is no higher element in the scope's stop // tags (as defined in section 12.2.3.2). It returns whether or not there was // such an element. If there was not, popUntil leaves the stack unchanged. // // For example, the set of stop tags for table scope is: "html", "table". If // the stack was: // ["html", "body", "font", "table", "b", "i", "u"] // then popUntil(tableScope, "font") would return false, but // popUntil(tableScope, "i") would return true and the stack would become: // ["html", "body", "font", "table", "b"] // // If an element's tag is in both the stop tags and matchTags, then the stack // will be popped and the function returns true (provided, of course, there was // no higher element in the stack that was also in the stop tags). For example, // popUntil(tableScope, "table") returns true and leaves: // ["html", "body", "font"] func (p *parser) popUntil(s scope, matchTags ...a.Atom) bool { if i := p.indexOfElementInScope(s, matchTags...); i != -1 { p.oe = p.oe[:i] return true } return false } // indexOfElementInScope returns the index in p.oe of the highest element whose // tag is in matchTags that is in scope. If no matching element is in scope, it // returns -1. func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { for i := len(p.oe) - 1; i >= 0; i-- { tagAtom := p.oe[i].DataAtom if p.oe[i].Namespace == "" { for _, t := range matchTags { if t == tagAtom { return i } } switch s { case defaultScope: // No-op. case listItemScope: if tagAtom == a.Ol || tagAtom == a.Ul { return -1 } case buttonScope: if tagAtom == a.Button { return -1 } case tableScope: if tagAtom == a.Html || tagAtom == a.Table { return -1 } case selectScope: if tagAtom != a.Optgroup && tagAtom != a.Option { return -1 } default: panic("unreachable") } } switch s { case defaultScope, listItemScope, buttonScope: for _, t := range defaultScopeStopTags[p.oe[i].Namespace] { if t == tagAtom { return -1 } } } } return -1 } // elementInScope is like popUntil, except that it doesn't modify the stack of // open elements. func (p *parser) elementInScope(s scope, matchTags ...a.Atom) bool { return p.indexOfElementInScope(s, matchTags...) != -1 } // clearStackToContext pops elements off the stack of open elements until a // scope-defined element is found. func (p *parser) clearStackToContext(s scope) { for i := len(p.oe) - 1; i >= 0; i-- { tagAtom := p.oe[i].DataAtom switch s { case tableScope: if tagAtom == a.Html || tagAtom == a.Table { p.oe = p.oe[:i+1] return } case tableRowScope: if tagAtom == a.Html || tagAtom == a.Tr { p.oe = p.oe[:i+1] return } case tableBodyScope: if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead { p.oe = p.oe[:i+1] return } default: panic("unreachable") } } } // generateImpliedEndTags pops nodes off the stack of open elements as long as // the top node has a tag name of dd, dt, li, option, optgroup, p, rp, or rt. // If exceptions are specified, nodes with that name will not be popped off. func (p *parser) generateImpliedEndTags(exceptions ...string) { var i int loop: for i = len(p.oe) - 1; i >= 0; i-- { n := p.oe[i] if n.Type == ElementNode { switch n.DataAtom { case a.Dd, a.Dt, a.Li, a.Option, a.Optgroup, a.P, a.Rp, a.Rt: for _, except := range exceptions { if n.Data == except { break loop } } continue } } break } p.oe = p.oe[:i+1] } // addChild adds a child node n to the top element, and pushes n onto the stack // of open elements if it is an element node. func (p *parser) addChild(n *Node) { if p.shouldFosterParent() { p.fosterParent(n) } else { p.top().AppendChild(n) } if n.Type == ElementNode { p.oe = append(p.oe, n) } } // shouldFosterParent returns whether the next node to be added should be // foster parented. func (p *parser) shouldFosterParent() bool { if p.fosterParenting { switch p.top().DataAtom { case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: return true } } return false } // fosterParent adds a child node according to the foster parenting rules. // Section 12.2.5.3, "foster parenting". func (p *parser) fosterParent(n *Node) { var table, parent, prev *Node var i int for i = len(p.oe) - 1; i >= 0; i-- { if p.oe[i].DataAtom == a.Table { table = p.oe[i] break } } if table == nil { // The foster parent is the html element. parent = p.oe[0] } else { parent = table.Parent } if parent == nil { parent = p.oe[i-1] } if table != nil { prev = table.PrevSibling } else { prev = parent.LastChild } if prev != nil && prev.Type == TextNode && n.Type == TextNode { prev.Data += n.Data return } parent.InsertBefore(n, table) } // addText adds text to the preceding node if it is a text node, or else it // calls addChild with a new text node. func (p *parser) addText(text string) { if text == "" { return } if p.shouldFosterParent() { p.fosterParent(&Node{ Type: TextNode, Data: text, }) return } t := p.top() if n := t.LastChild; n != nil && n.Type == TextNode { n.Data += text return } p.addChild(&Node{ Type: TextNode, Data: text, }) } // addElement adds a child element based on the current token. func (p *parser) addElement() { p.addChild(&Node{ Type: ElementNode, DataAtom: p.tok.DataAtom, Data: p.tok.Data, Attr: p.tok.Attr, }) } // Section 12.2.3.3. func (p *parser) addFormattingElement() { tagAtom, attr := p.tok.DataAtom, p.tok.Attr p.addElement() // Implement the Noah's Ark clause, but with three per family instead of two. identicalElements := 0 findIdenticalElements: for i := len(p.afe) - 1; i >= 0; i-- { n := p.afe[i] if n.Type == scopeMarkerNode { break } if n.Type != ElementNode { continue } if n.Namespace != "" { continue } if n.DataAtom != tagAtom { continue } if len(n.Attr) != len(attr) { continue } compareAttributes: for _, t0 := range n.Attr { for _, t1 := range attr { if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val { // Found a match for this attribute, continue with the next attribute. continue compareAttributes } } // If we get here, there is no attribute that matches a. // Therefore the element is not identical to the new one. continue findIdenticalElements } identicalElements++ if identicalElements >= 3 { p.afe.remove(n) } } p.afe = append(p.afe, p.top()) } // Section 12.2.3.3. func (p *parser) clearActiveFormattingElements() { for { n := p.afe.pop() if len(p.afe) == 0 || n.Type == scopeMarkerNode { return } } } // Section 12.2.3.3. func (p *parser) reconstructActiveFormattingElements() { n := p.afe.top() if n == nil { return } if n.Type == scopeMarkerNode || p.oe.index(n) != -1 { return } i := len(p.afe) - 1 for n.Type != scopeMarkerNode && p.oe.index(n) == -1 { if i == 0 { i = -1 break } i-- n = p.afe[i] } for { i++ clone := p.afe[i].clone() p.addChild(clone) p.afe[i] = clone if i == len(p.afe)-1 { break } } } // Section 12.2.4. func (p *parser) acknowledgeSelfClosingTag() { p.hasSelfClosingToken = false } // An insertion mode (section 12.2.3.1) is the state transition function from // a particular state in the HTML5 parser's state machine. It updates the // parser's fields depending on parser.tok (where ErrorToken means EOF). // It returns whether the token was consumed. type insertionMode func(*parser) bool // setOriginalIM sets the insertion mode to return to after completing a text or // inTableText insertion mode. // Section 12.2.3.1, "using the rules for". func (p *parser) setOriginalIM() { if p.originalIM != nil { panic("html: bad parser state: originalIM was set twice") } p.originalIM = p.im } // Section 12.2.3.1, "reset the insertion mode". func (p *parser) resetInsertionMode() { for i := len(p.oe) - 1; i >= 0; i-- { n := p.oe[i] if i == 0 && p.context != nil { n = p.context } switch n.DataAtom { case a.Select: p.im = inSelectIM case a.Td, a.Th: p.im = inCellIM case a.Tr: p.im = inRowIM case a.Tbody, a.Thead, a.Tfoot: p.im = inTableBodyIM case a.Caption: p.im = inCaptionIM case a.Colgroup: p.im = inColumnGroupIM case a.Table: p.im = inTableIM case a.Head: p.im = inBodyIM case a.Body: p.im = inBodyIM case a.Frameset: p.im = inFramesetIM case a.Html: p.im = beforeHeadIM default: continue } return } p.im = inBodyIM } const whitespace = " \t\r\n\f" // Section 12.2.5.4.1. func initialIM(p *parser) bool { switch p.tok.Type { case TextToken: p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) if len(p.tok.Data) == 0 { // It was all whitespace, so ignore it. return true } case CommentToken: p.doc.AppendChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: n, quirks := parseDoctype(p.tok.Data) p.doc.AppendChild(n) p.quirks = quirks p.im = beforeHTMLIM return true } p.quirks = true p.im = beforeHTMLIM return false } // Section 12.2.5.4.2. func beforeHTMLIM(p *parser) bool { switch p.tok.Type { case DoctypeToken: // Ignore the token. return true case TextToken: p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) if len(p.tok.Data) == 0 { // It was all whitespace, so ignore it. return true } case StartTagToken: if p.tok.DataAtom == a.Html { p.addElement() p.im = beforeHeadIM return true } case EndTagToken: switch p.tok.DataAtom { case a.Head, a.Body, a.Html, a.Br: p.parseImpliedToken(StartTagToken, a.Html, a.Html.String()) return false default: // Ignore the token. return true } case CommentToken: p.doc.AppendChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true } p.parseImpliedToken(StartTagToken, a.Html, a.Html.String()) return false } // Section 12.2.5.4.3. func beforeHeadIM(p *parser) bool { switch p.tok.Type { case TextToken: p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace) if len(p.tok.Data) == 0 { // It was all whitespace, so ignore it. return true } case StartTagToken: switch p.tok.DataAtom { case a.Head: p.addElement() p.head = p.top() p.im = inHeadIM return true case a.Html: return inBodyIM(p) } case EndTagToken: switch p.tok.DataAtom { case a.Head, a.Body, a.Html, a.Br: p.parseImpliedToken(StartTagToken, a.Head, a.Head.String()) return false default: // Ignore the token. return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: // Ignore the token. return true } p.parseImpliedToken(StartTagToken, a.Head, a.Head.String()) return false } // Section 12.2.5.4.4. func inHeadIM(p *parser) bool { switch p.tok.Type { case TextToken: s := strings.TrimLeft(p.tok.Data, whitespace) if len(s) < len(p.tok.Data) { // Add the initial whitespace to the current node. p.addText(p.tok.Data[:len(p.tok.Data)-len(s)]) if s == "" { return true } p.tok.Data = s } case StartTagToken: switch p.tok.DataAtom { case a.Html: return inBodyIM(p) case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta: p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() return true case a.Script, a.Title, a.Noscript, a.Noframes, a.Style: p.addElement() p.setOriginalIM() p.im = textIM return true case a.Head: // Ignore the token. return true } case EndTagToken: switch p.tok.DataAtom { case a.Head: n := p.oe.pop() if n.DataAtom != a.Head { panic("html: bad parser state: <head> element not found, in the in-head insertion mode") } p.im = afterHeadIM return true case a.Body, a.Html, a.Br: p.parseImpliedToken(EndTagToken, a.Head, a.Head.String()) return false default: // Ignore the token. return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: // Ignore the token. return true } p.parseImpliedToken(EndTagToken, a.Head, a.Head.String()) return false } // Section 12.2.5.4.6. func afterHeadIM(p *parser) bool { switch p.tok.Type { case TextToken: s := strings.TrimLeft(p.tok.Data, whitespace) if len(s) < len(p.tok.Data) { // Add the initial whitespace to the current node. p.addText(p.tok.Data[:len(p.tok.Data)-len(s)]) if s == "" { return true } p.tok.Data = s } case StartTagToken: switch p.tok.DataAtom { case a.Html: return inBodyIM(p) case a.Body: p.addElement() p.framesetOK = false p.im = inBodyIM return true case a.Frameset: p.addElement() p.im = inFramesetIM return true case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title: p.oe = append(p.oe, p.head) defer p.oe.remove(p.head) return inHeadIM(p) case a.Head: // Ignore the token. return true } case EndTagToken: switch p.tok.DataAtom { case a.Body, a.Html, a.Br: // Drop down to creating an implied <body> tag. default: // Ignore the token. return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: // Ignore the token. return true } p.parseImpliedToken(StartTagToken, a.Body, a.Body.String()) p.framesetOK = true return false } // copyAttributes copies attributes of src not found on dst to dst. func copyAttributes(dst *Node, src Token) { if len(src.Attr) == 0 { return } attr := map[string]string{} for _, t := range dst.Attr { attr[t.Key] = t.Val } for _, t := range src.Attr { if _, ok := attr[t.Key]; !ok { dst.Attr = append(dst.Attr, t) attr[t.Key] = t.Val } } } // Section 12.2.5.4.7. func inBodyIM(p *parser) bool { switch p.tok.Type { case TextToken: d := p.tok.Data switch n := p.oe.top(); n.DataAtom { case a.Pre, a.Listing: if n.FirstChild == nil { // Ignore a newline at the start of a <pre> block. if d != "" && d[0] == '\r' { d = d[1:] } if d != "" && d[0] == '\n' { d = d[1:] } } } d = strings.Replace(d, "\x00", "", -1) if d == "" { return true } p.reconstructActiveFormattingElements() p.addText(d) if p.framesetOK && strings.TrimLeft(d, whitespace) != "" { // There were non-whitespace characters inserted. p.framesetOK = false } case StartTagToken: switch p.tok.DataAtom { case a.Html: copyAttributes(p.oe[0], p.tok) case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title: return inHeadIM(p) case a.Body: if len(p.oe) >= 2 { body := p.oe[1] if body.Type == ElementNode && body.DataAtom == a.Body { p.framesetOK = false copyAttributes(body, p.tok) } } case a.Frameset: if !p.framesetOK || len(p.oe) < 2 || p.oe[1].DataAtom != a.Body { // Ignore the token. return true } body := p.oe[1] if body.Parent != nil { body.Parent.RemoveChild(body) } p.oe = p.oe[:1] p.addElement() p.im = inFramesetIM return true case a.Address, a.Article, a.Aside, a.Blockquote, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Menu, a.Nav, a.Ol, a.P, a.Section, a.Summary, a.Ul: p.popUntil(buttonScope, a.P) p.addElement() case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6: p.popUntil(buttonScope, a.P) switch n := p.top(); n.DataAtom { case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6: p.oe.pop() } p.addElement() case a.Pre, a.Listing: p.popUntil(buttonScope, a.P) p.addElement() // The newline, if any, will be dealt with by the TextToken case. p.framesetOK = false case a.Form: if p.form == nil { p.popUntil(buttonScope, a.P) p.addElement() p.form = p.top() } case a.Li: p.framesetOK = false for i := len(p.oe) - 1; i >= 0; i-- { node := p.oe[i] switch node.DataAtom { case a.Li: p.oe = p.oe[:i] case a.Address, a.Div, a.P: continue default: if !isSpecialElement(node) { continue } } break } p.popUntil(buttonScope, a.P) p.addElement() case a.Dd, a.Dt: p.framesetOK = false for i := len(p.oe) - 1; i >= 0; i-- { node := p.oe[i] switch node.DataAtom { case a.Dd, a.Dt: p.oe = p.oe[:i] case a.Address, a.Div, a.P: continue default: if !isSpecialElement(node) { continue } } break } p.popUntil(buttonScope, a.P) p.addElement() case a.Plaintext: p.popUntil(buttonScope, a.P) p.addElement() case a.Button: p.popUntil(defaultScope, a.Button) p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false case a.A: for i := len(p.afe) - 1; i >= 0 && p.afe[i].Type != scopeMarkerNode; i-- { if n := p.afe[i]; n.Type == ElementNode && n.DataAtom == a.A { p.inBodyEndTagFormatting(a.A) p.oe.remove(n) p.afe.remove(n) break } } p.reconstructActiveFormattingElements() p.addFormattingElement() case a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U: p.reconstructActiveFormattingElements() p.addFormattingElement() case a.Nobr: p.reconstructActiveFormattingElements() if p.elementInScope(defaultScope, a.Nobr) { p.inBodyEndTagFormatting(a.Nobr) p.reconstructActiveFormattingElements() } p.addFormattingElement() case a.Applet, a.Marquee, a.Object: p.reconstructActiveFormattingElements() p.addElement() p.afe = append(p.afe, &scopeMarker) p.framesetOK = false case a.Table: if !p.quirks { p.popUntil(buttonScope, a.P) } p.addElement() p.framesetOK = false p.im = inTableIM return true case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr: p.reconstructActiveFormattingElements() p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() if p.tok.DataAtom == a.Input { for _, t := range p.tok.Attr { if t.Key == "type" { if strings.ToLower(t.Val) == "hidden" { // Skip setting framesetOK = false return true } } } } p.framesetOK = false case a.Param, a.Source, a.Track: p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() case a.Hr: p.popUntil(buttonScope, a.P) p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() p.framesetOK = false case a.Image: p.tok.DataAtom = a.Img p.tok.Data = a.Img.String() return false case a.Isindex: if p.form != nil { // Ignore the token. return true } action := "" prompt := "This is a searchable index. Enter search keywords: " attr := []Attribute{{Key: "name", Val: "isindex"}} for _, t := range p.tok.Attr { switch t.Key { case "action": action = t.Val case "name": // Ignore the attribute. case "prompt": prompt = t.Val default: attr = append(attr, t) } } p.acknowledgeSelfClosingTag() p.popUntil(buttonScope, a.P) p.parseImpliedToken(StartTagToken, a.Form, a.Form.String()) if action != "" { p.form.Attr = []Attribute{{Key: "action", Val: action}} } p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String()) p.parseImpliedToken(StartTagToken, a.Label, a.Label.String()) p.addText(prompt) p.addChild(&Node{ Type: ElementNode, DataAtom: a.Input, Data: a.Input.String(), Attr: attr, }) p.oe.pop() p.parseImpliedToken(EndTagToken, a.Label, a.Label.String()) p.parseImpliedToken(StartTagToken, a.Hr, a.Hr.String()) p.parseImpliedToken(EndTagToken, a.Form, a.Form.String()) case a.Textarea: p.addElement() p.setOriginalIM() p.framesetOK = false p.im = textIM case a.Xmp: p.popUntil(buttonScope, a.P) p.reconstructActiveFormattingElements() p.framesetOK = false p.addElement() p.setOriginalIM() p.im = textIM case a.Iframe: p.framesetOK = false p.addElement() p.setOriginalIM() p.im = textIM case a.Noembed, a.Noscript: p.addElement() p.setOriginalIM() p.im = textIM case a.Select: p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false p.im = inSelectIM return true case a.Optgroup, a.Option: if p.top().DataAtom == a.Option { p.oe.pop() } p.reconstructActiveFormattingElements() p.addElement() case a.Rp, a.Rt: if p.elementInScope(defaultScope, a.Ruby) { p.generateImpliedEndTags() } p.addElement() case a.Math, a.Svg: p.reconstructActiveFormattingElements() if p.tok.DataAtom == a.Math { adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments) } else { adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments) } adjustForeignAttributes(p.tok.Attr) p.addElement() p.top().Namespace = p.tok.Data if p.hasSelfClosingToken { p.oe.pop() p.acknowledgeSelfClosingTag() } return true case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: // Ignore the token. default: p.reconstructActiveFormattingElements() p.addElement() } case EndTagToken: switch p.tok.DataAtom { case a.Body: if p.elementInScope(defaultScope, a.Body) { p.im = afterBodyIM } case a.Html: if p.elementInScope(defaultScope, a.Body) { p.parseImpliedToken(EndTagToken, a.Body, a.Body.String()) return false } return true case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul: p.popUntil(defaultScope, p.tok.DataAtom) case a.Form: node := p.form p.form = nil i := p.indexOfElementInScope(defaultScope, a.Form) if node == nil || i == -1 || p.oe[i] != node { // Ignore the token. return true } p.generateImpliedEndTags() p.oe.remove(node) case a.P: if !p.elementInScope(buttonScope, a.P) { p.parseImpliedToken(StartTagToken, a.P, a.P.String()) } p.popUntil(buttonScope, a.P) case a.Li: p.popUntil(listItemScope, a.Li) case a.Dd, a.Dt: p.popUntil(defaultScope, p.tok.DataAtom) case a.H1, a.H2, a.H3, a.H4, a.H5, a.H6: p.popUntil(defaultScope, a.H1, a.H2, a.H3, a.H4, a.H5, a.H6) case a.A, a.B, a.Big, a.Code, a.Em, a.Font, a.I, a.Nobr, a.S, a.Small, a.Strike, a.Strong, a.Tt, a.U: p.inBodyEndTagFormatting(p.tok.DataAtom) case a.Applet, a.Marquee, a.Object: if p.popUntil(defaultScope, p.tok.DataAtom) { p.clearActiveFormattingElements() } case a.Br: p.tok.Type = StartTagToken return false default: p.inBodyEndTagOther(p.tok.DataAtom) } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) } return true } func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) { // This is the "adoption agency" algorithm, described at // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#adoptionAgency // TODO: this is a fairly literal line-by-line translation of that algorithm. // Once the code successfully parses the comprehensive test suite, we should // refactor this code to be more idiomatic. // Steps 1-3. The outer loop. for i := 0; i < 8; i++ { // Step 4. Find the formatting element. var formattingElement *Node for j := len(p.afe) - 1; j >= 0; j-- { if p.afe[j].Type == scopeMarkerNode { break } if p.afe[j].DataAtom == tagAtom { formattingElement = p.afe[j] break } } if formattingElement == nil { p.inBodyEndTagOther(tagAtom) return } feIndex := p.oe.index(formattingElement) if feIndex == -1 { p.afe.remove(formattingElement) return } if !p.elementInScope(defaultScope, tagAtom) { // Ignore the tag. return } // Steps 5-6. Find the furthest block. var furthestBlock *Node for _, e := range p.oe[feIndex:] { if isSpecialElement(e) { furthestBlock = e break } } if furthestBlock == nil { e := p.oe.pop() for e != formattingElement { e = p.oe.pop() } p.afe.remove(e) return } // Steps 7-8. Find the common ancestor and bookmark node. commonAncestor := p.oe[feIndex-1] bookmark := p.afe.index(formattingElement) // Step 9. The inner loop. Find the lastNode to reparent. lastNode := furthestBlock node := furthestBlock x := p.oe.index(node) // Steps 9.1-9.3. for j := 0; j < 3; j++ { // Step 9.4. x-- node = p.oe[x] // Step 9.5. if p.afe.index(node) == -1 { p.oe.remove(node) continue } // Step 9.6. if node == formattingElement { break } // Step 9.7. clone := node.clone() p.afe[p.afe.index(node)] = clone p.oe[p.oe.index(node)] = clone node = clone // Step 9.8. if lastNode == furthestBlock { bookmark = p.afe.index(node) + 1 } // Step 9.9. if lastNode.Parent != nil { lastNode.Parent.RemoveChild(lastNode) } node.AppendChild(lastNode) // Step 9.10. lastNode = node } // Step 10. Reparent lastNode to the common ancestor, // or for misnested table nodes, to the foster parent. if lastNode.Parent != nil { lastNode.Parent.RemoveChild(lastNode) } switch commonAncestor.DataAtom { case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: p.fosterParent(lastNode) default: commonAncestor.AppendChild(lastNode) } // Steps 11-13. Reparent nodes from the furthest block's children // to a clone of the formatting element. clone := formattingElement.clone() reparentChildren(clone, furthestBlock) furthestBlock.AppendChild(clone) // Step 14. Fix up the list of active formatting elements. if oldLoc := p.afe.index(formattingElement); oldLoc != -1 && oldLoc < bookmark { // Move the bookmark with the rest of the list. bookmark-- } p.afe.remove(formattingElement) p.afe.insert(bookmark, clone) // Step 15. Fix up the stack of open elements. p.oe.remove(formattingElement) p.oe.insert(p.oe.index(furthestBlock)+1, clone) } } // inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM. func (p *parser) inBodyEndTagOther(tagAtom a.Atom) { for i := len(p.oe) - 1; i >= 0; i-- { if p.oe[i].DataAtom == tagAtom { p.oe = p.oe[:i] break } if isSpecialElement(p.oe[i]) { break } } } // Section 12.2.5.4.8. func textIM(p *parser) bool { switch p.tok.Type { case ErrorToken: p.oe.pop() case TextToken: d := p.tok.Data if n := p.oe.top(); n.DataAtom == a.Textarea && n.FirstChild == nil { // Ignore a newline at the start of a <textarea> block. if d != "" && d[0] == '\r' { d = d[1:] } if d != "" && d[0] == '\n' { d = d[1:] } } if d == "" { return true } p.addText(d) return true case EndTagToken: p.oe.pop() } p.im = p.originalIM p.originalIM = nil return p.tok.Type == EndTagToken } // Section 12.2.5.4.9. func inTableIM(p *parser) bool { switch p.tok.Type { case ErrorToken: // Stop parsing. return true case TextToken: p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1) switch p.oe.top().DataAtom { case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: if strings.Trim(p.tok.Data, whitespace) == "" { p.addText(p.tok.Data) return true } } case StartTagToken: switch p.tok.DataAtom { case a.Caption: p.clearStackToContext(tableScope) p.afe = append(p.afe, &scopeMarker) p.addElement() p.im = inCaptionIM return true case a.Colgroup: p.clearStackToContext(tableScope) p.addElement() p.im = inColumnGroupIM return true case a.Col: p.parseImpliedToken(StartTagToken, a.Colgroup, a.Colgroup.String()) return false case a.Tbody, a.Tfoot, a.Thead: p.clearStackToContext(tableScope) p.addElement() p.im = inTableBodyIM return true case a.Td, a.Th, a.Tr: p.parseImpliedToken(StartTagToken, a.Tbody, a.Tbody.String()) return false case a.Table: if p.popUntil(tableScope, a.Table) { p.resetInsertionMode() return false } // Ignore the token. return true case a.Style, a.Script: return inHeadIM(p) case a.Input: for _, t := range p.tok.Attr { if t.Key == "type" && strings.ToLower(t.Val) == "hidden" { p.addElement() p.oe.pop() return true } } // Otherwise drop down to the default action. case a.Form: if p.form != nil { // Ignore the token. return true } p.addElement() p.form = p.oe.pop() case a.Select: p.reconstructActiveFormattingElements() switch p.top().DataAtom { case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: p.fosterParenting = true } p.addElement() p.fosterParenting = false p.framesetOK = false p.im = inSelectInTableIM return true } case EndTagToken: switch p.tok.DataAtom { case a.Table: if p.popUntil(tableScope, a.Table) { p.resetInsertionMode() return true } // Ignore the token. return true case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: // Ignore the token. return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: // Ignore the token. return true } p.fosterParenting = true defer func() { p.fosterParenting = false }() return inBodyIM(p) } // Section 12.2.5.4.11. func inCaptionIM(p *parser) bool { switch p.tok.Type { case StartTagToken: switch p.tok.DataAtom { case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Thead, a.Tr: if p.popUntil(tableScope, a.Caption) { p.clearActiveFormattingElements() p.im = inTableIM return false } else { // Ignore the token. return true } case a.Select: p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false p.im = inSelectInTableIM return true } case EndTagToken: switch p.tok.DataAtom { case a.Caption: if p.popUntil(tableScope, a.Caption) { p.clearActiveFormattingElements() p.im = inTableIM } return true case a.Table: if p.popUntil(tableScope, a.Caption) { p.clearActiveFormattingElements() p.im = inTableIM return false } else { // Ignore the token. return true } case a.Body, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: // Ignore the token. return true } } return inBodyIM(p) } // Section 12.2.5.4.12. func inColumnGroupIM(p *parser) bool { switch p.tok.Type { case TextToken: s := strings.TrimLeft(p.tok.Data, whitespace) if len(s) < len(p.tok.Data) { // Add the initial whitespace to the current node. p.addText(p.tok.Data[:len(p.tok.Data)-len(s)]) if s == "" { return true } p.tok.Data = s } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: // Ignore the token. return true case StartTagToken: switch p.tok.DataAtom { case a.Html: return inBodyIM(p) case a.Col: p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() return true } case EndTagToken: switch p.tok.DataAtom { case a.Colgroup: if p.oe.top().DataAtom != a.Html { p.oe.pop() p.im = inTableIM } return true case a.Col: // Ignore the token. return true } } if p.oe.top().DataAtom != a.Html { p.oe.pop() p.im = inTableIM return false } return true } // Section 12.2.5.4.13. func inTableBodyIM(p *parser) bool { switch p.tok.Type { case StartTagToken: switch p.tok.DataAtom { case a.Tr: p.clearStackToContext(tableBodyScope) p.addElement() p.im = inRowIM return true case a.Td, a.Th: p.parseImpliedToken(StartTagToken, a.Tr, a.Tr.String()) return false case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Tfoot, a.Thead: if p.popUntil(tableScope, a.Tbody, a.Thead, a.Tfoot) { p.im = inTableIM return false } // Ignore the token. return true } case EndTagToken: switch p.tok.DataAtom { case a.Tbody, a.Tfoot, a.Thead: if p.elementInScope(tableScope, p.tok.DataAtom) { p.clearStackToContext(tableBodyScope) p.oe.pop() p.im = inTableIM } return true case a.Table: if p.popUntil(tableScope, a.Tbody, a.Thead, a.Tfoot) { p.im = inTableIM return false } // Ignore the token. return true case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Td, a.Th, a.Tr: // Ignore the token. return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true } return inTableIM(p) } // Section 12.2.5.4.14. func inRowIM(p *parser) bool { switch p.tok.Type { case StartTagToken: switch p.tok.DataAtom { case a.Td, a.Th: p.clearStackToContext(tableRowScope) p.addElement() p.afe = append(p.afe, &scopeMarker) p.im = inCellIM return true case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Tfoot, a.Thead, a.Tr: if p.popUntil(tableScope, a.Tr) { p.im = inTableBodyIM return false } // Ignore the token. return true } case EndTagToken: switch p.tok.DataAtom { case a.Tr: if p.popUntil(tableScope, a.Tr) { p.im = inTableBodyIM return true } // Ignore the token. return true case a.Table: if p.popUntil(tableScope, a.Tr) { p.im = inTableBodyIM return false } // Ignore the token. return true case a.Tbody, a.Tfoot, a.Thead: if p.elementInScope(tableScope, p.tok.DataAtom) { p.parseImpliedToken(EndTagToken, a.Tr, a.Tr.String()) return false } // Ignore the token. return true case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Td, a.Th: // Ignore the token. return true } } return inTableIM(p) } // Section 12.2.5.4.15. func inCellIM(p *parser) bool { switch p.tok.Type { case StartTagToken: switch p.tok.DataAtom { case a.Caption, a.Col, a.Colgroup, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr: if p.popUntil(tableScope, a.Td, a.Th) { // Close the cell and reprocess. p.clearActiveFormattingElements() p.im = inRowIM return false } // Ignore the token. return true case a.Select: p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false p.im = inSelectInTableIM return true } case EndTagToken: switch p.tok.DataAtom { case a.Td, a.Th: if !p.popUntil(tableScope, p.tok.DataAtom) { // Ignore the token. return true } p.clearActiveFormattingElements() p.im = inRowIM return true case a.Body, a.Caption, a.Col, a.Colgroup, a.Html: // Ignore the token. return true case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: if !p.elementInScope(tableScope, p.tok.DataAtom) { // Ignore the token. return true } // Close the cell and reprocess. p.popUntil(tableScope, a.Td, a.Th) p.clearActiveFormattingElements() p.im = inRowIM return false } } return inBodyIM(p) } // Section 12.2.5.4.16. func inSelectIM(p *parser) bool { switch p.tok.Type { case ErrorToken: // Stop parsing. return true case TextToken: p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) case StartTagToken: switch p.tok.DataAtom { case a.Html: return inBodyIM(p) case a.Option: if p.top().DataAtom == a.Option { p.oe.pop() } p.addElement() case a.Optgroup: if p.top().DataAtom == a.Option { p.oe.pop() } if p.top().DataAtom == a.Optgroup { p.oe.pop() } p.addElement() case a.Select: p.tok.Type = EndTagToken return false case a.Input, a.Keygen, a.Textarea: if p.elementInScope(selectScope, a.Select) { p.parseImpliedToken(EndTagToken, a.Select, a.Select.String()) return false } // In order to properly ignore <textarea>, we need to change the tokenizer mode. p.tokenizer.NextIsNotRawText() // Ignore the token. return true case a.Script: return inHeadIM(p) } case EndTagToken: switch p.tok.DataAtom { case a.Option: if p.top().DataAtom == a.Option { p.oe.pop() } case a.Optgroup: i := len(p.oe) - 1 if p.oe[i].DataAtom == a.Option { i-- } if p.oe[i].DataAtom == a.Optgroup { p.oe = p.oe[:i] } case a.Select: if p.popUntil(selectScope, a.Select) { p.resetInsertionMode() } } case CommentToken: p.doc.AppendChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) case DoctypeToken: // Ignore the token. return true } return true } // Section 12.2.5.4.17. func inSelectInTableIM(p *parser) bool { switch p.tok.Type { case StartTagToken, EndTagToken: switch p.tok.DataAtom { case a.Caption, a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr, a.Td, a.Th: if p.tok.Type == StartTagToken || p.elementInScope(tableScope, p.tok.DataAtom) { p.parseImpliedToken(EndTagToken, a.Select, a.Select.String()) return false } else { // Ignore the token. return true } } } return inSelectIM(p) } // Section 12.2.5.4.18. func afterBodyIM(p *parser) bool { switch p.tok.Type { case ErrorToken: // Stop parsing. return true case TextToken: s := strings.TrimLeft(p.tok.Data, whitespace) if len(s) == 0 { // It was all whitespace. return inBodyIM(p) } case StartTagToken: if p.tok.DataAtom == a.Html { return inBodyIM(p) } case EndTagToken: if p.tok.DataAtom == a.Html { if !p.fragment { p.im = afterAfterBodyIM } return true } case CommentToken: // The comment is attached to the <html> element. if len(p.oe) < 1 || p.oe[0].DataAtom != a.Html { panic("html: bad parser state: <html> element not found, in the after-body insertion mode") } p.oe[0].AppendChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true } p.im = inBodyIM return false } // Section 12.2.5.4.19. func inFramesetIM(p *parser) bool { switch p.tok.Type { case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) case TextToken: // Ignore all text but whitespace. s := strings.Map(func(c rune) rune { switch c { case ' ', '\t', '\n', '\f', '\r': return c } return -1 }, p.tok.Data) if s != "" { p.addText(s) } case StartTagToken: switch p.tok.DataAtom { case a.Html: return inBodyIM(p) case a.Frameset: p.addElement() case a.Frame: p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() case a.Noframes: return inHeadIM(p) } case EndTagToken: switch p.tok.DataAtom { case a.Frameset: if p.oe.top().DataAtom != a.Html { p.oe.pop() if p.oe.top().DataAtom != a.Frameset { p.im = afterFramesetIM return true } } } default: // Ignore the token. } return true } // Section 12.2.5.4.20. func afterFramesetIM(p *parser) bool { switch p.tok.Type { case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) case TextToken: // Ignore all text but whitespace. s := strings.Map(func(c rune) rune { switch c { case ' ', '\t', '\n', '\f', '\r': return c } return -1 }, p.tok.Data) if s != "" { p.addText(s) } case StartTagToken: switch p.tok.DataAtom { case a.Html: return inBodyIM(p) case a.Noframes: return inHeadIM(p) } case EndTagToken: switch p.tok.DataAtom { case a.Html: p.im = afterAfterFramesetIM return true } default: // Ignore the token. } return true } // Section 12.2.5.4.21. func afterAfterBodyIM(p *parser) bool { switch p.tok.Type { case ErrorToken: // Stop parsing. return true case TextToken: s := strings.TrimLeft(p.tok.Data, whitespace) if len(s) == 0 { // It was all whitespace. return inBodyIM(p) } case StartTagToken: if p.tok.DataAtom == a.Html { return inBodyIM(p) } case CommentToken: p.doc.AppendChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) return true case DoctypeToken: return inBodyIM(p) } p.im = inBodyIM return false } // Section 12.2.5.4.22. func afterAfterFramesetIM(p *parser) bool { switch p.tok.Type { case CommentToken: p.doc.AppendChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) case TextToken: // Ignore all text but whitespace. s := strings.Map(func(c rune) rune { switch c { case ' ', '\t', '\n', '\f', '\r': return c } return -1 }, p.tok.Data) if s != "" { p.tok.Data = s return inBodyIM(p) } case StartTagToken: switch p.tok.DataAtom { case a.Html: return inBodyIM(p) case a.Noframes: return inHeadIM(p) } case DoctypeToken: return inBodyIM(p) default: // Ignore the token. } return true } const whitespaceOrNUL = whitespace + "\x00" // Section 12.2.5.5. func parseForeignContent(p *parser) bool { switch p.tok.Type { case TextToken: if p.framesetOK { p.framesetOK = strings.TrimLeft(p.tok.Data, whitespaceOrNUL) == "" } p.tok.Data = strings.Replace(p.tok.Data, "\x00", "\ufffd", -1) p.addText(p.tok.Data) case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) case StartTagToken: b := breakout[p.tok.Data] if p.tok.DataAtom == a.Font { loop: for _, attr := range p.tok.Attr { switch attr.Key { case "color", "face", "size": b = true break loop } } } if b { for i := len(p.oe) - 1; i >= 0; i-- { n := p.oe[i] if n.Namespace == "" || htmlIntegrationPoint(n) || mathMLTextIntegrationPoint(n) { p.oe = p.oe[:i+1] break } } return false } switch p.top().Namespace { case "math": adjustAttributeNames(p.tok.Attr, mathMLAttributeAdjustments) case "svg": // Adjust SVG tag names. The tokenizer lower-cases tag names, but // SVG wants e.g. "foreignObject" with a capital second "O". if x := svgTagNameAdjustments[p.tok.Data]; x != "" { p.tok.DataAtom = a.Lookup([]byte(x)) p.tok.Data = x } adjustAttributeNames(p.tok.Attr, svgAttributeAdjustments) default: panic("html: bad parser state: unexpected namespace") } adjustForeignAttributes(p.tok.Attr) namespace := p.top().Namespace p.addElement() p.top().Namespace = namespace if namespace != "" { // Don't let the tokenizer go into raw text mode in foreign content // (e.g. in an SVG <title> tag). p.tokenizer.NextIsNotRawText() } if p.hasSelfClosingToken { p.oe.pop() p.acknowledgeSelfClosingTag() } case EndTagToken: for i := len(p.oe) - 1; i >= 0; i-- { if p.oe[i].Namespace == "" { return p.im(p) } if strings.EqualFold(p.oe[i].Data, p.tok.Data) { p.oe = p.oe[:i] break } } return true default: // Ignore the token. } return true } // Section 12.2.5. func (p *parser) inForeignContent() bool { if len(p.oe) == 0 { return false } n := p.oe[len(p.oe)-1] if n.Namespace == "" { return false } if mathMLTextIntegrationPoint(n) { if p.tok.Type == StartTagToken && p.tok.DataAtom != a.Mglyph && p.tok.DataAtom != a.Malignmark { return false } if p.tok.Type == TextToken { return false } } if n.Namespace == "math" && n.DataAtom == a.AnnotationXml && p.tok.Type == StartTagToken && p.tok.DataAtom == a.Svg { return false } if htmlIntegrationPoint(n) && (p.tok.Type == StartTagToken || p.tok.Type == TextToken) { return false } if p.tok.Type == ErrorToken { return false } return true } // parseImpliedToken parses a token as though it had appeared in the parser's // input. func (p *parser) parseImpliedToken(t TokenType, dataAtom a.Atom, data string) { realToken, selfClosing := p.tok, p.hasSelfClosingToken p.tok = Token{ Type: t, DataAtom: dataAtom, Data: data, } p.hasSelfClosingToken = false p.parseCurrentToken() p.tok, p.hasSelfClosingToken = realToken, selfClosing } // parseCurrentToken runs the current token through the parsing routines // until it is consumed. func (p *parser) parseCurrentToken() { if p.tok.Type == SelfClosingTagToken { p.hasSelfClosingToken = true p.tok.Type = StartTagToken } consumed := false for !consumed { if p.inForeignContent() { consumed = parseForeignContent(p) } else { consumed = p.im(p) } } if p.hasSelfClosingToken { // This is a parse error, but ignore it. p.hasSelfClosingToken = false } } func (p *parser) parse() error { // Iterate until EOF. Any other error will cause an early return. var err error for err != io.EOF { // CDATA sections are allowed only in foreign content. n := p.oe.top() p.tokenizer.AllowCDATA(n != nil && n.Namespace != "") // Read and parse the next token. p.tokenizer.Next() p.tok = p.tokenizer.Token() if p.tok.Type == ErrorToken { err = p.tokenizer.Err() if err != nil && err != io.EOF { return err } } p.parseCurrentToken() } return nil } // Parse returns the parse tree for the HTML from the given Reader. // The input is assumed to be UTF-8 encoded. func Parse(r io.Reader) (*Node, error) { p := &parser{ tokenizer: NewTokenizer(r), doc: &Node{ Type: DocumentNode, }, scripting: true, framesetOK: true, im: initialIM, } err := p.parse() if err != nil { return nil, err } return p.doc, nil } // ParseFragment parses a fragment of HTML and returns the nodes that were // found. If the fragment is the InnerHTML for an existing element, pass that // element in context. func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { contextTag := "" if context != nil { if context.Type != ElementNode { return nil, errors.New("html: ParseFragment of non-element Node") } // The next check isn't just context.DataAtom.String() == context.Data because // it is valid to pass an element whose tag isn't a known atom. For example, // DataAtom == 0 and Data = "tagfromthefuture" is perfectly consistent. if context.DataAtom != a.Lookup([]byte(context.Data)) { return nil, fmt.Errorf("html: inconsistent Node: DataAtom=%q, Data=%q", context.DataAtom, context.Data) } contextTag = context.DataAtom.String() } p := &parser{ tokenizer: NewTokenizerFragment(r, contextTag), doc: &Node{ Type: DocumentNode, }, scripting: true, fragment: true, context: context, } root := &Node{ Type: ElementNode, DataAtom: a.Html, Data: a.Html.String(), } p.doc.AppendChild(root) p.oe = nodeStack{root} p.resetInsertionMode() for n := context; n != nil; n = n.Parent { if n.Type == ElementNode && n.DataAtom == a.Form { p.form = n break } } err := p.parse() if err != nil { return nil, err } parent := p.doc if context != nil { parent = root } var result []*Node for c := parent.FirstChild; c != nil; { next := c.NextSibling parent.RemoveChild(c) result = append(result, c) c = next } return result, nil } �������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/render_test.go�����������������������������������0000644�0000153�0000161�00000005445�12321735652�024453� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 html import ( "bytes" "testing" ) func TestRenderer(t *testing.T) { nodes := [...]*Node{ 0: { Type: ElementNode, Data: "html", }, 1: { Type: ElementNode, Data: "head", }, 2: { Type: ElementNode, Data: "body", }, 3: { Type: TextNode, Data: "0<1", }, 4: { Type: ElementNode, Data: "p", Attr: []Attribute{ { Key: "id", Val: "A", }, { Key: "foo", Val: `abc"def`, }, }, }, 5: { Type: TextNode, Data: "2", }, 6: { Type: ElementNode, Data: "b", Attr: []Attribute{ { Key: "empty", Val: "", }, }, }, 7: { Type: TextNode, Data: "3", }, 8: { Type: ElementNode, Data: "i", Attr: []Attribute{ { Key: "backslash", Val: `\`, }, }, }, 9: { Type: TextNode, Data: "&4", }, 10: { Type: TextNode, Data: "5", }, 11: { Type: ElementNode, Data: "blockquote", }, 12: { Type: ElementNode, Data: "br", }, 13: { Type: TextNode, Data: "6", }, } // Build a tree out of those nodes, based on a textual representation. // Only the ".\t"s are significant. The trailing HTML-like text is // just commentary. The "0:" prefixes are for easy cross-reference with // the nodes array. treeAsText := [...]string{ 0: `<html>`, 1: `. <head>`, 2: `. <body>`, 3: `. . "0&lt;1"`, 4: `. . <p id="A" foo="abc&#34;def">`, 5: `. . . "2"`, 6: `. . . <b empty="">`, 7: `. . . . "3"`, 8: `. . . <i backslash="\">`, 9: `. . . . "&amp;4"`, 10: `. . "5"`, 11: `. . <blockquote>`, 12: `. . <br>`, 13: `. . "6"`, } if len(nodes) != len(treeAsText) { t.Fatal("len(nodes) != len(treeAsText)") } var stack [8]*Node for i, line := range treeAsText { level := 0 for line[0] == '.' { // Strip a leading ".\t". line = line[2:] level++ } n := nodes[i] if level == 0 { if stack[0] != nil { t.Fatal("multiple root nodes") } stack[0] = n } else { stack[level-1].AppendChild(n) stack[level] = n for i := level + 1; i < len(stack); i++ { stack[i] = nil } } // At each stage of tree construction, we check all nodes for consistency. for j, m := range nodes { if err := checkNodeConsistency(m); err != nil { t.Fatalf("i=%d, j=%d: %v", i, j, err) } } } want := `<html><head></head><body>0&lt;1<p id="A" foo="abc&#34;def">` + `2<b empty="">3</b><i backslash="\">&amp;4</i></p>` + `5<blockquote></blockquote><br/>6</body></html>` b := new(bytes.Buffer) if err := Render(b, nodes[0]); err != nil { t.Fatal(err) } if got := b.String(); got != want { t.Errorf("got vs want:\n%s\n%s\n", got, want) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/token.go�����������������������������������������0000644�0000153�0000161�00000072773�12321735652�023265� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 html import ( "bytes" "errors" "io" "strconv" "strings" "code.google.com/p/go.net/html/atom" ) // A TokenType is the type of a Token. type TokenType uint32 const ( // ErrorToken means that an error occurred during tokenization. ErrorToken TokenType = iota // TextToken means a text node. TextToken // A StartTagToken looks like <a>. StartTagToken // An EndTagToken looks like </a>. EndTagToken // A SelfClosingTagToken tag looks like <br/>. SelfClosingTagToken // A CommentToken looks like <!--x-->. CommentToken // A DoctypeToken looks like <!DOCTYPE x> 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 { case ErrorToken: return "Error" case TextToken: return "Text" case StartTagToken: return "StartTag" case EndTagToken: return "EndTag" case SelfClosingTagToken: return "SelfClosingTag" case CommentToken: return "Comment" case DoctypeToken: return "Doctype" } return "Invalid(" + strconv.Itoa(int(t)) + ")" } // An Attribute is an attribute namespace-key-value triple. Namespace is // non-empty for foreign attributes like xlink, Key is alphabetic (and hence // does not contain escapable characters like '&', '<' or '>'), and Val is // unescaped (it looks like "a<b" rather than "a&lt;b"). // // Namespace is only used by the parser, not the tokenizer. type Attribute struct { Namespace, Key, Val string } // A Token consists of a TokenType and some Data (tag name for start and end // tags, content for text, comments and doctypes). A tag Token may also contain // a slice of Attributes. Data is unescaped for all Tokens (it looks like "a<b" // rather than "a&lt;b"). For tag Tokens, DataAtom is the atom for Data, or // zero if Data is not a known tag name. type Token struct { Type TokenType DataAtom atom.Atom Data string Attr []Attribute } // tagString returns a string representation of a tag Token's Data and Attr. func (t Token) tagString() string { if len(t.Attr) == 0 { return t.Data } buf := bytes.NewBufferString(t.Data) for _, a := range t.Attr { buf.WriteByte(' ') buf.WriteString(a.Key) buf.WriteString(`="`) escape(buf, a.Val) buf.WriteByte('"') } return buf.String() } // String returns a string representation of the Token. func (t Token) String() string { switch t.Type { case ErrorToken: return "" case TextToken: return EscapeString(t.Data) case StartTagToken: return "<" + t.tagString() + ">" case EndTagToken: return "</" + t.tagString() + ">" case SelfClosingTagToken: return "<" + t.tagString() + "/>" case CommentToken: return "<!--" + t.Data + "-->" case DoctypeToken: return "<!DOCTYPE " + t.Data + ">" } return "Invalid(" + strconv.Itoa(int(t.Type)) + ")" } // span is a range of bytes in a Tokenizer's buffer. The start is inclusive, // the end is exclusive. type span struct { start, end int } // A Tokenizer returns a stream of HTML Tokens. type Tokenizer struct { // r is the source of the HTML text. r io.Reader // tt is the TokenType of the current token. tt TokenType // err is the first error encountered during tokenization. It is possible // for tt != Error && err != nil to hold: this means that Next returned a // valid token but the subsequent Next call will return an error token. // For example, if the HTML text input was just "plain", then the first // Next call would set z.err to io.EOF but return a TextToken, and all // subsequent Next calls would return an ErrorToken. // err is never reset. Once it becomes non-nil, it stays non-nil. err error // readErr is the error returned by the io.Reader r. It is separate from // err because it is valid for an io.Reader to return (n int, err1 error) // such that n > 0 && err1 != nil, and callers should always process the // n > 0 bytes before considering the error err1. readErr error // buf[raw.start:raw.end] holds the raw bytes of the current token. // 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 // pendingAttr is the attribute key and value currently being tokenized. // When complete, pendingAttr is pushed onto attr. nAttrReturned is // incremented on each call to TagAttr. pendingAttr [2]span attr [][2]span nAttrReturned int // rawTag is the "script" in "</script>" that closes the next token. If // non-empty, the subsequent call to Next will return a raw or RCDATA text // token: one that treats "<p>" as text instead of an element. // rawTag's contents are lower-cased. rawTag string // textIsRaw is whether the current text token's data is not escaped. textIsRaw bool // convertNUL is whether NUL bytes in the current token's data should // be converted into \ufffd replacement characters. convertNUL bool // allowCDATA is whether CDATA sections are allowed in the current context. allowCDATA bool } // AllowCDATA sets whether or not the tokenizer recognizes <![CDATA[foo]]> as // the text "foo". The default value is false, which means to recognize it as // a bogus comment "<!-- [CDATA[foo]] -->" instead. // // Strictly speaking, an HTML5 compliant tokenizer should allow CDATA if and // only if tokenizing foreign content, such as MathML and SVG. However, // tracking foreign-contentness is difficult to do purely in the tokenizer, // as opposed to the parser, due to HTML integration points: an <svg> element // can contain a <foreignObject> that is foreign-to-SVG but not foreign-to- // HTML. For strict compliance with the HTML5 tokenization algorithm, it is the // responsibility of the user of a tokenizer to call AllowCDATA as appropriate. // In practice, if using the tokenizer without caring whether MathML or SVG // CDATA is text or comments, such as tokenizing HTML to find all the anchor // text, it is acceptable to ignore this responsibility. func (z *Tokenizer) AllowCDATA(allowCDATA bool) { z.allowCDATA = allowCDATA } // NextIsNotRawText instructs the tokenizer that the next token should not be // considered as 'raw text'. Some elements, such as script and title elements, // normally require the next token after the opening tag to be 'raw text' that // has no child elements. For example, tokenizing "<title>a<b>c</b>d</title>" // yields a start tag token for "<title>", a text token for "a<b>c</b>d", and // an end tag token for "</title>". There are no distinct start tag or end tag // tokens for the "<b>" and "</b>". // // This tokenizer implementation will generally look for raw text at the right // times. Strictly speaking, an HTML5 compliant tokenizer should not look for // raw text if in foreign content: <title> generally needs raw text, but a // <title> inside an <svg> does not. Another example is that a <textarea> // generally needs raw text, but a <textarea> is not allowed as an immediate // child of a <select>; in normal parsing, a <textarea> implies </select>, but // one cannot close the implicit element when parsing a <select>'s InnerHTML. // Similarly to AllowCDATA, tracking the correct moment to override raw-text- // ness is difficult to do purely in the tokenizer, as opposed to the parser. // For strict compliance with the HTML5 tokenization algorithm, it is the // responsibility of the user of a tokenizer to call NextIsNotRawText as // appropriate. In practice, like AllowCDATA, it is acceptable to ignore this // responsibility for basic usage. // // Note that this 'raw text' concept is different from the one offered by the // Tokenizer.Raw method. func (z *Tokenizer) NextIsNotRawText() { z.rawTag = "" } // Err returns the error associated with the most recent ErrorToken token. // This is typically io.EOF, meaning the end of tokenization. func (z *Tokenizer) Err() error { if z.tt != ErrorToken { return nil } return z.err } // readByte returns the next byte from the input stream, doing a buffered read // from z.r into z.buf if necessary. z.buf[z.raw.start:z.raw.end] remains a contiguous byte // slice that holds all the bytes read so far for the current token. // It sets z.err if the underlying reader returns an error. // Pre-condition: z.err == nil. func (z *Tokenizer) readByte() byte { if z.raw.end >= len(z.buf) { // Our buffer is exhausted and we have to read from z.r. Check if the // previous read resulted in an error. if z.readErr != nil { z.err = z.readErr return 0 } // We copy z.buf[z.raw.start:z.raw.end] to the beginning of z.buf. If the length // z.raw.end - z.raw.start is more than half the capacity of z.buf, then we // allocate a new buffer before the copy. c := cap(z.buf) d := z.raw.end - z.raw.start var buf1 []byte if 2*d > c { buf1 = make([]byte, d, 2*c) } else { buf1 = z.buf[:d] } copy(buf1, z.buf[z.raw.start:z.raw.end]) if x := z.raw.start; x != 0 { // Adjust the data/attr spans to refer to the same contents after the copy. z.data.start -= x z.data.end -= x z.pendingAttr[0].start -= x z.pendingAttr[0].end -= x z.pendingAttr[1].start -= x z.pendingAttr[1].end -= x for i := range z.attr { z.attr[i][0].start -= x z.attr[i][0].end -= x z.attr[i][1].start -= x z.attr[i][1].end -= x } } z.raw.start, z.raw.end, z.buf = 0, d, buf1[:d] // Now that we have copied the live bytes to the start of the buffer, // we read from z.r into the remainder. var n int n, z.readErr = readAtLeastOneByte(z.r, buf1[d:cap(buf1)]) if n == 0 { z.err = z.readErr return 0 } z.buf = buf1[:d+n] } 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. func readAtLeastOneByte(r io.Reader, b []byte) (int, error) { for i := 0; i < 100; i++ { n, err := r.Read(b) if n != 0 || err != nil { return n, err } } return 0, io.ErrNoProgress } // skipWhiteSpace skips past any white space. func (z *Tokenizer) skipWhiteSpace() { if z.err != nil { return } for { c := z.readByte() if z.err != nil { return } switch c { case ' ', '\n', '\r', '\t', '\f': // No-op. default: z.raw.end-- return } } } // readRawOrRCDATA reads until the next "</foo>", where "foo" is z.rawTag and // is typically something like "script" or "textarea". func (z *Tokenizer) readRawOrRCDATA() { if z.rawTag == "script" { z.readScript() z.textIsRaw = true z.rawTag = "" return } loop: for { c := z.readByte() if z.err != nil { break loop } if c != '<' { continue loop } c = z.readByte() if z.err != nil { break loop } if c != '/' { continue loop } if z.readRawEndTag() || z.err != nil { break loop } } z.data.end = z.raw.end // A textarea's or title's RCDATA can contain escaped entities. z.textIsRaw = z.rawTag != "textarea" && z.rawTag != "title" z.rawTag = "" } // readRawEndTag attempts to read a tag like "</foo>", where "foo" is z.rawTag. // If it succeeds, it backs up the input position to reconsume the tag and // returns true. Otherwise it returns false. The opening "</" has already been // consumed. func (z *Tokenizer) readRawEndTag() bool { for i := 0; i < len(z.rawTag); i++ { c := z.readByte() if z.err != nil { return false } if c != z.rawTag[i] && c != z.rawTag[i]-('a'-'A') { z.raw.end-- return false } } c := z.readByte() if z.err != nil { return false } switch c { case ' ', '\n', '\r', '\t', '\f', '/', '>': // The 3 is 2 for the leading "</" plus 1 for the trailing character c. z.raw.end -= 3 + len(z.rawTag) return true } z.raw.end-- return false } // readScript reads until the next </script> tag, following the byzantine // rules for escaping/hiding the closing tag. func (z *Tokenizer) readScript() { defer func() { z.data.end = z.raw.end }() var c byte scriptData: c = z.readByte() if z.err != nil { return } if c == '<' { goto scriptDataLessThanSign } goto scriptData scriptDataLessThanSign: c = z.readByte() if z.err != nil { return } switch c { case '/': goto scriptDataEndTagOpen case '!': goto scriptDataEscapeStart } z.raw.end-- goto scriptData scriptDataEndTagOpen: if z.readRawEndTag() || z.err != nil { return } goto scriptData scriptDataEscapeStart: c = z.readByte() if z.err != nil { return } if c == '-' { goto scriptDataEscapeStartDash } z.raw.end-- goto scriptData scriptDataEscapeStartDash: c = z.readByte() if z.err != nil { return } if c == '-' { goto scriptDataEscapedDashDash } z.raw.end-- goto scriptData scriptDataEscaped: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataEscapedDash case '<': goto scriptDataEscapedLessThanSign } goto scriptDataEscaped scriptDataEscapedDash: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataEscapedDashDash case '<': goto scriptDataEscapedLessThanSign } goto scriptDataEscaped scriptDataEscapedDashDash: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataEscapedDashDash case '<': goto scriptDataEscapedLessThanSign case '>': goto scriptData } goto scriptDataEscaped scriptDataEscapedLessThanSign: c = z.readByte() if z.err != nil { return } if c == '/' { goto scriptDataEscapedEndTagOpen } if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' { goto scriptDataDoubleEscapeStart } z.raw.end-- goto scriptData scriptDataEscapedEndTagOpen: if z.readRawEndTag() || z.err != nil { return } goto scriptDataEscaped scriptDataDoubleEscapeStart: z.raw.end-- for i := 0; i < len("script"); i++ { c = z.readByte() if z.err != nil { return } if c != "script"[i] && c != "SCRIPT"[i] { z.raw.end-- goto scriptDataEscaped } } c = z.readByte() if z.err != nil { return } switch c { case ' ', '\n', '\r', '\t', '\f', '/', '>': goto scriptDataDoubleEscaped } z.raw.end-- goto scriptDataEscaped scriptDataDoubleEscaped: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataDoubleEscapedDash case '<': goto scriptDataDoubleEscapedLessThanSign } goto scriptDataDoubleEscaped scriptDataDoubleEscapedDash: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataDoubleEscapedDashDash case '<': goto scriptDataDoubleEscapedLessThanSign } goto scriptDataDoubleEscaped scriptDataDoubleEscapedDashDash: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataDoubleEscapedDashDash case '<': goto scriptDataDoubleEscapedLessThanSign case '>': goto scriptData } goto scriptDataDoubleEscaped scriptDataDoubleEscapedLessThanSign: c = z.readByte() if z.err != nil { return } if c == '/' { goto scriptDataDoubleEscapeEnd } z.raw.end-- goto scriptDataDoubleEscaped scriptDataDoubleEscapeEnd: if z.readRawEndTag() { z.raw.end += len("</script>") goto scriptDataEscaped } if z.err != nil { return } goto scriptDataDoubleEscaped } // readComment reads the next comment token starting with "<!--". The opening // "<!--" has already been consumed. func (z *Tokenizer) readComment() { z.data.start = z.raw.end defer func() { if z.data.end < z.data.start { // It's a comment with no data, like <!-->. z.data.end = z.data.start } }() for dashCount := 2; ; { c := z.readByte() if z.err != nil { // Ignore up to two dashes at EOF. if dashCount > 2 { dashCount = 2 } z.data.end = z.raw.end - dashCount return } switch c { case '-': dashCount++ continue case '>': if dashCount >= 2 { z.data.end = z.raw.end - len("-->") return } case '!': if dashCount >= 2 { c = z.readByte() if z.err != nil { z.data.end = z.raw.end return } if c == '>' { z.data.end = z.raw.end - len("--!>") return } } } dashCount = 0 } } // readUntilCloseAngle reads until the next ">". func (z *Tokenizer) readUntilCloseAngle() { z.data.start = z.raw.end for { c := z.readByte() if z.err != nil { z.data.end = z.raw.end return } if c == '>' { z.data.end = z.raw.end - len(">") return } } } // readMarkupDeclaration reads the next token starting with "<!". It might be // a "<!--comment-->", a "<!DOCTYPE foo>", a "<![CDATA[section]]>" or // "<!a bogus comment". The opening "<!" has already been consumed. func (z *Tokenizer) readMarkupDeclaration() TokenType { z.data.start = z.raw.end var c [2]byte for i := 0; i < 2; i++ { c[i] = z.readByte() if z.err != nil { z.data.end = z.raw.end return CommentToken } } if c[0] == '-' && c[1] == '-' { z.readComment() return CommentToken } z.raw.end -= 2 if z.readDoctype() { return DoctypeToken } if z.allowCDATA && z.readCDATA() { z.convertNUL = true return TextToken } // It's a bogus comment. z.readUntilCloseAngle() return CommentToken } // readDoctype attempts to read a doctype declaration and returns true if // successful. The opening "<!" has already been consumed. func (z *Tokenizer) readDoctype() bool { const s = "DOCTYPE" for i := 0; i < len(s); i++ { c := z.readByte() if z.err != nil { z.data.end = z.raw.end return false } if c != s[i] && c != s[i]+('a'-'A') { // Back up to read the fragment of "DOCTYPE" again. z.raw.end = z.data.start return false } } if z.skipWhiteSpace(); z.err != nil { z.data.start = z.raw.end z.data.end = z.raw.end return true } z.readUntilCloseAngle() return true } // readCDATA attempts to read a CDATA section and returns true if // successful. The opening "<!" has already been consumed. func (z *Tokenizer) readCDATA() bool { const s = "[CDATA[" for i := 0; i < len(s); i++ { c := z.readByte() if z.err != nil { z.data.end = z.raw.end return false } if c != s[i] { // Back up to read the fragment of "[CDATA[" again. z.raw.end = z.data.start return false } } z.data.start = z.raw.end brackets := 0 for { c := z.readByte() if z.err != nil { z.data.end = z.raw.end return true } switch c { case ']': brackets++ case '>': if brackets >= 2 { z.data.end = z.raw.end - len("]]>") return true } brackets = 0 default: brackets = 0 } } } // startTagIn returns whether the start tag in z.buf[z.data.start:z.data.end] // case-insensitively matches any element of ss. func (z *Tokenizer) startTagIn(ss ...string) bool { loop: for _, s := range ss { if z.data.end-z.data.start != len(s) { continue loop } for i := 0; i < len(s); i++ { c := z.buf[z.data.start+i] if 'A' <= c && c <= 'Z' { c += 'a' - 'A' } if c != s[i] { continue loop } } return true } return false } // readStartTag reads the next start tag token. The opening "<a" has already // been consumed, where 'a' means anything in [A-Za-z]. func (z *Tokenizer) readStartTag() TokenType { z.readTag(true) if z.err != nil { return ErrorToken } // Several tags flag the tokenizer's next token as raw. c, raw := z.buf[z.data.start], false if 'A' <= c && c <= 'Z' { c += 'a' - 'A' } switch c { case 'i': raw = z.startTagIn("iframe") case 'n': raw = z.startTagIn("noembed", "noframes", "noscript") case 'p': raw = z.startTagIn("plaintext") case 's': raw = z.startTagIn("script", "style") case 't': raw = z.startTagIn("textarea", "title") case 'x': raw = z.startTagIn("xmp") } if raw { z.rawTag = strings.ToLower(string(z.buf[z.data.start:z.data.end])) } // Look for a self-closing token like "<br/>". if z.err == nil && z.buf[z.raw.end-2] == '/' { return SelfClosingTagToken } return StartTagToken } // readTag reads the next tag token and its attributes. If saveAttr, those // attributes are saved in z.attr, otherwise z.attr is set to an empty slice. // The opening "<a" or "</a" has already been consumed, where 'a' means anything // in [A-Za-z]. func (z *Tokenizer) readTag(saveAttr bool) { z.attr = z.attr[:0] z.nAttrReturned = 0 // Read the tag name and attribute key/value pairs. z.readTagName() if z.skipWhiteSpace(); z.err != nil { return } for { c := z.readByte() if z.err != nil || c == '>' { break } z.raw.end-- z.readTagAttrKey() z.readTagAttrVal() // Save pendingAttr if saveAttr and that attribute has a non-empty key. if saveAttr && z.pendingAttr[0].start != z.pendingAttr[0].end { z.attr = append(z.attr, z.pendingAttr) } if z.skipWhiteSpace(); z.err != nil { break } } } // readTagName sets z.data to the "div" in "<div k=v>". The reader (z.raw.end) // is positioned such that the first byte of the tag name (the "d" in "<div") // has already been consumed. func (z *Tokenizer) readTagName() { z.data.start = z.raw.end - 1 for { c := z.readByte() if z.err != nil { z.data.end = z.raw.end return } switch c { case ' ', '\n', '\r', '\t', '\f': z.data.end = z.raw.end - 1 return case '/', '>': z.raw.end-- z.data.end = z.raw.end return } } } // readTagAttrKey sets z.pendingAttr[0] to the "k" in "<div k=v>". // Precondition: z.err == nil. func (z *Tokenizer) readTagAttrKey() { z.pendingAttr[0].start = z.raw.end for { c := z.readByte() if z.err != nil { z.pendingAttr[0].end = z.raw.end return } switch c { case ' ', '\n', '\r', '\t', '\f', '/': z.pendingAttr[0].end = z.raw.end - 1 return case '=', '>': z.raw.end-- z.pendingAttr[0].end = z.raw.end return } } } // readTagAttrVal sets z.pendingAttr[1] to the "v" in "<div k=v>". func (z *Tokenizer) readTagAttrVal() { z.pendingAttr[1].start = z.raw.end z.pendingAttr[1].end = z.raw.end if z.skipWhiteSpace(); z.err != nil { return } c := z.readByte() if z.err != nil { return } if c != '=' { z.raw.end-- return } if z.skipWhiteSpace(); z.err != nil { return } quote := z.readByte() if z.err != nil { return } switch quote { case '>': z.raw.end-- return case '\'', '"': z.pendingAttr[1].start = z.raw.end for { c := z.readByte() if z.err != nil { z.pendingAttr[1].end = z.raw.end return } if c == quote { z.pendingAttr[1].end = z.raw.end - 1 return } } default: z.pendingAttr[1].start = z.raw.end - 1 for { c := z.readByte() if z.err != nil { z.pendingAttr[1].end = z.raw.end return } switch c { case ' ', '\n', '\r', '\t', '\f': z.pendingAttr[1].end = z.raw.end - 1 return case '>': z.raw.end-- z.pendingAttr[1].end = z.raw.end return } } } } // 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 } if z.rawTag != "" { if z.rawTag == "plaintext" { // Read everything up to EOF. for z.err == nil { z.readByte() } z.data.end = z.raw.end z.textIsRaw = true } else { z.readRawOrRCDATA() } if z.data.end > z.data.start { z.tt = TextToken z.convertNUL = true return z.tt } } z.textIsRaw = false z.convertNUL = false loop: for { c := z.readByte() if z.err != nil { break loop } if c != '<' { continue loop } // Check if the '<' we have just read is part of a tag, comment // or doctype. If not, it's part of the accumulated text token. c = z.readByte() if z.err != nil { break loop } var tokenType TokenType switch { case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': tokenType = StartTagToken case c == '/': tokenType = EndTagToken case c == '!' || c == '?': // We use CommentToken to mean any of "<!--actual comments-->", // "<!DOCTYPE declarations>" and "<?xml processing instructions?>". tokenType = CommentToken default: continue } // We have a non-text token, but we might have accumulated some text // before that. If so, we return the text first, and return the non- // text token on the subsequent call to Next. if x := z.raw.end - len("<a"); z.raw.start < x { z.raw.end = x z.data.end = x z.tt = TextToken return z.tt } switch tokenType { case StartTagToken: z.tt = z.readStartTag() return z.tt case EndTagToken: c = z.readByte() if z.err != nil { break loop } if c == '>' { // "</>" 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.tt = CommentToken return z.tt } if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' { z.readTag(false) if z.err != nil { z.tt = ErrorToken } else { z.tt = EndTagToken } return z.tt } z.raw.end-- z.readUntilCloseAngle() z.tt = CommentToken return z.tt case CommentToken: if c == '!' { z.tt = z.readMarkupDeclaration() return z.tt } z.raw.end-- z.readUntilCloseAngle() z.tt = CommentToken return z.tt } } if z.raw.start < z.raw.end { z.data.end = z.raw.end z.tt = TextToken return z.tt } z.tt = ErrorToken return z.tt } // Raw returns the unmodified text of the current token. Calling Next, Token, // Text, TagName or TagAttr may change the contents of the returned slice. func (z *Tokenizer) Raw() []byte { return z.buf[z.raw.start:z.raw.end] } // convertNewlines converts "\r" and "\r\n" in s to "\n". // The conversion happens in place, but the resulting slice may be shorter. func convertNewlines(s []byte) []byte { for i, c := range s { if c != '\r' { continue } src := i + 1 if src >= len(s) || s[src] != '\n' { s[i] = '\n' continue } dst := i for src < len(s) { if s[src] == '\r' { if src+1 < len(s) && s[src+1] == '\n' { src++ } s[dst] = '\n' } else { s[dst] = s[src] } src++ dst++ } return s[:dst] } return s } var ( nul = []byte("\x00") replacement = []byte("\ufffd") ) // Text returns the unescaped text of a text, comment or doctype token. The // contents of the returned slice may change on the next call to Next. func (z *Tokenizer) Text() []byte { switch z.tt { case TextToken, CommentToken, DoctypeToken: s := z.buf[z.data.start:z.data.end] z.data.start = z.raw.end z.data.end = z.raw.end s = convertNewlines(s) if (z.convertNUL || z.tt == CommentToken) && bytes.Contains(s, nul) { s = bytes.Replace(s, nul, replacement, -1) } if !z.textIsRaw { s = unescape(s, false) } return s } return nil } // TagName returns the lower-cased name of a tag token (the `img` out of // `<IMG SRC="foo">`) and whether the tag has attributes. // The contents of the returned slice may change on the next call to Next. func (z *Tokenizer) TagName() (name []byte, hasAttr bool) { if z.data.start < z.data.end { switch z.tt { case StartTagToken, EndTagToken, SelfClosingTagToken: s := z.buf[z.data.start:z.data.end] z.data.start = z.raw.end z.data.end = z.raw.end return lower(s), z.nAttrReturned < len(z.attr) } } return nil, false } // TagAttr returns the lower-cased key and unescaped value of the next unparsed // attribute for the current tag token and whether there are more attributes. // The contents of the returned slices may change on the next call to Next. func (z *Tokenizer) TagAttr() (key, val []byte, moreAttr bool) { if z.nAttrReturned < len(z.attr) { switch z.tt { case StartTagToken, SelfClosingTagToken: x := z.attr[z.nAttrReturned] z.nAttrReturned++ key = z.buf[x[0].start:x[0].end] val = z.buf[x[1].start:x[1].end] return lower(key), unescape(convertNewlines(val), true), z.nAttrReturned < len(z.attr) } } return nil, nil, false } // Token returns the next Token. The result's Data and Attr values remain valid // after subsequent Next calls. func (z *Tokenizer) Token() Token { t := Token{Type: z.tt} switch z.tt { case TextToken, CommentToken, DoctypeToken: t.Data = string(z.Text()) case StartTagToken, SelfClosingTagToken, EndTagToken: name, moreAttr := z.TagName() for moreAttr { var key, val []byte key, val, moreAttr = z.TagAttr() t.Attr = append(t.Attr, Attribute{"", atom.String(key), string(val)}) } if a := atom.Lookup(name); a != 0 { t.DataAtom, t.Data = a, a.String() } else { t.DataAtom, t.Data = 0, string(name) } } 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 { return NewTokenizerFragment(r, "") } // NewTokenizerFragment returns a new HTML Tokenizer for the given Reader, for // tokenizing an exisitng element's InnerHTML fragment. contextTag is that // element's tag, such as "div" or "iframe". // // For example, how the InnerHTML "a<b" is tokenized depends on whether it is // for a <p> tag or a <script> tag. // // The input is assumed to be UTF-8 encoded. func NewTokenizerFragment(r io.Reader, contextTag string) *Tokenizer { z := &Tokenizer{ r: r, buf: make([]byte, 0, 4096), } if contextTag != "" { switch s := strings.ToLower(contextTag); s { case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "title", "textarea", "xmp": z.rawTag = s } } return z } �����juju-core_1.18.1/src/code.google.com/p/go.net/html/node.go������������������������������������������0000644�0000153�0000161�00000011353�12321735652�023055� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 html import ( "code.google.com/p/go.net/html/atom" ) // A NodeType is the type of a Node. type NodeType uint32 const ( ErrorNode NodeType = iota TextNode DocumentNode ElementNode CommentNode DoctypeNode scopeMarkerNode ) // Section 12.2.3.3 says "scope markers are inserted when entering applet // elements, buttons, object elements, marquees, table cells, and table // captions, and are used to prevent formatting from 'leaking'". var scopeMarker = Node{Type: scopeMarkerNode} // A Node consists of a NodeType and some Data (tag name for element nodes, // content for text) and are part of a tree of Nodes. Element nodes may also // have a Namespace and contain a slice of Attributes. Data is unescaped, so // that it looks like "a<b" rather than "a&lt;b". For element nodes, DataAtom // is the atom for Data, or zero if Data is not a known tag name. // // An empty Namespace implies a "http://www.w3.org/1999/xhtml" namespace. // Similarly, "math" is short for "http://www.w3.org/1998/Math/MathML", and // "svg" is short for "http://www.w3.org/2000/svg". type Node struct { Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node Type NodeType DataAtom atom.Atom Data string Namespace string Attr []Attribute } // InsertBefore inserts newChild as a child of n, immediately before oldChild // in the sequence of n's children. oldChild may be nil, in which case newChild // is appended to the end of n's children. // // It will panic if newChild already has a parent or siblings. func (n *Node) InsertBefore(newChild, oldChild *Node) { if newChild.Parent != nil || newChild.PrevSibling != nil || newChild.NextSibling != nil { panic("html: InsertBefore called for an attached child Node") } var prev, next *Node if oldChild != nil { prev, next = oldChild.PrevSibling, oldChild } else { prev = n.LastChild } if prev != nil { prev.NextSibling = newChild } else { n.FirstChild = newChild } if next != nil { next.PrevSibling = newChild } else { n.LastChild = newChild } newChild.Parent = n newChild.PrevSibling = prev newChild.NextSibling = next } // AppendChild adds a node c as a child of n. // // It will panic if c already has a parent or siblings. func (n *Node) AppendChild(c *Node) { if c.Parent != nil || c.PrevSibling != nil || c.NextSibling != nil { panic("html: AppendChild called for an attached child Node") } last := n.LastChild if last != nil { last.NextSibling = c } else { n.FirstChild = c } n.LastChild = c c.Parent = n c.PrevSibling = last } // RemoveChild removes a node c that is a child of n. Afterwards, c will have // no parent and no siblings. // // It will panic if c's parent is not n. func (n *Node) RemoveChild(c *Node) { if c.Parent != n { panic("html: RemoveChild called for a non-child Node") } if n.FirstChild == c { n.FirstChild = c.NextSibling } if c.NextSibling != nil { c.NextSibling.PrevSibling = c.PrevSibling } if n.LastChild == c { n.LastChild = c.PrevSibling } if c.PrevSibling != nil { c.PrevSibling.NextSibling = c.NextSibling } c.Parent = nil c.PrevSibling = nil c.NextSibling = nil } // reparentChildren reparents all of src's child nodes to dst. func reparentChildren(dst, src *Node) { for { child := src.FirstChild if child == nil { break } src.RemoveChild(child) dst.AppendChild(child) } } // clone returns a new node with the same type, data and attributes. // The clone has no parent, no siblings and no children. func (n *Node) clone() *Node { m := &Node{ Type: n.Type, DataAtom: n.DataAtom, Data: n.Data, Attr: make([]Attribute, len(n.Attr)), } copy(m.Attr, n.Attr) return m } // nodeStack is a stack of nodes. type nodeStack []*Node // pop pops the stack. It will panic if s is empty. func (s *nodeStack) pop() *Node { i := len(*s) n := (*s)[i-1] *s = (*s)[:i-1] return n } // top returns the most recently pushed node, or nil if s is empty. func (s *nodeStack) top() *Node { if i := len(*s); i > 0 { return (*s)[i-1] } return nil } // index returns the index of the top-most occurrence of n in the stack, or -1 // if n is not present. func (s *nodeStack) index(n *Node) int { for i := len(*s) - 1; i >= 0; i-- { if (*s)[i] == n { return i } } return -1 } // insert inserts a node at the given index. func (s *nodeStack) insert(i int, n *Node) { (*s) = append(*s, nil) copy((*s)[i+1:], (*s)[i:]) (*s)[i] = n } // remove removes a node from the stack. It is a no-op if n is not present. func (s *nodeStack) remove(n *Node) { i := s.index(n) if i == -1 { return } copy((*s)[i:], (*s)[i+1:]) j := len(*s) - 1 (*s)[j] = nil *s = (*s)[:j] } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/escape.go����������������������������������������0000644�0000153�0000161�00000013733�12321735652�023374� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 html import ( "bytes" "strings" "unicode/utf8" ) // These replacements permit compatibility with old numeric entities that // assumed Windows-1252 encoding. // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference var replacementTable = [...]rune{ '\u20AC', // First entry is what 0x80 should be replaced with. '\u0081', '\u201A', '\u0192', '\u201E', '\u2026', '\u2020', '\u2021', '\u02C6', '\u2030', '\u0160', '\u2039', '\u0152', '\u008D', '\u017D', '\u008F', '\u0090', '\u2018', '\u2019', '\u201C', '\u201D', '\u2022', '\u2013', '\u2014', '\u02DC', '\u2122', '\u0161', '\u203A', '\u0153', '\u009D', '\u017E', '\u0178', // Last entry is 0x9F. // 0x00->'\uFFFD' is handled programmatically. // 0x0D->'\u000D' is a no-op. } // unescapeEntity reads an entity like "&lt;" from b[src:] and writes the // corresponding "<" to b[dst:], returning the incremented dst and src cursors. // Precondition: b[src] == '&' && dst <= src. // attribute should be true if parsing an attribute value. func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. i, s := 1, b[src:] if len(s) <= 1 { b[dst] = b[src] return dst + 1, src + 1 } if s[i] == '#' { if len(s) <= 3 { // We need to have at least "&#.". b[dst] = b[src] return dst + 1, src + 1 } i++ c := s[i] hex := false if c == 'x' || c == 'X' { hex = true i++ } x := '\x00' for i < len(s) { c = s[i] i++ if hex { if '0' <= c && c <= '9' { x = 16*x + rune(c) - '0' continue } else if 'a' <= c && c <= 'f' { x = 16*x + rune(c) - 'a' + 10 continue } else if 'A' <= c && c <= 'F' { x = 16*x + rune(c) - 'A' + 10 continue } } else if '0' <= c && c <= '9' { x = 10*x + rune(c) - '0' continue } if c != ';' { i-- } break } if i <= 3 { // No characters matched. b[dst] = b[src] return dst + 1, src + 1 } if 0x80 <= x && x <= 0x9F { // Replace characters from Windows-1252 with UTF-8 equivalents. x = replacementTable[x-0x80] } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF { // Replace invalid characters with the replacement character. x = '\uFFFD' } return dst + utf8.EncodeRune(b[dst:], x), src + i } // Consume the maximum number of characters possible, with the // consumed characters matching one of the named references. for i < len(s) { c := s[i] i++ // Lower-cased characters are more common in entities, so we check for them first. if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { continue } if c != ';' { i-- } break } entityName := string(s[1:i]) if entityName == "" { // No-op. } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. } else if x := entity[entityName]; x != 0 { return dst + utf8.EncodeRune(b[dst:], x), src + i } else if x := entity2[entityName]; x[0] != 0 { dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i } else if !attribute { maxLen := len(entityName) - 1 if maxLen > longestEntityWithoutSemicolon { maxLen = longestEntityWithoutSemicolon } for j := maxLen; j > 1; j-- { if x := entity[entityName[:j]]; x != 0 { return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 } } } dst1, src1 = dst+i, src+i copy(b[dst:dst1], b[src:src1]) return dst1, src1 } // unescape unescapes b's entities in-place, so that "a&lt;b" becomes "a<b". // attribute should be true if parsing an attribute value. func unescape(b []byte, attribute bool) []byte { for i, c := range b { if c == '&' { dst, src := unescapeEntity(b, i, i, attribute) for src < len(b) { c := b[src] if c == '&' { dst, src = unescapeEntity(b, dst, src, attribute) } else { b[dst] = c dst, src = dst+1, src+1 } } return b[0:dst] } } return b } // lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc". func lower(b []byte) []byte { for i, c := range b { if 'A' <= c && c <= 'Z' { b[i] = c + 'a' - 'A' } } return b } const escapedChars = "&'<>\"\r" func escape(w writer, s string) error { i := strings.IndexAny(s, escapedChars) for i != -1 { if _, err := w.WriteString(s[:i]); err != nil { return err } var esc string switch s[i] { case '&': esc = "&amp;" case '\'': // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5. esc = "&#39;" case '<': esc = "&lt;" case '>': esc = "&gt;" case '"': // "&#34;" is shorter than "&quot;". esc = "&#34;" case '\r': esc = "&#13;" default: panic("unrecognized escape character") } s = s[i+1:] if _, err := w.WriteString(esc); err != nil { return err } i = strings.IndexAny(s, escapedChars) } _, err := w.WriteString(s) return err } // EscapeString escapes special characters like "<" to become "&lt;". It // escapes only five such characters: <, >, &, ' and ". // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func EscapeString(s string) string { if strings.IndexAny(s, escapedChars) == -1 { return s } var buf bytes.Buffer escape(&buf, s) return buf.String() } // UnescapeString unescapes entities like "&lt;" to become "<". It unescapes a // larger range of entities than EscapeString escapes. For example, "&aacute;" // unescapes to "á", as does "&#225;" and "&xE1;". // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func UnescapeString(s string) string { for _, c := range s { if c == '&' { return string(unescape([]byte(s), false)) } } return s } �������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/node_test.go�������������������������������������0000644�0000153�0000161�00000007305�12321735652�024116� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 html import ( "fmt" ) // checkTreeConsistency checks that a node and its descendants are all // consistent in their parent/child/sibling relationships. func checkTreeConsistency(n *Node) error { return checkTreeConsistency1(n, 0) } func checkTreeConsistency1(n *Node, depth int) error { if depth == 1e4 { return fmt.Errorf("html: tree looks like it contains a cycle") } if err := checkNodeConsistency(n); err != nil { return err } for c := n.FirstChild; c != nil; c = c.NextSibling { if err := checkTreeConsistency1(c, depth+1); err != nil { return err } } return nil } // checkNodeConsistency checks that a node's parent/child/sibling relationships // are consistent. func checkNodeConsistency(n *Node) error { if n == nil { return nil } nParent := 0 for p := n.Parent; p != nil; p = p.Parent { nParent++ if nParent == 1e4 { return fmt.Errorf("html: parent list looks like an infinite loop") } } nForward := 0 for c := n.FirstChild; c != nil; c = c.NextSibling { nForward++ if nForward == 1e6 { return fmt.Errorf("html: forward list of children looks like an infinite loop") } if c.Parent != n { return fmt.Errorf("html: inconsistent child/parent relationship") } } nBackward := 0 for c := n.LastChild; c != nil; c = c.PrevSibling { nBackward++ if nBackward == 1e6 { return fmt.Errorf("html: backward list of children looks like an infinite loop") } if c.Parent != n { return fmt.Errorf("html: inconsistent child/parent relationship") } } if n.Parent != nil { if n.Parent == n { return fmt.Errorf("html: inconsistent parent relationship") } if n.Parent == n.FirstChild { return fmt.Errorf("html: inconsistent parent/first relationship") } if n.Parent == n.LastChild { return fmt.Errorf("html: inconsistent parent/last relationship") } if n.Parent == n.PrevSibling { return fmt.Errorf("html: inconsistent parent/prev relationship") } if n.Parent == n.NextSibling { return fmt.Errorf("html: inconsistent parent/next relationship") } parentHasNAsAChild := false for c := n.Parent.FirstChild; c != nil; c = c.NextSibling { if c == n { parentHasNAsAChild = true break } } if !parentHasNAsAChild { return fmt.Errorf("html: inconsistent parent/child relationship") } } if n.PrevSibling != nil && n.PrevSibling.NextSibling != n { return fmt.Errorf("html: inconsistent prev/next relationship") } if n.NextSibling != nil && n.NextSibling.PrevSibling != n { return fmt.Errorf("html: inconsistent next/prev relationship") } if (n.FirstChild == nil) != (n.LastChild == nil) { return fmt.Errorf("html: inconsistent first/last relationship") } if n.FirstChild != nil && n.FirstChild == n.LastChild { // We have a sole child. if n.FirstChild.PrevSibling != nil || n.FirstChild.NextSibling != nil { return fmt.Errorf("html: inconsistent sole child's sibling relationship") } } seen := map[*Node]bool{} var last *Node for c := n.FirstChild; c != nil; c = c.NextSibling { if seen[c] { return fmt.Errorf("html: inconsistent repeated child") } seen[c] = true last = c } if last != n.LastChild { return fmt.Errorf("html: inconsistent last relationship") } var first *Node for c := n.LastChild; c != nil; c = c.PrevSibling { if !seen[c] { return fmt.Errorf("html: inconsistent missing child") } delete(seen, c) first = c } if first != n.FirstChild { return fmt.Errorf("html: inconsistent first relationship") } if len(seen) != 0 { return fmt.Errorf("html: inconsistent forwards/backwards child list") } return nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/foreign.go���������������������������������������0000644�0000153�0000161�00000015401�12321735652�023557� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 html import ( "strings" ) func adjustAttributeNames(aa []Attribute, nameMap map[string]string) { for i := range aa { if newName, ok := nameMap[aa[i].Key]; ok { aa[i].Key = newName } } } func adjustForeignAttributes(aa []Attribute) { for i, a := range aa { if a.Key == "" || a.Key[0] != 'x' { continue } switch a.Key { case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": j := strings.Index(a.Key, ":") aa[i].Namespace = a.Key[:j] aa[i].Key = a.Key[j+1:] } } } func htmlIntegrationPoint(n *Node) bool { if n.Type != ElementNode { return false } switch n.Namespace { case "math": if n.Data == "annotation-xml" { for _, a := range n.Attr { if a.Key == "encoding" { val := strings.ToLower(a.Val) if val == "text/html" || val == "application/xhtml+xml" { return true } } } } case "svg": switch n.Data { case "desc", "foreignObject", "title": return true } } return false } func mathMLTextIntegrationPoint(n *Node) bool { if n.Namespace != "math" { return false } switch n.Data { case "mi", "mo", "mn", "ms", "mtext": return true } return false } // Section 12.2.5.5. var breakout = map[string]bool{ "b": true, "big": true, "blockquote": true, "body": true, "br": true, "center": true, "code": true, "dd": true, "div": true, "dl": true, "dt": true, "em": true, "embed": true, "h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true, "head": true, "hr": true, "i": true, "img": true, "li": true, "listing": true, "menu": true, "meta": true, "nobr": true, "ol": true, "p": true, "pre": true, "ruby": true, "s": true, "small": true, "span": true, "strong": true, "strike": true, "sub": true, "sup": true, "table": true, "tt": true, "u": true, "ul": true, "var": true, } // Section 12.2.5.5. var svgTagNameAdjustments = map[string]string{ "altglyph": "altGlyph", "altglyphdef": "altGlyphDef", "altglyphitem": "altGlyphItem", "animatecolor": "animateColor", "animatemotion": "animateMotion", "animatetransform": "animateTransform", "clippath": "clipPath", "feblend": "feBlend", "fecolormatrix": "feColorMatrix", "fecomponenttransfer": "feComponentTransfer", "fecomposite": "feComposite", "feconvolvematrix": "feConvolveMatrix", "fediffuselighting": "feDiffuseLighting", "fedisplacementmap": "feDisplacementMap", "fedistantlight": "feDistantLight", "feflood": "feFlood", "fefunca": "feFuncA", "fefuncb": "feFuncB", "fefuncg": "feFuncG", "fefuncr": "feFuncR", "fegaussianblur": "feGaussianBlur", "feimage": "feImage", "femerge": "feMerge", "femergenode": "feMergeNode", "femorphology": "feMorphology", "feoffset": "feOffset", "fepointlight": "fePointLight", "fespecularlighting": "feSpecularLighting", "fespotlight": "feSpotLight", "fetile": "feTile", "feturbulence": "feTurbulence", "foreignobject": "foreignObject", "glyphref": "glyphRef", "lineargradient": "linearGradient", "radialgradient": "radialGradient", "textpath": "textPath", } // Section 12.2.5.1 var mathMLAttributeAdjustments = map[string]string{ "definitionurl": "definitionURL", } var svgAttributeAdjustments = map[string]string{ "attributename": "attributeName", "attributetype": "attributeType", "basefrequency": "baseFrequency", "baseprofile": "baseProfile", "calcmode": "calcMode", "clippathunits": "clipPathUnits", "contentscripttype": "contentScriptType", "contentstyletype": "contentStyleType", "diffuseconstant": "diffuseConstant", "edgemode": "edgeMode", "externalresourcesrequired": "externalResourcesRequired", "filterres": "filterRes", "filterunits": "filterUnits", "glyphref": "glyphRef", "gradienttransform": "gradientTransform", "gradientunits": "gradientUnits", "kernelmatrix": "kernelMatrix", "kernelunitlength": "kernelUnitLength", "keypoints": "keyPoints", "keysplines": "keySplines", "keytimes": "keyTimes", "lengthadjust": "lengthAdjust", "limitingconeangle": "limitingConeAngle", "markerheight": "markerHeight", "markerunits": "markerUnits", "markerwidth": "markerWidth", "maskcontentunits": "maskContentUnits", "maskunits": "maskUnits", "numoctaves": "numOctaves", "pathlength": "pathLength", "patterncontentunits": "patternContentUnits", "patterntransform": "patternTransform", "patternunits": "patternUnits", "pointsatx": "pointsAtX", "pointsaty": "pointsAtY", "pointsatz": "pointsAtZ", "preservealpha": "preserveAlpha", "preserveaspectratio": "preserveAspectRatio", "primitiveunits": "primitiveUnits", "refx": "refX", "refy": "refY", "repeatcount": "repeatCount", "repeatdur": "repeatDur", "requiredextensions": "requiredExtensions", "requiredfeatures": "requiredFeatures", "specularconstant": "specularConstant", "specularexponent": "specularExponent", "spreadmethod": "spreadMethod", "startoffset": "startOffset", "stddeviation": "stdDeviation", "stitchtiles": "stitchTiles", "surfacescale": "surfaceScale", "systemlanguage": "systemLanguage", "tablevalues": "tableValues", "targetx": "targetX", "targety": "targetY", "textlength": "textLength", "viewbox": "viewBox", "viewtarget": "viewTarget", "xchannelselector": "xChannelSelector", "ychannelselector": "yChannelSelector", "zoomandpan": "zoomAndPan", } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/render.go����������������������������������������0000644�0000153�0000161�00000015700�12321735652�023407� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 html import ( "bufio" "errors" "fmt" "io" "strings" ) type writer interface { io.Writer WriteByte(c byte) error // in Go 1.1, use io.ByteWriter WriteString(string) (int, error) } // Render renders the parse tree n to the given writer. // // Rendering is done on a 'best effort' basis: calling Parse on the output of // Render will always result in something similar to the original tree, but it // is not necessarily an exact clone unless the original tree was 'well-formed'. // 'Well-formed' is not easily specified; the HTML5 specification is // complicated. // // Calling Parse on arbitrary input typically results in a 'well-formed' parse // tree. However, it is possible for Parse to yield a 'badly-formed' parse tree. // For example, in a 'well-formed' parse tree, no <a> element is a child of // another <a> element: parsing "<a><a>" results in two sibling elements. // Similarly, in a 'well-formed' parse tree, no <a> element is a child of a // <table> element: parsing "<p><table><a>" results in a <p> with two sibling // children; the <a> is reparented to the <table>'s parent. However, calling // Parse on "<a><table><a>" does not return an error, but the result has an <a> // element with an <a> child, and is therefore not 'well-formed'. // // Programmatically constructed trees are typically also 'well-formed', but it // is possible to construct a tree that looks innocuous but, when rendered and // re-parsed, results in a different tree. A simple example is that a solitary // text node would become a tree containing <html>, <head> and <body> elements. // Another example is that the programmatic equivalent of "a<head>b</head>c" // becomes "<html><head><head/><body>abc</body></html>". func Render(w io.Writer, n *Node) error { if x, ok := w.(writer); ok { return render(x, n) } buf := bufio.NewWriter(w) if err := render(buf, n); err != nil { return err } return buf.Flush() } // plaintextAbort is returned from render1 when a <plaintext> element // has been rendered. No more end tags should be rendered after that. var plaintextAbort = errors.New("html: internal error (plaintext abort)") func render(w writer, n *Node) error { err := render1(w, n) if err == plaintextAbort { err = nil } return err } func render1(w writer, n *Node) error { // Render non-element nodes; these are the easy cases. switch n.Type { case ErrorNode: return errors.New("html: cannot render an ErrorNode node") case TextNode: return escape(w, n.Data) case DocumentNode: for c := n.FirstChild; c != nil; c = c.NextSibling { if err := render1(w, c); err != nil { return err } } return nil case ElementNode: // No-op. case CommentNode: if _, err := w.WriteString("<!--"); err != nil { return err } if _, err := w.WriteString(n.Data); err != nil { return err } if _, err := w.WriteString("-->"); err != nil { return err } return nil case DoctypeNode: if _, err := w.WriteString("<!DOCTYPE "); err != nil { return err } if _, err := w.WriteString(n.Data); err != nil { return err } if n.Attr != nil { var p, s string for _, a := range n.Attr { switch a.Key { case "public": p = a.Val case "system": s = a.Val } } if p != "" { if _, err := w.WriteString(" PUBLIC "); err != nil { return err } if err := writeQuoted(w, p); err != nil { return err } if s != "" { if err := w.WriteByte(' '); err != nil { return err } if err := writeQuoted(w, s); err != nil { return err } } } else if s != "" { if _, err := w.WriteString(" SYSTEM "); err != nil { return err } if err := writeQuoted(w, s); err != nil { return err } } } return w.WriteByte('>') default: return errors.New("html: unknown node type") } // Render the <xxx> opening tag. if err := w.WriteByte('<'); err != nil { return err } if _, err := w.WriteString(n.Data); err != nil { return err } for _, a := range n.Attr { if err := w.WriteByte(' '); err != nil { return err } if a.Namespace != "" { if _, err := w.WriteString(a.Namespace); err != nil { return err } if err := w.WriteByte(':'); err != nil { return err } } if _, err := w.WriteString(a.Key); err != nil { return err } if _, err := w.WriteString(`="`); err != nil { return err } if err := escape(w, a.Val); err != nil { return err } if err := w.WriteByte('"'); err != nil { return err } } if voidElements[n.Data] { if n.FirstChild != nil { return fmt.Errorf("html: void element <%s> has child nodes", n.Data) } _, err := w.WriteString("/>") return err } if err := w.WriteByte('>'); err != nil { return err } // Add initial newline where there is danger of a newline beging ignored. if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") { switch n.Data { case "pre", "listing", "textarea": if err := w.WriteByte('\n'); err != nil { return err } } } // Render any child nodes. switch n.Data { case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp": for c := n.FirstChild; c != nil; c = c.NextSibling { if c.Type == TextNode { if _, err := w.WriteString(c.Data); err != nil { return err } } else { if err := render1(w, c); err != nil { return err } } } if n.Data == "plaintext" { // Don't render anything else. <plaintext> must be the // last element in the file, with no closing tag. return plaintextAbort } default: for c := n.FirstChild; c != nil; c = c.NextSibling { if err := render1(w, c); err != nil { return err } } } // Render the </xxx> closing tag. if _, err := w.WriteString("</"); err != nil { return err } if _, err := w.WriteString(n.Data); err != nil { return err } return w.WriteByte('>') } // writeQuoted writes s to w surrounded by quotes. Normally it will use double // quotes, but if s contains a double quote, it will use single quotes. // It is used for writing the identifiers in a doctype declaration. // In valid HTML, they can't contain both types of quotes. func writeQuoted(w writer, s string) error { var q byte = '"' if strings.Contains(s, `"`) { q = '\'' } if err := w.WriteByte(q); err != nil { return err } if _, err := w.WriteString(s); err != nil { return err } if err := w.WriteByte(q); err != nil { return err } return nil } // Section 12.1.2, "Elements", gives this list of void elements. Void elements // are those that can't have any contents. var voidElements = map[string]bool{ "area": true, "base": true, "br": true, "col": true, "command": true, "embed": true, "hr": true, "img": true, "input": true, "keygen": true, "link": true, "meta": true, "param": true, "source": true, "track": true, "wbr": true, } ����������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/entity.go����������������������������������������0000644�0000153�0000161�00000303345�12321735652�023451� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 html // All entities that do not end with ';' are 6 or fewer bytes long. const longestEntityWithoutSemicolon = 6 // entity is a map from HTML entity names to their values. The semicolon matters: // http://www.whatwg.org/specs/web-apps/current-work/multipage/named-character-references.html // lists both "amp" and "amp;" as two separate entries. // // Note that the HTML5 list is larger than the HTML4 list at // http://www.w3.org/TR/html4/sgml/entities.html var entity = map[string]rune{ "AElig;": '\U000000C6', "AMP;": '\U00000026', "Aacute;": '\U000000C1', "Abreve;": '\U00000102', "Acirc;": '\U000000C2', "Acy;": '\U00000410', "Afr;": '\U0001D504', "Agrave;": '\U000000C0', "Alpha;": '\U00000391', "Amacr;": '\U00000100', "And;": '\U00002A53', "Aogon;": '\U00000104', "Aopf;": '\U0001D538', "ApplyFunction;": '\U00002061', "Aring;": '\U000000C5', "Ascr;": '\U0001D49C', "Assign;": '\U00002254', "Atilde;": '\U000000C3', "Auml;": '\U000000C4', "Backslash;": '\U00002216', "Barv;": '\U00002AE7', "Barwed;": '\U00002306', "Bcy;": '\U00000411', "Because;": '\U00002235', "Bernoullis;": '\U0000212C', "Beta;": '\U00000392', "Bfr;": '\U0001D505', "Bopf;": '\U0001D539', "Breve;": '\U000002D8', "Bscr;": '\U0000212C', "Bumpeq;": '\U0000224E', "CHcy;": '\U00000427', "COPY;": '\U000000A9', "Cacute;": '\U00000106', "Cap;": '\U000022D2', "CapitalDifferentialD;": '\U00002145', "Cayleys;": '\U0000212D', "Ccaron;": '\U0000010C', "Ccedil;": '\U000000C7', "Ccirc;": '\U00000108', "Cconint;": '\U00002230', "Cdot;": '\U0000010A', "Cedilla;": '\U000000B8', "CenterDot;": '\U000000B7', "Cfr;": '\U0000212D', "Chi;": '\U000003A7', "CircleDot;": '\U00002299', "CircleMinus;": '\U00002296', "CirclePlus;": '\U00002295', "CircleTimes;": '\U00002297', "ClockwiseContourIntegral;": '\U00002232', "CloseCurlyDoubleQuote;": '\U0000201D', "CloseCurlyQuote;": '\U00002019', "Colon;": '\U00002237', "Colone;": '\U00002A74', "Congruent;": '\U00002261', "Conint;": '\U0000222F', "ContourIntegral;": '\U0000222E', "Copf;": '\U00002102', "Coproduct;": '\U00002210', "CounterClockwiseContourIntegral;": '\U00002233', "Cross;": '\U00002A2F', "Cscr;": '\U0001D49E', "Cup;": '\U000022D3', "CupCap;": '\U0000224D', "DD;": '\U00002145', "DDotrahd;": '\U00002911', "DJcy;": '\U00000402', "DScy;": '\U00000405', "DZcy;": '\U0000040F', "Dagger;": '\U00002021', "Darr;": '\U000021A1', "Dashv;": '\U00002AE4', "Dcaron;": '\U0000010E', "Dcy;": '\U00000414', "Del;": '\U00002207', "Delta;": '\U00000394', "Dfr;": '\U0001D507', "DiacriticalAcute;": '\U000000B4', "DiacriticalDot;": '\U000002D9', "DiacriticalDoubleAcute;": '\U000002DD', "DiacriticalGrave;": '\U00000060', "DiacriticalTilde;": '\U000002DC', "Diamond;": '\U000022C4', "DifferentialD;": '\U00002146', "Dopf;": '\U0001D53B', "Dot;": '\U000000A8', "DotDot;": '\U000020DC', "DotEqual;": '\U00002250', "DoubleContourIntegral;": '\U0000222F', "DoubleDot;": '\U000000A8', "DoubleDownArrow;": '\U000021D3', "DoubleLeftArrow;": '\U000021D0', "DoubleLeftRightArrow;": '\U000021D4', "DoubleLeftTee;": '\U00002AE4', "DoubleLongLeftArrow;": '\U000027F8', "DoubleLongLeftRightArrow;": '\U000027FA', "DoubleLongRightArrow;": '\U000027F9', "DoubleRightArrow;": '\U000021D2', "DoubleRightTee;": '\U000022A8', "DoubleUpArrow;": '\U000021D1', "DoubleUpDownArrow;": '\U000021D5', "DoubleVerticalBar;": '\U00002225', "DownArrow;": '\U00002193', "DownArrowBar;": '\U00002913', "DownArrowUpArrow;": '\U000021F5', "DownBreve;": '\U00000311', "DownLeftRightVector;": '\U00002950', "DownLeftTeeVector;": '\U0000295E', "DownLeftVector;": '\U000021BD', "DownLeftVectorBar;": '\U00002956', "DownRightTeeVector;": '\U0000295F', "DownRightVector;": '\U000021C1', "DownRightVectorBar;": '\U00002957', "DownTee;": '\U000022A4', "DownTeeArrow;": '\U000021A7', "Downarrow;": '\U000021D3', "Dscr;": '\U0001D49F', "Dstrok;": '\U00000110', "ENG;": '\U0000014A', "ETH;": '\U000000D0', "Eacute;": '\U000000C9', "Ecaron;": '\U0000011A', "Ecirc;": '\U000000CA', "Ecy;": '\U0000042D', "Edot;": '\U00000116', "Efr;": '\U0001D508', "Egrave;": '\U000000C8', "Element;": '\U00002208', "Emacr;": '\U00000112', "EmptySmallSquare;": '\U000025FB', "EmptyVerySmallSquare;": '\U000025AB', "Eogon;": '\U00000118', "Eopf;": '\U0001D53C', "Epsilon;": '\U00000395', "Equal;": '\U00002A75', "EqualTilde;": '\U00002242', "Equilibrium;": '\U000021CC', "Escr;": '\U00002130', "Esim;": '\U00002A73', "Eta;": '\U00000397', "Euml;": '\U000000CB', "Exists;": '\U00002203', "ExponentialE;": '\U00002147', "Fcy;": '\U00000424', "Ffr;": '\U0001D509', "FilledSmallSquare;": '\U000025FC', "FilledVerySmallSquare;": '\U000025AA', "Fopf;": '\U0001D53D', "ForAll;": '\U00002200', "Fouriertrf;": '\U00002131', "Fscr;": '\U00002131', "GJcy;": '\U00000403', "GT;": '\U0000003E', "Gamma;": '\U00000393', "Gammad;": '\U000003DC', "Gbreve;": '\U0000011E', "Gcedil;": '\U00000122', "Gcirc;": '\U0000011C', "Gcy;": '\U00000413', "Gdot;": '\U00000120', "Gfr;": '\U0001D50A', "Gg;": '\U000022D9', "Gopf;": '\U0001D53E', "GreaterEqual;": '\U00002265', "GreaterEqualLess;": '\U000022DB', "GreaterFullEqual;": '\U00002267', "GreaterGreater;": '\U00002AA2', "GreaterLess;": '\U00002277', "GreaterSlantEqual;": '\U00002A7E', "GreaterTilde;": '\U00002273', "Gscr;": '\U0001D4A2', "Gt;": '\U0000226B', "HARDcy;": '\U0000042A', "Hacek;": '\U000002C7', "Hat;": '\U0000005E', "Hcirc;": '\U00000124', "Hfr;": '\U0000210C', "HilbertSpace;": '\U0000210B', "Hopf;": '\U0000210D', "HorizontalLine;": '\U00002500', "Hscr;": '\U0000210B', "Hstrok;": '\U00000126', "HumpDownHump;": '\U0000224E', "HumpEqual;": '\U0000224F', "IEcy;": '\U00000415', "IJlig;": '\U00000132', "IOcy;": '\U00000401', "Iacute;": '\U000000CD', "Icirc;": '\U000000CE', "Icy;": '\U00000418', "Idot;": '\U00000130', "Ifr;": '\U00002111', "Igrave;": '\U000000CC', "Im;": '\U00002111', "Imacr;": '\U0000012A', "ImaginaryI;": '\U00002148', "Implies;": '\U000021D2', "Int;": '\U0000222C', "Integral;": '\U0000222B', "Intersection;": '\U000022C2', "InvisibleComma;": '\U00002063', "InvisibleTimes;": '\U00002062', "Iogon;": '\U0000012E', "Iopf;": '\U0001D540', "Iota;": '\U00000399', "Iscr;": '\U00002110', "Itilde;": '\U00000128', "Iukcy;": '\U00000406', "Iuml;": '\U000000CF', "Jcirc;": '\U00000134', "Jcy;": '\U00000419', "Jfr;": '\U0001D50D', "Jopf;": '\U0001D541', "Jscr;": '\U0001D4A5', "Jsercy;": '\U00000408', "Jukcy;": '\U00000404', "KHcy;": '\U00000425', "KJcy;": '\U0000040C', "Kappa;": '\U0000039A', "Kcedil;": '\U00000136', "Kcy;": '\U0000041A', "Kfr;": '\U0001D50E', "Kopf;": '\U0001D542', "Kscr;": '\U0001D4A6', "LJcy;": '\U00000409', "LT;": '\U0000003C', "Lacute;": '\U00000139', "Lambda;": '\U0000039B', "Lang;": '\U000027EA', "Laplacetrf;": '\U00002112', "Larr;": '\U0000219E', "Lcaron;": '\U0000013D', "Lcedil;": '\U0000013B', "Lcy;": '\U0000041B', "LeftAngleBracket;": '\U000027E8', "LeftArrow;": '\U00002190', "LeftArrowBar;": '\U000021E4', "LeftArrowRightArrow;": '\U000021C6', "LeftCeiling;": '\U00002308', "LeftDoubleBracket;": '\U000027E6', "LeftDownTeeVector;": '\U00002961', "LeftDownVector;": '\U000021C3', "LeftDownVectorBar;": '\U00002959', "LeftFloor;": '\U0000230A', "LeftRightArrow;": '\U00002194', "LeftRightVector;": '\U0000294E', "LeftTee;": '\U000022A3', "LeftTeeArrow;": '\U000021A4', "LeftTeeVector;": '\U0000295A', "LeftTriangle;": '\U000022B2', "LeftTriangleBar;": '\U000029CF', "LeftTriangleEqual;": '\U000022B4', "LeftUpDownVector;": '\U00002951', "LeftUpTeeVector;": '\U00002960', "LeftUpVector;": '\U000021BF', "LeftUpVectorBar;": '\U00002958', "LeftVector;": '\U000021BC', "LeftVectorBar;": '\U00002952', "Leftarrow;": '\U000021D0', "Leftrightarrow;": '\U000021D4', "LessEqualGreater;": '\U000022DA', "LessFullEqual;": '\U00002266', "LessGreater;": '\U00002276', "LessLess;": '\U00002AA1', "LessSlantEqual;": '\U00002A7D', "LessTilde;": '\U00002272', "Lfr;": '\U0001D50F', "Ll;": '\U000022D8', "Lleftarrow;": '\U000021DA', "Lmidot;": '\U0000013F', "LongLeftArrow;": '\U000027F5', "LongLeftRightArrow;": '\U000027F7', "LongRightArrow;": '\U000027F6', "Longleftarrow;": '\U000027F8', "Longleftrightarrow;": '\U000027FA', "Longrightarrow;": '\U000027F9', "Lopf;": '\U0001D543', "LowerLeftArrow;": '\U00002199', "LowerRightArrow;": '\U00002198', "Lscr;": '\U00002112', "Lsh;": '\U000021B0', "Lstrok;": '\U00000141', "Lt;": '\U0000226A', "Map;": '\U00002905', "Mcy;": '\U0000041C', "MediumSpace;": '\U0000205F', "Mellintrf;": '\U00002133', "Mfr;": '\U0001D510', "MinusPlus;": '\U00002213', "Mopf;": '\U0001D544', "Mscr;": '\U00002133', "Mu;": '\U0000039C', "NJcy;": '\U0000040A', "Nacute;": '\U00000143', "Ncaron;": '\U00000147', "Ncedil;": '\U00000145', "Ncy;": '\U0000041D', "NegativeMediumSpace;": '\U0000200B', "NegativeThickSpace;": '\U0000200B', "NegativeThinSpace;": '\U0000200B', "NegativeVeryThinSpace;": '\U0000200B', "NestedGreaterGreater;": '\U0000226B', "NestedLessLess;": '\U0000226A', "NewLine;": '\U0000000A', "Nfr;": '\U0001D511', "NoBreak;": '\U00002060', "NonBreakingSpace;": '\U000000A0', "Nopf;": '\U00002115', "Not;": '\U00002AEC', "NotCongruent;": '\U00002262', "NotCupCap;": '\U0000226D', "NotDoubleVerticalBar;": '\U00002226', "NotElement;": '\U00002209', "NotEqual;": '\U00002260', "NotExists;": '\U00002204', "NotGreater;": '\U0000226F', "NotGreaterEqual;": '\U00002271', "NotGreaterLess;": '\U00002279', "NotGreaterTilde;": '\U00002275', "NotLeftTriangle;": '\U000022EA', "NotLeftTriangleEqual;": '\U000022EC', "NotLess;": '\U0000226E', "NotLessEqual;": '\U00002270', "NotLessGreater;": '\U00002278', "NotLessTilde;": '\U00002274', "NotPrecedes;": '\U00002280', "NotPrecedesSlantEqual;": '\U000022E0', "NotReverseElement;": '\U0000220C', "NotRightTriangle;": '\U000022EB', "NotRightTriangleEqual;": '\U000022ED', "NotSquareSubsetEqual;": '\U000022E2', "NotSquareSupersetEqual;": '\U000022E3', "NotSubsetEqual;": '\U00002288', "NotSucceeds;": '\U00002281', "NotSucceedsSlantEqual;": '\U000022E1', "NotSupersetEqual;": '\U00002289', "NotTilde;": '\U00002241', "NotTildeEqual;": '\U00002244', "NotTildeFullEqual;": '\U00002247', "NotTildeTilde;": '\U00002249', "NotVerticalBar;": '\U00002224', "Nscr;": '\U0001D4A9', "Ntilde;": '\U000000D1', "Nu;": '\U0000039D', "OElig;": '\U00000152', "Oacute;": '\U000000D3', "Ocirc;": '\U000000D4', "Ocy;": '\U0000041E', "Odblac;": '\U00000150', "Ofr;": '\U0001D512', "Ograve;": '\U000000D2', "Omacr;": '\U0000014C', "Omega;": '\U000003A9', "Omicron;": '\U0000039F', "Oopf;": '\U0001D546', "OpenCurlyDoubleQuote;": '\U0000201C', "OpenCurlyQuote;": '\U00002018', "Or;": '\U00002A54', "Oscr;": '\U0001D4AA', "Oslash;": '\U000000D8', "Otilde;": '\U000000D5', "Otimes;": '\U00002A37', "Ouml;": '\U000000D6', "OverBar;": '\U0000203E', "OverBrace;": '\U000023DE', "OverBracket;": '\U000023B4', "OverParenthesis;": '\U000023DC', "PartialD;": '\U00002202', "Pcy;": '\U0000041F', "Pfr;": '\U0001D513', "Phi;": '\U000003A6', "Pi;": '\U000003A0', "PlusMinus;": '\U000000B1', "Poincareplane;": '\U0000210C', "Popf;": '\U00002119', "Pr;": '\U00002ABB', "Precedes;": '\U0000227A', "PrecedesEqual;": '\U00002AAF', "PrecedesSlantEqual;": '\U0000227C', "PrecedesTilde;": '\U0000227E', "Prime;": '\U00002033', "Product;": '\U0000220F', "Proportion;": '\U00002237', "Proportional;": '\U0000221D', "Pscr;": '\U0001D4AB', "Psi;": '\U000003A8', "QUOT;": '\U00000022', "Qfr;": '\U0001D514', "Qopf;": '\U0000211A', "Qscr;": '\U0001D4AC', "RBarr;": '\U00002910', "REG;": '\U000000AE', "Racute;": '\U00000154', "Rang;": '\U000027EB', "Rarr;": '\U000021A0', "Rarrtl;": '\U00002916', "Rcaron;": '\U00000158', "Rcedil;": '\U00000156', "Rcy;": '\U00000420', "Re;": '\U0000211C', "ReverseElement;": '\U0000220B', "ReverseEquilibrium;": '\U000021CB', "ReverseUpEquilibrium;": '\U0000296F', "Rfr;": '\U0000211C', "Rho;": '\U000003A1', "RightAngleBracket;": '\U000027E9', "RightArrow;": '\U00002192', "RightArrowBar;": '\U000021E5', "RightArrowLeftArrow;": '\U000021C4', "RightCeiling;": '\U00002309', "RightDoubleBracket;": '\U000027E7', "RightDownTeeVector;": '\U0000295D', "RightDownVector;": '\U000021C2', "RightDownVectorBar;": '\U00002955', "RightFloor;": '\U0000230B', "RightTee;": '\U000022A2', "RightTeeArrow;": '\U000021A6', "RightTeeVector;": '\U0000295B', "RightTriangle;": '\U000022B3', "RightTriangleBar;": '\U000029D0', "RightTriangleEqual;": '\U000022B5', "RightUpDownVector;": '\U0000294F', "RightUpTeeVector;": '\U0000295C', "RightUpVector;": '\U000021BE', "RightUpVectorBar;": '\U00002954', "RightVector;": '\U000021C0', "RightVectorBar;": '\U00002953', "Rightarrow;": '\U000021D2', "Ropf;": '\U0000211D', "RoundImplies;": '\U00002970', "Rrightarrow;": '\U000021DB', "Rscr;": '\U0000211B', "Rsh;": '\U000021B1', "RuleDelayed;": '\U000029F4', "SHCHcy;": '\U00000429', "SHcy;": '\U00000428', "SOFTcy;": '\U0000042C', "Sacute;": '\U0000015A', "Sc;": '\U00002ABC', "Scaron;": '\U00000160', "Scedil;": '\U0000015E', "Scirc;": '\U0000015C', "Scy;": '\U00000421', "Sfr;": '\U0001D516', "ShortDownArrow;": '\U00002193', "ShortLeftArrow;": '\U00002190', "ShortRightArrow;": '\U00002192', "ShortUpArrow;": '\U00002191', "Sigma;": '\U000003A3', "SmallCircle;": '\U00002218', "Sopf;": '\U0001D54A', "Sqrt;": '\U0000221A', "Square;": '\U000025A1', "SquareIntersection;": '\U00002293', "SquareSubset;": '\U0000228F', "SquareSubsetEqual;": '\U00002291', "SquareSuperset;": '\U00002290', "SquareSupersetEqual;": '\U00002292', "SquareUnion;": '\U00002294', "Sscr;": '\U0001D4AE', "Star;": '\U000022C6', "Sub;": '\U000022D0', "Subset;": '\U000022D0', "SubsetEqual;": '\U00002286', "Succeeds;": '\U0000227B', "SucceedsEqual;": '\U00002AB0', "SucceedsSlantEqual;": '\U0000227D', "SucceedsTilde;": '\U0000227F', "SuchThat;": '\U0000220B', "Sum;": '\U00002211', "Sup;": '\U000022D1', "Superset;": '\U00002283', "SupersetEqual;": '\U00002287', "Supset;": '\U000022D1', "THORN;": '\U000000DE', "TRADE;": '\U00002122', "TSHcy;": '\U0000040B', "TScy;": '\U00000426', "Tab;": '\U00000009', "Tau;": '\U000003A4', "Tcaron;": '\U00000164', "Tcedil;": '\U00000162', "Tcy;": '\U00000422', "Tfr;": '\U0001D517', "Therefore;": '\U00002234', "Theta;": '\U00000398', "ThinSpace;": '\U00002009', "Tilde;": '\U0000223C', "TildeEqual;": '\U00002243', "TildeFullEqual;": '\U00002245', "TildeTilde;": '\U00002248', "Topf;": '\U0001D54B', "TripleDot;": '\U000020DB', "Tscr;": '\U0001D4AF', "Tstrok;": '\U00000166', "Uacute;": '\U000000DA', "Uarr;": '\U0000219F', "Uarrocir;": '\U00002949', "Ubrcy;": '\U0000040E', "Ubreve;": '\U0000016C', "Ucirc;": '\U000000DB', "Ucy;": '\U00000423', "Udblac;": '\U00000170', "Ufr;": '\U0001D518', "Ugrave;": '\U000000D9', "Umacr;": '\U0000016A', "UnderBar;": '\U0000005F', "UnderBrace;": '\U000023DF', "UnderBracket;": '\U000023B5', "UnderParenthesis;": '\U000023DD', "Union;": '\U000022C3', "UnionPlus;": '\U0000228E', "Uogon;": '\U00000172', "Uopf;": '\U0001D54C', "UpArrow;": '\U00002191', "UpArrowBar;": '\U00002912', "UpArrowDownArrow;": '\U000021C5', "UpDownArrow;": '\U00002195', "UpEquilibrium;": '\U0000296E', "UpTee;": '\U000022A5', "UpTeeArrow;": '\U000021A5', "Uparrow;": '\U000021D1', "Updownarrow;": '\U000021D5', "UpperLeftArrow;": '\U00002196', "UpperRightArrow;": '\U00002197', "Upsi;": '\U000003D2', "Upsilon;": '\U000003A5', "Uring;": '\U0000016E', "Uscr;": '\U0001D4B0', "Utilde;": '\U00000168', "Uuml;": '\U000000DC', "VDash;": '\U000022AB', "Vbar;": '\U00002AEB', "Vcy;": '\U00000412', "Vdash;": '\U000022A9', "Vdashl;": '\U00002AE6', "Vee;": '\U000022C1', "Verbar;": '\U00002016', "Vert;": '\U00002016', "VerticalBar;": '\U00002223', "VerticalLine;": '\U0000007C', "VerticalSeparator;": '\U00002758', "VerticalTilde;": '\U00002240', "VeryThinSpace;": '\U0000200A', "Vfr;": '\U0001D519', "Vopf;": '\U0001D54D', "Vscr;": '\U0001D4B1', "Vvdash;": '\U000022AA', "Wcirc;": '\U00000174', "Wedge;": '\U000022C0', "Wfr;": '\U0001D51A', "Wopf;": '\U0001D54E', "Wscr;": '\U0001D4B2', "Xfr;": '\U0001D51B', "Xi;": '\U0000039E', "Xopf;": '\U0001D54F', "Xscr;": '\U0001D4B3', "YAcy;": '\U0000042F', "YIcy;": '\U00000407', "YUcy;": '\U0000042E', "Yacute;": '\U000000DD', "Ycirc;": '\U00000176', "Ycy;": '\U0000042B', "Yfr;": '\U0001D51C', "Yopf;": '\U0001D550', "Yscr;": '\U0001D4B4', "Yuml;": '\U00000178', "ZHcy;": '\U00000416', "Zacute;": '\U00000179', "Zcaron;": '\U0000017D', "Zcy;": '\U00000417', "Zdot;": '\U0000017B', "ZeroWidthSpace;": '\U0000200B', "Zeta;": '\U00000396', "Zfr;": '\U00002128', "Zopf;": '\U00002124', "Zscr;": '\U0001D4B5', "aacute;": '\U000000E1', "abreve;": '\U00000103', "ac;": '\U0000223E', "acd;": '\U0000223F', "acirc;": '\U000000E2', "acute;": '\U000000B4', "acy;": '\U00000430', "aelig;": '\U000000E6', "af;": '\U00002061', "afr;": '\U0001D51E', "agrave;": '\U000000E0', "alefsym;": '\U00002135', "aleph;": '\U00002135', "alpha;": '\U000003B1', "amacr;": '\U00000101', "amalg;": '\U00002A3F', "amp;": '\U00000026', "and;": '\U00002227', "andand;": '\U00002A55', "andd;": '\U00002A5C', "andslope;": '\U00002A58', "andv;": '\U00002A5A', "ang;": '\U00002220', "ange;": '\U000029A4', "angle;": '\U00002220', "angmsd;": '\U00002221', "angmsdaa;": '\U000029A8', "angmsdab;": '\U000029A9', "angmsdac;": '\U000029AA', "angmsdad;": '\U000029AB', "angmsdae;": '\U000029AC', "angmsdaf;": '\U000029AD', "angmsdag;": '\U000029AE', "angmsdah;": '\U000029AF', "angrt;": '\U0000221F', "angrtvb;": '\U000022BE', "angrtvbd;": '\U0000299D', "angsph;": '\U00002222', "angst;": '\U000000C5', "angzarr;": '\U0000237C', "aogon;": '\U00000105', "aopf;": '\U0001D552', "ap;": '\U00002248', "apE;": '\U00002A70', "apacir;": '\U00002A6F', "ape;": '\U0000224A', "apid;": '\U0000224B', "apos;": '\U00000027', "approx;": '\U00002248', "approxeq;": '\U0000224A', "aring;": '\U000000E5', "ascr;": '\U0001D4B6', "ast;": '\U0000002A', "asymp;": '\U00002248', "asympeq;": '\U0000224D', "atilde;": '\U000000E3', "auml;": '\U000000E4', "awconint;": '\U00002233', "awint;": '\U00002A11', "bNot;": '\U00002AED', "backcong;": '\U0000224C', "backepsilon;": '\U000003F6', "backprime;": '\U00002035', "backsim;": '\U0000223D', "backsimeq;": '\U000022CD', "barvee;": '\U000022BD', "barwed;": '\U00002305', "barwedge;": '\U00002305', "bbrk;": '\U000023B5', "bbrktbrk;": '\U000023B6', "bcong;": '\U0000224C', "bcy;": '\U00000431', "bdquo;": '\U0000201E', "becaus;": '\U00002235', "because;": '\U00002235', "bemptyv;": '\U000029B0', "bepsi;": '\U000003F6', "bernou;": '\U0000212C', "beta;": '\U000003B2', "beth;": '\U00002136', "between;": '\U0000226C', "bfr;": '\U0001D51F', "bigcap;": '\U000022C2', "bigcirc;": '\U000025EF', "bigcup;": '\U000022C3', "bigodot;": '\U00002A00', "bigoplus;": '\U00002A01', "bigotimes;": '\U00002A02', "bigsqcup;": '\U00002A06', "bigstar;": '\U00002605', "bigtriangledown;": '\U000025BD', "bigtriangleup;": '\U000025B3', "biguplus;": '\U00002A04', "bigvee;": '\U000022C1', "bigwedge;": '\U000022C0', "bkarow;": '\U0000290D', "blacklozenge;": '\U000029EB', "blacksquare;": '\U000025AA', "blacktriangle;": '\U000025B4', "blacktriangledown;": '\U000025BE', "blacktriangleleft;": '\U000025C2', "blacktriangleright;": '\U000025B8', "blank;": '\U00002423', "blk12;": '\U00002592', "blk14;": '\U00002591', "blk34;": '\U00002593', "block;": '\U00002588', "bnot;": '\U00002310', "bopf;": '\U0001D553', "bot;": '\U000022A5', "bottom;": '\U000022A5', "bowtie;": '\U000022C8', "boxDL;": '\U00002557', "boxDR;": '\U00002554', "boxDl;": '\U00002556', "boxDr;": '\U00002553', "boxH;": '\U00002550', "boxHD;": '\U00002566', "boxHU;": '\U00002569', "boxHd;": '\U00002564', "boxHu;": '\U00002567', "boxUL;": '\U0000255D', "boxUR;": '\U0000255A', "boxUl;": '\U0000255C', "boxUr;": '\U00002559', "boxV;": '\U00002551', "boxVH;": '\U0000256C', "boxVL;": '\U00002563', "boxVR;": '\U00002560', "boxVh;": '\U0000256B', "boxVl;": '\U00002562', "boxVr;": '\U0000255F', "boxbox;": '\U000029C9', "boxdL;": '\U00002555', "boxdR;": '\U00002552', "boxdl;": '\U00002510', "boxdr;": '\U0000250C', "boxh;": '\U00002500', "boxhD;": '\U00002565', "boxhU;": '\U00002568', "boxhd;": '\U0000252C', "boxhu;": '\U00002534', "boxminus;": '\U0000229F', "boxplus;": '\U0000229E', "boxtimes;": '\U000022A0', "boxuL;": '\U0000255B', "boxuR;": '\U00002558', "boxul;": '\U00002518', "boxur;": '\U00002514', "boxv;": '\U00002502', "boxvH;": '\U0000256A', "boxvL;": '\U00002561', "boxvR;": '\U0000255E', "boxvh;": '\U0000253C', "boxvl;": '\U00002524', "boxvr;": '\U0000251C', "bprime;": '\U00002035', "breve;": '\U000002D8', "brvbar;": '\U000000A6', "bscr;": '\U0001D4B7', "bsemi;": '\U0000204F', "bsim;": '\U0000223D', "bsime;": '\U000022CD', "bsol;": '\U0000005C', "bsolb;": '\U000029C5', "bsolhsub;": '\U000027C8', "bull;": '\U00002022', "bullet;": '\U00002022', "bump;": '\U0000224E', "bumpE;": '\U00002AAE', "bumpe;": '\U0000224F', "bumpeq;": '\U0000224F', "cacute;": '\U00000107', "cap;": '\U00002229', "capand;": '\U00002A44', "capbrcup;": '\U00002A49', "capcap;": '\U00002A4B', "capcup;": '\U00002A47', "capdot;": '\U00002A40', "caret;": '\U00002041', "caron;": '\U000002C7', "ccaps;": '\U00002A4D', "ccaron;": '\U0000010D', "ccedil;": '\U000000E7', "ccirc;": '\U00000109', "ccups;": '\U00002A4C', "ccupssm;": '\U00002A50', "cdot;": '\U0000010B', "cedil;": '\U000000B8', "cemptyv;": '\U000029B2', "cent;": '\U000000A2', "centerdot;": '\U000000B7', "cfr;": '\U0001D520', "chcy;": '\U00000447', "check;": '\U00002713', "checkmark;": '\U00002713', "chi;": '\U000003C7', "cir;": '\U000025CB', "cirE;": '\U000029C3', "circ;": '\U000002C6', "circeq;": '\U00002257', "circlearrowleft;": '\U000021BA', "circlearrowright;": '\U000021BB', "circledR;": '\U000000AE', "circledS;": '\U000024C8', "circledast;": '\U0000229B', "circledcirc;": '\U0000229A', "circleddash;": '\U0000229D', "cire;": '\U00002257', "cirfnint;": '\U00002A10', "cirmid;": '\U00002AEF', "cirscir;": '\U000029C2', "clubs;": '\U00002663', "clubsuit;": '\U00002663', "colon;": '\U0000003A', "colone;": '\U00002254', "coloneq;": '\U00002254', "comma;": '\U0000002C', "commat;": '\U00000040', "comp;": '\U00002201', "compfn;": '\U00002218', "complement;": '\U00002201', "complexes;": '\U00002102', "cong;": '\U00002245', "congdot;": '\U00002A6D', "conint;": '\U0000222E', "copf;": '\U0001D554', "coprod;": '\U00002210', "copy;": '\U000000A9', "copysr;": '\U00002117', "crarr;": '\U000021B5', "cross;": '\U00002717', "cscr;": '\U0001D4B8', "csub;": '\U00002ACF', "csube;": '\U00002AD1', "csup;": '\U00002AD0', "csupe;": '\U00002AD2', "ctdot;": '\U000022EF', "cudarrl;": '\U00002938', "cudarrr;": '\U00002935', "cuepr;": '\U000022DE', "cuesc;": '\U000022DF', "cularr;": '\U000021B6', "cularrp;": '\U0000293D', "cup;": '\U0000222A', "cupbrcap;": '\U00002A48', "cupcap;": '\U00002A46', "cupcup;": '\U00002A4A', "cupdot;": '\U0000228D', "cupor;": '\U00002A45', "curarr;": '\U000021B7', "curarrm;": '\U0000293C', "curlyeqprec;": '\U000022DE', "curlyeqsucc;": '\U000022DF', "curlyvee;": '\U000022CE', "curlywedge;": '\U000022CF', "curren;": '\U000000A4', "curvearrowleft;": '\U000021B6', "curvearrowright;": '\U000021B7', "cuvee;": '\U000022CE', "cuwed;": '\U000022CF', "cwconint;": '\U00002232', "cwint;": '\U00002231', "cylcty;": '\U0000232D', "dArr;": '\U000021D3', "dHar;": '\U00002965', "dagger;": '\U00002020', "daleth;": '\U00002138', "darr;": '\U00002193', "dash;": '\U00002010', "dashv;": '\U000022A3', "dbkarow;": '\U0000290F', "dblac;": '\U000002DD', "dcaron;": '\U0000010F', "dcy;": '\U00000434', "dd;": '\U00002146', "ddagger;": '\U00002021', "ddarr;": '\U000021CA', "ddotseq;": '\U00002A77', "deg;": '\U000000B0', "delta;": '\U000003B4', "demptyv;": '\U000029B1', "dfisht;": '\U0000297F', "dfr;": '\U0001D521', "dharl;": '\U000021C3', "dharr;": '\U000021C2', "diam;": '\U000022C4', "diamond;": '\U000022C4', "diamondsuit;": '\U00002666', "diams;": '\U00002666', "die;": '\U000000A8', "digamma;": '\U000003DD', "disin;": '\U000022F2', "div;": '\U000000F7', "divide;": '\U000000F7', "divideontimes;": '\U000022C7', "divonx;": '\U000022C7', "djcy;": '\U00000452', "dlcorn;": '\U0000231E', "dlcrop;": '\U0000230D', "dollar;": '\U00000024', "dopf;": '\U0001D555', "dot;": '\U000002D9', "doteq;": '\U00002250', "doteqdot;": '\U00002251', "dotminus;": '\U00002238', "dotplus;": '\U00002214', "dotsquare;": '\U000022A1', "doublebarwedge;": '\U00002306', "downarrow;": '\U00002193', "downdownarrows;": '\U000021CA', "downharpoonleft;": '\U000021C3', "downharpoonright;": '\U000021C2', "drbkarow;": '\U00002910', "drcorn;": '\U0000231F', "drcrop;": '\U0000230C', "dscr;": '\U0001D4B9', "dscy;": '\U00000455', "dsol;": '\U000029F6', "dstrok;": '\U00000111', "dtdot;": '\U000022F1', "dtri;": '\U000025BF', "dtrif;": '\U000025BE', "duarr;": '\U000021F5', "duhar;": '\U0000296F', "dwangle;": '\U000029A6', "dzcy;": '\U0000045F', "dzigrarr;": '\U000027FF', "eDDot;": '\U00002A77', "eDot;": '\U00002251', "eacute;": '\U000000E9', "easter;": '\U00002A6E', "ecaron;": '\U0000011B', "ecir;": '\U00002256', "ecirc;": '\U000000EA', "ecolon;": '\U00002255', "ecy;": '\U0000044D', "edot;": '\U00000117', "ee;": '\U00002147', "efDot;": '\U00002252', "efr;": '\U0001D522', "eg;": '\U00002A9A', "egrave;": '\U000000E8', "egs;": '\U00002A96', "egsdot;": '\U00002A98', "el;": '\U00002A99', "elinters;": '\U000023E7', "ell;": '\U00002113', "els;": '\U00002A95', "elsdot;": '\U00002A97', "emacr;": '\U00000113', "empty;": '\U00002205', "emptyset;": '\U00002205', "emptyv;": '\U00002205', "emsp;": '\U00002003', "emsp13;": '\U00002004', "emsp14;": '\U00002005', "eng;": '\U0000014B', "ensp;": '\U00002002', "eogon;": '\U00000119', "eopf;": '\U0001D556', "epar;": '\U000022D5', "eparsl;": '\U000029E3', "eplus;": '\U00002A71', "epsi;": '\U000003B5', "epsilon;": '\U000003B5', "epsiv;": '\U000003F5', "eqcirc;": '\U00002256', "eqcolon;": '\U00002255', "eqsim;": '\U00002242', "eqslantgtr;": '\U00002A96', "eqslantless;": '\U00002A95', "equals;": '\U0000003D', "equest;": '\U0000225F', "equiv;": '\U00002261', "equivDD;": '\U00002A78', "eqvparsl;": '\U000029E5', "erDot;": '\U00002253', "erarr;": '\U00002971', "escr;": '\U0000212F', "esdot;": '\U00002250', "esim;": '\U00002242', "eta;": '\U000003B7', "eth;": '\U000000F0', "euml;": '\U000000EB', "euro;": '\U000020AC', "excl;": '\U00000021', "exist;": '\U00002203', "expectation;": '\U00002130', "exponentiale;": '\U00002147', "fallingdotseq;": '\U00002252', "fcy;": '\U00000444', "female;": '\U00002640', "ffilig;": '\U0000FB03', "fflig;": '\U0000FB00', "ffllig;": '\U0000FB04', "ffr;": '\U0001D523', "filig;": '\U0000FB01', "flat;": '\U0000266D', "fllig;": '\U0000FB02', "fltns;": '\U000025B1', "fnof;": '\U00000192', "fopf;": '\U0001D557', "forall;": '\U00002200', "fork;": '\U000022D4', "forkv;": '\U00002AD9', "fpartint;": '\U00002A0D', "frac12;": '\U000000BD', "frac13;": '\U00002153', "frac14;": '\U000000BC', "frac15;": '\U00002155', "frac16;": '\U00002159', "frac18;": '\U0000215B', "frac23;": '\U00002154', "frac25;": '\U00002156', "frac34;": '\U000000BE', "frac35;": '\U00002157', "frac38;": '\U0000215C', "frac45;": '\U00002158', "frac56;": '\U0000215A', "frac58;": '\U0000215D', "frac78;": '\U0000215E', "frasl;": '\U00002044', "frown;": '\U00002322', "fscr;": '\U0001D4BB', "gE;": '\U00002267', "gEl;": '\U00002A8C', "gacute;": '\U000001F5', "gamma;": '\U000003B3', "gammad;": '\U000003DD', "gap;": '\U00002A86', "gbreve;": '\U0000011F', "gcirc;": '\U0000011D', "gcy;": '\U00000433', "gdot;": '\U00000121', "ge;": '\U00002265', "gel;": '\U000022DB', "geq;": '\U00002265', "geqq;": '\U00002267', "geqslant;": '\U00002A7E', "ges;": '\U00002A7E', "gescc;": '\U00002AA9', "gesdot;": '\U00002A80', "gesdoto;": '\U00002A82', "gesdotol;": '\U00002A84', "gesles;": '\U00002A94', "gfr;": '\U0001D524', "gg;": '\U0000226B', "ggg;": '\U000022D9', "gimel;": '\U00002137', "gjcy;": '\U00000453', "gl;": '\U00002277', "glE;": '\U00002A92', "gla;": '\U00002AA5', "glj;": '\U00002AA4', "gnE;": '\U00002269', "gnap;": '\U00002A8A', "gnapprox;": '\U00002A8A', "gne;": '\U00002A88', "gneq;": '\U00002A88', "gneqq;": '\U00002269', "gnsim;": '\U000022E7', "gopf;": '\U0001D558', "grave;": '\U00000060', "gscr;": '\U0000210A', "gsim;": '\U00002273', "gsime;": '\U00002A8E', "gsiml;": '\U00002A90', "gt;": '\U0000003E', "gtcc;": '\U00002AA7', "gtcir;": '\U00002A7A', "gtdot;": '\U000022D7', "gtlPar;": '\U00002995', "gtquest;": '\U00002A7C', "gtrapprox;": '\U00002A86', "gtrarr;": '\U00002978', "gtrdot;": '\U000022D7', "gtreqless;": '\U000022DB', "gtreqqless;": '\U00002A8C', "gtrless;": '\U00002277', "gtrsim;": '\U00002273', "hArr;": '\U000021D4', "hairsp;": '\U0000200A', "half;": '\U000000BD', "hamilt;": '\U0000210B', "hardcy;": '\U0000044A', "harr;": '\U00002194', "harrcir;": '\U00002948', "harrw;": '\U000021AD', "hbar;": '\U0000210F', "hcirc;": '\U00000125', "hearts;": '\U00002665', "heartsuit;": '\U00002665', "hellip;": '\U00002026', "hercon;": '\U000022B9', "hfr;": '\U0001D525', "hksearow;": '\U00002925', "hkswarow;": '\U00002926', "hoarr;": '\U000021FF', "homtht;": '\U0000223B', "hookleftarrow;": '\U000021A9', "hookrightarrow;": '\U000021AA', "hopf;": '\U0001D559', "horbar;": '\U00002015', "hscr;": '\U0001D4BD', "hslash;": '\U0000210F', "hstrok;": '\U00000127', "hybull;": '\U00002043', "hyphen;": '\U00002010', "iacute;": '\U000000ED', "ic;": '\U00002063', "icirc;": '\U000000EE', "icy;": '\U00000438', "iecy;": '\U00000435', "iexcl;": '\U000000A1', "iff;": '\U000021D4', "ifr;": '\U0001D526', "igrave;": '\U000000EC', "ii;": '\U00002148', "iiiint;": '\U00002A0C', "iiint;": '\U0000222D', "iinfin;": '\U000029DC', "iiota;": '\U00002129', "ijlig;": '\U00000133', "imacr;": '\U0000012B', "image;": '\U00002111', "imagline;": '\U00002110', "imagpart;": '\U00002111', "imath;": '\U00000131', "imof;": '\U000022B7', "imped;": '\U000001B5', "in;": '\U00002208', "incare;": '\U00002105', "infin;": '\U0000221E', "infintie;": '\U000029DD', "inodot;": '\U00000131', "int;": '\U0000222B', "intcal;": '\U000022BA', "integers;": '\U00002124', "intercal;": '\U000022BA', "intlarhk;": '\U00002A17', "intprod;": '\U00002A3C', "iocy;": '\U00000451', "iogon;": '\U0000012F', "iopf;": '\U0001D55A', "iota;": '\U000003B9', "iprod;": '\U00002A3C', "iquest;": '\U000000BF', "iscr;": '\U0001D4BE', "isin;": '\U00002208', "isinE;": '\U000022F9', "isindot;": '\U000022F5', "isins;": '\U000022F4', "isinsv;": '\U000022F3', "isinv;": '\U00002208', "it;": '\U00002062', "itilde;": '\U00000129', "iukcy;": '\U00000456', "iuml;": '\U000000EF', "jcirc;": '\U00000135', "jcy;": '\U00000439', "jfr;": '\U0001D527', "jmath;": '\U00000237', "jopf;": '\U0001D55B', "jscr;": '\U0001D4BF', "jsercy;": '\U00000458', "jukcy;": '\U00000454', "kappa;": '\U000003BA', "kappav;": '\U000003F0', "kcedil;": '\U00000137', "kcy;": '\U0000043A', "kfr;": '\U0001D528', "kgreen;": '\U00000138', "khcy;": '\U00000445', "kjcy;": '\U0000045C', "kopf;": '\U0001D55C', "kscr;": '\U0001D4C0', "lAarr;": '\U000021DA', "lArr;": '\U000021D0', "lAtail;": '\U0000291B', "lBarr;": '\U0000290E', "lE;": '\U00002266', "lEg;": '\U00002A8B', "lHar;": '\U00002962', "lacute;": '\U0000013A', "laemptyv;": '\U000029B4', "lagran;": '\U00002112', "lambda;": '\U000003BB', "lang;": '\U000027E8', "langd;": '\U00002991', "langle;": '\U000027E8', "lap;": '\U00002A85', "laquo;": '\U000000AB', "larr;": '\U00002190', "larrb;": '\U000021E4', "larrbfs;": '\U0000291F', "larrfs;": '\U0000291D', "larrhk;": '\U000021A9', "larrlp;": '\U000021AB', "larrpl;": '\U00002939', "larrsim;": '\U00002973', "larrtl;": '\U000021A2', "lat;": '\U00002AAB', "latail;": '\U00002919', "late;": '\U00002AAD', "lbarr;": '\U0000290C', "lbbrk;": '\U00002772', "lbrace;": '\U0000007B', "lbrack;": '\U0000005B', "lbrke;": '\U0000298B', "lbrksld;": '\U0000298F', "lbrkslu;": '\U0000298D', "lcaron;": '\U0000013E', "lcedil;": '\U0000013C', "lceil;": '\U00002308', "lcub;": '\U0000007B', "lcy;": '\U0000043B', "ldca;": '\U00002936', "ldquo;": '\U0000201C', "ldquor;": '\U0000201E', "ldrdhar;": '\U00002967', "ldrushar;": '\U0000294B', "ldsh;": '\U000021B2', "le;": '\U00002264', "leftarrow;": '\U00002190', "leftarrowtail;": '\U000021A2', "leftharpoondown;": '\U000021BD', "leftharpoonup;": '\U000021BC', "leftleftarrows;": '\U000021C7', "leftrightarrow;": '\U00002194', "leftrightarrows;": '\U000021C6', "leftrightharpoons;": '\U000021CB', "leftrightsquigarrow;": '\U000021AD', "leftthreetimes;": '\U000022CB', "leg;": '\U000022DA', "leq;": '\U00002264', "leqq;": '\U00002266', "leqslant;": '\U00002A7D', "les;": '\U00002A7D', "lescc;": '\U00002AA8', "lesdot;": '\U00002A7F', "lesdoto;": '\U00002A81', "lesdotor;": '\U00002A83', "lesges;": '\U00002A93', "lessapprox;": '\U00002A85', "lessdot;": '\U000022D6', "lesseqgtr;": '\U000022DA', "lesseqqgtr;": '\U00002A8B', "lessgtr;": '\U00002276', "lesssim;": '\U00002272', "lfisht;": '\U0000297C', "lfloor;": '\U0000230A', "lfr;": '\U0001D529', "lg;": '\U00002276', "lgE;": '\U00002A91', "lhard;": '\U000021BD', "lharu;": '\U000021BC', "lharul;": '\U0000296A', "lhblk;": '\U00002584', "ljcy;": '\U00000459', "ll;": '\U0000226A', "llarr;": '\U000021C7', "llcorner;": '\U0000231E', "llhard;": '\U0000296B', "lltri;": '\U000025FA', "lmidot;": '\U00000140', "lmoust;": '\U000023B0', "lmoustache;": '\U000023B0', "lnE;": '\U00002268', "lnap;": '\U00002A89', "lnapprox;": '\U00002A89', "lne;": '\U00002A87', "lneq;": '\U00002A87', "lneqq;": '\U00002268', "lnsim;": '\U000022E6', "loang;": '\U000027EC', "loarr;": '\U000021FD', "lobrk;": '\U000027E6', "longleftarrow;": '\U000027F5', "longleftrightarrow;": '\U000027F7', "longmapsto;": '\U000027FC', "longrightarrow;": '\U000027F6', "looparrowleft;": '\U000021AB', "looparrowright;": '\U000021AC', "lopar;": '\U00002985', "lopf;": '\U0001D55D', "loplus;": '\U00002A2D', "lotimes;": '\U00002A34', "lowast;": '\U00002217', "lowbar;": '\U0000005F', "loz;": '\U000025CA', "lozenge;": '\U000025CA', "lozf;": '\U000029EB', "lpar;": '\U00000028', "lparlt;": '\U00002993', "lrarr;": '\U000021C6', "lrcorner;": '\U0000231F', "lrhar;": '\U000021CB', "lrhard;": '\U0000296D', "lrm;": '\U0000200E', "lrtri;": '\U000022BF', "lsaquo;": '\U00002039', "lscr;": '\U0001D4C1', "lsh;": '\U000021B0', "lsim;": '\U00002272', "lsime;": '\U00002A8D', "lsimg;": '\U00002A8F', "lsqb;": '\U0000005B', "lsquo;": '\U00002018', "lsquor;": '\U0000201A', "lstrok;": '\U00000142', "lt;": '\U0000003C', "ltcc;": '\U00002AA6', "ltcir;": '\U00002A79', "ltdot;": '\U000022D6', "lthree;": '\U000022CB', "ltimes;": '\U000022C9', "ltlarr;": '\U00002976', "ltquest;": '\U00002A7B', "ltrPar;": '\U00002996', "ltri;": '\U000025C3', "ltrie;": '\U000022B4', "ltrif;": '\U000025C2', "lurdshar;": '\U0000294A', "luruhar;": '\U00002966', "mDDot;": '\U0000223A', "macr;": '\U000000AF', "male;": '\U00002642', "malt;": '\U00002720', "maltese;": '\U00002720', "map;": '\U000021A6', "mapsto;": '\U000021A6', "mapstodown;": '\U000021A7', "mapstoleft;": '\U000021A4', "mapstoup;": '\U000021A5', "marker;": '\U000025AE', "mcomma;": '\U00002A29', "mcy;": '\U0000043C', "mdash;": '\U00002014', "measuredangle;": '\U00002221', "mfr;": '\U0001D52A', "mho;": '\U00002127', "micro;": '\U000000B5', "mid;": '\U00002223', "midast;": '\U0000002A', "midcir;": '\U00002AF0', "middot;": '\U000000B7', "minus;": '\U00002212', "minusb;": '\U0000229F', "minusd;": '\U00002238', "minusdu;": '\U00002A2A', "mlcp;": '\U00002ADB', "mldr;": '\U00002026', "mnplus;": '\U00002213', "models;": '\U000022A7', "mopf;": '\U0001D55E', "mp;": '\U00002213', "mscr;": '\U0001D4C2', "mstpos;": '\U0000223E', "mu;": '\U000003BC', "multimap;": '\U000022B8', "mumap;": '\U000022B8', "nLeftarrow;": '\U000021CD', "nLeftrightarrow;": '\U000021CE', "nRightarrow;": '\U000021CF', "nVDash;": '\U000022AF', "nVdash;": '\U000022AE', "nabla;": '\U00002207', "nacute;": '\U00000144', "nap;": '\U00002249', "napos;": '\U00000149', "napprox;": '\U00002249', "natur;": '\U0000266E', "natural;": '\U0000266E', "naturals;": '\U00002115', "nbsp;": '\U000000A0', "ncap;": '\U00002A43', "ncaron;": '\U00000148', "ncedil;": '\U00000146', "ncong;": '\U00002247', "ncup;": '\U00002A42', "ncy;": '\U0000043D', "ndash;": '\U00002013', "ne;": '\U00002260', "neArr;": '\U000021D7', "nearhk;": '\U00002924', "nearr;": '\U00002197', "nearrow;": '\U00002197', "nequiv;": '\U00002262', "nesear;": '\U00002928', "nexist;": '\U00002204', "nexists;": '\U00002204', "nfr;": '\U0001D52B', "nge;": '\U00002271', "ngeq;": '\U00002271', "ngsim;": '\U00002275', "ngt;": '\U0000226F', "ngtr;": '\U0000226F', "nhArr;": '\U000021CE', "nharr;": '\U000021AE', "nhpar;": '\U00002AF2', "ni;": '\U0000220B', "nis;": '\U000022FC', "nisd;": '\U000022FA', "niv;": '\U0000220B', "njcy;": '\U0000045A', "nlArr;": '\U000021CD', "nlarr;": '\U0000219A', "nldr;": '\U00002025', "nle;": '\U00002270', "nleftarrow;": '\U0000219A', "nleftrightarrow;": '\U000021AE', "nleq;": '\U00002270', "nless;": '\U0000226E', "nlsim;": '\U00002274', "nlt;": '\U0000226E', "nltri;": '\U000022EA', "nltrie;": '\U000022EC', "nmid;": '\U00002224', "nopf;": '\U0001D55F', "not;": '\U000000AC', "notin;": '\U00002209', "notinva;": '\U00002209', "notinvb;": '\U000022F7', "notinvc;": '\U000022F6', "notni;": '\U0000220C', "notniva;": '\U0000220C', "notnivb;": '\U000022FE', "notnivc;": '\U000022FD', "npar;": '\U00002226', "nparallel;": '\U00002226', "npolint;": '\U00002A14', "npr;": '\U00002280', "nprcue;": '\U000022E0', "nprec;": '\U00002280', "nrArr;": '\U000021CF', "nrarr;": '\U0000219B', "nrightarrow;": '\U0000219B', "nrtri;": '\U000022EB', "nrtrie;": '\U000022ED', "nsc;": '\U00002281', "nsccue;": '\U000022E1', "nscr;": '\U0001D4C3', "nshortmid;": '\U00002224', "nshortparallel;": '\U00002226', "nsim;": '\U00002241', "nsime;": '\U00002244', "nsimeq;": '\U00002244', "nsmid;": '\U00002224', "nspar;": '\U00002226', "nsqsube;": '\U000022E2', "nsqsupe;": '\U000022E3', "nsub;": '\U00002284', "nsube;": '\U00002288', "nsubseteq;": '\U00002288', "nsucc;": '\U00002281', "nsup;": '\U00002285', "nsupe;": '\U00002289', "nsupseteq;": '\U00002289', "ntgl;": '\U00002279', "ntilde;": '\U000000F1', "ntlg;": '\U00002278', "ntriangleleft;": '\U000022EA', "ntrianglelefteq;": '\U000022EC', "ntriangleright;": '\U000022EB', "ntrianglerighteq;": '\U000022ED', "nu;": '\U000003BD', "num;": '\U00000023', "numero;": '\U00002116', "numsp;": '\U00002007', "nvDash;": '\U000022AD', "nvHarr;": '\U00002904', "nvdash;": '\U000022AC', "nvinfin;": '\U000029DE', "nvlArr;": '\U00002902', "nvrArr;": '\U00002903', "nwArr;": '\U000021D6', "nwarhk;": '\U00002923', "nwarr;": '\U00002196', "nwarrow;": '\U00002196', "nwnear;": '\U00002927', "oS;": '\U000024C8', "oacute;": '\U000000F3', "oast;": '\U0000229B', "ocir;": '\U0000229A', "ocirc;": '\U000000F4', "ocy;": '\U0000043E', "odash;": '\U0000229D', "odblac;": '\U00000151', "odiv;": '\U00002A38', "odot;": '\U00002299', "odsold;": '\U000029BC', "oelig;": '\U00000153', "ofcir;": '\U000029BF', "ofr;": '\U0001D52C', "ogon;": '\U000002DB', "ograve;": '\U000000F2', "ogt;": '\U000029C1', "ohbar;": '\U000029B5', "ohm;": '\U000003A9', "oint;": '\U0000222E', "olarr;": '\U000021BA', "olcir;": '\U000029BE', "olcross;": '\U000029BB', "oline;": '\U0000203E', "olt;": '\U000029C0', "omacr;": '\U0000014D', "omega;": '\U000003C9', "omicron;": '\U000003BF', "omid;": '\U000029B6', "ominus;": '\U00002296', "oopf;": '\U0001D560', "opar;": '\U000029B7', "operp;": '\U000029B9', "oplus;": '\U00002295', "or;": '\U00002228', "orarr;": '\U000021BB', "ord;": '\U00002A5D', "order;": '\U00002134', "orderof;": '\U00002134', "ordf;": '\U000000AA', "ordm;": '\U000000BA', "origof;": '\U000022B6', "oror;": '\U00002A56', "orslope;": '\U00002A57', "orv;": '\U00002A5B', "oscr;": '\U00002134', "oslash;": '\U000000F8', "osol;": '\U00002298', "otilde;": '\U000000F5', "otimes;": '\U00002297', "otimesas;": '\U00002A36', "ouml;": '\U000000F6', "ovbar;": '\U0000233D', "par;": '\U00002225', "para;": '\U000000B6', "parallel;": '\U00002225', "parsim;": '\U00002AF3', "parsl;": '\U00002AFD', "part;": '\U00002202', "pcy;": '\U0000043F', "percnt;": '\U00000025', "period;": '\U0000002E', "permil;": '\U00002030', "perp;": '\U000022A5', "pertenk;": '\U00002031', "pfr;": '\U0001D52D', "phi;": '\U000003C6', "phiv;": '\U000003D5', "phmmat;": '\U00002133', "phone;": '\U0000260E', "pi;": '\U000003C0', "pitchfork;": '\U000022D4', "piv;": '\U000003D6', "planck;": '\U0000210F', "planckh;": '\U0000210E', "plankv;": '\U0000210F', "plus;": '\U0000002B', "plusacir;": '\U00002A23', "plusb;": '\U0000229E', "pluscir;": '\U00002A22', "plusdo;": '\U00002214', "plusdu;": '\U00002A25', "pluse;": '\U00002A72', "plusmn;": '\U000000B1', "plussim;": '\U00002A26', "plustwo;": '\U00002A27', "pm;": '\U000000B1', "pointint;": '\U00002A15', "popf;": '\U0001D561', "pound;": '\U000000A3', "pr;": '\U0000227A', "prE;": '\U00002AB3', "prap;": '\U00002AB7', "prcue;": '\U0000227C', "pre;": '\U00002AAF', "prec;": '\U0000227A', "precapprox;": '\U00002AB7', "preccurlyeq;": '\U0000227C', "preceq;": '\U00002AAF', "precnapprox;": '\U00002AB9', "precneqq;": '\U00002AB5', "precnsim;": '\U000022E8', "precsim;": '\U0000227E', "prime;": '\U00002032', "primes;": '\U00002119', "prnE;": '\U00002AB5', "prnap;": '\U00002AB9', "prnsim;": '\U000022E8', "prod;": '\U0000220F', "profalar;": '\U0000232E', "profline;": '\U00002312', "profsurf;": '\U00002313', "prop;": '\U0000221D', "propto;": '\U0000221D', "prsim;": '\U0000227E', "prurel;": '\U000022B0', "pscr;": '\U0001D4C5', "psi;": '\U000003C8', "puncsp;": '\U00002008', "qfr;": '\U0001D52E', "qint;": '\U00002A0C', "qopf;": '\U0001D562', "qprime;": '\U00002057', "qscr;": '\U0001D4C6', "quaternions;": '\U0000210D', "quatint;": '\U00002A16', "quest;": '\U0000003F', "questeq;": '\U0000225F', "quot;": '\U00000022', "rAarr;": '\U000021DB', "rArr;": '\U000021D2', "rAtail;": '\U0000291C', "rBarr;": '\U0000290F', "rHar;": '\U00002964', "racute;": '\U00000155', "radic;": '\U0000221A', "raemptyv;": '\U000029B3', "rang;": '\U000027E9', "rangd;": '\U00002992', "range;": '\U000029A5', "rangle;": '\U000027E9', "raquo;": '\U000000BB', "rarr;": '\U00002192', "rarrap;": '\U00002975', "rarrb;": '\U000021E5', "rarrbfs;": '\U00002920', "rarrc;": '\U00002933', "rarrfs;": '\U0000291E', "rarrhk;": '\U000021AA', "rarrlp;": '\U000021AC', "rarrpl;": '\U00002945', "rarrsim;": '\U00002974', "rarrtl;": '\U000021A3', "rarrw;": '\U0000219D', "ratail;": '\U0000291A', "ratio;": '\U00002236', "rationals;": '\U0000211A', "rbarr;": '\U0000290D', "rbbrk;": '\U00002773', "rbrace;": '\U0000007D', "rbrack;": '\U0000005D', "rbrke;": '\U0000298C', "rbrksld;": '\U0000298E', "rbrkslu;": '\U00002990', "rcaron;": '\U00000159', "rcedil;": '\U00000157', "rceil;": '\U00002309', "rcub;": '\U0000007D', "rcy;": '\U00000440', "rdca;": '\U00002937', "rdldhar;": '\U00002969', "rdquo;": '\U0000201D', "rdquor;": '\U0000201D', "rdsh;": '\U000021B3', "real;": '\U0000211C', "realine;": '\U0000211B', "realpart;": '\U0000211C', "reals;": '\U0000211D', "rect;": '\U000025AD', "reg;": '\U000000AE', "rfisht;": '\U0000297D', "rfloor;": '\U0000230B', "rfr;": '\U0001D52F', "rhard;": '\U000021C1', "rharu;": '\U000021C0', "rharul;": '\U0000296C', "rho;": '\U000003C1', "rhov;": '\U000003F1', "rightarrow;": '\U00002192', "rightarrowtail;": '\U000021A3', "rightharpoondown;": '\U000021C1', "rightharpoonup;": '\U000021C0', "rightleftarrows;": '\U000021C4', "rightleftharpoons;": '\U000021CC', "rightrightarrows;": '\U000021C9', "rightsquigarrow;": '\U0000219D', "rightthreetimes;": '\U000022CC', "ring;": '\U000002DA', "risingdotseq;": '\U00002253', "rlarr;": '\U000021C4', "rlhar;": '\U000021CC', "rlm;": '\U0000200F', "rmoust;": '\U000023B1', "rmoustache;": '\U000023B1', "rnmid;": '\U00002AEE', "roang;": '\U000027ED', "roarr;": '\U000021FE', "robrk;": '\U000027E7', "ropar;": '\U00002986', "ropf;": '\U0001D563', "roplus;": '\U00002A2E', "rotimes;": '\U00002A35', "rpar;": '\U00000029', "rpargt;": '\U00002994', "rppolint;": '\U00002A12', "rrarr;": '\U000021C9', "rsaquo;": '\U0000203A', "rscr;": '\U0001D4C7', "rsh;": '\U000021B1', "rsqb;": '\U0000005D', "rsquo;": '\U00002019', "rsquor;": '\U00002019', "rthree;": '\U000022CC', "rtimes;": '\U000022CA', "rtri;": '\U000025B9', "rtrie;": '\U000022B5', "rtrif;": '\U000025B8', "rtriltri;": '\U000029CE', "ruluhar;": '\U00002968', "rx;": '\U0000211E', "sacute;": '\U0000015B', "sbquo;": '\U0000201A', "sc;": '\U0000227B', "scE;": '\U00002AB4', "scap;": '\U00002AB8', "scaron;": '\U00000161', "sccue;": '\U0000227D', "sce;": '\U00002AB0', "scedil;": '\U0000015F', "scirc;": '\U0000015D', "scnE;": '\U00002AB6', "scnap;": '\U00002ABA', "scnsim;": '\U000022E9', "scpolint;": '\U00002A13', "scsim;": '\U0000227F', "scy;": '\U00000441', "sdot;": '\U000022C5', "sdotb;": '\U000022A1', "sdote;": '\U00002A66', "seArr;": '\U000021D8', "searhk;": '\U00002925', "searr;": '\U00002198', "searrow;": '\U00002198', "sect;": '\U000000A7', "semi;": '\U0000003B', "seswar;": '\U00002929', "setminus;": '\U00002216', "setmn;": '\U00002216', "sext;": '\U00002736', "sfr;": '\U0001D530', "sfrown;": '\U00002322', "sharp;": '\U0000266F', "shchcy;": '\U00000449', "shcy;": '\U00000448', "shortmid;": '\U00002223', "shortparallel;": '\U00002225', "shy;": '\U000000AD', "sigma;": '\U000003C3', "sigmaf;": '\U000003C2', "sigmav;": '\U000003C2', "sim;": '\U0000223C', "simdot;": '\U00002A6A', "sime;": '\U00002243', "simeq;": '\U00002243', "simg;": '\U00002A9E', "simgE;": '\U00002AA0', "siml;": '\U00002A9D', "simlE;": '\U00002A9F', "simne;": '\U00002246', "simplus;": '\U00002A24', "simrarr;": '\U00002972', "slarr;": '\U00002190', "smallsetminus;": '\U00002216', "smashp;": '\U00002A33', "smeparsl;": '\U000029E4', "smid;": '\U00002223', "smile;": '\U00002323', "smt;": '\U00002AAA', "smte;": '\U00002AAC', "softcy;": '\U0000044C', "sol;": '\U0000002F', "solb;": '\U000029C4', "solbar;": '\U0000233F', "sopf;": '\U0001D564', "spades;": '\U00002660', "spadesuit;": '\U00002660', "spar;": '\U00002225', "sqcap;": '\U00002293', "sqcup;": '\U00002294', "sqsub;": '\U0000228F', "sqsube;": '\U00002291', "sqsubset;": '\U0000228F', "sqsubseteq;": '\U00002291', "sqsup;": '\U00002290', "sqsupe;": '\U00002292', "sqsupset;": '\U00002290', "sqsupseteq;": '\U00002292', "squ;": '\U000025A1', "square;": '\U000025A1', "squarf;": '\U000025AA', "squf;": '\U000025AA', "srarr;": '\U00002192', "sscr;": '\U0001D4C8', "ssetmn;": '\U00002216', "ssmile;": '\U00002323', "sstarf;": '\U000022C6', "star;": '\U00002606', "starf;": '\U00002605', "straightepsilon;": '\U000003F5', "straightphi;": '\U000003D5', "strns;": '\U000000AF', "sub;": '\U00002282', "subE;": '\U00002AC5', "subdot;": '\U00002ABD', "sube;": '\U00002286', "subedot;": '\U00002AC3', "submult;": '\U00002AC1', "subnE;": '\U00002ACB', "subne;": '\U0000228A', "subplus;": '\U00002ABF', "subrarr;": '\U00002979', "subset;": '\U00002282', "subseteq;": '\U00002286', "subseteqq;": '\U00002AC5', "subsetneq;": '\U0000228A', "subsetneqq;": '\U00002ACB', "subsim;": '\U00002AC7', "subsub;": '\U00002AD5', "subsup;": '\U00002AD3', "succ;": '\U0000227B', "succapprox;": '\U00002AB8', "succcurlyeq;": '\U0000227D', "succeq;": '\U00002AB0', "succnapprox;": '\U00002ABA', "succneqq;": '\U00002AB6', "succnsim;": '\U000022E9', "succsim;": '\U0000227F', "sum;": '\U00002211', "sung;": '\U0000266A', "sup;": '\U00002283', "sup1;": '\U000000B9', "sup2;": '\U000000B2', "sup3;": '\U000000B3', "supE;": '\U00002AC6', "supdot;": '\U00002ABE', "supdsub;": '\U00002AD8', "supe;": '\U00002287', "supedot;": '\U00002AC4', "suphsol;": '\U000027C9', "suphsub;": '\U00002AD7', "suplarr;": '\U0000297B', "supmult;": '\U00002AC2', "supnE;": '\U00002ACC', "supne;": '\U0000228B', "supplus;": '\U00002AC0', "supset;": '\U00002283', "supseteq;": '\U00002287', "supseteqq;": '\U00002AC6', "supsetneq;": '\U0000228B', "supsetneqq;": '\U00002ACC', "supsim;": '\U00002AC8', "supsub;": '\U00002AD4', "supsup;": '\U00002AD6', "swArr;": '\U000021D9', "swarhk;": '\U00002926', "swarr;": '\U00002199', "swarrow;": '\U00002199', "swnwar;": '\U0000292A', "szlig;": '\U000000DF', "target;": '\U00002316', "tau;": '\U000003C4', "tbrk;": '\U000023B4', "tcaron;": '\U00000165', "tcedil;": '\U00000163', "tcy;": '\U00000442', "tdot;": '\U000020DB', "telrec;": '\U00002315', "tfr;": '\U0001D531', "there4;": '\U00002234', "therefore;": '\U00002234', "theta;": '\U000003B8', "thetasym;": '\U000003D1', "thetav;": '\U000003D1', "thickapprox;": '\U00002248', "thicksim;": '\U0000223C', "thinsp;": '\U00002009', "thkap;": '\U00002248', "thksim;": '\U0000223C', "thorn;": '\U000000FE', "tilde;": '\U000002DC', "times;": '\U000000D7', "timesb;": '\U000022A0', "timesbar;": '\U00002A31', "timesd;": '\U00002A30', "tint;": '\U0000222D', "toea;": '\U00002928', "top;": '\U000022A4', "topbot;": '\U00002336', "topcir;": '\U00002AF1', "topf;": '\U0001D565', "topfork;": '\U00002ADA', "tosa;": '\U00002929', "tprime;": '\U00002034', "trade;": '\U00002122', "triangle;": '\U000025B5', "triangledown;": '\U000025BF', "triangleleft;": '\U000025C3', "trianglelefteq;": '\U000022B4', "triangleq;": '\U0000225C', "triangleright;": '\U000025B9', "trianglerighteq;": '\U000022B5', "tridot;": '\U000025EC', "trie;": '\U0000225C', "triminus;": '\U00002A3A', "triplus;": '\U00002A39', "trisb;": '\U000029CD', "tritime;": '\U00002A3B', "trpezium;": '\U000023E2', "tscr;": '\U0001D4C9', "tscy;": '\U00000446', "tshcy;": '\U0000045B', "tstrok;": '\U00000167', "twixt;": '\U0000226C', "twoheadleftarrow;": '\U0000219E', "twoheadrightarrow;": '\U000021A0', "uArr;": '\U000021D1', "uHar;": '\U00002963', "uacute;": '\U000000FA', "uarr;": '\U00002191', "ubrcy;": '\U0000045E', "ubreve;": '\U0000016D', "ucirc;": '\U000000FB', "ucy;": '\U00000443', "udarr;": '\U000021C5', "udblac;": '\U00000171', "udhar;": '\U0000296E', "ufisht;": '\U0000297E', "ufr;": '\U0001D532', "ugrave;": '\U000000F9', "uharl;": '\U000021BF', "uharr;": '\U000021BE', "uhblk;": '\U00002580', "ulcorn;": '\U0000231C', "ulcorner;": '\U0000231C', "ulcrop;": '\U0000230F', "ultri;": '\U000025F8', "umacr;": '\U0000016B', "uml;": '\U000000A8', "uogon;": '\U00000173', "uopf;": '\U0001D566', "uparrow;": '\U00002191', "updownarrow;": '\U00002195', "upharpoonleft;": '\U000021BF', "upharpoonright;": '\U000021BE', "uplus;": '\U0000228E', "upsi;": '\U000003C5', "upsih;": '\U000003D2', "upsilon;": '\U000003C5', "upuparrows;": '\U000021C8', "urcorn;": '\U0000231D', "urcorner;": '\U0000231D', "urcrop;": '\U0000230E', "uring;": '\U0000016F', "urtri;": '\U000025F9', "uscr;": '\U0001D4CA', "utdot;": '\U000022F0', "utilde;": '\U00000169', "utri;": '\U000025B5', "utrif;": '\U000025B4', "uuarr;": '\U000021C8', "uuml;": '\U000000FC', "uwangle;": '\U000029A7', "vArr;": '\U000021D5', "vBar;": '\U00002AE8', "vBarv;": '\U00002AE9', "vDash;": '\U000022A8', "vangrt;": '\U0000299C', "varepsilon;": '\U000003F5', "varkappa;": '\U000003F0', "varnothing;": '\U00002205', "varphi;": '\U000003D5', "varpi;": '\U000003D6', "varpropto;": '\U0000221D', "varr;": '\U00002195', "varrho;": '\U000003F1', "varsigma;": '\U000003C2', "vartheta;": '\U000003D1', "vartriangleleft;": '\U000022B2', "vartriangleright;": '\U000022B3', "vcy;": '\U00000432', "vdash;": '\U000022A2', "vee;": '\U00002228', "veebar;": '\U000022BB', "veeeq;": '\U0000225A', "vellip;": '\U000022EE', "verbar;": '\U0000007C', "vert;": '\U0000007C', "vfr;": '\U0001D533', "vltri;": '\U000022B2', "vopf;": '\U0001D567', "vprop;": '\U0000221D', "vrtri;": '\U000022B3', "vscr;": '\U0001D4CB', "vzigzag;": '\U0000299A', "wcirc;": '\U00000175', "wedbar;": '\U00002A5F', "wedge;": '\U00002227', "wedgeq;": '\U00002259', "weierp;": '\U00002118', "wfr;": '\U0001D534', "wopf;": '\U0001D568', "wp;": '\U00002118', "wr;": '\U00002240', "wreath;": '\U00002240', "wscr;": '\U0001D4CC', "xcap;": '\U000022C2', "xcirc;": '\U000025EF', "xcup;": '\U000022C3', "xdtri;": '\U000025BD', "xfr;": '\U0001D535', "xhArr;": '\U000027FA', "xharr;": '\U000027F7', "xi;": '\U000003BE', "xlArr;": '\U000027F8', "xlarr;": '\U000027F5', "xmap;": '\U000027FC', "xnis;": '\U000022FB', "xodot;": '\U00002A00', "xopf;": '\U0001D569', "xoplus;": '\U00002A01', "xotime;": '\U00002A02', "xrArr;": '\U000027F9', "xrarr;": '\U000027F6', "xscr;": '\U0001D4CD', "xsqcup;": '\U00002A06', "xuplus;": '\U00002A04', "xutri;": '\U000025B3', "xvee;": '\U000022C1', "xwedge;": '\U000022C0', "yacute;": '\U000000FD', "yacy;": '\U0000044F', "ycirc;": '\U00000177', "ycy;": '\U0000044B', "yen;": '\U000000A5', "yfr;": '\U0001D536', "yicy;": '\U00000457', "yopf;": '\U0001D56A', "yscr;": '\U0001D4CE', "yucy;": '\U0000044E', "yuml;": '\U000000FF', "zacute;": '\U0000017A', "zcaron;": '\U0000017E', "zcy;": '\U00000437', "zdot;": '\U0000017C', "zeetrf;": '\U00002128', "zeta;": '\U000003B6', "zfr;": '\U0001D537', "zhcy;": '\U00000436', "zigrarr;": '\U000021DD', "zopf;": '\U0001D56B', "zscr;": '\U0001D4CF', "zwj;": '\U0000200D', "zwnj;": '\U0000200C', "AElig": '\U000000C6', "AMP": '\U00000026', "Aacute": '\U000000C1', "Acirc": '\U000000C2', "Agrave": '\U000000C0', "Aring": '\U000000C5', "Atilde": '\U000000C3', "Auml": '\U000000C4', "COPY": '\U000000A9', "Ccedil": '\U000000C7', "ETH": '\U000000D0', "Eacute": '\U000000C9', "Ecirc": '\U000000CA', "Egrave": '\U000000C8', "Euml": '\U000000CB', "GT": '\U0000003E', "Iacute": '\U000000CD', "Icirc": '\U000000CE', "Igrave": '\U000000CC', "Iuml": '\U000000CF', "LT": '\U0000003C', "Ntilde": '\U000000D1', "Oacute": '\U000000D3', "Ocirc": '\U000000D4', "Ograve": '\U000000D2', "Oslash": '\U000000D8', "Otilde": '\U000000D5', "Ouml": '\U000000D6', "QUOT": '\U00000022', "REG": '\U000000AE', "THORN": '\U000000DE', "Uacute": '\U000000DA', "Ucirc": '\U000000DB', "Ugrave": '\U000000D9', "Uuml": '\U000000DC', "Yacute": '\U000000DD', "aacute": '\U000000E1', "acirc": '\U000000E2', "acute": '\U000000B4', "aelig": '\U000000E6', "agrave": '\U000000E0', "amp": '\U00000026', "aring": '\U000000E5', "atilde": '\U000000E3', "auml": '\U000000E4', "brvbar": '\U000000A6', "ccedil": '\U000000E7', "cedil": '\U000000B8', "cent": '\U000000A2', "copy": '\U000000A9', "curren": '\U000000A4', "deg": '\U000000B0', "divide": '\U000000F7', "eacute": '\U000000E9', "ecirc": '\U000000EA', "egrave": '\U000000E8', "eth": '\U000000F0', "euml": '\U000000EB', "frac12": '\U000000BD', "frac14": '\U000000BC', "frac34": '\U000000BE', "gt": '\U0000003E', "iacute": '\U000000ED', "icirc": '\U000000EE', "iexcl": '\U000000A1', "igrave": '\U000000EC', "iquest": '\U000000BF', "iuml": '\U000000EF', "laquo": '\U000000AB', "lt": '\U0000003C', "macr": '\U000000AF', "micro": '\U000000B5', "middot": '\U000000B7', "nbsp": '\U000000A0', "not": '\U000000AC', "ntilde": '\U000000F1', "oacute": '\U000000F3', "ocirc": '\U000000F4', "ograve": '\U000000F2', "ordf": '\U000000AA', "ordm": '\U000000BA', "oslash": '\U000000F8', "otilde": '\U000000F5', "ouml": '\U000000F6', "para": '\U000000B6', "plusmn": '\U000000B1', "pound": '\U000000A3', "quot": '\U00000022', "raquo": '\U000000BB', "reg": '\U000000AE', "sect": '\U000000A7', "shy": '\U000000AD', "sup1": '\U000000B9', "sup2": '\U000000B2', "sup3": '\U000000B3', "szlig": '\U000000DF', "thorn": '\U000000FE', "times": '\U000000D7', "uacute": '\U000000FA', "ucirc": '\U000000FB', "ugrave": '\U000000F9', "uml": '\U000000A8', "uuml": '\U000000FC', "yacute": '\U000000FD', "yen": '\U000000A5', "yuml": '\U000000FF', } // HTML entities that are two unicode codepoints. var entity2 = map[string][2]rune{ // TODO(nigeltao): Handle replacements that are wider than their names. // "nLt;": {'\u226A', '\u20D2'}, // "nGt;": {'\u226B', '\u20D2'}, "NotEqualTilde;": {'\u2242', '\u0338'}, "NotGreaterFullEqual;": {'\u2267', '\u0338'}, "NotGreaterGreater;": {'\u226B', '\u0338'}, "NotGreaterSlantEqual;": {'\u2A7E', '\u0338'}, "NotHumpDownHump;": {'\u224E', '\u0338'}, "NotHumpEqual;": {'\u224F', '\u0338'}, "NotLeftTriangleBar;": {'\u29CF', '\u0338'}, "NotLessLess;": {'\u226A', '\u0338'}, "NotLessSlantEqual;": {'\u2A7D', '\u0338'}, "NotNestedGreaterGreater;": {'\u2AA2', '\u0338'}, "NotNestedLessLess;": {'\u2AA1', '\u0338'}, "NotPrecedesEqual;": {'\u2AAF', '\u0338'}, "NotRightTriangleBar;": {'\u29D0', '\u0338'}, "NotSquareSubset;": {'\u228F', '\u0338'}, "NotSquareSuperset;": {'\u2290', '\u0338'}, "NotSubset;": {'\u2282', '\u20D2'}, "NotSucceedsEqual;": {'\u2AB0', '\u0338'}, "NotSucceedsTilde;": {'\u227F', '\u0338'}, "NotSuperset;": {'\u2283', '\u20D2'}, "ThickSpace;": {'\u205F', '\u200A'}, "acE;": {'\u223E', '\u0333'}, "bne;": {'\u003D', '\u20E5'}, "bnequiv;": {'\u2261', '\u20E5'}, "caps;": {'\u2229', '\uFE00'}, "cups;": {'\u222A', '\uFE00'}, "fjlig;": {'\u0066', '\u006A'}, "gesl;": {'\u22DB', '\uFE00'}, "gvertneqq;": {'\u2269', '\uFE00'}, "gvnE;": {'\u2269', '\uFE00'}, "lates;": {'\u2AAD', '\uFE00'}, "lesg;": {'\u22DA', '\uFE00'}, "lvertneqq;": {'\u2268', '\uFE00'}, "lvnE;": {'\u2268', '\uFE00'}, "nGg;": {'\u22D9', '\u0338'}, "nGtv;": {'\u226B', '\u0338'}, "nLl;": {'\u22D8', '\u0338'}, "nLtv;": {'\u226A', '\u0338'}, "nang;": {'\u2220', '\u20D2'}, "napE;": {'\u2A70', '\u0338'}, "napid;": {'\u224B', '\u0338'}, "nbump;": {'\u224E', '\u0338'}, "nbumpe;": {'\u224F', '\u0338'}, "ncongdot;": {'\u2A6D', '\u0338'}, "nedot;": {'\u2250', '\u0338'}, "nesim;": {'\u2242', '\u0338'}, "ngE;": {'\u2267', '\u0338'}, "ngeqq;": {'\u2267', '\u0338'}, "ngeqslant;": {'\u2A7E', '\u0338'}, "nges;": {'\u2A7E', '\u0338'}, "nlE;": {'\u2266', '\u0338'}, "nleqq;": {'\u2266', '\u0338'}, "nleqslant;": {'\u2A7D', '\u0338'}, "nles;": {'\u2A7D', '\u0338'}, "notinE;": {'\u22F9', '\u0338'}, "notindot;": {'\u22F5', '\u0338'}, "nparsl;": {'\u2AFD', '\u20E5'}, "npart;": {'\u2202', '\u0338'}, "npre;": {'\u2AAF', '\u0338'}, "npreceq;": {'\u2AAF', '\u0338'}, "nrarrc;": {'\u2933', '\u0338'}, "nrarrw;": {'\u219D', '\u0338'}, "nsce;": {'\u2AB0', '\u0338'}, "nsubE;": {'\u2AC5', '\u0338'}, "nsubset;": {'\u2282', '\u20D2'}, "nsubseteqq;": {'\u2AC5', '\u0338'}, "nsucceq;": {'\u2AB0', '\u0338'}, "nsupE;": {'\u2AC6', '\u0338'}, "nsupset;": {'\u2283', '\u20D2'}, "nsupseteqq;": {'\u2AC6', '\u0338'}, "nvap;": {'\u224D', '\u20D2'}, "nvge;": {'\u2265', '\u20D2'}, "nvgt;": {'\u003E', '\u20D2'}, "nvle;": {'\u2264', '\u20D2'}, "nvlt;": {'\u003C', '\u20D2'}, "nvltrie;": {'\u22B4', '\u20D2'}, "nvrtrie;": {'\u22B5', '\u20D2'}, "nvsim;": {'\u223C', '\u20D2'}, "race;": {'\u223D', '\u0331'}, "smtes;": {'\u2AAC', '\uFE00'}, "sqcaps;": {'\u2293', '\uFE00'}, "sqcups;": {'\u2294', '\uFE00'}, "varsubsetneq;": {'\u228A', '\uFE00'}, "varsubsetneqq;": {'\u2ACB', '\uFE00'}, "varsupsetneq;": {'\u228B', '\uFE00'}, "varsupsetneqq;": {'\u2ACC', '\uFE00'}, "vnsub;": {'\u2282', '\u20D2'}, "vnsup;": {'\u2283', '\u20D2'}, "vsubnE;": {'\u2ACB', '\uFE00'}, "vsubne;": {'\u228A', '\uFE00'}, "vsupnE;": {'\u2ACC', '\uFE00'}, "vsupne;": {'\u228B', '\uFE00'}, } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/����������������������������������������0000755�0000153�0000161�00000000000�12321735652�023407� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/go1.html��������������������������������0000644�0000153�0000161�00000230523�12321735652�024770� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Go 1 Release Notes - The Go Programming Language</title> <link type="text/css" rel="stylesheet" href="/doc/style.css"> <script type="text/javascript" src="/doc/godocs.js"></script> <link rel="search" type="application/opensearchdescription+xml" title="godoc" href="/opensearch.xml" /> <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(["_setAccount", "UA-11222381-2"]); _gaq.push(["_trackPageview"]); </script> </head> <body> <div id="topbar"><div class="container wide"> <form method="GET" action="/search"> <div id="menu"> <a href="/doc/">Documents</a> <a href="/ref/">References</a> <a href="/pkg/">Packages</a> <a href="/project/">The Project</a> <a href="/help/">Help</a> <input type="text" id="search" name="q" class="inactive" value="Search"> </div> <div id="heading"><a href="/">The Go Programming Language</a></div> </form> </div></div> <div id="page" class="wide"> <div id="plusone"><g:plusone size="small" annotation="none"></g:plusone></div> <h1>Go 1 Release Notes</h1> <div id="nav"></div> <h2 id="introduction">Introduction to Go 1</h2> <p> Go version 1, Go 1 for short, defines a language and a set of core libraries that provide a stable foundation for creating reliable products, projects, and publications. </p> <p> The driving motivation for Go 1 is stability for its users. People should be able to write Go programs and expect that they will continue to compile and run without change, on a time scale of years, including in production environments such as Google App Engine. Similarly, people should be able to write books about Go, be able to say which version of Go the book is describing, and have that version number still be meaningful much later. </p> <p> Code that compiles in Go 1 should, with few exceptions, continue to compile and run throughout the lifetime of that version, even as we issue updates and bug fixes such as Go version 1.1, 1.2, and so on. Other than critical fixes, changes made to the language and library for subsequent releases of Go 1 may add functionality but will not break existing Go 1 programs. <a href="go1compat.html">The Go 1 compatibility document</a> explains the compatibility guidelines in more detail. </p> <p> Go 1 is a representation of Go as it used today, not a wholesale rethinking of the language. We avoided designing new features and instead focused on cleaning up problems and inconsistencies and improving portability. There are a number changes to the Go language and packages that we had considered for some time and prototyped but not released primarily because they are significant and backwards-incompatible. Go 1 was an opportunity to get them out, which is helpful for the long term, but also means that Go 1 introduces incompatibilities for old programs. Fortunately, the <code>go</code> <code>fix</code> tool can automate much of the work needed to bring programs up to the Go 1 standard. </p> <p> This document outlines the major changes in Go 1 that will affect programmers updating existing code; its reference point is the prior release, r60 (tagged as r60.3). It also explains how to update code from r60 to run under Go 1. </p> <h2 id="language">Changes to the language</h2> <h3 id="append">Append</h3> <p> The <code>append</code> predeclared variadic function makes it easy to grow a slice by adding elements to the end. A common use is to add bytes to the end of a byte slice when generating output. However, <code>append</code> did not provide a way to append a string to a <code>[]byte</code>, which is another common case. </p> <pre><!--{{code "/doc/progs/go1.go" `/greeting := ..byte/` `/append.*hello/`}} --> greeting := []byte{} greeting = append(greeting, []byte(&#34;hello &#34;)...)</pre> <p> By analogy with the similar property of <code>copy</code>, Go 1 permits a string to be appended (byte-wise) directly to a byte slice, reducing the friction between strings and byte slices. The conversion is no longer necessary: </p> <pre><!--{{code "/doc/progs/go1.go" `/append.*world/`}} --> greeting = append(greeting, &#34;world&#34;...)</pre> <p> <em>Updating</em>: This is a new feature, so existing code needs no changes. </p> <h3 id="close">Close</h3> <p> The <code>close</code> predeclared function provides a mechanism for a sender to signal that no more values will be sent. It is important to the implementation of <code>for</code> <code>range</code> loops over channels and is helpful in other situations. Partly by design and partly because of race conditions that can occur otherwise, it is intended for use only by the goroutine sending on the channel, not by the goroutine receiving data. However, before Go 1 there was no compile-time checking that <code>close</code> was being used correctly. </p> <p> To close this gap, at least in part, Go 1 disallows <code>close</code> on receive-only channels. Attempting to close such a channel is a compile-time error. </p> <pre> var c chan int var csend chan&lt;- int = c var crecv &lt;-chan int = c close(c) // legal close(csend) // legal close(crecv) // illegal </pre> <p> <em>Updating</em>: Existing code that attempts to close a receive-only channel was erroneous even before Go 1 and should be fixed. The compiler will now reject such code. </p> <h3 id="literals">Composite literals</h3> <p> In Go 1, a composite literal of array, slice, or map type can elide the type specification for the elements' initializers if they are of pointer type. All four of the initializations in this example are legal; the last one was illegal before Go 1. </p> <pre><!--{{code "/doc/progs/go1.go" `/type Date struct/` `/STOP/`}} --> type Date struct { month string day int } <span class="comment">// Struct values, fully qualified; always legal.</span> holiday1 := []Date{ Date{&#34;Feb&#34;, 14}, Date{&#34;Nov&#34;, 11}, Date{&#34;Dec&#34;, 25}, } <span class="comment">// Struct values, type name elided; always legal.</span> holiday2 := []Date{ {&#34;Feb&#34;, 14}, {&#34;Nov&#34;, 11}, {&#34;Dec&#34;, 25}, } <span class="comment">// Pointers, fully qualified, always legal.</span> holiday3 := []*Date{ &amp;Date{&#34;Feb&#34;, 14}, &amp;Date{&#34;Nov&#34;, 11}, &amp;Date{&#34;Dec&#34;, 25}, } <span class="comment">// Pointers, type name elided; legal in Go 1.</span> holiday4 := []*Date{ {&#34;Feb&#34;, 14}, {&#34;Nov&#34;, 11}, {&#34;Dec&#34;, 25}, }</pre> <p> <em>Updating</em>: This change has no effect on existing code, but the command <code>gofmt</code> <code>-s</code> applied to existing source will, among other things, elide explicit element types wherever permitted. </p> <h3 id="init">Goroutines during init</h3> <p> The old language defined that <code>go</code> statements executed during initialization created goroutines but that they did not begin to run until initialization of the entire program was complete. This introduced clumsiness in many places and, in effect, limited the utility of the <code>init</code> construct: if it was possible for another package to use the library during initialization, the library was forced to avoid goroutines. This design was done for reasons of simplicity and safety but, as our confidence in the language grew, it seemed unnecessary. Running goroutines during initialization is no more complex or unsafe than running them during normal execution. </p> <p> In Go 1, code that uses goroutines can be called from <code>init</code> routines and global initialization expressions without introducing a deadlock. </p> <pre><!--{{code "/doc/progs/go1.go" `/PackageGlobal/` `/^}/`}} -->var PackageGlobal int func init() { c := make(chan int) go initializationFunction(c) PackageGlobal = &lt;-c }</pre> <p> <em>Updating</em>: This is a new feature, so existing code needs no changes, although it's possible that code that depends on goroutines not starting before <code>main</code> will break. There was no such code in the standard repository. </p> <h3 id="rune">The rune type</h3> <p> The language spec allows the <code>int</code> type to be 32 or 64 bits wide, but current implementations set <code>int</code> to 32 bits even on 64-bit platforms. It would be preferable to have <code>int</code> be 64 bits on 64-bit platforms. (There are important consequences for indexing large slices.) However, this change would waste space when processing Unicode characters with the old language because the <code>int</code> type was also used to hold Unicode code points: each code point would waste an extra 32 bits of storage if <code>int</code> grew from 32 bits to 64. </p> <p> To make changing to 64-bit <code>int</code> feasible, Go 1 introduces a new basic type, <code>rune</code>, to represent individual Unicode code points. It is an alias for <code>int32</code>, analogous to <code>byte</code> as an alias for <code>uint8</code>. </p> <p> Character literals such as <code>'a'</code>, <code>'語'</code>, and <code>'\u0345'</code> now have default type <code>rune</code>, analogous to <code>1.0</code> having default type <code>float64</code>. A variable initialized to a character constant will therefore have type <code>rune</code> unless otherwise specified. </p> <p> Libraries have been updated to use <code>rune</code> rather than <code>int</code> when appropriate. For instance, the functions <code>unicode.ToLower</code> and relatives now take and return a <code>rune</code>. </p> <pre><!--{{code "/doc/progs/go1.go" `/STARTRUNE/` `/ENDRUNE/`}} --> delta := &#39;δ&#39; <span class="comment">// delta has type rune.</span> var DELTA rune DELTA = unicode.ToUpper(delta) epsilon := unicode.ToLower(DELTA + 1) if epsilon != &#39;δ&#39;+1 { log.Fatal(&#34;inconsistent casing for Greek&#34;) }</pre> <p> <em>Updating</em>: Most source code will be unaffected by this because the type inference from <code>:=</code> initializers introduces the new type silently, and it propagates from there. Some code may get type errors that a trivial conversion will resolve. </p> <h3 id="error">The error type</h3> <p> Go 1 introduces a new built-in type, <code>error</code>, which has the following definition: </p> <pre> type error interface { Error() string } </pre> <p> Since the consequences of this type are all in the package library, it is discussed <a href="#errors">below</a>. </p> <h3 id="delete">Deleting from maps</h3> <p> In the old language, to delete the entry with key <code>k</code> from map <code>m</code>, one wrote the statement, </p> <pre> m[k] = value, false </pre> <p> This syntax was a peculiar special case, the only two-to-one assignment. It required passing a value (usually ignored) that is evaluated but discarded, plus a boolean that was nearly always the constant <code>false</code>. It did the job but was odd and a point of contention. </p> <p> In Go 1, that syntax has gone; instead there is a new built-in function, <code>delete</code>. The call </p> <pre><!--{{code "/doc/progs/go1.go" `/delete\(m, k\)/`}} --> delete(m, k)</pre> <p> will delete the map entry retrieved by the expression <code>m[k]</code>. There is no return value. Deleting a non-existent entry is a no-op. </p> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will convert expressions of the form <code>m[k] = value, false</code> into <code>delete(m, k)</code> when it is clear that the ignored value can be safely discarded from the program and <code>false</code> refers to the predefined boolean constant. The fix tool will flag other uses of the syntax for inspection by the programmer. </p> <h3 id="iteration">Iterating in maps</h3> <p> The old language specification did not define the order of iteration for maps, and in practice it differed across hardware platforms. This caused tests that iterated over maps to be fragile and non-portable, with the unpleasant property that a test might always pass on one machine but break on another. </p> <p> In Go 1, the order in which elements are visited when iterating over a map using a <code>for</code> <code>range</code> statement is defined to be unpredictable, even if the same loop is run multiple times with the same map. Code should not assume that the elements are visited in any particular order. </p> <p> This change means that code that depends on iteration order is very likely to break early and be fixed long before it becomes a problem. Just as important, it allows the map implementation to ensure better map balancing even when programs are using range loops to select an element from a map. </p> <pre><!--{{code "/doc/progs/go1.go" `/Sunday/` `/^ }/`}} --> m := map[string]int{&#34;Sunday&#34;: 0, &#34;Monday&#34;: 1} for name, value := range m { <span class="comment">// This loop should not assume Sunday will be visited first.</span> f(name, value) }</pre> <p> <em>Updating</em>: This is one change where tools cannot help. Most existing code will be unaffected, but some programs may break or misbehave; we recommend manual checking of all range statements over maps to verify they do not depend on iteration order. There were a few such examples in the standard repository; they have been fixed. Note that it was already incorrect to depend on the iteration order, which was unspecified. This change codifies the unpredictability. </p> <h3 id="multiple_assignment">Multiple assignment</h3> <p> The language specification has long guaranteed that in assignments the right-hand-side expressions are all evaluated before any left-hand-side expressions are assigned. To guarantee predictable behavior, Go 1 refines the specification further. </p> <p> If the left-hand side of the assignment statement contains expressions that require evaluation, such as function calls or array indexing operations, these will all be done using the usual left-to-right rule before any variables are assigned their value. Once everything is evaluated, the actual assignments proceed in left-to-right order. </p> <p> These examples illustrate the behavior. </p> <pre><!--{{code "/doc/progs/go1.go" `/sa :=/` `/then sc.0. = 2/`}} --> sa := []int{1, 2, 3} i := 0 i, sa[i] = 1, 2 <span class="comment">// sets i = 1, sa[0] = 2</span> sb := []int{1, 2, 3} j := 0 sb[j], j = 2, 1 <span class="comment">// sets sb[0] = 2, j = 1</span> sc := []int{1, 2, 3} sc[0], sc[0] = 1, 2 <span class="comment">// sets sc[0] = 1, then sc[0] = 2 (so sc[0] = 2 at end)</span></pre> <p> <em>Updating</em>: This is one change where tools cannot help, but breakage is unlikely. No code in the standard repository was broken by this change, and code that depended on the previous unspecified behavior was already incorrect. </p> <h3 id="shadowing">Returns and shadowed variables</h3> <p> A common mistake is to use <code>return</code> (without arguments) after an assignment to a variable that has the same name as a result variable but is not the same variable. This situation is called <em>shadowing</em>: the result variable has been shadowed by another variable with the same name declared in an inner scope. </p> <p> In functions with named return values, the Go 1 compilers disallow return statements without arguments if any of the named return values is shadowed at the point of the return statement. (It isn't part of the specification, because this is one area we are still exploring; the situation is analogous to the compilers rejecting functions that do not end with an explicit return statement.) </p> <p> This function implicitly returns a shadowed return value and will be rejected by the compiler: </p> <pre> func Bug() (i, j, k int) { for i = 0; i &lt; 5; i++ { for j := 0; j &lt; 5; j++ { // Redeclares j. k += i*j if k > 100 { return // Rejected: j is shadowed here. } } } return // OK: j is not shadowed here. } </pre> <p> <em>Updating</em>: Code that shadows return values in this way will be rejected by the compiler and will need to be fixed by hand. The few cases that arose in the standard repository were mostly bugs. </p> <h3 id="unexported">Copying structs with unexported fields</h3> <p> The old language did not allow a package to make a copy of a struct value containing unexported fields belonging to a different package. There was, however, a required exception for a method receiver; also, the implementations of <code>copy</code> and <code>append</code> have never honored the restriction. </p> <p> Go 1 will allow packages to copy struct values containing unexported fields from other packages. Besides resolving the inconsistency, this change admits a new kind of API: a package can return an opaque value without resorting to a pointer or interface. The new implementations of <code>time.Time</code> and <code>reflect.Value</code> are examples of types taking advantage of this new property. </p> <p> As an example, if package <code>p</code> includes the definitions, </p> <pre> type Struct struct { Public int secret int } func NewStruct(a int) Struct { // Note: not a pointer. return Struct{a, f(a)} } func (s Struct) String() string { return fmt.Sprintf("{%d (secret %d)}", s.Public, s.secret) } </pre> <p> a package that imports <code>p</code> can assign and copy values of type <code>p.Struct</code> at will. Behind the scenes the unexported fields will be assigned and copied just as if they were exported, but the client code will never be aware of them. The code </p> <pre> import "p" myStruct := p.NewStruct(23) copyOfMyStruct := myStruct fmt.Println(myStruct, copyOfMyStruct) </pre> <p> will show that the secret field of the struct has been copied to the new value. </p> <p> <em>Updating</em>: This is a new feature, so existing code needs no changes. </p> <h3 id="equality">Equality</h3> <p> Before Go 1, the language did not define equality on struct and array values. This meant, among other things, that structs and arrays could not be used as map keys. On the other hand, Go did define equality on function and map values. Function equality was problematic in the presence of closures (when are two closures equal?) while map equality compared pointers, not the maps' content, which was usually not what the user would want. </p> <p> Go 1 addressed these issues. First, structs and arrays can be compared for equality and inequality (<code>==</code> and <code>!=</code>), and therefore be used as map keys, provided they are composed from elements for which equality is also defined, using element-wise comparison. </p> <pre><!--{{code "/doc/progs/go1.go" `/type Day struct/` `/Printf/`}} --> type Day struct { long string short string } Christmas := Day{&#34;Christmas&#34;, &#34;XMas&#34;} Thanksgiving := Day{&#34;Thanksgiving&#34;, &#34;Turkey&#34;} holiday := map[Day]bool{ Christmas: true, Thanksgiving: true, } fmt.Printf(&#34;Christmas is a holiday: %t\n&#34;, holiday[Christmas])</pre> <p> Second, Go 1 removes the definition of equality for function values, except for comparison with <code>nil</code>. Finally, map equality is gone too, also except for comparison with <code>nil</code>. </p> <p> Note that equality is still undefined for slices, for which the calculation is in general infeasible. Also note that the ordered comparison operators (<code>&lt;</code> <code>&lt;=</code> <code>&gt;</code> <code>&gt;=</code>) are still undefined for structs and arrays. <p> <em>Updating</em>: Struct and array equality is a new feature, so existing code needs no changes. Existing code that depends on function or map equality will be rejected by the compiler and will need to be fixed by hand. Few programs will be affected, but the fix may require some redesign. </p> <h2 id="packages">The package hierarchy</h2> <p> Go 1 addresses many deficiencies in the old standard library and cleans up a number of packages, making them more internally consistent and portable. </p> <p> This section describes how the packages have been rearranged in Go 1. Some have moved, some have been renamed, some have been deleted. New packages are described in later sections. </p> <h3 id="hierarchy">The package hierarchy</h3> <p> Go 1 has a rearranged package hierarchy that groups related items into subdirectories. For instance, <code>utf8</code> and <code>utf16</code> now occupy subdirectories of <code>unicode</code>. Also, <a href="#subrepo">some packages</a> have moved into subrepositories of <a href="http://code.google.com/p/go"><code>code.google.com/p/go</code></a> while <a href="#deleted">others</a> have been deleted outright. </p> <table class="codetable" frame="border" summary="Moved packages"> <colgroup align="left" width="60%"></colgroup> <colgroup align="left" width="40%"></colgroup> <tr> <th align="left">Old path</th> <th align="left">New path</th> </tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>asn1</td> <td>encoding/asn1</td></tr> <tr><td>csv</td> <td>encoding/csv</td></tr> <tr><td>gob</td> <td>encoding/gob</td></tr> <tr><td>json</td> <td>encoding/json</td></tr> <tr><td>xml</td> <td>encoding/xml</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>exp/template/html</td> <td>html/template</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>big</td> <td>math/big</td></tr> <tr><td>cmath</td> <td>math/cmplx</td></tr> <tr><td>rand</td> <td>math/rand</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>http</td> <td>net/http</td></tr> <tr><td>http/cgi</td> <td>net/http/cgi</td></tr> <tr><td>http/fcgi</td> <td>net/http/fcgi</td></tr> <tr><td>http/httptest</td> <td>net/http/httptest</td></tr> <tr><td>http/pprof</td> <td>net/http/pprof</td></tr> <tr><td>mail</td> <td>net/mail</td></tr> <tr><td>rpc</td> <td>net/rpc</td></tr> <tr><td>rpc/jsonrpc</td> <td>net/rpc/jsonrpc</td></tr> <tr><td>smtp</td> <td>net/smtp</td></tr> <tr><td>url</td> <td>net/url</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>exec</td> <td>os/exec</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>scanner</td> <td>text/scanner</td></tr> <tr><td>tabwriter</td> <td>text/tabwriter</td></tr> <tr><td>template</td> <td>text/template</td></tr> <tr><td>template/parse</td> <td>text/template/parse</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>utf8</td> <td>unicode/utf8</td></tr> <tr><td>utf16</td> <td>unicode/utf16</td></tr> </table> <p> Note that the package names for the old <code>cmath</code> and <code>exp/template/html</code> packages have changed to <code>cmplx</code> and <code>template</code>. </p> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update all imports and package renames for packages that remain inside the standard repository. Programs that import packages that are no longer in the standard repository will need to be edited by hand. </p> <h3 id="exp">The package tree exp</h3> <p> Because they are not standardized, the packages under the <code>exp</code> directory will not be available in the standard Go 1 release distributions, although they will be available in source code form in <a href="http://code.google.com/p/go/">the repository</a> for developers who wish to use them. </p> <p> Several packages have moved under <code>exp</code> at the time of Go 1's release: </p> <ul> <li><code>ebnf</code></li> <li><code>html</code><sup>&#8224;</sup></li> <li><code>go/types</code></li> </ul> <p> (<sup>&#8224;</sup>The <code>EscapeString</code> and <code>UnescapeString</code> types remain in package <code>html</code>.) </p> <p> All these packages are available under the same names, with the prefix <code>exp/</code>: <code>exp/ebnf</code> etc. </p> <p> Also, the <code>utf8.String</code> type has been moved to its own package, <code>exp/utf8string</code>. </p> <p> Finally, the <code>gotype</code> command now resides in <code>exp/gotype</code>, while <code>ebnflint</code> is now in <code>exp/ebnflint</code>. If they are installed, they now reside in <code>$GOROOT/bin/tool</code>. </p> <p> <em>Updating</em>: Code that uses packages in <code>exp</code> will need to be updated by hand, or else compiled from an installation that has <code>exp</code> available. The <code>go</code> <code>fix</code> tool or the compiler will complain about such uses. </p> <h3 id="old">The package tree old</h3> <p> Because they are deprecated, the packages under the <code>old</code> directory will not be available in the standard Go 1 release distributions, although they will be available in source code form for developers who wish to use them. </p> <p> The packages in their new locations are: </p> <ul> <li><code>old/netchan</code></li> <li><code>old/regexp</code></li> <li><code>old/template</code></li> </ul> <p> <em>Updating</em>: Code that uses packages now in <code>old</code> will need to be updated by hand, or else compiled from an installation that has <code>old</code> available. The <code>go</code> <code>fix</code> tool will warn about such uses. </p> <h3 id="deleted">Deleted packages</h3> <p> Go 1 deletes several packages outright: </p> <ul> <li><code>container/vector</code></li> <li><code>exp/datafmt</code></li> <li><code>go/typechecker</code></li> <li><code>try</code></li> </ul> <p> and also the command <code>gotry</code>. </p> <p> <em>Updating</em>: Code that uses <code>container/vector</code> should be updated to use slices directly. See <a href="http://code.google.com/p/go-wiki/wiki/SliceTricks">the Go Language Community Wiki</a> for some suggestions. Code that uses the other packages (there should be almost zero) will need to be rethought. </p> <h3 id="subrepo">Packages moving to subrepositories</h3> <p> Go 1 has moved a number of packages into other repositories, usually sub-repositories of <a href="http://code.google.com/p/go/">the main Go repository</a>. This table lists the old and new import paths: <table class="codetable" frame="border" summary="Sub-repositories"> <colgroup align="left" width="40%"></colgroup> <colgroup align="left" width="60%"></colgroup> <tr> <th align="left">Old</th> <th align="left">New</th> </tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>crypto/bcrypt</td> <td>code.google.com/p/go.crypto/bcrypt</tr> <tr><td>crypto/blowfish</td> <td>code.google.com/p/go.crypto/blowfish</tr> <tr><td>crypto/cast5</td> <td>code.google.com/p/go.crypto/cast5</tr> <tr><td>crypto/md4</td> <td>code.google.com/p/go.crypto/md4</tr> <tr><td>crypto/ocsp</td> <td>code.google.com/p/go.crypto/ocsp</tr> <tr><td>crypto/openpgp</td> <td>code.google.com/p/go.crypto/openpgp</tr> <tr><td>crypto/openpgp/armor</td> <td>code.google.com/p/go.crypto/openpgp/armor</tr> <tr><td>crypto/openpgp/elgamal</td> <td>code.google.com/p/go.crypto/openpgp/elgamal</tr> <tr><td>crypto/openpgp/errors</td> <td>code.google.com/p/go.crypto/openpgp/errors</tr> <tr><td>crypto/openpgp/packet</td> <td>code.google.com/p/go.crypto/openpgp/packet</tr> <tr><td>crypto/openpgp/s2k</td> <td>code.google.com/p/go.crypto/openpgp/s2k</tr> <tr><td>crypto/ripemd160</td> <td>code.google.com/p/go.crypto/ripemd160</tr> <tr><td>crypto/twofish</td> <td>code.google.com/p/go.crypto/twofish</tr> <tr><td>crypto/xtea</td> <td>code.google.com/p/go.crypto/xtea</tr> <tr><td>exp/ssh</td> <td>code.google.com/p/go.crypto/ssh</tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>image/bmp</td> <td>code.google.com/p/go.image/bmp</tr> <tr><td>image/tiff</td> <td>code.google.com/p/go.image/tiff</tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>net/dict</td> <td>code.google.com/p/go.net/dict</tr> <tr><td>net/websocket</td> <td>code.google.com/p/go.net/websocket</tr> <tr><td>exp/spdy</td> <td>code.google.com/p/go.net/spdy</tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>encoding/git85</td> <td>code.google.com/p/go.codereview/git85</tr> <tr><td>patch</td> <td>code.google.com/p/go.codereview/patch</tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>exp/wingui</td> <td>code.google.com/p/gowingui</tr> </table> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update imports of these packages to use the new import paths. Installations that depend on these packages will need to install them using a <code>go get</code> command. </p> <h2 id="major">Major changes to the library</h2> <p> This section describes significant changes to the core libraries, the ones that affect the most programs. </p> <h3 id="errors">The error type and errors package</h3> <p> The placement of <code>os.Error</code> in package <code>os</code> is mostly historical: errors first came up when implementing package <code>os</code>, and they seemed system-related at the time. Since then it has become clear that errors are more fundamental than the operating system. For example, it would be nice to use <code>Errors</code> in packages that <code>os</code> depends on, like <code>syscall</code>. Also, having <code>Error</code> in <code>os</code> introduces many dependencies on <code>os</code> that would otherwise not exist. </p> <p> Go 1 solves these problems by introducing a built-in <code>error</code> interface type and a separate <code>errors</code> package (analogous to <code>bytes</code> and <code>strings</code>) that contains utility functions. It replaces <code>os.NewError</code> with <a href="/pkg/errors/#New"><code>errors.New</code></a>, giving errors a more central place in the environment. </p> <p> So the widely-used <code>String</code> method does not cause accidental satisfaction of the <code>error</code> interface, the <code>error</code> interface uses instead the name <code>Error</code> for that method: </p> <pre> type error interface { Error() string } </pre> <p> The <code>fmt</code> library automatically invokes <code>Error</code>, as it already does for <code>String</code>, for easy printing of error values. </p> <pre><!--{{code "/doc/progs/go1.go" `/START ERROR EXAMPLE/` `/END ERROR EXAMPLE/`}} -->type SyntaxError struct { File string Line int Message string } func (se *SyntaxError) Error() string { return fmt.Sprintf(&#34;%s:%d: %s&#34;, se.File, se.Line, se.Message) }</pre> <p> All standard packages have been updated to use the new interface; the old <code>os.Error</code> is gone. </p> <p> A new package, <a href="/pkg/errors/"><code>errors</code></a>, contains the function </p> <pre> func New(text string) error </pre> <p> to turn a string into an error. It replaces the old <code>os.NewError</code>. </p> <pre><!--{{code "/doc/progs/go1.go" `/ErrSyntax/`}} --> var ErrSyntax = errors.New(&#34;syntax error&#34;)</pre> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update almost all code affected by the change. Code that defines error types with a <code>String</code> method will need to be updated by hand to rename the methods to <code>Error</code>. </p> <h3 id="errno">System call errors</h3> <p> The old <code>syscall</code> package, which predated <code>os.Error</code> (and just about everything else), returned errors as <code>int</code> values. In turn, the <code>os</code> package forwarded many of these errors, such as <code>EINVAL</code>, but using a different set of errors on each platform. This behavior was unpleasant and unportable. </p> <p> In Go 1, the <a href="/pkg/syscall/"><code>syscall</code></a> package instead returns an <code>error</code> for system call errors. On Unix, the implementation is done by a <a href="/pkg/syscall/#Errno"><code>syscall.Errno</code></a> type that satisfies <code>error</code> and replaces the old <code>os.Errno</code>. </p> <p> The changes affecting <code>os.EINVAL</code> and relatives are described <a href="#os">elsewhere</a>. <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update almost all code affected by the change. Regardless, most code should use the <code>os</code> package rather than <code>syscall</code> and so will be unaffected. </p> <h3 id="time">Time</h3> <p> Time is always a challenge to support well in a programming language. The old Go <code>time</code> package had <code>int64</code> units, no real type safety, and no distinction between absolute times and durations. </p> <p> One of the most sweeping changes in the Go 1 library is therefore a complete redesign of the <a href="/pkg/time/"><code>time</code></a> package. Instead of an integer number of nanoseconds as an <code>int64</code>, and a separate <code>*time.Time</code> type to deal with human units such as hours and years, there are now two fundamental types: <a href="/pkg/time/#Time"><code>time.Time</code></a> (a value, so the <code>*</code> is gone), which represents a moment in time; and <a href="/pkg/time/#Duration"><code>time.Duration</code></a>, which represents an interval. Both have nanosecond resolution. A <code>Time</code> can represent any time into the ancient past and remote future, while a <code>Duration</code> can span plus or minus only about 290 years. There are methods on these types, plus a number of helpful predefined constant durations such as <code>time.Second</code>. </p> <p> Among the new methods are things like <a href="/pkg/time/#Time.Add"><code>Time.Add</code></a>, which adds a <code>Duration</code> to a <code>Time</code>, and <a href="/pkg/time/#Time.Sub"><code>Time.Sub</code></a>, which subtracts two <code>Times</code> to yield a <code>Duration</code>. </p> <p> The most important semantic change is that the Unix epoch (Jan 1, 1970) is now relevant only for those functions and methods that mention Unix: <a href="/pkg/time/#Unix"><code>time.Unix</code></a> and the <a href="/pkg/time/#Time.Unix"><code>Unix</code></a> and <a href="/pkg/time/#Time.UnixNano"><code>UnixNano</code></a> methods of the <code>Time</code> type. In particular, <a href="/pkg/time/#Now"><code>time.Now</code></a> returns a <code>time.Time</code> value rather than, in the old API, an integer nanosecond count since the Unix epoch. </p> <pre><!--{{code "/doc/progs/go1.go" `/sleepUntil/` `/^}/`}} --><span class="comment">// sleepUntil sleeps until the specified time. It returns immediately if it&#39;s too late.</span> func sleepUntil(wakeup time.Time) { now := time.Now() <span class="comment">// A Time.</span> if !wakeup.After(now) { return } delta := wakeup.Sub(now) <span class="comment">// A Duration.</span> fmt.Printf(&#34;Sleeping for %.3fs\n&#34;, delta.Seconds()) time.Sleep(delta) }</pre> <p> The new types, methods, and constants have been propagated through all the standard packages that use time, such as <code>os</code> and its representation of file time stamps. </p> <p> <em>Updating</em>: The <code>go</code> <code>fix</code> tool will update many uses of the old <code>time</code> package to use the new types and methods, although it does not replace values such as <code>1e9</code> representing nanoseconds per second. Also, because of type changes in some of the values that arise, some of the expressions rewritten by the fix tool may require further hand editing; in such cases the rewrite will include the correct function or method for the old functionality, but may have the wrong type or require further analysis. </p> <h2 id="minor">Minor changes to the library</h2> <p> This section describes smaller changes, such as those to less commonly used packages or that affect few programs beyond the need to run <code>go</code> <code>fix</code>. This category includes packages that are new in Go 1. Collectively they improve portability, regularize behavior, and make the interfaces more modern and Go-like. </p> <h3 id="archive_zip">The archive/zip package</h3> <p> In Go 1, <a href="/pkg/archive/zip/#Writer"><code>*zip.Writer</code></a> no longer has a <code>Write</code> method. Its presence was a mistake. </p> <p> <em>Updating</em>: What little code is affected will be caught by the compiler and must be updated by hand. </p> <h3 id="bufio">The bufio package</h3> <p> In Go 1, <a href="/pkg/bufio/#NewReaderSize"><code>bufio.NewReaderSize</code></a> and <a href="/pkg/bufio/#NewWriterSize"><code>bufio.NewWriterSize</code></a> functions no longer return an error for invalid sizes. If the argument size is too small or invalid, it is adjusted. </p> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update calls that assign the error to _. Calls that aren't fixed will be caught by the compiler and must be updated by hand. </p> <h3 id="compress">The compress/flate, compress/gzip and compress/zlib packages</h3> <p> In Go 1, the <code>NewWriterXxx</code> functions in <a href="/pkg/compress/flate"><code>compress/flate</code></a>, <a href="/pkg/compress/gzip"><code>compress/gzip</code></a> and <a href="/pkg/compress/zlib"><code>compress/zlib</code></a> all return <code>(*Writer, error)</code> if they take a compression level, and <code>*Writer</code> otherwise. Package <code>gzip</code>'s <code>Compressor</code> and <code>Decompressor</code> types have been renamed to <code>Writer</code> and <code>Reader</code>. Package <code>flate</code>'s <code>WrongValueError</code> type has been removed. </p> <p> <em>Updating</em> Running <code>go</code> <code>fix</code> will update old names and calls that assign the error to _. Calls that aren't fixed will be caught by the compiler and must be updated by hand. </p> <h3 id="crypto_aes_des">The crypto/aes and crypto/des packages</h3> <p> In Go 1, the <code>Reset</code> method has been removed. Go does not guarantee that memory is not copied and therefore this method was misleading. </p> <p> The cipher-specific types <code>*aes.Cipher</code>, <code>*des.Cipher</code>, and <code>*des.TripleDESCipher</code> have been removed in favor of <code>cipher.Block</code>. </p> <p> <em>Updating</em>: Remove the calls to Reset. Replace uses of the specific cipher types with cipher.Block. </p> <h3 id="crypto_elliptic">The crypto/elliptic package</h3> <p> In Go 1, <a href="/pkg/crypto/elliptic/#Curve"><code>elliptic.Curve</code></a> has been made an interface to permit alternative implementations. The curve parameters have been moved to the <a href="/pkg/crypto/elliptic/#CurveParams"><code>elliptic.CurveParams</code></a> structure. </p> <p> <em>Updating</em>: Existing users of <code>*elliptic.Curve</code> will need to change to simply <code>elliptic.Curve</code>. Calls to <code>Marshal</code>, <code>Unmarshal</code> and <code>GenerateKey</code> are now functions in <code>crypto/elliptic</code> that take an <code>elliptic.Curve</code> as their first argument. </p> <h3 id="crypto_hmac">The crypto/hmac package</h3> <p> In Go 1, the hash-specific functions, such as <code>hmac.NewMD5</code>, have been removed from <code>crypto/hmac</code>. Instead, <code>hmac.New</code> takes a function that returns a <code>hash.Hash</code>, such as <code>md5.New</code>. </p> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will perform the needed changes. </p> <h3 id="crypto_x509">The crypto/x509 package</h3> <p> In Go 1, the <a href="/pkg/crypto/x509/#CreateCertificate"><code>CreateCertificate</code></a> and <a href="/pkg/crypto/x509/#CreateCRL"><code>CreateCRL</code></a> functions in <code>crypto/x509</code> have been altered to take an <code>interface{}</code> where they previously took a <code>*rsa.PublicKey</code> or <code>*rsa.PrivateKey</code>. This will allow other public key algorithms to be implemented in the future. </p> <p> <em>Updating</em>: No changes will be needed. </p> <h3 id="encoding_binary">The encoding/binary package</h3> <p> In Go 1, the <code>binary.TotalSize</code> function has been replaced by <a href="/pkg/encoding/binary/#Size"><code>Size</code></a>, which takes an <code>interface{}</code> argument rather than a <code>reflect.Value</code>. </p> <p> <em>Updating</em>: What little code is affected will be caught by the compiler and must be updated by hand. </p> <h3 id="encoding_xml">The encoding/xml package</h3> <p> In Go 1, the <a href="/pkg/encoding/xml/"><code>xml</code></a> package has been brought closer in design to the other marshaling packages such as <a href="/pkg/encoding/gob/"><code>encoding/gob</code></a>. </p> <p> The old <code>Parser</code> type is renamed <a href="/pkg/encoding/xml/#Decoder"><code>Decoder</code></a> and has a new <a href="/pkg/encoding/xml/#Decoder.Decode"><code>Decode</code></a> method. An <a href="/pkg/encoding/xml/#Encoder"><code>Encoder</code></a> type was also introduced. </p> <p> The functions <a href="/pkg/encoding/xml/#Marshal"><code>Marshal</code></a> and <a href="/pkg/encoding/xml/#Unmarshal"><code>Unmarshal</code></a> work with <code>[]byte</code> values now. To work with streams, use the new <a href="/pkg/encoding/xml/#Encoder"><code>Encoder</code></a> and <a href="/pkg/encoding/xml/#Decoder"><code>Decoder</code></a> types. </p> <p> When marshaling or unmarshaling values, the format of supported flags in field tags has changed to be closer to the <a href="/pkg/encoding/json"><code>json</code></a> package (<code>`xml:"name,flag"`</code>). The matching done between field tags, field names, and the XML attribute and element names is now case-sensitive. The <code>XMLName</code> field tag, if present, must also match the name of the XML element being marshaled. </p> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update most uses of the package except for some calls to <code>Unmarshal</code>. Special care must be taken with field tags, since the fix tool will not update them and if not fixed by hand they will misbehave silently in some cases. For example, the old <code>"attr"</code> is now written <code>",attr"</code> while plain <code>"attr"</code> remains valid but with a different meaning. </p> <h3 id="expvar">The expvar package</h3> <p> In Go 1, the <code>RemoveAll</code> function has been removed. The <code>Iter</code> function and Iter method on <code>*Map</code> have been replaced by <a href="/pkg/expvar/#Do"><code>Do</code></a> and <a href="/pkg/expvar/#Map.Do"><code>(*Map).Do</code></a>. </p> <p> <em>Updating</em>: Most code using <code>expvar</code> will not need changing. The rare code that used <code>Iter</code> can be updated to pass a closure to <code>Do</code> to achieve the same effect. </p> <h3 id="flag">The flag package</h3> <p> In Go 1, the interface <a href="/pkg/flag/#Value"><code>flag.Value</code></a> has changed slightly. The <code>Set</code> method now returns an <code>error</code> instead of a <code>bool</code> to indicate success or failure. </p> <p> There is also a new kind of flag, <code>Duration</code>, to support argument values specifying time intervals. Values for such flags must be given units, just as <code>time.Duration</code> formats them: <code>10s</code>, <code>1h30m</code>, etc. </p> <pre><!--{{code "/doc/progs/go1.go" `/timeout/`}} -->var timeout = flag.Duration(&#34;timeout&#34;, 30*time.Second, &#34;how long to wait for completion&#34;)</pre> <p> <em>Updating</em>: Programs that implement their own flags will need minor manual fixes to update their <code>Set</code> methods. The <code>Duration</code> flag is new and affects no existing code. </p> <h3 id="go">The go/* packages</h3> <p> Several packages under <code>go</code> have slightly revised APIs. </p> <p> A concrete <code>Mode</code> type was introduced for configuration mode flags in the packages <a href="/pkg/go/scanner/"><code>go/scanner</code></a>, <a href="/pkg/go/parser/"><code>go/parser</code></a>, <a href="/pkg/go/printer/"><code>go/printer</code></a>, and <a href="/pkg/go/doc/"><code>go/doc</code></a>. </p> <p> The modes <code>AllowIllegalChars</code> and <code>InsertSemis</code> have been removed from the <a href="/pkg/go/scanner/"><code>go/scanner</code></a> package. They were mostly useful for scanning text other then Go source files. Instead, the <a href="/pkg/text/scanner/"><code>text/scanner</code></a> package should be used for that purpose. </p> <p> The <a href="/pkg/go/scanner/#ErrorHandler"><code>ErrorHandler</code></a> provided to the scanner's <a href="/pkg/go/scanner/#Scanner.Init"><code>Init</code></a> method is now simply a function rather than an interface. The <code>ErrorVector</code> type has been removed in favor of the (existing) <a href="/pkg/go/scanner/#ErrorList"><code>ErrorList</code></a> type, and the <code>ErrorVector</code> methods have been migrated. Instead of embedding an <code>ErrorVector</code> in a client of the scanner, now a client should maintain an <code>ErrorList</code>. </p> <p> The set of parse functions provided by the <a href="/pkg/go/parser/"><code>go/parser</code></a> package has been reduced to the primary parse function <a href="/pkg/go/parser/#ParseFile"><code>ParseFile</code></a>, and a couple of convenience functions <a href="/pkg/go/parser/#ParseDir"><code>ParseDir</code></a> and <a href="/pkg/go/parser/#ParseExpr"><code>ParseExpr</code></a>. </p> <p> The <a href="/pkg/go/printer/"><code>go/printer</code></a> package supports an additional configuration mode <a href="/pkg/go/printer/#Mode"><code>SourcePos</code></a>; if set, the printer will emit <code>//line</code> comments such that the generated output contains the original source code position information. The new type <a href="/pkg/go/printer/#CommentedNode"><code>CommentedNode</code></a> can be used to provide comments associated with an arbitrary <a href="/pkg/go/ast/#Node"><code>ast.Node</code></a> (until now only <a href="/pkg/go/ast/#File"><code>ast.File</code></a> carried comment information). </p> <p> The type names of the <a href="/pkg/go/doc/"><code>go/doc</code></a> package have been streamlined by removing the <code>Doc</code> suffix: <code>PackageDoc</code> is now <code>Package</code>, <code>ValueDoc</code> is <code>Value</code>, etc. Also, all types now consistently have a <code>Name</code> field (or <code>Names</code>, in the case of type <code>Value</code>) and <code>Type.Factories</code> has become <code>Type.Funcs</code>. Instead of calling <code>doc.NewPackageDoc(pkg, importpath)</code>, documentation for a package is created with: </p> <pre> doc.New(pkg, importpath, mode) </pre> <p> where the new <code>mode</code> parameter specifies the operation mode: if set to <a href="/pkg/go/doc/#AllDecls"><code>AllDecls</code></a>, all declarations (not just exported ones) are considered. The function <code>NewFileDoc</code> was removed, and the function <code>CommentText</code> has become the method <a href="/pkg/go/ast/#Text"><code>Text</code></a> of <a href="/pkg/go/ast/#CommentGroup"><code>ast.CommentGroup</code></a>. </p> <p> In package <a href="/pkg/go/token/"><code>go/token</code></a>, the <a href="/pkg/go/token/#FileSet"><code>token.FileSet</code></a> method <code>Files</code> (which originally returned a channel of <code>*token.File</code>s) has been replaced with the iterator <a href="/pkg/go/token/#FileSet.Iterate"><code>Iterate</code></a> that accepts a function argument instead. </p> <p> In package <a href="/pkg/go/build/"><code>go/build</code></a>, the API has been nearly completely replaced. The package still computes Go package information but it does not run the build: the <code>Cmd</code> and <code>Script</code> types are gone. (To build code, use the new <a href="/cmd/go/"><code>go</code></a> command instead.) The <code>DirInfo</code> type is now named <a href="/pkg/go/build/#Package"><code>Package</code></a>. <code>FindTree</code> and <code>ScanDir</code> are replaced by <a href="/pkg/go/build/#Import"><code>Import</code></a> and <a href="/pkg/go/build/#ImportDir"><code>ImportDir</code></a>. </p> <p> <em>Updating</em>: Code that uses packages in <code>go</code> will have to be updated by hand; the compiler will reject incorrect uses. Templates used in conjunction with any of the <code>go/doc</code> types may need manual fixes; the renamed fields will lead to run-time errors. </p> <h3 id="hash">The hash package</h3> <p> In Go 1, the definition of <a href="/pkg/hash/#Hash"><code>hash.Hash</code></a> includes a new method, <code>BlockSize</code>. This new method is used primarily in the cryptographic libraries. </p> <p> The <code>Sum</code> method of the <a href="/pkg/hash/#Hash"><code>hash.Hash</code></a> interface now takes a <code>[]byte</code> argument, to which the hash value will be appended. The previous behavior can be recreated by adding a <code>nil</code> argument to the call. </p> <p> <em>Updating</em>: Existing implementations of <code>hash.Hash</code> will need to add a <code>BlockSize</code> method. Hashes that process the input one byte at a time can implement <code>BlockSize</code> to return 1. Running <code>go</code> <code>fix</code> will update calls to the <code>Sum</code> methods of the various implementations of <code>hash.Hash</code>. </p> <p> <em>Updating</em>: Since the package's functionality is new, no updating is necessary. </p> <h3 id="http">The http package</h3> <p> In Go 1 the <a href="/pkg/net/http/"><code>http</code></a> package is refactored, putting some of the utilities into a <a href="/pkg/net/http/httputil/"><code>httputil</code></a> subdirectory. These pieces are only rarely needed by HTTP clients. The affected items are: </p> <ul> <li>ClientConn</li> <li>DumpRequest</li> <li>DumpRequestOut</li> <li>DumpResponse</li> <li>NewChunkedReader</li> <li>NewChunkedWriter</li> <li>NewClientConn</li> <li>NewProxyClientConn</li> <li>NewServerConn</li> <li>NewSingleHostReverseProxy</li> <li>ReverseProxy</li> <li>ServerConn</li> </ul> <p> The <code>Request.RawURL</code> field has been removed; it was a historical artifact. </p> <p> The <code>Handle</code> and <code>HandleFunc</code> functions, and the similarly-named methods of <code>ServeMux</code>, now panic if an attempt is made to register the same pattern twice. </p> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update the few programs that are affected except for uses of <code>RawURL</code>, which must be fixed by hand. </p> <h3 id="image">The image package</h3> <p> The <a href="/pkg/image/"><code>image</code></a> package has had a number of minor changes, rearrangements and renamings. </p> <p> Most of the color handling code has been moved into its own package, <a href="/pkg/image/color/"><code>image/color</code></a>. For the elements that moved, a symmetry arises; for instance, each pixel of an <a href="/pkg/image/#RGBA"><code>image.RGBA</code></a> is a <a href="/pkg/image/color/#RGBA"><code>color.RGBA</code></a>. </p> <p> The old <code>image/ycbcr</code> package has been folded, with some renamings, into the <a href="/pkg/image/"><code>image</code></a> and <a href="/pkg/image/color/"><code>image/color</code></a> packages. </p> <p> The old <code>image.ColorImage</code> type is still in the <code>image</code> package but has been renamed <a href="/pkg/image/#Uniform"><code>image.Uniform</code></a>, while <code>image.Tiled</code> has been removed. </p> <p> This table lists the renamings. </p> <table class="codetable" frame="border" summary="image renames"> <colgroup align="left" width="50%"></colgroup> <colgroup align="left" width="50%"></colgroup> <tr> <th align="left">Old</th> <th align="left">New</th> </tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>image.Color</td> <td>color.Color</td></tr> <tr><td>image.ColorModel</td> <td>color.Model</td></tr> <tr><td>image.ColorModelFunc</td> <td>color.ModelFunc</td></tr> <tr><td>image.PalettedColorModel</td> <td>color.Palette</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>image.RGBAColor</td> <td>color.RGBA</td></tr> <tr><td>image.RGBA64Color</td> <td>color.RGBA64</td></tr> <tr><td>image.NRGBAColor</td> <td>color.NRGBA</td></tr> <tr><td>image.NRGBA64Color</td> <td>color.NRGBA64</td></tr> <tr><td>image.AlphaColor</td> <td>color.Alpha</td></tr> <tr><td>image.Alpha16Color</td> <td>color.Alpha16</td></tr> <tr><td>image.GrayColor</td> <td>color.Gray</td></tr> <tr><td>image.Gray16Color</td> <td>color.Gray16</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>image.RGBAColorModel</td> <td>color.RGBAModel</td></tr> <tr><td>image.RGBA64ColorModel</td> <td>color.RGBA64Model</td></tr> <tr><td>image.NRGBAColorModel</td> <td>color.NRGBAModel</td></tr> <tr><td>image.NRGBA64ColorModel</td> <td>color.NRGBA64Model</td></tr> <tr><td>image.AlphaColorModel</td> <td>color.AlphaModel</td></tr> <tr><td>image.Alpha16ColorModel</td> <td>color.Alpha16Model</td></tr> <tr><td>image.GrayColorModel</td> <td>color.GrayModel</td></tr> <tr><td>image.Gray16ColorModel</td> <td>color.Gray16Model</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>ycbcr.RGBToYCbCr</td> <td>color.RGBToYCbCr</td></tr> <tr><td>ycbcr.YCbCrToRGB</td> <td>color.YCbCrToRGB</td></tr> <tr><td>ycbcr.YCbCrColorModel</td> <td>color.YCbCrModel</td></tr> <tr><td>ycbcr.YCbCrColor</td> <td>color.YCbCr</td></tr> <tr><td>ycbcr.YCbCr</td> <td>image.YCbCr</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>ycbcr.SubsampleRatio444</td> <td>image.YCbCrSubsampleRatio444</td></tr> <tr><td>ycbcr.SubsampleRatio422</td> <td>image.YCbCrSubsampleRatio422</td></tr> <tr><td>ycbcr.SubsampleRatio420</td> <td>image.YCbCrSubsampleRatio420</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>image.ColorImage</td> <td>image.Uniform</td></tr> </table> <p> The image package's <code>New</code> functions (<a href="/pkg/image/#NewRGBA"><code>NewRGBA</code></a>, <a href="/pkg/image/#NewRGBA64"><code>NewRGBA64</code></a>, etc.) take an <a href="/pkg/image/#Rectangle"><code>image.Rectangle</code></a> as an argument instead of four integers. </p> <p> Finally, there are new predefined <code>color.Color</code> variables <a href="/pkg/image/color/#Black"><code>color.Black</code></a>, <a href="/pkg/image/color/#White"><code>color.White</code></a>, <a href="/pkg/image/color/#Opaque"><code>color.Opaque</code></a> and <a href="/pkg/image/color/#Transparent"><code>color.Transparent</code></a>. </p> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update almost all code affected by the change. </p> <h3 id="log_syslog">The log/syslog package</h3> <p> In Go 1, the <a href="/pkg/log/syslog/#NewLogger"><code>syslog.NewLogger</code></a> function returns an error as well as a <code>log.Logger</code>. </p> <p> <em>Updating</em>: What little code is affected will be caught by the compiler and must be updated by hand. </p> <h3 id="mime">The mime package</h3> <p> In Go 1, the <a href="/pkg/mime/#FormatMediaType"><code>FormatMediaType</code></a> function of the <code>mime</code> package has been simplified to make it consistent with <a href="/pkg/mime/#ParseMediaType"><code>ParseMediaType</code></a>. It now takes <code>"text/html"</code> rather than <code>"text"</code> and <code>"html"</code>. </p> <p> <em>Updating</em>: What little code is affected will be caught by the compiler and must be updated by hand. </p> <h3 id="net">The net package</h3> <p> In Go 1, the various <code>SetTimeout</code>, <code>SetReadTimeout</code>, and <code>SetWriteTimeout</code> methods have been replaced with <a href="/pkg/net/#IPConn.SetDeadline"><code>SetDeadline</code></a>, <a href="/pkg/net/#IPConn.SetReadDeadline"><code>SetReadDeadline</code></a>, and <a href="/pkg/net/#IPConn.SetWriteDeadline"><code>SetWriteDeadline</code></a>, respectively. Rather than taking a timeout value in nanoseconds that apply to any activity on the connection, the new methods set an absolute deadline (as a <code>time.Time</code> value) after which reads and writes will time out and no longer block. </p> <p> There are also new functions <a href="/pkg/net/#DialTimeout"><code>net.DialTimeout</code></a> to simplify timing out dialing a network address and <a href="/pkg/net/#ListenMulticastUDP"><code>net.ListenMulticastUDP</code></a> to allow multicast UDP to listen concurrently across multiple listeners. The <code>net.ListenMulticastUDP</code> function replaces the old <code>JoinGroup</code> and <code>LeaveGroup</code> methods. </p> <p> <em>Updating</em>: Code that uses the old methods will fail to compile and must be updated by hand. The semantic change makes it difficult for the fix tool to update automatically. </p> <h3 id="os">The os package</h3> <p> The <code>Time</code> function has been removed; callers should use the <a href="/pkg/time/#Time"><code>Time</code></a> type from the <code>time</code> package. </p> <p> The <code>Exec</code> function has been removed; callers should use <code>Exec</code> from the <code>syscall</code> package, where available. </p> <p> The <code>ShellExpand</code> function has been renamed to <a href="/pkg/os/#ExpandEnv"><code>ExpandEnv</code></a>. </p> <p> The <a href="/pkg/os/#NewFile"><code>NewFile</code></a> function now takes a <code>uintptr</code> fd, instead of an <code>int</code>. The <a href="/pkg/os/#File.Fd"><code>Fd</code></a> method on files now also returns a <code>uintptr</code>. </p> <p> There are no longer error constants such as <code>EINVAL</code> in the <code>os</code> package, since the set of values varied with the underlying operating system. There are new portable functions like <a href="/pkg/os/#IsPermission"><code>IsPermission</code></a> to test common error properties, plus a few new error values with more Go-like names, such as <a href="/pkg/os/#ErrPermission"><code>ErrPermission</code></a> and <a href="/pkg/os/#ErrNoEnv"><code>ErrNoEnv</code></a>. </p> <p> The <code>Getenverror</code> function has been removed. To distinguish between a non-existent environment variable and an empty string, use <a href="/pkg/os/#Environ"><code>os.Environ</code></a> or <a href="/pkg/syscall/#Getenv"><code>syscall.Getenv</code></a>. </p> <p> The <a href="/pkg/os/#Process.Wait"><code>Process.Wait</code></a> method has dropped its option argument and the associated constants are gone from the package. Also, the function <code>Wait</code> is gone; only the method of the <code>Process</code> type persists. </p> <p> The <code>Waitmsg</code> type returned by <a href="/pkg/os/#Process.Wait"><code>Process.Wait</code></a> has been replaced with a more portable <a href="/pkg/os/#ProcessState"><code>ProcessState</code></a> type with accessor methods to recover information about the process. Because of changes to <code>Wait</code>, the <code>ProcessState</code> value always describes an exited process. Portability concerns simplified the interface in other ways, but the values returned by the <a href="/pkg/os/#ProcessState.Sys"><code>ProcessState.Sys</code></a> and <a href="/pkg/os/#ProcessState.SysUsage"><code>ProcessState.SysUsage</code></a> methods can be type-asserted to underlying system-specific data structures such as <a href="/pkg/syscall/#WaitStatus"><code>syscall.WaitStatus</code></a> and <a href="/pkg/syscall/#Rusage"><code>syscall.Rusage</code></a> on Unix. </p> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will drop a zero argument to <code>Process.Wait</code>. All other changes will be caught by the compiler and must be updated by hand. </p> <h4 id="os_fileinfo">The os.FileInfo type</h4> <p> Go 1 redefines the <a href="/pkg/os/#FileInfo"><code>os.FileInfo</code></a> type, changing it from a struct to an interface: </p> <pre> type FileInfo interface { Name() string // base name of the file Size() int64 // length in bytes Mode() FileMode // file mode bits ModTime() time.Time // modification time IsDir() bool // abbreviation for Mode().IsDir() Sys() interface{} // underlying data source (can return nil) } </pre> <p> The file mode information has been moved into a subtype called <a href="/pkg/os/#FileMode"><code>os.FileMode</code></a>, a simple integer type with <code>IsDir</code>, <code>Perm</code>, and <code>String</code> methods. </p> <p> The system-specific details of file modes and properties such as (on Unix) i-number have been removed from <code>FileInfo</code> altogether. Instead, each operating system's <code>os</code> package provides an implementation of the <code>FileInfo</code> interface, which has a <code>Sys</code> method that returns the system-specific representation of file metadata. For instance, to discover the i-number of a file on a Unix system, unpack the <code>FileInfo</code> like this: </p> <pre> fi, err := os.Stat("hello.go") if err != nil { log.Fatal(err) } // Check that it's a Unix file. unixStat, ok := fi.Sys().(*syscall.Stat_t) if !ok { log.Fatal("hello.go: not a Unix file") } fmt.Printf("file i-number: %d\n", unixStat.Ino) </pre> <p> Assuming (which is unwise) that <code>"hello.go"</code> is a Unix file, the i-number expression could be contracted to </p> <pre> fi.Sys().(*syscall.Stat_t).Ino </pre> <p> The vast majority of uses of <code>FileInfo</code> need only the methods of the standard interface. </p> <p> The <code>os</code> package no longer contains wrappers for the POSIX errors such as <code>ENOENT</code>. For the few programs that need to verify particular error conditions, there are now the boolean functions <a href="/pkg/os/#IsExist"><code>IsExist</code></a>, <a href="/pkg/os/#IsNotExist"><code>IsNotExist</code></a> and <a href="/pkg/os/#IsPermission"><code>IsPermission</code></a>. </p> <pre><!--{{code "/doc/progs/go1.go" `/os\.Open/` `/}/`}} --> f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) if os.IsExist(err) { log.Printf(&#34;%s already exists&#34;, name) }</pre> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update code that uses the old equivalent of the current <code>os.FileInfo</code> and <code>os.FileMode</code> API. Code that needs system-specific file details will need to be updated by hand. Code that uses the old POSIX error values from the <code>os</code> package will fail to compile and will also need to be updated by hand. </p> <h3 id="os_signal">The os/signal package</h3> <p> The <code>os/signal</code> package in Go 1 replaces the <code>Incoming</code> function, which returned a channel that received all incoming signals, with the selective <code>Notify</code> function, which asks for delivery of specific signals on an existing channel. </p> <p> <em>Updating</em>: Code must be updated by hand. A literal translation of </p> <pre> c := signal.Incoming() </pre> <p> is </p> <pre> c := make(chan os.Signal) signal.Notify(c) // ask for all signals </pre> <p> but most code should list the specific signals it wants to handle instead: </p> <pre> c := make(chan os.Signal) signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT) </pre> <h3 id="path_filepath">The path/filepath package</h3> <p> In Go 1, the <a href="/pkg/path/filepath/#Walk"><code>Walk</code></a> function of the <code>path/filepath</code> package has been changed to take a function value of type <a href="/pkg/path/filepath/#WalkFunc"><code>WalkFunc</code></a> instead of a <code>Visitor</code> interface value. <code>WalkFunc</code> unifies the handling of both files and directories. </p> <pre> type WalkFunc func(path string, info os.FileInfo, err error) error </pre> <p> The <code>WalkFunc</code> function will be called even for files or directories that could not be opened; in such cases the error argument will describe the failure. If a directory's contents are to be skipped, the function should return the value <a href="/pkg/path/filepath/#variables"><code>filepath.SkipDir</code></a> </p> <pre><!--{{code "/doc/progs/go1.go" `/STARTWALK/` `/ENDWALK/`}} --> markFn := func(path string, info os.FileInfo, err error) error { if path == &#34;pictures&#34; { <span class="comment">// Will skip walking of directory pictures and its contents.</span> return filepath.SkipDir } if err != nil { return err } log.Println(path) return nil } err := filepath.Walk(&#34;.&#34;, markFn) if err != nil { log.Fatal(err) }</pre> <p> <em>Updating</em>: The change simplifies most code but has subtle consequences, so affected programs will need to be updated by hand. The compiler will catch code using the old interface. </p> <h3 id="regexp">The regexp package</h3> <p> The <a href="/pkg/regexp/"><code>regexp</code></a> package has been rewritten. It has the same interface but the specification of the regular expressions it supports has changed from the old "egrep" form to that of <a href="http://code.google.com/p/re2/">RE2</a>. </p> <p> <em>Updating</em>: Code that uses the package should have its regular expressions checked by hand. </p> <h3 id="runtime">The runtime package</h3> <p> In Go 1, much of the API exported by package <code>runtime</code> has been removed in favor of functionality provided by other packages. Code using the <code>runtime.Type</code> interface or its specific concrete type implementations should now use package <a href="/pkg/reflect/"><code>reflect</code></a>. Code using <code>runtime.Semacquire</code> or <code>runtime.Semrelease</code> should use channels or the abstractions in package <a href="/pkg/sync/"><code>sync</code></a>. The <code>runtime.Alloc</code>, <code>runtime.Free</code>, and <code>runtime.Lookup</code> functions, an unsafe API created for debugging the memory allocator, have no replacement. </p> <p> Before, <code>runtime.MemStats</code> was a global variable holding statistics about memory allocation, and calls to <code>runtime.UpdateMemStats</code> ensured that it was up to date. In Go 1, <code>runtime.MemStats</code> is a struct type, and code should use <a href="/pkg/runtime/#ReadMemStats"><code>runtime.ReadMemStats</code></a> to obtain the current statistics. </p> <p> The package adds a new function, <a href="/pkg/runtime/#NumCPU"><code>runtime.NumCPU</code></a>, that returns the number of CPUs available for parallel execution, as reported by the operating system kernel. Its value can inform the setting of <code>GOMAXPROCS</code>. The <code>runtime.Cgocalls</code> and <code>runtime.Goroutines</code> functions have been renamed to <code>runtime.NumCgoCall</code> and <code>runtime.NumGoroutine</code>. </p> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update code for the function renamings. Other code will need to be updated by hand. </p> <h3 id="strconv">The strconv package</h3> <p> In Go 1, the <a href="/pkg/strconv/"><code>strconv</code></a> package has been significantly reworked to make it more Go-like and less C-like, although <code>Atoi</code> lives on (it's similar to <code>int(ParseInt(x, 10, 0))</code>, as does <code>Itoa(x)</code> (<code>FormatInt(int64(x), 10)</code>). There are also new variants of some of the functions that append to byte slices rather than return strings, to allow control over allocation. </p> <p> This table summarizes the renamings; see the <a href="/pkg/strconv/">package documentation</a> for full details. </p> <table class="codetable" frame="border" summary="strconv renames"> <colgroup align="left" width="50%"></colgroup> <colgroup align="left" width="50%"></colgroup> <tr> <th align="left">Old call</th> <th align="left">New call</th> </tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Atob(x)</td> <td>ParseBool(x)</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Atof32(x)</td> <td>ParseFloat(x, 32)§</td></tr> <tr><td>Atof64(x)</td> <td>ParseFloat(x, 64)</td></tr> <tr><td>AtofN(x, n)</td> <td>ParseFloat(x, n)</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Atoi(x)</td> <td>Atoi(x)</td></tr> <tr><td>Atoi(x)</td> <td>ParseInt(x, 10, 0)§</td></tr> <tr><td>Atoi64(x)</td> <td>ParseInt(x, 10, 64)</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Atoui(x)</td> <td>ParseUint(x, 10, 0)§</td></tr> <tr><td>Atoui64(x)</td> <td>ParseUint(x, 10, 64)</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Btoi64(x, b)</td> <td>ParseInt(x, b, 64)</td></tr> <tr><td>Btoui64(x, b)</td> <td>ParseUint(x, b, 64)</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Btoa(x)</td> <td>FormatBool(x)</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Ftoa32(x, f, p)</td> <td>FormatFloat(float64(x), f, p, 32)</td></tr> <tr><td>Ftoa64(x, f, p)</td> <td>FormatFloat(x, f, p, 64)</td></tr> <tr><td>FtoaN(x, f, p, n)</td> <td>FormatFloat(x, f, p, n)</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Itoa(x)</td> <td>Itoa(x)</td></tr> <tr><td>Itoa(x)</td> <td>FormatInt(int64(x), 10)</td></tr> <tr><td>Itoa64(x)</td> <td>FormatInt(x, 10)</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Itob(x, b)</td> <td>FormatInt(int64(x), b)</td></tr> <tr><td>Itob64(x, b)</td> <td>FormatInt(x, b)</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Uitoa(x)</td> <td>FormatUint(uint64(x), 10)</td></tr> <tr><td>Uitoa64(x)</td> <td>FormatUint(x, 10)</td></tr> <tr> <td colspan="2"><hr></td> </tr> <tr><td>Uitob(x, b)</td> <td>FormatUint(uint64(x), b)</td></tr> <tr><td>Uitob64(x, b)</td> <td>FormatUint(x, b)</td></tr> </table> <p> <em>Updating</em>: Running <code>go</code> <code>fix</code> will update almost all code affected by the change. <br> § <code>Atoi</code> persists but <code>Atoui</code> and <code>Atof32</code> do not, so they may require a cast that must be added by hand; the <code>go</code> <code>fix</code> tool will warn about it. </p> <h3 id="templates">The template packages</h3> <p> The <code>template</code> and <code>exp/template/html</code> packages have moved to <a href="/pkg/text/template/"><code>text/template</code></a> and <a href="/pkg/html/template/"><code>html/template</code></a>. More significant, the interface to these packages has been simplified. The template language is the same, but the concept of "template set" is gone and the functions and methods of the packages have changed accordingly, often by elimination. </p> <p> Instead of sets, a <code>Template</code> object may contain multiple named template definitions, in effect constructing name spaces for template invocation. A template can invoke any other template associated with it, but only those templates associated with it. The simplest way to associate templates is to parse them together, something made easier with the new structure of the packages. </p> <p> <em>Updating</em>: The imports will be updated by fix tool. Single-template uses will be otherwise be largely unaffected. Code that uses multiple templates in concert will need to be updated by hand. The <a href="/pkg/text/template/#examples">examples</a> in the documentation for <code>text/template</code> can provide guidance. </p> <h3 id="testing">The testing package</h3> <p> The testing package has a type, <code>B</code>, passed as an argument to benchmark functions. In Go 1, <code>B</code> has new methods, analogous to those of <code>T</code>, enabling logging and failure reporting. </p> <pre><!--{{code "/doc/progs/go1.go" `/func.*Benchmark/` `/^}/`}} -->func BenchmarkSprintf(b *testing.B) { <span class="comment">// Verify correctness before running benchmark.</span> b.StopTimer() got := fmt.Sprintf(&#34;%x&#34;, 23) const expect = &#34;17&#34; if expect != got { b.Fatalf(&#34;expected %q; got %q&#34;, expect, got) } b.StartTimer() for i := 0; i &lt; b.N; i++ { fmt.Sprintf(&#34;%x&#34;, 23) } }</pre> <p> <em>Updating</em>: Existing code is unaffected, although benchmarks that use <code>println</code> or <code>panic</code> should be updated to use the new methods. </p> <h3 id="testing_script">The testing/script package</h3> <p> The testing/script package has been deleted. It was a dreg. </p> <p> <em>Updating</em>: No code is likely to be affected. </p> <h3 id="unsafe">The unsafe package</h3> <p> In Go 1, the functions <code>unsafe.Typeof</code>, <code>unsafe.Reflect</code>, <code>unsafe.Unreflect</code>, <code>unsafe.New</code>, and <code>unsafe.NewArray</code> have been removed; they duplicated safer functionality provided by package <a href="/pkg/reflect/"><code>reflect</code></a>. </p> <p> <em>Updating</em>: Code using these functions must be rewritten to use package <a href="/pkg/reflect/"><code>reflect</code></a>. The changes to <a href="http://code.google.com/p/go/source/detail?r=2646dc956207">encoding/gob</a> and the <a href="http://code.google.com/p/goprotobuf/source/detail?r=5340ad310031">protocol buffer library</a> may be helpful as examples. </p> <h3 id="url">The url package</h3> <p> In Go 1 several fields from the <a href="/pkg/net/url/#URL"><code>url.URL</code></a> type were removed or replaced. </p> <p> The <a href="/pkg/net/url/#URL.String"><code>String</code></a> method now predictably rebuilds an encoded URL string using all of <code>URL</code>'s fields as necessary. The resulting string will also no longer have passwords escaped. </p> <p> The <code>Raw</code> field has been removed. In most cases the <code>String</code> method may be used in its place. </p> <p> The old <code>RawUserinfo</code> field is replaced by the <code>User</code> field, of type <a href="/pkg/net/url/#Userinfo"><code>*net.Userinfo</code></a>. Values of this type may be created using the new <a href="/pkg/net/url/#User"><code>net.User</code></a> and <a href="/pkg/net/url/#UserPassword"><code>net.UserPassword</code></a> functions. The <code>EscapeUserinfo</code> and <code>UnescapeUserinfo</code> functions are also gone. </p> <p> The <code>RawAuthority</code> field has been removed. The same information is available in the <code>Host</code> and <code>User</code> fields. </p> <p> The <code>RawPath</code> field and the <code>EncodedPath</code> method have been removed. The path information in rooted URLs (with a slash following the schema) is now available only in decoded form in the <code>Path</code> field. Occasionally, the encoded data may be required to obtain information that was lost in the decoding process. These cases must be handled by accessing the data the URL was built from. </p> <p> URLs with non-rooted paths, such as <code>"mailto:dev@golang.org?subject=Hi"</code>, are also handled differently. The <code>OpaquePath</code> boolean field has been removed and a new <code>Opaque</code> string field introduced to hold the encoded path for such URLs. In Go 1, the cited URL parses as: </p> <pre> URL{ Scheme: "mailto", Opaque: "dev@golang.org", RawQuery: "subject=Hi", } </pre> <p> A new <a href="/pkg/net/url/#URL.RequestURI"><code>RequestURI</code></a> method was added to <code>URL</code>. </p> <p> The <code>ParseWithReference</code> function has been renamed to <code>ParseWithFragment</code>. </p> <p> <em>Updating</em>: Code that uses the old fields will fail to compile and must be updated by hand. The semantic changes make it difficult for the fix tool to update automatically. </p> <h2 id="cmd_go">The go command</h2> <p> Go 1 introduces the <a href="/cmd/go/">go command</a>, a tool for fetching, building, and installing Go packages and commands. The <code>go</code> command does away with makefiles, instead using Go source code to find dependencies and determine build conditions. Most existing Go programs will no longer require makefiles to be built. </p> <p> See <a href="/doc/code.html">How to Write Go Code</a> for a primer on the <code>go</code> command and the <a href="/cmd/go/">go command documentation</a> for the full details. </p> <p> <em>Updating</em>: Projects that depend on the Go project's old makefile-based build infrastructure (<code>Make.pkg</code>, <code>Make.cmd</code>, and so on) should switch to using the <code>go</code> command for building Go code and, if necessary, rewrite their makefiles to perform any auxiliary build tasks. </p> <h2 id="cmd_cgo">The cgo command</h2> <p> In Go 1, the <a href="/cmd/cgo">cgo command</a> uses a different <code>_cgo_export.h</code> file, which is generated for packages containing <code>//export</code> lines. The <code>_cgo_export.h</code> file now begins with the C preamble comment, so that exported function definitions can use types defined there. This has the effect of compiling the preamble multiple times, so a package using <code>//export</code> must not put function definitions or variable initializations in the C preamble. </p> <h2 id="releases">Packaged releases</h2> <p> One of the most significant changes associated with Go 1 is the availability of prepackaged, downloadable distributions. They are available for many combinations of architecture and operating system (including Windows) and the list will grow. Installation details are described on the <a href="/doc/install">Getting Started</a> page, while the distributions themselves are listed on the <a href="http://code.google.com/p/go/downloads/list">downloads page</a>. </div> <div id="footer"> Build version go1.0.1.<br> Except as <a href="http://code.google.com/policies.html#restrictions">noted</a>, the content of this page is licensed under the Creative Commons Attribution 3.0 License, and code is licensed under a <a href="/LICENSE">BSD license</a>.<br> <a href="/doc/tos.html">Terms of Service</a> | <a href="http://www.google.com/intl/en/privacy/privacy-policy.html">Privacy Policy</a> </div> <script type="text/javascript"> (function() { var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true; ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s); })(); </script> </body> <script type="text/javascript"> (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })(); </script> </html> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/���������������������������������0000755�0000153�0000161�00000000000�12321735652�024674� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests16.dat����������������������0000644�0000153�0000161�00000125251�12321735652�026705� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!doctype html><script> #errors Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | <body> #data <!doctype html><script>a #errors Line: 1 Col: 24 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "a" | <body> #data <!doctype html><script>< #errors Line: 1 Col: 24 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<" | <body> #data <!doctype html><script></ #errors Line: 1 Col: 25 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</" | <body> #data <!doctype html><script></S #errors Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</S" | <body> #data <!doctype html><script></SC #errors Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</SC" | <body> #data <!doctype html><script></SCR #errors Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</SCR" | <body> #data <!doctype html><script></SCRI #errors Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</SCRI" | <body> #data <!doctype html><script></SCRIP #errors Line: 1 Col: 30 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</SCRIP" | <body> #data <!doctype html><script></SCRIPT #errors Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</SCRIPT" | <body> #data <!doctype html><script></SCRIPT #errors Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | <body> #data <!doctype html><script></s #errors Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</s" | <body> #data <!doctype html><script></sc #errors Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</sc" | <body> #data <!doctype html><script></scr #errors Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</scr" | <body> #data <!doctype html><script></scri #errors Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</scri" | <body> #data <!doctype html><script></scrip #errors Line: 1 Col: 30 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</scrip" | <body> #data <!doctype html><script></script #errors Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "</script" | <body> #data <!doctype html><script></script #errors Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | <body> #data <!doctype html><script><! #errors Line: 1 Col: 25 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!" | <body> #data <!doctype html><script><!a #errors Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!a" | <body> #data <!doctype html><script><!- #errors Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!-" | <body> #data <!doctype html><script><!-a #errors Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!-a" | <body> #data <!doctype html><script><!-- #errors Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--" | <body> #data <!doctype html><script><!--a #errors Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--a" | <body> #data <!doctype html><script><!--< #errors Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<" | <body> #data <!doctype html><script><!--<a #errors Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<a" | <body> #data <!doctype html><script><!--</ #errors Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--</" | <body> #data <!doctype html><script><!--</script #errors Line: 1 Col: 35 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--</script" | <body> #data <!doctype html><script><!--</script #errors Line: 1 Col: 36 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--" | <body> #data <!doctype html><script><!--<s #errors Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<s" | <body> #data <!doctype html><script><!--<script #errors Line: 1 Col: 34 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script" | <body> #data <!doctype html><script><!--<script #errors Line: 1 Col: 35 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script " | <body> #data <!doctype html><script><!--<script < #errors Line: 1 Col: 36 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script <" | <body> #data <!doctype html><script><!--<script <a #errors Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script <a" | <body> #data <!doctype html><script><!--<script </ #errors Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </" | <body> #data <!doctype html><script><!--<script </s #errors Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </s" | <body> #data <!doctype html><script><!--<script </script #errors Line: 1 Col: 43 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script" | <body> #data <!doctype html><script><!--<script </scripta #errors Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </scripta" | <body> #data <!doctype html><script><!--<script </script #errors Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script " | <body> #data <!doctype html><script><!--<script </script> #errors Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script>" | <body> #data <!doctype html><script><!--<script </script/ #errors Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script/" | <body> #data <!doctype html><script><!--<script </script < #errors Line: 1 Col: 45 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script <" | <body> #data <!doctype html><script><!--<script </script <a #errors Line: 1 Col: 46 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script <a" | <body> #data <!doctype html><script><!--<script </script </ #errors Line: 1 Col: 46 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script </" | <body> #data <!doctype html><script><!--<script </script </script #errors Line: 1 Col: 52 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script </script" | <body> #data <!doctype html><script><!--<script </script </script #errors Line: 1 Col: 53 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script " | <body> #data <!doctype html><script><!--<script </script </script/ #errors Line: 1 Col: 53 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script " | <body> #data <!doctype html><script><!--<script </script </script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script </script " | <body> #data <!doctype html><script><!--<script - #errors Line: 1 Col: 36 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -" | <body> #data <!doctype html><script><!--<script -a #errors Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -a" | <body> #data <!doctype html><script><!--<script -< #errors Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -<" | <body> #data <!doctype html><script><!--<script -- #errors Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --" | <body> #data <!doctype html><script><!--<script --a #errors Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --a" | <body> #data <!doctype html><script><!--<script --< #errors Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --<" | <body> #data <!doctype html><script><!--<script --> #errors Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -->" | <body> #data <!doctype html><script><!--<script -->< #errors Line: 1 Col: 39 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --><" | <body> #data <!doctype html><script><!--<script --></ #errors Line: 1 Col: 40 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --></" | <body> #data <!doctype html><script><!--<script --></script #errors Line: 1 Col: 46 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script --></script" | <body> #data <!doctype html><script><!--<script --></script #errors Line: 1 Col: 47 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -->" | <body> #data <!doctype html><script><!--<script --></script/ #errors Line: 1 Col: 47 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -->" | <body> #data <!doctype html><script><!--<script --></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script -->" | <body> #data <!doctype html><script><!--<script><\/script>--></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script><\/script>-->" | <body> #data <!doctype html><script><!--<script></scr'+'ipt>--></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></scr'+'ipt>-->" | <body> #data <!doctype html><script><!--<script></script><script></script></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>" | <body> #data <!doctype html><script><!--<script></script><script></script>--><!--</script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>--><!--" | <body> #data <!doctype html><script><!--<script></script><script></script>-- ></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>-- >" | <body> #data <!doctype html><script><!--<script></script><script></script>- -></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>- ->" | <body> #data <!doctype html><script><!--<script></script><script></script>- - ></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>- - >" | <body> #data <!doctype html><script><!--<script></script><script></script>-></script> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></script><script></script>->" | <body> #data <!doctype html><script><!--<script>--!></script>X #errors Line: 1 Col: 49 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script>--!></script>X" | <body> #data <!doctype html><script><!--<scr'+'ipt></script>--></script> #errors Line: 1 Col: 59 Unexpected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<scr'+'ipt>" | <body> | "-->" #data <!doctype html><script><!--<script></scr'+'ipt></script>X #errors Line: 1 Col: 57 Unexpected end of file. Expected end tag (script). #document | <!DOCTYPE html> | <html> | <head> | <script> | "<!--<script></scr'+'ipt></script>X" | <body> #data <!doctype html><style><!--<style></style>--></style> #errors Line: 1 Col: 52 Unexpected end tag (style). #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--<style>" | <body> | "-->" #data <!doctype html><style><!--</style>X #errors #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--" | <body> | "X" #data <!doctype html><style><!--...</style>...--></style> #errors Line: 1 Col: 51 Unexpected end tag (style). #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--..." | <body> | "...-->" #data <!doctype html><style><!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style></style>X #errors #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style>" | <body> | "X" #data <!doctype html><style><!--...<style><!--...--!></style>--></style> #errors Line: 1 Col: 66 Unexpected end tag (style). #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--...<style><!--...--!>" | <body> | "-->" #data <!doctype html><style><!--...</style><!-- --><style>@import ...</style> #errors #document | <!DOCTYPE html> | <html> | <head> | <style> | "<!--..." | <!-- --> | <style> | "@import ..." | <body> #data <!doctype html><style>...<style><!--...</style><!-- --></style> #errors Line: 1 Col: 63 Unexpected end tag (style). #document | <!DOCTYPE html> | <html> | <head> | <style> | "...<style><!--..." | <!-- --> | <body> #data <!doctype html><style>...<!--[if IE]><style>...</style>X #errors #document | <!DOCTYPE html> | <html> | <head> | <style> | "...<!--[if IE]><style>..." | <body> | "X" #data <!doctype html><title><!--<title></title>--></title> #errors Line: 1 Col: 52 Unexpected end tag (title). #document | <!DOCTYPE html> | <html> | <head> | <title> | "<!--<title>" | <body> | "-->" #data <!doctype html><title>&lt;/title></title> #errors #document | <!DOCTYPE html> | <html> | <head> | <title> | "</title>" | <body> #data <!doctype html><title>foo/title><link></head><body>X #errors Line: 1 Col: 52 Unexpected end of file. Expected end tag (title). #document | <!DOCTYPE html> | <html> | <head> | <title> | "foo/title><link></head><body>X" | <body> #data <!doctype html><noscript><!--<noscript></noscript>--></noscript> #errors Line: 1 Col: 64 Unexpected end tag (noscript). #document | <!DOCTYPE html> | <html> | <head> | <noscript> | "<!--<noscript>" | <body> | "-->" #data <!doctype html><noscript><!--</noscript>X<noscript>--></noscript> #errors #document | <!DOCTYPE html> | <html> | <head> | <noscript> | "<!--" | <body> | "X" | <noscript> | "-->" #data <!doctype html><noscript><iframe></noscript>X #errors #document | <!DOCTYPE html> | <html> | <head> | <noscript> | "<iframe>" | <body> | "X" #data <!doctype html><noframes><!--<noframes></noframes>--></noframes> #errors Line: 1 Col: 64 Unexpected end tag (noframes). #document | <!DOCTYPE html> | <html> | <head> | <noframes> | "<!--<noframes>" | <body> | "-->" #data <!doctype html><noframes><body><script><!--...</script></body></noframes></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <noframes> | "<body><script><!--...</script></body>" | <body> #data <!doctype html><textarea><!--<textarea></textarea>--></textarea> #errors Line: 1 Col: 64 Unexpected end tag (textarea). #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> | "<!--<textarea>" | "-->" #data <!doctype html><textarea>&lt;/textarea></textarea> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> | "</textarea>" #data <!doctype html><textarea>&lt;</textarea> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> | "<" #data <!doctype html><textarea>a&lt;b</textarea> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> | "a<b" #data <!doctype html><iframe><!--<iframe></iframe>--></iframe> #errors Line: 1 Col: 56 Unexpected end tag (iframe). #document | <!DOCTYPE html> | <html> | <head> | <body> | <iframe> | "<!--<iframe>" | "-->" #data <!doctype html><iframe>...<!--X->...<!--/X->...</iframe> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <iframe> | "...<!--X->...<!--/X->..." #data <!doctype html><xmp><!--<xmp></xmp>--></xmp> #errors Line: 1 Col: 44 Unexpected end tag (xmp). #document | <!DOCTYPE html> | <html> | <head> | <body> | <xmp> | "<!--<xmp>" | "-->" #data <!doctype html><noembed><!--<noembed></noembed>--></noembed> #errors Line: 1 Col: 60 Unexpected end tag (noembed). #document | <!DOCTYPE html> | <html> | <head> | <body> | <noembed> | "<!--<noembed>" | "-->" #data <script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 8 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | <body> #data <script>a #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 9 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "a" | <body> #data <script>< #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 9 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<" | <body> #data <script></ #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 10 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</" | <body> #data <script></S #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</S" | <body> #data <script></SC #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</SC" | <body> #data <script></SCR #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</SCR" | <body> #data <script></SCRI #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</SCRI" | <body> #data <script></SCRIP #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 15 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</SCRIP" | <body> #data <script></SCRIPT #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 16 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</SCRIPT" | <body> #data <script></SCRIPT #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 17 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | <body> #data <script></s #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</s" | <body> #data <script></sc #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</sc" | <body> #data <script></scr #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</scr" | <body> #data <script></scri #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</scri" | <body> #data <script></scrip #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 15 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</scrip" | <body> #data <script></script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 16 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</script" | <body> #data <script></script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 17 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | <body> #data <script><! #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 10 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!" | <body> #data <script><!a #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!a" | <body> #data <script><!- #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!-" | <body> #data <script><!-a #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!-a" | <body> #data <script><!-- #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--" | <body> #data <script><!--a #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--a" | <body> #data <script><!--< #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<" | <body> #data <script><!--<a #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<a" | <body> #data <script><!--</ #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--</" | <body> #data <script><!--</script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 20 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--</script" | <body> #data <script><!--</script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 21 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--" | <body> #data <script><!--<s #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<s" | <body> #data <script><!--<script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 19 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script" | <body> #data <script><!--<script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 20 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script " | <body> #data <script><!--<script < #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 21 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script <" | <body> #data <script><!--<script <a #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script <a" | <body> #data <script><!--<script </ #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </" | <body> #data <script><!--<script </s #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </s" | <body> #data <script><!--<script </script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </script" | <body> #data <script><!--<script </scripta #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </scripta" | <body> #data <script><!--<script </script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </script " | <body> #data <script><!--<script </script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </script>" | <body> #data <script><!--<script </script/ #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </script/" | <body> #data <script><!--<script </script < #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 30 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </script <" | <body> #data <script><!--<script </script <a #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </script <a" | <body> #data <script><!--<script </script </ #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </script </" | <body> #data <script><!--<script </script </script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </script </script" | <body> #data <script><!--<script </script </script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </script " | <body> #data <script><!--<script </script </script/ #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script </script " | <body> #data <script><!--<script </script </script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | "<!--<script </script " | <body> #data <script><!--<script - #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 21 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script -" | <body> #data <script><!--<script -a #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script -a" | <body> #data <script><!--<script -- #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script --" | <body> #data <script><!--<script --a #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script --a" | <body> #data <script><!--<script --> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script -->" | <body> #data <script><!--<script -->< #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 24 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script --><" | <body> #data <script><!--<script --></ #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 25 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script --></" | <body> #data <script><!--<script --></script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script --></script" | <body> #data <script><!--<script --></script #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script -->" | <body> #data <script><!--<script --></script/ #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script -->" | <body> #data <script><!--<script --></script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | "<!--<script -->" | <body> #data <script><!--<script><\/script>--></script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | "<!--<script><\/script>-->" | <body> #data <script><!--<script></scr'+'ipt>--></script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | "<!--<script></scr'+'ipt>-->" | <body> #data <script><!--<script></script><script></script></script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | "<!--<script></script><script></script>" | <body> #data <script><!--<script></script><script></script>--><!--</script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | "<!--<script></script><script></script>--><!--" | <body> #data <script><!--<script></script><script></script>-- ></script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | "<!--<script></script><script></script>-- >" | <body> #data <script><!--<script></script><script></script>- -></script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | "<!--<script></script><script></script>- ->" | <body> #data <script><!--<script></script><script></script>- - ></script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | "<!--<script></script><script></script>- - >" | <body> #data <script><!--<script></script><script></script>-></script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | "<!--<script></script><script></script>->" | <body> #data <script><!--<script>--!></script>X #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 34 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script>--!></script>X" | <body> #data <script><!--<scr'+'ipt></script>--></script> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 44 Unexpected end tag (script). #document | <html> | <head> | <script> | "<!--<scr'+'ipt>" | <body> | "-->" #data <script><!--<script></scr'+'ipt></script>X #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 42 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "<!--<script></scr'+'ipt></script>X" | <body> #data <style><!--<style></style>--></style> #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. Line: 1 Col: 37 Unexpected end tag (style). #document | <html> | <head> | <style> | "<!--<style>" | <body> | "-->" #data <style><!--</style>X #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. #document | <html> | <head> | <style> | "<!--" | <body> | "X" #data <style><!--...</style>...--></style> #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. Line: 1 Col: 36 Unexpected end tag (style). #document | <html> | <head> | <style> | "<!--..." | <body> | "...-->" #data <style><!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style></style>X #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. #document | <html> | <head> | <style> | "<!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style>" | <body> | "X" #data <style><!--...<style><!--...--!></style>--></style> #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. Line: 1 Col: 51 Unexpected end tag (style). #document | <html> | <head> | <style> | "<!--...<style><!--...--!>" | <body> | "-->" #data <style><!--...</style><!-- --><style>@import ...</style> #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. #document | <html> | <head> | <style> | "<!--..." | <!-- --> | <style> | "@import ..." | <body> #data <style>...<style><!--...</style><!-- --></style> #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. Line: 1 Col: 48 Unexpected end tag (style). #document | <html> | <head> | <style> | "...<style><!--..." | <!-- --> | <body> #data <style>...<!--[if IE]><style>...</style>X #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. #document | <html> | <head> | <style> | "...<!--[if IE]><style>..." | <body> | "X" #data <title><!--<title></title>--></title> #errors Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. Line: 1 Col: 37 Unexpected end tag (title). #document | <html> | <head> | <title> | "<!--<title>" | <body> | "-->" #data <title>&lt;/title></title> #errors Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. #document | <html> | <head> | <title> | "</title>" | <body> #data <title>foo/title><link></head><body>X #errors Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. Line: 1 Col: 37 Unexpected end of file. Expected end tag (title). #document | <html> | <head> | <title> | "foo/title><link></head><body>X" | <body> #data <noscript><!--<noscript></noscript>--></noscript> #errors Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE. Line: 1 Col: 49 Unexpected end tag (noscript). #document | <html> | <head> | <noscript> | "<!--<noscript>" | <body> | "-->" #data <noscript><!--</noscript>X<noscript>--></noscript> #errors Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE. #document | <html> | <head> | <noscript> | "<!--" | <body> | "X" | <noscript> | "-->" #data <noscript><iframe></noscript>X #errors Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE. #document | <html> | <head> | <noscript> | "<iframe>" | <body> | "X" #data <noframes><!--<noframes></noframes>--></noframes> #errors Line: 1 Col: 10 Unexpected start tag (noframes). Expected DOCTYPE. Line: 1 Col: 49 Unexpected end tag (noframes). #document | <html> | <head> | <noframes> | "<!--<noframes>" | <body> | "-->" #data <noframes><body><script><!--...</script></body></noframes></html> #errors Line: 1 Col: 10 Unexpected start tag (noframes). Expected DOCTYPE. #document | <html> | <head> | <noframes> | "<body><script><!--...</script></body>" | <body> #data <textarea><!--<textarea></textarea>--></textarea> #errors Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. Line: 1 Col: 49 Unexpected end tag (textarea). #document | <html> | <head> | <body> | <textarea> | "<!--<textarea>" | "-->" #data <textarea>&lt;/textarea></textarea> #errors Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. #document | <html> | <head> | <body> | <textarea> | "</textarea>" #data <iframe><!--<iframe></iframe>--></iframe> #errors Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE. Line: 1 Col: 41 Unexpected end tag (iframe). #document | <html> | <head> | <body> | <iframe> | "<!--<iframe>" | "-->" #data <iframe>...<!--X->...<!--/X->...</iframe> #errors Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE. #document | <html> | <head> | <body> | <iframe> | "...<!--X->...<!--/X->..." #data <xmp><!--<xmp></xmp>--></xmp> #errors Line: 1 Col: 5 Unexpected start tag (xmp). Expected DOCTYPE. Line: 1 Col: 29 Unexpected end tag (xmp). #document | <html> | <head> | <body> | <xmp> | "<!--<xmp>" | "-->" #data <noembed><!--<noembed></noembed>--></noembed> #errors Line: 1 Col: 9 Unexpected start tag (noembed). Expected DOCTYPE. Line: 1 Col: 45 Unexpected end tag (noembed). #document | <html> | <head> | <body> | <noembed> | "<!--<noembed>" | "-->" #data <!doctype html><table> #errors Line 2 Col 0 Unexpected end of file. Expected table content. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | " " #data <!doctype html><table><td><span><font></span><span> #errors Line 1 Col 26 Unexpected table cell start tag (td) in the table body phase. Line 1 Col 45 Unexpected end tag (span). Line 1 Col 51 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <span> | <font> | <font> | <span> #data <!doctype html><form><table></form><form></table></form> #errors 35: Stray end tag “formâ€. 41: Start tag “form†seen in “tableâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <form> | <table> | <form> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/adoption02.dat�������������������0000644�0000153�0000161�00000000571�12321735652�027350� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <b>1<i>2<p>3</b>4 #errors #document | <html> | <head> | <body> | <b> | "1" | <i> | "2" | <i> | <p> | <b> | "3" | "4" #data <a><div><style></style><address><a> #errors #document | <html> | <head> | <body> | <a> | <div> | <a> | <style> | <address> | <a> | <a> ���������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests9.dat�����������������������0000644�0000153�0000161�00000027010�12321735652�026621� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html><math></math> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> #data <!DOCTYPE html><body><math></math> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> #data <!DOCTYPE html><math><mi> #errors 25: End of file in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mi> #data <!DOCTYPE html><math><annotation-xml><svg><u> #errors 45: HTML start tag “u†in a foreign namespace context. 45: End of file seen and there were open elements. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math annotation-xml> | <svg svg> | <u> #data <!DOCTYPE html><body><select><math></math></select> #errors Line: 1 Col: 35 Unexpected start tag token (math) in the select phase. Ignored. Line: 1 Col: 42 Unexpected end tag (math) in the select phase. Ignored. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!DOCTYPE html><body><select><option><math></math></option></select> #errors Line: 1 Col: 43 Unexpected start tag token (math) in the select phase. Ignored. Line: 1 Col: 50 Unexpected end tag (math) in the select phase. Ignored. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <option> #data <!DOCTYPE html><body><table><math></math></table> #errors Line: 1 Col: 34 Unexpected start tag (math) in table context caused voodoo mode. Line: 1 Col: 41 Unexpected end tag (math) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <table> #data <!DOCTYPE html><body><table><math><mi>foo</mi></math></table> #errors Line: 1 Col: 34 Unexpected start tag (math) in table context caused voodoo mode. Line: 1 Col: 46 Unexpected end tag (mi) in table context caused voodoo mode. Line: 1 Col: 53 Unexpected end tag (math) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mi> | "foo" | <table> #data <!DOCTYPE html><body><table><math><mi>foo</mi><mi>bar</mi></math></table> #errors Line: 1 Col: 34 Unexpected start tag (math) in table context caused voodoo mode. Line: 1 Col: 46 Unexpected end tag (mi) in table context caused voodoo mode. Line: 1 Col: 58 Unexpected end tag (mi) in table context caused voodoo mode. Line: 1 Col: 65 Unexpected end tag (math) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mi> | "foo" | <math mi> | "bar" | <table> #data <!DOCTYPE html><body><table><tbody><math><mi>foo</mi><mi>bar</mi></math></tbody></table> #errors Line: 1 Col: 41 Unexpected start tag (math) in table context caused voodoo mode. Line: 1 Col: 53 Unexpected end tag (mi) in table context caused voodoo mode. Line: 1 Col: 65 Unexpected end tag (mi) in table context caused voodoo mode. Line: 1 Col: 72 Unexpected end tag (math) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mi> | "foo" | <math mi> | "bar" | <table> | <tbody> #data <!DOCTYPE html><body><table><tbody><tr><math><mi>foo</mi><mi>bar</mi></math></tr></tbody></table> #errors Line: 1 Col: 45 Unexpected start tag (math) in table context caused voodoo mode. Line: 1 Col: 57 Unexpected end tag (mi) in table context caused voodoo mode. Line: 1 Col: 69 Unexpected end tag (mi) in table context caused voodoo mode. Line: 1 Col: 76 Unexpected end tag (math) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mi> | "foo" | <math mi> | "bar" | <table> | <tbody> | <tr> #data <!DOCTYPE html><body><table><tbody><tr><td><math><mi>foo</mi><mi>bar</mi></math></td></tr></tbody></table> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <math math> | <math mi> | "foo" | <math mi> | "bar" #data <!DOCTYPE html><body><table><tbody><tr><td><math><mi>foo</mi><mi>bar</mi></math><p>baz</td></tr></tbody></table> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <math math> | <math mi> | "foo" | <math mi> | "bar" | <p> | "baz" #data <!DOCTYPE html><body><table><caption><math><mi>foo</mi><mi>bar</mi></math><p>baz</caption></table> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <math math> | <math mi> | "foo" | <math mi> | "bar" | <p> | "baz" #data <!DOCTYPE html><body><table><caption><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux #errors Line: 1 Col: 70 HTML start tag "p" in a foreign namespace context. Line: 1 Col: 81 Unexpected end table tag in caption. Generates implied end caption. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <math math> | <math mi> | "foo" | <math mi> | "bar" | <p> | "baz" | <p> | "quux" #data <!DOCTYPE html><body><table><caption><math><mi>foo</mi><mi>bar</mi>baz</table><p>quux #errors Line: 1 Col: 78 Unexpected end table tag in caption. Generates implied end caption. Line: 1 Col: 78 Unexpected end tag (caption). Missing end tag (math). #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <math math> | <math mi> | "foo" | <math mi> | "bar" | "baz" | <p> | "quux" #data <!DOCTYPE html><body><table><colgroup><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux #errors Line: 1 Col: 44 Unexpected start tag (math) in table context caused voodoo mode. Line: 1 Col: 56 Unexpected end tag (mi) in table context caused voodoo mode. Line: 1 Col: 68 Unexpected end tag (mi) in table context caused voodoo mode. Line: 1 Col: 71 HTML start tag "p" in a foreign namespace context. Line: 1 Col: 71 Unexpected start tag (p) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mi> | "foo" | <math mi> | "bar" | <p> | "baz" | <table> | <colgroup> | <p> | "quux" #data <!DOCTYPE html><body><table><tr><td><select><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux #errors Line: 1 Col: 50 Unexpected start tag token (math) in the select phase. Ignored. Line: 1 Col: 54 Unexpected start tag token (mi) in the select phase. Ignored. Line: 1 Col: 62 Unexpected end tag (mi) in the select phase. Ignored. Line: 1 Col: 66 Unexpected start tag token (mi) in the select phase. Ignored. Line: 1 Col: 74 Unexpected end tag (mi) in the select phase. Ignored. Line: 1 Col: 77 Unexpected start tag token (p) in the select phase. Ignored. Line: 1 Col: 88 Unexpected table element end tag (tables) in the select in table phase. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <select> | "foobarbaz" | <p> | "quux" #data <!DOCTYPE html><body><table><select><math><mi>foo</mi><mi>bar</mi><p>baz</table><p>quux #errors Line: 1 Col: 36 Unexpected start tag (select) in table context caused voodoo mode. Line: 1 Col: 42 Unexpected start tag token (math) in the select phase. Ignored. Line: 1 Col: 46 Unexpected start tag token (mi) in the select phase. Ignored. Line: 1 Col: 54 Unexpected end tag (mi) in the select phase. Ignored. Line: 1 Col: 58 Unexpected start tag token (mi) in the select phase. Ignored. Line: 1 Col: 66 Unexpected end tag (mi) in the select phase. Ignored. Line: 1 Col: 69 Unexpected start tag token (p) in the select phase. Ignored. Line: 1 Col: 80 Unexpected table element end tag (tables) in the select in table phase. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | "foobarbaz" | <table> | <p> | "quux" #data <!DOCTYPE html><body></body></html><math><mi>foo</mi><mi>bar</mi><p>baz #errors Line: 1 Col: 41 Unexpected start tag (math). Line: 1 Col: 68 HTML start tag "p" in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mi> | "foo" | <math mi> | "bar" | <p> | "baz" #data <!DOCTYPE html><body></body><math><mi>foo</mi><mi>bar</mi><p>baz #errors Line: 1 Col: 34 Unexpected start tag token (math) in the after body phase. Line: 1 Col: 61 HTML start tag "p" in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mi> | "foo" | <math mi> | "bar" | <p> | "baz" #data <!DOCTYPE html><frameset><math><mi></mi><mi></mi><p><span> #errors Line: 1 Col: 31 Unexpected start tag token (math) in the frameset phase. Ignored. Line: 1 Col: 35 Unexpected start tag token (mi) in the frameset phase. Ignored. Line: 1 Col: 40 Unexpected end tag token (mi) in the frameset phase. Ignored. Line: 1 Col: 44 Unexpected start tag token (mi) in the frameset phase. Ignored. Line: 1 Col: 49 Unexpected end tag token (mi) in the frameset phase. Ignored. Line: 1 Col: 52 Unexpected start tag token (p) in the frameset phase. Ignored. Line: 1 Col: 58 Unexpected start tag token (span) in the frameset phase. Ignored. Line: 1 Col: 58 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!DOCTYPE html><frameset></frameset><math><mi></mi><mi></mi><p><span> #errors Line: 1 Col: 42 Unexpected start tag (math) in the after frameset phase. Ignored. Line: 1 Col: 46 Unexpected start tag (mi) in the after frameset phase. Ignored. Line: 1 Col: 51 Unexpected end tag (mi) in the after frameset phase. Ignored. Line: 1 Col: 55 Unexpected start tag (mi) in the after frameset phase. Ignored. Line: 1 Col: 60 Unexpected end tag (mi) in the after frameset phase. Ignored. Line: 1 Col: 63 Unexpected start tag (p) in the after frameset phase. Ignored. Line: 1 Col: 69 Unexpected start tag (span) in the after frameset phase. Ignored. #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!DOCTYPE html><body xlink:href=foo><math xlink:href=foo></math> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | xlink:href="foo" | <math math> | xlink href="foo" #data <!DOCTYPE html><body xlink:href=foo xml:lang=en><math><mi xml:lang=en xlink:href=foo></mi></math> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | xlink:href="foo" | xml:lang="en" | <math math> | <math mi> | xlink href="foo" | xml lang="en" #data <!DOCTYPE html><body xlink:href=foo xml:lang=en><math><mi xml:lang=en xlink:href=foo /></math> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | xlink:href="foo" | xml:lang="en" | <math math> | <math mi> | xlink href="foo" | xml lang="en" #data <!DOCTYPE html><body xlink:href=foo xml:lang=en><math><mi xml:lang=en xlink:href=foo />bar</math> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | xlink:href="foo" | xml:lang="en" | <math math> | <math mi> | xlink href="foo" | xml lang="en" | "bar" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests20.dat����������������������0000644�0000153�0000161�00000013650�12321735652�026677� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!doctype html><p><button><button> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <button> #data <!doctype html><p><button><address> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <address> #data <!doctype html><p><button><blockquote> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <blockquote> #data <!doctype html><p><button><menu> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <menu> #data <!doctype html><p><button><p> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <p> #data <!doctype html><p><button><ul> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <ul> #data <!doctype html><p><button><h1> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <h1> #data <!doctype html><p><button><h6> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <h6> #data <!doctype html><p><button><listing> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <listing> #data <!doctype html><p><button><pre> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <pre> #data <!doctype html><p><button><form> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <form> #data <!doctype html><p><button><li> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <li> #data <!doctype html><p><button><dd> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <dd> #data <!doctype html><p><button><dt> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <dt> #data <!doctype html><p><button><plaintext> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <plaintext> #data <!doctype html><p><button><table> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <table> #data <!doctype html><p><button><hr> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <hr> #data <!doctype html><p><button><xmp> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <xmp> #data <!doctype html><p><button></p> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <button> | <p> #data <!doctype html><address><button></address>a #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <address> | <button> | "a" #data <!doctype html><address><button></address>a #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <address> | <button> | "a" #data <p><table></p> #errors #document | <html> | <head> | <body> | <p> | <p> | <table> #data <!doctype html><svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> #data <!doctype html><p><figcaption> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <figcaption> #data <!doctype html><p><summary> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <summary> #data <!doctype html><form><table><form> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <form> | <table> #data <!doctype html><table><form><form> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <form> #data <!doctype html><table><form></table><form> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <form> #data <!doctype html><svg><foreignObject><p> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg foreignObject> | <p> #data <!doctype html><svg><title>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg title> | "abc" #data <option><span><option> #errors #document | <html> | <head> | <body> | <option> | <span> | <option> #data <option><option> #errors #document | <html> | <head> | <body> | <option> | <option> #data <math><annotation-xml><div> #errors #document | <html> | <head> | <body> | <math math> | <math annotation-xml> | <div> #data <math><annotation-xml encoding="application/svg+xml"><div> #errors #document | <html> | <head> | <body> | <math math> | <math annotation-xml> | encoding="application/svg+xml" | <div> #data <math><annotation-xml encoding="application/xhtml+xml"><div> #errors #document | <html> | <head> | <body> | <math math> | <math annotation-xml> | encoding="application/xhtml+xml" | <div> #data <math><annotation-xml encoding="aPPlication/xhtmL+xMl"><div> #errors #document | <html> | <head> | <body> | <math math> | <math annotation-xml> | encoding="aPPlication/xhtmL+xMl" | <div> #data <math><annotation-xml encoding="text/html"><div> #errors #document | <html> | <head> | <body> | <math math> | <math annotation-xml> | encoding="text/html" | <div> #data <math><annotation-xml encoding="Text/htmL"><div> #errors #document | <html> | <head> | <body> | <math math> | <math annotation-xml> | encoding="Text/htmL" | <div> #data <math><annotation-xml encoding=" text/html "><div> #errors #document | <html> | <head> | <body> | <math math> | <math annotation-xml> | encoding=" text/html " | <div> ����������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests22.dat����������������������0000644�0000153�0000161�00000006341�12321735652�026700� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <a><b><big><em><strong><div>X</a> #errors #document | <html> | <head> | <body> | <a> | <b> | <big> | <em> | <strong> | <big> | <em> | <strong> | <div> | <a> | "X" #data <a><b><div id=1><div id=2><div id=3><div id=4><div id=5><div id=6><div id=7><div id=8>A</a> #errors #document | <html> | <head> | <body> | <a> | <b> | <b> | <div> | id="1" | <a> | <div> | id="2" | <a> | <div> | id="3" | <a> | <div> | id="4" | <a> | <div> | id="5" | <a> | <div> | id="6" | <a> | <div> | id="7" | <a> | <div> | id="8" | <a> | "A" #data <a><b><div id=1><div id=2><div id=3><div id=4><div id=5><div id=6><div id=7><div id=8><div id=9>A</a> #errors #document | <html> | <head> | <body> | <a> | <b> | <b> | <div> | id="1" | <a> | <div> | id="2" | <a> | <div> | id="3" | <a> | <div> | id="4" | <a> | <div> | id="5" | <a> | <div> | id="6" | <a> | <div> | id="7" | <a> | <div> | id="8" | <a> | <div> | id="9" | "A" #data <a><b><div id=1><div id=2><div id=3><div id=4><div id=5><div id=6><div id=7><div id=8><div id=9><div id=10>A</a> #errors #document | <html> | <head> | <body> | <a> | <b> | <b> | <div> | id="1" | <a> | <div> | id="2" | <a> | <div> | id="3" | <a> | <div> | id="4" | <a> | <div> | id="5" | <a> | <div> | id="6" | <a> | <div> | id="7" | <a> | <div> | id="8" | <a> | <div> | id="9" | <div> | id="10" | "A" #data <cite><b><cite><i><cite><i><cite><i><div>X</b>TEST #errors Line: 1 Col: 6 Unexpected start tag (cite). Expected DOCTYPE. Line: 1 Col: 46 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 50 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <cite> | <b> | <cite> | <i> | <cite> | <i> | <cite> | <i> | <i> | <i> | <div> | <b> | "X" | "TEST" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests21.dat����������������������0000644�0000153�0000161�00000004731�12321735652�026700� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <svg><![CDATA[foo]]> #errors #document | <html> | <head> | <body> | <svg svg> | "foo" #data <math><![CDATA[foo]]> #errors #document | <html> | <head> | <body> | <math math> | "foo" #data <div><![CDATA[foo]]> #errors #document | <html> | <head> | <body> | <div> | <!-- [CDATA[foo]] --> #data <svg><![CDATA[foo #errors #document | <html> | <head> | <body> | <svg svg> | "foo" #data <svg><![CDATA[foo #errors #document | <html> | <head> | <body> | <svg svg> | "foo" #data <svg><![CDATA[ #errors #document | <html> | <head> | <body> | <svg svg> #data <svg><![CDATA[]]> #errors #document | <html> | <head> | <body> | <svg svg> #data <svg><![CDATA[]] >]]> #errors #document | <html> | <head> | <body> | <svg svg> | "]] >" #data <svg><![CDATA[]] >]]> #errors #document | <html> | <head> | <body> | <svg svg> | "]] >" #data <svg><![CDATA[]] #errors #document | <html> | <head> | <body> | <svg svg> | "]]" #data <svg><![CDATA[] #errors #document | <html> | <head> | <body> | <svg svg> | "]" #data <svg><![CDATA[]>a #errors #document | <html> | <head> | <body> | <svg svg> | "]>a" #data <svg><foreignObject><div><![CDATA[foo]]> #errors #document | <html> | <head> | <body> | <svg svg> | <svg foreignObject> | <div> | <!-- [CDATA[foo]] --> #data <svg><![CDATA[<svg>]]> #errors #document | <html> | <head> | <body> | <svg svg> | "<svg>" #data <svg><![CDATA[</svg>a]]> #errors #document | <html> | <head> | <body> | <svg svg> | "</svg>a" #data <svg><![CDATA[<svg>a #errors #document | <html> | <head> | <body> | <svg svg> | "<svg>a" #data <svg><![CDATA[</svg>a #errors #document | <html> | <head> | <body> | <svg svg> | "</svg>a" #data <svg><![CDATA[<svg>]]><path> #errors #document | <html> | <head> | <body> | <svg svg> | "<svg>" | <svg path> #data <svg><![CDATA[<svg>]]></path> #errors #document | <html> | <head> | <body> | <svg svg> | "<svg>" #data <svg><![CDATA[<svg>]]><!--path--> #errors #document | <html> | <head> | <body> | <svg svg> | "<svg>" | <!-- path --> #data <svg><![CDATA[<svg>]]>path #errors #document | <html> | <head> | <body> | <svg svg> | "<svg>path" #data <svg><![CDATA[<!--svg-->]]> #errors #document | <html> | <head> | <body> | <svg svg> | "<!--svg-->" ���������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests23.dat����������������������0000644�0000153�0000161�00000005762�12321735652�026707� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <p><font size=4><font color=red><font size=4><font size=4><font size=4><font size=4><font size=4><font color=red><p>X #errors 3: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. 116: Unclosed elements. 117: End of file seen and there were open elements. #document | <html> | <head> | <body> | <p> | <font> | size="4" | <font> | color="red" | <font> | size="4" | <font> | size="4" | <font> | size="4" | <font> | size="4" | <font> | size="4" | <font> | color="red" | <p> | <font> | color="red" | <font> | size="4" | <font> | size="4" | <font> | size="4" | <font> | color="red" | "X" #data <p><font size=4><font size=4><font size=4><font size=4><p>X #errors #document | <html> | <head> | <body> | <p> | <font> | size="4" | <font> | size="4" | <font> | size="4" | <font> | size="4" | <p> | <font> | size="4" | <font> | size="4" | <font> | size="4" | "X" #data <p><font size=4><font size=4><font size=4><font size="5"><font size=4><p>X #errors #document | <html> | <head> | <body> | <p> | <font> | size="4" | <font> | size="4" | <font> | size="4" | <font> | size="5" | <font> | size="4" | <p> | <font> | size="4" | <font> | size="4" | <font> | size="5" | <font> | size="4" | "X" #data <p><font size=4 id=a><font size=4 id=b><font size=4><font size=4><p>X #errors #document | <html> | <head> | <body> | <p> | <font> | id="a" | size="4" | <font> | id="b" | size="4" | <font> | size="4" | <font> | size="4" | <p> | <font> | id="a" | size="4" | <font> | id="b" | size="4" | <font> | size="4" | <font> | size="4" | "X" #data <p><b id=a><b id=a><b id=a><b><object><b id=a><b id=a>X</object><p>Y #errors #document | <html> | <head> | <body> | <p> | <b> | id="a" | <b> | id="a" | <b> | id="a" | <b> | <object> | <b> | id="a" | <b> | id="a" | "X" | <p> | <b> | id="a" | <b> | id="a" | <b> | id="a" | <b> | "Y" ��������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/comments01.dat�������������������0000644�0000153�0000161�00000003045�12321735652�027356� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data FOO<!-- BAR -->BAZ #errors #document | <html> | <head> | <body> | "FOO" | <!-- BAR --> | "BAZ" #data FOO<!-- BAR --!>BAZ #errors #document | <html> | <head> | <body> | "FOO" | <!-- BAR --> | "BAZ" #data FOO<!-- BAR -- >BAZ #errors #document | <html> | <head> | <body> | "FOO" | <!-- BAR -- >BAZ --> #data FOO<!-- BAR -- <QUX> -- MUX -->BAZ #errors #document | <html> | <head> | <body> | "FOO" | <!-- BAR -- <QUX> -- MUX --> | "BAZ" #data FOO<!-- BAR -- <QUX> -- MUX --!>BAZ #errors #document | <html> | <head> | <body> | "FOO" | <!-- BAR -- <QUX> -- MUX --> | "BAZ" #data FOO<!-- BAR -- <QUX> -- MUX -- >BAZ #errors #document | <html> | <head> | <body> | "FOO" | <!-- BAR -- <QUX> -- MUX -- >BAZ --> #data FOO<!---->BAZ #errors #document | <html> | <head> | <body> | "FOO" | <!-- --> | "BAZ" #data FOO<!--->BAZ #errors #document | <html> | <head> | <body> | "FOO" | <!-- --> | "BAZ" #data FOO<!-->BAZ #errors #document | <html> | <head> | <body> | "FOO" | <!-- --> | "BAZ" #data <?xml version="1.0">Hi #errors #document | <!-- ?xml version="1.0" --> | <html> | <head> | <body> | "Hi" #data <?xml version="1.0"> #errors #document | <!-- ?xml version="1.0" --> | <html> | <head> | <body> #data <?xml version #errors #document | <!-- ?xml version --> | <html> | <head> | <body> #data FOO<!----->BAZ #errors #document | <html> | <head> | <body> | "FOO" | <!-- - --> | "BAZ" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests11.dat����������������������0000644�0000153�0000161�00000041375�12321735652�026704� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html><body><svg attributeName='' attributeType='' baseFrequency='' baseProfile='' calcMode='' clipPathUnits='' contentScriptType='' contentStyleType='' diffuseConstant='' edgeMode='' externalResourcesRequired='' filterRes='' filterUnits='' glyphRef='' gradientTransform='' gradientUnits='' kernelMatrix='' kernelUnitLength='' keyPoints='' keySplines='' keyTimes='' lengthAdjust='' limitingConeAngle='' markerHeight='' markerUnits='' markerWidth='' maskContentUnits='' maskUnits='' numOctaves='' pathLength='' patternContentUnits='' patternTransform='' patternUnits='' pointsAtX='' pointsAtY='' pointsAtZ='' preserveAlpha='' preserveAspectRatio='' primitiveUnits='' refX='' refY='' repeatCount='' repeatDur='' requiredExtensions='' requiredFeatures='' specularConstant='' specularExponent='' spreadMethod='' startOffset='' stdDeviation='' stitchTiles='' surfaceScale='' systemLanguage='' tableValues='' targetX='' targetY='' textLength='' viewBox='' viewTarget='' xChannelSelector='' yChannelSelector='' zoomAndPan=''></svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | attributeName="" | attributeType="" | baseFrequency="" | baseProfile="" | calcMode="" | clipPathUnits="" | contentScriptType="" | contentStyleType="" | diffuseConstant="" | edgeMode="" | externalResourcesRequired="" | filterRes="" | filterUnits="" | glyphRef="" | gradientTransform="" | gradientUnits="" | kernelMatrix="" | kernelUnitLength="" | keyPoints="" | keySplines="" | keyTimes="" | lengthAdjust="" | limitingConeAngle="" | markerHeight="" | markerUnits="" | markerWidth="" | maskContentUnits="" | maskUnits="" | numOctaves="" | pathLength="" | patternContentUnits="" | patternTransform="" | patternUnits="" | pointsAtX="" | pointsAtY="" | pointsAtZ="" | preserveAlpha="" | preserveAspectRatio="" | primitiveUnits="" | refX="" | refY="" | repeatCount="" | repeatDur="" | requiredExtensions="" | requiredFeatures="" | specularConstant="" | specularExponent="" | spreadMethod="" | startOffset="" | stdDeviation="" | stitchTiles="" | surfaceScale="" | systemLanguage="" | tableValues="" | targetX="" | targetY="" | textLength="" | viewBox="" | viewTarget="" | xChannelSelector="" | yChannelSelector="" | zoomAndPan="" #data <!DOCTYPE html><BODY><SVG ATTRIBUTENAME='' ATTRIBUTETYPE='' BASEFREQUENCY='' BASEPROFILE='' CALCMODE='' CLIPPATHUNITS='' CONTENTSCRIPTTYPE='' CONTENTSTYLETYPE='' DIFFUSECONSTANT='' EDGEMODE='' EXTERNALRESOURCESREQUIRED='' FILTERRES='' FILTERUNITS='' GLYPHREF='' GRADIENTTRANSFORM='' GRADIENTUNITS='' KERNELMATRIX='' KERNELUNITLENGTH='' KEYPOINTS='' KEYSPLINES='' KEYTIMES='' LENGTHADJUST='' LIMITINGCONEANGLE='' MARKERHEIGHT='' MARKERUNITS='' MARKERWIDTH='' MASKCONTENTUNITS='' MASKUNITS='' NUMOCTAVES='' PATHLENGTH='' PATTERNCONTENTUNITS='' PATTERNTRANSFORM='' PATTERNUNITS='' POINTSATX='' POINTSATY='' POINTSATZ='' PRESERVEALPHA='' PRESERVEASPECTRATIO='' PRIMITIVEUNITS='' REFX='' REFY='' REPEATCOUNT='' REPEATDUR='' REQUIREDEXTENSIONS='' REQUIREDFEATURES='' SPECULARCONSTANT='' SPECULAREXPONENT='' SPREADMETHOD='' STARTOFFSET='' STDDEVIATION='' STITCHTILES='' SURFACESCALE='' SYSTEMLANGUAGE='' TABLEVALUES='' TARGETX='' TARGETY='' TEXTLENGTH='' VIEWBOX='' VIEWTARGET='' XCHANNELSELECTOR='' YCHANNELSELECTOR='' ZOOMANDPAN=''></SVG> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | attributeName="" | attributeType="" | baseFrequency="" | baseProfile="" | calcMode="" | clipPathUnits="" | contentScriptType="" | contentStyleType="" | diffuseConstant="" | edgeMode="" | externalResourcesRequired="" | filterRes="" | filterUnits="" | glyphRef="" | gradientTransform="" | gradientUnits="" | kernelMatrix="" | kernelUnitLength="" | keyPoints="" | keySplines="" | keyTimes="" | lengthAdjust="" | limitingConeAngle="" | markerHeight="" | markerUnits="" | markerWidth="" | maskContentUnits="" | maskUnits="" | numOctaves="" | pathLength="" | patternContentUnits="" | patternTransform="" | patternUnits="" | pointsAtX="" | pointsAtY="" | pointsAtZ="" | preserveAlpha="" | preserveAspectRatio="" | primitiveUnits="" | refX="" | refY="" | repeatCount="" | repeatDur="" | requiredExtensions="" | requiredFeatures="" | specularConstant="" | specularExponent="" | spreadMethod="" | startOffset="" | stdDeviation="" | stitchTiles="" | surfaceScale="" | systemLanguage="" | tableValues="" | targetX="" | targetY="" | textLength="" | viewBox="" | viewTarget="" | xChannelSelector="" | yChannelSelector="" | zoomAndPan="" #data <!DOCTYPE html><body><svg attributename='' attributetype='' basefrequency='' baseprofile='' calcmode='' clippathunits='' contentscripttype='' contentstyletype='' diffuseconstant='' edgemode='' externalresourcesrequired='' filterres='' filterunits='' glyphref='' gradienttransform='' gradientunits='' kernelmatrix='' kernelunitlength='' keypoints='' keysplines='' keytimes='' lengthadjust='' limitingconeangle='' markerheight='' markerunits='' markerwidth='' maskcontentunits='' maskunits='' numoctaves='' pathlength='' patterncontentunits='' patterntransform='' patternunits='' pointsatx='' pointsaty='' pointsatz='' preservealpha='' preserveaspectratio='' primitiveunits='' refx='' refy='' repeatcount='' repeatdur='' requiredextensions='' requiredfeatures='' specularconstant='' specularexponent='' spreadmethod='' startoffset='' stddeviation='' stitchtiles='' surfacescale='' systemlanguage='' tablevalues='' targetx='' targety='' textlength='' viewbox='' viewtarget='' xchannelselector='' ychannelselector='' zoomandpan=''></svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | attributeName="" | attributeType="" | baseFrequency="" | baseProfile="" | calcMode="" | clipPathUnits="" | contentScriptType="" | contentStyleType="" | diffuseConstant="" | edgeMode="" | externalResourcesRequired="" | filterRes="" | filterUnits="" | glyphRef="" | gradientTransform="" | gradientUnits="" | kernelMatrix="" | kernelUnitLength="" | keyPoints="" | keySplines="" | keyTimes="" | lengthAdjust="" | limitingConeAngle="" | markerHeight="" | markerUnits="" | markerWidth="" | maskContentUnits="" | maskUnits="" | numOctaves="" | pathLength="" | patternContentUnits="" | patternTransform="" | patternUnits="" | pointsAtX="" | pointsAtY="" | pointsAtZ="" | preserveAlpha="" | preserveAspectRatio="" | primitiveUnits="" | refX="" | refY="" | repeatCount="" | repeatDur="" | requiredExtensions="" | requiredFeatures="" | specularConstant="" | specularExponent="" | spreadMethod="" | startOffset="" | stdDeviation="" | stitchTiles="" | surfaceScale="" | systemLanguage="" | tableValues="" | targetX="" | targetY="" | textLength="" | viewBox="" | viewTarget="" | xChannelSelector="" | yChannelSelector="" | zoomAndPan="" #data <!DOCTYPE html><body><math attributeName='' attributeType='' baseFrequency='' baseProfile='' calcMode='' clipPathUnits='' contentScriptType='' contentStyleType='' diffuseConstant='' edgeMode='' externalResourcesRequired='' filterRes='' filterUnits='' glyphRef='' gradientTransform='' gradientUnits='' kernelMatrix='' kernelUnitLength='' keyPoints='' keySplines='' keyTimes='' lengthAdjust='' limitingConeAngle='' markerHeight='' markerUnits='' markerWidth='' maskContentUnits='' maskUnits='' numOctaves='' pathLength='' patternContentUnits='' patternTransform='' patternUnits='' pointsAtX='' pointsAtY='' pointsAtZ='' preserveAlpha='' preserveAspectRatio='' primitiveUnits='' refX='' refY='' repeatCount='' repeatDur='' requiredExtensions='' requiredFeatures='' specularConstant='' specularExponent='' spreadMethod='' startOffset='' stdDeviation='' stitchTiles='' surfaceScale='' systemLanguage='' tableValues='' targetX='' targetY='' textLength='' viewBox='' viewTarget='' xChannelSelector='' yChannelSelector='' zoomAndPan=''></math> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | attributename="" | attributetype="" | basefrequency="" | baseprofile="" | calcmode="" | clippathunits="" | contentscripttype="" | contentstyletype="" | diffuseconstant="" | edgemode="" | externalresourcesrequired="" | filterres="" | filterunits="" | glyphref="" | gradienttransform="" | gradientunits="" | kernelmatrix="" | kernelunitlength="" | keypoints="" | keysplines="" | keytimes="" | lengthadjust="" | limitingconeangle="" | markerheight="" | markerunits="" | markerwidth="" | maskcontentunits="" | maskunits="" | numoctaves="" | pathlength="" | patterncontentunits="" | patterntransform="" | patternunits="" | pointsatx="" | pointsaty="" | pointsatz="" | preservealpha="" | preserveaspectratio="" | primitiveunits="" | refx="" | refy="" | repeatcount="" | repeatdur="" | requiredextensions="" | requiredfeatures="" | specularconstant="" | specularexponent="" | spreadmethod="" | startoffset="" | stddeviation="" | stitchtiles="" | surfacescale="" | systemlanguage="" | tablevalues="" | targetx="" | targety="" | textlength="" | viewbox="" | viewtarget="" | xchannelselector="" | ychannelselector="" | zoomandpan="" #data <!DOCTYPE html><body><svg><altGlyph /><altGlyphDef /><altGlyphItem /><animateColor /><animateMotion /><animateTransform /><clipPath /><feBlend /><feColorMatrix /><feComponentTransfer /><feComposite /><feConvolveMatrix /><feDiffuseLighting /><feDisplacementMap /><feDistantLight /><feFlood /><feFuncA /><feFuncB /><feFuncG /><feFuncR /><feGaussianBlur /><feImage /><feMerge /><feMergeNode /><feMorphology /><feOffset /><fePointLight /><feSpecularLighting /><feSpotLight /><feTile /><feTurbulence /><foreignObject /><glyphRef /><linearGradient /><radialGradient /><textPath /></svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg altGlyph> | <svg altGlyphDef> | <svg altGlyphItem> | <svg animateColor> | <svg animateMotion> | <svg animateTransform> | <svg clipPath> | <svg feBlend> | <svg feColorMatrix> | <svg feComponentTransfer> | <svg feComposite> | <svg feConvolveMatrix> | <svg feDiffuseLighting> | <svg feDisplacementMap> | <svg feDistantLight> | <svg feFlood> | <svg feFuncA> | <svg feFuncB> | <svg feFuncG> | <svg feFuncR> | <svg feGaussianBlur> | <svg feImage> | <svg feMerge> | <svg feMergeNode> | <svg feMorphology> | <svg feOffset> | <svg fePointLight> | <svg feSpecularLighting> | <svg feSpotLight> | <svg feTile> | <svg feTurbulence> | <svg foreignObject> | <svg glyphRef> | <svg linearGradient> | <svg radialGradient> | <svg textPath> #data <!DOCTYPE html><body><svg><altglyph /><altglyphdef /><altglyphitem /><animatecolor /><animatemotion /><animatetransform /><clippath /><feblend /><fecolormatrix /><fecomponenttransfer /><fecomposite /><feconvolvematrix /><fediffuselighting /><fedisplacementmap /><fedistantlight /><feflood /><fefunca /><fefuncb /><fefuncg /><fefuncr /><fegaussianblur /><feimage /><femerge /><femergenode /><femorphology /><feoffset /><fepointlight /><fespecularlighting /><fespotlight /><fetile /><feturbulence /><foreignobject /><glyphref /><lineargradient /><radialgradient /><textpath /></svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg altGlyph> | <svg altGlyphDef> | <svg altGlyphItem> | <svg animateColor> | <svg animateMotion> | <svg animateTransform> | <svg clipPath> | <svg feBlend> | <svg feColorMatrix> | <svg feComponentTransfer> | <svg feComposite> | <svg feConvolveMatrix> | <svg feDiffuseLighting> | <svg feDisplacementMap> | <svg feDistantLight> | <svg feFlood> | <svg feFuncA> | <svg feFuncB> | <svg feFuncG> | <svg feFuncR> | <svg feGaussianBlur> | <svg feImage> | <svg feMerge> | <svg feMergeNode> | <svg feMorphology> | <svg feOffset> | <svg fePointLight> | <svg feSpecularLighting> | <svg feSpotLight> | <svg feTile> | <svg feTurbulence> | <svg foreignObject> | <svg glyphRef> | <svg linearGradient> | <svg radialGradient> | <svg textPath> #data <!DOCTYPE html><BODY><SVG><ALTGLYPH /><ALTGLYPHDEF /><ALTGLYPHITEM /><ANIMATECOLOR /><ANIMATEMOTION /><ANIMATETRANSFORM /><CLIPPATH /><FEBLEND /><FECOLORMATRIX /><FECOMPONENTTRANSFER /><FECOMPOSITE /><FECONVOLVEMATRIX /><FEDIFFUSELIGHTING /><FEDISPLACEMENTMAP /><FEDISTANTLIGHT /><FEFLOOD /><FEFUNCA /><FEFUNCB /><FEFUNCG /><FEFUNCR /><FEGAUSSIANBLUR /><FEIMAGE /><FEMERGE /><FEMERGENODE /><FEMORPHOLOGY /><FEOFFSET /><FEPOINTLIGHT /><FESPECULARLIGHTING /><FESPOTLIGHT /><FETILE /><FETURBULENCE /><FOREIGNOBJECT /><GLYPHREF /><LINEARGRADIENT /><RADIALGRADIENT /><TEXTPATH /></SVG> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg altGlyph> | <svg altGlyphDef> | <svg altGlyphItem> | <svg animateColor> | <svg animateMotion> | <svg animateTransform> | <svg clipPath> | <svg feBlend> | <svg feColorMatrix> | <svg feComponentTransfer> | <svg feComposite> | <svg feConvolveMatrix> | <svg feDiffuseLighting> | <svg feDisplacementMap> | <svg feDistantLight> | <svg feFlood> | <svg feFuncA> | <svg feFuncB> | <svg feFuncG> | <svg feFuncR> | <svg feGaussianBlur> | <svg feImage> | <svg feMerge> | <svg feMergeNode> | <svg feMorphology> | <svg feOffset> | <svg fePointLight> | <svg feSpecularLighting> | <svg feSpotLight> | <svg feTile> | <svg feTurbulence> | <svg foreignObject> | <svg glyphRef> | <svg linearGradient> | <svg radialGradient> | <svg textPath> #data <!DOCTYPE html><body><math><altGlyph /><altGlyphDef /><altGlyphItem /><animateColor /><animateMotion /><animateTransform /><clipPath /><feBlend /><feColorMatrix /><feComponentTransfer /><feComposite /><feConvolveMatrix /><feDiffuseLighting /><feDisplacementMap /><feDistantLight /><feFlood /><feFuncA /><feFuncB /><feFuncG /><feFuncR /><feGaussianBlur /><feImage /><feMerge /><feMergeNode /><feMorphology /><feOffset /><fePointLight /><feSpecularLighting /><feSpotLight /><feTile /><feTurbulence /><foreignObject /><glyphRef /><linearGradient /><radialGradient /><textPath /></math> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math altglyph> | <math altglyphdef> | <math altglyphitem> | <math animatecolor> | <math animatemotion> | <math animatetransform> | <math clippath> | <math feblend> | <math fecolormatrix> | <math fecomponenttransfer> | <math fecomposite> | <math feconvolvematrix> | <math fediffuselighting> | <math fedisplacementmap> | <math fedistantlight> | <math feflood> | <math fefunca> | <math fefuncb> | <math fefuncg> | <math fefuncr> | <math fegaussianblur> | <math feimage> | <math femerge> | <math femergenode> | <math femorphology> | <math feoffset> | <math fepointlight> | <math fespecularlighting> | <math fespotlight> | <math fetile> | <math feturbulence> | <math foreignobject> | <math glyphref> | <math lineargradient> | <math radialgradient> | <math textpath> #data <!DOCTYPE html><body><svg><solidColor /></svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg solidcolor> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/webkit02.dat���������������������0000644�0000153�0000161�00000004322�12321735652�027016� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <foo bar=qux/> #errors #document | <html> | <head> | <body> | <foo> | bar="qux/" #data <p id="status"><noscript><strong>A</strong></noscript><span>B</span></p> #errors #document | <html> | <head> | <body> | <p> | id="status" | <noscript> | "<strong>A</strong>" | <span> | "B" #data <div><sarcasm><div></div></sarcasm></div> #errors #document | <html> | <head> | <body> | <div> | <sarcasm> | <div> #data <html><body><img src="" border="0" alt="><div>A</div></body></html> #errors #document | <html> | <head> | <body> #data <table><td></tbody>A #errors #document | <html> | <head> | <body> | "A" | <table> | <tbody> | <tr> | <td> #data <table><td></thead>A #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | "A" #data <table><td></tfoot>A #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | "A" #data <table><thead><td></tbody>A #errors #document | <html> | <head> | <body> | <table> | <thead> | <tr> | <td> | "A" #data <legend>test</legend> #errors #document | <html> | <head> | <body> | <legend> | "test" #data <table><input> #errors #document | <html> | <head> | <body> | <input> | <table> #data <b><em><dcell><postfield><postfield><postfield><postfield><missing_glyph><missing_glyph><missing_glyph><missing_glyph><hkern><aside></b></em> #errors #document-fragment div #document | <b> | <em> | <dcell> | <postfield> | <postfield> | <postfield> | <postfield> | <missing_glyph> | <missing_glyph> | <missing_glyph> | <missing_glyph> | <hkern> | <aside> | <em> | <b> #data <isindex action="x"> #errors #document-fragment table #document | <form> | action="x" | <hr> | <label> | "This is a searchable index. Enter search keywords: " | <input> | name="isindex" | <hr> #data <option><XH<optgroup></optgroup> #errors #document-fragment select #document | <option> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests26.dat����������������������0000644�0000153�0000161�00000012312�12321735652�026677� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html><body><a href='#1'><nobr>1<nobr></a><br><a href='#2'><nobr>2<nobr></a><br><a href='#3'><nobr>3<nobr></a> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <a> | href="#1" | <nobr> | "1" | <nobr> | <nobr> | <br> | <a> | href="#2" | <a> | href="#2" | <nobr> | "2" | <nobr> | <nobr> | <br> | <a> | href="#3" | <a> | href="#3" | <nobr> | "3" | <nobr> #data <!DOCTYPE html><body><b><nobr>1<nobr></b><i><nobr>2<nobr></i>3 #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <b> | <nobr> | "1" | <nobr> | <nobr> | <i> | <i> | <nobr> | "2" | <nobr> | <nobr> | "3" #data <!DOCTYPE html><body><b><nobr>1<table><nobr></b><i><nobr>2<nobr></i>3 #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <b> | <nobr> | "1" | <nobr> | <i> | <i> | <nobr> | "2" | <nobr> | <nobr> | "3" | <table> #data <!DOCTYPE html><body><b><nobr>1<table><tr><td><nobr></b><i><nobr>2<nobr></i>3 #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <b> | <nobr> | "1" | <table> | <tbody> | <tr> | <td> | <nobr> | <i> | <i> | <nobr> | "2" | <nobr> | <nobr> | "3" #data <!DOCTYPE html><body><b><nobr>1<div><nobr></b><i><nobr>2<nobr></i>3 #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <b> | <nobr> | "1" | <div> | <b> | <nobr> | <nobr> | <nobr> | <i> | <i> | <nobr> | "2" | <nobr> | <nobr> | "3" #data <!DOCTYPE html><body><b><nobr>1<nobr></b><div><i><nobr>2<nobr></i>3 #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <b> | <nobr> | "1" | <nobr> | <div> | <nobr> | <i> | <i> | <nobr> | "2" | <nobr> | <nobr> | "3" #data <!DOCTYPE html><body><b><nobr>1<nobr><ins></b><i><nobr> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <b> | <nobr> | "1" | <nobr> | <ins> | <nobr> | <i> | <i> | <nobr> #data <!DOCTYPE html><body><b><nobr>1<ins><nobr></b><i>2 #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <b> | <nobr> | "1" | <ins> | <nobr> | <nobr> | <i> | "2" #data <!DOCTYPE html><body><b>1<nobr></b><i><nobr>2</i> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <b> | "1" | <nobr> | <nobr> | <i> | <i> | <nobr> | "2" #data <p><code x</code></p> #errors #document | <html> | <head> | <body> | <p> | <code> | code="" | x<="" | <code> | code="" | x<="" | " " #data <!DOCTYPE html><svg><foreignObject><p><i></p>a #errors 45: End tag “p†seen, but there were open elements. 41: Unclosed element “iâ€. 46: End of file seen and there were open elements. 35: Unclosed element “foreignObjectâ€. 20: Unclosed element “svgâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg foreignObject> | <p> | <i> | <i> | "a" #data <!DOCTYPE html><table><tr><td><svg><foreignObject><p><i></p>a #errors 56: End tag “p†seen, but there were open elements. 52: Unclosed element “iâ€. 57: End of file seen and there were open elements. 46: Unclosed element “foreignObjectâ€. 31: Unclosed element “svgâ€. 22: Unclosed element “tableâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <svg svg> | <svg foreignObject> | <p> | <i> | <i> | "a" #data <!DOCTYPE html><math><mtext><p><i></p>a #errors 38: End tag “p†seen, but there were open elements. 34: Unclosed element “iâ€. 39: End of file in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mtext> | <p> | <i> | <i> | "a" #data <!DOCTYPE html><table><tr><td><math><mtext><p><i></p>a #errors 53: End tag “p†seen, but there were open elements. 49: Unclosed element “iâ€. 54: End of file in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <math math> | <math mtext> | <p> | <i> | <i> | "a" #data <!DOCTYPE html><body><div><!/div>a #errors 29: Bogus comment. 34: End of file seen and there were open elements. 26: Unclosed element “divâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <div> | <!-- /div --> | "a" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/adoption01.dat�������������������0000644�0000153�0000161�00000004277�12321735652�027356� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <a><p></a></p> #errors #document | <html> | <head> | <body> | <a> | <p> | <a> #data <a>1<p>2</a>3</p> #errors #document | <html> | <head> | <body> | <a> | "1" | <p> | <a> | "2" | "3" #data <a>1<button>2</a>3</button> #errors #document | <html> | <head> | <body> | <a> | "1" | <button> | <a> | "2" | "3" #data <a>1<b>2</a>3</b> #errors #document | <html> | <head> | <body> | <a> | "1" | <b> | "2" | <b> | "3" #data <a>1<div>2<div>3</a>4</div>5</div> #errors #document | <html> | <head> | <body> | <a> | "1" | <div> | <a> | "2" | <div> | <a> | "3" | "4" | "5" #data <table><a>1<p>2</a>3</p> #errors #document | <html> | <head> | <body> | <a> | "1" | <p> | <a> | "2" | "3" | <table> #data <b><b><a><p></a> #errors #document | <html> | <head> | <body> | <b> | <b> | <a> | <p> | <a> #data <b><a><b><p></a> #errors #document | <html> | <head> | <body> | <b> | <a> | <b> | <b> | <p> | <a> #data <a><b><b><p></a> #errors #document | <html> | <head> | <body> | <a> | <b> | <b> | <b> | <b> | <p> | <a> #data <p>1<s id="A">2<b id="B">3</p>4</s>5</b> #errors #document | <html> | <head> | <body> | <p> | "1" | <s> | id="A" | "2" | <b> | id="B" | "3" | <s> | id="A" | <b> | id="B" | "4" | <b> | id="B" | "5" #data <table><a>1<td>2</td>3</table> #errors #document | <html> | <head> | <body> | <a> | "1" | <a> | "3" | <table> | <tbody> | <tr> | <td> | "2" #data <table>A<td>B</td>C</table> #errors #document | <html> | <head> | <body> | "AC" | <table> | <tbody> | <tr> | <td> | "B" #data <a><svg><tr><input></a> #errors #document | <html> | <head> | <body> | <a> | <svg svg> | <svg tr> | <svg input> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/scripted/������������������������0000755�0000153�0000161�00000000000�12321735652�026511� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/scripted/adoption01.dat����������0000644�0000153�0000161�00000000435�12321735652�031163� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <p><b id="A"><script>document.getElementById("A").id = "B"</script></p>TEXT</b> #errors #document | <html> | <head> | <body> | <p> | <b> | id="B" | <script> | "document.getElementById("A").id = "B"" | <b> | id="A" | "TEXT" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/scripted/webkit01.dat������������0000644�0000153�0000161�00000001105�12321735652�030626� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data 1<script>document.write("2")</script>3 #errors #document | <html> | <head> | <body> | "1" | <script> | "document.write("2")" | "23" #data 1<script>document.write("<script>document.write('2')</scr"+ "ipt><script>document.write('3')</scr" + "ipt>")</script>4 #errors #document | <html> | <head> | <body> | "1" | <script> | "document.write("<script>document.write('2')</scr"+ "ipt><script>document.write('3')</scr" + "ipt>")" | <script> | "document.write('2')" | "2" | <script> | "document.write('3')" | "34" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/doctype01.dat��������������������0000644�0000153�0000161�00000013261�12321735652�027201� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html>Hello #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "Hello" #data <!dOctYpE HtMl>Hello #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "Hello" #data <!DOCTYPEhtml>Hello #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE>Hello #errors #document | <!DOCTYPE > | <html> | <head> | <body> | "Hello" #data <!DOCTYPE >Hello #errors #document | <!DOCTYPE > | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato>Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato >Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato taco>Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato taco "ddd>Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato sYstEM>Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato sYstEM >Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato sYstEM ggg>Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato SYSTEM taco >Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato SYSTEM 'taco"'>Hello #errors #document | <!DOCTYPE potato "" "taco""> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato SYSTEM "taco">Hello #errors #document | <!DOCTYPE potato "" "taco"> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato SYSTEM "tai'co">Hello #errors #document | <!DOCTYPE potato "" "tai'co"> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato SYSTEMtaco "ddd">Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato grass SYSTEM taco>Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato pUbLIc>Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato pUbLIc >Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato pUbLIcgoof>Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato PUBLIC goof>Hello #errors #document | <!DOCTYPE potato> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato PUBLIC "go'of">Hello #errors #document | <!DOCTYPE potato "go'of" ""> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato PUBLIC 'go'of'>Hello #errors #document | <!DOCTYPE potato "go" ""> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato PUBLIC 'go:hh of' >Hello #errors #document | <!DOCTYPE potato "go:hh of" ""> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE potato PUBLIC "W3C-//dfdf" SYSTEM ggg>Hello #errors #document | <!DOCTYPE potato "W3C-//dfdf" ""> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">Hello #errors #document | <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE ...>Hello #errors #document | <!DOCTYPE ...> | <html> | <head> | <body> | "Hello" #data <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> #errors #document | <!DOCTYPE html "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | <html> | <head> | <body> #data <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> #errors #document | <!DOCTYPE html "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> | <html> | <head> | <body> #data <!DOCTYPE root-element [SYSTEM OR PUBLIC FPI] "uri" [ <!-- internal declarations --> ]> #errors #document | <!DOCTYPE root-element> | <html> | <head> | <body> | "]>" #data <!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd"> #errors #document | <!DOCTYPE html "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd"> | <html> | <head> | <body> #data <!DOCTYPE HTML SYSTEM "http://www.w3.org/DTD/HTML4-strict.dtd"><body><b>Mine!</b></body> #errors #document | <!DOCTYPE html "" "http://www.w3.org/DTD/HTML4-strict.dtd"> | <html> | <head> | <body> | <b> | "Mine!" #data <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd"> #errors #document | <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> | <html> | <head> | <body> #data <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"'http://www.w3.org/TR/html4/strict.dtd'> #errors #document | <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> | <html> | <head> | <body> #data <!DOCTYPE HTML PUBLIC"-//W3C//DTD HTML 4.01//EN"'http://www.w3.org/TR/html4/strict.dtd'> #errors #document | <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> | <html> | <head> | <body> #data <!DOCTYPE HTML PUBLIC'-//W3C//DTD HTML 4.01//EN''http://www.w3.org/TR/html4/strict.dtd'> #errors #document | <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> | <html> | <head> | <body> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests_innerHTML_1.dat������������0000644�0000153�0000161�00000015046�12321735652�030636� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <body><span> #errors #document-fragment body #document | <span> #data <span><body> #errors #document-fragment body #document | <span> #data <span><body> #errors #document-fragment div #document | <span> #data <body><span> #errors #document-fragment html #document | <head> | <body> | <span> #data <frameset><span> #errors #document-fragment body #document | <span> #data <span><frameset> #errors #document-fragment body #document | <span> #data <span><frameset> #errors #document-fragment div #document | <span> #data <frameset><span> #errors #document-fragment html #document | <head> | <frameset> #data <table><tr> #errors #document-fragment table #document | <tbody> | <tr> #data </table><tr> #errors #document-fragment table #document | <tbody> | <tr> #data <a> #errors #document-fragment table #document | <a> #data <a> #errors #document-fragment table #document | <a> #data <a><caption>a #errors #document-fragment table #document | <a> | <caption> | "a" #data <a><colgroup><col> #errors #document-fragment table #document | <a> | <colgroup> | <col> #data <a><tbody><tr> #errors #document-fragment table #document | <a> | <tbody> | <tr> #data <a><tfoot><tr> #errors #document-fragment table #document | <a> | <tfoot> | <tr> #data <a><thead><tr> #errors #document-fragment table #document | <a> | <thead> | <tr> #data <a><tr> #errors #document-fragment table #document | <a> | <tbody> | <tr> #data <a><th> #errors #document-fragment table #document | <a> | <tbody> | <tr> | <th> #data <a><td> #errors #document-fragment table #document | <a> | <tbody> | <tr> | <td> #data <table></table><tbody> #errors #document-fragment caption #document | <table> #data </table><span> #errors #document-fragment caption #document | <span> #data <span></table> #errors #document-fragment caption #document | <span> #data </caption><span> #errors #document-fragment caption #document | <span> #data <span></caption><span> #errors #document-fragment caption #document | <span> | <span> #data <span><caption><span> #errors #document-fragment caption #document | <span> | <span> #data <span><col><span> #errors #document-fragment caption #document | <span> | <span> #data <span><colgroup><span> #errors #document-fragment caption #document | <span> | <span> #data <span><html><span> #errors #document-fragment caption #document | <span> | <span> #data <span><tbody><span> #errors #document-fragment caption #document | <span> | <span> #data <span><td><span> #errors #document-fragment caption #document | <span> | <span> #data <span><tfoot><span> #errors #document-fragment caption #document | <span> | <span> #data <span><thead><span> #errors #document-fragment caption #document | <span> | <span> #data <span><th><span> #errors #document-fragment caption #document | <span> | <span> #data <span><tr><span> #errors #document-fragment caption #document | <span> | <span> #data <span></table><span> #errors #document-fragment caption #document | <span> | <span> #data </colgroup><col> #errors #document-fragment colgroup #document | <col> #data <a><col> #errors #document-fragment colgroup #document | <col> #data <caption><a> #errors #document-fragment tbody #document | <a> #data <col><a> #errors #document-fragment tbody #document | <a> #data <colgroup><a> #errors #document-fragment tbody #document | <a> #data <tbody><a> #errors #document-fragment tbody #document | <a> #data <tfoot><a> #errors #document-fragment tbody #document | <a> #data <thead><a> #errors #document-fragment tbody #document | <a> #data </table><a> #errors #document-fragment tbody #document | <a> #data <a><tr> #errors #document-fragment tbody #document | <a> | <tr> #data <a><td> #errors #document-fragment tbody #document | <a> | <tr> | <td> #data <a><td> #errors #document-fragment tbody #document | <a> | <tr> | <td> #data <a><td> #errors #document-fragment tbody #document | <a> | <tr> | <td> #data <td><table><tbody><a><tr> #errors #document-fragment tbody #document | <tr> | <td> | <a> | <table> | <tbody> | <tr> #data </tr><td> #errors #document-fragment tr #document | <td> #data <td><table><a><tr></tr><tr> #errors #document-fragment tr #document | <td> | <a> | <table> | <tbody> | <tr> | <tr> #data <caption><td> #errors #document-fragment tr #document | <td> #data <col><td> #errors #document-fragment tr #document | <td> #data <colgroup><td> #errors #document-fragment tr #document | <td> #data <tbody><td> #errors #document-fragment tr #document | <td> #data <tfoot><td> #errors #document-fragment tr #document | <td> #data <thead><td> #errors #document-fragment tr #document | <td> #data <tr><td> #errors #document-fragment tr #document | <td> #data </table><td> #errors #document-fragment tr #document | <td> #data <td><table></table><td> #errors #document-fragment tr #document | <td> | <table> | <td> #data <td><table></table><td> #errors #document-fragment tr #document | <td> | <table> | <td> #data <caption><a> #errors #document-fragment td #document | <a> #data <col><a> #errors #document-fragment td #document | <a> #data <colgroup><a> #errors #document-fragment td #document | <a> #data <tbody><a> #errors #document-fragment td #document | <a> #data <tfoot><a> #errors #document-fragment td #document | <a> #data <th><a> #errors #document-fragment td #document | <a> #data <thead><a> #errors #document-fragment td #document | <a> #data <tr><a> #errors #document-fragment td #document | <a> #data </table><a> #errors #document-fragment td #document | <a> #data </tbody><a> #errors #document-fragment td #document | <a> #data </td><a> #errors #document-fragment td #document | <a> #data </tfoot><a> #errors #document-fragment td #document | <a> #data </thead><a> #errors #document-fragment td #document | <a> #data </th><a> #errors #document-fragment td #document | <a> #data </tr><a> #errors #document-fragment td #document | <a> #data <table><td><td> #errors #document-fragment td #document | <table> | <tbody> | <tr> | <td> | <td> #data </select><option> #errors #document-fragment select #document | <option> #data <input><option> #errors #document-fragment select #document | <option> #data <keygen><option> #errors #document-fragment select #document | <option> #data <textarea><option> #errors #document-fragment select #document | <option> #data </html><!--abc--> #errors #document-fragment html #document | <head> | <body> | <!-- abc --> #data </frameset><frame> #errors #document-fragment frameset #document | <frame> #data #errors #document-fragment html #document | <head> | <body> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/webkit01.dat���������������������0000644�0000153�0000161�00000017347�12321735652�027030� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data Test #errors Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "Test" #data <div></div> #errors #document | <html> | <head> | <body> | <div> #data <div>Test</div> #errors #document | <html> | <head> | <body> | <div> | "Test" #data <di #errors #document | <html> | <head> | <body> #data <div>Hello</div> <script> console.log("PASS"); </script> <div>Bye</div> #errors #document | <html> | <head> | <body> | <div> | "Hello" | " " | <script> | " console.log("PASS"); " | " " | <div> | "Bye" #data <div foo="bar">Hello</div> #errors #document | <html> | <head> | <body> | <div> | foo="bar" | "Hello" #data <div>Hello</div> <script> console.log("FOO<span>BAR</span>BAZ"); </script> <div>Bye</div> #errors #document | <html> | <head> | <body> | <div> | "Hello" | " " | <script> | " console.log("FOO<span>BAR</span>BAZ"); " | " " | <div> | "Bye" #data <foo bar="baz"></foo><potato quack="duck"></potato> #errors #document | <html> | <head> | <body> | <foo> | bar="baz" | <potato> | quack="duck" #data <foo bar="baz"><potato quack="duck"></potato></foo> #errors #document | <html> | <head> | <body> | <foo> | bar="baz" | <potato> | quack="duck" #data <foo></foo bar="baz"><potato></potato quack="duck"> #errors #document | <html> | <head> | <body> | <foo> | <potato> #data </ tttt> #errors #document | <!-- tttt --> | <html> | <head> | <body> #data <div FOO ><img><img></div> #errors #document | <html> | <head> | <body> | <div> | foo="" | <img> | <img> #data <p>Test</p<p>Test2</p> #errors #document | <html> | <head> | <body> | <p> | "TestTest2" #data <rdar://problem/6869687> #errors #document | <html> | <head> | <body> | <rdar:> | 6869687="" | problem="" #data <A>test< /A> #errors #document | <html> | <head> | <body> | <a> | "test< /A>" #data &lt; #errors #document | <html> | <head> | <body> | "<" #data <body foo='bar'><body foo='baz' yo='mama'> #errors #document | <html> | <head> | <body> | foo="bar" | yo="mama" #data <body></br foo="bar"></body> #errors #document | <html> | <head> | <body> | <br> #data <bdy><br foo="bar"></body> #errors #document | <html> | <head> | <body> | <bdy> | <br> | foo="bar" #data <body></body></br foo="bar"> #errors #document | <html> | <head> | <body> | <br> #data <bdy></body><br foo="bar"> #errors #document | <html> | <head> | <body> | <bdy> | <br> | foo="bar" #data <html><body></body></html><!-- Hi there --> #errors #document | <html> | <head> | <body> | <!-- Hi there --> #data <html><body></body></html>x<!-- Hi there --> #errors #document | <html> | <head> | <body> | "x" | <!-- Hi there --> #data <html><body></body></html>x<!-- Hi there --></html><!-- Again --> #errors #document | <html> | <head> | <body> | "x" | <!-- Hi there --> | <!-- Again --> #data <html><body></body></html>x<!-- Hi there --></body></html><!-- Again --> #errors #document | <html> | <head> | <body> | "x" | <!-- Hi there --> | <!-- Again --> #data <html><body><ruby><div><rp>xx</rp></div></ruby></body></html> #errors #document | <html> | <head> | <body> | <ruby> | <div> | <rp> | "xx" #data <html><body><ruby><div><rt>xx</rt></div></ruby></body></html> #errors #document | <html> | <head> | <body> | <ruby> | <div> | <rt> | "xx" #data <html><frameset><!--1--><noframes>A</noframes><!--2--></frameset><!--3--><noframes>B</noframes><!--4--></html><!--5--><noframes>C</noframes><!--6--> #errors #document | <html> | <head> | <frameset> | <!-- 1 --> | <noframes> | "A" | <!-- 2 --> | <!-- 3 --> | <noframes> | "B" | <!-- 4 --> | <noframes> | "C" | <!-- 5 --> | <!-- 6 --> #data <select><option>A<select><option>B<select><option>C<select><option>D<select><option>E<select><option>F<select><option>G<select> #errors #document | <html> | <head> | <body> | <select> | <option> | "A" | <option> | "B" | <select> | <option> | "C" | <option> | "D" | <select> | <option> | "E" | <option> | "F" | <select> | <option> | "G" #data <dd><dd><dt><dt><dd><li><li> #errors #document | <html> | <head> | <body> | <dd> | <dd> | <dt> | <dt> | <dd> | <li> | <li> #data <div><b></div><div><nobr>a<nobr> #errors #document | <html> | <head> | <body> | <div> | <b> | <div> | <b> | <nobr> | "a" | <nobr> #data <head></head> <body></body> #errors #document | <html> | <head> | " " | <body> #data <head></head> <style></style>ddd #errors #document | <html> | <head> | <style> | " " | <body> | "ddd" #data <kbd><table></kbd><col><select><tr> #errors #document | <html> | <head> | <body> | <kbd> | <select> | <table> | <colgroup> | <col> | <tbody> | <tr> #data <kbd><table></kbd><col><select><tr></table><div> #errors #document | <html> | <head> | <body> | <kbd> | <select> | <table> | <colgroup> | <col> | <tbody> | <tr> | <div> #data <a><li><style></style><title></title></a> #errors #document | <html> | <head> | <body> | <a> | <li> | <a> | <style> | <title> #data <font></p><p><meta><title></title></font> #errors #document | <html> | <head> | <body> | <font> | <p> | <p> | <font> | <meta> | <title> #data <a><center><title></title><a> #errors #document | <html> | <head> | <body> | <a> | <center> | <a> | <title> | <a> #data <svg><title><div> #errors #document | <html> | <head> | <body> | <svg svg> | <svg title> | <div> #data <svg><title><rect><div> #errors #document | <html> | <head> | <body> | <svg svg> | <svg title> | <rect> | <div> #data <svg><title><svg><div> #errors #document | <html> | <head> | <body> | <svg svg> | <svg title> | <svg svg> | <div> #data <img <="" FAIL> #errors #document | <html> | <head> | <body> | <img> | <="" | fail="" #data <ul><li><div id='foo'/>A</li><li>B<div>C</div></li></ul> #errors #document | <html> | <head> | <body> | <ul> | <li> | <div> | id="foo" | "A" | <li> | "B" | <div> | "C" #data <svg><em><desc></em> #errors #document | <html> | <head> | <body> | <svg svg> | <em> | <desc> #data <table><tr><td><svg><desc><td></desc><circle> #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <svg svg> | <svg desc> | <td> | <circle> #data <svg><tfoot></mi><td> #errors #document | <html> | <head> | <body> | <svg svg> | <svg tfoot> | <svg td> #data <math><mrow><mrow><mn>1</mn></mrow><mi>a</mi></mrow></math> #errors #document | <html> | <head> | <body> | <math math> | <math mrow> | <math mrow> | <math mn> | "1" | <math mi> | "a" #data <!doctype html><input type="hidden"><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><input type="button"><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <input> | type="button" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests5.dat�����������������������0000644�0000153�0000161�00000006131�12321735652�026616� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <style> <!-- </style>x #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. Line: 1 Col: 22 Unexpected end of file. Expected end tag (style). #document | <html> | <head> | <style> | " <!-- " | <body> | "x" #data <style> <!-- </style> --> </style>x #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. #document | <html> | <head> | <style> | " <!-- " | " " | <body> | "--> x" #data <style> <!--> </style>x #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. #document | <html> | <head> | <style> | " <!--> " | <body> | "x" #data <style> <!---> </style>x #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. #document | <html> | <head> | <style> | " <!---> " | <body> | "x" #data <iframe> <!---> </iframe>x #errors Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE. #document | <html> | <head> | <body> | <iframe> | " <!---> " | "x" #data <iframe> <!--- </iframe>->x</iframe> --> </iframe>x #errors Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE. #document | <html> | <head> | <body> | <iframe> | " <!--- " | "->x --> x" #data <script> <!-- </script> --> </script>x #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. #document | <html> | <head> | <script> | " <!-- " | " " | <body> | "--> x" #data <title> <!-- </title> --> </title>x #errors Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. #document | <html> | <head> | <title> | " <!-- " | " " | <body> | "--> x" #data <textarea> <!--- </textarea>->x</textarea> --> </textarea>x #errors Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. #document | <html> | <head> | <body> | <textarea> | " <!--- " | "->x --> x" #data <style> <!</-- </style>x #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. #document | <html> | <head> | <style> | " <!</-- " | <body> | "x" #data <p><xmp></xmp> #errors XXX: Unknown #document | <html> | <head> | <body> | <p> | <xmp> #data <xmp> <!-- > --> </xmp> #errors Line: 1 Col: 5 Unexpected start tag (xmp). Expected DOCTYPE. #document | <html> | <head> | <body> | <xmp> | " <!-- > --> " #data <title>&amp;</title> #errors Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. #document | <html> | <head> | <title> | "&" | <body> #data <title><!--&amp;--></title> #errors Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. #document | <html> | <head> | <title> | "<!--&-->" | <body> #data <title><!--</title> #errors Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. Line: 1 Col: 19 Unexpected end of file. Expected end tag (title). #document | <html> | <head> | <title> | "<!--" | <body> #data <noscript><!--</noscript>--></noscript> #errors Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE. #document | <html> | <head> | <noscript> | "<!--" | <body> | "-->" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests19.dat����������������������0000644�0000153�0000161�00000042027�12321735652�026707� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!doctype html><math><mn DefinitionUrl="foo"> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mn> | definitionURL="foo" #data <!doctype html><html></p><!--foo--> #errors #document | <!DOCTYPE html> | <html> | <!-- foo --> | <head> | <body> #data <!doctype html><head></head></p><!--foo--> #errors #document | <!DOCTYPE html> | <html> | <head> | <!-- foo --> | <body> #data <!doctype html><body><p><pre> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <pre> #data <!doctype html><body><p><listing> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <listing> #data <!doctype html><p><plaintext> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <plaintext> #data <!doctype html><p><h1> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <h1> #data <!doctype html><form><isindex> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <form> #data <!doctype html><isindex action="POST"> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <form> | action="POST" | <hr> | <label> | "This is a searchable index. Enter search keywords: " | <input> | name="isindex" | <hr> #data <!doctype html><isindex prompt="this is isindex"> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <form> | <hr> | <label> | "this is isindex" | <input> | name="isindex" | <hr> #data <!doctype html><isindex type="hidden"> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <form> | <hr> | <label> | "This is a searchable index. Enter search keywords: " | <input> | name="isindex" | type="hidden" | <hr> #data <!doctype html><isindex name="foo"> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <form> | <hr> | <label> | "This is a searchable index. Enter search keywords: " | <input> | name="isindex" | <hr> #data <!doctype html><ruby><p><rp> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <p> | <rp> #data <!doctype html><ruby><div><span><rp> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <div> | <span> | <rp> #data <!doctype html><ruby><div><p><rp> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <div> | <p> | <rp> #data <!doctype html><ruby><p><rt> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <p> | <rt> #data <!doctype html><ruby><div><span><rt> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <div> | <span> | <rt> #data <!doctype html><ruby><div><p><rt> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <ruby> | <div> | <p> | <rt> #data <!doctype html><math/><foo> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <foo> #data <!doctype html><svg/><foo> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <foo> #data <!doctype html><div></body><!--foo--> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <div> | <!-- foo --> #data <!doctype html><h1><div><h3><span></h1>foo #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <h1> | <div> | <h3> | <span> | "foo" #data <!doctype html><p></h3>foo #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | "foo" #data <!doctype html><h3><li>abc</h2>foo #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <h3> | <li> | "abc" | "foo" #data <!doctype html><table>abc<!--foo--> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "abc" | <table> | <!-- foo --> #data <!doctype html><table> <!--foo--> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | " " | <!-- foo --> #data <!doctype html><table> b <!--foo--> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | " b " | <table> | <!-- foo --> #data <!doctype html><select><option><option> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <option> | <option> #data <!doctype html><select><option></optgroup> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <option> #data <!doctype html><select><option></optgroup> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <option> #data <!doctype html><p><math><mi><p><h1> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math mi> | <p> | <h1> #data <!doctype html><p><math><mo><p><h1> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math mo> | <p> | <h1> #data <!doctype html><p><math><mn><p><h1> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math mn> | <p> | <h1> #data <!doctype html><p><math><ms><p><h1> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math ms> | <p> | <h1> #data <!doctype html><p><math><mtext><p><h1> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math mtext> | <p> | <h1> #data <!doctype html><frameset></noframes> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><html c=d><body></html><html a=b> #errors #document | <!DOCTYPE html> | <html> | a="b" | c="d" | <head> | <body> #data <!doctype html><html c=d><frameset></frameset></html><html a=b> #errors #document | <!DOCTYPE html> | <html> | a="b" | c="d" | <head> | <frameset> #data <!doctype html><html><frameset></frameset></html><!--foo--> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <!-- foo --> #data <!doctype html><html><frameset></frameset></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | " " #data <!doctype html><html><frameset></frameset></html>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><html><frameset></frameset></html><p> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><html><frameset></frameset></html></p> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <html><frameset></frameset></html><!doctype html> #errors #document | <html> | <head> | <frameset> #data <!doctype html><body><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> #data <!doctype html><p><frameset><frame> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <!doctype html><p>a<frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | "a" #data <!doctype html><p> <frameset><frame> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <!doctype html><pre><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> #data <!doctype html><listing><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <listing> #data <!doctype html><li><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <li> #data <!doctype html><dd><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <dd> #data <!doctype html><dt><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <dt> #data <!doctype html><button><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <button> #data <!doctype html><applet><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <applet> #data <!doctype html><marquee><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <marquee> #data <!doctype html><object><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <object> #data <!doctype html><table><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> #data <!doctype html><area><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <area> #data <!doctype html><basefont><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <basefont> | <frameset> #data <!doctype html><bgsound><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <bgsound> | <frameset> #data <!doctype html><br><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <br> #data <!doctype html><embed><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <embed> #data <!doctype html><img><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <img> #data <!doctype html><input><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <input> #data <!doctype html><keygen><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <keygen> #data <!doctype html><wbr><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <wbr> #data <!doctype html><hr><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <hr> #data <!doctype html><textarea></textarea><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> #data <!doctype html><xmp></xmp><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <xmp> #data <!doctype html><iframe></iframe><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <iframe> #data <!doctype html><select></select><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><svg></svg><frameset><frame> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <!doctype html><math></math><frameset><frame> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <!doctype html><svg><foreignObject><div> <frameset><frame> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <!doctype html><svg>a</svg><frameset><frame> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | "a" #data <!doctype html><svg> </svg><frameset><frame> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <frame> #data <html>aaa<frameset></frameset> #errors #document | <html> | <head> | <body> | "aaa" #data <html> a <frameset></frameset> #errors #document | <html> | <head> | <body> | "a " #data <!doctype html><div><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!doctype html><div><body><frameset> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <div> #data <!doctype html><p><math></p>a #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | "a" #data <!doctype html><p><math><mn><span></p>a #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <math math> | <math mn> | <span> | <p> | "a" #data <!doctype html><math></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> #data <!doctype html><meta charset="ascii"> #errors #document | <!DOCTYPE html> | <html> | <head> | <meta> | charset="ascii" | <body> #data <!doctype html><meta http-equiv="content-type" content="text/html;charset=ascii"> #errors #document | <!DOCTYPE html> | <html> | <head> | <meta> | content="text/html;charset=ascii" | http-equiv="content-type" | <body> #data <!doctype html><head><!--aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--><meta charset="utf8"> #errors #document | <!DOCTYPE html> | <html> | <head> | <!-- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --> | <meta> | charset="utf8" | <body> #data <!doctype html><html a=b><head></head><html c=d> #errors #document | <!DOCTYPE html> | <html> | a="b" | c="d" | <head> | <body> #data <!doctype html><image/> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <img> #data <!doctype html>a<i>b<table>c<b>d</i>e</b>f #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "a" | <i> | "bc" | <b> | "de" | "f" | <table> #data <!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <i> | "a" | <b> | "b" | <b> | <div> | <b> | <i> | "c" | <a> | "d" | <a> | "e" | <a> | "f" | <table> #data <!doctype html><i>a<b>b<div>c<a>d</i>e</b>f #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <i> | "a" | <b> | "b" | <b> | <div> | <b> | <i> | "c" | <a> | "d" | <a> | "e" | <a> | "f" #data <!doctype html><table><i>a<b>b<div>c</i> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <i> | "a" | <b> | "b" | <b> | <div> | <i> | "c" | <table> #data <!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <i> | "a" | <b> | "b" | <b> | <div> | <b> | <i> | "c" | <a> | "d" | <a> | "e" | <a> | "f" | <table> #data <!doctype html><table><i>a<div>b<tr>c<b>d</i>e #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <i> | "a" | <div> | "b" | <i> | "c" | <b> | "d" | <b> | "e" | <table> | <tbody> | <tr> #data <!doctype html><table><td><table><i>a<div>b<b>c</i>d #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <i> | "a" | <div> | <i> | "b" | <b> | "c" | <b> | "d" | <table> #data <!doctype html><body><bgsound> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <bgsound> #data <!doctype html><body><basefont> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <basefont> #data <!doctype html><a><b></a><basefont> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <a> | <b> | <basefont> #data <!doctype html><a><b></a><bgsound> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <a> | <b> | <bgsound> #data <!doctype html><figcaption><article></figcaption>a #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <figcaption> | <article> | "a" #data <!doctype html><summary><article></summary>a #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <summary> | <article> | "a" #data <!doctype html><p><a><plaintext>b #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <a> | <plaintext> | <a> | "b" #data <!DOCTYPE html><div>a<a></div>b<p>c</p>d #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <div> | "a" | <a> | <a> | "b" | <p> | "c" | "d" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests15.dat����������������������0000644�0000153�0000161�00000010057�12321735652�026701� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html><p><b><i><u></p> <p>X #errors Line: 1 Col: 31 Unexpected end tag (p). Ignored. Line: 1 Col: 36 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <b> | <i> | <u> | <b> | <i> | <u> | " " | <p> | "X" #data <p><b><i><u></p> <p>X #errors Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE. Line: 1 Col: 16 Unexpected end tag (p). Ignored. Line: 2 Col: 4 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <p> | <b> | <i> | <u> | <b> | <i> | <u> | " " | <p> | "X" #data <!doctype html></html> <head> #errors Line: 1 Col: 22 Unexpected end tag (html) after the (implied) root element. #document | <!DOCTYPE html> | <html> | <head> | <body> | " " #data <!doctype html></body><meta> #errors Line: 1 Col: 22 Unexpected end tag (body) after the (implied) root element. #document | <!DOCTYPE html> | <html> | <head> | <body> | <meta> #data <html></html><!-- foo --> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. Line: 1 Col: 13 Unexpected end tag (html) after the (implied) root element. #document | <html> | <head> | <body> | <!-- foo --> #data <!doctype html></body><title>X</title> #errors Line: 1 Col: 22 Unexpected end tag (body) after the (implied) root element. #document | <!DOCTYPE html> | <html> | <head> | <body> | <title> | "X" #data <!doctype html><table> X<meta></table> #errors Line: 1 Col: 24 Unexpected non-space characters in table context caused voodoo mode. Line: 1 Col: 30 Unexpected start tag (meta) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | " X" | <meta> | <table> #data <!doctype html><table> x</table> #errors Line: 1 Col: 24 Unexpected non-space characters in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | " x" | <table> #data <!doctype html><table> x </table> #errors Line: 1 Col: 25 Unexpected non-space characters in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | " x " | <table> #data <!doctype html><table><tr> x</table> #errors Line: 1 Col: 28 Unexpected non-space characters in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | " x" | <table> | <tbody> | <tr> #data <!doctype html><table>X<style> <tr>x </style> </table> #errors Line: 1 Col: 23 Unexpected non-space characters in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | "X" | <table> | <style> | " <tr>x " | " " #data <!doctype html><div><table><a>foo</a> <tr><td>bar</td> </tr></table></div> #errors Line: 1 Col: 30 Unexpected start tag (a) in table context caused voodoo mode. Line: 1 Col: 37 Unexpected end tag (a) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | <div> | <a> | "foo" | <table> | " " | <tbody> | <tr> | <td> | "bar" | " " #data <frame></frame></frame><frameset><frame><frameset><frame></frameset><noframes></frameset><noframes> #errors 6: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. 13: Stray start tag “frameâ€. 21: Stray end tag “frameâ€. 29: Stray end tag “frameâ€. 39: “frameset†start tag after “body†already open. 105: End of file seen inside an [R]CDATA element. 105: End of file seen and there were open elements. XXX: These errors are wrong, please fix me! #document | <html> | <head> | <frameset> | <frame> | <frameset> | <frame> | <noframes> | "</frameset><noframes>" #data <!DOCTYPE html><object></html> #errors 1: Expected closing tag. Unexpected end of file #document | <!DOCTYPE html> | <html> | <head> | <body> | <object> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests3.dat�����������������������0000644�0000153�0000161�00000011626�12321735652�026621� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <head></head><style></style> #errors Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. Line: 1 Col: 20 Unexpected start tag (style) that can be in head. Moved. #document | <html> | <head> | <style> | <body> #data <head></head><script></script> #errors Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. Line: 1 Col: 21 Unexpected start tag (script) that can be in head. Moved. #document | <html> | <head> | <script> | <body> #data <head></head><!-- --><style></style><!-- --><script></script> #errors Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. Line: 1 Col: 28 Unexpected start tag (style) that can be in head. Moved. #document | <html> | <head> | <style> | <script> | <!-- --> | <!-- --> | <body> #data <head></head><!-- -->x<style></style><!-- --><script></script> #errors Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. #document | <html> | <head> | <!-- --> | <body> | "x" | <style> | <!-- --> | <script> #data <!DOCTYPE html><html><head></head><body><pre> </pre></body></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> #data <!DOCTYPE html><html><head></head><body><pre> foo</pre></body></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> | "foo" #data <!DOCTYPE html><html><head></head><body><pre> foo</pre></body></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> | " foo" #data <!DOCTYPE html><html><head></head><body><pre> foo </pre></body></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> | "foo " #data <!DOCTYPE html><html><head></head><body><pre>x</pre><span> </span></body></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> | "x" | <span> | " " #data <!DOCTYPE html><html><head></head><body><pre>x y</pre></body></html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> | "x y" #data <!DOCTYPE html><html><head></head><body><pre>x<div> y</pre></body></html> #errors Line: 2 Col: 7 End tag (pre) seen too early. Expected other end tag. #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> | "x" | <div> | " y" #data <!DOCTYPE html><pre>&#x0a;&#x0a;A</pre> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> | " A" #data <!DOCTYPE html><HTML><META><HEAD></HEAD></HTML> #errors Line: 1 Col: 33 Unexpected start tag head in existing head. Ignored. #document | <!DOCTYPE html> | <html> | <head> | <meta> | <body> #data <!DOCTYPE html><HTML><HEAD><head></HEAD></HTML> #errors Line: 1 Col: 33 Unexpected start tag head in existing head. Ignored. #document | <!DOCTYPE html> | <html> | <head> | <body> #data <textarea>foo<span>bar</span><i>baz #errors Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. Line: 1 Col: 35 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <textarea> | "foo<span>bar</span><i>baz" #data <title>foo<span>bar</em><i>baz #errors Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. Line: 1 Col: 30 Unexpected end of file. Expected end tag (title). #document | <html> | <head> | <title> | "foo<span>bar</em><i>baz" | <body> #data <!DOCTYPE html><textarea> </textarea> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> #data <!DOCTYPE html><textarea> foo</textarea> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> | "foo" #data <!DOCTYPE html><textarea> foo</textarea> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <textarea> | " foo" #data <!DOCTYPE html><html><head></head><body><ul><li><div><p><li></ul></body></html> #errors Line: 1 Col: 60 Missing end tag (div, li). #document | <!DOCTYPE html> | <html> | <head> | <body> | <ul> | <li> | <div> | <p> | <li> #data <!doctype html><nobr><nobr><nobr> #errors Line: 1 Col: 27 Unexpected start tag (nobr) implies end tag (nobr). Line: 1 Col: 33 Unexpected start tag (nobr) implies end tag (nobr). Line: 1 Col: 33 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | <nobr> | <nobr> | <nobr> #data <!doctype html><nobr><nobr></nobr><nobr> #errors Line: 1 Col: 27 Unexpected start tag (nobr) implies end tag (nobr). Line: 1 Col: 40 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | <nobr> | <nobr> | <nobr> #data <!doctype html><html><body><p><table></table></body></html> #errors Not known #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <table> #data <p><table></table> #errors Not known #document | <html> | <head> | <body> | <p> | <table> ����������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests8.dat�����������������������0000644�0000153�0000161�00000006501�12321735652�026622� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <div> <div></div> </span>x #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 3 Col: 7 Unexpected end tag (span). Ignored. Line: 3 Col: 8 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " " | <div> | " x" #data <div>x<div></div> </span>x #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 2 Col: 7 Unexpected end tag (span). Ignored. Line: 2 Col: 8 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | "x" | <div> | " x" #data <div>x<div></div>x</span>x #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 25 Unexpected end tag (span). Ignored. Line: 1 Col: 26 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | "x" | <div> | "xx" #data <div>x<div></div>y</span>z #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 25 Unexpected end tag (span). Ignored. Line: 1 Col: 26 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | "x" | <div> | "yz" #data <table><div>x<div></div>x</span>x #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 12 Unexpected start tag (div) in table context caused voodoo mode. Line: 1 Col: 18 Unexpected start tag (div) in table context caused voodoo mode. Line: 1 Col: 24 Unexpected end tag (div) in table context caused voodoo mode. Line: 1 Col: 32 Unexpected end tag (span) in table context caused voodoo mode. Line: 1 Col: 32 Unexpected end tag (span). Ignored. Line: 1 Col: 33 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | <div> | "x" | <div> | "xx" | <table> #data x<table>x #errors Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. Line: 1 Col: 9 Unexpected non-space characters in table context caused voodoo mode. Line: 1 Col: 9 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | "xx" | <table> #data x<table><table>x #errors Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. Line: 1 Col: 15 Unexpected start tag (table) implies end tag (table). Line: 1 Col: 16 Unexpected non-space characters in table context caused voodoo mode. Line: 1 Col: 16 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | "x" | <table> | "x" | <table> #data <b>a<div></div><div></b>y #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 24 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 25 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <b> | "a" | <div> | <div> | <b> | "y" #data <a><div><p></a> #errors Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE. Line: 1 Col: 15 End tag (a) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 15 End tag (a) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 15 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <a> | <div> | <a> | <p> | <a> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/html5test-com.dat����������������0000644�0000153�0000161�00000005162�12321735652�030077� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <div<div> #errors #document | <html> | <head> | <body> | <div<div> #data <div foo<bar=''> #errors #document | <html> | <head> | <body> | <div> | foo<bar="" #data <div foo=`bar`> #errors #document | <html> | <head> | <body> | <div> | foo="`bar`" #data <div \"foo=''> #errors #document | <html> | <head> | <body> | <div> | \"foo="" #data <a href='\nbar'></a> #errors #document | <html> | <head> | <body> | <a> | href="\nbar" #data <!DOCTYPE html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> #data &lang;&rang; #errors #document | <html> | <head> | <body> | "⟨⟩" #data &apos; #errors #document | <html> | <head> | <body> | "'" #data &ImaginaryI; #errors #document | <html> | <head> | <body> | "â…ˆ" #data &Kopf; #errors #document | <html> | <head> | <body> | "ð•‚" #data &notinva; #errors #document | <html> | <head> | <body> | "∉" #data <?import namespace="foo" implementation="#bar"> #errors #document | <!-- ?import namespace="foo" implementation="#bar" --> | <html> | <head> | <body> #data <!--foo--bar--> #errors #document | <!-- foo--bar --> | <html> | <head> | <body> #data <![CDATA[x]]> #errors #document | <!-- [CDATA[x]] --> | <html> | <head> | <body> #data <textarea><!--</textarea>--></textarea> #errors #document | <html> | <head> | <body> | <textarea> | "<!--" | "-->" #data <textarea><!--</textarea>--> #errors #document | <html> | <head> | <body> | <textarea> | "<!--" | "-->" #data <style><!--</style>--></style> #errors #document | <html> | <head> | <style> | "<!--" | <body> | "-->" #data <style><!--</style>--> #errors #document | <html> | <head> | <style> | "<!--" | <body> | "-->" #data <ul><li>A </li> <li>B</li></ul> #errors #document | <html> | <head> | <body> | <ul> | <li> | "A " | " " | <li> | "B" #data <table><form><input type=hidden><input></form><div></div></table> #errors #document | <html> | <head> | <body> | <input> | <div> | <table> | <form> | <input> | type="hidden" #data <i>A<b>B<p></i>C</b>D #errors #document | <html> | <head> | <body> | <i> | "A" | <b> | "B" | <b> | <p> | <b> | <i> | "C" | "D" #data <div></div> #errors #document | <html> | <head> | <body> | <div> #data <svg></svg> #errors #document | <html> | <head> | <body> | <svg svg> #data <math></math> #errors #document | <html> | <head> | <body> | <math math> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tables01.dat���������������������0000644�0000153�0000161�00000005322�12321735652�027003� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <table><th> #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <th> #data <table><td> #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> #data <table><col foo='bar'> #errors #document | <html> | <head> | <body> | <table> | <colgroup> | <col> | foo="bar" #data <table><colgroup></html>foo #errors #document | <html> | <head> | <body> | "foo" | <table> | <colgroup> #data <table></table><p>foo #errors #document | <html> | <head> | <body> | <table> | <p> | "foo" #data <table></body></caption></col></colgroup></html></tbody></td></tfoot></th></thead></tr><td> #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> #data <table><select><option>3</select></table> #errors #document | <html> | <head> | <body> | <select> | <option> | "3" | <table> #data <table><select><table></table></select></table> #errors #document | <html> | <head> | <body> | <select> | <table> | <table> #data <table><select></table> #errors #document | <html> | <head> | <body> | <select> | <table> #data <table><select><option>A<tr><td>B</td></tr></table> #errors #document | <html> | <head> | <body> | <select> | <option> | "A" | <table> | <tbody> | <tr> | <td> | "B" #data <table><td></body></caption></col></colgroup></html>foo #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | "foo" #data <table><td>A</table>B #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | "A" | "B" #data <table><tr><caption> #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <caption> #data <table><tr></body></caption></col></colgroup></html></td></th><td>foo #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | "foo" #data <table><td><tr> #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <tr> #data <table><td><button><td> #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <button> | <td> #data <table><tr><td><svg><desc><td> #errors #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <svg svg> | <svg desc> | <td> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests12.dat����������������������0000644�0000153�0000161�00000003114�12321735652�026672� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html><body><p>foo<math><mtext><i>baz</i></mtext><annotation-xml><svg><desc><b>eggs</b></desc><g><foreignObject><P>spam<TABLE><tr><td><img></td></table></foreignObject></g><g>quux</g></svg></annotation-xml></math>bar #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | "foo" | <math math> | <math mtext> | <i> | "baz" | <math annotation-xml> | <svg svg> | <svg desc> | <b> | "eggs" | <svg g> | <svg foreignObject> | <p> | "spam" | <table> | <tbody> | <tr> | <td> | <img> | <svg g> | "quux" | "bar" #data <!DOCTYPE html><body>foo<math><mtext><i>baz</i></mtext><annotation-xml><svg><desc><b>eggs</b></desc><g><foreignObject><P>spam<TABLE><tr><td><img></td></table></foreignObject></g><g>quux</g></svg></annotation-xml></math>bar #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "foo" | <math math> | <math mtext> | <i> | "baz" | <math annotation-xml> | <svg svg> | <svg desc> | <b> | "eggs" | <svg g> | <svg foreignObject> | <p> | "spam" | <table> | <tbody> | <tr> | <td> | <img> | <svg g> | "quux" | "bar" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests1.dat�����������������������0000644�0000153�0000161�00000152434�12321735652�026622� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data Test #errors Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "Test" #data <p>One<p>Two #errors Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE. #document | <html> | <head> | <body> | <p> | "One" | <p> | "Two" #data Line1<br>Line2<br>Line3<br>Line4 #errors Line: 1 Col: 5 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "Line1" | <br> | "Line2" | <br> | "Line3" | <br> | "Line4" #data <html> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. #document | <html> | <head> | <body> #data <head> #errors Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. #document | <html> | <head> | <body> #data <body> #errors Line: 1 Col: 6 Unexpected start tag (body). Expected DOCTYPE. #document | <html> | <head> | <body> #data <html><head> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. #document | <html> | <head> | <body> #data <html><head></head> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. #document | <html> | <head> | <body> #data <html><head></head><body> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. #document | <html> | <head> | <body> #data <html><head></head><body></body> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. #document | <html> | <head> | <body> #data <html><head><body></body></html> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. #document | <html> | <head> | <body> #data <html><head></body></html> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. Line: 1 Col: 19 Unexpected end tag (body). Line: 1 Col: 26 Unexpected end tag (html). #document | <html> | <head> | <body> #data <html><head><body></html> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. #document | <html> | <head> | <body> #data <html><body></html> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. #document | <html> | <head> | <body> #data <body></html> #errors Line: 1 Col: 6 Unexpected start tag (body). Expected DOCTYPE. #document | <html> | <head> | <body> #data <head></html> #errors Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. Line: 1 Col: 13 Unexpected end tag (html). Ignored. #document | <html> | <head> | <body> #data </head> #errors Line: 1 Col: 7 Unexpected end tag (head). Expected DOCTYPE. #document | <html> | <head> | <body> #data </body> #errors Line: 1 Col: 7 Unexpected end tag (body). Expected DOCTYPE. Line: 1 Col: 7 Unexpected end tag (body) after the (implied) root element. #document | <html> | <head> | <body> #data </html> #errors Line: 1 Col: 7 Unexpected end tag (html). Expected DOCTYPE. Line: 1 Col: 7 Unexpected end tag (html) after the (implied) root element. #document | <html> | <head> | <body> #data <b><table><td><i></table> #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 25 Got table cell end tag (td) while required end tags are missing. Line: 1 Col: 25 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <b> | <table> | <tbody> | <tr> | <td> | <i> #data <b><table><td></b><i></table>X #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 18 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 29 Got table cell end tag (td) while required end tags are missing. Line: 1 Col: 30 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <b> | <table> | <tbody> | <tr> | <td> | <i> | "X" #data <h1>Hello<h2>World #errors 4: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. 13: Heading cannot be a child of another heading. 18: End of file seen and there were open elements. #document | <html> | <head> | <body> | <h1> | "Hello" | <h2> | "World" #data <a><p>X<a>Y</a>Z</p></a> #errors Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE. Line: 1 Col: 10 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 10 End tag (a) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 24 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. #document | <html> | <head> | <body> | <a> | <p> | <a> | "X" | <a> | "Y" | "Z" #data <b><button>foo</b>bar #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 15 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. #document | <html> | <head> | <body> | <b> | <button> | <b> | "foo" | "bar" #data <!DOCTYPE html><span><button>foo</span>bar #errors 39: End tag “span†seen but there were unclosed elements. #document | <!DOCTYPE html> | <html> | <head> | <body> | <span> | <button> | "foobar" #data <p><b><div><marquee></p></b></div>X #errors Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE. Line: 1 Col: 11 Unexpected end tag (p). Ignored. Line: 1 Col: 24 Unexpected end tag (p). Ignored. Line: 1 Col: 28 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 34 End tag (div) seen too early. Expected other end tag. Line: 1 Col: 35 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <p> | <b> | <div> | <b> | <marquee> | <p> | "X" #data <script><div></script></div><title><p></title><p><p> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 28 Unexpected end tag (div). Ignored. #document | <html> | <head> | <script> | "<div>" | <title> | "<p>" | <body> | <p> | <p> #data <!--><div>--<!--> #errors Line: 1 Col: 5 Incorrect comment. Line: 1 Col: 10 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 17 Incorrect comment. Line: 1 Col: 17 Expected closing tag. Unexpected end of file. #document | <!-- --> | <html> | <head> | <body> | <div> | "--" | <!-- --> #data <p><hr></p> #errors Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE. Line: 1 Col: 11 Unexpected end tag (p). Ignored. #document | <html> | <head> | <body> | <p> | <hr> | <p> #data <select><b><option><select><option></b></select>X #errors Line: 1 Col: 8 Unexpected start tag (select). Expected DOCTYPE. Line: 1 Col: 11 Unexpected start tag token (b) in the select phase. Ignored. Line: 1 Col: 27 Unexpected select start tag in the select phase treated as select end tag. Line: 1 Col: 39 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 48 Unexpected end tag (select). Ignored. Line: 1 Col: 49 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <select> | <option> | <option> | "X" #data <a><table><td><a><table></table><a></tr><a></table><b>X</b>C<a>Y #errors Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE. Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 35 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 40 Got table cell end tag (td) while required end tags are missing. Line: 1 Col: 43 Unexpected start tag (a) in table context caused voodoo mode. Line: 1 Col: 43 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 43 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 51 Unexpected implied end tag (a) in the table phase. Line: 1 Col: 63 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 64 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <a> | <a> | <table> | <tbody> | <tr> | <td> | <a> | <table> | <a> | <a> | <b> | "X" | "C" | <a> | "Y" #data <a X>0<b>1<a Y>2 #errors Line: 1 Col: 5 Unexpected start tag (a). Expected DOCTYPE. Line: 1 Col: 15 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 15 End tag (a) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 16 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <a> | x="" | "0" | <b> | "1" | <b> | <a> | y="" | "2" #data <!-----><font><div>hello<table>excite!<b>me!<th><i>please!</tr><!--X--> #errors Line: 1 Col: 7 Unexpected '-' after '--' found in comment. Line: 1 Col: 14 Unexpected start tag (font). Expected DOCTYPE. Line: 1 Col: 38 Unexpected non-space characters in table context caused voodoo mode. Line: 1 Col: 41 Unexpected start tag (b) in table context caused voodoo mode. Line: 1 Col: 48 Unexpected implied end tag (b) in the table phase. Line: 1 Col: 48 Unexpected table cell start tag (th) in the table body phase. Line: 1 Col: 63 Got table cell end tag (th) while required end tags are missing. Line: 1 Col: 71 Unexpected end of file. Expected table content. #document | <!-- - --> | <html> | <head> | <body> | <font> | <div> | "helloexcite!" | <b> | "me!" | <table> | <tbody> | <tr> | <th> | <i> | "please!" | <!-- X --> #data <!DOCTYPE html><li>hello<li>world<ul>how<li>do</ul>you</body><!--do--> #errors Line: 1 Col: 61 Unexpected end tag (li). Missing end tag (body). #document | <!DOCTYPE html> | <html> | <head> | <body> | <li> | "hello" | <li> | "world" | <ul> | "how" | <li> | "do" | "you" | <!-- do --> #data <!DOCTYPE html>A<option>B<optgroup>C<select>D</option>E #errors Line: 1 Col: 54 Unexpected end tag (option) in the select phase. Ignored. Line: 1 Col: 55 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | "A" | <option> | "B" | <optgroup> | "C" | <select> | "DE" #data < #errors Line: 1 Col: 1 Expected tag name. Got something else instead Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "<" #data <# #errors Line: 1 Col: 1 Expected tag name. Got something else instead Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "<#" #data </ #errors Line: 1 Col: 2 Expected closing tag. Unexpected end of file. Line: 1 Col: 2 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "</" #data </# #errors Line: 1 Col: 2 Expected closing tag. Unexpected character '#' found. Line: 1 Col: 3 Unexpected End of file. Expected DOCTYPE. #document | <!-- # --> | <html> | <head> | <body> #data <? #errors Line: 1 Col: 1 Expected tag name. Got '?' instead. (HTML doesn't support processing instructions.) Line: 1 Col: 2 Unexpected End of file. Expected DOCTYPE. #document | <!-- ? --> | <html> | <head> | <body> #data <?# #errors Line: 1 Col: 1 Expected tag name. Got '?' instead. (HTML doesn't support processing instructions.) Line: 1 Col: 3 Unexpected End of file. Expected DOCTYPE. #document | <!-- ?# --> | <html> | <head> | <body> #data <! #errors Line: 1 Col: 2 Expected '--' or 'DOCTYPE'. Not found. Line: 1 Col: 2 Unexpected End of file. Expected DOCTYPE. #document | <!-- --> | <html> | <head> | <body> #data <!# #errors Line: 1 Col: 3 Expected '--' or 'DOCTYPE'. Not found. Line: 1 Col: 3 Unexpected End of file. Expected DOCTYPE. #document | <!-- # --> | <html> | <head> | <body> #data <?COMMENT?> #errors Line: 1 Col: 1 Expected tag name. Got '?' instead. (HTML doesn't support processing instructions.) Line: 1 Col: 11 Unexpected End of file. Expected DOCTYPE. #document | <!-- ?COMMENT? --> | <html> | <head> | <body> #data <!COMMENT> #errors Line: 1 Col: 2 Expected '--' or 'DOCTYPE'. Not found. Line: 1 Col: 10 Unexpected End of file. Expected DOCTYPE. #document | <!-- COMMENT --> | <html> | <head> | <body> #data </ COMMENT > #errors Line: 1 Col: 2 Expected closing tag. Unexpected character ' ' found. Line: 1 Col: 12 Unexpected End of file. Expected DOCTYPE. #document | <!-- COMMENT --> | <html> | <head> | <body> #data <?COM--MENT?> #errors Line: 1 Col: 1 Expected tag name. Got '?' instead. (HTML doesn't support processing instructions.) Line: 1 Col: 13 Unexpected End of file. Expected DOCTYPE. #document | <!-- ?COM--MENT? --> | <html> | <head> | <body> #data <!COM--MENT> #errors Line: 1 Col: 2 Expected '--' or 'DOCTYPE'. Not found. Line: 1 Col: 12 Unexpected End of file. Expected DOCTYPE. #document | <!-- COM--MENT --> | <html> | <head> | <body> #data </ COM--MENT > #errors Line: 1 Col: 2 Expected closing tag. Unexpected character ' ' found. Line: 1 Col: 14 Unexpected End of file. Expected DOCTYPE. #document | <!-- COM--MENT --> | <html> | <head> | <body> #data <!DOCTYPE html><style> EOF #errors Line: 1 Col: 26 Unexpected end of file. Expected end tag (style). #document | <!DOCTYPE html> | <html> | <head> | <style> | " EOF" | <body> #data <!DOCTYPE html><script> <!-- </script> --> </script> EOF #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | " <!-- " | " " | <body> | "--> EOF" #data <b><p></b>TEST #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 10 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. #document | <html> | <head> | <body> | <b> | <p> | <b> | "TEST" #data <p id=a><b><p id=b></b>TEST #errors Line: 1 Col: 8 Unexpected start tag (p). Expected DOCTYPE. Line: 1 Col: 19 Unexpected end tag (p). Ignored. Line: 1 Col: 23 End tag (b) violates step 1, paragraph 2 of the adoption agency algorithm. #document | <html> | <head> | <body> | <p> | id="a" | <b> | <p> | id="b" | "TEST" #data <b id=a><p><b id=b></p></b>TEST #errors Line: 1 Col: 8 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 23 Unexpected end tag (p). Ignored. Line: 1 Col: 27 End tag (b) violates step 1, paragraph 2 of the adoption agency algorithm. Line: 1 Col: 31 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <b> | id="a" | <p> | <b> | id="b" | "TEST" #data <!DOCTYPE html><title>U-test</title><body><div><p>Test<u></p></div></body> #errors Line: 1 Col: 61 Unexpected end tag (p). Ignored. #document | <!DOCTYPE html> | <html> | <head> | <title> | "U-test" | <body> | <div> | <p> | "Test" | <u> #data <!DOCTYPE html><font><table></font></table></font> #errors Line: 1 Col: 35 Unexpected end tag (font) in table context caused voodoo mode. Line: 1 Col: 35 End tag (font) violates step 1, paragraph 1 of the adoption agency algorithm. #document | <!DOCTYPE html> | <html> | <head> | <body> | <font> | <table> #data <font><p>hello<b>cruel</font>world #errors Line: 1 Col: 6 Unexpected start tag (font). Expected DOCTYPE. Line: 1 Col: 29 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 29 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 34 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <font> | <p> | <font> | "hello" | <b> | "cruel" | <b> | "world" #data <b>Test</i>Test #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 11 End tag (i) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 15 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <b> | "TestTest" #data <b>A<cite>B<div>C #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 17 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <b> | "A" | <cite> | "B" | <div> | "C" #data <b>A<cite>B<div>C</cite>D #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 24 Unexpected end tag (cite). Ignored. Line: 1 Col: 25 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <b> | "A" | <cite> | "B" | <div> | "CD" #data <b>A<cite>B<div>C</b>D #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 21 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 22 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <b> | "A" | <cite> | "B" | <div> | <b> | "C" | "D" #data #errors Line: 1 Col: 0 Unexpected End of file. Expected DOCTYPE. #document | <html> | <head> | <body> #data <DIV> #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 5 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> #data <DIV> abc #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 9 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc" #data <DIV> abc <B> #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 13 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> #data <DIV> abc <B> def #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 17 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def" #data <DIV> abc <B> def <I> #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 21 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def " | <i> #data <DIV> abc <B> def <I> ghi #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 25 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def " | <i> | " ghi" #data <DIV> abc <B> def <I> ghi <P> #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 29 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def " | <i> | " ghi " | <p> #data <DIV> abc <B> def <I> ghi <P> jkl #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 33 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def " | <i> | " ghi " | <p> | " jkl" #data <DIV> abc <B> def <I> ghi <P> jkl </B> #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 38 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def " | <i> | " ghi " | <i> | <p> | <b> | " jkl " #data <DIV> abc <B> def <I> ghi <P> jkl </B> mno #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 42 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def " | <i> | " ghi " | <i> | <p> | <b> | " jkl " | " mno" #data <DIV> abc <B> def <I> ghi <P> jkl </B> mno </I> #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 47 End tag (i) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 47 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def " | <i> | " ghi " | <i> | <p> | <i> | <b> | " jkl " | " mno " #data <DIV> abc <B> def <I> ghi <P> jkl </B> mno </I> pqr #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 47 End tag (i) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 51 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def " | <i> | " ghi " | <i> | <p> | <i> | <b> | " jkl " | " mno " | " pqr" #data <DIV> abc <B> def <I> ghi <P> jkl </B> mno </I> pqr </P> #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 47 End tag (i) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 56 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def " | <i> | " ghi " | <i> | <p> | <i> | <b> | " jkl " | " mno " | " pqr " #data <DIV> abc <B> def <I> ghi <P> jkl </B> mno </I> pqr </P> stu #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 38 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 47 End tag (i) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 60 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | " abc " | <b> | " def " | <i> | " ghi " | <i> | <p> | <i> | <b> | " jkl " | " mno " | " pqr " | " stu" #data <test attribute----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------> #errors Line: 1 Col: 1040 Unexpected start tag (test). Expected DOCTYPE. Line: 1 Col: 1040 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <test> | attribute----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------="" #data <a href="blah">aba<table><a href="foo">br<tr><td></td></tr>x</table>aoe #errors Line: 1 Col: 15 Unexpected start tag (a). Expected DOCTYPE. Line: 1 Col: 39 Unexpected start tag (a) in table context caused voodoo mode. Line: 1 Col: 39 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 39 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 45 Unexpected implied end tag (a) in the table phase. Line: 1 Col: 68 Unexpected implied end tag (a) in the table phase. Line: 1 Col: 71 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <a> | href="blah" | "aba" | <a> | href="foo" | "br" | <a> | href="foo" | "x" | <table> | <tbody> | <tr> | <td> | <a> | href="foo" | "aoe" #data <a href="blah">aba<table><tr><td><a href="foo">br</td></tr>x</table>aoe #errors Line: 1 Col: 15 Unexpected start tag (a). Expected DOCTYPE. Line: 1 Col: 54 Got table cell end tag (td) while required end tags are missing. Line: 1 Col: 60 Unexpected non-space characters in table context caused voodoo mode. Line: 1 Col: 71 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <a> | href="blah" | "abax" | <table> | <tbody> | <tr> | <td> | <a> | href="foo" | "br" | "aoe" #data <table><a href="blah">aba<tr><td><a href="foo">br</td></tr>x</table>aoe #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 22 Unexpected start tag (a) in table context caused voodoo mode. Line: 1 Col: 29 Unexpected implied end tag (a) in the table phase. Line: 1 Col: 54 Got table cell end tag (td) while required end tags are missing. Line: 1 Col: 68 Unexpected implied end tag (a) in the table phase. Line: 1 Col: 71 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <a> | href="blah" | "aba" | <a> | href="blah" | "x" | <table> | <tbody> | <tr> | <td> | <a> | href="foo" | "br" | <a> | href="blah" | "aoe" #data <a href=a>aa<marquee>aa<a href=b>bb</marquee>aa #errors Line: 1 Col: 10 Unexpected start tag (a). Expected DOCTYPE. Line: 1 Col: 45 End tag (marquee) seen too early. Expected other end tag. Line: 1 Col: 47 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <a> | href="a" | "aa" | <marquee> | "aa" | <a> | href="b" | "bb" | "aa" #data <wbr><strike><code></strike><code><strike></code> #errors Line: 1 Col: 5 Unexpected start tag (wbr). Expected DOCTYPE. Line: 1 Col: 28 End tag (strike) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 49 Unexpected end tag (code). Ignored. #document | <html> | <head> | <body> | <wbr> | <strike> | <code> | <code> | <code> | <strike> #data <!DOCTYPE html><spacer>foo #errors 26: End of file seen and there were open elements. #document | <!DOCTYPE html> | <html> | <head> | <body> | <spacer> | "foo" #data <title><meta></title><link><title><meta></title> #errors Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. #document | <html> | <head> | <title> | "<meta>" | <link> | <title> | "<meta>" | <body> #data <style><!--</style><meta><script>--><link></script> #errors Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. Line: 1 Col: 51 Unexpected end of file. Expected end tag (style). #document | <html> | <head> | <style> | "<!--" | <meta> | <script> | "--><link>" | <body> #data <head><meta></head><link> #errors Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. Line: 1 Col: 25 Unexpected start tag (link) that can be in head. Moved. #document | <html> | <head> | <meta> | <link> | <body> #data <table><tr><tr><td><td><span><th><span>X</table> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 33 Got table cell end tag (td) while required end tags are missing. Line: 1 Col: 48 Got table cell end tag (th) while required end tags are missing. #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <tr> | <td> | <td> | <span> | <th> | <span> | "X" #data <body><body><base><link><meta><title><p></title><body><p></body> #errors Line: 1 Col: 6 Unexpected start tag (body). Expected DOCTYPE. Line: 1 Col: 12 Unexpected start tag (body). Line: 1 Col: 54 Unexpected start tag (body). Line: 1 Col: 64 Unexpected end tag (p). Missing end tag (body). #document | <html> | <head> | <body> | <base> | <link> | <meta> | <title> | "<p>" | <p> #data <textarea><p></textarea> #errors Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. #document | <html> | <head> | <body> | <textarea> | "<p>" #data <p><image></p> #errors Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE. Line: 1 Col: 10 Unexpected start tag (image). Treated as img. #document | <html> | <head> | <body> | <p> | <img> #data <a><table><a></table><p><a><div><a> #errors Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE. Line: 1 Col: 13 Unexpected start tag (a) in table context caused voodoo mode. Line: 1 Col: 13 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 13 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 21 Unexpected end tag (table). Expected end tag (a). Line: 1 Col: 27 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 27 End tag (a) violates step 1, paragraph 2 of the adoption agency algorithm. Line: 1 Col: 32 Unexpected end tag (p). Ignored. Line: 1 Col: 35 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 35 End tag (a) violates step 1, paragraph 2 of the adoption agency algorithm. Line: 1 Col: 35 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <a> | <a> | <table> | <p> | <a> | <div> | <a> #data <head></p><meta><p> #errors Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. Line: 1 Col: 10 Unexpected end tag (p). Ignored. #document | <html> | <head> | <meta> | <body> | <p> #data <head></html><meta><p> #errors Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. Line: 1 Col: 19 Unexpected start tag (meta). #document | <html> | <head> | <body> | <meta> | <p> #data <b><table><td><i></table> #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 25 Got table cell end tag (td) while required end tags are missing. Line: 1 Col: 25 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <b> | <table> | <tbody> | <tr> | <td> | <i> #data <b><table><td></b><i></table> #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 18 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 29 Got table cell end tag (td) while required end tags are missing. Line: 1 Col: 29 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <b> | <table> | <tbody> | <tr> | <td> | <i> #data <h1><h2> #errors 4: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. 8: Heading cannot be a child of another heading. 8: End of file seen and there were open elements. #document | <html> | <head> | <body> | <h1> | <h2> #data <a><p><a></a></p></a> #errors Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE. Line: 1 Col: 9 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 9 End tag (a) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 21 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. #document | <html> | <head> | <body> | <a> | <p> | <a> | <a> #data <b><button></b></button></b> #errors Line: 1 Col: 3 Unexpected start tag (b). Expected DOCTYPE. Line: 1 Col: 15 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. #document | <html> | <head> | <body> | <b> | <button> | <b> #data <p><b><div><marquee></p></b></div> #errors Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE. Line: 1 Col: 11 Unexpected end tag (p). Ignored. Line: 1 Col: 24 Unexpected end tag (p). Ignored. Line: 1 Col: 28 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 34 End tag (div) seen too early. Expected other end tag. Line: 1 Col: 34 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <p> | <b> | <div> | <b> | <marquee> | <p> #data <script></script></div><title></title><p><p> #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 23 Unexpected end tag (div). Ignored. #document | <html> | <head> | <script> | <title> | <body> | <p> | <p> #data <p><hr></p> #errors Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE. Line: 1 Col: 11 Unexpected end tag (p). Ignored. #document | <html> | <head> | <body> | <p> | <hr> | <p> #data <select><b><option><select><option></b></select> #errors Line: 1 Col: 8 Unexpected start tag (select). Expected DOCTYPE. Line: 1 Col: 11 Unexpected start tag token (b) in the select phase. Ignored. Line: 1 Col: 27 Unexpected select start tag in the select phase treated as select end tag. Line: 1 Col: 39 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 48 Unexpected end tag (select). Ignored. Line: 1 Col: 48 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <select> | <option> | <option> #data <html><head><title></title><body></body></html> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. #document | <html> | <head> | <title> | <body> #data <a><table><td><a><table></table><a></tr><a></table><a> #errors Line: 1 Col: 3 Unexpected start tag (a). Expected DOCTYPE. Line: 1 Col: 14 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 35 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 40 Got table cell end tag (td) while required end tags are missing. Line: 1 Col: 43 Unexpected start tag (a) in table context caused voodoo mode. Line: 1 Col: 43 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 43 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 51 Unexpected implied end tag (a) in the table phase. Line: 1 Col: 54 Unexpected start tag (a) implies end tag (a). Line: 1 Col: 54 End tag (a) violates step 1, paragraph 2 of the adoption agency algorithm. Line: 1 Col: 54 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <a> | <a> | <table> | <tbody> | <tr> | <td> | <a> | <table> | <a> | <a> #data <ul><li></li><div><li></div><li><li><div><li><address><li><b><em></b><li></ul> #errors Line: 1 Col: 4 Unexpected start tag (ul). Expected DOCTYPE. Line: 1 Col: 45 Missing end tag (div, li). Line: 1 Col: 58 Missing end tag (address, li). Line: 1 Col: 69 End tag (b) violates step 1, paragraph 3 of the adoption agency algorithm. #document | <html> | <head> | <body> | <ul> | <li> | <div> | <li> | <li> | <li> | <div> | <li> | <address> | <li> | <b> | <em> | <li> #data <ul><li><ul></li><li>a</li></ul></li></ul> #errors XXX: fix me #document | <html> | <head> | <body> | <ul> | <li> | <ul> | <li> | "a" #data <frameset><frame><frameset><frame></frameset><noframes></noframes></frameset> #errors Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. #document | <html> | <head> | <frameset> | <frame> | <frameset> | <frame> | <noframes> #data <h1><table><td><h3></table><h3></h1> #errors 4: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. 15: “td†start tag in table body. 27: Unclosed elements. 31: Heading cannot be a child of another heading. 36: End tag “h1†seen but there were unclosed elements. #document | <html> | <head> | <body> | <h1> | <table> | <tbody> | <tr> | <td> | <h3> | <h3> #data <table><colgroup><col><colgroup><col><col><col><colgroup><col><col><thead><tr><td></table> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. #document | <html> | <head> | <body> | <table> | <colgroup> | <col> | <colgroup> | <col> | <col> | <col> | <colgroup> | <col> | <col> | <thead> | <tr> | <td> #data <table><col><tbody><col><tr><col><td><col></table><col> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 37 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 55 Unexpected start tag col. Ignored. #document | <html> | <head> | <body> | <table> | <colgroup> | <col> | <tbody> | <colgroup> | <col> | <tbody> | <tr> | <colgroup> | <col> | <tbody> | <tr> | <td> | <colgroup> | <col> #data <table><colgroup><tbody><colgroup><tr><colgroup><td><colgroup></table><colgroup> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 52 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 80 Unexpected start tag colgroup. Ignored. #document | <html> | <head> | <body> | <table> | <colgroup> | <tbody> | <colgroup> | <tbody> | <tr> | <colgroup> | <tbody> | <tr> | <td> | <colgroup> #data </strong></b></em></i></u></strike></s></blink></tt></pre></big></small></font></select></h1></h2></h3></h4></h5></h6></body></br></a></img></title></span></style></script></table></th></td></tr></frame></area></link></param></hr></input></col></base></meta></basefont></bgsound></embed></spacer></p></dd></dt></caption></colgroup></tbody></tfoot></thead></address></blockquote></center></dir></div></dl></fieldset></listing></menu></ol></ul></li></nobr></wbr></form></button></marquee></object></html></frameset></head></iframe></image></isindex></noembed></noframes></noscript></optgroup></option></plaintext></textarea> #errors Line: 1 Col: 9 Unexpected end tag (strong). Expected DOCTYPE. Line: 1 Col: 9 Unexpected end tag (strong) after the (implied) root element. Line: 1 Col: 13 Unexpected end tag (b) after the (implied) root element. Line: 1 Col: 18 Unexpected end tag (em) after the (implied) root element. Line: 1 Col: 22 Unexpected end tag (i) after the (implied) root element. Line: 1 Col: 26 Unexpected end tag (u) after the (implied) root element. Line: 1 Col: 35 Unexpected end tag (strike) after the (implied) root element. Line: 1 Col: 39 Unexpected end tag (s) after the (implied) root element. Line: 1 Col: 47 Unexpected end tag (blink) after the (implied) root element. Line: 1 Col: 52 Unexpected end tag (tt) after the (implied) root element. Line: 1 Col: 58 Unexpected end tag (pre) after the (implied) root element. Line: 1 Col: 64 Unexpected end tag (big) after the (implied) root element. Line: 1 Col: 72 Unexpected end tag (small) after the (implied) root element. Line: 1 Col: 79 Unexpected end tag (font) after the (implied) root element. Line: 1 Col: 88 Unexpected end tag (select) after the (implied) root element. Line: 1 Col: 93 Unexpected end tag (h1) after the (implied) root element. Line: 1 Col: 98 Unexpected end tag (h2) after the (implied) root element. Line: 1 Col: 103 Unexpected end tag (h3) after the (implied) root element. Line: 1 Col: 108 Unexpected end tag (h4) after the (implied) root element. Line: 1 Col: 113 Unexpected end tag (h5) after the (implied) root element. Line: 1 Col: 118 Unexpected end tag (h6) after the (implied) root element. Line: 1 Col: 125 Unexpected end tag (body) after the (implied) root element. Line: 1 Col: 130 Unexpected end tag (br). Treated as br element. Line: 1 Col: 134 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 140 This element (img) has no end tag. Line: 1 Col: 148 Unexpected end tag (title). Ignored. Line: 1 Col: 155 Unexpected end tag (span). Ignored. Line: 1 Col: 163 Unexpected end tag (style). Ignored. Line: 1 Col: 172 Unexpected end tag (script). Ignored. Line: 1 Col: 180 Unexpected end tag (table). Ignored. Line: 1 Col: 185 Unexpected end tag (th). Ignored. Line: 1 Col: 190 Unexpected end tag (td). Ignored. Line: 1 Col: 195 Unexpected end tag (tr). Ignored. Line: 1 Col: 203 This element (frame) has no end tag. Line: 1 Col: 210 This element (area) has no end tag. Line: 1 Col: 217 Unexpected end tag (link). Ignored. Line: 1 Col: 225 This element (param) has no end tag. Line: 1 Col: 230 This element (hr) has no end tag. Line: 1 Col: 238 This element (input) has no end tag. Line: 1 Col: 244 Unexpected end tag (col). Ignored. Line: 1 Col: 251 Unexpected end tag (base). Ignored. Line: 1 Col: 258 Unexpected end tag (meta). Ignored. Line: 1 Col: 269 This element (basefont) has no end tag. Line: 1 Col: 279 This element (bgsound) has no end tag. Line: 1 Col: 287 This element (embed) has no end tag. Line: 1 Col: 296 This element (spacer) has no end tag. Line: 1 Col: 300 Unexpected end tag (p). Ignored. Line: 1 Col: 305 End tag (dd) seen too early. Expected other end tag. Line: 1 Col: 310 End tag (dt) seen too early. Expected other end tag. Line: 1 Col: 320 Unexpected end tag (caption). Ignored. Line: 1 Col: 331 Unexpected end tag (colgroup). Ignored. Line: 1 Col: 339 Unexpected end tag (tbody). Ignored. Line: 1 Col: 347 Unexpected end tag (tfoot). Ignored. Line: 1 Col: 355 Unexpected end tag (thead). Ignored. Line: 1 Col: 365 End tag (address) seen too early. Expected other end tag. Line: 1 Col: 378 End tag (blockquote) seen too early. Expected other end tag. Line: 1 Col: 387 End tag (center) seen too early. Expected other end tag. Line: 1 Col: 393 Unexpected end tag (dir). Ignored. Line: 1 Col: 399 End tag (div) seen too early. Expected other end tag. Line: 1 Col: 404 End tag (dl) seen too early. Expected other end tag. Line: 1 Col: 415 End tag (fieldset) seen too early. Expected other end tag. Line: 1 Col: 425 End tag (listing) seen too early. Expected other end tag. Line: 1 Col: 432 End tag (menu) seen too early. Expected other end tag. Line: 1 Col: 437 End tag (ol) seen too early. Expected other end tag. Line: 1 Col: 442 End tag (ul) seen too early. Expected other end tag. Line: 1 Col: 447 End tag (li) seen too early. Expected other end tag. Line: 1 Col: 454 End tag (nobr) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 460 This element (wbr) has no end tag. Line: 1 Col: 476 End tag (button) seen too early. Expected other end tag. Line: 1 Col: 486 End tag (marquee) seen too early. Expected other end tag. Line: 1 Col: 495 End tag (object) seen too early. Expected other end tag. Line: 1 Col: 513 Unexpected end tag (html). Ignored. Line: 1 Col: 513 Unexpected end tag (frameset). Ignored. Line: 1 Col: 520 Unexpected end tag (head). Ignored. Line: 1 Col: 529 Unexpected end tag (iframe). Ignored. Line: 1 Col: 537 This element (image) has no end tag. Line: 1 Col: 547 This element (isindex) has no end tag. Line: 1 Col: 557 Unexpected end tag (noembed). Ignored. Line: 1 Col: 568 Unexpected end tag (noframes). Ignored. Line: 1 Col: 579 Unexpected end tag (noscript). Ignored. Line: 1 Col: 590 Unexpected end tag (optgroup). Ignored. Line: 1 Col: 599 Unexpected end tag (option). Ignored. Line: 1 Col: 611 Unexpected end tag (plaintext). Ignored. Line: 1 Col: 622 Unexpected end tag (textarea). Ignored. #document | <html> | <head> | <body> | <br> | <p> #data <table><tr></strong></b></em></i></u></strike></s></blink></tt></pre></big></small></font></select></h1></h2></h3></h4></h5></h6></body></br></a></img></title></span></style></script></table></th></td></tr></frame></area></link></param></hr></input></col></base></meta></basefont></bgsound></embed></spacer></p></dd></dt></caption></colgroup></tbody></tfoot></thead></address></blockquote></center></dir></div></dl></fieldset></listing></menu></ol></ul></li></nobr></wbr></form></button></marquee></object></html></frameset></head></iframe></image></isindex></noembed></noframes></noscript></optgroup></option></plaintext></textarea> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 20 Unexpected end tag (strong) in table context caused voodoo mode. Line: 1 Col: 20 End tag (strong) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 24 Unexpected end tag (b) in table context caused voodoo mode. Line: 1 Col: 24 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 29 Unexpected end tag (em) in table context caused voodoo mode. Line: 1 Col: 29 End tag (em) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 33 Unexpected end tag (i) in table context caused voodoo mode. Line: 1 Col: 33 End tag (i) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 37 Unexpected end tag (u) in table context caused voodoo mode. Line: 1 Col: 37 End tag (u) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 46 Unexpected end tag (strike) in table context caused voodoo mode. Line: 1 Col: 46 End tag (strike) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 50 Unexpected end tag (s) in table context caused voodoo mode. Line: 1 Col: 50 End tag (s) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 58 Unexpected end tag (blink) in table context caused voodoo mode. Line: 1 Col: 58 Unexpected end tag (blink). Ignored. Line: 1 Col: 63 Unexpected end tag (tt) in table context caused voodoo mode. Line: 1 Col: 63 End tag (tt) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 69 Unexpected end tag (pre) in table context caused voodoo mode. Line: 1 Col: 69 End tag (pre) seen too early. Expected other end tag. Line: 1 Col: 75 Unexpected end tag (big) in table context caused voodoo mode. Line: 1 Col: 75 End tag (big) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 83 Unexpected end tag (small) in table context caused voodoo mode. Line: 1 Col: 83 End tag (small) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 90 Unexpected end tag (font) in table context caused voodoo mode. Line: 1 Col: 90 End tag (font) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 99 Unexpected end tag (select) in table context caused voodoo mode. Line: 1 Col: 99 Unexpected end tag (select). Ignored. Line: 1 Col: 104 Unexpected end tag (h1) in table context caused voodoo mode. Line: 1 Col: 104 End tag (h1) seen too early. Expected other end tag. Line: 1 Col: 109 Unexpected end tag (h2) in table context caused voodoo mode. Line: 1 Col: 109 End tag (h2) seen too early. Expected other end tag. Line: 1 Col: 114 Unexpected end tag (h3) in table context caused voodoo mode. Line: 1 Col: 114 End tag (h3) seen too early. Expected other end tag. Line: 1 Col: 119 Unexpected end tag (h4) in table context caused voodoo mode. Line: 1 Col: 119 End tag (h4) seen too early. Expected other end tag. Line: 1 Col: 124 Unexpected end tag (h5) in table context caused voodoo mode. Line: 1 Col: 124 End tag (h5) seen too early. Expected other end tag. Line: 1 Col: 129 Unexpected end tag (h6) in table context caused voodoo mode. Line: 1 Col: 129 End tag (h6) seen too early. Expected other end tag. Line: 1 Col: 136 Unexpected end tag (body) in the table row phase. Ignored. Line: 1 Col: 141 Unexpected end tag (br) in table context caused voodoo mode. Line: 1 Col: 141 Unexpected end tag (br). Treated as br element. Line: 1 Col: 145 Unexpected end tag (a) in table context caused voodoo mode. Line: 1 Col: 145 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 151 Unexpected end tag (img) in table context caused voodoo mode. Line: 1 Col: 151 This element (img) has no end tag. Line: 1 Col: 159 Unexpected end tag (title) in table context caused voodoo mode. Line: 1 Col: 159 Unexpected end tag (title). Ignored. Line: 1 Col: 166 Unexpected end tag (span) in table context caused voodoo mode. Line: 1 Col: 166 Unexpected end tag (span). Ignored. Line: 1 Col: 174 Unexpected end tag (style) in table context caused voodoo mode. Line: 1 Col: 174 Unexpected end tag (style). Ignored. Line: 1 Col: 183 Unexpected end tag (script) in table context caused voodoo mode. Line: 1 Col: 183 Unexpected end tag (script). Ignored. Line: 1 Col: 196 Unexpected end tag (th). Ignored. Line: 1 Col: 201 Unexpected end tag (td). Ignored. Line: 1 Col: 206 Unexpected end tag (tr). Ignored. Line: 1 Col: 214 This element (frame) has no end tag. Line: 1 Col: 221 This element (area) has no end tag. Line: 1 Col: 228 Unexpected end tag (link). Ignored. Line: 1 Col: 236 This element (param) has no end tag. Line: 1 Col: 241 This element (hr) has no end tag. Line: 1 Col: 249 This element (input) has no end tag. Line: 1 Col: 255 Unexpected end tag (col). Ignored. Line: 1 Col: 262 Unexpected end tag (base). Ignored. Line: 1 Col: 269 Unexpected end tag (meta). Ignored. Line: 1 Col: 280 This element (basefont) has no end tag. Line: 1 Col: 290 This element (bgsound) has no end tag. Line: 1 Col: 298 This element (embed) has no end tag. Line: 1 Col: 307 This element (spacer) has no end tag. Line: 1 Col: 311 Unexpected end tag (p). Ignored. Line: 1 Col: 316 End tag (dd) seen too early. Expected other end tag. Line: 1 Col: 321 End tag (dt) seen too early. Expected other end tag. Line: 1 Col: 331 Unexpected end tag (caption). Ignored. Line: 1 Col: 342 Unexpected end tag (colgroup). Ignored. Line: 1 Col: 350 Unexpected end tag (tbody). Ignored. Line: 1 Col: 358 Unexpected end tag (tfoot). Ignored. Line: 1 Col: 366 Unexpected end tag (thead). Ignored. Line: 1 Col: 376 End tag (address) seen too early. Expected other end tag. Line: 1 Col: 389 End tag (blockquote) seen too early. Expected other end tag. Line: 1 Col: 398 End tag (center) seen too early. Expected other end tag. Line: 1 Col: 404 Unexpected end tag (dir). Ignored. Line: 1 Col: 410 End tag (div) seen too early. Expected other end tag. Line: 1 Col: 415 End tag (dl) seen too early. Expected other end tag. Line: 1 Col: 426 End tag (fieldset) seen too early. Expected other end tag. Line: 1 Col: 436 End tag (listing) seen too early. Expected other end tag. Line: 1 Col: 443 End tag (menu) seen too early. Expected other end tag. Line: 1 Col: 448 End tag (ol) seen too early. Expected other end tag. Line: 1 Col: 453 End tag (ul) seen too early. Expected other end tag. Line: 1 Col: 458 End tag (li) seen too early. Expected other end tag. Line: 1 Col: 465 End tag (nobr) violates step 1, paragraph 1 of the adoption agency algorithm. Line: 1 Col: 471 This element (wbr) has no end tag. Line: 1 Col: 487 End tag (button) seen too early. Expected other end tag. Line: 1 Col: 497 End tag (marquee) seen too early. Expected other end tag. Line: 1 Col: 506 End tag (object) seen too early. Expected other end tag. Line: 1 Col: 524 Unexpected end tag (html). Ignored. Line: 1 Col: 524 Unexpected end tag (frameset). Ignored. Line: 1 Col: 531 Unexpected end tag (head). Ignored. Line: 1 Col: 540 Unexpected end tag (iframe). Ignored. Line: 1 Col: 548 This element (image) has no end tag. Line: 1 Col: 558 This element (isindex) has no end tag. Line: 1 Col: 568 Unexpected end tag (noembed). Ignored. Line: 1 Col: 579 Unexpected end tag (noframes). Ignored. Line: 1 Col: 590 Unexpected end tag (noscript). Ignored. Line: 1 Col: 601 Unexpected end tag (optgroup). Ignored. Line: 1 Col: 610 Unexpected end tag (option). Ignored. Line: 1 Col: 622 Unexpected end tag (plaintext). Ignored. Line: 1 Col: 633 Unexpected end tag (textarea). Ignored. #document | <html> | <head> | <body> | <br> | <table> | <tbody> | <tr> | <p> #data <frameset> #errors Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. Line: 1 Col: 10 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <frameset> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/scriptdata01.dat�����������������0000644�0000153�0000161�00000010301�12321735652�027660� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data FOO<script>'Hello'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'Hello'" | "BAR" #data FOO<script></script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "BAR" #data FOO<script></script >BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "BAR" #data FOO<script></script/>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "BAR" #data FOO<script></script/ >BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "BAR" #data FOO<script type="text/plain"></scriptx>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | type="text/plain" | "</scriptx>BAR" #data FOO<script></script foo=">" dd>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "BAR" #data FOO<script>'<'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'<'" | "BAR" #data FOO<script>'<!'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'<!'" | "BAR" #data FOO<script>'<!-'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'<!-'" | "BAR" #data FOO<script>'<!--'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'<!--'" | "BAR" #data FOO<script>'<!---'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'<!---'" | "BAR" #data FOO<script>'<!-->'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'<!-->'" | "BAR" #data FOO<script>'<!-->'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'<!-->'" | "BAR" #data FOO<script>'<!-- potato'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'<!-- potato'" | "BAR" #data FOO<script>'<!-- <sCrIpt'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'<!-- <sCrIpt'" | "BAR" #data FOO<script type="text/plain">'<!-- <sCrIpt>'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | type="text/plain" | "'<!-- <sCrIpt>'</script>BAR" #data FOO<script type="text/plain">'<!-- <sCrIpt> -'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | type="text/plain" | "'<!-- <sCrIpt> -'</script>BAR" #data FOO<script type="text/plain">'<!-- <sCrIpt> --'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | type="text/plain" | "'<!-- <sCrIpt> --'</script>BAR" #data FOO<script>'<!-- <sCrIpt> -->'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | "'<!-- <sCrIpt> -->'" | "BAR" #data FOO<script type="text/plain">'<!-- <sCrIpt> --!>'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | type="text/plain" | "'<!-- <sCrIpt> --!>'</script>BAR" #data FOO<script type="text/plain">'<!-- <sCrIpt> -- >'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | type="text/plain" | "'<!-- <sCrIpt> -- >'</script>BAR" #data FOO<script type="text/plain">'<!-- <sCrIpt '</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | type="text/plain" | "'<!-- <sCrIpt '</script>BAR" #data FOO<script type="text/plain">'<!-- <sCrIpt/'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | type="text/plain" | "'<!-- <sCrIpt/'</script>BAR" #data FOO<script type="text/plain">'<!-- <sCrIpt\'</script>BAR #errors #document | <html> | <head> | <body> | "FOO" | <script> | type="text/plain" | "'<!-- <sCrIpt\'" | "BAR" #data FOO<script type="text/plain">'<!-- <sCrIpt/'</script>BAR</script>QUX #errors #document | <html> | <head> | <body> | "FOO" | <script> | type="text/plain" | "'<!-- <sCrIpt/'</script>BAR" | "QUX" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/pending-spec-changes.dat���������0000644�0000153�0000161�00000002455�12321735652�031356� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <input type="hidden"><frameset> #errors 21: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. 31: “frameset†start tag seen. 31: End of file seen and there were open elements. #document | <html> | <head> | <frameset> #data <!DOCTYPE html><table><caption><svg>foo</table>bar #errors 47: End tag “table†did not match the name of the current open element (“svgâ€). 47: “table†closed but “caption†was still open. 47: End tag “table†seen, but there were open elements. 36: Unclosed element “svgâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <svg svg> | "foo" | "bar" #data <table><tr><td><svg><desc><td></desc><circle> #errors 7: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. 30: A table cell was implicitly closed, but there were open elements. 26: Unclosed element “descâ€. 20: Unclosed element “svgâ€. 37: Stray end tag “descâ€. 45: End of file seen and there were open elements. 45: Unclosed element “circleâ€. 7: Unclosed element “tableâ€. #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <svg svg> | <svg desc> | <td> | <circle> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/entities01.dat�������������������0000644�0000153�0000161�00000013421�12321735652�027354� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data FOO&gt;BAR #errors #document | <html> | <head> | <body> | "FOO>BAR" #data FOO&gtBAR #errors #document | <html> | <head> | <body> | "FOO>BAR" #data FOO&gt BAR #errors #document | <html> | <head> | <body> | "FOO> BAR" #data FOO&gt;;;BAR #errors #document | <html> | <head> | <body> | "FOO>;;BAR" #data I'm &notit; I tell you #errors #document | <html> | <head> | <body> | "I'm ¬it; I tell you" #data I'm &notin; I tell you #errors #document | <html> | <head> | <body> | "I'm ∉ I tell you" #data FOO& BAR #errors #document | <html> | <head> | <body> | "FOO& BAR" #data FOO&<BAR> #errors #document | <html> | <head> | <body> | "FOO&" | <bar> #data FOO&&&&gt;BAR #errors #document | <html> | <head> | <body> | "FOO&&&>BAR" #data FOO&#41;BAR #errors #document | <html> | <head> | <body> | "FOO)BAR" #data FOO&#x41;BAR #errors #document | <html> | <head> | <body> | "FOOABAR" #data FOO&#X41;BAR #errors #document | <html> | <head> | <body> | "FOOABAR" #data FOO&#BAR #errors #document | <html> | <head> | <body> | "FOO&#BAR" #data FOO&#ZOO #errors #document | <html> | <head> | <body> | "FOO&#ZOO" #data FOO&#xBAR #errors #document | <html> | <head> | <body> | "FOOºR" #data FOO&#xZOO #errors #document | <html> | <head> | <body> | "FOO&#xZOO" #data FOO&#XZOO #errors #document | <html> | <head> | <body> | "FOO&#XZOO" #data FOO&#41BAR #errors #document | <html> | <head> | <body> | "FOO)BAR" #data FOO&#x41BAR #errors #document | <html> | <head> | <body> | "FOO䆺R" #data FOO&#x41ZOO #errors #document | <html> | <head> | <body> | "FOOAZOO" #data FOO&#x0000;ZOO #errors #document | <html> | <head> | <body> | "FOO�ZOO" #data FOO&#x0078;ZOO #errors #document | <html> | <head> | <body> | "FOOxZOO" #data FOO&#x0079;ZOO #errors #document | <html> | <head> | <body> | "FOOyZOO" #data FOO&#x0080;ZOO #errors #document | <html> | <head> | <body> | "FOO€ZOO" #data FOO&#x0081;ZOO #errors #document | <html> | <head> | <body> | "FOOÂZOO" #data FOO&#x0082;ZOO #errors #document | <html> | <head> | <body> | "FOO‚ZOO" #data FOO&#x0083;ZOO #errors #document | <html> | <head> | <body> | "FOOÆ’ZOO" #data FOO&#x0084;ZOO #errors #document | <html> | <head> | <body> | "FOO„ZOO" #data FOO&#x0085;ZOO #errors #document | <html> | <head> | <body> | "FOO…ZOO" #data FOO&#x0086;ZOO #errors #document | <html> | <head> | <body> | "FOO†ZOO" #data FOO&#x0087;ZOO #errors #document | <html> | <head> | <body> | "FOO‡ZOO" #data FOO&#x0088;ZOO #errors #document | <html> | <head> | <body> | "FOOˆZOO" #data FOO&#x0089;ZOO #errors #document | <html> | <head> | <body> | "FOO‰ZOO" #data FOO&#x008A;ZOO #errors #document | <html> | <head> | <body> | "FOOÅ ZOO" #data FOO&#x008B;ZOO #errors #document | <html> | <head> | <body> | "FOO‹ZOO" #data FOO&#x008C;ZOO #errors #document | <html> | <head> | <body> | "FOOÅ’ZOO" #data FOO&#x008D;ZOO #errors #document | <html> | <head> | <body> | "FOOÂZOO" #data FOO&#x008E;ZOO #errors #document | <html> | <head> | <body> | "FOOŽZOO" #data FOO&#x008F;ZOO #errors #document | <html> | <head> | <body> | "FOOÂZOO" #data FOO&#x0090;ZOO #errors #document | <html> | <head> | <body> | "FOOÂZOO" #data FOO&#x0091;ZOO #errors #document | <html> | <head> | <body> | "FOO‘ZOO" #data FOO&#x0092;ZOO #errors #document | <html> | <head> | <body> | "FOO’ZOO" #data FOO&#x0093;ZOO #errors #document | <html> | <head> | <body> | "FOO“ZOO" #data FOO&#x0094;ZOO #errors #document | <html> | <head> | <body> | "FOOâ€ZOO" #data FOO&#x0095;ZOO #errors #document | <html> | <head> | <body> | "FOO•ZOO" #data FOO&#x0096;ZOO #errors #document | <html> | <head> | <body> | "FOO–ZOO" #data FOO&#x0097;ZOO #errors #document | <html> | <head> | <body> | "FOO—ZOO" #data FOO&#x0098;ZOO #errors #document | <html> | <head> | <body> | "FOOËœZOO" #data FOO&#x0099;ZOO #errors #document | <html> | <head> | <body> | "FOOâ„¢ZOO" #data FOO&#x009A;ZOO #errors #document | <html> | <head> | <body> | "FOOÅ¡ZOO" #data FOO&#x009B;ZOO #errors #document | <html> | <head> | <body> | "FOO›ZOO" #data FOO&#x009C;ZOO #errors #document | <html> | <head> | <body> | "FOOÅ“ZOO" #data FOO&#x009D;ZOO #errors #document | <html> | <head> | <body> | "FOOÂZOO" #data FOO&#x009E;ZOO #errors #document | <html> | <head> | <body> | "FOOžZOO" #data FOO&#x009F;ZOO #errors #document | <html> | <head> | <body> | "FOOŸZOO" #data FOO&#x00A0;ZOO #errors #document | <html> | <head> | <body> | "FOO ZOO" #data FOO&#xD7FF;ZOO #errors #document | <html> | <head> | <body> | "FOO퟿ZOO" #data FOO&#xD800;ZOO #errors #document | <html> | <head> | <body> | "FOO�ZOO" #data FOO&#xD801;ZOO #errors #document | <html> | <head> | <body> | "FOO�ZOO" #data FOO&#xDFFE;ZOO #errors #document | <html> | <head> | <body> | "FOO�ZOO" #data FOO&#xDFFF;ZOO #errors #document | <html> | <head> | <body> | "FOO�ZOO" #data FOO&#xE000;ZOO #errors #document | <html> | <head> | <body> | "FOOZOO" #data FOO&#x10FFFE;ZOO #errors #document | <html> | <head> | <body> | "FOOô¿¾ZOO" #data FOO&#x1087D4;ZOO #errors #document | <html> | <head> | <body> | "FOOôˆŸ”ZOO" #data FOO&#x10FFFF;ZOO #errors #document | <html> | <head> | <body> | "FOOô¿¿ZOO" #data FOO&#x110000;ZOO #errors #document | <html> | <head> | <body> | "FOO�ZOO" #data FOO&#xFFFFFF;ZOO #errors #document | <html> | <head> | <body> | "FOO�ZOO" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests4.dat�����������������������0000644�0000153�0000161�00000001565�12321735652�026623� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data direct div content #errors #document-fragment div #document | "direct div content" #data direct textarea content #errors #document-fragment textarea #document | "direct textarea content" #data textarea content with <em>pseudo</em> <foo>markup #errors #document-fragment textarea #document | "textarea content with <em>pseudo</em> <foo>markup" #data this is &#x0043;DATA inside a <style> element #errors #document-fragment style #document | "this is &#x0043;DATA inside a <style> element" #data </plaintext> #errors #document-fragment plaintext #document | "</plaintext>" #data setting html's innerHTML #errors Line: 1 Col: 24 Unexpected EOF in inner html mode. #document-fragment html #document | <head> | <body> | "setting html's innerHTML" #data <title>setting head's innerHTML</title> #errors #document-fragment head #document | <title> | "setting head's innerHTML" �������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/README���������������������������0000644�0000153�0000161�00000002746�12321735652�025565� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������The *.dat files in this directory are copied from The WebKit Open Source Project, specifically $WEBKITROOT/LayoutTests/html5lib/resources. WebKit is licensed under a BSD style license. http://webkit.org/coding/bsd-license.html says: Copyright (C) 2009 Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ��������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/isindex.dat����������������������0000644�0000153�0000161�00000001074�12321735652�027033� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <isindex> #errors #document | <html> | <head> | <body> | <form> | <hr> | <label> | "This is a searchable index. Enter search keywords: " | <input> | name="isindex" | <hr> #data <isindex name="A" action="B" prompt="C" foo="D"> #errors #document | <html> | <head> | <body> | <form> | action="B" | <hr> | <label> | "C" | <input> | foo="D" | name="isindex" | <hr> #data <form><isindex> #errors #document | <html> | <head> | <body> | <form> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests2.dat�����������������������0000644�0000153�0000161�00000032313�12321735652�026614� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html>Test #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "Test" #data <textarea>test</div>test #errors Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. Line: 1 Col: 24 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <textarea> | "test</div>test" #data <table><td> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 11 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 11 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> #data <table><td>test</tbody></table> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 11 Unexpected table cell start tag (td) in the table body phase. #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | "test" #data <frame>test #errors Line: 1 Col: 7 Unexpected start tag (frame). Expected DOCTYPE. Line: 1 Col: 7 Unexpected start tag frame. Ignored. #document | <html> | <head> | <body> | "test" #data <!DOCTYPE html><frameset>test #errors Line: 1 Col: 29 Unepxected characters in the frameset phase. Characters ignored. Line: 1 Col: 29 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!DOCTYPE html><frameset><!DOCTYPE html> #errors Line: 1 Col: 40 Unexpected DOCTYPE. Ignored. Line: 1 Col: 40 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!DOCTYPE html><font><p><b>test</font> #errors Line: 1 Col: 38 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm. Line: 1 Col: 38 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm. #document | <!DOCTYPE html> | <html> | <head> | <body> | <font> | <p> | <font> | <b> | "test" #data <!DOCTYPE html><dt><div><dd> #errors Line: 1 Col: 28 Missing end tag (div, dt). #document | <!DOCTYPE html> | <html> | <head> | <body> | <dt> | <div> | <dd> #data <script></x #errors Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). #document | <html> | <head> | <script> | "</x" | <body> #data <table><plaintext><td> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 18 Unexpected start tag (plaintext) in table context caused voodoo mode. Line: 1 Col: 22 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | <plaintext> | "<td>" | <table> #data <plaintext></plaintext> #errors Line: 1 Col: 11 Unexpected start tag (plaintext). Expected DOCTYPE. Line: 1 Col: 23 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!DOCTYPE html><table><tr>TEST #errors Line: 1 Col: 30 Unexpected non-space characters in table context caused voodoo mode. Line: 1 Col: 30 Unexpected end of file. Expected table content. #document | <!DOCTYPE html> | <html> | <head> | <body> | "TEST" | <table> | <tbody> | <tr> #data <!DOCTYPE html><body t1=1><body t2=2><body t3=3 t4=4> #errors Line: 1 Col: 37 Unexpected start tag (body). Line: 1 Col: 53 Unexpected start tag (body). #document | <!DOCTYPE html> | <html> | <head> | <body> | t1="1" | t2="2" | t3="3" | t4="4" #data </b test #errors Line: 1 Col: 8 Unexpected end of file in attribute name. Line: 1 Col: 8 End tag contains unexpected attributes. Line: 1 Col: 8 Unexpected end tag (b). Expected DOCTYPE. Line: 1 Col: 8 Unexpected end tag (b) after the (implied) root element. #document | <html> | <head> | <body> #data <!DOCTYPE html></b test<b &=&amp>X #errors Line: 1 Col: 32 Named entity didn't end with ';'. Line: 1 Col: 33 End tag contains unexpected attributes. Line: 1 Col: 33 Unexpected end tag (b) after the (implied) root element. #document | <!DOCTYPE html> | <html> | <head> | <body> | "X" #data <!doctypehtml><scrIPt type=text/x-foobar;baz>X</SCRipt #errors Line: 1 Col: 9 No space after literal string 'DOCTYPE'. Line: 1 Col: 54 Unexpected end of file in the tag name. #document | <!DOCTYPE html> | <html> | <head> | <script> | type="text/x-foobar;baz" | "X</SCRipt" | <body> #data & #errors Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "&" #data &# #errors Line: 1 Col: 1 Numeric entity expected. Got end of file instead. Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "&#" #data &#X #errors Line: 1 Col: 3 Numeric entity expected but none found. Line: 1 Col: 3 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "&#X" #data &#x #errors Line: 1 Col: 3 Numeric entity expected but none found. Line: 1 Col: 3 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "&#x" #data &#45 #errors Line: 1 Col: 4 Numeric entity didn't end with ';'. Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "-" #data &x-test #errors Line: 1 Col: 1 Named entity expected. Got none. Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "&x-test" #data <!doctypehtml><p><li> #errors Line: 1 Col: 9 No space after literal string 'DOCTYPE'. #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <li> #data <!doctypehtml><p><dt> #errors Line: 1 Col: 9 No space after literal string 'DOCTYPE'. #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <dt> #data <!doctypehtml><p><dd> #errors Line: 1 Col: 9 No space after literal string 'DOCTYPE'. #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <dd> #data <!doctypehtml><p><form> #errors Line: 1 Col: 9 No space after literal string 'DOCTYPE'. Line: 1 Col: 23 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <form> #data <!DOCTYPE html><p></P>X #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | "X" #data &AMP #errors Line: 1 Col: 4 Named entity didn't end with ';'. Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "&" #data &AMp; #errors Line: 1 Col: 1 Named entity expected. Got none. Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "&AMp;" #data <!DOCTYPE html><html><head></head><body><thisISasillyTESTelementNameToMakeSureCrazyTagNamesArePARSEDcorrectLY> #errors Line: 1 Col: 110 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | <thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly> #data <!DOCTYPE html>X</body>X #errors Line: 1 Col: 24 Unexpected non-space characters in the after body phase. #document | <!DOCTYPE html> | <html> | <head> | <body> | "XX" #data <!DOCTYPE html><!-- X #errors Line: 1 Col: 21 Unexpected end of file in comment. #document | <!DOCTYPE html> | <!-- X --> | <html> | <head> | <body> #data <!DOCTYPE html><table><caption>test TEST</caption><td>test #errors Line: 1 Col: 54 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 58 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | "test TEST" | <tbody> | <tr> | <td> | "test" #data <!DOCTYPE html><select><option><optgroup> #errors Line: 1 Col: 41 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <option> | <optgroup> #data <!DOCTYPE html><select><optgroup><option></optgroup><option><select><option> #errors Line: 1 Col: 68 Unexpected select start tag in the select phase treated as select end tag. Line: 1 Col: 76 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <optgroup> | <option> | <option> | <option> #data <!DOCTYPE html><select><optgroup><option><optgroup> #errors Line: 1 Col: 51 Expected closing tag. Unexpected end of file. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <optgroup> | <option> | <optgroup> #data <!DOCTYPE html><datalist><option>foo</datalist>bar #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <datalist> | <option> | "foo" | "bar" #data <!DOCTYPE html><font><input><input></font> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <font> | <input> | <input> #data <!DOCTYPE html><!-- XXX - XXX --> #errors #document | <!DOCTYPE html> | <!-- XXX - XXX --> | <html> | <head> | <body> #data <!DOCTYPE html><!-- XXX - XXX #errors Line: 1 Col: 29 Unexpected end of file in comment (-) #document | <!DOCTYPE html> | <!-- XXX - XXX --> | <html> | <head> | <body> #data <!DOCTYPE html><!-- XXX - XXX - XXX --> #errors #document | <!DOCTYPE html> | <!-- XXX - XXX - XXX --> | <html> | <head> | <body> #data <isindex test=x name=x> #errors Line: 1 Col: 23 Unexpected start tag (isindex). Expected DOCTYPE. Line: 1 Col: 23 Unexpected start tag isindex. Don't use it! #document | <html> | <head> | <body> | <form> | <hr> | <label> | "This is a searchable index. Enter search keywords: " | <input> | name="isindex" | test="x" | <hr> #data test test #errors Line: 2 Col: 4 Unexpected non-space characters. Expected DOCTYPE. #document | <html> | <head> | <body> | "test test" #data <!DOCTYPE html><body><title>test</body></title> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <title> | "test</body>" #data <!DOCTYPE html><body><title>X</title><meta name=z><link rel=foo><style> x { content:"</style" } </style> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <title> | "X" | <meta> | name="z" | <link> | rel="foo" | <style> | " x { content:"</style" } " #data <!DOCTYPE html><select><optgroup></optgroup></select> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <optgroup> #data #errors Line: 2 Col: 1 Unexpected End of file. Expected DOCTYPE. #document | <html> | <head> | <body> #data <!DOCTYPE html> <html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> #data <!DOCTYPE html><script> </script> <title>x</title> </head> #errors #document | <!DOCTYPE html> | <html> | <head> | <script> | " " | " " | <title> | "x" | " " | <body> #data <!DOCTYPE html><html><body><html id=x> #errors Line: 1 Col: 38 html needs to be the first start tag. #document | <!DOCTYPE html> | <html> | id="x" | <head> | <body> #data <!DOCTYPE html>X</body><html id="x"> #errors Line: 1 Col: 36 Unexpected start tag token (html) in the after body phase. Line: 1 Col: 36 html needs to be the first start tag. #document | <!DOCTYPE html> | <html> | id="x" | <head> | <body> | "X" #data <!DOCTYPE html><head><html id=x> #errors Line: 1 Col: 32 html needs to be the first start tag. #document | <!DOCTYPE html> | <html> | id="x" | <head> | <body> #data <!DOCTYPE html>X</html>X #errors Line: 1 Col: 24 Unexpected non-space characters in the after body phase. #document | <!DOCTYPE html> | <html> | <head> | <body> | "XX" #data <!DOCTYPE html>X</html> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "X " #data <!DOCTYPE html>X</html><p>X #errors Line: 1 Col: 26 Unexpected start tag (p). #document | <!DOCTYPE html> | <html> | <head> | <body> | "X" | <p> | "X" #data <!DOCTYPE html>X<p/x/y/z> #errors Line: 1 Col: 19 Expected a > after the /. Line: 1 Col: 21 Solidus (/) incorrectly placed in tag. Line: 1 Col: 23 Solidus (/) incorrectly placed in tag. #document | <!DOCTYPE html> | <html> | <head> | <body> | "X" | <p> | x="" | y="" | z="" #data <!DOCTYPE html><!--x-- #errors Line: 1 Col: 22 Unexpected end of file in comment (--). #document | <!DOCTYPE html> | <!-- x --> | <html> | <head> | <body> #data <!DOCTYPE html><table><tr><td></p></table> #errors Line: 1 Col: 34 Unexpected end tag (p). Ignored. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <p> #data <!DOCTYPE <!DOCTYPE HTML>><!--<!--x-->--> #errors Line: 1 Col: 20 Expected space or '>'. Got '' Line: 1 Col: 25 Erroneous DOCTYPE. Line: 1 Col: 35 Unexpected character in comment found. #document | <!DOCTYPE <!doctype> | <html> | <head> | <body> | ">" | <!-- <!--x --> | "-->" #data <!doctype html><div><form></form><div></div></div> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <div> | <form> | <div> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/plain-text-unsafe.dat������������0000644�0000153�0000161�00000010106�12321735652�030730� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data FOO&#x000D;ZOO #errors #document | <html> | <head> | <body> | "FOO ZOO" #data <html>�<frameset></frameset> #errors #document | <html> | <head> | <frameset> #data <html> � <frameset></frameset> #errors #document | <html> | <head> | <frameset> #data <html>a�a<frameset></frameset> #errors #document | <html> | <head> | <body> | "aa" #data <html>��<frameset></frameset> #errors #document | <html> | <head> | <frameset> #data <html>� <frameset></frameset> #errors #document | <html> | <head> | <frameset> #data <html><select>� #errors #document | <html> | <head> | <body> | <select> #data � #errors #document | <html> | <head> | <body> #data <body>� #errors #document | <html> | <head> | <body> #data <plaintext>�filler�text� #errors #document | <html> | <head> | <body> | <plaintext> | "�filler�text�" #data <svg><![CDATA[�filler�text�]]> #errors #document | <html> | <head> | <body> | <svg svg> | "�filler�text�" #data <body><!�> #errors #document | <html> | <head> | <body> | <!-- � --> #data <body><!�filler�text> #errors #document | <html> | <head> | <body> | <!-- �filler�text --> #data <body><svg><foreignObject>�filler�text #errors #document | <html> | <head> | <body> | <svg svg> | <svg foreignObject> | "fillertext" #data <svg>�filler�text #errors #document | <html> | <head> | <body> | <svg svg> | "�filler�text" #data <svg>�<frameset> #errors #document | <html> | <head> | <body> | <svg svg> | "�" | <svg frameset> #data <svg>� <frameset> #errors #document | <html> | <head> | <body> | <svg svg> | "� " | <svg frameset> #data <svg>�a<frameset> #errors #document | <html> | <head> | <body> | <svg svg> | "�a" | <svg frameset> #data <svg>�</svg><frameset> #errors #document | <html> | <head> | <frameset> #data <svg>� </svg><frameset> #errors #document | <html> | <head> | <frameset> #data <svg>�a</svg><frameset> #errors #document | <html> | <head> | <body> | <svg svg> | "�a" #data <svg><path></path></svg><frameset> #errors #document | <html> | <head> | <frameset> #data <svg><p><frameset> #errors #document | <html> | <head> | <frameset> #data <!DOCTYPE html><pre> A</pre> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> | " A" #data <!DOCTYPE html><pre> A</pre> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> | " A" #data <!DOCTYPE html><pre> A</pre> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <pre> | "A" #data <!DOCTYPE html><table><tr><td><math><mtext>�a #errors 44: Saw U+0000 in stream. 45: End of file in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <math math> | <math mtext> | "a" #data <!DOCTYPE html><table><tr><td><svg><foreignObject>�a #errors 44: Saw U+0000 in stream. 45: End of file in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <svg svg> | <svg foreignObject> | "a" #data <!DOCTYPE html><math><mi>a�b #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mi> | "ab" #data <!DOCTYPE html><math><mo>a�b #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mo> | "ab" #data <!DOCTYPE html><math><mn>a�b #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mn> | "ab" #data <!DOCTYPE html><math><ms>a�b #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math ms> | "ab" #data <!DOCTYPE html><math><mtext>a�b #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <math math> | <math mtext> | "ab" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests24.dat����������������������0000644�0000153�0000161�00000001641�12321735652�026700� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html>&NotEqualTilde; #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "≂̸" #data <!DOCTYPE html>&NotEqualTilde;A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "≂̸A" #data <!DOCTYPE html>&ThickSpace; #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "âŸâ€Š" #data <!DOCTYPE html>&ThickSpace;A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "âŸâ€ŠA" #data <!DOCTYPE html>&NotSubset; #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "⊂⃒" #data <!DOCTYPE html>&NotSubset;A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "⊂⃒A" #data <!DOCTYPE html>&Gopf; #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "ð”¾" #data <!DOCTYPE html>&Gopf;A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "ð”¾A" �����������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tricky01.dat���������������������0000644�0000153�0000161�00000007657�12321735652�027053� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <b><p>Bold </b> Not bold</p> Also not bold. #errors #document | <html> | <head> | <body> | <b> | <p> | <b> | "Bold " | " Not bold" | " Also not bold." #data <html> <font color=red><i>Italic and Red<p>Italic and Red </font> Just italic.</p> Italic only.</i> Plain <p>I should not be red. <font color=red>Red. <i>Italic and red.</p> <p>Italic and red. </i> Red.</font> I should not be red.</p> <b>Bold <i>Bold and italic</b> Only Italic </i> Plain #errors #document | <html> | <head> | <body> | <font> | color="red" | <i> | "Italic and Red" | <i> | <p> | <font> | color="red" | "Italic and Red " | " Just italic." | " Italic only." | " Plain " | <p> | "I should not be red. " | <font> | color="red" | "Red. " | <i> | "Italic and red." | <font> | color="red" | <i> | " " | <p> | <font> | color="red" | <i> | "Italic and red. " | " Red." | " I should not be red." | " " | <b> | "Bold " | <i> | "Bold and italic" | <i> | " Only Italic " | " Plain" #data <html><body> <p><font size="7">First paragraph.</p> <p>Second paragraph.</p></font> <b><p><i>Bold and Italic</b> Italic</p> #errors #document | <html> | <head> | <body> | " " | <p> | <font> | size="7" | "First paragraph." | <font> | size="7" | " " | <p> | "Second paragraph." | " " | <b> | <p> | <b> | <i> | "Bold and Italic" | <i> | " Italic" #data <html> <dl> <dt><b>Boo <dd>Goo? </dl> </html> #errors #document | <html> | <head> | <body> | <dl> | " " | <dt> | <b> | "Boo " | <dd> | <b> | "Goo? " | <b> | " " #data <html><body> <label><a><div>Hello<div>World</div></a></label> </body></html> #errors #document | <html> | <head> | <body> | " " | <label> | <a> | <div> | <a> | "Hello" | <div> | "World" | " " #data <table><center> <font>a</center> <img> <tr><td> </td> </tr> </table> #errors #document | <html> | <head> | <body> | <center> | " " | <font> | "a" | <font> | <img> | " " | <table> | " " | <tbody> | <tr> | <td> | " " | " " | " " #data <table><tr><p><a><p>You should see this text. #errors #document | <html> | <head> | <body> | <p> | <a> | <p> | <a> | "You should see this text." | <table> | <tbody> | <tr> #data <TABLE> <TR> <CENTER><CENTER><TD></TD></TR><TR> <FONT> <TABLE><tr></tr></TABLE> </P> <a></font><font></a> This page contains an insanely badly-nested tag sequence. #errors #document | <html> | <head> | <body> | <center> | <center> | <font> | " " | <table> | " " | <tbody> | <tr> | " " | <td> | <tr> | " " | <table> | <tbody> | <tr> | <font> | " " | <p> | " " | <a> | <a> | <font> | <font> | " This page contains an insanely badly-nested tag sequence." #data <html> <body> <b><nobr><div>This text is in a div inside a nobr</nobr>More text that should not be in the nobr, i.e., the nobr should have closed the div inside it implicitly. </b><pre>A pre tag outside everything else.</pre> </body> </html> #errors #document | <html> | <head> | <body> | " " | <b> | <nobr> | <div> | <b> | <nobr> | "This text is in a div inside a nobr" | "More text that should not be in the nobr, i.e., the nobr should have closed the div inside it implicitly. " | <pre> | "A pre tag outside everything else." | " " ���������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests6.dat�����������������������0000644�0000153�0000161�00000035540�12321735652�026625� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!doctype html></head> <head> #errors Line: 1 Col: 29 Unexpected start tag head. Ignored. #document | <!DOCTYPE html> | <html> | <head> | " " | <body> #data <!doctype html><form><div></form><div> #errors 33: End tag "form" seen but there were unclosed elements. 38: End of file seen and there were open elements. #document | <!DOCTYPE html> | <html> | <head> | <body> | <form> | <div> | <div> #data <!doctype html><title>&amp;</title> #errors #document | <!DOCTYPE html> | <html> | <head> | <title> | "&" | <body> #data <!doctype html><title><!--&amp;--></title> #errors #document | <!DOCTYPE html> | <html> | <head> | <title> | "<!--&-->" | <body> #data <!doctype> #errors Line: 1 Col: 9 No space after literal string 'DOCTYPE'. Line: 1 Col: 10 Unexpected > character. Expected DOCTYPE name. Line: 1 Col: 10 Erroneous DOCTYPE. #document | <!DOCTYPE > | <html> | <head> | <body> #data <!---x #errors Line: 1 Col: 6 Unexpected end of file in comment. Line: 1 Col: 6 Unexpected End of file. Expected DOCTYPE. #document | <!-- -x --> | <html> | <head> | <body> #data <body> <div> #errors Line: 1 Col: 6 Unexpected start tag (body). Line: 2 Col: 5 Expected closing tag. Unexpected end of file. #document-fragment div #document | " " | <div> #data <frameset></frameset> foo #errors Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. Line: 2 Col: 3 Unexpected non-space characters in the after frameset phase. Ignored. #document | <html> | <head> | <frameset> | " " #data <frameset></frameset> <noframes> #errors Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. Line: 2 Col: 10 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <frameset> | " " | <noframes> #data <frameset></frameset> <div> #errors Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. Line: 2 Col: 5 Unexpected start tag (div) in the after frameset phase. Ignored. #document | <html> | <head> | <frameset> | " " #data <frameset></frameset> </html> #errors Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. #document | <html> | <head> | <frameset> | " " #data <frameset></frameset> </div> #errors Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. Line: 2 Col: 6 Unexpected end tag (div) in the after frameset phase. Ignored. #document | <html> | <head> | <frameset> | " " #data <form><form> #errors Line: 1 Col: 6 Unexpected start tag (form). Expected DOCTYPE. Line: 1 Col: 12 Unexpected start tag (form). Line: 1 Col: 12 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <form> #data <button><button> #errors Line: 1 Col: 8 Unexpected start tag (button). Expected DOCTYPE. Line: 1 Col: 16 Unexpected start tag (button) implies end tag (button). Line: 1 Col: 16 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <button> | <button> #data <table><tr><td></th> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 20 Unexpected end tag (th). Ignored. Line: 1 Col: 20 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> #data <table><caption><td> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 20 Unexpected end tag (td). Ignored. Line: 1 Col: 20 Unexpected table cell start tag (td) in the table body phase. Line: 1 Col: 20 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <table> | <caption> | <tbody> | <tr> | <td> #data <table><caption><div> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 21 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <table> | <caption> | <div> #data </caption><div> #errors Line: 1 Col: 10 Unexpected end tag (caption). Ignored. Line: 1 Col: 15 Expected closing tag. Unexpected end of file. #document-fragment caption #document | <div> #data <table><caption><div></caption> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 31 Unexpected end tag (caption). Missing end tag (div). Line: 1 Col: 31 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | <table> | <caption> | <div> #data <table><caption></table> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 24 Unexpected end table tag in caption. Generates implied end caption. #document | <html> | <head> | <body> | <table> | <caption> #data </table><div> #errors Line: 1 Col: 8 Unexpected end table tag in caption. Generates implied end caption. Line: 1 Col: 8 Unexpected end tag (caption). Ignored. Line: 1 Col: 13 Expected closing tag. Unexpected end of file. #document-fragment caption #document | <div> #data <table><caption></body></col></colgroup></html></tbody></td></tfoot></th></thead></tr> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 23 Unexpected end tag (body). Ignored. Line: 1 Col: 29 Unexpected end tag (col). Ignored. Line: 1 Col: 40 Unexpected end tag (colgroup). Ignored. Line: 1 Col: 47 Unexpected end tag (html). Ignored. Line: 1 Col: 55 Unexpected end tag (tbody). Ignored. Line: 1 Col: 60 Unexpected end tag (td). Ignored. Line: 1 Col: 68 Unexpected end tag (tfoot). Ignored. Line: 1 Col: 73 Unexpected end tag (th). Ignored. Line: 1 Col: 81 Unexpected end tag (thead). Ignored. Line: 1 Col: 86 Unexpected end tag (tr). Ignored. Line: 1 Col: 86 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <table> | <caption> #data <table><caption><div></div> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 27 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <table> | <caption> | <div> #data <table><tr><td></body></caption></col></colgroup></html> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 22 Unexpected end tag (body). Ignored. Line: 1 Col: 32 Unexpected end tag (caption). Ignored. Line: 1 Col: 38 Unexpected end tag (col). Ignored. Line: 1 Col: 49 Unexpected end tag (colgroup). Ignored. Line: 1 Col: 56 Unexpected end tag (html). Ignored. Line: 1 Col: 56 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> #data </table></tbody></tfoot></thead></tr><div> #errors Line: 1 Col: 8 Unexpected end tag (table). Ignored. Line: 1 Col: 16 Unexpected end tag (tbody). Ignored. Line: 1 Col: 24 Unexpected end tag (tfoot). Ignored. Line: 1 Col: 32 Unexpected end tag (thead). Ignored. Line: 1 Col: 37 Unexpected end tag (tr). Ignored. Line: 1 Col: 42 Expected closing tag. Unexpected end of file. #document-fragment td #document | <div> #data <table><colgroup>foo #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 20 Unexpected non-space characters in table context caused voodoo mode. Line: 1 Col: 20 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | "foo" | <table> | <colgroup> #data foo<col> #errors Line: 1 Col: 3 Unexpected end tag (colgroup). Ignored. #document-fragment colgroup #document | <col> #data <table><colgroup></col> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 23 This element (col) has no end tag. Line: 1 Col: 23 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <table> | <colgroup> #data <frameset><div> #errors Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. Line: 1 Col: 15 Unexpected start tag token (div) in the frameset phase. Ignored. Line: 1 Col: 15 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <frameset> #data </frameset><frame> #errors Line: 1 Col: 11 Unexpected end tag token (frameset) in the frameset phase (innerHTML). #document-fragment frameset #document | <frame> #data <frameset></div> #errors Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. Line: 1 Col: 16 Unexpected end tag token (div) in the frameset phase. Ignored. Line: 1 Col: 16 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <frameset> #data </body><div> #errors Line: 1 Col: 7 Unexpected end tag (body). Ignored. Line: 1 Col: 12 Expected closing tag. Unexpected end of file. #document-fragment body #document | <div> #data <table><tr><div> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 16 Unexpected start tag (div) in table context caused voodoo mode. Line: 1 Col: 16 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | <div> | <table> | <tbody> | <tr> #data </tr><td> #errors Line: 1 Col: 5 Unexpected end tag (tr). Ignored. #document-fragment tr #document | <td> #data </tbody></tfoot></thead><td> #errors Line: 1 Col: 8 Unexpected end tag (tbody). Ignored. Line: 1 Col: 16 Unexpected end tag (tfoot). Ignored. Line: 1 Col: 24 Unexpected end tag (thead). Ignored. #document-fragment tr #document | <td> #data <table><tr><div><td> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 16 Unexpected start tag (div) in table context caused voodoo mode. Line: 1 Col: 20 Unexpected implied end tag (div) in the table row phase. Line: 1 Col: 20 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | <table> | <tbody> | <tr> | <td> #data <caption><col><colgroup><tbody><tfoot><thead><tr> #errors Line: 1 Col: 9 Unexpected start tag (caption). Line: 1 Col: 14 Unexpected start tag (col). Line: 1 Col: 24 Unexpected start tag (colgroup). Line: 1 Col: 31 Unexpected start tag (tbody). Line: 1 Col: 38 Unexpected start tag (tfoot). Line: 1 Col: 45 Unexpected start tag (thead). Line: 1 Col: 49 Unexpected end of file. Expected table content. #document-fragment tbody #document | <tr> #data <table><tbody></thead> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 22 Unexpected end tag (thead) in the table body phase. Ignored. Line: 1 Col: 22 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | <table> | <tbody> #data </table><tr> #errors Line: 1 Col: 8 Unexpected end tag (table). Ignored. Line: 1 Col: 12 Unexpected end of file. Expected table content. #document-fragment tbody #document | <tr> #data <table><tbody></body></caption></col></colgroup></html></td></th></tr> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 21 Unexpected end tag (body) in the table body phase. Ignored. Line: 1 Col: 31 Unexpected end tag (caption) in the table body phase. Ignored. Line: 1 Col: 37 Unexpected end tag (col) in the table body phase. Ignored. Line: 1 Col: 48 Unexpected end tag (colgroup) in the table body phase. Ignored. Line: 1 Col: 55 Unexpected end tag (html) in the table body phase. Ignored. Line: 1 Col: 60 Unexpected end tag (td) in the table body phase. Ignored. Line: 1 Col: 65 Unexpected end tag (th) in the table body phase. Ignored. Line: 1 Col: 70 Unexpected end tag (tr) in the table body phase. Ignored. Line: 1 Col: 70 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | <table> | <tbody> #data <table><tbody></div> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 20 Unexpected end tag (div) in table context caused voodoo mode. Line: 1 Col: 20 End tag (div) seen too early. Expected other end tag. Line: 1 Col: 20 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | <table> | <tbody> #data <table><table> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 14 Unexpected start tag (table) implies end tag (table). Line: 1 Col: 14 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | <table> | <table> #data <table></body></caption></col></colgroup></html></tbody></td></tfoot></th></thead></tr> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. Line: 1 Col: 14 Unexpected end tag (body). Ignored. Line: 1 Col: 24 Unexpected end tag (caption). Ignored. Line: 1 Col: 30 Unexpected end tag (col). Ignored. Line: 1 Col: 41 Unexpected end tag (colgroup). Ignored. Line: 1 Col: 48 Unexpected end tag (html). Ignored. Line: 1 Col: 56 Unexpected end tag (tbody). Ignored. Line: 1 Col: 61 Unexpected end tag (td). Ignored. Line: 1 Col: 69 Unexpected end tag (tfoot). Ignored. Line: 1 Col: 74 Unexpected end tag (th). Ignored. Line: 1 Col: 82 Unexpected end tag (thead). Ignored. Line: 1 Col: 87 Unexpected end tag (tr). Ignored. Line: 1 Col: 87 Unexpected end of file. Expected table content. #document | <html> | <head> | <body> | <table> #data </table><tr> #errors Line: 1 Col: 8 Unexpected end tag (table). Ignored. Line: 1 Col: 12 Unexpected end of file. Expected table content. #document-fragment table #document | <tbody> | <tr> #data <body></body></html> #errors Line: 1 Col: 20 Unexpected html end tag in inner html mode. Line: 1 Col: 20 Unexpected EOF in inner html mode. #document-fragment html #document | <head> | <body> #data <html><frameset></frameset></html> #errors Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. #document | <html> | <head> | <frameset> | " " #data <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"><html></html> #errors Line: 1 Col: 50 Erroneous DOCTYPE. Line: 1 Col: 63 Unexpected end tag (html) after the (implied) root element. #document | <!DOCTYPE html "-//W3C//DTD HTML 4.01//EN" ""> | <html> | <head> | <body> #data <param><frameset></frameset> #errors Line: 1 Col: 7 Unexpected start tag (param). Expected DOCTYPE. Line: 1 Col: 17 Unexpected start tag (frameset). #document | <html> | <head> | <frameset> #data <source><frameset></frameset> #errors Line: 1 Col: 7 Unexpected start tag (source). Expected DOCTYPE. Line: 1 Col: 17 Unexpected start tag (frameset). #document | <html> | <head> | <frameset> #data <track><frameset></frameset> #errors Line: 1 Col: 7 Unexpected start tag (track). Expected DOCTYPE. Line: 1 Col: 17 Unexpected start tag (frameset). #document | <html> | <head> | <frameset> #data </html><frameset></frameset> #errors 7: End tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. 17: Stray “frameset†start tag. 17: “frameset†start tag seen. #document | <html> | <head> | <frameset> #data </body><frameset></frameset> #errors 7: End tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. 17: Stray “frameset†start tag. 17: “frameset†start tag seen. #document | <html> | <head> | <frameset> ����������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests10.dat����������������������0000644�0000153�0000161�00000033335�12321735652�026700� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html><svg></svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> #data <!DOCTYPE html><svg></svg><![CDATA[a]]> #errors 29: Bogus comment #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <!-- [CDATA[a]] --> #data <!DOCTYPE html><body><svg></svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> #data <!DOCTYPE html><body><select><svg></svg></select> #errors 35: Stray “svg†start tag. 42: Stray end tag “svg†#document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!DOCTYPE html><body><select><option><svg></svg></option></select> #errors 43: Stray “svg†start tag. 50: Stray end tag “svg†#document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <option> #data <!DOCTYPE html><body><table><svg></svg></table> #errors 34: Start tag “svg†seen in “tableâ€. 41: Stray end tag “svgâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <table> #data <!DOCTYPE html><body><table><svg><g>foo</g></svg></table> #errors 34: Start tag “svg†seen in “tableâ€. 46: Stray end tag “gâ€. 53: Stray end tag “svgâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg g> | "foo" | <table> #data <!DOCTYPE html><body><table><svg><g>foo</g><g>bar</g></svg></table> #errors 34: Start tag “svg†seen in “tableâ€. 46: Stray end tag “gâ€. 58: Stray end tag “gâ€. 65: Stray end tag “svgâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" | <table> #data <!DOCTYPE html><body><table><tbody><svg><g>foo</g><g>bar</g></svg></tbody></table> #errors 41: Start tag “svg†seen in “tableâ€. 53: Stray end tag “gâ€. 65: Stray end tag “gâ€. 72: Stray end tag “svgâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" | <table> | <tbody> #data <!DOCTYPE html><body><table><tbody><tr><svg><g>foo</g><g>bar</g></svg></tr></tbody></table> #errors 45: Start tag “svg†seen in “tableâ€. 57: Stray end tag “gâ€. 69: Stray end tag “gâ€. 76: Stray end tag “svgâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" | <table> | <tbody> | <tr> #data <!DOCTYPE html><body><table><tbody><tr><td><svg><g>foo</g><g>bar</g></svg></td></tr></tbody></table> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" #data <!DOCTYPE html><body><table><tbody><tr><td><svg><g>foo</g><g>bar</g></svg><p>baz</td></tr></tbody></table> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" | <p> | "baz" #data <!DOCTYPE html><body><table><caption><svg><g>foo</g><g>bar</g></svg><p>baz</caption></table> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" | <p> | "baz" #data <!DOCTYPE html><body><table><caption><svg><g>foo</g><g>bar</g><p>baz</table><p>quux #errors 70: HTML start tag “p†in a foreign namespace context. 81: “table†closed but “caption†was still open. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" | <p> | "baz" | <p> | "quux" #data <!DOCTYPE html><body><table><caption><svg><g>foo</g><g>bar</g>baz</table><p>quux #errors 78: “table†closed but “caption†was still open. 78: Unclosed elements on stack. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" | "baz" | <p> | "quux" #data <!DOCTYPE html><body><table><colgroup><svg><g>foo</g><g>bar</g><p>baz</table><p>quux #errors 44: Start tag “svg†seen in “tableâ€. 56: Stray end tag “gâ€. 68: Stray end tag “gâ€. 71: HTML start tag “p†in a foreign namespace context. 71: Start tag “p†seen in “tableâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" | <p> | "baz" | <table> | <colgroup> | <p> | "quux" #data <!DOCTYPE html><body><table><tr><td><select><svg><g>foo</g><g>bar</g><p>baz</table><p>quux #errors 50: Stray “svg†start tag. 54: Stray “g†start tag. 62: Stray end tag “g†66: Stray “g†start tag. 74: Stray end tag “g†77: Stray “p†start tag. 88: “table†end tag with “select†open. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <select> | "foobarbaz" | <p> | "quux" #data <!DOCTYPE html><body><table><select><svg><g>foo</g><g>bar</g><p>baz</table><p>quux #errors 36: Start tag “select†seen in “tableâ€. 42: Stray “svg†start tag. 46: Stray “g†start tag. 54: Stray end tag “g†58: Stray “g†start tag. 66: Stray end tag “g†69: Stray “p†start tag. 80: “table†end tag with “select†open. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | "foobarbaz" | <table> | <p> | "quux" #data <!DOCTYPE html><body></body></html><svg><g>foo</g><g>bar</g><p>baz #errors 41: Stray “svg†start tag. 68: HTML start tag “p†in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" | <p> | "baz" #data <!DOCTYPE html><body></body><svg><g>foo</g><g>bar</g><p>baz #errors 34: Stray “svg†start tag. 61: HTML start tag “p†in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg g> | "foo" | <svg g> | "bar" | <p> | "baz" #data <!DOCTYPE html><frameset><svg><g></g><g></g><p><span> #errors 31: Stray “svg†start tag. 35: Stray “g†start tag. 40: Stray end tag “g†44: Stray “g†start tag. 49: Stray end tag “g†52: Stray “p†start tag. 58: Stray “span†start tag. 58: End of file seen and there were open elements. #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!DOCTYPE html><frameset></frameset><svg><g></g><g></g><p><span> #errors 42: Stray “svg†start tag. 46: Stray “g†start tag. 51: Stray end tag “g†55: Stray “g†start tag. 60: Stray end tag “g†63: Stray “p†start tag. 69: Stray “span†start tag. #document | <!DOCTYPE html> | <html> | <head> | <frameset> #data <!DOCTYPE html><body xlink:href=foo><svg xlink:href=foo></svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | xlink:href="foo" | <svg svg> | xlink href="foo" #data <!DOCTYPE html><body xlink:href=foo xml:lang=en><svg><g xml:lang=en xlink:href=foo></g></svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | xlink:href="foo" | xml:lang="en" | <svg svg> | <svg g> | xlink href="foo" | xml lang="en" #data <!DOCTYPE html><body xlink:href=foo xml:lang=en><svg><g xml:lang=en xlink:href=foo /></svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | xlink:href="foo" | xml:lang="en" | <svg svg> | <svg g> | xlink href="foo" | xml lang="en" #data <!DOCTYPE html><body xlink:href=foo xml:lang=en><svg><g xml:lang=en xlink:href=foo />bar</svg> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | xlink:href="foo" | xml:lang="en" | <svg svg> | <svg g> | xlink href="foo" | xml lang="en" | "bar" #data <svg></path> #errors #document | <html> | <head> | <body> | <svg svg> #data <div><svg></div>a #errors #document | <html> | <head> | <body> | <div> | <svg svg> | "a" #data <div><svg><path></div>a #errors #document | <html> | <head> | <body> | <div> | <svg svg> | <svg path> | "a" #data <div><svg><path></svg><path> #errors #document | <html> | <head> | <body> | <div> | <svg svg> | <svg path> | <path> #data <div><svg><path><foreignObject><math></div>a #errors #document | <html> | <head> | <body> | <div> | <svg svg> | <svg path> | <svg foreignObject> | <math math> | "a" #data <div><svg><path><foreignObject><p></div>a #errors #document | <html> | <head> | <body> | <div> | <svg svg> | <svg path> | <svg foreignObject> | <p> | "a" #data <!DOCTYPE html><svg><desc><div><svg><ul>a #errors 40: HTML start tag “ul†in a foreign namespace context. 41: End of file in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg desc> | <div> | <svg svg> | <ul> | "a" #data <!DOCTYPE html><svg><desc><svg><ul>a #errors 35: HTML start tag “ul†in a foreign namespace context. 36: End of file in a foreign namespace context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <svg svg> | <svg desc> | <svg svg> | <ul> | "a" #data <!DOCTYPE html><p><svg><desc><p> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <svg svg> | <svg desc> | <p> #data <!DOCTYPE html><p><svg><title><p> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <svg svg> | <svg title> | <p> #data <div><svg><path><foreignObject><p></foreignObject><p> #errors #document | <html> | <head> | <body> | <div> | <svg svg> | <svg path> | <svg foreignObject> | <p> | <p> #data <math><mi><div><object><div><span></span></div></object></div></mi><mi> #errors #document | <html> | <head> | <body> | <math math> | <math mi> | <div> | <object> | <div> | <span> | <math mi> #data <math><mi><svg><foreignObject><div><div></div></div></foreignObject></svg></mi><mi> #errors #document | <html> | <head> | <body> | <math math> | <math mi> | <svg svg> | <svg foreignObject> | <div> | <div> | <math mi> #data <svg><script></script><path> #errors #document | <html> | <head> | <body> | <svg svg> | <svg script> | <svg path> #data <table><svg></svg><tr> #errors #document | <html> | <head> | <body> | <svg svg> | <table> | <tbody> | <tr> #data <math><mi><mglyph> #errors #document | <html> | <head> | <body> | <math math> | <math mi> | <math mglyph> #data <math><mi><malignmark> #errors #document | <html> | <head> | <body> | <math math> | <math mi> | <math malignmark> #data <math><mo><mglyph> #errors #document | <html> | <head> | <body> | <math math> | <math mo> | <math mglyph> #data <math><mo><malignmark> #errors #document | <html> | <head> | <body> | <math math> | <math mo> | <math malignmark> #data <math><mn><mglyph> #errors #document | <html> | <head> | <body> | <math math> | <math mn> | <math mglyph> #data <math><mn><malignmark> #errors #document | <html> | <head> | <body> | <math math> | <math mn> | <math malignmark> #data <math><ms><mglyph> #errors #document | <html> | <head> | <body> | <math math> | <math ms> | <math mglyph> #data <math><ms><malignmark> #errors #document | <html> | <head> | <body> | <math math> | <math ms> | <math malignmark> #data <math><mtext><mglyph> #errors #document | <html> | <head> | <body> | <math math> | <math mtext> | <math mglyph> #data <math><mtext><malignmark> #errors #document | <html> | <head> | <body> | <math math> | <math mtext> | <math malignmark> #data <math><annotation-xml><svg></svg></annotation-xml><mi> #errors #document | <html> | <head> | <body> | <math math> | <math annotation-xml> | <svg svg> | <math mi> #data <math><annotation-xml><svg><foreignObject><div><math><mi></mi></math><span></span></div></foreignObject><path></path></svg></annotation-xml><mi> #errors #document | <html> | <head> | <body> | <math math> | <math annotation-xml> | <svg svg> | <svg foreignObject> | <div> | <math math> | <math mi> | <span> | <svg path> | <math mi> #data <math><annotation-xml><svg><foreignObject><math><mi><svg></svg></mi><mo></mo></math><span></span></foreignObject><path></path></svg></annotation-xml><mi> #errors #document | <html> | <head> | <body> | <math math> | <math annotation-xml> | <svg svg> | <svg foreignObject> | <math math> | <math mi> | <svg svg> | <math mo> | <span> | <svg path> | <math mi> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests25.dat����������������������0000644�0000153�0000161�00000005025�12321735652�026701� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html><body><foo>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <foo> | "A" #data <!DOCTYPE html><body><area>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <area> | "A" #data <!DOCTYPE html><body><base>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <base> | "A" #data <!DOCTYPE html><body><basefont>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <basefont> | "A" #data <!DOCTYPE html><body><bgsound>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <bgsound> | "A" #data <!DOCTYPE html><body><br>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <br> | "A" #data <!DOCTYPE html><body><col>A #errors 26: Stray start tag “colâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | "A" #data <!DOCTYPE html><body><command>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <command> | "A" #data <!DOCTYPE html><body><embed>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <embed> | "A" #data <!DOCTYPE html><body><frame>A #errors 26: Stray start tag “frameâ€. #document | <!DOCTYPE html> | <html> | <head> | <body> | "A" #data <!DOCTYPE html><body><hr>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <hr> | "A" #data <!DOCTYPE html><body><img>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <img> | "A" #data <!DOCTYPE html><body><input>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <input> | "A" #data <!DOCTYPE html><body><keygen>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <keygen> | "A" #data <!DOCTYPE html><body><link>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <link> | "A" #data <!DOCTYPE html><body><meta>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <meta> | "A" #data <!DOCTYPE html><body><param>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <param> | "A" #data <!DOCTYPE html><body><source>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <source> | "A" #data <!DOCTYPE html><body><track>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <track> | "A" #data <!DOCTYPE html><body><wbr>A #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <wbr> | "A" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests7.dat�����������������������0000644�0000153�0000161�00000014756�12321735652�026634� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!doctype html><body><title>X</title> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <title> | "X" #data <!doctype html><table><title>X</title></table> #errors Line: 1 Col: 29 Unexpected start tag (title) in table context caused voodoo mode. Line: 1 Col: 38 Unexpected end tag (title) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | <title> | "X" | <table> #data <!doctype html><head></head><title>X</title> #errors Line: 1 Col: 35 Unexpected start tag (title) that can be in head. Moved. #document | <!DOCTYPE html> | <html> | <head> | <title> | "X" | <body> #data <!doctype html></head><title>X</title> #errors Line: 1 Col: 29 Unexpected start tag (title) that can be in head. Moved. #document | <!DOCTYPE html> | <html> | <head> | <title> | "X" | <body> #data <!doctype html><table><meta></table> #errors Line: 1 Col: 28 Unexpected start tag (meta) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | <meta> | <table> #data <!doctype html><table>X<tr><td><table> <meta></table></table> #errors Line: 1 Col: 23 Unexpected non-space characters in table context caused voodoo mode. Line: 1 Col: 45 Unexpected start tag (meta) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | "X" | <table> | <tbody> | <tr> | <td> | <meta> | <table> | " " #data <!doctype html><html> <head> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> #data <!doctype html> <head> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> #data <!doctype html><table><style> <tr>x </style> </table> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <style> | " <tr>x " | " " #data <!doctype html><table><TBODY><script> <tr>x </script> </table> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <script> | " <tr>x " | " " #data <!doctype html><p><applet><p>X</p></applet> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <p> | <applet> | <p> | "X" #data <!doctype html><listing> X</listing> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <listing> | "X" #data <!doctype html><select><input>X #errors Line: 1 Col: 30 Unexpected input start tag in the select phase. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <input> | "X" #data <!doctype html><select><select>X #errors Line: 1 Col: 31 Unexpected select start tag in the select phase treated as select end tag. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | "X" #data <!doctype html><table><input type=hidDEN></table> #errors Line: 1 Col: 41 Unexpected input with type hidden in table context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <input> | type="hidDEN" #data <!doctype html><table>X<input type=hidDEN></table> #errors Line: 1 Col: 23 Unexpected non-space characters in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | "X" | <table> | <input> | type="hidDEN" #data <!doctype html><table> <input type=hidDEN></table> #errors Line: 1 Col: 43 Unexpected input with type hidden in table context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | " " | <input> | type="hidDEN" #data <!doctype html><table> <input type='hidDEN'></table> #errors Line: 1 Col: 45 Unexpected input with type hidden in table context. #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | " " | <input> | type="hidDEN" #data <!doctype html><table><input type=" hidden"><input type=hidDEN></table> #errors Line: 1 Col: 44 Unexpected start tag (input) in table context caused voodoo mode. #document | <!DOCTYPE html> | <html> | <head> | <body> | <input> | type=" hidden" | <table> | <input> | type="hidDEN" #data <!doctype html><table><select>X<tr> #errors Line: 1 Col: 30 Unexpected start tag (select) in table context caused voodoo mode. Line: 1 Col: 35 Unexpected table element start tag (trs) in the select in table phase. Line: 1 Col: 35 Unexpected end of file. Expected table content. #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | "X" | <table> | <tbody> | <tr> #data <!doctype html><select>X</select> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | "X" #data <!DOCTYPE hTmL><html></html> #errors Line: 1 Col: 28 Unexpected end tag (html) after the (implied) root element. #document | <!DOCTYPE html> | <html> | <head> | <body> #data <!DOCTYPE HTML><html></html> #errors Line: 1 Col: 28 Unexpected end tag (html) after the (implied) root element. #document | <!DOCTYPE html> | <html> | <head> | <body> #data <body>X</body></body> #errors Line: 1 Col: 21 Unexpected end tag token (body) in the after body phase. Line: 1 Col: 21 Unexpected EOF in inner html mode. #document-fragment html #document | <head> | <body> | "X" #data <div><p>a</x> b #errors Line: 1 Col: 5 Unexpected start tag (div). Expected DOCTYPE. Line: 1 Col: 13 Unexpected end tag (x). Ignored. Line: 1 Col: 15 Expected closing tag. Unexpected end of file. #document | <html> | <head> | <body> | <div> | <p> | "a b" #data <table><tr><td><code></code> </table> #errors Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. #document | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <code> | " " #data <table><b><tr><td>aaa</td></tr>bbb</table>ccc #errors XXX: Fix me #document | <html> | <head> | <body> | <b> | <b> | "bbb" | <table> | <tbody> | <tr> | <td> | "aaa" | <b> | "ccc" #data A<table><tr> B</tr> B</table> #errors XXX: Fix me #document | <html> | <head> | <body> | "A B B" | <table> | <tbody> | <tr> #data A<table><tr> B</tr> </em>C</table> #errors XXX: Fix me #document | <html> | <head> | <body> | "A BC" | <table> | <tbody> | <tr> | " " #data <select><keygen> #errors Not known #document | <html> | <head> | <body> | <select> | <keygen> ������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests17.dat����������������������0000644�0000153�0000161�00000003721�12321735652�026703� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!doctype html><table><tbody><select><tr> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <table> | <tbody> | <tr> #data <!doctype html><table><tr><select><td> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <table> | <tbody> | <tr> | <td> #data <!doctype html><table><tr><td><select><td> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <select> | <td> #data <!doctype html><table><tr><th><select><td> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <th> | <select> | <td> #data <!doctype html><table><caption><select><tr> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <select> | <tbody> | <tr> #data <!doctype html><select><tr> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><td> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><th> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><tbody> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><thead> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><tfoot> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><select><caption> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> #data <!doctype html><table><tr></table>a #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | "a" �����������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/inbody01.dat���������������������0000644�0000153�0000161�00000000665�12321735652�027022� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <button>1</foo> #errors #document | <html> | <head> | <body> | <button> | "1" #data <foo>1<p>2</foo> #errors #document | <html> | <head> | <body> | <foo> | "1" | <p> | "2" #data <dd>1</foo> #errors #document | <html> | <head> | <body> | <dd> | "1" #data <foo>1<dd>2</foo> #errors #document | <html> | <head> | <body> | <foo> | "1" | <dd> | "2" ���������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/entities02.dat�������������������0000644�0000153�0000161�00000005534�12321735652�027363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <div bar="ZZ&gt;YY"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ>YY" #data <div bar="ZZ&"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&" #data <div bar='ZZ&'></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&" #data <div bar=ZZ&></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&" #data <div bar="ZZ&gt=YY"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&gt=YY" #data <div bar="ZZ&gt0YY"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&gt0YY" #data <div bar="ZZ&gt9YY"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&gt9YY" #data <div bar="ZZ&gtaYY"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&gtaYY" #data <div bar="ZZ&gtZYY"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&gtZYY" #data <div bar="ZZ&gt YY"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ> YY" #data <div bar="ZZ&gt"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ>" #data <div bar='ZZ&gt'></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ>" #data <div bar=ZZ&gt></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ>" #data <div bar="ZZ&pound_id=23"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ£_id=23" #data <div bar="ZZ&prod_id=23"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&prod_id=23" #data <div bar="ZZ&pound;_id=23"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ£_id=23" #data <div bar="ZZ&prod;_id=23"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZâˆ_id=23" #data <div bar="ZZ&pound=23"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&pound=23" #data <div bar="ZZ&prod=23"></div> #errors #document | <html> | <head> | <body> | <div> | bar="ZZ&prod=23" #data <div>ZZ&pound_id=23</div> #errors #document | <html> | <head> | <body> | <div> | "ZZ£_id=23" #data <div>ZZ&prod_id=23</div> #errors #document | <html> | <head> | <body> | <div> | "ZZ&prod_id=23" #data <div>ZZ&pound;_id=23</div> #errors #document | <html> | <head> | <body> | <div> | "ZZ£_id=23" #data <div>ZZ&prod;_id=23</div> #errors #document | <html> | <head> | <body> | <div> | "ZZâˆ_id=23" #data <div>ZZ&pound=23</div> #errors #document | <html> | <head> | <body> | <div> | "ZZ£=23" #data <div>ZZ&prod=23</div> #errors #document | <html> | <head> | <body> | <div> | "ZZ&prod=23" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests18.dat����������������������0000644�0000153�0000161�00000010056�12321735652�026703� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!doctype html><plaintext></plaintext> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" #data <!doctype html><table><plaintext></plaintext> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" | <table> #data <!doctype html><table><tbody><plaintext></plaintext> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" | <table> | <tbody> #data <!doctype html><table><tbody><tr><plaintext></plaintext> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" | <table> | <tbody> | <tr> #data <!doctype html><table><tbody><tr><plaintext></plaintext> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <plaintext> | "</plaintext>" | <table> | <tbody> | <tr> #data <!doctype html><table><td><plaintext></plaintext> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <plaintext> | "</plaintext>" #data <!doctype html><table><caption><plaintext></plaintext> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <plaintext> | "</plaintext>" #data <!doctype html><table><tr><style></script></style>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "abc" | <table> | <tbody> | <tr> | <style> | "</script>" #data <!doctype html><table><tr><script></style></script>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | "abc" | <table> | <tbody> | <tr> | <script> | "</style>" #data <!doctype html><table><caption><style></script></style>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <caption> | <style> | "</script>" | "abc" #data <!doctype html><table><td><style></script></style>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <style> | "</script>" | "abc" #data <!doctype html><select><script></style></script>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <script> | "</style>" | "abc" #data <!doctype html><table><select><script></style></script>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <script> | "</style>" | "abc" | <table> #data <!doctype html><table><tr><select><script></style></script>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <select> | <script> | "</style>" | "abc" | <table> | <tbody> | <tr> #data <!doctype html><frameset></frameset><noframes>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <noframes> | "abc" #data <!doctype html><frameset></frameset><noframes>abc</noframes><!--abc--> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <noframes> | "abc" | <!-- abc --> #data <!doctype html><frameset></frameset></html><noframes>abc #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <noframes> | "abc" #data <!doctype html><frameset></frameset></html><noframes>abc</noframes><!--abc--> #errors #document | <!DOCTYPE html> | <html> | <head> | <frameset> | <noframes> | "abc" | <!-- abc --> #data <!doctype html><table><tr></tbody><tfoot> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <tfoot> #data <!doctype html><table><td><svg></svg>abc<td> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <table> | <tbody> | <tr> | <td> | <svg svg> | "abc" | <td> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000156�00000000000�011567� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/pending-spec-changes-plain-text-unsafe.dat�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/pending-spec-changes-plain-text-u0000644�0000153�0000161�00000000163�12321735652�033126� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <body><table>�filler�text� #errors #document | <html> | <head> | <body> | "fillertext" | <table> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/html/testdata/webkit/tests14.dat����������������������0000644�0000153�0000161�00000002045�12321735652�026676� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#data <!DOCTYPE html><html><body><xyz:abc></xyz:abc> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <xyz:abc> #data <!DOCTYPE html><html><body><xyz:abc></xyz:abc><span></span> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | <xyz:abc> | <span> #data <!DOCTYPE html><html><html abc:def=gh><xyz:abc></xyz:abc> #errors 15: Unexpected start tag html #document | <!DOCTYPE html> | <html> | abc:def="gh" | <head> | <body> | <xyz:abc> #data <!DOCTYPE html><html xml:lang=bar><html xml:lang=foo> #errors 15: Unexpected start tag html #document | <!DOCTYPE html> | <html> | xml:lang="bar" | <head> | <body> #data <!DOCTYPE html><html 123=456> #errors #document | <!DOCTYPE html> | <html> | 123="456" | <head> | <body> #data <!DOCTYPE html><html 123=456><html 789=012> #errors #document | <!DOCTYPE html> | <html> | 123="456" | 789="012" | <head> | <body> #data <!DOCTYPE html><html><body 789=012> #errors #document | <!DOCTYPE html> | <html> | <head> | <body> | 789="012" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/publicsuffix/�����������������������������������������0000755�0000153�0000161�00000000000�12321735652�023335� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/publicsuffix/gen.go�����������������������������������0000644�0000153�0000161�00000040340�12321735652�024436� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ignore package main // This program generates table.go and table_test.go. // Invoke as: // // go run gen.go -version "xxx" >table.go // go run gen.go -version "xxx" -test >table_test.go // // The version is derived from information found at // http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat // which is linked from http://publicsuffix.org/list/. // // To fetch a particular hg revision, such as 05b11a8d1ace, pass // -url "http://hg.mozilla.org/mozilla-central/raw-file/05b11a8d1ace/netwerk/dns/effective_tld_names.dat" import ( "bufio" "bytes" "flag" "fmt" "go/format" "io" "net/http" "os" "regexp" "sort" "strings" "code.google.com/p/go.net/idna" ) const ( nodesBitsChildren = 9 nodesBitsICANN = 1 nodesBitsTextOffset = 15 nodesBitsTextLength = 6 childrenBitsWildcard = 1 childrenBitsNodeType = 2 childrenBitsHi = 14 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 nodeTypeParentOnly = 2 numNodeType = 3 ) func nodeTypeStr(n int) string { switch n { case nodeTypeNormal: return "+" case nodeTypeException: return "!" case nodeTypeParentOnly: return "o" } panic("unreachable") } var ( labelEncoding = map[string]uint32{} labelsList = []string{} 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", "http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1", "URL of the publicsuffix.org list. If empty, stdin is read instead") v = flag.Bool("v", false, "verbose output (to stderr)") version = flag.String("version", "", "the effective_tld_names.dat version") test = flag.Bool("test", false, "generate table_test.go") ) func main() { if err := main1(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func main1() error { flag.Parse() if nodesBitsTextLength+nodesBitsTextOffset+nodesBitsICANN+nodesBitsChildren > 32 { return fmt.Errorf("not enough bits to encode the nodes table") } if childrenBitsLo+childrenBitsHi+childrenBitsNodeType+childrenBitsWildcard > 32 { return fmt.Errorf("not enough bits to encode the children table") } if *version == "" { return fmt.Errorf("-version was not specified") } var r io.Reader = os.Stdin if *url != "" { res, err := http.Get(*url) if err != nil { return err } if res.StatusCode != http.StatusOK { return fmt.Errorf("bad GET status for %s: %d", *url, res.Status) } r = res.Body defer res.Body.Close() } var root node icann := false buf := new(bytes.Buffer) br := bufio.NewReader(r) for { s, err := br.ReadString('\n') if err != nil { if err == io.EOF { break } return err } s = strings.TrimSpace(s) if strings.Contains(s, "BEGIN ICANN DOMAINS") { icann = true continue } if strings.Contains(s, "END ICANN DOMAINS") { icann = false continue } if s == "" || strings.HasPrefix(s, "//") { continue } s, err = idna.ToASCII(s) if err != nil { return err } if !validSuffix.MatchString(s) { return fmt.Errorf("bad publicsuffix.org list data: %q", s) } if *subset { switch { case s == "ac.jp" || strings.HasSuffix(s, ".ac.jp"): case s == "ak.us" || strings.HasSuffix(s, ".ak.us"): case s == "ao" || strings.HasSuffix(s, ".ao"): case s == "ar" || strings.HasSuffix(s, ".ar"): case s == "arpa" || strings.HasSuffix(s, ".arpa"): case s == "cy" || strings.HasSuffix(s, ".cy"): case s == "dyndns.org" || strings.HasSuffix(s, ".dyndns.org"): case s == "jp": case s == "kobe.jp" || strings.HasSuffix(s, ".kobe.jp"): case s == "kyoto.jp" || strings.HasSuffix(s, ".kyoto.jp"): case s == "om" || strings.HasSuffix(s, ".om"): case s == "uk" || strings.HasSuffix(s, ".uk"): case s == "uk.com" || strings.HasSuffix(s, ".uk.com"): case s == "tw" || strings.HasSuffix(s, ".tw"): case s == "zw" || strings.HasSuffix(s, ".zw"): case s == "xn--p1ai" || strings.HasSuffix(s, ".xn--p1ai"): // xn--p1ai is Russian-Cyrillic "рф". default: continue } } rules = append(rules, s) nt, wildcard := nodeTypeNormal, false switch { case strings.HasPrefix(s, "*."): s, nt = s[2:], nodeTypeParentOnly wildcard = true case strings.HasPrefix(s, "!"): s, nt = s[1:], nodeTypeException } labels := strings.Split(s, ".") for n, i := &root, len(labels)-1; i >= 0; i-- { label := labels[i] n = n.child(label) if i == 0 { if nt != nodeTypeParentOnly && n.nodeType == nodeTypeParentOnly { n.nodeType = nt } n.icann = n.icann && icann n.wildcard = n.wildcard || wildcard } labelsMap[label] = true } } labelsList = make([]string, 0, len(labelsMap)) for label := range labelsMap { labelsList = append(labelsList, label) } sort.Strings(labelsList) p := printReal if *test { p = printTest } if err := p(buf, &root); err != nil { return err } if *v { fmt.Fprintf(os.Stderr, "max children %d (capacity %d)\n", maxChildren, 1<<nodesBitsChildren-1) fmt.Fprintf(os.Stderr, "max text offset %d (capacity %d)\n", maxTextOffset, 1<<nodesBitsTextOffset-1) fmt.Fprintf(os.Stderr, "max text length %d (capacity %d)\n", maxTextLength, 1<<nodesBitsTextLength-1) fmt.Fprintf(os.Stderr, "max hi %d (capacity %d)\n", maxHi, 1<<childrenBitsHi-1) fmt.Fprintf(os.Stderr, "max lo %d (capacity %d)\n", maxLo, 1<<childrenBitsLo-1) } b, err := format.Source(buf.Bytes()) if err != nil { return err } _, err = os.Stdout.Write(b) return err } func printTest(w io.Writer, n *node) error { fmt.Fprintf(w, "// generated by go run gen.go; DO NOT EDIT\n\n") fmt.Fprintf(w, "package publicsuffix\n\nvar rules = [...]string{\n") for _, rule := range rules { fmt.Fprintf(w, "%q,\n", rule) } fmt.Fprintf(w, "}\n\nvar nodeLabels = [...]string{\n") if err := n.walk(w, printNodeLabel); err != nil { return err } fmt.Fprintf(w, "}\n") return nil } func printReal(w io.Writer, n *node) error { const header = `// generated by go run gen.go; DO NOT EDIT package publicsuffix const version = %q const ( nodesBitsChildren = %d nodesBitsICANN = %d nodesBitsTextOffset = %d nodesBitsTextLength = %d childrenBitsWildcard = %d childrenBitsNodeType = %d childrenBitsHi = %d childrenBitsLo = %d ) const ( nodeTypeNormal = %d nodeTypeException = %d nodeTypeParentOnly = %d ) // numTLD is the number of top level domains. const numTLD = %d ` fmt.Fprintf(w, header, *version, nodesBitsChildren, nodesBitsICANN, nodesBitsTextOffset, nodesBitsTextLength, childrenBitsWildcard, childrenBitsNodeType, childrenBitsHi, childrenBitsLo, nodeTypeNormal, nodeTypeException, nodeTypeParentOnly, len(n.children)) text := makeText() if text == "" { return fmt.Errorf("internal error: makeText returned no text") } for _, label := range labelsList { offset, length := strings.Index(text, label), len(label) if offset < 0 { return fmt.Errorf("internal error: could not find %q in text %q", label, text) } maxTextOffset, maxTextLength = max(maxTextOffset, offset), max(maxTextLength, length) if offset >= 1<<nodesBitsTextOffset || length >= 1<<nodesBitsTextLength { return fmt.Errorf("text offset/length is too large: %d/%d", offset, length) } labelEncoding[label] = uint32(offset)<<nodesBitsTextLength | uint32(length) } fmt.Fprintf(w, "// Text is the combined text of all labels.\nconst text = ") for len(text) > 0 { n, plus := len(text), "" if n > 64 { n, plus = 64, " +" } fmt.Fprintf(w, "%q%s\n", text[:n], plus) text = text[n:] } n.walk(w, assignIndexes) fmt.Fprintf(w, ` // nodes is the list of nodes. Each node is represented as a uint32, which // encodes the node's children, wildcard bit and node type (as an index into // the children array), ICANN bit and text. // // In the //-comment after each node's data, the nodes indexes of the children // are formatted as (n0x1234-n0x1256), with * denoting the wildcard bit. The // nodeType is printed as + for normal, ! for exception, and o for parent-only // nodes that have children but don't match a domain label in their own right. // An I denotes an ICANN domain. // // The layout within the uint32, from MSB to LSB, is: // [%2d bits] unused // [%2d bits] children index // [%2d bits] ICANN bit // [%2d bits] text index // [%2d bits] text length var nodes = [...]uint32{ `, 32-nodesBitsChildren-nodesBitsICANN-nodesBitsTextOffset-nodesBitsTextLength, nodesBitsChildren, nodesBitsICANN, nodesBitsTextOffset, nodesBitsTextLength) if err := n.walk(w, printNode); err != nil { return err } fmt.Fprintf(w, `} // children is the list of nodes' children, the parent's wildcard bit and the // parent's node type. If a node has no children then their children index // will be in the range [0, 6), depending on the wildcard bit and node type. // // The layout within the uint32, from MSB to LSB, is: // [%2d bits] unused // [%2d bits] wildcard bit // [%2d bits] node type // [%2d bits] high nodes index (exclusive) of children // [%2d bits] low nodes index (inclusive) of children var children=[...]uint32{ `, 32-childrenBitsWildcard-childrenBitsNodeType-childrenBitsHi-childrenBitsLo, childrenBitsWildcard, childrenBitsNodeType, childrenBitsHi, childrenBitsLo) for i, c := range childrenEncoding { s := "---------------" lo := c & (1<<childrenBitsLo - 1) hi := (c >> childrenBitsLo) & (1<<childrenBitsHi - 1) if lo != hi { s = fmt.Sprintf("n0x%04x-n0x%04x", lo, hi) } nodeType := int(c>>(childrenBitsLo+childrenBitsHi)) & (1<<childrenBitsNodeType - 1) wildcard := c>>(childrenBitsLo+childrenBitsHi+childrenBitsNodeType) != 0 fmt.Fprintf(w, "0x%08x, // c0x%04x (%s)%s %s\n", c, i, s, wildcardStr(wildcard), nodeTypeStr(nodeType)) } fmt.Fprintf(w, "}\n") return nil } type node struct { label string nodeType int icann bool wildcard bool // nodesIndex and childrenIndex are the index of this node in the nodes // and the index of its children offset/length in the children arrays. nodesIndex, childrenIndex int // firstChild is the index of this node's first child, or zero if this // node has no children. firstChild int // children are the node's children, in strictly increasing node label order. children []*node } func (n *node) walk(w io.Writer, f func(w1 io.Writer, n1 *node) error) error { if err := f(w, n); err != nil { return err } for _, c := range n.children { if err := c.walk(w, f); err != nil { return err } } return nil } // child returns the child of n with the given label. The child is created if // it did not exist beforehand. func (n *node) child(label string) *node { for _, c := range n.children { if c.label == label { return c } } c := &node{ label: label, nodeType: nodeTypeParentOnly, icann: true, } n.children = append(n.children, c) sort.Sort(byLabel(n.children)) return c } type byLabel []*node func (b byLabel) Len() int { return len(b) } func (b byLabel) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b byLabel) Less(i, j int) bool { return b[i].label < b[j].label } var nextNodesIndex int // childrenEncoding are the encoded entries in the generated children array. // All these pre-defined entries have no children. var childrenEncoding = []uint32{ 0 << (childrenBitsLo + childrenBitsHi), // Without wildcard bit, nodeTypeNormal. 1 << (childrenBitsLo + childrenBitsHi), // Without wildcard bit, nodeTypeException. 2 << (childrenBitsLo + childrenBitsHi), // Without wildcard bit, nodeTypeParentOnly. 4 << (childrenBitsLo + childrenBitsHi), // With wildcard bit, nodeTypeNormal. 5 << (childrenBitsLo + childrenBitsHi), // With wildcard bit, nodeTypeException. 6 << (childrenBitsLo + childrenBitsHi), // With wildcard bit, nodeTypeParentOnly. } var firstCallToAssignIndexes = true func assignIndexes(w io.Writer, n *node) error { if len(n.children) != 0 { // Assign nodesIndex. n.firstChild = nextNodesIndex for _, c := range n.children { c.nodesIndex = nextNodesIndex nextNodesIndex++ } // The root node's children is implicit. if firstCallToAssignIndexes { firstCallToAssignIndexes = false return nil } // Assign childrenIndex. maxChildren = max(maxChildren, len(childrenEncoding)) if len(childrenEncoding) >= 1<<nodesBitsChildren { return fmt.Errorf("children table is too large") } n.childrenIndex = len(childrenEncoding) lo := uint32(n.firstChild) hi := lo + uint32(len(n.children)) maxLo, maxHi = u32max(maxLo, lo), u32max(maxHi, hi) if lo >= 1<<childrenBitsLo || hi >= 1<<childrenBitsHi { return fmt.Errorf("children lo/hi is too large: %d/%d", lo, hi) } enc := hi<<childrenBitsLo | lo enc |= uint32(n.nodeType) << (childrenBitsLo + childrenBitsHi) if n.wildcard { enc |= 1 << (childrenBitsLo + childrenBitsHi + childrenBitsNodeType) } childrenEncoding = append(childrenEncoding, enc) } else { n.childrenIndex = n.nodeType if n.wildcard { n.childrenIndex += numNodeType } } return nil } func printNode(w io.Writer, n *node) error { for _, c := range n.children { s := "---------------" if len(c.children) != 0 { s = fmt.Sprintf("n0x%04x-n0x%04x", c.firstChild, c.firstChild+len(c.children)) } encoding := labelEncoding[c.label] if c.icann { encoding |= 1 << (nodesBitsTextLength + nodesBitsTextOffset) } encoding |= uint32(c.childrenIndex) << (nodesBitsTextLength + nodesBitsTextOffset + nodesBitsICANN) fmt.Fprintf(w, "0x%08x, // n0x%04x c0x%04x (%s)%s %s %s %s\n", encoding, c.nodesIndex, c.childrenIndex, s, wildcardStr(c.wildcard), nodeTypeStr(c.nodeType), icannStr(c.icann), c.label, ) } return nil } func printNodeLabel(w io.Writer, n *node) error { for _, c := range n.children { fmt.Fprintf(w, "%q,\n", c.label) } return nil } func icannStr(icann bool) string { if icann { return "I" } return " " } func wildcardStr(wildcard bool) string { if wildcard { return "*" } return " " } // makeText combines all the strings in labelsList to form one giant string. // If the crush flag is true, then overlapping strings will be merged: "arpa" // and "parliament" could yield "arparliament". func makeText() string { if !*crush { return strings.Join(labelsList, "") } beforeLength := 0 for _, s := range labelsList { beforeLength += len(s) } // Make a copy of labelsList. ss := append(make([]string, 0, len(labelsList)), labelsList...) // Remove strings that are substrings of other strings. for changed := true; changed; { changed = false for i, s := range ss { if s == "" { continue } for j, t := range ss { if i != j && t != "" && strings.Contains(s, t) { changed = true ss[j] = "" } } } } // Remove the empty strings. sort.Strings(ss) for len(ss) > 0 && ss[0] == "" { ss = ss[1:] } // Join strings where one suffix matches another prefix. for { // Find best i, j, k such that ss[i][len-k:] == ss[j][:k], // maximizing overlap length k. besti := -1 bestj := -1 bestk := 0 for i, s := range ss { if s == "" { continue } for j, t := range ss { if i == j { continue } for k := bestk + 1; k <= len(s) && k <= len(t); k++ { if s[len(s)-k:] == t[:k] { besti = i bestj = j bestk = k } } } } if bestk > 0 { if *v { fmt.Fprintf(os.Stderr, "%d-length overlap at (%4d,%4d) out of (%4d,%4d): %q and %q\n", bestk, besti, bestj, len(ss), len(ss), ss[besti], ss[bestj]) } ss[besti] += ss[bestj][bestk:] ss[bestj] = "" continue } break } text := strings.Join(ss, "") if *v { fmt.Fprintf(os.Stderr, "crushed %d bytes to become %d bytes\n", beforeLength, len(text)) } return text } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/publicsuffix/list_test.go�����������������������������0000644�0000153�0000161�00000023160�12321735652�025700� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 publicsuffix import ( "sort" "strings" "testing" ) func TestNodeLabel(t *testing.T) { for i, want := range nodeLabels { got := nodeLabel(uint32(i)) if got != want { t.Errorf("%d: got %q, want %q", i, got, want) } } } func TestFind(t *testing.T) { testCases := []string{ "", "a", "a0", "aaaa", "ao", "ap", "ar", "aro", "arp", "arpa", "arpaa", "arpb", "az", "b", "b0", "ba", "z", "zu", "zv", "zw", "zx", "zy", "zz", "zzzz", } for _, tc := range testCases { got := find(tc, 0, numTLD) want := notFound for i := uint32(0); i < numTLD; i++ { if tc == nodeLabel(i) { want = i break } } if got != want { t.Errorf("%q: got %d, want %d", tc, got, want) } } } func TestICANN(t *testing.T) { testCases := map[string]bool{ "foo.org": true, "foo.co.uk": true, "foo.dyndns.org": false, "foo.go.dyndns.org": false, "foo.blogspot.co.uk": false, "foo.intranet": false, } for domain, want := range testCases { _, got := PublicSuffix(domain) if got != want { t.Errorf("%q: got %v, want %v", domain, got, want) } } } var publicSuffixTestCases = []struct { domain, want string }{ // Empty string. {"", ""}, // The .ao rules are: // ao // ed.ao // gv.ao // og.ao // co.ao // pb.ao // it.ao {"ao", "ao"}, {"www.ao", "ao"}, {"pb.ao", "pb.ao"}, {"www.pb.ao", "pb.ao"}, {"www.xxx.yyy.zzz.pb.ao", "pb.ao"}, // The .ar rules are: // ar // com.ar // edu.ar // gob.ar // int.ar // mil.ar // net.ar // org.ar // tur.ar // blogspot.com.ar {"ar", "ar"}, {"www.ar", "ar"}, {"nic.ar", "ar"}, {"www.nic.ar", "ar"}, {"com.ar", "com.ar"}, {"www.com.ar", "com.ar"}, {"blogspot.com.ar", "blogspot.com.ar"}, {"www.blogspot.com.ar", "blogspot.com.ar"}, {"www.xxx.yyy.zzz.blogspot.com.ar", "blogspot.com.ar"}, {"logspot.com.ar", "com.ar"}, {"zlogspot.com.ar", "com.ar"}, {"zblogspot.com.ar", "com.ar"}, // The .arpa rules are: // e164.arpa // in-addr.arpa // ip6.arpa // iris.arpa // uri.arpa // urn.arpa {"arpa", "arpa"}, {"www.arpa", "arpa"}, {"urn.arpa", "urn.arpa"}, {"www.urn.arpa", "urn.arpa"}, {"www.xxx.yyy.zzz.urn.arpa", "urn.arpa"}, // The relevant {kobe,kyoto}.jp rules are: // jp // *.kobe.jp // !city.kobe.jp // kyoto.jp // ide.kyoto.jp {"jp", "jp"}, {"kobe.jp", "jp"}, {"c.kobe.jp", "c.kobe.jp"}, {"b.c.kobe.jp", "c.kobe.jp"}, {"a.b.c.kobe.jp", "c.kobe.jp"}, {"city.kobe.jp", "kobe.jp"}, {"www.city.kobe.jp", "kobe.jp"}, {"kyoto.jp", "kyoto.jp"}, {"test.kyoto.jp", "kyoto.jp"}, {"ide.kyoto.jp", "ide.kyoto.jp"}, {"b.ide.kyoto.jp", "ide.kyoto.jp"}, {"a.b.ide.kyoto.jp", "ide.kyoto.jp"}, // The .tw rules are: // tw // edu.tw // gov.tw // mil.tw // com.tw // net.tw // org.tw // idv.tw // game.tw // ebiz.tw // club.tw // 網路.tw (xn--zf0ao64a.tw) // 組織.tw (xn--uc0atv.tw) // 商業.tw (xn--czrw28b.tw) // blogspot.tw {"tw", "tw"}, {"aaa.tw", "tw"}, {"www.aaa.tw", "tw"}, {"xn--czrw28b.aaa.tw", "tw"}, {"edu.tw", "edu.tw"}, {"www.edu.tw", "edu.tw"}, {"xn--czrw28b.edu.tw", "edu.tw"}, {"xn--czrw28b.tw", "xn--czrw28b.tw"}, {"www.xn--czrw28b.tw", "xn--czrw28b.tw"}, {"xn--uc0atv.xn--czrw28b.tw", "xn--czrw28b.tw"}, {"xn--kpry57d.tw", "tw"}, // The .uk rules are: // *.uk // *.sch.uk // !bl.uk // !british-library.uk // !jet.uk // !mod.uk // !national-library-scotland.uk // !nel.uk // !nic.uk // !nls.uk // !parliament.uk // blogspot.co.uk {"uk", "uk"}, {"aaa.uk", "aaa.uk"}, {"www.aaa.uk", "aaa.uk"}, {"mod.uk", "uk"}, {"www.mod.uk", "uk"}, {"sch.uk", "sch.uk"}, {"mod.sch.uk", "mod.sch.uk"}, {"www.sch.uk", "www.sch.uk"}, {"blogspot.co.uk", "blogspot.co.uk"}, {"blogspot.nic.uk", "uk"}, {"blogspot.sch.uk", "blogspot.sch.uk"}, // The .рф rules are // рф (xn--p1ai) {"xn--p1ai", "xn--p1ai"}, {"aaa.xn--p1ai", "xn--p1ai"}, {"www.xxx.yyy.xn--p1ai", "xn--p1ai"}, // The .zw rules are: // *.zw {"zw", "zw"}, {"www.zw", "www.zw"}, {"zzz.zw", "zzz.zw"}, {"www.zzz.zw", "zzz.zw"}, {"www.xxx.yyy.zzz.zw", "zzz.zw"}, // There are no .nosuchtld rules. {"nosuchtld", "nosuchtld"}, {"foo.nosuchtld", "nosuchtld"}, {"bar.foo.nosuchtld", "nosuchtld"}, } func BenchmarkPublicSuffix(b *testing.B) { for i := 0; i < b.N; i++ { for _, tc := range publicSuffixTestCases { List.PublicSuffix(tc.domain) } } } func TestPublicSuffix(t *testing.T) { for _, tc := range publicSuffixTestCases { got := List.PublicSuffix(tc.domain) if got != tc.want { t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want) } } } func TestSlowPublicSuffix(t *testing.T) { for _, tc := range publicSuffixTestCases { got := slowPublicSuffix(tc.domain) if got != tc.want { t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want) } } } // slowPublicSuffix implements the canonical (but O(number of rules)) public // suffix algorithm described at http://publicsuffix.org/list/. // // 1. Match domain against all rules and take note of the matching ones. // 2. If no rules match, the prevailing rule is "*". // 3. If more than one rule matches, the prevailing rule is the one which is an exception rule. // 4. If there is no matching exception rule, the prevailing rule is the one with the most labels. // 5. If the prevailing rule is a exception rule, modify it by removing the leftmost label. // 6. The public suffix is the set of labels from the domain which directly match the labels of the prevailing rule (joined by dots). // 7. The registered or registrable domain is the public suffix plus one additional label. // // This function returns the public suffix, not the registrable domain, and so // it stops after step 6. func slowPublicSuffix(domain string) string { match := func(rulePart, domainPart string) bool { switch rulePart[0] { case '*': return true case '!': return rulePart[1:] == domainPart } return rulePart == domainPart } domainParts := strings.Split(domain, ".") var matchingRules [][]string loop: for _, rule := range rules { ruleParts := strings.Split(rule, ".") if len(domainParts) < len(ruleParts) { continue } for i := range ruleParts { rulePart := ruleParts[len(ruleParts)-1-i] domainPart := domainParts[len(domainParts)-1-i] if !match(rulePart, domainPart) { continue loop } } matchingRules = append(matchingRules, ruleParts) } if len(matchingRules) == 0 { matchingRules = append(matchingRules, []string{"*"}) } else { sort.Sort(byPriority(matchingRules)) } prevailing := matchingRules[0] if prevailing[0][0] == '!' { prevailing = prevailing[1:] } if prevailing[0][0] == '*' { replaced := domainParts[len(domainParts)-len(prevailing)] prevailing = append([]string{replaced}, prevailing[1:]...) } return strings.Join(prevailing, ".") } type byPriority [][]string func (b byPriority) Len() int { return len(b) } func (b byPriority) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b byPriority) Less(i, j int) bool { if b[i][0][0] == '!' { return true } if b[j][0][0] == '!' { return false } return len(b[i]) > len(b[j]) } // eTLDPlusOneTestCases come from // http://mxr.mozilla.org/mozilla-central/source/netwerk/test/unit/data/test_psl.txt var eTLDPlusOneTestCases = []struct { domain, want string }{ // Empty input. {"", ""}, // Unlisted TLD. {"example", ""}, {"example.example", "example.example"}, {"b.example.example", "example.example"}, {"a.b.example.example", "example.example"}, // TLD with only 1 rule. {"biz", ""}, {"domain.biz", "domain.biz"}, {"b.domain.biz", "domain.biz"}, {"a.b.domain.biz", "domain.biz"}, // TLD with some 2-level rules. {"com", ""}, {"example.com", "example.com"}, {"b.example.com", "example.com"}, {"a.b.example.com", "example.com"}, {"uk.com", ""}, {"example.uk.com", "example.uk.com"}, {"b.example.uk.com", "example.uk.com"}, {"a.b.example.uk.com", "example.uk.com"}, {"test.ac", "test.ac"}, // TLD with only 1 (wildcard) rule. {"cy", ""}, {"c.cy", ""}, {"b.c.cy", "b.c.cy"}, {"a.b.c.cy", "b.c.cy"}, // More complex TLD. {"jp", ""}, {"test.jp", "test.jp"}, {"www.test.jp", "test.jp"}, {"ac.jp", ""}, {"test.ac.jp", "test.ac.jp"}, {"www.test.ac.jp", "test.ac.jp"}, {"kyoto.jp", ""}, {"test.kyoto.jp", "test.kyoto.jp"}, {"ide.kyoto.jp", ""}, {"b.ide.kyoto.jp", "b.ide.kyoto.jp"}, {"a.b.ide.kyoto.jp", "b.ide.kyoto.jp"}, {"c.kobe.jp", ""}, {"b.c.kobe.jp", "b.c.kobe.jp"}, {"a.b.c.kobe.jp", "b.c.kobe.jp"}, {"city.kobe.jp", "city.kobe.jp"}, {"www.city.kobe.jp", "city.kobe.jp"}, // TLD with a wildcard rule and exceptions. {"ck", ""}, {"test.ck", ""}, {"b.test.ck", "b.test.ck"}, {"a.b.test.ck", "b.test.ck"}, {"www.ck", "www.ck"}, {"www.www.ck", "www.ck"}, // US K12. {"us", ""}, {"test.us", "test.us"}, {"www.test.us", "test.us"}, {"ak.us", ""}, {"test.ak.us", "test.ak.us"}, {"www.test.ak.us", "test.ak.us"}, {"k12.ak.us", ""}, {"test.k12.ak.us", "test.k12.ak.us"}, {"www.test.k12.ak.us", "test.k12.ak.us"}, // Punycoded IDN labels {"xn--85x722f.com.cn", "xn--85x722f.com.cn"}, {"xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn"}, {"www.xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn"}, {"shishi.xn--55qx5d.cn", "shishi.xn--55qx5d.cn"}, {"xn--55qx5d.cn", ""}, {"xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s"}, {"www.xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s"}, {"shishi.xn--fiqs8s", "shishi.xn--fiqs8s"}, {"xn--fiqs8s", ""}, } func TestEffectiveTLDPlusOne(t *testing.T) { for _, tc := range eTLDPlusOneTestCases { got, _ := EffectiveTLDPlusOne(tc.domain) if got != tc.want { t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want) } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/publicsuffix/table.go���������������������������������0000644�0000153�0000161�00001535270�12321735652�024770� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// generated by go run gen.go; DO NOT EDIT package publicsuffix const version = "publicsuffix.org's effective_tld_names.dat, hg revision e85f83f352de (2014-02-07)" const ( nodesBitsChildren = 9 nodesBitsICANN = 1 nodesBitsTextOffset = 15 nodesBitsTextLength = 6 childrenBitsWildcard = 1 childrenBitsNodeType = 2 childrenBitsHi = 14 childrenBitsLo = 14 ) const ( nodeTypeNormal = 0 nodeTypeException = 1 nodeTypeParentOnly = 2 ) // numTLD is the number of top level domains. const numTLD = 560 // Text is the combined text of all labels. const text = "bifukagawatch-and-clockasaokamiokamiminers3-website-sa-east-1bih" + "orologyonaguniversityumenaustdalindaskimitsubatamiasakuchinotsuc" + "hiurakawalbrzychampionshipalanakatombetsupportargiheyakumodumelo" + "yalisteigenavalindesnes3-website-us-east-1bikedavvesiidazaifuchu" + "kotkakudamatsuedtirollagdenesnaaseralingenkainanaejrietiendaegub" + "alestrandabergamoarekesennumalvikarlsoyokote12bilbaogakievenassi" + "sibenikihokumakogenglandnipropetrovskashibatakasakiyosumitakagin" + "ozawaonsenavigationavuotnakasatsunaiitatebayashichinohelsinkitak" + "yushuaiabillustrationayorobirabioceanographics3-website-us-gov-w" + "est-1birdartdecoalinkzgorabirkenesoddtangenoamishirasatodayoriik" + "ashiharabirthplacemergencyberlevagangaviikarugaulardalinzaiiyama" + "nobeeldengeluidnpaleoceanographiquepilepsystems3-website-us-west" + "-1bjarkoystre-slidrettarnobrzeglassassinationalfirearms3-website" + "-us-west-2bjerkreimmobilienikkoebenhavnikolaevenessetagayaseljor" + "dpalermombetsupplyukindianapolis-a-bloggerbjugninohembygdsforbun" + "drangedalipetskashiwarabluenoharabmdrobakrehamninomiyakonojourna" + "lismolenskashiwazakizunokunimilitaryukuhashimogosenirasakindianm" + "arketingliwiceventsakuhokksundyndns-at-workinggroupowiatarumizus" + "awabomloabathsakuragawabonnishiawakurabostonakijinsekikogenovara" + "botanicalgardenishiazaindustriesteamsterdambulanceohtawaramotoin" + "eppullensvanglobalatinabeauxartsandcraftsakurainfoggiabotanicgar" + "denishigomurabotanycarrierboutiquebecartoonarteducationalchikugo" + "jomediamondsakyotanabellevuedatingloboknowsitallivornomutashinai" + "ntelligencevje-og-hornnesalangenishiharabozentsujiiexeterbrandyw" + "inevalleyurihonjostrodabrasiljan-mayenishiizunazukinternational-" + "library-scotlandyndns-blogdnsalatatarstanishikatakatorinuyamanou" + "chikuhokuryugasakitchenishikatsuragis-a-bulls-fanishikawazukanan" + "porostrolekanazawabremangerbresciabrindisiellakasamatsudoesntexi" + "steingeekasukabeerbristolgamvikasumigaurawa-mazowszexchangexhibi" + "tionishimerabritish-libraryazanconagawakuyachimatainaikawababia-" + "gorakkestadultateyamabritishcolumbialowiezachpomorskienishinomiy" + "ashironostrowiecasadelamonedabroadcastleasinglesaltdalosangelesa" + "lvadordalillehammerfest-mon-blogueurbroke-itatsunostrowwlkpalmsp" + "ringsakerbrokerbronnoysundyndns-freemasonryusuharabrumunddaloten" + "eis-a-candidatexposeducatorahimeshimakanegasakirovogradoyusuisse" + "minexpressexyzgorzeleccollectionishinoomotegobrunelblagrarboretu" + "memorialouvrebrusselsalzburglogowfarmsteadyndns-homeftpaccesshni" + "shinoshimabruxellesjamalborkdalowiczest-a-la-masionishiokoppegar" + "dyndns-ipanamabryanskjervoyagetmyiparachutingloppenzaokinawashir" + "osatobamaizurubtsovskjaknoluoktaikibichuozulminamidaitomandalucc" + "apebretonamiastarostwodzislawhalingminakamichigangwonishitosashi" + "mizunaminamiawajikis-a-catererbrynewhampshirecreationishiwakis-a" + "-celticsfanissedalucernebuskerudinewjerseyuulsandoyuzawabuyshous" + "esamegawabuzenisshinguidebuzzgradyndns-mailukowildlifedjelenia-g" + "orabvballooninggfarmerseine164bwindmilluroyuzhno-sakhalinskasuya" + "megurobzhytomyrcircuscultureggiocalabriacitychyllestadyndns-wiki" + "rkenesanjournalistavropolkowicecivilaviationcivilisationciviliza" + "tioncivilwarmiamibuildingrimstadyndns-workshoppdalustercleaningr" + "oks-thisayamanashichikashukujukuriyamarnardalutskautokeinoclinto" + "noshoesannanclothingrongacloudcontrolappspotenzagannakadomari-el" + "asticbeanstalkazimierz-dolnycloudcontrolledogawarabikomaezakirun" + "ore-og-uvdaluxembourgrossetouchijiwadepotherokusslattuminamiechi" + "zencloudfrontariodejaneiromskoguchikuzencntjeldsundyroycollegers" + "undcolonialwilliamsburgroundhandlingroznycoloradoplateaukraanghk" + "emerovodkagoshimalopolskanlandcolumbusantiquesannohemnesantabarb" + "aracomobaracompanycompute-1computerhistoryofscience-fictioncondo" + "shibuyachtsantacruzhgorodoyconferenceconstructionconsuladoomdnsa" + "liascolipicenord-aurdaltoadigemologicaliforniaconsultanthropolog" + "yconsultingvolluxurycontemporaryartgalleryggeelvinckazoologicalu" + "zerncontractorskenconventureshinodesashibetsuikinkobayashijonawa" + "televisioncookingrparliamentjomelhusdecorativeartsantafederation" + "coolbia-tempio-olbiatempioolbialystokkecooperaunitexasiacopenhag" + "encyclopedicasinordre-landyndns-office-on-the-webhopocznorfolkeb" + "iblegnicagliaridagawatchandclockaszubytomakomaibaracorporationco" + "rvettextileborkazunocosenzakopanewspapercostumedicaltanissettaiw" + "anaip6boneat-urlevangerhcloudappalace-burg12000councilvivcastres" + "istancecountydalcqhaborovnotairesanukis-a-democratoyakokamisatoh" + "noshoocranbrookuwanamizuhobby-sitecremonashorokanaiecrewroclawtc" + "atanzarowloclawekatowicecrimeacrotonewyorkshireggioemiliacruises" + "aotomeiwamatsushigeculturalcentertainmentoyokawacuneocupcakecxje" + "tztoyonakagyokutomaritimekeepingruefhvalerfieldfiguerest-le-patr" + "ondheiminamifuranofilateliafilminamiiserniafineartsapporofinland" + "finnoyfirenzefirminamiizukamisunagawafishingoparmafitjarchitectu" + "rennebudapest-a-la-maisondre-landfjalerdalflekkefjordflesbergenf" + "lightsaratovdonskgunmarugame-hostre-totenkawaflogisticsarpsborgu" + "ovdageaidnurembergushikamifuranoshirooshikamaishikshacknetnedalf" + "loraflorencefloridafloristanohatajirittogorgeflorovigorlicefndfo" + "lldalfor-better-thanawafor-ourfor-somedio-campidano-mediocampida" + "nomediofor-thedmarkhangelskhabarovskhakassiaforceforgotdnsarufut" + "sunomiyawakasaikaitakoelnforli-cesena-forlicesenaforlikescandyna" + "thomebuiltoyonoforsandasuolodingenfortmissoulan-udell-ogliastrak" + "hanamigawafortworthachijorpelandforuminamimakis-a-designerfosnes" + "asayamafotoyookanumazuryfredrikstadaokagakisarazure-mobileikange" + "rfreiburgwangjurfreightoyosatomobellunordkappgxn--1qqw23afribour" + "gzlgfrogansaseboltoyotaris-a-doctorfrognfrolandfrom-akunemuroran" + "koshigayabukicks-assedicharterfrom-alfrom-arqldfrom-azminamimino" + "wafrom-cahcesuolocalhistorybnikahokutoeigersundfrom-coffeedbackh" + "arkivguernseyfrom-ctoyotomiyazakis-a-financialadvisor-aurdalfrom" + "-dchattanooganordreisa-geekatsushikabeiarndyndns-picsiroroskoleb" + "timnetzjeonnamerikawauefrom-dellogliastraderfrom-flakstadtoyotsu" + "kaidofrom-gausdalfrom-hichisoftwarendalenvikingatlantakahamannos" + "egawafrom-iafrom-idfrom-ilfrom-incheonfrom-ksaskatchewanfrom-kyo" + "tobetsuwanouchikushinonsennanbungotakadafrom-lahppiacenzamamidor" + "is-a-geekharkovhachinohekinannestadfrom-mansionsassaris-a-greenf" + "rom-mdfrom-medizinhistorischesatxn--3bst00minamiogunionfrom-mids" + "undfrom-mnfrom-modalenfrom-msaudafrom-mtoyourafrom-ncheaparaglid" + "ingqcasertaishinomakikugawawithgoogleapisa-hockeynutattoolsztyns" + "ettlersamnangerfrom-ndfrom-nefrom-nhachiojiyaitakamoriokamchatka" + "meokameyamashinatsukigatakanabedzin-addrammenuernbergfrom-njevna" + "kerfrom-nminamisanrikubetsuppliesauheradfrom-nvegaskvollfrom-nyf" + "rom-ohdafrom-oketogurafrom-orlandfrom-pacificheltenham-radio-ope" + "nair-surveillancefrom-praxis-a-anarchistoirepair-traffic-control" + "leyfrom-ris-a-gurulvikhersonfrom-schlesischesavannahgafrom-sdfro" + "m-tnfrom-txn--3ds443gfrom-utazunjargafrom-vacationsaves-the-whal" + "essandria-trani-barletta-andriatranibarlettaandriafrom-vtozawafr" + "om-wafrom-wielunnerfrom-wvenneslaskerfrom-wyfrosinonefrostarnber" + "gfroyahikobearalvahkikuchikumagayagawalesundfstathelleirfjordfuj" + "iiderafujikawaguchikonefujiminohkurafujinomiyadafujiokayamantova" + "dsogndalfujisatoshonairguardfujisawafujishiroishidakabiratoridel" + "menhorstalowa-wolawafujiyoshidafukayabeardudinkakegawallonieruch" + "omoscienceandindustrynfukuchiyamadafukudominichelyabinsklepparis" + "or-froniyodogawafukuis-a-hard-workerfukumitsukefukuokazakishiwad" + "afukuroishigakisofukushimanxn--3e0b707efukusakisosakitagatakahar" + "unsakakinokis-a-hunterfukuyamagatakahashimamakitagawafunabashiri" + "uchinadafunagatakahatakaishimofusagaeroclubindallaspeziamallamad" + "ridvrdnsdojobojis-a-knightozsdefunahashikamiamakusatsumasendaise" + "nfundaciofuoiskujitawarafuosskoczowwwfurniturepbodyndns-at-homed" + "nswedenfurubiraquariuminamitanefurudonostiafurukawaharafusognefu" + "ssagamiharafutabayamaguchinomigawafutboldlygoingnowhere-for-more" + "gontrailroadfuttsunanjohanamakinoharafylkesbiblackfridayfyresdal" + "hakuis-a-landscaperugiahakusandnessjoenhaldenhalsaikitakamiizumi" + "sanoksnesayokkaichirurgiens-dentisteschokoladenhammarfeastafrica" + "mbridgets-itraininghamurakamigoris-a-lawyerhangglidinghannanmoku" + "izumodenakaniikawatanagurahannoverhallancashireportranaklodzkoda" + "irahanyuzenhapmirkutskhmelnitskiyamassa-carrara-massacarraramass" + "abusheyhappounzenhareidsbergbauernusgardenharstadharvestcelebrat" + "ionhasamarahasaminami-alpschoenbrunnhasudahasvikhmelnytskyiveron" + "amsosnowiecherkasydneyhatogayaizuwakamatsubushikusakadogawahatoy" + "amazakitakatakaokamikitayamatotakadahatsukaichiharahattfjelldalh" + "austevollhawaiijimarylandhayashimamotobunkyonanaoshimageandsound" + "andvisionhazuminobusenetranbyhemsedalheroyhigashichichiburyatiah" + "igashihiroshimanehigashiizumozakitamotosumidatlantichernigovernm" + "entaxis-a-chefarsundyndns-remoteginankokubunjis-a-conservativefs" + "nillfjordyndns-serverbaniahigashikagawahigashikagurasoedahigashi" + "kawakitaaikitanakagusukumodernhigashikurumeereschoolhigashimatsu" + "shimarylhurstationhigashimatsuyamakitaakitadaitoigawahigashimura" + "yamalatvuopmifunehigashinarusells-for-lesschweizhevskhverranhiga" + "shinehigashiomihachimanagementrani-andria-barletta-trani-andriah" + "igashiosakasayamamotorcyclesciencecentersciencehistoryhigashishi" + "rakawamatakarazukamikoaniikappulawyhigashisumiyoshikawaminamiaik" + "itashiobarahigashitsunohigashiurausukitaurayasudahigashiyamatoko" + "riyamanakakogawahigashiyodogawahigashiyoshinogaris-a-liberalhira" + "izumisatohmasfjordenhirakatashinagawahiranairportland-4-salernog" + "atagajoetsuruokafjordhirarahiratsukagawahirayakagehistorichouses" + "cientisteinkjerusalembetsukuis-a-libertarianhitachiomiyaginowani" + "ihamatamakawajimarburghitachiotagoshikiminokamoenairlinebraskaun" + "bieidsvollhitoyoshimihamadahitradinghjartdalhjelmelandholdingsmo" + "lajollanbibaidarholeckobierzyceholidayhomelinuxn--45brj9chernihi" + "vanovosibirskodjeffersoniigataitogakushimotoganewmexicodynaliasc" + "oli-picenonoichikawamisatobishimallorcabalsanagochihayaakasakawa" + "goebinagisodegauraustraliaomoriguchiharamlierneues3-ap-southeast" + "-1homesaverdehomeunixn--45q11chernivtsienagatorokunohealthruhere" + "ggio-emiliahonefosscrapper-sitehongotembaixadahonjyoichiropracti" + "chernovtsykkylveneziahornindalhorsells-for-uslivinghistoryhorten" + "dofinternetraniandriabarlettatraniandriahoteledatabaseballangenh" + "oyangerhoylandetroitranoyhumanitiescrappinghurdalhurumajis-a-lin" + "ux-useranishiaritabashiibahccavuotnagareyamalselvendrellhyugawar" + "aissmarterthanyouthachirogatakanezawaiwatarailwayiwateiwatsukiyo" + "nojgorajpnkosakaerodromedecinemailkoseis-a-playerkoshimizumakis-" + "a-republicanadakoshunantokamachildrensgardenkostromahabmerkosuge" + "kotohiradomainsurancekotourakouhokutamakis-a-rockstarachowicekou" + "nosumypetshimoichinosekigaharakouyamateramoldekouzushimatsumaeba" + "shikaois-a-socialistavangerkozagawakozakis-a-soxfankrageroticame" + "rakershuscountryestateshinanomachippubetsubetsugaruhrkrakowkrasn" + "oyarskomatsushimasudakredkristiansandefjordkristiansundkrodshera" + "dkrokstadelvaksdalkryminamiyamashirokawanabelgorodeokumatorinoku" + "mejimatsunokumenanyokaichibaikaliszkola-speziakunisakis-a-studen" + "travellinokunitachiaraisaijosoyrokunitomigusukukis-a-teacherkass" + "yzranzankunneppuwajimakunstsammlungkunstunddesignkureviewshimoji" + "s-a-techietipsigdalkurgankurobelaugustowadagestangeologykurogimi" + "matakashimatsusakahogithubaltimore-og-romsdalezajskarmoyokozebin" + "orilskarpaczeladz-2kuroisohuissier-justicekuromatsunairtraffichi" + "ryukyuragifudaigokasejnynysadonnaharimangonohejis-a-cpaderbornls" + "angokurotakikawasakis-a-therapistoiakurskomforbalsfjordivttasvuo" + "tnakamagayahabadajozoravennagaokakyotambaghdadlugolekamakurazaki" + "raustrheimatunduhrennesoygardenvironmentalconservationasushiobar" + "aostalbansnasaarlandgcadaques3-ap-southeast-2kushirogawakustanai" + "shobaraumagazinedre-eikerkusunndalkutchandakutnokuzbassnoasaintl" + "ouis-a-bookkeeperminanokuzumakis-an-accountantrdkvafjordkvalsund" + "kvamurskiptveterinairecipesaro-urbino-pesarourbinopesaromagnitka" + "gaminogiessengerdalto-adigeiseiroumuenchenebakkeshibechambagricu" + "ltureklamberkeleykvanangenkvinesdalkvinnheradkviteseidskogkvitso" + "ykwkyowariasahikawamishimatsuzakis-an-artistavernmissileirvikomi" + "tamamuramisugitokashikis-an-engineermitakeharamitourismincommuni" + "tysvardomitoyoakemiuramiyazurewebsiteshikagamiishikarikaturindal" + "miyotamanomjondalenmonmouthadanotogawamonticellondonetskommunalf" + "orbundmontrealestateofdelawaremarkermonza-brianzamonza-e-della-b" + "rianzamonzabrianzamonzaebrianzamonzaedellabrianzamordoviajesshei" + "minnesotaketakasugais-an-actormoriyamatta-varjjatromsolarssonmor" + "iyoshiokamitondabayashiogamagoriziamormoneyagawamoroyamamoscowmo" + "seushistorymosjoenmoskeneshimokawamosreggio-calabriamosshimokita" + "yamamosvikommunemuenstermugis-an-entertainermuikamitsuemukochiku" + "seihigashiagatsumagoizumizakitamidtre-gauldalmulhousellsyourhome" + "ipasadenakatsugawamunakatanemuncieszynmuosattemurmanskomonomurot" + "orcraftrusteemusashimurayamamusashinoharamuseetrysilkomorotsukam" + "ishihoronobeokaminokawanishiaizubangemuseumverenigingmutsuzawamy" + "photosloppadovanylvenicemytis-a-bruinsfanpaviapharmacienshimonit" + "ayanagis-byklebesbyglandpharmacymruovatmpassenger-associationphi" + "ladelphiaareadmyblogsitephilatelyphoenixn--54b7fta0cchitachinaka" + "gawaphotographyogoris-certifiedunethnologypilotshimonosekikawapi" + "nkomvuxn--4gbriminingpippupiszwpittsburghofauskedsmokorsettlemen" + "toyonezawapkonantanangerplanetariumisakis-an-actresseoullensaker" + "plantationplantshimosuwalkis-foundationplazaplchitosetogitsulike" + "s-pieplorenskogplumbingpodhaleitungsenpodlasiedlcepodzonepoltava" + "researchaeologicalpomorzeszowpordenoneporsangerporsanguitarshimo" + "tsukeporsgrunnanposts-and-telecommunicationshimotsumapoznanprdpr" + "eservationpresidioprincipescaravantaaprivneprochowiceproductions" + "hinichinanprofcateringebuildersanfranciscoldwarszawaprojectulaqu" + "ilapyatigorskongsbergpropertieshinjoyoitakasagotsukitahatakamats" + "ukawapruszkowprzeworskogptzpvturystykanzakiyosemitepwpzqponqslds" + "hiraokanmakiyosatohoboleslawiechocolatelemarkatsuyamasoyshiratak" + "ahagis-into-carservicesettsurgeonshalloffameldalshishikuis-into-" + "cartoonsevastopoleksviklabusinessebydgoszczecincinnationalherita" + "gematsubarakawachinaganoharaogashimadachicagobodoes-itrentinoshi" + "sokannamihokkaidovre-eikershisuifuelverumisasaguris-an-anarchist" + "oricalsocietysneservebbserveftpartnerservegame-servercellillesan" + "diegouvicenzaporizhzheguris-a-nursells-itransportrapaniizashitar" + "amashizukuishimodateshizuokanonjis-into-gamessinashikiwienshowas" + "imbirskongsvingersimple-urlsirdalslgslupskoversailleshinjukumano" + "snzsolognesolundsolutionshinkamigotoyohashimotokorozawasomasomna" + "khodkamogawasoosopotuvarggatverdalsor-odalsor-varangersorfoldsor" + "reisahayakawakamiichikaiseiyokoshibahikariwanumatakazakis-leetre" + "ntomskmshakotankokonoesortlandsorumisawasouthcarolinazawasouthwe" + "sterniiminamiashigarasowaspace-to-rentalstahaugesundspbambleanga" + "viikakamigaharauthordalandiscoveryokosukareliaosteroykeniwaizumi" + "otsukumiyamazonaws3-eu-west-1spjelkavikoninjaworznospydebergsqua" + "rezzoologysrvestbystjohnstjordalshalsenstockholmestrandstor-elvd" + "alstordalstorenburgstorfjordstpetersburgstuff-4-salelstuttgartsu" + "sakis-lostfoldsusonosuzakanoyakutiasuzukanrasvalbardurhamburgsve" + "iosvelvikonskowolanshellaskoyabenord-fronlineustargardsvizzerasw" + "idnicarbonia-iglesias-carboniaiglesiascarboniaswiebodzinderoyswi" + "noujscienceandhistorysxn--55qw42gvestneshinshinotsurgutsiracusai" + "tamatsukuris-gonevestre-slidreamhostershinshirovestre-totenris-n" + "ot-certifiedvestvagoyvevelstadvibo-valentiavibovalentiavideovill" + "asmatartcenterprisesakijogaszczytnord-odalvdalaskanittedalaheadj" + "udygarlandvinnicardshintokushimavinnytsiavirginiavirtualvirtuelv" + "iterbolzanordlandvladikavkazanvladimirumashikevladivostokaizukar" + "asjokonyvelomzaporizhzhiavlogvoldavolgogradvolkenkunderseaportvo" + "logdanskooris-a-photographerokuappartis-a-nascarfanvolyngdalvoro" + "nezhitomirvossevangenvotevotingvotonsbergvrnvyatkarasuyamashikok" + "uchuoxn--80adxhkshintomikasaharaxn--80ao21axn--80asehdbarcelonag" + "asakikonaioirasecngjemnes3-sa-east-1xn--80aswgxn--90a3academykol" + "aivano-frankivskiervaapsteiermarkopervikomakis-a-personaltrainer" + "xn--9dbhblg6dielddanuorrissafetysfjordxn--andy-iraxn--aroport-by" + "anaizuxn--asky-iraxn--aurskog-hland-jnbargainstitutelekommunikat" + "ionativeamericanantiques3-us-gov-west-1xn--avery-yuasakatakayama" + "xn--b-5gaxn--bdddj-mrabdxn--bearalvhki-y4axn--berlevg-jxaxn--bhc" + "avuotna-s4axn--bhccavuotna-k7axn--bidr-5nachikatsuuraxn--bievt-0" + "qaxn--bjarky-fyaotsurreyxn--bjddar-ptakinouexn--blt-elaborxn--bm" + "lo-grajewolominamataketomisatokuyamaxn--bod-2namsskoganeis-saved" + "xn--brnny-wuaccident-preventionjukudoyamaceratabusebastopologyeo" + "ngbukoryolkuszpartsherbrookegawaxn--brnnysund-m8achofunatoringer" + "ikexn--brum-voagatxn--btsfjord-9zaxn--c1avgxn--cg4bkis-slickolob" + "rzegyptiannefrankfurtrevisokndalxn--ciqpnxn--clchc0ea0b2g2a9gcdx" + "n--comunicaes-v6a2oxn--correios-e-telecomunicaes-ghc29axn--czr69" + "4barreaudnedalnaturalhistorymuseumcenterxn--czrs0txn--czru2dxn--" + "czrw28barrel-of-knowledgeometre-experts-comptables3-us-west-1xn-" + "-d1acj3barrell-of-knowledgeorgiautomotivelandivtasvuodnakaiwamiz" + "awastronomyokohamamatsudaejeonbuk-uralsk12xn--davvenjrga-y4axn--" + "dnna-grandrapidshinyoshitomiokaneyamazoexn--drbak-wuaxn--dyry-ir" + "axn--eveni-0qa01gaxn--finny-yuaxn--fiq228c5hshiojirishirifujieda" + "xn--fiq64baselburgjerdrumbonebizenakamuratajimicrolightingjersta" + "dotsurugashimaritimodellingjesdalimanowarudautoshimabariakepnord" + "dalewismillerxn--fiqs8shioyanagawaxn--fiqz9shirahamatonbetsurnad" + "alxn--fjord-lraxn--fl-ziaxn--flor-jraxn--fpcrj9c3dxn--frde-grane" + "xn--frna-woarais-uberleetroandinosaurexn--frya-hraxn--fzc2c9e2ch" + "onangooglecodespotaruis-a-cubicle-slaveroyrviknakanojoshkar-olan" + "gevagsoyxn--gecrj9choseikaluganskydivingretajimarinexn--ggaviika" + "-8ya47hadselfiparochesterxn--gildeskl-g0axn--givuotna-8yaroslavl" + "aanderenxn--gjvik-wuaxn--gls-elacaixaxn--gmq050is-very-badaddjam" + "isongdalenxn--gmqw5axn--h-2familyngenxn--h1aeghaebaruminamiuonum" + "atsuuraxn--h2brj9choshibukawaxn--hbmer-xqaxn--hcesuolo-7ya35bash" + "kiriavocataniaurskog-holandebudejjuedischesapeakebayekaterinburg" + "dyniagroks-theaternopilawakkanaibetsubamericanartanddesignieznod" + "awaraholtalendoftheinternetcmwegrowestfalenarviikanagawaeroporta" + "labamagasakishimabarahkkeravjudaicaarborteaches-yogasawaragusart" + "saritsynarutokigawagrinetworkangerimo-i-ranagahamaroyerotikadena" + "gaivuotnagakutechnologyeongnamegawakembuchikujobs3-ap-northeast-" + "1xn--hery-iraxn--hgebostad-g3axn--hmmrfeasta-s4achoyodontexistme" + "in-the-bandaiwafunewportlligatewayxn--hnefoss-q1axn--hobl-iraxn-" + "-holtlen-hxaxn--hpmir-xqaxn--hyanger-q1axn--hylandet-54axn--i1b6" + "b1a6a2exn--indery-fyasakaiminatottoris-very-evillagentsharis-a-p" + "ainteractivegarsheiheijis-a-llamashikiwakunigamiharustkarasjohka" + "minoyamatsuris-a-musicianxn--io0a7is-very-gooddaxn--j1amhagaxn--" + "j6w193gxn--jlster-byasugis-very-nicexn--jrpeland-54axn--karmy-yu" + "axn--kfjord-iuaxn--klbu-woaxn--koluokta-7ya57hagebostadxn--kprw1" + "3dxn--kpry57dxn--krager-gyasuokaratexn--kranghke-b0axn--krdshera" + "d-m8axn--krehamn-dxaxn--krjohka-hwab49jewelryxn--ksnes-uuaxn--kv" + "fjord-nxaxn--kvitsy-fyatominamibosojaxn--kvnangen-k0axn--l-1fare" + "astcoastaldefencexn--l1accident-investigationxn--laheadju-7yatsu" + "karatsuginamikatagamilanoxn--langevg-jxaxn--lcvr32dxn--ldingen-q" + "1axn--leagaviika-52batochigiftambovaroyomitanobanazawavoues3-fip" + "s-us-gov-west-1xn--lesund-huaxn--lgbbat1ad8jewishartromsaitokona" + "megatakatsukis-a-patsfanxn--lgrd-poachristmasakimobetsurutaharax" + "n--lhppi-xqaxn--linds-pratownxn--lns-qlarvikosaigawaxn--loabt-0q" + "axn--lrdal-sraxn--lrenskog-54axn--lt-liachtraeumtgeradefenseljej" + "uifastlyxn--lten-granvindafjordxn--lury-iraxn--mely-iraxn--merke" + "r-kuaxn--mgb2ddeshirakoenigxn--mgb9awbfermochizukiryuoharussiaxn" + "--mgba3a4f16axn--mgba3a4franapleshiranukaniepcexn--mgbaam7a8haib" + "arakitahiroshimarumorimachidaxn--mgbab2bdxn--mgbayh7gpaduaxn--mg" + "bbh1a71exn--mgbc0a9azcgxn--mgberp4a5d4a87gxn--mgberp4a5d4arxn--m" + "gbqly7c0a67fbchungbukaufenrwritesthisblogspotgoryxn--mgbqly7cvaf" + "ranziskanerimaniwakuratexn--mgbtf8flandershiraois-into-animeetre" + "exn--mgbx4cd0abatsfjordnepropetrovskaruizawaxaustinnarvikariyalt" + "aijibestadirectoryokamikawanehonbetsurugildeskalmykiamusementakk" + "ofuefukihabikinokawaircraftamayufuettertdasnetz-1xn--mjndalen-64" + "axn--mk0axis-very-sweetrogstadxn--mlatvuopmi-s4axn--mli-tlavagis" + "kexn--mlselv-iuaxn--moreke-juaxn--mosjen-eyatsushiroxn--mot-tlav" + "angenxn--mre-og-romsdal-qqbeneventochiokinoshimamurogawashington" + "dcareers3-us-west-2xn--msy-ula0hakatanotteroyxn--mtta-vrjjat-k7a" + "ferraraxn--muost-0qaxn--mxtq1misconfusedxn--ngbc5azdxn--nmesjevu" + "emie-tcbajddarchaeologyxn--nnx388axn--nodessakegawaxn--nqv7fs00e" + "maxn--nry-yla5gxn--nttery-byaesembokumamotoyamatsumotofukexn--nv" + "uotna-hwaxn--o3cw4hakodatexn--od0algxn--od0aq3beppubolognagasuke" + "trzynaturalsciencesnaturelles3-website-ap-northeast-1xn--ogbpf8f" + "latangerxn--oppegrd-ixaxn--ostery-fyawaraxn--osyro-wuaxn--p1acfe" + "tsundxn--p1ais-with-thebandoxn--pgbs0dhakonexn--porsgu-sta26fguj" + "olsterxn--q9jyb4chungnamdalseidfjordyndns-webetsukubahcavuotnaga" + "raxn--rady-iraxn--rdal-poaxn--rde-ulaxn--rdy-0nabarisleofmanches" + "terxn--rennesy-v1axn--rhkkervju-01afhskchristiansburgulenxn--rho" + "lt-mragoworse-thandsondrioxn--rhqv96gxn--risa-5narashinoxn--risr" + "-iraxn--rland-uuaxn--rlingen-mxaxn--rmskog-byawatahamaxn--rros-g" + "ratangenxn--rskog-uuaxn--rst-0naritakurashikisshikiyokawaraxn--r" + "sta-francaiseharaxn--ryken-vuaxn--ryrvik-byaxn--s-1farmequipment" + "xn--s9brj9chuvashiaxn--sandnessjen-ogberlincolnaturbruksgymnatur" + "historisches3-website-ap-southeast-1xn--sandy-yuaxn--seral-lraxn" + "--ses554gxn--sgne-grazxn--skierv-utazasnesoddenmarketplacertific" + "ationxn--skjervy-v1axn--skjk-soaxn--sknit-yqaxn--sknland-fxaxn--" + "slat-5naroyxn--slt-elabourxn--smla-hraxn--smna-graxn--snase-nrax" + "n--sndre-land-0cbgxn--snes-poaxn--snsa-roaxn--sr-aurdal-l8axn--s" + "r-fron-q1axn--sr-odal-q1axn--sr-varanger-ggbernrtarantokyotangob" + "ihirosakikamijimaxn--srfold-byaxn--srreisa-q1axn--srum-graxn--st" + "fold-9xaxn--stjrdal-s1axn--stjrdalshalsen-sqbeskidyn-o-saurlande" + "s3-website-ap-southeast-2xn--stre-toten-zcbetainaboxfordeatnuoro" + "ckartuzyonabarudmurtiaxn--tjme-hraxn--tn0agrigentomologyeonggieh" + "tavuoatnaamesjevuemielnoboribetsuitachikawakayamagadancechirebun" + "goonomichinomiyakeisenbahnxn--tnsberg-q1axn--trany-yuaxn--trgsta" + "d-r1axn--trna-woaxn--troms-zuaxn--tysvr-vraxn--uc0atvedestrandxn" + "--uc0ay4axn--unjrga-rtakizawaxn--unup4yxn--vads-jraxn--vard-jrax" + "n--vegrshei-c0axn--vestvgy-ixa6oxn--vg-yiabielawassamukawatariku" + "zentakatairaxn--vgan-qoaxn--vgsy-qoa0jfkomaganexn--vhquvestfoldx" + "n--vler-qoaxn--vre-eiker-k8axn--vrggt-xqadxn--vry-yla5gxn--wcvs2" + "2dxn--wgbh1cimperiaxn--wgbl6axn--xhq521biellaakesvuemieleccehime" + "jibigawaxn--xkc2al3hye2axn--xkc2dl3a5ee0hakubanpachigasakidsavon" + "akayamaxn--yer-znarusawaxn--yfro4i67oxn--ygarden-p1axn--ygbi2amm" + "xn--55qx5dxn--ystre-slidre-ujbieszczadygeyachiyodavvenjargalsaba" + "erobaticargodolls3-website-eu-west-1xn--zf0ao64axn--zf0avxn--6fr" + "z82gxn--zfr164bievat-band-campobassobetsuldalimomasvuotnakanotod" + "denatuurwetenschappenaumburgjovikarumaintenancembroideryonagoyax" + "xxn--6qq986b3xlxz" // nodes is the list of nodes. Each node is represented as a uint32, which // encodes the node's children, wildcard bit and node type (as an index into // the children array), ICANN bit and text. // // In the //-comment after each node's data, the nodes indexes of the children // are formatted as (n0x1234-n0x1256), with * denoting the wildcard bit. The // nodeType is printed as + for normal, ! for exception, and o for parent-only // nodes that have children but don't match a domain label in their own right. // An I denotes an ICANN domain. // // The layout within the uint32, from MSB to LSB, is: // [ 1 bits] unused // [ 9 bits] children index // [ 1 bits] ICANN bit // [15 bits] text index // [ 6 bits] text length var nodes = [...]uint32{ 0x01a0a3c2, // n0x0000 c0x0006 (n0x0230-n0x0236) + I ac 0x002f1d87, // n0x0001 c0x0000 (---------------) + I academy 0x0023b245, // n0x0002 c0x0000 (---------------) + I actor 0x01e1e242, // n0x0003 c0x0007 (n0x0236-n0x0237) + I ad 0x02204c02, // n0x0004 c0x0008 (n0x0237-n0x023e) + I ae 0x026728c4, // n0x0005 c0x0009 (n0x023e-n0x0297) + I aero 0x02a14942, // n0x0006 c0x000a (n0x0297-n0x029c) + I af 0x02e00142, // n0x0007 c0x000b (n0x029c-n0x02a1) + I ag 0x0023df86, // n0x0008 c0x0000 (---------------) + I agency 0x03203e42, // n0x0009 c0x000c (n0x02a1-n0x02a5) + I ai 0x03601782, // n0x000a c0x000d (n0x02a5-n0x02ab) + I al 0x00200702, // n0x000b c0x0000 (---------------) + I am 0x03a00382, // n0x000c c0x000e (n0x02ab-n0x02af) + I an 0x03e00642, // n0x000d c0x000f (n0x02af-n0x02b5) + I ao 0x00275382, // n0x000e c0x0000 (---------------) + I aq 0x04202b82, // n0x000f c0x0010 (n0x02b5-n0x02bd) + I ar 0x04aab184, // n0x0010 c0x0012 (n0x02be-n0x02c4) + I arpa 0x04e005c2, // n0x0011 c0x0013 (n0x02c4-n0x02c5) + I as 0x0023dd04, // n0x0012 c0x0000 (---------------) + I asia 0x05200242, // n0x0013 c0x0014 (n0x02c5-n0x02cc) + I at 0x05a01642, // n0x0014 c0x0016 (n0x02cd-n0x02e0) + I au 0x00306ac5, // n0x0015 c0x0000 (---------------) + I autos 0x06a001c2, // n0x0016 c0x001a (n0x02ef-n0x02f0) + I aw 0x00265602, // n0x0017 c0x0000 (---------------) + I ax 0x0032fac3, // n0x0018 c0x0000 (---------------) + I axa 0x06e03dc2, // n0x0019 c0x001b (n0x02f0-n0x02fc) + I az 0x07201b02, // n0x001a c0x001c (n0x02fc-n0x0306) + I ba 0x00236f03, // n0x001b c0x0000 (---------------) + I bar 0x002f4988, // n0x001c c0x0000 (---------------) + I bargains 0x07643682, // n0x001d c0x001d (n0x0306-n0x030e) + I bb 0x016f6342, // n0x001e c0x0005 (---------------)* o I bd 0x07a02902, // n0x001f c0x001e (n0x030e-n0x0310) + I be 0x0021c384, // n0x0020 c0x0000 (---------------) + I beer 0x00344946, // n0x0021 c0x0000 (---------------) + I berlin 0x00330104, // n0x0022 c0x0000 (---------------) + I best 0x07f29942, // n0x0023 c0x001f (n0x0310-n0x0311) + I bf 0x08349402, // n0x0024 c0x0020 (n0x0311-n0x0335) + I bg 0x0863ec02, // n0x0025 c0x0021 (n0x0335-n0x033a) + I bh 0x08a00002, // n0x0026 c0x0022 (n0x033a-n0x033f) + I bi 0x002f7883, // n0x0027 c0x0000 (---------------) + I bid 0x00203a84, // n0x0028 c0x0000 (---------------) + I bike 0x08f05743, // n0x0029 c0x0023 (n0x033f-n0x0346) + I biz 0x0920c082, // n0x002a c0x0024 (n0x0346-n0x034a) + I bj 0x00277b45, // n0x002b c0x0000 (---------------) + I black 0x00277b4b, // n0x002c c0x0000 (---------------) + I blackfriday 0x0020f5c4, // n0x002d c0x0000 (---------------) + I blue 0x0960f842, // n0x002e c0x0025 (n0x034a-n0x034f) + I bm 0x016566c2, // n0x002f c0x0005 (---------------)* o I bn 0x09a120c2, // n0x0030 c0x0026 (n0x034f-n0x0358) + I bo 0x00215688, // n0x0031 c0x0000 (---------------) + I boutique 0x09e02282, // n0x0032 c0x0027 (n0x0358-n0x039d) + I br 0x0a600b82, // n0x0033 c0x0029 (n0x039e-n0x03a3) + I bs 0x0aa27382, // n0x0034 c0x002a (n0x03a3-n0x03a8) + I bt 0x00249148, // n0x0035 c0x0000 (---------------) + I budapest 0x0022f445, // n0x0036 c0x0000 (---------------) + I build 0x002cf5c8, // n0x0037 c0x0000 (---------------) + I builders 0x002d4648, // n0x0038 c0x0000 (---------------) + I business 0x0022b644, // n0x0039 c0x0000 (---------------) + I buzz 0x0022c0c2, // n0x003a c0x0000 (---------------) + I bv 0x0ae2c782, // n0x003b c0x002b (n0x03a8-n0x03aa) + I bw 0x0b20ed42, // n0x003c c0x002c (n0x03aa-n0x03ae) + I by 0x0b62d182, // n0x003d c0x002d (n0x03ae-n0x03b3) + I bz 0x0ba13182, // n0x003e c0x002e (n0x03b3-n0x03c4) + I ca 0x00295943, // n0x003f c0x0000 (---------------) + I cab 0x002a2f06, // n0x0040 c0x0000 (---------------) + I camera 0x0024da84, // n0x0041 c0x0000 (---------------) + I camp 0x002ce6c7, // n0x0042 c0x0000 (---------------) + I caravan 0x002eb3c5, // n0x0043 c0x0000 (---------------) + I cards 0x00335046, // n0x0044 c0x0000 (---------------) + I career 0x00335047, // n0x0045 c0x0000 (---------------) + I careers 0x0021f3c4, // n0x0046 c0x0000 (---------------) + I casa 0x00215c43, // n0x0047 c0x0000 (---------------) + I cat 0x002cf388, // n0x0048 c0x0000 (---------------) + I catering 0x0be23542, // n0x0049 c0x002f (n0x03c4-n0x03c8) + I cc 0x0c2fdf42, // n0x004a c0x0030 (n0x03c8-n0x03c9) + I cd 0x002456c6, // n0x004b c0x0000 (---------------) + I center 0x00213c03, // n0x004c c0x0000 (---------------) + I ceo 0x0c73bf42, // n0x004d c0x0031 (n0x03c9-n0x03ca) + I cf 0x00214f02, // n0x004e c0x0000 (---------------) + I cg 0x0ca002c2, // n0x004f c0x0032 (n0x03ca-n0x03cb) + I ch 0x0025fd05, // n0x0050 c0x0000 (---------------) + I cheap 0x00325909, // n0x0051 c0x0000 (---------------) + I christmas 0x0ce1b7c2, // n0x0052 c0x0033 (n0x03cb-n0x03da) + I ci 0x0d200542, // n0x0053 c0x0034 (n0x03da-n0x03db)* o I ck 0x0d600482, // n0x0054 c0x0035 (n0x03db-n0x03df) + I cl 0x0022fdc8, // n0x0055 c0x0000 (---------------) + I cleaning 0x00231308, // n0x0056 c0x0000 (---------------) + I clothing 0x002729c4, // n0x0057 c0x0000 (---------------) + I club 0x0db126c2, // n0x0058 c0x0036 (n0x03df-n0x03e0) + I cm 0x0de34a02, // n0x0059 c0x0037 (n0x03e0-n0x040c) + I cn 0x0e209382, // n0x005a c0x0038 (n0x040c-n0x0419) + I co 0x0030a285, // n0x005b c0x0000 (---------------) + I codes 0x00256d06, // n0x005c c0x0000 (---------------) + I coffee 0x00234dc7, // n0x005d c0x0000 (---------------) + I college 0x0e6370c3, // n0x005e c0x0039 (n0x0419-n0x04d3) + I com 0x002b7e49, // n0x005f c0x0000 (---------------) + I community 0x002372c7, // n0x0060 c0x0000 (---------------) + I company 0x002376c8, // n0x0061 c0x0000 (---------------) + I computer 0x00237ec6, // n0x0062 c0x0000 (---------------) + I condos 0x0023894c, // n0x0063 c0x0000 (---------------) + I construction 0x0023a04a, // n0x0064 c0x0000 (---------------) + I consulting 0x0023b10b, // n0x0065 c0x0000 (---------------) + I contractors 0x0023c287, // n0x0066 c0x0000 (---------------) + I cooking 0x0023d004, // n0x0067 c0x0000 (---------------) + I cool 0x0023d9c4, // n0x0068 c0x0000 (---------------) + I coop 0x002a3247, // n0x0069 c0x0000 (---------------) + I country 0x0f6148c2, // n0x006a c0x003d (n0x04f4-n0x04fb) + I cr 0x00244e47, // n0x006b c0x0000 (---------------) + I cruises 0x0fa2d482, // n0x006c c0x003e (n0x04fb-n0x0501) + I cu 0x0ff22b42, // n0x006d c0x003f (n0x0501-n0x0502) + I cv 0x103393c2, // n0x006e c0x0040 (n0x0502-n0x0506) + I cw 0x10645f02, // n0x006f c0x0041 (n0x0506-n0x0508) + I cx 0x0160a602, // n0x0070 c0x0005 (---------------)* o I cy 0x10a25882, // n0x0071 c0x0042 (n0x0508-n0x0509) + I cz 0x0034fcc5, // n0x0072 c0x0000 (---------------) + I dance 0x00216706, // n0x0073 c0x0000 (---------------) + I dating 0x10e03442, // n0x0074 c0x0043 (n0x0509-n0x0511) + I de 0x00242a88, // n0x0075 c0x0000 (---------------) + I democrat 0x00251c84, // n0x0076 c0x0000 (---------------) + I desi 0x00216108, // n0x0077 c0x0000 (---------------) + I diamonds 0x00330249, // n0x0078 c0x0000 (---------------) + I directory 0x0022bd82, // n0x0079 c0x0000 (---------------) + I dj 0x112361c2, // n0x007a c0x0044 (n0x0511-n0x0512) + I dk 0x1162c882, // n0x007b c0x0045 (n0x0512-n0x0517) + I dm 0x0020b383, // n0x007c c0x0000 (---------------) + I dnp 0x11a1bdc2, // n0x007d c0x0046 (n0x0517-n0x0521) + I do 0x002a0207, // n0x007e c0x0000 (---------------) + I domains 0x11e28582, // n0x007f c0x0047 (n0x0521-n0x0529) + I dz 0x12209342, // n0x0080 c0x0048 (n0x0529-n0x0535) + I ec 0x00215b83, // n0x0081 c0x0000 (---------------) + I edu 0x00215b89, // n0x0082 c0x0000 (---------------) + I education 0x1260b0c2, // n0x0083 c0x0049 (n0x0535-n0x053f) + I ee 0x12a04f02, // n0x0084 c0x004a (n0x053f-n0x0548) + I eg 0x0029e745, // n0x0085 c0x0000 (---------------) + I email 0x002ea24b, // n0x0086 c0x0000 (---------------) + I enterprises 0x00343dc9, // n0x0087 c0x0000 (---------------) + I equipment 0x016009c2, // n0x0088 c0x0005 (---------------)* o I er 0x12e03482, // n0x0089 c0x004b (n0x0548-n0x054d) + I es 0x002a3406, // n0x008a c0x0000 (---------------) + I estate 0x01602942, // n0x008b c0x0005 (---------------)* o I et 0x00220842, // n0x008c c0x0000 (---------------) + I eu 0x002be043, // n0x008d c0x0000 (---------------) + I eus 0x00211346, // n0x008e c0x0000 (---------------) + I events 0x00300806, // n0x008f c0x0000 (---------------) + I expert 0x00222387, // n0x0090 c0x0000 (---------------) + I exposed 0x00224844, // n0x0091 c0x0000 (---------------) + I farm 0x00256dc8, // n0x0092 c0x0000 (---------------) + I feedback 0x1360cbc2, // n0x0093 c0x004d (n0x054e-n0x0551) + I fi 0x00248944, // n0x0094 c0x0000 (---------------) + I fish 0x00248947, // n0x0095 c0x0000 (---------------) + I fishing 0x01649842, // n0x0096 c0x0005 (---------------)* o I fj 0x01754982, // n0x0097 c0x0005 (---------------)* o I fk 0x00249fc7, // n0x0098 c0x0000 (---------------) + I flights 0x0024c4c7, // n0x0099 c0x0000 (---------------) + I florist 0x0033ee02, // n0x009a c0x0000 (---------------) + I fm 0x0020ee82, // n0x009b c0x0000 (---------------) + I fo 0x002ca74a, // n0x009c c0x0000 (---------------) + I foundation 0x13a21802, // n0x009d c0x004e (n0x0551-n0x0569) + I fr 0x00254107, // n0x009e c0x0000 (---------------) + I frogans 0x00276886, // n0x009f c0x0000 (---------------) + I futbol 0x00200182, // n0x00a0 c0x0000 (---------------) + I ga 0x0023a8c3, // n0x00a1 c0x0000 (---------------) + I gal 0x0023a8c7, // n0x00a2 c0x0000 (---------------) + I gallery 0x0027d6c2, // n0x00a3 c0x0000 (---------------) + I gb 0x002045c2, // n0x00a4 c0x0000 (---------------) + I gd 0x13e03202, // n0x00a5 c0x004f (n0x0569-n0x0570) + I ge 0x0022c3c2, // n0x00a6 c0x0000 (---------------) + I gf 0x1420e982, // n0x00a7 c0x0050 (n0x0570-n0x0573) + I gg 0x0023aa84, // n0x00a8 c0x0000 (---------------) + I ggee 0x14635f42, // n0x00a9 c0x0051 (n0x0573-n0x0578) + I gh 0x14a02c02, // n0x00aa c0x0052 (n0x0578-n0x057e) + I gi 0x00323684, // n0x00ab c0x0000 (---------------) + I gift 0x002064c2, // n0x00ac c0x0000 (---------------) + I gl 0x0020c785, // n0x00ad c0x0000 (---------------) + I glass 0x00216845, // n0x00ae c0x0000 (---------------) + I globo 0x00228882, // n0x00af c0x0000 (---------------) + I gm 0x14e0eb42, // n0x00b0 c0x0053 (n0x057e-n0x0584) + I gn 0x00248ac3, // n0x00b1 c0x0000 (---------------) + I gop 0x00208ec3, // n0x00b2 c0x0000 (---------------) + I gov 0x152c8502, // n0x00b3 c0x0054 (n0x0584-n0x058a) + I gp 0x00260082, // n0x00b4 c0x0000 (---------------) + I gq 0x15608982, // n0x00b5 c0x0055 (n0x058a-n0x0590) + I gr 0x00208988, // n0x00b6 c0x0000 (---------------) + I graphics 0x002210c2, // n0x00b7 c0x0000 (---------------) + I gs 0x15b34f02, // n0x00b8 c0x0056 (n0x0590-n0x0597) + I gt 0x01601282, // n0x00b9 c0x0005 (---------------)* o I gu 0x0022b505, // n0x00ba c0x0000 (---------------) + I guide 0x002ccd47, // n0x00bb c0x0000 (---------------) + I guitars 0x00266384, // n0x00bc c0x0000 (---------------) + I guru 0x00228c42, // n0x00bd c0x0000 (---------------) + I gw 0x15e01142, // n0x00be c0x0057 (n0x0597-n0x059a) + I gy 0x00280fc4, // n0x00bf c0x0000 (---------------) + I haus 0x16235f82, // n0x00c0 c0x0058 (n0x059a-n0x05b0) + I hk 0x0027c582, // n0x00c1 c0x0000 (---------------) + I hm 0x16624f42, // n0x00c2 c0x0059 (n0x05b0-n0x05b6) + I hn 0x00292d88, // n0x00c3 c0x0000 (---------------) + I holdings 0x002937c7, // n0x00c4 c0x0000 (---------------) + I holiday 0x00297085, // n0x00c5 c0x0000 (---------------) + I homes 0x00299785, // n0x00c6 c0x0000 (---------------) + I horse 0x0022af05, // n0x00c7 c0x0000 (---------------) + I house 0x16a39e02, // n0x00c8 c0x005a (n0x05b6-n0x05ba) + I hr 0x16e13cc2, // n0x00c9 c0x005b (n0x05ba-n0x05cb) + I ht 0x17203f82, // n0x00ca c0x005c (n0x05cb-n0x05eb) + I hu 0x17603d42, // n0x00cb c0x005d (n0x05eb-n0x05f5) + I id 0x17a04d02, // n0x00cc c0x005e (n0x05f5-n0x05f7) + I ie 0x17e05bc2, // n0x00cd c0x005f (n0x05f7-n0x05f8)* o I il 0x186008c2, // n0x00ce c0x0061 (n0x05f9-n0x0600) + I im 0x0020d44a, // n0x00cf c0x0000 (---------------) + I immobilien 0x18e00942, // n0x00d0 c0x0063 (n0x0602-n0x060f) + I in 0x0021358a, // n0x00d1 c0x0000 (---------------) + I industries 0x19214b84, // n0x00d2 c0x0064 (n0x060f-n0x0619) + I info 0x00207e43, // n0x00d3 c0x0000 (---------------) + I ink 0x002f4ac9, // n0x00d4 c0x0000 (---------------) + I institute 0x19616fc3, // n0x00d5 c0x0065 (n0x0619-n0x061a) + I int 0x00218c8d, // n0x00d6 c0x0000 (---------------) + I international 0x19a00782, // n0x00d7 c0x0066 (n0x061a-n0x061c) + I io 0x19e0b7c2, // n0x00d8 c0x0067 (n0x061c-n0x0622) + I iq 0x1a204442, // n0x00d9 c0x0068 (n0x0622-n0x062b) + I ir 0x1a6030c2, // n0x00da c0x0069 (n0x062b-n0x0632) + I is 0x1aa00c02, // n0x00db c0x006a (n0x0632-n0x0748) + I it 0x1ae0d2c2, // n0x00dc c0x006b (n0x0748-n0x074b) + I je 0x00245f85, // n0x00dd c0x0000 (---------------) + I jetzt 0x0174e6c2, // n0x00de c0x0005 (---------------)* o I jm 0x1b20df42, // n0x00df c0x006c (n0x074b-n0x0753) + I jo 0x00315b44, // n0x00e0 c0x0000 (---------------) + I jobs 0x1b69e1c2, // n0x00e1 c0x006d (n0x0753-n0x0793) + I jp 0x0032d506, // n0x00e2 c0x0000 (---------------) + I kaufen 0x01603b02, // n0x00e3 c0x0005 (---------------)* o I ke 0x2924a402, // n0x00e4 c0x00a4 (n0x0e29-n0x0e2f) + I kg 0x0162ccc2, // n0x00e5 c0x0005 (---------------)* o I kh 0x29601942, // n0x00e6 c0x00a5 (n0x0e2f-n0x0e36) + I ki 0x00201943, // n0x00e7 c0x0000 (---------------) + I kim 0x0021a407, // n0x00e8 c0x0000 (---------------) + I kitchen 0x002d9d04, // n0x00e9 c0x0000 (---------------) + I kiwi 0x29ade142, // n0x00ea c0x00a6 (n0x0e36-n0x0e47) + I km 0x29e16982, // n0x00eb c0x00a7 (n0x0e47-n0x0e4b) + I kn 0x2a220e42, // n0x00ec c0x00a8 (n0x0e4b-n0x0e51) + I kp 0x2a60d382, // n0x00ed c0x00a9 (n0x0e51-n0x0e6f) + I kr 0x0031ee03, // n0x00ee c0x0000 (---------------) + I krd 0x002a4544, // n0x00ef c0x0000 (---------------) + I kred 0x016b6042, // n0x00f0 c0x0005 (---------------)* o I kw 0x2aa07fc2, // n0x00f1 c0x00aa (n0x0e6f-n0x0e74) + I ky 0x2ae09502, // n0x00f2 c0x00ab (n0x0e74-n0x0e7a) + I kz 0x2b2026c2, // n0x00f3 c0x00ac (n0x0e7a-n0x0e83) + I la 0x0030d787, // n0x00f4 c0x0000 (---------------) + I lacaixa 0x00206504, // n0x00f5 c0x0000 (---------------) + I land 0x2b602242, // n0x00f6 c0x00ad (n0x0e83-n0x0e88) + I lb 0x2ba15e02, // n0x00f7 c0x00ae (n0x0e88-n0x0e8e) + I lc 0x002017c2, // n0x00f8 c0x0000 (---------------) + I li 0x00305c88, // n0x00f9 c0x0000 (---------------) + I lighting 0x0035bb04, // n0x00fa c0x0000 (---------------) + I limo 0x00209444, // n0x00fb c0x0000 (---------------) + I link 0x2be20e02, // n0x00fc c0x00af (n0x0e8e-n0x0e9c) + I lk 0x002b9b86, // n0x00fd c0x0000 (---------------) + I london 0x2c2771c2, // n0x00fe c0x00b0 (n0x0e9c-n0x0ea1) + I lr 0x2c6058c2, // n0x00ff c0x00b1 (n0x0ea1-n0x0ea3) + I ls 0x2ca1e302, // n0x0100 c0x00b2 (n0x0ea3-n0x0ea4) + I lt 0x002082c2, // n0x0101 c0x0000 (---------------) + I lu 0x002332c4, // n0x0102 c0x0000 (---------------) + I luxe 0x0023a386, // n0x0103 c0x0000 (---------------) + I luxury 0x2ce05742, // n0x0104 c0x00b3 (n0x0ea4-n0x0ead) + I lv 0x2d20e402, // n0x0105 c0x00b4 (n0x0ead-n0x0eb6) + I ly 0x2d604242, // n0x0106 c0x00b5 (n0x0eb6-n0x0ebc) + I ma 0x002494c6, // n0x0107 c0x0000 (---------------) + I maison 0x0028950a, // n0x0108 c0x0000 (---------------) + I management 0x002ac945, // n0x0109 c0x0000 (---------------) + I mango 0x00210fc9, // n0x010a c0x0000 (---------------) + I marketing 0x2da61e42, // n0x010b c0x00b6 (n0x0ebc-n0x0ebe) + I mc 0x0020f882, // n0x010c c0x0000 (---------------) + I md 0x2de01582, // n0x010d c0x00b7 (n0x0ebe-n0x0ec6) + I me 0x0032ee84, // n0x010e c0x0000 (---------------) + I meet 0x00262a84, // n0x010f c0x0000 (---------------) + I menu 0x2e329242, // n0x0110 c0x00b8 (n0x0ec6-n0x0ece) + I mg 0x002e87c2, // n0x0111 c0x0000 (---------------) + I mh 0x0022f305, // n0x0112 c0x0000 (---------------) + I miami 0x00210703, // n0x0113 c0x0000 (---------------) + I mil 0x0026ea44, // n0x0114 c0x0000 (---------------) + I mini 0x2e732142, // n0x0115 c0x00b9 (n0x0ece-n0x0ed5) + I mk 0x2ea12142, // n0x0116 c0x00ba (n0x0ed5-n0x0edc) + I ml 0x0160d482, // n0x0117 c0x0005 (---------------)* o I mm 0x2ee0fb42, // n0x0118 c0x00bb (n0x0edc-n0x0ee0) + I mn 0x2f202e02, // n0x0119 c0x00bc (n0x0ee0-n0x0ee5) + I mo 0x0020d4c4, // n0x011a c0x0000 (---------------) + I mobi 0x0025f3c4, // n0x011b c0x0000 (---------------) + I moda 0x00291b43, // n0x011c c0x0000 (---------------) + I moe 0x00243946, // n0x011d c0x0000 (---------------) + I monash 0x002bd8c6, // n0x011e c0x0000 (---------------) + I mormon 0x002bde06, // n0x011f c0x0000 (---------------) + I moscow 0x0028a44b, // n0x0120 c0x0000 (---------------) + I motorcycles 0x00202442, // n0x0121 c0x0000 (---------------) + I mp 0x0030da82, // n0x0122 c0x0000 (---------------) + I mq 0x2f6c5802, // n0x0123 c0x00bd (n0x0ee5-n0x0ee7) + I mr 0x0020bb42, // n0x0124 c0x0000 (---------------) + I ms 0x2fa5f982, // n0x0125 c0x00be (n0x0ee7-n0x0eeb) + I mt 0x2fe15242, // n0x0126 c0x00bf (n0x0eeb-n0x0ef2) + I mu 0x302c3946, // n0x0127 c0x00c0 (n0x0ef2-n0x1116) + I museum 0x3061c6c2, // n0x0128 c0x00c1 (n0x1116-n0x1124) + I mv 0x30b12702, // n0x0129 c0x00c2 (n0x1124-n0x112f) + I mw 0x30f36602, // n0x012a c0x00c3 (n0x112f-n0x1135) + I mx 0x312267c2, // n0x012b c0x00c4 (n0x1135-n0x113c) + I my 0x316ed302, // n0x012c c0x00c5 (n0x113c-n0x113d)* o I mz 0x31a01202, // n0x012d c0x00c6 (n0x113d-n0x114e) + I na 0x0035ce46, // n0x012e c0x0000 (---------------) + I nagoya 0x31e592c4, // n0x012f c0x00c7 (n0x114e-n0x1150) + I name 0x32a0a5c2, // n0x0130 c0x00ca (n0x1152-n0x1153) + I nc 0x00200982, // n0x0131 c0x0000 (---------------) + I ne 0x32e4bdc3, // n0x0132 c0x00cb (n0x1153-n0x1181) + I net 0x00314587, // n0x0133 c0x0000 (---------------) + I network 0x002e5ac7, // n0x0134 c0x0000 (---------------) + I neustar 0x33e14bc2, // n0x0135 c0x00cf (n0x1188-n0x1192) + I nf 0x34204982, // n0x0136 c0x00d0 (n0x1192-n0x119b) + I ng 0x01601302, // n0x0137 c0x0005 (---------------)* o I ni 0x002e1a05, // n0x0138 c0x0000 (---------------) + I ninja 0x34636642, // n0x0139 c0x00d1 (n0x119b-n0x119e) + I nl 0x34a01e82, // n0x013a c0x00d2 (n0x119e-n0x1474) + I no 0x0160b3c2, // n0x013b c0x0005 (---------------)* o I np 0x3ce21a02, // n0x013c c0x00f3 (n0x149c-n0x14a3) + I nr 0x0032d643, // n0x013d c0x0000 (---------------) + I nrw 0x3d205642, // n0x013e c0x00f4 (n0x14a3-n0x14a6) + I nu 0x3d60adc2, // n0x013f c0x00f5 (n0x14a6-n0x14a7)* o I nz 0x00226d47, // n0x0140 c0x0000 (---------------) + I okinawa 0x3de02882, // n0x0141 c0x00f7 (n0x14a8-n0x14b1) + I om 0x002e59c3, // n0x0142 c0x0000 (---------------) + I onl 0x3e24af03, // n0x0143 c0x00f8 (n0x14b1-n0x14e6) + I org 0x00259f06, // n0x0144 c0x0000 (---------------) + I otsuka 0x0025d343, // n0x0145 c0x0000 (---------------) + I ovh 0x3ea02642, // n0x0146 c0x00fa (n0x14e8-n0x14f3) + I pa 0x002d7788, // n0x0147 c0x0000 (---------------) + I partners 0x002fb385, // n0x0148 c0x0000 (---------------) + I parts 0x3ee06742, // n0x0149 c0x00fb (n0x14f3-n0x14fa) + I pe 0x3f33af02, // n0x014a c0x00fc (n0x14fa-n0x14fd) + I pf 0x01653b02, // n0x014b c0x0005 (---------------)* o I pg 0x3f608a42, // n0x014c c0x00fd (n0x14fd-n0x1505) + I ph 0x002c4005, // n0x014d c0x0000 (---------------) + I photo 0x002c710b, // n0x014e c0x0000 (---------------) + I photography 0x002c4006, // n0x014f c0x0000 (---------------) + I photos 0x00258c44, // n0x0150 c0x0000 (---------------) + I pics 0x002c7f84, // n0x0151 c0x0000 (---------------) + I pink 0x3fac9282, // n0x0152 c0x00fe (n0x1505-n0x1513) + I pk 0x3fe0a342, // n0x0153 c0x00ff (n0x1513-n0x15be) + I pl 0x002cb3c8, // n0x0154 c0x0000 (---------------) + I plumbing 0x0027c342, // n0x0155 c0x0000 (---------------) + I pm 0x4069e202, // n0x0156 c0x0101 (n0x15c7-n0x15cc) + I pn 0x002cd404, // n0x0157 c0x0000 (---------------) + I post 0x40a06682, // n0x0158 c0x0102 (n0x15cc-n0x15d9) + I pr 0x00265585, // n0x0159 c0x0000 (---------------) + I praxi 0x40e06683, // n0x015a c0x0103 (n0x15d9-n0x15e0) + I pro 0x002ced4b, // n0x015b c0x0000 (---------------) + I productions 0x002d054a, // n0x015c c0x0000 (---------------) + I properties 0x4120b9c2, // n0x015d c0x0104 (n0x15e0-n0x15e7) + I ps 0x416b3242, // n0x015e c0x0105 (n0x15e7-n0x15f0) + I pt 0x0029f103, // n0x015f c0x0000 (---------------) + I pub 0x41ad1cc2, // n0x0160 c0x0106 (n0x15f0-n0x15f6) + I pw 0x41ed00c2, // n0x0161 c0x0107 (n0x15f6-n0x15fd) + I py 0x422f8002, // n0x0162 c0x0108 (n0x15fd-n0x1605) + I qa 0x002d1dc4, // n0x0163 c0x0000 (---------------) + I qpon 0x002157c6, // n0x0164 c0x0000 (---------------) + I quebec 0x42605482, // n0x0165 c0x0109 (n0x1605-n0x1609) + I re 0x002b3507, // n0x0166 c0x0000 (---------------) + I recipes 0x00252643, // n0x0167 c0x0000 (---------------) + I red 0x00238803, // n0x0168 c0x0000 (---------------) + I ren 0x002df887, // n0x0169 c0x0000 (---------------) + I rentals 0x00265a86, // n0x016a c0x0000 (---------------) + I repair 0x0027bb46, // n0x016b c0x0000 (---------------) + I report 0x00246d04, // n0x016c c0x0000 (---------------) + I rest 0x002a8cc7, // n0x016d c0x0000 (---------------) + I reviews 0x0028fdc4, // n0x016e c0x0000 (---------------) + I rich 0x42a01042, // n0x016f c0x010a (n0x1609-n0x1615) + I ro 0x002a0b45, // n0x0170 c0x0000 (---------------) + I rocks 0x002a5bc5, // n0x0171 c0x0000 (---------------) + I rodeo 0x42e00a02, // n0x0172 c0x010b (n0x1615-n0x161b) + I rs 0x4320aac2, // n0x0173 c0x010c (n0x161b-n0x16a0) + I ru 0x002a3c44, // n0x0174 c0x0000 (---------------) + I ruhr 0x43700082, // n0x0175 c0x010d (n0x16a0-n0x16a9) + I rw 0x002ac006, // n0x0176 c0x0000 (---------------) + I ryukyu 0x43a00602, // n0x0177 c0x010e (n0x16a9-n0x16b1) + I sa 0x002b0308, // n0x0178 c0x0000 (---------------) + I saarland 0x43e35482, // n0x0179 c0x010f (n0x16b1-n0x16b6) + I sb 0x44219202, // n0x017a c0x0110 (n0x16b6-n0x16bb) + I sc 0x4463c8c2, // n0x017b c0x0111 (n0x16bb-n0x16c3) + I sd 0x44a04802, // n0x017c c0x0112 (n0x16c3-n0x16ec) + I se 0x00223244, // n0x017d c0x0000 (---------------) + I sexy 0x44e7d8c2, // n0x017e c0x0113 (n0x16ec-n0x16f3) + I sg 0x45202582, // n0x017f c0x0114 (n0x16f3-n0x16f8) + I sh 0x0024bb87, // n0x0180 c0x0000 (---------------) + I shiksha 0x00231085, // n0x0181 c0x0000 (---------------) + I shoes 0x00200bc2, // n0x0182 c0x0000 (---------------) + I si 0x0021fa47, // n0x0183 c0x0000 (---------------) + I singles 0x00225482, // n0x0184 c0x0000 (---------------) + I sj 0x45601902, // n0x0185 c0x0115 (n0x16f8-n0x16f9) + I sk 0x45a0c382, // n0x0186 c0x0116 (n0x16f9-n0x16fe) + I sl 0x00210082, // n0x0187 c0x0000 (---------------) + I sm 0x45e034c2, // n0x0188 c0x0117 (n0x16fe-n0x1705) + I sn 0x46205902, // n0x0189 c0x0118 (n0x1705-n0x1708) + I so 0x002a22c6, // n0x018a c0x0000 (---------------) + I social 0x002ab604, // n0x018b c0x0000 (---------------) + I sohu 0x002bcd05, // n0x018c c0x0000 (---------------) + I solar 0x002db409, // n0x018d c0x0000 (---------------) + I solutions 0x002be902, // n0x018e c0x0000 (---------------) + I sr 0x46600e42, // n0x018f c0x0119 (n0x1708-n0x1714) + I st 0x00201a82, // n0x0190 c0x0000 (---------------) + I su 0x00263648, // n0x0191 c0x0000 (---------------) + I supplies 0x0020e306, // n0x0192 c0x0000 (---------------) + I supply 0x002029c7, // n0x0193 c0x0000 (---------------) + I support 0x46a14202, // n0x0194 c0x011a (n0x1714-n0x1719) + I sv 0x46ee7542, // n0x0195 c0x011b (n0x1719-n0x171a) + I sx 0x4720ba02, // n0x0196 c0x011c (n0x171a-n0x1720) + I sy 0x0020ba07, // n0x0197 c0x0000 (---------------) + I systems 0x4761cc42, // n0x0198 c0x011d (n0x1720-n0x1723) + I sz 0x00260cc6, // n0x0199 c0x0000 (---------------) + I tattoo 0x00200282, // n0x019a c0x0000 (---------------) + I tc 0x47a01702, // n0x019b c0x011e (n0x1723-n0x1724) + I td 0x0031534a, // n0x019c c0x0000 (---------------) + I technology 0x00217043, // n0x019d c0x0000 (---------------) + I tel 0x00280d82, // n0x019e c0x0000 (---------------) + I tf 0x0023a882, // n0x019f c0x0000 (---------------) + I tg 0x47e0a2c2, // n0x01a0 c0x011f (n0x1724-n0x172b) + I th 0x00204d86, // n0x01a1 c0x0000 (---------------) + I tienda 0x002a9284, // n0x01a2 c0x0000 (---------------) + I tips 0x48234a82, // n0x01a3 c0x0120 (n0x172b-n0x173a) + I tj 0x00204082, // n0x01a4 c0x0000 (---------------) + I tk 0x486192c2, // n0x01a5 c0x0121 (n0x173a-n0x173b) + I tl 0x48a26782, // n0x01a6 c0x0122 (n0x173b-n0x1743) + I tm 0x48e075c2, // n0x01a7 c0x0123 (n0x1743-n0x1757) + I tn 0x49202842, // n0x01a8 c0x0124 (n0x1757-n0x175d) + I to 0x00209d85, // n0x01a9 c0x0000 (---------------) + I today 0x0034ad45, // n0x01aa c0x0000 (---------------) + I tokyo 0x00260d85, // n0x01ab c0x0000 (---------------) + I tools 0x00224d42, // n0x01ac c0x0000 (---------------) + I tp 0x49605102, // n0x01ad c0x0125 (n0x175d-n0x175f)* o I tr 0x0027a2c8, // n0x01ae c0x0000 (---------------) + I training 0x002a7006, // n0x01af c0x0000 (---------------) + I travel 0x49e0c502, // n0x01b0 c0x0127 (n0x1760-n0x1771) + I tt 0x4a288182, // n0x01b1 c0x0128 (n0x1771-n0x1775) + I tv 0x4a6284c2, // n0x01b2 c0x0129 (n0x1775-n0x1783) + I tw 0x4aa46002, // n0x01b3 c0x012a (n0x1783-n0x178f) + I tz 0x4ae08102, // n0x01b4 c0x012b (n0x178f-n0x17dd) + I ua 0x4b20ab02, // n0x01b5 c0x012c (n0x17dd-n0x17e5) + I ug 0x4b6000c2, // n0x01b6 c0x012d (n0x17e5-n0x17f0)* o I uk 0x00210543, // n0x01b7 c0x0000 (---------------) + I uno 0x4be01682, // n0x01b8 c0x012f (n0x17f1-n0x1830) + I us 0x5a219e02, // n0x01b9 c0x0168 (n0x18d4-n0x18da) + I uy 0x5a62acc2, // n0x01ba c0x0169 (n0x18da-n0x18de) + I uz 0x00203302, // n0x01bb c0x0000 (---------------) + I va 0x00267a89, // n0x01bc c0x0000 (---------------) + I vacations 0x5aa41e02, // n0x01bd c0x016a (n0x18de-n0x18e4) + I vc 0x5ae01382, // n0x01be c0x016b (n0x18e4-n0x18ee) + I ve 0x00263b85, // n0x01bf c0x0000 (---------------) + I vegas 0x0023b548, // n0x01c0 c0x0000 (---------------) + I ventures 0x00257102, // n0x01c1 c0x0000 (---------------) + I vg 0x5b205782, // n0x01c2 c0x016c (n0x18ee-n0x18f3) + I vi 0x002bbd86, // n0x01c3 c0x0000 (---------------) + I viajes 0x002e9f06, // n0x01c4 c0x0000 (---------------) + I villas 0x0023c106, // n0x01c5 c0x0000 (---------------) + I vision 0x5b60d942, // n0x01c6 c0x016d (n0x18f3-n0x18ff) + I vn 0x00236145, // n0x01c7 c0x0000 (---------------) + I vodka 0x002ef544, // n0x01c8 c0x0000 (---------------) + I vote 0x002ef646, // n0x01c9 c0x0000 (---------------) + I voting 0x002ef7c4, // n0x01ca c0x0000 (---------------) + I voto 0x00226606, // n0x01cb c0x0000 (---------------) + I voyage 0x00207502, // n0x01cc c0x0000 (---------------) + I vu 0x00253244, // n0x01cd c0x0000 (---------------) + I wang 0x00200205, // n0x01ce c0x0000 (---------------) + I watch 0x00275083, // n0x01cf c0x0000 (---------------) + I wed 0x00224802, // n0x01d0 c0x0000 (---------------) + I wf 0x002d9d84, // n0x01d1 c0x0000 (---------------) + I wien 0x0022df04, // n0x01d2 c0x0000 (---------------) + I wiki 0x002119c4, // n0x01d3 c0x0000 (---------------) + I work 0x0022f985, // n0x01d4 c0x0000 (---------------) + I works 0x5ba16a42, // n0x01d5 c0x016e (n0x18ff-n0x1906) + I ws 0x00243f43, // n0x01d6 c0x0000 (---------------) + I wtc 0x00253b8b, // n0x01d7 c0x0000 (---------------) + I xn--1qqw23a 0x0025e80b, // n0x01d8 c0x0000 (---------------) + I xn--3bst00m 0x0026728b, // n0x01d9 c0x0000 (---------------) + I xn--3ds443g 0x002706cc, // n0x01da c0x0000 (---------------) + I xn--3e0b707e 0x00293b8b, // n0x01db c0x0000 (---------------) + I xn--45brj9c 0x0029750a, // n0x01dc c0x0000 (---------------) + I xn--45q11c 0x002c818a, // n0x01dd c0x0000 (---------------) + I xn--4gbrim 0x002c69ce, // n0x01de c0x0000 (---------------) + I xn--54b7fta0cc 0x002e758b, // n0x01df c0x0000 (---------------) + I xn--55qw42g 0x0035900a, // n0x01e0 c0x0000 (---------------) + I xn--55qx5d 0x0035ae4b, // n0x01e1 c0x0000 (---------------) + I xn--6frz82g 0x0035d04e, // n0x01e2 c0x0000 (---------------) + I xn--6qq986b3xl 0x002f014c, // n0x01e3 c0x0000 (---------------) + I xn--80adxhks 0x002f080b, // n0x01e4 c0x0000 (---------------) + I xn--80ao21a 0x002f0acc, // n0x01e5 c0x0000 (---------------) + I xn--80asehdb 0x002f190a, // n0x01e6 c0x0000 (---------------) + I xn--80aswg 0x002f1b8a, // n0x01e7 c0x0000 (---------------) + I xn--90a3ac 0x002fc889, // n0x01e8 c0x0000 (---------------) + I xn--c1avg 0x002fcaca, // n0x01e9 c0x0000 (---------------) + I xn--cg4bki 0x002fda56, // n0x01ea c0x0000 (---------------) + I xn--clchc0ea0b2g2a9gcd 0x002fedcb, // n0x01eb c0x0000 (---------------) + I xn--czr694b 0x002ffa0a, // n0x01ec c0x0000 (---------------) + I xn--czrs0t 0x002ffc8a, // n0x01ed c0x0000 (---------------) + I xn--czru2d 0x00300f4b, // n0x01ee c0x0000 (---------------) + I xn--d1acj3b 0x003047ce, // n0x01ef c0x0000 (---------------) + I xn--fiq228c5hs 0x0030500a, // n0x01f0 c0x0000 (---------------) + I xn--fiq64b 0x0030734a, // n0x01f1 c0x0000 (---------------) + I xn--fiqs8s 0x0030788a, // n0x01f2 c0x0000 (---------------) + I xn--fiqz9s 0x0030894d, // n0x01f3 c0x0000 (---------------) + I xn--fpcrj9c3d 0x00309c8d, // n0x01f4 c0x0000 (---------------) + I xn--fzc2c9e2c 0x0030b20b, // n0x01f5 c0x0000 (---------------) + I xn--gecrj9c 0x0030f1cb, // n0x01f6 c0x0000 (---------------) + I xn--h2brj9c 0x00318e0f, // n0x01f7 c0x0000 (---------------) + I xn--i1b6b1a6a2e 0x0031b64a, // n0x01f8 c0x0000 (---------------) + I xn--io0a7i 0x0031bc09, // n0x01f9 c0x0000 (---------------) + I xn--j1amh 0x0031bf0b, // n0x01fa c0x0000 (---------------) + I xn--j6w193g 0x0031ddcb, // n0x01fb c0x0000 (---------------) + I xn--kprw13d 0x0031e08b, // n0x01fc c0x0000 (---------------) + I xn--kpry57d 0x00321449, // n0x01fd c0x0000 (---------------) + I xn--l1acc 0x0032474f, // n0x01fe c0x0000 (---------------) + I xn--lgbbat1ad8j 0x0032914c, // n0x01ff c0x0000 (---------------) + I xn--mgb2ddes 0x003296cc, // n0x0200 c0x0000 (---------------) + I xn--mgb9awbf 0x00329f8f, // n0x0201 c0x0000 (---------------) + I xn--mgba3a4f16a 0x0032a34e, // n0x0202 c0x0000 (---------------) + I xn--mgba3a4fra 0x0032abce, // n0x0203 c0x0000 (---------------) + I xn--mgbaam7a8h 0x0032b74c, // n0x0204 c0x0000 (---------------) + I xn--mgbab2bd 0x0032ba4e, // n0x0205 c0x0000 (---------------) + I xn--mgbayh7gpa 0x0032be8e, // n0x0206 c0x0000 (---------------) + I xn--mgbbh1a71e 0x0032c20f, // n0x0207 c0x0000 (---------------) + I xn--mgbc0a9azcg 0x0032c5d3, // n0x0208 c0x0000 (---------------) + I xn--mgberp4a5d4a87g 0x0032ca91, // n0x0209 c0x0000 (---------------) + I xn--mgberp4a5d4ar 0x0032ced3, // n0x020a c0x0000 (---------------) + I xn--mgbqly7c0a67fbc 0x0032dc50, // n0x020b c0x0000 (---------------) + I xn--mgbqly7cvafr 0x0032e60c, // n0x020c c0x0000 (---------------) + I xn--mgbtf8fl 0x0032f04e, // n0x020d c0x0000 (---------------) + I xn--mgbx4cd0ab 0x00336a0c, // n0x020e c0x0000 (---------------) + I xn--ngbc5azd 0x003375cb, // n0x020f c0x0000 (---------------) + I xn--nnx388a 0x00337888, // n0x0210 c0x0000 (---------------) + I xn--node 0x00337cc9, // n0x0211 c0x0000 (---------------) + I xn--nqv7f 0x00337ccf, // n0x0212 c0x0000 (---------------) + I xn--nqv7fs00ema 0x0033924a, // n0x0213 c0x0000 (---------------) + I xn--o3cw4h 0x0033ad4c, // n0x0214 c0x0000 (---------------) + I xn--ogbpf8fl 0x0033bd89, // n0x0215 c0x0000 (---------------) + I xn--p1acf 0x0033c148, // n0x0216 c0x0000 (---------------) + I xn--p1ai 0x0033c70b, // n0x0217 c0x0000 (---------------) + I xn--pgbs0dh 0x0033d18b, // n0x0218 c0x0000 (---------------) + I xn--q9jyb4c 0x0034068b, // n0x0219 c0x0000 (---------------) + I xn--rhqv96g 0x0034400b, // n0x021a c0x0000 (---------------) + I xn--s9brj9c 0x00345f8b, // n0x021b c0x0000 (---------------) + I xn--ses554g 0x0035274a, // n0x021c c0x0000 (---------------) + I xn--unup4y 0x00354bc9, // n0x021d c0x0000 (---------------) + I xn--vhquv 0x0035608a, // n0x021e c0x0000 (---------------) + I xn--wgbh1c 0x003564ca, // n0x021f c0x0000 (---------------) + I xn--wgbl6a 0x0035674b, // n0x0220 c0x0000 (---------------) + I xn--xhq521b 0x00357210, // n0x0221 c0x0000 (---------------) + I xn--xkc2al3hye2a 0x00357611, // n0x0222 c0x0000 (---------------) + I xn--xkc2dl3a5ee0h 0x0035860d, // n0x0223 c0x0000 (---------------) + I xn--yfro4i67o 0x00358d0d, // n0x0224 c0x0000 (---------------) + I xn--ygbi2ammx 0x0035b10b, // n0x0225 c0x0000 (---------------) + I xn--zfr164b 0x0035cfc3, // n0x0226 c0x0000 (---------------) + I xxx 0x002232c3, // n0x0227 c0x0000 (---------------) + I xyz 0x00238146, // n0x0228 c0x0000 (---------------) + I yachts 0x016188c2, // n0x0229 c0x0005 (---------------)* o I ye 0x00302288, // n0x022a c0x0000 (---------------) + I yokohama 0x0022d242, // n0x022b c0x0000 (---------------) + I yt 0x01603e02, // n0x022c c0x0005 (---------------)* o I za 0x01655d42, // n0x022d c0x0005 (---------------)* o I zm 0x002cbd44, // n0x022e c0x0000 (---------------) + I zone 0x016c8742, // n0x022f c0x0005 (---------------)* o I zw 0x002370c3, // n0x0230 c0x0000 (---------------) + I com 0x00215b83, // n0x0231 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0232 c0x0000 (---------------) + I gov 0x00210703, // n0x0233 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x0234 c0x0000 (---------------) + I net 0x0024af03, // n0x0235 c0x0000 (---------------) + I org 0x0020fc03, // n0x0236 c0x0000 (---------------) + I nom 0x0020a3c2, // n0x0237 c0x0000 (---------------) + I ac 0x00209382, // n0x0238 c0x0000 (---------------) + I co 0x00208ec3, // n0x0239 c0x0000 (---------------) + I gov 0x00210703, // n0x023a c0x0000 (---------------) + I mil 0x0024bdc3, // n0x023b c0x0000 (---------------) + I net 0x0024af03, // n0x023c c0x0000 (---------------) + I org 0x0025e643, // n0x023d c0x0000 (---------------) + I sch 0x003215d6, // n0x023e c0x0000 (---------------) + I accident-investigation 0x002fa313, // n0x023f c0x0000 (---------------) + I accident-prevention 0x00359fc9, // n0x0240 c0x0000 (---------------) + I aerobatic 0x002728c8, // n0x0241 c0x0000 (---------------) + I aeroclub 0x0029e3c9, // n0x0242 c0x0000 (---------------) + I aerodrome 0x00319c46, // n0x0243 c0x0000 (---------------) + I agents 0x00265050, // n0x0244 c0x0000 (---------------) + I air-surveillance 0x00265b53, // n0x0245 c0x0000 (---------------) + I air-traffic-control 0x003314c8, // n0x0246 c0x0000 (---------------) + I aircraft 0x00291c47, // n0x0247 c0x0000 (---------------) + I airline 0x0028ea87, // n0x0248 c0x0000 (---------------) + I airport 0x002abd0a, // n0x0249 c0x0000 (---------------) + I airtraffic 0x00213a49, // n0x024a c0x0000 (---------------) + I ambulance 0x00330d09, // n0x024b c0x0000 (---------------) + I amusement 0x002c5c8b, // n0x024c c0x0000 (---------------) + I association 0x002e0386, // n0x024d c0x0000 (---------------) + I author 0x0022c14a, // n0x024e c0x0000 (---------------) + I ballooning 0x00221246, // n0x024f c0x0000 (---------------) + I broker 0x00313843, // n0x0250 c0x0000 (---------------) + I caa 0x0035a1c5, // n0x0251 c0x0000 (---------------) + I cargo 0x002cf388, // n0x0252 c0x0000 (---------------) + I catering 0x00346e0d, // n0x0253 c0x0000 (---------------) + I certification 0x0020238c, // n0x0254 c0x0000 (---------------) + I championship 0x002555c7, // n0x0255 c0x0000 (---------------) + I charter 0x0022e7cd, // n0x0256 c0x0000 (---------------) + I civilaviation 0x002729c4, // n0x0257 c0x0000 (---------------) + I club 0x002386ca, // n0x0258 c0x0000 (---------------) + I conference 0x00239b8a, // n0x0259 c0x0000 (---------------) + I consultant 0x0023a04a, // n0x025a c0x0000 (---------------) + I consulting 0x00231787, // n0x025b c0x0000 (---------------) + I control 0x00241bc7, // n0x025c c0x0000 (---------------) + I council 0x00243d04, // n0x025d c0x0000 (---------------) + I crew 0x00251c86, // n0x025e c0x0000 (---------------) + I design 0x002b04c4, // n0x025f c0x0000 (---------------) + I dgca 0x002224c8, // n0x0260 c0x0000 (---------------) + I educator 0x0020a449, // n0x0261 c0x0000 (---------------) + I emergency 0x002b7706, // n0x0262 c0x0000 (---------------) + I engine 0x002b7708, // n0x0263 c0x0000 (---------------) + I engineer 0x0024570d, // n0x0264 c0x0000 (---------------) + I entertainment 0x00343dc9, // n0x0265 c0x0000 (---------------) + I equipment 0x0021ccc8, // n0x0266 c0x0000 (---------------) + I exchange 0x002230c7, // n0x0267 c0x0000 (---------------) + I express 0x0023cd8a, // n0x0268 c0x0000 (---------------) + I federation 0x00249fc6, // n0x0269 c0x0000 (---------------) + I flight 0x00253407, // n0x026a c0x0000 (---------------) + I freight 0x002d6804, // n0x026b c0x0000 (---------------) + I fuel 0x0025ff07, // n0x026c c0x0000 (---------------) + I gliding 0x00283e4a, // n0x026d c0x0000 (---------------) + I government 0x0023558e, // n0x026e c0x0000 (---------------) + I groundhandling 0x00211b85, // n0x026f c0x0000 (---------------) + I group 0x0027aacb, // n0x0270 c0x0000 (---------------) + I hanggliding 0x00250049, // n0x0271 c0x0000 (---------------) + I homebuilt 0x002a0309, // n0x0272 c0x0000 (---------------) + I insurance 0x0020fe87, // n0x0273 c0x0000 (---------------) + I journal 0x0022e20a, // n0x0274 c0x0000 (---------------) + I journalist 0x0021f987, // n0x0275 c0x0000 (---------------) + I leasing 0x0024ab89, // n0x0276 c0x0000 (---------------) + I logistics 0x002b1208, // n0x0277 c0x0000 (---------------) + I magazine 0x0035c90b, // n0x0278 c0x0000 (---------------) + I maintenance 0x00346bcb, // n0x0279 c0x0000 (---------------) + I marketplace 0x00216085, // n0x027a c0x0000 (---------------) + I media 0x00305b4a, // n0x027b c0x0000 (---------------) + I microlight 0x00306489, // n0x027c c0x0000 (---------------) + I modelling 0x0020724a, // n0x027d c0x0000 (---------------) + I navigation 0x0022688b, // n0x027e c0x0000 (---------------) + I parachuting 0x0025fe0b, // n0x027f c0x0000 (---------------) + I paragliding 0x002c5a15, // n0x0280 c0x0000 (---------------) + I passenger-association 0x002c7a85, // n0x0281 c0x0000 (---------------) + I pilot 0x00223145, // n0x0282 c0x0000 (---------------) + I press 0x002ced4a, // n0x0283 c0x0000 (---------------) + I production 0x00229aca, // n0x0284 c0x0000 (---------------) + I recreation 0x00274b07, // n0x0285 c0x0000 (---------------) + I repbody 0x0021b703, // n0x0286 c0x0000 (---------------) + I res 0x002cc008, // n0x0287 c0x0000 (---------------) + I research 0x002c1f4a, // n0x0288 c0x0000 (---------------) + I rotorcraft 0x002f3686, // n0x0289 c0x0000 (---------------) + I safety 0x0028ffc9, // n0x028a c0x0000 (---------------) + I scientist 0x002d33c8, // n0x028b c0x0000 (---------------) + I services 0x002d9e84, // n0x028c c0x0000 (---------------) + I show 0x0030b7c9, // n0x028d c0x0000 (---------------) + I skydiving 0x0025a6c8, // n0x028e c0x0000 (---------------) + I software 0x002a6e87, // n0x028f c0x0000 (---------------) + I student 0x00284084, // n0x0290 c0x0000 (---------------) + I taxi 0x00259986, // n0x0291 c0x0000 (---------------) + I trader 0x00292747, // n0x0292 c0x0000 (---------------) + I trading 0x002f2e47, // n0x0293 c0x0000 (---------------) + I trainer 0x0025ec85, // n0x0294 c0x0000 (---------------) + I union 0x002119cc, // n0x0295 c0x0000 (---------------) + I workinggroup 0x0022f985, // n0x0296 c0x0000 (---------------) + I works 0x002370c3, // n0x0297 c0x0000 (---------------) + I com 0x00215b83, // n0x0298 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0299 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x029a c0x0000 (---------------) + I net 0x0024af03, // n0x029b c0x0000 (---------------) + I org 0x00209382, // n0x029c c0x0000 (---------------) + I co 0x002370c3, // n0x029d c0x0000 (---------------) + I com 0x0024bdc3, // n0x029e c0x0000 (---------------) + I net 0x0020fc03, // n0x029f c0x0000 (---------------) + I nom 0x0024af03, // n0x02a0 c0x0000 (---------------) + I org 0x002370c3, // n0x02a1 c0x0000 (---------------) + I com 0x0024bdc3, // n0x02a2 c0x0000 (---------------) + I net 0x0023e803, // n0x02a3 c0x0000 (---------------) + I off 0x0024af03, // n0x02a4 c0x0000 (---------------) + I org 0x002370c3, // n0x02a5 c0x0000 (---------------) + I com 0x00215b83, // n0x02a6 c0x0000 (---------------) + I edu 0x00208ec3, // n0x02a7 c0x0000 (---------------) + I gov 0x00210703, // n0x02a8 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x02a9 c0x0000 (---------------) + I net 0x0024af03, // n0x02aa c0x0000 (---------------) + I org 0x002370c3, // n0x02ab c0x0000 (---------------) + I com 0x00215b83, // n0x02ac c0x0000 (---------------) + I edu 0x0024bdc3, // n0x02ad c0x0000 (---------------) + I net 0x0024af03, // n0x02ae c0x0000 (---------------) + I org 0x00209382, // n0x02af c0x0000 (---------------) + I co 0x00203b42, // n0x02b0 c0x0000 (---------------) + I ed 0x0023a282, // n0x02b1 c0x0000 (---------------) + I gv 0x00200c02, // n0x02b2 c0x0000 (---------------) + I it 0x00201102, // n0x02b3 c0x0000 (---------------) + I og 0x00274b82, // n0x02b4 c0x0000 (---------------) + I pb 0x046370c3, // n0x02b5 c0x0011 (n0x02bd-n0x02be) + I com 0x00215b83, // n0x02b6 c0x0000 (---------------) + I edu 0x00223ac3, // n0x02b7 c0x0000 (---------------) + I gob 0x00216fc3, // n0x02b8 c0x0000 (---------------) + I int 0x00210703, // n0x02b9 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x02ba c0x0000 (---------------) + I net 0x0024af03, // n0x02bb c0x0000 (---------------) + I org 0x0022d603, // n0x02bc c0x0000 (---------------) + I tur 0x0012d948, // n0x02bd c0x0000 (---------------) + blogspot 0x0022c684, // n0x02be c0x0000 (---------------) + I e164 0x00262847, // n0x02bf c0x0000 (---------------) + I in-addr 0x002410c3, // n0x02c0 c0x0000 (---------------) + I ip6 0x00304c44, // n0x02c1 c0x0000 (---------------) + I iris 0x00218243, // n0x02c2 c0x0000 (---------------) + I uri 0x0020ff03, // n0x02c3 c0x0000 (---------------) + I urn 0x00208ec3, // n0x02c4 c0x0000 (---------------) + I gov 0x0020a3c2, // n0x02c5 c0x0000 (---------------) + I ac 0x00105743, // n0x02c6 c0x0000 (---------------) + biz 0x05609382, // n0x02c7 c0x0015 (n0x02cc-n0x02cd) + I co 0x0023a282, // n0x02c8 c0x0000 (---------------) + I gv 0x00014b84, // n0x02c9 c0x0000 (---------------) + info 0x00201002, // n0x02ca c0x0000 (---------------) + I or 0x000ce944, // n0x02cb c0x0000 (---------------) + priv 0x0012d948, // n0x02cc c0x0000 (---------------) + blogspot 0x0023b243, // n0x02cd c0x0000 (---------------) + I act 0x002a3f43, // n0x02ce c0x0000 (---------------) + I asn 0x05e370c3, // n0x02cf c0x0017 (n0x02e0-n0x02e1) + I com 0x002386c4, // n0x02d0 c0x0000 (---------------) + I conf 0x00258cc5, // n0x02d1 c0x0000 (---------------) + I csiro 0x06215b83, // n0x02d2 c0x0018 (n0x02e1-n0x02e9) + I edu 0x06608ec3, // n0x02d3 c0x0019 (n0x02e9-n0x02ef) + I gov 0x00203d42, // n0x02d4 c0x0000 (---------------) + I id 0x00214b84, // n0x02d5 c0x0000 (---------------) + I info 0x0024bdc3, // n0x02d6 c0x0000 (---------------) + I net 0x00275003, // n0x02d7 c0x0000 (---------------) + I nsw 0x00211402, // n0x02d8 c0x0000 (---------------) + I nt 0x0024af03, // n0x02d9 c0x0000 (---------------) + I org 0x00207002, // n0x02da c0x0000 (---------------) + I oz 0x00255b03, // n0x02db c0x0000 (---------------) + I qld 0x00200602, // n0x02dc c0x0000 (---------------) + I sa 0x00216e03, // n0x02dd c0x0000 (---------------) + I tas 0x002d3483, // n0x02de c0x0000 (---------------) + I vic 0x00200202, // n0x02df c0x0000 (---------------) + I wa 0x0012d948, // n0x02e0 c0x0000 (---------------) + blogspot 0x0023b243, // n0x02e1 c0x0000 (---------------) + I act 0x00275003, // n0x02e2 c0x0000 (---------------) + I nsw 0x00211402, // n0x02e3 c0x0000 (---------------) + I nt 0x00255b03, // n0x02e4 c0x0000 (---------------) + I qld 0x00200602, // n0x02e5 c0x0000 (---------------) + I sa 0x00216e03, // n0x02e6 c0x0000 (---------------) + I tas 0x002d3483, // n0x02e7 c0x0000 (---------------) + I vic 0x00200202, // n0x02e8 c0x0000 (---------------) + I wa 0x0023b243, // n0x02e9 c0x0000 (---------------) + I act 0x00255b03, // n0x02ea c0x0000 (---------------) + I qld 0x00200602, // n0x02eb c0x0000 (---------------) + I sa 0x00216e03, // n0x02ec c0x0000 (---------------) + I tas 0x002d3483, // n0x02ed c0x0000 (---------------) + I vic 0x00200202, // n0x02ee c0x0000 (---------------) + I wa 0x002370c3, // n0x02ef c0x0000 (---------------) + I com 0x00305743, // n0x02f0 c0x0000 (---------------) + I biz 0x002370c3, // n0x02f1 c0x0000 (---------------) + I com 0x00215b83, // n0x02f2 c0x0000 (---------------) + I edu 0x00208ec3, // n0x02f3 c0x0000 (---------------) + I gov 0x00214b84, // n0x02f4 c0x0000 (---------------) + I info 0x00216fc3, // n0x02f5 c0x0000 (---------------) + I int 0x00210703, // n0x02f6 c0x0000 (---------------) + I mil 0x002592c4, // n0x02f7 c0x0000 (---------------) + I name 0x0024bdc3, // n0x02f8 c0x0000 (---------------) + I net 0x0024af03, // n0x02f9 c0x0000 (---------------) + I org 0x00202a42, // n0x02fa c0x0000 (---------------) + I pp 0x00206683, // n0x02fb c0x0000 (---------------) + I pro 0x00209382, // n0x02fc c0x0000 (---------------) + I co 0x002370c3, // n0x02fd c0x0000 (---------------) + I com 0x00215b83, // n0x02fe c0x0000 (---------------) + I edu 0x00208ec3, // n0x02ff c0x0000 (---------------) + I gov 0x00210703, // n0x0300 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x0301 c0x0000 (---------------) + I net 0x0024af03, // n0x0302 c0x0000 (---------------) + I org 0x00200a02, // n0x0303 c0x0000 (---------------) + I rs 0x00291f84, // n0x0304 c0x0000 (---------------) + I unbi 0x00271004, // n0x0305 c0x0000 (---------------) + I unsa 0x00305743, // n0x0306 c0x0000 (---------------) + I biz 0x002370c3, // n0x0307 c0x0000 (---------------) + I com 0x00215b83, // n0x0308 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0309 c0x0000 (---------------) + I gov 0x00214b84, // n0x030a c0x0000 (---------------) + I info 0x0024bdc3, // n0x030b c0x0000 (---------------) + I net 0x0024af03, // n0x030c c0x0000 (---------------) + I org 0x002e3245, // n0x030d c0x0000 (---------------) + I store 0x0020a3c2, // n0x030e c0x0000 (---------------) + I ac 0x0012d948, // n0x030f c0x0000 (---------------) + blogspot 0x00208ec3, // n0x0310 c0x0000 (---------------) + I gov 0x00241b01, // n0x0311 c0x0000 (---------------) + I 0 0x00200f01, // n0x0312 c0x0000 (---------------) + I 1 0x00205b41, // n0x0313 c0x0000 (---------------) + I 2 0x00200a81, // n0x0314 c0x0000 (---------------) + I 3 0x0022c741, // n0x0315 c0x0000 (---------------) + I 4 0x00293cc1, // n0x0316 c0x0000 (---------------) + I 5 0x0022c701, // n0x0317 c0x0000 (---------------) + I 6 0x002708c1, // n0x0318 c0x0000 (---------------) + I 7 0x002f0241, // n0x0319 c0x0000 (---------------) + I 8 0x00293dc1, // n0x031a c0x0000 (---------------) + I 9 0x00200141, // n0x031b c0x0000 (---------------) + I a 0x00200001, // n0x031c c0x0000 (---------------) + I b 0x002002c1, // n0x031d c0x0000 (---------------) + I c 0x00200401, // n0x031e c0x0000 (---------------) + I d 0x002009c1, // n0x031f c0x0000 (---------------) + I e 0x00200081, // n0x0320 c0x0000 (---------------) + I f 0x00200181, // n0x0321 c0x0000 (---------------) + I g 0x00200301, // n0x0322 c0x0000 (---------------) + I h 0x00200041, // n0x0323 c0x0000 (---------------) + I i 0x00204c81, // n0x0324 c0x0000 (---------------) + I j 0x00200101, // n0x0325 c0x0000 (---------------) + I k 0x002004c1, // n0x0326 c0x0000 (---------------) + I l 0x00200741, // n0x0327 c0x0000 (---------------) + I m 0x002003c1, // n0x0328 c0x0000 (---------------) + I n 0x00200501, // n0x0329 c0x0000 (---------------) + I o 0x00202481, // n0x032a c0x0000 (---------------) + I p 0x0020b801, // n0x032b c0x0000 (---------------) + I q 0x00200a01, // n0x032c c0x0000 (---------------) + I r 0x00200601, // n0x032d c0x0000 (---------------) + I s 0x00200281, // n0x032e c0x0000 (---------------) + I t 0x002000c1, // n0x032f c0x0000 (---------------) + I u 0x00201381, // n0x0330 c0x0000 (---------------) + I v 0x00200201, // n0x0331 c0x0000 (---------------) + I w 0x002146c1, // n0x0332 c0x0000 (---------------) + I x 0x00201181, // n0x0333 c0x0000 (---------------) + I y 0x00202301, // n0x0334 c0x0000 (---------------) + I z 0x002370c3, // n0x0335 c0x0000 (---------------) + I com 0x00215b83, // n0x0336 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0337 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0338 c0x0000 (---------------) + I net 0x0024af03, // n0x0339 c0x0000 (---------------) + I org 0x00209382, // n0x033a c0x0000 (---------------) + I co 0x002370c3, // n0x033b c0x0000 (---------------) + I com 0x00215b83, // n0x033c c0x0000 (---------------) + I edu 0x00201002, // n0x033d c0x0000 (---------------) + I or 0x0024af03, // n0x033e c0x0000 (---------------) + I org 0x00011746, // n0x033f c0x0000 (---------------) + dyndns 0x0004d14a, // n0x0340 c0x0000 (---------------) + for-better 0x00076e08, // n0x0341 c0x0000 (---------------) + for-more 0x0004d788, // n0x0342 c0x0000 (---------------) + for-some 0x0004e1c7, // n0x0343 c0x0000 (---------------) + for-the 0x0010c246, // n0x0344 c0x0000 (---------------) + selfip 0x0003eb86, // n0x0345 c0x0000 (---------------) + webhop 0x002c5c84, // n0x0346 c0x0000 (---------------) + I asso 0x002ff047, // n0x0347 c0x0000 (---------------) + I barreau 0x0012d948, // n0x0348 c0x0000 (---------------) + blogspot 0x002d80c4, // n0x0349 c0x0000 (---------------) + I gouv 0x002370c3, // n0x034a c0x0000 (---------------) + I com 0x00215b83, // n0x034b c0x0000 (---------------) + I edu 0x00208ec3, // n0x034c c0x0000 (---------------) + I gov 0x0024bdc3, // n0x034d c0x0000 (---------------) + I net 0x0024af03, // n0x034e c0x0000 (---------------) + I org 0x002370c3, // n0x034f c0x0000 (---------------) + I com 0x00215b83, // n0x0350 c0x0000 (---------------) + I edu 0x00223ac3, // n0x0351 c0x0000 (---------------) + I gob 0x00208ec3, // n0x0352 c0x0000 (---------------) + I gov 0x00216fc3, // n0x0353 c0x0000 (---------------) + I int 0x00210703, // n0x0354 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x0355 c0x0000 (---------------) + I net 0x0024af03, // n0x0356 c0x0000 (---------------) + I org 0x00288182, // n0x0357 c0x0000 (---------------) + I tv 0x002c6303, // n0x0358 c0x0000 (---------------) + I adm 0x00257b83, // n0x0359 c0x0000 (---------------) + I adv 0x00223d43, // n0x035a c0x0000 (---------------) + I agr 0x00200702, // n0x035b c0x0000 (---------------) + I am 0x00255a83, // n0x035c c0x0000 (---------------) + I arq 0x00209243, // n0x035d c0x0000 (---------------) + I art 0x00202803, // n0x035e c0x0000 (---------------) + I ato 0x00200001, // n0x035f c0x0000 (---------------) + I b 0x00208783, // n0x0360 c0x0000 (---------------) + I bio 0x0020e8c4, // n0x0361 c0x0000 (---------------) + I blog 0x0020f843, // n0x0362 c0x0000 (---------------) + I bmd 0x003562c3, // n0x0363 c0x0000 (---------------) + I cim 0x002f1403, // n0x0364 c0x0000 (---------------) + I cng 0x00234a03, // n0x0365 c0x0000 (---------------) + I cnt 0x0a2370c3, // n0x0366 c0x0028 (n0x039d-n0x039e) + I com 0x0023d9c4, // n0x0367 c0x0000 (---------------) + I coop 0x002f13c3, // n0x0368 c0x0000 (---------------) + I ecn 0x00209343, // n0x0369 c0x0000 (---------------) + I eco 0x00215b83, // n0x036a c0x0000 (---------------) + I edu 0x0023a603, // n0x036b c0x0000 (---------------) + I emp 0x00206443, // n0x036c c0x0000 (---------------) + I eng 0x0030a343, // n0x036d c0x0000 (---------------) + I esp 0x00312643, // n0x036e c0x0000 (---------------) + I etc 0x00204d43, // n0x036f c0x0000 (---------------) + I eti 0x00224843, // n0x0370 c0x0000 (---------------) + I far 0x0024ab44, // n0x0371 c0x0000 (---------------) + I flog 0x0033ee02, // n0x0372 c0x0000 (---------------) + I fm 0x0024cec3, // n0x0373 c0x0000 (---------------) + I fnd 0x002521c3, // n0x0374 c0x0000 (---------------) + I fot 0x0026ab03, // n0x0375 c0x0000 (---------------) + I fst 0x00241a43, // n0x0376 c0x0000 (---------------) + I g12 0x0022c383, // n0x0377 c0x0000 (---------------) + I ggf 0x00208ec3, // n0x0378 c0x0000 (---------------) + I gov 0x002da003, // n0x0379 c0x0000 (---------------) + I imb 0x00201803, // n0x037a c0x0000 (---------------) + I ind 0x00214b83, // n0x037b c0x0000 (---------------) + I inf 0x0020df43, // n0x037c c0x0000 (---------------) + I jor 0x002ab8c3, // n0x037d c0x0000 (---------------) + I jus 0x00234e83, // n0x037e c0x0000 (---------------) + I leg 0x002e3c83, // n0x037f c0x0000 (---------------) + I lel 0x00204243, // n0x0380 c0x0000 (---------------) + I mat 0x00216083, // n0x0381 c0x0000 (---------------) + I med 0x00210703, // n0x0382 c0x0000 (---------------) + I mil 0x002c2343, // n0x0383 c0x0000 (---------------) + I mus 0x0024bdc3, // n0x0384 c0x0000 (---------------) + I net 0x0020fc03, // n0x0385 c0x0000 (---------------) + I nom 0x00201e83, // n0x0386 c0x0000 (---------------) + I not 0x00231803, // n0x0387 c0x0000 (---------------) + I ntr 0x002385c3, // n0x0388 c0x0000 (---------------) + I odo 0x0024af03, // n0x0389 c0x0000 (---------------) + I org 0x00253ac3, // n0x038a c0x0000 (---------------) + I ppg 0x00206683, // n0x038b c0x0000 (---------------) + I pro 0x0027e603, // n0x038c c0x0000 (---------------) + I psc 0x002a9303, // n0x038d c0x0000 (---------------) + I psi 0x002d1ec3, // n0x038e c0x0000 (---------------) + I qsl 0x00264dc5, // n0x038f c0x0000 (---------------) + I radio 0x00229ac3, // n0x0390 c0x0000 (---------------) + I rec 0x002da843, // n0x0391 c0x0000 (---------------) + I slg 0x002e2283, // n0x0392 c0x0000 (---------------) + I srv 0x00284084, // n0x0393 c0x0000 (---------------) + I taxi 0x002ba483, // n0x0394 c0x0000 (---------------) + I teo 0x002c5983, // n0x0395 c0x0000 (---------------) + I tmp 0x002b2b43, // n0x0396 c0x0000 (---------------) + I trd 0x0022d603, // n0x0397 c0x0000 (---------------) + I tur 0x00288182, // n0x0398 c0x0000 (---------------) + I tv 0x00240043, // n0x0399 c0x0000 (---------------) + I vet 0x002ed644, // n0x039a c0x0000 (---------------) + I vlog 0x0022df04, // n0x039b c0x0000 (---------------) + I wiki 0x00254043, // n0x039c c0x0000 (---------------) + I zlg 0x0012d948, // n0x039d c0x0000 (---------------) + blogspot 0x002370c3, // n0x039e c0x0000 (---------------) + I com 0x00215b83, // n0x039f c0x0000 (---------------) + I edu 0x00208ec3, // n0x03a0 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x03a1 c0x0000 (---------------) + I net 0x0024af03, // n0x03a2 c0x0000 (---------------) + I org 0x002370c3, // n0x03a3 c0x0000 (---------------) + I com 0x00215b83, // n0x03a4 c0x0000 (---------------) + I edu 0x00208ec3, // n0x03a5 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x03a6 c0x0000 (---------------) + I net 0x0024af03, // n0x03a7 c0x0000 (---------------) + I org 0x00209382, // n0x03a8 c0x0000 (---------------) + I co 0x0024af03, // n0x03a9 c0x0000 (---------------) + I org 0x002370c3, // n0x03aa c0x0000 (---------------) + I com 0x00208ec3, // n0x03ab c0x0000 (---------------) + I gov 0x00210703, // n0x03ac c0x0000 (---------------) + I mil 0x00237a82, // n0x03ad c0x0000 (---------------) + I of 0x002370c3, // n0x03ae c0x0000 (---------------) + I com 0x00215b83, // n0x03af c0x0000 (---------------) + I edu 0x00208ec3, // n0x03b0 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x03b1 c0x0000 (---------------) + I net 0x0024af03, // n0x03b2 c0x0000 (---------------) + I org 0x00205242, // n0x03b3 c0x0000 (---------------) + I ab 0x0032c382, // n0x03b4 c0x0000 (---------------) + I bc 0x0012d948, // n0x03b5 c0x0000 (---------------) + blogspot 0x00009382, // n0x03b6 c0x0000 (---------------) + co 0x002b0502, // n0x03b7 c0x0000 (---------------) + I gc 0x002028c2, // n0x03b8 c0x0000 (---------------) + I mb 0x00243242, // n0x03b9 c0x0000 (---------------) + I nb 0x00214bc2, // n0x03ba c0x0000 (---------------) + I nf 0x00236642, // n0x03bb c0x0000 (---------------) + I nl 0x00202542, // n0x03bc c0x0000 (---------------) + I ns 0x00211402, // n0x03bd c0x0000 (---------------) + I nt 0x00205642, // n0x03be c0x0000 (---------------) + I nu 0x002011c2, // n0x03bf c0x0000 (---------------) + I on 0x00206742, // n0x03c0 c0x0000 (---------------) + I pe 0x002600c2, // n0x03c1 c0x0000 (---------------) + I qc 0x00201902, // n0x03c2 c0x0000 (---------------) + I sk 0x00299242, // n0x03c3 c0x0000 (---------------) + I yk 0x00024d09, // n0x03c4 c0x0000 (---------------) + ftpaccess 0x000d7a8b, // n0x03c5 c0x0000 (---------------) + game-server 0x000c3f88, // n0x03c6 c0x0000 (---------------) + myphotos 0x0009b889, // n0x03c7 c0x0000 (---------------) + scrapping 0x00208ec3, // n0x03c8 c0x0000 (---------------) + I gov 0x0012d948, // n0x03c9 c0x0000 (---------------) + blogspot 0x0012d948, // n0x03ca c0x0000 (---------------) + blogspot 0x0020a3c2, // n0x03cb c0x0000 (---------------) + I ac 0x002c5c84, // n0x03cc c0x0000 (---------------) + I asso 0x00209382, // n0x03cd c0x0000 (---------------) + I co 0x002370c3, // n0x03ce c0x0000 (---------------) + I com 0x00203b42, // n0x03cf c0x0000 (---------------) + I ed 0x00215b83, // n0x03d0 c0x0000 (---------------) + I edu 0x00208ec2, // n0x03d1 c0x0000 (---------------) + I go 0x002d80c4, // n0x03d2 c0x0000 (---------------) + I gouv 0x00216fc3, // n0x03d3 c0x0000 (---------------) + I int 0x0020f882, // n0x03d4 c0x0000 (---------------) + I md 0x0024bdc3, // n0x03d5 c0x0000 (---------------) + I net 0x00201002, // n0x03d6 c0x0000 (---------------) + I or 0x0024af03, // n0x03d7 c0x0000 (---------------) + I org 0x00223146, // n0x03d8 c0x0000 (---------------) + I presse 0x002f3c8f, // n0x03d9 c0x0000 (---------------) + I xn--aroport-bya 0x00674883, // n0x03da c0x0001 (---------------) ! I www 0x00209382, // n0x03db c0x0000 (---------------) + I co 0x00223ac3, // n0x03dc c0x0000 (---------------) + I gob 0x00208ec3, // n0x03dd c0x0000 (---------------) + I gov 0x00210703, // n0x03de c0x0000 (---------------) + I mil 0x00208ec3, // n0x03df c0x0000 (---------------) + I gov 0x0020a3c2, // n0x03e0 c0x0000 (---------------) + I ac 0x002226c2, // n0x03e1 c0x0000 (---------------) + I ah 0x0020c082, // n0x03e2 c0x0000 (---------------) + I bj 0x002370c3, // n0x03e3 c0x0000 (---------------) + I com 0x00242402, // n0x03e4 c0x0000 (---------------) + I cq 0x00215b83, // n0x03e5 c0x0000 (---------------) + I edu 0x00249842, // n0x03e6 c0x0000 (---------------) + I fj 0x002045c2, // n0x03e7 c0x0000 (---------------) + I gd 0x00208ec3, // n0x03e8 c0x0000 (---------------) + I gov 0x002210c2, // n0x03e9 c0x0000 (---------------) + I gs 0x00253b42, // n0x03ea c0x0000 (---------------) + I gx 0x00254002, // n0x03eb c0x0000 (---------------) + I gz 0x002023c2, // n0x03ec c0x0000 (---------------) + I ha 0x002f31c2, // n0x03ed c0x0000 (---------------) + I hb 0x00202c82, // n0x03ee c0x0000 (---------------) + I he 0x00201e02, // n0x03ef c0x0000 (---------------) + I hi 0x00235f82, // n0x03f0 c0x0000 (---------------) + I hk 0x002668c2, // n0x03f1 c0x0000 (---------------) + I hl 0x00224f42, // n0x03f2 c0x0000 (---------------) + I hn 0x0031c2c2, // n0x03f3 c0x0000 (---------------) + I jl 0x002aac02, // n0x03f4 c0x0000 (---------------) + I js 0x002f6d82, // n0x03f5 c0x0000 (---------------) + I jx 0x00232642, // n0x03f6 c0x0000 (---------------) + I ln 0x00210703, // n0x03f7 c0x0000 (---------------) + I mil 0x00202e02, // n0x03f8 c0x0000 (---------------) + I mo 0x0024bdc3, // n0x03f9 c0x0000 (---------------) + I net 0x00210f82, // n0x03fa c0x0000 (---------------) + I nm 0x00270682, // n0x03fb c0x0000 (---------------) + I nx 0x0024af03, // n0x03fc c0x0000 (---------------) + I org 0x00242442, // n0x03fd c0x0000 (---------------) + I qh 0x00219202, // n0x03fe c0x0000 (---------------) + I sc 0x0023c8c2, // n0x03ff c0x0000 (---------------) + I sd 0x00202582, // n0x0400 c0x0000 (---------------) + I sh 0x002034c2, // n0x0401 c0x0000 (---------------) + I sn 0x002e7542, // n0x0402 c0x0000 (---------------) + I sx 0x00234a82, // n0x0403 c0x0000 (---------------) + I tj 0x002284c2, // n0x0404 c0x0000 (---------------) + I tw 0x00245f42, // n0x0405 c0x0000 (---------------) + I xj 0x0035900a, // n0x0406 c0x0000 (---------------) + I xn--55qx5d 0x0031b64a, // n0x0407 c0x0000 (---------------) + I xn--io0a7i 0x0033968a, // n0x0408 c0x0000 (---------------) + I xn--od0alg 0x0035d3c2, // n0x0409 c0x0000 (---------------) + I xz 0x00211782, // n0x040a c0x0000 (---------------) + I yn 0x00259182, // n0x040b c0x0000 (---------------) + I zj 0x00214704, // n0x040c c0x0000 (---------------) + I arts 0x002370c3, // n0x040d c0x0000 (---------------) + I com 0x00215b83, // n0x040e c0x0000 (---------------) + I edu 0x00248344, // n0x040f c0x0000 (---------------) + I firm 0x00208ec3, // n0x0410 c0x0000 (---------------) + I gov 0x00214b84, // n0x0411 c0x0000 (---------------) + I info 0x00216fc3, // n0x0412 c0x0000 (---------------) + I int 0x00210703, // n0x0413 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x0414 c0x0000 (---------------) + I net 0x0020fc03, // n0x0415 c0x0000 (---------------) + I nom 0x0024af03, // n0x0416 c0x0000 (---------------) + I org 0x00229ac3, // n0x0417 c0x0000 (---------------) + I rec 0x00200b03, // n0x0418 c0x0000 (---------------) + I web 0x0eae1249, // n0x0419 c0x003a (n0x04d3-n0x04ea) o I amazonaws 0x00031947, // n0x041a c0x0000 (---------------) + appspot 0x00002b82, // n0x041b c0x0000 (---------------) + ar 0x0014da8a, // n0x041c c0x0000 (---------------) + betainabox 0x00019587, // n0x041d c0x0000 (---------------) + blogdns 0x0012d948, // n0x041e c0x0000 (---------------) + blogspot 0x00002282, // n0x041f c0x0000 (---------------) + br 0x0014fd87, // n0x0420 c0x0000 (---------------) + cechire 0x0003164f, // n0x0421 c0x0000 (---------------) + cloudcontrolapp 0x0003270f, // n0x0422 c0x0000 (---------------) + cloudcontrolled 0x00034a02, // n0x0423 c0x0000 (---------------) + cn 0x0010a288, // n0x0424 c0x0000 (---------------) + codespot 0x00003442, // n0x0425 c0x0000 (---------------) + de 0x00038f08, // n0x0426 c0x0000 (---------------) + dnsalias 0x00073147, // n0x0427 c0x0000 (---------------) + dnsdojo 0x0001bdcb, // n0x0428 c0x0000 (---------------) + doesntexist 0x00116d49, // n0x0429 c0x0000 (---------------) + dontexist 0x00038e07, // n0x042a c0x0000 (---------------) + doomdns 0x000e86cc, // n0x042b c0x0000 (---------------) + dreamhosters 0x0014cc4a, // n0x042c c0x0000 (---------------) + dyn-o-saur 0x00094dc8, // n0x042d c0x0000 (---------------) + dynalias 0x00074c4e, // n0x042e c0x0000 (---------------) + dyndns-at-home 0x0001174e, // n0x042f c0x0000 (---------------) + dyndns-at-work 0x000193cb, // n0x0430 c0x0000 (---------------) + dyndns-blog 0x0002164b, // n0x0431 c0x0000 (---------------) + dyndns-free 0x00024a4b, // n0x0432 c0x0000 (---------------) + dyndns-home 0x00026009, // n0x0433 c0x0000 (---------------) + dyndns-ip 0x0002b80b, // n0x0434 c0x0000 (---------------) + dyndns-mail 0x0003e64d, // n0x0435 c0x0000 (---------------) + dyndns-office 0x00058a8b, // n0x0436 c0x0000 (---------------) + dyndns-pics 0x000844cd, // n0x0437 c0x0000 (---------------) + dyndns-remote 0x0008520d, // n0x0438 c0x0000 (---------------) + dyndns-server 0x0013d8ca, // n0x0439 c0x0000 (---------------) + dyndns-web 0x0002dd4b, // n0x043a c0x0000 (---------------) + dyndns-wiki 0x0002f7cb, // n0x043b c0x0000 (---------------) + dyndns-work 0x00031f90, // n0x043c c0x0000 (---------------) + elasticbeanstalk 0x0004928f, // n0x043d c0x0000 (---------------) + est-a-la-maison 0x0002590f, // n0x043e c0x0000 (---------------) + est-a-la-masion 0x00046d4d, // n0x043f c0x0000 (---------------) + est-le-patron 0x00020510, // n0x0440 c0x0000 (---------------) + est-mon-blogueur 0x00020842, // n0x0441 c0x0000 (---------------) + eu 0x00054bc7, // n0x0442 c0x0000 (---------------) + from-ak 0x00055787, // n0x0443 c0x0000 (---------------) + from-al 0x00055947, // n0x0444 c0x0000 (---------------) + from-ar 0x00056087, // n0x0445 c0x0000 (---------------) + from-ca 0x00057347, // n0x0446 c0x0000 (---------------) + from-ct 0x00057f07, // n0x0447 c0x0000 (---------------) + from-dc 0x000595c7, // n0x0448 c0x0000 (---------------) + from-de 0x00059b07, // n0x0449 c0x0000 (---------------) + from-fl 0x0005a147, // n0x044a c0x0000 (---------------) + from-ga 0x0005a447, // n0x044b c0x0000 (---------------) + from-hi 0x0005b147, // n0x044c c0x0000 (---------------) + from-ia 0x0005b307, // n0x044d c0x0000 (---------------) + from-id 0x0005b4c7, // n0x044e c0x0000 (---------------) + from-il 0x0005b687, // n0x044f c0x0000 (---------------) + from-in 0x0005b987, // n0x0450 c0x0000 (---------------) + from-ks 0x0005be07, // n0x0451 c0x0000 (---------------) + from-ky 0x0005d8c7, // n0x0452 c0x0000 (---------------) + from-ma 0x0005dfc7, // n0x0453 c0x0000 (---------------) + from-md 0x0005edc7, // n0x0454 c0x0000 (---------------) + from-mi 0x0005f0c7, // n0x0455 c0x0000 (---------------) + from-mn 0x0005f287, // n0x0456 c0x0000 (---------------) + from-mo 0x0005f587, // n0x0457 c0x0000 (---------------) + from-ms 0x0005f847, // n0x0458 c0x0000 (---------------) + from-mt 0x0005fb87, // n0x0459 c0x0000 (---------------) + from-nc 0x000613c7, // n0x045a c0x0000 (---------------) + from-nd 0x00061587, // n0x045b c0x0000 (---------------) + from-ne 0x00061747, // n0x045c c0x0000 (---------------) + from-nh 0x00062d47, // n0x045d c0x0000 (---------------) + from-nj 0x000630c7, // n0x045e c0x0000 (---------------) + from-nm 0x00063a07, // n0x045f c0x0000 (---------------) + from-nv 0x00063fc7, // n0x0460 c0x0000 (---------------) + from-oh 0x00064207, // n0x0461 c0x0000 (---------------) + from-ok 0x00064587, // n0x0462 c0x0000 (---------------) + from-or 0x00064847, // n0x0463 c0x0000 (---------------) + from-pa 0x00065447, // n0x0464 c0x0000 (---------------) + from-pr 0x000660c7, // n0x0465 c0x0000 (---------------) + from-ri 0x00066707, // n0x0466 c0x0000 (---------------) + from-sc 0x00066d87, // n0x0467 c0x0000 (---------------) + from-sd 0x00066f47, // n0x0468 c0x0000 (---------------) + from-tn 0x00067107, // n0x0469 c0x0000 (---------------) + from-tx 0x00067547, // n0x046a c0x0000 (---------------) + from-ut 0x00067947, // n0x046b c0x0000 (---------------) + from-va 0x00068c87, // n0x046c c0x0000 (---------------) + from-vt 0x00068f87, // n0x046d c0x0000 (---------------) + from-wa 0x00069147, // n0x046e c0x0000 (---------------) + from-wi 0x000694c7, // n0x046f c0x0000 (---------------) + from-wv 0x00069947, // n0x0470 c0x0000 (---------------) + from-wy 0x0007d6c2, // n0x0471 c0x0000 (---------------) + gb 0x00026707, // n0x0472 c0x0000 (---------------) + getmyip 0x000607ca, // n0x0473 c0x0000 (---------------) + googleapis 0x0010a10a, // n0x0474 c0x0000 (---------------) + googlecode 0x0004ed06, // n0x0475 c0x0000 (---------------) + gotdns 0x00008982, // n0x0476 c0x0000 (---------------) + gr 0x000ee649, // n0x0477 c0x0000 (---------------) + herokuapp 0x00033a49, // n0x0478 c0x0000 (---------------) + herokussl 0x0004360a, // n0x0479 c0x0000 (---------------) + hobby-site 0x00093989, // n0x047a c0x0000 (---------------) + homelinux 0x00097348, // n0x047b c0x0000 (---------------) + homeunix 0x00003f82, // n0x047c c0x0000 (---------------) + hu 0x00072d89, // n0x047d c0x0000 (---------------) + iamallama 0x0006568e, // n0x047e c0x0000 (---------------) + is-a-anarchist 0x0000e78c, // n0x047f c0x0000 (---------------) + is-a-blogger 0x000b208f, // n0x0480 c0x0000 (---------------) + is-a-bookkeeper 0x0001a8ce, // n0x0481 c0x0000 (---------------) + is-a-bulls-fan 0x0002948c, // n0x0482 c0x0000 (---------------) + is-a-caterer 0x00084149, // n0x0483 c0x0000 (---------------) + is-a-chef 0x00084b51, // n0x0484 c0x0000 (---------------) + is-a-conservative 0x000acbc8, // n0x0485 c0x0000 (---------------) + is-a-cpa 0x0010a552, // n0x0486 c0x0000 (---------------) + is-a-cubicle-slave 0x0004294d, // n0x0487 c0x0000 (---------------) + is-a-democrat 0x00051b4d, // n0x0488 c0x0000 (---------------) + is-a-designer 0x0005460b, // n0x0489 c0x0000 (---------------) + is-a-doctor 0x00057815, // n0x048a c0x0000 (---------------) + is-a-financialadvisor 0x0005d009, // n0x048b c0x0000 (---------------) + is-a-geek 0x0005dd4a, // n0x048c c0x0000 (---------------) + is-a-green 0x00066249, // n0x048d c0x0000 (---------------) + is-a-guru 0x0006f510, // n0x048e c0x0000 (---------------) + is-a-hard-worker 0x000712cb, // n0x048f c0x0000 (---------------) + is-a-hunter 0x0007810f, // n0x0490 c0x0000 (---------------) + is-a-landscaper 0x0007a80b, // n0x0491 c0x0000 (---------------) + is-a-lawyer 0x0008dc0c, // n0x0492 c0x0000 (---------------) + is-a-liberal 0x00090710, // n0x0493 c0x0000 (---------------) + is-a-libertarian 0x0011a5ca, // n0x0494 c0x0000 (---------------) + is-a-llama 0x0011b30d, // n0x0495 c0x0000 (---------------) + is-a-musician 0x000ee94e, // n0x0496 c0x0000 (---------------) + is-a-nascarfan 0x000d864a, // n0x0497 c0x0000 (---------------) + is-a-nurse 0x00119e8c, // n0x0498 c0x0000 (---------------) + is-a-painter 0x000f2b14, // n0x0499 c0x0000 (---------------) + is-a-personaltrainer 0x000ee2d1, // n0x049a c0x0000 (---------------) + is-a-photographer 0x0009e98b, // n0x049b c0x0000 (---------------) + is-a-player 0x0009ef4f, // n0x049c c0x0000 (---------------) + is-a-republican 0x000a0a0d, // n0x049d c0x0000 (---------------) + is-a-rockstar 0x000a218e, // n0x049e c0x0000 (---------------) + is-a-socialist 0x000a6d4c, // n0x049f c0x0000 (---------------) + is-a-student 0x000a7c0c, // n0x04a0 c0x0000 (---------------) + is-a-teacher 0x000a8fcb, // n0x04a1 c0x0000 (---------------) + is-a-techie 0x000ad4ce, // n0x04a2 c0x0000 (---------------) + is-a-therapist 0x000b2790, // n0x04a3 c0x0000 (---------------) + is-an-accountant 0x000bc4cb, // n0x04a4 c0x0000 (---------------) + is-an-actor 0x000c99cd, // n0x04a5 c0x0000 (---------------) + is-an-actress 0x000d6c4f, // n0x04a6 c0x0000 (---------------) + is-an-anarchist 0x000b680c, // n0x04a7 c0x0000 (---------------) + is-an-artist 0x000b758e, // n0x04a8 c0x0000 (---------------) + is-an-engineer 0x000bf6d1, // n0x04a9 c0x0000 (---------------) + is-an-entertainer 0x000c74cc, // n0x04aa c0x0000 (---------------) + is-certified 0x000e8287, // n0x04ab c0x0000 (---------------) + is-gone 0x0012ebcd, // n0x04ac c0x0000 (---------------) + is-into-anime 0x000d310c, // n0x04ad c0x0000 (---------------) + is-into-cars 0x000d3e10, // n0x04ae c0x0000 (---------------) + is-into-cartoons 0x000d980d, // n0x04af c0x0000 (---------------) + is-into-games 0x000dddc7, // n0x04b0 c0x0000 (---------------) + is-leet 0x000e8f10, // n0x04b1 c0x0000 (---------------) + is-not-certified 0x000fcd08, // n0x04b2 c0x0000 (---------------) + is-slick 0x0010938b, // n0x04b3 c0x0000 (---------------) + is-uberleet 0x0013c30f, // n0x04b4 c0x0000 (---------------) + is-with-theband 0x000584c8, // n0x04b5 c0x0000 (---------------) + isa-geek 0x000609cd, // n0x04b6 c0x0000 (---------------) + isa-hockeynut 0x0009d050, // n0x04b7 c0x0000 (---------------) + issmarterthanyou 0x0009e1c3, // n0x04b8 c0x0000 (---------------) + jpn 0x0000d382, // n0x04b9 c0x0000 (---------------) + kr 0x000caf09, // n0x04ba c0x0000 (---------------) + likes-pie 0x0004fd0a, // n0x04bb c0x0000 (---------------) + likescandy 0x00041208, // n0x04bc c0x0000 (---------------) + neat-url 0x00001e82, // n0x04bd c0x0000 (---------------) + no 0x0003da4a, // n0x04be c0x0000 (---------------) + operaunite 0x000600c2, // n0x04bf c0x0000 (---------------) + qc 0x00041587, // n0x04c0 c0x0000 (---------------) + rhcloud 0x00001042, // n0x04c1 c0x0000 (---------------) + ro 0x0000aac2, // n0x04c2 c0x0000 (---------------) + ru 0x00000602, // n0x04c3 c0x0000 (---------------) + sa 0x00067c90, // n0x04c4 c0x0000 (---------------) + saves-the-whales 0x00004802, // n0x04c5 c0x0000 (---------------) + se 0x0010c246, // n0x04c6 c0x0000 (---------------) + selfip 0x0008870e, // n0x04c7 c0x0000 (---------------) + sells-for-less 0x0009984b, // n0x04c8 c0x0000 (---------------) + sells-for-u 0x000d7408, // n0x04c9 c0x0000 (---------------) + servebbs 0x000da44a, // n0x04ca c0x0000 (---------------) + simple-url 0x000df64d, // n0x04cb c0x0000 (---------------) + space-to-rent 0x00113a0c, // n0x04cc c0x0000 (---------------) + teaches-yoga 0x000000c2, // n0x04cd c0x0000 (---------------) + uk 0x00001682, // n0x04ce c0x0000 (---------------) + us 0x00019e02, // n0x04cf c0x0000 (---------------) + uy 0x000606ca, // n0x04d0 c0x0000 (---------------) + withgoogle 0x0012d6ce, // n0x04d1 c0x0000 (---------------) + writesthisblog 0x00003e02, // n0x04d2 c0x0000 (---------------) + za 0x0ec37487, // n0x04d3 c0x003b (n0x04ea-n0x04f2) + compute 0x0f037489, // n0x04d4 c0x003c (n0x04f2-n0x04f4) + compute-1 0x00023c43, // n0x04d5 c0x0000 (---------------) + elb 0x00000a42, // n0x04d6 c0x0000 (---------------) + s3 0x00115c11, // n0x04d7 c0x0000 (---------------) + s3-ap-northeast-1 0x00096c51, // n0x04d8 c0x0000 (---------------) + s3-ap-southeast-1 0x000b0711, // n0x04d9 c0x0000 (---------------) + s3-ap-southeast-2 0x000e144c, // n0x04da c0x0000 (---------------) + s3-eu-west-1 0x00123e95, // n0x04db c0x0000 (---------------) + s3-fips-us-gov-west-1 0x000f160c, // n0x04dc c0x0000 (---------------) + s3-sa-east-1 0x000f55d0, // n0x04dd c0x0000 (---------------) + s3-us-gov-west-1 0x00100c4c, // n0x04de c0x0000 (---------------) + s3-us-west-1 0x001351cc, // n0x04df c0x0000 (---------------) + s3-us-west-2 0x0013a719, // n0x04e0 c0x0000 (---------------) + s3-website-ap-northeast-1 0x001452d9, // n0x04e1 c0x0000 (---------------) + s3-website-ap-southeast-1 0x0014d019, // n0x04e2 c0x0000 (---------------) + s3-website-ap-southeast-2 0x0015a414, // n0x04e3 c0x0000 (---------------) + s3-website-eu-west-1 0x00000a54, // n0x04e4 c0x0000 (---------------) + s3-website-sa-east-1 0x00003594, // n0x04e5 c0x0000 (---------------) + s3-website-us-east-1 0x00008b58, // n0x04e6 c0x0000 (---------------) + s3-website-us-gov-west-1 0x0000bb94, // n0x04e7 c0x0000 (---------------) + s3-website-us-west-1 0x0000cd94, // n0x04e8 c0x0000 (---------------) + s3-website-us-west-2 0x00003849, // n0x04e9 c0x0000 (---------------) + us-east-1 0x00115cce, // n0x04ea c0x0000 (---------------) + ap-northeast-1 0x00096d0e, // n0x04eb c0x0000 (---------------) + ap-southeast-1 0x000b07ce, // n0x04ec c0x0000 (---------------) + ap-southeast-2 0x000e1509, // n0x04ed c0x0000 (---------------) + eu-west-1 0x00000d09, // n0x04ee c0x0000 (---------------) + sa-east-1 0x00008e0d, // n0x04ef c0x0000 (---------------) + us-gov-west-1 0x0000be49, // n0x04f0 c0x0000 (---------------) + us-west-1 0x0000d049, // n0x04f1 c0x0000 (---------------) + us-west-2 0x00131b83, // n0x04f2 c0x0000 (---------------) + z-1 0x000ab403, // n0x04f3 c0x0000 (---------------) + z-2 0x0020a3c2, // n0x04f4 c0x0000 (---------------) + I ac 0x00209382, // n0x04f5 c0x0000 (---------------) + I co 0x00203b42, // n0x04f6 c0x0000 (---------------) + I ed 0x0020cbc2, // n0x04f7 c0x0000 (---------------) + I fi 0x00208ec2, // n0x04f8 c0x0000 (---------------) + I go 0x00201002, // n0x04f9 c0x0000 (---------------) + I or 0x00200602, // n0x04fa c0x0000 (---------------) + I sa 0x002370c3, // n0x04fb c0x0000 (---------------) + I com 0x00215b83, // n0x04fc c0x0000 (---------------) + I edu 0x00208ec3, // n0x04fd c0x0000 (---------------) + I gov 0x00214b83, // n0x04fe c0x0000 (---------------) + I inf 0x0024bdc3, // n0x04ff c0x0000 (---------------) + I net 0x0024af03, // n0x0500 c0x0000 (---------------) + I org 0x0012d948, // n0x0501 c0x0000 (---------------) + blogspot 0x002370c3, // n0x0502 c0x0000 (---------------) + I com 0x00215b83, // n0x0503 c0x0000 (---------------) + I edu 0x0024bdc3, // n0x0504 c0x0000 (---------------) + I net 0x0024af03, // n0x0505 c0x0000 (---------------) + I org 0x00012283, // n0x0506 c0x0000 (---------------) + ath 0x00208ec3, // n0x0507 c0x0000 (---------------) + I gov 0x0012d948, // n0x0508 c0x0000 (---------------) + blogspot 0x0012d948, // n0x0509 c0x0000 (---------------) + blogspot 0x000370c3, // n0x050a c0x0000 (---------------) + com 0x0013180f, // n0x050b c0x0000 (---------------) + fuettertdasnetz 0x0001bfca, // n0x050c c0x0000 (---------------) + isteingeek 0x00116ec7, // n0x050d c0x0000 (---------------) + istmein 0x00058f4a, // n0x050e c0x0000 (---------------) + lebtimnetz 0x000cb70a, // n0x050f c0x0000 (---------------) + leitungsen 0x00127a8d, // n0x0510 c0x0000 (---------------) + traeumtgerade 0x0012d948, // n0x0511 c0x0000 (---------------) + blogspot 0x002370c3, // n0x0512 c0x0000 (---------------) + I com 0x00215b83, // n0x0513 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0514 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0515 c0x0000 (---------------) + I net 0x0024af03, // n0x0516 c0x0000 (---------------) + I org 0x00209243, // n0x0517 c0x0000 (---------------) + I art 0x002370c3, // n0x0518 c0x0000 (---------------) + I com 0x00215b83, // n0x0519 c0x0000 (---------------) + I edu 0x00223ac3, // n0x051a c0x0000 (---------------) + I gob 0x00208ec3, // n0x051b c0x0000 (---------------) + I gov 0x00210703, // n0x051c c0x0000 (---------------) + I mil 0x0024bdc3, // n0x051d c0x0000 (---------------) + I net 0x0024af03, // n0x051e c0x0000 (---------------) + I org 0x002d1f03, // n0x051f c0x0000 (---------------) + I sld 0x00200b03, // n0x0520 c0x0000 (---------------) + I web 0x00209243, // n0x0521 c0x0000 (---------------) + I art 0x002c5c84, // n0x0522 c0x0000 (---------------) + I asso 0x002370c3, // n0x0523 c0x0000 (---------------) + I com 0x00215b83, // n0x0524 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0525 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0526 c0x0000 (---------------) + I net 0x0024af03, // n0x0527 c0x0000 (---------------) + I org 0x0020e6c3, // n0x0528 c0x0000 (---------------) + I pol 0x002370c3, // n0x0529 c0x0000 (---------------) + I com 0x00215b83, // n0x052a c0x0000 (---------------) + I edu 0x00247ac3, // n0x052b c0x0000 (---------------) + I fin 0x00223ac3, // n0x052c c0x0000 (---------------) + I gob 0x00208ec3, // n0x052d c0x0000 (---------------) + I gov 0x00214b84, // n0x052e c0x0000 (---------------) + I info 0x003029c3, // n0x052f c0x0000 (---------------) + I k12 0x00216083, // n0x0530 c0x0000 (---------------) + I med 0x00210703, // n0x0531 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x0532 c0x0000 (---------------) + I net 0x0024af03, // n0x0533 c0x0000 (---------------) + I org 0x00206683, // n0x0534 c0x0000 (---------------) + I pro 0x00241083, // n0x0535 c0x0000 (---------------) + I aip 0x002370c3, // n0x0536 c0x0000 (---------------) + I com 0x00215b83, // n0x0537 c0x0000 (---------------) + I edu 0x00246a83, // n0x0538 c0x0000 (---------------) + I fie 0x00208ec3, // n0x0539 c0x0000 (---------------) + I gov 0x00219003, // n0x053a c0x0000 (---------------) + I lib 0x00216083, // n0x053b c0x0000 (---------------) + I med 0x0024af03, // n0x053c c0x0000 (---------------) + I org 0x00220fc3, // n0x053d c0x0000 (---------------) + I pri 0x00209f04, // n0x053e c0x0000 (---------------) + I riik 0x002370c3, // n0x053f c0x0000 (---------------) + I com 0x00215b83, // n0x0540 c0x0000 (---------------) + I edu 0x00297403, // n0x0541 c0x0000 (---------------) + I eun 0x00208ec3, // n0x0542 c0x0000 (---------------) + I gov 0x00210703, // n0x0543 c0x0000 (---------------) + I mil 0x002592c4, // n0x0544 c0x0000 (---------------) + I name 0x0024bdc3, // n0x0545 c0x0000 (---------------) + I net 0x0024af03, // n0x0546 c0x0000 (---------------) + I org 0x0021b783, // n0x0547 c0x0000 (---------------) + I sci 0x132370c3, // n0x0548 c0x004c (n0x054d-n0x054e) + I com 0x00215b83, // n0x0549 c0x0000 (---------------) + I edu 0x00223ac3, // n0x054a c0x0000 (---------------) + I gob 0x0020fc03, // n0x054b c0x0000 (---------------) + I nom 0x0024af03, // n0x054c c0x0000 (---------------) + I org 0x0012d948, // n0x054d c0x0000 (---------------) + blogspot 0x002e0545, // n0x054e c0x0000 (---------------) + I aland 0x0012d948, // n0x054f c0x0000 (---------------) + blogspot 0x00006143, // n0x0550 c0x0000 (---------------) + iki 0x00312dc8, // n0x0551 c0x0000 (---------------) + I aeroport 0x00255447, // n0x0552 c0x0000 (---------------) + I assedic 0x002c5c84, // n0x0553 c0x0000 (---------------) + I asso 0x00310106, // n0x0554 c0x0000 (---------------) + I avocat 0x00323d46, // n0x0555 c0x0000 (---------------) + I avoues 0x0012d948, // n0x0556 c0x0000 (---------------) + blogspot 0x002fa343, // n0x0557 c0x0000 (---------------) + I cci 0x002b4d49, // n0x0558 c0x0000 (---------------) + I chambagri 0x00279415, // n0x0559 c0x0000 (---------------) + I chirurgiens-dentistes 0x002370c3, // n0x055a c0x0000 (---------------) + I com 0x00300812, // n0x055b c0x0000 (---------------) + I experts-comptables 0x003005cf, // n0x055c c0x0000 (---------------) + I geometre-expert 0x002d80c4, // n0x055d c0x0000 (---------------) + I gouv 0x0030b9c5, // n0x055e c0x0000 (---------------) + I greta 0x002ab690, // n0x055f c0x0000 (---------------) + I huissier-justice 0x0029e587, // n0x0560 c0x0000 (---------------) + I medecin 0x0020fc03, // n0x0561 c0x0000 (---------------) + I nom 0x00242648, // n0x0562 c0x0000 (---------------) + I notaires 0x002c4b8a, // n0x0563 c0x0000 (---------------) + I pharmacien 0x00202a84, // n0x0564 c0x0000 (---------------) + I port 0x002cdec3, // n0x0565 c0x0000 (---------------) + I prd 0x00223146, // n0x0566 c0x0000 (---------------) + I presse 0x00226782, // n0x0567 c0x0000 (---------------) + I tm 0x002b32cb, // n0x0568 c0x0000 (---------------) + I veterinaire 0x002370c3, // n0x0569 c0x0000 (---------------) + I com 0x00215b83, // n0x056a c0x0000 (---------------) + I edu 0x00208ec3, // n0x056b c0x0000 (---------------) + I gov 0x00210703, // n0x056c c0x0000 (---------------) + I mil 0x0024bdc3, // n0x056d c0x0000 (---------------) + I net 0x0024af03, // n0x056e c0x0000 (---------------) + I org 0x002d16c3, // n0x056f c0x0000 (---------------) + I pvt 0x00209382, // n0x0570 c0x0000 (---------------) + I co 0x0024bdc3, // n0x0571 c0x0000 (---------------) + I net 0x0024af03, // n0x0572 c0x0000 (---------------) + I org 0x002370c3, // n0x0573 c0x0000 (---------------) + I com 0x00215b83, // n0x0574 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0575 c0x0000 (---------------) + I gov 0x00210703, // n0x0576 c0x0000 (---------------) + I mil 0x0024af03, // n0x0577 c0x0000 (---------------) + I org 0x002370c3, // n0x0578 c0x0000 (---------------) + I com 0x00215b83, // n0x0579 c0x0000 (---------------) + I edu 0x00208ec3, // n0x057a c0x0000 (---------------) + I gov 0x0021fc43, // n0x057b c0x0000 (---------------) + I ltd 0x00202e03, // n0x057c c0x0000 (---------------) + I mod 0x0024af03, // n0x057d c0x0000 (---------------) + I org 0x0020a3c2, // n0x057e c0x0000 (---------------) + I ac 0x002370c3, // n0x057f c0x0000 (---------------) + I com 0x00215b83, // n0x0580 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0581 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0582 c0x0000 (---------------) + I net 0x0024af03, // n0x0583 c0x0000 (---------------) + I org 0x002c5c84, // n0x0584 c0x0000 (---------------) + I asso 0x002370c3, // n0x0585 c0x0000 (---------------) + I com 0x00215b83, // n0x0586 c0x0000 (---------------) + I edu 0x0020d4c4, // n0x0587 c0x0000 (---------------) + I mobi 0x0024bdc3, // n0x0588 c0x0000 (---------------) + I net 0x0024af03, // n0x0589 c0x0000 (---------------) + I org 0x0012d948, // n0x058a c0x0000 (---------------) + blogspot 0x002370c3, // n0x058b c0x0000 (---------------) + I com 0x00215b83, // n0x058c c0x0000 (---------------) + I edu 0x00208ec3, // n0x058d c0x0000 (---------------) + I gov 0x0024bdc3, // n0x058e c0x0000 (---------------) + I net 0x0024af03, // n0x058f c0x0000 (---------------) + I org 0x002370c3, // n0x0590 c0x0000 (---------------) + I com 0x00215b83, // n0x0591 c0x0000 (---------------) + I edu 0x00223ac3, // n0x0592 c0x0000 (---------------) + I gob 0x00201803, // n0x0593 c0x0000 (---------------) + I ind 0x00210703, // n0x0594 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x0595 c0x0000 (---------------) + I net 0x0024af03, // n0x0596 c0x0000 (---------------) + I org 0x00209382, // n0x0597 c0x0000 (---------------) + I co 0x002370c3, // n0x0598 c0x0000 (---------------) + I com 0x0024bdc3, // n0x0599 c0x0000 (---------------) + I net 0x0012d948, // n0x059a c0x0000 (---------------) + blogspot 0x002370c3, // n0x059b c0x0000 (---------------) + I com 0x00215b83, // n0x059c c0x0000 (---------------) + I edu 0x00208ec3, // n0x059d c0x0000 (---------------) + I gov 0x00273043, // n0x059e c0x0000 (---------------) + I idv 0x0024bdc3, // n0x059f c0x0000 (---------------) + I net 0x0024af03, // n0x05a0 c0x0000 (---------------) + I org 0x0035900a, // n0x05a1 c0x0000 (---------------) + I xn--55qx5d 0x002fd809, // n0x05a2 c0x0000 (---------------) + I xn--ciqpn 0x0030d94b, // n0x05a3 c0x0000 (---------------) + I xn--gmq050i 0x0030e28a, // n0x05a4 c0x0000 (---------------) + I xn--gmqw5a 0x0031b64a, // n0x05a5 c0x0000 (---------------) + I xn--io0a7i 0x00322a0b, // n0x05a6 c0x0000 (---------------) + I xn--lcvr32d 0x0033204a, // n0x05a7 c0x0000 (---------------) + I xn--mk0axi 0x0033650a, // n0x05a8 c0x0000 (---------------) + I xn--mxtq1m 0x0033968a, // n0x05a9 c0x0000 (---------------) + I xn--od0alg 0x0033990b, // n0x05aa c0x0000 (---------------) + I xn--od0aq3b 0x0034e889, // n0x05ab c0x0000 (---------------) + I xn--tn0ag 0x00351aca, // n0x05ac c0x0000 (---------------) + I xn--uc0atv 0x00351f8b, // n0x05ad c0x0000 (---------------) + I xn--uc0ay4a 0x00355dcb, // n0x05ae c0x0000 (---------------) + I xn--wcvs22d 0x0035ac0a, // n0x05af c0x0000 (---------------) + I xn--zf0avx 0x002370c3, // n0x05b0 c0x0000 (---------------) + I com 0x00215b83, // n0x05b1 c0x0000 (---------------) + I edu 0x00223ac3, // n0x05b2 c0x0000 (---------------) + I gob 0x00210703, // n0x05b3 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x05b4 c0x0000 (---------------) + I net 0x0024af03, // n0x05b5 c0x0000 (---------------) + I org 0x002370c3, // n0x05b6 c0x0000 (---------------) + I com 0x00254bc4, // n0x05b7 c0x0000 (---------------) + I from 0x002104c2, // n0x05b8 c0x0000 (---------------) + I iz 0x002592c4, // n0x05b9 c0x0000 (---------------) + I name 0x0021e245, // n0x05ba c0x0000 (---------------) + I adult 0x00209243, // n0x05bb c0x0000 (---------------) + I art 0x002c5c84, // n0x05bc c0x0000 (---------------) + I asso 0x002370c3, // n0x05bd c0x0000 (---------------) + I com 0x0023d9c4, // n0x05be c0x0000 (---------------) + I coop 0x00215b83, // n0x05bf c0x0000 (---------------) + I edu 0x00248344, // n0x05c0 c0x0000 (---------------) + I firm 0x002d80c4, // n0x05c1 c0x0000 (---------------) + I gouv 0x00214b84, // n0x05c2 c0x0000 (---------------) + I info 0x00216083, // n0x05c3 c0x0000 (---------------) + I med 0x0024bdc3, // n0x05c4 c0x0000 (---------------) + I net 0x0024af03, // n0x05c5 c0x0000 (---------------) + I org 0x002f2c45, // n0x05c6 c0x0000 (---------------) + I perso 0x0020e6c3, // n0x05c7 c0x0000 (---------------) + I pol 0x00206683, // n0x05c8 c0x0000 (---------------) + I pro 0x0029cd03, // n0x05c9 c0x0000 (---------------) + I rel 0x0022fa84, // n0x05ca c0x0000 (---------------) + I shop 0x00241ac4, // n0x05cb c0x0000 (---------------) + I 2000 0x00223d45, // n0x05cc c0x0000 (---------------) + I agrar 0x0012d948, // n0x05cd c0x0000 (---------------) + blogspot 0x00254384, // n0x05ce c0x0000 (---------------) + I bolt 0x0023e2c6, // n0x05cf c0x0000 (---------------) + I casino 0x0022da04, // n0x05d0 c0x0000 (---------------) + I city 0x00209382, // n0x05d1 c0x0000 (---------------) + I co 0x002a2dc7, // n0x05d2 c0x0000 (---------------) + I erotica 0x00314d47, // n0x05d3 c0x0000 (---------------) + I erotika 0x002476c4, // n0x05d4 c0x0000 (---------------) + I film 0x00251805, // n0x05d5 c0x0000 (---------------) + I forum 0x002d9a05, // n0x05d6 c0x0000 (---------------) + I games 0x0029aa05, // n0x05d7 c0x0000 (---------------) + I hotel 0x00214b84, // n0x05d8 c0x0000 (---------------) + I info 0x0025ab08, // n0x05d9 c0x0000 (---------------) + I ingatlan 0x002ea5c6, // n0x05da c0x0000 (---------------) + I jogasz 0x002ed108, // n0x05db c0x0000 (---------------) + I konyvelo 0x0021bb05, // n0x05dc c0x0000 (---------------) + I lakas 0x00216085, // n0x05dd c0x0000 (---------------) + I media 0x002407c4, // n0x05de c0x0000 (---------------) + I news 0x0024af03, // n0x05df c0x0000 (---------------) + I org 0x002ce944, // n0x05e0 c0x0000 (---------------) + I priv 0x002b50c6, // n0x05e1 c0x0000 (---------------) + I reklam 0x00223243, // n0x05e2 c0x0000 (---------------) + I sex 0x0022fa84, // n0x05e3 c0x0000 (---------------) + I shop 0x002d8b05, // n0x05e4 c0x0000 (---------------) + I sport 0x002cae84, // n0x05e5 c0x0000 (---------------) + I suli 0x0021cc44, // n0x05e6 c0x0000 (---------------) + I szex 0x00226782, // n0x05e7 c0x0000 (---------------) + I tm 0x00273646, // n0x05e8 c0x0000 (---------------) + I tozsde 0x00346846, // n0x05e9 c0x0000 (---------------) + I utazas 0x002e9dc5, // n0x05ea c0x0000 (---------------) + I video 0x0020a3c2, // n0x05eb c0x0000 (---------------) + I ac 0x00305743, // n0x05ec c0x0000 (---------------) + I biz 0x00209382, // n0x05ed c0x0000 (---------------) + I co 0x00208ec2, // n0x05ee c0x0000 (---------------) + I go 0x00210703, // n0x05ef c0x0000 (---------------) + I mil 0x002267c2, // n0x05f0 c0x0000 (---------------) + I my 0x0024bdc3, // n0x05f1 c0x0000 (---------------) + I net 0x00201002, // n0x05f2 c0x0000 (---------------) + I or 0x0025e643, // n0x05f3 c0x0000 (---------------) + I sch 0x00200b03, // n0x05f4 c0x0000 (---------------) + I web 0x0012d948, // n0x05f5 c0x0000 (---------------) + blogspot 0x00208ec3, // n0x05f6 c0x0000 (---------------) + I gov 0x18209382, // n0x05f7 c0x0060 (n0x05f8-n0x05f9) o I co 0x0012d948, // n0x05f8 c0x0000 (---------------) + blogspot 0x0020a3c2, // n0x05f9 c0x0000 (---------------) + I ac 0x18a09382, // n0x05fa c0x0062 (n0x0600-n0x0602) + I co 0x002370c3, // n0x05fb c0x0000 (---------------) + I com 0x0024bdc3, // n0x05fc c0x0000 (---------------) + I net 0x0024af03, // n0x05fd c0x0000 (---------------) + I org 0x0020c502, // n0x05fe c0x0000 (---------------) + I tt 0x00288182, // n0x05ff c0x0000 (---------------) + I tv 0x0021fc43, // n0x0600 c0x0000 (---------------) + I ltd 0x002cab03, // n0x0601 c0x0000 (---------------) + I plc 0x0020a3c2, // n0x0602 c0x0000 (---------------) + I ac 0x0012d948, // n0x0603 c0x0000 (---------------) + blogspot 0x00209382, // n0x0604 c0x0000 (---------------) + I co 0x00215b83, // n0x0605 c0x0000 (---------------) + I edu 0x00248344, // n0x0606 c0x0000 (---------------) + I firm 0x00203203, // n0x0607 c0x0000 (---------------) + I gen 0x00208ec3, // n0x0608 c0x0000 (---------------) + I gov 0x00201803, // n0x0609 c0x0000 (---------------) + I ind 0x00210703, // n0x060a c0x0000 (---------------) + I mil 0x0024bdc3, // n0x060b c0x0000 (---------------) + I net 0x00213103, // n0x060c c0x0000 (---------------) + I nic 0x0024af03, // n0x060d c0x0000 (---------------) + I org 0x0021b703, // n0x060e c0x0000 (---------------) + I res 0x00100193, // n0x060f c0x0000 (---------------) + barrel-of-knowledge 0x001011d4, // n0x0610 c0x0000 (---------------) + barrell-of-knowledge 0x00011746, // n0x0611 c0x0000 (---------------) + dyndns 0x0004d5c7, // n0x0612 c0x0000 (---------------) + for-our 0x00111149, // n0x0613 c0x0000 (---------------) + groks-the 0x0002ff8a, // n0x0614 c0x0000 (---------------) + groks-this 0x00076ccd, // n0x0615 c0x0000 (---------------) + here-for-more 0x0001698a, // n0x0616 c0x0000 (---------------) + knowsitall 0x0010c246, // n0x0617 c0x0000 (---------------) + selfip 0x0003eb86, // n0x0618 c0x0000 (---------------) + webhop 0x00220842, // n0x0619 c0x0000 (---------------) + I eu 0x002370c3, // n0x061a c0x0000 (---------------) + I com 0x000aa506, // n0x061b c0x0000 (---------------) + github 0x002370c3, // n0x061c c0x0000 (---------------) + I com 0x00215b83, // n0x061d c0x0000 (---------------) + I edu 0x00208ec3, // n0x061e c0x0000 (---------------) + I gov 0x00210703, // n0x061f c0x0000 (---------------) + I mil 0x0024bdc3, // n0x0620 c0x0000 (---------------) + I net 0x0024af03, // n0x0621 c0x0000 (---------------) + I org 0x0020a3c2, // n0x0622 c0x0000 (---------------) + I ac 0x00209382, // n0x0623 c0x0000 (---------------) + I co 0x00208ec3, // n0x0624 c0x0000 (---------------) + I gov 0x00203d42, // n0x0625 c0x0000 (---------------) + I id 0x0024bdc3, // n0x0626 c0x0000 (---------------) + I net 0x0024af03, // n0x0627 c0x0000 (---------------) + I org 0x0025e643, // n0x0628 c0x0000 (---------------) + I sch 0x00329f8f, // n0x0629 c0x0000 (---------------) + I xn--mgba3a4f16a 0x0032a34e, // n0x062a c0x0000 (---------------) + I xn--mgba3a4fra 0x002370c3, // n0x062b c0x0000 (---------------) + I com 0x00045d47, // n0x062c c0x0000 (---------------) + cupcake 0x00215b83, // n0x062d c0x0000 (---------------) + I edu 0x00208ec3, // n0x062e c0x0000 (---------------) + I gov 0x00216fc3, // n0x062f c0x0000 (---------------) + I int 0x0024bdc3, // n0x0630 c0x0000 (---------------) + I net 0x0024af03, // n0x0631 c0x0000 (---------------) + I org 0x00200142, // n0x0632 c0x0000 (---------------) + I ag 0x0034ea49, // n0x0633 c0x0000 (---------------) + I agrigento 0x00201782, // n0x0634 c0x0000 (---------------) + I al 0x00267f8b, // n0x0635 c0x0000 (---------------) + I alessandria 0x002b444a, // n0x0636 c0x0000 (---------------) + I alto-adige 0x00239549, // n0x0637 c0x0000 (---------------) + I altoadige 0x00200382, // n0x0638 c0x0000 (---------------) + I an 0x0021d746, // n0x0639 c0x0000 (---------------) + I ancona 0x002898d5, // n0x063a c0x0000 (---------------) + I andria-barletta-trani 0x002680d5, // n0x063b c0x0000 (---------------) + I andria-trani-barletta 0x0029a3d3, // n0x063c c0x0000 (---------------) + I andriabarlettatrani 0x00268653, // n0x063d c0x0000 (---------------) + I andriatranibarletta 0x00200642, // n0x063e c0x0000 (---------------) + I ao 0x002b0005, // n0x063f c0x0000 (---------------) + I aosta 0x002e0b45, // n0x0640 c0x0000 (---------------) + I aoste 0x00208a02, // n0x0641 c0x0000 (---------------) + I ap 0x00275382, // n0x0642 c0x0000 (---------------) + I aq 0x002cff46, // n0x0643 c0x0000 (---------------) + I aquila 0x00202b82, // n0x0644 c0x0000 (---------------) + I ar 0x002e1fc6, // n0x0645 c0x0000 (---------------) + I arezzo 0x00294f4d, // n0x0646 c0x0000 (---------------) + I ascoli-piceno 0x0023908c, // n0x0647 c0x0000 (---------------) + I ascolipiceno 0x00232004, // n0x0648 c0x0000 (---------------) + I asti 0x00200242, // n0x0649 c0x0000 (---------------) + I at 0x002032c2, // n0x064a c0x0000 (---------------) + I av 0x002a7088, // n0x064b c0x0000 (---------------) + I avellino 0x00201b02, // n0x064c c0x0000 (---------------) + I ba 0x002959c6, // n0x064d c0x0000 (---------------) + I balsan 0x00306d04, // n0x064e c0x0000 (---------------) + I bari 0x00289a95, // n0x064f c0x0000 (---------------) + I barletta-trani-andria 0x0029a553, // n0x0650 c0x0000 (---------------) + I barlettatraniandria 0x00253807, // n0x0651 c0x0000 (---------------) + I belluno 0x00334689, // n0x0652 c0x0000 (---------------) + I benevento 0x00205287, // n0x0653 c0x0000 (---------------) + I bergamo 0x00349402, // n0x0654 c0x0000 (---------------) + I bg 0x00200002, // n0x0655 c0x0000 (---------------) + I bi 0x003569c6, // n0x0656 c0x0000 (---------------) + I biella 0x0020e8c2, // n0x0657 c0x0000 (---------------) + I bl 0x0012d948, // n0x0658 c0x0000 (---------------) + blogspot 0x002566c2, // n0x0659 c0x0000 (---------------) + I bn 0x002120c2, // n0x065a c0x0000 (---------------) + I bo 0x00339cc7, // n0x065b c0x0000 (---------------) + I bologna 0x002ec107, // n0x065c c0x0000 (---------------) + I bolzano 0x00217a05, // n0x065d c0x0000 (---------------) + I bozen 0x00202282, // n0x065e c0x0000 (---------------) + I br 0x0021b6c7, // n0x065f c0x0000 (---------------) + I brescia 0x0021b888, // n0x0660 c0x0000 (---------------) + I brindisi 0x00200b82, // n0x0661 c0x0000 (---------------) + I bs 0x00227382, // n0x0662 c0x0000 (---------------) + I bt 0x0022d182, // n0x0663 c0x0000 (---------------) + I bz 0x00213182, // n0x0664 c0x0000 (---------------) + I ca 0x0023f1c8, // n0x0665 c0x0000 (---------------) + I cagliari 0x00240c4d, // n0x0666 c0x0000 (---------------) + I caltanissetta 0x0024da8f, // n0x0667 c0x0000 (---------------) + I campidano-medio 0x0024de4e, // n0x0668 c0x0000 (---------------) + I campidanomedio 0x0035b68a, // n0x0669 c0x0000 (---------------) + I campobasso 0x002e6111, // n0x066a c0x0000 (---------------) + I carbonia-iglesias 0x002e6590, // n0x066b c0x0000 (---------------) + I carboniaiglesias 0x0027ca0d, // n0x066c c0x0000 (---------------) + I carrara-massa 0x0027cd4c, // n0x066d c0x0000 (---------------) + I carraramassa 0x00260107, // n0x066e c0x0000 (---------------) + I caserta 0x003101c7, // n0x066f c0x0000 (---------------) + I catania 0x00243fc9, // n0x0670 c0x0000 (---------------) + I catanzaro 0x00232102, // n0x0671 c0x0000 (---------------) + I cb 0x00208842, // n0x0672 c0x0000 (---------------) + I ce 0x0024f7cc, // n0x0673 c0x0000 (---------------) + I cesena-forli 0x0024facb, // n0x0674 c0x0000 (---------------) + I cesenaforli 0x002002c2, // n0x0675 c0x0000 (---------------) + I ch 0x002a9186, // n0x0676 c0x0000 (---------------) + I chieti 0x0021b7c2, // n0x0677 c0x0000 (---------------) + I ci 0x00200482, // n0x0678 c0x0000 (---------------) + I cl 0x00234a02, // n0x0679 c0x0000 (---------------) + I cn 0x00209382, // n0x067a c0x0000 (---------------) + I co 0x002370c4, // n0x067b c0x0000 (---------------) + I como 0x00240507, // n0x067c c0x0000 (---------------) + I cosenza 0x002148c2, // n0x067d c0x0000 (---------------) + I cr 0x00243887, // n0x067e c0x0000 (---------------) + I cremona 0x00244787, // n0x067f c0x0000 (---------------) + I crotone 0x00208b02, // n0x0680 c0x0000 (---------------) + I cs 0x002236c2, // n0x0681 c0x0000 (---------------) + I ct 0x00245c05, // n0x0682 c0x0000 (---------------) + I cuneo 0x00225882, // n0x0683 c0x0000 (---------------) + I cz 0x00250c4e, // n0x0684 c0x0000 (---------------) + I dell-ogliastra 0x0025970d, // n0x0685 c0x0000 (---------------) + I dellogliastra 0x00215b83, // n0x0686 c0x0000 (---------------) + I edu 0x002015c2, // n0x0687 c0x0000 (---------------) + I en 0x0025c604, // n0x0688 c0x0000 (---------------) + I enna 0x002cf342, // n0x0689 c0x0000 (---------------) + I fc 0x002204c2, // n0x068a c0x0000 (---------------) + I fe 0x00329985, // n0x068b c0x0000 (---------------) + I fermo 0x00336007, // n0x068c c0x0000 (---------------) + I ferrara 0x0033cf02, // n0x068d c0x0000 (---------------) + I fg 0x0020cbc2, // n0x068e c0x0000 (---------------) + I fi 0x00248187, // n0x068f c0x0000 (---------------) + I firenze 0x0024c108, // n0x0690 c0x0000 (---------------) + I florence 0x0033ee02, // n0x0691 c0x0000 (---------------) + I fm 0x00214c06, // n0x0692 c0x0000 (---------------) + I foggia 0x0024f64c, // n0x0693 c0x0000 (---------------) + I forli-cesena 0x0024f98b, // n0x0694 c0x0000 (---------------) + I forlicesena 0x00221802, // n0x0695 c0x0000 (---------------) + I fr 0x00269b09, // n0x0696 c0x0000 (---------------) + I frosinone 0x00203202, // n0x0697 c0x0000 (---------------) + I ge 0x00209a05, // n0x0698 c0x0000 (---------------) + I genoa 0x00212e06, // n0x0699 c0x0000 (---------------) + I genova 0x00208ec2, // n0x069a c0x0000 (---------------) + I go 0x002bd707, // n0x069b c0x0000 (---------------) + I gorizia 0x00208ec3, // n0x069c c0x0000 (---------------) + I gov 0x00208982, // n0x069d c0x0000 (---------------) + I gr 0x00233508, // n0x069e c0x0000 (---------------) + I grosseto 0x002e6351, // n0x069f c0x0000 (---------------) + I iglesias-carbonia 0x002e6790, // n0x06a0 c0x0000 (---------------) + I iglesiascarbonia 0x002008c2, // n0x06a1 c0x0000 (---------------) + I im 0x00356307, // n0x06a2 c0x0000 (---------------) + I imperia 0x002030c2, // n0x06a3 c0x0000 (---------------) + I is 0x00247907, // n0x06a4 c0x0000 (---------------) + I isernia 0x0020d382, // n0x06a5 c0x0000 (---------------) + I kr 0x002a6949, // n0x06a6 c0x0000 (---------------) + I la-spezia 0x002cff07, // n0x06a7 c0x0000 (---------------) + I laquila 0x00272c08, // n0x06a8 c0x0000 (---------------) + I laspezia 0x00214446, // n0x06a9 c0x0000 (---------------) + I latina 0x00215e02, // n0x06aa c0x0000 (---------------) + I lc 0x00205042, // n0x06ab c0x0000 (---------------) + I le 0x00356dc5, // n0x06ac c0x0000 (---------------) + I lecce 0x002234c5, // n0x06ad c0x0000 (---------------) + I lecco 0x002017c2, // n0x06ae c0x0000 (---------------) + I li 0x00216bc7, // n0x06af c0x0000 (---------------) + I livorno 0x002004c2, // n0x06b0 c0x0000 (---------------) + I lo 0x00250684, // n0x06b1 c0x0000 (---------------) + I lodi 0x0021e302, // n0x06b2 c0x0000 (---------------) + I lt 0x002082c2, // n0x06b3 c0x0000 (---------------) + I lu 0x00227f05, // n0x06b4 c0x0000 (---------------) + I lucca 0x002fa9c8, // n0x06b5 c0x0000 (---------------) + I macerata 0x0026be47, // n0x06b6 c0x0000 (---------------) + I mantova 0x0027c88d, // n0x06b7 c0x0000 (---------------) + I massa-carrara 0x0027cc0c, // n0x06b8 c0x0000 (---------------) + I massacarrara 0x002a18c6, // n0x06b9 c0x0000 (---------------) + I matera 0x002028c2, // n0x06ba c0x0000 (---------------) + I mb 0x00261e42, // n0x06bb c0x0000 (---------------) + I mc 0x00201582, // n0x06bc c0x0000 (---------------) + I me 0x0024d90f, // n0x06bd c0x0000 (---------------) + I medio-campidano 0x0024dd0e, // n0x06be c0x0000 (---------------) + I mediocampidano 0x002d9a87, // n0x06bf c0x0000 (---------------) + I messina 0x00200742, // n0x06c0 c0x0000 (---------------) + I mi 0x003224c5, // n0x06c1 c0x0000 (---------------) + I milan 0x003224c6, // n0x06c2 c0x0000 (---------------) + I milano 0x0020fb42, // n0x06c3 c0x0000 (---------------) + I mn 0x00202e02, // n0x06c4 c0x0000 (---------------) + I mo 0x0027b0c6, // n0x06c5 c0x0000 (---------------) + I modena 0x002ba905, // n0x06c6 c0x0000 (---------------) + I monza 0x002ba90d, // n0x06c7 c0x0000 (---------------) + I monza-brianza 0x002bac55, // n0x06c8 c0x0000 (---------------) + I monza-e-della-brianza 0x002bb18c, // n0x06c9 c0x0000 (---------------) + I monzabrianza 0x002bb48d, // n0x06ca c0x0000 (---------------) + I monzaebrianza 0x002bb7d2, // n0x06cb c0x0000 (---------------) + I monzaedellabrianza 0x0020bb42, // n0x06cc c0x0000 (---------------) + I ms 0x0025f982, // n0x06cd c0x0000 (---------------) + I mt 0x00201202, // n0x06ce c0x0000 (---------------) + I na 0x0032a6c6, // n0x06cf c0x0000 (---------------) + I naples 0x0020e646, // n0x06d0 c0x0000 (---------------) + I napoli 0x00201e82, // n0x06d1 c0x0000 (---------------) + I no 0x00212e86, // n0x06d2 c0x0000 (---------------) + I novara 0x00205642, // n0x06d3 c0x0000 (---------------) + I nu 0x0034dec5, // n0x06d4 c0x0000 (---------------) + I nuoro 0x00201102, // n0x06d5 c0x0000 (---------------) + I og 0x00250d89, // n0x06d6 c0x0000 (---------------) + I ogliastra 0x0023d08c, // n0x06d7 c0x0000 (---------------) + I olbia-tempio 0x0023d3cb, // n0x06d8 c0x0000 (---------------) + I olbiatempio 0x00201002, // n0x06d9 c0x0000 (---------------) + I or 0x0024c548, // n0x06da c0x0000 (---------------) + I oristano 0x00201ec2, // n0x06db c0x0000 (---------------) + I ot 0x00202642, // n0x06dc c0x0000 (---------------) + I pa 0x002c4246, // n0x06dd c0x0000 (---------------) + I padova 0x0032bd45, // n0x06de c0x0000 (---------------) + I padua 0x0020e047, // n0x06df c0x0000 (---------------) + I palermo 0x00248b45, // n0x06e0 c0x0000 (---------------) + I parma 0x002c4a45, // n0x06e1 c0x0000 (---------------) + I pavia 0x00245dc2, // n0x06e2 c0x0000 (---------------) + I pc 0x0022fb82, // n0x06e3 c0x0000 (---------------) + I pd 0x00206742, // n0x06e4 c0x0000 (---------------) + I pe 0x00278407, // n0x06e5 c0x0000 (---------------) + I perugia 0x002b360d, // n0x06e6 c0x0000 (---------------) + I pesaro-urbino 0x002b398c, // n0x06e7 c0x0000 (---------------) + I pesarourbino 0x002ce607, // n0x06e8 c0x0000 (---------------) + I pescara 0x00253b02, // n0x06e9 c0x0000 (---------------) + I pg 0x00202482, // n0x06ea c0x0000 (---------------) + I pi 0x0025cc48, // n0x06eb c0x0000 (---------------) + I piacenza 0x00260984, // n0x06ec c0x0000 (---------------) + I pisa 0x002ad747, // n0x06ed c0x0000 (---------------) + I pistoia 0x0029e202, // n0x06ee c0x0000 (---------------) + I pn 0x00202a82, // n0x06ef c0x0000 (---------------) + I po 0x002cc749, // n0x06f0 c0x0000 (---------------) + I pordenone 0x00231a47, // n0x06f1 c0x0000 (---------------) + I potenza 0x00206682, // n0x06f2 c0x0000 (---------------) + I pr 0x00326585, // n0x06f3 c0x0000 (---------------) + I prato 0x002b3242, // n0x06f4 c0x0000 (---------------) + I pt 0x00214082, // n0x06f5 c0x0000 (---------------) + I pu 0x002d16c2, // n0x06f6 c0x0000 (---------------) + I pv 0x002d1d42, // n0x06f7 c0x0000 (---------------) + I pz 0x002020c2, // n0x06f8 c0x0000 (---------------) + I ra 0x00313e06, // n0x06f9 c0x0000 (---------------) + I ragusa 0x002ae547, // n0x06fa c0x0000 (---------------) + I ravenna 0x0022d382, // n0x06fb c0x0000 (---------------) + I rc 0x00205482, // n0x06fc c0x0000 (---------------) + I re 0x002be94f, // n0x06fd c0x0000 (---------------) + I reggio-calabria 0x00297f8d, // n0x06fe c0x0000 (---------------) + I reggio-emilia 0x0022d68e, // n0x06ff c0x0000 (---------------) + I reggiocalabria 0x00244b4c, // n0x0700 c0x0000 (---------------) + I reggioemilia 0x00202bc2, // n0x0701 c0x0000 (---------------) + I rg 0x00204cc2, // n0x0702 c0x0000 (---------------) + I ri 0x00204cc5, // n0x0703 c0x0000 (---------------) + I rieti 0x002c8346, // n0x0704 c0x0000 (---------------) + I rimini 0x0020cd02, // n0x0705 c0x0000 (---------------) + I rm 0x0020c5c2, // n0x0706 c0x0000 (---------------) + I rn 0x00201042, // n0x0707 c0x0000 (---------------) + I ro 0x0029fc04, // n0x0708 c0x0000 (---------------) + I roma 0x0029e504, // n0x0709 c0x0000 (---------------) + I rome 0x0024cc06, // n0x070a c0x0000 (---------------) + I rovigo 0x00200602, // n0x070b c0x0000 (---------------) + I sa 0x0028ee07, // n0x070c c0x0000 (---------------) + I salerno 0x0025dbc7, // n0x070d c0x0000 (---------------) + I sassari 0x00357ec6, // n0x070e c0x0000 (---------------) + I savona 0x00200bc2, // n0x070f c0x0000 (---------------) + I si 0x00297945, // n0x0710 c0x0000 (---------------) + I siena 0x002e7dc8, // n0x0711 c0x0000 (---------------) + I siracusa 0x00205902, // n0x0712 c0x0000 (---------------) + I so 0x003404c7, // n0x0713 c0x0000 (---------------) + I sondrio 0x00220f82, // n0x0714 c0x0000 (---------------) + I sp 0x002be902, // n0x0715 c0x0000 (---------------) + I sr 0x00205f42, // n0x0716 c0x0000 (---------------) + I ss 0x00204309, // n0x0717 c0x0000 (---------------) + I suedtirol 0x00214202, // n0x0718 c0x0000 (---------------) + I sv 0x00201b82, // n0x0719 c0x0000 (---------------) + I ta 0x0034ac07, // n0x071a c0x0000 (---------------) + I taranto 0x00200c42, // n0x071b c0x0000 (---------------) + I te 0x0023d20c, // n0x071c c0x0000 (---------------) + I tempio-olbia 0x0023d50b, // n0x071d c0x0000 (---------------) + I tempioolbia 0x002a1946, // n0x071e c0x0000 (---------------) + I teramo 0x002df045, // n0x071f c0x0000 (---------------) + I terni 0x002075c2, // n0x0720 c0x0000 (---------------) + I tn 0x00202842, // n0x0721 c0x0000 (---------------) + I to 0x002a5e06, // n0x0722 c0x0000 (---------------) + I torino 0x00224d42, // n0x0723 c0x0000 (---------------) + I tp 0x00205102, // n0x0724 c0x0000 (---------------) + I tr 0x00289755, // n0x0725 c0x0000 (---------------) + I trani-andria-barletta 0x00268295, // n0x0726 c0x0000 (---------------) + I trani-barletta-andria 0x0029a293, // n0x0727 c0x0000 (---------------) + I traniandriabarletta 0x002687d3, // n0x0728 c0x0000 (---------------) + I tranibarlettaandria 0x002d8c07, // n0x0729 c0x0000 (---------------) + I trapani 0x002d5d48, // n0x072a c0x0000 (---------------) + I trentino 0x002ddf46, // n0x072b c0x0000 (---------------) + I trento 0x002fd507, // n0x072c c0x0000 (---------------) + I treviso 0x002136c7, // n0x072d c0x0000 (---------------) + I trieste 0x00201a42, // n0x072e c0x0000 (---------------) + I ts 0x002b8e05, // n0x072f c0x0000 (---------------) + I turin 0x00288182, // n0x0730 c0x0000 (---------------) + I tv 0x00204182, // n0x0731 c0x0000 (---------------) + I ud 0x0022a785, // n0x0732 c0x0000 (---------------) + I udine 0x002b37cd, // n0x0733 c0x0000 (---------------) + I urbino-pesaro 0x002b3b0c, // n0x0734 c0x0000 (---------------) + I urbinopesaro 0x00203302, // n0x0735 c0x0000 (---------------) + I va 0x002cbf86, // n0x0736 c0x0000 (---------------) + I varese 0x0022c102, // n0x0737 c0x0000 (---------------) + I vb 0x00241e02, // n0x0738 c0x0000 (---------------) + I vc 0x00201382, // n0x0739 c0x0000 (---------------) + I ve 0x00299387, // n0x073a c0x0000 (---------------) + I venezia 0x002c4486, // n0x073b c0x0000 (---------------) + I venice 0x00285488, // n0x073c c0x0000 (---------------) + I verbania 0x002d7c88, // n0x073d c0x0000 (---------------) + I vercelli 0x0027eec6, // n0x073e c0x0000 (---------------) + I verona 0x00205782, // n0x073f c0x0000 (---------------) + I vi 0x002e978d, // n0x0740 c0x0000 (---------------) + I vibo-valentia 0x002e9acc, // n0x0741 c0x0000 (---------------) + I vibovalentia 0x002d8187, // n0x0742 c0x0000 (---------------) + I vicenza 0x002ebfc7, // n0x0743 c0x0000 (---------------) + I viterbo 0x00224282, // n0x0744 c0x0000 (---------------) + I vr 0x00206882, // n0x0745 c0x0000 (---------------) + I vs 0x00268dc2, // n0x0746 c0x0000 (---------------) + I vt 0x00203c02, // n0x0747 c0x0000 (---------------) + I vv 0x00209382, // n0x0748 c0x0000 (---------------) + I co 0x0024bdc3, // n0x0749 c0x0000 (---------------) + I net 0x0024af03, // n0x074a c0x0000 (---------------) + I org 0x002370c3, // n0x074b c0x0000 (---------------) + I com 0x00215b83, // n0x074c c0x0000 (---------------) + I edu 0x00208ec3, // n0x074d c0x0000 (---------------) + I gov 0x00210703, // n0x074e c0x0000 (---------------) + I mil 0x002592c4, // n0x074f c0x0000 (---------------) + I name 0x0024bdc3, // n0x0750 c0x0000 (---------------) + I net 0x0024af03, // n0x0751 c0x0000 (---------------) + I org 0x0025e643, // n0x0752 c0x0000 (---------------) + I sch 0x0020a3c2, // n0x0753 c0x0000 (---------------) + I ac 0x0021e242, // n0x0754 c0x0000 (---------------) + I ad 0x1ba79385, // n0x0755 c0x006e (n0x0793-n0x07c8) + I aichi 0x1be70c85, // n0x0756 c0x006f (n0x07c8-n0x07e4) + I akita 0x1c296646, // n0x0757 c0x0070 (n0x07e4-n0x07fa) + I aomori 0x0012d948, // n0x0758 c0x0000 (---------------) + blogspot 0x1c6a65c5, // n0x0759 c0x0071 (n0x07fa-n0x0834) + I chiba 0x00209382, // n0x075a c0x0000 (---------------) + I co 0x00203b42, // n0x075b c0x0000 (---------------) + I ed 0x1cb56ec5, // n0x075c c0x0072 (n0x0834-n0x084a) + I ehime 0x1ce6f405, // n0x075d c0x0073 (n0x084a-n0x0859) + I fukui 0x1d26fbc7, // n0x075e c0x0074 (n0x0859-n0x0898) + I fukuoka 0x1d670449, // n0x075f c0x0075 (n0x0898-n0x08cb) + I fukushima 0x1daac204, // n0x0760 c0x0076 (n0x08cb-n0x08f1) + I gifu 0x00208ec2, // n0x0761 c0x0000 (---------------) + I go 0x00208982, // n0x0762 c0x0000 (---------------) + I gr 0x1de4a445, // n0x0763 c0x0077 (n0x08f1-n0x0915) + I gunma 0x1e283189, // n0x0764 c0x0078 (n0x0915-n0x092e) + I hiroshima 0x1e6d6248, // n0x0765 c0x0079 (n0x092e-n0x09bc) + I hokkaido 0x1eac7345, // n0x0766 c0x007a (n0x09bc-n0x09ea) + I hyogo 0x1ef2af87, // n0x0767 c0x007b (n0x09ea-n0x0a1d) + I ibaraki 0x1f21ac48, // n0x0768 c0x007c (n0x0a1d-n0x0a30) + I ishikawa 0x1f69dc45, // n0x0769 c0x007d (n0x0a30-n0x0a53) + I iwate 0x1fa00106, // n0x076a c0x007e (n0x0a53-n0x0a62) + I kagawa 0x1fe36209, // n0x076b c0x007f (n0x0a62-n0x0a76) + I kagoshima 0x20312c08, // n0x076c c0x0080 (n0x0a76-n0x0a94) + I kanagawa 0x206ad308, // n0x076d c0x0081 (n0x0a94-n0x0a95)* o I kawasaki 0x20a07eca, // n0x076e c0x0082 (n0x0a95-n0x0a96)* o I kitakyushu 0x20e6a204, // n0x076f c0x0083 (n0x0a96-n0x0a97)* o I kobe 0x212bfe45, // n0x0770 c0x0084 (n0x0a97-n0x0ab6) + I kochi 0x217388c8, // n0x0771 c0x0085 (n0x0ab6-n0x0ad0) + I kumamoto 0x21a5bf45, // n0x0772 c0x0086 (n0x0ad0-n0x0aef) + I kyoto 0x00213202, // n0x0773 c0x0000 (---------------) + I lg 0x21e32443, // n0x0774 c0x0087 (n0x0aef-n0x0b0d) + I mie 0x22290d06, // n0x0775 c0x0088 (n0x0b0d-n0x0b2e) + I miyagi 0x22657648, // n0x0776 c0x0089 (n0x0b2e-n0x0b49) + I miyazaki 0x22ad5486, // n0x0777 c0x008a (n0x0b49-n0x0b94) + I nagano 0x22ef0f48, // n0x0778 c0x008b (n0x0b94-n0x0baa) + I nagasaki 0x2335ce46, // n0x0779 c0x008c (n0x0baa-n0x0bab)* o I nagoya 0x23740bc4, // n0x077a c0x008d (n0x0bab-n0x0bd1) + I nara 0x00200982, // n0x077b c0x0000 (---------------) + I ne 0x23a945c7, // n0x077c c0x008e (n0x0bd1-n0x0bf3) + I niigata 0x23ed0944, // n0x077d c0x008f (n0x0bf3-n0x0c06) + I oita 0x2426bd07, // n0x077e c0x0090 (n0x0c06-n0x0c20) + I okayama 0x24626d47, // n0x077f c0x0091 (n0x0c20-n0x0c4a) + I okinawa 0x00201002, // n0x0780 c0x0000 (---------------) + I or 0x24a8a185, // n0x0781 c0x0092 (n0x0c4a-n0x0c7c) + I osaka 0x24e72804, // n0x0782 c0x0093 (n0x0c7c-n0x0c96) + I saga 0x252e7f47, // n0x0783 c0x0094 (n0x0c96-n0x0cdb) + I saitama 0x25647c87, // n0x0784 c0x0095 (n0x0cdb-n0x0cdc)* o I sapporo 0x25a73e06, // n0x0785 c0x0096 (n0x0cdc-n0x0cdd)* o I sendai 0x25e55085, // n0x0786 c0x0097 (n0x0cdd-n0x0cf4) + I shiga 0x26283287, // n0x0787 c0x0098 (n0x0cf4-n0x0d0b) + I shimane 0x266d9508, // n0x0788 c0x0099 (n0x0d0b-n0x0d2f) + I shizuoka 0x26b23547, // n0x0789 c0x009a (n0x0d2f-n0x0d4e) + I tochigi 0x26eeb5c9, // n0x078a c0x009b (n0x0d4e-n0x0d5f) + I tokushima 0x2734ad45, // n0x078b c0x009c (n0x0d5f-n0x0d98) + I tokyo 0x27719787, // n0x078c c0x009d (n0x0d98-n0x0da5) + I tottori 0x27a7ff46, // n0x078d c0x009e (n0x0da5-n0x0dbd) + I toyama 0x27f4fa48, // n0x078e c0x009f (n0x0dbd-n0x0dda) + I wakayama 0x28271688, // n0x078f c0x00a0 (n0x0dda-n0x0dfc) + I yamagata 0x28676449, // n0x0790 c0x00a1 (n0x0dfc-n0x0e0c) + I yamaguchi 0x28a30249, // n0x0791 c0x00a2 (n0x0e0c-n0x0e28) + I yamanashi 0x28f02288, // n0x0792 c0x00a3 (n0x0e28-n0x0e29)* o I yokohama 0x002a7545, // n0x0793 c0x0000 (---------------) + I aisai 0x00204203, // n0x0794 c0x0000 (---------------) + I ama 0x0022e184, // n0x0795 c0x0000 (---------------) + I anjo 0x00339ec5, // n0x0796 c0x0000 (---------------) + I asuke 0x002abf46, // n0x0797 c0x0000 (---------------) + I chiryu 0x002c6d05, // n0x0798 c0x0000 (---------------) + I chita 0x00275dc4, // n0x0799 c0x0000 (---------------) + I fuso 0x002bd608, // n0x079a c0x0000 (---------------) + I gamagori 0x002b1985, // n0x079b c0x0000 (---------------) + I handa 0x00282244, // n0x079c c0x0000 (---------------) + I hazu 0x0025d587, // n0x079d c0x0000 (---------------) + I hekinan 0x0028c5ca, // n0x079e c0x0000 (---------------) + I higashiura 0x0035018a, // n0x079f c0x0000 (---------------) + I ichinomiya 0x002dec87, // n0x07a0 c0x0000 (---------------) + I inazawa 0x00219d87, // n0x07a1 c0x0000 (---------------) + I inuyama 0x00342b07, // n0x07a2 c0x0000 (---------------) + I isshiki 0x0032e3c7, // n0x07a3 c0x0000 (---------------) + I iwakura 0x0032a9c5, // n0x07a4 c0x0000 (---------------) + I kanie 0x0032fe06, // n0x07a5 c0x0000 (---------------) + I kariya 0x002bc347, // n0x07a6 c0x0000 (---------------) + I kasugai 0x002aef84, // n0x07a7 c0x0000 (---------------) + I kira 0x00206c46, // n0x07a8 c0x0000 (---------------) + I kiyosu 0x002f29c6, // n0x07a9 c0x0000 (---------------) + I komaki 0x002c92c5, // n0x07aa c0x0000 (---------------) + I konan 0x002de284, // n0x07ab c0x0000 (---------------) + I kota 0x002924c6, // n0x07ac c0x0000 (---------------) + I mihama 0x0028bb07, // n0x07ad c0x0000 (---------------) + I miyoshi 0x003151c8, // n0x07ae c0x0000 (---------------) + I nagakute 0x00225c86, // n0x07af c0x0000 (---------------) + I nishio 0x0022b347, // n0x07b0 c0x0000 (---------------) + I nisshin 0x00281943, // n0x07b1 c0x0000 (---------------) + I obu 0x00234746, // n0x07b2 c0x0000 (---------------) + I oguchi 0x00329d45, // n0x07b3 c0x0000 (---------------) + I oharu 0x0026fcc7, // n0x07b4 c0x0000 (---------------) + I okazaki 0x002b614a, // n0x07b5 c0x0000 (---------------) + I owariasahi 0x00233604, // n0x07b6 c0x0000 (---------------) + I seto 0x0021a608, // n0x07b7 c0x0000 (---------------) + I shikatsu 0x002e8989, // n0x07b8 c0x0000 (---------------) + I shinshiro 0x002d8e87, // n0x07b9 c0x0000 (---------------) + I shitara 0x00325e46, // n0x07ba c0x0000 (---------------) + I tahara 0x0025ad08, // n0x07bb c0x0000 (---------------) + I takahama 0x00295609, // n0x07bc c0x0000 (---------------) + I tobishima 0x00256904, // n0x07bd c0x0000 (---------------) + I toei 0x0024c984, // n0x07be c0x0000 (---------------) + I togo 0x002ecd85, // n0x07bf c0x0000 (---------------) + I tokai 0x00324e88, // n0x07c0 c0x0000 (---------------) + I tokoname 0x002b8287, // n0x07c1 c0x0000 (---------------) + I toyoake 0x002db889, // n0x07c2 c0x0000 (---------------) + I toyohashi 0x00245a08, // n0x07c3 c0x0000 (---------------) + I toyokawa 0x002c9006, // n0x07c4 c0x0000 (---------------) + I toyone 0x00254446, // n0x07c5 c0x0000 (---------------) + I toyota 0x00286f48, // n0x07c6 c0x0000 (---------------) + I tsushima 0x00320546, // n0x07c7 c0x0000 (---------------) + I yatomi 0x00270c85, // n0x07c8 c0x0000 (---------------) + I akita 0x00273ec6, // n0x07c9 c0x0000 (---------------) + I daisen 0x0026c208, // n0x07ca c0x0000 (---------------) + I fujisato 0x00215f86, // n0x07cb c0x0000 (---------------) + I gojome 0x0029d48b, // n0x07cc c0x0000 (---------------) + I hachirogata 0x0027d1c6, // n0x07cd c0x0000 (---------------) + I happou 0x0028844d, // n0x07ce c0x0000 (---------------) + I higashinaruse 0x00218305, // n0x07cf c0x0000 (---------------) + I honjo 0x00298b86, // n0x07d0 c0x0000 (---------------) + I honjyo 0x0021ad05, // n0x07d1 c0x0000 (---------------) + I ikawa 0x0028b409, // n0x07d2 c0x0000 (---------------) + I kamikoani 0x002006c7, // n0x07d3 c0x0000 (---------------) + I kamioka 0x00322348, // n0x07d4 c0x0000 (---------------) + I katagami 0x00240386, // n0x07d5 c0x0000 (---------------) + I kazuno 0x00287889, // n0x07d6 c0x0000 (---------------) + I kitaakita 0x0029e286, // n0x07d7 c0x0000 (---------------) + I kosaka 0x002b60c5, // n0x07d8 c0x0000 (---------------) + I kyowa 0x00242e46, // n0x07d9 c0x0000 (---------------) + I misato 0x00275646, // n0x07da c0x0000 (---------------) + I mitane 0x002bcf49, // n0x07db c0x0000 (---------------) + I moriyoshi 0x00256706, // n0x07dc c0x0000 (---------------) + I nikaho 0x0024b787, // n0x07dd c0x0000 (---------------) + I noshiro 0x002d93c5, // n0x07de c0x0000 (---------------) + I odate 0x00205cc3, // n0x07df c0x0000 (---------------) + I oga 0x0028ef85, // n0x07e0 c0x0000 (---------------) + I ogata 0x00338787, // n0x07e1 c0x0000 (---------------) + I semboku 0x00205986, // n0x07e2 c0x0000 (---------------) + I yokote 0x00218209, // n0x07e3 c0x0000 (---------------) + I yurihonjo 0x00296646, // n0x07e4 c0x0000 (---------------) + I aomori 0x002aca06, // n0x07e5 c0x0000 (---------------) + I gonohe 0x0025d3c9, // n0x07e6 c0x0000 (---------------) + I hachinohe 0x002738c9, // n0x07e7 c0x0000 (---------------) + I hashikami 0x0028e947, // n0x07e8 c0x0000 (---------------) + I hiranai 0x0034b048, // n0x07e9 c0x0000 (---------------) + I hirosaki 0x002c4f89, // n0x07ea c0x0000 (---------------) + I itayanagi 0x002700c8, // n0x07eb c0x0000 (---------------) + I kuroishi 0x002de886, // n0x07ec c0x0000 (---------------) + I misawa 0x002c3d45, // n0x07ed c0x0000 (---------------) + I mutsu 0x00231cca, // n0x07ee c0x0000 (---------------) + I nakadomari 0x002aca86, // n0x07ef c0x0000 (---------------) + I noheji 0x002f1286, // n0x07f0 c0x0000 (---------------) + I oirase 0x00290ec5, // n0x07f1 c0x0000 (---------------) + I owani 0x00297b88, // n0x07f2 c0x0000 (---------------) + I rokunohe 0x00236b47, // n0x07f3 c0x0000 (---------------) + I sannohe 0x00207b4a, // n0x07f4 c0x0000 (---------------) + I shichinohe 0x002489c6, // n0x07f5 c0x0000 (---------------) + I shingo 0x00330f05, // n0x07f6 c0x0000 (---------------) + I takko 0x002a9946, // n0x07f7 c0x0000 (---------------) + I towada 0x002a3b07, // n0x07f8 c0x0000 (---------------) + I tsugaru 0x00325d07, // n0x07f9 c0x0000 (---------------) + I tsuruta 0x00232c45, // n0x07fa c0x0000 (---------------) + I abiko 0x002b6285, // n0x07fb c0x0000 (---------------) + I asahi 0x00309f86, // n0x07fc c0x0000 (---------------) + I chonan 0x0030b486, // n0x07fd c0x0000 (---------------) + I chosei 0x0030f446, // n0x07fe c0x0000 (---------------) + I choshi 0x00227944, // n0x07ff c0x0000 (---------------) + I chuo 0x00271d49, // n0x0800 c0x0000 (---------------) + I funabashi 0x00277306, // n0x0801 c0x0000 (---------------) + I futtsu 0x0025100a, // n0x0802 c0x0000 (---------------) + I hanamigawa 0x00280ac8, // n0x0803 c0x0000 (---------------) + I ichihara 0x00295308, // n0x0804 c0x0000 (---------------) + I ichikawa 0x0035018a, // n0x0805 c0x0000 (---------------) + I ichinomiya 0x0020ad85, // n0x0806 c0x0000 (---------------) + I inzai 0x0028ba45, // n0x0807 c0x0000 (---------------) + I isumi 0x002ae0c8, // n0x0808 c0x0000 (---------------) + I kamagaya 0x002dc108, // n0x0809 c0x0000 (---------------) + I kamogawa 0x0020f387, // n0x080a c0x0000 (---------------) + I kashiwa 0x00219c46, // n0x080b c0x0000 (---------------) + I katori 0x002f7b48, // n0x080c c0x0000 (---------------) + I katsuura 0x00201947, // n0x080d c0x0000 (---------------) + I kimitsu 0x00252a48, // n0x080e c0x0000 (---------------) + I kisarazu 0x002a28c6, // n0x080f c0x0000 (---------------) + I kozaki 0x00230688, // n0x0810 c0x0000 (---------------) + I kujukuri 0x00281a46, // n0x0811 c0x0000 (---------------) + I kyonan 0x0021bc87, // n0x0812 c0x0000 (---------------) + I matsudo 0x0025cec6, // n0x0813 c0x0000 (---------------) + I midori 0x002924c6, // n0x0814 c0x0000 (---------------) + I mihama 0x0032064a, // n0x0815 c0x0000 (---------------) + I minamiboso 0x00237146, // n0x0816 c0x0000 (---------------) + I mobara 0x002c3d49, // n0x0817 c0x0000 (---------------) + I mutsuzawa 0x0033df06, // n0x0818 c0x0000 (---------------) + I nagara 0x0029c88a, // n0x0819 c0x0000 (---------------) + I nagareyama 0x00340bc9, // n0x081a c0x0000 (---------------) + I narashino 0x00342786, // n0x081b c0x0000 (---------------) + I narita 0x00311f44, // n0x081c c0x0000 (---------------) + I noda 0x00209acd, // n0x081d c0x0000 (---------------) + I oamishirasato 0x002766c7, // n0x081e c0x0000 (---------------) + I omigawa 0x002fa746, // n0x081f c0x0000 (---------------) + I onjuku 0x002ad1c5, // n0x0820 c0x0000 (---------------) + I otaki 0x0029e305, // n0x0821 c0x0000 (---------------) + I sakae 0x00212346, // n0x0822 c0x0000 (---------------) + I sakura 0x00272649, // n0x0823 c0x0000 (---------------) + I shimofusa 0x00329407, // n0x0824 c0x0000 (---------------) + I shirako 0x0026ca06, // n0x0825 c0x0000 (---------------) + I shiroi 0x002d6686, // n0x0826 c0x0000 (---------------) + I shisui 0x00296249, // n0x0827 c0x0000 (---------------) + I sodegaura 0x00270bc4, // n0x0828 c0x0000 (---------------) + I sosa 0x0024f484, // n0x0829 c0x0000 (---------------) + I tako 0x0021e348, // n0x082a c0x0000 (---------------) + I tateyama 0x00294a86, // n0x082b c0x0000 (---------------) + I togane 0x00242f48, // n0x082c c0x0000 (---------------) + I tohnosho 0x002f9588, // n0x082d c0x0000 (---------------) + I tomisato 0x0028ca07, // n0x082e c0x0000 (---------------) + I urayasu 0x0021da49, // n0x082f c0x0000 (---------------) + I yachimata 0x00359a47, // n0x0830 c0x0000 (---------------) + I yachiyo 0x002a648a, // n0x0831 c0x0000 (---------------) + I yokaichiba 0x002dd6cf, // n0x0832 c0x0000 (---------------) + I yokoshibahikari 0x00259eca, // n0x0833 c0x0000 (---------------) + I yotsukaido 0x00204ac5, // n0x0834 c0x0000 (---------------) + I ainan 0x0026c445, // n0x0835 c0x0000 (---------------) + I honai 0x00219b05, // n0x0836 c0x0000 (---------------) + I ikata 0x00306c47, // n0x0837 c0x0000 (---------------) + I imabari 0x00206c83, // n0x0838 c0x0000 (---------------) + I iyo 0x0034b248, // n0x0839 c0x0000 (---------------) + I kamijima 0x00206186, // n0x083a c0x0000 (---------------) + I kihoku 0x00206289, // n0x083b c0x0000 (---------------) + I kumakogen 0x00325a86, // n0x083c c0x0000 (---------------) + I masaki 0x002a6107, // n0x083d c0x0000 (---------------) + I matsuno 0x00287649, // n0x083e c0x0000 (---------------) + I matsuyama 0x00322248, // n0x083f c0x0000 (---------------) + I namikata 0x00290f87, // n0x0840 c0x0000 (---------------) + I niihama 0x00227a03, // n0x0841 c0x0000 (---------------) + I ozu 0x002a75c5, // n0x0842 c0x0000 (---------------) + I saijo 0x002dd605, // n0x0843 c0x0000 (---------------) + I seiyo 0x002efe8b, // n0x0844 c0x0000 (---------------) + I shikokuchuo 0x0025c004, // n0x0845 c0x0000 (---------------) + I tobe 0x002159c4, // n0x0846 c0x0000 (---------------) + I toon 0x0026b3c6, // n0x0847 c0x0000 (---------------) + I uchiko 0x002a83c7, // n0x0848 c0x0000 (---------------) + I uwajima 0x00341b0a, // n0x0849 c0x0000 (---------------) + I yawatahama 0x00233f07, // n0x084a c0x0000 (---------------) + I echizen 0x0031a447, // n0x084b c0x0000 (---------------) + I eiheiji 0x0026f405, // n0x084c c0x0000 (---------------) + I fukui 0x00203ac5, // n0x084d c0x0000 (---------------) + I ikeda 0x002d2b09, // n0x084e c0x0000 (---------------) + I katsuyama 0x002924c6, // n0x084f c0x0000 (---------------) + I mihama 0x00233d8d, // n0x0850 c0x0000 (---------------) + I minamiechizen 0x00227105, // n0x0851 c0x0000 (---------------) + I obama 0x0028c583, // n0x0852 c0x0000 (---------------) + I ohi 0x0020fdc3, // n0x0853 c0x0000 (---------------) + I ono 0x00359f05, // n0x0854 c0x0000 (---------------) + I sabae 0x00319545, // n0x0855 c0x0000 (---------------) + I sakai 0x0025ad08, // n0x0856 c0x0000 (---------------) + I takahama 0x00306087, // n0x0857 c0x0000 (---------------) + I tsuruga 0x0024f206, // n0x0858 c0x0000 (---------------) + I wakasa 0x0028cd06, // n0x0859 c0x0000 (---------------) + I ashiya 0x0022b245, // n0x085a c0x0000 (---------------) + I buzen 0x00215e47, // n0x085b c0x0000 (---------------) + I chikugo 0x0021a007, // n0x085c c0x0000 (---------------) + I chikuho 0x00315a07, // n0x085d c0x0000 (---------------) + I chikujo 0x0025c30a, // n0x085e c0x0000 (---------------) + I chikushino 0x00234808, // n0x085f c0x0000 (---------------) + I chikuzen 0x00227944, // n0x0860 c0x0000 (---------------) + I chuo 0x00203d87, // n0x0861 c0x0000 (---------------) + I dazaifu 0x0026e587, // n0x0862 c0x0000 (---------------) + I fukuchi 0x003357c6, // n0x0863 c0x0000 (---------------) + I hakata 0x00282a87, // n0x0864 c0x0000 (---------------) + I higashi 0x002a5808, // n0x0865 c0x0000 (---------------) + I hirokawa 0x00230148, // n0x0866 c0x0000 (---------------) + I hisayama 0x00248546, // n0x0867 c0x0000 (---------------) + I iizuka 0x00262348, // n0x0868 c0x0000 (---------------) + I inatsuki 0x00256784, // n0x0869 c0x0000 (---------------) + I kaho 0x002bc346, // n0x086a c0x0000 (---------------) + I kasuga 0x0022ce86, // n0x086b c0x0000 (---------------) + I kasuya 0x00342d46, // n0x086c c0x0000 (---------------) + I kawara 0x00350406, // n0x086d c0x0000 (---------------) + I keisen 0x0028d284, // n0x086e c0x0000 (---------------) + I koga 0x0032e486, // n0x086f c0x0000 (---------------) + I kurate 0x002a9e06, // n0x0870 c0x0000 (---------------) + I kurogi 0x00286946, // n0x0871 c0x0000 (---------------) + I kurume 0x00227b06, // n0x0872 c0x0000 (---------------) + I minami 0x0020fc86, // n0x0873 c0x0000 (---------------) + I miyako 0x002a5646, // n0x0874 c0x0000 (---------------) + I miyama 0x0024f108, // n0x0875 c0x0000 (---------------) + I miyawaka 0x0029ed88, // n0x0876 c0x0000 (---------------) + I mizumaki 0x002c1488, // n0x0877 c0x0000 (---------------) + I munakata 0x002c6f08, // n0x0878 c0x0000 (---------------) + I nakagawa 0x002ae046, // n0x0879 c0x0000 (---------------) + I nakama 0x00212685, // n0x087a c0x0000 (---------------) + I nishi 0x0028ef46, // n0x087b c0x0000 (---------------) + I nogata 0x002c73c5, // n0x087c c0x0000 (---------------) + I ogori 0x00252907, // n0x087d c0x0000 (---------------) + I okagaki 0x00245ac5, // n0x087e c0x0000 (---------------) + I okawa 0x00226d43, // n0x087f c0x0000 (---------------) + I oki 0x00216d45, // n0x0880 c0x0000 (---------------) + I omuta 0x00231544, // n0x0881 c0x0000 (---------------) + I onga 0x0020fdc5, // n0x0882 c0x0000 (---------------) + I onojo 0x00213ec3, // n0x0883 c0x0000 (---------------) + I oto 0x00326b47, // n0x0884 c0x0000 (---------------) + I saigawa 0x002d6a88, // n0x0885 c0x0000 (---------------) + I sasaguri 0x0022b406, // n0x0886 c0x0000 (---------------) + I shingu 0x003033cd, // n0x0887 c0x0000 (---------------) + I shinyoshitomi 0x0026c406, // n0x0888 c0x0000 (---------------) + I shonai 0x00285d05, // n0x0889 c0x0000 (---------------) + I soeda 0x00204303, // n0x088a c0x0000 (---------------) + I sue 0x002a7389, // n0x088b c0x0000 (---------------) + I tachiarai 0x00271bc6, // n0x088c c0x0000 (---------------) + I tagawa 0x002801c6, // n0x088d c0x0000 (---------------) + I takata 0x002d2404, // n0x088e c0x0000 (---------------) + I toho 0x00259e47, // n0x088f c0x0000 (---------------) + I toyotsu 0x0023ba86, // n0x0890 c0x0000 (---------------) + I tsuiki 0x00331145, // n0x0891 c0x0000 (---------------) + I ukiha 0x00206d83, // n0x0892 c0x0000 (---------------) + I umi 0x00222e44, // n0x0893 c0x0000 (---------------) + I usui 0x0026e746, // n0x0894 c0x0000 (---------------) + I yamada 0x0022cf84, // n0x0895 c0x0000 (---------------) + I yame 0x00307688, // n0x0896 c0x0000 (---------------) + I yanagawa 0x002108c9, // n0x0897 c0x0000 (---------------) + I yukuhashi 0x002c3709, // n0x0898 c0x0000 (---------------) + I aizubange 0x0028dfca, // n0x0899 c0x0000 (---------------) + I aizumisato 0x0027f74d, // n0x089a c0x0000 (---------------) + I aizuwakamatsu 0x00295e47, // n0x089b c0x0000 (---------------) + I asakawa 0x003171c6, // n0x089c c0x0000 (---------------) + I bandai 0x002222c4, // n0x089d c0x0000 (---------------) + I date 0x00270449, // n0x089e c0x0000 (---------------) + I fukushima 0x002757c8, // n0x089f c0x0000 (---------------) + I furudono 0x002762c6, // n0x08a0 c0x0000 (---------------) + I futaba 0x0024d446, // n0x08a1 c0x0000 (---------------) + I hanawa 0x00282a87, // n0x08a2 c0x0000 (---------------) + I higashi 0x002d2e46, // n0x08a3 c0x0000 (---------------) + I hirata 0x0021f086, // n0x08a4 c0x0000 (---------------) + I hirono 0x002078c6, // n0x08a5 c0x0000 (---------------) + I iitate 0x00226dca, // n0x08a6 c0x0000 (---------------) + I inawashiro 0x0021ac48, // n0x08a7 c0x0000 (---------------) + I ishikawa 0x00229e05, // n0x08a8 c0x0000 (---------------) + I iwaki 0x002c0509, // n0x08a9 c0x0000 (---------------) + I izumizaki 0x002b8a0a, // n0x08aa c0x0000 (---------------) + I kagamiishi 0x00303748, // n0x08ab c0x0000 (---------------) + I kaneyama 0x0028b088, // n0x08ac c0x0000 (---------------) + I kawamata 0x00280148, // n0x08ad c0x0000 (---------------) + I kitakata 0x0028bfcc, // n0x08ae c0x0000 (---------------) + I kitashiobara 0x002ee1c5, // n0x08af c0x0000 (---------------) + I koori 0x0028cf88, // n0x08b0 c0x0000 (---------------) + I koriyama 0x00210606, // n0x08b1 c0x0000 (---------------) + I kunimi 0x0031ab86, // n0x08b2 c0x0000 (---------------) + I miharu 0x002b64c7, // n0x08b3 c0x0000 (---------------) + I mishima 0x00233e05, // n0x08b4 c0x0000 (---------------) + I namie 0x0030a045, // n0x08b5 c0x0000 (---------------) + I nango 0x002c35c9, // n0x08b6 c0x0000 (---------------) + I nishiaizu 0x00215087, // n0x08b7 c0x0000 (---------------) + I nishigo 0x00206245, // n0x08b8 c0x0000 (---------------) + I okuma 0x00223987, // n0x08b9 c0x0000 (---------------) + I omotego 0x0020fdc3, // n0x08ba c0x0000 (---------------) + I ono 0x002b90c5, // n0x08bb c0x0000 (---------------) + I otama 0x0022b048, // n0x08bc c0x0000 (---------------) + I samegawa 0x00210a47, // n0x08bd c0x0000 (---------------) + I shimogo 0x0028af49, // n0x08be c0x0000 (---------------) + I shirakawa 0x002d9e85, // n0x08bf c0x0000 (---------------) + I showa 0x002dbdc4, // n0x08c0 c0x0000 (---------------) + I soma 0x0028f808, // n0x08c1 c0x0000 (---------------) + I sukagawa 0x00260247, // n0x08c2 c0x0000 (---------------) + I taishin 0x00291148, // n0x08c3 c0x0000 (---------------) + I tamakawa 0x0027b488, // n0x08c4 c0x0000 (---------------) + I tanagura 0x00221f45, // n0x08c5 c0x0000 (---------------) + I tenei 0x002551c6, // n0x08c6 c0x0000 (---------------) + I yabuki 0x00280606, // n0x08c7 c0x0000 (---------------) + I yamato 0x0031b109, // n0x08c8 c0x0000 (---------------) + I yamatsuri 0x002f3fc7, // n0x08c9 c0x0000 (---------------) + I yanaizu 0x0029ce46, // n0x08ca c0x0000 (---------------) + I yugawa 0x00357b47, // n0x08cb c0x0000 (---------------) + I anpachi 0x002015c3, // n0x08cc c0x0000 (---------------) + I ena 0x002ac204, // n0x08cd c0x0000 (---------------) + I gifu 0x00284805, // n0x08ce c0x0000 (---------------) + I ginan 0x0035a284, // n0x08cf c0x0000 (---------------) + I godo 0x0033cf44, // n0x08d0 c0x0000 (---------------) + I gujo 0x00271907, // n0x08d1 c0x0000 (---------------) + I hashima 0x0025a587, // n0x08d2 c0x0000 (---------------) + I hichiso 0x0026cbc4, // n0x08d3 c0x0000 (---------------) + I hida 0x0028ad90, // n0x08d4 c0x0000 (---------------) + I higashishirakawa 0x00357047, // n0x08d5 c0x0000 (---------------) + I ibigawa 0x00203ac5, // n0x08d6 c0x0000 (---------------) + I ikeda 0x002e00cc, // n0x08d7 c0x0000 (---------------) + I kakamigahara 0x0027b244, // n0x08d8 c0x0000 (---------------) + I kani 0x002f0608, // n0x08d9 c0x0000 (---------------) + I kasahara 0x0021bb89, // n0x08da c0x0000 (---------------) + I kasamatsu 0x00259446, // n0x08db c0x0000 (---------------) + I kawaue 0x00270cc8, // n0x08dc c0x0000 (---------------) + I kitagata 0x00255f04, // n0x08dd c0x0000 (---------------) + I mino 0x002919c8, // n0x08de c0x0000 (---------------) + I minokamo 0x002b7906, // n0x08df c0x0000 (---------------) + I mitake 0x00229008, // n0x08e0 c0x0000 (---------------) + I mizunami 0x002838c6, // n0x08e1 c0x0000 (---------------) + I motosu 0x002c11cb, // n0x08e2 c0x0000 (---------------) + I nakatsugawa 0x00205cc5, // n0x08e3 c0x0000 (---------------) + I ogaki 0x002aa388, // n0x08e4 c0x0000 (---------------) + I sakahogi 0x00212c84, // n0x08e5 c0x0000 (---------------) + I seki 0x002a150a, // n0x08e6 c0x0000 (---------------) + I sekigahara 0x0028af49, // n0x08e7 c0x0000 (---------------) + I shirakawa 0x00305a46, // n0x08e8 c0x0000 (---------------) + I tajimi 0x002f5e08, // n0x08e9 c0x0000 (---------------) + I takayama 0x0030a445, // n0x08ea c0x0000 (---------------) + I tarui 0x003142c4, // n0x08eb c0x0000 (---------------) + I toki 0x002f0506, // n0x08ec c0x0000 (---------------) + I tomika 0x0025c1c8, // n0x08ed c0x0000 (---------------) + I wanouchi 0x00271688, // n0x08ee c0x0000 (---------------) + I yamagata 0x002f8386, // n0x08ef c0x0000 (---------------) + I yaotsu 0x00208584, // n0x08f0 c0x0000 (---------------) + I yoro 0x00231c46, // n0x08f1 c0x0000 (---------------) + I annaka 0x00359ac7, // n0x08f2 c0x0000 (---------------) + I chiyoda 0x0026bc07, // n0x08f3 c0x0000 (---------------) + I fujioka 0x002c00cf, // n0x08f4 c0x0000 (---------------) + I higashiagatsuma 0x002ea407, // n0x08f5 c0x0000 (---------------) + I isesaki 0x00342847, // n0x08f6 c0x0000 (---------------) + I itakura 0x002d6085, // n0x08f7 c0x0000 (---------------) + I kanna 0x002e49c5, // n0x08f8 c0x0000 (---------------) + I kanra 0x0028e609, // n0x08f9 c0x0000 (---------------) + I katashina 0x0021dd86, // n0x08fa c0x0000 (---------------) + I kawaba 0x00329c05, // n0x08fb c0x0000 (---------------) + I kiryu 0x00273bc7, // n0x08fc c0x0000 (---------------) + I kusatsu 0x002a1ec8, // n0x08fd c0x0000 (---------------) + I maebashi 0x00245105, // n0x08fe c0x0000 (---------------) + I meiwa 0x0025cec6, // n0x08ff c0x0000 (---------------) + I midori 0x002288c8, // n0x0900 c0x0000 (---------------) + I minakami 0x002d548a, // n0x0901 c0x0000 (---------------) + I naganohara 0x0030ab88, // n0x0902 c0x0000 (---------------) + I nakanojo 0x0027ae47, // n0x0903 c0x0000 (---------------) + I nanmoku 0x002ddb06, // n0x0904 c0x0000 (---------------) + I numata 0x002c04c6, // n0x0905 c0x0000 (---------------) + I oizumi 0x002095c3, // n0x0906 c0x0000 (---------------) + I ora 0x00213043, // n0x0907 c0x0000 (---------------) + I ota 0x0030f509, // n0x0908 c0x0000 (---------------) + I shibukawa 0x002c4e09, // n0x0909 c0x0000 (---------------) + I shimonita 0x002eb4c6, // n0x090a c0x0000 (---------------) + I shinto 0x002d9e85, // n0x090b c0x0000 (---------------) + I showa 0x00206ac8, // n0x090c c0x0000 (---------------) + I takasaki 0x002f5e08, // n0x090d c0x0000 (---------------) + I takayama 0x002b7008, // n0x090e c0x0000 (---------------) + I tamamura 0x0020794b, // n0x090f c0x0000 (---------------) + I tatebayashi 0x00303607, // n0x0910 c0x0000 (---------------) + I tomioka 0x0029de49, // n0x0911 c0x0000 (---------------) + I tsukiyono 0x002c0348, // n0x0912 c0x0000 (---------------) + I tsumagoi 0x0020f644, // n0x0913 c0x0000 (---------------) + I ueno 0x002bd048, // n0x0914 c0x0000 (---------------) + I yoshioka 0x0027e309, // n0x0915 c0x0000 (---------------) + I asaminami 0x00317285, // n0x0916 c0x0000 (---------------) + I daiwa 0x0030ba47, // n0x0917 c0x0000 (---------------) + I etajima 0x00203ec5, // n0x0918 c0x0000 (---------------) + I fuchu 0x00271588, // n0x0919 c0x0000 (---------------) + I fukuyama 0x0028090b, // n0x091a c0x0000 (---------------) + I hatsukaichi 0x00282fd0, // n0x091b c0x0000 (---------------) + I higashihiroshima 0x002987c5, // n0x091c c0x0000 (---------------) + I hongo 0x00212bcc, // n0x091d c0x0000 (---------------) + I jinsekikogen 0x0024f3c5, // n0x091e c0x0000 (---------------) + I kaita 0x0026f483, // n0x091f c0x0000 (---------------) + I kui 0x002dae86, // n0x0920 c0x0000 (---------------) + I kumano 0x002a8c44, // n0x0921 c0x0000 (---------------) + I kure 0x00276146, // n0x0922 c0x0000 (---------------) + I mihara 0x0028bb07, // n0x0923 c0x0000 (---------------) + I miyoshi 0x00202744, // n0x0924 c0x0000 (---------------) + I naka 0x00350088, // n0x0925 c0x0000 (---------------) + I onomichi 0x0034b10d, // n0x0926 c0x0000 (---------------) + I osakikamijima 0x002bc185, // n0x0927 c0x0000 (---------------) + I otake 0x00271084, // n0x0928 c0x0000 (---------------) + I saka 0x00204804, // n0x0929 c0x0000 (---------------) + I sera 0x0029c109, // n0x092a c0x0000 (---------------) + I seranishi 0x002cefc8, // n0x092b c0x0000 (---------------) + I shinichi 0x002b1007, // n0x092c c0x0000 (---------------) + I shobara 0x002b7988, // n0x092d c0x0000 (---------------) + I takehara 0x00271e08, // n0x092e c0x0000 (---------------) + I abashiri 0x0026cd05, // n0x092f c0x0000 (---------------) + I abira 0x00311787, // n0x0930 c0x0000 (---------------) + I aibetsu 0x0026cc87, // n0x0931 c0x0000 (---------------) + I akabira 0x002b4b07, // n0x0932 c0x0000 (---------------) + I akkeshi 0x002b6289, // n0x0933 c0x0000 (---------------) + I asahikawa 0x0023b909, // n0x0934 c0x0000 (---------------) + I ashibetsu 0x00243a06, // n0x0935 c0x0000 (---------------) + I ashoro 0x0027cf46, // n0x0936 c0x0000 (---------------) + I assabu 0x00273c86, // n0x0937 c0x0000 (---------------) + I atsuma 0x00293205, // n0x0938 c0x0000 (---------------) + I bibai 0x00292004, // n0x0939 c0x0000 (---------------) + I biei 0x00200006, // n0x093a c0x0000 (---------------) + I bifuka 0x00200f46, // n0x093b c0x0000 (---------------) + I bihoro 0x0026cd48, // n0x093c c0x0000 (---------------) + I biratori 0x002a37cb, // n0x093d c0x0000 (---------------) + I chippubetsu 0x002cab87, // n0x093e c0x0000 (---------------) + I chitose 0x002222c4, // n0x093f c0x0000 (---------------) + I date 0x0033dac6, // n0x0940 c0x0000 (---------------) + I ebetsu 0x002904c7, // n0x0941 c0x0000 (---------------) + I embetsu 0x002e0d85, // n0x0942 c0x0000 (---------------) + I eniwa 0x00314805, // n0x0943 c0x0000 (---------------) + I erimo 0x0022e104, // n0x0944 c0x0000 (---------------) + I esan 0x0023b886, // n0x0945 c0x0000 (---------------) + I esashi 0x00200088, // n0x0946 c0x0000 (---------------) + I fukagawa 0x00270449, // n0x0947 c0x0000 (---------------) + I fukushima 0x00247306, // n0x0948 c0x0000 (---------------) + I furano 0x002751c8, // n0x0949 c0x0000 (---------------) + I furubira 0x00242486, // n0x094a c0x0000 (---------------) + I haboro 0x00339488, // n0x094b c0x0000 (---------------) + I hakodate 0x00307c0c, // n0x094c c0x0000 (---------------) + I hamatonbetsu 0x0026cbc6, // n0x094d c0x0000 (---------------) + I hidaka 0x002859cd, // n0x094e c0x0000 (---------------) + I higashikagura 0x00285e4b, // n0x094f c0x0000 (---------------) + I higashikawa 0x0024b845, // n0x0950 c0x0000 (---------------) + I hiroo 0x0021a147, // n0x0951 c0x0000 (---------------) + I hokuryu 0x00256806, // n0x0952 c0x0000 (---------------) + I hokuto 0x00330748, // n0x0953 c0x0000 (---------------) + I honbetsu 0x00243a89, // n0x0954 c0x0000 (---------------) + I horokanai 0x002c3108, // n0x0955 c0x0000 (---------------) + I horonobe 0x00203ac5, // n0x0956 c0x0000 (---------------) + I ikeda 0x00222887, // n0x0957 c0x0000 (---------------) + I imakane 0x002b8b88, // n0x0958 c0x0000 (---------------) + I ishikari 0x00301e89, // n0x0959 c0x0000 (---------------) + I iwamizawa 0x00240f86, // n0x095a c0x0000 (---------------) + I iwanai 0x0024b58a, // n0x095b c0x0000 (---------------) + I kamifurano 0x003304c8, // n0x095c c0x0000 (---------------) + I kamikawa 0x002c2f4b, // n0x095d c0x0000 (---------------) + I kamishihoro 0x0024864c, // n0x095e c0x0000 (---------------) + I kamisunagawa 0x00291ac8, // n0x095f c0x0000 (---------------) + I kamoenai 0x0026d846, // n0x0960 c0x0000 (---------------) + I kayabe 0x003158c8, // n0x0961 c0x0000 (---------------) + I kembuchi 0x002f10c7, // n0x0962 c0x0000 (---------------) + I kikonai 0x00325b89, // n0x0963 c0x0000 (---------------) + I kimobetsu 0x0032b0cd, // n0x0964 c0x0000 (---------------) + I kitahiroshima 0x002c06c6, // n0x0965 c0x0000 (---------------) + I kitami 0x002d2288, // n0x0966 c0x0000 (---------------) + I kiyosato 0x0029ec49, // n0x0967 c0x0000 (---------------) + I koshimizu 0x002a8208, // n0x0968 c0x0000 (---------------) + I kunneppu 0x00230788, // n0x0969 c0x0000 (---------------) + I kuriyama 0x002aba8c, // n0x096a c0x0000 (---------------) + I kuromatsunai 0x002b0b47, // n0x096b c0x0000 (---------------) + I kushiro 0x002b1887, // n0x096c c0x0000 (---------------) + I kutchan 0x002b60c5, // n0x096d c0x0000 (---------------) + I kyowa 0x002ec9c7, // n0x096e c0x0000 (---------------) + I mashike 0x002a1d88, // n0x096f c0x0000 (---------------) + I matsumae 0x002f0586, // n0x0970 c0x0000 (---------------) + I mikasa 0x0024718c, // n0x0971 c0x0000 (---------------) + I minamifurano 0x0020e188, // n0x0972 c0x0000 (---------------) + I mombetsu 0x002bdf88, // n0x0973 c0x0000 (---------------) + I moseushi 0x00353d06, // n0x0974 c0x0000 (---------------) + I mukawa 0x00254e47, // n0x0975 c0x0000 (---------------) + I muroran 0x00243c04, // n0x0976 c0x0000 (---------------) + I naie 0x002c6f08, // n0x0977 c0x0000 (---------------) + I nakagawa 0x0020760c, // n0x0978 c0x0000 (---------------) + I nakasatsunai 0x0020274c, // n0x0979 c0x0000 (---------------) + I nakatombetsu 0x00204b45, // n0x097a c0x0000 (---------------) + I nanae 0x0021af47, // n0x097b c0x0000 (---------------) + I nanporo 0x00208506, // n0x097c c0x0000 (---------------) + I nayoro 0x00254dc6, // n0x097d c0x0000 (---------------) + I nemuro 0x0028b5c8, // n0x097e c0x0000 (---------------) + I niikappu 0x00206104, // n0x097f c0x0000 (---------------) + I niki 0x00225c8b, // n0x0980 c0x0000 (---------------) + I nishiokoppe 0x0034f58b, // n0x0981 c0x0000 (---------------) + I noboribetsu 0x002ddb06, // n0x0982 c0x0000 (---------------) + I numata 0x0034af87, // n0x0983 c0x0000 (---------------) + I obihiro 0x00208645, // n0x0984 c0x0000 (---------------) + I obira 0x00264345, // n0x0985 c0x0000 (---------------) + I oketo 0x00225dc6, // n0x0986 c0x0000 (---------------) + I okoppe 0x0030a405, // n0x0987 c0x0000 (---------------) + I otaru 0x0025bfc5, // n0x0988 c0x0000 (---------------) + I otobe 0x00338cc7, // n0x0989 c0x0000 (---------------) + I otofuke 0x00213ec9, // n0x098a c0x0000 (---------------) + I otoineppu 0x002b4804, // n0x098b c0x0000 (---------------) + I oumu 0x002ae485, // n0x098c c0x0000 (---------------) + I ozora 0x002c8545, // n0x098d c0x0000 (---------------) + I pippu 0x00254f48, // n0x098e c0x0000 (---------------) + I rankoshi 0x0034fec5, // n0x098f c0x0000 (---------------) + I rebun 0x00263489, // n0x0990 c0x0000 (---------------) + I rikubetsu 0x00304c87, // n0x0991 c0x0000 (---------------) + I rishiri 0x00304c8b, // n0x0992 c0x0000 (---------------) + I rishirifuji 0x002b3d06, // n0x0993 c0x0000 (---------------) + I saroma 0x0024ee49, // n0x0994 c0x0000 (---------------) + I sarufutsu 0x002de1c8, // n0x0995 c0x0000 (---------------) + I shakotan 0x00319d85, // n0x0996 c0x0000 (---------------) + I shari 0x002b4c08, // n0x0997 c0x0000 (---------------) + I shibecha 0x0023b948, // n0x0998 c0x0000 (---------------) + I shibetsu 0x002587c7, // n0x0999 c0x0000 (---------------) + I shikabe 0x002a2007, // n0x099a c0x0000 (---------------) + I shikaoi 0x00271989, // n0x099b c0x0000 (---------------) + I shimamaki 0x00228f47, // n0x099c c0x0000 (---------------) + I shimizu 0x002be649, // n0x099d c0x0000 (---------------) + I shimokawa 0x002e79cc, // n0x099e c0x0000 (---------------) + I shinshinotsu 0x002eb4c8, // n0x099f c0x0000 (---------------) + I shintoku 0x0032a809, // n0x09a0 c0x0000 (---------------) + I shiranuka 0x0032ea47, // n0x09a1 c0x0000 (---------------) + I shiraoi 0x00271ec9, // n0x09a2 c0x0000 (---------------) + I shiriuchi 0x0035b887, // n0x09a3 c0x0000 (---------------) + I sobetsu 0x00248748, // n0x09a4 c0x0000 (---------------) + I sunagawa 0x00227785, // n0x09a5 c0x0000 (---------------) + I taiki 0x002bc2c6, // n0x09a6 c0x0000 (---------------) + I takasu 0x002ad208, // n0x09a7 c0x0000 (---------------) + I takikawa 0x002f8908, // n0x09a8 c0x0000 (---------------) + I takinoue 0x002b88c9, // n0x09a9 c0x0000 (---------------) + I teshikaga 0x0025c007, // n0x09aa c0x0000 (---------------) + I tobetsu 0x0028e1c5, // n0x09ab c0x0000 (---------------) + I tohma 0x0023f989, // n0x09ac c0x0000 (---------------) + I tomakomai 0x002463c6, // n0x09ad c0x0000 (---------------) + I tomari 0x00242c44, // n0x09ae c0x0000 (---------------) + I toya 0x00242c46, // n0x09af c0x0000 (---------------) + I toyako 0x002574c8, // n0x09b0 c0x0000 (---------------) + I toyotomi 0x0025f9c7, // n0x09b1 c0x0000 (---------------) + I toyoura 0x002a39c8, // n0x09b2 c0x0000 (---------------) + I tsubetsu 0x00262409, // n0x09b3 c0x0000 (---------------) + I tsukigata 0x00202087, // n0x09b4 c0x0000 (---------------) + I urakawa 0x0028c786, // n0x09b5 c0x0000 (---------------) + I urausu 0x0021a204, // n0x09b6 c0x0000 (---------------) + I uryu 0x00216dc9, // n0x09b7 c0x0000 (---------------) + I utashinai 0x00311608, // n0x09b8 c0x0000 (---------------) + I wakkanai 0x00353bc7, // n0x09b9 c0x0000 (---------------) + I wassamu 0x00202d06, // n0x09ba c0x0000 (---------------) + I yakumo 0x00298c86, // n0x09bb c0x0000 (---------------) + I yoichi 0x002f1204, // n0x09bc c0x0000 (---------------) + I aioi 0x002aa0c6, // n0x09bd c0x0000 (---------------) + I akashi 0x00206343, // n0x09be c0x0000 (---------------) + I ako 0x003130c9, // n0x09bf c0x0000 (---------------) + I amagasaki 0x00205c86, // n0x09c0 c0x0000 (---------------) + I aogaki 0x002d0a85, // n0x09c1 c0x0000 (---------------) + I asago 0x0028cd06, // n0x09c2 c0x0000 (---------------) + I ashiya 0x00229305, // n0x09c3 c0x0000 (---------------) + I awaji 0x002709c8, // n0x09c4 c0x0000 (---------------) + I fukusaki 0x00291807, // n0x09c5 c0x0000 (---------------) + I goshiki 0x002ac846, // n0x09c6 c0x0000 (---------------) + I harima 0x00356f06, // n0x09c7 c0x0000 (---------------) + I himeji 0x00295308, // n0x09c8 c0x0000 (---------------) + I ichikawa 0x0028e787, // n0x09c9 c0x0000 (---------------) + I inagawa 0x002c0705, // n0x09ca c0x0000 (---------------) + I itami 0x0028d208, // n0x09cb c0x0000 (---------------) + I kakogawa 0x0027a648, // n0x09cc c0x0000 (---------------) + I kamigori 0x003304c8, // n0x09cd c0x0000 (---------------) + I kamikawa 0x0024f285, // n0x09ce c0x0000 (---------------) + I kasai 0x002bc346, // n0x09cf c0x0000 (---------------) + I kasuga 0x002c34c9, // n0x09d0 c0x0000 (---------------) + I kawanishi 0x00280484, // n0x09d1 c0x0000 (---------------) + I miki 0x0022918b, // n0x09d2 c0x0000 (---------------) + I minamiawaji 0x0021ed8b, // n0x09d3 c0x0000 (---------------) + I nishinomiya 0x00229d09, // n0x09d4 c0x0000 (---------------) + I nishiwaki 0x0020fdc3, // n0x09d5 c0x0000 (---------------) + I ono 0x00250485, // n0x09d6 c0x0000 (---------------) + I sanda 0x00231186, // n0x09d7 c0x0000 (---------------) + I sannan 0x00251fc8, // n0x09d8 c0x0000 (---------------) + I sasayama 0x00279204, // n0x09d9 c0x0000 (---------------) + I sayo 0x0022b406, // n0x09da c0x0000 (---------------) + I shingu 0x0025c449, // n0x09db c0x0000 (---------------) + I shinonsen 0x002d5f45, // n0x09dc c0x0000 (---------------) + I shiso 0x00338c06, // n0x09dd c0x0000 (---------------) + I sumoto 0x00260246, // n0x09de c0x0000 (---------------) + I taishi 0x00206ac4, // n0x09df c0x0000 (---------------) + I taka 0x0028b20a, // n0x09e0 c0x0000 (---------------) + I takarazuka 0x002d09c8, // n0x09e1 c0x0000 (---------------) + I takasago 0x002f8906, // n0x09e2 c0x0000 (---------------) + I takino 0x002ae905, // n0x09e3 c0x0000 (---------------) + I tamba 0x00220ac7, // n0x09e4 c0x0000 (---------------) + I tatsuno 0x00252247, // n0x09e5 c0x0000 (---------------) + I toyooka 0x002551c4, // n0x09e6 c0x0000 (---------------) + I yabu 0x0021efc7, // n0x09e7 c0x0000 (---------------) + I yashiro 0x00245a84, // n0x09e8 c0x0000 (---------------) + I yoka 0x00245a86, // n0x09e9 c0x0000 (---------------) + I yokawa 0x00200703, // n0x09ea c0x0000 (---------------) + I ami 0x002b6285, // n0x09eb c0x0000 (---------------) + I asahi 0x0033c5c5, // n0x09ec c0x0000 (---------------) + I bando 0x002bfec8, // n0x09ed c0x0000 (---------------) + I chikusei 0x002ac305, // n0x09ee c0x0000 (---------------) + I daigo 0x0026c909, // n0x09ef c0x0000 (---------------) + I fujishiro 0x00290b07, // n0x09f0 c0x0000 (---------------) + I hitachi 0x002c6d4b, // n0x09f1 c0x0000 (---------------) + I hitachinaka 0x00290b0c, // n0x09f2 c0x0000 (---------------) + I hitachiomiya 0x0029158a, // n0x09f3 c0x0000 (---------------) + I hitachiota 0x0032af87, // n0x09f4 c0x0000 (---------------) + I ibaraki 0x00204b03, // n0x09f5 c0x0000 (---------------) + I ina 0x002d9b88, // n0x09f6 c0x0000 (---------------) + I inashiki 0x0024f445, // n0x09f7 c0x0000 (---------------) + I itako 0x00245185, // n0x09f8 c0x0000 (---------------) + I iwama 0x002a7684, // n0x09f9 c0x0000 (---------------) + I joso 0x00248646, // n0x09fa c0x0000 (---------------) + I kamisu 0x0021bb86, // n0x09fb c0x0000 (---------------) + I kasama 0x002aa107, // n0x09fc c0x0000 (---------------) + I kashima 0x0021c78b, // n0x09fd c0x0000 (---------------) + I kasumigaura 0x0028d284, // n0x09fe c0x0000 (---------------) + I koga 0x002d61c4, // n0x09ff c0x0000 (---------------) + I miho 0x002b7b84, // n0x0a00 c0x0000 (---------------) + I mito 0x002bc786, // n0x0a01 c0x0000 (---------------) + I moriya 0x00202744, // n0x0a02 c0x0000 (---------------) + I naka 0x00324f88, // n0x0a03 c0x0000 (---------------) + I namegata 0x00309285, // n0x0a04 c0x0000 (---------------) + I oarai 0x00232ac5, // n0x0a05 c0x0000 (---------------) + I ogawa 0x002b6f47, // n0x0a06 c0x0000 (---------------) + I omitama 0x0021a249, // n0x0a07 c0x0000 (---------------) + I ryugasaki 0x00319545, // n0x0a08 c0x0000 (---------------) + I sakai 0x0021234a, // n0x0a09 c0x0000 (---------------) + I sakuragawa 0x002d92c9, // n0x0a0a c0x0000 (---------------) + I shimodate 0x002cdaca, // n0x0a0b c0x0000 (---------------) + I shimotsuma 0x00226f09, // n0x0a0c c0x0000 (---------------) + I shirosato 0x002df544, // n0x0a0d c0x0000 (---------------) + I sowa 0x002d6745, // n0x0a0e c0x0000 (---------------) + I suifu 0x002d2f48, // n0x0a0f c0x0000 (---------------) + I takahagi 0x002e800b, // n0x0a10 c0x0000 (---------------) + I tamatsukuri 0x002ecd85, // n0x0a11 c0x0000 (---------------) + I tokai 0x00253706, // n0x0a12 c0x0000 (---------------) + I tomobe 0x00244844, // n0x0a13 c0x0000 (---------------) + I tone 0x0026ce46, // n0x0a14 c0x0000 (---------------) + I toride 0x00201f09, // n0x0a15 c0x0000 (---------------) + I tsuchiura 0x0033db87, // n0x0a16 c0x0000 (---------------) + I tsukuba 0x00296808, // n0x0a17 c0x0000 (---------------) + I uchihara 0x0027fac6, // n0x0a18 c0x0000 (---------------) + I ushiku 0x00359a47, // n0x0a19 c0x0000 (---------------) + I yachiyo 0x00271688, // n0x0a1a c0x0000 (---------------) + I yamagata 0x0033b8c6, // n0x0a1b c0x0000 (---------------) + I yawara 0x0020e444, // n0x0a1c c0x0000 (---------------) + I yuki 0x00243447, // n0x0a1d c0x0000 (---------------) + I anamizu 0x00278005, // n0x0a1e c0x0000 (---------------) + I hakui 0x002785c7, // n0x0a1f c0x0000 (---------------) + I hakusan 0x00200104, // n0x0a20 c0x0000 (---------------) + I kaga 0x00256786, // n0x0a21 c0x0000 (---------------) + I kahoku 0x0021b288, // n0x0a22 c0x0000 (---------------) + I kanazawa 0x00286008, // n0x0a23 c0x0000 (---------------) + I kawakita 0x002a4147, // n0x0a24 c0x0000 (---------------) + I komatsu 0x0035bdc8, // n0x0a25 c0x0000 (---------------) + I nakanoto 0x00281b05, // n0x0a26 c0x0000 (---------------) + I nanao 0x0020fc04, // n0x0a27 c0x0000 (---------------) + I nomi 0x00295208, // n0x0a28 c0x0000 (---------------) + I nonoichi 0x002b9784, // n0x0a29 c0x0000 (---------------) + I noto 0x00219a85, // n0x0a2a c0x0000 (---------------) + I shika 0x002e48c4, // n0x0a2b c0x0000 (---------------) + I suzu 0x00201a47, // n0x0a2c c0x0000 (---------------) + I tsubata 0x00330887, // n0x0a2d c0x0000 (---------------) + I tsurugi 0x00272008, // n0x0a2e c0x0000 (---------------) + I uchinada 0x002912c6, // n0x0a2f c0x0000 (---------------) + I wajima 0x002ac285, // n0x0a30 c0x0000 (---------------) + I fudai 0x0026c708, // n0x0a31 c0x0000 (---------------) + I fujisawa 0x002775c8, // n0x0a32 c0x0000 (---------------) + I hanamaki 0x0028df09, // n0x0a33 c0x0000 (---------------) + I hiraizumi 0x0021f086, // n0x0a34 c0x0000 (---------------) + I hirono 0x00207bc8, // n0x0a35 c0x0000 (---------------) + I ichinohe 0x002a138a, // n0x0a36 c0x0000 (---------------) + I ichinoseki 0x002e0e08, // n0x0a37 c0x0000 (---------------) + I iwaizumi 0x0029dc45, // n0x0a38 c0x0000 (---------------) + I iwate 0x00273286, // n0x0a39 c0x0000 (---------------) + I joboji 0x0024ba48, // n0x0a3a c0x0000 (---------------) + I kamaishi 0x0022294a, // n0x0a3b c0x0000 (---------------) + I kanegasaki 0x0035c807, // n0x0a3c c0x0000 (---------------) + I karumai 0x00331405, // n0x0a3d c0x0000 (---------------) + I kawai 0x00278cc8, // n0x0a3e c0x0000 (---------------) + I kitakami 0x00274384, // n0x0a3f c0x0000 (---------------) + I kuji 0x00297c06, // n0x0a40 c0x0000 (---------------) + I kunohe 0x002b25c8, // n0x0a41 c0x0000 (---------------) + I kuzumaki 0x0020fc86, // n0x0a42 c0x0000 (---------------) + I miyako 0x00211ec8, // n0x0a43 c0x0000 (---------------) + I mizusawa 0x00261c87, // n0x0a44 c0x0000 (---------------) + I morioka 0x0020eb86, // n0x0a45 c0x0000 (---------------) + I ninohe 0x00311f44, // n0x0a46 c0x0000 (---------------) + I noda 0x002fbcc7, // n0x0a47 c0x0000 (---------------) + I ofunato 0x0029f404, // n0x0a48 c0x0000 (---------------) + I oshu 0x00201ec7, // n0x0a49 c0x0000 (---------------) + I otsuchi 0x00353f0d, // n0x0a4a c0x0000 (---------------) + I rikuzentakata 0x0020f405, // n0x0a4b c0x0000 (---------------) + I shiwa 0x002d90cb, // n0x0a4c c0x0000 (---------------) + I shizukuishi 0x00206d46, // n0x0a4d c0x0000 (---------------) + I sumita 0x00352548, // n0x0a4e c0x0000 (---------------) + I takizawa 0x0024c648, // n0x0a4f c0x0000 (---------------) + I tanohata 0x00230f84, // n0x0a50 c0x0000 (---------------) + I tono 0x002ae246, // n0x0a51 c0x0000 (---------------) + I yahaba 0x0026e746, // n0x0a52 c0x0000 (---------------) + I yamada 0x0026a7c7, // n0x0a53 c0x0000 (---------------) + I ayagawa 0x0028568d, // n0x0a54 c0x0000 (---------------) + I higashikagawa 0x002d9687, // n0x0a55 c0x0000 (---------------) + I kanonji 0x002a0008, // n0x0a56 c0x0000 (---------------) + I kotohira 0x0025ae85, // n0x0a57 c0x0000 (---------------) + I manno 0x0024a508, // n0x0a58 c0x0000 (---------------) + I marugame 0x002b8206, // n0x0a59 c0x0000 (---------------) + I mitoyo 0x00281b88, // n0x0a5a c0x0000 (---------------) + I naoshima 0x00242806, // n0x0a5b c0x0000 (---------------) + I sanuki 0x00305f87, // n0x0a5c c0x0000 (---------------) + I tadotsu 0x002d0e09, // n0x0a5d c0x0000 (---------------) + I takamatsu 0x00230f87, // n0x0a5e c0x0000 (---------------) + I tonosho 0x00276588, // n0x0a5f c0x0000 (---------------) + I uchinomi 0x00267685, // n0x0a60 c0x0000 (---------------) + I utazu 0x00217a88, // n0x0a61 c0x0000 (---------------) + I zentsuji 0x00254d05, // n0x0a62 c0x0000 (---------------) + I akune 0x0025ce05, // n0x0a63 c0x0000 (---------------) + I amami 0x00334905, // n0x0a64 c0x0000 (---------------) + I hioki 0x0022ec43, // n0x0a65 c0x0000 (---------------) + I isa 0x00273f44, // n0x0a66 c0x0000 (---------------) + I isen 0x00278ec5, // n0x0a67 c0x0000 (---------------) + I izumi 0x00236209, // n0x0a68 c0x0000 (---------------) + I kagoshima 0x002e4606, // n0x0a69 c0x0000 (---------------) + I kanoya 0x002a5908, // n0x0a6a c0x0000 (---------------) + I kawanabe 0x0023bb85, // n0x0a6b c0x0000 (---------------) + I kinko 0x002a1787, // n0x0a6c c0x0000 (---------------) + I kouyama 0x002aed8a, // n0x0a6d c0x0000 (---------------) + I makurazaki 0x00338b49, // n0x0a6e c0x0000 (---------------) + I matsumoto 0x0027554a, // n0x0a6f c0x0000 (---------------) + I minamitane 0x002c1508, // n0x0a70 c0x0000 (---------------) + I nakatane 0x002237cc, // n0x0a71 c0x0000 (---------------) + I nishinoomote 0x00273c4d, // n0x0a72 c0x0000 (---------------) + I satsumasendai 0x002dc303, // n0x0a73 c0x0000 (---------------) + I soo 0x00211dc8, // n0x0a74 c0x0000 (---------------) + I tarumizu 0x00222e05, // n0x0a75 c0x0000 (---------------) + I yusui 0x0021dd06, // n0x0a76 c0x0000 (---------------) + I aikawa 0x003220c6, // n0x0a77 c0x0000 (---------------) + I atsugi 0x0020ddc5, // n0x0a78 c0x0000 (---------------) + I ayase 0x00357c49, // n0x0a79 c0x0000 (---------------) + I chigasaki 0x00296085, // n0x0a7a c0x0000 (---------------) + I ebina 0x0026c708, // n0x0a7b c0x0000 (---------------) + I fujisawa 0x002b9686, // n0x0a7c c0x0000 (---------------) + I hadano 0x0033c986, // n0x0a7d c0x0000 (---------------) + I hakone 0x0028f6c9, // n0x0a7e c0x0000 (---------------) + I hiratsuka 0x00343287, // n0x0a7f c0x0000 (---------------) + I isehara 0x002dd546, // n0x0a80 c0x0000 (---------------) + I kaisei 0x002aed08, // n0x0a81 c0x0000 (---------------) + I kamakura 0x00342c48, // n0x0a82 c0x0000 (---------------) + I kiyokawa 0x00302487, // n0x0a83 c0x0000 (---------------) + I matsuda 0x002df1ce, // n0x0a84 c0x0000 (---------------) + I minamiashigara 0x002b8445, // n0x0a85 c0x0000 (---------------) + I miura 0x00301d85, // n0x0a86 c0x0000 (---------------) + I nakai 0x0020fb88, // n0x0a87 c0x0000 (---------------) + I ninomiya 0x00311f87, // n0x0a88 c0x0000 (---------------) + I odawara 0x00213f42, // n0x0a89 c0x0000 (---------------) + I oi 0x002ab584, // n0x0a8a c0x0000 (---------------) + I oiso 0x0027604a, // n0x0a8b c0x0000 (---------------) + I sagamihara 0x00353c88, // n0x0a8c c0x0000 (---------------) + I samukawa 0x002905c6, // n0x0a8d c0x0000 (---------------) + I tsukui 0x00287788, // n0x0a8e c0x0000 (---------------) + I yamakita 0x00280606, // n0x0a8f c0x0000 (---------------) + I yamato 0x002e0848, // n0x0a90 c0x0000 (---------------) + I yokosuka 0x0029ce48, // n0x0a91 c0x0000 (---------------) + I yugawara 0x0025cdc4, // n0x0a92 c0x0000 (---------------) + I zama 0x002a1c45, // n0x0a93 c0x0000 (---------------) + I zushi 0x0062da04, // n0x0a94 c0x0001 (---------------) ! I city 0x0062da04, // n0x0a95 c0x0001 (---------------) ! I city 0x0062da04, // n0x0a96 c0x0001 (---------------) ! I city 0x00205d43, // n0x0a97 c0x0000 (---------------) + I aki 0x002b4646, // n0x0a98 c0x0000 (---------------) + I geisei 0x0026cbc6, // n0x0a99 c0x0000 (---------------) + I hidaka 0x0028c2cc, // n0x0a9a c0x0000 (---------------) + I higashitsuno 0x00201e43, // n0x0a9b c0x0000 (---------------) + I ino 0x002b3f86, // n0x0a9c c0x0000 (---------------) + I kagami 0x002006c4, // n0x0a9d c0x0000 (---------------) + I kami 0x00271b48, // n0x0a9e c0x0000 (---------------) + I kitagawa 0x002bfe45, // n0x0a9f c0x0000 (---------------) + I kochi 0x00276146, // n0x0aa0 c0x0000 (---------------) + I mihara 0x003389c8, // n0x0aa1 c0x0000 (---------------) + I motoyama 0x002c1ec6, // n0x0aa2 c0x0000 (---------------) + I muroto 0x002ac7c6, // n0x0aa3 c0x0000 (---------------) + I nahari 0x00305848, // n0x0aa4 c0x0000 (---------------) + I nakamura 0x00284887, // n0x0aa5 c0x0000 (---------------) + I nankoku 0x00228d09, // n0x0aa6 c0x0000 (---------------) + I nishitosa 0x0026f18a, // n0x0aa7 c0x0000 (---------------) + I niyodogawa 0x00295bc4, // n0x0aa8 c0x0000 (---------------) + I ochi 0x00245ac5, // n0x0aa9 c0x0000 (---------------) + I okawa 0x00252205, // n0x0aaa c0x0000 (---------------) + I otoyo 0x002d0b86, // n0x0aab c0x0000 (---------------) + I otsuki 0x00295e86, // n0x0aac c0x0000 (---------------) + I sakawa 0x00286506, // n0x0aad c0x0000 (---------------) + I sukumo 0x002e3f86, // n0x0aae c0x0000 (---------------) + I susaki 0x00228e44, // n0x0aaf c0x0000 (---------------) + I tosa 0x00228e4b, // n0x0ab0 c0x0000 (---------------) + I tosashimizu 0x00245a04, // n0x0ab1 c0x0000 (---------------) + I toyo 0x00220b45, // n0x0ab2 c0x0000 (---------------) + I tsuno 0x0029bd05, // n0x0ab3 c0x0000 (---------------) + I umaji 0x0028cac6, // n0x0ab4 c0x0000 (---------------) + I yasuda 0x00221a88, // n0x0ab5 c0x0000 (---------------) + I yusuhara 0x00273b07, // n0x0ab6 c0x0000 (---------------) + I amakusa 0x002aff84, // n0x0ab7 c0x0000 (---------------) + I arao 0x00221943, // n0x0ab8 c0x0000 (---------------) + I aso 0x00316c05, // n0x0ab9 c0x0000 (---------------) + I choyo 0x00246287, // n0x0aba c0x0000 (---------------) + I gyokuto 0x00292289, // n0x0abb c0x0000 (---------------) + I hitoyoshi 0x00273a0b, // n0x0abc c0x0000 (---------------) + I kamiamakusa 0x002aa107, // n0x0abd c0x0000 (---------------) + I kashima 0x0026a4c7, // n0x0abe c0x0000 (---------------) + I kikuchi 0x0029e284, // n0x0abf c0x0000 (---------------) + I kosa 0x003388c8, // n0x0ac0 c0x0000 (---------------) + I kumamoto 0x0031a7c7, // n0x0ac1 c0x0000 (---------------) + I mashiki 0x002882c6, // n0x0ac2 c0x0000 (---------------) + I mifune 0x002f9308, // n0x0ac3 c0x0000 (---------------) + I minamata 0x0025ea8b, // n0x0ac4 c0x0000 (---------------) + I minamioguni 0x00339e06, // n0x0ac5 c0x0000 (---------------) + I nagasu 0x002177c9, // n0x0ac6 c0x0000 (---------------) + I nishihara 0x0025ec05, // n0x0ac7 c0x0000 (---------------) + I oguni 0x00227a03, // n0x0ac8 c0x0000 (---------------) + I ozu 0x00338c06, // n0x0ac9 c0x0000 (---------------) + I sumoto 0x00261b88, // n0x0aca c0x0000 (---------------) + I takamori 0x0020e483, // n0x0acb c0x0000 (---------------) + I uki 0x00230c83, // n0x0acc c0x0000 (---------------) + I uto 0x00271686, // n0x0acd c0x0000 (---------------) + I yamaga 0x00280606, // n0x0ace c0x0000 (---------------) + I yamato 0x00333a8a, // n0x0acf c0x0000 (---------------) + I yatsushiro 0x0026d885, // n0x0ad0 c0x0000 (---------------) + I ayabe 0x0026e58b, // n0x0ad1 c0x0000 (---------------) + I fukuchiyama 0x0028cc4b, // n0x0ad2 c0x0000 (---------------) + I higashiyama 0x0022b583, // n0x0ad3 c0x0000 (---------------) + I ide 0x00200943, // n0x0ad4 c0x0000 (---------------) + I ine 0x002d0884, // n0x0ad5 c0x0000 (---------------) + I joyo 0x00261f87, // n0x0ad6 c0x0000 (---------------) + I kameoka 0x00261c04, // n0x0ad7 c0x0000 (---------------) + I kamo 0x00207ec4, // n0x0ad8 c0x0000 (---------------) + I kita 0x00210484, // n0x0ad9 c0x0000 (---------------) + I kizu 0x002e1108, // n0x0ada c0x0000 (---------------) + I kumiyama 0x002ae848, // n0x0adb c0x0000 (---------------) + I kyotamba 0x00216349, // n0x0adc c0x0000 (---------------) + I kyotanabe 0x0034adc8, // n0x0add c0x0000 (---------------) + I kyotango 0x002271c7, // n0x0ade c0x0000 (---------------) + I maizuru 0x00227b06, // n0x0adf c0x0000 (---------------) + I minami 0x002a554f, // n0x0ae0 c0x0000 (---------------) + I minamiyamashiro 0x002b8586, // n0x0ae1 c0x0000 (---------------) + I miyazu 0x002bfdc4, // n0x0ae2 c0x0000 (---------------) + I muko 0x002ae68a, // n0x0ae3 c0x0000 (---------------) + I nagaokakyo 0x00246187, // n0x0ae4 c0x0000 (---------------) + I nakagyo 0x002c9346, // n0x0ae5 c0x0000 (---------------) + I nantan 0x0027ff89, // n0x0ae6 c0x0000 (---------------) + I oyamazaki 0x002162c5, // n0x0ae7 c0x0000 (---------------) + I sakyo 0x0030b545, // n0x0ae8 c0x0000 (---------------) + I seika 0x00216406, // n0x0ae9 c0x0000 (---------------) + I tanabe 0x00217bc3, // n0x0aea c0x0000 (---------------) + I uji 0x002743c9, // n0x0aeb c0x0000 (---------------) + I ujitawara 0x0021adc6, // n0x0aec c0x0000 (---------------) + I wazuka 0x002621c9, // n0x0aed c0x0000 (---------------) + I yamashina 0x00341b06, // n0x0aee c0x0000 (---------------) + I yawata 0x002b6285, // n0x0aef c0x0000 (---------------) + I asahi 0x00214505, // n0x0af0 c0x0000 (---------------) + I inabe 0x00244f03, // n0x0af1 c0x0000 (---------------) + I ise 0x002620c8, // n0x0af2 c0x0000 (---------------) + I kameyama 0x00295f07, // n0x0af3 c0x0000 (---------------) + I kawagoe 0x00206184, // n0x0af4 c0x0000 (---------------) + I kiho 0x00270b48, // n0x0af5 c0x0000 (---------------) + I kisosaki 0x0031a904, // n0x0af6 c0x0000 (---------------) + I kiwa 0x002c1d46, // n0x0af7 c0x0000 (---------------) + I komono 0x002dae86, // n0x0af8 c0x0000 (---------------) + I kumano 0x00243386, // n0x0af9 c0x0000 (---------------) + I kuwana 0x002aa249, // n0x0afa c0x0000 (---------------) + I matsusaka 0x00245105, // n0x0afb c0x0000 (---------------) + I meiwa 0x002924c6, // n0x0afc c0x0000 (---------------) + I mihama 0x00247789, // n0x0afd c0x0000 (---------------) + I minamiise 0x002b7206, // n0x0afe c0x0000 (---------------) + I misugi 0x002a5646, // n0x0aff c0x0000 (---------------) + I miyama 0x0033eb86, // n0x0b00 c0x0000 (---------------) + I nabari 0x00222805, // n0x0b01 c0x0000 (---------------) + I shima 0x002e48c6, // n0x0b02 c0x0000 (---------------) + I suzuka 0x00305f84, // n0x0b03 c0x0000 (---------------) + I tado 0x00227785, // n0x0b04 c0x0000 (---------------) + I taiki 0x002ad204, // n0x0b05 c0x0000 (---------------) + I taki 0x002a08c6, // n0x0b06 c0x0000 (---------------) + I tamaki 0x002270c4, // n0x0b07 c0x0000 (---------------) + I toba 0x00201a43, // n0x0b08 c0x0000 (---------------) + I tsu 0x00275885, // n0x0b09 c0x0000 (---------------) + I udono 0x0023b648, // n0x0b0a c0x0000 (---------------) + I ureshino 0x0029d987, // n0x0b0b c0x0000 (---------------) + I watarai 0x00279289, // n0x0b0c c0x0000 (---------------) + I yokkaichi 0x00275ac8, // n0x0b0d c0x0000 (---------------) + I furukawa 0x00286d11, // n0x0b0e c0x0000 (---------------) + I higashimatsushima 0x002602ca, // n0x0b0f c0x0000 (---------------) + I ishinomaki 0x002dda47, // n0x0b10 c0x0000 (---------------) + I iwanuma 0x002040c6, // n0x0b11 c0x0000 (---------------) + I kakuda 0x002006c4, // n0x0b12 c0x0000 (---------------) + I kami 0x002ad308, // n0x0b13 c0x0000 (---------------) + I kawasaki 0x00205509, // n0x0b14 c0x0000 (---------------) + I kesennuma 0x0032b388, // n0x0b15 c0x0000 (---------------) + I marumori 0x00286eca, // n0x0b16 c0x0000 (---------------) + I matsushima 0x0026324d, // n0x0b17 c0x0000 (---------------) + I minamisanriku 0x00242e46, // n0x0b18 c0x0000 (---------------) + I misato 0x00305946, // n0x0b19 c0x0000 (---------------) + I murata 0x002fbd86, // n0x0b1a c0x0000 (---------------) + I natori 0x00232ac7, // n0x0b1b c0x0000 (---------------) + I ogawara 0x002a00c5, // n0x0b1c c0x0000 (---------------) + I ohira 0x0021d807, // n0x0b1d c0x0000 (---------------) + I onagawa 0x00270c05, // n0x0b1e c0x0000 (---------------) + I osaki 0x00304dc4, // n0x0b1f c0x0000 (---------------) + I rifu 0x00222f86, // n0x0b20 c0x0000 (---------------) + I semine 0x00206987, // n0x0b21 c0x0000 (---------------) + I shibata 0x002303cd, // n0x0b22 c0x0000 (---------------) + I shichikashuku 0x0024b987, // n0x0b23 c0x0000 (---------------) + I shikama 0x002bd508, // n0x0b24 c0x0000 (---------------) + I shiogama 0x0026ca09, // n0x0b25 c0x0000 (---------------) + I shiroishi 0x0028f046, // n0x0b26 c0x0000 (---------------) + I tagajo 0x00240f05, // n0x0b27 c0x0000 (---------------) + I taiwa 0x00245084, // n0x0b28 c0x0000 (---------------) + I tome 0x002575c6, // n0x0b29 c0x0000 (---------------) + I tomiya 0x0021d946, // n0x0b2a c0x0000 (---------------) + I wakuya 0x00353e06, // n0x0b2b c0x0000 (---------------) + I watari 0x0028a348, // n0x0b2c c0x0000 (---------------) + I yamamoto 0x00226cc3, // n0x0b2d c0x0000 (---------------) + I zao 0x00207a83, // n0x0b2e c0x0000 (---------------) + I aya 0x002aaf05, // n0x0b2f c0x0000 (---------------) + I ebino 0x002ac3c6, // n0x0b30 c0x0000 (---------------) + I gokase 0x0029ce05, // n0x0b31 c0x0000 (---------------) + I hyuga 0x0027fcc8, // n0x0b32 c0x0000 (---------------) + I kadogawa 0x0028bcca, // n0x0b33 c0x0000 (---------------) + I kawaminami 0x002ea544, // n0x0b34 c0x0000 (---------------) + I kijo 0x00271b48, // n0x0b35 c0x0000 (---------------) + I kitagawa 0x00280148, // n0x0b36 c0x0000 (---------------) + I kitakata 0x0028c907, // n0x0b37 c0x0000 (---------------) + I kitaura 0x0023bc49, // n0x0b38 c0x0000 (---------------) + I kobayashi 0x002a7848, // n0x0b39 c0x0000 (---------------) + I kunitomi 0x002704c7, // n0x0b3a c0x0000 (---------------) + I kushima 0x002a9f86, // n0x0b3b c0x0000 (---------------) + I mimata 0x0020fc8a, // n0x0b3c c0x0000 (---------------) + I miyakonojo 0x00257648, // n0x0b3d c0x0000 (---------------) + I miyazaki 0x002c2d89, // n0x0b3e c0x0000 (---------------) + I morotsuka 0x002cf088, // n0x0b3f c0x0000 (---------------) + I nichinan 0x0021d0c9, // n0x0b40 c0x0000 (---------------) + I nishimera 0x002c3207, // n0x0b41 c0x0000 (---------------) + I nobeoka 0x00324dc5, // n0x0b42 c0x0000 (---------------) + I saito 0x0029c506, // n0x0b43 c0x0000 (---------------) + I shiiba 0x002f0408, // n0x0b44 c0x0000 (---------------) + I shintomi 0x00270e48, // n0x0b45 c0x0000 (---------------) + I takaharu 0x002625c8, // n0x0b46 c0x0000 (---------------) + I takanabe 0x002ddc08, // n0x0b47 c0x0000 (---------------) + I takazaki 0x00220b45, // n0x0b48 c0x0000 (---------------) + I tsuno 0x0021da84, // n0x0b49 c0x0000 (---------------) + I achi 0x002d4fc8, // n0x0b4a c0x0000 (---------------) + I agematsu 0x0021af04, // n0x0b4b c0x0000 (---------------) + I anan 0x00226d04, // n0x0b4c c0x0000 (---------------) + I aoki 0x002b6285, // n0x0b4d c0x0000 (---------------) + I asahi 0x00282287, // n0x0b4e c0x0000 (---------------) + I azumino 0x0021a009, // n0x0b4f c0x0000 (---------------) + I chikuhoku 0x0026a5c7, // n0x0b50 c0x0000 (---------------) + I chikuma 0x00201dc5, // n0x0b51 c0x0000 (---------------) + I chino 0x0026b5c6, // n0x0b52 c0x0000 (---------------) + I fujimi 0x00357a06, // n0x0b53 c0x0000 (---------------) + I hakuba 0x0020a104, // n0x0b54 c0x0000 (---------------) + I hara 0x0028fa06, // n0x0b55 c0x0000 (---------------) + I hiraya 0x00203d04, // n0x0b56 c0x0000 (---------------) + I iida 0x00281346, // n0x0b57 c0x0000 (---------------) + I iijima 0x0020ae86, // n0x0b58 c0x0000 (---------------) + I iiyama 0x00218a46, // n0x0b59 c0x0000 (---------------) + I iizuna 0x00203ac5, // n0x0b5a c0x0000 (---------------) + I ikeda 0x0027fb87, // n0x0b5b c0x0000 (---------------) + I ikusaka 0x00204b03, // n0x0b5c c0x0000 (---------------) + I ina 0x0032f8c9, // n0x0b5d c0x0000 (---------------) + I karuizawa 0x002dd248, // n0x0b5e c0x0000 (---------------) + I kawakami 0x00270344, // n0x0b5f c0x0000 (---------------) + I kiso 0x0027034d, // n0x0b60 c0x0000 (---------------) + I kisofukushima 0x00286108, // n0x0b61 c0x0000 (---------------) + I kitaaiki 0x003549c8, // n0x0b62 c0x0000 (---------------) + I komagane 0x002c2d06, // n0x0b63 c0x0000 (---------------) + I komoro 0x002d0f09, // n0x0b64 c0x0000 (---------------) + I matsukawa 0x00338b49, // n0x0b65 c0x0000 (---------------) + I matsumoto 0x00201c05, // n0x0b66 c0x0000 (---------------) + I miasa 0x0028bdca, // n0x0b67 c0x0000 (---------------) + I minamiaiki 0x0025190a, // n0x0b68 c0x0000 (---------------) + I minamimaki 0x00255d8c, // n0x0b69 c0x0000 (---------------) + I minamiminowa 0x00255f06, // n0x0b6a c0x0000 (---------------) + I minowa 0x0026ba86, // n0x0b6b c0x0000 (---------------) + I miyada 0x002b9006, // n0x0b6c c0x0000 (---------------) + I miyota 0x00329a49, // n0x0b6d c0x0000 (---------------) + I mochizuki 0x002d5486, // n0x0b6e c0x0000 (---------------) + I nagano 0x0021d846, // n0x0b6f c0x0000 (---------------) + I nagawa 0x00296146, // n0x0b70 c0x0000 (---------------) + I nagiso 0x002c6f08, // n0x0b71 c0x0000 (---------------) + I nakagawa 0x0030ab86, // n0x0b72 c0x0000 (---------------) + I nakano 0x00206fcb, // n0x0b73 c0x0000 (---------------) + I nozawaonsen 0x00282405, // n0x0b74 c0x0000 (---------------) + I obuse 0x00232ac5, // n0x0b75 c0x0000 (---------------) + I ogawa 0x0026bd05, // n0x0b76 c0x0000 (---------------) + I okaya 0x002a3706, // n0x0b77 c0x0000 (---------------) + I omachi 0x0020fc43, // n0x0b78 c0x0000 (---------------) + I omi 0x00243306, // n0x0b79 c0x0000 (---------------) + I ookuwa 0x0024b907, // n0x0b7a c0x0000 (---------------) + I ooshika 0x002ad1c5, // n0x0b7b c0x0000 (---------------) + I otaki 0x00254505, // n0x0b7c c0x0000 (---------------) + I otari 0x0029e305, // n0x0b7d c0x0000 (---------------) + I sakae 0x00271086, // n0x0b7e c0x0000 (---------------) + I sakaki 0x00201cc4, // n0x0b7f c0x0000 (---------------) + I saku 0x00211486, // n0x0b80 c0x0000 (---------------) + I sakuho 0x002ca3c9, // n0x0b81 c0x0000 (---------------) + I shimosuwa 0x002a358c, // n0x0b82 c0x0000 (---------------) + I shinanomachi 0x00304b08, // n0x0b83 c0x0000 (---------------) + I shiojiri 0x0025c144, // n0x0b84 c0x0000 (---------------) + I suwa 0x002e4506, // n0x0b85 c0x0000 (---------------) + I suzaka 0x00206e46, // n0x0b86 c0x0000 (---------------) + I takagi 0x00261b88, // n0x0b87 c0x0000 (---------------) + I takamori 0x002f5e08, // n0x0b88 c0x0000 (---------------) + I takayama 0x002a3489, // n0x0b89 c0x0000 (---------------) + I tateshina 0x00220ac7, // n0x0b8a c0x0000 (---------------) + I tatsuno 0x002947c9, // n0x0b8b c0x0000 (---------------) + I togakushi 0x00264406, // n0x0b8c c0x0000 (---------------) + I togura 0x002575c4, // n0x0b8d c0x0000 (---------------) + I tomi 0x00216684, // n0x0b8e c0x0000 (---------------) + I ueda 0x0026ff44, // n0x0b8f c0x0000 (---------------) + I wada 0x00271688, // n0x0b90 c0x0000 (---------------) + I yamagata 0x00219e4a, // n0x0b91 c0x0000 (---------------) + I yamanouchi 0x003194c6, // n0x0b92 c0x0000 (---------------) + I yasaka 0x0031e647, // n0x0b93 c0x0000 (---------------) + I yasuoka 0x00233747, // n0x0b94 c0x0000 (---------------) + I chijiwa 0x0024ef45, // n0x0b95 c0x0000 (---------------) + I futsu 0x002db804, // n0x0b96 c0x0000 (---------------) + I goto 0x0027e2c6, // n0x0b97 c0x0000 (---------------) + I hasami 0x002a0106, // n0x0b98 c0x0000 (---------------) + I hirado 0x00206143, // n0x0b99 c0x0000 (---------------) + I iki 0x002dd087, // n0x0b9a c0x0000 (---------------) + I isahaya 0x0027b388, // n0x0b9b c0x0000 (---------------) + I kawatana 0x00201d4a, // n0x0b9c c0x0000 (---------------) + I kuchinotsu 0x0030efc8, // n0x0b9d c0x0000 (---------------) + I matsuura 0x002f0f48, // n0x0b9e c0x0000 (---------------) + I nagasaki 0x00227105, // n0x0b9f c0x0000 (---------------) + I obama 0x00215205, // n0x0ba0 c0x0000 (---------------) + I omura 0x002cac85, // n0x0ba1 c0x0000 (---------------) + I oseto 0x0024f306, // n0x0ba2 c0x0000 (---------------) + I saikai 0x00254286, // n0x0ba3 c0x0000 (---------------) + I sasebo 0x002c0005, // n0x0ba4 c0x0000 (---------------) + I seihi 0x00313309, // n0x0ba5 c0x0000 (---------------) + I shimabara 0x002db60c, // n0x0ba6 c0x0000 (---------------) + I shinkamigoto 0x002cad47, // n0x0ba7 c0x0000 (---------------) + I togitsu 0x00286f48, // n0x0ba8 c0x0000 (---------------) + I tsushima 0x0027d305, // n0x0ba9 c0x0000 (---------------) + I unzen 0x0062da04, // n0x0baa c0x0001 (---------------) ! I city 0x0022ab84, // n0x0bab c0x0000 (---------------) + I ando 0x00210b84, // n0x0bac c0x0000 (---------------) + I gose 0x002d8506, // n0x0bad c0x0000 (---------------) + I heguri 0x0028d7ce, // n0x0bae c0x0000 (---------------) + I higashiyoshino 0x0020aa07, // n0x0baf c0x0000 (---------------) + I ikaruga 0x00232cc5, // n0x0bb0 c0x0000 (---------------) + I ikoma 0x0028040c, // n0x0bb1 c0x0000 (---------------) + I kamikitayama 0x002d2147, // n0x0bb2 c0x0000 (---------------) + I kanmaki 0x00206907, // n0x0bb3 c0x0000 (---------------) + I kashiba 0x00209fc9, // n0x0bb4 c0x0000 (---------------) + I kashihara 0x0021a6c9, // n0x0bb5 c0x0000 (---------------) + I katsuragi 0x00331405, // n0x0bb6 c0x0000 (---------------) + I kawai 0x002dd248, // n0x0bb7 c0x0000 (---------------) + I kawakami 0x002c34c9, // n0x0bb8 c0x0000 (---------------) + I kawanishi 0x002fb105, // n0x0bb9 c0x0000 (---------------) + I koryo 0x002ad108, // n0x0bba c0x0000 (---------------) + I kurotaki 0x002bfc46, // n0x0bbb c0x0000 (---------------) + I mitsue 0x00350306, // n0x0bbc c0x0000 (---------------) + I miyake 0x00340bc4, // n0x0bbd c0x0000 (---------------) + I nara 0x0025af48, // n0x0bbe c0x0000 (---------------) + I nosegawa 0x00261a03, // n0x0bbf c0x0000 (---------------) + I oji 0x00241684, // n0x0bc0 c0x0000 (---------------) + I ouda 0x00316c85, // n0x0bc1 c0x0000 (---------------) + I oyodo 0x00214a07, // n0x0bc2 c0x0000 (---------------) + I sakurai 0x002acfc5, // n0x0bc3 c0x0000 (---------------) + I sango 0x002a1249, // n0x0bc4 c0x0000 (---------------) + I shimoichi 0x002bedcd, // n0x0bc5 c0x0000 (---------------) + I shimokitayama 0x002d0786, // n0x0bc6 c0x0000 (---------------) + I shinjo 0x00294544, // n0x0bc7 c0x0000 (---------------) + I soni 0x00219bc8, // n0x0bc8 c0x0000 (---------------) + I takatori 0x00213d0a, // n0x0bc9 c0x0000 (---------------) + I tawaramoto 0x0024a987, // n0x0bca c0x0000 (---------------) + I tenkawa 0x002e8e05, // n0x0bcb c0x0000 (---------------) + I tenri 0x00204183, // n0x0bcc c0x0000 (---------------) + I uda 0x0028ce0e, // n0x0bcd c0x0000 (---------------) + I yamatokoriyama 0x0028060c, // n0x0bce c0x0000 (---------------) + I yamatotakada 0x00303847, // n0x0bcf c0x0000 (---------------) + I yamazoe 0x0028d987, // n0x0bd0 c0x0000 (---------------) + I yoshino 0x00200143, // n0x0bd1 c0x0000 (---------------) + I aga 0x002d54c5, // n0x0bd2 c0x0000 (---------------) + I agano 0x00210b85, // n0x0bd3 c0x0000 (---------------) + I gosen 0x00287b48, // n0x0bd4 c0x0000 (---------------) + I itoigawa 0x00283609, // n0x0bd5 c0x0000 (---------------) + I izumozaki 0x0028f146, // n0x0bd6 c0x0000 (---------------) + I joetsu 0x00261c04, // n0x0bd7 c0x0000 (---------------) + I kamo 0x002dd986, // n0x0bd8 c0x0000 (---------------) + I kariwa 0x0021024b, // n0x0bd9 c0x0000 (---------------) + I kashiwazaki 0x0030ed4c, // n0x0bda c0x0000 (---------------) + I minamiuonuma 0x0026fa07, // n0x0bdb c0x0000 (---------------) + I mitsuke 0x002bfb05, // n0x0bdc c0x0000 (---------------) + I muika 0x0027a548, // n0x0bdd c0x0000 (---------------) + I murakami 0x00302245, // n0x0bde c0x0000 (---------------) + I myoko 0x002ae687, // n0x0bdf c0x0000 (---------------) + I nagaoka 0x002945c7, // n0x0be0 c0x0000 (---------------) + I niigata 0x00261a05, // n0x0be1 c0x0000 (---------------) + I ojiya 0x0020fc43, // n0x0be2 c0x0000 (---------------) + I omi 0x002ac684, // n0x0be3 c0x0000 (---------------) + I sado 0x0022e145, // n0x0be4 c0x0000 (---------------) + I sanjo 0x002b4705, // n0x0be5 c0x0000 (---------------) + I seiro 0x002b4706, // n0x0be6 c0x0000 (---------------) + I seirou 0x002c7d88, // n0x0be7 c0x0000 (---------------) + I sekikawa 0x00206987, // n0x0be8 c0x0000 (---------------) + I shibata 0x003223c6, // n0x0be9 c0x0000 (---------------) + I tagami 0x0021dc06, // n0x0bea c0x0000 (---------------) + I tainai 0x00334846, // n0x0beb c0x0000 (---------------) + I tochio 0x0029f5c9, // n0x0bec c0x0000 (---------------) + I tokamachi 0x00311887, // n0x0bed c0x0000 (---------------) + I tsubame 0x002773c6, // n0x0bee c0x0000 (---------------) + I tsunan 0x0030eec6, // n0x0bef c0x0000 (---------------) + I uonuma 0x0026a106, // n0x0bf0 c0x0000 (---------------) + I yahiko 0x002d0905, // n0x0bf1 c0x0000 (---------------) + I yoita 0x0022ac86, // n0x0bf2 c0x0000 (---------------) + I yuzawa 0x00339b85, // n0x0bf3 c0x0000 (---------------) + I beppu 0x0034ff48, // n0x0bf4 c0x0000 (---------------) + I bungoono 0x0025c74b, // n0x0bf5 c0x0000 (---------------) + I bungotakada 0x0027e0c6, // n0x0bf6 c0x0000 (---------------) + I hasama 0x00233784, // n0x0bf7 c0x0000 (---------------) + I hiji 0x00222709, // n0x0bf8 c0x0000 (---------------) + I himeshima 0x00290b04, // n0x0bf9 c0x0000 (---------------) + I hita 0x002bfbc8, // n0x0bfa c0x0000 (---------------) + I kamitsue 0x002de3c7, // n0x0bfb c0x0000 (---------------) + I kokonoe 0x00230684, // n0x0bfc c0x0000 (---------------) + I kuju 0x002a6b88, // n0x0bfd c0x0000 (---------------) + I kunisaki 0x002b1644, // n0x0bfe c0x0000 (---------------) + I kusu 0x002d0944, // n0x0bff c0x0000 (---------------) + I oita 0x00278c05, // n0x0c00 c0x0000 (---------------) + I saiki 0x002bc1c6, // n0x0c01 c0x0000 (---------------) + I taketa 0x002e1047, // n0x0c02 c0x0000 (---------------) + I tsukumi 0x00211f83, // n0x0c03 c0x0000 (---------------) + I usa 0x0028c845, // n0x0c04 c0x0000 (---------------) + I usuki 0x00331784, // n0x0c05 c0x0000 (---------------) + I yufu 0x00301dc6, // n0x0c06 c0x0000 (---------------) + I akaiwa 0x00201c88, // n0x0c07 c0x0000 (---------------) + I asakuchi 0x00305745, // n0x0c08 c0x0000 (---------------) + I bizen 0x00281649, // n0x0c09 c0x0000 (---------------) + I hayashima 0x0023fb85, // n0x0c0a c0x0000 (---------------) + I ibara 0x002b3f88, // n0x0c0b c0x0000 (---------------) + I kagamino 0x00200587, // n0x0c0c c0x0000 (---------------) + I kasaoka 0x00227848, // n0x0c0d c0x0000 (---------------) + I kibichuo 0x002a62c7, // n0x0c0e c0x0000 (---------------) + I kumenan 0x00342909, // n0x0c0f c0x0000 (---------------) + I kurashiki 0x0032e306, // n0x0c10 c0x0000 (---------------) + I maniwa 0x002c9886, // n0x0c11 c0x0000 (---------------) + I misaki 0x00296144, // n0x0c12 c0x0000 (---------------) + I nagi 0x002df105, // n0x0c13 c0x0000 (---------------) + I niimi 0x0021268c, // n0x0c14 c0x0000 (---------------) + I nishiawakura 0x0026bd07, // n0x0c15 c0x0000 (---------------) + I okayama 0x0026c307, // n0x0c16 c0x0000 (---------------) + I satosho 0x00233608, // n0x0c17 c0x0000 (---------------) + I setouchi 0x002d0786, // n0x0c18 c0x0000 (---------------) + I shinjo 0x00243084, // n0x0c19 c0x0000 (---------------) + I shoo 0x00320844, // n0x0c1a c0x0000 (---------------) + I soja 0x00271809, // n0x0c1b c0x0000 (---------------) + I takahashi 0x002b9106, // n0x0c1c c0x0000 (---------------) + I tamano 0x002876c7, // n0x0c1d c0x0000 (---------------) + I tsuyama 0x00315844, // n0x0c1e c0x0000 (---------------) + I wake 0x0028fb06, // n0x0c1f c0x0000 (---------------) + I yakage 0x00201245, // n0x0c20 c0x0000 (---------------) + I aguni 0x00290e07, // n0x0c21 c0x0000 (---------------) + I ginowan 0x00206f46, // n0x0c22 c0x0000 (---------------) + I ginoza 0x0024b449, // n0x0c23 c0x0000 (---------------) + I gushikami 0x0030eb87, // n0x0c24 c0x0000 (---------------) + I haebaru 0x00282a87, // n0x0c25 c0x0000 (---------------) + I higashi 0x0028f546, // n0x0c26 c0x0000 (---------------) + I hirara 0x00202c45, // n0x0c27 c0x0000 (---------------) + I iheya 0x002701c8, // n0x0c28 c0x0000 (---------------) + I ishigaki 0x0021ac48, // n0x0c29 c0x0000 (---------------) + I ishikawa 0x00227d06, // n0x0c2a c0x0000 (---------------) + I itoman 0x00305785, // n0x0c2b c0x0000 (---------------) + I izena 0x00314e86, // n0x0c2c c0x0000 (---------------) + I kadena 0x0020e4c3, // n0x0c2d c0x0000 (---------------) + I kin 0x002879c9, // n0x0c2e c0x0000 (---------------) + I kitadaito 0x0028628e, // n0x0c2f c0x0000 (---------------) + I kitanakagusuku 0x002a5f88, // n0x0c30 c0x0000 (---------------) + I kumejima 0x0031aa08, // n0x0c31 c0x0000 (---------------) + I kunigami 0x00227b0b, // n0x0c32 c0x0000 (---------------) + I minamidaito 0x00281886, // n0x0c33 c0x0000 (---------------) + I motobu 0x00295b04, // n0x0c34 c0x0000 (---------------) + I nago 0x00273844, // n0x0c35 c0x0000 (---------------) + I naha 0x0028638a, // n0x0c36 c0x0000 (---------------) + I nakagusuku 0x00212ac7, // n0x0c37 c0x0000 (---------------) + I nakijin 0x00277485, // n0x0c38 c0x0000 (---------------) + I nanjo 0x002177c9, // n0x0c39 c0x0000 (---------------) + I nishihara 0x002a9ec5, // n0x0c3a c0x0000 (---------------) + I ogimi 0x00226d47, // n0x0c3b c0x0000 (---------------) + I okinawa 0x00259244, // n0x0c3c c0x0000 (---------------) + I onna 0x002a8e47, // n0x0c3d c0x0000 (---------------) + I shimoji 0x002f9488, // n0x0c3e c0x0000 (---------------) + I taketomi 0x002d8f46, // n0x0c3f c0x0000 (---------------) + I tarama 0x002b7389, // n0x0c40 c0x0000 (---------------) + I tokashiki 0x002a794a, // n0x0c41 c0x0000 (---------------) + I tomigusuku 0x00212a46, // n0x0c42 c0x0000 (---------------) + I tonaki 0x00285c46, // n0x0c43 c0x0000 (---------------) + I urasoe 0x0029bc85, // n0x0c44 c0x0000 (---------------) + I uruma 0x003386c5, // n0x0c45 c0x0000 (---------------) + I yaese 0x00323987, // n0x0c46 c0x0000 (---------------) + I yomitan 0x0034e1c8, // n0x0c47 c0x0000 (---------------) + I yonabaru 0x00201188, // n0x0c48 c0x0000 (---------------) + I yonaguni 0x0025cdc6, // n0x0c49 c0x0000 (---------------) + I zamami 0x002e5745, // n0x0c4a c0x0000 (---------------) + I abeno 0x00295c0e, // n0x0c4b c0x0000 (---------------) + I chihayaakasaka 0x00227944, // n0x0c4c c0x0000 (---------------) + I chuo 0x00227c85, // n0x0c4d c0x0000 (---------------) + I daito 0x0026af49, // n0x0c4e c0x0000 (---------------) + I fujiidera 0x00331208, // n0x0c4f c0x0000 (---------------) + I habikino 0x0027ad86, // n0x0c50 c0x0000 (---------------) + I hannan 0x00289fcc, // n0x0c51 c0x0000 (---------------) + I higashiosaka 0x0028b8d0, // n0x0c52 c0x0000 (---------------) + I higashisumiyoshi 0x0028d40f, // n0x0c53 c0x0000 (---------------) + I higashiyodogawa 0x0028e508, // n0x0c54 c0x0000 (---------------) + I hirakata 0x0032af87, // n0x0c55 c0x0000 (---------------) + I ibaraki 0x00203ac5, // n0x0c56 c0x0000 (---------------) + I ikeda 0x00278ec5, // n0x0c57 c0x0000 (---------------) + I izumi 0x002e0ec9, // n0x0c58 c0x0000 (---------------) + I izumiotsu 0x00278ec9, // n0x0c59 c0x0000 (---------------) + I izumisano 0x00231d46, // n0x0c5a c0x0000 (---------------) + I kadoma 0x002ece07, // n0x0c5b c0x0000 (---------------) + I kaizuka 0x0021aec5, // n0x0c5c c0x0000 (---------------) + I kanan 0x0020f389, // n0x0c5d c0x0000 (---------------) + I kashiwara 0x00335846, // n0x0c5e c0x0000 (---------------) + I katano 0x002d52cd, // n0x0c5f c0x0000 (---------------) + I kawachinagano 0x0026fe09, // n0x0c60 c0x0000 (---------------) + I kishiwada 0x00207ec4, // n0x0c61 c0x0000 (---------------) + I kita 0x002a5d08, // n0x0c62 c0x0000 (---------------) + I kumatori 0x002d5089, // n0x0c63 c0x0000 (---------------) + I matsubara 0x00319686, // n0x0c64 c0x0000 (---------------) + I minato 0x0026b6c5, // n0x0c65 c0x0000 (---------------) + I minoh 0x002c9886, // n0x0c66 c0x0000 (---------------) + I misaki 0x002966c9, // n0x0c67 c0x0000 (---------------) + I moriguchi 0x002bda08, // n0x0c68 c0x0000 (---------------) + I neyagawa 0x00212685, // n0x0c69 c0x0000 (---------------) + I nishi 0x0025af44, // n0x0c6a c0x0000 (---------------) + I nose 0x0028a18b, // n0x0c6b c0x0000 (---------------) + I osakasayama 0x00319545, // n0x0c6c c0x0000 (---------------) + I sakai 0x002301c6, // n0x0c6d c0x0000 (---------------) + I sayama 0x0025c5c6, // n0x0c6e c0x0000 (---------------) + I sennan 0x002d3586, // n0x0c6f c0x0000 (---------------) + I settsu 0x0023bdcb, // n0x0c70 c0x0000 (---------------) + I shijonawate 0x00281749, // n0x0c71 c0x0000 (---------------) + I shimamoto 0x0034f7c5, // n0x0c72 c0x0000 (---------------) + I suita 0x00252807, // n0x0c73 c0x0000 (---------------) + I tadaoka 0x00260246, // n0x0c74 c0x0000 (---------------) + I taishi 0x0024c7c6, // n0x0c75 c0x0000 (---------------) + I tajiri 0x00272508, // n0x0c76 c0x0000 (---------------) + I takaishi 0x00325109, // n0x0c77 c0x0000 (---------------) + I takatsuki 0x002bd2cc, // n0x0c78 c0x0000 (---------------) + I tondabayashi 0x00246088, // n0x0c79 c0x0000 (---------------) + I toyonaka 0x00250246, // n0x0c7a c0x0000 (---------------) + I toyono 0x002f8383, // n0x0c7b c0x0000 (---------------) + I yao 0x00306d46, // n0x0c7c c0x0000 (---------------) + I ariake 0x0029c345, // n0x0c7d c0x0000 (---------------) + I arita 0x0026e8c8, // n0x0c7e c0x0000 (---------------) + I fukudomi 0x002049c6, // n0x0c7f c0x0000 (---------------) + I genkai 0x00291048, // n0x0c80 c0x0000 (---------------) + I hamatama 0x00233f85, // n0x0c81 c0x0000 (---------------) + I hizen 0x003062c5, // n0x0c82 c0x0000 (---------------) + I imari 0x00200808, // n0x0c83 c0x0000 (---------------) + I kamimine 0x002d1907, // n0x0c84 c0x0000 (---------------) + I kanzaki 0x00322007, // n0x0c85 c0x0000 (---------------) + I karatsu 0x002aa107, // n0x0c86 c0x0000 (---------------) + I kashima 0x00270cc8, // n0x0c87 c0x0000 (---------------) + I kitagata 0x002d0c88, // n0x0c88 c0x0000 (---------------) + I kitahata 0x0027c786, // n0x0c89 c0x0000 (---------------) + I kiyama 0x002a0707, // n0x0c8a c0x0000 (---------------) + I kouhoku 0x002ac0c7, // n0x0c8b c0x0000 (---------------) + I kyuragi 0x0029c20a, // n0x0c8c c0x0000 (---------------) + I nishiarita 0x00239843, // n0x0c8d c0x0000 (---------------) + I ogi 0x002a3706, // n0x0c8e c0x0000 (---------------) + I omachi 0x00219f85, // n0x0c8f c0x0000 (---------------) + I ouchi 0x00272804, // n0x0c90 c0x0000 (---------------) + I saga 0x0026ca09, // n0x0c91 c0x0000 (---------------) + I shiroishi 0x00342884, // n0x0c92 c0x0000 (---------------) + I taku 0x0029da04, // n0x0c93 c0x0000 (---------------) + I tara 0x00283944, // n0x0c94 c0x0000 (---------------) + I tosu 0x0028d98b, // n0x0c95 c0x0000 (---------------) + I yoshinogari 0x002d5207, // n0x0c96 c0x0000 (---------------) + I arakawa 0x00295e45, // n0x0c97 c0x0000 (---------------) + I asaka 0x00282c48, // n0x0c98 c0x0000 (---------------) + I chichibu 0x0026b5c6, // n0x0c99 c0x0000 (---------------) + I fujimi 0x0026b5c8, // n0x0c9a c0x0000 (---------------) + I fujimino 0x0026d7c6, // n0x0c9b c0x0000 (---------------) + I fukaya 0x0027b685, // n0x0c9c c0x0000 (---------------) + I hanno 0x0027c0c5, // n0x0c9d c0x0000 (---------------) + I hanyu 0x0027e906, // n0x0c9e c0x0000 (---------------) + I hasuda 0x0027f588, // n0x0c9f c0x0000 (---------------) + I hatogaya 0x0027fec8, // n0x0ca0 c0x0000 (---------------) + I hatoyama 0x0026cbc6, // n0x0ca1 c0x0000 (---------------) + I hidaka 0x00282a8f, // n0x0ca2 c0x0000 (---------------) + I higashichichibu 0x00287490, // n0x0ca3 c0x0000 (---------------) + I higashimatsuyama 0x00218305, // n0x0ca4 c0x0000 (---------------) + I honjo 0x00204b03, // n0x0ca5 c0x0000 (---------------) + I ina 0x002ec905, // n0x0ca6 c0x0000 (---------------) + I iruma 0x0029dd88, // n0x0ca7 c0x0000 (---------------) + I iwatsuki 0x00278dc9, // n0x0ca8 c0x0000 (---------------) + I kamiizumi 0x003304c8, // n0x0ca9 c0x0000 (---------------) + I kamikawa 0x00242dc8, // n0x0caa c0x0000 (---------------) + I kamisato 0x0021c208, // n0x0cab c0x0000 (---------------) + I kasukabe 0x00295f07, // n0x0cac c0x0000 (---------------) + I kawagoe 0x0026b289, // n0x0cad c0x0000 (---------------) + I kawaguchi 0x00291248, // n0x0cae c0x0000 (---------------) + I kawajima 0x0023acc4, // n0x0caf c0x0000 (---------------) + I kazo 0x002837c8, // n0x0cb0 c0x0000 (---------------) + I kitamoto 0x00255009, // n0x0cb1 c0x0000 (---------------) + I koshigaya 0x002a0f47, // n0x0cb2 c0x0000 (---------------) + I kounosu 0x002a7b44, // n0x0cb3 c0x0000 (---------------) + I kuki 0x0026a688, // n0x0cb4 c0x0000 (---------------) + I kumagaya 0x0027f94a, // n0x0cb5 c0x0000 (---------------) + I matsubushi 0x002b2446, // n0x0cb6 c0x0000 (---------------) + I minano 0x00242e46, // n0x0cb7 c0x0000 (---------------) + I misato 0x0021ef49, // n0x0cb8 c0x0000 (---------------) + I miyashiro 0x0028bb07, // n0x0cb9 c0x0000 (---------------) + I miyoshi 0x002bdc08, // n0x0cba c0x0000 (---------------) + I moroyama 0x00297a08, // n0x0cbb c0x0000 (---------------) + I nagatoro 0x003156c8, // n0x0cbc c0x0000 (---------------) + I namegawa 0x002d8d45, // n0x0cbd c0x0000 (---------------) + I niiza 0x00258285, // n0x0cbe c0x0000 (---------------) + I ogano 0x00232ac5, // n0x0cbf c0x0000 (---------------) + I ogawa 0x00210b45, // n0x0cc0 c0x0000 (---------------) + I ogose 0x002fb647, // n0x0cc1 c0x0000 (---------------) + I okegawa 0x0020fc45, // n0x0cc2 c0x0000 (---------------) + I omiya 0x002ad1c5, // n0x0cc3 c0x0000 (---------------) + I otaki 0x002a8086, // n0x0cc4 c0x0000 (---------------) + I ranzan 0x00330407, // n0x0cc5 c0x0000 (---------------) + I ryokami 0x002e7f47, // n0x0cc6 c0x0000 (---------------) + I saitama 0x0027fc46, // n0x0cc7 c0x0000 (---------------) + I sakado 0x002c1a45, // n0x0cc8 c0x0000 (---------------) + I satte 0x002301c6, // n0x0cc9 c0x0000 (---------------) + I sayama 0x00291885, // n0x0cca c0x0000 (---------------) + I shiki 0x002d1fc8, // n0x0ccb c0x0000 (---------------) + I shiraoka 0x002d6004, // n0x0ccc c0x0000 (---------------) + I soka 0x002b7286, // n0x0ccd c0x0000 (---------------) + I sugito 0x00209d84, // n0x0cce c0x0000 (---------------) + I toda 0x003142c8, // n0x0ccf c0x0000 (---------------) + I tokigawa 0x002dbb4a, // n0x0cd0 c0x0000 (---------------) + I tokorozawa 0x0030608c, // n0x0cd1 c0x0000 (---------------) + I tsurugashima 0x0021c985, // n0x0cd2 c0x0000 (---------------) + I urawa 0x00232b86, // n0x0cd3 c0x0000 (---------------) + I warabi 0x002bd486, // n0x0cd4 c0x0000 (---------------) + I yashio 0x002aadc6, // n0x0cd5 c0x0000 (---------------) + I yokoze 0x002502c4, // n0x0cd6 c0x0000 (---------------) + I yono 0x00209e85, // n0x0cd7 c0x0000 (---------------) + I yorii 0x0026d607, // n0x0cd8 c0x0000 (---------------) + I yoshida 0x0028bb89, // n0x0cd9 c0x0000 (---------------) + I yoshikawa 0x00292387, // n0x0cda c0x0000 (---------------) + I yoshimi 0x0062da04, // n0x0cdb c0x0001 (---------------) ! I city 0x0062da04, // n0x0cdc c0x0001 (---------------) ! I city 0x002b0f85, // n0x0cdd c0x0000 (---------------) + I aisho 0x00205344, // n0x0cde c0x0000 (---------------) + I gamo 0x0028914a, // n0x0cdf c0x0000 (---------------) + I higashiomi 0x0026b446, // n0x0ce0 c0x0000 (---------------) + I hikone 0x00242d44, // n0x0ce1 c0x0000 (---------------) + I koka 0x002c92c5, // n0x0ce2 c0x0000 (---------------) + I konan 0x0029e885, // n0x0ce3 c0x0000 (---------------) + I kosei 0x002a0004, // n0x0ce4 c0x0000 (---------------) + I koto 0x00273bc7, // n0x0ce5 c0x0000 (---------------) + I kusatsu 0x0023fb07, // n0x0ce6 c0x0000 (---------------) + I maibara 0x002bc788, // n0x0ce7 c0x0000 (---------------) + I moriyama 0x00314a88, // n0x0ce8 c0x0000 (---------------) + I nagahama 0x00213389, // n0x0ce9 c0x0000 (---------------) + I nishiazai 0x002b9788, // n0x0cea c0x0000 (---------------) + I notogawa 0x0028930b, // n0x0ceb c0x0000 (---------------) + I omihachiman 0x00201ec4, // n0x0cec c0x0000 (---------------) + I otsu 0x0024c8c5, // n0x0ced c0x0000 (---------------) + I ritto 0x00329c85, // n0x0cee c0x0000 (---------------) + I ryuoh 0x002aa089, // n0x0cef c0x0000 (---------------) + I takashima 0x00325109, // n0x0cf0 c0x0000 (---------------) + I takatsuki 0x00222608, // n0x0cf1 c0x0000 (---------------) + I torahime 0x00253588, // n0x0cf2 c0x0000 (---------------) + I toyosato 0x0028cac4, // n0x0cf3 c0x0000 (---------------) + I yasu 0x00206e85, // n0x0cf4 c0x0000 (---------------) + I akagi 0x00204203, // n0x0cf5 c0x0000 (---------------) + I ama 0x002d0b45, // n0x0cf6 c0x0000 (---------------) + I gotsu 0x00292546, // n0x0cf7 c0x0000 (---------------) + I hamada 0x0028344c, // n0x0cf8 c0x0000 (---------------) + I higashiizumo 0x0021acc6, // n0x0cf9 c0x0000 (---------------) + I hikawa 0x002918c6, // n0x0cfa c0x0000 (---------------) + I hikimi 0x0027b005, // n0x0cfb c0x0000 (---------------) + I izumo 0x00271108, // n0x0cfc c0x0000 (---------------) + I kakinoki 0x002a43c6, // n0x0cfd c0x0000 (---------------) + I masuda 0x00204246, // n0x0cfe c0x0000 (---------------) + I matsue 0x00242e46, // n0x0cff c0x0000 (---------------) + I misato 0x00224f8c, // n0x0d00 c0x0000 (---------------) + I nishinoshima 0x00264104, // n0x0d01 c0x0000 (---------------) + I ohda 0x0033498a, // n0x0d02 c0x0000 (---------------) + I okinoshima 0x0027af48, // n0x0d03 c0x0000 (---------------) + I okuizumo 0x00283287, // n0x0d04 c0x0000 (---------------) + I shimane 0x00331686, // n0x0d05 c0x0000 (---------------) + I tamayu 0x0025c107, // n0x0d06 c0x0000 (---------------) + I tsuwano 0x002cd2c5, // n0x0d07 c0x0000 (---------------) + I unnan 0x00202d06, // n0x0d08 c0x0000 (---------------) + I yakumo 0x0031c4c6, // n0x0d09 c0x0000 (---------------) + I yasugi 0x00321ec7, // n0x0d0a c0x0000 (---------------) + I yatsuka 0x0029cf84, // n0x0d0b c0x0000 (---------------) + I arai 0x00201b45, // n0x0d0c c0x0000 (---------------) + I atami 0x0026af44, // n0x0d0d c0x0000 (---------------) + I fuji 0x00304e47, // n0x0d0e c0x0000 (---------------) + I fujieda 0x0026b188, // n0x0d0f c0x0000 (---------------) + I fujikawa 0x0026b90a, // n0x0d10 c0x0000 (---------------) + I fujinomiya 0x00270047, // n0x0d11 c0x0000 (---------------) + I fukuroi 0x00298887, // n0x0d12 c0x0000 (---------------) + I gotemba 0x0032af07, // n0x0d13 c0x0000 (---------------) + I haibara 0x00302389, // n0x0d14 c0x0000 (---------------) + I hamamatsu 0x0028344a, // n0x0d15 c0x0000 (---------------) + I higashiizu 0x00227d03, // n0x0d16 c0x0000 (---------------) + I ito 0x0029d945, // n0x0d17 c0x0000 (---------------) + I iwata 0x002104c3, // n0x0d18 c0x0000 (---------------) + I izu 0x002104c9, // n0x0d19 c0x0000 (---------------) + I izunokuni 0x0026db88, // n0x0d1a c0x0000 (---------------) + I kakegawa 0x002d6087, // n0x0d1b c0x0000 (---------------) + I kannami 0x003305c9, // n0x0d1c c0x0000 (---------------) + I kawanehon 0x0021ad46, // n0x0d1d c0x0000 (---------------) + I kawazu 0x002604c8, // n0x0d1e c0x0000 (---------------) + I kikugawa 0x00326ac5, // n0x0d1f c0x0000 (---------------) + I kosai 0x002776ca, // n0x0d20 c0x0000 (---------------) + I makinohara 0x002b6609, // n0x0d21 c0x0000 (---------------) + I matsuzaki 0x00248409, // n0x0d22 c0x0000 (---------------) + I minamiizu 0x002b64c7, // n0x0d23 c0x0000 (---------------) + I mishima 0x0032b489, // n0x0d24 c0x0000 (---------------) + I morimachi 0x00218948, // n0x0d25 c0x0000 (---------------) + I nishiizu 0x00252406, // n0x0d26 c0x0000 (---------------) + I numazu 0x00232d48, // n0x0d27 c0x0000 (---------------) + I omaezaki 0x002d57c7, // n0x0d28 c0x0000 (---------------) + I shimada 0x00228f47, // n0x0d29 c0x0000 (---------------) + I shimizu 0x002d92c7, // n0x0d2a c0x0000 (---------------) + I shimoda 0x002d9508, // n0x0d2b c0x0000 (---------------) + I shizuoka 0x002e4386, // n0x0d2c c0x0000 (---------------) + I susono 0x0027f705, // n0x0d2d c0x0000 (---------------) + I yaizu 0x0026d607, // n0x0d2e c0x0000 (---------------) + I yoshida 0x00285748, // n0x0d2f c0x0000 (---------------) + I ashikaga 0x003234c4, // n0x0d30 c0x0000 (---------------) + I bato 0x0031be04, // n0x0d31 c0x0000 (---------------) + I haga 0x002dd447, // n0x0d32 c0x0000 (---------------) + I ichikai 0x00317307, // n0x0d33 c0x0000 (---------------) + I iwafune 0x002c334a, // n0x0d34 c0x0000 (---------------) + I kaminokawa 0x00252386, // n0x0d35 c0x0000 (---------------) + I kanuma 0x002efc0a, // n0x0d36 c0x0000 (---------------) + I karasuyama 0x002ab4c7, // n0x0d37 c0x0000 (---------------) + I kuroiso 0x002efe07, // n0x0d38 c0x0000 (---------------) + I mashiko 0x0022f3c4, // n0x0d39 c0x0000 (---------------) + I mibu 0x002be704, // n0x0d3a c0x0000 (---------------) + I moka 0x00284706, // n0x0d3b c0x0000 (---------------) + I motegi 0x002afd44, // n0x0d3c c0x0000 (---------------) + I nasu 0x002afd4c, // n0x0d3d c0x0000 (---------------) + I nasushiobara 0x0020d685, // n0x0d3e c0x0000 (---------------) + I nikko 0x00219a09, // n0x0d3f c0x0000 (---------------) + I nishikata 0x002b4104, // n0x0d40 c0x0000 (---------------) + I nogi 0x002a00c5, // n0x0d41 c0x0000 (---------------) + I ohira 0x00213c88, // n0x0d42 c0x0000 (---------------) + I ohtawara 0x0027ff85, // n0x0d43 c0x0000 (---------------) + I oyama 0x00212346, // n0x0d44 c0x0000 (---------------) + I sakura 0x00279004, // n0x0d45 c0x0000 (---------------) + I sano 0x002cceca, // n0x0d46 c0x0000 (---------------) + I shimotsuke 0x00307586, // n0x0d47 c0x0000 (---------------) + I shioya 0x0029d6ca, // n0x0d48 c0x0000 (---------------) + I takanezawa 0x00323547, // n0x0d49 c0x0000 (---------------) + I tochigi 0x002a3b05, // n0x0d4a c0x0000 (---------------) + I tsuga 0x00217bc5, // n0x0d4b c0x0000 (---------------) + I ujiie 0x0024ef8a, // n0x0d4c c0x0000 (---------------) + I utsunomiya 0x00261ac5, // n0x0d4d c0x0000 (---------------) + I yaita 0x0028dfc6, // n0x0d4e c0x0000 (---------------) + I aizumi 0x0021af04, // n0x0d4f c0x0000 (---------------) + I anan 0x002a6586, // n0x0d50 c0x0000 (---------------) + I ichiba 0x00323a45, // n0x0d51 c0x0000 (---------------) + I itano 0x00204a86, // n0x0d52 c0x0000 (---------------) + I kainan 0x002a414c, // n0x0d53 c0x0000 (---------------) + I komatsushima 0x0024524a, // n0x0d54 c0x0000 (---------------) + I matsushige 0x00251a04, // n0x0d55 c0x0000 (---------------) + I mima 0x00227b06, // n0x0d56 c0x0000 (---------------) + I minami 0x0028bb07, // n0x0d57 c0x0000 (---------------) + I miyoshi 0x002bf604, // n0x0d58 c0x0000 (---------------) + I mugi 0x002c6f08, // n0x0d59 c0x0000 (---------------) + I nakagawa 0x003141c6, // n0x0d5a c0x0000 (---------------) + I naruto 0x00295a89, // n0x0d5b c0x0000 (---------------) + I sanagochi 0x002d3c09, // n0x0d5c c0x0000 (---------------) + I shishikui 0x002eb5c9, // n0x0d5d c0x0000 (---------------) + I tokushima 0x00229346, // n0x0d5e c0x0000 (---------------) + I wajiki 0x002d58c6, // n0x0d5f c0x0000 (---------------) + I adachi 0x00232e87, // n0x0d60 c0x0000 (---------------) + I akiruno 0x00313248, // n0x0d61 c0x0000 (---------------) + I akishima 0x002d56c9, // n0x0d62 c0x0000 (---------------) + I aogashima 0x002d5207, // n0x0d63 c0x0000 (---------------) + I arakawa 0x00281986, // n0x0d64 c0x0000 (---------------) + I bunkyo 0x00359ac7, // n0x0d65 c0x0000 (---------------) + I chiyoda 0x002fbc45, // n0x0d66 c0x0000 (---------------) + I chofu 0x00227944, // n0x0d67 c0x0000 (---------------) + I chuo 0x00232a47, // n0x0d68 c0x0000 (---------------) + I edogawa 0x00203ec5, // n0x0d69 c0x0000 (---------------) + I fuchu 0x00275f85, // n0x0d6a c0x0000 (---------------) + I fussa 0x00251487, // n0x0d6b c0x0000 (---------------) + I hachijo 0x002618c8, // n0x0d6c c0x0000 (---------------) + I hachioji 0x0027a4c6, // n0x0d6d c0x0000 (---------------) + I hamura 0x0028678d, // n0x0d6e c0x0000 (---------------) + I higashikurume 0x00287d4f, // n0x0d6f c0x0000 (---------------) + I higashimurayama 0x0028cc4d, // n0x0d70 c0x0000 (---------------) + I higashiyamato 0x00201e04, // n0x0d71 c0x0000 (---------------) + I hino 0x0023b746, // n0x0d72 c0x0000 (---------------) + I hinode 0x002c2848, // n0x0d73 c0x0000 (---------------) + I hinohara 0x00296105, // n0x0d74 c0x0000 (---------------) + I inagi 0x0029c3c8, // n0x0d75 c0x0000 (---------------) + I itabashi 0x0025868a, // n0x0d76 c0x0000 (---------------) + I katsushika 0x00207ec4, // n0x0d77 c0x0000 (---------------) + I kita 0x002d1a46, // n0x0d78 c0x0000 (---------------) + I kiyose 0x0027bf07, // n0x0d79 c0x0000 (---------------) + I kodaira 0x002f9c87, // n0x0d7a c0x0000 (---------------) + I koganei 0x00284949, // n0x0d7b c0x0000 (---------------) + I kokubunji 0x00232d05, // n0x0d7c c0x0000 (---------------) + I komae 0x002a0004, // n0x0d7d c0x0000 (---------------) + I koto 0x002a1b8a, // n0x0d7e c0x0000 (---------------) + I kouzushima 0x002a7289, // n0x0d7f c0x0000 (---------------) + I kunitachi 0x0032b587, // n0x0d80 c0x0000 (---------------) + I machida 0x0022d006, // n0x0d81 c0x0000 (---------------) + I meguro 0x00319686, // n0x0d82 c0x0000 (---------------) + I minato 0x00206dc6, // n0x0d83 c0x0000 (---------------) + I mitaka 0x00243506, // n0x0d84 c0x0000 (---------------) + I mizuho 0x002c234f, // n0x0d85 c0x0000 (---------------) + I musashimurayama 0x002c2709, // n0x0d86 c0x0000 (---------------) + I musashino 0x0030ab86, // n0x0d87 c0x0000 (---------------) + I nakano 0x0032e206, // n0x0d88 c0x0000 (---------------) + I nerima 0x00313c49, // n0x0d89 c0x0000 (---------------) + I ogasawara 0x002a0807, // n0x0d8a c0x0000 (---------------) + I okutama 0x00216043, // n0x0d8b c0x0000 (---------------) + I ome 0x00225106, // n0x0d8c c0x0000 (---------------) + I oshima 0x00213043, // n0x0d8d c0x0000 (---------------) + I ota 0x0020dc88, // n0x0d8e c0x0000 (---------------) + I setagaya 0x00238007, // n0x0d8f c0x0000 (---------------) + I shibuya 0x0028e709, // n0x0d90 c0x0000 (---------------) + I shinagawa 0x002dad08, // n0x0d91 c0x0000 (---------------) + I shinjuku 0x00322148, // n0x0d92 c0x0000 (---------------) + I suginami 0x002839c6, // n0x0d93 c0x0000 (---------------) + I sumida 0x0034f889, // n0x0d94 c0x0000 (---------------) + I tachikawa 0x00294705, // n0x0d95 c0x0000 (---------------) + I taito 0x00291144, // n0x0d96 c0x0000 (---------------) + I tama 0x00306b47, // n0x0d97 c0x0000 (---------------) + I toshima 0x00329ac5, // n0x0d98 c0x0000 (---------------) + I chizu 0x00201e04, // n0x0d99 c0x0000 (---------------) + I hino 0x00275bc8, // n0x0d9a c0x0000 (---------------) + I kawahara 0x00206384, // n0x0d9b c0x0000 (---------------) + I koge 0x002a0547, // n0x0d9c c0x0000 (---------------) + I kotoura 0x002d6a06, // n0x0d9d c0x0000 (---------------) + I misasa 0x0025c685, // n0x0d9e c0x0000 (---------------) + I nanbu 0x002cf088, // n0x0d9f c0x0000 (---------------) + I nichinan 0x0031954b, // n0x0da0 c0x0000 (---------------) + I sakaiminato 0x00319787, // n0x0da1 c0x0000 (---------------) + I tottori 0x0024f206, // n0x0da2 c0x0000 (---------------) + I wakasa 0x002b8604, // n0x0da3 c0x0000 (---------------) + I yazu 0x0035cdc6, // n0x0da4 c0x0000 (---------------) + I yonago 0x002b6285, // n0x0da5 c0x0000 (---------------) + I asahi 0x00203ec5, // n0x0da6 c0x0000 (---------------) + I fuchu 0x0026f909, // n0x0da7 c0x0000 (---------------) + I fukumitsu 0x002737c9, // n0x0da8 c0x0000 (---------------) + I funahashi 0x00228f84, // n0x0da9 c0x0000 (---------------) + I himi 0x00228fc5, // n0x0daa c0x0000 (---------------) + I imizu 0x00227b45, // n0x0dab c0x0000 (---------------) + I inami 0x00277546, // n0x0dac c0x0000 (---------------) + I johana 0x002dd348, // n0x0dad c0x0000 (---------------) + I kamiichi 0x002a9646, // n0x0dae c0x0000 (---------------) + I kurobe 0x0027b1cb, // n0x0daf c0x0000 (---------------) + I nakaniikawa 0x002592ca, // n0x0db0 c0x0000 (---------------) + I namerikawa 0x0029f505, // n0x0db1 c0x0000 (---------------) + I nanto 0x0027c146, // n0x0db2 c0x0000 (---------------) + I nyuzen 0x002e56c5, // n0x0db3 c0x0000 (---------------) + I oyabe 0x003541c5, // n0x0db4 c0x0000 (---------------) + I taira 0x002802c7, // n0x0db5 c0x0000 (---------------) + I takaoka 0x0021e348, // n0x0db6 c0x0000 (---------------) + I tateyama 0x0027f604, // n0x0db7 c0x0000 (---------------) + I toga 0x00228186, // n0x0db8 c0x0000 (---------------) + I tonami 0x0027ff46, // n0x0db9 c0x0000 (---------------) + I toyama 0x00218b07, // n0x0dba c0x0000 (---------------) + I unazuki 0x002279c4, // n0x0dbb c0x0000 (---------------) + I uozu 0x0026e746, // n0x0dbc c0x0000 (---------------) + I yamada 0x0023f305, // n0x0dbd c0x0000 (---------------) + I arida 0x0023f309, // n0x0dbe c0x0000 (---------------) + I aridagawa 0x002d5ac4, // n0x0dbf c0x0000 (---------------) + I gobo 0x002db989, // n0x0dc0 c0x0000 (---------------) + I hashimoto 0x0026cbc6, // n0x0dc1 c0x0000 (---------------) + I hidaka 0x002b0c08, // n0x0dc2 c0x0000 (---------------) + I hirogawa 0x00227b45, // n0x0dc3 c0x0000 (---------------) + I inami 0x00233845, // n0x0dc4 c0x0000 (---------------) + I iwade 0x00204a86, // n0x0dc5 c0x0000 (---------------) + I kainan 0x002bd1c9, // n0x0dc6 c0x0000 (---------------) + I kamitonda 0x0021a6c9, // n0x0dc7 c0x0000 (---------------) + I katsuragi 0x00291946, // n0x0dc8 c0x0000 (---------------) + I kimino 0x00331308, // n0x0dc9 c0x0000 (---------------) + I kinokawa 0x00280508, // n0x0dca c0x0000 (---------------) + I kitayama 0x002e5684, // n0x0dcb c0x0000 (---------------) + I koya 0x002a26c4, // n0x0dcc c0x0000 (---------------) + I koza 0x002a26c8, // n0x0dcd c0x0000 (---------------) + I kozagawa 0x002fa848, // n0x0dce c0x0000 (---------------) + I kudoyama 0x002948c9, // n0x0dcf c0x0000 (---------------) + I kushimoto 0x002924c6, // n0x0dd0 c0x0000 (---------------) + I mihama 0x00242e46, // n0x0dd1 c0x0000 (---------------) + I misato 0x002f7a0d, // n0x0dd2 c0x0000 (---------------) + I nachikatsuura 0x0022b406, // n0x0dd3 c0x0000 (---------------) + I shingu 0x00307ac9, // n0x0dd4 c0x0000 (---------------) + I shirahama 0x0032ffc5, // n0x0dd5 c0x0000 (---------------) + I taiji 0x00216406, // n0x0dd6 c0x0000 (---------------) + I tanabe 0x0034fa48, // n0x0dd7 c0x0000 (---------------) + I wakayama 0x002f5c45, // n0x0dd8 c0x0000 (---------------) + I yuasa 0x002ac104, // n0x0dd9 c0x0000 (---------------) + I yura 0x002b6285, // n0x0dda c0x0000 (---------------) + I asahi 0x00272208, // n0x0ddb c0x0000 (---------------) + I funagata 0x00288f09, // n0x0ddc c0x0000 (---------------) + I higashine 0x0026b004, // n0x0ddd c0x0000 (---------------) + I iide 0x00256786, // n0x0dde c0x0000 (---------------) + I kahoku 0x0031af8a, // n0x0ddf c0x0000 (---------------) + I kaminoyama 0x00303748, // n0x0de0 c0x0000 (---------------) + I kaneyama 0x002c34c9, // n0x0de1 c0x0000 (---------------) + I kawanishi 0x00334b8a, // n0x0de2 c0x0000 (---------------) + I mamurogawa 0x00330546, // n0x0de3 c0x0000 (---------------) + I mikawa 0x00287f08, // n0x0de4 c0x0000 (---------------) + I murayama 0x00314f85, // n0x0de5 c0x0000 (---------------) + I nagai 0x00357fc8, // n0x0de6 c0x0000 (---------------) + I nakayama 0x002a63c5, // n0x0de7 c0x0000 (---------------) + I nanyo 0x0021ac09, // n0x0de8 c0x0000 (---------------) + I nishikawa 0x00323b49, // n0x0de9 c0x0000 (---------------) + I obanazawa 0x0020d782, // n0x0dea c0x0000 (---------------) + I oe 0x0025ec05, // n0x0deb c0x0000 (---------------) + I oguni 0x0026b786, // n0x0dec c0x0000 (---------------) + I ohkura 0x0026cb07, // n0x0ded c0x0000 (---------------) + I oishida 0x00272805, // n0x0dee c0x0000 (---------------) + I sagae 0x002f5d06, // n0x0def c0x0000 (---------------) + I sakata 0x00337ac8, // n0x0df0 c0x0000 (---------------) + I sakegawa 0x002d0786, // n0x0df1 c0x0000 (---------------) + I shinjo 0x002d2e09, // n0x0df2 c0x0000 (---------------) + I shirataka 0x0026c406, // n0x0df3 c0x0000 (---------------) + I shonai 0x00272388, // n0x0df4 c0x0000 (---------------) + I takahata 0x00299f45, // n0x0df5 c0x0000 (---------------) + I tendo 0x00268e06, // n0x0df6 c0x0000 (---------------) + I tozawa 0x0028f208, // n0x0df7 c0x0000 (---------------) + I tsuruoka 0x00271688, // n0x0df8 c0x0000 (---------------) + I yamagata 0x0020af08, // n0x0df9 c0x0000 (---------------) + I yamanobe 0x002c9088, // n0x0dfa c0x0000 (---------------) + I yonezawa 0x0022ac84, // n0x0dfb c0x0000 (---------------) + I yuza 0x0022adc3, // n0x0dfc c0x0000 (---------------) + I abu 0x002d3044, // n0x0dfd c0x0000 (---------------) + I hagi 0x002b8c06, // n0x0dfe c0x0000 (---------------) + I hikari 0x002fbc84, // n0x0dff c0x0000 (---------------) + I hofu 0x0031a947, // n0x0e00 c0x0000 (---------------) + I iwakuni 0x00204149, // n0x0e01 c0x0000 (---------------) + I kudamatsu 0x002b7b85, // n0x0e02 c0x0000 (---------------) + I mitou 0x00297a06, // n0x0e03 c0x0000 (---------------) + I nagato 0x00225106, // n0x0e04 c0x0000 (---------------) + I oshima 0x002c7bcb, // n0x0e05 c0x0000 (---------------) + I shimonoseki 0x0029f446, // n0x0e06 c0x0000 (---------------) + I shunan 0x002fab46, // n0x0e07 c0x0000 (---------------) + I tabuse 0x002f9708, // n0x0e08 c0x0000 (---------------) + I tokuyama 0x00254446, // n0x0e09 c0x0000 (---------------) + I toyota 0x00263543, // n0x0e0a c0x0000 (---------------) + I ube 0x0022aa43, // n0x0e0b c0x0000 (---------------) + I yuu 0x00227944, // n0x0e0c c0x0000 (---------------) + I chuo 0x00237f85, // n0x0e0d c0x0000 (---------------) + I doshi 0x00331047, // n0x0e0e c0x0000 (---------------) + I fuefuki 0x0026b188, // n0x0e0f c0x0000 (---------------) + I fujikawa 0x0026b18f, // n0x0e10 c0x0000 (---------------) + I fujikawaguchiko 0x0026d50b, // n0x0e11 c0x0000 (---------------) + I fujiyoshida 0x002dd148, // n0x0e12 c0x0000 (---------------) + I hayakawa 0x00256806, // n0x0e13 c0x0000 (---------------) + I hokuto 0x0029530e, // n0x0e14 c0x0000 (---------------) + I ichikawamisato 0x00204a83, // n0x0e15 c0x0000 (---------------) + I kai 0x00330fc4, // n0x0e16 c0x0000 (---------------) + I kofu 0x0029f3c5, // n0x0e17 c0x0000 (---------------) + I koshu 0x0029fe86, // n0x0e18 c0x0000 (---------------) + I kosuge 0x0027e3cb, // n0x0e19 c0x0000 (---------------) + I minami-alps 0x00282346, // n0x0e1a c0x0000 (---------------) + I minobu 0x00228949, // n0x0e1b c0x0000 (---------------) + I nakamichi 0x0025c685, // n0x0e1c c0x0000 (---------------) + I nanbu 0x00358408, // n0x0e1d c0x0000 (---------------) + I narusawa 0x00210c88, // n0x0e1e c0x0000 (---------------) + I nirasaki 0x0021a58c, // n0x0e1f c0x0000 (---------------) + I nishikatsura 0x0028d9c6, // n0x0e20 c0x0000 (---------------) + I oshino 0x002d0b86, // n0x0e21 c0x0000 (---------------) + I otsuki 0x002d9e85, // n0x0e22 c0x0000 (---------------) + I showa 0x00276348, // n0x0e23 c0x0000 (---------------) + I tabayama 0x0028f205, // n0x0e24 c0x0000 (---------------) + I tsuru 0x0020f648, // n0x0e25 c0x0000 (---------------) + I uenohara 0x0028d08a, // n0x0e26 c0x0000 (---------------) + I yamanakako 0x00230249, // n0x0e27 c0x0000 (---------------) + I yamanashi 0x0062da04, // n0x0e28 c0x0001 (---------------) ! I city 0x002370c3, // n0x0e29 c0x0000 (---------------) + I com 0x00215b83, // n0x0e2a c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e2b c0x0000 (---------------) + I gov 0x00210703, // n0x0e2c c0x0000 (---------------) + I mil 0x0024bdc3, // n0x0e2d c0x0000 (---------------) + I net 0x0024af03, // n0x0e2e c0x0000 (---------------) + I org 0x00305743, // n0x0e2f c0x0000 (---------------) + I biz 0x002370c3, // n0x0e30 c0x0000 (---------------) + I com 0x00215b83, // n0x0e31 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e32 c0x0000 (---------------) + I gov 0x00214b84, // n0x0e33 c0x0000 (---------------) + I info 0x0024bdc3, // n0x0e34 c0x0000 (---------------) + I net 0x0024af03, // n0x0e35 c0x0000 (---------------) + I org 0x00205f03, // n0x0e36 c0x0000 (---------------) + I ass 0x002c5c84, // n0x0e37 c0x0000 (---------------) + I asso 0x002370c3, // n0x0e38 c0x0000 (---------------) + I com 0x0023d9c4, // n0x0e39 c0x0000 (---------------) + I coop 0x00215b83, // n0x0e3a c0x0000 (---------------) + I edu 0x002d80c4, // n0x0e3b c0x0000 (---------------) + I gouv 0x00208ec3, // n0x0e3c c0x0000 (---------------) + I gov 0x0029e587, // n0x0e3d c0x0000 (---------------) + I medecin 0x00210703, // n0x0e3e c0x0000 (---------------) + I mil 0x0020fc03, // n0x0e3f c0x0000 (---------------) + I nom 0x00242648, // n0x0e40 c0x0000 (---------------) + I notaires 0x0024af03, // n0x0e41 c0x0000 (---------------) + I org 0x002c4b8b, // n0x0e42 c0x0000 (---------------) + I pharmaciens 0x002cdec3, // n0x0e43 c0x0000 (---------------) + I prd 0x00223146, // n0x0e44 c0x0000 (---------------) + I presse 0x00226782, // n0x0e45 c0x0000 (---------------) + I tm 0x002b32cb, // n0x0e46 c0x0000 (---------------) + I veterinaire 0x00215b83, // n0x0e47 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e48 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0e49 c0x0000 (---------------) + I net 0x0024af03, // n0x0e4a c0x0000 (---------------) + I org 0x002370c3, // n0x0e4b c0x0000 (---------------) + I com 0x00215b83, // n0x0e4c c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e4d c0x0000 (---------------) + I gov 0x0024af03, // n0x0e4e c0x0000 (---------------) + I org 0x00265a83, // n0x0e4f c0x0000 (---------------) + I rep 0x00205103, // n0x0e50 c0x0000 (---------------) + I tra 0x0020a3c2, // n0x0e51 c0x0000 (---------------) + I ac 0x0012d948, // n0x0e52 c0x0000 (---------------) + blogspot 0x002368c5, // n0x0e53 c0x0000 (---------------) + I busan 0x0032d348, // n0x0e54 c0x0000 (---------------) + I chungbuk 0x0033d408, // n0x0e55 c0x0000 (---------------) + I chungnam 0x00209382, // n0x0e56 c0x0000 (---------------) + I co 0x00204e85, // n0x0e57 c0x0000 (---------------) + I daegu 0x003025c7, // n0x0e58 c0x0000 (---------------) + I daejeon 0x00203482, // n0x0e59 c0x0000 (---------------) + I es 0x00228b87, // n0x0e5a c0x0000 (---------------) + I gangwon 0x00208ec2, // n0x0e5b c0x0000 (---------------) + I go 0x00253207, // n0x0e5c c0x0000 (---------------) + I gwangju 0x002faf09, // n0x0e5d c0x0000 (---------------) + I gyeongbuk 0x0034ed88, // n0x0e5e c0x0000 (---------------) + I gyeonggi 0x00315549, // n0x0e5f c0x0000 (---------------) + I gyeongnam 0x00212302, // n0x0e60 c0x0000 (---------------) + I hs 0x0025b7c7, // n0x0e61 c0x0000 (---------------) + I incheon 0x00327f44, // n0x0e62 c0x0000 (---------------) + I jeju 0x00302687, // n0x0e63 c0x0000 (---------------) + I jeonbuk 0x002591c7, // n0x0e64 c0x0000 (---------------) + I jeonnam 0x0024a402, // n0x0e65 c0x0000 (---------------) + I kg 0x00210703, // n0x0e66 c0x0000 (---------------) + I mil 0x0020bb42, // n0x0e67 c0x0000 (---------------) + I ms 0x00200982, // n0x0e68 c0x0000 (---------------) + I ne 0x00201002, // n0x0e69 c0x0000 (---------------) + I or 0x00206742, // n0x0e6a c0x0000 (---------------) + I pe 0x00205482, // n0x0e6b c0x0000 (---------------) + I re 0x00219202, // n0x0e6c c0x0000 (---------------) + I sc 0x002c9cc5, // n0x0e6d c0x0000 (---------------) + I seoul 0x0022aac5, // n0x0e6e c0x0000 (---------------) + I ulsan 0x002370c3, // n0x0e6f c0x0000 (---------------) + I com 0x00215b83, // n0x0e70 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e71 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0e72 c0x0000 (---------------) + I net 0x0024af03, // n0x0e73 c0x0000 (---------------) + I org 0x002370c3, // n0x0e74 c0x0000 (---------------) + I com 0x00215b83, // n0x0e75 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e76 c0x0000 (---------------) + I gov 0x00210703, // n0x0e77 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x0e78 c0x0000 (---------------) + I net 0x0024af03, // n0x0e79 c0x0000 (---------------) + I org 0x000002c1, // n0x0e7a c0x0000 (---------------) + c 0x002370c3, // n0x0e7b c0x0000 (---------------) + I com 0x00215b83, // n0x0e7c c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e7d c0x0000 (---------------) + I gov 0x00214b84, // n0x0e7e c0x0000 (---------------) + I info 0x00216fc3, // n0x0e7f c0x0000 (---------------) + I int 0x0024bdc3, // n0x0e80 c0x0000 (---------------) + I net 0x0024af03, // n0x0e81 c0x0000 (---------------) + I org 0x0023da83, // n0x0e82 c0x0000 (---------------) + I per 0x002370c3, // n0x0e83 c0x0000 (---------------) + I com 0x00215b83, // n0x0e84 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e85 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0e86 c0x0000 (---------------) + I net 0x0024af03, // n0x0e87 c0x0000 (---------------) + I org 0x00209382, // n0x0e88 c0x0000 (---------------) + I co 0x002370c3, // n0x0e89 c0x0000 (---------------) + I com 0x00215b83, // n0x0e8a c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e8b c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0e8c c0x0000 (---------------) + I net 0x0024af03, // n0x0e8d c0x0000 (---------------) + I org 0x002b1d04, // n0x0e8e c0x0000 (---------------) + I assn 0x002370c3, // n0x0e8f c0x0000 (---------------) + I com 0x00215b83, // n0x0e90 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e91 c0x0000 (---------------) + I gov 0x0023c403, // n0x0e92 c0x0000 (---------------) + I grp 0x0029aa05, // n0x0e93 c0x0000 (---------------) + I hotel 0x00216fc3, // n0x0e94 c0x0000 (---------------) + I int 0x0021fc43, // n0x0e95 c0x0000 (---------------) + I ltd 0x0024bdc3, // n0x0e96 c0x0000 (---------------) + I net 0x00248a83, // n0x0e97 c0x0000 (---------------) + I ngo 0x0024af03, // n0x0e98 c0x0000 (---------------) + I org 0x0025e643, // n0x0e99 c0x0000 (---------------) + I sch 0x002a22c3, // n0x0e9a c0x0000 (---------------) + I soc 0x00200b03, // n0x0e9b c0x0000 (---------------) + I web 0x002370c3, // n0x0e9c c0x0000 (---------------) + I com 0x00215b83, // n0x0e9d c0x0000 (---------------) + I edu 0x00208ec3, // n0x0e9e c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0e9f c0x0000 (---------------) + I net 0x0024af03, // n0x0ea0 c0x0000 (---------------) + I org 0x00209382, // n0x0ea1 c0x0000 (---------------) + I co 0x0024af03, // n0x0ea2 c0x0000 (---------------) + I org 0x00208ec3, // n0x0ea3 c0x0000 (---------------) + I gov 0x002a3f43, // n0x0ea4 c0x0000 (---------------) + I asn 0x002370c3, // n0x0ea5 c0x0000 (---------------) + I com 0x002386c4, // n0x0ea6 c0x0000 (---------------) + I conf 0x00215b83, // n0x0ea7 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0ea8 c0x0000 (---------------) + I gov 0x00203d42, // n0x0ea9 c0x0000 (---------------) + I id 0x00210703, // n0x0eaa c0x0000 (---------------) + I mil 0x0024bdc3, // n0x0eab c0x0000 (---------------) + I net 0x0024af03, // n0x0eac c0x0000 (---------------) + I org 0x002370c3, // n0x0ead c0x0000 (---------------) + I com 0x00215b83, // n0x0eae c0x0000 (---------------) + I edu 0x00208ec3, // n0x0eaf c0x0000 (---------------) + I gov 0x00203d42, // n0x0eb0 c0x0000 (---------------) + I id 0x00216083, // n0x0eb1 c0x0000 (---------------) + I med 0x0024bdc3, // n0x0eb2 c0x0000 (---------------) + I net 0x0024af03, // n0x0eb3 c0x0000 (---------------) + I org 0x002cab03, // n0x0eb4 c0x0000 (---------------) + I plc 0x0025e643, // n0x0eb5 c0x0000 (---------------) + I sch 0x0020a3c2, // n0x0eb6 c0x0000 (---------------) + I ac 0x00209382, // n0x0eb7 c0x0000 (---------------) + I co 0x00208ec3, // n0x0eb8 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0eb9 c0x0000 (---------------) + I net 0x0024af03, // n0x0eba c0x0000 (---------------) + I org 0x00223145, // n0x0ebb c0x0000 (---------------) + I press 0x002c5c84, // n0x0ebc c0x0000 (---------------) + I asso 0x00226782, // n0x0ebd c0x0000 (---------------) + I tm 0x0020a3c2, // n0x0ebe c0x0000 (---------------) + I ac 0x00209382, // n0x0ebf c0x0000 (---------------) + I co 0x00215b83, // n0x0ec0 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0ec1 c0x0000 (---------------) + I gov 0x00201a03, // n0x0ec2 c0x0000 (---------------) + I its 0x0024bdc3, // n0x0ec3 c0x0000 (---------------) + I net 0x0024af03, // n0x0ec4 c0x0000 (---------------) + I org 0x002ce944, // n0x0ec5 c0x0000 (---------------) + I priv 0x002370c3, // n0x0ec6 c0x0000 (---------------) + I com 0x00215b83, // n0x0ec7 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0ec8 c0x0000 (---------------) + I gov 0x00210703, // n0x0ec9 c0x0000 (---------------) + I mil 0x0020fc03, // n0x0eca c0x0000 (---------------) + I nom 0x0024af03, // n0x0ecb c0x0000 (---------------) + I org 0x002cdec3, // n0x0ecc c0x0000 (---------------) + I prd 0x00226782, // n0x0ecd c0x0000 (---------------) + I tm 0x002370c3, // n0x0ece c0x0000 (---------------) + I com 0x00215b83, // n0x0ecf c0x0000 (---------------) + I edu 0x00208ec3, // n0x0ed0 c0x0000 (---------------) + I gov 0x00214b83, // n0x0ed1 c0x0000 (---------------) + I inf 0x002592c4, // n0x0ed2 c0x0000 (---------------) + I name 0x0024bdc3, // n0x0ed3 c0x0000 (---------------) + I net 0x0024af03, // n0x0ed4 c0x0000 (---------------) + I org 0x002370c3, // n0x0ed5 c0x0000 (---------------) + I com 0x00215b83, // n0x0ed6 c0x0000 (---------------) + I edu 0x002d80c4, // n0x0ed7 c0x0000 (---------------) + I gouv 0x00208ec3, // n0x0ed8 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0ed9 c0x0000 (---------------) + I net 0x0024af03, // n0x0eda c0x0000 (---------------) + I org 0x00223146, // n0x0edb c0x0000 (---------------) + I presse 0x00215b83, // n0x0edc c0x0000 (---------------) + I edu 0x00208ec3, // n0x0edd c0x0000 (---------------) + I gov 0x00015443, // n0x0ede c0x0000 (---------------) + nyc 0x0024af03, // n0x0edf c0x0000 (---------------) + I org 0x002370c3, // n0x0ee0 c0x0000 (---------------) + I com 0x00215b83, // n0x0ee1 c0x0000 (---------------) + I edu 0x00208ec3, // n0x0ee2 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0ee3 c0x0000 (---------------) + I net 0x0024af03, // n0x0ee4 c0x0000 (---------------) + I org 0x0012d948, // n0x0ee5 c0x0000 (---------------) + blogspot 0x00208ec3, // n0x0ee6 c0x0000 (---------------) + I gov 0x002370c3, // n0x0ee7 c0x0000 (---------------) + I com 0x00215b83, // n0x0ee8 c0x0000 (---------------) + I edu 0x0024bdc3, // n0x0ee9 c0x0000 (---------------) + I net 0x0024af03, // n0x0eea c0x0000 (---------------) + I org 0x0020a3c2, // n0x0eeb c0x0000 (---------------) + I ac 0x00209382, // n0x0eec c0x0000 (---------------) + I co 0x002370c3, // n0x0eed c0x0000 (---------------) + I com 0x00208ec3, // n0x0eee c0x0000 (---------------) + I gov 0x0024bdc3, // n0x0eef c0x0000 (---------------) + I net 0x00201002, // n0x0ef0 c0x0000 (---------------) + I or 0x0024af03, // n0x0ef1 c0x0000 (---------------) + I org 0x002f1d87, // n0x0ef2 c0x0000 (---------------) + I academy 0x002b4e8b, // n0x0ef3 c0x0000 (---------------) + I agriculture 0x00242703, // n0x0ef4 c0x0000 (---------------) + I air 0x0026c508, // n0x0ef5 c0x0000 (---------------) + I airguard 0x00312fc7, // n0x0ef6 c0x0000 (---------------) + I alabama 0x002eab06, // n0x0ef7 c0x0000 (---------------) + I alaska 0x002b51c5, // n0x0ef8 c0x0000 (---------------) + I amber 0x00213a49, // n0x0ef9 c0x0000 (---------------) + I ambulance 0x002f5208, // n0x0efa c0x0000 (---------------) + I american 0x002f5209, // n0x0efb c0x0000 (---------------) + I americana 0x002f5210, // n0x0efc c0x0000 (---------------) + I americanantiques 0x0031198b, // n0x0efd c0x0000 (---------------) + I americanart 0x00213889, // n0x0efe c0x0000 (---------------) + I amsterdam 0x00200383, // n0x0eff c0x0000 (---------------) + I and 0x002fd209, // n0x0f00 c0x0000 (---------------) + I annefrank 0x00239d46, // n0x0f01 c0x0000 (---------------) + I anthro 0x00239d4c, // n0x0f02 c0x0000 (---------------) + I anthropology 0x00236988, // n0x0f03 c0x0000 (---------------) + I antiques 0x00275388, // n0x0f04 c0x0000 (---------------) + I aquarium 0x00223e09, // n0x0f05 c0x0000 (---------------) + I arboretum 0x002cc10e, // n0x0f06 c0x0000 (---------------) + I archaeological 0x0033730b, // n0x0f07 c0x0000 (---------------) + I archaeology 0x00248d8c, // n0x0f08 c0x0000 (---------------) + I architecture 0x00209243, // n0x0f09 c0x0000 (---------------) + I art 0x00311b8c, // n0x0f0a c0x0000 (---------------) + I artanddesign 0x002ea149, // n0x0f0b c0x0000 (---------------) + I artcenter 0x00209247, // n0x0f0c c0x0000 (---------------) + I artdeco 0x00215acc, // n0x0f0d c0x0000 (---------------) + I arteducation 0x0023a80a, // n0x0f0e c0x0000 (---------------) + I artgallery 0x00214704, // n0x0f0f c0x0000 (---------------) + I arts 0x0021470d, // n0x0f10 c0x0000 (---------------) + I artsandcrafts 0x002ea008, // n0x0f11 c0x0000 (---------------) + I asmatart 0x0020c80d, // n0x0f12 c0x0000 (---------------) + I assassination 0x00205f06, // n0x0f13 c0x0000 (---------------) + I assisi 0x002c5c8b, // n0x0f14 c0x0000 (---------------) + I association 0x00302089, // n0x0f15 c0x0000 (---------------) + I astronomy 0x0025abc7, // n0x0f16 c0x0000 (---------------) + I atlanta 0x0032fb46, // n0x0f17 c0x0000 (---------------) + I austin 0x00296449, // n0x0f18 c0x0000 (---------------) + I australia 0x003017ca, // n0x0f19 c0x0000 (---------------) + I automotive 0x0022e908, // n0x0f1a c0x0000 (---------------) + I aviation 0x00265604, // n0x0f1b c0x0000 (---------------) + I axis 0x002ae347, // n0x0f1c c0x0000 (---------------) + I badajoz 0x002ae9c7, // n0x0f1d c0x0000 (---------------) + I baghdad 0x00350584, // n0x0f1e c0x0000 (---------------) + I bahn 0x00204fc4, // n0x0f1f c0x0000 (---------------) + I bale 0x002aa649, // n0x0f20 c0x0000 (---------------) + I baltimore 0x002f0d89, // n0x0f21 c0x0000 (---------------) + I barcelona 0x0029ac88, // n0x0f22 c0x0000 (---------------) + I baseball 0x00305245, // n0x0f23 c0x0000 (---------------) + I basel 0x00212245, // n0x0f24 c0x0000 (---------------) + I baths 0x0027d706, // n0x0f25 c0x0000 (---------------) + I bauern 0x002145c9, // n0x0f26 c0x0000 (---------------) + I beauxarts 0x0020b08d, // n0x0f27 c0x0000 (---------------) + I beeldengeluid 0x00216508, // n0x0f28 c0x0000 (---------------) + I bellevue 0x0027d607, // n0x0f29 c0x0000 (---------------) + I bergbau 0x002b5248, // n0x0f2a c0x0000 (---------------) + I berkeley 0x00344946, // n0x0f2b c0x0000 (---------------) + I berlin 0x0034aac4, // n0x0f2c c0x0000 (---------------) + I bern 0x0023efc5, // n0x0f2d c0x0000 (---------------) + I bible 0x00205b86, // n0x0f2e c0x0000 (---------------) + I bilbao 0x00208204, // n0x0f2f c0x0000 (---------------) + I bill 0x00209147, // n0x0f30 c0x0000 (---------------) + I birdart 0x0020a20a, // n0x0f31 c0x0000 (---------------) + I birthplace 0x002125c4, // n0x0f32 c0x0000 (---------------) + I bonn 0x00212986, // n0x0f33 c0x0000 (---------------) + I boston 0x00213009, // n0x0f34 c0x0000 (---------------) + I botanical 0x0021300f, // n0x0f35 c0x0000 (---------------) + I botanicalgarden 0x00214d8d, // n0x0f36 c0x0000 (---------------) + I botanicgarden 0x00215346, // n0x0f37 c0x0000 (---------------) + I botany 0x00217e50, // n0x0f38 c0x0000 (---------------) + I brandywinevalley 0x002185c6, // n0x0f39 c0x0000 (---------------) + I brasil 0x0021c487, // n0x0f3a c0x0000 (---------------) + I bristol 0x0021d307, // n0x0f3b c0x0000 (---------------) + I british 0x0021e54f, // n0x0f3c c0x0000 (---------------) + I britishcolumbia 0x0021f749, // n0x0f3d c0x0000 (---------------) + I broadcast 0x00223b46, // n0x0f3e c0x0000 (---------------) + I brunel 0x00224347, // n0x0f3f c0x0000 (---------------) + I brussel 0x00224348, // n0x0f40 c0x0000 (---------------) + I brussels 0x00225289, // n0x0f41 c0x0000 (---------------) + I bruxelles 0x0022f448, // n0x0f42 c0x0000 (---------------) + I building 0x002c8907, // n0x0f43 c0x0000 (---------------) + I burghof 0x0022a603, // n0x0f44 c0x0000 (---------------) + I bus 0x0027d046, // n0x0f45 c0x0000 (---------------) + I bushey 0x002b0548, // n0x0f46 c0x0000 (---------------) + I cadaques 0x0023990a, // n0x0f47 c0x0000 (---------------) + I california 0x00279f89, // n0x0f48 c0x0000 (---------------) + I cambridge 0x00222183, // n0x0f49 c0x0000 (---------------) + I can 0x0029f246, // n0x0f4a c0x0000 (---------------) + I canada 0x00227fca, // n0x0f4b c0x0000 (---------------) + I capebreton 0x002154c7, // n0x0f4c c0x0000 (---------------) + I carrier 0x0021590a, // n0x0f4d c0x0000 (---------------) + I cartoonart 0x0021f3ce, // n0x0f4e c0x0000 (---------------) + I casadelamoneda 0x0021f886, // n0x0f4f c0x0000 (---------------) + I castle 0x00241e47, // n0x0f50 c0x0000 (---------------) + I castres 0x0022a046, // n0x0f51 c0x0000 (---------------) + I celtic 0x002456c6, // n0x0f52 c0x0000 (---------------) + I center 0x0025808b, // n0x0f53 c0x0000 (---------------) + I chattanooga 0x00264b0a, // n0x0f54 c0x0000 (---------------) + I cheltenham 0x003109cd, // n0x0f55 c0x0000 (---------------) + I chesapeakebay 0x002d5987, // n0x0f56 c0x0000 (---------------) + I chicago 0x0029f748, // n0x0f57 c0x0000 (---------------) + I children 0x0029f749, // n0x0f58 c0x0000 (---------------) + I childrens 0x0029f74f, // n0x0f59 c0x0000 (---------------) + I childrensgarden 0x00298d4c, // n0x0f5a c0x0000 (---------------) + I chiropractic 0x002d2789, // n0x0f5b c0x0000 (---------------) + I chocolate 0x0033f9ce, // n0x0f5c c0x0000 (---------------) + I christiansburg 0x002d4b0a, // n0x0f5d c0x0000 (---------------) + I cincinnati 0x0029e686, // n0x0f5e c0x0000 (---------------) + I cinema 0x0022d3c6, // n0x0f5f c0x0000 (---------------) + I circus 0x0022eb0c, // n0x0f60 c0x0000 (---------------) + I civilisation 0x0022ee0c, // n0x0f61 c0x0000 (---------------) + I civilization 0x0022f108, // n0x0f62 c0x0000 (---------------) + I civilwar 0x00230e87, // n0x0f63 c0x0000 (---------------) + I clinton 0x00200485, // n0x0f64 c0x0000 (---------------) + I clock 0x00209384, // n0x0f65 c0x0000 (---------------) + I coal 0x003210ce, // n0x0f66 c0x0000 (---------------) + I coastaldefence 0x00294d44, // n0x0f67 c0x0000 (---------------) + I cody 0x002cfa07, // n0x0f68 c0x0000 (---------------) + I coldwar 0x0022358a, // n0x0f69 c0x0000 (---------------) + I collection 0x002350d4, // n0x0f6a c0x0000 (---------------) + I colonialwilliamsburg 0x00235a4f, // n0x0f6b c0x0000 (---------------) + I coloradoplateau 0x0021e708, // n0x0f6c c0x0000 (---------------) + I columbia 0x00236788, // n0x0f6d c0x0000 (---------------) + I columbus 0x002cd78d, // n0x0f6e c0x0000 (---------------) + I communication 0x002cd78e, // n0x0f6f c0x0000 (---------------) + I communications 0x002b7e49, // n0x0f70 c0x0000 (---------------) + I community 0x002376c8, // n0x0f71 c0x0000 (---------------) + I computer 0x002376cf, // n0x0f72 c0x0000 (---------------) + I computerhistory 0x0023a50c, // n0x0f73 c0x0000 (---------------) + I contemporary 0x0023a50f, // n0x0f74 c0x0000 (---------------) + I contemporaryart 0x0023b487, // n0x0f75 c0x0000 (---------------) + I convent 0x0023de0a, // n0x0f76 c0x0000 (---------------) + I copenhagen 0x0023fccb, // n0x0f77 c0x0000 (---------------) + I corporation 0x0023ff88, // n0x0f78 c0x0000 (---------------) + I corvette 0x00240a07, // n0x0f79 c0x0000 (---------------) + I costume 0x002a324d, // n0x0f7a c0x0000 (---------------) + I countryestate 0x002421c6, // n0x0f7b c0x0000 (---------------) + I county 0x002148c6, // n0x0f7c c0x0000 (---------------) + I crafts 0x00243189, // n0x0f7d c0x0000 (---------------) + I cranbrook 0x00229b48, // n0x0f7e c0x0000 (---------------) + I creation 0x002454c8, // n0x0f7f c0x0000 (---------------) + I cultural 0x002454ce, // n0x0f80 c0x0000 (---------------) + I culturalcenter 0x0022d547, // n0x0f81 c0x0000 (---------------) + I culture 0x0020a605, // n0x0f82 c0x0000 (---------------) + I cyber 0x002c5785, // n0x0f83 c0x0000 (---------------) + I cymru 0x00201744, // n0x0f84 c0x0000 (---------------) + I dali 0x00272b46, // n0x0f85 c0x0000 (---------------) + I dallas 0x0029ab88, // n0x0f86 c0x0000 (---------------) + I database 0x00262943, // n0x0f87 c0x0000 (---------------) + I ddr 0x0023c90e, // n0x0f88 c0x0000 (---------------) + I decorativearts 0x002ba588, // n0x0f89 c0x0000 (---------------) + I delaware 0x0026cf4b, // n0x0f8a c0x0000 (---------------) + I delmenhorst 0x00346b07, // n0x0f8b c0x0000 (---------------) + I denmark 0x00233905, // n0x0f8c c0x0000 (---------------) + I depot 0x00251c86, // n0x0f8d c0x0000 (---------------) + I design 0x0029b347, // n0x0f8e c0x0000 (---------------) + I detroit 0x00309748, // n0x0f8f c0x0000 (---------------) + I dinosaur 0x002e0649, // n0x0f90 c0x0000 (---------------) + I discovery 0x0035a305, // n0x0f91 c0x0000 (---------------) + I dolls 0x002758c8, // n0x0f92 c0x0000 (---------------) + I donostia 0x002e4cc6, // n0x0f93 c0x0000 (---------------) + I durham 0x00279d8a, // n0x0f94 c0x0000 (---------------) + I eastafrica 0x00320fc9, // n0x0f95 c0x0000 (---------------) + I eastcoast 0x00215b89, // n0x0f96 c0x0000 (---------------) + I education 0x00215b8b, // n0x0f97 c0x0000 (---------------) + I educational 0x002fd088, // n0x0f98 c0x0000 (---------------) + I egyptian 0x00350449, // n0x0f99 c0x0000 (---------------) + I eisenbahn 0x00305306, // n0x0f9a c0x0000 (---------------) + I elburg 0x0029cb8a, // n0x0f9b c0x0000 (---------------) + I elvendrell 0x0035cb8a, // n0x0f9c c0x0000 (---------------) + I embroidery 0x0023e00c, // n0x0f9d c0x0000 (---------------) + I encyclopedic 0x00206447, // n0x0f9e c0x0000 (---------------) + I england 0x0034eb8a, // n0x0f9f c0x0000 (---------------) + I entomology 0x002af74b, // n0x0fa0 c0x0000 (---------------) + I environment 0x002af759, // n0x0fa1 c0x0000 (---------------) + I environmentalconservation 0x0020b888, // n0x0fa2 c0x0000 (---------------) + I epilepsy 0x002231c5, // n0x0fa3 c0x0000 (---------------) + I essex 0x002a3406, // n0x0fa4 c0x0000 (---------------) + I estate 0x002c7849, // n0x0fa5 c0x0000 (---------------) + I ethnology 0x00217cc6, // n0x0fa6 c0x0000 (---------------) + I exeter 0x0021ce8a, // n0x0fa7 c0x0000 (---------------) + I exhibition 0x0030e6c6, // n0x0fa8 c0x0000 (---------------) + I family 0x00224844, // n0x0fa9 c0x0000 (---------------) + I farm 0x00343ccd, // n0x0faa c0x0000 (---------------) + I farmequipment 0x0022c407, // n0x0fab c0x0000 (---------------) + I farmers 0x00224849, // n0x0fac c0x0000 (---------------) + I farmstead 0x00246a85, // n0x0fad c0x0000 (---------------) + I field 0x00246bc8, // n0x0fae c0x0000 (---------------) + I figueres 0x00247489, // n0x0faf c0x0000 (---------------) + I filatelia 0x002476c4, // n0x0fb0 c0x0000 (---------------) + I film 0x00247ac7, // n0x0fb1 c0x0000 (---------------) + I fineart 0x00247ac8, // n0x0fb2 c0x0000 (---------------) + I finearts 0x00247e47, // n0x0fb3 c0x0000 (---------------) + I finland 0x0032e888, // n0x0fb4 c0x0000 (---------------) + I flanders 0x0024c307, // n0x0fb5 c0x0000 (---------------) + I florida 0x0024eb05, // n0x0fb6 c0x0000 (---------------) + I force 0x0025088c, // n0x0fb7 c0x0000 (---------------) + I fortmissoula 0x00251289, // n0x0fb8 c0x0000 (---------------) + I fortworth 0x002ca74a, // n0x0fb9 c0x0000 (---------------) + I foundation 0x00343109, // n0x0fba c0x0000 (---------------) + I francaise 0x002fd309, // n0x0fbb c0x0000 (---------------) + I frankfurt 0x0032dfcc, // n0x0fbc c0x0000 (---------------) + I franziskaner 0x0022180b, // n0x0fbd c0x0000 (---------------) + I freemasonry 0x00253048, // n0x0fbe c0x0000 (---------------) + I freiburg 0x00253e48, // n0x0fbf c0x0000 (---------------) + I fribourg 0x00254104, // n0x0fc0 c0x0000 (---------------) + I frog 0x00274048, // n0x0fc1 c0x0000 (---------------) + I fundacio 0x00274949, // n0x0fc2 c0x0000 (---------------) + I furniture 0x0023a8c7, // n0x0fc3 c0x0000 (---------------) + I gallery 0x00213246, // n0x0fc4 c0x0000 (---------------) + I garden 0x003176c7, // n0x0fc5 c0x0000 (---------------) + I gateway 0x0023aac9, // n0x0fc6 c0x0000 (---------------) + I geelvinck 0x0023970b, // n0x0fc7 c0x0000 (---------------) + I gemological 0x002a9c47, // n0x0fc8 c0x0000 (---------------) + I geology 0x00301647, // n0x0fc9 c0x0000 (---------------) + I georgia 0x002b4187, // n0x0fca c0x0000 (---------------) + I giessen 0x0020c784, // n0x0fcb c0x0000 (---------------) + I glas 0x0020c785, // n0x0fcc c0x0000 (---------------) + I glass 0x0024ca05, // n0x0fcd c0x0000 (---------------) + I gorge 0x0030314b, // n0x0fce c0x0000 (---------------) + I grandrapids 0x00346484, // n0x0fcf c0x0000 (---------------) + I graz 0x00257148, // n0x0fd0 c0x0000 (---------------) + I guernsey 0x002d388a, // n0x0fd1 c0x0000 (---------------) + I halloffame 0x002e4d87, // n0x0fd2 c0x0000 (---------------) + I hamburg 0x003403c7, // n0x0fd3 c0x0000 (---------------) + I handson 0x0027dc52, // n0x0fd4 c0x0000 (---------------) + I harvestcelebration 0x00281246, // n0x0fd5 c0x0000 (---------------) + I hawaii 0x00297d06, // n0x0fd6 c0x0000 (---------------) + I health 0x002af18e, // n0x0fd7 c0x0000 (---------------) + I heimatunduhren 0x002e5506, // n0x0fd8 c0x0000 (---------------) + I hellas 0x00207d48, // n0x0fd9 c0x0000 (---------------) + I helsinki 0x0020ec8f, // n0x0fda c0x0000 (---------------) + I hembygdsforbund 0x002d4e88, // n0x0fdb c0x0000 (---------------) + I heritage 0x00265908, // n0x0fdc c0x0000 (---------------) + I histoire 0x002d6f0a, // n0x0fdd c0x0000 (---------------) + I historical 0x002d6f11, // n0x0fde c0x0000 (---------------) + I historicalsociety 0x0028fc8e, // n0x0fdf c0x0000 (---------------) + I historichouses 0x0025e48a, // n0x0fe0 c0x0000 (---------------) + I historisch 0x0025e48c, // n0x0fe1 c0x0000 (---------------) + I historisches 0x002378c7, // n0x0fe2 c0x0000 (---------------) + I history 0x002378d0, // n0x0fe3 c0x0000 (---------------) + I historyofscience 0x00200fc8, // n0x0fe4 c0x0000 (---------------) + I horology 0x0022af05, // n0x0fe5 c0x0000 (---------------) + I house 0x0029b64a, // n0x0fe6 c0x0000 (---------------) + I humanities 0x0020824c, // n0x0fe7 c0x0000 (---------------) + I illustration 0x00281ccd, // n0x0fe8 c0x0000 (---------------) + I imageandsound 0x0020e506, // n0x0fe9 c0x0000 (---------------) + I indian 0x0020e507, // n0x0fea c0x0000 (---------------) + I indiana 0x0020e50c, // n0x0feb c0x0000 (---------------) + I indianapolis 0x00210e4c, // n0x0fec c0x0000 (---------------) + I indianmarket 0x00216fcc, // n0x0fed c0x0000 (---------------) + I intelligence 0x0031a04b, // n0x0fee c0x0000 (---------------) + I interactive 0x00275304, // n0x0fef c0x0000 (---------------) + I iraq 0x0021f0c4, // n0x0ff0 c0x0000 (---------------) + I iron 0x0033ecc9, // n0x0ff1 c0x0000 (---------------) + I isleofman 0x0030df47, // n0x0ff2 c0x0000 (---------------) + I jamison 0x002943c9, // n0x0ff3 c0x0000 (---------------) + I jefferson 0x00290309, // n0x0ff4 c0x0000 (---------------) + I jerusalem 0x0031f987, // n0x0ff5 c0x0000 (---------------) + I jewelry 0x00324ac6, // n0x0ff6 c0x0000 (---------------) + I jewish 0x00324ac9, // n0x0ff7 c0x0000 (---------------) + I jewishart 0x00354943, // n0x0ff8 c0x0000 (---------------) + I jfk 0x0020fe8a, // n0x0ff9 c0x0000 (---------------) + I journalism 0x00313707, // n0x0ffa c0x0000 (---------------) + I judaica 0x002eafcb, // n0x0ffb c0x0000 (---------------) + I judygarland 0x0031084a, // n0x0ffc c0x0000 (---------------) + I juedisches 0x00327fc4, // n0x0ffd c0x0000 (---------------) + I juif 0x0031e786, // n0x0ffe c0x0000 (---------------) + I karate 0x002b8c89, // n0x0fff c0x0000 (---------------) + I karikatur 0x00357e04, // n0x1000 c0x0000 (---------------) + I kids 0x0020d74a, // n0x1001 c0x0000 (---------------) + I koebenhavn 0x0024f505, // n0x1002 c0x0000 (---------------) + I koeln 0x002a8585, // n0x1003 c0x0000 (---------------) + I kunst 0x002a858d, // n0x1004 c0x0000 (---------------) + I kunstsammlung 0x002a88ce, // n0x1005 c0x0000 (---------------) + I kunstunddesign 0x002f8d45, // n0x1006 c0x0000 (---------------) + I labor 0x00348506, // n0x1007 c0x0000 (---------------) + I labour 0x00293007, // n0x1008 c0x0000 (---------------) + I lajolla 0x0027b94a, // n0x1009 c0x0000 (---------------) + I lancashire 0x0034cec6, // n0x100a c0x0000 (---------------) + I landes 0x002e5404, // n0x100b c0x0000 (---------------) + I lans 0x002bcd87, // n0x100c c0x0000 (---------------) + I larsson 0x0030708b, // n0x100d c0x0000 (---------------) + I lewismiller 0x00344a07, // n0x100e c0x0000 (---------------) + I lincoln 0x0020ad44, // n0x100f c0x0000 (---------------) + I linz 0x00299b46, // n0x1010 c0x0000 (---------------) + I living 0x00299b4d, // n0x1011 c0x0000 (---------------) + I livinghistory 0x002563cc, // n0x1012 c0x0000 (---------------) + I localhistory 0x002b9b86, // n0x1013 c0x0000 (---------------) + I london 0x0021fd4a, // n0x1014 c0x0000 (---------------) + I losangeles 0x002241c6, // n0x1015 c0x0000 (---------------) + I louvre 0x00202f88, // n0x1016 c0x0000 (---------------) + I loyalist 0x0022a447, // n0x1017 c0x0000 (---------------) + I lucerne 0x002332ca, // n0x1018 c0x0000 (---------------) + I luxembourg 0x0023af86, // n0x1019 c0x0000 (---------------) + I luzern 0x0026e7c3, // n0x101a c0x0000 (---------------) + I mad 0x00272f46, // n0x101b c0x0000 (---------------) + I madrid 0x002957c8, // n0x101c c0x0000 (---------------) + I mallorca 0x0033ee4a, // n0x101d c0x0000 (---------------) + I manchester 0x0025da07, // n0x101e c0x0000 (---------------) + I mansion 0x0025da08, // n0x101f c0x0000 (---------------) + I mansions 0x00270604, // n0x1020 c0x0000 (---------------) + I manx 0x002913c7, // n0x1021 c0x0000 (---------------) + I marburg 0x00246448, // n0x1022 c0x0000 (---------------) + I maritime 0x00306308, // n0x1023 c0x0000 (---------------) + I maritimo 0x00281448, // n0x1024 c0x0000 (---------------) + I maryland 0x002870ca, // n0x1025 c0x0000 (---------------) + I marylhurst 0x00216085, // n0x1026 c0x0000 (---------------) + I media 0x00240b47, // n0x1027 c0x0000 (---------------) + I medical 0x0025e2d3, // n0x1028 c0x0000 (---------------) + I medizinhistorisches 0x00286a46, // n0x1029 c0x0000 (---------------) + I meeres 0x00224008, // n0x102a c0x0000 (---------------) + I memorial 0x00297109, // n0x102b c0x0000 (---------------) + I mesaverde 0x00228a48, // n0x102c c0x0000 (---------------) + I michigan 0x00283a4b, // n0x102d c0x0000 (---------------) + I midatlantic 0x00210708, // n0x102e c0x0000 (---------------) + I military 0x0022c8c4, // n0x102f c0x0000 (---------------) + I mill 0x00200906, // n0x1030 c0x0000 (---------------) + I miners 0x002c83c6, // n0x1031 c0x0000 (---------------) + I mining 0x002bc009, // n0x1032 c0x0000 (---------------) + I minnesota 0x002b6c47, // n0x1033 c0x0000 (---------------) + I missile 0x00250988, // n0x1034 c0x0000 (---------------) + I missoula 0x00286606, // n0x1035 c0x0000 (---------------) + I modern 0x0035bb84, // n0x1036 c0x0000 (---------------) + I moma 0x002bd985, // n0x1037 c0x0000 (---------------) + I money 0x002b94c8, // n0x1038 c0x0000 (---------------) + I monmouth 0x002b998a, // n0x1039 c0x0000 (---------------) + I monticello 0x002ba188, // n0x103a c0x0000 (---------------) + I montreal 0x002bde06, // n0x103b c0x0000 (---------------) + I moscow 0x0028a44a, // n0x103c c0x0000 (---------------) + I motorcycle 0x002b4888, // n0x103d c0x0000 (---------------) + I muenchen 0x002bf408, // n0x103e c0x0000 (---------------) + I muenster 0x002c0b48, // n0x103f c0x0000 (---------------) + I mulhouse 0x002c1706, // n0x1040 c0x0000 (---------------) + I muncie 0x002c2a46, // n0x1041 c0x0000 (---------------) + I museet 0x002ff70c, // n0x1042 c0x0000 (---------------) + I museumcenter 0x002c3950, // n0x1043 c0x0000 (---------------) + I museumvereniging 0x0031b445, // n0x1044 c0x0000 (---------------) + I music 0x0020c9c8, // n0x1045 c0x0000 (---------------) + I national 0x0020c9d0, // n0x1046 c0x0000 (---------------) + I nationalfirearms 0x002d4c90, // n0x1047 c0x0000 (---------------) + I nationalheritage 0x002f508e, // n0x1048 c0x0000 (---------------) + I nativeamerican 0x002ff38e, // n0x1049 c0x0000 (---------------) + I naturalhistory 0x002ff394, // n0x104a c0x0000 (---------------) + I naturalhistorymuseum 0x0033a10f, // n0x104b c0x0000 (---------------) + I naturalsciences 0x0033a4c6, // n0x104c c0x0000 (---------------) + I nature 0x00344ed1, // n0x104d c0x0000 (---------------) + I naturhistorisches 0x0035c093, // n0x104e c0x0000 (---------------) + I natuurwetenschappen 0x0035c508, // n0x104f c0x0000 (---------------) + I naumburg 0x00203285, // n0x1050 c0x0000 (---------------) + I naval 0x00291d88, // n0x1051 c0x0000 (---------------) + I nebraska 0x00296b45, // n0x1052 c0x0000 (---------------) + I neues 0x0022984c, // n0x1053 c0x0000 (---------------) + I newhampshire 0x0022a849, // n0x1054 c0x0000 (---------------) + I newjersey 0x00294b89, // n0x1055 c0x0000 (---------------) + I newmexico 0x00317447, // n0x1056 c0x0000 (---------------) + I newport 0x002407c9, // n0x1057 c0x0000 (---------------) + I newspaper 0x002448c7, // n0x1058 c0x0000 (---------------) + I newyork 0x0032aa46, // n0x1059 c0x0000 (---------------) + I niepce 0x0023edc7, // n0x105a c0x0000 (---------------) + I norfolk 0x00315d85, // n0x105b c0x0000 (---------------) + I north 0x0032d643, // n0x105c c0x0000 (---------------) + I nrw 0x00262b09, // n0x105d c0x0000 (---------------) + I nuernberg 0x0024b249, // n0x105e c0x0000 (---------------) + I nuremberg 0x00215443, // n0x105f c0x0000 (---------------) + I nyc 0x002ac584, // n0x1060 c0x0000 (---------------) + I nyny 0x0020880d, // n0x1061 c0x0000 (---------------) + I oceanographic 0x0020b50f, // n0x1062 c0x0000 (---------------) + I oceanographique 0x0029fc45, // n0x1063 c0x0000 (---------------) + I omaha 0x002e59c6, // n0x1064 c0x0000 (---------------) + I online 0x00234287, // n0x1065 c0x0000 (---------------) + I ontario 0x00264f47, // n0x1066 c0x0000 (---------------) + I openair 0x00276f46, // n0x1067 c0x0000 (---------------) + I oregon 0x00276f4b, // n0x1068 c0x0000 (---------------) + I oregontrail 0x00291745, // n0x1069 c0x0000 (---------------) + I otago 0x0034dc86, // n0x106a c0x0000 (---------------) + I oxford 0x00264987, // n0x106b c0x0000 (---------------) + I pacific 0x002acd49, // n0x106c c0x0000 (---------------) + I paderborn 0x002417c6, // n0x106d c0x0000 (---------------) + I palace 0x0020b405, // n0x106e c0x0000 (---------------) + I paleo 0x00220e8b, // n0x106f c0x0000 (---------------) + I palmsprings 0x00226206, // n0x1070 c0x0000 (---------------) + I panama 0x0026eec5, // n0x1071 c0x0000 (---------------) + I paris 0x002c1048, // n0x1072 c0x0000 (---------------) + I pasadena 0x002c5608, // n0x1073 c0x0000 (---------------) + I pharmacy 0x002c5f4c, // n0x1074 c0x0000 (---------------) + I philadelphia 0x002c5f50, // n0x1075 c0x0000 (---------------) + I philadelphiaarea 0x002c6609, // n0x1076 c0x0000 (---------------) + I philately 0x002c6847, // n0x1077 c0x0000 (---------------) + I phoenix 0x002c710b, // n0x1078 c0x0000 (---------------) + I photography 0x002c7a86, // n0x1079 c0x0000 (---------------) + I pilots 0x002c87ca, // n0x107a c0x0000 (---------------) + I pittsburgh 0x002c960b, // n0x107b c0x0000 (---------------) + I planetarium 0x002ca00a, // n0x107c c0x0000 (---------------) + I plantation 0x002ca286, // n0x107d c0x0000 (---------------) + I plants 0x002ca9c5, // n0x107e c0x0000 (---------------) + I plaza 0x00312ec6, // n0x107f c0x0000 (---------------) + I portal 0x0028eb48, // n0x1080 c0x0000 (---------------) + I portland 0x0031750a, // n0x1081 c0x0000 (---------------) + I portlligat 0x002cd41c, // n0x1082 c0x0000 (---------------) + I posts-and-telecommunications 0x002cdf8c, // n0x1083 c0x0000 (---------------) + I preservation 0x002ce288, // n0x1084 c0x0000 (---------------) + I presidio 0x00223145, // n0x1085 c0x0000 (---------------) + I press 0x002cfd07, // n0x1086 c0x0000 (---------------) + I project 0x0029f106, // n0x1087 c0x0000 (---------------) + I public 0x00339c45, // n0x1088 c0x0000 (---------------) + I pubol 0x002157c6, // n0x1089 c0x0000 (---------------) + I quebec 0x00277108, // n0x108a c0x0000 (---------------) + I railroad 0x0029da87, // n0x108b c0x0000 (---------------) + I railway 0x002cc008, // n0x108c c0x0000 (---------------) + I research 0x00241f4a, // n0x108d c0x0000 (---------------) + I resistance 0x0023438c, // n0x108e c0x0000 (---------------) + I riodejaneiro 0x0030c409, // n0x108f c0x0000 (---------------) + I rochester 0x0034df87, // n0x1090 c0x0000 (---------------) + I rockart 0x0029fc04, // n0x1091 c0x0000 (---------------) + I roma 0x00329e06, // n0x1092 c0x0000 (---------------) + I russia 0x002b1e8a, // n0x1093 c0x0000 (---------------) + I saintlouis 0x00290405, // n0x1094 c0x0000 (---------------) + I salem 0x0021ff8c, // n0x1095 c0x0000 (---------------) + I salvadordali 0x00224508, // n0x1096 c0x0000 (---------------) + I salzburg 0x002d7f48, // n0x1097 c0x0000 (---------------) + I sandiego 0x002cf78c, // n0x1098 c0x0000 (---------------) + I sanfrancisco 0x00236dcc, // n0x1099 c0x0000 (---------------) + I santabarbara 0x00238289, // n0x109a c0x0000 (---------------) + I santacruz 0x0023cc47, // n0x109b c0x0000 (---------------) + I santafe 0x0025bb0c, // n0x109c c0x0000 (---------------) + I saskatchewan 0x0025e744, // n0x109d c0x0000 (---------------) + I satx 0x00266b0a, // n0x109e c0x0000 (---------------) + I savannahga 0x0026684c, // n0x109f c0x0000 (---------------) + I schlesisches 0x0027e64b, // n0x10a0 c0x0000 (---------------) + I schoenbrunn 0x0027990b, // n0x10a1 c0x0000 (---------------) + I schokoladen 0x00286b86, // n0x10a2 c0x0000 (---------------) + I school 0x00288a47, // n0x10a3 c0x0000 (---------------) + I schweiz 0x00237b07, // n0x10a4 c0x0000 (---------------) + I science 0x00237b0f, // n0x10a5 c0x0000 (---------------) + I science-fiction 0x002e7111, // n0x10a6 c0x0000 (---------------) + I scienceandhistory 0x0026e0d2, // n0x10a7 c0x0000 (---------------) + I scienceandindustry 0x0028a6cd, // n0x10a8 c0x0000 (---------------) + I sciencecenter 0x0028a6ce, // n0x10a9 c0x0000 (---------------) + I sciencecenters 0x0028aa0e, // n0x10aa c0x0000 (---------------) + I sciencehistory 0x0033a2c8, // n0x10ab c0x0000 (---------------) + I sciences 0x0033a2d2, // n0x10ac c0x0000 (---------------) + I sciencesnaturelles 0x00219208, // n0x10ad c0x0000 (---------------) + I scotland 0x002eddc7, // n0x10ae c0x0000 (---------------) + I seaport 0x002c8dca, // n0x10af c0x0000 (---------------) + I settlement 0x00260fc8, // n0x10b0 c0x0000 (---------------) + I settlers 0x002e54c5, // n0x10b1 c0x0000 (---------------) + I shell 0x002fb48a, // n0x10b2 c0x0000 (---------------) + I sherbrooke 0x00206007, // n0x10b3 c0x0000 (---------------) + I sibenik 0x002c2c44, // n0x10b4 c0x0000 (---------------) + I silk 0x00201903, // n0x10b5 c0x0000 (---------------) + I ski 0x00258e85, // n0x10b6 c0x0000 (---------------) + I skole 0x002d7187, // n0x10b7 c0x0000 (---------------) + I society 0x002db0c7, // n0x10b8 c0x0000 (---------------) + I sologne 0x00281ece, // n0x10b9 c0x0000 (---------------) + I soundandvision 0x002dea0d, // n0x10ba c0x0000 (---------------) + I southcarolina 0x002dee49, // n0x10bb c0x0000 (---------------) + I southwest 0x002df645, // n0x10bc c0x0000 (---------------) + I space 0x002e1cc3, // n0x10bd c0x0000 (---------------) + I spy 0x002e1f06, // n0x10be c0x0000 (---------------) + I square 0x00259d45, // n0x10bf c0x0000 (---------------) + I stadt 0x002b0088, // n0x10c0 c0x0000 (---------------) + I stalbans 0x00269e09, // n0x10c1 c0x0000 (---------------) + I starnberg 0x002a3445, // n0x10c2 c0x0000 (---------------) + I state 0x002ba3cf, // n0x10c3 c0x0000 (---------------) + I stateofdelaware 0x002872c7, // n0x10c4 c0x0000 (---------------) + I station 0x002137c5, // n0x10c5 c0x0000 (---------------) + I steam 0x002f25ca, // n0x10c6 c0x0000 (---------------) + I steiermark 0x002e2486, // n0x10c7 c0x0000 (---------------) + I stjohn 0x002e29c9, // n0x10c8 c0x0000 (---------------) + I stockholm 0x002e370c, // n0x10c9 c0x0000 (---------------) + I stpetersburg 0x002e3d49, // n0x10ca c0x0000 (---------------) + I stuttgart 0x00222e86, // n0x10cb c0x0000 (---------------) + I suisse 0x002d368c, // n0x10cc c0x0000 (---------------) + I surgeonshall 0x002f8486, // n0x10cd c0x0000 (---------------) + I surrey 0x002e5d88, // n0x10ce c0x0000 (---------------) + I svizzera 0x00275046, // n0x10cf c0x0000 (---------------) + I sweden 0x0027f406, // n0x10d0 c0x0000 (---------------) + I sydney 0x002de304, // n0x10d1 c0x0000 (---------------) + I tank 0x00312683, // n0x10d2 c0x0000 (---------------) + I tcm 0x0031534a, // n0x10d3 c0x0000 (---------------) + I technology 0x002f4c91, // n0x10d4 c0x0000 (---------------) + I telekommunikation 0x0023c00a, // n0x10d5 c0x0000 (---------------) + I television 0x0023dc45, // n0x10d6 c0x0000 (---------------) + I texas 0x00240107, // n0x10d7 c0x0000 (---------------) + I textile 0x003112c7, // n0x10d8 c0x0000 (---------------) + I theater 0x00246544, // n0x10d9 c0x0000 (---------------) + I time 0x0024654b, // n0x10da c0x0000 (---------------) + I timekeeping 0x002fad88, // n0x10db c0x0000 (---------------) + I topology 0x002a5e06, // n0x10dc c0x0000 (---------------) + I torino 0x00233685, // n0x10dd c0x0000 (---------------) + I touch 0x00326644, // n0x10de c0x0000 (---------------) + I town 0x002d8a09, // n0x10df c0x0000 (---------------) + I transport 0x0032ef44, // n0x10e0 c0x0000 (---------------) + I tree 0x00265f07, // n0x10e1 c0x0000 (---------------) + I trolley 0x002c2185, // n0x10e2 c0x0000 (---------------) + I trust 0x002c2187, // n0x10e3 c0x0000 (---------------) + I trustee 0x002af3c5, // n0x10e4 c0x0000 (---------------) + I uhren 0x00227a83, // n0x10e5 c0x0000 (---------------) + I ulm 0x002edc88, // n0x10e6 c0x0000 (---------------) + I undersea 0x002012ca, // n0x10e7 c0x0000 (---------------) + I university 0x00211f83, // n0x10e8 c0x0000 (---------------) + I usa 0x0023690a, // n0x10e9 c0x0000 (---------------) + I usantiques 0x00313ec6, // n0x10ea c0x0000 (---------------) + I usarts 0x002a31cf, // n0x10eb c0x0000 (---------------) + I uscountryestate 0x0022d4c9, // n0x10ec c0x0000 (---------------) + I usculture 0x0023c890, // n0x10ed c0x0000 (---------------) + I usdecorativearts 0x0027d888, // n0x10ee c0x0000 (---------------) + I usgarden 0x002be089, // n0x10ef c0x0000 (---------------) + I ushistory 0x00208047, // n0x10f0 c0x0000 (---------------) + I ushuaia 0x00299acf, // n0x10f1 c0x0000 (---------------) + I uslivinghistory 0x00325e04, // n0x10f2 c0x0000 (---------------) + I utah 0x002d8144, // n0x10f3 c0x0000 (---------------) + I uvic 0x002180c6, // n0x10f4 c0x0000 (---------------) + I valley 0x002ce7c6, // n0x10f5 c0x0000 (---------------) + I vantaa 0x002daaca, // n0x10f6 c0x0000 (---------------) + I versailles 0x0025aa46, // n0x10f7 c0x0000 (---------------) + I viking 0x00319b47, // n0x10f8 c0x0000 (---------------) + I village 0x002eba48, // n0x10f9 c0x0000 (---------------) + I virginia 0x002ebc47, // n0x10fa c0x0000 (---------------) + I virtual 0x002ebe07, // n0x10fb c0x0000 (---------------) + I virtuel 0x0030cf8a, // n0x10fc c0x0000 (---------------) + I vlaanderen 0x002edacb, // n0x10fd c0x0000 (---------------) + I volkenkunde 0x0026a905, // n0x10fe c0x0000 (---------------) + I wales 0x0026dd08, // n0x10ff c0x0000 (---------------) + I wallonie 0x0020f4c3, // n0x1100 c0x0000 (---------------) + I war 0x00334d8c, // n0x1101 c0x0000 (---------------) + I washingtondc 0x0020020f, // n0x1102 c0x0000 (---------------) + I watch-and-clock 0x0023f4cd, // n0x1103 c0x0000 (---------------) + I watchandclock 0x002def87, // n0x1104 c0x0000 (---------------) + I western 0x00312889, // n0x1105 c0x0000 (---------------) + I westfalen 0x00228707, // n0x1106 c0x0000 (---------------) + I whaling 0x0022bb88, // n0x1107 c0x0000 (---------------) + I wildlife 0x002352cc, // n0x1108 c0x0000 (---------------) + I williamsburg 0x0022c7c8, // n0x1109 c0x0000 (---------------) + I windmill 0x0022f988, // n0x110a c0x0000 (---------------) + I workshop 0x002f300e, // n0x110b c0x0000 (---------------) + I xn--9dbhblg6di 0x002fdfd4, // n0x110c c0x0000 (---------------) + I xn--comunicaes-v6a2o 0x002fe4e4, // n0x110d c0x0000 (---------------) + I xn--correios-e-telecomunicaes-ghc29a 0x0030e94a, // n0x110e c0x0000 (---------------) + I xn--h1aegh 0x0032674b, // n0x110f c0x0000 (---------------) + I xn--lns-qla 0x00244984, // n0x1110 c0x0000 (---------------) + I york 0x00244989, // n0x1111 c0x0000 (---------------) + I yorkshire 0x002d1ac8, // n0x1112 c0x0000 (---------------) + I yosemite 0x0029d385, // n0x1113 c0x0000 (---------------) + I youth 0x0023ad4a, // n0x1114 c0x0000 (---------------) + I zoological 0x002e20c7, // n0x1115 c0x0000 (---------------) + I zoology 0x002728c4, // n0x1116 c0x0000 (---------------) + I aero 0x00305743, // n0x1117 c0x0000 (---------------) + I biz 0x002370c3, // n0x1118 c0x0000 (---------------) + I com 0x0023d9c4, // n0x1119 c0x0000 (---------------) + I coop 0x00215b83, // n0x111a c0x0000 (---------------) + I edu 0x00208ec3, // n0x111b c0x0000 (---------------) + I gov 0x00214b84, // n0x111c c0x0000 (---------------) + I info 0x00216fc3, // n0x111d c0x0000 (---------------) + I int 0x00210703, // n0x111e c0x0000 (---------------) + I mil 0x002c3946, // n0x111f c0x0000 (---------------) + I museum 0x002592c4, // n0x1120 c0x0000 (---------------) + I name 0x0024bdc3, // n0x1121 c0x0000 (---------------) + I net 0x0024af03, // n0x1122 c0x0000 (---------------) + I org 0x00206683, // n0x1123 c0x0000 (---------------) + I pro 0x0020a3c2, // n0x1124 c0x0000 (---------------) + I ac 0x00305743, // n0x1125 c0x0000 (---------------) + I biz 0x00209382, // n0x1126 c0x0000 (---------------) + I co 0x002370c3, // n0x1127 c0x0000 (---------------) + I com 0x0023d9c4, // n0x1128 c0x0000 (---------------) + I coop 0x00215b83, // n0x1129 c0x0000 (---------------) + I edu 0x00208ec3, // n0x112a c0x0000 (---------------) + I gov 0x00216fc3, // n0x112b c0x0000 (---------------) + I int 0x002c3946, // n0x112c c0x0000 (---------------) + I museum 0x0024bdc3, // n0x112d c0x0000 (---------------) + I net 0x0024af03, // n0x112e c0x0000 (---------------) + I org 0x0012d948, // n0x112f c0x0000 (---------------) + blogspot 0x002370c3, // n0x1130 c0x0000 (---------------) + I com 0x00215b83, // n0x1131 c0x0000 (---------------) + I edu 0x00223ac3, // n0x1132 c0x0000 (---------------) + I gob 0x0024bdc3, // n0x1133 c0x0000 (---------------) + I net 0x0024af03, // n0x1134 c0x0000 (---------------) + I org 0x002370c3, // n0x1135 c0x0000 (---------------) + I com 0x00215b83, // n0x1136 c0x0000 (---------------) + I edu 0x00208ec3, // n0x1137 c0x0000 (---------------) + I gov 0x00210703, // n0x1138 c0x0000 (---------------) + I mil 0x002592c4, // n0x1139 c0x0000 (---------------) + I name 0x0024bdc3, // n0x113a c0x0000 (---------------) + I net 0x0024af03, // n0x113b c0x0000 (---------------) + I org 0x0069aa88, // n0x113c c0x0001 (---------------) ! I teledata 0x00213182, // n0x113d c0x0000 (---------------) + I ca 0x00223542, // n0x113e c0x0000 (---------------) + I cc 0x00209382, // n0x113f c0x0000 (---------------) + I co 0x002370c3, // n0x1140 c0x0000 (---------------) + I com 0x0020c442, // n0x1141 c0x0000 (---------------) + I dr 0x00200942, // n0x1142 c0x0000 (---------------) + I in 0x00214b84, // n0x1143 c0x0000 (---------------) + I info 0x0020d4c4, // n0x1144 c0x0000 (---------------) + I mobi 0x00336602, // n0x1145 c0x0000 (---------------) + I mx 0x002592c4, // n0x1146 c0x0000 (---------------) + I name 0x00201002, // n0x1147 c0x0000 (---------------) + I or 0x0024af03, // n0x1148 c0x0000 (---------------) + I org 0x00206683, // n0x1149 c0x0000 (---------------) + I pro 0x00286b86, // n0x114a c0x0000 (---------------) + I school 0x00288182, // n0x114b c0x0000 (---------------) + I tv 0x00201682, // n0x114c c0x0000 (---------------) + I us 0x00216a42, // n0x114d c0x0000 (---------------) + I ws 0x32233a43, // n0x114e c0x00c8 (n0x1150-n0x1151) o I her 0x32630143, // n0x114f c0x00c9 (n0x1151-n0x1152) o I his 0x0004ec46, // n0x1150 c0x0000 (---------------) + forgot 0x0004ec46, // n0x1151 c0x0000 (---------------) + forgot 0x002c5c84, // n0x1152 c0x0000 (---------------) + I asso 0x0015b48c, // n0x1153 c0x0000 (---------------) + at-band-camp 0x00052b8c, // n0x1154 c0x0000 (---------------) + azure-mobile 0x000b864d, // n0x1155 c0x0000 (---------------) + azurewebsites 0x00019587, // n0x1156 c0x0000 (---------------) + blogdns 0x00020908, // n0x1157 c0x0000 (---------------) + broke-it 0x0002ae0a, // n0x1158 c0x0000 (---------------) + buyshouses 0x00041608, // n0x1159 c0x0000 (---------------) + cloudapp 0x000340ca, // n0x115a c0x0000 (---------------) + cloudfront 0x00038f08, // n0x115b c0x0000 (---------------) + dnsalias 0x00073147, // n0x115c c0x0000 (---------------) + dnsdojo 0x000d5bc7, // n0x115d c0x0000 (---------------) + does-it 0x00116d49, // n0x115e c0x0000 (---------------) + dontexist 0x00094dc8, // n0x115f c0x0000 (---------------) + dynalias 0x0004ff09, // n0x1160 c0x0000 (---------------) + dynathome 0x00099f8d, // n0x1161 c0x0000 (---------------) + endofinternet 0x33328086, // n0x1162 c0x00cc (n0x1181-n0x1183) o I fastly 0x00055bc7, // n0x1163 c0x0000 (---------------) + from-az 0x00056bc7, // n0x1164 c0x0000 (---------------) + from-co 0x0005ca07, // n0x1165 c0x0000 (---------------) + from-la 0x00063e07, // n0x1166 c0x0000 (---------------) + from-ny 0x0007d6c2, // n0x1167 c0x0000 (---------------) + gb 0x0007a147, // n0x1168 c0x0000 (---------------) + gets-it 0x00064ccc, // n0x1169 c0x0000 (---------------) + ham-radio-op 0x00024c07, // n0x116a c0x0000 (---------------) + homeftp 0x000c0f06, // n0x116b c0x0000 (---------------) + homeip 0x00093989, // n0x116c c0x0000 (---------------) + homelinux 0x00097348, // n0x116d c0x0000 (---------------) + homeunix 0x00003f82, // n0x116e c0x0000 (---------------) + hu 0x0011700b, // n0x116f c0x0000 (---------------) + in-the-band 0x00084149, // n0x1170 c0x0000 (---------------) + is-a-chef 0x0005d009, // n0x1171 c0x0000 (---------------) + is-a-geek 0x000584c8, // n0x1172 c0x0000 (---------------) + isa-geek 0x0009e1c2, // n0x1173 c0x0000 (---------------) + jp 0x000552c9, // n0x1174 c0x0000 (---------------) + kicks-ass 0x0003e80d, // n0x1175 c0x0000 (---------------) + office-on-the 0x000cbc87, // n0x1176 c0x0000 (---------------) + podzone 0x0009848d, // n0x1177 c0x0000 (---------------) + scrapper-site 0x00004802, // n0x1178 c0x0000 (---------------) + se 0x0010c246, // n0x1179 c0x0000 (---------------) + selfip 0x000d8848, // n0x117a c0x0000 (---------------) + sells-it 0x000d7408, // n0x117b c0x0000 (---------------) + servebbs 0x000d75c8, // n0x117c c0x0000 (---------------) + serveftp 0x00097e08, // n0x117d c0x0000 (---------------) + thruhere 0x000000c2, // n0x117e c0x0000 (---------------) + uk 0x0003eb86, // n0x117f c0x0000 (---------------) + webhop 0x00003e02, // n0x1180 c0x0000 (---------------) + za 0x336ced44, // n0x1181 c0x00cd (n0x1183-n0x1185) o I prod 0x33a33bc3, // n0x1182 c0x00ce (n0x1185-n0x1188) o I ssl 0x00000141, // n0x1183 c0x0000 (---------------) + a 0x00014306, // n0x1184 c0x0000 (---------------) + global 0x00000141, // n0x1185 c0x0000 (---------------) + a 0x00000001, // n0x1186 c0x0000 (---------------) + b 0x00014306, // n0x1187 c0x0000 (---------------) + global 0x00214704, // n0x1188 c0x0000 (---------------) + I arts 0x002370c3, // n0x1189 c0x0000 (---------------) + I com 0x00248344, // n0x118a c0x0000 (---------------) + I firm 0x00214b84, // n0x118b c0x0000 (---------------) + I info 0x0024bdc3, // n0x118c c0x0000 (---------------) + I net 0x002339c5, // n0x118d c0x0000 (---------------) + I other 0x0023da83, // n0x118e c0x0000 (---------------) + I per 0x00229ac3, // n0x118f c0x0000 (---------------) + I rec 0x002e3245, // n0x1190 c0x0000 (---------------) + I store 0x00200b03, // n0x1191 c0x0000 (---------------) + I web 0x002370c3, // n0x1192 c0x0000 (---------------) + I com 0x00215b83, // n0x1193 c0x0000 (---------------) + I edu 0x00208ec3, // n0x1194 c0x0000 (---------------) + I gov 0x00210703, // n0x1195 c0x0000 (---------------) + I mil 0x0020d4c4, // n0x1196 c0x0000 (---------------) + I mobi 0x002592c4, // n0x1197 c0x0000 (---------------) + I name 0x0024bdc3, // n0x1198 c0x0000 (---------------) + I net 0x0024af03, // n0x1199 c0x0000 (---------------) + I org 0x0025e643, // n0x119a c0x0000 (---------------) + I sch 0x0012d948, // n0x119b c0x0000 (---------------) + blogspot 0x0022c0c2, // n0x119c c0x0000 (---------------) + I bv 0x00009382, // n0x119d c0x0000 (---------------) + co 0x34e04782, // n0x119e c0x00d3 (n0x1474-n0x1475) + I aa 0x00313888, // n0x119f c0x0000 (---------------) + I aarborte 0x00204c06, // n0x11a0 c0x0000 (---------------) + I aejrie 0x0028f3c6, // n0x11a1 c0x0000 (---------------) + I afjord 0x00204587, // n0x11a2 c0x0000 (---------------) + I agdenes 0x352226c2, // n0x11a3 c0x00d4 (n0x1475-n0x1476) + I ah 0x356a3048, // n0x11a4 c0x00d5 (n0x1476-n0x1477) o I akershus 0x0022758a, // n0x11a5 c0x0000 (---------------) + I aknoluokta 0x0020f9c8, // n0x11a6 c0x0000 (---------------) + I akrehamn 0x00201782, // n0x11a7 c0x0000 (---------------) + I al 0x002eae09, // n0x11a8 c0x0000 (---------------) + I alaheadju 0x0026a947, // n0x11a9 c0x0000 (---------------) + I alesund 0x002131c6, // n0x11aa c0x0000 (---------------) + I algard 0x002df989, // n0x11ab c0x0000 (---------------) + I alstahaug 0x00240c84, // n0x11ac c0x0000 (---------------) + I alta 0x002eaa06, // n0x11ad c0x0000 (---------------) + I alvdal 0x002969c4, // n0x11ae c0x0000 (---------------) + I amli 0x00213e44, // n0x11af c0x0000 (---------------) + I amot 0x002504c9, // n0x11b0 c0x0000 (---------------) + I andasuolo 0x00310606, // n0x11b1 c0x0000 (---------------) + I andebu 0x0022ab85, // n0x11b2 c0x0000 (---------------) + I andoy 0x0020ac45, // n0x11b3 c0x0000 (---------------) + I ardal 0x002ba6c7, // n0x11b4 c0x0000 (---------------) + I aremark 0x0025a807, // n0x11b5 c0x0000 (---------------) + I arendal 0x00230944, // n0x11b6 c0x0000 (---------------) + I arna 0x002047c6, // n0x11b7 c0x0000 (---------------) + I aseral 0x00269805, // n0x11b8 c0x0000 (---------------) + I asker 0x002018c5, // n0x11b9 c0x0000 (---------------) + I askim 0x002e5605, // n0x11ba c0x0000 (---------------) + I askoy 0x00263c47, // n0x11bb c0x0000 (---------------) + I askvoll 0x00346945, // n0x11bc c0x0000 (---------------) + I asnes 0x002ff189, // n0x11bd c0x0000 (---------------) + I audnedaln 0x00235d85, // n0x11be c0x0000 (---------------) + I aukra 0x00309884, // n0x11bf c0x0000 (---------------) + I aure 0x0034ce07, // n0x11c0 c0x0000 (---------------) + I aurland 0x0031034e, // n0x11c1 c0x0000 (---------------) + I aurskog-holand 0x00281009, // n0x11c2 c0x0000 (---------------) + I austevoll 0x002af049, // n0x11c3 c0x0000 (---------------) + I austrheim 0x0030a906, // n0x11c4 c0x0000 (---------------) + I averoy 0x0030ddc8, // n0x11c5 c0x0000 (---------------) + I badaddja 0x0033dccb, // n0x11c6 c0x0000 (---------------) + I bahcavuotna 0x0029c60c, // n0x11c7 c0x0000 (---------------) + I bahccavuotna 0x00293286, // n0x11c8 c0x0000 (---------------) + I baidar 0x003371c7, // n0x11c9 c0x0000 (---------------) + I bajddar 0x002143c5, // n0x11ca c0x0000 (---------------) + I balat 0x00204fca, // n0x11cb c0x0000 (---------------) + I balestrand 0x0029ad89, // n0x11cc c0x0000 (---------------) + I ballangen 0x002adb89, // n0x11cd c0x0000 (---------------) + I balsfjord 0x002dfd86, // n0x11ce c0x0000 (---------------) + I bamble 0x002e4c05, // n0x11cf c0x0000 (---------------) + I bardu 0x0030ec45, // n0x11d0 c0x0000 (---------------) + I barum 0x0032f389, // n0x11d1 c0x0000 (---------------) + I batsfjord 0x0026a28b, // n0x11d2 c0x0000 (---------------) + I bearalvahki 0x0026d946, // n0x11d3 c0x0000 (---------------) + I beardu 0x00258906, // n0x11d4 c0x0000 (---------------) + I beiarn 0x00205284, // n0x11d5 c0x0000 (---------------) + I berg 0x00249e46, // n0x11d6 c0x0000 (---------------) + I bergen 0x0020a688, // n0x11d7 c0x0000 (---------------) + I berlevag 0x0035b386, // n0x11d8 c0x0000 (---------------) + I bievat 0x00272a86, // n0x11d9 c0x0000 (---------------) + I bindal 0x00209688, // n0x11da c0x0000 (---------------) + I birkenes 0x0020c087, // n0x11db c0x0000 (---------------) + I bjarkoy 0x0020d289, // n0x11dc c0x0000 (---------------) + I bjerkreim 0x0020ea85, // n0x11dd c0x0000 (---------------) + I bjugn 0x0012d948, // n0x11de c0x0000 (---------------) + blogspot 0x002d5b44, // n0x11df c0x0000 (---------------) + I bodo 0x00216904, // n0x11e0 c0x0000 (---------------) + I bokn 0x002120c5, // n0x11e1 c0x0000 (---------------) + I bomlo 0x0021b489, // n0x11e2 c0x0000 (---------------) + I bremanger 0x002213c7, // n0x11e3 c0x0000 (---------------) + I bronnoy 0x002213cb, // n0x11e4 c0x0000 (---------------) + I bronnoysund 0x00221c8a, // n0x11e5 c0x0000 (---------------) + I brumunddal 0x00229785, // n0x11e6 c0x0000 (---------------) + I bryne 0x35a0ef42, // n0x11e7 c0x00d6 (n0x1477-n0x1478) + I bu 0x00310707, // n0x11e8 c0x0000 (---------------) + I budejju 0x35e2a608, // n0x11e9 c0x00d7 (n0x1478-n0x1479) o I buskerud 0x002c5447, // n0x11ea c0x0000 (---------------) + I bygland 0x002c5245, // n0x11eb c0x0000 (---------------) + I bykle 0x002561ca, // n0x11ec c0x0000 (---------------) + I cahcesuolo 0x00009382, // n0x11ed c0x0000 (---------------) + co 0x00359c0b, // n0x11ee c0x0000 (---------------) + I davvenjarga 0x00203b8a, // n0x11ef c0x0000 (---------------) + I davvesiida 0x0034ddc6, // n0x11f0 c0x0000 (---------------) + I deatnu 0x00233903, // n0x11f1 c0x0000 (---------------) + I dep 0x002f330d, // n0x11f2 c0x0000 (---------------) + I dielddanuorri 0x00301b0c, // n0x11f3 c0x0000 (---------------) + I divtasvuodna 0x002add8d, // n0x11f4 c0x0000 (---------------) + I divttasvuotna 0x002ac705, // n0x11f5 c0x0000 (---------------) + I donna 0x002d63c5, // n0x11f6 c0x0000 (---------------) + I dovre 0x00262987, // n0x11f7 c0x0000 (---------------) + I drammen 0x0020f009, // n0x11f8 c0x0000 (---------------) + I drangedal 0x0020f8c6, // n0x11f9 c0x0000 (---------------) + I drobak 0x00234c85, // n0x11fa c0x0000 (---------------) + I dyroy 0x00234ec8, // n0x11fb c0x0000 (---------------) + I egersund 0x0027d503, // n0x11fc c0x0000 (---------------) + I eid 0x0033d708, // n0x11fd c0x0000 (---------------) + I eidfjord 0x0027d508, // n0x11fe c0x0000 (---------------) + I eidsberg 0x002b5cc7, // n0x11ff c0x0000 (---------------) + I eidskog 0x00292088, // n0x1200 c0x0000 (---------------) + I eidsvoll 0x00256989, // n0x1201 c0x0000 (---------------) + I eigersund 0x002d6887, // n0x1202 c0x0000 (---------------) + I elverum 0x002b4a07, // n0x1203 c0x0000 (---------------) + I enebakk 0x002b42c8, // n0x1204 c0x0000 (---------------) + I engerdal 0x0024be04, // n0x1205 c0x0000 (---------------) + I etne 0x0024be07, // n0x1206 c0x0000 (---------------) + I etnedal 0x00205e08, // n0x1207 c0x0000 (---------------) + I evenassi 0x0020db06, // n0x1208 c0x0000 (---------------) + I evenes 0x0021728f, // n0x1209 c0x0000 (---------------) + I evje-og-hornnes 0x00284347, // n0x120a c0x0000 (---------------) + I farsund 0x002c8a86, // n0x120b c0x0000 (---------------) + I fauske 0x0022bd05, // n0x120c c0x0000 (---------------) + I fedje 0x002f3703, // n0x120d c0x0000 (---------------) + I fet 0x0033bf87, // n0x120e c0x0000 (---------------) + I fetsund 0x0033f8c3, // n0x120f c0x0000 (---------------) + I fhs 0x00248006, // n0x1210 c0x0000 (---------------) + I finnoy 0x00248c86, // n0x1211 c0x0000 (---------------) + I fitjar 0x00249846, // n0x1212 c0x0000 (---------------) + I fjaler 0x00280dc5, // n0x1213 c0x0000 (---------------) + I fjell 0x00259c43, // n0x1214 c0x0000 (---------------) + I fla 0x00259c48, // n0x1215 c0x0000 (---------------) + I flakstad 0x0033afc9, // n0x1216 c0x0000 (---------------) + I flatanger 0x00249a8b, // n0x1217 c0x0000 (---------------) + I flekkefjord 0x00249d48, // n0x1218 c0x0000 (---------------) + I flesberg 0x0024bfc5, // n0x1219 c0x0000 (---------------) + I flora 0x0024cb45, // n0x121a c0x0000 (---------------) + I floro 0x3633ee02, // n0x121b c0x00d8 (n0x1479-n0x147a) + I fm 0x0023ee89, // n0x121c c0x0000 (---------------) + I folkebibl 0x0024cf87, // n0x121d c0x0000 (---------------) + I folldal 0x0034dd05, // n0x121e c0x0000 (---------------) + I forde 0x002503c7, // n0x121f c0x0000 (---------------) + I forsand 0x00251e86, // n0x1220 c0x0000 (---------------) + I fosnes 0x0032a605, // n0x1221 c0x0000 (---------------) + I frana 0x0025260b, // n0x1222 c0x0000 (---------------) + I fredrikstad 0x00253044, // n0x1223 c0x0000 (---------------) + I frei 0x002548c5, // n0x1224 c0x0000 (---------------) + I frogn 0x00254a07, // n0x1225 c0x0000 (---------------) + I froland 0x00269d46, // n0x1226 c0x0000 (---------------) + I frosta 0x0026a045, // n0x1227 c0x0000 (---------------) + I froya 0x00274247, // n0x1228 c0x0000 (---------------) + I fuoisku 0x00274607, // n0x1229 c0x0000 (---------------) + I fuossko 0x00272784, // n0x122a c0x0000 (---------------) + I fusa 0x0027794a, // n0x122b c0x0000 (---------------) + I fylkesbibl 0x00277e08, // n0x122c c0x0000 (---------------) + I fyresdal 0x00315009, // n0x122d c0x0000 (---------------) + I gaivuotna 0x00359e45, // n0x122e c0x0000 (---------------) + I galsa 0x0021c646, // n0x122f c0x0000 (---------------) + I gamvik 0x0020a84a, // n0x1230 c0x0000 (---------------) + I gangaviika 0x0020ab46, // n0x1231 c0x0000 (---------------) + I gaular 0x0025a287, // n0x1232 c0x0000 (---------------) + I gausdal 0x0034ef0d, // n0x1233 c0x0000 (---------------) + I giehtavuoatna 0x003309c9, // n0x1234 c0x0000 (---------------) + I gildeskal 0x00332f45, // n0x1235 c0x0000 (---------------) + I giske 0x002f1487, // n0x1236 c0x0000 (---------------) + I gjemnes 0x00305448, // n0x1237 c0x0000 (---------------) + I gjerdrum 0x00305e48, // n0x1238 c0x0000 (---------------) + I gjerstad 0x00306687, // n0x1239 c0x0000 (---------------) + I gjesdal 0x0035c6c6, // n0x123a c0x0000 (---------------) + I gjovik 0x00226b07, // n0x123b c0x0000 (---------------) + I gloppen 0x002aec03, // n0x123c c0x0000 (---------------) + I gol 0x00303144, // n0x123d c0x0000 (---------------) + I gran 0x00308ec5, // n0x123e c0x0000 (---------------) + I grane 0x00328447, // n0x123f c0x0000 (---------------) + I granvin 0x00341fc9, // n0x1240 c0x0000 (---------------) + I gratangen 0x0022f608, // n0x1241 c0x0000 (---------------) + I grimstad 0x002314c5, // n0x1242 c0x0000 (---------------) + I grong 0x002467c4, // n0x1243 c0x0000 (---------------) + I grue 0x0033fd05, // n0x1244 c0x0000 (---------------) + I gulen 0x0024af8d, // n0x1245 c0x0000 (---------------) + I guovdageaidnu 0x002023c2, // n0x1246 c0x0000 (---------------) + I ha 0x0029fd06, // n0x1247 c0x0000 (---------------) + I habmer 0x0030c186, // n0x1248 c0x0000 (---------------) + I hadsel 0x0031db4a, // n0x1249 c0x0000 (---------------) + I hagebostad 0x002789c6, // n0x124a c0x0000 (---------------) + I halden 0x00278b45, // n0x124b c0x0000 (---------------) + I halsa 0x00314b85, // n0x124c c0x0000 (---------------) + I hamar 0x00314b87, // n0x124d c0x0000 (---------------) + I hamaroy 0x00279bcc, // n0x124e c0x0000 (---------------) + I hammarfeasta 0x0022034a, // n0x124f c0x0000 (---------------) + I hammerfest 0x0027c2c6, // n0x1250 c0x0000 (---------------) + I hapmir 0x00296905, // n0x1251 c0x0000 (---------------) + I haram 0x0027d446, // n0x1252 c0x0000 (---------------) + I hareid 0x0027da87, // n0x1253 c0x0000 (---------------) + I harstad 0x0027ea86, // n0x1254 c0x0000 (---------------) + I hasvik 0x00280ccc, // n0x1255 c0x0000 (---------------) + I hattfjelldal 0x002dfac9, // n0x1256 c0x0000 (---------------) + I haugesund 0x3664e307, // n0x1257 c0x00d9 (n0x147a-n0x147d) o I hedmark 0x00236c85, // n0x1258 c0x0000 (---------------) + I hemne 0x00236c86, // n0x1259 c0x0000 (---------------) + I hemnes 0x00282748, // n0x125a c0x0000 (---------------) + I hemsedal 0x002638c5, // n0x125b c0x0000 (---------------) + I herad 0x002926c5, // n0x125c c0x0000 (---------------) + I hitra 0x00292908, // n0x125d c0x0000 (---------------) + I hjartdal 0x00292b0a, // n0x125e c0x0000 (---------------) + I hjelmeland 0x36a668c2, // n0x125f c0x00da (n0x147d-n0x147e) + I hl 0x36e7c582, // n0x1260 c0x00db (n0x147e-n0x147f) + I hm 0x002d2485, // n0x1261 c0x0000 (---------------) + I hobol 0x002c8a03, // n0x1262 c0x0000 (---------------) + I hof 0x00211588, // n0x1263 c0x0000 (---------------) + I hokksund 0x00292d83, // n0x1264 c0x0000 (---------------) + I hol 0x00293404, // n0x1265 c0x0000 (---------------) + I hole 0x002e2b0b, // n0x1266 c0x0000 (---------------) + I holmestrand 0x00312148, // n0x1267 c0x0000 (---------------) + I holtalen 0x002982c8, // n0x1268 c0x0000 (---------------) + I honefoss 0x372e0449, // n0x1269 c0x00dc (n0x147f-n0x1480) o I hordaland 0x00299549, // n0x126a c0x0000 (---------------) + I hornindal 0x00299e86, // n0x126b c0x0000 (---------------) + I horten 0x0029afc8, // n0x126c c0x0000 (---------------) + I hoyanger 0x0029b1c9, // n0x126d c0x0000 (---------------) + I hoylandet 0x0029bac6, // n0x126e c0x0000 (---------------) + I hurdal 0x0029bc45, // n0x126f c0x0000 (---------------) + I hurum 0x00246906, // n0x1270 c0x0000 (---------------) + I hvaler 0x0022db49, // n0x1271 c0x0000 (---------------) + I hyllestad 0x003300c7, // n0x1272 c0x0000 (---------------) + I ibestad 0x0020c406, // n0x1273 c0x0000 (---------------) + I idrett 0x002e6d87, // n0x1274 c0x0000 (---------------) + I inderoy 0x00301987, // n0x1275 c0x0000 (---------------) + I iveland 0x002570c4, // n0x1276 c0x0000 (---------------) + I ivgu 0x37618749, // n0x1277 c0x00dd (n0x1480-n0x1481) + I jan-mayen 0x002bbe48, // n0x1278 c0x0000 (---------------) + I jessheim 0x00262ec8, // n0x1279 c0x0000 (---------------) + I jevnaker 0x0033cfc7, // n0x127a c0x0000 (---------------) + I jolster 0x002b92c6, // n0x127b c0x0000 (---------------) + I jondal 0x002515c9, // n0x127c c0x0000 (---------------) + I jorpeland 0x0028f387, // n0x127d c0x0000 (---------------) + I kafjord 0x0031ad8a, // n0x127e c0x0000 (---------------) + I karasjohka 0x002ecf48, // n0x127f c0x0000 (---------------) + I karasjok 0x00205807, // n0x1280 c0x0000 (---------------) + I karlsoy 0x002aac86, // n0x1281 c0x0000 (---------------) + I karmoy 0x00230c0a, // n0x1282 c0x0000 (---------------) + I kautokeino 0x0022df88, // n0x1283 c0x0000 (---------------) + I kirkenes 0x002d4585, // n0x1284 c0x0000 (---------------) + I klabu 0x0026edc5, // n0x1285 c0x0000 (---------------) + I klepp 0x002bf247, // n0x1286 c0x0000 (---------------) + I kommune 0x002d0309, // n0x1287 c0x0000 (---------------) + I kongsberg 0x002da18b, // n0x1288 c0x0000 (---------------) + I kongsvinger 0x002f2808, // n0x1289 c0x0000 (---------------) + I kopervik 0x00235e09, // n0x128a c0x0000 (---------------) + I kraanghke 0x002a2cc7, // n0x128b c0x0000 (---------------) + I kragero 0x002a464c, // n0x128c c0x0000 (---------------) + I kristiansand 0x002a4acc, // n0x128d c0x0000 (---------------) + I kristiansund 0x002a4dca, // n0x128e c0x0000 (---------------) + I krodsherad 0x002a504c, // n0x128f c0x0000 (---------------) + I krokstadelva 0x002b2c08, // n0x1290 c0x0000 (---------------) + I kvafjord 0x002b2e08, // n0x1291 c0x0000 (---------------) + I kvalsund 0x002b3004, // n0x1292 c0x0000 (---------------) + I kvam 0x002b5449, // n0x1293 c0x0000 (---------------) + I kvanangen 0x002b5689, // n0x1294 c0x0000 (---------------) + I kvinesdal 0x002b58ca, // n0x1295 c0x0000 (---------------) + I kvinnherad 0x002b5b49, // n0x1296 c0x0000 (---------------) + I kviteseid 0x002b5e87, // n0x1297 c0x0000 (---------------) + I kvitsoy 0x00356acc, // n0x1298 c0x0000 (---------------) + I laakesvuemie 0x0025cb46, // n0x1299 c0x0000 (---------------) + I lahppi 0x0030af48, // n0x129a c0x0000 (---------------) + I langevag 0x0020ac06, // n0x129b c0x0000 (---------------) + I lardal 0x00326986, // n0x129c c0x0000 (---------------) + I larvik 0x00332e47, // n0x129d c0x0000 (---------------) + I lavagis 0x00333f48, // n0x129e c0x0000 (---------------) + I lavangen 0x002dfe8b, // n0x129f c0x0000 (---------------) + I leangaviika 0x002c5307, // n0x12a0 c0x0000 (---------------) + I lebesby 0x00252e09, // n0x12a1 c0x0000 (---------------) + I leikanger 0x0026ad09, // n0x12a2 c0x0000 (---------------) + I leirfjord 0x002b6d87, // n0x12a3 c0x0000 (---------------) + I leirvik 0x0021b204, // n0x12a4 c0x0000 (---------------) + I leka 0x002d4407, // n0x12a5 c0x0000 (---------------) + I leksvik 0x0025a986, // n0x12a6 c0x0000 (---------------) + I lenvik 0x00249906, // n0x12a7 c0x0000 (---------------) + I lerdal 0x00225405, // n0x12a8 c0x0000 (---------------) + I lesja 0x002413c8, // n0x12a9 c0x0000 (---------------) + I levanger 0x00296a44, // n0x12aa c0x0000 (---------------) + I lier 0x00296a46, // n0x12ab c0x0000 (---------------) + I lierne 0x0022020b, // n0x12ac c0x0000 (---------------) + I lillehammer 0x002d7e09, // n0x12ad c0x0000 (---------------) + I lillesand 0x002017c6, // n0x12ae c0x0000 (---------------) + I lindas 0x00203389, // n0x12af c0x0000 (---------------) + I lindesnes 0x00212186, // n0x12b0 c0x0000 (---------------) + I loabat 0x00250688, // n0x12b1 c0x0000 (---------------) + I lodingen 0x002ed283, // n0x12b2 c0x0000 (---------------) + I lom 0x002c4185, // n0x12b3 c0x0000 (---------------) + I loppa 0x002cb189, // n0x12b4 c0x0000 (---------------) + I lorenskog 0x00221ec5, // n0x12b5 c0x0000 (---------------) + I loten 0x002db304, // n0x12b6 c0x0000 (---------------) + I lund 0x00269346, // n0x12b7 c0x0000 (---------------) + I lunner 0x0022c985, // n0x12b8 c0x0000 (---------------) + I luroy 0x0022fc46, // n0x12b9 c0x0000 (---------------) + I luster 0x002eed47, // n0x12ba c0x0000 (---------------) + I lyngdal 0x0030e7c6, // n0x12bb c0x0000 (---------------) + I lyngen 0x0028808b, // n0x12bc c0x0000 (---------------) + I malatvuopmi 0x0029ca87, // n0x12bd c0x0000 (---------------) + I malselv 0x002056c6, // n0x12be c0x0000 (---------------) + I malvik 0x00227dc6, // n0x12bf c0x0000 (---------------) + I mandal 0x002ba786, // n0x12c0 c0x0000 (---------------) + I marker 0x00230909, // n0x12c1 c0x0000 (---------------) + I marnardal 0x0028e28a, // n0x12c2 c0x0000 (---------------) + I masfjorden 0x002d2cc5, // n0x12c3 c0x0000 (---------------) + I masoy 0x002bc90d, // n0x12c4 c0x0000 (---------------) + I matta-varjjat 0x00292c06, // n0x12c5 c0x0000 (---------------) + I meland 0x002d3a86, // n0x12c6 c0x0000 (---------------) + I meldal 0x0023c786, // n0x12c7 c0x0000 (---------------) + I melhus 0x00202f05, // n0x12c8 c0x0000 (---------------) + I meloy 0x002a2f87, // n0x12c9 c0x0000 (---------------) + I meraker 0x0025ef07, // n0x12ca c0x0000 (---------------) + I midsund 0x002c07ce, // n0x12cb c0x0000 (---------------) + I midtre-gauldal 0x00210703, // n0x12cc c0x0000 (---------------) + I mil 0x002b9289, // n0x12cd c0x0000 (---------------) + I mjondalen 0x003148c9, // n0x12ce c0x0000 (---------------) + I mo-i-rana 0x002053c7, // n0x12cf c0x0000 (---------------) + I moareke 0x0025f3c7, // n0x12d0 c0x0000 (---------------) + I modalen 0x00202e05, // n0x12d1 c0x0000 (---------------) + I modum 0x002a1a45, // n0x12d2 c0x0000 (---------------) + I molde 0x37aaa78f, // n0x12d3 c0x00de (n0x1481-n0x1483) o I more-og-romsdal 0x002be2c7, // n0x12d4 c0x0000 (---------------) + I mosjoen 0x002be488, // n0x12d5 c0x0000 (---------------) + I moskenes 0x002bed04, // n0x12d6 c0x0000 (---------------) + I moss 0x002bf106, // n0x12d7 c0x0000 (---------------) + I mosvik 0x37ec5802, // n0x12d8 c0x00df (n0x1483-n0x1484) + I mr 0x002c1986, // n0x12d9 c0x0000 (---------------) + I muosat 0x002c3946, // n0x12da c0x0000 (---------------) + I museum 0x0034f1ce, // n0x12db c0x0000 (---------------) + I naamesjevuemie 0x0033d54a, // n0x12dc c0x0000 (---------------) + I namdalseid 0x0027efc6, // n0x12dd c0x0000 (---------------) + I namsos 0x002f9b4a, // n0x12de c0x0000 (---------------) + I namsskogan 0x0025d689, // n0x12df c0x0000 (---------------) + I nannestad 0x00348185, // n0x12e0 c0x0000 (---------------) + I naroy 0x00312a88, // n0x12e1 c0x0000 (---------------) + I narviika 0x0032fcc6, // n0x12e2 c0x0000 (---------------) + I narvik 0x00201608, // n0x12e3 c0x0000 (---------------) + I naustdal 0x00207488, // n0x12e4 c0x0000 (---------------) + I navuotna 0x002b138b, // n0x12e5 c0x0000 (---------------) + I nedre-eiker 0x00204685, // n0x12e6 c0x0000 (---------------) + I nesna 0x003469c8, // n0x12e7 c0x0000 (---------------) + I nesodden 0x002097cc, // n0x12e8 c0x0000 (---------------) + I nesoddtangen 0x002d4747, // n0x12e9 c0x0000 (---------------) + I nesseby 0x0020dbc6, // n0x12ea c0x0000 (---------------) + I nesset 0x0022a288, // n0x12eb c0x0000 (---------------) + I nissedal 0x002eac88, // n0x12ec c0x0000 (---------------) + I nittedal 0x38236642, // n0x12ed c0x00e0 (n0x1484-n0x1485) + I nl 0x0023930b, // n0x12ee c0x0000 (---------------) + I nord-aurdal 0x002e5809, // n0x12ef c0x0000 (---------------) + I nord-fron 0x002ea849, // n0x12f0 c0x0000 (---------------) + I nord-odal 0x00306f07, // n0x12f1 c0x0000 (---------------) + I norddal 0x00253948, // n0x12f2 c0x0000 (---------------) + I nordkapp 0x386ec248, // n0x12f3 c0x00e1 (n0x1485-n0x1489) o I nordland 0x0023e3cb, // n0x12f4 c0x0000 (---------------) + I nordre-land 0x00258349, // n0x12f5 c0x0000 (---------------) + I nordreisa 0x00232fcd, // n0x12f6 c0x0000 (---------------) + I nore-og-uvdal 0x0035bec8, // n0x12f7 c0x0000 (---------------) + I notodden 0x00335948, // n0x12f8 c0x0000 (---------------) + I notteroy 0x38a11402, // n0x12f9 c0x00e2 (n0x1489-n0x148a) + I nt 0x0031bb04, // n0x12fa c0x0000 (---------------) + I odda 0x38e37a82, // n0x12fb c0x00e3 (n0x148a-n0x148b) + I of 0x002790c6, // n0x12fc c0x0000 (---------------) + I oksnes 0x39201082, // n0x12fd c0x00e4 (n0x148b-n0x148c) + I ol 0x0035bbca, // n0x12fe c0x0000 (---------------) + I omasvuotna 0x0022fb06, // n0x12ff c0x0000 (---------------) + I oppdal 0x00225e48, // n0x1300 c0x0000 (---------------) + I oppegard 0x00314688, // n0x1301 c0x0000 (---------------) + I orkanger 0x00225646, // n0x1302 c0x0000 (---------------) + I orkdal 0x002646c6, // n0x1303 c0x0000 (---------------) + I orland 0x002d1486, // n0x1304 c0x0000 (---------------) + I orskog 0x0026d105, // n0x1305 c0x0000 (---------------) + I orsta 0x00210bc4, // n0x1306 c0x0000 (---------------) + I osen 0x396c4104, // n0x1307 c0x00e5 (n0x148c-n0x148d) + I oslo 0x002a76c6, // n0x1308 c0x0000 (---------------) + I osoyro 0x002e0b87, // n0x1309 c0x0000 (---------------) + I osteroy 0x39ae41c7, // n0x130a c0x00e6 (n0x148d-n0x148e) o I ostfold 0x0024a78b, // n0x130b c0x0000 (---------------) + I ostre-toten 0x0027b789, // n0x130c c0x0000 (---------------) + I overhalla 0x002d640a, // n0x130d c0x0000 (---------------) + I ovre-eiker 0x00314cc4, // n0x130e c0x0000 (---------------) + I oyer 0x002af5c8, // n0x130f c0x0000 (---------------) + I oygarden 0x0020c1cd, // n0x1310 c0x0000 (---------------) + I oystre-slidre 0x002cc989, // n0x1311 c0x0000 (---------------) + I porsanger 0x002ccbc8, // n0x1312 c0x0000 (---------------) + I porsangu 0x002cd149, // n0x1313 c0x0000 (---------------) + I porsgrunn 0x002ce944, // n0x1314 c0x0000 (---------------) + I priv 0x002599c4, // n0x1315 c0x0000 (---------------) + I rade 0x00222d05, // n0x1316 c0x0000 (---------------) + I radoy 0x003134cb, // n0x1317 c0x0000 (---------------) + I rahkkeravju 0x003120c6, // n0x1318 c0x0000 (---------------) + I raholt 0x002a7505, // n0x1319 c0x0000 (---------------) + I raisa 0x0021e089, // n0x131a c0x0000 (---------------) + I rakkestad 0x00204888, // n0x131b c0x0000 (---------------) + I ralingen 0x0027bcc4, // n0x131c c0x0000 (---------------) + I rana 0x00205149, // n0x131d c0x0000 (---------------) + I randaberg 0x002b1145, // n0x131e c0x0000 (---------------) + I rauma 0x0025a848, // n0x131f c0x0000 (---------------) + I rendalen 0x00249007, // n0x1320 c0x0000 (---------------) + I rennebu 0x002af448, // n0x1321 c0x0000 (---------------) + I rennesoy 0x002b8e86, // n0x1322 c0x0000 (---------------) + I rindal 0x002cf487, // n0x1323 c0x0000 (---------------) + I ringebu 0x002fbe89, // n0x1324 c0x0000 (---------------) + I ringerike 0x00221009, // n0x1325 c0x0000 (---------------) + I ringsaker 0x0026ef45, // n0x1326 c0x0000 (---------------) + I risor 0x002f35c5, // n0x1327 c0x0000 (---------------) + I rissa 0x39e05882, // n0x1328 c0x00e7 (n0x148e-n0x148f) + I rl 0x00309644, // n0x1329 c0x0000 (---------------) + I roan 0x00238585, // n0x132a c0x0000 (---------------) + I rodoy 0x00204486, // n0x132b c0x0000 (---------------) + I rollag 0x00324d05, // n0x132c c0x0000 (---------------) + I romsa 0x00234607, // n0x132d c0x0000 (---------------) + I romskog 0x00258d85, // n0x132e c0x0000 (---------------) + I roros 0x0021b084, // n0x132f c0x0000 (---------------) + I rost 0x002e0c86, // n0x1330 c0x0000 (---------------) + I royken 0x0030a9c7, // n0x1331 c0x0000 (---------------) + I royrvik 0x002c5846, // n0x1332 c0x0000 (---------------) + I ruovat 0x0023aa05, // n0x1333 c0x0000 (---------------) + I rygge 0x00217608, // n0x1334 c0x0000 (---------------) + I salangen 0x00219705, // n0x1335 c0x0000 (---------------) + I salat 0x0021fbc7, // n0x1336 c0x0000 (---------------) + I saltdal 0x00261189, // n0x1337 c0x0000 (---------------) + I samnanger 0x002a484a, // n0x1338 c0x0000 (---------------) + I sandefjord 0x002786c7, // n0x1339 c0x0000 (---------------) + I sandnes 0x002786cc, // n0x133a c0x0000 (---------------) + I sandnessjoen 0x0022ab46, // n0x133b c0x0000 (---------------) + I sandoy 0x0024ad89, // n0x133c c0x0000 (---------------) + I sarpsborg 0x0025f705, // n0x133d c0x0000 (---------------) + I sauda 0x00263808, // n0x133e c0x0000 (---------------) + I sauherad 0x0020de83, // n0x133f c0x0000 (---------------) + I sel 0x003052c5, // n0x1340 c0x0000 (---------------) + I selbu 0x00327e85, // n0x1341 c0x0000 (---------------) + I selje 0x0020de87, // n0x1342 c0x0000 (---------------) + I seljord 0x3a20ee42, // n0x1343 c0x00e8 (n0x148f-n0x1490) + I sf 0x0021ba07, // n0x1344 c0x0000 (---------------) + I siellak 0x002a9346, // n0x1345 c0x0000 (---------------) + I sigdal 0x00218686, // n0x1346 c0x0000 (---------------) + I siljan 0x002da6c6, // n0x1347 c0x0000 (---------------) + I sirdal 0x002eabc6, // n0x1348 c0x0000 (---------------) + I skanit 0x00236588, // n0x1349 c0x0000 (---------------) + I skanland 0x00291ec5, // n0x134a c0x0000 (---------------) + I skaun 0x002c8b47, // n0x134b c0x0000 (---------------) + I skedsmo 0x002c8b4d, // n0x134c c0x0000 (---------------) + I skedsmokorset 0x00201903, // n0x134d c0x0000 (---------------) + I ski 0x0021ec85, // n0x134e c0x0000 (---------------) + I skien 0x002f2387, // n0x134f c0x0000 (---------------) + I skierva 0x002b3188, // n0x1350 c0x0000 (---------------) + I skiptvet 0x002274c5, // n0x1351 c0x0000 (---------------) + I skjak 0x002264c8, // n0x1352 c0x0000 (---------------) + I skjervoy 0x002942c6, // n0x1353 c0x0000 (---------------) + I skodje 0x00233c07, // n0x1354 c0x0000 (---------------) + I slattum 0x00292f45, // n0x1355 c0x0000 (---------------) + I smola 0x00204706, // n0x1356 c0x0000 (---------------) + I snaase 0x002b0245, // n0x1357 c0x0000 (---------------) + I snasa 0x00284fca, // n0x1358 c0x0000 (---------------) + I snillfjord 0x002b1d86, // n0x1359 c0x0000 (---------------) + I snoasa 0x0026c047, // n0x135a c0x0000 (---------------) + I sogndal 0x00275e45, // n0x135b c0x0000 (---------------) + I sogne 0x002fd647, // n0x135c c0x0000 (---------------) + I sokndal 0x002bcd04, // n0x135d c0x0000 (---------------) + I sola 0x002db286, // n0x135e c0x0000 (---------------) + I solund 0x002dbec5, // n0x135f c0x0000 (---------------) + I somna 0x0024958b, // n0x1360 c0x0000 (---------------) + I sondre-land 0x0030e049, // n0x1361 c0x0000 (---------------) + I songdalen 0x00257c8a, // n0x1362 c0x0000 (---------------) + I sor-aurdal 0x0026efc8, // n0x1363 c0x0000 (---------------) + I sor-fron 0x002dc888, // n0x1364 c0x0000 (---------------) + I sor-odal 0x002dca8c, // n0x1365 c0x0000 (---------------) + I sor-varanger 0x002dcd87, // n0x1366 c0x0000 (---------------) + I sorfold 0x002dcf48, // n0x1367 c0x0000 (---------------) + I sorreisa 0x002de588, // n0x1368 c0x0000 (---------------) + I sortland 0x002de785, // n0x1369 c0x0000 (---------------) + I sorum 0x002e174a, // n0x136a c0x0000 (---------------) + I spjelkavik 0x002e1cc9, // n0x136b c0x0000 (---------------) + I spydeberg 0x3a600e42, // n0x136c c0x00e9 (n0x1490-n0x1491) + I st 0x002a9b46, // n0x136d c0x0000 (---------------) + I stange 0x0026ab44, // n0x136e c0x0000 (---------------) + I stat 0x0026ab49, // n0x136f c0x0000 (---------------) + I stathelle 0x002a2489, // n0x1370 c0x0000 (---------------) + I stavanger 0x002b6a87, // n0x1371 c0x0000 (---------------) + I stavern 0x00203107, // n0x1372 c0x0000 (---------------) + I steigen 0x00290189, // n0x1373 c0x0000 (---------------) + I steinkjer 0x002e2608, // n0x1374 c0x0000 (---------------) + I stjordal 0x002e260f, // n0x1375 c0x0000 (---------------) + I stjordalshalsen 0x0023d846, // n0x1376 c0x0000 (---------------) + I stokke 0x002e2dcb, // n0x1377 c0x0000 (---------------) + I stor-elvdal 0x002e3085, // n0x1378 c0x0000 (---------------) + I stord 0x002e3087, // n0x1379 c0x0000 (---------------) + I stordal 0x002e34c9, // n0x137a c0x0000 (---------------) + I storfjord 0x002050c6, // n0x137b c0x0000 (---------------) + I strand 0x002050c7, // n0x137c c0x0000 (---------------) + I stranda 0x0026e445, // n0x137d c0x0000 (---------------) + I stryn 0x00238d04, // n0x137e c0x0000 (---------------) + I sula 0x0035b9c6, // n0x137f c0x0000 (---------------) + I suldal 0x00211684, // n0x1380 c0x0000 (---------------) + I sund 0x002b16c7, // n0x1381 c0x0000 (---------------) + I sunndal 0x00307e88, // n0x1382 c0x0000 (---------------) + I surnadal 0x3aae4b08, // n0x1383 c0x00ea (n0x1491-n0x1492) + I svalbard 0x002e4f45, // n0x1384 c0x0000 (---------------) + I sveio 0x002e5087, // n0x1385 c0x0000 (---------------) + I svelvik 0x00299209, // n0x1386 c0x0000 (---------------) + I sykkylven 0x00216404, // n0x1387 c0x0000 (---------------) + I tana 0x002c9408, // n0x1388 c0x0000 (---------------) + I tananger 0x3aed2948, // n0x1389 c0x00eb (n0x1492-n0x1494) o I telemark 0x00246544, // n0x138a c0x0000 (---------------) + I time 0x0023a1c8, // n0x138b c0x0000 (---------------) + I tingvoll 0x0032fc04, // n0x138c c0x0000 (---------------) + I tinn 0x00234a89, // n0x138d c0x0000 (---------------) + I tjeldsund 0x0023c6c5, // n0x138e c0x0000 (---------------) + I tjome 0x3b226782, // n0x138f c0x00ec (n0x1494-n0x1495) + I tm 0x0023d885, // n0x1390 c0x0000 (---------------) + I tokke 0x0021c585, // n0x1391 c0x0000 (---------------) + I tolga 0x002ef848, // n0x1392 c0x0000 (---------------) + I tonsberg 0x0023b2c7, // n0x1393 c0x0000 (---------------) + I torsken 0x3b605102, // n0x1394 c0x00ed (n0x1495-n0x1496) + I tr 0x0027bc85, // n0x1395 c0x0000 (---------------) + I trana 0x002825c6, // n0x1396 c0x0000 (---------------) + I tranby 0x0029b4c6, // n0x1397 c0x0000 (---------------) + I tranoy 0x00309608, // n0x1398 c0x0000 (---------------) + I troandin 0x00332588, // n0x1399 c0x0000 (---------------) + I trogstad 0x00324cc6, // n0x139a c0x0000 (---------------) + I tromsa 0x002bcc06, // n0x139b c0x0000 (---------------) + I tromso 0x00246f89, // n0x139c c0x0000 (---------------) + I trondheim 0x002c2b86, // n0x139d c0x0000 (---------------) + I trysil 0x00351ccb, // n0x139e c0x0000 (---------------) + I tvedestrand 0x002422c5, // n0x139f c0x0000 (---------------) + I tydal 0x00260f06, // n0x13a0 c0x0000 (---------------) + I tynset 0x002f3788, // n0x13a1 c0x0000 (---------------) + I tysfjord 0x002d72c6, // n0x13a2 c0x0000 (---------------) + I tysnes 0x002b8006, // n0x13a3 c0x0000 (---------------) + I tysvar 0x002c9d8a, // n0x13a4 c0x0000 (---------------) + I ullensaker 0x002140ca, // n0x13a5 c0x0000 (---------------) + I ullensvang 0x00266445, // n0x13a6 c0x0000 (---------------) + I ulvik 0x00267787, // n0x13a7 c0x0000 (---------------) + I unjarga 0x002e7d46, // n0x13a8 c0x0000 (---------------) + I utsira 0x3ba03302, // n0x13a9 c0x00ee (n0x1496-n0x1497) + I va 0x002f24c7, // n0x13aa c0x0000 (---------------) + I vaapste 0x0026bf85, // n0x13ab c0x0000 (---------------) + I vadso 0x0020a7c4, // n0x13ac c0x0000 (---------------) + I vaga 0x0020a7c5, // n0x13ad c0x0000 (---------------) + I vagan 0x0030b086, // n0x13ae c0x0000 (---------------) + I vagsoy 0x002a52c7, // n0x13af c0x0000 (---------------) + I vaksdal 0x002180c5, // n0x13b0 c0x0000 (---------------) + I valle 0x00214244, // n0x13b1 c0x0000 (---------------) + I vang 0x002c4348, // n0x13b2 c0x0000 (---------------) + I vanylven 0x002b80c5, // n0x13b3 c0x0000 (---------------) + I vardo 0x002dc547, // n0x13b4 c0x0000 (---------------) + I varggat 0x00323885, // n0x13b5 c0x0000 (---------------) + I varoy 0x00284f05, // n0x13b6 c0x0000 (---------------) + I vefsn 0x00263b84, // n0x13b7 c0x0000 (---------------) + I vega 0x0031a289, // n0x13b8 c0x0000 (---------------) + I vegarshei 0x00269648, // n0x13b9 c0x0000 (---------------) + I vennesla 0x002dc706, // n0x13ba c0x0000 (---------------) + I verdal 0x00288d86, // n0x13bb c0x0000 (---------------) + I verran 0x002e2306, // n0x13bc c0x0000 (---------------) + I vestby 0x3bf54dc8, // n0x13bd c0x00ef (n0x1497-n0x1498) o I vestfold 0x002e7847, // n0x13be c0x0000 (---------------) + I vestnes 0x002e844d, // n0x13bf c0x0000 (---------------) + I vestre-slidre 0x002e8bcc, // n0x13c0 c0x0000 (---------------) + I vestre-toten 0x002e9309, // n0x13c1 c0x0000 (---------------) + I vestvagoy 0x002e9549, // n0x13c2 c0x0000 (---------------) + I vevelstad 0x3c31ffc2, // n0x13c3 c0x00f0 (n0x1498-n0x1499) + I vf 0x00354703, // n0x13c4 c0x0000 (---------------) + I vgs 0x00205783, // n0x13c5 c0x0000 (---------------) + I vik 0x0030aac5, // n0x13c6 c0x0000 (---------------) + I vikna 0x0032854a, // n0x13c7 c0x0000 (---------------) + I vindafjord 0x002fc306, // n0x13c8 c0x0000 (---------------) + I voagat 0x002ed745, // n0x13c9 c0x0000 (---------------) + I volda 0x002ef284, // n0x13ca c0x0000 (---------------) + I voss 0x002ef28b, // n0x13cb c0x0000 (---------------) + I vossevangen 0x002f398c, // n0x13cc c0x0000 (---------------) + I xn--andy-ira 0x002f418c, // n0x13cd c0x0000 (---------------) + I xn--asky-ira 0x002f4495, // n0x13ce c0x0000 (---------------) + I xn--aurskog-hland-jnb 0x002f59cd, // n0x13cf c0x0000 (---------------) + I xn--avery-yua 0x002f624f, // n0x13d0 c0x0000 (---------------) + I xn--bdddj-mrabd 0x002f6612, // n0x13d1 c0x0000 (---------------) + I xn--bearalvhki-y4a 0x002f6a8f, // n0x13d2 c0x0000 (---------------) + I xn--berlevg-jxa 0x002f6e52, // n0x13d3 c0x0000 (---------------) + I xn--bhcavuotna-s4a 0x002f72d3, // n0x13d4 c0x0000 (---------------) + I xn--bhccavuotna-k7a 0x002f778d, // n0x13d5 c0x0000 (---------------) + I xn--bidr-5nac 0x002f7d4d, // n0x13d6 c0x0000 (---------------) + I xn--bievt-0qa 0x002f808e, // n0x13d7 c0x0000 (---------------) + I xn--bjarky-fya 0x002f860e, // n0x13d8 c0x0000 (---------------) + I xn--bjddar-pta 0x002f8b0c, // n0x13d9 c0x0000 (---------------) + I xn--blt-elab 0x002f8e8c, // n0x13da c0x0000 (---------------) + I xn--bmlo-gra 0x002f990b, // n0x13db c0x0000 (---------------) + I xn--bod-2na 0x002fa00e, // n0x13dc c0x0000 (---------------) + I xn--brnny-wuac 0x002fb812, // n0x13dd c0x0000 (---------------) + I xn--brnnysund-m8ac 0x002fc0cc, // n0x13de c0x0000 (---------------) + I xn--brum-voa 0x002fc490, // n0x13df c0x0000 (---------------) + I xn--btsfjord-9za 0x00302a92, // n0x13e0 c0x0000 (---------------) + I xn--davvenjrga-y4a 0x00302f0c, // n0x13e1 c0x0000 (---------------) + I xn--dnna-gra 0x00303a0d, // n0x13e2 c0x0000 (---------------) + I xn--drbak-wua 0x00303d4c, // n0x13e3 c0x0000 (---------------) + I xn--dyry-ira 0x00304051, // n0x13e4 c0x0000 (---------------) + I xn--eveni-0qa01ga 0x0030448d, // n0x13e5 c0x0000 (---------------) + I xn--finny-yua 0x0030808d, // n0x13e6 c0x0000 (---------------) + I xn--fjord-lra 0x003083ca, // n0x13e7 c0x0000 (---------------) + I xn--fl-zia 0x0030864c, // n0x13e8 c0x0000 (---------------) + I xn--flor-jra 0x00308c8c, // n0x13e9 c0x0000 (---------------) + I xn--frde-gra 0x0030900c, // n0x13ea c0x0000 (---------------) + I xn--frna-woa 0x0030998c, // n0x13eb c0x0000 (---------------) + I xn--frya-hra 0x0030bd13, // n0x13ec c0x0000 (---------------) + I xn--ggaviika-8ya47h 0x0030c650, // n0x13ed c0x0000 (---------------) + I xn--gildeskl-g0a 0x0030ca50, // n0x13ee c0x0000 (---------------) + I xn--givuotna-8ya 0x0030d20d, // n0x13ef c0x0000 (---------------) + I xn--gjvik-wua 0x0030d54c, // n0x13f0 c0x0000 (---------------) + I xn--gls-elac 0x0030e509, // n0x13f1 c0x0000 (---------------) + I xn--h-2fa 0x0030f74d, // n0x13f2 c0x0000 (---------------) + I xn--hbmer-xqa 0x0030fa93, // n0x13f3 c0x0000 (---------------) + I xn--hcesuolo-7ya35b 0x00316351, // n0x13f4 c0x0000 (---------------) + I xn--hgebostad-g3a 0x00316793, // n0x13f5 c0x0000 (---------------) + I xn--hmmrfeasta-s4ac 0x0031788f, // n0x13f6 c0x0000 (---------------) + I xn--hnefoss-q1a 0x00317c4c, // n0x13f7 c0x0000 (---------------) + I xn--hobl-ira 0x00317f4f, // n0x13f8 c0x0000 (---------------) + I xn--holtlen-hxa 0x0031830d, // n0x13f9 c0x0000 (---------------) + I xn--hpmir-xqa 0x0031864f, // n0x13fa c0x0000 (---------------) + I xn--hyanger-q1a 0x00318a10, // n0x13fb c0x0000 (---------------) + I xn--hylandet-54a 0x003191ce, // n0x13fc c0x0000 (---------------) + I xn--indery-fya 0x0031c1ce, // n0x13fd c0x0000 (---------------) + I xn--jlster-bya 0x0031c910, // n0x13fe c0x0000 (---------------) + I xn--jrpeland-54a 0x0031cd0d, // n0x13ff c0x0000 (---------------) + I xn--karmy-yua 0x0031d04e, // n0x1400 c0x0000 (---------------) + I xn--kfjord-iua 0x0031d3cc, // n0x1401 c0x0000 (---------------) + I xn--klbu-woa 0x0031d6d3, // n0x1402 c0x0000 (---------------) + I xn--koluokta-7ya57h 0x0031e34e, // n0x1403 c0x0000 (---------------) + I xn--krager-gya 0x0031e910, // n0x1404 c0x0000 (---------------) + I xn--kranghke-b0a 0x0031ed11, // n0x1405 c0x0000 (---------------) + I xn--krdsherad-m8a 0x0031f14f, // n0x1406 c0x0000 (---------------) + I xn--krehamn-dxa 0x0031f513, // n0x1407 c0x0000 (---------------) + I xn--krjohka-hwab49j 0x0031fb4d, // n0x1408 c0x0000 (---------------) + I xn--ksnes-uua 0x0031fe8f, // n0x1409 c0x0000 (---------------) + I xn--kvfjord-nxa 0x0032024e, // n0x140a c0x0000 (---------------) + I xn--kvitsy-fya 0x00320950, // n0x140b c0x0000 (---------------) + I xn--kvnangen-k0a 0x00320d49, // n0x140c c0x0000 (---------------) + I xn--l-1fa 0x00321b50, // n0x140d c0x0000 (---------------) + I xn--laheadju-7ya 0x0032264f, // n0x140e c0x0000 (---------------) + I xn--langevg-jxa 0x00322ccf, // n0x140f c0x0000 (---------------) + I xn--ldingen-q1a 0x00323092, // n0x1410 c0x0000 (---------------) + I xn--leagaviika-52b 0x003243ce, // n0x1411 c0x0000 (---------------) + I xn--lesund-hua 0x0032560d, // n0x1412 c0x0000 (---------------) + I xn--lgrd-poac 0x00325fcd, // n0x1413 c0x0000 (---------------) + I xn--lhppi-xqa 0x0032630d, // n0x1414 c0x0000 (---------------) + I xn--linds-pra 0x00326d0d, // n0x1415 c0x0000 (---------------) + I xn--loabt-0qa 0x0032704d, // n0x1416 c0x0000 (---------------) + I xn--lrdal-sra 0x00327390, // n0x1417 c0x0000 (---------------) + I xn--lrenskog-54a 0x0032778b, // n0x1418 c0x0000 (---------------) + I xn--lt-liac 0x0032820c, // n0x1419 c0x0000 (---------------) + I xn--lten-gra 0x003287cc, // n0x141a c0x0000 (---------------) + I xn--lury-ira 0x00328acc, // n0x141b c0x0000 (---------------) + I xn--mely-ira 0x00328dce, // n0x141c c0x0000 (---------------) + I xn--merker-kua 0x00331c50, // n0x141d c0x0000 (---------------) + I xn--mjndalen-64a 0x00332792, // n0x141e c0x0000 (---------------) + I xn--mlatvuopmi-s4a 0x00332c0b, // n0x141f c0x0000 (---------------) + I xn--mli-tla 0x0033308e, // n0x1420 c0x0000 (---------------) + I xn--mlselv-iua 0x0033340e, // n0x1421 c0x0000 (---------------) + I xn--moreke-jua 0x0033378e, // n0x1422 c0x0000 (---------------) + I xn--mosjen-eya 0x00333d0b, // n0x1423 c0x0000 (---------------) + I xn--mot-tla 0x3c734156, // n0x1424 c0x00f1 (n0x1499-n0x149b) o I xn--mre-og-romsdal-qqb 0x003354cd, // n0x1425 c0x0000 (---------------) + I xn--msy-ula0h 0x00335b54, // n0x1426 c0x0000 (---------------) + I xn--mtta-vrjjat-k7af 0x003361cd, // n0x1427 c0x0000 (---------------) + I xn--muost-0qa 0x00336d15, // n0x1428 c0x0000 (---------------) + I xn--nmesjevuemie-tcba 0x0033808d, // n0x1429 c0x0000 (---------------) + I xn--nry-yla5g 0x003383cf, // n0x142a c0x0000 (---------------) + I xn--nttery-byae 0x00338e8f, // n0x142b c0x0000 (---------------) + I xn--nvuotna-hwa 0x0033b20f, // n0x142c c0x0000 (---------------) + I xn--oppegrd-ixa 0x0033b5ce, // n0x142d c0x0000 (---------------) + I xn--ostery-fya 0x0033ba4d, // n0x142e c0x0000 (---------------) + I xn--osyro-wua 0x0033cb11, // n0x142f c0x0000 (---------------) + I xn--porsgu-sta26f 0x0033e08c, // n0x1430 c0x0000 (---------------) + I xn--rady-ira 0x0033e38c, // n0x1431 c0x0000 (---------------) + I xn--rdal-poa 0x0033e68b, // n0x1432 c0x0000 (---------------) + I xn--rde-ula 0x0033e94c, // n0x1433 c0x0000 (---------------) + I xn--rdy-0nab 0x0033f0cf, // n0x1434 c0x0000 (---------------) + I xn--rennesy-v1a 0x0033f492, // n0x1435 c0x0000 (---------------) + I xn--rhkkervju-01af 0x0033fe4d, // n0x1436 c0x0000 (---------------) + I xn--rholt-mra 0x0034094c, // n0x1437 c0x0000 (---------------) + I xn--risa-5na 0x00340e0c, // n0x1438 c0x0000 (---------------) + I xn--risr-ira 0x0034110d, // n0x1439 c0x0000 (---------------) + I xn--rland-uua 0x0034144f, // n0x143a c0x0000 (---------------) + I xn--rlingen-mxa 0x0034180e, // n0x143b c0x0000 (---------------) + I xn--rmskog-bya 0x00341d8c, // n0x143c c0x0000 (---------------) + I xn--rros-gra 0x0034220d, // n0x143d c0x0000 (---------------) + I xn--rskog-uua 0x0034254b, // n0x143e c0x0000 (---------------) + I xn--rst-0na 0x00342ecc, // n0x143f c0x0000 (---------------) + I xn--rsta-fra 0x0034344d, // n0x1440 c0x0000 (---------------) + I xn--ryken-vua 0x0034378e, // n0x1441 c0x0000 (---------------) + I xn--ryrvik-bya 0x00343b09, // n0x1442 c0x0000 (---------------) + I xn--s-1fa 0x003444d3, // n0x1443 c0x0000 (---------------) + I xn--sandnessjen-ogb 0x0034590d, // n0x1444 c0x0000 (---------------) + I xn--sandy-yua 0x00345c4d, // n0x1445 c0x0000 (---------------) + I xn--seral-lra 0x0034624c, // n0x1446 c0x0000 (---------------) + I xn--sgne-gra 0x0034658e, // n0x1447 c0x0000 (---------------) + I xn--skierv-uta 0x0034714f, // n0x1448 c0x0000 (---------------) + I xn--skjervy-v1a 0x0034750c, // n0x1449 c0x0000 (---------------) + I xn--skjk-soa 0x0034780d, // n0x144a c0x0000 (---------------) + I xn--sknit-yqa 0x00347b4f, // n0x144b c0x0000 (---------------) + I xn--sknland-fxa 0x00347f0c, // n0x144c c0x0000 (---------------) + I xn--slat-5na 0x003482cc, // n0x144d c0x0000 (---------------) + I xn--slt-elab 0x0034868c, // n0x144e c0x0000 (---------------) + I xn--smla-hra 0x0034898c, // n0x144f c0x0000 (---------------) + I xn--smna-gra 0x00348c8d, // n0x1450 c0x0000 (---------------) + I xn--snase-nra 0x00348fd2, // n0x1451 c0x0000 (---------------) + I xn--sndre-land-0cb 0x0034948c, // n0x1452 c0x0000 (---------------) + I xn--snes-poa 0x0034978c, // n0x1453 c0x0000 (---------------) + I xn--snsa-roa 0x00349a91, // n0x1454 c0x0000 (---------------) + I xn--sr-aurdal-l8a 0x00349ecf, // n0x1455 c0x0000 (---------------) + I xn--sr-fron-q1a 0x0034a28f, // n0x1456 c0x0000 (---------------) + I xn--sr-odal-q1a 0x0034a653, // n0x1457 c0x0000 (---------------) + I xn--sr-varanger-ggb 0x0034b44e, // n0x1458 c0x0000 (---------------) + I xn--srfold-bya 0x0034b7cf, // n0x1459 c0x0000 (---------------) + I xn--srreisa-q1a 0x0034bb8c, // n0x145a c0x0000 (---------------) + I xn--srum-gra 0x3cb4be8e, // n0x145b c0x00f2 (n0x149b-n0x149c) o I xn--stfold-9xa 0x0034c20f, // n0x145c c0x0000 (---------------) + I xn--stjrdal-s1a 0x0034c5d6, // n0x145d c0x0000 (---------------) + I xn--stjrdalshalsen-sqb 0x0034d652, // n0x145e c0x0000 (---------------) + I xn--stre-toten-zcb 0x0034e58c, // n0x145f c0x0000 (---------------) + I xn--tjme-hra 0x0035068f, // n0x1460 c0x0000 (---------------) + I xn--tnsberg-q1a 0x00350a4d, // n0x1461 c0x0000 (---------------) + I xn--trany-yua 0x00350d8f, // n0x1462 c0x0000 (---------------) + I xn--trgstad-r1a 0x0035114c, // n0x1463 c0x0000 (---------------) + I xn--trna-woa 0x0035144d, // n0x1464 c0x0000 (---------------) + I xn--troms-zua 0x0035178d, // n0x1465 c0x0000 (---------------) + I xn--tysvr-vra 0x0035224e, // n0x1466 c0x0000 (---------------) + I xn--unjrga-rta 0x003529cc, // n0x1467 c0x0000 (---------------) + I xn--vads-jra 0x00352ccc, // n0x1468 c0x0000 (---------------) + I xn--vard-jra 0x00352fd0, // n0x1469 c0x0000 (---------------) + I xn--vegrshei-c0a 0x003533d1, // n0x146a c0x0000 (---------------) + I xn--vestvgy-ixa6o 0x0035380b, // n0x146b c0x0000 (---------------) + I xn--vg-yiab 0x0035430c, // n0x146c c0x0000 (---------------) + I xn--vgan-qoa 0x0035460e, // n0x146d c0x0000 (---------------) + I xn--vgsy-qoa0j 0x003552d1, // n0x146e c0x0000 (---------------) + I xn--vre-eiker-k8a 0x0035570e, // n0x146f c0x0000 (---------------) + I xn--vrggt-xqad 0x00355a8d, // n0x1470 c0x0000 (---------------) + I xn--vry-yla5g 0x003581cb, // n0x1471 c0x0000 (---------------) + I xn--yer-zna 0x0035894f, // n0x1472 c0x0000 (---------------) + I xn--ygarden-p1a 0x00359294, // n0x1473 c0x0000 (---------------) + I xn--ystre-slidre-ujb 0x002210c2, // n0x1474 c0x0000 (---------------) + I gs 0x002210c2, // n0x1475 c0x0000 (---------------) + I gs 0x00203503, // n0x1476 c0x0000 (---------------) + I nes 0x002210c2, // n0x1477 c0x0000 (---------------) + I gs 0x00203503, // n0x1478 c0x0000 (---------------) + I nes 0x002210c2, // n0x1479 c0x0000 (---------------) + I gs 0x00206d02, // n0x147a c0x0000 (---------------) + I os 0x00246945, // n0x147b c0x0000 (---------------) + I valer 0x00354fcc, // n0x147c c0x0000 (---------------) + I xn--vler-qoa 0x002210c2, // n0x147d c0x0000 (---------------) + I gs 0x002210c2, // n0x147e c0x0000 (---------------) + I gs 0x00206d02, // n0x147f c0x0000 (---------------) + I os 0x002210c2, // n0x1480 c0x0000 (---------------) + I gs 0x00282945, // n0x1481 c0x0000 (---------------) + I heroy 0x002a4845, // n0x1482 c0x0000 (---------------) + I sande 0x002210c2, // n0x1483 c0x0000 (---------------) + I gs 0x002210c2, // n0x1484 c0x0000 (---------------) + I gs 0x002120c2, // n0x1485 c0x0000 (---------------) + I bo 0x00282945, // n0x1486 c0x0000 (---------------) + I heroy 0x002f6009, // n0x1487 c0x0000 (---------------) + I xn--b-5ga 0x0031604c, // n0x1488 c0x0000 (---------------) + I xn--hery-ira 0x002210c2, // n0x1489 c0x0000 (---------------) + I gs 0x002210c2, // n0x148a c0x0000 (---------------) + I gs 0x002210c2, // n0x148b c0x0000 (---------------) + I gs 0x002210c2, // n0x148c c0x0000 (---------------) + I gs 0x00246945, // n0x148d c0x0000 (---------------) + I valer 0x002210c2, // n0x148e c0x0000 (---------------) + I gs 0x002210c2, // n0x148f c0x0000 (---------------) + I gs 0x002210c2, // n0x1490 c0x0000 (---------------) + I gs 0x002210c2, // n0x1491 c0x0000 (---------------) + I gs 0x002120c2, // n0x1492 c0x0000 (---------------) + I bo 0x002f6009, // n0x1493 c0x0000 (---------------) + I xn--b-5ga 0x002210c2, // n0x1494 c0x0000 (---------------) + I gs 0x002210c2, // n0x1495 c0x0000 (---------------) + I gs 0x002210c2, // n0x1496 c0x0000 (---------------) + I gs 0x002a4845, // n0x1497 c0x0000 (---------------) + I sande 0x002210c2, // n0x1498 c0x0000 (---------------) + I gs 0x002a4845, // n0x1499 c0x0000 (---------------) + I sande 0x0031604c, // n0x149a c0x0000 (---------------) + I xn--hery-ira 0x00354fcc, // n0x149b c0x0000 (---------------) + I xn--vler-qoa 0x00305743, // n0x149c c0x0000 (---------------) + I biz 0x002370c3, // n0x149d c0x0000 (---------------) + I com 0x00215b83, // n0x149e c0x0000 (---------------) + I edu 0x00208ec3, // n0x149f c0x0000 (---------------) + I gov 0x00214b84, // n0x14a0 c0x0000 (---------------) + I info 0x0024bdc3, // n0x14a1 c0x0000 (---------------) + I net 0x0024af03, // n0x14a2 c0x0000 (---------------) + I org 0x0002c4c8, // n0x14a3 c0x0000 (---------------) + merseine 0x00000904, // n0x14a4 c0x0000 (---------------) + mine 0x0004bc88, // n0x14a5 c0x0000 (---------------) + shacknet 0x3da09382, // n0x14a6 c0x00f6 (n0x14a7-n0x14a8) o I co 0x0012d948, // n0x14a7 c0x0000 (---------------) + blogspot 0x00209382, // n0x14a8 c0x0000 (---------------) + I co 0x002370c3, // n0x14a9 c0x0000 (---------------) + I com 0x00215b83, // n0x14aa c0x0000 (---------------) + I edu 0x00208ec3, // n0x14ab c0x0000 (---------------) + I gov 0x00216083, // n0x14ac c0x0000 (---------------) + I med 0x002c3946, // n0x14ad c0x0000 (---------------) + I museum 0x0024bdc3, // n0x14ae c0x0000 (---------------) + I net 0x0024af03, // n0x14af c0x0000 (---------------) + I org 0x00206683, // n0x14b0 c0x0000 (---------------) + I pro 0x00004c02, // n0x14b1 c0x0000 (---------------) + ae 0x00019587, // n0x14b2 c0x0000 (---------------) + blogdns 0x000c6408, // n0x14b3 c0x0000 (---------------) + blogsite 0x00076952, // n0x14b4 c0x0000 (---------------) + boldlygoingnowhere 0x00038f08, // n0x14b5 c0x0000 (---------------) + dnsalias 0x00073147, // n0x14b6 c0x0000 (---------------) + dnsdojo 0x0001bdcb, // n0x14b7 c0x0000 (---------------) + doesntexist 0x00116d49, // n0x14b8 c0x0000 (---------------) + dontexist 0x00038e07, // n0x14b9 c0x0000 (---------------) + doomdns 0x00073086, // n0x14ba c0x0000 (---------------) + dvrdns 0x00094dc8, // n0x14bb c0x0000 (---------------) + dynalias 0x3e411746, // n0x14bc c0x00f9 (n0x14e6-n0x14e8) + dyndns 0x00099f8d, // n0x14bd c0x0000 (---------------) + endofinternet 0x001122d0, // n0x14be c0x0000 (---------------) + endoftheinternet 0x0005e187, // n0x14bf c0x0000 (---------------) + from-me 0x0004a609, // n0x14c0 c0x0000 (---------------) + game-host 0x0004ed06, // n0x14c1 c0x0000 (---------------) + gotdns 0x0004360a, // n0x14c2 c0x0000 (---------------) + hobby-site 0x00074ec7, // n0x14c3 c0x0000 (---------------) + homedns 0x00024c07, // n0x14c4 c0x0000 (---------------) + homeftp 0x00093989, // n0x14c5 c0x0000 (---------------) + homelinux 0x00097348, // n0x14c6 c0x0000 (---------------) + homeunix 0x000c46ce, // n0x14c7 c0x0000 (---------------) + is-a-bruinsfan 0x0002204e, // n0x14c8 c0x0000 (---------------) + is-a-candidate 0x00029f0f, // n0x14c9 c0x0000 (---------------) + is-a-celticsfan 0x00084149, // n0x14ca c0x0000 (---------------) + is-a-chef 0x0005d009, // n0x14cb c0x0000 (---------------) + is-a-geek 0x000733cb, // n0x14cc c0x0000 (---------------) + is-a-knight 0x0009be0f, // n0x14cd c0x0000 (---------------) + is-a-linux-user 0x0012530c, // n0x14ce c0x0000 (---------------) + is-a-patsfan 0x000a2a0b, // n0x14cf c0x0000 (---------------) + is-a-soxfan 0x000ca688, // n0x14d0 c0x0000 (---------------) + is-found 0x000e40c7, // n0x14d1 c0x0000 (---------------) + is-lost 0x000f9e08, // n0x14d2 c0x0000 (---------------) + is-saved 0x0010dbcb, // n0x14d3 c0x0000 (---------------) + is-very-bad 0x0011990c, // n0x14d4 c0x0000 (---------------) + is-very-evil 0x0011b88c, // n0x14d5 c0x0000 (---------------) + is-very-good 0x0011c60c, // n0x14d6 c0x0000 (---------------) + is-very-nice 0x0013228d, // n0x14d7 c0x0000 (---------------) + is-very-sweet 0x000584c8, // n0x14d8 c0x0000 (---------------) + isa-geek 0x000552c9, // n0x14d9 c0x0000 (---------------) + kicks-ass 0x0013674b, // n0x14da c0x0000 (---------------) + misconfused 0x000cbc87, // n0x14db c0x0000 (---------------) + podzone 0x000c628a, // n0x14dc c0x0000 (---------------) + readmyblog 0x0010c246, // n0x14dd c0x0000 (---------------) + selfip 0x000c0ccd, // n0x14de c0x0000 (---------------) + sellsyourhome 0x000d7408, // n0x14df c0x0000 (---------------) + servebbs 0x000d75c8, // n0x14e0 c0x0000 (---------------) + serveftp 0x000d7949, // n0x14e1 c0x0000 (---------------) + servegame 0x000e3a0c, // n0x14e2 c0x0000 (---------------) + stuff-4-sale 0x00001682, // n0x14e3 c0x0000 (---------------) + us 0x0003eb86, // n0x14e4 c0x0000 (---------------) + webhop 0x00003e02, // n0x14e5 c0x0000 (---------------) + za 0x00008ec2, // n0x14e6 c0x0000 (---------------) + go 0x00024c04, // n0x14e7 c0x0000 (---------------) + home 0x00212083, // n0x14e8 c0x0000 (---------------) + I abo 0x0020a3c2, // n0x14e9 c0x0000 (---------------) + I ac 0x002370c3, // n0x14ea c0x0000 (---------------) + I com 0x00215b83, // n0x14eb c0x0000 (---------------) + I edu 0x00223ac3, // n0x14ec c0x0000 (---------------) + I gob 0x00204943, // n0x14ed c0x0000 (---------------) + I ing 0x00216083, // n0x14ee c0x0000 (---------------) + I med 0x0024bdc3, // n0x14ef c0x0000 (---------------) + I net 0x0020fc03, // n0x14f0 c0x0000 (---------------) + I nom 0x0024af03, // n0x14f1 c0x0000 (---------------) + I org 0x002d1f03, // n0x14f2 c0x0000 (---------------) + I sld 0x002370c3, // n0x14f3 c0x0000 (---------------) + I com 0x00215b83, // n0x14f4 c0x0000 (---------------) + I edu 0x00223ac3, // n0x14f5 c0x0000 (---------------) + I gob 0x00210703, // n0x14f6 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x14f7 c0x0000 (---------------) + I net 0x0020fc03, // n0x14f8 c0x0000 (---------------) + I nom 0x0024af03, // n0x14f9 c0x0000 (---------------) + I org 0x002370c3, // n0x14fa c0x0000 (---------------) + I com 0x00215b83, // n0x14fb c0x0000 (---------------) + I edu 0x0024af03, // n0x14fc c0x0000 (---------------) + I org 0x002370c3, // n0x14fd c0x0000 (---------------) + I com 0x00215b83, // n0x14fe c0x0000 (---------------) + I edu 0x00208ec3, // n0x14ff c0x0000 (---------------) + I gov 0x00200041, // n0x1500 c0x0000 (---------------) + I i 0x00210703, // n0x1501 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x1502 c0x0000 (---------------) + I net 0x00248a83, // n0x1503 c0x0000 (---------------) + I ngo 0x0024af03, // n0x1504 c0x0000 (---------------) + I org 0x00305743, // n0x1505 c0x0000 (---------------) + I biz 0x002370c3, // n0x1506 c0x0000 (---------------) + I com 0x00215b83, // n0x1507 c0x0000 (---------------) + I edu 0x002d3a03, // n0x1508 c0x0000 (---------------) + I fam 0x00223ac3, // n0x1509 c0x0000 (---------------) + I gob 0x002ac3c3, // n0x150a c0x0000 (---------------) + I gok 0x00277003, // n0x150b c0x0000 (---------------) + I gon 0x00248ac3, // n0x150c c0x0000 (---------------) + I gop 0x00210b83, // n0x150d c0x0000 (---------------) + I gos 0x00208ec3, // n0x150e c0x0000 (---------------) + I gov 0x00214b84, // n0x150f c0x0000 (---------------) + I info 0x0024bdc3, // n0x1510 c0x0000 (---------------) + I net 0x0024af03, // n0x1511 c0x0000 (---------------) + I org 0x00200b03, // n0x1512 c0x0000 (---------------) + I web 0x00241145, // n0x1513 c0x0000 (---------------) + I 6bone 0x00311104, // n0x1514 c0x0000 (---------------) + I agro 0x0024b183, // n0x1515 c0x0000 (---------------) + I aid 0x00209243, // n0x1516 c0x0000 (---------------) + I art 0x002c5943, // n0x1517 c0x0000 (---------------) + I atm 0x002a9808, // n0x1518 c0x0000 (---------------) + I augustow 0x00230c44, // n0x1519 c0x0000 (---------------) + I auto 0x0021de8a, // n0x151a c0x0000 (---------------) + I babia-gora 0x00262746, // n0x151b c0x0000 (---------------) + I bedzin 0x0034cb07, // n0x151c c0x0000 (---------------) + I beskidy 0x0021e84a, // n0x151d c0x0000 (---------------) + I bialowieza 0x0023d709, // n0x151e c0x0000 (---------------) + I bialystok 0x00353a87, // n0x151f c0x0000 (---------------) + I bielawa 0x0035974a, // n0x1520 c0x0000 (---------------) + I bieszczady 0x00305743, // n0x1521 c0x0000 (---------------) + I biz 0x002d250b, // n0x1522 c0x0000 (---------------) + I boleslawiec 0x002d4889, // n0x1523 c0x0000 (---------------) + I bydgoszcz 0x0023f905, // n0x1524 c0x0000 (---------------) + I bytom 0x002c17c7, // n0x1525 c0x0000 (---------------) + I cieszyn 0x00009382, // n0x1526 c0x0000 (---------------) + co 0x002370c3, // n0x1527 c0x0000 (---------------) + I com 0x002ab287, // n0x1528 c0x0000 (---------------) + I czeladz 0x00225885, // n0x1529 c0x0000 (---------------) + I czest 0x002aeb49, // n0x152a c0x0000 (---------------) + I dlugoleka 0x00215b83, // n0x152b c0x0000 (---------------) + I edu 0x00223c46, // n0x152c c0x0000 (---------------) + I elblag 0x002e1803, // n0x152d c0x0000 (---------------) + I elk 0x002a93c3, // n0x152e c0x0000 (---------------) + I gda 0x002ee086, // n0x152f c0x0000 (---------------) + I gdansk 0x00310fc6, // n0x1530 c0x0000 (---------------) + I gdynia 0x002111c7, // n0x1531 c0x0000 (---------------) + I gliwice 0x002246c6, // n0x1532 c0x0000 (---------------) + I glogow 0x00228885, // n0x1533 c0x0000 (---------------) + I gmina 0x00311e07, // n0x1534 c0x0000 (---------------) + I gniezno 0x0024cd07, // n0x1535 c0x0000 (---------------) + I gorlice 0x40208ec3, // n0x1536 c0x0100 (n0x15be-n0x15c7) + I gov 0x002f90c7, // n0x1537 c0x0000 (---------------) + I grajewo 0x00292f03, // n0x1538 c0x0000 (---------------) + I gsm 0x00311545, // n0x1539 c0x0000 (---------------) + I ilawa 0x00214b84, // n0x153a c0x0000 (---------------) + I info 0x0022d403, // n0x153b c0x0000 (---------------) + I irc 0x002e1ac8, // n0x153c c0x0000 (---------------) + I jaworzno 0x0022bdcc, // n0x153d c0x0000 (---------------) + I jelenia-gora 0x0029e085, // n0x153e c0x0000 (---------------) + I jgora 0x002a6746, // n0x153f c0x0000 (---------------) + I kalisz 0x002ab147, // n0x1540 c0x0000 (---------------) + I karpacz 0x0034e047, // n0x1541 c0x0000 (---------------) + I kartuzy 0x0023f7c7, // n0x1542 c0x0000 (---------------) + I kaszuby 0x00244408, // n0x1543 c0x0000 (---------------) + I katowice 0x0023234f, // n0x1544 c0x0000 (---------------) + I kazimierz-dolny 0x00306e45, // n0x1545 c0x0000 (---------------) + I kepno 0x00339f87, // n0x1546 c0x0000 (---------------) + I ketrzyn 0x0027bdc7, // n0x1547 c0x0000 (---------------) + I klodzko 0x0029354a, // n0x1548 c0x0000 (---------------) + I kobierzyce 0x002fcec9, // n0x1549 c0x0000 (---------------) + I kolobrzeg 0x002e1985, // n0x154a c0x0000 (---------------) + I konin 0x002e520a, // n0x154b c0x0000 (---------------) + I konskowola 0x002a3d46, // n0x154c c0x0000 (---------------) + I krakow 0x002b1ac5, // n0x154d c0x0000 (---------------) + I kutno 0x002d0044, // n0x154e c0x0000 (---------------) + I lapy 0x00240246, // n0x154f c0x0000 (---------------) + I lebork 0x0023f087, // n0x1550 c0x0000 (---------------) + I legnica 0x002aab07, // n0x1551 c0x0000 (---------------) + I lezajsk 0x00306808, // n0x1552 c0x0000 (---------------) + I limanowa 0x002ed285, // n0x1553 c0x0000 (---------------) + I lomza 0x00225786, // n0x1554 c0x0000 (---------------) + I lowicz 0x00272a05, // n0x1555 c0x0000 (---------------) + I lubin 0x0022ba85, // n0x1556 c0x0000 (---------------) + I lukow 0x0022b9c4, // n0x1557 c0x0000 (---------------) + I mail 0x00225547, // n0x1558 c0x0000 (---------------) + I malbork 0x002363ca, // n0x1559 c0x0000 (---------------) + I malopolska 0x0021cb08, // n0x155a c0x0000 (---------------) + I mazowsze 0x00252486, // n0x155b c0x0000 (---------------) + I mazury 0x00305605, // n0x155c c0x0000 (---------------) + I mbone 0x00216083, // n0x155d c0x0000 (---------------) + I med 0x00216085, // n0x155e c0x0000 (---------------) + I media 0x00228286, // n0x155f c0x0000 (---------------) + I miasta 0x00356d06, // n0x1560 c0x0000 (---------------) + I mielec 0x0034f486, // n0x1561 c0x0000 (---------------) + I mielno 0x00210703, // n0x1562 c0x0000 (---------------) + I mil 0x003400c7, // n0x1563 c0x0000 (---------------) + I mragowo 0x0027bd45, // n0x1564 c0x0000 (---------------) + I naklo 0x0024bdc3, // n0x1565 c0x0000 (---------------) + I net 0x00248a83, // n0x1566 c0x0000 (---------------) + I ngo 0x0026de4d, // n0x1567 c0x0000 (---------------) + I nieruchomosci 0x0020fc03, // n0x1568 c0x0000 (---------------) + I nom 0x00306908, // n0x1569 c0x0000 (---------------) + I nowaruda 0x002ac604, // n0x156a c0x0000 (---------------) + I nysa 0x0026d3c5, // n0x156b c0x0000 (---------------) + I olawa 0x00293446, // n0x156c c0x0000 (---------------) + I olecko 0x002fb206, // n0x156d c0x0000 (---------------) + I olkusz 0x00260e07, // n0x156e c0x0000 (---------------) + I olsztyn 0x0023ec87, // n0x156f c0x0000 (---------------) + I opoczno 0x002d4345, // n0x1570 c0x0000 (---------------) + I opole 0x0024af03, // n0x1571 c0x0000 (---------------) + I org 0x00218407, // n0x1572 c0x0000 (---------------) + I ostroda 0x0021b0c9, // n0x1573 c0x0000 (---------------) + I ostroleka 0x0021f1c9, // n0x1574 c0x0000 (---------------) + I ostrowiec 0x00220c4a, // n0x1575 c0x0000 (---------------) + I ostrowwlkp 0x00245dc2, // n0x1576 c0x0000 (---------------) + I pc 0x00311504, // n0x1577 c0x0000 (---------------) + I pila 0x002c8684, // n0x1578 c0x0000 (---------------) + I pisz 0x002cb5c7, // n0x1579 c0x0000 (---------------) + I podhale 0x002cb988, // n0x157a c0x0000 (---------------) + I podlasie 0x0022e589, // n0x157b c0x0000 (---------------) + I polkowice 0x0021eb49, // n0x157c c0x0000 (---------------) + I pomorskie 0x002cc487, // n0x157d c0x0000 (---------------) + I pomorze 0x00211c86, // n0x157e c0x0000 (---------------) + I powiat 0x002cdd46, // n0x157f c0x0000 (---------------) + I poznan 0x002ce944, // n0x1580 c0x0000 (---------------) + I priv 0x002ceaca, // n0x1581 c0x0000 (---------------) + I prochowice 0x002d1148, // n0x1582 c0x0000 (---------------) + I pruszkow 0x002d1349, // n0x1583 c0x0000 (---------------) + I przeworsk 0x0028b746, // n0x1584 c0x0000 (---------------) + I pulawy 0x002a0185, // n0x1585 c0x0000 (---------------) + I radom 0x0021c9c8, // n0x1586 c0x0000 (---------------) + I rawa-maz 0x002ba28a, // n0x1587 c0x0000 (---------------) + I realestate 0x0029cd03, // n0x1588 c0x0000 (---------------) + I rel 0x00256646, // n0x1589 c0x0000 (---------------) + I rybnik 0x002cc587, // n0x158a c0x0000 (---------------) + I rzeszow 0x00279005, // n0x158b c0x0000 (---------------) + I sanok 0x002ac4c5, // n0x158c c0x0000 (---------------) + I sejny 0x00223243, // n0x158d c0x0000 (---------------) + I sex 0x0022fa84, // n0x158e c0x0000 (---------------) + I shop 0x002cbac7, // n0x158f c0x0000 (---------------) + I siedlce 0x0026ed85, // n0x1590 c0x0000 (---------------) + I sklep 0x00274707, // n0x1591 c0x0000 (---------------) + I skoczow 0x00269785, // n0x1592 c0x0000 (---------------) + I slask 0x002da906, // n0x1593 c0x0000 (---------------) + I slupsk 0x002dc3c5, // n0x1594 c0x0000 (---------------) + I sopot 0x00270bc3, // n0x1595 c0x0000 (---------------) + I sos 0x0027f089, // n0x1596 c0x0000 (---------------) + I sosnowiec 0x0026d18c, // n0x1597 c0x0000 (---------------) + I stalowa-wola 0x002a0c4c, // n0x1598 c0x0000 (---------------) + I starachowice 0x002e5b88, // n0x1599 c0x0000 (---------------) + I stargard 0x002ca507, // n0x159a c0x0000 (---------------) + I suwalki 0x002e5f88, // n0x159b c0x0000 (---------------) + I swidnica 0x002e6b8a, // n0x159c c0x0000 (---------------) + I swiebodzin 0x002e6f4b, // n0x159d c0x0000 (---------------) + I swinoujscie 0x002d49c8, // n0x159e c0x0000 (---------------) + I szczecin 0x002ea6c8, // n0x159f c0x0000 (---------------) + I szczytno 0x002a6846, // n0x15a0 c0x0000 (---------------) + I szkola 0x00202b45, // n0x15a1 c0x0000 (---------------) + I targi 0x0020c54a, // n0x15a2 c0x0000 (---------------) + I tarnobrzeg 0x0032db05, // n0x15a3 c0x0000 (---------------) + I tgory 0x00226782, // n0x15a4 c0x0000 (---------------) + I tm 0x002b7c07, // n0x15a5 c0x0000 (---------------) + I tourism 0x002a7006, // n0x15a6 c0x0000 (---------------) + I travel 0x002b5045, // n0x15a7 c0x0000 (---------------) + I turek 0x002d1749, // n0x15a8 c0x0000 (---------------) + I turystyka 0x0022da85, // n0x15a9 c0x0000 (---------------) + I tychy 0x00282486, // n0x15aa c0x0000 (---------------) + I usenet 0x0031acc5, // n0x15ab c0x0000 (---------------) + I ustka 0x002021c9, // n0x15ac c0x0000 (---------------) + I walbrzych 0x0022f246, // n0x15ad c0x0000 (---------------) + I warmia 0x002cfb08, // n0x15ae c0x0000 (---------------) + I warszawa 0x00260643, // n0x15af c0x0000 (---------------) + I waw 0x00312746, // n0x15b0 c0x0000 (---------------) + I wegrow 0x00269286, // n0x15b1 c0x0000 (---------------) + I wielun 0x00244205, // n0x15b2 c0x0000 (---------------) + I wlocl 0x00244209, // n0x15b3 c0x0000 (---------------) + I wloclawek 0x00228509, // n0x15b4 c0x0000 (---------------) + I wodzislaw 0x002f9207, // n0x15b5 c0x0000 (---------------) + I wolomin 0x00243dc4, // n0x15b6 c0x0000 (---------------) + I wroc 0x00243dc7, // n0x15b7 c0x0000 (---------------) + I wroclaw 0x0021ea49, // n0x15b8 c0x0000 (---------------) + I zachpomor 0x00231b85, // n0x15b9 c0x0000 (---------------) + I zagan 0x00240648, // n0x15ba c0x0000 (---------------) + I zakopane 0x00244105, // n0x15bb c0x0000 (---------------) + I zarow 0x00209545, // n0x15bc c0x0000 (---------------) + I zgora 0x00223349, // n0x15bd c0x0000 (---------------) + I zgorzelec 0x00202642, // n0x15be c0x0000 (---------------) + I pa 0x00202a82, // n0x15bf c0x0000 (---------------) + I po 0x00205902, // n0x15c0 c0x0000 (---------------) + I so 0x002be902, // n0x15c1 c0x0000 (---------------) + I sr 0x00228349, // n0x15c2 c0x0000 (---------------) + I starostwo 0x0020ab02, // n0x15c3 c0x0000 (---------------) + I ug 0x00201542, // n0x15c4 c0x0000 (---------------) + I um 0x00211c44, // n0x15c5 c0x0000 (---------------) + I upow 0x002433c2, // n0x15c6 c0x0000 (---------------) + I uw 0x00209382, // n0x15c7 c0x0000 (---------------) + I co 0x00215b83, // n0x15c8 c0x0000 (---------------) + I edu 0x00208ec3, // n0x15c9 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x15ca c0x0000 (---------------) + I net 0x0024af03, // n0x15cb c0x0000 (---------------) + I org 0x0020a3c2, // n0x15cc c0x0000 (---------------) + I ac 0x00305743, // n0x15cd c0x0000 (---------------) + I biz 0x002370c3, // n0x15ce c0x0000 (---------------) + I com 0x00215b83, // n0x15cf c0x0000 (---------------) + I edu 0x00205083, // n0x15d0 c0x0000 (---------------) + I est 0x00208ec3, // n0x15d1 c0x0000 (---------------) + I gov 0x00214b84, // n0x15d2 c0x0000 (---------------) + I info 0x00228604, // n0x15d3 c0x0000 (---------------) + I isla 0x002592c4, // n0x15d4 c0x0000 (---------------) + I name 0x0024bdc3, // n0x15d5 c0x0000 (---------------) + I net 0x0024af03, // n0x15d6 c0x0000 (---------------) + I org 0x00206683, // n0x15d7 c0x0000 (---------------) + I pro 0x002cf284, // n0x15d8 c0x0000 (---------------) + I prof 0x00267ac3, // n0x15d9 c0x0000 (---------------) + I aca 0x00236f03, // n0x15da c0x0000 (---------------) + I bar 0x002acd03, // n0x15db c0x0000 (---------------) + I cpa 0x00206443, // n0x15dc c0x0000 (---------------) + I eng 0x00253343, // n0x15dd c0x0000 (---------------) + I jur 0x00228683, // n0x15de c0x0000 (---------------) + I law 0x00216083, // n0x15df c0x0000 (---------------) + I med 0x002370c3, // n0x15e0 c0x0000 (---------------) + I com 0x00215b83, // n0x15e1 c0x0000 (---------------) + I edu 0x00208ec3, // n0x15e2 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x15e3 c0x0000 (---------------) + I net 0x0024af03, // n0x15e4 c0x0000 (---------------) + I org 0x002cb143, // n0x15e5 c0x0000 (---------------) + I plo 0x002f1383, // n0x15e6 c0x0000 (---------------) + I sec 0x0012d948, // n0x15e7 c0x0000 (---------------) + blogspot 0x002370c3, // n0x15e8 c0x0000 (---------------) + I com 0x00215b83, // n0x15e9 c0x0000 (---------------) + I edu 0x00208ec3, // n0x15ea c0x0000 (---------------) + I gov 0x00216fc3, // n0x15eb c0x0000 (---------------) + I int 0x0024bdc3, // n0x15ec c0x0000 (---------------) + I net 0x0024e004, // n0x15ed c0x0000 (---------------) + I nome 0x0024af03, // n0x15ee c0x0000 (---------------) + I org 0x0029f104, // n0x15ef c0x0000 (---------------) + I publ 0x002a9745, // n0x15f0 c0x0000 (---------------) + I belau 0x00209382, // n0x15f1 c0x0000 (---------------) + I co 0x00203b42, // n0x15f2 c0x0000 (---------------) + I ed 0x00208ec2, // n0x15f3 c0x0000 (---------------) + I go 0x00200982, // n0x15f4 c0x0000 (---------------) + I ne 0x00201002, // n0x15f5 c0x0000 (---------------) + I or 0x002370c3, // n0x15f6 c0x0000 (---------------) + I com 0x0023d9c4, // n0x15f7 c0x0000 (---------------) + I coop 0x00215b83, // n0x15f8 c0x0000 (---------------) + I edu 0x00208ec3, // n0x15f9 c0x0000 (---------------) + I gov 0x00210703, // n0x15fa c0x0000 (---------------) + I mil 0x0024bdc3, // n0x15fb c0x0000 (---------------) + I net 0x0024af03, // n0x15fc c0x0000 (---------------) + I org 0x002370c3, // n0x15fd c0x0000 (---------------) + I com 0x00215b83, // n0x15fe c0x0000 (---------------) + I edu 0x00208ec3, // n0x15ff c0x0000 (---------------) + I gov 0x00210703, // n0x1600 c0x0000 (---------------) + I mil 0x002592c4, // n0x1601 c0x0000 (---------------) + I name 0x0024bdc3, // n0x1602 c0x0000 (---------------) + I net 0x0024af03, // n0x1603 c0x0000 (---------------) + I org 0x0025e643, // n0x1604 c0x0000 (---------------) + I sch 0x002c5c84, // n0x1605 c0x0000 (---------------) + I asso 0x0012d948, // n0x1606 c0x0000 (---------------) + blogspot 0x002370c3, // n0x1607 c0x0000 (---------------) + I com 0x0020fc03, // n0x1608 c0x0000 (---------------) + I nom 0x00214704, // n0x1609 c0x0000 (---------------) + I arts 0x0012d948, // n0x160a c0x0000 (---------------) + blogspot 0x002370c3, // n0x160b c0x0000 (---------------) + I com 0x00248344, // n0x160c c0x0000 (---------------) + I firm 0x00214b84, // n0x160d c0x0000 (---------------) + I info 0x0020fc03, // n0x160e c0x0000 (---------------) + I nom 0x00211402, // n0x160f c0x0000 (---------------) + I nt 0x0024af03, // n0x1610 c0x0000 (---------------) + I org 0x00229ac3, // n0x1611 c0x0000 (---------------) + I rec 0x002e3245, // n0x1612 c0x0000 (---------------) + I store 0x00226782, // n0x1613 c0x0000 (---------------) + I tm 0x00274883, // n0x1614 c0x0000 (---------------) + I www 0x0020a3c2, // n0x1615 c0x0000 (---------------) + I ac 0x00209382, // n0x1616 c0x0000 (---------------) + I co 0x00215b83, // n0x1617 c0x0000 (---------------) + I edu 0x00208ec3, // n0x1618 c0x0000 (---------------) + I gov 0x00200942, // n0x1619 c0x0000 (---------------) + I in 0x0024af03, // n0x161a c0x0000 (---------------) + I org 0x0020a3c2, // n0x161b c0x0000 (---------------) + I ac 0x00359907, // n0x161c c0x0000 (---------------) + I adygeya 0x0032ff45, // n0x161d c0x0000 (---------------) + I altai 0x0027a504, // n0x161e c0x0000 (---------------) + I amur 0x002b3086, // n0x161f c0x0000 (---------------) + I amursk 0x0024e40b, // n0x1620 c0x0000 (---------------) + I arkhangelsk 0x00250e89, // n0x1621 c0x0000 (---------------) + I astrakhan 0x002a6686, // n0x1622 c0x0000 (---------------) + I baikal 0x0030ff09, // n0x1623 c0x0000 (---------------) + I bashkiria 0x002a5a88, // n0x1624 c0x0000 (---------------) + I belgorod 0x00208683, // n0x1625 c0x0000 (---------------) + I bir 0x00226387, // n0x1626 c0x0000 (---------------) + I bryansk 0x00282dc8, // n0x1627 c0x0000 (---------------) + I buryatia 0x003493c3, // n0x1628 c0x0000 (---------------) + I cbg 0x00264b04, // n0x1629 c0x0000 (---------------) + I chel 0x0026eb4b, // n0x162a c0x0000 (---------------) + I chelyabinsk 0x002c6d05, // n0x162b c0x0000 (---------------) + I chita 0x00203f48, // n0x162c c0x0000 (---------------) + I chukotka 0x00344289, // n0x162d c0x0000 (---------------) + I chuvashia 0x003126c3, // n0x162e c0x0000 (---------------) + I cmw 0x002370c3, // n0x162f c0x0000 (---------------) + I com 0x002a9a48, // n0x1630 c0x0000 (---------------) + I dagestan 0x0026da47, // n0x1631 c0x0000 (---------------) + I dudinka 0x00241906, // n0x1632 c0x0000 (---------------) + I e-burg 0x00215b83, // n0x1633 c0x0000 (---------------) + I edu 0x00320f07, // n0x1634 c0x0000 (---------------) + I fareast 0x00208ec3, // n0x1635 c0x0000 (---------------) + I gov 0x002358c6, // n0x1636 c0x0000 (---------------) + I grozny 0x00216fc3, // n0x1637 c0x0000 (---------------) + I int 0x0027c3c7, // n0x1638 c0x0000 (---------------) + I irkutsk 0x00293fc7, // n0x1639 c0x0000 (---------------) + I ivanovo 0x00288b87, // n0x163a c0x0000 (---------------) + I izhevsk 0x002254c5, // n0x163b c0x0000 (---------------) + I jamal 0x0020c0c3, // n0x163c c0x0000 (---------------) + I jar 0x0030ad0b, // n0x163d c0x0000 (---------------) + I joshkar-ola 0x00302808, // n0x163e c0x0000 (---------------) + I k-uralsk 0x00330b48, // n0x163f c0x0000 (---------------) + I kalmykia 0x0030b606, // n0x1640 c0x0000 (---------------) + I kaluga 0x00261dc9, // n0x1641 c0x0000 (---------------) + I kamchatka 0x002e09c7, // n0x1642 c0x0000 (---------------) + I karelia 0x002ec645, // n0x1643 c0x0000 (---------------) + I kazan 0x0033f984, // n0x1644 c0x0000 (---------------) + I kchr 0x00235fc8, // n0x1645 c0x0000 (---------------) + I kemerovo 0x0024e68a, // n0x1646 c0x0000 (---------------) + I khabarovsk 0x0024e8c9, // n0x1647 c0x0000 (---------------) + I khakassia 0x00288d03, // n0x1648 c0x0000 (---------------) + I khv 0x00222b45, // n0x1649 c0x0000 (---------------) + I kirov 0x002de143, // n0x164a c0x0000 (---------------) + I kms 0x00329546, // n0x164b c0x0000 (---------------) + I koenig 0x002b6f04, // n0x164c c0x0000 (---------------) + I komi 0x0029fb08, // n0x164d c0x0000 (---------------) + I kostroma 0x002a3ecb, // n0x164e c0x0000 (---------------) + I krasnoyarsk 0x00357a85, // n0x164f c0x0000 (---------------) + I kuban 0x002a94c6, // n0x1650 c0x0000 (---------------) + I kurgan 0x002ad905, // n0x1651 c0x0000 (---------------) + I kursk 0x002b0e08, // n0x1652 c0x0000 (---------------) + I kustanai 0x002b1c07, // n0x1653 c0x0000 (---------------) + I kuzbass 0x0020f207, // n0x1654 c0x0000 (---------------) + I lipetsk 0x0034fbc7, // n0x1655 c0x0000 (---------------) + I magadan 0x002b3e08, // n0x1656 c0x0000 (---------------) + I magnitka 0x00231e44, // n0x1657 c0x0000 (---------------) + I mari 0x00231e47, // n0x1658 c0x0000 (---------------) + I mari-el 0x0030bb86, // n0x1659 c0x0000 (---------------) + I marine 0x00210703, // n0x165a c0x0000 (---------------) + I mil 0x002bbc48, // n0x165b c0x0000 (---------------) + I mordovia 0x002be886, // n0x165c c0x0000 (---------------) + I mosreg 0x00234683, // n0x165d c0x0000 (---------------) + I msk 0x002c1b88, // n0x165e c0x0000 (---------------) + I murmansk 0x002c4605, // n0x165f c0x0000 (---------------) + I mytis 0x002dbf88, // n0x1660 c0x0000 (---------------) + I nakhodka 0x00215d87, // n0x1661 c0x0000 (---------------) + I nalchik 0x0024bdc3, // n0x1662 c0x0000 (---------------) + I net 0x002094c3, // n0x1663 c0x0000 (---------------) + I nkz 0x0027b704, // n0x1664 c0x0000 (---------------) + I nnov 0x002aafc7, // n0x1665 c0x0000 (---------------) + I norilsk 0x00212e83, // n0x1666 c0x0000 (---------------) + I nov 0x0029408b, // n0x1667 c0x0000 (---------------) + I novosibirsk 0x002101c3, // n0x1668 c0x0000 (---------------) + I nsk 0x00234644, // n0x1669 c0x0000 (---------------) + I omsk 0x002e32c8, // n0x166a c0x0000 (---------------) + I orenburg 0x0024af03, // n0x166b c0x0000 (---------------) + I org 0x002fb145, // n0x166c c0x0000 (---------------) + I oryol 0x00258e45, // n0x166d c0x0000 (---------------) + I oskol 0x00202646, // n0x166e c0x0000 (---------------) + I palana 0x00226c05, // n0x166f c0x0000 (---------------) + I penza 0x002b2384, // n0x1670 c0x0000 (---------------) + I perm 0x00202a42, // n0x1671 c0x0000 (---------------) + I pp 0x002da9c5, // n0x1672 c0x0000 (---------------) + I pskov 0x002d1603, // n0x1673 c0x0000 (---------------) + I ptz 0x002d00ca, // n0x1674 c0x0000 (---------------) + I pyatigorsk 0x00258a03, // n0x1675 c0x0000 (---------------) + I rnd 0x00227309, // n0x1676 c0x0000 (---------------) + I rubtsovsk 0x0021d646, // n0x1677 c0x0000 (---------------) + I ryazan 0x0022cc48, // n0x1678 c0x0000 (---------------) + I sakhalin 0x0027e146, // n0x1679 c0x0000 (---------------) + I samara 0x0024a147, // n0x167a c0x0000 (---------------) + I saratov 0x002d9fc8, // n0x167b c0x0000 (---------------) + I simbirsk 0x00210088, // n0x167c c0x0000 (---------------) + I smolensk 0x002db003, // n0x167d c0x0000 (---------------) + I snz 0x002dfd03, // n0x167e c0x0000 (---------------) + I spb 0x0022e409, // n0x167f c0x0000 (---------------) + I stavropol 0x002e9383, // n0x1680 c0x0000 (---------------) + I stv 0x002e7c46, // n0x1681 c0x0000 (---------------) + I surgut 0x002a7fc6, // n0x1682 c0x0000 (---------------) + I syzran 0x00323746, // n0x1683 c0x0000 (---------------) + I tambov 0x00219809, // n0x1684 c0x0000 (---------------) + I tatarstan 0x0032d784, // n0x1685 c0x0000 (---------------) + I test 0x00202843, // n0x1686 c0x0000 (---------------) + I tom 0x002de045, // n0x1687 c0x0000 (---------------) + I tomsk 0x00313fc9, // n0x1688 c0x0000 (---------------) + I tsaritsyn 0x0020f303, // n0x1689 c0x0000 (---------------) + I tsk 0x002cfe84, // n0x168a c0x0000 (---------------) + I tula 0x002dc4c4, // n0x168b c0x0000 (---------------) + I tuva 0x002dc6c4, // n0x168c c0x0000 (---------------) + I tver 0x002014c6, // n0x168d c0x0000 (---------------) + I tyumen 0x0034e383, // n0x168e c0x0000 (---------------) + I udm 0x0034e388, // n0x168f c0x0000 (---------------) + I udmurtia 0x00250ac8, // n0x1690 c0x0000 (---------------) + I ulan-ude 0x0024a2c6, // n0x1691 c0x0000 (---------------) + I vdonsk 0x002ec44b, // n0x1692 c0x0000 (---------------) + I vladikavkaz 0x002ec788, // n0x1693 c0x0000 (---------------) + I vladimir 0x002ecb8b, // n0x1694 c0x0000 (---------------) + I vladivostok 0x002ed889, // n0x1695 c0x0000 (---------------) + I volgograd 0x002edf87, // n0x1696 c0x0000 (---------------) + I vologda 0x002eef08, // n0x1697 c0x0000 (---------------) + I voronezh 0x002efa43, // n0x1698 c0x0000 (---------------) + I vrn 0x002efb06, // n0x1699 c0x0000 (---------------) + I vyatka 0x002e4707, // n0x169a c0x0000 (---------------) + I yakutia 0x00288005, // n0x169b c0x0000 (---------------) + I yamal 0x0030cdc9, // n0x169c c0x0000 (---------------) + I yaroslavl 0x00310ccd, // n0x169d c0x0000 (---------------) + I yekaterinburg 0x0022ca91, // n0x169e c0x0000 (---------------) + I yuzhno-sakhalinsk 0x0022b705, // n0x169f c0x0000 (---------------) + I zgrad 0x0020a3c2, // n0x16a0 c0x0000 (---------------) + I ac 0x00209382, // n0x16a1 c0x0000 (---------------) + I co 0x002370c3, // n0x16a2 c0x0000 (---------------) + I com 0x00215b83, // n0x16a3 c0x0000 (---------------) + I edu 0x002d80c4, // n0x16a4 c0x0000 (---------------) + I gouv 0x00208ec3, // n0x16a5 c0x0000 (---------------) + I gov 0x00216fc3, // n0x16a6 c0x0000 (---------------) + I int 0x00210703, // n0x16a7 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x16a8 c0x0000 (---------------) + I net 0x002370c3, // n0x16a9 c0x0000 (---------------) + I com 0x00215b83, // n0x16aa c0x0000 (---------------) + I edu 0x00208ec3, // n0x16ab c0x0000 (---------------) + I gov 0x00216083, // n0x16ac c0x0000 (---------------) + I med 0x0024bdc3, // n0x16ad c0x0000 (---------------) + I net 0x0024af03, // n0x16ae c0x0000 (---------------) + I org 0x0029f103, // n0x16af c0x0000 (---------------) + I pub 0x0025e643, // n0x16b0 c0x0000 (---------------) + I sch 0x002370c3, // n0x16b1 c0x0000 (---------------) + I com 0x00215b83, // n0x16b2 c0x0000 (---------------) + I edu 0x00208ec3, // n0x16b3 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x16b4 c0x0000 (---------------) + I net 0x0024af03, // n0x16b5 c0x0000 (---------------) + I org 0x002370c3, // n0x16b6 c0x0000 (---------------) + I com 0x00215b83, // n0x16b7 c0x0000 (---------------) + I edu 0x00208ec3, // n0x16b8 c0x0000 (---------------) + I gov 0x0024bdc3, // n0x16b9 c0x0000 (---------------) + I net 0x0024af03, // n0x16ba c0x0000 (---------------) + I org 0x002370c3, // n0x16bb c0x0000 (---------------) + I com 0x00215b83, // n0x16bc c0x0000 (---------------) + I edu 0x00208ec3, // n0x16bd c0x0000 (---------------) + I gov 0x00214b84, // n0x16be c0x0000 (---------------) + I info 0x00216083, // n0x16bf c0x0000 (---------------) + I med 0x0024bdc3, // n0x16c0 c0x0000 (---------------) + I net 0x0024af03, // n0x16c1 c0x0000 (---------------) + I org 0x00288182, // n0x16c2 c0x0000 (---------------) + I tv 0x00200141, // n0x16c3 c0x0000 (---------------) + I a 0x0020a3c2, // n0x16c4 c0x0000 (---------------) + I ac 0x00200001, // n0x16c5 c0x0000 (---------------) + I b 0x002f6342, // n0x16c6 c0x0000 (---------------) + I bd 0x0012d948, // n0x16c7 c0x0000 (---------------) + blogspot 0x00217e45, // n0x16c8 c0x0000 (---------------) + I brand 0x002002c1, // n0x16c9 c0x0000 (---------------) + I c 0x00200401, // n0x16ca c0x0000 (---------------) + I d 0x002009c1, // n0x16cb c0x0000 (---------------) + I e 0x00200081, // n0x16cc c0x0000 (---------------) + I f 0x002468c2, // n0x16cd c0x0000 (---------------) + I fh 0x0033f8c4, // n0x16ce c0x0000 (---------------) + I fhsk 0x002468c3, // n0x16cf c0x0000 (---------------) + I fhv 0x00200181, // n0x16d0 c0x0000 (---------------) + I g 0x00200301, // n0x16d1 c0x0000 (---------------) + I h 0x00200041, // n0x16d2 c0x0000 (---------------) + I i 0x00200101, // n0x16d3 c0x0000 (---------------) + I k 0x002ada07, // n0x16d4 c0x0000 (---------------) + I komforb 0x002b9dcf, // n0x16d5 c0x0000 (---------------) + I kommunalforbund 0x002c8046, // n0x16d6 c0x0000 (---------------) + I komvux 0x002004c1, // n0x16d7 c0x0000 (---------------) + I l 0x00293146, // n0x16d8 c0x0000 (---------------) + I lanbib 0x00200741, // n0x16d9 c0x0000 (---------------) + I m 0x002003c1, // n0x16da c0x0000 (---------------) + I n 0x00344b8e, // n0x16db c0x0000 (---------------) + I naturbruksgymn 0x00200501, // n0x16dc c0x0000 (---------------) + I o 0x0024af03, // n0x16dd c0x0000 (---------------) + I org 0x00202481, // n0x16de c0x0000 (---------------) + I p 0x002ee845, // n0x16df c0x0000 (---------------) + I parti 0x00202a42, // n0x16e0 c0x0000 (---------------) + I pp 0x00223145, // n0x16e1 c0x0000 (---------------) + I press 0x00200a01, // n0x16e2 c0x0000 (---------------) + I r 0x00200601, // n0x16e3 c0x0000 (---------------) + I s 0x00224ec4, // n0x16e4 c0x0000 (---------------) + I sshn 0x00200281, // n0x16e5 c0x0000 (---------------) + I t 0x00226782, // n0x16e6 c0x0000 (---------------) + I tm 0x002000c1, // n0x16e7 c0x0000 (---------------) + I u 0x00200201, // n0x16e8 c0x0000 (---------------) + I w 0x002146c1, // n0x16e9 c0x0000 (---------------) + I x 0x00201181, // n0x16ea c0x0000 (---------------) + I y 0x00202301, // n0x16eb c0x0000 (---------------) + I z 0x0012d948, // n0x16ec c0x0000 (---------------) + blogspot 0x002370c3, // n0x16ed c0x0000 (---------------) + I com 0x00215b83, // n0x16ee c0x0000 (---------------) + I edu 0x00208ec3, // n0x16ef c0x0000 (---------------) + I gov 0x0024bdc3, // n0x16f0 c0x0000 (---------------) + I net 0x0024af03, // n0x16f1 c0x0000 (---------------) + I org 0x0023da83, // n0x16f2 c0x0000 (---------------) + I per 0x002370c3, // n0x16f3 c0x0000 (---------------) + I com 0x00208ec3, // n0x16f4 c0x0000 (---------------) + I gov 0x00210703, // n0x16f5 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x16f6 c0x0000 (---------------) + I net 0x0024af03, // n0x16f7 c0x0000 (---------------) + I org 0x0012d948, // n0x16f8 c0x0000 (---------------) + blogspot 0x002370c3, // n0x16f9 c0x0000 (---------------) + I com 0x00215b83, // n0x16fa c0x0000 (---------------) + I edu 0x00208ec3, // n0x16fb c0x0000 (---------------) + I gov 0x0024bdc3, // n0x16fc c0x0000 (---------------) + I net 0x0024af03, // n0x16fd c0x0000 (---------------) + I org 0x00209243, // n0x16fe c0x0000 (---------------) + I art 0x002370c3, // n0x16ff c0x0000 (---------------) + I com 0x00215b83, // n0x1700 c0x0000 (---------------) + I edu 0x002d80c4, // n0x1701 c0x0000 (---------------) + I gouv 0x0024af03, // n0x1702 c0x0000 (---------------) + I org 0x002f2c45, // n0x1703 c0x0000 (---------------) + I perso 0x002012c4, // n0x1704 c0x0000 (---------------) + I univ 0x002370c3, // n0x1705 c0x0000 (---------------) + I com 0x0024bdc3, // n0x1706 c0x0000 (---------------) + I net 0x0024af03, // n0x1707 c0x0000 (---------------) + I org 0x00209382, // n0x1708 c0x0000 (---------------) + I co 0x002370c3, // n0x1709 c0x0000 (---------------) + I com 0x00238c49, // n0x170a c0x0000 (---------------) + I consulado 0x00215b83, // n0x170b c0x0000 (---------------) + I edu 0x00298949, // n0x170c c0x0000 (---------------) + I embaixada 0x00208ec3, // n0x170d c0x0000 (---------------) + I gov 0x00210703, // n0x170e c0x0000 (---------------) + I mil 0x0024bdc3, // n0x170f c0x0000 (---------------) + I net 0x0024af03, // n0x1710 c0x0000 (---------------) + I org 0x002ce488, // n0x1711 c0x0000 (---------------) + I principe 0x00244fc7, // n0x1712 c0x0000 (---------------) + I saotome 0x002e3245, // n0x1713 c0x0000 (---------------) + I store 0x002370c3, // n0x1714 c0x0000 (---------------) + I com 0x00215b83, // n0x1715 c0x0000 (---------------) + I edu 0x00223ac3, // n0x1716 c0x0000 (---------------) + I gob 0x0024af03, // n0x1717 c0x0000 (---------------) + I org 0x00252643, // n0x1718 c0x0000 (---------------) + I red 0x00208ec3, // n0x1719 c0x0000 (---------------) + I gov 0x002370c3, // n0x171a c0x0000 (---------------) + I com 0x00215b83, // n0x171b c0x0000 (---------------) + I edu 0x00208ec3, // n0x171c c0x0000 (---------------) + I gov 0x00210703, // n0x171d c0x0000 (---------------) + I mil 0x0024bdc3, // n0x171e c0x0000 (---------------) + I net 0x0024af03, // n0x171f c0x0000 (---------------) + I org 0x0020a3c2, // n0x1720 c0x0000 (---------------) + I ac 0x00209382, // n0x1721 c0x0000 (---------------) + I co 0x0024af03, // n0x1722 c0x0000 (---------------) + I org 0x0012d948, // n0x1723 c0x0000 (---------------) + blogspot 0x0020a3c2, // n0x1724 c0x0000 (---------------) + I ac 0x00209382, // n0x1725 c0x0000 (---------------) + I co 0x00208ec2, // n0x1726 c0x0000 (---------------) + I go 0x00200942, // n0x1727 c0x0000 (---------------) + I in 0x00200742, // n0x1728 c0x0000 (---------------) + I mi 0x0024bdc3, // n0x1729 c0x0000 (---------------) + I net 0x00201002, // n0x172a c0x0000 (---------------) + I or 0x0020a3c2, // n0x172b c0x0000 (---------------) + I ac 0x00305743, // n0x172c c0x0000 (---------------) + I biz 0x00209382, // n0x172d c0x0000 (---------------) + I co 0x002370c3, // n0x172e c0x0000 (---------------) + I com 0x00215b83, // n0x172f c0x0000 (---------------) + I edu 0x00208ec2, // n0x1730 c0x0000 (---------------) + I go 0x00208ec3, // n0x1731 c0x0000 (---------------) + I gov 0x00216fc3, // n0x1732 c0x0000 (---------------) + I int 0x00210703, // n0x1733 c0x0000 (---------------) + I mil 0x002592c4, // n0x1734 c0x0000 (---------------) + I name 0x0024bdc3, // n0x1735 c0x0000 (---------------) + I net 0x00213103, // n0x1736 c0x0000 (---------------) + I nic 0x0024af03, // n0x1737 c0x0000 (---------------) + I org 0x0032d784, // n0x1738 c0x0000 (---------------) + I test 0x00200b03, // n0x1739 c0x0000 (---------------) + I web 0x00208ec3, // n0x173a c0x0000 (---------------) + I gov 0x00209382, // n0x173b c0x0000 (---------------) + I co 0x002370c3, // n0x173c c0x0000 (---------------) + I com 0x00215b83, // n0x173d c0x0000 (---------------) + I edu 0x00208ec3, // n0x173e c0x0000 (---------------) + I gov 0x00210703, // n0x173f c0x0000 (---------------) + I mil 0x0024bdc3, // n0x1740 c0x0000 (---------------) + I net 0x0020fc03, // n0x1741 c0x0000 (---------------) + I nom 0x0024af03, // n0x1742 c0x0000 (---------------) + I org 0x00314487, // n0x1743 c0x0000 (---------------) + I agrinet 0x002370c3, // n0x1744 c0x0000 (---------------) + I com 0x00327d47, // n0x1745 c0x0000 (---------------) + I defense 0x002c7746, // n0x1746 c0x0000 (---------------) + I edunet 0x00210183, // n0x1747 c0x0000 (---------------) + I ens 0x00247ac3, // n0x1748 c0x0000 (---------------) + I fin 0x00208ec3, // n0x1749 c0x0000 (---------------) + I gov 0x00201803, // n0x174a c0x0000 (---------------) + I ind 0x00214b84, // n0x174b c0x0000 (---------------) + I info 0x002b1f04, // n0x174c c0x0000 (---------------) + I intl 0x002b7d86, // n0x174d c0x0000 (---------------) + I mincom 0x0020c9c3, // n0x174e c0x0000 (---------------) + I nat 0x0024bdc3, // n0x174f c0x0000 (---------------) + I net 0x0024af03, // n0x1750 c0x0000 (---------------) + I org 0x002f2c45, // n0x1751 c0x0000 (---------------) + I perso 0x0034ab44, // n0x1752 c0x0000 (---------------) + I rnrt 0x00257203, // n0x1753 c0x0000 (---------------) + I rns 0x0027d803, // n0x1754 c0x0000 (---------------) + I rnu 0x002b7c07, // n0x1755 c0x0000 (---------------) + I tourism 0x00248f85, // n0x1756 c0x0000 (---------------) + I turen 0x002370c3, // n0x1757 c0x0000 (---------------) + I com 0x00215b83, // n0x1758 c0x0000 (---------------) + I edu 0x00208ec3, // n0x1759 c0x0000 (---------------) + I gov 0x00210703, // n0x175a c0x0000 (---------------) + I mil 0x0024bdc3, // n0x175b c0x0000 (---------------) + I net 0x0024af03, // n0x175c c0x0000 (---------------) + I org 0x49a0a5c2, // n0x175d c0x0126 (n0x175f-n0x1760) o I nc 0x00613103, // n0x175e c0x0001 (---------------) ! I nic 0x00208ec3, // n0x175f c0x0000 (---------------) + I gov 0x002728c4, // n0x1760 c0x0000 (---------------) + I aero 0x00305743, // n0x1761 c0x0000 (---------------) + I biz 0x00209382, // n0x1762 c0x0000 (---------------) + I co 0x002370c3, // n0x1763 c0x0000 (---------------) + I com 0x0023d9c4, // n0x1764 c0x0000 (---------------) + I coop 0x00215b83, // n0x1765 c0x0000 (---------------) + I edu 0x00208ec3, // n0x1766 c0x0000 (---------------) + I gov 0x00214b84, // n0x1767 c0x0000 (---------------) + I info 0x00216fc3, // n0x1768 c0x0000 (---------------) + I int 0x00315b44, // n0x1769 c0x0000 (---------------) + I jobs 0x0020d4c4, // n0x176a c0x0000 (---------------) + I mobi 0x002c3946, // n0x176b c0x0000 (---------------) + I museum 0x002592c4, // n0x176c c0x0000 (---------------) + I name 0x0024bdc3, // n0x176d c0x0000 (---------------) + I net 0x0024af03, // n0x176e c0x0000 (---------------) + I org 0x00206683, // n0x176f c0x0000 (---------------) + I pro 0x002a7006, // n0x1770 c0x0000 (---------------) + I travel 0x0004d24b, // n0x1771 c0x0000 (---------------) + better-than 0x00011746, // n0x1772 c0x0000 (---------------) + dyndns 0x0003e9ca, // n0x1773 c0x0000 (---------------) + on-the-web 0x0014020a, // n0x1774 c0x0000 (---------------) + worse-than 0x0012d948, // n0x1775 c0x0000 (---------------) + blogspot 0x002729c4, // n0x1776 c0x0000 (---------------) + I club 0x002370c3, // n0x1777 c0x0000 (---------------) + I com 0x00305704, // n0x1778 c0x0000 (---------------) + I ebiz 0x00215b83, // n0x1779 c0x0000 (---------------) + I edu 0x0024a604, // n0x177a c0x0000 (---------------) + I game 0x00208ec3, // n0x177b c0x0000 (---------------) + I gov 0x00273043, // n0x177c c0x0000 (---------------) + I idv 0x00210703, // n0x177d c0x0000 (---------------) + I mil 0x0024bdc3, // n0x177e c0x0000 (---------------) + I net 0x0024af03, // n0x177f c0x0000 (---------------) + I org 0x002fff0b, // n0x1780 c0x0000 (---------------) + I xn--czrw28b 0x00351aca, // n0x1781 c0x0000 (---------------) + I xn--uc0atv 0x0035a90c, // n0x1782 c0x0000 (---------------) + I xn--zf0ao64a 0x0020a3c2, // n0x1783 c0x0000 (---------------) + I ac 0x00209382, // n0x1784 c0x0000 (---------------) + I co 0x00208ec2, // n0x1785 c0x0000 (---------------) + I go 0x0029aa05, // n0x1786 c0x0000 (---------------) + I hotel 0x00214b84, // n0x1787 c0x0000 (---------------) + I info 0x00201582, // n0x1788 c0x0000 (---------------) + I me 0x00210703, // n0x1789 c0x0000 (---------------) + I mil 0x0020d4c4, // n0x178a c0x0000 (---------------) + I mobi 0x00200982, // n0x178b c0x0000 (---------------) + I ne 0x00201002, // n0x178c c0x0000 (---------------) + I or 0x00219202, // n0x178d c0x0000 (---------------) + I sc 0x00288182, // n0x178e c0x0000 (---------------) + I tv 0x002a7e09, // n0x178f c0x0000 (---------------) + I cherkassy 0x0027f288, // n0x1790 c0x0000 (---------------) + I cherkasy 0x00283cc9, // n0x1791 c0x0000 (---------------) + I chernigov 0x00293e09, // n0x1792 c0x0000 (---------------) + I chernihiv 0x0029774a, // n0x1793 c0x0000 (---------------) + I chernivtsi 0x0029900a, // n0x1794 c0x0000 (---------------) + I chernovtsy 0x00200542, // n0x1795 c0x0000 (---------------) + I ck 0x00234a02, // n0x1796 c0x0000 (---------------) + I cn 0x00209382, // n0x1797 c0x0000 (---------------) + I co 0x002370c3, // n0x1798 c0x0000 (---------------) + I com 0x002148c2, // n0x1799 c0x0000 (---------------) + I cr 0x00244606, // n0x179a c0x0000 (---------------) + I crimea 0x00322b42, // n0x179b c0x0000 (---------------) + I cv 0x002065c2, // n0x179c c0x0000 (---------------) + I dn 0x0032f58e, // n0x179d c0x0000 (---------------) + I dnepropetrovsk 0x002065ce, // n0x179e c0x0000 (---------------) + I dnipropetrovsk 0x0026e9c7, // n0x179f c0x0000 (---------------) + I dominic 0x002b9c47, // n0x17a0 c0x0000 (---------------) + I donetsk 0x0020e002, // n0x17a1 c0x0000 (---------------) + I dp 0x00215b83, // n0x17a2 c0x0000 (---------------) + I edu 0x00208ec3, // n0x17a3 c0x0000 (---------------) + I gov 0x00200042, // n0x17a4 c0x0000 (---------------) + I if 0x00200942, // n0x17a5 c0x0000 (---------------) + I in 0x002f204f, // n0x17a6 c0x0000 (---------------) + I ivano-frankivsk 0x0022ccc2, // n0x17a7 c0x0000 (---------------) + I kh 0x00256f87, // n0x17a8 c0x0000 (---------------) + I kharkiv 0x0025d207, // n0x17a9 c0x0000 (---------------) + I kharkov 0x00266547, // n0x17aa c0x0000 (---------------) + I kherson 0x0027c54c, // n0x17ab c0x0000 (---------------) + I khmelnitskiy 0x0027ebcc, // n0x17ac c0x0000 (---------------) + I khmelnytskyi 0x00205d84, // n0x17ad c0x0000 (---------------) + I kiev 0x00222b4a, // n0x17ae c0x0000 (---------------) + I kirovograd 0x002de142, // n0x17af c0x0000 (---------------) + I km 0x0020d382, // n0x17b0 c0x0000 (---------------) + I kr 0x002a5484, // n0x17b1 c0x0000 (---------------) + I krym 0x00211642, // n0x17b2 c0x0000 (---------------) + I ks 0x00263cc2, // n0x17b3 c0x0000 (---------------) + I kv 0x0027ee04, // n0x17b4 c0x0000 (---------------) + I kyiv 0x00213202, // n0x17b5 c0x0000 (---------------) + I lg 0x0021e302, // n0x17b6 c0x0000 (---------------) + I lt 0x0030b687, // n0x17b7 c0x0000 (---------------) + I lugansk 0x00230b05, // n0x17b8 c0x0000 (---------------) + I lutsk 0x00205742, // n0x17b9 c0x0000 (---------------) + I lv 0x00241d44, // n0x17ba c0x0000 (---------------) + I lviv 0x00332142, // n0x17bb c0x0000 (---------------) + I mk 0x002f1ec8, // n0x17bc c0x0000 (---------------) + I mykolaiv 0x0024bdc3, // n0x17bd c0x0000 (---------------) + I net 0x0020d988, // n0x17be c0x0000 (---------------) + I nikolaev 0x00202e42, // n0x17bf c0x0000 (---------------) + I od 0x0023b805, // n0x17c0 c0x0000 (---------------) + I odesa 0x003379c6, // n0x17c1 c0x0000 (---------------) + I odessa 0x0024af03, // n0x17c2 c0x0000 (---------------) + I org 0x0020a342, // n0x17c3 c0x0000 (---------------) + I pl 0x002cbe47, // n0x17c4 c0x0000 (---------------) + I poltava 0x00202a42, // n0x17c5 c0x0000 (---------------) + I pp 0x002ce985, // n0x17c6 c0x0000 (---------------) + I rivne 0x00242585, // n0x17c7 c0x0000 (---------------) + I rovno 0x002265c2, // n0x17c8 c0x0000 (---------------) + I rv 0x00235482, // n0x17c9 c0x0000 (---------------) + I sb 0x002fac4a, // n0x17ca c0x0000 (---------------) + I sebastopol 0x002d41ca, // n0x17cb c0x0000 (---------------) + I sevastopol 0x00210082, // n0x17cc c0x0000 (---------------) + I sm 0x002a1084, // n0x17cd c0x0000 (---------------) + I sumy 0x00200c42, // n0x17ce c0x0000 (---------------) + I te 0x003113c8, // n0x17cf c0x0000 (---------------) + I ternopil 0x0022acc2, // n0x17d0 c0x0000 (---------------) + I uz 0x00238448, // n0x17d1 c0x0000 (---------------) + I uzhgorod 0x002eb287, // n0x17d2 c0x0000 (---------------) + I vinnica 0x002eb809, // n0x17d3 c0x0000 (---------------) + I vinnytsia 0x0020d942, // n0x17d4 c0x0000 (---------------) + I vn 0x002eecc5, // n0x17d5 c0x0000 (---------------) + I volyn 0x0032ff05, // n0x17d6 c0x0000 (---------------) + I yalta 0x002d82cb, // n0x17d7 c0x0000 (---------------) + I zaporizhzhe 0x002ed34c, // n0x17d8 c0x0000 (---------------) + I zaporizhzhia 0x002ef088, // n0x17d9 c0x0000 (---------------) + I zhitomir 0x0022d1c8, // n0x17da c0x0000 (---------------) + I zhytomyr 0x002d1682, // n0x17db c0x0000 (---------------) + I zp 0x00246042, // n0x17dc c0x0000 (---------------) + I zt 0x0020a3c2, // n0x17dd c0x0000 (---------------) + I ac 0x00209382, // n0x17de c0x0000 (---------------) + I co 0x002370c3, // n0x17df c0x0000 (---------------) + I com 0x00208ec2, // n0x17e0 c0x0000 (---------------) + I go 0x00200982, // n0x17e1 c0x0000 (---------------) + I ne 0x00201002, // n0x17e2 c0x0000 (---------------) + I or 0x0024af03, // n0x17e3 c0x0000 (---------------) + I org 0x00219202, // n0x17e4 c0x0000 (---------------) + I sc 0x0060e8c2, // n0x17e5 c0x0001 (---------------) ! I bl 0x0061d30f, // n0x17e6 c0x0001 (---------------) ! I british-library 0x4ba09382, // n0x17e7 c0x012e (n0x17f0-n0x17f1) o I co 0x00645f83, // n0x17e8 c0x0001 (---------------) ! I jet 0x00602e03, // n0x17e9 c0x0001 (---------------) ! I mod 0x00618dd9, // n0x17ea c0x0001 (---------------) ! I national-library-scotland 0x00623c03, // n0x17eb c0x0001 (---------------) ! I nel 0x00613103, // n0x17ec c0x0001 (---------------) ! I nic 0x006acf43, // n0x17ed c0x0001 (---------------) ! I nls 0x0063c48a, // n0x17ee c0x0001 (---------------) ! I parliament 0x0165e643, // n0x17ef c0x0005 (---------------)* o I sch 0x0012d948, // n0x17f0 c0x0000 (---------------) + blogspot 0x4c201d02, // n0x17f1 c0x0130 (n0x1830-n0x1833) + I ak 0x4c601782, // n0x17f2 c0x0131 (n0x1833-n0x1836) + I al 0x4ca02b82, // n0x17f3 c0x0132 (n0x1836-n0x1839) + I ar 0x4ce005c2, // n0x17f4 c0x0133 (n0x1839-n0x183c) + I as 0x4d203dc2, // n0x17f5 c0x0134 (n0x183c-n0x183f) + I az 0x4d613182, // n0x17f6 c0x0135 (n0x183f-n0x1842) + I ca 0x4da09382, // n0x17f7 c0x0136 (n0x1842-n0x1845) + I co 0x4de236c2, // n0x17f8 c0x0137 (n0x1845-n0x1848) + I ct 0x4e214882, // n0x17f9 c0x0138 (n0x1848-n0x184b) + I dc 0x4e603442, // n0x17fa c0x0139 (n0x184b-n0x184e) + I de 0x002065c3, // n0x17fb c0x0000 (---------------) + I dni 0x0022bd03, // n0x17fc c0x0000 (---------------) + I fed 0x4ea49a82, // n0x17fd c0x013a (n0x184e-n0x1851) + I fl 0x4ee00182, // n0x17fe c0x013b (n0x1851-n0x1854) + I ga 0x4f201282, // n0x17ff c0x013c (n0x1854-n0x1857) + I gu 0x4f601e02, // n0x1800 c0x013d (n0x1857-n0x1859) + I hi 0x4fa01c42, // n0x1801 c0x013e (n0x1859-n0x185c) + I ia 0x4fe03d42, // n0x1802 c0x013f (n0x185c-n0x185f) + I id 0x50205bc2, // n0x1803 c0x0140 (n0x185f-n0x1862) + I il 0x50600942, // n0x1804 c0x0141 (n0x1862-n0x1865) + I in 0x000c5185, // n0x1805 c0x0000 (---------------) + is-by 0x0022ec43, // n0x1806 c0x0000 (---------------) + I isa 0x00357e04, // n0x1807 c0x0000 (---------------) + I kids 0x50a11642, // n0x1808 c0x0142 (n0x1865-n0x1868) + I ks 0x50e07fc2, // n0x1809 c0x0143 (n0x1868-n0x186b) + I ky 0x512026c2, // n0x180a c0x0144 (n0x186b-n0x186e) + I la 0x0008ec4b, // n0x180b c0x0000 (---------------) + land-4-sale 0x51604242, // n0x180c c0x0145 (n0x186e-n0x1871) + I ma 0x51e0f882, // n0x180d c0x0147 (n0x1874-n0x1877) + I md 0x52201582, // n0x180e c0x0148 (n0x1877-n0x187a) + I me 0x52600742, // n0x180f c0x0149 (n0x187a-n0x187d) + I mi 0x52a0fb42, // n0x1810 c0x014a (n0x187d-n0x1880) + I mn 0x52e02e02, // n0x1811 c0x014b (n0x1880-n0x1883) + I mo 0x5320bb42, // n0x1812 c0x014c (n0x1883-n0x1886) + I ms 0x5365f982, // n0x1813 c0x014d (n0x1886-n0x1889) + I mt 0x53a0a5c2, // n0x1814 c0x014e (n0x1889-n0x188c) + I nc 0x53e003c2, // n0x1815 c0x014f (n0x188c-n0x188f) + I nd 0x54200982, // n0x1816 c0x0150 (n0x188f-n0x1892) + I ne 0x5460d882, // n0x1817 c0x0151 (n0x1892-n0x1895) + I nh 0x54a18382, // n0x1818 c0x0152 (n0x1895-n0x1898) + I nj 0x54e10f82, // n0x1819 c0x0153 (n0x1898-n0x189b) + I nm 0x002b0203, // n0x181a c0x0000 (---------------) + I nsn 0x5523b502, // n0x181b c0x0154 (n0x189b-n0x189e) + I nv 0x55615442, // n0x181c c0x0155 (n0x189e-n0x18a1) + I ny 0x55a07d02, // n0x181d c0x0156 (n0x18a1-n0x18a4) + I oh 0x55e00682, // n0x181e c0x0157 (n0x18a4-n0x18a7) + I ok 0x56201002, // n0x181f c0x0158 (n0x18a7-n0x18aa) + I or 0x56602642, // n0x1820 c0x0159 (n0x18aa-n0x18ad) + I pa 0x56a06682, // n0x1821 c0x015a (n0x18ad-n0x18b0) + I pr 0x56e04cc2, // n0x1822 c0x015b (n0x18b0-n0x18b3) + I ri 0x57219202, // n0x1823 c0x015c (n0x18b3-n0x18b6) + I sc 0x5763c8c2, // n0x1824 c0x015d (n0x18b6-n0x18b8) + I sd 0x000e3a0c, // n0x1825 c0x0000 (---------------) + stuff-4-sale 0x57a075c2, // n0x1826 c0x015e (n0x18b8-n0x18bb) + I tn 0x57e5e7c2, // n0x1827 c0x015f (n0x18bb-n0x18be) + I tx 0x58215702, // n0x1828 c0x0160 (n0x18be-n0x18c1) + I ut 0x58603302, // n0x1829 c0x0161 (n0x18c1-n0x18c4) + I va 0x58a05782, // n0x182a c0x0162 (n0x18c4-n0x18c7) + I vi 0x58e68dc2, // n0x182b c0x0163 (n0x18c7-n0x18ca) + I vt 0x59200202, // n0x182c c0x0164 (n0x18ca-n0x18cd) + I wa 0x59611282, // n0x182d c0x0165 (n0x18cd-n0x18d0) + I wi 0x59a69602, // n0x182e c0x0166 (n0x18d0-n0x18d1) + I wv 0x59e44942, // n0x182f c0x0167 (n0x18d1-n0x18d4) + I wy 0x00223542, // n0x1830 c0x0000 (---------------) + I cc 0x003029c3, // n0x1831 c0x0000 (---------------) + I k12 0x00219003, // n0x1832 c0x0000 (---------------) + I lib 0x00223542, // n0x1833 c0x0000 (---------------) + I cc 0x003029c3, // n0x1834 c0x0000 (---------------) + I k12 0x00219003, // n0x1835 c0x0000 (---------------) + I lib 0x00223542, // n0x1836 c0x0000 (---------------) + I cc 0x003029c3, // n0x1837 c0x0000 (---------------) + I k12 0x00219003, // n0x1838 c0x0000 (---------------) + I lib 0x00223542, // n0x1839 c0x0000 (---------------) + I cc 0x003029c3, // n0x183a c0x0000 (---------------) + I k12 0x00219003, // n0x183b c0x0000 (---------------) + I lib 0x00223542, // n0x183c c0x0000 (---------------) + I cc 0x003029c3, // n0x183d c0x0000 (---------------) + I k12 0x00219003, // n0x183e c0x0000 (---------------) + I lib 0x00223542, // n0x183f c0x0000 (---------------) + I cc 0x003029c3, // n0x1840 c0x0000 (---------------) + I k12 0x00219003, // n0x1841 c0x0000 (---------------) + I lib 0x00223542, // n0x1842 c0x0000 (---------------) + I cc 0x003029c3, // n0x1843 c0x0000 (---------------) + I k12 0x00219003, // n0x1844 c0x0000 (---------------) + I lib 0x00223542, // n0x1845 c0x0000 (---------------) + I cc 0x003029c3, // n0x1846 c0x0000 (---------------) + I k12 0x00219003, // n0x1847 c0x0000 (---------------) + I lib 0x00223542, // n0x1848 c0x0000 (---------------) + I cc 0x003029c3, // n0x1849 c0x0000 (---------------) + I k12 0x00219003, // n0x184a c0x0000 (---------------) + I lib 0x00223542, // n0x184b c0x0000 (---------------) + I cc 0x003029c3, // n0x184c c0x0000 (---------------) + I k12 0x00219003, // n0x184d c0x0000 (---------------) + I lib 0x00223542, // n0x184e c0x0000 (---------------) + I cc 0x003029c3, // n0x184f c0x0000 (---------------) + I k12 0x00219003, // n0x1850 c0x0000 (---------------) + I lib 0x00223542, // n0x1851 c0x0000 (---------------) + I cc 0x003029c3, // n0x1852 c0x0000 (---------------) + I k12 0x00219003, // n0x1853 c0x0000 (---------------) + I lib 0x00223542, // n0x1854 c0x0000 (---------------) + I cc 0x003029c3, // n0x1855 c0x0000 (---------------) + I k12 0x00219003, // n0x1856 c0x0000 (---------------) + I lib 0x00223542, // n0x1857 c0x0000 (---------------) + I cc 0x00219003, // n0x1858 c0x0000 (---------------) + I lib 0x00223542, // n0x1859 c0x0000 (---------------) + I cc 0x003029c3, // n0x185a c0x0000 (---------------) + I k12 0x00219003, // n0x185b c0x0000 (---------------) + I lib 0x00223542, // n0x185c c0x0000 (---------------) + I cc 0x003029c3, // n0x185d c0x0000 (---------------) + I k12 0x00219003, // n0x185e c0x0000 (---------------) + I lib 0x00223542, // n0x185f c0x0000 (---------------) + I cc 0x003029c3, // n0x1860 c0x0000 (---------------) + I k12 0x00219003, // n0x1861 c0x0000 (---------------) + I lib 0x00223542, // n0x1862 c0x0000 (---------------) + I cc 0x003029c3, // n0x1863 c0x0000 (---------------) + I k12 0x00219003, // n0x1864 c0x0000 (---------------) + I lib 0x00223542, // n0x1865 c0x0000 (---------------) + I cc 0x003029c3, // n0x1866 c0x0000 (---------------) + I k12 0x00219003, // n0x1867 c0x0000 (---------------) + I lib 0x00223542, // n0x1868 c0x0000 (---------------) + I cc 0x003029c3, // n0x1869 c0x0000 (---------------) + I k12 0x00219003, // n0x186a c0x0000 (---------------) + I lib 0x00223542, // n0x186b c0x0000 (---------------) + I cc 0x003029c3, // n0x186c c0x0000 (---------------) + I k12 0x00219003, // n0x186d c0x0000 (---------------) + I lib 0x00223542, // n0x186e c0x0000 (---------------) + I cc 0x51b029c3, // n0x186f c0x0146 (n0x1871-n0x1874) + I k12 0x00219003, // n0x1870 c0x0000 (---------------) + I lib 0x00327a04, // n0x1871 c0x0000 (---------------) + I chtr 0x0030c386, // n0x1872 c0x0000 (---------------) + I paroch 0x002d16c3, // n0x1873 c0x0000 (---------------) + I pvt 0x00223542, // n0x1874 c0x0000 (---------------) + I cc 0x003029c3, // n0x1875 c0x0000 (---------------) + I k12 0x00219003, // n0x1876 c0x0000 (---------------) + I lib 0x00223542, // n0x1877 c0x0000 (---------------) + I cc 0x003029c3, // n0x1878 c0x0000 (---------------) + I k12 0x00219003, // n0x1879 c0x0000 (---------------) + I lib 0x00223542, // n0x187a c0x0000 (---------------) + I cc 0x003029c3, // n0x187b c0x0000 (---------------) + I k12 0x00219003, // n0x187c c0x0000 (---------------) + I lib 0x00223542, // n0x187d c0x0000 (---------------) + I cc 0x003029c3, // n0x187e c0x0000 (---------------) + I k12 0x00219003, // n0x187f c0x0000 (---------------) + I lib 0x00223542, // n0x1880 c0x0000 (---------------) + I cc 0x003029c3, // n0x1881 c0x0000 (---------------) + I k12 0x00219003, // n0x1882 c0x0000 (---------------) + I lib 0x00223542, // n0x1883 c0x0000 (---------------) + I cc 0x003029c3, // n0x1884 c0x0000 (---------------) + I k12 0x00219003, // n0x1885 c0x0000 (---------------) + I lib 0x00223542, // n0x1886 c0x0000 (---------------) + I cc 0x003029c3, // n0x1887 c0x0000 (---------------) + I k12 0x00219003, // n0x1888 c0x0000 (---------------) + I lib 0x00223542, // n0x1889 c0x0000 (---------------) + I cc 0x003029c3, // n0x188a c0x0000 (---------------) + I k12 0x00219003, // n0x188b c0x0000 (---------------) + I lib 0x00223542, // n0x188c c0x0000 (---------------) + I cc 0x003029c3, // n0x188d c0x0000 (---------------) + I k12 0x00219003, // n0x188e c0x0000 (---------------) + I lib 0x00223542, // n0x188f c0x0000 (---------------) + I cc 0x003029c3, // n0x1890 c0x0000 (---------------) + I k12 0x00219003, // n0x1891 c0x0000 (---------------) + I lib 0x00223542, // n0x1892 c0x0000 (---------------) + I cc 0x003029c3, // n0x1893 c0x0000 (---------------) + I k12 0x00219003, // n0x1894 c0x0000 (---------------) + I lib 0x00223542, // n0x1895 c0x0000 (---------------) + I cc 0x003029c3, // n0x1896 c0x0000 (---------------) + I k12 0x00219003, // n0x1897 c0x0000 (---------------) + I lib 0x00223542, // n0x1898 c0x0000 (---------------) + I cc 0x003029c3, // n0x1899 c0x0000 (---------------) + I k12 0x00219003, // n0x189a c0x0000 (---------------) + I lib 0x00223542, // n0x189b c0x0000 (---------------) + I cc 0x003029c3, // n0x189c c0x0000 (---------------) + I k12 0x00219003, // n0x189d c0x0000 (---------------) + I lib 0x00223542, // n0x189e c0x0000 (---------------) + I cc 0x003029c3, // n0x189f c0x0000 (---------------) + I k12 0x00219003, // n0x18a0 c0x0000 (---------------) + I lib 0x00223542, // n0x18a1 c0x0000 (---------------) + I cc 0x003029c3, // n0x18a2 c0x0000 (---------------) + I k12 0x00219003, // n0x18a3 c0x0000 (---------------) + I lib 0x00223542, // n0x18a4 c0x0000 (---------------) + I cc 0x003029c3, // n0x18a5 c0x0000 (---------------) + I k12 0x00219003, // n0x18a6 c0x0000 (---------------) + I lib 0x00223542, // n0x18a7 c0x0000 (---------------) + I cc 0x003029c3, // n0x18a8 c0x0000 (---------------) + I k12 0x00219003, // n0x18a9 c0x0000 (---------------) + I lib 0x00223542, // n0x18aa c0x0000 (---------------) + I cc 0x003029c3, // n0x18ab c0x0000 (---------------) + I k12 0x00219003, // n0x18ac c0x0000 (---------------) + I lib 0x00223542, // n0x18ad c0x0000 (---------------) + I cc 0x003029c3, // n0x18ae c0x0000 (---------------) + I k12 0x00219003, // n0x18af c0x0000 (---------------) + I lib 0x00223542, // n0x18b0 c0x0000 (---------------) + I cc 0x003029c3, // n0x18b1 c0x0000 (---------------) + I k12 0x00219003, // n0x18b2 c0x0000 (---------------) + I lib 0x00223542, // n0x18b3 c0x0000 (---------------) + I cc 0x003029c3, // n0x18b4 c0x0000 (---------------) + I k12 0x00219003, // n0x18b5 c0x0000 (---------------) + I lib 0x00223542, // n0x18b6 c0x0000 (---------------) + I cc 0x00219003, // n0x18b7 c0x0000 (---------------) + I lib 0x00223542, // n0x18b8 c0x0000 (---------------) + I cc 0x003029c3, // n0x18b9 c0x0000 (---------------) + I k12 0x00219003, // n0x18ba c0x0000 (---------------) + I lib 0x00223542, // n0x18bb c0x0000 (---------------) + I cc 0x003029c3, // n0x18bc c0x0000 (---------------) + I k12 0x00219003, // n0x18bd c0x0000 (---------------) + I lib 0x00223542, // n0x18be c0x0000 (---------------) + I cc 0x003029c3, // n0x18bf c0x0000 (---------------) + I k12 0x00219003, // n0x18c0 c0x0000 (---------------) + I lib 0x00223542, // n0x18c1 c0x0000 (---------------) + I cc 0x003029c3, // n0x18c2 c0x0000 (---------------) + I k12 0x00219003, // n0x18c3 c0x0000 (---------------) + I lib 0x00223542, // n0x18c4 c0x0000 (---------------) + I cc 0x003029c3, // n0x18c5 c0x0000 (---------------) + I k12 0x00219003, // n0x18c6 c0x0000 (---------------) + I lib 0x00223542, // n0x18c7 c0x0000 (---------------) + I cc 0x003029c3, // n0x18c8 c0x0000 (---------------) + I k12 0x00219003, // n0x18c9 c0x0000 (---------------) + I lib 0x00223542, // n0x18ca c0x0000 (---------------) + I cc 0x003029c3, // n0x18cb c0x0000 (---------------) + I k12 0x00219003, // n0x18cc c0x0000 (---------------) + I lib 0x00223542, // n0x18cd c0x0000 (---------------) + I cc 0x003029c3, // n0x18ce c0x0000 (---------------) + I k12 0x00219003, // n0x18cf c0x0000 (---------------) + I lib 0x00223542, // n0x18d0 c0x0000 (---------------) + I cc 0x00223542, // n0x18d1 c0x0000 (---------------) + I cc 0x003029c3, // n0x18d2 c0x0000 (---------------) + I k12 0x00219003, // n0x18d3 c0x0000 (---------------) + I lib 0x002370c3, // n0x18d4 c0x0000 (---------------) + I com 0x00215b83, // n0x18d5 c0x0000 (---------------) + I edu 0x00204f43, // n0x18d6 c0x0000 (---------------) + I gub 0x00210703, // n0x18d7 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x18d8 c0x0000 (---------------) + I net 0x0024af03, // n0x18d9 c0x0000 (---------------) + I org 0x00209382, // n0x18da c0x0000 (---------------) + I co 0x002370c3, // n0x18db c0x0000 (---------------) + I com 0x0024bdc3, // n0x18dc c0x0000 (---------------) + I net 0x0024af03, // n0x18dd c0x0000 (---------------) + I org 0x002370c3, // n0x18de c0x0000 (---------------) + I com 0x00215b83, // n0x18df c0x0000 (---------------) + I edu 0x00208ec3, // n0x18e0 c0x0000 (---------------) + I gov 0x00210703, // n0x18e1 c0x0000 (---------------) + I mil 0x0024bdc3, // n0x18e2 c0x0000 (---------------) + I net 0x0024af03, // n0x18e3 c0x0000 (---------------) + I org 0x00209382, // n0x18e4 c0x0000 (---------------) + I co 0x002370c3, // n0x18e5 c0x0000 (---------------) + I com 0x00205ac3, // n0x18e6 c0x0000 (---------------) + I e12 0x00215b83, // n0x18e7 c0x0000 (---------------) + I edu 0x00208ec3, // n0x18e8 c0x0000 (---------------) + I gov 0x00214b84, // n0x18e9 c0x0000 (---------------) + I info 0x00210703, // n0x18ea c0x0000 (---------------) + I mil 0x0024bdc3, // n0x18eb c0x0000 (---------------) + I net 0x0024af03, // n0x18ec c0x0000 (---------------) + I org 0x00200b03, // n0x18ed c0x0000 (---------------) + I web 0x00209382, // n0x18ee c0x0000 (---------------) + I co 0x002370c3, // n0x18ef c0x0000 (---------------) + I com 0x003029c3, // n0x18f0 c0x0000 (---------------) + I k12 0x0024bdc3, // n0x18f1 c0x0000 (---------------) + I net 0x0024af03, // n0x18f2 c0x0000 (---------------) + I org 0x0020a3c2, // n0x18f3 c0x0000 (---------------) + I ac 0x00305743, // n0x18f4 c0x0000 (---------------) + I biz 0x002370c3, // n0x18f5 c0x0000 (---------------) + I com 0x00215b83, // n0x18f6 c0x0000 (---------------) + I edu 0x00208ec3, // n0x18f7 c0x0000 (---------------) + I gov 0x00297d06, // n0x18f8 c0x0000 (---------------) + I health 0x00214b84, // n0x18f9 c0x0000 (---------------) + I info 0x00216fc3, // n0x18fa c0x0000 (---------------) + I int 0x002592c4, // n0x18fb c0x0000 (---------------) + I name 0x0024bdc3, // n0x18fc c0x0000 (---------------) + I net 0x0024af03, // n0x18fd c0x0000 (---------------) + I org 0x00206683, // n0x18fe c0x0000 (---------------) + I pro 0x002370c3, // n0x18ff c0x0000 (---------------) + I com 0x00011746, // n0x1900 c0x0000 (---------------) + dyndns 0x00215b83, // n0x1901 c0x0000 (---------------) + I edu 0x00208ec3, // n0x1902 c0x0000 (---------------) + I gov 0x000a1106, // n0x1903 c0x0000 (---------------) + mypets 0x0024bdc3, // n0x1904 c0x0000 (---------------) + I net 0x0024af03, // n0x1905 c0x0000 (---------------) + I org } // children is the list of nodes' children, the parent's wildcard bit and the // parent's node type. If a node has no children then their children index // will be in the range [0, 6), depending on the wildcard bit and node type. // // The layout within the uint32, from MSB to LSB, is: // [ 1 bits] unused // [ 1 bits] wildcard bit // [ 2 bits] node type // [14 bits] high nodes index (exclusive) of children // [14 bits] low nodes index (inclusive) of children var children = [...]uint32{ 0x00000000, // c0x0000 (---------------) + 0x10000000, // c0x0001 (---------------) ! 0x20000000, // c0x0002 (---------------) o 0x40000000, // c0x0003 (---------------)* + 0x50000000, // c0x0004 (---------------)* ! 0x60000000, // c0x0005 (---------------)* o 0x008d8230, // c0x0006 (n0x0230-n0x0236) + 0x008dc236, // c0x0007 (n0x0236-n0x0237) + 0x008f8237, // c0x0008 (n0x0237-n0x023e) + 0x00a5c23e, // c0x0009 (n0x023e-n0x0297) + 0x00a70297, // c0x000a (n0x0297-n0x029c) + 0x00a8429c, // c0x000b (n0x029c-n0x02a1) + 0x00a942a1, // c0x000c (n0x02a1-n0x02a5) + 0x00aac2a5, // c0x000d (n0x02a5-n0x02ab) + 0x00abc2ab, // c0x000e (n0x02ab-n0x02af) + 0x00ad42af, // c0x000f (n0x02af-n0x02b5) + 0x00af42b5, // c0x0010 (n0x02b5-n0x02bd) + 0x00af82bd, // c0x0011 (n0x02bd-n0x02be) + 0x00b102be, // c0x0012 (n0x02be-n0x02c4) + 0x00b142c4, // c0x0013 (n0x02c4-n0x02c5) + 0x00b302c5, // c0x0014 (n0x02c5-n0x02cc) + 0x00b342cc, // c0x0015 (n0x02cc-n0x02cd) + 0x00b802cd, // c0x0016 (n0x02cd-n0x02e0) + 0x00b842e0, // c0x0017 (n0x02e0-n0x02e1) + 0x00ba42e1, // c0x0018 (n0x02e1-n0x02e9) + 0x00bbc2e9, // c0x0019 (n0x02e9-n0x02ef) + 0x00bc02ef, // c0x001a (n0x02ef-n0x02f0) + 0x00bf02f0, // c0x001b (n0x02f0-n0x02fc) + 0x00c182fc, // c0x001c (n0x02fc-n0x0306) + 0x00c38306, // c0x001d (n0x0306-n0x030e) + 0x00c4030e, // c0x001e (n0x030e-n0x0310) + 0x00c44310, // c0x001f (n0x0310-n0x0311) + 0x00cd4311, // c0x0020 (n0x0311-n0x0335) + 0x00ce8335, // c0x0021 (n0x0335-n0x033a) + 0x00cfc33a, // c0x0022 (n0x033a-n0x033f) + 0x00d1833f, // c0x0023 (n0x033f-n0x0346) + 0x00d28346, // c0x0024 (n0x0346-n0x034a) + 0x00d3c34a, // c0x0025 (n0x034a-n0x034f) + 0x00d6034f, // c0x0026 (n0x034f-n0x0358) + 0x00e74358, // c0x0027 (n0x0358-n0x039d) + 0x00e7839d, // c0x0028 (n0x039d-n0x039e) + 0x00e8c39e, // c0x0029 (n0x039e-n0x03a3) + 0x00ea03a3, // c0x002a (n0x03a3-n0x03a8) + 0x00ea83a8, // c0x002b (n0x03a8-n0x03aa) + 0x00eb83aa, // c0x002c (n0x03aa-n0x03ae) + 0x00ecc3ae, // c0x002d (n0x03ae-n0x03b3) + 0x00f103b3, // c0x002e (n0x03b3-n0x03c4) + 0x00f203c4, // c0x002f (n0x03c4-n0x03c8) + 0x00f243c8, // c0x0030 (n0x03c8-n0x03c9) + 0x00f283c9, // c0x0031 (n0x03c9-n0x03ca) + 0x00f2c3ca, // c0x0032 (n0x03ca-n0x03cb) + 0x00f683cb, // c0x0033 (n0x03cb-n0x03da) + 0x60f6c3da, // c0x0034 (n0x03da-n0x03db)* o 0x00f7c3db, // c0x0035 (n0x03db-n0x03df) + 0x00f803df, // c0x0036 (n0x03df-n0x03e0) + 0x010303e0, // c0x0037 (n0x03e0-n0x040c) + 0x0106440c, // c0x0038 (n0x040c-n0x0419) + 0x0134c419, // c0x0039 (n0x0419-n0x04d3) + 0x213a84d3, // c0x003a (n0x04d3-n0x04ea) o 0x013c84ea, // c0x003b (n0x04ea-n0x04f2) + 0x013d04f2, // c0x003c (n0x04f2-n0x04f4) + 0x013ec4f4, // c0x003d (n0x04f4-n0x04fb) + 0x014044fb, // c0x003e (n0x04fb-n0x0501) + 0x01408501, // c0x003f (n0x0501-n0x0502) + 0x01418502, // c0x0040 (n0x0502-n0x0506) + 0x01420506, // c0x0041 (n0x0506-n0x0508) + 0x01424508, // c0x0042 (n0x0508-n0x0509) + 0x01444509, // c0x0043 (n0x0509-n0x0511) + 0x01448511, // c0x0044 (n0x0511-n0x0512) + 0x0145c512, // c0x0045 (n0x0512-n0x0517) + 0x01484517, // c0x0046 (n0x0517-n0x0521) + 0x014a4521, // c0x0047 (n0x0521-n0x0529) + 0x014d4529, // c0x0048 (n0x0529-n0x0535) + 0x014fc535, // c0x0049 (n0x0535-n0x053f) + 0x0152053f, // c0x004a (n0x053f-n0x0548) + 0x01534548, // c0x004b (n0x0548-n0x054d) + 0x0153854d, // c0x004c (n0x054d-n0x054e) + 0x0154454e, // c0x004d (n0x054e-n0x0551) + 0x015a4551, // c0x004e (n0x0551-n0x0569) + 0x015c0569, // c0x004f (n0x0569-n0x0570) + 0x015cc570, // c0x0050 (n0x0570-n0x0573) + 0x015e0573, // c0x0051 (n0x0573-n0x0578) + 0x015f8578, // c0x0052 (n0x0578-n0x057e) + 0x0161057e, // c0x0053 (n0x057e-n0x0584) + 0x01628584, // c0x0054 (n0x0584-n0x058a) + 0x0164058a, // c0x0055 (n0x058a-n0x0590) + 0x0165c590, // c0x0056 (n0x0590-n0x0597) + 0x01668597, // c0x0057 (n0x0597-n0x059a) + 0x016c059a, // c0x0058 (n0x059a-n0x05b0) + 0x016d85b0, // c0x0059 (n0x05b0-n0x05b6) + 0x016e85b6, // c0x005a (n0x05b6-n0x05ba) + 0x0172c5ba, // c0x005b (n0x05ba-n0x05cb) + 0x017ac5cb, // c0x005c (n0x05cb-n0x05eb) + 0x017d45eb, // c0x005d (n0x05eb-n0x05f5) + 0x017dc5f5, // c0x005e (n0x05f5-n0x05f7) + 0x617e05f7, // c0x005f (n0x05f7-n0x05f8)* o 0x217e45f8, // c0x0060 (n0x05f8-n0x05f9) o 0x018005f9, // c0x0061 (n0x05f9-n0x0600) + 0x01808600, // c0x0062 (n0x0600-n0x0602) + 0x0183c602, // c0x0063 (n0x0602-n0x060f) + 0x0186460f, // c0x0064 (n0x060f-n0x0619) + 0x01868619, // c0x0065 (n0x0619-n0x061a) + 0x0187061a, // c0x0066 (n0x061a-n0x061c) + 0x0188861c, // c0x0067 (n0x061c-n0x0622) + 0x018ac622, // c0x0068 (n0x0622-n0x062b) + 0x018c862b, // c0x0069 (n0x062b-n0x0632) + 0x01d20632, // c0x006a (n0x0632-n0x0748) + 0x01d2c748, // c0x006b (n0x0748-n0x074b) + 0x01d4c74b, // c0x006c (n0x074b-n0x0753) + 0x01e4c753, // c0x006d (n0x0753-n0x0793) + 0x01f20793, // c0x006e (n0x0793-n0x07c8) + 0x01f907c8, // c0x006f (n0x07c8-n0x07e4) + 0x01fe87e4, // c0x0070 (n0x07e4-n0x07fa) + 0x020d07fa, // c0x0071 (n0x07fa-n0x0834) + 0x02128834, // c0x0072 (n0x0834-n0x084a) + 0x0216484a, // c0x0073 (n0x084a-n0x0859) + 0x02260859, // c0x0074 (n0x0859-n0x0898) + 0x0232c898, // c0x0075 (n0x0898-n0x08cb) + 0x023c48cb, // c0x0076 (n0x08cb-n0x08f1) + 0x024548f1, // c0x0077 (n0x08f1-n0x0915) + 0x024b8915, // c0x0078 (n0x0915-n0x092e) + 0x026f092e, // c0x0079 (n0x092e-n0x09bc) + 0x027a89bc, // c0x007a (n0x09bc-n0x09ea) + 0x028749ea, // c0x007b (n0x09ea-n0x0a1d) + 0x028c0a1d, // c0x007c (n0x0a1d-n0x0a30) + 0x0294ca30, // c0x007d (n0x0a30-n0x0a53) + 0x02988a53, // c0x007e (n0x0a53-n0x0a62) + 0x029d8a62, // c0x007f (n0x0a62-n0x0a76) + 0x02a50a76, // c0x0080 (n0x0a76-n0x0a94) + 0x62a54a94, // c0x0081 (n0x0a94-n0x0a95)* o 0x62a58a95, // c0x0082 (n0x0a95-n0x0a96)* o 0x62a5ca96, // c0x0083 (n0x0a96-n0x0a97)* o 0x02ad8a97, // c0x0084 (n0x0a97-n0x0ab6) + 0x02b40ab6, // c0x0085 (n0x0ab6-n0x0ad0) + 0x02bbcad0, // c0x0086 (n0x0ad0-n0x0aef) + 0x02c34aef, // c0x0087 (n0x0aef-n0x0b0d) + 0x02cb8b0d, // c0x0088 (n0x0b0d-n0x0b2e) + 0x02d24b2e, // c0x0089 (n0x0b2e-n0x0b49) + 0x02e50b49, // c0x008a (n0x0b49-n0x0b94) + 0x02ea8b94, // c0x008b (n0x0b94-n0x0baa) + 0x62eacbaa, // c0x008c (n0x0baa-n0x0bab)* o 0x02f44bab, // c0x008d (n0x0bab-n0x0bd1) + 0x02fccbd1, // c0x008e (n0x0bd1-n0x0bf3) + 0x03018bf3, // c0x008f (n0x0bf3-n0x0c06) + 0x03080c06, // c0x0090 (n0x0c06-n0x0c20) + 0x03128c20, // c0x0091 (n0x0c20-n0x0c4a) + 0x031f0c4a, // c0x0092 (n0x0c4a-n0x0c7c) + 0x03258c7c, // c0x0093 (n0x0c7c-n0x0c96) + 0x0336cc96, // c0x0094 (n0x0c96-n0x0cdb) + 0x63370cdb, // c0x0095 (n0x0cdb-n0x0cdc)* o 0x63374cdc, // c0x0096 (n0x0cdc-n0x0cdd)* o 0x033d0cdd, // c0x0097 (n0x0cdd-n0x0cf4) + 0x0342ccf4, // c0x0098 (n0x0cf4-n0x0d0b) + 0x034bcd0b, // c0x0099 (n0x0d0b-n0x0d2f) + 0x03538d2f, // c0x009a (n0x0d2f-n0x0d4e) + 0x0357cd4e, // c0x009b (n0x0d4e-n0x0d5f) + 0x03660d5f, // c0x009c (n0x0d5f-n0x0d98) + 0x03694d98, // c0x009d (n0x0d98-n0x0da5) + 0x036f4da5, // c0x009e (n0x0da5-n0x0dbd) + 0x03768dbd, // c0x009f (n0x0dbd-n0x0dda) + 0x037f0dda, // c0x00a0 (n0x0dda-n0x0dfc) + 0x03830dfc, // c0x00a1 (n0x0dfc-n0x0e0c) + 0x038a0e0c, // c0x00a2 (n0x0e0c-n0x0e28) + 0x638a4e28, // c0x00a3 (n0x0e28-n0x0e29)* o 0x038bce29, // c0x00a4 (n0x0e29-n0x0e2f) + 0x038d8e2f, // c0x00a5 (n0x0e2f-n0x0e36) + 0x0391ce36, // c0x00a6 (n0x0e36-n0x0e47) + 0x0392ce47, // c0x00a7 (n0x0e47-n0x0e4b) + 0x03944e4b, // c0x00a8 (n0x0e4b-n0x0e51) + 0x039bce51, // c0x00a9 (n0x0e51-n0x0e6f) + 0x039d0e6f, // c0x00aa (n0x0e6f-n0x0e74) + 0x039e8e74, // c0x00ab (n0x0e74-n0x0e7a) + 0x03a0ce7a, // c0x00ac (n0x0e7a-n0x0e83) + 0x03a20e83, // c0x00ad (n0x0e83-n0x0e88) + 0x03a38e88, // c0x00ae (n0x0e88-n0x0e8e) + 0x03a70e8e, // c0x00af (n0x0e8e-n0x0e9c) + 0x03a84e9c, // c0x00b0 (n0x0e9c-n0x0ea1) + 0x03a8cea1, // c0x00b1 (n0x0ea1-n0x0ea3) + 0x03a90ea3, // c0x00b2 (n0x0ea3-n0x0ea4) + 0x03ab4ea4, // c0x00b3 (n0x0ea4-n0x0ead) + 0x03ad8ead, // c0x00b4 (n0x0ead-n0x0eb6) + 0x03af0eb6, // c0x00b5 (n0x0eb6-n0x0ebc) + 0x03af8ebc, // c0x00b6 (n0x0ebc-n0x0ebe) + 0x03b18ebe, // c0x00b7 (n0x0ebe-n0x0ec6) + 0x03b38ec6, // c0x00b8 (n0x0ec6-n0x0ece) + 0x03b54ece, // c0x00b9 (n0x0ece-n0x0ed5) + 0x03b70ed5, // c0x00ba (n0x0ed5-n0x0edc) + 0x03b80edc, // c0x00bb (n0x0edc-n0x0ee0) + 0x03b94ee0, // c0x00bc (n0x0ee0-n0x0ee5) + 0x03b9cee5, // c0x00bd (n0x0ee5-n0x0ee7) + 0x03bacee7, // c0x00be (n0x0ee7-n0x0eeb) + 0x03bc8eeb, // c0x00bf (n0x0eeb-n0x0ef2) + 0x04458ef2, // c0x00c0 (n0x0ef2-n0x1116) + 0x04491116, // c0x00c1 (n0x1116-n0x1124) + 0x044bd124, // c0x00c2 (n0x1124-n0x112f) + 0x044d512f, // c0x00c3 (n0x112f-n0x1135) + 0x044f1135, // c0x00c4 (n0x1135-n0x113c) + 0x644f513c, // c0x00c5 (n0x113c-n0x113d)* o 0x0453913d, // c0x00c6 (n0x113d-n0x114e) + 0x0454114e, // c0x00c7 (n0x114e-n0x1150) + 0x24545150, // c0x00c8 (n0x1150-n0x1151) o 0x24549151, // c0x00c9 (n0x1151-n0x1152) o 0x0454d152, // c0x00ca (n0x1152-n0x1153) + 0x04605153, // c0x00cb (n0x1153-n0x1181) + 0x2460d181, // c0x00cc (n0x1181-n0x1183) o 0x24615183, // c0x00cd (n0x1183-n0x1185) o 0x24621185, // c0x00ce (n0x1185-n0x1188) o 0x04649188, // c0x00cf (n0x1188-n0x1192) + 0x0466d192, // c0x00d0 (n0x1192-n0x119b) + 0x0467919b, // c0x00d1 (n0x119b-n0x119e) + 0x051d119e, // c0x00d2 (n0x119e-n0x1474) + 0x051d5474, // c0x00d3 (n0x1474-n0x1475) + 0x051d9475, // c0x00d4 (n0x1475-n0x1476) + 0x251dd476, // c0x00d5 (n0x1476-n0x1477) o 0x051e1477, // c0x00d6 (n0x1477-n0x1478) + 0x251e5478, // c0x00d7 (n0x1478-n0x1479) o 0x051e9479, // c0x00d8 (n0x1479-n0x147a) + 0x251f547a, // c0x00d9 (n0x147a-n0x147d) o 0x051f947d, // c0x00da (n0x147d-n0x147e) + 0x051fd47e, // c0x00db (n0x147e-n0x147f) + 0x2520147f, // c0x00dc (n0x147f-n0x1480) o 0x05205480, // c0x00dd (n0x1480-n0x1481) + 0x2520d481, // c0x00de (n0x1481-n0x1483) o 0x05211483, // c0x00df (n0x1483-n0x1484) + 0x05215484, // c0x00e0 (n0x1484-n0x1485) + 0x25225485, // c0x00e1 (n0x1485-n0x1489) o 0x05229489, // c0x00e2 (n0x1489-n0x148a) + 0x0522d48a, // c0x00e3 (n0x148a-n0x148b) + 0x0523148b, // c0x00e4 (n0x148b-n0x148c) + 0x0523548c, // c0x00e5 (n0x148c-n0x148d) + 0x2523948d, // c0x00e6 (n0x148d-n0x148e) o 0x0523d48e, // c0x00e7 (n0x148e-n0x148f) + 0x0524148f, // c0x00e8 (n0x148f-n0x1490) + 0x05245490, // c0x00e9 (n0x1490-n0x1491) + 0x05249491, // c0x00ea (n0x1491-n0x1492) + 0x25251492, // c0x00eb (n0x1492-n0x1494) o 0x05255494, // c0x00ec (n0x1494-n0x1495) + 0x05259495, // c0x00ed (n0x1495-n0x1496) + 0x0525d496, // c0x00ee (n0x1496-n0x1497) + 0x25261497, // c0x00ef (n0x1497-n0x1498) o 0x05265498, // c0x00f0 (n0x1498-n0x1499) + 0x2526d499, // c0x00f1 (n0x1499-n0x149b) o 0x2527149b, // c0x00f2 (n0x149b-n0x149c) o 0x0528d49c, // c0x00f3 (n0x149c-n0x14a3) + 0x052994a3, // c0x00f4 (n0x14a3-n0x14a6) + 0x6529d4a6, // c0x00f5 (n0x14a6-n0x14a7)* o 0x252a14a7, // c0x00f6 (n0x14a7-n0x14a8) o 0x052c54a8, // c0x00f7 (n0x14a8-n0x14b1) + 0x053994b1, // c0x00f8 (n0x14b1-n0x14e6) + 0x053a14e6, // c0x00f9 (n0x14e6-n0x14e8) + 0x053cd4e8, // c0x00fa (n0x14e8-n0x14f3) + 0x053e94f3, // c0x00fb (n0x14f3-n0x14fa) + 0x053f54fa, // c0x00fc (n0x14fa-n0x14fd) + 0x054154fd, // c0x00fd (n0x14fd-n0x1505) + 0x0544d505, // c0x00fe (n0x1505-n0x1513) + 0x056f9513, // c0x00ff (n0x1513-n0x15be) + 0x0571d5be, // c0x0100 (n0x15be-n0x15c7) + 0x057315c7, // c0x0101 (n0x15c7-n0x15cc) + 0x057655cc, // c0x0102 (n0x15cc-n0x15d9) + 0x057815d9, // c0x0103 (n0x15d9-n0x15e0) + 0x0579d5e0, // c0x0104 (n0x15e0-n0x15e7) + 0x057c15e7, // c0x0105 (n0x15e7-n0x15f0) + 0x057d95f0, // c0x0106 (n0x15f0-n0x15f6) + 0x057f55f6, // c0x0107 (n0x15f6-n0x15fd) + 0x058155fd, // c0x0108 (n0x15fd-n0x1605) + 0x05825605, // c0x0109 (n0x1605-n0x1609) + 0x05855609, // c0x010a (n0x1609-n0x1615) + 0x0586d615, // c0x010b (n0x1615-n0x161b) + 0x05a8161b, // c0x010c (n0x161b-n0x16a0) + 0x05aa56a0, // c0x010d (n0x16a0-n0x16a9) + 0x05ac56a9, // c0x010e (n0x16a9-n0x16b1) + 0x05ad96b1, // c0x010f (n0x16b1-n0x16b6) + 0x05aed6b6, // c0x0110 (n0x16b6-n0x16bb) + 0x05b0d6bb, // c0x0111 (n0x16bb-n0x16c3) + 0x05bb16c3, // c0x0112 (n0x16c3-n0x16ec) + 0x05bcd6ec, // c0x0113 (n0x16ec-n0x16f3) + 0x05be16f3, // c0x0114 (n0x16f3-n0x16f8) + 0x05be56f8, // c0x0115 (n0x16f8-n0x16f9) + 0x05bf96f9, // c0x0116 (n0x16f9-n0x16fe) + 0x05c156fe, // c0x0117 (n0x16fe-n0x1705) + 0x05c21705, // c0x0118 (n0x1705-n0x1708) + 0x05c51708, // c0x0119 (n0x1708-n0x1714) + 0x05c65714, // c0x011a (n0x1714-n0x1719) + 0x05c69719, // c0x011b (n0x1719-n0x171a) + 0x05c8171a, // c0x011c (n0x171a-n0x1720) + 0x05c8d720, // c0x011d (n0x1720-n0x1723) + 0x05c91723, // c0x011e (n0x1723-n0x1724) + 0x05cad724, // c0x011f (n0x1724-n0x172b) + 0x05ce972b, // c0x0120 (n0x172b-n0x173a) + 0x05ced73a, // c0x0121 (n0x173a-n0x173b) + 0x05d0d73b, // c0x0122 (n0x173b-n0x1743) + 0x05d5d743, // c0x0123 (n0x1743-n0x1757) + 0x05d75757, // c0x0124 (n0x1757-n0x175d) + 0x65d7d75d, // c0x0125 (n0x175d-n0x175f)* o 0x25d8175f, // c0x0126 (n0x175f-n0x1760) o 0x05dc5760, // c0x0127 (n0x1760-n0x1771) + 0x05dd5771, // c0x0128 (n0x1771-n0x1775) + 0x05e0d775, // c0x0129 (n0x1775-n0x1783) + 0x05e3d783, // c0x012a (n0x1783-n0x178f) + 0x05f7578f, // c0x012b (n0x178f-n0x17dd) + 0x05f957dd, // c0x012c (n0x17dd-n0x17e5) + 0x65fc17e5, // c0x012d (n0x17e5-n0x17f0)* o 0x25fc57f0, // c0x012e (n0x17f0-n0x17f1) o 0x060c17f1, // c0x012f (n0x17f1-n0x1830) + 0x060cd830, // c0x0130 (n0x1830-n0x1833) + 0x060d9833, // c0x0131 (n0x1833-n0x1836) + 0x060e5836, // c0x0132 (n0x1836-n0x1839) + 0x060f1839, // c0x0133 (n0x1839-n0x183c) + 0x060fd83c, // c0x0134 (n0x183c-n0x183f) + 0x0610983f, // c0x0135 (n0x183f-n0x1842) + 0x06115842, // c0x0136 (n0x1842-n0x1845) + 0x06121845, // c0x0137 (n0x1845-n0x1848) + 0x0612d848, // c0x0138 (n0x1848-n0x184b) + 0x0613984b, // c0x0139 (n0x184b-n0x184e) + 0x0614584e, // c0x013a (n0x184e-n0x1851) + 0x06151851, // c0x013b (n0x1851-n0x1854) + 0x0615d854, // c0x013c (n0x1854-n0x1857) + 0x06165857, // c0x013d (n0x1857-n0x1859) + 0x06171859, // c0x013e (n0x1859-n0x185c) + 0x0617d85c, // c0x013f (n0x185c-n0x185f) + 0x0618985f, // c0x0140 (n0x185f-n0x1862) + 0x06195862, // c0x0141 (n0x1862-n0x1865) + 0x061a1865, // c0x0142 (n0x1865-n0x1868) + 0x061ad868, // c0x0143 (n0x1868-n0x186b) + 0x061b986b, // c0x0144 (n0x186b-n0x186e) + 0x061c586e, // c0x0145 (n0x186e-n0x1871) + 0x061d1871, // c0x0146 (n0x1871-n0x1874) + 0x061dd874, // c0x0147 (n0x1874-n0x1877) + 0x061e9877, // c0x0148 (n0x1877-n0x187a) + 0x061f587a, // c0x0149 (n0x187a-n0x187d) + 0x0620187d, // c0x014a (n0x187d-n0x1880) + 0x0620d880, // c0x014b (n0x1880-n0x1883) + 0x06219883, // c0x014c (n0x1883-n0x1886) + 0x06225886, // c0x014d (n0x1886-n0x1889) + 0x06231889, // c0x014e (n0x1889-n0x188c) + 0x0623d88c, // c0x014f (n0x188c-n0x188f) + 0x0624988f, // c0x0150 (n0x188f-n0x1892) + 0x06255892, // c0x0151 (n0x1892-n0x1895) + 0x06261895, // c0x0152 (n0x1895-n0x1898) + 0x0626d898, // c0x0153 (n0x1898-n0x189b) + 0x0627989b, // c0x0154 (n0x189b-n0x189e) + 0x0628589e, // c0x0155 (n0x189e-n0x18a1) + 0x062918a1, // c0x0156 (n0x18a1-n0x18a4) + 0x0629d8a4, // c0x0157 (n0x18a4-n0x18a7) + 0x062a98a7, // c0x0158 (n0x18a7-n0x18aa) + 0x062b58aa, // c0x0159 (n0x18aa-n0x18ad) + 0x062c18ad, // c0x015a (n0x18ad-n0x18b0) + 0x062cd8b0, // c0x015b (n0x18b0-n0x18b3) + 0x062d98b3, // c0x015c (n0x18b3-n0x18b6) + 0x062e18b6, // c0x015d (n0x18b6-n0x18b8) + 0x062ed8b8, // c0x015e (n0x18b8-n0x18bb) + 0x062f98bb, // c0x015f (n0x18bb-n0x18be) + 0x063058be, // c0x0160 (n0x18be-n0x18c1) + 0x063118c1, // c0x0161 (n0x18c1-n0x18c4) + 0x0631d8c4, // c0x0162 (n0x18c4-n0x18c7) + 0x063298c7, // c0x0163 (n0x18c7-n0x18ca) + 0x063358ca, // c0x0164 (n0x18ca-n0x18cd) + 0x063418cd, // c0x0165 (n0x18cd-n0x18d0) + 0x063458d0, // c0x0166 (n0x18d0-n0x18d1) + 0x063518d1, // c0x0167 (n0x18d1-n0x18d4) + 0x063698d4, // c0x0168 (n0x18d4-n0x18da) + 0x063798da, // c0x0169 (n0x18da-n0x18de) + 0x063918de, // c0x016a (n0x18de-n0x18e4) + 0x063b98e4, // c0x016b (n0x18e4-n0x18ee) + 0x063cd8ee, // c0x016c (n0x18ee-n0x18f3) + 0x063fd8f3, // c0x016d (n0x18f3-n0x18ff) + 0x064198ff, // c0x016e (n0x18ff-n0x1906) + } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/publicsuffix/table_test.go����������������������������0000644�0000153�0000161�00000532006�12321735652�026020� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// generated by go run gen.go; DO NOT EDIT package publicsuffix var rules = [...]string{ "ac", "com.ac", "edu.ac", "gov.ac", "net.ac", "mil.ac", "org.ac", "ad", "nom.ad", "ae", "co.ae", "net.ae", "org.ae", "sch.ae", "ac.ae", "gov.ae", "mil.ae", "aero", "accident-investigation.aero", "accident-prevention.aero", "aerobatic.aero", "aeroclub.aero", "aerodrome.aero", "agents.aero", "aircraft.aero", "airline.aero", "airport.aero", "air-surveillance.aero", "airtraffic.aero", "air-traffic-control.aero", "ambulance.aero", "amusement.aero", "association.aero", "author.aero", "ballooning.aero", "broker.aero", "caa.aero", "cargo.aero", "catering.aero", "certification.aero", "championship.aero", "charter.aero", "civilaviation.aero", "club.aero", "conference.aero", "consultant.aero", "consulting.aero", "control.aero", "council.aero", "crew.aero", "design.aero", "dgca.aero", "educator.aero", "emergency.aero", "engine.aero", "engineer.aero", "entertainment.aero", "equipment.aero", "exchange.aero", "express.aero", "federation.aero", "flight.aero", "freight.aero", "fuel.aero", "gliding.aero", "government.aero", "groundhandling.aero", "group.aero", "hanggliding.aero", "homebuilt.aero", "insurance.aero", "journal.aero", "journalist.aero", "leasing.aero", "logistics.aero", "magazine.aero", "maintenance.aero", "marketplace.aero", "media.aero", "microlight.aero", "modelling.aero", "navigation.aero", "parachuting.aero", "paragliding.aero", "passenger-association.aero", "pilot.aero", "press.aero", "production.aero", "recreation.aero", "repbody.aero", "res.aero", "research.aero", "rotorcraft.aero", "safety.aero", "scientist.aero", "services.aero", "show.aero", "skydiving.aero", "software.aero", "student.aero", "taxi.aero", "trader.aero", "trading.aero", "trainer.aero", "union.aero", "workinggroup.aero", "works.aero", "af", "gov.af", "com.af", "org.af", "net.af", "edu.af", "ag", "com.ag", "org.ag", "net.ag", "co.ag", "nom.ag", "ai", "off.ai", "com.ai", "net.ai", "org.ai", "al", "com.al", "edu.al", "gov.al", "mil.al", "net.al", "org.al", "am", "an", "com.an", "net.an", "org.an", "edu.an", "ao", "ed.ao", "gv.ao", "og.ao", "co.ao", "pb.ao", "it.ao", "aq", "ar", "com.ar", "edu.ar", "gob.ar", "int.ar", "mil.ar", "net.ar", "org.ar", "tur.ar", "arpa", "e164.arpa", "in-addr.arpa", "ip6.arpa", "iris.arpa", "uri.arpa", "urn.arpa", "as", "gov.as", "asia", "at", "ac.at", "co.at", "gv.at", "or.at", "au", "com.au", "net.au", "org.au", "edu.au", "gov.au", "asn.au", "id.au", "csiro.au", "info.au", "conf.au", "oz.au", "act.au", "nsw.au", "nt.au", "qld.au", "sa.au", "tas.au", "vic.au", "wa.au", "act.edu.au", "nsw.edu.au", "nt.edu.au", "qld.edu.au", "sa.edu.au", "tas.edu.au", "vic.edu.au", "wa.edu.au", "act.gov.au", "qld.gov.au", "sa.gov.au", "tas.gov.au", "vic.gov.au", "wa.gov.au", "aw", "com.aw", "ax", "az", "com.az", "net.az", "int.az", "gov.az", "org.az", "edu.az", "info.az", "pp.az", "mil.az", "name.az", "pro.az", "biz.az", "ba", "org.ba", "net.ba", "edu.ba", "gov.ba", "mil.ba", "unsa.ba", "unbi.ba", "co.ba", "com.ba", "rs.ba", "bb", "biz.bb", "com.bb", "edu.bb", "gov.bb", "info.bb", "net.bb", "org.bb", "store.bb", "*.bd", "be", "ac.be", "bf", "gov.bf", "bg", "a.bg", "b.bg", "c.bg", "d.bg", "e.bg", "f.bg", "g.bg", "h.bg", "i.bg", "j.bg", "k.bg", "l.bg", "m.bg", "n.bg", "o.bg", "p.bg", "q.bg", "r.bg", "s.bg", "t.bg", "u.bg", "v.bg", "w.bg", "x.bg", "y.bg", "z.bg", "0.bg", "1.bg", "2.bg", "3.bg", "4.bg", "5.bg", "6.bg", "7.bg", "8.bg", "9.bg", "bh", "com.bh", "edu.bh", "net.bh", "org.bh", "gov.bh", "bi", "co.bi", "com.bi", "edu.bi", "or.bi", "org.bi", "biz", "bj", "asso.bj", "barreau.bj", "gouv.bj", "bm", "com.bm", "edu.bm", "gov.bm", "net.bm", "org.bm", "*.bn", "bo", "com.bo", "edu.bo", "gov.bo", "gob.bo", "int.bo", "org.bo", "net.bo", "mil.bo", "tv.bo", "br", "adm.br", "adv.br", "agr.br", "am.br", "arq.br", "art.br", "ato.br", "b.br", "bio.br", "blog.br", "bmd.br", "cim.br", "cng.br", "cnt.br", "com.br", "coop.br", "ecn.br", "eco.br", "edu.br", "emp.br", "eng.br", "esp.br", "etc.br", "eti.br", "far.br", "flog.br", "fm.br", "fnd.br", "fot.br", "fst.br", "g12.br", "ggf.br", "gov.br", "imb.br", "ind.br", "inf.br", "jor.br", "jus.br", "leg.br", "lel.br", "mat.br", "med.br", "mil.br", "mus.br", "net.br", "nom.br", "not.br", "ntr.br", "odo.br", "org.br", "ppg.br", "pro.br", "psc.br", "psi.br", "qsl.br", "radio.br", "rec.br", "slg.br", "srv.br", "taxi.br", "teo.br", "tmp.br", "trd.br", "tur.br", "tv.br", "vet.br", "vlog.br", "wiki.br", "zlg.br", "bs", "com.bs", "net.bs", "org.bs", "edu.bs", "gov.bs", "bt", "com.bt", "edu.bt", "gov.bt", "net.bt", "org.bt", "bv", "bw", "co.bw", "org.bw", "by", "gov.by", "mil.by", "com.by", "of.by", "bz", "com.bz", "net.bz", "org.bz", "edu.bz", "gov.bz", "ca", "ab.ca", "bc.ca", "mb.ca", "nb.ca", "nf.ca", "nl.ca", "ns.ca", "nt.ca", "nu.ca", "on.ca", "pe.ca", "qc.ca", "sk.ca", "yk.ca", "gc.ca", "cat", "cc", "cd", "gov.cd", "cf", "cg", "ch", "ci", "org.ci", "or.ci", "com.ci", "co.ci", "edu.ci", "ed.ci", "ac.ci", "net.ci", "go.ci", "asso.ci", "xn--aroport-bya.ci", "int.ci", "presse.ci", "md.ci", "gouv.ci", "*.ck", "!www.ck", "cl", "gov.cl", "gob.cl", "co.cl", "mil.cl", "cm", "gov.cm", "cn", "ac.cn", "com.cn", "edu.cn", "gov.cn", "net.cn", "org.cn", "mil.cn", "xn--55qx5d.cn", "xn--io0a7i.cn", "xn--od0alg.cn", "ah.cn", "bj.cn", "cq.cn", "fj.cn", "gd.cn", "gs.cn", "gz.cn", "gx.cn", "ha.cn", "hb.cn", "he.cn", "hi.cn", "hl.cn", "hn.cn", "jl.cn", "js.cn", "jx.cn", "ln.cn", "nm.cn", "nx.cn", "qh.cn", "sc.cn", "sd.cn", "sh.cn", "sn.cn", "sx.cn", "tj.cn", "xj.cn", "xz.cn", "yn.cn", "zj.cn", "hk.cn", "mo.cn", "tw.cn", "co", "arts.co", "com.co", "edu.co", "firm.co", "gov.co", "info.co", "int.co", "mil.co", "net.co", "nom.co", "org.co", "rec.co", "web.co", "com", "coop", "cr", "ac.cr", "co.cr", "ed.cr", "fi.cr", "go.cr", "or.cr", "sa.cr", "cu", "com.cu", "edu.cu", "org.cu", "net.cu", "gov.cu", "inf.cu", "cv", "cw", "com.cw", "edu.cw", "net.cw", "org.cw", "cx", "gov.cx", "*.cy", "cz", "de", "dj", "dk", "dm", "com.dm", "net.dm", "org.dm", "edu.dm", "gov.dm", "do", "art.do", "com.do", "edu.do", "gob.do", "gov.do", "mil.do", "net.do", "org.do", "sld.do", "web.do", "dz", "com.dz", "org.dz", "net.dz", "gov.dz", "edu.dz", "asso.dz", "pol.dz", "art.dz", "ec", "com.ec", "info.ec", "net.ec", "fin.ec", "k12.ec", "med.ec", "pro.ec", "org.ec", "edu.ec", "gov.ec", "gob.ec", "mil.ec", "edu", "ee", "edu.ee", "gov.ee", "riik.ee", "lib.ee", "med.ee", "com.ee", "pri.ee", "aip.ee", "org.ee", "fie.ee", "eg", "com.eg", "edu.eg", "eun.eg", "gov.eg", "mil.eg", "name.eg", "net.eg", "org.eg", "sci.eg", "*.er", "es", "com.es", "nom.es", "org.es", "gob.es", "edu.es", "*.et", "eu", "fi", "aland.fi", "*.fj", "*.fk", "fm", "fo", "fr", "com.fr", "asso.fr", "nom.fr", "prd.fr", "presse.fr", "tm.fr", "aeroport.fr", "assedic.fr", "avocat.fr", "avoues.fr", "cci.fr", "chambagri.fr", "chirurgiens-dentistes.fr", "experts-comptables.fr", "geometre-expert.fr", "gouv.fr", "greta.fr", "huissier-justice.fr", "medecin.fr", "notaires.fr", "pharmacien.fr", "port.fr", "veterinaire.fr", "ga", "gb", "gd", "ge", "com.ge", "edu.ge", "gov.ge", "org.ge", "mil.ge", "net.ge", "pvt.ge", "gf", "gg", "co.gg", "net.gg", "org.gg", "gh", "com.gh", "edu.gh", "gov.gh", "org.gh", "mil.gh", "gi", "com.gi", "ltd.gi", "gov.gi", "mod.gi", "edu.gi", "org.gi", "gl", "gm", "gn", "ac.gn", "com.gn", "edu.gn", "gov.gn", "org.gn", "net.gn", "gov", "gp", "com.gp", "net.gp", "mobi.gp", "edu.gp", "org.gp", "asso.gp", "gq", "gr", "com.gr", "edu.gr", "net.gr", "org.gr", "gov.gr", "gs", "gt", "com.gt", "edu.gt", "gob.gt", "ind.gt", "mil.gt", "net.gt", "org.gt", "*.gu", "gw", "gy", "co.gy", "com.gy", "net.gy", "hk", "com.hk", "edu.hk", "gov.hk", "idv.hk", "net.hk", "org.hk", "xn--55qx5d.hk", "xn--wcvs22d.hk", "xn--lcvr32d.hk", "xn--mxtq1m.hk", "xn--gmqw5a.hk", "xn--ciqpn.hk", "xn--gmq050i.hk", "xn--zf0avx.hk", "xn--io0a7i.hk", "xn--mk0axi.hk", "xn--od0alg.hk", "xn--od0aq3b.hk", "xn--tn0ag.hk", "xn--uc0atv.hk", "xn--uc0ay4a.hk", "hm", "hn", "com.hn", "edu.hn", "org.hn", "net.hn", "mil.hn", "gob.hn", "hr", "iz.hr", "from.hr", "name.hr", "com.hr", "ht", "com.ht", "shop.ht", "firm.ht", "info.ht", "adult.ht", "net.ht", "pro.ht", "org.ht", "med.ht", "art.ht", "coop.ht", "pol.ht", "asso.ht", "edu.ht", "rel.ht", "gouv.ht", "perso.ht", "hu", "co.hu", "info.hu", "org.hu", "priv.hu", "sport.hu", "tm.hu", "2000.hu", "agrar.hu", "bolt.hu", "casino.hu", "city.hu", "erotica.hu", "erotika.hu", "film.hu", "forum.hu", "games.hu", "hotel.hu", "ingatlan.hu", "jogasz.hu", "konyvelo.hu", "lakas.hu", "media.hu", "news.hu", "reklam.hu", "sex.hu", "shop.hu", "suli.hu", "szex.hu", "tozsde.hu", "utazas.hu", "video.hu", "id", "ac.id", "biz.id", "co.id", "go.id", "mil.id", "my.id", "net.id", "or.id", "sch.id", "web.id", "ie", "gov.ie", "*.il", "im", "ac.im", "co.im", "com.im", "ltd.co.im", "net.im", "org.im", "plc.co.im", "tt.im", "tv.im", "in", "co.in", "firm.in", "net.in", "org.in", "gen.in", "ind.in", "nic.in", "ac.in", "edu.in", "res.in", "gov.in", "mil.in", "info", "int", "eu.int", "io", "com.io", "iq", "gov.iq", "edu.iq", "mil.iq", "com.iq", "org.iq", "net.iq", "ir", "ac.ir", "co.ir", "gov.ir", "id.ir", "net.ir", "org.ir", "sch.ir", "xn--mgba3a4f16a.ir", "xn--mgba3a4fra.ir", "is", "net.is", "com.is", "edu.is", "gov.is", "org.is", "int.is", "it", "gov.it", "edu.it", "agrigento.it", "ag.it", "alessandria.it", "al.it", "ancona.it", "an.it", "aosta.it", "aoste.it", "ao.it", "arezzo.it", "ar.it", "ascoli-piceno.it", "ascolipiceno.it", "ap.it", "asti.it", "at.it", "avellino.it", "av.it", "bari.it", "ba.it", "andria-barletta-trani.it", "andriabarlettatrani.it", "trani-barletta-andria.it", "tranibarlettaandria.it", "barletta-trani-andria.it", "barlettatraniandria.it", "andria-trani-barletta.it", "andriatranibarletta.it", "trani-andria-barletta.it", "traniandriabarletta.it", "bt.it", "belluno.it", "bl.it", "benevento.it", "bn.it", "bergamo.it", "bg.it", "biella.it", "bi.it", "bologna.it", "bo.it", "bolzano.it", "bozen.it", "balsan.it", "alto-adige.it", "altoadige.it", "suedtirol.it", "bz.it", "brescia.it", "bs.it", "brindisi.it", "br.it", "cagliari.it", "ca.it", "caltanissetta.it", "cl.it", "campobasso.it", "cb.it", "carboniaiglesias.it", "carbonia-iglesias.it", "iglesias-carbonia.it", "iglesiascarbonia.it", "ci.it", "caserta.it", "ce.it", "catania.it", "ct.it", "catanzaro.it", "cz.it", "chieti.it", "ch.it", "como.it", "co.it", "cosenza.it", "cs.it", "cremona.it", "cr.it", "crotone.it", "kr.it", "cuneo.it", "cn.it", "dell-ogliastra.it", "dellogliastra.it", "ogliastra.it", "og.it", "enna.it", "en.it", "ferrara.it", "fe.it", "fermo.it", "fm.it", "firenze.it", "florence.it", "fi.it", "foggia.it", "fg.it", "forli-cesena.it", "forlicesena.it", "cesena-forli.it", "cesenaforli.it", "fc.it", "frosinone.it", "fr.it", "genova.it", "genoa.it", "ge.it", "gorizia.it", "go.it", "grosseto.it", "gr.it", "imperia.it", "im.it", "isernia.it", "is.it", "laquila.it", "aquila.it", "aq.it", "la-spezia.it", "laspezia.it", "sp.it", "latina.it", "lt.it", "lecce.it", "le.it", "lecco.it", "lc.it", "livorno.it", "li.it", "lodi.it", "lo.it", "lucca.it", "lu.it", "macerata.it", "mc.it", "mantova.it", "mn.it", "massa-carrara.it", "massacarrara.it", "carrara-massa.it", "carraramassa.it", "ms.it", "matera.it", "mt.it", "medio-campidano.it", "mediocampidano.it", "campidano-medio.it", "campidanomedio.it", "vs.it", "messina.it", "me.it", "milano.it", "milan.it", "mi.it", "modena.it", "mo.it", "monza.it", "monza-brianza.it", "monzabrianza.it", "monzaebrianza.it", "monzaedellabrianza.it", "monza-e-della-brianza.it", "mb.it", "napoli.it", "naples.it", "na.it", "novara.it", "no.it", "nuoro.it", "nu.it", "oristano.it", "or.it", "padova.it", "padua.it", "pd.it", "palermo.it", "pa.it", "parma.it", "pr.it", "pavia.it", "pv.it", "perugia.it", "pg.it", "pescara.it", "pe.it", "pesaro-urbino.it", "pesarourbino.it", "urbino-pesaro.it", "urbinopesaro.it", "pu.it", "piacenza.it", "pc.it", "pisa.it", "pi.it", "pistoia.it", "pt.it", "pordenone.it", "pn.it", "potenza.it", "pz.it", "prato.it", "po.it", "ragusa.it", "rg.it", "ravenna.it", "ra.it", "reggio-calabria.it", "reggiocalabria.it", "rc.it", "reggio-emilia.it", "reggioemilia.it", "re.it", "rieti.it", "ri.it", "rimini.it", "rn.it", "roma.it", "rome.it", "rm.it", "rovigo.it", "ro.it", "salerno.it", "sa.it", "sassari.it", "ss.it", "savona.it", "sv.it", "siena.it", "si.it", "siracusa.it", "sr.it", "sondrio.it", "so.it", "taranto.it", "ta.it", "tempio-olbia.it", "tempioolbia.it", "olbia-tempio.it", "olbiatempio.it", "ot.it", "teramo.it", "te.it", "terni.it", "tr.it", "torino.it", "turin.it", "to.it", "trapani.it", "tp.it", "trento.it", "trentino.it", "tn.it", "treviso.it", "tv.it", "trieste.it", "ts.it", "udine.it", "ud.it", "varese.it", "va.it", "venezia.it", "venice.it", "ve.it", "verbania.it", "vb.it", "vercelli.it", "vc.it", "verona.it", "vr.it", "vibo-valentia.it", "vibovalentia.it", "vv.it", "vicenza.it", "vi.it", "viterbo.it", "vt.it", "je", "co.je", "net.je", "org.je", "*.jm", "jo", "com.jo", "org.jo", "net.jo", "edu.jo", "sch.jo", "gov.jo", "mil.jo", "name.jo", "jobs", "jp", "ac.jp", "ad.jp", "co.jp", "ed.jp", "go.jp", "gr.jp", "lg.jp", "ne.jp", "or.jp", "aichi.jp", "akita.jp", "aomori.jp", "chiba.jp", "ehime.jp", "fukui.jp", "fukuoka.jp", "fukushima.jp", "gifu.jp", "gunma.jp", "hiroshima.jp", "hokkaido.jp", "hyogo.jp", "ibaraki.jp", "ishikawa.jp", "iwate.jp", "kagawa.jp", "kagoshima.jp", "kanagawa.jp", "kochi.jp", "kumamoto.jp", "kyoto.jp", "mie.jp", "miyagi.jp", "miyazaki.jp", "nagano.jp", "nagasaki.jp", "nara.jp", "niigata.jp", "oita.jp", "okayama.jp", "okinawa.jp", "osaka.jp", "saga.jp", "saitama.jp", "shiga.jp", "shimane.jp", "shizuoka.jp", "tochigi.jp", "tokushima.jp", "tokyo.jp", "tottori.jp", "toyama.jp", "wakayama.jp", "yamagata.jp", "yamaguchi.jp", "yamanashi.jp", "*.kawasaki.jp", "*.kitakyushu.jp", "*.kobe.jp", "*.nagoya.jp", "*.sapporo.jp", "*.sendai.jp", "*.yokohama.jp", "!city.kawasaki.jp", "!city.kitakyushu.jp", "!city.kobe.jp", "!city.nagoya.jp", "!city.sapporo.jp", "!city.sendai.jp", "!city.yokohama.jp", "aisai.aichi.jp", "ama.aichi.jp", "anjo.aichi.jp", "asuke.aichi.jp", "chiryu.aichi.jp", "chita.aichi.jp", "fuso.aichi.jp", "gamagori.aichi.jp", "handa.aichi.jp", "hazu.aichi.jp", "hekinan.aichi.jp", "higashiura.aichi.jp", "ichinomiya.aichi.jp", "inazawa.aichi.jp", "inuyama.aichi.jp", "isshiki.aichi.jp", "iwakura.aichi.jp", "kanie.aichi.jp", "kariya.aichi.jp", "kasugai.aichi.jp", "kira.aichi.jp", "kiyosu.aichi.jp", "komaki.aichi.jp", "konan.aichi.jp", "kota.aichi.jp", "mihama.aichi.jp", "miyoshi.aichi.jp", "nagakute.aichi.jp", "nishio.aichi.jp", "nisshin.aichi.jp", "obu.aichi.jp", "oguchi.aichi.jp", "oharu.aichi.jp", "okazaki.aichi.jp", "owariasahi.aichi.jp", "seto.aichi.jp", "shikatsu.aichi.jp", "shinshiro.aichi.jp", "shitara.aichi.jp", "tahara.aichi.jp", "takahama.aichi.jp", "tobishima.aichi.jp", "toei.aichi.jp", "togo.aichi.jp", "tokai.aichi.jp", "tokoname.aichi.jp", "toyoake.aichi.jp", "toyohashi.aichi.jp", "toyokawa.aichi.jp", "toyone.aichi.jp", "toyota.aichi.jp", "tsushima.aichi.jp", "yatomi.aichi.jp", "akita.akita.jp", "daisen.akita.jp", "fujisato.akita.jp", "gojome.akita.jp", "hachirogata.akita.jp", "happou.akita.jp", "higashinaruse.akita.jp", "honjo.akita.jp", "honjyo.akita.jp", "ikawa.akita.jp", "kamikoani.akita.jp", "kamioka.akita.jp", "katagami.akita.jp", "kazuno.akita.jp", "kitaakita.akita.jp", "kosaka.akita.jp", "kyowa.akita.jp", "misato.akita.jp", "mitane.akita.jp", "moriyoshi.akita.jp", "nikaho.akita.jp", "noshiro.akita.jp", "odate.akita.jp", "oga.akita.jp", "ogata.akita.jp", "semboku.akita.jp", "yokote.akita.jp", "yurihonjo.akita.jp", "aomori.aomori.jp", "gonohe.aomori.jp", "hachinohe.aomori.jp", "hashikami.aomori.jp", "hiranai.aomori.jp", "hirosaki.aomori.jp", "itayanagi.aomori.jp", "kuroishi.aomori.jp", "misawa.aomori.jp", "mutsu.aomori.jp", "nakadomari.aomori.jp", "noheji.aomori.jp", "oirase.aomori.jp", "owani.aomori.jp", "rokunohe.aomori.jp", "sannohe.aomori.jp", "shichinohe.aomori.jp", "shingo.aomori.jp", "takko.aomori.jp", "towada.aomori.jp", "tsugaru.aomori.jp", "tsuruta.aomori.jp", "abiko.chiba.jp", "asahi.chiba.jp", "chonan.chiba.jp", "chosei.chiba.jp", "choshi.chiba.jp", "chuo.chiba.jp", "funabashi.chiba.jp", "futtsu.chiba.jp", "hanamigawa.chiba.jp", "ichihara.chiba.jp", "ichikawa.chiba.jp", "ichinomiya.chiba.jp", "inzai.chiba.jp", "isumi.chiba.jp", "kamagaya.chiba.jp", "kamogawa.chiba.jp", "kashiwa.chiba.jp", "katori.chiba.jp", "katsuura.chiba.jp", "kimitsu.chiba.jp", "kisarazu.chiba.jp", "kozaki.chiba.jp", "kujukuri.chiba.jp", "kyonan.chiba.jp", "matsudo.chiba.jp", "midori.chiba.jp", "mihama.chiba.jp", "minamiboso.chiba.jp", "mobara.chiba.jp", "mutsuzawa.chiba.jp", "nagara.chiba.jp", "nagareyama.chiba.jp", "narashino.chiba.jp", "narita.chiba.jp", "noda.chiba.jp", "oamishirasato.chiba.jp", "omigawa.chiba.jp", "onjuku.chiba.jp", "otaki.chiba.jp", "sakae.chiba.jp", "sakura.chiba.jp", "shimofusa.chiba.jp", "shirako.chiba.jp", "shiroi.chiba.jp", "shisui.chiba.jp", "sodegaura.chiba.jp", "sosa.chiba.jp", "tako.chiba.jp", "tateyama.chiba.jp", "togane.chiba.jp", "tohnosho.chiba.jp", "tomisato.chiba.jp", "urayasu.chiba.jp", "yachimata.chiba.jp", "yachiyo.chiba.jp", "yokaichiba.chiba.jp", "yokoshibahikari.chiba.jp", "yotsukaido.chiba.jp", "ainan.ehime.jp", "honai.ehime.jp", "ikata.ehime.jp", "imabari.ehime.jp", "iyo.ehime.jp", "kamijima.ehime.jp", "kihoku.ehime.jp", "kumakogen.ehime.jp", "masaki.ehime.jp", "matsuno.ehime.jp", "matsuyama.ehime.jp", "namikata.ehime.jp", "niihama.ehime.jp", "ozu.ehime.jp", "saijo.ehime.jp", "seiyo.ehime.jp", "shikokuchuo.ehime.jp", "tobe.ehime.jp", "toon.ehime.jp", "uchiko.ehime.jp", "uwajima.ehime.jp", "yawatahama.ehime.jp", "echizen.fukui.jp", "eiheiji.fukui.jp", "fukui.fukui.jp", "ikeda.fukui.jp", "katsuyama.fukui.jp", "mihama.fukui.jp", "minamiechizen.fukui.jp", "obama.fukui.jp", "ohi.fukui.jp", "ono.fukui.jp", "sabae.fukui.jp", "sakai.fukui.jp", "takahama.fukui.jp", "tsuruga.fukui.jp", "wakasa.fukui.jp", "ashiya.fukuoka.jp", "buzen.fukuoka.jp", "chikugo.fukuoka.jp", "chikuho.fukuoka.jp", "chikujo.fukuoka.jp", "chikushino.fukuoka.jp", "chikuzen.fukuoka.jp", "chuo.fukuoka.jp", "dazaifu.fukuoka.jp", "fukuchi.fukuoka.jp", "hakata.fukuoka.jp", "higashi.fukuoka.jp", "hirokawa.fukuoka.jp", "hisayama.fukuoka.jp", "iizuka.fukuoka.jp", "inatsuki.fukuoka.jp", "kaho.fukuoka.jp", "kasuga.fukuoka.jp", "kasuya.fukuoka.jp", "kawara.fukuoka.jp", "keisen.fukuoka.jp", "koga.fukuoka.jp", "kurate.fukuoka.jp", "kurogi.fukuoka.jp", "kurume.fukuoka.jp", "minami.fukuoka.jp", "miyako.fukuoka.jp", "miyama.fukuoka.jp", "miyawaka.fukuoka.jp", "mizumaki.fukuoka.jp", "munakata.fukuoka.jp", "nakagawa.fukuoka.jp", "nakama.fukuoka.jp", "nishi.fukuoka.jp", "nogata.fukuoka.jp", "ogori.fukuoka.jp", "okagaki.fukuoka.jp", "okawa.fukuoka.jp", "oki.fukuoka.jp", "omuta.fukuoka.jp", "onga.fukuoka.jp", "onojo.fukuoka.jp", "oto.fukuoka.jp", "saigawa.fukuoka.jp", "sasaguri.fukuoka.jp", "shingu.fukuoka.jp", "shinyoshitomi.fukuoka.jp", "shonai.fukuoka.jp", "soeda.fukuoka.jp", "sue.fukuoka.jp", "tachiarai.fukuoka.jp", "tagawa.fukuoka.jp", "takata.fukuoka.jp", "toho.fukuoka.jp", "toyotsu.fukuoka.jp", "tsuiki.fukuoka.jp", "ukiha.fukuoka.jp", "umi.fukuoka.jp", "usui.fukuoka.jp", "yamada.fukuoka.jp", "yame.fukuoka.jp", "yanagawa.fukuoka.jp", "yukuhashi.fukuoka.jp", "aizubange.fukushima.jp", "aizumisato.fukushima.jp", "aizuwakamatsu.fukushima.jp", "asakawa.fukushima.jp", "bandai.fukushima.jp", "date.fukushima.jp", "fukushima.fukushima.jp", "furudono.fukushima.jp", "futaba.fukushima.jp", "hanawa.fukushima.jp", "higashi.fukushima.jp", "hirata.fukushima.jp", "hirono.fukushima.jp", "iitate.fukushima.jp", "inawashiro.fukushima.jp", "ishikawa.fukushima.jp", "iwaki.fukushima.jp", "izumizaki.fukushima.jp", "kagamiishi.fukushima.jp", "kaneyama.fukushima.jp", "kawamata.fukushima.jp", "kitakata.fukushima.jp", "kitashiobara.fukushima.jp", "koori.fukushima.jp", "koriyama.fukushima.jp", "kunimi.fukushima.jp", "miharu.fukushima.jp", "mishima.fukushima.jp", "namie.fukushima.jp", "nango.fukushima.jp", "nishiaizu.fukushima.jp", "nishigo.fukushima.jp", "okuma.fukushima.jp", "omotego.fukushima.jp", "ono.fukushima.jp", "otama.fukushima.jp", "samegawa.fukushima.jp", "shimogo.fukushima.jp", "shirakawa.fukushima.jp", "showa.fukushima.jp", "soma.fukushima.jp", "sukagawa.fukushima.jp", "taishin.fukushima.jp", "tamakawa.fukushima.jp", "tanagura.fukushima.jp", "tenei.fukushima.jp", "yabuki.fukushima.jp", "yamato.fukushima.jp", "yamatsuri.fukushima.jp", "yanaizu.fukushima.jp", "yugawa.fukushima.jp", "anpachi.gifu.jp", "ena.gifu.jp", "gifu.gifu.jp", "ginan.gifu.jp", "godo.gifu.jp", "gujo.gifu.jp", "hashima.gifu.jp", "hichiso.gifu.jp", "hida.gifu.jp", "higashishirakawa.gifu.jp", "ibigawa.gifu.jp", "ikeda.gifu.jp", "kakamigahara.gifu.jp", "kani.gifu.jp", "kasahara.gifu.jp", "kasamatsu.gifu.jp", "kawaue.gifu.jp", "kitagata.gifu.jp", "mino.gifu.jp", "minokamo.gifu.jp", "mitake.gifu.jp", "mizunami.gifu.jp", "motosu.gifu.jp", "nakatsugawa.gifu.jp", "ogaki.gifu.jp", "sakahogi.gifu.jp", "seki.gifu.jp", "sekigahara.gifu.jp", "shirakawa.gifu.jp", "tajimi.gifu.jp", "takayama.gifu.jp", "tarui.gifu.jp", "toki.gifu.jp", "tomika.gifu.jp", "wanouchi.gifu.jp", "yamagata.gifu.jp", "yaotsu.gifu.jp", "yoro.gifu.jp", "annaka.gunma.jp", "chiyoda.gunma.jp", "fujioka.gunma.jp", "higashiagatsuma.gunma.jp", "isesaki.gunma.jp", "itakura.gunma.jp", "kanna.gunma.jp", "kanra.gunma.jp", "katashina.gunma.jp", "kawaba.gunma.jp", "kiryu.gunma.jp", "kusatsu.gunma.jp", "maebashi.gunma.jp", "meiwa.gunma.jp", "midori.gunma.jp", "minakami.gunma.jp", "naganohara.gunma.jp", "nakanojo.gunma.jp", "nanmoku.gunma.jp", "numata.gunma.jp", "oizumi.gunma.jp", "ora.gunma.jp", "ota.gunma.jp", "shibukawa.gunma.jp", "shimonita.gunma.jp", "shinto.gunma.jp", "showa.gunma.jp", "takasaki.gunma.jp", "takayama.gunma.jp", "tamamura.gunma.jp", "tatebayashi.gunma.jp", "tomioka.gunma.jp", "tsukiyono.gunma.jp", "tsumagoi.gunma.jp", "ueno.gunma.jp", "yoshioka.gunma.jp", "asaminami.hiroshima.jp", "daiwa.hiroshima.jp", "etajima.hiroshima.jp", "fuchu.hiroshima.jp", "fukuyama.hiroshima.jp", "hatsukaichi.hiroshima.jp", "higashihiroshima.hiroshima.jp", "hongo.hiroshima.jp", "jinsekikogen.hiroshima.jp", "kaita.hiroshima.jp", "kui.hiroshima.jp", "kumano.hiroshima.jp", "kure.hiroshima.jp", "mihara.hiroshima.jp", "miyoshi.hiroshima.jp", "naka.hiroshima.jp", "onomichi.hiroshima.jp", "osakikamijima.hiroshima.jp", "otake.hiroshima.jp", "saka.hiroshima.jp", "sera.hiroshima.jp", "seranishi.hiroshima.jp", "shinichi.hiroshima.jp", "shobara.hiroshima.jp", "takehara.hiroshima.jp", "abashiri.hokkaido.jp", "abira.hokkaido.jp", "aibetsu.hokkaido.jp", "akabira.hokkaido.jp", "akkeshi.hokkaido.jp", "asahikawa.hokkaido.jp", "ashibetsu.hokkaido.jp", "ashoro.hokkaido.jp", "assabu.hokkaido.jp", "atsuma.hokkaido.jp", "bibai.hokkaido.jp", "biei.hokkaido.jp", "bifuka.hokkaido.jp", "bihoro.hokkaido.jp", "biratori.hokkaido.jp", "chippubetsu.hokkaido.jp", "chitose.hokkaido.jp", "date.hokkaido.jp", "ebetsu.hokkaido.jp", "embetsu.hokkaido.jp", "eniwa.hokkaido.jp", "erimo.hokkaido.jp", "esan.hokkaido.jp", "esashi.hokkaido.jp", "fukagawa.hokkaido.jp", "fukushima.hokkaido.jp", "furano.hokkaido.jp", "furubira.hokkaido.jp", "haboro.hokkaido.jp", "hakodate.hokkaido.jp", "hamatonbetsu.hokkaido.jp", "hidaka.hokkaido.jp", "higashikagura.hokkaido.jp", "higashikawa.hokkaido.jp", "hiroo.hokkaido.jp", "hokuryu.hokkaido.jp", "hokuto.hokkaido.jp", "honbetsu.hokkaido.jp", "horokanai.hokkaido.jp", "horonobe.hokkaido.jp", "ikeda.hokkaido.jp", "imakane.hokkaido.jp", "ishikari.hokkaido.jp", "iwamizawa.hokkaido.jp", "iwanai.hokkaido.jp", "kamifurano.hokkaido.jp", "kamikawa.hokkaido.jp", "kamishihoro.hokkaido.jp", "kamisunagawa.hokkaido.jp", "kamoenai.hokkaido.jp", "kayabe.hokkaido.jp", "kembuchi.hokkaido.jp", "kikonai.hokkaido.jp", "kimobetsu.hokkaido.jp", "kitahiroshima.hokkaido.jp", "kitami.hokkaido.jp", "kiyosato.hokkaido.jp", "koshimizu.hokkaido.jp", "kunneppu.hokkaido.jp", "kuriyama.hokkaido.jp", "kuromatsunai.hokkaido.jp", "kushiro.hokkaido.jp", "kutchan.hokkaido.jp", "kyowa.hokkaido.jp", "mashike.hokkaido.jp", "matsumae.hokkaido.jp", "mikasa.hokkaido.jp", "minamifurano.hokkaido.jp", "mombetsu.hokkaido.jp", "moseushi.hokkaido.jp", "mukawa.hokkaido.jp", "muroran.hokkaido.jp", "naie.hokkaido.jp", "nakagawa.hokkaido.jp", "nakasatsunai.hokkaido.jp", "nakatombetsu.hokkaido.jp", "nanae.hokkaido.jp", "nanporo.hokkaido.jp", "nayoro.hokkaido.jp", "nemuro.hokkaido.jp", "niikappu.hokkaido.jp", "niki.hokkaido.jp", "nishiokoppe.hokkaido.jp", "noboribetsu.hokkaido.jp", "numata.hokkaido.jp", "obihiro.hokkaido.jp", "obira.hokkaido.jp", "oketo.hokkaido.jp", "okoppe.hokkaido.jp", "otaru.hokkaido.jp", "otobe.hokkaido.jp", "otofuke.hokkaido.jp", "otoineppu.hokkaido.jp", "oumu.hokkaido.jp", "ozora.hokkaido.jp", "pippu.hokkaido.jp", "rankoshi.hokkaido.jp", "rebun.hokkaido.jp", "rikubetsu.hokkaido.jp", "rishiri.hokkaido.jp", "rishirifuji.hokkaido.jp", "saroma.hokkaido.jp", "sarufutsu.hokkaido.jp", "shakotan.hokkaido.jp", "shari.hokkaido.jp", "shibecha.hokkaido.jp", "shibetsu.hokkaido.jp", "shikabe.hokkaido.jp", "shikaoi.hokkaido.jp", "shimamaki.hokkaido.jp", "shimizu.hokkaido.jp", "shimokawa.hokkaido.jp", "shinshinotsu.hokkaido.jp", "shintoku.hokkaido.jp", "shiranuka.hokkaido.jp", "shiraoi.hokkaido.jp", "shiriuchi.hokkaido.jp", "sobetsu.hokkaido.jp", "sunagawa.hokkaido.jp", "taiki.hokkaido.jp", "takasu.hokkaido.jp", "takikawa.hokkaido.jp", "takinoue.hokkaido.jp", "teshikaga.hokkaido.jp", "tobetsu.hokkaido.jp", "tohma.hokkaido.jp", "tomakomai.hokkaido.jp", "tomari.hokkaido.jp", "toya.hokkaido.jp", "toyako.hokkaido.jp", "toyotomi.hokkaido.jp", "toyoura.hokkaido.jp", "tsubetsu.hokkaido.jp", "tsukigata.hokkaido.jp", "urakawa.hokkaido.jp", "urausu.hokkaido.jp", "uryu.hokkaido.jp", "utashinai.hokkaido.jp", "wakkanai.hokkaido.jp", "wassamu.hokkaido.jp", "yakumo.hokkaido.jp", "yoichi.hokkaido.jp", "aioi.hyogo.jp", "akashi.hyogo.jp", "ako.hyogo.jp", "amagasaki.hyogo.jp", "aogaki.hyogo.jp", "asago.hyogo.jp", "ashiya.hyogo.jp", "awaji.hyogo.jp", "fukusaki.hyogo.jp", "goshiki.hyogo.jp", "harima.hyogo.jp", "himeji.hyogo.jp", "ichikawa.hyogo.jp", "inagawa.hyogo.jp", "itami.hyogo.jp", "kakogawa.hyogo.jp", "kamigori.hyogo.jp", "kamikawa.hyogo.jp", "kasai.hyogo.jp", "kasuga.hyogo.jp", "kawanishi.hyogo.jp", "miki.hyogo.jp", "minamiawaji.hyogo.jp", "nishinomiya.hyogo.jp", "nishiwaki.hyogo.jp", "ono.hyogo.jp", "sanda.hyogo.jp", "sannan.hyogo.jp", "sasayama.hyogo.jp", "sayo.hyogo.jp", "shingu.hyogo.jp", "shinonsen.hyogo.jp", "shiso.hyogo.jp", "sumoto.hyogo.jp", "taishi.hyogo.jp", "taka.hyogo.jp", "takarazuka.hyogo.jp", "takasago.hyogo.jp", "takino.hyogo.jp", "tamba.hyogo.jp", "tatsuno.hyogo.jp", "toyooka.hyogo.jp", "yabu.hyogo.jp", "yashiro.hyogo.jp", "yoka.hyogo.jp", "yokawa.hyogo.jp", "ami.ibaraki.jp", "asahi.ibaraki.jp", "bando.ibaraki.jp", "chikusei.ibaraki.jp", "daigo.ibaraki.jp", "fujishiro.ibaraki.jp", "hitachi.ibaraki.jp", "hitachinaka.ibaraki.jp", "hitachiomiya.ibaraki.jp", "hitachiota.ibaraki.jp", "ibaraki.ibaraki.jp", "ina.ibaraki.jp", "inashiki.ibaraki.jp", "itako.ibaraki.jp", "iwama.ibaraki.jp", "joso.ibaraki.jp", "kamisu.ibaraki.jp", "kasama.ibaraki.jp", "kashima.ibaraki.jp", "kasumigaura.ibaraki.jp", "koga.ibaraki.jp", "miho.ibaraki.jp", "mito.ibaraki.jp", "moriya.ibaraki.jp", "naka.ibaraki.jp", "namegata.ibaraki.jp", "oarai.ibaraki.jp", "ogawa.ibaraki.jp", "omitama.ibaraki.jp", "ryugasaki.ibaraki.jp", "sakai.ibaraki.jp", "sakuragawa.ibaraki.jp", "shimodate.ibaraki.jp", "shimotsuma.ibaraki.jp", "shirosato.ibaraki.jp", "sowa.ibaraki.jp", "suifu.ibaraki.jp", "takahagi.ibaraki.jp", "tamatsukuri.ibaraki.jp", "tokai.ibaraki.jp", "tomobe.ibaraki.jp", "tone.ibaraki.jp", "toride.ibaraki.jp", "tsuchiura.ibaraki.jp", "tsukuba.ibaraki.jp", "uchihara.ibaraki.jp", "ushiku.ibaraki.jp", "yachiyo.ibaraki.jp", "yamagata.ibaraki.jp", "yawara.ibaraki.jp", "yuki.ibaraki.jp", "anamizu.ishikawa.jp", "hakui.ishikawa.jp", "hakusan.ishikawa.jp", "kaga.ishikawa.jp", "kahoku.ishikawa.jp", "kanazawa.ishikawa.jp", "kawakita.ishikawa.jp", "komatsu.ishikawa.jp", "nakanoto.ishikawa.jp", "nanao.ishikawa.jp", "nomi.ishikawa.jp", "nonoichi.ishikawa.jp", "noto.ishikawa.jp", "shika.ishikawa.jp", "suzu.ishikawa.jp", "tsubata.ishikawa.jp", "tsurugi.ishikawa.jp", "uchinada.ishikawa.jp", "wajima.ishikawa.jp", "fudai.iwate.jp", "fujisawa.iwate.jp", "hanamaki.iwate.jp", "hiraizumi.iwate.jp", "hirono.iwate.jp", "ichinohe.iwate.jp", "ichinoseki.iwate.jp", "iwaizumi.iwate.jp", "iwate.iwate.jp", "joboji.iwate.jp", "kamaishi.iwate.jp", "kanegasaki.iwate.jp", "karumai.iwate.jp", "kawai.iwate.jp", "kitakami.iwate.jp", "kuji.iwate.jp", "kunohe.iwate.jp", "kuzumaki.iwate.jp", "miyako.iwate.jp", "mizusawa.iwate.jp", "morioka.iwate.jp", "ninohe.iwate.jp", "noda.iwate.jp", "ofunato.iwate.jp", "oshu.iwate.jp", "otsuchi.iwate.jp", "rikuzentakata.iwate.jp", "shiwa.iwate.jp", "shizukuishi.iwate.jp", "sumita.iwate.jp", "takizawa.iwate.jp", "tanohata.iwate.jp", "tono.iwate.jp", "yahaba.iwate.jp", "yamada.iwate.jp", "ayagawa.kagawa.jp", "higashikagawa.kagawa.jp", "kanonji.kagawa.jp", "kotohira.kagawa.jp", "manno.kagawa.jp", "marugame.kagawa.jp", "mitoyo.kagawa.jp", "naoshima.kagawa.jp", "sanuki.kagawa.jp", "tadotsu.kagawa.jp", "takamatsu.kagawa.jp", "tonosho.kagawa.jp", "uchinomi.kagawa.jp", "utazu.kagawa.jp", "zentsuji.kagawa.jp", "akune.kagoshima.jp", "amami.kagoshima.jp", "hioki.kagoshima.jp", "isa.kagoshima.jp", "isen.kagoshima.jp", "izumi.kagoshima.jp", "kagoshima.kagoshima.jp", "kanoya.kagoshima.jp", "kawanabe.kagoshima.jp", "kinko.kagoshima.jp", "kouyama.kagoshima.jp", "makurazaki.kagoshima.jp", "matsumoto.kagoshima.jp", "minamitane.kagoshima.jp", "nakatane.kagoshima.jp", "nishinoomote.kagoshima.jp", "satsumasendai.kagoshima.jp", "soo.kagoshima.jp", "tarumizu.kagoshima.jp", "yusui.kagoshima.jp", "aikawa.kanagawa.jp", "atsugi.kanagawa.jp", "ayase.kanagawa.jp", "chigasaki.kanagawa.jp", "ebina.kanagawa.jp", "fujisawa.kanagawa.jp", "hadano.kanagawa.jp", "hakone.kanagawa.jp", "hiratsuka.kanagawa.jp", "isehara.kanagawa.jp", "kaisei.kanagawa.jp", "kamakura.kanagawa.jp", "kiyokawa.kanagawa.jp", "matsuda.kanagawa.jp", "minamiashigara.kanagawa.jp", "miura.kanagawa.jp", "nakai.kanagawa.jp", "ninomiya.kanagawa.jp", "odawara.kanagawa.jp", "oi.kanagawa.jp", "oiso.kanagawa.jp", "sagamihara.kanagawa.jp", "samukawa.kanagawa.jp", "tsukui.kanagawa.jp", "yamakita.kanagawa.jp", "yamato.kanagawa.jp", "yokosuka.kanagawa.jp", "yugawara.kanagawa.jp", "zama.kanagawa.jp", "zushi.kanagawa.jp", "aki.kochi.jp", "geisei.kochi.jp", "hidaka.kochi.jp", "higashitsuno.kochi.jp", "ino.kochi.jp", "kagami.kochi.jp", "kami.kochi.jp", "kitagawa.kochi.jp", "kochi.kochi.jp", "mihara.kochi.jp", "motoyama.kochi.jp", "muroto.kochi.jp", "nahari.kochi.jp", "nakamura.kochi.jp", "nankoku.kochi.jp", "nishitosa.kochi.jp", "niyodogawa.kochi.jp", "ochi.kochi.jp", "okawa.kochi.jp", "otoyo.kochi.jp", "otsuki.kochi.jp", "sakawa.kochi.jp", "sukumo.kochi.jp", "susaki.kochi.jp", "tosa.kochi.jp", "tosashimizu.kochi.jp", "toyo.kochi.jp", "tsuno.kochi.jp", "umaji.kochi.jp", "yasuda.kochi.jp", "yusuhara.kochi.jp", "amakusa.kumamoto.jp", "arao.kumamoto.jp", "aso.kumamoto.jp", "choyo.kumamoto.jp", "gyokuto.kumamoto.jp", "hitoyoshi.kumamoto.jp", "kamiamakusa.kumamoto.jp", "kashima.kumamoto.jp", "kikuchi.kumamoto.jp", "kosa.kumamoto.jp", "kumamoto.kumamoto.jp", "mashiki.kumamoto.jp", "mifune.kumamoto.jp", "minamata.kumamoto.jp", "minamioguni.kumamoto.jp", "nagasu.kumamoto.jp", "nishihara.kumamoto.jp", "oguni.kumamoto.jp", "ozu.kumamoto.jp", "sumoto.kumamoto.jp", "takamori.kumamoto.jp", "uki.kumamoto.jp", "uto.kumamoto.jp", "yamaga.kumamoto.jp", "yamato.kumamoto.jp", "yatsushiro.kumamoto.jp", "ayabe.kyoto.jp", "fukuchiyama.kyoto.jp", "higashiyama.kyoto.jp", "ide.kyoto.jp", "ine.kyoto.jp", "joyo.kyoto.jp", "kameoka.kyoto.jp", "kamo.kyoto.jp", "kita.kyoto.jp", "kizu.kyoto.jp", "kumiyama.kyoto.jp", "kyotamba.kyoto.jp", "kyotanabe.kyoto.jp", "kyotango.kyoto.jp", "maizuru.kyoto.jp", "minami.kyoto.jp", "minamiyamashiro.kyoto.jp", "miyazu.kyoto.jp", "muko.kyoto.jp", "nagaokakyo.kyoto.jp", "nakagyo.kyoto.jp", "nantan.kyoto.jp", "oyamazaki.kyoto.jp", "sakyo.kyoto.jp", "seika.kyoto.jp", "tanabe.kyoto.jp", "uji.kyoto.jp", "ujitawara.kyoto.jp", "wazuka.kyoto.jp", "yamashina.kyoto.jp", "yawata.kyoto.jp", "asahi.mie.jp", "inabe.mie.jp", "ise.mie.jp", "kameyama.mie.jp", "kawagoe.mie.jp", "kiho.mie.jp", "kisosaki.mie.jp", "kiwa.mie.jp", "komono.mie.jp", "kumano.mie.jp", "kuwana.mie.jp", "matsusaka.mie.jp", "meiwa.mie.jp", "mihama.mie.jp", "minamiise.mie.jp", "misugi.mie.jp", "miyama.mie.jp", "nabari.mie.jp", "shima.mie.jp", "suzuka.mie.jp", "tado.mie.jp", "taiki.mie.jp", "taki.mie.jp", "tamaki.mie.jp", "toba.mie.jp", "tsu.mie.jp", "udono.mie.jp", "ureshino.mie.jp", "watarai.mie.jp", "yokkaichi.mie.jp", "furukawa.miyagi.jp", "higashimatsushima.miyagi.jp", "ishinomaki.miyagi.jp", "iwanuma.miyagi.jp", "kakuda.miyagi.jp", "kami.miyagi.jp", "kawasaki.miyagi.jp", "kesennuma.miyagi.jp", "marumori.miyagi.jp", "matsushima.miyagi.jp", "minamisanriku.miyagi.jp", "misato.miyagi.jp", "murata.miyagi.jp", "natori.miyagi.jp", "ogawara.miyagi.jp", "ohira.miyagi.jp", "onagawa.miyagi.jp", "osaki.miyagi.jp", "rifu.miyagi.jp", "semine.miyagi.jp", "shibata.miyagi.jp", "shichikashuku.miyagi.jp", "shikama.miyagi.jp", "shiogama.miyagi.jp", "shiroishi.miyagi.jp", "tagajo.miyagi.jp", "taiwa.miyagi.jp", "tome.miyagi.jp", "tomiya.miyagi.jp", "wakuya.miyagi.jp", "watari.miyagi.jp", "yamamoto.miyagi.jp", "zao.miyagi.jp", "aya.miyazaki.jp", "ebino.miyazaki.jp", "gokase.miyazaki.jp", "hyuga.miyazaki.jp", "kadogawa.miyazaki.jp", "kawaminami.miyazaki.jp", "kijo.miyazaki.jp", "kitagawa.miyazaki.jp", "kitakata.miyazaki.jp", "kitaura.miyazaki.jp", "kobayashi.miyazaki.jp", "kunitomi.miyazaki.jp", "kushima.miyazaki.jp", "mimata.miyazaki.jp", "miyakonojo.miyazaki.jp", "miyazaki.miyazaki.jp", "morotsuka.miyazaki.jp", "nichinan.miyazaki.jp", "nishimera.miyazaki.jp", "nobeoka.miyazaki.jp", "saito.miyazaki.jp", "shiiba.miyazaki.jp", "shintomi.miyazaki.jp", "takaharu.miyazaki.jp", "takanabe.miyazaki.jp", "takazaki.miyazaki.jp", "tsuno.miyazaki.jp", "achi.nagano.jp", "agematsu.nagano.jp", "anan.nagano.jp", "aoki.nagano.jp", "asahi.nagano.jp", "azumino.nagano.jp", "chikuhoku.nagano.jp", "chikuma.nagano.jp", "chino.nagano.jp", "fujimi.nagano.jp", "hakuba.nagano.jp", "hara.nagano.jp", "hiraya.nagano.jp", "iida.nagano.jp", "iijima.nagano.jp", "iiyama.nagano.jp", "iizuna.nagano.jp", "ikeda.nagano.jp", "ikusaka.nagano.jp", "ina.nagano.jp", "karuizawa.nagano.jp", "kawakami.nagano.jp", "kiso.nagano.jp", "kisofukushima.nagano.jp", "kitaaiki.nagano.jp", "komagane.nagano.jp", "komoro.nagano.jp", "matsukawa.nagano.jp", "matsumoto.nagano.jp", "miasa.nagano.jp", "minamiaiki.nagano.jp", "minamimaki.nagano.jp", "minamiminowa.nagano.jp", "minowa.nagano.jp", "miyada.nagano.jp", "miyota.nagano.jp", "mochizuki.nagano.jp", "nagano.nagano.jp", "nagawa.nagano.jp", "nagiso.nagano.jp", "nakagawa.nagano.jp", "nakano.nagano.jp", "nozawaonsen.nagano.jp", "obuse.nagano.jp", "ogawa.nagano.jp", "okaya.nagano.jp", "omachi.nagano.jp", "omi.nagano.jp", "ookuwa.nagano.jp", "ooshika.nagano.jp", "otaki.nagano.jp", "otari.nagano.jp", "sakae.nagano.jp", "sakaki.nagano.jp", "saku.nagano.jp", "sakuho.nagano.jp", "shimosuwa.nagano.jp", "shinanomachi.nagano.jp", "shiojiri.nagano.jp", "suwa.nagano.jp", "suzaka.nagano.jp", "takagi.nagano.jp", "takamori.nagano.jp", "takayama.nagano.jp", "tateshina.nagano.jp", "tatsuno.nagano.jp", "togakushi.nagano.jp", "togura.nagano.jp", "tomi.nagano.jp", "ueda.nagano.jp", "wada.nagano.jp", "yamagata.nagano.jp", "yamanouchi.nagano.jp", "yasaka.nagano.jp", "yasuoka.nagano.jp", "chijiwa.nagasaki.jp", "futsu.nagasaki.jp", "goto.nagasaki.jp", "hasami.nagasaki.jp", "hirado.nagasaki.jp", "iki.nagasaki.jp", "isahaya.nagasaki.jp", "kawatana.nagasaki.jp", "kuchinotsu.nagasaki.jp", "matsuura.nagasaki.jp", "nagasaki.nagasaki.jp", "obama.nagasaki.jp", "omura.nagasaki.jp", "oseto.nagasaki.jp", "saikai.nagasaki.jp", "sasebo.nagasaki.jp", "seihi.nagasaki.jp", "shimabara.nagasaki.jp", "shinkamigoto.nagasaki.jp", "togitsu.nagasaki.jp", "tsushima.nagasaki.jp", "unzen.nagasaki.jp", "ando.nara.jp", "gose.nara.jp", "heguri.nara.jp", "higashiyoshino.nara.jp", "ikaruga.nara.jp", "ikoma.nara.jp", "kamikitayama.nara.jp", "kanmaki.nara.jp", "kashiba.nara.jp", "kashihara.nara.jp", "katsuragi.nara.jp", "kawai.nara.jp", "kawakami.nara.jp", "kawanishi.nara.jp", "koryo.nara.jp", "kurotaki.nara.jp", "mitsue.nara.jp", "miyake.nara.jp", "nara.nara.jp", "nosegawa.nara.jp", "oji.nara.jp", "ouda.nara.jp", "oyodo.nara.jp", "sakurai.nara.jp", "sango.nara.jp", "shimoichi.nara.jp", "shimokitayama.nara.jp", "shinjo.nara.jp", "soni.nara.jp", "takatori.nara.jp", "tawaramoto.nara.jp", "tenkawa.nara.jp", "tenri.nara.jp", "uda.nara.jp", "yamatokoriyama.nara.jp", "yamatotakada.nara.jp", "yamazoe.nara.jp", "yoshino.nara.jp", "aga.niigata.jp", "agano.niigata.jp", "gosen.niigata.jp", "itoigawa.niigata.jp", "izumozaki.niigata.jp", "joetsu.niigata.jp", "kamo.niigata.jp", "kariwa.niigata.jp", "kashiwazaki.niigata.jp", "minamiuonuma.niigata.jp", "mitsuke.niigata.jp", "muika.niigata.jp", "murakami.niigata.jp", "myoko.niigata.jp", "nagaoka.niigata.jp", "niigata.niigata.jp", "ojiya.niigata.jp", "omi.niigata.jp", "sado.niigata.jp", "sanjo.niigata.jp", "seiro.niigata.jp", "seirou.niigata.jp", "sekikawa.niigata.jp", "shibata.niigata.jp", "tagami.niigata.jp", "tainai.niigata.jp", "tochio.niigata.jp", "tokamachi.niigata.jp", "tsubame.niigata.jp", "tsunan.niigata.jp", "uonuma.niigata.jp", "yahiko.niigata.jp", "yoita.niigata.jp", "yuzawa.niigata.jp", "beppu.oita.jp", "bungoono.oita.jp", "bungotakada.oita.jp", "hasama.oita.jp", "hiji.oita.jp", "himeshima.oita.jp", "hita.oita.jp", "kamitsue.oita.jp", "kokonoe.oita.jp", "kuju.oita.jp", "kunisaki.oita.jp", "kusu.oita.jp", "oita.oita.jp", "saiki.oita.jp", "taketa.oita.jp", "tsukumi.oita.jp", "usa.oita.jp", "usuki.oita.jp", "yufu.oita.jp", "akaiwa.okayama.jp", "asakuchi.okayama.jp", "bizen.okayama.jp", "hayashima.okayama.jp", "ibara.okayama.jp", "kagamino.okayama.jp", "kasaoka.okayama.jp", "kibichuo.okayama.jp", "kumenan.okayama.jp", "kurashiki.okayama.jp", "maniwa.okayama.jp", "misaki.okayama.jp", "nagi.okayama.jp", "niimi.okayama.jp", "nishiawakura.okayama.jp", "okayama.okayama.jp", "satosho.okayama.jp", "setouchi.okayama.jp", "shinjo.okayama.jp", "shoo.okayama.jp", "soja.okayama.jp", "takahashi.okayama.jp", "tamano.okayama.jp", "tsuyama.okayama.jp", "wake.okayama.jp", "yakage.okayama.jp", "aguni.okinawa.jp", "ginowan.okinawa.jp", "ginoza.okinawa.jp", "gushikami.okinawa.jp", "haebaru.okinawa.jp", "higashi.okinawa.jp", "hirara.okinawa.jp", "iheya.okinawa.jp", "ishigaki.okinawa.jp", "ishikawa.okinawa.jp", "itoman.okinawa.jp", "izena.okinawa.jp", "kadena.okinawa.jp", "kin.okinawa.jp", "kitadaito.okinawa.jp", "kitanakagusuku.okinawa.jp", "kumejima.okinawa.jp", "kunigami.okinawa.jp", "minamidaito.okinawa.jp", "motobu.okinawa.jp", "nago.okinawa.jp", "naha.okinawa.jp", "nakagusuku.okinawa.jp", "nakijin.okinawa.jp", "nanjo.okinawa.jp", "nishihara.okinawa.jp", "ogimi.okinawa.jp", "okinawa.okinawa.jp", "onna.okinawa.jp", "shimoji.okinawa.jp", "taketomi.okinawa.jp", "tarama.okinawa.jp", "tokashiki.okinawa.jp", "tomigusuku.okinawa.jp", "tonaki.okinawa.jp", "urasoe.okinawa.jp", "uruma.okinawa.jp", "yaese.okinawa.jp", "yomitan.okinawa.jp", "yonabaru.okinawa.jp", "yonaguni.okinawa.jp", "zamami.okinawa.jp", "abeno.osaka.jp", "chihayaakasaka.osaka.jp", "chuo.osaka.jp", "daito.osaka.jp", "fujiidera.osaka.jp", "habikino.osaka.jp", "hannan.osaka.jp", "higashiosaka.osaka.jp", "higashisumiyoshi.osaka.jp", "higashiyodogawa.osaka.jp", "hirakata.osaka.jp", "ibaraki.osaka.jp", "ikeda.osaka.jp", "izumi.osaka.jp", "izumiotsu.osaka.jp", "izumisano.osaka.jp", "kadoma.osaka.jp", "kaizuka.osaka.jp", "kanan.osaka.jp", "kashiwara.osaka.jp", "katano.osaka.jp", "kawachinagano.osaka.jp", "kishiwada.osaka.jp", "kita.osaka.jp", "kumatori.osaka.jp", "matsubara.osaka.jp", "minato.osaka.jp", "minoh.osaka.jp", "misaki.osaka.jp", "moriguchi.osaka.jp", "neyagawa.osaka.jp", "nishi.osaka.jp", "nose.osaka.jp", "osakasayama.osaka.jp", "sakai.osaka.jp", "sayama.osaka.jp", "sennan.osaka.jp", "settsu.osaka.jp", "shijonawate.osaka.jp", "shimamoto.osaka.jp", "suita.osaka.jp", "tadaoka.osaka.jp", "taishi.osaka.jp", "tajiri.osaka.jp", "takaishi.osaka.jp", "takatsuki.osaka.jp", "tondabayashi.osaka.jp", "toyonaka.osaka.jp", "toyono.osaka.jp", "yao.osaka.jp", "ariake.saga.jp", "arita.saga.jp", "fukudomi.saga.jp", "genkai.saga.jp", "hamatama.saga.jp", "hizen.saga.jp", "imari.saga.jp", "kamimine.saga.jp", "kanzaki.saga.jp", "karatsu.saga.jp", "kashima.saga.jp", "kitagata.saga.jp", "kitahata.saga.jp", "kiyama.saga.jp", "kouhoku.saga.jp", "kyuragi.saga.jp", "nishiarita.saga.jp", "ogi.saga.jp", "omachi.saga.jp", "ouchi.saga.jp", "saga.saga.jp", "shiroishi.saga.jp", "taku.saga.jp", "tara.saga.jp", "tosu.saga.jp", "yoshinogari.saga.jp", "arakawa.saitama.jp", "asaka.saitama.jp", "chichibu.saitama.jp", "fujimi.saitama.jp", "fujimino.saitama.jp", "fukaya.saitama.jp", "hanno.saitama.jp", "hanyu.saitama.jp", "hasuda.saitama.jp", "hatogaya.saitama.jp", "hatoyama.saitama.jp", "hidaka.saitama.jp", "higashichichibu.saitama.jp", "higashimatsuyama.saitama.jp", "honjo.saitama.jp", "ina.saitama.jp", "iruma.saitama.jp", "iwatsuki.saitama.jp", "kamiizumi.saitama.jp", "kamikawa.saitama.jp", "kamisato.saitama.jp", "kasukabe.saitama.jp", "kawagoe.saitama.jp", "kawaguchi.saitama.jp", "kawajima.saitama.jp", "kazo.saitama.jp", "kitamoto.saitama.jp", "koshigaya.saitama.jp", "kounosu.saitama.jp", "kuki.saitama.jp", "kumagaya.saitama.jp", "matsubushi.saitama.jp", "minano.saitama.jp", "misato.saitama.jp", "miyashiro.saitama.jp", "miyoshi.saitama.jp", "moroyama.saitama.jp", "nagatoro.saitama.jp", "namegawa.saitama.jp", "niiza.saitama.jp", "ogano.saitama.jp", "ogawa.saitama.jp", "ogose.saitama.jp", "okegawa.saitama.jp", "omiya.saitama.jp", "otaki.saitama.jp", "ranzan.saitama.jp", "ryokami.saitama.jp", "saitama.saitama.jp", "sakado.saitama.jp", "satte.saitama.jp", "sayama.saitama.jp", "shiki.saitama.jp", "shiraoka.saitama.jp", "soka.saitama.jp", "sugito.saitama.jp", "toda.saitama.jp", "tokigawa.saitama.jp", "tokorozawa.saitama.jp", "tsurugashima.saitama.jp", "urawa.saitama.jp", "warabi.saitama.jp", "yashio.saitama.jp", "yokoze.saitama.jp", "yono.saitama.jp", "yorii.saitama.jp", "yoshida.saitama.jp", "yoshikawa.saitama.jp", "yoshimi.saitama.jp", "aisho.shiga.jp", "gamo.shiga.jp", "higashiomi.shiga.jp", "hikone.shiga.jp", "koka.shiga.jp", "konan.shiga.jp", "kosei.shiga.jp", "koto.shiga.jp", "kusatsu.shiga.jp", "maibara.shiga.jp", "moriyama.shiga.jp", "nagahama.shiga.jp", "nishiazai.shiga.jp", "notogawa.shiga.jp", "omihachiman.shiga.jp", "otsu.shiga.jp", "ritto.shiga.jp", "ryuoh.shiga.jp", "takashima.shiga.jp", "takatsuki.shiga.jp", "torahime.shiga.jp", "toyosato.shiga.jp", "yasu.shiga.jp", "akagi.shimane.jp", "ama.shimane.jp", "gotsu.shimane.jp", "hamada.shimane.jp", "higashiizumo.shimane.jp", "hikawa.shimane.jp", "hikimi.shimane.jp", "izumo.shimane.jp", "kakinoki.shimane.jp", "masuda.shimane.jp", "matsue.shimane.jp", "misato.shimane.jp", "nishinoshima.shimane.jp", "ohda.shimane.jp", "okinoshima.shimane.jp", "okuizumo.shimane.jp", "shimane.shimane.jp", "tamayu.shimane.jp", "tsuwano.shimane.jp", "unnan.shimane.jp", "yakumo.shimane.jp", "yasugi.shimane.jp", "yatsuka.shimane.jp", "arai.shizuoka.jp", "atami.shizuoka.jp", "fuji.shizuoka.jp", "fujieda.shizuoka.jp", "fujikawa.shizuoka.jp", "fujinomiya.shizuoka.jp", "fukuroi.shizuoka.jp", "gotemba.shizuoka.jp", "haibara.shizuoka.jp", "hamamatsu.shizuoka.jp", "higashiizu.shizuoka.jp", "ito.shizuoka.jp", "iwata.shizuoka.jp", "izu.shizuoka.jp", "izunokuni.shizuoka.jp", "kakegawa.shizuoka.jp", "kannami.shizuoka.jp", "kawanehon.shizuoka.jp", "kawazu.shizuoka.jp", "kikugawa.shizuoka.jp", "kosai.shizuoka.jp", "makinohara.shizuoka.jp", "matsuzaki.shizuoka.jp", "minamiizu.shizuoka.jp", "mishima.shizuoka.jp", "morimachi.shizuoka.jp", "nishiizu.shizuoka.jp", "numazu.shizuoka.jp", "omaezaki.shizuoka.jp", "shimada.shizuoka.jp", "shimizu.shizuoka.jp", "shimoda.shizuoka.jp", "shizuoka.shizuoka.jp", "susono.shizuoka.jp", "yaizu.shizuoka.jp", "yoshida.shizuoka.jp", "ashikaga.tochigi.jp", "bato.tochigi.jp", "haga.tochigi.jp", "ichikai.tochigi.jp", "iwafune.tochigi.jp", "kaminokawa.tochigi.jp", "kanuma.tochigi.jp", "karasuyama.tochigi.jp", "kuroiso.tochigi.jp", "mashiko.tochigi.jp", "mibu.tochigi.jp", "moka.tochigi.jp", "motegi.tochigi.jp", "nasu.tochigi.jp", "nasushiobara.tochigi.jp", "nikko.tochigi.jp", "nishikata.tochigi.jp", "nogi.tochigi.jp", "ohira.tochigi.jp", "ohtawara.tochigi.jp", "oyama.tochigi.jp", "sakura.tochigi.jp", "sano.tochigi.jp", "shimotsuke.tochigi.jp", "shioya.tochigi.jp", "takanezawa.tochigi.jp", "tochigi.tochigi.jp", "tsuga.tochigi.jp", "ujiie.tochigi.jp", "utsunomiya.tochigi.jp", "yaita.tochigi.jp", "aizumi.tokushima.jp", "anan.tokushima.jp", "ichiba.tokushima.jp", "itano.tokushima.jp", "kainan.tokushima.jp", "komatsushima.tokushima.jp", "matsushige.tokushima.jp", "mima.tokushima.jp", "minami.tokushima.jp", "miyoshi.tokushima.jp", "mugi.tokushima.jp", "nakagawa.tokushima.jp", "naruto.tokushima.jp", "sanagochi.tokushima.jp", "shishikui.tokushima.jp", "tokushima.tokushima.jp", "wajiki.tokushima.jp", "adachi.tokyo.jp", "akiruno.tokyo.jp", "akishima.tokyo.jp", "aogashima.tokyo.jp", "arakawa.tokyo.jp", "bunkyo.tokyo.jp", "chiyoda.tokyo.jp", "chofu.tokyo.jp", "chuo.tokyo.jp", "edogawa.tokyo.jp", "fuchu.tokyo.jp", "fussa.tokyo.jp", "hachijo.tokyo.jp", "hachioji.tokyo.jp", "hamura.tokyo.jp", "higashikurume.tokyo.jp", "higashimurayama.tokyo.jp", "higashiyamato.tokyo.jp", "hino.tokyo.jp", "hinode.tokyo.jp", "hinohara.tokyo.jp", "inagi.tokyo.jp", "itabashi.tokyo.jp", "katsushika.tokyo.jp", "kita.tokyo.jp", "kiyose.tokyo.jp", "kodaira.tokyo.jp", "koganei.tokyo.jp", "kokubunji.tokyo.jp", "komae.tokyo.jp", "koto.tokyo.jp", "kouzushima.tokyo.jp", "kunitachi.tokyo.jp", "machida.tokyo.jp", "meguro.tokyo.jp", "minato.tokyo.jp", "mitaka.tokyo.jp", "mizuho.tokyo.jp", "musashimurayama.tokyo.jp", "musashino.tokyo.jp", "nakano.tokyo.jp", "nerima.tokyo.jp", "ogasawara.tokyo.jp", "okutama.tokyo.jp", "ome.tokyo.jp", "oshima.tokyo.jp", "ota.tokyo.jp", "setagaya.tokyo.jp", "shibuya.tokyo.jp", "shinagawa.tokyo.jp", "shinjuku.tokyo.jp", "suginami.tokyo.jp", "sumida.tokyo.jp", "tachikawa.tokyo.jp", "taito.tokyo.jp", "tama.tokyo.jp", "toshima.tokyo.jp", "chizu.tottori.jp", "hino.tottori.jp", "kawahara.tottori.jp", "koge.tottori.jp", "kotoura.tottori.jp", "misasa.tottori.jp", "nanbu.tottori.jp", "nichinan.tottori.jp", "sakaiminato.tottori.jp", "tottori.tottori.jp", "wakasa.tottori.jp", "yazu.tottori.jp", "yonago.tottori.jp", "asahi.toyama.jp", "fuchu.toyama.jp", "fukumitsu.toyama.jp", "funahashi.toyama.jp", "himi.toyama.jp", "imizu.toyama.jp", "inami.toyama.jp", "johana.toyama.jp", "kamiichi.toyama.jp", "kurobe.toyama.jp", "nakaniikawa.toyama.jp", "namerikawa.toyama.jp", "nanto.toyama.jp", "nyuzen.toyama.jp", "oyabe.toyama.jp", "taira.toyama.jp", "takaoka.toyama.jp", "tateyama.toyama.jp", "toga.toyama.jp", "tonami.toyama.jp", "toyama.toyama.jp", "unazuki.toyama.jp", "uozu.toyama.jp", "yamada.toyama.jp", "arida.wakayama.jp", "aridagawa.wakayama.jp", "gobo.wakayama.jp", "hashimoto.wakayama.jp", "hidaka.wakayama.jp", "hirogawa.wakayama.jp", "inami.wakayama.jp", "iwade.wakayama.jp", "kainan.wakayama.jp", "kamitonda.wakayama.jp", "katsuragi.wakayama.jp", "kimino.wakayama.jp", "kinokawa.wakayama.jp", "kitayama.wakayama.jp", "koya.wakayama.jp", "koza.wakayama.jp", "kozagawa.wakayama.jp", "kudoyama.wakayama.jp", "kushimoto.wakayama.jp", "mihama.wakayama.jp", "misato.wakayama.jp", "nachikatsuura.wakayama.jp", "shingu.wakayama.jp", "shirahama.wakayama.jp", "taiji.wakayama.jp", "tanabe.wakayama.jp", "wakayama.wakayama.jp", "yuasa.wakayama.jp", "yura.wakayama.jp", "asahi.yamagata.jp", "funagata.yamagata.jp", "higashine.yamagata.jp", "iide.yamagata.jp", "kahoku.yamagata.jp", "kaminoyama.yamagata.jp", "kaneyama.yamagata.jp", "kawanishi.yamagata.jp", "mamurogawa.yamagata.jp", "mikawa.yamagata.jp", "murayama.yamagata.jp", "nagai.yamagata.jp", "nakayama.yamagata.jp", "nanyo.yamagata.jp", "nishikawa.yamagata.jp", "obanazawa.yamagata.jp", "oe.yamagata.jp", "oguni.yamagata.jp", "ohkura.yamagata.jp", "oishida.yamagata.jp", "sagae.yamagata.jp", "sakata.yamagata.jp", "sakegawa.yamagata.jp", "shinjo.yamagata.jp", "shirataka.yamagata.jp", "shonai.yamagata.jp", "takahata.yamagata.jp", "tendo.yamagata.jp", "tozawa.yamagata.jp", "tsuruoka.yamagata.jp", "yamagata.yamagata.jp", "yamanobe.yamagata.jp", "yonezawa.yamagata.jp", "yuza.yamagata.jp", "abu.yamaguchi.jp", "hagi.yamaguchi.jp", "hikari.yamaguchi.jp", "hofu.yamaguchi.jp", "iwakuni.yamaguchi.jp", "kudamatsu.yamaguchi.jp", "mitou.yamaguchi.jp", "nagato.yamaguchi.jp", "oshima.yamaguchi.jp", "shimonoseki.yamaguchi.jp", "shunan.yamaguchi.jp", "tabuse.yamaguchi.jp", "tokuyama.yamaguchi.jp", "toyota.yamaguchi.jp", "ube.yamaguchi.jp", "yuu.yamaguchi.jp", "chuo.yamanashi.jp", "doshi.yamanashi.jp", "fuefuki.yamanashi.jp", "fujikawa.yamanashi.jp", "fujikawaguchiko.yamanashi.jp", "fujiyoshida.yamanashi.jp", "hayakawa.yamanashi.jp", "hokuto.yamanashi.jp", "ichikawamisato.yamanashi.jp", "kai.yamanashi.jp", "kofu.yamanashi.jp", "koshu.yamanashi.jp", "kosuge.yamanashi.jp", "minami-alps.yamanashi.jp", "minobu.yamanashi.jp", "nakamichi.yamanashi.jp", "nanbu.yamanashi.jp", "narusawa.yamanashi.jp", "nirasaki.yamanashi.jp", "nishikatsura.yamanashi.jp", "oshino.yamanashi.jp", "otsuki.yamanashi.jp", "showa.yamanashi.jp", "tabayama.yamanashi.jp", "tsuru.yamanashi.jp", "uenohara.yamanashi.jp", "yamanakako.yamanashi.jp", "yamanashi.yamanashi.jp", "*.ke", "kg", "org.kg", "net.kg", "com.kg", "edu.kg", "gov.kg", "mil.kg", "*.kh", "ki", "edu.ki", "biz.ki", "net.ki", "org.ki", "gov.ki", "info.ki", "com.ki", "km", "org.km", "nom.km", "gov.km", "prd.km", "tm.km", "edu.km", "mil.km", "ass.km", "com.km", "coop.km", "asso.km", "presse.km", "medecin.km", "notaires.km", "pharmaciens.km", "veterinaire.km", "gouv.km", "kn", "net.kn", "org.kn", "edu.kn", "gov.kn", "kp", "com.kp", "edu.kp", "gov.kp", "org.kp", "rep.kp", "tra.kp", "kr", "ac.kr", "co.kr", "es.kr", "go.kr", "hs.kr", "kg.kr", "mil.kr", "ms.kr", "ne.kr", "or.kr", "pe.kr", "re.kr", "sc.kr", "busan.kr", "chungbuk.kr", "chungnam.kr", "daegu.kr", "daejeon.kr", "gangwon.kr", "gwangju.kr", "gyeongbuk.kr", "gyeonggi.kr", "gyeongnam.kr", "incheon.kr", "jeju.kr", "jeonbuk.kr", "jeonnam.kr", "seoul.kr", "ulsan.kr", "*.kw", "ky", "edu.ky", "gov.ky", "com.ky", "org.ky", "net.ky", "kz", "org.kz", "edu.kz", "net.kz", "gov.kz", "mil.kz", "com.kz", "la", "int.la", "net.la", "info.la", "edu.la", "gov.la", "per.la", "com.la", "org.la", "lb", "com.lb", "edu.lb", "gov.lb", "net.lb", "org.lb", "lc", "com.lc", "net.lc", "co.lc", "org.lc", "edu.lc", "gov.lc", "li", "lk", "gov.lk", "sch.lk", "net.lk", "int.lk", "com.lk", "org.lk", "edu.lk", "ngo.lk", "soc.lk", "web.lk", "ltd.lk", "assn.lk", "grp.lk", "hotel.lk", "lr", "com.lr", "edu.lr", "gov.lr", "org.lr", "net.lr", "ls", "co.ls", "org.ls", "lt", "gov.lt", "lu", "lv", "com.lv", "edu.lv", "gov.lv", "org.lv", "mil.lv", "id.lv", "net.lv", "asn.lv", "conf.lv", "ly", "com.ly", "net.ly", "gov.ly", "plc.ly", "edu.ly", "sch.ly", "med.ly", "org.ly", "id.ly", "ma", "co.ma", "net.ma", "gov.ma", "org.ma", "ac.ma", "press.ma", "mc", "tm.mc", "asso.mc", "md", "me", "co.me", "net.me", "org.me", "edu.me", "ac.me", "gov.me", "its.me", "priv.me", "mg", "org.mg", "nom.mg", "gov.mg", "prd.mg", "tm.mg", "edu.mg", "mil.mg", "com.mg", "mh", "mil", "mk", "com.mk", "org.mk", "net.mk", "edu.mk", "gov.mk", "inf.mk", "name.mk", "ml", "com.ml", "edu.ml", "gouv.ml", "gov.ml", "net.ml", "org.ml", "presse.ml", "*.mm", "mn", "gov.mn", "edu.mn", "org.mn", "mo", "com.mo", "net.mo", "org.mo", "edu.mo", "gov.mo", "mobi", "mp", "mq", "mr", "gov.mr", "ms", "mt", "com.mt", "edu.mt", "net.mt", "org.mt", "mu", "com.mu", "net.mu", "org.mu", "gov.mu", "ac.mu", "co.mu", "or.mu", "museum", "academy.museum", "agriculture.museum", "air.museum", "airguard.museum", "alabama.museum", "alaska.museum", "amber.museum", "ambulance.museum", "american.museum", "americana.museum", "americanantiques.museum", "americanart.museum", "amsterdam.museum", "and.museum", "annefrank.museum", "anthro.museum", "anthropology.museum", "antiques.museum", "aquarium.museum", "arboretum.museum", "archaeological.museum", "archaeology.museum", "architecture.museum", "art.museum", "artanddesign.museum", "artcenter.museum", "artdeco.museum", "arteducation.museum", "artgallery.museum", "arts.museum", "artsandcrafts.museum", "asmatart.museum", "assassination.museum", "assisi.museum", "association.museum", "astronomy.museum", "atlanta.museum", "austin.museum", "australia.museum", "automotive.museum", "aviation.museum", "axis.museum", "badajoz.museum", "baghdad.museum", "bahn.museum", "bale.museum", "baltimore.museum", "barcelona.museum", "baseball.museum", "basel.museum", "baths.museum", "bauern.museum", "beauxarts.museum", "beeldengeluid.museum", "bellevue.museum", "bergbau.museum", "berkeley.museum", "berlin.museum", "bern.museum", "bible.museum", "bilbao.museum", "bill.museum", "birdart.museum", "birthplace.museum", "bonn.museum", "boston.museum", "botanical.museum", "botanicalgarden.museum", "botanicgarden.museum", "botany.museum", "brandywinevalley.museum", "brasil.museum", "bristol.museum", "british.museum", "britishcolumbia.museum", "broadcast.museum", "brunel.museum", "brussel.museum", "brussels.museum", "bruxelles.museum", "building.museum", "burghof.museum", "bus.museum", "bushey.museum", "cadaques.museum", "california.museum", "cambridge.museum", "can.museum", "canada.museum", "capebreton.museum", "carrier.museum", "cartoonart.museum", "casadelamoneda.museum", "castle.museum", "castres.museum", "celtic.museum", "center.museum", "chattanooga.museum", "cheltenham.museum", "chesapeakebay.museum", "chicago.museum", "children.museum", "childrens.museum", "childrensgarden.museum", "chiropractic.museum", "chocolate.museum", "christiansburg.museum", "cincinnati.museum", "cinema.museum", "circus.museum", "civilisation.museum", "civilization.museum", "civilwar.museum", "clinton.museum", "clock.museum", "coal.museum", "coastaldefence.museum", "cody.museum", "coldwar.museum", "collection.museum", "colonialwilliamsburg.museum", "coloradoplateau.museum", "columbia.museum", "columbus.museum", "communication.museum", "communications.museum", "community.museum", "computer.museum", "computerhistory.museum", "xn--comunicaes-v6a2o.museum", "contemporary.museum", "contemporaryart.museum", "convent.museum", "copenhagen.museum", "corporation.museum", "xn--correios-e-telecomunicaes-ghc29a.museum", "corvette.museum", "costume.museum", "countryestate.museum", "county.museum", "crafts.museum", "cranbrook.museum", "creation.museum", "cultural.museum", "culturalcenter.museum", "culture.museum", "cyber.museum", "cymru.museum", "dali.museum", "dallas.museum", "database.museum", "ddr.museum", "decorativearts.museum", "delaware.museum", "delmenhorst.museum", "denmark.museum", "depot.museum", "design.museum", "detroit.museum", "dinosaur.museum", "discovery.museum", "dolls.museum", "donostia.museum", "durham.museum", "eastafrica.museum", "eastcoast.museum", "education.museum", "educational.museum", "egyptian.museum", "eisenbahn.museum", "elburg.museum", "elvendrell.museum", "embroidery.museum", "encyclopedic.museum", "england.museum", "entomology.museum", "environment.museum", "environmentalconservation.museum", "epilepsy.museum", "essex.museum", "estate.museum", "ethnology.museum", "exeter.museum", "exhibition.museum", "family.museum", "farm.museum", "farmequipment.museum", "farmers.museum", "farmstead.museum", "field.museum", "figueres.museum", "filatelia.museum", "film.museum", "fineart.museum", "finearts.museum", "finland.museum", "flanders.museum", "florida.museum", "force.museum", "fortmissoula.museum", "fortworth.museum", "foundation.museum", "francaise.museum", "frankfurt.museum", "franziskaner.museum", "freemasonry.museum", "freiburg.museum", "fribourg.museum", "frog.museum", "fundacio.museum", "furniture.museum", "gallery.museum", "garden.museum", "gateway.museum", "geelvinck.museum", "gemological.museum", "geology.museum", "georgia.museum", "giessen.museum", "glas.museum", "glass.museum", "gorge.museum", "grandrapids.museum", "graz.museum", "guernsey.museum", "halloffame.museum", "hamburg.museum", "handson.museum", "harvestcelebration.museum", "hawaii.museum", "health.museum", "heimatunduhren.museum", "hellas.museum", "helsinki.museum", "hembygdsforbund.museum", "heritage.museum", "histoire.museum", "historical.museum", "historicalsociety.museum", "historichouses.museum", "historisch.museum", "historisches.museum", "history.museum", "historyofscience.museum", "horology.museum", "house.museum", "humanities.museum", "illustration.museum", "imageandsound.museum", "indian.museum", "indiana.museum", "indianapolis.museum", "indianmarket.museum", "intelligence.museum", "interactive.museum", "iraq.museum", "iron.museum", "isleofman.museum", "jamison.museum", "jefferson.museum", "jerusalem.museum", "jewelry.museum", "jewish.museum", "jewishart.museum", "jfk.museum", "journalism.museum", "judaica.museum", "judygarland.museum", "juedisches.museum", "juif.museum", "karate.museum", "karikatur.museum", "kids.museum", "koebenhavn.museum", "koeln.museum", "kunst.museum", "kunstsammlung.museum", "kunstunddesign.museum", "labor.museum", "labour.museum", "lajolla.museum", "lancashire.museum", "landes.museum", "lans.museum", "xn--lns-qla.museum", "larsson.museum", "lewismiller.museum", "lincoln.museum", "linz.museum", "living.museum", "livinghistory.museum", "localhistory.museum", "london.museum", "losangeles.museum", "louvre.museum", "loyalist.museum", "lucerne.museum", "luxembourg.museum", "luzern.museum", "mad.museum", "madrid.museum", "mallorca.museum", "manchester.museum", "mansion.museum", "mansions.museum", "manx.museum", "marburg.museum", "maritime.museum", "maritimo.museum", "maryland.museum", "marylhurst.museum", "media.museum", "medical.museum", "medizinhistorisches.museum", "meeres.museum", "memorial.museum", "mesaverde.museum", "michigan.museum", "midatlantic.museum", "military.museum", "mill.museum", "miners.museum", "mining.museum", "minnesota.museum", "missile.museum", "missoula.museum", "modern.museum", "moma.museum", "money.museum", "monmouth.museum", "monticello.museum", "montreal.museum", "moscow.museum", "motorcycle.museum", "muenchen.museum", "muenster.museum", "mulhouse.museum", "muncie.museum", "museet.museum", "museumcenter.museum", "museumvereniging.museum", "music.museum", "national.museum", "nationalfirearms.museum", "nationalheritage.museum", "nativeamerican.museum", "naturalhistory.museum", "naturalhistorymuseum.museum", "naturalsciences.museum", "nature.museum", "naturhistorisches.museum", "natuurwetenschappen.museum", "naumburg.museum", "naval.museum", "nebraska.museum", "neues.museum", "newhampshire.museum", "newjersey.museum", "newmexico.museum", "newport.museum", "newspaper.museum", "newyork.museum", "niepce.museum", "norfolk.museum", "north.museum", "nrw.museum", "nuernberg.museum", "nuremberg.museum", "nyc.museum", "nyny.museum", "oceanographic.museum", "oceanographique.museum", "omaha.museum", "online.museum", "ontario.museum", "openair.museum", "oregon.museum", "oregontrail.museum", "otago.museum", "oxford.museum", "pacific.museum", "paderborn.museum", "palace.museum", "paleo.museum", "palmsprings.museum", "panama.museum", "paris.museum", "pasadena.museum", "pharmacy.museum", "philadelphia.museum", "philadelphiaarea.museum", "philately.museum", "phoenix.museum", "photography.museum", "pilots.museum", "pittsburgh.museum", "planetarium.museum", "plantation.museum", "plants.museum", "plaza.museum", "portal.museum", "portland.museum", "portlligat.museum", "posts-and-telecommunications.museum", "preservation.museum", "presidio.museum", "press.museum", "project.museum", "public.museum", "pubol.museum", "quebec.museum", "railroad.museum", "railway.museum", "research.museum", "resistance.museum", "riodejaneiro.museum", "rochester.museum", "rockart.museum", "roma.museum", "russia.museum", "saintlouis.museum", "salem.museum", "salvadordali.museum", "salzburg.museum", "sandiego.museum", "sanfrancisco.museum", "santabarbara.museum", "santacruz.museum", "santafe.museum", "saskatchewan.museum", "satx.museum", "savannahga.museum", "schlesisches.museum", "schoenbrunn.museum", "schokoladen.museum", "school.museum", "schweiz.museum", "science.museum", "scienceandhistory.museum", "scienceandindustry.museum", "sciencecenter.museum", "sciencecenters.museum", "science-fiction.museum", "sciencehistory.museum", "sciences.museum", "sciencesnaturelles.museum", "scotland.museum", "seaport.museum", "settlement.museum", "settlers.museum", "shell.museum", "sherbrooke.museum", "sibenik.museum", "silk.museum", "ski.museum", "skole.museum", "society.museum", "sologne.museum", "soundandvision.museum", "southcarolina.museum", "southwest.museum", "space.museum", "spy.museum", "square.museum", "stadt.museum", "stalbans.museum", "starnberg.museum", "state.museum", "stateofdelaware.museum", "station.museum", "steam.museum", "steiermark.museum", "stjohn.museum", "stockholm.museum", "stpetersburg.museum", "stuttgart.museum", "suisse.museum", "surgeonshall.museum", "surrey.museum", "svizzera.museum", "sweden.museum", "sydney.museum", "tank.museum", "tcm.museum", "technology.museum", "telekommunikation.museum", "television.museum", "texas.museum", "textile.museum", "theater.museum", "time.museum", "timekeeping.museum", "topology.museum", "torino.museum", "touch.museum", "town.museum", "transport.museum", "tree.museum", "trolley.museum", "trust.museum", "trustee.museum", "uhren.museum", "ulm.museum", "undersea.museum", "university.museum", "usa.museum", "usantiques.museum", "usarts.museum", "uscountryestate.museum", "usculture.museum", "usdecorativearts.museum", "usgarden.museum", "ushistory.museum", "ushuaia.museum", "uslivinghistory.museum", "utah.museum", "uvic.museum", "valley.museum", "vantaa.museum", "versailles.museum", "viking.museum", "village.museum", "virginia.museum", "virtual.museum", "virtuel.museum", "vlaanderen.museum", "volkenkunde.museum", "wales.museum", "wallonie.museum", "war.museum", "washingtondc.museum", "watchandclock.museum", "watch-and-clock.museum", "western.museum", "westfalen.museum", "whaling.museum", "wildlife.museum", "williamsburg.museum", "windmill.museum", "workshop.museum", "york.museum", "yorkshire.museum", "yosemite.museum", "youth.museum", "zoological.museum", "zoology.museum", "xn--9dbhblg6di.museum", "xn--h1aegh.museum", "mv", "aero.mv", "biz.mv", "com.mv", "coop.mv", "edu.mv", "gov.mv", "info.mv", "int.mv", "mil.mv", "museum.mv", "name.mv", "net.mv", "org.mv", "pro.mv", "mw", "ac.mw", "biz.mw", "co.mw", "com.mw", "coop.mw", "edu.mw", "gov.mw", "int.mw", "museum.mw", "net.mw", "org.mw", "mx", "com.mx", "org.mx", "gob.mx", "edu.mx", "net.mx", "my", "com.my", "net.my", "org.my", "gov.my", "edu.my", "mil.my", "name.my", "*.mz", "!teledata.mz", "na", "info.na", "pro.na", "name.na", "school.na", "or.na", "dr.na", "us.na", "mx.na", "ca.na", "in.na", "cc.na", "tv.na", "ws.na", "mobi.na", "co.na", "com.na", "org.na", "name", "nc", "asso.nc", "ne", "net", "nf", "com.nf", "net.nf", "per.nf", "rec.nf", "web.nf", "arts.nf", "firm.nf", "info.nf", "other.nf", "store.nf", "ng", "com.ng", "edu.ng", "name.ng", "net.ng", "org.ng", "sch.ng", "gov.ng", "mil.ng", "mobi.ng", "*.ni", "nl", "bv.nl", "no", "fhs.no", "vgs.no", "fylkesbibl.no", "folkebibl.no", "museum.no", "idrett.no", "priv.no", "mil.no", "stat.no", "dep.no", "kommune.no", "herad.no", "aa.no", "ah.no", "bu.no", "fm.no", "hl.no", "hm.no", "jan-mayen.no", "mr.no", "nl.no", "nt.no", "of.no", "ol.no", "oslo.no", "rl.no", "sf.no", "st.no", "svalbard.no", "tm.no", "tr.no", "va.no", "vf.no", "gs.aa.no", "gs.ah.no", "gs.bu.no", "gs.fm.no", "gs.hl.no", "gs.hm.no", "gs.jan-mayen.no", "gs.mr.no", "gs.nl.no", "gs.nt.no", "gs.of.no", "gs.ol.no", "gs.oslo.no", "gs.rl.no", "gs.sf.no", "gs.st.no", "gs.svalbard.no", "gs.tm.no", "gs.tr.no", "gs.va.no", "gs.vf.no", "akrehamn.no", "xn--krehamn-dxa.no", "algard.no", "xn--lgrd-poac.no", "arna.no", "brumunddal.no", "bryne.no", "bronnoysund.no", "xn--brnnysund-m8ac.no", "drobak.no", "xn--drbak-wua.no", "egersund.no", "fetsund.no", "floro.no", "xn--flor-jra.no", "fredrikstad.no", "hokksund.no", "honefoss.no", "xn--hnefoss-q1a.no", "jessheim.no", "jorpeland.no", "xn--jrpeland-54a.no", "kirkenes.no", "kopervik.no", "krokstadelva.no", "langevag.no", "xn--langevg-jxa.no", "leirvik.no", "mjondalen.no", "xn--mjndalen-64a.no", "mo-i-rana.no", "mosjoen.no", "xn--mosjen-eya.no", "nesoddtangen.no", "orkanger.no", "osoyro.no", "xn--osyro-wua.no", "raholt.no", "xn--rholt-mra.no", "sandnessjoen.no", "xn--sandnessjen-ogb.no", "skedsmokorset.no", "slattum.no", "spjelkavik.no", "stathelle.no", "stavern.no", "stjordalshalsen.no", "xn--stjrdalshalsen-sqb.no", "tananger.no", "tranby.no", "vossevangen.no", "afjord.no", "xn--fjord-lra.no", "agdenes.no", "al.no", "xn--l-1fa.no", "alesund.no", "xn--lesund-hua.no", "alstahaug.no", "alta.no", "xn--lt-liac.no", "alaheadju.no", "xn--laheadju-7ya.no", "alvdal.no", "amli.no", "xn--mli-tla.no", "amot.no", "xn--mot-tla.no", "andebu.no", "andoy.no", "xn--andy-ira.no", "andasuolo.no", "ardal.no", "xn--rdal-poa.no", "aremark.no", "arendal.no", "xn--s-1fa.no", "aseral.no", "xn--seral-lra.no", "asker.no", "askim.no", "askvoll.no", "askoy.no", "xn--asky-ira.no", "asnes.no", "xn--snes-poa.no", "audnedaln.no", "aukra.no", "aure.no", "aurland.no", "aurskog-holand.no", "xn--aurskog-hland-jnb.no", "austevoll.no", "austrheim.no", "averoy.no", "xn--avery-yua.no", "balestrand.no", "ballangen.no", "balat.no", "xn--blt-elab.no", "balsfjord.no", "bahccavuotna.no", "xn--bhccavuotna-k7a.no", "bamble.no", "bardu.no", "beardu.no", "beiarn.no", "bajddar.no", "xn--bjddar-pta.no", "baidar.no", "xn--bidr-5nac.no", "berg.no", "bergen.no", "berlevag.no", "xn--berlevg-jxa.no", "bearalvahki.no", "xn--bearalvhki-y4a.no", "bindal.no", "birkenes.no", "bjarkoy.no", "xn--bjarky-fya.no", "bjerkreim.no", "bjugn.no", "bodo.no", "xn--bod-2na.no", "badaddja.no", "xn--bdddj-mrabd.no", "budejju.no", "bokn.no", "bremanger.no", "bronnoy.no", "xn--brnny-wuac.no", "bygland.no", "bykle.no", "barum.no", "xn--brum-voa.no", "bo.telemark.no", "xn--b-5ga.telemark.no", "bo.nordland.no", "xn--b-5ga.nordland.no", "bievat.no", "xn--bievt-0qa.no", "bomlo.no", "xn--bmlo-gra.no", "batsfjord.no", "xn--btsfjord-9za.no", "bahcavuotna.no", "xn--bhcavuotna-s4a.no", "dovre.no", "drammen.no", "drangedal.no", "dyroy.no", "xn--dyry-ira.no", "donna.no", "xn--dnna-gra.no", "eid.no", "eidfjord.no", "eidsberg.no", "eidskog.no", "eidsvoll.no", "eigersund.no", "elverum.no", "enebakk.no", "engerdal.no", "etne.no", "etnedal.no", "evenes.no", "evenassi.no", "xn--eveni-0qa01ga.no", "evje-og-hornnes.no", "farsund.no", "fauske.no", "fuossko.no", "fuoisku.no", "fedje.no", "fet.no", "finnoy.no", "xn--finny-yua.no", "fitjar.no", "fjaler.no", "fjell.no", "flakstad.no", "flatanger.no", "flekkefjord.no", "flesberg.no", "flora.no", "fla.no", "xn--fl-zia.no", "folldal.no", "forsand.no", "fosnes.no", "frei.no", "frogn.no", "froland.no", "frosta.no", "frana.no", "xn--frna-woa.no", "froya.no", "xn--frya-hra.no", "fusa.no", "fyresdal.no", "forde.no", "xn--frde-gra.no", "gamvik.no", "gangaviika.no", "xn--ggaviika-8ya47h.no", "gaular.no", "gausdal.no", "gildeskal.no", "xn--gildeskl-g0a.no", "giske.no", "gjemnes.no", "gjerdrum.no", "gjerstad.no", "gjesdal.no", "gjovik.no", "xn--gjvik-wua.no", "gloppen.no", "gol.no", "gran.no", "grane.no", "granvin.no", "gratangen.no", "grimstad.no", "grong.no", "kraanghke.no", "xn--kranghke-b0a.no", "grue.no", "gulen.no", "hadsel.no", "halden.no", "halsa.no", "hamar.no", "hamaroy.no", "habmer.no", "xn--hbmer-xqa.no", "hapmir.no", "xn--hpmir-xqa.no", "hammerfest.no", "hammarfeasta.no", "xn--hmmrfeasta-s4ac.no", "haram.no", "hareid.no", "harstad.no", "hasvik.no", "aknoluokta.no", "xn--koluokta-7ya57h.no", "hattfjelldal.no", "aarborte.no", "haugesund.no", "hemne.no", "hemnes.no", "hemsedal.no", "heroy.more-og-romsdal.no", "xn--hery-ira.xn--mre-og-romsdal-qqb.no", "heroy.nordland.no", "xn--hery-ira.nordland.no", "hitra.no", "hjartdal.no", "hjelmeland.no", "hobol.no", "xn--hobl-ira.no", "hof.no", "hol.no", "hole.no", "holmestrand.no", "holtalen.no", "xn--holtlen-hxa.no", "hornindal.no", "horten.no", "hurdal.no", "hurum.no", "hvaler.no", "hyllestad.no", "hagebostad.no", "xn--hgebostad-g3a.no", "hoyanger.no", "xn--hyanger-q1a.no", "hoylandet.no", "xn--hylandet-54a.no", "ha.no", "xn--h-2fa.no", "ibestad.no", "inderoy.no", "xn--indery-fya.no", "iveland.no", "jevnaker.no", "jondal.no", "jolster.no", "xn--jlster-bya.no", "karasjok.no", "karasjohka.no", "xn--krjohka-hwab49j.no", "karlsoy.no", "galsa.no", "xn--gls-elac.no", "karmoy.no", "xn--karmy-yua.no", "kautokeino.no", "guovdageaidnu.no", "klepp.no", "klabu.no", "xn--klbu-woa.no", "kongsberg.no", "kongsvinger.no", "kragero.no", "xn--krager-gya.no", "kristiansand.no", "kristiansund.no", "krodsherad.no", "xn--krdsherad-m8a.no", "kvalsund.no", "rahkkeravju.no", "xn--rhkkervju-01af.no", "kvam.no", "kvinesdal.no", "kvinnherad.no", "kviteseid.no", "kvitsoy.no", "xn--kvitsy-fya.no", "kvafjord.no", "xn--kvfjord-nxa.no", "giehtavuoatna.no", "kvanangen.no", "xn--kvnangen-k0a.no", "navuotna.no", "xn--nvuotna-hwa.no", "kafjord.no", "xn--kfjord-iua.no", "gaivuotna.no", "xn--givuotna-8ya.no", "larvik.no", "lavangen.no", "lavagis.no", "loabat.no", "xn--loabt-0qa.no", "lebesby.no", "davvesiida.no", "leikanger.no", "leirfjord.no", "leka.no", "leksvik.no", "lenvik.no", "leangaviika.no", "xn--leagaviika-52b.no", "lesja.no", "levanger.no", "lier.no", "lierne.no", "lillehammer.no", "lillesand.no", "lindesnes.no", "lindas.no", "xn--linds-pra.no", "lom.no", "loppa.no", "lahppi.no", "xn--lhppi-xqa.no", "lund.no", "lunner.no", "luroy.no", "xn--lury-ira.no", "luster.no", "lyngdal.no", "lyngen.no", "ivgu.no", "lardal.no", "lerdal.no", "xn--lrdal-sra.no", "lodingen.no", "xn--ldingen-q1a.no", "lorenskog.no", "xn--lrenskog-54a.no", "loten.no", "xn--lten-gra.no", "malvik.no", "masoy.no", "xn--msy-ula0h.no", "muosat.no", "xn--muost-0qa.no", "mandal.no", "marker.no", "marnardal.no", "masfjorden.no", "meland.no", "meldal.no", "melhus.no", "meloy.no", "xn--mely-ira.no", "meraker.no", "xn--merker-kua.no", "moareke.no", "xn--moreke-jua.no", "midsund.no", "midtre-gauldal.no", "modalen.no", "modum.no", "molde.no", "moskenes.no", "moss.no", "mosvik.no", "malselv.no", "xn--mlselv-iua.no", "malatvuopmi.no", "xn--mlatvuopmi-s4a.no", "namdalseid.no", "aejrie.no", "namsos.no", "namsskogan.no", "naamesjevuemie.no", "xn--nmesjevuemie-tcba.no", "laakesvuemie.no", "nannestad.no", "narvik.no", "narviika.no", "naustdal.no", "nedre-eiker.no", "nes.akershus.no", "nes.buskerud.no", "nesna.no", "nesodden.no", "nesseby.no", "unjarga.no", "xn--unjrga-rta.no", "nesset.no", "nissedal.no", "nittedal.no", "nord-aurdal.no", "nord-fron.no", "nord-odal.no", "norddal.no", "nordkapp.no", "davvenjarga.no", "xn--davvenjrga-y4a.no", "nordre-land.no", "nordreisa.no", "raisa.no", "xn--risa-5na.no", "nore-og-uvdal.no", "notodden.no", "naroy.no", "xn--nry-yla5g.no", "notteroy.no", "xn--nttery-byae.no", "odda.no", "oksnes.no", "xn--ksnes-uua.no", "oppdal.no", "oppegard.no", "xn--oppegrd-ixa.no", "orkdal.no", "orland.no", "xn--rland-uua.no", "orskog.no", "xn--rskog-uua.no", "orsta.no", "xn--rsta-fra.no", "os.hedmark.no", "os.hordaland.no", "osen.no", "osteroy.no", "xn--ostery-fya.no", "ostre-toten.no", "xn--stre-toten-zcb.no", "overhalla.no", "ovre-eiker.no", "xn--vre-eiker-k8a.no", "oyer.no", "xn--yer-zna.no", "oygarden.no", "xn--ygarden-p1a.no", "oystre-slidre.no", "xn--ystre-slidre-ujb.no", "porsanger.no", "porsangu.no", "xn--porsgu-sta26f.no", "porsgrunn.no", "radoy.no", "xn--rady-ira.no", "rakkestad.no", "rana.no", "ruovat.no", "randaberg.no", "rauma.no", "rendalen.no", "rennebu.no", "rennesoy.no", "xn--rennesy-v1a.no", "rindal.no", "ringebu.no", "ringerike.no", "ringsaker.no", "rissa.no", "risor.no", "xn--risr-ira.no", "roan.no", "rollag.no", "rygge.no", "ralingen.no", "xn--rlingen-mxa.no", "rodoy.no", "xn--rdy-0nab.no", "romskog.no", "xn--rmskog-bya.no", "roros.no", "xn--rros-gra.no", "rost.no", "xn--rst-0na.no", "royken.no", "xn--ryken-vua.no", "royrvik.no", "xn--ryrvik-bya.no", "rade.no", "xn--rde-ula.no", "salangen.no", "siellak.no", "saltdal.no", "salat.no", "xn--slt-elab.no", "xn--slat-5na.no", "samnanger.no", "sande.more-og-romsdal.no", "sande.xn--mre-og-romsdal-qqb.no", "sande.vestfold.no", "sandefjord.no", "sandnes.no", "sandoy.no", "xn--sandy-yua.no", "sarpsborg.no", "sauda.no", "sauherad.no", "sel.no", "selbu.no", "selje.no", "seljord.no", "sigdal.no", "siljan.no", "sirdal.no", "skaun.no", "skedsmo.no", "ski.no", "skien.no", "skiptvet.no", "skjervoy.no", "xn--skjervy-v1a.no", "skierva.no", "xn--skierv-uta.no", "skjak.no", "xn--skjk-soa.no", "skodje.no", "skanland.no", "xn--sknland-fxa.no", "skanit.no", "xn--sknit-yqa.no", "smola.no", "xn--smla-hra.no", "snillfjord.no", "snasa.no", "xn--snsa-roa.no", "snoasa.no", "snaase.no", "xn--snase-nra.no", "sogndal.no", "sokndal.no", "sola.no", "solund.no", "songdalen.no", "sortland.no", "spydeberg.no", "stange.no", "stavanger.no", "steigen.no", "steinkjer.no", "stjordal.no", "xn--stjrdal-s1a.no", "stokke.no", "stor-elvdal.no", "stord.no", "stordal.no", "storfjord.no", "omasvuotna.no", "strand.no", "stranda.no", "stryn.no", "sula.no", "suldal.no", "sund.no", "sunndal.no", "surnadal.no", "sveio.no", "svelvik.no", "sykkylven.no", "sogne.no", "xn--sgne-gra.no", "somna.no", "xn--smna-gra.no", "sondre-land.no", "xn--sndre-land-0cb.no", "sor-aurdal.no", "xn--sr-aurdal-l8a.no", "sor-fron.no", "xn--sr-fron-q1a.no", "sor-odal.no", "xn--sr-odal-q1a.no", "sor-varanger.no", "xn--sr-varanger-ggb.no", "matta-varjjat.no", "xn--mtta-vrjjat-k7af.no", "sorfold.no", "xn--srfold-bya.no", "sorreisa.no", "xn--srreisa-q1a.no", "sorum.no", "xn--srum-gra.no", "tana.no", "deatnu.no", "time.no", "tingvoll.no", "tinn.no", "tjeldsund.no", "dielddanuorri.no", "tjome.no", "xn--tjme-hra.no", "tokke.no", "tolga.no", "torsken.no", "tranoy.no", "xn--trany-yua.no", "tromso.no", "xn--troms-zua.no", "tromsa.no", "romsa.no", "trondheim.no", "troandin.no", "trysil.no", "trana.no", "xn--trna-woa.no", "trogstad.no", "xn--trgstad-r1a.no", "tvedestrand.no", "tydal.no", "tynset.no", "tysfjord.no", "divtasvuodna.no", "divttasvuotna.no", "tysnes.no", "tysvar.no", "xn--tysvr-vra.no", "tonsberg.no", "xn--tnsberg-q1a.no", "ullensaker.no", "ullensvang.no", "ulvik.no", "utsira.no", "vadso.no", "xn--vads-jra.no", "cahcesuolo.no", "xn--hcesuolo-7ya35b.no", "vaksdal.no", "valle.no", "vang.no", "vanylven.no", "vardo.no", "xn--vard-jra.no", "varggat.no", "xn--vrggt-xqad.no", "vefsn.no", "vaapste.no", "vega.no", "vegarshei.no", "xn--vegrshei-c0a.no", "vennesla.no", "verdal.no", "verran.no", "vestby.no", "vestnes.no", "vestre-slidre.no", "vestre-toten.no", "vestvagoy.no", "xn--vestvgy-ixa6o.no", "vevelstad.no", "vik.no", "vikna.no", "vindafjord.no", "volda.no", "voss.no", "varoy.no", "xn--vry-yla5g.no", "vagan.no", "xn--vgan-qoa.no", "voagat.no", "vagsoy.no", "xn--vgsy-qoa0j.no", "vaga.no", "xn--vg-yiab.no", "valer.ostfold.no", "xn--vler-qoa.xn--stfold-9xa.no", "valer.hedmark.no", "xn--vler-qoa.hedmark.no", "*.np", "nr", "biz.nr", "info.nr", "gov.nr", "edu.nr", "org.nr", "net.nr", "com.nr", "nu", "*.nz", "om", "co.om", "com.om", "edu.om", "gov.om", "med.om", "museum.om", "net.om", "org.om", "pro.om", "org", "pa", "ac.pa", "gob.pa", "com.pa", "org.pa", "sld.pa", "edu.pa", "net.pa", "ing.pa", "abo.pa", "med.pa", "nom.pa", "pe", "edu.pe", "gob.pe", "nom.pe", "mil.pe", "org.pe", "com.pe", "net.pe", "pf", "com.pf", "org.pf", "edu.pf", "*.pg", "ph", "com.ph", "net.ph", "org.ph", "gov.ph", "edu.ph", "ngo.ph", "mil.ph", "i.ph", "pk", "com.pk", "net.pk", "edu.pk", "org.pk", "fam.pk", "biz.pk", "web.pk", "gov.pk", "gob.pk", "gok.pk", "gon.pk", "gop.pk", "gos.pk", "info.pk", "pl", "aid.pl", "agro.pl", "atm.pl", "auto.pl", "biz.pl", "com.pl", "edu.pl", "gmina.pl", "gsm.pl", "info.pl", "mail.pl", "miasta.pl", "media.pl", "mil.pl", "net.pl", "nieruchomosci.pl", "nom.pl", "org.pl", "pc.pl", "powiat.pl", "priv.pl", "realestate.pl", "rel.pl", "sex.pl", "shop.pl", "sklep.pl", "sos.pl", "szkola.pl", "targi.pl", "tm.pl", "tourism.pl", "travel.pl", "turystyka.pl", "6bone.pl", "art.pl", "mbone.pl", "gov.pl", "uw.gov.pl", "um.gov.pl", "ug.gov.pl", "upow.gov.pl", "starostwo.gov.pl", "so.gov.pl", "sr.gov.pl", "po.gov.pl", "pa.gov.pl", "ngo.pl", "irc.pl", "usenet.pl", "augustow.pl", "babia-gora.pl", "bedzin.pl", "beskidy.pl", "bialowieza.pl", "bialystok.pl", "bielawa.pl", "bieszczady.pl", "boleslawiec.pl", "bydgoszcz.pl", "bytom.pl", "cieszyn.pl", "czeladz.pl", "czest.pl", "dlugoleka.pl", "elblag.pl", "elk.pl", "glogow.pl", "gniezno.pl", "gorlice.pl", "grajewo.pl", "ilawa.pl", "jaworzno.pl", "jelenia-gora.pl", "jgora.pl", "kalisz.pl", "kazimierz-dolny.pl", "karpacz.pl", "kartuzy.pl", "kaszuby.pl", "katowice.pl", "kepno.pl", "ketrzyn.pl", "klodzko.pl", "kobierzyce.pl", "kolobrzeg.pl", "konin.pl", "konskowola.pl", "kutno.pl", "lapy.pl", "lebork.pl", "legnica.pl", "lezajsk.pl", "limanowa.pl", "lomza.pl", "lowicz.pl", "lubin.pl", "lukow.pl", "malbork.pl", "malopolska.pl", "mazowsze.pl", "mazury.pl", "mielec.pl", "mielno.pl", "mragowo.pl", "naklo.pl", "nowaruda.pl", "nysa.pl", "olawa.pl", "olecko.pl", "olkusz.pl", "olsztyn.pl", "opoczno.pl", "opole.pl", "ostroda.pl", "ostroleka.pl", "ostrowiec.pl", "ostrowwlkp.pl", "pila.pl", "pisz.pl", "podhale.pl", "podlasie.pl", "polkowice.pl", "pomorze.pl", "pomorskie.pl", "prochowice.pl", "pruszkow.pl", "przeworsk.pl", "pulawy.pl", "radom.pl", "rawa-maz.pl", "rybnik.pl", "rzeszow.pl", "sanok.pl", "sejny.pl", "siedlce.pl", "slask.pl", "slupsk.pl", "sosnowiec.pl", "stalowa-wola.pl", "skoczow.pl", "starachowice.pl", "stargard.pl", "suwalki.pl", "swidnica.pl", "swiebodzin.pl", "swinoujscie.pl", "szczecin.pl", "szczytno.pl", "tarnobrzeg.pl", "tgory.pl", "turek.pl", "tychy.pl", "ustka.pl", "walbrzych.pl", "warmia.pl", "warszawa.pl", "waw.pl", "wegrow.pl", "wielun.pl", "wlocl.pl", "wloclawek.pl", "wodzislaw.pl", "wolomin.pl", "wroclaw.pl", "zachpomor.pl", "zagan.pl", "zarow.pl", "zgora.pl", "zgorzelec.pl", "gda.pl", "gdansk.pl", "gdynia.pl", "med.pl", "sopot.pl", "gliwice.pl", "krakow.pl", "poznan.pl", "wroc.pl", "zakopane.pl", "pm", "pn", "gov.pn", "co.pn", "org.pn", "edu.pn", "net.pn", "post", "pr", "com.pr", "net.pr", "org.pr", "gov.pr", "edu.pr", "isla.pr", "pro.pr", "biz.pr", "info.pr", "name.pr", "est.pr", "prof.pr", "ac.pr", "pro", "aca.pro", "bar.pro", "cpa.pro", "jur.pro", "law.pro", "med.pro", "eng.pro", "ps", "edu.ps", "gov.ps", "sec.ps", "plo.ps", "com.ps", "org.ps", "net.ps", "pt", "net.pt", "gov.pt", "org.pt", "edu.pt", "int.pt", "publ.pt", "com.pt", "nome.pt", "pw", "co.pw", "ne.pw", "or.pw", "ed.pw", "go.pw", "belau.pw", "py", "com.py", "coop.py", "edu.py", "gov.py", "mil.py", "net.py", "org.py", "qa", "com.qa", "edu.qa", "gov.qa", "mil.qa", "name.qa", "net.qa", "org.qa", "sch.qa", "re", "com.re", "asso.re", "nom.re", "ro", "com.ro", "org.ro", "tm.ro", "nt.ro", "nom.ro", "info.ro", "rec.ro", "arts.ro", "firm.ro", "store.ro", "www.ro", "rs", "co.rs", "org.rs", "edu.rs", "ac.rs", "gov.rs", "in.rs", "ru", "ac.ru", "com.ru", "edu.ru", "int.ru", "net.ru", "org.ru", "pp.ru", "adygeya.ru", "altai.ru", "amur.ru", "arkhangelsk.ru", "astrakhan.ru", "bashkiria.ru", "belgorod.ru", "bir.ru", "bryansk.ru", "buryatia.ru", "cbg.ru", "chel.ru", "chelyabinsk.ru", "chita.ru", "chukotka.ru", "chuvashia.ru", "dagestan.ru", "dudinka.ru", "e-burg.ru", "grozny.ru", "irkutsk.ru", "ivanovo.ru", "izhevsk.ru", "jar.ru", "joshkar-ola.ru", "kalmykia.ru", "kaluga.ru", "kamchatka.ru", "karelia.ru", "kazan.ru", "kchr.ru", "kemerovo.ru", "khabarovsk.ru", "khakassia.ru", "khv.ru", "kirov.ru", "koenig.ru", "komi.ru", "kostroma.ru", "krasnoyarsk.ru", "kuban.ru", "kurgan.ru", "kursk.ru", "lipetsk.ru", "magadan.ru", "mari.ru", "mari-el.ru", "marine.ru", "mordovia.ru", "mosreg.ru", "msk.ru", "murmansk.ru", "nalchik.ru", "nnov.ru", "nov.ru", "novosibirsk.ru", "nsk.ru", "omsk.ru", "orenburg.ru", "oryol.ru", "palana.ru", "penza.ru", "perm.ru", "pskov.ru", "ptz.ru", "rnd.ru", "ryazan.ru", "sakhalin.ru", "samara.ru", "saratov.ru", "simbirsk.ru", "smolensk.ru", "spb.ru", "stavropol.ru", "stv.ru", "surgut.ru", "tambov.ru", "tatarstan.ru", "tom.ru", "tomsk.ru", "tsaritsyn.ru", "tsk.ru", "tula.ru", "tuva.ru", "tver.ru", "tyumen.ru", "udm.ru", "udmurtia.ru", "ulan-ude.ru", "vladikavkaz.ru", "vladimir.ru", "vladivostok.ru", "volgograd.ru", "vologda.ru", "voronezh.ru", "vrn.ru", "vyatka.ru", "yakutia.ru", "yamal.ru", "yaroslavl.ru", "yekaterinburg.ru", "yuzhno-sakhalinsk.ru", "amursk.ru", "baikal.ru", "cmw.ru", "fareast.ru", "jamal.ru", "kms.ru", "k-uralsk.ru", "kustanai.ru", "kuzbass.ru", "magnitka.ru", "mytis.ru", "nakhodka.ru", "nkz.ru", "norilsk.ru", "oskol.ru", "pyatigorsk.ru", "rubtsovsk.ru", "snz.ru", "syzran.ru", "vdonsk.ru", "zgrad.ru", "gov.ru", "mil.ru", "test.ru", "rw", "gov.rw", "net.rw", "edu.rw", "ac.rw", "com.rw", "co.rw", "int.rw", "mil.rw", "gouv.rw", "sa", "com.sa", "net.sa", "org.sa", "gov.sa", "med.sa", "pub.sa", "edu.sa", "sch.sa", "sb", "com.sb", "edu.sb", "gov.sb", "net.sb", "org.sb", "sc", "com.sc", "gov.sc", "net.sc", "org.sc", "edu.sc", "sd", "com.sd", "net.sd", "org.sd", "edu.sd", "med.sd", "tv.sd", "gov.sd", "info.sd", "se", "a.se", "ac.se", "b.se", "bd.se", "brand.se", "c.se", "d.se", "e.se", "f.se", "fh.se", "fhsk.se", "fhv.se", "g.se", "h.se", "i.se", "k.se", "komforb.se", "kommunalforbund.se", "komvux.se", "l.se", "lanbib.se", "m.se", "n.se", "naturbruksgymn.se", "o.se", "org.se", "p.se", "parti.se", "pp.se", "press.se", "r.se", "s.se", "sshn.se", "t.se", "tm.se", "u.se", "w.se", "x.se", "y.se", "z.se", "sg", "com.sg", "net.sg", "org.sg", "gov.sg", "edu.sg", "per.sg", "sh", "com.sh", "net.sh", "gov.sh", "org.sh", "mil.sh", "si", "sj", "sk", "sl", "com.sl", "net.sl", "edu.sl", "gov.sl", "org.sl", "sm", "sn", "art.sn", "com.sn", "edu.sn", "gouv.sn", "org.sn", "perso.sn", "univ.sn", "so", "com.so", "net.so", "org.so", "sr", "st", "co.st", "com.st", "consulado.st", "edu.st", "embaixada.st", "gov.st", "mil.st", "net.st", "org.st", "principe.st", "saotome.st", "store.st", "su", "sv", "com.sv", "edu.sv", "gob.sv", "org.sv", "red.sv", "sx", "gov.sx", "sy", "edu.sy", "gov.sy", "net.sy", "mil.sy", "com.sy", "org.sy", "sz", "co.sz", "ac.sz", "org.sz", "tc", "td", "tel", "tf", "tg", "th", "ac.th", "co.th", "go.th", "in.th", "mi.th", "net.th", "or.th", "tj", "ac.tj", "biz.tj", "co.tj", "com.tj", "edu.tj", "go.tj", "gov.tj", "int.tj", "mil.tj", "name.tj", "net.tj", "nic.tj", "org.tj", "test.tj", "web.tj", "tk", "tl", "gov.tl", "tm", "com.tm", "co.tm", "org.tm", "net.tm", "nom.tm", "gov.tm", "mil.tm", "edu.tm", "tn", "com.tn", "ens.tn", "fin.tn", "gov.tn", "ind.tn", "intl.tn", "nat.tn", "net.tn", "org.tn", "info.tn", "perso.tn", "tourism.tn", "edunet.tn", "rnrt.tn", "rns.tn", "rnu.tn", "mincom.tn", "agrinet.tn", "defense.tn", "turen.tn", "to", "com.to", "gov.to", "net.to", "org.to", "edu.to", "mil.to", "tp", "*.tr", "!nic.tr", "gov.nc.tr", "travel", "tt", "co.tt", "com.tt", "org.tt", "net.tt", "biz.tt", "info.tt", "pro.tt", "int.tt", "coop.tt", "jobs.tt", "mobi.tt", "travel.tt", "museum.tt", "aero.tt", "name.tt", "gov.tt", "edu.tt", "tv", "tw", "edu.tw", "gov.tw", "mil.tw", "com.tw", "net.tw", "org.tw", "idv.tw", "game.tw", "ebiz.tw", "club.tw", "xn--zf0ao64a.tw", "xn--uc0atv.tw", "xn--czrw28b.tw", "tz", "ac.tz", "co.tz", "go.tz", "hotel.tz", "info.tz", "me.tz", "mil.tz", "mobi.tz", "ne.tz", "or.tz", "sc.tz", "tv.tz", "ua", "com.ua", "edu.ua", "gov.ua", "in.ua", "net.ua", "org.ua", "cherkassy.ua", "cherkasy.ua", "chernigov.ua", "chernihiv.ua", "chernivtsi.ua", "chernovtsy.ua", "ck.ua", "cn.ua", "cr.ua", "crimea.ua", "cv.ua", "dn.ua", "dnepropetrovsk.ua", "dnipropetrovsk.ua", "dominic.ua", "donetsk.ua", "dp.ua", "if.ua", "ivano-frankivsk.ua", "kh.ua", "kharkiv.ua", "kharkov.ua", "kherson.ua", "khmelnitskiy.ua", "khmelnytskyi.ua", "kiev.ua", "kirovograd.ua", "km.ua", "kr.ua", "krym.ua", "ks.ua", "kv.ua", "kyiv.ua", "lg.ua", "lt.ua", "lugansk.ua", "lutsk.ua", "lv.ua", "lviv.ua", "mk.ua", "mykolaiv.ua", "nikolaev.ua", "od.ua", "odesa.ua", "odessa.ua", "pl.ua", "poltava.ua", "rivne.ua", "rovno.ua", "rv.ua", "sb.ua", "sebastopol.ua", "sevastopol.ua", "sm.ua", "sumy.ua", "te.ua", "ternopil.ua", "uz.ua", "uzhgorod.ua", "vinnica.ua", "vinnytsia.ua", "vn.ua", "volyn.ua", "yalta.ua", "zaporizhzhe.ua", "zaporizhzhia.ua", "zhitomir.ua", "zhytomyr.ua", "zp.ua", "zt.ua", "co.ua", "pp.ua", "ug", "co.ug", "or.ug", "ac.ug", "sc.ug", "go.ug", "ne.ug", "com.ug", "org.ug", "*.uk", "*.sch.uk", "!bl.uk", "!british-library.uk", "!jet.uk", "!mod.uk", "!national-library-scotland.uk", "!nel.uk", "!nic.uk", "!nls.uk", "!parliament.uk", "us", "dni.us", "fed.us", "isa.us", "kids.us", "nsn.us", "ak.us", "al.us", "ar.us", "as.us", "az.us", "ca.us", "co.us", "ct.us", "dc.us", "de.us", "fl.us", "ga.us", "gu.us", "hi.us", "ia.us", "id.us", "il.us", "in.us", "ks.us", "ky.us", "la.us", "ma.us", "md.us", "me.us", "mi.us", "mn.us", "mo.us", "ms.us", "mt.us", "nc.us", "nd.us", "ne.us", "nh.us", "nj.us", "nm.us", "nv.us", "ny.us", "oh.us", "ok.us", "or.us", "pa.us", "pr.us", "ri.us", "sc.us", "sd.us", "tn.us", "tx.us", "ut.us", "vi.us", "vt.us", "va.us", "wa.us", "wi.us", "wv.us", "wy.us", "k12.ak.us", "k12.al.us", "k12.ar.us", "k12.as.us", "k12.az.us", "k12.ca.us", "k12.co.us", "k12.ct.us", "k12.dc.us", "k12.de.us", "k12.fl.us", "k12.ga.us", "k12.gu.us", "k12.ia.us", "k12.id.us", "k12.il.us", "k12.in.us", "k12.ks.us", "k12.ky.us", "k12.la.us", "k12.ma.us", "k12.md.us", "k12.me.us", "k12.mi.us", "k12.mn.us", "k12.mo.us", "k12.ms.us", "k12.mt.us", "k12.nc.us", "k12.nd.us", "k12.ne.us", "k12.nh.us", "k12.nj.us", "k12.nm.us", "k12.nv.us", "k12.ny.us", "k12.oh.us", "k12.ok.us", "k12.or.us", "k12.pa.us", "k12.pr.us", "k12.ri.us", "k12.sc.us", "k12.tn.us", "k12.tx.us", "k12.ut.us", "k12.vi.us", "k12.vt.us", "k12.va.us", "k12.wa.us", "k12.wi.us", "k12.wy.us", "cc.ak.us", "cc.al.us", "cc.ar.us", "cc.as.us", "cc.az.us", "cc.ca.us", "cc.co.us", "cc.ct.us", "cc.dc.us", "cc.de.us", "cc.fl.us", "cc.ga.us", "cc.gu.us", "cc.hi.us", "cc.ia.us", "cc.id.us", "cc.il.us", "cc.in.us", "cc.ks.us", "cc.ky.us", "cc.la.us", "cc.ma.us", "cc.md.us", "cc.me.us", "cc.mi.us", "cc.mn.us", "cc.mo.us", "cc.ms.us", "cc.mt.us", "cc.nc.us", "cc.nd.us", "cc.ne.us", "cc.nh.us", "cc.nj.us", "cc.nm.us", "cc.nv.us", "cc.ny.us", "cc.oh.us", "cc.ok.us", "cc.or.us", "cc.pa.us", "cc.pr.us", "cc.ri.us", "cc.sc.us", "cc.sd.us", "cc.tn.us", "cc.tx.us", "cc.ut.us", "cc.vi.us", "cc.vt.us", "cc.va.us", "cc.wa.us", "cc.wi.us", "cc.wv.us", "cc.wy.us", "lib.ak.us", "lib.al.us", "lib.ar.us", "lib.as.us", "lib.az.us", "lib.ca.us", "lib.co.us", "lib.ct.us", "lib.dc.us", "lib.de.us", "lib.fl.us", "lib.ga.us", "lib.gu.us", "lib.hi.us", "lib.ia.us", "lib.id.us", "lib.il.us", "lib.in.us", "lib.ks.us", "lib.ky.us", "lib.la.us", "lib.ma.us", "lib.md.us", "lib.me.us", "lib.mi.us", "lib.mn.us", "lib.mo.us", "lib.ms.us", "lib.mt.us", "lib.nc.us", "lib.nd.us", "lib.ne.us", "lib.nh.us", "lib.nj.us", "lib.nm.us", "lib.nv.us", "lib.ny.us", "lib.oh.us", "lib.ok.us", "lib.or.us", "lib.pa.us", "lib.pr.us", "lib.ri.us", "lib.sc.us", "lib.sd.us", "lib.tn.us", "lib.tx.us", "lib.ut.us", "lib.vi.us", "lib.vt.us", "lib.va.us", "lib.wa.us", "lib.wi.us", "lib.wy.us", "pvt.k12.ma.us", "chtr.k12.ma.us", "paroch.k12.ma.us", "uy", "com.uy", "edu.uy", "gub.uy", "mil.uy", "net.uy", "org.uy", "uz", "co.uz", "com.uz", "net.uz", "org.uz", "va", "vc", "com.vc", "net.vc", "org.vc", "gov.vc", "mil.vc", "edu.vc", "ve", "co.ve", "com.ve", "e12.ve", "edu.ve", "gov.ve", "info.ve", "mil.ve", "net.ve", "org.ve", "web.ve", "vg", "vi", "co.vi", "com.vi", "k12.vi", "net.vi", "org.vi", "vn", "com.vn", "net.vn", "org.vn", "edu.vn", "gov.vn", "int.vn", "ac.vn", "biz.vn", "info.vn", "name.vn", "pro.vn", "health.vn", "vu", "wf", "ws", "com.ws", "net.ws", "org.ws", "gov.ws", "edu.ws", "yt", "xn--mgbaam7a8h", "xn--54b7fta0cc", "xn--fiqs8s", "xn--fiqz9s", "xn--lgbbat1ad8j", "xn--wgbh1c", "xn--node", "xn--j6w193g", "xn--h2brj9c", "xn--mgbbh1a71e", "xn--fpcrj9c3d", "xn--gecrj9c", "xn--s9brj9c", "xn--45brj9c", "xn--xkc2dl3a5ee0h", "xn--mgba3a4f16a", "xn--mgba3a4fra", "xn--mgbayh7gpa", "xn--3e0b707e", "xn--80ao21a", "xn--fzc2c9e2c", "xn--xkc2al3hye2a", "xn--mgbc0a9azcg", "xn--l1acc", "xn--mgbx4cd0ab", "xn--mgb9awbf", "xn--ygbi2ammx", "xn--90a3ac", "xn--p1ai", "xn--wgbl6a", "xn--mgberp4a5d4ar", "xn--mgberp4a5d4a87g", "xn--mgbqly7c0a67fbc", "xn--mgbqly7cvafr", "xn--ogbpf8fl", "xn--mgbtf8fl", "xn--yfro4i67o", "xn--clchc0ea0b2g2a9gcd", "xn--o3cw4h", "xn--pgbs0dh", "xn--kpry57d", "xn--kprw13d", "xn--nnx388a", "xn--j1amh", "xn--mgb2ddes", "xxx", "*.ye", "*.za", "*.zm", "*.zw", "xn--80asehdb", "xn--80aswg", "xn--ngbc5azd", "xn--unup4y", "xn--vhquv", "camera", "clothing", "lighting", "singles", "ventures", "voyage", "guru", "holdings", "equipment", "bike", "estate", "tattoo", "xn--3ds443g", "xn--fiq228c5hs", "land", "plumbing", "contractors", "sexy", "menu", "xn--rhqv96g", "uno", "gallery", "technology", "xn--3bst00m", "reviews", "guide", "xn--6qq986b3xl", "graphics", "construction", "onl", "xn--q9jyb4c", "diamonds", "kiwi", "enterprises", "today", "futbol", "photography", "tips", "directory", "kitchen", "xn--6frz82g", "kim", "xn--cg4bki", "monash", "wed", "pink", "ruhr", "buzz", "careers", "shoes", "xn--4gbrim", "career", "otsuka", "xn--fiq64b", "gift", "recipes", "coffee", "luxury", "domains", "photos", "limo", "viajes", "wang", "democrat", "mango", "cab", "support", "dance", "nagoya", "computer", "wien", "berlin", "codes", "email", "xn--mgbab2bd", "repair", "holiday", "center", "systems", "wiki", "ceo", "international", "solar", "company", "education", "training", "academy", "marketing", "florist", "solutions", "build", "institute", "builders", "red", "blue", "ninja", "business", "gal", "social", "house", "camp", "immobilien", "moda", "glass", "management", "kaufen", "farm", "xn--55qw42g", "xn--zfr164b", "club", "voting", "tokyo", "moe", "guitars", "bargains", "xn--nqv7fs00ema", "desi", "cool", "boutique", "pics", "xn--c1avg", "xn--55qx5d", "xn--io0a7i", "cheap", "xn--xhq521b", "photo", "network", "zone", "xn--nqv7f", "link", "qpon", "xn--i1b6b1a6a2e", "agency", "tienda", "works", "london", "watch", "rocks", "shiksha", "xn--d1acj3b", "budapest", "nrw", "vote", "fishing", "expert", "horse", "christmas", "cooking", "xn--czru2d", "casa", "rich", "voto", "tools", "xn--45q11c", "praxi", "events", "flights", "report", "partners", "neustar", "rentals", "catering", "community", "maison", "parts", "cleaning", "okinawa", "foundation", "properties", "vacations", "productions", "industries", "haus", "vision", "mormon", "cards", "ink", "villas", "consulting", "cruises", "krd", "xyz", "dating", "exposed", "condos", "eus", "caravan", "actor", "saarland", "yokohama", "pub", "xn--p1acf", "ren", "fish", "bar", "dnp", "bid", "supply", "miami", "supplies", "quebec", "moscow", "globo", "axa", "xn--80adxhks", "xn--czrs0t", "vodka", "rest", "frogans", "wtc", "rodeo", "sohu", "best", "country", "kred", "feedback", "work", "luxe", "ryukyu", "autos", "homes", "jetzt", "yachts", "motorcycles", "mini", "ggee", "beer", "xn--1qqw23a", "college", "ovh", "meet", "xn--ses554g", "gop", "blackfriday", "lacaixa", "xn--czr694b", "vegas", "black", "cloudfront.net", "compute.amazonaws.com", "us-east-1.amazonaws.com", "compute-1.amazonaws.com", "z-1.compute-1.amazonaws.com", "z-2.compute-1.amazonaws.com", "ap-northeast-1.compute.amazonaws.com", "ap-southeast-1.compute.amazonaws.com", "ap-southeast-2.compute.amazonaws.com", "eu-west-1.compute.amazonaws.com", "sa-east-1.compute.amazonaws.com", "us-gov-west-1.compute.amazonaws.com", "us-west-1.compute.amazonaws.com", "us-west-2.compute.amazonaws.com", "elasticbeanstalk.com", "elb.amazonaws.com", "s3.amazonaws.com", "s3-us-west-2.amazonaws.com", "s3-us-west-1.amazonaws.com", "s3-eu-west-1.amazonaws.com", "s3-ap-southeast-1.amazonaws.com", "s3-ap-southeast-2.amazonaws.com", "s3-ap-northeast-1.amazonaws.com", "s3-sa-east-1.amazonaws.com", "s3-us-gov-west-1.amazonaws.com", "s3-fips-us-gov-west-1.amazonaws.com", "s3-website-us-east-1.amazonaws.com", "s3-website-us-west-2.amazonaws.com", "s3-website-us-west-1.amazonaws.com", "s3-website-eu-west-1.amazonaws.com", "s3-website-ap-southeast-1.amazonaws.com", "s3-website-ap-southeast-2.amazonaws.com", "s3-website-ap-northeast-1.amazonaws.com", "s3-website-sa-east-1.amazonaws.com", "s3-website-us-gov-west-1.amazonaws.com", "betainabox.com", "ae.org", "ar.com", "br.com", "cn.com", "com.de", "de.com", "eu.com", "gb.com", "gb.net", "gr.com", "hu.com", "hu.net", "jp.net", "jpn.com", "kr.com", "no.com", "qc.com", "ru.com", "sa.com", "se.com", "se.net", "uk.com", "uk.net", "us.com", "us.org", "uy.com", "za.com", "c.la", "cloudcontrolled.com", "cloudcontrolapp.com", "co.ca", "co.nl", "co.no", "cupcake.is", "dreamhosters.com", "dyndns-at-home.com", "dyndns-at-work.com", "dyndns-blog.com", "dyndns-free.com", "dyndns-home.com", "dyndns-ip.com", "dyndns-mail.com", "dyndns-office.com", "dyndns-pics.com", "dyndns-remote.com", "dyndns-server.com", "dyndns-web.com", "dyndns-wiki.com", "dyndns-work.com", "dyndns.biz", "dyndns.info", "dyndns.org", "dyndns.tv", "at-band-camp.net", "ath.cx", "barrel-of-knowledge.info", "barrell-of-knowledge.info", "better-than.tv", "blogdns.com", "blogdns.net", "blogdns.org", "blogsite.org", "boldlygoingnowhere.org", "broke-it.net", "buyshouses.net", "cechire.com", "dnsalias.com", "dnsalias.net", "dnsalias.org", "dnsdojo.com", "dnsdojo.net", "dnsdojo.org", "does-it.net", "doesntexist.com", "doesntexist.org", "dontexist.com", "dontexist.net", "dontexist.org", "doomdns.com", "doomdns.org", "dvrdns.org", "dyn-o-saur.com", "dynalias.com", "dynalias.net", "dynalias.org", "dynathome.net", "dyndns.ws", "endofinternet.net", "endofinternet.org", "endoftheinternet.org", "est-a-la-maison.com", "est-a-la-masion.com", "est-le-patron.com", "est-mon-blogueur.com", "for-better.biz", "for-more.biz", "for-our.info", "for-some.biz", "for-the.biz", "forgot.her.name", "forgot.his.name", "from-ak.com", "from-al.com", "from-ar.com", "from-az.net", "from-ca.com", "from-co.net", "from-ct.com", "from-dc.com", "from-de.com", "from-fl.com", "from-ga.com", "from-hi.com", "from-ia.com", "from-id.com", "from-il.com", "from-in.com", "from-ks.com", "from-ky.com", "from-la.net", "from-ma.com", "from-md.com", "from-me.org", "from-mi.com", "from-mn.com", "from-mo.com", "from-ms.com", "from-mt.com", "from-nc.com", "from-nd.com", "from-ne.com", "from-nh.com", "from-nj.com", "from-nm.com", "from-nv.com", "from-ny.net", "from-oh.com", "from-ok.com", "from-or.com", "from-pa.com", "from-pr.com", "from-ri.com", "from-sc.com", "from-sd.com", "from-tn.com", "from-tx.com", "from-ut.com", "from-va.com", "from-vt.com", "from-wa.com", "from-wi.com", "from-wv.com", "from-wy.com", "ftpaccess.cc", "fuettertdasnetz.de", "game-host.org", "game-server.cc", "getmyip.com", "gets-it.net", "go.dyndns.org", "gotdns.com", "gotdns.org", "groks-the.info", "groks-this.info", "ham-radio-op.net", "here-for-more.info", "hobby-site.com", "hobby-site.org", "home.dyndns.org", "homedns.org", "homeftp.net", "homeftp.org", "homeip.net", "homelinux.com", "homelinux.net", "homelinux.org", "homeunix.com", "homeunix.net", "homeunix.org", "iamallama.com", "in-the-band.net", "is-a-anarchist.com", "is-a-blogger.com", "is-a-bookkeeper.com", "is-a-bruinsfan.org", "is-a-bulls-fan.com", "is-a-candidate.org", "is-a-caterer.com", "is-a-celticsfan.org", "is-a-chef.com", "is-a-chef.net", "is-a-chef.org", "is-a-conservative.com", "is-a-cpa.com", "is-a-cubicle-slave.com", "is-a-democrat.com", "is-a-designer.com", "is-a-doctor.com", "is-a-financialadvisor.com", "is-a-geek.com", "is-a-geek.net", "is-a-geek.org", "is-a-green.com", "is-a-guru.com", "is-a-hard-worker.com", "is-a-hunter.com", "is-a-knight.org", "is-a-landscaper.com", "is-a-lawyer.com", "is-a-liberal.com", "is-a-libertarian.com", "is-a-linux-user.org", "is-a-llama.com", "is-a-musician.com", "is-a-nascarfan.com", "is-a-nurse.com", "is-a-painter.com", "is-a-patsfan.org", "is-a-personaltrainer.com", "is-a-photographer.com", "is-a-player.com", "is-a-republican.com", "is-a-rockstar.com", "is-a-socialist.com", "is-a-soxfan.org", "is-a-student.com", "is-a-teacher.com", "is-a-techie.com", "is-a-therapist.com", "is-an-accountant.com", "is-an-actor.com", "is-an-actress.com", "is-an-anarchist.com", "is-an-artist.com", "is-an-engineer.com", "is-an-entertainer.com", "is-by.us", "is-certified.com", "is-found.org", "is-gone.com", "is-into-anime.com", "is-into-cars.com", "is-into-cartoons.com", "is-into-games.com", "is-leet.com", "is-lost.org", "is-not-certified.com", "is-saved.org", "is-slick.com", "is-uberleet.com", "is-very-bad.org", "is-very-evil.org", "is-very-good.org", "is-very-nice.org", "is-very-sweet.org", "is-with-theband.com", "isa-geek.com", "isa-geek.net", "isa-geek.org", "isa-hockeynut.com", "issmarterthanyou.com", "isteingeek.de", "istmein.de", "kicks-ass.net", "kicks-ass.org", "knowsitall.info", "land-4-sale.us", "lebtimnetz.de", "leitungsen.de", "likes-pie.com", "likescandy.com", "merseine.nu", "mine.nu", "misconfused.org", "mypets.ws", "myphotos.cc", "neat-url.com", "office-on-the.net", "on-the-web.tv", "podzone.net", "podzone.org", "readmyblog.org", "saves-the-whales.com", "scrapper-site.net", "scrapping.cc", "selfip.biz", "selfip.com", "selfip.info", "selfip.net", "selfip.org", "sells-for-less.com", "sells-for-u.com", "sells-it.net", "sellsyourhome.org", "servebbs.com", "servebbs.net", "servebbs.org", "serveftp.net", "serveftp.org", "servegame.org", "shacknet.nu", "simple-url.com", "space-to-rent.com", "stuff-4-sale.org", "stuff-4-sale.us", "teaches-yoga.com", "thruhere.net", "traeumtgerade.de", "webhop.biz", "webhop.info", "webhop.net", "webhop.org", "worse-than.tv", "writesthisblog.com", "a.ssl.fastly.net", "b.ssl.fastly.net", "global.ssl.fastly.net", "a.prod.fastly.net", "global.prod.fastly.net", "github.io", "ro.com", "appspot.com", "blogspot.be", "blogspot.bj", "blogspot.ca", "blogspot.cf", "blogspot.ch", "blogspot.co.at", "blogspot.co.il", "blogspot.co.nz", "blogspot.co.uk", "blogspot.com", "blogspot.com.ar", "blogspot.com.au", "blogspot.com.br", "blogspot.com.es", "blogspot.cv", "blogspot.cz", "blogspot.de", "blogspot.dk", "blogspot.fi", "blogspot.fr", "blogspot.gr", "blogspot.hk", "blogspot.hu", "blogspot.ie", "blogspot.in", "blogspot.it", "blogspot.jp", "blogspot.kr", "blogspot.mr", "blogspot.mx", "blogspot.nl", "blogspot.no", "blogspot.pt", "blogspot.re", "blogspot.ro", "blogspot.se", "blogspot.sg", "blogspot.sk", "blogspot.td", "blogspot.tw", "codespot.com", "googleapis.com", "googlecode.com", "withgoogle.com", "herokuapp.com", "herokussl.com", "iki.fi", "biz.at", "info.at", "co.pl", "azurewebsites.net", "azure-mobile.net", "cloudapp.net", "nyc.mn", "operaunite.com", "rhcloud.com", "priv.at", "za.net", "za.org", } var nodeLabels = [...]string{ "ac", "academy", "actor", "ad", "ae", "aero", "af", "ag", "agency", "ai", "al", "am", "an", "ao", "aq", "ar", "arpa", "as", "asia", "at", "au", "autos", "aw", "ax", "axa", "az", "ba", "bar", "bargains", "bb", "bd", "be", "beer", "berlin", "best", "bf", "bg", "bh", "bi", "bid", "bike", "biz", "bj", "black", "blackfriday", "blue", "bm", "bn", "bo", "boutique", "br", "bs", "bt", "budapest", "build", "builders", "business", "buzz", "bv", "bw", "by", "bz", "ca", "cab", "camera", "camp", "caravan", "cards", "career", "careers", "casa", "cat", "catering", "cc", "cd", "center", "ceo", "cf", "cg", "ch", "cheap", "christmas", "ci", "ck", "cl", "cleaning", "clothing", "club", "cm", "cn", "co", "codes", "coffee", "college", "com", "community", "company", "computer", "condos", "construction", "consulting", "contractors", "cooking", "cool", "coop", "country", "cr", "cruises", "cu", "cv", "cw", "cx", "cy", "cz", "dance", "dating", "de", "democrat", "desi", "diamonds", "directory", "dj", "dk", "dm", "dnp", "do", "domains", "dz", "ec", "edu", "education", "ee", "eg", "email", "enterprises", "equipment", "er", "es", "estate", "et", "eu", "eus", "events", "expert", "exposed", "farm", "feedback", "fi", "fish", "fishing", "fj", "fk", "flights", "florist", "fm", "fo", "foundation", "fr", "frogans", "futbol", "ga", "gal", "gallery", "gb", "gd", "ge", "gf", "gg", "ggee", "gh", "gi", "gift", "gl", "glass", "globo", "gm", "gn", "gop", "gov", "gp", "gq", "gr", "graphics", "gs", "gt", "gu", "guide", "guitars", "guru", "gw", "gy", "haus", "hk", "hm", "hn", "holdings", "holiday", "homes", "horse", "house", "hr", "ht", "hu", "id", "ie", "il", "im", "immobilien", "in", "industries", "info", "ink", "institute", "int", "international", "io", "iq", "ir", "is", "it", "je", "jetzt", "jm", "jo", "jobs", "jp", "kaufen", "ke", "kg", "kh", "ki", "kim", "kitchen", "kiwi", "km", "kn", "kp", "kr", "krd", "kred", "kw", "ky", "kz", "la", "lacaixa", "land", "lb", "lc", "li", "lighting", "limo", "link", "lk", "london", "lr", "ls", "lt", "lu", "luxe", "luxury", "lv", "ly", "ma", "maison", "management", "mango", "marketing", "mc", "md", "me", "meet", "menu", "mg", "mh", "miami", "mil", "mini", "mk", "ml", "mm", "mn", "mo", "mobi", "moda", "moe", "monash", "mormon", "moscow", "motorcycles", "mp", "mq", "mr", "ms", "mt", "mu", "museum", "mv", "mw", "mx", "my", "mz", "na", "nagoya", "name", "nc", "ne", "net", "network", "neustar", "nf", "ng", "ni", "ninja", "nl", "no", "np", "nr", "nrw", "nu", "nz", "okinawa", "om", "onl", "org", "otsuka", "ovh", "pa", "partners", "parts", "pe", "pf", "pg", "ph", "photo", "photography", "photos", "pics", "pink", "pk", "pl", "plumbing", "pm", "pn", "post", "pr", "praxi", "pro", "productions", "properties", "ps", "pt", "pub", "pw", "py", "qa", "qpon", "quebec", "re", "recipes", "red", "ren", "rentals", "repair", "report", "rest", "reviews", "rich", "ro", "rocks", "rodeo", "rs", "ru", "ruhr", "rw", "ryukyu", "sa", "saarland", "sb", "sc", "sd", "se", "sexy", "sg", "sh", "shiksha", "shoes", "si", "singles", "sj", "sk", "sl", "sm", "sn", "so", "social", "sohu", "solar", "solutions", "sr", "st", "su", "supplies", "supply", "support", "sv", "sx", "sy", "systems", "sz", "tattoo", "tc", "td", "technology", "tel", "tf", "tg", "th", "tienda", "tips", "tj", "tk", "tl", "tm", "tn", "to", "today", "tokyo", "tools", "tp", "tr", "training", "travel", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "uno", "us", "uy", "uz", "va", "vacations", "vc", "ve", "vegas", "ventures", "vg", "vi", "viajes", "villas", "vision", "vn", "vodka", "vote", "voting", "voto", "voyage", "vu", "wang", "watch", "wed", "wf", "wien", "wiki", "work", "works", "ws", "wtc", "xn--1qqw23a", "xn--3bst00m", "xn--3ds443g", "xn--3e0b707e", "xn--45brj9c", "xn--45q11c", "xn--4gbrim", "xn--54b7fta0cc", "xn--55qw42g", "xn--55qx5d", "xn--6frz82g", "xn--6qq986b3xl", "xn--80adxhks", "xn--80ao21a", "xn--80asehdb", "xn--80aswg", "xn--90a3ac", "xn--c1avg", "xn--cg4bki", "xn--clchc0ea0b2g2a9gcd", "xn--czr694b", "xn--czrs0t", "xn--czru2d", "xn--d1acj3b", "xn--fiq228c5hs", "xn--fiq64b", "xn--fiqs8s", "xn--fiqz9s", "xn--fpcrj9c3d", "xn--fzc2c9e2c", "xn--gecrj9c", "xn--h2brj9c", "xn--i1b6b1a6a2e", "xn--io0a7i", "xn--j1amh", "xn--j6w193g", "xn--kprw13d", "xn--kpry57d", "xn--l1acc", "xn--lgbbat1ad8j", "xn--mgb2ddes", "xn--mgb9awbf", "xn--mgba3a4f16a", "xn--mgba3a4fra", "xn--mgbaam7a8h", "xn--mgbab2bd", "xn--mgbayh7gpa", "xn--mgbbh1a71e", "xn--mgbc0a9azcg", "xn--mgberp4a5d4a87g", "xn--mgberp4a5d4ar", "xn--mgbqly7c0a67fbc", "xn--mgbqly7cvafr", "xn--mgbtf8fl", "xn--mgbx4cd0ab", "xn--ngbc5azd", "xn--nnx388a", "xn--node", "xn--nqv7f", "xn--nqv7fs00ema", "xn--o3cw4h", "xn--ogbpf8fl", "xn--p1acf", "xn--p1ai", "xn--pgbs0dh", "xn--q9jyb4c", "xn--rhqv96g", "xn--s9brj9c", "xn--ses554g", "xn--unup4y", "xn--vhquv", "xn--wgbh1c", "xn--wgbl6a", "xn--xhq521b", "xn--xkc2al3hye2a", "xn--xkc2dl3a5ee0h", "xn--yfro4i67o", "xn--ygbi2ammx", "xn--zfr164b", "xxx", "xyz", "yachts", "ye", "yokohama", "yt", "za", "zm", "zone", "zw", "com", "edu", "gov", "mil", "net", "org", "nom", "ac", "co", "gov", "mil", "net", "org", "sch", "accident-investigation", "accident-prevention", "aerobatic", "aeroclub", "aerodrome", "agents", "air-surveillance", "air-traffic-control", "aircraft", "airline", "airport", "airtraffic", "ambulance", "amusement", "association", "author", "ballooning", "broker", "caa", "cargo", "catering", "certification", "championship", "charter", "civilaviation", "club", "conference", "consultant", "consulting", "control", "council", "crew", "design", "dgca", "educator", "emergency", "engine", "engineer", "entertainment", "equipment", "exchange", "express", "federation", "flight", "freight", "fuel", "gliding", "government", "groundhandling", "group", "hanggliding", "homebuilt", "insurance", "journal", "journalist", "leasing", "logistics", "magazine", "maintenance", "marketplace", "media", "microlight", "modelling", "navigation", "parachuting", "paragliding", "passenger-association", "pilot", "press", "production", "recreation", "repbody", "res", "research", "rotorcraft", "safety", "scientist", "services", "show", "skydiving", "software", "student", "taxi", "trader", "trading", "trainer", "union", "workinggroup", "works", "com", "edu", "gov", "net", "org", "co", "com", "net", "nom", "org", "com", "net", "off", "org", "com", "edu", "gov", "mil", "net", "org", "com", "edu", "net", "org", "co", "ed", "gv", "it", "og", "pb", "com", "edu", "gob", "int", "mil", "net", "org", "tur", "blogspot", "e164", "in-addr", "ip6", "iris", "uri", "urn", "gov", "ac", "biz", "co", "gv", "info", "or", "priv", "blogspot", "act", "asn", "com", "conf", "csiro", "edu", "gov", "id", "info", "net", "nsw", "nt", "org", "oz", "qld", "sa", "tas", "vic", "wa", "blogspot", "act", "nsw", "nt", "qld", "sa", "tas", "vic", "wa", "act", "qld", "sa", "tas", "vic", "wa", "com", "biz", "com", "edu", "gov", "info", "int", "mil", "name", "net", "org", "pp", "pro", "co", "com", "edu", "gov", "mil", "net", "org", "rs", "unbi", "unsa", "biz", "com", "edu", "gov", "info", "net", "org", "store", "ac", "blogspot", "gov", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "com", "edu", "gov", "net", "org", "co", "com", "edu", "or", "org", "dyndns", "for-better", "for-more", "for-some", "for-the", "selfip", "webhop", "asso", "barreau", "blogspot", "gouv", "com", "edu", "gov", "net", "org", "com", "edu", "gob", "gov", "int", "mil", "net", "org", "tv", "adm", "adv", "agr", "am", "arq", "art", "ato", "b", "bio", "blog", "bmd", "cim", "cng", "cnt", "com", "coop", "ecn", "eco", "edu", "emp", "eng", "esp", "etc", "eti", "far", "flog", "fm", "fnd", "fot", "fst", "g12", "ggf", "gov", "imb", "ind", "inf", "jor", "jus", "leg", "lel", "mat", "med", "mil", "mus", "net", "nom", "not", "ntr", "odo", "org", "ppg", "pro", "psc", "psi", "qsl", "radio", "rec", "slg", "srv", "taxi", "teo", "tmp", "trd", "tur", "tv", "vet", "vlog", "wiki", "zlg", "blogspot", "com", "edu", "gov", "net", "org", "com", "edu", "gov", "net", "org", "co", "org", "com", "gov", "mil", "of", "com", "edu", "gov", "net", "org", "ab", "bc", "blogspot", "co", "gc", "mb", "nb", "nf", "nl", "ns", "nt", "nu", "on", "pe", "qc", "sk", "yk", "ftpaccess", "game-server", "myphotos", "scrapping", "gov", "blogspot", "blogspot", "ac", "asso", "co", "com", "ed", "edu", "go", "gouv", "int", "md", "net", "or", "org", "presse", "xn--aroport-bya", "www", "co", "gob", "gov", "mil", "gov", "ac", "ah", "bj", "com", "cq", "edu", "fj", "gd", "gov", "gs", "gx", "gz", "ha", "hb", "he", "hi", "hk", "hl", "hn", "jl", "js", "jx", "ln", "mil", "mo", "net", "nm", "nx", "org", "qh", "sc", "sd", "sh", "sn", "sx", "tj", "tw", "xj", "xn--55qx5d", "xn--io0a7i", "xn--od0alg", "xz", "yn", "zj", "arts", "com", "edu", "firm", "gov", "info", "int", "mil", "net", "nom", "org", "rec", "web", "amazonaws", "appspot", "ar", "betainabox", "blogdns", "blogspot", "br", "cechire", "cloudcontrolapp", "cloudcontrolled", "cn", "codespot", "de", "dnsalias", "dnsdojo", "doesntexist", "dontexist", "doomdns", "dreamhosters", "dyn-o-saur", "dynalias", "dyndns-at-home", "dyndns-at-work", "dyndns-blog", "dyndns-free", "dyndns-home", "dyndns-ip", "dyndns-mail", "dyndns-office", "dyndns-pics", "dyndns-remote", "dyndns-server", "dyndns-web", "dyndns-wiki", "dyndns-work", "elasticbeanstalk", "est-a-la-maison", "est-a-la-masion", "est-le-patron", "est-mon-blogueur", "eu", "from-ak", "from-al", "from-ar", "from-ca", "from-ct", "from-dc", "from-de", "from-fl", "from-ga", "from-hi", "from-ia", "from-id", "from-il", "from-in", "from-ks", "from-ky", "from-ma", "from-md", "from-mi", "from-mn", "from-mo", "from-ms", "from-mt", "from-nc", "from-nd", "from-ne", "from-nh", "from-nj", "from-nm", "from-nv", "from-oh", "from-ok", "from-or", "from-pa", "from-pr", "from-ri", "from-sc", "from-sd", "from-tn", "from-tx", "from-ut", "from-va", "from-vt", "from-wa", "from-wi", "from-wv", "from-wy", "gb", "getmyip", "googleapis", "googlecode", "gotdns", "gr", "herokuapp", "herokussl", "hobby-site", "homelinux", "homeunix", "hu", "iamallama", "is-a-anarchist", "is-a-blogger", "is-a-bookkeeper", "is-a-bulls-fan", "is-a-caterer", "is-a-chef", "is-a-conservative", "is-a-cpa", "is-a-cubicle-slave", "is-a-democrat", "is-a-designer", "is-a-doctor", "is-a-financialadvisor", "is-a-geek", "is-a-green", "is-a-guru", "is-a-hard-worker", "is-a-hunter", "is-a-landscaper", "is-a-lawyer", "is-a-liberal", "is-a-libertarian", "is-a-llama", "is-a-musician", "is-a-nascarfan", "is-a-nurse", "is-a-painter", "is-a-personaltrainer", "is-a-photographer", "is-a-player", "is-a-republican", "is-a-rockstar", "is-a-socialist", "is-a-student", "is-a-teacher", "is-a-techie", "is-a-therapist", "is-an-accountant", "is-an-actor", "is-an-actress", "is-an-anarchist", "is-an-artist", "is-an-engineer", "is-an-entertainer", "is-certified", "is-gone", "is-into-anime", "is-into-cars", "is-into-cartoons", "is-into-games", "is-leet", "is-not-certified", "is-slick", "is-uberleet", "is-with-theband", "isa-geek", "isa-hockeynut", "issmarterthanyou", "jpn", "kr", "likes-pie", "likescandy", "neat-url", "no", "operaunite", "qc", "rhcloud", "ro", "ru", "sa", "saves-the-whales", "se", "selfip", "sells-for-less", "sells-for-u", "servebbs", "simple-url", "space-to-rent", "teaches-yoga", "uk", "us", "uy", "withgoogle", "writesthisblog", "za", "compute", "compute-1", "elb", "s3", "s3-ap-northeast-1", "s3-ap-southeast-1", "s3-ap-southeast-2", "s3-eu-west-1", "s3-fips-us-gov-west-1", "s3-sa-east-1", "s3-us-gov-west-1", "s3-us-west-1", "s3-us-west-2", "s3-website-ap-northeast-1", "s3-website-ap-southeast-1", "s3-website-ap-southeast-2", "s3-website-eu-west-1", "s3-website-sa-east-1", "s3-website-us-east-1", "s3-website-us-gov-west-1", "s3-website-us-west-1", "s3-website-us-west-2", "us-east-1", "ap-northeast-1", "ap-southeast-1", "ap-southeast-2", "eu-west-1", "sa-east-1", "us-gov-west-1", "us-west-1", "us-west-2", "z-1", "z-2", "ac", "co", "ed", "fi", "go", "or", "sa", "com", "edu", "gov", "inf", "net", "org", "blogspot", "com", "edu", "net", "org", "ath", "gov", "blogspot", "blogspot", "com", "fuettertdasnetz", "isteingeek", "istmein", "lebtimnetz", "leitungsen", "traeumtgerade", "blogspot", "com", "edu", "gov", "net", "org", "art", "com", "edu", "gob", "gov", "mil", "net", "org", "sld", "web", "art", "asso", "com", "edu", "gov", "net", "org", "pol", "com", "edu", "fin", "gob", "gov", "info", "k12", "med", "mil", "net", "org", "pro", "aip", "com", "edu", "fie", "gov", "lib", "med", "org", "pri", "riik", "com", "edu", "eun", "gov", "mil", "name", "net", "org", "sci", "com", "edu", "gob", "nom", "org", "blogspot", "aland", "blogspot", "iki", "aeroport", "assedic", "asso", "avocat", "avoues", "blogspot", "cci", "chambagri", "chirurgiens-dentistes", "com", "experts-comptables", "geometre-expert", "gouv", "greta", "huissier-justice", "medecin", "nom", "notaires", "pharmacien", "port", "prd", "presse", "tm", "veterinaire", "com", "edu", "gov", "mil", "net", "org", "pvt", "co", "net", "org", "com", "edu", "gov", "mil", "org", "com", "edu", "gov", "ltd", "mod", "org", "ac", "com", "edu", "gov", "net", "org", "asso", "com", "edu", "mobi", "net", "org", "blogspot", "com", "edu", "gov", "net", "org", "com", "edu", "gob", "ind", "mil", "net", "org", "co", "com", "net", "blogspot", "com", "edu", "gov", "idv", "net", "org", "xn--55qx5d", "xn--ciqpn", "xn--gmq050i", "xn--gmqw5a", "xn--io0a7i", "xn--lcvr32d", "xn--mk0axi", "xn--mxtq1m", "xn--od0alg", "xn--od0aq3b", "xn--tn0ag", "xn--uc0atv", "xn--uc0ay4a", "xn--wcvs22d", "xn--zf0avx", "com", "edu", "gob", "mil", "net", "org", "com", "from", "iz", "name", "adult", "art", "asso", "com", "coop", "edu", "firm", "gouv", "info", "med", "net", "org", "perso", "pol", "pro", "rel", "shop", "2000", "agrar", "blogspot", "bolt", "casino", "city", "co", "erotica", "erotika", "film", "forum", "games", "hotel", "info", "ingatlan", "jogasz", "konyvelo", "lakas", "media", "news", "org", "priv", "reklam", "sex", "shop", "sport", "suli", "szex", "tm", "tozsde", "utazas", "video", "ac", "biz", "co", "go", "mil", "my", "net", "or", "sch", "web", "blogspot", "gov", "co", "blogspot", "ac", "co", "com", "net", "org", "tt", "tv", "ltd", "plc", "ac", "blogspot", "co", "edu", "firm", "gen", "gov", "ind", "mil", "net", "nic", "org", "res", "barrel-of-knowledge", "barrell-of-knowledge", "dyndns", "for-our", "groks-the", "groks-this", "here-for-more", "knowsitall", "selfip", "webhop", "eu", "com", "github", "com", "edu", "gov", "mil", "net", "org", "ac", "co", "gov", "id", "net", "org", "sch", "xn--mgba3a4f16a", "xn--mgba3a4fra", "com", "cupcake", "edu", "gov", "int", "net", "org", "ag", "agrigento", "al", "alessandria", "alto-adige", "altoadige", "an", "ancona", "andria-barletta-trani", "andria-trani-barletta", "andriabarlettatrani", "andriatranibarletta", "ao", "aosta", "aoste", "ap", "aq", "aquila", "ar", "arezzo", "ascoli-piceno", "ascolipiceno", "asti", "at", "av", "avellino", "ba", "balsan", "bari", "barletta-trani-andria", "barlettatraniandria", "belluno", "benevento", "bergamo", "bg", "bi", "biella", "bl", "blogspot", "bn", "bo", "bologna", "bolzano", "bozen", "br", "brescia", "brindisi", "bs", "bt", "bz", "ca", "cagliari", "caltanissetta", "campidano-medio", "campidanomedio", "campobasso", "carbonia-iglesias", "carboniaiglesias", "carrara-massa", "carraramassa", "caserta", "catania", "catanzaro", "cb", "ce", "cesena-forli", "cesenaforli", "ch", "chieti", "ci", "cl", "cn", "co", "como", "cosenza", "cr", "cremona", "crotone", "cs", "ct", "cuneo", "cz", "dell-ogliastra", "dellogliastra", "edu", "en", "enna", "fc", "fe", "fermo", "ferrara", "fg", "fi", "firenze", "florence", "fm", "foggia", "forli-cesena", "forlicesena", "fr", "frosinone", "ge", "genoa", "genova", "go", "gorizia", "gov", "gr", "grosseto", "iglesias-carbonia", "iglesiascarbonia", "im", "imperia", "is", "isernia", "kr", "la-spezia", "laquila", "laspezia", "latina", "lc", "le", "lecce", "lecco", "li", "livorno", "lo", "lodi", "lt", "lu", "lucca", "macerata", "mantova", "massa-carrara", "massacarrara", "matera", "mb", "mc", "me", "medio-campidano", "mediocampidano", "messina", "mi", "milan", "milano", "mn", "mo", "modena", "monza", "monza-brianza", "monza-e-della-brianza", "monzabrianza", "monzaebrianza", "monzaedellabrianza", "ms", "mt", "na", "naples", "napoli", "no", "novara", "nu", "nuoro", "og", "ogliastra", "olbia-tempio", "olbiatempio", "or", "oristano", "ot", "pa", "padova", "padua", "palermo", "parma", "pavia", "pc", "pd", "pe", "perugia", "pesaro-urbino", "pesarourbino", "pescara", "pg", "pi", "piacenza", "pisa", "pistoia", "pn", "po", "pordenone", "potenza", "pr", "prato", "pt", "pu", "pv", "pz", "ra", "ragusa", "ravenna", "rc", "re", "reggio-calabria", "reggio-emilia", "reggiocalabria", "reggioemilia", "rg", "ri", "rieti", "rimini", "rm", "rn", "ro", "roma", "rome", "rovigo", "sa", "salerno", "sassari", "savona", "si", "siena", "siracusa", "so", "sondrio", "sp", "sr", "ss", "suedtirol", "sv", "ta", "taranto", "te", "tempio-olbia", "tempioolbia", "teramo", "terni", "tn", "to", "torino", "tp", "tr", "trani-andria-barletta", "trani-barletta-andria", "traniandriabarletta", "tranibarlettaandria", "trapani", "trentino", "trento", "treviso", "trieste", "ts", "turin", "tv", "ud", "udine", "urbino-pesaro", "urbinopesaro", "va", "varese", "vb", "vc", "ve", "venezia", "venice", "verbania", "vercelli", "verona", "vi", "vibo-valentia", "vibovalentia", "vicenza", "viterbo", "vr", "vs", "vt", "vv", "co", "net", "org", "com", "edu", "gov", "mil", "name", "net", "org", "sch", "ac", "ad", "aichi", "akita", "aomori", "blogspot", "chiba", "co", "ed", "ehime", "fukui", "fukuoka", "fukushima", "gifu", "go", "gr", "gunma", "hiroshima", "hokkaido", "hyogo", "ibaraki", "ishikawa", "iwate", "kagawa", "kagoshima", "kanagawa", "kawasaki", "kitakyushu", "kobe", "kochi", "kumamoto", "kyoto", "lg", "mie", "miyagi", "miyazaki", "nagano", "nagasaki", "nagoya", "nara", "ne", "niigata", "oita", "okayama", "okinawa", "or", "osaka", "saga", "saitama", "sapporo", "sendai", "shiga", "shimane", "shizuoka", "tochigi", "tokushima", "tokyo", "tottori", "toyama", "wakayama", "yamagata", "yamaguchi", "yamanashi", "yokohama", "aisai", "ama", "anjo", "asuke", "chiryu", "chita", "fuso", "gamagori", "handa", "hazu", "hekinan", "higashiura", "ichinomiya", "inazawa", "inuyama", "isshiki", "iwakura", "kanie", "kariya", "kasugai", "kira", "kiyosu", "komaki", "konan", "kota", "mihama", "miyoshi", "nagakute", "nishio", "nisshin", "obu", "oguchi", "oharu", "okazaki", "owariasahi", "seto", "shikatsu", "shinshiro", "shitara", "tahara", "takahama", "tobishima", "toei", "togo", "tokai", "tokoname", "toyoake", "toyohashi", "toyokawa", "toyone", "toyota", "tsushima", "yatomi", "akita", "daisen", "fujisato", "gojome", "hachirogata", "happou", "higashinaruse", "honjo", "honjyo", "ikawa", "kamikoani", "kamioka", "katagami", "kazuno", "kitaakita", "kosaka", "kyowa", "misato", "mitane", "moriyoshi", "nikaho", "noshiro", "odate", "oga", "ogata", "semboku", "yokote", "yurihonjo", "aomori", "gonohe", "hachinohe", "hashikami", "hiranai", "hirosaki", "itayanagi", "kuroishi", "misawa", "mutsu", "nakadomari", "noheji", "oirase", "owani", "rokunohe", "sannohe", "shichinohe", "shingo", "takko", "towada", "tsugaru", "tsuruta", "abiko", "asahi", "chonan", "chosei", "choshi", "chuo", "funabashi", "futtsu", "hanamigawa", "ichihara", "ichikawa", "ichinomiya", "inzai", "isumi", "kamagaya", "kamogawa", "kashiwa", "katori", "katsuura", "kimitsu", "kisarazu", "kozaki", "kujukuri", "kyonan", "matsudo", "midori", "mihama", "minamiboso", "mobara", "mutsuzawa", "nagara", "nagareyama", "narashino", "narita", "noda", "oamishirasato", "omigawa", "onjuku", "otaki", "sakae", "sakura", "shimofusa", "shirako", "shiroi", "shisui", "sodegaura", "sosa", "tako", "tateyama", "togane", "tohnosho", "tomisato", "urayasu", "yachimata", "yachiyo", "yokaichiba", "yokoshibahikari", "yotsukaido", "ainan", "honai", "ikata", "imabari", "iyo", "kamijima", "kihoku", "kumakogen", "masaki", "matsuno", "matsuyama", "namikata", "niihama", "ozu", "saijo", "seiyo", "shikokuchuo", "tobe", "toon", "uchiko", "uwajima", "yawatahama", "echizen", "eiheiji", "fukui", "ikeda", "katsuyama", "mihama", "minamiechizen", "obama", "ohi", "ono", "sabae", "sakai", "takahama", "tsuruga", "wakasa", "ashiya", "buzen", "chikugo", "chikuho", "chikujo", "chikushino", "chikuzen", "chuo", "dazaifu", "fukuchi", "hakata", "higashi", "hirokawa", "hisayama", "iizuka", "inatsuki", "kaho", "kasuga", "kasuya", "kawara", "keisen", "koga", "kurate", "kurogi", "kurume", "minami", "miyako", "miyama", "miyawaka", "mizumaki", "munakata", "nakagawa", "nakama", "nishi", "nogata", "ogori", "okagaki", "okawa", "oki", "omuta", "onga", "onojo", "oto", "saigawa", "sasaguri", "shingu", "shinyoshitomi", "shonai", "soeda", "sue", "tachiarai", "tagawa", "takata", "toho", "toyotsu", "tsuiki", "ukiha", "umi", "usui", "yamada", "yame", "yanagawa", "yukuhashi", "aizubange", "aizumisato", "aizuwakamatsu", "asakawa", "bandai", "date", "fukushima", "furudono", "futaba", "hanawa", "higashi", "hirata", "hirono", "iitate", "inawashiro", "ishikawa", "iwaki", "izumizaki", "kagamiishi", "kaneyama", "kawamata", "kitakata", "kitashiobara", "koori", "koriyama", "kunimi", "miharu", "mishima", "namie", "nango", "nishiaizu", "nishigo", "okuma", "omotego", "ono", "otama", "samegawa", "shimogo", "shirakawa", "showa", "soma", "sukagawa", "taishin", "tamakawa", "tanagura", "tenei", "yabuki", "yamato", "yamatsuri", "yanaizu", "yugawa", "anpachi", "ena", "gifu", "ginan", "godo", "gujo", "hashima", "hichiso", "hida", "higashishirakawa", "ibigawa", "ikeda", "kakamigahara", "kani", "kasahara", "kasamatsu", "kawaue", "kitagata", "mino", "minokamo", "mitake", "mizunami", "motosu", "nakatsugawa", "ogaki", "sakahogi", "seki", "sekigahara", "shirakawa", "tajimi", "takayama", "tarui", "toki", "tomika", "wanouchi", "yamagata", "yaotsu", "yoro", "annaka", "chiyoda", "fujioka", "higashiagatsuma", "isesaki", "itakura", "kanna", "kanra", "katashina", "kawaba", "kiryu", "kusatsu", "maebashi", "meiwa", "midori", "minakami", "naganohara", "nakanojo", "nanmoku", "numata", "oizumi", "ora", "ota", "shibukawa", "shimonita", "shinto", "showa", "takasaki", "takayama", "tamamura", "tatebayashi", "tomioka", "tsukiyono", "tsumagoi", "ueno", "yoshioka", "asaminami", "daiwa", "etajima", "fuchu", "fukuyama", "hatsukaichi", "higashihiroshima", "hongo", "jinsekikogen", "kaita", "kui", "kumano", "kure", "mihara", "miyoshi", "naka", "onomichi", "osakikamijima", "otake", "saka", "sera", "seranishi", "shinichi", "shobara", "takehara", "abashiri", "abira", "aibetsu", "akabira", "akkeshi", "asahikawa", "ashibetsu", "ashoro", "assabu", "atsuma", "bibai", "biei", "bifuka", "bihoro", "biratori", "chippubetsu", "chitose", "date", "ebetsu", "embetsu", "eniwa", "erimo", "esan", "esashi", "fukagawa", "fukushima", "furano", "furubira", "haboro", "hakodate", "hamatonbetsu", "hidaka", "higashikagura", "higashikawa", "hiroo", "hokuryu", "hokuto", "honbetsu", "horokanai", "horonobe", "ikeda", "imakane", "ishikari", "iwamizawa", "iwanai", "kamifurano", "kamikawa", "kamishihoro", "kamisunagawa", "kamoenai", "kayabe", "kembuchi", "kikonai", "kimobetsu", "kitahiroshima", "kitami", "kiyosato", "koshimizu", "kunneppu", "kuriyama", "kuromatsunai", "kushiro", "kutchan", "kyowa", "mashike", "matsumae", "mikasa", "minamifurano", "mombetsu", "moseushi", "mukawa", "muroran", "naie", "nakagawa", "nakasatsunai", "nakatombetsu", "nanae", "nanporo", "nayoro", "nemuro", "niikappu", "niki", "nishiokoppe", "noboribetsu", "numata", "obihiro", "obira", "oketo", "okoppe", "otaru", "otobe", "otofuke", "otoineppu", "oumu", "ozora", "pippu", "rankoshi", "rebun", "rikubetsu", "rishiri", "rishirifuji", "saroma", "sarufutsu", "shakotan", "shari", "shibecha", "shibetsu", "shikabe", "shikaoi", "shimamaki", "shimizu", "shimokawa", "shinshinotsu", "shintoku", "shiranuka", "shiraoi", "shiriuchi", "sobetsu", "sunagawa", "taiki", "takasu", "takikawa", "takinoue", "teshikaga", "tobetsu", "tohma", "tomakomai", "tomari", "toya", "toyako", "toyotomi", "toyoura", "tsubetsu", "tsukigata", "urakawa", "urausu", "uryu", "utashinai", "wakkanai", "wassamu", "yakumo", "yoichi", "aioi", "akashi", "ako", "amagasaki", "aogaki", "asago", "ashiya", "awaji", "fukusaki", "goshiki", "harima", "himeji", "ichikawa", "inagawa", "itami", "kakogawa", "kamigori", "kamikawa", "kasai", "kasuga", "kawanishi", "miki", "minamiawaji", "nishinomiya", "nishiwaki", "ono", "sanda", "sannan", "sasayama", "sayo", "shingu", "shinonsen", "shiso", "sumoto", "taishi", "taka", "takarazuka", "takasago", "takino", "tamba", "tatsuno", "toyooka", "yabu", "yashiro", "yoka", "yokawa", "ami", "asahi", "bando", "chikusei", "daigo", "fujishiro", "hitachi", "hitachinaka", "hitachiomiya", "hitachiota", "ibaraki", "ina", "inashiki", "itako", "iwama", "joso", "kamisu", "kasama", "kashima", "kasumigaura", "koga", "miho", "mito", "moriya", "naka", "namegata", "oarai", "ogawa", "omitama", "ryugasaki", "sakai", "sakuragawa", "shimodate", "shimotsuma", "shirosato", "sowa", "suifu", "takahagi", "tamatsukuri", "tokai", "tomobe", "tone", "toride", "tsuchiura", "tsukuba", "uchihara", "ushiku", "yachiyo", "yamagata", "yawara", "yuki", "anamizu", "hakui", "hakusan", "kaga", "kahoku", "kanazawa", "kawakita", "komatsu", "nakanoto", "nanao", "nomi", "nonoichi", "noto", "shika", "suzu", "tsubata", "tsurugi", "uchinada", "wajima", "fudai", "fujisawa", "hanamaki", "hiraizumi", "hirono", "ichinohe", "ichinoseki", "iwaizumi", "iwate", "joboji", "kamaishi", "kanegasaki", "karumai", "kawai", "kitakami", "kuji", "kunohe", "kuzumaki", "miyako", "mizusawa", "morioka", "ninohe", "noda", "ofunato", "oshu", "otsuchi", "rikuzentakata", "shiwa", "shizukuishi", "sumita", "takizawa", "tanohata", "tono", "yahaba", "yamada", "ayagawa", "higashikagawa", "kanonji", "kotohira", "manno", "marugame", "mitoyo", "naoshima", "sanuki", "tadotsu", "takamatsu", "tonosho", "uchinomi", "utazu", "zentsuji", "akune", "amami", "hioki", "isa", "isen", "izumi", "kagoshima", "kanoya", "kawanabe", "kinko", "kouyama", "makurazaki", "matsumoto", "minamitane", "nakatane", "nishinoomote", "satsumasendai", "soo", "tarumizu", "yusui", "aikawa", "atsugi", "ayase", "chigasaki", "ebina", "fujisawa", "hadano", "hakone", "hiratsuka", "isehara", "kaisei", "kamakura", "kiyokawa", "matsuda", "minamiashigara", "miura", "nakai", "ninomiya", "odawara", "oi", "oiso", "sagamihara", "samukawa", "tsukui", "yamakita", "yamato", "yokosuka", "yugawara", "zama", "zushi", "city", "city", "city", "aki", "geisei", "hidaka", "higashitsuno", "ino", "kagami", "kami", "kitagawa", "kochi", "mihara", "motoyama", "muroto", "nahari", "nakamura", "nankoku", "nishitosa", "niyodogawa", "ochi", "okawa", "otoyo", "otsuki", "sakawa", "sukumo", "susaki", "tosa", "tosashimizu", "toyo", "tsuno", "umaji", "yasuda", "yusuhara", "amakusa", "arao", "aso", "choyo", "gyokuto", "hitoyoshi", "kamiamakusa", "kashima", "kikuchi", "kosa", "kumamoto", "mashiki", "mifune", "minamata", "minamioguni", "nagasu", "nishihara", "oguni", "ozu", "sumoto", "takamori", "uki", "uto", "yamaga", "yamato", "yatsushiro", "ayabe", "fukuchiyama", "higashiyama", "ide", "ine", "joyo", "kameoka", "kamo", "kita", "kizu", "kumiyama", "kyotamba", "kyotanabe", "kyotango", "maizuru", "minami", "minamiyamashiro", "miyazu", "muko", "nagaokakyo", "nakagyo", "nantan", "oyamazaki", "sakyo", "seika", "tanabe", "uji", "ujitawara", "wazuka", "yamashina", "yawata", "asahi", "inabe", "ise", "kameyama", "kawagoe", "kiho", "kisosaki", "kiwa", "komono", "kumano", "kuwana", "matsusaka", "meiwa", "mihama", "minamiise", "misugi", "miyama", "nabari", "shima", "suzuka", "tado", "taiki", "taki", "tamaki", "toba", "tsu", "udono", "ureshino", "watarai", "yokkaichi", "furukawa", "higashimatsushima", "ishinomaki", "iwanuma", "kakuda", "kami", "kawasaki", "kesennuma", "marumori", "matsushima", "minamisanriku", "misato", "murata", "natori", "ogawara", "ohira", "onagawa", "osaki", "rifu", "semine", "shibata", "shichikashuku", "shikama", "shiogama", "shiroishi", "tagajo", "taiwa", "tome", "tomiya", "wakuya", "watari", "yamamoto", "zao", "aya", "ebino", "gokase", "hyuga", "kadogawa", "kawaminami", "kijo", "kitagawa", "kitakata", "kitaura", "kobayashi", "kunitomi", "kushima", "mimata", "miyakonojo", "miyazaki", "morotsuka", "nichinan", "nishimera", "nobeoka", "saito", "shiiba", "shintomi", "takaharu", "takanabe", "takazaki", "tsuno", "achi", "agematsu", "anan", "aoki", "asahi", "azumino", "chikuhoku", "chikuma", "chino", "fujimi", "hakuba", "hara", "hiraya", "iida", "iijima", "iiyama", "iizuna", "ikeda", "ikusaka", "ina", "karuizawa", "kawakami", "kiso", "kisofukushima", "kitaaiki", "komagane", "komoro", "matsukawa", "matsumoto", "miasa", "minamiaiki", "minamimaki", "minamiminowa", "minowa", "miyada", "miyota", "mochizuki", "nagano", "nagawa", "nagiso", "nakagawa", "nakano", "nozawaonsen", "obuse", "ogawa", "okaya", "omachi", "omi", "ookuwa", "ooshika", "otaki", "otari", "sakae", "sakaki", "saku", "sakuho", "shimosuwa", "shinanomachi", "shiojiri", "suwa", "suzaka", "takagi", "takamori", "takayama", "tateshina", "tatsuno", "togakushi", "togura", "tomi", "ueda", "wada", "yamagata", "yamanouchi", "yasaka", "yasuoka", "chijiwa", "futsu", "goto", "hasami", "hirado", "iki", "isahaya", "kawatana", "kuchinotsu", "matsuura", "nagasaki", "obama", "omura", "oseto", "saikai", "sasebo", "seihi", "shimabara", "shinkamigoto", "togitsu", "tsushima", "unzen", "city", "ando", "gose", "heguri", "higashiyoshino", "ikaruga", "ikoma", "kamikitayama", "kanmaki", "kashiba", "kashihara", "katsuragi", "kawai", "kawakami", "kawanishi", "koryo", "kurotaki", "mitsue", "miyake", "nara", "nosegawa", "oji", "ouda", "oyodo", "sakurai", "sango", "shimoichi", "shimokitayama", "shinjo", "soni", "takatori", "tawaramoto", "tenkawa", "tenri", "uda", "yamatokoriyama", "yamatotakada", "yamazoe", "yoshino", "aga", "agano", "gosen", "itoigawa", "izumozaki", "joetsu", "kamo", "kariwa", "kashiwazaki", "minamiuonuma", "mitsuke", "muika", "murakami", "myoko", "nagaoka", "niigata", "ojiya", "omi", "sado", "sanjo", "seiro", "seirou", "sekikawa", "shibata", "tagami", "tainai", "tochio", "tokamachi", "tsubame", "tsunan", "uonuma", "yahiko", "yoita", "yuzawa", "beppu", "bungoono", "bungotakada", "hasama", "hiji", "himeshima", "hita", "kamitsue", "kokonoe", "kuju", "kunisaki", "kusu", "oita", "saiki", "taketa", "tsukumi", "usa", "usuki", "yufu", "akaiwa", "asakuchi", "bizen", "hayashima", "ibara", "kagamino", "kasaoka", "kibichuo", "kumenan", "kurashiki", "maniwa", "misaki", "nagi", "niimi", "nishiawakura", "okayama", "satosho", "setouchi", "shinjo", "shoo", "soja", "takahashi", "tamano", "tsuyama", "wake", "yakage", "aguni", "ginowan", "ginoza", "gushikami", "haebaru", "higashi", "hirara", "iheya", "ishigaki", "ishikawa", "itoman", "izena", "kadena", "kin", "kitadaito", "kitanakagusuku", "kumejima", "kunigami", "minamidaito", "motobu", "nago", "naha", "nakagusuku", "nakijin", "nanjo", "nishihara", "ogimi", "okinawa", "onna", "shimoji", "taketomi", "tarama", "tokashiki", "tomigusuku", "tonaki", "urasoe", "uruma", "yaese", "yomitan", "yonabaru", "yonaguni", "zamami", "abeno", "chihayaakasaka", "chuo", "daito", "fujiidera", "habikino", "hannan", "higashiosaka", "higashisumiyoshi", "higashiyodogawa", "hirakata", "ibaraki", "ikeda", "izumi", "izumiotsu", "izumisano", "kadoma", "kaizuka", "kanan", "kashiwara", "katano", "kawachinagano", "kishiwada", "kita", "kumatori", "matsubara", "minato", "minoh", "misaki", "moriguchi", "neyagawa", "nishi", "nose", "osakasayama", "sakai", "sayama", "sennan", "settsu", "shijonawate", "shimamoto", "suita", "tadaoka", "taishi", "tajiri", "takaishi", "takatsuki", "tondabayashi", "toyonaka", "toyono", "yao", "ariake", "arita", "fukudomi", "genkai", "hamatama", "hizen", "imari", "kamimine", "kanzaki", "karatsu", "kashima", "kitagata", "kitahata", "kiyama", "kouhoku", "kyuragi", "nishiarita", "ogi", "omachi", "ouchi", "saga", "shiroishi", "taku", "tara", "tosu", "yoshinogari", "arakawa", "asaka", "chichibu", "fujimi", "fujimino", "fukaya", "hanno", "hanyu", "hasuda", "hatogaya", "hatoyama", "hidaka", "higashichichibu", "higashimatsuyama", "honjo", "ina", "iruma", "iwatsuki", "kamiizumi", "kamikawa", "kamisato", "kasukabe", "kawagoe", "kawaguchi", "kawajima", "kazo", "kitamoto", "koshigaya", "kounosu", "kuki", "kumagaya", "matsubushi", "minano", "misato", "miyashiro", "miyoshi", "moroyama", "nagatoro", "namegawa", "niiza", "ogano", "ogawa", "ogose", "okegawa", "omiya", "otaki", "ranzan", "ryokami", "saitama", "sakado", "satte", "sayama", "shiki", "shiraoka", "soka", "sugito", "toda", "tokigawa", "tokorozawa", "tsurugashima", "urawa", "warabi", "yashio", "yokoze", "yono", "yorii", "yoshida", "yoshikawa", "yoshimi", "city", "city", "aisho", "gamo", "higashiomi", "hikone", "koka", "konan", "kosei", "koto", "kusatsu", "maibara", "moriyama", "nagahama", "nishiazai", "notogawa", "omihachiman", "otsu", "ritto", "ryuoh", "takashima", "takatsuki", "torahime", "toyosato", "yasu", "akagi", "ama", "gotsu", "hamada", "higashiizumo", "hikawa", "hikimi", "izumo", "kakinoki", "masuda", "matsue", "misato", "nishinoshima", "ohda", "okinoshima", "okuizumo", "shimane", "tamayu", "tsuwano", "unnan", "yakumo", "yasugi", "yatsuka", "arai", "atami", "fuji", "fujieda", "fujikawa", "fujinomiya", "fukuroi", "gotemba", "haibara", "hamamatsu", "higashiizu", "ito", "iwata", "izu", "izunokuni", "kakegawa", "kannami", "kawanehon", "kawazu", "kikugawa", "kosai", "makinohara", "matsuzaki", "minamiizu", "mishima", "morimachi", "nishiizu", "numazu", "omaezaki", "shimada", "shimizu", "shimoda", "shizuoka", "susono", "yaizu", "yoshida", "ashikaga", "bato", "haga", "ichikai", "iwafune", "kaminokawa", "kanuma", "karasuyama", "kuroiso", "mashiko", "mibu", "moka", "motegi", "nasu", "nasushiobara", "nikko", "nishikata", "nogi", "ohira", "ohtawara", "oyama", "sakura", "sano", "shimotsuke", "shioya", "takanezawa", "tochigi", "tsuga", "ujiie", "utsunomiya", "yaita", "aizumi", "anan", "ichiba", "itano", "kainan", "komatsushima", "matsushige", "mima", "minami", "miyoshi", "mugi", "nakagawa", "naruto", "sanagochi", "shishikui", "tokushima", "wajiki", "adachi", "akiruno", "akishima", "aogashima", "arakawa", "bunkyo", "chiyoda", "chofu", "chuo", "edogawa", "fuchu", "fussa", "hachijo", "hachioji", "hamura", "higashikurume", "higashimurayama", "higashiyamato", "hino", "hinode", "hinohara", "inagi", "itabashi", "katsushika", "kita", "kiyose", "kodaira", "koganei", "kokubunji", "komae", "koto", "kouzushima", "kunitachi", "machida", "meguro", "minato", "mitaka", "mizuho", "musashimurayama", "musashino", "nakano", "nerima", "ogasawara", "okutama", "ome", "oshima", "ota", "setagaya", "shibuya", "shinagawa", "shinjuku", "suginami", "sumida", "tachikawa", "taito", "tama", "toshima", "chizu", "hino", "kawahara", "koge", "kotoura", "misasa", "nanbu", "nichinan", "sakaiminato", "tottori", "wakasa", "yazu", "yonago", "asahi", "fuchu", "fukumitsu", "funahashi", "himi", "imizu", "inami", "johana", "kamiichi", "kurobe", "nakaniikawa", "namerikawa", "nanto", "nyuzen", "oyabe", "taira", "takaoka", "tateyama", "toga", "tonami", "toyama", "unazuki", "uozu", "yamada", "arida", "aridagawa", "gobo", "hashimoto", "hidaka", "hirogawa", "inami", "iwade", "kainan", "kamitonda", "katsuragi", "kimino", "kinokawa", "kitayama", "koya", "koza", "kozagawa", "kudoyama", "kushimoto", "mihama", "misato", "nachikatsuura", "shingu", "shirahama", "taiji", "tanabe", "wakayama", "yuasa", "yura", "asahi", "funagata", "higashine", "iide", "kahoku", "kaminoyama", "kaneyama", "kawanishi", "mamurogawa", "mikawa", "murayama", "nagai", "nakayama", "nanyo", "nishikawa", "obanazawa", "oe", "oguni", "ohkura", "oishida", "sagae", "sakata", "sakegawa", "shinjo", "shirataka", "shonai", "takahata", "tendo", "tozawa", "tsuruoka", "yamagata", "yamanobe", "yonezawa", "yuza", "abu", "hagi", "hikari", "hofu", "iwakuni", "kudamatsu", "mitou", "nagato", "oshima", "shimonoseki", "shunan", "tabuse", "tokuyama", "toyota", "ube", "yuu", "chuo", "doshi", "fuefuki", "fujikawa", "fujikawaguchiko", "fujiyoshida", "hayakawa", "hokuto", "ichikawamisato", "kai", "kofu", "koshu", "kosuge", "minami-alps", "minobu", "nakamichi", "nanbu", "narusawa", "nirasaki", "nishikatsura", "oshino", "otsuki", "showa", "tabayama", "tsuru", "uenohara", "yamanakako", "yamanashi", "city", "com", "edu", "gov", "mil", "net", "org", "biz", "com", "edu", "gov", "info", "net", "org", "ass", "asso", "com", "coop", "edu", "gouv", "gov", "medecin", "mil", "nom", "notaires", "org", "pharmaciens", "prd", "presse", "tm", "veterinaire", "edu", "gov", "net", "org", "com", "edu", "gov", "org", "rep", "tra", "ac", "blogspot", "busan", "chungbuk", "chungnam", "co", "daegu", "daejeon", "es", "gangwon", "go", "gwangju", "gyeongbuk", "gyeonggi", "gyeongnam", "hs", "incheon", "jeju", "jeonbuk", "jeonnam", "kg", "mil", "ms", "ne", "or", "pe", "re", "sc", "seoul", "ulsan", "com", "edu", "gov", "net", "org", "com", "edu", "gov", "mil", "net", "org", "c", "com", "edu", "gov", "info", "int", "net", "org", "per", "com", "edu", "gov", "net", "org", "co", "com", "edu", "gov", "net", "org", "assn", "com", "edu", "gov", "grp", "hotel", "int", "ltd", "net", "ngo", "org", "sch", "soc", "web", "com", "edu", "gov", "net", "org", "co", "org", "gov", "asn", "com", "conf", "edu", "gov", "id", "mil", "net", "org", "com", "edu", "gov", "id", "med", "net", "org", "plc", "sch", "ac", "co", "gov", "net", "org", "press", "asso", "tm", "ac", "co", "edu", "gov", "its", "net", "org", "priv", "com", "edu", "gov", "mil", "nom", "org", "prd", "tm", "com", "edu", "gov", "inf", "name", "net", "org", "com", "edu", "gouv", "gov", "net", "org", "presse", "edu", "gov", "nyc", "org", "com", "edu", "gov", "net", "org", "blogspot", "gov", "com", "edu", "net", "org", "ac", "co", "com", "gov", "net", "or", "org", "academy", "agriculture", "air", "airguard", "alabama", "alaska", "amber", "ambulance", "american", "americana", "americanantiques", "americanart", "amsterdam", "and", "annefrank", "anthro", "anthropology", "antiques", "aquarium", "arboretum", "archaeological", "archaeology", "architecture", "art", "artanddesign", "artcenter", "artdeco", "arteducation", "artgallery", "arts", "artsandcrafts", "asmatart", "assassination", "assisi", "association", "astronomy", "atlanta", "austin", "australia", "automotive", "aviation", "axis", "badajoz", "baghdad", "bahn", "bale", "baltimore", "barcelona", "baseball", "basel", "baths", "bauern", "beauxarts", "beeldengeluid", "bellevue", "bergbau", "berkeley", "berlin", "bern", "bible", "bilbao", "bill", "birdart", "birthplace", "bonn", "boston", "botanical", "botanicalgarden", "botanicgarden", "botany", "brandywinevalley", "brasil", "bristol", "british", "britishcolumbia", "broadcast", "brunel", "brussel", "brussels", "bruxelles", "building", "burghof", "bus", "bushey", "cadaques", "california", "cambridge", "can", "canada", "capebreton", "carrier", "cartoonart", "casadelamoneda", "castle", "castres", "celtic", "center", "chattanooga", "cheltenham", "chesapeakebay", "chicago", "children", "childrens", "childrensgarden", "chiropractic", "chocolate", "christiansburg", "cincinnati", "cinema", "circus", "civilisation", "civilization", "civilwar", "clinton", "clock", "coal", "coastaldefence", "cody", "coldwar", "collection", "colonialwilliamsburg", "coloradoplateau", "columbia", "columbus", "communication", "communications", "community", "computer", "computerhistory", "contemporary", "contemporaryart", "convent", "copenhagen", "corporation", "corvette", "costume", "countryestate", "county", "crafts", "cranbrook", "creation", "cultural", "culturalcenter", "culture", "cyber", "cymru", "dali", "dallas", "database", "ddr", "decorativearts", "delaware", "delmenhorst", "denmark", "depot", "design", "detroit", "dinosaur", "discovery", "dolls", "donostia", "durham", "eastafrica", "eastcoast", "education", "educational", "egyptian", "eisenbahn", "elburg", "elvendrell", "embroidery", "encyclopedic", "england", "entomology", "environment", "environmentalconservation", "epilepsy", "essex", "estate", "ethnology", "exeter", "exhibition", "family", "farm", "farmequipment", "farmers", "farmstead", "field", "figueres", "filatelia", "film", "fineart", "finearts", "finland", "flanders", "florida", "force", "fortmissoula", "fortworth", "foundation", "francaise", "frankfurt", "franziskaner", "freemasonry", "freiburg", "fribourg", "frog", "fundacio", "furniture", "gallery", "garden", "gateway", "geelvinck", "gemological", "geology", "georgia", "giessen", "glas", "glass", "gorge", "grandrapids", "graz", "guernsey", "halloffame", "hamburg", "handson", "harvestcelebration", "hawaii", "health", "heimatunduhren", "hellas", "helsinki", "hembygdsforbund", "heritage", "histoire", "historical", "historicalsociety", "historichouses", "historisch", "historisches", "history", "historyofscience", "horology", "house", "humanities", "illustration", "imageandsound", "indian", "indiana", "indianapolis", "indianmarket", "intelligence", "interactive", "iraq", "iron", "isleofman", "jamison", "jefferson", "jerusalem", "jewelry", "jewish", "jewishart", "jfk", "journalism", "judaica", "judygarland", "juedisches", "juif", "karate", "karikatur", "kids", "koebenhavn", "koeln", "kunst", "kunstsammlung", "kunstunddesign", "labor", "labour", "lajolla", "lancashire", "landes", "lans", "larsson", "lewismiller", "lincoln", "linz", "living", "livinghistory", "localhistory", "london", "losangeles", "louvre", "loyalist", "lucerne", "luxembourg", "luzern", "mad", "madrid", "mallorca", "manchester", "mansion", "mansions", "manx", "marburg", "maritime", "maritimo", "maryland", "marylhurst", "media", "medical", "medizinhistorisches", "meeres", "memorial", "mesaverde", "michigan", "midatlantic", "military", "mill", "miners", "mining", "minnesota", "missile", "missoula", "modern", "moma", "money", "monmouth", "monticello", "montreal", "moscow", "motorcycle", "muenchen", "muenster", "mulhouse", "muncie", "museet", "museumcenter", "museumvereniging", "music", "national", "nationalfirearms", "nationalheritage", "nativeamerican", "naturalhistory", "naturalhistorymuseum", "naturalsciences", "nature", "naturhistorisches", "natuurwetenschappen", "naumburg", "naval", "nebraska", "neues", "newhampshire", "newjersey", "newmexico", "newport", "newspaper", "newyork", "niepce", "norfolk", "north", "nrw", "nuernberg", "nuremberg", "nyc", "nyny", "oceanographic", "oceanographique", "omaha", "online", "ontario", "openair", "oregon", "oregontrail", "otago", "oxford", "pacific", "paderborn", "palace", "paleo", "palmsprings", "panama", "paris", "pasadena", "pharmacy", "philadelphia", "philadelphiaarea", "philately", "phoenix", "photography", "pilots", "pittsburgh", "planetarium", "plantation", "plants", "plaza", "portal", "portland", "portlligat", "posts-and-telecommunications", "preservation", "presidio", "press", "project", "public", "pubol", "quebec", "railroad", "railway", "research", "resistance", "riodejaneiro", "rochester", "rockart", "roma", "russia", "saintlouis", "salem", "salvadordali", "salzburg", "sandiego", "sanfrancisco", "santabarbara", "santacruz", "santafe", "saskatchewan", "satx", "savannahga", "schlesisches", "schoenbrunn", "schokoladen", "school", "schweiz", "science", "science-fiction", "scienceandhistory", "scienceandindustry", "sciencecenter", "sciencecenters", "sciencehistory", "sciences", "sciencesnaturelles", "scotland", "seaport", "settlement", "settlers", "shell", "sherbrooke", "sibenik", "silk", "ski", "skole", "society", "sologne", "soundandvision", "southcarolina", "southwest", "space", "spy", "square", "stadt", "stalbans", "starnberg", "state", "stateofdelaware", "station", "steam", "steiermark", "stjohn", "stockholm", "stpetersburg", "stuttgart", "suisse", "surgeonshall", "surrey", "svizzera", "sweden", "sydney", "tank", "tcm", "technology", "telekommunikation", "television", "texas", "textile", "theater", "time", "timekeeping", "topology", "torino", "touch", "town", "transport", "tree", "trolley", "trust", "trustee", "uhren", "ulm", "undersea", "university", "usa", "usantiques", "usarts", "uscountryestate", "usculture", "usdecorativearts", "usgarden", "ushistory", "ushuaia", "uslivinghistory", "utah", "uvic", "valley", "vantaa", "versailles", "viking", "village", "virginia", "virtual", "virtuel", "vlaanderen", "volkenkunde", "wales", "wallonie", "war", "washingtondc", "watch-and-clock", "watchandclock", "western", "westfalen", "whaling", "wildlife", "williamsburg", "windmill", "workshop", "xn--9dbhblg6di", "xn--comunicaes-v6a2o", "xn--correios-e-telecomunicaes-ghc29a", "xn--h1aegh", "xn--lns-qla", "york", "yorkshire", "yosemite", "youth", "zoological", "zoology", "aero", "biz", "com", "coop", "edu", "gov", "info", "int", "mil", "museum", "name", "net", "org", "pro", "ac", "biz", "co", "com", "coop", "edu", "gov", "int", "museum", "net", "org", "blogspot", "com", "edu", "gob", "net", "org", "com", "edu", "gov", "mil", "name", "net", "org", "teledata", "ca", "cc", "co", "com", "dr", "in", "info", "mobi", "mx", "name", "or", "org", "pro", "school", "tv", "us", "ws", "her", "his", "forgot", "forgot", "asso", "at-band-camp", "azure-mobile", "azurewebsites", "blogdns", "broke-it", "buyshouses", "cloudapp", "cloudfront", "dnsalias", "dnsdojo", "does-it", "dontexist", "dynalias", "dynathome", "endofinternet", "fastly", "from-az", "from-co", "from-la", "from-ny", "gb", "gets-it", "ham-radio-op", "homeftp", "homeip", "homelinux", "homeunix", "hu", "in-the-band", "is-a-chef", "is-a-geek", "isa-geek", "jp", "kicks-ass", "office-on-the", "podzone", "scrapper-site", "se", "selfip", "sells-it", "servebbs", "serveftp", "thruhere", "uk", "webhop", "za", "prod", "ssl", "a", "global", "a", "b", "global", "arts", "com", "firm", "info", "net", "other", "per", "rec", "store", "web", "com", "edu", "gov", "mil", "mobi", "name", "net", "org", "sch", "blogspot", "bv", "co", "aa", "aarborte", "aejrie", "afjord", "agdenes", "ah", "akershus", "aknoluokta", "akrehamn", "al", "alaheadju", "alesund", "algard", "alstahaug", "alta", "alvdal", "amli", "amot", "andasuolo", "andebu", "andoy", "ardal", "aremark", "arendal", "arna", "aseral", "asker", "askim", "askoy", "askvoll", "asnes", "audnedaln", "aukra", "aure", "aurland", "aurskog-holand", "austevoll", "austrheim", "averoy", "badaddja", "bahcavuotna", "bahccavuotna", "baidar", "bajddar", "balat", "balestrand", "ballangen", "balsfjord", "bamble", "bardu", "barum", "batsfjord", "bearalvahki", "beardu", "beiarn", "berg", "bergen", "berlevag", "bievat", "bindal", "birkenes", "bjarkoy", "bjerkreim", "bjugn", "blogspot", "bodo", "bokn", "bomlo", "bremanger", "bronnoy", "bronnoysund", "brumunddal", "bryne", "bu", "budejju", "buskerud", "bygland", "bykle", "cahcesuolo", "co", "davvenjarga", "davvesiida", "deatnu", "dep", "dielddanuorri", "divtasvuodna", "divttasvuotna", "donna", "dovre", "drammen", "drangedal", "drobak", "dyroy", "egersund", "eid", "eidfjord", "eidsberg", "eidskog", "eidsvoll", "eigersund", "elverum", "enebakk", "engerdal", "etne", "etnedal", "evenassi", "evenes", "evje-og-hornnes", "farsund", "fauske", "fedje", "fet", "fetsund", "fhs", "finnoy", "fitjar", "fjaler", "fjell", "fla", "flakstad", "flatanger", "flekkefjord", "flesberg", "flora", "floro", "fm", "folkebibl", "folldal", "forde", "forsand", "fosnes", "frana", "fredrikstad", "frei", "frogn", "froland", "frosta", "froya", "fuoisku", "fuossko", "fusa", "fylkesbibl", "fyresdal", "gaivuotna", "galsa", "gamvik", "gangaviika", "gaular", "gausdal", "giehtavuoatna", "gildeskal", "giske", "gjemnes", "gjerdrum", "gjerstad", "gjesdal", "gjovik", "gloppen", "gol", "gran", "grane", "granvin", "gratangen", "grimstad", "grong", "grue", "gulen", "guovdageaidnu", "ha", "habmer", "hadsel", "hagebostad", "halden", "halsa", "hamar", "hamaroy", "hammarfeasta", "hammerfest", "hapmir", "haram", "hareid", "harstad", "hasvik", "hattfjelldal", "haugesund", "hedmark", "hemne", "hemnes", "hemsedal", "herad", "hitra", "hjartdal", "hjelmeland", "hl", "hm", "hobol", "hof", "hokksund", "hol", "hole", "holmestrand", "holtalen", "honefoss", "hordaland", "hornindal", "horten", "hoyanger", "hoylandet", "hurdal", "hurum", "hvaler", "hyllestad", "ibestad", "idrett", "inderoy", "iveland", "ivgu", "jan-mayen", "jessheim", "jevnaker", "jolster", "jondal", "jorpeland", "kafjord", "karasjohka", "karasjok", "karlsoy", "karmoy", "kautokeino", "kirkenes", "klabu", "klepp", "kommune", "kongsberg", "kongsvinger", "kopervik", "kraanghke", "kragero", "kristiansand", "kristiansund", "krodsherad", "krokstadelva", "kvafjord", "kvalsund", "kvam", "kvanangen", "kvinesdal", "kvinnherad", "kviteseid", "kvitsoy", "laakesvuemie", "lahppi", "langevag", "lardal", "larvik", "lavagis", "lavangen", "leangaviika", "lebesby", "leikanger", "leirfjord", "leirvik", "leka", "leksvik", "lenvik", "lerdal", "lesja", "levanger", "lier", "lierne", "lillehammer", "lillesand", "lindas", "lindesnes", "loabat", "lodingen", "lom", "loppa", "lorenskog", "loten", "lund", "lunner", "luroy", "luster", "lyngdal", "lyngen", "malatvuopmi", "malselv", "malvik", "mandal", "marker", "marnardal", "masfjorden", "masoy", "matta-varjjat", "meland", "meldal", "melhus", "meloy", "meraker", "midsund", "midtre-gauldal", "mil", "mjondalen", "mo-i-rana", "moareke", "modalen", "modum", "molde", "more-og-romsdal", "mosjoen", "moskenes", "moss", "mosvik", "mr", "muosat", "museum", "naamesjevuemie", "namdalseid", "namsos", "namsskogan", "nannestad", "naroy", "narviika", "narvik", "naustdal", "navuotna", "nedre-eiker", "nesna", "nesodden", "nesoddtangen", "nesseby", "nesset", "nissedal", "nittedal", "nl", "nord-aurdal", "nord-fron", "nord-odal", "norddal", "nordkapp", "nordland", "nordre-land", "nordreisa", "nore-og-uvdal", "notodden", "notteroy", "nt", "odda", "of", "oksnes", "ol", "omasvuotna", "oppdal", "oppegard", "orkanger", "orkdal", "orland", "orskog", "orsta", "osen", "oslo", "osoyro", "osteroy", "ostfold", "ostre-toten", "overhalla", "ovre-eiker", "oyer", "oygarden", "oystre-slidre", "porsanger", "porsangu", "porsgrunn", "priv", "rade", "radoy", "rahkkeravju", "raholt", "raisa", "rakkestad", "ralingen", "rana", "randaberg", "rauma", "rendalen", "rennebu", "rennesoy", "rindal", "ringebu", "ringerike", "ringsaker", "risor", "rissa", "rl", "roan", "rodoy", "rollag", "romsa", "romskog", "roros", "rost", "royken", "royrvik", "ruovat", "rygge", "salangen", "salat", "saltdal", "samnanger", "sandefjord", "sandnes", "sandnessjoen", "sandoy", "sarpsborg", "sauda", "sauherad", "sel", "selbu", "selje", "seljord", "sf", "siellak", "sigdal", "siljan", "sirdal", "skanit", "skanland", "skaun", "skedsmo", "skedsmokorset", "ski", "skien", "skierva", "skiptvet", "skjak", "skjervoy", "skodje", "slattum", "smola", "snaase", "snasa", "snillfjord", "snoasa", "sogndal", "sogne", "sokndal", "sola", "solund", "somna", "sondre-land", "songdalen", "sor-aurdal", "sor-fron", "sor-odal", "sor-varanger", "sorfold", "sorreisa", "sortland", "sorum", "spjelkavik", "spydeberg", "st", "stange", "stat", "stathelle", "stavanger", "stavern", "steigen", "steinkjer", "stjordal", "stjordalshalsen", "stokke", "stor-elvdal", "stord", "stordal", "storfjord", "strand", "stranda", "stryn", "sula", "suldal", "sund", "sunndal", "surnadal", "svalbard", "sveio", "svelvik", "sykkylven", "tana", "tananger", "telemark", "time", "tingvoll", "tinn", "tjeldsund", "tjome", "tm", "tokke", "tolga", "tonsberg", "torsken", "tr", "trana", "tranby", "tranoy", "troandin", "trogstad", "tromsa", "tromso", "trondheim", "trysil", "tvedestrand", "tydal", "tynset", "tysfjord", "tysnes", "tysvar", "ullensaker", "ullensvang", "ulvik", "unjarga", "utsira", "va", "vaapste", "vadso", "vaga", "vagan", "vagsoy", "vaksdal", "valle", "vang", "vanylven", "vardo", "varggat", "varoy", "vefsn", "vega", "vegarshei", "vennesla", "verdal", "verran", "vestby", "vestfold", "vestnes", "vestre-slidre", "vestre-toten", "vestvagoy", "vevelstad", "vf", "vgs", "vik", "vikna", "vindafjord", "voagat", "volda", "voss", "vossevangen", "xn--andy-ira", "xn--asky-ira", "xn--aurskog-hland-jnb", "xn--avery-yua", "xn--bdddj-mrabd", "xn--bearalvhki-y4a", "xn--berlevg-jxa", "xn--bhcavuotna-s4a", "xn--bhccavuotna-k7a", "xn--bidr-5nac", "xn--bievt-0qa", "xn--bjarky-fya", "xn--bjddar-pta", "xn--blt-elab", "xn--bmlo-gra", "xn--bod-2na", "xn--brnny-wuac", "xn--brnnysund-m8ac", "xn--brum-voa", "xn--btsfjord-9za", "xn--davvenjrga-y4a", "xn--dnna-gra", "xn--drbak-wua", "xn--dyry-ira", "xn--eveni-0qa01ga", "xn--finny-yua", "xn--fjord-lra", "xn--fl-zia", "xn--flor-jra", "xn--frde-gra", "xn--frna-woa", "xn--frya-hra", "xn--ggaviika-8ya47h", "xn--gildeskl-g0a", "xn--givuotna-8ya", "xn--gjvik-wua", "xn--gls-elac", "xn--h-2fa", "xn--hbmer-xqa", "xn--hcesuolo-7ya35b", "xn--hgebostad-g3a", "xn--hmmrfeasta-s4ac", "xn--hnefoss-q1a", "xn--hobl-ira", "xn--holtlen-hxa", "xn--hpmir-xqa", "xn--hyanger-q1a", "xn--hylandet-54a", "xn--indery-fya", "xn--jlster-bya", "xn--jrpeland-54a", "xn--karmy-yua", "xn--kfjord-iua", "xn--klbu-woa", "xn--koluokta-7ya57h", "xn--krager-gya", "xn--kranghke-b0a", "xn--krdsherad-m8a", "xn--krehamn-dxa", "xn--krjohka-hwab49j", "xn--ksnes-uua", "xn--kvfjord-nxa", "xn--kvitsy-fya", "xn--kvnangen-k0a", "xn--l-1fa", "xn--laheadju-7ya", "xn--langevg-jxa", "xn--ldingen-q1a", "xn--leagaviika-52b", "xn--lesund-hua", "xn--lgrd-poac", "xn--lhppi-xqa", "xn--linds-pra", "xn--loabt-0qa", "xn--lrdal-sra", "xn--lrenskog-54a", "xn--lt-liac", "xn--lten-gra", "xn--lury-ira", "xn--mely-ira", "xn--merker-kua", "xn--mjndalen-64a", "xn--mlatvuopmi-s4a", "xn--mli-tla", "xn--mlselv-iua", "xn--moreke-jua", "xn--mosjen-eya", "xn--mot-tla", "xn--mre-og-romsdal-qqb", "xn--msy-ula0h", "xn--mtta-vrjjat-k7af", "xn--muost-0qa", "xn--nmesjevuemie-tcba", "xn--nry-yla5g", "xn--nttery-byae", "xn--nvuotna-hwa", "xn--oppegrd-ixa", "xn--ostery-fya", "xn--osyro-wua", "xn--porsgu-sta26f", "xn--rady-ira", "xn--rdal-poa", "xn--rde-ula", "xn--rdy-0nab", "xn--rennesy-v1a", "xn--rhkkervju-01af", "xn--rholt-mra", "xn--risa-5na", "xn--risr-ira", "xn--rland-uua", "xn--rlingen-mxa", "xn--rmskog-bya", "xn--rros-gra", "xn--rskog-uua", "xn--rst-0na", "xn--rsta-fra", "xn--ryken-vua", "xn--ryrvik-bya", "xn--s-1fa", "xn--sandnessjen-ogb", "xn--sandy-yua", "xn--seral-lra", "xn--sgne-gra", "xn--skierv-uta", "xn--skjervy-v1a", "xn--skjk-soa", "xn--sknit-yqa", "xn--sknland-fxa", "xn--slat-5na", "xn--slt-elab", "xn--smla-hra", "xn--smna-gra", "xn--snase-nra", "xn--sndre-land-0cb", "xn--snes-poa", "xn--snsa-roa", "xn--sr-aurdal-l8a", "xn--sr-fron-q1a", "xn--sr-odal-q1a", "xn--sr-varanger-ggb", "xn--srfold-bya", "xn--srreisa-q1a", "xn--srum-gra", "xn--stfold-9xa", "xn--stjrdal-s1a", "xn--stjrdalshalsen-sqb", "xn--stre-toten-zcb", "xn--tjme-hra", "xn--tnsberg-q1a", "xn--trany-yua", "xn--trgstad-r1a", "xn--trna-woa", "xn--troms-zua", "xn--tysvr-vra", "xn--unjrga-rta", "xn--vads-jra", "xn--vard-jra", "xn--vegrshei-c0a", "xn--vestvgy-ixa6o", "xn--vg-yiab", "xn--vgan-qoa", "xn--vgsy-qoa0j", "xn--vre-eiker-k8a", "xn--vrggt-xqad", "xn--vry-yla5g", "xn--yer-zna", "xn--ygarden-p1a", "xn--ystre-slidre-ujb", "gs", "gs", "nes", "gs", "nes", "gs", "os", "valer", "xn--vler-qoa", "gs", "gs", "os", "gs", "heroy", "sande", "gs", "gs", "bo", "heroy", "xn--b-5ga", "xn--hery-ira", "gs", "gs", "gs", "gs", "valer", "gs", "gs", "gs", "gs", "bo", "xn--b-5ga", "gs", "gs", "gs", "sande", "gs", "sande", "xn--hery-ira", "xn--vler-qoa", "biz", "com", "edu", "gov", "info", "net", "org", "merseine", "mine", "shacknet", "co", "blogspot", "co", "com", "edu", "gov", "med", "museum", "net", "org", "pro", "ae", "blogdns", "blogsite", "boldlygoingnowhere", "dnsalias", "dnsdojo", "doesntexist", "dontexist", "doomdns", "dvrdns", "dynalias", "dyndns", "endofinternet", "endoftheinternet", "from-me", "game-host", "gotdns", "hobby-site", "homedns", "homeftp", "homelinux", "homeunix", "is-a-bruinsfan", "is-a-candidate", "is-a-celticsfan", "is-a-chef", "is-a-geek", "is-a-knight", "is-a-linux-user", "is-a-patsfan", "is-a-soxfan", "is-found", "is-lost", "is-saved", "is-very-bad", "is-very-evil", "is-very-good", "is-very-nice", "is-very-sweet", "isa-geek", "kicks-ass", "misconfused", "podzone", "readmyblog", "selfip", "sellsyourhome", "servebbs", "serveftp", "servegame", "stuff-4-sale", "us", "webhop", "za", "go", "home", "abo", "ac", "com", "edu", "gob", "ing", "med", "net", "nom", "org", "sld", "com", "edu", "gob", "mil", "net", "nom", "org", "com", "edu", "org", "com", "edu", "gov", "i", "mil", "net", "ngo", "org", "biz", "com", "edu", "fam", "gob", "gok", "gon", "gop", "gos", "gov", "info", "net", "org", "web", "6bone", "agro", "aid", "art", "atm", "augustow", "auto", "babia-gora", "bedzin", "beskidy", "bialowieza", "bialystok", "bielawa", "bieszczady", "biz", "boleslawiec", "bydgoszcz", "bytom", "cieszyn", "co", "com", "czeladz", "czest", "dlugoleka", "edu", "elblag", "elk", "gda", "gdansk", "gdynia", "gliwice", "glogow", "gmina", "gniezno", "gorlice", "gov", "grajewo", "gsm", "ilawa", "info", "irc", "jaworzno", "jelenia-gora", "jgora", "kalisz", "karpacz", "kartuzy", "kaszuby", "katowice", "kazimierz-dolny", "kepno", "ketrzyn", "klodzko", "kobierzyce", "kolobrzeg", "konin", "konskowola", "krakow", "kutno", "lapy", "lebork", "legnica", "lezajsk", "limanowa", "lomza", "lowicz", "lubin", "lukow", "mail", "malbork", "malopolska", "mazowsze", "mazury", "mbone", "med", "media", "miasta", "mielec", "mielno", "mil", "mragowo", "naklo", "net", "ngo", "nieruchomosci", "nom", "nowaruda", "nysa", "olawa", "olecko", "olkusz", "olsztyn", "opoczno", "opole", "org", "ostroda", "ostroleka", "ostrowiec", "ostrowwlkp", "pc", "pila", "pisz", "podhale", "podlasie", "polkowice", "pomorskie", "pomorze", "powiat", "poznan", "priv", "prochowice", "pruszkow", "przeworsk", "pulawy", "radom", "rawa-maz", "realestate", "rel", "rybnik", "rzeszow", "sanok", "sejny", "sex", "shop", "siedlce", "sklep", "skoczow", "slask", "slupsk", "sopot", "sos", "sosnowiec", "stalowa-wola", "starachowice", "stargard", "suwalki", "swidnica", "swiebodzin", "swinoujscie", "szczecin", "szczytno", "szkola", "targi", "tarnobrzeg", "tgory", "tm", "tourism", "travel", "turek", "turystyka", "tychy", "usenet", "ustka", "walbrzych", "warmia", "warszawa", "waw", "wegrow", "wielun", "wlocl", "wloclawek", "wodzislaw", "wolomin", "wroc", "wroclaw", "zachpomor", "zagan", "zakopane", "zarow", "zgora", "zgorzelec", "pa", "po", "so", "sr", "starostwo", "ug", "um", "upow", "uw", "co", "edu", "gov", "net", "org", "ac", "biz", "com", "edu", "est", "gov", "info", "isla", "name", "net", "org", "pro", "prof", "aca", "bar", "cpa", "eng", "jur", "law", "med", "com", "edu", "gov", "net", "org", "plo", "sec", "blogspot", "com", "edu", "gov", "int", "net", "nome", "org", "publ", "belau", "co", "ed", "go", "ne", "or", "com", "coop", "edu", "gov", "mil", "net", "org", "com", "edu", "gov", "mil", "name", "net", "org", "sch", "asso", "blogspot", "com", "nom", "arts", "blogspot", "com", "firm", "info", "nom", "nt", "org", "rec", "store", "tm", "www", "ac", "co", "edu", "gov", "in", "org", "ac", "adygeya", "altai", "amur", "amursk", "arkhangelsk", "astrakhan", "baikal", "bashkiria", "belgorod", "bir", "bryansk", "buryatia", "cbg", "chel", "chelyabinsk", "chita", "chukotka", "chuvashia", "cmw", "com", "dagestan", "dudinka", "e-burg", "edu", "fareast", "gov", "grozny", "int", "irkutsk", "ivanovo", "izhevsk", "jamal", "jar", "joshkar-ola", "k-uralsk", "kalmykia", "kaluga", "kamchatka", "karelia", "kazan", "kchr", "kemerovo", "khabarovsk", "khakassia", "khv", "kirov", "kms", "koenig", "komi", "kostroma", "krasnoyarsk", "kuban", "kurgan", "kursk", "kustanai", "kuzbass", "lipetsk", "magadan", "magnitka", "mari", "mari-el", "marine", "mil", "mordovia", "mosreg", "msk", "murmansk", "mytis", "nakhodka", "nalchik", "net", "nkz", "nnov", "norilsk", "nov", "novosibirsk", "nsk", "omsk", "orenburg", "org", "oryol", "oskol", "palana", "penza", "perm", "pp", "pskov", "ptz", "pyatigorsk", "rnd", "rubtsovsk", "ryazan", "sakhalin", "samara", "saratov", "simbirsk", "smolensk", "snz", "spb", "stavropol", "stv", "surgut", "syzran", "tambov", "tatarstan", "test", "tom", "tomsk", "tsaritsyn", "tsk", "tula", "tuva", "tver", "tyumen", "udm", "udmurtia", "ulan-ude", "vdonsk", "vladikavkaz", "vladimir", "vladivostok", "volgograd", "vologda", "voronezh", "vrn", "vyatka", "yakutia", "yamal", "yaroslavl", "yekaterinburg", "yuzhno-sakhalinsk", "zgrad", "ac", "co", "com", "edu", "gouv", "gov", "int", "mil", "net", "com", "edu", "gov", "med", "net", "org", "pub", "sch", "com", "edu", "gov", "net", "org", "com", "edu", "gov", "net", "org", "com", "edu", "gov", "info", "med", "net", "org", "tv", "a", "ac", "b", "bd", "blogspot", "brand", "c", "d", "e", "f", "fh", "fhsk", "fhv", "g", "h", "i", "k", "komforb", "kommunalforbund", "komvux", "l", "lanbib", "m", "n", "naturbruksgymn", "o", "org", "p", "parti", "pp", "press", "r", "s", "sshn", "t", "tm", "u", "w", "x", "y", "z", "blogspot", "com", "edu", "gov", "net", "org", "per", "com", "gov", "mil", "net", "org", "blogspot", "com", "edu", "gov", "net", "org", "art", "com", "edu", "gouv", "org", "perso", "univ", "com", "net", "org", "co", "com", "consulado", "edu", "embaixada", "gov", "mil", "net", "org", "principe", "saotome", "store", "com", "edu", "gob", "org", "red", "gov", "com", "edu", "gov", "mil", "net", "org", "ac", "co", "org", "blogspot", "ac", "co", "go", "in", "mi", "net", "or", "ac", "biz", "co", "com", "edu", "go", "gov", "int", "mil", "name", "net", "nic", "org", "test", "web", "gov", "co", "com", "edu", "gov", "mil", "net", "nom", "org", "agrinet", "com", "defense", "edunet", "ens", "fin", "gov", "ind", "info", "intl", "mincom", "nat", "net", "org", "perso", "rnrt", "rns", "rnu", "tourism", "turen", "com", "edu", "gov", "mil", "net", "org", "nc", "nic", "gov", "aero", "biz", "co", "com", "coop", "edu", "gov", "info", "int", "jobs", "mobi", "museum", "name", "net", "org", "pro", "travel", "better-than", "dyndns", "on-the-web", "worse-than", "blogspot", "club", "com", "ebiz", "edu", "game", "gov", "idv", "mil", "net", "org", "xn--czrw28b", "xn--uc0atv", "xn--zf0ao64a", "ac", "co", "go", "hotel", "info", "me", "mil", "mobi", "ne", "or", "sc", "tv", "cherkassy", "cherkasy", "chernigov", "chernihiv", "chernivtsi", "chernovtsy", "ck", "cn", "co", "com", "cr", "crimea", "cv", "dn", "dnepropetrovsk", "dnipropetrovsk", "dominic", "donetsk", "dp", "edu", "gov", "if", "in", "ivano-frankivsk", "kh", "kharkiv", "kharkov", "kherson", "khmelnitskiy", "khmelnytskyi", "kiev", "kirovograd", "km", "kr", "krym", "ks", "kv", "kyiv", "lg", "lt", "lugansk", "lutsk", "lv", "lviv", "mk", "mykolaiv", "net", "nikolaev", "od", "odesa", "odessa", "org", "pl", "poltava", "pp", "rivne", "rovno", "rv", "sb", "sebastopol", "sevastopol", "sm", "sumy", "te", "ternopil", "uz", "uzhgorod", "vinnica", "vinnytsia", "vn", "volyn", "yalta", "zaporizhzhe", "zaporizhzhia", "zhitomir", "zhytomyr", "zp", "zt", "ac", "co", "com", "go", "ne", "or", "org", "sc", "bl", "british-library", "co", "jet", "mod", "national-library-scotland", "nel", "nic", "nls", "parliament", "sch", "blogspot", "ak", "al", "ar", "as", "az", "ca", "co", "ct", "dc", "de", "dni", "fed", "fl", "ga", "gu", "hi", "ia", "id", "il", "in", "is-by", "isa", "kids", "ks", "ky", "la", "land-4-sale", "ma", "md", "me", "mi", "mn", "mo", "ms", "mt", "nc", "nd", "ne", "nh", "nj", "nm", "nsn", "nv", "ny", "oh", "ok", "or", "pa", "pr", "ri", "sc", "sd", "stuff-4-sale", "tn", "tx", "ut", "va", "vi", "vt", "wa", "wi", "wv", "wy", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "chtr", "paroch", "pvt", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "k12", "lib", "cc", "cc", "k12", "lib", "com", "edu", "gub", "mil", "net", "org", "co", "com", "net", "org", "com", "edu", "gov", "mil", "net", "org", "co", "com", "e12", "edu", "gov", "info", "mil", "net", "org", "web", "co", "com", "k12", "net", "org", "ac", "biz", "com", "edu", "gov", "health", "info", "int", "name", "net", "org", "pro", "com", "dyndns", "edu", "gov", "mypets", "net", "org", } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/publicsuffix/list.go����������������������������������0000644�0000153�0000161�00000007363�12321735652�024650� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 publicsuffix provides a public suffix list based on data from // http://publicsuffix.org/. A public suffix is one under which Internet users // can directly register names. package publicsuffix // TODO: specify case sensitivity and leading/trailing dot behavior for // func PublicSuffix and func EffectiveTLDPlusOne. import ( "fmt" "net/http/cookiejar" "strings" ) // List implements the cookiejar.PublicSuffixList interface by calling the // PublicSuffix function. var List cookiejar.PublicSuffixList = list{} type list struct{} func (list) PublicSuffix(domain string) string { ps, _ := PublicSuffix(domain) return ps } func (list) String() string { return version } // PublicSuffix returns the public suffix of the domain using a copy of the // publicsuffix.org database compiled into the library. // // icann is whether the public suffix is managed by the Internet Corporation // for Assigned Names and Numbers. If not, the public suffix is privately // managed. For example, foo.org and foo.co.uk are ICANN domains, // foo.dyndns.org and foo.blogspot.co.uk are private domains. // // Use cases for distinguishing ICANN domains like foo.com from private // domains like foo.appspot.com can be found at // https://wiki.mozilla.org/Public_Suffix_List/Use_Cases func PublicSuffix(domain string) (publicSuffix string, icann bool) { lo, hi := uint32(0), uint32(numTLD) s, suffix, wildcard := domain, len(domain), false loop: for { dot := strings.LastIndex(s, ".") if wildcard { suffix = 1 + dot } if lo == hi { break } f := find(s[1+dot:], lo, hi) if f == notFound { break } u := nodes[f] >> (nodesBitsTextOffset + nodesBitsTextLength) icann = u&(1<<nodesBitsICANN-1) != 0 u >>= nodesBitsICANN u = children[u&(1<<nodesBitsChildren-1)] lo = u & (1<<childrenBitsLo - 1) u >>= childrenBitsLo hi = u & (1<<childrenBitsHi - 1) u >>= childrenBitsHi switch u & (1<<childrenBitsNodeType - 1) { case nodeTypeNormal: suffix = 1 + dot case nodeTypeException: suffix = 1 + len(s) break loop } u >>= childrenBitsNodeType wildcard = u&(1<<childrenBitsWildcard-1) != 0 if dot == -1 { break } s = s[:dot] } if suffix == len(domain) { // If no rules match, the prevailing rule is "*". return domain[1+strings.LastIndex(domain, "."):], icann } return domain[suffix:], icann } const notFound uint32 = 1<<32 - 1 // find returns the index of the node in the range [lo, hi) whose label equals // label, or notFound if there is no such node. The range is assumed to be in // strictly increasing node label order. func find(label string, lo, hi uint32) uint32 { for lo < hi { mid := lo + (hi-lo)/2 s := nodeLabel(mid) if s < label { lo = mid + 1 } else if s == label { return mid } else { hi = mid } } return notFound } // nodeLabel returns the label for the i'th node. func nodeLabel(i uint32) string { x := nodes[i] length := x & (1<<nodesBitsTextLength - 1) x >>= nodesBitsTextLength offset := x & (1<<nodesBitsTextOffset - 1) return text[offset : offset+length] } // EffectiveTLDPlusOne returns the effective top level domain plus one more // label. For example, the eTLD+1 for "foo.bar.golang.org" is "golang.org". func EffectiveTLDPlusOne(domain string) (string, error) { suffix, _ := PublicSuffix(domain) if len(domain) <= len(suffix) { return "", fmt.Errorf("publicsuffix: cannot derive eTLD+1 for domain %q", domain) } i := len(domain) - len(suffix) - 1 if domain[i] != '.' { return "", fmt.Errorf("publicsuffix: invalid public suffix %q for domain %q", suffix, domain) } return domain[1+strings.LastIndex(domain[:i], "."):], nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/spdy/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735652�021611� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/spdy/dictionary.go������������������������������������0000644�0000153�0000161�00000021453�12321735652�024312� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 spdy // headerDictionary is the dictionary sent to the zlib compressor/decompressor. var headerDictionary = []byte{ 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e, } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/spdy/write.go�����������������������������������������0000644�0000153�0000161�00000017665�12321735652�023311� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 spdy import ( "encoding/binary" "io" "net/http" "strings" ) func (frame *SynStreamFrame) write(f *Framer) error { return f.writeSynStreamFrame(frame) } func (frame *SynReplyFrame) write(f *Framer) error { return f.writeSynReplyFrame(frame) } func (frame *RstStreamFrame) write(f *Framer) (err error) { if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } frame.CFHeader.version = Version frame.CFHeader.frameType = TypeRstStream frame.CFHeader.Flags = 0 frame.CFHeader.length = 8 // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return } if frame.Status == 0 { return &Error{InvalidControlFrame, frame.StreamId} } if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { return } return } func (frame *SettingsFrame) write(f *Framer) (err error) { frame.CFHeader.version = Version frame.CFHeader.frameType = TypeSettings frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { return } for _, flagIdValue := range frame.FlagIdValues { flagId := uint32(flagIdValue.Flag)<<24 | uint32(flagIdValue.Id) if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil { return } } return } func (frame *PingFrame) write(f *Framer) (err error) { if frame.Id == 0 { return &Error{ZeroStreamId, 0} } frame.CFHeader.version = Version frame.CFHeader.frameType = TypePing frame.CFHeader.Flags = 0 frame.CFHeader.length = 4 // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil { return } return } func (frame *GoAwayFrame) write(f *Framer) (err error) { frame.CFHeader.version = Version frame.CFHeader.frameType = TypeGoAway frame.CFHeader.Flags = 0 frame.CFHeader.length = 8 // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { return } return nil } func (frame *HeadersFrame) write(f *Framer) error { return f.writeHeadersFrame(frame) } func (frame *WindowUpdateFrame) write(f *Framer) (err error) { frame.CFHeader.version = Version frame.CFHeader.frameType = TypeWindowUpdate frame.CFHeader.Flags = 0 frame.CFHeader.length = 8 // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.DeltaWindowSize); err != nil { return } return nil } func (frame *DataFrame) write(f *Framer) error { return f.writeDataFrame(frame) } // WriteFrame writes a frame. func (f *Framer) WriteFrame(frame Frame) error { return frame.write(f) } func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error { if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil { return err } if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil { return err } flagsAndLength := uint32(h.Flags)<<24 | h.length if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil { return err } return nil } func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) { n = 0 if err = binary.Write(w, binary.BigEndian, uint32(len(h))); err != nil { return } n += 2 for name, values := range h { if err = binary.Write(w, binary.BigEndian, uint32(len(name))); err != nil { return } n += 2 name = strings.ToLower(name) if _, err = io.WriteString(w, name); err != nil { return } n += len(name) v := strings.Join(values, headerValueSeparator) if err = binary.Write(w, binary.BigEndian, uint32(len(v))); err != nil { return } n += 2 if _, err = io.WriteString(w, v); err != nil { return } n += len(v) } return } func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) { if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } // Marshal the headers. var writer io.Writer = f.headerBuf if !f.headerCompressionDisabled { writer = f.headerCompressor } if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { return } if !f.headerCompressionDisabled { f.headerCompressor.Flush() } // Set ControlFrameHeader. frame.CFHeader.version = Version frame.CFHeader.frameType = TypeSynStream frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return err } if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return err } if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil { return err } if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<5); err != nil { return err } if err = binary.Write(f.w, binary.BigEndian, frame.Slot); err != nil { return err } if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { return err } f.headerBuf.Reset() return nil } func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) { if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } // Marshal the headers. var writer io.Writer = f.headerBuf if !f.headerCompressionDisabled { writer = f.headerCompressor } if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { return } if !f.headerCompressionDisabled { f.headerCompressor.Flush() } // Set ControlFrameHeader. frame.CFHeader.version = Version frame.CFHeader.frameType = TypeSynReply frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return } if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { return } f.headerBuf.Reset() return } func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) { if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } // Marshal the headers. var writer io.Writer = f.headerBuf if !f.headerCompressionDisabled { writer = f.headerCompressor } if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { return } if !f.headerCompressionDisabled { f.headerCompressor.Flush() } // Set ControlFrameHeader. frame.CFHeader.version = Version frame.CFHeader.frameType = TypeHeaders frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return } if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { return } f.headerBuf.Reset() return } func (f *Framer) writeDataFrame(frame *DataFrame) (err error) { if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } if frame.StreamId&0x80000000 != 0 || len(frame.Data) > MaxDataLength { return &Error{InvalidDataFrame, frame.StreamId} } // Serialize frame to Writer. if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return } flagsAndLength := uint32(frame.Flags)<<24 | uint32(len(frame.Data)) if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { return } if _, err = f.w.Write(frame.Data); err != nil { return } return nil } ���������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/spdy/spdy_test.go�������������������������������������0000644�0000153�0000161�00000041562�12321735652�024166� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 spdy import ( "bytes" "compress/zlib" "encoding/base64" "io" "io/ioutil" "net/http" "reflect" "testing" ) var HeadersFixture = http.Header{ "Url": []string{"http://www.google.com/"}, "Method": []string{"get"}, "Version": []string{"http/1.1"}, } func TestHeaderParsing(t *testing.T) { var headerValueBlockBuf bytes.Buffer writeHeaderValueBlock(&headerValueBlockBuf, HeadersFixture) const bogusStreamId = 1 newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId) if err != nil { t.Fatal("parseHeaderValueBlock:", err) } if !reflect.DeepEqual(HeadersFixture, newHeaders) { t.Fatal("got: ", newHeaders, "\nwant: ", HeadersFixture) } } func TestCreateParseSynStreamFrameCompressionDisable(t *testing.T) { buffer := new(bytes.Buffer) // Fixture framer for no compression test. framer := &Framer{ headerCompressionDisabled: true, w: buffer, headerBuf: new(bytes.Buffer), r: buffer, } synStreamFrame := SynStreamFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeSynStream, }, StreamId: 2, Headers: HeadersFixture, } if err := framer.WriteFrame(&synStreamFrame); err != nil { t.Fatal("WriteFrame without compression:", err) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame without compression:", err) } parsedSynStreamFrame, ok := frame.(*SynStreamFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) } } func TestCreateParseSynStreamFrameCompressionEnable(t *testing.T) { buffer := new(bytes.Buffer) framer, err := NewFramer(buffer, buffer) synStreamFrame := SynStreamFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeSynStream, }, StreamId: 2, Headers: HeadersFixture, } if err != nil { t.Fatal("Failed to create new framer:", err) } if err := framer.WriteFrame(&synStreamFrame); err != nil { t.Fatal("WriteFrame with compression:", err) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame with compression:", err) } parsedSynStreamFrame, ok := frame.(*SynStreamFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) } } func TestCreateParseSynReplyFrameCompressionDisable(t *testing.T) { buffer := new(bytes.Buffer) framer := &Framer{ headerCompressionDisabled: true, w: buffer, headerBuf: new(bytes.Buffer), r: buffer, } synReplyFrame := SynReplyFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeSynReply, }, StreamId: 2, Headers: HeadersFixture, } if err := framer.WriteFrame(&synReplyFrame); err != nil { t.Fatal("WriteFrame without compression:", err) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame without compression:", err) } parsedSynReplyFrame, ok := frame.(*SynReplyFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) } } func TestCreateParseSynReplyFrameCompressionEnable(t *testing.T) { buffer := new(bytes.Buffer) framer, err := NewFramer(buffer, buffer) synReplyFrame := SynReplyFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeSynReply, }, StreamId: 2, Headers: HeadersFixture, } if err != nil { t.Fatal("Failed to create new framer:", err) } if err := framer.WriteFrame(&synReplyFrame); err != nil { t.Fatal("WriteFrame with compression:", err) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame with compression:", err) } parsedSynReplyFrame, ok := frame.(*SynReplyFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) } } func TestCreateParseRstStream(t *testing.T) { buffer := new(bytes.Buffer) framer, err := NewFramer(buffer, buffer) if err != nil { t.Fatal("Failed to create new framer:", err) } rstStreamFrame := RstStreamFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeRstStream, }, StreamId: 1, Status: InvalidStream, } if err := framer.WriteFrame(&rstStreamFrame); err != nil { t.Fatal("WriteFrame:", err) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame:", err) } parsedRstStreamFrame, ok := frame.(*RstStreamFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if !reflect.DeepEqual(rstStreamFrame, *parsedRstStreamFrame) { t.Fatal("got: ", *parsedRstStreamFrame, "\nwant: ", rstStreamFrame) } } func TestCreateParseSettings(t *testing.T) { buffer := new(bytes.Buffer) framer, err := NewFramer(buffer, buffer) if err != nil { t.Fatal("Failed to create new framer:", err) } settingsFrame := SettingsFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeSettings, }, FlagIdValues: []SettingsFlagIdValue{ {FlagSettingsPersistValue, SettingsCurrentCwnd, 10}, {FlagSettingsPersisted, SettingsUploadBandwidth, 1}, }, } if err := framer.WriteFrame(&settingsFrame); err != nil { t.Fatal("WriteFrame:", err) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame:", err) } parsedSettingsFrame, ok := frame.(*SettingsFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if !reflect.DeepEqual(settingsFrame, *parsedSettingsFrame) { t.Fatal("got: ", *parsedSettingsFrame, "\nwant: ", settingsFrame) } } func TestCreateParsePing(t *testing.T) { buffer := new(bytes.Buffer) framer, err := NewFramer(buffer, buffer) if err != nil { t.Fatal("Failed to create new framer:", err) } pingFrame := PingFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypePing, }, Id: 31337, } if err := framer.WriteFrame(&pingFrame); err != nil { t.Fatal("WriteFrame:", err) } if pingFrame.CFHeader.Flags != 0 { t.Fatal("Incorrect frame type:", pingFrame) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame:", err) } parsedPingFrame, ok := frame.(*PingFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if parsedPingFrame.CFHeader.Flags != 0 { t.Fatal("Parsed incorrect frame type:", parsedPingFrame) } if !reflect.DeepEqual(pingFrame, *parsedPingFrame) { t.Fatal("got: ", *parsedPingFrame, "\nwant: ", pingFrame) } } func TestCreateParseGoAway(t *testing.T) { buffer := new(bytes.Buffer) framer, err := NewFramer(buffer, buffer) if err != nil { t.Fatal("Failed to create new framer:", err) } goAwayFrame := GoAwayFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeGoAway, }, LastGoodStreamId: 31337, Status: 1, } if err := framer.WriteFrame(&goAwayFrame); err != nil { t.Fatal("WriteFrame:", err) } if goAwayFrame.CFHeader.Flags != 0 { t.Fatal("Incorrect frame type:", goAwayFrame) } if goAwayFrame.CFHeader.length != 8 { t.Fatal("Incorrect frame type:", goAwayFrame) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame:", err) } parsedGoAwayFrame, ok := frame.(*GoAwayFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if parsedGoAwayFrame.CFHeader.Flags != 0 { t.Fatal("Incorrect frame type:", parsedGoAwayFrame) } if parsedGoAwayFrame.CFHeader.length != 8 { t.Fatal("Incorrect frame type:", parsedGoAwayFrame) } if !reflect.DeepEqual(goAwayFrame, *parsedGoAwayFrame) { t.Fatal("got: ", *parsedGoAwayFrame, "\nwant: ", goAwayFrame) } } func TestCreateParseHeadersFrame(t *testing.T) { buffer := new(bytes.Buffer) framer := &Framer{ headerCompressionDisabled: true, w: buffer, headerBuf: new(bytes.Buffer), r: buffer, } headersFrame := HeadersFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeHeaders, }, StreamId: 2, } headersFrame.Headers = HeadersFixture if err := framer.WriteFrame(&headersFrame); err != nil { t.Fatal("WriteFrame without compression:", err) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame without compression:", err) } parsedHeadersFrame, ok := frame.(*HeadersFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) } } func TestCreateParseHeadersFrameCompressionEnable(t *testing.T) { buffer := new(bytes.Buffer) headersFrame := HeadersFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeHeaders, }, StreamId: 2, } headersFrame.Headers = HeadersFixture framer, err := NewFramer(buffer, buffer) if err := framer.WriteFrame(&headersFrame); err != nil { t.Fatal("WriteFrame with compression:", err) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame with compression:", err) } parsedHeadersFrame, ok := frame.(*HeadersFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) } } func TestCreateParseWindowUpdateFrame(t *testing.T) { buffer := new(bytes.Buffer) framer, err := NewFramer(buffer, buffer) if err != nil { t.Fatal("Failed to create new framer:", err) } windowUpdateFrame := WindowUpdateFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeWindowUpdate, }, StreamId: 31337, DeltaWindowSize: 1, } if err := framer.WriteFrame(&windowUpdateFrame); err != nil { t.Fatal("WriteFrame:", err) } if windowUpdateFrame.CFHeader.Flags != 0 { t.Fatal("Incorrect frame type:", windowUpdateFrame) } if windowUpdateFrame.CFHeader.length != 8 { t.Fatal("Incorrect frame type:", windowUpdateFrame) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame:", err) } parsedWindowUpdateFrame, ok := frame.(*WindowUpdateFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if parsedWindowUpdateFrame.CFHeader.Flags != 0 { t.Fatal("Incorrect frame type:", parsedWindowUpdateFrame) } if parsedWindowUpdateFrame.CFHeader.length != 8 { t.Fatal("Incorrect frame type:", parsedWindowUpdateFrame) } if !reflect.DeepEqual(windowUpdateFrame, *parsedWindowUpdateFrame) { t.Fatal("got: ", *parsedWindowUpdateFrame, "\nwant: ", windowUpdateFrame) } } func TestCreateParseDataFrame(t *testing.T) { buffer := new(bytes.Buffer) framer, err := NewFramer(buffer, buffer) if err != nil { t.Fatal("Failed to create new framer:", err) } dataFrame := DataFrame{ StreamId: 1, Data: []byte{'h', 'e', 'l', 'l', 'o'}, } if err := framer.WriteFrame(&dataFrame); err != nil { t.Fatal("WriteFrame:", err) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame:", err) } parsedDataFrame, ok := frame.(*DataFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if !reflect.DeepEqual(dataFrame, *parsedDataFrame) { t.Fatal("got: ", *parsedDataFrame, "\nwant: ", dataFrame) } } func TestCompressionContextAcrossFrames(t *testing.T) { buffer := new(bytes.Buffer) framer, err := NewFramer(buffer, buffer) if err != nil { t.Fatal("Failed to create new framer:", err) } headersFrame := HeadersFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeHeaders, }, StreamId: 2, Headers: HeadersFixture, } if err := framer.WriteFrame(&headersFrame); err != nil { t.Fatal("WriteFrame (HEADERS):", err) } synStreamFrame := SynStreamFrame{ ControlFrameHeader{ Version, TypeSynStream, 0, // Flags 0, // length }, 2, // StreamId 0, // AssociatedTOStreamID 0, // Priority 1, // Slot nil, // Headers } synStreamFrame.Headers = HeadersFixture if err := framer.WriteFrame(&synStreamFrame); err != nil { t.Fatal("WriteFrame (SYN_STREAM):", err) } frame, err := framer.ReadFrame() if err != nil { t.Fatal("ReadFrame (HEADERS):", err, buffer.Bytes()) } parsedHeadersFrame, ok := frame.(*HeadersFrame) if !ok { t.Fatalf("expected HeadersFrame; got %T %v", frame, frame) } if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) } frame, err = framer.ReadFrame() if err != nil { t.Fatal("ReadFrame (SYN_STREAM):", err, buffer.Bytes()) } parsedSynStreamFrame, ok := frame.(*SynStreamFrame) if !ok { t.Fatalf("expected SynStreamFrame; got %T %v", frame, frame) } if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) } } func TestMultipleSPDYFrames(t *testing.T) { // Initialize the framers. pr1, pw1 := io.Pipe() pr2, pw2 := io.Pipe() writer, err := NewFramer(pw1, pr2) if err != nil { t.Fatal("Failed to create writer:", err) } reader, err := NewFramer(pw2, pr1) if err != nil { t.Fatal("Failed to create reader:", err) } // Set up the frames we're actually transferring. headersFrame := HeadersFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeHeaders, }, StreamId: 2, Headers: HeadersFixture, } synStreamFrame := SynStreamFrame{ CFHeader: ControlFrameHeader{ version: Version, frameType: TypeSynStream, }, StreamId: 2, Headers: HeadersFixture, } // Start the goroutines to write the frames. go func() { if err := writer.WriteFrame(&headersFrame); err != nil { t.Fatal("WriteFrame (HEADERS): ", err) } if err := writer.WriteFrame(&synStreamFrame); err != nil { t.Fatal("WriteFrame (SYN_STREAM): ", err) } }() // Read the frames and verify they look as expected. frame, err := reader.ReadFrame() if err != nil { t.Fatal("ReadFrame (HEADERS): ", err) } parsedHeadersFrame, ok := frame.(*HeadersFrame) if !ok { t.Fatal("Parsed incorrect frame type:", frame) } if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) } frame, err = reader.ReadFrame() if err != nil { t.Fatal("ReadFrame (SYN_STREAM):", err) } parsedSynStreamFrame, ok := frame.(*SynStreamFrame) if !ok { t.Fatal("Parsed incorrect frame type.") } if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) } } func TestReadMalformedZlibHeader(t *testing.T) { // These were constructed by corrupting the first byte of the zlib // header after writing. malformedStructs := map[string]string{ "SynStreamFrame": "gAIAAQAAABgAAAACAAAAAAAAF/nfolGyYmAAAAAA//8=", "SynReplyFrame": "gAIAAgAAABQAAAACAAAX+d+iUbJiYAAAAAD//w==", "HeadersFrame": "gAIACAAAABQAAAACAAAX+d+iUbJiYAAAAAD//w==", } for name, bad := range malformedStructs { b, err := base64.StdEncoding.DecodeString(bad) if err != nil { t.Errorf("Unable to decode base64 encoded frame %s: %v", name, err) } buf := bytes.NewBuffer(b) reader, err := NewFramer(buf, buf) if err != nil { t.Fatalf("NewFramer: %v", err) } _, err = reader.ReadFrame() if err != zlib.ErrHeader { t.Errorf("Frame %s, expected: %#v, actual: %#v", name, zlib.ErrHeader, err) } } } // TODO: these tests are too weak for updating SPDY spec. Fix me. type zeroStream struct { frame Frame encoded string } var streamIdZeroFrames = map[string]zeroStream{ "SynStreamFrame": { &SynStreamFrame{StreamId: 0}, "gAIAAQAAABgAAAAAAAAAAAAAePnfolGyYmAAAAAA//8=", }, "SynReplyFrame": { &SynReplyFrame{StreamId: 0}, "gAIAAgAAABQAAAAAAAB4+d+iUbJiYAAAAAD//w==", }, "RstStreamFrame": { &RstStreamFrame{StreamId: 0}, "gAIAAwAAAAgAAAAAAAAAAA==", }, "HeadersFrame": { &HeadersFrame{StreamId: 0}, "gAIACAAAABQAAAAAAAB4+d+iUbJiYAAAAAD//w==", }, "DataFrame": { &DataFrame{StreamId: 0}, "AAAAAAAAAAA=", }, "PingFrame": { &PingFrame{Id: 0}, "gAIABgAAAAQAAAAA", }, } func TestNoZeroStreamId(t *testing.T) { t.Log("skipping") // TODO: update to work with SPDY3 return for name, f := range streamIdZeroFrames { b, err := base64.StdEncoding.DecodeString(f.encoded) if err != nil { t.Errorf("Unable to decode base64 encoded frame %s: %v", f, err) continue } framer, err := NewFramer(ioutil.Discard, bytes.NewReader(b)) if err != nil { t.Fatalf("NewFramer: %v", err) } err = framer.WriteFrame(f.frame) checkZeroStreamId(t, name, "WriteFrame", err) _, err = framer.ReadFrame() checkZeroStreamId(t, name, "ReadFrame", err) } } func checkZeroStreamId(t *testing.T, frame string, method string, err error) { if err == nil { t.Errorf("%s ZeroStreamId, no error on %s", method, frame) return } eerr, ok := err.(*Error) if !ok || eerr.Err != ZeroStreamId { t.Errorf("%s ZeroStreamId, incorrect error %#v, frame %s", method, eerr, frame) } } ����������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/spdy/read.go������������������������������������������0000644�0000153�0000161�00000023101�12321735652�023050� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 spdy import ( "compress/zlib" "encoding/binary" "io" "net/http" "strings" ) func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) error { return f.readSynStreamFrame(h, frame) } func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) error { return f.readSynReplyFrame(h, frame) } func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) error { frame.CFHeader = h if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { return err } if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { return err } if frame.Status == 0 { return &Error{InvalidControlFrame, frame.StreamId} } if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } return nil } func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error { frame.CFHeader = h var numSettings uint32 if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { return err } frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) for i := uint32(0); i < numSettings; i++ { if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { return err } frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24) frame.FlagIdValues[i].Id &= 0xffffff if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil { return err } } return nil } func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) error { frame.CFHeader = h if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil { return err } if frame.Id == 0 { return &Error{ZeroStreamId, 0} } if frame.CFHeader.Flags != 0 { return &Error{InvalidControlFrame, StreamId(frame.Id)} } return nil } func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) error { frame.CFHeader = h if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil { return err } if frame.CFHeader.Flags != 0 { return &Error{InvalidControlFrame, frame.LastGoodStreamId} } if frame.CFHeader.length != 8 { return &Error{InvalidControlFrame, frame.LastGoodStreamId} } if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { return err } return nil } func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) error { return f.readHeadersFrame(h, frame) } func (frame *WindowUpdateFrame) read(h ControlFrameHeader, f *Framer) error { frame.CFHeader = h if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { return err } if frame.CFHeader.Flags != 0 { return &Error{InvalidControlFrame, frame.StreamId} } if frame.CFHeader.length != 8 { return &Error{InvalidControlFrame, frame.StreamId} } if err := binary.Read(f.r, binary.BigEndian, &frame.DeltaWindowSize); err != nil { return err } return nil } func newControlFrame(frameType ControlFrameType) (controlFrame, error) { ctor, ok := cframeCtor[frameType] if !ok { return nil, &Error{Err: InvalidControlFrame} } return ctor(), nil } var cframeCtor = map[ControlFrameType]func() controlFrame{ TypeSynStream: func() controlFrame { return new(SynStreamFrame) }, TypeSynReply: func() controlFrame { return new(SynReplyFrame) }, TypeRstStream: func() controlFrame { return new(RstStreamFrame) }, TypeSettings: func() controlFrame { return new(SettingsFrame) }, TypePing: func() controlFrame { return new(PingFrame) }, TypeGoAway: func() controlFrame { return new(GoAwayFrame) }, TypeHeaders: func() controlFrame { return new(HeadersFrame) }, TypeWindowUpdate: func() controlFrame { return new(WindowUpdateFrame) }, } func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) error { if f.headerDecompressor != nil { f.headerReader.N = payloadSize return nil } f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(headerDictionary)) if err != nil { return err } f.headerDecompressor = decompressor return nil } // ReadFrame reads SPDY encoded data and returns a decompressed Frame. func (f *Framer) ReadFrame() (Frame, error) { var firstWord uint32 if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil { return nil, err } if firstWord&0x80000000 != 0 { frameType := ControlFrameType(firstWord & 0xffff) version := uint16(firstWord >> 16 & 0x7fff) return f.parseControlFrame(version, frameType) } return f.parseDataFrame(StreamId(firstWord & 0x7fffffff)) } func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, error) { var length uint32 if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { return nil, err } flags := ControlFlags((length & 0xff000000) >> 24) length &= 0xffffff header := ControlFrameHeader{version, frameType, flags, length} cframe, err := newControlFrame(frameType) if err != nil { return nil, err } if err = cframe.read(header, f); err != nil { return nil, err } return cframe, nil } func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) { var numHeaders uint32 if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { return nil, err } var e error h := make(http.Header, int(numHeaders)) for i := 0; i < int(numHeaders); i++ { var length uint32 if err := binary.Read(r, binary.BigEndian, &length); err != nil { return nil, err } nameBytes := make([]byte, length) if _, err := io.ReadFull(r, nameBytes); err != nil { return nil, err } name := string(nameBytes) if name != strings.ToLower(name) { e = &Error{UnlowercasedHeaderName, streamId} name = strings.ToLower(name) } if h[name] != nil { e = &Error{DuplicateHeaders, streamId} } if err := binary.Read(r, binary.BigEndian, &length); err != nil { return nil, err } value := make([]byte, length) if _, err := io.ReadFull(r, value); err != nil { return nil, err } valueList := strings.Split(string(value), headerValueSeparator) for _, v := range valueList { h.Add(name, v) } } if e != nil { return h, e } return h, nil } func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) error { frame.CFHeader = h var err error if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { return err } if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil { return err } if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil { return err } frame.Priority >>= 5 if err = binary.Read(f.r, binary.BigEndian, &frame.Slot); err != nil { return err } reader := f.r if !f.headerCompressionDisabled { err := f.uncorkHeaderDecompressor(int64(h.length - 10)) if err != nil { return err } reader = f.headerDecompressor } frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { err = &Error{WrongCompressedPayloadSize, 0} } if err != nil { return err } for h := range frame.Headers { if invalidReqHeaders[h] { return &Error{InvalidHeaderPresent, frame.StreamId} } } if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } return nil } func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) error { frame.CFHeader = h var err error if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { return err } reader := f.r if !f.headerCompressionDisabled { err := f.uncorkHeaderDecompressor(int64(h.length - 4)) if err != nil { return err } reader = f.headerDecompressor } frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { err = &Error{WrongCompressedPayloadSize, 0} } if err != nil { return err } for h := range frame.Headers { if invalidRespHeaders[h] { return &Error{InvalidHeaderPresent, frame.StreamId} } } if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } return nil } func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) error { frame.CFHeader = h var err error if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { return err } reader := f.r if !f.headerCompressionDisabled { err := f.uncorkHeaderDecompressor(int64(h.length - 4)) if err != nil { return err } reader = f.headerDecompressor } frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { err = &Error{WrongCompressedPayloadSize, 0} } if err != nil { return err } var invalidHeaders map[string]bool if frame.StreamId%2 == 0 { invalidHeaders = invalidReqHeaders } else { invalidHeaders = invalidRespHeaders } for h := range frame.Headers { if invalidHeaders[h] { return &Error{InvalidHeaderPresent, frame.StreamId} } } if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } return nil } func (f *Framer) parseDataFrame(streamId StreamId) (*DataFrame, error) { var length uint32 if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { return nil, err } var frame DataFrame frame.StreamId = streamId frame.Flags = DataFlags(length >> 24) length &= 0xffffff frame.Data = make([]byte, length) if _, err := io.ReadFull(f.r, frame.Data); err != nil { return nil, err } if frame.StreamId == 0 { return nil, &Error{ZeroStreamId, 0} } return &frame, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/spdy/types.go�����������������������������������������0000644�0000153�0000161�00000017430�12321735652�023311� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 spdy implements the SPDY protocol (currently SPDY/3), described in // http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3. package spdy import ( "bytes" "compress/zlib" "io" "net/http" ) // Version is the protocol version number that this package implements. const Version = 3 // ControlFrameType stores the type field in a control frame header. type ControlFrameType uint16 const ( TypeSynStream ControlFrameType = 0x0001 TypeSynReply = 0x0002 TypeRstStream = 0x0003 TypeSettings = 0x0004 TypePing = 0x0006 TypeGoAway = 0x0007 TypeHeaders = 0x0008 TypeWindowUpdate = 0x0009 ) // ControlFlags are the flags that can be set on a control frame. type ControlFlags uint8 const ( ControlFlagFin ControlFlags = 0x01 ControlFlagUnidirectional = 0x02 ControlFlagSettingsClearSettings = 0x01 ) // DataFlags are the flags that can be set on a data frame. type DataFlags uint8 const ( DataFlagFin DataFlags = 0x01 ) // MaxDataLength is the maximum number of bytes that can be stored in one frame. const MaxDataLength = 1<<24 - 1 // headerValueSepator separates multiple header values. const headerValueSeparator = "\x00" // Frame is a single SPDY frame in its unpacked in-memory representation. Use // Framer to read and write it. type Frame interface { write(f *Framer) error } // ControlFrameHeader contains all the fields in a control frame header, // in its unpacked in-memory representation. type ControlFrameHeader struct { // Note, high bit is the "Control" bit. version uint16 // spdy version number frameType ControlFrameType Flags ControlFlags length uint32 // length of data field } type controlFrame interface { Frame read(h ControlFrameHeader, f *Framer) error } // StreamId represents a 31-bit value identifying the stream. type StreamId uint32 // SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM // frame. type SynStreamFrame struct { CFHeader ControlFrameHeader StreamId StreamId AssociatedToStreamId StreamId // stream id for a stream which this stream is associated to Priority uint8 // priority of this frame (3-bit) Slot uint8 // index in the server's credential vector of the client certificate Headers http.Header } // SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame. type SynReplyFrame struct { CFHeader ControlFrameHeader StreamId StreamId Headers http.Header } // RstStreamStatus represents the status that led to a RST_STREAM. type RstStreamStatus uint32 const ( ProtocolError RstStreamStatus = iota + 1 InvalidStream RefusedStream UnsupportedVersion Cancel InternalError FlowControlError StreamInUse StreamAlreadyClosed InvalidCredentials FrameTooLarge ) // RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM // frame. type RstStreamFrame struct { CFHeader ControlFrameHeader StreamId StreamId Status RstStreamStatus } // SettingsFlag represents a flag in a SETTINGS frame. type SettingsFlag uint8 const ( FlagSettingsPersistValue SettingsFlag = 0x1 FlagSettingsPersisted = 0x2 ) // SettingsFlag represents the id of an id/value pair in a SETTINGS frame. type SettingsId uint32 const ( SettingsUploadBandwidth SettingsId = iota + 1 SettingsDownloadBandwidth SettingsRoundTripTime SettingsMaxConcurrentStreams SettingsCurrentCwnd SettingsDownloadRetransRate SettingsInitialWindowSize SettingsClientCretificateVectorSize ) // SettingsFlagIdValue is the unpacked, in-memory representation of the // combined flag/id/value for a setting in a SETTINGS frame. type SettingsFlagIdValue struct { Flag SettingsFlag Id SettingsId Value uint32 } // SettingsFrame is the unpacked, in-memory representation of a SPDY // SETTINGS frame. type SettingsFrame struct { CFHeader ControlFrameHeader FlagIdValues []SettingsFlagIdValue } // PingFrame is the unpacked, in-memory representation of a PING frame. type PingFrame struct { CFHeader ControlFrameHeader Id uint32 // unique id for this ping, from server is even, from client is odd. } // GoAwayStatus represents the status in a GoAwayFrame. type GoAwayStatus uint32 const ( GoAwayOK GoAwayStatus = iota GoAwayProtocolError GoAwayInternalError ) // GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame. type GoAwayFrame struct { CFHeader ControlFrameHeader LastGoodStreamId StreamId // last stream id which was accepted by sender Status GoAwayStatus } // HeadersFrame is the unpacked, in-memory representation of a HEADERS frame. type HeadersFrame struct { CFHeader ControlFrameHeader StreamId StreamId Headers http.Header } // WindowUpdateFrame is the unpacked, in-memory representation of a // WINDOW_UPDATE frame. type WindowUpdateFrame struct { CFHeader ControlFrameHeader StreamId StreamId DeltaWindowSize uint32 // additional number of bytes to existing window size } // TODO: Implement credential frame and related methods. // DataFrame is the unpacked, in-memory representation of a DATA frame. type DataFrame struct { // Note, high bit is the "Control" bit. Should be 0 for data frames. StreamId StreamId Flags DataFlags Data []byte // payload data of this frame } // A SPDY specific error. type ErrorCode string const ( UnlowercasedHeaderName ErrorCode = "header was not lowercased" DuplicateHeaders = "multiple headers with same name" WrongCompressedPayloadSize = "compressed payload size was incorrect" UnknownFrameType = "unknown frame type" InvalidControlFrame = "invalid control frame" InvalidDataFrame = "invalid data frame" InvalidHeaderPresent = "frame contained invalid header" ZeroStreamId = "stream id zero is disallowed" ) // Error contains both the type of error and additional values. StreamId is 0 // if Error is not associated with a stream. type Error struct { Err ErrorCode StreamId StreamId } func (e *Error) Error() string { return string(e.Err) } var invalidReqHeaders = map[string]bool{ "Connection": true, "Host": true, "Keep-Alive": true, "Proxy-Connection": true, "Transfer-Encoding": true, } var invalidRespHeaders = map[string]bool{ "Connection": true, "Keep-Alive": true, "Proxy-Connection": true, "Transfer-Encoding": true, } // Framer handles serializing/deserializing SPDY frames, including compressing/ // decompressing payloads. type Framer struct { headerCompressionDisabled bool w io.Writer headerBuf *bytes.Buffer headerCompressor *zlib.Writer r io.Reader headerReader io.LimitedReader headerDecompressor io.ReadCloser } // NewFramer allocates a new Framer for a given SPDY connection, repesented by // a io.Writer and io.Reader. Note that Framer will read and write individual fields // from/to the Reader and Writer, so the caller should pass in an appropriately // buffered implementation to optimize performance. func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { compressBuf := new(bytes.Buffer) compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(headerDictionary)) if err != nil { return nil, err } framer := &Framer{ w: w, headerBuf: compressBuf, headerCompressor: compressor, r: r, } return framer, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/netutil/����������������������������������������������0000755�0000153�0000161�00000000000�12321735652�022316� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/netutil/listen_test.go��������������������������������0000644�0000153�0000161�00000002517�12321735652�025207� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/netutil/listen.go�������������������������������������0000644�0000153�0000161�00000002044�12321735652�024143� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/AUTHORS�����������������������������������������������0000644�0000153�0000161�00000000255�12321735652�021704� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This source code refers to The Go Authors for copyright purposes. # The master list of authors is in the main Go distribution, # visible at http://tip.golang.org/AUTHORS. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/CONTRIBUTORS������������������������������������������0000644�0000153�0000161�00000000252�12321735652�022511� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This source code was written by the Go contributors. # The master list of contributors is in the main Go distribution, # visible at http://tip.golang.org/CONTRIBUTORS. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/websocket/��������������������������������������������0000755�0000153�0000161�00000000000�12321735652�022620� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/websocket/examplehandler_test.go����������������������0000644�0000153�0000161�00000001117�12321735652�027177� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 websocket_test import ( "io" "net/http" "code.google.com/p/go.net/websocket" ) // Echo the data received on the WebSocket. func EchoServer(ws *websocket.Conn) { io.Copy(ws, ws) } // This example demonstrates a trivial echo server. func ExampleHandler() { http.Handle("/echo", websocket.Handler(EchoServer)) err := http.ListenAndServe(":12345", nil) if err != nil { panic("ListenAndServe: " + err.Error()) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/websocket/websocket_test.go���������������������������0000644�0000153�0000161�00000017157�12321735652�026207� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package websocket import ( "bytes" "fmt" "io" "log" "net" "net/http" "net/http/httptest" "net/url" "strings" "sync" "testing" ) var serverAddr string var once sync.Once func echoServer(ws *Conn) { io.Copy(ws, ws) } type Count struct { S string N int } func countServer(ws *Conn) { for { var count Count err := JSON.Receive(ws, &count) if err != nil { return } count.N++ count.S = strings.Repeat(count.S, count.N) err = JSON.Send(ws, count) if err != nil { return } } } func subProtocolHandshake(config *Config, req *http.Request) error { for _, proto := range config.Protocol { if proto == "chat" { config.Protocol = []string{proto} return nil } } return ErrBadWebSocketProtocol } func subProtoServer(ws *Conn) { for _, proto := range ws.Config().Protocol { io.WriteString(ws, proto) } } func startServer() { http.Handle("/echo", Handler(echoServer)) http.Handle("/count", Handler(countServer)) subproto := Server{ Handshake: subProtocolHandshake, Handler: Handler(subProtoServer), } http.Handle("/subproto", subproto) server := httptest.NewServer(nil) serverAddr = server.Listener.Addr().String() log.Print("Test WebSocket server listening on ", serverAddr) } func newConfig(t *testing.T, path string) *Config { config, _ := NewConfig(fmt.Sprintf("ws://%s%s", serverAddr, path), "http://localhost") return config } func TestEcho(t *testing.T) { once.Do(startServer) // websocket.Dial() client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } conn, err := NewClient(newConfig(t, "/echo"), client) if err != nil { t.Errorf("WebSocket handshake error: %v", err) return } msg := []byte("hello, world\n") if _, err := conn.Write(msg); err != nil { t.Errorf("Write: %v", err) } var actual_msg = make([]byte, 512) n, err := conn.Read(actual_msg) if err != nil { t.Errorf("Read: %v", err) } actual_msg = actual_msg[0:n] if !bytes.Equal(msg, actual_msg) { t.Errorf("Echo: expected %q got %q", msg, actual_msg) } conn.Close() } func TestAddr(t *testing.T) { once.Do(startServer) // websocket.Dial() client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } conn, err := NewClient(newConfig(t, "/echo"), client) if err != nil { t.Errorf("WebSocket handshake error: %v", err) return } ra := conn.RemoteAddr().String() if !strings.HasPrefix(ra, "ws://") || !strings.HasSuffix(ra, "/echo") { t.Errorf("Bad remote addr: %v", ra) } la := conn.LocalAddr().String() if !strings.HasPrefix(la, "http://") { t.Errorf("Bad local addr: %v", la) } conn.Close() } func TestCount(t *testing.T) { once.Do(startServer) // websocket.Dial() client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } conn, err := NewClient(newConfig(t, "/count"), client) if err != nil { t.Errorf("WebSocket handshake error: %v", err) return } var count Count count.S = "hello" if err := JSON.Send(conn, count); err != nil { t.Errorf("Write: %v", err) } if err := JSON.Receive(conn, &count); err != nil { t.Errorf("Read: %v", err) } if count.N != 1 { t.Errorf("count: expected %d got %d", 1, count.N) } if count.S != "hello" { t.Errorf("count: expected %q got %q", "hello", count.S) } if err := JSON.Send(conn, count); err != nil { t.Errorf("Write: %v", err) } if err := JSON.Receive(conn, &count); err != nil { t.Errorf("Read: %v", err) } if count.N != 2 { t.Errorf("count: expected %d got %d", 2, count.N) } if count.S != "hellohello" { t.Errorf("count: expected %q got %q", "hellohello", count.S) } conn.Close() } func TestWithQuery(t *testing.T) { once.Do(startServer) client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } config := newConfig(t, "/echo") config.Location, err = url.ParseRequestURI(fmt.Sprintf("ws://%s/echo?q=v", serverAddr)) if err != nil { t.Fatal("location url", err) } ws, err := NewClient(config, client) if err != nil { t.Errorf("WebSocket handshake: %v", err) return } ws.Close() } func testWithProtocol(t *testing.T, subproto []string) (string, error) { once.Do(startServer) client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } config := newConfig(t, "/subproto") config.Protocol = subproto ws, err := NewClient(config, client) if err != nil { return "", err } msg := make([]byte, 16) n, err := ws.Read(msg) if err != nil { return "", err } ws.Close() return string(msg[:n]), nil } func TestWithProtocol(t *testing.T) { proto, err := testWithProtocol(t, []string{"chat"}) if err != nil { t.Errorf("SubProto: unexpected error: %v", err) } if proto != "chat" { t.Errorf("SubProto: expected %q, got %q", "chat", proto) } } func TestWithTwoProtocol(t *testing.T) { proto, err := testWithProtocol(t, []string{"test", "chat"}) if err != nil { t.Errorf("SubProto: unexpected error: %v", err) } if proto != "chat" { t.Errorf("SubProto: expected %q, got %q", "chat", proto) } } func TestWithBadProtocol(t *testing.T) { _, err := testWithProtocol(t, []string{"test"}) if err != ErrBadStatus { t.Errorf("SubProto: expected %v, got %v", ErrBadStatus, err) } } func TestHTTP(t *testing.T) { once.Do(startServer) // If the client did not send a handshake that matches the protocol // specification, the server MUST return an HTTP response with an // appropriate error code (such as 400 Bad Request) resp, err := http.Get(fmt.Sprintf("http://%s/echo", serverAddr)) if err != nil { t.Errorf("Get: error %#v", err) return } if resp == nil { t.Error("Get: resp is null") return } if resp.StatusCode != http.StatusBadRequest { t.Errorf("Get: expected %q got %q", http.StatusBadRequest, resp.StatusCode) } } func TestTrailingSpaces(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=955 // The last runs of this create keys with trailing spaces that should not be // generated by the client. once.Do(startServer) config := newConfig(t, "/echo") for i := 0; i < 30; i++ { // body ws, err := DialConfig(config) if err != nil { t.Errorf("Dial #%d failed: %v", i, err) break } ws.Close() } } 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. once.Do(startServer) // websocket.Dial() client, err := net.Dial("tcp", serverAddr) if err != nil { t.Fatal("dialing", err) } conn, err := NewClient(newConfig(t, "/echo"), client) if err != nil { t.Errorf("WebSocket handshake error: %v", err) return } msg := []byte("hello, world\n") if _, err := conn.Write(msg); err != nil { t.Errorf("Write: %v", err) } var small_msg = make([]byte, 8) n, err := conn.Read(small_msg) if err != nil { t.Errorf("Read: %v", err) } if !bytes.Equal(msg[:len(small_msg)], small_msg) { t.Errorf("Echo: expected %q got %q", msg[:len(small_msg)], small_msg) } var second_msg = make([]byte, len(msg)) n, err = conn.Read(second_msg) if err != nil { t.Errorf("Read: %v", err) } second_msg = second_msg[0:n] if !bytes.Equal(msg[len(small_msg):], second_msg) { t.Errorf("Echo: expected %q got %q", msg[len(small_msg):], second_msg) } conn.Close() } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/websocket/server.go�����������������������������������0000644�0000153�0000161�00000006623�12321735652�024464� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package websocket import ( "bufio" "fmt" "io" "net/http" ) func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) { var hs serverHandshaker = &hybiServerHandshaker{Config: config} code, err := hs.ReadHandshake(buf.Reader, req) if err == ErrBadWebSocketVersion { fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) buf.WriteString("\r\n") buf.WriteString(err.Error()) buf.Flush() return } 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()) buf.Flush() return } if handshake != nil { err = handshake(config, req) if err != nil { code = http.StatusForbidden fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) buf.WriteString("\r\n") buf.Flush() return } } err = hs.AcceptHandshake(buf.Writer) if err != nil { code = http.StatusBadRequest fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) buf.WriteString("\r\n") buf.Flush() return } conn = hs.NewServerConn(buf, rwc, req) return } // Server represents a server of a WebSocket. type Server struct { // Config is a WebSocket configuration for new WebSocket connection. Config // Handshake is an optional function in WebSocket handshake. // For example, you can check, or don't check Origin header. // Another example, you can select config.Protocol. Handshake func(*Config, *http.Request) error // Handler handles a WebSocket connection. Handler } // ServeHTTP implements the http.Handler interface for a WebSocket func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { s.serveWebSocket(w, req) } func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) { rwc, buf, err := w.(http.Hijacker).Hijack() if err != nil { panic("Hijack failed: " + err.Error()) return } // The server should abort the WebSocket connection if it finds // the client did not send a handshake that matches with protocol // specification. defer rwc.Close() conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake) if err != nil { return } if conn == nil { panic("unexpected nil conn") } s.Handler(conn) } // Handler is a simple interface to a WebSocket browser client. // It checks if Origin header is valid URL by default. // You might want to verify websocket.Conn.Config().Origin in the func. // If you use Server instead of Handler, you could call websocket.Origin and // check the origin in your Handshake func. So, if you want to accept // non-browser client, which doesn't send Origin header, you could use Server //. that doesn't check origin in its Handshake. type Handler func(*Conn) func checkOrigin(config *Config, req *http.Request) (err error) { config.Origin, err = Origin(config, req) if err == nil && config.Origin == nil { return fmt.Errorf("null origin") } return err } // ServeHTTP implements the http.Handler interface for a WebSocket func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { s := Server{Handler: h, Handshake: checkOrigin} s.serveWebSocket(w, req) } �������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/websocket/hybi_test.go��������������������������������0000644�0000153�0000161�00000043207�12321735652�025147� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 getNonceAccept function with values in // http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 func TestSecWebSocketAccept(t *testing.T) { nonce := []byte("dGhlIHNhbXBsZSBub25jZQ==") expected := []byte("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") accept, err := getNonceAccept(nonce) if err != nil { t.Errorf("getNonceAccept: returned error %v", err) return } if !bytes.Equal(expected, accept) { t.Errorf("getNonceAccept: expected %q got %q", expected, accept) } } func TestHybiClientHandshake(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 = ProtocolVersionHybi13 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 /chat, 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 server.example.com, but got %v", req.Host) } var expectedHeader = map[string]string{ "Connection": "Upgrade", "Upgrade": "websocket", "Sec-Websocket-Key": config.handshakeData["key"], "Origin": config.Origin.String(), "Sec-Websocket-Protocol": "chat, superchat", "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13), } 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 TestHybiClientHandshakeWithHeader(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 = ProtocolVersionHybi13 config.Header = http.Header(make(map[string][]string)) config.Header.Add("User-Agent", "test") 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 /chat, 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 server.example.com, but got %v", req.Host) } var expectedHeader = map[string]string{ "Connection": "Upgrade", "Upgrade": "websocket", "Sec-Websocket-Key": config.handshakeData["key"], "Origin": config.Origin.String(), "Sec-Websocket-Protocol": "chat, superchat", "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13), "User-Agent": "test", } 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} br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 `)) 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) } expectedProtocols := []string{"chat", "superchat"} if fmt.Sprintf("%v", config.Protocol) != fmt.Sprintf("%v", expectedProtocols) { t.Errorf("protocol expected %q but got %q", expectedProtocols, config.Protocol) } b := bytes.NewBuffer([]byte{}) bw := bufio.NewWriter(b) config.Protocol = config.Protocol[:1] 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()) } } func TestHybiServerHandshakeNoSubProtocol(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== Origin: http://example.com Sec-WebSocket-Version: 13 `)) 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) } if len(config.Protocol) != 0 { t.Errorf("len(config.Protocol) expected 0, but got %q", len(config.Protocol)) } 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 Switching Protocols", "Upgrade: websocket", "Connection: Upgrade", "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", "", ""}, "\r\n") if b.String() != expectedResponse { t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) } } func TestHybiServerHandshakeHybiBadVersion(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: 9 `)) req, err := http.ReadRequest(br) if err != nil { t.Fatal("request", err) } code, err := handshaker.ReadHandshake(br, req) if err != ErrBadWebSocketVersion { t.Errorf("handshake expected err %q but got %q", ErrBadWebSocketVersion, err) } if code != http.StatusBadRequest { t.Errorf("status expected %q but got %q", http.StatusBadRequest, code) } } func testHybiFrame(t *testing.T, testHeader, testPayload, testMaskedPayload []byte, frameHeader *hybiFrameHeader) { b := bytes.NewBuffer([]byte{}) frameWriterFactory := &hybiFrameWriterFactory{bufio.NewWriter(b), false} w, _ := frameWriterFactory.NewFrameWriter(TextFrame) w.(*hybiFrameWriter).header = frameHeader _, err := w.Write(testPayload) w.Close() if err != nil { t.Errorf("Write error %q", err) } var expectedFrame []byte expectedFrame = append(expectedFrame, testHeader...) expectedFrame = append(expectedFrame, testMaskedPayload...) if !bytes.Equal(expectedFrame, b.Bytes()) { t.Errorf("frame expected %q got %q", expectedFrame, b.Bytes()) } frameReaderFactory := &hybiFrameReaderFactory{bufio.NewReader(b)} r, err := frameReaderFactory.NewFrameReader() if err != nil { t.Errorf("Read error %q", err) } if header := r.HeaderReader(); header == nil { t.Errorf("no header") } else { actualHeader := make([]byte, r.Len()) n, err := header.Read(actualHeader) if err != nil { t.Errorf("Read header error %q", err) } else { if n < len(testHeader) { t.Errorf("header too short %q got %q", testHeader, actualHeader[:n]) } if !bytes.Equal(testHeader, actualHeader[:n]) { t.Errorf("header expected %q got %q", testHeader, actualHeader[:n]) } } } if trailer := r.TrailerReader(); trailer != nil { t.Errorf("unexpected trailer %q", trailer) } frame := r.(*hybiFrameReader) if frameHeader.Fin != frame.header.Fin || frameHeader.OpCode != frame.header.OpCode || len(testPayload) != int(frame.header.Length) { t.Errorf("mismatch %v (%d) vs %v", frameHeader, len(testPayload), frame) } payload := make([]byte, len(testPayload)) _, err = r.Read(payload) if err != nil { t.Errorf("read %v", err) } if !bytes.Equal(testPayload, payload) { t.Errorf("payload %q vs %q", testPayload, payload) } } func TestHybiShortTextFrame(t *testing.T) { frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} payload := []byte("hello") testHybiFrame(t, []byte{0x81, 0x05}, payload, payload, frameHeader) payload = make([]byte, 125) testHybiFrame(t, []byte{0x81, 125}, payload, payload, frameHeader) } func TestHybiShortMaskedTextFrame(t *testing.T) { frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame, MaskingKey: []byte{0xcc, 0x55, 0x80, 0x20}} payload := []byte("hello") maskedPayload := []byte{0xa4, 0x30, 0xec, 0x4c, 0xa3} header := []byte{0x81, 0x85} header = append(header, frameHeader.MaskingKey...) testHybiFrame(t, header, payload, maskedPayload, frameHeader) } func TestHybiShortBinaryFrame(t *testing.T) { frameHeader := &hybiFrameHeader{Fin: true, OpCode: BinaryFrame} payload := []byte("hello") testHybiFrame(t, []byte{0x82, 0x05}, payload, payload, frameHeader) payload = make([]byte, 125) testHybiFrame(t, []byte{0x82, 125}, payload, payload, frameHeader) } func TestHybiControlFrame(t *testing.T) { frameHeader := &hybiFrameHeader{Fin: true, OpCode: PingFrame} payload := []byte("hello") testHybiFrame(t, []byte{0x89, 0x05}, payload, payload, frameHeader) frameHeader = &hybiFrameHeader{Fin: true, OpCode: PongFrame} testHybiFrame(t, []byte{0x8A, 0x05}, payload, payload, frameHeader) frameHeader = &hybiFrameHeader{Fin: true, OpCode: CloseFrame} payload = []byte{0x03, 0xe8} // 1000 testHybiFrame(t, []byte{0x88, 0x02}, payload, payload, frameHeader) } func TestHybiLongFrame(t *testing.T) { frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} payload := make([]byte, 126) testHybiFrame(t, []byte{0x81, 126, 0x00, 126}, payload, payload, frameHeader) payload = make([]byte, 65535) testHybiFrame(t, []byte{0x81, 126, 0xff, 0xff}, payload, payload, frameHeader) payload = make([]byte, 65536) testHybiFrame(t, []byte{0x81, 127, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}, payload, payload, frameHeader) } func TestHybiClientRead(t *testing.T) { wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} br := bufio.NewReader(bytes.NewBuffer(wireData)) bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) msg := make([]byte, 512) n, err := conn.Read(msg) if err != nil { t.Errorf("read 1st frame, error %q", err) } if n != 5 { t.Errorf("read 1st frame, expect 5, got %d", n) } if !bytes.Equal(wireData[2:7], msg[:n]) { t.Errorf("read 1st frame %v, got %v", wireData[2:7], msg[:n]) } n, err = conn.Read(msg) if err != nil { t.Errorf("read 2nd frame, error %q", err) } if n != 5 { t.Errorf("read 2nd frame, expect 5, got %d", n) } if !bytes.Equal(wireData[16:21], msg[:n]) { t.Errorf("read 2nd frame %v, got %v", wireData[16:21], msg[:n]) } n, err = conn.Read(msg) if err == nil { t.Errorf("read not EOF") } if n != 0 { t.Errorf("expect read 0, got %d", n) } } func TestHybiShortRead(t *testing.T) { wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} br := bufio.NewReader(bytes.NewBuffer(wireData)) bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) step := 0 pos := 0 expectedPos := []int{2, 5, 16, 19} expectedLen := []int{3, 2, 3, 2} for { msg := make([]byte, 3) n, err := conn.Read(msg) if step >= len(expectedPos) { if err == nil { t.Errorf("read not EOF") } if n != 0 { t.Errorf("expect read 0, got %d", n) } return } pos = expectedPos[step] endPos := pos + expectedLen[step] if err != nil { t.Errorf("read from %d, got error %q", pos, err) return } if n != endPos-pos { t.Errorf("read from %d, expect %d, got %d", pos, endPos-pos, n) } if !bytes.Equal(wireData[pos:endPos], msg[:n]) { t.Errorf("read from %d, frame %v, got %v", pos, wireData[pos:endPos], msg[:n]) } step++ } } func TestHybiServerRead(t *testing.T) { wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello 0x89, 0x85, 0xcc, 0x55, 0x80, 0x20, 0xa4, 0x30, 0xec, 0x4c, 0xa3, // ping: hello 0x81, 0x85, 0xed, 0x83, 0xb4, 0x24, 0x9a, 0xec, 0xc6, 0x48, 0x89, // world } br := bufio.NewReader(bytes.NewBuffer(wireData)) bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) expected := [][]byte{[]byte("hello"), []byte("world")} msg := make([]byte, 512) n, err := conn.Read(msg) if err != nil { t.Errorf("read 1st frame, error %q", err) } if n != 5 { t.Errorf("read 1st frame, expect 5, got %d", n) } if !bytes.Equal(expected[0], msg[:n]) { t.Errorf("read 1st frame %q, got %q", expected[0], msg[:n]) } n, err = conn.Read(msg) if err != nil { t.Errorf("read 2nd frame, error %q", err) } if n != 5 { t.Errorf("read 2nd frame, expect 5, got %d", n) } if !bytes.Equal(expected[1], msg[:n]) { t.Errorf("read 2nd frame %q, got %q", expected[1], msg[:n]) } n, err = conn.Read(msg) if err == nil { t.Errorf("read not EOF") } if n != 0 { t.Errorf("expect read 0, got %d", n) } } func TestHybiServerReadWithoutMasking(t *testing.T) { wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o'} br := bufio.NewReader(bytes.NewBuffer(wireData)) bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) // server MUST close the connection upon receiving a non-masked frame. msg := make([]byte, 512) _, err := conn.Read(msg) if err != io.EOF { t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) } } func TestHybiClientReadWithMasking(t *testing.T) { wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello } br := bufio.NewReader(bytes.NewBuffer(wireData)) bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) // client MUST close the connection upon receiving a masked frame. msg := make([]byte, 512) _, err := conn.Read(msg) if err != io.EOF { t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) } } // Test the hybiServerHandshaker supports firefox implementation and // checks Connection request header include (but it's not necessary // equal to) "upgrade" func TestHybiServerFirefoxHandshake(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: keep-alive, upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 `)) 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()) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/websocket/websocket.go��������������������������������0000644�0000153�0000161�00000024470�12321735652�025144� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // Package websocket implements a client and server for the WebSocket protocol // as specified in RFC 6455. package websocket import ( "bufio" "crypto/tls" "encoding/json" "errors" "io" "io/ioutil" "net" "net/http" "net/url" "sync" "time" ) const ( ProtocolVersionHybi13 = 13 ProtocolVersionHybi = ProtocolVersionHybi13 SupportedProtocolVersion = "13" ContinuationFrame = 0 TextFrame = 1 BinaryFrame = 2 CloseFrame = 8 PingFrame = 9 PongFrame = 10 UnknownFrame = 255 ) // ProtocolError represents WebSocket protocol errors. type ProtocolError struct { ErrorString string } func (err *ProtocolError) Error() string { return err.ErrorString } var ( ErrBadProtocolVersion = &ProtocolError{"bad protocol version"} ErrBadScheme = &ProtocolError{"bad scheme"} ErrBadStatus = &ProtocolError{"bad status"} ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"} ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"} ErrBadFrame = &ProtocolError{"bad frame"} ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"} ErrNotWebSocket = &ProtocolError{"not websocket protocol"} ErrBadRequestMethod = &ProtocolError{"bad method"} ErrNotSupported = &ProtocolError{"not supported"} ) // Addr is an implementation of net.Addr for WebSocket. type Addr struct { *url.URL } // Network returns the network type for a WebSocket, "websocket". func (addr *Addr) Network() string { return "websocket" } // Config is a WebSocket configuration type Config struct { // A WebSocket server address. Location *url.URL // A Websocket client origin. Origin *url.URL // WebSocket subprotocols. Protocol []string // WebSocket protocol version. Version int // TLS config for secure WebSocket (wss). TlsConfig *tls.Config // Additional header fields to be sent in WebSocket opening handshake. Header http.Header handshakeData map[string]string } // serverHandshaker is an interface to handle WebSocket server side handshake. type serverHandshaker interface { // ReadHandshake reads handshake request message from client. // Returns http response code and error if any. ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) // AcceptHandshake accepts the client handshake request and sends // handshake response back to client. AcceptHandshake(buf *bufio.Writer) (err error) // NewServerConn creates a new WebSocket connection. NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) } // frameReader is an interface to read a WebSocket frame. type frameReader interface { // Reader is to read payload of the frame. io.Reader // PayloadType returns payload type. PayloadType() byte // HeaderReader returns a reader to read header of the frame. HeaderReader() io.Reader // TrailerReader returns a reader to read trailer of the frame. // If it returns nil, there is no trailer in the frame. TrailerReader() io.Reader // Len returns total length of the frame, including header and trailer. Len() int } // frameReaderFactory is an interface to creates new frame reader. type frameReaderFactory interface { NewFrameReader() (r frameReader, err error) } // frameWriter is an interface to write a WebSocket frame. type frameWriter interface { // Writer is to write playload of the frame. io.WriteCloser } // frameWriterFactory is an interface to create new frame writer. type frameWriterFactory interface { NewFrameWriter(payloadType byte) (w frameWriter, err error) } type frameHandler interface { HandleFrame(frame frameReader) (r frameReader, err error) WriteClose(status int) (err error) } // Conn represents a WebSocket connection. type Conn struct { config *Config request *http.Request buf *bufio.ReadWriter rwc io.ReadWriteCloser rio sync.Mutex frameReaderFactory frameReader wio sync.Mutex frameWriterFactory frameHandler PayloadType byte defaultCloseStatus int } // Read implements the io.Reader interface: // it reads data of a frame from the WebSocket connection. // if msg is not large enough for the frame data, it fills the msg and next Read // will read the rest of the frame data. // it reads Text frame or Binary frame. func (ws *Conn) Read(msg []byte) (n int, err error) { ws.rio.Lock() defer ws.rio.Unlock() again: if ws.frameReader == nil { frame, err := ws.frameReaderFactory.NewFrameReader() if err != nil { return 0, err } ws.frameReader, err = ws.frameHandler.HandleFrame(frame) if err != nil { return 0, err } if ws.frameReader == nil { goto again } } n, err = ws.frameReader.Read(msg) if err == io.EOF { if trailer := ws.frameReader.TrailerReader(); trailer != nil { io.Copy(ioutil.Discard, trailer) } ws.frameReader = nil goto again } return n, err } // Write implements the io.Writer interface: // it writes data as a frame to the WebSocket connection. func (ws *Conn) Write(msg []byte) (n int, err error) { ws.wio.Lock() defer ws.wio.Unlock() w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) if err != nil { return 0, err } n, err = w.Write(msg) w.Close() if err != nil { return n, err } return n, err } // Close implements the io.Closer interface. func (ws *Conn) Close() error { err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) if err != nil { return err } return ws.rwc.Close() } func (ws *Conn) IsClientConn() bool { return ws.request == nil } func (ws *Conn) IsServerConn() bool { return ws.request != nil } // LocalAddr returns the WebSocket Origin for the connection for client, or // the WebSocket location for server. func (ws *Conn) LocalAddr() net.Addr { if ws.IsClientConn() { return &Addr{ws.config.Origin} } return &Addr{ws.config.Location} } // RemoteAddr returns the WebSocket location for the connection for client, or // the Websocket Origin for server. func (ws *Conn) RemoteAddr() net.Addr { if ws.IsClientConn() { return &Addr{ws.config.Location} } return &Addr{ws.config.Origin} } var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn") // SetDeadline sets the connection's network read & write deadlines. func (ws *Conn) SetDeadline(t time.Time) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetDeadline(t) } return errSetDeadline } // SetReadDeadline sets the connection's network read deadline. func (ws *Conn) SetReadDeadline(t time.Time) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetReadDeadline(t) } return errSetDeadline } // SetWriteDeadline sets the connection's network write deadline. func (ws *Conn) SetWriteDeadline(t time.Time) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetWriteDeadline(t) } return errSetDeadline } // Config returns the WebSocket config. func (ws *Conn) Config() *Config { return ws.config } // Request returns the http request upgraded to the WebSocket. // It is nil for client side. func (ws *Conn) Request() *http.Request { return ws.request } // Codec represents a symmetric pair of functions that implement a codec. type Codec struct { Marshal func(v interface{}) (data []byte, payloadType byte, err error) Unmarshal func(data []byte, payloadType byte, v interface{}) (err error) } // Send sends v marshaled by cd.Marshal as single frame to ws. func (cd Codec) Send(ws *Conn, v interface{}) (err error) { data, payloadType, err := cd.Marshal(v) if err != nil { return err } ws.wio.Lock() defer ws.wio.Unlock() w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) if err != nil { return err } _, err = w.Write(data) w.Close() return err } // Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v. func (cd Codec) Receive(ws *Conn, v interface{}) (err error) { ws.rio.Lock() defer ws.rio.Unlock() if ws.frameReader != nil { _, err = io.Copy(ioutil.Discard, ws.frameReader) if err != nil { return err } ws.frameReader = nil } again: frame, err := ws.frameReaderFactory.NewFrameReader() if err != nil { return err } frame, err = ws.frameHandler.HandleFrame(frame) if err != nil { return err } if frame == nil { goto again } payloadType := frame.PayloadType() data, err := ioutil.ReadAll(frame) if err != nil { return err } return cd.Unmarshal(data, payloadType, v) } func marshal(v interface{}) (msg []byte, payloadType byte, err error) { switch data := v.(type) { case string: return []byte(data), TextFrame, nil case []byte: return data, BinaryFrame, nil } return nil, UnknownFrame, ErrNotSupported } func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) { switch data := v.(type) { case *string: *data = string(msg) return nil case *[]byte: *data = msg return nil } return ErrNotSupported } /* Message is a codec to send/receive text/binary data in a frame on WebSocket connection. To send/receive text frame, use string type. To send/receive binary frame, use []byte type. Trivial usage: import "websocket" // receive text frame var message string websocket.Message.Receive(ws, &message) // send text frame message = "hello" websocket.Message.Send(ws, message) // receive binary frame var data []byte websocket.Message.Receive(ws, &data) // send binary frame data = []byte{0, 1, 2} websocket.Message.Send(ws, data) */ var Message = Codec{marshal, unmarshal} func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { msg, err = json.Marshal(v) return msg, TextFrame, err } func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { return json.Unmarshal(msg, v) } /* JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. Trivial usage: import "websocket" type T struct { Msg string Count int } // receive JSON type T var data T websocket.JSON.Receive(ws, &data) // send JSON type T websocket.JSON.Send(ws, data) */ var JSON = Codec{jsonMarshal, jsonUnmarshal} ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/websocket/client.go�����������������������������������0000644�0000153�0000161�00000004334�12321735652�024431� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package websocket import ( "bufio" "crypto/tls" "io" "net" "net/http" "net/url" ) // DialError is an error that occurs while dialling a websocket server. type DialError struct { *Config Err error } func (e *DialError) Error() string { return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() } // NewConfig creates a new WebSocket config for client connection. func NewConfig(server, origin string) (config *Config, err error) { config = new(Config) config.Version = ProtocolVersionHybi13 config.Location, err = url.ParseRequestURI(server) if err != nil { return } config.Origin, err = url.ParseRequestURI(origin) if err != nil { return } config.Header = http.Header(make(map[string][]string)) return } // NewClient creates a new WebSocket client connection over rwc. func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) { br := bufio.NewReader(rwc) bw := bufio.NewWriter(rwc) err = hybiClientHandshake(config, br, bw) if err != nil { return } buf := bufio.NewReadWriter(br, bw) ws = newHybiClientConn(config, buf, rwc) return } // Dial opens a new client connection to a WebSocket. func Dial(url_, protocol, origin string) (ws *Conn, err error) { config, err := NewConfig(url_, origin) if err != nil { return nil, err } if protocol != "" { config.Protocol = []string{protocol} } return DialConfig(config) } // DialConfig opens a new client connection to a WebSocket with a config. func DialConfig(config *Config) (ws *Conn, err error) { var client net.Conn if config.Location == nil { return nil, &DialError{config, ErrBadWebSocketLocation} } if config.Origin == nil { return nil, &DialError{config, ErrBadWebSocketOrigin} } switch config.Location.Scheme { case "ws": client, err = net.Dial("tcp", config.Location.Host) case "wss": client, err = tls.Dial("tcp", config.Location.Host, config.TlsConfig) default: err = ErrBadScheme } if err != nil { goto Error } ws, err = NewClient(config, client) if err != nil { goto Error } return Error: return nil, &DialError{config, err} } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/websocket/hybi.go�������������������������������������0000644�0000153�0000161�00000036032�12321735652�024106� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 // This file implements a protocol of hybi draft. // http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 import ( "bufio" "bytes" "crypto/rand" "crypto/sha1" "encoding/base64" "encoding/binary" "fmt" "io" "io/ioutil" "net/http" "net/url" "strings" ) const ( websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" closeStatusNormal = 1000 closeStatusGoingAway = 1001 closeStatusProtocolError = 1002 closeStatusUnsupportedData = 1003 closeStatusFrameTooLarge = 1004 closeStatusNoStatusRcvd = 1005 closeStatusAbnormalClosure = 1006 closeStatusBadMessageData = 1007 closeStatusPolicyViolation = 1008 closeStatusTooBigData = 1009 closeStatusExtensionMismatch = 1010 maxControlFramePayloadLength = 125 ) var ( ErrBadMaskingKey = &ProtocolError{"bad masking key"} ErrBadPongMessage = &ProtocolError{"bad pong message"} ErrBadClosingStatus = &ProtocolError{"bad closing status"} ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"} ErrNotImplemented = &ProtocolError{"not implemented"} handshakeHeader = map[string]bool{ "Host": true, "Upgrade": true, "Connection": true, "Sec-Websocket-Key": true, "Sec-Websocket-Origin": true, "Sec-Websocket-Version": true, "Sec-Websocket-Protocol": true, "Sec-Websocket-Accept": true, } ) // A hybiFrameHeader is a frame header as defined in hybi draft. type hybiFrameHeader struct { Fin bool Rsv [3]bool OpCode byte Length int64 MaskingKey []byte data *bytes.Buffer } // A hybiFrameReader is a reader for hybi frame. type hybiFrameReader struct { reader io.Reader header hybiFrameHeader pos int64 length int } func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) { n, err = frame.reader.Read(msg) if err != nil { return 0, err } if frame.header.MaskingKey != nil { for i := 0; i < n; i++ { msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4] frame.pos++ } } return n, err } func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode } func (frame *hybiFrameReader) HeaderReader() io.Reader { if frame.header.data == nil { return nil } if frame.header.data.Len() == 0 { return nil } return frame.header.data } func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil } func (frame *hybiFrameReader) Len() (n int) { return frame.length } // A hybiFrameReaderFactory creates new frame reader based on its frame type. type hybiFrameReaderFactory struct { *bufio.Reader } // NewFrameReader reads a frame header from the connection, and creates new reader for the frame. // See Section 5.2 Base Framing protocol for detail. // http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2 func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) { hybiFrame := new(hybiFrameReader) frame = hybiFrame var header []byte var b byte // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits) b, err = buf.ReadByte() if err != nil { return } header = append(header, b) hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0 for i := 0; i < 3; i++ { j := uint(6 - i) hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0 } hybiFrame.header.OpCode = header[0] & 0x0f // Second byte. Mask/Payload len(7bits) b, err = buf.ReadByte() if err != nil { return } header = append(header, b) mask := (b & 0x80) != 0 b &= 0x7f lengthFields := 0 switch { case b <= 125: // Payload length 7bits. hybiFrame.header.Length = int64(b) case b == 126: // Payload length 7+16bits lengthFields = 2 case b == 127: // Payload length 7+64bits lengthFields = 8 } for i := 0; i < lengthFields; i++ { b, err = buf.ReadByte() if err != nil { return } header = append(header, b) hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b) } if mask { // Masking key. 4 bytes. for i := 0; i < 4; i++ { b, err = buf.ReadByte() if err != nil { return } header = append(header, b) hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b) } } hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length) hybiFrame.header.data = bytes.NewBuffer(header) hybiFrame.length = len(header) + int(hybiFrame.header.Length) return } // A HybiFrameWriter is a writer for hybi frame. type hybiFrameWriter struct { writer *bufio.Writer header *hybiFrameHeader } func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) { var header []byte var b byte if frame.header.Fin { b |= 0x80 } for i := 0; i < 3; i++ { if frame.header.Rsv[i] { j := uint(6 - i) b |= 1 << j } } b |= frame.header.OpCode header = append(header, b) if frame.header.MaskingKey != nil { b = 0x80 } else { b = 0 } lengthFields := 0 length := len(msg) switch { case length <= 125: b |= byte(length) case length < 65536: b |= 126 lengthFields = 2 default: b |= 127 lengthFields = 8 } header = append(header, b) for i := 0; i < lengthFields; i++ { j := uint((lengthFields - i - 1) * 8) b = byte((length >> j) & 0xff) header = append(header, b) } if frame.header.MaskingKey != nil { if len(frame.header.MaskingKey) != 4 { return 0, ErrBadMaskingKey } header = append(header, frame.header.MaskingKey...) frame.writer.Write(header) data := make([]byte, length) for i := range data { data[i] = msg[i] ^ frame.header.MaskingKey[i%4] } frame.writer.Write(data) err = frame.writer.Flush() return length, err } frame.writer.Write(header) frame.writer.Write(msg) err = frame.writer.Flush() return length, err } func (frame *hybiFrameWriter) Close() error { return nil } type hybiFrameWriterFactory struct { *bufio.Writer needMaskingKey bool } func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) { frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType} if buf.needMaskingKey { frameHeader.MaskingKey, err = generateMaskingKey() if err != nil { return nil, err } } return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil } type hybiFrameHandler struct { conn *Conn payloadType byte } func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) { if handler.conn.IsServerConn() { // The client MUST mask all frames sent to the server. if frame.(*hybiFrameReader).header.MaskingKey == nil { handler.WriteClose(closeStatusProtocolError) return nil, io.EOF } } else { // The server MUST NOT mask all frames. if frame.(*hybiFrameReader).header.MaskingKey != nil { handler.WriteClose(closeStatusProtocolError) return nil, io.EOF } } if header := frame.HeaderReader(); header != nil { io.Copy(ioutil.Discard, header) } switch frame.PayloadType() { case ContinuationFrame: frame.(*hybiFrameReader).header.OpCode = handler.payloadType case TextFrame, BinaryFrame: handler.payloadType = frame.PayloadType() case CloseFrame: return nil, io.EOF case PingFrame: pingMsg := make([]byte, maxControlFramePayloadLength) n, err := io.ReadFull(frame, pingMsg) if err != nil && err != io.ErrUnexpectedEOF { return nil, err } io.Copy(ioutil.Discard, frame) n, err = handler.WritePong(pingMsg[:n]) if err != nil { return nil, err } return nil, nil case PongFrame: return nil, ErrNotImplemented } return frame, nil } func (handler *hybiFrameHandler) WriteClose(status int) (err error) { handler.conn.wio.Lock() defer handler.conn.wio.Unlock() w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame) if err != nil { return err } msg := make([]byte, 2) binary.BigEndian.PutUint16(msg, uint16(status)) _, err = w.Write(msg) w.Close() return err } func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) { handler.conn.wio.Lock() defer handler.conn.wio.Unlock() w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame) if err != nil { return 0, err } n, err = w.Write(msg) w.Close() return n, err } // newHybiConn creates a new WebSocket connection speaking hybi draft protocol. func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { if buf == nil { br := bufio.NewReader(rwc) bw := bufio.NewWriter(rwc) buf = bufio.NewReadWriter(br, bw) } ws := &Conn{config: config, request: request, buf: buf, rwc: rwc, frameReaderFactory: hybiFrameReaderFactory{buf.Reader}, frameWriterFactory: hybiFrameWriterFactory{ buf.Writer, request == nil}, PayloadType: TextFrame, defaultCloseStatus: closeStatusNormal} ws.frameHandler = &hybiFrameHandler{conn: ws} return ws } // generateMaskingKey generates a masking key for a frame. func generateMaskingKey() (maskingKey []byte, err error) { maskingKey = make([]byte, 4) if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil { return } return } // generateNonce generates a nonce consisting of a randomly selected 16-byte // value that has been base64-encoded. func generateNonce() (nonce []byte) { key := make([]byte, 16) if _, err := io.ReadFull(rand.Reader, key); err != nil { panic(err) } nonce = make([]byte, 24) base64.StdEncoding.Encode(nonce, key) return } // getNonceAccept computes the base64-encoded SHA-1 of the concatenation of // the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string. func getNonceAccept(nonce []byte) (expected []byte, err error) { h := sha1.New() if _, err = h.Write(nonce); err != nil { return } if _, err = h.Write([]byte(websocketGUID)); err != nil { return } expected = make([]byte, 28) base64.StdEncoding.Encode(expected, h.Sum(nil)) return } // Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17 func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") bw.WriteString("Host: " + config.Location.Host + "\r\n") bw.WriteString("Upgrade: websocket\r\n") bw.WriteString("Connection: Upgrade\r\n") nonce := generateNonce() if config.handshakeData != nil { nonce = []byte(config.handshakeData["key"]) } bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\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") } // TODO(ukai): send Sec-WebSocket-Extensions. err = config.Header.WriteSubset(bw, handshakeHeader) if err != nil { return err } bw.WriteString("\r\n") if err = bw.Flush(); err != nil { return err } resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) if err != nil { return err } if resp.StatusCode != 101 { return ErrBadStatus } if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" || strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { return ErrBadUpgrade } expectedAccept, err := getNonceAccept(nonce) if err != nil { return err } if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) { return ErrChallengeResponse } if resp.Header.Get("Sec-WebSocket-Extensions") != "" { return ErrUnsupportedExtensions } offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol") if offeredProtocol != "" { protocolMatched := false for i := 0; i < len(config.Protocol); i++ { if config.Protocol[i] == offeredProtocol { protocolMatched = true break } } if !protocolMatched { return ErrBadWebSocketProtocol } config.Protocol = []string{offeredProtocol} } return nil } // newHybiClientConn creates a client WebSocket connection after handshake. func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { return newHybiConn(config, buf, rwc, nil) } // A HybiServerHandshaker performs a server handshake using hybi draft protocol. type hybiServerHandshaker struct { *Config accept []byte } func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { c.Version = ProtocolVersionHybi13 if req.Method != "GET" { return http.StatusMethodNotAllowed, ErrBadRequestMethod } // HTTP version can be safely ignored. if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") { return http.StatusBadRequest, ErrNotWebSocket } key := req.Header.Get("Sec-Websocket-Key") if key == "" { return http.StatusBadRequest, ErrChallengeResponse } version := req.Header.Get("Sec-Websocket-Version") switch version { case "13": c.Version = ProtocolVersionHybi13 default: return http.StatusBadRequest, ErrBadWebSocketVersion } 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("Sec-Websocket-Protocol")) if protocol != "" { protocols := strings.Split(protocol, ",") for i := 0; i < len(protocols); i++ { c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) } } c.accept, err = getNonceAccept([]byte(key)) if err != nil { return http.StatusInternalServerError, err } return http.StatusSwitchingProtocols, nil } // Origin parses Origin header in "req". // If origin is "null", returns (nil, nil). func Origin(config *Config, req *http.Request) (*url.URL, error) { var origin string switch config.Version { case ProtocolVersionHybi13: origin = req.Header.Get("Origin") } if origin == "null" { return nil, nil } return url.ParseRequestURI(origin) } func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { if len(c.Protocol) > 0 { if len(c.Protocol) != 1 { // You need choose a Protocol in Handshake func in Server. return ErrBadWebSocketProtocol } } buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n") buf.WriteString("Upgrade: websocket\r\n") buf.WriteString("Connection: Upgrade\r\n") buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n") if len(c.Protocol) > 0 { buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") } // TODO(ukai): send Sec-WebSocket-Extensions. if c.Header != nil { err := c.Header.WriteSubset(buf, handshakeHeader) if err != nil { return err } } buf.WriteString("\r\n") return buf.Flush() } func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { return newHybiServerConn(c.Config, buf, rwc, request) } // newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol. func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { return newHybiConn(config, buf, rwc, request) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/websocket/exampledial_test.go�������������������������0000644�0000153�0000161�00000001261�12321735652�026473� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 websocket_test import ( "fmt" "log" "code.google.com/p/go.net/websocket" ) // This example demonstrates a trivial client. func ExampleDial() { origin := "http://localhost/" url := "ws://localhost:12345/ws" ws, err := websocket.Dial(url, "", origin) if err != nil { log.Fatal(err) } if _, err := ws.Write([]byte("hello, world!\n")); err != nil { log.Fatal(err) } var msg = make([]byte, 512) var n int if n, err = ws.Read(msg); err != nil { log.Fatal(err) } fmt.Printf("Received: %s.\n", msg[:n]) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/proxy/������������������������������������������������0000755�0000153�0000161�00000000000�12321735652�022013� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/proxy/proxy.go����������������������������������������0000644�0000153�0000161�00000004544�12321735652�023532� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 proxy provides support for a variety of protocols to proxy network // data. package proxy import ( "errors" "net" "net/url" "os" ) // A Dialer is a means to establish a connection. type Dialer interface { // Dial connects to the given address via the proxy. Dial(network, addr string) (c net.Conn, err error) } // Auth contains authentication parameters that specific Dialers may require. type Auth struct { User, Password string } // FromEnvironment returns the dialer specified by the proxy related variables in // the environment. func FromEnvironment() Dialer { allProxy := os.Getenv("all_proxy") if len(allProxy) == 0 { return Direct } proxyURL, err := url.Parse(allProxy) if err != nil { return Direct } proxy, err := FromURL(proxyURL, Direct) if err != nil { return Direct } noProxy := os.Getenv("no_proxy") if len(noProxy) == 0 { return proxy } perHost := NewPerHost(proxy, Direct) perHost.AddFromString(noProxy) return perHost } // proxySchemes is a map from URL schemes to a function that creates a Dialer // from a URL with such a scheme. var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error) // RegisterDialerType takes a URL scheme and a function to generate Dialers from // a URL with that scheme and a forwarding Dialer. Registered schemes are used // by FromURL. func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) { if proxySchemes == nil { proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error)) } proxySchemes[scheme] = f } // FromURL returns a Dialer given a URL specification and an underlying // Dialer for it to make network requests. func FromURL(u *url.URL, forward Dialer) (Dialer, error) { var auth *Auth if u.User != nil { auth = new(Auth) auth.User = u.User.Username() if p, ok := u.User.Password(); ok { auth.Password = p } } switch u.Scheme { case "socks5": return SOCKS5("tcp", u.Host, auth, forward) } // If the scheme doesn't match any of the built-in schemes, see if it // was registered by another package. if proxySchemes != nil { if f, ok := proxySchemes[u.Scheme]; ok { return f(u, forward) } } return nil, errors.New("proxy: unknown scheme: " + u.Scheme) } ������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/proxy/per_host_test.go��������������������������������0000644�0000153�0000161�00000002415�12321735652�025226� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 proxy import ( "errors" "net" "reflect" "testing" ) type recordingProxy struct { addrs []string } func (r *recordingProxy) Dial(network, addr string) (net.Conn, error) { r.addrs = append(r.addrs, addr) return nil, errors.New("recordingProxy") } func TestPerHost(t *testing.T) { var def, bypass recordingProxy perHost := NewPerHost(&def, &bypass) perHost.AddFromString("localhost,*.zone,127.0.0.1,10.0.0.1/8,1000::/16") expectedDef := []string{ "example.com:123", "1.2.3.4:123", "[1001::]:123", } expectedBypass := []string{ "localhost:123", "zone:123", "foo.zone:123", "127.0.0.1:123", "10.1.2.3:123", "[1000::]:123", } for _, addr := range expectedDef { perHost.Dial("tcp", addr) } for _, addr := range expectedBypass { perHost.Dial("tcp", addr) } if !reflect.DeepEqual(expectedDef, def.addrs) { t.Errorf("Hosts which went to the default proxy didn't match. Got %v, want %v", def.addrs, expectedDef) } if !reflect.DeepEqual(expectedBypass, bypass.addrs) { t.Errorf("Hosts which went to the bypass proxy didn't match. Got %v, want %v", bypass.addrs, expectedBypass) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/proxy/proxy_test.go�����������������������������������0000644�0000153�0000161�00000006470�12321735652�024571� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 proxy import ( "io" "net" "net/url" "strconv" "sync" "testing" ) func TestFromURL(t *testing.T) { endSystem, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("net.Listen failed: %v", err) } defer endSystem.Close() gateway, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("net.Listen failed: %v", err) } defer gateway.Close() var wg sync.WaitGroup wg.Add(1) go socks5Gateway(t, gateway, endSystem, socks5Domain, &wg) url, err := url.Parse("socks5://user:password@" + gateway.Addr().String()) if err != nil { t.Fatalf("url.Parse failed: %v", err) } proxy, err := FromURL(url, Direct) if err != nil { t.Fatalf("FromURL failed: %v", err) } _, port, err := net.SplitHostPort(endSystem.Addr().String()) if err != nil { t.Fatalf("net.SplitHostPort failed: %v", err) } if c, err := proxy.Dial("tcp", "localhost:"+port); err != nil { t.Fatalf("FromURL.Dial failed: %v", err) } else { c.Close() } wg.Wait() } func TestSOCKS5(t *testing.T) { endSystem, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("net.Listen failed: %v", err) } defer endSystem.Close() gateway, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("net.Listen failed: %v", err) } defer gateway.Close() var wg sync.WaitGroup wg.Add(1) go socks5Gateway(t, gateway, endSystem, socks5IP4, &wg) proxy, err := SOCKS5("tcp", gateway.Addr().String(), nil, Direct) if err != nil { t.Fatalf("SOCKS5 failed: %v", err) } if c, err := proxy.Dial("tcp", endSystem.Addr().String()); err != nil { t.Fatalf("SOCKS5.Dial failed: %v", err) } else { c.Close() } wg.Wait() } func socks5Gateway(t *testing.T, gateway, endSystem net.Listener, typ byte, wg *sync.WaitGroup) { defer wg.Done() c, err := gateway.Accept() if err != nil { t.Errorf("net.Listener.Accept failed: %v", err) return } defer c.Close() b := make([]byte, 32) var n int if typ == socks5Domain { n = 4 } else { n = 3 } if _, err := io.ReadFull(c, b[:n]); err != nil { t.Errorf("io.ReadFull failed: %v", err) return } if _, err := c.Write([]byte{socks5Version, socks5AuthNone}); err != nil { t.Errorf("net.Conn.Write failed: %v", err) return } if typ == socks5Domain { n = 16 } else { n = 10 } if _, err := io.ReadFull(c, b[:n]); err != nil { t.Errorf("io.ReadFull failed: %v", err) return } if b[0] != socks5Version || b[1] != socks5Connect || b[2] != 0x00 || b[3] != typ { t.Errorf("got an unexpected packet: %#02x %#02x %#02x %#02x", b[0], b[1], b[2], b[3]) return } if typ == socks5Domain { copy(b[:5], []byte{socks5Version, 0x00, 0x00, socks5Domain, 9}) b = append(b, []byte("localhost")...) } else { copy(b[:4], []byte{socks5Version, 0x00, 0x00, socks5IP4}) } host, port, err := net.SplitHostPort(endSystem.Addr().String()) if err != nil { t.Errorf("net.SplitHostPort failed: %v", err) return } b = append(b, []byte(net.ParseIP(host).To4())...) p, err := strconv.Atoi(port) if err != nil { t.Errorf("strconv.Atoi failed: %v", err) return } b = append(b, []byte{byte(p >> 8), byte(p)}...) if _, err := c.Write(b); err != nil { t.Errorf("net.Conn.Write failed: %v", err) return } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/proxy/direct.go���������������������������������������0000644�0000153�0000161�00000000630�12321735652�023613� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 proxy import ( "net" ) type direct struct{} // Direct is a direct proxy: one that makes network connections directly. var Direct = direct{} func (direct) Dial(network, addr string) (net.Conn, error) { return net.Dial(network, addr) } ��������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/proxy/socks5.go���������������������������������������0000644�0000153�0000161�00000012706�12321735652�023557� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 proxy import ( "errors" "io" "net" "strconv" ) // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address // with an optional username and password. See RFC 1928. func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) { s := &socks5{ network: network, addr: addr, forward: forward, } if auth != nil { s.user = auth.User s.password = auth.Password } return s, nil } type socks5 struct { user, password string network, addr string forward Dialer } const socks5Version = 5 const ( socks5AuthNone = 0 socks5AuthPassword = 2 ) const socks5Connect = 1 const ( socks5IP4 = 1 socks5Domain = 3 socks5IP6 = 4 ) var socks5Errors = []string{ "", "general failure", "connection forbidden", "network unreachable", "host unreachable", "connection refused", "TTL expired", "command not supported", "address type not supported", } // Dial connects to the address addr on the network net via the SOCKS5 proxy. func (s *socks5) Dial(network, addr string) (net.Conn, error) { switch network { case "tcp", "tcp6", "tcp4": default: return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network) } conn, err := s.forward.Dial(s.network, s.addr) if err != nil { return nil, err } closeConn := &conn defer func() { if closeConn != nil { (*closeConn).Close() } }() host, portStr, err := net.SplitHostPort(addr) if err != nil { return nil, err } port, err := strconv.Atoi(portStr) if err != nil { return nil, errors.New("proxy: failed to parse port number: " + portStr) } if port < 1 || port > 0xffff { return nil, errors.New("proxy: port number out of range: " + portStr) } // the size here is just an estimate buf := make([]byte, 0, 6+len(host)) buf = append(buf, socks5Version) if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword) } else { buf = append(buf, 1 /* num auth methods */, socks5AuthNone) } if _, err := conn.Write(buf); err != nil { return nil, errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) } if _, err := io.ReadFull(conn, buf[:2]); err != nil { return nil, errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } if buf[0] != 5 { return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) } if buf[1] == 0xff { return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") } if buf[1] == socks5AuthPassword { buf = buf[:0] buf = append(buf, 1 /* password protocol version */) buf = append(buf, uint8(len(s.user))) buf = append(buf, s.user...) buf = append(buf, uint8(len(s.password))) buf = append(buf, s.password...) if _, err := conn.Write(buf); err != nil { return nil, errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) } if _, err := io.ReadFull(conn, buf[:2]); err != nil { return nil, errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } if buf[1] != 0 { return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") } } buf = buf[:0] buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */) if ip := net.ParseIP(host); ip != nil { if ip4 := ip.To4(); ip4 != nil { buf = append(buf, socks5IP4) ip = ip4 } else { buf = append(buf, socks5IP6) } buf = append(buf, ip...) } else { buf = append(buf, socks5Domain) buf = append(buf, byte(len(host))) buf = append(buf, host...) } buf = append(buf, byte(port>>8), byte(port)) if _, err := conn.Write(buf); err != nil { return nil, errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) } if _, err := io.ReadFull(conn, buf[:4]); err != nil { return nil, errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } failure := "unknown error" if int(buf[1]) < len(socks5Errors) { failure = socks5Errors[buf[1]] } if len(failure) > 0 { return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) } bytesToDiscard := 0 switch buf[3] { case socks5IP4: bytesToDiscard = net.IPv4len case socks5IP6: bytesToDiscard = net.IPv6len case socks5Domain: _, err := io.ReadFull(conn, buf[:1]) if err != nil { return nil, errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } bytesToDiscard = int(buf[0]) default: return nil, errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) } if cap(buf) < bytesToDiscard { buf = make([]byte, bytesToDiscard) } else { buf = buf[:bytesToDiscard] } if _, err := io.ReadFull(conn, buf); err != nil { return nil, errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } // Also need to discard the port number if _, err := io.ReadFull(conn, buf[:2]); err != nil { return nil, errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } closeConn = nil return conn, nil } ����������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/proxy/per_host.go�������������������������������������0000644�0000153�0000161�00000007174�12321735652�024176� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 proxy import ( "net" "strings" ) // A PerHost directs connections to a default Dialer unless the hostname // requested matches one of a number of exceptions. type PerHost struct { def, bypass Dialer bypassNetworks []*net.IPNet bypassIPs []net.IP bypassZones []string bypassHosts []string } // NewPerHost returns a PerHost Dialer that directs connections to either // defaultDialer or bypass, depending on whether the connection matches one of // the configured rules. func NewPerHost(defaultDialer, bypass Dialer) *PerHost { return &PerHost{ def: defaultDialer, bypass: bypass, } } // Dial connects to the address addr on the given network through either // defaultDialer or bypass. func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) { host, _, err := net.SplitHostPort(addr) if err != nil { return nil, err } return p.dialerForRequest(host).Dial(network, addr) } func (p *PerHost) dialerForRequest(host string) Dialer { if ip := net.ParseIP(host); ip != nil { for _, net := range p.bypassNetworks { if net.Contains(ip) { return p.bypass } } for _, bypassIP := range p.bypassIPs { if bypassIP.Equal(ip) { return p.bypass } } return p.def } for _, zone := range p.bypassZones { if strings.HasSuffix(host, zone) { return p.bypass } if host == zone[1:] { // For a zone "example.com", we match "example.com" // too. return p.bypass } } for _, bypassHost := range p.bypassHosts { if bypassHost == host { return p.bypass } } return p.def } // AddFromString parses a string that contains comma-separated values // specifying hosts that should use the bypass proxy. Each value is either an // IP address, a CIDR range, a zone (*.example.com) or a hostname // (localhost). A best effort is made to parse the string and errors are // ignored. func (p *PerHost) AddFromString(s string) { hosts := strings.Split(s, ",") for _, host := range hosts { host = strings.TrimSpace(host) if len(host) == 0 { continue } if strings.Contains(host, "/") { // We assume that it's a CIDR address like 127.0.0.0/8 if _, net, err := net.ParseCIDR(host); err == nil { p.AddNetwork(net) } continue } if ip := net.ParseIP(host); ip != nil { p.AddIP(ip) continue } if strings.HasPrefix(host, "*.") { p.AddZone(host[1:]) continue } p.AddHost(host) } } // AddIP specifies an IP address that will use the bypass proxy. Note that // this will only take effect if a literal IP address is dialed. A connection // to a named host will never match an IP. func (p *PerHost) AddIP(ip net.IP) { p.bypassIPs = append(p.bypassIPs, ip) } // AddNetwork specifies an IP range that will use the bypass proxy. Note that // this will only take effect if a literal IP address is dialed. A connection // to a named host will never match. func (p *PerHost) AddNetwork(net *net.IPNet) { p.bypassNetworks = append(p.bypassNetworks, net) } // AddZone specifies a DNS suffix that will use the bypass proxy. A zone of // "example.com" matches "example.com" and all of its subdomains. func (p *PerHost) AddZone(zone string) { if strings.HasSuffix(zone, ".") { zone = zone[:len(zone)-1] } if !strings.HasPrefix(zone, ".") { zone = "." + zone } p.bypassZones = append(p.bypassZones, zone) } // AddHost specifies a hostname that will use the bypass proxy. func (p *PerHost) AddHost(host string) { if strings.HasSuffix(host, ".") { host = host[:len(host)-1] } p.bypassHosts = append(p.bypassHosts, host) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/README������������������������������������������������0000644�0000153�0000161�00000000217�12321735652�021512� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This repository holds supplementary Go networking libraries. To submit changes to this repository, see http://golang.org/doc/contribute.html. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735652�021514� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/control_bsd.go�����������������������������������0000644�0000153�0000161�00000005346�12321735652�024363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd netbsd openbsd package ipv4 import ( "net" "os" "syscall" "unsafe" ) func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { opt.Lock() defer opt.Unlock() if cf&FlagTTL != 0 { if err := setIPv4ReceiveTTL(fd, on); err != nil { return err } if on { opt.set(FlagTTL) } else { opt.clear(FlagTTL) } } if cf&FlagDst != 0 { if err := setIPv4ReceiveDestinationAddress(fd, on); err != nil { return err } if on { opt.set(FlagDst) } else { opt.clear(FlagDst) } } if cf&FlagInterface != 0 { if err := setIPv4ReceiveInterface(fd, on); err != nil { return err } if on { opt.set(FlagInterface) } else { opt.clear(FlagInterface) } } return nil } func newControlMessage(opt *rawOpt) (oob []byte) { opt.Lock() defer opt.Unlock() l, off := 0, 0 if opt.isset(FlagTTL) { l += syscall.CmsgSpace(1) } if opt.isset(FlagDst) { l += syscall.CmsgSpace(net.IPv4len) } if opt.isset(FlagInterface) { l += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink) } if l > 0 { oob = make([]byte, l) if opt.isset(FlagTTL) { m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) m.Level = ianaProtocolIP m.Type = syscall.IP_RECVTTL m.SetLen(syscall.CmsgLen(1)) off += syscall.CmsgSpace(1) } if opt.isset(FlagDst) { m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) m.Level = ianaProtocolIP m.Type = syscall.IP_RECVDSTADDR m.SetLen(syscall.CmsgLen(net.IPv4len)) off += syscall.CmsgSpace(net.IPv4len) } if opt.isset(FlagInterface) { m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) m.Level = ianaProtocolIP m.Type = syscall.IP_RECVIF m.SetLen(syscall.CmsgLen(syscall.SizeofSockaddrDatalink)) off += syscall.CmsgSpace(syscall.SizeofSockaddrDatalink) } } 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 != ianaProtocolIP { continue } switch m.Header.Type { case syscall.IP_RECVTTL: cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) case syscall.IP_RECVDSTADDR: cm.Dst = m.Data[:net.IPv4len] case syscall.IP_RECVIF: sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&m.Data[0])) cm.IfIndex = int(sadl.Index) } } return cm, nil } func marshalControlMessage(cm *ControlMessage) []byte { // TODO(mikio): Implement IP_PKTINFO stuff when OS X 10.8 comes return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/genericopt_posix.go������������������������������0000644�0000153�0000161�00000002304�12321735652�025423� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd windows package ipv4 import ( "syscall" ) // TOS returns the type-of-service field value for outgoing packets. func (c *genericOpt) TOS() (int, error) { if !c.ok() { return 0, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return 0, err } return ipv4TOS(fd) } // SetTOS sets the type-of-service field value for future outgoing // packets. func (c *genericOpt) SetTOS(tos int) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv4TOS(fd, tos) } // TTL returns the time-to-live field value for outgoing packets. func (c *genericOpt) TTL() (int, error) { if !c.ok() { return 0, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return 0, err } return ipv4TTL(fd) } // SetTTL sets the time-to-live field value for future outgoing // packets. func (c *genericOpt) SetTTL(ttl int) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv4TTL(fd, ttl) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/sockopt_unix.go����������������������������������0000644�0000153�0000161�00000003234�12321735652�024572� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd package ipv4 import ( "os" "syscall" ) func ipv4TOS(fd int) (int, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_TOS) if err != nil { return 0, os.NewSyscallError("getsockopt", err) } return v, nil } func setIPv4TOS(fd int, v int) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_TOS, v)) } func ipv4TTL(fd int) (int, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_TTL) if err != nil { return 0, os.NewSyscallError("getsockopt", err) } return v, nil } func setIPv4TTL(fd int, v int) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_TTL, v)) } func ipv4ReceiveTTL(fd int) (bool, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTTL) if err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv4ReceiveTTL(fd int, v bool) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTTL, boolint(v))) } func ipv4HeaderPrepend(fd int) (bool, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_HDRINCL) if err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv4HeaderPrepend(fd int, v bool) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_HDRINCL, boolint(v))) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/control.go���������������������������������������0000644�0000153�0000161�00000003215�12321735652�023524� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "fmt" "net" "sync" ) type rawOpt struct { sync.Mutex cflags ControlFlags } func (c *rawOpt) set(f ControlFlags) { c.cflags |= f } func (c *rawOpt) clear(f ControlFlags) { c.cflags &^= f } func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 } type ControlFlags uint const ( FlagTTL ControlFlags = 1 << iota // pass the TTL on the received packet FlagSrc // pass the source address on the received packet FlagDst // pass the destination address on the received packet FlagInterface // pass the interface index on the received packet ) // A ControlMessage represents per packet basis IP-level socket options. type ControlMessage struct { // Receiving socket options: SetControlMessage allows to // receive the options from the protocol stack using ReadFrom // method of PacketConn or RawConn. // // Specifying socket options: ControlMessage for WriteTo // method of PacketConn or RawConn allows to send the options // to the protocol stack. // TTL int // time-to-live, receiving only Src net.IP // source address, specifying only Dst net.IP // destination address, receiving only IfIndex int // interface index, must be 1 <= value when specifying } func (cm *ControlMessage) String() string { if cm == nil { return "<nil>" } return fmt.Sprintf("ttl: %v, src: %v, dst: %v, ifindex: %v", cm.TTL, cm.Src, cm.Dst, cm.IfIndex) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/dgramopt_posix.go��������������������������������0000644�0000153�0000161�00000005627�12321735652�025114� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd windows package ipv4 import ( "net" "syscall" ) // MulticastTTL returns the time-to-live field value for outgoing // multicast packets. func (c *dgramOpt) MulticastTTL() (int, error) { if !c.ok() { return 0, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return 0, err } return ipv4MulticastTTL(fd) } // SetMulticastTTL sets the time-to-live field value for future // outgoing multicast packets. func (c *dgramOpt) SetMulticastTTL(ttl int) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv4MulticastTTL(fd, ttl) } // MulticastInterface returns the default interface for multicast // packet transmissions. func (c *dgramOpt) MulticastInterface() (*net.Interface, error) { if !c.ok() { return nil, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return nil, err } return ipv4MulticastInterface(fd) } // SetMulticastInterface sets the default interface for future // multicast packet transmissions. func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv4MulticastInterface(fd, ifi) } // MulticastLoopback reports whether transmitted multicast packets // should be copied and send back to the originator. func (c *dgramOpt) MulticastLoopback() (bool, error) { if !c.ok() { return false, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return false, err } return ipv4MulticastLoopback(fd) } // SetMulticastLoopback sets whether transmitted multicast packets // should be copied and send back to the originator. func (c *dgramOpt) SetMulticastLoopback(on bool) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv4MulticastLoopback(fd, on) } // 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 { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } grp := netAddrToIP4(group) if grp == nil { return errMissingAddress } return joinIPv4Group(fd, ifi, grp) } // LeaveGroup leaves the group address group on the interface ifi. func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } grp := netAddrToIP4(group) if grp == nil { return errMissingAddress } return leaveIPv4Group(fd, ifi, grp) } ���������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/iana_test.go�������������������������������������0000644�0000153�0000161�00000002377�12321735652�024023� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// go run gentest.go // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT package ipv4_test // Differentiated Services Field Codepoints (DSCP), Updated: 2013-06-25 const ( DiffServCS0 = 0x0 // CS0 DiffServCS1 = 0x20 // CS1 DiffServCS2 = 0x40 // CS2 DiffServCS3 = 0x60 // CS3 DiffServCS4 = 0x80 // CS4 DiffServCS5 = 0xa0 // CS5 DiffServCS6 = 0xc0 // CS6 DiffServCS7 = 0xe0 // CS7 DiffServAF11 = 0x28 // AF11 DiffServAF12 = 0x30 // AF12 DiffServAF13 = 0x38 // AF13 DiffServAF21 = 0x48 // AF21 DiffServAF22 = 0x50 // AF22 DiffServAF23 = 0x58 // AF23 DiffServAF31 = 0x68 // AF31 DiffServAF32 = 0x70 // AF32 DiffServAF33 = 0x78 // AF33 DiffServAF41 = 0x88 // AF41 DiffServAF42 = 0x90 // AF42 DiffServAF43 = 0x98 // AF43 DiffServEFPHB = 0xb8 // EF PHB DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT ) // IPv4 TOS Byte and IPv6 Traffic Class Octet, Updated: 2001-09-06 const ( NotECNTransport = 0x0 // Not-ECT (Not ECN-Capable Transport) ECNTransport1 = 0x1 // ECT(1) (ECN-Capable Transport(1)) ECNTransport0 = 0x2 // ECT(0) (ECN-Capable Transport(0)) CongestionExperienced = 0x3 // CE (Congestion Experienced) ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/header.go����������������������������������������0000644�0000153�0000161�00000012132�12321735652�023272� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "errors" "fmt" "net" "runtime" "syscall" "unsafe" ) var ( errMissingAddress = errors.New("missing address") errMissingHeader = errors.New("missing header") errHeaderTooShort = errors.New("header too short") errBufferTooShort = errors.New("buffer too short") errInvalidConnType = errors.New("invalid conn type") ) // References: // // RFC 791 Internet Protocol // http://tools.ietf.org/html/rfc791 // RFC 1112 Host Extensions for IP Multicasting // http://tools.ietf.org/html/rfc1112 // RFC 1122 Requirements for Internet Hosts // http://tools.ietf.org/html/rfc1122 const ( Version = 4 // protocol version HeaderLen = 20 // header length without extension headers maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields ) 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 ( 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 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 "<nil>" } 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 // OpenBSD. IP(7) on Linux. const supportsNewIPInput = runtime.GOOS == "linux" || runtime.GOOS == "openbsd" // Marshal returns the binary encoding of the IPv4 header h. func (h *Header) Marshal() ([]byte, error) { if h == nil { return nil, syscall.EINVAL } if h.Len < HeaderLen { return nil, errHeaderTooShort } hdrlen := HeaderLen + len(h.Options) 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(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(flagsAndFragOff) } b[posID], b[posID+1] = byte(h.ID>>8), byte(h.ID) b[posTTL] = byte(h.TTL) b[posProtocol] = byte(h.Protocol) b[posChecksum], b[posChecksum+1] = byte(h.Checksum>>8), byte(h.Checksum) if ip := h.Src.To4(); ip != nil { copy(b[posSrc:posSrc+net.IPv4len], ip[:net.IPv4len]) } if ip := h.Dst.To4(); ip != nil { copy(b[posDst:posDst+net.IPv4len], ip[:net.IPv4len]) } else { return nil, errMissingAddress } if len(h.Options) > 0 { copy(b[HeaderLen:], h.Options) } 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 { return nil, errHeaderTooShort } hdrlen := int(b[0]&0x0f) << 2 if hdrlen > len(b) { return nil, errBufferTooShort } h := &Header{} h.Version = int(b[0] >> 4) h.Len = hdrlen h.TOS = int(b[posTOS]) if supportsNewIPInput { h.TotalLen = int(b[posTotalLen])<<8 | int(b[posTotalLen+1]) h.FragOff = int(b[posFragOff])<<8 | int(b[posFragOff+1]) } else { h.TotalLen = int(*(*uint16)(unsafe.Pointer(&b[posTotalLen : posTotalLen+1][0]))) 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]) h.Checksum = int(b[posChecksum])<<8 | int(b[posChecksum+1]) h.Src = net.IPv4(b[posSrc], b[posSrc+1], b[posSrc+2], b[posSrc+3]) h.Dst = net.IPv4(b[posDst], b[posDst+1], b[posDst+2], b[posDst+3]) if hdrlen-HeaderLen > 0 { h.Options = make([]byte, hdrlen-HeaderLen) copy(h.Options, b[HeaderLen:]) } return h, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/sockopt_bsd.go�����������������������������������0000644�0000153�0000161�00000006114�12321735652�024357� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd netbsd openbsd package ipv4 import ( "net" "os" "syscall" ) func ipv4MulticastTTL(fd int) (int, error) { v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL) if err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil } func setIPv4MulticastTTL(fd int, v int) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, byte(v))) } func ipv4ReceiveDestinationAddress(fd int) (bool, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVDSTADDR) if err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv4ReceiveDestinationAddress(fd int, v bool) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVDSTADDR, boolint(v))) } func ipv4ReceiveInterface(fd int) (bool, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVIF) if err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv4ReceiveInterface(fd int, v bool) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVIF, boolint(v))) } func ipv4MulticastInterface(fd int) (*net.Interface, error) { v, err := syscall.GetsockoptInet4Addr(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF) if err != nil { return nil, os.NewSyscallError("getsockopt", err) } return netIP4ToInterface(net.IPv4(v[0], v[1], v[2], v[3])) } func setIPv4MulticastInterface(fd int, ifi *net.Interface) error { ip, err := netInterfaceToIP4(ifi) if err != nil { return os.NewSyscallError("setsockopt", err) } var v [4]byte copy(v[:], ip.To4()) return os.NewSyscallError("setsockopt", syscall.SetsockoptInet4Addr(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, v)) } func ipv4MulticastLoopback(fd int) (bool, error) { v, err := syscall.GetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP) if err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv4MulticastLoopback(fd int, v bool) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))) } func joinIPv4Group(fd int, ifi *net.Interface, grp net.IP) error { mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}} if err := setSyscallIPMreq(&mreq, ifi); err != nil { return err } return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd, ianaProtocolIP, syscall.IP_ADD_MEMBERSHIP, &mreq)) } func leaveIPv4Group(fd int, ifi *net.Interface, grp net.IP) error { mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}} if err := setSyscallIPMreq(&mreq, ifi); err != nil { return err } return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd, ianaProtocolIP, syscall.IP_DROP_MEMBERSHIP, &mreq)) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/gen.go�������������������������������������������0000644�0000153�0000161�00000012765�12321735652�022627� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ignore // This program generates internet protocol constatns and tables by // reading IANA protocol registries. // // Usage of this program: // go run gen.go > iana.go package main import ( "bytes" "encoding/xml" "fmt" "go/format" "io" "net/http" "os" "strconv" "strings" ) var registries = []struct { url string parse func(io.Writer, io.Reader) error }{ { "http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xml", parseICMPv4Parameters, }, { "http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml", parseProtocolNumbers, }, } func main() { var bb bytes.Buffer fmt.Fprintf(&bb, "// go run gen.go\n") fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n") fmt.Fprintf(&bb, "package ipv4\n\n") for _, r := range registries { resp, err := http.Get(r.url) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url) os.Exit(1) } if err := r.parse(&bb, resp.Body); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Fprintf(&bb, "\n") } b, err := format.Source(bb.Bytes()) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } os.Stdout.Write(b) } func parseICMPv4Parameters(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var icp icmpv4Parameters if err := dec.Decode(&icp); err != nil { return err } prs := icp.escape() fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated) fmt.Fprintf(w, "const (\n") for _, pr := range prs { if pr.Descr == "" { continue } fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Descr, pr.Value) fmt.Fprintf(w, "// %s\n", pr.OrigDescr) } fmt.Fprintf(w, ")\n\n") fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated) fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n") for _, pr := range prs { if pr.Descr == "" { continue } fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigDescr)) } fmt.Fprintf(w, "}\n") return nil } type icmpv4Parameters struct { 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"` Descr string `xml:"description"` } `xml:"record"` } `xml:"registry"` } type canonICMPv4ParamRecord struct { OrigDescr string Descr string Value int } func (icp *icmpv4Parameters) escape() []canonICMPv4ParamRecord { id := -1 for i, r := range icp.Registries { if strings.Contains(r.Title, "Type") || strings.Contains(r.Title, "type") { id = i break } } if id < 0 { return nil } prs := make([]canonICMPv4ParamRecord, len(icp.Registries[id].Records)) sr := strings.NewReplacer( "Messages", "", "Message", "", "ICMP", "", "+", "P", "-", "", "/", "", ".", "", " ", "", ) for i, pr := range icp.Registries[id].Records { if strings.Contains(pr.Descr, "Reserved") || strings.Contains(pr.Descr, "Unassigned") || strings.Contains(pr.Descr, "Deprecated") || strings.Contains(pr.Descr, "Experiment") || strings.Contains(pr.Descr, "experiment") { continue } ss := strings.Split(pr.Descr, "\n") if len(ss) > 1 { prs[i].Descr = strings.Join(ss, " ") } else { prs[i].Descr = ss[0] } s := strings.TrimSpace(prs[i].Descr) prs[i].OrigDescr = s prs[i].Descr = sr.Replace(s) prs[i].Value, _ = strconv.Atoi(pr.Value) } return prs } func parseProtocolNumbers(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var pn protocolNumbers if err := dec.Decode(&pn); err != nil { return err } prs := pn.escape() prs = append([]canonProtocolRecord{{ Name: "IP", Descr: "IPv4 encapsulation, pseudo protocol number", Value: 0, }}, prs...) fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated) fmt.Fprintf(w, "const (\n") for _, pr := range prs { if pr.Name == "" { continue } fmt.Fprintf(w, "ianaProtocol%s = %d", pr.Name, pr.Value) s := pr.Descr if s == "" { s = pr.OrigName } fmt.Fprintf(w, "// %s\n", s) } fmt.Fprintf(w, ")\n") return nil } 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 []struct { Value string `xml:"value"` Name string `xml:"name"` Descr string `xml:"description"` } `xml:"registry>record"` } type canonProtocolRecord struct { OrigName string Name string Descr string Value int } func (pn *protocolNumbers) escape() []canonProtocolRecord { prs := make([]canonProtocolRecord, len(pn.Records)) sr := strings.NewReplacer( "-in-", "in", "-within-", "within", "-over-", "over", "+", "P", "-", "", "/", "", ".", "", " ", "", ) for i, pr := range pn.Records { prs[i].OrigName = pr.Name s := strings.TrimSpace(pr.Name) switch pr.Name { case "ISIS over IPv4": prs[i].Name = "ISIS" case "manet": prs[i].Name = "MANET" default: prs[i].Name = sr.Replace(s) } ss := strings.Split(pr.Descr, "\n") for i := range ss { ss[i] = strings.TrimSpace(ss[i]) } if len(ss) > 1 { prs[i].Descr = strings.Join(ss, " ") } else { prs[i].Descr = ss[0] } prs[i].Value, _ = strconv.Atoi(pr.Value) } return prs } �����������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/control_stub.go����������������������������������0000644�0000153�0000161�00000001231�12321735652�024555� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { // TODO(mikio): Implement this return errOpNoSupport } func newControlMessage(opt *rawOpt) []byte { // TODO(mikio): Implement this return nil } func parseControlMessage(b []byte) (*ControlMessage, error) { // TODO(mikio): Implement this return nil, errOpNoSupport } func marshalControlMessage(cm *ControlMessage) []byte { // TODO(mikio): Implement this return nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/example_test.go����������������������������������0000644�0000153�0000161�00000012745�12321735652�024546� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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_test import ( "code.google.com/p/go.net/ipv4" "log" "net" ) func ExampleUnicastTCPListener() { ln, err := net.Listen("tcp4", "0.0.0.0:1024") if err != nil { log.Fatal(err) } defer ln.Close() for { c, err := ln.Accept() if err != nil { log.Fatal(err) } go func(c net.Conn) { defer c.Close() err := ipv4.NewConn(c).SetTOS(DiffServAF11) if err != nil { log.Fatal(err) } _, err = c.Write([]byte("HELLO-R-U-THERE-ACK")) if err != nil { log.Fatal(err) } }(c) } } func ExampleMulticastUDPListener() { en0, err := net.InterfaceByName("en0") if err != nil { log.Fatal(err) } en1, err := net.InterfaceByIndex(911) if err != nil { log.Fatal(err) } group := net.IPv4(224, 0, 0, 250) c, err := net.ListenPacket("udp4", "0.0.0.0:1024") if err != nil { log.Fatal(err) } defer c.Close() p := ipv4.NewPacketConn(c) err = p.JoinGroup(en0, &net.UDPAddr{IP: group}) if err != nil { log.Fatal(err) } err = p.JoinGroup(en1, &net.UDPAddr{IP: group}) if err != nil { log.Fatal(err) } err = p.SetControlMessage(ipv4.FlagDst, true) if err != nil { log.Fatal(err) } b := make([]byte, 1500) for { n, cm, src, err := p.ReadFrom(b) if err != nil { log.Fatal(err) } if cm.Dst.IsMulticast() { if cm.Dst.Equal(group) { // joined group, do something } else { // unknown group, discard continue } } p.SetTOS(DiffServCS7) p.SetTTL(16) _, err = p.WriteTo(b[:n], nil, src) if err != nil { log.Fatal(err) } dst := &net.UDPAddr{IP: group, Port: 1024} for _, ifi := range []*net.Interface{en0, en1} { err := p.SetMulticastInterface(ifi) if err != nil { log.Fatal(err) } p.SetMulticastTTL(2) _, err = p.WriteTo(b[:n], nil, dst) if err != nil { log.Fatal(err) } } } err = p.LeaveGroup(en1, &net.UDPAddr{IP: group}) if err != nil { log.Fatal(err) } newgroup := net.IPv4(224, 0, 0, 249) err = p.JoinGroup(en1, &net.UDPAddr{IP: newgroup}) if err != nil { log.Fatal(err) } } type OSPFHeader struct { Version byte Type byte Len uint16 RouterID uint32 AreaID uint32 Checksum uint16 } const ( OSPFHeaderLen = 14 OSPFHelloHeaderLen = 20 OSPF_VERSION = 2 OSPF_TYPE_HELLO = iota + 1 OSPF_TYPE_DB_DESCRIPTION OSPF_TYPE_LS_REQUEST OSPF_TYPE_LS_UPDATE OSPF_TYPE_LS_ACK ) var ( AllSPFRouters = net.IPv4(224, 0, 0, 5) AllDRouters = net.IPv4(224, 0, 0, 6) ) func ExampleIPOSPFListener() { var ifs []*net.Interface en0, err := net.InterfaceByName("en0") if err != nil { log.Fatal(err) } ifs = append(ifs, en0) en1, err := net.InterfaceByIndex(911) if err != nil { log.Fatal(err) } ifs = append(ifs, en1) c, err := net.ListenPacket("ip4:89", "0.0.0.0") // OSFP for IPv4 if err != nil { log.Fatal(err) } defer c.Close() r, err := ipv4.NewRawConn(c) if err != nil { log.Fatal(err) } for _, ifi := range ifs { err := r.JoinGroup(ifi, &net.IPAddr{IP: AllSPFRouters}) if err != nil { log.Fatal(err) } err = r.JoinGroup(ifi, &net.IPAddr{IP: AllDRouters}) if err != nil { log.Fatal(err) } } err = r.SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true) if err != nil { log.Fatal(err) } r.SetTOS(DiffServCS6) parseOSPFHeader := func(b []byte) *OSPFHeader { if len(b) < OSPFHeaderLen { return nil } return &OSPFHeader{ Version: b[0], Type: b[1], Len: uint16(b[2])<<8 | uint16(b[3]), RouterID: uint32(b[4])<<24 | uint32(b[5])<<16 | uint32(b[6])<<8 | uint32(b[7]), AreaID: uint32(b[8])<<24 | uint32(b[9])<<16 | uint32(b[10])<<8 | uint32(b[11]), Checksum: uint16(b[12])<<8 | uint16(b[13]), } } b := make([]byte, 1500) for { iph, p, _, err := r.ReadFrom(b) if err != nil { log.Fatal(err) } if iph.Version != ipv4.Version { continue } if iph.Dst.IsMulticast() { if !iph.Dst.Equal(AllSPFRouters) && !iph.Dst.Equal(AllDRouters) { continue } } ospfh := parseOSPFHeader(p) if ospfh == nil { continue } if ospfh.Version != OSPF_VERSION { continue } switch ospfh.Type { case OSPF_TYPE_HELLO: case OSPF_TYPE_DB_DESCRIPTION: case OSPF_TYPE_LS_REQUEST: case OSPF_TYPE_LS_UPDATE: case OSPF_TYPE_LS_ACK: } } } func ExampleWriteIPOSPFHello() { var ifs []*net.Interface en0, err := net.InterfaceByName("en0") if err != nil { log.Fatal(err) } ifs = append(ifs, en0) en1, err := net.InterfaceByIndex(911) if err != nil { log.Fatal(err) } ifs = append(ifs, en1) c, err := net.ListenPacket("ip4:89", "0.0.0.0") // OSPF for IPv4 if err != nil { log.Fatal(err) } defer c.Close() r, err := ipv4.NewRawConn(c) if err != nil { log.Fatal(err) } for _, ifi := range ifs { err := r.JoinGroup(ifi, &net.IPAddr{IP: AllSPFRouters}) if err != nil { log.Fatal(err) } err = r.JoinGroup(ifi, &net.IPAddr{IP: AllDRouters}) if err != nil { log.Fatal(err) } } hello := make([]byte, OSPFHelloHeaderLen) ospf := make([]byte, OSPFHeaderLen) ospf[0] = OSPF_VERSION ospf[1] = OSPF_TYPE_HELLO ospf = append(ospf, hello...) iph := &ipv4.Header{} iph.Version = ipv4.Version iph.Len = ipv4.HeaderLen iph.TOS = DiffServCS6 iph.TotalLen = ipv4.HeaderLen + len(ospf) iph.TTL = 1 iph.Protocol = 89 iph.Dst = AllSPFRouters for _, ifi := range ifs { err := r.SetMulticastInterface(ifi) if err != nil { return } err = r.WriteTo(iph, ospf, nil) if err != nil { return } } } ���������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/doc.go�������������������������������������������0000644�0000153�0000161�00000013665�12321735652�022623� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 implements IP-level socket options for the Internet // Protocol version 4. // // The package provides IP-level socket options that allow // manipulation of IPv4 facilities. The IPv4 and basic host // requirements for IPv4 are defined in RFC 791, RFC 1112 and RFC // 1122. // // // Unicasting // // The options for unicasting are available for net.TCPConn, // net.UDPConn and net.IPConn which are created as network connections // that use the IPv4 transport. When a single TCP connection carrying // a data flow of multiple packets needs to indicate the flow is // important, ipv4.Conn is used to set the type-of-service field on // the IPv4 header for each packet. // // ln, err := net.Listen("tcp4", "0.0.0.0:1024") // if err != nil { // // error handling // } // defer ln.Close() // for { // c, err := ln.Accept() // if err != nil { // // error handling // } // go func(c net.Conn) { // defer c.Close() // // The outgoing packets will be labeled DiffServ assured forwarding // class 1 low drop precedence, as known as AF11 packets. // // if err := ipv4.NewConn(c).SetTOS(DiffServAF11); err != nil { // // error handling // } // if _, err := c.Write(data); err != nil { // // error handling // } // }(c) // } // // // Multicasting // // The options for multicasting are available for net.UDPConn and // net.IPconn which are created as network connections that use the // IPv4 transport. A few network facilities must be prepared before // you begin multicasting, at a minimum joining network interfaces and // multicast groups. // // en0, err := net.InterfaceByName("en0") // if err != nil { // // error handling // } // en1, err := net.InterfaceByIndex(911) // if err != nil { // // error handling // } // group := net.IPv4(224, 0, 0, 250) // // First, an application listens to an appropriate address with an // appropriate service port. // // c, err := net.ListenPacket("udp4", "0.0.0.0:1024") // if err != nil { // // error handling // } // defer c.Close() // // Second, the application joins multicast groups, starts listening to // the groups on the specified network interfaces. Note that the // service port for transport layer protocol does not matter with this // operation as joining groups affects only network and link layer // protocols, such as IPv4 and Ethernet. // // p := ipv4.NewPacketConn(c) // if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil { // // error handling // } // if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil { // // error handling // } // // The application might set per packet control message transmissions // between the protocol stack within the kernel. When the application // needs a destination address on an incoming packet, // SetControlMessage of ipv4.PacketConn is used to enable control // message transmissons. // // if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil { // // error handling // } // // The application could identify whether the received packets are // of interest by using the control message that contains the // destination address of the received packet. // // b := make([]byte, 1500) // for { // n, cm, src, err := p.ReadFrom(b) // if err != nil { // // error handling // } // if cm.Dst.IsMulticast() { // if cm.Dst.Equal(group) // // joined group, do something // } else { // // unknown group, discard // continue // } // } // // The application can also send both unicast and multicast packets. // // p.SetTOS(DiffServCS0) // p.SetTTL(16) // if _, err := p.WriteTo(data, nil, src); err != nil { // // error handling // } // dst := &net.UDPAddr{IP: group, Port: 1024} // for _, ifi := range []*net.Interface{en0, en1} { // if err := p.SetMulticastInterface(ifi); err != nil { // // error handling // } // p.SetMulticastTTL(2) // if _, err := p.WriteTo(data, nil, dst); err != nil { // // error handling // } // } // } // // // More multicasting // // An application that uses PacketConn or RawConn may join multiple // multicast groups. For example, a UDP listener with port 1024 might // join two different groups across over two different network // interfaces by using: // // c, err := net.ListenPacket("udp4", "0.0.0.0:1024") // if err != nil { // // error handling // } // defer c.Close() // p := ipv4.NewPacketConn(c) // if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil { // // error handling // } // if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil { // // error handling // } // if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil { // // error handling // } // // It is possible for multiple UDP listeners that listen on the same // UDP port to join the same multicast group. The net package will // provide a socket that listens to a wildcard address with reusable // UDP port when an appropriate multicast address prefix is passed to // the net.ListenPacket or net.ListenUDP. // // c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") // if err != nil { // // error handling // } // defer c1.Close() // c2, err := net.ListenPacket("udp4", "224.0.0.0:1024") // if err != nil { // // error handling // } // defer c2.Close() // p1 := ipv4.NewPacketConn(c1) // if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil { // // error handling // } // p2 := ipv4.NewPacketConn(c2) // if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil { // // error handling // } // // Also it is possible for the application to leave or rejoin a // multicast group on the network interface. // // if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil { // // error handling // } // if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}); err != nil { // // error handling // } package ipv4 ���������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/helper_posix.go����������������������������������0000644�0000153�0000161�00000001463�12321735652�024550� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd windows package ipv4 import ( "bytes" "net" "syscall" ) func setSyscallIPMreq(mreq *syscall.IPMreq, ifi *net.Interface) error { if ifi == nil { return nil } ifat, err := ifi.Addrs() if err != nil { return err } for _, ifa := range ifat { switch v := ifa.(type) { case *net.IPAddr: if a := v.IP.To4(); a != nil { copy(mreq.Interface[:], a) goto done } case *net.IPNet: if a := v.IP.To4(); a != nil { copy(mreq.Interface[:], a) goto done } } } done: if bytes.Equal(mreq.Multiaddr[:], net.IPv4zero.To4()) { return errNoSuchMulticastInterface } return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/helper_windows.go��������������������������������0000644�0000153�0000161�00000002247�12321735652�025101� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "net" "reflect" "syscall" ) func (c *genericOpt) sysfd() (syscall.Handle, error) { switch p := c.Conn.(type) { case *net.TCPConn, *net.UDPConn, *net.IPConn: return sysfd(p) } return syscall.InvalidHandle, errInvalidConnType } func (c *dgramOpt) sysfd() (syscall.Handle, error) { switch p := c.PacketConn.(type) { case *net.UDPConn, *net.IPConn: return sysfd(p.(net.Conn)) } return syscall.InvalidHandle, errInvalidConnType } func (c *payloadHandler) sysfd() (syscall.Handle, error) { return sysfd(c.PacketConn.(net.Conn)) } func (c *packetHandler) sysfd() (syscall.Handle, error) { return sysfd(c.c) } func sysfd(c net.Conn) (syscall.Handle, error) { cv := reflect.ValueOf(c) switch ce := cv.Elem(); ce.Kind() { case reflect.Struct: netfd := ce.FieldByName("conn").FieldByName("fd") switch fe := netfd.Elem(); fe.Kind() { case reflect.Struct: fd := fe.FieldByName("sysfd") return syscall.Handle(fd.Uint()), nil } } return syscall.InvalidHandle, errInvalidConnType } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/multicast_test.go��������������������������������0000644�0000153�0000161�00000011413�12321735652�025107� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd package ipv4_test import ( "code.google.com/p/go.net/ipv4" "net" "os" "testing" ) func TestReadWriteMulticastIPPayloadUDP(t *testing.T) { if testing.Short() || !*testExternal { t.Skip("to avoid external network") } c, err := net.ListenPacket("udp4", "224.0.0.0:1024") // see RFC 4727 if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() ifi := loopbackInterface() if ifi == nil { t.Skip("an appropriate interface not found") } dst, err := net.ResolveUDPAddr("udp4", "224.0.0.254:1024") // see RFC 4727 if err != nil { t.Fatalf("net.ResolveUDPAddr failed: %v", err) } p := ipv4.NewPacketConn(c) if err := p.JoinGroup(ifi, dst); err != nil { t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err) } if err := p.SetMulticastInterface(ifi); err != nil { t.Fatalf("ipv4.PacketConn.SetMulticastInterface failed: %v", err) } if err := p.SetMulticastLoopback(true); err != nil { t.Fatalf("ipv4.PacketConn.SetMulticastLoopback failed: %v", err) } cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface for i, toggle := range []bool{true, false, true} { if err := p.SetControlMessage(cf, toggle); err != nil { t.Fatalf("ipv4.PacketConn.SetControlMessage failed: %v", err) } writeThenReadPayload(t, i, p, []byte("HELLO-R-U-THERE"), dst) } } func TestReadWriteMulticastIPPayloadICMP(t *testing.T) { if testing.Short() || !*testExternal { t.Skip("to avoid external network") } if os.Getuid() != 0 { t.Skip("must be root") } c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() ifi := loopbackInterface() if ifi == nil { t.Skip("an appropriate interface not found") } dst, err := net.ResolveIPAddr("ip4", "224.0.0.254") // see RFC 4727 if err != nil { t.Fatalf("net.ResolveIPAddr failed: %v", err) } p := ipv4.NewPacketConn(c) if err := p.JoinGroup(ifi, dst); err != nil { t.Fatalf("ipv4.PacketConn.JoinGroup on %v failed: %v", ifi, err) } if err := p.SetMulticastInterface(ifi); err != nil { t.Fatalf("ipv4.PacketConn.SetMulticastInterface failed: %v", err) } cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface for i, toggle := range []bool{true, false, true} { wb, err := (&icmpMessage{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmpEcho{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, }).Marshal() if err != nil { t.Fatalf("icmpMessage.Marshal failed: %v", err) } if err := p.SetControlMessage(cf, toggle); err != nil { t.Fatalf("ipv4.PacketConn.SetControlMessage failed: %v", err) } rb := writeThenReadPayload(t, i, p, wb, dst) m, err := parseICMPMessage(rb) if err != nil { t.Fatalf("parseICMPMessage failed: %v", err) } if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 { t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) } } } func TestReadWriteMulticastIPDatagram(t *testing.T) { if testing.Short() || !*testExternal { t.Skip("to avoid external network") } if os.Getuid() != 0 { t.Skip("must be root") } c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() ifi := loopbackInterface() if ifi == nil { t.Skip("an appropriate interface not found") } dst, err := net.ResolveIPAddr("ip4", "224.0.0.254") // see RFC 4727 if err != nil { t.Fatalf("ResolveIPAddr failed: %v", err) } r, err := ipv4.NewRawConn(c) if err != nil { t.Fatalf("ipv4.NewRawConn failed: %v", err) } if err := r.JoinGroup(ifi, dst); err != nil { t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err) } if err := r.SetMulticastInterface(ifi); err != nil { t.Fatalf("ipv4.PacketConn.SetMulticastInterface failed: %v", err) } cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface for i, toggle := range []bool{true, false, true} { wb, err := (&icmpMessage{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmpEcho{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, }).Marshal() if err != nil { t.Fatalf("icmpMessage.Marshal failed: %v", err) } if err := r.SetControlMessage(cf, toggle); err != nil { t.Fatalf("ipv4.RawConn.SetControlMessage failed: %v", err) } rb := writeThenReadDatagram(t, i, r, wb, nil, dst) m, err := parseICMPMessage(rb) if err != nil { t.Fatalf("parseICMPMessage failed: %v", err) } if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 { t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/sys_freebsd.go�����������������������������������0000644�0000153�0000161�00000000414�12321735652�024352� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/control_linux.go���������������������������������0000644�0000153�0000161�00000005634�12321735652�024752� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "os" "syscall" "unsafe" ) // Linux provides a convenient path control option IP_PKTINFO that // contains IP_SENDSRCADDR, IP_RECVDSTADDR, IP_RECVIF and IP_SENDIF. const pktinfo = FlagSrc | FlagDst | FlagInterface func setControlMessage(fd int, opt *rawOpt, cf ControlFlags, on bool) error { opt.Lock() defer opt.Unlock() if cf&FlagTTL != 0 { if err := setIPv4ReceiveTTL(fd, on); err != nil { return err } if on { opt.set(FlagTTL) } else { opt.clear(FlagTTL) } } if cf&pktinfo != 0 { if err := setIPv4PacketInfo(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(FlagTTL) { l += syscall.CmsgSpace(1) } if opt.isset(pktinfo) { l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) } if l > 0 { oob = make([]byte, l) if opt.isset(FlagTTL) { m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) m.Level = ianaProtocolIP m.Type = syscall.IP_RECVTTL m.SetLen(syscall.CmsgLen(1)) off += syscall.CmsgSpace(1) } if opt.isset(pktinfo) { m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) m.Level = ianaProtocolIP m.Type = syscall.IP_PKTINFO m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo)) off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) } } 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 != ianaProtocolIP { continue } switch m.Header.Type { case syscall.IP_TTL: cm.TTL = int(*(*byte)(unsafe.Pointer(&m.Data[:1][0]))) case syscall.IP_PKTINFO: pi := (*syscall.Inet4Pktinfo)(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 pion := false if cm.Src.To4() != nil || cm.IfIndex != 0 { pion = true l += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) } if l > 0 { oob = make([]byte, l) if pion { m := (*syscall.Cmsghdr)(unsafe.Pointer(&oob[off])) m.Level = ianaProtocolIP m.Type = syscall.IP_PKTINFO m.SetLen(syscall.CmsgLen(syscall.SizeofInet4Pktinfo)) pi := (*syscall.Inet4Pktinfo)(unsafe.Pointer(&oob[off+syscall.CmsgLen(0)])) if ip := cm.Src.To4(); ip != nil { copy(pi.Addr[:], ip) } if cm.IfIndex != 0 { pi.Ifindex = int32(cm.IfIndex) } off += syscall.CmsgSpace(syscall.SizeofInet4Pktinfo) } } return } ����������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/endpoint.go��������������������������������������0000644�0000153�0000161�00000011114�12321735652�023661� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "net" "syscall" "time" ) // A Conn represents a network endpoint that uses the IPv4 transport. // It is used to control basic IP-level socket options such as TOS and // TTL. type Conn struct { genericOpt } type genericOpt struct { net.Conn } func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil } // NewConn returns a new Conn. func NewConn(c net.Conn) *Conn { return &Conn{ genericOpt: genericOpt{Conn: c}, } } // A PacketConn represents a packet network endpoint that uses the // IPv4 transport. It is used to control several IP-level socket // options including multicasting. It also provides datagram based // network I/O methods specific to the IPv4 and higher layer protocols // such as UDP. type PacketConn struct { genericOpt dgramOpt payloadHandler } type dgramOpt struct { net.PacketConn } func (c *dgramOpt) ok() bool { return c != nil && c.PacketConn != nil } // SetControlMessage sets the per packet IP-level socket options. func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error { if !c.payloadHandler.ok() { return syscall.EINVAL } fd, err := c.payloadHandler.sysfd() if err != nil { return err } return setControlMessage(fd, &c.payloadHandler.rawOpt, cf, on) } // SetDeadline sets the read and write deadlines associated with the // endpoint. func (c *PacketConn) SetDeadline(t time.Time) error { if !c.payloadHandler.ok() { return syscall.EINVAL } return c.payloadHandler.PacketConn.SetDeadline(t) } // SetReadDeadline sets the read deadline associated with the // endpoint. func (c *PacketConn) SetReadDeadline(t time.Time) error { if !c.payloadHandler.ok() { return syscall.EINVAL } return c.payloadHandler.PacketConn.SetReadDeadline(t) } // SetWriteDeadline sets the write deadline associated with the // endpoint. func (c *PacketConn) SetWriteDeadline(t time.Time) error { if !c.payloadHandler.ok() { return syscall.EINVAL } return c.payloadHandler.PacketConn.SetWriteDeadline(t) } // Close closes the endpoint. func (c *PacketConn) Close() error { if !c.payloadHandler.ok() { return syscall.EINVAL } return c.payloadHandler.PacketConn.Close() } // NewPacketConn returns a new PacketConn using c as its underlying // transport. func NewPacketConn(c net.PacketConn) *PacketConn { return &PacketConn{ genericOpt: genericOpt{Conn: c.(net.Conn)}, dgramOpt: dgramOpt{PacketConn: c}, payloadHandler: payloadHandler{PacketConn: c}, } } // A RawConn represents a packet network endpoint that uses the IPv4 // transport. It is used to control several IP-level socket options // including IPv4 header manipulation. It also provides datagram // based network I/O methods specific to the IPv4 and higher layer // protocols that handle IPv4 datagram directly such as OSPF, GRE. type RawConn struct { genericOpt dgramOpt packetHandler } // SetControlMessage sets the per packet IP-level socket options. func (c *RawConn) SetControlMessage(cf ControlFlags, on bool) error { if !c.packetHandler.ok() { return syscall.EINVAL } fd, err := c.packetHandler.sysfd() if err != nil { return err } return setControlMessage(fd, &c.packetHandler.rawOpt, cf, on) } // SetDeadline sets the read and write deadlines associated with the // endpoint. func (c *RawConn) SetDeadline(t time.Time) error { if !c.packetHandler.ok() { return syscall.EINVAL } return c.packetHandler.c.SetDeadline(t) } // SetReadDeadline sets the read deadline associated with the // endpoint. func (c *RawConn) SetReadDeadline(t time.Time) error { if !c.packetHandler.ok() { return syscall.EINVAL } return c.packetHandler.c.SetReadDeadline(t) } // SetWriteDeadline sets the write deadline associated with the // endpoint. func (c *RawConn) SetWriteDeadline(t time.Time) error { if !c.packetHandler.ok() { return syscall.EINVAL } return c.packetHandler.c.SetWriteDeadline(t) } // Close closes the endpoint. func (c *RawConn) Close() error { if !c.packetHandler.ok() { return syscall.EINVAL } return c.packetHandler.c.Close() } // NewRawConn returns a new RawConn using c as its underlying // transport. func NewRawConn(c net.PacketConn) (*RawConn, error) { r := &RawConn{ genericOpt: genericOpt{Conn: c.(net.Conn)}, dgramOpt: dgramOpt{PacketConn: c}, packetHandler: packetHandler{c: c.(*net.IPConn)}, } fd, err := r.packetHandler.sysfd() if err != nil { return nil, err } if err := setIPv4HeaderPrepend(fd, true); err != nil { return nil, err } return r, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/gentest.go���������������������������������������0000644�0000153�0000161�00000010371�12321735652�023516� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ignore // This program generates internet protocol constants by reading IANA // protocol registries. // // Usage: // go run gentest.go > iana_test.go package main import ( "bytes" "encoding/xml" "fmt" "go/format" "io" "net/http" "os" "strconv" "strings" ) var registries = []struct { url string parse func(io.Writer, io.Reader) error }{ { "http://www.iana.org/assignments/dscp-registry/dscp-registry.xml", parseDSCPRegistry, }, { "http://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml", parseTOSTCByte, }, } func main() { var bb bytes.Buffer 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 { resp, err := http.Get(r.url) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url) os.Exit(1) } if err := r.parse(&bb, resp.Body); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Fprintf(&bb, "\n") } b, err := format.Source(bb.Bytes()) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } os.Stdout.Write(b) } func parseDSCPRegistry(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var dr dscpRegistry if err := dec.Decode(&dr); err != nil { return err } drs := dr.escape() fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated) fmt.Fprintf(w, "const (\n") for _, dr := range drs { fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value) fmt.Fprintf(w, "// %s\n", dr.OrigName) } fmt.Fprintf(w, ")\n") return nil } 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 []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 { OrigName string Name string Value int } func (drr *dscpRegistry) escape() []canonDSCPRecord { drs := make([]canonDSCPRecord, len(drr.Records)) sr := strings.NewReplacer( "+", "", "-", "", "/", "", ".", "", " ", "", ) for i, dr := range drr.Records { s := strings.TrimSpace(dr.Name) drs[i].OrigName = s drs[i].Name = sr.Replace(s) n, err := strconv.ParseUint(dr.Space, 2, 8) if err != nil { continue } drs[i].Value = int(n) << 2 } return drs } func parseTOSTCByte(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var ttb tosTCByte if err := dec.Decode(&ttb); err != nil { return err } trs := ttb.escape() fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated) fmt.Fprintf(w, "const (\n") for _, tr := range trs { fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value) fmt.Fprintf(w, "// %s\n", tr.OrigKeyword) } fmt.Fprintf(w, ")\n") return nil } 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 []struct { Binary string `xml:"binary"` Keyword string `xml:"keyword"` } `xml:"registry>record"` } type canonTOSTCByteRecord struct { OrigKeyword string Keyword string Value int } func (ttb *tosTCByte) escape() []canonTOSTCByteRecord { trs := make([]canonTOSTCByteRecord, len(ttb.Records)) sr := strings.NewReplacer( "Capable", "", "(", "", ")", "", "+", "", "-", "", "/", "", ".", "", " ", "", ) for i, tr := range ttb.Records { s := strings.TrimSpace(tr.Keyword) trs[i].OrigKeyword = s ss := strings.Split(s, " ") if len(ss) > 1 { trs[i].Keyword = strings.Join(ss[1:], " ") } else { trs[i].Keyword = ss[0] } trs[i].Keyword = sr.Replace(trs[i].Keyword) n, err := strconv.ParseUint(tr.Binary, 2, 8) if err != nil { continue } trs[i].Value = int(n) } return trs } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/mocktransponder_test.go��������������������������0000644�0000153�0000161�00000006444�12321735652�026323� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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_test import ( "code.google.com/p/go.net/ipv4" "net" "testing" "time" ) // writeThenReadPayload transmits IPv4 datagram payloads to the // loopback address or interface and captures the loopback'd datagram // payloads. func writeThenReadPayload(t *testing.T, i int, c *ipv4.PacketConn, wb []byte, dst net.Addr) []byte { rb := make([]byte, 1500) c.SetTOS(i + 1) var ip net.IP switch v := dst.(type) { case *net.UDPAddr: ip = v.IP case *net.IPAddr: ip = v.IP } if ip.IsMulticast() { c.SetMulticastTTL(i + 1) } else { c.SetTTL(i + 1) } c.SetDeadline(time.Now().Add(100 * time.Millisecond)) if _, err := c.WriteTo(wb, nil, dst); err != nil { t.Fatalf("ipv4.PacketConn.WriteTo failed: %v", err) } n, cm, _, err := c.ReadFrom(rb) if err != nil { t.Fatalf("ipv4.PacketConn.ReadFrom failed: %v", err) } t.Logf("rcvd cmsg: %v", cm) return rb[:n] } // writeThenReadDatagram transmits ICMP for IPv4 datagrams to the // loopback address or interface and captures the response datagrams // from the protocol stack within the kernel. func writeThenReadDatagram(t *testing.T, i int, c *ipv4.RawConn, wb []byte, src, dst net.Addr) []byte { rb := make([]byte, ipv4.HeaderLen+len(wb)) wh := &ipv4.Header{ Version: ipv4.Version, Len: ipv4.HeaderLen, TOS: i + 1, TotalLen: ipv4.HeaderLen + len(wb), TTL: i + 1, Protocol: 1, } if src != nil { wh.Src = src.(*net.IPAddr).IP } if dst != nil { wh.Dst = dst.(*net.IPAddr).IP } c.SetDeadline(time.Now().Add(100 * time.Millisecond)) if err := c.WriteTo(wh, wb, nil); err != nil { t.Fatalf("ipv4.RawConn.WriteTo failed: %v", err) } rh, b, cm, err := c.ReadFrom(rb) if err != nil { t.Fatalf("ipv4.RawConn.ReadFrom failed: %v", err) } t.Logf("rcvd cmsg: %v", cm.String()) t.Logf("rcvd hdr: %v", rh.String()) 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 { ift, err := net.Interfaces() if err != nil { return nil } for _, ifi := range ift { 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 } // isMulticastAvailable returns true if ifi is a multicast access // 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 == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 { return nil, false } ifat, err := ifi.Addrs() if err != nil { return nil, false } for _, ifa := range ifat { switch ifa := ifa.(type) { case *net.IPAddr: if isUnicast(ifa.IP) { return ifa.IP, true } case *net.IPNet: if isUnicast(ifa.IP) { return ifa.IP, true } } } return nil, false } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/helper.go����������������������������������������0000644�0000153�0000161�00000003063�12321735652�023324� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "errors" "net" ) var ( errOpNoSupport = errors.New("operation not supported") errNoSuchInterface = errors.New("no such interface") errNoSuchMulticastInterface = errors.New("no such multicast interface") ) func boolint(b bool) int { if b { return 1 } return 0 } func netAddrToIP4(a net.Addr) net.IP { switch v := a.(type) { case *net.UDPAddr: if ip := v.IP.To4(); ip != nil { return ip } case *net.IPAddr: if ip := v.IP.To4(); ip != nil { return ip } } return nil } func netIP4ToInterface(ip net.IP) (*net.Interface, error) { ift, err := net.Interfaces() if err != nil { return nil, err } for _, ifi := range ift { ifat, err := ifi.Addrs() if err != nil { return nil, err } for _, ifa := range ifat { switch v := ifa.(type) { case *net.IPAddr: if ip.Equal(v.IP) { return &ifi, nil } case *net.IPNet: if ip.Equal(v.IP) { return &ifi, nil } } } } return nil, errNoSuchInterface } func netInterfaceToIP4(ifi *net.Interface) (net.IP, error) { if ifi == nil { return net.IPv4zero, nil } ifat, err := ifi.Addrs() if err != nil { return nil, err } for _, ifa := range ifat { switch v := ifa.(type) { case *net.IPAddr: if v.IP.To4() != nil { return v.IP, nil } case *net.IPNet: if v.IP.To4() != nil { return v.IP, nil } } } return nil, errNoSuchInterface } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/helper_stub.go�����������������������������������0000644�0000153�0000161�00000001172�12321735652�024360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/sockopt_linux.go���������������������������������0000644�0000153�0000161�00000005675�12321735652�024761� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "net" "os" "syscall" ) func ipv4ReceiveTOS(fd int) (bool, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTOS) if err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv4ReceiveTOS(fd int, v bool) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_RECVTOS, boolint(v))) } func ipv4MulticastTTL(fd int) (int, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL) if err != nil { return 0, os.NewSyscallError("getsockopt", err) } return v, nil } func setIPv4MulticastTTL(fd int, v int) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, v)) } func ipv4PacketInfo(fd int) (bool, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_PKTINFO) if err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv4PacketInfo(fd int, v bool) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_PKTINFO, boolint(v))) } func ipv4MulticastInterface(fd int) (*net.Interface, error) { mreqn, err := syscall.GetsockoptIPMreqn(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF) if err != nil { return nil, os.NewSyscallError("getsockopt", err) } if int(mreqn.Ifindex) == 0 { return nil, nil } return net.InterfaceByIndex(int(mreqn.Ifindex)) } func setIPv4MulticastInterface(fd int, ifi *net.Interface) error { mreqn := syscall.IPMreqn{} if ifi != nil { mreqn.Ifindex = int32(ifi.Index) } return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, &mreqn)) } func ipv4MulticastLoopback(fd int) (bool, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP) if err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv4MulticastLoopback(fd int, v bool) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, boolint(v))) } func joinIPv4Group(fd int, ifi *net.Interface, grp net.IP) error { mreqn := syscall.IPMreqn{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}} if ifi != nil { mreqn.Ifindex = int32(ifi.Index) } return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, syscall.IP_ADD_MEMBERSHIP, &mreqn)) } func leaveIPv4Group(fd int, ifi *net.Interface, grp net.IP) error { mreqn := syscall.IPMreqn{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}} if ifi != nil { mreqn.Ifindex = int32(ifi.Index) } return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd, ianaProtocolIP, syscall.IP_DROP_MEMBERSHIP, &mreqn)) } �������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/sockopt_freebsd.go�������������������������������0000644�0000153�0000161�00000001135�12321735652�025217� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "os" "syscall" ) func ipv4SendSourceAddress(fd int) (bool, error) { v, err := syscall.GetsockoptInt(fd, ianaProtocolIP, syscall.IP_SENDSRCADDR) if err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv4SendSourceAddress(fd int, v bool) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, ianaProtocolIP, syscall.IP_SENDSRCADDR, boolint(v))) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/packet.go����������������������������������������0000644�0000153�0000161�00000005301�12321735652�023311� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "net" "syscall" ) // A packetHandler represents the IPv4 datagram handler. type packetHandler struct { c *net.IPConn rawOpt } func (c *packetHandler) ok() bool { return c != nil && c.c != nil } // ReadFrom reads an IPv4 datagram from the endpoint c, copying the // datagram into b. It returns the received datagram as the IPv4 // header h, the payload p and the control message cm. func (c *packetHandler) ReadFrom(b []byte) (h *Header, p []byte, cm *ControlMessage, err error) { if !c.ok() { return nil, nil, nil, syscall.EINVAL } oob := newControlMessage(&c.rawOpt) n, oobn, _, src, err := c.c.ReadMsgIP(b, oob) if err != nil { return nil, nil, nil, err } var hs []byte if hs, p, err = slicePacket(b[:n]); err != nil { return nil, nil, nil, err } if h, err = ParseHeader(hs); err != nil { return nil, nil, nil, err } if cm, err = parseControlMessage(oob[:oobn]); err != nil { return nil, nil, nil, err } if src != nil && cm != nil { cm.Src = src.IP } return } func slicePacket(b []byte) (h, p []byte, err error) { if len(b) < HeaderLen { return nil, nil, errHeaderTooShort } hdrlen := int(b[0]&0x0f) << 2 return b[:hdrlen], b[hdrlen:], nil } // WriteTo writes an IPv4 datagram through the endpoint c, copying the // datagram from the IPv4 header h and the payload p. The control // message cm allows the datagram path and the outgoing interface to be // specified. Currently only Linux supports this. The cm may be nil // if control of the outgoing datagram is not required. // // The IPv4 header h must contain appropriate fields that include: // // Version = ipv4.Version // Len = <must be specified> // TOS = <must be specified> // TotalLen = <must be specified> // ID = platform sets an appropriate value if ID is zero // FragOff = <must be specified> // TTL = <must be specified> // Protocol = <must be specified> // Checksum = platform sets an appropriate value if Checksum is zero // Src = platform sets an appropriate value if Src is nil // Dst = <must be specified> // Options = optional func (c *packetHandler) WriteTo(h *Header, p []byte, cm *ControlMessage) error { if !c.ok() { return syscall.EINVAL } oob := marshalControlMessage(cm) wh, err := h.Marshal() if err != nil { return err } dst := &net.IPAddr{} if cm != nil { if ip := cm.Dst.To4(); ip != nil { dst.IP = ip } } if dst.IP == nil { dst.IP = h.Dst } wh = append(wh, p...) _, _, err = c.c.WriteMsgIP(wh, oob, dst) return err } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/icmp.go������������������������������������������0000644�0000153�0000161�00000000535�12321735652�022776� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ipv4 // An ICMPType represents a type of ICMP message. type ICMPType int func (typ ICMPType) String() string { s, ok := icmpTypes[typ] if !ok { return "<nil>" } return s } �������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/helper_unix.go�����������������������������������0000644�0000153�0000161�00000002114�12321735652�024363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd package ipv4 import ( "net" "reflect" ) func (c *genericOpt) sysfd() (int, error) { switch p := c.Conn.(type) { case *net.TCPConn, *net.UDPConn, *net.IPConn: return sysfd(p) } return 0, errInvalidConnType } func (c *dgramOpt) sysfd() (int, error) { switch p := c.PacketConn.(type) { case *net.UDPConn, *net.IPConn: return sysfd(p.(net.Conn)) } return 0, errInvalidConnType } func (c *payloadHandler) sysfd() (int, error) { return sysfd(c.PacketConn.(net.Conn)) } func (c *packetHandler) sysfd() (int, error) { return sysfd(c.c) } func sysfd(c net.Conn) (int, error) { cv := reflect.ValueOf(c) switch ce := cv.Elem(); ce.Kind() { case reflect.Struct: netfd := ce.FieldByName("conn").FieldByName("fd") switch fe := netfd.Elem(); fe.Kind() { case reflect.Struct: fd := fe.FieldByName("sysfd") return int(fd.Int()), nil } } return 0, errInvalidConnType } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/unicastsockopt_test.go���������������������������0000644�0000153�0000161�00000006120�12321735652�026152� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd windows package ipv4_test import ( "code.google.com/p/go.net/ipv4" "errors" "net" "os" "runtime" "testing" ) type testUnicastConn interface { TOS() (int, error) SetTOS(int) error TTL() (int, error) SetTTL(int) error } type unicastSockoptTest struct { tos int ttl int } var unicastSockoptTests = []unicastSockoptTest{ {DiffServCS0 | NotECNTransport, 127}, {DiffServAF11 | NotECNTransport, 255}, } func TestTCPUnicastSockopt(t *testing.T) { for _, tt := range unicastSockoptTests { listener := make(chan net.Listener) go tcpListener(t, "127.0.0.1:0", listener) ln := <-listener if ln == nil { return } defer ln.Close() c, err := net.Dial("tcp4", ln.Addr().String()) if err != nil { t.Errorf("net.Dial failed: %v", err) return } defer c.Close() cc := ipv4.NewConn(c) if err := testUnicastSockopt(t, tt, cc); err != nil { break } } } func tcpListener(t *testing.T, addr string, listener chan<- net.Listener) { ln, err := net.Listen("tcp4", addr) if err != nil { t.Errorf("net.Listen failed: %v", err) listener <- nil return } listener <- ln c, err := ln.Accept() if err != nil { return } c.Close() } func TestUDPUnicastSockopt(t *testing.T) { for _, tt := range unicastSockoptTests { c, err := net.ListenPacket("udp4", "127.0.0.1:0") if err != nil { t.Errorf("net.ListenPacket failed: %v", err) return } defer c.Close() p := ipv4.NewPacketConn(c) if err := testUnicastSockopt(t, tt, p); err != nil { break } } } func TestIPUnicastSockopt(t *testing.T) { if os.Getuid() != 0 { t.Skip("must be root") } for _, tt := range unicastSockoptTests { c, err := net.ListenPacket("ip4:icmp", "127.0.0.1") if err != nil { t.Errorf("net.ListenPacket failed: %v", err) return } defer c.Close() r, err := ipv4.NewRawConn(c) if err != nil { t.Errorf("ipv4.NewRawConn failed: %v", err) return } if err := testUnicastSockopt(t, tt, r); err != nil { break } } } func testUnicastSockopt(t *testing.T, tt unicastSockoptTest, c testUnicastConn) error { switch runtime.GOOS { case "windows": // IP_TOS option is supported on Windows 8 and beyond. t.Logf("skipping IP_TOS test on %q", runtime.GOOS) default: if err := c.SetTOS(tt.tos); err != nil { t.Errorf("ipv4.Conn.SetTOS failed: %v", err) return err } if v, err := c.TOS(); err != nil { t.Errorf("ipv4.Conn.TOS failed: %v", err) return err } else if v != tt.tos { t.Errorf("Got unexpected TOS value %v; expected %v", v, tt.tos) return errors.New("Got unexpected TOS value") } } if err := c.SetTTL(tt.ttl); err != nil { t.Errorf("ipv4.Conn.SetTTL failed: %v", err) return err } if v, err := c.TTL(); err != nil { t.Errorf("ipv4.Conn.TTL failed: %v", err) return err } else if v != tt.ttl { t.Errorf("Got unexpected TTL value %v; expected %v", v, tt.ttl) return errors.New("Got unexpected TTL value") } return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/header_test.go�����������������������������������0000644�0000153�0000161�00000004410�12321735652�024331� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "bytes" "net" "reflect" "runtime" "testing" ) var ( wireHeaderFromKernel = [HeaderLen]byte{ 0x45, 0x01, 0xbe, 0xef, 0xca, 0xfe, 0x45, 0xdc, 0xff, 0x01, 0xde, 0xad, 172, 16, 254, 254, 192, 168, 0, 1, } wireHeaderToKernel = [HeaderLen]byte{ 0x45, 0x01, 0xbe, 0xef, 0xca, 0xfe, 0x45, 0xdc, 0xff, 0x01, 0xde, 0xad, 172, 16, 254, 254, 192, 168, 0, 1, } wireHeaderFromTradBSDKernel = [HeaderLen]byte{ 0x45, 0x01, 0xdb, 0xbe, 0xca, 0xfe, 0xdc, 0x45, 0xff, 0x01, 0xde, 0xad, 172, 16, 254, 254, 192, 168, 0, 1, } wireHeaderFromFreeBSD10Kernel = [HeaderLen]byte{ 0x45, 0x01, 0xef, 0xbe, 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, } // TODO(mikio): Add platform dependent wire header formats when // we support new platforms. testHeader = &Header{ Version: Version, Len: HeaderLen, TOS: 1, TotalLen: 0xbeef, ID: 0xcafe, Flags: DontFragment, FragOff: 1500, TTL: 255, Protocol: 1, Checksum: 0xdead, Src: net.IPv4(172, 16, 254, 254), Dst: net.IPv4(192, 168, 0, 1), } ) func TestMarshalHeader(t *testing.T) { b, err := testHeader.Marshal() if err != nil { t.Fatalf("ipv4.Header.Marshal failed: %v", err) } var wh []byte if supportsNewIPInput { wh = wireHeaderToKernel[:] } else { wh = wireHeaderToTradBSDKernel[:] } if !bytes.Equal(b, wh) { t.Fatalf("ipv4.Header.Marshal failed: %#v not equal %#v", b, wh) } } func TestParseHeader(t *testing.T) { var wh []byte if supportsNewIPInput { wh = wireHeaderFromKernel[:] } else { if runtime.GOOS == "freebsd" && freebsdVersion >= 1000000 { wh = wireHeaderFromFreeBSD10Kernel[:] } else { wh = wireHeaderFromTradBSDKernel[:] } } h, err := ParseHeader(wh) if err != nil { t.Fatalf("ipv4.ParseHeader failed: %v", err) } if !reflect.DeepEqual(h, testHeader) { t.Fatalf("ipv4.ParseHeader failed: %#v not equal %#v", h, testHeader) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/dgramopt_stub.go���������������������������������0000644�0000153�0000161�00000002251�12321735652�024715� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 import "net" func (c *dgramOpt) MulticastTTL() (int, error) { // TODO(mikio): Implement this return 0, errOpNoSupport } func (c *dgramOpt) SetMulticastTTL(ttl int) error { // TODO(mikio): Implement this return errOpNoSupport } func (c *dgramOpt) MulticastInterface() (*net.Interface, error) { // TODO(mikio): Implement this return nil, errOpNoSupport } func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error { // TODO(mikio): Implement this return errOpNoSupport } func (c *dgramOpt) MulticastLoopback() (bool, error) { // TODO(mikio): Implement this return false, errOpNoSupport } func (c *dgramOpt) SetMulticastLoopback(on bool) error { // TODO(mikio): Implement this return errOpNoSupport } func (c *dgramOpt) JoinGroup(ifi *net.Interface, grp net.Addr) error { // TODO(mikio): Implement this return errOpNoSupport } func (c *dgramOpt) LeaveGroup(ifi *net.Interface, grp net.Addr) error { // TODO(mikio): Implement this return errOpNoSupport } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/payload.go���������������������������������������0000644�0000153�0000161�00000004414�12321735652�023477� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "net" "syscall" ) // A payloadHandler represents the IPv4 datagram payload handler. type payloadHandler struct { net.PacketConn rawOpt } func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil } // ReadFrom reads a payload of the received IPv4 datagram, from the // endpoint c, copying the payload into b. It returns the number of // bytes copied into b, the control message cm and the source address // src of the received datagram. func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { if !c.ok() { return 0, nil, nil, syscall.EINVAL } oob := newControlMessage(&c.rawOpt) var oobn int switch c := c.PacketConn.(type) { case *net.UDPConn: if n, oobn, _, src, err = c.ReadMsgUDP(b, oob); err != nil { return 0, nil, nil, err } case *net.IPConn: nb := make([]byte, maxHeaderLen+len(b)) if n, oobn, _, src, err = c.ReadMsgIP(nb, oob); err != nil { return 0, nil, nil, err } hdrlen := int(nb[0]&0x0f) << 2 copy(b, nb[hdrlen:]) n -= hdrlen default: return 0, nil, nil, errInvalidConnType } if cm, err = parseControlMessage(oob[:oobn]); err != nil { return 0, nil, nil, err } if cm != nil { cm.Src = netAddrToIP4(src) } return } // WriteTo writes a payload of the IPv4 datagram, to the destination // address dst through the endpoint c, copying the payload from b. It // returns the number of bytes written. The control message cm allows // the datagram path and the outgoing interface to be specified. // Currently only Linux supports this. The cm may be nil if control // of the outgoing datagram is not required. func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { if !c.ok() { return 0, syscall.EINVAL } oob := marshalControlMessage(cm) if dst == nil { return 0, errMissingAddress } switch c := c.PacketConn.(type) { case *net.UDPConn: n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr)) case *net.IPConn: n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr)) default: return 0, errInvalidConnType } if err != nil { return 0, err } return } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/control_windows.go�������������������������������0000644�0000153�0000161�00000001227�12321735652�025277� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 setControlMessage(fd syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error { // TODO(mikio): Implement this return syscall.EWINDOWS } func newControlMessage(opt *rawOpt) []byte { // TODO(mikio): Implement this return nil } func parseControlMessage(b []byte) (*ControlMessage, error) { // TODO(mikio): Implement this return nil, syscall.EWINDOWS } func marshalControlMessage(cm *ControlMessage) []byte { // TODO(mikio): Implement this return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/sockopt_stub.go����������������������������������0000644�0000153�0000161�00000000655�12321735652�024570� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } �����������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/multicastlistener_test.go������������������������0000644�0000153�0000161�00000014527�12321735652�026666� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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_test import ( "code.google.com/p/go.net/ipv4" "net" "os" "runtime" "testing" ) 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 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 _, 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) } for i, ifi := range ift { if _, ok := isMulticastAvailable(&ifi); !ok { continue } 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, gaddr); err != nil { t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err) } } } } 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 _, 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") // wildcard address with reusable port if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c2.Close() 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) } for i, ifi := range ift { if _, ok := isMulticastAvailable(&ifi); !ok { continue } for _, p := range ps { 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, gaddr); err != nil { t.Fatalf("ipv4.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err) } } } } } 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") } 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 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("udp4", ip.String()+":"+"1024") // unicast address with non-reusable port 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) } mlt = append(mlt, &ml{p, &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) } } } 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") } c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") // wildcard address 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 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 { continue } if err := r.JoinGroup(&ifi, &gaddr); err != nil { t.Fatalf("ipv4.RawConn.JoinGroup on %v failed: %v", ifi, err) } mift = append(mift, &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) } } } 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") } 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.RawConn 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("ip4:253", ip.String()) // unicast address if err != nil { t.Fatalf("net.ListenPacket with %v failed: %v", ip, err) } defer c.Close() r, err := ipv4.NewRawConn(c) if err != nil { t.Fatalf("ipv4.NewRawConn failed: %v", err) } 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 { t.Fatalf("ipv4.RawConn.LeaveGroup on %v failed: %v", m.ifi, err) } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/unicast_test.go����������������������������������0000644�0000153�0000161�00000012303�12321735652�024547� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd package ipv4_test import ( "code.google.com/p/go.net/ipv4" "net" "os" "testing" ) func benchmarkUDPListener() (net.PacketConn, net.Addr, error) { c, err := net.ListenPacket("udp4", "127.0.0.1:0") if err != nil { return nil, nil, err } dst, err := net.ResolveUDPAddr("udp4", 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 BenchmarkReadWriteIPv4UDP(b *testing.B) { c, dst, err := benchmarkUDPListener() if err != nil { b.Fatalf("benchmarkUDPListener failed: %v", err) } defer c.Close() p := ipv4.NewPacketConn(c) cf := ipv4.FlagTTL | ipv4.FlagInterface if err := p.SetControlMessage(cf, true); err != nil { b.Fatalf("ipv4.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++ { benchmarkReadWriteIPv4UDP(b, p, wb, rb, dst, ifi) } } func benchmarkReadWriteIPv4UDP(b *testing.B, p *ipv4.PacketConn, wb, rb []byte, dst net.Addr, ifi *net.Interface) { cm := ipv4.ControlMessage{TTL: 1} if ifi != nil { cm.IfIndex = ifi.Index } if _, err := p.WriteTo(wb, &cm, dst); err != nil { b.Fatalf("ipv4.PacketConn.WriteTo failed: %v", err) } if _, _, _, err := p.ReadFrom(rb); err != nil { b.Fatalf("ipv4.PacketConn.ReadFrom failed: %v", err) } } func TestReadWriteUnicastIPPayloadUDP(t *testing.T) { c, err := net.ListenPacket("udp4", "127.0.0.1:0") if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() dst, err := net.ResolveUDPAddr("udp4", c.LocalAddr().String()) if err != nil { t.Fatalf("net.ResolveUDPAddr failed: %v", err) } p := ipv4.NewPacketConn(c) cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface for i, toggle := range []bool{true, false, true} { if err := p.SetControlMessage(cf, toggle); err != nil { t.Fatalf("ipv4.PacketConn.SetControlMessage failed: %v", err) } writeThenReadPayload(t, i, p, []byte("HELLO-R-U-THERE"), dst) } } func TestReadWriteUnicastIPPayloadICMP(t *testing.T) { if os.Getuid() != 0 { t.Skip("must be root") } c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() dst, err := net.ResolveIPAddr("ip4", "127.0.0.1") if err != nil { t.Fatalf("ResolveIPAddr failed: %v", err) } p := ipv4.NewPacketConn(c) cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface for i, toggle := range []bool{true, false, true} { wb, err := (&icmpMessage{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmpEcho{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, }).Marshal() if err != nil { t.Fatalf("icmpMessage.Marshal failed: %v", err) } if err := p.SetControlMessage(cf, toggle); err != nil { t.Fatalf("ipv4.PacketConn.SetControlMessage failed: %v", err) } rb := writeThenReadPayload(t, i, p, wb, dst) m, err := parseICMPMessage(rb) if err != nil { t.Fatalf("parseICMPMessage failed: %v", err) } if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 { t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) } } } func TestReadWriteUnicastIPDatagram(t *testing.T) { if os.Getuid() != 0 { t.Skip("must be root") } c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() dst, err := net.ResolveIPAddr("ip4", "127.0.0.1") if err != nil { t.Fatalf("ResolveIPAddr failed: %v", err) } r, err := ipv4.NewRawConn(c) if err != nil { t.Fatalf("ipv4.NewRawConn failed: %v", err) } cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface for i, toggle := range []bool{true, false, true} { wb, err := (&icmpMessage{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmpEcho{ ID: os.Getpid() & 0xffff, Seq: i + 1, Data: []byte("HELLO-R-U-THERE"), }, }).Marshal() if err != nil { t.Fatalf("icmpMessage.Marshal failed: %v", err) } if err := r.SetControlMessage(cf, toggle); err != nil { t.Fatalf("ipv4.RawConn.SetControlMessage failed: %v", err) } rb := writeThenReadDatagram(t, i, r, wb, nil, dst) m, err := parseICMPMessage(rb) if err != nil { t.Fatalf("parseICMPMessage failed: %v", err) } if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 { t.Fatalf("got type=%v, code=%v; expected type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/mockicmp_test.go���������������������������������0000644�0000153�0000161�00000005277�12321735652�024717� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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_test import ( "code.google.com/p/go.net/ipv4" "errors" "flag" ) var testExternal = flag.Bool("external", true, "allow use of external networks during long test") // icmpMessage represents an ICMP message. type icmpMessage struct { Type ipv4.ICMPType // type Code int // code Checksum int // checksum Body icmpMessageBody // body } // icmpMessageBody represents an ICMP message body. type icmpMessageBody interface { Len() int Marshal() ([]byte, error) } // Marshal returns the binary enconding of the ICMP echo request or // reply message m. func (m *icmpMessage) Marshal() ([]byte, error) { b := []byte{byte(m.Type), byte(m.Code), 0, 0} if m.Body != nil && m.Body.Len() != 0 { mb, err := m.Body.Marshal() if err != nil { return nil, err } b = append(b, mb...) } csumcv := len(b) - 1 // checksum coverage s := uint32(0) for i := 0; i < csumcv; i += 2 { s += uint32(b[i+1])<<8 | uint32(b[i]) } if csumcv&1 == 0 { s += uint32(b[csumcv]) } s = s>>16 + s&0xffff 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 } // parseICMPMessage parses b as an ICMP message. func parseICMPMessage(b []byte) (*icmpMessage, error) { msglen := len(b) if msglen < 4 { return nil, errors.New("message too short") } m := &icmpMessage{Type: ipv4.ICMPType(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])} if msglen > 4 { var err error switch m.Type { case ipv4.ICMPTypeEcho, ipv4.ICMPTypeEchoReply: m.Body, err = parseICMPEcho(b[4:]) if err != nil { return nil, err } } } return m, nil } // imcpEcho represenets an ICMP echo request or reply message body. type icmpEcho struct { ID int // identifier Seq int // sequence number Data []byte // data } func (p *icmpEcho) Len() int { if p == nil { return 0 } return 4 + len(p.Data) } // Marshal returns the binary enconding of the ICMP echo request or // reply message body p. func (p *icmpEcho) Marshal() ([]byte, error) { b := make([]byte, 4+len(p.Data)) b[0], b[1] = byte(p.ID>>8), byte(p.ID) b[2], b[3] = byte(p.Seq>>8), byte(p.Seq) copy(b[4:], p.Data) return b, nil } // parseICMPEcho parses b as an ICMP echo request or reply message // body. func parseICMPEcho(b []byte) (*icmpEcho, error) { bodylen := len(b) p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])} if bodylen > 4 { p.Data = make([]byte, bodylen-4) copy(p.Data, b[4:]) } return p, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/genericopt_stub.go�������������������������������0000644�0000153�0000161�00000001155�12321735652�025241� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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) TOS() (int, error) { // TODO(mikio): Implement this return 0, errOpNoSupport } func (c *genericOpt) SetTOS(tos int) error { // TODO(mikio): Implement this return errOpNoSupport } func (c *genericOpt) TTL() (int, error) { // TODO(mikio): Implement this return 0, errOpNoSupport } func (c *genericOpt) SetTTL(ttl int) error { // TODO(mikio): Implement this return errOpNoSupport } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/multicastsockopt_test.go�������������������������0000644�0000153�0000161�00000006744�12321735652�026525� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd windows package ipv4_test import ( "code.google.com/p/go.net/ipv4" "net" "os" "runtime" "testing" ) type testMulticastConn interface { testUnicastConn MulticastTTL() (int, error) SetMulticastTTL(ttl int) error MulticastLoopback() (bool, error) SetMulticastLoopback(bool) error JoinGroup(*net.Interface, net.Addr) error LeaveGroup(*net.Interface, net.Addr) error } type multicastSockoptTest struct { tos int ttl int mcttl int mcloop bool gaddr net.IP } var multicastSockoptTests = []multicastSockoptTest{ {DiffServCS0 | NotECNTransport, 127, 128, false, net.IPv4(224, 0, 0, 249)}, // see RFC 4727 {DiffServAF11 | NotECNTransport, 255, 254, true, net.IPv4(224, 0, 0, 250)}, // see RFC 4727 } func TestUDPMulticastSockopt(t *testing.T) { if testing.Short() || !*testExternal { t.Skip("to avoid external network") } for _, tt := range multicastSockoptTests { c, err := net.ListenPacket("udp4", "0.0.0.0:0") if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() p := ipv4.NewPacketConn(c) testMulticastSockopt(t, tt, p, &net.UDPAddr{IP: tt.gaddr}) } } func TestIPMulticastSockopt(t *testing.T) { if testing.Short() || !*testExternal { t.Skip("to avoid external network") } if os.Getuid() != 0 { t.Skip("must be root") } for _, tt := range multicastSockoptTests { c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() r, _ := ipv4.NewRawConn(c) testMulticastSockopt(t, tt, r, &net.IPAddr{IP: tt.gaddr}) } } func testMulticastSockopt(t *testing.T, tt multicastSockoptTest, c testMulticastConn, gaddr net.Addr) { switch runtime.GOOS { case "windows": // IP_TOS option is supported on Windows 8 and beyond. t.Logf("skipping IP_TOS test on %q", runtime.GOOS) default: if err := c.SetTOS(tt.tos); err != nil { t.Fatalf("ipv4.PacketConn.SetTOS failed: %v", err) } if v, err := c.TOS(); err != nil { t.Fatalf("ipv4.PacketConn.TOS failed: %v", err) } else if v != tt.tos { t.Fatalf("Got unexpected TOS value %v; expected %v", v, tt.tos) } } if err := c.SetTTL(tt.ttl); err != nil { t.Fatalf("ipv4.PacketConn.SetTTL failed: %v", err) } if v, err := c.TTL(); err != nil { t.Fatalf("ipv4.PacketConn.TTL failed: %v", err) } else if v != tt.ttl { t.Fatalf("Got unexpected TTL value %v; expected %v", v, tt.ttl) } if err := c.SetMulticastTTL(tt.mcttl); err != nil { t.Fatalf("ipv4.PacketConn.SetMulticastTTL failed: %v", err) } if v, err := c.MulticastTTL(); err != nil { t.Fatalf("ipv4.PacketConn.MulticastTTL failed: %v", err) } else if v != tt.mcttl { t.Fatalf("Got unexpected MulticastTTL value %v; expected %v", v, tt.mcttl) } if err := c.SetMulticastLoopback(tt.mcloop); err != nil { t.Fatalf("ipv4.PacketConn.SetMulticastLoopback failed: %v", err) } if v, err := c.MulticastLoopback(); err != nil { t.Fatalf("ipv4.PacketConn.MulticastLoopback failed: %v", err) } else if v != tt.mcloop { t.Fatalf("Got unexpected MulticastLoopback value %v; expected %v", v, tt.mcloop) } if err := c.JoinGroup(nil, gaddr); err != nil { t.Fatalf("ipv4.PacketConn.JoinGroup(%v) failed: %v", gaddr, err) } if err := c.LeaveGroup(nil, gaddr); err != nil { t.Fatalf("ipv4.PacketConn.LeaveGroup(%v) failed: %v", gaddr, err) } } ����������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/iana.go������������������������������������������0000644�0000153�0000161�00000022417�12321735652�022761� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// go run gen.go // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT package ipv4 // Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19 const ( ICMPTypeEchoReply ICMPType = 0 // Echo Reply ICMPTypeDestinationUnreachable ICMPType = 3 // Destination Unreachable ICMPTypeRedirect ICMPType = 5 // Redirect ICMPTypeEcho ICMPType = 8 // Echo ICMPTypeRouterAdvertisement ICMPType = 9 // Router Advertisement ICMPTypeRouterSolicitation ICMPType = 10 // Router Solicitation ICMPTypeTimeExceeded ICMPType = 11 // Time Exceeded ICMPTypeParameterProblem ICMPType = 12 // Parameter Problem ICMPTypeTimestamp ICMPType = 13 // Timestamp ICMPTypeTimestampReply ICMPType = 14 // Timestamp Reply ICMPTypePhoturis ICMPType = 40 // Photuris ) // Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19 var icmpTypes = map[ICMPType]string{ 0: "echo reply", 3: "destination unreachable", 5: "redirect", 8: "echo", 9: "router advertisement", 10: "router solicitation", 11: "time exceeded", 12: "parameter problem", 13: "timestamp", 14: "timestamp reply", 40: "photuris", } // Protocol Numbers, Updated: 2013-02-17 const ( ianaProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number ianaProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option ianaProtocolICMP = 1 // Internet Control Message ianaProtocolIGMP = 2 // Internet Group Management ianaProtocolGGP = 3 // Gateway-to-Gateway ianaProtocolIPv4 = 4 // IPv4 encapsulation ianaProtocolST = 5 // Stream ianaProtocolTCP = 6 // Transmission Control ianaProtocolCBT = 7 // CBT ianaProtocolEGP = 8 // Exterior Gateway Protocol ianaProtocolIGP = 9 // any private interior gateway (used by Cisco for their IGRP) ianaProtocolBBNRCCMON = 10 // BBN RCC Monitoring ianaProtocolNVPII = 11 // Network Voice Protocol ianaProtocolPUP = 12 // PUP ianaProtocolARGUS = 13 // ARGUS ianaProtocolEMCON = 14 // EMCON ianaProtocolXNET = 15 // Cross Net Debugger ianaProtocolCHAOS = 16 // Chaos ianaProtocolUDP = 17 // User Datagram ianaProtocolMUX = 18 // Multiplexing ianaProtocolDCNMEAS = 19 // DCN Measurement Subsystems ianaProtocolHMP = 20 // Host Monitoring ianaProtocolPRM = 21 // Packet Radio Measurement ianaProtocolXNSIDP = 22 // XEROX NS IDP ianaProtocolTRUNK1 = 23 // Trunk-1 ianaProtocolTRUNK2 = 24 // Trunk-2 ianaProtocolLEAF1 = 25 // Leaf-1 ianaProtocolLEAF2 = 26 // Leaf-2 ianaProtocolRDP = 27 // Reliable Data Protocol ianaProtocolIRTP = 28 // Internet Reliable Transaction ianaProtocolISOTP4 = 29 // ISO Transport Protocol Class 4 ianaProtocolNETBLT = 30 // Bulk Data Transfer Protocol ianaProtocolMFENSP = 31 // MFE Network Services Protocol ianaProtocolMERITINP = 32 // MERIT Internodal Protocol ianaProtocolDCCP = 33 // Datagram Congestion Control Protocol ianaProtocol3PC = 34 // Third Party Connect Protocol ianaProtocolIDPR = 35 // Inter-Domain Policy Routing Protocol ianaProtocolXTP = 36 // XTP ianaProtocolDDP = 37 // Datagram Delivery Protocol ianaProtocolIDPRCMTP = 38 // IDPR Control Message Transport Proto ianaProtocolTPPP = 39 // TP++ Transport Protocol ianaProtocolIL = 40 // IL Transport Protocol ianaProtocolIPv6 = 41 // IPv6 encapsulation ianaProtocolSDRP = 42 // Source Demand Routing Protocol ianaProtocolIPv6Route = 43 // Routing Header for IPv6 ianaProtocolIPv6Frag = 44 // Fragment Header for IPv6 ianaProtocolIDRP = 45 // Inter-Domain Routing Protocol ianaProtocolRSVP = 46 // Reservation Protocol ianaProtocolGRE = 47 // Generic Routing Encapsulation ianaProtocolDSR = 48 // Dynamic Source Routing Protocol ianaProtocolBNA = 49 // BNA ianaProtocolESP = 50 // Encap Security Payload ianaProtocolAH = 51 // Authentication Header ianaProtocolINLSP = 52 // Integrated Net Layer Security TUBA ianaProtocolSWIPE = 53 // IP with Encryption ianaProtocolNARP = 54 // NBMA Address Resolution Protocol ianaProtocolMOBILE = 55 // IP Mobility ianaProtocolTLSP = 56 // Transport Layer Security Protocol using Kryptonet key management ianaProtocolSKIP = 57 // SKIP ianaProtocolIPv6ICMP = 58 // ICMP for IPv6 ianaProtocolIPv6NoNxt = 59 // No Next Header for IPv6 ianaProtocolIPv6Opts = 60 // Destination Options for IPv6 ianaProtocolCFTP = 62 // CFTP ianaProtocolSATEXPAK = 64 // SATNET and Backroom EXPAK ianaProtocolKRYPTOLAN = 65 // Kryptolan ianaProtocolRVD = 66 // MIT Remote Virtual Disk Protocol ianaProtocolIPPC = 67 // Internet Pluribus Packet Core ianaProtocolSATMON = 69 // SATNET Monitoring ianaProtocolVISA = 70 // VISA Protocol ianaProtocolIPCV = 71 // Internet Packet Core Utility ianaProtocolCPNX = 72 // Computer Protocol Network Executive ianaProtocolCPHB = 73 // Computer Protocol Heart Beat ianaProtocolWSN = 74 // Wang Span Network ianaProtocolPVP = 75 // Packet Video Protocol ianaProtocolBRSATMON = 76 // Backroom SATNET Monitoring ianaProtocolSUNND = 77 // SUN ND PROTOCOL-Temporary ianaProtocolWBMON = 78 // WIDEBAND Monitoring ianaProtocolWBEXPAK = 79 // WIDEBAND EXPAK ianaProtocolISOIP = 80 // ISO Internet Protocol ianaProtocolVMTP = 81 // VMTP ianaProtocolSECUREVMTP = 82 // SECURE-VMTP ianaProtocolVINES = 83 // VINES ianaProtocolTTP = 84 // TTP ianaProtocolIPTM = 84 // Protocol Internet Protocol Traffic Manager ianaProtocolNSFNETIGP = 85 // NSFNET-IGP ianaProtocolDGP = 86 // Dissimilar Gateway Protocol ianaProtocolTCF = 87 // TCF ianaProtocolEIGRP = 88 // EIGRP ianaProtocolOSPFIGP = 89 // OSPFIGP ianaProtocolSpriteRPC = 90 // Sprite RPC Protocol ianaProtocolLARP = 91 // Locus Address Resolution Protocol ianaProtocolMTP = 92 // Multicast Transport Protocol ianaProtocolAX25 = 93 // AX.25 Frames ianaProtocolIPIP = 94 // IP-within-IP Encapsulation Protocol ianaProtocolMICP = 95 // Mobile Internetworking Control Pro. ianaProtocolSCCSP = 96 // Semaphore Communications Sec. Pro. ianaProtocolETHERIP = 97 // Ethernet-within-IP Encapsulation ianaProtocolENCAP = 98 // Encapsulation Header ianaProtocolGMTP = 100 // GMTP ianaProtocolIFMP = 101 // Ipsilon Flow Management Protocol ianaProtocolPNNI = 102 // PNNI over IP ianaProtocolPIM = 103 // Protocol Independent Multicast ianaProtocolARIS = 104 // ARIS ianaProtocolSCPS = 105 // SCPS ianaProtocolQNX = 106 // QNX ianaProtocolAN = 107 // Active Networks ianaProtocolIPComp = 108 // IP Payload Compression Protocol ianaProtocolSNP = 109 // Sitara Networks Protocol ianaProtocolCompaqPeer = 110 // Compaq Peer Protocol ianaProtocolIPXinIP = 111 // IPX in IP ianaProtocolVRRP = 112 // Virtual Router Redundancy Protocol ianaProtocolPGM = 113 // PGM Reliable Transport Protocol ianaProtocolL2TP = 115 // Layer Two Tunneling Protocol ianaProtocolDDX = 116 // D-II Data Exchange (DDX) ianaProtocolIATP = 117 // Interactive Agent Transfer Protocol ianaProtocolSTP = 118 // Schedule Transfer Protocol ianaProtocolSRP = 119 // SpectraLink Radio Protocol ianaProtocolUTI = 120 // UTI ianaProtocolSMP = 121 // Simple Message Protocol ianaProtocolSM = 122 // SM ianaProtocolPTP = 123 // Performance Transparency Protocol ianaProtocolISIS = 124 // ISIS over IPv4 ianaProtocolFIRE = 125 // FIRE ianaProtocolCRTP = 126 // Combat Radio Transport Protocol ianaProtocolCRUDP = 127 // Combat Radio User Datagram ianaProtocolSSCOPMCE = 128 // SSCOPMCE ianaProtocolIPLT = 129 // IPLT ianaProtocolSPS = 130 // Secure Packet Shield ianaProtocolPIPE = 131 // Private IP Encapsulation within IP ianaProtocolSCTP = 132 // Stream Control Transmission Protocol ianaProtocolFC = 133 // Fibre Channel ianaProtocolRSVPE2EIGNORE = 134 // RSVP-E2E-IGNORE ianaProtocolMobilityHeader = 135 // Mobility Header ianaProtocolUDPLite = 136 // UDPLite ianaProtocolMPLSinIP = 137 // MPLS-in-IP ianaProtocolMANET = 138 // MANET Protocols ianaProtocolHIP = 139 // Host Identity Protocol ianaProtocolShim6 = 140 // Shim6 Protocol ianaProtocolWESP = 141 // Wrapped Encapsulating Security Payload ianaProtocolROHC = 142 // Robust Header Compression ianaProtocolReserved = 255 // Reserved ) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv4/sockopt_windows.go�������������������������������0000644�0000153�0000161�00000011513�12321735652�025300� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "net" "os" "syscall" "unsafe" ) // Please refer to the online manual; // http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx func ipv4TOS(fd syscall.Handle) (int, error) { var v int32 l := int32(4) if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_TOS, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil } func setIPv4TOS(fd syscall.Handle, v int) error { vv := int32(v) return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_TOS, (*byte)(unsafe.Pointer(&vv)), 4)) } func ipv4TTL(fd syscall.Handle) (int, error) { var v int32 l := int32(4) if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_TTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil } func setIPv4TTL(fd syscall.Handle, v int) error { vv := int32(v) return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_TTL, (*byte)(unsafe.Pointer(&vv)), 4)) } func ipv4MulticastTTL(fd syscall.Handle) (int, error) { var v int32 l := int32(4) if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil } func setIPv4MulticastTTL(fd syscall.Handle, v int) error { vv := int32(v) return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_TTL, (*byte)(unsafe.Pointer(&vv)), 4)) } func ipv4ReceiveTTL(fd syscall.Handle) (bool, error) { // NOTE: Not supported yet on any Windows return false, syscall.EWINDOWS } func setIPv4ReceiveTTL(fd syscall.Handle, v bool) error { // NOTE: Not supported yet on any Windows return syscall.EWINDOWS } func ipv4ReceiveDestinationAddress(fd syscall.Handle) (bool, error) { // TODO(mikio): Implement this for XP and beyond return false, syscall.EWINDOWS } func setIPv4ReceiveDestinationAddress(fd syscall.Handle, v bool) error { // TODO(mikio): Implement this for XP and beyond return syscall.EWINDOWS } func ipv4HeaderPrepend(fd syscall.Handle) (bool, error) { // TODO(mikio): Implement this for XP and beyond return false, syscall.EWINDOWS } func setIPv4HeaderPrepend(fd syscall.Handle, v bool) error { // TODO(mikio): Implement this for XP and beyond return syscall.EWINDOWS } func ipv4ReceiveInterface(fd syscall.Handle) (bool, error) { // TODO(mikio): Implement this for Vista and beyond return false, syscall.EWINDOWS } func setIPv4ReceiveInterface(fd syscall.Handle, v bool) error { // TODO(mikio): Implement this for Vista and beyond return syscall.EWINDOWS } func ipv4MulticastInterface(fd syscall.Handle) (*net.Interface, error) { var v [4]byte l := int32(4) if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, (*byte)(unsafe.Pointer(&v[0])), &l); err != nil { return nil, os.NewSyscallError("getsockopt", err) } return netIP4ToInterface(net.IPv4(v[0], v[1], v[2], v[3])) } func setIPv4MulticastInterface(fd syscall.Handle, ifi *net.Interface) error { ip, err := netInterfaceToIP4(ifi) if err != nil { return os.NewSyscallError("setsockopt", err) } var v [4]byte copy(v[:], ip.To4()) return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_IF, (*byte)(unsafe.Pointer(&v[0])), 4)) } func ipv4MulticastLoopback(fd syscall.Handle) (bool, error) { var v int32 l := int32(4) if err := syscall.Getsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv4MulticastLoopback(fd syscall.Handle, v bool) error { vv := int32(boolint(v)) return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_MULTICAST_LOOP, (*byte)(unsafe.Pointer(&vv)), 4)) } func joinIPv4Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error { mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}} if err := setSyscallIPMreq(&mreq, ifi); err != nil { return err } return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_ADD_MEMBERSHIP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq)))) } func leaveIPv4Group(fd syscall.Handle, ifi *net.Interface, grp net.IP) error { mreq := syscall.IPMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}} if err := setSyscallIPMreq(&mreq, ifi); err != nil { return err } return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIP, syscall.IP_DROP_MEMBERSHIP, (*byte)(unsafe.Pointer(&mreq)), int32(unsafe.Sizeof(mreq)))) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/.hgignore���������������������������������������������0000644�0000153�0000161�00000000140�12321735652�022430� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Add no patterns to .hgignore except for files generated by the build. syntax:glob last-change ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735652�021516� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/payload_noncmsg.go�������������������������������0000644�0000153�0000161�00000002426�12321735652�025226� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 plan9 windows package ipv6 import ( "net" "syscall" ) // ReadFrom reads a payload of the received IPv6 datagram, from the // endpoint c, copying the payload into b. It returns the number of // bytes copied into b, the control message cm and the source address // src of the received datagram. func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { if !c.ok() { return 0, nil, nil, syscall.EINVAL } if n, src, err = c.PacketConn.ReadFrom(b); err != nil { return 0, nil, nil, err } return } // WriteTo writes a payload of the IPv6 datagram, to the destination // address dst through the endpoint c, copying the payload from b. It // returns the number of bytes written. The control message cm allows // the IPv6 header fields and the datagram path to be specified. The // cm may be nil if control of the outgoing datagram is not required. func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { if !c.ok() { return 0, syscall.EINVAL } if dst == nil { return 0, errMissingAddress } return c.PacketConn.WriteTo(b, dst) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/syscall_linux_386.s������������������������������0000644�0000153�0000161�00000003060�12321735652�025172� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/genericopt_posix.go������������������������������0000644�0000153�0000161�00000002430�12321735652�025425� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 windows package ipv6 import "syscall" // TrafficClass returns the traffic class field value for outgoing // packets. func (c *genericOpt) TrafficClass() (int, error) { if !c.ok() { return 0, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return 0, err } return ipv6TrafficClass(fd) } // SetTrafficClass sets the traffic class field value for future // outgoing packets. func (c *genericOpt) SetTrafficClass(tclass int) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv6TrafficClass(fd, tclass) } // HopLimit returns the hop limit field value for outgoing packets. func (c *genericOpt) HopLimit() (int, error) { if !c.ok() { return 0, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return 0, err } return ipv6HopLimit(fd) } // SetHopLimit sets the hop limit field value for future outgoing // packets. func (c *genericOpt) SetHopLimit(hoplim int) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv6HopLimit(fd, hoplim) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/control.go���������������������������������������0000644�0000153�0000161�00000006046�12321735652�023533� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "errors" "fmt" "net" "sync" ) var ( errMissingAddress = errors.New("missing address") errInvalidConnType = errors.New("invalid conn type") errNoSuchInterface = errors.New("no such interface") ) // References: // // RFC 2292 Advanced Sockets API for IPv6 // http://tools.ietf.org/html/rfc2292 // RFC 2460 Internet Protocol, Version 6 (IPv6) Specification // http://tools.ietf.org/html/rfc2460 // RFC 3493 Basic Socket Interface Extensions for IPv6 // http://tools.ietf.org/html/rfc3493.html // RFC 3542 Advanced Sockets Application Program Interface (API) for IPv6 // http://tools.ietf.org/html/rfc3542 // // Note that RFC 3542 obsoltes RFC 2292 but OS X Snow Leopard and the // former still support RFC 2292 only. Please be aware that almost // all protocol implementations prohibit using a combination of RFC // 2292 and RFC 3542 for some practical reasons. type rawOpt struct { sync.Mutex cflags ControlFlags } func (c *rawOpt) set(f ControlFlags) { c.cflags |= f } func (c *rawOpt) clear(f ControlFlags) { c.cflags &^= f } func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 } // A ControlFlags reprensents per packet basis IP-level socket option // control flags. type ControlFlags uint const ( FlagTrafficClass ControlFlags = 1 << iota // pass the traffic class on the received packet FlagHopLimit // pass the hop limit on the received packet FlagSrc // pass the source address on the received packet FlagDst // pass the destination address on the received packet FlagInterface // pass the interface index on the received packet FlagPathMTU // pass the path MTU on the received packet path ) // A ControlMessage represents per packet basis IP-level socket // options. type ControlMessage struct { // Receiving socket options: SetControlMessage allows to // receive the options from the protocol stack using ReadFrom // method of PacketConn. // // Specifying socket options: ControlMessage for WriteTo // method of PacketConn allows to send the options to the // protocol stack. // TrafficClass int // traffic class, must be 1 <= value <= 255 when specifying HopLimit int // hop limit, must be 1 <= value <= 255 when specifying Src net.IP // source address, specifying only Dst net.IP // destination address, receiving only IfIndex int // interface index, must be 1 <= value when specifying NextHop net.IP // next hop address, specifying only MTU int // path MTU, receiving only } func (cm *ControlMessage) String() string { if cm == nil { return "<nil>" } return fmt.Sprintf("tclass: %#x, hoplim: %v, src: %v, dst: %v, ifindex: %v, nexthop: %v, mtu: %v", cm.TrafficClass, cm.HopLimit, cm.Src, cm.Dst, cm.IfIndex, cm.NextHop, cm.MTU) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/readwrite_test.go��������������������������������0000644�0000153�0000161�00000010271�12321735652�025073� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/dgramopt_posix.go��������������������������������0000644�0000153�0000161�00000010252�12321735652�025104� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 windows package ipv6 import ( "net" "syscall" ) // MulticastHopLimit returns the hop limit field value for outgoing // multicast packets. func (c *dgramOpt) MulticastHopLimit() (int, error) { if !c.ok() { return 0, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return 0, err } return ipv6MulticastHopLimit(fd) } // SetMulticastHopLimit sets the hop limit field value for future // outgoing multicast packets. func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv6MulticastHopLimit(fd, hoplim) } // MulticastInterface returns the default interface for multicast // packet transmissions. func (c *dgramOpt) MulticastInterface() (*net.Interface, error) { if !c.ok() { return nil, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return nil, err } return ipv6MulticastInterface(fd) } // SetMulticastInterface sets the default interface for future // multicast packet transmissions. func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv6MulticastInterface(fd, ifi) } // MulticastLoopback reports whether transmitted multicast packets // should be copied and send back to the originator. func (c *dgramOpt) MulticastLoopback() (bool, error) { if !c.ok() { return false, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return false, err } return ipv6MulticastLoopback(fd) } // SetMulticastLoopback sets whether transmitted multicast packets // should be copied and send back to the originator. func (c *dgramOpt) SetMulticastLoopback(on bool) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv6MulticastLoopback(fd, on) } // 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 { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } grp := netAddrToIP16(group) if grp == nil { return errMissingAddress } return joinIPv6Group(fd, ifi, grp) } // LeaveGroup leaves the group address group on the interface ifi. func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } grp := netAddrToIP16(group) if grp == nil { return errMissingAddress } return leaveIPv6Group(fd, ifi, grp) } // 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) { if !c.ok() { return false, 0, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return false, 0, err } return ipv6Checksum(fd) } // 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 { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv6Checksum(fd, on, offset) } // ICMPFilter returns an ICMP filter. func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) { if !c.ok() { return nil, syscall.EINVAL } fd, err := c.sysfd() if err != nil { return nil, err } return ipv6ICMPFilter(fd) } // SetICMPFilter deploys the ICMP filter. func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error { if !c.ok() { return syscall.EINVAL } fd, err := c.sysfd() if err != nil { return err } return setIPv6ICMPFilter(fd, f) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc2292_unix.go��������������������������0000644�0000153�0000161�00000007277�12321735652�025756� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/iana_test.go�������������������������������������0000644�0000153�0000161�00000002377�12321735652�024025� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// go run gentest.go // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT package ipv6_test // Differentiated Services Field Codepoints (DSCP), Updated: 2013-06-25 const ( DiffServCS0 = 0x0 // CS0 DiffServCS1 = 0x20 // CS1 DiffServCS2 = 0x40 // CS2 DiffServCS3 = 0x60 // CS3 DiffServCS4 = 0x80 // CS4 DiffServCS5 = 0xa0 // CS5 DiffServCS6 = 0xc0 // CS6 DiffServCS7 = 0xe0 // CS7 DiffServAF11 = 0x28 // AF11 DiffServAF12 = 0x30 // AF12 DiffServAF13 = 0x38 // AF13 DiffServAF21 = 0x48 // AF21 DiffServAF22 = 0x50 // AF22 DiffServAF23 = 0x58 // AF23 DiffServAF31 = 0x68 // AF31 DiffServAF32 = 0x70 // AF32 DiffServAF33 = 0x78 // AF33 DiffServAF41 = 0x88 // AF41 DiffServAF42 = 0x90 // AF42 DiffServAF43 = 0x98 // AF43 DiffServEFPHB = 0xb8 // EF PHB DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT ) // IPv4 TOS Byte and IPv6 Traffic Class Octet, Updated: 2001-09-06 const ( NotECNTransport = 0x0 // Not-ECT (Not ECN-Capable Transport) ECNTransport1 = 0x1 // ECT(1) (ECN-Capable Transport(1)) ECNTransport0 = 0x2 // ECT(0) (ECN-Capable Transport(0)) CongestionExperienced = 0x3 // CE (Congestion Experienced) ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/gen.go�������������������������������������������0000644�0000153�0000161�00000012512�12321735652�022617� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ignore // This program generates internet protocol constatns and tables by // reading IANA protocol registries. // // Usage: // go run gen.go > iana.go package main import ( "bytes" "encoding/xml" "fmt" "go/format" "io" "net/http" "os" "strconv" "strings" ) var registries = []struct { url string parse func(io.Writer, io.Reader) error }{ { "http://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml", parseICMPv6Parameters, }, { "http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml", parseProtocolNumbers, }, } func main() { var bb bytes.Buffer fmt.Fprintf(&bb, "// go run gen.go\n") fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n") fmt.Fprintf(&bb, "package ipv6\n\n") for _, r := range registries { resp, err := http.Get(r.url) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url) os.Exit(1) } if err := r.parse(&bb, resp.Body); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Fprintf(&bb, "\n") } b, err := format.Source(bb.Bytes()) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } os.Stdout.Write(b) } func parseICMPv6Parameters(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var icp icmpv6Parameters if err := dec.Decode(&icp); err != nil { return err } prs := icp.escape() fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated) fmt.Fprintf(w, "const (\n") for _, pr := range prs { if pr.Name == "" { continue } fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Name, pr.Value) fmt.Fprintf(w, "// %s\n", pr.OrigName) } fmt.Fprintf(w, ")\n\n") fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated) fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n") for _, pr := range prs { if pr.Name == "" { continue } fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigName)) } fmt.Fprintf(w, "}\n") return nil } type icmpv6Parameters struct { 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 { OrigName string Name string Value int } func (icp *icmpv6Parameters) escape() []canonICMPv6ParamRecord { id := -1 for i, r := range icp.Registries { if strings.Contains(r.Title, "Type") || strings.Contains(r.Title, "type") { id = i break } } if id < 0 { return nil } prs := make([]canonICMPv6ParamRecord, len(icp.Registries[id].Records)) sr := strings.NewReplacer( "Messages", "", "Message", "", "ICMP", "", "+", "P", "-", "", "/", "", ".", "", " ", "", ) for i, pr := range icp.Registries[id].Records { if strings.Contains(pr.Name, "Reserved") || strings.Contains(pr.Name, "Unassigned") || strings.Contains(pr.Name, "Deprecated") || strings.Contains(pr.Name, "Experiment") || strings.Contains(pr.Name, "experiment") { continue } ss := strings.Split(pr.Name, "\n") if len(ss) > 1 { prs[i].Name = strings.Join(ss, " ") } else { prs[i].Name = ss[0] } s := strings.TrimSpace(prs[i].Name) prs[i].OrigName = s prs[i].Name = sr.Replace(s) prs[i].Value, _ = strconv.Atoi(pr.Value) } return prs } func parseProtocolNumbers(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var pn protocolNumbers if err := dec.Decode(&pn); err != nil { return err } prs := pn.escape() fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated) fmt.Fprintf(w, "const (\n") for _, pr := range prs { if pr.Name == "" { continue } fmt.Fprintf(w, "ianaProtocol%s = %d", pr.Name, pr.Value) s := pr.Descr if s == "" { s = pr.OrigName } fmt.Fprintf(w, "// %s\n", s) } fmt.Fprintf(w, ")\n") return nil } 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 []struct { Value string `xml:"value"` Name string `xml:"name"` Descr string `xml:"description"` } `xml:"registry>record"` } type canonProtocolRecord struct { OrigName string Name string Descr string Value int } func (pn *protocolNumbers) escape() []canonProtocolRecord { prs := make([]canonProtocolRecord, len(pn.Records)) sr := strings.NewReplacer( "-in-", "in", "-within-", "within", "-over-", "over", "+", "P", "-", "", "/", "", ".", "", " ", "", ) for i, pr := range pn.Records { prs[i].OrigName = pr.Name s := strings.TrimSpace(pr.Name) switch pr.Name { case "ISIS over IPv4": prs[i].Name = "ISIS" case "manet": prs[i].Name = "MANET" default: prs[i].Name = sr.Replace(s) } ss := strings.Split(pr.Descr, "\n") for i := range ss { ss[i] = strings.TrimSpace(ss[i]) } if len(ss) > 1 { prs[i].Descr = strings.Join(ss, " ") } else { prs[i].Descr = ss[0] } prs[i].Value, _ = strconv.Atoi(pr.Value) } return prs } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/syscall_unix.go����������������������������������0000644�0000153�0000161�00000001451�12321735652�024563� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_unix.go��������������������������0000644�0000153�0000161�00000007365�12321735652�025762� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "net" "os" "unsafe" ) func ipv6TrafficClass(fd int) (int, error) { 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 int(v), nil } func setIPv6TrafficClass(fd, v int) error { vv := int32(v) return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptTrafficClass, uintptr(unsafe.Pointer(&vv)), 4)) } func ipv6HopLimit(fd int) (int, error) { 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 int(v), nil } func setIPv6HopLimit(fd, v int) error { vv := int32(v) return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, uintptr(unsafe.Pointer(&vv)), 4)) } func ipv6Checksum(fd int) (bool, int, error) { 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, int(v), nil } func ipv6MulticastHopLimit(fd int) (int, error) { 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 int(v), nil } func setIPv6MulticastHopLimit(fd, v int) error { vv := int32(v) return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, uintptr(unsafe.Pointer(&vv)), 4)) } func ipv6MulticastInterface(fd int) (*net.Interface, error) { 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(int(v)) if err != nil { return nil, err } return ifi, nil } func setIPv6MulticastInterface(fd int, ifi *net.Interface) error { var v int32 if ifi != nil { v = int32(ifi.Index) } return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastInterface, uintptr(unsafe.Pointer(&v)), 4)) } func ipv6MulticastLoopback(fd int) (bool, error) { 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 { 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 := sysMulticastReq{} copy(mreq.IP[:], grp) if ifi != nil { mreq.IfIndex = uint32(ifi.Index) } 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 := sysMulticastReq{} copy(mreq.IP[:], grp) if ifi != nil { mreq.IfIndex = uint32(ifi.Index) } return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptLeaveGroup, uintptr(unsafe.Pointer(&mreq)), sysSizeofMulticastReq)) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sys_bsd.go���������������������������������������0000644�0000153�0000161�00000002322�12321735652�023512� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sys_linux.go�������������������������������������0000644�0000153�0000161�00000002216�12321735652�024103� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_unix.go��������������������������0000644�0000153�0000161�00000012450�12321735652�025742� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/doc.go�������������������������������������������0000644�0000153�0000161�00000013654�12321735652�022623� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 implements IP-level socket options for the Internet // Protocol version 6. // // The package provides IP-level socket options that allow // manipulation of IPv6 facilities. The IPv6 and socket options for // IPv6 are defined in RFC 2460, RFC 3493 and RFC 3542. // // // Unicasting // // The options for unicasting are available for net.TCPConn, // net.UDPConn and net.IPConn which are created as network connections // that use the IPv6 transport. When a single TCP connection carrying // a data flow of multiple packets needs to indicate the flow is // important, ipv6.Conn is used to set the traffic class field on the // IPv6 header for each packet. // // ln, err := net.Listen("tcp6", "[::]:1024") // if err != nil { // // error handling // } // defer ln.Close() // for { // c, err := ln.Accept() // if err != nil { // // error handling // } // go func(c net.Conn) { // defer c.Close() // // The outgoing packets will be labeled DiffServ assured forwarding // class 1 low drop precedence, as known as AF11 packets. // // if err := ipv6.NewConn(c).SetTrafficClass(DiffServAF11); err != nil { // // error handling // } // if _, err := c.Write(data); err != nil { // // error handling // } // }(c) // } // // // Multicasting // // The options for multicasting are available for net.UDPConn and // net.IPconn which are created as network connections that use the // IPv6 transport. A few network facilities must be prepared before // you begin multicasting, at a minimum joining network interfaces and // multicast groups. // // en0, err := net.InterfaceByName("en0") // if err != nil { // // error handling // } // en1, err := net.InterfaceByIndex(911) // if err != nil { // // error handling // } // group := net.ParseIP("ff02::114") // // First, an application listens to an appropriate address with an // appropriate service port. // // c, err := net.ListenPacket("udp6", "[::]:1024") // if err != nil { // // error handling // } // defer c.Close() // // Second, the application joins multicast groups, starts listening to // the groups on the specified network interfaces. Note that the // service port for transport layer protocol does not matter with this // operation as joining groups affects only network and link layer // protocols, such as IPv6 and Ethernet. // // p := ipv6.NewPacketConn(c) // if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil { // // error handling // } // if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil { // // error handling // } // // The application might set per packet control message transmissions // between the protocol stack within the kernel. When the application // needs a destination address on an incoming packet, // SetControlMessage of ipv6.PacketConn is used to enable control // message transmissons. // // if err := p.SetControlMessage(ipv6.FlagDst, true); err != nil { // // error handling // } // // The application could identify whether the received packets are // of interest by using the control message that contains the // destination address of the received packet. // // b := make([]byte, 1500) // for { // n, rcm, src, err := p.ReadFrom(b) // if err != nil { // // error handling // } // if rcm.Dst.IsMulticast() { // if rcm.Dst.Equal(group) // // joined group, do something // } else { // // unknown group, discard // continue // } // } // // The application can also send both unicast and multicast packets. // // p.SetTrafficClass(DiffServCS0) // p.SetHopLimit(16) // if _, err := p.WriteTo(data[:n], nil, src); err != nil { // // error handling // } // dst := &net.UDPAddr{IP: group, Port: 1024} // wcm := ipv6.ControlMessage{TrafficClass: DiffServCS7, HopLimit: 1} // for _, ifi := range []*net.Interface{en0, en1} { // wcm.IfIndex = ifi.Index // if _, err := p.WriteTo(data[:n], &wcm, dst); err != nil { // // error handling // } // } // } // // // More multicasting // // An application that uses PacketConn may join multiple multicast // groups. For example, a UDP listener with port 1024 might join two // different groups across over two different network interfaces by // using: // // c, err := net.ListenPacket("udp6", "[::]:1024") // if err != nil { // // error handling // } // defer c.Close() // p := ipv6.NewPacketConn(c) // if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}); err != nil { // // error handling // } // if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil { // // error handling // } // if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil { // // error handling // } // // It is possible for multiple UDP listeners that listen on the same // UDP port to join the same multicast group. The net package will // provide a socket that listens to a wildcard address with reusable // UDP port when an appropriate multicast address prefix is passed to // the net.ListenPacket or net.ListenUDP. // // c1, err := net.ListenPacket("udp6", "[ff02::]:1024") // if err != nil { // // error handling // } // defer c1.Close() // c2, err := net.ListenPacket("udp6", "[ff02::]:1024") // if err != nil { // // error handling // } // defer c2.Close() // p1 := ipv6.NewPacketConn(c1) // if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil { // // error handling // } // p2 := ipv6.NewPacketConn(c2) // if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil { // // error handling // } // // Also it is possible for the application to leave or rejoin a // multicast group on the network interface. // // if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil { // // error handling // } // if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff01::114")}); err != nil { // // error handling // } package ipv6 ������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_test.go����������������������������������0000644�0000153�0000161�00000006142�12321735652�024571� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "code.google.com/p/go.net/ipv6" "net" "os" "runtime" "testing" ) var supportsIPv6 bool func init() { if ln, err := net.Listen("tcp6", "[::1]:0"); err == nil { ln.Close() supportsIPv6 = true } } var condFatalf = func() func(*testing.T, string, ...interface{}) { // A few APIs are not implemented yet on some platforms. switch runtime.GOOS { case "darwin", "dragonfly", "plan9", "solaris", "windows": return (*testing.T).Logf } return (*testing.T).Fatalf }() func TestConnInitiatorPathMTU(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") } ln, err := net.Listen("tcp6", "[::1]:0") if err != nil { t.Fatalf("net.Listen failed: %v", err) } defer ln.Close() done := make(chan bool) go acceptor(t, ln, done) c, err := net.Dial("tcp6", ln.Addr().String()) if err != nil { t.Fatalf("net.Dial failed: %v", err) } defer c.Close() if pmtu, err := ipv6.NewConn(c).PathMTU(); err != nil { condFatalf(t, "ipv6.Conn.PathMTU failed: %v", err) } else { t.Logf("path mtu for %v: %v", c.RemoteAddr(), pmtu) } <-done } func TestConnResponderPathMTU(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") } ln, err := net.Listen("tcp6", "[::1]:0") if err != nil { t.Fatalf("net.Listen failed: %v", err) } defer ln.Close() done := make(chan bool) go connector(t, "tcp6", ln.Addr().String(), done) c, err := ln.Accept() if err != nil { t.Fatalf("net.Accept failed: %v", err) } defer c.Close() if pmtu, err := ipv6.NewConn(c).PathMTU(); err != nil { condFatalf(t, "ipv6.Conn.PathMTU failed: %v", err) } else { t.Logf("path mtu for %v: %v", c.RemoteAddr(), pmtu) } <-done } func TestPacketConnChecksum(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") } if os.Getuid() != 0 { t.Skip("must be root") } c, err := net.ListenPacket("ip6:89", "::") // OSPF for IPv6 if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() p := ipv6.NewPacketConn(c) offset := 12 // see RFC 5340 for _, toggle := range []bool{false, true} { if err := p.SetChecksum(toggle, offset); err != nil { if toggle { t.Fatalf("ipv6.PacketConn.SetChecksum(%v, %v) failed: %v", toggle, offset, err) } else { // Some platforms never allow to disable the kernel // checksum processing. t.Logf("ipv6.PacketConn.SetChecksum(%v, %v) failed: %v", toggle, offset, err) } } if on, offset, err := p.Checksum(); err != nil { t.Fatalf("ipv6.PacketConn.Checksum failed: %v", err) } else { t.Logf("kernel checksum processing enabled=%v, offset=%v", on, offset) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/helper_windows.go��������������������������������0000644�0000153�0000161�00000002127�12321735652�025100� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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" "reflect" "syscall" ) func (c *genericOpt) sysfd() (syscall.Handle, error) { switch p := c.Conn.(type) { case *net.TCPConn, *net.UDPConn, *net.IPConn: return sysfd(p) } return syscall.InvalidHandle, errInvalidConnType } func (c *dgramOpt) sysfd() (syscall.Handle, error) { switch p := c.PacketConn.(type) { case *net.UDPConn, *net.IPConn: return sysfd(p.(net.Conn)) } return syscall.InvalidHandle, errInvalidConnType } func (c *payloadHandler) sysfd() (syscall.Handle, error) { return sysfd(c.PacketConn.(net.Conn)) } func sysfd(c net.Conn) (syscall.Handle, error) { cv := reflect.ValueOf(c) switch ce := cv.Elem(); ce.Kind() { case reflect.Struct: netfd := ce.FieldByName("conn").FieldByName("fd") switch fe := netfd.Elem(); fe.Kind() { case reflect.Struct: fd := fe.FieldByName("sysfd") return syscall.Handle(fd.Uint()), nil } } return syscall.InvalidHandle, errInvalidConnType } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/multicast_test.go��������������������������������0000644�0000153�0000161�00000014371�12321735652�025117� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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" "os" "runtime" "testing" "time" ) func TestPacketConnReadWriteMulticastUDP(t *testing.T) { switch runtime.GOOS { 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 "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } if !supportsIPv6 { t.Skip("ipv6 is not supported") } ifi := loopbackInterface() if ifi == nil { t.Skipf("not available on %q", runtime.GOOS) } c, err := net.ListenPacket("udp6", "[ff02::114]:0") // see RFC 4727 if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() _, port, err := net.SplitHostPort(c.LocalAddr().String()) if err != nil { t.Fatalf("net.SplitHostPort failed: %v", err) } dst, err := net.ResolveUDPAddr("udp6", "[ff02::114]:"+port) // see RFC 4727 if err != nil { t.Fatalf("net.ResolveUDPAddr failed: %v", err) } 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.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 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 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) } } } func TestPacketConnReadWriteMulticastICMP(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") } if os.Getuid() != 0 { t.Skip("must be root") } ifi := loopbackInterface() if ifi == nil { t.Skipf("not available on %q", runtime.GOOS) } c, err := net.ListenPacket("ip6:ipv6-icmp", "::") if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() dst, err := net.ResolveIPAddr("ip6", "ff02::114") // see RFC 4727 if err != nil { 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.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU var f ipv6.ICMPFilter f.SetAll(true) f.Set(ipv6.ICMPTypeEchoReply, false) if err := p.SetICMPFilter(&f); err != nil { 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(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 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 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(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) } } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/syscall_nosplit4_linux_386.go��������������������0000644�0000153�0000161�00000000533�12321735652�027173� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/syscall_linux_386.go�����������������������������0000644�0000153�0000161�00000002502�12321735652�025335� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sys_darwin.go������������������������������������0000644�0000153�0000161�00000002527�12321735652�024235� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/endpoint.go��������������������������������������0000644�0000153�0000161�00000005557�12321735652�023701� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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" "time" ) // A Conn represents a network endpoint that uses IPv6 transport. // It allows to set basic IP-level socket options such as traffic // class and hop limit. type Conn struct { genericOpt } type genericOpt struct { net.Conn } func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil } // PathMTU returns a path MTU value for the destination associated // with the endpoint. func (c *Conn) PathMTU() (int, error) { if !c.genericOpt.ok() { return 0, syscall.EINVAL } fd, err := c.genericOpt.sysfd() if err != nil { return 0, err } return ipv6PathMTU(fd) } // NewConn returns a new Conn. func NewConn(c net.Conn) *Conn { return &Conn{ genericOpt: genericOpt{Conn: c}, } } // A PacketConn represents a packet network endpoint that uses IPv6 // transport. It is used to control several IP-level socket options // including IPv6 header manipulation. It also provides datagram // based network I/O methods specific to the IPv6 and higher layer // protocols such as OSPF, GRE, and UDP. type PacketConn struct { genericOpt dgramOpt payloadHandler } type dgramOpt struct { net.PacketConn } func (c *dgramOpt) ok() bool { return c != nil && c.PacketConn != nil } // SetControlMessage allows to receive the per packet basis IP-level // socket options. func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error { if !c.payloadHandler.ok() { return syscall.EINVAL } fd, err := c.payloadHandler.sysfd() if err != nil { return err } return setControlMessage(fd, &c.payloadHandler.rawOpt, cf, on) } // SetDeadline sets the read and write deadlines associated with the // endpoint. func (c *PacketConn) SetDeadline(t time.Time) error { if !c.payloadHandler.ok() { return syscall.EINVAL } return c.payloadHandler.SetDeadline(t) } // SetReadDeadline sets the read deadline associated with the // endpoint. func (c *PacketConn) SetReadDeadline(t time.Time) error { if !c.payloadHandler.ok() { return syscall.EINVAL } return c.payloadHandler.SetReadDeadline(t) } // SetWriteDeadline sets the write deadline associated with the // endpoint. func (c *PacketConn) SetWriteDeadline(t time.Time) error { if !c.payloadHandler.ok() { return syscall.EINVAL } return c.payloadHandler.SetWriteDeadline(t) } // Close closes the endpoint. func (c *PacketConn) Close() error { if !c.payloadHandler.ok() { return syscall.EINVAL } return c.payloadHandler.Close() } // NewPacketConn returns a new PacketConn using c as its underlying // transport. func NewPacketConn(c net.PacketConn) *PacketConn { return &PacketConn{ genericOpt: genericOpt{Conn: c.(net.Conn)}, dgramOpt: dgramOpt{PacketConn: c}, payloadHandler: payloadHandler{PacketConn: c}, } } �������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sys_windows.go�����������������������������������0000644�0000153�0000161�00000001306�12321735652�024435� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/icmp_linux.go������������������������������������0000644�0000153�0000161�00000001210�12321735652�024206� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 sysICMPFilter struct { Data [8]uint32 } func (f *sysICMPFilter) set(typ ICMPType, block bool) { if block { f.Data[typ>>5] |= 1 << (uint32(typ) & 31) } else { f.Data[typ>>5] &^= 1 << (uint32(typ) & 31) } } func (f *sysICMPFilter) setAll(block bool) { for i := range f.Data { if block { f.Data[i] = 1<<32 - 1 } else { f.Data[i] = 0 } } } func (f *sysICMPFilter) willBlock(typ ICMPType) bool { return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0 } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/gentest.go���������������������������������������0000644�0000153�0000161�00000010371�12321735652�023520� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ignore // This program generates internet protocol constants by reading IANA // protocol registries. // // Usage: // go run gentest.go > iana_test.go package main import ( "bytes" "encoding/xml" "fmt" "go/format" "io" "net/http" "os" "strconv" "strings" ) var registries = []struct { url string parse func(io.Writer, io.Reader) error }{ { "http://www.iana.org/assignments/dscp-registry/dscp-registry.xml", parseDSCPRegistry, }, { "http://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml", parseTOSTCByte, }, } func main() { var bb bytes.Buffer 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 { resp, err := http.Get(r.url) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url) os.Exit(1) } if err := r.parse(&bb, resp.Body); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Fprintf(&bb, "\n") } b, err := format.Source(bb.Bytes()) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } os.Stdout.Write(b) } func parseDSCPRegistry(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var dr dscpRegistry if err := dec.Decode(&dr); err != nil { return err } drs := dr.escape() fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated) fmt.Fprintf(w, "const (\n") for _, dr := range drs { fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value) fmt.Fprintf(w, "// %s\n", dr.OrigName) } fmt.Fprintf(w, ")\n") return nil } 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 []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 { OrigName string Name string Value int } func (drr *dscpRegistry) escape() []canonDSCPRecord { drs := make([]canonDSCPRecord, len(drr.Records)) sr := strings.NewReplacer( "+", "", "-", "", "/", "", ".", "", " ", "", ) for i, dr := range drr.Records { s := strings.TrimSpace(dr.Name) drs[i].OrigName = s drs[i].Name = sr.Replace(s) n, err := strconv.ParseUint(dr.Space, 2, 8) if err != nil { continue } drs[i].Value = int(n) << 2 } return drs } func parseTOSTCByte(w io.Writer, r io.Reader) error { dec := xml.NewDecoder(r) var ttb tosTCByte if err := dec.Decode(&ttb); err != nil { return err } trs := ttb.escape() fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated) fmt.Fprintf(w, "const (\n") for _, tr := range trs { fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value) fmt.Fprintf(w, "// %s\n", tr.OrigKeyword) } fmt.Fprintf(w, ")\n") return nil } 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 []struct { Binary string `xml:"binary"` Keyword string `xml:"keyword"` } `xml:"registry>record"` } type canonTOSTCByteRecord struct { OrigKeyword string Keyword string Value int } func (ttb *tosTCByte) escape() []canonTOSTCByteRecord { trs := make([]canonTOSTCByteRecord, len(ttb.Records)) sr := strings.NewReplacer( "Capable", "", "(", "", ")", "", "+", "", "-", "", "/", "", ".", "", " ", "", ) for i, tr := range ttb.Records { s := strings.TrimSpace(tr.Keyword) trs[i].OrigKeyword = s ss := strings.Split(s, " ") if len(ss) > 1 { trs[i].Keyword = strings.Join(ss[1:], " ") } else { trs[i].Keyword = ss[0] } trs[i].Keyword = sr.Replace(trs[i].Keyword) n, err := strconv.ParseUint(tr.Binary, 2, 8) if err != nil { continue } trs[i].Value = int(n) } return trs } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/mocktransponder_test.go��������������������������0000644�0000153�0000161�00000003353�12321735652�026321� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "net" "testing" ) func isLinkLocalUnicast(ip net.IP) bool { return ip.To4() == nil && ip.To16() != nil && ip.IsLinkLocalUnicast() } func loopbackInterface() *net.Interface { ift, err := net.Interfaces() if err != nil { return nil } for _, ifi := range ift { 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 isLinkLocalUnicast(ifa.IP) { return &ifi } case *net.IPNet: if isLinkLocalUnicast(ifa.IP) { return &ifi } } } } return nil } func isMulticastAvailable(ifi *net.Interface) (net.IP, bool) { 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 } for _, ifa := range ifat { switch ifa := ifa.(type) { case *net.IPAddr: if isLinkLocalUnicast(ifa.IP) { return ifa.IP, true } case *net.IPNet: if isLinkLocalUnicast(ifa.IP) { return ifa.IP, true } } } return nil, false } func connector(t *testing.T, network, addr string, done chan<- bool) { defer func() { done <- true }() c, err := net.Dial(network, addr) if err != nil { t.Errorf("net.Dial failed: %v", err) return } c.Close() } func acceptor(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 } c.Close() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/icmp_test.go�������������������������������������0000644�0000153�0000161�00000004326�12321735652�024041� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "code.google.com/p/go.net/ipv6" "net" "os" "reflect" "runtime" "sync" "testing" ) var icmpStringTests = []struct { in ipv6.ICMPType out string }{ {ipv6.ICMPTypeDestinationUnreachable, "destination unreachable"}, {256, "<nil>"}, } 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 "dragonfly", "plan9", "solaris", "windows": t.Skipf("not supported on %q", runtime.GOOS) } var f ipv6.ICMPFilter for _, toggle := range []bool{false, true} { f.SetAll(toggle) var wg sync.WaitGroup for _, typ := range []ipv6.ICMPType{ ipv6.ICMPTypeDestinationUnreachable, ipv6.ICMPTypeEchoReply, ipv6.ICMPTypeNeighborSolicitation, ipv6.ICMPTypeDuplicateAddressConfirmation, } { wg.Add(1) go func(typ ipv6.ICMPType) { defer wg.Done() f.Set(typ, false) if f.WillBlock(typ) { t.Errorf("ipv6.ICMPFilter.Set(%v, false) failed", typ) } f.Set(typ, true) if !f.WillBlock(typ) { t.Errorf("ipv6.ICMPFilter.Set(%v, true) failed", typ) } }(typ) } wg.Wait() } } func TestSetICMPFilter(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") } if os.Getuid() != 0 { t.Skip("must be root") } c, err := net.ListenPacket("ip6:ipv6-icmp", "::1") if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() p := ipv6.NewPacketConn(c) var f ipv6.ICMPFilter f.SetAll(true) f.Set(ipv6.ICMPTypeEchoRequest, false) f.Set(ipv6.ICMPTypeEchoReply, false) if err := p.SetICMPFilter(&f); err != nil { t.Fatalf("ipv6.PacketConn.SetICMPFilter failed: %v", err) } kf, err := p.ICMPFilter() if err != nil { t.Fatalf("ipv6.PacketConn.ICMPFilter failed: %v", err) } if !reflect.DeepEqual(kf, &f) { t.Fatalf("got unexpected filter %#v; expected %#v", kf, f) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/helper.go����������������������������������������0000644�0000153�0000161�00000001116�12321735652�023323� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "errors" "net" ) var errOpNoSupport = errors.New("operation not supported") func boolint(b bool) int { if b { return 1 } return 0 } func netAddrToIP16(a net.Addr) net.IP { switch v := a.(type) { case *net.UDPAddr: if ip := v.IP.To16(); ip != nil && ip.To4() == nil { return ip } case *net.IPAddr: if ip := v.IP.To16(); ip != nil && ip.To4() == nil { return ip } } return nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/helper_stub.go�����������������������������������0000644�0000153�0000161�00000001016�12321735652�024357� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc2292_unix.go��������������������������0000644�0000153�0000161�00000004140�12321735652�025742� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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)) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_windows.go�����������������������0000644�0000153�0000161�00000003040�12321735652�026446� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ipv6ReceiveTrafficClass(fd syscall.Handle) (bool, error) { // TODO(mikio): Implement this return false, syscall.EWINDOWS } func setIPv6ReceiveTrafficClass(fd syscall.Handle, v bool) error { // TODO(mikio): Implement this return syscall.EWINDOWS } func ipv6ReceiveHopLimit(fd syscall.Handle) (bool, error) { // TODO(mikio): Implement this return false, syscall.EWINDOWS } func setIPv6ReceiveHopLimit(fd syscall.Handle, v bool) error { // TODO(mikio): Implement this return syscall.EWINDOWS } func ipv6ReceivePacketInfo(fd syscall.Handle) (bool, error) { // TODO(mikio): Implement this return false, syscall.EWINDOWS } func setIPv6ReceivePacketInfo(fd syscall.Handle, v bool) error { // TODO(mikio): Implement this return syscall.EWINDOWS } func ipv6PathMTU(fd syscall.Handle) (int, error) { // TODO(mikio): Implement this return 0, syscall.EWINDOWS } func ipv6ReceivePathMTU(fd syscall.Handle) (bool, error) { // TODO(mikio): Implement this return false, syscall.EWINDOWS } func setIPv6ReceivePathMTU(fd syscall.Handle, v bool) error { // TODO(mikio): Implement this return syscall.EWINDOWS } func ipv6ICMPFilter(fd syscall.Handle) (*ICMPFilter, error) { // TODO(mikio): Implement this return nil, syscall.EWINDOWS } func setIPv6ICMPFilter(fd syscall.Handle, f *ICMPFilter) error { // TODO(mikio): Implement this return syscall.EWINDOWS } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/icmp.go������������������������������������������0000644�0000153�0000161�00000001724�12321735652�023001� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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" // An ICMPType represents a type of ICMP message. type ICMPType int func (typ ICMPType) String() string { s, ok := icmpTypes[typ] if !ok { return "<nil>" } return s } // An ICMPFilter represents an ICMP message filter for incoming // packets. type ICMPFilter struct { mu sync.RWMutex sysICMPFilter } // Set sets the ICMP type and filter action to the filter. func (f *ICMPFilter) Set(typ ICMPType, block bool) { f.mu.Lock() f.set(typ, block) f.mu.Unlock() } // SetAll sets the filter action to the filter. func (f *ICMPFilter) SetAll(block bool) { f.mu.Lock() 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() ok := f.willBlock(typ) f.mu.RUnlock() return ok } ��������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/syscall_nosplit7_linux_386.go��������������������0000644�0000153�0000161�00000000542�12321735652�027176� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ��������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/payload_cmsg.go����������������������������������0000644�0000153�0000161�00000003707�12321735652�024516� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 !plan9,!windows package ipv6 import ( "net" "syscall" ) // ReadFrom reads a payload of the received IPv6 datagram, from the // endpoint c, copying the payload into b. It returns the number of // bytes copied into b, the control message cm and the source address // src of the received datagram. func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { if !c.ok() { return 0, nil, nil, syscall.EINVAL } oob := newControlMessage(&c.rawOpt) var oobn int switch c := c.PacketConn.(type) { case *net.UDPConn: if n, oobn, _, src, err = c.ReadMsgUDP(b, oob); err != nil { return 0, nil, nil, err } case *net.IPConn: if n, oobn, _, src, err = c.ReadMsgIP(b, oob); err != nil { return 0, nil, nil, err } default: return 0, nil, nil, errInvalidConnType } if cm, err = parseControlMessage(oob[:oobn]); err != nil { return 0, nil, nil, err } if cm != nil { cm.Src = netAddrToIP16(src) } return } // WriteTo writes a payload of the IPv6 datagram, to the destination // address dst through the endpoint c, copying the payload from b. It // returns the number of bytes written. The control message cm allows // the IPv6 header fields and the datagram path to be specified. The // cm may be nil if control of the outgoing datagram is not required. func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { if !c.ok() { return 0, syscall.EINVAL } oob := marshalControlMessage(cm) if dst == nil { return 0, errMissingAddress } switch c := c.PacketConn.(type) { case *net.UDPConn: n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr)) case *net.IPConn: n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr)) default: return 0, errInvalidConnType } if err != nil { return 0, err } return } ���������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_stub.go��������������������������0000644�0000153�0000161�00000001245�12321735652�025734� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/icmp_stub.go�������������������������������������0000644�0000153�0000161�00000001043�12321735652�024030� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/control_rfc3542_windows.go�����������������������0000644�0000153�0000161�00000001243�12321735652�026447� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 syscall.Handle, opt *rawOpt, cf ControlFlags, on bool) error { // TODO(mikio): Implement this return syscall.EWINDOWS } 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.EWINDOWS } func marshalControlMessage(cm *ControlMessage) (oob []byte) { // TODO(mikio): Implement this return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/helper_unix.go�����������������������������������0000644�0000153�0000161�00000002003�12321735652�024362� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "net" "reflect" ) func (c *genericOpt) sysfd() (int, error) { switch p := c.Conn.(type) { case *net.TCPConn, *net.UDPConn, *net.IPConn: return sysfd(p) } return 0, errInvalidConnType } func (c *dgramOpt) sysfd() (int, error) { switch p := c.PacketConn.(type) { case *net.UDPConn, *net.IPConn: return sysfd(p.(net.Conn)) } return 0, errInvalidConnType } func (c *payloadHandler) sysfd() (int, error) { return sysfd(c.PacketConn.(net.Conn)) } func sysfd(c net.Conn) (int, error) { cv := reflect.ValueOf(c) switch ce := cv.Elem(); ce.Kind() { case reflect.Struct: nfd := ce.FieldByName("conn").FieldByName("fd") switch fe := nfd.Elem(); fe.Kind() { case reflect.Struct: fd := fe.FieldByName("sysfd") return int(fd.Int()), nil } } return 0, errInvalidConnType } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/unicastsockopt_test.go���������������������������0000644�0000153�0000161�00000004632�12321735652�026162� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "code.google.com/p/go.net/ipv6" "net" "os" "runtime" "testing" ) func TestConnUnicastSocketOptions(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") } ln, err := net.Listen("tcp6", "[::1]:0") if err != nil { t.Fatalf("net.Listen failed: %v", err) } defer ln.Close() done := make(chan bool) go acceptor(t, ln, done) c, err := net.Dial("tcp6", ln.Addr().String()) if err != nil { t.Fatalf("net.Dial failed: %v", err) } defer c.Close() testUnicastSocketOptions(t, ipv6.NewConn(c)) <-done } var packetConnUnicastSocketOptionTests = []struct { net, proto, addr string }{ {"udp6", "", "[::1]:0"}, {"ip6", ":ipv6-icmp", "::1"}, } func TestPacketConnUnicastSocketOptions(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") } for _, tt := range packetConnUnicastSocketOptionTests { if tt.net == "ip6" && os.Getuid() != 0 { t.Skip("must be root") } c, err := net.ListenPacket(tt.net+tt.proto, tt.addr) if err != nil { t.Fatalf("net.ListenPacket(%q, %q) failed: %v", tt.net+tt.proto, tt.addr, err) } defer c.Close() testUnicastSocketOptions(t, ipv6.NewPacketConn(c)) } } type testIPv6UnicastConn interface { TrafficClass() (int, error) SetTrafficClass(int) error HopLimit() (int, error) SetHopLimit(int) error } func testUnicastSocketOptions(t *testing.T, c testIPv6UnicastConn) { tclass := DiffServCS0 | NotECNTransport if err := c.SetTrafficClass(tclass); err != nil { t.Fatalf("ipv6.Conn.SetTrafficClass failed: %v", err) } if v, err := c.TrafficClass(); err != nil { t.Fatalf("ipv6.Conn.TrafficClass failed: %v", err) } else if v != tclass { t.Fatalf("got unexpected traffic class %v; expected %v", v, tclass) } hoplim := 255 if err := c.SetHopLimit(hoplim); err != nil { t.Fatalf("ipv6.Conn.SetHopLimit failed: %v", err) } if v, err := c.HopLimit(); err != nil { t.Fatalf("ipv6.Conn.HopLimit failed: %v", err) } else if v != hoplim { t.Fatalf("got unexpected hop limit %v; expected %v", v, hoplim) } } ������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_linux.go�������������������������0000644�0000153�0000161�00000000671�12321735652�026127� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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" "unsafe" ) func setIPv6Checksum(fd int, on bool, offset int) error { if !on { offset = -1 } v := int32(offset) return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolReserved, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4)) } �����������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sys_unix.go��������������������������������������0000644�0000153�0000161�00000000526�12321735652�023731� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/dgramopt_stub.go���������������������������������0000644�0000153�0000161�00000006066�12321735652�024727� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/payload.go���������������������������������������0000644�0000153�0000161�00000000605�12321735652�023477� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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" // A payloadHandler represents the IPv6 datagram payload handler. type payloadHandler struct { net.PacketConn rawOpt } func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil } ���������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_stub.go��������������������������0000644�0000153�0000161�00000000466�12321735652�025742� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sys.go�������������������������������������������0000644�0000153�0000161�00000000662�12321735652�022667� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/icmp_windows.go����������������������������������0000644�0000153�0000161�00000001000�12321735652�024536� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 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 } juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/multicastlistener_test.go������������������������0000644�0000153�0000161�00000014455�12321735652�026670� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "code.google.com/p/go.net/ipv6" "fmt" "net" "os" "runtime" "testing" ) var udpMultipleGroupListenerTests = []net.Addr{ &net.UDPAddr{IP: net.ParseIP("ff02::114")}, // see RFC 4727 &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}, } func TestUDPSinglePacketConnWithMultipleGroupListeners(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") } for _, gaddr := range udpMultipleGroupListenerTests { c, err := net.ListenPacket("udp6", "[::]:0") // wildcard address with non-reusable port if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() p := ipv6.NewPacketConn(c) 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 { continue } if err := p.JoinGroup(&ifi, gaddr); err != nil { t.Fatalf("ipv6.PacketConn.JoinGroup %v on %v failed: %v", gaddr, ifi, err) } mift = append(mift, &ift[i]) } for _, ifi := range mift { if err := p.LeaveGroup(ifi, gaddr); err != nil { t.Fatalf("ipv6.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err) } } } } func TestUDPMultiplePacketConnWithMultipleGroupListeners(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") } for _, gaddr := range udpMultipleGroupListenerTests { c1, err := net.ListenPacket("udp6", "[ff02::]:1024") // wildcard address with reusable port if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c1.Close() c2, err := net.ListenPacket("udp6", "[ff02::]:1024") // wildcard address with reusable port if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c2.Close() var ps [2]*ipv6.PacketConn ps[0] = ipv6.NewPacketConn(c1) ps[1] = ipv6.NewPacketConn(c2) 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 { continue } for _, p := range ps { if err := p.JoinGroup(&ifi, gaddr); err != nil { t.Fatalf("ipv6.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, gaddr); err != nil { t.Fatalf("ipv6.PacketConn.LeaveGroup %v on %v failed: %v", gaddr, ifi, err) } } } } } func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(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") } 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("udp6", fmt.Sprintf("[%s%%%s]:1024", ip.String(), ifi.Name)) // unicast address with non-reusable port if err != nil { t.Fatalf("net.ListenPacket with %v failed: %v", ip, 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) } } } func TestIPSinglePacketConnWithSingleGroupListener(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") } if os.Getuid() != 0 { t.Skip("must be root") } 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 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 { continue } 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 { 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) } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/unicast_test.go����������������������������������0000644�0000153�0000161�00000012014�12321735652�024550� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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" "os" "runtime" "testing" "time" ) func TestPacketConnReadWriteUnicastUDP(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) } 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 } 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.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) } 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) } } } func TestPacketConnReadWriteUnicastICMP(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") } if os.Getuid() != 0 { t.Skip("must be root") } c, err := net.ListenPacket("ip6:ipv6-icmp", "::1") if err != nil { 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) } 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 } var f ipv6.ICMPFilter f.SetAll(true) f.Set(ipv6.ICMPTypeEchoReply, false) if err := p.SetICMPFilter(&f); err != nil { 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(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) } cm.HopLimit = i + 1 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) } 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(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) } } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/mockicmp_test.go���������������������������������0000644�0000153�0000161�00000006166�12321735652�024717� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "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 Code int // code Checksum int // checksum Body icmpMessageBody // body } // icmpMessageBody represents an ICMP message body. type icmpMessageBody interface { Len() int Marshal() ([]byte, error) } // Marshal returns the binary enconding of the ICMP echo request or // reply message m. 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 { return nil, err } b = append(b, mb...) } 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 { s += uint32(b[i+1])<<8 | uint32(b[i]) } if csumcv&1 == 0 { s += uint32(b[csumcv]) } s = s>>16 + s&0xffff s = s + s>>16 // Place checksum back in header; using ^= avoids the // assumption the checksum bytes are zero. 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. func parseICMPMessage(b []byte) (*icmpMessage, error) { msglen := len(b) if msglen < 4 { return nil, errors.New("message too short") } m := &icmpMessage{Type: ipv6.ICMPType(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])} if msglen > 4 { var err error switch m.Type { case ipv6.ICMPTypeEchoRequest, ipv6.ICMPTypeEchoReply: m.Body, err = parseICMPEcho(b[4:]) if err != nil { return nil, err } } } return m, nil } // imcpEcho represenets an ICMP echo request or reply message body. type icmpEcho struct { ID int // identifier Seq int // sequence number Data []byte // data } func (p *icmpEcho) Len() int { if p == nil { return 0 } return 4 + len(p.Data) } // Marshal returns the binary enconding of the ICMP echo request or // reply message body p. func (p *icmpEcho) Marshal() ([]byte, error) { b := make([]byte, 4+len(p.Data)) b[0], b[1] = byte(p.ID>>8), byte(p.ID) b[2], b[3] = byte(p.Seq>>8), byte(p.Seq) copy(b[4:], p.Data) return b, nil } // parseICMPEcho parses b as an ICMP echo request or reply message // body. func parseICMPEcho(b []byte) (*icmpEcho, error) { bodylen := len(b) p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])} if bodylen > 4 { p.Data = make([]byte, bodylen-4) copy(p.Data, b[4:]) } return p, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/genericopt_stub.go�������������������������������0000644�0000153�0000161�00000001706�12321735652�025245� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ����������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/multicastsockopt_test.go�������������������������0000644�0000153�0000161�00000004313�12321735652�026515� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "code.google.com/p/go.net/ipv6" "net" "os" "runtime" "testing" ) var packetConnMulticastSocketOptionTests = []struct { net, proto, addr string gaddr net.Addr }{ {"udp6", "", "[ff02::]:0", &net.UDPAddr{IP: net.ParseIP("ff02::114")}}, // see RFC 4727 {"ip6", ":ipv6-icmp", "::", &net.IPAddr{IP: net.ParseIP("ff02::114")}}, // see RFC 4727 } func TestPacketConnMulticastSocketOptions(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") } ifi := loopbackInterface() if ifi == nil { t.Skipf("not available on %q", runtime.GOOS) } for _, tt := range packetConnMulticastSocketOptionTests { if tt.net == "ip6" && os.Getuid() != 0 { t.Skip("must be root") } c, err := net.ListenPacket(tt.net+tt.proto, tt.addr) if err != nil { t.Fatalf("net.ListenPacket failed: %v", err) } defer c.Close() p := ipv6.NewPacketConn(c) hoplim := 255 if err := p.SetMulticastHopLimit(hoplim); err != nil { t.Fatalf("ipv6.PacketConn.SetMulticastHopLimit failed: %v", err) } if v, err := p.MulticastHopLimit(); err != nil { t.Fatalf("ipv6.PacketConn.MulticastHopLimit failed: %v", err) } else if v != hoplim { t.Fatalf("got unexpected multicast hop limit %v; expected %v", v, hoplim) } for _, toggle := range []bool{true, false} { if err := p.SetMulticastLoopback(toggle); err != nil { t.Fatalf("ipv6.PacketConn.SetMulticastLoopback failed: %v", err) } if v, err := p.MulticastLoopback(); err != nil { t.Fatalf("ipv6.PacketConn.MulticastLoopback failed: %v", err) } else if v != toggle { t.Fatalf("got unexpected multicast loopback %v; expected %v", v, toggle) } } if err := p.JoinGroup(ifi, tt.gaddr); err != nil { t.Fatalf("ipv6.PacketConn.JoinGroup(%v, %v) failed: %v", ifi, tt.gaddr, err) } if err := p.LeaveGroup(ifi, tt.gaddr); err != nil { t.Fatalf("ipv6.PacketConn.LeaveGroup(%v, %v) failed: %v", ifi, tt.gaddr, err) } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/iana.go������������������������������������������0000644�0000153�0000161�00000031060�12321735652�022755� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// go run gen.go // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT package ipv6 // Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2013-07-03 const ( ICMPTypeDestinationUnreachable ICMPType = 1 // Destination Unreachable ICMPTypePacketTooBig ICMPType = 2 // Packet Too Big ICMPTypeTimeExceeded ICMPType = 3 // Time Exceeded ICMPTypeParameterProblem ICMPType = 4 // Parameter Problem ICMPTypeEchoRequest ICMPType = 128 // Echo Request ICMPTypeEchoReply ICMPType = 129 // Echo Reply ICMPTypeMulticastListenerQuery ICMPType = 130 // Multicast Listener Query ICMPTypeMulticastListenerReport ICMPType = 131 // Multicast Listener Report ICMPTypeMulticastListenerDone ICMPType = 132 // Multicast Listener Done ICMPTypeRouterSolicitation ICMPType = 133 // Router Solicitation ICMPTypeRouterAdvertisement ICMPType = 134 // Router Advertisement ICMPTypeNeighborSolicitation ICMPType = 135 // Neighbor Solicitation ICMPTypeNeighborAdvertisement ICMPType = 136 // Neighbor Advertisement ICMPTypeRedirect ICMPType = 137 // Redirect Message ICMPTypeRouterRenumbering ICMPType = 138 // Router Renumbering ICMPTypeNodeInformationQuery ICMPType = 139 // ICMP Node Information Query ICMPTypeNodeInformationResponse ICMPType = 140 // ICMP Node Information Response ICMPTypeInverseNeighborDiscoverySolicitation ICMPType = 141 // Inverse Neighbor Discovery Solicitation Message ICMPTypeInverseNeighborDiscoveryAdvertisement ICMPType = 142 // Inverse Neighbor Discovery Advertisement Message ICMPTypeVersion2MulticastListenerReport ICMPType = 143 // Version 2 Multicast Listener Report ICMPTypeHomeAgentAddressDiscoveryRequest ICMPType = 144 // Home Agent Address Discovery Request Message ICMPTypeHomeAgentAddressDiscoveryReply ICMPType = 145 // Home Agent Address Discovery Reply Message ICMPTypeMobilePrefixSolicitation ICMPType = 146 // Mobile Prefix Solicitation ICMPTypeMobilePrefixAdvertisement ICMPType = 147 // Mobile Prefix Advertisement ICMPTypeCertificationPathSolicitation ICMPType = 148 // Certification Path Solicitation Message ICMPTypeCertificationPathAdvertisement ICMPType = 149 // Certification Path Advertisement Message ICMPTypeMulticastRouterAdvertisement ICMPType = 151 // Multicast Router Advertisement ICMPTypeMulticastRouterSolicitation ICMPType = 152 // Multicast Router Solicitation ICMPTypeMulticastRouterTermination ICMPType = 153 // Multicast Router Termination ICMPTypeFMIPv6 ICMPType = 154 // FMIPv6 Messages ICMPTypeRPLControl ICMPType = 155 // RPL Control Message ICMPTypeILNPv6LocatorUpdate ICMPType = 156 // ILNPv6 Locator Update Message ICMPTypeDuplicateAddressRequest ICMPType = 157 // Duplicate Address Request ICMPTypeDuplicateAddressConfirmation ICMPType = 158 // Duplicate Address Confirmation ) // Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2013-07-03 var icmpTypes = map[ICMPType]string{ 1: "destination unreachable", 2: "packet too big", 3: "time exceeded", 4: "parameter problem", 128: "echo request", 129: "echo reply", 130: "multicast listener query", 131: "multicast listener report", 132: "multicast listener done", 133: "router solicitation", 134: "router advertisement", 135: "neighbor solicitation", 136: "neighbor advertisement", 137: "redirect message", 138: "router renumbering", 139: "icmp node information query", 140: "icmp node information response", 141: "inverse neighbor discovery solicitation message", 142: "inverse neighbor discovery advertisement message", 143: "version 2 multicast listener report", 144: "home agent address discovery request message", 145: "home agent address discovery reply message", 146: "mobile prefix solicitation", 147: "mobile prefix advertisement", 148: "certification path solicitation message", 149: "certification path advertisement message", 151: "multicast router advertisement", 152: "multicast router solicitation", 153: "multicast router termination", 154: "fmipv6 messages", 155: "rpl control message", 156: "ilnpv6 locator update message", 157: "duplicate address request", 158: "duplicate address confirmation", } // Protocol Numbers, Updated: 2013-02-17 const ( ianaProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option ianaProtocolICMP = 1 // Internet Control Message ianaProtocolIGMP = 2 // Internet Group Management ianaProtocolGGP = 3 // Gateway-to-Gateway ianaProtocolIPv4 = 4 // IPv4 encapsulation ianaProtocolST = 5 // Stream ianaProtocolTCP = 6 // Transmission Control ianaProtocolCBT = 7 // CBT ianaProtocolEGP = 8 // Exterior Gateway Protocol ianaProtocolIGP = 9 // any private interior gateway (used by Cisco for their IGRP) ianaProtocolBBNRCCMON = 10 // BBN RCC Monitoring ianaProtocolNVPII = 11 // Network Voice Protocol ianaProtocolPUP = 12 // PUP ianaProtocolARGUS = 13 // ARGUS ianaProtocolEMCON = 14 // EMCON ianaProtocolXNET = 15 // Cross Net Debugger ianaProtocolCHAOS = 16 // Chaos ianaProtocolUDP = 17 // User Datagram ianaProtocolMUX = 18 // Multiplexing ianaProtocolDCNMEAS = 19 // DCN Measurement Subsystems ianaProtocolHMP = 20 // Host Monitoring ianaProtocolPRM = 21 // Packet Radio Measurement ianaProtocolXNSIDP = 22 // XEROX NS IDP ianaProtocolTRUNK1 = 23 // Trunk-1 ianaProtocolTRUNK2 = 24 // Trunk-2 ianaProtocolLEAF1 = 25 // Leaf-1 ianaProtocolLEAF2 = 26 // Leaf-2 ianaProtocolRDP = 27 // Reliable Data Protocol ianaProtocolIRTP = 28 // Internet Reliable Transaction ianaProtocolISOTP4 = 29 // ISO Transport Protocol Class 4 ianaProtocolNETBLT = 30 // Bulk Data Transfer Protocol ianaProtocolMFENSP = 31 // MFE Network Services Protocol ianaProtocolMERITINP = 32 // MERIT Internodal Protocol ianaProtocolDCCP = 33 // Datagram Congestion Control Protocol ianaProtocol3PC = 34 // Third Party Connect Protocol ianaProtocolIDPR = 35 // Inter-Domain Policy Routing Protocol ianaProtocolXTP = 36 // XTP ianaProtocolDDP = 37 // Datagram Delivery Protocol ianaProtocolIDPRCMTP = 38 // IDPR Control Message Transport Proto ianaProtocolTPPP = 39 // TP++ Transport Protocol ianaProtocolIL = 40 // IL Transport Protocol ianaProtocolIPv6 = 41 // IPv6 encapsulation ianaProtocolSDRP = 42 // Source Demand Routing Protocol ianaProtocolIPv6Route = 43 // Routing Header for IPv6 ianaProtocolIPv6Frag = 44 // Fragment Header for IPv6 ianaProtocolIDRP = 45 // Inter-Domain Routing Protocol ianaProtocolRSVP = 46 // Reservation Protocol ianaProtocolGRE = 47 // Generic Routing Encapsulation ianaProtocolDSR = 48 // Dynamic Source Routing Protocol ianaProtocolBNA = 49 // BNA ianaProtocolESP = 50 // Encap Security Payload ianaProtocolAH = 51 // Authentication Header ianaProtocolINLSP = 52 // Integrated Net Layer Security TUBA ianaProtocolSWIPE = 53 // IP with Encryption ianaProtocolNARP = 54 // NBMA Address Resolution Protocol ianaProtocolMOBILE = 55 // IP Mobility ianaProtocolTLSP = 56 // Transport Layer Security Protocol using Kryptonet key management ianaProtocolSKIP = 57 // SKIP ianaProtocolIPv6ICMP = 58 // ICMP for IPv6 ianaProtocolIPv6NoNxt = 59 // No Next Header for IPv6 ianaProtocolIPv6Opts = 60 // Destination Options for IPv6 ianaProtocolCFTP = 62 // CFTP ianaProtocolSATEXPAK = 64 // SATNET and Backroom EXPAK ianaProtocolKRYPTOLAN = 65 // Kryptolan ianaProtocolRVD = 66 // MIT Remote Virtual Disk Protocol ianaProtocolIPPC = 67 // Internet Pluribus Packet Core ianaProtocolSATMON = 69 // SATNET Monitoring ianaProtocolVISA = 70 // VISA Protocol ianaProtocolIPCV = 71 // Internet Packet Core Utility ianaProtocolCPNX = 72 // Computer Protocol Network Executive ianaProtocolCPHB = 73 // Computer Protocol Heart Beat ianaProtocolWSN = 74 // Wang Span Network ianaProtocolPVP = 75 // Packet Video Protocol ianaProtocolBRSATMON = 76 // Backroom SATNET Monitoring ianaProtocolSUNND = 77 // SUN ND PROTOCOL-Temporary ianaProtocolWBMON = 78 // WIDEBAND Monitoring ianaProtocolWBEXPAK = 79 // WIDEBAND EXPAK ianaProtocolISOIP = 80 // ISO Internet Protocol ianaProtocolVMTP = 81 // VMTP ianaProtocolSECUREVMTP = 82 // SECURE-VMTP ianaProtocolVINES = 83 // VINES ianaProtocolTTP = 84 // TTP ianaProtocolIPTM = 84 // Protocol Internet Protocol Traffic Manager ianaProtocolNSFNETIGP = 85 // NSFNET-IGP ianaProtocolDGP = 86 // Dissimilar Gateway Protocol ianaProtocolTCF = 87 // TCF ianaProtocolEIGRP = 88 // EIGRP ianaProtocolOSPFIGP = 89 // OSPFIGP ianaProtocolSpriteRPC = 90 // Sprite RPC Protocol ianaProtocolLARP = 91 // Locus Address Resolution Protocol ianaProtocolMTP = 92 // Multicast Transport Protocol ianaProtocolAX25 = 93 // AX.25 Frames ianaProtocolIPIP = 94 // IP-within-IP Encapsulation Protocol ianaProtocolMICP = 95 // Mobile Internetworking Control Pro. ianaProtocolSCCSP = 96 // Semaphore Communications Sec. Pro. ianaProtocolETHERIP = 97 // Ethernet-within-IP Encapsulation ianaProtocolENCAP = 98 // Encapsulation Header ianaProtocolGMTP = 100 // GMTP ianaProtocolIFMP = 101 // Ipsilon Flow Management Protocol ianaProtocolPNNI = 102 // PNNI over IP ianaProtocolPIM = 103 // Protocol Independent Multicast ianaProtocolARIS = 104 // ARIS ianaProtocolSCPS = 105 // SCPS ianaProtocolQNX = 106 // QNX ianaProtocolAN = 107 // Active Networks ianaProtocolIPComp = 108 // IP Payload Compression Protocol ianaProtocolSNP = 109 // Sitara Networks Protocol ianaProtocolCompaqPeer = 110 // Compaq Peer Protocol ianaProtocolIPXinIP = 111 // IPX in IP ianaProtocolVRRP = 112 // Virtual Router Redundancy Protocol ianaProtocolPGM = 113 // PGM Reliable Transport Protocol ianaProtocolL2TP = 115 // Layer Two Tunneling Protocol ianaProtocolDDX = 116 // D-II Data Exchange (DDX) ianaProtocolIATP = 117 // Interactive Agent Transfer Protocol ianaProtocolSTP = 118 // Schedule Transfer Protocol ianaProtocolSRP = 119 // SpectraLink Radio Protocol ianaProtocolUTI = 120 // UTI ianaProtocolSMP = 121 // Simple Message Protocol ianaProtocolSM = 122 // SM ianaProtocolPTP = 123 // Performance Transparency Protocol ianaProtocolISIS = 124 // ISIS over IPv4 ianaProtocolFIRE = 125 // FIRE ianaProtocolCRTP = 126 // Combat Radio Transport Protocol ianaProtocolCRUDP = 127 // Combat Radio User Datagram ianaProtocolSSCOPMCE = 128 // SSCOPMCE ianaProtocolIPLT = 129 // IPLT ianaProtocolSPS = 130 // Secure Packet Shield ianaProtocolPIPE = 131 // Private IP Encapsulation within IP ianaProtocolSCTP = 132 // Stream Control Transmission Protocol ianaProtocolFC = 133 // Fibre Channel ianaProtocolRSVPE2EIGNORE = 134 // RSVP-E2E-IGNORE ianaProtocolMobilityHeader = 135 // Mobility Header ianaProtocolUDPLite = 136 // UDPLite ianaProtocolMPLSinIP = 137 // MPLS-in-IP ianaProtocolMANET = 138 // MANET Protocols ianaProtocolHIP = 139 // Host Identity Protocol ianaProtocolShim6 = 140 // Shim6 Protocol ianaProtocolWESP = 141 // Wrapped Encapsulating Security Payload ianaProtocolROHC = 142 // Robust Header Compression ianaProtocolReserved = 255 // Reserved ) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3542_unix.go��������������������������0000644�0000153�0000161�00000006005�12321735652�025743� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ( "os" "unsafe" ) func ipv6ReceiveTrafficClass(fd int) (bool, error) { 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 { vv := int32(boolint(v)) return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveTrafficClass, uintptr(unsafe.Pointer(&vv)), 4)) } func ipv6ReceiveHopLimit(fd int) (bool, error) { 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 { vv := int32(boolint(v)) return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptReceiveHopLimit, uintptr(unsafe.Pointer(&vv)), 4)) } func ipv6ReceivePacketInfo(fd int) (bool, error) { 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 { 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)) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_windows.go�����������������������0000644�0000153�0000161�00000007136�12321735652�026465� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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" ) func ipv6TrafficClass(fd syscall.Handle) (int, error) { // TODO(mikio): Implement this return 0, syscall.EWINDOWS } func setIPv6TrafficClass(fd syscall.Handle, v int) error { // TODO(mikio): Implement this return syscall.EWINDOWS } func ipv6HopLimit(fd syscall.Handle) (int, error) { var v int32 l := int32(4) if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil } func setIPv6HopLimit(fd syscall.Handle, v int) error { vv := int32(v) return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd, ianaProtocolIPv6, sysSockoptUnicastHopLimit, (*byte)(unsafe.Pointer(&vv)), 4)) } func ipv6Checksum(fd syscall.Handle) (bool, int, error) { // TODO(mikio): Implement this return false, 0, syscall.EWINDOWS } func ipv6MulticastHopLimit(fd syscall.Handle) (int, error) { var v int32 l := int32(4) if err := syscall.Getsockopt(fd, ianaProtocolIPv6, sysSockoptMulticastHopLimit, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return 0, os.NewSyscallError("getsockopt", err) } return int(v), nil } func setIPv6MulticastHopLimit(fd syscall.Handle, v int) error { vv := int32(v) 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, sysSockoptMulticastInterface, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return nil, os.NewSyscallError("getsockopt", err) } if v == 0 { return nil, nil } ifi, err := net.InterfaceByIndex(int(v)) if err != nil { return nil, err } return ifi, nil } func setIPv6MulticastInterface(fd syscall.Handle, ifi *net.Interface) error { var v int32 if ifi != nil { v = int32(ifi.Index) } 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, sysSockoptMulticastLoopback, (*byte)(unsafe.Pointer(&v)), &l); err != nil { return false, os.NewSyscallError("getsockopt", err) } return v == 1, nil } func setIPv6MulticastLoopback(fd syscall.Handle, v bool) error { vv := int32(boolint(v)) 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 := sysMulticastReq{} copy(mreq.IP[:], grp) if ifi != nil { mreq.IfIndex = uint32(ifi.Index) } 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 := sysMulticastReq{} copy(mreq.IP[:], grp) if ifi != nil { mreq.IfIndex = uint32(ifi.Index) } 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 { // TODO(mikio): Implement this return syscall.EWINDOWS } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/sockopt_rfc3493_bsd.go���������������������������0000644�0000153�0000161�00000000736�12321735652�025542� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 netbsd openbsd package ipv6 import ( "os" "unsafe" ) func setIPv6Checksum(fd int, on bool, offset int) error { if !on { offset = -1 } v := int32(offset) return os.NewSyscallError("setsockopt", setsockopt(fd, ianaProtocolIPv6, sysSockoptChecksum, uintptr(unsafe.Pointer(&v)), 4)) } ����������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/ipv6/icmp_bsd.go��������������������������������������0000644�0000153�0000161�00000001261�12321735652�023625� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 netbsd openbsd package ipv6 type sysICMPFilter struct { Filt [8]uint32 } func (f *sysICMPFilter) set(typ ICMPType, block bool) { if block { f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31) } else { f.Filt[typ>>5] |= 1 << (uint32(typ) & 31) } } func (f *sysICMPFilter) setAll(block bool) { for i := range f.Filt { if block { f.Filt[i] = 0 } else { f.Filt[i] = 1<<32 - 1 } } } func (f *sysICMPFilter) willBlock(typ ICMPType) bool { return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0 } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/idna/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735652�021545� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/idna/punycode_test.go���������������������������������0000644�0000153�0000161�00000013433�12321735652�024765� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 idna import ( "strings" "testing" ) var punycodeTestCases = [...]struct { s, encoded string }{ {"", ""}, {"-", "--"}, {"-a", "-a-"}, {"-a-", "-a--"}, {"a", "a-"}, {"a-", "a--"}, {"a-b", "a-b-"}, {"books", "books-"}, {"bücher", "bcher-kva"}, {"Hello世界", "Hello-ck1hg65u"}, {"ü", "tda"}, {"üý", "tdac"}, // The test cases below come from RFC 3492 section 7.1 with Errata 3026. { // (A) Arabic (Egyptian). "\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644" + "\u0645\u0648\u0634\u0639\u0631\u0628\u064A\u061F", "egbpdaj6bu4bxfgehfvwxn", }, { // (B) Chinese (simplified). "\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587", "ihqwcrb4cv8a8dqg056pqjye", }, { // (C) Chinese (traditional). "\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587", "ihqwctvzc91f659drss3x8bo0yb", }, { // (D) Czech. "\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074" + "\u011B\u006E\u0065\u006D\u006C\u0075\u0076\u00ED\u010D" + "\u0065\u0073\u006B\u0079", "Proprostnemluvesky-uyb24dma41a", }, { // (E) Hebrew. "\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8" + "\u05DC\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2" + "\u05D1\u05E8\u05D9\u05EA", "4dbcagdahymbxekheh6e0a7fei0b", }, { // (F) Hindi (Devanagari). "\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D" + "\u0926\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939" + "\u0940\u0902\u092C\u094B\u0932\u0938\u0915\u0924\u0947" + "\u0939\u0948\u0902", "i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd", }, { // (G) Japanese (kanji and hiragana). "\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092" + "\u8A71\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B", "n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa", }, { // (H) Korean (Hangul syllables). "\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774" + "\uD55C\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74" + "\uC5BC\uB9C8\uB098\uC88B\uC744\uAE4C", "989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5j" + "psd879ccm6fea98c", }, { // (I) Russian (Cyrillic). "\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E" + "\u043D\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440" + "\u044F\u0442\u043F\u043E\u0440\u0443\u0441\u0441\u043A" + "\u0438", "b1abfaaepdrnnbgefbadotcwatmq2g4l", }, { // (J) Spanish. "\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070" + "\u0075\u0065\u0064\u0065\u006E\u0073\u0069\u006D\u0070" + "\u006C\u0065\u006D\u0065\u006E\u0074\u0065\u0068\u0061" + "\u0062\u006C\u0061\u0072\u0065\u006E\u0045\u0073\u0070" + "\u0061\u00F1\u006F\u006C", "PorqunopuedensimplementehablarenEspaol-fmd56a", }, { // (K) Vietnamese. "\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B" + "\u0068\u00F4\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068" + "\u1EC9\u006E\u00F3\u0069\u0074\u0069\u1EBF\u006E\u0067" + "\u0056\u0069\u1EC7\u0074", "TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g", }, { // (L) 3<nen>B<gumi><kinpachi><sensei>. "\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F", "3B-ww4c5e180e575a65lsy2b", }, { // (M) <amuro><namie>-with-SUPER-MONKEYS. "\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074" + "\u0068\u002D\u0053\u0055\u0050\u0045\u0052\u002D\u004D" + "\u004F\u004E\u004B\u0045\u0059\u0053", "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n", }, { // (N) Hello-Another-Way-<sorezore><no><basho>. "\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F" + "\u0074\u0068\u0065\u0072\u002D\u0057\u0061\u0079\u002D" + "\u305D\u308C\u305E\u308C\u306E\u5834\u6240", "Hello-Another-Way--fc4qua05auwb3674vfr0b", }, { // (O) <hitotsu><yane><no><shita>2. "\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032", "2-u9tlzr9756bt3uc0v", }, { // (P) Maji<de>Koi<suru>5<byou><mae> "\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059" + "\u308B\u0035\u79D2\u524D", "MajiKoi5-783gue6qz075azm5e", }, { // (Q) <pafii>de<runba> "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0", "de-jg4avhby1noc0d", }, { // (R) <sono><supiido><de> "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067", "d9juau41awczczp", }, { // (S) -> $1.00 <- "\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020" + "\u003C\u002D", "-> $1.00 <--", }, } func TestPunycode(t *testing.T) { for _, tc := range punycodeTestCases { if got, err := decode(tc.encoded); err != nil { t.Errorf("decode(%q): %v", tc.encoded, err) } else if got != tc.s { t.Errorf("decode(%q): got %q, want %q", tc.encoded, got, tc.s) } if got, err := encode("", tc.s); err != nil { t.Errorf(`encode("", %q): %v`, tc.s, err) } else if got != tc.encoded { t.Errorf(`encode("", %q): got %q, want %q`, tc.s, got, tc.encoded) } } } var punycodeErrorTestCases = [...]string{ "decode -", // A sole '-' is invalid. "decode foo\x00bar", // '\x00' is not in [0-9A-Za-z]. "decode foo#bar", // '#' is not in [0-9A-Za-z]. "decode foo\u00A3bar", // '\u00A3' is not in [0-9A-Za-z]. "decode 9", // "9a" decodes to codepoint \u00A3; "9" is truncated. "decode 99999a", // "99999a" decodes to codepoint \U0048A3C1, which is > \U0010FFFF. "decode 9999999999a", // "9999999999a" overflows the int32 calculation. "encode " + strings.Repeat("x", 65536) + "\uff00", // int32 overflow. } func TestPunycodeErrors(t *testing.T) { for _, tc := range punycodeErrorTestCases { var err error switch { case strings.HasPrefix(tc, "decode "): _, err = decode(tc[7:]) case strings.HasPrefix(tc, "encode "): _, err = encode("", tc[7:]) } if err == nil { if len(tc) > 256 { tc = tc[:100] + "..." + tc[len(tc)-100:] } t.Errorf("no error for %s", tc) } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/idna/idna_test.go�������������������������������������0000644�0000153�0000161�00000002115�12321735652�024045� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 idna import ( "testing" ) var idnaTestCases = [...]struct { ascii, unicode string }{ // Labels. {"books", "books"}, {"xn--bcher-kva", "bücher"}, // Domains. {"foo--xn--bar.org", "foo--xn--bar.org"}, {"golang.org", "golang.org"}, {"example.xn--p1ai", "example.рф"}, {"xn--czrw28b.tw", "商業.tw"}, {"www.xn--mller-kva.de", "www.müller.de"}, } func TestIDNA(t *testing.T) { for _, tc := range idnaTestCases { if a, err := ToASCII(tc.unicode); err != nil { t.Errorf("ToASCII(%q): %v", tc.unicode, err) } else if a != tc.ascii { t.Errorf("ToASCII(%q): got %q, want %q", tc.unicode, a, tc.ascii) } if u, err := ToUnicode(tc.ascii); err != nil { t.Errorf("ToUnicode(%q): %v", tc.ascii, err) } else if u != tc.unicode { t.Errorf("ToUnicode(%q): got %q, want %q", tc.ascii, u, tc.unicode) } } } // TODO(nigeltao): test errors, once we've specified when ToASCII and ToUnicode // return errors. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/idna/idna.go������������������������������������������0000644�0000153�0000161�00000003356�12321735652�023016� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 idna implements IDNA2008 (Internationalized Domain Names for // Applications), defined in RFC 5890, RFC 5891, RFC 5892, RFC 5893 and // RFC 5894. package idna import ( "strings" "unicode/utf8" ) // TODO(nigeltao): specify when errors occur. For example, is ToASCII(".") or // ToASCII("foo\x00") an error? See also http://www.unicode.org/faq/idn.html#11 // acePrefix is the ASCII Compatible Encoding prefix. const acePrefix = "xn--" // ToASCII converts a domain or domain label to its ASCII form. For example, // ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and // ToASCII("golang") is "golang". func ToASCII(s string) (string, error) { if ascii(s) { return s, nil } labels := strings.Split(s, ".") for i, label := range labels { if !ascii(label) { a, err := encode(acePrefix, label) if err != nil { return "", err } labels[i] = a } } return strings.Join(labels, "."), nil } // ToUnicode converts a domain or domain label to its Unicode form. For example, // ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and // ToUnicode("golang") is "golang". func ToUnicode(s string) (string, error) { if !strings.Contains(s, acePrefix) { return s, nil } labels := strings.Split(s, ".") for i, label := range labels { if strings.HasPrefix(label, acePrefix) { u, err := decode(label[len(acePrefix):]) if err != nil { return "", err } labels[i] = u } } return strings.Join(labels, "."), nil } func ascii(s string) bool { for i := 0; i < len(s); i++ { if s[i] >= utf8.RuneSelf { return false } } return true } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.net/idna/punycode.go��������������������������������������0000644�0000153�0000161�00000010537�12321735652�023730� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 idna // This file implements the Punycode algorithm from RFC 3492. import ( "fmt" "math" "strings" "unicode/utf8" ) // These parameter values are specified in section 5. // // All computation is done with int32s, so that overflow behavior is identical // regardless of whether int is 32-bit or 64-bit. const ( base int32 = 36 damp int32 = 700 initialBias int32 = 72 initialN int32 = 128 skew int32 = 38 tmax int32 = 26 tmin int32 = 1 ) // decode decodes a string as specified in section 6.2. func decode(encoded string) (string, error) { if encoded == "" { return "", nil } pos := 1 + strings.LastIndex(encoded, "-") if pos == 1 { return "", fmt.Errorf("idna: invalid label %q", encoded) } if pos == len(encoded) { return encoded[:len(encoded)-1], nil } output := make([]rune, 0, len(encoded)) if pos != 0 { for _, r := range encoded[:pos-1] { output = append(output, r) } } i, n, bias := int32(0), initialN, initialBias for pos < len(encoded) { oldI, w := i, int32(1) for k := base; ; k += base { if pos == len(encoded) { return "", fmt.Errorf("idna: invalid label %q", encoded) } digit, ok := decodeDigit(encoded[pos]) if !ok { return "", fmt.Errorf("idna: invalid label %q", encoded) } pos++ i += digit * w if i < 0 { return "", fmt.Errorf("idna: invalid label %q", encoded) } t := k - bias if t < tmin { t = tmin } else if t > tmax { t = tmax } if digit < t { break } w *= base - t if w >= math.MaxInt32/base { return "", fmt.Errorf("idna: invalid label %q", encoded) } } x := int32(len(output) + 1) bias = adapt(i-oldI, x, oldI == 0) n += i / x i %= x if n > utf8.MaxRune || len(output) >= 1024 { return "", fmt.Errorf("idna: invalid label %q", encoded) } output = append(output, 0) copy(output[i+1:], output[i:]) output[i] = n i++ } return string(output), nil } // encode encodes a string as specified in section 6.3 and prepends prefix to // the result. // // The "while h < length(input)" line in the specification becomes "for // remaining != 0" in the Go code, because len(s) in Go is in bytes, not runes. func encode(prefix, s string) (string, error) { output := make([]byte, len(prefix), len(prefix)+1+2*len(s)) copy(output, prefix) delta, n, bias := int32(0), initialN, initialBias b, remaining := int32(0), int32(0) for _, r := range s { if r < 0x80 { b++ output = append(output, byte(r)) } else { remaining++ } } h := b if b > 0 { output = append(output, '-') } for remaining != 0 { m := int32(0x7fffffff) for _, r := range s { if m > r && r >= n { m = r } } delta += (m - n) * (h + 1) if delta < 0 { return "", fmt.Errorf("idna: invalid label %q", s) } n = m for _, r := range s { if r < n { delta++ if delta < 0 { return "", fmt.Errorf("idna: invalid label %q", s) } continue } if r > n { continue } q := delta for k := base; ; k += base { t := k - bias if t < tmin { t = tmin } else if t > tmax { t = tmax } if q < t { break } output = append(output, encodeDigit(t+(q-t)%(base-t))) q = (q - t) / (base - t) } output = append(output, encodeDigit(q)) bias = adapt(delta, h+1, h == b) delta = 0 h++ remaining-- } delta++ n++ } return string(output), nil } func decodeDigit(x byte) (digit int32, ok bool) { switch { case '0' <= x && x <= '9': return int32(x - ('0' - 26)), true case 'A' <= x && x <= 'Z': return int32(x - 'A'), true case 'a' <= x && x <= 'z': return int32(x - 'a'), true } return 0, false } func encodeDigit(digit int32) byte { switch { case 0 <= digit && digit < 26: return byte(digit + 'a') case 26 <= digit && digit < 36: return byte(digit + ('0' - 26)) } panic("idna: internal error in punycode encoding") } // adapt is the bias adaptation function specified in section 6.1. func adapt(delta, numPoints int32, firstTime bool) int32 { if firstTime { delta /= damp } else { delta /= 2 } delta += delta / numPoints k := int32(0) for delta > ((base-tmin)*tmax)/2 { delta /= base - tmin k += base } return k + (base-tmin+1)*delta/(delta+skew) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/���������������������������������������������������0000755�0000153�0000161�00000000000�12321736016�021357� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/codereview.cfg�������������������������������������0000644�0000153�0000161�00000000136�12321736016�024174� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������defaultcc: golang-dev@googlegroups.com contributors: http://go.googlecode.com/hg/CONTRIBUTORS ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/nacl/����������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022305� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/nacl/secretbox/������������������������������������0000755�0000153�0000161�00000000000�12321735647�024303� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/nacl/secretbox/secretbox.go������������������������0000644�0000153�0000161�00000011154�12321735647�026632� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 secretbox encrypts and authenticates small messages. Secretbox uses XSalsa20 and Poly1305 to encrypt and authenticate messages with secret-key cryptography. The length of messages is not hidden. It is the caller's responsibility to ensure the uniqueness of nonces—for example, by using nonce 1 for the first message, nonce 2 for the second message, etc. Nonces are long enough that randomly generated nonces have negligible risk of collision. This package is interoperable with NaCl: http://nacl.cr.yp.to/secretbox.html. */ package secretbox import ( "code.google.com/p/go.crypto/poly1305" "code.google.com/p/go.crypto/salsa20/salsa" ) // Overhead is the number of bytes of overhead when boxing a message. const Overhead = poly1305.TagSize // setup produces a sub-key and Salsa20 counter given a nonce and key. func setup(subKey *[32]byte, counter *[16]byte, nonce *[24]byte, key *[32]byte) { // We use XSalsa20 for encryption so first we need to generate a // key and nonce with HSalsa20. var hNonce [16]byte copy(hNonce[:], nonce[:]) salsa.HSalsa20(subKey, &hNonce, key, &salsa.Sigma) // The final 8 bytes of the original nonce form the new nonce. copy(counter[:], nonce[16:]) } // sliceForAppend takes a slice and a requested number of bytes. It returns a // slice with the contents of the given slice followed by that many bytes and a // second slice that aliases into it and contains only the extra bytes. If the // original slice has sufficient capacity then no allocation is performed. func sliceForAppend(in []byte, n int) (head, tail []byte) { if total := len(in) + n; cap(in) >= total { head = in[:total] } else { head = make([]byte, total) copy(head, in) } tail = head[len(in):] return } // Seal appends an encrypted and authenticated copy of message to out, which // must not overlap message. The key and nonce pair must be unique for each // distinct message and the output will be Overhead bytes longer than message. func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte { var subKey [32]byte var counter [16]byte setup(&subKey, &counter, nonce, key) // The Poly1305 key is generated by encrypting 32 bytes of zeros. Since // Salsa20 works with 64-byte blocks, we also generate 32 bytes of // keystream as a side effect. var firstBlock [64]byte salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey) var poly1305Key [32]byte copy(poly1305Key[:], firstBlock[:]) ret, out := sliceForAppend(out, len(message)+poly1305.TagSize) // We XOR up to 32 bytes of message with the keystream generated from // the first block. firstMessageBlock := message if len(firstMessageBlock) > 32 { firstMessageBlock = firstMessageBlock[:32] } tagOut := out out = out[poly1305.TagSize:] for i, x := range firstMessageBlock { out[i] = firstBlock[32+i] ^ x } message = message[len(firstMessageBlock):] ciphertext := out out = out[len(firstMessageBlock):] // Now encrypt the rest. counter[8] = 1 salsa.XORKeyStream(out, message, &counter, &subKey) var tag [poly1305.TagSize]byte poly1305.Sum(&tag, ciphertext, &poly1305Key) copy(tagOut, tag[:]) return ret } // Open authenticates and decrypts a box produced by Seal and appends the // message to out, which must not overlap box. The output will be Overhead // bytes smaller than box. func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) { if len(box) < Overhead { return nil, false } var subKey [32]byte var counter [16]byte setup(&subKey, &counter, nonce, key) // The Poly1305 key is generated by encrypting 32 bytes of zeros. Since // Salsa20 works with 64-byte blocks, we also generate 32 bytes of // keystream as a side effect. var firstBlock [64]byte salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey) var poly1305Key [32]byte copy(poly1305Key[:], firstBlock[:]) var tag [poly1305.TagSize]byte copy(tag[:], box) if !poly1305.Verify(&tag, box[poly1305.TagSize:], &poly1305Key) { return nil, false } ret, out := sliceForAppend(out, len(box)-Overhead) // We XOR up to 32 bytes of box with the keystream generated from // the first block. box = box[Overhead:] firstMessageBlock := box if len(firstMessageBlock) > 32 { firstMessageBlock = firstMessageBlock[:32] } for i, x := range firstMessageBlock { out[i] = firstBlock[32+i] ^ x } box = box[len(firstMessageBlock):] out = out[len(firstMessageBlock):] // Now decrypt the rest. counter[8] = 1 salsa.XORKeyStream(out, box, &counter, &subKey) return ret, true } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/nacl/secretbox/secretbox_test.go�������������������0000644�0000153�0000161�00000004021�12321735647�027664� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 secretbox import ( "bytes" "crypto/rand" "encoding/hex" "testing" ) func TestSealOpen(t *testing.T) { var key [32]byte var nonce [24]byte rand.Reader.Read(key[:]) rand.Reader.Read(nonce[:]) var box, opened []byte for msgLen := 0; msgLen < 128; msgLen += 17 { message := make([]byte, msgLen) rand.Reader.Read(message) box = Seal(box[:0], message, &nonce, &key) var ok bool opened, ok = Open(opened[:0], box, &nonce, &key) if !ok { t.Errorf("%d: failed to open box", msgLen) continue } if !bytes.Equal(opened, message) { t.Errorf("%d: got %x, expected %x", msgLen, opened, message) continue } } for i := range box { box[i] ^= 0x20 _, ok := Open(opened[:0], box, &nonce, &key) if ok { t.Errorf("box was opened after corrupting byte %d", i) } box[i] ^= 0x20 } } func TestSecretBox(t *testing.T) { var key [32]byte var nonce [24]byte var message [64]byte for i := range key[:] { key[i] = 1 } for i := range nonce[:] { nonce[i] = 2 } for i := range message[:] { message[i] = 3 } box := Seal(nil, message[:], &nonce, &key) // expected was generated using the C implementation of NaCl. expected, _ := hex.DecodeString("8442bc313f4626f1359e3b50122b6ce6fe66ddfe7d39d14e637eb4fd5b45beadab55198df6ab5368439792a23c87db70acb6156dc5ef957ac04f6276cf6093b84be77ff0849cc33e34b7254d5a8f65ad") if !bytes.Equal(box, expected) { t.Fatalf("box didn't match, got\n%x\n, expected\n%x", box, expected) } } func TestAppend(t *testing.T) { var key [32]byte var nonce [24]byte var message [8]byte out := make([]byte, 4) box := Seal(out, message[:], &nonce, &key) if !bytes.Equal(box[:4], out[:4]) { t.Fatalf("Seal didn't correctly append") } out = make([]byte, 4, 100) box = Seal(out, message[:], &nonce, &key) if !bytes.Equal(box[:4], out[:4]) { t.Fatalf("Seal didn't correctly append with sufficient capacity.") } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/nacl/box/������������������������������������������0000755�0000153�0000161�00000000000�12321735647�023075� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/nacl/box/box_test.go�������������������������������0000644�0000153�0000161�00000003570�12321735647�025260� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 box import ( "bytes" "crypto/rand" "encoding/hex" "testing" "code.google.com/p/go.crypto/curve25519" ) func TestSealOpen(t *testing.T) { publicKey1, privateKey1, _ := GenerateKey(rand.Reader) publicKey2, privateKey2, _ := GenerateKey(rand.Reader) if *privateKey1 == *privateKey2 { t.Fatalf("private keys are equal!") } if *publicKey1 == *publicKey2 { t.Fatalf("public keys are equal!") } message := []byte("test message") var nonce [24]byte box := Seal(nil, message, &nonce, publicKey1, privateKey2) opened, ok := Open(nil, box, &nonce, publicKey2, privateKey1) if !ok { t.Fatalf("failed to open box") } if !bytes.Equal(opened, message) { t.Fatalf("got %x, want %x", opened, message) } for i := range box { box[i] ^= 0x40 _, ok := Open(nil, box, &nonce, publicKey2, privateKey1) if ok { t.Fatalf("opened box with byte %d corrupted", i) } box[i] ^= 0x40 } } func TestBox(t *testing.T) { var privateKey1, privateKey2 [32]byte for i := range privateKey1[:] { privateKey1[i] = 1 } for i := range privateKey2[:] { privateKey2[i] = 2 } var publicKey1 [32]byte curve25519.ScalarBaseMult(&publicKey1, &privateKey1) var message [64]byte for i := range message[:] { message[i] = 3 } var nonce [24]byte for i := range nonce[:] { nonce[i] = 4 } box := Seal(nil, message[:], &nonce, &publicKey1, &privateKey2) // expected was generated using the C implementation of NaCl. expected, _ := hex.DecodeString("78ea30b19d2341ebbdba54180f821eec265cf86312549bea8a37652a8bb94f07b78a73ed1708085e6ddd0e943bbdeb8755079a37eb31d86163ce241164a47629c0539f330b4914cd135b3855bc2a2dfc") if !bytes.Equal(box, expected) { t.Fatalf("box didn't match, got\n%x\n, expected\n%x", box, expected) } } ����������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/nacl/box/box.go������������������������������������0000644�0000153�0000161�00000006257�12321735647�024226� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 box authenticates and encrypts messages using public-key cryptography. Box uses Curve25519, XSalsa20 and Poly1305 to encrypt and authenticate messages. The length of messages is not hidden. It is the caller's responsibility to ensure the uniqueness of nonces—for example, by using nonce 1 for the first message, nonce 2 for the second message, etc. Nonces are long enough that randomly generated nonces have negligible risk of collision. This package is interoperable with NaCl: http://nacl.cr.yp.to/box.html. */ package box import ( "code.google.com/p/go.crypto/curve25519" "code.google.com/p/go.crypto/nacl/secretbox" "code.google.com/p/go.crypto/salsa20/salsa" "io" ) // Overhead is the number of bytes of overhead when boxing a message. const Overhead = secretbox.Overhead // GenerateKey generates a new public/private key pair suitable for use with // Seal and Open. func GenerateKey(rand io.Reader) (publicKey, privateKey *[32]byte, err error) { publicKey = new([32]byte) privateKey = new([32]byte) _, err = io.ReadFull(rand, privateKey[:]) if err != nil { publicKey = nil privateKey = nil return } curve25519.ScalarBaseMult(publicKey, privateKey) return } var zeros [16]byte // Precompute calculates the shared key between peersPublicKey and privateKey // and writes it to sharedKey. The shared key can be used with // OpenAfterPrecomputation and SealAfterPrecomputation to speed up processing // when using the same pair of keys repeatedly. func Precompute(sharedKey, peersPublicKey, privateKey *[32]byte) { curve25519.ScalarMult(sharedKey, privateKey, peersPublicKey) salsa.HSalsa20(sharedKey, &zeros, sharedKey, &salsa.Sigma) } // Seal appends an encrypted and authenticated copy of message to out, which // will be Overhead bytes longer than the original and must not overlap. The // nonce must be unique for each distinct message for a given pair of keys. func Seal(out, message []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) []byte { var sharedKey [32]byte Precompute(&sharedKey, peersPublicKey, privateKey) return secretbox.Seal(out, message, nonce, &sharedKey) } // SealAfterPrecomputation performs the same actions as Seal, but takes a // shared key as generated by Precompute. func SealAfterPrecomputation(out, message []byte, nonce *[24]byte, sharedKey *[32]byte) []byte { return secretbox.Seal(out, message, nonce, sharedKey) } // Open authenticates and decrypts a box produced by Seal and appends the // message to out, which must not overlap box. The output will be Overhead // bytes smaller than box. func Open(out, box []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) ([]byte, bool) { var sharedKey [32]byte Precompute(&sharedKey, peersPublicKey, privateKey) return secretbox.Open(out, box, nonce, &sharedKey) } // OpenAfterPrecomputation performs the same actions as Open, but takes a // shared key as generated by Precompute. func OpenAfterPrecomputation(out, box []byte, nonce *[24]byte, sharedKey *[32]byte) ([]byte, bool) { return secretbox.Open(out, box, nonce, sharedKey) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/twofish/�������������������������������������������0000755�0000153�0000161�00000000000�12321735647�023053� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/twofish/twofish_test.go����������������������������0000644�0000153�0000161�00000011363�12321735647�026130� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 twofish import ( "bytes" "testing" ) var qbox = [2][4][16]byte{ { {0x8, 0x1, 0x7, 0xD, 0x6, 0xF, 0x3, 0x2, 0x0, 0xB, 0x5, 0x9, 0xE, 0xC, 0xA, 0x4}, {0xE, 0xC, 0xB, 0x8, 0x1, 0x2, 0x3, 0x5, 0xF, 0x4, 0xA, 0x6, 0x7, 0x0, 0x9, 0xD}, {0xB, 0xA, 0x5, 0xE, 0x6, 0xD, 0x9, 0x0, 0xC, 0x8, 0xF, 0x3, 0x2, 0x4, 0x7, 0x1}, {0xD, 0x7, 0xF, 0x4, 0x1, 0x2, 0x6, 0xE, 0x9, 0xB, 0x3, 0x0, 0x8, 0x5, 0xC, 0xA}, }, { {0x2, 0x8, 0xB, 0xD, 0xF, 0x7, 0x6, 0xE, 0x3, 0x1, 0x9, 0x4, 0x0, 0xA, 0xC, 0x5}, {0x1, 0xE, 0x2, 0xB, 0x4, 0xC, 0x3, 0x7, 0x6, 0xD, 0xA, 0x5, 0xF, 0x9, 0x0, 0x8}, {0x4, 0xC, 0x7, 0x5, 0x1, 0x6, 0x9, 0xA, 0x0, 0xE, 0xD, 0x8, 0x2, 0xB, 0x3, 0xF}, {0xB, 0x9, 0x5, 0x1, 0xC, 0x3, 0xD, 0xE, 0x6, 0x4, 0x7, 0xF, 0x2, 0x0, 0x8, 0xA}, }, } // genSbox generates the variable sbox func genSbox(qi int, x byte) byte { a0, b0 := x/16, x%16 for i := 0; i < 2; i++ { a1 := a0 ^ b0 b1 := (a0 ^ ((b0 << 3) | (b0 >> 1)) ^ (a0 << 3)) & 15 a0 = qbox[qi][2*i][a1] b0 = qbox[qi][2*i+1][b1] } return (b0 << 4) + a0 } func TestSbox(t *testing.T) { for n := range sbox { for m := range sbox[n] { if genSbox(n, byte(m)) != sbox[n][m] { t.Errorf("#%d|%d: sbox value = %d want %d", n, m, sbox[n][m], genSbox(n, byte(m))) } } } } var testVectors = []struct { key []byte dec []byte enc []byte }{ // These tests are extracted from LibTom { []byte{0x9F, 0x58, 0x9F, 0x5C, 0xF6, 0x12, 0x2C, 0x32, 0xB6, 0xBF, 0xEC, 0x2F, 0x2A, 0xE8, 0xC3, 0x5A}, []byte{0xD4, 0x91, 0xDB, 0x16, 0xE7, 0xB1, 0xC3, 0x9E, 0x86, 0xCB, 0x08, 0x6B, 0x78, 0x9F, 0x54, 0x19}, []byte{0x01, 0x9F, 0x98, 0x09, 0xDE, 0x17, 0x11, 0x85, 0x8F, 0xAA, 0xC3, 0xA3, 0xBA, 0x20, 0xFB, 0xC3}, }, { []byte{0x88, 0xB2, 0xB2, 0x70, 0x6B, 0x10, 0x5E, 0x36, 0xB4, 0x46, 0xBB, 0x6D, 0x73, 0x1A, 0x1E, 0x88, 0xEF, 0xA7, 0x1F, 0x78, 0x89, 0x65, 0xBD, 0x44}, []byte{0x39, 0xDA, 0x69, 0xD6, 0xBA, 0x49, 0x97, 0xD5, 0x85, 0xB6, 0xDC, 0x07, 0x3C, 0xA3, 0x41, 0xB2}, []byte{0x18, 0x2B, 0x02, 0xD8, 0x14, 0x97, 0xEA, 0x45, 0xF9, 0xDA, 0xAC, 0xDC, 0x29, 0x19, 0x3A, 0x65}, }, { []byte{0xD4, 0x3B, 0xB7, 0x55, 0x6E, 0xA3, 0x2E, 0x46, 0xF2, 0xA2, 0x82, 0xB7, 0xD4, 0x5B, 0x4E, 0x0D, 0x57, 0xFF, 0x73, 0x9D, 0x4D, 0xC9, 0x2C, 0x1B, 0xD7, 0xFC, 0x01, 0x70, 0x0C, 0xC8, 0x21, 0x6F}, []byte{0x90, 0xAF, 0xE9, 0x1B, 0xB2, 0x88, 0x54, 0x4F, 0x2C, 0x32, 0xDC, 0x23, 0x9B, 0x26, 0x35, 0xE6}, []byte{0x6C, 0xB4, 0x56, 0x1C, 0x40, 0xBF, 0x0A, 0x97, 0x05, 0x93, 0x1C, 0xB6, 0xD4, 0x08, 0xE7, 0xFA}, }, // These test are derived from http://www.schneier.com/code/ecb_ival.txt { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x9F, 0x58, 0x9F, 0x5C, 0xF6, 0x12, 0x2C, 0x32, 0xB6, 0xBF, 0xEC, 0x2F, 0x2A, 0xE8, 0xC3, 0x5A}, }, { []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, }, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0xCF, 0xD1, 0xD2, 0xE5, 0xA9, 0xBE, 0x9C, 0xDF, 0x50, 0x1F, 0x13, 0xB8, 0x92, 0xBD, 0x22, 0x48}, }, { []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, }, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x37, 0x52, 0x7B, 0xE0, 0x05, 0x23, 0x34, 0xB8, 0x9F, 0x0C, 0xFC, 0xCA, 0xE8, 0x7C, 0xFA, 0x20}, }, } func TestCipher(t *testing.T) { for n, tt := range testVectors { // Test if the plaintext (dec) is encrypts to the given // ciphertext (enc) using the given key. Test also if enc can // be decrypted again into dec. c, err := NewCipher(tt.key) if err != nil { t.Errorf("#%d: NewCipher: %v", n, err) return } buf := make([]byte, 16) c.Encrypt(buf, tt.dec) if !bytes.Equal(buf, tt.enc) { t.Errorf("#%d: encrypt = %x want %x", n, buf, tt.enc) } c.Decrypt(buf, tt.enc) if !bytes.Equal(buf, tt.dec) { t.Errorf("#%d: decrypt = %x want %x", n, buf, tt.dec) } // Test that 16 zero bytes, encrypted 1000 times then decrypted // 1000 times results in zero bytes again. zero := make([]byte, 16) buf = make([]byte, 16) for i := 0; i < 1000; i++ { c.Encrypt(buf, buf) } for i := 0; i < 1000; i++ { c.Decrypt(buf, buf) } if !bytes.Equal(buf, zero) { t.Errorf("#%d: encrypt/decrypt 1000: have %x want %x", n, buf, zero) } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/twofish/twofish.go���������������������������������0000644�0000153�0000161�00000027226�12321735647�025076� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 twofish implements Bruce Schneier's Twofish encryption algorithm. package twofish // Twofish is defined in http://www.schneier.com/paper-twofish-paper.pdf [TWOFISH] // This code is a port of the LibTom C implementation. // See http://libtom.org/?page=features&newsitems=5&whatfile=crypt. // LibTomCrypt is free for all purposes under the public domain. // It was heavily inspired by the go blowfish package. import "strconv" // BlockSize is the constant block size of Twofish. const BlockSize = 16 const mdsPolynomial = 0x169 // x^8 + x^6 + x^5 + x^3 + 1, see [TWOFISH] 4.2 const rsPolynomial = 0x14d // x^8 + x^6 + x^3 + x^2 + 1, see [TWOFISH] 4.3 // A Cipher is an instance of Twofish encryption using a particular key. type Cipher struct { s [4][256]uint32 k [40]uint32 } type KeySizeError int func (k KeySizeError) Error() string { return "crypto/twofish: invalid key size " + strconv.Itoa(int(k)) } // NewCipher creates and returns a Cipher. // The key argument should be the Twofish key, 16, 24 or 32 bytes. func NewCipher(key []byte) (*Cipher, error) { keylen := len(key) if keylen != 16 && keylen != 24 && keylen != 32 { return nil, KeySizeError(keylen) } // k is the number of 64 bit words in key k := keylen / 8 // Create the S[..] words var S [4 * 4]byte for i := 0; i < k; i++ { // Computes [y0 y1 y2 y3] = rs . [x0 x1 x2 x3 x4 x5 x6 x7] for j, rsRow := range rs { for k, rsVal := range rsRow { S[4*i+j] ^= gfMult(key[8*i+k], rsVal, rsPolynomial) } } } // Calculate subkeys c := new(Cipher) var tmp [4]byte for i := byte(0); i < 20; i++ { // A = h(p * 2x, Me) for j := range tmp { tmp[j] = 2 * i } A := h(tmp[:], key, 0) // B = rolc(h(p * (2x + 1), Mo), 8) for j := range tmp { tmp[j] = 2*i + 1 } B := h(tmp[:], key, 1) B = rol(B, 8) c.k[2*i] = A + B // K[2i+1] = (A + 2B) <<< 9 c.k[2*i+1] = rol(2*B+A, 9) } // Calculate sboxes switch k { case 2: for i := range c.s[0] { c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][byte(i)]^S[0]]^S[4]], 0) c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][byte(i)]^S[1]]^S[5]], 1) c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][byte(i)]^S[2]]^S[6]], 2) c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][byte(i)]^S[3]]^S[7]], 3) } case 3: for i := range c.s[0] { c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]], 0) c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[1]]^S[5]]^S[9]], 1) c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]], 2) c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[3]]^S[7]]^S[11]], 3) } default: for i := range c.s[0] { c.s[0][i] = mdsColumnMult(sbox[1][sbox[0][sbox[0][sbox[1][sbox[1][byte(i)]^S[0]]^S[4]]^S[8]]^S[12]], 0) c.s[1][i] = mdsColumnMult(sbox[0][sbox[0][sbox[1][sbox[1][sbox[0][byte(i)]^S[1]]^S[5]]^S[9]]^S[13]], 1) c.s[2][i] = mdsColumnMult(sbox[1][sbox[1][sbox[0][sbox[0][sbox[0][byte(i)]^S[2]]^S[6]]^S[10]]^S[14]], 2) c.s[3][i] = mdsColumnMult(sbox[0][sbox[1][sbox[1][sbox[0][sbox[1][byte(i)]^S[3]]^S[7]]^S[11]]^S[15]], 3) } } return c, nil } // BlockSize returns the Twofish block size, 16 bytes. func (c *Cipher) BlockSize() int { return BlockSize } // store32l stores src in dst in little-endian form. func store32l(dst []byte, src uint32) { dst[0] = byte(src) dst[1] = byte(src >> 8) dst[2] = byte(src >> 16) dst[3] = byte(src >> 24) return } // load32l reads a little-endian uint32 from src. func load32l(src []byte) uint32 { return uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24 } // rol returns x after a left circular rotation of y bits. func rol(x, y uint32) uint32 { return (x << (y & 31)) | (x >> (32 - (y & 31))) } // ror returns x after a right circular rotation of y bits. func ror(x, y uint32) uint32 { return (x >> (y & 31)) | (x << (32 - (y & 31))) } // The RS matrix. See [TWOFISH] 4.3 var rs = [4][8]byte{ {0x01, 0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E}, {0xA4, 0x56, 0x82, 0xF3, 0x1E, 0xC6, 0x68, 0xE5}, {0x02, 0xA1, 0xFC, 0xC1, 0x47, 0xAE, 0x3D, 0x19}, {0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E, 0x03}, } // sbox tables var sbox = [2][256]byte{ { 0xa9, 0x67, 0xb3, 0xe8, 0x04, 0xfd, 0xa3, 0x76, 0x9a, 0x92, 0x80, 0x78, 0xe4, 0xdd, 0xd1, 0x38, 0x0d, 0xc6, 0x35, 0x98, 0x18, 0xf7, 0xec, 0x6c, 0x43, 0x75, 0x37, 0x26, 0xfa, 0x13, 0x94, 0x48, 0xf2, 0xd0, 0x8b, 0x30, 0x84, 0x54, 0xdf, 0x23, 0x19, 0x5b, 0x3d, 0x59, 0xf3, 0xae, 0xa2, 0x82, 0x63, 0x01, 0x83, 0x2e, 0xd9, 0x51, 0x9b, 0x7c, 0xa6, 0xeb, 0xa5, 0xbe, 0x16, 0x0c, 0xe3, 0x61, 0xc0, 0x8c, 0x3a, 0xf5, 0x73, 0x2c, 0x25, 0x0b, 0xbb, 0x4e, 0x89, 0x6b, 0x53, 0x6a, 0xb4, 0xf1, 0xe1, 0xe6, 0xbd, 0x45, 0xe2, 0xf4, 0xb6, 0x66, 0xcc, 0x95, 0x03, 0x56, 0xd4, 0x1c, 0x1e, 0xd7, 0xfb, 0xc3, 0x8e, 0xb5, 0xe9, 0xcf, 0xbf, 0xba, 0xea, 0x77, 0x39, 0xaf, 0x33, 0xc9, 0x62, 0x71, 0x81, 0x79, 0x09, 0xad, 0x24, 0xcd, 0xf9, 0xd8, 0xe5, 0xc5, 0xb9, 0x4d, 0x44, 0x08, 0x86, 0xe7, 0xa1, 0x1d, 0xaa, 0xed, 0x06, 0x70, 0xb2, 0xd2, 0x41, 0x7b, 0xa0, 0x11, 0x31, 0xc2, 0x27, 0x90, 0x20, 0xf6, 0x60, 0xff, 0x96, 0x5c, 0xb1, 0xab, 0x9e, 0x9c, 0x52, 0x1b, 0x5f, 0x93, 0x0a, 0xef, 0x91, 0x85, 0x49, 0xee, 0x2d, 0x4f, 0x8f, 0x3b, 0x47, 0x87, 0x6d, 0x46, 0xd6, 0x3e, 0x69, 0x64, 0x2a, 0xce, 0xcb, 0x2f, 0xfc, 0x97, 0x05, 0x7a, 0xac, 0x7f, 0xd5, 0x1a, 0x4b, 0x0e, 0xa7, 0x5a, 0x28, 0x14, 0x3f, 0x29, 0x88, 0x3c, 0x4c, 0x02, 0xb8, 0xda, 0xb0, 0x17, 0x55, 0x1f, 0x8a, 0x7d, 0x57, 0xc7, 0x8d, 0x74, 0xb7, 0xc4, 0x9f, 0x72, 0x7e, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, 0x6e, 0x50, 0xde, 0x68, 0x65, 0xbc, 0xdb, 0xf8, 0xc8, 0xa8, 0x2b, 0x40, 0xdc, 0xfe, 0x32, 0xa4, 0xca, 0x10, 0x21, 0xf0, 0xd3, 0x5d, 0x0f, 0x00, 0x6f, 0x9d, 0x36, 0x42, 0x4a, 0x5e, 0xc1, 0xe0, }, { 0x75, 0xf3, 0xc6, 0xf4, 0xdb, 0x7b, 0xfb, 0xc8, 0x4a, 0xd3, 0xe6, 0x6b, 0x45, 0x7d, 0xe8, 0x4b, 0xd6, 0x32, 0xd8, 0xfd, 0x37, 0x71, 0xf1, 0xe1, 0x30, 0x0f, 0xf8, 0x1b, 0x87, 0xfa, 0x06, 0x3f, 0x5e, 0xba, 0xae, 0x5b, 0x8a, 0x00, 0xbc, 0x9d, 0x6d, 0xc1, 0xb1, 0x0e, 0x80, 0x5d, 0xd2, 0xd5, 0xa0, 0x84, 0x07, 0x14, 0xb5, 0x90, 0x2c, 0xa3, 0xb2, 0x73, 0x4c, 0x54, 0x92, 0x74, 0x36, 0x51, 0x38, 0xb0, 0xbd, 0x5a, 0xfc, 0x60, 0x62, 0x96, 0x6c, 0x42, 0xf7, 0x10, 0x7c, 0x28, 0x27, 0x8c, 0x13, 0x95, 0x9c, 0xc7, 0x24, 0x46, 0x3b, 0x70, 0xca, 0xe3, 0x85, 0xcb, 0x11, 0xd0, 0x93, 0xb8, 0xa6, 0x83, 0x20, 0xff, 0x9f, 0x77, 0xc3, 0xcc, 0x03, 0x6f, 0x08, 0xbf, 0x40, 0xe7, 0x2b, 0xe2, 0x79, 0x0c, 0xaa, 0x82, 0x41, 0x3a, 0xea, 0xb9, 0xe4, 0x9a, 0xa4, 0x97, 0x7e, 0xda, 0x7a, 0x17, 0x66, 0x94, 0xa1, 0x1d, 0x3d, 0xf0, 0xde, 0xb3, 0x0b, 0x72, 0xa7, 0x1c, 0xef, 0xd1, 0x53, 0x3e, 0x8f, 0x33, 0x26, 0x5f, 0xec, 0x76, 0x2a, 0x49, 0x81, 0x88, 0xee, 0x21, 0xc4, 0x1a, 0xeb, 0xd9, 0xc5, 0x39, 0x99, 0xcd, 0xad, 0x31, 0x8b, 0x01, 0x18, 0x23, 0xdd, 0x1f, 0x4e, 0x2d, 0xf9, 0x48, 0x4f, 0xf2, 0x65, 0x8e, 0x78, 0x5c, 0x58, 0x19, 0x8d, 0xe5, 0x98, 0x57, 0x67, 0x7f, 0x05, 0x64, 0xaf, 0x63, 0xb6, 0xfe, 0xf5, 0xb7, 0x3c, 0xa5, 0xce, 0xe9, 0x68, 0x44, 0xe0, 0x4d, 0x43, 0x69, 0x29, 0x2e, 0xac, 0x15, 0x59, 0xa8, 0x0a, 0x9e, 0x6e, 0x47, 0xdf, 0x34, 0x35, 0x6a, 0xcf, 0xdc, 0x22, 0xc9, 0xc0, 0x9b, 0x89, 0xd4, 0xed, 0xab, 0x12, 0xa2, 0x0d, 0x52, 0xbb, 0x02, 0x2f, 0xa9, 0xd7, 0x61, 0x1e, 0xb4, 0x50, 0x04, 0xf6, 0xc2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xbe, 0x91, }, } // gfMult returns a·b in GF(2^8)/p func gfMult(a, b byte, p uint32) byte { B := [2]uint32{0, uint32(b)} P := [2]uint32{0, p} var result uint32 // branchless GF multiplier for i := 0; i < 7; i++ { result ^= B[a&1] a >>= 1 B[1] = P[B[1]>>7] ^ (B[1] << 1) } result ^= B[a&1] return byte(result) } // mdsColumnMult calculates y{col} where [y0 y1 y2 y3] = MDS · [x0] func mdsColumnMult(in byte, col int) uint32 { mul01 := in mul5B := gfMult(in, 0x5B, mdsPolynomial) mulEF := gfMult(in, 0xEF, mdsPolynomial) switch col { case 0: return uint32(mul01) | uint32(mul5B)<<8 | uint32(mulEF)<<16 | uint32(mulEF)<<24 case 1: return uint32(mulEF) | uint32(mulEF)<<8 | uint32(mul5B)<<16 | uint32(mul01)<<24 case 2: return uint32(mul5B) | uint32(mulEF)<<8 | uint32(mul01)<<16 | uint32(mulEF)<<24 case 3: return uint32(mul5B) | uint32(mul01)<<8 | uint32(mulEF)<<16 | uint32(mul5B)<<24 } panic("unreachable") } // h implements the S-box generation function. See [TWOFISH] 4.3.5 func h(in, key []byte, offset int) uint32 { var y [4]byte for x := range y { y[x] = in[x] } switch len(key) / 8 { case 4: y[0] = sbox[1][y[0]] ^ key[4*(6+offset)+0] y[1] = sbox[0][y[1]] ^ key[4*(6+offset)+1] y[2] = sbox[0][y[2]] ^ key[4*(6+offset)+2] y[3] = sbox[1][y[3]] ^ key[4*(6+offset)+3] fallthrough case 3: y[0] = sbox[1][y[0]] ^ key[4*(4+offset)+0] y[1] = sbox[1][y[1]] ^ key[4*(4+offset)+1] y[2] = sbox[0][y[2]] ^ key[4*(4+offset)+2] y[3] = sbox[0][y[3]] ^ key[4*(4+offset)+3] fallthrough case 2: y[0] = sbox[1][sbox[0][sbox[0][y[0]]^key[4*(2+offset)+0]]^key[4*(0+offset)+0]] y[1] = sbox[0][sbox[0][sbox[1][y[1]]^key[4*(2+offset)+1]]^key[4*(0+offset)+1]] y[2] = sbox[1][sbox[1][sbox[0][y[2]]^key[4*(2+offset)+2]]^key[4*(0+offset)+2]] y[3] = sbox[0][sbox[1][sbox[1][y[3]]^key[4*(2+offset)+3]]^key[4*(0+offset)+3]] } // [y0 y1 y2 y3] = MDS . [x0 x1 x2 x3] var mdsMult uint32 for i := range y { mdsMult ^= mdsColumnMult(y[i], i) } return mdsMult } // Encrypt encrypts a 16-byte block from src to dst, which may overlap. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; // instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { S1 := c.s[0] S2 := c.s[1] S3 := c.s[2] S4 := c.s[3] // Load input ia := load32l(src[0:4]) ib := load32l(src[4:8]) ic := load32l(src[8:12]) id := load32l(src[12:16]) // Pre-whitening ia ^= c.k[0] ib ^= c.k[1] ic ^= c.k[2] id ^= c.k[3] for i := 0; i < 8; i++ { k := c.k[8+i*4 : 12+i*4] t2 := S2[byte(ib)] ^ S3[byte(ib>>8)] ^ S4[byte(ib>>16)] ^ S1[byte(ib>>24)] t1 := S1[byte(ia)] ^ S2[byte(ia>>8)] ^ S3[byte(ia>>16)] ^ S4[byte(ia>>24)] + t2 ic = ror(ic^(t1+k[0]), 1) id = rol(id, 1) ^ (t2 + t1 + k[1]) t2 = S2[byte(id)] ^ S3[byte(id>>8)] ^ S4[byte(id>>16)] ^ S1[byte(id>>24)] t1 = S1[byte(ic)] ^ S2[byte(ic>>8)] ^ S3[byte(ic>>16)] ^ S4[byte(ic>>24)] + t2 ia = ror(ia^(t1+k[2]), 1) ib = rol(ib, 1) ^ (t2 + t1 + k[3]) } // Output with "undo last swap" ta := ic ^ c.k[4] tb := id ^ c.k[5] tc := ia ^ c.k[6] td := ib ^ c.k[7] store32l(dst[0:4], ta) store32l(dst[4:8], tb) store32l(dst[8:12], tc) store32l(dst[12:16], td) } // Decrypt decrypts a 16-byte block from src to dst, which may overlap. func (c *Cipher) Decrypt(dst, src []byte) { S1 := c.s[0] S2 := c.s[1] S3 := c.s[2] S4 := c.s[3] // Load input ta := load32l(src[0:4]) tb := load32l(src[4:8]) tc := load32l(src[8:12]) td := load32l(src[12:16]) // Undo undo final swap ia := tc ^ c.k[6] ib := td ^ c.k[7] ic := ta ^ c.k[4] id := tb ^ c.k[5] for i := 8; i > 0; i-- { k := c.k[4+i*4 : 8+i*4] t2 := S2[byte(id)] ^ S3[byte(id>>8)] ^ S4[byte(id>>16)] ^ S1[byte(id>>24)] t1 := S1[byte(ic)] ^ S2[byte(ic>>8)] ^ S3[byte(ic>>16)] ^ S4[byte(ic>>24)] + t2 ia = rol(ia, 1) ^ (t1 + k[2]) ib = ror(ib^(t2+t1+k[3]), 1) t2 = S2[byte(ib)] ^ S3[byte(ib>>8)] ^ S4[byte(ib>>16)] ^ S1[byte(ib>>24)] t1 = S1[byte(ia)] ^ S2[byte(ia>>8)] ^ S3[byte(ia>>16)] ^ S4[byte(ia>>24)] + t2 ic = rol(ic, 1) ^ (t1 + k[0]) id = ror(id^(t2+t1+k[1]), 1) } // Undo pre-whitening ia ^= c.k[0] ib ^= c.k[1] ic ^= c.k[2] id ^= c.k[3] store32l(dst[0:4], ia) store32l(dst[4:8], ib) store32l(dst[8:12], ic) store32l(dst[12:16], id) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/PATENTS��������������������������������������������0000644�0000153�0000161�00000002427�12321735647�022436� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/LICENSE��������������������������������������������0000644�0000153�0000161�00000002707�12321735647�022403� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ���������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/�������������������������������������������0000755�0000153�0000161�00000000000�12321735647�023040� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/elgamal/�����������������������������������0000755�0000153�0000161�00000000000�12321735647�024442� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/elgamal/elgamal.go�������������������������0000644�0000153�0000161�00000006647�12321735647�026410� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 elgamal implements ElGamal encryption, suitable for OpenPGP, // as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on // Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31, // n. 4, 1985, pp. 469-472. // // This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it // unsuitable for other protocols. RSA should be used in preference in any // case. package elgamal import ( "crypto/rand" "crypto/subtle" "errors" "io" "math/big" ) // PublicKey represents an ElGamal public key. type PublicKey struct { G, P, Y *big.Int } // PrivateKey represents an ElGamal private key. type PrivateKey struct { PublicKey X *big.Int } // Encrypt encrypts the given message to the given public key. The result is a // pair of integers. Errors can result from reading random, or because msg is // too large to be encrypted to the public key. func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err error) { pLen := (pub.P.BitLen() + 7) / 8 if len(msg) > pLen-11 { err = errors.New("elgamal: message too long") return } // EM = 0x02 || PS || 0x00 || M em := make([]byte, pLen-1) em[0] = 2 ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):] err = nonZeroRandomBytes(ps, random) if err != nil { return } em[len(em)-len(msg)-1] = 0 copy(mm, msg) m := new(big.Int).SetBytes(em) k, err := rand.Int(random, pub.P) if err != nil { return } c1 = new(big.Int).Exp(pub.G, k, pub.P) s := new(big.Int).Exp(pub.Y, k, pub.P) c2 = s.Mul(s, m) c2.Mod(c2, pub.P) return } // Decrypt takes two integers, resulting from an ElGamal encryption, and // returns the plaintext of the message. An error can result only if the // ciphertext is invalid. Users should keep in mind that this is a padding // oracle and thus, if exposed to an adaptive chosen ciphertext attack, can // be used to break the cryptosystem. See ``Chosen Ciphertext Attacks // Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel // Bleichenbacher, Advances in Cryptology (Crypto '98), func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) { s := new(big.Int).Exp(c1, priv.X, priv.P) s.ModInverse(s, priv.P) s.Mul(s, c2) s.Mod(s, priv.P) em := s.Bytes() firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2) // The remainder of the plaintext must be a string of non-zero random // octets, followed by a 0, followed by the message. // lookingForIndex: 1 iff we are still looking for the zero. // index: the offset of the first zero byte. var lookingForIndex, index int lookingForIndex = 1 for i := 1; i < len(em); i++ { equals0 := subtle.ConstantTimeByteEq(em[i], 0) index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index) lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) } if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 { return nil, errors.New("elgamal: decryption error") } return em[index+1:], nil } // nonZeroRandomBytes fills the given slice with non-zero random octets. func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) { _, err = io.ReadFull(rand, s) if err != nil { return } for i := 0; i < len(s); i++ { for s[i] == 0 { _, err = io.ReadFull(rand, s[i:i+1]) if err != nil { return } } } return } �����������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/elgamal/elgamal_test.go��������������������0000644�0000153�0000161�00000003065�12321735647�027436� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 elgamal import ( "bytes" "crypto/rand" "math/big" "testing" ) // This is the 1024-bit MODP group from RFC 5114, section 2.1: const primeHex = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371" const generatorHex = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5" func fromHex(hex string) *big.Int { n, ok := new(big.Int).SetString(hex, 16) if !ok { panic("failed to parse hex number") } return n } func TestEncryptDecrypt(t *testing.T) { priv := &PrivateKey{ PublicKey: PublicKey{ G: fromHex(generatorHex), P: fromHex(primeHex), }, X: fromHex("42"), } priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P) message := []byte("hello world") c1, c2, err := Encrypt(rand.Reader, &priv.PublicKey, message) if err != nil { t.Errorf("error encrypting: %s", err) } message2, err := Decrypt(priv, c1, c2) if err != nil { t.Errorf("error decrypting: %s", err) } if !bytes.Equal(message2, message) { t.Errorf("decryption failed, got: %x, want: %x", message2, message) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/read_test.go�������������������������������0000644�0000153�0000161�00000074631�12321735647�025354� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 openpgp import ( "bytes" "code.google.com/p/go.crypto/openpgp/errors" _ "crypto/sha512" "encoding/hex" "io" "io/ioutil" "testing" ) func readerFromHex(s string) io.Reader { data, err := hex.DecodeString(s) if err != nil { panic("readerFromHex: bad input") } return bytes.NewBuffer(data) } func TestReadKeyRing(t *testing.T) { kring, err := ReadKeyRing(readerFromHex(testKeys1And2Hex)) if err != nil { t.Error(err) return } if len(kring) != 2 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB || uint32(kring[1].PrimaryKey.KeyId) != 0x1E35246B { t.Errorf("bad keyring: %#v", kring) } } func TestRereadKeyRing(t *testing.T) { kring, err := ReadKeyRing(readerFromHex(testKeys1And2Hex)) if err != nil { t.Errorf("error in initial parse: %s", err) return } out := new(bytes.Buffer) err = kring[0].Serialize(out) if err != nil { t.Errorf("error in serialization: %s", err) return } kring, err = ReadKeyRing(out) if err != nil { t.Errorf("error in second parse: %s", err) return } if len(kring) != 1 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB { t.Errorf("bad keyring: %#v", kring) } } func TestReadPrivateKeyRing(t *testing.T) { kring, err := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) if err != nil { t.Error(err) return } if len(kring) != 2 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB || uint32(kring[1].PrimaryKey.KeyId) != 0x1E35246B || kring[0].PrimaryKey == nil { t.Errorf("bad keyring: %#v", kring) } } func TestReadDSAKey(t *testing.T) { kring, err := ReadKeyRing(readerFromHex(dsaTestKeyHex)) if err != nil { t.Error(err) return } if len(kring) != 1 || uint32(kring[0].PrimaryKey.KeyId) != 0x0CCC0360 { t.Errorf("bad parse: %#v", kring) } } func TestDSAHashTruncatation(t *testing.T) { // dsaKeyWithSHA512 was generated with GnuPG and --cert-digest-algo // SHA512 in order to require DSA hash truncation to verify correctly. _, err := ReadKeyRing(readerFromHex(dsaKeyWithSHA512)) if err != nil { t.Error(err) } } func TestGetKeyById(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) keys := kring.KeysById(0xa34d7e18c20c31bb) if len(keys) != 1 || keys[0].Entity != kring[0] { t.Errorf("bad result for 0xa34d7e18c20c31bb: %#v", keys) } keys = kring.KeysById(0xfd94408d4543314f) if len(keys) != 1 || keys[0].Entity != kring[0] { t.Errorf("bad result for 0xa34d7e18c20c31bb: %#v", keys) } } func checkSignedMessage(t *testing.T, signedHex, expected string) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) md, err := ReadMessage(readerFromHex(signedHex), kring, nil, nil) if err != nil { t.Error(err) return } if !md.IsSigned || md.SignedByKeyId != 0xa34d7e18c20c31bb || md.SignedBy == nil || md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) != 0 || md.IsSymmetricallyEncrypted { t.Errorf("bad MessageDetails: %#v", md) } contents, err := ioutil.ReadAll(md.UnverifiedBody) if err != nil { t.Errorf("error reading UnverifiedBody: %s", err) } if string(contents) != expected { t.Errorf("bad UnverifiedBody got:%s want:%s", string(contents), expected) } if md.SignatureError != nil || md.Signature == nil { t.Errorf("failed to validate: %s", md.SignatureError) } } func TestSignedMessage(t *testing.T) { checkSignedMessage(t, signedMessageHex, signedInput) } func TestTextSignedMessage(t *testing.T) { checkSignedMessage(t, signedTextMessageHex, signedTextInput) } var signedEncryptedMessageTests = []struct { keyRingHex string messageHex string signedByKeyId uint64 encryptedToKeyId uint64 }{ { testKeys1And2PrivateHex, signedEncryptedMessageHex, 0xa34d7e18c20c31bb, 0x2a67d68660df41c7, }, { dsaElGamalTestKeysHex, signedEncryptedMessage2Hex, 0x33af447ccd759b09, 0xcf6a7abcd43e3673, }, } func TestSignedEncryptedMessage(t *testing.T) { for i, test := range signedEncryptedMessageTests { expected := "Signed and encrypted message\n" kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex)) prompt := func(keys []Key, symmetric bool) ([]byte, error) { if symmetric { t.Errorf("prompt: message was marked as symmetrically encrypted") return nil, errors.ErrKeyIncorrect } if len(keys) == 0 { t.Error("prompt: no keys requested") return nil, errors.ErrKeyIncorrect } err := keys[0].PrivateKey.Decrypt([]byte("passphrase")) if err != nil { t.Errorf("prompt: error decrypting key: %s", err) return nil, errors.ErrKeyIncorrect } return nil, nil } md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt, nil) if err != nil { t.Errorf("#%d: error reading message: %s", i, err) return } if !md.IsSigned || md.SignedByKeyId != test.signedByKeyId || md.SignedBy == nil || !md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) == 0 || md.EncryptedToKeyIds[0] != test.encryptedToKeyId { t.Errorf("#%d: bad MessageDetails: %#v", i, md) } contents, err := ioutil.ReadAll(md.UnverifiedBody) if err != nil { t.Errorf("#%d: error reading UnverifiedBody: %s", i, err) } if string(contents) != expected { t.Errorf("#%d: bad UnverifiedBody got:%s want:%s", i, string(contents), expected) } if md.SignatureError != nil || md.Signature == nil { t.Errorf("#%d: failed to validate: %s", i, md.SignatureError) } } } func TestUnspecifiedRecipient(t *testing.T) { expected := "Recipient unspecified\n" kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) md, err := ReadMessage(readerFromHex(recipientUnspecifiedHex), kring, nil, nil) if err != nil { t.Errorf("error reading message: %s", err) return } contents, err := ioutil.ReadAll(md.UnverifiedBody) if err != nil { t.Errorf("error reading UnverifiedBody: %s", err) } if string(contents) != expected { t.Errorf("bad UnverifiedBody got:%s want:%s", string(contents), expected) } } func TestSymmetricallyEncrypted(t *testing.T) { expected := "Symmetrically encrypted.\n" prompt := func(keys []Key, symmetric bool) ([]byte, error) { if len(keys) != 0 { t.Errorf("prompt: len(keys) = %d (want 0)", len(keys)) } if !symmetric { t.Errorf("symmetric is not set") } return []byte("password"), nil } md, err := ReadMessage(readerFromHex(symmetricallyEncryptedCompressedHex), nil, prompt, nil) if err != nil { t.Errorf("ReadMessage: %s", err) return } contents, err := ioutil.ReadAll(md.UnverifiedBody) if err != nil { t.Errorf("ReadAll: %s", err) } expectedCreationTime := uint32(1295992998) if md.LiteralData.Time != expectedCreationTime { t.Errorf("LiteralData.Time is %d, want %d", md.LiteralData.Time, expectedCreationTime) } if string(contents) != expected { t.Errorf("contents got: %s want: %s", string(contents), expected) } } func testDetachedSignature(t *testing.T, kring KeyRing, signature io.Reader, sigInput, tag string, expectedSignerKeyId uint64) { signed := bytes.NewBufferString(sigInput) signer, err := CheckDetachedSignature(kring, signed, signature) if err != nil { t.Errorf("%s: signature error: %s", tag, err) return } if signer == nil { t.Errorf("%s: signer is nil", tag) return } if signer.PrimaryKey.KeyId != expectedSignerKeyId { t.Errorf("%s: wrong signer got:%x want:%x", tag, signer.PrimaryKey.KeyId, expectedSignerKeyId) } } func TestDetachedSignature(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2Hex)) testDetachedSignature(t, kring, readerFromHex(detachedSignatureHex), signedInput, "binary", testKey1KeyId) testDetachedSignature(t, kring, readerFromHex(detachedSignatureTextHex), signedInput, "text", testKey1KeyId) testDetachedSignature(t, kring, readerFromHex(detachedSignatureV3TextHex), signedInput, "v3", testKey1KeyId) } func TestDetachedSignatureDSA(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyHex)) testDetachedSignature(t, kring, readerFromHex(detachedSignatureDSAHex), signedInput, "binary", testKey3KeyId) } func TestReadingArmoredPrivateKey(t *testing.T) { el, err := ReadArmoredKeyRing(bytes.NewBufferString(armoredPrivateKeyBlock)) if err != nil { t.Error(err) } if len(el) != 1 { t.Errorf("got %d entities, wanted 1\n", len(el)) } } func TestNoArmoredData(t *testing.T) { _, err := ReadArmoredKeyRing(bytes.NewBufferString("foo")) if _, ok := err.(errors.InvalidArgumentError); !ok { t.Errorf("error was not an InvalidArgumentError: %s", err) } } const testKey1KeyId = 0xA34D7E18C20C31BB const testKey3KeyId = 0x338934250CCC0360 const signedInput = "Signed message\nline 2\nline 3\n" const signedTextInput = "Signed message\r\nline 2\r\nline 3\r\n" const recipientUnspecifiedHex = "848c0300000000000000000103ff62d4d578d03cf40c3da998dfe216c074fa6ddec5e31c197c9666ba292830d91d18716a80f699f9d897389a90e6d62d0238f5f07a5248073c0f24920e4bc4a30c2d17ee4e0cae7c3d4aaa4e8dced50e3010a80ee692175fa0385f62ecca4b56ee6e9980aa3ec51b61b077096ac9e800edaf161268593eedb6cc7027ff5cb32745d250010d407a6221ae22ef18469b444f2822478c4d190b24d36371a95cb40087cdd42d9399c3d06a53c0673349bfb607927f20d1e122bde1e2bf3aa6cae6edf489629bcaa0689539ae3b718914d88ededc3b" const detachedSignatureHex = "889c04000102000605024d449cd1000a0910a34d7e18c20c31bb167603ff57718d09f28a519fdc7b5a68b6a3336da04df85e38c5cd5d5bd2092fa4629848a33d85b1729402a2aab39c3ac19f9d573f773cc62c264dc924c067a79dfd8a863ae06c7c8686120760749f5fd9b1e03a64d20a7df3446ddc8f0aeadeaeba7cbaee5c1e366d65b6a0c6cc749bcb912d2f15013f812795c2e29eb7f7b77f39ce77" const detachedSignatureTextHex = "889c04010102000605024d449d21000a0910a34d7e18c20c31bbc8c60400a24fbef7342603a41cb1165767bd18985d015fb72fe05db42db36cfb2f1d455967f1e491194fbf6cf88146222b23bf6ffbd50d17598d976a0417d3192ff9cc0034fd00f287b02e90418bbefe609484b09231e4e7a5f3562e199bf39909ab5276c4d37382fe088f6b5c3426fc1052865da8b3ab158672d58b6264b10823dc4b39" const detachedSignatureV3TextHex = "8900950305005255c25ca34d7e18c20c31bb0102bb3f04009f6589ef8a028d6e54f6eaf25432e590d31c3a41f4710897585e10c31e5e332c7f9f409af8512adceaff24d0da1474ab07aa7bce4f674610b010fccc5b579ae5eb00a127f272fb799f988ab8e4574c141da6dbfecfef7e6b2c478d9a3d2551ba741f260ee22bec762812f0053e05380bfdd55ad0f22d8cdf71b233fe51ae8a24" const detachedSignatureDSAHex = "884604001102000605024d6c4eac000a0910338934250ccc0360f18d00a087d743d6405ed7b87755476629600b8b694a39e900a0abff8126f46faf1547c1743c37b21b4ea15b8f83" const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b0020003b88d044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f0011010001889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab0020003988d044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b0020003b88d044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020003" const testKeys1And2PrivateHex = "9501d8044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd00110100010003ff4d91393b9a8e3430b14d6209df42f98dc927425b881f1209f319220841273a802a97c7bdb8b3a7740b3ab5866c4d1d308ad0d3a79bd1e883aacf1ac92dfe720285d10d08752a7efe3c609b1d00f17f2805b217be53999a7da7e493bfc3e9618fd17018991b8128aea70a05dbce30e4fbe626aa45775fa255dd9177aabf4df7cf0200c1ded12566e4bc2bb590455e5becfb2e2c9796482270a943343a7835de41080582c2be3caf5981aa838140e97afa40ad652a0b544f83eb1833b0957dce26e47b0200eacd6046741e9ce2ec5beb6fb5e6335457844fb09477f83b050a96be7da043e17f3a9523567ed40e7a521f818813a8b8a72209f1442844843ccc7eb9805442570200bdafe0438d97ac36e773c7162028d65844c4d463e2420aa2228c6e50dc2743c3d6c72d0d782a5173fe7be2169c8a9f4ef8a7cf3e37165e8c61b89c346cdc6c1799d2b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b00200009d01d8044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f00110100010003fd17a7490c22a79c59281fb7b20f5e6553ec0c1637ae382e8adaea295f50241037f8997cf42c1ce26417e015091451b15424b2c59eb8d4161b0975630408e394d3b00f88d4b4e18e2cc85e8251d4753a27c639c83f5ad4a571c4f19d7cd460b9b73c25ade730c99df09637bd173d8e3e981ac64432078263bb6dc30d3e974150dd0200d0ee05be3d4604d2146fb0457f31ba17c057560785aa804e8ca5530a7cd81d3440d0f4ba6851efcfd3954b7e68908fc0ba47f7ac37bf559c6c168b70d3a7c8cd0200da1c677c4bce06a068070f2b3733b0a714e88d62aa3f9a26c6f5216d48d5c2b5624144f3807c0df30be66b3268eeeca4df1fbded58faf49fc95dc3c35f134f8b01fd1396b6c0fc1b6c4f0eb8f5e44b8eace1e6073e20d0b8bc5385f86f1cf3f050f66af789f3ef1fc107b7f4421e19e0349c730c68f0a226981f4e889054fdb4dc149e8e889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab00200009501fe044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001fe030302e9030f3c783e14856063f16938530e148bc57a7aa3f3e4f90df9dceccdc779bc0835e1ad3d006e4a8d7b36d08b8e0de5a0d947254ecfbd22037e6572b426bcfdc517796b224b0036ff90bc574b5509bede85512f2eefb520fb4b02aa523ba739bff424a6fe81c5041f253f8d757e69a503d3563a104d0d49e9e890b9d0c26f96b55b743883b472caa7050c4acfd4a21f875bdf1258d88bd61224d303dc9df77f743137d51e6d5246b88c406780528fd9a3e15bab5452e5b93970d9dcc79f48b38651b9f15bfbcf6da452837e9cc70683d1bdca94507870f743e4ad902005812488dd342f836e72869afd00ce1850eea4cfa53ce10e3608e13d3c149394ee3cbd0e23d018fcbcb6e2ec5a1a22972d1d462ca05355d0d290dd2751e550d5efb38c6c89686344df64852bf4ff86638708f644e8ec6bd4af9b50d8541cb91891a431326ab2e332faa7ae86cfb6e0540aa63160c1e5cdd5a4add518b303fff0a20117c6bc77f7cfbaf36b04c865c6c2b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b00200009d01fe044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001fe030302e9030f3c783e148560f936097339ae381d63116efcf802ff8b1c9360767db5219cc987375702a4123fd8657d3e22700f23f95020d1b261eda5257e9a72f9a918e8ef22dd5b3323ae03bbc1923dd224db988cadc16acc04b120a9f8b7e84da9716c53e0334d7b66586ddb9014df604b41be1e960dcfcbc96f4ed150a1a0dd070b9eb14276b9b6be413a769a75b519a53d3ecc0c220e85cd91ca354d57e7344517e64b43b6e29823cbd87eae26e2b2e78e6dedfbb76e3e9f77bcb844f9a8932eb3db2c3f9e44316e6f5d60e9e2a56e46b72abe6b06dc9a31cc63f10023d1f5e12d2a3ee93b675c96f504af0001220991c88db759e231b3320dcedf814dcf723fd9857e3d72d66a0f2af26950b915abdf56c1596f46a325bf17ad4810d3535fb02a259b247ac3dbd4cc3ecf9c51b6c07cebb009c1506fba0a89321ec8683e3fd009a6e551d50243e2d5092fefb3321083a4bad91320dc624bd6b5dddf93553e3d53924c05bfebec1fb4bd47e89a1a889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020000" const dsaElGamalTestKeysHex = "9501e1044dfcb16a110400aa3e5c1a1f43dd28c2ffae8abf5cfce555ee874134d8ba0a0f7b868ce2214beddc74e5e1e21ded354a95d18acdaf69e5e342371a71fbb9093162e0c5f3427de413a7f2c157d83f5cd2f9d791256dc4f6f0e13f13c3302af27f2384075ab3021dff7a050e14854bbde0a1094174855fc02f0bae8e00a340d94a1f22b32e48485700a0cec672ac21258fb95f61de2ce1af74b2c4fa3e6703ff698edc9be22c02ae4d916e4fa223f819d46582c0516235848a77b577ea49018dcd5e9e15cff9dbb4663a1ae6dd7580fa40946d40c05f72814b0f88481207e6c0832c3bded4853ebba0a7e3bd8e8c66df33d5a537cd4acf946d1080e7a3dcea679cb2b11a72a33a2b6a9dc85f466ad2ddf4c3db6283fa645343286971e3dd700703fc0c4e290d45767f370831a90187e74e9972aae5bff488eeff7d620af0362bfb95c1a6c3413ab5d15a2e4139e5d07a54d72583914661ed6a87cce810be28a0aa8879a2dd39e52fb6fe800f4f181ac7e328f740cde3d09a05cecf9483e4cca4253e60d4429ffd679d9996a520012aad119878c941e3cf151459873bdfc2a9563472fe0303027a728f9feb3b864260a1babe83925ce794710cfd642ee4ae0e5b9d74cee49e9c67b6cd0ea5dfbb582132195a121356a1513e1bca73e5b80c58c7ccb4164453412f456c47616d616c2054657374204b65792031886204131102002205024dfcb16a021b03060b090807030206150802090a0b0416020301021e01021780000a091033af447ccd759b09fadd00a0b8fd6f5a790bad7e9f2dbb7632046dc4493588db009c087c6a9ba9f7f49fab221587a74788c00db4889ab00200009d0157044dfcb16a1004008dec3f9291205255ccff8c532318133a6840739dd68b03ba942676f9038612071447bf07d00d559c5c0875724ea16a4c774f80d8338b55fca691a0522e530e604215b467bbc9ccfd483a1da99d7bc2648b4318fdbd27766fc8bfad3fddb37c62b8ae7ccfe9577e9b8d1e77c1d417ed2c2ef02d52f4da11600d85d3229607943700030503ff506c94c87c8cab778e963b76cf63770f0a79bf48fb49d3b4e52234620fc9f7657f9f8d56c96a2b7c7826ae6b57ebb2221a3fe154b03b6637cea7e6d98e3e45d87cf8dc432f723d3d71f89c5192ac8d7290684d2c25ce55846a80c9a7823f6acd9bb29fa6cd71f20bc90eccfca20451d0c976e460e672b000df49466408d527affe0303027a728f9feb3b864260abd761730327bca2aaa4ea0525c175e92bf240682a0e83b226f97ecb2e935b62c9a133858ce31b271fa8eb41f6a1b3cd72a63025ce1a75ee4180dcc284884904181102000905024dfcb16a021b0c000a091033af447ccd759b09dd0b009e3c3e7296092c81bee5a19929462caaf2fff3ae26009e218c437a2340e7ea628149af1ec98ec091a43992b00200009501e1044dfcb1be1104009f61faa61aa43df75d128cbe53de528c4aec49ce9360c992e70c77072ad5623de0a3a6212771b66b39a30dad6781799e92608316900518ec01184a85d872365b7d2ba4bacfb5882ea3c2473d3750dc6178cc1cf82147fb58caa28b28e9f12f6d1efcb0534abed644156c91cca4ab78834268495160b2400bc422beb37d237c2300a0cac94911b6d493bda1e1fbc6feeca7cb7421d34b03fe22cec6ccb39675bb7b94a335c2b7be888fd3906a1125f33301d8aa6ec6ee6878f46f73961c8d57a3e9544d8ef2a2cbfd4d52da665b1266928cfe4cb347a58c412815f3b2d2369dec04b41ac9a71cc9547426d5ab941cccf3b18575637ccfb42df1a802df3cfe0a999f9e7109331170e3a221991bf868543960f8c816c28097e503fe319db10fb98049f3a57d7c80c420da66d56f3644371631fad3f0ff4040a19a4fedc2d07727a1b27576f75a4d28c47d8246f27071e12d7a8de62aad216ddbae6aa02efd6b8a3e2818cda48526549791ab277e447b3a36c57cefe9b592f5eab73959743fcc8e83cbefec03a329b55018b53eec196765ae40ef9e20521a603c551efe0303020950d53a146bf9c66034d00c23130cce95576a2ff78016ca471276e8227fb30b1ffbd92e61804fb0c3eff9e30b1a826ee8f3e4730b4d86273ca977b4164453412f456c47616d616c2054657374204b65792032886204131102002205024dfcb1be021b03060b090807030206150802090a0b0416020301021e01021780000a0910a86bf526325b21b22bd9009e34511620415c974750a20df5cb56b182f3b48e6600a0a9466cb1a1305a84953445f77d461593f1d42bc1b00200009d0157044dfcb1be1004009565a951da1ee87119d600c077198f1c1bceb0f7aa54552489298e41ff788fa8f0d43a69871f0f6f77ebdfb14a4260cf9fbeb65d5844b4272a1904dd95136d06c3da745dc46327dd44a0f16f60135914368c8039a34033862261806bb2c5ce1152e2840254697872c85441ccb7321431d75a747a4bfb1d2c66362b51ce76311700030503fc0ea76601c196768070b7365a200e6ddb09307f262d5f39eec467b5f5784e22abdf1aa49226f59ab37cb49969d8f5230ea65caf56015abda62604544ed526c5c522bf92bed178a078789f6c807b6d34885688024a5bed9e9f8c58d11d4b82487b44c5f470c5606806a0443b79cadb45e0f897a561a53f724e5349b9267c75ca17fe0303020950d53a146bf9c660bc5f4ce8f072465e2d2466434320c1e712272fafc20e342fe7608101580fa1a1a367e60486a7cd1246b7ef5586cf5e10b32762b710a30144f12dd17dd4884904181102000905024dfcb1be021b0c000a0910a86bf526325b21b2904c00a0b2b66b4b39ccffda1d10f3ea8d58f827e30a8b8e009f4255b2d8112a184e40cde43a34e8655ca7809370b0020000" const signedMessageHex = "a3019bc0cbccc0c4b8d8b74ee2108fe16ec6d3ca490cbe362d3f8333d3f352531472538b8b13d353b97232f352158c20943157c71c16064626063656269052062e4e01987e9b6fccff4b7df3a34c534b23e679cbec3bc0f8f6e64dfb4b55fe3f8efa9ce110ddb5cd79faf1d753c51aecfa669f7e7aa043436596cccc3359cb7dd6bbe9ecaa69e5989d9e57209571edc0b2fa7f57b9b79a64ee6e99ce1371395fee92fec2796f7b15a77c386ff668ee27f6d38f0baa6c438b561657377bf6acff3c5947befd7bf4c196252f1d6e5c524d0300" const signedTextMessageHex = "a3019bc0cbccc8c4b8d8b74ee2108fe16ec6d36a250cbece0c178233d3f352531472538b8b13d35379b97232f352158ca0b4312f57c71c1646462606365626906a062e4e019811591798ff99bf8afee860b0d8a8c2a85c3387e3bcf0bb3b17987f2bbcfab2aa526d930cbfd3d98757184df3995c9f3e7790e36e3e9779f06089d4c64e9e47dd6202cb6e9bc73c5d11bb59fbaf89d22d8dc7cf199ddf17af96e77c5f65f9bbed56f427bd8db7af37f6c9984bf9385efaf5f184f986fb3e6adb0ecfe35bbf92d16a7aa2a344fb0bc52fb7624f0200" const signedEncryptedMessageHex = "848c032a67d68660df41c70103ff5789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8d2c03b018bd210b1d3791e1aba74b0f1034e122ab72e760492c192383cf5e20b5628bd043272d63df9b923f147eb6091cd897553204832aba48fec54aa447547bb16305a1024713b90e77fd0065f1918271947549205af3c74891af22ee0b56cd29bfec6d6e351901cd4ab3ece7c486f1e32a792d4e474aed98ee84b3f591c7dff37b64e0ecd68fd036d517e412dcadf85840ce184ad7921ad446c4ee28db80447aea1ca8d4f574db4d4e37688158ddd19e14ee2eab4873d46947d65d14a23e788d912cf9a19624ca7352469b72a83866b7c23cb5ace3deab3c7018061b0ba0f39ed2befe27163e5083cf9b8271e3e3d52cc7ad6e2a3bd81d4c3d7022f8d" const signedEncryptedMessage2Hex = "85010e03cf6a7abcd43e36731003fb057f5495b79db367e277cdbe4ab90d924ddee0c0381494112ff8c1238fb0184af35d1731573b01bc4c55ecacd2aafbe2003d36310487d1ecc9ac994f3fada7f9f7f5c3a64248ab7782906c82c6ff1303b69a84d9a9529c31ecafbcdb9ba87e05439897d87e8a2a3dec55e14df19bba7f7bd316291c002ae2efd24f83f9e3441203fc081c0c23dc3092a454ca8a082b27f631abf73aca341686982e8fbda7e0e7d863941d68f3de4a755c2964407f4b5e0477b3196b8c93d551dd23c8beef7d0f03fbb1b6066f78907faf4bf1677d8fcec72651124080e0b7feae6b476e72ab207d38d90b958759fdedfc3c6c35717c9dbfc979b3cfbbff0a76d24a5e57056bb88acbd2a901ef64bc6e4db02adc05b6250ff378de81dca18c1910ab257dff1b9771b85bb9bbe0a69f5989e6d1710a35e6dfcceb7d8fb5ccea8db3932b3d9ff3fe0d327597c68b3622aec8e3716c83a6c93f497543b459b58ba504ed6bcaa747d37d2ca746fe49ae0a6ce4a8b694234e941b5159ff8bd34b9023da2814076163b86f40eed7c9472f81b551452d5ab87004a373c0172ec87ea6ce42ccfa7dbdad66b745496c4873d8019e8c28d6b3" const symmetricallyEncryptedCompressedHex = "8c0d04030302eb4a03808145d0d260c92f714339e13de5a79881216431925bf67ee2898ea61815f07894cd0703c50d0a76ef64d482196f47a8bc729af9b80bb6" const dsaTestKeyHex = "9901a2044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794" const dsaTestKeyPrivateHex = "9501bb044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4d00009f592e0619d823953577d4503061706843317e4fee083db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794" const armoredPrivateKeyBlock = `-----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux) lQHYBE2rFNoBBADFwqWQIW/DSqcB4yCQqnAFTJ27qS5AnB46ccAdw3u4Greeu3Bp idpoHdjULy7zSKlwR1EA873dO/k/e11Ml3dlAFUinWeejWaK2ugFP6JjiieSsrKn vWNicdCS4HTWn0X4sjl0ZiAygw6GNhqEQ3cpLeL0g8E9hnYzJKQ0LWJa0QARAQAB AAP/TB81EIo2VYNmTq0pK1ZXwUpxCrvAAIG3hwKjEzHcbQznsjNvPUihZ+NZQ6+X 0HCfPAdPkGDCLCb6NavcSW+iNnLTrdDnSI6+3BbIONqWWdRDYJhqZCkqmG6zqSfL IdkJgCw94taUg5BWP/AAeQrhzjChvpMQTVKQL5mnuZbUCeMCAN5qrYMP2S9iKdnk VANIFj7656ARKt/nf4CBzxcpHTyB8+d2CtPDKCmlJP6vL8t58Jmih+kHJMvC0dzn gr5f5+sCAOOe5gt9e0am7AvQWhdbHVfJU0TQJx+m2OiCJAqGTB1nvtBLHdJnfdC9 TnXXQ6ZXibqLyBies/xeY2sCKL5qtTMCAKnX9+9d/5yQxRyrQUHt1NYhaXZnJbHx q4ytu0eWz+5i68IYUSK69jJ1NWPM0T6SkqpB3KCAIv68VFm9PxqG1KmhSrQIVGVz dCBLZXmIuAQTAQIAIgUCTasU2gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA CgkQO9o98PRieSoLhgQAkLEZex02Qt7vGhZzMwuN0R22w3VwyYyjBx+fM3JFETy1 ut4xcLJoJfIaF5ZS38UplgakHG0FQ+b49i8dMij0aZmDqGxrew1m4kBfjXw9B/v+ eIqpODryb6cOSwyQFH0lQkXC040pjq9YqDsO5w0WYNXYKDnzRV0p4H1pweo2VDid AdgETasU2gEEAN46UPeWRqKHvA99arOxee38fBt2CI08iiWyI8T3J6ivtFGixSqV bRcPxYO/qLpVe5l84Nb3X71GfVXlc9hyv7CD6tcowL59hg1E/DC5ydI8K8iEpUmK /UnHdIY5h8/kqgGxkY/T/hgp5fRQgW1ZoZxLajVlMRZ8W4tFtT0DeA+JABEBAAEA A/0bE1jaaZKj6ndqcw86jd+QtD1SF+Cf21CWRNeLKnUds4FRRvclzTyUMuWPkUeX TaNNsUOFqBsf6QQ2oHUBBK4VCHffHCW4ZEX2cd6umz7mpHW6XzN4DECEzOVksXtc lUC1j4UB91DC/RNQqwX1IV2QLSwssVotPMPqhOi0ZLNY7wIA3n7DWKInxYZZ4K+6 rQ+POsz6brEoRHwr8x6XlHenq1Oki855pSa1yXIARoTrSJkBtn5oI+f8AzrnN0BN oyeQAwIA/7E++3HDi5aweWrViiul9cd3rcsS0dEnksPhvS0ozCJiHsq/6GFmy7J8 QSHZPteedBnZyNp5jR+H7cIfVN3KgwH/Skq4PsuPhDq5TKK6i8Pc1WW8MA6DXTdU nLkX7RGmMwjC0DBf7KWAlPjFaONAX3a8ndnz//fy1q7u2l9AZwrj1qa1iJ8EGAEC AAkFAk2rFNoCGwwACgkQO9o98PRieSo2/QP/WTzr4ioINVsvN1akKuekmEMI3LAp BfHwatufxxP1U+3Si/6YIk7kuPB9Hs+pRqCXzbvPRrI8NHZBmc8qIGthishdCYad AHcVnXjtxrULkQFGbGvhKURLvS9WnzD/m1K2zzwxzkPTzT9/Yf06O6Mal5AdugPL VrM0m72/jnpKo04= =zNCn -----END PGP PRIVATE KEY BLOCK-----` const dsaKeyWithSHA512 = `9901a2044f04b07f110400db244efecc7316553ee08d179972aab87bb1214de7692593fcf5b6feb1c80fba268722dd464748539b85b81d574cd2d7ad0ca2444de4d849b8756bad7768c486c83a824f9bba4af773d11742bdfb4ac3b89ef8cc9452d4aad31a37e4b630d33927bff68e879284a1672659b8b298222fc68f370f3e24dccacc4a862442b9438b00a0ea444a24088dc23e26df7daf8f43cba3bffc4fe703fe3d6cd7fdca199d54ed8ae501c30e3ec7871ea9cdd4cf63cfe6fc82281d70a5b8bb493f922cd99fba5f088935596af087c8d818d5ec4d0b9afa7f070b3d7c1dd32a84fca08d8280b4890c8da1dde334de8e3cad8450eed2a4a4fcc2db7b8e5528b869a74a7f0189e11ef097ef1253582348de072bb07a9fa8ab838e993cef0ee203ff49298723e2d1f549b00559f886cd417a41692ce58d0ac1307dc71d85a8af21b0cf6eaa14baf2922d3a70389bedf17cc514ba0febbd107675a372fe84b90162a9e88b14d4b1c6be855b96b33fb198c46f058568817780435b6936167ebb3724b680f32bf27382ada2e37a879b3d9de2abe0c3f399350afd1ad438883f4791e2e3b4184453412068617368207472756e636174696f6e207465737488620413110a002205024f04b07f021b03060b090807030206150802090a0b0416020301021e01021780000a0910ef20e0cefca131581318009e2bf3bf047a44d75a9bacd00161ee04d435522397009a03a60d51bd8a568c6c021c8d7cf1be8d990d6417b0020003` �������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/write.go�����������������������������������0000644�0000153�0000161�00000026521�12321735647�024527� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 openpgp import ( "code.google.com/p/go.crypto/openpgp/armor" "code.google.com/p/go.crypto/openpgp/errors" "code.google.com/p/go.crypto/openpgp/packet" "code.google.com/p/go.crypto/openpgp/s2k" "crypto" "hash" "io" "strconv" "time" ) // DetachSign signs message with the private key from signer (which must // already have been decrypted) and writes the signature to w. // If config is nil, sensible defaults will be used. func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { return detachSign(w, signer, message, packet.SigTypeBinary, config) } // ArmoredDetachSign signs message with the private key from signer (which // must already have been decrypted) and writes an armored signature to w. // If config is nil, sensible defaults will be used. func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) { return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config) } // DetachSignText signs message (after canonicalising the line endings) with // the private key from signer (which must already have been decrypted) and // writes the signature to w. // If config is nil, sensible defaults will be used. func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { return detachSign(w, signer, message, packet.SigTypeText, config) } // ArmoredDetachSignText signs message (after canonicalising the line endings) // with the private key from signer (which must already have been decrypted) // and writes an armored signature to w. // If config is nil, sensible defaults will be used. func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { return armoredDetachSign(w, signer, message, packet.SigTypeText, config) } func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { out, err := armor.Encode(w, SignatureType, nil) if err != nil { return } err = detachSign(out, signer, message, sigType, config) if err != nil { return } return out.Close() } func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) { if signer.PrivateKey == nil { return errors.InvalidArgumentError("signing key doesn't have a private key") } if signer.PrivateKey.Encrypted { return errors.InvalidArgumentError("signing key is encrypted") } sig := new(packet.Signature) sig.SigType = sigType sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo sig.Hash = config.Hash() sig.CreationTime = config.Now() sig.IssuerKeyId = &signer.PrivateKey.KeyId h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) if err != nil { return } io.Copy(wrappedHash, message) err = sig.Sign(h, signer.PrivateKey, config) if err != nil { return } return sig.Serialize(w) } // FileHints contains metadata about encrypted files. This metadata is, itself, // encrypted. type FileHints struct { // IsBinary can be set to hint that the contents are binary data. IsBinary bool // FileName hints at the name of the file that should be written. It's // truncated to 255 bytes if longer. It may be empty to suggest that the // file should not be written to disk. It may be equal to "_CONSOLE" to // suggest the data should not be written to disk. FileName string // ModTime contains the modification time of the file, or the zero time if not applicable. ModTime time.Time } // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. // The resulting WriteCloser must be closed after the contents of the file have // been written. // If config is nil, sensible defaults will be used. func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { if hints == nil { hints = &FileHints{} } key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config) if err != nil { return } w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config) if err != nil { return } literaldata := w if algo := config.Compression(); algo != packet.CompressionNone { var compConfig *packet.CompressionConfig if config != nil { compConfig = config.CompressionConfig } literaldata, err = packet.SerializeCompressed(w, algo, compConfig) if err != nil { return } } var epochSeconds uint32 if !hints.ModTime.IsZero() { epochSeconds = uint32(hints.ModTime.Unix()) } return packet.SerializeLiteral(literaldata, hints.IsBinary, hints.FileName, epochSeconds) } // intersectPreferences mutates and returns a prefix of a that contains only // the values in the intersection of a and b. The order of a is preserved. func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) { var j int for _, v := range a { for _, v2 := range b { if v == v2 { a[j] = v j++ break } } } return a[:j] } func hashToHashId(h crypto.Hash) uint8 { v, ok := s2k.HashToHashId(h) if !ok { panic("tried to convert unknown hash") } return v } // Encrypt encrypts a message to a number of recipients and, optionally, signs // it. hints contains optional information, that is also encrypted, that aids // the recipients in processing the message. The resulting WriteCloser must // be closed after the contents of the file have been written. // If config is nil, sensible defaults will be used. func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) { var signer *packet.PrivateKey if signed != nil { signKey, ok := signed.signingKey(config.Now()) if !ok { return nil, errors.InvalidArgumentError("no valid signing keys") } signer = signKey.PrivateKey if signer.Encrypted { return nil, errors.InvalidArgumentError("signing key must be decrypted") } } // These are the possible ciphers that we'll use for the message. candidateCiphers := []uint8{ uint8(packet.CipherAES128), uint8(packet.CipherAES256), uint8(packet.CipherCAST5), } // These are the possible hash functions that we'll use for the signature. candidateHashes := []uint8{ hashToHashId(crypto.SHA256), hashToHashId(crypto.SHA512), hashToHashId(crypto.SHA1), hashToHashId(crypto.RIPEMD160), } // In the event that a recipient doesn't specify any supported ciphers // or hash functions, these are the ones that we assume that every // implementation supports. defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] defaultHashes := candidateHashes[len(candidateHashes)-1:] encryptKeys := make([]Key, len(to)) for i := range to { var ok bool encryptKeys[i], ok = to[i].encryptionKey(config.Now()) if !ok { return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") } sig := to[i].primaryIdentity().SelfSignature preferredSymmetric := sig.PreferredSymmetric if len(preferredSymmetric) == 0 { preferredSymmetric = defaultCiphers } preferredHashes := sig.PreferredHash if len(preferredHashes) == 0 { preferredHashes = defaultHashes } candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) candidateHashes = intersectPreferences(candidateHashes, preferredHashes) } if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") } cipher := packet.CipherFunction(candidateCiphers[0]) // If the cipher specifed by config is a candidate, we'll use that. configuredCipher := config.Cipher() for _, c := range candidateCiphers { cipherFunc := packet.CipherFunction(c) if cipherFunc == configuredCipher { cipher = cipherFunc break } } var hash crypto.Hash for _, hashId := range candidateHashes { if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() { hash = h break } } // If the hash specified by config is a candidate, we'll use that. if configuredHash := config.Hash(); configuredHash.Available() { for _, hashId := range candidateHashes { if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash { hash = h break } } } if hash == 0 { hashId := candidateHashes[0] name, ok := s2k.HashIdToString(hashId) if !ok { name = "#" + strconv.Itoa(int(hashId)) } return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)") } symKey := make([]byte, cipher.KeySize()) if _, err := io.ReadFull(config.Random(), symKey); err != nil { return nil, err } for _, key := range encryptKeys { if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil { return nil, err } } encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config) if err != nil { return } if signer != nil { ops := &packet.OnePassSignature{ SigType: packet.SigTypeBinary, Hash: hash, PubKeyAlgo: signer.PubKeyAlgo, KeyId: signer.KeyId, IsLast: true, } if err := ops.Serialize(encryptedData); err != nil { return nil, err } } if hints == nil { hints = &FileHints{} } w := encryptedData if signer != nil { // If we need to write a signature packet after the literal // data then we need to stop literalData from closing // encryptedData. w = noOpCloser{encryptedData} } var epochSeconds uint32 if !hints.ModTime.IsZero() { epochSeconds = uint32(hints.ModTime.Unix()) } literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) if err != nil { return nil, err } if signer != nil { return signatureWriter{encryptedData, literalData, hash, hash.New(), signer, config}, nil } return literalData, nil } // signatureWriter hashes the contents of a message while passing it along to // literalData. When closed, it closes literalData, writes a signature packet // to encryptedData and then also closes encryptedData. type signatureWriter struct { encryptedData io.WriteCloser literalData io.WriteCloser hashType crypto.Hash h hash.Hash signer *packet.PrivateKey config *packet.Config } func (s signatureWriter) Write(data []byte) (int, error) { s.h.Write(data) return s.literalData.Write(data) } func (s signatureWriter) Close() error { sig := &packet.Signature{ SigType: packet.SigTypeBinary, PubKeyAlgo: s.signer.PubKeyAlgo, Hash: s.hashType, CreationTime: s.config.Now(), IssuerKeyId: &s.signer.KeyId, } if err := sig.Sign(s.h, s.signer, s.config); err != nil { return err } if err := s.literalData.Close(); err != nil { return err } if err := sig.Serialize(s.encryptedData); err != nil { return err } return s.encryptedData.Close() } // noOpCloser is like an ioutil.NopCloser, but for an io.Writer. // TODO: we have two of these in OpenPGP packages alone. This probably needs // to be promoted somewhere more common. type noOpCloser struct { w io.Writer } func (c noOpCloser) Write(data []byte) (n int, err error) { return c.w.Write(data) } func (c noOpCloser) Close() error { return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/write_test.go������������������������������0000644�0000153�0000161�00000013211�12321735647�025556� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 openpgp import ( "bytes" "io" "io/ioutil" "testing" "time" ) func TestSignDetached(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) out := bytes.NewBuffer(nil) message := bytes.NewBufferString(signedInput) err := DetachSign(out, kring[0], message, nil) if err != nil { t.Error(err) } testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId) } func TestSignTextDetached(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) out := bytes.NewBuffer(nil) message := bytes.NewBufferString(signedInput) err := DetachSignText(out, kring[0], message, nil) if err != nil { t.Error(err) } testDetachedSignature(t, kring, out, signedInput, "check", testKey1KeyId) } func TestSignDetachedDSA(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(dsaTestKeyPrivateHex)) out := bytes.NewBuffer(nil) message := bytes.NewBufferString(signedInput) err := DetachSign(out, kring[0], message, nil) if err != nil { t.Error(err) } testDetachedSignature(t, kring, out, signedInput, "check", testKey3KeyId) } func TestNewEntity(t *testing.T) { if testing.Short() { return } e, err := NewEntity("Test User", "test", "test@example.com", nil) if err != nil { t.Errorf("failed to create entity: %s", err) return } w := bytes.NewBuffer(nil) if err := e.SerializePrivate(w, nil); err != nil { t.Errorf("failed to serialize entity: %s", err) return } serialized := w.Bytes() el, err := ReadKeyRing(w) if err != nil { t.Errorf("failed to reparse entity: %s", err) return } if len(el) != 1 { t.Errorf("wrong number of entities found, got %d, want 1", len(el)) } w = bytes.NewBuffer(nil) if err := e.SerializePrivate(w, nil); err != nil { t.Errorf("failed to serialize entity second time: %s", err) return } if !bytes.Equal(w.Bytes(), serialized) { t.Errorf("results differed") } } func TestSymmetricEncryption(t *testing.T) { buf := new(bytes.Buffer) plaintext, err := SymmetricallyEncrypt(buf, []byte("testing"), nil, nil) if err != nil { t.Errorf("error writing headers: %s", err) return } message := []byte("hello world\n") _, err = plaintext.Write(message) if err != nil { t.Errorf("error writing to plaintext writer: %s", err) } err = plaintext.Close() if err != nil { t.Errorf("error closing plaintext writer: %s", err) } md, err := ReadMessage(buf, nil, func(keys []Key, symmetric bool) ([]byte, error) { return []byte("testing"), nil }, nil) if err != nil { t.Errorf("error rereading message: %s", err) } messageBuf := bytes.NewBuffer(nil) _, err = io.Copy(messageBuf, md.UnverifiedBody) if err != nil { t.Errorf("error rereading message: %s", err) } if !bytes.Equal(message, messageBuf.Bytes()) { t.Errorf("recovered message incorrect got '%s', want '%s'", messageBuf.Bytes(), message) } } var testEncryptionTests = []struct { keyRingHex string isSigned bool }{ { testKeys1And2PrivateHex, false, }, { testKeys1And2PrivateHex, true, }, { dsaElGamalTestKeysHex, false, }, { dsaElGamalTestKeysHex, true, }, } func TestEncryption(t *testing.T) { for i, test := range testEncryptionTests { kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex)) passphrase := []byte("passphrase") for _, entity := range kring { if entity.PrivateKey != nil && entity.PrivateKey.Encrypted { err := entity.PrivateKey.Decrypt(passphrase) if err != nil { t.Errorf("#%d: failed to decrypt key", i) } } for _, subkey := range entity.Subkeys { if subkey.PrivateKey != nil && subkey.PrivateKey.Encrypted { err := subkey.PrivateKey.Decrypt(passphrase) if err != nil { t.Errorf("#%d: failed to decrypt subkey", i) } } } } var signed *Entity if test.isSigned { signed = kring[0] } buf := new(bytes.Buffer) w, err := Encrypt(buf, kring[:1], signed, nil /* no hints */, nil) if err != nil { t.Errorf("#%d: error in Encrypt: %s", i, err) continue } const message = "testing" _, err = w.Write([]byte(message)) if err != nil { t.Errorf("#%d: error writing plaintext: %s", i, err) continue } err = w.Close() if err != nil { t.Errorf("#%d: error closing WriteCloser: %s", i, err) continue } md, err := ReadMessage(buf, kring, nil /* no prompt */, nil) if err != nil { t.Errorf("#%d: error reading message: %s", i, err) continue } testTime, _ := time.Parse("2006-01-02", "2013-07-01") if test.isSigned { signKey, _ := kring[0].signingKey(testTime) expectedKeyId := signKey.PublicKey.KeyId if md.SignedByKeyId != expectedKeyId { t.Errorf("#%d: message signed by wrong key id, got: %d, want: %d", i, *md.SignedBy, expectedKeyId) } if md.SignedBy == nil { t.Errorf("#%d: failed to find the signing Entity", i) } } plaintext, err := ioutil.ReadAll(md.UnverifiedBody) if err != nil { t.Errorf("#%d: error reading encrypted contents: %s", i, err) continue } encryptKey, _ := kring[0].encryptionKey(testTime) expectedKeyId := encryptKey.PublicKey.KeyId if len(md.EncryptedToKeyIds) != 1 || md.EncryptedToKeyIds[0] != expectedKeyId { t.Errorf("#%d: expected message to be encrypted to %v, but got %#v", i, expectedKeyId, md.EncryptedToKeyIds) } if string(plaintext) != message { t.Errorf("#%d: got: %s, want: %s", i, string(plaintext), message) } if test.isSigned { if md.SignatureError != nil { t.Errorf("#%d: signature error: %s", i, md.SignatureError) } if md.Signature == nil { t.Error("signature missing") } } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/������������������������������������0000755�0000153�0000161�00000000000�12321735647�024307� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/public_key_test.go������������������0000644�0000153�0000161�00000017742�12321735647�030036� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "encoding/hex" "testing" "time" ) var pubKeyTests = []struct { hexData string hexFingerprint string creationTime time.Time pubKeyAlgo PublicKeyAlgorithm keyId uint64 keyIdString string keyIdShort string }{ {rsaPkDataHex, rsaFingerprintHex, time.Unix(0x4d3c5c10, 0), PubKeyAlgoRSA, 0xa34d7e18c20c31bb, "A34D7E18C20C31BB", "C20C31BB"}, {dsaPkDataHex, dsaFingerprintHex, time.Unix(0x4d432f89, 0), PubKeyAlgoDSA, 0x8e8fbe54062f19ed, "8E8FBE54062F19ED", "062F19ED"}, {ecdsaPkDataHex, ecdsaFingerprintHex, time.Unix(0x5071c294, 0), PubKeyAlgoECDSA, 0x43fe956c542ca00b, "43FE956C542CA00B", "542CA00B"}, } func TestPublicKeyRead(t *testing.T) { for i, test := range pubKeyTests { packet, err := Read(readerFromHex(test.hexData)) if err != nil { t.Errorf("#%d: Read error: %s", i, err) continue } pk, ok := packet.(*PublicKey) if !ok { t.Errorf("#%d: failed to parse, got: %#v", i, packet) continue } if pk.PubKeyAlgo != test.pubKeyAlgo { t.Errorf("#%d: bad public key algorithm got:%x want:%x", i, pk.PubKeyAlgo, test.pubKeyAlgo) } if !pk.CreationTime.Equal(test.creationTime) { t.Errorf("#%d: bad creation time got:%v want:%v", i, pk.CreationTime, test.creationTime) } expectedFingerprint, _ := hex.DecodeString(test.hexFingerprint) if !bytes.Equal(expectedFingerprint, pk.Fingerprint[:]) { t.Errorf("#%d: bad fingerprint got:%x want:%x", i, pk.Fingerprint[:], expectedFingerprint) } if pk.KeyId != test.keyId { t.Errorf("#%d: bad keyid got:%x want:%x", i, pk.KeyId, test.keyId) } if g, e := pk.KeyIdString(), test.keyIdString; g != e { t.Errorf("#%d: bad KeyIdString got:%q want:%q", i, g, e) } if g, e := pk.KeyIdShortString(), test.keyIdShort; g != e { t.Errorf("#%d: bad KeyIdShortString got:%q want:%q", i, g, e) } } } func TestPublicKeySerialize(t *testing.T) { for i, test := range pubKeyTests { packet, err := Read(readerFromHex(test.hexData)) if err != nil { t.Errorf("#%d: Read error: %s", i, err) continue } pk, ok := packet.(*PublicKey) if !ok { t.Errorf("#%d: failed to parse, got: %#v", i, packet) continue } serializeBuf := bytes.NewBuffer(nil) err = pk.Serialize(serializeBuf) if err != nil { t.Errorf("#%d: failed to serialize: %s", i, err) continue } packet, err = Read(serializeBuf) if err != nil { t.Errorf("#%d: Read error (from serialized data): %s", i, err) continue } pk, ok = packet.(*PublicKey) if !ok { t.Errorf("#%d: failed to parse serialized data, got: %#v", i, packet) continue } } } func TestEcc384Serialize(t *testing.T) { r := readerFromHex(ecc384PubHex) var w bytes.Buffer for i := 0; i < 2; i++ { // Public key p, err := Read(r) if err != nil { t.Error(err) } pubkey := p.(*PublicKey) if !bytes.Equal(pubkey.ec.oid, []byte{0x2b, 0x81, 0x04, 0x00, 0x22}) { t.Errorf("Unexpected pubkey OID: %x", pubkey.ec.oid) } if !bytes.Equal(pubkey.ec.p.bytes[:5], []byte{0x04, 0xf6, 0xb8, 0xc5, 0xac}) { t.Errorf("Unexpected pubkey P[:5]: %x", pubkey.ec.p.bytes) } if pubkey.KeyId != 0x098033880F54719F { t.Errorf("Unexpected pubkey ID: %x", pubkey.KeyId) } err = pubkey.Serialize(&w) if err != nil { t.Error(err) } // User ID p, err = Read(r) if err != nil { t.Error(err) } uid := p.(*UserId) if uid.Id != "ec_dsa_dh_384 <openpgp@brainhub.org>" { t.Error("Unexpected UID:", uid.Id) } err = uid.Serialize(&w) if err != nil { t.Error(err) } // User ID Sig p, err = Read(r) if err != nil { t.Error(err) } uidSig := p.(*Signature) err = pubkey.VerifyUserIdSignature(uid.Id, uidSig) if err != nil { t.Error(err, ": UID") } err = uidSig.Serialize(&w) if err != nil { t.Error(err) } // Subkey p, err = Read(r) if err != nil { t.Error(err) } subkey := p.(*PublicKey) if !bytes.Equal(subkey.ec.oid, []byte{0x2b, 0x81, 0x04, 0x00, 0x22}) { t.Errorf("Unexpected subkey OID: %x", subkey.ec.oid) } if !bytes.Equal(subkey.ec.p.bytes[:5], []byte{0x04, 0x2f, 0xaa, 0x84, 0x02}) { t.Errorf("Unexpected subkey P[:5]: %x", subkey.ec.p.bytes) } if subkey.ecdh.KdfHash != 0x09 { t.Error("Expected KDF hash function SHA384 (0x09), got", subkey.ecdh.KdfHash) } if subkey.ecdh.KdfAlgo != 0x09 { t.Error("Expected KDF symmetric alg AES256 (0x09), got", subkey.ecdh.KdfAlgo) } if subkey.KeyId != 0xAA8B938F9A201946 { t.Errorf("Unexpected subkey ID: %x", subkey.KeyId) } err = subkey.Serialize(&w) if err != nil { t.Error(err) } // Subkey Sig p, err = Read(r) if err != nil { t.Error(err) } subkeySig := p.(*Signature) err = pubkey.VerifyKeySignature(subkey, subkeySig) if err != nil { t.Error(err) } err = subkeySig.Serialize(&w) if err != nil { t.Error(err) } // Now read back what we've written again r = bytes.NewBuffer(w.Bytes()) w.Reset() } } const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb" const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001" const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed" const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0" const ecdsaFingerprintHex = "9892270b38b8980b05c8d56d43fe956c542ca00b" const ecdsaPkDataHex = "9893045071c29413052b8104002304230401f4867769cedfa52c325018896245443968e52e51d0c2df8d939949cb5b330f2921711fbee1c9b9dddb95d15cb0255e99badeddda7cc23d9ddcaacbc290969b9f24019375d61c2e4e3b36953a28d8b2bc95f78c3f1d592fb24499be348656a7b17e3963187b4361afe497bc5f9f81213f04069f8e1fb9e6a6290ae295ca1a92b894396cb4" // Source: https://sites.google.com/site/brainhub/pgpecckeys#TOC-ECC-NIST-P-384-key const ecc384PubHex = `99006f044d53059213052b81040022030304f6b8c5aced5b84ef9f4a209db2e4a9dfb70d28cb8c10ecd57674a9fa5a67389942b62d5e51367df4c7bfd3f8e500feecf07ed265a621a8ebbbe53e947ec78c677eba143bd1533c2b350e1c29f82313e1e1108eba063be1e64b10e6950e799c2db42465635f6473615f64685f333834203c6f70656e70677040627261696e6875622e6f72673e8900cb04101309005305024d530592301480000000002000077072656665727265642d656d61696c2d656e636f64696e67407067702e636f6d7067706d696d65040b090807021901051b03000000021602051e010000000415090a08000a0910098033880f54719fca2b0180aa37350968bd5f115afd8ce7bc7b103822152dbff06d0afcda835329510905b98cb469ba208faab87c7412b799e7b633017f58364ea480e8a1a3f253a0c5f22c446e8be9a9fce6210136ee30811abbd49139de28b5bdf8dc36d06ae748579e9ff503b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec9180301090989008404181309000c05024d530592051b0c000000000a0910098033880f54719f80970180eee7a6d8fcee41ee4f9289df17f9bcf9d955dca25c583b94336f3a2b2d4986dc5cf417b8d2dc86f741a9e1a6d236c0e3017d1c76575458a0cfb93ae8a2b274fcc65ceecd7a91eec83656ba13219969f06945b48c56bd04152c3a0553c5f2f4bd1267` ������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/reader.go���������������������������0000644�0000153�0000161�00000002754�12321735647�026110� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "code.google.com/p/go.crypto/openpgp/errors" "io" ) // Reader reads packets from an io.Reader and allows packets to be 'unread' so // that they result from the next call to Next. type Reader struct { q []Packet readers []io.Reader } // Next returns the most recently unread Packet, or reads another packet from // the top-most io.Reader. Unknown packet types are skipped. func (r *Reader) Next() (p Packet, err error) { if len(r.q) > 0 { p = r.q[len(r.q)-1] r.q = r.q[:len(r.q)-1] return } for len(r.readers) > 0 { p, err = Read(r.readers[len(r.readers)-1]) if err == nil { return } if err == io.EOF { r.readers = r.readers[:len(r.readers)-1] continue } if _, ok := err.(errors.UnknownPacketTypeError); !ok { return nil, err } } return nil, io.EOF } // Push causes the Reader to start reading from a new io.Reader. When an EOF // error is seen from the new io.Reader, it is popped and the Reader continues // to read from the next most recent io.Reader. func (r *Reader) Push(reader io.Reader) { r.readers = append(r.readers, reader) } // Unread causes the given Packet to be returned from the next call to Next. func (r *Reader) Unread(p Packet) { r.q = append(r.q, p) } func NewReader(r io.Reader) *Reader { return &Reader{ q: nil, readers: []io.Reader{r}, } } ��������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/literal.go��������������������������0000644�0000153�0000161�00000003604�12321735647�026275� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "encoding/binary" "io" ) // LiteralData represents an encrypted file. See RFC 4880, section 5.9. type LiteralData struct { IsBinary bool FileName string Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined. Body io.Reader } // ForEyesOnly returns whether the contents of the LiteralData have been marked // as especially sensitive. func (l *LiteralData) ForEyesOnly() bool { return l.FileName == "_CONSOLE" } func (l *LiteralData) parse(r io.Reader) (err error) { var buf [256]byte _, err = readFull(r, buf[:2]) if err != nil { return } l.IsBinary = buf[0] == 'b' fileNameLen := int(buf[1]) _, err = readFull(r, buf[:fileNameLen]) if err != nil { return } l.FileName = string(buf[:fileNameLen]) _, err = readFull(r, buf[:4]) if err != nil { return } l.Time = binary.BigEndian.Uint32(buf[:4]) l.Body = r return } // SerializeLiteral serializes a literal data packet to w and returns a // WriteCloser to which the data itself can be written and which MUST be closed // on completion. The fileName is truncated to 255 bytes. func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) { var buf [4]byte buf[0] = 't' if isBinary { buf[0] = 'b' } if len(fileName) > 255 { fileName = fileName[:255] } buf[1] = byte(len(fileName)) inner, err := serializeStreamHeader(w, packetTypeLiteralData) if err != nil { return } _, err = inner.Write(buf[:2]) if err != nil { return } _, err = inner.Write([]byte(fileName)) if err != nil { return } binary.BigEndian.PutUint32(buf[:], time) _, err = inner.Write(buf[:]) if err != nil { return } plaintext = inner return } ����������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/compressed_test.go������������������0000644�0000153�0000161�00000001601�12321735647�030037� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "encoding/hex" "io" "io/ioutil" "testing" ) func TestCompressed(t *testing.T) { packet, err := Read(readerFromHex(compressedHex)) if err != nil { t.Errorf("failed to read Compressed: %s", err) return } c, ok := packet.(*Compressed) if !ok { t.Error("didn't find Compressed packet") return } contents, err := ioutil.ReadAll(c.Body) if err != nil && err != io.EOF { t.Error(err) return } expected, _ := hex.DecodeString(compressedExpectedHex) if !bytes.Equal(expected, contents) { t.Errorf("got:%x want:%x", contents, expected) } } const compressedHex = "a3013b2d90c4e02b72e25f727e5e496a5e49b11e1700" const compressedExpectedHex = "cb1062004d14c8fe636f6e74656e74732e0a" �������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/signature_v3_test.go����������������0000644�0000153�0000161�00000005330�12321735647�030307� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "crypto" "encoding/hex" "io" "io/ioutil" "testing" "code.google.com/p/go.crypto/openpgp/armor" ) func TestSignatureV3Read(t *testing.T) { r := v3KeyReader(t) Read(r) // Skip public key Read(r) // Skip uid packet, err := Read(r) // Signature if err != nil { t.Error(err) return } sig, ok := packet.(*SignatureV3) if !ok || sig.SigType != SigTypeGenericCert || sig.PubKeyAlgo != PubKeyAlgoRSA || sig.Hash != crypto.MD5 { t.Errorf("failed to parse, got: %#v", packet) } } func TestSignatureV3Reserialize(t *testing.T) { r := v3KeyReader(t) Read(r) // Skip public key Read(r) // Skip uid packet, err := Read(r) if err != nil { t.Error(err) return } sig := packet.(*SignatureV3) out := new(bytes.Buffer) if err = sig.Serialize(out); err != nil { t.Errorf("error reserializing: %s", err) return } expected, err := ioutil.ReadAll(v3KeyReader(t)) if err != nil { t.Error(err) return } expected = expected[4+141+4+39:] // See pgpdump offsets below, this is where the sig starts if !bytes.Equal(expected, out.Bytes()) { t.Errorf("output doesn't match input (got vs expected):\n%s\n%s", hex.Dump(out.Bytes()), hex.Dump(expected)) } } func v3KeyReader(t *testing.T) io.Reader { armorBlock, err := armor.Decode(bytes.NewBufferString(keySigV3Armor)) if err != nil { t.Fatalf("armor Decode failed: %v", err) } return armorBlock.Body } // keySigV3Armor is some V3 public key I found in an SKS dump. // Old: Public Key Packet(tag 6)(141 bytes) // Ver 4 - new // Public key creation time - Fri Sep 16 17:13:54 CDT 1994 // Pub alg - unknown(pub 0) // Unknown public key(pub 0) // Old: User ID Packet(tag 13)(39 bytes) // User ID - Armin M. Warda <warda@nephilim.ruhr.de> // Old: Signature Packet(tag 2)(149 bytes) // Ver 4 - new // Sig type - unknown(05) // Pub alg - ElGamal Encrypt-Only(pub 16) // Hash alg - unknown(hash 46) // Hashed Sub: unknown(sub 81, critical)(1988 bytes) const keySigV3Armor = `-----BEGIN PGP PUBLIC KEY BLOCK----- Version: SKS 1.0.10 mI0CLnoYogAAAQQA1qwA2SuJwfQ5bCQ6u5t20ulnOtY0gykf7YjiK4LiVeRBwHjGq7v30tGV 5Qti7qqRW4Ww7CDCJc4sZMFnystucR2vLkXaSoNWoFm4Fg47NiisDdhDezHwbVPW6OpCFNSi ZAamtj4QAUBu8j4LswafrJqZqR9336/V3g8Yil2l48kABRG0J0FybWluIE0uIFdhcmRhIDx3 YXJkYUBuZXBoaWxpbS5ydWhyLmRlPoiVAgUQLok2xwXR6zmeWEiZAQE/DgP/WgxPQh40/Po4 gSkWZCDAjNdph7zexvAb0CcUWahcwiBIgg3U5ErCx9I5CNVA9U+s8bNrDZwgSIeBzp3KhWUx 524uhGgm6ZUTOAIKA6CbV6pfqoLpJnRYvXYQU5mIWsNa99wcu2qu18OeEDnztb7aLA6Ra9OF YFCbq4EjXRoOrYM= =LPjs -----END PGP PUBLIC KEY BLOCK-----` ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/userattribute_test.go���������������0000644�0000153�0000161�00000013361�12321735647�030603� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "encoding/base64" "image/color" "image/jpeg" "testing" ) func TestParseUserAttribute(t *testing.T) { r := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(userAttributePacket)) for i := 0; i < 2; i++ { p, err := Read(r) if err != nil { t.Fatal(err) } uat := p.(*UserAttribute) imgs := uat.ImageData() if len(imgs) != 1 { t.Errorf("Unexpected number of images in user attribute packet: %d", len(imgs)) } if len(imgs[0]) != 3395 { t.Errorf("Unexpected JPEG image size: %d", len(imgs[0])) } img, err := jpeg.Decode(bytes.NewBuffer(imgs[0])) if err != nil { t.Errorf("Error decoding JPEG image: %v", err) } // A pixel in my right eye. pixel := color.NRGBAModel.Convert(img.At(56, 36)) ref := color.NRGBA{R: 157, G: 128, B: 124, A: 255} if pixel != ref { t.Errorf("Unexpected pixel color: %v", pixel) } w := bytes.NewBuffer(nil) err = uat.Serialize(w) if err != nil { t.Errorf("Error writing user attribute: %v", err) } r = bytes.NewBuffer(w.Bytes()) } } const userAttributePacket = ` 0cyWzJQBEAABAQAAAAAAAAAAAAAAAP/Y/+AAEEpGSUYAAQIAAAEAAQAA/9sAQwAFAwQEBAMFBAQE BQUFBgcMCAcHBwcPCgsJDBEPEhIRDxEQExYcFxMUGhUQERghGBocHR8fHxMXIiQiHiQcHh8e/9sA QwEFBQUHBgcOCAgOHhQRFB4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e Hh4eHh4eHh4e/8AAEQgAZABkAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYH CAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHw JDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6 g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk 5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIB AgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEX GBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKT lJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX2 9/j5+v/aAAwDAQACEQMRAD8A5uGP06VehQ4pIox04q5EnHSvAep+hIIl4zVuMHGPWmRrUWtalaaN pU2oXsgSGJSxPr6ClvoitErs0Itqjc7BQOpPAFYmrfEnwjojtHNqaXEynBjtx5hH4jj9a8B8d+Od W8UXZjWR4LJT+7t0Jwfc+prnIdO1CWZEW2mZ3HyDactXXDB3V5s8evm1namj6r0H4weCLtxG+ova ueP30RA/MV6not1bX0Ed1ZzxzwyDKvGwZSPqK+Ff+ES8R8t/ZV2oHUmM10Hgbxp4m8BatEfNnWBH /eWshOxx9Kmpg4te49RUM1kn+8Wh9zQ4P1FaMC7l465rjPh14y0fxnoseoaXOpfaPOgJ+eI98j09 67W19M15bi4uzPSqTU480WXkjZkAyAR61DPE6OCSOalWRRgZxjvTb598sfU4FBwx5uY4T4feIm8P TeJbAgc65NIM+8cX+FFeLfF3Vr3SfiNrMFrMypJMJcDPUqP8KK+kpVFyLU+ar037SXqX4hxVpMY7 1UhPpVlT2rybKx9smWYz3NeH/EDVLzxt40j8O6bITaQybPlbKkjq39K9O8fasdH8IahfKxWQRFIy Ou9uB/OuE/Z/0y3j1d9TuyoZCMs5xjuea1pLli5nn46q240l13PcfhN8EvDNtpcEl/CklyVBLuMk mvU/Dfwo0BL/AO13FjEDD/qyV7Vn+CvGPg8zRpJrVm8ikLtEg6+1ew2dxZ3EQaJgysuQPasH7eXW 1zzsbVhT92kk/PsYieEND+zlPs6c/wCyAPyryH4wfCPRtW0u6j+xRLOxLxSoADkDpXY+MPjJ4c0S 9k082d3O8ZKkxw5XI96ytK+IGk+IpFjRpod+Qq3C7QT6A1E6NenaXbqRg6rlLlqS0fRnxjpd1r/w w8afa7GWRPKbZLGeBKmeVNfZngLxNaeKfDdprVjxHcLlkJ5Vh1H5185/tDad9h8XOsqAw3Cb0cjq CfX61P8AsveKf7L8T3fhe5nxa3g324YniQdh9R/KuivTdSmp9TXB1/Z1nRlsfU249QBx1pWfcwI7 Cq6u2Ovamb9rYz16V5x7Psz5q/aJhZfibcupIElvE3H+7j+lFbXx9szP45jlUfeso8/99OKK9elL 3EeNVopzZVharCtxVRGGMk02S5JyFOB69zWTieypnL/GksfB+0cr9oQt69awPhPpD69Y3Ky3DWth CWluGU4LAdq3vibGs/g68BJygVxjrwRW5+ztoRv/AAs8EeCZnO/J/hzz/Kumi4wp3kePjlOdZKPY ml8Mvo6WM9ppi7J0EkQYMzkb1X0wW+bJHGACa+ivg14huZPCkjXUO6SImIYOQAP6UQ2sGneHmiWF CYoSAAuM8etXfhBpMr+EZ3SSNRcMx6ZxWdes6ytBGSwkMNFuo7pnP614Ut9Zn1C4uLySKcwObGFA Qnm4+XcR71h+CfDHiKCQWuv2YWFtw+bBZQD8rcE8n2Ney+GbGGQSM6I7xvtI681rXdp8hKRRp6t3 FYPE1VDlsY1nQjWdl+J8w/tOeDZZ/AMd/EGefTHyxxyYjwfyODXg3waRh8UtEcFh+8Jb8FNfZPxh Ak8J6nbPIsiyW7LnseK+Ofh99ptPHFnf2lu0y2twGcKuSEPB/Q1WHk50miq1o14TXU+xop+On61H NMC6Nis1LgsAcUTSt1APFcXJZn0EqmhyvxA037friTYziBV6f7Tf40Vr3k4aXLx5OMZIzRXZB2ik efJXbPHJJcnaD9aN2R1qoGO8/WkuLlIV+YjdjpXSonQ5lTxfiTwzqCnkeQxx9BWx+zPrQsrBFYja zEfrXL6lfie3khcjY6lSPUGud+G3iA6FrY0uQ/KJsA9gCa0jSvFpnBi6tpKSPu++nsIfDFxeXciR qIicscY4rxTwB8RUkn1axsPEf2LTYx85kTGzqCUP8VcJ47+JOs+I0Hhq1njjt/ufIeSvq1VtE+Gs eoaUbSHUrkHdu3WtuX5Ix81XRh7OL5jirVpV5Whdn0F8C/iX4auVn0i612T7bASoe8wjTAd89K9g vtSt5NMa4t5lkRhgOh3Dn6V8aaz8KZrIR3OlQ6r56LySmSxxz06Vo/CHx34h0rxBP4XvJ5AjK2RP nEbAEj6ZxjPrWM6fMmoswqJxqJ1VZnqHxn1NLPwveqWHmNC2BnnNcD8DfDkGi+CH1m+ijN1qMzNA 4GSIiAMf+hVxPxU8Tapc3c0F9MGCn5GU5BX0Pau3+HmrT3XgXSIJCBHDGdgAx1NYSpezha52Yauq 1dya2Wh2onAIwTj1p0lxxWWLkhRyCKWa5O3ORXOos9KVQluZm83j0oqi84JyWH50Vdmc7ep43d3I t1Z2Iz2FYdxeSTsxyRnvTdVuDNcNluM9KrKcg817NOnZGNbEXdkNckjrXGeIIprPxFFdRHAlIwem COtdmxrG8Q2cd/ZNExw45RvQ1bVjim+dWNzw7eaTD4mN3dndCQCo6hmI5zXpj/Ea/wBHjkh0kwRW xXEfl4yTxXzXZalJDL9nuWKMmRnHcV2Hh3WreCyYXW2SWQhd5P3F6n+lS43d2cTm6d7Ox9EWPxH1 ODQxPqWpCaSU/ukUc4z3/WvKW8UhviAdaMewYZG98gj9c1ymoa8LyWOJHwkTDaVPb0qpr+q2m6Nb cfvNo349az9mou9iZVXNWbub3jm98/Vza2ReV7lsJg/e3dsV654UR9N0K0sZP9ZDGFbHr3rzL4P+ H7rXfEEWr3I3W1qf3IYdW9fwqDxf4k8UeH/G95p08kscHmk25dPlZT0we9YTj7SXKjpw1aNG8mj3 FLv5ccU959ycnmvKPDnxB82YQarGsZPAlTp+IrvIr1ZIgySKwIyCOhFYTpyg9T0qWIhVV4svzPvf IdhgY4orPachj81FRdmtzxqdiZmJ9aQEgdqZcPtmbJ71DJcAZ5r20kkeXJtsfPIQDwPzrG1a+S3i LyHAHvmp7y7HOD1rlNdm+1T7Acovf3o+J2RMpezjzMvrob67pX9o2ShZlYgg/wAWKxZLLWLZ/Ke3 mVh14yK9M+BMC3dre2ko3LHKCB7EV7EngeGQJdQ7HyBkMKS0djgq1W3c+XtK03U522RwzsTwNiEk ntXoHgf4calql9El/G8UZbLfLyfr7V9FeGvh+s+0Lbxxcglu2K1NW1nwN4Gk/wBLuI57tV5jjwzE /QVNS+0dWYRqNvXRFv4eeCodKsY1ggVIY1G3K4z714h+1Jqul3GpwaXYeXJLbzgyyrg4b+6D+HNb vjz436zq9m+naHF/ZdkeGfOZXH17V4Vqt2b29K+ZuOc5bnce5zWdPBShL2lTfojSeJhy+zp/NjVz 1Bwa6DSfFGq6fbJFDKrov8DjPFcu97ZxsUe4jVhwVJ5Bpp1mwQiLewJPXacVq6fNpYyjOUXdHoKf EG8VQHsInbuVcgflRXnt5fIs2FYHgcgUVi8LG+xusdW/mN7U2KgEVkTzPt60UVfQ9eHxGHrV1MGi iD4V25x1qvdgLAMd6KK0pbHm4x++dp8FtUubLxJ5EIjMc+A4Za+qfD8pe1JZVOBmiinW3RyRPMfi R8QPE638+k2l6LK0Hylbddhb6nOa80mlkcmWR2kcnlnOSaKK7qCXKcNdu5narcSrAoBxvODWJIga VckjDdqKKwq/EaQ0gUdbjQ6mr7QGBUcd6tPBC6gtGpOOuKKKie5qn7qIpEXd0HSiiimSf//Z` �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/public_key_v3_test.go���������������0000644�0000153�0000161�00000004476�12321735647�030446� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "encoding/hex" "testing" "time" ) var pubKeyV3Test = struct { hexFingerprint string creationTime time.Time pubKeyAlgo PublicKeyAlgorithm keyId uint64 keyIdString string keyIdShort string }{ "103BECF5BD1E837C89D19E98487767F7", time.Unix(779753634, 0), PubKeyAlgoRSA, 0xDE0F188A5DA5E3C9, "DE0F188A5DA5E3C9", "5DA5E3C9"} func TestPublicKeyV3Read(t *testing.T) { i, test := 0, pubKeyV3Test packet, err := Read(v3KeyReader(t)) if err != nil { t.Fatalf("#%d: Read error: %s", i, err) } pk, ok := packet.(*PublicKeyV3) if !ok { t.Fatalf("#%d: failed to parse, got: %#v", i, packet) } if pk.PubKeyAlgo != test.pubKeyAlgo { t.Errorf("#%d: bad public key algorithm got:%x want:%x", i, pk.PubKeyAlgo, test.pubKeyAlgo) } if !pk.CreationTime.Equal(test.creationTime) { t.Errorf("#%d: bad creation time got:%v want:%v", i, pk.CreationTime, test.creationTime) } expectedFingerprint, _ := hex.DecodeString(test.hexFingerprint) if !bytes.Equal(expectedFingerprint, pk.Fingerprint[:]) { t.Errorf("#%d: bad fingerprint got:%x want:%x", i, pk.Fingerprint[:], expectedFingerprint) } if pk.KeyId != test.keyId { t.Errorf("#%d: bad keyid got:%x want:%x", i, pk.KeyId, test.keyId) } if g, e := pk.KeyIdString(), test.keyIdString; g != e { t.Errorf("#%d: bad KeyIdString got:%q want:%q", i, g, e) } if g, e := pk.KeyIdShortString(), test.keyIdShort; g != e { t.Errorf("#%d: bad KeyIdShortString got:%q want:%q", i, g, e) } } func TestPublicKeyV3Serialize(t *testing.T) { //for i, test := range pubKeyV3Tests { i := 0 packet, err := Read(v3KeyReader(t)) if err != nil { t.Fatalf("#%d: Read error: %s", i, err) } pk, ok := packet.(*PublicKeyV3) if !ok { t.Fatalf("#%d: failed to parse, got: %#v", i, packet) } var serializeBuf bytes.Buffer if err = pk.Serialize(&serializeBuf); err != nil { t.Fatalf("#%d: failed to serialize: %s", i, err) } if packet, err = Read(bytes.NewBuffer(serializeBuf.Bytes())); err != nil { t.Fatalf("#%d: Read error (from serialized data): %s", i, err) } if pk, ok = packet.(*PublicKeyV3); !ok { t.Fatalf("#%d: failed to parse serialized data, got: %#v", i, packet) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/config.go���������������������������0000644�0000153�0000161�00000003320�12321735647�026101� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "crypto" "crypto/rand" "io" "time" ) // Config collects a number of parameters along with sensible defaults. // A nil *Config is valid and results in all default values. type Config struct { // Rand provides the source of entropy. // If nil, the crypto/rand Reader is used. Rand io.Reader // DefaultHash is the default hash function to be used. // If zero, SHA-256 is used. DefaultHash crypto.Hash // DefaultCipher is the cipher to be used. // If zero, AES-128 is used. DefaultCipher CipherFunction // Time returns the current time as the number of seconds since the // epoch. If Time is nil, time.Now is used. Time func() time.Time // DefaultCompressionAlgo is the compression algorithm to be // applied to the plaintext before encryption. If zero, no // compression is done. DefaultCompressionAlgo CompressionAlgo // CompressionConfig configures the compression settings. CompressionConfig *CompressionConfig } func (c *Config) Random() io.Reader { if c == nil || c.Rand == nil { return rand.Reader } return c.Rand } func (c *Config) Hash() crypto.Hash { if c == nil || uint(c.DefaultHash) == 0 { return crypto.SHA256 } return c.DefaultHash } func (c *Config) Cipher() CipherFunction { if c == nil || uint8(c.DefaultCipher) == 0 { return CipherAES128 } return c.DefaultCipher } func (c *Config) Now() time.Time { if c == nil || c.Time == nil { return time.Now() } return c.Time() } func (c *Config) Compression() CompressionAlgo { if c == nil { return CompressionNone } return c.DefaultCompressionAlgo } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/public_key_v3.go��������������������0000644�0000153�0000161�00000017426�12321735647�027406� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "crypto" "crypto/md5" "crypto/rsa" "encoding/binary" "fmt" "hash" "io" "math/big" "strconv" "time" "code.google.com/p/go.crypto/openpgp/errors" ) // PublicKeyV3 represents older, version 3 public keys. These keys are less secure and // should not be used for signing or encrypting. They are supported here only for // parsing version 3 key material and validating signatures. // See RFC 4880, section 5.5.2. type PublicKeyV3 struct { CreationTime time.Time DaysToExpire uint16 PubKeyAlgo PublicKeyAlgorithm PublicKey *rsa.PublicKey Fingerprint [16]byte KeyId uint64 IsSubkey bool n, e parsedMPI } // newRSAPublicKeyV3 returns a PublicKey that wraps the given rsa.PublicKey. // Included here for testing purposes only. RFC 4880, section 5.5.2: // "an implementation MUST NOT generate a V3 key, but MAY accept it." func newRSAPublicKeyV3(creationTime time.Time, pub *rsa.PublicKey) *PublicKeyV3 { pk := &PublicKeyV3{ CreationTime: creationTime, PublicKey: pub, n: fromBig(pub.N), e: fromBig(big.NewInt(int64(pub.E))), } pk.setFingerPrintAndKeyId() return pk } func (pk *PublicKeyV3) parse(r io.Reader) (err error) { // RFC 4880, section 5.5.2 var buf [8]byte if _, err = readFull(r, buf[:]); err != nil { return } if buf[0] < 2 || buf[0] > 3 { return errors.UnsupportedError("public key version") } pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) pk.DaysToExpire = binary.BigEndian.Uint16(buf[5:7]) pk.PubKeyAlgo = PublicKeyAlgorithm(buf[7]) switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: err = pk.parseRSA(r) default: err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) } if err != nil { return } pk.setFingerPrintAndKeyId() return } func (pk *PublicKeyV3) setFingerPrintAndKeyId() { // RFC 4880, section 12.2 fingerPrint := md5.New() fingerPrint.Write(pk.n.bytes) fingerPrint.Write(pk.e.bytes) fingerPrint.Sum(pk.Fingerprint[:0]) pk.KeyId = binary.BigEndian.Uint64(pk.n.bytes[len(pk.n.bytes)-8:]) } // parseRSA parses RSA public key material from the given Reader. See RFC 4880, // section 5.5.2. func (pk *PublicKeyV3) parseRSA(r io.Reader) (err error) { if pk.n.bytes, pk.n.bitLength, err = readMPI(r); err != nil { return } if pk.e.bytes, pk.e.bitLength, err = readMPI(r); err != nil { return } if len(pk.e.bytes) > 3 { err = errors.UnsupportedError("large public exponent") return } rsa := &rsa.PublicKey{N: new(big.Int).SetBytes(pk.n.bytes)} for i := 0; i < len(pk.e.bytes); i++ { rsa.E <<= 8 rsa.E |= int(pk.e.bytes[i]) } pk.PublicKey = rsa return } // SerializeSignaturePrefix writes the prefix for this public key to the given Writer. // The prefix is used when calculating a signature over this public key. See // RFC 4880, section 5.2.4. func (pk *PublicKeyV3) SerializeSignaturePrefix(w io.Writer) { var pLength uint16 switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: pLength += 2 + uint16(len(pk.n.bytes)) pLength += 2 + uint16(len(pk.e.bytes)) default: panic("unknown public key algorithm") } pLength += 6 w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) return } func (pk *PublicKeyV3) Serialize(w io.Writer) (err error) { length := 8 // 8 byte header switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: length += 2 + len(pk.n.bytes) length += 2 + len(pk.e.bytes) default: panic("unknown public key algorithm") } packetType := packetTypePublicKey if pk.IsSubkey { packetType = packetTypePublicSubkey } if err = serializeHeader(w, packetType, length); err != nil { return } return pk.serializeWithoutHeaders(w) } // serializeWithoutHeaders marshals the PublicKey to w in the form of an // OpenPGP public key packet, not including the packet header. func (pk *PublicKeyV3) serializeWithoutHeaders(w io.Writer) (err error) { var buf [8]byte // Version 3 buf[0] = 3 // Creation time t := uint32(pk.CreationTime.Unix()) buf[1] = byte(t >> 24) buf[2] = byte(t >> 16) buf[3] = byte(t >> 8) buf[4] = byte(t) // Days to expire buf[5] = byte(pk.DaysToExpire >> 8) buf[6] = byte(pk.DaysToExpire) // Public key algorithm buf[7] = byte(pk.PubKeyAlgo) if _, err = w.Write(buf[:]); err != nil { return } switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: return writeMPIs(w, pk.n, pk.e) } return errors.InvalidArgumentError("bad public-key algorithm") } // CanSign returns true iff this public key can generate signatures func (pk *PublicKeyV3) CanSign() bool { return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly } // VerifySignatureV3 returns nil iff sig is a valid signature, made by this // public key, of the data hashed into signed. signed is mutated by this call. func (pk *PublicKeyV3) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err error) { if !pk.CanSign() { return errors.InvalidArgumentError("public key cannot generate signatures") } suffix := make([]byte, 5) suffix[0] = byte(sig.SigType) binary.BigEndian.PutUint32(suffix[1:], uint32(sig.CreationTime.Unix())) signed.Write(suffix) hashBytes := signed.Sum(nil) if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { return errors.SignatureError("hash tag doesn't match") } if pk.PubKeyAlgo != sig.PubKeyAlgo { return errors.InvalidArgumentError("public key and signature use different algorithms") } switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: if err = rsa.VerifyPKCS1v15(pk.PublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil { return errors.SignatureError("RSA verification failure") } return default: // V3 public keys only support RSA. panic("shouldn't happen") } panic("unreachable") } // VerifyUserIdSignatureV3 returns nil iff sig is a valid signature, made by this // public key, of id. func (pk *PublicKeyV3) VerifyUserIdSignatureV3(id string, sig *SignatureV3) (err error) { h, err := userIdSignatureV3Hash(id, pk, sig.Hash) if err != nil { return err } return pk.VerifySignatureV3(h, sig) } // VerifyKeySignatureV3 returns nil iff sig is a valid signature, made by this // public key, of signed. func (pk *PublicKeyV3) VerifyKeySignatureV3(signed *PublicKeyV3, sig *SignatureV3) (err error) { h, err := keySignatureHash(pk, signed, sig.Hash) if err != nil { return err } return pk.VerifySignatureV3(h, sig) } // userIdSignatureV3Hash returns a Hash of the message that needs to be signed // to assert that pk is a valid key for id. func userIdSignatureV3Hash(id string, pk signingKey, hfn crypto.Hash) (h hash.Hash, err error) { if h = hfn.New(); h == nil { return nil, errors.UnsupportedError("hash function") } // RFC 4880, section 5.2.4 pk.SerializeSignaturePrefix(h) pk.serializeWithoutHeaders(h) h.Write([]byte(id)) return } // KeyIdString returns the public key's fingerprint in capital hex // (e.g. "6C7EE1B8621CC013"). func (pk *PublicKeyV3) KeyIdString() string { return fmt.Sprintf("%X", pk.KeyId) } // KeyIdShortString returns the short form of public key's fingerprint // in capital hex, as shown by gpg --list-keys (e.g. "621CC013"). func (pk *PublicKeyV3) KeyIdShortString() string { return fmt.Sprintf("%X", pk.KeyId&0xFFFFFFFF) } // BitLength returns the bit length for the given public key. func (pk *PublicKeyV3) BitLength() (bitLength uint16, err error) { switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: bitLength = pk.n.bitLength default: err = errors.InvalidArgumentError("bad public-key algorithm") } return } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/opaque_test.go����������������������0000644�0000153�0000161�00000004705�12321735647�027175� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "encoding/hex" "io" "testing" ) // Test packet.Read error handling in OpaquePacket.Parse, // which attempts to re-read an OpaquePacket as a supported // Packet type. func TestOpaqueParseReason(t *testing.T) { buf, err := hex.DecodeString(UnsupportedKeyHex) if err != nil { t.Fatal(err) } or := NewOpaqueReader(bytes.NewBuffer(buf)) count := 0 badPackets := 0 var uid *UserId for { op, err := or.Next() if err == io.EOF { break } else if err != nil { t.Errorf("#%d: opaque read error: %v", count, err) break } // try to parse opaque packet p, err := op.Parse() switch pkt := p.(type) { case *UserId: uid = pkt case *OpaquePacket: // If an OpaquePacket can't re-parse, packet.Read // certainly had its reasons. if pkt.Reason == nil { t.Errorf("#%d: opaque packet, no reason", count) } else { badPackets++ } } count++ } const expectedBad = 3 // Test post-conditions, make sure we actually parsed packets as expected. if badPackets != expectedBad { t.Errorf("unexpected # unparseable packets: %d (want %d)", badPackets, expectedBad) } if uid == nil { t.Errorf("failed to find expected UID in unsupported keyring") } else if uid.Id != "Armin M. Warda <warda@nephilim.ruhr.de>" { t.Errorf("unexpected UID: %v", uid.Id) } } // This key material has public key and signature packet versions modified to // an unsupported value (1), so that trying to parse the OpaquePacket to // a typed packet will get an error. It also contains a GnuPG trust packet. // (Created with: od -An -t x1 pubring.gpg | xargs | sed 's/ //g') const UnsupportedKeyHex = `988d012e7a18a20000010400d6ac00d92b89c1f4396c243abb9b76d2e9673ad63483291fed88e22b82e255e441c078c6abbbf7d2d195e50b62eeaa915b85b0ec20c225ce2c64c167cacb6e711daf2e45da4a8356a059b8160e3b3628ac0dd8437b31f06d53d6e8ea4214d4a26406a6b63e1001406ef23e0bb3069fac9a99a91f77dfafd5de0f188a5da5e3c9000511b42741726d696e204d2e205761726461203c7761726461406e657068696c696d2e727568722e64653e8900950105102e8936c705d1eb399e58489901013f0e03ff5a0c4f421e34fcfa388129166420c08cd76987bcdec6f01bd0271459a85cc22048820dd4e44ac2c7d23908d540f54facf1b36b0d9c20488781ce9dca856531e76e2e846826e9951338020a03a09b57aa5faa82e9267458bd76105399885ac35af7dc1cbb6aaed7c39e1039f3b5beda2c0e916bd38560509bab81235d1a0ead83b0020000` �����������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/signature.go������������������������0000644�0000153�0000161�00000044073�12321735647�026647� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "code.google.com/p/go.crypto/openpgp/errors" "code.google.com/p/go.crypto/openpgp/s2k" "crypto" "crypto/dsa" "crypto/rsa" "encoding/binary" "hash" "io" "strconv" "time" ) // Signature represents a signature. See RFC 4880, section 5.2. type Signature struct { SigType SignatureType PubKeyAlgo PublicKeyAlgorithm Hash crypto.Hash // HashSuffix is extra data that is hashed in after the signed data. HashSuffix []byte // HashTag contains the first two bytes of the hash for fast rejection // of bad signed data. HashTag [2]byte CreationTime time.Time RSASignature parsedMPI DSASigR, DSASigS parsedMPI ECDSASigR, ECDSASigS parsedMPI // rawSubpackets contains the unparsed subpackets, in order. rawSubpackets []outputSubpacket // The following are optional so are nil when not included in the // signature. SigLifetimeSecs, KeyLifetimeSecs *uint32 PreferredSymmetric, PreferredHash, PreferredCompression []uint8 IssuerKeyId *uint64 IsPrimaryId *bool // FlagsValid is set if any flags were given. See RFC 4880, section // 5.2.3.21 for details. FlagsValid bool FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool outSubpackets []outputSubpacket } func (sig *Signature) parse(r io.Reader) (err error) { // RFC 4880, section 5.2.3 var buf [5]byte _, err = readFull(r, buf[:1]) if err != nil { return } if buf[0] != 4 { err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) return } _, err = readFull(r, buf[:5]) if err != nil { return } sig.SigType = SignatureType(buf[0]) sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA: default: err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) return } var ok bool sig.Hash, ok = s2k.HashIdToHash(buf[2]) if !ok { return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) } hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) l := 6 + hashedSubpacketsLength sig.HashSuffix = make([]byte, l+6) sig.HashSuffix[0] = 4 copy(sig.HashSuffix[1:], buf[:5]) hashedSubpackets := sig.HashSuffix[6:l] _, err = readFull(r, hashedSubpackets) if err != nil { return } // See RFC 4880, section 5.2.4 trailer := sig.HashSuffix[l:] trailer[0] = 4 trailer[1] = 0xff trailer[2] = uint8(l >> 24) trailer[3] = uint8(l >> 16) trailer[4] = uint8(l >> 8) trailer[5] = uint8(l) err = parseSignatureSubpackets(sig, hashedSubpackets, true) if err != nil { return } _, err = readFull(r, buf[:2]) if err != nil { return } unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) unhashedSubpackets := make([]byte, unhashedSubpacketsLength) _, err = readFull(r, unhashedSubpackets) if err != nil { return } err = parseSignatureSubpackets(sig, unhashedSubpackets, false) if err != nil { return } _, err = readFull(r, sig.HashTag[:2]) if err != nil { return } switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) case PubKeyAlgoDSA: sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r) if err == nil { sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) } case PubKeyAlgoECDSA: sig.ECDSASigR.bytes, sig.ECDSASigR.bitLength, err = readMPI(r) if err == nil { sig.ECDSASigS.bytes, sig.ECDSASigS.bitLength, err = readMPI(r) } default: panic("unreachable") } return } // parseSignatureSubpackets parses subpackets of the main signature packet. See // RFC 4880, section 5.2.3.1. func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) { for len(subpackets) > 0 { subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed) if err != nil { return } } if sig.CreationTime.IsZero() { err = errors.StructuralError("no creation time in signature") } return } type signatureSubpacketType uint8 const ( creationTimeSubpacket signatureSubpacketType = 2 signatureExpirationSubpacket signatureSubpacketType = 3 keyExpirationSubpacket signatureSubpacketType = 9 prefSymmetricAlgosSubpacket signatureSubpacketType = 11 issuerSubpacket signatureSubpacketType = 16 prefHashAlgosSubpacket signatureSubpacketType = 21 prefCompressionSubpacket signatureSubpacketType = 22 primaryUserIdSubpacket signatureSubpacketType = 25 keyFlagsSubpacket signatureSubpacketType = 27 ) // parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) { // RFC 4880, section 5.2.3.1 var ( length uint32 packetType signatureSubpacketType isCritical bool ) switch { case subpacket[0] < 192: length = uint32(subpacket[0]) subpacket = subpacket[1:] case subpacket[0] < 255: if len(subpacket) < 2 { goto Truncated } length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192 subpacket = subpacket[2:] default: if len(subpacket) < 5 { goto Truncated } length = uint32(subpacket[1])<<24 | uint32(subpacket[2])<<16 | uint32(subpacket[3])<<8 | uint32(subpacket[4]) subpacket = subpacket[5:] } if length > uint32(len(subpacket)) { goto Truncated } rest = subpacket[length:] subpacket = subpacket[:length] if len(subpacket) == 0 { err = errors.StructuralError("zero length signature subpacket") return } packetType = signatureSubpacketType(subpacket[0] & 0x7f) isCritical = subpacket[0]&0x80 == 0x80 subpacket = subpacket[1:] sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) switch packetType { case creationTimeSubpacket: if !isHashed { err = errors.StructuralError("signature creation time in non-hashed area") return } if len(subpacket) != 4 { err = errors.StructuralError("signature creation time not four bytes") return } t := binary.BigEndian.Uint32(subpacket) sig.CreationTime = time.Unix(int64(t), 0) case signatureExpirationSubpacket: // Signature expiration time, section 5.2.3.10 if !isHashed { return } if len(subpacket) != 4 { err = errors.StructuralError("expiration subpacket with bad length") return } sig.SigLifetimeSecs = new(uint32) *sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) case keyExpirationSubpacket: // Key expiration time, section 5.2.3.6 if !isHashed { return } if len(subpacket) != 4 { err = errors.StructuralError("key expiration subpacket with bad length") return } sig.KeyLifetimeSecs = new(uint32) *sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) case prefSymmetricAlgosSubpacket: // Preferred symmetric algorithms, section 5.2.3.7 if !isHashed { return } sig.PreferredSymmetric = make([]byte, len(subpacket)) copy(sig.PreferredSymmetric, subpacket) case issuerSubpacket: // Issuer, section 5.2.3.5 if len(subpacket) != 8 { err = errors.StructuralError("issuer subpacket with bad length") return } sig.IssuerKeyId = new(uint64) *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) case prefHashAlgosSubpacket: // Preferred hash algorithms, section 5.2.3.8 if !isHashed { return } sig.PreferredHash = make([]byte, len(subpacket)) copy(sig.PreferredHash, subpacket) case prefCompressionSubpacket: // Preferred compression algorithms, section 5.2.3.9 if !isHashed { return } sig.PreferredCompression = make([]byte, len(subpacket)) copy(sig.PreferredCompression, subpacket) case primaryUserIdSubpacket: // Primary User ID, section 5.2.3.19 if !isHashed { return } if len(subpacket) != 1 { err = errors.StructuralError("primary user id subpacket with bad length") return } sig.IsPrimaryId = new(bool) if subpacket[0] > 0 { *sig.IsPrimaryId = true } case keyFlagsSubpacket: // Key flags, section 5.2.3.21 if !isHashed { return } if len(subpacket) == 0 { err = errors.StructuralError("empty key flags subpacket") return } sig.FlagsValid = true if subpacket[0]&1 != 0 { sig.FlagCertify = true } if subpacket[0]&2 != 0 { sig.FlagSign = true } if subpacket[0]&4 != 0 { sig.FlagEncryptCommunications = true } if subpacket[0]&8 != 0 { sig.FlagEncryptStorage = true } default: if isCritical { err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) return } } return Truncated: err = errors.StructuralError("signature subpacket truncated") return } // subpacketLengthLength returns the length, in bytes, of an encoded length value. func subpacketLengthLength(length int) int { if length < 192 { return 1 } if length < 16320 { return 2 } return 5 } // serializeSubpacketLength marshals the given length into to. func serializeSubpacketLength(to []byte, length int) int { // RFC 4880, Section 4.2.2. if length < 192 { to[0] = byte(length) return 1 } if length < 16320 { length -= 192 to[0] = byte((length >> 8) + 192) to[1] = byte(length) return 2 } to[0] = 255 to[1] = byte(length >> 24) to[2] = byte(length >> 16) to[3] = byte(length >> 8) to[4] = byte(length) return 5 } // subpacketsLength returns the serialized length, in bytes, of the given // subpackets. func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) { for _, subpacket := range subpackets { if subpacket.hashed == hashed { length += subpacketLengthLength(len(subpacket.contents) + 1) length += 1 // type byte length += len(subpacket.contents) } } return } // serializeSubpackets marshals the given subpackets into to. func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { for _, subpacket := range subpackets { if subpacket.hashed == hashed { n := serializeSubpacketLength(to, len(subpacket.contents)+1) to[n] = byte(subpacket.subpacketType) to = to[1+n:] n = copy(to, subpacket.contents) to = to[n:] } } return } // KeyExpired returns whether sig is a self-signature of a key that has // expired. func (sig *Signature) KeyExpired(currentTime time.Time) bool { if sig.KeyLifetimeSecs == nil { return false } expiry := sig.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second) return currentTime.After(expiry) } // buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. func (sig *Signature) buildHashSuffix() (err error) { hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) var ok bool l := 6 + hashedSubpacketsLen sig.HashSuffix = make([]byte, l+6) sig.HashSuffix[0] = 4 sig.HashSuffix[1] = uint8(sig.SigType) sig.HashSuffix[2] = uint8(sig.PubKeyAlgo) sig.HashSuffix[3], ok = s2k.HashToHashId(sig.Hash) if !ok { sig.HashSuffix = nil return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) } sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8) sig.HashSuffix[5] = byte(hashedSubpacketsLen) serializeSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true) trailer := sig.HashSuffix[l:] trailer[0] = 4 trailer[1] = 0xff trailer[2] = byte(l >> 24) trailer[3] = byte(l >> 16) trailer[4] = byte(l >> 8) trailer[5] = byte(l) return } func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { err = sig.buildHashSuffix() if err != nil { return } h.Write(sig.HashSuffix) digest = h.Sum(nil) copy(sig.HashTag[:], digest) return } // Sign signs a message with a private key. The hash, h, must contain // the hash of the message to be signed and will be mutated by this function. // On success, the signature is stored in sig. Call Serialize to write it out. // If config is nil, sensible defaults will be used. func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) { sig.outSubpackets = sig.buildSubpackets() digest, err := sig.signPrepareHash(h) if err != nil { return } switch priv.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: sig.RSASignature.bytes, err = rsa.SignPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) case PubKeyAlgoDSA: dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) // Need to truncate hashBytes to match FIPS 186-3 section 4.6. subgroupSize := (dsaPriv.Q.BitLen() + 7) / 8 if len(digest) > subgroupSize { digest = digest[:subgroupSize] } r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) if err == nil { sig.DSASigR.bytes = r.Bytes() sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) sig.DSASigS.bytes = s.Bytes() sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes)) } default: err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) } return } // SignUserId computes a signature from priv, asserting that pub is a valid // key for the identity id. On success, the signature is stored in sig. Call // Serialize to write it out. // If config is nil, sensible defaults will be used. func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error { h, err := userIdSignatureHash(id, pub, sig.Hash) if err != nil { return nil } return sig.Sign(h, priv, config) } // SignKey computes a signature from priv, asserting that pub is a subkey. On // success, the signature is stored in sig. Call Serialize to write it out. // If config is nil, sensible defaults will be used. func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error { h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash) if err != nil { return err } return sig.Sign(h, priv, config) } // Serialize marshals sig to w. Sign, SignUserId or SignKey must have been // called first. func (sig *Signature) Serialize(w io.Writer) (err error) { if len(sig.outSubpackets) == 0 { sig.outSubpackets = sig.rawSubpackets } if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil && sig.ECDSASigR.bytes == nil { return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") } sigLength := 0 switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: sigLength = 2 + len(sig.RSASignature.bytes) case PubKeyAlgoDSA: sigLength = 2 + len(sig.DSASigR.bytes) sigLength += 2 + len(sig.DSASigS.bytes) case PubKeyAlgoECDSA: sigLength = 2 + len(sig.ECDSASigR.bytes) sigLength += 2 + len(sig.ECDSASigS.bytes) default: panic("impossible") } unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) length := len(sig.HashSuffix) - 6 /* trailer not included */ + 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + 2 /* hash tag */ + sigLength err = serializeHeader(w, packetTypeSignature, length) if err != nil { return } _, err = w.Write(sig.HashSuffix[:len(sig.HashSuffix)-6]) if err != nil { return } unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) unhashedSubpackets[1] = byte(unhashedSubpacketsLen) serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) _, err = w.Write(unhashedSubpackets) if err != nil { return } _, err = w.Write(sig.HashTag[:]) if err != nil { return } switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: err = writeMPIs(w, sig.RSASignature) case PubKeyAlgoDSA: err = writeMPIs(w, sig.DSASigR, sig.DSASigS) case PubKeyAlgoECDSA: err = writeMPIs(w, sig.ECDSASigR, sig.ECDSASigS) default: panic("impossible") } return } // outputSubpacket represents a subpacket to be marshaled. type outputSubpacket struct { hashed bool // true if this subpacket is in the hashed area. subpacketType signatureSubpacketType isCritical bool contents []byte } func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { creationTime := make([]byte, 4) binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix())) subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) if sig.IssuerKeyId != nil { keyId := make([]byte, 8) binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) } if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { sigLifetime := make([]byte, 4) binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime}) } // Key flags may only appear in self-signatures or certification signatures. if sig.FlagsValid { var flags byte if sig.FlagCertify { flags |= 1 } if sig.FlagSign { flags |= 2 } if sig.FlagEncryptCommunications { flags |= 4 } if sig.FlagEncryptStorage { flags |= 8 } subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}}) } // The following subpackets may only appear in self-signatures if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { keyLifetime := make([]byte, 4) binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs) subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime}) } if sig.IsPrimaryId != nil && *sig.IsPrimaryId { subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) } if len(sig.PreferredSymmetric) > 0 { subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric}) } if len(sig.PreferredHash) > 0 { subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash}) } if len(sig.PreferredCompression) > 0 { subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) } return } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/packet_test.go����������������������0000644�0000153�0000161�00000014316�12321735647�027151� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "code.google.com/p/go.crypto/openpgp/errors" "encoding/hex" "fmt" "io" "io/ioutil" "testing" ) func TestReadFull(t *testing.T) { var out [4]byte b := bytes.NewBufferString("foo") n, err := readFull(b, out[:3]) if n != 3 || err != nil { t.Errorf("full read failed n:%d err:%s", n, err) } b = bytes.NewBufferString("foo") n, err = readFull(b, out[:4]) if n != 3 || err != io.ErrUnexpectedEOF { t.Errorf("partial read failed n:%d err:%s", n, err) } b = bytes.NewBuffer(nil) n, err = readFull(b, out[:3]) if n != 0 || err != io.ErrUnexpectedEOF { t.Errorf("empty read failed n:%d err:%s", n, err) } } func readerFromHex(s string) io.Reader { data, err := hex.DecodeString(s) if err != nil { panic("readerFromHex: bad input") } return bytes.NewBuffer(data) } var readLengthTests = []struct { hexInput string length int64 isPartial bool err error }{ {"", 0, false, io.ErrUnexpectedEOF}, {"1f", 31, false, nil}, {"c0", 0, false, io.ErrUnexpectedEOF}, {"c101", 256 + 1 + 192, false, nil}, {"e0", 1, true, nil}, {"e1", 2, true, nil}, {"e2", 4, true, nil}, {"ff", 0, false, io.ErrUnexpectedEOF}, {"ff00", 0, false, io.ErrUnexpectedEOF}, {"ff0000", 0, false, io.ErrUnexpectedEOF}, {"ff000000", 0, false, io.ErrUnexpectedEOF}, {"ff00000000", 0, false, nil}, {"ff01020304", 16909060, false, nil}, } func TestReadLength(t *testing.T) { for i, test := range readLengthTests { length, isPartial, err := readLength(readerFromHex(test.hexInput)) if test.err != nil { if err != test.err { t.Errorf("%d: expected different error got:%s want:%s", i, err, test.err) } continue } if err != nil { t.Errorf("%d: unexpected error: %s", i, err) continue } if length != test.length || isPartial != test.isPartial { t.Errorf("%d: bad result got:(%d,%t) want:(%d,%t)", i, length, isPartial, test.length, test.isPartial) } } } var partialLengthReaderTests = []struct { hexInput string err error hexOutput string }{ {"e0", io.ErrUnexpectedEOF, ""}, {"e001", io.ErrUnexpectedEOF, ""}, {"e0010102", nil, "0102"}, {"ff00000000", nil, ""}, {"e10102e1030400", nil, "01020304"}, {"e101", io.ErrUnexpectedEOF, ""}, } func TestPartialLengthReader(t *testing.T) { for i, test := range partialLengthReaderTests { r := &partialLengthReader{readerFromHex(test.hexInput), 0, true} out, err := ioutil.ReadAll(r) if test.err != nil { if err != test.err { t.Errorf("%d: expected different error got:%s want:%s", i, err, test.err) } continue } if err != nil { t.Errorf("%d: unexpected error: %s", i, err) continue } got := fmt.Sprintf("%x", out) if got != test.hexOutput { t.Errorf("%d: got:%s want:%s", i, test.hexOutput, got) } } } var readHeaderTests = []struct { hexInput string structuralError bool unexpectedEOF bool tag int length int64 hexOutput string }{ {"", false, false, 0, 0, ""}, {"7f", true, false, 0, 0, ""}, // Old format headers {"80", false, true, 0, 0, ""}, {"8001", false, true, 0, 1, ""}, {"800102", false, false, 0, 1, "02"}, {"81000102", false, false, 0, 1, "02"}, {"820000000102", false, false, 0, 1, "02"}, {"860000000102", false, false, 1, 1, "02"}, {"83010203", false, false, 0, -1, "010203"}, // New format headers {"c0", false, true, 0, 0, ""}, {"c000", false, false, 0, 0, ""}, {"c00102", false, false, 0, 1, "02"}, {"c0020203", false, false, 0, 2, "0203"}, {"c00202", false, true, 0, 2, ""}, {"c3020203", false, false, 3, 2, "0203"}, } func TestReadHeader(t *testing.T) { for i, test := range readHeaderTests { tag, length, contents, err := readHeader(readerFromHex(test.hexInput)) if test.structuralError { if _, ok := err.(errors.StructuralError); ok { continue } t.Errorf("%d: expected StructuralError, got:%s", i, err) continue } if err != nil { if len(test.hexInput) == 0 && err == io.EOF { continue } if !test.unexpectedEOF || err != io.ErrUnexpectedEOF { t.Errorf("%d: unexpected error from readHeader: %s", i, err) } continue } if int(tag) != test.tag || length != test.length { t.Errorf("%d: got:(%d,%d) want:(%d,%d)", i, int(tag), length, test.tag, test.length) continue } body, err := ioutil.ReadAll(contents) if err != nil { if !test.unexpectedEOF || err != io.ErrUnexpectedEOF { t.Errorf("%d: unexpected error from contents: %s", i, err) } continue } if test.unexpectedEOF { t.Errorf("%d: expected ErrUnexpectedEOF from contents but got no error", i) continue } got := fmt.Sprintf("%x", body) if got != test.hexOutput { t.Errorf("%d: got:%s want:%s", i, got, test.hexOutput) } } } func TestSerializeHeader(t *testing.T) { tag := packetTypePublicKey lengths := []int{0, 1, 2, 64, 192, 193, 8000, 8384, 8385, 10000} for _, length := range lengths { buf := bytes.NewBuffer(nil) serializeHeader(buf, tag, length) tag2, length2, _, err := readHeader(buf) if err != nil { t.Errorf("length %d, err: %s", length, err) } if tag2 != tag { t.Errorf("length %d, tag incorrect (got %d, want %d)", length, tag2, tag) } if int(length2) != length { t.Errorf("length %d, length incorrect (got %d)", length, length2) } } } func TestPartialLengths(t *testing.T) { buf := bytes.NewBuffer(nil) w := new(partialLengthWriter) w.w = noOpCloser{buf} const maxChunkSize = 64 var b [maxChunkSize]byte var n uint8 for l := 1; l <= maxChunkSize; l++ { for i := 0; i < l; i++ { b[i] = n n++ } m, err := w.Write(b[:l]) if m != l { t.Errorf("short write got: %d want: %d", m, l) } if err != nil { t.Errorf("error from write: %s", err) } } w.Close() want := (maxChunkSize * (maxChunkSize + 1)) / 2 copyBuf := bytes.NewBuffer(nil) r := &partialLengthReader{buf, 0, true} m, err := io.Copy(copyBuf, r) if m != int64(want) { t.Errorf("short copy got: %d want: %d", m, want) } if err != nil { t.Errorf("error from copy: %s", err) } copyBytes := copyBuf.Bytes() for i := 0; i < want; i++ { if copyBytes[i] != uint8(i) { t.Errorf("bad pattern in copy at %d", i) break } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/userid_test.go����������������������0000644�0000153�0000161�00000004404�12321735647�027172� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "testing" ) var userIdTests = []struct { id string name, comment, email string }{ {"", "", "", ""}, {"John Smith", "John Smith", "", ""}, {"John Smith ()", "John Smith", "", ""}, {"John Smith () <>", "John Smith", "", ""}, {"(comment", "", "comment", ""}, {"(comment)", "", "comment", ""}, {"<email", "", "", "email"}, {"<email> sdfk", "", "", "email"}, {" John Smith ( Comment ) asdkflj < email > lksdfj", "John Smith", "Comment", "email"}, {" John Smith < email > lksdfj", "John Smith", "", "email"}, {"(<foo", "", "<foo", ""}, {"René Descartes (العربي)", "René Descartes", "العربي", ""}, } func TestParseUserId(t *testing.T) { for i, test := range userIdTests { name, comment, email := parseUserId(test.id) if name != test.name { t.Errorf("%d: name mismatch got:%s want:%s", i, name, test.name) } if comment != test.comment { t.Errorf("%d: comment mismatch got:%s want:%s", i, comment, test.comment) } if email != test.email { t.Errorf("%d: email mismatch got:%s want:%s", i, email, test.email) } } } var newUserIdTests = []struct { name, comment, email, id string }{ {"foo", "", "", "foo"}, {"", "bar", "", "(bar)"}, {"", "", "baz", "<baz>"}, {"foo", "bar", "", "foo (bar)"}, {"foo", "", "baz", "foo <baz>"}, {"", "bar", "baz", "(bar) <baz>"}, {"foo", "bar", "baz", "foo (bar) <baz>"}, } func TestNewUserId(t *testing.T) { for i, test := range newUserIdTests { uid := NewUserId(test.name, test.comment, test.email) if uid == nil { t.Errorf("#%d: returned nil", i) continue } if uid.Id != test.id { t.Errorf("#%d: got '%s', want '%s'", i, uid.Id, test.id) } } } var invalidNewUserIdTests = []struct { name, comment, email string }{ {"foo(", "", ""}, {"foo<", "", ""}, {"", "bar)", ""}, {"", "bar<", ""}, {"", "", "baz>"}, {"", "", "baz)"}, {"", "", "baz\x00"}, } func TestNewUserIdWithInvalidInput(t *testing.T) { for i, test := range invalidNewUserIdTests { if uid := NewUserId(test.name, test.comment, test.email); uid != nil { t.Errorf("#%d: returned non-nil value: %#v", i, uid) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/ocfb.go�����������������������������0000644�0000153�0000161�00000007173�12321735647�025557� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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. // OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9 package packet import ( "crypto/cipher" ) type ocfbEncrypter struct { b cipher.Block fre []byte outUsed int } // An OCFBResyncOption determines if the "resynchronization step" of OCFB is // performed. type OCFBResyncOption bool const ( OCFBResync OCFBResyncOption = true OCFBNoResync OCFBResyncOption = false ) // NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's // cipher feedback mode using the given cipher.Block, and an initial amount of // ciphertext. randData must be random bytes and be the same length as the // cipher.Block's block size. Resync determines if the "resynchronization step" // from RFC 4880, 13.9 step 7 is performed. Different parts of OpenPGP vary on // this point. func NewOCFBEncrypter(block cipher.Block, randData []byte, resync OCFBResyncOption) (cipher.Stream, []byte) { blockSize := block.BlockSize() if len(randData) != blockSize { return nil, nil } x := &ocfbEncrypter{ b: block, fre: make([]byte, blockSize), outUsed: 0, } prefix := make([]byte, blockSize+2) block.Encrypt(x.fre, x.fre) for i := 0; i < blockSize; i++ { prefix[i] = randData[i] ^ x.fre[i] } block.Encrypt(x.fre, prefix[:blockSize]) prefix[blockSize] = x.fre[0] ^ randData[blockSize-2] prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1] if resync { block.Encrypt(x.fre, prefix[2:]) } else { x.fre[0] = prefix[blockSize] x.fre[1] = prefix[blockSize+1] x.outUsed = 2 } return x, prefix } func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) { for i := 0; i < len(src); i++ { if x.outUsed == len(x.fre) { x.b.Encrypt(x.fre, x.fre) x.outUsed = 0 } x.fre[x.outUsed] ^= src[i] dst[i] = x.fre[x.outUsed] x.outUsed++ } } type ocfbDecrypter struct { b cipher.Block fre []byte outUsed int } // NewOCFBDecrypter returns a cipher.Stream which decrypts data with OpenPGP's // cipher feedback mode using the given cipher.Block. Prefix must be the first // blockSize + 2 bytes of the ciphertext, where blockSize is the cipher.Block's // block size. If an incorrect key is detected then nil is returned. On // successful exit, blockSize+2 bytes of decrypted data are written into // prefix. Resync determines if the "resynchronization step" from RFC 4880, // 13.9 step 7 is performed. Different parts of OpenPGP vary on this point. func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption) cipher.Stream { blockSize := block.BlockSize() if len(prefix) != blockSize+2 { return nil } x := &ocfbDecrypter{ b: block, fre: make([]byte, blockSize), outUsed: 0, } prefixCopy := make([]byte, len(prefix)) copy(prefixCopy, prefix) block.Encrypt(x.fre, x.fre) for i := 0; i < blockSize; i++ { prefixCopy[i] ^= x.fre[i] } block.Encrypt(x.fre, prefix[:blockSize]) prefixCopy[blockSize] ^= x.fre[0] prefixCopy[blockSize+1] ^= x.fre[1] if prefixCopy[blockSize-2] != prefixCopy[blockSize] || prefixCopy[blockSize-1] != prefixCopy[blockSize+1] { return nil } if resync { block.Encrypt(x.fre, prefix[2:]) } else { x.fre[0] = prefix[blockSize] x.fre[1] = prefix[blockSize+1] x.outUsed = 2 } copy(prefix, prefixCopy) return x } func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) { for i := 0; i < len(src); i++ { if x.outUsed == len(x.fre) { x.b.Encrypt(x.fre, x.fre) x.outUsed = 0 } c := src[i] dst[i] = x.fre[x.outUsed] ^ src[i] x.fre[x.outUsed] = c x.outUsed++ } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/symmetric_key_encrypted.go����������0000644�0000153�0000161�00000011102�12321735647�031572� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "code.google.com/p/go.crypto/openpgp/errors" "code.google.com/p/go.crypto/openpgp/s2k" "crypto/cipher" "io" "strconv" ) // This is the largest session key that we'll support. Since no 512-bit cipher // has even been seriously used, this is comfortably large. const maxSessionKeySizeInBytes = 64 // SymmetricKeyEncrypted represents a passphrase protected session key. See RFC // 4880, section 5.3. type SymmetricKeyEncrypted struct { CipherFunc CipherFunction Encrypted bool Key []byte // Empty unless Encrypted is false. s2k func(out, in []byte) encryptedKey []byte } const symmetricKeyEncryptedVersion = 4 func (ske *SymmetricKeyEncrypted) parse(r io.Reader) (err error) { // RFC 4880, section 5.3. var buf [2]byte _, err = readFull(r, buf[:]) if err != nil { return } if buf[0] != symmetricKeyEncryptedVersion { return errors.UnsupportedError("SymmetricKeyEncrypted version") } ske.CipherFunc = CipherFunction(buf[1]) if ske.CipherFunc.KeySize() == 0 { return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) } ske.s2k, err = s2k.Parse(r) if err != nil { return } encryptedKey := make([]byte, maxSessionKeySizeInBytes) // The session key may follow. We just have to try and read to find // out. If it exists then we limit it to maxSessionKeySizeInBytes. n, err := readFull(r, encryptedKey) if err != nil && err != io.ErrUnexpectedEOF { return } err = nil if n != 0 { if n == maxSessionKeySizeInBytes { return errors.UnsupportedError("oversized encrypted session key") } ske.encryptedKey = encryptedKey[:n] } ske.Encrypted = true return } // Decrypt attempts to decrypt an encrypted session key. If it returns nil, // ske.Key will contain the session key. func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) error { if !ske.Encrypted { return nil } key := make([]byte, ske.CipherFunc.KeySize()) ske.s2k(key, passphrase) if len(ske.encryptedKey) == 0 { ske.Key = key } else { // the IV is all zeros iv := make([]byte, ske.CipherFunc.blockSize()) c := cipher.NewCFBDecrypter(ske.CipherFunc.new(key), iv) c.XORKeyStream(ske.encryptedKey, ske.encryptedKey) ske.CipherFunc = CipherFunction(ske.encryptedKey[0]) if ske.CipherFunc.blockSize() == 0 { return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(ske.CipherFunc))) } ske.CipherFunc = CipherFunction(ske.encryptedKey[0]) ske.Key = ske.encryptedKey[1:] if len(ske.Key)%ske.CipherFunc.blockSize() != 0 { ske.Key = nil return errors.StructuralError("length of decrypted key not a multiple of block size") } } ske.Encrypted = false return nil } // SerializeSymmetricKeyEncrypted serializes a symmetric key packet to w. The // packet contains a random session key, encrypted by a key derived from the // given passphrase. The session key is returned and must be passed to // SerializeSymmetricallyEncrypted. // If config is nil, sensible defaults will be used. func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) { cipherFunc := config.Cipher() keySize := cipherFunc.KeySize() if keySize == 0 { return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) } s2kBuf := new(bytes.Buffer) keyEncryptingKey := make([]byte, keySize) // s2k.Serialize salts and stretches the passphrase, and writes the // resulting key to keyEncryptingKey and the s2k descriptor to s2kBuf. err = s2k.Serialize(s2kBuf, keyEncryptingKey, config.Random(), passphrase) if err != nil { return } s2kBytes := s2kBuf.Bytes() packetLength := 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength) if err != nil { return } var buf [2]byte buf[0] = symmetricKeyEncryptedVersion buf[1] = byte(cipherFunc) _, err = w.Write(buf[:]) if err != nil { return } _, err = w.Write(s2kBytes) if err != nil { return } sessionKey := make([]byte, keySize) _, err = io.ReadFull(config.Random(), sessionKey) if err != nil { return } iv := make([]byte, cipherFunc.blockSize()) c := cipher.NewCFBEncrypter(cipherFunc.new(keyEncryptingKey), iv) encryptedCipherAndKey := make([]byte, keySize+1) c.XORKeyStream(encryptedCipherAndKey, buf[1:]) c.XORKeyStream(encryptedCipherAndKey[1:], sessionKey) _, err = w.Write(encryptedCipherAndKey) if err != nil { return } key = sessionKey return } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/opaque.go���������������������������0000644�0000153�0000161�00000010006�12321735647�026125� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "code.google.com/p/go.crypto/openpgp/errors" "io" "io/ioutil" ) // OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is // useful for splitting and storing the original packet contents separately, // handling unsupported packet types or accessing parts of the packet not yet // implemented by this package. type OpaquePacket struct { // Packet type Tag uint8 // Reason why the packet was parsed opaquely Reason error // Binary contents of the packet data Contents []byte } func (op *OpaquePacket) parse(r io.Reader) (err error) { op.Contents, err = ioutil.ReadAll(r) return } // Serialize marshals the packet to a writer in its original form, including // the packet header. func (op *OpaquePacket) Serialize(w io.Writer) (err error) { err = serializeHeader(w, packetType(op.Tag), len(op.Contents)) if err == nil { _, err = w.Write(op.Contents) } return } // Parse attempts to parse the opaque contents into a structure supported by // this package. If the packet is not known then the result will be another // OpaquePacket. func (op *OpaquePacket) Parse() (p Packet, err error) { hdr := bytes.NewBuffer(nil) err = serializeHeader(hdr, packetType(op.Tag), len(op.Contents)) if err != nil { op.Reason = err return op, err } p, err = Read(io.MultiReader(hdr, bytes.NewBuffer(op.Contents))) if err != nil { op.Reason = err p = op } return } // OpaqueReader reads OpaquePackets from an io.Reader. type OpaqueReader struct { r io.Reader } func NewOpaqueReader(r io.Reader) *OpaqueReader { return &OpaqueReader{r: r} } // Read the next OpaquePacket. func (or *OpaqueReader) Next() (op *OpaquePacket, err error) { tag, _, contents, err := readHeader(or.r) if err != nil { return } op = &OpaquePacket{Tag: uint8(tag), Reason: err} err = op.parse(contents) if err != nil { consumeAll(contents) } return } // OpaqueSubpacket represents an unparsed OpenPGP subpacket, // as found in signature and user attribute packets. type OpaqueSubpacket struct { SubType uint8 Contents []byte } // OpaqueSubpackets extracts opaque, unparsed OpenPGP subpackets from // their byte representation. func OpaqueSubpackets(contents []byte) (result []*OpaqueSubpacket, err error) { var ( subHeaderLen int subPacket *OpaqueSubpacket ) for len(contents) > 0 { subHeaderLen, subPacket, err = nextSubpacket(contents) if err != nil { break } result = append(result, subPacket) contents = contents[subHeaderLen+len(subPacket.Contents):] } return } func nextSubpacket(contents []byte) (subHeaderLen int, subPacket *OpaqueSubpacket, err error) { // RFC 4880, section 5.2.3.1 var subLen uint32 if len(contents) < 1 { goto Truncated } subPacket = &OpaqueSubpacket{} switch { case contents[0] < 192: subHeaderLen = 2 // 1 length byte, 1 subtype byte if len(contents) < subHeaderLen { goto Truncated } subLen = uint32(contents[0]) contents = contents[1:] case contents[0] < 255: subHeaderLen = 3 // 2 length bytes, 1 subtype if len(contents) < subHeaderLen { goto Truncated } subLen = uint32(contents[0]-192)<<8 + uint32(contents[1]) + 192 contents = contents[2:] default: subHeaderLen = 6 // 5 length bytes, 1 subtype if len(contents) < subHeaderLen { goto Truncated } subLen = uint32(contents[1])<<24 | uint32(contents[2])<<16 | uint32(contents[3])<<8 | uint32(contents[4]) contents = contents[5:] } if subLen > uint32(len(contents)) { goto Truncated } subPacket.SubType = contents[0] subPacket.Contents = contents[1:subLen] return Truncated: err = errors.StructuralError("subpacket truncated") return } func (osp *OpaqueSubpacket) Serialize(w io.Writer) (err error) { buf := make([]byte, 6) n := serializeSubpacketLength(buf, len(osp.Contents)+1) buf[n] = osp.SubType if _, err = w.Write(buf[:n+1]); err != nil { return } _, err = w.Write(osp.Contents) return } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/private_key_test.go�����������������0000644�0000153�0000161�00000005625�12321735647�030227� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "testing" "time" ) var privateKeyTests = []struct { privateKeyHex string creationTime time.Time }{ { privKeyRSAHex, time.Unix(0x4cc349a8, 0), }, { privKeyElGamalHex, time.Unix(0x4df9ee1a, 0), }, } func TestPrivateKeyRead(t *testing.T) { for i, test := range privateKeyTests { packet, err := Read(readerFromHex(test.privateKeyHex)) if err != nil { t.Errorf("#%d: failed to parse: %s", i, err) continue } privKey := packet.(*PrivateKey) if !privKey.Encrypted { t.Errorf("#%d: private key isn't encrypted", i) continue } err = privKey.Decrypt([]byte("testing")) if err != nil { t.Errorf("#%d: failed to decrypt: %s", i, err) continue } if !privKey.CreationTime.Equal(test.creationTime) || privKey.Encrypted { t.Errorf("#%d: bad result, got: %#v", i, privKey) } } } // Generated with `gpg --export-secret-keys "Test Key 2"` const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" // Generated by `gpg --export-secret-keys` followed by a manual extraction of // the ElGamal subkey from the packets. const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc" �����������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/signature_test.go�������������������0000644�0000153�0000161�00000003131�12321735647�027674� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "crypto" "encoding/hex" "testing" ) func TestSignatureRead(t *testing.T) { packet, err := Read(readerFromHex(signatureDataHex)) if err != nil { t.Error(err) return } sig, ok := packet.(*Signature) if !ok || sig.SigType != SigTypeBinary || sig.PubKeyAlgo != PubKeyAlgoRSA || sig.Hash != crypto.SHA1 { t.Errorf("failed to parse, got: %#v", packet) } } func TestSignatureReserialize(t *testing.T) { packet, _ := Read(readerFromHex(signatureDataHex)) sig := packet.(*Signature) out := new(bytes.Buffer) err := sig.Serialize(out) if err != nil { t.Errorf("error reserializing: %s", err) return } expected, _ := hex.DecodeString(signatureDataHex) if !bytes.Equal(expected, out.Bytes()) { t.Errorf("output doesn't match input (got vs expected):\n%s\n%s", hex.Dump(out.Bytes()), hex.Dump(expected)) } } const signatureDataHex = "c2c05c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/one_pass_signature.go���������������0000644�0000153�0000161�00000003402�12321735647�030525� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "code.google.com/p/go.crypto/openpgp/errors" "code.google.com/p/go.crypto/openpgp/s2k" "crypto" "encoding/binary" "io" "strconv" ) // OnePassSignature represents a one-pass signature packet. See RFC 4880, // section 5.4. type OnePassSignature struct { SigType SignatureType Hash crypto.Hash PubKeyAlgo PublicKeyAlgorithm KeyId uint64 IsLast bool } const onePassSignatureVersion = 3 func (ops *OnePassSignature) parse(r io.Reader) (err error) { var buf [13]byte _, err = readFull(r, buf[:]) if err != nil { return } if buf[0] != onePassSignatureVersion { err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) } var ok bool ops.Hash, ok = s2k.HashIdToHash(buf[2]) if !ok { return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2]))) } ops.SigType = SignatureType(buf[1]) ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) ops.KeyId = binary.BigEndian.Uint64(buf[4:12]) ops.IsLast = buf[12] != 0 return } // Serialize marshals the given OnePassSignature to w. func (ops *OnePassSignature) Serialize(w io.Writer) error { var buf [13]byte buf[0] = onePassSignatureVersion buf[1] = uint8(ops.SigType) var ok bool buf[2], ok = s2k.HashToHashId(ops.Hash) if !ok { return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) } buf[3] = uint8(ops.PubKeyAlgo) binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) if ops.IsLast { buf[12] = 1 } if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { return err } _, err := w.Write(buf[:]) return err } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/compressed.go�����������������������0000644�0000153�0000161�00000006341�12321735647�027006� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "code.google.com/p/go.crypto/openpgp/errors" "compress/bzip2" "compress/flate" "compress/zlib" "io" "strconv" ) // Compressed represents a compressed OpenPGP packet. The decompressed contents // will contain more OpenPGP packets. See RFC 4880, section 5.6. type Compressed struct { Body io.Reader } const ( NoCompression = flate.NoCompression BestSpeed = flate.BestSpeed BestCompression = flate.BestCompression DefaultCompression = flate.DefaultCompression ) // CompressionConfig contains compressor configuration settings. type CompressionConfig struct { // Level is the compression level to use. It must be set to // between -1 and 9, with -1 causing the compressor to use the // default compression level, 0 causing the compressor to use // no compression and 1 to 9 representing increasing (better, // slower) compression levels. If Level is less than -1 or // more then 9, a non-nil error will be returned during // encryption. See the constants above for convenient common // settings for Level. Level int } func (c *Compressed) parse(r io.Reader) error { var buf [1]byte _, err := readFull(r, buf[:]) if err != nil { return err } switch buf[0] { case 1: c.Body = flate.NewReader(r) case 2: c.Body, err = zlib.NewReader(r) case 3: c.Body = bzip2.NewReader(r) default: err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0]))) } return err } // compressedWriterCloser represents the serialized compression stream // header and the compressor. Its Close() method ensures that both the // compressor and serialized stream header are closed. Its Write() // method writes to the compressor. type compressedWriteCloser struct { sh io.Closer // Stream Header c io.WriteCloser // Compressor } func (cwc compressedWriteCloser) Write(p []byte) (int, error) { return cwc.c.Write(p) } func (cwc compressedWriteCloser) Close() (err error) { err = cwc.c.Close() if err != nil { return err } return cwc.sh.Close() } // SerializeCompressed serializes a compressed data packet to w and // returns a WriteCloser to which the literal data packets themselves // can be written and which MUST be closed on completion. If cc is // nil, sensible defaults will be used to configure the compression // algorithm. func SerializeCompressed(w io.WriteCloser, algo CompressionAlgo, cc *CompressionConfig) (literaldata io.WriteCloser, err error) { compressed, err := serializeStreamHeader(w, packetTypeCompressed) if err != nil { return } _, err = compressed.Write([]byte{uint8(algo)}) if err != nil { return } level := DefaultCompression if cc != nil { level = cc.Level } var compressor io.WriteCloser switch algo { case CompressionZIP: compressor, err = flate.NewWriter(compressed, level) case CompressionZLIB: compressor, err = zlib.NewWriterLevel(compressed, level) default: s := strconv.Itoa(int(algo)) err = errors.UnsupportedError("Unsupported compression algorithm: " + s) } if err != nil { return } literaldata = compressedWriteCloser{compressed, compressor} return } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/packet.go���������������������������0000644�0000153�0000161�00000032061�12321735647�026107� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet implements parsing and serialization of OpenPGP packets, as // specified in RFC 4880. package packet import ( "bufio" "code.google.com/p/go.crypto/cast5" "code.google.com/p/go.crypto/openpgp/errors" "crypto/aes" "crypto/cipher" "crypto/des" "io" "math/big" ) // readFull is the same as io.ReadFull except that reading zero bytes returns // ErrUnexpectedEOF rather than EOF. func readFull(r io.Reader, buf []byte) (n int, err error) { n, err = io.ReadFull(r, buf) if err == io.EOF { err = io.ErrUnexpectedEOF } return } // readLength reads an OpenPGP length from r. See RFC 4880, section 4.2.2. func readLength(r io.Reader) (length int64, isPartial bool, err error) { var buf [4]byte _, err = readFull(r, buf[:1]) if err != nil { return } switch { case buf[0] < 192: length = int64(buf[0]) case buf[0] < 224: length = int64(buf[0]-192) << 8 _, err = readFull(r, buf[0:1]) if err != nil { return } length += int64(buf[0]) + 192 case buf[0] < 255: length = int64(1) << (buf[0] & 0x1f) isPartial = true default: _, err = readFull(r, buf[0:4]) if err != nil { return } length = int64(buf[0])<<24 | int64(buf[1])<<16 | int64(buf[2])<<8 | int64(buf[3]) } return } // partialLengthReader wraps an io.Reader and handles OpenPGP partial lengths. // The continuation lengths are parsed and removed from the stream and EOF is // returned at the end of the packet. See RFC 4880, section 4.2.2.4. type partialLengthReader struct { r io.Reader remaining int64 isPartial bool } func (r *partialLengthReader) Read(p []byte) (n int, err error) { for r.remaining == 0 { if !r.isPartial { return 0, io.EOF } r.remaining, r.isPartial, err = readLength(r.r) if err != nil { return 0, err } } toRead := int64(len(p)) if toRead > r.remaining { toRead = r.remaining } n, err = r.r.Read(p[:int(toRead)]) r.remaining -= int64(n) if n < int(toRead) && err == io.EOF { err = io.ErrUnexpectedEOF } return } // partialLengthWriter writes a stream of data using OpenPGP partial lengths. // See RFC 4880, section 4.2.2.4. type partialLengthWriter struct { w io.WriteCloser lengthByte [1]byte } func (w *partialLengthWriter) Write(p []byte) (n int, err error) { for len(p) > 0 { for power := uint(14); power < 32; power-- { l := 1 << power if len(p) >= l { w.lengthByte[0] = 224 + uint8(power) _, err = w.w.Write(w.lengthByte[:]) if err != nil { return } var m int m, err = w.w.Write(p[:l]) n += m if err != nil { return } p = p[l:] break } } } return } func (w *partialLengthWriter) Close() error { w.lengthByte[0] = 0 _, err := w.w.Write(w.lengthByte[:]) if err != nil { return err } return w.w.Close() } // A spanReader is an io.LimitReader, but it returns ErrUnexpectedEOF if the // underlying Reader returns EOF before the limit has been reached. type spanReader struct { r io.Reader n int64 } func (l *spanReader) Read(p []byte) (n int, err error) { if l.n <= 0 { return 0, io.EOF } if int64(len(p)) > l.n { p = p[0:l.n] } n, err = l.r.Read(p) l.n -= int64(n) if l.n > 0 && err == io.EOF { err = io.ErrUnexpectedEOF } return } // readHeader parses a packet header and returns an io.Reader which will return // the contents of the packet. See RFC 4880, section 4.2. func readHeader(r io.Reader) (tag packetType, length int64, contents io.Reader, err error) { var buf [4]byte _, err = io.ReadFull(r, buf[:1]) if err != nil { return } if buf[0]&0x80 == 0 { err = errors.StructuralError("tag byte does not have MSB set") return } if buf[0]&0x40 == 0 { // Old format packet tag = packetType((buf[0] & 0x3f) >> 2) lengthType := buf[0] & 3 if lengthType == 3 { length = -1 contents = r return } lengthBytes := 1 << lengthType _, err = readFull(r, buf[0:lengthBytes]) if err != nil { return } for i := 0; i < lengthBytes; i++ { length <<= 8 length |= int64(buf[i]) } contents = &spanReader{r, length} return } // New format packet tag = packetType(buf[0] & 0x3f) length, isPartial, err := readLength(r) if err != nil { return } if isPartial { contents = &partialLengthReader{ remaining: length, isPartial: true, r: r, } length = -1 } else { contents = &spanReader{r, length} } return } // serializeHeader writes an OpenPGP packet header to w. See RFC 4880, section // 4.2. func serializeHeader(w io.Writer, ptype packetType, length int) (err error) { var buf [6]byte var n int buf[0] = 0x80 | 0x40 | byte(ptype) if length < 192 { buf[1] = byte(length) n = 2 } else if length < 8384 { length -= 192 buf[1] = 192 + byte(length>>8) buf[2] = byte(length) n = 3 } else { buf[1] = 255 buf[2] = byte(length >> 24) buf[3] = byte(length >> 16) buf[4] = byte(length >> 8) buf[5] = byte(length) n = 6 } _, err = w.Write(buf[:n]) return } // serializeStreamHeader writes an OpenPGP packet header to w where the // length of the packet is unknown. It returns a io.WriteCloser which can be // used to write the contents of the packet. See RFC 4880, section 4.2. func serializeStreamHeader(w io.WriteCloser, ptype packetType) (out io.WriteCloser, err error) { var buf [1]byte buf[0] = 0x80 | 0x40 | byte(ptype) _, err = w.Write(buf[:]) if err != nil { return } out = &partialLengthWriter{w: w} return } // Packet represents an OpenPGP packet. Users are expected to try casting // instances of this interface to specific packet types. type Packet interface { parse(io.Reader) error } // consumeAll reads from the given Reader until error, returning the number of // bytes read. func consumeAll(r io.Reader) (n int64, err error) { var m int var buf [1024]byte for { m, err = r.Read(buf[:]) n += int64(m) if err == io.EOF { err = nil return } if err != nil { return } } panic("unreachable") } // packetType represents the numeric ids of the different OpenPGP packet types. See // http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-2 type packetType uint8 const ( packetTypeEncryptedKey packetType = 1 packetTypeSignature packetType = 2 packetTypeSymmetricKeyEncrypted packetType = 3 packetTypeOnePassSignature packetType = 4 packetTypePrivateKey packetType = 5 packetTypePublicKey packetType = 6 packetTypePrivateSubkey packetType = 7 packetTypeCompressed packetType = 8 packetTypeSymmetricallyEncrypted packetType = 9 packetTypeLiteralData packetType = 11 packetTypeUserId packetType = 13 packetTypePublicSubkey packetType = 14 packetTypeUserAttribute packetType = 17 packetTypeSymmetricallyEncryptedMDC packetType = 18 ) // peekVersion detects the version of a public key packet about to // be read. A bufio.Reader at the original position of the io.Reader // is returned. func peekVersion(r io.Reader) (bufr *bufio.Reader, ver byte, err error) { bufr = bufio.NewReader(r) var verBuf []byte if verBuf, err = bufr.Peek(1); err != nil { return } ver = verBuf[0] return } // Read reads a single OpenPGP packet from the given io.Reader. If there is an // error parsing a packet, the whole packet is consumed from the input. func Read(r io.Reader) (p Packet, err error) { tag, _, contents, err := readHeader(r) if err != nil { return } switch tag { case packetTypeEncryptedKey: p = new(EncryptedKey) case packetTypeSignature: var version byte // Detect signature version if contents, version, err = peekVersion(contents); err != nil { return } if version < 4 { p = new(SignatureV3) } else { p = new(Signature) } case packetTypeSymmetricKeyEncrypted: p = new(SymmetricKeyEncrypted) case packetTypeOnePassSignature: p = new(OnePassSignature) case packetTypePrivateKey, packetTypePrivateSubkey: pk := new(PrivateKey) if tag == packetTypePrivateSubkey { pk.IsSubkey = true } p = pk case packetTypePublicKey, packetTypePublicSubkey: var version byte if contents, version, err = peekVersion(contents); err != nil { return } isSubkey := tag == packetTypePublicSubkey if version < 4 { p = &PublicKeyV3{IsSubkey: isSubkey} } else { p = &PublicKey{IsSubkey: isSubkey} } case packetTypeCompressed: p = new(Compressed) case packetTypeSymmetricallyEncrypted: p = new(SymmetricallyEncrypted) case packetTypeLiteralData: p = new(LiteralData) case packetTypeUserId: p = new(UserId) case packetTypeUserAttribute: p = new(UserAttribute) case packetTypeSymmetricallyEncryptedMDC: se := new(SymmetricallyEncrypted) se.MDC = true p = se default: err = errors.UnknownPacketTypeError(tag) } if p != nil { err = p.parse(contents) } if err != nil { consumeAll(contents) } return } // SignatureType represents the different semantic meanings of an OpenPGP // signature. See RFC 4880, section 5.2.1. type SignatureType uint8 const ( SigTypeBinary SignatureType = 0 SigTypeText = 1 SigTypeGenericCert = 0x10 SigTypePersonaCert = 0x11 SigTypeCasualCert = 0x12 SigTypePositiveCert = 0x13 SigTypeSubkeyBinding = 0x18 ) // PublicKeyAlgorithm represents the different public key system specified for // OpenPGP. See // http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-12 type PublicKeyAlgorithm uint8 const ( PubKeyAlgoRSA PublicKeyAlgorithm = 1 PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 PubKeyAlgoElGamal PublicKeyAlgorithm = 16 PubKeyAlgoDSA PublicKeyAlgorithm = 17 // RFC 6637, Section 5. PubKeyAlgoECDH PublicKeyAlgorithm = 18 PubKeyAlgoECDSA PublicKeyAlgorithm = 19 ) // CanEncrypt returns true if it's possible to encrypt a message to a public // key of the given type. func (pka PublicKeyAlgorithm) CanEncrypt() bool { switch pka { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal: return true } return false } // CanSign returns true if it's possible for a public key of the given type to // sign a message. func (pka PublicKeyAlgorithm) CanSign() bool { switch pka { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: return true } return false } // CipherFunction represents the different block ciphers specified for OpenPGP. See // http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 type CipherFunction uint8 const ( Cipher3DES CipherFunction = 2 CipherCAST5 CipherFunction = 3 CipherAES128 CipherFunction = 7 CipherAES192 CipherFunction = 8 CipherAES256 CipherFunction = 9 ) // KeySize returns the key size, in bytes, of cipher. func (cipher CipherFunction) KeySize() int { switch cipher { case Cipher3DES: return 24 case CipherCAST5: return cast5.KeySize case CipherAES128: return 16 case CipherAES192: return 24 case CipherAES256: return 32 } return 0 } // blockSize returns the block size, in bytes, of cipher. func (cipher CipherFunction) blockSize() int { switch cipher { case Cipher3DES: return des.BlockSize case CipherCAST5: return 8 case CipherAES128, CipherAES192, CipherAES256: return 16 } return 0 } // new returns a fresh instance of the given cipher. func (cipher CipherFunction) new(key []byte) (block cipher.Block) { switch cipher { case Cipher3DES: block, _ = des.NewTripleDESCipher(key) case CipherCAST5: block, _ = cast5.NewCipher(key) case CipherAES128, CipherAES192, CipherAES256: block, _ = aes.NewCipher(key) } return } // readMPI reads a big integer from r. The bit length returned is the bit // length that was specified in r. This is preserved so that the integer can be // reserialized exactly. func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) { var buf [2]byte _, err = readFull(r, buf[0:]) if err != nil { return } bitLength = uint16(buf[0])<<8 | uint16(buf[1]) numBytes := (int(bitLength) + 7) / 8 mpi = make([]byte, numBytes) _, err = readFull(r, mpi) return } // mpiLength returns the length of the given *big.Int when serialized as an // MPI. func mpiLength(n *big.Int) (mpiLengthInBytes int) { mpiLengthInBytes = 2 /* MPI length */ mpiLengthInBytes += (n.BitLen() + 7) / 8 return } // writeMPI serializes a big integer to w. func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) { _, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)}) if err == nil { _, err = w.Write(mpiBytes) } return } // writeBig serializes a *big.Int to w. func writeBig(w io.Writer, i *big.Int) error { return writeMPI(w, uint16(i.BitLen()), i.Bytes()) } // CompressionAlgo Represents the different compression algorithms // supported by OpenPGP (except for BZIP2, which is not currently // supported). See Section 9.3 of RFC 4880. type CompressionAlgo uint8 const ( CompressionNone CompressionAlgo = 0 CompressionZIP CompressionAlgo = 1 CompressionZLIB CompressionAlgo = 2 ) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/userattribute.go��������������������0000644�0000153�0000161�00000004746�12321735647�027553� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "image" "image/jpeg" "io" "io/ioutil" ) const UserAttrImageSubpacket = 1 // UserAttribute is capable of storing other types of data about a user // beyond name, email and a text comment. In practice, user attributes are typically used // to store a signed thumbnail photo JPEG image of the user. // See RFC 4880, section 5.12. type UserAttribute struct { Contents []*OpaqueSubpacket } // NewUserAttributePhoto creates a user attribute packet // containing the given images. func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error) { uat = new(UserAttribute) for _, photo := range photos { var buf bytes.Buffer // RFC 4880, Section 5.12.1. data := []byte{ 0x10, 0x00, // Little-endian image header length (16 bytes) 0x01, // Image header version 1 0x01, // JPEG 0, 0, 0, 0, // 12 reserved octets, must be all zero. 0, 0, 0, 0, 0, 0, 0, 0} if _, err = buf.Write(data); err != nil { return } if err = jpeg.Encode(&buf, photo, nil); err != nil { return } uat.Contents = append(uat.Contents, &OpaqueSubpacket{ SubType: UserAttrImageSubpacket, Contents: buf.Bytes()}) } return } // NewUserAttribute creates a new user attribute packet containing the given subpackets. func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute { return &UserAttribute{Contents: contents} } func (uat *UserAttribute) parse(r io.Reader) (err error) { // RFC 4880, section 5.13 b, err := ioutil.ReadAll(r) if err != nil { return } uat.Contents, err = OpaqueSubpackets(b) return } // Serialize marshals the user attribute to w in the form of an OpenPGP packet, including // header. func (uat *UserAttribute) Serialize(w io.Writer) (err error) { var buf bytes.Buffer for _, sp := range uat.Contents { sp.Serialize(&buf) } if err = serializeHeader(w, packetTypeUserAttribute, buf.Len()); err != nil { return err } _, err = w.Write(buf.Bytes()) return } // ImageData returns zero or more byte slices, each containing // JPEG File Interchange Format (JFIF), for each photo in the // the user attribute packet. func (uat *UserAttribute) ImageData() (imageData [][]byte) { for _, sp := range uat.Contents { if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 { imageData = append(imageData, sp.Contents[16:]) } } return } ��������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/userid.go���������������������������0000644�0000153�0000161�00000006676�12321735647�026150� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "io" "io/ioutil" "strings" ) // UserId contains text that is intended to represent the name and email // address of the key holder. See RFC 4880, section 5.11. By convention, this // takes the form "Full Name (Comment) <email@example.com>" type UserId struct { Id string // By convention, this takes the form "Full Name (Comment) <email@example.com>" which is split out in the fields below. Name, Comment, Email string } func hasInvalidCharacters(s string) bool { for _, c := range s { switch c { case '(', ')', '<', '>', 0: return true } } return false } // NewUserId returns a UserId or nil if any of the arguments contain invalid // characters. The invalid characters are '\x00', '(', ')', '<' and '>' func NewUserId(name, comment, email string) *UserId { // RFC 4880 doesn't deal with the structure of userid strings; the // name, comment and email form is just a convention. However, there's // no convention about escaping the metacharacters and GPG just refuses // to create user ids where, say, the name contains a '('. We mirror // this behaviour. if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) { return nil } uid := new(UserId) uid.Name, uid.Comment, uid.Email = name, comment, email uid.Id = name if len(comment) > 0 { if len(uid.Id) > 0 { uid.Id += " " } uid.Id += "(" uid.Id += comment uid.Id += ")" } if len(email) > 0 { if len(uid.Id) > 0 { uid.Id += " " } uid.Id += "<" uid.Id += email uid.Id += ">" } return uid } func (uid *UserId) parse(r io.Reader) (err error) { // RFC 4880, section 5.11 b, err := ioutil.ReadAll(r) if err != nil { return } uid.Id = string(b) uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id) return } // Serialize marshals uid to w in the form of an OpenPGP packet, including // header. func (uid *UserId) Serialize(w io.Writer) error { err := serializeHeader(w, packetTypeUserId, len(uid.Id)) if err != nil { return err } _, err = w.Write([]byte(uid.Id)) return err } // parseUserId extracts the name, comment and email from a user id string that // is formatted as "Full Name (Comment) <email@example.com>". func parseUserId(id string) (name, comment, email string) { var n, c, e struct { start, end int } var state int for offset, rune := range id { switch state { case 0: // Entering name n.start = offset state = 1 fallthrough case 1: // In name if rune == '(' { state = 2 n.end = offset } else if rune == '<' { state = 5 n.end = offset } case 2: // Entering comment c.start = offset state = 3 fallthrough case 3: // In comment if rune == ')' { state = 4 c.end = offset } case 4: // Between comment and email if rune == '<' { state = 5 } case 5: // Entering email e.start = offset state = 6 fallthrough case 6: // In email if rune == '>' { state = 7 e.end = offset } default: // After email } } switch state { case 1: // ended in the name n.end = len(id) case 3: // ended in comment c.end = len(id) case 6: // ended in email e.end = len(id) } name = strings.TrimSpace(id[n.start:n.end]) comment = strings.TrimSpace(id[c.start:c.end]) email = strings.TrimSpace(id[e.start:e.end]) return } ������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/symmetrically_encrypted_test.go�����0000644�0000153�0000161�00000005403�12321735647�032652� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "code.google.com/p/go.crypto/openpgp/errors" "crypto/sha1" "encoding/hex" "io" "io/ioutil" "testing" ) // TestReader wraps a []byte and returns reads of a specific length. type testReader struct { data []byte stride int } func (t *testReader) Read(buf []byte) (n int, err error) { n = t.stride if n > len(t.data) { n = len(t.data) } if n > len(buf) { n = len(buf) } copy(buf, t.data) t.data = t.data[n:] if len(t.data) == 0 { err = io.EOF } return } func testMDCReader(t *testing.T) { mdcPlaintext, _ := hex.DecodeString(mdcPlaintextHex) for stride := 1; stride < len(mdcPlaintext)/2; stride++ { r := &testReader{data: mdcPlaintext, stride: stride} mdcReader := &seMDCReader{in: r, h: sha1.New()} body, err := ioutil.ReadAll(mdcReader) if err != nil { t.Errorf("stride: %d, error: %s", stride, err) continue } if !bytes.Equal(body, mdcPlaintext[:len(mdcPlaintext)-22]) { t.Errorf("stride: %d: bad contents %x", stride, body) continue } err = mdcReader.Close() if err != nil { t.Errorf("stride: %d, error on Close: %s", stride, err) } } mdcPlaintext[15] ^= 80 r := &testReader{data: mdcPlaintext, stride: 2} mdcReader := &seMDCReader{in: r, h: sha1.New()} _, err := ioutil.ReadAll(mdcReader) if err != nil { t.Errorf("corruption test, error: %s", err) return } err = mdcReader.Close() if err == nil { t.Error("corruption: no error") } else if _, ok := err.(*errors.SignatureError); !ok { t.Errorf("corruption: expected SignatureError, got: %s", err) } } const mdcPlaintextHex = "a302789c3b2d93c4e0eb9aba22283539b3203335af44a134afb800c849cb4c4de10200aff40b45d31432c80cb384299a0655966d6939dfdeed1dddf980" func TestSerialize(t *testing.T) { buf := bytes.NewBuffer(nil) c := CipherAES128 key := make([]byte, c.KeySize()) w, err := SerializeSymmetricallyEncrypted(buf, c, key, nil) if err != nil { t.Errorf("error from SerializeSymmetricallyEncrypted: %s", err) return } contents := []byte("hello world\n") w.Write(contents) w.Close() p, err := Read(buf) if err != nil { t.Errorf("error from Read: %s", err) return } se, ok := p.(*SymmetricallyEncrypted) if !ok { t.Errorf("didn't read a *SymmetricallyEncrypted") return } r, err := se.Decrypt(c, key) if err != nil { t.Errorf("error from Decrypt: %s", err) return } contentsCopy := bytes.NewBuffer(nil) _, err = io.Copy(contentsCopy, r) if err != nil { t.Errorf("error from io.Copy: %s", err) return } if !bytes.Equal(contentsCopy.Bytes(), contents) { t.Errorf("contents not equal got: %x want: %x", contentsCopy.Bytes(), contents) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/signature_v3.go���������������������0000644�0000153�0000161�00000007560�12321735647�027257� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "crypto" "encoding/binary" "fmt" "io" "strconv" "time" "code.google.com/p/go.crypto/openpgp/errors" "code.google.com/p/go.crypto/openpgp/s2k" ) // SignatureV3 represents older version 3 signatures. These signatures are less secure // than version 4 and should not be used to create new signatures. They are included // here for backwards compatibility to read and validate with older key material. // See RFC 4880, section 5.2.2. type SignatureV3 struct { SigType SignatureType CreationTime time.Time IssuerKeyId uint64 PubKeyAlgo PublicKeyAlgorithm Hash crypto.Hash HashTag [2]byte RSASignature parsedMPI DSASigR, DSASigS parsedMPI } func (sig *SignatureV3) parse(r io.Reader) (err error) { // RFC 4880, section 5.2.2 var buf [8]byte if _, err = readFull(r, buf[:1]); err != nil { return } if buf[0] < 2 || buf[0] > 3 { err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) return } if _, err = readFull(r, buf[:1]); err != nil { return } if buf[0] != 5 { err = errors.UnsupportedError( "invalid hashed material length " + strconv.Itoa(int(buf[0]))) return } // Read hashed material: signature type + creation time if _, err = readFull(r, buf[:5]); err != nil { return } sig.SigType = SignatureType(buf[0]) t := binary.BigEndian.Uint32(buf[1:5]) sig.CreationTime = time.Unix(int64(t), 0) // Eight-octet Key ID of signer. if _, err = readFull(r, buf[:8]); err != nil { return } sig.IssuerKeyId = binary.BigEndian.Uint64(buf[:]) // Public-key and hash algorithm if _, err = readFull(r, buf[:2]); err != nil { return } sig.PubKeyAlgo = PublicKeyAlgorithm(buf[0]) switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: default: err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) return } var ok bool if sig.Hash, ok = s2k.HashIdToHash(buf[1]); !ok { return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) } // Two-octet field holding left 16 bits of signed hash value. if _, err = readFull(r, sig.HashTag[:2]); err != nil { return } switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) case PubKeyAlgoDSA: if sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r); err != nil { return } sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) default: panic("unreachable") } return } // Serialize marshals sig to w. Sign, SignUserId or SignKey must have been // called first. func (sig *SignatureV3) Serialize(w io.Writer) (err error) { buf := make([]byte, 8) // Write the sig type and creation time buf[0] = byte(sig.SigType) binary.BigEndian.PutUint32(buf[1:5], uint32(sig.CreationTime.Unix())) if _, err = w.Write(buf[:5]); err != nil { return } // Write the issuer long key ID binary.BigEndian.PutUint64(buf[:8], sig.IssuerKeyId) if _, err = w.Write(buf[:8]); err != nil { return } // Write public key algorithm, hash ID, and hash value buf[0] = byte(sig.PubKeyAlgo) hashId, ok := s2k.HashToHashId(sig.Hash) if !ok { return errors.UnsupportedError(fmt.Sprintf("hash function %v", sig.Hash)) } buf[1] = hashId copy(buf[2:4], sig.HashTag[:]) if _, err = w.Write(buf[:4]); err != nil { return } if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil { return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") } switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: err = writeMPIs(w, sig.RSASignature) case PubKeyAlgoDSA: err = writeMPIs(w, sig.DSASigR, sig.DSASigS) default: panic("impossible") } return } ������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/encrypted_key_test.go���������������0000644�0000153�0000161�00000006754�12321735647�030556� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "crypto/rsa" "fmt" "math/big" "testing" ) func bigFromBase10(s string) *big.Int { b, ok := new(big.Int).SetString(s, 10) if !ok { panic("bigFromBase10 failed") } return b } var encryptedKeyPub = rsa.PublicKey{ E: 65537, N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"), } var encryptedKeyRSAPriv = &rsa.PrivateKey{ PublicKey: encryptedKeyPub, D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"), } var encryptedKeyPriv = &PrivateKey{ PublicKey: PublicKey{ PubKeyAlgo: PubKeyAlgoRSA, }, PrivateKey: encryptedKeyRSAPriv, } func TestDecryptingEncryptedKey(t *testing.T) { const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8" const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b" p, err := Read(readerFromHex(encryptedKeyHex)) if err != nil { t.Errorf("error from Read: %s", err) return } ek, ok := p.(*EncryptedKey) if !ok { t.Errorf("didn't parse an EncryptedKey, got %#v", p) return } if ek.KeyId != 0x2a67d68660df41c7 || ek.Algo != PubKeyAlgoRSA { t.Errorf("unexpected EncryptedKey contents: %#v", ek) return } err = ek.Decrypt(encryptedKeyPriv, nil) if err != nil { t.Errorf("error from Decrypt: %s", err) return } if ek.CipherFunc != CipherAES256 { t.Errorf("unexpected EncryptedKey contents: %#v", ek) return } keyHex := fmt.Sprintf("%x", ek.Key) if keyHex != expectedKeyHex { t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) } } func TestEncryptingEncryptedKey(t *testing.T) { key := []byte{1, 2, 3, 4} const expectedKeyHex = "01020304" const keyId = 42 pub := &PublicKey{ PublicKey: &encryptedKeyPub, KeyId: keyId, PubKeyAlgo: PubKeyAlgoRSAEncryptOnly, } buf := new(bytes.Buffer) err := SerializeEncryptedKey(buf, pub, CipherAES128, key, nil) if err != nil { t.Errorf("error writing encrypted key packet: %s", err) } p, err := Read(buf) if err != nil { t.Errorf("error from Read: %s", err) return } ek, ok := p.(*EncryptedKey) if !ok { t.Errorf("didn't parse an EncryptedKey, got %#v", p) return } if ek.KeyId != keyId || ek.Algo != PubKeyAlgoRSAEncryptOnly { t.Errorf("unexpected EncryptedKey contents: %#v", ek) return } err = ek.Decrypt(encryptedKeyPriv, nil) if err != nil { t.Errorf("error from Decrypt: %s", err) return } if ek.CipherFunc != CipherAES128 { t.Errorf("unexpected EncryptedKey contents: %#v", ek) return } keyHex := fmt.Sprintf("%x", ek.Key) if keyHex != expectedKeyHex { t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) } } ��������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/public_key.go�����������������������0000644�0000153�0000161�00000044445�12321735647�026777� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "code.google.com/p/go.crypto/openpgp/elgamal" "code.google.com/p/go.crypto/openpgp/errors" "crypto" "crypto/dsa" "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" "crypto/sha1" _ "crypto/sha256" _ "crypto/sha512" "encoding/binary" "fmt" "hash" "io" "math/big" "strconv" "time" ) var ( // NIST curve P-256 oidCurveP256 []byte = []byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07} // NIST curve P-384 oidCurveP384 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x22} // NIST curve P-521 oidCurveP521 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x23} ) const maxOIDLength = 8 // ecdsaKey stores the algorithm-specific fields for ECDSA keys. // as defined in RFC 6637, Section 9. type ecdsaKey struct { // oid contains the OID byte sequence identifying the elliptic curve used oid []byte // p contains the elliptic curve point that represents the public key p parsedMPI } // parseOID reads the OID for the curve as defined in RFC 6637, Section 9. func parseOID(r io.Reader) (oid []byte, err error) { buf := make([]byte, maxOIDLength) if _, err = readFull(r, buf[:1]); err != nil { return } oidLen := buf[0] if int(oidLen) > len(buf) { err = errors.UnsupportedError("invalid oid length: " + strconv.Itoa(int(oidLen))) return } oid = buf[:oidLen] _, err = readFull(r, oid) return } func (f *ecdsaKey) parse(r io.Reader) (err error) { if f.oid, err = parseOID(r); err != nil { return err } f.p.bytes, f.p.bitLength, err = readMPI(r) return } func (f *ecdsaKey) serialize(w io.Writer) (err error) { buf := make([]byte, maxOIDLength+1) buf[0] = byte(len(f.oid)) copy(buf[1:], f.oid) if _, err = w.Write(buf[:len(f.oid)+1]); err != nil { return } return writeMPIs(w, f.p) } func (f *ecdsaKey) newECDSA() (*ecdsa.PublicKey, error) { var c elliptic.Curve if bytes.Equal(f.oid, oidCurveP256) { c = elliptic.P256() } else if bytes.Equal(f.oid, oidCurveP384) { c = elliptic.P384() } else if bytes.Equal(f.oid, oidCurveP521) { c = elliptic.P521() } else { return nil, errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", f.oid)) } x, y := elliptic.Unmarshal(c, f.p.bytes) if x == nil { return nil, errors.UnsupportedError("failed to parse EC point") } return &ecdsa.PublicKey{Curve: c, X: x, Y: y}, nil } func (f *ecdsaKey) byteLen() int { return 1 + len(f.oid) + 2 + len(f.p.bytes) } type kdfHashFunction byte type kdfAlgorithm byte // ecdhKdf stores key derivation function parameters // used for ECDH encryption. See RFC 6637, Section 9. type ecdhKdf struct { KdfHash kdfHashFunction KdfAlgo kdfAlgorithm } func (f *ecdhKdf) parse(r io.Reader) (err error) { buf := make([]byte, 1) if _, err = readFull(r, buf); err != nil { return } kdfLen := int(buf[0]) if kdfLen < 3 { return errors.UnsupportedError("Unsupported ECDH KDF length: " + strconv.Itoa(kdfLen)) } buf = make([]byte, kdfLen) if _, err = readFull(r, buf); err != nil { return } reserved := int(buf[0]) f.KdfHash = kdfHashFunction(buf[1]) f.KdfAlgo = kdfAlgorithm(buf[2]) if reserved != 0x01 { return errors.UnsupportedError("Unsupported KDF reserved field: " + strconv.Itoa(reserved)) } return } func (f *ecdhKdf) serialize(w io.Writer) (err error) { buf := make([]byte, 4) // See RFC 6637, Section 9, Algorithm-Specific Fields for ECDH keys. buf[0] = byte(0x03) // Length of the following fields buf[1] = byte(0x01) // Reserved for future extensions, must be 1 for now buf[2] = byte(f.KdfHash) buf[3] = byte(f.KdfAlgo) _, err = w.Write(buf[:]) return } func (f *ecdhKdf) byteLen() int { return 4 } // PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2. type PublicKey struct { CreationTime time.Time PubKeyAlgo PublicKeyAlgorithm PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey or *ecdsa.PublicKey Fingerprint [20]byte KeyId uint64 IsSubkey bool n, e, p, q, g, y parsedMPI // RFC 6637 fields ec *ecdsaKey ecdh *ecdhKdf } // signingKey provides a convenient abstraction over signature verification // for v3 and v4 public keys. type signingKey interface { SerializeSignaturePrefix(io.Writer) serializeWithoutHeaders(io.Writer) error } func fromBig(n *big.Int) parsedMPI { return parsedMPI{ bytes: n.Bytes(), bitLength: uint16(n.BitLen()), } } // NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey. func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey { pk := &PublicKey{ CreationTime: creationTime, PubKeyAlgo: PubKeyAlgoRSA, PublicKey: pub, n: fromBig(pub.N), e: fromBig(big.NewInt(int64(pub.E))), } pk.setFingerPrintAndKeyId() return pk } // NewDSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey. func NewDSAPublicKey(creationTime time.Time, pub *dsa.PublicKey) *PublicKey { pk := &PublicKey{ CreationTime: creationTime, PubKeyAlgo: PubKeyAlgoDSA, PublicKey: pub, p: fromBig(pub.P), q: fromBig(pub.Q), g: fromBig(pub.G), y: fromBig(pub.Y), } pk.setFingerPrintAndKeyId() return pk } func (pk *PublicKey) parse(r io.Reader) (err error) { // RFC 4880, section 5.5.2 var buf [6]byte _, err = readFull(r, buf[:]) if err != nil { return } if buf[0] != 4 { return errors.UnsupportedError("public key version") } pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0) pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5]) switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: err = pk.parseRSA(r) case PubKeyAlgoDSA: err = pk.parseDSA(r) case PubKeyAlgoElGamal: err = pk.parseElGamal(r) case PubKeyAlgoECDSA: pk.ec = new(ecdsaKey) if err = pk.ec.parse(r); err != nil { return err } pk.PublicKey, err = pk.ec.newECDSA() case PubKeyAlgoECDH: pk.ec = new(ecdsaKey) if err = pk.ec.parse(r); err != nil { return } pk.ecdh = new(ecdhKdf) if err = pk.ecdh.parse(r); err != nil { return } // The ECDH key is stored in an ecdsa.PublicKey for convenience. pk.PublicKey, err = pk.ec.newECDSA() default: err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) } if err != nil { return } pk.setFingerPrintAndKeyId() return } func (pk *PublicKey) setFingerPrintAndKeyId() { // RFC 4880, section 12.2 fingerPrint := sha1.New() pk.SerializeSignaturePrefix(fingerPrint) pk.serializeWithoutHeaders(fingerPrint) copy(pk.Fingerprint[:], fingerPrint.Sum(nil)) pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20]) } // parseRSA parses RSA public key material from the given Reader. See RFC 4880, // section 5.5.2. func (pk *PublicKey) parseRSA(r io.Reader) (err error) { pk.n.bytes, pk.n.bitLength, err = readMPI(r) if err != nil { return } pk.e.bytes, pk.e.bitLength, err = readMPI(r) if err != nil { return } if len(pk.e.bytes) > 3 { err = errors.UnsupportedError("large public exponent") return } rsa := &rsa.PublicKey{ N: new(big.Int).SetBytes(pk.n.bytes), E: 0, } for i := 0; i < len(pk.e.bytes); i++ { rsa.E <<= 8 rsa.E |= int(pk.e.bytes[i]) } pk.PublicKey = rsa return } // parseDSA parses DSA public key material from the given Reader. See RFC 4880, // section 5.5.2. func (pk *PublicKey) parseDSA(r io.Reader) (err error) { pk.p.bytes, pk.p.bitLength, err = readMPI(r) if err != nil { return } pk.q.bytes, pk.q.bitLength, err = readMPI(r) if err != nil { return } pk.g.bytes, pk.g.bitLength, err = readMPI(r) if err != nil { return } pk.y.bytes, pk.y.bitLength, err = readMPI(r) if err != nil { return } dsa := new(dsa.PublicKey) dsa.P = new(big.Int).SetBytes(pk.p.bytes) dsa.Q = new(big.Int).SetBytes(pk.q.bytes) dsa.G = new(big.Int).SetBytes(pk.g.bytes) dsa.Y = new(big.Int).SetBytes(pk.y.bytes) pk.PublicKey = dsa return } // parseElGamal parses ElGamal public key material from the given Reader. See // RFC 4880, section 5.5.2. func (pk *PublicKey) parseElGamal(r io.Reader) (err error) { pk.p.bytes, pk.p.bitLength, err = readMPI(r) if err != nil { return } pk.g.bytes, pk.g.bitLength, err = readMPI(r) if err != nil { return } pk.y.bytes, pk.y.bitLength, err = readMPI(r) if err != nil { return } elgamal := new(elgamal.PublicKey) elgamal.P = new(big.Int).SetBytes(pk.p.bytes) elgamal.G = new(big.Int).SetBytes(pk.g.bytes) elgamal.Y = new(big.Int).SetBytes(pk.y.bytes) pk.PublicKey = elgamal return } // SerializeSignaturePrefix writes the prefix for this public key to the given Writer. // The prefix is used when calculating a signature over this public key. See // RFC 4880, section 5.2.4. func (pk *PublicKey) SerializeSignaturePrefix(h io.Writer) { var pLength uint16 switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: pLength += 2 + uint16(len(pk.n.bytes)) pLength += 2 + uint16(len(pk.e.bytes)) case PubKeyAlgoDSA: pLength += 2 + uint16(len(pk.p.bytes)) pLength += 2 + uint16(len(pk.q.bytes)) pLength += 2 + uint16(len(pk.g.bytes)) pLength += 2 + uint16(len(pk.y.bytes)) case PubKeyAlgoElGamal: pLength += 2 + uint16(len(pk.p.bytes)) pLength += 2 + uint16(len(pk.g.bytes)) pLength += 2 + uint16(len(pk.y.bytes)) case PubKeyAlgoECDSA: pLength += uint16(pk.ec.byteLen()) case PubKeyAlgoECDH: pLength += uint16(pk.ec.byteLen()) pLength += uint16(pk.ecdh.byteLen()) default: panic("unknown public key algorithm") } pLength += 6 h.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}) return } func (pk *PublicKey) Serialize(w io.Writer) (err error) { length := 6 // 6 byte header switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: length += 2 + len(pk.n.bytes) length += 2 + len(pk.e.bytes) case PubKeyAlgoDSA: length += 2 + len(pk.p.bytes) length += 2 + len(pk.q.bytes) length += 2 + len(pk.g.bytes) length += 2 + len(pk.y.bytes) case PubKeyAlgoElGamal: length += 2 + len(pk.p.bytes) length += 2 + len(pk.g.bytes) length += 2 + len(pk.y.bytes) case PubKeyAlgoECDSA: length += pk.ec.byteLen() case PubKeyAlgoECDH: length += pk.ec.byteLen() length += pk.ecdh.byteLen() default: panic("unknown public key algorithm") } packetType := packetTypePublicKey if pk.IsSubkey { packetType = packetTypePublicSubkey } err = serializeHeader(w, packetType, length) if err != nil { return } return pk.serializeWithoutHeaders(w) } // serializeWithoutHeaders marshals the PublicKey to w in the form of an // OpenPGP public key packet, not including the packet header. func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { var buf [6]byte buf[0] = 4 t := uint32(pk.CreationTime.Unix()) buf[1] = byte(t >> 24) buf[2] = byte(t >> 16) buf[3] = byte(t >> 8) buf[4] = byte(t) buf[5] = byte(pk.PubKeyAlgo) _, err = w.Write(buf[:]) if err != nil { return } switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: return writeMPIs(w, pk.n, pk.e) case PubKeyAlgoDSA: return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) case PubKeyAlgoElGamal: return writeMPIs(w, pk.p, pk.g, pk.y) case PubKeyAlgoECDSA: return pk.ec.serialize(w) case PubKeyAlgoECDH: if err = pk.ec.serialize(w); err != nil { return } return pk.ecdh.serialize(w) } return errors.InvalidArgumentError("bad public-key algorithm") } // CanSign returns true iff this public key can generate signatures func (pk *PublicKey) CanSign() bool { return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal } // VerifySignature returns nil iff sig is a valid signature, made by this // public key, of the data hashed into signed. signed is mutated by this call. func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) { if !pk.CanSign() { return errors.InvalidArgumentError("public key cannot generate signatures") } signed.Write(sig.HashSuffix) hashBytes := signed.Sum(nil) if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { return errors.SignatureError("hash tag doesn't match") } if pk.PubKeyAlgo != sig.PubKeyAlgo { return errors.InvalidArgumentError("public key and signature use different algorithms") } switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes) if err != nil { return errors.SignatureError("RSA verification failure") } return nil case PubKeyAlgoDSA: dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey) // Need to truncate hashBytes to match FIPS 186-3 section 4.6. subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8 if len(hashBytes) > subgroupSize { hashBytes = hashBytes[:subgroupSize] } if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { return errors.SignatureError("DSA verification failure") } return nil case PubKeyAlgoECDSA: ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey) if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.bytes), new(big.Int).SetBytes(sig.ECDSASigS.bytes)) { return errors.SignatureError("ECDSA verification failure") } return nil default: return errors.SignatureError("Unsupported public key algorithm used in signature") } panic("unreachable") } // VerifySignatureV3 returns nil iff sig is a valid signature, made by this // public key, of the data hashed into signed. signed is mutated by this call. func (pk *PublicKey) VerifySignatureV3(signed hash.Hash, sig *SignatureV3) (err error) { if !pk.CanSign() { return errors.InvalidArgumentError("public key cannot generate signatures") } suffix := make([]byte, 5) suffix[0] = byte(sig.SigType) binary.BigEndian.PutUint32(suffix[1:], uint32(sig.CreationTime.Unix())) signed.Write(suffix) hashBytes := signed.Sum(nil) if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { return errors.SignatureError("hash tag doesn't match") } if pk.PubKeyAlgo != sig.PubKeyAlgo { return errors.InvalidArgumentError("public key and signature use different algorithms") } switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: rsaPublicKey := pk.PublicKey.(*rsa.PublicKey) if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil { return errors.SignatureError("RSA verification failure") } return case PubKeyAlgoDSA: dsaPublicKey := pk.PublicKey.(*dsa.PublicKey) // Need to truncate hashBytes to match FIPS 186-3 section 4.6. subgroupSize := (dsaPublicKey.Q.BitLen() + 7) / 8 if len(hashBytes) > subgroupSize { hashBytes = hashBytes[:subgroupSize] } if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { return errors.SignatureError("DSA verification failure") } return nil default: panic("shouldn't happen") } panic("unreachable") } // keySignatureHash returns a Hash of the message that needs to be signed for // pk to assert a subkey relationship to signed. func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) { if !hashFunc.Available() { return nil, errors.UnsupportedError("hash function") } h = hashFunc.New() // RFC 4880, section 5.2.4 pk.SerializeSignaturePrefix(h) pk.serializeWithoutHeaders(h) signed.SerializeSignaturePrefix(h) signed.serializeWithoutHeaders(h) return } // VerifyKeySignature returns nil iff sig is a valid signature, made by this // public key, of signed. func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) (err error) { h, err := keySignatureHash(pk, signed, sig.Hash) if err != nil { return err } return pk.VerifySignature(h, sig) } // userIdSignatureHash returns a Hash of the message that needs to be signed // to assert that pk is a valid key for id. func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) { if !hashFunc.Available() { return nil, errors.UnsupportedError("hash function") } h = hashFunc.New() // RFC 4880, section 5.2.4 pk.SerializeSignaturePrefix(h) pk.serializeWithoutHeaders(h) var buf [5]byte buf[0] = 0xb4 buf[1] = byte(len(id) >> 24) buf[2] = byte(len(id) >> 16) buf[3] = byte(len(id) >> 8) buf[4] = byte(len(id)) h.Write(buf[:]) h.Write([]byte(id)) return } // VerifyUserIdSignature returns nil iff sig is a valid signature, made by this // public key, of id. func (pk *PublicKey) VerifyUserIdSignature(id string, sig *Signature) (err error) { h, err := userIdSignatureHash(id, pk, sig.Hash) if err != nil { return err } return pk.VerifySignature(h, sig) } // VerifyUserIdSignatureV3 returns nil iff sig is a valid signature, made by this // public key, of id. func (pk *PublicKey) VerifyUserIdSignatureV3(id string, sig *SignatureV3) (err error) { h, err := userIdSignatureV3Hash(id, pk, sig.Hash) if err != nil { return err } return pk.VerifySignatureV3(h, sig) } // KeyIdString returns the public key's fingerprint in capital hex // (e.g. "6C7EE1B8621CC013"). func (pk *PublicKey) KeyIdString() string { return fmt.Sprintf("%X", pk.Fingerprint[12:20]) } // KeyIdShortString returns the short form of public key's fingerprint // in capital hex, as shown by gpg --list-keys (e.g. "621CC013"). func (pk *PublicKey) KeyIdShortString() string { return fmt.Sprintf("%X", pk.Fingerprint[16:20]) } // A parsedMPI is used to store the contents of a big integer, along with the // bit length that was specified in the original input. This allows the MPI to // be reserialized exactly. type parsedMPI struct { bytes []byte bitLength uint16 } // writeMPIs is a utility function for serializing several big integers to the // given Writer. func writeMPIs(w io.Writer, mpis ...parsedMPI) (err error) { for _, mpi := range mpis { err = writeMPI(w, mpi.bitLength, mpi.bytes) if err != nil { return } } return } // BitLength returns the bit length for the given public key. func (pk *PublicKey) BitLength() (bitLength uint16, err error) { switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly: bitLength = pk.n.bitLength case PubKeyAlgoDSA: bitLength = pk.p.bitLength case PubKeyAlgoElGamal: bitLength = pk.p.bitLength default: err = errors.InvalidArgumentError("bad public-key algorithm") } return } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/ocfb_test.go������������������������0000644�0000153�0000161�00000002365�12321735647�026614� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 packet import ( "bytes" "crypto/aes" "crypto/rand" "testing" ) var commonKey128 = []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} func testOCFB(t *testing.T, resync OCFBResyncOption) { block, err := aes.NewCipher(commonKey128) if err != nil { t.Error(err) return } plaintext := []byte("this is the plaintext, which is long enough to span several blocks.") randData := make([]byte, block.BlockSize()) rand.Reader.Read(randData) ocfb, prefix := NewOCFBEncrypter(block, randData, resync) ciphertext := make([]byte, len(plaintext)) ocfb.XORKeyStream(ciphertext, plaintext) ocfbdec := NewOCFBDecrypter(block, prefix, resync) if ocfbdec == nil { t.Errorf("NewOCFBDecrypter failed (resync: %t)", resync) return } plaintextCopy := make([]byte, len(plaintext)) ocfbdec.XORKeyStream(plaintextCopy, ciphertext) if !bytes.Equal(plaintextCopy, plaintext) { t.Errorf("got: %x, want: %x (resync: %t)", plaintextCopy, plaintext, resync) } } func TestOCFB(t *testing.T) { testOCFB(t, OCFBNoResync) testOCFB(t, OCFBResync) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/symmetric_key_encrypted_test.go�����0000644�0000153�0000161�00000004640�12321735647�032642� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "encoding/hex" "io" "io/ioutil" "testing" ) func TestSymmetricKeyEncrypted(t *testing.T) { buf := readerFromHex(symmetricallyEncryptedHex) packet, err := Read(buf) if err != nil { t.Errorf("failed to read SymmetricKeyEncrypted: %s", err) return } ske, ok := packet.(*SymmetricKeyEncrypted) if !ok { t.Error("didn't find SymmetricKeyEncrypted packet") return } err = ske.Decrypt([]byte("password")) if err != nil { t.Error(err) return } packet, err = Read(buf) if err != nil { t.Errorf("failed to read SymmetricallyEncrypted: %s", err) return } se, ok := packet.(*SymmetricallyEncrypted) if !ok { t.Error("didn't find SymmetricallyEncrypted packet") return } r, err := se.Decrypt(ske.CipherFunc, ske.Key) if err != nil { t.Error(err) return } contents, err := ioutil.ReadAll(r) if err != nil && err != io.EOF { t.Error(err) return } expectedContents, _ := hex.DecodeString(symmetricallyEncryptedContentsHex) if !bytes.Equal(expectedContents, contents) { t.Errorf("bad contents got:%x want:%x", contents, expectedContents) } } const symmetricallyEncryptedHex = "8c0d04030302371a0b38d884f02060c91cf97c9973b8e58e028e9501708ccfe618fb92afef7fa2d80ddadd93cf" const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a" func TestSerializeSymmetricKeyEncrypted(t *testing.T) { buf := bytes.NewBuffer(nil) passphrase := []byte("testing") config := &Config{ DefaultCipher: CipherAES128, } key, err := SerializeSymmetricKeyEncrypted(buf, passphrase, config) if err != nil { t.Errorf("failed to serialize: %s", err) return } p, err := Read(buf) if err != nil { t.Errorf("failed to reparse: %s", err) return } ske, ok := p.(*SymmetricKeyEncrypted) if !ok { t.Errorf("parsed a different packet type: %#v", p) return } if !ske.Encrypted { t.Errorf("SKE not encrypted but should be") } if ske.CipherFunc != config.DefaultCipher { t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, config.DefaultCipher) } err = ske.Decrypt(passphrase) if err != nil { t.Errorf("failed to decrypt reparsed SKE: %s", err) return } if !bytes.Equal(key, ske.Key) { t.Errorf("keys don't match after Decrpyt: %x (original) vs %x (parsed)", key, ske.Key) } } ������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/private_key.go����������������������0000644�0000153�0000161�00000015376�12321735647�027174� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "bytes" "code.google.com/p/go.crypto/openpgp/elgamal" "code.google.com/p/go.crypto/openpgp/errors" "code.google.com/p/go.crypto/openpgp/s2k" "crypto/cipher" "crypto/dsa" "crypto/rsa" "crypto/sha1" "io" "io/ioutil" "math/big" "strconv" "time" ) // PrivateKey represents a possibly encrypted private key. See RFC 4880, // section 5.5.3. type PrivateKey struct { PublicKey Encrypted bool // if true then the private key is unavailable until Decrypt has been called. encryptedData []byte cipher CipherFunction s2k func(out, in []byte) PrivateKey interface{} // An *rsa.PrivateKey or *dsa.PrivateKey. sha1Checksum bool iv []byte } func NewRSAPrivateKey(currentTime time.Time, priv *rsa.PrivateKey) *PrivateKey { pk := new(PrivateKey) pk.PublicKey = *NewRSAPublicKey(currentTime, &priv.PublicKey) pk.PrivateKey = priv return pk } func NewDSAPrivateKey(currentTime time.Time, priv *dsa.PrivateKey) *PrivateKey { pk := new(PrivateKey) pk.PublicKey = *NewDSAPublicKey(currentTime, &priv.PublicKey) pk.PrivateKey = priv return pk } func (pk *PrivateKey) parse(r io.Reader) (err error) { err = (&pk.PublicKey).parse(r) if err != nil { return } var buf [1]byte _, err = readFull(r, buf[:]) if err != nil { return } s2kType := buf[0] switch s2kType { case 0: pk.s2k = nil pk.Encrypted = false case 254, 255: _, err = readFull(r, buf[:]) if err != nil { return } pk.cipher = CipherFunction(buf[0]) pk.Encrypted = true pk.s2k, err = s2k.Parse(r) if err != nil { return } if s2kType == 254 { pk.sha1Checksum = true } default: return errors.UnsupportedError("deprecated s2k function in private key") } if pk.Encrypted { blockSize := pk.cipher.blockSize() if blockSize == 0 { return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher))) } pk.iv = make([]byte, blockSize) _, err = readFull(r, pk.iv) if err != nil { return } } pk.encryptedData, err = ioutil.ReadAll(r) if err != nil { return } if !pk.Encrypted { return pk.parsePrivateKey(pk.encryptedData) } return } func mod64kHash(d []byte) uint16 { var h uint16 for _, b := range d { h += uint16(b) } return h } func (pk *PrivateKey) Serialize(w io.Writer) (err error) { // TODO(agl): support encrypted private keys buf := bytes.NewBuffer(nil) err = pk.PublicKey.serializeWithoutHeaders(buf) if err != nil { return } buf.WriteByte(0 /* no encryption */) privateKeyBuf := bytes.NewBuffer(nil) switch priv := pk.PrivateKey.(type) { case *rsa.PrivateKey: err = serializeRSAPrivateKey(privateKeyBuf, priv) case *dsa.PrivateKey: err = serializeDSAPrivateKey(privateKeyBuf, priv) default: err = errors.InvalidArgumentError("unknown private key type") } if err != nil { return } ptype := packetTypePrivateKey contents := buf.Bytes() privateKeyBytes := privateKeyBuf.Bytes() if pk.IsSubkey { ptype = packetTypePrivateSubkey } err = serializeHeader(w, ptype, len(contents)+len(privateKeyBytes)+2) if err != nil { return } _, err = w.Write(contents) if err != nil { return } _, err = w.Write(privateKeyBytes) if err != nil { return } checksum := mod64kHash(privateKeyBytes) var checksumBytes [2]byte checksumBytes[0] = byte(checksum >> 8) checksumBytes[1] = byte(checksum) _, err = w.Write(checksumBytes[:]) return } func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error { err := writeBig(w, priv.D) if err != nil { return err } err = writeBig(w, priv.Primes[1]) if err != nil { return err } err = writeBig(w, priv.Primes[0]) if err != nil { return err } return writeBig(w, priv.Precomputed.Qinv) } func serializeDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error { return writeBig(w, priv.X) } // Decrypt decrypts an encrypted private key using a passphrase. func (pk *PrivateKey) Decrypt(passphrase []byte) error { if !pk.Encrypted { return nil } key := make([]byte, pk.cipher.KeySize()) pk.s2k(key, passphrase) block := pk.cipher.new(key) cfb := cipher.NewCFBDecrypter(block, pk.iv) data := pk.encryptedData cfb.XORKeyStream(data, data) if pk.sha1Checksum { if len(data) < sha1.Size { return errors.StructuralError("truncated private key data") } h := sha1.New() h.Write(data[:len(data)-sha1.Size]) sum := h.Sum(nil) if !bytes.Equal(sum, data[len(data)-sha1.Size:]) { return errors.StructuralError("private key checksum failure") } data = data[:len(data)-sha1.Size] } else { if len(data) < 2 { return errors.StructuralError("truncated private key data") } var sum uint16 for i := 0; i < len(data)-2; i++ { sum += uint16(data[i]) } if data[len(data)-2] != uint8(sum>>8) || data[len(data)-1] != uint8(sum) { return errors.StructuralError("private key checksum failure") } data = data[:len(data)-2] } return pk.parsePrivateKey(data) } func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) { switch pk.PublicKey.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly: return pk.parseRSAPrivateKey(data) case PubKeyAlgoDSA: return pk.parseDSAPrivateKey(data) case PubKeyAlgoElGamal: return pk.parseElGamalPrivateKey(data) } panic("impossible") } func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) { rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey) rsaPriv := new(rsa.PrivateKey) rsaPriv.PublicKey = *rsaPub buf := bytes.NewBuffer(data) d, _, err := readMPI(buf) if err != nil { return } p, _, err := readMPI(buf) if err != nil { return } q, _, err := readMPI(buf) if err != nil { return } rsaPriv.D = new(big.Int).SetBytes(d) rsaPriv.Primes = make([]*big.Int, 2) rsaPriv.Primes[0] = new(big.Int).SetBytes(p) rsaPriv.Primes[1] = new(big.Int).SetBytes(q) rsaPriv.Precompute() pk.PrivateKey = rsaPriv pk.Encrypted = false pk.encryptedData = nil return nil } func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err error) { dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey) dsaPriv := new(dsa.PrivateKey) dsaPriv.PublicKey = *dsaPub buf := bytes.NewBuffer(data) x, _, err := readMPI(buf) if err != nil { return } dsaPriv.X = new(big.Int).SetBytes(x) pk.PrivateKey = dsaPriv pk.Encrypted = false pk.encryptedData = nil return nil } func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) { pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey) priv := new(elgamal.PrivateKey) priv.PublicKey = *pub buf := bytes.NewBuffer(data) x, _, err := readMPI(buf) if err != nil { return } priv.X = new(big.Int).SetBytes(x) pk.PrivateKey = priv pk.Encrypted = false pk.encryptedData = nil return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/symmetrically_encrypted.go����������0000644�0000153�0000161�00000016124�12321735647�031615� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "code.google.com/p/go.crypto/openpgp/errors" "crypto/cipher" "crypto/sha1" "crypto/subtle" "hash" "io" "strconv" ) // SymmetricallyEncrypted represents a symmetrically encrypted byte string. The // encrypted contents will consist of more OpenPGP packets. See RFC 4880, // sections 5.7 and 5.13. type SymmetricallyEncrypted struct { MDC bool // true iff this is a type 18 packet and thus has an embedded MAC. contents io.Reader prefix []byte } const symmetricallyEncryptedVersion = 1 func (se *SymmetricallyEncrypted) parse(r io.Reader) error { if se.MDC { // See RFC 4880, section 5.13. var buf [1]byte _, err := readFull(r, buf[:]) if err != nil { return err } if buf[0] != symmetricallyEncryptedVersion { return errors.UnsupportedError("unknown SymmetricallyEncrypted version") } } se.contents = r return nil } // Decrypt returns a ReadCloser, from which the decrypted contents of the // packet can be read. An incorrect key can, with high probability, be detected // immediately and this will result in a KeyIncorrect error being returned. func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) { keySize := c.KeySize() if keySize == 0 { return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) } if len(key) != keySize { return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length") } if se.prefix == nil { se.prefix = make([]byte, c.blockSize()+2) _, err := readFull(se.contents, se.prefix) if err != nil { return nil, err } } else if len(se.prefix) != c.blockSize()+2 { return nil, errors.InvalidArgumentError("can't try ciphers with different block lengths") } ocfbResync := OCFBResync if se.MDC { // MDC packets use a different form of OCFB mode. ocfbResync = OCFBNoResync } s := NewOCFBDecrypter(c.new(key), se.prefix, ocfbResync) if s == nil { return nil, errors.ErrKeyIncorrect } plaintext := cipher.StreamReader{S: s, R: se.contents} if se.MDC { // MDC packets have an embedded hash that we need to check. h := sha1.New() h.Write(se.prefix) return &seMDCReader{in: plaintext, h: h}, nil } // Otherwise, we just need to wrap plaintext so that it's a valid ReadCloser. return seReader{plaintext}, nil } // seReader wraps an io.Reader with a no-op Close method. type seReader struct { in io.Reader } func (ser seReader) Read(buf []byte) (int, error) { return ser.in.Read(buf) } func (ser seReader) Close() error { return nil } const mdcTrailerSize = 1 /* tag byte */ + 1 /* length byte */ + sha1.Size // An seMDCReader wraps an io.Reader, maintains a running hash and keeps hold // of the most recent 22 bytes (mdcTrailerSize). Upon EOF, those bytes form an // MDC packet containing a hash of the previous contents which is checked // against the running hash. See RFC 4880, section 5.13. type seMDCReader struct { in io.Reader h hash.Hash trailer [mdcTrailerSize]byte scratch [mdcTrailerSize]byte trailerUsed int error bool eof bool } func (ser *seMDCReader) Read(buf []byte) (n int, err error) { if ser.error { err = io.ErrUnexpectedEOF return } if ser.eof { err = io.EOF return } // If we haven't yet filled the trailer buffer then we must do that // first. for ser.trailerUsed < mdcTrailerSize { n, err = ser.in.Read(ser.trailer[ser.trailerUsed:]) ser.trailerUsed += n if err == io.EOF { if ser.trailerUsed != mdcTrailerSize { n = 0 err = io.ErrUnexpectedEOF ser.error = true return } ser.eof = true n = 0 return } if err != nil { n = 0 return } } // If it's a short read then we read into a temporary buffer and shift // the data into the caller's buffer. if len(buf) <= mdcTrailerSize { n, err = readFull(ser.in, ser.scratch[:len(buf)]) copy(buf, ser.trailer[:n]) ser.h.Write(buf[:n]) copy(ser.trailer[:], ser.trailer[n:]) copy(ser.trailer[mdcTrailerSize-n:], ser.scratch[:]) if n < len(buf) { ser.eof = true err = io.EOF } return } n, err = ser.in.Read(buf[mdcTrailerSize:]) copy(buf, ser.trailer[:]) ser.h.Write(buf[:n]) copy(ser.trailer[:], buf[n:]) if err == io.EOF { ser.eof = true } return } // This is a new-format packet tag byte for a type 19 (MDC) packet. const mdcPacketTagByte = byte(0x80) | 0x40 | 19 func (ser *seMDCReader) Close() error { if ser.error { return errors.SignatureError("error during reading") } for !ser.eof { // We haven't seen EOF so we need to read to the end var buf [1024]byte _, err := ser.Read(buf[:]) if err == io.EOF { break } if err != nil { return errors.SignatureError("error during reading") } } if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size { return errors.SignatureError("MDC packet not found") } ser.h.Write(ser.trailer[:2]) final := ser.h.Sum(nil) if subtle.ConstantTimeCompare(final, ser.trailer[2:]) != 1 { return errors.SignatureError("hash mismatch") } return nil } // An seMDCWriter writes through to an io.WriteCloser while maintains a running // hash of the data written. On close, it emits an MDC packet containing the // running hash. type seMDCWriter struct { w io.WriteCloser h hash.Hash } func (w *seMDCWriter) Write(buf []byte) (n int, err error) { w.h.Write(buf) return w.w.Write(buf) } func (w *seMDCWriter) Close() (err error) { var buf [mdcTrailerSize]byte buf[0] = mdcPacketTagByte buf[1] = sha1.Size w.h.Write(buf[:2]) digest := w.h.Sum(nil) copy(buf[2:], digest) _, err = w.w.Write(buf[:]) if err != nil { return } return w.w.Close() } // noOpCloser is like an ioutil.NopCloser, but for an io.Writer. type noOpCloser struct { w io.Writer } func (c noOpCloser) Write(data []byte) (n int, err error) { return c.w.Write(data) } func (c noOpCloser) Close() error { return nil } // SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet // to w and returns a WriteCloser to which the to-be-encrypted packets can be // written. // If config is nil, sensible defaults will be used. func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte, config *Config) (contents io.WriteCloser, err error) { if c.KeySize() != len(key) { return nil, errors.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") } writeCloser := noOpCloser{w} ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedMDC) if err != nil { return } _, err = ciphertext.Write([]byte{symmetricallyEncryptedVersion}) if err != nil { return } block := c.new(key) blockSize := block.BlockSize() iv := make([]byte, blockSize) _, err = config.Random().Read(iv) if err != nil { return } s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync) _, err = ciphertext.Write(prefix) if err != nil { return } plaintext := cipher.StreamWriter{S: s, W: ciphertext} h := sha1.New() h.Write(iv) h.Write(iv[blockSize-2:]) contents = &seMDCWriter{w: plaintext, h: h} return } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/packet/encrypted_key.go��������������������0000644�0000153�0000161�00000012076�12321735647�027511� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 packet import ( "code.google.com/p/go.crypto/openpgp/elgamal" "code.google.com/p/go.crypto/openpgp/errors" "crypto/rsa" "encoding/binary" "io" "math/big" "strconv" ) const encryptedKeyVersion = 3 // EncryptedKey represents a public-key encrypted session key. See RFC 4880, // section 5.1. type EncryptedKey struct { KeyId uint64 Algo PublicKeyAlgorithm CipherFunc CipherFunction // only valid after a successful Decrypt Key []byte // only valid after a successful Decrypt encryptedMPI1, encryptedMPI2 []byte } func (e *EncryptedKey) parse(r io.Reader) (err error) { var buf [10]byte _, err = readFull(r, buf[:]) if err != nil { return } if buf[0] != encryptedKeyVersion { return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) } e.KeyId = binary.BigEndian.Uint64(buf[1:9]) e.Algo = PublicKeyAlgorithm(buf[9]) switch e.Algo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: e.encryptedMPI1, _, err = readMPI(r) case PubKeyAlgoElGamal: e.encryptedMPI1, _, err = readMPI(r) if err != nil { return } e.encryptedMPI2, _, err = readMPI(r) } _, err = consumeAll(r) return } func checksumKeyMaterial(key []byte) uint16 { var checksum uint16 for _, v := range key { checksum += uint16(v) } return checksum } // Decrypt decrypts an encrypted session key with the given private key. The // private key must have been decrypted first. // If config is nil, sensible defaults will be used. func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { var err error var b []byte // TODO(agl): use session key decryption routines here to avoid // padding oracle attacks. switch priv.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: b, err = rsa.DecryptPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1) case PubKeyAlgoElGamal: c1 := new(big.Int).SetBytes(e.encryptedMPI1) c2 := new(big.Int).SetBytes(e.encryptedMPI2) b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2) default: err = errors.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) } if err != nil { return err } e.CipherFunc = CipherFunction(b[0]) e.Key = b[1 : len(b)-2] expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) checksum := checksumKeyMaterial(e.Key) if checksum != expectedChecksum { return errors.StructuralError("EncryptedKey checksum incorrect") } return nil } // SerializeEncryptedKey serializes an encrypted key packet to w that contains // key, encrypted to pub. // If config is nil, sensible defaults will be used. func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error { var buf [10]byte buf[0] = encryptedKeyVersion binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) buf[9] = byte(pub.PubKeyAlgo) keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */) keyBlock[0] = byte(cipherFunc) copy(keyBlock[1:], key) checksum := checksumKeyMaterial(key) keyBlock[1+len(key)] = byte(checksum >> 8) keyBlock[1+len(key)+1] = byte(checksum) switch pub.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) case PubKeyAlgoElGamal: return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) } return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) } func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) error { cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock) if err != nil { return errors.InvalidArgumentError("RSA encryption failed: " + err.Error()) } packetLen := 10 /* header length */ + 2 /* mpi size */ + len(cipherText) err = serializeHeader(w, packetTypeEncryptedKey, packetLen) if err != nil { return err } _, err = w.Write(header[:]) if err != nil { return err } return writeMPI(w, 8*uint16(len(cipherText)), cipherText) } func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error { c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock) if err != nil { return errors.InvalidArgumentError("ElGamal encryption failed: " + err.Error()) } packetLen := 10 /* header length */ packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8 packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8 err = serializeHeader(w, packetTypeEncryptedKey, packetLen) if err != nil { return err } _, err = w.Write(header[:]) if err != nil { return err } err = writeBig(w, c1) if err != nil { return err } return writeBig(w, c2) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/armor/�������������������������������������0000755�0000153�0000161�00000000000�12321735647�024160� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/armor/armor_test.go������������������������0000644�0000153�0000161�00000006036�12321735647�026673� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 armor import ( "bytes" "hash/adler32" "io/ioutil" "testing" ) func TestDecodeEncode(t *testing.T) { buf := bytes.NewBuffer([]byte(armorExample1)) result, err := Decode(buf) if err != nil { t.Error(err) } expectedType := "PGP SIGNATURE" if result.Type != expectedType { t.Errorf("result.Type: got:%s want:%s", result.Type, expectedType) } if len(result.Header) != 1 { t.Errorf("len(result.Header): got:%d want:1", len(result.Header)) } v, ok := result.Header["Version"] if !ok || v != "GnuPG v1.4.10 (GNU/Linux)" { t.Errorf("result.Header: got:%#v", result.Header) } contents, err := ioutil.ReadAll(result.Body) if err != nil { t.Error(err) } if adler32.Checksum(contents) != 0x27b144be { t.Errorf("contents: got: %x", contents) } buf = bytes.NewBuffer(nil) w, err := Encode(buf, result.Type, result.Header) if err != nil { t.Error(err) } _, err = w.Write(contents) if err != nil { t.Error(err) } w.Close() if !bytes.Equal(buf.Bytes(), []byte(armorExample1)) { t.Errorf("got: %s\nwant: %s", string(buf.Bytes()), armorExample1) } } func TestLongHeader(t *testing.T) { buf := bytes.NewBuffer([]byte(armorLongLine)) result, err := Decode(buf) if err != nil { t.Error(err) return } value, ok := result.Header["Version"] if !ok { t.Errorf("missing Version header") } if value != longValueExpected { t.Errorf("got: %s want: %s", value, longValueExpected) } } const armorExample1 = `-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) iJwEAAECAAYFAk1Fv/0ACgkQo01+GMIMMbsYTwQAiAw+QAaNfY6WBdplZ/uMAccm 4g+81QPmTSGHnetSb6WBiY13kVzK4HQiZH8JSkmmroMLuGeJwsRTEL4wbjRyUKEt p1xwUZDECs234F1xiG5enc5SGlRtP7foLBz9lOsjx+LEcA4sTl5/2eZR9zyFZqWW TxRjs+fJCIFuo71xb1g= =/teI -----END PGP SIGNATURE-----` const armorLongLine = `-----BEGIN PGP SIGNATURE----- Version: 0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz iQEcBAABAgAGBQJMtFESAAoJEKsQXJGvOPsVj40H/1WW6jaMXv4BW+1ueDSMDwM8 kx1fLOXbVM5/Kn5LStZNt1jWWnpxdz7eq3uiqeCQjmqUoRde3YbB2EMnnwRbAhpp cacnAvy9ZQ78OTxUdNW1mhX5bS6q1MTEJnl+DcyigD70HG/yNNQD7sOPMdYQw0TA byQBwmLwmTsuZsrYqB68QyLHI+DUugn+kX6Hd2WDB62DKa2suoIUIHQQCd/ofwB3 WfCYInXQKKOSxu2YOg2Eb4kLNhSMc1i9uKUWAH+sdgJh7NBgdoE4MaNtBFkHXRvv okWuf3+xA9ksp1npSY/mDvgHijmjvtpRDe6iUeqfCn8N9u9CBg8geANgaG8+QA4= =wfQG -----END PGP SIGNATURE-----` const longValueExpected = "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/armor/armor.go�����������������������������0000644�0000153�0000161�00000011574�12321735647�025637� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is // very similar to PEM except that it has an additional CRC checksum. package armor import ( "bufio" "bytes" "code.google.com/p/go.crypto/openpgp/errors" "encoding/base64" "io" ) // A Block represents an OpenPGP armored structure. // // The encoded form is: // -----BEGIN Type----- // Headers // // base64-encoded Bytes // '=' base64 encoded checksum // -----END Type----- // where Headers is a possibly empty sequence of Key: Value lines. // // Since the armored data can be very large, this package presents a streaming // interface. type Block struct { Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE"). Header map[string]string // Optional headers. Body io.Reader // A Reader from which the contents can be read lReader lineReader oReader openpgpReader } var ArmorCorrupt error = errors.StructuralError("armor invalid") const crc24Init = 0xb704ce const crc24Poly = 0x1864cfb const crc24Mask = 0xffffff // crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 func crc24(crc uint32, d []byte) uint32 { for _, b := range d { crc ^= uint32(b) << 16 for i := 0; i < 8; i++ { crc <<= 1 if crc&0x1000000 != 0 { crc ^= crc24Poly } } } return crc } var armorStart = []byte("-----BEGIN ") var armorEnd = []byte("-----END ") var armorEndOfLine = []byte("-----") // lineReader wraps a line based reader. It watches for the end of an armor // block and records the expected CRC value. type lineReader struct { in *bufio.Reader buf []byte eof bool crc uint32 } func (l *lineReader) Read(p []byte) (n int, err error) { if l.eof { return 0, io.EOF } if len(l.buf) > 0 { n = copy(p, l.buf) l.buf = l.buf[n:] return } line, isPrefix, err := l.in.ReadLine() if err != nil { return } if isPrefix { return 0, ArmorCorrupt } if len(line) == 5 && line[0] == '=' { // This is the checksum line var expectedBytes [3]byte var m int m, err = base64.StdEncoding.Decode(expectedBytes[0:], line[1:]) if m != 3 || err != nil { return } l.crc = uint32(expectedBytes[0])<<16 | uint32(expectedBytes[1])<<8 | uint32(expectedBytes[2]) line, _, err = l.in.ReadLine() if err != nil && err != io.EOF { return } if !bytes.HasPrefix(line, armorEnd) { return 0, ArmorCorrupt } l.eof = true return 0, io.EOF } if len(line) > 96 { return 0, ArmorCorrupt } n = copy(p, line) bytesToSave := len(line) - n if bytesToSave > 0 { if cap(l.buf) < bytesToSave { l.buf = make([]byte, 0, bytesToSave) } l.buf = l.buf[0:bytesToSave] copy(l.buf, line[n:]) } return } // openpgpReader passes Read calls to the underlying base64 decoder, but keeps // a running CRC of the resulting data and checks the CRC against the value // found by the lineReader at EOF. type openpgpReader struct { lReader *lineReader b64Reader io.Reader currentCRC uint32 } func (r *openpgpReader) Read(p []byte) (n int, err error) { n, err = r.b64Reader.Read(p) r.currentCRC = crc24(r.currentCRC, p[:n]) if err == io.EOF { if r.lReader.crc != uint32(r.currentCRC&crc24Mask) { return 0, ArmorCorrupt } } return } // Decode reads a PGP armored block from the given Reader. It will ignore // leading garbage. If it doesn't find a block, it will return nil, io.EOF. The // given Reader is not usable after calling this function: an arbitrary amount // of data may have been read past the end of the block. func Decode(in io.Reader) (p *Block, err error) { r := bufio.NewReaderSize(in, 100) var line []byte ignoreNext := false TryNextBlock: p = nil // Skip leading garbage for { ignoreThis := ignoreNext line, ignoreNext, err = r.ReadLine() if err != nil { return } if ignoreNext || ignoreThis { continue } line = bytes.TrimSpace(line) if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) { break } } p = new(Block) p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)]) p.Header = make(map[string]string) nextIsContinuation := false var lastKey string // Read headers for { isContinuation := nextIsContinuation line, nextIsContinuation, err = r.ReadLine() if err != nil { p = nil return } if isContinuation { p.Header[lastKey] += string(line) continue } line = bytes.TrimSpace(line) if len(line) == 0 { break } i := bytes.Index(line, []byte(": ")) if i == -1 { goto TryNextBlock } lastKey = string(line[:i]) p.Header[lastKey] = string(line[i+2:]) } p.lReader.in = r p.oReader.currentCRC = crc24Init p.oReader.lReader = &p.lReader p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) p.Body = &p.oReader return } ������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/armor/encode.go����������������������������0000644�0000153�0000161�00000006500�12321735647�025745� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 armor import ( "encoding/base64" "io" ) var armorHeaderSep = []byte(": ") var blockEnd = []byte("\n=") var newline = []byte("\n") var armorEndOfLineOut = []byte("-----\n") // writeSlices writes its arguments to the given Writer. func writeSlices(out io.Writer, slices ...[]byte) (err error) { for _, s := range slices { _, err = out.Write(s) if err != nil { return err } } return } // lineBreaker breaks data across several lines, all of the same byte length // (except possibly the last). Lines are broken with a single '\n'. type lineBreaker struct { lineLength int line []byte used int out io.Writer haveWritten bool } func newLineBreaker(out io.Writer, lineLength int) *lineBreaker { return &lineBreaker{ lineLength: lineLength, line: make([]byte, lineLength), used: 0, out: out, } } func (l *lineBreaker) Write(b []byte) (n int, err error) { n = len(b) if n == 0 { return } if l.used == 0 && l.haveWritten { _, err = l.out.Write([]byte{'\n'}) if err != nil { return } } if l.used+len(b) < l.lineLength { l.used += copy(l.line[l.used:], b) return } l.haveWritten = true _, err = l.out.Write(l.line[0:l.used]) if err != nil { return } excess := l.lineLength - l.used l.used = 0 _, err = l.out.Write(b[0:excess]) if err != nil { return } _, err = l.Write(b[excess:]) return } func (l *lineBreaker) Close() (err error) { if l.used > 0 { _, err = l.out.Write(l.line[0:l.used]) if err != nil { return } } return } // encoding keeps track of a running CRC24 over the data which has been written // to it and outputs a OpenPGP checksum when closed, followed by an armor // trailer. // // It's built into a stack of io.Writers: // encoding -> base64 encoder -> lineBreaker -> out type encoding struct { out io.Writer breaker *lineBreaker b64 io.WriteCloser crc uint32 blockType []byte } func (e *encoding) Write(data []byte) (n int, err error) { e.crc = crc24(e.crc, data) return e.b64.Write(data) } func (e *encoding) Close() (err error) { err = e.b64.Close() if err != nil { return } e.breaker.Close() var checksumBytes [3]byte checksumBytes[0] = byte(e.crc >> 16) checksumBytes[1] = byte(e.crc >> 8) checksumBytes[2] = byte(e.crc) var b64ChecksumBytes [4]byte base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine) } // Encode returns a WriteCloser which will encode the data written to it in // OpenPGP armor. func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) { bType := []byte(blockType) err = writeSlices(out, armorStart, bType, armorEndOfLineOut) if err != nil { return } for k, v := range headers { err = writeSlices(out, []byte(k), armorHeaderSep, []byte(v), newline) if err != nil { return } } _, err = out.Write(newline) if err != nil { return } e := &encoding{ out: out, breaker: newLineBreaker(out, 64), crc: crc24Init, blockType: bType, } e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) return e, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/keys_test.go�������������������������������0000644�0000153�0000161�00000006470�12321735647�025410� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package openpgp import ( "testing" "time" ) func TestKeyExpiry(t *testing.T) { kring, _ := ReadKeyRing(readerFromHex(expiringKeyHex)) entity := kring[0] const timeFormat = "2006-01-02" time1, _ := time.Parse(timeFormat, "2013-07-01") // The expiringKeyHex key is structured as: // // pub 1024R/5E237D8C created: 2013-07-01 expires: 2013-07-31 usage: SC // sub 1024R/1ABB25A0 created: 2013-07-01 expires: 2013-07-08 usage: E // sub 1024R/96A672F5 created: 2013-07-01 expires: 2013-07-31 usage: E // // So this should select the first, non-expired encryption key. key, _ := entity.encryptionKey(time1) if id := key.PublicKey.KeyIdShortString(); id != "1ABB25A0" { t.Errorf("Expected key 1ABB25A0 at time %s, but got key %s", time1.Format(timeFormat), id) } // Once the first encryption subkey has expired, the second should be // selected. time2, _ := time.Parse(timeFormat, "2013-07-09") key, _ = entity.encryptionKey(time2) if id := key.PublicKey.KeyIdShortString(); id != "96A672F5" { t.Errorf("Expected key 96A672F5 at time %s, but got key %s", time2.Format(timeFormat), id) } // Once all the keys have expired, nothing should be returned. time3, _ := time.Parse(timeFormat, "2013-08-01") if key, ok := entity.encryptionKey(time3); ok { t.Errorf("Expected no key at time %s, but got key %s", time3.Format(timeFormat), key.PublicKey.KeyIdShortString()) } } const expiringKeyHex = "988d0451d1ec5d010400ba3385721f2dc3f4ab096b2ee867ab77213f0a27a8538441c35d2fa225b08798a1439a66a5150e6bdc3f40f5d28d588c712394c632b6299f77db8c0d48d37903fb72ebd794d61be6aa774688839e5fdecfe06b2684cc115d240c98c66cb1ef22ae84e3aa0c2b0c28665c1e7d4d044e7f270706193f5223c8d44e0d70b7b8da830011010001b40f4578706972792074657374206b657988be041301020028050251d1ec5d021b03050900278d00060b090807030206150802090a0b0416020301021e01021780000a091072589ad75e237d8c033503fd10506d72837834eb7f994117740723adc39227104b0d326a1161871c0b415d25b4aedef946ca77ea4c05af9c22b32cf98be86ab890111fced1ee3f75e87b7cc3c00dc63bbc85dfab91c0dc2ad9de2c4d13a34659333a85c6acc1a669c5e1d6cecb0cf1e56c10e72d855ae177ddc9e766f9b2dda57ccbb75f57156438bbdb4e42b88d0451d1ec5d0104009c64906559866c5cb61578f5846a94fcee142a489c9b41e67b12bb54cfe86eb9bc8566460f9a720cb00d6526fbccfd4f552071a8e3f7744b1882d01036d811ee5a3fb91a1c568055758f43ba5d2c6a9676b012f3a1a89e47bbf624f1ad571b208f3cc6224eb378f1645dd3d47584463f9eadeacfd1ce6f813064fbfdcc4b5a53001101000188a504180102000f021b0c050251d1f06b050900093e89000a091072589ad75e237d8c20e00400ab8310a41461425b37889c4da28129b5fae6084fafbc0a47dd1adc74a264c6e9c9cc125f40462ee1433072a58384daef88c961c390ed06426a81b464a53194c4e291ddd7e2e2ba3efced01537d713bd111f48437bde2363446200995e8e0d4e528dda377fd1e8f8ede9c8e2198b393bd86852ce7457a7e3daf74d510461a5b77b88d0451d1ece8010400b3a519f83ab0010307e83bca895170acce8964a044190a2b368892f7a244758d9fc193482648acb1fb9780d28cc22d171931f38bb40279389fc9bf2110876d4f3db4fcfb13f22f7083877fe56592b3b65251312c36f83ffcb6d313c6a17f197dd471f0712aad15a8537b435a92471ba2e5b0c72a6c72536c3b567c558d7b6051001101000188a504180102000f021b0c050251d1f07b050900279091000a091072589ad75e237d8ce69e03fe286026afacf7c97ee20673864d4459a2240b5655219950643c7dba0ac384b1d4359c67805b21d98211f7b09c2a0ccf6410c8c04d4ff4a51293725d8d6570d9d8bb0e10c07d22357caeb49626df99c180be02d77d1fe8ed25e7a54481237646083a9f89a11566cd20b9e995b1487c5f9e02aeb434f3a1897cd416dd0a87861838da3e9e" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/keys.go������������������������������������0000644�0000153�0000161�00000036713�12321735647�024354� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 openpgp import ( "code.google.com/p/go.crypto/openpgp/armor" "code.google.com/p/go.crypto/openpgp/errors" "code.google.com/p/go.crypto/openpgp/packet" "crypto/rsa" "io" "time" ) // PublicKeyType is the armor type for a PGP public key. var PublicKeyType = "PGP PUBLIC KEY BLOCK" // PrivateKeyType is the armor type for a PGP private key. var PrivateKeyType = "PGP PRIVATE KEY BLOCK" // An Entity represents the components of an OpenPGP key: a primary public key // (which must be a signing key), one or more identities claimed by that key, // and zero or more subkeys, which may be encryption keys. type Entity struct { PrimaryKey *packet.PublicKey PrivateKey *packet.PrivateKey Identities map[string]*Identity // indexed by Identity.Name Subkeys []Subkey } // An Identity represents an identity claimed by an Entity and zero or more // assertions by other entities about that claim. type Identity struct { Name string // by convention, has the form "Full Name (comment) <email@example.com>" UserId *packet.UserId SelfSignature *packet.Signature Signatures []*packet.Signature } // A Subkey is an additional public key in an Entity. Subkeys can be used for // encryption. type Subkey struct { PublicKey *packet.PublicKey PrivateKey *packet.PrivateKey Sig *packet.Signature } // A Key identifies a specific public key in an Entity. This is either the // Entity's primary key or a subkey. type Key struct { Entity *Entity PublicKey *packet.PublicKey PrivateKey *packet.PrivateKey SelfSignature *packet.Signature } // A KeyRing provides access to public and private keys. type KeyRing interface { // KeysById returns the set of keys that have the given key id. KeysById(id uint64) []Key // DecryptionKeys returns all private keys that are valid for // decryption. DecryptionKeys() []Key } // primaryIdentity returns the Identity marked as primary or the first identity // if none are so marked. func (e *Entity) primaryIdentity() *Identity { var firstIdentity *Identity for _, ident := range e.Identities { if firstIdentity == nil { firstIdentity = ident } if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { return ident } } return firstIdentity } // encryptionKey returns the best candidate Key for encrypting a message to the // given Entity. func (e *Entity) encryptionKey(now time.Time) (Key, bool) { candidateSubkey := -1 for i, subkey := range e.Subkeys { if subkey.Sig.FlagsValid && subkey.Sig.FlagEncryptCommunications && subkey.PublicKey.PubKeyAlgo.CanEncrypt() && !subkey.Sig.KeyExpired(now) { candidateSubkey = i break } } if candidateSubkey != -1 { subkey := e.Subkeys[candidateSubkey] return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true } // If we don't have any candidate subkeys for encryption and // the primary key doesn't have any usage metadata then we // assume that the primary key is ok. Or, if the primary key is // marked as ok to encrypt to, then we can obviously use it. i := e.primaryIdentity() if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && e.PrimaryKey.PubKeyAlgo.CanEncrypt() && !i.SelfSignature.KeyExpired(now) { return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true } // This Entity appears to be signing only. return Key{}, false } // signingKey return the best candidate Key for signing a message with this // Entity. func (e *Entity) signingKey(now time.Time) (Key, bool) { candidateSubkey := -1 for i, subkey := range e.Subkeys { if subkey.Sig.FlagsValid && subkey.Sig.FlagSign && subkey.PublicKey.PubKeyAlgo.CanSign() && !subkey.Sig.KeyExpired(now) { candidateSubkey = i break } } if candidateSubkey != -1 { subkey := e.Subkeys[candidateSubkey] return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true } // If we have no candidate subkey then we assume that it's ok to sign // with the primary key. i := e.primaryIdentity() if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign && !i.SelfSignature.KeyExpired(now) { return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true } return Key{}, false } // An EntityList contains one or more Entities. type EntityList []*Entity // KeysById returns the set of keys that have the given key id. func (el EntityList) KeysById(id uint64) (keys []Key) { for _, e := range el { if e.PrimaryKey.KeyId == id { var selfSig *packet.Signature for _, ident := range e.Identities { if selfSig == nil { selfSig = ident.SelfSignature } else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { selfSig = ident.SelfSignature break } } keys = append(keys, Key{e, e.PrimaryKey, e.PrivateKey, selfSig}) } for _, subKey := range e.Subkeys { if subKey.PublicKey.KeyId == id { keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) } } } return } // DecryptionKeys returns all private keys that are valid for decryption. func (el EntityList) DecryptionKeys() (keys []Key) { for _, e := range el { for _, subKey := range e.Subkeys { if subKey.PrivateKey != nil && (!subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications) { keys = append(keys, Key{e, subKey.PublicKey, subKey.PrivateKey, subKey.Sig}) } } } return } // ReadArmoredKeyRing reads one or more public/private keys from an armor keyring file. func ReadArmoredKeyRing(r io.Reader) (EntityList, error) { block, err := armor.Decode(r) if err == io.EOF { return nil, errors.InvalidArgumentError("no armored data found") } if err != nil { return nil, err } if block.Type != PublicKeyType && block.Type != PrivateKeyType { return nil, errors.InvalidArgumentError("expected public or private key block, got: " + block.Type) } return ReadKeyRing(block.Body) } // ReadKeyRing reads one or more public/private keys. Unsupported keys are // ignored as long as at least a single valid key is found. func ReadKeyRing(r io.Reader) (el EntityList, err error) { packets := packet.NewReader(r) var lastUnsupportedError error for { var e *Entity e, err = ReadEntity(packets) if err != nil { // TODO: warn about skipped unsupported/unreadable keys if _, ok := err.(errors.UnsupportedError); ok { lastUnsupportedError = err err = readToNextPublicKey(packets) } else if _, ok := err.(errors.StructuralError); ok { // Skip unreadable, badly-formatted keys lastUnsupportedError = err err = readToNextPublicKey(packets) } if err == io.EOF { err = nil break } if err != nil { el = nil break } } else { el = append(el, e) } } if len(el) == 0 && err == nil { err = lastUnsupportedError } return } // readToNextPublicKey reads packets until the start of the entity and leaves // the first packet of the new entity in the Reader. func readToNextPublicKey(packets *packet.Reader) (err error) { var p packet.Packet for { p, err = packets.Next() if err == io.EOF { return } else if err != nil { if _, ok := err.(errors.UnsupportedError); ok { err = nil continue } return } if pk, ok := p.(*packet.PublicKey); ok && !pk.IsSubkey { packets.Unread(p) return } } panic("unreachable") } // ReadEntity reads an entity (public key, identities, subkeys etc) from the // given Reader. func ReadEntity(packets *packet.Reader) (*Entity, error) { e := new(Entity) e.Identities = make(map[string]*Identity) p, err := packets.Next() if err != nil { return nil, err } var ok bool if e.PrimaryKey, ok = p.(*packet.PublicKey); !ok { if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok { packets.Unread(p) return nil, errors.StructuralError("first packet was not a public/private key") } else { e.PrimaryKey = &e.PrivateKey.PublicKey } } if !e.PrimaryKey.PubKeyAlgo.CanSign() { return nil, errors.StructuralError("primary key cannot be used for signatures") } var current *Identity EachPacket: for { p, err := packets.Next() if err == io.EOF { break } else if err != nil { return nil, err } switch pkt := p.(type) { case *packet.UserId: current = new(Identity) current.Name = pkt.Id current.UserId = pkt e.Identities[pkt.Id] = current for { p, err = packets.Next() if err == io.EOF { return nil, io.ErrUnexpectedEOF } else if err != nil { return nil, err } sig, ok := p.(*packet.Signature) if !ok { return nil, errors.StructuralError("user ID packet not followed by self-signature") } if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, sig); err != nil { return nil, errors.StructuralError("user ID self-signature invalid: " + err.Error()) } current.SelfSignature = sig break } current.Signatures = append(current.Signatures, sig) } case *packet.Signature: if current == nil { return nil, errors.StructuralError("signature packet found before user id packet") } current.Signatures = append(current.Signatures, pkt) case *packet.PrivateKey: if pkt.IsSubkey == false { packets.Unread(p) break EachPacket } err = addSubkey(e, packets, &pkt.PublicKey, pkt) if err != nil { return nil, err } case *packet.PublicKey: if pkt.IsSubkey == false { packets.Unread(p) break EachPacket } err = addSubkey(e, packets, pkt, nil) if err != nil { return nil, err } default: // we ignore unknown packets } } if len(e.Identities) == 0 { return nil, errors.StructuralError("entity without any identities") } return e, nil } func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error { var subKey Subkey subKey.PublicKey = pub subKey.PrivateKey = priv p, err := packets.Next() if err == io.EOF { return io.ErrUnexpectedEOF } if err != nil { return errors.StructuralError("subkey signature invalid: " + err.Error()) } var ok bool subKey.Sig, ok = p.(*packet.Signature) if !ok { return errors.StructuralError("subkey packet not followed by signature") } if subKey.Sig.SigType != packet.SigTypeSubkeyBinding { return errors.StructuralError("subkey signature with wrong type") } err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig) if err != nil { return errors.StructuralError("subkey signature invalid: " + err.Error()) } e.Subkeys = append(e.Subkeys, subKey) return nil } const defaultRSAKeyBits = 2048 // NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a // single identity composed of the given full name, comment and email, any of // which may be empty but must not contain any of "()<>\x00". // If config is nil, sensible defaults will be used. func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) { currentTime := config.Now() uid := packet.NewUserId(name, comment, email) if uid == nil { return nil, errors.InvalidArgumentError("user id field contained invalid characters") } signingPriv, err := rsa.GenerateKey(config.Random(), defaultRSAKeyBits) if err != nil { return nil, err } encryptingPriv, err := rsa.GenerateKey(config.Random(), defaultRSAKeyBits) if err != nil { return nil, err } e := &Entity{ PrimaryKey: packet.NewRSAPublicKey(currentTime, &signingPriv.PublicKey), PrivateKey: packet.NewRSAPrivateKey(currentTime, signingPriv), Identities: make(map[string]*Identity), } isPrimaryId := true e.Identities[uid.Id] = &Identity{ Name: uid.Name, UserId: uid, SelfSignature: &packet.Signature{ CreationTime: currentTime, SigType: packet.SigTypePositiveCert, PubKeyAlgo: packet.PubKeyAlgoRSA, Hash: config.Hash(), IsPrimaryId: &isPrimaryId, FlagsValid: true, FlagSign: true, FlagCertify: true, IssuerKeyId: &e.PrimaryKey.KeyId, }, } e.Subkeys = make([]Subkey, 1) e.Subkeys[0] = Subkey{ PublicKey: packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey), PrivateKey: packet.NewRSAPrivateKey(currentTime, encryptingPriv), Sig: &packet.Signature{ CreationTime: currentTime, SigType: packet.SigTypeSubkeyBinding, PubKeyAlgo: packet.PubKeyAlgoRSA, Hash: config.Hash(), FlagsValid: true, FlagEncryptStorage: true, FlagEncryptCommunications: true, IssuerKeyId: &e.PrimaryKey.KeyId, }, } e.Subkeys[0].PublicKey.IsSubkey = true e.Subkeys[0].PrivateKey.IsSubkey = true return e, nil } // SerializePrivate serializes an Entity, including private key material, to // the given Writer. For now, it must only be used on an Entity returned from // NewEntity. // If config is nil, sensible defaults will be used. func (e *Entity) SerializePrivate(w io.Writer, config *packet.Config) (err error) { err = e.PrivateKey.Serialize(w) if err != nil { return } for _, ident := range e.Identities { err = ident.UserId.Serialize(w) if err != nil { return } err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey, config) if err != nil { return } err = ident.SelfSignature.Serialize(w) if err != nil { return } } for _, subkey := range e.Subkeys { err = subkey.PrivateKey.Serialize(w) if err != nil { return } err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config) if err != nil { return } err = subkey.Sig.Serialize(w) if err != nil { return } } return nil } // Serialize writes the public part of the given Entity to w. (No private // key material will be output). func (e *Entity) Serialize(w io.Writer) error { err := e.PrimaryKey.Serialize(w) if err != nil { return err } for _, ident := range e.Identities { err = ident.UserId.Serialize(w) if err != nil { return err } err = ident.SelfSignature.Serialize(w) if err != nil { return err } for _, sig := range ident.Signatures { err = sig.Serialize(w) if err != nil { return err } } } for _, subkey := range e.Subkeys { err = subkey.PublicKey.Serialize(w) if err != nil { return err } err = subkey.Sig.Serialize(w) if err != nil { return err } } return nil } // SignIdentity adds a signature to e, from signer, attesting that identity is // associated with e. The provided identity must already be an element of // e.Identities and the private key of signer must have been decrypted if // necessary. // If config is nil, sensible defaults will be used. func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Config) error { if signer.PrivateKey == nil { return errors.InvalidArgumentError("signing Entity must have a private key") } if signer.PrivateKey.Encrypted { return errors.InvalidArgumentError("signing Entity's private key must be decrypted") } ident, ok := e.Identities[identity] if !ok { return errors.InvalidArgumentError("given identity string not found in Entity") } sig := &packet.Signature{ SigType: packet.SigTypeGenericCert, PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, Hash: config.Hash(), CreationTime: config.Now(), IssuerKeyId: &signer.PrivateKey.KeyId, } if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey, config); err != nil { return err } ident.Signatures = append(ident.Signatures, sig) return nil } �����������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/canonical_text.go��������������������������0000644�0000153�0000161�00000002167�12321735647�026370� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 openpgp import "hash" // NewCanonicalTextHash reformats text written to it into the canonical // form and then applies the hash h. See RFC 4880, section 5.2.1. func NewCanonicalTextHash(h hash.Hash) hash.Hash { return &canonicalTextHash{h, 0} } type canonicalTextHash struct { h hash.Hash s int } var newline = []byte{'\r', '\n'} func (cth *canonicalTextHash) Write(buf []byte) (int, error) { start := 0 for i, c := range buf { switch cth.s { case 0: if c == '\r' { cth.s = 1 } else if c == '\n' { cth.h.Write(buf[start:i]) cth.h.Write(newline) start = i + 1 } case 1: cth.s = 0 } } cth.h.Write(buf[start:]) return len(buf), nil } func (cth *canonicalTextHash) Sum(in []byte) []byte { return cth.h.Sum(in) } func (cth *canonicalTextHash) Reset() { cth.h.Reset() cth.s = 0 } func (cth *canonicalTextHash) Size() int { return cth.h.Size() } func (cth *canonicalTextHash) BlockSize() int { return cth.h.BlockSize() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/s2k/���������������������������������������0000755�0000153�0000161�00000000000�12321735647�023537� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/s2k/s2k_test.go����������������������������0000644�0000153�0000161�00000005225�12321735647�025630� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 s2k import ( "bytes" "crypto/rand" "crypto/sha1" "encoding/hex" "testing" ) var saltedTests = []struct { in, out string }{ {"hello", "10295ac1"}, {"world", "ac587a5e"}, {"foo", "4dda8077"}, {"bar", "bd8aac6b9ea9cae04eae6a91c6133b58b5d9a61c14f355516ed9370456"}, {"x", "f1d3f289"}, {"xxxxxxxxxxxxxxxxxxxxxxx", "e00d7b45"}, } func TestSalted(t *testing.T) { h := sha1.New() salt := [4]byte{1, 2, 3, 4} for i, test := range saltedTests { expected, _ := hex.DecodeString(test.out) out := make([]byte, len(expected)) Salted(out, h, []byte(test.in), salt[:]) if !bytes.Equal(expected, out) { t.Errorf("#%d, got: %x want: %x", i, out, expected) } } } var iteratedTests = []struct { in, out string }{ {"hello", "83126105"}, {"world", "6fa317f9"}, {"foo", "8fbc35b9"}, {"bar", "2af5a99b54f093789fd657f19bd245af7604d0f6ae06f66602a46a08ae"}, {"x", "5a684dfe"}, {"xxxxxxxxxxxxxxxxxxxxxxx", "18955174"}, } func TestIterated(t *testing.T) { h := sha1.New() salt := [4]byte{4, 3, 2, 1} for i, test := range iteratedTests { expected, _ := hex.DecodeString(test.out) out := make([]byte, len(expected)) Iterated(out, h, []byte(test.in), salt[:], 31) if !bytes.Equal(expected, out) { t.Errorf("#%d, got: %x want: %x", i, out, expected) } } } var parseTests = []struct { spec, in, out string }{ /* Simple with SHA1 */ {"0002", "hello", "aaf4c61d"}, /* Salted with SHA1 */ {"01020102030405060708", "hello", "f4f7d67e"}, /* Iterated with SHA1 */ {"03020102030405060708f1", "hello", "f2a57b7c"}, } func TestParse(t *testing.T) { for i, test := range parseTests { spec, _ := hex.DecodeString(test.spec) buf := bytes.NewBuffer(spec) f, err := Parse(buf) if err != nil { t.Errorf("%d: Parse returned error: %s", i, err) continue } expected, _ := hex.DecodeString(test.out) out := make([]byte, len(expected)) f(out, []byte(test.in)) if !bytes.Equal(out, expected) { t.Errorf("%d: output got: %x want: %x", i, out, expected) } if testing.Short() { break } } } func TestSerialize(t *testing.T) { buf := bytes.NewBuffer(nil) key := make([]byte, 16) passphrase := []byte("testing") err := Serialize(buf, key, rand.Reader, passphrase) if err != nil { t.Errorf("failed to serialize: %s", err) return } f, err := Parse(buf) if err != nil { t.Errorf("failed to reparse: %s", err) return } key2 := make([]byte, len(key)) f(key2, passphrase) if !bytes.Equal(key2, key) { t.Errorf("keys don't match: %x (serialied) vs %x (parsed)", key, key2) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/s2k/s2k.go���������������������������������0000644�0000153�0000161�00000011377�12321735647�024576� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 s2k implements the various OpenPGP string-to-key transforms as // specified in RFC 4800 section 3.7.1. package s2k import ( "code.google.com/p/go.crypto/openpgp/errors" "crypto" "hash" "io" "strconv" ) // Simple writes to out the result of computing the Simple S2K function (RFC // 4880, section 3.7.1.1) using the given hash and input passphrase. func Simple(out []byte, h hash.Hash, in []byte) { Salted(out, h, in, nil) } var zero [1]byte // Salted writes to out the result of computing the Salted S2K function (RFC // 4880, section 3.7.1.2) using the given hash, input passphrase and salt. func Salted(out []byte, h hash.Hash, in []byte, salt []byte) { done := 0 var digest []byte for i := 0; done < len(out); i++ { h.Reset() for j := 0; j < i; j++ { h.Write(zero[:]) } h.Write(salt) h.Write(in) digest = h.Sum(digest[:0]) n := copy(out[done:], digest) done += n } } // Iterated writes to out the result of computing the Iterated and Salted S2K // function (RFC 4880, section 3.7.1.3) using the given hash, input passphrase, // salt and iteration count. func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) { combined := make([]byte, len(in)+len(salt)) copy(combined, salt) copy(combined[len(salt):], in) if count < len(combined) { count = len(combined) } done := 0 var digest []byte for i := 0; done < len(out); i++ { h.Reset() for j := 0; j < i; j++ { h.Write(zero[:]) } written := 0 for written < count { if written+len(combined) > count { todo := count - written h.Write(combined[:todo]) written = count } else { h.Write(combined) written += len(combined) } } digest = h.Sum(digest[:0]) n := copy(out[done:], digest) done += n } } // Parse reads a binary specification for a string-to-key transformation from r // and returns a function which performs that transform. func Parse(r io.Reader) (f func(out, in []byte), err error) { var buf [9]byte _, err = io.ReadFull(r, buf[:2]) if err != nil { return } hash, ok := HashIdToHash(buf[1]) if !ok { return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1]))) } h := hash.New() if h == nil { return nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hash))) } switch buf[0] { case 0: f := func(out, in []byte) { Simple(out, h, in) } return f, nil case 1: _, err = io.ReadFull(r, buf[:8]) if err != nil { return } f := func(out, in []byte) { Salted(out, h, in, buf[:8]) } return f, nil case 3: _, err = io.ReadFull(r, buf[:9]) if err != nil { return } count := (16 + int(buf[8]&15)) << (uint32(buf[8]>>4) + 6) f := func(out, in []byte) { Iterated(out, h, in, buf[:8], count) } return f, nil } return nil, errors.UnsupportedError("S2K function") } // Serialize salts and stretches the given passphrase and writes the resulting // key into key. It also serializes an S2K descriptor to w. func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte) error { var buf [11]byte buf[0] = 3 /* iterated and salted */ buf[1], _ = HashToHashId(crypto.SHA1) salt := buf[2:10] if _, err := io.ReadFull(rand, salt); err != nil { return err } const count = 65536 // this is the default in gpg buf[10] = 96 // 65536 iterations if _, err := w.Write(buf[:]); err != nil { return err } Iterated(key, crypto.SHA1.New(), passphrase, salt, count) return nil } // hashToHashIdMapping contains pairs relating OpenPGP's hash identifier with // Go's crypto.Hash type. See RFC 4880, section 9.4. var hashToHashIdMapping = []struct { id byte hash crypto.Hash name string }{ {1, crypto.MD5, "MD5"}, {2, crypto.SHA1, "SHA1"}, {3, crypto.RIPEMD160, "RIPEMD160"}, {8, crypto.SHA256, "SHA256"}, {9, crypto.SHA384, "SHA384"}, {10, crypto.SHA512, "SHA512"}, {11, crypto.SHA224, "SHA224"}, } // HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP // hash id. func HashIdToHash(id byte) (h crypto.Hash, ok bool) { for _, m := range hashToHashIdMapping { if m.id == id { return m.hash, true } } return 0, false } // HashIdToString returns the name of the hash function corresponding to the // given OpenPGP hash id, or panics if id is unknown. func HashIdToString(id byte) (name string, ok bool) { for _, m := range hashToHashIdMapping { if m.id == id { return m.name, true } } return "", false } // HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. func HashToHashId(h crypto.Hash) (id byte, ok bool) { for _, m := range hashToHashIdMapping { if m.hash == h { return m.id, true } } return 0, false } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/clearsign/���������������������������������0000755�0000153�0000161�00000000000�12321735647�025007� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/clearsign/clearsign.go���������������������0000644�0000153�0000161�00000022235�12321735647�027311� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 clearsign generates and processes OpenPGP, clear-signed data. See // RFC 4880, section 7. // // Clearsigned messages are cryptographically signed, but the contents of the // message are kept in plaintext so that it can be read without special tools. package clearsign import ( "bufio" "bytes" "crypto" "hash" "io" "net/textproto" "strconv" "code.google.com/p/go.crypto/openpgp/armor" "code.google.com/p/go.crypto/openpgp/errors" "code.google.com/p/go.crypto/openpgp/packet" ) // A Block represents a clearsigned message. A signature on a Block can // be checked by passing Bytes into openpgp.CheckDetachedSignature. type Block struct { Headers textproto.MIMEHeader // Optional message headers Plaintext []byte // The original message text Bytes []byte // The signed message ArmoredSignature *armor.Block // The signature block } // start is the marker which denotes the beginning of a clearsigned message. var start = []byte("\n-----BEGIN PGP SIGNED MESSAGE-----") // dashEscape is prefixed to any lines that begin with a hypen so that they // can't be confused with endText. var dashEscape = []byte("- ") // endText is a marker which denotes the end of the message and the start of // an armored signature. var endText = []byte("-----BEGIN PGP SIGNATURE-----") // end is a marker which denotes the end of the armored signature. var end = []byte("\n-----END PGP SIGNATURE-----") var crlf = []byte("\r\n") var lf = byte('\n') // getLine returns the first \r\n or \n delineated line from the given byte // array. The line does not include the \r\n or \n. The remainder of the byte // array (also not including the new line bytes) is also returned and this will // always be smaller than the original argument. func getLine(data []byte) (line, rest []byte) { i := bytes.Index(data, []byte{'\n'}) var j int if i < 0 { i = len(data) j = i } else { j = i + 1 if i > 0 && data[i-1] == '\r' { i-- } } return data[0:i], data[j:] } // Decode finds the first clearsigned message in data and returns it, as well // as the suffix of data which remains after the message. func Decode(data []byte) (b *Block, rest []byte) { // start begins with a newline. However, at the very beginning of // the byte array, we'll accept the start string without it. rest = data if bytes.HasPrefix(data, start[1:]) { rest = rest[len(start)-1:] } else if i := bytes.Index(data, start); i >= 0 { rest = rest[i+len(start):] } else { return nil, data } // Consume the start line. _, rest = getLine(rest) var line []byte b = &Block{ Headers: make(textproto.MIMEHeader), } // Next come a series of header lines. for { // This loop terminates because getLine's second result is // always smaller than its argument. if len(rest) == 0 { return nil, data } // An empty line marks the end of the headers. if line, rest = getLine(rest); len(line) == 0 { break } i := bytes.Index(line, []byte{':'}) if i == -1 { return nil, data } key, val := line[0:i], line[i+1:] key = bytes.TrimSpace(key) val = bytes.TrimSpace(val) b.Headers.Add(string(key), string(val)) } for { start := rest line, rest = getLine(rest) if bytes.Equal(line, endText) { // Back up to the start of the line because armor expects to see the // header line. rest = start break } // The final CRLF isn't included in the hash so we don't write it until // we've seen the next line. if len(b.Bytes) > 0 { b.Bytes = append(b.Bytes, crlf...) } if bytes.HasPrefix(line, dashEscape) { line = line[2:] } line = bytes.TrimRight(line, " \t") b.Bytes = append(b.Bytes, line...) b.Plaintext = append(b.Plaintext, line...) b.Plaintext = append(b.Plaintext, lf) } // We want to find the extent of the armored data (including any newlines at // the end). i := bytes.Index(rest, end) if i == -1 { return nil, data } i += len(end) for i < len(rest) && (rest[i] == '\r' || rest[i] == '\n') { i++ } armored := rest[:i] rest = rest[i:] var err error b.ArmoredSignature, err = armor.Decode(bytes.NewBuffer(armored)) if err != nil { return nil, data } return b, rest } // A dashEscaper is an io.WriteCloser which processes the body of a clear-signed // message. The clear-signed message is written to buffered and a hash, suitable // for signing, is maintained in h. // // When closed, an armored signature is created and written to complete the // message. type dashEscaper struct { buffered *bufio.Writer h hash.Hash hashType crypto.Hash atBeginningOfLine bool isFirstLine bool whitespace []byte byteBuf []byte // a one byte buffer to save allocations privateKey *packet.PrivateKey config *packet.Config } func (d *dashEscaper) Write(data []byte) (n int, err error) { for _, b := range data { d.byteBuf[0] = b if d.atBeginningOfLine { // The final CRLF isn't included in the hash so we have to wait // until this point (the start of the next line) before writing it. if !d.isFirstLine { d.h.Write(crlf) } d.isFirstLine = false // At the beginning of a line, hyphens have to be escaped. if b == '-' { // The signature isn't calculated over the dash-escaped text so // the escape is only written to buffered. if _, err = d.buffered.Write(dashEscape); err != nil { return } d.h.Write(d.byteBuf) d.atBeginningOfLine = false } else if b == '\n' { // Nothing to do because we dely writing CRLF to the hash. } else { d.h.Write(d.byteBuf) d.atBeginningOfLine = false } if err = d.buffered.WriteByte(b); err != nil { return } } else { // Any whitespace at the end of the line has to be removed so we // buffer it until we find out whether there's more on this line. if b == ' ' || b == '\t' || b == '\r' { d.whitespace = append(d.whitespace, b) } else if b == '\n' { // We got a raw \n. Drop any trailing whitespace and write a // CRLF. d.whitespace = d.whitespace[:0] // We dely writing CRLF to the hash until the start of the // next line. if err = d.buffered.WriteByte(b); err != nil { return } d.atBeginningOfLine = true } else { // Any buffered whitespace wasn't at the end of the line so // we need to write it out. if len(d.whitespace) > 0 { d.h.Write(d.whitespace) if _, err = d.buffered.Write(d.whitespace); err != nil { return } d.whitespace = d.whitespace[:0] } d.h.Write(d.byteBuf) if err = d.buffered.WriteByte(b); err != nil { return } } } } n = len(data) return } func (d *dashEscaper) Close() (err error) { if !d.atBeginningOfLine { if err = d.buffered.WriteByte(lf); err != nil { return } } sig := new(packet.Signature) sig.SigType = packet.SigTypeText sig.PubKeyAlgo = d.privateKey.PubKeyAlgo sig.Hash = d.hashType sig.CreationTime = d.config.Now() sig.IssuerKeyId = &d.privateKey.KeyId if err = sig.Sign(d.h, d.privateKey, d.config); err != nil { return } out, err := armor.Encode(d.buffered, "PGP SIGNATURE", nil) if err != nil { return } if err = sig.Serialize(out); err != nil { return } if err = out.Close(); err != nil { return } if err = d.buffered.Flush(); err != nil { return } return } // Encode returns a WriteCloser which will clear-sign a message with privateKey // and write it to w. If config is nil, sensible defaults are used. func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (plaintext io.WriteCloser, err error) { if privateKey.Encrypted { return nil, errors.InvalidArgumentError("signing key is encrypted") } hashType := config.Hash() name := nameOfHash(hashType) if len(name) == 0 { return nil, errors.UnsupportedError("unknown hash type: " + strconv.Itoa(int(hashType))) } h := hashType.New() if h == nil { return nil, errors.UnsupportedError("unsupported hash type: " + strconv.Itoa(int(hashType))) } buffered := bufio.NewWriter(w) // start has a \n at the beginning that we don't want here. if _, err = buffered.Write(start[1:]); err != nil { return } if err = buffered.WriteByte(lf); err != nil { return } if _, err = buffered.WriteString("Hash: "); err != nil { return } if _, err = buffered.WriteString(name); err != nil { return } if err = buffered.WriteByte(lf); err != nil { return } if err = buffered.WriteByte(lf); err != nil { return } plaintext = &dashEscaper{ buffered: buffered, h: h, hashType: hashType, atBeginningOfLine: true, isFirstLine: true, byteBuf: make([]byte, 1), privateKey: privateKey, config: config, } return } // nameOfHash returns the OpenPGP name for the given hash, or the empty string // if the name isn't known. See RFC 4880, section 9.4. func nameOfHash(h crypto.Hash) string { switch h { case crypto.MD5: return "MD5" case crypto.SHA1: return "SHA1" case crypto.RIPEMD160: return "RIPEMD160" case crypto.SHA224: return "SHA224" case crypto.SHA256: return "SHA256" case crypto.SHA384: return "SHA384" case crypto.SHA512: return "SHA512" } return "" } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/clearsign/clearsign_test.go����������������0000644�0000153�0000161�00000012311�12321735647�030342� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 clearsign import ( "bytes" "code.google.com/p/go.crypto/openpgp" "testing" ) func TestParse(t *testing.T) { b, rest := Decode(clearsignInput) if b == nil { t.Fatal("failed to decode clearsign message") } if !bytes.Equal(rest, []byte("trailing")) { t.Errorf("unexpected remaining bytes returned: %s", string(rest)) } if b.ArmoredSignature.Type != "PGP SIGNATURE" { t.Errorf("bad armor type, got:%s, want:PGP SIGNATURE", b.ArmoredSignature.Type) } expected := []byte("Hello world\r\nline 2") if !bytes.Equal(b.Bytes, expected) { t.Errorf("bad body, got:%x want:%x", b.Bytes, expected) } expected = []byte("Hello world\nline 2\n") if !bytes.Equal(b.Plaintext, expected) { t.Errorf("bad plaintext, got:%x want:%x", b.Plaintext, expected) } keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(signingKey)) if err != nil { t.Errorf("failed to parse public key: %s", err) } if _, err := openpgp.CheckDetachedSignature(keyring, bytes.NewBuffer(b.Bytes), b.ArmoredSignature.Body); err != nil { t.Errorf("failed to check signature: %s", err) } } func TestParseWithNoNewlineAtEnd(t *testing.T) { input := clearsignInput input = input[:len(input)-len("trailing")-1] b, rest := Decode(input) if b == nil { t.Fatal("failed to decode clearsign message") } if len(rest) > 0 { t.Errorf("unexpected remaining bytes returned: %s", string(rest)) } } var signingTests = []struct { in, signed, plaintext string }{ {"", "", ""}, {"a", "a", "a\n"}, {"a\n", "a", "a\n"}, {"-a\n", "-a", "-a\n"}, {"--a\nb", "--a\r\nb", "--a\nb\n"}, } func TestSigning(t *testing.T) { keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(signingKey)) if err != nil { t.Errorf("failed to parse public key: %s", err) } for i, test := range signingTests { var buf bytes.Buffer plaintext, err := Encode(&buf, keyring[0].PrivateKey, nil) if err != nil { t.Errorf("#%d: error from Encode: %s", i, err) continue } if _, err := plaintext.Write([]byte(test.in)); err != nil { t.Errorf("#%d: error from Write: %s", i, err) continue } if err := plaintext.Close(); err != nil { t.Fatalf("#%d: error from Close: %s", i, err) continue } b, _ := Decode(buf.Bytes()) if b == nil { t.Errorf("#%d: failed to decode clearsign message", i) continue } if !bytes.Equal(b.Bytes, []byte(test.signed)) { t.Errorf("#%d: bad result, got:%x, want:%x", i, b.Bytes, test.signed) continue } if !bytes.Equal(b.Plaintext, []byte(test.plaintext)) { t.Errorf("#%d: bad result, got:%x, want:%x", i, b.Plaintext, test.plaintext) continue } if _, err := openpgp.CheckDetachedSignature(keyring, bytes.NewBuffer(b.Bytes), b.ArmoredSignature.Body); err != nil { t.Errorf("#%d: failed to check signature: %s", i, err) } } } var clearsignInput = []byte(` ;lasjlkfdsa -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hello world line 2 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) iJwEAQECAAYFAk8kMuEACgkQO9o98PRieSpMsAQAhmY/vwmNpflrPgmfWsYhk5O8 pjnBUzZwqTDoDeINjZEoPDSpQAHGhjFjgaDx/Gj4fAl0dM4D0wuUEBb6QOrwflog 2A2k9kfSOMOtk0IH/H5VuFN1Mie9L/erYXjTQIptv9t9J7NoRBMU0QOOaFU0JaO9 MyTpno24AjIAGb+mH1U= =hIJ6 -----END PGP SIGNATURE----- trailing`) var signingKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux) lQHYBE2rFNoBBADFwqWQIW/DSqcB4yCQqnAFTJ27qS5AnB46ccAdw3u4Greeu3Bp idpoHdjULy7zSKlwR1EA873dO/k/e11Ml3dlAFUinWeejWaK2ugFP6JjiieSsrKn vWNicdCS4HTWn0X4sjl0ZiAygw6GNhqEQ3cpLeL0g8E9hnYzJKQ0LWJa0QARAQAB AAP/TB81EIo2VYNmTq0pK1ZXwUpxCrvAAIG3hwKjEzHcbQznsjNvPUihZ+NZQ6+X 0HCfPAdPkGDCLCb6NavcSW+iNnLTrdDnSI6+3BbIONqWWdRDYJhqZCkqmG6zqSfL IdkJgCw94taUg5BWP/AAeQrhzjChvpMQTVKQL5mnuZbUCeMCAN5qrYMP2S9iKdnk VANIFj7656ARKt/nf4CBzxcpHTyB8+d2CtPDKCmlJP6vL8t58Jmih+kHJMvC0dzn gr5f5+sCAOOe5gt9e0am7AvQWhdbHVfJU0TQJx+m2OiCJAqGTB1nvtBLHdJnfdC9 TnXXQ6ZXibqLyBies/xeY2sCKL5qtTMCAKnX9+9d/5yQxRyrQUHt1NYhaXZnJbHx q4ytu0eWz+5i68IYUSK69jJ1NWPM0T6SkqpB3KCAIv68VFm9PxqG1KmhSrQIVGVz dCBLZXmIuAQTAQIAIgUCTasU2gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA CgkQO9o98PRieSoLhgQAkLEZex02Qt7vGhZzMwuN0R22w3VwyYyjBx+fM3JFETy1 ut4xcLJoJfIaF5ZS38UplgakHG0FQ+b49i8dMij0aZmDqGxrew1m4kBfjXw9B/v+ eIqpODryb6cOSwyQFH0lQkXC040pjq9YqDsO5w0WYNXYKDnzRV0p4H1pweo2VDid AdgETasU2gEEAN46UPeWRqKHvA99arOxee38fBt2CI08iiWyI8T3J6ivtFGixSqV bRcPxYO/qLpVe5l84Nb3X71GfVXlc9hyv7CD6tcowL59hg1E/DC5ydI8K8iEpUmK /UnHdIY5h8/kqgGxkY/T/hgp5fRQgW1ZoZxLajVlMRZ8W4tFtT0DeA+JABEBAAEA A/0bE1jaaZKj6ndqcw86jd+QtD1SF+Cf21CWRNeLKnUds4FRRvclzTyUMuWPkUeX TaNNsUOFqBsf6QQ2oHUBBK4VCHffHCW4ZEX2cd6umz7mpHW6XzN4DECEzOVksXtc lUC1j4UB91DC/RNQqwX1IV2QLSwssVotPMPqhOi0ZLNY7wIA3n7DWKInxYZZ4K+6 rQ+POsz6brEoRHwr8x6XlHenq1Oki855pSa1yXIARoTrSJkBtn5oI+f8AzrnN0BN oyeQAwIA/7E++3HDi5aweWrViiul9cd3rcsS0dEnksPhvS0ozCJiHsq/6GFmy7J8 QSHZPteedBnZyNp5jR+H7cIfVN3KgwH/Skq4PsuPhDq5TKK6i8Pc1WW8MA6DXTdU nLkX7RGmMwjC0DBf7KWAlPjFaONAX3a8ndnz//fy1q7u2l9AZwrj1qa1iJ8EGAEC AAkFAk2rFNoCGwwACgkQO9o98PRieSo2/QP/WTzr4ioINVsvN1akKuekmEMI3LAp BfHwatufxxP1U+3Si/6YIk7kuPB9Hs+pRqCXzbvPRrI8NHZBmc8qIGthishdCYad AHcVnXjtxrULkQFGbGvhKURLvS9WnzD/m1K2zzwxzkPTzT9/Yf06O6Mal5AdugPL VrM0m72/jnpKo04= =zNCn -----END PGP PRIVATE KEY BLOCK----- ` �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/read.go������������������������������������0000644�0000153�0000161�00000031502�12321735647�024303� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 openpgp implements high level operations on OpenPGP messages. package openpgp import ( "code.google.com/p/go.crypto/openpgp/armor" "code.google.com/p/go.crypto/openpgp/errors" "code.google.com/p/go.crypto/openpgp/packet" "crypto" _ "crypto/sha256" "hash" "io" "strconv" ) // SignatureType is the armor type for a PGP signature. var SignatureType = "PGP SIGNATURE" // readArmored reads an armored block with the given type. func readArmored(r io.Reader, expectedType string) (body io.Reader, err error) { block, err := armor.Decode(r) if err != nil { return } if block.Type != expectedType { return nil, errors.InvalidArgumentError("expected '" + expectedType + "', got: " + block.Type) } return block.Body, nil } // MessageDetails contains the result of parsing an OpenPGP encrypted and/or // signed message. type MessageDetails struct { IsEncrypted bool // true if the message was encrypted. EncryptedToKeyIds []uint64 // the list of recipient key ids. IsSymmetricallyEncrypted bool // true if a passphrase could have decrypted the message. DecryptedWith Key // the private key used to decrypt the message, if any. IsSigned bool // true if the message is signed. SignedByKeyId uint64 // the key id of the signer, if any. SignedBy *Key // the key of the signer, if available. LiteralData *packet.LiteralData // the metadata of the contents UnverifiedBody io.Reader // the contents of the message. // If IsSigned is true and SignedBy is non-zero then the signature will // be verified as UnverifiedBody is read. The signature cannot be // checked until the whole of UnverifiedBody is read so UnverifiedBody // must be consumed until EOF before the data can trusted. Even if a // message isn't signed (or the signer is unknown) the data may contain // an authentication code that is only checked once UnverifiedBody has // been consumed. Once EOF has been seen, the following fields are // valid. (An authentication code failure is reported as a // SignatureError error when reading from UnverifiedBody.) SignatureError error // nil if the signature is good. Signature *packet.Signature // the signature packet itself. decrypted io.ReadCloser } // A PromptFunction is used as a callback by functions that may need to decrypt // a private key, or prompt for a passphrase. It is called with a list of // acceptable, encrypted private keys and a boolean that indicates whether a // passphrase is usable. It should either decrypt a private key or return a // passphrase to try. If the decrypted private key or given passphrase isn't // correct, the function will be called again, forever. Any error returned will // be passed up. type PromptFunction func(keys []Key, symmetric bool) ([]byte, error) // A keyEnvelopePair is used to store a private key with the envelope that // contains a symmetric key, encrypted with that key. type keyEnvelopePair struct { key Key encryptedKey *packet.EncryptedKey } // ReadMessage parses an OpenPGP message that may be signed and/or encrypted. // The given KeyRing should contain both public keys (for signature // verification) and, possibly encrypted, private keys for decrypting. // If config is nil, sensible defaults will be used. func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction, config *packet.Config) (md *MessageDetails, err error) { var p packet.Packet var symKeys []*packet.SymmetricKeyEncrypted var pubKeys []keyEnvelopePair var se *packet.SymmetricallyEncrypted packets := packet.NewReader(r) md = new(MessageDetails) md.IsEncrypted = true // The message, if encrypted, starts with a number of packets // containing an encrypted decryption key. The decryption key is either // encrypted to a public key, or with a passphrase. This loop // collects these packets. ParsePackets: for { p, err = packets.Next() if err != nil { return nil, err } switch p := p.(type) { case *packet.SymmetricKeyEncrypted: // This packet contains the decryption key encrypted with a passphrase. md.IsSymmetricallyEncrypted = true symKeys = append(symKeys, p) case *packet.EncryptedKey: // This packet contains the decryption key encrypted to a public key. md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) switch p.Algo { case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal: break default: continue } var keys []Key if p.KeyId == 0 { keys = keyring.DecryptionKeys() } else { keys = keyring.KeysById(p.KeyId) } for _, k := range keys { pubKeys = append(pubKeys, keyEnvelopePair{k, p}) } case *packet.SymmetricallyEncrypted: se = p break ParsePackets case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature: // This message isn't encrypted. if len(symKeys) != 0 || len(pubKeys) != 0 { return nil, errors.StructuralError("key material not followed by encrypted message") } packets.Unread(p) return readSignedMessage(packets, nil, keyring) } } var candidates []Key var decrypted io.ReadCloser // Now that we have the list of encrypted keys we need to decrypt at // least one of them or, if we cannot, we need to call the prompt // function so that it can decrypt a key or give us a passphrase. FindKey: for { // See if any of the keys already have a private key available candidates = candidates[:0] candidateFingerprints := make(map[string]bool) for _, pk := range pubKeys { if pk.key.PrivateKey == nil { continue } if !pk.key.PrivateKey.Encrypted { if len(pk.encryptedKey.Key) == 0 { pk.encryptedKey.Decrypt(pk.key.PrivateKey, config) } if len(pk.encryptedKey.Key) == 0 { continue } decrypted, err = se.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key) if err != nil && err != errors.ErrKeyIncorrect { return nil, err } if decrypted != nil { md.DecryptedWith = pk.key break FindKey } } else { fpr := string(pk.key.PublicKey.Fingerprint[:]) if v := candidateFingerprints[fpr]; v { continue } candidates = append(candidates, pk.key) candidateFingerprints[fpr] = true } } if len(candidates) == 0 && len(symKeys) == 0 { return nil, errors.ErrKeyIncorrect } if prompt == nil { return nil, errors.ErrKeyIncorrect } passphrase, err := prompt(candidates, len(symKeys) != 0) if err != nil { return nil, err } // Try the symmetric passphrase first if len(symKeys) != 0 && passphrase != nil { for _, s := range symKeys { err = s.Decrypt(passphrase) if err == nil && !s.Encrypted { decrypted, err = se.Decrypt(s.CipherFunc, s.Key) if err != nil && err != errors.ErrKeyIncorrect { return nil, err } if decrypted != nil { break FindKey } } } } } md.decrypted = decrypted packets.Push(decrypted) return readSignedMessage(packets, md, keyring) } // readSignedMessage reads a possibly signed message if mdin is non-zero then // that structure is updated and returned. Otherwise a fresh MessageDetails is // used. func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing) (md *MessageDetails, err error) { if mdin == nil { mdin = new(MessageDetails) } md = mdin var p packet.Packet var h hash.Hash var wrappedHash hash.Hash FindLiteralData: for { p, err = packets.Next() if err != nil { return nil, err } switch p := p.(type) { case *packet.Compressed: packets.Push(p.Body) case *packet.OnePassSignature: if !p.IsLast { return nil, errors.UnsupportedError("nested signatures") } h, wrappedHash, err = hashForSignature(p.Hash, p.SigType) if err != nil { md = nil return } md.IsSigned = true md.SignedByKeyId = p.KeyId keys := keyring.KeysById(p.KeyId) for i, key := range keys { if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign { continue } md.SignedBy = &keys[i] break } case *packet.LiteralData: md.LiteralData = p break FindLiteralData } } if md.SignedBy != nil { md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md} } else if md.decrypted != nil { md.UnverifiedBody = checkReader{md} } else { md.UnverifiedBody = md.LiteralData.Body } return md, nil } // hashForSignature returns a pair of hashes that can be used to verify a // signature. The signature may specify that the contents of the signed message // should be preprocessed (i.e. to normalize line endings). Thus this function // returns two hashes. The second should be used to hash the message itself and // performs any needed preprocessing. func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) { h := hashId.New() if h == nil { return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId))) } switch sigType { case packet.SigTypeBinary: return h, h, nil case packet.SigTypeText: return h, NewCanonicalTextHash(h), nil } return nil, nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType))) } // checkReader wraps an io.Reader from a LiteralData packet. When it sees EOF // it closes the ReadCloser from any SymmetricallyEncrypted packet to trigger // MDC checks. type checkReader struct { md *MessageDetails } func (cr checkReader) Read(buf []byte) (n int, err error) { n, err = cr.md.LiteralData.Body.Read(buf) if err == io.EOF { mdcErr := cr.md.decrypted.Close() if mdcErr != nil { err = mdcErr } } return } // signatureCheckReader wraps an io.Reader from a LiteralData packet and hashes // the data as it is read. When it sees an EOF from the underlying io.Reader // it parses and checks a trailing Signature packet and triggers any MDC checks. type signatureCheckReader struct { packets *packet.Reader h, wrappedHash hash.Hash md *MessageDetails } func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) { n, err = scr.md.LiteralData.Body.Read(buf) scr.wrappedHash.Write(buf[:n]) if err == io.EOF { var p packet.Packet p, scr.md.SignatureError = scr.packets.Next() if scr.md.SignatureError != nil { return } var ok bool if scr.md.Signature, ok = p.(*packet.Signature); !ok { scr.md.SignatureError = errors.StructuralError("LiteralData not followed by Signature") return } scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature) // The SymmetricallyEncrypted packet, if any, might have an // unsigned hash of its own. In order to check this we need to // close that Reader. if scr.md.decrypted != nil { mdcErr := scr.md.decrypted.Close() if mdcErr != nil { err = mdcErr } } } return } // CheckDetachedSignature takes a signed file and a detached signature and // returns the signer if the signature is valid. If the signer isn't known, // ErrUnknownIssuer is returned. func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) { p, err := packet.Read(signature) if err != nil { return } var issuerKeyId uint64 var hashFunc crypto.Hash var sigType packet.SignatureType switch sig := p.(type) { case *packet.Signature: if sig.IssuerKeyId == nil { return nil, errors.StructuralError("signature doesn't have an issuer") } issuerKeyId = *sig.IssuerKeyId hashFunc = sig.Hash sigType = sig.SigType case *packet.SignatureV3: issuerKeyId = sig.IssuerKeyId hashFunc = sig.Hash sigType = sig.SigType default: return nil, errors.StructuralError("non signature packet found") } keys := keyring.KeysById(issuerKeyId) if len(keys) == 0 { return nil, errors.ErrUnknownIssuer } h, wrappedHash, err := hashForSignature(hashFunc, sigType) if err != nil { return } _, err = io.Copy(wrappedHash, signed) if err != nil && err != io.EOF { return } for _, key := range keys { if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign { continue } switch sig := p.(type) { case *packet.Signature: err = key.PublicKey.VerifySignature(h, sig) case *packet.SignatureV3: err = key.PublicKey.VerifySignatureV3(h, sig) } if err == nil { return key.Entity, nil } } if err != nil { return } return nil, errors.ErrUnknownIssuer } // CheckArmoredDetachedSignature performs the same actions as // CheckDetachedSignature but expects the signature to be armored. func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) { body, err := readArmored(signature, SignatureType) if err != nil { return } return CheckDetachedSignature(keyring, signed, body) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/errors/������������������������������������0000755�0000153�0000161�00000000000�12321735647�024354� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/errors/errors.go���������������������������0000644�0000153�0000161�00000003210�12321735647�026213� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 errors contains common error types for the OpenPGP packages. package errors import ( "strconv" ) // A StructuralError is returned when OpenPGP data is found to be syntactically // invalid. type StructuralError string func (s StructuralError) Error() string { return "openpgp: invalid data: " + string(s) } // UnsupportedError indicates that, although the OpenPGP data is valid, it // makes use of currently unimplemented features. type UnsupportedError string func (s UnsupportedError) Error() string { return "openpgp: unsupported feature: " + string(s) } // InvalidArgumentError indicates that the caller is in error and passed an // incorrect value. type InvalidArgumentError string func (i InvalidArgumentError) Error() string { return "openpgp: invalid argument: " + string(i) } // SignatureError indicates that a syntactically valid signature failed to // validate. type SignatureError string func (b SignatureError) Error() string { return "openpgp: invalid signature: " + string(b) } type keyIncorrectError int func (ki keyIncorrectError) Error() string { return "openpgp: incorrect key" } var ErrKeyIncorrect error = keyIncorrectError(0) type unknownIssuerError int func (unknownIssuerError) Error() string { return "openpgp: signature made by unknown entity" } var ErrUnknownIssuer error = unknownIssuerError(0) type UnknownPacketTypeError uint8 func (upte UnknownPacketTypeError) Error() string { return "openpgp: unknown packet type: " + strconv.Itoa(int(upte)) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/openpgp/canonical_text_test.go���������������������0000644�0000153�0000161�00000002262�12321735647�027423� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 openpgp import ( "bytes" "testing" ) type recordingHash struct { buf *bytes.Buffer } func (r recordingHash) Write(b []byte) (n int, err error) { return r.buf.Write(b) } func (r recordingHash) Sum(in []byte) []byte { return append(in, r.buf.Bytes()...) } func (r recordingHash) Reset() { panic("shouldn't be called") } func (r recordingHash) Size() int { panic("shouldn't be called") } func (r recordingHash) BlockSize() int { panic("shouldn't be called") } func testCanonicalText(t *testing.T, input, expected string) { r := recordingHash{bytes.NewBuffer(nil)} c := NewCanonicalTextHash(r) c.Write([]byte(input)) result := c.Sum(nil) if expected != string(result) { t.Errorf("input: %x got: %x want: %x", input, result, expected) } } func TestCanonicalText(t *testing.T) { testCanonicalText(t, "foo\n", "foo\r\n") testCanonicalText(t, "foo", "foo") testCanonicalText(t, "foo\r\n", "foo\r\n") testCanonicalText(t, "foo\r\nbar", "foo\r\nbar") testCanonicalText(t, "foo\r\nbar\n\n", "foo\r\nbar\r\n\r\n") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/blowfish/������������������������������������������0000755�0000153�0000161�00000000000�12321736016�023174� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/blowfish/const.go����������������������������������0000644�0000153�0000161�00000031513�12321735647�024665� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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. // The startup permutation array and substitution boxes. // They are the hexadecimal digits of PI; see: // http://www.schneier.com/code/constants.txt. package blowfish var s0 = [256]uint32{ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, } var s1 = [256]uint32{ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, } var s2 = [256]uint32{ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, } var s3 = [256]uint32{ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, } var p = [18]uint32{ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/blowfish/blowfish_test.go��������������������������0000644�0000153�0000161�00000017251�12321736016�026405� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 blowfish import ( "testing" ) type CryptTest struct { key []byte in []byte out []byte } // Test vector values are from http://www.schneier.com/code/vectors.txt. var encryptTests = []CryptTest{ { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}}, { []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, []byte{0x51, 0x86, 0x6F, 0xD5, 0xB8, 0x5E, 0xCB, 0x8A}}, { []byte{0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, []byte{0x7D, 0x85, 0x6F, 0x9A, 0x61, 0x30, 0x63, 0xF2}}, { []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, []byte{0x24, 0x66, 0xDD, 0x87, 0x8B, 0x96, 0x3C, 0x9D}}, { []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, []byte{0x61, 0xF9, 0xC3, 0x80, 0x22, 0x81, 0xB0, 0x96}}, { []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0x7D, 0x0C, 0xC6, 0x30, 0xAF, 0xDA, 0x1E, 0xC7}}, { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}}, { []byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0x0A, 0xCE, 0xAB, 0x0F, 0xC6, 0xA0, 0xA2, 0x8D}}, { []byte{0x7C, 0xA1, 0x10, 0x45, 0x4A, 0x1A, 0x6E, 0x57}, []byte{0x01, 0xA1, 0xD6, 0xD0, 0x39, 0x77, 0x67, 0x42}, []byte{0x59, 0xC6, 0x82, 0x45, 0xEB, 0x05, 0x28, 0x2B}}, { []byte{0x01, 0x31, 0xD9, 0x61, 0x9D, 0xC1, 0x37, 0x6E}, []byte{0x5C, 0xD5, 0x4C, 0xA8, 0x3D, 0xEF, 0x57, 0xDA}, []byte{0xB1, 0xB8, 0xCC, 0x0B, 0x25, 0x0F, 0x09, 0xA0}}, { []byte{0x07, 0xA1, 0x13, 0x3E, 0x4A, 0x0B, 0x26, 0x86}, []byte{0x02, 0x48, 0xD4, 0x38, 0x06, 0xF6, 0x71, 0x72}, []byte{0x17, 0x30, 0xE5, 0x77, 0x8B, 0xEA, 0x1D, 0xA4}}, { []byte{0x38, 0x49, 0x67, 0x4C, 0x26, 0x02, 0x31, 0x9E}, []byte{0x51, 0x45, 0x4B, 0x58, 0x2D, 0xDF, 0x44, 0x0A}, []byte{0xA2, 0x5E, 0x78, 0x56, 0xCF, 0x26, 0x51, 0xEB}}, { []byte{0x04, 0xB9, 0x15, 0xBA, 0x43, 0xFE, 0xB5, 0xB6}, []byte{0x42, 0xFD, 0x44, 0x30, 0x59, 0x57, 0x7F, 0xA2}, []byte{0x35, 0x38, 0x82, 0xB1, 0x09, 0xCE, 0x8F, 0x1A}}, { []byte{0x01, 0x13, 0xB9, 0x70, 0xFD, 0x34, 0xF2, 0xCE}, []byte{0x05, 0x9B, 0x5E, 0x08, 0x51, 0xCF, 0x14, 0x3A}, []byte{0x48, 0xF4, 0xD0, 0x88, 0x4C, 0x37, 0x99, 0x18}}, { []byte{0x01, 0x70, 0xF1, 0x75, 0x46, 0x8F, 0xB5, 0xE6}, []byte{0x07, 0x56, 0xD8, 0xE0, 0x77, 0x47, 0x61, 0xD2}, []byte{0x43, 0x21, 0x93, 0xB7, 0x89, 0x51, 0xFC, 0x98}}, { []byte{0x43, 0x29, 0x7F, 0xAD, 0x38, 0xE3, 0x73, 0xFE}, []byte{0x76, 0x25, 0x14, 0xB8, 0x29, 0xBF, 0x48, 0x6A}, []byte{0x13, 0xF0, 0x41, 0x54, 0xD6, 0x9D, 0x1A, 0xE5}}, { []byte{0x07, 0xA7, 0x13, 0x70, 0x45, 0xDA, 0x2A, 0x16}, []byte{0x3B, 0xDD, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02}, []byte{0x2E, 0xED, 0xDA, 0x93, 0xFF, 0xD3, 0x9C, 0x79}}, { []byte{0x04, 0x68, 0x91, 0x04, 0xC2, 0xFD, 0x3B, 0x2F}, []byte{0x26, 0x95, 0x5F, 0x68, 0x35, 0xAF, 0x60, 0x9A}, []byte{0xD8, 0x87, 0xE0, 0x39, 0x3C, 0x2D, 0xA6, 0xE3}}, { []byte{0x37, 0xD0, 0x6B, 0xB5, 0x16, 0xCB, 0x75, 0x46}, []byte{0x16, 0x4D, 0x5E, 0x40, 0x4F, 0x27, 0x52, 0x32}, []byte{0x5F, 0x99, 0xD0, 0x4F, 0x5B, 0x16, 0x39, 0x69}}, { []byte{0x1F, 0x08, 0x26, 0x0D, 0x1A, 0xC2, 0x46, 0x5E}, []byte{0x6B, 0x05, 0x6E, 0x18, 0x75, 0x9F, 0x5C, 0xCA}, []byte{0x4A, 0x05, 0x7A, 0x3B, 0x24, 0xD3, 0x97, 0x7B}}, { []byte{0x58, 0x40, 0x23, 0x64, 0x1A, 0xBA, 0x61, 0x76}, []byte{0x00, 0x4B, 0xD6, 0xEF, 0x09, 0x17, 0x60, 0x62}, []byte{0x45, 0x20, 0x31, 0xC1, 0xE4, 0xFA, 0xDA, 0x8E}}, { []byte{0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xB0, 0x07}, []byte{0x48, 0x0D, 0x39, 0x00, 0x6E, 0xE7, 0x62, 0xF2}, []byte{0x75, 0x55, 0xAE, 0x39, 0xF5, 0x9B, 0x87, 0xBD}}, { []byte{0x49, 0x79, 0x3E, 0xBC, 0x79, 0xB3, 0x25, 0x8F}, []byte{0x43, 0x75, 0x40, 0xC8, 0x69, 0x8F, 0x3C, 0xFA}, []byte{0x53, 0xC5, 0x5F, 0x9C, 0xB4, 0x9F, 0xC0, 0x19}}, { []byte{0x4F, 0xB0, 0x5E, 0x15, 0x15, 0xAB, 0x73, 0xA7}, []byte{0x07, 0x2D, 0x43, 0xA0, 0x77, 0x07, 0x52, 0x92}, []byte{0x7A, 0x8E, 0x7B, 0xFA, 0x93, 0x7E, 0x89, 0xA3}}, { []byte{0x49, 0xE9, 0x5D, 0x6D, 0x4C, 0xA2, 0x29, 0xBF}, []byte{0x02, 0xFE, 0x55, 0x77, 0x81, 0x17, 0xF1, 0x2A}, []byte{0xCF, 0x9C, 0x5D, 0x7A, 0x49, 0x86, 0xAD, 0xB5}}, { []byte{0x01, 0x83, 0x10, 0xDC, 0x40, 0x9B, 0x26, 0xD6}, []byte{0x1D, 0x9D, 0x5C, 0x50, 0x18, 0xF7, 0x28, 0xC2}, []byte{0xD1, 0xAB, 0xB2, 0x90, 0x65, 0x8B, 0xC7, 0x78}}, { []byte{0x1C, 0x58, 0x7F, 0x1C, 0x13, 0x92, 0x4F, 0xEF}, []byte{0x30, 0x55, 0x32, 0x28, 0x6D, 0x6F, 0x29, 0x5A}, []byte{0x55, 0xCB, 0x37, 0x74, 0xD1, 0x3E, 0xF2, 0x01}}, { []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0xFA, 0x34, 0xEC, 0x48, 0x47, 0xB2, 0x68, 0xB2}}, { []byte{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E}, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0xA7, 0x90, 0x79, 0x51, 0x08, 0xEA, 0x3C, 0xAE}}, { []byte{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE}, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0xC3, 0x9E, 0x07, 0x2D, 0x9F, 0xAC, 0x63, 0x1D}}, { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, []byte{0x01, 0x49, 0x33, 0xE0, 0xCD, 0xAF, 0xF6, 0xE4}}, { []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0xF2, 0x1E, 0x9A, 0x77, 0xB7, 0x1C, 0x49, 0xBC}}, { []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x24, 0x59, 0x46, 0x88, 0x57, 0x54, 0x36, 0x9A}}, { []byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, []byte{0x6B, 0x5C, 0x5A, 0x9C, 0x5D, 0x9E, 0x0A, 0x5A}}, } func TestCipherEncrypt(t *testing.T) { for i, tt := range encryptTests { c, err := NewCipher(tt.key) if err != nil { t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err) continue } ct := make([]byte, len(tt.out)) c.Encrypt(ct, tt.in) for j, v := range ct { if v != tt.out[j] { t.Errorf("Cipher.Encrypt, test vector #%d: cipher-text[%d] = %#x, expected %#x", i, j, v, tt.out[j]) break } } } } func TestCipherDecrypt(t *testing.T) { for i, tt := range encryptTests { c, err := NewCipher(tt.key) if err != nil { t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err) continue } pt := make([]byte, len(tt.in)) c.Decrypt(pt, tt.out) for j, v := range pt { if v != tt.in[j] { t.Errorf("Cipher.Decrypt, test vector #%d: plain-text[%d] = %#x, expected %#x", i, j, v, tt.in[j]) break } } } } func TestSaltedCipherKeyLength(t *testing.T) { var key []byte for i := 0; i < 4; i++ { _, err := NewSaltedCipher(key, []byte{'a'}) if err != KeySizeError(i) { t.Errorf("NewSaltedCipher with short key, gave error %#v, expected %#v", err, KeySizeError(i)) } key = append(key, 'a') } // A 57-byte key. One over the typical blowfish restriction. key = []byte("012345678901234567890123456789012345678901234567890123456") _, err := NewSaltedCipher(key, []byte{'a'}) if err != nil { t.Errorf("NewSaltedCipher with long key, gave error %#v", err) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/blowfish/cipher.go���������������������������������0000644�0000153�0000161�00000006007�12321736016�025000� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 blowfish implements Bruce Schneier's Blowfish encryption algorithm. package blowfish // The code is a port of Bruce Schneier's C implementation. // See http://www.schneier.com/blowfish.html. import "strconv" // The Blowfish block size in bytes. const BlockSize = 8 // A Cipher is an instance of Blowfish encryption using a particular key. type Cipher struct { p [18]uint32 s0, s1, s2, s3 [256]uint32 } type KeySizeError int func (k KeySizeError) Error() string { return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k)) } // NewCipher creates and returns a Cipher. // The key argument should be the Blowfish key, 4 to 56 bytes. func NewCipher(key []byte) (*Cipher, error) { var result Cipher k := len(key) if k < 4 || k > 56 { return nil, KeySizeError(k) } initCipher(key, &result) ExpandKey(key, &result) return &result, nil } // NewSaltedCipher creates a returns a Cipher that folds a salt into its key // schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is // sufficient and desirable. For bcrypt compatiblity, the key can be over 56 // bytes. Only the first 16 bytes of salt are used. func NewSaltedCipher(key, salt []byte) (*Cipher, error) { var result Cipher k := len(key) if k < 4 { return nil, KeySizeError(k) } initCipher(key, &result) expandKeyWithSalt(key, salt, &result) return &result, nil } // BlockSize returns the Blowfish block size, 8 bytes. // It is necessary to satisfy the Block interface in the // package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } // Encrypt encrypts the 8-byte buffer src using the key k // and stores the result in dst. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; // instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) l, r = encryptBlock(l, r, c) dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) } // Decrypt decrypts the 8-byte buffer src using the key k // and stores the result in dst. func (c *Cipher) Decrypt(dst, src []byte) { l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) l, r = decryptBlock(l, r, c) dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) } func initCipher(key []byte, c *Cipher) { copy(c.p[0:], p[0:]) copy(c.s0[0:], s0[0:]) copy(c.s1[0:], s1[0:]) copy(c.s2[0:], s2[0:]) copy(c.s3[0:], s3[0:]) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/blowfish/block.go����������������������������������0000644�0000153�0000161�00000014600�12321735647�024627� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 blowfish // ExpandKey performs a key expansion on the given *Cipher. Specifically, it // performs the Blowfish algorithm's key schedule which sets up the *Cipher's // pi and substitution tables for calls to Encrypt. This is used, primarily, // by the bcrypt package to reuse the Blowfish key schedule during its // set up. It's unlikely that you need to use this directly. func ExpandKey(key []byte, c *Cipher) { j := 0 for i := 0; i < 18; i++ { var d uint32 for k := 0; k < 4; k++ { d = d<<8 | uint32(key[j]) j++ if j >= len(key) { j = 0 } } c.p[i] ^= d } var l, r uint32 for i := 0; i < 18; i += 2 { l, r = encryptBlock(l, r, c) c.p[i], c.p[i+1] = l, r } for i := 0; i < 256; i += 2 { l, r = encryptBlock(l, r, c) c.s0[i], c.s0[i+1] = l, r } for i := 0; i < 256; i += 2 { l, r = encryptBlock(l, r, c) c.s1[i], c.s1[i+1] = l, r } for i := 0; i < 256; i += 2 { l, r = encryptBlock(l, r, c) c.s2[i], c.s2[i+1] = l, r } for i := 0; i < 256; i += 2 { l, r = encryptBlock(l, r, c) c.s3[i], c.s3[i+1] = l, r } } // This is similar to ExpandKey, but folds the salt during the key // schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero // salt passed in, reusing ExpandKey turns out to be a place of inefficiency // and specializing it here is useful. func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) { j := 0 for i := 0; i < 18; i++ { var d uint32 for k := 0; k < 4; k++ { d = d<<8 | uint32(key[j]) j++ if j >= len(key) { j = 0 } } c.p[i] ^= d } j = 0 var expandedSalt [4]uint32 for i := range expandedSalt { var d uint32 for k := 0; k < 4; k++ { d = d<<8 | uint32(salt[j]) j++ if j >= len(salt) { j = 0 } } expandedSalt[i] = d } var l, r uint32 for i := 0; i < 18; i += 2 { l ^= expandedSalt[i&2] r ^= expandedSalt[(i&2)+1] l, r = encryptBlock(l, r, c) c.p[i], c.p[i+1] = l, r } for i := 0; i < 256; i += 4 { l ^= expandedSalt[2] r ^= expandedSalt[3] l, r = encryptBlock(l, r, c) c.s0[i], c.s0[i+1] = l, r l ^= expandedSalt[0] r ^= expandedSalt[1] l, r = encryptBlock(l, r, c) c.s0[i+2], c.s0[i+3] = l, r } for i := 0; i < 256; i += 4 { l ^= expandedSalt[2] r ^= expandedSalt[3] l, r = encryptBlock(l, r, c) c.s1[i], c.s1[i+1] = l, r l ^= expandedSalt[0] r ^= expandedSalt[1] l, r = encryptBlock(l, r, c) c.s1[i+2], c.s1[i+3] = l, r } for i := 0; i < 256; i += 4 { l ^= expandedSalt[2] r ^= expandedSalt[3] l, r = encryptBlock(l, r, c) c.s2[i], c.s2[i+1] = l, r l ^= expandedSalt[0] r ^= expandedSalt[1] l, r = encryptBlock(l, r, c) c.s2[i+2], c.s2[i+3] = l, r } for i := 0; i < 256; i += 4 { l ^= expandedSalt[2] r ^= expandedSalt[3] l, r = encryptBlock(l, r, c) c.s3[i], c.s3[i+1] = l, r l ^= expandedSalt[0] r ^= expandedSalt[1] l, r = encryptBlock(l, r, c) c.s3[i+2], c.s3[i+3] = l, r } } func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { xl, xr := l, r xl ^= c.p[0] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16] xr ^= c.p[17] return xr, xl } func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { xl, xr := l, r xl ^= c.p[17] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3] xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2] xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1] xr ^= c.p[0] return xr, xl } func zero(x []uint32) { for i := range x { x[i] = 0 } } ��������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/����������������������������������������0000755�0000153�0000161�00000000000�12321735647�023122� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/ladderstep_amd64.s����������������������0000644�0000153�0000161�00000047706�12321735647�026446� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // +build amd64,!gccgo // func ladderstep(inout *[5][5]uint64) TEXT ·ladderstep(SB),0,$384-8 MOVQ inout+0(FP),DI MOVQ SP,R11 MOVQ $31,CX NOTQ CX ANDQ CX,SP ADDQ $32,SP MOVQ R11,0(SP) MOVQ R12,8(SP) MOVQ R13,16(SP) MOVQ R14,24(SP) MOVQ R15,32(SP) MOVQ BX,40(SP) MOVQ BP,48(SP) MOVQ 40(DI),SI MOVQ 48(DI),DX MOVQ 56(DI),CX MOVQ 64(DI),R8 MOVQ 72(DI),R9 MOVQ SI,AX MOVQ DX,R10 MOVQ CX,R11 MOVQ R8,R12 MOVQ R9,R13 ADDQ ·_2P0(SB),AX ADDQ ·_2P1234(SB),R10 ADDQ ·_2P1234(SB),R11 ADDQ ·_2P1234(SB),R12 ADDQ ·_2P1234(SB),R13 ADDQ 80(DI),SI ADDQ 88(DI),DX ADDQ 96(DI),CX ADDQ 104(DI),R8 ADDQ 112(DI),R9 SUBQ 80(DI),AX SUBQ 88(DI),R10 SUBQ 96(DI),R11 SUBQ 104(DI),R12 SUBQ 112(DI),R13 MOVQ SI,56(SP) MOVQ DX,64(SP) MOVQ CX,72(SP) MOVQ R8,80(SP) MOVQ R9,88(SP) MOVQ AX,96(SP) MOVQ R10,104(SP) MOVQ R11,112(SP) MOVQ R12,120(SP) MOVQ R13,128(SP) MOVQ 96(SP),AX MULQ 96(SP) MOVQ AX,SI MOVQ DX,CX MOVQ 96(SP),AX SHLQ $1,AX MULQ 104(SP) MOVQ AX,R8 MOVQ DX,R9 MOVQ 96(SP),AX SHLQ $1,AX MULQ 112(SP) MOVQ AX,R10 MOVQ DX,R11 MOVQ 96(SP),AX SHLQ $1,AX MULQ 120(SP) MOVQ AX,R12 MOVQ DX,R13 MOVQ 96(SP),AX SHLQ $1,AX MULQ 128(SP) MOVQ AX,R14 MOVQ DX,R15 MOVQ 104(SP),AX MULQ 104(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 104(SP),AX SHLQ $1,AX MULQ 112(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 104(SP),AX SHLQ $1,AX MULQ 120(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 104(SP),DX IMUL3Q $38,DX,AX MULQ 128(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 112(SP),AX MULQ 112(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 112(SP),DX IMUL3Q $38,DX,AX MULQ 120(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 112(SP),DX IMUL3Q $38,DX,AX MULQ 128(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 120(SP),DX IMUL3Q $19,DX,AX MULQ 120(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 120(SP),DX IMUL3Q $38,DX,AX MULQ 128(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 128(SP),DX IMUL3Q $19,DX,AX MULQ 128(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ ·REDMASK51(SB),DX SHLQ $13,CX:SI ANDQ DX,SI SHLQ $13,R9:R8 ANDQ DX,R8 ADDQ CX,R8 SHLQ $13,R11:R10 ANDQ DX,R10 ADDQ R9,R10 SHLQ $13,R13:R12 ANDQ DX,R12 ADDQ R11,R12 SHLQ $13,R15:R14 ANDQ DX,R14 ADDQ R13,R14 IMUL3Q $19,R15,CX ADDQ CX,SI MOVQ SI,CX SHRQ $51,CX ADDQ R8,CX ANDQ DX,SI MOVQ CX,R8 SHRQ $51,CX ADDQ R10,CX ANDQ DX,R8 MOVQ CX,R9 SHRQ $51,CX ADDQ R12,CX ANDQ DX,R9 MOVQ CX,AX SHRQ $51,CX ADDQ R14,CX ANDQ DX,AX MOVQ CX,R10 SHRQ $51,CX IMUL3Q $19,CX,CX ADDQ CX,SI ANDQ DX,R10 MOVQ SI,136(SP) MOVQ R8,144(SP) MOVQ R9,152(SP) MOVQ AX,160(SP) MOVQ R10,168(SP) MOVQ 56(SP),AX MULQ 56(SP) MOVQ AX,SI MOVQ DX,CX MOVQ 56(SP),AX SHLQ $1,AX MULQ 64(SP) MOVQ AX,R8 MOVQ DX,R9 MOVQ 56(SP),AX SHLQ $1,AX MULQ 72(SP) MOVQ AX,R10 MOVQ DX,R11 MOVQ 56(SP),AX SHLQ $1,AX MULQ 80(SP) MOVQ AX,R12 MOVQ DX,R13 MOVQ 56(SP),AX SHLQ $1,AX MULQ 88(SP) MOVQ AX,R14 MOVQ DX,R15 MOVQ 64(SP),AX MULQ 64(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 64(SP),AX SHLQ $1,AX MULQ 72(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 64(SP),AX SHLQ $1,AX MULQ 80(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 64(SP),DX IMUL3Q $38,DX,AX MULQ 88(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 72(SP),AX MULQ 72(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 72(SP),DX IMUL3Q $38,DX,AX MULQ 80(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 72(SP),DX IMUL3Q $38,DX,AX MULQ 88(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 80(SP),DX IMUL3Q $19,DX,AX MULQ 80(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 80(SP),DX IMUL3Q $38,DX,AX MULQ 88(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 88(SP),DX IMUL3Q $19,DX,AX MULQ 88(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ ·REDMASK51(SB),DX SHLQ $13,CX:SI ANDQ DX,SI SHLQ $13,R9:R8 ANDQ DX,R8 ADDQ CX,R8 SHLQ $13,R11:R10 ANDQ DX,R10 ADDQ R9,R10 SHLQ $13,R13:R12 ANDQ DX,R12 ADDQ R11,R12 SHLQ $13,R15:R14 ANDQ DX,R14 ADDQ R13,R14 IMUL3Q $19,R15,CX ADDQ CX,SI MOVQ SI,CX SHRQ $51,CX ADDQ R8,CX ANDQ DX,SI MOVQ CX,R8 SHRQ $51,CX ADDQ R10,CX ANDQ DX,R8 MOVQ CX,R9 SHRQ $51,CX ADDQ R12,CX ANDQ DX,R9 MOVQ CX,AX SHRQ $51,CX ADDQ R14,CX ANDQ DX,AX MOVQ CX,R10 SHRQ $51,CX IMUL3Q $19,CX,CX ADDQ CX,SI ANDQ DX,R10 MOVQ SI,176(SP) MOVQ R8,184(SP) MOVQ R9,192(SP) MOVQ AX,200(SP) MOVQ R10,208(SP) MOVQ SI,SI MOVQ R8,DX MOVQ R9,CX MOVQ AX,R8 MOVQ R10,R9 ADDQ ·_2P0(SB),SI ADDQ ·_2P1234(SB),DX ADDQ ·_2P1234(SB),CX ADDQ ·_2P1234(SB),R8 ADDQ ·_2P1234(SB),R9 SUBQ 136(SP),SI SUBQ 144(SP),DX SUBQ 152(SP),CX SUBQ 160(SP),R8 SUBQ 168(SP),R9 MOVQ SI,216(SP) MOVQ DX,224(SP) MOVQ CX,232(SP) MOVQ R8,240(SP) MOVQ R9,248(SP) MOVQ 120(DI),SI MOVQ 128(DI),DX MOVQ 136(DI),CX MOVQ 144(DI),R8 MOVQ 152(DI),R9 MOVQ SI,AX MOVQ DX,R10 MOVQ CX,R11 MOVQ R8,R12 MOVQ R9,R13 ADDQ ·_2P0(SB),AX ADDQ ·_2P1234(SB),R10 ADDQ ·_2P1234(SB),R11 ADDQ ·_2P1234(SB),R12 ADDQ ·_2P1234(SB),R13 ADDQ 160(DI),SI ADDQ 168(DI),DX ADDQ 176(DI),CX ADDQ 184(DI),R8 ADDQ 192(DI),R9 SUBQ 160(DI),AX SUBQ 168(DI),R10 SUBQ 176(DI),R11 SUBQ 184(DI),R12 SUBQ 192(DI),R13 MOVQ SI,256(SP) MOVQ DX,264(SP) MOVQ CX,272(SP) MOVQ R8,280(SP) MOVQ R9,288(SP) MOVQ AX,296(SP) MOVQ R10,304(SP) MOVQ R11,312(SP) MOVQ R12,320(SP) MOVQ R13,328(SP) MOVQ 280(SP),SI IMUL3Q $19,SI,AX MOVQ AX,336(SP) MULQ 112(SP) MOVQ AX,SI MOVQ DX,CX MOVQ 288(SP),DX IMUL3Q $19,DX,AX MOVQ AX,344(SP) MULQ 104(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 256(SP),AX MULQ 96(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 256(SP),AX MULQ 104(SP) MOVQ AX,R8 MOVQ DX,R9 MOVQ 256(SP),AX MULQ 112(SP) MOVQ AX,R10 MOVQ DX,R11 MOVQ 256(SP),AX MULQ 120(SP) MOVQ AX,R12 MOVQ DX,R13 MOVQ 256(SP),AX MULQ 128(SP) MOVQ AX,R14 MOVQ DX,R15 MOVQ 264(SP),AX MULQ 96(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 264(SP),AX MULQ 104(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 264(SP),AX MULQ 112(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 264(SP),AX MULQ 120(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 264(SP),DX IMUL3Q $19,DX,AX MULQ 128(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 272(SP),AX MULQ 96(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 272(SP),AX MULQ 104(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 272(SP),AX MULQ 112(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 272(SP),DX IMUL3Q $19,DX,AX MULQ 120(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 272(SP),DX IMUL3Q $19,DX,AX MULQ 128(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 280(SP),AX MULQ 96(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 280(SP),AX MULQ 104(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 336(SP),AX MULQ 120(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 336(SP),AX MULQ 128(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 288(SP),AX MULQ 96(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 344(SP),AX MULQ 112(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 344(SP),AX MULQ 120(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 344(SP),AX MULQ 128(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ ·REDMASK51(SB),DX SHLQ $13,CX:SI ANDQ DX,SI SHLQ $13,R9:R8 ANDQ DX,R8 ADDQ CX,R8 SHLQ $13,R11:R10 ANDQ DX,R10 ADDQ R9,R10 SHLQ $13,R13:R12 ANDQ DX,R12 ADDQ R11,R12 SHLQ $13,R15:R14 ANDQ DX,R14 ADDQ R13,R14 IMUL3Q $19,R15,CX ADDQ CX,SI MOVQ SI,CX SHRQ $51,CX ADDQ R8,CX MOVQ CX,R8 SHRQ $51,CX ANDQ DX,SI ADDQ R10,CX MOVQ CX,R9 SHRQ $51,CX ANDQ DX,R8 ADDQ R12,CX MOVQ CX,AX SHRQ $51,CX ANDQ DX,R9 ADDQ R14,CX MOVQ CX,R10 SHRQ $51,CX ANDQ DX,AX IMUL3Q $19,CX,CX ADDQ CX,SI ANDQ DX,R10 MOVQ SI,96(SP) MOVQ R8,104(SP) MOVQ R9,112(SP) MOVQ AX,120(SP) MOVQ R10,128(SP) MOVQ 320(SP),SI IMUL3Q $19,SI,AX MOVQ AX,256(SP) MULQ 72(SP) MOVQ AX,SI MOVQ DX,CX MOVQ 328(SP),DX IMUL3Q $19,DX,AX MOVQ AX,264(SP) MULQ 64(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 296(SP),AX MULQ 56(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 296(SP),AX MULQ 64(SP) MOVQ AX,R8 MOVQ DX,R9 MOVQ 296(SP),AX MULQ 72(SP) MOVQ AX,R10 MOVQ DX,R11 MOVQ 296(SP),AX MULQ 80(SP) MOVQ AX,R12 MOVQ DX,R13 MOVQ 296(SP),AX MULQ 88(SP) MOVQ AX,R14 MOVQ DX,R15 MOVQ 304(SP),AX MULQ 56(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 304(SP),AX MULQ 64(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 304(SP),AX MULQ 72(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 304(SP),AX MULQ 80(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 304(SP),DX IMUL3Q $19,DX,AX MULQ 88(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 312(SP),AX MULQ 56(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 312(SP),AX MULQ 64(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 312(SP),AX MULQ 72(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 312(SP),DX IMUL3Q $19,DX,AX MULQ 80(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 312(SP),DX IMUL3Q $19,DX,AX MULQ 88(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 320(SP),AX MULQ 56(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 320(SP),AX MULQ 64(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 256(SP),AX MULQ 80(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 256(SP),AX MULQ 88(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 328(SP),AX MULQ 56(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 264(SP),AX MULQ 72(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 264(SP),AX MULQ 80(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 264(SP),AX MULQ 88(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ ·REDMASK51(SB),DX SHLQ $13,CX:SI ANDQ DX,SI SHLQ $13,R9:R8 ANDQ DX,R8 ADDQ CX,R8 SHLQ $13,R11:R10 ANDQ DX,R10 ADDQ R9,R10 SHLQ $13,R13:R12 ANDQ DX,R12 ADDQ R11,R12 SHLQ $13,R15:R14 ANDQ DX,R14 ADDQ R13,R14 IMUL3Q $19,R15,CX ADDQ CX,SI MOVQ SI,CX SHRQ $51,CX ADDQ R8,CX MOVQ CX,R8 SHRQ $51,CX ANDQ DX,SI ADDQ R10,CX MOVQ CX,R9 SHRQ $51,CX ANDQ DX,R8 ADDQ R12,CX MOVQ CX,AX SHRQ $51,CX ANDQ DX,R9 ADDQ R14,CX MOVQ CX,R10 SHRQ $51,CX ANDQ DX,AX IMUL3Q $19,CX,CX ADDQ CX,SI ANDQ DX,R10 MOVQ SI,DX MOVQ R8,CX MOVQ R9,R11 MOVQ AX,R12 MOVQ R10,R13 ADDQ ·_2P0(SB),DX ADDQ ·_2P1234(SB),CX ADDQ ·_2P1234(SB),R11 ADDQ ·_2P1234(SB),R12 ADDQ ·_2P1234(SB),R13 ADDQ 96(SP),SI ADDQ 104(SP),R8 ADDQ 112(SP),R9 ADDQ 120(SP),AX ADDQ 128(SP),R10 SUBQ 96(SP),DX SUBQ 104(SP),CX SUBQ 112(SP),R11 SUBQ 120(SP),R12 SUBQ 128(SP),R13 MOVQ SI,120(DI) MOVQ R8,128(DI) MOVQ R9,136(DI) MOVQ AX,144(DI) MOVQ R10,152(DI) MOVQ DX,160(DI) MOVQ CX,168(DI) MOVQ R11,176(DI) MOVQ R12,184(DI) MOVQ R13,192(DI) MOVQ 120(DI),AX MULQ 120(DI) MOVQ AX,SI MOVQ DX,CX MOVQ 120(DI),AX SHLQ $1,AX MULQ 128(DI) MOVQ AX,R8 MOVQ DX,R9 MOVQ 120(DI),AX SHLQ $1,AX MULQ 136(DI) MOVQ AX,R10 MOVQ DX,R11 MOVQ 120(DI),AX SHLQ $1,AX MULQ 144(DI) MOVQ AX,R12 MOVQ DX,R13 MOVQ 120(DI),AX SHLQ $1,AX MULQ 152(DI) MOVQ AX,R14 MOVQ DX,R15 MOVQ 128(DI),AX MULQ 128(DI) ADDQ AX,R10 ADCQ DX,R11 MOVQ 128(DI),AX SHLQ $1,AX MULQ 136(DI) ADDQ AX,R12 ADCQ DX,R13 MOVQ 128(DI),AX SHLQ $1,AX MULQ 144(DI) ADDQ AX,R14 ADCQ DX,R15 MOVQ 128(DI),DX IMUL3Q $38,DX,AX MULQ 152(DI) ADDQ AX,SI ADCQ DX,CX MOVQ 136(DI),AX MULQ 136(DI) ADDQ AX,R14 ADCQ DX,R15 MOVQ 136(DI),DX IMUL3Q $38,DX,AX MULQ 144(DI) ADDQ AX,SI ADCQ DX,CX MOVQ 136(DI),DX IMUL3Q $38,DX,AX MULQ 152(DI) ADDQ AX,R8 ADCQ DX,R9 MOVQ 144(DI),DX IMUL3Q $19,DX,AX MULQ 144(DI) ADDQ AX,R8 ADCQ DX,R9 MOVQ 144(DI),DX IMUL3Q $38,DX,AX MULQ 152(DI) ADDQ AX,R10 ADCQ DX,R11 MOVQ 152(DI),DX IMUL3Q $19,DX,AX MULQ 152(DI) ADDQ AX,R12 ADCQ DX,R13 MOVQ ·REDMASK51(SB),DX SHLQ $13,CX:SI ANDQ DX,SI SHLQ $13,R9:R8 ANDQ DX,R8 ADDQ CX,R8 SHLQ $13,R11:R10 ANDQ DX,R10 ADDQ R9,R10 SHLQ $13,R13:R12 ANDQ DX,R12 ADDQ R11,R12 SHLQ $13,R15:R14 ANDQ DX,R14 ADDQ R13,R14 IMUL3Q $19,R15,CX ADDQ CX,SI MOVQ SI,CX SHRQ $51,CX ADDQ R8,CX ANDQ DX,SI MOVQ CX,R8 SHRQ $51,CX ADDQ R10,CX ANDQ DX,R8 MOVQ CX,R9 SHRQ $51,CX ADDQ R12,CX ANDQ DX,R9 MOVQ CX,AX SHRQ $51,CX ADDQ R14,CX ANDQ DX,AX MOVQ CX,R10 SHRQ $51,CX IMUL3Q $19,CX,CX ADDQ CX,SI ANDQ DX,R10 MOVQ SI,120(DI) MOVQ R8,128(DI) MOVQ R9,136(DI) MOVQ AX,144(DI) MOVQ R10,152(DI) MOVQ 160(DI),AX MULQ 160(DI) MOVQ AX,SI MOVQ DX,CX MOVQ 160(DI),AX SHLQ $1,AX MULQ 168(DI) MOVQ AX,R8 MOVQ DX,R9 MOVQ 160(DI),AX SHLQ $1,AX MULQ 176(DI) MOVQ AX,R10 MOVQ DX,R11 MOVQ 160(DI),AX SHLQ $1,AX MULQ 184(DI) MOVQ AX,R12 MOVQ DX,R13 MOVQ 160(DI),AX SHLQ $1,AX MULQ 192(DI) MOVQ AX,R14 MOVQ DX,R15 MOVQ 168(DI),AX MULQ 168(DI) ADDQ AX,R10 ADCQ DX,R11 MOVQ 168(DI),AX SHLQ $1,AX MULQ 176(DI) ADDQ AX,R12 ADCQ DX,R13 MOVQ 168(DI),AX SHLQ $1,AX MULQ 184(DI) ADDQ AX,R14 ADCQ DX,R15 MOVQ 168(DI),DX IMUL3Q $38,DX,AX MULQ 192(DI) ADDQ AX,SI ADCQ DX,CX MOVQ 176(DI),AX MULQ 176(DI) ADDQ AX,R14 ADCQ DX,R15 MOVQ 176(DI),DX IMUL3Q $38,DX,AX MULQ 184(DI) ADDQ AX,SI ADCQ DX,CX MOVQ 176(DI),DX IMUL3Q $38,DX,AX MULQ 192(DI) ADDQ AX,R8 ADCQ DX,R9 MOVQ 184(DI),DX IMUL3Q $19,DX,AX MULQ 184(DI) ADDQ AX,R8 ADCQ DX,R9 MOVQ 184(DI),DX IMUL3Q $38,DX,AX MULQ 192(DI) ADDQ AX,R10 ADCQ DX,R11 MOVQ 192(DI),DX IMUL3Q $19,DX,AX MULQ 192(DI) ADDQ AX,R12 ADCQ DX,R13 MOVQ ·REDMASK51(SB),DX SHLQ $13,CX:SI ANDQ DX,SI SHLQ $13,R9:R8 ANDQ DX,R8 ADDQ CX,R8 SHLQ $13,R11:R10 ANDQ DX,R10 ADDQ R9,R10 SHLQ $13,R13:R12 ANDQ DX,R12 ADDQ R11,R12 SHLQ $13,R15:R14 ANDQ DX,R14 ADDQ R13,R14 IMUL3Q $19,R15,CX ADDQ CX,SI MOVQ SI,CX SHRQ $51,CX ADDQ R8,CX ANDQ DX,SI MOVQ CX,R8 SHRQ $51,CX ADDQ R10,CX ANDQ DX,R8 MOVQ CX,R9 SHRQ $51,CX ADDQ R12,CX ANDQ DX,R9 MOVQ CX,AX SHRQ $51,CX ADDQ R14,CX ANDQ DX,AX MOVQ CX,R10 SHRQ $51,CX IMUL3Q $19,CX,CX ADDQ CX,SI ANDQ DX,R10 MOVQ SI,160(DI) MOVQ R8,168(DI) MOVQ R9,176(DI) MOVQ AX,184(DI) MOVQ R10,192(DI) MOVQ 184(DI),SI IMUL3Q $19,SI,AX MOVQ AX,56(SP) MULQ 16(DI) MOVQ AX,SI MOVQ DX,CX MOVQ 192(DI),DX IMUL3Q $19,DX,AX MOVQ AX,64(SP) MULQ 8(DI) ADDQ AX,SI ADCQ DX,CX MOVQ 160(DI),AX MULQ 0(DI) ADDQ AX,SI ADCQ DX,CX MOVQ 160(DI),AX MULQ 8(DI) MOVQ AX,R8 MOVQ DX,R9 MOVQ 160(DI),AX MULQ 16(DI) MOVQ AX,R10 MOVQ DX,R11 MOVQ 160(DI),AX MULQ 24(DI) MOVQ AX,R12 MOVQ DX,R13 MOVQ 160(DI),AX MULQ 32(DI) MOVQ AX,R14 MOVQ DX,R15 MOVQ 168(DI),AX MULQ 0(DI) ADDQ AX,R8 ADCQ DX,R9 MOVQ 168(DI),AX MULQ 8(DI) ADDQ AX,R10 ADCQ DX,R11 MOVQ 168(DI),AX MULQ 16(DI) ADDQ AX,R12 ADCQ DX,R13 MOVQ 168(DI),AX MULQ 24(DI) ADDQ AX,R14 ADCQ DX,R15 MOVQ 168(DI),DX IMUL3Q $19,DX,AX MULQ 32(DI) ADDQ AX,SI ADCQ DX,CX MOVQ 176(DI),AX MULQ 0(DI) ADDQ AX,R10 ADCQ DX,R11 MOVQ 176(DI),AX MULQ 8(DI) ADDQ AX,R12 ADCQ DX,R13 MOVQ 176(DI),AX MULQ 16(DI) ADDQ AX,R14 ADCQ DX,R15 MOVQ 176(DI),DX IMUL3Q $19,DX,AX MULQ 24(DI) ADDQ AX,SI ADCQ DX,CX MOVQ 176(DI),DX IMUL3Q $19,DX,AX MULQ 32(DI) ADDQ AX,R8 ADCQ DX,R9 MOVQ 184(DI),AX MULQ 0(DI) ADDQ AX,R12 ADCQ DX,R13 MOVQ 184(DI),AX MULQ 8(DI) ADDQ AX,R14 ADCQ DX,R15 MOVQ 56(SP),AX MULQ 24(DI) ADDQ AX,R8 ADCQ DX,R9 MOVQ 56(SP),AX MULQ 32(DI) ADDQ AX,R10 ADCQ DX,R11 MOVQ 192(DI),AX MULQ 0(DI) ADDQ AX,R14 ADCQ DX,R15 MOVQ 64(SP),AX MULQ 16(DI) ADDQ AX,R8 ADCQ DX,R9 MOVQ 64(SP),AX MULQ 24(DI) ADDQ AX,R10 ADCQ DX,R11 MOVQ 64(SP),AX MULQ 32(DI) ADDQ AX,R12 ADCQ DX,R13 MOVQ ·REDMASK51(SB),DX SHLQ $13,CX:SI ANDQ DX,SI SHLQ $13,R9:R8 ANDQ DX,R8 ADDQ CX,R8 SHLQ $13,R11:R10 ANDQ DX,R10 ADDQ R9,R10 SHLQ $13,R13:R12 ANDQ DX,R12 ADDQ R11,R12 SHLQ $13,R15:R14 ANDQ DX,R14 ADDQ R13,R14 IMUL3Q $19,R15,CX ADDQ CX,SI MOVQ SI,CX SHRQ $51,CX ADDQ R8,CX MOVQ CX,R8 SHRQ $51,CX ANDQ DX,SI ADDQ R10,CX MOVQ CX,R9 SHRQ $51,CX ANDQ DX,R8 ADDQ R12,CX MOVQ CX,AX SHRQ $51,CX ANDQ DX,R9 ADDQ R14,CX MOVQ CX,R10 SHRQ $51,CX ANDQ DX,AX IMUL3Q $19,CX,CX ADDQ CX,SI ANDQ DX,R10 MOVQ SI,160(DI) MOVQ R8,168(DI) MOVQ R9,176(DI) MOVQ AX,184(DI) MOVQ R10,192(DI) MOVQ 200(SP),SI IMUL3Q $19,SI,AX MOVQ AX,56(SP) MULQ 152(SP) MOVQ AX,SI MOVQ DX,CX MOVQ 208(SP),DX IMUL3Q $19,DX,AX MOVQ AX,64(SP) MULQ 144(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 176(SP),AX MULQ 136(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 176(SP),AX MULQ 144(SP) MOVQ AX,R8 MOVQ DX,R9 MOVQ 176(SP),AX MULQ 152(SP) MOVQ AX,R10 MOVQ DX,R11 MOVQ 176(SP),AX MULQ 160(SP) MOVQ AX,R12 MOVQ DX,R13 MOVQ 176(SP),AX MULQ 168(SP) MOVQ AX,R14 MOVQ DX,R15 MOVQ 184(SP),AX MULQ 136(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 184(SP),AX MULQ 144(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 184(SP),AX MULQ 152(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 184(SP),AX MULQ 160(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 184(SP),DX IMUL3Q $19,DX,AX MULQ 168(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 192(SP),AX MULQ 136(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 192(SP),AX MULQ 144(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 192(SP),AX MULQ 152(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 192(SP),DX IMUL3Q $19,DX,AX MULQ 160(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 192(SP),DX IMUL3Q $19,DX,AX MULQ 168(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 200(SP),AX MULQ 136(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 200(SP),AX MULQ 144(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 56(SP),AX MULQ 160(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 56(SP),AX MULQ 168(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 208(SP),AX MULQ 136(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 64(SP),AX MULQ 152(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 64(SP),AX MULQ 160(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 64(SP),AX MULQ 168(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ ·REDMASK51(SB),DX SHLQ $13,CX:SI ANDQ DX,SI SHLQ $13,R9:R8 ANDQ DX,R8 ADDQ CX,R8 SHLQ $13,R11:R10 ANDQ DX,R10 ADDQ R9,R10 SHLQ $13,R13:R12 ANDQ DX,R12 ADDQ R11,R12 SHLQ $13,R15:R14 ANDQ DX,R14 ADDQ R13,R14 IMUL3Q $19,R15,CX ADDQ CX,SI MOVQ SI,CX SHRQ $51,CX ADDQ R8,CX MOVQ CX,R8 SHRQ $51,CX ANDQ DX,SI ADDQ R10,CX MOVQ CX,R9 SHRQ $51,CX ANDQ DX,R8 ADDQ R12,CX MOVQ CX,AX SHRQ $51,CX ANDQ DX,R9 ADDQ R14,CX MOVQ CX,R10 SHRQ $51,CX ANDQ DX,AX IMUL3Q $19,CX,CX ADDQ CX,SI ANDQ DX,R10 MOVQ SI,40(DI) MOVQ R8,48(DI) MOVQ R9,56(DI) MOVQ AX,64(DI) MOVQ R10,72(DI) MOVQ 216(SP),AX MULQ ·_121666_213(SB) SHRQ $13,AX MOVQ AX,SI MOVQ DX,CX MOVQ 224(SP),AX MULQ ·_121666_213(SB) SHRQ $13,AX ADDQ AX,CX MOVQ DX,R8 MOVQ 232(SP),AX MULQ ·_121666_213(SB) SHRQ $13,AX ADDQ AX,R8 MOVQ DX,R9 MOVQ 240(SP),AX MULQ ·_121666_213(SB) SHRQ $13,AX ADDQ AX,R9 MOVQ DX,R10 MOVQ 248(SP),AX MULQ ·_121666_213(SB) SHRQ $13,AX ADDQ AX,R10 IMUL3Q $19,DX,DX ADDQ DX,SI ADDQ 136(SP),SI ADDQ 144(SP),CX ADDQ 152(SP),R8 ADDQ 160(SP),R9 ADDQ 168(SP),R10 MOVQ SI,80(DI) MOVQ CX,88(DI) MOVQ R8,96(DI) MOVQ R9,104(DI) MOVQ R10,112(DI) MOVQ 104(DI),SI IMUL3Q $19,SI,AX MOVQ AX,56(SP) MULQ 232(SP) MOVQ AX,SI MOVQ DX,CX MOVQ 112(DI),DX IMUL3Q $19,DX,AX MOVQ AX,64(SP) MULQ 224(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 80(DI),AX MULQ 216(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 80(DI),AX MULQ 224(SP) MOVQ AX,R8 MOVQ DX,R9 MOVQ 80(DI),AX MULQ 232(SP) MOVQ AX,R10 MOVQ DX,R11 MOVQ 80(DI),AX MULQ 240(SP) MOVQ AX,R12 MOVQ DX,R13 MOVQ 80(DI),AX MULQ 248(SP) MOVQ AX,R14 MOVQ DX,R15 MOVQ 88(DI),AX MULQ 216(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 88(DI),AX MULQ 224(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 88(DI),AX MULQ 232(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 88(DI),AX MULQ 240(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 88(DI),DX IMUL3Q $19,DX,AX MULQ 248(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 96(DI),AX MULQ 216(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 96(DI),AX MULQ 224(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 96(DI),AX MULQ 232(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 96(DI),DX IMUL3Q $19,DX,AX MULQ 240(SP) ADDQ AX,SI ADCQ DX,CX MOVQ 96(DI),DX IMUL3Q $19,DX,AX MULQ 248(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 104(DI),AX MULQ 216(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ 104(DI),AX MULQ 224(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 56(SP),AX MULQ 240(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 56(SP),AX MULQ 248(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 112(DI),AX MULQ 216(SP) ADDQ AX,R14 ADCQ DX,R15 MOVQ 64(SP),AX MULQ 232(SP) ADDQ AX,R8 ADCQ DX,R9 MOVQ 64(SP),AX MULQ 240(SP) ADDQ AX,R10 ADCQ DX,R11 MOVQ 64(SP),AX MULQ 248(SP) ADDQ AX,R12 ADCQ DX,R13 MOVQ ·REDMASK51(SB),DX SHLQ $13,CX:SI ANDQ DX,SI SHLQ $13,R9:R8 ANDQ DX,R8 ADDQ CX,R8 SHLQ $13,R11:R10 ANDQ DX,R10 ADDQ R9,R10 SHLQ $13,R13:R12 ANDQ DX,R12 ADDQ R11,R12 SHLQ $13,R15:R14 ANDQ DX,R14 ADDQ R13,R14 IMUL3Q $19,R15,CX ADDQ CX,SI MOVQ SI,CX SHRQ $51,CX ADDQ R8,CX MOVQ CX,R8 SHRQ $51,CX ANDQ DX,SI ADDQ R10,CX MOVQ CX,R9 SHRQ $51,CX ANDQ DX,R8 ADDQ R12,CX MOVQ CX,AX SHRQ $51,CX ANDQ DX,R9 ADDQ R14,CX MOVQ CX,R10 SHRQ $51,CX ANDQ DX,AX IMUL3Q $19,CX,CX ADDQ CX,SI ANDQ DX,R10 MOVQ SI,80(DI) MOVQ R8,88(DI) MOVQ R9,96(DI) MOVQ AX,104(DI) MOVQ R10,112(DI) MOVQ 0(SP),R11 MOVQ 8(SP),R12 MOVQ 16(SP),R13 MOVQ 24(SP),R14 MOVQ 32(SP),R15 MOVQ 40(SP),BX MOVQ 48(SP),BP MOVQ R11,SP MOVQ DI,AX MOVQ SI,DX RET ����������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/doc.go����������������������������������0000644�0000153�0000161�00000001750�12321735647�024221� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 curve25519 provides an implementation of scalar multiplication on // the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html package curve25519 // basePoint is the x coordinate of the generator of the curve. var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // ScalarMult sets dst to the product in*base where dst and base are the x // coordinates of group points and all values are in little-endian form. func ScalarMult(dst, in, base *[32]byte) { scalarMult(dst, in, base) } // ScalarBaseMult sets dst to the product in*base where dst and base are the x // coordinates of group points, base is the standard generator and all values // are in little-endian form. func ScalarBaseMult(dst, in *[32]byte) { ScalarMult(dst, in, &basePoint) } ������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/cswap_amd64.s���������������������������0000644�0000153�0000161�00000003005�12321735647�025414� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // +build amd64,!gccgo // func cswap(inout *[5]uint64, v uint64) TEXT ·cswap(SB),7,$0 MOVQ inout+0(FP),DI MOVQ v+8(FP),SI CMPQ SI,$1 MOVQ 0(DI),SI MOVQ 80(DI),DX MOVQ 8(DI),CX MOVQ 88(DI),R8 MOVQ SI,R9 CMOVQEQ DX,SI CMOVQEQ R9,DX MOVQ CX,R9 CMOVQEQ R8,CX CMOVQEQ R9,R8 MOVQ SI,0(DI) MOVQ DX,80(DI) MOVQ CX,8(DI) MOVQ R8,88(DI) MOVQ 16(DI),SI MOVQ 96(DI),DX MOVQ 24(DI),CX MOVQ 104(DI),R8 MOVQ SI,R9 CMOVQEQ DX,SI CMOVQEQ R9,DX MOVQ CX,R9 CMOVQEQ R8,CX CMOVQEQ R9,R8 MOVQ SI,16(DI) MOVQ DX,96(DI) MOVQ CX,24(DI) MOVQ R8,104(DI) MOVQ 32(DI),SI MOVQ 112(DI),DX MOVQ 40(DI),CX MOVQ 120(DI),R8 MOVQ SI,R9 CMOVQEQ DX,SI CMOVQEQ R9,DX MOVQ CX,R9 CMOVQEQ R8,CX CMOVQEQ R9,R8 MOVQ SI,32(DI) MOVQ DX,112(DI) MOVQ CX,40(DI) MOVQ R8,120(DI) MOVQ 48(DI),SI MOVQ 128(DI),DX MOVQ 56(DI),CX MOVQ 136(DI),R8 MOVQ SI,R9 CMOVQEQ DX,SI CMOVQEQ R9,DX MOVQ CX,R9 CMOVQEQ R8,CX CMOVQEQ R9,R8 MOVQ SI,48(DI) MOVQ DX,128(DI) MOVQ CX,56(DI) MOVQ R8,136(DI) MOVQ 64(DI),SI MOVQ 144(DI),DX MOVQ 72(DI),CX MOVQ 152(DI),R8 MOVQ SI,R9 CMOVQEQ DX,SI CMOVQEQ R9,DX MOVQ CX,R9 CMOVQEQ R8,CX CMOVQEQ R9,R8 MOVQ SI,64(DI) MOVQ DX,144(DI) MOVQ CX,72(DI) MOVQ R8,152(DI) MOVQ DI,AX MOVQ SI,DX RET ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/mul_amd64.s�����������������������������0000644�0000153�0000161�00000005465�12321735647�025110� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // +build amd64,!gccgo // func mul(dest, a, b *[5]uint64) TEXT ·mul(SB),0,$128-24 MOVQ dest+0(FP), DI MOVQ a+8(FP), SI MOVQ b+16(FP), DX MOVQ SP,R11 MOVQ $31,CX NOTQ CX ANDQ CX,SP ADDQ $32,SP MOVQ R11,0(SP) MOVQ R12,8(SP) MOVQ R13,16(SP) MOVQ R14,24(SP) MOVQ R15,32(SP) MOVQ BX,40(SP) MOVQ BP,48(SP) MOVQ DI,56(SP) MOVQ DX,CX MOVQ 24(SI),DX IMUL3Q $19,DX,AX MOVQ AX,64(SP) MULQ 16(CX) MOVQ AX,R8 MOVQ DX,R9 MOVQ 32(SI),DX IMUL3Q $19,DX,AX MOVQ AX,72(SP) MULQ 8(CX) ADDQ AX,R8 ADCQ DX,R9 MOVQ 0(SI),AX MULQ 0(CX) ADDQ AX,R8 ADCQ DX,R9 MOVQ 0(SI),AX MULQ 8(CX) MOVQ AX,R10 MOVQ DX,R11 MOVQ 0(SI),AX MULQ 16(CX) MOVQ AX,R12 MOVQ DX,R13 MOVQ 0(SI),AX MULQ 24(CX) MOVQ AX,R14 MOVQ DX,R15 MOVQ 0(SI),AX MULQ 32(CX) MOVQ AX,BX MOVQ DX,BP MOVQ 8(SI),AX MULQ 0(CX) ADDQ AX,R10 ADCQ DX,R11 MOVQ 8(SI),AX MULQ 8(CX) ADDQ AX,R12 ADCQ DX,R13 MOVQ 8(SI),AX MULQ 16(CX) ADDQ AX,R14 ADCQ DX,R15 MOVQ 8(SI),AX MULQ 24(CX) ADDQ AX,BX ADCQ DX,BP MOVQ 8(SI),DX IMUL3Q $19,DX,AX MULQ 32(CX) ADDQ AX,R8 ADCQ DX,R9 MOVQ 16(SI),AX MULQ 0(CX) ADDQ AX,R12 ADCQ DX,R13 MOVQ 16(SI),AX MULQ 8(CX) ADDQ AX,R14 ADCQ DX,R15 MOVQ 16(SI),AX MULQ 16(CX) ADDQ AX,BX ADCQ DX,BP MOVQ 16(SI),DX IMUL3Q $19,DX,AX MULQ 24(CX) ADDQ AX,R8 ADCQ DX,R9 MOVQ 16(SI),DX IMUL3Q $19,DX,AX MULQ 32(CX) ADDQ AX,R10 ADCQ DX,R11 MOVQ 24(SI),AX MULQ 0(CX) ADDQ AX,R14 ADCQ DX,R15 MOVQ 24(SI),AX MULQ 8(CX) ADDQ AX,BX ADCQ DX,BP MOVQ 64(SP),AX MULQ 24(CX) ADDQ AX,R10 ADCQ DX,R11 MOVQ 64(SP),AX MULQ 32(CX) ADDQ AX,R12 ADCQ DX,R13 MOVQ 32(SI),AX MULQ 0(CX) ADDQ AX,BX ADCQ DX,BP MOVQ 72(SP),AX MULQ 16(CX) ADDQ AX,R10 ADCQ DX,R11 MOVQ 72(SP),AX MULQ 24(CX) ADDQ AX,R12 ADCQ DX,R13 MOVQ 72(SP),AX MULQ 32(CX) ADDQ AX,R14 ADCQ DX,R15 MOVQ ·REDMASK51(SB),SI SHLQ $13,R9:R8 ANDQ SI,R8 SHLQ $13,R11:R10 ANDQ SI,R10 ADDQ R9,R10 SHLQ $13,R13:R12 ANDQ SI,R12 ADDQ R11,R12 SHLQ $13,R15:R14 ANDQ SI,R14 ADDQ R13,R14 SHLQ $13,BP:BX ANDQ SI,BX ADDQ R15,BX IMUL3Q $19,BP,DX ADDQ DX,R8 MOVQ R8,DX SHRQ $51,DX ADDQ R10,DX MOVQ DX,CX SHRQ $51,DX ANDQ SI,R8 ADDQ R12,DX MOVQ DX,R9 SHRQ $51,DX ANDQ SI,CX ADDQ R14,DX MOVQ DX,AX SHRQ $51,DX ANDQ SI,R9 ADDQ BX,DX MOVQ DX,R10 SHRQ $51,DX ANDQ SI,AX IMUL3Q $19,DX,DX ADDQ DX,R8 ANDQ SI,R10 MOVQ R8,0(DI) MOVQ CX,8(DI) MOVQ R9,16(DI) MOVQ AX,24(DI) MOVQ R10,32(DI) MOVQ 0(SP),R11 MOVQ 8(SP),R12 MOVQ 16(SP),R13 MOVQ 24(SP),R14 MOVQ 32(SP),R15 MOVQ 40(SP),BX MOVQ 48(SP),BP MOVQ R11,SP MOVQ DI,AX MOVQ SI,DX RET �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/curve25519.go���������������������������0000644�0000153�0000161�00000053020�12321735647�025203� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // We have a implementation in amd64 assembly so this code is only run on // non-amd64 platforms. The amd64 assembly does not support gccgo. // +build !amd64 gccgo package curve25519 // This code is a port of the public domain, "ref10" implementation of // curve25519 from SUPERCOP 20130419 by D. J. Bernstein. // fieldElement represents an element of the field GF(2^255 - 19). An element // t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 // t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on // context. type fieldElement [10]int32 func feZero(fe *fieldElement) { for i := range fe { fe[i] = 0 } } func feOne(fe *fieldElement) { feZero(fe) fe[0] = 1 } func feAdd(dst, a, b *fieldElement) { for i := range dst { dst[i] = a[i] + b[i] } } func feSub(dst, a, b *fieldElement) { for i := range dst { dst[i] = a[i] - b[i] } } func feCopy(dst, src *fieldElement) { for i := range dst { dst[i] = src[i] } } // feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0. // // Preconditions: b in {0,1}. func feCSwap(f, g *fieldElement, b int32) { var x fieldElement b = -b for i := range x { x[i] = b & (f[i] ^ g[i]) } for i := range f { f[i] ^= x[i] } for i := range g { g[i] ^= x[i] } } // load3 reads a 24-bit, little-endian value from in. func load3(in []byte) int64 { var r int64 r = int64(in[0]) r |= int64(in[1]) << 8 r |= int64(in[2]) << 16 return r } // load4 reads a 32-bit, little-endian value from in. func load4(in []byte) int64 { var r int64 r = int64(in[0]) r |= int64(in[1]) << 8 r |= int64(in[2]) << 16 r |= int64(in[3]) << 24 return r } func feFromBytes(dst *fieldElement, src *[32]byte) { h0 := load4(src[:]) h1 := load3(src[4:]) << 6 h2 := load3(src[7:]) << 5 h3 := load3(src[10:]) << 3 h4 := load3(src[13:]) << 2 h5 := load4(src[16:]) h6 := load3(src[20:]) << 7 h7 := load3(src[23:]) << 5 h8 := load3(src[26:]) << 4 h9 := load3(src[29:]) << 2 var carry [10]int64 carry[9] = (h9 + 1<<24) >> 25 h0 += carry[9] * 19 h9 -= carry[9] << 25 carry[1] = (h1 + 1<<24) >> 25 h2 += carry[1] h1 -= carry[1] << 25 carry[3] = (h3 + 1<<24) >> 25 h4 += carry[3] h3 -= carry[3] << 25 carry[5] = (h5 + 1<<24) >> 25 h6 += carry[5] h5 -= carry[5] << 25 carry[7] = (h7 + 1<<24) >> 25 h8 += carry[7] h7 -= carry[7] << 25 carry[0] = (h0 + 1<<25) >> 26 h1 += carry[0] h0 -= carry[0] << 26 carry[2] = (h2 + 1<<25) >> 26 h3 += carry[2] h2 -= carry[2] << 26 carry[4] = (h4 + 1<<25) >> 26 h5 += carry[4] h4 -= carry[4] << 26 carry[6] = (h6 + 1<<25) >> 26 h7 += carry[6] h6 -= carry[6] << 26 carry[8] = (h8 + 1<<25) >> 26 h9 += carry[8] h8 -= carry[8] << 26 dst[0] = int32(h0) dst[1] = int32(h1) dst[2] = int32(h2) dst[3] = int32(h3) dst[4] = int32(h4) dst[5] = int32(h5) dst[6] = int32(h6) dst[7] = int32(h7) dst[8] = int32(h8) dst[9] = int32(h9) } // feToBytes marshals h to s. // Preconditions: // |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. // // Write p=2^255-19; q=floor(h/p). // Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). // // Proof: // Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. // Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. // // Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). // Then 0<y<1. // // Write r=h-pq. // Have 0<=r<=p-1=2^255-20. // Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1. // // Write x=r+19(2^-255)r+y. // Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q. // // Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1)) // so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q. func feToBytes(s *[32]byte, h *fieldElement) { var carry [10]int32 q := (19*h[9] + (1 << 24)) >> 25 q = (h[0] + q) >> 26 q = (h[1] + q) >> 25 q = (h[2] + q) >> 26 q = (h[3] + q) >> 25 q = (h[4] + q) >> 26 q = (h[5] + q) >> 25 q = (h[6] + q) >> 26 q = (h[7] + q) >> 25 q = (h[8] + q) >> 26 q = (h[9] + q) >> 25 // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. h[0] += 19 * q // Goal: Output h-2^255 q, which is between 0 and 2^255-20. carry[0] = h[0] >> 26 h[1] += carry[0] h[0] -= carry[0] << 26 carry[1] = h[1] >> 25 h[2] += carry[1] h[1] -= carry[1] << 25 carry[2] = h[2] >> 26 h[3] += carry[2] h[2] -= carry[2] << 26 carry[3] = h[3] >> 25 h[4] += carry[3] h[3] -= carry[3] << 25 carry[4] = h[4] >> 26 h[5] += carry[4] h[4] -= carry[4] << 26 carry[5] = h[5] >> 25 h[6] += carry[5] h[5] -= carry[5] << 25 carry[6] = h[6] >> 26 h[7] += carry[6] h[6] -= carry[6] << 26 carry[7] = h[7] >> 25 h[8] += carry[7] h[7] -= carry[7] << 25 carry[8] = h[8] >> 26 h[9] += carry[8] h[8] -= carry[8] << 26 carry[9] = h[9] >> 25 h[9] -= carry[9] << 25 // h10 = carry9 // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. // Have h[0]+...+2^230 h[9] between 0 and 2^255-1; // evidently 2^255 h10-2^255 q = 0. // Goal: Output h[0]+...+2^230 h[9]. s[0] = byte(h[0] >> 0) s[1] = byte(h[0] >> 8) s[2] = byte(h[0] >> 16) s[3] = byte((h[0] >> 24) | (h[1] << 2)) s[4] = byte(h[1] >> 6) s[5] = byte(h[1] >> 14) s[6] = byte((h[1] >> 22) | (h[2] << 3)) s[7] = byte(h[2] >> 5) s[8] = byte(h[2] >> 13) s[9] = byte((h[2] >> 21) | (h[3] << 5)) s[10] = byte(h[3] >> 3) s[11] = byte(h[3] >> 11) s[12] = byte((h[3] >> 19) | (h[4] << 6)) s[13] = byte(h[4] >> 2) s[14] = byte(h[4] >> 10) s[15] = byte(h[4] >> 18) s[16] = byte(h[5] >> 0) s[17] = byte(h[5] >> 8) s[18] = byte(h[5] >> 16) s[19] = byte((h[5] >> 24) | (h[6] << 1)) s[20] = byte(h[6] >> 7) s[21] = byte(h[6] >> 15) s[22] = byte((h[6] >> 23) | (h[7] << 3)) s[23] = byte(h[7] >> 5) s[24] = byte(h[7] >> 13) s[25] = byte((h[7] >> 21) | (h[8] << 4)) s[26] = byte(h[8] >> 4) s[27] = byte(h[8] >> 12) s[28] = byte((h[8] >> 20) | (h[9] << 6)) s[29] = byte(h[9] >> 2) s[30] = byte(h[9] >> 10) s[31] = byte(h[9] >> 18) } // feMul calculates h = f * g // Can overlap h with f or g. // // Preconditions: // |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. // |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. // // Postconditions: // |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. // // Notes on implementation strategy: // // Using schoolbook multiplication. // Karatsuba would save a little in some cost models. // // Most multiplications by 2 and 19 are 32-bit precomputations; // cheaper than 64-bit postcomputations. // // There is one remaining multiplication by 19 in the carry chain; // one *19 precomputation can be merged into this, // but the resulting data flow is considerably less clean. // // There are 12 carries below. // 10 of them are 2-way parallelizable and vectorizable. // Can get away with 11 carries, but then data flow is much deeper. // // With tighter constraints on inputs can squeeze carries into int32. func feMul(h, f, g *fieldElement) { f0 := f[0] f1 := f[1] f2 := f[2] f3 := f[3] f4 := f[4] f5 := f[5] f6 := f[6] f7 := f[7] f8 := f[8] f9 := f[9] g0 := g[0] g1 := g[1] g2 := g[2] g3 := g[3] g4 := g[4] g5 := g[5] g6 := g[6] g7 := g[7] g8 := g[8] g9 := g[9] g1_19 := 19 * g1 // 1.4*2^29 g2_19 := 19 * g2 // 1.4*2^30; still ok g3_19 := 19 * g3 g4_19 := 19 * g4 g5_19 := 19 * g5 g6_19 := 19 * g6 g7_19 := 19 * g7 g8_19 := 19 * g8 g9_19 := 19 * g9 f1_2 := 2 * f1 f3_2 := 2 * f3 f5_2 := 2 * f5 f7_2 := 2 * f7 f9_2 := 2 * f9 f0g0 := int64(f0) * int64(g0) f0g1 := int64(f0) * int64(g1) f0g2 := int64(f0) * int64(g2) f0g3 := int64(f0) * int64(g3) f0g4 := int64(f0) * int64(g4) f0g5 := int64(f0) * int64(g5) f0g6 := int64(f0) * int64(g6) f0g7 := int64(f0) * int64(g7) f0g8 := int64(f0) * int64(g8) f0g9 := int64(f0) * int64(g9) f1g0 := int64(f1) * int64(g0) f1g1_2 := int64(f1_2) * int64(g1) f1g2 := int64(f1) * int64(g2) f1g3_2 := int64(f1_2) * int64(g3) f1g4 := int64(f1) * int64(g4) f1g5_2 := int64(f1_2) * int64(g5) f1g6 := int64(f1) * int64(g6) f1g7_2 := int64(f1_2) * int64(g7) f1g8 := int64(f1) * int64(g8) f1g9_38 := int64(f1_2) * int64(g9_19) f2g0 := int64(f2) * int64(g0) f2g1 := int64(f2) * int64(g1) f2g2 := int64(f2) * int64(g2) f2g3 := int64(f2) * int64(g3) f2g4 := int64(f2) * int64(g4) f2g5 := int64(f2) * int64(g5) f2g6 := int64(f2) * int64(g6) f2g7 := int64(f2) * int64(g7) f2g8_19 := int64(f2) * int64(g8_19) f2g9_19 := int64(f2) * int64(g9_19) f3g0 := int64(f3) * int64(g0) f3g1_2 := int64(f3_2) * int64(g1) f3g2 := int64(f3) * int64(g2) f3g3_2 := int64(f3_2) * int64(g3) f3g4 := int64(f3) * int64(g4) f3g5_2 := int64(f3_2) * int64(g5) f3g6 := int64(f3) * int64(g6) f3g7_38 := int64(f3_2) * int64(g7_19) f3g8_19 := int64(f3) * int64(g8_19) f3g9_38 := int64(f3_2) * int64(g9_19) f4g0 := int64(f4) * int64(g0) f4g1 := int64(f4) * int64(g1) f4g2 := int64(f4) * int64(g2) f4g3 := int64(f4) * int64(g3) f4g4 := int64(f4) * int64(g4) f4g5 := int64(f4) * int64(g5) f4g6_19 := int64(f4) * int64(g6_19) f4g7_19 := int64(f4) * int64(g7_19) f4g8_19 := int64(f4) * int64(g8_19) f4g9_19 := int64(f4) * int64(g9_19) f5g0 := int64(f5) * int64(g0) f5g1_2 := int64(f5_2) * int64(g1) f5g2 := int64(f5) * int64(g2) f5g3_2 := int64(f5_2) * int64(g3) f5g4 := int64(f5) * int64(g4) f5g5_38 := int64(f5_2) * int64(g5_19) f5g6_19 := int64(f5) * int64(g6_19) f5g7_38 := int64(f5_2) * int64(g7_19) f5g8_19 := int64(f5) * int64(g8_19) f5g9_38 := int64(f5_2) * int64(g9_19) f6g0 := int64(f6) * int64(g0) f6g1 := int64(f6) * int64(g1) f6g2 := int64(f6) * int64(g2) f6g3 := int64(f6) * int64(g3) f6g4_19 := int64(f6) * int64(g4_19) f6g5_19 := int64(f6) * int64(g5_19) f6g6_19 := int64(f6) * int64(g6_19) f6g7_19 := int64(f6) * int64(g7_19) f6g8_19 := int64(f6) * int64(g8_19) f6g9_19 := int64(f6) * int64(g9_19) f7g0 := int64(f7) * int64(g0) f7g1_2 := int64(f7_2) * int64(g1) f7g2 := int64(f7) * int64(g2) f7g3_38 := int64(f7_2) * int64(g3_19) f7g4_19 := int64(f7) * int64(g4_19) f7g5_38 := int64(f7_2) * int64(g5_19) f7g6_19 := int64(f7) * int64(g6_19) f7g7_38 := int64(f7_2) * int64(g7_19) f7g8_19 := int64(f7) * int64(g8_19) f7g9_38 := int64(f7_2) * int64(g9_19) f8g0 := int64(f8) * int64(g0) f8g1 := int64(f8) * int64(g1) f8g2_19 := int64(f8) * int64(g2_19) f8g3_19 := int64(f8) * int64(g3_19) f8g4_19 := int64(f8) * int64(g4_19) f8g5_19 := int64(f8) * int64(g5_19) f8g6_19 := int64(f8) * int64(g6_19) f8g7_19 := int64(f8) * int64(g7_19) f8g8_19 := int64(f8) * int64(g8_19) f8g9_19 := int64(f8) * int64(g9_19) f9g0 := int64(f9) * int64(g0) f9g1_38 := int64(f9_2) * int64(g1_19) f9g2_19 := int64(f9) * int64(g2_19) f9g3_38 := int64(f9_2) * int64(g3_19) f9g4_19 := int64(f9) * int64(g4_19) f9g5_38 := int64(f9_2) * int64(g5_19) f9g6_19 := int64(f9) * int64(g6_19) f9g7_38 := int64(f9_2) * int64(g7_19) f9g8_19 := int64(f9) * int64(g8_19) f9g9_38 := int64(f9_2) * int64(g9_19) h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38 h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19 h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38 h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19 h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38 h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19 h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38 h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19 h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38 h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 var carry [10]int64 // |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) // i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 // |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) // i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 carry[0] = (h0 + (1 << 25)) >> 26 h1 += carry[0] h0 -= carry[0] << 26 carry[4] = (h4 + (1 << 25)) >> 26 h5 += carry[4] h4 -= carry[4] << 26 // |h0| <= 2^25 // |h4| <= 2^25 // |h1| <= 1.51*2^58 // |h5| <= 1.51*2^58 carry[1] = (h1 + (1 << 24)) >> 25 h2 += carry[1] h1 -= carry[1] << 25 carry[5] = (h5 + (1 << 24)) >> 25 h6 += carry[5] h5 -= carry[5] << 25 // |h1| <= 2^24; from now on fits into int32 // |h5| <= 2^24; from now on fits into int32 // |h2| <= 1.21*2^59 // |h6| <= 1.21*2^59 carry[2] = (h2 + (1 << 25)) >> 26 h3 += carry[2] h2 -= carry[2] << 26 carry[6] = (h6 + (1 << 25)) >> 26 h7 += carry[6] h6 -= carry[6] << 26 // |h2| <= 2^25; from now on fits into int32 unchanged // |h6| <= 2^25; from now on fits into int32 unchanged // |h3| <= 1.51*2^58 // |h7| <= 1.51*2^58 carry[3] = (h3 + (1 << 24)) >> 25 h4 += carry[3] h3 -= carry[3] << 25 carry[7] = (h7 + (1 << 24)) >> 25 h8 += carry[7] h7 -= carry[7] << 25 // |h3| <= 2^24; from now on fits into int32 unchanged // |h7| <= 2^24; from now on fits into int32 unchanged // |h4| <= 1.52*2^33 // |h8| <= 1.52*2^33 carry[4] = (h4 + (1 << 25)) >> 26 h5 += carry[4] h4 -= carry[4] << 26 carry[8] = (h8 + (1 << 25)) >> 26 h9 += carry[8] h8 -= carry[8] << 26 // |h4| <= 2^25; from now on fits into int32 unchanged // |h8| <= 2^25; from now on fits into int32 unchanged // |h5| <= 1.01*2^24 // |h9| <= 1.51*2^58 carry[9] = (h9 + (1 << 24)) >> 25 h0 += carry[9] * 19 h9 -= carry[9] << 25 // |h9| <= 2^24; from now on fits into int32 unchanged // |h0| <= 1.8*2^37 carry[0] = (h0 + (1 << 25)) >> 26 h1 += carry[0] h0 -= carry[0] << 26 // |h0| <= 2^25; from now on fits into int32 unchanged // |h1| <= 1.01*2^24 h[0] = int32(h0) h[1] = int32(h1) h[2] = int32(h2) h[3] = int32(h3) h[4] = int32(h4) h[5] = int32(h5) h[6] = int32(h6) h[7] = int32(h7) h[8] = int32(h8) h[9] = int32(h9) } // feSquare calculates h = f*f. Can overlap h with f. // // Preconditions: // |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. // // Postconditions: // |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. func feSquare(h, f *fieldElement) { f0 := f[0] f1 := f[1] f2 := f[2] f3 := f[3] f4 := f[4] f5 := f[5] f6 := f[6] f7 := f[7] f8 := f[8] f9 := f[9] f0_2 := 2 * f0 f1_2 := 2 * f1 f2_2 := 2 * f2 f3_2 := 2 * f3 f4_2 := 2 * f4 f5_2 := 2 * f5 f6_2 := 2 * f6 f7_2 := 2 * f7 f5_38 := 38 * f5 // 1.31*2^30 f6_19 := 19 * f6 // 1.31*2^30 f7_38 := 38 * f7 // 1.31*2^30 f8_19 := 19 * f8 // 1.31*2^30 f9_38 := 38 * f9 // 1.31*2^30 f0f0 := int64(f0) * int64(f0) f0f1_2 := int64(f0_2) * int64(f1) f0f2_2 := int64(f0_2) * int64(f2) f0f3_2 := int64(f0_2) * int64(f3) f0f4_2 := int64(f0_2) * int64(f4) f0f5_2 := int64(f0_2) * int64(f5) f0f6_2 := int64(f0_2) * int64(f6) f0f7_2 := int64(f0_2) * int64(f7) f0f8_2 := int64(f0_2) * int64(f8) f0f9_2 := int64(f0_2) * int64(f9) f1f1_2 := int64(f1_2) * int64(f1) f1f2_2 := int64(f1_2) * int64(f2) f1f3_4 := int64(f1_2) * int64(f3_2) f1f4_2 := int64(f1_2) * int64(f4) f1f5_4 := int64(f1_2) * int64(f5_2) f1f6_2 := int64(f1_2) * int64(f6) f1f7_4 := int64(f1_2) * int64(f7_2) f1f8_2 := int64(f1_2) * int64(f8) f1f9_76 := int64(f1_2) * int64(f9_38) f2f2 := int64(f2) * int64(f2) f2f3_2 := int64(f2_2) * int64(f3) f2f4_2 := int64(f2_2) * int64(f4) f2f5_2 := int64(f2_2) * int64(f5) f2f6_2 := int64(f2_2) * int64(f6) f2f7_2 := int64(f2_2) * int64(f7) f2f8_38 := int64(f2_2) * int64(f8_19) f2f9_38 := int64(f2) * int64(f9_38) f3f3_2 := int64(f3_2) * int64(f3) f3f4_2 := int64(f3_2) * int64(f4) f3f5_4 := int64(f3_2) * int64(f5_2) f3f6_2 := int64(f3_2) * int64(f6) f3f7_76 := int64(f3_2) * int64(f7_38) f3f8_38 := int64(f3_2) * int64(f8_19) f3f9_76 := int64(f3_2) * int64(f9_38) f4f4 := int64(f4) * int64(f4) f4f5_2 := int64(f4_2) * int64(f5) f4f6_38 := int64(f4_2) * int64(f6_19) f4f7_38 := int64(f4) * int64(f7_38) f4f8_38 := int64(f4_2) * int64(f8_19) f4f9_38 := int64(f4) * int64(f9_38) f5f5_38 := int64(f5) * int64(f5_38) f5f6_38 := int64(f5_2) * int64(f6_19) f5f7_76 := int64(f5_2) * int64(f7_38) f5f8_38 := int64(f5_2) * int64(f8_19) f5f9_76 := int64(f5_2) * int64(f9_38) f6f6_19 := int64(f6) * int64(f6_19) f6f7_38 := int64(f6) * int64(f7_38) f6f8_38 := int64(f6_2) * int64(f8_19) f6f9_38 := int64(f6) * int64(f9_38) f7f7_38 := int64(f7) * int64(f7_38) f7f8_38 := int64(f7_2) * int64(f8_19) f7f9_76 := int64(f7_2) * int64(f9_38) f8f8_19 := int64(f8) * int64(f8_19) f8f9_38 := int64(f8) * int64(f9_38) f9f9_38 := int64(f9) * int64(f9_38) h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38 h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38 h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19 h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38 h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38 h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38 h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19 h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38 h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38 h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2 var carry [10]int64 carry[0] = (h0 + (1 << 25)) >> 26 h1 += carry[0] h0 -= carry[0] << 26 carry[4] = (h4 + (1 << 25)) >> 26 h5 += carry[4] h4 -= carry[4] << 26 carry[1] = (h1 + (1 << 24)) >> 25 h2 += carry[1] h1 -= carry[1] << 25 carry[5] = (h5 + (1 << 24)) >> 25 h6 += carry[5] h5 -= carry[5] << 25 carry[2] = (h2 + (1 << 25)) >> 26 h3 += carry[2] h2 -= carry[2] << 26 carry[6] = (h6 + (1 << 25)) >> 26 h7 += carry[6] h6 -= carry[6] << 26 carry[3] = (h3 + (1 << 24)) >> 25 h4 += carry[3] h3 -= carry[3] << 25 carry[7] = (h7 + (1 << 24)) >> 25 h8 += carry[7] h7 -= carry[7] << 25 carry[4] = (h4 + (1 << 25)) >> 26 h5 += carry[4] h4 -= carry[4] << 26 carry[8] = (h8 + (1 << 25)) >> 26 h9 += carry[8] h8 -= carry[8] << 26 carry[9] = (h9 + (1 << 24)) >> 25 h0 += carry[9] * 19 h9 -= carry[9] << 25 carry[0] = (h0 + (1 << 25)) >> 26 h1 += carry[0] h0 -= carry[0] << 26 h[0] = int32(h0) h[1] = int32(h1) h[2] = int32(h2) h[3] = int32(h3) h[4] = int32(h4) h[5] = int32(h5) h[6] = int32(h6) h[7] = int32(h7) h[8] = int32(h8) h[9] = int32(h9) } // feMul121666 calculates h = f * 121666. Can overlap h with f. // // Preconditions: // |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. // // Postconditions: // |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. func feMul121666(h, f *fieldElement) { h0 := int64(f[0]) * 121666 h1 := int64(f[1]) * 121666 h2 := int64(f[2]) * 121666 h3 := int64(f[3]) * 121666 h4 := int64(f[4]) * 121666 h5 := int64(f[5]) * 121666 h6 := int64(f[6]) * 121666 h7 := int64(f[7]) * 121666 h8 := int64(f[8]) * 121666 h9 := int64(f[9]) * 121666 var carry [10]int64 carry[9] = (h9 + (1 << 24)) >> 25 h0 += carry[9] * 19 h9 -= carry[9] << 25 carry[1] = (h1 + (1 << 24)) >> 25 h2 += carry[1] h1 -= carry[1] << 25 carry[3] = (h3 + (1 << 24)) >> 25 h4 += carry[3] h3 -= carry[3] << 25 carry[5] = (h5 + (1 << 24)) >> 25 h6 += carry[5] h5 -= carry[5] << 25 carry[7] = (h7 + (1 << 24)) >> 25 h8 += carry[7] h7 -= carry[7] << 25 carry[0] = (h0 + (1 << 25)) >> 26 h1 += carry[0] h0 -= carry[0] << 26 carry[2] = (h2 + (1 << 25)) >> 26 h3 += carry[2] h2 -= carry[2] << 26 carry[4] = (h4 + (1 << 25)) >> 26 h5 += carry[4] h4 -= carry[4] << 26 carry[6] = (h6 + (1 << 25)) >> 26 h7 += carry[6] h6 -= carry[6] << 26 carry[8] = (h8 + (1 << 25)) >> 26 h9 += carry[8] h8 -= carry[8] << 26 h[0] = int32(h0) h[1] = int32(h1) h[2] = int32(h2) h[3] = int32(h3) h[4] = int32(h4) h[5] = int32(h5) h[6] = int32(h6) h[7] = int32(h7) h[8] = int32(h8) h[9] = int32(h9) } // feInvert sets out = z^-1. func feInvert(out, z *fieldElement) { var t0, t1, t2, t3 fieldElement var i int feSquare(&t0, z) for i = 1; i < 1; i++ { feSquare(&t0, &t0) } feSquare(&t1, &t0) for i = 1; i < 2; i++ { feSquare(&t1, &t1) } feMul(&t1, z, &t1) feMul(&t0, &t0, &t1) feSquare(&t2, &t0) for i = 1; i < 1; i++ { feSquare(&t2, &t2) } feMul(&t1, &t1, &t2) feSquare(&t2, &t1) for i = 1; i < 5; i++ { feSquare(&t2, &t2) } feMul(&t1, &t2, &t1) feSquare(&t2, &t1) for i = 1; i < 10; i++ { feSquare(&t2, &t2) } feMul(&t2, &t2, &t1) feSquare(&t3, &t2) for i = 1; i < 20; i++ { feSquare(&t3, &t3) } feMul(&t2, &t3, &t2) feSquare(&t2, &t2) for i = 1; i < 10; i++ { feSquare(&t2, &t2) } feMul(&t1, &t2, &t1) feSquare(&t2, &t1) for i = 1; i < 50; i++ { feSquare(&t2, &t2) } feMul(&t2, &t2, &t1) feSquare(&t3, &t2) for i = 1; i < 100; i++ { feSquare(&t3, &t3) } feMul(&t2, &t3, &t2) feSquare(&t2, &t2) for i = 1; i < 50; i++ { feSquare(&t2, &t2) } feMul(&t1, &t2, &t1) feSquare(&t1, &t1) for i = 1; i < 5; i++ { feSquare(&t1, &t1) } feMul(out, &t1, &t0) } func scalarMult(out, in, base *[32]byte) { var e [32]byte copy(e[:], in[:]) e[0] &= 248 e[31] &= 127 e[31] |= 64 var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement feFromBytes(&x1, base) feOne(&x2) feCopy(&x3, &x1) feOne(&z3) swap := int32(0) for pos := 254; pos >= 0; pos-- { b := e[pos/8] >> uint(pos&7) b &= 1 swap ^= int32(b) feCSwap(&x2, &x3, swap) feCSwap(&z2, &z3, swap) swap = int32(b) feSub(&tmp0, &x3, &z3) feSub(&tmp1, &x2, &z2) feAdd(&x2, &x2, &z2) feAdd(&z2, &x3, &z3) feMul(&z3, &tmp0, &x2) feMul(&z2, &z2, &tmp1) feSquare(&tmp0, &tmp1) feSquare(&tmp1, &x2) feAdd(&x3, &z3, &z2) feSub(&z2, &z3, &z2) feMul(&x2, &tmp1, &tmp0) feSub(&tmp1, &tmp1, &tmp0) feSquare(&z2, &z2) feMul121666(&z3, &tmp1) feSquare(&x3, &x3) feAdd(&tmp0, &tmp0, &z3) feMul(&z3, &x1, &z2) feMul(&z2, &tmp1, &tmp0) } feCSwap(&x2, &x3, swap) feCSwap(&z2, &z3, swap) feInvert(&z2, &z2) feMul(&x2, &x2, &z2) feToBytes(out, &x2) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/const_amd64.s���������������������������0000644�0000153�0000161�00000001111�12321735647�025421� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // +build amd64,!gccgo DATA ·REDMASK51(SB)/8, $0x0007FFFFFFFFFFFF GLOBL ·REDMASK51(SB), $8 DATA ·_121666_213(SB)/8, $996687872 GLOBL ·_121666_213(SB), $8 DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA GLOBL ·_2P0(SB), $8 DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE GLOBL ·_2P1234(SB), $8 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/curve25519_test.go����������������������0000644�0000153�0000161�00000001135�12321735647�026242� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 curve25519 import ( "fmt" "testing" ) const expectedHex = "89161fde887b2b53de549af483940106ecc114d6982daa98256de23bdf77661a" func TestBaseScalarMult(t *testing.T) { var a, b [32]byte in := &a out := &b a[0] = 1 for i := 0; i < 200; i++ { ScalarBaseMult(out, in) in, out = out, in } result := fmt.Sprintf("%x", in[:]) if result != expectedHex { t.Errorf("incorrect result: got %s, want %s", result, expectedHex) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/freeze_amd64.s��������������������������0000644�0000153�0000161�00000003004�12321735647�025556� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // +build amd64,!gccgo // func freeze(inout *[5]uint64) TEXT ·freeze(SB),7,$96-8 MOVQ inout+0(FP), DI MOVQ SP,R11 MOVQ $31,CX NOTQ CX ANDQ CX,SP ADDQ $32,SP MOVQ R11,0(SP) MOVQ R12,8(SP) MOVQ R13,16(SP) MOVQ R14,24(SP) MOVQ R15,32(SP) MOVQ BX,40(SP) MOVQ BP,48(SP) MOVQ 0(DI),SI MOVQ 8(DI),DX MOVQ 16(DI),CX MOVQ 24(DI),R8 MOVQ 32(DI),R9 MOVQ ·REDMASK51(SB),AX MOVQ AX,R10 SUBQ $18,R10 MOVQ $3,R11 REDUCELOOP: MOVQ SI,R12 SHRQ $51,R12 ANDQ AX,SI ADDQ R12,DX MOVQ DX,R12 SHRQ $51,R12 ANDQ AX,DX ADDQ R12,CX MOVQ CX,R12 SHRQ $51,R12 ANDQ AX,CX ADDQ R12,R8 MOVQ R8,R12 SHRQ $51,R12 ANDQ AX,R8 ADDQ R12,R9 MOVQ R9,R12 SHRQ $51,R12 ANDQ AX,R9 IMUL3Q $19,R12,R12 ADDQ R12,SI SUBQ $1,R11 JA REDUCELOOP MOVQ $1,R12 CMPQ R10,SI CMOVQLT R11,R12 CMPQ AX,DX CMOVQNE R11,R12 CMPQ AX,CX CMOVQNE R11,R12 CMPQ AX,R8 CMOVQNE R11,R12 CMPQ AX,R9 CMOVQNE R11,R12 NEGQ R12 ANDQ R12,AX ANDQ R12,R10 SUBQ R10,SI SUBQ AX,DX SUBQ AX,CX SUBQ AX,R8 SUBQ AX,R9 MOVQ SI,0(DI) MOVQ DX,8(DI) MOVQ CX,16(DI) MOVQ R8,24(DI) MOVQ R9,32(DI) MOVQ 0(SP),R11 MOVQ 8(SP),R12 MOVQ 16(SP),R13 MOVQ 24(SP),R14 MOVQ 32(SP),R15 MOVQ 40(SP),BX MOVQ 48(SP),BP MOVQ R11,SP MOVQ DI,AX MOVQ SI,DX RET ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/mont25519_amd64.go����������������������0000644�0000153�0000161�00000012216�12321735647�026031� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 amd64,!gccgo package curve25519 // These functions are implemented in the .s files. The names of the functions // in the rest of the file are also taken from the SUPERCOP sources to help // people following along. //go:noescape func cswap(inout *[5]uint64, v uint64) //go:noescape func ladderstep(inout *[5][5]uint64) //go:noescape func freeze(inout *[5]uint64) //go:noescape func mul(dest, a, b *[5]uint64) //go:noescape func square(out, in *[5]uint64) // mladder uses a Montgomery ladder to calculate (xr/zr) *= s. func mladder(xr, zr *[5]uint64, s *[32]byte) { var work [5][5]uint64 work[0] = *xr setint(&work[1], 1) setint(&work[2], 0) work[3] = *xr setint(&work[4], 1) j := uint(6) var prevbit byte for i := 31; i >= 0; i-- { for j < 8 { bit := ((*s)[i] >> j) & 1 swap := bit ^ prevbit prevbit = bit cswap(&work[1], uint64(swap)) ladderstep(&work) j-- } j = 7 } *xr = work[1] *zr = work[2] } func scalarMult(out, in, base *[32]byte) { var e [32]byte copy(e[:], (*in)[:]) e[0] &= 248 e[31] &= 127 e[31] |= 64 var t, z [5]uint64 unpack(&t, base) mladder(&t, &z, &e) invert(&z, &z) mul(&t, &t, &z) pack(out, &t) } func setint(r *[5]uint64, v uint64) { r[0] = v r[1] = 0 r[2] = 0 r[3] = 0 r[4] = 0 } // unpack sets r = x where r consists of 5, 51-bit limbs in little-endian // order. func unpack(r *[5]uint64, x *[32]byte) { r[0] = uint64(x[0]) | uint64(x[1])<<8 | uint64(x[2])<<16 | uint64(x[3])<<24 | uint64(x[4])<<32 | uint64(x[5])<<40 | uint64(x[6]&7)<<48 r[1] = uint64(x[6])>>3 | uint64(x[7])<<5 | uint64(x[8])<<13 | uint64(x[9])<<21 | uint64(x[10])<<29 | uint64(x[11])<<37 | uint64(x[12]&63)<<45 r[2] = uint64(x[12])>>6 | uint64(x[13])<<2 | uint64(x[14])<<10 | uint64(x[15])<<18 | uint64(x[16])<<26 | uint64(x[17])<<34 | uint64(x[18])<<42 | uint64(x[19]&1)<<50 r[3] = uint64(x[19])>>1 | uint64(x[20])<<7 | uint64(x[21])<<15 | uint64(x[22])<<23 | uint64(x[23])<<31 | uint64(x[24])<<39 | uint64(x[25]&15)<<47 r[4] = uint64(x[25])>>4 | uint64(x[26])<<4 | uint64(x[27])<<12 | uint64(x[28])<<20 | uint64(x[29])<<28 | uint64(x[30])<<36 | uint64(x[31]&127)<<44 } // pack sets out = x where out is the usual, little-endian form of the 5, // 51-bit limbs in x. func pack(out *[32]byte, x *[5]uint64) { t := *x freeze(&t) out[0] = byte(t[0]) out[1] = byte(t[0] >> 8) out[2] = byte(t[0] >> 16) out[3] = byte(t[0] >> 24) out[4] = byte(t[0] >> 32) out[5] = byte(t[0] >> 40) out[6] = byte(t[0] >> 48) out[6] ^= byte(t[1]<<3) & 0xf8 out[7] = byte(t[1] >> 5) out[8] = byte(t[1] >> 13) out[9] = byte(t[1] >> 21) out[10] = byte(t[1] >> 29) out[11] = byte(t[1] >> 37) out[12] = byte(t[1] >> 45) out[12] ^= byte(t[2]<<6) & 0xc0 out[13] = byte(t[2] >> 2) out[14] = byte(t[2] >> 10) out[15] = byte(t[2] >> 18) out[16] = byte(t[2] >> 26) out[17] = byte(t[2] >> 34) out[18] = byte(t[2] >> 42) out[19] = byte(t[2] >> 50) out[19] ^= byte(t[3]<<1) & 0xfe out[20] = byte(t[3] >> 7) out[21] = byte(t[3] >> 15) out[22] = byte(t[3] >> 23) out[23] = byte(t[3] >> 31) out[24] = byte(t[3] >> 39) out[25] = byte(t[3] >> 47) out[25] ^= byte(t[4]<<4) & 0xf0 out[26] = byte(t[4] >> 4) out[27] = byte(t[4] >> 12) out[28] = byte(t[4] >> 20) out[29] = byte(t[4] >> 28) out[30] = byte(t[4] >> 36) out[31] = byte(t[4] >> 44) } // invert calculates r = x^-1 mod p using Fermat's little theorem. func invert(r *[5]uint64, x *[5]uint64) { var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64 square(&z2, x) /* 2 */ square(&t, &z2) /* 4 */ square(&t, &t) /* 8 */ mul(&z9, &t, x) /* 9 */ mul(&z11, &z9, &z2) /* 11 */ square(&t, &z11) /* 22 */ mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */ square(&t, &z2_5_0) /* 2^6 - 2^1 */ for i := 1; i < 5; i++ { /* 2^20 - 2^10 */ square(&t, &t) } mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */ square(&t, &z2_10_0) /* 2^11 - 2^1 */ for i := 1; i < 10; i++ { /* 2^20 - 2^10 */ square(&t, &t) } mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */ square(&t, &z2_20_0) /* 2^21 - 2^1 */ for i := 1; i < 20; i++ { /* 2^40 - 2^20 */ square(&t, &t) } mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */ square(&t, &t) /* 2^41 - 2^1 */ for i := 1; i < 10; i++ { /* 2^50 - 2^10 */ square(&t, &t) } mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */ square(&t, &z2_50_0) /* 2^51 - 2^1 */ for i := 1; i < 50; i++ { /* 2^100 - 2^50 */ square(&t, &t) } mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */ square(&t, &z2_100_0) /* 2^101 - 2^1 */ for i := 1; i < 100; i++ { /* 2^200 - 2^100 */ square(&t, &t) } mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */ square(&t, &t) /* 2^201 - 2^1 */ for i := 1; i < 50; i++ { /* 2^250 - 2^50 */ square(&t, &t) } mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */ square(&t, &t) /* 2^251 - 2^1 */ square(&t, &t) /* 2^252 - 2^2 */ square(&t, &t) /* 2^253 - 2^3 */ square(&t, &t) /* 2^254 - 2^4 */ square(&t, &t) /* 2^255 - 2^5 */ mul(r, &t, &z11) /* 2^255 - 21 */ } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/curve25519/square_amd64.s��������������������������0000644�0000153�0000161�00000004455�12321735647�025611� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // +build amd64,!gccgo // func square(out, in *[5]uint64) TEXT ·square(SB),7,$96-16 MOVQ out+0(FP), DI MOVQ in+8(FP), SI MOVQ SP,R11 MOVQ $31,CX NOTQ CX ANDQ CX,SP ADDQ $32, SP MOVQ R11,0(SP) MOVQ R12,8(SP) MOVQ R13,16(SP) MOVQ R14,24(SP) MOVQ R15,32(SP) MOVQ BX,40(SP) MOVQ BP,48(SP) MOVQ 0(SI),AX MULQ 0(SI) MOVQ AX,CX MOVQ DX,R8 MOVQ 0(SI),AX SHLQ $1,AX MULQ 8(SI) MOVQ AX,R9 MOVQ DX,R10 MOVQ 0(SI),AX SHLQ $1,AX MULQ 16(SI) MOVQ AX,R11 MOVQ DX,R12 MOVQ 0(SI),AX SHLQ $1,AX MULQ 24(SI) MOVQ AX,R13 MOVQ DX,R14 MOVQ 0(SI),AX SHLQ $1,AX MULQ 32(SI) MOVQ AX,R15 MOVQ DX,BX MOVQ 8(SI),AX MULQ 8(SI) ADDQ AX,R11 ADCQ DX,R12 MOVQ 8(SI),AX SHLQ $1,AX MULQ 16(SI) ADDQ AX,R13 ADCQ DX,R14 MOVQ 8(SI),AX SHLQ $1,AX MULQ 24(SI) ADDQ AX,R15 ADCQ DX,BX MOVQ 8(SI),DX IMUL3Q $38,DX,AX MULQ 32(SI) ADDQ AX,CX ADCQ DX,R8 MOVQ 16(SI),AX MULQ 16(SI) ADDQ AX,R15 ADCQ DX,BX MOVQ 16(SI),DX IMUL3Q $38,DX,AX MULQ 24(SI) ADDQ AX,CX ADCQ DX,R8 MOVQ 16(SI),DX IMUL3Q $38,DX,AX MULQ 32(SI) ADDQ AX,R9 ADCQ DX,R10 MOVQ 24(SI),DX IMUL3Q $19,DX,AX MULQ 24(SI) ADDQ AX,R9 ADCQ DX,R10 MOVQ 24(SI),DX IMUL3Q $38,DX,AX MULQ 32(SI) ADDQ AX,R11 ADCQ DX,R12 MOVQ 32(SI),DX IMUL3Q $19,DX,AX MULQ 32(SI) ADDQ AX,R13 ADCQ DX,R14 MOVQ ·REDMASK51(SB),SI SHLQ $13,R8:CX ANDQ SI,CX SHLQ $13,R10:R9 ANDQ SI,R9 ADDQ R8,R9 SHLQ $13,R12:R11 ANDQ SI,R11 ADDQ R10,R11 SHLQ $13,R14:R13 ANDQ SI,R13 ADDQ R12,R13 SHLQ $13,BX:R15 ANDQ SI,R15 ADDQ R14,R15 IMUL3Q $19,BX,DX ADDQ DX,CX MOVQ CX,DX SHRQ $51,DX ADDQ R9,DX ANDQ SI,CX MOVQ DX,R8 SHRQ $51,DX ADDQ R11,DX ANDQ SI,R8 MOVQ DX,R9 SHRQ $51,DX ADDQ R13,DX ANDQ SI,R9 MOVQ DX,AX SHRQ $51,DX ADDQ R15,DX ANDQ SI,AX MOVQ DX,R10 SHRQ $51,DX IMUL3Q $19,DX,DX ADDQ DX,CX ANDQ SI,R10 MOVQ CX,0(DI) MOVQ R8,8(DI) MOVQ R9,16(DI) MOVQ AX,24(DI) MOVQ R10,32(DI) MOVQ 0(SP),R11 MOVQ 8(SP),R12 MOVQ 16(SP),R13 MOVQ 24(SP),R14 MOVQ 32(SP),R15 MOVQ 40(SP),BX MOVQ 48(SP),BP MOVQ R11,SP MOVQ DI,AX MOVQ SI,DX RET �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ocsp/����������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022334� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ocsp/ocsp.go���������������������������������������0000644�0000153�0000161�00000025132�12321735647�023632� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ocsp parses OCSP responses as specified in RFC 2560. OCSP responses // are signed messages attesting to the validity of a certificate for a small // period of time. This is used to manage revocation for X.509 certificates. package ocsp import ( "crypto" _ "crypto/sha1" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "math/big" "time" ) var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1}) // These are internal structures that reflect the ASN.1 structure of an OCSP // response. See RFC 2560, section 4.2. const ( ocspSuccess = 0 ocspMalformed = 1 ocspInternalError = 2 ocspTryLater = 3 ocspSigRequired = 4 ocspUnauthorized = 5 ) type certID struct { HashAlgorithm pkix.AlgorithmIdentifier NameHash []byte IssuerKeyHash []byte SerialNumber *big.Int } type responseASN1 struct { Status asn1.Enumerated Response responseBytes `asn1:"explicit,tag:0"` } type responseBytes struct { ResponseType asn1.ObjectIdentifier Response []byte } type basicResponse struct { TBSResponseData responseData SignatureAlgorithm pkix.AlgorithmIdentifier Signature asn1.BitString Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"` } type responseData struct { Raw asn1.RawContent Version int `asn1:"optional,default:1,explicit,tag:0"` RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"` KeyHash []byte `asn1:"optional,explicit,tag:2"` ProducedAt time.Time Responses []singleResponse } type singleResponse struct { CertID certID Good asn1.Flag `asn1:"explicit,tag:0,optional"` Revoked revokedInfo `asn1:"explicit,tag:1,optional"` Unknown asn1.Flag `asn1:"explicit,tag:2,optional"` ThisUpdate time.Time NextUpdate time.Time `asn1:"explicit,tag:0,optional"` } type revokedInfo struct { RevocationTime time.Time Reason int `asn1:"explicit,tag:0,optional"` } var ( oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} ) // TODO(agl): this is taken from crypto/x509 and so should probably be exported // from crypto/x509 or crypto/x509/pkix. func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.SignatureAlgorithm { switch { case oid.Equal(oidSignatureMD2WithRSA): return x509.MD2WithRSA case oid.Equal(oidSignatureMD5WithRSA): return x509.MD5WithRSA case oid.Equal(oidSignatureSHA1WithRSA): return x509.SHA1WithRSA case oid.Equal(oidSignatureSHA256WithRSA): return x509.SHA256WithRSA case oid.Equal(oidSignatureSHA384WithRSA): return x509.SHA384WithRSA case oid.Equal(oidSignatureSHA512WithRSA): return x509.SHA512WithRSA case oid.Equal(oidSignatureDSAWithSHA1): return x509.DSAWithSHA1 case oid.Equal(oidSignatureDSAWithSHA256): return x509.DSAWithSHA256 case oid.Equal(oidSignatureECDSAWithSHA1): return x509.ECDSAWithSHA1 case oid.Equal(oidSignatureECDSAWithSHA256): return x509.ECDSAWithSHA256 case oid.Equal(oidSignatureECDSAWithSHA384): return x509.ECDSAWithSHA384 case oid.Equal(oidSignatureECDSAWithSHA512): return x509.ECDSAWithSHA512 } return x509.UnknownSignatureAlgorithm } // This is the exposed reflection of the internal OCSP structures. const ( // Good means that the certificate is valid. Good = iota // Revoked means that the certificate has been deliberately revoked. Revoked = iota // Unknown means that the OCSP responder doesn't know about the certificate. Unknown = iota // ServerFailed means that the OCSP responder failed to process the request. ServerFailed = iota ) // Response represents an OCSP response. See RFC 2560. type Response struct { // Status is one of {Good, Revoked, Unknown, ServerFailed} Status int SerialNumber *big.Int ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time RevocationReason int Certificate *x509.Certificate // TBSResponseData contains the raw bytes of the signed response. If // Certificate is nil then this can be used to verify Signature. TBSResponseData []byte Signature []byte SignatureAlgorithm x509.SignatureAlgorithm } // CheckSignatureFrom checks that the signature in resp is a valid signature // from issuer. This should only be used if resp.Certificate is nil. Otherwise, // the OCSP response contained an intermediate certificate that created the // signature. That signature is checked by ParseResponse and only // resp.Certificate remains to be validated. func (resp *Response) CheckSignatureFrom(issuer *x509.Certificate) error { return issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature) } // ParseError results from an invalid OCSP response. type ParseError string func (p ParseError) Error() string { return string(p) } // ParseResponse parses an OCSP response in DER form. It only supports // responses for a single certificate. If the response contains a certificate // then the signature over the response is checked. If issuer is not nil then // it will be used to validate the signature or embedded certificate. Invalid // signatures or parse failures will result in a ParseError. func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) { var resp responseASN1 rest, err := asn1.Unmarshal(bytes, &resp) if err != nil { return nil, err } if len(rest) > 0 { return nil, ParseError("trailing data in OCSP response") } ret := new(Response) if resp.Status != ocspSuccess { ret.Status = ServerFailed return ret, nil } if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) { return nil, ParseError("bad OCSP response type") } var basicResp basicResponse rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp) if err != nil { return nil, err } if len(basicResp.Certificates) > 1 { return nil, ParseError("OCSP response contains bad number of certificates") } if len(basicResp.TBSResponseData.Responses) != 1 { return nil, ParseError("OCSP response contains bad number of responses") } ret.TBSResponseData = basicResp.TBSResponseData.Raw ret.Signature = basicResp.Signature.RightAlign() ret.SignatureAlgorithm = getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm) if len(basicResp.Certificates) > 0 { ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes) if err != nil { return nil, err } if err := ret.CheckSignatureFrom(ret.Certificate); err != nil { return nil, ParseError("bad OCSP signature") } if issuer != nil { if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil { return nil, ParseError("bad signature on embedded certificate") } } } else if issuer != nil { if err := ret.CheckSignatureFrom(issuer); err != nil { return nil, ParseError("bad OCSP signature") } } r := basicResp.TBSResponseData.Responses[0] ret.SerialNumber = r.CertID.SerialNumber switch { case bool(r.Good): ret.Status = Good case bool(r.Unknown): ret.Status = Unknown default: ret.Status = Revoked ret.RevokedAt = r.Revoked.RevocationTime ret.RevocationReason = r.Revoked.Reason } ret.ProducedAt = basicResp.TBSResponseData.ProducedAt ret.ThisUpdate = r.ThisUpdate ret.NextUpdate = r.NextUpdate return ret, nil } // https://tools.ietf.org/html/rfc2560#section-4.1.1 type ocspRequest struct { TBSRequest tbsRequest } type tbsRequest struct { Version int `asn1:"explicit,tag:0,default:0"` RequestList []request } type request struct { Cert certID } // RequestOptions contains options for constructing OCSP requests. type RequestOptions struct { // Hash contains the hash function that should be used when // constructing the OCSP request. If zero, SHA-1 will be used. Hash crypto.Hash } func (opts *RequestOptions) hash() crypto.Hash { if opts == nil || opts.Hash == 0 { // SHA-1 is nearly universally used in OCSP. return crypto.SHA1 } return opts.Hash } // CreateRequest returns a DER-encoded, OCSP request for the status of cert. If // opts is nil then sensible defaults are used. func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte, error) { hashFunc := opts.hash() // OCSP seems to be the only place where these raw hash identifiers are // used. I took the following from // http://msdn.microsoft.com/en-us/library/ff635603.aspx var hashOID asn1.ObjectIdentifier switch hashFunc { case crypto.SHA1: hashOID = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) case crypto.SHA256: hashOID = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}) case crypto.SHA384: hashOID = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}) case crypto.SHA512: hashOID = asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}) default: return nil, x509.ErrUnsupportedAlgorithm } if !hashFunc.Available() { return nil, x509.ErrUnsupportedAlgorithm } h := opts.hash().New() var publicKeyInfo struct { Algorithm pkix.AlgorithmIdentifier PublicKey asn1.BitString } if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil { return nil, err } h.Write(publicKeyInfo.PublicKey.RightAlign()) issuerKeyHash := h.Sum(nil) h.Reset() h.Write(issuer.RawSubject) issuerNameHash := h.Sum(nil) return asn1.Marshal(ocspRequest{ tbsRequest{ Version: 0, RequestList: []request{ { Cert: certID{ pkix.AlgorithmIdentifier{ Algorithm: hashOID, Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */}, }, issuerNameHash, issuerKeyHash, cert.SerialNumber, }, }, }, }, }) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ocsp/ocsp_test.go����������������������������������0000644�0000153�0000161�00000036356�12321735647�024703� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ocsp import ( "bytes" "crypto/x509" "encoding/hex" "math/big" "reflect" "testing" "time" ) func TestOCSPDecode(t *testing.T) { responseBytes, _ := hex.DecodeString(ocspResponseHex) resp, err := ParseResponse(responseBytes, nil) if err != nil { t.Error(err) } expected := Response{ Status: 0, SerialNumber: big.NewInt(0x1d0fa), RevocationReason: 0, ThisUpdate: time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC), NextUpdate: time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC), } if !reflect.DeepEqual(resp.ThisUpdate, expected.ThisUpdate) { t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate) } if !reflect.DeepEqual(resp.NextUpdate, expected.NextUpdate) { t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, expected.NextUpdate) } if resp.Status != expected.Status { t.Errorf("resp.Status: got %d, want %d", resp.Status, expected.Status) } if resp.SerialNumber.Cmp(expected.SerialNumber) != 0 { t.Errorf("resp.SerialNumber: got %x, want %x", resp.SerialNumber, expected.SerialNumber) } if resp.RevocationReason != expected.RevocationReason { t.Errorf("resp.RevocationReason: got %d, want %d", resp.RevocationReason, expected.RevocationReason) } } func TestOCSPDecodeWithoutCert(t *testing.T) { responseBytes, _ := hex.DecodeString(ocspResponseWithoutCertHex) _, err := ParseResponse(responseBytes, nil) if err != nil { t.Error(err) } } func TestOCSPSignature(t *testing.T) { issuerCert, _ := hex.DecodeString(startComHex) issuer, err := x509.ParseCertificate(issuerCert) if err != nil { t.Fatal(err) } response, _ := hex.DecodeString(ocspResponseHex) if _, err := ParseResponse(response, issuer); err != nil { t.Error(err) } } func TestOCSPRequest(t *testing.T) { leafCert, _ := hex.DecodeString(leafCertHex) cert, err := x509.ParseCertificate(leafCert) if err != nil { t.Fatal(err) } issuerCert, _ := hex.DecodeString(issuerCertHex) issuer, err := x509.ParseCertificate(issuerCert) if err != nil { t.Fatal(err) } request, err := CreateRequest(cert, issuer, nil) if err != nil { t.Fatal(err) } expected, _ := hex.DecodeString(ocspRequestHex) if !bytes.Equal(request, expected) { t.Errorf("got %x, wanted %x", request, expected) } } // This OCSP response was taken from Thawte's public OCSP responder. // To recreate: // $ openssl s_client -tls1 -showcerts -servername www.google.com -connect www.google.com:443 // Copy and paste the first certificate into /tmp/cert.crt and the second into // /tmp/intermediate.crt // $ openssl ocsp -issuer /tmp/intermediate.crt -cert /tmp/cert.crt -url http://ocsp.thawte.com -resp_text -respout /tmp/ocsp.der // Then hex encode the result: // $ python -c 'print file("/tmp/ocsp.der", "r").read().encode("hex")' const ocspResponseHex = "308206bc0a0100a08206b5308206b106092b0601050507300101048206a23082069e3081" + "c9a14e304c310b300906035504061302494c31163014060355040a130d5374617274436f" + "6d204c74642e312530230603550403131c5374617274436f6d20436c6173732031204f43" + "5350205369676e6572180f32303130303730373137333531375a30663064303c30090605" + "2b0e03021a050004146568874f40750f016a3475625e1f5c93e5a26d580414eb4234d098" + "b0ab9ff41b6b08f7cc642eef0e2c45020301d0fa8000180f323031303037303731353031" + "30355aa011180f32303130303730373138333531375a300d06092a864886f70d01010505" + "000382010100ab557ff070d1d7cebbb5f0ec91a15c3fed22eb2e1b8244f1b84545f013a4" + "fb46214c5e3fbfbebb8a56acc2b9db19f68fd3c3201046b3824d5ba689f99864328710cb" + "467195eb37d84f539e49f859316b32964dc3e47e36814ce94d6c56dd02733b1d0802f7ff" + "4eebdbbd2927dcf580f16cbc290f91e81b53cb365e7223f1d6e20a88ea064104875e0145" + "672b20fc14829d51ca122f5f5d77d3ad6c83889c55c7dc43680ba2fe3cef8b05dbcabdc0" + "d3e09aaf9725597f8c858c2fa38c0d6aed2e6318194420dd1a1137445d13e1c97ab47896" + "17a4e08925f46f867b72e3a4dc1f08cb870b2b0717f7207faa0ac512e628a029aba7457a" + "e63dcf3281e2162d9349a08204ba308204b6308204b23082039aa003020102020101300d" + "06092a864886f70d010105050030818c310b300906035504061302494c31163014060355" + "040a130d5374617274436f6d204c74642e312b3029060355040b13225365637572652044" + "69676974616c204365727469666963617465205369676e696e6731383036060355040313" + "2f5374617274436f6d20436c6173732031205072696d61727920496e7465726d65646961" + "746520536572766572204341301e170d3037313032353030323330365a170d3132313032" + "333030323330365a304c310b300906035504061302494c31163014060355040a130d5374" + "617274436f6d204c74642e312530230603550403131c5374617274436f6d20436c617373" + "2031204f435350205369676e657230820122300d06092a864886f70d0101010500038201" + "0f003082010a0282010100b9561b4c45318717178084e96e178df2255e18ed8d8ecc7c2b" + "7b51a6c1c2e6bf0aa3603066f132fe10ae97b50e99fa24b83fc53dd2777496387d14e1c3" + "a9b6a4933e2ac12413d085570a95b8147414a0bc007c7bcf222446ef7f1a156d7ea1c577" + "fc5f0facdfd42eb0f5974990cb2f5cefebceef4d1bdc7ae5c1075c5a99a93171f2b0845b" + "4ff0864e973fcfe32f9d7511ff87a3e943410c90a4493a306b6944359340a9ca96f02b66" + "ce67f028df2980a6aaee8d5d5d452b8b0eb93f923cc1e23fcccbdbe7ffcb114d08fa7a6a" + "3c404f825d1a0e715935cf623a8c7b59670014ed0622f6089a9447a7a19010f7fe58f841" + "29a2765ea367824d1c3bb2fda308530203010001a382015c30820158300c0603551d1301" + "01ff04023000300b0603551d0f0404030203a8301e0603551d250417301506082b060105" + "0507030906092b0601050507300105301d0603551d0e0416041445e0a36695414c5dd449" + "bc00e33cdcdbd2343e173081a80603551d230481a030819d8014eb4234d098b0ab9ff41b" + "6b08f7cc642eef0e2c45a18181a47f307d310b300906035504061302494c311630140603" + "55040a130d5374617274436f6d204c74642e312b3029060355040b132253656375726520" + "4469676974616c204365727469666963617465205369676e696e67312930270603550403" + "13205374617274436f6d2043657274696669636174696f6e20417574686f726974798201" + "0a30230603551d12041c301a8618687474703a2f2f7777772e737461727473736c2e636f" + "6d2f302c06096086480186f842010d041f161d5374617274436f6d205265766f63617469" + "6f6e20417574686f72697479300d06092a864886f70d01010505000382010100182d2215" + "8f0fc0291324fa8574c49bb8ff2835085adcbf7b7fc4191c397ab6951328253fffe1e5ec" + "2a7da0d50fca1a404e6968481366939e666c0a6209073eca57973e2fefa9ed1718e8176f" + "1d85527ff522c08db702e3b2b180f1cbff05d98128252cf0f450f7dd2772f4188047f19d" + "c85317366f94bc52d60f453a550af58e308aaab00ced33040b62bf37f5b1ab2a4f7f0f80" + "f763bf4d707bc8841d7ad9385ee2a4244469260b6f2bf085977af9074796048ecc2f9d48" + "a1d24ce16e41a9941568fec5b42771e118f16c106a54ccc339a4b02166445a167902e75e" + "6d8620b0825dcd18a069b90fd851d10fa8effd409deec02860d26d8d833f304b10669b42" const startComHex = "308206343082041ca003020102020118300d06092a864886f70d0101050500307d310b30" + "0906035504061302494c31163014060355040a130d5374617274436f6d204c74642e312b" + "3029060355040b1322536563757265204469676974616c20436572746966696361746520" + "5369676e696e6731293027060355040313205374617274436f6d20436572746966696361" + "74696f6e20417574686f72697479301e170d3037313032343230353431375a170d313731" + "3032343230353431375a30818c310b300906035504061302494c31163014060355040a13" + "0d5374617274436f6d204c74642e312b3029060355040b13225365637572652044696769" + "74616c204365727469666963617465205369676e696e67313830360603550403132f5374" + "617274436f6d20436c6173732031205072696d61727920496e7465726d65646961746520" + "53657276657220434130820122300d06092a864886f70d01010105000382010f00308201" + "0a0282010100b689c6acef09527807ac9263d0f44418188480561f91aee187fa3250b4d3" + "4706f0e6075f700e10f71dc0ce103634855a0f92ac83c6ac58523fba38e8fce7a724e240" + "a60876c0926e9e2a6d4d3f6e61200adb59ded27d63b33e46fefa215118d7cd30a6ed076e" + "3b7087b4f9faebee823c056f92f7a4dc0a301e9373fe07cad75f809d225852ae06da8b87" + "2369b0e42ad8ea83d2bdf371db705a280faf5a387045123f304dcd3baf17e50fcba0a95d" + "48aab16150cb34cd3c5cc30be810c08c9bf0030362feb26c3e720eee1c432ac9480e5739" + "c43121c810c12c87fe5495521f523c31129b7fe7c0a0a559d5e28f3ef0d5a8e1d77031a9" + "c4b3cfaf6d532f06f4a70203010001a38201ad308201a9300f0603551d130101ff040530" + "030101ff300e0603551d0f0101ff040403020106301d0603551d0e04160414eb4234d098" + "b0ab9ff41b6b08f7cc642eef0e2c45301f0603551d230418301680144e0bef1aa4405ba5" + "17698730ca346843d041aef2306606082b06010505070101045a3058302706082b060105" + "05073001861b687474703a2f2f6f6373702e737461727473736c2e636f6d2f6361302d06" + "082b060105050730028621687474703a2f2f7777772e737461727473736c2e636f6d2f73" + "667363612e637274305b0603551d1f045430523027a025a0238621687474703a2f2f7777" + "772e737461727473736c2e636f6d2f73667363612e63726c3027a025a023862168747470" + "3a2f2f63726c2e737461727473736c2e636f6d2f73667363612e63726c3081800603551d" + "20047930773075060b2b0601040181b5370102013066302e06082b060105050702011622" + "687474703a2f2f7777772e737461727473736c2e636f6d2f706f6c6963792e7064663034" + "06082b060105050702011628687474703a2f2f7777772e737461727473736c2e636f6d2f" + "696e7465726d6564696174652e706466300d06092a864886f70d01010505000382020100" + "2109493ea5886ee00b8b48da314d8ff75657a2e1d36257e9b556f38545753be5501f048b" + "e6a05a3ee700ae85d0fbff200364cbad02e1c69172f8a34dd6dee8cc3fa18aa2e37c37a7" + "c64f8f35d6f4d66e067bdd21d9cf56ffcb302249fe8904f385e5aaf1e71fe875904dddf9" + "46f74234f745580c110d84b0c6da5d3ef9019ee7e1da5595be741c7bfc4d144fac7e5547" + "7d7bf4a50d491e95e8f712c1ccff76a62547d0f37535be97b75816ebaa5c786fec5330af" + "ea044dcca902e3f0b60412f630b1113d904e5664d7dc3c435f7339ef4baf87ebf6fe6888" + "4472ead207c669b0c1a18bef1749d761b145485f3b2021e95bb2ccf4d7e931f50b15613b" + "7a94e3ebd9bc7f94ae6ae3626296a8647cb887f399327e92a252bebbf865cfc9f230fc8b" + "c1c2a696d75f89e15c3480f58f47072fb491bfb1a27e5f4b5ad05b9f248605515a690365" + "434971c5e06f94346bf61bd8a9b04c7e53eb8f48dfca33b548fa364a1a53a6330cd089cd" + "4915cd89313c90c072d7654b52358a461144b93d8e2865a63e799e5c084429adb035112e" + "214eb8d2e7103e5d8483b3c3c2e4d2c6fd094b7409ddf1b3d3193e800da20b19f038e7c5" + "c2afe223db61e29d5c6e2089492e236ab262c145b49faf8ba7f1223bf87de290d07a19fb" + "4a4ce3d27d5f4a8303ed27d6239e6b8db459a2d9ef6c8229dd75193c3f4c108defbb7527" + "d2ae83a7a8ce5ba7" const ocspResponseWithoutCertHex = "308201d40a0100a08201cd308201c906092b0601050507300101048201ba3082" + "01b630819fa2160414884451ff502a695e2d88f421bad90cf2cecbea7c180f3230313330" + "3631383037323434335a30743072304a300906052b0e03021a0500041448b60d38238df8" + "456e4ee5843ea394111802979f0414884451ff502a695e2d88f421bad90cf2cecbea7c02" + "1100f78b13b946fc9635d8ab49de9d2148218000180f3230313330363138303732343433" + "5aa011180f32303133303632323037323434335a300d06092a864886f70d010105050003" + "82010100103e18b3d297a5e7a6c07a4fc52ac46a15c0eba96f3be17f0ffe84de5b8c8e05" + "5a8f577586a849dc4abd6440eb6fedde4622451e2823c1cbf3558b4e8184959c9fe96eff" + "8bc5f95866c58c6d087519faabfdae37e11d9874f1bc0db292208f645dd848185e4dd38b" + "6a8547dfa7b74d514a8470015719064d35476b95bebb03d4d2845c5ca15202d2784878f2" + "0f904c24f09736f044609e9c271381713400e563023d212db422236440c6f377bbf24b2b" + "9e7dec8698e36a8df68b7592ad3489fb2937afb90eb85d2aa96b81c94c25057dbd4759d9" + "20a1a65c7f0b6427a224b3c98edd96b9b61f706099951188b0289555ad30a216fb774651" + "5a35fca2e054dfa8" const ocspRequestHex = "30563054a003020100304d304b3049300906052b0e03021a05000414c0fe0278fc991888" + "91b3f212e9c7e1b21ab7bfc004140dfc1df0a9e0f01ce7f2b213177e6f8d157cd4f60210" + "017f77deb3bcbb235d44ccc7dba62e72" const leafCertHex = "308203c830820331a0030201020210017f77deb3bcbb235d44ccc7dba62e72300d06092a" + "864886f70d01010505003081ba311f301d060355040a1316566572695369676e20547275" + "7374204e6574776f726b31173015060355040b130e566572695369676e2c20496e632e31" + "333031060355040b132a566572695369676e20496e7465726e6174696f6e616c20536572" + "766572204341202d20436c617373203331493047060355040b13407777772e7665726973" + "69676e2e636f6d2f43505320496e636f72702e6279205265662e204c494142494c495459" + "204c54442e286329393720566572695369676e301e170d3132303632313030303030305a" + "170d3133313233313233353935395a3068310b3009060355040613025553311330110603" + "550408130a43616c69666f726e6961311230100603550407130950616c6f20416c746f31" + "173015060355040a130e46616365626f6f6b2c20496e632e311730150603550403140e2a" + "2e66616365626f6f6b2e636f6d30819f300d06092a864886f70d010101050003818d0030" + "818902818100ae94b171e2deccc1693e051063240102e0689ae83c39b6b3e74b97d48d7b" + "23689100b0b496ee62f0e6d356bcf4aa0f50643402f5d1766aa972835a7564723f39bbef" + "5290ded9bcdbf9d3d55dfad23aa03dc604c54d29cf1d4b3bdbd1a809cfae47b44c7eae17" + "c5109bee24a9cf4a8d911bb0fd0415ae4c3f430aa12a557e2ae10203010001a382011e30" + "82011a30090603551d130402300030440603551d20043d303b3039060b6086480186f845" + "01071703302a302806082b06010505070201161c68747470733a2f2f7777772e76657269" + "7369676e2e636f6d2f727061303c0603551d1f043530333031a02fa02d862b687474703a" + "2f2f535652496e746c2d63726c2e766572697369676e2e636f6d2f535652496e746c2e63" + "726c301d0603551d250416301406082b0601050507030106082b06010505070302300b06" + "03551d0f0404030205a0303406082b0601050507010104283026302406082b0601050507" + "30018618687474703a2f2f6f6373702e766572697369676e2e636f6d30270603551d1104" + "20301e820e2a2e66616365626f6f6b2e636f6d820c66616365626f6f6b2e636f6d300d06" + "092a864886f70d0101050500038181005b6c2b75f8ed30aa51aad36aba595e555141951f" + "81a53b447910ac1f76ff78fc2781616b58f3122afc1c87010425e9ed43df1a7ba6498060" + "67e2688af03db58c7df4ee03309a6afc247ccb134dc33e54c6bc1d5133a532a73273b1d7" + "9cadc08e7e1a83116d34523340b0305427a21742827c98916698ee7eaf8c3bdd71700817" const issuerCertHex = "30820383308202eca003020102021046fcebbab4d02f0f926098233f93078f300d06092a" + "864886f70d0101050500305f310b300906035504061302555331173015060355040a130e" + "566572695369676e2c20496e632e31373035060355040b132e436c617373203320507562" + "6c6963205072696d6172792043657274696669636174696f6e20417574686f7269747930" + "1e170d3937303431373030303030305a170d3136313032343233353935395a3081ba311f" + "301d060355040a1316566572695369676e205472757374204e6574776f726b3117301506" + "0355040b130e566572695369676e2c20496e632e31333031060355040b132a5665726953" + "69676e20496e7465726e6174696f6e616c20536572766572204341202d20436c61737320" + "3331493047060355040b13407777772e766572697369676e2e636f6d2f43505320496e63" + "6f72702e6279205265662e204c494142494c495459204c54442e28632939372056657269" + "5369676e30819f300d06092a864886f70d010101050003818d0030818902818100d88280" + "e8d619027d1f85183925a2652be1bfd405d3bce6363baaf04c6c5bb6e7aa3c734555b2f1" + "bdea9742ed9a340a15d4a95cf54025ddd907c132b2756cc4cabba3fe56277143aa63f530" + "3e9328e5faf1093bf3b74d4e39f75c495ab8c11dd3b28afe70309542cbfe2b518b5a3c3a" + "f9224f90b202a7539c4f34e7ab04b27b6f0203010001a381e33081e0300f0603551d1304" + "0830060101ff02010030440603551d20043d303b3039060b6086480186f8450107010130" + "2a302806082b06010505070201161c68747470733a2f2f7777772e766572697369676e2e" + "636f6d2f43505330340603551d25042d302b06082b0601050507030106082b0601050507" + "030206096086480186f8420401060a6086480186f845010801300b0603551d0f04040302" + "0106301106096086480186f842010104040302010630310603551d1f042a30283026a024" + "a0228620687474703a2f2f63726c2e766572697369676e2e636f6d2f706361332e63726c" + "300d06092a864886f70d010105050003818100408e4997968a73dd8e4def3e61b7caa062" + "adf40e0abb753de26ed82cc7bff4b98c369bcaa2d09c724639f6a682036511c4bcbf2da6" + "f5d93b0ab598fab378b91ef22b4c62d5fdb27a1ddf33fd73f9a5d82d8c2aead1fcb028b6" + "e94948134b838a1b487b24f738de6f4154b8ab576b06dfc7a2d4a9f6f136628088f28b75" + "d68071" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/���������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022224� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/curve.go�������������������������������������0000644�0000153�0000161�00000012545�12321735647�023706� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bn256 import ( "math/big" ) // curvePoint implements the elliptic curve y²=x³+3. Points are kept in // Jacobian form and t=z² when valid. Gâ‚ is the set of points of this curve on // GF(p). type curvePoint struct { x, y, z, t *big.Int } var curveB = new(big.Int).SetInt64(3) // curveGen is the generator of Gâ‚. var curveGen = &curvePoint{ new(big.Int).SetInt64(1), new(big.Int).SetInt64(-2), new(big.Int).SetInt64(1), new(big.Int).SetInt64(1), } func newCurvePoint(pool *bnPool) *curvePoint { return &curvePoint{ pool.Get(), pool.Get(), pool.Get(), pool.Get(), } } func (c *curvePoint) String() string { c.MakeAffine(new(bnPool)) return "(" + c.x.String() + ", " + c.y.String() + ")" } func (c *curvePoint) Put(pool *bnPool) { pool.Put(c.x) pool.Put(c.y) pool.Put(c.z) pool.Put(c.t) } func (c *curvePoint) Set(a *curvePoint) { c.x.Set(a.x) c.y.Set(a.y) c.z.Set(a.z) c.t.Set(a.t) } // IsOnCurve returns true iff c is on the curve where c must be in affine form. func (c *curvePoint) IsOnCurve() bool { yy := new(big.Int).Mul(c.y, c.y) xxx := new(big.Int).Mul(c.x, c.x) xxx.Mul(xxx, c.x) yy.Sub(yy, xxx) yy.Sub(yy, curveB) if yy.Sign() < 0 || yy.Cmp(p) >= 0 { yy.Mod(yy, p) } return yy.Sign() == 0 } func (c *curvePoint) SetInfinity() { c.z.SetInt64(0) } func (c *curvePoint) IsInfinity() bool { return c.z.Sign() == 0 } func (c *curvePoint) Add(a, b *curvePoint, pool *bnPool) { if a.IsInfinity() { c.Set(b) return } if b.IsInfinity() { c.Set(a) return } // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3 // Normalize the points by replacing a = [x1:y1:z1] and b = [x2:y2:z2] // by [u1:s1:z1·z2] and [u2:s2:z1·z2] // where u1 = x1·z2², s1 = y1·z2³ and u1 = x2·z1², s2 = y2·z1³ z1z1 := pool.Get().Mul(a.z, a.z) z1z1.Mod(z1z1, p) z2z2 := pool.Get().Mul(b.z, b.z) z2z2.Mod(z2z2, p) u1 := pool.Get().Mul(a.x, z2z2) u1.Mod(u1, p) u2 := pool.Get().Mul(b.x, z1z1) u2.Mod(u2, p) t := pool.Get().Mul(b.z, z2z2) t.Mod(t, p) s1 := pool.Get().Mul(a.y, t) s1.Mod(s1, p) t.Mul(a.z, z1z1) t.Mod(t, p) s2 := pool.Get().Mul(b.y, t) s2.Mod(s2, p) // Compute x = (2h)²(s²-u1-u2) // where s = (s2-s1)/(u2-u1) is the slope of the line through // (u1,s1) and (u2,s2). The extra factor 2h = 2(u2-u1) comes from the value of z below. // This is also: // 4(s2-s1)² - 4h²(u1+u2) = 4(s2-s1)² - 4h³ - 4h²(2u1) // = r² - j - 2v // with the notations below. h := pool.Get().Sub(u2, u1) xEqual := h.Sign() == 0 t.Add(h, h) // i = 4h² i := pool.Get().Mul(t, t) i.Mod(i, p) // j = 4h³ j := pool.Get().Mul(h, i) j.Mod(j, p) t.Sub(s2, s1) yEqual := t.Sign() == 0 if xEqual && yEqual { c.Double(a, pool) return } r := pool.Get().Add(t, t) v := pool.Get().Mul(u1, i) v.Mod(v, p) // t4 = 4(s2-s1)² t4 := pool.Get().Mul(r, r) t4.Mod(t4, p) t.Add(v, v) t6 := pool.Get().Sub(t4, j) c.x.Sub(t6, t) // Set y = -(2h)³(s1 + s*(x/4h²-u1)) // This is also // y = - 2·s1·j - (s2-s1)(2x - 2i·u1) = r(v-x) - 2·s1·j t.Sub(v, c.x) // t7 t4.Mul(s1, j) // t8 t4.Mod(t4, p) t6.Add(t4, t4) // t9 t4.Mul(r, t) // t10 t4.Mod(t4, p) c.y.Sub(t4, t6) // Set z = 2(u2-u1)·z1·z2 = 2h·z1·z2 t.Add(a.z, b.z) // t11 t4.Mul(t, t) // t12 t4.Mod(t4, p) t.Sub(t4, z1z1) // t13 t4.Sub(t, z2z2) // t14 c.z.Mul(t4, h) c.z.Mod(c.z, p) pool.Put(z1z1) pool.Put(z2z2) pool.Put(u1) pool.Put(u2) pool.Put(t) pool.Put(s1) pool.Put(s2) pool.Put(h) pool.Put(i) pool.Put(j) pool.Put(r) pool.Put(v) pool.Put(t4) pool.Put(t6) } func (c *curvePoint) Double(a *curvePoint, pool *bnPool) { // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3 A := pool.Get().Mul(a.x, a.x) A.Mod(A, p) B := pool.Get().Mul(a.y, a.y) B.Mod(B, p) C := pool.Get().Mul(B, B) C.Mod(C, p) t := pool.Get().Add(a.x, B) t2 := pool.Get().Mul(t, t) t2.Mod(t2, p) t.Sub(t2, A) t2.Sub(t, C) d := pool.Get().Add(t2, t2) t.Add(A, A) e := pool.Get().Add(t, A) f := pool.Get().Mul(e, e) f.Mod(f, p) t.Add(d, d) c.x.Sub(f, t) t.Add(C, C) t2.Add(t, t) t.Add(t2, t2) c.y.Sub(d, c.x) t2.Mul(e, c.y) t2.Mod(t2, p) c.y.Sub(t2, t) t.Mul(a.y, a.z) t.Mod(t, p) c.z.Add(t, t) pool.Put(A) pool.Put(B) pool.Put(C) pool.Put(t) pool.Put(t2) pool.Put(d) pool.Put(e) pool.Put(f) } func (c *curvePoint) Mul(a *curvePoint, scalar *big.Int, pool *bnPool) *curvePoint { sum := newCurvePoint(pool) sum.SetInfinity() t := newCurvePoint(pool) for i := scalar.BitLen(); i >= 0; i-- { t.Double(sum, pool) if scalar.Bit(i) != 0 { sum.Add(t, a, pool) } else { sum.Set(t) } } c.Set(sum) sum.Put(pool) t.Put(pool) return c } func (c *curvePoint) MakeAffine(pool *bnPool) *curvePoint { if words := c.z.Bits(); len(words) == 1 && words[0] == 1 { return c } zInv := pool.Get().ModInverse(c.z, p) t := pool.Get().Mul(c.y, zInv) t.Mod(t, p) zInv2 := pool.Get().Mul(zInv, zInv) zInv2.Mod(zInv2, p) c.y.Mul(t, zInv2) c.y.Mod(c.y, p) t.Mul(c.x, zInv2) t.Mod(t, p) c.x.Set(t) c.z.SetInt64(1) c.t.SetInt64(1) pool.Put(zInv) pool.Put(t) pool.Put(zInv2) return c } func (c *curvePoint) Negative(a *curvePoint) { c.x.Set(a.x) c.y.Neg(a.y) c.z.Set(a.z) c.t.SetInt64(0) } �����������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/bn256_test.go��������������������������������0000644�0000153�0000161�00000015050�12321735647�024447� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bn256 import ( "bytes" "crypto/rand" "math/big" "testing" ) func TestGFp2Invert(t *testing.T) { pool := new(bnPool) a := newGFp2(pool) a.x.SetString("23423492374", 10) a.y.SetString("12934872398472394827398470", 10) inv := newGFp2(pool) inv.Invert(a, pool) b := newGFp2(pool).Mul(inv, a, pool) if b.x.Int64() != 0 || b.y.Int64() != 1 { t.Fatalf("bad result for a^-1*a: %s %s", b.x, b.y) } a.Put(pool) b.Put(pool) inv.Put(pool) if c := pool.Count(); c > 0 { t.Errorf("Pool count non-zero: %d\n", c) } } func isZero(n *big.Int) bool { return new(big.Int).Mod(n, p).Int64() == 0 } func isOne(n *big.Int) bool { return new(big.Int).Mod(n, p).Int64() == 1 } func TestGFp6Invert(t *testing.T) { pool := new(bnPool) a := newGFp6(pool) a.x.x.SetString("239487238491", 10) a.x.y.SetString("2356249827341", 10) a.y.x.SetString("082659782", 10) a.y.y.SetString("182703523765", 10) a.z.x.SetString("978236549263", 10) a.z.y.SetString("64893242", 10) inv := newGFp6(pool) inv.Invert(a, pool) b := newGFp6(pool).Mul(inv, a, pool) if !isZero(b.x.x) || !isZero(b.x.y) || !isZero(b.y.x) || !isZero(b.y.y) || !isZero(b.z.x) || !isOne(b.z.y) { t.Fatalf("bad result for a^-1*a: %s", b) } a.Put(pool) b.Put(pool) inv.Put(pool) if c := pool.Count(); c > 0 { t.Errorf("Pool count non-zero: %d\n", c) } } func TestGFp12Invert(t *testing.T) { pool := new(bnPool) a := newGFp12(pool) a.x.x.x.SetString("239846234862342323958623", 10) a.x.x.y.SetString("2359862352529835623", 10) a.x.y.x.SetString("928836523", 10) a.x.y.y.SetString("9856234", 10) a.x.z.x.SetString("235635286", 10) a.x.z.y.SetString("5628392833", 10) a.y.x.x.SetString("252936598265329856238956532167968", 10) a.y.x.y.SetString("23596239865236954178968", 10) a.y.y.x.SetString("95421692834", 10) a.y.y.y.SetString("236548", 10) a.y.z.x.SetString("924523", 10) a.y.z.y.SetString("12954623", 10) inv := newGFp12(pool) inv.Invert(a, pool) b := newGFp12(pool).Mul(inv, a, pool) if !isZero(b.x.x.x) || !isZero(b.x.x.y) || !isZero(b.x.y.x) || !isZero(b.x.y.y) || !isZero(b.x.z.x) || !isZero(b.x.z.y) || !isZero(b.y.x.x) || !isZero(b.y.x.y) || !isZero(b.y.y.x) || !isZero(b.y.y.y) || !isZero(b.y.z.x) || !isOne(b.y.z.y) { t.Fatalf("bad result for a^-1*a: %s", b) } a.Put(pool) b.Put(pool) inv.Put(pool) if c := pool.Count(); c > 0 { t.Errorf("Pool count non-zero: %d\n", c) } } func TestCurveImpl(t *testing.T) { pool := new(bnPool) g := &curvePoint{ pool.Get().SetInt64(1), pool.Get().SetInt64(-2), pool.Get().SetInt64(1), pool.Get().SetInt64(0), } x := pool.Get().SetInt64(32498273234) X := newCurvePoint(pool).Mul(g, x, pool) y := pool.Get().SetInt64(98732423523) Y := newCurvePoint(pool).Mul(g, y, pool) s1 := newCurvePoint(pool).Mul(X, y, pool).MakeAffine(pool) s2 := newCurvePoint(pool).Mul(Y, x, pool).MakeAffine(pool) if s1.x.Cmp(s2.x) != 0 || s2.x.Cmp(s1.x) != 0 { t.Errorf("DH points don't match: (%s, %s) (%s, %s)", s1.x, s1.y, s2.x, s2.y) } pool.Put(x) X.Put(pool) pool.Put(y) Y.Put(pool) s1.Put(pool) s2.Put(pool) g.Put(pool) if c := pool.Count(); c > 0 { t.Errorf("Pool count non-zero: %d\n", c) } } func TestOrderG1(t *testing.T) { g := new(G1).ScalarBaseMult(Order) if !g.p.IsInfinity() { t.Error("G1 has incorrect order") } one := new(G1).ScalarBaseMult(new(big.Int).SetInt64(1)) g.Add(g, one) g.p.MakeAffine(nil) if g.p.x.Cmp(one.p.x) != 0 || g.p.y.Cmp(one.p.y) != 0 { t.Errorf("1+0 != 1 in G1") } } func TestOrderG2(t *testing.T) { g := new(G2).ScalarBaseMult(Order) if !g.p.IsInfinity() { t.Error("G2 has incorrect order") } one := new(G2).ScalarBaseMult(new(big.Int).SetInt64(1)) g.Add(g, one) g.p.MakeAffine(nil) if g.p.x.x.Cmp(one.p.x.x) != 0 || g.p.x.y.Cmp(one.p.x.y) != 0 || g.p.y.x.Cmp(one.p.y.x) != 0 || g.p.y.y.Cmp(one.p.y.y) != 0 { t.Errorf("1+0 != 1 in G2") } } func TestOrderGT(t *testing.T) { gt := Pair(&G1{curveGen}, &G2{twistGen}) g := new(GT).ScalarMult(gt, Order) if !g.p.IsOne() { t.Error("GT has incorrect order") } } func TestBilinearity(t *testing.T) { for i := 0; i < 2; i++ { a, p1, _ := RandomG1(rand.Reader) b, p2, _ := RandomG2(rand.Reader) e1 := Pair(p1, p2) e2 := Pair(&G1{curveGen}, &G2{twistGen}) e2.ScalarMult(e2, a) e2.ScalarMult(e2, b) minusE2 := new(GT).Neg(e2) e1.Add(e1, minusE2) if !e1.p.IsOne() { t.Fatalf("bad pairing result: %s", e1) } } } func TestG1Marshal(t *testing.T) { g := new(G1).ScalarBaseMult(new(big.Int).SetInt64(1)) form := g.Marshal() _, ok := new(G1).Unmarshal(form) if !ok { t.Fatalf("failed to unmarshal") } g.ScalarBaseMult(Order) form = g.Marshal() g2, ok := new(G1).Unmarshal(form) if !ok { t.Fatalf("failed to unmarshal ∞") } if !g2.p.IsInfinity() { t.Fatalf("∞ unmarshaled incorrectly") } } func TestG2Marshal(t *testing.T) { g := new(G2).ScalarBaseMult(new(big.Int).SetInt64(1)) form := g.Marshal() _, ok := new(G2).Unmarshal(form) if !ok { t.Fatalf("failed to unmarshal") } g.ScalarBaseMult(Order) form = g.Marshal() g2, ok := new(G2).Unmarshal(form) if !ok { t.Fatalf("failed to unmarshal ∞") } if !g2.p.IsInfinity() { t.Fatalf("∞ unmarshaled incorrectly") } } func TestG1Identity(t *testing.T) { g := new(G1).ScalarBaseMult(new(big.Int).SetInt64(0)) if !g.p.IsInfinity() { t.Error("failure") } } func TestG2Identity(t *testing.T) { g := new(G2).ScalarBaseMult(new(big.Int).SetInt64(0)) if !g.p.IsInfinity() { t.Error("failure") } } func TestTripartiteDiffieHellman(t *testing.T) { a, _ := rand.Int(rand.Reader, Order) b, _ := rand.Int(rand.Reader, Order) c, _ := rand.Int(rand.Reader, Order) pa, _ := new(G1).Unmarshal(new(G1).ScalarBaseMult(a).Marshal()) qa, _ := new(G2).Unmarshal(new(G2).ScalarBaseMult(a).Marshal()) pb, _ := new(G1).Unmarshal(new(G1).ScalarBaseMult(b).Marshal()) qb, _ := new(G2).Unmarshal(new(G2).ScalarBaseMult(b).Marshal()) pc, _ := new(G1).Unmarshal(new(G1).ScalarBaseMult(c).Marshal()) qc, _ := new(G2).Unmarshal(new(G2).ScalarBaseMult(c).Marshal()) k1 := Pair(pb, qc) k1.ScalarMult(k1, a) k1Bytes := k1.Marshal() k2 := Pair(pc, qa) k2.ScalarMult(k2, b) k2Bytes := k2.Marshal() k3 := Pair(pa, qb) k3.ScalarMult(k3, c) k3Bytes := k3.Marshal() if !bytes.Equal(k1Bytes, k2Bytes) || !bytes.Equal(k2Bytes, k3Bytes) { t.Errorf("keys didn't agree") } } func BenchmarkPairing(b *testing.B) { for i := 0; i < b.N; i++ { Pair(&G1{curveGen}, &G2{twistGen}) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/optate.go������������������������������������0000644�0000153�0000161�00000020733�12321735647�024054� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bn256 func lineFunctionAdd(r, p *twistPoint, q *curvePoint, r2 *gfP2, pool *bnPool) (a, b, c *gfP2, rOut *twistPoint) { // See the mixed addition algorithm from "Faster Computation of the // Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf B := newGFp2(pool).Mul(p.x, r.t, pool) D := newGFp2(pool).Add(p.y, r.z) D.Square(D, pool) D.Sub(D, r2) D.Sub(D, r.t) D.Mul(D, r.t, pool) H := newGFp2(pool).Sub(B, r.x) I := newGFp2(pool).Square(H, pool) E := newGFp2(pool).Add(I, I) E.Add(E, E) J := newGFp2(pool).Mul(H, E, pool) L1 := newGFp2(pool).Sub(D, r.y) L1.Sub(L1, r.y) V := newGFp2(pool).Mul(r.x, E, pool) rOut = newTwistPoint(pool) rOut.x.Square(L1, pool) rOut.x.Sub(rOut.x, J) rOut.x.Sub(rOut.x, V) rOut.x.Sub(rOut.x, V) rOut.z.Add(r.z, H) rOut.z.Square(rOut.z, pool) rOut.z.Sub(rOut.z, r.t) rOut.z.Sub(rOut.z, I) t := newGFp2(pool).Sub(V, rOut.x) t.Mul(t, L1, pool) t2 := newGFp2(pool).Mul(r.y, J, pool) t2.Add(t2, t2) rOut.y.Sub(t, t2) rOut.t.Square(rOut.z, pool) t.Add(p.y, rOut.z) t.Square(t, pool) t.Sub(t, r2) t.Sub(t, rOut.t) t2.Mul(L1, p.x, pool) t2.Add(t2, t2) a = newGFp2(pool) a.Sub(t2, t) c = newGFp2(pool) c.MulScalar(rOut.z, q.y) c.Add(c, c) b = newGFp2(pool) b.SetZero() b.Sub(b, L1) b.MulScalar(b, q.x) b.Add(b, b) B.Put(pool) D.Put(pool) H.Put(pool) I.Put(pool) E.Put(pool) J.Put(pool) L1.Put(pool) V.Put(pool) t.Put(pool) t2.Put(pool) return } func lineFunctionDouble(r *twistPoint, q *curvePoint, pool *bnPool) (a, b, c *gfP2, rOut *twistPoint) { // See the doubling algorithm for a=0 from "Faster Computation of the // Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf A := newGFp2(pool).Square(r.x, pool) B := newGFp2(pool).Square(r.y, pool) C := newGFp2(pool).Square(B, pool) D := newGFp2(pool).Add(r.x, B) D.Square(D, pool) D.Sub(D, A) D.Sub(D, C) D.Add(D, D) E := newGFp2(pool).Add(A, A) E.Add(E, A) G := newGFp2(pool).Square(E, pool) rOut = newTwistPoint(pool) rOut.x.Sub(G, D) rOut.x.Sub(rOut.x, D) rOut.z.Add(r.y, r.z) rOut.z.Square(rOut.z, pool) rOut.z.Sub(rOut.z, B) rOut.z.Sub(rOut.z, r.t) rOut.y.Sub(D, rOut.x) rOut.y.Mul(rOut.y, E, pool) t := newGFp2(pool).Add(C, C) t.Add(t, t) t.Add(t, t) rOut.y.Sub(rOut.y, t) rOut.t.Square(rOut.z, pool) t.Mul(E, r.t, pool) t.Add(t, t) b = newGFp2(pool) b.SetZero() b.Sub(b, t) b.MulScalar(b, q.x) a = newGFp2(pool) a.Add(r.x, E) a.Square(a, pool) a.Sub(a, A) a.Sub(a, G) t.Add(B, B) t.Add(t, t) a.Sub(a, t) c = newGFp2(pool) c.Mul(rOut.z, r.t, pool) c.Add(c, c) c.MulScalar(c, q.y) A.Put(pool) B.Put(pool) C.Put(pool) D.Put(pool) E.Put(pool) G.Put(pool) t.Put(pool) return } func mulLine(ret *gfP12, a, b, c *gfP2, pool *bnPool) { a2 := newGFp6(pool) a2.x.SetZero() a2.y.Set(a) a2.z.Set(b) a2.Mul(a2, ret.x, pool) t3 := newGFp6(pool).MulScalar(ret.y, c, pool) t := newGFp2(pool) t.Add(b, c) t2 := newGFp6(pool) t2.x.SetZero() t2.y.Set(a) t2.z.Set(t) ret.x.Add(ret.x, ret.y) ret.y.Set(t3) ret.x.Mul(ret.x, t2, pool) ret.x.Sub(ret.x, a2) ret.x.Sub(ret.x, ret.y) a2.MulTau(a2, pool) ret.y.Add(ret.y, a2) a2.Put(pool) t3.Put(pool) t2.Put(pool) t.Put(pool) } // sixuPlus2NAF is 6u+2 in non-adjacent form. var sixuPlus2NAF = []int8{0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, -1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, -1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 1} // miller implements the Miller loop for calculating the Optimal Ate pairing. // See algorithm 1 from http://cryptojedi.org/papers/dclxvi-20100714.pdf func miller(q *twistPoint, p *curvePoint, pool *bnPool) *gfP12 { ret := newGFp12(pool) ret.SetOne() aAffine := newTwistPoint(pool) aAffine.Set(q) aAffine.MakeAffine(pool) bAffine := newCurvePoint(pool) bAffine.Set(p) bAffine.MakeAffine(pool) minusA := newTwistPoint(pool) minusA.Negative(aAffine, pool) r := newTwistPoint(pool) r.Set(aAffine) r2 := newGFp2(pool) r2.Square(aAffine.y, pool) for i := len(sixuPlus2NAF) - 1; i > 0; i-- { a, b, c, newR := lineFunctionDouble(r, bAffine, pool) if i != len(sixuPlus2NAF)-1 { ret.Square(ret, pool) } mulLine(ret, a, b, c, pool) a.Put(pool) b.Put(pool) c.Put(pool) r.Put(pool) r = newR switch sixuPlus2NAF[i-1] { case 1: a, b, c, newR = lineFunctionAdd(r, aAffine, bAffine, r2, pool) case -1: a, b, c, newR = lineFunctionAdd(r, minusA, bAffine, r2, pool) default: continue } mulLine(ret, a, b, c, pool) a.Put(pool) b.Put(pool) c.Put(pool) r.Put(pool) r = newR } // In order to calculate Q1 we have to convert q from the sextic twist // to the full GF(p^12) group, apply the Frobenius there, and convert // back. // // The twist isomorphism is (x', y') -> (xω², yω³). If we consider just // x for a moment, then after applying the Frobenius, we have x̄ω^(2p) // where xÌ„ is the conjugate of x. If we are going to apply the inverse // isomorphism we need a value with a single coefficient of ω² so we // rewrite this as x̄ω^(2p-2)ω². ξⶠ= ω and, due to the construction of // p, 2p-2 is a multiple of six. Therefore we can rewrite as // x̄ξ^((p-1)/3)ω² and applying the inverse isomorphism eliminates the // ω². // // A similar argument can be made for the y value. q1 := newTwistPoint(pool) q1.x.Conjugate(aAffine.x) q1.x.Mul(q1.x, xiToPMinus1Over3, pool) q1.y.Conjugate(aAffine.y) q1.y.Mul(q1.y, xiToPMinus1Over2, pool) q1.z.SetOne() q1.t.SetOne() // For Q2 we are applying the p² Frobenius. The two conjugations cancel // out and we are left only with the factors from the isomorphism. In // the case of x, we end up with a pure number which is why // xiToPSquaredMinus1Over3 is ∈ GF(p). With y we get a factor of -1. We // ignore this to end up with -Q2. minusQ2 := newTwistPoint(pool) minusQ2.x.MulScalar(aAffine.x, xiToPSquaredMinus1Over3) minusQ2.y.Set(aAffine.y) minusQ2.z.SetOne() minusQ2.t.SetOne() r2.Square(q1.y, pool) a, b, c, newR := lineFunctionAdd(r, q1, bAffine, r2, pool) mulLine(ret, a, b, c, pool) a.Put(pool) b.Put(pool) c.Put(pool) r.Put(pool) r = newR r2.Square(minusQ2.y, pool) a, b, c, newR = lineFunctionAdd(r, minusQ2, bAffine, r2, pool) mulLine(ret, a, b, c, pool) a.Put(pool) b.Put(pool) c.Put(pool) r.Put(pool) r = newR aAffine.Put(pool) bAffine.Put(pool) minusA.Put(pool) r.Put(pool) r2.Put(pool) return ret } // finalExponentiation computes the (p¹²-1)/Order-th power of an element of // GF(p¹²) to obtain an element of GT (steps 13-15 of algorithm 1 from // http://cryptojedi.org/papers/dclxvi-20100714.pdf) func finalExponentiation(in *gfP12, pool *bnPool) *gfP12 { t1 := newGFp12(pool) // This is the p^6-Frobenius t1.x.Negative(in.x) t1.y.Set(in.y) inv := newGFp12(pool) inv.Invert(in, pool) t1.Mul(t1, inv, pool) t2 := newGFp12(pool).FrobeniusP2(t1, pool) t1.Mul(t1, t2, pool) fp := newGFp12(pool).Frobenius(t1, pool) fp2 := newGFp12(pool).FrobeniusP2(t1, pool) fp3 := newGFp12(pool).Frobenius(fp2, pool) fu, fu2, fu3 := newGFp12(pool), newGFp12(pool), newGFp12(pool) fu.Exp(t1, u, pool) fu2.Exp(fu, u, pool) fu3.Exp(fu2, u, pool) y3 := newGFp12(pool).Frobenius(fu, pool) fu2p := newGFp12(pool).Frobenius(fu2, pool) fu3p := newGFp12(pool).Frobenius(fu3, pool) y2 := newGFp12(pool).FrobeniusP2(fu2, pool) y0 := newGFp12(pool) y0.Mul(fp, fp2, pool) y0.Mul(y0, fp3, pool) y1, y4, y5 := newGFp12(pool), newGFp12(pool), newGFp12(pool) y1.Conjugate(t1) y5.Conjugate(fu2) y3.Conjugate(y3) y4.Mul(fu, fu2p, pool) y4.Conjugate(y4) y6 := newGFp12(pool) y6.Mul(fu3, fu3p, pool) y6.Conjugate(y6) t0 := newGFp12(pool) t0.Square(y6, pool) t0.Mul(t0, y4, pool) t0.Mul(t0, y5, pool) t1.Mul(y3, y5, pool) t1.Mul(t1, t0, pool) t0.Mul(t0, y2, pool) t1.Square(t1, pool) t1.Mul(t1, t0, pool) t1.Square(t1, pool) t0.Mul(t1, y1, pool) t1.Mul(t1, y0, pool) t0.Square(t0, pool) t0.Mul(t0, t1, pool) inv.Put(pool) t1.Put(pool) t2.Put(pool) fp.Put(pool) fp2.Put(pool) fp3.Put(pool) fu.Put(pool) fu2.Put(pool) fu3.Put(pool) fu2p.Put(pool) fu3p.Put(pool) y0.Put(pool) y1.Put(pool) y2.Put(pool) y3.Put(pool) y4.Put(pool) y5.Put(pool) y6.Put(pool) return t0 } func optimalAte(a *twistPoint, b *curvePoint, pool *bnPool) *gfP12 { e := miller(a, b, pool) ret := finalExponentiation(e, pool) e.Put(pool) if a.IsInfinity() || b.IsInfinity() { ret.SetOne() } return ret } �������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/example_test.go������������������������������0000644�0000153�0000161�00000002213�12321735647�025243� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bn256 import ( "crypto/rand" ) func ExamplePair() { // This implements the tripartite Diffie-Hellman algorithm from "A One // Round Protocol for Tripartite Diffie-Hellman", A. Joux. // http://www.springerlink.com/content/cddc57yyva0hburb/fulltext.pdf // Each of three parties, a, b and c, generate a private value. a, _ := rand.Int(rand.Reader, Order) b, _ := rand.Int(rand.Reader, Order) c, _ := rand.Int(rand.Reader, Order) // Then each party calculates gâ‚ and gâ‚‚ times their private value. pa := new(G1).ScalarBaseMult(a) qa := new(G2).ScalarBaseMult(a) pb := new(G1).ScalarBaseMult(b) qb := new(G2).ScalarBaseMult(b) pc := new(G1).ScalarBaseMult(c) qc := new(G2).ScalarBaseMult(c) // Now each party exchanges its public values with the other two and // all parties can calculate the shared key. k1 := Pair(pb, qc) k1.ScalarMult(k1, a) k2 := Pair(pc, qa) k2.ScalarMult(k2, b) k3 := Pair(pa, qb) k3.ScalarMult(k3, c) // k1, k2 and k3 will all be equal. } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/gfp12.go�������������������������������������0000644�0000153�0000161�00000007075�12321735647�023503� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bn256 // For details of the algorithms used, see "Multiplication and Squaring on // Pairing-Friendly Fields, Devegili et al. // http://eprint.iacr.org/2006/471.pdf. import ( "math/big" ) // gfP12 implements the field of size p¹² as a quadratic extension of gfP6 // where ω²=Ï„. type gfP12 struct { x, y *gfP6 // value is xω + y } func newGFp12(pool *bnPool) *gfP12 { return &gfP12{newGFp6(pool), newGFp6(pool)} } func (e *gfP12) String() string { return "(" + e.x.String() + "," + e.y.String() + ")" } func (e *gfP12) Put(pool *bnPool) { e.x.Put(pool) e.y.Put(pool) } func (e *gfP12) Set(a *gfP12) *gfP12 { e.x.Set(a.x) e.y.Set(a.y) return e } func (e *gfP12) SetZero() *gfP12 { e.x.SetZero() e.y.SetZero() return e } func (e *gfP12) SetOne() *gfP12 { e.x.SetZero() e.y.SetOne() return e } func (e *gfP12) Minimal() { e.x.Minimal() e.y.Minimal() } func (e *gfP12) IsZero() bool { e.Minimal() return e.x.IsZero() && e.y.IsZero() } func (e *gfP12) IsOne() bool { e.Minimal() return e.x.IsZero() && e.y.IsOne() } func (e *gfP12) Conjugate(a *gfP12) *gfP12 { e.x.Negative(a.x) e.y.Set(a.y) return a } func (e *gfP12) Negative(a *gfP12) *gfP12 { e.x.Negative(a.x) e.y.Negative(a.y) return e } // Frobenius computes (xω+y)^p = x^p ω·ξ^((p-1)/6) + y^p func (e *gfP12) Frobenius(a *gfP12, pool *bnPool) *gfP12 { e.x.Frobenius(a.x, pool) e.y.Frobenius(a.y, pool) e.x.MulScalar(e.x, xiToPMinus1Over6, pool) return e } // FrobeniusP2 computes (xω+y)^p² = x^p² ω·ξ^((p²-1)/6) + y^p² func (e *gfP12) FrobeniusP2(a *gfP12, pool *bnPool) *gfP12 { e.x.FrobeniusP2(a.x) e.x.MulGFP(e.x, xiToPSquaredMinus1Over6) e.y.FrobeniusP2(a.y) return e } func (e *gfP12) Add(a, b *gfP12) *gfP12 { e.x.Add(a.x, b.x) e.y.Add(a.y, b.y) return e } func (e *gfP12) Sub(a, b *gfP12) *gfP12 { e.x.Sub(a.x, b.x) e.y.Sub(a.y, b.y) return e } func (e *gfP12) Mul(a, b *gfP12, pool *bnPool) *gfP12 { tx := newGFp6(pool) tx.Mul(a.x, b.y, pool) t := newGFp6(pool) t.Mul(b.x, a.y, pool) tx.Add(tx, t) ty := newGFp6(pool) ty.Mul(a.y, b.y, pool) t.Mul(a.x, b.x, pool) t.MulTau(t, pool) e.y.Add(ty, t) e.x.Set(tx) tx.Put(pool) ty.Put(pool) t.Put(pool) return e } func (e *gfP12) MulScalar(a *gfP12, b *gfP6, pool *bnPool) *gfP12 { e.x.Mul(e.x, b, pool) e.y.Mul(e.y, b, pool) return e } func (c *gfP12) Exp(a *gfP12, power *big.Int, pool *bnPool) *gfP12 { sum := newGFp12(pool) sum.SetOne() t := newGFp12(pool) for i := power.BitLen() - 1; i >= 0; i-- { t.Square(sum, pool) if power.Bit(i) != 0 { sum.Mul(t, a, pool) } else { sum.Set(t) } } c.Set(sum) sum.Put(pool) t.Put(pool) return c } func (e *gfP12) Square(a *gfP12, pool *bnPool) *gfP12 { // Complex squaring algorithm v0 := newGFp6(pool) v0.Mul(a.x, a.y, pool) t := newGFp6(pool) t.MulTau(a.x, pool) t.Add(a.y, t) ty := newGFp6(pool) ty.Add(a.x, a.y) ty.Mul(ty, t, pool) ty.Sub(ty, v0) t.MulTau(v0, pool) ty.Sub(ty, t) e.y.Set(ty) e.x.Double(v0) v0.Put(pool) t.Put(pool) ty.Put(pool) return e } func (e *gfP12) Invert(a *gfP12, pool *bnPool) *gfP12 { // See "Implementing cryptographic pairings", M. Scott, section 3.2. // ftp://136.206.11.249/pub/crypto/pairings.pdf t1 := newGFp6(pool) t2 := newGFp6(pool) t1.Square(a.x, pool) t2.Square(a.y, pool) t1.MulTau(t1, pool) t1.Sub(t2, t1) t2.Invert(t1, pool) e.x.Negative(a.x) e.y.Set(a.y) e.MulScalar(e, t2, pool) t1.Put(pool) t2.Put(pool) return e } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/constants.go���������������������������������0000644�0000153�0000161�00000004650�12321735647�024574� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bn256 import ( "math/big" ) func bigFromBase10(s string) *big.Int { n, _ := new(big.Int).SetString(s, 10) return n } // u is the BN parameter that determines the prime: 1868033³. var u = bigFromBase10("6518589491078791937") // p is a prime over which we form a basic field: 36uâ´+36u³+24u³+6u+1. var p = bigFromBase10("65000549695646603732796438742359905742825358107623003571877145026864184071783") // Order is the number of elements in both Gâ‚ and Gâ‚‚: 36uâ´+36u³+18u³+6u+1. var Order = bigFromBase10("65000549695646603732796438742359905742570406053903786389881062969044166799969") // xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+3. var xiToPMinus1Over6 = &gfP2{bigFromBase10("8669379979083712429711189836753509758585994370025260553045152614783263110636"), bigFromBase10("19998038925833620163537568958541907098007303196759855091367510456613536016040")} // xiToPMinus1Over3 is ξ^((p-1)/3) where ξ = i+3. var xiToPMinus1Over3 = &gfP2{bigFromBase10("26098034838977895781559542626833399156321265654106457577426020397262786167059"), bigFromBase10("15931493369629630809226283458085260090334794394361662678240713231519278691715")} // xiToPMinus1Over2 is ξ^((p-1)/2) where ξ = i+3. var xiToPMinus1Over2 = &gfP2{bigFromBase10("50997318142241922852281555961173165965672272825141804376761836765206060036244"), bigFromBase10("38665955945962842195025998234511023902832543644254935982879660597356748036009")} // xiToPSquaredMinus1Over3 is ξ^((p²-1)/3) where ξ = i+3. var xiToPSquaredMinus1Over3 = bigFromBase10("65000549695646603727810655408050771481677621702948236658134783353303381437752") // xiTo2PSquaredMinus2Over3 is ξ^((2p²-2)/3) where ξ = i+3 (a cubic root of unity, mod p). var xiTo2PSquaredMinus2Over3 = bigFromBase10("4985783334309134261147736404674766913742361673560802634030") // xiToPSquaredMinus1Over6 is ξ^((1p²-1)/6) where ξ = i+3 (a cubic root of -1, mod p). var xiToPSquaredMinus1Over6 = bigFromBase10("65000549695646603727810655408050771481677621702948236658134783353303381437753") // xiTo2PMinus2Over3 is ξ^((2p-2)/3) where ξ = i+3. var xiTo2PMinus2Over3 = &gfP2{bigFromBase10("19885131339612776214803633203834694332692106372356013117629940868870585019582"), bigFromBase10("21645619881471562101905880913352894726728173167203616652430647841922248593627")} ����������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/gfp6.go��������������������������������������0000644�0000153�0000161�00000013221�12321735647�023414� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bn256 // For details of the algorithms used, see "Multiplication and Squaring on // Pairing-Friendly Fields, Devegili et al. // http://eprint.iacr.org/2006/471.pdf. import ( "math/big" ) // gfP6 implements the field of size pâ¶ as a cubic extension of gfP2 where τ³=ξ // and ξ=i+3. type gfP6 struct { x, y, z *gfP2 // value is xτ² + yÏ„ + z } func newGFp6(pool *bnPool) *gfP6 { return &gfP6{newGFp2(pool), newGFp2(pool), newGFp2(pool)} } func (e *gfP6) String() string { return "(" + e.x.String() + "," + e.y.String() + "," + e.z.String() + ")" } func (e *gfP6) Put(pool *bnPool) { e.x.Put(pool) e.y.Put(pool) e.z.Put(pool) } func (e *gfP6) Set(a *gfP6) *gfP6 { e.x.Set(a.x) e.y.Set(a.y) e.z.Set(a.z) return e } func (e *gfP6) SetZero() *gfP6 { e.x.SetZero() e.y.SetZero() e.z.SetZero() return e } func (e *gfP6) SetOne() *gfP6 { e.x.SetZero() e.y.SetZero() e.z.SetOne() return e } func (e *gfP6) Minimal() { e.x.Minimal() e.y.Minimal() e.z.Minimal() } func (e *gfP6) IsZero() bool { return e.x.IsZero() && e.y.IsZero() && e.z.IsZero() } func (e *gfP6) IsOne() bool { return e.x.IsZero() && e.y.IsZero() && e.z.IsOne() } func (e *gfP6) Negative(a *gfP6) *gfP6 { e.x.Negative(a.x) e.y.Negative(a.y) e.z.Negative(a.z) return e } func (e *gfP6) Frobenius(a *gfP6, pool *bnPool) *gfP6 { e.x.Conjugate(a.x) e.y.Conjugate(a.y) e.z.Conjugate(a.z) e.x.Mul(e.x, xiTo2PMinus2Over3, pool) e.y.Mul(e.y, xiToPMinus1Over3, pool) return e } // FrobeniusP2 computes (xτ²+yÏ„+z)^(p²) = xÏ„^(2p²) + yÏ„^(p²) + z func (e *gfP6) FrobeniusP2(a *gfP6) *gfP6 { // Ï„^(2p²) = τ²τ^(2p²-2) = τ²ξ^((2p²-2)/3) e.x.MulScalar(a.x, xiTo2PSquaredMinus2Over3) // Ï„^(p²) = ττ^(p²-1) = τξ^((p²-1)/3) e.y.MulScalar(a.y, xiToPSquaredMinus1Over3) e.z.Set(a.z) return e } func (e *gfP6) Add(a, b *gfP6) *gfP6 { e.x.Add(a.x, b.x) e.y.Add(a.y, b.y) e.z.Add(a.z, b.z) return e } func (e *gfP6) Sub(a, b *gfP6) *gfP6 { e.x.Sub(a.x, b.x) e.y.Sub(a.y, b.y) e.z.Sub(a.z, b.z) return e } func (e *gfP6) Double(a *gfP6) *gfP6 { e.x.Double(a.x) e.y.Double(a.y) e.z.Double(a.z) return e } func (e *gfP6) Mul(a, b *gfP6, pool *bnPool) *gfP6 { // "Multiplication and Squaring on Pairing-Friendly Fields" // Section 4, Karatsuba method. // http://eprint.iacr.org/2006/471.pdf v0 := newGFp2(pool) v0.Mul(a.z, b.z, pool) v1 := newGFp2(pool) v1.Mul(a.y, b.y, pool) v2 := newGFp2(pool) v2.Mul(a.x, b.x, pool) t0 := newGFp2(pool) t0.Add(a.x, a.y) t1 := newGFp2(pool) t1.Add(b.x, b.y) tz := newGFp2(pool) tz.Mul(t0, t1, pool) tz.Sub(tz, v1) tz.Sub(tz, v2) tz.MulXi(tz, pool) tz.Add(tz, v0) t0.Add(a.y, a.z) t1.Add(b.y, b.z) ty := newGFp2(pool) ty.Mul(t0, t1, pool) ty.Sub(ty, v0) ty.Sub(ty, v1) t0.MulXi(v2, pool) ty.Add(ty, t0) t0.Add(a.x, a.z) t1.Add(b.x, b.z) tx := newGFp2(pool) tx.Mul(t0, t1, pool) tx.Sub(tx, v0) tx.Add(tx, v1) tx.Sub(tx, v2) e.x.Set(tx) e.y.Set(ty) e.z.Set(tz) t0.Put(pool) t1.Put(pool) tx.Put(pool) ty.Put(pool) tz.Put(pool) v0.Put(pool) v1.Put(pool) v2.Put(pool) return e } func (e *gfP6) MulScalar(a *gfP6, b *gfP2, pool *bnPool) *gfP6 { e.x.Mul(a.x, b, pool) e.y.Mul(a.y, b, pool) e.z.Mul(a.z, b, pool) return e } func (e *gfP6) MulGFP(a *gfP6, b *big.Int) *gfP6 { e.x.MulScalar(a.x, b) e.y.MulScalar(a.y, b) e.z.MulScalar(a.z, b) return e } // MulTau computes τ·(aτ²+bÏ„+c) = bτ²+cÏ„+aξ func (e *gfP6) MulTau(a *gfP6, pool *bnPool) { tz := newGFp2(pool) tz.MulXi(a.x, pool) ty := newGFp2(pool) ty.Set(a.y) e.y.Set(a.z) e.x.Set(ty) e.z.Set(tz) tz.Put(pool) ty.Put(pool) } func (e *gfP6) Square(a *gfP6, pool *bnPool) *gfP6 { v0 := newGFp2(pool).Square(a.z, pool) v1 := newGFp2(pool).Square(a.y, pool) v2 := newGFp2(pool).Square(a.x, pool) c0 := newGFp2(pool).Add(a.x, a.y) c0.Square(c0, pool) c0.Sub(c0, v1) c0.Sub(c0, v2) c0.MulXi(c0, pool) c0.Add(c0, v0) c1 := newGFp2(pool).Add(a.y, a.z) c1.Square(c1, pool) c1.Sub(c1, v0) c1.Sub(c1, v1) xiV2 := newGFp2(pool).MulXi(v2, pool) c1.Add(c1, xiV2) c2 := newGFp2(pool).Add(a.x, a.z) c2.Square(c2, pool) c2.Sub(c2, v0) c2.Add(c2, v1) c2.Sub(c2, v2) e.x.Set(c2) e.y.Set(c1) e.z.Set(c0) v0.Put(pool) v1.Put(pool) v2.Put(pool) c0.Put(pool) c1.Put(pool) c2.Put(pool) xiV2.Put(pool) return e } func (e *gfP6) Invert(a *gfP6, pool *bnPool) *gfP6 { // See "Implementing cryptographic pairings", M. Scott, section 3.2. // ftp://136.206.11.249/pub/crypto/pairings.pdf // Here we can give a short explanation of how it works: let j be a cubic root of // unity in GF(p²) so that 1+j+j²=0. // Then (xτ² + yÏ„ + z)(xj²τ² + yjÏ„ + z)(xjτ² + yj²τ + z) // = (xτ² + yÏ„ + z)(Cτ²+BÏ„+A) // = (x³ξ²+y³ξ+z³-3ξxyz) = F is an element of the base field (the norm). // // On the other hand (xj²τ² + yjÏ„ + z)(xjτ² + yj²τ + z) // = τ²(y²-ξxz) + Ï„(ξx²-yz) + (z²-ξxy) // // So that's why A = (z²-ξxy), B = (ξx²-yz), C = (y²-ξxz) t1 := newGFp2(pool) A := newGFp2(pool) A.Square(a.z, pool) t1.Mul(a.x, a.y, pool) t1.MulXi(t1, pool) A.Sub(A, t1) B := newGFp2(pool) B.Square(a.x, pool) B.MulXi(B, pool) t1.Mul(a.y, a.z, pool) B.Sub(B, t1) C := newGFp2(pool) C.Square(a.y, pool) t1.Mul(a.x, a.z, pool) C.Sub(C, t1) F := newGFp2(pool) F.Mul(C, a.y, pool) F.MulXi(F, pool) t1.Mul(A, a.z, pool) F.Add(F, t1) t1.Mul(B, a.x, pool) t1.MulXi(t1, pool) F.Add(F, t1) F.Invert(F, pool) e.x.Mul(C, F, pool) e.y.Mul(B, F, pool) e.z.Mul(A, F, pool) t1.Put(pool) A.Put(pool) B.Put(pool) C.Put(pool) F.Put(pool) return e } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/twist.go�������������������������������������0000644�0000153�0000161�00000012272�12321735647�023731� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bn256 import ( "math/big" ) // twistPoint implements the elliptic curve y²=x³+3/ξ over GF(p²). Points are // kept in Jacobian form and t=z² when valid. The group Gâ‚‚ is the set of // n-torsion points of this curve over GF(p²) (where n = Order) type twistPoint struct { x, y, z, t *gfP2 } var twistB = &gfP2{ bigFromBase10("6500054969564660373279643874235990574282535810762300357187714502686418407178"), bigFromBase10("45500384786952622612957507119651934019977750675336102500314001518804928850249"), } // twistGen is the generator of group Gâ‚‚. var twistGen = &twistPoint{ &gfP2{ bigFromBase10("21167961636542580255011770066570541300993051739349375019639421053990175267184"), bigFromBase10("64746500191241794695844075326670126197795977525365406531717464316923369116492"), }, &gfP2{ bigFromBase10("20666913350058776956210519119118544732556678129809273996262322366050359951122"), bigFromBase10("17778617556404439934652658462602675281523610326338642107814333856843981424549"), }, &gfP2{ bigFromBase10("0"), bigFromBase10("1"), }, &gfP2{ bigFromBase10("0"), bigFromBase10("1"), }, } func newTwistPoint(pool *bnPool) *twistPoint { return &twistPoint{ newGFp2(pool), newGFp2(pool), newGFp2(pool), newGFp2(pool), } } func (c *twistPoint) String() string { return "(" + c.x.String() + ", " + c.y.String() + ", " + c.z.String() + ")" } func (c *twistPoint) Put(pool *bnPool) { c.x.Put(pool) c.y.Put(pool) c.z.Put(pool) c.t.Put(pool) } func (c *twistPoint) Set(a *twistPoint) { c.x.Set(a.x) c.y.Set(a.y) c.z.Set(a.z) c.t.Set(a.t) } // IsOnCurve returns true iff c is on the curve where c must be in affine form. func (c *twistPoint) IsOnCurve() bool { pool := new(bnPool) yy := newGFp2(pool).Square(c.y, pool) xxx := newGFp2(pool).Square(c.x, pool) xxx.Mul(xxx, c.x, pool) yy.Sub(yy, xxx) yy.Sub(yy, twistB) yy.Minimal() return yy.x.Sign() == 0 && yy.y.Sign() == 0 } func (c *twistPoint) SetInfinity() { c.z.SetZero() } func (c *twistPoint) IsInfinity() bool { return c.z.IsZero() } func (c *twistPoint) Add(a, b *twistPoint, pool *bnPool) { // For additional comments, see the same function in curve.go. if a.IsInfinity() { c.Set(b) return } if b.IsInfinity() { c.Set(a) return } // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3 z1z1 := newGFp2(pool).Square(a.z, pool) z2z2 := newGFp2(pool).Square(b.z, pool) u1 := newGFp2(pool).Mul(a.x, z2z2, pool) u2 := newGFp2(pool).Mul(b.x, z1z1, pool) t := newGFp2(pool).Mul(b.z, z2z2, pool) s1 := newGFp2(pool).Mul(a.y, t, pool) t.Mul(a.z, z1z1, pool) s2 := newGFp2(pool).Mul(b.y, t, pool) h := newGFp2(pool).Sub(u2, u1) xEqual := h.IsZero() t.Add(h, h) i := newGFp2(pool).Square(t, pool) j := newGFp2(pool).Mul(h, i, pool) t.Sub(s2, s1) yEqual := t.IsZero() if xEqual && yEqual { c.Double(a, pool) return } r := newGFp2(pool).Add(t, t) v := newGFp2(pool).Mul(u1, i, pool) t4 := newGFp2(pool).Square(r, pool) t.Add(v, v) t6 := newGFp2(pool).Sub(t4, j) c.x.Sub(t6, t) t.Sub(v, c.x) // t7 t4.Mul(s1, j, pool) // t8 t6.Add(t4, t4) // t9 t4.Mul(r, t, pool) // t10 c.y.Sub(t4, t6) t.Add(a.z, b.z) // t11 t4.Square(t, pool) // t12 t.Sub(t4, z1z1) // t13 t4.Sub(t, z2z2) // t14 c.z.Mul(t4, h, pool) z1z1.Put(pool) z2z2.Put(pool) u1.Put(pool) u2.Put(pool) t.Put(pool) s1.Put(pool) s2.Put(pool) h.Put(pool) i.Put(pool) j.Put(pool) r.Put(pool) v.Put(pool) t4.Put(pool) t6.Put(pool) } func (c *twistPoint) Double(a *twistPoint, pool *bnPool) { // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3 A := newGFp2(pool).Square(a.x, pool) B := newGFp2(pool).Square(a.y, pool) C := newGFp2(pool).Square(B, pool) t := newGFp2(pool).Add(a.x, B) t2 := newGFp2(pool).Square(t, pool) t.Sub(t2, A) t2.Sub(t, C) d := newGFp2(pool).Add(t2, t2) t.Add(A, A) e := newGFp2(pool).Add(t, A) f := newGFp2(pool).Square(e, pool) t.Add(d, d) c.x.Sub(f, t) t.Add(C, C) t2.Add(t, t) t.Add(t2, t2) c.y.Sub(d, c.x) t2.Mul(e, c.y, pool) c.y.Sub(t2, t) t.Mul(a.y, a.z, pool) c.z.Add(t, t) A.Put(pool) B.Put(pool) C.Put(pool) t.Put(pool) t2.Put(pool) d.Put(pool) e.Put(pool) f.Put(pool) } func (c *twistPoint) Mul(a *twistPoint, scalar *big.Int, pool *bnPool) *twistPoint { sum := newTwistPoint(pool) sum.SetInfinity() t := newTwistPoint(pool) for i := scalar.BitLen(); i >= 0; i-- { t.Double(sum, pool) if scalar.Bit(i) != 0 { sum.Add(t, a, pool) } else { sum.Set(t) } } c.Set(sum) sum.Put(pool) t.Put(pool) return c } func (c *twistPoint) MakeAffine(pool *bnPool) *twistPoint { if c.z.IsOne() { return c } zInv := newGFp2(pool).Invert(c.z, pool) t := newGFp2(pool).Mul(c.y, zInv, pool) zInv2 := newGFp2(pool).Square(zInv, pool) c.y.Mul(t, zInv2, pool) t.Mul(c.x, zInv2, pool) c.x.Set(t) c.z.SetOne() c.t.SetOne() zInv.Put(pool) t.Put(pool) zInv2.Put(pool) return c } func (c *twistPoint) Negative(a *twistPoint, pool *bnPool) { c.x.Set(a.x) c.y.SetZero() c.y.Sub(c.y, a.y) c.z.Set(a.z) c.t.SetZero() } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/gfp2.go��������������������������������������0000644�0000153�0000161�00000007320�12321735647�023413� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bn256 // For details of the algorithms used, see "Multiplication and Squaring on // Pairing-Friendly Fields, Devegili et al. // http://eprint.iacr.org/2006/471.pdf. import ( "math/big" ) // gfP2 implements a field of size p² as a quadratic extension of the base // field where i²=-1. type gfP2 struct { x, y *big.Int // value is xi+y. } func newGFp2(pool *bnPool) *gfP2 { return &gfP2{pool.Get(), pool.Get()} } func (e *gfP2) String() string { x := new(big.Int).Mod(e.x, p) y := new(big.Int).Mod(e.y, p) return "(" + x.String() + "," + y.String() + ")" } func (e *gfP2) Put(pool *bnPool) { pool.Put(e.x) pool.Put(e.y) } func (e *gfP2) Set(a *gfP2) *gfP2 { e.x.Set(a.x) e.y.Set(a.y) return e } func (e *gfP2) SetZero() *gfP2 { e.x.SetInt64(0) e.y.SetInt64(0) return e } func (e *gfP2) SetOne() *gfP2 { e.x.SetInt64(0) e.y.SetInt64(1) return e } func (e *gfP2) Minimal() { if e.x.Sign() < 0 || e.x.Cmp(p) >= 0 { e.x.Mod(e.x, p) } if e.y.Sign() < 0 || e.y.Cmp(p) >= 0 { e.y.Mod(e.y, p) } } func (e *gfP2) IsZero() bool { return e.x.Sign() == 0 && e.y.Sign() == 0 } func (e *gfP2) IsOne() bool { if e.x.Sign() != 0 { return false } words := e.y.Bits() return len(words) == 1 && words[0] == 1 } func (e *gfP2) Conjugate(a *gfP2) *gfP2 { e.y.Set(a.y) e.x.Neg(a.x) return e } func (e *gfP2) Negative(a *gfP2) *gfP2 { e.x.Neg(a.x) e.y.Neg(a.y) return e } func (e *gfP2) Add(a, b *gfP2) *gfP2 { e.x.Add(a.x, b.x) e.y.Add(a.y, b.y) return e } func (e *gfP2) Sub(a, b *gfP2) *gfP2 { e.x.Sub(a.x, b.x) e.y.Sub(a.y, b.y) return e } func (e *gfP2) Double(a *gfP2) *gfP2 { e.x.Lsh(a.x, 1) e.y.Lsh(a.y, 1) return e } func (c *gfP2) Exp(a *gfP2, power *big.Int, pool *bnPool) *gfP2 { sum := newGFp2(pool) sum.SetOne() t := newGFp2(pool) for i := power.BitLen() - 1; i >= 0; i-- { t.Square(sum, pool) if power.Bit(i) != 0 { sum.Mul(t, a, pool) } else { sum.Set(t) } } c.Set(sum) sum.Put(pool) t.Put(pool) return c } // See "Multiplication and Squaring in Pairing-Friendly Fields", // http://eprint.iacr.org/2006/471.pdf func (e *gfP2) Mul(a, b *gfP2, pool *bnPool) *gfP2 { tx := pool.Get().Mul(a.x, b.y) t := pool.Get().Mul(b.x, a.y) tx.Add(tx, t) tx.Mod(tx, p) ty := pool.Get().Mul(a.y, b.y) t.Mul(a.x, b.x) ty.Sub(ty, t) e.y.Mod(ty, p) e.x.Set(tx) pool.Put(tx) pool.Put(ty) pool.Put(t) return e } func (e *gfP2) MulScalar(a *gfP2, b *big.Int) *gfP2 { e.x.Mul(a.x, b) e.y.Mul(a.y, b) return e } // MulXi sets e=ξa where ξ=i+3 and then returns e. func (e *gfP2) MulXi(a *gfP2, pool *bnPool) *gfP2 { // (xi+y)(i+3) = (3x+y)i+(3y-x) tx := pool.Get().Lsh(a.x, 1) tx.Add(tx, a.x) tx.Add(tx, a.y) ty := pool.Get().Lsh(a.y, 1) ty.Add(ty, a.y) ty.Sub(ty, a.x) e.x.Set(tx) e.y.Set(ty) pool.Put(tx) pool.Put(ty) return e } func (e *gfP2) Square(a *gfP2, pool *bnPool) *gfP2 { // Complex squaring algorithm: // (xi+b)² = (x+y)(y-x) + 2*i*x*y t1 := pool.Get().Sub(a.y, a.x) t2 := pool.Get().Add(a.x, a.y) ty := pool.Get().Mul(t1, t2) ty.Mod(ty, p) t1.Mul(a.x, a.y) t1.Lsh(t1, 1) e.x.Mod(t1, p) e.y.Set(ty) pool.Put(t1) pool.Put(t2) pool.Put(ty) return e } func (e *gfP2) Invert(a *gfP2, pool *bnPool) *gfP2 { // See "Implementing cryptographic pairings", M. Scott, section 3.2. // ftp://136.206.11.249/pub/crypto/pairings.pdf t := pool.Get() t.Mul(a.y, a.y) t2 := pool.Get() t2.Mul(a.x, a.x) t.Add(t, t2) inv := pool.Get() inv.ModInverse(t, p) e.x.Neg(a.x) e.x.Mul(e.x, inv) e.x.Mod(e.x, p) e.y.Mul(a.y, inv) e.y.Mod(e.y, p) pool.Put(t) pool.Put(t2) pool.Put(inv) return e } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bn256/bn256.go�������������������������������������0000644�0000153�0000161�00000022557�12321735647�023422� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bn256 implements a particular bilinear group at the 128-bit security level. // // Bilinear groups are the basis of many of the new cryptographic protocols // that have been proposed over the past decade. They consist of a triplet of // groups (Gâ‚, Gâ‚‚ and GT) such that there exists a function e(gâ‚Ë£,g₂ʸ)=gTˣʸ // (where gâ‚“ is a generator of the respective group). That function is called // a pairing function. // // This package specifically implements the Optimal Ate pairing over a 256-bit // Barreto-Naehrig curve as described in // http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible // with the implementation described in that paper. package bn256 import ( "crypto/rand" "io" "math/big" ) // BUG(agl): this implementation is not constant time. // TODO(agl): keep GF(p²) elements in Mongomery form. // G1 is an abstract cyclic group. The zero value is suitable for use as the // output of an operation, but cannot be used as an input. type G1 struct { p *curvePoint } // RandomG1 returns x and gâ‚Ë£ where x is a random, non-zero number read from r. func RandomG1(r io.Reader) (*big.Int, *G1, error) { var k *big.Int var err error for { k, err = rand.Int(r, Order) if err != nil { return nil, nil, err } if k.Sign() > 0 { break } } return k, new(G1).ScalarBaseMult(k), nil } func (g *G1) String() string { return "bn256.G1" + g.p.String() } // ScalarBaseMult sets e to g*k where g is the generator of the group and // then returns e. func (e *G1) ScalarBaseMult(k *big.Int) *G1 { if e.p == nil { e.p = newCurvePoint(nil) } e.p.Mul(curveGen, k, new(bnPool)) return e } // ScalarMult sets e to a*k and then returns e. func (e *G1) ScalarMult(a *G1, k *big.Int) *G1 { if e.p == nil { e.p = newCurvePoint(nil) } e.p.Mul(a.p, k, new(bnPool)) return e } // Add sets e to a+b and then returns e. // BUG(agl): this function is not complete: a==b fails. func (e *G1) Add(a, b *G1) *G1 { if e.p == nil { e.p = newCurvePoint(nil) } e.p.Add(a.p, b.p, new(bnPool)) return e } // Neg sets e to -a and then returns e. func (e *G1) Neg(a *G1) *G1 { if e.p == nil { e.p = newCurvePoint(nil) } e.p.Negative(a.p) return e } // Marshal converts n to a byte slice. func (n *G1) Marshal() []byte { n.p.MakeAffine(nil) xBytes := new(big.Int).Mod(n.p.x, p).Bytes() yBytes := new(big.Int).Mod(n.p.y, p).Bytes() // Each value is a 256-bit number. const numBytes = 256 / 8 ret := make([]byte, numBytes*2) copy(ret[1*numBytes-len(xBytes):], xBytes) copy(ret[2*numBytes-len(yBytes):], yBytes) return ret } // Unmarshal sets e to the result of converting the output of Marshal back into // a group element and then returns e. func (e *G1) Unmarshal(m []byte) (*G1, bool) { // Each value is a 256-bit number. const numBytes = 256 / 8 if len(m) != 2*numBytes { return nil, false } if e.p == nil { e.p = newCurvePoint(nil) } e.p.x.SetBytes(m[0*numBytes : 1*numBytes]) e.p.y.SetBytes(m[1*numBytes : 2*numBytes]) if e.p.x.Sign() == 0 && e.p.y.Sign() == 0 { // This is the point at infinity. e.p.y.SetInt64(1) e.p.z.SetInt64(0) e.p.t.SetInt64(0) } else { e.p.z.SetInt64(1) e.p.t.SetInt64(1) if !e.p.IsOnCurve() { return nil, false } } return e, true } // G2 is an abstract cyclic group. The zero value is suitable for use as the // output of an operation, but cannot be used as an input. type G2 struct { p *twistPoint } // RandomG1 returns x and gâ‚‚Ë£ where x is a random, non-zero number read from r. func RandomG2(r io.Reader) (*big.Int, *G2, error) { var k *big.Int var err error for { k, err = rand.Int(r, Order) if err != nil { return nil, nil, err } if k.Sign() > 0 { break } } return k, new(G2).ScalarBaseMult(k), nil } func (g *G2) String() string { return "bn256.G2" + g.p.String() } // ScalarBaseMult sets e to g*k where g is the generator of the group and // then returns out. func (e *G2) ScalarBaseMult(k *big.Int) *G2 { if e.p == nil { e.p = newTwistPoint(nil) } e.p.Mul(twistGen, k, new(bnPool)) return e } // ScalarMult sets e to a*k and then returns e. func (e *G2) ScalarMult(a *G2, k *big.Int) *G2 { if e.p == nil { e.p = newTwistPoint(nil) } e.p.Mul(a.p, k, new(bnPool)) return e } // Add sets e to a+b and then returns e. // BUG(agl): this function is not complete: a==b fails. func (e *G2) Add(a, b *G2) *G2 { if e.p == nil { e.p = newTwistPoint(nil) } e.p.Add(a.p, b.p, new(bnPool)) return e } // Marshal converts n into a byte slice. func (n *G2) Marshal() []byte { n.p.MakeAffine(nil) xxBytes := new(big.Int).Mod(n.p.x.x, p).Bytes() xyBytes := new(big.Int).Mod(n.p.x.y, p).Bytes() yxBytes := new(big.Int).Mod(n.p.y.x, p).Bytes() yyBytes := new(big.Int).Mod(n.p.y.y, p).Bytes() // Each value is a 256-bit number. const numBytes = 256 / 8 ret := make([]byte, numBytes*4) copy(ret[1*numBytes-len(xxBytes):], xxBytes) copy(ret[2*numBytes-len(xyBytes):], xyBytes) copy(ret[3*numBytes-len(yxBytes):], yxBytes) copy(ret[4*numBytes-len(yyBytes):], yyBytes) return ret } // Unmarshal sets e to the result of converting the output of Marshal back into // a group element and then returns e. func (e *G2) Unmarshal(m []byte) (*G2, bool) { // Each value is a 256-bit number. const numBytes = 256 / 8 if len(m) != 4*numBytes { return nil, false } if e.p == nil { e.p = newTwistPoint(nil) } e.p.x.x.SetBytes(m[0*numBytes : 1*numBytes]) e.p.x.y.SetBytes(m[1*numBytes : 2*numBytes]) e.p.y.x.SetBytes(m[2*numBytes : 3*numBytes]) e.p.y.y.SetBytes(m[3*numBytes : 4*numBytes]) if e.p.x.x.Sign() == 0 && e.p.x.y.Sign() == 0 && e.p.y.x.Sign() == 0 && e.p.y.y.Sign() == 0 { // This is the point at infinity. e.p.y.SetOne() e.p.z.SetZero() e.p.t.SetZero() } else { e.p.z.SetOne() e.p.t.SetOne() if !e.p.IsOnCurve() { return nil, false } } return e, true } // GT is an abstract cyclic group. The zero value is suitable for use as the // output of an operation, but cannot be used as an input. type GT struct { p *gfP12 } func (g *GT) String() string { return "bn256.GT" + g.p.String() } // ScalarMult sets e to a*k and then returns e. func (e *GT) ScalarMult(a *GT, k *big.Int) *GT { if e.p == nil { e.p = newGFp12(nil) } e.p.Exp(a.p, k, new(bnPool)) return e } // Add sets e to a+b and then returns e. func (e *GT) Add(a, b *GT) *GT { if e.p == nil { e.p = newGFp12(nil) } e.p.Mul(a.p, b.p, new(bnPool)) return e } // Neg sets e to -a and then returns e. func (e *GT) Neg(a *GT) *GT { if e.p == nil { e.p = newGFp12(nil) } e.p.Invert(a.p, new(bnPool)) return e } // Marshal converts n into a byte slice. func (n *GT) Marshal() []byte { n.p.Minimal() xxxBytes := n.p.x.x.x.Bytes() xxyBytes := n.p.x.x.y.Bytes() xyxBytes := n.p.x.y.x.Bytes() xyyBytes := n.p.x.y.y.Bytes() xzxBytes := n.p.x.z.x.Bytes() xzyBytes := n.p.x.z.y.Bytes() yxxBytes := n.p.y.x.x.Bytes() yxyBytes := n.p.y.x.y.Bytes() yyxBytes := n.p.y.y.x.Bytes() yyyBytes := n.p.y.y.y.Bytes() yzxBytes := n.p.y.z.x.Bytes() yzyBytes := n.p.y.z.y.Bytes() // Each value is a 256-bit number. const numBytes = 256 / 8 ret := make([]byte, numBytes*12) copy(ret[1*numBytes-len(xxxBytes):], xxxBytes) copy(ret[2*numBytes-len(xxyBytes):], xxyBytes) copy(ret[3*numBytes-len(xyxBytes):], xyxBytes) copy(ret[4*numBytes-len(xyyBytes):], xyyBytes) copy(ret[5*numBytes-len(xzxBytes):], xzxBytes) copy(ret[6*numBytes-len(xzyBytes):], xzyBytes) copy(ret[7*numBytes-len(yxxBytes):], yxxBytes) copy(ret[8*numBytes-len(yxyBytes):], yxyBytes) copy(ret[9*numBytes-len(yyxBytes):], yyxBytes) copy(ret[10*numBytes-len(yyyBytes):], yyyBytes) copy(ret[11*numBytes-len(yzxBytes):], yzxBytes) copy(ret[12*numBytes-len(yzyBytes):], yzyBytes) return ret } // Unmarshal sets e to the result of converting the output of Marshal back into // a group element and then returns e. func (e *GT) Unmarshal(m []byte) (*GT, bool) { // Each value is a 256-bit number. const numBytes = 256 / 8 if len(m) != 12*numBytes { return nil, false } if e.p == nil { e.p = newGFp12(nil) } e.p.x.x.x.SetBytes(m[0*numBytes : 1*numBytes]) e.p.x.x.y.SetBytes(m[1*numBytes : 2*numBytes]) e.p.x.y.x.SetBytes(m[2*numBytes : 3*numBytes]) e.p.x.y.y.SetBytes(m[3*numBytes : 4*numBytes]) e.p.x.z.x.SetBytes(m[4*numBytes : 5*numBytes]) e.p.x.z.y.SetBytes(m[5*numBytes : 6*numBytes]) e.p.y.x.x.SetBytes(m[6*numBytes : 7*numBytes]) e.p.y.x.y.SetBytes(m[7*numBytes : 8*numBytes]) e.p.y.y.x.SetBytes(m[8*numBytes : 9*numBytes]) e.p.y.y.y.SetBytes(m[9*numBytes : 10*numBytes]) e.p.y.z.x.SetBytes(m[10*numBytes : 11*numBytes]) e.p.y.z.y.SetBytes(m[11*numBytes : 12*numBytes]) return e, true } // Pair calculates an Optimal Ate pairing. func Pair(g1 *G1, g2 *G2) *GT { return &GT{optimalAte(g2.p, g1.p, new(bnPool))} } // bnPool implements a tiny cache of *big.Int objects that's used to reduce the // number of allocations made during processing. type bnPool struct { bns []*big.Int count int } func (pool *bnPool) Get() *big.Int { if pool == nil { return new(big.Int) } pool.count++ l := len(pool.bns) if l == 0 { return new(big.Int) } bn := pool.bns[l-1] pool.bns = pool.bns[:l-1] return bn } func (pool *bnPool) Put(bn *big.Int) { if pool == nil { return } pool.bns = append(pool.bns, bn) pool.count-- } func (pool *bnPool) Count() int { return pool.count } �������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bcrypt/��������������������������������������������0000755�0000153�0000161�00000000000�12321736016�022662� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bcrypt/base64.go�����������������������������������0000644�0000153�0000161�00000001461�12321735647�024310� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bcrypt import "encoding/base64" const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" var bcEncoding = base64.NewEncoding(alphabet) func base64Encode(src []byte) []byte { n := bcEncoding.EncodedLen(len(src)) dst := make([]byte, n) bcEncoding.Encode(dst, src) for dst[n-1] == '=' { n-- } return dst[:n] } func base64Decode(src []byte) ([]byte, error) { numOfEquals := 4 - (len(src) % 4) for i := 0; i < numOfEquals; i++ { src = append(src, '=') } dst := make([]byte, bcEncoding.DecodedLen(len(src))) n, err := bcEncoding.Decode(dst, src) if err != nil { return nil, err } return dst[:n], nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bcrypt/bcrypt.go�����������������������������������0000644�0000153�0000161�00000017251�12321735647�024533� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bcrypt implements Provos and Mazières's bcrypt adaptive hashing // algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf package bcrypt // The code is a port of Provos and Mazières's C implementation. import ( "code.google.com/p/go.crypto/blowfish" "crypto/rand" "crypto/subtle" "errors" "fmt" "io" "strconv" ) const ( MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword ) // The error returned from CompareHashAndPassword when a password and hash do // not match. var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password") // The error returned from CompareHashAndPassword when a hash is too short to // be a bcrypt hash. var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password") // The error returned from CompareHashAndPassword when a hash was created with // a bcrypt algorithm newer than this implementation. type HashVersionTooNewError byte func (hv HashVersionTooNewError) Error() string { return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion) } // The error returned from CompareHashAndPassword when a hash starts with something other than '$' type InvalidHashPrefixError byte func (ih InvalidHashPrefixError) Error() string { return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih)) } type InvalidCostError int func (ic InvalidCostError) Error() string { return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost)) } const ( majorVersion = '2' minorVersion = 'a' maxSaltSize = 16 maxCryptedHashSize = 23 encodedSaltSize = 22 encodedHashSize = 31 minHashSize = 59 ) // magicCipherData is an IV for the 64 Blowfish encryption calls in // bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes. var magicCipherData = []byte{ 0x4f, 0x72, 0x70, 0x68, 0x65, 0x61, 0x6e, 0x42, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x53, 0x63, 0x72, 0x79, 0x44, 0x6f, 0x75, 0x62, 0x74, } type hashed struct { hash []byte salt []byte cost int // allowed range is MinCost to MaxCost major byte minor byte } // GenerateFromPassword returns the bcrypt hash of the password at the given // cost. If the cost given is less than MinCost, the cost will be set to // DefaultCost, instead. Use CompareHashAndPassword, as defined in this package, // to compare the returned hashed password with its cleartext version. func GenerateFromPassword(password []byte, cost int) ([]byte, error) { p, err := newFromPassword(password, cost) if err != nil { return nil, err } return p.Hash(), nil } // CompareHashAndPassword compares a bcrypt hashed password with its possible // plaintext equivalent. Returns nil on success, or an error on failure. func CompareHashAndPassword(hashedPassword, password []byte) error { p, err := newFromHash(hashedPassword) if err != nil { return err } otherHash, err := bcrypt(password, p.cost, p.salt) if err != nil { return err } otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor} if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 { return nil } return ErrMismatchedHashAndPassword } // Cost returns the hashing cost used to create the given hashed // password. When, in the future, the hashing cost of a password system needs // to be increased in order to adjust for greater computational power, this // function allows one to establish which passwords need to be updated. func Cost(hashedPassword []byte) (int, error) { p, err := newFromHash(hashedPassword) if err != nil { return 0, err } return p.cost, nil } func newFromPassword(password []byte, cost int) (*hashed, error) { if cost < MinCost { cost = DefaultCost } p := new(hashed) p.major = majorVersion p.minor = minorVersion err := checkCost(cost) if err != nil { return nil, err } p.cost = cost unencodedSalt := make([]byte, maxSaltSize) _, err = io.ReadFull(rand.Reader, unencodedSalt) if err != nil { return nil, err } p.salt = base64Encode(unencodedSalt) hash, err := bcrypt(password, p.cost, p.salt) if err != nil { return nil, err } p.hash = hash return p, err } func newFromHash(hashedSecret []byte) (*hashed, error) { if len(hashedSecret) < minHashSize { return nil, ErrHashTooShort } p := new(hashed) n, err := p.decodeVersion(hashedSecret) if err != nil { return nil, err } hashedSecret = hashedSecret[n:] n, err = p.decodeCost(hashedSecret) if err != nil { return nil, err } hashedSecret = hashedSecret[n:] // The "+2" is here because we'll have to append at most 2 '=' to the salt // when base64 decoding it in expensiveBlowfishSetup(). p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2) copy(p.salt, hashedSecret[:encodedSaltSize]) hashedSecret = hashedSecret[encodedSaltSize:] p.hash = make([]byte, len(hashedSecret)) copy(p.hash, hashedSecret) return p, nil } func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) { cipherData := make([]byte, len(magicCipherData)) copy(cipherData, magicCipherData) c, err := expensiveBlowfishSetup(password, uint32(cost), salt) if err != nil { return nil, err } for i := 0; i < 24; i += 8 { for j := 0; j < 64; j++ { c.Encrypt(cipherData[i:i+8], cipherData[i:i+8]) } } // Bug compatibility with C bcrypt implementations. We only encode 23 of // the 24 bytes encrypted. hsh := base64Encode(cipherData[:maxCryptedHashSize]) return hsh, nil } func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) { csalt, err := base64Decode(salt) if err != nil { return nil, err } // Bug compatibility with C bcrypt implementations. They use the trailing // NULL in the key string during expansion. ckey := append(key, 0) c, err := blowfish.NewSaltedCipher(ckey, csalt) if err != nil { return nil, err } var i, rounds uint64 rounds = 1 << cost for i = 0; i < rounds; i++ { blowfish.ExpandKey(ckey, c) blowfish.ExpandKey(csalt, c) } return c, nil } func (p *hashed) Hash() []byte { arr := make([]byte, 60) arr[0] = '$' arr[1] = p.major n := 2 if p.minor != 0 { arr[2] = p.minor n = 3 } arr[n] = '$' n += 1 copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost))) n += 2 arr[n] = '$' n += 1 copy(arr[n:], p.salt) n += encodedSaltSize copy(arr[n:], p.hash) n += encodedHashSize return arr[:n] } func (p *hashed) decodeVersion(sbytes []byte) (int, error) { if sbytes[0] != '$' { return -1, InvalidHashPrefixError(sbytes[0]) } if sbytes[1] > majorVersion { return -1, HashVersionTooNewError(sbytes[1]) } p.major = sbytes[1] n := 3 if sbytes[2] != '$' { p.minor = sbytes[2] n++ } return n, nil } // sbytes should begin where decodeVersion left off. func (p *hashed) decodeCost(sbytes []byte) (int, error) { cost, err := strconv.Atoi(string(sbytes[0:2])) if err != nil { return -1, err } err = checkCost(cost) if err != nil { return -1, err } p.cost = cost return 3, nil } func (p *hashed) String() string { return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor) } func checkCost(cost int) error { if cost < MinCost || cost > MaxCost { return InvalidCostError(cost) } return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/bcrypt/bcrypt_test.go������������������������������0000644�0000153�0000161�00000014014�12321736016�025553� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 bcrypt import ( "bytes" "fmt" "testing" ) func TestBcryptingIsEasy(t *testing.T) { pass := []byte("mypassword") hp, err := GenerateFromPassword(pass, 0) if err != nil { t.Fatalf("GenerateFromPassword error: %s", err) } if CompareHashAndPassword(hp, pass) != nil { t.Errorf("%v should hash %s correctly", hp, pass) } notPass := "notthepass" err = CompareHashAndPassword(hp, []byte(notPass)) if err != ErrMismatchedHashAndPassword { t.Errorf("%v and %s should be mismatched", hp, notPass) } } func TestBcryptingIsCorrect(t *testing.T) { pass := []byte("allmine") salt := []byte("XajjQvNhvvRt5GSeFk1xFe") expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga") hash, err := bcrypt(pass, 10, salt) if err != nil { t.Fatalf("bcrypt blew up: %v", err) } if !bytes.HasSuffix(expectedHash, hash) { t.Errorf("%v should be the suffix of %v", hash, expectedHash) } h, err := newFromHash(expectedHash) if err != nil { t.Errorf("Unable to parse %s: %v", string(expectedHash), err) } // This is not the safe way to compare these hashes. We do this only for // testing clarity. Use bcrypt.CompareHashAndPassword() if err == nil && !bytes.Equal(expectedHash, h.Hash()) { t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash) } } func TestTooLongPasswordsWork(t *testing.T) { salt := []byte("XajjQvNhvvRt5GSeFk1xFe") // One byte over the usual 56 byte limit that blowfish has tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456") tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C") hash, err := bcrypt(tooLongPass, 10, salt) if err != nil { t.Fatalf("bcrypt blew up on long password: %v", err) } if !bytes.HasSuffix(tooLongExpected, hash) { t.Errorf("%v should be the suffix of %v", hash, tooLongExpected) } } type InvalidHashTest struct { err error hash []byte } var invalidTests = []InvalidHashTest{ {ErrHashTooShort, []byte("$2a$10$fooo")}, {ErrHashTooShort, []byte("$2a")}, {HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")}, {InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")}, {InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")}, } func TestInvalidHashErrors(t *testing.T) { check := func(name string, expected, err error) { if err == nil { t.Errorf("%s: Should have returned an error", name) } if err != nil && err != expected { t.Errorf("%s gave err %v but should have given %v", name, err, expected) } } for _, iht := range invalidTests { _, err := newFromHash(iht.hash) check("newFromHash", iht.err, err) err = CompareHashAndPassword(iht.hash, []byte("anything")) check("CompareHashAndPassword", iht.err, err) } } func TestUnpaddedBase64Encoding(t *testing.T) { original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30} encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe") encoded := base64Encode(original) if !bytes.Equal(encodedOriginal, encoded) { t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal) } decoded, err := base64Decode(encodedOriginal) if err != nil { t.Fatalf("base64Decode blew up: %s", err) } if !bytes.Equal(decoded, original) { t.Errorf("Decoded %v should have equaled %v", decoded, original) } } func TestCost(t *testing.T) { suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C" for _, vers := range []string{"2a", "2"} { for _, cost := range []int{4, 10} { s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix) h := []byte(s) actual, err := Cost(h) if err != nil { t.Errorf("Cost, error: %s", err) continue } if actual != cost { t.Errorf("Cost, expected: %d, actual: %d", cost, actual) } } } _, err := Cost([]byte("$a$a$" + suffix)) if err == nil { t.Errorf("Cost, malformed but no error returned") } } func TestCostValidationInHash(t *testing.T) { if testing.Short() { return } pass := []byte("mypassword") for c := 0; c < MinCost; c++ { p, _ := newFromPassword(pass, c) if p.cost != DefaultCost { t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost) } } p, _ := newFromPassword(pass, 14) if p.cost != 14 { t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost) } hp, _ := newFromHash(p.Hash()) if p.cost != hp.cost { t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost) } _, err := newFromPassword(pass, 32) if err == nil { t.Fatalf("newFromPassword: should return a cost error") } if err != InvalidCostError(32) { t.Errorf("newFromPassword: should return cost error, got %#v", err) } } func TestCostReturnsWithLeadingZeroes(t *testing.T) { hp, _ := newFromPassword([]byte("abcdefgh"), 7) cost := hp.Hash()[4:7] expected := []byte("07$") if !bytes.Equal(expected, cost) { t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected) } } func TestMinorNotRequired(t *testing.T) { noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga") h, err := newFromHash(noMinorHash) if err != nil { t.Fatalf("No minor hash blew up: %s", err) } if h.minor != 0 { t.Errorf("Should leave minor version at 0, but was %d", h.minor) } if !bytes.Equal(noMinorHash, h.Hash()) { t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash()) } } func BenchmarkEqual(b *testing.B) { b.StopTimer() passwd := []byte("somepasswordyoulike") hash, _ := GenerateFromPassword(passwd, 10) b.StartTimer() for i := 0; i < b.N; i++ { CompareHashAndPassword(hash, passwd) } } func BenchmarkGeneration(b *testing.B) { b.StopTimer() passwd := []byte("mylongpassword1234") b.StartTimer() for i := 0; i < b.N; i++ { GenerateFromPassword(passwd, 10) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/AUTHORS��������������������������������������������0000644�0000153�0000161�00000000255�12321735647�022442� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This source code refers to The Go Authors for copyright purposes. # The master list of authors is in the main Go distribution, # visible at http://tip.golang.org/AUTHORS. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/xts/�����������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022206� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/xts/xts_test.go������������������������������������0000644�0000153�0000161�00000020477�12321735647�024424� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 xts import ( "bytes" "crypto/aes" "encoding/hex" "testing" ) // These test vectors have been taken from IEEE P1619/D16, Annex B. var xtsTestVectors = []struct { key string sector uint64 plaintext string ciphertext string }{ { "0000000000000000000000000000000000000000000000000000000000000000", 0, "0000000000000000000000000000000000000000000000000000000000000000", "917cf69ebd68b2ec9b9fe9a3eadda692cd43d2f59598ed858c02c2652fbf922e", }, { "1111111111111111111111111111111122222222222222222222222222222222", 0x3333333333, "4444444444444444444444444444444444444444444444444444444444444444", "c454185e6a16936e39334038acef838bfb186fff7480adc4289382ecd6d394f0", }, { "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f022222222222222222222222222222222", 0x3333333333, "4444444444444444444444444444444444444444444444444444444444444444", "af85336b597afc1a900b2eb21ec949d292df4c047e0b21532186a5971a227a89", }, { "2718281828459045235360287471352631415926535897932384626433832795", 0, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "27a7479befa1d476489f308cd4cfa6e2a96e4bbe3208ff25287dd3819616e89cc78cf7f5e543445f8333d8fa7f56000005279fa5d8b5e4ad40e736ddb4d35412328063fd2aab53e5ea1e0a9f332500a5df9487d07a5c92cc512c8866c7e860ce93fdf166a24912b422976146ae20ce846bb7dc9ba94a767aaef20c0d61ad02655ea92dc4c4e41a8952c651d33174be51a10c421110e6d81588ede82103a252d8a750e8768defffed9122810aaeb99f9172af82b604dc4b8e51bcb08235a6f4341332e4ca60482a4ba1a03b3e65008fc5da76b70bf1690db4eae29c5f1badd03c5ccf2a55d705ddcd86d449511ceb7ec30bf12b1fa35b913f9f747a8afd1b130e94bff94effd01a91735ca1726acd0b197c4e5b03393697e126826fb6bbde8ecc1e08298516e2c9ed03ff3c1b7860f6de76d4cecd94c8119855ef5297ca67e9f3e7ff72b1e99785ca0a7e7720c5b36dc6d72cac9574c8cbbc2f801e23e56fd344b07f22154beba0f08ce8891e643ed995c94d9a69c9f1b5f499027a78572aeebd74d20cc39881c213ee770b1010e4bea718846977ae119f7a023ab58cca0ad752afe656bb3c17256a9f6e9bf19fdd5a38fc82bbe872c5539edb609ef4f79c203ebb140f2e583cb2ad15b4aa5b655016a8449277dbd477ef2c8d6c017db738b18deb4a427d1923ce3ff262735779a418f20a282df920147beabe421ee5319d0568", }, { "2718281828459045235360287471352631415926535897932384626433832795", 1, "27a7479befa1d476489f308cd4cfa6e2a96e4bbe3208ff25287dd3819616e89cc78cf7f5e543445f8333d8fa7f56000005279fa5d8b5e4ad40e736ddb4d35412328063fd2aab53e5ea1e0a9f332500a5df9487d07a5c92cc512c8866c7e860ce93fdf166a24912b422976146ae20ce846bb7dc9ba94a767aaef20c0d61ad02655ea92dc4c4e41a8952c651d33174be51a10c421110e6d81588ede82103a252d8a750e8768defffed9122810aaeb99f9172af82b604dc4b8e51bcb08235a6f4341332e4ca60482a4ba1a03b3e65008fc5da76b70bf1690db4eae29c5f1badd03c5ccf2a55d705ddcd86d449511ceb7ec30bf12b1fa35b913f9f747a8afd1b130e94bff94effd01a91735ca1726acd0b197c4e5b03393697e126826fb6bbde8ecc1e08298516e2c9ed03ff3c1b7860f6de76d4cecd94c8119855ef5297ca67e9f3e7ff72b1e99785ca0a7e7720c5b36dc6d72cac9574c8cbbc2f801e23e56fd344b07f22154beba0f08ce8891e643ed995c94d9a69c9f1b5f499027a78572aeebd74d20cc39881c213ee770b1010e4bea718846977ae119f7a023ab58cca0ad752afe656bb3c17256a9f6e9bf19fdd5a38fc82bbe872c5539edb609ef4f79c203ebb140f2e583cb2ad15b4aa5b655016a8449277dbd477ef2c8d6c017db738b18deb4a427d1923ce3ff262735779a418f20a282df920147beabe421ee5319d0568", "264d3ca8512194fec312c8c9891f279fefdd608d0c027b60483a3fa811d65ee59d52d9e40ec5672d81532b38b6b089ce951f0f9c35590b8b978d175213f329bb1c2fd30f2f7f30492a61a532a79f51d36f5e31a7c9a12c286082ff7d2394d18f783e1a8e72c722caaaa52d8f065657d2631fd25bfd8e5baad6e527d763517501c68c5edc3cdd55435c532d7125c8614deed9adaa3acade5888b87bef641c4c994c8091b5bcd387f3963fb5bc37aa922fbfe3df4e5b915e6eb514717bdd2a74079a5073f5c4bfd46adf7d282e7a393a52579d11a028da4d9cd9c77124f9648ee383b1ac763930e7162a8d37f350b2f74b8472cf09902063c6b32e8c2d9290cefbd7346d1c779a0df50edcde4531da07b099c638e83a755944df2aef1aa31752fd323dcb710fb4bfbb9d22b925bc3577e1b8949e729a90bbafeacf7f7879e7b1147e28ba0bae940db795a61b15ecf4df8db07b824bb062802cc98a9545bb2aaeed77cb3fc6db15dcd7d80d7d5bc406c4970a3478ada8899b329198eb61c193fb6275aa8ca340344a75a862aebe92eee1ce032fd950b47d7704a3876923b4ad62844bf4a09c4dbe8b4397184b7471360c9564880aedddb9baa4af2e75394b08cd32ff479c57a07d3eab5d54de5f9738b8d27f27a9f0ab11799d7b7ffefb2704c95c6ad12c39f1e867a4b7b1d7818a4b753dfd2a89ccb45e001a03a867b187f225dd", }, { "27182818284590452353602874713526624977572470936999595749669676273141592653589793238462643383279502884197169399375105820974944592", 0xff, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "1c3b3a102f770386e4836c99e370cf9bea00803f5e482357a4ae12d414a3e63b5d31e276f8fe4a8d66b317f9ac683f44680a86ac35adfc3345befecb4bb188fd5776926c49a3095eb108fd1098baec70aaa66999a72a82f27d848b21d4a741b0c5cd4d5fff9dac89aeba122961d03a757123e9870f8acf1000020887891429ca2a3e7a7d7df7b10355165c8b9a6d0a7de8b062c4500dc4cd120c0f7418dae3d0b5781c34803fa75421c790dfe1de1834f280d7667b327f6c8cd7557e12ac3a0f93ec05c52e0493ef31a12d3d9260f79a289d6a379bc70c50841473d1a8cc81ec583e9645e07b8d9670655ba5bbcfecc6dc3966380ad8fecb17b6ba02469a020a84e18e8f84252070c13e9f1f289be54fbc481457778f616015e1327a02b140f1505eb309326d68378f8374595c849d84f4c333ec4423885143cb47bd71c5edae9be69a2ffeceb1bec9de244fbe15992b11b77c040f12bd8f6a975a44a0f90c29a9abc3d4d893927284c58754cce294529f8614dcd2aba991925fedc4ae74ffac6e333b93eb4aff0479da9a410e4450e0dd7ae4c6e2910900575da401fc07059f645e8b7e9bfdef33943054ff84011493c27b3429eaedb4ed5376441a77ed43851ad77f16f541dfd269d50d6a5f14fb0aab1cbb4c1550be97f7ab4066193c4caa773dad38014bd2092fa755c824bb5e54c4f36ffda9fcea70b9c6e693e148c151", }, } func fromHex(s string) []byte { ret, err := hex.DecodeString(s) if err != nil { panic("xts: invalid hex in test") } return ret } func TestXTS(t *testing.T) { for i, test := range xtsTestVectors { c, err := NewCipher(aes.NewCipher, fromHex(test.key)) if err != nil { t.Errorf("#%d: failed to create cipher: %s", i, err) continue } plaintext := fromHex(test.plaintext) ciphertext := make([]byte, len(plaintext)) c.Encrypt(ciphertext, plaintext, test.sector) expectedCiphertext := fromHex(test.ciphertext) if !bytes.Equal(ciphertext, expectedCiphertext) { t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext) continue } decrypted := make([]byte, len(ciphertext)) c.Decrypt(decrypted, ciphertext, test.sector) if !bytes.Equal(decrypted, plaintext) { t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext) } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/xts/xts.go�����������������������������������������0000644�0000153�0000161�00000010560�12321735647�023355� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 xts implements the XTS cipher mode as specified in IEEE P1619/D16. // // XTS mode is typically used for disk encryption, which presents a number of // novel problems that make more common modes inapplicable. The disk is // conceptually an array of sectors and we must be able to encrypt and decrypt // a sector in isolation. However, an attacker must not be able to transpose // two sectors of plaintext by transposing their ciphertext. // // XTS wraps a block cipher with Rogaway's XEX mode in order to build a // tweakable block cipher. This allows each sector to have a unique tweak and // effectively create a unique key for each sector. // // XTS does not provide any authentication. An attacker can manipulate the // ciphertext and randomise a block (16 bytes) of the plaintext. // // (Note: this package does not implement ciphertext-stealing so sectors must // be a multiple of 16 bytes.) package xts import ( "crypto/cipher" "errors" ) // Cipher contains an expanded key structure. It doesn't contain mutable state // and therefore can be used concurrently. type Cipher struct { k1, k2 cipher.Block } // blockSize is the block size that the underlying cipher must have. XTS is // only defined for 16-byte ciphers. const blockSize = 16 // NewCipher creates a Cipher given a function for creating the underlying // block cipher (which must have a block size of 16 bytes). The key must be // twice the length of the underlying cipher's key. func NewCipher(cipherFunc func([]byte) (cipher.Block, error), key []byte) (c *Cipher, err error) { c = new(Cipher) if c.k1, err = cipherFunc(key[:len(key)/2]); err != nil { return } c.k2, err = cipherFunc(key[len(key)/2:]) if c.k1.BlockSize() != blockSize { err = errors.New("xts: cipher does not have a block size of 16") } return } // Encrypt encrypts a sector of plaintext and puts the result into ciphertext. // Plaintext and ciphertext may be the same slice but should not overlap. // Sectors must be a multiple of 16 bytes and less than 2²ⴠbytes. func (c *Cipher) Encrypt(ciphertext, plaintext []byte, sectorNum uint64) { if len(ciphertext) < len(plaintext) { panic("xts: ciphertext is smaller than plaintext") } if len(plaintext)%blockSize != 0 { panic("xts: plaintext is not a multiple of the block size") } var tweak [blockSize]byte for i := 0; i < 8; i++ { tweak[i] = byte(sectorNum) sectorNum >>= 8 } c.k2.Encrypt(tweak[:], tweak[:]) for i := 0; i < len(plaintext); i += blockSize { for j := 0; j < blockSize; j++ { ciphertext[i+j] = plaintext[i+j] ^ tweak[j] } c.k1.Encrypt(ciphertext[i:], ciphertext[i:]) for j := 0; j < blockSize; j++ { ciphertext[i+j] ^= tweak[j] } mul2(&tweak) } } // Decrypt decrypts a sector of ciphertext and puts the result into plaintext. // Plaintext and ciphertext may be the same slice but should not overlap. // Sectors must be a multiple of 16 bytes and less than 2²ⴠbytes. func (c *Cipher) Decrypt(plaintext, ciphertext []byte, sectorNum uint64) { if len(plaintext) < len(ciphertext) { panic("xts: plaintext is smaller than ciphertext") } if len(ciphertext)%blockSize != 0 { panic("xts: ciphertext is not a multiple of the block size") } var tweak [blockSize]byte for i := 0; i < 8; i++ { tweak[i] = byte(sectorNum) sectorNum >>= 8 } c.k2.Encrypt(tweak[:], tweak[:]) for i := 0; i < len(plaintext); i += blockSize { for j := 0; j < blockSize; j++ { plaintext[i+j] = ciphertext[i+j] ^ tweak[j] } c.k1.Decrypt(plaintext[i:], plaintext[i:]) for j := 0; j < blockSize; j++ { plaintext[i+j] ^= tweak[j] } mul2(&tweak) } } // mul2 multiplies tweak by 2 in GF(2¹²â¸) with an irreducible polynomial of // x¹²⸠+ xâ· + x² + x + 1. func mul2(tweak *[blockSize]byte) { var carryIn byte for j := range tweak { carryOut := tweak[j] >> 7 tweak[j] = (tweak[j] << 1) + carryIn carryIn = carryOut } if carryIn != 0 { // If we have a carry bit then we need to subtract a multiple // of the irreducible polynomial (x¹²⸠+ xâ· + x² + x + 1). // By dropping the carry bit, we're subtracting the x^128 term // so all that remains is to subtract xâ· + x² + x + 1. // Subtraction (and addition) in this representation is just // XOR. tweak[0] ^= 1<<7 | 1<<2 | 1<<1 | 1 } } ������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/CONTRIBUTORS���������������������������������������0000644�0000153�0000161�00000000252�12321735647�023247� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This source code was written by the Go contributors. # The master list of contributors is in the main Go distribution, # visible at http://tip.golang.org/CONTRIBUTORS. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ripemd160/�����������������������������������������0000755�0000153�0000161�00000000000�12321735647�023077� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ripemd160/ripemd160block.go������������������������0000644�0000153�0000161�00000010241�12321735647�026146� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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. // RIPEMD-160 block step. // In its own file so that a faster assembly or C version // can be substituted easily. package ripemd160 // work buffer indices and roll amounts for one line var _n = [80]uint{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13, } var _r = [80]uint{ 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6, } // same for the other parallel one var n_ = [80]uint{ 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11, } var r_ = [80]uint{ 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11, } func _Block(md *digest, p []byte) int { n := 0 var x [16]uint32 var alpha, beta uint32 for len(p) >= BlockSize { a, b, c, d, e := md.s[0], md.s[1], md.s[2], md.s[3], md.s[4] aa, bb, cc, dd, ee := a, b, c, d, e j := 0 for i := 0; i < 16; i++ { x[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 j += 4 } // round 1 i := 0 for i < 16 { alpha = a + (b ^ c ^ d) + x[_n[i]] s := _r[i] alpha = (alpha<<s | alpha>>(32-s)) + e beta = c<<10 | c>>22 a, b, c, d, e = e, alpha, b, beta, d // parallel line alpha = aa + (bb ^ (cc | ^dd)) + x[n_[i]] + 0x50a28be6 s = r_[i] alpha = (alpha<<s | alpha>>(32-s)) + ee beta = cc<<10 | cc>>22 aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd i++ } // round 2 for i < 32 { alpha = a + (b&c | ^b&d) + x[_n[i]] + 0x5a827999 s := _r[i] alpha = (alpha<<s | alpha>>(32-s)) + e beta = c<<10 | c>>22 a, b, c, d, e = e, alpha, b, beta, d // parallel line alpha = aa + (bb&dd | cc&^dd) + x[n_[i]] + 0x5c4dd124 s = r_[i] alpha = (alpha<<s | alpha>>(32-s)) + ee beta = cc<<10 | cc>>22 aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd i++ } // round 3 for i < 48 { alpha = a + (b | ^c ^ d) + x[_n[i]] + 0x6ed9eba1 s := _r[i] alpha = (alpha<<s | alpha>>(32-s)) + e beta = c<<10 | c>>22 a, b, c, d, e = e, alpha, b, beta, d // parallel line alpha = aa + (bb | ^cc ^ dd) + x[n_[i]] + 0x6d703ef3 s = r_[i] alpha = (alpha<<s | alpha>>(32-s)) + ee beta = cc<<10 | cc>>22 aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd i++ } // round 4 for i < 64 { alpha = a + (b&d | c&^d) + x[_n[i]] + 0x8f1bbcdc s := _r[i] alpha = (alpha<<s | alpha>>(32-s)) + e beta = c<<10 | c>>22 a, b, c, d, e = e, alpha, b, beta, d // parallel line alpha = aa + (bb&cc | ^bb&dd) + x[n_[i]] + 0x7a6d76e9 s = r_[i] alpha = (alpha<<s | alpha>>(32-s)) + ee beta = cc<<10 | cc>>22 aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd i++ } // round 5 for i < 80 { alpha = a + (b ^ (c | ^d)) + x[_n[i]] + 0xa953fd4e s := _r[i] alpha = (alpha<<s | alpha>>(32-s)) + e beta = c<<10 | c>>22 a, b, c, d, e = e, alpha, b, beta, d // parallel line alpha = aa + (bb ^ cc ^ dd) + x[n_[i]] s = r_[i] alpha = (alpha<<s | alpha>>(32-s)) + ee beta = cc<<10 | cc>>22 aa, bb, cc, dd, ee = ee, alpha, bb, beta, dd i++ } // combine results dd += c + md.s[1] md.s[1] = md.s[2] + d + ee md.s[2] = md.s[3] + e + aa md.s[3] = md.s[4] + a + bb md.s[4] = md.s[0] + b + cc md.s[0] = dd p = p[BlockSize:] n += BlockSize } return n } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ripemd160/ripemd160.go�����������������������������0000644�0000153�0000161�00000004602�12321735647�025137� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 ripemd160 implements the RIPEMD-160 hash algorithm. package ripemd160 // RIPEMD-160 is designed by by Hans Dobbertin, Antoon Bosselaers, and Bart // Preneel with specifications available at: // http://homes.esat.kuleuven.be/~cosicart/pdf/AB-9601/AB-9601.pdf. import ( "crypto" "hash" ) func init() { crypto.RegisterHash(crypto.RIPEMD160, New) } // The size of the checksum in bytes. const Size = 20 // The block size of the hash algorithm in bytes. const BlockSize = 64 const ( _s0 = 0x67452301 _s1 = 0xefcdab89 _s2 = 0x98badcfe _s3 = 0x10325476 _s4 = 0xc3d2e1f0 ) // digest represents the partial evaluation of a checksum. type digest struct { s [5]uint32 // running context x [BlockSize]byte // temporary buffer nx int // index into x tc uint64 // total count of bytes processed } func (d *digest) Reset() { d.s[0], d.s[1], d.s[2], d.s[3], d.s[4] = _s0, _s1, _s2, _s3, _s4 d.nx = 0 d.tc = 0 } // New returns a new hash.Hash computing the checksum. func New() hash.Hash { result := new(digest) result.Reset() return result } func (d *digest) Size() int { return Size } func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { nn = len(p) d.tc += uint64(nn) if d.nx > 0 { n := len(p) if n > BlockSize-d.nx { n = BlockSize - d.nx } for i := 0; i < n; i++ { d.x[d.nx+i] = p[i] } d.nx += n if d.nx == BlockSize { _Block(d, d.x[0:]) d.nx = 0 } p = p[n:] } n := _Block(d, p) p = p[n:] if len(p) > 0 { d.nx = copy(d.x[:], p) } return } func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := *d0 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. tc := d.tc var tmp [64]byte tmp[0] = 0x80 if tc%64 < 56 { d.Write(tmp[0 : 56-tc%64]) } else { d.Write(tmp[0 : 64+56-tc%64]) } // Length in bits. tc <<= 3 for i := uint(0); i < 8; i++ { tmp[i] = byte(tc >> (8 * i)) } d.Write(tmp[0:8]) if d.nx != 0 { panic("d.nx != 0") } var digest [Size]byte for i, s := range d.s { digest[i*4] = byte(s) digest[i*4+1] = byte(s >> 8) digest[i*4+2] = byte(s >> 16) digest[i*4+3] = byte(s >> 24) } return append(in, digest[:]...) } ������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ripemd160/ripemd160_test.go������������������������0000644�0000153�0000161�00000003377�12321735647�026206� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 ripemd160 // Test vectors are from: // http://homes.esat.kuleuven.be/~bosselae/ripemd160.html import ( "fmt" "io" "testing" ) type mdTest struct { out string in string } var vectors = [...]mdTest{ {"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""}, {"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"}, {"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"}, {"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"}, {"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"}, {"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, {"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, {"9b752e45573d4b39f4dbd3323cab82bf63326bfb", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"}, } func TestVectors(t *testing.T) { for i := 0; i < len(vectors); i++ { tv := vectors[i] md := New() for j := 0; j < 3; j++ { if j < 2 { io.WriteString(md, tv.in) } else { io.WriteString(md, tv.in[0:len(tv.in)/2]) md.Sum(nil) io.WriteString(md, tv.in[len(tv.in)/2:]) } s := fmt.Sprintf("%x", md.Sum(nil)) if s != tv.out { t.Fatalf("RIPEMD-160[%d](%s) = %s, expected %s", j, tv.in, s, tv.out) } md.Reset() } } } func TestMillionA(t *testing.T) { md := New() for i := 0; i < 100000; i++ { io.WriteString(md, "aaaaaaaaaa") } out := "52783243c1697bdbe16d37f97f68f08325dc1528" s := fmt.Sprintf("%x", md.Sum(nil)) if s != out { t.Fatalf("RIPEMD-160 (1 million 'a') = %s, expected %s", s, out) } md.Reset() } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/README���������������������������������������������0000644�0000153�0000161�00000000221�12321735647�022243� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This repository holds supplementary Go cryptography libraries. To submit changes to this repository, see http://golang.org/doc/contribute.html. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/pbkdf2/��������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022540� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/pbkdf2/pbkdf2_test.go������������������������������0000644�0000153�0000161�00000006275�12321735647�025310� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 pbkdf2 import ( "bytes" "crypto/sha1" "crypto/sha256" "hash" "testing" ) type testVector struct { password string salt string iter int output []byte } // Test vectors from RFC 6070, http://tools.ietf.org/html/rfc6070 var sha1TestVectors = []testVector{ { "password", "salt", 1, []byte{ 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, 0x2f, 0xe0, 0x37, 0xa6, }, }, { "password", "salt", 2, []byte{ 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, 0xd8, 0xde, 0x89, 0x57, }, }, { "password", "salt", 4096, []byte{ 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, 0x65, 0xa4, 0x29, 0xc1, }, }, // // This one takes too long // { // "password", // "salt", // 16777216, // []byte{ // 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4, // 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c, // 0x26, 0x34, 0xe9, 0x84, // }, // }, { "passwordPASSWORDpassword", "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, []byte{ 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a, 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, 0x38, }, }, { "pass\000word", "sa\000lt", 4096, []byte{ 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d, 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3, }, }, } // Test vectors from // http://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors var sha256TestVectors = []testVector{ { "password", "salt", 1, []byte{ 0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, }, }, { "password", "salt", 2, []byte{ 0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3, 0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, 0x6d, 0xd0, 0x2a, 0x30, 0x3f, 0x8e, }, }, { "password", "salt", 4096, []byte{ 0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, }, }, { "passwordPASSWORDpassword", "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, []byte{ 0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f, 0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf, 0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18, 0x1c, }, }, { "pass\000word", "sa\000lt", 4096, []byte{ 0x89, 0xb6, 0x9d, 0x05, 0x16, 0xf8, 0x29, 0x89, 0x3c, 0x69, 0x62, 0x26, 0x65, 0x0a, 0x86, 0x87, }, }, } func testHash(t *testing.T, h func() hash.Hash, hashName string, vectors []testVector) { for i, v := range vectors { o := Key([]byte(v.password), []byte(v.salt), v.iter, len(v.output), h) if !bytes.Equal(o, v.output) { t.Errorf("%s %d: expected %x, got %x", hashName, i, v.output, o) } } } func TestWithHMACSHA1(t *testing.T) { testHash(t, sha1.New, "SHA1", sha1TestVectors) } func TestWithHMACSHA256(t *testing.T) { testHash(t, sha256.New, "SHA256", sha256TestVectors) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/pbkdf2/pbkdf2.go�����������������������������������0000644�0000153�0000161�00000004615�12321735647�024245� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 pbkdf2 implements the key derivation function PBKDF2 as defined in RFC 2898 / PKCS #5 v2.0. A key derivation function is useful when encrypting data based on a password or any other not-fully-random data. It uses a pseudorandom function to derive a secure encryption key based on the password. While v2.0 of the standard defines only one pseudorandom function to use, HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To choose, you can pass the `New` functions from the different SHA packages to pbkdf2.Key. */ package pbkdf2 import ( "crypto/hmac" "hash" ) // Key derives a key from the password, salt and iteration count, returning a // []byte of length keylen that can be used as cryptographic key. The key is // derived based on the method described as PBKDF2 with the HMAC variant using // the supplied hash function. // // For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you // can get a derived key for e.g. AES-256 (which needs a 32-byte key) by // doing: // // dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) // // Remember to get a good random salt. At least 8 bytes is recommended by the // RFC. // // Using a higher iteration count will increase the cost of an exhaustive // search but will also make derivation proportionally slower. func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { prf := hmac.New(h, password) hashLen := prf.Size() numBlocks := (keyLen + hashLen - 1) / hashLen var buf [4]byte dk := make([]byte, 0, numBlocks*hashLen) U := make([]byte, hashLen) for block := 1; block <= numBlocks; block++ { // N.B.: || means concatenation, ^ means XOR // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter // U_1 = PRF(password, salt || uint(i)) prf.Reset() prf.Write(salt) buf[0] = byte(block >> 24) buf[1] = byte(block >> 16) buf[2] = byte(block >> 8) buf[3] = byte(block) prf.Write(buf[:4]) dk = prf.Sum(dk) T := dk[len(dk)-hashLen:] copy(U, T) // U_n = PRF(password, U_(n-1)) for n := 2; n <= iter; n++ { prf.Reset() prf.Write(U) U = U[:0] U = prf.Sum(U) for x := range U { T[x] ^= U[x] } } } return dk[:keyLen] } �������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/poly1305/������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022664� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/poly1305/poly1305_test.go��������������������������0000644�0000153�0000161�00000003014�12321735647�025544� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 poly1305 import ( "bytes" "testing" ) var testData = []struct { in, k, correct []byte }{ { []byte("Hello world!"), []byte("this is 32-byte key for Poly1305"), []byte{0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0}, }, { make([]byte, 32), []byte("this is 32-byte key for Poly1305"), []byte{0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07}, }, { make([]byte, 2007), []byte("this is 32-byte key for Poly1305"), []byte{0xda, 0x84, 0xbc, 0xab, 0x02, 0x67, 0x6c, 0x38, 0xcd, 0xb0, 0x15, 0x60, 0x42, 0x74, 0xc2, 0xaa}, }, { make([]byte, 2007), make([]byte, 32), make([]byte, 16), }, } func TestSum(t *testing.T) { var out [16]byte var key [32]byte for i, v := range testData { copy(key[:], v.k) Sum(&out, v.in, &key) if !bytes.Equal(out[:], v.correct) { t.Errorf("%d: expected %x, got %x", i, v.correct, out[:]) } } } func Benchmark1K(b *testing.B) { b.StopTimer() var out [16]byte var key [32]byte in := make([]byte, 1024) b.SetBytes(int64(len(in))) b.StartTimer() for i := 0; i < b.N; i++ { Sum(&out, in, &key) } } func Benchmark64(b *testing.B) { b.StopTimer() var out [16]byte var key [32]byte in := make([]byte, 64) b.SetBytes(int64(len(in))) b.StartTimer() for i := 0; i < b.N; i++ { Sum(&out, in, &key) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/poly1305/const_amd64.s�����������������������������0000644�0000153�0000161�00000002773�12321735647�025202� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // +build amd64,!gccgo DATA ·SCALE(SB)/8, $0x37F4000000000000 GLOBL ·SCALE(SB), $8 DATA ·TWO32(SB)/8, $0x41F0000000000000 GLOBL ·TWO32(SB), $8 DATA ·TWO64(SB)/8, $0x43F0000000000000 GLOBL ·TWO64(SB), $8 DATA ·TWO96(SB)/8, $0x45F0000000000000 GLOBL ·TWO96(SB), $8 DATA ·ALPHA32(SB)/8, $0x45E8000000000000 GLOBL ·ALPHA32(SB), $8 DATA ·ALPHA64(SB)/8, $0x47E8000000000000 GLOBL ·ALPHA64(SB), $8 DATA ·ALPHA96(SB)/8, $0x49E8000000000000 GLOBL ·ALPHA96(SB), $8 DATA ·ALPHA130(SB)/8, $0x4C08000000000000 GLOBL ·ALPHA130(SB), $8 DATA ·DOFFSET0(SB)/8, $0x4330000000000000 GLOBL ·DOFFSET0(SB), $8 DATA ·DOFFSET1(SB)/8, $0x4530000000000000 GLOBL ·DOFFSET1(SB), $8 DATA ·DOFFSET2(SB)/8, $0x4730000000000000 GLOBL ·DOFFSET2(SB), $8 DATA ·DOFFSET3(SB)/8, $0x4930000000000000 GLOBL ·DOFFSET3(SB), $8 DATA ·DOFFSET3MINUSTWO128(SB)/8, $0x492FFFFE00000000 GLOBL ·DOFFSET3MINUSTWO128(SB), $8 DATA ·HOFFSET0(SB)/8, $0x43300001FFFFFFFB GLOBL ·HOFFSET0(SB), $8 DATA ·HOFFSET1(SB)/8, $0x45300001FFFFFFFE GLOBL ·HOFFSET1(SB), $8 DATA ·HOFFSET2(SB)/8, $0x47300001FFFFFFFE GLOBL ·HOFFSET2(SB), $8 DATA ·HOFFSET3(SB)/8, $0x49300003FFFFFFFE GLOBL ·HOFFSET3(SB), $8 DATA ·ROUNDING(SB)/2, $0x137f GLOBL ·ROUNDING(SB), $2 �����juju-core_1.18.1/src/code.google.com/p/go.crypto/poly1305/sum_amd64.go������������������������������0000644�0000153�0000161�00000001262�12321735647�025013� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 amd64,!gccgo package poly1305 // This function is implemented in poly1305_amd64.s //go:noescape func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte) // Sum generates an authenticator for m using a one-time key and puts the // 16-byte result into out. Authenticating two different messages with the same // key allows an attacker to forge messages at will. func Sum(out *[16]byte, m []byte, key *[32]byte) { var mPtr *byte if len(m) > 0 { mPtr = &m[0] } poly1305(out, mPtr, uint64(len(m)), key) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/poly1305/poly1305.go�������������������������������0000644�0000153�0000161�00000002327�12321735647�024513� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 poly1305 implements Poly1305 one-time message authentication code as specified in http://cr.yp.to/mac/poly1305-20050329.pdf. Poly1305 is a fast, one-time authentication function. It is infeasible for an attacker to generate an authenticator for a message without the key. However, a key must only be used for a single message. Authenticating two different messages with the same key allows an attacker to forge authenticators for other messages with the same key. Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was used with a fixed key in order to generate one-time keys from an nonce. However, in this package AES isn't used and the one-time key is specified directly. */ package poly1305 import "crypto/subtle" // TagSize is the size, in bytes, of a poly1305 authenticator. const TagSize = 16 // Verify returns true if mac is a valid authenticator for m with the given // key. func Verify(mac *[16]byte, m []byte, key *[32]byte) bool { var tmp [16]byte Sum(&tmp, m, key) return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1 } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/poly1305/sum_ref.go��������������������������������0000644�0000153�0000161�00000050536�12321735647�024664� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 !amd64 gccgo package poly1305 // Based on original, public domain implementation from NaCl by D. J. // Bernstein. import "math" const ( alpham80 = 0.00000000558793544769287109375 alpham48 = 24.0 alpham16 = 103079215104.0 alpha0 = 6755399441055744.0 alpha18 = 1770887431076116955136.0 alpha32 = 29014219670751100192948224.0 alpha50 = 7605903601369376408980219232256.0 alpha64 = 124615124604835863084731911901282304.0 alpha82 = 32667107224410092492483962313449748299776.0 alpha96 = 535217884764734955396857238543560676143529984.0 alpha112 = 35076039295941670036888435985190792471742381031424.0 alpha130 = 9194973245195333150150082162901855101712434733101613056.0 scale = 0.0000000000000000000000000000000000000036734198463196484624023016788195177431833298649127735047148490821200539357960224151611328125 offset0 = 6755408030990331.0 offset1 = 29014256564239239022116864.0 offset2 = 124615283061160854719918951570079744.0 offset3 = 535219245894202480694386063513315216128475136.0 ) // Sum generates an authenticator for m using a one-time key and puts the // 16-byte result into out. Authenticating two different messages with the same // key allows an attacker to forge messages at will. func Sum(out *[16]byte, m []byte, key *[32]byte) { r := key s := key[16:] var ( y7 float64 y6 float64 y1 float64 y0 float64 y5 float64 y4 float64 x7 float64 x6 float64 x1 float64 x0 float64 y3 float64 y2 float64 x5 float64 r3lowx0 float64 x4 float64 r0lowx6 float64 x3 float64 r3highx0 float64 x2 float64 r0highx6 float64 r0lowx0 float64 sr1lowx6 float64 r0highx0 float64 sr1highx6 float64 sr3low float64 r1lowx0 float64 sr2lowx6 float64 r1highx0 float64 sr2highx6 float64 r2lowx0 float64 sr3lowx6 float64 r2highx0 float64 sr3highx6 float64 r1highx4 float64 r1lowx4 float64 r0highx4 float64 r0lowx4 float64 sr3highx4 float64 sr3lowx4 float64 sr2highx4 float64 sr2lowx4 float64 r0lowx2 float64 r0highx2 float64 r1lowx2 float64 r1highx2 float64 r2lowx2 float64 r2highx2 float64 sr3lowx2 float64 sr3highx2 float64 z0 float64 z1 float64 z2 float64 z3 float64 m0 int64 m1 int64 m2 int64 m3 int64 m00 uint32 m01 uint32 m02 uint32 m03 uint32 m10 uint32 m11 uint32 m12 uint32 m13 uint32 m20 uint32 m21 uint32 m22 uint32 m23 uint32 m30 uint32 m31 uint32 m32 uint32 m33 uint64 lbelow2 int32 lbelow3 int32 lbelow4 int32 lbelow5 int32 lbelow6 int32 lbelow7 int32 lbelow8 int32 lbelow9 int32 lbelow10 int32 lbelow11 int32 lbelow12 int32 lbelow13 int32 lbelow14 int32 lbelow15 int32 s00 uint32 s01 uint32 s02 uint32 s03 uint32 s10 uint32 s11 uint32 s12 uint32 s13 uint32 s20 uint32 s21 uint32 s22 uint32 s23 uint32 s30 uint32 s31 uint32 s32 uint32 s33 uint32 bits32 uint64 f uint64 f0 uint64 f1 uint64 f2 uint64 f3 uint64 f4 uint64 g uint64 g0 uint64 g1 uint64 g2 uint64 g3 uint64 g4 uint64 ) var p int32 l := int32(len(m)) r00 := uint32(r[0]) r01 := uint32(r[1]) r02 := uint32(r[2]) r0 := int64(2151) r03 := uint32(r[3]) r03 &= 15 r0 <<= 51 r10 := uint32(r[4]) r10 &= 252 r01 <<= 8 r0 += int64(r00) r11 := uint32(r[5]) r02 <<= 16 r0 += int64(r01) r12 := uint32(r[6]) r03 <<= 24 r0 += int64(r02) r13 := uint32(r[7]) r13 &= 15 r1 := int64(2215) r0 += int64(r03) d0 := r0 r1 <<= 51 r2 := int64(2279) r20 := uint32(r[8]) r20 &= 252 r11 <<= 8 r1 += int64(r10) r21 := uint32(r[9]) r12 <<= 16 r1 += int64(r11) r22 := uint32(r[10]) r13 <<= 24 r1 += int64(r12) r23 := uint32(r[11]) r23 &= 15 r2 <<= 51 r1 += int64(r13) d1 := r1 r21 <<= 8 r2 += int64(r20) r30 := uint32(r[12]) r30 &= 252 r22 <<= 16 r2 += int64(r21) r31 := uint32(r[13]) r23 <<= 24 r2 += int64(r22) r32 := uint32(r[14]) r2 += int64(r23) r3 := int64(2343) d2 := r2 r3 <<= 51 r33 := uint32(r[15]) r33 &= 15 r31 <<= 8 r3 += int64(r30) r32 <<= 16 r3 += int64(r31) r33 <<= 24 r3 += int64(r32) r3 += int64(r33) h0 := alpha32 - alpha32 d3 := r3 h1 := alpha32 - alpha32 h2 := alpha32 - alpha32 h3 := alpha32 - alpha32 h4 := alpha32 - alpha32 r0low := math.Float64frombits(uint64(d0)) h5 := alpha32 - alpha32 r1low := math.Float64frombits(uint64(d1)) h6 := alpha32 - alpha32 r2low := math.Float64frombits(uint64(d2)) h7 := alpha32 - alpha32 r0low -= alpha0 r1low -= alpha32 r2low -= alpha64 r0high := r0low + alpha18 r3low := math.Float64frombits(uint64(d3)) r1high := r1low + alpha50 sr1low := scale * r1low r2high := r2low + alpha82 sr2low := scale * r2low r0high -= alpha18 r0high_stack := r0high r3low -= alpha96 r1high -= alpha50 r1high_stack := r1high sr1high := sr1low + alpham80 r0low -= r0high r2high -= alpha82 sr3low = scale * r3low sr2high := sr2low + alpham48 r1low -= r1high r1low_stack := r1low sr1high -= alpham80 sr1high_stack := sr1high r2low -= r2high r2low_stack := r2low sr2high -= alpham48 sr2high_stack := sr2high r3high := r3low + alpha112 r0low_stack := r0low sr1low -= sr1high sr1low_stack := sr1low sr3high := sr3low + alpham16 r2high_stack := r2high sr2low -= sr2high sr2low_stack := sr2low r3high -= alpha112 r3high_stack := r3high sr3high -= alpham16 sr3high_stack := sr3high r3low -= r3high r3low_stack := r3low sr3low -= sr3high sr3low_stack := sr3low if l < 16 { goto addatmost15bytes } m00 = uint32(m[p+0]) m0 = 2151 m0 <<= 51 m1 = 2215 m01 = uint32(m[p+1]) m1 <<= 51 m2 = 2279 m02 = uint32(m[p+2]) m2 <<= 51 m3 = 2343 m03 = uint32(m[p+3]) m10 = uint32(m[p+4]) m01 <<= 8 m0 += int64(m00) m11 = uint32(m[p+5]) m02 <<= 16 m0 += int64(m01) m12 = uint32(m[p+6]) m03 <<= 24 m0 += int64(m02) m13 = uint32(m[p+7]) m3 <<= 51 m0 += int64(m03) m20 = uint32(m[p+8]) m11 <<= 8 m1 += int64(m10) m21 = uint32(m[p+9]) m12 <<= 16 m1 += int64(m11) m22 = uint32(m[p+10]) m13 <<= 24 m1 += int64(m12) m23 = uint32(m[p+11]) m1 += int64(m13) m30 = uint32(m[p+12]) m21 <<= 8 m2 += int64(m20) m31 = uint32(m[p+13]) m22 <<= 16 m2 += int64(m21) m32 = uint32(m[p+14]) m23 <<= 24 m2 += int64(m22) m33 = uint64(m[p+15]) m2 += int64(m23) d0 = m0 m31 <<= 8 m3 += int64(m30) d1 = m1 m32 <<= 16 m3 += int64(m31) d2 = m2 m33 += 256 m33 <<= 24 m3 += int64(m32) m3 += int64(m33) d3 = m3 p += 16 l -= 16 z0 = math.Float64frombits(uint64(d0)) z1 = math.Float64frombits(uint64(d1)) z2 = math.Float64frombits(uint64(d2)) z3 = math.Float64frombits(uint64(d3)) z0 -= alpha0 z1 -= alpha32 z2 -= alpha64 z3 -= alpha96 h0 += z0 h1 += z1 h3 += z2 h5 += z3 if l < 16 { goto multiplyaddatmost15bytes } multiplyaddatleast16bytes: m2 = 2279 m20 = uint32(m[p+8]) y7 = h7 + alpha130 m2 <<= 51 m3 = 2343 m21 = uint32(m[p+9]) y6 = h6 + alpha130 m3 <<= 51 m0 = 2151 m22 = uint32(m[p+10]) y1 = h1 + alpha32 m0 <<= 51 m1 = 2215 m23 = uint32(m[p+11]) y0 = h0 + alpha32 m1 <<= 51 m30 = uint32(m[p+12]) y7 -= alpha130 m21 <<= 8 m2 += int64(m20) m31 = uint32(m[p+13]) y6 -= alpha130 m22 <<= 16 m2 += int64(m21) m32 = uint32(m[p+14]) y1 -= alpha32 m23 <<= 24 m2 += int64(m22) m33 = uint64(m[p+15]) y0 -= alpha32 m2 += int64(m23) m00 = uint32(m[p+0]) y5 = h5 + alpha96 m31 <<= 8 m3 += int64(m30) m01 = uint32(m[p+1]) y4 = h4 + alpha96 m32 <<= 16 m02 = uint32(m[p+2]) x7 = h7 - y7 y7 *= scale m33 += 256 m03 = uint32(m[p+3]) x6 = h6 - y6 y6 *= scale m33 <<= 24 m3 += int64(m31) m10 = uint32(m[p+4]) x1 = h1 - y1 m01 <<= 8 m3 += int64(m32) m11 = uint32(m[p+5]) x0 = h0 - y0 m3 += int64(m33) m0 += int64(m00) m12 = uint32(m[p+6]) y5 -= alpha96 m02 <<= 16 m0 += int64(m01) m13 = uint32(m[p+7]) y4 -= alpha96 m03 <<= 24 m0 += int64(m02) d2 = m2 x1 += y7 m0 += int64(m03) d3 = m3 x0 += y6 m11 <<= 8 m1 += int64(m10) d0 = m0 x7 += y5 m12 <<= 16 m1 += int64(m11) x6 += y4 m13 <<= 24 m1 += int64(m12) y3 = h3 + alpha64 m1 += int64(m13) d1 = m1 y2 = h2 + alpha64 x0 += x1 x6 += x7 y3 -= alpha64 r3low = r3low_stack y2 -= alpha64 r0low = r0low_stack x5 = h5 - y5 r3lowx0 = r3low * x0 r3high = r3high_stack x4 = h4 - y4 r0lowx6 = r0low * x6 r0high = r0high_stack x3 = h3 - y3 r3highx0 = r3high * x0 sr1low = sr1low_stack x2 = h2 - y2 r0highx6 = r0high * x6 sr1high = sr1high_stack x5 += y3 r0lowx0 = r0low * x0 r1low = r1low_stack h6 = r3lowx0 + r0lowx6 sr1lowx6 = sr1low * x6 r1high = r1high_stack x4 += y2 r0highx0 = r0high * x0 sr2low = sr2low_stack h7 = r3highx0 + r0highx6 sr1highx6 = sr1high * x6 sr2high = sr2high_stack x3 += y1 r1lowx0 = r1low * x0 r2low = r2low_stack h0 = r0lowx0 + sr1lowx6 sr2lowx6 = sr2low * x6 r2high = r2high_stack x2 += y0 r1highx0 = r1high * x0 sr3low = sr3low_stack h1 = r0highx0 + sr1highx6 sr2highx6 = sr2high * x6 sr3high = sr3high_stack x4 += x5 r2lowx0 = r2low * x0 z2 = math.Float64frombits(uint64(d2)) h2 = r1lowx0 + sr2lowx6 sr3lowx6 = sr3low * x6 x2 += x3 r2highx0 = r2high * x0 z3 = math.Float64frombits(uint64(d3)) h3 = r1highx0 + sr2highx6 sr3highx6 = sr3high * x6 r1highx4 = r1high * x4 z2 -= alpha64 h4 = r2lowx0 + sr3lowx6 r1lowx4 = r1low * x4 r0highx4 = r0high * x4 z3 -= alpha96 h5 = r2highx0 + sr3highx6 r0lowx4 = r0low * x4 h7 += r1highx4 sr3highx4 = sr3high * x4 h6 += r1lowx4 sr3lowx4 = sr3low * x4 h5 += r0highx4 sr2highx4 = sr2high * x4 h4 += r0lowx4 sr2lowx4 = sr2low * x4 h3 += sr3highx4 r0lowx2 = r0low * x2 h2 += sr3lowx4 r0highx2 = r0high * x2 h1 += sr2highx4 r1lowx2 = r1low * x2 h0 += sr2lowx4 r1highx2 = r1high * x2 h2 += r0lowx2 r2lowx2 = r2low * x2 h3 += r0highx2 r2highx2 = r2high * x2 h4 += r1lowx2 sr3lowx2 = sr3low * x2 h5 += r1highx2 sr3highx2 = sr3high * x2 p += 16 l -= 16 h6 += r2lowx2 h7 += r2highx2 z1 = math.Float64frombits(uint64(d1)) h0 += sr3lowx2 z0 = math.Float64frombits(uint64(d0)) h1 += sr3highx2 z1 -= alpha32 z0 -= alpha0 h5 += z3 h3 += z2 h1 += z1 h0 += z0 if l >= 16 { goto multiplyaddatleast16bytes } multiplyaddatmost15bytes: y7 = h7 + alpha130 y6 = h6 + alpha130 y1 = h1 + alpha32 y0 = h0 + alpha32 y7 -= alpha130 y6 -= alpha130 y1 -= alpha32 y0 -= alpha32 y5 = h5 + alpha96 y4 = h4 + alpha96 x7 = h7 - y7 y7 *= scale x6 = h6 - y6 y6 *= scale x1 = h1 - y1 x0 = h0 - y0 y5 -= alpha96 y4 -= alpha96 x1 += y7 x0 += y6 x7 += y5 x6 += y4 y3 = h3 + alpha64 y2 = h2 + alpha64 x0 += x1 x6 += x7 y3 -= alpha64 r3low = r3low_stack y2 -= alpha64 r0low = r0low_stack x5 = h5 - y5 r3lowx0 = r3low * x0 r3high = r3high_stack x4 = h4 - y4 r0lowx6 = r0low * x6 r0high = r0high_stack x3 = h3 - y3 r3highx0 = r3high * x0 sr1low = sr1low_stack x2 = h2 - y2 r0highx6 = r0high * x6 sr1high = sr1high_stack x5 += y3 r0lowx0 = r0low * x0 r1low = r1low_stack h6 = r3lowx0 + r0lowx6 sr1lowx6 = sr1low * x6 r1high = r1high_stack x4 += y2 r0highx0 = r0high * x0 sr2low = sr2low_stack h7 = r3highx0 + r0highx6 sr1highx6 = sr1high * x6 sr2high = sr2high_stack x3 += y1 r1lowx0 = r1low * x0 r2low = r2low_stack h0 = r0lowx0 + sr1lowx6 sr2lowx6 = sr2low * x6 r2high = r2high_stack x2 += y0 r1highx0 = r1high * x0 sr3low = sr3low_stack h1 = r0highx0 + sr1highx6 sr2highx6 = sr2high * x6 sr3high = sr3high_stack x4 += x5 r2lowx0 = r2low * x0 h2 = r1lowx0 + sr2lowx6 sr3lowx6 = sr3low * x6 x2 += x3 r2highx0 = r2high * x0 h3 = r1highx0 + sr2highx6 sr3highx6 = sr3high * x6 r1highx4 = r1high * x4 h4 = r2lowx0 + sr3lowx6 r1lowx4 = r1low * x4 r0highx4 = r0high * x4 h5 = r2highx0 + sr3highx6 r0lowx4 = r0low * x4 h7 += r1highx4 sr3highx4 = sr3high * x4 h6 += r1lowx4 sr3lowx4 = sr3low * x4 h5 += r0highx4 sr2highx4 = sr2high * x4 h4 += r0lowx4 sr2lowx4 = sr2low * x4 h3 += sr3highx4 r0lowx2 = r0low * x2 h2 += sr3lowx4 r0highx2 = r0high * x2 h1 += sr2highx4 r1lowx2 = r1low * x2 h0 += sr2lowx4 r1highx2 = r1high * x2 h2 += r0lowx2 r2lowx2 = r2low * x2 h3 += r0highx2 r2highx2 = r2high * x2 h4 += r1lowx2 sr3lowx2 = sr3low * x2 h5 += r1highx2 sr3highx2 = sr3high * x2 h6 += r2lowx2 h7 += r2highx2 h0 += sr3lowx2 h1 += sr3highx2 addatmost15bytes: if l == 0 { goto nomorebytes } lbelow2 = l - 2 lbelow3 = l - 3 lbelow2 >>= 31 lbelow4 = l - 4 m00 = uint32(m[p+0]) lbelow3 >>= 31 p += lbelow2 m01 = uint32(m[p+1]) lbelow4 >>= 31 p += lbelow3 m02 = uint32(m[p+2]) p += lbelow4 m0 = 2151 m03 = uint32(m[p+3]) m0 <<= 51 m1 = 2215 m0 += int64(m00) m01 &^= uint32(lbelow2) m02 &^= uint32(lbelow3) m01 -= uint32(lbelow2) m01 <<= 8 m03 &^= uint32(lbelow4) m0 += int64(m01) lbelow2 -= lbelow3 m02 += uint32(lbelow2) lbelow3 -= lbelow4 m02 <<= 16 m03 += uint32(lbelow3) m03 <<= 24 m0 += int64(m02) m0 += int64(m03) lbelow5 = l - 5 lbelow6 = l - 6 lbelow7 = l - 7 lbelow5 >>= 31 lbelow8 = l - 8 lbelow6 >>= 31 p += lbelow5 m10 = uint32(m[p+4]) lbelow7 >>= 31 p += lbelow6 m11 = uint32(m[p+5]) lbelow8 >>= 31 p += lbelow7 m12 = uint32(m[p+6]) m1 <<= 51 p += lbelow8 m13 = uint32(m[p+7]) m10 &^= uint32(lbelow5) lbelow4 -= lbelow5 m10 += uint32(lbelow4) lbelow5 -= lbelow6 m11 &^= uint32(lbelow6) m11 += uint32(lbelow5) m11 <<= 8 m1 += int64(m10) m1 += int64(m11) m12 &^= uint32(lbelow7) lbelow6 -= lbelow7 m13 &^= uint32(lbelow8) m12 += uint32(lbelow6) lbelow7 -= lbelow8 m12 <<= 16 m13 += uint32(lbelow7) m13 <<= 24 m1 += int64(m12) m1 += int64(m13) m2 = 2279 lbelow9 = l - 9 m3 = 2343 lbelow10 = l - 10 lbelow11 = l - 11 lbelow9 >>= 31 lbelow12 = l - 12 lbelow10 >>= 31 p += lbelow9 m20 = uint32(m[p+8]) lbelow11 >>= 31 p += lbelow10 m21 = uint32(m[p+9]) lbelow12 >>= 31 p += lbelow11 m22 = uint32(m[p+10]) m2 <<= 51 p += lbelow12 m23 = uint32(m[p+11]) m20 &^= uint32(lbelow9) lbelow8 -= lbelow9 m20 += uint32(lbelow8) lbelow9 -= lbelow10 m21 &^= uint32(lbelow10) m21 += uint32(lbelow9) m21 <<= 8 m2 += int64(m20) m2 += int64(m21) m22 &^= uint32(lbelow11) lbelow10 -= lbelow11 m23 &^= uint32(lbelow12) m22 += uint32(lbelow10) lbelow11 -= lbelow12 m22 <<= 16 m23 += uint32(lbelow11) m23 <<= 24 m2 += int64(m22) m3 <<= 51 lbelow13 = l - 13 lbelow13 >>= 31 lbelow14 = l - 14 lbelow14 >>= 31 p += lbelow13 lbelow15 = l - 15 m30 = uint32(m[p+12]) lbelow15 >>= 31 p += lbelow14 m31 = uint32(m[p+13]) p += lbelow15 m2 += int64(m23) m32 = uint32(m[p+14]) m30 &^= uint32(lbelow13) lbelow12 -= lbelow13 m30 += uint32(lbelow12) lbelow13 -= lbelow14 m3 += int64(m30) m31 &^= uint32(lbelow14) m31 += uint32(lbelow13) m32 &^= uint32(lbelow15) m31 <<= 8 lbelow14 -= lbelow15 m3 += int64(m31) m32 += uint32(lbelow14) d0 = m0 m32 <<= 16 m33 = uint64(lbelow15 + 1) d1 = m1 m33 <<= 24 m3 += int64(m32) d2 = m2 m3 += int64(m33) d3 = m3 z3 = math.Float64frombits(uint64(d3)) z2 = math.Float64frombits(uint64(d2)) z1 = math.Float64frombits(uint64(d1)) z0 = math.Float64frombits(uint64(d0)) z3 -= alpha96 z2 -= alpha64 z1 -= alpha32 z0 -= alpha0 h5 += z3 h3 += z2 h1 += z1 h0 += z0 y7 = h7 + alpha130 y6 = h6 + alpha130 y1 = h1 + alpha32 y0 = h0 + alpha32 y7 -= alpha130 y6 -= alpha130 y1 -= alpha32 y0 -= alpha32 y5 = h5 + alpha96 y4 = h4 + alpha96 x7 = h7 - y7 y7 *= scale x6 = h6 - y6 y6 *= scale x1 = h1 - y1 x0 = h0 - y0 y5 -= alpha96 y4 -= alpha96 x1 += y7 x0 += y6 x7 += y5 x6 += y4 y3 = h3 + alpha64 y2 = h2 + alpha64 x0 += x1 x6 += x7 y3 -= alpha64 r3low = r3low_stack y2 -= alpha64 r0low = r0low_stack x5 = h5 - y5 r3lowx0 = r3low * x0 r3high = r3high_stack x4 = h4 - y4 r0lowx6 = r0low * x6 r0high = r0high_stack x3 = h3 - y3 r3highx0 = r3high * x0 sr1low = sr1low_stack x2 = h2 - y2 r0highx6 = r0high * x6 sr1high = sr1high_stack x5 += y3 r0lowx0 = r0low * x0 r1low = r1low_stack h6 = r3lowx0 + r0lowx6 sr1lowx6 = sr1low * x6 r1high = r1high_stack x4 += y2 r0highx0 = r0high * x0 sr2low = sr2low_stack h7 = r3highx0 + r0highx6 sr1highx6 = sr1high * x6 sr2high = sr2high_stack x3 += y1 r1lowx0 = r1low * x0 r2low = r2low_stack h0 = r0lowx0 + sr1lowx6 sr2lowx6 = sr2low * x6 r2high = r2high_stack x2 += y0 r1highx0 = r1high * x0 sr3low = sr3low_stack h1 = r0highx0 + sr1highx6 sr2highx6 = sr2high * x6 sr3high = sr3high_stack x4 += x5 r2lowx0 = r2low * x0 h2 = r1lowx0 + sr2lowx6 sr3lowx6 = sr3low * x6 x2 += x3 r2highx0 = r2high * x0 h3 = r1highx0 + sr2highx6 sr3highx6 = sr3high * x6 r1highx4 = r1high * x4 h4 = r2lowx0 + sr3lowx6 r1lowx4 = r1low * x4 r0highx4 = r0high * x4 h5 = r2highx0 + sr3highx6 r0lowx4 = r0low * x4 h7 += r1highx4 sr3highx4 = sr3high * x4 h6 += r1lowx4 sr3lowx4 = sr3low * x4 h5 += r0highx4 sr2highx4 = sr2high * x4 h4 += r0lowx4 sr2lowx4 = sr2low * x4 h3 += sr3highx4 r0lowx2 = r0low * x2 h2 += sr3lowx4 r0highx2 = r0high * x2 h1 += sr2highx4 r1lowx2 = r1low * x2 h0 += sr2lowx4 r1highx2 = r1high * x2 h2 += r0lowx2 r2lowx2 = r2low * x2 h3 += r0highx2 r2highx2 = r2high * x2 h4 += r1lowx2 sr3lowx2 = sr3low * x2 h5 += r1highx2 sr3highx2 = sr3high * x2 h6 += r2lowx2 h7 += r2highx2 h0 += sr3lowx2 h1 += sr3highx2 nomorebytes: y7 = h7 + alpha130 y0 = h0 + alpha32 y1 = h1 + alpha32 y2 = h2 + alpha64 y7 -= alpha130 y3 = h3 + alpha64 y4 = h4 + alpha96 y5 = h5 + alpha96 x7 = h7 - y7 y7 *= scale y0 -= alpha32 y1 -= alpha32 y2 -= alpha64 h6 += x7 y3 -= alpha64 y4 -= alpha96 y5 -= alpha96 y6 = h6 + alpha130 x0 = h0 - y0 x1 = h1 - y1 x2 = h2 - y2 y6 -= alpha130 x0 += y7 x3 = h3 - y3 x4 = h4 - y4 x5 = h5 - y5 x6 = h6 - y6 y6 *= scale x2 += y0 x3 += y1 x4 += y2 x0 += y6 x5 += y3 x6 += y4 x2 += x3 x0 += x1 x4 += x5 x6 += y5 x2 += offset1 d1 = int64(math.Float64bits(x2)) x0 += offset0 d0 = int64(math.Float64bits(x0)) x4 += offset2 d2 = int64(math.Float64bits(x4)) x6 += offset3 d3 = int64(math.Float64bits(x6)) f0 = uint64(d0) f1 = uint64(d1) bits32 = math.MaxUint64 f2 = uint64(d2) bits32 >>= 32 f3 = uint64(d3) f = f0 >> 32 f0 &= bits32 f &= 255 f1 += f g0 = f0 + 5 g = g0 >> 32 g0 &= bits32 f = f1 >> 32 f1 &= bits32 f &= 255 g1 = f1 + g g = g1 >> 32 f2 += f f = f2 >> 32 g1 &= bits32 f2 &= bits32 f &= 255 f3 += f g2 = f2 + g g = g2 >> 32 g2 &= bits32 f4 = f3 >> 32 f3 &= bits32 f4 &= 255 g3 = f3 + g g = g3 >> 32 g3 &= bits32 g4 = f4 + g g4 = g4 - 4 s00 = uint32(s[0]) f = uint64(int64(g4) >> 63) s01 = uint32(s[1]) f0 &= f g0 &^= f s02 = uint32(s[2]) f1 &= f f0 |= g0 s03 = uint32(s[3]) g1 &^= f f2 &= f s10 = uint32(s[4]) f3 &= f g2 &^= f s11 = uint32(s[5]) g3 &^= f f1 |= g1 s12 = uint32(s[6]) f2 |= g2 f3 |= g3 s13 = uint32(s[7]) s01 <<= 8 f0 += uint64(s00) s20 = uint32(s[8]) s02 <<= 16 f0 += uint64(s01) s21 = uint32(s[9]) s03 <<= 24 f0 += uint64(s02) s22 = uint32(s[10]) s11 <<= 8 f1 += uint64(s10) s23 = uint32(s[11]) s12 <<= 16 f1 += uint64(s11) s30 = uint32(s[12]) s13 <<= 24 f1 += uint64(s12) s31 = uint32(s[13]) f0 += uint64(s03) f1 += uint64(s13) s32 = uint32(s[14]) s21 <<= 8 f2 += uint64(s20) s33 = uint32(s[15]) s22 <<= 16 f2 += uint64(s21) s23 <<= 24 f2 += uint64(s22) s31 <<= 8 f3 += uint64(s30) s32 <<= 16 f3 += uint64(s31) s33 <<= 24 f3 += uint64(s32) f2 += uint64(s23) f3 += uint64(s33) out[0] = byte(f0) f0 >>= 8 out[1] = byte(f0) f0 >>= 8 out[2] = byte(f0) f0 >>= 8 out[3] = byte(f0) f0 >>= 8 f1 += f0 out[4] = byte(f1) f1 >>= 8 out[5] = byte(f1) f1 >>= 8 out[6] = byte(f1) f1 >>= 8 out[7] = byte(f1) f1 >>= 8 f2 += f1 out[8] = byte(f2) f2 >>= 8 out[9] = byte(f2) f2 >>= 8 out[10] = byte(f2) f2 >>= 8 out[11] = byte(f2) f2 >>= 8 f3 += f2 out[12] = byte(f3) f3 >>= 8 out[13] = byte(f3) f3 >>= 8 out[14] = byte(f3) f3 >>= 8 out[15] = byte(f3) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/poly1305/poly1305_amd64.s��������������������������0000644�0000153�0000161�00000020465�12321735647�025346� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // +build amd64,!gccgo // func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key) TEXT ·poly1305(SB),0,$224-32 MOVQ out+0(FP),DI MOVQ m+8(FP),SI MOVQ mlen+16(FP),DX MOVQ key+24(FP),CX MOVQ SP,R11 MOVQ $31,R9 NOTQ R9 ANDQ R9,SP ADDQ $32,SP MOVQ R11,32(SP) MOVQ R12,40(SP) MOVQ R13,48(SP) MOVQ R14,56(SP) MOVQ R15,64(SP) MOVQ BX,72(SP) MOVQ BP,80(SP) FLDCW ·ROUNDING(SB) MOVL 0(CX),R8 MOVL 4(CX),R9 MOVL 8(CX),AX MOVL 12(CX),R10 MOVQ DI,88(SP) MOVQ CX,96(SP) MOVL $0X43300000,108(SP) MOVL $0X45300000,116(SP) MOVL $0X47300000,124(SP) MOVL $0X49300000,132(SP) ANDL $0X0FFFFFFF,R8 ANDL $0X0FFFFFFC,R9 ANDL $0X0FFFFFFC,AX ANDL $0X0FFFFFFC,R10 MOVL R8,104(SP) MOVL R9,112(SP) MOVL AX,120(SP) MOVL R10,128(SP) FMOVD 104(SP), F0 FSUBD ·DOFFSET0(SB), F0 FMOVD 112(SP), F0 FSUBD ·DOFFSET1(SB), F0 FMOVD 120(SP), F0 FSUBD ·DOFFSET2(SB), F0 FMOVD 128(SP), F0 FSUBD ·DOFFSET3(SB), F0 FXCHD F0, F3 FMOVDP F0, 136(SP) FXCHD F0, F1 FMOVD F0, 144(SP) FMULD ·SCALE(SB), F0 FMOVDP F0, 152(SP) FMOVD F0, 160(SP) FMULD ·SCALE(SB), F0 FMOVDP F0, 168(SP) FMOVD F0, 176(SP) FMULD ·SCALE(SB), F0 FMOVDP F0, 184(SP) FLDZ FLDZ FLDZ FLDZ CMPQ DX,$16 JB ADDATMOST15BYTES INITIALATLEAST16BYTES: MOVL 12(SI),DI MOVL 8(SI),CX MOVL 4(SI),R8 MOVL 0(SI),R9 MOVL DI,128(SP) MOVL CX,120(SP) MOVL R8,112(SP) MOVL R9,104(SP) ADDQ $16,SI SUBQ $16,DX FXCHD F0, F3 FADDD 128(SP), F0 FSUBD ·DOFFSET3MINUSTWO128(SB), F0 FXCHD F0, F1 FADDD 112(SP), F0 FSUBD ·DOFFSET1(SB), F0 FXCHD F0, F2 FADDD 120(SP), F0 FSUBD ·DOFFSET2(SB), F0 FXCHD F0, F3 FADDD 104(SP), F0 FSUBD ·DOFFSET0(SB), F0 CMPQ DX,$16 JB MULTIPLYADDATMOST15BYTES MULTIPLYADDATLEAST16BYTES: MOVL 12(SI),DI MOVL 8(SI),CX MOVL 4(SI),R8 MOVL 0(SI),R9 MOVL DI,128(SP) MOVL CX,120(SP) MOVL R8,112(SP) MOVL R9,104(SP) ADDQ $16,SI SUBQ $16,DX FMOVD ·ALPHA130(SB), F0 FADDD F2,F0 FSUBD ·ALPHA130(SB), F0 FSUBD F0,F2 FMULD ·SCALE(SB), F0 FMOVD ·ALPHA32(SB), F0 FADDD F2,F0 FSUBD ·ALPHA32(SB), F0 FSUBD F0,F2 FXCHD F0, F2 FADDDP F0,F1 FMOVD ·ALPHA64(SB), F0 FADDD F4,F0 FSUBD ·ALPHA64(SB), F0 FSUBD F0,F4 FMOVD ·ALPHA96(SB), F0 FADDD F6,F0 FSUBD ·ALPHA96(SB), F0 FSUBD F0,F6 FXCHD F0, F6 FADDDP F0,F1 FXCHD F0, F3 FADDDP F0,F5 FXCHD F0, F3 FADDDP F0,F1 FMOVD 176(SP), F0 FMULD F3,F0 FMOVD 160(SP), F0 FMULD F4,F0 FMOVD 144(SP), F0 FMULD F5,F0 FMOVD 136(SP), F0 FMULDP F0,F6 FMOVD 160(SP), F0 FMULD F4,F0 FADDDP F0,F3 FMOVD 144(SP), F0 FMULD F4,F0 FADDDP F0,F2 FMOVD 136(SP), F0 FMULD F4,F0 FADDDP F0,F1 FMOVD 184(SP), F0 FMULDP F0,F4 FXCHD F0, F3 FADDDP F0,F5 FMOVD 144(SP), F0 FMULD F4,F0 FADDDP F0,F2 FMOVD 136(SP), F0 FMULD F4,F0 FADDDP F0,F1 FMOVD 184(SP), F0 FMULD F4,F0 FADDDP F0,F3 FMOVD 168(SP), F0 FMULDP F0,F4 FXCHD F0, F3 FADDDP F0,F4 FMOVD 136(SP), F0 FMULD F5,F0 FADDDP F0,F1 FXCHD F0, F3 FMOVD 184(SP), F0 FMULD F5,F0 FADDDP F0,F3 FXCHD F0, F1 FMOVD 168(SP), F0 FMULD F5,F0 FADDDP F0,F1 FMOVD 152(SP), F0 FMULDP F0,F5 FXCHD F0, F4 FADDDP F0,F1 CMPQ DX,$16 FXCHD F0, F2 FMOVD 128(SP), F0 FSUBD ·DOFFSET3MINUSTWO128(SB), F0 FADDDP F0,F1 FXCHD F0, F1 FMOVD 120(SP), F0 FSUBD ·DOFFSET2(SB), F0 FADDDP F0,F1 FXCHD F0, F3 FMOVD 112(SP), F0 FSUBD ·DOFFSET1(SB), F0 FADDDP F0,F1 FXCHD F0, F2 FMOVD 104(SP), F0 FSUBD ·DOFFSET0(SB), F0 FADDDP F0,F1 JAE MULTIPLYADDATLEAST16BYTES MULTIPLYADDATMOST15BYTES: FMOVD ·ALPHA130(SB), F0 FADDD F2,F0 FSUBD ·ALPHA130(SB), F0 FSUBD F0,F2 FMULD ·SCALE(SB), F0 FMOVD ·ALPHA32(SB), F0 FADDD F2,F0 FSUBD ·ALPHA32(SB), F0 FSUBD F0,F2 FMOVD ·ALPHA64(SB), F0 FADDD F5,F0 FSUBD ·ALPHA64(SB), F0 FSUBD F0,F5 FMOVD ·ALPHA96(SB), F0 FADDD F7,F0 FSUBD ·ALPHA96(SB), F0 FSUBD F0,F7 FXCHD F0, F7 FADDDP F0,F1 FXCHD F0, F5 FADDDP F0,F1 FXCHD F0, F3 FADDDP F0,F5 FADDDP F0,F1 FMOVD 176(SP), F0 FMULD F1,F0 FMOVD 160(SP), F0 FMULD F2,F0 FMOVD 144(SP), F0 FMULD F3,F0 FMOVD 136(SP), F0 FMULDP F0,F4 FMOVD 160(SP), F0 FMULD F5,F0 FADDDP F0,F3 FMOVD 144(SP), F0 FMULD F5,F0 FADDDP F0,F2 FMOVD 136(SP), F0 FMULD F5,F0 FADDDP F0,F1 FMOVD 184(SP), F0 FMULDP F0,F5 FXCHD F0, F4 FADDDP F0,F3 FMOVD 144(SP), F0 FMULD F5,F0 FADDDP F0,F2 FMOVD 136(SP), F0 FMULD F5,F0 FADDDP F0,F1 FMOVD 184(SP), F0 FMULD F5,F0 FADDDP F0,F4 FMOVD 168(SP), F0 FMULDP F0,F5 FXCHD F0, F4 FADDDP F0,F2 FMOVD 136(SP), F0 FMULD F5,F0 FADDDP F0,F1 FMOVD 184(SP), F0 FMULD F5,F0 FADDDP F0,F4 FMOVD 168(SP), F0 FMULD F5,F0 FADDDP F0,F3 FMOVD 152(SP), F0 FMULDP F0,F5 FXCHD F0, F4 FADDDP F0,F1 ADDATMOST15BYTES: CMPQ DX,$0 JE NOMOREBYTES MOVL $0,0(SP) MOVL $0, 4 (SP) MOVL $0, 8 (SP) MOVL $0, 12 (SP) LEAQ 0(SP),DI MOVQ DX,CX REP; MOVSB MOVB $1,0(DI) MOVL 12 (SP),DI MOVL 8 (SP),SI MOVL 4 (SP),DX MOVL 0(SP),CX MOVL DI,128(SP) MOVL SI,120(SP) MOVL DX,112(SP) MOVL CX,104(SP) FXCHD F0, F3 FADDD 128(SP), F0 FSUBD ·DOFFSET3(SB), F0 FXCHD F0, F2 FADDD 120(SP), F0 FSUBD ·DOFFSET2(SB), F0 FXCHD F0, F1 FADDD 112(SP), F0 FSUBD ·DOFFSET1(SB), F0 FXCHD F0, F3 FADDD 104(SP), F0 FSUBD ·DOFFSET0(SB), F0 FMOVD ·ALPHA130(SB), F0 FADDD F3,F0 FSUBD ·ALPHA130(SB), F0 FSUBD F0,F3 FMULD ·SCALE(SB), F0 FMOVD ·ALPHA32(SB), F0 FADDD F2,F0 FSUBD ·ALPHA32(SB), F0 FSUBD F0,F2 FMOVD ·ALPHA64(SB), F0 FADDD F6,F0 FSUBD ·ALPHA64(SB), F0 FSUBD F0,F6 FMOVD ·ALPHA96(SB), F0 FADDD F5,F0 FSUBD ·ALPHA96(SB), F0 FSUBD F0,F5 FXCHD F0, F4 FADDDP F0,F3 FXCHD F0, F6 FADDDP F0,F1 FXCHD F0, F3 FADDDP F0,F5 FXCHD F0, F3 FADDDP F0,F1 FMOVD 176(SP), F0 FMULD F3,F0 FMOVD 160(SP), F0 FMULD F4,F0 FMOVD 144(SP), F0 FMULD F5,F0 FMOVD 136(SP), F0 FMULDP F0,F6 FMOVD 160(SP), F0 FMULD F5,F0 FADDDP F0,F3 FMOVD 144(SP), F0 FMULD F5,F0 FADDDP F0,F2 FMOVD 136(SP), F0 FMULD F5,F0 FADDDP F0,F1 FMOVD 184(SP), F0 FMULDP F0,F5 FXCHD F0, F4 FADDDP F0,F5 FMOVD 144(SP), F0 FMULD F6,F0 FADDDP F0,F2 FMOVD 136(SP), F0 FMULD F6,F0 FADDDP F0,F1 FMOVD 184(SP), F0 FMULD F6,F0 FADDDP F0,F4 FMOVD 168(SP), F0 FMULDP F0,F6 FXCHD F0, F5 FADDDP F0,F4 FMOVD 136(SP), F0 FMULD F2,F0 FADDDP F0,F1 FMOVD 184(SP), F0 FMULD F2,F0 FADDDP F0,F5 FMOVD 168(SP), F0 FMULD F2,F0 FADDDP F0,F3 FMOVD 152(SP), F0 FMULDP F0,F2 FXCHD F0, F1 FADDDP F0,F3 FXCHD F0, F3 FXCHD F0, F2 NOMOREBYTES: MOVL $0,R10 FMOVD ·ALPHA130(SB), F0 FADDD F4,F0 FSUBD ·ALPHA130(SB), F0 FSUBD F0,F4 FMULD ·SCALE(SB), F0 FMOVD ·ALPHA32(SB), F0 FADDD F2,F0 FSUBD ·ALPHA32(SB), F0 FSUBD F0,F2 FMOVD ·ALPHA64(SB), F0 FADDD F4,F0 FSUBD ·ALPHA64(SB), F0 FSUBD F0,F4 FMOVD ·ALPHA96(SB), F0 FADDD F6,F0 FSUBD ·ALPHA96(SB), F0 FXCHD F0, F6 FSUBD F6,F0 FXCHD F0, F4 FADDDP F0,F3 FXCHD F0, F4 FADDDP F0,F1 FXCHD F0, F2 FADDDP F0,F3 FXCHD F0, F4 FADDDP F0,F3 FXCHD F0, F3 FADDD ·HOFFSET0(SB), F0 FXCHD F0, F3 FADDD ·HOFFSET1(SB), F0 FXCHD F0, F1 FADDD ·HOFFSET2(SB), F0 FXCHD F0, F2 FADDD ·HOFFSET3(SB), F0 FXCHD F0, F3 FMOVDP F0, 104(SP) FMOVDP F0, 112(SP) FMOVDP F0, 120(SP) FMOVDP F0, 128(SP) MOVL 108(SP),DI ANDL $63,DI MOVL 116(SP),SI ANDL $63,SI MOVL 124(SP),DX ANDL $63,DX MOVL 132(SP),CX ANDL $63,CX MOVL 112(SP),R8 ADDL DI,R8 MOVQ R8,112(SP) MOVL 120(SP),DI ADCL SI,DI MOVQ DI,120(SP) MOVL 128(SP),DI ADCL DX,DI MOVQ DI,128(SP) MOVL R10,DI ADCL CX,DI MOVQ DI,136(SP) MOVQ $5,DI MOVL 104(SP),SI ADDL SI,DI MOVQ DI,104(SP) MOVL R10,DI MOVQ 112(SP),DX ADCL DX,DI MOVQ DI,112(SP) MOVL R10,DI MOVQ 120(SP),CX ADCL CX,DI MOVQ DI,120(SP) MOVL R10,DI MOVQ 128(SP),R8 ADCL R8,DI MOVQ DI,128(SP) MOVQ $0XFFFFFFFC,DI MOVQ 136(SP),R9 ADCL R9,DI SARL $16,DI MOVQ DI,R9 XORL $0XFFFFFFFF,R9 ANDQ DI,SI MOVQ 104(SP),AX ANDQ R9,AX ORQ AX,SI ANDQ DI,DX MOVQ 112(SP),AX ANDQ R9,AX ORQ AX,DX ANDQ DI,CX MOVQ 120(SP),AX ANDQ R9,AX ORQ AX,CX ANDQ DI,R8 MOVQ 128(SP),DI ANDQ R9,DI ORQ DI,R8 MOVQ 88(SP),DI MOVQ 96(SP),R9 ADDL 16(R9),SI ADCL 20(R9),DX ADCL 24(R9),CX ADCL 28(R9),R8 MOVL SI,0(DI) MOVL DX,4(DI) MOVL CX,8(DI) MOVL R8,12(DI) MOVQ 32(SP),R11 MOVQ 40(SP),R12 MOVQ 48(SP),R13 MOVQ 56(SP),R14 MOVQ 64(SP),R15 MOVQ 72(SP),BX MOVQ 80(SP),BP MOVQ R11,SP RET �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/�����������������������������������������������0000755�0000153�0000161�00000000000�12321736016�022154� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/certs_test.go����������������������������������0000644�0000153�0000161�00000004423�12321736016�024665� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "bytes" "testing" ) // Cert generated by ssh-keygen 6.0p1 Debian-4. // % ssh-keygen -s ca-key -I test user-key var exampleSSHCert = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgb1srW/W3ZDjYAO45xLYAwzHBDLsJ4Ux6ICFIkTjb1LEAAAADAQABAAAAYQCkoR51poH0wE8w72cqSB8Sszx+vAhzcMdCO0wqHTj7UNENHWEXGrU0E0UQekD7U+yhkhtoyjbPOVIP7hNa6aRk/ezdh/iUnCIt4Jt1v3Z1h1P+hA4QuYFMHNB+rmjPwAcAAAAAAAAAAAAAAAEAAAAEdGVzdAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAHcAAAAHc3NoLXJzYQAAAAMBAAEAAABhANFS2kaktpSGc+CcmEKPyw9mJC4nZKxHKTgLVZeaGbFZOvJTNzBspQHdy7Q1uKSfktxpgjZnksiu/tFF9ngyY2KFoc+U88ya95IZUycBGCUbBQ8+bhDtw/icdDGQD5WnUwAAAG8AAAAHc3NoLXJzYQAAAGC8Y9Z2LQKhIhxf52773XaWrXdxP0t3GBVo4A10vUWiYoAGepr6rQIoGGXFxT4B9Gp+nEBJjOwKDXPrAevow0T9ca8gZN+0ykbhSrXLE5Ao48rqr3zP4O1/9P7e6gp0gw8=` func TestParseCert(t *testing.T) { authKeyBytes := []byte(exampleSSHCert) key, _, _, rest, ok := ParseAuthorizedKey(authKeyBytes) if !ok { t.Fatalf("could not parse certificate") } if len(rest) > 0 { t.Errorf("rest: got %q, want empty", rest) } if _, ok = key.(*OpenSSHCertV01); !ok { t.Fatalf("got %#v, want *OpenSSHCertV01", key) } marshaled := MarshalAuthorizedKey(key) // Before comparison, remove the trailing newline that // MarshalAuthorizedKey adds. marshaled = marshaled[:len(marshaled)-1] if !bytes.Equal(authKeyBytes, marshaled) { t.Errorf("marshaled certificate does not match original: got %q, want %q", marshaled, authKeyBytes) } } func TestVerifyCert(t *testing.T) { key, _, _, _, _ := ParseAuthorizedKey([]byte(exampleSSHCert)) validCert := key.(*OpenSSHCertV01) if ok := validateOpenSSHCertV01Signature(validCert); !ok { t.Error("Unable to validate certificate!") } invalidCert := &OpenSSHCertV01{ Key: rsaKey.PublicKey(), SignatureKey: ecdsaKey.PublicKey(), Signature: &signature{}, } if ok := validateOpenSSHCertV01Signature(invalidCert); ok { t.Error("Invalid cert signature passed validation!") } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/test/������������������������������������������0000755�0000153�0000161�00000000000�12321736016�023133� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/test/forward_unix_test.go����������������������0000644�0000153�0000161�00000006327�12321736016�027240� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd package test import ( "bytes" "io" "io/ioutil" "math/rand" "net" "testing" "time" ) func TestPortForward(t *testing.T) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) defer conn.Close() sshListener, err := conn.Listen("tcp", "localhost:0") if err != nil { t.Fatal(err) } go func() { sshConn, err := sshListener.Accept() if err != nil { t.Fatalf("listen.Accept failed: %v", err) } _, err = io.Copy(sshConn, sshConn) if err != nil && err != io.EOF { t.Fatalf("ssh client copy: %v", err) } sshConn.Close() }() forwardedAddr := sshListener.Addr().String() tcpConn, err := net.Dial("tcp", forwardedAddr) if err != nil { t.Fatalf("TCP dial failed: %v", err) } readChan := make(chan []byte) go func() { data, _ := ioutil.ReadAll(tcpConn) readChan <- data }() // Invent some data. data := make([]byte, 100*1000) for i := range data { data[i] = byte(i % 255) } var sent []byte for len(sent) < 1000*1000 { // Send random sized chunks m := rand.Intn(len(data)) n, err := tcpConn.Write(data[:m]) if err != nil { break } sent = append(sent, data[:n]...) } if err := tcpConn.(*net.TCPConn).CloseWrite(); err != nil { t.Errorf("tcpConn.CloseWrite: %v", err) } read := <-readChan if len(sent) != len(read) { t.Fatalf("got %d bytes, want %d", len(read), len(sent)) } if bytes.Compare(sent, read) != 0 { t.Fatalf("read back data does not match") } if err := sshListener.Close(); err != nil { t.Fatalf("sshListener.Close: %v", err) } // Check that the forward disappeared. tcpConn, err = net.Dial("tcp", forwardedAddr) if err == nil { tcpConn.Close() t.Errorf("still listening to %s after closing", forwardedAddr) } } func TestAcceptClose(t *testing.T) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) sshListener, err := conn.Listen("tcp", "localhost:0") if err != nil { t.Fatal(err) } quit := make(chan error, 1) go func() { for { c, err := sshListener.Accept() if err != nil { quit <- err break } c.Close() } }() sshListener.Close() select { case <-time.After(1 * time.Second): t.Errorf("timeout: listener did not close.") case err := <-quit: t.Logf("quit as expected (error %v)", err) } } // Check that listeners exit if the underlying client transport dies. func TestPortForwardConnectionClose(t *testing.T) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) sshListener, err := conn.Listen("tcp", "localhost:0") if err != nil { t.Fatal(err) } quit := make(chan error, 1) go func() { for { c, err := sshListener.Accept() if err != nil { quit <- err break } c.Close() } }() // It would be even nicer if we closed the server side, but it // is more involved as the fd for that side is dup()ed. server.clientConn.Close() select { case <-time.After(1 * time.Second): t.Errorf("timeout: listener did not close.") case err := <-quit: t.Logf("quit as expected (error %v)", err) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/test/doc.go������������������������������������0000644�0000153�0000161�00000000414�12321735647�024237� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This package contains integration tests for the // code.google.com/p/go.crypto/ssh package. package test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/test/keys_test.go������������������������������0000644�0000153�0000161�00000024373�12321736016�025505� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package test import ( "reflect" "strings" "testing" "code.google.com/p/go.crypto/ssh" ) var ( validKey = `AAAAB3NzaC1yc2EAAAADAQABAAABAQDEX/dPu4PmtvgK3La9zioCEDrJ` + `yUr6xEIK7Pr+rLgydcqWTU/kt7w7gKjOw4vvzgHfjKl09CWyvgb+y5dCiTk` + `9MxI+erGNhs3pwaoS+EavAbawB7iEqYyTep3YaJK+4RJ4OX7ZlXMAIMrTL+` + `UVrK89t56hCkFYaAgo3VY+z6rb/b3bDBYtE1Y2tS7C3au73aDgeb9psIrSV` + `86ucKBTl5X62FnYiyGd++xCnLB6uLximM5OKXfLzJQNS/QyZyk12g3D8y69` + `Xw1GzCSKX1u1+MQboyf0HJcG2ryUCLHdcDVppApyHx2OLq53hlkQ/yxdflD` + `qCqAE4j+doagSsIfC1T2T` authWithOptions = []string{ `# comments to ignore before any keys...`, ``, `env="HOME=/home/root",no-port-forwarding ssh-rsa ` + validKey + ` user@host`, `# comments to ignore, along with a blank line`, ``, `env="HOME=/home/root2" ssh-rsa ` + validKey + ` user2@host2`, ``, `# more comments, plus a invalid entry`, `ssh-rsa data-that-will-not-parse user@host3`, } authOptions = strings.Join(authWithOptions, "\n") authWithCRLF = strings.Join(authWithOptions, "\r\n") authInvalid = []byte(`ssh-rsa`) authWithQuotedCommaInEnv = []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + validKey + ` user@host`) authWithQuotedSpaceInEnv = []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + validKey + ` user@host`) authWithQuotedQuoteInEnv = []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + validKey + ` user@host`) authWithDoubleQuotedQuote = []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + validKey + "\t" + `user@host`) authWithInvalidSpace = []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + validKey + ` user@host #more to follow but still no valid keys`) authWithMissingQuote = []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + validKey + ` user@host env="HOME=/home/root",shared-control ssh-rsa ` + validKey + ` user@host`) testClientPrivateKey = `-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAxF/3T7uD5rb4Cty2vc4qAhA6yclK+sRCCuz6/qy4MnXKlk1P 5Le8O4CozsOL784B34ypdPQlsr4G/suXQok5PTMSPnqxjYbN6cGqEvhGrwG2sAe4 hKmMk3qd2GiSvuESeDl+2ZVzACDK0y/lFayvPbeeoQpBWGgIKN1WPs+q2/292wwW LRNWNrUuwt2ru92g4Hm/abCK0lfOrnCgU5eV+thZ2IshnfvsQpyweri8YpjOTil3 y8yUDUv0MmcpNdoNw/MuvV8NRswkil9btfjEG6Mn9ByXBtq8lAix3XA1aaQKch8d ji6ud4ZZEP8sXX5Q6gqgBOI/naGoErCHwtU9kwIDAQABAoIBAFJRKAp0QEZmTHPB MZk+4r0asIoFpziXLFgIHu7C2DPOzK1Umzj1DCKlPB3wOqi7Ym2jOSWdcnAK2EPW dAGgJC5TSkKGjAcXixmB5RkumfKidUI0+lQh/puTurcMnvcEwglDkLkEvMBA/sSo Pw9m486rOgOnmNzGPyViItURmD2+0yDdLl/vOsO/L1p76GCd0q0J3LqnmsQmawi7 Zwj2Stm6BIrggG5GsF204Iet5219TYLo4g1Qb2AlJ9C8P1FtAWhMwJalDxH9Os2/ KCDjnaq5n3bXbIU+3QjskjeVXL/Fnbhjnh4zs1EA7eHzl9dCGbcZ2LOimo2PRo8q wVQmz4ECgYEA9dhiu74TxRVoaO5N2X+FsMzRO8gZdP3Z9IrV4jVN8WT4Vdp0snoF gkVkqqbQUNKUb5K6B3Js/qNKfcjLbCNq9fewTcT6WsHQdtPbX/QA6Pa2Z29wrlA2 wrIYaAkmVaHny7wsOmgX01aOnuf2MlUnksK43sjZHdIo/m+sDKwwY1cCgYEAzHx4 mwUDMdRF4qpDKJhthraBNejRextNQQYsHVnNaMwZ4aeQcH5l85Cgjm7VpGlbVyBQ h4zwFvllImp3D2U3mjVkV8Tm9ID98eWvw2YDzBnS3P3SysajD23Z+BXSG9GNv/8k oAm+bVlvnJy4haK2AcIMk1YFuDuAOmy73abk7iUCgYEAj4qVM1sq/eKfAM1LJRfg /jbIX+hYfMePD8pUUWygIra6jJ4tjtvSBZrwyPb3IImjY3W/KoP0AcVjxAeORohz dkP1a6L8LiuFxSuzpdW5BkyuebxGhXCOWKVVvMDC4jLTPVCUXlHSv3GFemCjjgXM QlNxT5rjsha4Gr8nLIsJAacCgYA4VA1Q/pd7sXKy1p37X8nD8yAyvnh+Be5I/C9I woUP2jFC9MqYAmmJJ4ziz2swiAkuPeuQ+2Tjnz2ZtmQnrIUdiJmkh8vrDGFnshKx q7deELsCPzVCwGcIiAUkDra7DQWUHu9y2lxHePyC0rUNst2aLF8UcvzOXC2danhx vViQtQKBgCmZ7YavE/GNWww8N3xHBJ6UPmUuhQlnAbgNCcdyz30MevBg/JbyUTs2 slftTH15QusJ1UoITnnZuFJ40LqDvh8UhiK09ffM/IbUx839/m2vUOdFZB/WNn9g Cy0LzddU4KE8JZ/tlk68+hM5fjLLA0aqSunaql5CKfplwLu8x1hL -----END RSA PRIVATE KEY----- ` keys = map[string]string{ "ssh_host_dsa_key": `-----BEGIN DSA PRIVATE KEY----- MIIBugIBAAKBgQDe2SIKvZdBp+InawtSXH0NotiMPhm3udyu4hh/E+icMz264kDX v+sV7ddnSQGQWZ/eVU7Jtx29dCMD1VlFpEd7yGKzmdwJIeA+YquNWoqBRQEJsWWS 7Fsfvv83dA/DTNIQfOY3+TIs6Mb9vagbgQMU3JUWEhbLE9LCEU6UwwRlpQIVAL4p JF83SwpE8Jx6KnDpR89npkl/AoGAAy00TdDnAXvStwrZiAFbjZi8xDmPa9WwpfhJ Rkno45TthDLrS+WmqY8/LTwlqZdOBtoBAynMJfKkUiZM21lWWpL1hRKYdwBlIBy5 XdR2/6wcPSuZ0tCQhDBTstX0Q3P1j198KGKvzy7q9vILKQwtSRqLS1y4JJERafdO E+9CnGwCgYBz0WwBe2EZtGhGhBdnelTIBeo7PIsr0PzqxQj+dc8PBl8K9FfhRyOp U39stUvoUxE9vaIFrY1P5xENjLFnPf+hlcuf40GUWEssW9YWPOaBp8afa9hY5Sxs pvNR6eZFEFOJnx/ZgcA4g+vbrgGi5cM0W470mbGw2CkfJQUafdoIgAIUF+2I9kZe 2FTBuC9uacqczDlc+0k= -----END DSA PRIVATE KEY-----`, "ssh_host_rsa_key": `-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAuf76Ue2Wtae9oDtaS6rIJgO7iCFTsZUTW9LBsvx/2nli6jKU d9tUbBRzgdbnRLJ32UljXhERuB/axlrX8/lBzUZ+oYiM0KkEEOXY1z/bcMxdRxGF XHuf4uXvyC2XyA4+ZvBeS4j1QFyIHZ62o7gAlKMTjiek3B4AQEJAlCLmhH3jB8wc K/IYXAOlNGM5G44/ZLQpTi8diOV6DLs7tJ7rtEQedOEJfZng5rwp0USFkqcbfDbe 9/hk0J32jZvOtZNBokYtBb4YEdIiWBzzNtHzU3Dzw61+TKVXaH5HaIvzL9iMrw9f kJbJyogfZk9BJfemEN+xqP72jlhE8LXNhpTxFQIDAQABAoIBAHbdf+Y5+5XuNF6h b8xpwW2h9whBnDYiOnP1VfroKWFbMB7R4lZS4joMO+FfkP8zOyqvHwTvza4pFWys g9SUmDvy8FyVYsC7MzEFYzX0xm3o/Te898ip7P1Zy4rXsGeWysSImwqU5X+TYx3i 33/zyNM1APtZVJ+jwK9QZ+sD/uPuZK2yS03HGSMZq6ebdoOSaYhluKrxXllSLO1J KJxDiDdy2lEFw0W8HcI3ly1lg6OI+TRqqaCcLVNF4fNJmYIFM+2VEI9BdgynIh0Q pMZlJKgaEBcSqCymnTK81ohYD1cV4st2B0km3Sw35Rl04Ij5ITeiya3hp8VfE6UY PljkA6UCgYEA4811FTFj+kzNZ86C4OW1T5sM4NZt8gcz6CSvVnl+bDzbEOMMyzP7 2I9zKsR5ApdodH2m8d+RUw1Oe0bNGW5xig/DH/hn9lLQaO52JAi0we8A94dUUMSq fUk9jKZEXpP/MlfTdJaPos9mxT7z8jREQxIiqH9AV0rLVDOCfDbSWj8CgYEA0QTE IAUuki3UUqYKzLQrh/QmhY5KTx5amNW9XZ2VGtJvDPJrtBSBZlPEuXZAc4eBWEc7 U3Y9QwsalzupU6Yi6+gmofaXs8xJnj+jKth1DnJvrbLLGlSmf2Ijnwt22TyFUOtt UAknpjHutDjQPf7pUGWaCPgwwKFsdB8EBjpJF6sCgYAfXesBQAvEK08dPBJJZVfR 3kenrd71tIgxLtv1zETcIoUHjjv0vvOunhH9kZAYC0EWyTZzl5UrGmn0D4uuNMbt e74iaNHn2P9Zc3xQ+eHp0j8P1lKFzI6tMaiH9Vz0qOw6wl0bcJ/WizhbcI+migvc MGMVUHBLlMDqly0gbWwJgQKBgQCgtb9ut01FjANSwORQ3L8Tu3/a9Lrh9n7GQKFn V4CLrP1BwStavOF5ojMCPo/zxF6JV8ufsqwL3n/FhFP/QyBarpb1tTqTPiHkkR2O Ffx67TY9IdnUFv4lt3mYEiKBiW0f+MSF42Qe/wmAfKZw5IzUCirTdrFVi0huSGK5 vxrwHQKBgHZ7RoC3I2f6F5fflA2ZAe9oJYC7XT624rY7VeOBwK0W0F47iV3euPi/ pKvLIBLcWL1Lboo+girnmSZtIYg2iLS3b4T9VFcKWg0y4AVwmhMWe9jWIltfWAAX 9l0lNikMRGAx3eXudKXEtbGt3/cUzPVaQUHy5LiBxkxnFxgaJPXs -----END RSA PRIVATE KEY-----`, "ssh_host_ecdsa_key": `-----BEGIN EC PRIVATE KEY----- MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49 AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+ 6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA== -----END EC PRIVATE KEY-----`, "authorized_keys": `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEX/dPu4PmtvgK3La9zioCEDrJyUr6xEIK7Pr+rLgydcqWTU/kt7w7gKjOw4vvzgHfjKl09CWyvgb+y5dCiTk9MxI+erGNhs3pwaoS+EavAbawB7iEqYyTep3YaJK+4RJ4OX7ZlXMAIMrTL+UVrK89t56hCkFYaAgo3VY+z6rb/b3bDBYtE1Y2tS7C3au73aDgeb9psIrSV86ucKBTl5X62FnYiyGd++xCnLB6uLximM5OKXfLzJQNS/QyZyk12g3D8y69Xw1GzCSKX1u1+MQboyf0HJcG2ryUCLHdcDVppApyHx2OLq53hlkQ/yxdflDqCqAE4j+doagSsIfC1T2T user@host`, } ) func TestMarshalParsePublicKey(t *testing.T) { pub := getTestPublicKey(t) authKeys := ssh.MarshalAuthorizedKey(pub) actualFields := strings.Fields(string(authKeys)) if len(actualFields) == 0 { t.Fatalf("failed authKeys: %v", authKeys) } // drop the comment expectedFields := strings.Fields(keys["authorized_keys"])[0:2] if !reflect.DeepEqual(actualFields, expectedFields) { t.Errorf("got %v, expected %v", actualFields, expectedFields) } actPub, _, _, _, ok := ssh.ParseAuthorizedKey([]byte(keys["authorized_keys"])) if !ok { t.Fatalf("cannot parse %v", keys["authorized_keys"]) } if !reflect.DeepEqual(actPub, pub) { t.Errorf("got %v, expected %v", actPub, pub) } } type authResult struct { pubKey interface{} //*rsa.PublicKey options []string comments string rest string ok bool } func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) { rest := authKeys var values []authResult for len(rest) > 0 { var r authResult r.pubKey, r.comments, r.options, rest, r.ok = ssh.ParseAuthorizedKey(rest) r.rest = string(rest) values = append(values, r) } if !reflect.DeepEqual(values, expected) { t.Errorf("got %q, expected %q", values, expected) } } func getTestPublicKey(t *testing.T) ssh.PublicKey { priv, err := ssh.ParsePrivateKey([]byte(testClientPrivateKey)) if err != nil { t.Fatalf("ParsePrivateKey: %v", err) } return priv.PublicKey() } func TestAuth(t *testing.T) { pub := getTestPublicKey(t) rest2 := strings.Join(authWithOptions[3:], "\n") rest3 := strings.Join(authWithOptions[6:], "\n") testAuthorizedKeys(t, []byte(authOptions), []authResult{ {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true}, {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true}, {nil, nil, "", "", false}, }) } func TestAuthWithCRLF(t *testing.T) { pub := getTestPublicKey(t) rest2 := strings.Join(authWithOptions[3:], "\r\n") rest3 := strings.Join(authWithOptions[6:], "\r\n") testAuthorizedKeys(t, []byte(authWithCRLF), []authResult{ {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true}, {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true}, {nil, nil, "", "", false}, }) } func TestAuthWithQuotedSpaceInEnv(t *testing.T) { pub := getTestPublicKey(t) testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{ {pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true}, }) } func TestAuthWithQuotedCommaInEnv(t *testing.T) { pub := getTestPublicKey(t) testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{ {pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true}, }) } func TestAuthWithQuotedQuoteInEnv(t *testing.T) { pub := getTestPublicKey(t) testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{ {pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true}, }) testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{ {pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true}, }) } func TestAuthWithInvalidSpace(t *testing.T) { testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{ {nil, nil, "", "", false}, }) } func TestAuthWithMissingQuote(t *testing.T) { pub := getTestPublicKey(t) testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{ {pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true}, }) } func TestInvalidEntry(t *testing.T) { _, _, _, _, ok := ssh.ParseAuthorizedKey(authInvalid) if ok { t.Errorf("Expected invalid entry, returned valid entry") } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/test/session_test.go���������������������������0000644�0000153�0000161�00000010063�12321736016�026204� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 !windows package test // Session functional tests. import ( "bytes" "code.google.com/p/go.crypto/ssh" "io" "strings" "testing" ) func TestRunCommandSuccess(t *testing.T) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("session failed: %v", err) } defer session.Close() err = session.Run("true") if err != nil { t.Fatalf("session failed: %v", err) } } func TestHostKeyCheck(t *testing.T) { server := newServer(t) defer server.Shutdown() conf := clientConfig() k := conf.HostKeyChecker.(*storedHostKey) // change the keys. k.keys[ssh.KeyAlgoRSA][25]++ k.keys[ssh.KeyAlgoDSA][25]++ k.keys[ssh.KeyAlgoECDSA256][25]++ conn, err := server.TryDial(conf) if err == nil { conn.Close() t.Fatalf("dial should have failed.") } else if !strings.Contains(err.Error(), "host key mismatch") { t.Fatalf("'host key mismatch' not found in %v", err) } } func TestRunCommandFailed(t *testing.T) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("session failed: %v", err) } defer session.Close() err = session.Run(`bash -c "kill -9 $$"`) if err == nil { t.Fatalf("session succeeded: %v", err) } } func TestRunCommandWeClosed(t *testing.T) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("session failed: %v", err) } err = session.Shell() if err != nil { t.Fatalf("shell failed: %v", err) } err = session.Close() if err != nil { t.Fatalf("shell failed: %v", err) } } func TestFuncLargeRead(t *testing.T) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("unable to create new session: %s", err) } stdout, err := session.StdoutPipe() if err != nil { t.Fatalf("unable to acquire stdout pipe: %s", err) } err = session.Start("dd if=/dev/urandom bs=2048 count=1") if err != nil { t.Fatalf("unable to execute remote command: %s", err) } buf := new(bytes.Buffer) n, err := io.Copy(buf, stdout) if err != nil { t.Fatalf("error reading from remote stdout: %s", err) } if n != 2048 { t.Fatalf("Expected %d bytes but read only %d from remote command", 2048, n) } } func TestInvalidTerminalMode(t *testing.T) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("session failed: %v", err) } defer session.Close() if err = session.RequestPty("vt100", 80, 40, ssh.TerminalModes{255: 1984}); err == nil { t.Fatalf("req-pty failed: successful request with invalid mode") } } func TestValidTerminalMode(t *testing.T) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("session failed: %v", err) } defer session.Close() stdout, err := session.StdoutPipe() if err != nil { t.Fatalf("unable to acquire stdout pipe: %s", err) } stdin, err := session.StdinPipe() if err != nil { t.Fatalf("unable to acquire stdin pipe: %s", err) } tm := ssh.TerminalModes{ssh.ECHO: 0} if err = session.RequestPty("xterm", 80, 40, tm); err != nil { t.Fatalf("req-pty failed: %s", err) } err = session.Shell() if err != nil { t.Fatalf("session failed: %s", err) } stdin.Write([]byte("stty -a && exit\n")) var buf bytes.Buffer if _, err := io.Copy(&buf, stdout); err != nil { t.Fatalf("reading failed: %s", err) } if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "-echo ") { t.Fatalf("terminal mode failure: expected -echo in stty output, got %s", sttyOutput) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/test/tcpip_test.go�����������������������������0000644�0000153�0000161�00000001675�12321736016�025651� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 !windows package test // direct-tcpip functional tests import ( "net" "net/http" "testing" ) func TestTCPIPHTTP(t *testing.T) { // google.com will generate at least one redirect, possibly three // depending on your location. doTest(t, "http://google.com") } func TestTCPIPHTTPS(t *testing.T) { doTest(t, "https://encrypted.google.com/") } func doTest(t *testing.T, url string) { server := newServer(t) defer server.Shutdown() conn := server.Dial(clientConfig()) defer conn.Close() tr := &http.Transport{ Dial: func(n, addr string) (net.Conn, error) { return conn.Dial(n, addr) }, } client := &http.Client{ Transport: tr, } resp, err := client.Get(url) if err != nil { t.Fatalf("unable to proxy: %s", err) } // got a body without error t.Log(resp) } �������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/test/test_unix_test.go�������������������������0000644�0000153�0000161�00000014614�12321736016�026551� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 darwin freebsd linux netbsd openbsd package test // functional test harness for unix. import ( "bytes" "fmt" "io" "io/ioutil" "log" "net" "os" "os/exec" "os/user" "path/filepath" "testing" "text/template" "code.google.com/p/go.crypto/ssh" ) const sshd_config = ` Protocol 2 HostKey {{.Dir}}/ssh_host_rsa_key HostKey {{.Dir}}/ssh_host_dsa_key HostKey {{.Dir}}/ssh_host_ecdsa_key Pidfile {{.Dir}}/sshd.pid #UsePrivilegeSeparation no KeyRegenerationInterval 3600 ServerKeyBits 768 SyslogFacility AUTH LogLevel DEBUG2 LoginGraceTime 120 PermitRootLogin no StrictModes no RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile {{.Dir}}/authorized_keys IgnoreRhosts yes RhostsRSAAuthentication no HostbasedAuthentication no ` var ( configTmpl template.Template privateKey ssh.Signer hostKeyRSA ssh.Signer hostKeyECDSA ssh.Signer hostKeyDSA ssh.Signer ) func init() { template.Must(configTmpl.Parse(sshd_config)) for n, k := range map[string]*ssh.Signer{ "ssh_host_ecdsa_key": &hostKeyECDSA, "ssh_host_rsa_key": &hostKeyRSA, "ssh_host_dsa_key": &hostKeyDSA, } { var err error *k, err = ssh.ParsePrivateKey([]byte(keys[n])) if err != nil { panic(fmt.Sprintf("ParsePrivateKey(%q): %v", n, err)) } } var err error privateKey, err = ssh.ParsePrivateKey([]byte(testClientPrivateKey)) if err != nil { panic(fmt.Sprintf("ParsePrivateKey: %v", err)) } } type server struct { t *testing.T cleanup func() // executed during Shutdown configfile string cmd *exec.Cmd output bytes.Buffer // holds stderr from sshd process // Client half of the network connection. clientConn net.Conn } func username() string { var username string if user, err := user.Current(); err == nil { username = user.Username } else { // user.Current() currently requires cgo. If an error is // returned attempt to get the username from the environment. log.Printf("user.Current: %v; falling back on $USER", err) username = os.Getenv("USER") } if username == "" { panic("Unable to get username") } return username } type storedHostKey struct { // keys map from an algorithm string to binary key data. keys map[string][]byte } func (k *storedHostKey) Add(key ssh.PublicKey) { if k.keys == nil { k.keys = map[string][]byte{} } k.keys[key.PublicKeyAlgo()] = ssh.MarshalPublicKey(key) } func (k *storedHostKey) Check(addr string, remote net.Addr, algo string, key []byte) error { if k.keys == nil || bytes.Compare(key, k.keys[algo]) != 0 { return fmt.Errorf("host key mismatch. Got %q, want %q", key, k.keys[algo]) } return nil } func clientConfig() *ssh.ClientConfig { keyChecker := storedHostKey{} keyChecker.Add(hostKeyECDSA.PublicKey()) keyChecker.Add(hostKeyRSA.PublicKey()) keyChecker.Add(hostKeyDSA.PublicKey()) kc := new(keychain) kc.keys = append(kc.keys, privateKey) config := &ssh.ClientConfig{ User: username(), Auth: []ssh.ClientAuth{ ssh.ClientAuthKeyring(kc), }, HostKeyChecker: &keyChecker, } return config } // unixConnection creates two halves of a connected net.UnixConn. It // is used for connecting the Go SSH client with sshd without opening // ports. func unixConnection() (*net.UnixConn, *net.UnixConn, error) { dir, err := ioutil.TempDir("", "unixConnection") if err != nil { return nil, nil, err } defer os.Remove(dir) addr := filepath.Join(dir, "ssh") listener, err := net.Listen("unix", addr) if err != nil { return nil, nil, err } defer listener.Close() c1, err := net.Dial("unix", addr) if err != nil { return nil, nil, err } c2, err := listener.Accept() if err != nil { c1.Close() return nil, nil, err } return c1.(*net.UnixConn), c2.(*net.UnixConn), nil } func (s *server) TryDial(config *ssh.ClientConfig) (*ssh.ClientConn, error) { sshd, err := exec.LookPath("sshd") if err != nil { s.t.Skipf("skipping test: %v", err) } c1, c2, err := unixConnection() if err != nil { s.t.Fatalf("unixConnection: %v", err) } s.cmd = exec.Command(sshd, "-f", s.configfile, "-i", "-e") f, err := c2.File() if err != nil { s.t.Fatalf("UnixConn.File: %v", err) } defer f.Close() s.cmd.Stdin = f s.cmd.Stdout = f s.cmd.Stderr = &s.output if err := s.cmd.Start(); err != nil { s.t.Fail() s.Shutdown() s.t.Fatalf("s.cmd.Start: %v", err) } s.clientConn = c1 return ssh.Client(c1, config) } func (s *server) Dial(config *ssh.ClientConfig) *ssh.ClientConn { conn, err := s.TryDial(config) if err != nil { s.t.Fail() s.Shutdown() s.t.Fatalf("ssh.Client: %v", err) } return conn } func (s *server) Shutdown() { if s.cmd != nil && s.cmd.Process != nil { // Don't check for errors; if it fails it's most // likely "os: process already finished", and we don't // care about that. Use os.Interrupt, so child // processes are killed too. s.cmd.Process.Signal(os.Interrupt) s.cmd.Wait() } if s.t.Failed() { // log any output from sshd process s.t.Logf("sshd: %s", s.output.String()) } s.cleanup() } // newServer returns a new mock ssh server. func newServer(t *testing.T) *server { dir, err := ioutil.TempDir("", "sshtest") if err != nil { t.Fatal(err) } f, err := os.Create(filepath.Join(dir, "sshd_config")) if err != nil { t.Fatal(err) } err = configTmpl.Execute(f, map[string]string{ "Dir": dir, }) if err != nil { t.Fatal(err) } f.Close() for k, v := range keys { f, err := os.OpenFile(filepath.Join(dir, k), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600) if err != nil { t.Fatal(err) } if _, err := f.Write([]byte(v)); err != nil { t.Fatal(err) } f.Close() } return &server{ t: t, configfile: f.Name(), cleanup: func() { if err := os.RemoveAll(dir); err != nil { t.Error(err) } }, } } // keychain implements the ClientKeyring interface. type keychain struct { keys []ssh.Signer } func (k *keychain) Key(i int) (ssh.PublicKey, error) { if i < 0 || i >= len(k.keys) { return nil, nil } return k.keys[i].PublicKey(), nil } func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) { return k.keys[i].Sign(rand, data) } func (k *keychain) loadPEM(file string) error { buf, err := ioutil.ReadFile(file) if err != nil { return err } key, err := ssh.ParsePrivateKey(buf) if err != nil { return err } k.keys = append(k.keys, key) return nil } ��������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/tcpip.go���������������������������������������0000644�0000153�0000161�00000022431�12321736016�023624� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "errors" "fmt" "io" "math/rand" "net" "strconv" "strings" "sync" "time" ) // Listen requests the remote peer open a listening socket // on addr. Incoming connections will be available by calling // Accept on the returned net.Listener. func (c *ClientConn) Listen(n, addr string) (net.Listener, error) { laddr, err := net.ResolveTCPAddr(n, addr) if err != nil { return nil, err } return c.ListenTCP(laddr) } // Automatic port allocation is broken with OpenSSH before 6.0. See // also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In // particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0, // rather than the actual port number. This means you can never open // two different listeners with auto allocated ports. We work around // this by trying explicit ports until we succeed. const openSSHPrefix = "OpenSSH_" var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano())) // isBrokenOpenSSHVersion returns true if the given version string // specifies a version of OpenSSH that is known to have a bug in port // forwarding. func isBrokenOpenSSHVersion(versionStr string) bool { i := strings.Index(versionStr, openSSHPrefix) if i < 0 { return false } i += len(openSSHPrefix) j := i for ; j < len(versionStr); j++ { if versionStr[j] < '0' || versionStr[j] > '9' { break } } version, _ := strconv.Atoi(versionStr[i:j]) return version < 6 } // autoPortListenWorkaround simulates automatic port allocation by // trying random ports repeatedly. func (c *ClientConn) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) { var sshListener net.Listener var err error const tries = 10 for i := 0; i < tries; i++ { addr := *laddr addr.Port = 1024 + portRandomizer.Intn(60000) sshListener, err = c.ListenTCP(&addr) if err == nil { laddr.Port = addr.Port return sshListener, err } } return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err) } // RFC 4254 7.1 type channelForwardMsg struct { Message string WantReply bool raddr string rport uint32 } // ListenTCP requests the remote peer open a listening socket // on laddr. Incoming connections will be available by calling // Accept on the returned net.Listener. func (c *ClientConn) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { if laddr.Port == 0 && isBrokenOpenSSHVersion(c.serverVersion) { return c.autoPortListenWorkaround(laddr) } m := channelForwardMsg{ "tcpip-forward", true, // sendGlobalRequest waits for a reply laddr.IP.String(), uint32(laddr.Port), } // send message resp, err := c.sendGlobalRequest(m) if err != nil { return nil, err } // If the original port was 0, then the remote side will // supply a real port number in the response. if laddr.Port == 0 { port, _, ok := parseUint32(resp.Data) if !ok { return nil, errors.New("unable to parse response") } laddr.Port = int(port) } // Register this forward, using the port number we obtained. ch := c.forwardList.add(*laddr) return &tcpListener{laddr, c, ch}, nil } // forwardList stores a mapping between remote // forward requests and the tcpListeners. type forwardList struct { sync.Mutex entries []forwardEntry } // forwardEntry represents an established mapping of a laddr on a // remote ssh server to a channel connected to a tcpListener. type forwardEntry struct { laddr net.TCPAddr c chan forward } // forward represents an incoming forwarded tcpip connection. The // arguments to add/remove/lookup should be address as specified in // the original forward-request. type forward struct { c *clientChan // the ssh client channel underlying this forward raddr *net.TCPAddr // the raddr of the incoming connection } func (l *forwardList) add(addr net.TCPAddr) chan forward { l.Lock() defer l.Unlock() f := forwardEntry{ addr, make(chan forward, 1), } l.entries = append(l.entries, f) return f.c } // remove removes the forward entry, and the channel feeding its // listener. func (l *forwardList) remove(addr net.TCPAddr) { l.Lock() defer l.Unlock() for i, f := range l.entries { if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port { l.entries = append(l.entries[:i], l.entries[i+1:]...) close(f.c) return } } } // closeAll closes and clears all forwards. func (l *forwardList) closeAll() { l.Lock() defer l.Unlock() for _, f := range l.entries { close(f.c) } l.entries = nil } func (l *forwardList) lookup(addr net.TCPAddr) (chan forward, bool) { l.Lock() defer l.Unlock() for _, f := range l.entries { if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port { return f.c, true } } return nil, false } type tcpListener struct { laddr *net.TCPAddr conn *ClientConn in <-chan forward } // Accept waits for and returns the next connection to the listener. func (l *tcpListener) Accept() (net.Conn, error) { s, ok := <-l.in if !ok { return nil, io.EOF } return &tcpChanConn{ tcpChan: &tcpChan{ clientChan: s.c, Reader: s.c.stdout, Writer: s.c.stdin, }, laddr: l.laddr, raddr: s.raddr, }, nil } // Close closes the listener. func (l *tcpListener) Close() error { m := channelForwardMsg{ "cancel-tcpip-forward", true, l.laddr.IP.String(), uint32(l.laddr.Port), } l.conn.forwardList.remove(*l.laddr) if _, err := l.conn.sendGlobalRequest(m); err != nil { return err } return nil } // Addr returns the listener's network address. func (l *tcpListener) Addr() net.Addr { return l.laddr } // Dial initiates a connection to the addr from the remote host. // The resulting connection has a zero LocalAddr() and RemoteAddr(). func (c *ClientConn) Dial(n, addr string) (net.Conn, error) { // Parse the address into host and numeric port. host, portString, err := net.SplitHostPort(addr) if err != nil { return nil, err } port, err := strconv.ParseUint(portString, 10, 16) if err != nil { return nil, err } // Use a zero address for local and remote address. zeroAddr := &net.TCPAddr{ IP: net.IPv4zero, Port: 0, } ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port)) if err != nil { return nil, err } return &tcpChanConn{ tcpChan: ch, laddr: zeroAddr, raddr: zeroAddr, }, nil } // DialTCP connects to the remote address raddr on the network net, // which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used // as the local address for the connection. func (c *ClientConn) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) { if laddr == nil { laddr = &net.TCPAddr{ IP: net.IPv4zero, Port: 0, } } ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port) if err != nil { return nil, err } return &tcpChanConn{ tcpChan: ch, laddr: laddr, raddr: raddr, }, nil } // RFC 4254 7.2 type channelOpenDirectMsg struct { ChanType string PeersId uint32 PeersWindow uint32 MaxPacketSize uint32 raddr string rport uint32 laddr string lport uint32 } // dial opens a direct-tcpip connection to the remote server. laddr and raddr are passed as // strings and are expected to be resolvable at the remote end. func (c *ClientConn) dial(laddr string, lport int, raddr string, rport int) (*tcpChan, error) { ch := c.newChan(c.transport) if err := c.transport.writePacket(marshal(msgChannelOpen, channelOpenDirectMsg{ ChanType: "direct-tcpip", PeersId: ch.localId, PeersWindow: channelWindowSize, MaxPacketSize: channelMaxPacketSize, raddr: raddr, rport: uint32(rport), laddr: laddr, lport: uint32(lport), })); err != nil { c.chanList.remove(ch.localId) return nil, err } if err := ch.waitForChannelOpenResponse(); err != nil { c.chanList.remove(ch.localId) return nil, fmt.Errorf("ssh: unable to open direct tcpip connection: %v", err) } return &tcpChan{ clientChan: ch, Reader: ch.stdout, Writer: ch.stdin, }, nil } type tcpChan struct { *clientChan // the backing channel io.Reader io.Writer } // tcpChanConn fulfills the net.Conn interface without // the tcpChan having to hold laddr or raddr directly. type tcpChanConn struct { *tcpChan laddr, raddr net.Addr } // LocalAddr returns the local network address. func (t *tcpChanConn) LocalAddr() net.Addr { return t.laddr } // RemoteAddr returns the remote network address. func (t *tcpChanConn) RemoteAddr() net.Addr { return t.raddr } // SetDeadline sets the read and write deadlines associated // with the connection. func (t *tcpChanConn) SetDeadline(deadline time.Time) error { if err := t.SetReadDeadline(deadline); err != nil { return err } return t.SetWriteDeadline(deadline) } // SetReadDeadline sets the read deadline. // A zero value for t means Read will not time out. // After the deadline, the error from Read will implement net.Error // with Timeout() == true. func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { return errors.New("ssh: tcpChan: deadline not supported") } // SetWriteDeadline exists to satisfy the net.Conn interface // but is not implemented by this type. It always returns an error. func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { return errors.New("ssh: tcpChan: deadline not supported") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/messages.go������������������������������������0000644�0000153�0000161�00000034664�12321736016�024327� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "bytes" "encoding/binary" "io" "math/big" "reflect" ) // These are SSH message type numbers. They are scattered around several // documents but many were taken from [SSH-PARAMETERS]. const ( msgDisconnect = 1 msgIgnore = 2 msgUnimplemented = 3 msgDebug = 4 msgServiceRequest = 5 msgServiceAccept = 6 msgKexInit = 20 msgNewKeys = 21 // Diffie-Helman msgKexDHInit = 30 msgKexDHReply = 31 msgKexECDHInit = 30 msgKexECDHReply = 31 // Standard authentication messages msgUserAuthRequest = 50 msgUserAuthFailure = 51 msgUserAuthSuccess = 52 msgUserAuthBanner = 53 msgUserAuthPubKeyOk = 60 // Method specific messages msgUserAuthInfoRequest = 60 msgUserAuthInfoResponse = 61 msgGlobalRequest = 80 msgRequestSuccess = 81 msgRequestFailure = 82 // Channel manipulation msgChannelOpen = 90 msgChannelOpenConfirm = 91 msgChannelOpenFailure = 92 msgChannelWindowAdjust = 93 msgChannelData = 94 msgChannelExtendedData = 95 msgChannelEOF = 96 msgChannelClose = 97 msgChannelRequest = 98 msgChannelSuccess = 99 msgChannelFailure = 100 ) // SSH messages: // // These structures mirror the wire format of the corresponding SSH messages. // They are marshaled using reflection with the marshal and unmarshal functions // in this file. The only wrinkle is that a final member of type []byte with a // ssh tag of "rest" receives the remainder of a packet when unmarshaling. // See RFC 4253, section 11.1. type disconnectMsg struct { Reason uint32 Message string Language string } // See RFC 4253, section 7.1. type kexInitMsg struct { Cookie [16]byte KexAlgos []string ServerHostKeyAlgos []string CiphersClientServer []string CiphersServerClient []string MACsClientServer []string MACsServerClient []string CompressionClientServer []string CompressionServerClient []string LanguagesClientServer []string LanguagesServerClient []string FirstKexFollows bool Reserved uint32 } // See RFC 4253, section 8. type kexDHInitMsg struct { X *big.Int } type kexECDHInitMsg struct { ClientPubKey []byte } type kexECDHReplyMsg struct { HostKey []byte EphemeralPubKey []byte Signature []byte } type kexDHReplyMsg struct { HostKey []byte Y *big.Int Signature []byte } // See RFC 4253, section 10. type serviceRequestMsg struct { Service string } // See RFC 4253, section 10. type serviceAcceptMsg struct { Service string } // See RFC 4252, section 5. type userAuthRequestMsg struct { User string Service string Method string Payload []byte `ssh:"rest"` } // See RFC 4252, section 5.1 type userAuthFailureMsg struct { Methods []string PartialSuccess bool } // See RFC 4256, section 3.2 type userAuthInfoRequestMsg struct { User string Instruction string DeprecatedLanguage string NumPrompts uint32 Prompts []byte `ssh:"rest"` } // See RFC 4254, section 5.1. type channelOpenMsg struct { ChanType string PeersId uint32 PeersWindow uint32 MaxPacketSize uint32 TypeSpecificData []byte `ssh:"rest"` } // See RFC 4254, section 5.1. type channelOpenConfirmMsg struct { PeersId uint32 MyId uint32 MyWindow uint32 MaxPacketSize uint32 TypeSpecificData []byte `ssh:"rest"` } // See RFC 4254, section 5.1. type channelOpenFailureMsg struct { PeersId uint32 Reason RejectionReason Message string Language string } type channelRequestMsg struct { PeersId uint32 Request string WantReply bool RequestSpecificData []byte `ssh:"rest"` } // See RFC 4254, section 5.4. type channelRequestSuccessMsg struct { PeersId uint32 } // See RFC 4254, section 5.4. type channelRequestFailureMsg struct { PeersId uint32 } // See RFC 4254, section 5.3 type channelCloseMsg struct { PeersId uint32 } // See RFC 4254, section 5.3 type channelEOFMsg struct { PeersId uint32 } // See RFC 4254, section 4 type globalRequestMsg struct { Type string WantReply bool } // See RFC 4254, section 4 type globalRequestSuccessMsg struct { Data []byte `ssh:"rest"` } // See RFC 4254, section 4 type globalRequestFailureMsg struct { Data []byte `ssh:"rest"` } // See RFC 4254, section 5.2 type windowAdjustMsg struct { PeersId uint32 AdditionalBytes uint32 } // See RFC 4252, section 7 type userAuthPubKeyOkMsg struct { Algo string PubKey string } // unmarshal parses the SSH wire data in packet into out using // reflection. expectedType, if non-zero, is the SSH message type that // the packet is expected to start with. unmarshal either returns nil // on success, or a ParseError or UnexpectedMessageError on error. func unmarshal(out interface{}, packet []byte, expectedType uint8) error { if len(packet) == 0 { return ParseError{expectedType} } if expectedType > 0 { if packet[0] != expectedType { return UnexpectedMessageError{expectedType, packet[0]} } packet = packet[1:] } v := reflect.ValueOf(out).Elem() structType := v.Type() var ok bool for i := 0; i < v.NumField(); i++ { field := v.Field(i) t := field.Type() switch t.Kind() { case reflect.Bool: if len(packet) < 1 { return ParseError{expectedType} } field.SetBool(packet[0] != 0) packet = packet[1:] case reflect.Array: if t.Elem().Kind() != reflect.Uint8 { panic("array of non-uint8") } if len(packet) < t.Len() { return ParseError{expectedType} } for j, n := 0, t.Len(); j < n; j++ { field.Index(j).Set(reflect.ValueOf(packet[j])) } packet = packet[t.Len():] case reflect.Uint32: var u32 uint32 if u32, packet, ok = parseUint32(packet); !ok { return ParseError{expectedType} } field.SetUint(uint64(u32)) case reflect.String: var s []byte if s, packet, ok = parseString(packet); !ok { return ParseError{expectedType} } field.SetString(string(s)) case reflect.Slice: switch t.Elem().Kind() { case reflect.Uint8: if structType.Field(i).Tag.Get("ssh") == "rest" { field.Set(reflect.ValueOf(packet)) packet = nil } else { var s []byte if s, packet, ok = parseString(packet); !ok { return ParseError{expectedType} } field.Set(reflect.ValueOf(s)) } case reflect.String: var nl []string if nl, packet, ok = parseNameList(packet); !ok { return ParseError{expectedType} } field.Set(reflect.ValueOf(nl)) default: panic("slice of unknown type") } case reflect.Ptr: if t == bigIntType { var n *big.Int if n, packet, ok = parseInt(packet); !ok { return ParseError{expectedType} } field.Set(reflect.ValueOf(n)) } else { panic("pointer to unknown type") } default: panic("unknown type") } } if len(packet) != 0 { return ParseError{expectedType} } return nil } // marshal serializes the message in msg. The given message type is // prepended if it is non-zero. func marshal(msgType uint8, msg interface{}) []byte { out := make([]byte, 0, 64) if msgType > 0 { out = append(out, msgType) } v := reflect.ValueOf(msg) for i, n := 0, v.NumField(); i < n; i++ { field := v.Field(i) switch t := field.Type(); t.Kind() { case reflect.Bool: var v uint8 if field.Bool() { v = 1 } out = append(out, v) case reflect.Array: if t.Elem().Kind() != reflect.Uint8 { panic("array of non-uint8") } for j, l := 0, t.Len(); j < l; j++ { out = append(out, uint8(field.Index(j).Uint())) } case reflect.Uint32: out = appendU32(out, uint32(field.Uint())) case reflect.String: s := field.String() out = appendInt(out, len(s)) out = append(out, s...) case reflect.Slice: switch t.Elem().Kind() { case reflect.Uint8: if v.Type().Field(i).Tag.Get("ssh") != "rest" { out = appendInt(out, field.Len()) } out = append(out, field.Bytes()...) case reflect.String: offset := len(out) out = appendU32(out, 0) if n := field.Len(); n > 0 { for j := 0; j < n; j++ { f := field.Index(j) if j != 0 { out = append(out, ',') } out = append(out, f.String()...) } // overwrite length value binary.BigEndian.PutUint32(out[offset:], uint32(len(out)-offset-4)) } default: panic("slice of unknown type") } case reflect.Ptr: if t == bigIntType { var n *big.Int nValue := reflect.ValueOf(&n) nValue.Elem().Set(field) needed := intLength(n) oldLength := len(out) if cap(out)-len(out) < needed { newOut := make([]byte, len(out), 2*(len(out)+needed)) copy(newOut, out) out = newOut } out = out[:oldLength+needed] marshalInt(out[oldLength:], n) } else { panic("pointer to unknown type") } } } return out } var bigOne = big.NewInt(1) func parseString(in []byte) (out, rest []byte, ok bool) { if len(in) < 4 { return } length := binary.BigEndian.Uint32(in) if uint32(len(in)) < 4+length { return } out = in[4 : 4+length] rest = in[4+length:] ok = true return } var ( comma = []byte{','} emptyNameList = []string{} ) func parseNameList(in []byte) (out []string, rest []byte, ok bool) { contents, rest, ok := parseString(in) if !ok { return } if len(contents) == 0 { out = emptyNameList return } parts := bytes.Split(contents, comma) out = make([]string, len(parts)) for i, part := range parts { out[i] = string(part) } return } func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) { contents, rest, ok := parseString(in) if !ok { return } out = new(big.Int) if len(contents) > 0 && contents[0]&0x80 == 0x80 { // This is a negative number notBytes := make([]byte, len(contents)) for i := range notBytes { notBytes[i] = ^contents[i] } out.SetBytes(notBytes) out.Add(out, bigOne) out.Neg(out) } else { // Positive number out.SetBytes(contents) } ok = true return } func parseUint32(in []byte) (uint32, []byte, bool) { if len(in) < 4 { return 0, nil, false } return binary.BigEndian.Uint32(in), in[4:], true } func parseUint64(in []byte) (uint64, []byte, bool) { if len(in) < 8 { return 0, nil, false } return binary.BigEndian.Uint64(in), in[8:], true } func nameListLength(namelist []string) int { length := 4 /* uint32 length prefix */ for i, name := range namelist { if i != 0 { length++ /* comma */ } length += len(name) } return length } func intLength(n *big.Int) int { length := 4 /* length bytes */ if n.Sign() < 0 { nMinus1 := new(big.Int).Neg(n) nMinus1.Sub(nMinus1, bigOne) bitLen := nMinus1.BitLen() if bitLen%8 == 0 { // The number will need 0xff padding length++ } length += (bitLen + 7) / 8 } else if n.Sign() == 0 { // A zero is the zero length string } else { bitLen := n.BitLen() if bitLen%8 == 0 { // The number will need 0x00 padding length++ } length += (bitLen + 7) / 8 } return length } func marshalUint32(to []byte, n uint32) []byte { binary.BigEndian.PutUint32(to, n) return to[4:] } func marshalUint64(to []byte, n uint64) []byte { binary.BigEndian.PutUint64(to, n) return to[8:] } func marshalInt(to []byte, n *big.Int) []byte { lengthBytes := to to = to[4:] length := 0 if n.Sign() < 0 { // A negative number has to be converted to two's-complement // form. So we'll subtract 1 and invert. If the // most-significant-bit isn't set then we'll need to pad the // beginning with 0xff in order to keep the number negative. nMinus1 := new(big.Int).Neg(n) nMinus1.Sub(nMinus1, bigOne) bytes := nMinus1.Bytes() for i := range bytes { bytes[i] ^= 0xff } if len(bytes) == 0 || bytes[0]&0x80 == 0 { to[0] = 0xff to = to[1:] length++ } nBytes := copy(to, bytes) to = to[nBytes:] length += nBytes } else if n.Sign() == 0 { // A zero is the zero length string } else { bytes := n.Bytes() if len(bytes) > 0 && bytes[0]&0x80 != 0 { // We'll have to pad this with a 0x00 in order to // stop it looking like a negative number. to[0] = 0 to = to[1:] length++ } nBytes := copy(to, bytes) to = to[nBytes:] length += nBytes } lengthBytes[0] = byte(length >> 24) lengthBytes[1] = byte(length >> 16) lengthBytes[2] = byte(length >> 8) lengthBytes[3] = byte(length) return to } func writeInt(w io.Writer, n *big.Int) { length := intLength(n) buf := make([]byte, length) marshalInt(buf, n) w.Write(buf) } func writeString(w io.Writer, s []byte) { var lengthBytes [4]byte lengthBytes[0] = byte(len(s) >> 24) lengthBytes[1] = byte(len(s) >> 16) lengthBytes[2] = byte(len(s) >> 8) lengthBytes[3] = byte(len(s)) w.Write(lengthBytes[:]) w.Write(s) } func stringLength(n int) int { return 4 + n } func marshalString(to []byte, s []byte) []byte { to[0] = byte(len(s) >> 24) to[1] = byte(len(s) >> 16) to[2] = byte(len(s) >> 8) to[3] = byte(len(s)) to = to[4:] copy(to, s) return to[len(s):] } var bigIntType = reflect.TypeOf((*big.Int)(nil)) // Decode a packet into its corresponding message. func decode(packet []byte) (interface{}, error) { var msg interface{} switch packet[0] { case msgDisconnect: msg = new(disconnectMsg) case msgServiceRequest: msg = new(serviceRequestMsg) case msgServiceAccept: msg = new(serviceAcceptMsg) case msgKexInit: msg = new(kexInitMsg) case msgKexDHInit: msg = new(kexDHInitMsg) case msgKexDHReply: msg = new(kexDHReplyMsg) case msgUserAuthRequest: msg = new(userAuthRequestMsg) case msgUserAuthFailure: msg = new(userAuthFailureMsg) case msgUserAuthPubKeyOk: msg = new(userAuthPubKeyOkMsg) case msgGlobalRequest: msg = new(globalRequestMsg) case msgRequestSuccess: msg = new(globalRequestSuccessMsg) case msgRequestFailure: msg = new(globalRequestFailureMsg) case msgChannelOpen: msg = new(channelOpenMsg) case msgChannelOpenConfirm: msg = new(channelOpenConfirmMsg) case msgChannelOpenFailure: msg = new(channelOpenFailureMsg) case msgChannelWindowAdjust: msg = new(windowAdjustMsg) case msgChannelEOF: msg = new(channelEOFMsg) case msgChannelClose: msg = new(channelCloseMsg) case msgChannelRequest: msg = new(channelRequestMsg) case msgChannelSuccess: msg = new(channelRequestSuccessMsg) case msgChannelFailure: msg = new(channelRequestFailureMsg) default: return nil, UnexpectedMessageError{0, packet[0]} } if err := unmarshal(msg, packet, packet[0]); err != nil { return nil, err } return msg, nil } ����������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/cipher_test.go���������������������������������0000644�0000153�0000161�00000003214�12321736016�025014� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "bytes" "testing" ) // TestCipherReversal tests that each cipher factory produces ciphers that can // encrypt and decrypt some data successfully. func TestCipherReversal(t *testing.T) { testData := []byte("abcdefghijklmnopqrstuvwxyz012345") testKey := []byte("AbCdEfGhIjKlMnOpQrStUvWxYz012345") testIv := []byte("sdflkjhsadflkjhasdflkjhsadfklhsa") cryptBuffer := make([]byte, 32) for name, cipherMode := range cipherModes { encrypter, err := cipherMode.createCipher(testKey, testIv) if err != nil { t.Errorf("failed to create encrypter for %q: %s", name, err) continue } decrypter, err := cipherMode.createCipher(testKey, testIv) if err != nil { t.Errorf("failed to create decrypter for %q: %s", name, err) continue } copy(cryptBuffer, testData) encrypter.XORKeyStream(cryptBuffer, cryptBuffer) if name == "none" { if !bytes.Equal(cryptBuffer, testData) { t.Errorf("encryption made change with 'none' cipher") continue } } else { if bytes.Equal(cryptBuffer, testData) { t.Errorf("encryption made no change with %q", name) continue } } decrypter.XORKeyStream(cryptBuffer, cryptBuffer) if !bytes.Equal(cryptBuffer, testData) { t.Errorf("decrypted bytes not equal to input with %q", name) continue } } } func TestDefaultCiphersExist(t *testing.T) { for _, cipherAlgo := range DefaultCipherOrder { if _, ok := cipherModes[cipherAlgo]; !ok { t.Errorf("default cipher %q is unknown", cipherAlgo) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/client_test.go���������������������������������0000644�0000153�0000161�00000001404�12321736016�025017� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ssh import ( "net" "testing" ) func testClientVersion(t *testing.T, config *ClientConfig, expected string) { clientConn, serverConn := net.Pipe() receivedVersion := make(chan string, 1) go func() { version, err := readVersion(serverConn) if err != nil { receivedVersion <- "" } else { receivedVersion <- string(version) } serverConn.Close() }() Client(clientConn, config) actual := <-receivedVersion if actual != expected { t.Fatalf("got %s; want %s", actual, expected) } } func TestCustomClientVersion(t *testing.T) { version := "Test-Client-Version-0.0" testClientVersion(t, &ClientConfig{ClientVersion: version}, version) } func TestDefaultClientVersion(t *testing.T) { testClientVersion(t, &ClientConfig{}, packageVersion) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/messages_test.go�������������������������������0000644�0000153�0000161�00000012335�12321736016�025355� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "math/big" "math/rand" "reflect" "testing" "testing/quick" ) var intLengthTests = []struct { val, length int }{ {0, 4 + 0}, {1, 4 + 1}, {127, 4 + 1}, {128, 4 + 2}, {-1, 4 + 1}, } func TestIntLength(t *testing.T) { for _, test := range intLengthTests { v := new(big.Int).SetInt64(int64(test.val)) length := intLength(v) if length != test.length { t.Errorf("For %d, got length %d but expected %d", test.val, length, test.length) } } } var messageTypes = []interface{}{ &kexInitMsg{}, &kexDHInitMsg{}, &serviceRequestMsg{}, &serviceAcceptMsg{}, &userAuthRequestMsg{}, &channelOpenMsg{}, &channelOpenConfirmMsg{}, &channelOpenFailureMsg{}, &channelRequestMsg{}, &channelRequestSuccessMsg{}, } func TestMarshalUnmarshal(t *testing.T) { rand := rand.New(rand.NewSource(0)) for i, iface := range messageTypes { ty := reflect.ValueOf(iface).Type() n := 100 if testing.Short() { n = 5 } for j := 0; j < n; j++ { v, ok := quick.Value(ty, rand) if !ok { t.Errorf("#%d: failed to create value", i) break } m1 := v.Elem().Interface() m2 := iface marshaled := marshal(msgIgnore, m1) if err := unmarshal(m2, marshaled, msgIgnore); err != nil { t.Errorf("#%d failed to unmarshal %#v: %s", i, m1, err) break } if !reflect.DeepEqual(v.Interface(), m2) { t.Errorf("#%d\ngot: %#v\nwant:%#v\n%x", i, m2, m1, marshaled) break } } } } func TestUnmarshalEmptyPacket(t *testing.T) { var b []byte var m channelRequestSuccessMsg err := unmarshal(&m, b, msgChannelRequest) want := ParseError{msgChannelRequest} if _, ok := err.(ParseError); !ok { t.Fatalf("got %T, want %T", err, want) } if got := err.(ParseError); want != got { t.Fatal("got %#v, want %#v", got, want) } } func TestUnmarshalUnexpectedPacket(t *testing.T) { type S struct { I uint32 S string B bool } s := S{42, "hello", true} packet := marshal(42, s) roundtrip := S{} err := unmarshal(&roundtrip, packet, 43) if err == nil { t.Fatal("expected error, not nil") } want := UnexpectedMessageError{43, 42} if got, ok := err.(UnexpectedMessageError); !ok || want != got { t.Fatal("expected %q, got %q", want, got) } } func TestBareMarshalUnmarshal(t *testing.T) { type S struct { I uint32 S string B bool } s := S{42, "hello", true} packet := marshal(0, s) roundtrip := S{} unmarshal(&roundtrip, packet, 0) if !reflect.DeepEqual(s, roundtrip) { t.Errorf("got %#v, want %#v", roundtrip, s) } } func TestBareMarshal(t *testing.T) { type S2 struct { I uint32 } s := S2{42} packet := marshal(0, s) i, rest, ok := parseUint32(packet) if len(rest) > 0 || !ok { t.Errorf("parseInt(%q): parse error", packet) } if i != s.I { t.Errorf("got %d, want %d", i, s.I) } } func randomBytes(out []byte, rand *rand.Rand) { for i := 0; i < len(out); i++ { out[i] = byte(rand.Int31()) } } func randomNameList(rand *rand.Rand) []string { ret := make([]string, rand.Int31()&15) for i := range ret { s := make([]byte, 1+(rand.Int31()&15)) for j := range s { s[j] = 'a' + uint8(rand.Int31()&15) } ret[i] = string(s) } return ret } func randomInt(rand *rand.Rand) *big.Int { return new(big.Int).SetInt64(int64(int32(rand.Uint32()))) } func (*kexInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { ki := &kexInitMsg{} randomBytes(ki.Cookie[:], rand) ki.KexAlgos = randomNameList(rand) ki.ServerHostKeyAlgos = randomNameList(rand) ki.CiphersClientServer = randomNameList(rand) ki.CiphersServerClient = randomNameList(rand) ki.MACsClientServer = randomNameList(rand) ki.MACsServerClient = randomNameList(rand) ki.CompressionClientServer = randomNameList(rand) ki.CompressionServerClient = randomNameList(rand) ki.LanguagesClientServer = randomNameList(rand) ki.LanguagesServerClient = randomNameList(rand) if rand.Int31()&1 == 1 { ki.FirstKexFollows = true } return reflect.ValueOf(ki) } func (*kexDHInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { dhi := &kexDHInitMsg{} dhi.X = randomInt(rand) return reflect.ValueOf(dhi) } // TODO(dfc) maybe this can be removed in the future if testing/quick can handle // derived basic types. func (RejectionReason) Generate(rand *rand.Rand, size int) reflect.Value { m := RejectionReason(Prohibited) return reflect.ValueOf(m) } var ( _kexInitMsg = new(kexInitMsg).Generate(rand.New(rand.NewSource(0)), 10).Elem().Interface() _kexDHInitMsg = new(kexDHInitMsg).Generate(rand.New(rand.NewSource(0)), 10).Elem().Interface() _kexInit = marshal(msgKexInit, _kexInitMsg) _kexDHInit = marshal(msgKexDHInit, _kexDHInitMsg) ) func BenchmarkMarshalKexInitMsg(b *testing.B) { for i := 0; i < b.N; i++ { marshal(msgKexInit, _kexInitMsg) } } func BenchmarkUnmarshalKexInitMsg(b *testing.B) { m := new(kexInitMsg) for i := 0; i < b.N; i++ { unmarshal(m, _kexInit, msgKexInit) } } func BenchmarkMarshalKexDHInitMsg(b *testing.B) { for i := 0; i < b.N; i++ { marshal(msgKexDHInit, _kexDHInitMsg) } } func BenchmarkUnmarshalKexDHInitMsg(b *testing.B) { m := new(kexDHInitMsg) for i := 0; i < b.N; i++ { unmarshal(m, _kexDHInit, msgKexDHInit) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/terminal/��������������������������������������0000755�0000153�0000161�00000000000�12321736016�023767� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/terminal/terminal.go���������������������������0000644�0000153�0000161�00000035625�12321735647�026155� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 terminal import ( "io" "sync" "unicode/utf8" ) // EscapeCodes contains escape sequences that can be written to the terminal in // order to achieve different styles of text. type EscapeCodes struct { // Foreground colors Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte // Reset all attributes Reset []byte } var vt100EscapeCodes = EscapeCodes{ Black: []byte{keyEscape, '[', '3', '0', 'm'}, Red: []byte{keyEscape, '[', '3', '1', 'm'}, Green: []byte{keyEscape, '[', '3', '2', 'm'}, Yellow: []byte{keyEscape, '[', '3', '3', 'm'}, Blue: []byte{keyEscape, '[', '3', '4', 'm'}, Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, White: []byte{keyEscape, '[', '3', '7', 'm'}, Reset: []byte{keyEscape, '[', '0', 'm'}, } // Terminal contains the state for running a VT100 terminal that is capable of // reading lines of input. type Terminal struct { // AutoCompleteCallback, if non-null, is called for each keypress with // the full input line and the current position of the cursor (in // bytes, as an index into |line|). If it returns ok=false, the key // press is processed normally. Otherwise it returns a replacement line // and the new cursor position. AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool) // Escape contains a pointer to the escape codes for this terminal. // It's always a valid pointer, although the escape codes themselves // may be empty if the terminal doesn't support them. Escape *EscapeCodes // lock protects the terminal and the state in this object from // concurrent processing of a key press and a Write() call. lock sync.Mutex c io.ReadWriter prompt string // line is the current line being entered. line []rune // pos is the logical position of the cursor in line pos int // echo is true if local echo is enabled echo bool // cursorX contains the current X value of the cursor where the left // edge is 0. cursorY contains the row number where the first row of // the current line is 0. cursorX, cursorY int // maxLine is the greatest value of cursorY so far. maxLine int termWidth, termHeight int // outBuf contains the terminal data to be sent. outBuf []byte // remainder contains the remainder of any partial key sequences after // a read. It aliases into inBuf. remainder []byte inBuf [256]byte // history contains previously entered commands so that they can be // accessed with the up and down keys. history stRingBuffer // historyIndex stores the currently accessed history entry, where zero // means the immediately previous entry. historyIndex int // When navigating up and down the history it's possible to return to // the incomplete, initial line. That value is stored in // historyPending. historyPending string } // NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is // a local terminal, that terminal must first have been put into raw mode. // prompt is a string that is written at the start of each input line (i.e. // "> "). func NewTerminal(c io.ReadWriter, prompt string) *Terminal { return &Terminal{ Escape: &vt100EscapeCodes, c: c, prompt: prompt, termWidth: 80, termHeight: 24, echo: true, historyIndex: -1, } } const ( keyCtrlD = 4 keyEnter = '\r' keyEscape = 27 keyBackspace = 127 keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota keyUp keyDown keyLeft keyRight keyAltLeft keyAltRight keyHome keyEnd keyDeleteWord keyDeleteLine ) // bytesToKey tries to parse a key sequence from b. If successful, it returns // the key and the remainder of the input. Otherwise it returns utf8.RuneError. func bytesToKey(b []byte) (rune, []byte) { if len(b) == 0 { return utf8.RuneError, nil } switch b[0] { case 1: // ^A return keyHome, b[1:] case 5: // ^E return keyEnd, b[1:] case 8: // ^H return keyBackspace, b[1:] case 11: // ^K return keyDeleteLine, b[1:] case 23: // ^W return keyDeleteWord, b[1:] } if b[0] != keyEscape { if !utf8.FullRune(b) { return utf8.RuneError, b } r, l := utf8.DecodeRune(b) return r, b[l:] } if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { switch b[2] { case 'A': return keyUp, b[3:] case 'B': return keyDown, b[3:] case 'C': return keyRight, b[3:] case 'D': return keyLeft, b[3:] } } if len(b) >= 3 && b[0] == keyEscape && b[1] == 'O' { switch b[2] { case 'H': return keyHome, b[3:] case 'F': return keyEnd, b[3:] } } if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { switch b[5] { case 'C': return keyAltRight, b[6:] case 'D': return keyAltLeft, b[6:] } } // If we get here then we have a key that we don't recognise, or a // partial sequence. It's not clear how one should find the end of a // sequence without knowing them all, but it seems that [a-zA-Z] only // appears at the end of a sequence. for i, c := range b[0:] { if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' { return keyUnknown, b[i+1:] } } return utf8.RuneError, b } // queue appends data to the end of t.outBuf func (t *Terminal) queue(data []rune) { t.outBuf = append(t.outBuf, []byte(string(data))...) } var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} var space = []rune{' '} func isPrintable(key rune) bool { isInSurrogateArea := key >= 0xd800 && key <= 0xdbff return key >= 32 && !isInSurrogateArea } // moveCursorToPos appends data to t.outBuf which will move the cursor to the // given, logical position in the text. func (t *Terminal) moveCursorToPos(pos int) { if !t.echo { return } x := len(t.prompt) + pos y := x / t.termWidth x = x % t.termWidth up := 0 if y < t.cursorY { up = t.cursorY - y } down := 0 if y > t.cursorY { down = y - t.cursorY } left := 0 if x < t.cursorX { left = t.cursorX - x } right := 0 if x > t.cursorX { right = x - t.cursorX } t.cursorX = x t.cursorY = y t.move(up, down, left, right) } func (t *Terminal) move(up, down, left, right int) { movement := make([]rune, 3*(up+down+left+right)) m := movement for i := 0; i < up; i++ { m[0] = keyEscape m[1] = '[' m[2] = 'A' m = m[3:] } for i := 0; i < down; i++ { m[0] = keyEscape m[1] = '[' m[2] = 'B' m = m[3:] } for i := 0; i < left; i++ { m[0] = keyEscape m[1] = '[' m[2] = 'D' m = m[3:] } for i := 0; i < right; i++ { m[0] = keyEscape m[1] = '[' m[2] = 'C' m = m[3:] } t.queue(movement) } func (t *Terminal) clearLineToRight() { op := []rune{keyEscape, '[', 'K'} t.queue(op) } const maxLineLength = 4096 func (t *Terminal) setLine(newLine []rune, newPos int) { if t.echo { t.moveCursorToPos(0) t.writeLine(newLine) for i := len(newLine); i < len(t.line); i++ { t.writeLine(space) } t.moveCursorToPos(newPos) } t.line = newLine t.pos = newPos } func (t *Terminal) eraseNPreviousChars(n int) { if n == 0 { return } if t.pos < n { n = t.pos } t.pos -= n t.moveCursorToPos(t.pos) copy(t.line[t.pos:], t.line[n+t.pos:]) t.line = t.line[:len(t.line)-n] if t.echo { t.writeLine(t.line[t.pos:]) for i := 0; i < n; i++ { t.queue(space) } t.cursorX += n t.moveCursorToPos(t.pos) } } // countToLeftWord returns then number of characters from the cursor to the // start of the previous word. func (t *Terminal) countToLeftWord() int { if t.pos == 0 { return 0 } pos := t.pos - 1 for pos > 0 { if t.line[pos] != ' ' { break } pos-- } for pos > 0 { if t.line[pos] == ' ' { pos++ break } pos-- } return t.pos - pos } // countToRightWord returns then number of characters from the cursor to the // start of the next word. func (t *Terminal) countToRightWord() int { pos := t.pos for pos < len(t.line) { if t.line[pos] == ' ' { break } pos++ } for pos < len(t.line) { if t.line[pos] != ' ' { break } pos++ } return pos - t.pos } // handleKey processes the given key and, optionally, returns a line of text // that the user has entered. func (t *Terminal) handleKey(key rune) (line string, ok bool) { switch key { case keyBackspace: if t.pos == 0 { return } t.eraseNPreviousChars(1) case keyAltLeft: // move left by a word. t.pos -= t.countToLeftWord() t.moveCursorToPos(t.pos) case keyAltRight: // move right by a word. t.pos += t.countToRightWord() t.moveCursorToPos(t.pos) case keyLeft: if t.pos == 0 { return } t.pos-- t.moveCursorToPos(t.pos) case keyRight: if t.pos == len(t.line) { return } t.pos++ t.moveCursorToPos(t.pos) case keyHome: if t.pos == 0 { return } t.pos = 0 t.moveCursorToPos(t.pos) case keyEnd: if t.pos == len(t.line) { return } t.pos = len(t.line) t.moveCursorToPos(t.pos) case keyUp: entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1) if !ok { return "", false } if t.historyIndex == -1 { t.historyPending = string(t.line) } t.historyIndex++ runes := []rune(entry) t.setLine(runes, len(runes)) case keyDown: switch t.historyIndex { case -1: return case 0: runes := []rune(t.historyPending) t.setLine(runes, len(runes)) t.historyIndex-- default: entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1) if ok { t.historyIndex-- runes := []rune(entry) t.setLine(runes, len(runes)) } } case keyEnter: t.moveCursorToPos(len(t.line)) t.queue([]rune("\r\n")) line = string(t.line) ok = true t.line = t.line[:0] t.pos = 0 t.cursorX = 0 t.cursorY = 0 t.maxLine = 0 case keyDeleteWord: // Delete zero or more spaces and then one or more characters. t.eraseNPreviousChars(t.countToLeftWord()) case keyDeleteLine: // Delete everything from the current cursor position to the // end of line. for i := t.pos; i < len(t.line); i++ { t.queue(space) t.cursorX++ } t.line = t.line[:t.pos] t.moveCursorToPos(t.pos) default: if t.AutoCompleteCallback != nil { prefix := string(t.line[:t.pos]) suffix := string(t.line[t.pos:]) t.lock.Unlock() newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key) t.lock.Lock() if completeOk { t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos])) return } } if !isPrintable(key) { return } if len(t.line) == maxLineLength { return } if len(t.line) == cap(t.line) { newLine := make([]rune, len(t.line), 2*(1+len(t.line))) copy(newLine, t.line) t.line = newLine } t.line = t.line[:len(t.line)+1] copy(t.line[t.pos+1:], t.line[t.pos:]) t.line[t.pos] = key if t.echo { t.writeLine(t.line[t.pos:]) } t.pos++ t.moveCursorToPos(t.pos) } return } func (t *Terminal) writeLine(line []rune) { for len(line) != 0 { remainingOnLine := t.termWidth - t.cursorX todo := len(line) if todo > remainingOnLine { todo = remainingOnLine } t.queue(line[:todo]) t.cursorX += todo line = line[todo:] if t.cursorX == t.termWidth { t.cursorX = 0 t.cursorY++ if t.cursorY > t.maxLine { t.maxLine = t.cursorY } } } } func (t *Terminal) Write(buf []byte) (n int, err error) { t.lock.Lock() defer t.lock.Unlock() if t.cursorX == 0 && t.cursorY == 0 { // This is the easy case: there's nothing on the screen that we // have to move out of the way. return t.c.Write(buf) } // We have a prompt and possibly user input on the screen. We // have to clear it first. t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */) t.cursorX = 0 t.clearLineToRight() for t.cursorY > 0 { t.move(1 /* up */, 0, 0, 0) t.cursorY-- t.clearLineToRight() } if _, err = t.c.Write(t.outBuf); err != nil { return } t.outBuf = t.outBuf[:0] if n, err = t.c.Write(buf); err != nil { return } t.queue([]rune(t.prompt)) chars := len(t.prompt) if t.echo { t.queue(t.line) chars += len(t.line) } t.cursorX = chars % t.termWidth t.cursorY = chars / t.termWidth t.moveCursorToPos(t.pos) if _, err = t.c.Write(t.outBuf); err != nil { return } t.outBuf = t.outBuf[:0] return } // ReadPassword temporarily changes the prompt and reads a password, without // echo, from the terminal. func (t *Terminal) ReadPassword(prompt string) (line string, err error) { t.lock.Lock() defer t.lock.Unlock() oldPrompt := t.prompt t.prompt = prompt t.echo = false line, err = t.readLine() t.prompt = oldPrompt t.echo = true return } // ReadLine returns a line of input from the terminal. func (t *Terminal) ReadLine() (line string, err error) { t.lock.Lock() defer t.lock.Unlock() return t.readLine() } func (t *Terminal) readLine() (line string, err error) { // t.lock must be held at this point if t.cursorX == 0 && t.cursorY == 0 { t.writeLine([]rune(t.prompt)) t.c.Write(t.outBuf) t.outBuf = t.outBuf[:0] } for { rest := t.remainder lineOk := false for !lineOk { var key rune key, rest = bytesToKey(rest) if key == utf8.RuneError { break } if key == keyCtrlD { return "", io.EOF } line, lineOk = t.handleKey(key) } if len(rest) > 0 { n := copy(t.inBuf[:], rest) t.remainder = t.inBuf[:n] } else { t.remainder = nil } t.c.Write(t.outBuf) t.outBuf = t.outBuf[:0] if lineOk { if t.echo { t.historyIndex = -1 t.history.Add(line) } return } // t.remainder is a slice at the beginning of t.inBuf // containing a partial key sequence readBuf := t.inBuf[len(t.remainder):] var n int t.lock.Unlock() n, err = t.c.Read(readBuf) t.lock.Lock() if err != nil { return } t.remainder = t.inBuf[:n+len(t.remainder)] } panic("unreachable") // for Go 1.0. } // SetPrompt sets the prompt to be used when reading subsequent lines. func (t *Terminal) SetPrompt(prompt string) { t.lock.Lock() defer t.lock.Unlock() t.prompt = prompt } func (t *Terminal) SetSize(width, height int) { t.lock.Lock() defer t.lock.Unlock() t.termWidth, t.termHeight = width, height } // stRingBuffer is a ring buffer of strings. type stRingBuffer struct { // entries contains max elements. entries []string max int // head contains the index of the element most recently added to the ring. head int // size contains the number of elements in the ring. size int } func (s *stRingBuffer) Add(a string) { if s.entries == nil { const defaultNumEntries = 100 s.entries = make([]string, defaultNumEntries) s.max = defaultNumEntries } s.head = (s.head + 1) % s.max s.entries[s.head] = a if s.size < s.max { s.size++ } } // NthPreviousEntry returns the value passed to the nth previous call to Add. // If n is zero then the immediately prior value is returned, if one, then the // next most recent, and so on. If such an element doesn't exist then ok is // false. func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { if n >= s.size { return "", false } index := s.head - n if index < 0 { index += s.max } return s.entries[index], true } �����������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/terminal/util.go�������������������������������0000644�0000153�0000161�00000007553�12321736016�025305� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // +build linux,!appengine darwin // Package terminal provides support functions for dealing with terminals, as // commonly found on UNIX systems. // // Putting a terminal into raw mode is the most common requirement: // // oldState, err := terminal.MakeRaw(0) // if err != nil { // panic(err) // } // defer terminal.Restore(0, oldState) package terminal import ( "io" "syscall" "unsafe" ) // State contains the state of a terminal. type State struct { termios syscall.Termios } // IsTerminal returns true if the given file descriptor is a terminal. func IsTerminal(fd int) bool { var termios syscall.Termios _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) return err == 0 } // MakeRaw put the terminal connected to the given file descriptor into raw // mode and returns the previous state of the terminal so that it can be // restored. func MakeRaw(fd int) (*State, error) { var oldState State if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { return nil, err } newState := oldState.termios newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { return nil, err } return &oldState, nil } // GetState returns the current state of a terminal which may be useful to // restore the terminal after a signal. func GetState(fd int) (*State, error) { var oldState State if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { return nil, err } return &oldState, nil } // Restore restores the terminal connected to the given file descriptor to a // previous state. func Restore(fd int, state *State) error { _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) return err } // GetSize returns the dimensions of the given terminal. func GetSize(fd int) (width, height int, err error) { var dimensions [4]uint16 if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { return -1, -1, err } return int(dimensions[1]), int(dimensions[0]), nil } // ReadPassword reads a line of input from a terminal without local echo. This // is commonly used for inputting passwords and other sensitive data. The slice // returned does not include the \n. func ReadPassword(fd int) ([]byte, error) { var oldState syscall.Termios if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 { return nil, err } newState := oldState newState.Lflag &^= syscall.ECHO newState.Lflag |= syscall.ICANON | syscall.ISIG newState.Iflag |= syscall.ICRNL if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { return nil, err } defer func() { syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) }() var buf [16]byte var ret []byte for { n, err := syscall.Read(fd, buf[:]) if err != nil { return nil, err } if n == 0 { if len(ret) == 0 { return nil, io.EOF } break } if buf[n-1] == '\n' { n-- } ret = append(ret, buf[:n]...) if n < len(buf) { break } } return ret, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/terminal/util_bsd.go���������������������������0000644�0000153�0000161�00000000453�12321736016�026125� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 terminal import "syscall" const ioctlReadTermios = syscall.TIOCGETA const ioctlWriteTermios = syscall.TIOCSETA ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/terminal/util_linux.go�������������������������0000644�0000153�0000161�00000000446�12321735647�026527� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 linux package terminal import "syscall" const ioctlReadTermios = syscall.TCGETS const ioctlWriteTermios = syscall.TCSETS ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/terminal/terminal_test.go����������������������0000644�0000153�0000161�00000007537�12321735647�027215� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 terminal import ( "io" "testing" ) type MockTerminal struct { toSend []byte bytesPerRead int received []byte } func (c *MockTerminal) Read(data []byte) (n int, err error) { n = len(data) if n == 0 { return } if n > len(c.toSend) { n = len(c.toSend) } if n == 0 { return 0, io.EOF } if c.bytesPerRead > 0 && n > c.bytesPerRead { n = c.bytesPerRead } copy(data, c.toSend[:n]) c.toSend = c.toSend[n:] return } func (c *MockTerminal) Write(data []byte) (n int, err error) { c.received = append(c.received, data...) return len(data), nil } func TestClose(t *testing.T) { c := &MockTerminal{} ss := NewTerminal(c, "> ") line, err := ss.ReadLine() if line != "" { t.Errorf("Expected empty line but got: %s", line) } if err != io.EOF { t.Errorf("Error should have been EOF but got: %s", err) } } var keyPressTests = []struct { in string line string err error throwAwayLines int }{ { err: io.EOF, }, { in: "\r", line: "", }, { in: "foo\r", line: "foo", }, { in: "a\x1b[Cb\r", // right line: "ab", }, { in: "a\x1b[Db\r", // left line: "ba", }, { in: "a\177b\r", // backspace line: "b", }, { in: "\x1b[A\r", // up }, { in: "\x1b[B\r", // down }, { in: "line\x1b[A\x1b[B\r", // up then down line: "line", }, { in: "line1\rline2\x1b[A\r", // recall previous line. line: "line1", throwAwayLines: 1, }, { // recall two previous lines and append. in: "line1\rline2\rline3\x1b[A\x1b[Axxx\r", line: "line1xxx", throwAwayLines: 2, }, { // Ctrl-A to move to beginning of line followed by ^K to kill // line. in: "a b \001\013\r", line: "", }, { // Ctrl-A to move to beginning of line, Ctrl-E to move to end, // finally ^K to kill nothing. in: "a b \001\005\013\r", line: "a b ", }, { in: "\027\r", line: "", }, { in: "a\027\r", line: "", }, { in: "a \027\r", line: "", }, { in: "a b\027\r", line: "a ", }, { in: "a b \027\r", line: "a ", }, { in: "one two thr\x1b[D\027\r", line: "one two r", }, { in: "\013\r", line: "", }, { in: "a\013\r", line: "a", }, { in: "ab\x1b[D\013\r", line: "a", }, { in: "Ξεσκεπάζω\r", line: "Ξεσκεπάζω", }, { in: "£\r\x1b[A\177\r", // non-ASCII char, enter, up, backspace. line: "", throwAwayLines: 1, }, { in: "£\r££\x1b[A\x1b[B\177\r", // non-ASCII char, enter, 2x non-ASCII, up, down, backspace, enter. line: "£", throwAwayLines: 1, }, } func TestKeyPresses(t *testing.T) { for i, test := range keyPressTests { for j := 1; j < len(test.in); j++ { c := &MockTerminal{ toSend: []byte(test.in), bytesPerRead: j, } ss := NewTerminal(c, "> ") for k := 0; k < test.throwAwayLines; k++ { _, err := ss.ReadLine() if err != nil { t.Errorf("Throwaway line %d from test %d resulted in error: %s", k, i, err) } } line, err := ss.ReadLine() if line != test.line { t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line) break } if err != test.err { t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err) break } } } } func TestPasswordNotSaved(t *testing.T) { c := &MockTerminal{ toSend: []byte("password\r\x1b[A\r"), bytesPerRead: 1, } ss := NewTerminal(c, "> ") pw, _ := ss.ReadPassword("> ") if pw != "password" { t.Fatalf("failed to read password, got %s", pw) } line, _ := ss.ReadLine() if len(line) > 0 { t.Fatalf("password was saved in history") } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/example_test.go��������������������������������0000644�0000153�0000161�00000011754�12321736016�025205� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "bytes" "fmt" "io/ioutil" "log" "net/http" "code.google.com/p/go.crypto/ssh/terminal" ) func ExampleListen() { // An SSH server is represented by a ServerConfig, which holds // certificate details and handles authentication of ServerConns. config := &ServerConfig{ PasswordCallback: func(conn *ServerConn, user, pass string) bool { return user == "testuser" && pass == "tiger" }, } privateBytes, err := ioutil.ReadFile("id_rsa") if err != nil { panic("Failed to load private key") } private, err := ParsePrivateKey(privateBytes) if err != nil { panic("Failed to parse private key") } config.AddHostKey(private) // Once a ServerConfig has been configured, connections can be // accepted. listener, err := Listen("tcp", "0.0.0.0:2022", config) if err != nil { panic("failed to listen for connection") } sConn, err := listener.Accept() if err != nil { panic("failed to accept incoming connection") } if err := sConn.Handshake(); err != nil { panic("failed to handshake") } // A ServerConn multiplexes several channels, which must // themselves be Accepted. for { // Accept reads from the connection, demultiplexes packets // to their corresponding channels and returns when a new // channel request is seen. Some goroutine must always be // calling Accept; otherwise no messages will be forwarded // to the channels. channel, err := sConn.Accept() if err != nil { panic("error from Accept") } // Channels have a type, depending on the application level // protocol intended. In the case of a shell, the type is // "session" and ServerShell may be used to present a simple // terminal interface. if channel.ChannelType() != "session" { channel.Reject(UnknownChannelType, "unknown channel type") continue } channel.Accept() term := terminal.NewTerminal(channel, "> ") serverTerm := &ServerTerminal{ Term: term, Channel: channel, } go func() { defer channel.Close() for { line, err := serverTerm.ReadLine() if err != nil { break } fmt.Println(line) } }() } } func ExampleDial() { // An SSH client is represented with a ClientConn. Currently only // the "password" authentication method is supported. // // To authenticate with the remote server you must pass at least one // implementation of ClientAuth via the Auth field in ClientConfig. config := &ClientConfig{ User: "username", Auth: []ClientAuth{ // ClientAuthPassword wraps a ClientPassword implementation // in a type that implements ClientAuth. ClientAuthPassword(password("yourpassword")), }, } client, err := Dial("tcp", "yourserver.com:22", config) if err != nil { panic("Failed to dial: " + err.Error()) } // Each ClientConn can support multiple interactive sessions, // represented by a Session. session, err := client.NewSession() if err != nil { panic("Failed to create session: " + err.Error()) } defer session.Close() // Once a Session is created, you can execute a single command on // the remote side using the Run method. var b bytes.Buffer session.Stdout = &b if err := session.Run("/usr/bin/whoami"); err != nil { panic("Failed to run: " + err.Error()) } fmt.Println(b.String()) } func ExampleClientConn_Listen() { config := &ClientConfig{ User: "username", Auth: []ClientAuth{ ClientAuthPassword(password("password")), }, } // Dial your ssh server. conn, err := Dial("tcp", "localhost:22", config) if err != nil { log.Fatalf("unable to connect: %s", err) } defer conn.Close() // Request the remote side to open port 8080 on all interfaces. l, err := conn.Listen("tcp", "0.0.0.0:8080") if err != nil { log.Fatalf("unable to register tcp forward: %v", err) } defer l.Close() // Serve HTTP with your SSH server acting as a reverse proxy. http.Serve(l, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { fmt.Fprintf(resp, "Hello world!\n") })) } func ExampleSession_RequestPty() { // Create client config config := &ClientConfig{ User: "username", Auth: []ClientAuth{ ClientAuthPassword(password("password")), }, } // Connect to ssh server conn, err := Dial("tcp", "localhost:22", config) if err != nil { log.Fatalf("unable to connect: %s", err) } defer conn.Close() // Create a session session, err := conn.NewSession() if err != nil { log.Fatalf("unable to create session: %s", err) } defer session.Close() // Set up terminal modes modes := TerminalModes{ ECHO: 0, // disable echoing TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud } // Request pseudo terminal if err := session.RequestPty("xterm", 80, 40, modes); err != nil { log.Fatalf("request for pseudo terminal failed: %s", err) } // Start remote shell if err := session.Shell(); err != nil { log.Fatalf("failed to start shell: %s", err) } } ��������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/kex.go�����������������������������������������0000644�0000153�0000161�00000024015�12321736016�023274� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "errors" "io" "math/big" ) const ( kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" kexAlgoECDH256 = "ecdh-sha2-nistp256" kexAlgoECDH384 = "ecdh-sha2-nistp384" kexAlgoECDH521 = "ecdh-sha2-nistp521" ) // kexResult captures the outcome of a key exchange. type kexResult struct { // Session hash. See also RFC 4253, section 8. H []byte // Shared secret. See also RFC 4253, section 8. K []byte // Host key as hashed into H HostKey []byte // Signature of H Signature []byte // A cryptographic hash function that matches the security // level of the key exchange algorithm. It is used for // calculating H, and for deriving keys from H and K. Hash crypto.Hash // The session ID, which is the first H computed. This is used // to signal data inside transport. SessionID []byte } // handshakeMagics contains data that is always included in the // session hash. type handshakeMagics struct { clientVersion, serverVersion []byte clientKexInit, serverKexInit []byte } func (m *handshakeMagics) write(w io.Writer) { writeString(w, m.clientVersion) writeString(w, m.serverVersion) writeString(w, m.clientKexInit) writeString(w, m.serverKexInit) } // kexAlgorithm abstracts different key exchange algorithms. type kexAlgorithm interface { // Server runs server-side key agreement, signing the result // with a hostkey. Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error) // Client runs the client-side key agreement. Caller is // responsible for verifying the host key signature. Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) } // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. type dhGroup struct { g, p *big.Int } func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { if theirPublic.Sign() <= 0 || theirPublic.Cmp(group.p) >= 0 { return nil, errors.New("ssh: DH parameter out of bounds") } return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil } func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { hashFunc := crypto.SHA1 x, err := rand.Int(randSource, group.p) if err != nil { return nil, err } X := new(big.Int).Exp(group.g, x, group.p) kexDHInit := kexDHInitMsg{ X: X, } if err := c.writePacket(marshal(msgKexDHInit, kexDHInit)); err != nil { return nil, err } packet, err := c.readPacket() if err != nil { return nil, err } var kexDHReply kexDHReplyMsg if err = unmarshal(&kexDHReply, packet, msgKexDHReply); err != nil { return nil, err } kInt, err := group.diffieHellman(kexDHReply.Y, x) if err != nil { return nil, err } h := hashFunc.New() magics.write(h) writeString(h, kexDHReply.HostKey) writeInt(h, X) writeInt(h, kexDHReply.Y) K := make([]byte, intLength(kInt)) marshalInt(K, kInt) h.Write(K) return &kexResult{ H: h.Sum(nil), K: K, HostKey: kexDHReply.HostKey, Signature: kexDHReply.Signature, Hash: crypto.SHA1, }, nil } func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { hashFunc := crypto.SHA1 packet, err := c.readPacket() if err != nil { return } var kexDHInit kexDHInitMsg if err = unmarshal(&kexDHInit, packet, msgKexDHInit); err != nil { return } y, err := rand.Int(randSource, group.p) if err != nil { return } Y := new(big.Int).Exp(group.g, y, group.p) kInt, err := group.diffieHellman(kexDHInit.X, y) if err != nil { return nil, err } hostKeyBytes := MarshalPublicKey(priv.PublicKey()) h := hashFunc.New() magics.write(h) writeString(h, hostKeyBytes) writeInt(h, kexDHInit.X) writeInt(h, Y) K := make([]byte, intLength(kInt)) marshalInt(K, kInt) h.Write(K) H := h.Sum(nil) // H is already a hash, but the hostkey signing will apply its // own key-specific hash algorithm. sig, err := signAndMarshal(priv, randSource, H) if err != nil { return nil, err } kexDHReply := kexDHReplyMsg{ HostKey: hostKeyBytes, Y: Y, Signature: sig, } packet = marshal(msgKexDHReply, kexDHReply) err = c.writePacket(packet) return &kexResult{ H: H, K: K, HostKey: hostKeyBytes, Signature: sig, Hash: crypto.SHA1, }, nil } // ecdh performs Elliptic Curve Diffie-Hellman key exchange as // described in RFC 5656, section 4. type ecdh struct { curve elliptic.Curve } func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { ephKey, err := ecdsa.GenerateKey(kex.curve, rand) if err != nil { return nil, err } kexInit := kexECDHInitMsg{ ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y), } serialized := marshal(msgKexECDHInit, kexInit) if err := c.writePacket(serialized); err != nil { return nil, err } packet, err := c.readPacket() if err != nil { return nil, err } var reply kexECDHReplyMsg if err = unmarshal(&reply, packet, msgKexECDHReply); err != nil { return nil, err } x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey) if err != nil { return nil, err } // generate shared secret secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes()) h := ecHash(kex.curve).New() magics.write(h) writeString(h, reply.HostKey) writeString(h, kexInit.ClientPubKey) writeString(h, reply.EphemeralPubKey) K := make([]byte, intLength(secret)) marshalInt(K, secret) h.Write(K) return &kexResult{ H: h.Sum(nil), K: K, HostKey: reply.HostKey, Signature: reply.Signature, Hash: ecHash(kex.curve), }, nil } // unmarshalECKey parses and checks an EC key. func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) { x, y = elliptic.Unmarshal(curve, pubkey) if x == nil { return nil, nil, errors.New("ssh: elliptic.Unmarshal failure") } if !validateECPublicKey(curve, x, y) { return nil, nil, errors.New("ssh: public key not on curve") } return x, y, nil } // validateECPublicKey checks that the point is a valid public key for // the given curve. See [SEC1], 3.2.2 func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool { if x.Sign() == 0 && y.Sign() == 0 { return false } if x.Cmp(curve.Params().P) >= 0 { return false } if y.Cmp(curve.Params().P) >= 0 { return false } if !curve.IsOnCurve(x, y) { return false } // We don't check if N * PubKey == 0, since // // - the NIST curves have cofactor = 1, so this is implicit. // (We don't foresee an implementation that supports non NIST // curves) // // - for ephemeral keys, we don't need to worry about small // subgroup attacks. return true } func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { packet, err := c.readPacket() if err != nil { return nil, err } var kexECDHInit kexECDHInitMsg if err = unmarshal(&kexECDHInit, packet, msgKexECDHInit); err != nil { return nil, err } clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey) if err != nil { return nil, err } // We could cache this key across multiple users/multiple // connection attempts, but the benefit is small. OpenSSH // generates a new key for each incoming connection. ephKey, err := ecdsa.GenerateKey(kex.curve, rand) if err != nil { return nil, err } hostKeyBytes := MarshalPublicKey(priv.PublicKey()) serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y) // generate shared secret secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes()) h := ecHash(kex.curve).New() magics.write(h) writeString(h, hostKeyBytes) writeString(h, kexECDHInit.ClientPubKey) writeString(h, serializedEphKey) K := make([]byte, intLength(secret)) marshalInt(K, secret) h.Write(K) H := h.Sum(nil) // H is already a hash, but the hostkey signing will apply its // own key-specific hash algorithm. sig, err := signAndMarshal(priv, rand, H) if err != nil { return nil, err } reply := kexECDHReplyMsg{ EphemeralPubKey: serializedEphKey, HostKey: hostKeyBytes, Signature: sig, } serialized := marshal(msgKexECDHReply, reply) if err := c.writePacket(serialized); err != nil { return nil, err } return &kexResult{ H: H, K: K, HostKey: reply.HostKey, Signature: sig, Hash: ecHash(kex.curve), }, nil } var kexAlgoMap = map[string]kexAlgorithm{} func init() { // This is the group called diffie-hellman-group1-sha1 in RFC // 4253 and Oakley Group 2 in RFC 2409. p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ g: new(big.Int).SetInt64(2), p: p, } // This is the group called diffie-hellman-group14-sha1 in RFC // 4253 and Oakley Group 14 in RFC 3526. p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ g: new(big.Int).SetInt64(2), p: p, } kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()} kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()} } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/mac.go�����������������������������������������0000644�0000153�0000161�00000002515�12321736016�023246� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh // Message authentication support import ( "crypto/hmac" "crypto/sha1" "hash" ) type macMode struct { keySize int new func(key []byte) hash.Hash } // truncatingMAC wraps around a hash.Hash and truncates the output digest to // a given size. type truncatingMAC struct { length int hmac hash.Hash } func (t truncatingMAC) Write(data []byte) (int, error) { return t.hmac.Write(data) } func (t truncatingMAC) Sum(in []byte) []byte { out := t.hmac.Sum(in) return out[:len(in)+t.length] } func (t truncatingMAC) Reset() { t.hmac.Reset() } func (t truncatingMAC) Size() int { return t.length } func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } // Specifies a default set of MAC algorithms and a preference order. // This is based on RFC 4253, section 6.4, with the removal of the // hmac-md5 variants as they have reached the end of their useful life. var DefaultMACOrder = []string{"hmac-sha1", "hmac-sha1-96"} var macModes = map[string]*macMode{ "hmac-sha1": {20, func(key []byte) hash.Hash { return hmac.New(sha1.New, key) }}, "hmac-sha1-96": {20, func(key []byte) hash.Hash { return truncatingMAC{12, hmac.New(sha1.New, key)} }}, } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/kex_test.go������������������������������������0000644�0000153�0000161�00000002014�12321736016�024326� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh // Key exchange tests. import ( "crypto/rand" "reflect" "testing" ) func TestKexes(t *testing.T) { type kexResultErr struct { result *kexResult err error } for name, kex := range kexAlgoMap { a, b := memPipe() s := make(chan kexResultErr, 1) c := make(chan kexResultErr, 1) var magics handshakeMagics go func() { r, e := kex.Client(a, rand.Reader, &magics) c <- kexResultErr{r, e} }() go func() { r, e := kex.Server(b, rand.Reader, &magics, ecdsaKey) s <- kexResultErr{r, e} }() clientRes := <-c serverRes := <-s if clientRes.err != nil { t.Errorf("client: %v", clientRes.err) } if serverRes.err != nil { t.Errorf("server: %v", serverRes.err) } if !reflect.DeepEqual(clientRes.result, serverRes.result) { t.Errorf("kex %q: mismatch %#v, %#v", name, clientRes.result, serverRes.result) } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/common_test.go���������������������������������0000644�0000153�0000161�00000002513�12321736016�025033� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "io" "net" "testing" ) func TestSafeString(t *testing.T) { strings := map[string]string{ "\x20\x0d\x0a": "\x20\x0d\x0a", "flibble": "flibble", "new\x20line": "new\x20line", "123456\x07789": "123456 789", "\t\t\x10\r\n": "\t\t \r\n", } for s, expected := range strings { actual := safeString(s) if expected != actual { t.Errorf("expected: %v, actual: %v", []byte(expected), []byte(actual)) } } } // Make sure Read/Write are not exposed. func TestConnHideRWMethods(t *testing.T) { for _, c := range []interface{}{new(ServerConn), new(ClientConn)} { if _, ok := c.(io.Reader); ok { t.Errorf("%T implements io.Reader", c) } if _, ok := c.(io.Writer); ok { t.Errorf("%T implements io.Writer", c) } } } func TestConnSupportsLocalRemoteMethods(t *testing.T) { type LocalAddr interface { LocalAddr() net.Addr } type RemoteAddr interface { RemoteAddr() net.Addr } for _, c := range []interface{}{new(ServerConn), new(ClientConn)} { if _, ok := c.(LocalAddr); !ok { t.Errorf("%T does not implement LocalAddr", c) } if _, ok := c.(RemoteAddr); !ok { t.Errorf("%T does not implement RemoteAddr", c) } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/server_terminal.go�����������������������������0000644�0000153�0000161�00000003361�12321736016�025707� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh // A Terminal is capable of parsing and generating virtual terminal // data from an SSH client. type Terminal interface { ReadLine() (line string, err error) SetSize(x, y int) Write([]byte) (int, error) } // ServerTerminal contains the state for running a terminal that is capable of // reading lines of input. type ServerTerminal struct { Term Terminal Channel Channel } // parsePtyRequest parses the payload of the pty-req message and extracts the // dimensions of the terminal. See RFC 4254, section 6.2. func parsePtyRequest(s []byte) (width, height int, ok bool) { _, s, ok = parseString(s) if !ok { return } width32, s, ok := parseUint32(s) if !ok { return } height32, _, ok := parseUint32(s) width = int(width32) height = int(height32) if width < 1 { ok = false } if height < 1 { ok = false } return } func (ss *ServerTerminal) Write(buf []byte) (n int, err error) { return ss.Term.Write(buf) } // ReadLine returns a line of input from the terminal. func (ss *ServerTerminal) ReadLine() (line string, err error) { for { if line, err = ss.Term.ReadLine(); err == nil { return } req, ok := err.(ChannelRequest) if !ok { return } ok = false switch req.Request { case "pty-req": var width, height int width, height, ok = parsePtyRequest(req.Payload) ss.Term.SetSize(width, height) case "shell": ok = true if len(req.Payload) > 0 { // We don't accept any commands, only the default shell. ok = false } case "env": ok = true } if req.WantReply { ss.Channel.AckRequest(ok) } } panic("unreachable") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/doc.go�����������������������������������������0000644�0000153�0000161�00000001473�12321736016�023255� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh implements an SSH client and server. SSH is a transport security protocol, an authentication protocol and a family of application protocols. The most typical application level protocol is a remote shell and this is specifically implemented. However, the multiplexed nature of SSH is exposed to users that wish to support others. References: [PROTOCOL.certkeys]: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys [PROTOCOL.agent]: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 */ package ssh �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/server.go��������������������������������������0000644�0000153�0000161�00000044177�12321736016�024026� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "bytes" "crypto/rand" "encoding/binary" "errors" "fmt" "io" "net" "sync" _ "crypto/sha1" ) type ServerConfig struct { hostKeys []Signer // Rand provides the source of entropy for key exchange. If Rand is // nil, the cryptographic random reader in package crypto/rand will // be used. Rand io.Reader // NoClientAuth is true if clients are allowed to connect without // authenticating. NoClientAuth bool // PasswordCallback, if non-nil, is called when a user attempts to // authenticate using a password. It may be called concurrently from // several goroutines. PasswordCallback func(conn *ServerConn, user, password string) bool // PublicKeyCallback, if non-nil, is called when a client attempts public // key authentication. It must return true if the given public key is // valid for the given user. PublicKeyCallback func(conn *ServerConn, user, algo string, pubkey []byte) bool // KeyboardInteractiveCallback, if non-nil, is called when // keyboard-interactive authentication is selected (RFC // 4256). The client object's Challenge function should be // used to query the user. The callback may offer multiple // Challenge rounds. To avoid information leaks, the client // should be presented a challenge even if the user is // unknown. KeyboardInteractiveCallback func(conn *ServerConn, user string, client ClientKeyboardInteractive) bool // Cryptographic-related configuration. Crypto CryptoConfig } func (c *ServerConfig) rand() io.Reader { if c.Rand == nil { return rand.Reader } return c.Rand } // AddHostKey adds a private key as a host key. If an existing host // key exists with the same algorithm, it is overwritten. func (s *ServerConfig) AddHostKey(key Signer) { for i, k := range s.hostKeys { if k.PublicKey().PublicKeyAlgo() == key.PublicKey().PublicKeyAlgo() { s.hostKeys[i] = key return } } s.hostKeys = append(s.hostKeys, key) } // SetRSAPrivateKey sets the private key for a Server. A Server must have a // private key configured in order to accept connections. The private key must // be in the form of a PEM encoded, PKCS#1, RSA private key. The file "id_rsa" // typically contains such a key. func (s *ServerConfig) SetRSAPrivateKey(pemBytes []byte) error { priv, err := ParsePrivateKey(pemBytes) if err != nil { return err } s.AddHostKey(priv) return nil } // cachedPubKey contains the results of querying whether a public key is // acceptable for a user. The cache only applies to a single ServerConn. type cachedPubKey struct { user, algo string pubKey []byte result bool } const maxCachedPubKeys = 16 // A ServerConn represents an incoming connection. type ServerConn struct { transport *transport config *ServerConfig channels map[uint32]*serverChan nextChanId uint32 // lock protects err and channels. lock sync.Mutex err error // cachedPubKeys contains the cache results of tests for public keys. // Since SSH clients will query whether a public key is acceptable // before attempting to authenticate with it, we end up with duplicate // queries for public key validity. cachedPubKeys []cachedPubKey // User holds the successfully authenticated user name. // It is empty if no authentication is used. It is populated before // any authentication callback is called and not assigned to after that. User string // ClientVersion is the client's version, populated after // Handshake is called. It should not be modified. ClientVersion []byte // Our version. serverVersion []byte } // Server returns a new SSH server connection // using c as the underlying transport. func Server(c net.Conn, config *ServerConfig) *ServerConn { return &ServerConn{ transport: newTransport(c, config.rand(), false /* not client */), channels: make(map[uint32]*serverChan), config: config, } } // signAndMarshal signs the data with the appropriate algorithm, // and serializes the result in SSH wire format. func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) { sig, err := k.Sign(rand, data) if err != nil { return nil, err } return serializeSignature(k.PublicKey().PrivateKeyAlgo(), sig), nil } // Close closes the connection. func (s *ServerConn) Close() error { return s.transport.Close() } // LocalAddr returns the local network address. func (c *ServerConn) LocalAddr() net.Addr { return c.transport.LocalAddr() } // RemoteAddr returns the remote network address. func (c *ServerConn) RemoteAddr() net.Addr { return c.transport.RemoteAddr() } // Handshake performs an SSH transport and client authentication on the given ServerConn. func (s *ServerConn) Handshake() error { var err error s.serverVersion = []byte(packageVersion) s.ClientVersion, err = exchangeVersions(s.transport.Conn, s.serverVersion) if err != nil { return err } if err := s.clientInitHandshake(nil, nil); err != nil { return err } var packet []byte if packet, err = s.transport.readPacket(); err != nil { return err } var serviceRequest serviceRequestMsg if err := unmarshal(&serviceRequest, packet, msgServiceRequest); err != nil { return err } if serviceRequest.Service != serviceUserAuth { return errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating") } serviceAccept := serviceAcceptMsg{ Service: serviceUserAuth, } if err := s.transport.writePacket(marshal(msgServiceAccept, serviceAccept)); err != nil { return err } if err := s.authenticate(); err != nil { return err } return err } func (s *ServerConn) clientInitHandshake(clientKexInit *kexInitMsg, clientKexInitPacket []byte) (err error) { serverKexInit := kexInitMsg{ KexAlgos: s.config.Crypto.kexes(), CiphersClientServer: s.config.Crypto.ciphers(), CiphersServerClient: s.config.Crypto.ciphers(), MACsClientServer: s.config.Crypto.macs(), MACsServerClient: s.config.Crypto.macs(), CompressionClientServer: supportedCompressions, CompressionServerClient: supportedCompressions, } for _, k := range s.config.hostKeys { serverKexInit.ServerHostKeyAlgos = append( serverKexInit.ServerHostKeyAlgos, k.PublicKey().PublicKeyAlgo()) } serverKexInitPacket := marshal(msgKexInit, serverKexInit) if err = s.transport.writePacket(serverKexInitPacket); err != nil { return } if clientKexInitPacket == nil { clientKexInit = new(kexInitMsg) if clientKexInitPacket, err = s.transport.readPacket(); err != nil { return } if err = unmarshal(clientKexInit, clientKexInitPacket, msgKexInit); err != nil { return } } algs := findAgreedAlgorithms(clientKexInit, &serverKexInit) if algs == nil { return errors.New("ssh: no common algorithms") } if clientKexInit.FirstKexFollows && algs.kex != clientKexInit.KexAlgos[0] { // The client sent a Kex message for the wrong algorithm, // which we have to ignore. if _, err = s.transport.readPacket(); err != nil { return } } var hostKey Signer for _, k := range s.config.hostKeys { if algs.hostKey == k.PublicKey().PublicKeyAlgo() { hostKey = k } } kex, ok := kexAlgoMap[algs.kex] if !ok { return fmt.Errorf("ssh: unexpected key exchange algorithm %v", algs.kex) } magics := handshakeMagics{ serverVersion: s.serverVersion, clientVersion: s.ClientVersion, serverKexInit: marshal(msgKexInit, serverKexInit), clientKexInit: clientKexInitPacket, } result, err := kex.Server(s.transport, s.config.rand(), &magics, hostKey) if err != nil { return err } if err = s.transport.prepareKeyChange(algs, result); err != nil { return err } if err = s.transport.writePacket([]byte{msgNewKeys}); err != nil { return } if packet, err := s.transport.readPacket(); err != nil { return err } else if packet[0] != msgNewKeys { return UnexpectedMessageError{msgNewKeys, packet[0]} } return } func isAcceptableAlgo(algo string) bool { switch algo { case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: return true } return false } // testPubKey returns true if the given public key is acceptable for the user. func (s *ServerConn) testPubKey(user, algo string, pubKey []byte) bool { if s.config.PublicKeyCallback == nil || !isAcceptableAlgo(algo) { return false } for _, c := range s.cachedPubKeys { if c.user == user && c.algo == algo && bytes.Equal(c.pubKey, pubKey) { return c.result } } result := s.config.PublicKeyCallback(s, user, algo, pubKey) if len(s.cachedPubKeys) < maxCachedPubKeys { c := cachedPubKey{ user: user, algo: algo, pubKey: make([]byte, len(pubKey)), result: result, } copy(c.pubKey, pubKey) s.cachedPubKeys = append(s.cachedPubKeys, c) } return result } func (s *ServerConn) authenticate() error { var userAuthReq userAuthRequestMsg var err error var packet []byte userAuthLoop: for { if packet, err = s.transport.readPacket(); err != nil { return err } if err = unmarshal(&userAuthReq, packet, msgUserAuthRequest); err != nil { return err } if userAuthReq.Service != serviceSSH { return errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service) } switch userAuthReq.Method { case "none": if s.config.NoClientAuth { break userAuthLoop } case "password": if s.config.PasswordCallback == nil { break } payload := userAuthReq.Payload if len(payload) < 1 || payload[0] != 0 { return ParseError{msgUserAuthRequest} } payload = payload[1:] password, payload, ok := parseString(payload) if !ok || len(payload) > 0 { return ParseError{msgUserAuthRequest} } s.User = userAuthReq.User if s.config.PasswordCallback(s, userAuthReq.User, string(password)) { break userAuthLoop } case "keyboard-interactive": if s.config.KeyboardInteractiveCallback == nil { break } s.User = userAuthReq.User if s.config.KeyboardInteractiveCallback(s, s.User, &sshClientKeyboardInteractive{s}) { break userAuthLoop } case "publickey": if s.config.PublicKeyCallback == nil { break } payload := userAuthReq.Payload if len(payload) < 1 { return ParseError{msgUserAuthRequest} } isQuery := payload[0] == 0 payload = payload[1:] algoBytes, payload, ok := parseString(payload) if !ok { return ParseError{msgUserAuthRequest} } algo := string(algoBytes) pubKey, payload, ok := parseString(payload) if !ok { return ParseError{msgUserAuthRequest} } if isQuery { // The client can query if the given public key // would be okay. if len(payload) > 0 { return ParseError{msgUserAuthRequest} } if s.testPubKey(userAuthReq.User, algo, pubKey) { okMsg := userAuthPubKeyOkMsg{ Algo: algo, PubKey: string(pubKey), } if err = s.transport.writePacket(marshal(msgUserAuthPubKeyOk, okMsg)); err != nil { return err } continue userAuthLoop } } else { sig, payload, ok := parseSignature(payload) if !ok || len(payload) > 0 { return ParseError{msgUserAuthRequest} } // Ensure the public key algo and signature algo // are supported. Compare the private key // algorithm name that corresponds to algo with // sig.Format. This is usually the same, but // for certs, the names differ. if !isAcceptableAlgo(algo) || !isAcceptableAlgo(sig.Format) || pubAlgoToPrivAlgo(algo) != sig.Format { break } signedData := buildDataSignedForAuth(s.transport.sessionID, userAuthReq, algoBytes, pubKey) key, _, ok := ParsePublicKey(pubKey) if !ok { return ParseError{msgUserAuthRequest} } if !key.Verify(signedData, sig.Blob) { return ParseError{msgUserAuthRequest} } // TODO(jmpittman): Implement full validation for certificates. s.User = userAuthReq.User if s.testPubKey(userAuthReq.User, algo, pubKey) { break userAuthLoop } } } var failureMsg userAuthFailureMsg if s.config.PasswordCallback != nil { failureMsg.Methods = append(failureMsg.Methods, "password") } if s.config.PublicKeyCallback != nil { failureMsg.Methods = append(failureMsg.Methods, "publickey") } if s.config.KeyboardInteractiveCallback != nil { failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive") } if len(failureMsg.Methods) == 0 { return errors.New("ssh: no authentication methods configured but NoClientAuth is also false") } if err = s.transport.writePacket(marshal(msgUserAuthFailure, failureMsg)); err != nil { return err } } packet = []byte{msgUserAuthSuccess} if err = s.transport.writePacket(packet); err != nil { return err } return nil } // sshClientKeyboardInteractive implements a ClientKeyboardInteractive by // asking the client on the other side of a ServerConn. type sshClientKeyboardInteractive struct { *ServerConn } func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) { if len(questions) != len(echos) { return nil, errors.New("ssh: echos and questions must have equal length") } var prompts []byte for i := range questions { prompts = appendString(prompts, questions[i]) prompts = appendBool(prompts, echos[i]) } if err := c.transport.writePacket(marshal(msgUserAuthInfoRequest, userAuthInfoRequestMsg{ Instruction: instruction, NumPrompts: uint32(len(questions)), Prompts: prompts, })); err != nil { return nil, err } packet, err := c.transport.readPacket() if err != nil { return nil, err } if packet[0] != msgUserAuthInfoResponse { return nil, UnexpectedMessageError{msgUserAuthInfoResponse, packet[0]} } packet = packet[1:] n, packet, ok := parseUint32(packet) if !ok || int(n) != len(questions) { return nil, &ParseError{msgUserAuthInfoResponse} } for i := uint32(0); i < n; i++ { ans, rest, ok := parseString(packet) if !ok { return nil, &ParseError{msgUserAuthInfoResponse} } answers = append(answers, string(ans)) packet = rest } if len(packet) != 0 { return nil, errors.New("ssh: junk at end of message") } return answers, nil } const defaultWindowSize = 32768 // Accept reads and processes messages on a ServerConn. It must be called // in order to demultiplex messages to any resulting Channels. func (s *ServerConn) Accept() (Channel, error) { // TODO(dfc) s.lock is not held here so visibility of s.err is not guaranteed. if s.err != nil { return nil, s.err } for { packet, err := s.transport.readPacket() if err != nil { s.lock.Lock() s.err = err s.lock.Unlock() // TODO(dfc) s.lock protects s.channels but isn't being held here. for _, c := range s.channels { c.setDead() c.handleData(nil) } return nil, err } switch packet[0] { case msgChannelData: if len(packet) < 9 { // malformed data packet return nil, ParseError{msgChannelData} } remoteId := binary.BigEndian.Uint32(packet[1:5]) s.lock.Lock() c, ok := s.channels[remoteId] if !ok { s.lock.Unlock() continue } if length := binary.BigEndian.Uint32(packet[5:9]); length > 0 { packet = packet[9:] c.handleData(packet[:length]) } s.lock.Unlock() default: decoded, err := decode(packet) if err != nil { return nil, err } switch msg := decoded.(type) { case *channelOpenMsg: if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { return nil, errors.New("ssh: invalid MaxPacketSize from peer") } c := &serverChan{ channel: channel{ packetConn: s.transport, remoteId: msg.PeersId, remoteWin: window{Cond: newCond()}, maxPacket: msg.MaxPacketSize, }, chanType: msg.ChanType, extraData: msg.TypeSpecificData, myWindow: defaultWindowSize, serverConn: s, cond: newCond(), pendingData: make([]byte, defaultWindowSize), } c.remoteWin.add(msg.PeersWindow) s.lock.Lock() c.localId = s.nextChanId s.nextChanId++ s.channels[c.localId] = c s.lock.Unlock() return c, nil case *channelRequestMsg: s.lock.Lock() c, ok := s.channels[msg.PeersId] if !ok { s.lock.Unlock() continue } c.handlePacket(msg) s.lock.Unlock() case *windowAdjustMsg: s.lock.Lock() c, ok := s.channels[msg.PeersId] if !ok { s.lock.Unlock() continue } c.handlePacket(msg) s.lock.Unlock() case *channelEOFMsg: s.lock.Lock() c, ok := s.channels[msg.PeersId] if !ok { s.lock.Unlock() continue } c.handlePacket(msg) s.lock.Unlock() case *channelCloseMsg: s.lock.Lock() c, ok := s.channels[msg.PeersId] if !ok { s.lock.Unlock() continue } c.handlePacket(msg) s.lock.Unlock() case *globalRequestMsg: if msg.WantReply { if err := s.transport.writePacket([]byte{msgRequestFailure}); err != nil { return nil, err } } case *kexInitMsg: s.lock.Lock() if err := s.clientInitHandshake(msg, packet); err != nil { s.lock.Unlock() return nil, err } s.lock.Unlock() case *disconnectMsg: return nil, io.EOF default: // Unknown message. Ignore. } } } panic("unreachable") } // A Listener implements a network listener (net.Listener) for SSH connections. type Listener struct { listener net.Listener config *ServerConfig } // Addr returns the listener's network address. func (l *Listener) Addr() net.Addr { return l.listener.Addr() } // Close closes the listener. func (l *Listener) Close() error { return l.listener.Close() } // Accept waits for and returns the next incoming SSH connection. // The receiver should call Handshake() in another goroutine // to avoid blocking the accepter. func (l *Listener) Accept() (*ServerConn, error) { c, err := l.listener.Accept() if err != nil { return nil, err } return Server(c, l.config), nil } // Listen creates an SSH listener accepting connections on // the given network address using net.Listen. func Listen(network, addr string, config *ServerConfig) (*Listener, error) { l, err := net.Listen(network, addr) if err != nil { return nil, err } return &Listener{ l, config, }, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/cipher.go��������������������������������������0000644�0000153�0000161�00000005666�12321736016�023772� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "crypto/aes" "crypto/cipher" "crypto/rc4" ) // streamDump is used to dump the initial keystream for stream ciphers. It is a // a write-only buffer, and not intended for reading so do not require a mutex. var streamDump [512]byte // noneCipher implements cipher.Stream and provides no encryption. It is used // by the transport before the first key-exchange. type noneCipher struct{} func (c noneCipher) XORKeyStream(dst, src []byte) { copy(dst, src) } func newAESCTR(key, iv []byte) (cipher.Stream, error) { c, err := aes.NewCipher(key) if err != nil { return nil, err } return cipher.NewCTR(c, iv), nil } func newRC4(key, iv []byte) (cipher.Stream, error) { return rc4.NewCipher(key) } type cipherMode struct { keySize int ivSize int skip int createFunc func(key, iv []byte) (cipher.Stream, error) } func (c *cipherMode) createCipher(key, iv []byte) (cipher.Stream, error) { if len(key) < c.keySize { panic("ssh: key length too small for cipher") } if len(iv) < c.ivSize { panic("ssh: iv too small for cipher") } stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize]) if err != nil { return nil, err } for remainingToDump := c.skip; remainingToDump > 0; { dumpThisTime := remainingToDump if dumpThisTime > len(streamDump) { dumpThisTime = len(streamDump) } stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) remainingToDump -= dumpThisTime } return stream, nil } // Specifies a default set of ciphers and a preference order. This is based on // OpenSSH's default client preference order, minus algorithms that are not // implemented. var DefaultCipherOrder = []string{ "aes128-ctr", "aes192-ctr", "aes256-ctr", "arcfour256", "arcfour128", } // cipherModes documents properties of supported ciphers. Ciphers not included // are not supported and will not be negotiated, even if explicitly requested in // ClientConfig.Crypto.Ciphers. var cipherModes = map[string]*cipherMode{ // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms // are defined in the order specified in the RFC. "aes128-ctr": {16, aes.BlockSize, 0, newAESCTR}, "aes192-ctr": {24, aes.BlockSize, 0, newAESCTR}, "aes256-ctr": {32, aes.BlockSize, 0, newAESCTR}, // Ciphers from RFC4345, which introduces security-improved arcfour ciphers. // They are defined in the order specified in the RFC. "arcfour128": {16, 0, 1536, newRC4}, "arcfour256": {32, 0, 1536, newRC4}, } // defaultKeyExchangeOrder specifies a default set of key exchange algorithms // with preferences. var defaultKeyExchangeOrder = []string{ // P384 and P521 are not constant-time yet, but since we don't // reuse ephemeral keys, using them for ECDH should be OK. kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, kexAlgoDH14SHA1, kexAlgoDH1SHA1, } ��������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/transport.go�����������������������������������0000644�0000153�0000161�00000024440�12321736016�024543� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "bufio" "crypto/cipher" "crypto/subtle" "encoding/binary" "errors" "hash" "io" "net" "sync" ) const ( packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. // RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations // MUST be able to process (plus a few more kilobytes for padding and mac). The RFC // indicates implementations SHOULD be able to handle larger packet sizes, but then // waffles on about reasonable limits. // // OpenSSH caps their maxPacket at 256kb so we choose to do the same. maxPacket = 256 * 1024 ) // packetConn represents a transport that implements packet based // operations. type packetConn interface { // Encrypt and send a packet of data to the remote peer. writePacket(packet []byte) error // Read a packet from the connection readPacket() ([]byte, error) // Close closes the write-side of the connection. Close() error } // transport represents the SSH connection to the remote peer. type transport struct { reader writer net.Conn // Initial H used for the session ID. Once assigned this does // not change, even during subsequent key exchanges. sessionID []byte } // reader represents the incoming connection state. type reader struct { io.Reader common } // writer represents the outgoing connection state. type writer struct { sync.Mutex // protects writer.Writer from concurrent writes *bufio.Writer rand io.Reader common } // prepareKeyChange sets up key material for a keychange. The key changes in // both directions are triggered by reading and writing a msgNewKey packet // respectively. func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error { t.writer.cipherAlgo = algs.wCipher t.writer.macAlgo = algs.wMAC t.writer.compressionAlgo = algs.wCompression t.reader.cipherAlgo = algs.rCipher t.reader.macAlgo = algs.rMAC t.reader.compressionAlgo = algs.rCompression if t.sessionID == nil { t.sessionID = kexResult.H } kexResult.SessionID = t.sessionID t.reader.pendingKeyChange <- kexResult t.writer.pendingKeyChange <- kexResult return nil } // common represents the cipher state needed to process messages in a single // direction. type common struct { seqNum uint32 mac hash.Hash cipher cipher.Stream cipherAlgo string macAlgo string compressionAlgo string dir direction pendingKeyChange chan *kexResult } // Read and decrypt a single packet from the remote peer. func (r *reader) readPacket() ([]byte, error) { var lengthBytes = make([]byte, 5) var macSize uint32 if _, err := io.ReadFull(r, lengthBytes); err != nil { return nil, err } r.cipher.XORKeyStream(lengthBytes, lengthBytes) if r.mac != nil { r.mac.Reset() seqNumBytes := []byte{ byte(r.seqNum >> 24), byte(r.seqNum >> 16), byte(r.seqNum >> 8), byte(r.seqNum), } r.mac.Write(seqNumBytes) r.mac.Write(lengthBytes) macSize = uint32(r.mac.Size()) } length := binary.BigEndian.Uint32(lengthBytes[0:4]) paddingLength := uint32(lengthBytes[4]) if length <= paddingLength+1 { return nil, errors.New("ssh: invalid packet length, packet too small") } if length > maxPacket { return nil, errors.New("ssh: invalid packet length, packet too large") } packet := make([]byte, length-1+macSize) if _, err := io.ReadFull(r, packet); err != nil { return nil, err } mac := packet[length-1:] r.cipher.XORKeyStream(packet, packet[:length-1]) if r.mac != nil { r.mac.Write(packet[:length-1]) if subtle.ConstantTimeCompare(r.mac.Sum(nil), mac) != 1 { return nil, errors.New("ssh: MAC failure") } } r.seqNum++ packet = packet[:length-paddingLength-1] if len(packet) > 0 && packet[0] == msgNewKeys { select { case k := <-r.pendingKeyChange: if err := r.setupKeys(r.dir, k); err != nil { return nil, err } default: return nil, errors.New("ssh: got bogus newkeys message.") } } return packet, nil } // Read and decrypt next packet discarding debug and noop messages. func (t *transport) readPacket() ([]byte, error) { for { packet, err := t.reader.readPacket() if err != nil { return nil, err } if len(packet) == 0 { return nil, errors.New("ssh: zero length packet") } if packet[0] != msgIgnore && packet[0] != msgDebug { return packet, nil } } panic("unreachable") } // Encrypt and send a packet of data to the remote peer. func (w *writer) writePacket(packet []byte) error { changeKeys := len(packet) > 0 && packet[0] == msgNewKeys if len(packet) > maxPacket { return errors.New("ssh: packet too large") } w.Mutex.Lock() defer w.Mutex.Unlock() paddingLength := packetSizeMultiple - (5+len(packet))%packetSizeMultiple if paddingLength < 4 { paddingLength += packetSizeMultiple } length := len(packet) + 1 + paddingLength lengthBytes := []byte{ byte(length >> 24), byte(length >> 16), byte(length >> 8), byte(length), byte(paddingLength), } padding := make([]byte, paddingLength) _, err := io.ReadFull(w.rand, padding) if err != nil { return err } if w.mac != nil { w.mac.Reset() seqNumBytes := []byte{ byte(w.seqNum >> 24), byte(w.seqNum >> 16), byte(w.seqNum >> 8), byte(w.seqNum), } w.mac.Write(seqNumBytes) w.mac.Write(lengthBytes) w.mac.Write(packet) w.mac.Write(padding) } // TODO(dfc) lengthBytes, packet and padding should be // subslices of a single buffer w.cipher.XORKeyStream(lengthBytes, lengthBytes) w.cipher.XORKeyStream(packet, packet) w.cipher.XORKeyStream(padding, padding) if _, err := w.Write(lengthBytes); err != nil { return err } if _, err := w.Write(packet); err != nil { return err } if _, err := w.Write(padding); err != nil { return err } if w.mac != nil { if _, err := w.Write(w.mac.Sum(nil)); err != nil { return err } } w.seqNum++ if err = w.Flush(); err != nil { return err } if changeKeys { select { case k := <-w.pendingKeyChange: err = w.setupKeys(w.dir, k) default: panic("ssh: no key material for msgNewKeys") } } return err } func newTransport(conn net.Conn, rand io.Reader, isClient bool) *transport { t := &transport{ reader: reader{ Reader: bufio.NewReader(conn), common: common{ cipher: noneCipher{}, pendingKeyChange: make(chan *kexResult, 1), }, }, writer: writer{ Writer: bufio.NewWriter(conn), rand: rand, common: common{ cipher: noneCipher{}, pendingKeyChange: make(chan *kexResult, 1), }, }, Conn: conn, } if isClient { t.reader.dir = serverKeys t.writer.dir = clientKeys } else { t.reader.dir = clientKeys t.writer.dir = serverKeys } return t } type direction struct { ivTag []byte keyTag []byte macKeyTag []byte } // TODO(dfc) can this be made a constant ? var ( serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} ) // setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as // described in RFC 4253, section 6.4. direction should either be serverKeys // (to setup server->client keys) or clientKeys (for client->server keys). func (c *common) setupKeys(d direction, r *kexResult) error { cipherMode := cipherModes[c.cipherAlgo] macMode := macModes[c.macAlgo] iv := make([]byte, cipherMode.ivSize) key := make([]byte, cipherMode.keySize) macKey := make([]byte, macMode.keySize) h := r.Hash.New() generateKeyMaterial(iv, d.ivTag, r.K, r.H, r.SessionID, h) generateKeyMaterial(key, d.keyTag, r.K, r.H, r.SessionID, h) generateKeyMaterial(macKey, d.macKeyTag, r.K, r.H, r.SessionID, h) c.mac = macMode.new(macKey) var err error c.cipher, err = cipherMode.createCipher(key, iv) return err } // generateKeyMaterial fills out with key material generated from tag, K, H // and sessionId, as specified in RFC 4253, section 7.2. func generateKeyMaterial(out, tag []byte, K, H, sessionId []byte, h hash.Hash) { var digestsSoFar []byte for len(out) > 0 { h.Reset() h.Write(K) h.Write(H) if len(digestsSoFar) == 0 { h.Write(tag) h.Write(sessionId) } else { h.Write(digestsSoFar) } digest := h.Sum(nil) n := copy(out, digest) out = out[n:] if len(out) > 0 { digestsSoFar = append(digestsSoFar, digest...) } } } const packageVersion = "SSH-2.0-Go" // Sends and receives a version line. The versionLine string should // be US ASCII, start with "SSH-2.0-", and should not include a // newline. exchangeVersions returns the other side's version line. func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) { // Contrary to the RFC, we do not ignore lines that don't // start with "SSH-2.0-" to make the library usable with // nonconforming servers. for _, c := range versionLine { // The spec disallows non US-ASCII chars, and // specifically forbids null chars. if c < 32 { return nil, errors.New("ssh: junk character in version line") } } if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil { return } them, err = readVersion(rw) return them, err } // maxVersionStringBytes is the maximum number of bytes that we'll // accept as a version string. RFC 4253 section 4.2 limits this at 255 // chars const maxVersionStringBytes = 255 // Read version string as specified by RFC 4253, section 4.2. func readVersion(r io.Reader) ([]byte, error) { versionString := make([]byte, 0, 64) var ok bool var buf [1]byte for len(versionString) < maxVersionStringBytes { _, err := io.ReadFull(r, buf[:]) if err != nil { return nil, err } // The RFC says that the version should be terminated with \r\n // but several SSH servers actually only send a \n. if buf[0] == '\n' { ok = true break } // non ASCII chars are disallowed, but we are lenient, // since Go doesn't use null-terminated strings. // The RFC allows a comment after a space, however, // all of it (version and comments) goes into the // session hash. versionString = append(versionString, buf[0]) } if !ok { return nil, errors.New("ssh: overflow reading version string") } // There might be a '\r' on the end which we should remove. if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' { versionString = versionString[:len(versionString)-1] } return versionString, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/certs.go���������������������������������������0000644�0000153�0000161�00000021573�12321736016�023633� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "time" ) // These constants from [PROTOCOL.certkeys] represent the algorithm names // for certificate types supported by this package. const ( CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com" CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com" CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com" CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com" CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com" ) // Certificate types are used to specify whether a certificate is for identification // of a user or a host. Current identities are defined in [PROTOCOL.certkeys]. const ( UserCert = 1 HostCert = 2 ) type signature struct { Format string Blob []byte } type tuple struct { Name string Data string } const ( maxUint64 = 1<<64 - 1 maxInt64 = 1<<63 - 1 ) // CertTime represents an unsigned 64-bit time value in seconds starting from // UNIX epoch. We use CertTime instead of time.Time in order to properly handle // the "infinite" time value ^0, which would become negative when expressed as // an int64. type CertTime uint64 func (ct CertTime) Time() time.Time { if ct > maxInt64 { return time.Unix(maxInt64, 0) } return time.Unix(int64(ct), 0) } func (ct CertTime) IsInfinite() bool { return ct == maxUint64 } // An OpenSSHCertV01 represents an OpenSSH certificate as defined in // [PROTOCOL.certkeys]?rev=1.8. type OpenSSHCertV01 struct { Nonce []byte Key PublicKey Serial uint64 Type uint32 KeyId string ValidPrincipals []string ValidAfter, ValidBefore CertTime CriticalOptions []tuple Extensions []tuple Reserved []byte SignatureKey PublicKey Signature *signature } // validateOpenSSHCertV01Signature uses the cert's SignatureKey to verify that // the cert's Signature.Blob is the result of signing the cert bytes starting // from the algorithm string and going up to and including the SignatureKey. func validateOpenSSHCertV01Signature(cert *OpenSSHCertV01) bool { return cert.SignatureKey.Verify(cert.BytesForSigning(), cert.Signature.Blob) } var certAlgoNames = map[string]string{ KeyAlgoRSA: CertAlgoRSAv01, KeyAlgoDSA: CertAlgoDSAv01, KeyAlgoECDSA256: CertAlgoECDSA256v01, KeyAlgoECDSA384: CertAlgoECDSA384v01, KeyAlgoECDSA521: CertAlgoECDSA521v01, } // certToPrivAlgo returns the underlying algorithm for a certificate algorithm. // Panics if a non-certificate algorithm is passed. func certToPrivAlgo(algo string) string { for privAlgo, pubAlgo := range certAlgoNames { if pubAlgo == algo { return privAlgo } } panic("unknown cert algorithm") } func (cert *OpenSSHCertV01) marshal(includeAlgo, includeSig bool) []byte { algoName := cert.PublicKeyAlgo() pubKey := cert.Key.Marshal() sigKey := MarshalPublicKey(cert.SignatureKey) var length int if includeAlgo { length += stringLength(len(algoName)) } length += stringLength(len(cert.Nonce)) length += len(pubKey) length += 8 // Length of Serial length += 4 // Length of Type length += stringLength(len(cert.KeyId)) length += lengthPrefixedNameListLength(cert.ValidPrincipals) length += 8 // Length of ValidAfter length += 8 // Length of ValidBefore length += tupleListLength(cert.CriticalOptions) length += tupleListLength(cert.Extensions) length += stringLength(len(cert.Reserved)) length += stringLength(len(sigKey)) if includeSig { length += signatureLength(cert.Signature) } ret := make([]byte, length) r := ret if includeAlgo { r = marshalString(r, []byte(algoName)) } r = marshalString(r, cert.Nonce) copy(r, pubKey) r = r[len(pubKey):] r = marshalUint64(r, cert.Serial) r = marshalUint32(r, cert.Type) r = marshalString(r, []byte(cert.KeyId)) r = marshalLengthPrefixedNameList(r, cert.ValidPrincipals) r = marshalUint64(r, uint64(cert.ValidAfter)) r = marshalUint64(r, uint64(cert.ValidBefore)) r = marshalTupleList(r, cert.CriticalOptions) r = marshalTupleList(r, cert.Extensions) r = marshalString(r, cert.Reserved) r = marshalString(r, sigKey) if includeSig { r = marshalSignature(r, cert.Signature) } if len(r) > 0 { panic("ssh: internal error, marshaling certificate did not fill the entire buffer") } return ret } func (cert *OpenSSHCertV01) BytesForSigning() []byte { return cert.marshal(true, false) } func (cert *OpenSSHCertV01) Marshal() []byte { return cert.marshal(false, true) } func (c *OpenSSHCertV01) PublicKeyAlgo() string { algo, ok := certAlgoNames[c.Key.PublicKeyAlgo()] if !ok { panic("unknown cert key type") } return algo } func (c *OpenSSHCertV01) PrivateKeyAlgo() string { return c.Key.PrivateKeyAlgo() } func (c *OpenSSHCertV01) Verify(data []byte, sig []byte) bool { return c.Key.Verify(data, sig) } func parseOpenSSHCertV01(in []byte, algo string) (out *OpenSSHCertV01, rest []byte, ok bool) { cert := new(OpenSSHCertV01) if cert.Nonce, in, ok = parseString(in); !ok { return } privAlgo := certToPrivAlgo(algo) cert.Key, in, ok = parsePubKey(in, privAlgo) if !ok { return } // We test PublicKeyAlgo to make sure we don't use some weird sub-cert. if cert.Key.PublicKeyAlgo() != privAlgo { ok = false return } if cert.Serial, in, ok = parseUint64(in); !ok { return } if cert.Type, in, ok = parseUint32(in); !ok { return } keyId, in, ok := parseString(in) if !ok { return } cert.KeyId = string(keyId) if cert.ValidPrincipals, in, ok = parseLengthPrefixedNameList(in); !ok { return } va, in, ok := parseUint64(in) if !ok { return } cert.ValidAfter = CertTime(va) vb, in, ok := parseUint64(in) if !ok { return } cert.ValidBefore = CertTime(vb) if cert.CriticalOptions, in, ok = parseTupleList(in); !ok { return } if cert.Extensions, in, ok = parseTupleList(in); !ok { return } if cert.Reserved, in, ok = parseString(in); !ok { return } sigKey, in, ok := parseString(in) if !ok { return } if cert.SignatureKey, _, ok = ParsePublicKey(sigKey); !ok { return } if cert.Signature, in, ok = parseSignature(in); !ok { return } ok = true return cert, in, ok } func lengthPrefixedNameListLength(namelist []string) int { length := 4 // length prefix for list for _, name := range namelist { length += 4 // length prefix for name length += len(name) } return length } func marshalLengthPrefixedNameList(to []byte, namelist []string) []byte { length := uint32(lengthPrefixedNameListLength(namelist) - 4) to = marshalUint32(to, length) for _, name := range namelist { to = marshalString(to, []byte(name)) } return to } func parseLengthPrefixedNameList(in []byte) (out []string, rest []byte, ok bool) { list, rest, ok := parseString(in) if !ok { return } for len(list) > 0 { var next []byte if next, list, ok = parseString(list); !ok { return nil, nil, false } out = append(out, string(next)) } ok = true return } func tupleListLength(tupleList []tuple) int { length := 4 // length prefix for list for _, t := range tupleList { length += 4 // length prefix for t.Name length += len(t.Name) length += 4 // length prefix for t.Data length += len(t.Data) } return length } func marshalTupleList(to []byte, tuplelist []tuple) []byte { length := uint32(tupleListLength(tuplelist) - 4) to = marshalUint32(to, length) for _, t := range tuplelist { to = marshalString(to, []byte(t.Name)) to = marshalString(to, []byte(t.Data)) } return to } func parseTupleList(in []byte) (out []tuple, rest []byte, ok bool) { list, rest, ok := parseString(in) if !ok { return } for len(list) > 0 { var name, data []byte var ok bool name, list, ok = parseString(list) if !ok { return nil, nil, false } data, list, ok = parseString(list) if !ok { return nil, nil, false } out = append(out, tuple{string(name), string(data)}) } ok = true return } func signatureLength(sig *signature) int { length := 4 // length prefix for signature length += stringLength(len(sig.Format)) length += stringLength(len(sig.Blob)) return length } func marshalSignature(to []byte, sig *signature) []byte { length := uint32(signatureLength(sig) - 4) to = marshalUint32(to, length) to = marshalString(to, []byte(sig.Format)) to = marshalString(to, sig.Blob) return to } func parseSignatureBody(in []byte) (out *signature, rest []byte, ok bool) { var format []byte if format, in, ok = parseString(in); !ok { return } out = &signature{ Format: string(format), } if out.Blob, in, ok = parseString(in); !ok { return } return out, in, ok } func parseSignature(in []byte) (out *signature, rest []byte, ok bool) { var sigBytes []byte if sigBytes, rest, ok = parseString(in); !ok { return } out, sigBytes, ok = parseSignatureBody(sigBytes) if !ok || len(sigBytes) > 0 { return nil, nil, false } return } �������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/session.go�������������������������������������0000644�0000153�0000161�00000035753�12321736016�024203� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh // Session implements an interactive session described in // "RFC 4254, section 6". import ( "bytes" "errors" "fmt" "io" "io/ioutil" "sync" ) type Signal string // POSIX signals as listed in RFC 4254 Section 6.10. const ( SIGABRT Signal = "ABRT" SIGALRM Signal = "ALRM" SIGFPE Signal = "FPE" SIGHUP Signal = "HUP" SIGILL Signal = "ILL" SIGINT Signal = "INT" SIGKILL Signal = "KILL" SIGPIPE Signal = "PIPE" SIGQUIT Signal = "QUIT" SIGSEGV Signal = "SEGV" SIGTERM Signal = "TERM" SIGUSR1 Signal = "USR1" SIGUSR2 Signal = "USR2" ) var signals = map[Signal]int{ SIGABRT: 6, SIGALRM: 14, SIGFPE: 8, SIGHUP: 1, SIGILL: 4, SIGINT: 2, SIGKILL: 9, SIGPIPE: 13, SIGQUIT: 3, SIGSEGV: 11, SIGTERM: 15, } type TerminalModes map[uint8]uint32 // POSIX terminal mode flags as listed in RFC 4254 Section 8. const ( tty_OP_END = 0 VINTR = 1 VQUIT = 2 VERASE = 3 VKILL = 4 VEOF = 5 VEOL = 6 VEOL2 = 7 VSTART = 8 VSTOP = 9 VSUSP = 10 VDSUSP = 11 VREPRINT = 12 VWERASE = 13 VLNEXT = 14 VFLUSH = 15 VSWTCH = 16 VSTATUS = 17 VDISCARD = 18 IGNPAR = 30 PARMRK = 31 INPCK = 32 ISTRIP = 33 INLCR = 34 IGNCR = 35 ICRNL = 36 IUCLC = 37 IXON = 38 IXANY = 39 IXOFF = 40 IMAXBEL = 41 ISIG = 50 ICANON = 51 XCASE = 52 ECHO = 53 ECHOE = 54 ECHOK = 55 ECHONL = 56 NOFLSH = 57 TOSTOP = 58 IEXTEN = 59 ECHOCTL = 60 ECHOKE = 61 PENDIN = 62 OPOST = 70 OLCUC = 71 ONLCR = 72 OCRNL = 73 ONOCR = 74 ONLRET = 75 CS7 = 90 CS8 = 91 PARENB = 92 PARODD = 93 TTY_OP_ISPEED = 128 TTY_OP_OSPEED = 129 ) // A Session represents a connection to a remote command or shell. type Session struct { // Stdin specifies the remote process's standard input. // If Stdin is nil, the remote process reads from an empty // bytes.Buffer. Stdin io.Reader // Stdout and Stderr specify the remote process's standard // output and error. // // If either is nil, Run connects the corresponding file // descriptor to an instance of ioutil.Discard. There is a // fixed amount of buffering that is shared for the two streams. // If either blocks it may eventually cause the remote // command to block. Stdout io.Writer Stderr io.Writer *clientChan // the channel backing this session started bool // true once Start, Run or Shell is invoked. copyFuncs []func() error errors chan error // one send per copyFunc // true if pipe method is active stdinpipe, stdoutpipe, stderrpipe bool } // RFC 4254 Section 6.4. type setenvRequest struct { PeersId uint32 Request string WantReply bool Name string Value string } // RFC 4254 Section 6.5. type subsystemRequestMsg struct { PeersId uint32 Request string WantReply bool Subsystem string } // Setenv sets an environment variable that will be applied to any // command executed by Shell or Run. func (s *Session) Setenv(name, value string) error { req := setenvRequest{ PeersId: s.remoteId, Request: "env", WantReply: true, Name: name, Value: value, } if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { return err } return s.waitForResponse() } // RFC 4254 Section 6.2. type ptyRequestMsg struct { PeersId uint32 Request string WantReply bool Term string Columns uint32 Rows uint32 Width uint32 Height uint32 Modelist string } // RequestPty requests the association of a pty with the session on the remote host. func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error { var tm []byte for k, v := range termmodes { tm = append(tm, k) tm = appendU32(tm, v) } tm = append(tm, tty_OP_END) req := ptyRequestMsg{ PeersId: s.remoteId, Request: "pty-req", WantReply: true, Term: term, Columns: uint32(w), Rows: uint32(h), Width: uint32(w * 8), Height: uint32(h * 8), Modelist: string(tm), } if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { return err } return s.waitForResponse() } // RequestSubsystem requests the association of a subsystem with the session on the remote host. // A subsystem is a predefined command that runs in the background when the ssh session is initiated func (s *Session) RequestSubsystem(subsystem string) error { req := subsystemRequestMsg{ PeersId: s.remoteId, Request: "subsystem", WantReply: true, Subsystem: subsystem, } if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { return err } return s.waitForResponse() } // RFC 4254 Section 6.9. type signalMsg struct { PeersId uint32 Request string WantReply bool Signal string } // Signal sends the given signal to the remote process. // sig is one of the SIG* constants. func (s *Session) Signal(sig Signal) error { req := signalMsg{ PeersId: s.remoteId, Request: "signal", WantReply: false, Signal: string(sig), } return s.writePacket(marshal(msgChannelRequest, req)) } // RFC 4254 Section 6.5. type execMsg struct { PeersId uint32 Request string WantReply bool Command string } // Start runs cmd on the remote host. Typically, the remote // server passes cmd to the shell for interpretation. // A Session only accepts one call to Run, Start or Shell. func (s *Session) Start(cmd string) error { if s.started { return errors.New("ssh: session already started") } req := execMsg{ PeersId: s.remoteId, Request: "exec", WantReply: true, Command: cmd, } if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { return err } if err := s.waitForResponse(); err != nil { return fmt.Errorf("ssh: could not execute command %s: %v", cmd, err) } return s.start() } // Run runs cmd on the remote host. Typically, the remote // server passes cmd to the shell for interpretation. // A Session only accepts one call to Run, Start, Shell, Output, // or CombinedOutput. // // The returned error is nil if the command runs, has no problems // copying stdin, stdout, and stderr, and exits with a zero exit // status. // // If the command fails to run or doesn't complete successfully, the // error is of type *ExitError. Other error types may be // returned for I/O problems. func (s *Session) Run(cmd string) error { err := s.Start(cmd) if err != nil { return err } return s.Wait() } // Output runs cmd on the remote host and returns its standard output. func (s *Session) Output(cmd string) ([]byte, error) { if s.Stdout != nil { return nil, errors.New("ssh: Stdout already set") } var b bytes.Buffer s.Stdout = &b err := s.Run(cmd) return b.Bytes(), err } type singleWriter struct { b bytes.Buffer mu sync.Mutex } func (w *singleWriter) Write(p []byte) (int, error) { w.mu.Lock() defer w.mu.Unlock() return w.b.Write(p) } // CombinedOutput runs cmd on the remote host and returns its combined // standard output and standard error. func (s *Session) CombinedOutput(cmd string) ([]byte, error) { if s.Stdout != nil { return nil, errors.New("ssh: Stdout already set") } if s.Stderr != nil { return nil, errors.New("ssh: Stderr already set") } var b singleWriter s.Stdout = &b s.Stderr = &b err := s.Run(cmd) return b.b.Bytes(), err } // Shell starts a login shell on the remote host. A Session only // accepts one call to Run, Start, Shell, Output, or CombinedOutput. func (s *Session) Shell() error { if s.started { return errors.New("ssh: session already started") } req := channelRequestMsg{ PeersId: s.remoteId, Request: "shell", WantReply: true, } if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { return err } if err := s.waitForResponse(); err != nil { return fmt.Errorf("ssh: could not execute shell: %v", err) } return s.start() } func (s *Session) waitForResponse() error { msg := <-s.msg switch msg.(type) { case *channelRequestSuccessMsg: return nil case *channelRequestFailureMsg: return errors.New("ssh: request failed") } return fmt.Errorf("ssh: unknown packet %T received: %v", msg, msg) } func (s *Session) start() error { s.started = true type F func(*Session) for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} { setupFd(s) } s.errors = make(chan error, len(s.copyFuncs)) for _, fn := range s.copyFuncs { go func(fn func() error) { s.errors <- fn() }(fn) } return nil } // Wait waits for the remote command to exit. // // The returned error is nil if the command runs, has no problems // copying stdin, stdout, and stderr, and exits with a zero exit // status. // // If the command fails to run or doesn't complete successfully, the // error is of type *ExitError. Other error types may be // returned for I/O problems. func (s *Session) Wait() error { if !s.started { return errors.New("ssh: session not started") } waitErr := s.wait() var copyError error for _ = range s.copyFuncs { if err := <-s.errors; err != nil && copyError == nil { copyError = err } } if waitErr != nil { return waitErr } return copyError } func (s *Session) wait() error { wm := Waitmsg{status: -1} // Wait for msg channel to be closed before returning. for msg := range s.msg { switch msg := msg.(type) { case *channelRequestMsg: switch msg.Request { case "exit-status": d := msg.RequestSpecificData wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3]) case "exit-signal": signal, rest, ok := parseString(msg.RequestSpecificData) if !ok { return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData) } wm.signal = safeString(string(signal)) // skip coreDumped bool if len(rest) == 0 { return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData) } rest = rest[1:] errmsg, rest, ok := parseString(rest) if !ok { return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData) } wm.msg = safeString(string(errmsg)) lang, _, ok := parseString(rest) if !ok { return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData) } wm.lang = safeString(string(lang)) default: // This handles keepalives and matches // OpenSSH's behaviour. if msg.WantReply { s.writePacket(marshal(msgChannelFailure, channelRequestFailureMsg{ PeersId: s.remoteId, })) } } default: return fmt.Errorf("wait: unexpected packet %T received: %v", msg, msg) } } if wm.status == 0 { return nil } if wm.status == -1 { // exit-status was never sent from server if wm.signal == "" { return errors.New("wait: remote command exited without exit status or exit signal") } wm.status = 128 if _, ok := signals[Signal(wm.signal)]; ok { wm.status += signals[Signal(wm.signal)] } } return &ExitError{wm} } func (s *Session) stdin() { if s.stdinpipe { return } if s.Stdin == nil { s.Stdin = new(bytes.Buffer) } s.copyFuncs = append(s.copyFuncs, func() error { _, err := io.Copy(s.clientChan.stdin, s.Stdin) if err1 := s.clientChan.stdin.Close(); err == nil && err1 != io.EOF { err = err1 } return err }) } func (s *Session) stdout() { if s.stdoutpipe { return } if s.Stdout == nil { s.Stdout = ioutil.Discard } s.copyFuncs = append(s.copyFuncs, func() error { _, err := io.Copy(s.Stdout, s.clientChan.stdout) return err }) } func (s *Session) stderr() { if s.stderrpipe { return } if s.Stderr == nil { s.Stderr = ioutil.Discard } s.copyFuncs = append(s.copyFuncs, func() error { _, err := io.Copy(s.Stderr, s.clientChan.stderr) return err }) } // StdinPipe returns a pipe that will be connected to the // remote command's standard input when the command starts. func (s *Session) StdinPipe() (io.WriteCloser, error) { if s.Stdin != nil { return nil, errors.New("ssh: Stdin already set") } if s.started { return nil, errors.New("ssh: StdinPipe after process started") } s.stdinpipe = true return s.clientChan.stdin, nil } // StdoutPipe returns a pipe that will be connected to the // remote command's standard output when the command starts. // There is a fixed amount of buffering that is shared between // stdout and stderr streams. If the StdoutPipe reader is // not serviced fast enough it may eventually cause the // remote command to block. func (s *Session) StdoutPipe() (io.Reader, error) { if s.Stdout != nil { return nil, errors.New("ssh: Stdout already set") } if s.started { return nil, errors.New("ssh: StdoutPipe after process started") } s.stdoutpipe = true return s.clientChan.stdout, nil } // StderrPipe returns a pipe that will be connected to the // remote command's standard error when the command starts. // There is a fixed amount of buffering that is shared between // stdout and stderr streams. If the StderrPipe reader is // not serviced fast enough it may eventually cause the // remote command to block. func (s *Session) StderrPipe() (io.Reader, error) { if s.Stderr != nil { return nil, errors.New("ssh: Stderr already set") } if s.started { return nil, errors.New("ssh: StderrPipe after process started") } s.stderrpipe = true return s.clientChan.stderr, nil } // NewSession returns a new interactive session on the remote host. func (c *ClientConn) NewSession() (*Session, error) { ch := c.newChan(c.transport) if err := c.transport.writePacket(marshal(msgChannelOpen, channelOpenMsg{ ChanType: "session", PeersId: ch.localId, PeersWindow: channelWindowSize, MaxPacketSize: channelMaxPacketSize, })); err != nil { c.chanList.remove(ch.localId) return nil, err } if err := ch.waitForChannelOpenResponse(); err != nil { c.chanList.remove(ch.localId) return nil, fmt.Errorf("ssh: unable to open session: %v", err) } return &Session{ clientChan: ch, }, nil } // An ExitError reports unsuccessful completion of a remote command. type ExitError struct { Waitmsg } func (e *ExitError) Error() string { return e.Waitmsg.String() } // Waitmsg stores the information about an exited remote command // as reported by Wait. type Waitmsg struct { status int signal string msg string lang string } // ExitStatus returns the exit status of the remote command. func (w Waitmsg) ExitStatus() int { return w.status } // Signal returns the exit signal of the remote command if // it was terminated violently. func (w Waitmsg) Signal() string { return w.signal } // Msg returns the exit message given by the remote command func (w Waitmsg) Msg() string { return w.msg } // Lang returns the language tag. See RFC 3066 func (w Waitmsg) Lang() string { return w.lang } func (w Waitmsg) String() string { return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal) } ���������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/keys_test.go�����������������������������������0000644�0000153�0000161�00000013101�12321736016�024511� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ssh import ( "crypto/dsa" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "reflect" "strings" "testing" ) var ( ecdsaKey Signer ecdsa384Key Signer ecdsa521Key Signer testCertKey Signer ) type testSigner struct { Signer pub PublicKey } func (ts *testSigner) PublicKey() PublicKey { if ts.pub != nil { return ts.pub } return ts.Signer.PublicKey() } func init() { raw256, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) ecdsaKey, _ = NewSignerFromKey(raw256) raw384, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) ecdsa384Key, _ = NewSignerFromKey(raw384) raw521, _ := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) ecdsa521Key, _ = NewSignerFromKey(raw521) // Create a cert and sign it for use in tests. testCert := &OpenSSHCertV01{ Nonce: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil Key: ecdsaKey.PublicKey(), ValidPrincipals: []string{"gopher1", "gopher2"}, // increases test coverage ValidAfter: 0, // unix epoch ValidBefore: maxUint64, // The end of currently representable time. Reserved: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil SignatureKey: rsaKey.PublicKey(), } sigBytes, _ := rsaKey.Sign(rand.Reader, testCert.BytesForSigning()) testCert.Signature = &signature{ Format: testCert.SignatureKey.PublicKeyAlgo(), Blob: sigBytes, } testCertKey = &testSigner{ Signer: ecdsaKey, pub: testCert, } } func rawKey(pub PublicKey) interface{} { switch k := pub.(type) { case *rsaPublicKey: return (*rsa.PublicKey)(k) case *dsaPublicKey: return (*dsa.PublicKey)(k) case *ecdsaPublicKey: return (*ecdsa.PublicKey)(k) case *OpenSSHCertV01: return k } panic("unknown key type") } func TestKeyMarshalParse(t *testing.T) { keys := []Signer{rsaKey, dsaKey, ecdsaKey, ecdsa384Key, ecdsa521Key, testCertKey} for _, priv := range keys { pub := priv.PublicKey() roundtrip, rest, ok := ParsePublicKey(MarshalPublicKey(pub)) if !ok { t.Errorf("ParsePublicKey(%T) failed", pub) } if len(rest) > 0 { t.Errorf("ParsePublicKey(%T): trailing junk", pub) } k1 := rawKey(pub) k2 := rawKey(roundtrip) if !reflect.DeepEqual(k1, k2) { t.Errorf("got %#v in roundtrip, want %#v", k2, k1) } } } func TestUnsupportedCurves(t *testing.T) { raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) if err != nil { t.Fatalf("GenerateKey: %v", err) } if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P256") { t.Fatalf("NewPrivateKey should not succeed with P224, got: %v", err) } if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P256") { t.Fatalf("NewPublicKey should not succeed with P224, got: %v", err) } } func TestNewPublicKey(t *testing.T) { keys := []Signer{rsaKey, dsaKey, ecdsaKey} for _, k := range keys { raw := rawKey(k.PublicKey()) pub, err := NewPublicKey(raw) if err != nil { t.Errorf("NewPublicKey(%#v): %v", raw, err) } if !reflect.DeepEqual(k.PublicKey(), pub) { t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey()) } } } func TestKeySignVerify(t *testing.T) { keys := []Signer{rsaKey, dsaKey, ecdsaKey, testCertKey} for _, priv := range keys { pub := priv.PublicKey() data := []byte("sign me") sig, err := priv.Sign(rand.Reader, data) if err != nil { t.Fatalf("Sign(%T): %v", priv, err) } if !pub.Verify(data, sig) { t.Errorf("publicKey.Verify(%T) failed", priv) } } } func TestParseRSAPrivateKey(t *testing.T) { key, err := ParsePrivateKey([]byte(testServerPrivateKey)) if err != nil { t.Fatalf("ParsePrivateKey: %v", err) } rsa, ok := key.(*rsaPrivateKey) if !ok { t.Fatalf("got %T, want *rsa.PrivateKey", rsa) } if err := rsa.Validate(); err != nil { t.Errorf("Validate: %v", err) } } func TestParseECPrivateKey(t *testing.T) { // Taken from the data in test/ . pem := []byte(`-----BEGIN EC PRIVATE KEY----- MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49 AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+ 6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA== -----END EC PRIVATE KEY-----`) key, err := ParsePrivateKey(pem) if err != nil { t.Fatalf("ParsePrivateKey: %v", err) } ecKey, ok := key.(*ecdsaPrivateKey) if !ok { t.Fatalf("got %T, want *ecdsaPrivateKey", ecKey) } if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) { t.Fatalf("public key does not validate.") } } // ssh-keygen -t dsa -f /tmp/idsa.pem var dsaPEM = `-----BEGIN DSA PRIVATE KEY----- MIIBuwIBAAKBgQD6PDSEyXiI9jfNs97WuM46MSDCYlOqWw80ajN16AohtBncs1YB lHk//dQOvCYOsYaE+gNix2jtoRjwXhDsc25/IqQbU1ahb7mB8/rsaILRGIbA5WH3 EgFtJmXFovDz3if6F6TzvhFpHgJRmLYVR8cqsezL3hEZOvvs2iH7MorkxwIVAJHD nD82+lxh2fb4PMsIiaXudAsBAoGAQRf7Q/iaPRn43ZquUhd6WwvirqUj+tkIu6eV 2nZWYmXLlqFQKEy4Tejl7Wkyzr2OSYvbXLzo7TNxLKoWor6ips0phYPPMyXld14r juhT24CrhOzuLMhDduMDi032wDIZG4Y+K7ElU8Oufn8Sj5Wge8r6ANmmVgmFfynr FhdYCngCgYEA3ucGJ93/Mx4q4eKRDxcWD3QzWyqpbRVRRV1Vmih9Ha/qC994nJFz DQIdjxDIT2Rk2AGzMqFEB68Zc3O+Wcsmz5eWWzEwFxaTwOGWTyDqsDRLm3fD+QYj nOwuxb0Kce+gWI8voWcqC9cyRm09jGzu2Ab3Bhtpg8JJ8L7gS3MRZK4CFEx4UAfY Fmsr0W6fHB9nhS4/UXM8 -----END DSA PRIVATE KEY-----` func TestParseDSA(t *testing.T) { s, err := ParsePrivateKey([]byte(dsaPEM)) if err != nil { t.Fatalf("ParsePrivateKey returned error: %s", err) } data := []byte("sign me") sig, err := s.Sign(rand.Reader, data) if err != nil { t.Fatalf("dsa.Sign: %v", err) } if !s.PublicKey().Verify(data, sig) { t.Error("Verify failed.") } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/transport_test.go������������������������������0000644�0000153�0000161�00000003200�12321736016�025571� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "bytes" "strings" "testing" ) func TestReadVersion(t *testing.T) { longversion := strings.Repeat("SSH-2.0-bla", 50)[:253] cases := map[string]string{ "SSH-2.0-bla\r\n": "SSH-2.0-bla", "SSH-2.0-bla\n": "SSH-2.0-bla", longversion + "\r\n": longversion, } for in, want := range cases { result, err := readVersion(bytes.NewBufferString(in)) if err != nil { t.Errorf("readVersion(%q): %s", in, err) } got := string(result) if got != want { t.Errorf("got %q, want %q", got, want) } } } func TestReadVersionError(t *testing.T) { longversion := strings.Repeat("SSH-2.0-bla", 50)[:253] cases := []string{ longversion + "too-long\r\n", } for _, in := range cases { if _, err := readVersion(bytes.NewBufferString(in)); err == nil { t.Errorf("readVersion(%q) should have failed", in) } } } func TestExchangeVersionsBasic(t *testing.T) { v := "SSH-2.0-bla" buf := bytes.NewBufferString(v + "\r\n") them, err := exchangeVersions(buf, []byte("xyz")) if err != nil { t.Errorf("exchangeVersions: %v", err) } if want := "SSH-2.0-bla"; string(them) != want { t.Errorf("got %q want %q for our version", them, want) } } func TestExchangeVersions(t *testing.T) { cases := []string{ "not\x000allowed", "not allowed\n", } for _, c := range cases { buf := bytes.NewBufferString("SSH-2.0-bla\r\n") if _, err := exchangeVersions(buf, []byte(c)); err == nil { t.Errorf("exchangeVersions(%q): should have failed", c) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/buffer.go��������������������������������������0000644�0000153�0000161�00000004241�12321736016�023755� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "io" "sync" ) // buffer provides a linked list buffer for data exchange // between producer and consumer. Theoretically the buffer is // of unlimited capacity as it does no allocation of its own. type buffer struct { // protects concurrent access to head, tail and closed *sync.Cond head *element // the buffer that will be read first tail *element // the buffer that will be read last closed bool } // An element represents a single link in a linked list. type element struct { buf []byte next *element } // newBuffer returns an empty buffer that is not closed. func newBuffer() *buffer { e := new(element) b := &buffer{ Cond: newCond(), head: e, tail: e, } return b } // write makes buf available for Read to receive. // buf must not be modified after the call to write. func (b *buffer) write(buf []byte) { b.Cond.L.Lock() defer b.Cond.L.Unlock() e := &element{buf: buf} b.tail.next = e b.tail = e b.Cond.Signal() } // eof closes the buffer. Reads from the buffer once all // the data has been consumed will receive os.EOF. func (b *buffer) eof() error { b.Cond.L.Lock() defer b.Cond.L.Unlock() b.closed = true b.Cond.Signal() return nil } // Read reads data from the internal buffer in buf. // Reads will block if no data is available, or until // the buffer is closed. func (b *buffer) Read(buf []byte) (n int, err error) { b.Cond.L.Lock() defer b.Cond.L.Unlock() for len(buf) > 0 { // if there is data in b.head, copy it if len(b.head.buf) > 0 { r := copy(buf, b.head.buf) buf, b.head.buf = buf[r:], b.head.buf[r:] n += r continue } // if there is a next buffer, make it the head if len(b.head.buf) == 0 && b.head != b.tail { b.head = b.head.next continue } // if at least one byte has been copied, return if n > 0 { break } // if nothing was read, and there is nothing outstanding // check to see if the buffer is closed. if b.closed { err = io.EOF break } // out of buffers, wait for producer b.Cond.Wait() } return } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/session_test.go��������������������������������0000644�0000153�0000161�00000050757�12321736016�025243� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh // Session tests. import ( "bytes" crypto_rand "crypto/rand" "io" "io/ioutil" "math/rand" "net" "testing" "code.google.com/p/go.crypto/ssh/terminal" ) type serverType func(*serverChan, *testing.T) // dial constructs a new test server and returns a *ClientConn. func dial(handler serverType, t *testing.T) *ClientConn { l, err := Listen("tcp", "127.0.0.1:0", serverConfig) if err != nil { t.Fatalf("unable to listen: %v", err) } go func() { defer l.Close() conn, err := l.Accept() if err != nil { t.Errorf("Unable to accept: %v", err) return } defer conn.Close() if err := conn.Handshake(); err != nil { t.Errorf("Unable to handshake: %v", err) return } done := make(chan struct{}) for { ch, err := conn.Accept() if err == io.EOF || err == io.ErrUnexpectedEOF { return } // We sometimes get ECONNRESET rather than EOF. if _, ok := err.(*net.OpError); ok { return } if err != nil { t.Errorf("Unable to accept incoming channel request: %v", err) return } if ch.ChannelType() != "session" { ch.Reject(UnknownChannelType, "unknown channel type") continue } ch.Accept() go func() { defer close(done) handler(ch.(*serverChan), t) }() } <-done }() config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthPassword(clientPassword), }, } c, err := Dial("tcp", l.Addr().String(), config) if err != nil { t.Fatalf("unable to dial remote side: %v", err) } return c } // Test a simple string is returned to session.Stdout. func TestSessionShell(t *testing.T) { conn := dial(shellHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() stdout := new(bytes.Buffer) session.Stdout = stdout if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %s", err) } if err := session.Wait(); err != nil { t.Fatalf("Remote command did not exit cleanly: %v", err) } actual := stdout.String() if actual != "golang" { t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) } } // TODO(dfc) add support for Std{in,err}Pipe when the Server supports it. // Test a simple string is returned via StdoutPipe. func TestSessionStdoutPipe(t *testing.T) { conn := dial(shellHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() stdout, err := session.StdoutPipe() if err != nil { t.Fatalf("Unable to request StdoutPipe(): %v", err) } var buf bytes.Buffer if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } done := make(chan bool, 1) go func() { if _, err := io.Copy(&buf, stdout); err != nil { t.Errorf("Copy of stdout failed: %v", err) } done <- true }() if err := session.Wait(); err != nil { t.Fatalf("Remote command did not exit cleanly: %v", err) } <-done actual := buf.String() if actual != "golang" { t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) } } // Test that a simple string is returned via the Output helper, // and that stderr is discarded. func TestSessionOutput(t *testing.T) { conn := dial(fixedOutputHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() buf, err := session.Output("") // cmd is ignored by fixedOutputHandler if err != nil { t.Error("Remote command did not exit cleanly:", err) } w := "this-is-stdout." g := string(buf) if g != w { t.Error("Remote command did not return expected string:") t.Logf("want %q", w) t.Logf("got %q", g) } } // Test that both stdout and stderr are returned // via the CombinedOutput helper. func TestSessionCombinedOutput(t *testing.T) { conn := dial(fixedOutputHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() buf, err := session.CombinedOutput("") // cmd is ignored by fixedOutputHandler if err != nil { t.Error("Remote command did not exit cleanly:", err) } const stdout = "this-is-stdout." const stderr = "this-is-stderr." g := string(buf) if g != stdout+stderr && g != stderr+stdout { t.Error("Remote command did not return expected string:") t.Logf("want %q, or %q", stdout+stderr, stderr+stdout) t.Logf("got %q", g) } } // Test non-0 exit status is returned correctly. func TestExitStatusNonZero(t *testing.T) { conn := dial(exitStatusNonZeroHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } err = session.Wait() if err == nil { t.Fatalf("expected command to fail but it didn't") } e, ok := err.(*ExitError) if !ok { t.Fatalf("expected *ExitError but got %T", err) } if e.ExitStatus() != 15 { t.Fatalf("expected command to exit with 15 but got %v", e.ExitStatus()) } } // Test 0 exit status is returned correctly. func TestExitStatusZero(t *testing.T) { conn := dial(exitStatusZeroHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } err = session.Wait() if err != nil { t.Fatalf("expected nil but got %v", err) } } // Test exit signal and status are both returned correctly. func TestExitSignalAndStatus(t *testing.T) { conn := dial(exitSignalAndStatusHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } err = session.Wait() if err == nil { t.Fatalf("expected command to fail but it didn't") } e, ok := err.(*ExitError) if !ok { t.Fatalf("expected *ExitError but got %T", err) } if e.Signal() != "TERM" || e.ExitStatus() != 15 { t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus()) } } // Test exit signal and status are both returned correctly. func TestKnownExitSignalOnly(t *testing.T) { conn := dial(exitSignalHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } err = session.Wait() if err == nil { t.Fatalf("expected command to fail but it didn't") } e, ok := err.(*ExitError) if !ok { t.Fatalf("expected *ExitError but got %T", err) } if e.Signal() != "TERM" || e.ExitStatus() != 143 { t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus()) } } // Test exit signal and status are both returned correctly. func TestUnknownExitSignal(t *testing.T) { conn := dial(exitSignalUnknownHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } err = session.Wait() if err == nil { t.Fatalf("expected command to fail but it didn't") } e, ok := err.(*ExitError) if !ok { t.Fatalf("expected *ExitError but got %T", err) } if e.Signal() != "SYS" || e.ExitStatus() != 128 { t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus()) } } // Test WaitMsg is not returned if the channel closes abruptly. func TestExitWithoutStatusOrSignal(t *testing.T) { conn := dial(exitWithoutSignalOrStatus, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } err = session.Wait() if err == nil { t.Fatalf("expected command to fail but it didn't") } _, ok := err.(*ExitError) if ok { // you can't actually test for errors.errorString // because it's not exported. t.Fatalf("expected *errorString but got %T", err) } } func TestInvalidServerMessage(t *testing.T) { conn := dial(sendInvalidRecord, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } // Make sure that we closed all the clientChans when the connection // failed. session.wait() defer session.Close() } // In the wild some clients (and servers) send zero sized window updates. // Test that the client can continue after receiving a zero sized update. func TestClientZeroWindowAdjust(t *testing.T) { conn := dial(sendZeroWindowAdjust, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } err = session.Wait() if err != nil { t.Fatalf("expected nil but got %v", err) } } // In the wild some clients (and servers) send zero sized window updates. // Test that the server can continue after receiving a zero size update. func TestServerZeroWindowAdjust(t *testing.T) { conn := dial(exitStatusZeroHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } // send a bogus zero sized window update session.clientChan.sendWindowAdj(0) err = session.Wait() if err != nil { t.Fatalf("expected nil but got %v", err) } } // Verify that the client never sends a packet larger than maxpacket. func TestClientStdinRespectsMaxPacketSize(t *testing.T) { conn := dial(discardHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("failed to request new session: %v", err) } defer session.Close() stdin, err := session.StdinPipe() if err != nil { t.Fatalf("failed to obtain stdinpipe: %v", err) } const size = 100 * 1000 for i := 0; i < 10; i++ { n, err := stdin.Write(make([]byte, size)) if n != size || err != nil { t.Fatalf("failed to write: %d, %v", n, err) } } } // Verify that the client never accepts a packet larger than maxpacket. func TestServerStdoutRespectsMaxPacketSize(t *testing.T) { conn := dial(largeSendHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() out, err := session.StdoutPipe() if err != nil { t.Fatalf("Unable to connect to Stdout: %v", err) } if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } if _, err := ioutil.ReadAll(out); err != nil { t.Fatalf("failed to read: %v", err) } } func TestClientCannotSendAfterEOF(t *testing.T) { conn := dial(exitWithoutSignalOrStatus, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() in, err := session.StdinPipe() if err != nil { t.Fatalf("Unable to connect channel stdin: %v", err) } if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } if err := in.Close(); err != nil { t.Fatalf("Unable to close stdin: %v", err) } if _, err := in.Write([]byte("foo")); err == nil { t.Fatalf("Session write should fail") } } func TestClientCannotSendAfterClose(t *testing.T) { conn := dial(exitWithoutSignalOrStatus, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatalf("Unable to request new session: %v", err) } defer session.Close() in, err := session.StdinPipe() if err != nil { t.Fatalf("Unable to connect channel stdin: %v", err) } if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } // close underlying channel if err := session.channel.Close(); err != nil { t.Fatalf("Unable to close session: %v", err) } if _, err := in.Write([]byte("foo")); err == nil { t.Fatalf("Session write should fail") } } func TestClientCannotSendHugePacket(t *testing.T) { // client and server use the same transport write code so this // test suffices for both. conn := dial(shellHandler, t) defer conn.Close() if err := conn.transport.writePacket(make([]byte, maxPacket*2)); err == nil { t.Fatalf("huge packet write should fail") } } // windowTestBytes is the number of bytes that we'll send to the SSH server. const windowTestBytes = 16000 * 200 // TestServerWindow writes random data to the server. The server is expected to echo // the same data back, which is compared against the original. func TestServerWindow(t *testing.T) { origBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes)) io.CopyN(origBuf, crypto_rand.Reader, windowTestBytes) origBytes := origBuf.Bytes() conn := dial(echoHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatal(err) } defer session.Close() result := make(chan []byte) go func() { defer close(result) echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes)) serverStdout, err := session.StdoutPipe() if err != nil { t.Errorf("StdoutPipe failed: %v", err) return } n, err := copyNRandomly("stdout", echoedBuf, serverStdout, windowTestBytes) if err != nil && err != io.EOF { t.Errorf("Read only %d bytes from server, expected %d: %v", n, windowTestBytes, err) } result <- echoedBuf.Bytes() }() serverStdin, err := session.StdinPipe() if err != nil { t.Fatalf("StdinPipe failed: %v", err) } written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes) if err != nil { t.Fatalf("failed to copy origBuf to serverStdin: %v", err) } if written != windowTestBytes { t.Fatalf("Wrote only %d of %d bytes to server", written, windowTestBytes) } echoedBytes := <-result if !bytes.Equal(origBytes, echoedBytes) { t.Fatalf("Echoed buffer differed from original, orig %d, echoed %d", len(origBytes), len(echoedBytes)) } } // Verify the client can handle a keepalive packet from the server. func TestClientHandlesKeepalives(t *testing.T) { conn := dial(channelKeepaliveSender, t) defer conn.Close() session, err := conn.NewSession() if err != nil { t.Fatal(err) } defer session.Close() if err := session.Shell(); err != nil { t.Fatalf("Unable to execute command: %v", err) } err = session.Wait() if err != nil { t.Fatalf("expected nil but got: %v", err) } } type exitStatusMsg struct { PeersId uint32 Request string WantReply bool Status uint32 } type exitSignalMsg struct { PeersId uint32 Request string WantReply bool Signal string CoreDumped bool Errmsg string Lang string } func newServerShell(ch *serverChan, prompt string) *ServerTerminal { term := terminal.NewTerminal(ch, prompt) return &ServerTerminal{ Term: term, Channel: ch, } } func exitStatusZeroHandler(ch *serverChan, t *testing.T) { defer ch.Close() // this string is returned to stdout shell := newServerShell(ch, "> ") readLine(shell, t) sendStatus(0, ch, t) } func exitStatusNonZeroHandler(ch *serverChan, t *testing.T) { defer ch.Close() shell := newServerShell(ch, "> ") readLine(shell, t) sendStatus(15, ch, t) } func exitSignalAndStatusHandler(ch *serverChan, t *testing.T) { defer ch.Close() shell := newServerShell(ch, "> ") readLine(shell, t) sendStatus(15, ch, t) sendSignal("TERM", ch, t) } func exitSignalHandler(ch *serverChan, t *testing.T) { defer ch.Close() shell := newServerShell(ch, "> ") readLine(shell, t) sendSignal("TERM", ch, t) } func exitSignalUnknownHandler(ch *serverChan, t *testing.T) { defer ch.Close() shell := newServerShell(ch, "> ") readLine(shell, t) sendSignal("SYS", ch, t) } func exitWithoutSignalOrStatus(ch *serverChan, t *testing.T) { defer ch.Close() shell := newServerShell(ch, "> ") readLine(shell, t) } func shellHandler(ch *serverChan, t *testing.T) { defer ch.Close() // this string is returned to stdout shell := newServerShell(ch, "golang") readLine(shell, t) sendStatus(0, ch, t) } // Ignores the command, writes fixed strings to stderr and stdout. // Strings are "this-is-stdout." and "this-is-stderr.". func fixedOutputHandler(ch *serverChan, t *testing.T) { defer ch.Close() _, err := ch.Read(make([]byte, 0)) if _, ok := err.(ChannelRequest); !ok { t.Fatalf("error: expected channel request, got: %#v", err) return } // ignore request, always send some text ch.AckRequest(true) _, err = io.WriteString(ch, "this-is-stdout.") if err != nil { t.Fatalf("error writing on server: %v", err) } _, err = io.WriteString(ch.Stderr(), "this-is-stderr.") if err != nil { t.Fatalf("error writing on server: %v", err) } sendStatus(0, ch, t) } func readLine(shell *ServerTerminal, t *testing.T) { if _, err := shell.ReadLine(); err != nil && err != io.EOF { t.Errorf("unable to read line: %v", err) } } func sendStatus(status uint32, ch *serverChan, t *testing.T) { msg := exitStatusMsg{ PeersId: ch.remoteId, Request: "exit-status", WantReply: false, Status: status, } if err := ch.writePacket(marshal(msgChannelRequest, msg)); err != nil { t.Errorf("unable to send status: %v", err) } } func sendSignal(signal string, ch *serverChan, t *testing.T) { sig := exitSignalMsg{ PeersId: ch.remoteId, Request: "exit-signal", WantReply: false, Signal: signal, CoreDumped: false, Errmsg: "Process terminated", Lang: "en-GB-oed", } if err := ch.writePacket(marshal(msgChannelRequest, sig)); err != nil { t.Errorf("unable to send signal: %v", err) } } func sendInvalidRecord(ch *serverChan, t *testing.T) { defer ch.Close() packet := make([]byte, 1+4+4+1) packet[0] = msgChannelData marshalUint32(packet[1:], 29348723 /* invalid channel id */) marshalUint32(packet[5:], 1) packet[9] = 42 if err := ch.writePacket(packet); err != nil { t.Errorf("unable send invalid record: %v", err) } } func sendZeroWindowAdjust(ch *serverChan, t *testing.T) { defer ch.Close() // send a bogus zero sized window update ch.sendWindowAdj(0) shell := newServerShell(ch, "> ") readLine(shell, t) sendStatus(0, ch, t) } func discardHandler(ch *serverChan, t *testing.T) { defer ch.Close() // grow the window to avoid being fooled by // the initial 1 << 14 window. ch.sendWindowAdj(1024 * 1024) io.Copy(ioutil.Discard, ch) } func largeSendHandler(ch *serverChan, t *testing.T) { defer ch.Close() // grow the window to avoid being fooled by // the initial 1 << 14 window. ch.sendWindowAdj(1024 * 1024) shell := newServerShell(ch, "> ") readLine(shell, t) // try to send more than the 32k window // will allow if err := ch.writePacket(make([]byte, 128*1024)); err == nil { t.Errorf("wrote packet larger than 32k") } } func echoHandler(ch *serverChan, t *testing.T) { defer ch.Close() if n, err := copyNRandomly("echohandler", ch, ch, windowTestBytes); err != nil { t.Errorf("short write, wrote %d, expected %d: %v ", n, windowTestBytes, err) } } // copyNRandomly copies n bytes from src to dst. It uses a variable, and random, // buffer size to exercise more code paths. func copyNRandomly(title string, dst io.Writer, src io.Reader, n int) (int, error) { var ( buf = make([]byte, 32*1024) written int remaining = n ) for remaining > 0 { l := rand.Intn(1 << 15) if remaining < l { l = remaining } nr, er := src.Read(buf[:l]) nw, ew := dst.Write(buf[:nr]) remaining -= nw written += nw if ew != nil { return written, ew } if nr != nw { return written, io.ErrShortWrite } if er != nil && er != io.EOF { return written, er } } return written, nil } func channelKeepaliveSender(ch *serverChan, t *testing.T) { defer ch.Close() shell := newServerShell(ch, "> ") readLine(shell, t) msg := channelRequestMsg{ PeersId: ch.remoteId, Request: "keepalive@openssh.com", WantReply: true, } if err := ch.writePacket(marshal(msgChannelRequest, msg)); err != nil { t.Errorf("unable to send channel keepalive request: %v", err) } sendStatus(0, ch, t) } �����������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/keys.go����������������������������������������0000644�0000153�0000161�00000034072�12321736016�023464� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "bytes" "crypto" "crypto/dsa" "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" "crypto/x509" "encoding/asn1" "encoding/base64" "encoding/pem" "errors" "fmt" "io" "math/big" ) // These constants represent the algorithm names for key types supported by this // package. const ( KeyAlgoRSA = "ssh-rsa" KeyAlgoDSA = "ssh-dss" KeyAlgoECDSA256 = "ecdsa-sha2-nistp256" KeyAlgoECDSA384 = "ecdsa-sha2-nistp384" KeyAlgoECDSA521 = "ecdsa-sha2-nistp521" ) // parsePubKey parses a public key of the given algorithm. // Use ParsePublicKey for keys with prepended algorithm. func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, ok bool) { switch algo { case KeyAlgoRSA: return parseRSA(in) case KeyAlgoDSA: return parseDSA(in) case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: return parseECDSA(in) case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: return parseOpenSSHCertV01(in, algo) } return nil, nil, false } // parseAuthorizedKey parses a public key in OpenSSH authorized_keys format // (see sshd(8) manual page) once the options and key type fields have been // removed. func parseAuthorizedKey(in []byte) (out PublicKey, comment string, ok bool) { in = bytes.TrimSpace(in) i := bytes.IndexAny(in, " \t") if i == -1 { i = len(in) } base64Key := in[:i] key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key))) n, err := base64.StdEncoding.Decode(key, base64Key) if err != nil { return } key = key[:n] out, _, ok = ParsePublicKey(key) if !ok { return nil, "", false } comment = string(bytes.TrimSpace(in[i:])) return } // ParseAuthorizedKeys parses a public key from an authorized_keys // file used in OpenSSH according to the sshd(8) manual page. func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, ok bool) { for len(in) > 0 { end := bytes.IndexByte(in, '\n') if end != -1 { rest = in[end+1:] in = in[:end] } else { rest = nil } end = bytes.IndexByte(in, '\r') if end != -1 { in = in[:end] } in = bytes.TrimSpace(in) if len(in) == 0 || in[0] == '#' { in = rest continue } i := bytes.IndexAny(in, " \t") if i == -1 { in = rest continue } if out, comment, ok = parseAuthorizedKey(in[i:]); ok { return } // No key type recognised. Maybe there's an options field at // the beginning. var b byte inQuote := false var candidateOptions []string optionStart := 0 for i, b = range in { isEnd := !inQuote && (b == ' ' || b == '\t') if (b == ',' && !inQuote) || isEnd { if i-optionStart > 0 { candidateOptions = append(candidateOptions, string(in[optionStart:i])) } optionStart = i + 1 } if isEnd { break } if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) { inQuote = !inQuote } } for i < len(in) && (in[i] == ' ' || in[i] == '\t') { i++ } if i == len(in) { // Invalid line: unmatched quote in = rest continue } in = in[i:] i = bytes.IndexAny(in, " \t") if i == -1 { in = rest continue } if out, comment, ok = parseAuthorizedKey(in[i:]); ok { options = candidateOptions return } in = rest continue } return } // ParsePublicKey parses an SSH public key formatted for use in // the SSH wire protocol according to RFC 4253, section 6.6. func ParsePublicKey(in []byte) (out PublicKey, rest []byte, ok bool) { algo, in, ok := parseString(in) if !ok { return } return parsePubKey(in, string(algo)) } // MarshalAuthorizedKey returns a byte stream suitable for inclusion // in an OpenSSH authorized_keys file following the format specified // in the sshd(8) manual page. func MarshalAuthorizedKey(key PublicKey) []byte { b := &bytes.Buffer{} b.WriteString(key.PublicKeyAlgo()) b.WriteByte(' ') e := base64.NewEncoder(base64.StdEncoding, b) e.Write(MarshalPublicKey(key)) e.Close() b.WriteByte('\n') return b.Bytes() } // PublicKey is an abstraction of different types of public keys. type PublicKey interface { // PrivateKeyAlgo returns the name of the encryption system. PrivateKeyAlgo() string // PublicKeyAlgo returns the algorithm for the public key, // which may be different from PrivateKeyAlgo for certificates. PublicKeyAlgo() string // Marshal returns the serialized key data in SSH wire format, // without the name prefix. Callers should typically use // MarshalPublicKey(). Marshal() []byte // Verify that sig is a signature on the given data using this // key. This function will hash the data appropriately first. Verify(data []byte, sigBlob []byte) bool } // A Signer is can create signatures that verify against a public key. type Signer interface { // PublicKey returns an associated PublicKey instance. PublicKey() PublicKey // Sign returns raw signature for the given data. This method // will apply the hash specified for the keytype to the data. Sign(rand io.Reader, data []byte) ([]byte, error) } type rsaPublicKey rsa.PublicKey func (r *rsaPublicKey) PrivateKeyAlgo() string { return "ssh-rsa" } func (r *rsaPublicKey) PublicKeyAlgo() string { return r.PrivateKeyAlgo() } // parseRSA parses an RSA key according to RFC 4253, section 6.6. func parseRSA(in []byte) (out PublicKey, rest []byte, ok bool) { key := new(rsa.PublicKey) bigE, in, ok := parseInt(in) if !ok || bigE.BitLen() > 24 { return } e := bigE.Int64() if e < 3 || e&1 == 0 { ok = false return } key.E = int(e) if key.N, in, ok = parseInt(in); !ok { return } ok = true return (*rsaPublicKey)(key), in, ok } func (r *rsaPublicKey) Marshal() []byte { // See RFC 4253, section 6.6. e := new(big.Int).SetInt64(int64(r.E)) length := intLength(e) length += intLength(r.N) ret := make([]byte, length) rest := marshalInt(ret, e) marshalInt(rest, r.N) return ret } func (r *rsaPublicKey) Verify(data []byte, sig []byte) bool { h := crypto.SHA1.New() h.Write(data) digest := h.Sum(nil) return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig) == nil } type rsaPrivateKey struct { *rsa.PrivateKey } func (r *rsaPrivateKey) PublicKey() PublicKey { return (*rsaPublicKey)(&r.PrivateKey.PublicKey) } func (r *rsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) { h := crypto.SHA1.New() h.Write(data) digest := h.Sum(nil) return rsa.SignPKCS1v15(rand, r.PrivateKey, crypto.SHA1, digest) } type dsaPublicKey dsa.PublicKey func (r *dsaPublicKey) PrivateKeyAlgo() string { return "ssh-dss" } func (r *dsaPublicKey) PublicKeyAlgo() string { return r.PrivateKeyAlgo() } // parseDSA parses an DSA key according to RFC 4253, section 6.6. func parseDSA(in []byte) (out PublicKey, rest []byte, ok bool) { key := new(dsa.PublicKey) if key.P, in, ok = parseInt(in); !ok { return } if key.Q, in, ok = parseInt(in); !ok { return } if key.G, in, ok = parseInt(in); !ok { return } if key.Y, in, ok = parseInt(in); !ok { return } ok = true return (*dsaPublicKey)(key), in, ok } func (r *dsaPublicKey) Marshal() []byte { // See RFC 4253, section 6.6. length := intLength(r.P) length += intLength(r.Q) length += intLength(r.G) length += intLength(r.Y) ret := make([]byte, length) rest := marshalInt(ret, r.P) rest = marshalInt(rest, r.Q) rest = marshalInt(rest, r.G) marshalInt(rest, r.Y) return ret } func (k *dsaPublicKey) Verify(data []byte, sigBlob []byte) bool { h := crypto.SHA1.New() h.Write(data) digest := h.Sum(nil) // Per RFC 4253, section 6.6, // The value for 'dss_signature_blob' is encoded as a string containing // r, followed by s (which are 160-bit integers, without lengths or // padding, unsigned, and in network byte order). // For DSS purposes, sig.Blob should be exactly 40 bytes in length. if len(sigBlob) != 40 { return false } r := new(big.Int).SetBytes(sigBlob[:20]) s := new(big.Int).SetBytes(sigBlob[20:]) return dsa.Verify((*dsa.PublicKey)(k), digest, r, s) } type dsaPrivateKey struct { *dsa.PrivateKey } func (k *dsaPrivateKey) PublicKey() PublicKey { return (*dsaPublicKey)(&k.PrivateKey.PublicKey) } func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) { h := crypto.SHA1.New() h.Write(data) digest := h.Sum(nil) r, s, err := dsa.Sign(rand, k.PrivateKey, digest) if err != nil { return nil, err } sig := make([]byte, 40) copy(sig[:20], r.Bytes()) copy(sig[20:], s.Bytes()) return sig, nil } type ecdsaPublicKey ecdsa.PublicKey func (key *ecdsaPublicKey) PrivateKeyAlgo() string { return "ecdsa-sha2-" + key.nistID() } func (key *ecdsaPublicKey) nistID() string { switch key.Params().BitSize { case 256: return "nistp256" case 384: return "nistp384" case 521: return "nistp521" } panic("ssh: unsupported ecdsa key size") } func supportedEllipticCurve(curve elliptic.Curve) bool { return (curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()) } // ecHash returns the hash to match the given elliptic curve, see RFC // 5656, section 6.2.1 func ecHash(curve elliptic.Curve) crypto.Hash { bitSize := curve.Params().BitSize switch { case bitSize <= 256: return crypto.SHA256 case bitSize <= 384: return crypto.SHA384 } return crypto.SHA512 } func (key *ecdsaPublicKey) PublicKeyAlgo() string { return key.PrivateKeyAlgo() } // parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. func parseECDSA(in []byte) (out PublicKey, rest []byte, ok bool) { var identifier []byte if identifier, in, ok = parseString(in); !ok { return } key := new(ecdsa.PublicKey) switch string(identifier) { case "nistp256": key.Curve = elliptic.P256() case "nistp384": key.Curve = elliptic.P384() case "nistp521": key.Curve = elliptic.P521() default: ok = false return } var keyBytes []byte if keyBytes, in, ok = parseString(in); !ok { return } key.X, key.Y = elliptic.Unmarshal(key.Curve, keyBytes) if key.X == nil || key.Y == nil { ok = false return } return (*ecdsaPublicKey)(key), in, ok } func (key *ecdsaPublicKey) Marshal() []byte { // See RFC 5656, section 3.1. keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y) ID := key.nistID() length := stringLength(len(ID)) length += stringLength(len(keyBytes)) ret := make([]byte, length) r := marshalString(ret, []byte(ID)) r = marshalString(r, keyBytes) return ret } func (key *ecdsaPublicKey) Verify(data []byte, sigBlob []byte) bool { h := ecHash(key.Curve).New() h.Write(data) digest := h.Sum(nil) // Per RFC 5656, section 3.1.2, // The ecdsa_signature_blob value has the following specific encoding: // mpint r // mpint s r, rest, ok := parseInt(sigBlob) if !ok { return false } s, rest, ok := parseInt(rest) if !ok || len(rest) > 0 { return false } return ecdsa.Verify((*ecdsa.PublicKey)(key), digest, r, s) } type ecdsaPrivateKey struct { *ecdsa.PrivateKey } func (k *ecdsaPrivateKey) PublicKey() PublicKey { return (*ecdsaPublicKey)(&k.PrivateKey.PublicKey) } func (k *ecdsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) { h := ecHash(k.PrivateKey.PublicKey.Curve).New() h.Write(data) digest := h.Sum(nil) r, s, err := ecdsa.Sign(rand, k.PrivateKey, digest) if err != nil { return nil, err } sig := make([]byte, intLength(r)+intLength(s)) rest := marshalInt(sig, r) marshalInt(rest, s) return sig, nil } // NewPrivateKey takes a pointer to rsa, dsa or ecdsa PrivateKey // returns a corresponding Signer instance. EC keys should use P256, // P384 or P521. func NewSignerFromKey(k interface{}) (Signer, error) { var sshKey Signer switch t := k.(type) { case *rsa.PrivateKey: sshKey = &rsaPrivateKey{t} case *dsa.PrivateKey: sshKey = &dsaPrivateKey{t} case *ecdsa.PrivateKey: if !supportedEllipticCurve(t.Curve) { return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.") } sshKey = &ecdsaPrivateKey{t} default: return nil, fmt.Errorf("ssh: unsupported key type %T", k) } return sshKey, nil } // NewPublicKey takes a pointer to rsa, dsa or ecdsa PublicKey // and returns a corresponding ssh PublicKey instance. EC keys should use P256, P384 or P521. func NewPublicKey(k interface{}) (PublicKey, error) { var sshKey PublicKey switch t := k.(type) { case *rsa.PublicKey: sshKey = (*rsaPublicKey)(t) case *ecdsa.PublicKey: if !supportedEllipticCurve(t.Curve) { return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.") } sshKey = (*ecdsaPublicKey)(t) case *dsa.PublicKey: sshKey = (*dsaPublicKey)(t) default: return nil, fmt.Errorf("ssh: unsupported key type %T", k) } return sshKey, nil } // ParsePublicKey parses a PEM encoded private key. It supports // PKCS#1, RSA, DSA and ECDSA private keys. func ParsePrivateKey(pemBytes []byte) (Signer, error) { block, _ := pem.Decode(pemBytes) if block == nil { return nil, errors.New("ssh: no key found") } var rawkey interface{} switch block.Type { case "RSA PRIVATE KEY": rsa, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return nil, err } rawkey = rsa case "EC PRIVATE KEY": ec, err := x509.ParseECPrivateKey(block.Bytes) if err != nil { return nil, err } rawkey = ec case "DSA PRIVATE KEY": ec, err := parseDSAPrivate(block.Bytes) if err != nil { return nil, err } rawkey = ec default: return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) } return NewSignerFromKey(rawkey) } // parseDSAPrivate parses a DSA key in ASN.1 DER encoding, as // documented in the OpenSSL DSA manpage. // TODO(hanwen): move this in to crypto/x509 after the Go 1.2 freeze. func parseDSAPrivate(p []byte) (*dsa.PrivateKey, error) { k := struct { Version int P *big.Int Q *big.Int G *big.Int Priv *big.Int Pub *big.Int }{} rest, err := asn1.Unmarshal(p, &k) if err != nil { return nil, errors.New("ssh: failed to parse DSA key: " + err.Error()) } if len(rest) > 0 { return nil, errors.New("ssh: garbage after DSA key") } return &dsa.PrivateKey{ PublicKey: dsa.PublicKey{ Parameters: dsa.Parameters{ P: k.P, Q: k.Q, G: k.G, }, Y: k.Priv, }, X: k.Pub, }, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/client_auth.go���������������������������������0000644�0000153�0000161�00000032726�12321736016�025014� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "errors" "fmt" "io" "net" ) // authenticate authenticates with the remote server. See RFC 4252. func (c *ClientConn) authenticate() error { // initiate user auth session if err := c.transport.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil { return err } packet, err := c.transport.readPacket() if err != nil { return err } var serviceAccept serviceAcceptMsg if err := unmarshal(&serviceAccept, packet, msgServiceAccept); err != nil { return err } // during the authentication phase the client first attempts the "none" method // then any untried methods suggested by the server. tried, remain := make(map[string]bool), make(map[string]bool) for auth := ClientAuth(new(noneAuth)); auth != nil; { ok, methods, err := auth.auth(c.transport.sessionID, c.config.User, c.transport, c.config.rand()) if err != nil { return err } if ok { // success return nil } tried[auth.method()] = true delete(remain, auth.method()) for _, meth := range methods { if tried[meth] { // if we've tried meth already, skip it. continue } remain[meth] = true } auth = nil for _, a := range c.config.Auth { if remain[a.method()] { auth = a break } } } return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried)) } func keys(m map[string]bool) (s []string) { for k := range m { s = append(s, k) } return } // HostKeyChecker represents a database of known server host keys. type HostKeyChecker interface { // Check is called during the handshake to check server's // public key for unexpected changes. The hostKey argument is // in SSH wire format. It can be parsed using // ssh.ParsePublicKey. The address before DNS resolution is // passed in the addr argument, so the key can also be checked // against the hostname. Check(addr string, remote net.Addr, algorithm string, hostKey []byte) error } // A ClientAuth represents an instance of an RFC 4252 authentication method. type ClientAuth interface { // auth authenticates user over transport t. // Returns true if authentication is successful. // If authentication is not successful, a []string of alternative // method names is returned. auth(session []byte, user string, p packetConn, rand io.Reader) (bool, []string, error) // method returns the RFC 4252 method name. method() string } // "none" authentication, RFC 4252 section 5.2. type noneAuth int func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { if err := c.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{ User: user, Service: serviceSSH, Method: "none", })); err != nil { return false, nil, err } return handleAuthResponse(c) } func (n *noneAuth) method() string { return "none" } // "password" authentication, RFC 4252 Section 8. type passwordAuth struct { ClientPassword } func (p *passwordAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { type passwordAuthMsg struct { User string Service string Method string Reply bool Password string } pw, err := p.Password(user) if err != nil { return false, nil, err } if err := c.writePacket(marshal(msgUserAuthRequest, passwordAuthMsg{ User: user, Service: serviceSSH, Method: "password", Reply: false, Password: pw, })); err != nil { return false, nil, err } return handleAuthResponse(c) } func (p *passwordAuth) method() string { return "password" } // A ClientPassword implements access to a client's passwords. type ClientPassword interface { // Password returns the password to use for user. Password(user string) (password string, err error) } // ClientAuthPassword returns a ClientAuth using password authentication. func ClientAuthPassword(impl ClientPassword) ClientAuth { return &passwordAuth{impl} } // ClientKeyring implements access to a client key ring. type ClientKeyring interface { // Key returns the i'th Publickey, or nil if no key exists at i. Key(i int) (key PublicKey, err error) // Sign returns a signature of the given data using the i'th key // and the supplied random source. Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) } // "publickey" authentication, RFC 4252 Section 7. type publickeyAuth struct { ClientKeyring } type publickeyAuthMsg struct { User string Service string Method string // HasSig indicates to the receiver packet that the auth request is signed and // should be used for authentication of the request. HasSig bool Algoname string Pubkey string // Sig is defined as []byte so marshal will exclude it during validateKey Sig []byte `ssh:"rest"` } func (p *publickeyAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { // Authentication is performed in two stages. The first stage sends an // enquiry to test if each key is acceptable to the remote. The second // stage attempts to authenticate with the valid keys obtained in the // first stage. var index int // a map of public keys to their index in the keyring validKeys := make(map[int]PublicKey) for { key, err := p.Key(index) if err != nil { return false, nil, err } if key == nil { // no more keys in the keyring break } if ok, err := p.validateKey(key, user, c); ok { validKeys[index] = key } else { if err != nil { return false, nil, err } } index++ } // methods that may continue if this auth is not successful. var methods []string for i, key := range validKeys { pubkey := MarshalPublicKey(key) algoname := key.PublicKeyAlgo() data := buildDataSignedForAuth(session, userAuthRequestMsg{ User: user, Service: serviceSSH, Method: p.method(), }, []byte(algoname), pubkey) sigBlob, err := p.Sign(i, rand, data) if err != nil { return false, nil, err } // manually wrap the serialized signature in a string s := serializeSignature(key.PublicKeyAlgo(), sigBlob) sig := make([]byte, stringLength(len(s))) marshalString(sig, s) msg := publickeyAuthMsg{ User: user, Service: serviceSSH, Method: p.method(), HasSig: true, Algoname: algoname, Pubkey: string(pubkey), Sig: sig, } p := marshal(msgUserAuthRequest, msg) if err := c.writePacket(p); err != nil { return false, nil, err } success, methods, err := handleAuthResponse(c) if err != nil { return false, nil, err } if success { return success, methods, err } } return false, methods, nil } // validateKey validates the key provided it is acceptable to the server. func (p *publickeyAuth) validateKey(key PublicKey, user string, c packetConn) (bool, error) { pubkey := MarshalPublicKey(key) algoname := key.PublicKeyAlgo() msg := publickeyAuthMsg{ User: user, Service: serviceSSH, Method: p.method(), HasSig: false, Algoname: algoname, Pubkey: string(pubkey), } if err := c.writePacket(marshal(msgUserAuthRequest, msg)); err != nil { return false, err } return p.confirmKeyAck(key, c) } func (p *publickeyAuth) confirmKeyAck(key PublicKey, c packetConn) (bool, error) { pubkey := MarshalPublicKey(key) algoname := key.PublicKeyAlgo() for { packet, err := c.readPacket() if err != nil { return false, err } switch packet[0] { case msgUserAuthBanner: // TODO(gpaul): add callback to present the banner to the user case msgUserAuthPubKeyOk: msg := userAuthPubKeyOkMsg{} if err := unmarshal(&msg, packet, msgUserAuthPubKeyOk); err != nil { return false, err } if msg.Algo != algoname || msg.PubKey != string(pubkey) { return false, nil } return true, nil case msgUserAuthFailure: return false, nil default: return false, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} } } panic("unreachable") } func (p *publickeyAuth) method() string { return "publickey" } // ClientAuthKeyring returns a ClientAuth using public key authentication. func ClientAuthKeyring(impl ClientKeyring) ClientAuth { return &publickeyAuth{impl} } // handleAuthResponse returns whether the preceding authentication request succeeded // along with a list of remaining authentication methods to try next and // an error if an unexpected response was received. func handleAuthResponse(c packetConn) (bool, []string, error) { for { packet, err := c.readPacket() if err != nil { return false, nil, err } switch packet[0] { case msgUserAuthBanner: // TODO: add callback to present the banner to the user case msgUserAuthFailure: msg := userAuthFailureMsg{} if err := unmarshal(&msg, packet, msgUserAuthFailure); err != nil { return false, nil, err } return false, msg.Methods, nil case msgUserAuthSuccess: return true, nil, nil case msgDisconnect: return false, nil, io.EOF default: return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} } } panic("unreachable") } // ClientAuthAgent returns a ClientAuth using public key authentication via // an agent. func ClientAuthAgent(agent *AgentClient) ClientAuth { return ClientAuthKeyring(&agentKeyring{agent: agent}) } // agentKeyring implements ClientKeyring. type agentKeyring struct { agent *AgentClient keys []*AgentKey } func (kr *agentKeyring) Key(i int) (key PublicKey, err error) { if kr.keys == nil { if kr.keys, err = kr.agent.RequestIdentities(); err != nil { return } } if i >= len(kr.keys) { return } return kr.keys[i].Key() } func (kr *agentKeyring) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) { var key PublicKey if key, err = kr.Key(i); err != nil { return } if key == nil { return nil, errors.New("ssh: key index out of range") } if sig, err = kr.agent.SignRequest(key, data); err != nil { return } // Unmarshal the signature. var ok bool if _, sig, ok = parseString(sig); !ok { return nil, errors.New("ssh: malformed signature response from agent") } if sig, _, ok = parseString(sig); !ok { return nil, errors.New("ssh: malformed signature response from agent") } return sig, nil } // ClientKeyboardInteractive should prompt the user for the given // questions. type ClientKeyboardInteractive interface { // Challenge should print the questions, optionally disabling // echoing (eg. for passwords), and return all the answers. // Challenge may be called multiple times in a single // session. After successful authentication, the server may // send a challenge with no questions, for which the user and // instruction messages should be printed. RFC 4256 section // 3.3 details how the UI should behave for both CLI and // GUI environments. Challenge(user, instruction string, questions []string, echos []bool) ([]string, error) } // ClientAuthKeyboardInteractive returns a ClientAuth using a // prompt/response sequence controlled by the server. func ClientAuthKeyboardInteractive(impl ClientKeyboardInteractive) ClientAuth { return &keyboardInteractiveAuth{impl} } type keyboardInteractiveAuth struct { ClientKeyboardInteractive } func (k *keyboardInteractiveAuth) method() string { return "keyboard-interactive" } func (k *keyboardInteractiveAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { type initiateMsg struct { User string Service string Method string Language string Submethods string } if err := c.writePacket(marshal(msgUserAuthRequest, initiateMsg{ User: user, Service: serviceSSH, Method: "keyboard-interactive", })); err != nil { return false, nil, err } for { packet, err := c.readPacket() if err != nil { return false, nil, err } // like handleAuthResponse, but with less options. switch packet[0] { case msgUserAuthBanner: // TODO: Print banners during userauth. continue case msgUserAuthInfoRequest: // OK case msgUserAuthFailure: var msg userAuthFailureMsg if err := unmarshal(&msg, packet, msgUserAuthFailure); err != nil { return false, nil, err } return false, msg.Methods, nil case msgUserAuthSuccess: return true, nil, nil default: return false, nil, UnexpectedMessageError{msgUserAuthInfoRequest, packet[0]} } var msg userAuthInfoRequestMsg if err := unmarshal(&msg, packet, packet[0]); err != nil { return false, nil, err } // Manually unpack the prompt/echo pairs. rest := msg.Prompts var prompts []string var echos []bool for i := 0; i < int(msg.NumPrompts); i++ { prompt, r, ok := parseString(rest) if !ok || len(r) == 0 { return false, nil, errors.New("ssh: prompt format error") } prompts = append(prompts, string(prompt)) echos = append(echos, r[0] != 0) rest = r[1:] } if len(rest) != 0 { return false, nil, fmt.Errorf("ssh: junk following message %q", rest) } answers, err := k.Challenge(msg.User, msg.Instruction, prompts, echos) if err != nil { return false, nil, err } if len(answers) != len(prompts) { return false, nil, errors.New("ssh: not enough answers from keyboard-interactive callback") } responseLength := 1 + 4 for _, a := range answers { responseLength += stringLength(len(a)) } serialized := make([]byte, responseLength) p := serialized p[0] = msgUserAuthInfoResponse p = p[1:] p = marshalUint32(p, uint32(len(answers))) for _, a := range answers { p = marshalString(p, []byte(a)) } if err := c.writePacket(serialized); err != nil { return false, nil, err } } } ������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/client_auth_test.go����������������������������0000644�0000153�0000161�00000025136�12321736016�026050� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "bytes" "crypto/dsa" "io" "io/ioutil" "math/big" "strings" "testing" _ "crypto/sha1" ) // private key for mock server const testServerPrivateKey = `-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU 70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx 9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT +IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4 KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8 e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1 D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+ 3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw 64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g /SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw== -----END RSA PRIVATE KEY-----` const testClientPrivateKey = `-----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBALdGZxkXDAjsYk10ihwU6Id2KeILz1TAJuoq4tOgDWxEEGeTrcld r/ZwVaFzjWzxaf6zQIJbfaSEAhqD5yo72+sCAwEAAQJBAK8PEVU23Wj8mV0QjwcJ tZ4GcTUYQL7cF4+ezTCE9a1NrGnCP2RuQkHEKxuTVrxXt+6OF15/1/fuXnxKjmJC nxkCIQDaXvPPBi0c7vAxGwNY9726x01/dNbHCE0CBtcotobxpwIhANbbQbh3JHVW 2haQh4fAG5mhesZKAGcxTyv4mQ7uMSQdAiAj+4dzMpJWdSzQ+qGHlHMIBvVHLkqB y2VdEyF7DPCZewIhAI7GOI/6LDIFOvtPo6Bj2nNmyQ1HU6k/LRtNIXi4c9NJAiAr rrxx26itVhJmcvoUhOjwuzSlP2bE5VHAvkGB352YBg== -----END RSA PRIVATE KEY-----` // keychain implements the ClientKeyring interface type keychain struct { keys []Signer } func (k *keychain) Key(i int) (PublicKey, error) { if i < 0 || i >= len(k.keys) { return nil, nil } return k.keys[i].PublicKey(), nil } func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) { return k.keys[i].Sign(rand, data) } func (k *keychain) add(key Signer) { k.keys = append(k.keys, key) } func (k *keychain) loadPEM(file string) error { buf, err := ioutil.ReadFile(file) if err != nil { return err } key, err := ParsePrivateKey(buf) if err != nil { return err } k.add(key) return nil } // password implements the ClientPassword interface type password string func (p password) Password(user string) (string, error) { return string(p), nil } type keyboardInteractive map[string]string func (cr *keyboardInteractive) Challenge(user string, instruction string, questions []string, echos []bool) ([]string, error) { var answers []string for _, q := range questions { answers = append(answers, (*cr)[q]) } return answers, nil } // reused internally by tests var ( rsaKey Signer dsaKey Signer clientKeychain = new(keychain) clientPassword = password("tiger") serverConfig = &ServerConfig{ PasswordCallback: func(conn *ServerConn, user, pass string) bool { return user == "testuser" && pass == string(clientPassword) }, PublicKeyCallback: func(conn *ServerConn, user, algo string, pubkey []byte) bool { key, _ := clientKeychain.Key(0) expected := MarshalPublicKey(key) algoname := key.PublicKeyAlgo() return user == "testuser" && algo == algoname && bytes.Equal(pubkey, expected) }, KeyboardInteractiveCallback: func(conn *ServerConn, user string, client ClientKeyboardInteractive) bool { ans, err := client.Challenge("user", "instruction", []string{"question1", "question2"}, []bool{true, true}) if err != nil { return false } ok := user == "testuser" && ans[0] == "answer1" && ans[1] == "answer2" client.Challenge("user", "motd", nil, nil) return ok }, } ) func init() { var err error rsaKey, err = ParsePrivateKey([]byte(testServerPrivateKey)) if err != nil { panic("unable to set private key: " + err.Error()) } rawDSAKey := new(dsa.PrivateKey) // taken from crypto/dsa/dsa_test.go rawDSAKey.P, _ = new(big.Int).SetString("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF", 16) rawDSAKey.Q, _ = new(big.Int).SetString("E1D3391245933D68A0714ED34BBCB7A1F422B9C1", 16) rawDSAKey.G, _ = new(big.Int).SetString("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA", 16) rawDSAKey.Y, _ = new(big.Int).SetString("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2", 16) rawDSAKey.X, _ = new(big.Int).SetString("5078D4D29795CBE76D3AACFE48C9AF0BCDBEE91A", 16) dsaKey, err = NewSignerFromKey(rawDSAKey) if err != nil { panic("NewSignerFromKey: " + err.Error()) } clientKeychain.add(rsaKey) serverConfig.AddHostKey(rsaKey) } // newMockAuthServer creates a new Server bound to // the loopback interface. The server exits after // processing one handshake. func newMockAuthServer(t *testing.T) string { l, err := Listen("tcp", "127.0.0.1:0", serverConfig) if err != nil { t.Fatalf("unable to newMockAuthServer: %s", err) } go func() { defer l.Close() c, err := l.Accept() if err != nil { t.Errorf("Unable to accept incoming connection: %v", err) return } if err := c.Handshake(); err != nil { // not Errorf because this is expected to // fail for some tests. t.Logf("Handshaking error: %v", err) return } defer c.Close() }() return l.Addr().String() } func TestClientAuthPublicKey(t *testing.T) { config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthKeyring(clientKeychain), }, } c, err := Dial("tcp", newMockAuthServer(t), config) if err != nil { t.Fatalf("unable to dial remote side: %s", err) } c.Close() } func TestClientAuthPassword(t *testing.T) { config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthPassword(clientPassword), }, } c, err := Dial("tcp", newMockAuthServer(t), config) if err != nil { t.Fatalf("unable to dial remote side: %s", err) } c.Close() } func TestClientAuthWrongPassword(t *testing.T) { wrongPw := password("wrong") config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthPassword(wrongPw), ClientAuthKeyring(clientKeychain), }, } c, err := Dial("tcp", newMockAuthServer(t), config) if err != nil { t.Fatalf("unable to dial remote side: %s", err) } c.Close() } func TestClientAuthKeyboardInteractive(t *testing.T) { answers := keyboardInteractive(map[string]string{ "question1": "answer1", "question2": "answer2", }) config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthKeyboardInteractive(&answers), }, } c, err := Dial("tcp", newMockAuthServer(t), config) if err != nil { t.Fatalf("unable to dial remote side: %s", err) } c.Close() } func TestClientAuthWrongKeyboardInteractive(t *testing.T) { answers := keyboardInteractive(map[string]string{ "question1": "answer1", "question2": "WRONG", }) config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthKeyboardInteractive(&answers), }, } c, err := Dial("tcp", newMockAuthServer(t), config) if err == nil { c.Close() t.Fatalf("wrong answers should not have authenticated with KeyboardInteractive") } } // the mock server will only authenticate ssh-rsa keys func TestClientAuthInvalidPublicKey(t *testing.T) { kc := new(keychain) kc.add(dsaKey) config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthKeyring(kc), }, } c, err := Dial("tcp", newMockAuthServer(t), config) if err == nil { c.Close() t.Fatalf("dsa private key should not have authenticated with rsa public key") } } // the client should authenticate with the second key func TestClientAuthRSAandDSA(t *testing.T) { kc := new(keychain) kc.add(dsaKey) kc.add(rsaKey) config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthKeyring(kc), }, } c, err := Dial("tcp", newMockAuthServer(t), config) if err != nil { t.Fatalf("client could not authenticate with rsa key: %v", err) } c.Close() } func TestClientHMAC(t *testing.T) { kc := new(keychain) kc.add(rsaKey) for _, mac := range DefaultMACOrder { config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthKeyring(kc), }, Crypto: CryptoConfig{ MACs: []string{mac}, }, } c, err := Dial("tcp", newMockAuthServer(t), config) if err != nil { t.Fatalf("client could not authenticate with mac algo %s: %v", mac, err) } c.Close() } } // issue 4285. func TestClientUnsupportedCipher(t *testing.T) { kc := new(keychain) config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthKeyring(kc), }, Crypto: CryptoConfig{ Ciphers: []string{"aes128-cbc"}, // not currently supported }, } c, err := Dial("tcp", newMockAuthServer(t), config) if err == nil { t.Errorf("expected no ciphers in common") c.Close() } } func TestClientUnsupportedKex(t *testing.T) { kc := new(keychain) config := &ClientConfig{ User: "testuser", Auth: []ClientAuth{ ClientAuthKeyring(kc), }, Crypto: CryptoConfig{ KeyExchanges: []string{"diffie-hellman-group-exchange-sha256"}, // not currently supported }, } c, err := Dial("tcp", newMockAuthServer(t), config) if err == nil || !strings.Contains(err.Error(), "no common algorithms") { t.Errorf("got %v, expected 'no common algorithms'", err) } if c != nil { c.Close() } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/tcpip_test.go����������������������������������0000644�0000153�0000161�00000000520�12321736016�024656� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ssh import ( "testing" ) func TestAutoPortListenBroken(t *testing.T) { broken := "SSH-2.0-OpenSSH_5.9hh11" works := "SSH-2.0-OpenSSH_6.1" if !isBrokenOpenSSHVersion(broken) { t.Errorf("version %q not marked as broken", broken) } if isBrokenOpenSSHVersion(works) { t.Errorf("version %q marked as broken", works) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/agent.go���������������������������������������0000644�0000153�0000161�00000015111�12321736016�023600� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "encoding/base64" "errors" "io" "sync" ) // See [PROTOCOL.agent], section 3. const ( // 3.2 Requests from client to agent for protocol 2 key operations agentRequestIdentities = 11 agentSignRequest = 13 agentAddIdentity = 17 agentRemoveIdentity = 18 agentRemoveAllIdentities = 19 agentAddIdConstrained = 25 // 3.3 Key-type independent requests from client to agent agentAddSmartcardKey = 20 agentRemoveSmartcardKey = 21 agentLock = 22 agentUnlock = 23 agentAddSmartcardKeyConstrained = 26 // 3.4 Generic replies from agent to client agentFailure = 5 agentSuccess = 6 // 3.6 Replies from agent to client for protocol 2 key operations agentIdentitiesAnswer = 12 agentSignResponse = 14 // 3.7 Key constraint identifiers agentConstrainLifetime = 1 agentConstrainConfirm = 2 ) // maxAgentResponseBytes is the maximum agent reply size that is accepted. This // is a sanity check, not a limit in the spec. const maxAgentResponseBytes = 16 << 20 // Agent messages: // These structures mirror the wire format of the corresponding ssh agent // messages found in [PROTOCOL.agent]. type failureAgentMsg struct{} type successAgentMsg struct{} // See [PROTOCOL.agent], section 2.5.2. type requestIdentitiesAgentMsg struct{} // See [PROTOCOL.agent], section 2.5.2. type identitiesAnswerAgentMsg struct { NumKeys uint32 Keys []byte `ssh:"rest"` } // See [PROTOCOL.agent], section 2.6.2. type signRequestAgentMsg struct { KeyBlob []byte Data []byte Flags uint32 } // See [PROTOCOL.agent], section 2.6.2. type signResponseAgentMsg struct { SigBlob []byte } // AgentKey represents a protocol 2 key as defined in [PROTOCOL.agent], // section 2.5.2. type AgentKey struct { blob []byte Comment string } // String returns the storage form of an agent key with the format, base64 // encoded serialized key, and the comment if it is not empty. func (ak *AgentKey) String() string { algo, _, ok := parseString(ak.blob) if !ok { return "ssh: malformed key" } s := string(algo) + " " + base64.StdEncoding.EncodeToString(ak.blob) if ak.Comment != "" { s += " " + ak.Comment } return s } // Key returns an agent's public key as one of the supported key or certificate types. func (ak *AgentKey) Key() (PublicKey, error) { if key, _, ok := ParsePublicKey(ak.blob); ok { return key, nil } return nil, errors.New("ssh: failed to parse key blob") } func parseAgentKey(in []byte) (out *AgentKey, rest []byte, ok bool) { ak := new(AgentKey) if ak.blob, in, ok = parseString(in); !ok { return } comment, in, ok := parseString(in) if !ok { return } ak.Comment = string(comment) return ak, in, true } // AgentClient provides a means to communicate with an ssh agent process based // on the protocol described in [PROTOCOL.agent]?rev=1.6. type AgentClient struct { // conn is typically represented by using a *net.UnixConn conn io.ReadWriter // mu is used to prevent concurrent access to the agent mu sync.Mutex } // NewAgentClient creates and returns a new *AgentClient using the // passed in io.ReadWriter as a connection to a ssh agent. func NewAgentClient(rw io.ReadWriter) *AgentClient { return &AgentClient{conn: rw} } // sendAndReceive sends req to the agent and waits for a reply. On success, // the reply is unmarshaled into reply and replyType is set to the first byte of // the reply, which contains the type of the message. func (ac *AgentClient) sendAndReceive(req []byte) (reply interface{}, replyType uint8, err error) { // ac.mu prevents multiple, concurrent requests. Since the agent is typically // on the same machine, we don't attempt to pipeline the requests. ac.mu.Lock() defer ac.mu.Unlock() msg := make([]byte, stringLength(len(req))) marshalString(msg, req) if _, err = ac.conn.Write(msg); err != nil { return } var respSizeBuf [4]byte if _, err = io.ReadFull(ac.conn, respSizeBuf[:]); err != nil { return } respSize, _, _ := parseUint32(respSizeBuf[:]) if respSize > maxAgentResponseBytes { err = errors.New("ssh: agent reply too large") return } buf := make([]byte, respSize) if _, err = io.ReadFull(ac.conn, buf); err != nil { return } return unmarshalAgentMsg(buf) } // RequestIdentities queries the agent for protocol 2 keys as defined in // [PROTOCOL.agent] section 2.5.2. func (ac *AgentClient) RequestIdentities() ([]*AgentKey, error) { req := marshal(agentRequestIdentities, requestIdentitiesAgentMsg{}) msg, msgType, err := ac.sendAndReceive(req) if err != nil { return nil, err } switch msg := msg.(type) { case *identitiesAnswerAgentMsg: if msg.NumKeys > maxAgentResponseBytes/8 { return nil, errors.New("ssh: too many keys in agent reply") } keys := make([]*AgentKey, msg.NumKeys) data := msg.Keys for i := uint32(0); i < msg.NumKeys; i++ { var key *AgentKey var ok bool if key, data, ok = parseAgentKey(data); !ok { return nil, ParseError{agentIdentitiesAnswer} } keys[i] = key } return keys, nil case *failureAgentMsg: return nil, errors.New("ssh: failed to list keys") } return nil, UnexpectedMessageError{agentIdentitiesAnswer, msgType} } // SignRequest requests the signing of data by the agent using a protocol 2 key // as defined in [PROTOCOL.agent] section 2.6.2. func (ac *AgentClient) SignRequest(key PublicKey, data []byte) ([]byte, error) { req := marshal(agentSignRequest, signRequestAgentMsg{ KeyBlob: MarshalPublicKey(key), Data: data, }) msg, msgType, err := ac.sendAndReceive(req) if err != nil { return nil, err } switch msg := msg.(type) { case *signResponseAgentMsg: return msg.SigBlob, nil case *failureAgentMsg: return nil, errors.New("ssh: failed to sign challenge") } return nil, UnexpectedMessageError{agentSignResponse, msgType} } // unmarshalAgentMsg parses an agent message in packet, returning the parsed // form and the message type of packet. func unmarshalAgentMsg(packet []byte) (interface{}, uint8, error) { if len(packet) < 1 { return nil, 0, ParseError{0} } var msg interface{} switch packet[0] { case agentFailure: msg = new(failureAgentMsg) case agentSuccess: msg = new(successAgentMsg) case agentIdentitiesAnswer: msg = new(identitiesAnswerAgentMsg) case agentSignResponse: msg = new(signResponseAgentMsg) default: return nil, 0, UnexpectedMessageError{0, packet[0]} } if err := unmarshal(msg, packet, packet[0]); err != nil { return nil, 0, err } return msg, packet[0], nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/client.go��������������������������������������0000644�0000153�0000161�00000033053�12321736016�023765� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "crypto/rand" "encoding/binary" "errors" "fmt" "io" "net" "sync" ) // ClientConn represents the client side of an SSH connection. type ClientConn struct { transport *transport config *ClientConfig chanList // channels associated with this connection forwardList // forwarded tcpip connections from the remote side globalRequest // Address as passed to the Dial function. dialAddress string serverVersion string } type globalRequest struct { sync.Mutex response chan interface{} } // Client returns a new SSH client connection using c as the underlying transport. func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) { return clientWithAddress(c, "", config) } func clientWithAddress(c net.Conn, addr string, config *ClientConfig) (*ClientConn, error) { conn := &ClientConn{ transport: newTransport(c, config.rand(), true /* is client */), config: config, globalRequest: globalRequest{response: make(chan interface{}, 1)}, dialAddress: addr, } if err := conn.handshake(); err != nil { conn.transport.Close() return nil, fmt.Errorf("handshake failed: %v", err) } go conn.mainLoop() return conn, nil } // Close closes the connection. func (c *ClientConn) Close() error { return c.transport.Close() } // LocalAddr returns the local network address. func (c *ClientConn) LocalAddr() net.Addr { return c.transport.LocalAddr() } // RemoteAddr returns the remote network address. func (c *ClientConn) RemoteAddr() net.Addr { return c.transport.RemoteAddr() } // handshake performs the client side key exchange. See RFC 4253 Section 7. func (c *ClientConn) handshake() error { clientVersion := []byte(packageVersion) if c.config.ClientVersion != "" { clientVersion = []byte(c.config.ClientVersion) } serverVersion, err := exchangeVersions(c.transport.Conn, clientVersion) if err != nil { return err } c.serverVersion = string(serverVersion) clientKexInit := kexInitMsg{ KexAlgos: c.config.Crypto.kexes(), ServerHostKeyAlgos: supportedHostKeyAlgos, CiphersClientServer: c.config.Crypto.ciphers(), CiphersServerClient: c.config.Crypto.ciphers(), MACsClientServer: c.config.Crypto.macs(), MACsServerClient: c.config.Crypto.macs(), CompressionClientServer: supportedCompressions, CompressionServerClient: supportedCompressions, } kexInitPacket := marshal(msgKexInit, clientKexInit) if err := c.transport.writePacket(kexInitPacket); err != nil { return err } packet, err := c.transport.readPacket() if err != nil { return err } var serverKexInit kexInitMsg if err = unmarshal(&serverKexInit, packet, msgKexInit); err != nil { return err } algs := findAgreedAlgorithms(&clientKexInit, &serverKexInit) if algs == nil { return errors.New("ssh: no common algorithms") } if serverKexInit.FirstKexFollows && algs.kex != serverKexInit.KexAlgos[0] { // The server sent a Kex message for the wrong algorithm, // which we have to ignore. if _, err := c.transport.readPacket(); err != nil { return err } } kex, ok := kexAlgoMap[algs.kex] if !ok { return fmt.Errorf("ssh: unexpected key exchange algorithm %v", algs.kex) } magics := handshakeMagics{ clientVersion: clientVersion, serverVersion: serverVersion, clientKexInit: kexInitPacket, serverKexInit: packet, } result, err := kex.Client(c.transport, c.config.rand(), &magics) if err != nil { return err } err = verifyHostKeySignature(algs.hostKey, result.HostKey, result.H, result.Signature) if err != nil { return err } if checker := c.config.HostKeyChecker; checker != nil { err = checker.Check(c.dialAddress, c.transport.RemoteAddr(), algs.hostKey, result.HostKey) if err != nil { return err } } c.transport.prepareKeyChange(algs, result) if err = c.transport.writePacket([]byte{msgNewKeys}); err != nil { return err } if packet, err = c.transport.readPacket(); err != nil { return err } if packet[0] != msgNewKeys { return UnexpectedMessageError{msgNewKeys, packet[0]} } return c.authenticate() } // Verify the host key obtained in the key exchange. func verifyHostKeySignature(hostKeyAlgo string, hostKeyBytes []byte, data []byte, signature []byte) error { hostKey, rest, ok := ParsePublicKey(hostKeyBytes) if len(rest) > 0 || !ok { return errors.New("ssh: could not parse hostkey") } sig, rest, ok := parseSignatureBody(signature) if len(rest) > 0 || !ok { return errors.New("ssh: signature parse error") } if sig.Format != hostKeyAlgo { return fmt.Errorf("ssh: unexpected signature type %q", sig.Format) } if !hostKey.Verify(data, sig.Blob) { return errors.New("ssh: host key signature error") } return nil } // mainLoop reads incoming messages and routes channel messages // to their respective ClientChans. func (c *ClientConn) mainLoop() { defer func() { c.transport.Close() c.chanList.closeAll() c.forwardList.closeAll() }() for { packet, err := c.transport.readPacket() if err != nil { break } // TODO(dfc) A note on blocking channel use. // The msg, data and dataExt channels of a clientChan can // cause this loop to block indefinitely if the consumer does // not service them. switch packet[0] { case msgChannelData: if len(packet) < 9 { // malformed data packet return } remoteId := binary.BigEndian.Uint32(packet[1:5]) length := binary.BigEndian.Uint32(packet[5:9]) packet = packet[9:] if length != uint32(len(packet)) { return } ch, ok := c.getChan(remoteId) if !ok { return } ch.stdout.write(packet) case msgChannelExtendedData: if len(packet) < 13 { // malformed data packet return } remoteId := binary.BigEndian.Uint32(packet[1:5]) datatype := binary.BigEndian.Uint32(packet[5:9]) length := binary.BigEndian.Uint32(packet[9:13]) packet = packet[13:] if length != uint32(len(packet)) { return } // RFC 4254 5.2 defines data_type_code 1 to be data destined // for stderr on interactive sessions. Other data types are // silently discarded. if datatype == 1 { ch, ok := c.getChan(remoteId) if !ok { return } ch.stderr.write(packet) } default: decoded, err := decode(packet) if err != nil { if _, ok := err.(UnexpectedMessageError); ok { fmt.Printf("mainLoop: unexpected message: %v\n", err) continue } return } switch msg := decoded.(type) { case *channelOpenMsg: c.handleChanOpen(msg) case *channelOpenConfirmMsg: ch, ok := c.getChan(msg.PeersId) if !ok { return } ch.msg <- msg case *channelOpenFailureMsg: ch, ok := c.getChan(msg.PeersId) if !ok { return } ch.msg <- msg case *channelCloseMsg: ch, ok := c.getChan(msg.PeersId) if !ok { return } ch.Close() close(ch.msg) c.chanList.remove(msg.PeersId) case *channelEOFMsg: ch, ok := c.getChan(msg.PeersId) if !ok { return } ch.stdout.eof() // RFC 4254 is mute on how EOF affects dataExt messages but // it is logical to signal EOF at the same time. ch.stderr.eof() case *channelRequestSuccessMsg: ch, ok := c.getChan(msg.PeersId) if !ok { return } ch.msg <- msg case *channelRequestFailureMsg: ch, ok := c.getChan(msg.PeersId) if !ok { return } ch.msg <- msg case *channelRequestMsg: ch, ok := c.getChan(msg.PeersId) if !ok { return } ch.msg <- msg case *windowAdjustMsg: ch, ok := c.getChan(msg.PeersId) if !ok { return } if !ch.remoteWin.add(msg.AdditionalBytes) { // invalid window update return } case *globalRequestMsg: // This handles keepalive messages and matches // the behaviour of OpenSSH. if msg.WantReply { c.transport.writePacket(marshal(msgRequestFailure, globalRequestFailureMsg{})) } case *globalRequestSuccessMsg, *globalRequestFailureMsg: c.globalRequest.response <- msg case *disconnectMsg: return default: fmt.Printf("mainLoop: unhandled message %T: %v\n", msg, msg) } } } } // Handle channel open messages from the remote side. func (c *ClientConn) handleChanOpen(msg *channelOpenMsg) { if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { c.sendConnectionFailed(msg.PeersId) } switch msg.ChanType { case "forwarded-tcpip": laddr, rest, ok := parseTCPAddr(msg.TypeSpecificData) if !ok { // invalid request c.sendConnectionFailed(msg.PeersId) return } l, ok := c.forwardList.lookup(*laddr) if !ok { // TODO: print on a more structured log. fmt.Println("could not find forward list entry for", laddr) // Section 7.2, implementations MUST reject spurious incoming // connections. c.sendConnectionFailed(msg.PeersId) return } raddr, rest, ok := parseTCPAddr(rest) if !ok { // invalid request c.sendConnectionFailed(msg.PeersId) return } ch := c.newChan(c.transport) ch.remoteId = msg.PeersId ch.remoteWin.add(msg.PeersWindow) ch.maxPacket = msg.MaxPacketSize m := channelOpenConfirmMsg{ PeersId: ch.remoteId, MyId: ch.localId, MyWindow: channelWindowSize, MaxPacketSize: channelMaxPacketSize, } c.transport.writePacket(marshal(msgChannelOpenConfirm, m)) l <- forward{ch, raddr} default: // unknown channel type m := channelOpenFailureMsg{ PeersId: msg.PeersId, Reason: UnknownChannelType, Message: fmt.Sprintf("unknown channel type: %v", msg.ChanType), Language: "en_US.UTF-8", } c.transport.writePacket(marshal(msgChannelOpenFailure, m)) } } // sendGlobalRequest sends a global request message as specified // in RFC4254 section 4. To correctly synchronise messages, a lock // is held internally until a response is returned. func (c *ClientConn) sendGlobalRequest(m interface{}) (*globalRequestSuccessMsg, error) { c.globalRequest.Lock() defer c.globalRequest.Unlock() if err := c.transport.writePacket(marshal(msgGlobalRequest, m)); err != nil { return nil, err } r := <-c.globalRequest.response if r, ok := r.(*globalRequestSuccessMsg); ok { return r, nil } return nil, errors.New("request failed") } // sendConnectionFailed rejects an incoming channel identified // by remoteId. func (c *ClientConn) sendConnectionFailed(remoteId uint32) error { m := channelOpenFailureMsg{ PeersId: remoteId, Reason: ConnectionFailed, Message: "invalid request", Language: "en_US.UTF-8", } return c.transport.writePacket(marshal(msgChannelOpenFailure, m)) } // parseTCPAddr parses the originating address from the remote into a *net.TCPAddr. // RFC 4254 section 7.2 is mute on what to do if parsing fails but the forwardlist // requires a valid *net.TCPAddr to operate, so we enforce that restriction here. func parseTCPAddr(b []byte) (*net.TCPAddr, []byte, bool) { addr, b, ok := parseString(b) if !ok { return nil, b, false } port, b, ok := parseUint32(b) if !ok { return nil, b, false } ip := net.ParseIP(string(addr)) if ip == nil { return nil, b, false } return &net.TCPAddr{IP: ip, Port: int(port)}, b, true } // Dial connects to the given network address using net.Dial and // then initiates a SSH handshake, returning the resulting client connection. func Dial(network, addr string, config *ClientConfig) (*ClientConn, error) { conn, err := net.Dial(network, addr) if err != nil { return nil, err } return clientWithAddress(conn, addr, config) } // A ClientConfig structure is used to configure a ClientConn. After one has // been passed to an SSH function it must not be modified. type ClientConfig struct { // Rand provides the source of entropy for key exchange. If Rand is // nil, the cryptographic random reader in package crypto/rand will // be used. Rand io.Reader // The username to authenticate. User string // A slice of ClientAuth methods. Only the first instance // of a particular RFC 4252 method will be used during authentication. Auth []ClientAuth // HostKeyChecker, if not nil, is called during the cryptographic // handshake to validate the server's host key. A nil HostKeyChecker // implies that all host keys are accepted. HostKeyChecker HostKeyChecker // Cryptographic-related configuration. Crypto CryptoConfig // The identification string that will be used for the connection. // If empty, a reasonable default is used. ClientVersion string } func (c *ClientConfig) rand() io.Reader { if c.Rand == nil { return rand.Reader } return c.Rand } // Thread safe channel list. type chanList struct { // protects concurrent access to chans sync.Mutex // chans are indexed by the local id of the channel, clientChan.localId. // The PeersId value of messages received by ClientConn.mainLoop is // used to locate the right local clientChan in this slice. chans []*clientChan } // Allocate a new ClientChan with the next avail local id. func (c *chanList) newChan(p packetConn) *clientChan { c.Lock() defer c.Unlock() for i := range c.chans { if c.chans[i] == nil { ch := newClientChan(p, uint32(i)) c.chans[i] = ch return ch } } i := len(c.chans) ch := newClientChan(p, uint32(i)) c.chans = append(c.chans, ch) return ch } func (c *chanList) getChan(id uint32) (*clientChan, bool) { c.Lock() defer c.Unlock() if id >= uint32(len(c.chans)) { return nil, false } return c.chans[id], true } func (c *chanList) remove(id uint32) { c.Lock() defer c.Unlock() c.chans[id] = nil } func (c *chanList) closeAll() { c.Lock() defer c.Unlock() for _, ch := range c.chans { if ch == nil { continue } ch.Close() close(ch.msg) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/buffer_test.go���������������������������������0000644�0000153�0000161�00000004221�12321736016�025012� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "io" "testing" ) var BYTES = []byte("abcdefghijklmnopqrstuvwxyz") func TestBufferReadwrite(t *testing.T) { b := newBuffer() b.write(BYTES[:10]) r, _ := b.Read(make([]byte, 10)) if r != 10 { t.Fatalf("Expected written == read == 10, written: 10, read %d", r) } b = newBuffer() b.write(BYTES[:5]) r, _ = b.Read(make([]byte, 10)) if r != 5 { t.Fatalf("Expected written == read == 5, written: 5, read %d", r) } b = newBuffer() b.write(BYTES[:10]) r, _ = b.Read(make([]byte, 5)) if r != 5 { t.Fatalf("Expected written == 10, read == 5, written: 10, read %d", r) } b = newBuffer() b.write(BYTES[:5]) b.write(BYTES[5:15]) r, _ = b.Read(make([]byte, 10)) r2, _ := b.Read(make([]byte, 10)) if r != 10 || r2 != 5 || 15 != r+r2 { t.Fatal("Expected written == read == 15") } } func TestBufferClose(t *testing.T) { b := newBuffer() b.write(BYTES[:10]) b.eof() _, err := b.Read(make([]byte, 5)) if err != nil { t.Fatal("expected read of 5 to not return EOF") } b = newBuffer() b.write(BYTES[:10]) b.eof() r, err := b.Read(make([]byte, 5)) r2, err2 := b.Read(make([]byte, 10)) if r != 5 || r2 != 5 || err != nil || err2 != nil { t.Fatal("expected reads of 5 and 5") } b = newBuffer() b.write(BYTES[:10]) b.eof() r, err = b.Read(make([]byte, 5)) r2, err2 = b.Read(make([]byte, 10)) r3, err3 := b.Read(make([]byte, 10)) if r != 5 || r2 != 5 || r3 != 0 || err != nil || err2 != nil || err3 != io.EOF { t.Fatal("expected reads of 5 and 5 and 0, with EOF") } b = newBuffer() b.write(make([]byte, 5)) b.write(make([]byte, 10)) b.eof() r, err = b.Read(make([]byte, 9)) r2, err2 = b.Read(make([]byte, 3)) r3, err3 = b.Read(make([]byte, 3)) r4, err4 := b.Read(make([]byte, 10)) if err != nil || err2 != nil || err3 != nil || err4 != io.EOF { t.Fatalf("Expected EOF on forth read only, err=%v, err2=%v, err3=%v, err4=%v", err, err2, err3, err4) } if r != 9 || r2 != 3 || r3 != 3 || r4 != 0 { t.Fatal("Expected written == read == 15", r, r2, r3, r4) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/common.go��������������������������������������0000644�0000153�0000161�00000021577�12321736016�024007� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "crypto" "fmt" "sync" _ "crypto/sha1" _ "crypto/sha256" _ "crypto/sha512" ) // These are string constants in the SSH protocol. const ( compressionNone = "none" serviceUserAuth = "ssh-userauth" serviceSSH = "ssh-connection" ) var supportedKexAlgos = []string{ kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, kexAlgoDH14SHA1, kexAlgoDH1SHA1, } var supportedHostKeyAlgos = []string{ KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoRSA, KeyAlgoDSA, } var supportedCompressions = []string{compressionNone} // hashFuncs keeps the mapping of supported algorithms to their respective // hashes needed for signature verification. var hashFuncs = map[string]crypto.Hash{ KeyAlgoRSA: crypto.SHA1, KeyAlgoDSA: crypto.SHA1, KeyAlgoECDSA256: crypto.SHA256, KeyAlgoECDSA384: crypto.SHA384, KeyAlgoECDSA521: crypto.SHA512, CertAlgoRSAv01: crypto.SHA1, CertAlgoDSAv01: crypto.SHA1, CertAlgoECDSA256v01: crypto.SHA256, CertAlgoECDSA384v01: crypto.SHA384, CertAlgoECDSA521v01: crypto.SHA512, } // UnexpectedMessageError results when the SSH message that we received didn't // match what we wanted. type UnexpectedMessageError struct { expected, got uint8 } func (u UnexpectedMessageError) Error() string { return fmt.Sprintf("ssh: unexpected message type %d (expected %d)", u.got, u.expected) } // ParseError results from a malformed SSH message. type ParseError struct { msgType uint8 } func (p ParseError) Error() string { return fmt.Sprintf("ssh: parse error in message type %d", p.msgType) } func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) { for _, clientAlgo := range clientAlgos { for _, serverAlgo := range serverAlgos { if clientAlgo == serverAlgo { return clientAlgo, true } } } return } func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCipher string, ok bool) { for _, clientCipher := range clientCiphers { for _, serverCipher := range serverCiphers { // reject the cipher if we have no cipherModes definition if clientCipher == serverCipher && cipherModes[clientCipher] != nil { return clientCipher, true } } } return } type algorithms struct { kex string hostKey string wCipher string rCipher string rMAC string wMAC string rCompression string wCompression string } func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms) { var ok bool result := &algorithms{} result.kex, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos) if !ok { return } result.hostKey, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) if !ok { return } result.wCipher, ok = findCommonCipher(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) if !ok { return } result.rCipher, ok = findCommonCipher(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) if !ok { return } result.wMAC, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) if !ok { return } result.rMAC, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) if !ok { return } result.wCompression, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) if !ok { return } result.rCompression, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) if !ok { return } return result } // Cryptographic configuration common to both ServerConfig and ClientConfig. type CryptoConfig struct { // The allowed key exchanges algorithms. If unspecified then a // default set of algorithms is used. KeyExchanges []string // The allowed cipher algorithms. If unspecified then DefaultCipherOrder is // used. Ciphers []string // The allowed MAC algorithms. If unspecified then DefaultMACOrder is used. MACs []string } func (c *CryptoConfig) ciphers() []string { if c.Ciphers == nil { return DefaultCipherOrder } return c.Ciphers } func (c *CryptoConfig) kexes() []string { if c.KeyExchanges == nil { return defaultKeyExchangeOrder } return c.KeyExchanges } func (c *CryptoConfig) macs() []string { if c.MACs == nil { return DefaultMACOrder } return c.MACs } // serialize a signed slice according to RFC 4254 6.6. The name should // be a key type name, rather than a cert type name. func serializeSignature(name string, sig []byte) []byte { length := stringLength(len(name)) length += stringLength(len(sig)) ret := make([]byte, length) r := marshalString(ret, []byte(name)) r = marshalString(r, sig) return ret } // MarshalPublicKey serializes a supported key or certificate for use // by the SSH wire protocol. It can be used for comparison with the // pubkey argument of ServerConfig's PublicKeyCallback as well as for // generating an authorized_keys or host_keys file. func MarshalPublicKey(key PublicKey) []byte { // See also RFC 4253 6.6. algoname := key.PublicKeyAlgo() blob := key.Marshal() length := stringLength(len(algoname)) length += len(blob) ret := make([]byte, length) r := marshalString(ret, []byte(algoname)) copy(r, blob) return ret } // pubAlgoToPrivAlgo returns the private key algorithm format name that // corresponds to a given public key algorithm format name. For most // public keys, the private key algorithm name is the same. For some // situations, such as openssh certificates, the private key algorithm and // public key algorithm names differ. This accounts for those situations. func pubAlgoToPrivAlgo(pubAlgo string) string { switch pubAlgo { case CertAlgoRSAv01: return KeyAlgoRSA case CertAlgoDSAv01: return KeyAlgoDSA case CertAlgoECDSA256v01: return KeyAlgoECDSA256 case CertAlgoECDSA384v01: return KeyAlgoECDSA384 case CertAlgoECDSA521v01: return KeyAlgoECDSA521 } return pubAlgo } // buildDataSignedForAuth returns the data that is signed in order to prove // possession of a private key. See RFC 4252, section 7. func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { user := []byte(req.User) service := []byte(req.Service) method := []byte(req.Method) length := stringLength(len(sessionId)) length += 1 length += stringLength(len(user)) length += stringLength(len(service)) length += stringLength(len(method)) length += 1 length += stringLength(len(algo)) length += stringLength(len(pubKey)) ret := make([]byte, length) r := marshalString(ret, sessionId) r[0] = msgUserAuthRequest r = r[1:] r = marshalString(r, user) r = marshalString(r, service) r = marshalString(r, method) r[0] = 1 r = r[1:] r = marshalString(r, algo) r = marshalString(r, pubKey) return ret } // safeString sanitises s according to RFC 4251, section 9.2. // All control characters except tab, carriage return and newline are // replaced by 0x20. func safeString(s string) string { out := []byte(s) for i, c := range out { if c < 0x20 && c != 0xd && c != 0xa && c != 0x9 { out[i] = 0x20 } } return string(out) } func appendU16(buf []byte, n uint16) []byte { return append(buf, byte(n>>8), byte(n)) } func appendU32(buf []byte, n uint32) []byte { return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) } func appendInt(buf []byte, n int) []byte { return appendU32(buf, uint32(n)) } func appendString(buf []byte, s string) []byte { buf = appendU32(buf, uint32(len(s))) buf = append(buf, s...) return buf } func appendBool(buf []byte, b bool) []byte { if b { buf = append(buf, 1) } else { buf = append(buf, 0) } return buf } // newCond is a helper to hide the fact that there is no usable zero // value for sync.Cond. func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } // window represents the buffer available to clients // wishing to write to a channel. type window struct { *sync.Cond win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 } // add adds win to the amount of window available // for consumers. func (w *window) add(win uint32) bool { // a zero sized window adjust is a noop. if win == 0 { return true } w.L.Lock() if w.win+win < win { w.L.Unlock() return false } w.win += win // It is unusual that multiple goroutines would be attempting to reserve // window space, but not guaranteed. Use broadcast to notify all waiters // that additional window is available. w.Broadcast() w.L.Unlock() return true } // reserve reserves win from the available window capacity. // If no capacity remains, reserve will block. reserve may // return less than requested. func (w *window) reserve(win uint32) uint32 { w.L.Lock() for w.win == 0 { w.Wait() } if w.win < win { win = w.win } w.win -= win w.L.Unlock() return win } ���������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/mempipe_test.go��������������������������������0000644�0000153�0000161�00000003566�12321736016�025210� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "io" "sync" "testing" ) // An in-memory packetConn. It is safe to call Close and writePacket // from different goroutines. type memTransport struct { eof bool pending [][]byte write *memTransport sync.Mutex *sync.Cond } func (t *memTransport) readPacket() ([]byte, error) { t.Lock() defer t.Unlock() for { if len(t.pending) > 0 { r := t.pending[0] t.pending = t.pending[1:] return r, nil } if t.eof { return nil, io.EOF } t.Cond.Wait() } } func (t *memTransport) Close() error { t.write.Lock() defer t.write.Unlock() if t.write.eof { return io.EOF } t.write.eof = true t.write.Cond.Broadcast() return nil } func (t *memTransport) writePacket(p []byte) error { t.write.Lock() defer t.write.Unlock() if t.write.eof { return io.EOF } t.write.pending = append(t.write.pending, p) t.write.Cond.Signal() return nil } func memPipe() (a, b packetConn) { t1 := memTransport{} t2 := memTransport{} t1.write = &t2 t2.write = &t1 t1.Cond = sync.NewCond(&t1.Mutex) t2.Cond = sync.NewCond(&t2.Mutex) return &t1, &t2 } func TestmemPipe(t *testing.T) { a, b := memPipe() if err := a.writePacket([]byte{42}); err != nil { t.Fatalf("writePacket: %v", err) } if err := a.Close(); err != nil { t.Fatal("Close: ", err) } p, err := b.readPacket() if err != nil { t.Fatal("readPacket: ", err) } if len(p) != 1 || p[0] != 42 { t.Fatalf("got %v, want {42}", p) } p, err = b.readPacket() if err != io.EOF { t.Fatalf("got %v, %v, want EOF", p, err) } } func TestDoubleClose(t *testing.T) { a, _ := memPipe() err := a.Close() if err != nil { t.Errorf("Close: %v", err) } err = a.Close() if err != io.EOF { t.Errorf("expect EOF on double close.") } } ������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/ssh/channel.go�������������������������������������0000644�0000153�0000161�00000034412�12321736016�024117� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 ssh import ( "errors" "fmt" "io" "sync" "sync/atomic" ) // extendedDataTypeCode identifies an OpenSSL extended data type. See RFC 4254, // section 5.2. type extendedDataTypeCode uint32 const ( // extendedDataStderr is the extended data type that is used for stderr. extendedDataStderr extendedDataTypeCode = 1 // minPacketLength defines the smallest valid packet minPacketLength = 9 // channelMaxPacketSize defines the maximum packet size advertised in open messages channelMaxPacketSize = 1 << 15 // RFC 4253 6.1, minimum 32 KiB // channelWindowSize defines the window size advertised in open messages channelWindowSize = 64 * channelMaxPacketSize // Like OpenSSH ) // A Channel is an ordered, reliable, duplex stream that is multiplexed over an // SSH connection. Channel.Read can return a ChannelRequest as an error. type Channel interface { // Accept accepts the channel creation request. Accept() error // Reject rejects the channel creation request. After calling this, no // other methods on the Channel may be called. If they are then the // peer is likely to signal a protocol error and drop the connection. Reject(reason RejectionReason, message string) error // Read may return a ChannelRequest as an error. Read(data []byte) (int, error) Write(data []byte) (int, error) Close() error // Stderr returns an io.Writer that writes to this channel with the // extended data type set to stderr. Stderr() io.Writer // AckRequest either sends an ack or nack to the channel request. AckRequest(ok bool) error // ChannelType returns the type of the channel, as supplied by the // client. ChannelType() string // ExtraData returns the arbitrary payload for this channel, as supplied // by the client. This data is specific to the channel type. ExtraData() []byte } // ChannelRequest represents a request sent on a channel, outside of the normal // stream of bytes. It may result from calling Read on a Channel. type ChannelRequest struct { Request string WantReply bool Payload []byte } func (c ChannelRequest) Error() string { return "ssh: channel request received" } // RejectionReason is an enumeration used when rejecting channel creation // requests. See RFC 4254, section 5.1. type RejectionReason uint32 const ( Prohibited RejectionReason = iota + 1 ConnectionFailed UnknownChannelType ResourceShortage ) // String converts the rejection reason to human readable form. func (r RejectionReason) String() string { switch r { case Prohibited: return "administratively prohibited" case ConnectionFailed: return "connect failed" case UnknownChannelType: return "unknown channel type" case ResourceShortage: return "resource shortage" } return fmt.Sprintf("unknown reason %d", int(r)) } type channel struct { packetConn // the underlying transport localId, remoteId uint32 remoteWin window maxPacket uint32 isClosed uint32 // atomic bool, non zero if true } func (c *channel) sendWindowAdj(n int) error { msg := windowAdjustMsg{ PeersId: c.remoteId, AdditionalBytes: uint32(n), } return c.writePacket(marshal(msgChannelWindowAdjust, msg)) } // sendEOF sends EOF to the remote side. RFC 4254 Section 5.3 func (c *channel) sendEOF() error { return c.writePacket(marshal(msgChannelEOF, channelEOFMsg{ PeersId: c.remoteId, })) } // sendClose informs the remote side of our intent to close the channel. func (c *channel) sendClose() error { return c.packetConn.writePacket(marshal(msgChannelClose, channelCloseMsg{ PeersId: c.remoteId, })) } func (c *channel) sendChannelOpenFailure(reason RejectionReason, message string) error { reject := channelOpenFailureMsg{ PeersId: c.remoteId, Reason: reason, Message: message, Language: "en", } return c.writePacket(marshal(msgChannelOpenFailure, reject)) } func (c *channel) writePacket(b []byte) error { if c.closed() { return io.EOF } if uint32(len(b)) > c.maxPacket { return fmt.Errorf("ssh: cannot write %d bytes, maxPacket is %d bytes", len(b), c.maxPacket) } return c.packetConn.writePacket(b) } func (c *channel) closed() bool { return atomic.LoadUint32(&c.isClosed) > 0 } func (c *channel) setClosed() bool { return atomic.CompareAndSwapUint32(&c.isClosed, 0, 1) } type serverChan struct { channel // immutable once created chanType string extraData []byte serverConn *ServerConn myWindow uint32 theyClosed bool // indicates the close msg has been received from the remote side theySentEOF bool isDead uint32 err error pendingRequests []ChannelRequest pendingData []byte head, length int // This lock is inferior to serverConn.lock cond *sync.Cond } func (c *serverChan) Accept() error { c.serverConn.lock.Lock() defer c.serverConn.lock.Unlock() if c.serverConn.err != nil { return c.serverConn.err } confirm := channelOpenConfirmMsg{ PeersId: c.remoteId, MyId: c.localId, MyWindow: c.myWindow, MaxPacketSize: c.maxPacket, } return c.writePacket(marshal(msgChannelOpenConfirm, confirm)) } func (c *serverChan) Reject(reason RejectionReason, message string) error { c.serverConn.lock.Lock() defer c.serverConn.lock.Unlock() if c.serverConn.err != nil { return c.serverConn.err } return c.sendChannelOpenFailure(reason, message) } func (c *serverChan) handlePacket(packet interface{}) { c.cond.L.Lock() defer c.cond.L.Unlock() switch packet := packet.(type) { case *channelRequestMsg: req := ChannelRequest{ Request: packet.Request, WantReply: packet.WantReply, Payload: packet.RequestSpecificData, } c.pendingRequests = append(c.pendingRequests, req) c.cond.Signal() case *channelCloseMsg: c.theyClosed = true c.cond.Signal() case *channelEOFMsg: c.theySentEOF = true c.cond.Signal() case *windowAdjustMsg: if !c.remoteWin.add(packet.AdditionalBytes) { panic("illegal window update") } default: panic("unknown packet type") } } func (c *serverChan) handleData(data []byte) { c.cond.L.Lock() defer c.cond.L.Unlock() // The other side should never send us more than our window. if len(data)+c.length > len(c.pendingData) { // TODO(agl): we should tear down the channel with a protocol // error. return } c.myWindow -= uint32(len(data)) for i := 0; i < 2; i++ { tail := c.head + c.length if tail >= len(c.pendingData) { tail -= len(c.pendingData) } n := copy(c.pendingData[tail:], data) data = data[n:] c.length += n } c.cond.Signal() } func (c *serverChan) Stderr() io.Writer { return extendedDataChannel{c: c, t: extendedDataStderr} } // extendedDataChannel is an io.Writer that writes any data to c as extended // data of the given type. type extendedDataChannel struct { t extendedDataTypeCode c *serverChan } func (edc extendedDataChannel) Write(data []byte) (n int, err error) { const headerLength = 13 // 1 byte message type, 4 bytes remoteId, 4 bytes extended message type, 4 bytes data length c := edc.c for len(data) > 0 { space := min(c.maxPacket-headerLength, len(data)) if space, err = c.getWindowSpace(space); err != nil { return 0, err } todo := data if uint32(len(todo)) > space { todo = todo[:space] } packet := make([]byte, headerLength+len(todo)) packet[0] = msgChannelExtendedData marshalUint32(packet[1:], c.remoteId) marshalUint32(packet[5:], uint32(edc.t)) marshalUint32(packet[9:], uint32(len(todo))) copy(packet[13:], todo) if err = c.writePacket(packet); err != nil { return } n += len(todo) data = data[len(todo):] } return } func (c *serverChan) Read(data []byte) (n int, err error) { n, err, windowAdjustment := c.read(data) if windowAdjustment > 0 { packet := marshal(msgChannelWindowAdjust, windowAdjustMsg{ PeersId: c.remoteId, AdditionalBytes: windowAdjustment, }) err = c.writePacket(packet) } return } func (c *serverChan) read(data []byte) (n int, err error, windowAdjustment uint32) { c.cond.L.Lock() defer c.cond.L.Unlock() if c.err != nil { return 0, c.err, 0 } for { if c.theySentEOF || c.theyClosed || c.dead() { return 0, io.EOF, 0 } if len(c.pendingRequests) > 0 { req := c.pendingRequests[0] if len(c.pendingRequests) == 1 { c.pendingRequests = nil } else { oldPendingRequests := c.pendingRequests c.pendingRequests = make([]ChannelRequest, len(oldPendingRequests)-1) copy(c.pendingRequests, oldPendingRequests[1:]) } return 0, req, 0 } if c.length > 0 { tail := min(uint32(c.head+c.length), len(c.pendingData)) n = copy(data, c.pendingData[c.head:tail]) c.head += n c.length -= n if c.head == len(c.pendingData) { c.head = 0 } windowAdjustment = uint32(len(c.pendingData)-c.length) - c.myWindow if windowAdjustment < uint32(len(c.pendingData)/2) { windowAdjustment = 0 } c.myWindow += windowAdjustment return } c.cond.Wait() } panic("unreachable") } // getWindowSpace takes, at most, max bytes of space from the peer's window. It // returns the number of bytes actually reserved. func (c *serverChan) getWindowSpace(max uint32) (uint32, error) { if c.dead() || c.closed() { return 0, io.EOF } return c.remoteWin.reserve(max), nil } func (c *serverChan) dead() bool { return atomic.LoadUint32(&c.isDead) > 0 } func (c *serverChan) setDead() { atomic.StoreUint32(&c.isDead, 1) } func (c *serverChan) Write(data []byte) (n int, err error) { const headerLength = 9 // 1 byte message type, 4 bytes remoteId, 4 bytes data length for len(data) > 0 { space := min(c.maxPacket-headerLength, len(data)) if space, err = c.getWindowSpace(space); err != nil { return 0, err } todo := data if uint32(len(todo)) > space { todo = todo[:space] } packet := make([]byte, headerLength+len(todo)) packet[0] = msgChannelData marshalUint32(packet[1:], c.remoteId) marshalUint32(packet[5:], uint32(len(todo))) copy(packet[9:], todo) if err = c.writePacket(packet); err != nil { return } n += len(todo) data = data[len(todo):] } return } // Close signals the intent to close the channel. func (c *serverChan) Close() error { c.serverConn.lock.Lock() defer c.serverConn.lock.Unlock() if c.serverConn.err != nil { return c.serverConn.err } if !c.setClosed() { return errors.New("ssh: channel already closed") } return c.sendClose() } func (c *serverChan) AckRequest(ok bool) error { c.serverConn.lock.Lock() defer c.serverConn.lock.Unlock() if c.serverConn.err != nil { return c.serverConn.err } if !ok { ack := channelRequestFailureMsg{ PeersId: c.remoteId, } return c.writePacket(marshal(msgChannelFailure, ack)) } ack := channelRequestSuccessMsg{ PeersId: c.remoteId, } return c.writePacket(marshal(msgChannelSuccess, ack)) } func (c *serverChan) ChannelType() string { return c.chanType } func (c *serverChan) ExtraData() []byte { return c.extraData } // A clientChan represents a single RFC 4254 channel multiplexed // over a SSH connection. type clientChan struct { channel stdin *chanWriter stdout *chanReader stderr *chanReader msg chan interface{} } // newClientChan returns a partially constructed *clientChan // using the local id provided. To be usable clientChan.remoteId // needs to be assigned once known. func newClientChan(cc packetConn, id uint32) *clientChan { c := &clientChan{ channel: channel{ packetConn: cc, localId: id, remoteWin: window{Cond: newCond()}, }, msg: make(chan interface{}, 16), } c.stdin = &chanWriter{ channel: &c.channel, } c.stdout = &chanReader{ channel: &c.channel, buffer: newBuffer(), } c.stderr = &chanReader{ channel: &c.channel, buffer: newBuffer(), } return c } // waitForChannelOpenResponse, if successful, fills out // the remoteId and records any initial window advertisement. func (c *clientChan) waitForChannelOpenResponse() error { switch msg := (<-c.msg).(type) { case *channelOpenConfirmMsg: if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { return errors.New("ssh: invalid MaxPacketSize from peer") } // fixup remoteId field c.remoteId = msg.MyId c.maxPacket = msg.MaxPacketSize c.remoteWin.add(msg.MyWindow) return nil case *channelOpenFailureMsg: return errors.New(safeString(msg.Message)) } return errors.New("ssh: unexpected packet") } // Close signals the intent to close the channel. func (c *clientChan) Close() error { if !c.setClosed() { return errors.New("ssh: channel already closed") } c.stdout.eof() c.stderr.eof() return c.sendClose() } // A chanWriter represents the stdin of a remote process. type chanWriter struct { *channel // indicates the writer has been closed. eof is owned by the // caller of Write/Close. eof bool } // Write writes data to the remote process's standard input. func (w *chanWriter) Write(data []byte) (written int, err error) { const headerLength = 9 // 1 byte message type, 4 bytes remoteId, 4 bytes data length for len(data) > 0 { if w.eof || w.closed() { err = io.EOF return } // never send more data than maxPacket even if // there is sufficient window. n := min(w.maxPacket-headerLength, len(data)) r := w.remoteWin.reserve(n) n = r remoteId := w.remoteId packet := []byte{ msgChannelData, byte(remoteId >> 24), byte(remoteId >> 16), byte(remoteId >> 8), byte(remoteId), byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n), } if err = w.writePacket(append(packet, data[:n]...)); err != nil { break } data = data[n:] written += int(n) } return } func min(a uint32, b int) uint32 { if a < uint32(b) { return a } return uint32(b) } func (w *chanWriter) Close() error { w.eof = true return w.sendEOF() } // A chanReader represents stdout or stderr of a remote process. type chanReader struct { *channel // the channel backing this reader *buffer } // Read reads data from the remote process's stdout or stderr. func (r *chanReader) Read(buf []byte) (int, error) { n, err := r.buffer.Read(buf) if err != nil { if err == io.EOF { return n, err } return 0, err } err = r.sendWindowAdj(n) if err == io.EOF && n > 0 { // sendWindowAdjust can return io.EOF if the remote peer has // closed the connection, however we want to defer forwarding io.EOF to the // caller of Read until the buffer has been drained. err = nil } return n, err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/.hgignore������������������������������������������0000644�0000153�0000161�00000000030�12321736016�023153� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������syntax:glob last-change ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/sha3/����������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022226� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/sha3/sha3.go���������������������������������������0000644�0000153�0000161�00000021576�12321735647�023426� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 sha3 implements the SHA3 hash algorithm (formerly called Keccak) chosen by NIST in 2012. // This file provides a SHA3 implementation which implements the standard hash.Hash interface. // Writing input data, including padding, and reading output data are computed in this file. // Note that the current implementation can compute the hash of an integral number of bytes only. // This is a consequence of the hash interface in which a buffer of bytes is passed in. // The internals of the Keccak-f function are computed in keccakf.go. // For the detailed specification, refer to the Keccak web site (http://keccak.noekeon.org/). package sha3 import ( "encoding/binary" "hash" ) // laneSize is the size in bytes of each "lane" of the internal state of SHA3 (5 * 5 * 8). // Note that changing this size would requires using a type other than uint64 to store each lane. const laneSize = 8 // sliceSize represents the dimensions of the internal state, a square matrix of // sliceSize ** 2 lanes. This is the size of both the "rows" and "columns" dimensions in the // terminology of the SHA3 specification. const sliceSize = 5 // numLanes represents the total number of lanes in the state. const numLanes = sliceSize * sliceSize // stateSize is the size in bytes of the internal state of SHA3 (5 * 5 * WSize). const stateSize = laneSize * numLanes // digest represents the partial evaluation of a checksum. // Note that capacity, and not outputSize, is the critical security parameter, as SHA3 can output // an arbitrary number of bytes for any given capacity. The Keccak proposal recommends that // capacity = 2*outputSize to ensure that finding a collision of size outputSize requires // O(2^{outputSize/2}) computations (the birthday lower bound). Future standards may modify the // capacity/outputSize ratio to allow for more output with lower cryptographic security. type digest struct { a [numLanes]uint64 // main state of the hash outputSize int // desired output size in bytes capacity int // number of bytes to leave untouched during squeeze/absorb absorbed int // number of bytes absorbed thus far } // minInt returns the lesser of two integer arguments, to simplify the absorption routine. func minInt(v1, v2 int) int { if v1 <= v2 { return v1 } return v2 } // rate returns the number of bytes of the internal state which can be absorbed or squeezed // in between calls to the permutation function. func (d *digest) rate() int { return stateSize - d.capacity } // Reset clears the internal state by zeroing bytes in the state buffer. // This can be skipped for a newly-created hash state; the default zero-allocated state is correct. func (d *digest) Reset() { d.absorbed = 0 for i := range d.a { d.a[i] = 0 } } // BlockSize, required by the hash.Hash interface, does not have a standard intepretation // for a sponge-based construction like SHA3. We return the data rate: the number of bytes which // can be absorbed per invocation of the permutation function. For Merkle-DamgÃ¥rd based hashes // (ie SHA1, SHA2, MD5) the output size of the internal compression function is returned. // We consider this to be roughly equivalent because it represents the number of bytes of output // produced per cryptographic operation. func (d *digest) BlockSize() int { return d.rate() } // Size returns the output size of the hash function in bytes. func (d *digest) Size() int { return d.outputSize } // unalignedAbsorb is a helper function for Write, which absorbs data that isn't aligned with an // 8-byte lane. This requires shifting the individual bytes into position in a uint64. func (d *digest) unalignedAbsorb(p []byte) { var t uint64 for i := len(p) - 1; i >= 0; i-- { t <<= 8 t |= uint64(p[i]) } offset := (d.absorbed) % d.rate() t <<= 8 * uint(offset%laneSize) d.a[offset/laneSize] ^= t d.absorbed += len(p) } // Write "absorbs" bytes into the state of the SHA3 hash, updating as needed when the sponge // "fills up" with rate() bytes. Since lanes are stored internally as type uint64, this requires // converting the incoming bytes into uint64s using a little endian interpretation. This // implementation is optimized for large, aligned writes of multiples of 8 bytes (laneSize). // Non-aligned or uneven numbers of bytes require shifting and are slower. func (d *digest) Write(p []byte) (int, error) { // An initial offset is needed if the we aren't absorbing to the first lane initially. offset := d.absorbed % d.rate() toWrite := len(p) // The first lane may need to absorb unaligned and/or incomplete data. if (offset%laneSize != 0 || len(p) < 8) && len(p) > 0 { toAbsorb := minInt(laneSize-(offset%laneSize), len(p)) d.unalignedAbsorb(p[:toAbsorb]) p = p[toAbsorb:] offset = (d.absorbed) % d.rate() // For every rate() bytes absorbed, the state must be permuted via the F Function. if (d.absorbed)%d.rate() == 0 { keccakF(&d.a) } } // This loop should absorb the bulk of the data into full, aligned lanes. // It will call the update function as necessary. for len(p) > 7 { firstLane := offset / laneSize lastLane := minInt(d.rate()/laneSize, firstLane+len(p)/laneSize) // This inner loop absorbs input bytes into the state in groups of 8, converted to uint64s. for lane := firstLane; lane < lastLane; lane++ { d.a[lane] ^= binary.LittleEndian.Uint64(p[:laneSize]) p = p[laneSize:] } d.absorbed += (lastLane - firstLane) * laneSize // For every rate() bytes absorbed, the state must be permuted via the F Function. if (d.absorbed)%d.rate() == 0 { keccakF(&d.a) } offset = 0 } // If there are insufficient bytes to fill the final lane, an unaligned absorption. // This should always start at a correct lane boundary though, or else it would be caught // by the uneven opening lane case above. if len(p) > 0 { d.unalignedAbsorb(p) } return toWrite, nil } // pad computes the SHA3 padding scheme based on the number of bytes absorbed. // The padding is a 1 bit, followed by an arbitrary number of 0s and then a final 1 bit, such that // the input bits plus padding bits are a multiple of rate(). Adding the padding simply requires // xoring an opening and closing bit into the appropriate lanes. func (d *digest) pad() { offset := d.absorbed % d.rate() // The opening pad bit must be shifted into position based on the number of bytes absorbed padOpenLane := offset / laneSize d.a[padOpenLane] ^= 0x0000000000000001 << uint(8*(offset%laneSize)) // The closing padding bit is always in the last position padCloseLane := (d.rate() / laneSize) - 1 d.a[padCloseLane] ^= 0x8000000000000000 } // finalize prepares the hash to output data by padding and one final permutation of the state. func (d *digest) finalize() { d.pad() keccakF(&d.a) } // squeeze outputs an arbitrary number of bytes from the hash state. // Squeezing can require multiple calls to the F function (one per rate() bytes squeezed), // although this is not the case for standard SHA3 parameters. This implementation only supports // squeezing a single time, subsequent squeezes may lose alignment. Future implementations // may wish to support multiple squeeze calls, for example to support use as a PRNG. func (d *digest) squeeze(in []byte, toSqueeze int) []byte { // Because we read in blocks of laneSize, we need enough room to read // an integral number of lanes needed := toSqueeze + (laneSize-toSqueeze%laneSize)%laneSize if cap(in)-len(in) < needed { newIn := make([]byte, len(in), len(in)+needed) copy(newIn, in) in = newIn } out := in[len(in) : len(in)+needed] for len(out) > 0 { for i := 0; i < d.rate() && len(out) > 0; i += laneSize { binary.LittleEndian.PutUint64(out[:], d.a[i/laneSize]) out = out[laneSize:] } if len(out) > 0 { keccakF(&d.a) } } return in[:len(in)+toSqueeze] // Re-slice in case we wrote extra data. } // Sum applies padding to the hash state and then squeezes out the desired nubmer of output bytes. func (d *digest) Sum(in []byte) []byte { // Make a copy of the original hash so that caller can keep writing and summing. dup := *d dup.finalize() return dup.squeeze(in, dup.outputSize) } // The NewKeccakX constructors enable initializing a hash in any of the four recommend sizes // from the Keccak specification, all of which set capacity=2*outputSize. Note that the final // NIST standard for SHA3 may specify different input/output lengths. // The output size is indicated in bits but converted into bytes internally. func NewKeccak224() hash.Hash { return &digest{outputSize: 224 / 8, capacity: 2 * 224 / 8} } func NewKeccak256() hash.Hash { return &digest{outputSize: 256 / 8, capacity: 2 * 256 / 8} } func NewKeccak384() hash.Hash { return &digest{outputSize: 384 / 8, capacity: 2 * 384 / 8} } func NewKeccak512() hash.Hash { return &digest{outputSize: 512 / 8, capacity: 2 * 512 / 8} } ����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/sha3/keccakf.go������������������������������������0000644�0000153�0000161�00000007576�12321735647�024163� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 sha3 // This file implements the core Keccak permutation function necessary for computing SHA3. // This is implemented in a separate file to allow for replacement by an optimized implementation. // Nothing in this package is exported. // For the detailed specification, refer to the Keccak web site (http://keccak.noekeon.org/). // rc stores the round constants for use in the ι step. var rc = [...]uint64{ 0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A, 0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A, 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, } // keccakF computes the complete Keccak-f function consisting of 24 rounds with a different // constant (rc) in each round. This implementation fully unrolls the round function to avoid // inner loops, as well as pre-calculating shift offsets. func keccakF(a *[numLanes]uint64) { var t, bc0, bc1, bc2, bc3, bc4 uint64 for _, roundConstant := range rc { // θ step bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20] bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21] bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22] bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23] bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24] t = bc4 ^ (bc1<<1 ^ bc1>>63) a[0] ^= t a[5] ^= t a[10] ^= t a[15] ^= t a[20] ^= t t = bc0 ^ (bc2<<1 ^ bc2>>63) a[1] ^= t a[6] ^= t a[11] ^= t a[16] ^= t a[21] ^= t t = bc1 ^ (bc3<<1 ^ bc3>>63) a[2] ^= t a[7] ^= t a[12] ^= t a[17] ^= t a[22] ^= t t = bc2 ^ (bc4<<1 ^ bc4>>63) a[3] ^= t a[8] ^= t a[13] ^= t a[18] ^= t a[23] ^= t t = bc3 ^ (bc0<<1 ^ bc0>>63) a[4] ^= t a[9] ^= t a[14] ^= t a[19] ^= t a[24] ^= t // Ï and Ï€ steps t = a[1] t, a[10] = a[10], t<<1^t>>(64-1) t, a[7] = a[7], t<<3^t>>(64-3) t, a[11] = a[11], t<<6^t>>(64-6) t, a[17] = a[17], t<<10^t>>(64-10) t, a[18] = a[18], t<<15^t>>(64-15) t, a[3] = a[3], t<<21^t>>(64-21) t, a[5] = a[5], t<<28^t>>(64-28) t, a[16] = a[16], t<<36^t>>(64-36) t, a[8] = a[8], t<<45^t>>(64-45) t, a[21] = a[21], t<<55^t>>(64-55) t, a[24] = a[24], t<<2^t>>(64-2) t, a[4] = a[4], t<<14^t>>(64-14) t, a[15] = a[15], t<<27^t>>(64-27) t, a[23] = a[23], t<<41^t>>(64-41) t, a[19] = a[19], t<<56^t>>(64-56) t, a[13] = a[13], t<<8^t>>(64-8) t, a[12] = a[12], t<<25^t>>(64-25) t, a[2] = a[2], t<<43^t>>(64-43) t, a[20] = a[20], t<<62^t>>(64-62) t, a[14] = a[14], t<<18^t>>(64-18) t, a[22] = a[22], t<<39^t>>(64-39) t, a[9] = a[9], t<<61^t>>(64-61) t, a[6] = a[6], t<<20^t>>(64-20) a[1] = t<<44 ^ t>>(64-44) // χ step bc0 = a[0] bc1 = a[1] bc2 = a[2] bc3 = a[3] bc4 = a[4] a[0] ^= bc2 &^ bc1 a[1] ^= bc3 &^ bc2 a[2] ^= bc4 &^ bc3 a[3] ^= bc0 &^ bc4 a[4] ^= bc1 &^ bc0 bc0 = a[5] bc1 = a[6] bc2 = a[7] bc3 = a[8] bc4 = a[9] a[5] ^= bc2 &^ bc1 a[6] ^= bc3 &^ bc2 a[7] ^= bc4 &^ bc3 a[8] ^= bc0 &^ bc4 a[9] ^= bc1 &^ bc0 bc0 = a[10] bc1 = a[11] bc2 = a[12] bc3 = a[13] bc4 = a[14] a[10] ^= bc2 &^ bc1 a[11] ^= bc3 &^ bc2 a[12] ^= bc4 &^ bc3 a[13] ^= bc0 &^ bc4 a[14] ^= bc1 &^ bc0 bc0 = a[15] bc1 = a[16] bc2 = a[17] bc3 = a[18] bc4 = a[19] a[15] ^= bc2 &^ bc1 a[16] ^= bc3 &^ bc2 a[17] ^= bc4 &^ bc3 a[18] ^= bc0 &^ bc4 a[19] ^= bc1 &^ bc0 bc0 = a[20] bc1 = a[21] bc2 = a[22] bc3 = a[23] bc4 = a[24] a[20] ^= bc2 &^ bc1 a[21] ^= bc3 &^ bc2 a[22] ^= bc4 &^ bc3 a[23] ^= bc0 &^ bc4 a[24] ^= bc1 &^ bc0 // ι step a[0] ^= roundConstant } } ����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/sha3/sha3_test.go����������������������������������0000644�0000153�0000161�00000022400�12321735647�024450� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 sha3 // These tests are a subset of those provided by the Keccak web site(http://keccak.noekeon.org/). import ( "bytes" "encoding/hex" "fmt" "hash" "strings" "testing" ) // testDigests maintains a digest state of each standard type. var testDigests = map[string]*digest{ "Keccak224": {outputSize: 224 / 8, capacity: 2 * 224 / 8}, "Keccak256": {outputSize: 256 / 8, capacity: 2 * 256 / 8}, "Keccak384": {outputSize: 384 / 8, capacity: 2 * 384 / 8}, "Keccak512": {outputSize: 512 / 8, capacity: 2 * 512 / 8}, } // testVector represents a test input and expected outputs from multiple algorithm variants. type testVector struct { desc string input []byte repeat int // input will be concatenated the input this many times. want map[string]string } // decodeHex converts an hex-encoded string into a raw byte string. func decodeHex(s string) []byte { b, err := hex.DecodeString(s) if err != nil { panic(err) } return b } // shortTestVectors stores a series of short testVectors. // Inputs of 8, 248, and 264 bits from http://keccak.noekeon.org/ are included below. // The standard defines additional test inputs of all sizes between 0 and 2047 bits. // Because the current implementation can only handle an integral number of bytes, // most of the standard test inputs can't be used. var shortKeccakTestVectors = []testVector{ { desc: "short-8b", input: decodeHex("CC"), repeat: 1, want: map[string]string{ "Keccak224": "A9CAB59EB40A10B246290F2D6086E32E3689FAF1D26B470C899F2802", "Keccak256": "EEAD6DBFC7340A56CAEDC044696A168870549A6A7F6F56961E84A54BD9970B8A", "Keccak384": "1B84E62A46E5A201861754AF5DC95C4A1A69CAF4A796AE405680161E29572641F5FA1E8641D7958336EE7B11C58F73E9", "Keccak512": "8630C13CBD066EA74BBE7FE468FEC1DEE10EDC1254FB4C1B7C5FD69B646E44160B8CE01D05A0908CA790DFB080F4B513BC3B6225ECE7A810371441A5AC666EB9", }, }, { desc: "short-248b", input: decodeHex("84FB51B517DF6C5ACCB5D022F8F28DA09B10232D42320FFC32DBECC3835B29"), repeat: 1, want: map[string]string{ "Keccak224": "81AF3A7A5BD4C1F948D6AF4B96F93C3B0CF9C0E7A6DA6FCD71EEC7F6", "Keccak256": "D477FB02CAAA95B3280EC8EE882C29D9E8A654B21EF178E0F97571BF9D4D3C1C", "Keccak384": "503DCAA4ADDA5A9420B2E436DD62D9AB2E0254295C2982EF67FCE40F117A2400AB492F7BD5D133C6EC2232268BC27B42", "Keccak512": "9D8098D8D6EDBBAA2BCFC6FB2F89C3EAC67FEC25CDFE75AA7BD570A648E8C8945FF2EC280F6DCF73386109155C5BBC444C707BB42EAB873F5F7476657B1BC1A8", }, }, { desc: "short-264b", input: decodeHex("DE8F1B3FAA4B7040ED4563C3B8E598253178E87E4D0DF75E4FF2F2DEDD5A0BE046"), repeat: 1, want: map[string]string{ "Keccak224": "F217812E362EC64D4DC5EACFABC165184BFA456E5C32C2C7900253D0", "Keccak256": "E78C421E6213AFF8DE1F025759A4F2C943DB62BBDE359C8737E19B3776ED2DD2", "Keccak384": "CF38764973F1EC1C34B5433AE75A3AAD1AAEF6AB197850C56C8617BCD6A882F6666883AC17B2DCCDBAA647075D0972B5", "Keccak512": "9A7688E31AAF40C15575FC58C6B39267AAD3722E696E518A9945CF7F7C0FEA84CB3CB2E9F0384A6B5DC671ADE7FB4D2B27011173F3EEEAF17CB451CF26542031", }, }, } // longTestVectors stores longer testVectors (currently only one). // The computed test vector is 64 MiB long and is a truncated version of the // ExtremelyLongMsgKAT taken from http://keccak.noekeon.org/. var longKeccakTestVectors = []testVector{ { desc: "long-64MiB", input: []byte("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno"), repeat: 1024 * 1024, want: map[string]string{ "Keccak224": "50E35E40980FEEFF1EA490957B0E970257F75EA0D410EE0F0B8A7A58", "Keccak256": "5015A4935F0B51E091C6550A94DCD262C08998232CCAA22E7F0756DEAC0DC0D0", "Keccak384": "7907A8D0FAA7BC6A90FE14C6C958C956A0877E751455D8F13ACDB96F144B5896E716C06EC0CB56557A94EF5C3355F6F3", "Keccak512": "3EC327D6759F769DEB74E80CA70C831BC29CAB048A4BF4190E4A1DD5C6507CF2B4B58937FDE81D36014E7DFE1B1DD8B0F27CB7614F9A645FEC114F1DAAEFC056", }, }, } // TestKeccakVectors checks that correct output is produced for a set of known testVectors. func TestKeccakVectors(t *testing.T) { testCases := append([]testVector{}, shortKeccakTestVectors...) if !testing.Short() { testCases = append(testCases, longKeccakTestVectors...) } for _, tc := range testCases { for alg, want := range tc.want { d := testDigests[alg] d.Reset() for i := 0; i < tc.repeat; i++ { d.Write(tc.input) } got := strings.ToUpper(hex.EncodeToString(d.Sum(nil))) if got != want { t.Errorf("%s, alg=%s\ngot %q, want %q", tc.desc, alg, got, want) } } } } // dumpState is a debugging function to pretty-print the internal state of the hash. func (d *digest) dumpState() { fmt.Printf("SHA3 hash, %d B output, %d B capacity (%d B rate)\n", d.outputSize, d.capacity, d.rate()) fmt.Printf("Internal state after absorbing %d B:\n", d.absorbed) for x := 0; x < sliceSize; x++ { for y := 0; y < sliceSize; y++ { fmt.Printf("%v, ", d.a[x*sliceSize+y]) } fmt.Println("") } } // TestUnalignedWrite tests that writing data in an arbitrary pattern with small input buffers. func TestUnalignedWrite(t *testing.T) { buf := sequentialBytes(0x10000) for alg, d := range testDigests { d.Reset() d.Write(buf) want := d.Sum(nil) d.Reset() for i := 0; i < len(buf); { // Cycle through offsets which make a 137 byte sequence. // Because 137 is prime this sequence should exercise all corner cases. offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1} for _, j := range offsets { j = minInt(j, len(buf)-i) d.Write(buf[i : i+j]) i += j } } got := d.Sum(nil) if !bytes.Equal(got, want) { t.Errorf("Unaligned writes, alg=%s\ngot %q, want %q", alg, got, want) } } } func TestAppend(t *testing.T) { d := NewKeccak224() for capacity := 2; capacity < 64; capacity += 64 { // The first time around the loop, Sum will have to reallocate. // The second time, it will not. buf := make([]byte, 2, capacity) d.Reset() d.Write([]byte{0xcc}) buf = d.Sum(buf) expected := "0000A9CAB59EB40A10B246290F2D6086E32E3689FAF1D26B470C899F2802" if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { t.Errorf("got %s, want %s", got, expected) } } } func TestAppendNoRealloc(t *testing.T) { buf := make([]byte, 1, 200) d := NewKeccak224() d.Write([]byte{0xcc}) buf = d.Sum(buf) expected := "00A9CAB59EB40A10B246290F2D6086E32E3689FAF1D26B470C899F2802" if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { t.Errorf("got %s, want %s", got, expected) } } // sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing. func sequentialBytes(size int) []byte { result := make([]byte, size) for i := range result { result[i] = byte(i) } return result } // benchmarkBlockWrite tests the speed of writing data and never calling the permutation function. func benchmarkBlockWrite(b *testing.B, d *digest) { b.StopTimer() d.Reset() // Write all but the last byte of a block, to ensure that the permutation is not called. data := sequentialBytes(d.rate() - 1) b.SetBytes(int64(len(data))) b.StartTimer() for i := 0; i < b.N; i++ { d.absorbed = 0 // Reset absorbed to avoid ever calling the permutation function d.Write(data) } b.StopTimer() d.Reset() } // BenchmarkPermutationFunction measures the speed of the permutation function with no input data. func BenchmarkPermutationFunction(b *testing.B) { b.SetBytes(int64(stateSize)) var lanes [numLanes]uint64 for i := 0; i < b.N; i++ { keccakF(&lanes) } } // BenchmarkSingleByteWrite tests the latency from writing a single byte func BenchmarkSingleByteWrite(b *testing.B) { b.StopTimer() d := testDigests["Keccak512"] d.Reset() data := sequentialBytes(1) //1 byte buffer b.SetBytes(int64(d.rate()) - 1) b.StartTimer() for i := 0; i < b.N; i++ { d.absorbed = 0 // Reset absorbed to avoid ever calling the permutation function // Write all but the last byte of a block, one byte at a time. for j := 0; j < d.rate()-1; j++ { d.Write(data) } } b.StopTimer() d.Reset() } // BenchmarkSingleByteX measures the block write speed for each size of the digest. func BenchmarkBlockWrite512(b *testing.B) { benchmarkBlockWrite(b, testDigests["Keccak512"]) } func BenchmarkBlockWrite384(b *testing.B) { benchmarkBlockWrite(b, testDigests["Keccak384"]) } func BenchmarkBlockWrite256(b *testing.B) { benchmarkBlockWrite(b, testDigests["Keccak256"]) } func BenchmarkBlockWrite224(b *testing.B) { benchmarkBlockWrite(b, testDigests["Keccak224"]) } // benchmarkBulkHash tests the speed to hash a 16 KiB buffer. func benchmarkBulkHash(b *testing.B, h hash.Hash) { b.StopTimer() h.Reset() size := 1 << 14 data := sequentialBytes(size) b.SetBytes(int64(size)) b.StartTimer() var digest []byte for i := 0; i < b.N; i++ { h.Write(data) digest = h.Sum(digest[:0]) } b.StopTimer() h.Reset() } // benchmarkBulkKeccakX test the speed to hash a 16 KiB buffer by calling benchmarkBulkHash. func BenchmarkBulkKeccak512(b *testing.B) { benchmarkBulkHash(b, NewKeccak512()) } func BenchmarkBulkKeccak384(b *testing.B) { benchmarkBulkHash(b, NewKeccak384()) } func BenchmarkBulkKeccak256(b *testing.B) { benchmarkBulkHash(b, NewKeccak256()) } func BenchmarkBulkKeccak224(b *testing.B) { benchmarkBulkHash(b, NewKeccak224()) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/scrypt/��������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022714� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/scrypt/scrypt_test.go������������������������������0000644�0000153�0000161�00000010547�12321735647�025635� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 scrypt import ( "bytes" "testing" ) type testVector struct { password string salt string N, r, p int output []byte } var good = []testVector{ { "password", "salt", 2, 10, 10, []byte{ 0x48, 0x2c, 0x85, 0x8e, 0x22, 0x90, 0x55, 0xe6, 0x2f, 0x41, 0xe0, 0xec, 0x81, 0x9a, 0x5e, 0xe1, 0x8b, 0xdb, 0x87, 0x25, 0x1a, 0x53, 0x4f, 0x75, 0xac, 0xd9, 0x5a, 0xc5, 0xe5, 0xa, 0xa1, 0x5f, }, }, { "password", "salt", 16, 100, 100, []byte{ 0x88, 0xbd, 0x5e, 0xdb, 0x52, 0xd1, 0xdd, 0x0, 0x18, 0x87, 0x72, 0xad, 0x36, 0x17, 0x12, 0x90, 0x22, 0x4e, 0x74, 0x82, 0x95, 0x25, 0xb1, 0x8d, 0x73, 0x23, 0xa5, 0x7f, 0x91, 0x96, 0x3c, 0x37, }, }, { "this is a long \000 password", "and this is a long \000 salt", 16384, 8, 1, []byte{ 0xc3, 0xf1, 0x82, 0xee, 0x2d, 0xec, 0x84, 0x6e, 0x70, 0xa6, 0x94, 0x2f, 0xb5, 0x29, 0x98, 0x5a, 0x3a, 0x09, 0x76, 0x5e, 0xf0, 0x4c, 0x61, 0x29, 0x23, 0xb1, 0x7f, 0x18, 0x55, 0x5a, 0x37, 0x07, 0x6d, 0xeb, 0x2b, 0x98, 0x30, 0xd6, 0x9d, 0xe5, 0x49, 0x26, 0x51, 0xe4, 0x50, 0x6a, 0xe5, 0x77, 0x6d, 0x96, 0xd4, 0x0f, 0x67, 0xaa, 0xee, 0x37, 0xe1, 0x77, 0x7b, 0x8a, 0xd5, 0xc3, 0x11, 0x14, 0x32, 0xbb, 0x3b, 0x6f, 0x7e, 0x12, 0x64, 0x40, 0x18, 0x79, 0xe6, 0x41, 0xae, }, }, { "p", "s", 2, 1, 1, []byte{ 0x48, 0xb0, 0xd2, 0xa8, 0xa3, 0x27, 0x26, 0x11, 0x98, 0x4c, 0x50, 0xeb, 0xd6, 0x30, 0xaf, 0x52, }, }, { "", "", 16, 1, 1, []byte{ 0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20, 0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a, 0x04, 0x97, 0xf1, 0x6b, 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8, 0xdf, 0xdf, 0xfa, 0x3f, 0xed, 0xe2, 0x14, 0x42, 0xfc, 0xd0, 0x06, 0x9d, 0xed, 0x09, 0x48, 0xf8, 0x32, 0x6a, 0x75, 0x3a, 0x0f, 0xc8, 0x1f, 0x17, 0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, 0x36, 0x28, 0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, 0x06, }, }, { "password", "NaCl", 1024, 8, 16, []byte{ 0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00, 0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9, 0xfe, 0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30, 0xe7, 0x73, 0x76, 0x63, 0x4b, 0x37, 0x31, 0x62, 0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88, 0x6f, 0xf1, 0x09, 0x27, 0x9d, 0x98, 0x30, 0xda, 0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d, 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40, }, }, { "pleaseletmein", "SodiumChloride", 16384, 8, 1, []byte{ 0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48, 0x46, 0x1c, 0x06, 0xcd, 0x81, 0xfd, 0x38, 0xeb, 0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e, 0xa9, 0xb5, 0x43, 0xf6, 0x54, 0x5d, 0xa1, 0xf2, 0xd5, 0x43, 0x29, 0x55, 0x61, 0x3f, 0x0f, 0xcf, 0x62, 0xd4, 0x97, 0x05, 0x24, 0x2a, 0x9a, 0xf9, 0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, 0x1e, 0x40, 0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, 0x87, }, }, /* // Disabled: needs 1 GiB RAM and takes too long for a simple test. { "pleaseletmein", "SodiumChloride", 1048576, 8, 1, []byte{ 0x21, 0x01, 0xcb, 0x9b, 0x6a, 0x51, 0x1a, 0xae, 0xad, 0xdb, 0xbe, 0x09, 0xcf, 0x70, 0xf8, 0x81, 0xec, 0x56, 0x8d, 0x57, 0x4a, 0x2f, 0xfd, 0x4d, 0xab, 0xe5, 0xee, 0x98, 0x20, 0xad, 0xaa, 0x47, 0x8e, 0x56, 0xfd, 0x8f, 0x4b, 0xa5, 0xd0, 0x9f, 0xfa, 0x1c, 0x6d, 0x92, 0x7c, 0x40, 0xf4, 0xc3, 0x37, 0x30, 0x40, 0x49, 0xe8, 0xa9, 0x52, 0xfb, 0xcb, 0xf4, 0x5c, 0x6f, 0xa7, 0x7a, 0x41, 0xa4, }, }, */ } var bad = []testVector{ {"p", "s", 0, 1, 1, nil}, // N == 0 {"p", "s", 1, 1, 1, nil}, // N == 1 {"p", "s", 7, 8, 1, nil}, // N is not power of 2 {"p", "s", 16, maxInt / 2, maxInt / 2, nil}, // p * r too large } func TestKey(t *testing.T) { for i, v := range good { k, err := Key([]byte(v.password), []byte(v.salt), v.N, v.r, v.p, len(v.output)) if err != nil { t.Errorf("%d: got unexpected error: %s", i, err) } if !bytes.Equal(k, v.output) { t.Errorf("%d: expected %x, got %x", i, v.output, k) } } for i, v := range bad { _, err := Key([]byte(v.password), []byte(v.salt), v.N, v.r, v.p, 32) if err == nil { t.Errorf("%d: expected error, got nil", i) } } } func BenchmarkKey(b *testing.B) { for i := 0; i < b.N; i++ { Key([]byte("password"), []byte("salt"), 16384, 8, 1, 64) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/scrypt/scrypt.go�����������������������������������0000644�0000153�0000161�00000013313�12321735647�024570� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 scrypt implements the scrypt key derivation function as defined in // Colin Percival's paper "Stronger Key Derivation via Sequential Memory-Hard // Functions" (http://www.tarsnap.com/scrypt/scrypt.pdf). package scrypt import ( "crypto/sha256" "errors" "code.google.com/p/go.crypto/pbkdf2" ) const maxInt = int(^uint(0) >> 1) // blockCopy copies n numbers from src into dst. func blockCopy(dst, src []uint32, n int) { copy(dst, src[:n]) } // blockXOR XORs numbers from dst with n numbers from src. func blockXOR(dst, src []uint32, n int) { for i, v := range src[:n] { dst[i] ^= v } } // salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in, // and puts the result into both both tmp and out. func salsaXOR(tmp *[16]uint32, in, out []uint32) { w0 := tmp[0] ^ in[0] w1 := tmp[1] ^ in[1] w2 := tmp[2] ^ in[2] w3 := tmp[3] ^ in[3] w4 := tmp[4] ^ in[4] w5 := tmp[5] ^ in[5] w6 := tmp[6] ^ in[6] w7 := tmp[7] ^ in[7] w8 := tmp[8] ^ in[8] w9 := tmp[9] ^ in[9] w10 := tmp[10] ^ in[10] w11 := tmp[11] ^ in[11] w12 := tmp[12] ^ in[12] w13 := tmp[13] ^ in[13] w14 := tmp[14] ^ in[14] w15 := tmp[15] ^ in[15] x0, x1, x2, x3, x4, x5, x6, x7, x8 := w0, w1, w2, w3, w4, w5, w6, w7, w8 x9, x10, x11, x12, x13, x14, x15 := w9, w10, w11, w12, w13, w14, w15 for i := 0; i < 8; i += 2 { u := x0 + x12 x4 ^= u<<7 | u>>(32-7) u = x4 + x0 x8 ^= u<<9 | u>>(32-9) u = x8 + x4 x12 ^= u<<13 | u>>(32-13) u = x12 + x8 x0 ^= u<<18 | u>>(32-18) u = x5 + x1 x9 ^= u<<7 | u>>(32-7) u = x9 + x5 x13 ^= u<<9 | u>>(32-9) u = x13 + x9 x1 ^= u<<13 | u>>(32-13) u = x1 + x13 x5 ^= u<<18 | u>>(32-18) u = x10 + x6 x14 ^= u<<7 | u>>(32-7) u = x14 + x10 x2 ^= u<<9 | u>>(32-9) u = x2 + x14 x6 ^= u<<13 | u>>(32-13) u = x6 + x2 x10 ^= u<<18 | u>>(32-18) u = x15 + x11 x3 ^= u<<7 | u>>(32-7) u = x3 + x15 x7 ^= u<<9 | u>>(32-9) u = x7 + x3 x11 ^= u<<13 | u>>(32-13) u = x11 + x7 x15 ^= u<<18 | u>>(32-18) u = x0 + x3 x1 ^= u<<7 | u>>(32-7) u = x1 + x0 x2 ^= u<<9 | u>>(32-9) u = x2 + x1 x3 ^= u<<13 | u>>(32-13) u = x3 + x2 x0 ^= u<<18 | u>>(32-18) u = x5 + x4 x6 ^= u<<7 | u>>(32-7) u = x6 + x5 x7 ^= u<<9 | u>>(32-9) u = x7 + x6 x4 ^= u<<13 | u>>(32-13) u = x4 + x7 x5 ^= u<<18 | u>>(32-18) u = x10 + x9 x11 ^= u<<7 | u>>(32-7) u = x11 + x10 x8 ^= u<<9 | u>>(32-9) u = x8 + x11 x9 ^= u<<13 | u>>(32-13) u = x9 + x8 x10 ^= u<<18 | u>>(32-18) u = x15 + x14 x12 ^= u<<7 | u>>(32-7) u = x12 + x15 x13 ^= u<<9 | u>>(32-9) u = x13 + x12 x14 ^= u<<13 | u>>(32-13) u = x14 + x13 x15 ^= u<<18 | u>>(32-18) } x0 += w0 x1 += w1 x2 += w2 x3 += w3 x4 += w4 x5 += w5 x6 += w6 x7 += w7 x8 += w8 x9 += w9 x10 += w10 x11 += w11 x12 += w12 x13 += w13 x14 += w14 x15 += w15 out[0], tmp[0] = x0, x0 out[1], tmp[1] = x1, x1 out[2], tmp[2] = x2, x2 out[3], tmp[3] = x3, x3 out[4], tmp[4] = x4, x4 out[5], tmp[5] = x5, x5 out[6], tmp[6] = x6, x6 out[7], tmp[7] = x7, x7 out[8], tmp[8] = x8, x8 out[9], tmp[9] = x9, x9 out[10], tmp[10] = x10, x10 out[11], tmp[11] = x11, x11 out[12], tmp[12] = x12, x12 out[13], tmp[13] = x13, x13 out[14], tmp[14] = x14, x14 out[15], tmp[15] = x15, x15 } func blockMix(tmp *[16]uint32, in, out []uint32, r int) { blockCopy(tmp[:], in[(2*r-1)*16:], 16) for i := 0; i < 2*r; i += 2 { salsaXOR(tmp, in[i*16:], out[i*8:]) salsaXOR(tmp, in[i*16+16:], out[i*8+r*16:]) } } func integer(b []uint32, r int) uint64 { j := (2*r - 1) * 16 return uint64(b[j]) | uint64(b[j+1])<<32 } func smix(b []byte, r, N int, v, xy []uint32) { var tmp [16]uint32 x := xy y := xy[32*r:] j := 0 for i := 0; i < 32*r; i++ { x[i] = uint32(b[j]) | uint32(b[j+1])<<8 | uint32(b[j+2])<<16 | uint32(b[j+3])<<24 j += 4 } for i := 0; i < N; i += 2 { blockCopy(v[i*(32*r):], x, 32*r) blockMix(&tmp, x, y, r) blockCopy(v[(i+1)*(32*r):], y, 32*r) blockMix(&tmp, y, x, r) } for i := 0; i < N; i += 2 { j := int(integer(x, r) & uint64(N-1)) blockXOR(x, v[j*(32*r):], 32*r) blockMix(&tmp, x, y, r) j = int(integer(y, r) & uint64(N-1)) blockXOR(y, v[j*(32*r):], 32*r) blockMix(&tmp, y, x, r) } j = 0 for _, v := range x[:32*r] { b[j+0] = byte(v >> 0) b[j+1] = byte(v >> 8) b[j+2] = byte(v >> 16) b[j+3] = byte(v >> 24) j += 4 } } // Key derives a key from the password, salt, and cost parameters, returning // a byte slice of length keyLen that can be used as cryptographic key. // // N is a CPU/memory cost parameter, which must be a power of two greater than 1. // r and p must satisfy r * p < 2³â°. If the parameters do not satisfy the // limits, the function returns a nil byte slice and an error. // // For example, you can get a derived key for e.g. AES-256 (which needs a // 32-byte key) by doing: // // dk := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32) // // The recommended parameters for interactive logins as of 2009 are N=16384, // r=8, p=1. They should be increased as memory latency and CPU parallelism // increases. Remember to get a good random salt. func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error) { if N <= 1 || N&(N-1) != 0 { return nil, errors.New("scrypt: N must be > 1 and a power of 2") } if uint64(r)*uint64(p) >= 1<<30 || r > maxInt/128/p || r > maxInt/256 || N > maxInt/128/r { return nil, errors.New("scrypt: parameters are too large") } xy := make([]uint32, 64*r) v := make([]uint32, 32*N*r) b := pbkdf2.Key(password, salt, 1, p*128*r, sha256.New) for i := 0; i < p; i++ { smix(b[i*128*r:], r, N, v, xy) } return pbkdf2.Key(password, b, 1, keyLen, sha256.New), nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/otr/�����������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022174� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/otr/smp.go�����������������������������������������0000644�0000153�0000161�00000027316�12321735647�023333� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This file implements the Socialist Millionaires Protocol as described in // http://www.cypherpunks.ca/otr/Protocol-v2-3.1.0.html. The protocol // specification is required in order to understand this code and, where // possible, the variable names in the code match up with the spec. package otr import ( "bytes" "crypto/sha256" "errors" "hash" "math/big" ) type smpFailure string func (s smpFailure) Error() string { return string(s) } var smpFailureError = smpFailure("otr: SMP protocol failed") var smpSecretMissingError = smpFailure("otr: mutual secret needed") const smpVersion = 1 const ( smpState1 = iota smpState2 smpState3 smpState4 ) type smpState struct { state int a2, a3, b2, b3, pb, qb *big.Int g2a, g3a *big.Int g2, g3 *big.Int g3b, papb, qaqb, ra *big.Int saved *tlv secret *big.Int question string } func (c *Conversation) startSMP(question string) (tlvs []tlv) { if c.smp.state != smpState1 { tlvs = append(tlvs, c.generateSMPAbort()) } tlvs = append(tlvs, c.generateSMP1(question)) c.smp.question = "" c.smp.state = smpState2 return } func (c *Conversation) resetSMP() { c.smp.state = smpState1 c.smp.secret = nil c.smp.question = "" } func (c *Conversation) processSMP(in tlv) (out tlv, complete bool, err error) { data := in.data switch in.typ { case tlvTypeSMPAbort: if c.smp.state != smpState1 { err = smpFailureError } c.resetSMP() return case tlvTypeSMP1WithQuestion: // We preprocess this into a SMP1 message. nulPos := bytes.IndexByte(data, 0) if nulPos == -1 { err = errors.New("otr: SMP message with question didn't contain a NUL byte") return } c.smp.question = string(data[:nulPos]) data = data[nulPos+1:] } numMPIs, data, ok := getU32(data) if !ok || numMPIs > 20 { err = errors.New("otr: corrupt SMP message") return } mpis := make([]*big.Int, numMPIs) for i := range mpis { var ok bool mpis[i], data, ok = getMPI(data) if !ok { err = errors.New("otr: corrupt SMP message") return } } switch in.typ { case tlvTypeSMP1, tlvTypeSMP1WithQuestion: if c.smp.state != smpState1 { c.resetSMP() out = c.generateSMPAbort() return } if c.smp.secret == nil { err = smpSecretMissingError return } if err = c.processSMP1(mpis); err != nil { return } c.smp.state = smpState3 out = c.generateSMP2() case tlvTypeSMP2: if c.smp.state != smpState2 { c.resetSMP() out = c.generateSMPAbort() return } if out, err = c.processSMP2(mpis); err != nil { out = c.generateSMPAbort() return } c.smp.state = smpState4 case tlvTypeSMP3: if c.smp.state != smpState3 { c.resetSMP() out = c.generateSMPAbort() return } if out, err = c.processSMP3(mpis); err != nil { return } c.smp.state = smpState1 c.smp.secret = nil complete = true case tlvTypeSMP4: if c.smp.state != smpState4 { c.resetSMP() out = c.generateSMPAbort() return } if err = c.processSMP4(mpis); err != nil { out = c.generateSMPAbort() return } c.smp.state = smpState1 c.smp.secret = nil complete = true default: panic("unknown SMP message") } return } func (c *Conversation) calcSMPSecret(mutualSecret []byte, weStarted bool) { h := sha256.New() h.Write([]byte{smpVersion}) if weStarted { h.Write(c.PrivateKey.PublicKey.Fingerprint()) h.Write(c.TheirPublicKey.Fingerprint()) } else { h.Write(c.TheirPublicKey.Fingerprint()) h.Write(c.PrivateKey.PublicKey.Fingerprint()) } h.Write(c.SSID[:]) h.Write(mutualSecret) c.smp.secret = new(big.Int).SetBytes(h.Sum(nil)) } func (c *Conversation) generateSMP1(question string) tlv { var randBuf [16]byte c.smp.a2 = c.randMPI(randBuf[:]) c.smp.a3 = c.randMPI(randBuf[:]) g2a := new(big.Int).Exp(g, c.smp.a2, p) g3a := new(big.Int).Exp(g, c.smp.a3, p) h := sha256.New() r2 := c.randMPI(randBuf[:]) r := new(big.Int).Exp(g, r2, p) c2 := new(big.Int).SetBytes(hashMPIs(h, 1, r)) d2 := new(big.Int).Mul(c.smp.a2, c2) d2.Sub(r2, d2) d2.Mod(d2, q) if d2.Sign() < 0 { d2.Add(d2, q) } r3 := c.randMPI(randBuf[:]) r.Exp(g, r3, p) c3 := new(big.Int).SetBytes(hashMPIs(h, 2, r)) d3 := new(big.Int).Mul(c.smp.a3, c3) d3.Sub(r3, d3) d3.Mod(d3, q) if d3.Sign() < 0 { d3.Add(d3, q) } var ret tlv if len(question) > 0 { ret.typ = tlvTypeSMP1WithQuestion ret.data = append(ret.data, question...) ret.data = append(ret.data, 0) } else { ret.typ = tlvTypeSMP1 } ret.data = appendU32(ret.data, 6) ret.data = appendMPIs(ret.data, g2a, c2, d2, g3a, c3, d3) return ret } func (c *Conversation) processSMP1(mpis []*big.Int) error { if len(mpis) != 6 { return errors.New("otr: incorrect number of arguments in SMP1 message") } g2a := mpis[0] c2 := mpis[1] d2 := mpis[2] g3a := mpis[3] c3 := mpis[4] d3 := mpis[5] h := sha256.New() r := new(big.Int).Exp(g, d2, p) s := new(big.Int).Exp(g2a, c2, p) r.Mul(r, s) r.Mod(r, p) t := new(big.Int).SetBytes(hashMPIs(h, 1, r)) if c2.Cmp(t) != 0 { return errors.New("otr: ZKP c2 incorrect in SMP1 message") } r.Exp(g, d3, p) s.Exp(g3a, c3, p) r.Mul(r, s) r.Mod(r, p) t.SetBytes(hashMPIs(h, 2, r)) if c3.Cmp(t) != 0 { return errors.New("otr: ZKP c3 incorrect in SMP1 message") } c.smp.g2a = g2a c.smp.g3a = g3a return nil } func (c *Conversation) generateSMP2() tlv { var randBuf [16]byte b2 := c.randMPI(randBuf[:]) c.smp.b3 = c.randMPI(randBuf[:]) r2 := c.randMPI(randBuf[:]) r3 := c.randMPI(randBuf[:]) r4 := c.randMPI(randBuf[:]) r5 := c.randMPI(randBuf[:]) r6 := c.randMPI(randBuf[:]) g2b := new(big.Int).Exp(g, b2, p) g3b := new(big.Int).Exp(g, c.smp.b3, p) r := new(big.Int).Exp(g, r2, p) h := sha256.New() c2 := new(big.Int).SetBytes(hashMPIs(h, 3, r)) d2 := new(big.Int).Mul(b2, c2) d2.Sub(r2, d2) d2.Mod(d2, q) if d2.Sign() < 0 { d2.Add(d2, q) } r.Exp(g, r3, p) c3 := new(big.Int).SetBytes(hashMPIs(h, 4, r)) d3 := new(big.Int).Mul(c.smp.b3, c3) d3.Sub(r3, d3) d3.Mod(d3, q) if d3.Sign() < 0 { d3.Add(d3, q) } c.smp.g2 = new(big.Int).Exp(c.smp.g2a, b2, p) c.smp.g3 = new(big.Int).Exp(c.smp.g3a, c.smp.b3, p) c.smp.pb = new(big.Int).Exp(c.smp.g3, r4, p) c.smp.qb = new(big.Int).Exp(g, r4, p) r.Exp(c.smp.g2, c.smp.secret, p) c.smp.qb.Mul(c.smp.qb, r) c.smp.qb.Mod(c.smp.qb, p) s := new(big.Int) s.Exp(c.smp.g2, r6, p) r.Exp(g, r5, p) s.Mul(r, s) s.Mod(s, p) r.Exp(c.smp.g3, r5, p) cp := new(big.Int).SetBytes(hashMPIs(h, 5, r, s)) // D5 = r5 - r4 cP mod q and D6 = r6 - y cP mod q s.Mul(r4, cp) r.Sub(r5, s) d5 := new(big.Int).Mod(r, q) if d5.Sign() < 0 { d5.Add(d5, q) } s.Mul(c.smp.secret, cp) r.Sub(r6, s) d6 := new(big.Int).Mod(r, q) if d6.Sign() < 0 { d6.Add(d6, q) } var ret tlv ret.typ = tlvTypeSMP2 ret.data = appendU32(ret.data, 11) ret.data = appendMPIs(ret.data, g2b, c2, d2, g3b, c3, d3, c.smp.pb, c.smp.qb, cp, d5, d6) return ret } func (c *Conversation) processSMP2(mpis []*big.Int) (out tlv, err error) { if len(mpis) != 11 { err = errors.New("otr: incorrect number of arguments in SMP2 message") return } g2b := mpis[0] c2 := mpis[1] d2 := mpis[2] g3b := mpis[3] c3 := mpis[4] d3 := mpis[5] pb := mpis[6] qb := mpis[7] cp := mpis[8] d5 := mpis[9] d6 := mpis[10] h := sha256.New() r := new(big.Int).Exp(g, d2, p) s := new(big.Int).Exp(g2b, c2, p) r.Mul(r, s) r.Mod(r, p) s.SetBytes(hashMPIs(h, 3, r)) if c2.Cmp(s) != 0 { err = errors.New("otr: ZKP c2 failed in SMP2 message") return } r.Exp(g, d3, p) s.Exp(g3b, c3, p) r.Mul(r, s) r.Mod(r, p) s.SetBytes(hashMPIs(h, 4, r)) if c3.Cmp(s) != 0 { err = errors.New("otr: ZKP c3 failed in SMP2 message") return } c.smp.g2 = new(big.Int).Exp(g2b, c.smp.a2, p) c.smp.g3 = new(big.Int).Exp(g3b, c.smp.a3, p) r.Exp(g, d5, p) s.Exp(c.smp.g2, d6, p) r.Mul(r, s) s.Exp(qb, cp, p) r.Mul(r, s) r.Mod(r, p) s.Exp(c.smp.g3, d5, p) t := new(big.Int).Exp(pb, cp, p) s.Mul(s, t) s.Mod(s, p) t.SetBytes(hashMPIs(h, 5, s, r)) if cp.Cmp(t) != 0 { err = errors.New("otr: ZKP cP failed in SMP2 message") return } var randBuf [16]byte r4 := c.randMPI(randBuf[:]) r5 := c.randMPI(randBuf[:]) r6 := c.randMPI(randBuf[:]) r7 := c.randMPI(randBuf[:]) pa := new(big.Int).Exp(c.smp.g3, r4, p) r.Exp(c.smp.g2, c.smp.secret, p) qa := new(big.Int).Exp(g, r4, p) qa.Mul(qa, r) qa.Mod(qa, p) r.Exp(g, r5, p) s.Exp(c.smp.g2, r6, p) r.Mul(r, s) r.Mod(r, p) s.Exp(c.smp.g3, r5, p) cp.SetBytes(hashMPIs(h, 6, s, r)) r.Mul(r4, cp) d5 = new(big.Int).Sub(r5, r) d5.Mod(d5, q) if d5.Sign() < 0 { d5.Add(d5, q) } r.Mul(c.smp.secret, cp) d6 = new(big.Int).Sub(r6, r) d6.Mod(d6, q) if d6.Sign() < 0 { d6.Add(d6, q) } r.ModInverse(qb, p) qaqb := new(big.Int).Mul(qa, r) qaqb.Mod(qaqb, p) ra := new(big.Int).Exp(qaqb, c.smp.a3, p) r.Exp(qaqb, r7, p) s.Exp(g, r7, p) cr := new(big.Int).SetBytes(hashMPIs(h, 7, s, r)) r.Mul(c.smp.a3, cr) d7 := new(big.Int).Sub(r7, r) d7.Mod(d7, q) if d7.Sign() < 0 { d7.Add(d7, q) } c.smp.g3b = g3b c.smp.qaqb = qaqb r.ModInverse(pb, p) c.smp.papb = new(big.Int).Mul(pa, r) c.smp.papb.Mod(c.smp.papb, p) c.smp.ra = ra out.typ = tlvTypeSMP3 out.data = appendU32(out.data, 8) out.data = appendMPIs(out.data, pa, qa, cp, d5, d6, ra, cr, d7) return } func (c *Conversation) processSMP3(mpis []*big.Int) (out tlv, err error) { if len(mpis) != 8 { err = errors.New("otr: incorrect number of arguments in SMP3 message") return } pa := mpis[0] qa := mpis[1] cp := mpis[2] d5 := mpis[3] d6 := mpis[4] ra := mpis[5] cr := mpis[6] d7 := mpis[7] h := sha256.New() r := new(big.Int).Exp(g, d5, p) s := new(big.Int).Exp(c.smp.g2, d6, p) r.Mul(r, s) s.Exp(qa, cp, p) r.Mul(r, s) r.Mod(r, p) s.Exp(c.smp.g3, d5, p) t := new(big.Int).Exp(pa, cp, p) s.Mul(s, t) s.Mod(s, p) t.SetBytes(hashMPIs(h, 6, s, r)) if t.Cmp(cp) != 0 { err = errors.New("otr: ZKP cP failed in SMP3 message") return } r.ModInverse(c.smp.qb, p) qaqb := new(big.Int).Mul(qa, r) qaqb.Mod(qaqb, p) r.Exp(qaqb, d7, p) s.Exp(ra, cr, p) r.Mul(r, s) r.Mod(r, p) s.Exp(g, d7, p) t.Exp(c.smp.g3a, cr, p) s.Mul(s, t) s.Mod(s, p) t.SetBytes(hashMPIs(h, 7, s, r)) if t.Cmp(cr) != 0 { err = errors.New("otr: ZKP cR failed in SMP3 message") return } var randBuf [16]byte r7 := c.randMPI(randBuf[:]) rb := new(big.Int).Exp(qaqb, c.smp.b3, p) r.Exp(qaqb, r7, p) s.Exp(g, r7, p) cr = new(big.Int).SetBytes(hashMPIs(h, 8, s, r)) r.Mul(c.smp.b3, cr) d7 = new(big.Int).Sub(r7, r) d7.Mod(d7, q) if d7.Sign() < 0 { d7.Add(d7, q) } out.typ = tlvTypeSMP4 out.data = appendU32(out.data, 3) out.data = appendMPIs(out.data, rb, cr, d7) r.ModInverse(c.smp.pb, p) r.Mul(pa, r) r.Mod(r, p) s.Exp(ra, c.smp.b3, p) if r.Cmp(s) != 0 { err = smpFailureError } return } func (c *Conversation) processSMP4(mpis []*big.Int) error { if len(mpis) != 3 { return errors.New("otr: incorrect number of arguments in SMP4 message") } rb := mpis[0] cr := mpis[1] d7 := mpis[2] h := sha256.New() r := new(big.Int).Exp(c.smp.qaqb, d7, p) s := new(big.Int).Exp(rb, cr, p) r.Mul(r, s) r.Mod(r, p) s.Exp(g, d7, p) t := new(big.Int).Exp(c.smp.g3b, cr, p) s.Mul(s, t) s.Mod(s, p) t.SetBytes(hashMPIs(h, 8, s, r)) if t.Cmp(cr) != 0 { return errors.New("otr: ZKP cR failed in SMP4 message") } r.Exp(rb, c.smp.a3, p) if r.Cmp(c.smp.papb) != 0 { return smpFailureError } return nil } func (c *Conversation) generateSMPAbort() tlv { return tlv{typ: tlvTypeSMPAbort} } func hashMPIs(h hash.Hash, magic byte, mpis ...*big.Int) []byte { if h != nil { h.Reset() } else { h = sha256.New() } h.Write([]byte{magic}) for _, mpi := range mpis { h.Write(appendMPI(nil, mpi)) } return h.Sum(nil) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/otr/otr.go�����������������������������������������0000644�0000153�0000161�00000105766�12321735647�023346� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 otr implements the Off The Record protocol as specified in // http://www.cypherpunks.ca/otr/Protocol-v2-3.1.0.html package otr import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/dsa" "crypto/hmac" "crypto/rand" "crypto/sha1" "crypto/sha256" "crypto/subtle" "encoding/base64" "encoding/hex" "errors" "hash" "io" "math/big" "strconv" ) // SecurityChange describes a change in the security state of a Conversation. type SecurityChange int const ( NoChange SecurityChange = iota // NewKeys indicates that a key exchange has completed. This occurs // when a conversation first becomes encrypted, and when the keys are // renegotiated within an encrypted conversation. NewKeys // SMPSecretNeeded indicates that the peer has started an // authentication and that we need to supply a secret. Call SMPQuestion // to get the optional, human readable challenge and then Authenticate // to supply the matching secret. SMPSecretNeeded // SMPComplete indicates that an authentication completed. The identity // of the peer has now been confirmed. SMPComplete // SMPFailed indicates that an authentication failed. SMPFailed // ConversationEnded indicates that the peer ended the secure // conversation. ConversationEnded ) // QueryMessage can be sent to a peer to start an OTR conversation. var QueryMessage = "?OTRv2?" // ErrorPrefix can be used to make an OTR error by appending an error message // to it. var ErrorPrefix = "?OTR Error:" var ( fragmentPartSeparator = []byte(",") fragmentPrefix = []byte("?OTR,") msgPrefix = []byte("?OTR:") queryMarker = []byte("?OTR") ) // isQuery attempts to parse an OTR query from msg and returns the greatest // common version, or 0 if msg is not an OTR query. func isQuery(msg []byte) (greatestCommonVersion int) { pos := bytes.Index(msg, queryMarker) if pos == -1 { return 0 } for i, c := range msg[pos+len(queryMarker):] { if i == 0 { if c == '?' { // Indicates support for version 1, but we don't // implement that. continue } if c != 'v' { // Invalid message return 0 } continue } if c == '?' { // End of message return } if c == ' ' || c == '\t' { // Probably an invalid message return 0 } if c == '2' { greatestCommonVersion = 2 } } return 0 } const ( statePlaintext = iota stateEncrypted stateFinished ) const ( authStateNone = iota authStateAwaitingDHKey authStateAwaitingRevealSig authStateAwaitingSig ) const ( msgTypeDHCommit = 2 msgTypeData = 3 msgTypeDHKey = 10 msgTypeRevealSig = 17 msgTypeSig = 18 ) const ( // If the requested fragment size is less than this, it will be ignored. minFragmentSize = 18 // Messages are padded to a multiple of this number of bytes. paddingGranularity = 256 // The number of bytes in a Diffie-Hellman private value (320-bits). dhPrivateBytes = 40 // The number of bytes needed to represent an element of the DSA // subgroup (160-bits). dsaSubgroupBytes = 20 // The number of bytes of the MAC that are sent on the wire (160-bits). macPrefixBytes = 20 ) // These are the global, common group parameters for OTR. var ( p *big.Int // group prime g *big.Int // group generator q *big.Int // group order pMinus2 *big.Int ) func init() { p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 16) q, _ = new(big.Int).SetString("7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68948127044533E63A0105DF531D89CD9128A5043CC71A026EF7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9EE1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AFC1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36B3861AA7255E4C0278BA36046511B993FFFFFFFFFFFFFFFF", 16) g = new(big.Int).SetInt64(2) pMinus2 = new(big.Int).Sub(p, g) } // Conversation represents a relation with a peer. The zero value is a valid // Conversation, although PrivateKey must be set. // // When communicating with a peer, all inbound messages should be passed to // Conversation.Receive and all outbound messages to Conversation.Send. The // Conversation will take care of maintaining the encryption state and // negotiating encryption as needed. type Conversation struct { // PrivateKey contains the private key to use to sign key exchanges. PrivateKey *PrivateKey // Rand can be set to override the entropy source. Otherwise, // crypto/rand will be used. Rand io.Reader // If FragmentSize is set, all messages produced by Receive and Send // will be fragmented into messages of, at most, this number of bytes. FragmentSize int // Once Receive has returned NewKeys once, the following fields are // valid. SSID [8]byte TheirPublicKey PublicKey state, authState int r [16]byte x, y *big.Int gx, gy *big.Int gxBytes []byte digest [sha256.Size]byte revealKeys, sigKeys akeKeys myKeyId uint32 myCurrentDHPub *big.Int myCurrentDHPriv *big.Int myLastDHPub *big.Int myLastDHPriv *big.Int theirKeyId uint32 theirCurrentDHPub *big.Int theirLastDHPub *big.Int keySlots [4]keySlot myCounter [8]byte theirLastCtr [8]byte oldMACs []byte k, n int // fragment state frag []byte smp smpState } // A keySlot contains key material for a specific (their keyid, my keyid) pair. type keySlot struct { // used is true if this slot is valid. If false, it's free for reuse. used bool theirKeyId uint32 myKeyId uint32 sendAESKey, recvAESKey []byte sendMACKey, recvMACKey []byte theirLastCtr [8]byte } // akeKeys are generated during key exchange. There's one set for the reveal // signature message and another for the signature message. In the protocol // spec the latter are indicated with a prime mark. type akeKeys struct { c [16]byte m1, m2 [32]byte } func (c *Conversation) rand() io.Reader { if c.Rand != nil { return c.Rand } return rand.Reader } func (c *Conversation) randMPI(buf []byte) *big.Int { _, err := io.ReadFull(c.rand(), buf) if err != nil { panic("otr: short read from random source") } return new(big.Int).SetBytes(buf) } // tlv represents the type-length value from the protocol. type tlv struct { typ, length uint16 data []byte } const ( tlvTypePadding = 0 tlvTypeDisconnected = 1 tlvTypeSMP1 = 2 tlvTypeSMP2 = 3 tlvTypeSMP3 = 4 tlvTypeSMP4 = 5 tlvTypeSMPAbort = 6 tlvTypeSMP1WithQuestion = 7 ) // Receive handles a message from a peer. It returns a human readable message, // an indicator of whether that message was encrypted, a hint about the // encryption state and zero or more messages to send back to the peer. // These messages do not need to be passed to Send before transmission. func (c *Conversation) Receive(in []byte) (out []byte, encrypted bool, change SecurityChange, toSend [][]byte, err error) { if bytes.HasPrefix(in, fragmentPrefix) { in, err = c.processFragment(in) if in == nil || err != nil { return } } if bytes.HasPrefix(in, msgPrefix) && in[len(in)-1] == '.' { in = in[len(msgPrefix) : len(in)-1] } else if version := isQuery(in); version > 0 { c.authState = authStateAwaitingDHKey c.myKeyId = 0 toSend = c.encode(c.generateDHCommit()) return } else { // plaintext message out = in return } msg := make([]byte, base64.StdEncoding.DecodedLen(len(in))) msgLen, err := base64.StdEncoding.Decode(msg, in) if err != nil { err = errors.New("otr: invalid base64 encoding in message") return } msg = msg[:msgLen] // The first two bytes are the protocol version (2) if len(msg) < 3 || msg[0] != 0 || msg[1] != 2 { err = errors.New("otr: invalid OTR message") return } msgType := int(msg[2]) msg = msg[3:] switch msgType { case msgTypeDHCommit: switch c.authState { case authStateNone: c.authState = authStateAwaitingRevealSig if err = c.processDHCommit(msg); err != nil { return } c.myKeyId = 0 toSend = c.encode(c.generateDHKey()) return case authStateAwaitingDHKey: // This is a 'SYN-crossing'. The greater digest wins. var cmp int if cmp, err = c.compareToDHCommit(msg); err != nil { return } if cmp > 0 { // We win. Retransmit DH commit. toSend = c.encode(c.serializeDHCommit()) return } else { // They win. We forget about our DH commit. c.authState = authStateAwaitingRevealSig if err = c.processDHCommit(msg); err != nil { return } c.myKeyId = 0 toSend = c.encode(c.generateDHKey()) return } case authStateAwaitingRevealSig: if err = c.processDHCommit(msg); err != nil { return } toSend = c.encode(c.serializeDHKey()) case authStateAwaitingSig: if err = c.processDHCommit(msg); err != nil { return } c.myKeyId = 0 toSend = c.encode(c.generateDHKey()) c.authState = authStateAwaitingRevealSig default: panic("bad state") } case msgTypeDHKey: switch c.authState { case authStateAwaitingDHKey: var isSame bool if isSame, err = c.processDHKey(msg); err != nil { return } if isSame { err = errors.New("otr: unexpected duplicate DH key") return } toSend = c.encode(c.generateRevealSig()) c.authState = authStateAwaitingSig case authStateAwaitingSig: var isSame bool if isSame, err = c.processDHKey(msg); err != nil { return } if isSame { toSend = c.encode(c.serializeDHKey()) } } case msgTypeRevealSig: if c.authState != authStateAwaitingRevealSig { return } if err = c.processRevealSig(msg); err != nil { return } toSend = c.encode(c.generateSig()) c.authState = authStateNone c.state = stateEncrypted change = NewKeys case msgTypeSig: if c.authState != authStateAwaitingSig { return } if err = c.processSig(msg); err != nil { return } c.authState = authStateNone c.state = stateEncrypted change = NewKeys case msgTypeData: if c.state != stateEncrypted { err = errors.New("otr: encrypted message received without encrypted session established") return } var tlvs []tlv out, tlvs, err = c.processData(msg) encrypted = true EachTLV: for _, inTLV := range tlvs { switch inTLV.typ { case tlvTypeDisconnected: change = ConversationEnded c.state = stateFinished break EachTLV case tlvTypeSMP1, tlvTypeSMP2, tlvTypeSMP3, tlvTypeSMP4, tlvTypeSMPAbort, tlvTypeSMP1WithQuestion: var reply tlv var complete bool reply, complete, err = c.processSMP(inTLV) if err == smpSecretMissingError { err = nil change = SMPSecretNeeded c.smp.saved = &inTLV return } else if err == smpFailureError { err = nil change = SMPFailed return } if complete { change = SMPComplete } if reply.typ != 0 { toSend = c.encode(c.generateData(nil, &reply)) } break EachTLV default: // skip unknown TLVs } } default: err = errors.New("otr: unknown message type " + strconv.Itoa(msgType)) } return } // Send takes a human readable message from the local user, possibly encrypts // it and returns zero one or more messages to send to the peer. func (c *Conversation) Send(msg []byte) ([][]byte, error) { switch c.state { case statePlaintext: return [][]byte{msg}, nil case stateEncrypted: return c.encode(c.generateData(msg, nil)), nil case stateFinished: return nil, errors.New("otr: cannot send message because secure conversation has finished") } return nil, errors.New("otr: cannot send message in current state") } // SMPQuestion returns the human readable challenge question from the peer. // It's only valid after Receive has returned SMPSecretNeeded. func (c *Conversation) SMPQuestion() string { return c.smp.question } // Authenticate begins an authentication with the peer. Authentication involves // an optional challenge message and a shared secret. The authentication // proceeds until either Receive returns SMPComplete, SMPSecretNeeded (which // indicates that a new authentication is happening and thus this one was // aborted) or SMPFailed. func (c *Conversation) Authenticate(question string, mutualSecret []byte) (toSend [][]byte, err error) { if c.state != stateEncrypted { err = errors.New("otr: can't authenticate a peer without a secure conversation established") return } if c.smp.saved != nil { c.calcSMPSecret(mutualSecret, false /* they started it */) var out tlv var complete bool out, complete, err = c.processSMP(*c.smp.saved) if complete { panic("SMP completed on the first message") } c.smp.saved = nil if out.typ != 0 { toSend = c.encode(c.generateData(nil, &out)) } return } c.calcSMPSecret(mutualSecret, true /* we started it */) outs := c.startSMP(question) for _, out := range outs { toSend = append(toSend, c.encode(c.generateData(nil, &out))...) } return } // End ends a secure conversation by generating a termination message for // the peer and switches to unencrypted communication. func (c *Conversation) End() (toSend [][]byte) { switch c.state { case statePlaintext: return nil case stateEncrypted: c.state = statePlaintext return c.encode(c.generateData(nil, &tlv{typ: tlvTypeDisconnected})) case stateFinished: c.state = statePlaintext return nil } panic("unreachable") } // IsEncrypted returns true if a message passed to Send would be encrypted // before transmission. This result remains valid until the next call to // Receive or End, which may change the state of the Conversation. func (c *Conversation) IsEncrypted() bool { return c.state == stateEncrypted } var fragmentError = errors.New("otr: invalid OTR fragment") // processFragment processes a fragmented OTR message and possibly returns a // complete message. Fragmented messages look like "?OTR,k,n,msg," where k is // the fragment number (starting from 1), n is the number of fragments in this // message and msg is a substring of the base64 encoded message. func (c *Conversation) processFragment(in []byte) (out []byte, err error) { in = in[len(fragmentPrefix):] // remove "?OTR," parts := bytes.Split(in, fragmentPartSeparator) if len(parts) != 4 || len(parts[3]) != 0 { return nil, fragmentError } k, err := strconv.Atoi(string(parts[0])) if err != nil { return nil, fragmentError } n, err := strconv.Atoi(string(parts[1])) if err != nil { return nil, fragmentError } if k < 1 || n < 1 || k > n { return nil, fragmentError } if k == 1 { c.frag = append(c.frag[:0], parts[2]...) c.k, c.n = k, n } else if n == c.n && k == c.k+1 { c.frag = append(c.frag, parts[2]...) c.k++ } else { c.frag = c.frag[:0] c.n, c.k = 0, 0 } if c.n > 0 && c.k == c.n { c.n, c.k = 0, 0 return c.frag, nil } return nil, nil } func (c *Conversation) generateDHCommit() []byte { _, err := io.ReadFull(c.rand(), c.r[:]) if err != nil { panic("otr: short read from random source") } var xBytes [dhPrivateBytes]byte c.x = c.randMPI(xBytes[:]) c.gx = new(big.Int).Exp(g, c.x, p) c.gy = nil c.gxBytes = appendMPI(nil, c.gx) h := sha256.New() h.Write(c.gxBytes) h.Sum(c.digest[:0]) aesCipher, err := aes.NewCipher(c.r[:]) if err != nil { panic(err.Error()) } var iv [aes.BlockSize]byte ctr := cipher.NewCTR(aesCipher, iv[:]) ctr.XORKeyStream(c.gxBytes, c.gxBytes) return c.serializeDHCommit() } func (c *Conversation) serializeDHCommit() []byte { var ret []byte ret = appendU16(ret, 2) // protocol version ret = append(ret, msgTypeDHCommit) ret = appendData(ret, c.gxBytes) ret = appendData(ret, c.digest[:]) return ret } func (c *Conversation) processDHCommit(in []byte) error { var ok1, ok2 bool c.gxBytes, in, ok1 = getData(in) digest, in, ok2 := getData(in) if !ok1 || !ok2 || len(in) > 0 { return errors.New("otr: corrupt DH commit message") } copy(c.digest[:], digest) return nil } func (c *Conversation) compareToDHCommit(in []byte) (int, error) { _, in, ok1 := getData(in) digest, in, ok2 := getData(in) if !ok1 || !ok2 || len(in) > 0 { return 0, errors.New("otr: corrupt DH commit message") } return bytes.Compare(c.digest[:], digest), nil } func (c *Conversation) generateDHKey() []byte { var yBytes [dhPrivateBytes]byte c.y = c.randMPI(yBytes[:]) c.gy = new(big.Int).Exp(g, c.y, p) return c.serializeDHKey() } func (c *Conversation) serializeDHKey() []byte { var ret []byte ret = appendU16(ret, 2) // protocol version ret = append(ret, msgTypeDHKey) ret = appendMPI(ret, c.gy) return ret } func (c *Conversation) processDHKey(in []byte) (isSame bool, err error) { gy, in, ok := getMPI(in) if !ok { err = errors.New("otr: corrupt DH key message") return } if gy.Cmp(g) < 0 || gy.Cmp(pMinus2) > 0 { err = errors.New("otr: DH value out of range") return } if c.gy != nil { isSame = c.gy.Cmp(gy) == 0 return } c.gy = gy return } func (c *Conversation) generateEncryptedSignature(keys *akeKeys, xFirst bool) ([]byte, []byte) { var xb []byte xb = c.PrivateKey.PublicKey.Serialize(xb) var verifyData []byte if xFirst { verifyData = appendMPI(verifyData, c.gx) verifyData = appendMPI(verifyData, c.gy) } else { verifyData = appendMPI(verifyData, c.gy) verifyData = appendMPI(verifyData, c.gx) } verifyData = append(verifyData, xb...) verifyData = appendU32(verifyData, c.myKeyId) mac := hmac.New(sha256.New, keys.m1[:]) mac.Write(verifyData) mb := mac.Sum(nil) xb = appendU32(xb, c.myKeyId) xb = append(xb, c.PrivateKey.Sign(c.rand(), mb)...) aesCipher, err := aes.NewCipher(keys.c[:]) if err != nil { panic(err.Error()) } var iv [aes.BlockSize]byte ctr := cipher.NewCTR(aesCipher, iv[:]) ctr.XORKeyStream(xb, xb) mac = hmac.New(sha256.New, keys.m2[:]) encryptedSig := appendData(nil, xb) mac.Write(encryptedSig) return encryptedSig, mac.Sum(nil) } func (c *Conversation) generateRevealSig() []byte { s := new(big.Int).Exp(c.gy, c.x, p) c.calcAKEKeys(s) c.myKeyId++ encryptedSig, mac := c.generateEncryptedSignature(&c.revealKeys, true /* gx comes first */) c.myCurrentDHPub = c.gx c.myCurrentDHPriv = c.x c.rotateDHKeys() incCounter(&c.myCounter) var ret []byte ret = appendU16(ret, 2) ret = append(ret, msgTypeRevealSig) ret = appendData(ret, c.r[:]) ret = append(ret, encryptedSig...) ret = append(ret, mac[:20]...) return ret } func (c *Conversation) processEncryptedSig(encryptedSig, theirMAC []byte, keys *akeKeys, xFirst bool) error { mac := hmac.New(sha256.New, keys.m2[:]) mac.Write(appendData(nil, encryptedSig)) myMAC := mac.Sum(nil)[:20] if len(myMAC) != len(theirMAC) || subtle.ConstantTimeCompare(myMAC, theirMAC) == 0 { return errors.New("bad signature MAC in encrypted signature") } aesCipher, err := aes.NewCipher(keys.c[:]) if err != nil { panic(err.Error()) } var iv [aes.BlockSize]byte ctr := cipher.NewCTR(aesCipher, iv[:]) ctr.XORKeyStream(encryptedSig, encryptedSig) sig := encryptedSig sig, ok1 := c.TheirPublicKey.Parse(sig) keyId, sig, ok2 := getU32(sig) if !ok1 || !ok2 { return errors.New("otr: corrupt encrypted signature") } var verifyData []byte if xFirst { verifyData = appendMPI(verifyData, c.gx) verifyData = appendMPI(verifyData, c.gy) } else { verifyData = appendMPI(verifyData, c.gy) verifyData = appendMPI(verifyData, c.gx) } verifyData = c.TheirPublicKey.Serialize(verifyData) verifyData = appendU32(verifyData, keyId) mac = hmac.New(sha256.New, keys.m1[:]) mac.Write(verifyData) mb := mac.Sum(nil) sig, ok1 = c.TheirPublicKey.Verify(mb, sig) if !ok1 { return errors.New("bad signature in encrypted signature") } if len(sig) > 0 { return errors.New("corrupt encrypted signature") } c.theirKeyId = keyId zero(c.theirLastCtr[:]) return nil } func (c *Conversation) processRevealSig(in []byte) error { r, in, ok1 := getData(in) encryptedSig, in, ok2 := getData(in) theirMAC := in if !ok1 || !ok2 || len(theirMAC) != 20 { return errors.New("otr: corrupt reveal signature message") } aesCipher, err := aes.NewCipher(r) if err != nil { return errors.New("otr: cannot create AES cipher from reveal signature message: " + err.Error()) } var iv [aes.BlockSize]byte ctr := cipher.NewCTR(aesCipher, iv[:]) ctr.XORKeyStream(c.gxBytes, c.gxBytes) h := sha256.New() h.Write(c.gxBytes) digest := h.Sum(nil) if len(digest) != len(c.digest) || subtle.ConstantTimeCompare(digest, c.digest[:]) == 0 { return errors.New("otr: bad commit MAC in reveal signature message") } var rest []byte c.gx, rest, ok1 = getMPI(c.gxBytes) if !ok1 || len(rest) > 0 { return errors.New("otr: gx corrupt after decryption") } if c.gx.Cmp(g) < 0 || c.gx.Cmp(pMinus2) > 0 { return errors.New("otr: DH value out of range") } s := new(big.Int).Exp(c.gx, c.y, p) c.calcAKEKeys(s) if err := c.processEncryptedSig(encryptedSig, theirMAC, &c.revealKeys, true /* gx comes first */); err != nil { return errors.New("otr: in reveal signature message: " + err.Error()) } c.theirCurrentDHPub = c.gx c.theirLastDHPub = nil return nil } func (c *Conversation) generateSig() []byte { c.myKeyId++ encryptedSig, mac := c.generateEncryptedSignature(&c.sigKeys, false /* gy comes first */) c.myCurrentDHPub = c.gy c.myCurrentDHPriv = c.y c.rotateDHKeys() incCounter(&c.myCounter) var ret []byte ret = appendU16(ret, 2) ret = append(ret, msgTypeSig) ret = append(ret, encryptedSig...) ret = append(ret, mac[:macPrefixBytes]...) return ret } func (c *Conversation) processSig(in []byte) error { encryptedSig, in, ok1 := getData(in) theirMAC := in if !ok1 || len(theirMAC) != macPrefixBytes { return errors.New("otr: corrupt signature message") } if err := c.processEncryptedSig(encryptedSig, theirMAC, &c.sigKeys, false /* gy comes first */); err != nil { return errors.New("otr: in signature message: " + err.Error()) } c.theirCurrentDHPub = c.gy c.theirLastDHPub = nil return nil } func (c *Conversation) rotateDHKeys() { // evict slots using our retired key id for i := range c.keySlots { slot := &c.keySlots[i] if slot.used && slot.myKeyId == c.myKeyId-1 { slot.used = false c.oldMACs = append(c.oldMACs, slot.sendMACKey...) c.oldMACs = append(c.oldMACs, slot.recvMACKey...) } } c.myLastDHPriv = c.myCurrentDHPriv c.myLastDHPub = c.myCurrentDHPub var xBytes [dhPrivateBytes]byte c.myCurrentDHPriv = c.randMPI(xBytes[:]) c.myCurrentDHPub = new(big.Int).Exp(g, c.myCurrentDHPriv, p) c.myKeyId++ } func (c *Conversation) processData(in []byte) (out []byte, tlvs []tlv, err error) { origIn := in flags, in, ok1 := getU8(in) theirKeyId, in, ok2 := getU32(in) myKeyId, in, ok3 := getU32(in) y, in, ok4 := getMPI(in) counter, in, ok5 := getNBytes(in, 8) encrypted, in, ok6 := getData(in) macedData := origIn[:len(origIn)-len(in)] theirMAC, in, ok7 := getNBytes(in, macPrefixBytes) _, in, ok8 := getData(in) if !ok1 || !ok2 || !ok3 || !ok4 || !ok5 || !ok6 || !ok7 || !ok8 || len(in) > 0 { err = errors.New("otr: corrupt data message") return } ignoreErrors := flags&1 != 0 slot, err := c.calcDataKeys(myKeyId, theirKeyId) if err != nil { if ignoreErrors { err = nil } return } mac := hmac.New(sha1.New, slot.recvMACKey) mac.Write([]byte{0, 2, 3}) mac.Write(macedData) myMAC := mac.Sum(nil) if len(myMAC) != len(theirMAC) || subtle.ConstantTimeCompare(myMAC, theirMAC) == 0 { if !ignoreErrors { err = errors.New("otr: bad MAC on data message") } return } if bytes.Compare(counter, slot.theirLastCtr[:]) <= 0 { err = errors.New("otr: counter regressed") return } copy(slot.theirLastCtr[:], counter) var iv [aes.BlockSize]byte copy(iv[:], counter) aesCipher, err := aes.NewCipher(slot.recvAESKey) if err != nil { panic(err.Error()) } ctr := cipher.NewCTR(aesCipher, iv[:]) ctr.XORKeyStream(encrypted, encrypted) decrypted := encrypted if myKeyId == c.myKeyId { c.rotateDHKeys() } if theirKeyId == c.theirKeyId { // evict slots using their retired key id for i := range c.keySlots { slot := &c.keySlots[i] if slot.used && slot.theirKeyId == theirKeyId-1 { slot.used = false c.oldMACs = append(c.oldMACs, slot.sendMACKey...) c.oldMACs = append(c.oldMACs, slot.recvMACKey...) } } c.theirLastDHPub = c.theirCurrentDHPub c.theirKeyId++ c.theirCurrentDHPub = y } if nulPos := bytes.IndexByte(decrypted, 0); nulPos >= 0 { out = decrypted[:nulPos] tlvData := decrypted[nulPos+1:] for len(tlvData) > 0 { var t tlv var ok1, ok2, ok3 bool t.typ, tlvData, ok1 = getU16(tlvData) t.length, tlvData, ok2 = getU16(tlvData) t.data, tlvData, ok3 = getNBytes(tlvData, int(t.length)) if !ok1 || !ok2 || !ok3 { err = errors.New("otr: corrupt tlv data") } tlvs = append(tlvs, t) } } else { out = decrypted } return } func (c *Conversation) generateData(msg []byte, extra *tlv) []byte { slot, err := c.calcDataKeys(c.myKeyId-1, c.theirKeyId) if err != nil { panic("otr: failed to generate sending keys: " + err.Error()) } var plaintext []byte plaintext = append(plaintext, msg...) plaintext = append(plaintext, 0) padding := paddingGranularity - ((len(plaintext) + 4) % paddingGranularity) plaintext = appendU16(plaintext, tlvTypePadding) plaintext = appendU16(plaintext, uint16(padding)) for i := 0; i < padding; i++ { plaintext = append(plaintext, 0) } if extra != nil { plaintext = appendU16(plaintext, extra.typ) plaintext = appendU16(plaintext, uint16(len(extra.data))) plaintext = append(plaintext, extra.data...) } encrypted := make([]byte, len(plaintext)) var iv [aes.BlockSize]byte copy(iv[:], c.myCounter[:]) aesCipher, err := aes.NewCipher(slot.sendAESKey) if err != nil { panic(err.Error()) } ctr := cipher.NewCTR(aesCipher, iv[:]) ctr.XORKeyStream(encrypted, plaintext) var ret []byte ret = appendU16(ret, 2) ret = append(ret, msgTypeData) ret = append(ret, 0 /* flags */) ret = appendU32(ret, c.myKeyId-1) ret = appendU32(ret, c.theirKeyId) ret = appendMPI(ret, c.myCurrentDHPub) ret = append(ret, c.myCounter[:]...) ret = appendData(ret, encrypted) mac := hmac.New(sha1.New, slot.sendMACKey) mac.Write(ret) ret = append(ret, mac.Sum(nil)[:macPrefixBytes]...) ret = appendData(ret, c.oldMACs) c.oldMACs = nil incCounter(&c.myCounter) return ret } func incCounter(counter *[8]byte) { for i := 7; i >= 0; i-- { counter[i]++ if counter[i] > 0 { break } } } // calcDataKeys computes the keys used to encrypt a data message given the key // IDs. func (c *Conversation) calcDataKeys(myKeyId, theirKeyId uint32) (slot *keySlot, err error) { // Check for a cache hit. for i := range c.keySlots { slot = &c.keySlots[i] if slot.used && slot.theirKeyId == theirKeyId && slot.myKeyId == myKeyId { return } } // Find an empty slot to write into. slot = nil for i := range c.keySlots { if !c.keySlots[i].used { slot = &c.keySlots[i] break } } if slot == nil { err = errors.New("otr: internal error: no key slots") return } var myPriv, myPub, theirPub *big.Int if myKeyId == c.myKeyId { myPriv = c.myCurrentDHPriv myPub = c.myCurrentDHPub } else if myKeyId == c.myKeyId-1 { myPriv = c.myLastDHPriv myPub = c.myLastDHPub } else { err = errors.New("otr: peer requested keyid " + strconv.FormatUint(uint64(myKeyId), 10) + " when I'm on " + strconv.FormatUint(uint64(c.myKeyId), 10)) return } if theirKeyId == c.theirKeyId { theirPub = c.theirCurrentDHPub } else if theirKeyId == c.theirKeyId-1 && c.theirLastDHPub != nil { theirPub = c.theirLastDHPub } else { err = errors.New("otr: peer requested keyid " + strconv.FormatUint(uint64(myKeyId), 10) + " when they're on " + strconv.FormatUint(uint64(c.myKeyId), 10)) return } var sendPrefixByte, recvPrefixByte [1]byte if myPub.Cmp(theirPub) > 0 { // we're the high end sendPrefixByte[0], recvPrefixByte[0] = 1, 2 } else { // we're the low end sendPrefixByte[0], recvPrefixByte[0] = 2, 1 } s := new(big.Int).Exp(theirPub, myPriv, p) sBytes := appendMPI(nil, s) h := sha1.New() h.Write(sendPrefixByte[:]) h.Write(sBytes) slot.sendAESKey = h.Sum(slot.sendAESKey[:0])[:16] h.Reset() h.Write(slot.sendAESKey) slot.sendMACKey = h.Sum(slot.sendMACKey[:0]) h.Reset() h.Write(recvPrefixByte[:]) h.Write(sBytes) slot.recvAESKey = h.Sum(slot.recvAESKey[:0])[:16] h.Reset() h.Write(slot.recvAESKey) slot.recvMACKey = h.Sum(slot.recvMACKey[:0]) zero(slot.theirLastCtr[:]) return } func (c *Conversation) calcAKEKeys(s *big.Int) { mpi := appendMPI(nil, s) h := sha256.New() var cBytes [32]byte hashWithPrefix(c.SSID[:], 0, mpi, h) hashWithPrefix(cBytes[:], 1, mpi, h) copy(c.revealKeys.c[:], cBytes[:16]) copy(c.sigKeys.c[:], cBytes[16:]) hashWithPrefix(c.revealKeys.m1[:], 2, mpi, h) hashWithPrefix(c.revealKeys.m2[:], 3, mpi, h) hashWithPrefix(c.sigKeys.m1[:], 4, mpi, h) hashWithPrefix(c.sigKeys.m2[:], 5, mpi, h) } func hashWithPrefix(out []byte, prefix byte, in []byte, h hash.Hash) { h.Reset() var p [1]byte p[0] = prefix h.Write(p[:]) h.Write(in) if len(out) == h.Size() { h.Sum(out[:0]) } else { digest := h.Sum(nil) copy(out, digest) } } func (c *Conversation) encode(msg []byte) [][]byte { b64 := make([]byte, base64.StdEncoding.EncodedLen(len(msg))+len(msgPrefix)+1) base64.StdEncoding.Encode(b64[len(msgPrefix):], msg) copy(b64, msgPrefix) b64[len(b64)-1] = '.' if c.FragmentSize < minFragmentSize || len(b64) <= c.FragmentSize { // We can encode this in a single fragment. return [][]byte{b64} } // We have to fragment this message. var ret [][]byte bytesPerFragment := c.FragmentSize - minFragmentSize numFragments := (len(b64) + bytesPerFragment) / bytesPerFragment for i := 0; i < numFragments; i++ { frag := []byte("?OTR," + strconv.Itoa(i+1) + "," + strconv.Itoa(numFragments) + ",") todo := bytesPerFragment if todo > len(b64) { todo = len(b64) } frag = append(frag, b64[:todo]...) b64 = b64[todo:] frag = append(frag, ',') ret = append(ret, frag) } return ret } type PublicKey struct { dsa.PublicKey } func (pk *PublicKey) Parse(in []byte) ([]byte, bool) { var ok bool var pubKeyType uint16 if pubKeyType, in, ok = getU16(in); !ok || pubKeyType != 0 { return nil, false } if pk.P, in, ok = getMPI(in); !ok { return nil, false } if pk.Q, in, ok = getMPI(in); !ok { return nil, false } if pk.G, in, ok = getMPI(in); !ok { return nil, false } if pk.Y, in, ok = getMPI(in); !ok { return nil, false } return in, true } func (pk *PublicKey) Serialize(in []byte) []byte { in = appendU16(in, 0) in = appendMPI(in, pk.P) in = appendMPI(in, pk.Q) in = appendMPI(in, pk.G) in = appendMPI(in, pk.Y) return in } // Fingerprint returns the 20-byte, binary fingerprint of the PublicKey. func (pk *PublicKey) Fingerprint() []byte { b := pk.Serialize(nil) h := sha1.New() h.Write(b[2:]) return h.Sum(nil) } func (pk *PublicKey) Verify(hashed, sig []byte) ([]byte, bool) { if len(sig) != 2*dsaSubgroupBytes { return nil, false } r := new(big.Int).SetBytes(sig[:dsaSubgroupBytes]) s := new(big.Int).SetBytes(sig[dsaSubgroupBytes:]) ok := dsa.Verify(&pk.PublicKey, hashed, r, s) return sig[dsaSubgroupBytes*2:], ok } type PrivateKey struct { PublicKey dsa.PrivateKey } func (priv *PrivateKey) Sign(rand io.Reader, hashed []byte) []byte { r, s, err := dsa.Sign(rand, &priv.PrivateKey, hashed) if err != nil { panic(err.Error()) } rBytes := r.Bytes() sBytes := s.Bytes() if len(rBytes) > dsaSubgroupBytes || len(sBytes) > dsaSubgroupBytes { panic("DSA signature too large") } out := make([]byte, 2*dsaSubgroupBytes) copy(out[dsaSubgroupBytes-len(rBytes):], rBytes) copy(out[len(out)-len(sBytes):], sBytes) return out } func (priv *PrivateKey) Serialize(in []byte) []byte { in = priv.PublicKey.Serialize(in) in = appendMPI(in, priv.PrivateKey.X) return in } func (priv *PrivateKey) Parse(in []byte) ([]byte, bool) { in, ok := priv.PublicKey.Parse(in) if !ok { return in, ok } priv.PrivateKey.PublicKey = priv.PublicKey.PublicKey priv.PrivateKey.X, in, ok = getMPI(in) return in, ok } func (priv *PrivateKey) Generate(rand io.Reader) { if err := dsa.GenerateParameters(&priv.PrivateKey.PublicKey.Parameters, rand, dsa.L1024N160); err != nil { panic(err.Error()) } if err := dsa.GenerateKey(&priv.PrivateKey, rand); err != nil { panic(err.Error()) } priv.PublicKey.PublicKey = priv.PrivateKey.PublicKey } func notHex(r rune) bool { if r >= '0' && r <= '9' || r >= 'a' && r <= 'f' || r >= 'A' && r <= 'F' { return false } return true } // Import parses the contents of a libotr private key file. func (priv *PrivateKey) Import(in []byte) bool { mpiStart := []byte(" #") mpis := make([]*big.Int, 5) for i := 0; i < len(mpis); i++ { start := bytes.Index(in, mpiStart) if start == -1 { return false } in = in[start+len(mpiStart):] end := bytes.IndexFunc(in, notHex) if end == -1 { return false } hexBytes := in[:end] in = in[end:] if len(hexBytes)&1 != 0 { return false } mpiBytes := make([]byte, len(hexBytes)/2) if _, err := hex.Decode(mpiBytes, hexBytes); err != nil { return false } mpis[i] = new(big.Int).SetBytes(mpiBytes) } priv.PrivateKey.P = mpis[0] priv.PrivateKey.Q = mpis[1] priv.PrivateKey.G = mpis[2] priv.PrivateKey.Y = mpis[3] priv.PrivateKey.X = mpis[4] priv.PublicKey.PublicKey = priv.PrivateKey.PublicKey a := new(big.Int).Exp(priv.PrivateKey.G, priv.PrivateKey.X, priv.PrivateKey.P) return a.Cmp(priv.PrivateKey.Y) == 0 } func getU8(in []byte) (uint8, []byte, bool) { if len(in) < 1 { return 0, in, false } return in[0], in[1:], true } func getU16(in []byte) (uint16, []byte, bool) { if len(in) < 2 { return 0, in, false } r := uint16(in[0])<<8 | uint16(in[1]) return r, in[2:], true } func getU32(in []byte) (uint32, []byte, bool) { if len(in) < 4 { return 0, in, false } r := uint32(in[0])<<24 | uint32(in[1])<<16 | uint32(in[2])<<8 | uint32(in[3]) return r, in[4:], true } func getMPI(in []byte) (*big.Int, []byte, bool) { l, in, ok := getU32(in) if !ok || uint32(len(in)) < l { return nil, in, false } r := new(big.Int).SetBytes(in[:l]) return r, in[l:], true } func getData(in []byte) ([]byte, []byte, bool) { l, in, ok := getU32(in) if !ok || uint32(len(in)) < l { return nil, in, false } return in[:l], in[l:], true } func getNBytes(in []byte, n int) ([]byte, []byte, bool) { if len(in) < n { return nil, in, false } return in[:n], in[n:], true } func appendU16(out []byte, v uint16) []byte { out = append(out, byte(v>>8), byte(v)) return out } func appendU32(out []byte, v uint32) []byte { out = append(out, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) return out } func appendData(out, v []byte) []byte { out = appendU32(out, uint32(len(v))) out = append(out, v...) return out } func appendMPI(out []byte, v *big.Int) []byte { vBytes := v.Bytes() out = appendU32(out, uint32(len(vBytes))) out = append(out, vBytes...) return out } func appendMPIs(out []byte, mpis ...*big.Int) []byte { for _, mpi := range mpis { out = appendMPI(out, mpi) } return out } func zero(b []byte) { for i := range b { b[i] = 0 } } ����������juju-core_1.18.1/src/code.google.com/p/go.crypto/otr/otr_test.go������������������������������������0000644�0000153�0000161�00000027540�12321735647�024376� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 otr import ( "bufio" "bytes" "crypto/rand" "encoding/hex" "math/big" "os" "os/exec" "testing" ) var isQueryTests = []struct { msg string expectedVersion int }{ {"foo", 0}, {"?OtR", 0}, {"?OtR?", 0}, {"?OTR?", 0}, {"?OTRv?", 0}, {"?OTRv1?", 0}, {"?OTR?v1?", 0}, {"?OTR?v?", 0}, {"?OTR?v2?", 2}, {"?OTRv2?", 2}, {"?OTRv23?", 2}, {"?OTRv23 ?", 0}, } func TestIsQuery(t *testing.T) { for i, test := range isQueryTests { version := isQuery([]byte(test.msg)) if version != test.expectedVersion { t.Errorf("#%d: got %d, want %d", i, version, test.expectedVersion) } } } var alicePrivateKeyHex = "000000000080c81c2cb2eb729b7e6fd48e975a932c638b3a9055478583afa46755683e30102447f6da2d8bec9f386bbb5da6403b0040fee8650b6ab2d7f32c55ab017ae9b6aec8c324ab5844784e9a80e194830d548fb7f09a0410df2c4d5c8bc2b3e9ad484e65412be689cf0834694e0839fb2954021521ffdffb8f5c32c14dbf2020b3ce7500000014da4591d58def96de61aea7b04a8405fe1609308d000000808ddd5cb0b9d66956e3dea5a915d9aba9d8a6e7053b74dadb2fc52f9fe4e5bcc487d2305485ed95fed026ad93f06ebb8c9e8baf693b7887132c7ffdd3b0f72f4002ff4ed56583ca7c54458f8c068ca3e8a4dfa309d1dd5d34e2a4b68e6f4338835e5e0fb4317c9e4c7e4806dafda3ef459cd563775a586dd91b1319f72621bf3f00000080b8147e74d8c45e6318c37731b8b33b984a795b3653c2cd1d65cc99efe097cb7eb2fa49569bab5aab6e8a1c261a27d0f7840a5e80b317e6683042b59b6dceca2879c6ffc877a465be690c15e4a42f9a7588e79b10faac11b1ce3741fcef7aba8ce05327a2c16d279ee1b3d77eb783fb10e3356caa25635331e26dd42b8396c4d00000001420bec691fea37ecea58a5c717142f0b804452f57" var aliceFingerprintHex = "0bb01c360424522e94ee9c346ce877a1a4288b2f" var bobPrivateKeyHex = "000000000080a5138eb3d3eb9c1d85716faecadb718f87d31aaed1157671d7fee7e488f95e8e0ba60ad449ec732710a7dec5190f7182af2e2f98312d98497221dff160fd68033dd4f3a33b7c078d0d9f66e26847e76ca7447d4bab35486045090572863d9e4454777f24d6706f63e02548dfec2d0a620af37bbc1d24f884708a212c343b480d00000014e9c58f0ea21a5e4dfd9f44b6a9f7f6a9961a8fa9000000803c4d111aebd62d3c50c2889d420a32cdf1e98b70affcc1fcf44d59cca2eb019f6b774ef88153fb9b9615441a5fe25ea2d11b74ce922ca0232bd81b3c0fcac2a95b20cb6e6c0c5c1ace2e26f65dc43c751af0edbb10d669890e8ab6beea91410b8b2187af1a8347627a06ecea7e0f772c28aae9461301e83884860c9b656c722f0000008065af8625a555ea0e008cd04743671a3cda21162e83af045725db2eb2bb52712708dc0cc1a84c08b3649b88a966974bde27d8612c2861792ec9f08786a246fcadd6d8d3a81a32287745f309238f47618c2bd7612cb8b02d940571e0f30b96420bcd462ff542901b46109b1e5ad6423744448d20a57818a8cbb1647d0fea3b664e0000001440f9f2eb554cb00d45a5826b54bfa419b6980e48" func TestKeySerialization(t *testing.T) { var priv PrivateKey alicePrivateKey, _ := hex.DecodeString(alicePrivateKeyHex) rest, ok := priv.Parse(alicePrivateKey) if !ok { t.Error("failed to parse private key") } if len(rest) > 0 { t.Error("data remaining after parsing private key") } out := priv.Serialize(nil) if !bytes.Equal(alicePrivateKey, out) { t.Errorf("serialization (%x) is not equal to original (%x)", out, alicePrivateKey) } aliceFingerprint, _ := hex.DecodeString(aliceFingerprintHex) fingerprint := priv.PublicKey.Fingerprint() if !bytes.Equal(aliceFingerprint, fingerprint) { t.Errorf("fingerprint (%x) is not equal to expected value (%x)", fingerprint, aliceFingerprint) } } const libOTRPrivateKey = `(privkeys (account (name "foo@example.com") (protocol prpl-jabber) (private-key (dsa (p #00FC07ABCF0DC916AFF6E9AE47BEF60C7AB9B4D6B2469E436630E36F8A489BE812486A09F30B71224508654940A835301ACC525A4FF133FC152CC53DCC59D65C30A54F1993FE13FE63E5823D4C746DB21B90F9B9C00B49EC7404AB1D929BA7FBA12F2E45C6E0A651689750E8528AB8C031D3561FECEE72EBB4A090D450A9B7A857#) (q #00997BD266EF7B1F60A5C23F3A741F2AEFD07A2081#) (g #535E360E8A95EBA46A4F7DE50AD6E9B2A6DB785A66B64EB9F20338D2A3E8FB0E94725848F1AA6CC567CB83A1CC517EC806F2E92EAE71457E80B2210A189B91250779434B41FC8A8873F6DB94BEA7D177F5D59E7E114EE10A49CFD9CEF88AE43387023B672927BA74B04EB6BBB5E57597766A2F9CE3857D7ACE3E1E3BC1FC6F26#) (y #0AC8670AD767D7A8D9D14CC1AC6744CD7D76F993B77FFD9E39DF01E5A6536EF65E775FCEF2A983E2A19BD6415500F6979715D9FD1257E1FE2B6F5E1E74B333079E7C880D39868462A93454B41877BE62E5EF0A041C2EE9C9E76BD1E12AE25D9628DECB097025DD625EF49C3258A1A3C0FF501E3DC673B76D7BABF349009B6ECF#) (x #14D0345A3562C480A039E3C72764F72D79043216#) ) ) ) )` func TestParseLibOTRPrivateKey(t *testing.T) { var priv PrivateKey if !priv.Import([]byte(libOTRPrivateKey)) { t.Fatalf("Failed to import sample private key") } } func TestSignVerify(t *testing.T) { var priv PrivateKey alicePrivateKey, _ := hex.DecodeString(alicePrivateKeyHex) _, ok := priv.Parse(alicePrivateKey) if !ok { t.Error("failed to parse private key") } var msg [32]byte rand.Reader.Read(msg[:]) sig := priv.Sign(rand.Reader, msg[:]) rest, ok := priv.PublicKey.Verify(msg[:], sig) if !ok { t.Errorf("signature (%x) of %x failed to verify", sig, msg[:]) } else if len(rest) > 0 { t.Error("signature data remains after verification") } sig[10] ^= 80 _, ok = priv.PublicKey.Verify(msg[:], sig) if ok { t.Errorf("corrupted signature (%x) of %x verified", sig, msg[:]) } } func TestConversation(t *testing.T) { alicePrivateKey, _ := hex.DecodeString(alicePrivateKeyHex) bobPrivateKey, _ := hex.DecodeString(bobPrivateKeyHex) var alice, bob Conversation alice.PrivateKey = new(PrivateKey) bob.PrivateKey = new(PrivateKey) alice.PrivateKey.Parse(alicePrivateKey) bob.PrivateKey.Parse(bobPrivateKey) alice.FragmentSize = 100 bob.FragmentSize = 100 var alicesMessage, bobsMessage [][]byte var out []byte var aliceChange, bobChange SecurityChange var err error alicesMessage = append(alicesMessage, []byte(QueryMessage)) if alice.IsEncrypted() { t.Error("Alice believes that the conversation is secure before we've started") } if bob.IsEncrypted() { t.Error("Bob believes that the conversation is secure before we've started") } for round := 0; len(alicesMessage) > 0 || len(bobsMessage) > 0; round++ { bobsMessage = nil for i, msg := range alicesMessage { out, _, bobChange, bobsMessage, err = bob.Receive(msg) if len(out) > 0 { t.Errorf("Bob generated output during key exchange, round %d, message %d", round, i) } if err != nil { t.Fatalf("Bob returned an error, round %d, message %d (%x): %s", round, i, msg, err) } if len(bobsMessage) > 0 && i != len(alicesMessage)-1 { t.Errorf("Bob produced output while processing a fragment, round %d, message %d", round, i) } } alicesMessage = nil for i, msg := range bobsMessage { out, _, aliceChange, alicesMessage, err = alice.Receive(msg) if len(out) > 0 { t.Errorf("Alice generated output during key exchange, round %d, message %d", round, i) } if err != nil { t.Fatalf("Alice returned an error, round %d, message %d (%x): %s", round, i, msg, err) } if len(alicesMessage) > 0 && i != len(bobsMessage)-1 { t.Errorf("Alice produced output while processing a fragment, round %d, message %d", round, i) } } } if aliceChange != NewKeys { t.Errorf("Alice terminated without signaling new keys") } if bobChange != NewKeys { t.Errorf("Bob terminated without signaling new keys") } if !bytes.Equal(alice.SSID[:], bob.SSID[:]) { t.Errorf("Session identifiers don't match. Alice has %x, Bob has %x", alice.SSID[:], bob.SSID[:]) } if !alice.IsEncrypted() { t.Error("Alice doesn't believe that the conversation is secure") } if !bob.IsEncrypted() { t.Error("Bob doesn't believe that the conversation is secure") } var testMessage = []byte("hello Bob") alicesMessage, err = alice.Send(testMessage) for i, msg := range alicesMessage { out, encrypted, _, _, err := bob.Receive(msg) if err != nil { t.Errorf("Error generated while processing test message: %s", err.Error()) } if len(out) > 0 { if i != len(alicesMessage)-1 { t.Fatal("Bob produced a message while processing a fragment of Alice's") } if !encrypted { t.Errorf("Message was not marked as encrypted") } if !bytes.Equal(out, testMessage) { t.Errorf("Message corrupted: got %x, want %x", out, testMessage) } } } } func TestGoodSMP(t *testing.T) { var alice, bob Conversation alice.smp.secret = new(big.Int).SetInt64(42) bob.smp.secret = alice.smp.secret var alicesMessages, bobsMessages []tlv var aliceComplete, bobComplete bool var err error var out tlv alicesMessages = alice.startSMP("") for round := 0; len(alicesMessages) > 0 || len(bobsMessages) > 0; round++ { bobsMessages = bobsMessages[:0] for i, msg := range alicesMessages { out, bobComplete, err = bob.processSMP(msg) if err != nil { t.Errorf("Error from Bob in round %d: %s", round, err) } if bobComplete && i != len(alicesMessages)-1 { t.Errorf("Bob returned a completed signal before processing all of Alice's messages in round %d", round) } if out.typ != 0 { bobsMessages = append(bobsMessages, out) } } alicesMessages = alicesMessages[:0] for i, msg := range bobsMessages { out, aliceComplete, err = alice.processSMP(msg) if err != nil { t.Errorf("Error from Alice in round %d: %s", round, err) } if aliceComplete && i != len(bobsMessages)-1 { t.Errorf("Alice returned a completed signal before processing all of Bob's messages in round %d", round) } if out.typ != 0 { alicesMessages = append(alicesMessages, out) } } } if !aliceComplete || !bobComplete { t.Errorf("SMP completed without both sides reporting success: alice: %v, bob: %v\n", aliceComplete, bobComplete) } } func TestBadSMP(t *testing.T) { var alice, bob Conversation alice.smp.secret = new(big.Int).SetInt64(42) bob.smp.secret = new(big.Int).SetInt64(43) var alicesMessages, bobsMessages []tlv alicesMessages = alice.startSMP("") for round := 0; len(alicesMessages) > 0 || len(bobsMessages) > 0; round++ { bobsMessages = bobsMessages[:0] for _, msg := range alicesMessages { out, complete, _ := bob.processSMP(msg) if complete { t.Errorf("Bob signaled completion in round %d", round) } if out.typ != 0 { bobsMessages = append(bobsMessages, out) } } alicesMessages = alicesMessages[:0] for _, msg := range bobsMessages { out, complete, _ := alice.processSMP(msg) if complete { t.Errorf("Alice signaled completion in round %d", round) } if out.typ != 0 { alicesMessages = append(alicesMessages, out) } } } } func TestAgainstLibOTR(t *testing.T) { // This test requires otr.c.test to be built as /tmp/a.out. // If enabled, this tests runs forever performing OTR handshakes in a // loop. return alicePrivateKey, _ := hex.DecodeString(alicePrivateKeyHex) var alice Conversation alice.PrivateKey = new(PrivateKey) alice.PrivateKey.Parse(alicePrivateKey) cmd := exec.Command("/tmp/a.out") cmd.Stderr = os.Stderr out, err := cmd.StdinPipe() if err != nil { t.Fatal(err) } defer out.Close() stdout, err := cmd.StdoutPipe() if err != nil { t.Fatal(err) } in := bufio.NewReader(stdout) if err := cmd.Start(); err != nil { t.Fatal(err) } out.Write([]byte(QueryMessage)) out.Write([]byte("\n")) var expectedText = []byte("test message") for { line, isPrefix, err := in.ReadLine() if isPrefix { t.Fatal("line from subprocess too long") } if err != nil { t.Fatal(err) } text, encrypted, change, alicesMessage, err := alice.Receive(line) if err != nil { t.Fatal(err) } for _, msg := range alicesMessage { out.Write(msg) out.Write([]byte("\n")) } if change == NewKeys { alicesMessage, err := alice.Send([]byte("Go -> libotr test message")) if err != nil { t.Errorf("error sending message: %s", err.Error()) } else { for _, msg := range alicesMessage { out.Write(msg) out.Write([]byte("\n")) } } } if len(text) > 0 { if !bytes.Equal(text, expectedText) { t.Errorf("expected %x, but got %x", expectedText, text) } if !encrypted { t.Error("message wasn't encrypted") } } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/otr/libotr_test_helper.c���������������������������0000644�0000153�0000161�00000012642�12321735647�026236� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // This code can be compiled and used to test the otr package against libotr. // See otr_test.go. // +build ignore #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <proto.h> #include <message.h> static int g_session_established = 0; OtrlPolicy policy(void *opdata, ConnContext *context) { return OTRL_POLICY_ALWAYS; } int is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient) { return 1; } void inject_message(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) { printf("%s\n", message); fflush(stdout); fprintf(stderr, "libotr helper sent: %s\n", message); } void notify(void *opdata, OtrlNotifyLevel level, const char *accountname, const char *protocol, const char *username, const char *title, const char *primary, const char *secondary) { fprintf(stderr, "NOTIFY: %s %s %s %s\n", username, title, primary, secondary); } int display_otr_message(void *opdata, const char *accountname, const char *protocol, const char *username, const char *msg) { fprintf(stderr, "MESSAGE: %s %s\n", username, msg); return 1; } void update_context_list(void *opdata) { } const char *protocol_name(void *opdata, const char *protocol) { return "PROTOCOL"; } void protocol_name_free(void *opdata, const char *protocol_name) { } void new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, const char *protocol, const char *username, unsigned char fingerprint[20]) { fprintf(stderr, "NEW FINGERPRINT\n"); g_session_established = 1; } void write_fingerprints(void *opdata) { } void gone_secure(void *opdata, ConnContext *context) { } void gone_insecure(void *opdata, ConnContext *context) { } void still_secure(void *opdata, ConnContext *context, int is_reply) { } void log_message(void *opdata, const char *message) { fprintf(stderr, "MESSAGE: %s\n", message); } int max_message_size(void *opdata, ConnContext *context) { return 99999; } const char *account_name(void *opdata, const char *account, const char *protocol) { return "ACCOUNT"; } void account_name_free(void *opdata, const char *account_name) { } OtrlMessageAppOps uiops = { policy, NULL, is_logged_in, inject_message, notify, display_otr_message, update_context_list, protocol_name, protocol_name_free, new_fingerprint, write_fingerprints, gone_secure, gone_insecure, still_secure, log_message, max_message_size, account_name, account_name_free, }; static const char kPrivateKeyData[] = "(privkeys (account (name \"account\") (protocol proto) (private-key (dsa (p #00FC07ABCF0DC916AFF6E9AE47BEF60C7AB9B4D6B2469E436630E36F8A489BE812486A09F30B71224508654940A835301ACC525A4FF133FC152CC53DCC59D65C30A54F1993FE13FE63E5823D4C746DB21B90F9B9C00B49EC7404AB1D929BA7FBA12F2E45C6E0A651689750E8528AB8C031D3561FECEE72EBB4A090D450A9B7A857#) (q #00997BD266EF7B1F60A5C23F3A741F2AEFD07A2081#) (g #535E360E8A95EBA46A4F7DE50AD6E9B2A6DB785A66B64EB9F20338D2A3E8FB0E94725848F1AA6CC567CB83A1CC517EC806F2E92EAE71457E80B2210A189B91250779434B41FC8A8873F6DB94BEA7D177F5D59E7E114EE10A49CFD9CEF88AE43387023B672927BA74B04EB6BBB5E57597766A2F9CE3857D7ACE3E1E3BC1FC6F26#) (y #0AC8670AD767D7A8D9D14CC1AC6744CD7D76F993B77FFD9E39DF01E5A6536EF65E775FCEF2A983E2A19BD6415500F6979715D9FD1257E1FE2B6F5E1E74B333079E7C880D39868462A93454B41877BE62E5EF0A041C2EE9C9E76BD1E12AE25D9628DECB097025DD625EF49C3258A1A3C0FF501E3DC673B76D7BABF349009B6ECF#) (x #14D0345A3562C480A039E3C72764F72D79043216#)))))\n"; int main() { OTRL_INIT; // We have to write the private key information to a file because the libotr // API demands a filename to read from. const char *tmpdir = "/tmp"; if (getenv("TMP")) { tmpdir = getenv("TMP"); } char private_key_file[256]; snprintf(private_key_file, sizeof(private_key_file), "%s/libotr_test_helper_privatekeys-XXXXXX", tmpdir); int fd = mkstemp(private_key_file); if (fd == -1) { perror("creating temp file"); } write(fd, kPrivateKeyData, sizeof(kPrivateKeyData)-1); close(fd); OtrlUserState userstate = otrl_userstate_create(); otrl_privkey_read(userstate, private_key_file); unlink(private_key_file); fprintf(stderr, "libotr helper started\n"); char buf[4096]; for (;;) { char* message = fgets(buf, sizeof(buf), stdin); if (strlen(message) == 0) { break; } message[strlen(message) - 1] = 0; fprintf(stderr, "libotr helper got: %s\n", message); char *newmessage = NULL; OtrlTLV *tlvs; int ignore_message = otrl_message_receiving(userstate, &uiops, NULL, "account", "proto", "peer", message, &newmessage, &tlvs, NULL, NULL); if (tlvs) { otrl_tlv_free(tlvs); } if (newmessage != NULL) { fprintf(stderr, "libotr got: %s\n", newmessage); otrl_message_free(newmessage); gcry_error_t err; char *newmessage = NULL; err = otrl_message_sending(userstate, &uiops, NULL, "account", "proto", "peer", "test message", NULL, &newmessage, NULL, NULL); if (newmessage == NULL) { fprintf(stderr, "libotr didn't encrypt message\n"); return 1; } write(1, newmessage, strlen(newmessage)); write(1, "\n", 1); g_session_established = 0; otrl_message_free(newmessage); write(1, "?OTRv2?\n", 8); } } return 0; } ����������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/cast5/���������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022407� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/cast5/cast5.go�������������������������������������0000644�0000153�0000161�00000077367�12321735647�024001� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 cast5 implements CAST5, as defined in RFC 2144. CAST5 is a common // OpenPGP cipher. package cast5 import "errors" const BlockSize = 8 const KeySize = 16 type Cipher struct { masking [16]uint32 rotate [16]uint8 } func NewCipher(key []byte) (c *Cipher, err error) { if len(key) != KeySize { return nil, errors.New("CAST5: keys must be 16 bytes") } c = new(Cipher) c.keySchedule(key) return } func (c *Cipher) BlockSize() int { return BlockSize } func (c *Cipher) Encrypt(dst, src []byte) { l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) l, r = r, l^f1(r, c.masking[0], c.rotate[0]) l, r = r, l^f2(r, c.masking[1], c.rotate[1]) l, r = r, l^f3(r, c.masking[2], c.rotate[2]) l, r = r, l^f1(r, c.masking[3], c.rotate[3]) l, r = r, l^f2(r, c.masking[4], c.rotate[4]) l, r = r, l^f3(r, c.masking[5], c.rotate[5]) l, r = r, l^f1(r, c.masking[6], c.rotate[6]) l, r = r, l^f2(r, c.masking[7], c.rotate[7]) l, r = r, l^f3(r, c.masking[8], c.rotate[8]) l, r = r, l^f1(r, c.masking[9], c.rotate[9]) l, r = r, l^f2(r, c.masking[10], c.rotate[10]) l, r = r, l^f3(r, c.masking[11], c.rotate[11]) l, r = r, l^f1(r, c.masking[12], c.rotate[12]) l, r = r, l^f2(r, c.masking[13], c.rotate[13]) l, r = r, l^f3(r, c.masking[14], c.rotate[14]) l, r = r, l^f1(r, c.masking[15], c.rotate[15]) dst[0] = uint8(r >> 24) dst[1] = uint8(r >> 16) dst[2] = uint8(r >> 8) dst[3] = uint8(r) dst[4] = uint8(l >> 24) dst[5] = uint8(l >> 16) dst[6] = uint8(l >> 8) dst[7] = uint8(l) } func (c *Cipher) Decrypt(dst, src []byte) { l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) l, r = r, l^f1(r, c.masking[15], c.rotate[15]) l, r = r, l^f3(r, c.masking[14], c.rotate[14]) l, r = r, l^f2(r, c.masking[13], c.rotate[13]) l, r = r, l^f1(r, c.masking[12], c.rotate[12]) l, r = r, l^f3(r, c.masking[11], c.rotate[11]) l, r = r, l^f2(r, c.masking[10], c.rotate[10]) l, r = r, l^f1(r, c.masking[9], c.rotate[9]) l, r = r, l^f3(r, c.masking[8], c.rotate[8]) l, r = r, l^f2(r, c.masking[7], c.rotate[7]) l, r = r, l^f1(r, c.masking[6], c.rotate[6]) l, r = r, l^f3(r, c.masking[5], c.rotate[5]) l, r = r, l^f2(r, c.masking[4], c.rotate[4]) l, r = r, l^f1(r, c.masking[3], c.rotate[3]) l, r = r, l^f3(r, c.masking[2], c.rotate[2]) l, r = r, l^f2(r, c.masking[1], c.rotate[1]) l, r = r, l^f1(r, c.masking[0], c.rotate[0]) dst[0] = uint8(r >> 24) dst[1] = uint8(r >> 16) dst[2] = uint8(r >> 8) dst[3] = uint8(r) dst[4] = uint8(l >> 24) dst[5] = uint8(l >> 16) dst[6] = uint8(l >> 8) dst[7] = uint8(l) } type keyScheduleA [4][7]uint8 type keyScheduleB [4][5]uint8 // keyScheduleRound contains the magic values for a round of the key schedule. // The keyScheduleA deals with the lines like: // z0z1z2z3 = x0x1x2x3 ^ S5[xD] ^ S6[xF] ^ S7[xC] ^ S8[xE] ^ S7[x8] // Conceptually, both x and z are in the same array, x first. The first // element describes which word of this array gets written to and the // second, which word gets read. So, for the line above, it's "4, 0", because // it's writing to the first word of z, which, being after x, is word 4, and // reading from the first word of x: word 0. // // Next are the indexes into the S-boxes. Now the array is treated as bytes. So // "xD" is 0xd. The first byte of z is written as "16 + 0", just to be clear // that it's z that we're indexing. // // keyScheduleB deals with lines like: // K1 = S5[z8] ^ S6[z9] ^ S7[z7] ^ S8[z6] ^ S5[z2] // "K1" is ignored because key words are always written in order. So the five // elements are the S-box indexes. They use the same form as in keyScheduleA, // above. type keyScheduleRound struct{} type keySchedule []keyScheduleRound var schedule = []struct { a keyScheduleA b keyScheduleB }{ { keyScheduleA{ {4, 0, 0xd, 0xf, 0xc, 0xe, 0x8}, {5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa}, {6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9}, {7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb}, }, keyScheduleB{ {16 + 8, 16 + 9, 16 + 7, 16 + 6, 16 + 2}, {16 + 0xa, 16 + 0xb, 16 + 5, 16 + 4, 16 + 6}, {16 + 0xc, 16 + 0xd, 16 + 3, 16 + 2, 16 + 9}, {16 + 0xe, 16 + 0xf, 16 + 1, 16 + 0, 16 + 0xc}, }, }, { keyScheduleA{ {0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0}, {1, 4, 0, 2, 1, 3, 16 + 2}, {2, 5, 7, 6, 5, 4, 16 + 1}, {3, 7, 0xa, 9, 0xb, 8, 16 + 3}, }, keyScheduleB{ {3, 2, 0xc, 0xd, 8}, {1, 0, 0xe, 0xf, 0xd}, {7, 6, 8, 9, 3}, {5, 4, 0xa, 0xb, 7}, }, }, { keyScheduleA{ {4, 0, 0xd, 0xf, 0xc, 0xe, 8}, {5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa}, {6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9}, {7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb}, }, keyScheduleB{ {16 + 3, 16 + 2, 16 + 0xc, 16 + 0xd, 16 + 9}, {16 + 1, 16 + 0, 16 + 0xe, 16 + 0xf, 16 + 0xc}, {16 + 7, 16 + 6, 16 + 8, 16 + 9, 16 + 2}, {16 + 5, 16 + 4, 16 + 0xa, 16 + 0xb, 16 + 6}, }, }, { keyScheduleA{ {0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0}, {1, 4, 0, 2, 1, 3, 16 + 2}, {2, 5, 7, 6, 5, 4, 16 + 1}, {3, 7, 0xa, 9, 0xb, 8, 16 + 3}, }, keyScheduleB{ {8, 9, 7, 6, 3}, {0xa, 0xb, 5, 4, 7}, {0xc, 0xd, 3, 2, 8}, {0xe, 0xf, 1, 0, 0xd}, }, }, } func (c *Cipher) keySchedule(in []byte) { var t [8]uint32 var k [32]uint32 for i := 0; i < 4; i++ { j := i * 4 t[i] = uint32(in[j])<<24 | uint32(in[j+1])<<16 | uint32(in[j+2])<<8 | uint32(in[j+3]) } x := []byte{6, 7, 4, 5} ki := 0 for half := 0; half < 2; half++ { for _, round := range schedule { for j := 0; j < 4; j++ { var a [7]uint8 copy(a[:], round.a[j][:]) w := t[a[1]] w ^= sBox[4][(t[a[2]>>2]>>(24-8*(a[2]&3)))&0xff] w ^= sBox[5][(t[a[3]>>2]>>(24-8*(a[3]&3)))&0xff] w ^= sBox[6][(t[a[4]>>2]>>(24-8*(a[4]&3)))&0xff] w ^= sBox[7][(t[a[5]>>2]>>(24-8*(a[5]&3)))&0xff] w ^= sBox[x[j]][(t[a[6]>>2]>>(24-8*(a[6]&3)))&0xff] t[a[0]] = w } for j := 0; j < 4; j++ { var b [5]uint8 copy(b[:], round.b[j][:]) w := sBox[4][(t[b[0]>>2]>>(24-8*(b[0]&3)))&0xff] w ^= sBox[5][(t[b[1]>>2]>>(24-8*(b[1]&3)))&0xff] w ^= sBox[6][(t[b[2]>>2]>>(24-8*(b[2]&3)))&0xff] w ^= sBox[7][(t[b[3]>>2]>>(24-8*(b[3]&3)))&0xff] w ^= sBox[4+j][(t[b[4]>>2]>>(24-8*(b[4]&3)))&0xff] k[ki] = w ki++ } } } for i := 0; i < 16; i++ { c.masking[i] = k[i] c.rotate[i] = uint8(k[16+i] & 0x1f) } } // These are the three 'f' functions. See RFC 2144, section 2.2. func f1(d, m uint32, r uint8) uint32 { t := m + d I := (t << r) | (t >> (32 - r)) return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff] } func f2(d, m uint32, r uint8) uint32 { t := m ^ d I := (t << r) | (t >> (32 - r)) return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff] } func f3(d, m uint32, r uint8) uint32 { t := m - d I := (t << r) | (t >> (32 - r)) return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff] } var sBox = [8][256]uint32{ { 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf, }, { 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1, }, { 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783, }, { 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2, }, { 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4, }, { 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f, }, { 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3, }, { 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e, }, } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/cast5/cast5_test.go��������������������������������0000644�0000153�0000161�00000005100�12321735647�025010� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 cast5 import ( "bytes" "encoding/hex" "testing" ) // This test vector is taken from RFC 2144, App B.1. // Since the other two test vectors are for reduced-round variants, we can't // use them. var basicTests = []struct { key, plainText, cipherText string }{ { "0123456712345678234567893456789a", "0123456789abcdef", "238b4fe5847e44b2", }, } func TestBasic(t *testing.T) { for i, test := range basicTests { key, _ := hex.DecodeString(test.key) plainText, _ := hex.DecodeString(test.plainText) expected, _ := hex.DecodeString(test.cipherText) c, err := NewCipher(key) if err != nil { t.Errorf("#%d: failed to create Cipher: %s", i, err) continue } var cipherText [BlockSize]byte c.Encrypt(cipherText[:], plainText) if !bytes.Equal(cipherText[:], expected) { t.Errorf("#%d: got:%x want:%x", i, cipherText, expected) } var plainTextAgain [BlockSize]byte c.Decrypt(plainTextAgain[:], cipherText[:]) if !bytes.Equal(plainTextAgain[:], plainText) { t.Errorf("#%d: got:%x want:%x", i, plainTextAgain, plainText) } } } // TestFull performs the test specified in RFC 2144, App B.2. // However, due to the length of time taken, it's disabled here and a more // limited version is included, below. func TestFull(t *testing.T) { if testing.Short() { // This is too slow for normal testing return } a, b := iterate(1000000) const expectedA = "eea9d0a249fd3ba6b3436fb89d6dca92" const expectedB = "b2c95eb00c31ad7180ac05b8e83d696e" if hex.EncodeToString(a) != expectedA { t.Errorf("a: got:%x want:%s", a, expectedA) } if hex.EncodeToString(b) != expectedB { t.Errorf("b: got:%x want:%s", b, expectedB) } } func iterate(iterations int) ([]byte, []byte) { const initValueHex = "0123456712345678234567893456789a" initValue, _ := hex.DecodeString(initValueHex) var a, b [16]byte copy(a[:], initValue) copy(b[:], initValue) for i := 0; i < iterations; i++ { c, _ := NewCipher(b[:]) c.Encrypt(a[:8], a[:8]) c.Encrypt(a[8:], a[8:]) c, _ = NewCipher(a[:]) c.Encrypt(b[:8], b[:8]) c.Encrypt(b[8:], b[8:]) } return a[:], b[:] } func TestLimited(t *testing.T) { a, b := iterate(1000) const expectedA = "23f73b14b02a2ad7dfb9f2c35644798d" const expectedB = "e5bf37eff14c456a40b21ce369370a9f" if hex.EncodeToString(a) != expectedA { t.Errorf("a: got:%x want:%s", a, expectedA) } if hex.EncodeToString(b) != expectedB { t.Errorf("b: got:%x want:%s", b, expectedB) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/salsa20/�������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022635� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/salsa20/salsa20.go���������������������������������0000644�0000153�0000161�00000003537�12321735647�024441� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 salsa20 implements the Salsa20 stream cipher as specified in http://cr.yp.to/snuffle/spec.pdf. Salsa20 differs from many other stream ciphers in that it is message orientated rather than byte orientated. Keystream blocks are not preserved between calls, therefore each side must encrypt/decrypt data with the same segmentation. Another aspect of this difference is that part of the counter is exposed as an nonce in each call. Encrypting two different messages with the same (key, nonce) pair leads to trivial plaintext recovery. This is analogous to encrypting two different messages with the same key with a traditional stream cipher. This package also implements XSalsa20: a version of Salsa20 with a 24-byte nonce as specified in http://cr.yp.to/snuffle/xsalsa-20081128.pdf. Simply passing a 24-byte slice as the nonce triggers XSalsa20. */ package salsa20 // TODO(agl): implement XORKeyStream12 and XORKeyStream8 - the reduced round variants of Salsa20. import ( "code.google.com/p/go.crypto/salsa20/salsa" ) // XORKeyStream crypts bytes from in to out using the given key and nonce. In // and out may be the same slice but otherwise should not overlap. Nonce must // be either 8 or 24 bytes long. func XORKeyStream(out, in []byte, nonce []byte, key *[32]byte) { if len(out) < len(in) { in = in[:len(out)] } var subNonce [16]byte if len(nonce) == 24 { var subKey [32]byte var hNonce [16]byte copy(hNonce[:], nonce[:16]) salsa.HSalsa20(&subKey, &hNonce, key, &salsa.Sigma) copy(subNonce[:], nonce[16:]) key = &subKey } else if len(nonce) == 8 { copy(subNonce[:], nonce[:]) } else { panic("salsa20: nonce must be 8 or 24 bytes") } salsa.XORKeyStream(out, in, &subNonce, key) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/salsa20/salsa20_test.go����������������������������0000644�0000153�0000161�00000007273�12321735647�025501� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 salsa20 import ( "bytes" "encoding/hex" "testing" ) func fromHex(s string) []byte { ret, err := hex.DecodeString(s) if err != nil { panic(err) } return ret } // testVectors was taken from set 6 of the ECRYPT test vectors: // http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/full/verified.test-vectors?logsort=rev&rev=210&view=markup var testVectors = []struct { key []byte iv []byte numBytes int xor []byte }{ { fromHex("0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D"), fromHex("0D74DB42A91077DE"), 131072, fromHex("C349B6A51A3EC9B712EAED3F90D8BCEE69B7628645F251A996F55260C62EF31FD6C6B0AEA94E136C9D984AD2DF3578F78E457527B03A0450580DD874F63B1AB9"), }, { fromHex("0558ABFE51A4F74A9DF04396E93C8FE23588DB2E81D4277ACD2073C6196CBF12"), fromHex("167DE44BB21980E7"), 131072, fromHex("C3EAAF32836BACE32D04E1124231EF47E101367D6305413A0EEB07C60698A2876E4D031870A739D6FFDDD208597AFF0A47AC17EDB0167DD67EBA84F1883D4DFD"), }, { fromHex("0A5DB00356A9FC4FA2F5489BEE4194E73A8DE03386D92C7FD22578CB1E71C417"), fromHex("1F86ED54BB2289F0"), 131072, fromHex("3CD23C3DC90201ACC0CF49B440B6C417F0DC8D8410A716D5314C059E14B1A8D9A9FB8EA3D9C8DAE12B21402F674AA95C67B1FC514E994C9D3F3A6E41DFF5BBA6"), }, { fromHex("0F62B5085BAE0154A7FA4DA0F34699EC3F92E5388BDE3184D72A7DD02376C91C"), fromHex("288FF65DC42B92F9"), 131072, fromHex("E00EBCCD70D69152725F9987982178A2E2E139C7BCBE04CA8A0E99E318D9AB76F988C8549F75ADD790BA4F81C176DA653C1A043F11A958E169B6D2319F4EEC1A"), }, } func TestSalsa20(t *testing.T) { var inBuf, outBuf []byte var key [32]byte for i, test := range testVectors { if test.numBytes%64 != 0 { t.Errorf("#%d: numBytes is not a multiple of 64", i) continue } if test.numBytes > len(inBuf) { inBuf = make([]byte, test.numBytes) outBuf = make([]byte, test.numBytes) } in := inBuf[:test.numBytes] out := outBuf[:test.numBytes] copy(key[:], test.key) XORKeyStream(out, in, test.iv, &key) var xor [64]byte for len(out) > 0 { for i := 0; i < 64; i++ { xor[i] ^= out[i] } out = out[64:] } if !bytes.Equal(xor[:], test.xor) { t.Errorf("#%d: bad result", i) } } } var xSalsa20TestData = []struct { in, nonce, key, out []byte }{ { []byte("Hello world!"), []byte("24-byte nonce for xsalsa"), []byte("this is 32-byte key for xsalsa20"), []byte{0x00, 0x2d, 0x45, 0x13, 0x84, 0x3f, 0xc2, 0x40, 0xc4, 0x01, 0xe5, 0x41}, }, { make([]byte, 64), []byte("24-byte nonce for xsalsa"), []byte("this is 32-byte key for xsalsa20"), []byte{0x48, 0x48, 0x29, 0x7f, 0xeb, 0x1f, 0xb5, 0x2f, 0xb6, 0x6d, 0x81, 0x60, 0x9b, 0xd5, 0x47, 0xfa, 0xbc, 0xbe, 0x70, 0x26, 0xed, 0xc8, 0xb5, 0xe5, 0xe4, 0x49, 0xd0, 0x88, 0xbf, 0xa6, 0x9c, 0x08, 0x8f, 0x5d, 0x8d, 0xa1, 0xd7, 0x91, 0x26, 0x7c, 0x2c, 0x19, 0x5a, 0x7f, 0x8c, 0xae, 0x9c, 0x4b, 0x40, 0x50, 0xd0, 0x8c, 0xe6, 0xd3, 0xa1, 0x51, 0xec, 0x26, 0x5f, 0x3a, 0x58, 0xe4, 0x76, 0x48}, }, } func TestXSalsa20(t *testing.T) { var key [32]byte for i, test := range xSalsa20TestData { out := make([]byte, len(test.in)) copy(key[:], test.key) XORKeyStream(out, test.in, test.nonce, &key) if !bytes.Equal(out, test.out) { t.Errorf("%d: expected %x, got %x", i, test.out, out) } } } var ( keyArray [32]byte key = &keyArray nonce [8]byte msg = make([]byte, 1<<10) ) func BenchmarkXOR1K(b *testing.B) { b.StopTimer() out := make([]byte, 1024) b.StartTimer() for i := 0; i < b.N; i++ { XORKeyStream(out, msg[:1024], nonce[:], key) } b.SetBytes(1024) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/salsa20/salsa/�������������������������������������0000755�0000153�0000161�00000000000�12321735647�023740� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/salsa20/salsa/salsa20_amd64.go���������������������0000644�0000153�0000161�00000001373�12321735647�026533� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 amd64,!appengine,!gccgo package salsa // This function is implemented in salsa2020_amd64.s. //go:noescape func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte) // XORKeyStream crypts bytes from in to out using the given key and counters. // In and out may be the same slice but otherwise should not overlap. Counter // contains the raw salsa20 counter bytes (both nonce and block counter). func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) { if len(in) == 0 { return } salsa2020XORKeyStream(&out[0], &in[0], uint64(len(in)), &counter[0], &key[0]) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/salsa20/salsa/salsa208.go��������������������������0000644�0000153�0000161�00000011567�12321735647�025636� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 salsa // Core208 applies the Salsa20/8 core function to the 64-byte array in and puts // the result into the 64-byte array out. The input and output may be the same array. func Core208(out *[64]byte, in *[64]byte) { j0 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 j1 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24 j2 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24 j3 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24 j4 := uint32(in[16]) | uint32(in[17])<<8 | uint32(in[18])<<16 | uint32(in[19])<<24 j5 := uint32(in[20]) | uint32(in[21])<<8 | uint32(in[22])<<16 | uint32(in[23])<<24 j6 := uint32(in[24]) | uint32(in[25])<<8 | uint32(in[26])<<16 | uint32(in[27])<<24 j7 := uint32(in[28]) | uint32(in[29])<<8 | uint32(in[30])<<16 | uint32(in[31])<<24 j8 := uint32(in[32]) | uint32(in[33])<<8 | uint32(in[34])<<16 | uint32(in[35])<<24 j9 := uint32(in[36]) | uint32(in[37])<<8 | uint32(in[38])<<16 | uint32(in[39])<<24 j10 := uint32(in[40]) | uint32(in[41])<<8 | uint32(in[42])<<16 | uint32(in[43])<<24 j11 := uint32(in[44]) | uint32(in[45])<<8 | uint32(in[46])<<16 | uint32(in[47])<<24 j12 := uint32(in[48]) | uint32(in[49])<<8 | uint32(in[50])<<16 | uint32(in[51])<<24 j13 := uint32(in[52]) | uint32(in[53])<<8 | uint32(in[54])<<16 | uint32(in[55])<<24 j14 := uint32(in[56]) | uint32(in[57])<<8 | uint32(in[58])<<16 | uint32(in[59])<<24 j15 := uint32(in[60]) | uint32(in[61])<<8 | uint32(in[62])<<16 | uint32(in[63])<<24 x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8 x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15 for i := 0; i < 8; i += 2 { u := x0 + x12 x4 ^= u<<7 | u>>(32-7) u = x4 + x0 x8 ^= u<<9 | u>>(32-9) u = x8 + x4 x12 ^= u<<13 | u>>(32-13) u = x12 + x8 x0 ^= u<<18 | u>>(32-18) u = x5 + x1 x9 ^= u<<7 | u>>(32-7) u = x9 + x5 x13 ^= u<<9 | u>>(32-9) u = x13 + x9 x1 ^= u<<13 | u>>(32-13) u = x1 + x13 x5 ^= u<<18 | u>>(32-18) u = x10 + x6 x14 ^= u<<7 | u>>(32-7) u = x14 + x10 x2 ^= u<<9 | u>>(32-9) u = x2 + x14 x6 ^= u<<13 | u>>(32-13) u = x6 + x2 x10 ^= u<<18 | u>>(32-18) u = x15 + x11 x3 ^= u<<7 | u>>(32-7) u = x3 + x15 x7 ^= u<<9 | u>>(32-9) u = x7 + x3 x11 ^= u<<13 | u>>(32-13) u = x11 + x7 x15 ^= u<<18 | u>>(32-18) u = x0 + x3 x1 ^= u<<7 | u>>(32-7) u = x1 + x0 x2 ^= u<<9 | u>>(32-9) u = x2 + x1 x3 ^= u<<13 | u>>(32-13) u = x3 + x2 x0 ^= u<<18 | u>>(32-18) u = x5 + x4 x6 ^= u<<7 | u>>(32-7) u = x6 + x5 x7 ^= u<<9 | u>>(32-9) u = x7 + x6 x4 ^= u<<13 | u>>(32-13) u = x4 + x7 x5 ^= u<<18 | u>>(32-18) u = x10 + x9 x11 ^= u<<7 | u>>(32-7) u = x11 + x10 x8 ^= u<<9 | u>>(32-9) u = x8 + x11 x9 ^= u<<13 | u>>(32-13) u = x9 + x8 x10 ^= u<<18 | u>>(32-18) u = x15 + x14 x12 ^= u<<7 | u>>(32-7) u = x12 + x15 x13 ^= u<<9 | u>>(32-9) u = x13 + x12 x14 ^= u<<13 | u>>(32-13) u = x14 + x13 x15 ^= u<<18 | u>>(32-18) } x0 += j0 x1 += j1 x2 += j2 x3 += j3 x4 += j4 x5 += j5 x6 += j6 x7 += j7 x8 += j8 x9 += j9 x10 += j10 x11 += j11 x12 += j12 x13 += j13 x14 += j14 x15 += j15 out[0] = byte(x0) out[1] = byte(x0 >> 8) out[2] = byte(x0 >> 16) out[3] = byte(x0 >> 24) out[4] = byte(x1) out[5] = byte(x1 >> 8) out[6] = byte(x1 >> 16) out[7] = byte(x1 >> 24) out[8] = byte(x2) out[9] = byte(x2 >> 8) out[10] = byte(x2 >> 16) out[11] = byte(x2 >> 24) out[12] = byte(x3) out[13] = byte(x3 >> 8) out[14] = byte(x3 >> 16) out[15] = byte(x3 >> 24) out[16] = byte(x4) out[17] = byte(x4 >> 8) out[18] = byte(x4 >> 16) out[19] = byte(x4 >> 24) out[20] = byte(x5) out[21] = byte(x5 >> 8) out[22] = byte(x5 >> 16) out[23] = byte(x5 >> 24) out[24] = byte(x6) out[25] = byte(x6 >> 8) out[26] = byte(x6 >> 16) out[27] = byte(x6 >> 24) out[28] = byte(x7) out[29] = byte(x7 >> 8) out[30] = byte(x7 >> 16) out[31] = byte(x7 >> 24) out[32] = byte(x8) out[33] = byte(x8 >> 8) out[34] = byte(x8 >> 16) out[35] = byte(x8 >> 24) out[36] = byte(x9) out[37] = byte(x9 >> 8) out[38] = byte(x9 >> 16) out[39] = byte(x9 >> 24) out[40] = byte(x10) out[41] = byte(x10 >> 8) out[42] = byte(x10 >> 16) out[43] = byte(x10 >> 24) out[44] = byte(x11) out[45] = byte(x11 >> 8) out[46] = byte(x11 >> 16) out[47] = byte(x11 >> 24) out[48] = byte(x12) out[49] = byte(x12 >> 8) out[50] = byte(x12 >> 16) out[51] = byte(x12 >> 24) out[52] = byte(x13) out[53] = byte(x13 >> 8) out[54] = byte(x13 >> 16) out[55] = byte(x13 >> 24) out[56] = byte(x14) out[57] = byte(x14 >> 8) out[58] = byte(x14 >> 16) out[59] = byte(x14 >> 24) out[60] = byte(x15) out[61] = byte(x15 >> 8) out[62] = byte(x15 >> 16) out[63] = byte(x15 >> 24) } �����������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/salsa20/salsa/salsa2020_amd64.s��������������������0000644�0000153�0000161�00000033151�12321735647�026531� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 amd64,!appengine,!gccgo // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html // func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte) TEXT ·salsa2020XORKeyStream(SB),0,$512-40 MOVQ out+0(FP),DI MOVQ in+8(FP),SI MOVQ n+16(FP),DX MOVQ nonce+24(FP),CX MOVQ key+32(FP),R8 MOVQ SP,R11 MOVQ $31,R9 NOTQ R9 ANDQ R9,SP ADDQ $32,SP MOVQ R11,352(SP) MOVQ R12,360(SP) MOVQ R13,368(SP) MOVQ R14,376(SP) MOVQ R15,384(SP) MOVQ BX,392(SP) MOVQ BP,400(SP) MOVQ DX,R9 MOVQ CX,DX MOVQ R8,R10 CMPQ R9,$0 JBE DONE START: MOVL 20(R10),CX MOVL 0(R10),R8 MOVL 0(DX),AX MOVL 16(R10),R11 MOVL CX,0(SP) MOVL R8, 4 (SP) MOVL AX, 8 (SP) MOVL R11, 12 (SP) MOVL 8(DX),CX MOVL 24(R10),R8 MOVL 4(R10),AX MOVL 4(DX),R11 MOVL CX,16(SP) MOVL R8, 20 (SP) MOVL AX, 24 (SP) MOVL R11, 28 (SP) MOVL 12(DX),CX MOVL 12(R10),DX MOVL 28(R10),R8 MOVL 8(R10),AX MOVL DX,32(SP) MOVL CX, 36 (SP) MOVL R8, 40 (SP) MOVL AX, 44 (SP) MOVQ $1634760805,DX MOVQ $857760878,CX MOVQ $2036477234,R8 MOVQ $1797285236,AX MOVL DX,48(SP) MOVL CX, 52 (SP) MOVL R8, 56 (SP) MOVL AX, 60 (SP) CMPQ R9,$256 JB BYTESBETWEEN1AND255 MOVOA 48(SP),X0 PSHUFL $0X55,X0,X1 PSHUFL $0XAA,X0,X2 PSHUFL $0XFF,X0,X3 PSHUFL $0X00,X0,X0 MOVOA X1,64(SP) MOVOA X2,80(SP) MOVOA X3,96(SP) MOVOA X0,112(SP) MOVOA 0(SP),X0 PSHUFL $0XAA,X0,X1 PSHUFL $0XFF,X0,X2 PSHUFL $0X00,X0,X3 PSHUFL $0X55,X0,X0 MOVOA X1,128(SP) MOVOA X2,144(SP) MOVOA X3,160(SP) MOVOA X0,176(SP) MOVOA 16(SP),X0 PSHUFL $0XFF,X0,X1 PSHUFL $0X55,X0,X2 PSHUFL $0XAA,X0,X0 MOVOA X1,192(SP) MOVOA X2,208(SP) MOVOA X0,224(SP) MOVOA 32(SP),X0 PSHUFL $0X00,X0,X1 PSHUFL $0XAA,X0,X2 PSHUFL $0XFF,X0,X0 MOVOA X1,240(SP) MOVOA X2,256(SP) MOVOA X0,272(SP) BYTESATLEAST256: MOVL 16(SP),DX MOVL 36 (SP),CX MOVL DX,288(SP) MOVL CX,304(SP) ADDQ $1,DX SHLQ $32,CX ADDQ CX,DX MOVQ DX,CX SHRQ $32,CX MOVL DX, 292 (SP) MOVL CX, 308 (SP) ADDQ $1,DX SHLQ $32,CX ADDQ CX,DX MOVQ DX,CX SHRQ $32,CX MOVL DX, 296 (SP) MOVL CX, 312 (SP) ADDQ $1,DX SHLQ $32,CX ADDQ CX,DX MOVQ DX,CX SHRQ $32,CX MOVL DX, 300 (SP) MOVL CX, 316 (SP) ADDQ $1,DX SHLQ $32,CX ADDQ CX,DX MOVQ DX,CX SHRQ $32,CX MOVL DX,16(SP) MOVL CX, 36 (SP) MOVQ R9,408(SP) MOVQ $20,DX MOVOA 64(SP),X0 MOVOA 80(SP),X1 MOVOA 96(SP),X2 MOVOA 256(SP),X3 MOVOA 272(SP),X4 MOVOA 128(SP),X5 MOVOA 144(SP),X6 MOVOA 176(SP),X7 MOVOA 192(SP),X8 MOVOA 208(SP),X9 MOVOA 224(SP),X10 MOVOA 304(SP),X11 MOVOA 112(SP),X12 MOVOA 160(SP),X13 MOVOA 240(SP),X14 MOVOA 288(SP),X15 MAINLOOP1: MOVOA X1,320(SP) MOVOA X2,336(SP) MOVOA X13,X1 PADDL X12,X1 MOVOA X1,X2 PSLLL $7,X1 PXOR X1,X14 PSRLL $25,X2 PXOR X2,X14 MOVOA X7,X1 PADDL X0,X1 MOVOA X1,X2 PSLLL $7,X1 PXOR X1,X11 PSRLL $25,X2 PXOR X2,X11 MOVOA X12,X1 PADDL X14,X1 MOVOA X1,X2 PSLLL $9,X1 PXOR X1,X15 PSRLL $23,X2 PXOR X2,X15 MOVOA X0,X1 PADDL X11,X1 MOVOA X1,X2 PSLLL $9,X1 PXOR X1,X9 PSRLL $23,X2 PXOR X2,X9 MOVOA X14,X1 PADDL X15,X1 MOVOA X1,X2 PSLLL $13,X1 PXOR X1,X13 PSRLL $19,X2 PXOR X2,X13 MOVOA X11,X1 PADDL X9,X1 MOVOA X1,X2 PSLLL $13,X1 PXOR X1,X7 PSRLL $19,X2 PXOR X2,X7 MOVOA X15,X1 PADDL X13,X1 MOVOA X1,X2 PSLLL $18,X1 PXOR X1,X12 PSRLL $14,X2 PXOR X2,X12 MOVOA 320(SP),X1 MOVOA X12,320(SP) MOVOA X9,X2 PADDL X7,X2 MOVOA X2,X12 PSLLL $18,X2 PXOR X2,X0 PSRLL $14,X12 PXOR X12,X0 MOVOA X5,X2 PADDL X1,X2 MOVOA X2,X12 PSLLL $7,X2 PXOR X2,X3 PSRLL $25,X12 PXOR X12,X3 MOVOA 336(SP),X2 MOVOA X0,336(SP) MOVOA X6,X0 PADDL X2,X0 MOVOA X0,X12 PSLLL $7,X0 PXOR X0,X4 PSRLL $25,X12 PXOR X12,X4 MOVOA X1,X0 PADDL X3,X0 MOVOA X0,X12 PSLLL $9,X0 PXOR X0,X10 PSRLL $23,X12 PXOR X12,X10 MOVOA X2,X0 PADDL X4,X0 MOVOA X0,X12 PSLLL $9,X0 PXOR X0,X8 PSRLL $23,X12 PXOR X12,X8 MOVOA X3,X0 PADDL X10,X0 MOVOA X0,X12 PSLLL $13,X0 PXOR X0,X5 PSRLL $19,X12 PXOR X12,X5 MOVOA X4,X0 PADDL X8,X0 MOVOA X0,X12 PSLLL $13,X0 PXOR X0,X6 PSRLL $19,X12 PXOR X12,X6 MOVOA X10,X0 PADDL X5,X0 MOVOA X0,X12 PSLLL $18,X0 PXOR X0,X1 PSRLL $14,X12 PXOR X12,X1 MOVOA 320(SP),X0 MOVOA X1,320(SP) MOVOA X4,X1 PADDL X0,X1 MOVOA X1,X12 PSLLL $7,X1 PXOR X1,X7 PSRLL $25,X12 PXOR X12,X7 MOVOA X8,X1 PADDL X6,X1 MOVOA X1,X12 PSLLL $18,X1 PXOR X1,X2 PSRLL $14,X12 PXOR X12,X2 MOVOA 336(SP),X12 MOVOA X2,336(SP) MOVOA X14,X1 PADDL X12,X1 MOVOA X1,X2 PSLLL $7,X1 PXOR X1,X5 PSRLL $25,X2 PXOR X2,X5 MOVOA X0,X1 PADDL X7,X1 MOVOA X1,X2 PSLLL $9,X1 PXOR X1,X10 PSRLL $23,X2 PXOR X2,X10 MOVOA X12,X1 PADDL X5,X1 MOVOA X1,X2 PSLLL $9,X1 PXOR X1,X8 PSRLL $23,X2 PXOR X2,X8 MOVOA X7,X1 PADDL X10,X1 MOVOA X1,X2 PSLLL $13,X1 PXOR X1,X4 PSRLL $19,X2 PXOR X2,X4 MOVOA X5,X1 PADDL X8,X1 MOVOA X1,X2 PSLLL $13,X1 PXOR X1,X14 PSRLL $19,X2 PXOR X2,X14 MOVOA X10,X1 PADDL X4,X1 MOVOA X1,X2 PSLLL $18,X1 PXOR X1,X0 PSRLL $14,X2 PXOR X2,X0 MOVOA 320(SP),X1 MOVOA X0,320(SP) MOVOA X8,X0 PADDL X14,X0 MOVOA X0,X2 PSLLL $18,X0 PXOR X0,X12 PSRLL $14,X2 PXOR X2,X12 MOVOA X11,X0 PADDL X1,X0 MOVOA X0,X2 PSLLL $7,X0 PXOR X0,X6 PSRLL $25,X2 PXOR X2,X6 MOVOA 336(SP),X2 MOVOA X12,336(SP) MOVOA X3,X0 PADDL X2,X0 MOVOA X0,X12 PSLLL $7,X0 PXOR X0,X13 PSRLL $25,X12 PXOR X12,X13 MOVOA X1,X0 PADDL X6,X0 MOVOA X0,X12 PSLLL $9,X0 PXOR X0,X15 PSRLL $23,X12 PXOR X12,X15 MOVOA X2,X0 PADDL X13,X0 MOVOA X0,X12 PSLLL $9,X0 PXOR X0,X9 PSRLL $23,X12 PXOR X12,X9 MOVOA X6,X0 PADDL X15,X0 MOVOA X0,X12 PSLLL $13,X0 PXOR X0,X11 PSRLL $19,X12 PXOR X12,X11 MOVOA X13,X0 PADDL X9,X0 MOVOA X0,X12 PSLLL $13,X0 PXOR X0,X3 PSRLL $19,X12 PXOR X12,X3 MOVOA X15,X0 PADDL X11,X0 MOVOA X0,X12 PSLLL $18,X0 PXOR X0,X1 PSRLL $14,X12 PXOR X12,X1 MOVOA X9,X0 PADDL X3,X0 MOVOA X0,X12 PSLLL $18,X0 PXOR X0,X2 PSRLL $14,X12 PXOR X12,X2 MOVOA 320(SP),X12 MOVOA 336(SP),X0 SUBQ $2,DX JA MAINLOOP1 PADDL 112(SP),X12 PADDL 176(SP),X7 PADDL 224(SP),X10 PADDL 272(SP),X4 MOVD X12,DX MOVD X7,CX MOVD X10,R8 MOVD X4,R9 PSHUFL $0X39,X12,X12 PSHUFL $0X39,X7,X7 PSHUFL $0X39,X10,X10 PSHUFL $0X39,X4,X4 XORL 0(SI),DX XORL 4(SI),CX XORL 8(SI),R8 XORL 12(SI),R9 MOVL DX,0(DI) MOVL CX,4(DI) MOVL R8,8(DI) MOVL R9,12(DI) MOVD X12,DX MOVD X7,CX MOVD X10,R8 MOVD X4,R9 PSHUFL $0X39,X12,X12 PSHUFL $0X39,X7,X7 PSHUFL $0X39,X10,X10 PSHUFL $0X39,X4,X4 XORL 64(SI),DX XORL 68(SI),CX XORL 72(SI),R8 XORL 76(SI),R9 MOVL DX,64(DI) MOVL CX,68(DI) MOVL R8,72(DI) MOVL R9,76(DI) MOVD X12,DX MOVD X7,CX MOVD X10,R8 MOVD X4,R9 PSHUFL $0X39,X12,X12 PSHUFL $0X39,X7,X7 PSHUFL $0X39,X10,X10 PSHUFL $0X39,X4,X4 XORL 128(SI),DX XORL 132(SI),CX XORL 136(SI),R8 XORL 140(SI),R9 MOVL DX,128(DI) MOVL CX,132(DI) MOVL R8,136(DI) MOVL R9,140(DI) MOVD X12,DX MOVD X7,CX MOVD X10,R8 MOVD X4,R9 XORL 192(SI),DX XORL 196(SI),CX XORL 200(SI),R8 XORL 204(SI),R9 MOVL DX,192(DI) MOVL CX,196(DI) MOVL R8,200(DI) MOVL R9,204(DI) PADDL 240(SP),X14 PADDL 64(SP),X0 PADDL 128(SP),X5 PADDL 192(SP),X8 MOVD X14,DX MOVD X0,CX MOVD X5,R8 MOVD X8,R9 PSHUFL $0X39,X14,X14 PSHUFL $0X39,X0,X0 PSHUFL $0X39,X5,X5 PSHUFL $0X39,X8,X8 XORL 16(SI),DX XORL 20(SI),CX XORL 24(SI),R8 XORL 28(SI),R9 MOVL DX,16(DI) MOVL CX,20(DI) MOVL R8,24(DI) MOVL R9,28(DI) MOVD X14,DX MOVD X0,CX MOVD X5,R8 MOVD X8,R9 PSHUFL $0X39,X14,X14 PSHUFL $0X39,X0,X0 PSHUFL $0X39,X5,X5 PSHUFL $0X39,X8,X8 XORL 80(SI),DX XORL 84(SI),CX XORL 88(SI),R8 XORL 92(SI),R9 MOVL DX,80(DI) MOVL CX,84(DI) MOVL R8,88(DI) MOVL R9,92(DI) MOVD X14,DX MOVD X0,CX MOVD X5,R8 MOVD X8,R9 PSHUFL $0X39,X14,X14 PSHUFL $0X39,X0,X0 PSHUFL $0X39,X5,X5 PSHUFL $0X39,X8,X8 XORL 144(SI),DX XORL 148(SI),CX XORL 152(SI),R8 XORL 156(SI),R9 MOVL DX,144(DI) MOVL CX,148(DI) MOVL R8,152(DI) MOVL R9,156(DI) MOVD X14,DX MOVD X0,CX MOVD X5,R8 MOVD X8,R9 XORL 208(SI),DX XORL 212(SI),CX XORL 216(SI),R8 XORL 220(SI),R9 MOVL DX,208(DI) MOVL CX,212(DI) MOVL R8,216(DI) MOVL R9,220(DI) PADDL 288(SP),X15 PADDL 304(SP),X11 PADDL 80(SP),X1 PADDL 144(SP),X6 MOVD X15,DX MOVD X11,CX MOVD X1,R8 MOVD X6,R9 PSHUFL $0X39,X15,X15 PSHUFL $0X39,X11,X11 PSHUFL $0X39,X1,X1 PSHUFL $0X39,X6,X6 XORL 32(SI),DX XORL 36(SI),CX XORL 40(SI),R8 XORL 44(SI),R9 MOVL DX,32(DI) MOVL CX,36(DI) MOVL R8,40(DI) MOVL R9,44(DI) MOVD X15,DX MOVD X11,CX MOVD X1,R8 MOVD X6,R9 PSHUFL $0X39,X15,X15 PSHUFL $0X39,X11,X11 PSHUFL $0X39,X1,X1 PSHUFL $0X39,X6,X6 XORL 96(SI),DX XORL 100(SI),CX XORL 104(SI),R8 XORL 108(SI),R9 MOVL DX,96(DI) MOVL CX,100(DI) MOVL R8,104(DI) MOVL R9,108(DI) MOVD X15,DX MOVD X11,CX MOVD X1,R8 MOVD X6,R9 PSHUFL $0X39,X15,X15 PSHUFL $0X39,X11,X11 PSHUFL $0X39,X1,X1 PSHUFL $0X39,X6,X6 XORL 160(SI),DX XORL 164(SI),CX XORL 168(SI),R8 XORL 172(SI),R9 MOVL DX,160(DI) MOVL CX,164(DI) MOVL R8,168(DI) MOVL R9,172(DI) MOVD X15,DX MOVD X11,CX MOVD X1,R8 MOVD X6,R9 XORL 224(SI),DX XORL 228(SI),CX XORL 232(SI),R8 XORL 236(SI),R9 MOVL DX,224(DI) MOVL CX,228(DI) MOVL R8,232(DI) MOVL R9,236(DI) PADDL 160(SP),X13 PADDL 208(SP),X9 PADDL 256(SP),X3 PADDL 96(SP),X2 MOVD X13,DX MOVD X9,CX MOVD X3,R8 MOVD X2,R9 PSHUFL $0X39,X13,X13 PSHUFL $0X39,X9,X9 PSHUFL $0X39,X3,X3 PSHUFL $0X39,X2,X2 XORL 48(SI),DX XORL 52(SI),CX XORL 56(SI),R8 XORL 60(SI),R9 MOVL DX,48(DI) MOVL CX,52(DI) MOVL R8,56(DI) MOVL R9,60(DI) MOVD X13,DX MOVD X9,CX MOVD X3,R8 MOVD X2,R9 PSHUFL $0X39,X13,X13 PSHUFL $0X39,X9,X9 PSHUFL $0X39,X3,X3 PSHUFL $0X39,X2,X2 XORL 112(SI),DX XORL 116(SI),CX XORL 120(SI),R8 XORL 124(SI),R9 MOVL DX,112(DI) MOVL CX,116(DI) MOVL R8,120(DI) MOVL R9,124(DI) MOVD X13,DX MOVD X9,CX MOVD X3,R8 MOVD X2,R9 PSHUFL $0X39,X13,X13 PSHUFL $0X39,X9,X9 PSHUFL $0X39,X3,X3 PSHUFL $0X39,X2,X2 XORL 176(SI),DX XORL 180(SI),CX XORL 184(SI),R8 XORL 188(SI),R9 MOVL DX,176(DI) MOVL CX,180(DI) MOVL R8,184(DI) MOVL R9,188(DI) MOVD X13,DX MOVD X9,CX MOVD X3,R8 MOVD X2,R9 XORL 240(SI),DX XORL 244(SI),CX XORL 248(SI),R8 XORL 252(SI),R9 MOVL DX,240(DI) MOVL CX,244(DI) MOVL R8,248(DI) MOVL R9,252(DI) MOVQ 408(SP),R9 SUBQ $256,R9 ADDQ $256,SI ADDQ $256,DI CMPQ R9,$256 JAE BYTESATLEAST256 CMPQ R9,$0 JBE DONE BYTESBETWEEN1AND255: CMPQ R9,$64 JAE NOCOPY MOVQ DI,DX LEAQ 416(SP),DI MOVQ R9,CX REP; MOVSB LEAQ 416(SP),DI LEAQ 416(SP),SI NOCOPY: MOVQ R9,408(SP) MOVOA 48(SP),X0 MOVOA 0(SP),X1 MOVOA 16(SP),X2 MOVOA 32(SP),X3 MOVOA X1,X4 MOVQ $20,CX MAINLOOP2: PADDL X0,X4 MOVOA X0,X5 MOVOA X4,X6 PSLLL $7,X4 PSRLL $25,X6 PXOR X4,X3 PXOR X6,X3 PADDL X3,X5 MOVOA X3,X4 MOVOA X5,X6 PSLLL $9,X5 PSRLL $23,X6 PXOR X5,X2 PSHUFL $0X93,X3,X3 PXOR X6,X2 PADDL X2,X4 MOVOA X2,X5 MOVOA X4,X6 PSLLL $13,X4 PSRLL $19,X6 PXOR X4,X1 PSHUFL $0X4E,X2,X2 PXOR X6,X1 PADDL X1,X5 MOVOA X3,X4 MOVOA X5,X6 PSLLL $18,X5 PSRLL $14,X6 PXOR X5,X0 PSHUFL $0X39,X1,X1 PXOR X6,X0 PADDL X0,X4 MOVOA X0,X5 MOVOA X4,X6 PSLLL $7,X4 PSRLL $25,X6 PXOR X4,X1 PXOR X6,X1 PADDL X1,X5 MOVOA X1,X4 MOVOA X5,X6 PSLLL $9,X5 PSRLL $23,X6 PXOR X5,X2 PSHUFL $0X93,X1,X1 PXOR X6,X2 PADDL X2,X4 MOVOA X2,X5 MOVOA X4,X6 PSLLL $13,X4 PSRLL $19,X6 PXOR X4,X3 PSHUFL $0X4E,X2,X2 PXOR X6,X3 PADDL X3,X5 MOVOA X1,X4 MOVOA X5,X6 PSLLL $18,X5 PSRLL $14,X6 PXOR X5,X0 PSHUFL $0X39,X3,X3 PXOR X6,X0 PADDL X0,X4 MOVOA X0,X5 MOVOA X4,X6 PSLLL $7,X4 PSRLL $25,X6 PXOR X4,X3 PXOR X6,X3 PADDL X3,X5 MOVOA X3,X4 MOVOA X5,X6 PSLLL $9,X5 PSRLL $23,X6 PXOR X5,X2 PSHUFL $0X93,X3,X3 PXOR X6,X2 PADDL X2,X4 MOVOA X2,X5 MOVOA X4,X6 PSLLL $13,X4 PSRLL $19,X6 PXOR X4,X1 PSHUFL $0X4E,X2,X2 PXOR X6,X1 PADDL X1,X5 MOVOA X3,X4 MOVOA X5,X6 PSLLL $18,X5 PSRLL $14,X6 PXOR X5,X0 PSHUFL $0X39,X1,X1 PXOR X6,X0 PADDL X0,X4 MOVOA X0,X5 MOVOA X4,X6 PSLLL $7,X4 PSRLL $25,X6 PXOR X4,X1 PXOR X6,X1 PADDL X1,X5 MOVOA X1,X4 MOVOA X5,X6 PSLLL $9,X5 PSRLL $23,X6 PXOR X5,X2 PSHUFL $0X93,X1,X1 PXOR X6,X2 PADDL X2,X4 MOVOA X2,X5 MOVOA X4,X6 PSLLL $13,X4 PSRLL $19,X6 PXOR X4,X3 PSHUFL $0X4E,X2,X2 PXOR X6,X3 SUBQ $4,CX PADDL X3,X5 MOVOA X1,X4 MOVOA X5,X6 PSLLL $18,X5 PXOR X7,X7 PSRLL $14,X6 PXOR X5,X0 PSHUFL $0X39,X3,X3 PXOR X6,X0 JA MAINLOOP2 PADDL 48(SP),X0 PADDL 0(SP),X1 PADDL 16(SP),X2 PADDL 32(SP),X3 MOVD X0,CX MOVD X1,R8 MOVD X2,R9 MOVD X3,AX PSHUFL $0X39,X0,X0 PSHUFL $0X39,X1,X1 PSHUFL $0X39,X2,X2 PSHUFL $0X39,X3,X3 XORL 0(SI),CX XORL 48(SI),R8 XORL 32(SI),R9 XORL 16(SI),AX MOVL CX,0(DI) MOVL R8,48(DI) MOVL R9,32(DI) MOVL AX,16(DI) MOVD X0,CX MOVD X1,R8 MOVD X2,R9 MOVD X3,AX PSHUFL $0X39,X0,X0 PSHUFL $0X39,X1,X1 PSHUFL $0X39,X2,X2 PSHUFL $0X39,X3,X3 XORL 20(SI),CX XORL 4(SI),R8 XORL 52(SI),R9 XORL 36(SI),AX MOVL CX,20(DI) MOVL R8,4(DI) MOVL R9,52(DI) MOVL AX,36(DI) MOVD X0,CX MOVD X1,R8 MOVD X2,R9 MOVD X3,AX PSHUFL $0X39,X0,X0 PSHUFL $0X39,X1,X1 PSHUFL $0X39,X2,X2 PSHUFL $0X39,X3,X3 XORL 40(SI),CX XORL 24(SI),R8 XORL 8(SI),R9 XORL 56(SI),AX MOVL CX,40(DI) MOVL R8,24(DI) MOVL R9,8(DI) MOVL AX,56(DI) MOVD X0,CX MOVD X1,R8 MOVD X2,R9 MOVD X3,AX XORL 60(SI),CX XORL 44(SI),R8 XORL 28(SI),R9 XORL 12(SI),AX MOVL CX,60(DI) MOVL R8,44(DI) MOVL R9,28(DI) MOVL AX,12(DI) MOVQ 408(SP),R9 MOVL 16(SP),CX MOVL 36 (SP),R8 ADDQ $1,CX SHLQ $32,R8 ADDQ R8,CX MOVQ CX,R8 SHRQ $32,R8 MOVL CX,16(SP) MOVL R8, 36 (SP) CMPQ R9,$64 JA BYTESATLEAST65 JAE BYTESATLEAST64 MOVQ DI,SI MOVQ DX,DI MOVQ R9,CX REP; MOVSB BYTESATLEAST64: DONE: MOVQ 352(SP),R11 MOVQ 360(SP),R12 MOVQ 368(SP),R13 MOVQ 376(SP),R14 MOVQ 384(SP),R15 MOVQ 392(SP),BX MOVQ 400(SP),BP MOVQ R11,SP RET BYTESATLEAST65: SUBQ $64,R9 ADDQ $64,DI ADDQ $64,SI JMP BYTESBETWEEN1AND255 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/salsa20/salsa/salsa_test.go������������������������0000644�0000153�0000161�00000002175�12321735647�026436� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 salsa import "testing" func TestCore208(t *testing.T) { in := [64]byte{ 0x7e, 0x87, 0x9a, 0x21, 0x4f, 0x3e, 0xc9, 0x86, 0x7c, 0xa9, 0x40, 0xe6, 0x41, 0x71, 0x8f, 0x26, 0xba, 0xee, 0x55, 0x5b, 0x8c, 0x61, 0xc1, 0xb5, 0x0d, 0xf8, 0x46, 0x11, 0x6d, 0xcd, 0x3b, 0x1d, 0xee, 0x24, 0xf3, 0x19, 0xdf, 0x9b, 0x3d, 0x85, 0x14, 0x12, 0x1e, 0x4b, 0x5a, 0xc5, 0xaa, 0x32, 0x76, 0x02, 0x1d, 0x29, 0x09, 0xc7, 0x48, 0x29, 0xed, 0xeb, 0xc6, 0x8d, 0xb8, 0xb8, 0xc2, 0x5e} out := [64]byte{ 0xa4, 0x1f, 0x85, 0x9c, 0x66, 0x08, 0xcc, 0x99, 0x3b, 0x81, 0xca, 0xcb, 0x02, 0x0c, 0xef, 0x05, 0x04, 0x4b, 0x21, 0x81, 0xa2, 0xfd, 0x33, 0x7d, 0xfd, 0x7b, 0x1c, 0x63, 0x96, 0x68, 0x2f, 0x29, 0xb4, 0x39, 0x31, 0x68, 0xe3, 0xc9, 0xe6, 0xbc, 0xfe, 0x6b, 0xc5, 0xb7, 0xa0, 0x6d, 0x96, 0xba, 0xe4, 0x24, 0xcc, 0x10, 0x2c, 0x91, 0x74, 0x5c, 0x24, 0xad, 0x67, 0x3d, 0xc7, 0x61, 0x8f, 0x81, } Core208(&in, &in) if in != out { t.Errorf("expected %x, got %x", out, in) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/salsa20/salsa/salsa20_ref.go�����������������������0000644�0000153�0000161�00000013170�12321735647�026372� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 !amd64 appengine gccgo package salsa const rounds = 20 // core applies the Salsa20 core function to 16-byte input in, 32-byte key k, // and 16-byte constant c, and puts the result into 64-byte array out. func core(out *[64]byte, in *[16]byte, k *[32]byte, c *[16]byte) { j0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24 j1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24 j2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24 j3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24 j4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24 j5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24 j6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 j7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24 j8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24 j9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24 j10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24 j11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24 j12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24 j13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24 j14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24 j15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24 x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8 x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15 for i := 0; i < rounds; i += 2 { u := x0 + x12 x4 ^= u<<7 | u>>(32-7) u = x4 + x0 x8 ^= u<<9 | u>>(32-9) u = x8 + x4 x12 ^= u<<13 | u>>(32-13) u = x12 + x8 x0 ^= u<<18 | u>>(32-18) u = x5 + x1 x9 ^= u<<7 | u>>(32-7) u = x9 + x5 x13 ^= u<<9 | u>>(32-9) u = x13 + x9 x1 ^= u<<13 | u>>(32-13) u = x1 + x13 x5 ^= u<<18 | u>>(32-18) u = x10 + x6 x14 ^= u<<7 | u>>(32-7) u = x14 + x10 x2 ^= u<<9 | u>>(32-9) u = x2 + x14 x6 ^= u<<13 | u>>(32-13) u = x6 + x2 x10 ^= u<<18 | u>>(32-18) u = x15 + x11 x3 ^= u<<7 | u>>(32-7) u = x3 + x15 x7 ^= u<<9 | u>>(32-9) u = x7 + x3 x11 ^= u<<13 | u>>(32-13) u = x11 + x7 x15 ^= u<<18 | u>>(32-18) u = x0 + x3 x1 ^= u<<7 | u>>(32-7) u = x1 + x0 x2 ^= u<<9 | u>>(32-9) u = x2 + x1 x3 ^= u<<13 | u>>(32-13) u = x3 + x2 x0 ^= u<<18 | u>>(32-18) u = x5 + x4 x6 ^= u<<7 | u>>(32-7) u = x6 + x5 x7 ^= u<<9 | u>>(32-9) u = x7 + x6 x4 ^= u<<13 | u>>(32-13) u = x4 + x7 x5 ^= u<<18 | u>>(32-18) u = x10 + x9 x11 ^= u<<7 | u>>(32-7) u = x11 + x10 x8 ^= u<<9 | u>>(32-9) u = x8 + x11 x9 ^= u<<13 | u>>(32-13) u = x9 + x8 x10 ^= u<<18 | u>>(32-18) u = x15 + x14 x12 ^= u<<7 | u>>(32-7) u = x12 + x15 x13 ^= u<<9 | u>>(32-9) u = x13 + x12 x14 ^= u<<13 | u>>(32-13) u = x14 + x13 x15 ^= u<<18 | u>>(32-18) } x0 += j0 x1 += j1 x2 += j2 x3 += j3 x4 += j4 x5 += j5 x6 += j6 x7 += j7 x8 += j8 x9 += j9 x10 += j10 x11 += j11 x12 += j12 x13 += j13 x14 += j14 x15 += j15 out[0] = byte(x0) out[1] = byte(x0 >> 8) out[2] = byte(x0 >> 16) out[3] = byte(x0 >> 24) out[4] = byte(x1) out[5] = byte(x1 >> 8) out[6] = byte(x1 >> 16) out[7] = byte(x1 >> 24) out[8] = byte(x2) out[9] = byte(x2 >> 8) out[10] = byte(x2 >> 16) out[11] = byte(x2 >> 24) out[12] = byte(x3) out[13] = byte(x3 >> 8) out[14] = byte(x3 >> 16) out[15] = byte(x3 >> 24) out[16] = byte(x4) out[17] = byte(x4 >> 8) out[18] = byte(x4 >> 16) out[19] = byte(x4 >> 24) out[20] = byte(x5) out[21] = byte(x5 >> 8) out[22] = byte(x5 >> 16) out[23] = byte(x5 >> 24) out[24] = byte(x6) out[25] = byte(x6 >> 8) out[26] = byte(x6 >> 16) out[27] = byte(x6 >> 24) out[28] = byte(x7) out[29] = byte(x7 >> 8) out[30] = byte(x7 >> 16) out[31] = byte(x7 >> 24) out[32] = byte(x8) out[33] = byte(x8 >> 8) out[34] = byte(x8 >> 16) out[35] = byte(x8 >> 24) out[36] = byte(x9) out[37] = byte(x9 >> 8) out[38] = byte(x9 >> 16) out[39] = byte(x9 >> 24) out[40] = byte(x10) out[41] = byte(x10 >> 8) out[42] = byte(x10 >> 16) out[43] = byte(x10 >> 24) out[44] = byte(x11) out[45] = byte(x11 >> 8) out[46] = byte(x11 >> 16) out[47] = byte(x11 >> 24) out[48] = byte(x12) out[49] = byte(x12 >> 8) out[50] = byte(x12 >> 16) out[51] = byte(x12 >> 24) out[52] = byte(x13) out[53] = byte(x13 >> 8) out[54] = byte(x13 >> 16) out[55] = byte(x13 >> 24) out[56] = byte(x14) out[57] = byte(x14 >> 8) out[58] = byte(x14 >> 16) out[59] = byte(x14 >> 24) out[60] = byte(x15) out[61] = byte(x15 >> 8) out[62] = byte(x15 >> 16) out[63] = byte(x15 >> 24) } // XORKeyStream crypts bytes from in to out using the given key and counters. // In and out may be the same slice but otherwise should not overlap. Counter // contains the raw salsa20 counter bytes (both nonce and block counter). func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) { var block [64]byte var counterCopy [16]byte copy(counterCopy[:], counter[:]) for len(in) >= 64 { core(&block, &counterCopy, key, &Sigma) for i, x := range block { out[i] = in[i] ^ x } u := uint32(1) for i := 8; i < 16; i++ { u += uint32(counterCopy[i]) counterCopy[i] = byte(u) u >>= 8 } in = in[64:] out = out[64:] } if len(in) > 0 { core(&block, &counterCopy, key, &Sigma) for i, v := range in { out[i] = v ^ block[i] } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/salsa20/salsa/hsalsa20.go��������������������������0000644�0000153�0000161�00000007724�12321735647�025716� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 salsa provides low-level access to functions in the Salsa family. package salsa // Sigma is the Salsa20 constant for 256-bit keys. var Sigma = [16]byte{'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'} // HSalsa20 applies the HSalsa20 core function to a 16-byte input in, 32-byte // key k, and 16-byte constant c, and puts the result into the 32-byte array // out. func HSalsa20(out *[32]byte, in *[16]byte, k *[32]byte, c *[16]byte) { x0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24 x1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24 x2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24 x3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24 x4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24 x5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24 x6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 x7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24 x8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24 x9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24 x10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24 x11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24 x12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24 x13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24 x14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24 x15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24 for i := 0; i < 20; i += 2 { u := x0 + x12 x4 ^= u<<7 | u>>(32-7) u = x4 + x0 x8 ^= u<<9 | u>>(32-9) u = x8 + x4 x12 ^= u<<13 | u>>(32-13) u = x12 + x8 x0 ^= u<<18 | u>>(32-18) u = x5 + x1 x9 ^= u<<7 | u>>(32-7) u = x9 + x5 x13 ^= u<<9 | u>>(32-9) u = x13 + x9 x1 ^= u<<13 | u>>(32-13) u = x1 + x13 x5 ^= u<<18 | u>>(32-18) u = x10 + x6 x14 ^= u<<7 | u>>(32-7) u = x14 + x10 x2 ^= u<<9 | u>>(32-9) u = x2 + x14 x6 ^= u<<13 | u>>(32-13) u = x6 + x2 x10 ^= u<<18 | u>>(32-18) u = x15 + x11 x3 ^= u<<7 | u>>(32-7) u = x3 + x15 x7 ^= u<<9 | u>>(32-9) u = x7 + x3 x11 ^= u<<13 | u>>(32-13) u = x11 + x7 x15 ^= u<<18 | u>>(32-18) u = x0 + x3 x1 ^= u<<7 | u>>(32-7) u = x1 + x0 x2 ^= u<<9 | u>>(32-9) u = x2 + x1 x3 ^= u<<13 | u>>(32-13) u = x3 + x2 x0 ^= u<<18 | u>>(32-18) u = x5 + x4 x6 ^= u<<7 | u>>(32-7) u = x6 + x5 x7 ^= u<<9 | u>>(32-9) u = x7 + x6 x4 ^= u<<13 | u>>(32-13) u = x4 + x7 x5 ^= u<<18 | u>>(32-18) u = x10 + x9 x11 ^= u<<7 | u>>(32-7) u = x11 + x10 x8 ^= u<<9 | u>>(32-9) u = x8 + x11 x9 ^= u<<13 | u>>(32-13) u = x9 + x8 x10 ^= u<<18 | u>>(32-18) u = x15 + x14 x12 ^= u<<7 | u>>(32-7) u = x12 + x15 x13 ^= u<<9 | u>>(32-9) u = x13 + x12 x14 ^= u<<13 | u>>(32-13) u = x14 + x13 x15 ^= u<<18 | u>>(32-18) } out[0] = byte(x0) out[1] = byte(x0 >> 8) out[2] = byte(x0 >> 16) out[3] = byte(x0 >> 24) out[4] = byte(x5) out[5] = byte(x5 >> 8) out[6] = byte(x5 >> 16) out[7] = byte(x5 >> 24) out[8] = byte(x10) out[9] = byte(x10 >> 8) out[10] = byte(x10 >> 16) out[11] = byte(x10 >> 24) out[12] = byte(x15) out[13] = byte(x15 >> 8) out[14] = byte(x15 >> 16) out[15] = byte(x15 >> 24) out[16] = byte(x6) out[17] = byte(x6 >> 8) out[18] = byte(x6 >> 16) out[19] = byte(x6 >> 24) out[20] = byte(x7) out[21] = byte(x7 >> 8) out[22] = byte(x7 >> 16) out[23] = byte(x7 >> 24) out[24] = byte(x8) out[25] = byte(x8 >> 8) out[26] = byte(x8 >> 16) out[27] = byte(x8 >> 24) out[28] = byte(x9) out[29] = byte(x9 >> 8) out[30] = byte(x9 >> 16) out[31] = byte(x9 >> 24) } ��������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/md4/�����������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022054� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/md4/md4.go�����������������������������������������0000644�0000153�0000161�00000004050�12321735647�023066� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // Package md4 implements the MD4 hash algorithm as defined in RFC 1320. package md4 import ( "crypto" "hash" ) func init() { crypto.RegisterHash(crypto.MD4, New) } // The size of an MD4 checksum in bytes. const Size = 16 // The blocksize of MD4 in bytes. const BlockSize = 64 const ( _Chunk = 64 _Init0 = 0x67452301 _Init1 = 0xEFCDAB89 _Init2 = 0x98BADCFE _Init3 = 0x10325476 ) // digest represents the partial evaluation of a checksum. type digest struct { s [4]uint32 x [_Chunk]byte nx int len uint64 } func (d *digest) Reset() { d.s[0] = _Init0 d.s[1] = _Init1 d.s[2] = _Init2 d.s[3] = _Init3 d.nx = 0 d.len = 0 } // New returns a new hash.Hash computing the MD4 checksum. func New() hash.Hash { d := new(digest) d.Reset() return d } func (d *digest) Size() int { return Size } func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { nn = len(p) d.len += uint64(nn) if d.nx > 0 { n := len(p) if n > _Chunk-d.nx { n = _Chunk - d.nx } for i := 0; i < n; i++ { d.x[d.nx+i] = p[i] } d.nx += n if d.nx == _Chunk { _Block(d, d.x[0:]) d.nx = 0 } p = p[n:] } n := _Block(d, p) p = p[n:] if len(p) > 0 { d.nx = copy(d.x[:], p) } return } func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0, so that caller can keep writing and summing. d := new(digest) *d = *d0 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. len := d.len var tmp [64]byte tmp[0] = 0x80 if len%64 < 56 { d.Write(tmp[0 : 56-len%64]) } else { d.Write(tmp[0 : 64+56-len%64]) } // Length in bits. len <<= 3 for i := uint(0); i < 8; i++ { tmp[i] = byte(len >> (8 * i)) } d.Write(tmp[0:8]) if d.nx != 0 { panic("d.nx != 0") } for _, s := range d.s { in = append(in, byte(s>>0)) in = append(in, byte(s>>8)) in = append(in, byte(s>>16)) in = append(in, byte(s>>24)) } return in } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/md4/md4block.go������������������������������������0000644�0000153�0000161�00000003764�12321735647�024114� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // MD4 block step. // In its own file so that a faster assembly or C version // can be substituted easily. package md4 var shift1 = []uint{3, 7, 11, 19} var shift2 = []uint{3, 5, 9, 13} var shift3 = []uint{3, 9, 11, 15} var xIndex2 = []uint{0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15} var xIndex3 = []uint{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15} func _Block(dig *digest, p []byte) int { a := dig.s[0] b := dig.s[1] c := dig.s[2] d := dig.s[3] n := 0 var X [16]uint32 for len(p) >= _Chunk { aa, bb, cc, dd := a, b, c, d j := 0 for i := 0; i < 16; i++ { X[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 j += 4 } // If this needs to be made faster in the future, // the usual trick is to unroll each of these // loops by a factor of 4; that lets you replace // the shift[] lookups with constants and, // with suitable variable renaming in each // unrolled body, delete the a, b, c, d = d, a, b, c // (or you can let the optimizer do the renaming). // // The index variables are uint so that % by a power // of two can be optimized easily by a compiler. // Round 1. for i := uint(0); i < 16; i++ { x := i s := shift1[i%4] f := ((c ^ d) & b) ^ d a += f + X[x] a = a<<s | a>>(32-s) a, b, c, d = d, a, b, c } // Round 2. for i := uint(0); i < 16; i++ { x := xIndex2[i] s := shift2[i%4] g := (b & c) | (b & d) | (c & d) a += g + X[x] + 0x5a827999 a = a<<s | a>>(32-s) a, b, c, d = d, a, b, c } // Round 3. for i := uint(0); i < 16; i++ { x := xIndex3[i] s := shift3[i%4] h := b ^ c ^ d a += h + X[x] + 0x6ed9eba1 a = a<<s | a>>(32-s) a, b, c, d = d, a, b, c } a += aa b += bb c += cc d += dd p = p[_Chunk:] n += _Chunk } dig.s[0] = a dig.s[1] = b dig.s[2] = c dig.s[3] = d return n } ������������juju-core_1.18.1/src/code.google.com/p/go.crypto/md4/md4_test.go������������������������������������0000644�0000153�0000161�00000006353�12321735647�024135� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package md4 import ( "fmt" "io" "testing" ) type md4Test struct { out string in string } var golden = []md4Test{ {"31d6cfe0d16ae931b73c59d7e0c089c0", ""}, {"bde52cb31de33e46245e05fbdbd6fb24", "a"}, {"ec388dd78999dfc7cf4632465693b6bf", "ab"}, {"a448017aaf21d8525fc10ae87aa6729d", "abc"}, {"41decd8f579255c5200f86a4bb3ba740", "abcd"}, {"9803f4a34e8eb14f96adba49064a0c41", "abcde"}, {"804e7f1c2586e50b49ac65db5b645131", "abcdef"}, {"752f4adfe53d1da0241b5bc216d098fc", "abcdefg"}, {"ad9daf8d49d81988590a6f0e745d15dd", "abcdefgh"}, {"1e4e28b05464316b56402b3815ed2dfd", "abcdefghi"}, {"dc959c6f5d6f9e04e4380777cc964b3d", "abcdefghij"}, {"1b5701e265778898ef7de5623bbe7cc0", "Discard medicine more than two years old."}, {"d7f087e090fe7ad4a01cb59dacc9a572", "He who has a shady past knows that nice guys finish last."}, {"a6f8fd6df617c72837592fc3570595c9", "I wouldn't marry him with a ten foot pole."}, {"c92a84a9526da8abc240c05d6b1a1ce0", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"}, {"f6013160c4dcb00847069fee3bb09803", "The days of the digital watch are numbered. -Tom Stoppard"}, {"2c3bb64f50b9107ed57640fe94bec09f", "Nepal premier won't resign."}, {"45b7d8a32c7806f2f7f897332774d6e4", "For every action there is an equal and opposite government program."}, {"b5b4f9026b175c62d7654bdc3a1cd438", "His money is twice tainted: 'taint yours and 'taint mine."}, {"caf44e80f2c20ce19b5ba1cab766e7bd", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"}, {"191fae6707f496aa54a6bce9f2ecf74d", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"}, {"9ddc753e7a4ccee6081cd1b45b23a834", "size: a.out: bad magic"}, {"8d050f55b1cadb9323474564be08a521", "The major problem is with sendmail. -Mark Horton"}, {"ad6e2587f74c3e3cc19146f6127fa2e3", "Give me a rock, paper and scissors and I will move the world. CCFestoon"}, {"1d616d60a5fabe85589c3f1566ca7fca", "If the enemy is within range, then so are you."}, {"aec3326a4f496a2ced65a1963f84577f", "It's well we cannot hear the screams/That we create in others' dreams."}, {"77b4fd762d6b9245e61c50bf6ebf118b", "You remind me of a TV show, but that's all right: I watch it anyway."}, {"e8f48c726bae5e516f6ddb1a4fe62438", "C is as portable as Stonehedge!!"}, {"a3a84366e7219e887423b01f9be7166e", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"}, {"a6b7aa35157e984ef5d9b7f32e5fbb52", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"}, {"75661f0545955f8f9abeeb17845f3fd6", "How can you write a big system without C++? -Paul Glick"}, } func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] c := New() for j := 0; j < 3; j++ { if j < 2 { io.WriteString(c, g.in) } else { io.WriteString(c, g.in[0:len(g.in)/2]) c.Sum(nil) io.WriteString(c, g.in[len(g.in)/2:]) } s := fmt.Sprintf("%x", c.Sum(nil)) if s != g.out { t.Fatalf("md4[%d](%s) = %s want %s", j, g.in, s, g.out) } c.Reset() } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/xtea/����������������������������������������������0000755�0000153�0000161�00000000000�12321735647�022331� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/xtea/cipher.go�������������������������������������0000644�0000153�0000161�00000004537�12321735647�024143� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // Package xtea implements XTEA encryption, as defined in Needham and Wheeler's // 1997 technical report, "Tea extensions." package xtea // For details, see http://www.cix.co.uk/~klockstone/xtea.pdf import "strconv" // The XTEA block size in bytes. const BlockSize = 8 // A Cipher is an instance of an XTEA cipher using a particular key. // table contains a series of precalculated values that are used each round. type Cipher struct { table [64]uint32 } type KeySizeError int func (k KeySizeError) Error() string { return "crypto/xtea: invalid key size " + strconv.Itoa(int(k)) } // NewCipher creates and returns a new Cipher. // The key argument should be the XTEA key. // XTEA only supports 128 bit (16 byte) keys. func NewCipher(key []byte) (*Cipher, error) { k := len(key) switch k { default: return nil, KeySizeError(k) case 16: break } c := new(Cipher) initCipher(c, key) return c, nil } // BlockSize returns the XTEA block size, 8 bytes. // It is necessary to satisfy the Block interface in the // package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } // Encrypt encrypts the 8 byte buffer src using the key and stores the result in dst. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; // instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c, dst, src) } // Decrypt decrypts the 8 byte buffer src using the key k and stores the result in dst. func (c *Cipher) Decrypt(dst, src []byte) { decryptBlock(c, dst, src) } // initCipher initializes the cipher context by creating a look up table // of precalculated values that are based on the key. func initCipher(c *Cipher, key []byte) { // Load the key into four uint32s var k [4]uint32 for i := 0; i < len(k); i++ { j := i << 2 // Multiply by 4 k[i] = uint32(key[j+0])<<24 | uint32(key[j+1])<<16 | uint32(key[j+2])<<8 | uint32(key[j+3]) } // Precalculate the table const delta = 0x9E3779B9 var sum uint32 = 0 // Two rounds of XTEA applied per loop for i := 0; i < numRounds; { c.table[i] = sum + k[sum&3] i++ sum += delta c.table[i] = sum + k[(sum>>11)&3] i++ } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/xtea/block.go��������������������������������������0000644�0000153�0000161�00000003417�12321735647�023757� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. /* Implementation adapted from Needham and Wheeler's paper: http://www.cix.co.uk/~klockstone/xtea.pdf A precalculated look up table is used during encryption/decryption for values that are based purely on the key. */ package xtea // XTEA is based on 64 rounds. const numRounds = 64 // blockToUint32 reads an 8 byte slice into two uint32s. // The block is treated as big endian. func blockToUint32(src []byte) (uint32, uint32) { r0 := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) r1 := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) return r0, r1 } // uint32ToBlock writes two uint32s into an 8 byte data block. // Values are written as big endian. func uint32ToBlock(v0, v1 uint32, dst []byte) { dst[0] = byte(v0 >> 24) dst[1] = byte(v0 >> 16) dst[2] = byte(v0 >> 8) dst[3] = byte(v0) dst[4] = byte(v1 >> 24) dst[5] = byte(v1 >> 16) dst[6] = byte(v1 >> 8) dst[7] = byte(v1 >> 0) } // encryptBlock encrypts a single 8 byte block using XTEA. func encryptBlock(c *Cipher, dst, src []byte) { v0, v1 := blockToUint32(src) // Two rounds of XTEA applied per loop for i := 0; i < numRounds; { v0 += ((v1<<4 ^ v1>>5) + v1) ^ c.table[i] i++ v1 += ((v0<<4 ^ v0>>5) + v0) ^ c.table[i] i++ } uint32ToBlock(v0, v1, dst) } // decryptBlock decrypt a single 8 byte block using XTEA. func decryptBlock(c *Cipher, dst, src []byte) { v0, v1 := blockToUint32(src) // Two rounds of XTEA applied per loop for i := numRounds; i > 0; { i-- v1 -= ((v0<<4 ^ v0>>5) + v0) ^ c.table[i] i-- v0 -= ((v1<<4 ^ v1>>5) + v1) ^ c.table[i] } uint32ToBlock(v0, v1, dst) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/code.google.com/p/go.crypto/xtea/xtea_test.go����������������������������������0000644�0000153�0000161�00000016371�12321735647�024670� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package xtea import ( "testing" ) // A sample test key for when we just want to initialize a cipher var testKey = []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF} // Test that the block size for XTEA is correct func TestBlocksize(t *testing.T) { if BlockSize != 8 { t.Errorf("BlockSize constant - expected 8, got %d", BlockSize) return } c, err := NewCipher(testKey) if err != nil { t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) return } result := c.BlockSize() if result != 8 { t.Errorf("BlockSize function - expected 8, got %d", result) return } } // A series of test values to confirm that the Cipher.table array was initialized correctly var testTable = []uint32{ 0x00112233, 0x6B1568B8, 0xE28CE030, 0xC5089E2D, 0xC5089E2D, 0x1EFBD3A2, 0xA7845C2A, 0x78EF0917, 0x78EF0917, 0x172682D0, 0x5B6AC714, 0x822AC955, 0x3DE68511, 0xDC1DFECA, 0x2062430E, 0x3611343F, 0xF1CCEFFB, 0x900469B4, 0xD448ADF8, 0x2E3BE36D, 0xB6C46BF5, 0x994029F2, 0x994029F2, 0xF3335F67, 0x6AAAD6DF, 0x4D2694DC, 0x4D2694DC, 0xEB5E0E95, 0x2FA252D9, 0x4551440A, 0x121E10D6, 0xB0558A8F, 0xE388BDC3, 0x0A48C004, 0xC6047BC0, 0x643BF579, 0xA88039BD, 0x02736F32, 0x8AFBF7BA, 0x5C66A4A7, 0x5C66A4A7, 0xC76AEB2C, 0x3EE262A4, 0x215E20A1, 0x215E20A1, 0x7B515616, 0x03D9DE9E, 0x1988CFCF, 0xD5448B8B, 0x737C0544, 0xB7C04988, 0xDE804BC9, 0x9A3C0785, 0x3873813E, 0x7CB7C582, 0xD6AAFAF7, 0x4E22726F, 0x309E306C, 0x309E306C, 0x8A9165E1, 0x1319EE69, 0xF595AC66, 0xF595AC66, 0x4F88E1DB, } // Test that the cipher context is initialized correctly func TestCipherInit(t *testing.T) { c, err := NewCipher(testKey) if err != nil { t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) return } for i := 0; i < len(c.table); i++ { if c.table[i] != testTable[i] { t.Errorf("NewCipher() failed to initialize Cipher.table[%d] correctly. Expected %08X, got %08X", i, testTable[i], c.table[i]) break } } } // Test that invalid key sizes return an error func TestInvalidKeySize(t *testing.T) { // Test a long key key := []byte{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, } _, err := NewCipher(key) if err == nil { t.Errorf("Invalid key size %d didn't result in an error.", len(key)) } // Test a short key key = []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77} _, err = NewCipher(key) if err == nil { t.Errorf("Invalid key size %d didn't result in an error.", len(key)) } } // Test that we can correctly decode some bytes we have encoded func TestEncodeDecode(t *testing.T) { original := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF} input := original output := make([]byte, BlockSize) c, err := NewCipher(testKey) if err != nil { t.Errorf("NewCipher(%d bytes) = %s", len(testKey), err) return } // Encrypt the input block c.Encrypt(output, input) // Check that the output does not match the input differs := false for i := 0; i < len(input); i++ { if output[i] != input[i] { differs = true break } } if differs == false { t.Error("Cipher.Encrypt: Failed to encrypt the input block.") return } // Decrypt the block we just encrypted input = output output = make([]byte, BlockSize) c.Decrypt(output, input) // Check that the output from decrypt matches our initial input for i := 0; i < len(input); i++ { if output[i] != original[i] { t.Errorf("Decrypted byte %d differed. Expected %02X, got %02X\n", i, original[i], output[i]) return } } } // Test Vectors type CryptTest struct { key []byte plainText []byte cipherText []byte } var CryptTests = []CryptTest{ // These were sourced from http://www.freemedialibrary.com/index.php/XTEA_test_vectors { []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, []byte{0x49, 0x7d, 0xf3, 0xd0, 0x72, 0x61, 0x2c, 0xb5}, }, { []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, []byte{0xe7, 0x8f, 0x2d, 0x13, 0x74, 0x43, 0x41, 0xd8}, }, { []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, []byte{0x5a, 0x5b, 0x6e, 0x27, 0x89, 0x48, 0xd7, 0x7f}, []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, }, { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}, []byte{0xa0, 0x39, 0x05, 0x89, 0xf8, 0xb8, 0xef, 0xa5}, }, { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, []byte{0xed, 0x23, 0x37, 0x5a, 0x82, 0x1a, 0x8c, 0x2d}, }, { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x70, 0xe1, 0x22, 0x5d, 0x6e, 0x4e, 0x76, 0x55}, []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41}, }, // These vectors are from http://wiki.secondlife.com/wiki/XTEA_Strong_Encryption_Implementation#Bouncy_Castle_C.23_API { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0xDE, 0xE9, 0xD4, 0xD8, 0xF7, 0x13, 0x1E, 0xD9}, }, { []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, []byte{0x06, 0x5C, 0x1B, 0x89, 0x75, 0xC6, 0xA8, 0x16}, }, { []byte{0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x1F, 0xF9, 0xA0, 0x26, 0x1A, 0xC6, 0x42, 0x64}, }, { []byte{0x01, 0x23, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, 0x23, 0x45, 0x67, 0x89, 0x34, 0x56, 0x78, 0x9A}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, []byte{0x8C, 0x67, 0x15, 0x5B, 0x2E, 0xF9, 0x1E, 0xAD}, }, } // Test encryption func TestCipherEncrypt(t *testing.T) { for i, tt := range CryptTests { c, err := NewCipher(tt.key) if err != nil { t.Errorf("NewCipher(%d bytes), vector %d = %s", len(tt.key), i, err) continue } out := make([]byte, len(tt.plainText)) c.Encrypt(out, tt.plainText) for j := 0; j < len(out); j++ { if out[j] != tt.cipherText[j] { t.Errorf("Cipher.Encrypt %d: out[%d] = %02X, expected %02X", i, j, out[j], tt.cipherText[j]) break } } } } // Test decryption func TestCipherDecrypt(t *testing.T) { for i, tt := range CryptTests { c, err := NewCipher(tt.key) if err != nil { t.Errorf("NewCipher(%d bytes), vector %d = %s", len(tt.key), i, err) continue } out := make([]byte, len(tt.cipherText)) c.Decrypt(out, tt.cipherText) for j := 0; j < len(out); j++ { if out[j] != tt.plainText[j] { t.Errorf("Cipher.Decrypt %d: out[%d] = %02X, expected %02X", i, j, out[j], tt.plainText[j]) break } } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/��������������������������������������������������������������������0000755�0000153�0000161�00000000000�12321735762�016300� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/���������������������������������������������������������������0000755�0000153�0000161�00000000000�12321735761�017254� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/ratelimit/�����������������������������������������������������0000755�0000153�0000161�00000000000�12321736016�021240� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/ratelimit/LICENSE����������������������������������������������0000644�0000153�0000161�00000021053�12321735762�022255� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This software is licensed under the LGPLv3, included below. As a special exception to the GNU Lesser General Public License version 3 ("LGPL3"), the copyright holders of this Library give you permission to convey to a third party a Combined Work that links statically or dynamically to this Library without providing any Minimal Corresponding Source or Minimal Application Code as set out in 4d or providing the installation information set out in section 4e, provided that you comply with the other provisions of LGPL3 and provided that you meet, for the Application the terms and conditions of the license(s) which apply to the Application. Except as stated in this special exception, the provisions of LGPL3 will continue to comply in full to this Library. If you modify this Library, you may apply this exception to your version of this Library, but you are not obliged to do so. If you do not wish to do so, delete this exception statement from your version. This exception does not (and cannot) modify any license terms which apply to the Application, with which you must still comply. GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/ratelimit/ratelimit_test.go������������������������������������0000644�0000153�0000161�00000006070�12321736016�024623� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package ratelimit import ( gc "launchpad.net/gocheck" "testing" "time" ) func TestPackage(t *testing.T) { gc.TestingT(t) } type rateLimitSuite struct{} var _ = gc.Suite(rateLimitSuite{}) type req struct { time time.Duration count int64 expectWait time.Duration } var rateLimitTests = []struct { about string fillInterval time.Duration capacity int64 reqs []req }{{ about: "serial requests", fillInterval: 250 * time.Millisecond, capacity: 10, reqs: []req{{ time: 0, count: 0, expectWait: 0, }, { time: 0, count: 10, expectWait: 0, }, { time: 0, count: 1, expectWait: 250 * time.Millisecond, }, { time: 250 * time.Millisecond, count: 1, expectWait: 250 * time.Millisecond, }}, }, { about: "concurrent requests", fillInterval: 250 * time.Millisecond, capacity: 10, reqs: []req{{ time: 0, count: 10, expectWait: 0, }, { time: 0, count: 2, expectWait: 500 * time.Millisecond, }, { time: 0, count: 2, expectWait: 1000 * time.Millisecond, }, { time: 0, count: 1, expectWait: 1250 * time.Millisecond, }}, }, { about: "more than capacity", fillInterval: 1 * time.Millisecond, capacity: 10, reqs: []req{{ time: 0, count: 10, expectWait: 0, }, { time: 20 * time.Millisecond, count: 15, expectWait: 5 * time.Millisecond, }}, }, { about: "sub-quantum time", fillInterval: 10 * time.Millisecond, capacity: 10, reqs: []req{{ time: 0, count: 10, expectWait: 0, }, { time: 7 * time.Millisecond, count: 1, expectWait: 3 * time.Millisecond, }, { time: 8 * time.Millisecond, count: 1, expectWait: 12 * time.Millisecond, }}, }, { about: "within capacity", fillInterval: 10 * time.Millisecond, capacity: 5, reqs: []req{{ time: 0, count: 5, expectWait: 0, }, { time: 60 * time.Millisecond, count: 5, expectWait: 0, }, { time: 60 * time.Millisecond, count: 1, expectWait: 10 * time.Millisecond, }, { time: 80 * time.Millisecond, count: 2, expectWait: 10 * time.Millisecond, }}, }} func (rateLimitSuite) TestRateLimit(c *gc.C) { for i, test := range rateLimitTests { tb := New(test.fillInterval, test.capacity) for j, req := range test.reqs { d := tb.take(tb.startTime.Add(req.time), req.count) if d != req.expectWait { c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait) } } } } func (rateLimitSuite) TestPanics(c *gc.C) { c.Assert(func() { New(0, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0") c.Assert(func() { New(-2, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0") c.Assert(func() { New(1, 0) }, gc.PanicMatches, "token bucket capacity is not > 0") c.Assert(func() { New(1, -2) }, gc.PanicMatches, "token bucket capacity is not > 0") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/ratelimit/ratelimit.go�����������������������������������������0000644�0000153�0000161�00000004765�12321736016�023575� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. // The ratelimit package provides an efficient token bucket implementation. // See http://en.wikipedia.org/wiki/Token_bucket. package ratelimit import ( "sync" "time" ) // TODO what about aborting requests? // TokenBucket represents a token bucket // that fills at a predetermined rate. // Methods on TokenBucket may be called // concurrently. type TokenBucket struct { mu sync.Mutex startTime time.Time capacity int64 fillInterval time.Duration availTick int64 avail int64 } // New returns a new token bucket that fills at the // rate of one token every fillInterval, up to the given // maximum capacity. Both arguments must be // positive. The bucket is initially full. func New(fillInterval time.Duration, capacity int64) *TokenBucket { if fillInterval <= 0 { panic("token bucket fill interval is not > 0") } if capacity <= 0 { panic("token bucket capacity is not > 0") } return &TokenBucket{ startTime: time.Now(), capacity: capacity, avail: capacity, fillInterval: fillInterval, } } // Wait takes count tokens from the bucket, // waiting until they are available. func (tb *TokenBucket) Wait(count int64) { if d := tb.Take(count); d > 0 { time.Sleep(d) } } // Take takes count tokens from the bucket without // blocking. It returns the time that the caller should // wait until the tokens are actually available. // // Note that if the request is irrevocable - there // is no way to return tokens to the bucket once // this method commits us to taking them. func (tb *TokenBucket) Take(count int64) time.Duration { return tb.take(time.Now(), count) } // take is the internal version of Take - it takes // the current time as an argument to enable easy testing. func (tb *TokenBucket) take(now time.Time, count int64) time.Duration { if count <= 0 { return 0 } tb.mu.Lock() defer tb.mu.Unlock() currentTick := int64(now.Sub(tb.startTime) / tb.fillInterval) tb.adjust(currentTick) tb.avail -= count if tb.avail >= 0 { return 0 } endTick := currentTick - tb.avail endTime := tb.startTime.Add(time.Duration(endTick) * tb.fillInterval) return endTime.Sub(now) } // adjust adjusts the current bucket capacity based // on the current tick. func (tb *TokenBucket) adjust(currentTick int64) { if tb.avail >= tb.capacity { return } tb.avail += currentTick - tb.availTick if tb.avail > tb.capacity { tb.avail = tb.capacity } tb.availTick = currentTick } �����������juju-core_1.18.1/src/github.com/juju/ratelimit/README.md��������������������������������������������0000644�0000153�0000161�00000002204�12321736016�022515� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# ratelimit -- import "github.com/juju/ratelimit" The ratelimit package provides an efficient token bucket implementation. See http://en.wikipedia.org/wiki/Token_bucket. ## Usage #### type TokenBucket ```go type TokenBucket struct { } ``` TokenBucket represents a token bucket that fills at a predetermined rate. Methods on TokenBucket may be called concurrently. #### func New ```go func New(fillInterval time.Duration, capacity int64) *TokenBucket ``` New returns a new token bucket that fills at the rate of one token every fillInterval, up to the given maximum capacity. Both arguments must be positive. #### func (*TokenBucket) Take ```go func (tb *TokenBucket) Take(count int64) time.Duration ``` Take takes count tokens from the bucket without blocking. It returns the time that the caller should wait until the tokens are actually available. Note that if the request is irrevocable - there is no way to return tokens to the bucket once this method commits us to taking them. #### func (*TokenBucket) Wait ```go func (tb *TokenBucket) Wait(count int64) ``` Wait takes count tokens from the bucket, waiting until they are available. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/�������������������������������������������������������0000755�0000153�0000161�00000000000�12321736014�020721� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/LICENSE������������������������������������������������0000644�0000153�0000161�00000021053�12321735703�021733� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This software is licensed under the LGPLv3, included below. As a special exception to the GNU Lesser General Public License version 3 ("LGPL3"), the copyright holders of this Library give you permission to convey to a third party a Combined Work that links statically or dynamically to this Library without providing any Minimal Corresponding Source or Minimal Application Code as set out in 4d or providing the installation information set out in section 4e, provided that you comply with the other provisions of LGPL3 and provided that you meet, for the Application the terms and conditions of the license(s) which apply to the Application. Except as stated in this special exception, the provisions of LGPL3 will continue to comply in full to this Library. If you modify this Library, you may apply this exception to your version of this Library, but you are not obliged to do so. If you do not wish to do so, delete this exception statement from your version. This exception does not (and cannot) modify any license terms which apply to the Application, with which you must still comply. GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/cleanup_test.go����������������������������������������0000644�0000153�0000161�00000005327�12321735703�023751� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "os" gc "launchpad.net/gocheck" "github.com/juju/testing" ) type cleanupSuite struct { testing.CleanupSuite } var _ = gc.Suite(&cleanupSuite{}) func (s *cleanupSuite) TestTearDownSuiteEmpty(c *gc.C) { // The suite stack is empty initially, check we can tear that down. s.TearDownSuite(c) s.SetUpSuite(c) } func (s *cleanupSuite) TestTearDownTestEmpty(c *gc.C) { // The test stack is empty initially, check we can tear that down. s.TearDownTest(c) s.SetUpTest(c) } func (s *cleanupSuite) TestAddSuiteCleanup(c *gc.C) { order := []string{} s.AddSuiteCleanup(func(*gc.C) { order = append(order, "first") }) s.AddSuiteCleanup(func(*gc.C) { order = append(order, "second") }) s.TearDownSuite(c) c.Assert(order, gc.DeepEquals, []string{"second", "first"}) // SetUpSuite resets the cleanup stack, this stops the cleanup functions // being called again. s.SetUpSuite(c) } func (s *cleanupSuite) TestAddCleanup(c *gc.C) { order := []string{} s.AddCleanup(func(*gc.C) { order = append(order, "first") }) s.AddCleanup(func(*gc.C) { order = append(order, "second") }) s.TearDownTest(c) c.Assert(order, gc.DeepEquals, []string{"second", "first"}) // SetUpTest resets the cleanup stack, this stops the cleanup functions // being called again. s.SetUpTest(c) } func (s *cleanupSuite) TestPatchEnvironment(c *gc.C) { const envName = "TESTING_PATCH_ENVIRONMENT" // remember the old value, and set it to something we can check oldValue := os.Getenv(envName) os.Setenv(envName, "initial") s.PatchEnvironment(envName, "new value") // Using check to make sure the environment gets set back properly in the test. c.Check(os.Getenv(envName), gc.Equals, "new value") s.TearDownTest(c) c.Check(os.Getenv(envName), gc.Equals, "initial") // SetUpTest resets the cleanup stack, this stops the cleanup functions // being called again. s.SetUpTest(c) // explicitly return the envName to the old value os.Setenv(envName, oldValue) } func (s *cleanupSuite) TestPatchValueInt(c *gc.C) { i := 42 s.PatchValue(&i, 0) c.Assert(i, gc.Equals, 0) s.TearDownTest(c) c.Assert(i, gc.Equals, 42) // SetUpTest resets the cleanup stack, this stops the cleanup functions // being called again. s.SetUpTest(c) } func (s *cleanupSuite) TestPatchValueFunction(c *gc.C) { function := func() string { return "original" } s.PatchValue(&function, func() string { return "patched" }) c.Assert(function(), gc.Equals, "patched") s.TearDownTest(c) c.Assert(function(), gc.Equals, "original") // SetUpTest resets the cleanup stack, this stops the cleanup functions // being called again. s.SetUpTest(c) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/cmd.go�������������������������������������������������0000644�0000153�0000161�00000005467�12321736014�022027� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" gc "launchpad.net/gocheck" ) // HookCommandOutput intercepts CommandOutput to a function that passes the // actual command and it's output back via a channel, and returns the error // passed into this function. It also returns a cleanup function so you can // restore the original function func HookCommandOutput( outputFunc *func(cmd *exec.Cmd) ([]byte, error), output []byte, err error) (<-chan *exec.Cmd, func()) { cmdChan := make(chan *exec.Cmd, 1) origCommandOutput := *outputFunc cleanup := func() { close(cmdChan) *outputFunc = origCommandOutput } *outputFunc = func(cmd *exec.Cmd) ([]byte, error) { cmdChan <- cmd return output, err } return cmdChan, cleanup } const ( // EchoQuotedArgs is a simple bash script that prints out the // basename of the command followed by the args as quoted strings. EchoQuotedArgs = `#!/bin/bash --norc name=` + "`basename $0`" + ` argfile="$name.out" rm -f $argfile printf "%s" $name | tee -a $argfile for arg in "$@"; do printf " \"%s\"" "$arg" | tee -a $argfile done printf "\n" | tee -a $argfile ` ) // EnvironmentPatcher is an interface that requires just one method: // PatchEnvironment. type EnvironmentPatcher interface { PatchEnvironment(name, value string) } // PatchExecutable creates an executable called 'execName' in a new test // directory and that directory is added to the path. func PatchExecutable(c *gc.C, patcher EnvironmentPatcher, execName, script string) { dir := c.MkDir() patcher.PatchEnvironment("PATH", joinPathLists(dir, os.Getenv("PATH"))) filename := filepath.Join(dir, execName) err := ioutil.WriteFile(filename, []byte(script), 0755) c.Assert(err, gc.IsNil) } type CleanupPatcher interface { PatchEnvironment(name, value string) AddCleanup(cleanup CleanupFunc) } // PatchExecutableAsEchoArgs creates an executable called 'execName' in a new // test directory and that directory is added to the path. The content of the // script is 'EchoQuotedArgs', and the args file is removed using a cleanup // function. func PatchExecutableAsEchoArgs(c *gc.C, patcher CleanupPatcher, execName string) { PatchExecutable(c, patcher, execName, EchoQuotedArgs) patcher.AddCleanup(func(*gc.C) { os.Remove(execName + ".out") }) } // AssertEchoArgs is used to check the args from an execution of a command // that has been patchec using PatchExecutable containing EchoQuotedArgs. func AssertEchoArgs(c *gc.C, execName string, args ...string) { content, err := ioutil.ReadFile(execName + ".out") c.Assert(err, gc.IsNil) expected := execName for _, arg := range args { expected = fmt.Sprintf("%s %q", expected, arg) } expected += "\n" c.Assert(string(content), gc.Equals, expected) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/����������������������������������������������0000755�0000153�0000161�00000000000�12321735703�022514� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/checker.go������������������������������������0000644�0000153�0000161�00000012401�12321735703�024445� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "reflect" "strings" "time" gc "launchpad.net/gocheck" ) func TimeBetween(start, end time.Time) gc.Checker { if end.Before(start) { return &timeBetweenChecker{end, start} } return &timeBetweenChecker{start, end} } type timeBetweenChecker struct { start, end time.Time } func (checker *timeBetweenChecker) Info() *gc.CheckerInfo { info := gc.CheckerInfo{ Name: "TimeBetween", Params: []string{"obtained"}, } return &info } func (checker *timeBetweenChecker) Check(params []interface{}, names []string) (result bool, error string) { when, ok := params[0].(time.Time) if !ok { return false, "obtained value type must be time.Time" } if when.Before(checker.start) { return false, fmt.Sprintf("obtained value %#v type must before start value of %#v", when, checker.start) } if when.After(checker.end) { return false, fmt.Sprintf("obtained value %#v type must after end value of %#v", when, checker.end) } return true, "" } // DurationLessThan checker type durationLessThanChecker struct { *gc.CheckerInfo } var DurationLessThan gc.Checker = &durationLessThanChecker{ &gc.CheckerInfo{Name: "DurationLessThan", Params: []string{"obtained", "expected"}}, } func (checker *durationLessThanChecker) Check(params []interface{}, names []string) (result bool, error string) { obtained, ok := params[0].(time.Duration) if !ok { return false, "obtained value type must be time.Duration" } expected, ok := params[1].(time.Duration) if !ok { return false, "expected value type must be time.Duration" } return obtained.Nanoseconds() < expected.Nanoseconds(), "" } // HasPrefix checker for checking strings func stringOrStringer(value interface{}) (string, bool) { result, isString := value.(string) if !isString { if stringer, isStringer := value.(fmt.Stringer); isStringer { result, isString = stringer.String(), true } } return result, isString } type hasPrefixChecker struct { *gc.CheckerInfo } var HasPrefix gc.Checker = &hasPrefixChecker{ &gc.CheckerInfo{Name: "HasPrefix", Params: []string{"obtained", "expected"}}, } func (checker *hasPrefixChecker) Check(params []interface{}, names []string) (result bool, error string) { expected, ok := params[1].(string) if !ok { return false, "expected must be a string" } obtained, isString := stringOrStringer(params[0]) if isString { return strings.HasPrefix(obtained, expected), "" } return false, "Obtained value is not a string and has no .String()" } type hasSuffixChecker struct { *gc.CheckerInfo } var HasSuffix gc.Checker = &hasSuffixChecker{ &gc.CheckerInfo{Name: "HasSuffix", Params: []string{"obtained", "expected"}}, } func (checker *hasSuffixChecker) Check(params []interface{}, names []string) (result bool, error string) { expected, ok := params[1].(string) if !ok { return false, "expected must be a string" } obtained, isString := stringOrStringer(params[0]) if isString { return strings.HasSuffix(obtained, expected), "" } return false, "Obtained value is not a string and has no .String()" } type containsChecker struct { *gc.CheckerInfo } var Contains gc.Checker = &containsChecker{ &gc.CheckerInfo{Name: "Contains", Params: []string{"obtained", "expected"}}, } func (checker *containsChecker) Check(params []interface{}, names []string) (result bool, error string) { expected, ok := params[1].(string) if !ok { return false, "expected must be a string" } obtained, isString := stringOrStringer(params[0]) if isString { return strings.Contains(obtained, expected), "" } return false, "Obtained value is not a string and has no .String()" } type sameContents struct { *gc.CheckerInfo } // SameContents checks that the obtained slice contains all the values (and // same number of values) of the expected slice and vice versa, without respect // to order or duplicates. Uses DeepEquals on mapped contents to compare. var SameContents gc.Checker = &sameContents{ &gc.CheckerInfo{Name: "SameContents", Params: []string{"obtained", "expected"}}, } func (checker *sameContents) Check(params []interface{}, names []string) (result bool, error string) { if len(params) != 2 { return false, "SameContents expects two slice arguments" } obtained := params[0] expected := params[1] tob := reflect.TypeOf(obtained) if tob.Kind() != reflect.Slice { return false, fmt.Sprintf("SameContents expects the obtained value to be a slice, got %q", tob.Kind()) } texp := reflect.TypeOf(expected) if texp.Kind() != reflect.Slice { return false, fmt.Sprintf("SameContents expects the expected value to be a slice, got %q", texp.Kind()) } if texp != tob { return false, fmt.Sprintf( "SameContents expects two slices of the same type, expected: %q, got: %q", texp, tob) } vexp := reflect.ValueOf(expected) vob := reflect.ValueOf(obtained) length := vexp.Len() if vob.Len() != length { // Slice has incorrect number of elements return false, "" } // spin up maps with the entries as keys and the counts as values mob := make(map[interface{}]int, length) mexp := make(map[interface{}]int, length) for i := 0; i < length; i++ { mexp[vexp.Index(i).Interface()]++ mob[vob.Index(i).Interface()]++ } return reflect.DeepEqual(mob, mexp), "" } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/file_test.go����������������������������������0000644�0000153�0000161�00000012103�12321735703�025016� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( "fmt" "io/ioutil" "os" "path/filepath" gc "launchpad.net/gocheck" jc "github.com/juju/testing/checkers" ) type FileSuite struct{} var _ = gc.Suite(&FileSuite{}) func (s *FileSuite) TestIsNonEmptyFile(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) fmt.Fprintf(file, "something") file.Close() c.Assert(file.Name(), jc.IsNonEmptyFile) } func (s *FileSuite) TestIsNonEmptyFileWithEmptyFile(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) file.Close() result, message := jc.IsNonEmptyFile.Check([]interface{}{file.Name()}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, file.Name()+" is empty") } func (s *FileSuite) TestIsNonEmptyFileWithMissingFile(c *gc.C) { name := filepath.Join(c.MkDir(), "missing") result, message := jc.IsNonEmptyFile.Check([]interface{}{name}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, name+" does not exist") } func (s *FileSuite) TestIsNonEmptyFileWithNumber(c *gc.C) { result, message := jc.IsNonEmptyFile.Check([]interface{}{42}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, "obtained value is not a string and has no .String(), int:42") } func (s *FileSuite) TestIsDirectory(c *gc.C) { dir := c.MkDir() c.Assert(dir, jc.IsDirectory) } func (s *FileSuite) TestIsDirectoryMissing(c *gc.C) { absentDir := filepath.Join(c.MkDir(), "foo") result, message := jc.IsDirectory.Check([]interface{}{absentDir}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, absentDir+" does not exist") } func (s *FileSuite) TestIsDirectoryWithFile(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) file.Close() result, message := jc.IsDirectory.Check([]interface{}{file.Name()}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, file.Name()+" is not a directory") } func (s *FileSuite) TestIsDirectoryWithNumber(c *gc.C) { result, message := jc.IsDirectory.Check([]interface{}{42}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, "obtained value is not a string and has no .String(), int:42") } func (s *FileSuite) TestDoesNotExist(c *gc.C) { absentDir := filepath.Join(c.MkDir(), "foo") c.Assert(absentDir, jc.DoesNotExist) } func (s *FileSuite) TestDoesNotExistWithPath(c *gc.C) { dir := c.MkDir() result, message := jc.DoesNotExist.Check([]interface{}{dir}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, dir+" exists") } func (s *FileSuite) TestDoesNotExistWithSymlink(c *gc.C) { dir := c.MkDir() deadPath := filepath.Join(dir, "dead") symlinkPath := filepath.Join(dir, "a-symlink") err := os.Symlink(deadPath, symlinkPath) c.Assert(err, gc.IsNil) // A valid symlink pointing to something that doesn't exist passes. // Use SymlinkDoesNotExist to check for the non-existence of the link itself. c.Assert(symlinkPath, jc.DoesNotExist) } func (s *FileSuite) TestDoesNotExistWithNumber(c *gc.C) { result, message := jc.DoesNotExist.Check([]interface{}{42}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, "obtained value is not a string and has no .String(), int:42") } func (s *FileSuite) TestSymlinkDoesNotExist(c *gc.C) { absentDir := filepath.Join(c.MkDir(), "foo") c.Assert(absentDir, jc.SymlinkDoesNotExist) } func (s *FileSuite) TestSymlinkDoesNotExistWithPath(c *gc.C) { dir := c.MkDir() result, message := jc.SymlinkDoesNotExist.Check([]interface{}{dir}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, dir+" exists") } func (s *FileSuite) TestSymlinkDoesNotExistWithSymlink(c *gc.C) { dir := c.MkDir() deadPath := filepath.Join(dir, "dead") symlinkPath := filepath.Join(dir, "a-symlink") err := os.Symlink(deadPath, symlinkPath) c.Assert(err, gc.IsNil) result, message := jc.SymlinkDoesNotExist.Check([]interface{}{symlinkPath}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, symlinkPath+" exists") } func (s *FileSuite) TestSymlinkDoesNotExistWithNumber(c *gc.C) { result, message := jc.SymlinkDoesNotExist.Check([]interface{}{42}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, gc.Equals, "obtained value is not a string and has no .String(), int:42") } func (s *FileSuite) TestIsSymlink(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) c.Log(file.Name()) c.Log(filepath.Dir(file.Name())) symlinkPath := filepath.Join(filepath.Dir(file.Name()), "a-symlink") err = os.Symlink(file.Name(), symlinkPath) c.Assert(err, gc.IsNil) c.Assert(symlinkPath, jc.IsSymlink) } func (s *FileSuite) TestIsSymlinkWithFile(c *gc.C) { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) result, message := jc.IsSymlink.Check([]interface{}{file.Name()}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, jc.Contains, " is not a symlink") } func (s *FileSuite) TestIsSymlinkWithDir(c *gc.C) { result, message := jc.IsSymlink.Check([]interface{}{c.MkDir()}, nil) c.Assert(result, jc.IsFalse) c.Assert(message, jc.Contains, " is not a symlink") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/log.go����������������������������������������0000644�0000153�0000161�00000005507�12321735703�023633� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "regexp" "strings" "github.com/juju/loggo" gc "launchpad.net/gocheck" ) type SimpleMessage struct { Level loggo.Level Message string } type SimpleMessages []SimpleMessage func (s SimpleMessage) String() string { return fmt.Sprintf("%s %s", s.Level, s.Message) } func (s SimpleMessages) GoString() string { out := make([]string, len(s)) for i, m := range s { out[i] = m.String() } return fmt.Sprintf("SimpleMessages{\n%s\n}", strings.Join(out, "\n")) } func logToSimpleMessages(log []loggo.TestLogValues) SimpleMessages { out := make(SimpleMessages, len(log)) for i, val := range log { out[i].Level = val.Level out[i].Message = val.Message } return out } type logMatches struct { *gc.CheckerInfo } func (checker *logMatches) Check(params []interface{}, names []string) (result bool, error string) { var obtained SimpleMessages switch params[0].(type) { case []loggo.TestLogValues: obtained = logToSimpleMessages(params[0].([]loggo.TestLogValues)) default: return false, "Obtained value must be of type []loggo.TestLogValues or SimpleMessage" } var expected SimpleMessages switch param := params[1].(type) { case []SimpleMessage: expected = SimpleMessages(param) case SimpleMessages: expected = param case []string: expected = make(SimpleMessages, len(param)) for i, s := range param { expected[i] = SimpleMessage{ Message: s, Level: loggo.UNSPECIFIED, } } default: return false, "Expected value must be of type []string or []SimpleMessage" } obtainedSinceLastMatch := obtained for len(expected) > 0 && len(obtained) >= len(expected) { var msg SimpleMessage msg, obtained = obtained[0], obtained[1:] expect := expected[0] if expect.Level != loggo.UNSPECIFIED && msg.Level != expect.Level { continue } matched, err := regexp.MatchString(expect.Message, msg.Message) if err != nil { return false, fmt.Sprintf("bad message regexp %q: %v", expect.Message, err) } else if !matched { continue } expected = expected[1:] obtainedSinceLastMatch = obtained } if len(obtained) < len(expected) { params[0] = obtainedSinceLastMatch params[1] = expected return false, "" } return true, "" } // LogMatches checks whether a given TestLogValues actually contains the log // messages we expected. If you compare it against a list of strings, we only // compare that the strings in the messages are correct. You can alternatively // pass a slice of SimpleMessage and we will check that the log levels are // also correct. // // The log may contain additional messages before and after each of the specified // expected messages. var LogMatches gc.Checker = &logMatches{ &gc.CheckerInfo{Name: "LogMatches", Params: []string{"obtained", "expected"}}, } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/bool_test.go����������������������������������0000644�0000153�0000161�00000005105�12321735703�025036� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( "errors" "os" gc "launchpad.net/gocheck" jc "github.com/juju/testing/checkers" ) type BoolSuite struct{} var _ = gc.Suite(&BoolSuite{}) func (s *BoolSuite) TestIsTrue(c *gc.C) { c.Assert(true, jc.IsTrue) c.Assert(false, gc.Not(jc.IsTrue)) result, msg := jc.IsTrue.Check([]interface{}{false}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Equals, "") result, msg = jc.IsTrue.Check([]interface{}{"foo"}, nil) c.Assert(result, gc.Equals, false) c.Check(msg, gc.Equals, `expected type bool, received type string`) result, msg = jc.IsTrue.Check([]interface{}{42}, nil) c.Assert(result, gc.Equals, false) c.Assert(msg, gc.Equals, `expected type bool, received type int`) } func (s *BoolSuite) TestIsFalse(c *gc.C) { c.Assert(false, jc.IsFalse) c.Assert(true, gc.Not(jc.IsFalse)) } func is42(i int) bool { return i == 42 } var satisfiesTests = []struct { f interface{} arg interface{} result bool msg string }{{ f: is42, arg: 42, result: true, }, { f: is42, arg: 41, result: false, }, { f: is42, arg: "", result: false, msg: "wrong argument type string for func(int) bool", }, { f: os.IsNotExist, arg: errors.New("foo"), result: false, }, { f: os.IsNotExist, arg: os.ErrNotExist, result: true, }, { f: os.IsNotExist, arg: nil, result: false, }, { f: func(chan int) bool { return true }, arg: nil, result: true, }, { f: func(func()) bool { return true }, arg: nil, result: true, }, { f: func(interface{}) bool { return true }, arg: nil, result: true, }, { f: func(map[string]bool) bool { return true }, arg: nil, result: true, }, { f: func(*int) bool { return true }, arg: nil, result: true, }, { f: func([]string) bool { return true }, arg: nil, result: true, }} func (s *BoolSuite) TestSatisfies(c *gc.C) { for i, test := range satisfiesTests { c.Logf("test %d. %T %T", i, test.f, test.arg) result, msg := jc.Satisfies.Check([]interface{}{test.arg, test.f}, nil) c.Check(result, gc.Equals, test.result) c.Check(msg, gc.Equals, test.msg) } } func (s *BoolSuite) TestDeepEquals(c *gc.C) { for i, test := range deepEqualTests { c.Logf("test %d. %v == %v is %v", i, test.a, test.b, test.eq) result, msg := jc.DeepEquals.Check([]interface{}{test.a, test.b}, nil) c.Check(result, gc.Equals, test.eq) if test.eq { c.Check(msg, gc.Equals, "") } else { c.Check(msg, gc.Not(gc.Equals), "") } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/relop_test.go���������������������������������0000644�0000153�0000161�00000001757�12321735703�025235� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( gc "launchpad.net/gocheck" jc "github.com/juju/testing/checkers" ) type RelopSuite struct{} var _ = gc.Suite(&RelopSuite{}) func (s *RelopSuite) TestGreaterThan(c *gc.C) { c.Assert(45, jc.GreaterThan, 42) c.Assert(2.25, jc.GreaterThan, 1.0) c.Assert(42, gc.Not(jc.GreaterThan), 42) c.Assert(10, gc.Not(jc.GreaterThan), 42) result, msg := jc.GreaterThan.Check([]interface{}{"Hello", "World"}, nil) c.Assert(result, jc.IsFalse) c.Assert(msg, gc.Equals, `obtained value string:"Hello" not supported`) } func (s *RelopSuite) TestLessThan(c *gc.C) { c.Assert(42, jc.LessThan, 45) c.Assert(1.0, jc.LessThan, 2.25) c.Assert(42, gc.Not(jc.LessThan), 42) c.Assert(42, gc.Not(jc.LessThan), 10) result, msg := jc.LessThan.Check([]interface{}{"Hello", "World"}, nil) c.Assert(result, jc.IsFalse) c.Assert(msg, gc.Equals, `obtained value string:"Hello" not supported`) } �����������������juju-core_1.18.1/src/github.com/juju/testing/checkers/checker_test.go�������������������������������0000644�0000153�0000161�00000005011�12321735703�025503� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( "testing" gc "launchpad.net/gocheck" jc "github.com/juju/testing/checkers" ) func Test(t *testing.T) { gc.TestingT(t) } type CheckerSuite struct{} var _ = gc.Suite(&CheckerSuite{}) func (s *CheckerSuite) TestHasPrefix(c *gc.C) { c.Assert("foo bar", jc.HasPrefix, "foo") c.Assert("foo bar", gc.Not(jc.HasPrefix), "omg") } func (s *CheckerSuite) TestHasSuffix(c *gc.C) { c.Assert("foo bar", jc.HasSuffix, "bar") c.Assert("foo bar", gc.Not(jc.HasSuffix), "omg") } func (s *CheckerSuite) TestContains(c *gc.C) { c.Assert("foo bar baz", jc.Contains, "foo") c.Assert("foo bar baz", jc.Contains, "bar") c.Assert("foo bar baz", jc.Contains, "baz") c.Assert("foo bar baz", gc.Not(jc.Contains), "omg") } func (s *CheckerSuite) TestSameContents(c *gc.C) { //// positive cases //// // same c.Check( []int{1, 2, 3}, jc.SameContents, []int{1, 2, 3}) // empty c.Check( []int{}, jc.SameContents, []int{}) // single c.Check( []int{1}, jc.SameContents, []int{1}) // different order c.Check( []int{1, 2, 3}, jc.SameContents, []int{3, 2, 1}) // multiple copies of same c.Check( []int{1, 1, 2}, jc.SameContents, []int{2, 1, 1}) type test struct { s string i int } // test structs c.Check( []test{{"a", 1}, {"b", 2}}, jc.SameContents, []test{{"b", 2}, {"a", 1}}) //// negative cases //// // different contents c.Check( []int{1, 3, 2, 5}, gc.Not(jc.SameContents), []int{5, 2, 3, 4}) // different size slices c.Check( []int{1, 2, 3}, gc.Not(jc.SameContents), []int{1, 2}) // different counts of same items c.Check( []int{1, 1, 2}, gc.Not(jc.SameContents), []int{1, 2, 2}) /// Error cases /// // note: for these tests, we can't use gc.Not, since Not passes the error value through // and checks with a non-empty error always count as failed // Oddly, there doesn't seem to actually be a way to check for an error from a Checker. // different type res, err := jc.SameContents.Check([]interface{}{ []string{"1", "2"}, []int{1, 2}, }, []string{}) c.Check(res, jc.IsFalse) c.Check(err, gc.Not(gc.Equals), "") // obtained not a slice res, err = jc.SameContents.Check([]interface{}{ "test", []int{1}, }, []string{}) c.Check(res, jc.IsFalse) c.Check(err, gc.Not(gc.Equals), "") // expected not a slice res, err = jc.SameContents.Check([]interface{}{ []int{1}, "test", }, []string{}) c.Check(res, jc.IsFalse) c.Check(err, gc.Not(gc.Equals), "") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/log_test.go�����������������������������������0000644�0000153�0000161�00000006622�12321735703�024671� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers_test import ( "github.com/juju/loggo" gc "launchpad.net/gocheck" jc "github.com/juju/testing/checkers" ) type LogMatchesSuite struct{} var _ = gc.Suite(&LogMatchesSuite{}) func (s *LogMatchesSuite) TestMatchSimpleMessage(c *gc.C) { log := []loggo.TestLogValues{ {Level: loggo.INFO, Message: "foo bar"}, {Level: loggo.INFO, Message: "12345"}, } c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.INFO, "foo bar"}, {loggo.INFO, "12345"}, }) c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.INFO, "foo .*"}, {loggo.INFO, "12345"}, }) // UNSPECIFIED means we don't care what the level is, // just check the message string matches. c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.UNSPECIFIED, "foo .*"}, {loggo.INFO, "12345"}, }) c.Check(log, gc.Not(jc.LogMatches), []jc.SimpleMessage{ {loggo.INFO, "foo bar"}, {loggo.DEBUG, "12345"}, }) } func (s *LogMatchesSuite) TestMatchStrings(c *gc.C) { log := []loggo.TestLogValues{ {Level: loggo.INFO, Message: "foo bar"}, {Level: loggo.INFO, Message: "12345"}, } c.Check(log, jc.LogMatches, []string{"foo bar", "12345"}) c.Check(log, jc.LogMatches, []string{"foo .*", "12345"}) c.Check(log, gc.Not(jc.LogMatches), []string{"baz", "bing"}) } func (s *LogMatchesSuite) TestMatchInexact(c *gc.C) { log := []loggo.TestLogValues{ {Level: loggo.INFO, Message: "foo bar"}, {Level: loggo.INFO, Message: "baz"}, {Level: loggo.DEBUG, Message: "12345"}, {Level: loggo.ERROR, Message: "12345"}, {Level: loggo.INFO, Message: "67890"}, } c.Check(log, jc.LogMatches, []string{"foo bar", "12345"}) c.Check(log, jc.LogMatches, []string{"foo .*", "12345"}) c.Check(log, jc.LogMatches, []string{"foo .*", "67890"}) c.Check(log, jc.LogMatches, []string{"67890"}) // Matches are always left-most after the previous match. c.Check(log, jc.LogMatches, []string{".*", "baz"}) c.Check(log, jc.LogMatches, []string{"foo bar", ".*", "12345"}) c.Check(log, jc.LogMatches, []string{"foo bar", ".*", "67890"}) // Order is important: 67890 advances to the last item in obtained, // and so there's nothing after to match against ".*". c.Check(log, gc.Not(jc.LogMatches), []string{"67890", ".*"}) // ALL specified patterns MUST match in the order given. c.Check(log, gc.Not(jc.LogMatches), []string{".*", "foo bar"}) // Check that levels are matched. c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.UNSPECIFIED, "12345"}, {loggo.UNSPECIFIED, "12345"}, }) c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.DEBUG, "12345"}, {loggo.ERROR, "12345"}, }) c.Check(log, jc.LogMatches, []jc.SimpleMessage{ {loggo.DEBUG, "12345"}, {loggo.INFO, ".*"}, }) c.Check(log, gc.Not(jc.LogMatches), []jc.SimpleMessage{ {loggo.DEBUG, "12345"}, {loggo.INFO, ".*"}, {loggo.UNSPECIFIED, ".*"}, }) } func (s *LogMatchesSuite) TestFromLogMatches(c *gc.C) { tw := &loggo.TestWriter{} _, err := loggo.ReplaceDefaultWriter(tw) c.Assert(err, gc.IsNil) defer loggo.ResetWriters() logger := loggo.GetLogger("test") logger.SetLogLevel(loggo.DEBUG) logger.Infof("foo") logger.Debugf("bar") logger.Tracef("hidden") c.Check(tw.Log, jc.LogMatches, []string{"foo", "bar"}) c.Check(tw.Log, gc.Not(jc.LogMatches), []string{"foo", "bad"}) c.Check(tw.Log, gc.Not(jc.LogMatches), []jc.SimpleMessage{ {loggo.INFO, "foo"}, {loggo.INFO, "bar"}, }) } ��������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/relop.go��������������������������������������0000644�0000153�0000161�00000004116�12321735703�024166� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "reflect" gc "launchpad.net/gocheck" ) // GreaterThan checker type greaterThanChecker struct { *gc.CheckerInfo } var GreaterThan gc.Checker = &greaterThanChecker{ &gc.CheckerInfo{Name: "GreaterThan", Params: []string{"obtained", "expected"}}, } func (checker *greaterThanChecker) Check(params []interface{}, names []string) (result bool, error string) { defer func() { if v := recover(); v != nil { result = false error = fmt.Sprint(v) } }() p0value := reflect.ValueOf(params[0]) p1value := reflect.ValueOf(params[1]) switch p0value.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return p0value.Int() > p1value.Int(), "" case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return p0value.Uint() > p1value.Uint(), "" case reflect.Float32, reflect.Float64: return p0value.Float() > p1value.Float(), "" default: } return false, fmt.Sprintf("obtained value %s:%#v not supported", p0value.Kind(), params[0]) } // LessThan checker type lessThanChecker struct { *gc.CheckerInfo } var LessThan gc.Checker = &lessThanChecker{ &gc.CheckerInfo{Name: "LessThan", Params: []string{"obtained", "expected"}}, } func (checker *lessThanChecker) Check(params []interface{}, names []string) (result bool, error string) { defer func() { if v := recover(); v != nil { result = false error = fmt.Sprint(v) } }() p0value := reflect.ValueOf(params[0]) p1value := reflect.ValueOf(params[1]) switch p0value.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return p0value.Int() < p1value.Int(), "" case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return p0value.Uint() < p1value.Uint(), "" case reflect.Float32, reflect.Float64: return p0value.Float() < p1value.Float(), "" default: } return false, fmt.Sprintf("obtained value %s:%#v not supported", p0value.Kind(), params[0]) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/deepequal_test.go�����������������������������0000644�0000153�0000161�00000015143�12321735703�026053� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copied with small adaptations from the reflect package in the // Go source tree. We use testing rather than gocheck to preserve // as much source equivalence as possible. // TODO tests for error messages // 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. package checkers_test import ( "regexp" "testing" "github.com/juju/testing/checkers" ) func deepEqual(a1, a2 interface{}) bool { ok, _ := checkers.DeepEqual(a1, a2) return ok } type Basic struct { x int y float32 } type NotBasic Basic type DeepEqualTest struct { a, b interface{} eq bool msg string } // Simple functions for DeepEqual tests. var ( fn1 func() // nil. fn2 func() // nil. fn3 = func() { fn1() } // Not nil. ) var deepEqualTests = []DeepEqualTest{ // Equalities {nil, nil, true, ""}, {1, 1, true, ""}, {int32(1), int32(1), true, ""}, {0.5, 0.5, true, ""}, {float32(0.5), float32(0.5), true, ""}, {"hello", "hello", true, ""}, {make([]int, 10), make([]int, 10), true, ""}, {&[3]int{1, 2, 3}, &[3]int{1, 2, 3}, true, ""}, {Basic{1, 0.5}, Basic{1, 0.5}, true, ""}, {error(nil), error(nil), true, ""}, {map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true, ""}, {fn1, fn2, true, ""}, // Inequalities {1, 2, false, `mismatch at top level: unequal; obtained 1; expected 2`}, {int32(1), int32(2), false, `mismatch at top level: unequal; obtained 1; expected 2`}, {0.5, 0.6, false, `mismatch at top level: unequal; obtained 0\.5; expected 0\.6`}, {float32(0.5), float32(0.6), false, `mismatch at top level: unequal; obtained 0\.5; expected 0\.6`}, {"hello", "hey", false, `mismatch at top level: unequal; obtained "hello"; expected "hey"`}, {make([]int, 10), make([]int, 11), false, `mismatch at top level: length mismatch, 10 vs 11; obtained \[\]int\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\}; expected \[\]int\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\}`}, {&[3]int{1, 2, 3}, &[3]int{1, 2, 4}, false, `mismatch at \(\*\)\[2\]: unequal; obtained 3; expected 4`}, {Basic{1, 0.5}, Basic{1, 0.6}, false, `mismatch at \.y: unequal; obtained 0\.5; expected 0\.6`}, {Basic{1, 0}, Basic{2, 0}, false, `mismatch at \.x: unequal; obtained 1; expected 2`}, {map[int]string{1: "one", 3: "two"}, map[int]string{2: "two", 1: "one"}, false, `mismatch at \[3\]: validity mismatch; obtained "two"; expected <nil>`}, {map[int]string{1: "one", 2: "txo"}, map[int]string{2: "two", 1: "one"}, false, `mismatch at \[2\]: unequal; obtained "txo"; expected "two"`}, {map[int]string{1: "one"}, map[int]string{2: "two", 1: "one"}, false, `mismatch at top level: length mismatch, 1 vs 2; obtained map\[int\]string\{1:"one"\}; expected map\[int\]string\{.*\}`}, {map[int]string{2: "two", 1: "one"}, map[int]string{1: "one"}, false, `mismatch at top level: length mismatch, 2 vs 1; obtained map\[int\]string\{.*\}; expected map\[int\]string\{1:"one"\}`}, {nil, 1, false, `mismatch at top level: nil vs non-nil mismatch; obtained <nil>; expected 1`}, {1, nil, false, `mismatch at top level: nil vs non-nil mismatch; obtained 1; expected <nil>`}, {fn1, fn3, false, `mismatch at top level: non-nil functions; obtained \(func\(\)\)\(nil\); expected \(func\(\)\)\(0x[0-9a-f]+\)`}, {fn3, fn3, false, `mismatch at top level: non-nil functions; obtained \(func\(\)\)\(0x[0-9a-f]+\); expected \(func\(\)\)\(0x[0-9a-f]+\)`}, // Nil vs empty: they're the same (difference from normal DeepEqual) {[]int{}, []int(nil), true, ""}, {[]int{}, []int{}, true, ""}, {[]int(nil), []int(nil), true, ""}, // Mismatched types {1, 1.0, false, `mismatch at top level: type mismatch int vs float64; obtained 1; expected 1`}, {int32(1), int64(1), false, `mismatch at top level: type mismatch int32 vs int64; obtained 1; expected 1`}, {0.5, "hello", false, `mismatch at top level: type mismatch float64 vs string; obtained 0\.5; expected "hello"`}, {[]int{1, 2, 3}, [3]int{1, 2, 3}, false, `mismatch at top level: type mismatch \[\]int vs \[3\]int; obtained \[\]int\{1, 2, 3\}; expected \[3\]int\{1, 2, 3\}`}, {&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false, `mismatch at \(\*\)\[2\]: type mismatch int vs string; obtained 4; expected "s"`}, {Basic{1, 0.5}, NotBasic{1, 0.5}, false, `mismatch at top level: type mismatch checkers_test\.Basic vs checkers_test\.NotBasic; obtained checkers_test\.Basic\{x:1, y:0\.5\}; expected checkers_test\.NotBasic\{x:1, y:0\.5\}`}, { map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false, `mismatch at top level: type mismatch map\[uint\]string vs map\[int\]string; obtained map\[uint\]string\{.*\}; expected map\[int\]string\{.*\}`, }, } func TestDeepEqual(t *testing.T) { for _, test := range deepEqualTests { r, err := checkers.DeepEqual(test.a, test.b) if r != test.eq { t.Errorf("deepEqual(%v, %v) = %v, want %v", test.a, test.b, r, test.eq) } if test.eq { if err != nil { t.Errorf("deepEqual(%v, %v): unexpected error message %q when equal", test.a, test.b, err) } } else { if ok, _ := regexp.MatchString(test.msg, err.Error()); !ok { t.Errorf("deepEqual(%v, %v); unexpected error %q, want %q", test.a, test.b, err.Error(), test.msg) } } } } type Recursive struct { x int r *Recursive } func TestDeepEqualRecursiveStruct(t *testing.T) { a, b := new(Recursive), new(Recursive) *a = Recursive{12, a} *b = Recursive{12, b} if !deepEqual(a, b) { t.Error("deepEqual(recursive same) = false, want true") } } type _Complex struct { a int b [3]*_Complex c *string d map[float64]float64 } func TestDeepEqualComplexStruct(t *testing.T) { m := make(map[float64]float64) stra, strb := "hello", "hello" a, b := new(_Complex), new(_Complex) *a = _Complex{5, [3]*_Complex{a, b, a}, &stra, m} *b = _Complex{5, [3]*_Complex{b, a, a}, &strb, m} if !deepEqual(a, b) { t.Error("deepEqual(complex same) = false, want true") } } func TestDeepEqualComplexStructInequality(t *testing.T) { m := make(map[float64]float64) stra, strb := "hello", "helloo" // Difference is here a, b := new(_Complex), new(_Complex) *a = _Complex{5, [3]*_Complex{a, b, a}, &stra, m} *b = _Complex{5, [3]*_Complex{b, a, a}, &strb, m} if deepEqual(a, b) { t.Error("deepEqual(complex different) = true, want false") } } type UnexpT struct { m map[int]int } func TestDeepEqualUnexportedMap(t *testing.T) { // Check that DeepEqual can look at unexported fields. x1 := UnexpT{map[int]int{1: 2}} x2 := UnexpT{map[int]int{1: 2}} if !deepEqual(&x1, &x2) { t.Error("deepEqual(x1, x2) = false, want true") } y1 := UnexpT{map[int]int{2: 3}} if deepEqual(&x1, &y1) { t.Error("deepEqual(x1, y1) = true, want false") } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/deepequal.go����������������������������������0000644�0000153�0000161�00000020032�12321735703�025005� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copied with small adaptations from the reflect package in the // Go source tree. // 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. package checkers import ( "fmt" "reflect" "unsafe" ) // During deepValueEqual, must keep track of checks that are // in progress. The comparison algorithm assumes that all // checks in progress are true when it reencounters them. // Visited comparisons are stored in a map indexed by visit. type visit struct { a1 uintptr a2 uintptr typ reflect.Type } type mismatchError struct { v1, v2 reflect.Value path string how string } func (err *mismatchError) Error() string { path := err.path if path == "" { path = "top level" } return fmt.Sprintf("mismatch at %s: %s; obtained %#v; expected %#v", path, err.how, interfaceOf(err.v1), interfaceOf(err.v2)) } // Tests for deep equality using reflected types. The map argument tracks // comparisons that have already been seen, which allows short circuiting on // recursive types. func deepValueEqual(path string, v1, v2 reflect.Value, visited map[visit]bool, depth int) (ok bool, err error) { errorf := func(f string, a ...interface{}) error { return &mismatchError{ v1: v1, v2: v2, path: path, how: fmt.Sprintf(f, a...), } } if !v1.IsValid() || !v2.IsValid() { if v1.IsValid() == v2.IsValid() { return true, nil } return false, errorf("validity mismatch") } if v1.Type() != v2.Type() { return false, errorf("type mismatch %s vs %s", v1.Type(), v2.Type()) } // if depth > 10 { panic("deepValueEqual") } // for debugging hard := func(k reflect.Kind) bool { switch k { case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct: return true } return false } if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) { addr1 := v1.UnsafeAddr() addr2 := v2.UnsafeAddr() if addr1 > addr2 { // Canonicalize order to reduce number of entries in visited. addr1, addr2 = addr2, addr1 } // Short circuit if references are identical ... if addr1 == addr2 { return true, nil } // ... or already seen typ := v1.Type() v := visit{addr1, addr2, typ} if visited[v] { return true, nil } // Remember for later. visited[v] = true } switch v1.Kind() { case reflect.Array: if v1.Len() != v2.Len() { // can't happen! return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len()) } for i := 0; i < v1.Len(); i++ { if ok, err := deepValueEqual( fmt.Sprintf("%s[%d]", path, i), v1.Index(i), v2.Index(i), visited, depth+1); !ok { return false, err } } return true, nil case reflect.Slice: // We treat a nil slice the same as an empty slice. if v1.Len() != v2.Len() { return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len()) } if v1.Pointer() == v2.Pointer() { return true, nil } for i := 0; i < v1.Len(); i++ { if ok, err := deepValueEqual( fmt.Sprintf("%s[%d]", path, i), v1.Index(i), v2.Index(i), visited, depth+1); !ok { return false, err } } return true, nil case reflect.Interface: if v1.IsNil() || v2.IsNil() { if v1.IsNil() != v2.IsNil() { return false, fmt.Errorf("nil vs non-nil interface mismatch") } return true, nil } return deepValueEqual(path, v1.Elem(), v2.Elem(), visited, depth+1) case reflect.Ptr: return deepValueEqual("(*"+path+")", v1.Elem(), v2.Elem(), visited, depth+1) case reflect.Struct: for i, n := 0, v1.NumField(); i < n; i++ { path := path + "." + v1.Type().Field(i).Name if ok, err := deepValueEqual(path, v1.Field(i), v2.Field(i), visited, depth+1); !ok { return false, err } } return true, nil case reflect.Map: if v1.IsNil() != v2.IsNil() { return false, errorf("nil vs non-nil mismatch") } if v1.Len() != v2.Len() { return false, errorf("length mismatch, %d vs %d", v1.Len(), v2.Len()) } if v1.Pointer() == v2.Pointer() { return true, nil } for _, k := range v1.MapKeys() { var p string if k.CanInterface() { p = path + "[" + fmt.Sprintf("%#v", k.Interface()) + "]" } else { p = path + "[someKey]" } if ok, err := deepValueEqual(p, v1.MapIndex(k), v2.MapIndex(k), visited, depth+1); !ok { return false, err } } return true, nil case reflect.Func: if v1.IsNil() && v2.IsNil() { return true, nil } // Can't do better than this: return false, errorf("non-nil functions") case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if v1.Int() != v2.Int() { return false, errorf("unequal") } return true, nil case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if v1.Uint() != v2.Uint() { return false, errorf("unequal") } return true, nil case reflect.Float32, reflect.Float64: if v1.Float() != v2.Float() { return false, errorf("unequal") } return true, nil case reflect.Complex64, reflect.Complex128: if v1.Complex() != v2.Complex() { return false, errorf("unequal") } return true, nil case reflect.Bool: if v1.Bool() != v2.Bool() { return false, errorf("unequal") } return true, nil case reflect.String: if v1.String() != v2.String() { return false, errorf("unequal") } return true, nil case reflect.Chan, reflect.UnsafePointer: if v1.Pointer() != v2.Pointer() { return false, errorf("unequal") } return true, nil default: panic("unexpected type " + v1.Type().String()) } } // DeepEqual tests for deep equality. It uses normal == equality where // possible but will scan elements of arrays, slices, maps, and fields // of structs. In maps, keys are compared with == but elements use deep // equality. DeepEqual correctly handles recursive types. Functions are // equal only if they are both nil. // // DeepEqual differs from reflect.DeepEqual in that an empty slice is // equal to a nil slice. If the two values compare unequal, the // resulting error holds the first difference encountered. func DeepEqual(a1, a2 interface{}) (bool, error) { errorf := func(f string, a ...interface{}) error { return &mismatchError{ v1: reflect.ValueOf(a1), v2: reflect.ValueOf(a2), path: "", how: fmt.Sprintf(f, a...), } } if a1 == nil || a2 == nil { if a1 == a2 { return true, nil } return false, errorf("nil vs non-nil mismatch") } v1 := reflect.ValueOf(a1) v2 := reflect.ValueOf(a2) if v1.Type() != v2.Type() { return false, errorf("type mismatch %s vs %s", v1.Type(), v2.Type()) } return deepValueEqual("", v1, v2, make(map[visit]bool), 0) } // interfaceOf returns v.Interface() even if v.CanInterface() == false. // This enables us to call fmt.Printf on a value even if it's derived // from inside an unexported field. func interfaceOf(v reflect.Value) interface{} { if !v.IsValid() { return nil } return bypassCanInterface(v).Interface() } type flag uintptr // copied from reflect/value.go const ( flagRO flag = 1 << iota ) var flagValOffset = func() uintptr { field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") if !ok { panic("reflect.Value has no flag field") } return field.Offset }() func flagField(v *reflect.Value) *flag { return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) } // bypassCanInterface returns a version of v that // bypasses the CanInterface check. func bypassCanInterface(v reflect.Value) reflect.Value { if !v.IsValid() || v.CanInterface() { return v } *flagField(&v) &^= flagRO return v } // Sanity checks against future reflect package changes // to the type or semantics of the Value.flag field. func init() { field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") if !ok { panic("reflect.Value has no flag field") } if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { panic("reflect.Value flag field has changed kind") } var t struct { a int A int } vA := reflect.ValueOf(t).FieldByName("A") va := reflect.ValueOf(t).FieldByName("a") flagA := *flagField(&vA) flaga := *flagField(&va) if flagA&flagRO != 0 || flaga&flagRO == 0 { panic("reflect.Value read-only flag has changed value") } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/file.go���������������������������������������0000644�0000153�0000161�00000010440�12321735703�023761� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "os" "reflect" gc "launchpad.net/gocheck" ) // IsNonEmptyFile checker type isNonEmptyFileChecker struct { *gc.CheckerInfo } var IsNonEmptyFile gc.Checker = &isNonEmptyFileChecker{ &gc.CheckerInfo{Name: "IsNonEmptyFile", Params: []string{"obtained"}}, } func (checker *isNonEmptyFileChecker) Check(params []interface{}, names []string) (result bool, error string) { filename, isString := stringOrStringer(params[0]) if isString { fileInfo, err := os.Stat(filename) if os.IsNotExist(err) { return false, fmt.Sprintf("%s does not exist", filename) } else if err != nil { return false, fmt.Sprintf("other stat error: %v", err) } if fileInfo.Size() > 0 { return true, "" } else { return false, fmt.Sprintf("%s is empty", filename) } } value := reflect.ValueOf(params[0]) return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) } // IsDirectory checker type isDirectoryChecker struct { *gc.CheckerInfo } var IsDirectory gc.Checker = &isDirectoryChecker{ &gc.CheckerInfo{Name: "IsDirectory", Params: []string{"obtained"}}, } func (checker *isDirectoryChecker) Check(params []interface{}, names []string) (result bool, error string) { path, isString := stringOrStringer(params[0]) if isString { fileInfo, err := os.Stat(path) if os.IsNotExist(err) { return false, fmt.Sprintf("%s does not exist", path) } else if err != nil { return false, fmt.Sprintf("other stat error: %v", err) } if fileInfo.IsDir() { return true, "" } else { return false, fmt.Sprintf("%s is not a directory", path) } } value := reflect.ValueOf(params[0]) return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) } // IsSymlink checker type isSymlinkChecker struct { *gc.CheckerInfo } var IsSymlink gc.Checker = &isSymlinkChecker{ &gc.CheckerInfo{Name: "IsSymlink", Params: []string{"obtained"}}, } func (checker *isSymlinkChecker) Check(params []interface{}, names []string) (result bool, error string) { path, isString := stringOrStringer(params[0]) if isString { fileInfo, err := os.Lstat(path) if os.IsNotExist(err) { return false, fmt.Sprintf("%s does not exist", path) } else if err != nil { return false, fmt.Sprintf("other stat error: %v", err) } if fileInfo.Mode()&os.ModeSymlink != 0 { return true, "" } else { return false, fmt.Sprintf("%s is not a symlink: %+v", path, fileInfo) } } value := reflect.ValueOf(params[0]) return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) } // DoesNotExist checker makes sure the path specified doesn't exist. type doesNotExistChecker struct { *gc.CheckerInfo } var DoesNotExist gc.Checker = &doesNotExistChecker{ &gc.CheckerInfo{Name: "DoesNotExist", Params: []string{"obtained"}}, } func (checker *doesNotExistChecker) Check(params []interface{}, names []string) (result bool, error string) { path, isString := stringOrStringer(params[0]) if isString { _, err := os.Stat(path) if os.IsNotExist(err) { return true, "" } else if err != nil { return false, fmt.Sprintf("other stat error: %v", err) } return false, fmt.Sprintf("%s exists", path) } value := reflect.ValueOf(params[0]) return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) } // SymlinkDoesNotExist checker makes sure the path specified doesn't exist. type symlinkDoesNotExistChecker struct { *gc.CheckerInfo } var SymlinkDoesNotExist gc.Checker = &symlinkDoesNotExistChecker{ &gc.CheckerInfo{Name: "SymlinkDoesNotExist", Params: []string{"obtained"}}, } func (checker *symlinkDoesNotExistChecker) Check(params []interface{}, names []string) (result bool, error string) { path, isString := stringOrStringer(params[0]) if isString { _, err := os.Lstat(path) if os.IsNotExist(err) { return true, "" } else if err != nil { return false, fmt.Sprintf("other stat error: %v", err) } return false, fmt.Sprintf("%s exists", path) } value := reflect.ValueOf(params[0]) return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/checkers/bool.go���������������������������������������0000644�0000153�0000161�00000005702�12321735703�024002� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package checkers import ( "fmt" "reflect" gc "launchpad.net/gocheck" ) type isTrueChecker struct { *gc.CheckerInfo } // IsTrue checks whether a value has an underlying // boolean type and is true. var IsTrue gc.Checker = &isTrueChecker{ &gc.CheckerInfo{Name: "IsTrue", Params: []string{"obtained"}}, } // IsTrue checks whether a value has an underlying // boolean type and is false. var IsFalse gc.Checker = gc.Not(IsTrue) func (checker *isTrueChecker) Check(params []interface{}, names []string) (result bool, error string) { value := reflect.ValueOf(params[0]) switch value.Kind() { case reflect.Bool: return value.Bool(), "" } return false, fmt.Sprintf("expected type bool, received type %s", value.Type()) } type satisfiesChecker struct { *gc.CheckerInfo } // Satisfies checks whether a value causes the argument // function to return true. The function must be of // type func(T) bool where the value being checked // is assignable to T. var Satisfies gc.Checker = &satisfiesChecker{ &gc.CheckerInfo{ Name: "Satisfies", Params: []string{"obtained", "func(T) bool"}, }, } func (checker *satisfiesChecker) Check(params []interface{}, names []string) (result bool, error string) { f := reflect.ValueOf(params[1]) ft := f.Type() if ft.Kind() != reflect.Func || ft.NumIn() != 1 || ft.NumOut() != 1 || ft.Out(0) != reflect.TypeOf(true) { return false, fmt.Sprintf("expected func(T) bool, got %s", ft) } v := reflect.ValueOf(params[0]) if !v.IsValid() { if !canBeNil(ft.In(0)) { return false, fmt.Sprintf("cannot assign nil to argument %T", ft.In(0)) } v = reflect.Zero(ft.In(0)) } if !v.Type().AssignableTo(ft.In(0)) { return false, fmt.Sprintf("wrong argument type %s for %s", v.Type(), ft) } return f.Call([]reflect.Value{v})[0].Interface().(bool), "" } func canBeNil(t reflect.Type) bool { switch t.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: return true } return false } type deepEqualsChecker struct { *gc.CheckerInfo } // The DeepEquals checker verifies that the obtained value is deep-equal to // the expected value. The check will work correctly even when facing // slices, interfaces, and values of different types (which always fail // the test). // // For example: // // c.Assert(value, DeepEquals, 42) // c.Assert(array, DeepEquals, []string{"hi", "there"}) // // This checker differs from gocheck.DeepEquals in that // it will compare a nil slice equal to an empty slice, // and a nil map equal to an empty map. var DeepEquals gc.Checker = &deepEqualsChecker{ &gc.CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}}, } func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) { if ok, err := DeepEqual(params[0], params[1]); !ok { return false, err.Error() } return true, "" } ��������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/patch_test.go������������������������������������������0000644�0000153�0000161�00000005221�12321735703�023412� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "errors" "os" gc "launchpad.net/gocheck" "github.com/juju/testing" ) type PatchValueSuite struct{} var _ = gc.Suite(&PatchValueSuite{}) func (*PatchValueSuite) TestSetInt(c *gc.C) { i := 99 restore := testing.PatchValue(&i, 88) c.Assert(i, gc.Equals, 88) restore() c.Assert(i, gc.Equals, 99) } func (*PatchValueSuite) TestSetError(c *gc.C) { oldErr := errors.New("foo") newErr := errors.New("bar") err := oldErr restore := testing.PatchValue(&err, newErr) c.Assert(err, gc.Equals, newErr) restore() c.Assert(err, gc.Equals, oldErr) } func (*PatchValueSuite) TestSetErrorToNil(c *gc.C) { oldErr := errors.New("foo") err := oldErr restore := testing.PatchValue(&err, nil) c.Assert(err, gc.Equals, nil) restore() c.Assert(err, gc.Equals, oldErr) } func (*PatchValueSuite) TestSetMapToNil(c *gc.C) { oldMap := map[string]int{"foo": 1234} m := oldMap restore := testing.PatchValue(&m, nil) c.Assert(m, gc.IsNil) restore() c.Assert(m, gc.DeepEquals, oldMap) } func (*PatchValueSuite) TestSetPanicsWhenNotAssignable(c *gc.C) { i := 99 type otherInt int c.Assert(func() { testing.PatchValue(&i, otherInt(88)) }, gc.PanicMatches, `reflect\.Set: value of type testing_test\.otherInt is not assignable to type int`) } type PatchEnvironmentSuite struct{} var _ = gc.Suite(&PatchEnvironmentSuite{}) func (*PatchEnvironmentSuite) TestPatchEnvironment(c *gc.C) { const envName = "TESTING_PATCH_ENVIRONMENT" // remember the old value, and set it to something we can check oldValue := os.Getenv(envName) os.Setenv(envName, "initial") restore := testing.PatchEnvironment(envName, "new value") // Using check to make sure the environment gets set back properly in the test. c.Check(os.Getenv(envName), gc.Equals, "new value") restore() c.Check(os.Getenv(envName), gc.Equals, "initial") os.Setenv(envName, oldValue) } func (*PatchEnvironmentSuite) TestRestorerAdd(c *gc.C) { var order []string first := testing.Restorer(func() { order = append(order, "first") }) second := testing.Restorer(func() { order = append(order, "second") }) restore := first.Add(second) restore() c.Assert(order, gc.DeepEquals, []string{"second", "first"}) } func (*PatchEnvironmentSuite) TestPatchEnvPathPrepend(c *gc.C) { oldPath := os.Getenv("PATH") dir := "/bin/bar" // just in case something goes wrong defer os.Setenv("PATH", oldPath) restore := testing.PatchEnvPathPrepend(dir) expect := dir + string(os.PathListSeparator) + oldPath c.Check(os.Getenv("PATH"), gc.Equals, expect) restore() c.Check(os.Getenv("PATH"), gc.Equals, oldPath) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/logging/�����������������������������������������������0000755�0000153�0000161�00000000000�12321735703�022353� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/logging/log.go�����������������������������������������0000644�0000153�0000161�00000002011�12321735703�023455� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package logging import ( "fmt" "time" "github.com/juju/loggo" gc "launchpad.net/gocheck" "github.com/juju/testing" ) // LoggingSuite redirects the juju logger to the test logger // when embedded in a gocheck suite type. type LoggingSuite struct { testing.CleanupSuite } type gocheckWriter struct { c *gc.C } func (w *gocheckWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) { // Magic calldepth value... w.c.Output(3, fmt.Sprintf("%s %s %s", level, module, message)) } func (t *LoggingSuite) SetUpSuite(c *gc.C) { t.CleanupSuite.SetUpSuite(c) t.setUp(c) t.AddSuiteCleanup(func(*gc.C) { loggo.ResetLoggers() loggo.ResetWriters() }) } func (t *LoggingSuite) SetUpTest(c *gc.C) { t.CleanupSuite.SetUpTest(c) t.setUp(c) } func (t *LoggingSuite) setUp(c *gc.C) { loggo.ResetWriters() loggo.ReplaceDefaultWriter(&gocheckWriter{c}) loggo.ResetLoggers() } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/logging/log_test.go������������������������������������0000644�0000153�0000161�00000002646�12321735703�024532� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package logging_test import ( gc "launchpad.net/gocheck" "github.com/juju/loggo" "github.com/juju/testing/logging" ) var logger = loggo.GetLogger("test") type logSuite struct { logging.LoggingSuite } var _ = gc.Suite(&logSuite{}) func (s *logSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) logger.SetLogLevel(loggo.INFO) logger.Infof("testing-SetUpSuite") c.Assert(c.GetTestLog(), gc.Matches, ".*INFO test testing-SetUpSuite\n") } func (s *logSuite) TearDownSuite(c *gc.C) { // Unfortunately there's no way of testing that the // log output is printed, as the logger is printing // a previously set up *gc.C. We print a message // anyway so that we can manually verify it. logger.Infof("testing-TearDownSuite") } func (s *logSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) // The SetUpTest resets the logging levels. logger.SetLogLevel(loggo.INFO) logger.Infof("testing-SetUpTest") c.Assert(c.GetTestLog(), gc.Matches, ".*INFO test testing-SetUpTest\n") } func (s *logSuite) TearDownTest(c *gc.C) { // The same applies here as to TearDownSuite. logger.Infof("testing-TearDownTest") s.LoggingSuite.TearDownTest(c) } func (s *logSuite) TestLog(c *gc.C) { logger.Infof("testing-Test") c.Assert(c.GetTestLog(), gc.Matches, ".*INFO test testing-SetUpTest\n"+ ".*INFO test testing-Test\n", ) } ������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/logging/package_test.go��������������������������������0000644�0000153�0000161�00000000332�12321735703�025332� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package logging_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/package_test.go����������������������������������������0000644�0000153�0000161�00000000332�12321735703�023704� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/README.md����������������������������������������������0000644�0000153�0000161�00000000150�12321735703�022200� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju/testing ============ This package provides additional base test suites to be used with gocheck. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/cleanup.go���������������������������������������������0000644�0000153�0000161�00000005312�12321735703�022704� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "os/exec" gc "launchpad.net/gocheck" ) type CleanupFunc func(*gc.C) type cleanupStack []CleanupFunc // CleanupSuite adds the ability to add cleanup functions that are called // during either test tear down or suite tear down depending on the method // called. type CleanupSuite struct { testStack cleanupStack suiteStack cleanupStack } func (s *CleanupSuite) SetUpSuite(c *gc.C) { s.suiteStack = nil } func (s *CleanupSuite) TearDownSuite(c *gc.C) { s.callStack(c, s.suiteStack) } func (s *CleanupSuite) SetUpTest(c *gc.C) { s.testStack = nil } func (s *CleanupSuite) TearDownTest(c *gc.C) { s.callStack(c, s.testStack) } func (s *CleanupSuite) callStack(c *gc.C, stack cleanupStack) { for i := len(stack) - 1; i >= 0; i-- { stack[i](c) } } // AddCleanup pushes the cleanup function onto the stack of functions to be // called during TearDownTest. func (s *CleanupSuite) AddCleanup(cleanup CleanupFunc) { s.testStack = append(s.testStack, cleanup) } // AddSuiteCleanup pushes the cleanup function onto the stack of functions to // be called during TearDownSuite. func (s *CleanupSuite) AddSuiteCleanup(cleanup CleanupFunc) { s.suiteStack = append(s.suiteStack, cleanup) } // PatchEnvironment sets the environment variable 'name' the the value passed // in. The old value is saved and returned to the original value at test tear // down time using a cleanup function. func (s *CleanupSuite) PatchEnvironment(name, value string) { restore := PatchEnvironment(name, value) s.AddCleanup(func(*gc.C) { restore() }) } // PatchEnvPathPrepend prepends the given path to the environment $PATH and restores the // original path on test teardown. func (s *CleanupSuite) PatchEnvPathPrepend(dir string) { restore := PatchEnvPathPrepend(dir) s.AddCleanup(func(*gc.C) { restore() }) } // PatchValue sets the 'dest' variable the the value passed in. The old value // is saved and returned to the original value at test tear down time using a // cleanup function. The value must be assignable to the element type of the // destination. func (s *CleanupSuite) PatchValue(dest, value interface{}) { restore := PatchValue(dest, value) s.AddCleanup(func(*gc.C) { restore() }) } // HookCommandOutput calls the package function of the same name to mock out // the result of a particular comand execution, and will call the restore // function on test teardown. func (s *CleanupSuite) HookCommandOutput( outputFunc *func(cmd *exec.Cmd) ([]byte, error), output []byte, err error, ) <-chan *exec.Cmd { result, restore := HookCommandOutput(outputFunc, output, err) s.AddCleanup(func(*gc.C) { restore() }) return result } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/cmd_test.go��������������������������������������������0000644�0000153�0000161�00000003270�12321735703�023060� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing_test import ( "os/exec" gc "launchpad.net/gocheck" "github.com/juju/testing" jc "github.com/juju/testing/checkers" ) type cmdSuite struct { testing.CleanupSuite } var _ = gc.Suite(&cmdSuite{}) func (s *cmdSuite) TestHookCommandOutput(c *gc.C) { var CommandOutput = (*exec.Cmd).CombinedOutput cmdChan, cleanup := testing.HookCommandOutput(&CommandOutput, []byte{1, 2, 3, 4}, nil) defer cleanup() testCmd := exec.Command("fake-command", "arg1", "arg2") out, err := CommandOutput(testCmd) c.Assert(err, gc.IsNil) cmd := <-cmdChan c.Assert(out, gc.DeepEquals, []byte{1, 2, 3, 4}) c.Assert(cmd.Args, gc.DeepEquals, []string{"fake-command", "arg1", "arg2"}) } func (s *cmdSuite) EnsureArgFileRemoved(name string) { s.AddCleanup(func(c *gc.C) { c.Assert(name+".out", jc.DoesNotExist) }) } const testFunc = "test-ouput" func (s *cmdSuite) TestPatchExecutableNoArgs(c *gc.C) { s.EnsureArgFileRemoved(testFunc) testing.PatchExecutableAsEchoArgs(c, s, testFunc) output := runCommand(c, testFunc) c.Assert(output, gc.Equals, testFunc+"\n") testing.AssertEchoArgs(c, testFunc) } func (s *cmdSuite) TestPatchExecutableWithArgs(c *gc.C) { s.EnsureArgFileRemoved(testFunc) testing.PatchExecutableAsEchoArgs(c, s, testFunc) output := runCommand(c, testFunc, "foo", "bar baz") c.Assert(output, gc.Equals, testFunc+" \"foo\" \"bar baz\"\n") testing.AssertEchoArgs(c, testFunc, "foo", "bar baz") } func runCommand(c *gc.C, command string, args ...string) string { cmd := exec.Command(command, args...) out, err := cmd.CombinedOutput() c.Assert(err, gc.IsNil) return string(out) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/testing/patch.go�����������������������������������������������0000644�0000153�0000161�00000003660�12321735703�022360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013, 2014 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. package testing import ( "os" "reflect" "strings" ) // Restorer holds a function that can be used // to restore some previous state. type Restorer func() // Add returns a Restorer that restores first f1 // and then f. It is valid to call this on a nil // Restorer. func (f Restorer) Add(f1 Restorer) Restorer { return func() { f1.Restore() if f != nil { f.Restore() } } } // Restore restores some previous state. func (r Restorer) Restore() { r() } // PatchValue sets the value pointed to by the given destination to the given // value, and returns a function to restore it to its original value. The // value must be assignable to the element type of the destination. func PatchValue(dest, value interface{}) Restorer { destv := reflect.ValueOf(dest).Elem() oldv := reflect.New(destv.Type()).Elem() oldv.Set(destv) valuev := reflect.ValueOf(value) if !valuev.IsValid() { // This isn't quite right when the destination type is not // nilable, but it's better than the complex alternative. valuev = reflect.Zero(destv.Type()) } destv.Set(valuev) return func() { destv.Set(oldv) } } // PatchEnvironment provides a test a simple way to override a single // environment variable. A function is returned that will return the // environment to what it was before. func PatchEnvironment(name, value string) Restorer { oldValue := os.Getenv(name) os.Setenv(name, value) return func() { os.Setenv(name, oldValue) } } // PatchEnvPathPrepend provides a simple way to prepend path to the start of the // PATH environment variable. Returns a function that restores the environment // to what it was before. func PatchEnvPathPrepend(dir string) Restorer { return PatchEnvironment("PATH", joinPathLists(dir, os.Getenv("PATH"))) } func joinPathLists(paths ...string) string { return strings.Join(paths, string(os.PathListSeparator)) } ��������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/���������������������������������������������������������0000755�0000153�0000161�00000000000�12321736015�020354� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/COPYING.LESSER�������������������������������������������0000644�0000153�0000161�00000016743�12321735652�022424� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �����������������������������juju-core_1.18.1/src/github.com/juju/loggo/LICENSE��������������������������������������������������0000644�0000153�0000161�00000001214�12321735652�021365� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������loggo - A logging library for Go Copyright 2013, Canonical Ltd. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. See both COPYING and COPYING.LESSER for the full terms of the GNU Lesser General Public License. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/testwriter.go��������������������������������������������0000644�0000153�0000161�00000001607�12321735652�023131� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo import ( "path" "time" ) // TestLogValues represents a single logging call. type TestLogValues struct { Level Level Module string Filename string Line int Timestamp time.Time Message string } // TestWriter is a useful Writer for testing purposes. Each component of the // logging message is stored in the Log array. type TestWriter struct { Log []TestLogValues } // Write saves the params as members in the TestLogValues struct appended to the Log array. func (writer *TestWriter) Write(level Level, module, filename string, line int, timestamp time.Time, message string) { if writer.Log == nil { writer.Log = []TestLogValues{} } writer.Log = append(writer.Log, TestLogValues{level, module, path.Base(filename), line, timestamp, message}) } // Clear removes any saved log messages. func (writer *TestWriter) Clear() { writer.Log = []TestLogValues{} } �������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/logger.go������������������������������������������������0000644�0000153�0000161�00000027360�12321736015�022172� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Module level logging for Go // // This package provides an alternative to the standard library log package. // // The actual logging functions never return errors. If you are logging // something, you really don't want to be worried about the logging // having trouble. // // Modules have names that are defined by dotted strings. // e.g. "first.second.third" // // There is a root module that has the name "". Each module // (except the root module) has a parent, identified by the part of // the name without the last dotted value. // e.g. the parent of "first.second.third" is "first.second" // the parent of "first.second" is "first" // the parent of "first" is "" (the root module) // // Each module can specify its own severity level. Logging calls that are of // a lower severity than the module's effective severity level are not written // out. // // Loggers are created using the GetLogger function. // e.g. logger := loggo.GetLogger("foo.bar") // // By default there is one writer registered, which will write to Stderr, // and the root module, which will only emit warnings and above. // If you want to continue using the default // logger, but have it emit all logging levels you need to do the following. // // writer, _, err := loggo.RemoveWriter("default") // // err is non-nil if and only if the name isn't found. // loggo.RegisterWriter("default", writer, loggo.TRACE) package loggo import ( "fmt" "runtime" "sort" "strings" "sync" "sync/atomic" "time" ) // Level holds a severity level. type Level uint32 // The severity levels. Higher values are more considered more // important. const ( UNSPECIFIED Level = iota TRACE DEBUG INFO WARNING ERROR CRITICAL ) // A Logger represents a logging module. It has an associated logging // level which can be changed; messages of lesser severity will // be dropped. Loggers have a hierarchical relationship - see // the package documentation. // // The zero Logger value is usable - any messages logged // to it will be sent to the root Logger. type Logger struct { impl *module } type module struct { name string level Level parent *module } // Initially the modules map only contains the root module. var ( root = &module{level: WARNING} modulesMutex sync.Mutex modules = map[string]*module{ "": root, } ) func (level Level) String() string { switch level { case UNSPECIFIED: return "UNSPECIFIED" case TRACE: return "TRACE" case DEBUG: return "DEBUG" case INFO: return "INFO" case WARNING: return "WARNING" case ERROR: return "ERROR" case CRITICAL: return "CRITICAL" } return "<unknown>" } // get atomically gets the value of the given level. func (level *Level) get() Level { return Level(atomic.LoadUint32((*uint32)(level))) } // set atomically sets the value of the receiver // to the given level. func (level *Level) set(newLevel Level) { atomic.StoreUint32((*uint32)(level), uint32(newLevel)) } // getLoggerInternal assumes that the modulesMutex is locked. func getLoggerInternal(name string) Logger { impl, found := modules[name] if found { return Logger{impl} } parentName := "" if i := strings.LastIndex(name, "."); i >= 0 { parentName = name[0:i] } parent := getLoggerInternal(parentName).impl impl = &module{name, UNSPECIFIED, parent} modules[name] = impl return Logger{impl} } // GetLogger returns a Logger for the given module name, // creating it and its parents if necessary. func GetLogger(name string) Logger { // Lowercase the module name, and look for it in the modules map. name = strings.ToLower(name) modulesMutex.Lock() defer modulesMutex.Unlock() return getLoggerInternal(name) } // LoggerInfo returns information about the configured loggers and their logging // levels. The information is returned in the format expected by // ConfigureModules. Loggers with UNSPECIFIED level will not // be included. func LoggerInfo() string { output := []string{} // output in alphabetical order. keys := []string{} modulesMutex.Lock() defer modulesMutex.Unlock() for key := range modules { keys = append(keys, key) } sort.Strings(keys) for _, name := range keys { mod := modules[name] severity := mod.level if severity == UNSPECIFIED { continue } output = append(output, fmt.Sprintf("%s=%s", mod.Name(), severity)) } return strings.Join(output, ";") } // ParseConfigurationString parses a logger configuration string into a map of // logger names and their associated log level. This method is provided to // allow other programs to pre-validate a configuration string rather than // just calling ConfigureLoggers. // // Loggers are colon- or semicolon-separated; each module is specified as // <modulename>=<level>. White space outside of module names and levels is // ignored. The root module is specified with the name "<root>". // // An example specification: // `<root>=ERROR; foo.bar=WARNING` func ParseConfigurationString(specification string) (map[string]Level, error) { values := strings.FieldsFunc(specification, func(r rune) bool { return r == ';' || r == ':' }) levels := make(map[string]Level) for _, value := range values { s := strings.SplitN(value, "=", 2) if len(s) < 2 { return nil, fmt.Errorf("logger specification expected '=', found %q", value) } name := strings.TrimSpace(s[0]) levelStr := strings.TrimSpace(s[1]) if name == "" || levelStr == "" { return nil, fmt.Errorf("logger specification %q has blank name or level", value) } if name == "<root>" { name = "" } level, ok := ParseLevel(levelStr) if !ok { return nil, fmt.Errorf("unknown severity level %q", levelStr) } levels[name] = level } return levels, nil } // ConfigureLoggers configures loggers according to the given string // specification, which specifies a set of modules and their associated // logging levels. Loggers are colon- or semicolon-separated; each // module is specified as <modulename>=<level>. White space outside of // module names and levels is ignored. The root module is specified // with the name "<root>". // // An example specification: // `<root>=ERROR; foo.bar=WARNING` func ConfigureLoggers(specification string) error { if specification == "" { return nil } levels, err := ParseConfigurationString(specification) if err != nil { return err } for name, level := range levels { GetLogger(name).SetLogLevel(level) } return nil } // ResetLogging iterates through the known modules and sets the levels of all // to UNSPECIFIED, except for <root> which is set to WARNING. func ResetLoggers() { modulesMutex.Lock() defer modulesMutex.Unlock() for name, module := range modules { if name == "" { module.level.set(WARNING) } else { module.level.set(UNSPECIFIED) } } } // ParseLevel converts a string representation of a logging level to a // Level. It returns the level and whether it was valid or not. func ParseLevel(level string) (Level, bool) { level = strings.ToUpper(level) switch level { case "UNSPECIFIED": return UNSPECIFIED, true case "TRACE": return TRACE, true case "DEBUG": return DEBUG, true case "INFO": return INFO, true case "WARN", "WARNING": return WARNING, true case "ERROR": return ERROR, true case "CRITICAL": return CRITICAL, true } return UNSPECIFIED, false } func (logger Logger) getModule() *module { if logger.impl == nil { return root } return logger.impl } // Name returns the logger's module name. func (logger Logger) Name() string { return logger.getModule().Name() } // LogLevel returns the configured log level of the logger. func (logger Logger) LogLevel() Level { return logger.getModule().level.get() } func (module *module) getEffectiveLogLevel() Level { // Note: the root module is guaranteed to have a // specified logging level, so acts as a suitable sentinel // for this loop. for { if level := module.level.get(); level != UNSPECIFIED { return level } module = module.parent } panic("unreachable") } func (module *module) Name() string { if module.name == "" { return "<root>" } return module.name } // EffectiveLogLevel returns the effective log level of // the receiver - that is, messages with a lesser severity // level will be discarded. // // If the log level of the receiver is unspecified, // it will be taken from the effective log level of its // parent. func (logger Logger) EffectiveLogLevel() Level { return logger.getModule().getEffectiveLogLevel() } // SetLogLevel sets the severity level of the given logger. // The root logger cannot be set to UNSPECIFIED level. // See EffectiveLogLevel for how this affects the // actual messages logged. func (logger Logger) SetLogLevel(level Level) { module := logger.getModule() // The root module can't be unspecified. if module.name == "" && level == UNSPECIFIED { level = WARNING } module.level.set(level) } // Logf logs a printf-formatted message at the given level. // A message will be discarded if level is less than the // the effective log level of the logger. // Note that the writers may also filter out messages that // are less than their registered minimum severity level. func (logger Logger) Logf(level Level, message string, args ...interface{}) { if logger.getModule().getEffectiveLogLevel() > level || !WillWrite(level) || level < TRACE || level > CRITICAL { return } // Gather time, and filename, line number. now := time.Now() // get this early. // Param to Caller is the call depth. Since this method is called from // the Logger methods, we want the place that those were called from. _, file, line, ok := runtime.Caller(2) if !ok { file = "???" line = 0 } // Trim newline off format string, following usual // Go logging conventions. if len(message) > 0 && message[len(message)-1] == '\n' { message = message[0 : len(message)-1] } formattedMessage := fmt.Sprintf(message, args...) writeToWriters(level, logger.impl.name, file, line, now, formattedMessage) } // Criticalf logs the printf-formatted message at critical level. func (logger Logger) Criticalf(message string, args ...interface{}) { logger.Logf(CRITICAL, message, args...) } // Errorf logs the printf-formatted message at error level. func (logger Logger) Errorf(message string, args ...interface{}) { logger.Logf(ERROR, message, args...) } // Warningf logs the printf-formatted message at warning level. func (logger Logger) Warningf(message string, args ...interface{}) { logger.Logf(WARNING, message, args...) } // Infof logs the printf-formatted message at info level. func (logger Logger) Infof(message string, args ...interface{}) { logger.Logf(INFO, message, args...) } // Debugf logs the printf-formatted message at debug level. func (logger Logger) Debugf(message string, args ...interface{}) { logger.Logf(DEBUG, message, args...) } // Tracef logs the printf-formatted message at trace level. func (logger Logger) Tracef(message string, args ...interface{}) { logger.Logf(TRACE, message, args...) } // IsLevelEnabled returns whether debugging is enabled // for the given log level. func (logger Logger) IsLevelEnabled(level Level) bool { return logger.getModule().getEffectiveLogLevel() <= level } // IsErrorEnabled returns whether debugging is enabled // at error level. func (logger Logger) IsErrorEnabled() bool { return logger.IsLevelEnabled(ERROR) } // IsWarningEnabled returns whether debugging is enabled // at warning level. func (logger Logger) IsWarningEnabled() bool { return logger.IsLevelEnabled(WARNING) } // IsInfoEnabled returns whether debugging is enabled // at info level. func (logger Logger) IsInfoEnabled() bool { return logger.IsLevelEnabled(INFO) } // IsDebugEnabled returns whether debugging is enabled // at debug level. func (logger Logger) IsDebugEnabled() bool { return logger.IsLevelEnabled(DEBUG) } // IsTraceEnabled returns whether debugging is enabled // at trace level. func (logger Logger) IsTraceEnabled() bool { return logger.IsLevelEnabled(TRACE) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/formatter_test.go����������������������������������������0000644�0000153�0000161�00000001132�12321735652�023750� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo_test import ( "time" gc "launchpad.net/gocheck" "github.com/juju/loggo" ) type formatterSuite struct{} var _ = gc.Suite(&formatterSuite{}) func (*formatterSuite) TestDefaultFormat(c *gc.C) { location, err := time.LoadLocation("UTC") c.Assert(err, gc.IsNil) testTime := time.Date(2013, 5, 3, 10, 53, 24, 123456, location) formatter := &loggo.DefaultFormatter{} formatted := formatter.Format(loggo.WARNING, "test.module", "some/deep/filename", 42, testTime, "hello world!") c.Assert(formatted, gc.Equals, "2013-05-03 10:53:24 WARNING test.module filename:42 hello world!") } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/formatter.go���������������������������������������������0000644�0000153�0000161�00000001652�12321735652�022720� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo import ( "fmt" "path/filepath" "time" ) // Formatter defines the single method Format, which takes the logging // information, and converts it to a string. type Formatter interface { Format(level Level, module, filename string, line int, timestamp time.Time, message string) string } // DefaultFormatter provides a simple concatenation of all the components. type DefaultFormatter struct{} // Format returns the parameters separated by spaces except for filename and // line which are separated by a colon. The timestamp is shown to second // resolution in UTC. func (*DefaultFormatter) Format(level Level, module, filename string, line int, timestamp time.Time, message string) string { ts := timestamp.In(time.UTC).Format("2006-01-02 15:04:05") // Just get the basename from the filename filename = filepath.Base(filename) return fmt.Sprintf("%s %s %s %s:%d %s", ts, level, module, filename, line, message) } ��������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/writer_test.go�������������������������������������������0000644�0000153�0000161�00000017211�12321735652�023266� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo_test import ( "fmt" "time" gc "launchpad.net/gocheck" "github.com/juju/loggo" ) type writerBasicsSuite struct{} var _ = gc.Suite(&writerBasicsSuite{}) func (s *writerBasicsSuite) TearDownTest(c *gc.C) { loggo.ResetWriters() } func (*writerBasicsSuite) TestRemoveDefaultWriter(c *gc.C) { defaultWriter, level, err := loggo.RemoveWriter("default") c.Assert(err, gc.IsNil) c.Assert(level, gc.Equals, loggo.TRACE) c.Assert(defaultWriter, gc.NotNil) // Trying again fails. defaultWriter, level, err = loggo.RemoveWriter("default") c.Assert(err, gc.ErrorMatches, `Writer "default" is not registered`) c.Assert(level, gc.Equals, loggo.UNSPECIFIED) c.Assert(defaultWriter, gc.IsNil) } func (*writerBasicsSuite) TestRegisterWriterExistingName(c *gc.C) { err := loggo.RegisterWriter("default", &loggo.TestWriter{}, loggo.INFO) c.Assert(err, gc.ErrorMatches, `there is already a Writer registered with the name "default"`) } func (*writerBasicsSuite) TestRegisterNilWriter(c *gc.C) { err := loggo.RegisterWriter("nil", nil, loggo.INFO) c.Assert(err, gc.ErrorMatches, `Writer cannot be nil`) } func (*writerBasicsSuite) TestRegisterWriterTypedNil(c *gc.C) { // If the interface is a typed nil, we have to trust the user. var writer *loggo.TestWriter err := loggo.RegisterWriter("nil", writer, loggo.INFO) c.Assert(err, gc.IsNil) } func (*writerBasicsSuite) TestReplaceDefaultWriter(c *gc.C) { oldWriter, err := loggo.ReplaceDefaultWriter(&loggo.TestWriter{}) c.Assert(oldWriter, gc.NotNil) c.Assert(err, gc.IsNil) } func (*writerBasicsSuite) TestReplaceDefaultWriterWithNil(c *gc.C) { oldWriter, err := loggo.ReplaceDefaultWriter(nil) c.Assert(oldWriter, gc.IsNil) c.Assert(err, gc.ErrorMatches, "Writer cannot be nil") } func (*writerBasicsSuite) TestReplaceDefaultWriterNoDefault(c *gc.C) { loggo.RemoveWriter("default") oldWriter, err := loggo.ReplaceDefaultWriter(&loggo.TestWriter{}) c.Assert(oldWriter, gc.IsNil) c.Assert(err, gc.ErrorMatches, `there is no "default" writer`) } func (s *writerBasicsSuite) TestWillWrite(c *gc.C) { // By default, the root logger watches TRACE messages c.Assert(loggo.WillWrite(loggo.TRACE), gc.Equals, true) // Note: ReplaceDefaultWriter doesn't let us change the default log // level :( writer, _, err := loggo.RemoveWriter("default") c.Assert(err, gc.IsNil) c.Assert(writer, gc.NotNil) err = loggo.RegisterWriter("default", writer, loggo.CRITICAL) c.Assert(err, gc.IsNil) c.Assert(loggo.WillWrite(loggo.TRACE), gc.Equals, false) c.Assert(loggo.WillWrite(loggo.DEBUG), gc.Equals, false) c.Assert(loggo.WillWrite(loggo.INFO), gc.Equals, false) c.Assert(loggo.WillWrite(loggo.WARNING), gc.Equals, false) c.Assert(loggo.WillWrite(loggo.CRITICAL), gc.Equals, true) } type writerSuite struct { logger loggo.Logger } var _ = gc.Suite(&writerSuite{}) func (s *writerSuite) SetUpTest(c *gc.C) { loggo.ResetLoggers() loggo.RemoveWriter("default") s.logger = loggo.GetLogger("test.writer") // Make it so the logger itself writes all messages. s.logger.SetLogLevel(loggo.TRACE) } func (s *writerSuite) TearDownTest(c *gc.C) { loggo.ResetWriters() } func (s *writerSuite) TearDownSuite(c *gc.C) { loggo.ResetLoggers() } func (s *writerSuite) TestWritingCapturesFileAndLineAndModule(c *gc.C) { writer := &loggo.TestWriter{} err := loggo.RegisterWriter("test", writer, loggo.INFO) c.Assert(err, gc.IsNil) s.logger.Infof("Info message") // WARNING: test checks the line number of the above logger lines, this // will mean that if the above line moves, the test will fail unless // updated. c.Assert(writer.Log, gc.HasLen, 1) c.Assert(writer.Log[0].Filename, gc.Equals, "writer_test.go") c.Assert(writer.Log[0].Line, gc.Equals, 113) c.Assert(writer.Log[0].Module, gc.Equals, "test.writer") } func (s *writerSuite) TestWritingLimitWarning(c *gc.C) { writer := &loggo.TestWriter{} err := loggo.RegisterWriter("test", writer, loggo.WARNING) c.Assert(err, gc.IsNil) start := time.Now() s.logger.Criticalf("Something critical.") s.logger.Errorf("An error.") s.logger.Warningf("A warning message") s.logger.Infof("Info message") s.logger.Tracef("Trace the function") end := time.Now() c.Assert(writer.Log, gc.HasLen, 3) c.Assert(writer.Log[0].Level, gc.Equals, loggo.CRITICAL) c.Assert(writer.Log[0].Message, gc.Equals, "Something critical.") c.Assert(writer.Log[0].Timestamp, Between(start, end)) c.Assert(writer.Log[1].Level, gc.Equals, loggo.ERROR) c.Assert(writer.Log[1].Message, gc.Equals, "An error.") c.Assert(writer.Log[1].Timestamp, Between(start, end)) c.Assert(writer.Log[2].Level, gc.Equals, loggo.WARNING) c.Assert(writer.Log[2].Message, gc.Equals, "A warning message") c.Assert(writer.Log[2].Timestamp, Between(start, end)) } func (s *writerSuite) TestWritingLimitTrace(c *gc.C) { writer := &loggo.TestWriter{} err := loggo.RegisterWriter("test", writer, loggo.TRACE) c.Assert(err, gc.IsNil) start := time.Now() s.logger.Criticalf("Something critical.") s.logger.Errorf("An error.") s.logger.Warningf("A warning message") s.logger.Infof("Info message") s.logger.Tracef("Trace the function") end := time.Now() c.Assert(writer.Log, gc.HasLen, 5) c.Assert(writer.Log[0].Level, gc.Equals, loggo.CRITICAL) c.Assert(writer.Log[0].Message, gc.Equals, "Something critical.") c.Assert(writer.Log[0].Timestamp, Between(start, end)) c.Assert(writer.Log[1].Level, gc.Equals, loggo.ERROR) c.Assert(writer.Log[1].Message, gc.Equals, "An error.") c.Assert(writer.Log[1].Timestamp, Between(start, end)) c.Assert(writer.Log[2].Level, gc.Equals, loggo.WARNING) c.Assert(writer.Log[2].Message, gc.Equals, "A warning message") c.Assert(writer.Log[2].Timestamp, Between(start, end)) c.Assert(writer.Log[3].Level, gc.Equals, loggo.INFO) c.Assert(writer.Log[3].Message, gc.Equals, "Info message") c.Assert(writer.Log[3].Timestamp, Between(start, end)) c.Assert(writer.Log[4].Level, gc.Equals, loggo.TRACE) c.Assert(writer.Log[4].Message, gc.Equals, "Trace the function") c.Assert(writer.Log[4].Timestamp, Between(start, end)) } func (s *writerSuite) TestMultipleWriters(c *gc.C) { errorWriter := &loggo.TestWriter{} err := loggo.RegisterWriter("error", errorWriter, loggo.ERROR) c.Assert(err, gc.IsNil) warningWriter := &loggo.TestWriter{} err = loggo.RegisterWriter("warning", warningWriter, loggo.WARNING) c.Assert(err, gc.IsNil) infoWriter := &loggo.TestWriter{} err = loggo.RegisterWriter("info", infoWriter, loggo.INFO) c.Assert(err, gc.IsNil) traceWriter := &loggo.TestWriter{} err = loggo.RegisterWriter("trace", traceWriter, loggo.TRACE) c.Assert(err, gc.IsNil) s.logger.Errorf("An error.") s.logger.Warningf("A warning message") s.logger.Infof("Info message") s.logger.Tracef("Trace the function") c.Assert(errorWriter.Log, gc.HasLen, 1) c.Assert(warningWriter.Log, gc.HasLen, 2) c.Assert(infoWriter.Log, gc.HasLen, 3) c.Assert(traceWriter.Log, gc.HasLen, 4) } func Between(start, end time.Time) gc.Checker { if end.Before(start) { return &betweenChecker{end, start} } return &betweenChecker{start, end} } type betweenChecker struct { start, end time.Time } func (checker *betweenChecker) Info() *gc.CheckerInfo { info := gc.CheckerInfo{ Name: "Between", Params: []string{"obtained"}, } return &info } func (checker *betweenChecker) Check(params []interface{}, names []string) (result bool, error string) { when, ok := params[0].(time.Time) if !ok { return false, "obtained value type must be time.Time" } if when.Before(checker.start) { return false, fmt.Sprintf("obtained value %#v type must before start value of %#v", when, checker.start) } if when.After(checker.end) { return false, fmt.Sprintf("obtained value %#v type must after end value of %#v", when, checker.end) } return true, "" } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/README.md������������������������������������������������0000644�0000153�0000161�00000002675�12321735652�021653� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������loggo - hierarchical loggers for Go =================================== This package provides an alternative to the standard library log package. The actual logging functions never return errors. If you are logging something, you really don't want to be worried about the logging having trouble. Modules have names that are defined by dotted strings. ``` "first.second.third" ``` There is a root module that has the name `""`. Each module (except the root module) has a parent, identified by the part of the name without the last dotted value. * the parent of `"first.second.third"` is `"first.second"` * the parent of `"first.second"` is `"first"` * the parent of `"first"` is `""` (the root module) Each module can specify its own severity level. Logging calls that are of a lower severity than the module's effective severity level are not written out. Loggers are created using the GetLogger function. ``` logger := loggo.GetLogger("foo.bar") ``` By default there is one writer registered, which will write to Stderr, and the root module, which will only emit warnings and above. Use the usual `Sprintf` syntax for: ``` logger.Criticalf(...) logger.Errorf(...) logger.Warningf(...) logger.Infof(...) logger.Debugf(...) logger.Tracef(...) ``` If in doubt, look at the tests. They are quite good at observing the expected behaviour. The loggers themselves are go-routine safe. The locking is on the output to the writers, and transparent to the caller. �������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/logger_test.go�������������������������������������������0000644�0000153�0000161�00000031777�12321736015�023240� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo_test import ( "io/ioutil" "os" "testing" gc "launchpad.net/gocheck" "github.com/juju/loggo" ) func Test(t *testing.T) { gc.TestingT(t) } type loggerSuite struct{} var _ = gc.Suite(&loggerSuite{}) func (*loggerSuite) SetUpTest(c *gc.C) { loggo.ResetLoggers() } func (*loggerSuite) TestRootLogger(c *gc.C) { root := loggo.Logger{} c.Assert(root.Name(), gc.Equals, "<root>") c.Assert(root.IsErrorEnabled(), gc.Equals, true) c.Assert(root.IsWarningEnabled(), gc.Equals, true) c.Assert(root.IsInfoEnabled(), gc.Equals, false) c.Assert(root.IsDebugEnabled(), gc.Equals, false) c.Assert(root.IsTraceEnabled(), gc.Equals, false) } func (*loggerSuite) TestModuleName(c *gc.C) { logger := loggo.GetLogger("loggo.testing") c.Assert(logger.Name(), gc.Equals, "loggo.testing") } func (*loggerSuite) TestSetLevel(c *gc.C) { logger := loggo.GetLogger("testing") c.Assert(logger.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.WARNING) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, true) c.Assert(logger.IsInfoEnabled(), gc.Equals, false) c.Assert(logger.IsDebugEnabled(), gc.Equals, false) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) logger.SetLogLevel(loggo.TRACE) c.Assert(logger.LogLevel(), gc.Equals, loggo.TRACE) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.TRACE) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, true) c.Assert(logger.IsInfoEnabled(), gc.Equals, true) c.Assert(logger.IsDebugEnabled(), gc.Equals, true) c.Assert(logger.IsTraceEnabled(), gc.Equals, true) logger.SetLogLevel(loggo.DEBUG) c.Assert(logger.LogLevel(), gc.Equals, loggo.DEBUG) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.DEBUG) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, true) c.Assert(logger.IsInfoEnabled(), gc.Equals, true) c.Assert(logger.IsDebugEnabled(), gc.Equals, true) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) logger.SetLogLevel(loggo.INFO) c.Assert(logger.LogLevel(), gc.Equals, loggo.INFO) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.INFO) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, true) c.Assert(logger.IsInfoEnabled(), gc.Equals, true) c.Assert(logger.IsDebugEnabled(), gc.Equals, false) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) logger.SetLogLevel(loggo.WARNING) c.Assert(logger.LogLevel(), gc.Equals, loggo.WARNING) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.WARNING) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, true) c.Assert(logger.IsInfoEnabled(), gc.Equals, false) c.Assert(logger.IsDebugEnabled(), gc.Equals, false) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) logger.SetLogLevel(loggo.ERROR) c.Assert(logger.LogLevel(), gc.Equals, loggo.ERROR) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, false) c.Assert(logger.IsInfoEnabled(), gc.Equals, false) c.Assert(logger.IsDebugEnabled(), gc.Equals, false) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) // This is added for completeness, but not really expected to be used. logger.SetLogLevel(loggo.CRITICAL) c.Assert(logger.LogLevel(), gc.Equals, loggo.CRITICAL) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.CRITICAL) c.Assert(logger.IsErrorEnabled(), gc.Equals, false) c.Assert(logger.IsWarningEnabled(), gc.Equals, false) c.Assert(logger.IsInfoEnabled(), gc.Equals, false) c.Assert(logger.IsDebugEnabled(), gc.Equals, false) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) logger.SetLogLevel(loggo.UNSPECIFIED) c.Assert(logger.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.WARNING) } func (*loggerSuite) TestLevelsSharedForSameModule(c *gc.C) { logger1 := loggo.GetLogger("testing.module") logger2 := loggo.GetLogger("testing.module") logger1.SetLogLevel(loggo.INFO) c.Assert(logger1.IsInfoEnabled(), gc.Equals, true) c.Assert(logger2.IsInfoEnabled(), gc.Equals, true) } func (*loggerSuite) TestModuleLowered(c *gc.C) { logger1 := loggo.GetLogger("TESTING.MODULE") logger2 := loggo.GetLogger("Testing") c.Assert(logger1.Name(), gc.Equals, "testing.module") c.Assert(logger2.Name(), gc.Equals, "testing") } func (*loggerSuite) TestLevelsInherited(c *gc.C) { root := loggo.GetLogger("") first := loggo.GetLogger("first") second := loggo.GetLogger("first.second") root.SetLogLevel(loggo.ERROR) c.Assert(root.LogLevel(), gc.Equals, loggo.ERROR) c.Assert(root.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(first.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(first.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(second.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(second.EffectiveLogLevel(), gc.Equals, loggo.ERROR) first.SetLogLevel(loggo.DEBUG) c.Assert(root.LogLevel(), gc.Equals, loggo.ERROR) c.Assert(root.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(first.LogLevel(), gc.Equals, loggo.DEBUG) c.Assert(first.EffectiveLogLevel(), gc.Equals, loggo.DEBUG) c.Assert(second.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(second.EffectiveLogLevel(), gc.Equals, loggo.DEBUG) second.SetLogLevel(loggo.INFO) c.Assert(root.LogLevel(), gc.Equals, loggo.ERROR) c.Assert(root.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(first.LogLevel(), gc.Equals, loggo.DEBUG) c.Assert(first.EffectiveLogLevel(), gc.Equals, loggo.DEBUG) c.Assert(second.LogLevel(), gc.Equals, loggo.INFO) c.Assert(second.EffectiveLogLevel(), gc.Equals, loggo.INFO) first.SetLogLevel(loggo.UNSPECIFIED) c.Assert(root.LogLevel(), gc.Equals, loggo.ERROR) c.Assert(root.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(first.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(first.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(second.LogLevel(), gc.Equals, loggo.INFO) c.Assert(second.EffectiveLogLevel(), gc.Equals, loggo.INFO) } var parseLevelTests = []struct { str string level loggo.Level fail bool }{{ str: "trace", level: loggo.TRACE, }, { str: "TrAce", level: loggo.TRACE, }, { str: "TRACE", level: loggo.TRACE, }, { str: "debug", level: loggo.DEBUG, }, { str: "DEBUG", level: loggo.DEBUG, }, { str: "info", level: loggo.INFO, }, { str: "INFO", level: loggo.INFO, }, { str: "warn", level: loggo.WARNING, }, { str: "WARN", level: loggo.WARNING, }, { str: "warning", level: loggo.WARNING, }, { str: "WARNING", level: loggo.WARNING, }, { str: "error", level: loggo.ERROR, }, { str: "ERROR", level: loggo.ERROR, }, { str: "critical", level: loggo.CRITICAL, }, { str: "not_specified", fail: true, }, { str: "other", fail: true, }, { str: "", fail: true, }} func (*loggerSuite) TestParseLevel(c *gc.C) { for _, test := range parseLevelTests { level, ok := loggo.ParseLevel(test.str) c.Assert(level, gc.Equals, test.level) c.Assert(ok, gc.Equals, !test.fail) } } var levelStringValueTests = map[loggo.Level]string{ loggo.UNSPECIFIED: "UNSPECIFIED", loggo.DEBUG: "DEBUG", loggo.TRACE: "TRACE", loggo.INFO: "INFO", loggo.WARNING: "WARNING", loggo.ERROR: "ERROR", loggo.CRITICAL: "CRITICAL", loggo.Level(42): "<unknown>", // other values are unknown } func (*loggerSuite) TestLevelStringValue(c *gc.C) { for level, str := range levelStringValueTests { c.Assert(level.String(), gc.Equals, str) } } var configureLoggersTests = []struct { spec string info string err string }{{ spec: "", info: "<root>=WARNING", }, { spec: "<root>=UNSPECIFIED", info: "<root>=WARNING", }, { spec: "<root>=DEBUG", info: "<root>=DEBUG", }, { spec: "test.module=debug", info: "<root>=WARNING;test.module=DEBUG", }, { spec: "module=info; sub.module=debug; other.module=warning", info: "<root>=WARNING;module=INFO;other.module=WARNING;sub.module=DEBUG", }, { spec: " foo.bar \t\r\n= \t\r\nCRITICAL \t\r\n; \t\r\nfoo \r\t\n = DEBUG", info: "<root>=WARNING;foo=DEBUG;foo.bar=CRITICAL", }, { spec: "foo;bar", info: "<root>=WARNING", err: `logger specification expected '=', found "foo"`, }, { spec: "=foo", info: "<root>=WARNING", err: `logger specification "=foo" has blank name or level`, }, { spec: "foo=", info: "<root>=WARNING", err: `logger specification "foo=" has blank name or level`, }, { spec: "=", info: "<root>=WARNING", err: `logger specification "=" has blank name or level`, }, { spec: "foo=unknown", info: "<root>=WARNING", err: `unknown severity level "unknown"`, }, { // Test that nothing is changed even when the // first part of the specification parses ok. spec: "module=info; foo=unknown", info: "<root>=WARNING", err: `unknown severity level "unknown"`, }} func (*loggerSuite) TestConfigureLoggers(c *gc.C) { for i, test := range configureLoggersTests { c.Logf("test %d: %q", i, test.spec) loggo.ResetLoggers() err := loggo.ConfigureLoggers(test.spec) c.Check(loggo.LoggerInfo(), gc.Equals, test.info) if test.err != "" { c.Assert(err, gc.ErrorMatches, test.err) continue } c.Assert(err, gc.IsNil) // Test that it's idempotent. err = loggo.ConfigureLoggers(test.spec) c.Assert(err, gc.IsNil) c.Assert(loggo.LoggerInfo(), gc.Equals, test.info) // Test that calling ConfigureLoggers with the // output of LoggerInfo works too. err = loggo.ConfigureLoggers(test.info) c.Assert(err, gc.IsNil) c.Assert(loggo.LoggerInfo(), gc.Equals, test.info) } } type logwriterSuite struct { logger loggo.Logger writer *loggo.TestWriter } var _ = gc.Suite(&logwriterSuite{}) func (s *logwriterSuite) SetUpTest(c *gc.C) { loggo.ResetLoggers() loggo.RemoveWriter("default") s.writer = &loggo.TestWriter{} err := loggo.RegisterWriter("test", s.writer, loggo.TRACE) c.Assert(err, gc.IsNil) s.logger = loggo.GetLogger("test.writer") // Make it so the logger itself writes all messages. s.logger.SetLogLevel(loggo.TRACE) } func (s *logwriterSuite) TearDownTest(c *gc.C) { loggo.ResetWriters() } func (s *logwriterSuite) TestLogDoesntLogWeirdLevels(c *gc.C) { s.logger.Logf(loggo.UNSPECIFIED, "message") c.Assert(s.writer.Log, gc.HasLen, 0) s.logger.Logf(loggo.Level(42), "message") c.Assert(s.writer.Log, gc.HasLen, 0) s.logger.Logf(loggo.CRITICAL+loggo.Level(1), "message") c.Assert(s.writer.Log, gc.HasLen, 0) } func (s *logwriterSuite) TestMessageFormatting(c *gc.C) { s.logger.Logf(loggo.INFO, "some %s included", "formatting") c.Assert(s.writer.Log, gc.HasLen, 1) c.Assert(s.writer.Log[0].Message, gc.Equals, "some formatting included") c.Assert(s.writer.Log[0].Level, gc.Equals, loggo.INFO) } func (s *logwriterSuite) BenchmarkLoggingNoWriters(c *gc.C) { // No writers loggo.RemoveWriter("test") for i := 0; i < c.N; i++ { s.logger.Warningf("just a simple warning for %d", i) } } func (s *logwriterSuite) BenchmarkLoggingNoWritersNoFormat(c *gc.C) { // No writers loggo.RemoveWriter("test") for i := 0; i < c.N; i++ { s.logger.Warningf("just a simple warning") } } func (s *logwriterSuite) BenchmarkLoggingTestWriters(c *gc.C) { for i := 0; i < c.N; i++ { s.logger.Warningf("just a simple warning for %d", i) } c.Assert(s.writer.Log, gc.HasLen, c.N) } func setupTempFileWriter(c *gc.C) (logFile *os.File, cleanup func()) { loggo.RemoveWriter("test") logFile, err := ioutil.TempFile("", "loggo-test") c.Assert(err, gc.IsNil) cleanup = func() { logFile.Close() os.Remove(logFile.Name()) } writer := loggo.NewSimpleWriter(logFile, &loggo.DefaultFormatter{}) err = loggo.RegisterWriter("testfile", writer, loggo.TRACE) c.Assert(err, gc.IsNil) return } func (s *logwriterSuite) BenchmarkLoggingDiskWriter(c *gc.C) { logFile, cleanup := setupTempFileWriter(c) defer cleanup() msg := "just a simple warning for %d" for i := 0; i < c.N; i++ { s.logger.Warningf(msg, i) } offset, err := logFile.Seek(0, os.SEEK_CUR) c.Assert(err, gc.IsNil) c.Assert((offset > int64(len(msg))*int64(c.N)), gc.Equals, true, gc.Commentf("Not enough data was written to the log file.")) } func (s *logwriterSuite) BenchmarkLoggingDiskWriterNoMessages(c *gc.C) { logFile, cleanup := setupTempFileWriter(c) defer cleanup() // Change the log level writer, _, err := loggo.RemoveWriter("testfile") c.Assert(err, gc.IsNil) loggo.RegisterWriter("testfile", writer, loggo.WARNING) msg := "just a simple warning for %d" for i := 0; i < c.N; i++ { s.logger.Debugf(msg, i) } offset, err := logFile.Seek(0, os.SEEK_CUR) c.Assert(err, gc.IsNil) c.Assert(offset, gc.Equals, int64(0), gc.Commentf("Data was written to the log file.")) } func (s *logwriterSuite) BenchmarkLoggingDiskWriterNoMessagesLogLevel(c *gc.C) { logFile, cleanup := setupTempFileWriter(c) defer cleanup() // Change the log level s.logger.SetLogLevel(loggo.WARNING) msg := "just a simple warning for %d" for i := 0; i < c.N; i++ { s.logger.Debugf(msg, i) } offset, err := logFile.Seek(0, os.SEEK_CUR) c.Assert(err, gc.IsNil) c.Assert(offset, gc.Equals, int64(0), gc.Commentf("Data was written to the log file.")) } �juju-core_1.18.1/src/github.com/juju/loggo/writer.go������������������������������������������������0000644�0000153�0000161�00000010406�12321735652�022226� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo import ( "fmt" "io" "os" "sync" "time" ) // Writer is implemented by any recipient of log messages. type Writer interface { // Write writes a message to the Writer with the given // level and module name. The filename and line hold // the file name and line number of the code that is // generating the log message; the time stamp holds // the time the log message was generated, and // message holds the log message itself. Write(level Level, name, filename string, line int, timestamp time.Time, message string) } type registeredWriter struct { writer Writer level Level } // defaultName is the name of a writer that is registered // by default that writes to stderr. const defaultName = "default" var ( writerMutex sync.Mutex writers = map[string]*registeredWriter{ defaultName: &registeredWriter{ writer: NewSimpleWriter(os.Stderr, &DefaultFormatter{}), level: TRACE, }, } globalMinLevel = TRACE ) // ResetWriters puts the list of writers back into the initial state. func ResetWriters() { writerMutex.Lock() defer writerMutex.Unlock() writers = map[string]*registeredWriter{ "default": &registeredWriter{ writer: NewSimpleWriter(os.Stderr, &DefaultFormatter{}), level: TRACE, }, } findMinLevel() } // ReplaceDefaultWriter is a convenience method that does the equivalent of // RemoveWriter and then RegisterWriter with the name "default". The previous // default writer, if any is returned. func ReplaceDefaultWriter(writer Writer) (Writer, error) { if writer == nil { return nil, fmt.Errorf("Writer cannot be nil") } writerMutex.Lock() defer writerMutex.Unlock() reg, found := writers[defaultName] if !found { return nil, fmt.Errorf("there is no %q writer", defaultName) } oldWriter := reg.writer reg.writer = writer return oldWriter, nil } // RegisterWriter adds the writer to the list of writers that get notified // when logging. When registering, the caller specifies the minimum logging // level that will be written, and a name for the writer. If there is already // a registered writer with that name, an error is returned. func RegisterWriter(name string, writer Writer, minLevel Level) error { if writer == nil { return fmt.Errorf("Writer cannot be nil") } writerMutex.Lock() defer writerMutex.Unlock() if _, found := writers[name]; found { return fmt.Errorf("there is already a Writer registered with the name %q", name) } writers[name] = &registeredWriter{writer: writer, level: minLevel} findMinLevel() return nil } // RemoveWriter removes the Writer identified by 'name' and returns it. // If the Writer is not found, an error is returned. func RemoveWriter(name string) (Writer, Level, error) { writerMutex.Lock() defer writerMutex.Unlock() registered, found := writers[name] if !found { return nil, UNSPECIFIED, fmt.Errorf("Writer %q is not registered", name) } delete(writers, name) findMinLevel() return registered.writer, registered.level, nil } func findMinLevel() { // We assume the lock is already held minLevel := CRITICAL for _, registered := range writers { if registered.level < minLevel { minLevel = registered.level } } globalMinLevel.set(minLevel) } // WillWrite returns whether there are any writers registered // at or above the given severity level. If it returns // false, a log message at the given level will be discarded. func WillWrite(level Level) bool { return level >= globalMinLevel.get() } func writeToWriters(level Level, module, filename string, line int, timestamp time.Time, message string) { writerMutex.Lock() defer writerMutex.Unlock() for _, registered := range writers { if level >= registered.level { registered.writer.Write(level, module, filename, line, timestamp, message) } } } type simpleWriter struct { writer io.Writer formatter Formatter } // NewSimpleWriter returns a new writer that writes // log messages to the given io.Writer formatting the // messages with the given formatter. func NewSimpleWriter(writer io.Writer, formatter Formatter) Writer { return &simpleWriter{writer, formatter} } func (simple *simpleWriter) Write(level Level, module, filename string, line int, timestamp time.Time, message string) { logLine := simple.formatter.Format(level, module, filename, line, timestamp, message) fmt.Fprintln(simple.writer, logLine) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/COPYING��������������������������������������������������0000644�0000153�0000161�00000104513�12321735652�021421� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/.gitignore�����������������������������������������������0000644�0000153�0000161�00000000020�12321735652�022342� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������example/example ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/example/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735652�022015� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/example/second.go����������������������������������������0000644�0000153�0000161�00000000630�12321735652�023616� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "github.com/juju/loggo" ) var second = loggo.GetLogger("second") func SecondCritical(message string) { second.Criticalf(message) } func SecondError(message string) { second.Errorf(message) } func SecondWarning(message string) { second.Warningf(message) } func SecondInfo(message string) { second.Infof(message) } func SecondTrace(message string) { second.Tracef(message) } ��������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/example/main.go������������������������������������������0000644�0000153�0000161�00000001404�12321735652�023267� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "fmt" "os" "github.com/juju/loggo" ) var logger = loggo.GetLogger("main") var rootLogger = loggo.GetLogger("") func main() { args := os.Args if len(args) > 1 { loggo.ConfigureLoggers(args[1]) } else { fmt.Println("Add a parameter to configure the logging:") fmt.Println("E.g. \"<root>=INFO;first=TRACE\"") } fmt.Println("\nCurrent logging levels:") fmt.Println(loggo.LoggerInfo()) fmt.Println("") rootLogger.Infof("Start of test.") FirstCritical("first critical") FirstError("first error") FirstWarning("first warning") FirstInfo("first info") FirstTrace("first trace") SecondCritical("first critical") SecondError("first error") SecondWarning("first warning") SecondInfo("first info") SecondTrace("first trace") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/juju/loggo/example/first.go�����������������������������������������0000644�0000153�0000161�00000000614�12321735652�023474� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "github.com/juju/loggo" ) var first = loggo.GetLogger("first") func FirstCritical(message string) { first.Criticalf(message) } func FirstError(message string) { first.Errorf(message) } func FirstWarning(message string) { first.Warningf(message) } func FirstInfo(message string) { first.Infof(message) } func FirstTrace(message string) { first.Tracef(message) } ��������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/errgo/��������������������������������������������������������������0000755�0000153�0000161�00000000000�12321735652�017414� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/errgo/errgo/��������������������������������������������������������0000755�0000153�0000161�00000000000�12321736015�020524� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/errgo/errgo/LICENSE�������������������������������������������������0000644�0000153�0000161�00000016720�12321735652�021545� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. ������������������������������������������������juju-core_1.18.1/src/github.com/errgo/errgo/test_functions_test.go����������������������������������0000644�0000153�0000161�00000001262�12321735652�025170� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package errgo // Functions in this file are used in the tests and the names of the functions // along with the file and line numbers are used, so please don't mess around // with them too much. import ( "errors" ) func one() error { return errors.New("one") } func two() error { return Annotate(one(), "two") } func three() error { return Annotate(two(), "three") } func transtwo() error { return Translate( one(), errors.New("translated"), "transtwo") } func transthree() error { return Translate( two(), errors.New("translated"), "transthree") } func four() error { return Annotate(transthree(), "four") } func test_new() error { return New("get location") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/errgo/errgo/errgo.go������������������������������������������������0000644�0000153�0000161�00000012715�12321736015�022177� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// errgo is a package that provides an easy way to annotate errors without // losing the orginal error context. package errgo import ( "errors" "fmt" "path/filepath" "runtime" "strings" ) type annotation struct { message string err error function string file string line int } type annotatedError struct { stack []annotation } var _ error = (*annotatedError)(nil) func errorString(annotations []annotation) string { parts := []string{} err := annotations[0].err var origin string for i, a := range annotations { if a.err != err { origin = fmt.Sprintf(" (%s)", errorString(annotations[i:])) break } if a.message != "" { parts = append(parts, a.message) } } message := strings.Join(parts, ", ") if message == "" { return fmt.Sprintf("%v%s", err, origin) } return fmt.Sprintf("%s: %v%s", message, err, origin) } // Error returns the annotation where the annotations are joined by commas, // and separated by the original error by a colon. If there are translated // errors in the stack, the Error representation of the previous annotations // are in brackets. func (a *annotatedError) Error() string { return errorString(a.stack) } func (a *annotatedError) addAnnotation(an annotation) *annotatedError { a.stack = append( []annotation{an}, a.stack...) return a } func partialPath(filename string, elements int) string { if filename == "" { return "" } if elements < 1 { return filename } base := filepath.Base(filename) if elements == 1 { return base } dir := filepath.Dir(filename) if dir != "." { dir = partialPath(dir, elements-1) } return filepath.Join(dir, base) } func newAnnotation(message string) annotation { result := annotation{message: message} pc, file, line, ok := runtime.Caller(3) if ok { result.file = file result.line = line result.function = runtime.FuncForPC(pc).Name() } return result } func annotate(err error, message string) error { a := newAnnotation(message) if annotated, ok := err.(*annotatedError); ok { a.err = annotated.stack[0].err return annotated.addAnnotation(a) } a.err = err return &annotatedError{[]annotation{a}} } func New(format string, args ...interface{}) error { return annotate(errors.New(fmt.Sprintf(format, args...)), "") } // Trace records the location of the Trace call, and adds it to the annotation // stack. func Trace(err error) error { return annotate(err, "") } // Annotate is used to add extra context to an existing error. The location of // the Annotate call is recorded with the annotations. The file, line and // function are also recorded. func Annotate(err error, message string) error { return annotate(err, message) } // Annotatef operates like Annotate, but uses the a format and args that match // the fmt package. func Annotatef(err error, format string, args ...interface{}) error { return annotate(err, fmt.Sprintf(format, args...)) } func translate(err, newError error, message string) error { a := newAnnotation(message) a.err = newError if annotated, ok := err.(*annotatedError); ok { return annotated.addAnnotation(a) } return &annotatedError{ []annotation{ a, {err: err}, }, } } // Translate records the newError along with the message in the annotation // stack. func Translate(err, newError error, message string) error { return translate(err, newError, message) } // Translatef operates like Translate, but uses the a format and args that // match the fmt package. func Translatef(err, newError error, format string, args ...interface{}) error { return translate(err, newError, fmt.Sprintf(format, args)) } // Check looks at the underling error to see if it matches the checker // function. func Check(err error, checker func(error) bool) bool { if annotated, ok := err.(*annotatedError); ok { return checker(annotated.stack[0].err) } return checker(err) } // GetErrorStack returns a slice of errors stored in the annotated errors. If // the error isn't an annotated error, a slice with a single value is // returned. func GetErrorStack(err error) []error { if annotated, ok := err.(*annotatedError); ok { result := []error{} var last error for _, a := range annotated.stack { if a.err != last { last = a.err result = append(result, last) } } return result } return []error{err} } // OutputParams are used to control the look of the DetailedErrorStack. type OutputParams struct { Prefix string FileDepth int } // Default is a simple pre-defined params for the DetailedErrorStack method // that has no prefix, and shows files to a depth of one. var Default = OutputParams{FileDepth: 2} // DetailedErrorStack gives a slice containing the detailed error stack, // annotation and original error, along with the location if it was recorded. func DetailedErrorStack(err error, params OutputParams) string { if annotated, ok := err.(*annotatedError); ok { result := []string{} size := len(annotated.stack) for i, a := range annotated.stack { errText := "" if i == (size-1) || a.err != annotated.stack[i+1].err { format := ": %v" if a.message == "" { format = "%v" } errText = fmt.Sprintf(format, a.err) } line := fmt.Sprintf( "%s%s%s%s", params.Prefix, a.message, errText, a.location(params.FileDepth)) result = append(result, line) } return strings.Join(result, "\n") } return err.Error() } func (a *annotation) location(depth int) string { if a.file != "" { return fmt.Sprintf(" [%s:%v, %s]", partialPath(a.file, depth), a.line, a.function) } return "" } ���������������������������������������������������juju-core_1.18.1/src/github.com/errgo/errgo/README.md�����������������������������������������������0000644�0000153�0000161�00000000124�12321735652�022006� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������errgo ===== A package for the Go language to provide extra information for errors. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/errgo/errgo/errgo_internal_test.go����������������������������������0000644�0000153�0000161�00000010230�12321736015�025120� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package errgo import ( "fmt" gc "launchpad.net/gocheck" ) type internalSuite struct{} var _ = gc.Suite(&internalSuite{}) func (*internalSuite) TestPartialPath(c *gc.C) { for i, test := range []struct { filename string count int expected string }{ { filename: "foo/bar/baz", count: -1, expected: "foo/bar/baz", }, { filename: "foo/bar/baz", count: 0, expected: "foo/bar/baz", }, { filename: "foo/bar/baz", count: 1, expected: "baz", }, { filename: "foo/bar/baz", count: 2, expected: "bar/baz", }, { filename: "foo/bar/baz", count: 5, expected: "foo/bar/baz", }, { filename: "", count: 2, expected: "", }, } { c.Logf("Test %v", i) c.Check(partialPath(test.filename, test.count), gc.Equals, test.expected) } } func (*internalSuite) TestInternalAnnotate(c *gc.C) { for i, test := range []struct { message string errFunc func() error expected []annotation }{ { message: "structure of a simple annotation", errFunc: two, expected: []annotation{ { message: "two", file: "errgo/test_functions_test.go", line: 16, function: "github.com/errgo/errgo.two", err: fmt.Errorf("one"), }, }, }, { message: "structure of a stacked annotation", errFunc: three, expected: []annotation{ { message: "three", file: "errgo/test_functions_test.go", line: 20, function: "github.com/errgo/errgo.three", err: fmt.Errorf("one"), }, { message: "two", file: "errgo/test_functions_test.go", line: 16, function: "github.com/errgo/errgo.two", err: fmt.Errorf("one"), }, }, }, { message: "structure of a simple translation", errFunc: transtwo, expected: []annotation{ { message: "transtwo", file: "errgo/test_functions_test.go", line: 27, function: "github.com/errgo/errgo.transtwo", err: fmt.Errorf("translated"), }, { err: fmt.Errorf("one"), }, }, }, { message: "structure of a simple annotation then translated", errFunc: transthree, expected: []annotation{ { message: "transthree", file: "errgo/test_functions_test.go", line: 34, function: "github.com/errgo/errgo.transthree", err: fmt.Errorf("translated"), }, { message: "two", file: "errgo/test_functions_test.go", line: 16, function: "github.com/errgo/errgo.two", err: fmt.Errorf("one"), }, }, }, { message: "structure of an annotationed, translated annotation", errFunc: four, expected: []annotation{ { message: "four", file: "errgo/test_functions_test.go", line: 38, function: "github.com/errgo/errgo.four", err: fmt.Errorf("translated"), }, { message: "transthree", file: "errgo/test_functions_test.go", line: 34, function: "github.com/errgo/errgo.transthree", err: fmt.Errorf("translated"), }, { message: "two", file: "errgo/test_functions_test.go", line: 16, function: "github.com/errgo/errgo.two", err: fmt.Errorf("one"), }, }, }, { message: "test new", errFunc: test_new, expected: []annotation{ { message: "", file: "errgo/test_functions_test.go", line: 42, function: "github.com/errgo/errgo.test_new", err: fmt.Errorf("get location"), }, }, }, } { c.Logf("%v: %s", i, test.message) err := test.errFunc() annotated, ok := err.(*annotatedError) c.Assert(ok, gc.Equals, true) c.Assert(annotated.stack, gc.HasLen, len(test.expected)) for i, annotation := range test.expected { c.Logf(" %v: %v", i, annotation) stacked := annotated.stack[i] c.Assert(stacked.message, gc.Equals, annotation.message) c.Assert(partialPath(stacked.file, 2), gc.Equals, annotation.file) c.Assert(stacked.function, gc.Equals, annotation.function) c.Assert(stacked.line, gc.Equals, annotation.line) // We use ErrorMatches here as we can't easily test identity. c.Assert(stacked.err, gc.ErrorMatches, fmt.Sprint(annotation.err)) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/errgo/errgo/errgo_test.go�������������������������������������������0000644�0000153�0000161�00000007740�12321736015�023240� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package errgo import ( "fmt" "os" "path/filepath" "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } type errgoSuite struct{} var _ = gc.Suite(&errgoSuite{}) func (*errgoSuite) TestErrorStringOneAnnotation(c *gc.C) { first := fmt.Errorf("first error") err := Annotate(first, "annotation") c.Assert(err, gc.ErrorMatches, `annotation: first error`) } func (*errgoSuite) TestErrorStringTwoAnnotations(c *gc.C) { first := fmt.Errorf("first error") err := Annotate(first, "annotation") err = Annotate(err, "another") c.Assert(err, gc.ErrorMatches, `another, annotation: first error`) } func (*errgoSuite) TestErrorStringThreeAnnotations(c *gc.C) { first := fmt.Errorf("first error") err := Annotate(first, "annotation") err = Annotate(err, "another") err = Annotate(err, "third") c.Assert(err, gc.ErrorMatches, `third, another, annotation: first error`) } func (*errgoSuite) TestExampleAnnotations(c *gc.C) { for _, test := range []struct { err error expected string }{ { err: two(), expected: "two: one", }, { err: three(), expected: "three, two: one", }, { err: transtwo(), expected: "transtwo: translated (one)", }, { err: transthree(), expected: "transthree: translated (two: one)", }, { err: four(), expected: "four, transthree: translated (two: one)", }, } { c.Assert(test.err.Error(), gc.Equals, test.expected) } } func (*errgoSuite) TestAnnotatedErrorCheck(c *gc.C) { // Look for a file that we know isn't there. dir := c.MkDir() _, err := os.Stat(filepath.Join(dir, "not-there")) c.Assert(os.IsNotExist(err), gc.Equals, true) c.Assert(Check(err, os.IsNotExist), gc.Equals, true) err = Annotate(err, "wrap it") // Now the error itself isn't a 'IsNotExist'. c.Assert(os.IsNotExist(err), gc.Equals, false) // However if we use the Check method, it is. c.Assert(Check(err, os.IsNotExist), gc.Equals, true) } func (*errgoSuite) TestGetErrorStack(c *gc.C) { for _, test := range []struct { err error matches []string }{ { err: fmt.Errorf("testing"), matches: []string{"testing"}, }, { err: Annotate(fmt.Errorf("testing"), "annotation"), matches: []string{"testing"}, }, { err: one(), matches: []string{"one"}, }, { err: two(), matches: []string{"one"}, }, { err: three(), matches: []string{"one"}, }, { err: transtwo(), matches: []string{"translated", "one"}, }, { err: transthree(), matches: []string{"translated", "one"}, }, { err: four(), matches: []string{"translated", "one"}, }, } { stack := GetErrorStack(test.err) c.Assert(stack, gc.HasLen, len(test.matches)) for i, match := range test.matches { c.Assert(stack[i], gc.ErrorMatches, match) } } } func (*errgoSuite) TestDetailedErrorStack(c *gc.C) { for _, test := range []struct { err error expected string }{ { err: one(), expected: "one", }, { err: two(), expected: "two: one [errgo/test_functions_test.go:16, github.com/errgo/errgo.two]", }, { err: three(), expected: "three [errgo/test_functions_test.go:20, github.com/errgo/errgo.three]\n" + "two: one [errgo/test_functions_test.go:16, github.com/errgo/errgo.two]", }, { err: transtwo(), expected: "transtwo: translated [errgo/test_functions_test.go:27, github.com/errgo/errgo.transtwo]\n" + "one", }, { err: transthree(), expected: "transthree: translated [errgo/test_functions_test.go:34, github.com/errgo/errgo.transthree]\n" + "two: one [errgo/test_functions_test.go:16, github.com/errgo/errgo.two]", }, { err: four(), expected: "four [errgo/test_functions_test.go:38, github.com/errgo/errgo.four]\n" + "transthree: translated [errgo/test_functions_test.go:34, github.com/errgo/errgo.transthree]\n" + "two: one [errgo/test_functions_test.go:16, github.com/errgo/errgo.two]", }, } { stack := DetailedErrorStack(test.err, Default) c.Check(stack, gc.DeepEquals, test.expected) } } ��������������������������������juju-core_1.18.1/src/github.com/errgo/errgo/.gitignore����������������������������������������������0000644�0000153�0000161�00000000374�12321735652�022526� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/�������������������������������������������������������������0000755�0000153�0000161�00000000000�12321735720�017602� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/����������������������������������������������������0000755�0000153�0000161�00000000000�12321735717�021426� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/gocommon_test.go������������������������������������0000644�0000153�0000161�00000000543�12321735717�024634� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gocommon - Go library to interact with the JoyentCloud // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package gocommon import ( gc "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { gc.TestingT(t) } type GoCommonTestSuite struct { } var _ = gc.Suite(&GoCommonTestSuite{}) �������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/COPYING.LESSER��������������������������������������0000644�0000153�0000161�00000016743�12321735717�023470� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �����������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/jpc/������������������������������������������������0000755�0000153�0000161�00000000000�12321735717�022202� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/jpc/jpc.go������������������������������������������0000644�0000153�0000161�00000004643�12321735717�023314� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gocommon - Go library to interact with the JoyentCloud // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package jpc import ( "fmt" "io/ioutil" "os" "reflect" "runtime" "github.com/joyent/gosign/auth" ) const ( // Environment variables SdcAccount = "SDC_ACCOUNT" SdcKeyId = "SDC_KEY_ID" SdcUrl = "SDC_URL" MantaUser = "MANTA_USER" MantaKeyId = "MANTA_KEY_ID" MantaUrl = "MANTA_URL" ) var Locations = map[string]string{ "us-east-1": "America/New_York", "us-west-1": "America/Los_Angeles", "us-sw-1": "America/Los_Angeles", "eu-ams-1": "Europe/Amsterdam", } // getConfig returns the value of the first available environment // variable, among the given ones. func getConfig(envVars ...string) (value string) { value = "" for _, v := range envVars { value = os.Getenv(v) if value != "" { break } } return } // getUserHome returns the value of HOME environment // variable for the user environment. func getUserHome() string { if runtime.GOOS == "windows" { return os.Getenv("APPDATA") } else { return os.Getenv("HOME") } } // credentialsFromEnv creates and initializes the credentials from the // environment variables. func credentialsFromEnv(key string) (*auth.Credentials, error) { var keyName string if key == "" { keyName = getUserHome() + "/.ssh/id_rsa" } else { keyName = key } privateKey, err := ioutil.ReadFile(keyName) if err != nil { return nil, err } authentication := auth.Auth{User: getConfig(SdcAccount, MantaUser), PrivateKey: string(privateKey), Algorithm: "rsa-sha256"} return &auth.Credentials{ UserAuthentication: authentication, SdcKeyId: getConfig(SdcKeyId), SdcEndpoint: auth.Endpoint{URL: getConfig(SdcUrl)}, MantaKeyId: getConfig(MantaKeyId), MantaEndpoint: auth.Endpoint{URL: getConfig(MantaUrl)}, }, nil } // CompleteCredentialsFromEnv gets and verifies all the required // authentication parameters have values in the environment. func CompleteCredentialsFromEnv(keyName string) (cred *auth.Credentials, err error) { cred, err = credentialsFromEnv(keyName) if err != nil { return nil, err } v := reflect.ValueOf(cred).Elem() t := v.Type() for i := 0; i < v.NumField(); i++ { f := v.Field(i) if f.String() == "" { return nil, fmt.Errorf("Required environment variable not set for credentials attribute: %s", t.Field(i).Name) } } return cred, nil } ���������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/LICENSE���������������������������������������������0000644�0000153�0000161�00000001266�12321735717�022440� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������GoCommon - Go Common Library for the Joyent Public Cloud and Joyent Manta Copyright (c) 2013, Joyent Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. See both COPYING and COPYING.LESSER for the full terms of the GNU Lesser General Public License. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/version_test.go�������������������������������������0000644�0000153�0000161�00000000632�12321735717�024502� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gocommon - Go library to interact with the JoyentCloud // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package gocommon import ( gc "launchpad.net/gocheck" ) type VersionTestSuite struct { } var _ = gc.Suite(&VersionTestSuite{}) func (s *VersionTestSuite) TestStringMatches(c *gc.C) { c.Assert(Version, gc.Equals, VersionNumber.String()) } ������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/version.go������������������������������������������0000644�0000153�0000161�00000000727�12321735717�023450� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gocommon - Go library to interact with the JoyentCloud // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package gocommon import ( "fmt" ) type VersionNum struct { Major int Minor int Micro int } func (v *VersionNum) String() string { return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Micro) } var VersionNumber = VersionNum{ Major: 0, Minor: 1, Micro: 0, } var Version = VersionNumber.String() �����������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/client/���������������������������������������������0000755�0000153�0000161�00000000000�12321735717�022704� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/client/client_test.go�������������������������������0000644�0000153�0000161�00000003123�12321735717�025547� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gocommon - Go library to interact with the JoyentCloud // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package client_test import ( "flag" "fmt" "github.com/joyent/gocommon/client" joyenthttp "github.com/joyent/gocommon/http" "github.com/joyent/gocommon/jpc" "github.com/joyent/gosign/auth" gc "launchpad.net/gocheck" "net/http" "testing" "time" ) type ClientSuite struct { creds *auth.Credentials } var keyName = flag.String("key.name", "", "Specify the full path to the private key, defaults to ~/.ssh/id_rsa") func Test(t *testing.T) { creds, err := jpc.CompleteCredentialsFromEnv(*keyName) if err != nil { t.Fatalf("Error setting up test suite: %v", err) } gc.Suite(&ClientSuite{creds: creds}) gc.TestingT(t) } func (s *ClientSuite) TestNewClient(c *gc.C) { cl := client.NewClient(s.creds.SdcEndpoint.URL, "", s.creds, nil) c.Assert(cl, gc.NotNil) } func (s *ClientSuite) TestSendRequest(c *gc.C) { cl := client.NewClient(s.creds.SdcEndpoint.URL, "", s.creds, nil) c.Assert(cl, gc.NotNil) req := joyenthttp.RequestData{} resp := joyenthttp.ResponseData{ExpectedStatus: []int{http.StatusOK}} err := cl.SendRequest(client.GET, "", "", &req, &resp) c.Assert(err, gc.IsNil) } func (s *ClientSuite) TestSignURL(c *gc.C) { cl := client.NewClient(s.creds.MantaEndpoint.URL, "", s.creds, nil) c.Assert(cl, gc.NotNil) path := fmt.Sprintf("/%s/stor", s.creds.UserAuthentication.User) singedUrl, err := cl.SignURL(path, time.Now().Add(time.Minute*5)) c.Assert(err, gc.IsNil) c.Assert(singedUrl, gc.Not(gc.Equals), "") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/client/client.go������������������������������������0000644�0000153�0000161�00000006747�12321735717�024527� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gocommon - Go library to interact with the JoyentCloud // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package client import ( "fmt" "net/url" "strings" "sync" "time" "github.com/juju/loggo" joyenthttp "github.com/joyent/gocommon/http" "github.com/joyent/gosign/auth" ) const ( // The HTTP request methods. GET = "GET" POST = "POST" PUT = "PUT" DELETE = "DELETE" HEAD = "HEAD" COPY = "COPY" ) // Client implementations sends service requests to the JoyentCloud. type Client interface { SendRequest(method, apiCall, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error) // MakeServiceURL prepares a full URL to a service endpoint, with optional // URL parts. It uses the first endpoint it can find for the given service type. MakeServiceURL(parts []string) string SignURL(path string, expires time.Time) (string, error) } // This client sends requests without authenticating. type client struct { mu sync.Mutex logger *loggo.Logger baseURL string creds *auth.Credentials httpClient *joyenthttp.Client } var _ Client = (*client)(nil) func newClient(baseURL string, credentials *auth.Credentials, httpClient *joyenthttp.Client, logger *loggo.Logger) Client { client := client{baseURL: baseURL, logger: logger, creds: credentials, httpClient: httpClient} return &client } func NewClient(baseURL, apiVersion string, credentials *auth.Credentials, logger *loggo.Logger) Client { sharedHttpClient := joyenthttp.New(credentials, apiVersion, logger) return newClient(baseURL, credentials, sharedHttpClient, logger) } func (c *client) sendRequest(method, url, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error) { if request.ReqValue != nil || response.RespValue != nil { err = c.httpClient.JsonRequest(method, url, rfc1123Date, request, response) } else { err = c.httpClient.BinaryRequest(method, url, rfc1123Date, request, response) } return } func (c *client) SendRequest(method, apiCall, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error) { url := c.MakeServiceURL([]string{c.creds.UserAuthentication.User, apiCall}) err = c.sendRequest(method, url, rfc1123Date, request, response) return } func makeURL(base string, parts []string) string { if !strings.HasSuffix(base, "/") && len(parts) > 0 { base += "/" } if parts[1] == "" { return base + parts[0] } return base + strings.Join(parts, "/") } func (c *client) MakeServiceURL(parts []string) string { return makeURL(c.baseURL, parts) } func (c *client) SignURL(path string, expires time.Time) (string, error) { parsedURL, err := url.Parse(c.baseURL) if err != nil { return "", fmt.Errorf("bad Manta endpoint URL %q: %v", c.baseURL, err) } userAuthentication := c.creds.UserAuthentication userAuthentication.Algorithm = "RSA-SHA1" keyId := url.QueryEscape(fmt.Sprintf("/%s/keys/%s", userAuthentication.User, c.creds.MantaKeyId)) params := fmt.Sprintf("algorithm=%s&expires=%d&keyId=%s", userAuthentication.Algorithm, expires.Unix(), keyId) signingLine := fmt.Sprintf("GET\n%s\n%s\n%s", parsedURL.Host, path, params) signature, err := auth.GetSignature(&userAuthentication, signingLine) if err != nil { return "", fmt.Errorf("cannot generate URL signature: %v", err) } signedURL := fmt.Sprintf("%s%s?%s&signature=%s", c.baseURL, path, params, url.QueryEscape(signature)) return signedURL, nil } �������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/README.md�������������������������������������������0000644�0000153�0000161�00000000022�12321735717�022677� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gocommon ======== ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/errors/���������������������������������������������0000755�0000153�0000161�00000000000�12321735717�022742� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/errors/errors_test.go�������������������������������0000644�0000153�0000161�00000020716�12321735717�025652� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gocommon - Go library to interact with the JoyentCloud // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package errors_test import ( "github.com/joyent/gocommon/errors" gc "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { gc.TestingT(t) } type ErrorsSuite struct { } var _ = gc.Suite(&ErrorsSuite{}) func (s *ErrorsSuite) TestCreateSimpleBadRequestError(c *gc.C) { context := "context" err := errors.NewBadRequestf(nil, context, "") c.Assert(errors.IsBadRequest(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Bad Request: context") } func (s *ErrorsSuite) TestCreateBadRequestError(c *gc.C) { context := "context" err := errors.NewBadRequestf(nil, context, "It was bad request: %s", context) c.Assert(errors.IsBadRequest(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was bad request: context") } func (s *ErrorsSuite) TestCreateSimpleInternalErrorError(c *gc.C) { context := "context" err := errors.NewInternalErrorf(nil, context, "") c.Assert(errors.IsInternalError(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Internal Error: context") } func (s *ErrorsSuite) TestCreateInternalErrorError(c *gc.C) { context := "context" err := errors.NewInternalErrorf(nil, context, "It was internal error: %s", context) c.Assert(errors.IsInternalError(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was internal error: context") } func (s *ErrorsSuite) TestCreateSimpleInvalidArgumentError(c *gc.C) { context := "context" err := errors.NewInvalidArgumentf(nil, context, "") c.Assert(errors.IsInvalidArgument(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Invalid Argument: context") } func (s *ErrorsSuite) TestCreateInvalidArgumentError(c *gc.C) { context := "context" err := errors.NewInvalidArgumentf(nil, context, "It was invalid argument: %s", context) c.Assert(errors.IsInvalidArgument(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was invalid argument: context") } func (s *ErrorsSuite) TestCreateSimpleInvalidCredentialsError(c *gc.C) { context := "context" err := errors.NewInvalidCredentialsf(nil, context, "") c.Assert(errors.IsInvalidCredentials(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Invalid Credentials: context") } func (s *ErrorsSuite) TestCreateInvalidCredentialsError(c *gc.C) { context := "context" err := errors.NewInvalidCredentialsf(nil, context, "It was invalid credentials: %s", context) c.Assert(errors.IsInvalidCredentials(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was invalid credentials: context") } func (s *ErrorsSuite) TestCreateSimpleInvalidHeaderError(c *gc.C) { context := "context" err := errors.NewInvalidHeaderf(nil, context, "") c.Assert(errors.IsInvalidHeader(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Invalid Header: context") } func (s *ErrorsSuite) TestCreateInvalidHeaderError(c *gc.C) { context := "context" err := errors.NewInvalidHeaderf(nil, context, "It was invalid header: %s", context) c.Assert(errors.IsInvalidHeader(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was invalid header: context") } func (s *ErrorsSuite) TestCreateSimpleInvalidVersionError(c *gc.C) { context := "context" err := errors.NewInvalidVersionf(nil, context, "") c.Assert(errors.IsInvalidVersion(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Invalid Version: context") } func (s *ErrorsSuite) TestCreateInvalidVersionError(c *gc.C) { context := "context" err := errors.NewInvalidVersionf(nil, context, "It was invalid version: %s", context) c.Assert(errors.IsInvalidVersion(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was invalid version: context") } func (s *ErrorsSuite) TestCreateSimpleMissingParameterError(c *gc.C) { context := "context" err := errors.NewMissingParameterf(nil, context, "") c.Assert(errors.IsMissingParameter(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Missing Parameter: context") } func (s *ErrorsSuite) TestCreateMissingParameterError(c *gc.C) { context := "context" err := errors.NewMissingParameterf(nil, context, "It was missing parameter: %s", context) c.Assert(errors.IsMissingParameter(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was missing parameter: context") } func (s *ErrorsSuite) TestCreateSimpleNotAuthorizedError(c *gc.C) { context := "context" err := errors.NewNotAuthorizedf(nil, context, "") c.Assert(errors.IsNotAuthorized(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Not Authorized: context") } func (s *ErrorsSuite) TestCreateNotAuthorizedError(c *gc.C) { context := "context" err := errors.NewNotAuthorizedf(nil, context, "It was not authorized: %s", context) c.Assert(errors.IsNotAuthorized(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was not authorized: context") } func (s *ErrorsSuite) TestCreateSimpleRequestThrottledError(c *gc.C) { context := "context" err := errors.NewRequestThrottledf(nil, context, "") c.Assert(errors.IsRequestThrottled(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Request Throttled: context") } func (s *ErrorsSuite) TestCreateRequestThrottledError(c *gc.C) { context := "context" err := errors.NewRequestThrottledf(nil, context, "It was request throttled: %s", context) c.Assert(errors.IsRequestThrottled(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was request throttled: context") } func (s *ErrorsSuite) TestCreateSimpleRequestTooLargeError(c *gc.C) { context := "context" err := errors.NewRequestTooLargef(nil, context, "") c.Assert(errors.IsRequestTooLarge(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Request Too Large: context") } func (s *ErrorsSuite) TestCreateRequestTooLargeError(c *gc.C) { context := "context" err := errors.NewRequestTooLargef(nil, context, "It was request too large: %s", context) c.Assert(errors.IsRequestTooLarge(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was request too large: context") } func (s *ErrorsSuite) TestCreateSimpleRequestMovedError(c *gc.C) { context := "context" err := errors.NewRequestMovedf(nil, context, "") c.Assert(errors.IsRequestMoved(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Request Moved: context") } func (s *ErrorsSuite) TestCreateRequestMovedError(c *gc.C) { context := "context" err := errors.NewRequestMovedf(nil, context, "It was request moved: %s", context) c.Assert(errors.IsRequestMoved(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was request moved: context") } func (s *ErrorsSuite) TestCreateSimpleResourceNotFoundError(c *gc.C) { context := "context" err := errors.NewResourceNotFoundf(nil, context, "") c.Assert(errors.IsResourceNotFound(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Resource Not Found: context") } func (s *ErrorsSuite) TestCreateResourceNotFoundError(c *gc.C) { context := "context" err := errors.NewResourceNotFoundf(nil, context, "It was resource not found: %s", context) c.Assert(errors.IsResourceNotFound(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was resource not found: context") } func (s *ErrorsSuite) TestCreateSimpleUnknownErrorError(c *gc.C) { context := "context" err := errors.NewUnknownErrorf(nil, context, "") c.Assert(errors.IsUnknownError(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "Unknown Error: context") } func (s *ErrorsSuite) TestCreateUnknownErrorError(c *gc.C) { context := "context" err := errors.NewUnknownErrorf(nil, context, "It was unknown error: %s", context) c.Assert(errors.IsUnknownError(err), gc.Equals, true) c.Assert(err.Error(), gc.Equals, "It was unknown error: context") } func (s *ErrorsSuite) TestErrorCause(c *gc.C) { rootCause := errors.NewResourceNotFoundf(nil, "some value", "") // Construct a new error, based on a resource not found root cause. err := errors.Newf(rootCause, "an error occurred") c.Assert(err.Cause(), gc.Equals, rootCause) // Check the other error attributes. c.Assert(err.Error(), gc.Equals, "an error occurred\ncaused by: Resource Not Found: some value") } func (s *ErrorsSuite) TestErrorIsType(c *gc.C) { rootCause := errors.NewBadRequestf(nil, "some value", "") // Construct a new error, based on a bad request root cause. err := errors.Newf(rootCause, "an error occurred") // Check that the error is not falsely identified as something it is not. c.Assert(errors.IsNotAuthorized(err), gc.Equals, false) // Check that the error is correctly identified as a not found error. c.Assert(errors.IsBadRequest(err), gc.Equals, true) } ��������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/errors/errors.go������������������������������������0000644�0000153�0000161�00000020772�12321735717�024615� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gocommon - Go library to interact with the JoyentCloud // This package provides an Error implementation which knows about types of error, and which has support // for error causes. // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package errors import "fmt" type Code string const ( // Public available error types. // These errors are provided because they are specifically required by business logic in the callers. BadRequestError = Code("BadRequest") InternalErrorError = Code("InternalError") InvalidArgumentError = Code("InvalidArgument") InvalidCredentialsError = Code("InvalidCredentials") InvalidHeaderError = Code("InvalidHeader") InvalidVersionError = Code("InvalidVersion") MissingParameterError = Code("MissinParameter") NotAuthorizedError = Code("NotAuthorized") RequestThrottledError = Code("RequestThrottled") RequestTooLargeError = Code("RequestTooLarge") RequestMovedError = Code("RequestMoved") ResourceNotFoundError = Code("ResourceNotFound") UnknownErrorError = Code("UnkownError") ) // Error instances store an optional error cause. type Error interface { error Cause() error } type gojoyentError struct { error errcode Code cause error } // Type checks. var _ Error = (*gojoyentError)(nil) // Code returns the error code. func (err *gojoyentError) code() Code { if err.errcode != UnknownErrorError { return err.errcode } if e, ok := err.cause.(*gojoyentError); ok { return e.code() } return UnknownErrorError } // Cause returns the error cause. func (err *gojoyentError) Cause() error { return err.cause } // CausedBy returns true if this error or its cause are of the specified error code. func (err *gojoyentError) causedBy(code Code) bool { if err.code() == code { return true } if cause, ok := err.cause.(*gojoyentError); ok { return cause.code() == code } return false } // Error fulfills the error interface, taking account of any caused by error. func (err *gojoyentError) Error() string { if err.cause != nil { return fmt.Sprintf("%v\ncaused by: %v", err.error, err.cause) } return err.error.Error() } func IsBadRequest(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(BadRequestError) } return false } func IsInternalError(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(InternalErrorError) } return false } func IsInvalidArgument(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(InvalidArgumentError) } return false } func IsInvalidCredentials(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(InvalidCredentialsError) } return false } func IsInvalidHeader(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(InvalidHeaderError) } return false } func IsInvalidVersion(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(InvalidVersionError) } return false } func IsMissingParameter(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(MissingParameterError) } return false } func IsNotAuthorized(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(NotAuthorizedError) } return false } func IsRequestThrottled(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(RequestThrottledError) } return false } func IsRequestTooLarge(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(RequestTooLargeError) } return false } func IsRequestMoved(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(RequestMovedError) } return false } func IsResourceNotFound(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(ResourceNotFoundError) } return false } func IsUnknownError(err error) bool { if e, ok := err.(*gojoyentError); ok { return e.causedBy(UnknownErrorError) } return false } // New creates a new Error instance with the specified cause. func makeErrorf(code Code, cause error, format string, args ...interface{}) Error { return &gojoyentError{ errcode: code, error: fmt.Errorf(format, args...), cause: cause, } } // New creates a new UnknownError Error instance with the specified cause. func Newf(cause error, format string, args ...interface{}) Error { return makeErrorf(UnknownErrorError, cause, format, args...) } // New creates a new BadRequest Error instance with the specified cause. func NewBadRequestf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Bad Request: %s", context) } return makeErrorf(BadRequestError, cause, format, args...) } // New creates a new InternalError Error instance with the specified cause. func NewInternalErrorf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Internal Error: %s", context) } return makeErrorf(InternalErrorError, cause, format, args...) } // New creates a new InvalidArgument Error instance with the specified cause. func NewInvalidArgumentf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Invalid Argument: %s", context) } return makeErrorf(InvalidArgumentError, cause, format, args...) } // New creates a new InvalidCredentials Error instance with the specified cause. func NewInvalidCredentialsf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Invalid Credentials: %s", context) } return makeErrorf(InvalidCredentialsError, cause, format, args...) } // New creates a new InvalidHeader Error instance with the specified cause. func NewInvalidHeaderf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Invalid Header: %s", context) } return makeErrorf(InvalidHeaderError, cause, format, args...) } // New creates a new InvalidVersion Error instance with the specified cause. func NewInvalidVersionf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Invalid Version: %s", context) } return makeErrorf(InvalidVersionError, cause, format, args...) } // New creates a new MissingParameter Error instance with the specified cause. func NewMissingParameterf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Missing Parameter: %s", context) } return makeErrorf(MissingParameterError, cause, format, args...) } // New creates a new NotAuthorized Error instance with the specified cause. func NewNotAuthorizedf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Not Authorized: %s", context) } return makeErrorf(NotAuthorizedError, cause, format, args...) } // New creates a new RequestThrottled Error instance with the specified cause. func NewRequestThrottledf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Request Throttled: %s", context) } return makeErrorf(RequestThrottledError, cause, format, args...) } // New creates a new RequestTooLarge Error instance with the specified cause. func NewRequestTooLargef(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Request Too Large: %s", context) } return makeErrorf(RequestTooLargeError, cause, format, args...) } // New creates a new RequestMoved Error instance with the specified cause. func NewRequestMovedf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Request Moved: %s", context) } return makeErrorf(RequestMovedError, cause, format, args...) } // New creates a new ResourceNotFound Error instance with the specified cause. func NewResourceNotFoundf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Resource Not Found: %s", context) } return makeErrorf(ResourceNotFoundError, cause, format, args...) } // New creates a new UnknownError Error instance with the specified cause. func NewUnknownErrorf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Unknown Error: %s", context) } return makeErrorf(UnknownErrorError, cause, format, args...) } ������juju-core_1.18.1/src/github.com/joyent/gocommon/http/�����������������������������������������������0000755�0000153�0000161�00000000000�12321735717�022405� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/http/client_test.go���������������������������������0000644�0000153�0000161�00000020105�12321735717�025247� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package http import ( "bytes" "fmt" "io/ioutil" gc "launchpad.net/gocheck" "net/http" "testing" httpsuite "github.com/joyent/gocommon/testing" "github.com/joyent/gosign/auth" ) const ( Signature = "yK0J17CQ04ZvMsFLoH163Sjyg8tE4BoIeCsmKWLQKN3BYgSpR0XyqrecheQ2A0o4L99oSumYSKIscBSiH5rqdf4/1zC/FEkYOI2UzcIHYb1MPNzO3g/5X44TppYE+8dxoH99V+Ts8RT3ZurEYjQ8wmK0TnxdirAevSpbypZJaBOFXUZSxx80m5BD4QE/MSGo/eaVdJI/Iw+nardHNvimVCr6pRNycX1I4FdyRR6kgrAl2NkY2yxx/CAY21Ir+dmbG3A1x4GiIE485LLheAL5/toPo7Gh8G5fkrF9dXWVyX0k9AZXqXNWn5AZxc32dKL2enH09j/X86RtwiR1IEuPww==" key = `-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAyLOtVh8qXjdwfjZZYwkEgg1yoSzmpKKpmzYW745lBGtPH87F spHVHeqjmgFnBsARsD7CHzYyQTho7oLrAEbuF7tKdGRK25wJIenPKKuL+UVwZNeJ VEXSiMNmX3Y4IqRteqRIjhw3DmXYHEWvBc2JVy8lWtyK+o6o8jlO0aRTTT2+dETp yqKqNJyHVNz2u6XVtm7jqyLU7tAqW+qpr5zSoNmuUAyz6JDCRnlWvwp1qzuS1LV3 2HK9yfq8TGriDVPyPRpFRmiRGWGIrIKrmm4sImpoLfuVBITjeh8V3Ee0OCDmTLgY lTHAmCLFJxaW5Y8b4cTt5pbT7R1iu77RKJo3fwIBIwKCAQEAmtPAOx9bMr0NozE9 pCuHIn9nDp77EUpIUym57AAiCrkuaP6YgnB/1T/6jL9A2VJWycoDdynO/xzjO6bS iy9nNuDwSyjMCH+vRgwjdyVAGBD+7rTmSFMewUZHqLpIj8CsOgm0UF7opLT3K8C6 N60vbyRepS3KTEIqjvkCSfPLO5Sp38KZYXKg0/Abb21WDSEzWonjV8JfOyMfUYhh 7QCE+Nf8s3b+vxskOuCQq1WHoqo8CqXrMYVknkvnQuRFPuaOLEjMbJVqlTb9ns2V SKxmo46R7fl2dKgMBll+Nec+3Dn2/Iq/qnHq34HF/rhz0uvQDv1w1cSEMjLQaHtH yZMkgwKBgQDtIAY+yqcmGFYiHQT7V35QbmfeJX1/v9KgpcA7L9Qi6H2LgKPlZu3e Fc5Pp8C82uIzxuKBbEqauoWAEfP7r2wn1EwoQGMdsY9MpPiScS5iwLKuiSWyyjyf Snmq+wLwVMYr71ijCuD+Ydm2xGoYeogwkV+QuTOS79s7HGM5tAN9TQKBgQDYrXHR Nc84Xt+86cWCXJ2rAhHoTMXQQSVXoc75CjBPM2oUH6iguVBM7dPG0ORU14+o7Q7Y gUvQCV6xoWH05nESHG++sidRifM/HT07M1bSjbMPcFmeAeA0mTFodXfRN6dKyibb 5kHUHgkgsC8qpXZr1KsNR7BcvC+xKuG1qC1R+wKBgDz5mzS3xJTEbeuDzhS+uhSu rP6b7RI4o+AqnyUpjlIehq7X76FjnEBsAdn377uIvdLMvebEEy8aBRJNwmVKXaPX gUwt0FgXtyJWTowOeaRdb8Z7CbGht9EwaG3LhGmvZiiOANl303Sc0ZVltOG5G7S3 qtwSXbgRyqjMyQ7WhI3vAoGBAMYa67f2rtRz/8Kp2Sa7E8+M3Swo719RgTotighD 1GWrWav/sB3rQhpzCsRnNyj/mUn9T2bcnRX58C1gWY9zmpQ3QZhoXnZvf0+ltFNi I36tcIMk5DixQgQ0Sm4iQalXdGGi4bMbqeaB3HWoZaNVc5XJwPYy6mNqOjuU657F pcdLAoGBAOQRc5kaZ3APKGHu64DzKh5TOam9J2gpRSD5kF2fIkAeaYWU0bE9PlqV MUxNRzxbIC16eCKFUoDkErIXGQfIMUOm+aCT/qpoAdXIvuO7H0OYRjMDmbscSDEV cQYaFsx8Z1KwMVBTwDtiGXhd+82+dKnXxH4bZC+WAKs7L79HqhER -----END RSA PRIVATE KEY-----` ) func Test(t *testing.T) { gc.TestingT(t) } type LoopingHTTPSuite struct { httpsuite.HTTPSuite creds *auth.Credentials } func (s *LoopingHTTPSuite) setupLoopbackRequest() (*http.Header, chan string, *Client) { var headers http.Header bodyChan := make(chan string, 1) handler := func(resp http.ResponseWriter, req *http.Request) { headers = req.Header bodyBytes, _ := ioutil.ReadAll(req.Body) req.Body.Close() bodyChan <- string(bodyBytes) resp.Header().Add("Content-Length", "0") resp.WriteHeader(http.StatusNoContent) resp.Write([]byte{}) } s.Mux.HandleFunc("/", handler) client := New(s.creds, "", nil) return &headers, bodyChan, client } type HTTPClientTestSuite struct { LoopingHTTPSuite } type HTTPSClientTestSuite struct { LoopingHTTPSuite } var _ = gc.Suite(&HTTPClientTestSuite{LoopingHTTPSuite{httpsuite.HTTPSuite{}, &auth.Credentials{ UserAuthentication: auth.Auth{User: "test_user", PrivateKey: key, Algorithm: "rsa-sha256"}, SdcKeyId: "test_key", SdcEndpoint: auth.Endpoint{URL: "http://gotest.api.joyentcloud.com"}, }}}) var _ = gc.Suite(&HTTPSClientTestSuite{LoopingHTTPSuite{httpsuite.HTTPSuite{UseTLS: true}, &auth.Credentials{ UserAuthentication: auth.Auth{User: "test_user", PrivateKey: key, Algorithm: "rsa-sha256"}, SdcKeyId: "test_key", SdcEndpoint: auth.Endpoint{URL: "http://gotest.api.joyentcloud.com"}, }}}) func (s *HTTPClientTestSuite) assertHeaderValues(c *gc.C, apiVersion string) { emptyHeaders := http.Header{} date := "Mon, 14 Oct 2013 18:49:29 GMT" headers, _ := createHeaders(emptyHeaders, s.creds, "content-type", date, apiVersion, false) contentTypes := []string{"content-type"} dateHeader := []string{"Mon, 14 Oct 2013 18:49:29 GMT"} authorizationHeader := []string{"Signature keyId=\"/test_user/keys/test_key\",algorithm=\"rsa-sha256\" " + Signature} headerData := map[string][]string{ "Date": dateHeader, "Authorization": authorizationHeader, "Content-Type": contentTypes, "Accept": contentTypes, "User-Agent": []string{gojoyentAgent()}} if apiVersion != "" { headerData["X-Api-Version"] = []string{apiVersion} } expectedHeaders := http.Header(headerData) c.Assert(headers, gc.DeepEquals, expectedHeaders) c.Assert(emptyHeaders, gc.DeepEquals, http.Header{}) } func (s *HTTPClientTestSuite) TestCreateHeadersNoApiVersion(c *gc.C) { s.assertHeaderValues(c, "") } func (s *HTTPClientTestSuite) TestCreateHeadersWithApiVersion(c *gc.C) { s.assertHeaderValues(c, "token") } func (s *HTTPClientTestSuite) TestCreateHeadersCopiesSupplied(c *gc.C) { initialHeaders := make(http.Header) date := "Mon, 14 Oct 2013 18:49:29 GMT" initialHeaders["Foo"] = []string{"Bar"} contentType := contentTypeJSON contentTypes := []string{contentType} dateHeader := []string{"Mon, 14 Oct 2013 18:49:29 GMT"} authorizationHeader := []string{"Signature keyId=\"/test_user/keys/test_key\",algorithm=\"rsa-sha256\" " + Signature} headers, _ := createHeaders(initialHeaders, s.creds, contentType, date, "", false) // it should not change the headers passed in c.Assert(initialHeaders, gc.DeepEquals, http.Header{"Foo": []string{"Bar"}}) // The initial headers should be in the output c.Assert(headers, gc.DeepEquals, http.Header{"Foo": []string{"Bar"}, "Date": dateHeader, "Authorization": authorizationHeader, "Content-Type": contentTypes, "Accept": contentTypes, "User-Agent": []string{gojoyentAgent()}}) } func (s *HTTPClientTestSuite) TestBinaryRequestSetsUserAgent(c *gc.C) { headers, _, client := s.setupLoopbackRequest() req := RequestData{} resp := ResponseData{ExpectedStatus: []int{http.StatusNoContent}} err := client.BinaryRequest("POST", s.Server.URL, "", &req, &resp) c.Assert(err, gc.IsNil) agent := headers.Get("User-Agent") c.Check(agent, gc.Not(gc.Equals), "") c.Check(agent, gc.Equals, gojoyentAgent()) } func (s *HTTPClientTestSuite) TestJSONRequestSetsUserAgent(c *gc.C) { headers, _, client := s.setupLoopbackRequest() req := RequestData{} resp := ResponseData{ExpectedStatus: []int{http.StatusNoContent}} err := client.JsonRequest("POST", s.Server.URL, "", &req, &resp) c.Assert(err, gc.IsNil) agent := headers.Get("User-Agent") c.Check(agent, gc.Not(gc.Equals), "") c.Check(agent, gc.Equals, gojoyentAgent()) } func (s *HTTPClientTestSuite) TestBinaryRequestSetsContentLength(c *gc.C) { headers, bodyChan, client := s.setupLoopbackRequest() content := "binary\ncontent\n" req := RequestData{ ReqReader: bytes.NewBufferString(content), ReqLength: len(content), } resp := ResponseData{ExpectedStatus: []int{http.StatusNoContent}} err := client.BinaryRequest("POST", s.Server.URL, "", &req, &resp) c.Assert(err, gc.IsNil) encoding := headers.Get("Transfer-Encoding") c.Check(encoding, gc.Equals, "") length := headers.Get("Content-Length") c.Check(length, gc.Equals, fmt.Sprintf("%d", len(content))) body, ok := <-bodyChan c.Assert(ok, gc.Equals, true) c.Check(body, gc.Equals, content) } func (s *HTTPClientTestSuite) TestJSONRequestSetsContentLength(c *gc.C) { headers, bodyChan, client := s.setupLoopbackRequest() reqMap := map[string]string{"key": "value"} req := RequestData{ ReqValue: reqMap, } resp := ResponseData{ExpectedStatus: []int{http.StatusNoContent}} err := client.JsonRequest("POST", s.Server.URL, "", &req, &resp) c.Assert(err, gc.IsNil) encoding := headers.Get("Transfer-Encoding") c.Check(encoding, gc.Equals, "") length := headers.Get("Content-Length") body, ok := <-bodyChan c.Assert(ok, gc.Equals, true) c.Check(body, gc.Not(gc.Equals), "") c.Check(length, gc.Equals, fmt.Sprintf("%d", len(body))) } func (s *HTTPSClientTestSuite) TestDefaultClientRejectSelfSigned(c *gc.C) { _, _, client := s.setupLoopbackRequest() req := RequestData{} resp := ResponseData{ExpectedStatus: []int{http.StatusNoContent}} err := client.BinaryRequest("POST", s.Server.URL, "", &req, &resp) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, "(.|\\n)*x509: certificate signed by unknown authority") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/http/client.go��������������������������������������0000644�0000153�0000161�00000031605�12321735717�024217� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gocommon - Go library to interact with the JoyentCloud // An HTTP Client which sends json and binary requests, handling data marshalling and response processing. // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package http import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "net/http" "net/url" "reflect" "strconv" "strings" "time" "github.com/juju/loggo" "github.com/joyent/gocommon" "github.com/joyent/gocommon/errors" "github.com/joyent/gocommon/jpc" "github.com/joyent/gosign/auth" ) const ( contentTypeJSON = "application/json" contentTypeOctetStream = "application/octet-stream" ) type Client struct { http.Client maxSendAttempts int credentials *auth.Credentials apiVersion string logger *loggo.Logger } type ErrorResponse struct { Message string `json:"message"` Code int `json:"code"` } func (e *ErrorResponse) Error() string { return fmt.Sprintf("Failed: %d: %s", e.Code, e.Message) } type ErrorWrapper struct { Error ErrorResponse `json:"error"` } type RequestData struct { ReqHeaders http.Header Params *url.Values ReqValue interface{} ReqReader io.Reader ReqLength int } type ResponseData struct { ExpectedStatus []int RespHeaders *http.Header RespValue interface{} RespReader io.ReadCloser } const ( // The maximum number of times to try sending a request before we give up // (assuming any unsuccessful attempts can be sensibly tried again). MaxSendAttempts = 3 ) // New returns a new http *Client using the default net/http client. func New(credentials *auth.Credentials, apiVersion string, logger *loggo.Logger) *Client { return &Client{*http.DefaultClient, MaxSendAttempts, credentials, apiVersion, logger} } func gojoyentAgent() string { return fmt.Sprintf("gocommon (%s)", gocommon.Version) } func createHeaders(extraHeaders http.Header, credentials *auth.Credentials, contentType, rfc1123Date, apiVersion string, isMantaRequest bool) (http.Header, error) { headers := make(http.Header) if extraHeaders != nil { for header, values := range extraHeaders { for _, value := range values { headers.Add(header, value) } } } if extraHeaders.Get("Content-Type") == "" { headers.Add("Content-Type", contentType) } if extraHeaders.Get("Accept") == "" { headers.Add("Accept", contentType) } if rfc1123Date != "" { headers.Set("Date", rfc1123Date) } else { headers.Set("Date", getDateForRegion(credentials, isMantaRequest)) } authHeaders, err := auth.CreateAuthorizationHeader(headers, credentials, isMantaRequest) if err != nil { return http.Header{}, err } headers.Set("Authorization", authHeaders) if apiVersion != "" { headers.Set("X-Api-Version", apiVersion) } headers.Add("User-Agent", gojoyentAgent()) return headers, nil } func getDateForRegion(credentials *auth.Credentials, isManta bool) string { if isManta { location, _ := time.LoadLocation(jpc.Locations["us-east-1"]) return time.Now().In(location).Format(time.RFC1123) } else { location, _ := time.LoadLocation(jpc.Locations[credentials.Region()]) return time.Now().In(location).Format(time.RFC1123) } } // JsonRequest JSON encodes and sends the object in reqData.ReqValue (if any) to the specified URL. // Optional method arguments are passed using the RequestData object. // Relevant RequestData fields: // ReqHeaders: additional HTTP header values to add to the request. // ExpectedStatus: the allowed HTTP response status values, else an error is returned. // ReqValue: the data object to send. // RespValue: the data object to decode the result into. func (c *Client) JsonRequest(method, url, rfc1123Date string, request *RequestData, response *ResponseData) (err error) { err = nil var body []byte if request.Params != nil { url += "?" + request.Params.Encode() } if request.ReqValue != nil { body, err = json.Marshal(request.ReqValue) if err != nil { err = errors.Newf(err, "failed marshalling the request body") return } } headers, err := createHeaders(request.ReqHeaders, c.credentials, contentTypeJSON, rfc1123Date, c.apiVersion, isMantaRequest(url, c.credentials.UserAuthentication.User)) if err != nil { return err } respBody, respHeader, err := c.sendRequest( method, url, bytes.NewReader(body), len(body), headers, response.ExpectedStatus, c.logger) if err != nil { return } defer respBody.Close() respData, err := ioutil.ReadAll(respBody) if err != nil { err = errors.Newf(err, "failed reading the response body") return } if len(respData) > 0 { if response.RespValue != nil { if _, ok := response.RespValue.(*[]byte); ok { response.RespValue = respData //err = decodeJSON(bytes.NewReader(respData), false, response.RespValue) //if err != nil { // err = errors.Newf(err, "failed unmarshaling/decoding the response body: %s", respData) //} } else { err = json.Unmarshal(respData, response.RespValue) if err != nil { err = decodeJSON(bytes.NewReader(respData), true, response.RespValue) if err != nil { err = errors.Newf(err, "failed unmarshaling/decoding the response body: %s", respData) } } } } } if respHeader != nil { response.RespHeaders = respHeader } return } func decodeJSON(r io.Reader, multiple bool, into interface{}) error { d := json.NewDecoder(r) if multiple { return decodeStream(d, into) } return d.Decode(into) } func decodeStream(d *json.Decoder, into interface{}) error { t := reflect.TypeOf(into) if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice { return fmt.Errorf("unexpected type %s", t) } elemType := t.Elem().Elem() slice := reflect.ValueOf(into).Elem() for { val := reflect.New(elemType) if err := d.Decode(val.Interface()); err != nil { if err == io.EOF { break } return err } slice.Set(reflect.Append(slice, val.Elem())) } return nil } // Sends the byte array in reqData.ReqValue (if any) to the specified URL. // Optional method arguments are passed using the RequestData object. // Relevant RequestData fields: // ReqHeaders: additional HTTP header values to add to the request. // ExpectedStatus: the allowed HTTP response status values, else an error is returned. // ReqReader: an io.Reader providing the bytes to send. // RespReader: assigned an io.ReadCloser instance used to read the returned data.. func (c *Client) BinaryRequest(method, url, rfc1123Date string, request *RequestData, response *ResponseData) (err error) { err = nil if request.Params != nil { url += "?" + request.Params.Encode() } headers, err := createHeaders(request.ReqHeaders, c.credentials, contentTypeOctetStream, rfc1123Date, c.apiVersion, isMantaRequest(url, c.credentials.UserAuthentication.User)) if err != nil { return err } respBody, respHeader, err := c.sendRequest( method, url, request.ReqReader, request.ReqLength, headers, response.ExpectedStatus, c.logger) if err != nil { return } if response.RespReader != nil { response.RespReader = respBody } if respHeader != nil { response.RespHeaders = respHeader } return } // Sends the specified request to URL and checks that the HTTP response status is as expected. // reqReader: a reader returning the data to send. // length: the number of bytes to send. // headers: HTTP headers to include with the request. // expectedStatus: a slice of allowed response status codes. func (c *Client) sendRequest(method, URL string, reqReader io.Reader, length int, headers http.Header, expectedStatus []int, logger *loggo.Logger) (rc io.ReadCloser, respHeader *http.Header, err error) { reqData := make([]byte, length) if reqReader != nil { nrRead, err := io.ReadFull(reqReader, reqData) if err != nil { err = errors.Newf(err, "failed reading the request data, read %v of %v bytes", nrRead, length) return rc, respHeader, err } } rawResp, err := c.sendRateLimitedRequest(method, URL, headers, reqData, logger) if err != nil { return } if logger != nil && logger.IsTraceEnabled() { logger.Tracef("Request: %s %s\n", method, URL) logger.Tracef("Request header: %s\n", headers) logger.Tracef("Request body: %s\n", reqData) logger.Tracef("Response: %s\n", rawResp.Status) logger.Tracef("Response header: %s\n", rawResp.Header) logger.Tracef("Response body: %s\n", rawResp.Body) logger.Tracef("Response error: %s\n", err) } foundStatus := false if len(expectedStatus) == 0 { expectedStatus = []int{http.StatusOK} } for _, status := range expectedStatus { if rawResp.StatusCode == status { foundStatus = true break } } if !foundStatus && len(expectedStatus) > 0 { err = handleError(URL, rawResp) rawResp.Body.Close() return } return rawResp.Body, &rawResp.Header, err } func (c *Client) sendRateLimitedRequest(method, URL string, headers http.Header, reqData []byte, logger *loggo.Logger) (resp *http.Response, err error) { for i := 0; i < c.maxSendAttempts; i++ { var reqReader io.Reader if reqData != nil { reqReader = bytes.NewReader(reqData) } req, err := http.NewRequest(method, URL, reqReader) if err != nil { err = errors.Newf(err, "failed creating the request %s", URL) return nil, err } // Setting req.Close to true to avoid malformed HTTP version "nullHTTP/1.1" error // See http://stackoverflow.com/questions/17714494/golang-http-request-results-in-eof-errors-when-making-multiple-requests-successi req.Close = true for header, values := range headers { for _, value := range values { req.Header.Add(header, value) } } req.ContentLength = int64(len(reqData)) resp, err = c.Do(req) if err != nil { return nil, errors.Newf(err, "failed executing the request %s", URL) } if resp.StatusCode != http.StatusRequestEntityTooLarge || resp.Header.Get("Retry-After") == "" { return resp, nil } resp.Body.Close() retryAfter, err := strconv.ParseFloat(resp.Header.Get("Retry-After"), 64) if err != nil { return nil, errors.Newf(err, "Invalid Retry-After header %s", URL) } if retryAfter == 0 { return nil, errors.Newf(err, "Resource limit exeeded at URL %s", URL) } if logger != nil { logger.Warningf("Too many requests, retrying in %dms.", int(retryAfter*1000)) } time.Sleep(time.Duration(retryAfter) * time.Second) } return nil, errors.Newf(err, "Maximum number of attempts (%d) reached sending request to %s", c.maxSendAttempts, URL) } type HttpError struct { StatusCode int Data map[string][]string Url string ResponseMessage string } func (e *HttpError) Error() string { return fmt.Sprintf("request %q returned unexpected status %d with body %q", e.Url, e.StatusCode, e.ResponseMessage, ) } // The HTTP response status code was not one of those expected, so we construct an error. // NotFound (404) codes have their own NotFound error type. // We also make a guess at duplicate value errors. func handleError(URL string, resp *http.Response) error { errBytes, _ := ioutil.ReadAll(resp.Body) errInfo := string(errBytes) // Check if we have a JSON representation of the failure, if so decode it. if resp.Header.Get("Content-Type") == contentTypeJSON { var errResponse ErrorResponse if err := json.Unmarshal(errBytes, &errResponse); err == nil { errInfo = errResponse.Message } } httpError := &HttpError{ resp.StatusCode, map[string][]string(resp.Header), URL, errInfo, } switch resp.StatusCode { case http.StatusBadRequest: return errors.NewBadRequestf(httpError, "", "Bad request %s", URL) case http.StatusUnauthorized: return errors.NewNotAuthorizedf(httpError, "", "Unauthorised URL %s", URL) //return errors.NewInvalidCredentialsf(httpError, "", "Unauthorised URL %s", URL) case http.StatusForbidden: //return errors. case http.StatusNotFound: return errors.NewResourceNotFoundf(httpError, "", "Resource not found %s", URL) case http.StatusMethodNotAllowed: //return errors. case http.StatusNotAcceptable: return errors.NewInvalidHeaderf(httpError, "", "Invalid Header %s", URL) case http.StatusConflict: return errors.NewMissingParameterf(httpError, "", "Missing parameters %s", URL) //return errors.NewInvalidArgumentf(httpError, "", "Invalid parameter %s", URL) case http.StatusRequestEntityTooLarge: return errors.NewRequestTooLargef(httpError, "", "Request too large %s", URL) case http.StatusUnsupportedMediaType: //return errors. case http.StatusServiceUnavailable: return errors.NewInternalErrorf(httpError, "", "Internal error %s", URL) case 420: // SlowDown return errors.NewRequestThrottledf(httpError, "", "Request throttled %s", URL) case 422: // Unprocessable Entity return errors.NewInvalidArgumentf(httpError, "", "Invalid parameters %s", URL) case 449: // RetryWith return errors.NewInvalidVersionf(httpError, "", "Invalid version %s", URL) //RequestMovedError -> ? } return errors.NewUnknownErrorf(httpError, "", "Unknown error %s", URL) } func isMantaRequest(url, user string) bool { return strings.Contains(url, "/"+user+"/stor") || strings.Contains(url, "/"+user+"/jobs") || strings.Contains(url, "/"+user+"/public") } ���������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/COPYING���������������������������������������������0000644�0000153�0000161�00000104513�12321735717�022465� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/.gitignore������������������������������������������0000644�0000153�0000161�00000000431�12321735717�023414� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe # IntelliJ files .idea *.iml���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/testing/��������������������������������������������0000755�0000153�0000161�00000000000�12321735717�023103� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/testing/httpsuite.go��������������������������������0000644�0000153�0000161�00000002000�12321735717�025453� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package testing // This package provides an HTTPSuite infrastructure that lets you bring up an // HTTP server. The server will handle requests based on whatever Handlers are // attached to HTTPSuite.Mux. This Mux is reset after every test case, and the // server is shut down at the end of the test suite. import ( gc "launchpad.net/gocheck" "net/http" "net/http/httptest" ) var _ = gc.Suite(&HTTPSuite{}) type HTTPSuite struct { Server *httptest.Server Mux *http.ServeMux oldHandler http.Handler UseTLS bool } func (s *HTTPSuite) SetUpSuite(c *gc.C) { if s.UseTLS { s.Server = httptest.NewTLSServer(nil) } else { s.Server = httptest.NewServer(nil) } } func (s *HTTPSuite) SetUpTest(c *gc.C) { s.oldHandler = s.Server.Config.Handler s.Mux = http.NewServeMux() s.Server.Config.Handler = s.Mux } func (s *HTTPSuite) TearDownTest(c *gc.C) { s.Mux = nil s.Server.Config.Handler = s.oldHandler } func (s *HTTPSuite) TearDownSuite(c *gc.C) { if s.Server != nil { s.Server.Close() } } juju-core_1.18.1/src/github.com/joyent/gocommon/testing/httpsuite_test.go���������������������������0000644�0000153�0000161�00000003555�12321735717�026532� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package testing_test import ( "crypto/tls" "crypto/x509" "io/ioutil" gc "launchpad.net/gocheck" "net/http" "net/url" "reflect" "testing" jt "github.com/joyent/gocommon/testing" ) type HTTPTestSuite struct { jt.HTTPSuite } type HTTPSTestSuite struct { jt.HTTPSuite } func Test(t *testing.T) { gc.TestingT(t) } var _ = gc.Suite(&HTTPTestSuite{}) var _ = gc.Suite(&HTTPSTestSuite{jt.HTTPSuite{UseTLS: true}}) type HelloHandler struct{} func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(200) w.Write([]byte("Hello World\n")) } func (s *HTTPTestSuite) TestHelloWorld(c *gc.C) { s.Mux.Handle("/", &HelloHandler{}) response, err := http.Get(s.Server.URL) c.Check(err, gc.IsNil) content, err := ioutil.ReadAll(response.Body) response.Body.Close() c.Check(err, gc.IsNil) c.Check(response.Status, gc.Equals, "200 OK") c.Check(response.StatusCode, gc.Equals, 200) c.Check(string(content), gc.Equals, "Hello World\n") } func (s *HTTPSTestSuite) TestHelloWorldWithTLS(c *gc.C) { s.Mux.Handle("/", &HelloHandler{}) c.Check(s.Server.URL[:8], gc.Equals, "https://") response, err := http.Get(s.Server.URL) // Default http.Get fails because the cert is self-signed c.Assert(err, gc.NotNil) c.Assert(reflect.TypeOf(err.(*url.Error).Err), gc.Equals, reflect.TypeOf(x509.UnknownAuthorityError{})) // Connect again with a Client that doesn't validate the cert insecureClient := &http.Client{Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}} response, err = insecureClient.Get(s.Server.URL) c.Assert(err, gc.IsNil) content, err := ioutil.ReadAll(response.Body) response.Body.Close() c.Check(err, gc.IsNil) c.Check(response.Status, gc.Equals, "200 OK") c.Check(response.StatusCode, gc.Equals, 200) c.Check(string(content), gc.Equals, "Hello World\n") } ���������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gocommon/gocommon.go�����������������������������������������0000644�0000153�0000161�00000001112�12321735717�023566� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* The gocommon package collects common packages to interact with the Joyent Public Cloud and Joyent Manta services. The gocommon package is structured as follow: - gocommon/client. Client for sending requests. - gocommon/errors. Joyent specific errors. - gocommon/http. HTTP client for sending requests. - gocommon/jpc. This package provides common structures and functions across packages. - gocommon/testing. Testing Suite for local testing. Licensed under LGPL v3. Copyright (c) 2013 Joyent Inc. Written by Daniele Stroppa <daniele.stroppa@joyent.com> */ package gocommon ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosign/������������������������������������������������������0000755�0000153�0000161�00000000000�12321735717�021076� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosign/COPYING.LESSER����������������������������������������0000644�0000153�0000161�00000016743�12321735717�023140� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �����������������������������juju-core_1.18.1/src/github.com/joyent/gosign/gosign_test.go����������������������������������������0000644�0000153�0000161�00000000564�12321735717�023757� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosign - Go HTTP signing library for the Joyent Public Cloud and Joyent Manta // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package gosign import ( gc "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { gc.TestingT(t) } type GoSignTestSuite struct { } var _ = gc.Suite(&GoSignTestSuite{}) ��������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosign/LICENSE�����������������������������������������������0000644�0000153�0000161�00000001251�12321735717�022102� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������GoSign - Go HTTP signing library for the Joyent Public Cloud Copyright (c) 2013, Joyent Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. See both COPYING and COPYING.LESSER for the full terms of the GNU Lesser General Public License. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosign/gosign.go���������������������������������������������0000644�0000153�0000161�00000000576�12321735717�022723� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* The sign package enables Go programs to create signed requests for the Joyent Public Cloud and Joyent Manta services. The sign package is structured as follow: - gosign/auth. This package deals with the authorization and signature of requests. Licensed under LGPL v3. Copyright (c) 2013 Joyent Inc. Written by Daniele Stroppa <daniele.stroppa@joyent.com> */ package gosign ����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosign/auth/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735717�022037� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosign/auth/auth.go������������������������������������������0000644�0000153�0000161�00000006557�12321735717�023344� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosign - Go HTTP signing library for the Joyent Public Cloud and Joyent Manta // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package auth import ( "crypto" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/pem" "fmt" "net/http" "net/url" "strings" ) const ( // Authorization Headers SdcSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\" %s" MantaSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\",signature=\"%s\"" ) type Endpoint struct { URL string } type Auth struct { User string PrivateKey string Algorithm string } type Credentials struct { UserAuthentication Auth SdcKeyId string SdcEndpoint Endpoint MantaKeyId string MantaEndpoint Endpoint } type PrivateKey struct { key *rsa.PrivateKey } // The CreateAuthorizationHeader returns the Authorization header for the give request. func CreateAuthorizationHeader(headers http.Header, credentials *Credentials, isMantaRequest bool) (string, error) { if isMantaRequest { signature, err := GetSignature(&credentials.UserAuthentication, "date: "+headers.Get("Date")) if err != nil { return "", err } return fmt.Sprintf(MantaSignature, credentials.UserAuthentication.User, credentials.MantaKeyId, credentials.UserAuthentication.Algorithm, signature), nil } signature, err := GetSignature(&credentials.UserAuthentication, headers.Get("Date")) if err != nil { return "", err } return fmt.Sprintf(SdcSignature, credentials.UserAuthentication.User, credentials.SdcKeyId, credentials.UserAuthentication.Algorithm, signature), nil } // The GetSignature method signs the specified key according to http://apidocs.joyent.com/cloudapi/#issuing-requests // and http://apidocs.joyent.com/manta/api.html#authentication. func GetSignature(auth *Auth, signing string) (string, error) { block, _ := pem.Decode([]byte(auth.PrivateKey)) if block == nil { return "", fmt.Errorf("invalid private key data: %s", auth.PrivateKey) } rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return "", fmt.Errorf("An error occurred while parsing the key: %s", err) } privateKey := &PrivateKey{rsakey} hashFunc := getHashFunction(auth.Algorithm) hash := hashFunc.New() hash.Write([]byte(signing)) digest := hash.Sum(nil) signed, err := rsa.SignPKCS1v15(rand.Reader, privateKey.key, hashFunc, digest) if err != nil { return "", fmt.Errorf("An error occurred while signing the key: %s", err) } return base64.StdEncoding.EncodeToString(signed), nil } // Helper method to get the Hash function based on the algorithm func getHashFunction(algorithm string) (hashFunc crypto.Hash) { switch strings.ToLower(algorithm) { case "rsa-sha1": hashFunc = crypto.SHA1 case "rsa-sha224", "rsa-sha256": hashFunc = crypto.SHA256 case "rsa-sha384", "rsa-sha512": hashFunc = crypto.SHA512 default: hashFunc = crypto.SHA256 } return } func (cred *Credentials) Region() string { sdcUrl := cred.SdcEndpoint.URL if isLocalhost(sdcUrl) { return "some-region" } return sdcUrl[strings.LastIndex(sdcUrl, "/")+1 : strings.Index(sdcUrl, ".")] } func isLocalhost(u string) bool { parsedUrl, err := url.Parse(u) if err != nil { return false } if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.1") { return true } return false } �������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosign/auth/auth_test.go�������������������������������������0000644�0000153�0000161�00000010213�12321735717�024363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosign - Go HTTP signing library for the Joyent Public Cloud and Joyent Manta // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package auth_test import ( "net/http" "testing" gc "launchpad.net/gocheck" "github.com/joyent/gosign/auth" ) const ( SdcSignature = "yK0J17CQ04ZvMsFLoH163Sjyg8tE4BoIeCsmKWLQKN3BYgSpR0XyqrecheQ2A0o4L99oSumYSKIscBSiH5rqdf4/1zC/FEkYOI2UzcIHYb1MPNzO3g/5X44TppYE+8dxoH99V+Ts8RT3ZurEYjQ8wmK0TnxdirAevSpbypZJaBOFXUZSxx80m5BD4QE/MSGo/eaVdJI/Iw+nardHNvimVCr6pRNycX1I4FdyRR6kgrAl2NkY2yxx/CAY21Ir+dmbG3A1x4GiIE485LLheAL5/toPo7Gh8G5fkrF9dXWVyX0k9AZXqXNWn5AZxc32dKL2enH09j/X86RtwiR1IEuPww==" MantaSignature = "unBowZ/HOydMxzYkmoB192rn006vujsuZvhx/CieAl+k+YoQsHMM1tAPwbxs71o65sMMymRBZGOZU91lvbEW94rF950HDYy1mhqTf4QAHXc3Km3lInXvAQuvsMrZUofNApzxIdAacNL/ESJ8JCU8sxT2919cDCKkVI8vqOvUJvCyCSIlkBr9d+MLBHuFwr6zRgd3pZSMbMoKrrX6XzsQIUhOldrbSJXYzaQnwvvY2pygPEl491mzY+gt+jiykSVTMlLM2+iCrP4/rmMHenpGYjN2tNftNwo2U6rFwWKwWkK5G1n5YrKMLIt6CV6z+nFLsvhimCtP7WY+pOuVU+1hrA==" testJpcKeyName = "test_key" key = `-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAyLOtVh8qXjdwfjZZYwkEgg1yoSzmpKKpmzYW745lBGtPH87F spHVHeqjmgFnBsARsD7CHzYyQTho7oLrAEbuF7tKdGRK25wJIenPKKuL+UVwZNeJ VEXSiMNmX3Y4IqRteqRIjhw3DmXYHEWvBc2JVy8lWtyK+o6o8jlO0aRTTT2+dETp yqKqNJyHVNz2u6XVtm7jqyLU7tAqW+qpr5zSoNmuUAyz6JDCRnlWvwp1qzuS1LV3 2HK9yfq8TGriDVPyPRpFRmiRGWGIrIKrmm4sImpoLfuVBITjeh8V3Ee0OCDmTLgY lTHAmCLFJxaW5Y8b4cTt5pbT7R1iu77RKJo3fwIBIwKCAQEAmtPAOx9bMr0NozE9 pCuHIn9nDp77EUpIUym57AAiCrkuaP6YgnB/1T/6jL9A2VJWycoDdynO/xzjO6bS iy9nNuDwSyjMCH+vRgwjdyVAGBD+7rTmSFMewUZHqLpIj8CsOgm0UF7opLT3K8C6 N60vbyRepS3KTEIqjvkCSfPLO5Sp38KZYXKg0/Abb21WDSEzWonjV8JfOyMfUYhh 7QCE+Nf8s3b+vxskOuCQq1WHoqo8CqXrMYVknkvnQuRFPuaOLEjMbJVqlTb9ns2V SKxmo46R7fl2dKgMBll+Nec+3Dn2/Iq/qnHq34HF/rhz0uvQDv1w1cSEMjLQaHtH yZMkgwKBgQDtIAY+yqcmGFYiHQT7V35QbmfeJX1/v9KgpcA7L9Qi6H2LgKPlZu3e Fc5Pp8C82uIzxuKBbEqauoWAEfP7r2wn1EwoQGMdsY9MpPiScS5iwLKuiSWyyjyf Snmq+wLwVMYr71ijCuD+Ydm2xGoYeogwkV+QuTOS79s7HGM5tAN9TQKBgQDYrXHR Nc84Xt+86cWCXJ2rAhHoTMXQQSVXoc75CjBPM2oUH6iguVBM7dPG0ORU14+o7Q7Y gUvQCV6xoWH05nESHG++sidRifM/HT07M1bSjbMPcFmeAeA0mTFodXfRN6dKyibb 5kHUHgkgsC8qpXZr1KsNR7BcvC+xKuG1qC1R+wKBgDz5mzS3xJTEbeuDzhS+uhSu rP6b7RI4o+AqnyUpjlIehq7X76FjnEBsAdn377uIvdLMvebEEy8aBRJNwmVKXaPX gUwt0FgXtyJWTowOeaRdb8Z7CbGht9EwaG3LhGmvZiiOANl303Sc0ZVltOG5G7S3 qtwSXbgRyqjMyQ7WhI3vAoGBAMYa67f2rtRz/8Kp2Sa7E8+M3Swo719RgTotighD 1GWrWav/sB3rQhpzCsRnNyj/mUn9T2bcnRX58C1gWY9zmpQ3QZhoXnZvf0+ltFNi I36tcIMk5DixQgQ0Sm4iQalXdGGi4bMbqeaB3HWoZaNVc5XJwPYy6mNqOjuU657F pcdLAoGBAOQRc5kaZ3APKGHu64DzKh5TOam9J2gpRSD5kF2fIkAeaYWU0bE9PlqV MUxNRzxbIC16eCKFUoDkErIXGQfIMUOm+aCT/qpoAdXIvuO7H0OYRjMDmbscSDEV cQYaFsx8Z1KwMVBTwDtiGXhd+82+dKnXxH4bZC+WAKs7L79HqhER -----END RSA PRIVATE KEY-----` ) func Test(t *testing.T) { gc.TestingT(t) } type AuthSuite struct { } var _ = gc.Suite(&AuthSuite{}) func (s *AuthSuite) TestCreateSdcAuthorizationHeader(c *gc.C) { headers := make(http.Header) headers["Date"] = []string{"Mon, 14 Oct 2013 18:49:29 GMT"} authentication := auth.Auth{User: "test_user", PrivateKey: key, Algorithm: "rsa-sha256"} credentials := &auth.Credentials{ UserAuthentication: authentication, SdcKeyId: "test_key", SdcEndpoint: auth.Endpoint{URL: "http://gotest.api.joyentcloud.com"}, } authHeader, err := auth.CreateAuthorizationHeader(headers, credentials, false) c.Assert(err, gc.IsNil) c.Assert(authHeader, gc.Equals, "Signature keyId=\"/test_user/keys/"+testJpcKeyName+"\",algorithm=\"rsa-sha256\" "+SdcSignature) } func (s *AuthSuite) TestCreateMantaAuthorizationHeader(c *gc.C) { headers := make(http.Header) headers["Date"] = []string{"Mon, 14 Oct 2013 18:49:29 GMT"} authentication := auth.Auth{User: "test_user", PrivateKey: key, Algorithm: "rsa-sha256"} credentials := &auth.Credentials{ UserAuthentication: authentication, MantaKeyId: "test_key", MantaEndpoint: auth.Endpoint{URL: "http://gotest.manta.joyent.com"}, } authHeader, err := auth.CreateAuthorizationHeader(headers, credentials, true) c.Assert(err, gc.IsNil) c.Assert(authHeader, gc.Equals, "Signature keyId=\"/test_user/keys/"+testJpcKeyName+"\",algorithm=\"rsa-sha256\",signature=\""+MantaSignature+"\"") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosign/README.md���������������������������������������������0000644�0000153�0000161�00000000047�12321735717�022356� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gosign ====== Go HTTP signing library �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosign/COPYING�����������������������������������������������0000644�0000153�0000161�00000104513�12321735717�022135� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosign/.gitignore��������������������������������������������0000644�0000153�0000161�00000000431�12321735717�023064� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe # IntelliJ files .idea *.iml���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/�������������������������������������������������������0000755�0000153�0000161�00000000000�12321735720�020701� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/COPYING.LESSER�����������������������������������������0000644�0000153�0000161�00000016743�12321735720�022743� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �����������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/LICENSE������������������������������������������������0000644�0000153�0000161�00000001233�12321735720�021705� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������GoSdc - Go Library for the Joyent Public Cloud Copyright (c) 2013, Joyent Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. See both COPYING and COPYING.LESSER for the full terms of the GNU Lesser General Public License. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/gosdc.go�����������������������������������������������0000644�0000153�0000161�00000000670�12321735720�022332� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* The gosdc package enables Go programs to interact with the Joyent CloudAPI. The gosdc package is structured as follow: - gosdc/cloudapi. This package interacts with the Cloud API (http://apidocs.joyent.com/cloudapi/). - gosdc/localservices. This package provides local services to be used for testing. Licensed under LGPL v3. Copyright (c) 2013 Joyent Inc. Written by Daniele Stroppa <daniele.stroppa@joyent.com> */ package gosdc ������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/gosdc_test.go������������������������������������������0000644�0000153�0000161�00000000533�12321735720�023367� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosdc - Go library to interact with the Joyent CloudAPI // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package gosdc import ( gc "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { gc.TestingT(t) } type GoSdcTestSuite struct { } var _ = gc.Suite(&GoSdcTestSuite{}) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/cloudapi/����������������������������������������������0000755�0000153�0000161�00000000000�12321735720�022501� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/cloudapi/local_test.go���������������������������������0000644�0000153�0000161�00000037712�12321735720�025173� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosdc - Go library to interact with the Joyent CloudAPI // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package cloudapi_test import ( "io/ioutil" "net/http" "net/http/httptest" "os" "strings" "time" gc "launchpad.net/gocheck" "github.com/joyent/gocommon/client" "github.com/joyent/gosdc/cloudapi" lc "github.com/joyent/gosdc/localservices/cloudapi" "github.com/joyent/gosign/auth" ) var privateKey []byte func registerLocalTests(keyName string) { var localKeyFile string if keyName == "" { localKeyFile = os.Getenv("HOME") + "/.ssh/id_rsa" } else { localKeyFile = keyName } privateKey, _ = ioutil.ReadFile(localKeyFile) gc.Suite(&LocalTests{}) } type LocalTests struct { //LocalTests creds *auth.Credentials testClient *cloudapi.Client Server *httptest.Server Mux *http.ServeMux oldHandler http.Handler cloudapi *lc.CloudAPI } func (s *LocalTests) SetUpSuite(c *gc.C) { // Set up the HTTP server. s.Server = httptest.NewServer(nil) s.oldHandler = s.Server.Config.Handler s.Mux = http.NewServeMux() s.Server.Config.Handler = s.Mux // Set up a Joyent CloudAPI service. authentication := auth.Auth{User: "localtest", PrivateKey: string(privateKey), Algorithm: "rsa-sha256"} s.creds = &auth.Credentials{ UserAuthentication: authentication, SdcKeyId: "", SdcEndpoint: auth.Endpoint{URL: s.Server.URL}, } s.cloudapi = lc.New(s.creds.SdcEndpoint.URL, s.creds.UserAuthentication.User) s.cloudapi.SetupHTTP(s.Mux) } func (s *LocalTests) TearDownSuite(c *gc.C) { s.Mux = nil s.Server.Config.Handler = s.oldHandler s.Server.Close() } func (s *LocalTests) SetUpTest(c *gc.C) { client := client.NewClient(s.creds.SdcEndpoint.URL, cloudapi.DefaultAPIVersion, s.creds, &cloudapi.Logger) c.Assert(client, gc.NotNil) s.testClient = cloudapi.New(client) c.Assert(s.testClient, gc.NotNil) } // Helper method to create a test key in the user account func (s *LocalTests) createKey(c *gc.C) { key, err := s.testClient.CreateKey(cloudapi.CreateKeyOpts{Name: "fake-key", Key: testKey}) c.Assert(err, gc.IsNil) c.Assert(key, gc.DeepEquals, &cloudapi.Key{Name: "fake-key", Fingerprint: "", Key: testKey}) } func (s *LocalTests) deleteKey(c *gc.C) { err := s.testClient.DeleteKey("fake-key") c.Assert(err, gc.IsNil) } // Helper method to create a test virtual machine in the user account func (s *LocalTests) createMachine(c *gc.C) *cloudapi.Machine { machine, err := s.testClient.CreateMachine(cloudapi.CreateMachineOpts{Package: localPackageName, Image: localImageId}) c.Assert(err, gc.IsNil) c.Assert(machine, gc.NotNil) // wait for machine to be provisioned for !s.pollMachineState(c, machine.Id, "running") { time.Sleep(1 * time.Second) } return machine } // Helper method to test the state of a given VM func (s *LocalTests) pollMachineState(c *gc.C, machineId, state string) bool { machineConfig, err := s.testClient.GetMachine(machineId) c.Assert(err, gc.IsNil) return strings.EqualFold(machineConfig.State, state) } // Helper method to delete a test virtual machine once the test has executed func (s *LocalTests) deleteMachine(c *gc.C, machineId string) { err := s.testClient.StopMachine(machineId) c.Assert(err, gc.IsNil) // wait for machine to be stopped for !s.pollMachineState(c, machineId, "stopped") { time.Sleep(1 * time.Second) } err = s.testClient.DeleteMachine(machineId) c.Assert(err, gc.IsNil) } // Helper method to list virtual machine according to the specified filter func (s *LocalTests) listMachines(c *gc.C, filter *cloudapi.Filter) { var contains bool testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) machines, err := s.testClient.ListMachines(filter) c.Assert(err, gc.IsNil) c.Assert(machines, gc.NotNil) for _, m := range machines { if m.Id == testMachine.Id { contains = true break } } // result if !contains { c.Fatalf("Obtained machines [%s] do not contain test machine [%s]", machines, *testMachine) } } // Helper method to create a test firewall rule func (s *LocalTests) createFirewallRule(c *gc.C) *cloudapi.FirewallRule { fwRule, err := s.testClient.CreateFirewallRule(cloudapi.CreateFwRuleOpts{Enabled: false, Rule: testFwRule}) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) c.Assert(fwRule.Rule, gc.Equals, testFwRule) c.Assert(fwRule.Enabled, gc.Equals, false) time.Sleep(10 * time.Second) return fwRule } // Helper method to a test firewall rule func (s *LocalTests) deleteFwRule(c *gc.C, fwRuleId string) { err := s.testClient.DeleteFirewallRule(fwRuleId) c.Assert(err, gc.IsNil) } // Keys API func (s *LocalTests) TestCreateKey(c *gc.C) { s.createKey(c) s.deleteKey(c) } func (s *LocalTests) TestListKeys(c *gc.C) { s.createKey(c) defer s.deleteKey(c) keys, err := s.testClient.ListKeys() c.Assert(err, gc.IsNil) c.Assert(keys, gc.NotNil) fakeKey := cloudapi.Key{Name: "fake-key", Fingerprint: "", Key: testKey} for _, k := range keys { if c.Check(k, gc.DeepEquals, fakeKey) { c.SucceedNow() } } c.Fatalf("Obtained keys [%s] do not contain test key [%s]", keys, fakeKey) } func (s *LocalTests) TestGetKeyByName(c *gc.C) { s.createKey(c) defer s.deleteKey(c) key, err := s.testClient.GetKey("fake-key") c.Assert(err, gc.IsNil) c.Assert(key, gc.NotNil) c.Assert(key, gc.DeepEquals, &cloudapi.Key{Name: "fake-key", Fingerprint: "", Key: testKey}) } /*func (s *LocalTests) TestGetKeyByFingerprint(c *gc.C) { s.createKey(c) defer s.deleteKey(c) key, err := s.testClient.GetKey(testKeyFingerprint) c.Assert(err, gc.IsNil) c.Assert(key, gc.NotNil) c.Assert(key, gc.DeepEquals, &cloudapi.Key{Name: "fake-key", Fingerprint: testKeyFingerprint, Key: testKey}) } */ func (s *LocalTests) TestDeleteKey(c *gc.C) { s.createKey(c) s.deleteKey(c) } // Packages API func (s *LocalTests) TestListPackages(c *gc.C) { pkgs, err := s.testClient.ListPackages(nil) c.Assert(err, gc.IsNil) c.Assert(pkgs, gc.NotNil) for _, pkg := range pkgs { c.Check(pkg.Name, gc.FitsTypeOf, string("")) c.Check(pkg.Memory, gc.FitsTypeOf, int(0)) c.Check(pkg.Disk, gc.FitsTypeOf, int(0)) c.Check(pkg.Swap, gc.FitsTypeOf, int(0)) c.Check(pkg.VCPUs, gc.FitsTypeOf, int(0)) c.Check(pkg.Default, gc.FitsTypeOf, bool(false)) c.Check(pkg.Id, gc.FitsTypeOf, string("")) c.Check(pkg.Version, gc.FitsTypeOf, string("")) c.Check(pkg.Description, gc.FitsTypeOf, string("")) c.Check(pkg.Group, gc.FitsTypeOf, string("")) } } func (s *LocalTests) TestListPackagesWithFilter(c *gc.C) { filter := cloudapi.NewFilter() filter.Set("memory", "1024") pkgs, err := s.testClient.ListPackages(filter) c.Assert(err, gc.IsNil) c.Assert(pkgs, gc.NotNil) for _, pkg := range pkgs { c.Check(pkg.Name, gc.FitsTypeOf, string("")) c.Check(pkg.Memory, gc.Equals, 1024) c.Check(pkg.Disk, gc.FitsTypeOf, int(0)) c.Check(pkg.Swap, gc.FitsTypeOf, int(0)) c.Check(pkg.VCPUs, gc.FitsTypeOf, int(0)) c.Check(pkg.Default, gc.FitsTypeOf, bool(false)) c.Check(pkg.Id, gc.FitsTypeOf, string("")) c.Check(pkg.Version, gc.FitsTypeOf, string("")) c.Check(pkg.Description, gc.FitsTypeOf, string("")) c.Check(pkg.Group, gc.FitsTypeOf, string("")) } } func (s *LocalTests) TestGetPackageFromName(c *gc.C) { key, err := s.testClient.GetPackage(localPackageName) c.Assert(err, gc.IsNil) c.Assert(key, gc.NotNil) c.Assert(key, gc.DeepEquals, &cloudapi.Package{ Name: "Small", Memory: 1024, Disk: 16384, Swap: 2048, VCPUs: 1, Default: true, Id: "11223344-1212-abab-3434-aabbccddeeff", Version: "1.0.2", }) } func (s *LocalTests) TestGetPackageFromId(c *gc.C) { key, err := s.testClient.GetPackage(localPackageId) c.Assert(err, gc.IsNil) c.Assert(key, gc.NotNil) c.Assert(key, gc.DeepEquals, &cloudapi.Package{ Name: "Small", Memory: 1024, Disk: 16384, Swap: 2048, VCPUs: 1, Default: true, Id: "11223344-1212-abab-3434-aabbccddeeff", Version: "1.0.2", }) } // Images API func (s *LocalTests) TestListImages(c *gc.C) { imgs, err := s.testClient.ListImages(nil) c.Assert(err, gc.IsNil) c.Assert(imgs, gc.NotNil) for _, img := range imgs { c.Check(img.Id, gc.FitsTypeOf, string("")) c.Check(img.Name, gc.FitsTypeOf, string("")) c.Check(img.OS, gc.FitsTypeOf, string("")) c.Check(img.Version, gc.FitsTypeOf, string("")) c.Check(img.Type, gc.FitsTypeOf, string("")) c.Check(img.Description, gc.FitsTypeOf, string("")) c.Check(img.Requirements, gc.FitsTypeOf, map[string]interface{}{"key": "value"}) c.Check(img.Homepage, gc.FitsTypeOf, string("")) c.Check(img.PublishedAt, gc.FitsTypeOf, string("")) c.Check(img.Public, gc.FitsTypeOf, string("")) c.Check(img.State, gc.FitsTypeOf, string("")) c.Check(img.Tags, gc.FitsTypeOf, map[string]string{"key": "value"}) c.Check(img.EULA, gc.FitsTypeOf, string("")) c.Check(img.ACL, gc.FitsTypeOf, []string{"", ""}) } } func (s *LocalTests) TestListImagesWithFilter(c *gc.C) { filter := cloudapi.NewFilter() filter.Set("os", "smartos") imgs, err := s.testClient.ListImages(filter) c.Assert(err, gc.IsNil) c.Assert(imgs, gc.NotNil) for _, img := range imgs { c.Check(img.Id, gc.FitsTypeOf, string("")) c.Check(img.Name, gc.FitsTypeOf, string("")) c.Check(img.OS, gc.Equals, "smartos") c.Check(img.Version, gc.FitsTypeOf, string("")) c.Check(img.Type, gc.FitsTypeOf, string("")) c.Check(img.Description, gc.FitsTypeOf, string("")) c.Check(img.Requirements, gc.FitsTypeOf, map[string]interface{}{"key": "value"}) c.Check(img.Homepage, gc.FitsTypeOf, string("")) c.Check(img.PublishedAt, gc.FitsTypeOf, string("")) c.Check(img.Public, gc.FitsTypeOf, string("")) c.Check(img.State, gc.FitsTypeOf, string("")) c.Check(img.Tags, gc.FitsTypeOf, map[string]string{"key": "value"}) c.Check(img.EULA, gc.FitsTypeOf, string("")) c.Check(img.ACL, gc.FitsTypeOf, []string{"", ""}) } } // TODO Add test for deleteImage, exportImage and CreateMachineFormIMage func (s *LocalTests) TestGetImage(c *gc.C) { img, err := s.testClient.GetImage(localImageId) c.Assert(err, gc.IsNil) c.Assert(img, gc.NotNil) c.Assert(img, gc.DeepEquals, &cloudapi.Image{ Id: "12345678-a1a1-b2b2-c3c3-098765432100", Name: "SmartOS Std", OS: "smartos", Version: "13.3.1", Type: "smartmachine", Description: "Test SmartOS image (32 bit)", Homepage: "http://test.joyent.com/Standard_Instance", PublishedAt: "2014-01-08T17:42:31Z", Public: "true", State: "active", }) } // Tests for Machine API func (s *LocalTests) TestCreateMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) c.Assert(testMachine.Type, gc.Equals, "smartmachine") c.Assert(testMachine.Memory, gc.Equals, 1024) c.Assert(testMachine.Disk, gc.Equals, 16384) c.Assert(testMachine.Package, gc.Equals, localPackageName) c.Assert(testMachine.Image, gc.Equals, localImageId) } func (s *LocalTests) TestListMachines(c *gc.C) { s.listMachines(c, nil) } func (s *LocalTests) TestListMachinesWithFilter(c *gc.C) { filter := cloudapi.NewFilter() filter.Set("memory", "1024") s.listMachines(c, filter) } /*func (s *LocalTests) TestCountMachines(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) count, err := s.testClient.CountMachines() c.Assert(err, gc.IsNil) c.Assert(count >= 1, gc.Equals, true) }*/ func (s *LocalTests) TestGetMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) machine, err := s.testClient.GetMachine(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(machine, gc.NotNil) c.Assert(machine.Equals(*testMachine), gc.Equals, true) } func (s *LocalTests) TestStopMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.StopMachine(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LocalTests) TestStartMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.StopMachine(testMachine.Id) c.Assert(err, gc.IsNil) // wait for machine to be stopped for !s.pollMachineState(c, testMachine.Id, "stopped") { time.Sleep(1 * time.Second) } err = s.testClient.StartMachine(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LocalTests) TestRebootMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.RebootMachine(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LocalTests) TestRenameMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.RenameMachine(testMachine.Id, "test-machine-renamed") c.Assert(err, gc.IsNil) renamed, err := s.testClient.GetMachine(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(renamed.Name, gc.Equals, "test-machine-renamed") } func (s *LocalTests) TestResizeMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.ResizeMachine(testMachine.Id, "Medium") c.Assert(err, gc.IsNil) resized, err := s.testClient.GetMachine(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(resized.Package, gc.Equals, "Medium") } func (s *LocalTests) TestListMachinesFirewallRules(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) fwRules, err := s.testClient.ListMachineFirewallRules(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(fwRules, gc.NotNil) } func (s *LocalTests) TestEnableFirewallMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.EnableFirewallMachine(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LocalTests) TestDisableFirewallMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.DisableFirewallMachine(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LocalTests) TestDeleteMachine(c *gc.C) { testMachine := s.createMachine(c) s.deleteMachine(c, testMachine.Id) } // FirewallRules API func (s *LocalTests) TestCreateFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) // cleanup s.deleteFwRule(c, testFwRule.Id) } func (s *LocalTests) TestListFirewallRules(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) rules, err := s.testClient.ListFirewallRules() c.Assert(err, gc.IsNil) c.Assert(rules, gc.NotNil) } func (s *LocalTests) TestGetFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) fwRule, err := s.testClient.GetFirewallRule(testFwRule.Id) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) c.Assert((*fwRule), gc.DeepEquals, (*testFwRule)) } func (s *LocalTests) TestUpdateFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) fwRule, err := s.testClient.UpdateFirewallRule(testFwRule.Id, cloudapi.CreateFwRuleOpts{Rule: testUpdatedFwRule}) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) c.Assert(fwRule.Rule, gc.Equals, testUpdatedFwRule) } func (s *LocalTests) TestEnableFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) fwRule, err := s.testClient.EnableFirewallRule((*testFwRule).Id) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) } func (s *LocalTests) TestDisableFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) fwRule, err := s.testClient.DisableFirewallRule((*testFwRule).Id) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) } func (s *LocalTests) TestDeleteFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) s.deleteFwRule(c, testFwRule.Id) } // Networks API func (s *LocalTests) TestListNetworks(c *gc.C) { nets, err := s.testClient.ListNetworks() c.Assert(err, gc.IsNil) c.Assert(nets, gc.NotNil) } func (s *LocalTests) TestGetNetwork(c *gc.C) { net, err := s.testClient.GetNetwork(localNetworkId) c.Assert(err, gc.IsNil) c.Assert(net, gc.NotNil) c.Assert(net, gc.DeepEquals, &cloudapi.Network{ Id: localNetworkId, Name: "Test-Joyent-Public", Public: true, Description: "", }) } ������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/cloudapi/live_test.go����������������������������������0000644�0000153�0000161�00000057557�12321735720�025051� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosdc - Go library to interact with the Joyent CloudAPI // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package cloudapi_test import ( "strings" "time" gc "launchpad.net/gocheck" "github.com/joyent/gocommon/client" "github.com/joyent/gosdc/cloudapi" "github.com/joyent/gosign/auth" ) func registerJoyentCloudTests(creds *auth.Credentials) { gc.Suite(&LiveTests{creds: creds}) } type LiveTests struct { creds *auth.Credentials testClient *cloudapi.Client } func (s *LiveTests) SetUpTest(c *gc.C) { client := client.NewClient(s.creds.SdcEndpoint.URL, cloudapi.DefaultAPIVersion, s.creds, &cloudapi.Logger) c.Assert(client, gc.NotNil) s.testClient = cloudapi.New(client) c.Assert(s.testClient, gc.NotNil) } // Helper method to create a test key in the user account func (s *LiveTests) createKey(c *gc.C) { key, err := s.testClient.CreateKey(cloudapi.CreateKeyOpts{Name: "fake-key", Key: testKey}) c.Assert(err, gc.IsNil) c.Assert(key, gc.NotNil) c.Assert(key, gc.DeepEquals, &cloudapi.Key{Name: "fake-key", Fingerprint: testKeyFingerprint, Key: testKey}) } // Helper method to create a test virtual machine in the user account func (s *LiveTests) createMachine(c *gc.C) *cloudapi.Machine { machine, err := s.testClient.CreateMachine(cloudapi.CreateMachineOpts{Package: packageName, Image: imageId}) c.Assert(err, gc.IsNil) c.Assert(machine, gc.NotNil) // wait for machine to be provisioned for !s.pollMachineState(c, machine.Id, "running") { time.Sleep(1 * time.Second) } return machine } // Helper method to create a test virtual machine in the user account with the specified tags func (s *LiveTests) createMachineWithTags(c *gc.C, tags map[string]string) *cloudapi.Machine { machine, err := s.testClient.CreateMachine(cloudapi.CreateMachineOpts{Package: packageName, Image: imageId, Tags: tags}) c.Assert(err, gc.IsNil) c.Assert(machine, gc.NotNil) // wait for machine to be provisioned for !s.pollMachineState(c, machine.Id, "running") { time.Sleep(1 * time.Second) } return machine } // Helper method to test the state of a given VM func (s *LiveTests) pollMachineState(c *gc.C, machineId, state string) bool { machineConfig, err := s.testClient.GetMachine(machineId) c.Assert(err, gc.IsNil) return strings.EqualFold(machineConfig.State, state) } // Helper method to delete a test virtual machine once the test has executed func (s *LiveTests) deleteMachine(c *gc.C, machineId string) { err := s.testClient.StopMachine(machineId) c.Assert(err, gc.IsNil) // wait for machine to be stopped for !s.pollMachineState(c, machineId, "stopped") { time.Sleep(1 * time.Second) } err = s.testClient.DeleteMachine(machineId) c.Assert(err, gc.IsNil) } // Helper method to list virtual machine according to the specified filter func (s *LiveTests) listMachines(c *gc.C, filter *cloudapi.Filter) { var contains bool testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) machines, err := s.testClient.ListMachines(filter) c.Assert(err, gc.IsNil) c.Assert(machines, gc.NotNil) for _, m := range machines { if m.Id == testMachine.Id { contains = true break } } // result if !contains { c.Fatalf("Obtained machines [%s] do not contain test machine [%s]", machines, *testMachine) } } // Helper method to create a snapshot of a test virtual machine func (s *LiveTests) createMachineSnapshot(c *gc.C, machineId string) string { // generates a unique snapshot name using the current timestamp t := time.Now() snapshotName := "test-machine-snapshot-" + t.Format("20060102_150405") snapshot, err := s.testClient.CreateMachineSnapshot(machineId, cloudapi.SnapshotOpts{Name: snapshotName}) c.Assert(err, gc.IsNil) c.Assert(snapshot, gc.NotNil) c.Assert(snapshot, gc.DeepEquals, &cloudapi.Snapshot{Name: snapshotName, State: "queued"}) return snapshotName } // Helper method to create a test firewall rule func (s *LiveTests) createFirewallRule(c *gc.C) *cloudapi.FirewallRule { fwRule, err := s.testClient.CreateFirewallRule(cloudapi.CreateFwRuleOpts{Enabled: false, Rule: testFwRule}) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) c.Assert(fwRule.Rule, gc.Equals, testFwRule) c.Assert(fwRule.Enabled, gc.Equals, false) time.Sleep(10 * time.Second) return fwRule } // Helper method to a test firewall rule func (s *LiveTests) deleteFwRule(c *gc.C, fwRuleId string) { err := s.testClient.DeleteFirewallRule(fwRuleId) c.Assert(err, gc.IsNil) } // Keys API func (s *LiveTests) TestCreateKey(c *gc.C) { s.createKey(c) } func (s *LiveTests) TestListKeys(c *gc.C) { s.createKey(c) keys, err := s.testClient.ListKeys() c.Assert(err, gc.IsNil) c.Assert(keys, gc.NotNil) fakeKey := cloudapi.Key{Name: "fake-key", Fingerprint: testKeyFingerprint, Key: testKey} for _, k := range keys { if c.Check(k, gc.DeepEquals, fakeKey) { c.SucceedNow() } } c.Fatalf("Obtained keys [%s] do not contain test key [%s]", keys, fakeKey) } func (s *LiveTests) TestGetKeyByName(c *gc.C) { s.createKey(c) key, err := s.testClient.GetKey("fake-key") c.Assert(err, gc.IsNil) c.Assert(key, gc.NotNil) c.Assert(key, gc.DeepEquals, &cloudapi.Key{Name: "fake-key", Fingerprint: testKeyFingerprint, Key: testKey}) } func (s *LiveTests) TestGetKeyByFingerprint(c *gc.C) { s.createKey(c) key, err := s.testClient.GetKey(testKeyFingerprint) c.Assert(err, gc.IsNil) c.Assert(key, gc.NotNil) c.Assert(key, gc.DeepEquals, &cloudapi.Key{Name: "fake-key", Fingerprint: testKeyFingerprint, Key: testKey}) } func (s *LiveTests) TestDeleteKey(c *gc.C) { s.createKey(c) err := s.testClient.DeleteKey("fake-key") c.Assert(err, gc.IsNil) } // Packages API func (s *LiveTests) TestListPackages(c *gc.C) { pkgs, err := s.testClient.ListPackages(nil) c.Assert(err, gc.IsNil) c.Assert(pkgs, gc.NotNil) for _, pkg := range pkgs { c.Check(pkg.Name, gc.FitsTypeOf, string("")) c.Check(pkg.Memory, gc.FitsTypeOf, int(0)) c.Check(pkg.Disk, gc.FitsTypeOf, int(0)) c.Check(pkg.Swap, gc.FitsTypeOf, int(0)) c.Check(pkg.VCPUs, gc.FitsTypeOf, int(0)) c.Check(pkg.Default, gc.FitsTypeOf, bool(false)) c.Check(pkg.Id, gc.FitsTypeOf, string("")) c.Check(pkg.Version, gc.FitsTypeOf, string("")) c.Check(pkg.Description, gc.FitsTypeOf, string("")) c.Check(pkg.Group, gc.FitsTypeOf, string("")) } } func (s *LiveTests) TestListPackagesWithFilter(c *gc.C) { filter := cloudapi.NewFilter() filter.Set("memory", "1024") pkgs, err := s.testClient.ListPackages(filter) c.Assert(err, gc.IsNil) c.Assert(pkgs, gc.NotNil) for _, pkg := range pkgs { c.Check(pkg.Name, gc.FitsTypeOf, string("")) c.Check(pkg.Memory, gc.Equals, 1024) c.Check(pkg.Disk, gc.FitsTypeOf, int(0)) c.Check(pkg.Swap, gc.FitsTypeOf, int(0)) c.Check(pkg.VCPUs, gc.FitsTypeOf, int(0)) c.Check(pkg.Default, gc.FitsTypeOf, bool(false)) c.Check(pkg.Id, gc.FitsTypeOf, string("")) c.Check(pkg.Version, gc.FitsTypeOf, string("")) c.Check(pkg.Description, gc.FitsTypeOf, string("")) c.Check(pkg.Group, gc.FitsTypeOf, string("")) } } func (s *LiveTests) TestGetPackageFromName(c *gc.C) { key, err := s.testClient.GetPackage(packageName) c.Assert(err, gc.IsNil) c.Assert(key, gc.NotNil) c.Assert(key, gc.DeepEquals, &cloudapi.Package{ Name: packageName, Memory: 1024, Disk: 33792, Swap: 2048, VCPUs: 0, Default: false, Id: packageId, Version: "1.0.0", Description: "Standard 1 GB RAM 0.25 vCPU and bursting 33 GB Disk", Group: "Standard", }) } func (s *LiveTests) TestGetPackageFromId(c *gc.C) { key, err := s.testClient.GetPackage(packageId) c.Assert(err, gc.IsNil) c.Assert(key, gc.NotNil) c.Assert(key, gc.DeepEquals, &cloudapi.Package{ Name: packageName, Memory: 1024, Disk: 33792, Swap: 2048, VCPUs: 0, Default: false, Id: packageId, Version: "1.0.0", Description: "Standard 1 GB RAM 0.25 vCPU and bursting 33 GB Disk", Group: "Standard", }) } // Images API func (s *LiveTests) TestListImages(c *gc.C) { imgs, err := s.testClient.ListImages(nil) c.Assert(err, gc.IsNil) c.Assert(imgs, gc.NotNil) for _, img := range imgs { c.Check(img.Id, gc.FitsTypeOf, string("")) c.Check(img.Name, gc.FitsTypeOf, string("")) c.Check(img.OS, gc.FitsTypeOf, string("")) c.Check(img.Version, gc.FitsTypeOf, string("")) c.Check(img.Type, gc.FitsTypeOf, string("")) c.Check(img.Description, gc.FitsTypeOf, string("")) c.Check(img.Requirements, gc.FitsTypeOf, map[string]interface{}{"key": "value"}) c.Check(img.Homepage, gc.FitsTypeOf, string("")) c.Check(img.PublishedAt, gc.FitsTypeOf, string("")) c.Check(img.Public, gc.FitsTypeOf, string("")) c.Check(img.State, gc.FitsTypeOf, string("")) c.Check(img.Tags, gc.FitsTypeOf, map[string]string{"key": "value"}) c.Check(img.EULA, gc.FitsTypeOf, string("")) c.Check(img.ACL, gc.FitsTypeOf, []string{"", ""}) } } func (s *LiveTests) TestListImagesWithFilter(c *gc.C) { filter := cloudapi.NewFilter() filter.Set("os", "smartos") imgs, err := s.testClient.ListImages(filter) c.Assert(err, gc.IsNil) c.Assert(imgs, gc.NotNil) for _, img := range imgs { c.Check(img.Id, gc.FitsTypeOf, string("")) c.Check(img.Name, gc.FitsTypeOf, string("")) c.Check(img.OS, gc.Equals, "smartos") c.Check(img.Version, gc.FitsTypeOf, string("")) c.Check(img.Type, gc.FitsTypeOf, string("")) c.Check(img.Description, gc.FitsTypeOf, string("")) c.Check(img.Requirements, gc.FitsTypeOf, map[string]interface{}{"key": "value"}) c.Check(img.Homepage, gc.FitsTypeOf, string("")) c.Check(img.PublishedAt, gc.FitsTypeOf, string("")) c.Check(img.Public, gc.FitsTypeOf, string("")) c.Check(img.State, gc.FitsTypeOf, string("")) c.Check(img.Tags, gc.FitsTypeOf, map[string]string{"key": "value"}) c.Check(img.EULA, gc.FitsTypeOf, string("")) c.Check(img.ACL, gc.FitsTypeOf, []string{"", ""}) } } // TODO Add test for deleteImage, exportImage and CreateMachineFormIMage func (s *LiveTests) TestGetImage(c *gc.C) { requirements := map[string]interface{}{} img, err := s.testClient.GetImage(imageId) c.Assert(err, gc.IsNil) c.Assert(img, gc.NotNil) c.Assert(img, gc.DeepEquals, &cloudapi.Image{ Id: imageId, Name: "base", Version: "13.1.0", OS: "smartos", Type: "smartmachine", Description: "A 32-bit SmartOS image with just essential packages installed. Ideal for users who are comfortable with setting up their own environment and tools.", Requirements: requirements, PublishedAt: "2013-04-26T15:16:02Z", }) } // Datacenter API func (s *LiveTests) TestListDatacenters(c *gc.C) { dcs, err := s.testClient.ListDatacenters() c.Assert(err, gc.IsNil) c.Assert(dcs, gc.HasLen, 4) c.Assert(dcs["us-west-1"], gc.Equals, "https://us-west-1.api.joyentcloud.com") } func (s *LiveTests) TestGetDatacenter(c *gc.C) { dc, err := s.testClient.GetDatacenter("us-west-1") c.Assert(err, gc.IsNil) c.Assert(dc, gc.Equals, "https://us-west-1.api.joyentcloud.com") } func (s *LiveTests) TestCreateMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) c.Assert(testMachine.Type, gc.Equals, "smartmachine") c.Assert(testMachine.Dataset, gc.Equals, "sdc:sdc:base:13.1.0") c.Assert(testMachine.Memory, gc.Equals, 1024) c.Assert(testMachine.Disk, gc.Equals, 33792) c.Assert(testMachine.Package, gc.Equals, packageName) c.Assert(testMachine.Image, gc.Equals, imageId) } func (s *LiveTests) TestCreateMachineWithTags(c *gc.C) { tags := map[string]string{"tag.tag1": "value1", "tag.tag2": "value2"} testMachine := s.createMachineWithTags(c, tags) defer s.deleteMachine(c, testMachine.Id) c.Assert(testMachine.Type, gc.Equals, "smartmachine") c.Assert(testMachine.Dataset, gc.Equals, "sdc:sdc:base:13.1.0") c.Assert(testMachine.Memory, gc.Equals, 1024) c.Assert(testMachine.Disk, gc.Equals, 33792) c.Assert(testMachine.Package, gc.Equals, packageName) c.Assert(testMachine.Image, gc.Equals, imageId) c.Assert(testMachine.Tags, gc.DeepEquals, map[string]string{"tag1": "value1", "tag2": "value2"}) } func (s *LiveTests) TestListMachines(c *gc.C) { s.listMachines(c, nil) } func (s *LiveTests) TestListMachinesWithFilter(c *gc.C) { filter := cloudapi.NewFilter() filter.Set("memory", "1024") s.listMachines(c, filter) } func (s *LiveTests) TestCountMachines(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) count, err := s.testClient.CountMachines() c.Assert(err, gc.IsNil) c.Assert(count >= 1, gc.Equals, true) } func (s *LiveTests) TestGetMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) machine, err := s.testClient.GetMachine(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(machine, gc.NotNil) c.Assert(machine.Equals(*testMachine), gc.Equals, true) } func (s *LiveTests) TestStopMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.StopMachine(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestStartMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.StopMachine(testMachine.Id) c.Assert(err, gc.IsNil) // wait for machine to be stopped for !s.pollMachineState(c, testMachine.Id, "stopped") { time.Sleep(1 * time.Second) } err = s.testClient.StartMachine(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestRebootMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.RebootMachine(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestRenameMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.RenameMachine(testMachine.Id, "test-machine-renamed") c.Assert(err, gc.IsNil) renamed, err := s.testClient.GetMachine(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(renamed.Name, gc.Equals, "test-machine-renamed") } func (s *LiveTests) TestResizeMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.ResizeMachine(testMachine.Id, "g3-standard-1.75-smartos") c.Assert(err, gc.IsNil) resized, err := s.testClient.GetMachine(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(resized.Package, gc.Equals, "g3-standard-1.75-smartos") } func (s *LiveTests) TestListMachinesFirewallRules(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) fwRules, err := s.testClient.ListMachineFirewallRules(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(fwRules, gc.NotNil) } func (s *LiveTests) TestEnableFirewallMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.EnableFirewallMachine(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestDisableFirewallMachine(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.DisableFirewallMachine(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestCreateMachineSnapshot(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) s.createMachineSnapshot(c, testMachine.Id) } func (s *LiveTests) TestStartMachineFromShapshot(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) snapshotName := s.createMachineSnapshot(c, testMachine.Id) err := s.testClient.StopMachine(testMachine.Id) c.Assert(err, gc.IsNil) // wait for machine to be stopped for !s.pollMachineState(c, testMachine.Id, "stopped") { time.Sleep(1 * time.Second) } err = s.testClient.StartMachineFromSnapshot(testMachine.Id, snapshotName) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestListMachineSnapshots(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) s.createMachineSnapshot(c, testMachine.Id) snapshots, err := s.testClient.ListMachineSnapshots(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(snapshots, gc.HasLen, 1) } func (s *LiveTests) TestGetMachineSnapshot(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) snapshotName := s.createMachineSnapshot(c, testMachine.Id) snapshot, err := s.testClient.GetMachineSnapshot(testMachine.Id, snapshotName) c.Assert(err, gc.IsNil) c.Assert(snapshot, gc.NotNil) c.Assert(snapshot, gc.DeepEquals, &cloudapi.Snapshot{Name: snapshotName, State: "created"}) } func (s *LiveTests) TestDeleteMachineSnapshot(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) snapshotName := s.createMachineSnapshot(c, testMachine.Id) err := s.testClient.DeleteMachineSnapshot(testMachine.Id, snapshotName) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestUpdateMachineMetadata(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) md, err := s.testClient.UpdateMachineMetadata(testMachine.Id, map[string]string{"test-metadata": "md value", "test": "test"}) metadata := map[string]interface{}{"root_authorized_keys": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyucy41MNcPxEkNUneeRT0j4mXX5zzW4cFYZ0G/Wpqrb/A+JZV6xlwmAwsGPAvebeEp40CSc0gzauR0nsQ+0Hefdp+dHdEEZlZ7WhedknponA8cQURU38cmTnGweaw2B0+vkULo5AUPAjv0Y1nGPZVlKWNeR6NJhq51pEtj4eLYCJ+kylHEIjQbP5Q1LQHxxotoY29N/xMx+ZVYGprHUJ5ihMOC1nrz2kqUjbCvvMLC0yzAI3vfKtL14BQs9Aq9ggl9oZZylmsgy9CnrPa5t98/wqG+snGyrPSL27km0rll1Jz6xcraGkXQP0adFJxw7mFrXItAt6TUyAuLoohhjHd daniele@lightman.local\n", "origin": "cloudapi", "creator_uuid": "ff0c4a2b-f89a-4f14-81ee-5b31e7c89ece", "test": "test", "test-metadata": "md value", "context": map[string]interface{}{"caller": map[string]interface{}{"type": "signature", "ip": "127.0.0.1", "keyId": "/dstroppa/keys/12:c3:a7:cb:a2:29:e2:90:88:3f:04:53:3b:4e:75:40"}, "params": map[string]interface{}{"account": "dstroppa", "machine": testMachine.Id, "test": "test", "test-metadata": "md value"}}} c.Assert(err, gc.IsNil) c.Assert(md, gc.NotNil) c.Assert(md, gc.DeepEquals, metadata) } func (s *LiveTests) TestGetMachineMetadata(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) md, err := s.testClient.GetMachineMetadata(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(md, gc.NotNil) c.Assert(md, gc.HasLen, 5) } func (s *LiveTests) TestDeleteMachineMetadata(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) _, err := s.testClient.UpdateMachineMetadata(testMachine.Id, map[string]string{"test-metadata": "md value"}) c.Assert(err, gc.IsNil) // allow update to propagate time.Sleep(10 * time.Second) err = s.testClient.DeleteMachineMetadata(testMachine.Id, "test-metadata") c.Assert(err, gc.IsNil) } func (s *LiveTests) TestDeleteAllMachineMetadata(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.DeleteAllMachineMetadata(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestAddMachineTags(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) machineTags := map[string]string{"test-tag": "test-tag-value", "test": "test", "tag1": "tagtag"} tags, err := s.testClient.AddMachineTags(testMachine.Id, map[string]string{"test-tag": "test-tag-value", "test": "test", "tag1": "tagtag"}) c.Assert(err, gc.IsNil) c.Assert(tags, gc.NotNil) c.Assert(tags, gc.DeepEquals, machineTags) } func (s *LiveTests) TestReplaceMachineTags(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) machineTags := map[string]string{"origin": "cloudapi", "creator_uuid": "ff0c4a2b-f89a-4f14-81ee-5b31e7c89ece", "test-tag": "test-tag-value", "test": "test tag", "tag1": "tag2"} tags, err := s.testClient.ReplaceMachineTags(testMachine.Id, map[string]string{"test-tag": "test-tag-value", "test": "test tag", "tag1": "tag2"}) c.Assert(err, gc.IsNil) c.Assert(tags, gc.NotNil) c.Assert(tags, gc.DeepEquals, machineTags) } func (s *LiveTests) TestListMachineTags(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) tags, err := s.testClient.ListMachineTags(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(tags, gc.NotNil) c.Assert(tags, gc.HasLen, 5) } func (s *LiveTests) TestGetMachineTag(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) _, err := s.testClient.AddMachineTags(testMachine.Id, map[string]string{"test-tag": "test-tag-value"}) c.Assert(err, gc.IsNil) // allow update to propagate time.Sleep(15 * time.Second) tag, err := s.testClient.GetMachineTag(testMachine.Id, "test-tag") c.Assert(err, gc.IsNil) c.Assert(tag, gc.NotNil) c.Assert(tag, gc.Equals, "test-tag-value") } func (s *LiveTests) TestDeleteMachineTag(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) _, err := s.testClient.AddMachineTags(testMachine.Id, map[string]string{"test-tag": "test-tag-value"}) c.Assert(err, gc.IsNil) // allow update to propagate time.Sleep(15 * time.Second) err = s.testClient.DeleteMachineTag(testMachine.Id, "test-tag") c.Assert(err, gc.IsNil) } func (s *LiveTests) TestDeleteMachineTags(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) err := s.testClient.DeleteMachineTags(testMachine.Id) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestMachineAudit(c *gc.C) { testMachine := s.createMachine(c) defer s.deleteMachine(c, testMachine.Id) actions, err := s.testClient.MachineAudit(testMachine.Id) c.Assert(err, gc.IsNil) c.Assert(actions, gc.NotNil) c.Assert(len(actions) > 0, gc.Equals, true) } func (s *LiveTests) TestDeleteMachine(c *gc.C) { testMachine := s.createMachine(c) s.deleteMachine(c, testMachine.Id) } // Analytics API // FirewallRules API func (s *LiveTests) TestCreateFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) // cleanup s.deleteFwRule(c, testFwRule.Id) } func (s *LiveTests) TestListFirewallRules(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) rules, err := s.testClient.ListFirewallRules() c.Assert(err, gc.IsNil) c.Assert(rules, gc.NotNil) } func (s *LiveTests) TestGetFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) fwRule, err := s.testClient.GetFirewallRule(testFwRule.Id) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) c.Assert((*fwRule), gc.DeepEquals, (*testFwRule)) } func (s *LiveTests) TestUpdateFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) fwRule, err := s.testClient.UpdateFirewallRule(testFwRule.Id, cloudapi.CreateFwRuleOpts{Rule: testUpdatedFwRule}) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) c.Assert(fwRule.Rule, gc.Equals, testUpdatedFwRule) } func (s *LiveTests) TestEnableFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) fwRule, err := s.testClient.EnableFirewallRule((*testFwRule).Id) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) } func (s *LiveTests) TestListFirewallRuleMachines(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) machines, err := s.testClient.ListFirewallRuleMachines((*testFwRule).Id) c.Assert(err, gc.IsNil) c.Assert(machines, gc.NotNil) } func (s *LiveTests) TestDisableFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) fwRule, err := s.testClient.DisableFirewallRule((*testFwRule).Id) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) } func (s *LiveTests) TestDeleteFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) s.deleteFwRule(c, testFwRule.Id) } // Networks API func (s *LiveTests) TestListNetworks(c *gc.C) { nets, err := s.testClient.ListNetworks() c.Assert(err, gc.IsNil) c.Assert(nets, gc.NotNil) } func (s *LiveTests) TestGetNetwork(c *gc.C) { net, err := s.testClient.GetNetwork(networkId) c.Assert(err, gc.IsNil) c.Assert(net, gc.NotNil) c.Assert(net, gc.DeepEquals, &cloudapi.Network{ Id: networkId, Name: "Joyent-SDC-Public", Public: true, Description: "", }) } �������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/cloudapi/cloudapi.go�����������������������������������0000644�0000153�0000161�00000143140�12321735720�024633� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* The gosdc/cloudapi package interacts with the Cloud API (http://apidocs.joyent.com/cloudapi/). Licensed under LGPL v3. Copyright (c) 2013 Joyent Inc. Written by Daniele Stroppa <daniele.stroppa@joyent.com> */ package cloudapi import ( "encoding/json" "fmt" "net/http" "net/url" "path" "github.com/joyent/gocommon/client" "github.com/joyent/gocommon/errors" jh "github.com/joyent/gocommon/http" "github.com/juju/loggo" ) const ( // The default version of the Cloud API to use DefaultAPIVersion = "~7.0" // CloudAPI URL parts apiKeys = "keys" apiPackages = "packages" apiImages = "images" apiDatacenters = "datacenters" apiMachines = "machines" apiMetadata = "metadata" apiSnapshots = "snapshots" apiTags = "tags" apiAnalytics = "analytics" apiInstrumentations = "instrumentations" apiInstrumentationsValue = "value" apiInstrumentationsRaw = "raw" apiInstrumentationsHeatmap = "heatmap" apiInstrumentationsImage = "image" apiInstrumentationsDetails = "details" apiUsage = "usage" apiAudit = "audit" apiFirewallRules = "fwrules" apiFirewallRulesEnable = "enable" apiFirewallRulesDisable = "disable" apiNetworks = "networks" // CloudAPI actions actionExport = "export" actionStop = "stop" actionStart = "start" actionReboot = "reboot" actionResize = "resize" actionRename = "rename" actionEnableFw = "enable_firewall" actionDisableFw = "disable_firewall" ) // Logger for this package var Logger = loggo.GetLogger("gosdc.cloudapi") // Client provides a means to access the Joyent CloudAPI type Client struct { client client.Client } // New creates a new Client. func New(client client.Client) *Client { return &Client{client} } // Filter represents a filter that can be applied to an API request. type Filter struct { v url.Values } // NewFilter creates a new Filter. func NewFilter() *Filter { return &Filter{make(url.Values)} } // Set a value for the specified filter. func (f *Filter) Set(filter, value string) { f.v.Set(filter, value) } // Add a value for the specified filter. func (f *Filter) Add(filter, value string) { f.v.Add(filter, value) } // request represents an API request type request struct { method string url string filter *Filter reqValue interface{} reqHeader http.Header resp interface{} respHeader *http.Header expectedStatus int } // Helper method to send an API request func (c *Client) sendRequest(req request) (*jh.ResponseData, error) { request := jh.RequestData{ ReqValue: req.reqValue, ReqHeaders: req.reqHeader, } if req.filter != nil { request.Params = &req.filter.v } if req.expectedStatus == 0 { req.expectedStatus = http.StatusOK } respData := jh.ResponseData{ RespValue: req.resp, RespHeaders: req.respHeader, ExpectedStatus: []int{req.expectedStatus}, } err := c.client.SendRequest(req.method, req.url, "", &request, &respData) return &respData, err } // Helper method to create the API URL func makeURL(parts ...string) string { return path.Join(parts...) } // Key represent a public key type Key struct { Name string // Name for the key Fingerprint string // Key Fingerprint Key string // OpenSSH formatted public key } /*func (k Key) Equals(other Key) bool { if k.Name == other.Name && k.Fingerprint == other.Fingerprint && k.Key == other.Key { return true } return false }*/ // CreateKeyOpts represent the option that can be specified // when creating a new key. type CreateKeyOpts struct { Name string `json:"name"` // Name for the key, optional Key string `json:"key"` // OpenSSH formatted public key } // Returns a list of public keys registered with a specific account. // See API docs: http://apidocs.joyent.com/cloudapi/#ListKeys func (c *Client) ListKeys() ([]Key, error) { var resp []Key req := request{ method: client.GET, url: apiKeys, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of keys") } return resp, nil } // Returns the key identified by keyName. // See API docs: http://apidocs.joyent.com/cloudapi/#GetKey func (c *Client) GetKey(keyName string) (*Key, error) { var resp Key req := request{ method: client.GET, url: makeURL(apiKeys, keyName), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get key with name: %s", keyName) } return &resp, nil } // Creates a new key with the specified options. // See API docs: http://apidocs.joyent.com/cloudapi/#CreateKey func (c *Client) CreateKey(opts CreateKeyOpts) (*Key, error) { var resp Key req := request{ method: client.POST, url: apiKeys, reqValue: opts, resp: &resp, expectedStatus: http.StatusCreated, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to create key with name: %s", opts.Name) } return &resp, nil } // Deletes the key identified by keyName. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteKey func (c *Client) DeleteKey(keyName string) error { req := request{ method: client.DELETE, url: makeURL(apiKeys, keyName), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete key with name: %s", keyName) } return nil } // A Package represent a named collections of resources that are used to describe the ‘sizes’ // of either a smart machine or a virtual machine. type Package struct { Name string // Name for the package Memory int // Memory available (in Mb) Disk int // Disk space available (in Gb) Swap int // Swap memory available (in Mb) VCPUs int // Number of VCPUs for the package Default bool // Indicates whether this is the default package in the datacenter Id string // Unique identifier for the package Version string // Version for the package Group string // Group this package belongs to Description string // Human friendly description for the package } // Provides a list of packages available in the datacenter. // See API docs: http://apidocs.joyent.com/cloudapi/#ListPackages func (c *Client) ListPackages(filter *Filter) ([]Package, error) { var resp []Package req := request{ method: client.GET, url: apiPackages, filter: filter, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of packages") } return resp, nil } // Returns the package specified by packageName. NOTE: packageName can specify // either the package name or package Id. // See API docs: http://apidocs.joyent.com/cloudapi/#GetPackage func (c *Client) GetPackage(packageName string) (*Package, error) { var resp Package req := request{ method: client.GET, url: makeURL(apiPackages, packageName), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get package with name: %s", packageName) } return &resp, nil } // Image represent the software packages that will be available on newly provisioned machines type Image struct { Id string // Unique identifier for the image Name string // Image friendly name OS string // Underlying operating system Version string // Image version Type string // Image type, one of 'smartmachine' or 'virtualmachine' Description string // Image description Requirements map[string]interface{} // Minimum requirements for provisioning a machine with this image, e.g. 'password' indicates that a password must be provided Homepage string // URL for a web page including detailed information for this image (new in API version 7.0) PublishedAt string `json:"published_at"` // Time this image has been made publicly available (new in API version 7.0) Public string // Indicates if the image is publicly available (new in API version 7.1) State string // Current image state. One of 'active', 'unactivated', 'disabled', 'creating', 'failed' (new in API version 7.1) Tags map[string]string // A map of key/value pairs that allows clients to categorize images by any given criteria (new in API version 7.1) EULA string // URL of the End User License Agreement (EULA) for the image (new in API version 7.1) ACL []string // An array of account UUIDs given access to a private image. The field is only relevant to private images (new in API version 7.1) Owner string // The UUID of the user owning the image } // ExportImageOpts represent the option that can be specified // when exporting an image. type ExportImageOpts struct { MantaPath string `json:"manta_path"` // The Manta path prefix to use when exporting the image } // MantaLocation represent the properties that allow a user // to retrieve the image file and manifest from Manta type MantaLocation struct { MantaURL string `json:"manta_url"` // Manta datacenter URL ImagePath string `json:"image_path"` // Path to the image ManifestPath string `json:"manifest_path"` // Path to the image manifest } // CreateImageFromMachineOpts represent the option that can be specified // when creating a new image from an existing machine. type CreateImageFromMachineOpts struct { Machine string `json:"machine"` // The machine UUID from which the image is to be created Name string `json:"name"` // Image name Version string `json:"version"` // Image version Description string `json:"description"` // Image description Homepage string `json:"homepage"` // URL for a web page including detailed information for this image EULA string `json:"eula"` // URL of the End User License Agreement (EULA) for the image ACL []string `json:"acl"` // An array of account UUIDs given access to a private image. The field is only relevant to private images Tags map[string]string `json:"tags"` // A map of key/value pairs that allows clients to categorize images by any given criteria } // Provides a list of images available in the datacenter. // See API docs: http://apidocs.joyent.com/cloudapi/#ListImages func (c *Client) ListImages(filter *Filter) ([]Image, error) { var resp []Image req := request{ method: client.GET, url: apiImages, filter: filter, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of images") } return resp, nil } // Returns the image specified by imageId. // See API docs: http://apidocs.joyent.com/cloudapi/#GetImage func (c *Client) GetImage(imageId string) (*Image, error) { var resp Image req := request{ method: client.GET, url: makeURL(apiImages, imageId), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get image with id: %s", imageId) } return &resp, nil } // (Beta) Delete the image specified by imageId. Must be image owner to do so. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteImage func (c *Client) DeleteImage(imageId string) error { req := request{ method: client.DELETE, url: makeURL(apiImages, imageId), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete image with id: %s", imageId) } return nil } // (Beta) Exports an image to the specified Manta path. // See API docs: http://apidocs.joyent.com/cloudapi/#ListImages func (c *Client) ExportImage(imageId string, opts ExportImageOpts) (*MantaLocation, error) { var resp MantaLocation req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s", apiImages, imageId, actionExport), reqValue: opts, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to export image %s to %s", imageId, opts.MantaPath) } return &resp, nil } // (Beta) Create a new custom image from a machine. // See API docs: http://apidocs.joyent.com/cloudapi/#ListImages func (c *Client) CreateImageFromMachine(opts CreateImageFromMachineOpts) (*Image, error) { var resp Image req := request{ method: client.POST, url: apiImages, reqValue: opts, resp: &resp, expectedStatus: http.StatusCreated, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to create image from machine %s", opts.Machine) } return &resp, nil } // Provides a list of all datacenters this cloud is aware of. // See API docs: http://apidocs.joyent.com/cloudapi/#ListDatacenters func (c *Client) ListDatacenters() (map[string]interface{}, error) { var resp map[string]interface{} req := request{ method: client.GET, url: apiDatacenters, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of datcenters") } return resp, nil } // Gets an individual datacenter by name. Returns an HTTP redirect to your client, // the datacenter URL is in the Location header. // See API docs: http://apidocs.joyent.com/cloudapi/#GetDatacenter func (c *Client) GetDatacenter(datacenterName string) (string, error) { var respHeader http.Header req := request{ method: client.GET, url: makeURL(apiDatacenters, datacenterName), respHeader: &respHeader, expectedStatus: http.StatusFound, } respData, err := c.sendRequest(req) if err != nil { return "", errors.Newf(err, "failed to get datacenter with name: %s", datacenterName) } return respData.RespHeaders.Get("Location"), nil } // Machine represent a provisioned virtual machines type Machine struct { Id string // Unique identifier for the image Name string // Machine friendly name Type string // Machine type, one of 'smartmachine' or 'virtualmachine' State string // Current state of the machine Dataset string // The dataset URN the machine was provisioned with. For new images/datasets this value will be the dataset id, i.e, same value than the image attribute Memory int // The amount of memory the machine has (in Mb) Disk int // The amount of disk the machine has (in Gb) IPs []string // The IP addresses the machine has Metadata map[string]string // Map of the machine metadata, e.g. authorized-keys Tags map[string]string // Map of the machine tags Created string // When the machine was created Updated string // When the machine was updated Package string // The name of the package used to create the machine Image string // The image id the machine was provisioned with PrimaryIP string // The primary (public) IP address for the machine Networks []string // The network IDs for the machine } // Helper method to compare two machines. Ignores state and timestamps. func (m Machine) Equals(other Machine) bool { if m.Id == other.Id && m.Name == other.Name && m.Type == other.Type && m.Dataset == other.Dataset && m.Memory == other.Memory && m.Disk == other.Disk && m.Package == other.Package && m.Image == other.Image && m.compareIPs(other) && m.compareMetadata(other) { return true } return false } // Helper method to compare two machines IPs func (m Machine) compareIPs(other Machine) bool { if len(m.IPs) != len(other.IPs) { return false } for i, v := range m.IPs { if v != other.IPs[i] { return false } } return true } // Helper method to compare two machines metadata func (m Machine) compareMetadata(other Machine) bool { if len(m.Metadata) != len(other.Metadata) { return false } for k, v := range m.Metadata { if v != other.Metadata[k] { return false } } return true } // CreateMachineOpts represent the option that can be specified // when creating a new machine. type CreateMachineOpts struct { Name string `json:"name"` // Machine friendly name, default is a randomly generated name Package string `json:"package"` // Name of the package to use on provisioning Image string `json:"image"` // The image UUID Networks []string `json:"networks"` // Desired networks IDs Metadata map[string]string `json:"-"` // An arbitrary set of metadata key/value pairs can be set at provision time Tags map[string]string `json:"-"` // An arbitrary set of tags can be set at provision time FirewallEnabled bool `json:"firewall_enabled"` // Completely enable or disable firewall for this machine (new in API version 7.0) } // Snapshot represent a point in time state of a machine. type Snapshot struct { Name string // Snapshot name State string // Snapshot state } // SnapshotOpts represent the option that can be specified // when creating a new machine snapshot. type SnapshotOpts struct { Name string `json:"name"` // Snapshot name } // AuditAction represents an action/event accomplished by a machine. type AuditAction struct { Action string // Action name Parameters map[string]interface{} // Original set of parameters sent when the action was requested Time string // When the action finished Success string // Either 'yes' or 'no', depending on the action successfulness Caller Caller // Account requesting the action } // Caller represents an account requesting an action. type Caller struct { Type string // Authentication type for the action request. One of 'basic', 'operator', 'signature' or 'token' User string // When the authentication type is 'basic', this member will be present and include user login IP string // The IP addresses this from which the action was requested. Not present if type is 'operator' KeyId string // When authentication type is either 'signature' or 'token', SSH key identifier } // appendJSON marshals the given attribute value and appends it as an encoded value to the given json data. // The newly encode (attr, value) is inserted just before the closing "}" in the json data. func appendJSON(data []byte, attr string, value interface{}) ([]byte, error) { newData, err := json.Marshal(&value) if err != nil { return nil, err } strData := string(data) result := fmt.Sprintf(`%s, "%s":%s}`, strData[:len(strData)-1], attr, string(newData)) return []byte(result), nil } type jsonOpts CreateMachineOpts func (opts CreateMachineOpts) MarshalJSON() ([]byte, error) { var jo jsonOpts = jsonOpts(opts) data, err := json.Marshal(&jo) if err != nil { return nil, err } for k, v := range opts.Tags { data, err = appendJSON(data, k, v) if err != nil { return nil, err } } for k, v := range opts.Metadata { data, err = appendJSON(data, k, v) if err != nil { return nil, err } } return data, nil } // Lists all machines on record for an account. // You can paginate this API by passing in offset, and limit // See API docs: http://apidocs.joyent.com/cloudapi/#ListMachines func (c *Client) ListMachines(filter *Filter) ([]Machine, error) { var resp []Machine req := request{ method: client.GET, url: apiMachines, filter: filter, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of machines") } return resp, nil } // Returns the number of machines on record for an account. // See API docs: http://apidocs.joyent.com/cloudapi/#ListMachines func (c *Client) CountMachines() (int, error) { var resp int req := request{ method: client.HEAD, url: apiMachines, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return -1, errors.Newf(err, "failed to get count of machines") } return resp, nil } // Returns the machine specified by machineId. // See API docs: http://apidocs.joyent.com/cloudapi/#GetMachine func (c *Client) GetMachine(machineId string) (*Machine, error) { var resp Machine req := request{ method: client.GET, url: makeURL(apiMachines, machineId), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get machine with id: %s", machineId) } return &resp, nil } // Creates a new machine with the options specified. // See API docs: http://apidocs.joyent.com/cloudapi/#CreateMachine func (c *Client) CreateMachine(opts CreateMachineOpts) (*Machine, error) { var resp Machine req := request{ method: client.POST, url: apiMachines, reqValue: opts, resp: &resp, expectedStatus: http.StatusCreated, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to create machine with name: %s", opts.Name) } return &resp, nil } // Stops a running machine. // See API docs: http://apidocs.joyent.com/cloudapi/#StopMachine func (c *Client) StopMachine(machineId string) error { req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineId, actionStop), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to stop machine with id: %s", machineId) } return nil } // Starts a stopped machine. // See API docs: http://apidocs.joyent.com/cloudapi/#StartMachine func (c *Client) StartMachine(machineId string) error { req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineId, actionStart), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to start machine with id: %s", machineId) } return nil } // Reboots (stop followed by a start) a machine. // See API docs: http://apidocs.joyent.com/cloudapi/#RebootMachine func (c *Client) RebootMachine(machineId string) error { req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineId, actionReboot), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to reboot machine with id: %s", machineId) } return nil } // Allows you to resize a SmartMachine. Virtual machines can also be resized, // but only resizing virtual machines to a higher capacity package is supported. // See API docs: http://apidocs.joyent.com/cloudapi/#ResizeMachine func (c *Client) ResizeMachine(machineId, packageName string) error { req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s&package=%s", apiMachines, machineId, actionResize, packageName), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to resize machine with id: %s", machineId) } return nil } // Renames an existing machine. // See API docs: http://apidocs.joyent.com/cloudapi/#RenameMachine func (c *Client) RenameMachine(machineId, machineName string) error { req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s&name=%s", apiMachines, machineId, actionRename, machineName), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to rename machine with id: %s", machineId) } return nil } // List all the firewall rules for the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#ListMachineFirewallRules func (c *Client) ListMachineFirewallRules(machineId string) ([]FirewallRule, error) { var resp []FirewallRule req := request{ method: client.GET, url: makeURL(apiMachines, machineId, apiFirewallRules), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of firewall rules for machine with id %s", machineId) } return resp, nil } // Enable the firewall for the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#EnableMachineFirewall func (c *Client) EnableFirewallMachine(machineId string) error { req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineId, actionEnableFw), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to enable firewall on machine with id: %s", machineId) } return nil } // Disable the firewall for the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#DisableMachineFirewall func (c *Client) DisableFirewallMachine(machineId string) error { req := request{ method: client.POST, url: fmt.Sprintf("%s/%s?action=%s", apiMachines, machineId, actionDisableFw), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to disable firewall on machine with id: %s", machineId) } return nil } // Creates a new snapshot for the machine with the options specified. // See API docs: http://apidocs.joyent.com/cloudapi/#CreateMachineSnapshot func (c *Client) CreateMachineSnapshot(machineId string, opts SnapshotOpts) (*Snapshot, error) { var resp Snapshot req := request{ method: client.POST, url: makeURL(apiMachines, machineId, apiSnapshots), reqValue: opts, resp: &resp, expectedStatus: http.StatusCreated, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to create snapshot %s from machine with id %s", opts.Name, machineId) } return &resp, nil } // Start the machine from the specified snapshot. Machine must be in 'stopped' state. // See API docs: http://apidocs.joyent.com/cloudapi/#StartMachineFromSnapshot func (c *Client) StartMachineFromSnapshot(machineId, snapshotName string) error { req := request{ method: client.POST, url: makeURL(apiMachines, machineId, apiSnapshots, snapshotName), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to start machine with id %s from snapshot %s", machineId, snapshotName) } return nil } // List all snapshots for the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#ListMachineSnapshots func (c *Client) ListMachineSnapshots(machineId string) ([]Snapshot, error) { var resp []Snapshot req := request{ method: client.GET, url: makeURL(apiMachines, machineId, apiSnapshots), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of snapshots for machine with id %s", machineId) } return resp, nil } // Returns the state of the specified snapshot. // See API docs: http://apidocs.joyent.com/cloudapi/#GetMachineSnapshot func (c *Client) GetMachineSnapshot(machineId, snapshotName string) (*Snapshot, error) { var resp Snapshot req := request{ method: client.GET, url: makeURL(apiMachines, machineId, apiSnapshots, snapshotName), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get snapshot %s for machine with id %s", snapshotName, machineId) } return &resp, nil } // Deletes the specified snapshot. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineSnapshot func (c *Client) DeleteMachineSnapshot(machineId, snapshotName string) error { req := request{ method: client.DELETE, url: makeURL(apiMachines, machineId, apiSnapshots, snapshotName), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete snapshot %s for machine with id %s", snapshotName, machineId) } return nil } // Updates the metadata for a given machine. // Any metadata keys passed in here are created if they do not exist, and overwritten if they do. // See API docs: http://apidocs.joyent.com/cloudapi/#UpdateMachineMetadata func (c *Client) UpdateMachineMetadata(machineId string, metadata map[string]string) (map[string]interface{}, error) { var resp map[string]interface{} req := request{ method: client.POST, url: makeURL(apiMachines, machineId, apiMetadata), reqValue: metadata, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to update metadata for machine with id %s", machineId) } return resp, nil } // Returns the complete set of metadata associated with the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#GetMachineMetadata func (c *Client) GetMachineMetadata(machineId string) (map[string]interface{}, error) { var resp map[string]interface{} req := request{ method: client.GET, url: makeURL(apiMachines, machineId, apiMetadata), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of metadata for machine with id %s", machineId) } return resp, nil } // Deletes a single metadata key from the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineMetadata func (c *Client) DeleteMachineMetadata(machineId, metadataKey string) error { req := request{ method: client.DELETE, url: makeURL(apiMachines, machineId, apiMetadata, metadataKey), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete metadata with key %s for machine with id %s", metadataKey, machineId) } return nil } // Deletes all metadata keys from the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteAllMachineMetadata func (c *Client) DeleteAllMachineMetadata(machineId string) error { req := request{ method: client.DELETE, url: makeURL(apiMachines, machineId, apiMetadata), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete metadata for machine with id %s", machineId) } return nil } // Adds additional tags to the specified machine. // This API lets you append new tags, not overwrite existing tags. // See API docs: http://apidocs.joyent.com/cloudapi/#AddMachineTags func (c *Client) AddMachineTags(machineId string, tags map[string]string) (map[string]string, error) { var resp map[string]string req := request{ method: client.POST, url: makeURL(apiMachines, machineId, apiTags), reqValue: tags, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to add tags for machine with id %s", machineId) } return resp, nil } // Replaces existing tags for the specified machine. // This API lets you overwrite existing tags, not append to existing tags. // See API docs: http://apidocs.joyent.com/cloudapi/#ReplaceMachineTags func (c *Client) ReplaceMachineTags(machineId string, tags map[string]string) (map[string]string, error) { var resp map[string]string req := request{ method: client.PUT, url: makeURL(apiMachines, machineId, apiTags), reqValue: tags, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to replace tags for machine with id %s", machineId) } return resp, nil } // Returns the complete set of tags associated with the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#ListMachineTags func (c *Client) ListMachineTags(machineId string) (map[string]string, error) { var resp map[string]string req := request{ method: client.GET, url: makeURL(apiMachines, machineId, apiTags), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of tags for machine with id %s", machineId) } return resp, nil } // Returns the value for a single tag on the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#GetMachineTag func (c *Client) GetMachineTag(machineId, tagKey string) (string, error) { var resp string requestHeaders := make(http.Header) requestHeaders.Set("Accept", "text/plain") req := request{ method: client.GET, url: makeURL(apiMachines, machineId, apiTags, tagKey), resp: &resp, reqHeader: requestHeaders, } if _, err := c.sendRequest(req); err != nil { return "", errors.Newf(err, "failed to get tag %s for machine with id %s", tagKey, machineId) } return resp, nil } // Deletes a single tag from the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineTag func (c *Client) DeleteMachineTag(machineId, tagKey string) error { req := request{ method: client.DELETE, url: makeURL(apiMachines, machineId, apiTags, tagKey), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete tag with key %s for machine with id %s", tagKey, machineId) } return nil } // Deletes all tags from the specified machine. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachineTags func (c *Client) DeleteMachineTags(machineId string) error { req := request{ method: client.DELETE, url: makeURL(apiMachines, machineId, apiTags), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete tags for machine with id %s", machineId) } return nil } // Allows you to completely destroy a machine. Machine must be in the 'stopped' state. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteMachine func (c *Client) DeleteMachine(machineId string) error { req := request{ method: client.DELETE, url: makeURL(apiMachines, machineId), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete machine with id %s", machineId) } return nil } // Provides a list of machine's accomplished actions, (sorted from latest to older one). // See API docs: http://apidocs.joyent.com/cloudapi/#MachineAudit func (c *Client) MachineAudit(machineId string) ([]AuditAction, error) { var resp []AuditAction req := request{ method: client.GET, url: makeURL(apiMachines, machineId, apiAudit), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get actions for machine with id %s", machineId) } return resp, nil } // Analytics represents the available analytics type Analytics struct { Modules map[string]interface{} // Namespace to organize metrics Fields map[string]interface{} // Fields represent metadata by which data points can be filtered or decomposed Types map[string]interface{} // Types are used with both metrics and fields for two purposes: to hint to clients at how to best label values, and to distinguish between numeric and discrete quantities. Metrics map[string]interface{} // Metrics describe quantities which can be measured by the system Transformations map[string]interface{} // Transformations are post-processing functions that can be applied to data when it's retrieved. } // Instrumentation specify which metric to collect, how frequently to aggregate data (e.g., every second, every hour, etc.) // how much data to keep (e.g., 10 minutes' worth, 6 months' worth, etc.) and other configuration options type Instrumentation struct { Module string `json:"module"` Stat string `json:"stat"` Predicate string `json:"predicate"` Decomposition []string `json:"decomposition"` ValueDimension int `json:"value-dimenstion"` ValueArity string `json:"value-arity"` RetentionTime int `json:"retention-time"` Granularity int `json:"granularitiy"` IdleMax int `json:"idle-max"` Transformations []string `json:"transformations"` PersistData bool `json:"persist-data"` Crtime int `json:"crtime"` ValueScope string `json:"value-scope"` Id string `json:"id"` Uris []Uri `json:"uris"` } // Uri represents a Universal Resource Identifier type Uri struct { Uri string // Resource identifier Name string // URI name } // InstrumentationValue represents the data associated to an instrumentation for a point in time type InstrumentationValue struct { Value interface{} Transformations map[string]interface{} StartTime int Duration int } // HeatmapOpts represent the option that can be specified // when retrieving an instrumentation.'s heatmap type HeatmapOpts struct { Height int `json:"height"` // Height of the image in pixels Width int `json:"width"` // Width of the image in pixels Ymin int `json:"ymin"` // Y-Axis value for the bottom of the image (default: 0) Ymax int `json:"ymax"` // Y-Axis value for the top of the image (default: auto) Nbuckets int `json:"nbuckets"` // Number of buckets in the vertical dimension Selected []string `json:"selected"` // Array of field values to highlight, isolate or exclude Isolate bool `json:"isolate"` // If true, only draw selected values Exclude bool `json:"exclude"` // If true, don't draw selected values at all Hues []string `json:"hues"` // Array of colors for highlighting selected field values DecomposeAll bool `json:"decompose_all"` // Highlight all field values X int `json:"x"` Y int `json:"y"` } // Heatmap represents an instrumentation's heatmap type Heatmap struct { BucketTime int `json:"bucket_time"` // Time corresponding to the bucket (Unix seconds) BucketYmin int `json:"bucket_ymin"` // Minimum y-axis value for the bucket BucketYmax int `json:"bucket_ymax"` // Maximum y-axis value for the bucket Present map[string]interface{} `json:"present"` // If the instrumentation defines a discrete decomposition, this property's value is an object whose keys are values of that field and whose values are the number of data points in that bucket for that key Total int `json:"total"` // The total number of data points in the bucket } // CreateInstrumentationOpts represent the option that can be specified // when creating a new instrumentation. type CreateInstrumentationOpts struct { Clone int `json:"clone"` // An existing instrumentation ID to be cloned Module string `json:"module"` // Analytics module Stat string `json:"stat"` // Analytics stat Predicate string `json:"predicate"` // Instrumentation predicate, must be JSON string Decomposition string `json:"decomposition"` Granularity int `json:"granularity"` // Number of seconds between data points (default is 1) RetentionTime int `json:"retention-time"` // How long to keep this instrumentation data for PersistData bool `json:"persist-data"` // Whether or not to store this for historical analysis IdleMax int `json:"idle-max"` // Number of seconds after which if the instrumentation or its data has not been accessed via the API the service may delete the instrumentation and its data } // Retrieves the "schema" for instrumentations that can be created. // See API docs: http://apidocs.joyent.com/cloudapi/#DescribeAnalytics func (c *Client) DescribeAnalytics() (*Analytics, error) { var resp Analytics req := request{ method: client.GET, url: apiAnalytics, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get analytics") } return &resp, nil } // Retrieves all currently created instrumentations. // See API docs: http://apidocs.joyent.com/cloudapi/#ListInstrumentations func (c *Client) ListInstrumentations() ([]Instrumentation, error) { var resp []Instrumentation req := request{ method: client.GET, url: makeURL(apiAnalytics, apiInstrumentations), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get instrumentations") } return resp, nil } // Retrieves the configuration for the specified instrumentation. // See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentation func (c *Client) GetInstrumentation(instrumentationId string) (*Instrumentation, error) { var resp Instrumentation req := request{ method: client.GET, url: makeURL(apiAnalytics, apiInstrumentations, instrumentationId), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get instrumentation with id %s", instrumentationId) } return &resp, nil } // Retrieves the data associated to an instrumentation for a point in time. // See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentationValue func (c *Client) GetInstrumentationValue(instrumentationId string) (*InstrumentationValue, error) { var resp InstrumentationValue req := request{ method: client.GET, url: makeURL(apiAnalytics, apiInstrumentations, instrumentationId, apiInstrumentationsValue, apiInstrumentationsRaw), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get value for instrumentation with id %s", instrumentationId) } return &resp, nil } // Retrieves the specified instrumentation's heatmap. // See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentationHeatmap func (c *Client) GetInstrumentationHeatmap(instrumentationId string) (*Heatmap, error) { var resp Heatmap req := request{ method: client.GET, url: makeURL(apiAnalytics, apiInstrumentations, instrumentationId, apiInstrumentationsValue, apiInstrumentationsHeatmap, apiInstrumentationsImage), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get heatmap image for instrumentation with id %s", instrumentationId) } return &resp, nil } // Allows you to retrieve the bucket details for a heatmap. // See API docs: http://apidocs.joyent.com/cloudapi/#GetInstrumentationHeatmapDetails func (c *Client) GetInstrumentationHeatmapDetails(instrumentationId string) (*Heatmap, error) { var resp Heatmap req := request{ method: client.GET, url: makeURL(apiAnalytics, apiInstrumentations, instrumentationId, apiInstrumentationsValue, apiInstrumentationsHeatmap, apiInstrumentationsDetails), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get heatmap details for instrumentation with id %s", instrumentationId) } return &resp, nil } // Creates an instrumentation. // You can clone an existing instrumentation by passing in the parameter clone, which should be a numeric id of an existing instrumentation. // See API docs: http://apidocs.joyent.com/cloudapi/#CreateInstrumentation func (c *Client) CreateInstrumentation(opts CreateInstrumentationOpts) (*Instrumentation, error) { var resp Instrumentation req := request{ method: client.POST, url: makeURL(apiAnalytics, apiInstrumentations), reqValue: opts, resp: &resp, expectedStatus: http.StatusCreated, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to create instrumentation") } return &resp, nil } // Destroys an instrumentation. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteInstrumentation func (c *Client) DeleteInstrumentation(instrumentationId string) error { req := request{ method: client.DELETE, url: makeURL(apiAnalytics, apiInstrumentations, instrumentationId), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete instrumentation with id %s", instrumentationId) } return nil } // FirewallRule represent a firewall rule that can be specifed for a machine. type FirewallRule struct { Id string // Unique identifier for the rule Enabled bool // Whether the rule is enabled or not Rule string // Firewall rule in the form 'FROM <target a> TO <target b> <action> <protocol> <port>' } // CreateFwRuleOpts represent the option that can be specified // when creating a new firewall rule. type CreateFwRuleOpts struct { Enabled bool `json:"enabled"` // Whether to enable the rule or not Rule string `json:"rule"` // Firewall rule in the form 'FROM <target a> TO <target b> <action> <protocol> <port>' } // Lists all the firewall rules on record for a specified account. // See API docs: http://apidocs.joyent.com/cloudapi/#ListFirewallRules func (c *Client) ListFirewallRules() ([]FirewallRule, error) { var resp []FirewallRule req := request{ method: client.GET, url: apiFirewallRules, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of firewall rules") } return resp, nil } // Returns the specified firewall rule. // See API docs: http://apidocs.joyent.com/cloudapi/#GetFirewallRule func (c *Client) GetFirewallRule(fwRuleId string) (*FirewallRule, error) { var resp FirewallRule req := request{ method: client.GET, url: makeURL(apiFirewallRules, fwRuleId), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get firewall rule with id %s", fwRuleId) } return &resp, nil } // Creates the firewall rule with the specified options. // See API docs: http://apidocs.joyent.com/cloudapi/#CreateFirewallRule func (c *Client) CreateFirewallRule(opts CreateFwRuleOpts) (*FirewallRule, error) { var resp FirewallRule req := request{ method: client.POST, url: apiFirewallRules, reqValue: opts, resp: &resp, expectedStatus: http.StatusCreated, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to create firewall rule: %s", opts.Rule) } return &resp, nil } // Updates the specified firewall rule. // See API docs: http://apidocs.joyent.com/cloudapi/#UpdateFirewallRule func (c *Client) UpdateFirewallRule(fwRuleId string, opts CreateFwRuleOpts) (*FirewallRule, error) { var resp FirewallRule req := request{ method: client.POST, url: makeURL(apiFirewallRules, fwRuleId), reqValue: opts, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to update firewall rule with id %s to %s", fwRuleId, opts.Rule) } return &resp, nil } // Enables the given firewall rule record if it is disabled. // See API docs: http://apidocs.joyent.com/cloudapi/#EnableFirewallRule func (c *Client) EnableFirewallRule(fwRuleId string) (*FirewallRule, error) { var resp FirewallRule req := request{ method: client.POST, url: makeURL(apiFirewallRules, fwRuleId, apiFirewallRulesEnable), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to enable firewall rule with id %s", fwRuleId) } return &resp, nil } // Disables the given firewall rule record if it is enabled. // See API docs: http://apidocs.joyent.com/cloudapi/#DisableFirewallRule func (c *Client) DisableFirewallRule(fwRuleId string) (*FirewallRule, error) { var resp FirewallRule req := request{ method: client.POST, url: makeURL(apiFirewallRules, fwRuleId, apiFirewallRulesDisable), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to disable firewall rule with id %s", fwRuleId) } return &resp, nil } // Removes the given firewall rule record from all the required account machines. // See API docs: http://apidocs.joyent.com/cloudapi/#DeleteFirewallRule func (c *Client) DeleteFirewallRule(fwRuleId string) error { req := request{ method: client.DELETE, url: makeURL(apiFirewallRules, fwRuleId), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete firewall rule with id %s", fwRuleId) } return nil } // Return the list of machines affected by the given firewall rule. // See API docs: http://apidocs.joyent.com/cloudapi/#ListFirewallRuleMachines func (c *Client) ListFirewallRuleMachines(fwRuleId string) ([]Machine, error) { var resp []Machine req := request{ method: client.GET, url: makeURL(apiFirewallRules, fwRuleId, apiMachines), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of machines affected by firewall rule wit id %s", fwRuleId) } return resp, nil } // Network represents a network available to a given account type Network struct { Id string // Unique identifier for the network Name string // Network name Public bool // Whether this a public or private (rfc1918) network Description string // Optional description for this network, when name is not enough } // List all the networks which can be used by the given account. // See API docs: http://apidocs.joyent.com/cloudapi/#ListNetworks func (c *Client) ListNetworks() ([]Network, error) { var resp []Network req := request{ method: client.GET, url: apiNetworks, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get list of networks") } return resp, nil } // Retrieves an individual network record. // See API docs: http://apidocs.joyent.com/cloudapi/#GetNetwork func (c *Client) GetNetwork(networkId string) (*Network, error) { var resp Network req := request{ method: client.GET, url: makeURL(apiNetworks, networkId), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get network with id %s", networkId) } return &resp, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/cloudapi/cloudapi_test.go������������������������������0000644�0000153�0000161�00000003567�12321735720�025702� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosdc - Go library to interact with the Joyent CloudAPI // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package cloudapi_test import ( "flag" gc "launchpad.net/gocheck" "testing" "github.com/joyent/gocommon/jpc" ) const ( testKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDdArXEuyqVPwJ7uT/QLFYrGLposHGKRP4U1YPuXFFYQMa2Mq9cke6c6YYoHpNU3mVjatHp+sicfQHcO9nPMaWXoIn53kWdldvo0brsqGXXaHcQCjCaSooJiMgG4jDWUmnfySOQA0sEAXcktqmePpLsDlih05mORiueAR1Mglrc6TiVvjd8ZTPhZejMzETVusMweIilE+K7cNjQVxwHId5WVjTRAqRCvZXAIcP2+fzDXTmuKWhSdln19bKz5AEp1jU/eg4D4PuQvwynb9A8Ra2SJnOZ2+9cfDVhrbpzVMty4qQU6WblJNjpLnLpkm8w0isYk2Vr13a+1/N941gFcZaZ daniele@lightman.local" testKeyFingerprint = "6b:06:0c:6b:0b:44:67:97:2c:4f:87:28:28:f3:c6:a9" packageId = "d6ca9994-53e7-4adf-a818-aadd3c90a916" localPackageId = "11223344-1212-abab-3434-aabbccddeeff" packageName = "g3-standard-1-smartos" localPackageName = "Small" imageId = "f669428c-a939-11e2-a485-b790efc0f0c1" localImageId = "12345678-a1a1-b2b2-c3c3-098765432100" testFwRule = "FROM subnet 10.35.76.0/24 TO subnet 10.35.101.0/24 ALLOW tcp (PORT 80 AND PORT 443)" testUpdatedFwRule = "FROM subnet 10.35.76.0/24 TO subnet 10.35.101.0/24 ALLOW tcp (port 80 AND port 443 AND port 8080)" networkId = "42325ea0-eb62-44c1-8eb6-0af3e2f83abc" localNetworkId = "123abc4d-0011-aabb-2233-ccdd4455" ) var live = flag.Bool("live", false, "Include live Joyent Cloud tests") var keyName = flag.String("key.name", "", "Specify the full path to the private key, defaults to ~/.ssh/id_rsa") func Test(t *testing.T) { if *live { creds, err := jpc.CompleteCredentialsFromEnv(*keyName) if err != nil { t.Fatalf("Error setting up test suite: %s", err.Error()) } registerJoyentCloudTests(creds) } registerLocalTests(*keyName) gc.TestingT(t) } �����������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/localservices/�����������������������������������������0000755�0000153�0000161�00000000000�12321735720�023537� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/localservices/cloudapi/��������������������������������0000755�0000153�0000161�00000000000�12321735720�025337� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/localservices/cloudapi/service_test.go�����������������0000644�0000153�0000161�00000030146�12321735720�030371� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosdc - Go library to interact with the Joyent CloudAPI // // CloudAPI double testing service - internal direct API test // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package cloudapi_test import ( gc "launchpad.net/gocheck" "testing" "github.com/joyent/gosdc/cloudapi" lc "github.com/joyent/gosdc/localservices/cloudapi" ) type CloudAPISuite struct { service *lc.CloudAPI } const ( testServiceURL = "https://go-test.api.joyentcloud.com" testUserAccount = "gouser" testKeyName = "test-key" testKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLF4s7GLYfPYVr3zqZcNCZcM2qFDXXxE5pCGuGowySGKTnxrqrPY4HO+9CQ+5X55o4rJOfNJ9ZRa+2Qmlr4F/qACcT/ZJbXPs+LcbOVtgUaynn6ooh0C4V/MdKPZmW8FSTy98GstVJZXJO2gJwlKGHQwoWuZ5H/IeN6gCaXi65NPg4eu3Ls9a6BtvOf1Vtb1wwl7QqoZqziT5omA9bDeoBXdoYOoowDS3LUprFRvc1lW7fY9eLKNvoQ4oOJMMn5cPh2CICj5cb2eRH1pHJA/9mxxW4+bB7QdL3N7hDbpV4Qz5MxjxYN3DWldvyP1zCe/Tgyduiz4X3gDBhy735Bpat gouser@localhost" testPackage = "Small" testImage = "11223344-0a0a-ff99-11bb-0a1b2c3d4e5f" testMachineName = "test-machine" testFwRule = "FROM subnet 10.35.76.0/24 TO subnet 10.35.101.0/24 ALLOW tcp (PORT 80 AND PORT 443)" testUpdatedFwRule = "FROM subnet 10.35.76.0/24 TO subnet 10.35.101.0/24 ALLOW tcp (port 80 AND port 443 AND port 8080)" testNetworkId = "123abc4d-0011-aabb-2233-ccdd4455" ) var _ = gc.Suite(&CloudAPISuite{}) func Test(t *testing.T) { gc.TestingT(t) } func (s *CloudAPISuite) SetUpSuite(c *gc.C) { s.service = lc.New(testServiceURL, testUserAccount) } // Helpers func (s *CloudAPISuite) createKey(c *gc.C, keyName, key string) *cloudapi.Key { k, err := s.service.CreateKey(keyName, key) c.Assert(err, gc.IsNil) return k } func (s *CloudAPISuite) deleteKey(c *gc.C, keyName string) { err := s.service.DeleteKey(keyName) c.Assert(err, gc.IsNil) } func (s *CloudAPISuite) createMachine(c *gc.C, name, pkg, image string, metadata, tags map[string]string) *cloudapi.Machine { m, err := s.service.CreateMachine(name, pkg, image, metadata, tags) c.Assert(err, gc.IsNil) return m } func (s *CloudAPISuite) deleteMachine(c *gc.C, machineId string) { err := s.service.StopMachine(machineId) c.Assert(err, gc.IsNil) err = s.service.DeleteMachine(machineId) c.Assert(err, gc.IsNil) } // Helper method to create a test firewall rule func (s *CloudAPISuite) createFirewallRule(c *gc.C) *cloudapi.FirewallRule { fwRule, err := s.service.CreateFirewallRule(testFwRule, false) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) c.Assert(fwRule.Rule, gc.Equals, testFwRule) c.Assert(fwRule.Enabled, gc.Equals, false) return fwRule } // Helper method to a test firewall rule func (s *CloudAPISuite) deleteFwRule(c *gc.C, fwRuleId string) { err := s.service.DeleteFirewallRule(fwRuleId) c.Assert(err, gc.IsNil) } // Tests for Keys API func (s *CloudAPISuite) TestListKeys(c *gc.C) { k := s.createKey(c, testKeyName, testKey) defer s.deleteKey(c, testKeyName) keys, err := s.service.ListKeys() c.Assert(err, gc.IsNil) for _, key := range keys { if c.Check(&key, gc.DeepEquals, k) { c.SucceedNow() } } c.Fatalf("Obtained keys [%s] do not contain test key [%s]", keys, k) } func (s *CloudAPISuite) TestGetKey(c *gc.C) { k := s.createKey(c, testKeyName, testKey) defer s.deleteKey(c, testKeyName) key, err := s.service.GetKey(testKeyName) c.Assert(err, gc.IsNil) c.Assert(key, gc.DeepEquals, k) } func (s *CloudAPISuite) TestCreateKey(c *gc.C) { k := s.createKey(c, testKeyName, testKey) defer s.deleteKey(c, testKeyName) c.Assert(k.Name, gc.Equals, testKeyName) c.Assert(k.Key, gc.Equals, testKey) } func (s *CloudAPISuite) TestDeleteKey(c *gc.C) { s.createKey(c, testKeyName, testKey) s.deleteKey(c, testKeyName) } // Tests for Package API func (s *CloudAPISuite) TestListPackages(c *gc.C) { pkgs, err := s.service.ListPackages(nil) c.Assert(err, gc.IsNil) c.Assert(len(pkgs), gc.Equals, 4) } func (s *CloudAPISuite) TestListPackagesWithFilter(c *gc.C) { pkgs, err := s.service.ListPackages(map[string]string{"memory": "1024"}) c.Assert(err, gc.IsNil) c.Assert(len(pkgs), gc.Equals, 1) } func (s *CloudAPISuite) TestGetPackage(c *gc.C) { pkg, err := s.service.GetPackage(testPackage) c.Assert(err, gc.IsNil) c.Assert(pkg.Name, gc.Equals, "Small") c.Assert(pkg.Memory, gc.Equals, 1024) c.Assert(pkg.Disk, gc.Equals, 16384) c.Assert(pkg.Swap, gc.Equals, 2048) c.Assert(pkg.VCPUs, gc.Equals, 1) c.Assert(pkg.Default, gc.Equals, true) c.Assert(pkg.Id, gc.Equals, "11223344-1212-abab-3434-aabbccddeeff") c.Assert(pkg.Version, gc.Equals, "1.0.2") } // Tests for Images API func (s *CloudAPISuite) TestListImages(c *gc.C) { images, err := s.service.ListImages(nil) c.Assert(err, gc.IsNil) c.Assert(len(images), gc.Equals, 6) } func (s *CloudAPISuite) TestListImagesWithFilter(c *gc.C) { images, err := s.service.ListImages(map[string]string{"os": "linux"}) c.Assert(err, gc.IsNil) c.Assert(len(images), gc.Equals, 4) } func (s *CloudAPISuite) TestGetImage(c *gc.C) { image, err := s.service.GetImage(testImage) c.Assert(err, gc.IsNil) c.Assert(image.Id, gc.Equals, "11223344-0a0a-ff99-11bb-0a1b2c3d4e5f") c.Assert(image.Name, gc.Equals, "ubuntu12.04") c.Assert(image.OS, gc.Equals, "linux") c.Assert(image.Version, gc.Equals, "2.3.1") c.Assert(image.Type, gc.Equals, "virtualmachine") c.Assert(image.Description, gc.Equals, "Test Ubuntu 12.04 image (64 bit)") c.Assert(image.PublishedAt, gc.Equals, "2014-01-20T16:12:31Z") c.Assert(image.Public, gc.Equals, "true") c.Assert(image.State, gc.Equals, "active") } // Test for Machine API func (s *CloudAPISuite) TestListMachines(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) machines, err := s.service.ListMachines(nil) c.Assert(err, gc.IsNil) for _, machine := range machines { if machine.Id == m.Id { c.SucceedNow() } } c.Fatalf("Obtained machine [%s] do not contain test machine [%s]", machines, m) } func (s *CloudAPISuite) TestCountMachines(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) count, err := s.service.CountMachines() c.Assert(err, gc.IsNil) c.Assert(count, gc.Equals, 1) } func (s *CloudAPISuite) TestGetMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) machine, err := s.service.GetMachine(m.Id) c.Assert(err, gc.IsNil) c.Assert(machine.Name, gc.Equals, testMachineName) c.Assert(machine.Package, gc.Equals, testPackage) c.Assert(machine.Image, gc.Equals, testImage) } func (s *CloudAPISuite) TestCreateMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) c.Assert(m.Name, gc.Equals, testMachineName) c.Assert(m.Package, gc.Equals, testPackage) c.Assert(m.Image, gc.Equals, testImage) c.Assert(m.Type, gc.Equals, "virtualmachine") c.Assert(len(m.IPs), gc.Equals, 2) c.Assert(m.State, gc.Equals, "running") } func (s *CloudAPISuite) TestStartMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) err := s.service.StartMachine(m.Id) c.Assert(err, gc.IsNil) machine, err := s.service.GetMachine(m.Id) c.Assert(err, gc.IsNil) c.Assert(machine.State, gc.Equals, "running") } func (s *CloudAPISuite) TestStopMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) err := s.service.StopMachine(m.Id) c.Assert(err, gc.IsNil) machine, err := s.service.GetMachine(m.Id) c.Assert(err, gc.IsNil) c.Assert(machine.State, gc.Equals, "stopped") } func (s *CloudAPISuite) TestRebootMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) err := s.service.RebootMachine(m.Id) c.Assert(err, gc.IsNil) machine, err := s.service.GetMachine(m.Id) c.Assert(err, gc.IsNil) c.Assert(machine.State, gc.Equals, "running") } func (s *CloudAPISuite) TestResizeMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) err := s.service.ResizeMachine(m.Id, "Medium") c.Assert(err, gc.IsNil) machine, err := s.service.GetMachine(m.Id) c.Assert(err, gc.IsNil) c.Assert(machine.Package, gc.Equals, "Medium") c.Assert(machine.Memory, gc.Equals, 2048) c.Assert(machine.Disk, gc.Equals, 32768) } func (s *CloudAPISuite) TestRenameMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) err := s.service.RenameMachine(m.Id, "new-test-name") c.Assert(err, gc.IsNil) machine, err := s.service.GetMachine(m.Id) c.Assert(err, gc.IsNil) c.Assert(machine.Name, gc.Equals, "new-test-name") } func (s *CloudAPISuite) TestListMachinesFirewallRules(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) fwRules, err := s.service.ListMachineFirewallRules(m.Id) c.Assert(err, gc.IsNil) c.Assert(fwRules, gc.NotNil) } func (s *CloudAPISuite) TestEnableFirewallMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) err := s.service.EnableFirewallMachine(m.Id) c.Assert(err, gc.IsNil) } func (s *CloudAPISuite) TestDisableFirewallMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) err := s.service.DisableFirewallMachine(m.Id) c.Assert(err, gc.IsNil) } func (s *CloudAPISuite) TestDeleteMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) s.deleteMachine(c, m.Id) } // Tests for FirewallRules API func (s *CloudAPISuite) TestCreateFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) // cleanup s.deleteFwRule(c, testFwRule.Id) } func (s *CloudAPISuite) TestListFirewallRules(c *gc.C) { testFwRule := s.createFirewallRule(c) rules, err := s.service.ListFirewallRules() c.Assert(err, gc.IsNil) c.Assert(rules, gc.NotNil) // cleanup s.deleteFwRule(c, testFwRule.Id) } func (s *CloudAPISuite) TestGetFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) fwRule, err := s.service.GetFirewallRule(testFwRule.Id) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) c.Assert((*fwRule), gc.DeepEquals, (*testFwRule)) // cleanup s.deleteFwRule(c, testFwRule.Id) } func (s *CloudAPISuite) TestUpdateFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) fwRule, err := s.service.UpdateFirewallRule(testFwRule.Id, testUpdatedFwRule, true) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) c.Assert(fwRule.Rule, gc.Equals, testUpdatedFwRule) // cleanup s.deleteFwRule(c, testFwRule.Id) } func (s *CloudAPISuite) TestEnableFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) fwRule, err := s.service.EnableFirewallRule((*testFwRule).Id) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) // cleanup s.deleteFwRule(c, testFwRule.Id) } func (s *CloudAPISuite) TestListFirewallRuleMachines(c *gc.C) { testFwRule := s.createFirewallRule(c) machines, err := s.service.ListFirewallRuleMachines((*testFwRule).Id) c.Assert(err, gc.IsNil) c.Assert(machines, gc.NotNil) // cleanup s.deleteFwRule(c, testFwRule.Id) } func (s *CloudAPISuite) TestDisableFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) fwRule, err := s.service.DisableFirewallRule((*testFwRule).Id) c.Assert(err, gc.IsNil) c.Assert(fwRule, gc.NotNil) // cleanup s.deleteFwRule(c, testFwRule.Id) } func (s *CloudAPISuite) TestDeleteFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) s.deleteFwRule(c, testFwRule.Id) } // tests for Networks API func (s *CloudAPISuite) TestListNetworks(c *gc.C) { nets, err := s.service.ListNetworks() c.Assert(err, gc.IsNil) c.Assert(nets, gc.NotNil) } func (s *CloudAPISuite) TestGetNetwork(c *gc.C) { net, err := s.service.GetNetwork(testNetworkId) c.Assert(err, gc.IsNil) c.Assert(net, gc.NotNil) c.Assert(net, gc.DeepEquals, &cloudapi.Network{ Id: testNetworkId, Name: "Test-Joyent-Public", Public: true, Description: "", }) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/localservices/cloudapi/service_http.go�����������������0000644�0000153�0000161�00000040122�12321735720�030364� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosdc - Go library to interact with the Joyent CloudAPI // // CloudAPI double testing service - HTTP API implementation // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package cloudapi import ( "encoding/json" "fmt" "io/ioutil" "net/http" "strconv" "strings" "github.com/joyent/gosdc/cloudapi" ) // ErrorResponse defines a single HTTP error response. type ErrorResponse struct { Code int Body string contentType string errorText string headers map[string]string cloudapi *CloudAPI } var ( ErrNotAllowed = &ErrorResponse{ http.StatusMethodNotAllowed, "Method is not allowed", "text/plain; charset=UTF-8", "MethodNotAllowedError", nil, nil, } ErrNotFound = &ErrorResponse{ http.StatusNotFound, "Resource Not Found", "text/plain; charset=UTF-8", "NotFoundError", nil, nil, } ErrBadRequest = &ErrorResponse{ http.StatusBadRequest, "Malformed request url", "text/plain; charset=UTF-8", "BadRequestError", nil, nil, } ) func (e *ErrorResponse) Error() string { return e.errorText } func (e *ErrorResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) { if e.contentType != "" { w.Header().Set("Content-Type", e.contentType) } body := e.Body if e.headers != nil { for h, v := range e.headers { w.Header().Set(h, v) } } // workaround for https://code.google.com/p/go/issues/detail?id=4454 w.Header().Set("Content-Length", strconv.Itoa(len(body))) if e.Code != 0 { w.WriteHeader(e.Code) } if len(body) > 0 { w.Write([]byte(body)) } } type cloudapiHandler struct { cloudapi *CloudAPI method func(m *CloudAPI, w http.ResponseWriter, r *http.Request) error } func (h *cloudapiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path // handle trailing slash in the path if strings.HasSuffix(path, "/") && path != "/" { ErrNotFound.ServeHTTP(w, r) return } err := h.method(h.cloudapi, w, r) if err == nil { return } var resp http.Handler resp, _ = err.(http.Handler) if resp == nil { resp = &ErrorResponse{ http.StatusInternalServerError, `{"internalServerError":{"message":"Unkown Error",code:500}}`, "application/json", err.Error(), nil, h.cloudapi, } } resp.ServeHTTP(w, r) } func writeResponse(w http.ResponseWriter, code int, body []byte) { // workaround for https://code.google.com/p/go/issues/detail?id=4454 w.Header().Set("Content-Length", strconv.Itoa(len(body))) w.WriteHeader(code) w.Write(body) } // sendJSON sends the specified response serialized as JSON. func sendJSON(code int, resp interface{}, w http.ResponseWriter, r *http.Request) error { data, err := json.Marshal(resp) if err != nil { return err } writeResponse(w, code, data) return nil } func processFilter(rawQuery string) map[string]string { var filters map[string]string if rawQuery != "" { filters = make(map[string]string) for _, filter := range strings.Split(rawQuery, "&") { filters[filter[:strings.Index(filter, "=")]] = filter[strings.Index(filter, "=")+1:] } } return filters } func (cloudapi *CloudAPI) handler(method func(m *CloudAPI, w http.ResponseWriter, r *http.Request) error) http.Handler { return &cloudapiHandler{cloudapi, method} } // handleKeys handles the keys HTTP API. func (c *CloudAPI) handleKeys(w http.ResponseWriter, r *http.Request) error { prefix := fmt.Sprintf("/%s/keys/", c.ServiceInstance.UserAccount) keyName := strings.TrimPrefix(r.URL.Path, prefix) switch r.Method { case "GET": if strings.HasSuffix(r.URL.Path, "keys") { // ListKeys keys, err := c.ListKeys() if err != nil { return err } if keys == nil { keys = []cloudapi.Key{} } resp := keys return sendJSON(http.StatusOK, resp, w, r) } else { // GetKey key, err := c.GetKey(keyName) if err != nil { return err } if key == nil { key = &cloudapi.Key{} } resp := key return sendJSON(http.StatusOK, resp, w, r) } case "POST": if strings.HasSuffix(r.URL.Path, "keys") { // CreateKey var ( name string key string ) opts := &cloudapi.CreateKeyOpts{} body, errB := ioutil.ReadAll(r.Body) if errB != nil { return errB } if len(body) > 0 { if errJ := json.Unmarshal(body, opts); errJ != nil { return errJ } name = opts.Name key = opts.Key } k, err := c.CreateKey(name, key) if err != nil { return err } if k == nil { k = &cloudapi.Key{} } resp := k return sendJSON(http.StatusCreated, resp, w, r) } else { return ErrNotAllowed } case "PUT": return ErrNotAllowed case "DELETE": if strings.HasSuffix(r.URL.Path, "keys") { return ErrNotAllowed } else { // DeleteKey err := c.DeleteKey(keyName) if err != nil { return err } return sendJSON(http.StatusNoContent, nil, w, r) } } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleImages handles the images HTTP API. func (c *CloudAPI) handleImages(w http.ResponseWriter, r *http.Request) error { prefix := fmt.Sprintf("/%s/images/", c.ServiceInstance.UserAccount) imageId := strings.TrimPrefix(r.URL.Path, prefix) switch r.Method { case "GET": if strings.HasSuffix(r.URL.Path, "images") { // ListImages images, err := c.ListImages(processFilter(r.URL.RawQuery)) if err != nil { return err } if images == nil { images = []cloudapi.Image{} } resp := images return sendJSON(http.StatusOK, resp, w, r) } else { // GetImage image, err := c.GetImage(imageId) if err != nil { return err } if image == nil { image = &cloudapi.Image{} } resp := image return sendJSON(http.StatusOK, resp, w, r) } case "POST": if strings.HasSuffix(r.URL.Path, "images") { // CreateImageFromMachine return ErrNotFound } else { return ErrNotAllowed } case "PUT": return ErrNotAllowed case "DELETE": /*if strings.HasSuffix(r.URL.Path, "images") { return ErrNotAllowed } else { err := c.DeleteImage(imageId) if err != nil { return err } return sendJSON(http.StatusNoContent, nil, w, r) }*/ return ErrNotAllowed } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handlePackages handles the packages HTTP API. func (c *CloudAPI) handlePackages(w http.ResponseWriter, r *http.Request) error { prefix := fmt.Sprintf("/%s/packages/", c.ServiceInstance.UserAccount) pkgName := strings.TrimPrefix(r.URL.Path, prefix) switch r.Method { case "GET": if strings.HasSuffix(r.URL.Path, "packages") { // ListPackages pkgs, err := c.ListPackages(processFilter(r.URL.RawQuery)) if err != nil { return err } if pkgs == nil { pkgs = []cloudapi.Package{} } resp := pkgs return sendJSON(http.StatusOK, resp, w, r) } else { // GetPackage pkg, err := c.GetPackage(pkgName) if err != nil { return err } if pkg == nil { pkg = &cloudapi.Package{} } resp := pkg return sendJSON(http.StatusOK, resp, w, r) } case "POST": return ErrNotAllowed case "PUT": return ErrNotAllowed case "DELETE": return ErrNotAllowed } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleMachines handles the machine HTTP API. func (c *CloudAPI) handleMachines(w http.ResponseWriter, r *http.Request) error { prefix := fmt.Sprintf("/%s/machines/", c.ServiceInstance.UserAccount) machineId := strings.TrimPrefix(r.URL.Path, prefix) switch r.Method { case "GET": if strings.HasSuffix(r.URL.Path, "machines") { // ListMachines machines, err := c.ListMachines(processFilter(r.URL.RawQuery)) if err != nil { return err } if machines == nil { machines = []*cloudapi.Machine{} } resp := machines return sendJSON(http.StatusOK, resp, w, r) } else if strings.HasSuffix(r.URL.Path, "fwrules") { // ListMachineFirewallRules machineId = strings.TrimSuffix(machineId, "/fwrules") fwRules, err := c.ListMachineFirewallRules(machineId) if err != nil { return err } if fwRules == nil { fwRules = []*cloudapi.FirewallRule{} } resp := fwRules return sendJSON(http.StatusOK, resp, w, r) } else { // GetMachine machine, err := c.GetMachine(machineId) if err != nil { return err } if machine == nil { machine = &cloudapi.Machine{} } resp := machine return sendJSON(http.StatusOK, resp, w, r) } case "HEAD": if strings.HasSuffix(r.URL.Path, "machines") { // CountMachines count, err := c.CountMachines() if err != nil { return err } resp := count return sendJSON(http.StatusOK, resp, w, r) } else { return ErrNotAllowed } case "POST": if strings.HasSuffix(r.URL.Path, "machines") { // CreateMachine var ( name string pkg string image string metadata map[string]string tags map[string]string ) opts := &cloudapi.CreateMachineOpts{} body, errB := ioutil.ReadAll(r.Body) if errB != nil { return errB } if len(body) > 0 { if errJ := json.Unmarshal(body, opts); errJ != nil { return errJ } name = opts.Name pkg = opts.Package image = opts.Image metadata = opts.Metadata tags = opts.Tags } machine, err := c.CreateMachine(name, pkg, image, metadata, tags) if err != nil { return err } if machine == nil { machine = &cloudapi.Machine{} } resp := machine return sendJSON(http.StatusCreated, resp, w, r) } else if r.URL.Query().Get("action") == "stop" { //StopMachine err := c.StopMachine(machineId) if err != nil { return err } return sendJSON(http.StatusAccepted, nil, w, r) } else if r.URL.Query().Get("action") == "start" { //StartMachine err := c.StartMachine(machineId) if err != nil { return err } return sendJSON(http.StatusAccepted, nil, w, r) } else if r.URL.Query().Get("action") == "reboot" { //RebootMachine err := c.RebootMachine(machineId) if err != nil { return err } return sendJSON(http.StatusAccepted, nil, w, r) } else if r.URL.Query().Get("action") == "resize" { //ResizeMachine err := c.ResizeMachine(machineId, r.URL.Query().Get("package")) if err != nil { return err } return sendJSON(http.StatusAccepted, nil, w, r) } else if r.URL.Query().Get("action") == "rename" { //RenameMachine err := c.RenameMachine(machineId, r.URL.Query().Get("name")) if err != nil { return err } return sendJSON(http.StatusAccepted, nil, w, r) } else if r.URL.Query().Get("action") == "enable_firewall" { //EnableFirewallMachine err := c.EnableFirewallMachine(machineId) if err != nil { return err } return sendJSON(http.StatusAccepted, nil, w, r) } else if r.URL.Query().Get("action") == "disable_firewall" { //DisableFirewallMachine err := c.DisableFirewallMachine(machineId) if err != nil { return err } return sendJSON(http.StatusAccepted, nil, w, r) } else { return ErrNotAllowed } case "PUT": return ErrNotAllowed case "DELETE": if strings.HasSuffix(r.URL.Path, "machines") { return ErrNotAllowed } else { // DeleteMachine err := c.DeleteMachine(machineId) if err != nil { return err } return sendJSON(http.StatusNoContent, nil, w, r) } } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleFwRules handles the firewall rules HTTP API. func (c *CloudAPI) handleFwRules(w http.ResponseWriter, r *http.Request) error { prefix := fmt.Sprintf("/%s/fwrules/", c.ServiceInstance.UserAccount) fwRuleId := strings.TrimPrefix(r.URL.Path, prefix) switch r.Method { case "GET": if strings.HasSuffix(r.URL.Path, "fwrules") { // ListFirewallRules fwRules, err := c.ListFirewallRules() if err != nil { return err } if fwRules == nil { fwRules = []*cloudapi.FirewallRule{} } resp := fwRules return sendJSON(http.StatusOK, resp, w, r) } else { // GetFirewallRule fwRule, err := c.GetFirewallRule(fwRuleId) if err != nil { return err } if fwRule == nil { fwRule = &cloudapi.FirewallRule{} } resp := fwRule return sendJSON(http.StatusOK, resp, w, r) } case "POST": if strings.HasSuffix(r.URL.Path, "fwrules") { // CreateFirewallRule var ( rule string enabled bool ) opts := &cloudapi.CreateFwRuleOpts{} body, errB := ioutil.ReadAll(r.Body) if errB != nil { return errB } if len(body) > 0 { if errJ := json.Unmarshal(body, opts); errJ != nil { return errJ } rule = opts.Rule enabled = opts.Enabled } fwRule, err := c.CreateFirewallRule(rule, enabled) if err != nil { return err } if fwRule == nil { fwRule = &cloudapi.FirewallRule{} } resp := fwRule return sendJSON(http.StatusCreated, resp, w, r) } else if strings.HasSuffix(r.URL.Path, "enable") { // EnableFirewallRule fwRuleId = strings.TrimSuffix(fwRuleId, "/enable") fwRule, err := c.EnableFirewallRule(fwRuleId) if err != nil { return err } if fwRule == nil { fwRule = &cloudapi.FirewallRule{} } resp := fwRule return sendJSON(http.StatusOK, resp, w, r) } else if strings.HasSuffix(r.URL.Path, "disable") { // DisableFirewallRule fwRuleId = strings.TrimSuffix(fwRuleId, "/disable") fwRule, err := c.DisableFirewallRule(fwRuleId) if err != nil { return err } if fwRule == nil { fwRule = &cloudapi.FirewallRule{} } resp := fwRule return sendJSON(http.StatusOK, resp, w, r) } else { // UpdateFirewallRule var ( rule string enabled bool ) opts := &cloudapi.CreateFwRuleOpts{} body, errB := ioutil.ReadAll(r.Body) if errB != nil { return errB } if len(body) > 0 { if errJ := json.Unmarshal(body, opts); errJ != nil { return errJ } rule = opts.Rule enabled = opts.Enabled } fwRule, err := c.UpdateFirewallRule(fwRuleId, rule, enabled) if err != nil { return err } if fwRule == nil { fwRule = &cloudapi.FirewallRule{} } resp := fwRule return sendJSON(http.StatusOK, resp, w, r) } case "PUT": return ErrNotAllowed case "DELETE": if strings.HasSuffix(r.URL.Path, "fwrules") { return ErrNotAllowed } else { // DeleteFirewallRule err := c.DeleteFirewallRule(fwRuleId) if err != nil { return err } return sendJSON(http.StatusNoContent, nil, w, r) } } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleNetworks handles the networks HTTP API. func (c *CloudAPI) handleNetworks(w http.ResponseWriter, r *http.Request) error { prefix := fmt.Sprintf("/%s/networks/", c.ServiceInstance.UserAccount) networkId := strings.TrimPrefix(r.URL.Path, prefix) switch r.Method { case "GET": if strings.HasSuffix(r.URL.Path, "networks") { // ListNetworks networks, err := c.ListNetworks() if err != nil { return err } if networks == nil { networks = []cloudapi.Network{} } resp := networks return sendJSON(http.StatusOK, resp, w, r) } else { // GetNetwork network, err := c.GetNetwork(networkId) if err != nil { return err } if network == nil { network = &cloudapi.Network{} } resp := network return sendJSON(http.StatusOK, resp, w, r) } case "POST": return ErrNotAllowed case "PUT": return ErrNotAllowed case "DELETE": return ErrNotAllowed } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // setupHTTP attaches all the needed handlers to provide the HTTP API. func (c *CloudAPI) SetupHTTP(mux *http.ServeMux) { handlers := map[string]http.Handler{ "/": ErrNotFound, "/$user/": ErrBadRequest, "/$user/keys": c.handler((*CloudAPI).handleKeys), "/$user/images": c.handler((*CloudAPI).handleImages), "/$user/packages": c.handler((*CloudAPI).handlePackages), "/$user/machines": c.handler((*CloudAPI).handleMachines), //"/$user/datacenters": c.handler((*CloudAPI).handleDatacenters), "/$user/fwrules": c.handler((*CloudAPI).handleFwRules), "/$user/networks": c.handler((*CloudAPI).handleNetworks), } for path, h := range handlers { path = strings.Replace(path, "$user", c.ServiceInstance.UserAccount, 1) if !strings.HasSuffix(path, "/") { mux.Handle(path+"/", h) } mux.Handle(path, h) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/localservices/cloudapi/service.go����������������������0000644�0000153�0000161�00000045222�12321735720�027333� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosdc - Go library to interact with the Joyent CloudAPI // // CloudAPI double testing service - internal direct API implementation // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package cloudapi import ( "fmt" "math/rand" "net/url" "strconv" "strings" "time" "github.com/joyent/gosdc/cloudapi" "github.com/joyent/gosdc/localservices" ) var ( separator = "/" packagesFilters = []string{"name", "memory", "disk", "swap", "version", "vcpus", "group"} imagesFilters = []string{"name", "os", "version", "public", "state", "owner", "type"} machinesFilters = []string{"type", "name", "image", "state", "memory", "tombstone", "limit", "offset", "credentials"} ) type CloudAPI struct { localservices.ServiceInstance keys []cloudapi.Key packages []cloudapi.Package images []cloudapi.Image machines []*cloudapi.Machine machineFw map[string]bool snapshots map[string][]cloudapi.Snapshot firewallRules []*cloudapi.FirewallRule networks []cloudapi.Network } func New(serviceURL, userAccount string) *CloudAPI { URL, err := url.Parse(serviceURL) if err != nil { panic(err) } hostname := URL.Host if !strings.HasSuffix(hostname, separator) { hostname += separator } keys := make([]cloudapi.Key, 0) machines := make([]*cloudapi.Machine, 0) machineFw := make(map[string]bool) snapshots := make(map[string][]cloudapi.Snapshot) firewallRules := make([]*cloudapi.FirewallRule, 0) cloudapiService := &CloudAPI{ keys: keys, packages: initPackages(), images: initImages(), machines: machines, machineFw: machineFw, snapshots: snapshots, firewallRules: firewallRules, networks: []cloudapi.Network{ {Id: "123abc4d-0011-aabb-2233-ccdd4455", Name: "Test-Joyent-Public", Public: true}, {Id: "456def0a-33ff-7f8e-9a0b-33bb44cc", Name: "Test-Joyent-Private", Public: false}, }, ServiceInstance: localservices.ServiceInstance{ Scheme: URL.Scheme, Hostname: hostname, UserAccount: userAccount, }, } return cloudapiService } func initPackages() []cloudapi.Package { return []cloudapi.Package{ { Name: "Micro", Memory: 512, Disk: 8192, Swap: 1024, VCPUs: 1, Default: false, Id: "12345678-aaaa-bbbb-cccc-000000000000", Version: "1.0.0", }, { Name: "Small", Memory: 1024, Disk: 16384, Swap: 2048, VCPUs: 1, Default: true, Id: "11223344-1212-abab-3434-aabbccddeeff", Version: "1.0.2", }, { Name: "Medium", Memory: 2048, Disk: 32768, Swap: 4096, VCPUs: 2, Default: false, Id: "aabbccdd-abcd-abcd-abcd-112233445566", Version: "1.0.4", }, { Name: "Large", Memory: 4096, Disk: 65536, Swap: 16384, VCPUs: 4, Default: false, Id: "00998877-dddd-eeee-ffff-111111111111", Version: "1.0.1", }, } } func initImages() []cloudapi.Image { return []cloudapi.Image{ { Id: "12345678-a1a1-b2b2-c3c3-098765432100", Name: "SmartOS Std", OS: "smartos", Version: "13.3.1", Type: "smartmachine", Description: "Test SmartOS image (32 bit)", Homepage: "http://test.joyent.com/Standard_Instance", PublishedAt: "2014-01-08T17:42:31Z", Public: "true", State: "active", }, { Id: "12345678-b1b1-a4a4-d8d8-111111999999", Name: "standard32", OS: "smartos", Version: "13.3.1", Type: "smartmachine", Description: "Test SmartOS image (64 bit)", Homepage: "http://test.joyent.com/Standard_Instance", PublishedAt: "2014-01-08T17:43:16Z", Public: "true", State: "active", }, { Id: "a1b2c3d4-0011-2233-4455-0f1e2d3c4b5a", Name: "centos6.4", OS: "linux", Version: "2.4.1", Type: "virtualmachine", Description: "Test CentOS 6.4 image (64 bit)", PublishedAt: "2014-01-02T10:58:31Z", Public: "true", State: "active", }, { Id: "11223344-0a0a-ff99-11bb-0a1b2c3d4e5f", Name: "ubuntu12.04", OS: "linux", Version: "2.3.1", Type: "virtualmachine", Description: "Test Ubuntu 12.04 image (64 bit)", PublishedAt: "2014-01-20T16:12:31Z", Public: "true", State: "active", }, { Id: "11223344-0a0a-ee88-22ab-00aa11bb22cc", Name: "ubuntu12.10", OS: "linux", Version: "2.3.2", Type: "virtualmachine", Description: "Test Ubuntu 12.10 image (64 bit)", PublishedAt: "2014-01-20T16:12:31Z", Public: "true", State: "active", }, { Id: "11223344-0a0a-dd77-33cd-abcd1234e5f6", Name: "ubuntu13.04", OS: "linux", Version: "2.2.8", Type: "virtualmachine", Description: "Test Ubuntu 13.04 image (64 bit)", PublishedAt: "2014-01-20T16:12:31Z", Public: "true", State: "active", }, } } func generatePublicIPAddress() string { r := rand.New(rand.NewSource(time.Now().UnixNano())) return fmt.Sprintf("32.151.%d.%d", r.Intn(255), r.Intn(255)) } func generatePrivateIPAddress() string { r := rand.New(rand.NewSource(time.Now().UnixNano())) return fmt.Sprintf("10.201.%d.%d", r.Intn(255), r.Intn(255)) } func contains(list []string, elem string) bool { for _, t := range list { if t == elem { return true } } return false } // Keys APIs func (c *CloudAPI) ListKeys() ([]cloudapi.Key, error) { if err := c.ProcessFunctionHook(c); err != nil { return nil, err } return c.keys, nil } func (c *CloudAPI) GetKey(keyName string) (*cloudapi.Key, error) { if err := c.ProcessFunctionHook(c, keyName); err != nil { return nil, err } for _, key := range c.keys { if key.Name == keyName { return &key, nil } } return nil, fmt.Errorf("Key %s not found", keyName) } func (c *CloudAPI) CreateKey(keyName, key string) (*cloudapi.Key, error) { if err := c.ProcessFunctionHook(c, keyName, key); err != nil { return nil, err } // check if key already exists or keyName already in use for _, k := range c.keys { if k.Name == keyName { return nil, fmt.Errorf("Key name %s already in use", keyName) } if k.Key == key { return nil, fmt.Errorf("Key %s already exists", key) } } newKey := cloudapi.Key{Name: keyName, Fingerprint: "", Key: key} c.keys = append(c.keys, newKey) return &newKey, nil } func (c *CloudAPI) DeleteKey(keyName string) error { if err := c.ProcessFunctionHook(c, keyName); err != nil { return err } for i, key := range c.keys { if key.Name == keyName { c.keys = append(c.keys[:i], c.keys[i+1:]...) return nil } } return fmt.Errorf("Key %s not found", keyName) } // Packages APIs func (c *CloudAPI) ListPackages(filters map[string]string) ([]cloudapi.Package, error) { if err := c.ProcessFunctionHook(c, filters); err != nil { return nil, err } availablePackages := c.packages if filters != nil { for k, f := range filters { // check if valid filter if contains(packagesFilters, k) { pkgs := []cloudapi.Package{} // filter from availablePackages and add to pkgs for _, p := range availablePackages { if k == "name" && p.Name == f { pkgs = append(pkgs, p) } else if k == "memory" { i, err := strconv.Atoi(f) if err == nil && p.Memory == i { pkgs = append(pkgs, p) } } else if k == "disk" { i, err := strconv.Atoi(f) if err == nil && p.Disk == i { pkgs = append(pkgs, p) } } else if k == "swap" { i, err := strconv.Atoi(f) if err == nil && p.Swap == i { pkgs = append(pkgs, p) } } else if k == "version" && p.Version == f { pkgs = append(pkgs, p) } else if k == "vcpus" { i, err := strconv.Atoi(f) if err == nil && p.VCPUs == i { pkgs = append(pkgs, p) } } else if k == "group" && p.Group == f { pkgs = append(pkgs, p) } } availablePackages = pkgs } } } return availablePackages, nil } func (c *CloudAPI) GetPackage(packageName string) (*cloudapi.Package, error) { if err := c.ProcessFunctionHook(c, packageName); err != nil { return nil, err } for _, pkg := range c.packages { if pkg.Name == packageName { return &pkg, nil } if pkg.Id == packageName { return &pkg, nil } } return nil, fmt.Errorf("Package %s not found", packageName) } // Images APIs func (c *CloudAPI) ListImages(filters map[string]string) ([]cloudapi.Image, error) { if err := c.ProcessFunctionHook(c, filters); err != nil { return nil, err } availableImages := c.images if filters != nil { for k, f := range filters { // check if valid filter if contains(imagesFilters, k) { imgs := []cloudapi.Image{} // filter from availableImages and add to imgs for _, i := range availableImages { if k == "name" && i.Name == f { imgs = append(imgs, i) } else if k == "os" && i.OS == f { imgs = append(imgs, i) } else if k == "version" && i.Version == f { imgs = append(imgs, i) } else if k == "public" && i.Public == f { imgs = append(imgs, i) } else if k == "state" && i.State == f { imgs = append(imgs, i) } else if k == "owner" && i.Owner == f { imgs = append(imgs, i) } else if k == "type" && i.Type == f { imgs = append(imgs, i) } } availableImages = imgs } } } return availableImages, nil } func (c *CloudAPI) GetImage(imageId string) (*cloudapi.Image, error) { if err := c.ProcessFunctionHook(c, imageId); err != nil { return nil, err } for _, image := range c.images { if image.Id == imageId { return &image, nil } } return nil, fmt.Errorf("Image %s not found", imageId) } // Machine APIs func (c *CloudAPI) ListMachines(filters map[string]string) ([]*cloudapi.Machine, error) { if err := c.ProcessFunctionHook(c, filters); err != nil { return nil, err } availableMachines := c.machines if filters != nil { for k, f := range filters { // check if valid filter if contains(machinesFilters, k) { machines := []*cloudapi.Machine{} // filter from availableMachines and add to machines for _, m := range availableMachines { if k == "name" && m.Name == f { machines = append(machines, m) } else if k == "type" && m.Type == f { machines = append(machines, m) } else if k == "state" && m.State == f { machines = append(machines, m) } else if k == "image" && m.Image == f { machines = append(machines, m) } else if k == "memory" { i, err := strconv.Atoi(f) if err == nil && m.Memory == i { machines = append(machines, m) } } else if strings.HasPrefix(k, "tags.") { for t, v := range m.Tags { if t == k[strings.Index(k, ".")+1:] && v == f { machines = append(machines, m) } } } } availableMachines = machines } } } return availableMachines, nil } func (c *CloudAPI) CountMachines() (int, error) { if err := c.ProcessFunctionHook(c); err != nil { return 0, err } return len(c.machines), nil } func (c *CloudAPI) GetMachine(machineId string) (*cloudapi.Machine, error) { if err := c.ProcessFunctionHook(c, machineId); err != nil { return nil, err } for _, machine := range c.machines { if machine.Id == machineId { return machine, nil } } return nil, fmt.Errorf("Machine %s not found", machineId) } func (c *CloudAPI) CreateMachine(name, pkg, image string, metadata, tags map[string]string) (*cloudapi.Machine, error) { if err := c.ProcessFunctionHook(c, name, pkg, image); err != nil { return nil, err } machineId, err := localservices.NewUUID() if err != nil { return nil, err } mPkg, err := c.GetPackage(pkg) if err != nil { return nil, err } mImg, err := c.GetImage(image) if err != nil { return nil, err } publicIP := generatePublicIPAddress() newMachine := &cloudapi.Machine{ Id: machineId, Name: name, Type: mImg.Type, State: "running", Memory: mPkg.Memory, Disk: mPkg.Disk, IPs: []string{publicIP, generatePrivateIPAddress()}, Created: time.Now().Format("2013-11-26T19:47:13.448Z"), Package: pkg, Image: image, Metadata: metadata, Tags: tags, PrimaryIP: publicIP, } c.machines = append(c.machines, newMachine) return newMachine, nil } func (c *CloudAPI) StopMachine(machineId string) error { if err := c.ProcessFunctionHook(c, machineId); err != nil { return err } for _, machine := range c.machines { if machine.Id == machineId { machine.State = "stopped" machine.Updated = time.Now().Format("2013-11-26T19:47:13.448Z") return nil } } return fmt.Errorf("Machine %s not found", machineId) } func (c *CloudAPI) StartMachine(machineId string) error { if err := c.ProcessFunctionHook(c, machineId); err != nil { return err } for _, machine := range c.machines { if machine.Id == machineId { machine.State = "running" machine.Updated = time.Now().Format("2013-11-26T19:47:13.448Z") return nil } } return fmt.Errorf("Machine %s not found", machineId) } func (c *CloudAPI) RebootMachine(machineId string) error { if err := c.ProcessFunctionHook(c, machineId); err != nil { return err } for _, machine := range c.machines { if machine.Id == machineId { machine.State = "running" machine.Updated = time.Now().Format("2013-11-26T19:47:13.448Z") return nil } } return fmt.Errorf("Machine %s not found", machineId) } func (c *CloudAPI) ResizeMachine(machineId, packageName string) error { if err := c.ProcessFunctionHook(c, machineId, packageName); err != nil { return err } mPkg, err := c.GetPackage(packageName) if err != nil { return err } for _, machine := range c.machines { if machine.Id == machineId { machine.Package = packageName machine.Memory = mPkg.Memory machine.Disk = mPkg.Disk machine.Updated = time.Now().Format("2013-11-26T19:47:13.448Z") return nil } } return fmt.Errorf("Machine %s not found", machineId) } func (c *CloudAPI) RenameMachine(machineId, newName string) error { if err := c.ProcessFunctionHook(c, machineId, newName); err != nil { return err } for _, machine := range c.machines { if machine.Id == machineId { machine.Name = newName machine.Updated = time.Now().Format("2013-11-26T19:47:13.448Z") return nil } } return fmt.Errorf("Machine %s not found", machineId) } func (c *CloudAPI) ListMachineFirewallRules(machineId string) ([]*cloudapi.FirewallRule, error) { if err := c.ProcessFunctionHook(c, machineId); err != nil { return nil, err } fwRules := []*cloudapi.FirewallRule{} for _, r := range c.firewallRules { vm := "vm " + machineId if strings.Contains(r.Rule, vm) { fwRules = append(fwRules, r) } } return fwRules, nil } func (c *CloudAPI) EnableFirewallMachine(machineId string) error { if err := c.ProcessFunctionHook(c, machineId); err != nil { return err } c.machineFw[machineId] = true return nil } func (c *CloudAPI) DisableFirewallMachine(machineId string) error { if err := c.ProcessFunctionHook(c, machineId); err != nil { return err } c.machineFw[machineId] = false return nil } func (c *CloudAPI) DeleteMachine(machineId string) error { if err := c.ProcessFunctionHook(c, machineId); err != nil { return err } for i, machine := range c.machines { if machine.Id == machineId { if machine.State == "stopped" { c.machines = append(c.machines[:i], c.machines[i+1:]...) return nil } else { return fmt.Errorf("Cannot Delete machine %s, machine is not stopped.", machineId) } } } return fmt.Errorf("Machine %s not found", machineId) } // FirewallRule APIs func (c *CloudAPI) ListFirewallRules() ([]*cloudapi.FirewallRule, error) { if err := c.ProcessFunctionHook(c); err != nil { return nil, err } return c.firewallRules, nil } func (c *CloudAPI) GetFirewallRule(fwRuleId string) (*cloudapi.FirewallRule, error) { if err := c.ProcessFunctionHook(c, fwRuleId); err != nil { return nil, err } for _, r := range c.firewallRules { if strings.EqualFold(r.Id, fwRuleId) { return r, nil } } return nil, fmt.Errorf("Firewall rule %s not found", fwRuleId) } func (c *CloudAPI) CreateFirewallRule(rule string, enabled bool) (*cloudapi.FirewallRule, error) { if err := c.ProcessFunctionHook(c, rule, enabled); err != nil { return nil, err } fwRuleId, err := localservices.NewUUID() if err != nil { return nil, fmt.Errorf("Error creating firewall rule: %q", err) } fwRule := &cloudapi.FirewallRule{Id: fwRuleId, Rule: rule, Enabled: enabled} c.firewallRules = append(c.firewallRules, fwRule) return fwRule, nil } func (c *CloudAPI) UpdateFirewallRule(fwRuleId, rule string, enabled bool) (*cloudapi.FirewallRule, error) { if err := c.ProcessFunctionHook(c, fwRuleId, rule, enabled); err != nil { return nil, err } for _, r := range c.firewallRules { if strings.EqualFold(r.Id, fwRuleId) { r.Rule = rule r.Enabled = enabled return r, nil } } return nil, fmt.Errorf("Firewall rule %s not found", fwRuleId) } func (c *CloudAPI) EnableFirewallRule(fwRuleId string) (*cloudapi.FirewallRule, error) { if err := c.ProcessFunctionHook(c, fwRuleId); err != nil { return nil, err } for _, r := range c.firewallRules { if strings.EqualFold(r.Id, fwRuleId) { r.Enabled = true return r, nil } } return nil, fmt.Errorf("Firewall rule %s not found", fwRuleId) } func (c *CloudAPI) DisableFirewallRule(fwRuleId string) (*cloudapi.FirewallRule, error) { if err := c.ProcessFunctionHook(c, fwRuleId); err != nil { return nil, err } for _, r := range c.firewallRules { if strings.EqualFold(r.Id, fwRuleId) { r.Enabled = false return r, nil } } return nil, fmt.Errorf("Firewall rule %s not found", fwRuleId) } func (c *CloudAPI) DeleteFirewallRule(fwRuleId string) error { if err := c.ProcessFunctionHook(c, fwRuleId); err != nil { return err } for i, r := range c.firewallRules { if strings.EqualFold(r.Id, fwRuleId) { c.firewallRules = append(c.firewallRules[:i], c.firewallRules[i+1:]...) return nil } } return fmt.Errorf("Firewall rule %s not found", fwRuleId) } func (c *CloudAPI) ListFirewallRuleMachines(fwRuleId string) ([]*cloudapi.Machine, error) { if err := c.ProcessFunctionHook(c, fwRuleId); err != nil { return nil, err } return c.machines, nil } // Networks API func (c *CloudAPI) ListNetworks() ([]cloudapi.Network, error) { if err := c.ProcessFunctionHook(c); err != nil { return nil, err } return c.networks, nil } func (c *CloudAPI) GetNetwork(networkId string) (*cloudapi.Network, error) { if err := c.ProcessFunctionHook(c, networkId); err != nil { return nil, err } for _, n := range c.networks { if strings.EqualFold(n.Id, networkId) { return &n, nil } } return nil, fmt.Errorf("Network %s not found", networkId) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/localservices/cloudapi/service_http_test.go������������0000644�0000153�0000161�00000052166�12321735720�031436� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosdc - Go library to interact with the Joyent CloudAPI // // CloudAPI double testing service - HTTP API tests // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package cloudapi_test import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "path" "strconv" "strings" gc "launchpad.net/gocheck" "github.com/joyent/gocommon/testing" "github.com/joyent/gosdc/cloudapi" lc "github.com/joyent/gosdc/localservices/cloudapi" ) type CloudAPIHTTPSuite struct { testing.HTTPSuite service *lc.CloudAPI } var _ = gc.Suite(&CloudAPIHTTPSuite{}) type CloudAPIHTTPSSuite struct { testing.HTTPSuite service *lc.CloudAPI } var _ = gc.Suite(&CloudAPIHTTPSSuite{HTTPSuite: testing.HTTPSuite{UseTLS: true}}) func (s *CloudAPIHTTPSuite) SetUpSuite(c *gc.C) { s.HTTPSuite.SetUpSuite(c) c.Assert(s.Server.URL[:7], gc.Equals, "http://") s.service = lc.New(s.Server.URL, testUserAccount) } func (s *CloudAPIHTTPSuite) TearDownSuite(c *gc.C) { s.HTTPSuite.TearDownSuite(c) } func (s *CloudAPIHTTPSuite) SetUpTest(c *gc.C) { s.HTTPSuite.SetUpTest(c) s.service.SetupHTTP(s.Mux) } func (s *CloudAPIHTTPSuite) TearDownTest(c *gc.C) { s.HTTPSuite.TearDownTest(c) } // assertJSON asserts the passed http.Response's body can be // unmarshalled into the given expected object, populating it with the // successfully parsed data. func assertJSON(c *gc.C, resp *http.Response, expected interface{}) { body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() c.Assert(err, gc.IsNil) err = json.Unmarshal(body, &expected) c.Assert(err, gc.IsNil) } // assertBody asserts the passed http.Response's body matches the // expected response, replacing any variables in the expected body. func assertBody(c *gc.C, resp *http.Response, expected *lc.ErrorResponse) { body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() c.Assert(err, gc.IsNil) expBody := expected.Body // cast to string for easier asserts debugging c.Assert(string(body), gc.Equals, string(expBody)) } // sendRequest constructs an HTTP request from the parameters and // sends it, returning the response or an error. func (s *CloudAPIHTTPSuite) sendRequest(method, path string, body []byte, headers http.Header) (*http.Response, error) { if headers == nil { headers = make(http.Header) } requestURL := "http://" + s.service.Hostname + strings.TrimLeft(path, "/") req, err := http.NewRequest(method, requestURL, bytes.NewReader(body)) if err != nil { return nil, err } req.Close = true for header, values := range headers { for _, value := range values { req.Header.Add(header, value) } } // workaround for https://code.google.com/p/go/issues/detail?id=4454 req.Header.Set("Content-Length", strconv.Itoa(len(body))) return http.DefaultClient.Do(req) } // jsonRequest serializes the passed body object to JSON and sends a // the request with authRequest(). func (s *CloudAPIHTTPSuite) jsonRequest(method, path string, body interface{}, headers http.Header) (*http.Response, error) { jsonBody, err := json.Marshal(body) if err != nil { return nil, err } return s.sendRequest(method, path, jsonBody, headers) } // Helpers func (s *CloudAPIHTTPSuite) createKey(c *gc.C, keyName, key string) *cloudapi.Key { opts := cloudapi.CreateKeyOpts{Name: keyName, Key: key} optsByte, err := json.Marshal(opts) c.Assert(err, gc.IsNil) resp, err := s.sendRequest("POST", path.Join(testUserAccount, "keys"), optsByte, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) k := &cloudapi.Key{} assertJSON(c, resp, k) return k } func (s *CloudAPIHTTPSuite) deleteKey(c *gc.C, keyName string) { resp, err := s.sendRequest("DELETE", path.Join(testUserAccount, "keys", keyName), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) } func (s *CloudAPIHTTPSuite) createMachine(c *gc.C, name, pkg, image string, metadata, tags map[string]string) *cloudapi.Machine { opts := cloudapi.CreateMachineOpts{Name: name, Image: image, Package: pkg, Tags: tags, Metadata: metadata} optsByte, err := json.Marshal(opts) c.Assert(err, gc.IsNil) resp, err := s.sendRequest("POST", path.Join(testUserAccount, "machines"), optsByte, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) m := &cloudapi.Machine{} assertJSON(c, resp, m) return m } func (s *CloudAPIHTTPSuite) getMachine(c *gc.C, machineId string, expected *cloudapi.Machine) { resp, err := s.sendRequest("GET", path.Join(testUserAccount, "machines", machineId), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, expected) } func (s *CloudAPIHTTPSuite) deleteMachine(c *gc.C, machineId string) { resp, err := s.sendRequest("POST", fmt.Sprintf("%s?action=stop", path.Join(testUserAccount, "machines", machineId)), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted) resp, err = s.sendRequest("DELETE", path.Join(testUserAccount, "machines", machineId), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) } // Helper method to create a test firewall rule func (s *CloudAPIHTTPSuite) createFirewallRule(c *gc.C) *cloudapi.FirewallRule { opts := cloudapi.CreateFwRuleOpts{Rule: testFwRule, Enabled: true} optsByte, err := json.Marshal(opts) c.Assert(err, gc.IsNil) resp, err := s.sendRequest("POST", path.Join(testUserAccount, "fwrules"), optsByte, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) r := &cloudapi.FirewallRule{} assertJSON(c, resp, r) return r } // Helper method to a test firewall rule func (s *CloudAPIHTTPSuite) deleteFwRule(c *gc.C, fwRuleId string) { resp, err := s.sendRequest("DELETE", path.Join(testUserAccount, "fwrules", fwRuleId), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) } // SimpleTest defines a simple request without a body and expected response. type SimpleTest struct { method string url string headers http.Header expect *lc.ErrorResponse } func (s *CloudAPIHTTPSuite) simpleTests() []SimpleTest { var simpleTests = []SimpleTest{ { method: "GET", url: "/", headers: make(http.Header), expect: lc.ErrNotFound, }, { method: "POST", url: "/", headers: make(http.Header), expect: lc.ErrNotFound, }, { method: "DELETE", url: "/", headers: make(http.Header), expect: lc.ErrNotFound, }, { method: "PUT", url: "/", headers: make(http.Header), expect: lc.ErrNotFound, }, { method: "GET", url: "/any", headers: make(http.Header), expect: lc.ErrNotFound, }, { method: "POST", url: "/any", headers: make(http.Header), expect: lc.ErrNotFound, }, { method: "DELETE", url: "/any", headers: make(http.Header), expect: lc.ErrNotFound, }, { method: "PUT", url: "/any", headers: make(http.Header), expect: lc.ErrNotFound, }, { method: "PUT", url: path.Join(testUserAccount, "keys"), headers: make(http.Header), expect: lc.ErrNotAllowed, }, { method: "PUT", url: path.Join(testUserAccount, "images"), headers: make(http.Header), expect: lc.ErrNotAllowed, }, { method: "POST", url: path.Join(testUserAccount, "packages"), headers: make(http.Header), expect: lc.ErrNotAllowed, }, { method: "PUT", url: path.Join(testUserAccount, "packages"), headers: make(http.Header), expect: lc.ErrNotAllowed, }, { method: "DELETE", url: path.Join(testUserAccount, "packages"), headers: make(http.Header), expect: lc.ErrNotAllowed, }, { method: "PUT", url: path.Join(testUserAccount, "machines"), headers: make(http.Header), expect: lc.ErrNotAllowed, }, { method: "PUT", url: path.Join(testUserAccount, "fwrules"), headers: make(http.Header), expect: lc.ErrNotAllowed, }, { method: "POST", url: path.Join(testUserAccount, "networks"), headers: make(http.Header), expect: lc.ErrNotAllowed, }, { method: "PUT", url: path.Join(testUserAccount, "networks"), headers: make(http.Header), expect: lc.ErrNotAllowed, }, { method: "DELETE", url: path.Join(testUserAccount, "networks"), headers: make(http.Header), expect: lc.ErrNotAllowed, }, } return simpleTests } func (s *CloudAPIHTTPSuite) TestSimpleRequestTests(c *gc.C) { simpleTests := s.simpleTests() for i, t := range simpleTests { c.Logf("#%d. %s %s -> %d", i, t.method, t.url, t.expect.Code) if t.headers == nil { t.headers = make(http.Header) } var ( resp *http.Response err error ) resp, err = s.sendRequest(t.method, t.url, nil, t.headers) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, t.expect.Code) assertBody(c, resp, t.expect) } } // Tests for Keys API func (s *CloudAPIHTTPSuite) TestListKeys(c *gc.C) { var expected []cloudapi.Key k := s.createKey(c, testKeyName, testKey) defer s.deleteKey(c, testKeyName) resp, err := s.sendRequest("GET", path.Join(testUserAccount, "keys"), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) for _, key := range expected { if c.Check(&key, gc.DeepEquals, k) { c.SucceedNow() } } c.Fatalf("Obtained keys [%s] do not contain test key [%s]", expected, k) } func (s *CloudAPIHTTPSuite) TestGetKey(c *gc.C) { var expected cloudapi.Key k := s.createKey(c, testKeyName, testKey) defer s.deleteKey(c, testKeyName) resp, err := s.sendRequest("GET", path.Join(testUserAccount, "keys", testKeyName), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(&expected, gc.DeepEquals, k) } func (s *CloudAPIHTTPSuite) TestCreateKey(c *gc.C) { k := s.createKey(c, testKeyName, testKey) defer s.deleteKey(c, testKeyName) c.Assert(k.Name, gc.Equals, testKeyName) c.Assert(k.Key, gc.Equals, testKey) } func (s *CloudAPIHTTPSuite) TestDeleteKey(c *gc.C) { s.createKey(c, testKeyName, testKey) s.deleteKey(c, testKeyName) } // Tests for Images API func (s *CloudAPIHTTPSuite) TestListImages(c *gc.C) { var expected []cloudapi.Image resp, err := s.sendRequest("GET", path.Join(testUserAccount, "images"), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(len(expected), gc.Equals, 6) } func (s *CloudAPIHTTPSuite) TestGetImage(c *gc.C) { var expected cloudapi.Image resp, err := s.sendRequest("GET", path.Join(testUserAccount, "images", testImage), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Id, gc.Equals, "11223344-0a0a-ff99-11bb-0a1b2c3d4e5f") c.Assert(expected.Name, gc.Equals, "ubuntu12.04") c.Assert(expected.OS, gc.Equals, "linux") c.Assert(expected.Version, gc.Equals, "2.3.1") c.Assert(expected.Type, gc.Equals, "virtualmachine") c.Assert(expected.Description, gc.Equals, "Test Ubuntu 12.04 image (64 bit)") c.Assert(expected.PublishedAt, gc.Equals, "2014-01-20T16:12:31Z") c.Assert(expected.Public, gc.Equals, "true") c.Assert(expected.State, gc.Equals, "active") } // Tests for Packages API func (s *CloudAPIHTTPSuite) TestListPackages(c *gc.C) { var expected []cloudapi.Package resp, err := s.sendRequest("GET", path.Join(testUserAccount, "packages"), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(len(expected), gc.Equals, 4) } func (s *CloudAPIHTTPSuite) TestGetPackage(c *gc.C) { var expected cloudapi.Package resp, err := s.sendRequest("GET", path.Join(testUserAccount, "packages", testPackage), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Name, gc.Equals, "Small") c.Assert(expected.Memory, gc.Equals, 1024) c.Assert(expected.Disk, gc.Equals, 16384) c.Assert(expected.Swap, gc.Equals, 2048) c.Assert(expected.VCPUs, gc.Equals, 1) c.Assert(expected.Default, gc.Equals, true) c.Assert(expected.Id, gc.Equals, "11223344-1212-abab-3434-aabbccddeeff") c.Assert(expected.Version, gc.Equals, "1.0.2") } // Tests for Machines API func (s *CloudAPIHTTPSuite) TestListMachines(c *gc.C) { var expected []cloudapi.Machine m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) resp, err := s.sendRequest("GET", path.Join(testUserAccount, "machines"), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) for _, machine := range expected { if machine.Id == m.Id { c.SucceedNow() } } c.Fatalf("Obtained machine [%s] do not contain test machine [%s]", expected, m) } /*func (s *CloudAPIHTTPSuite) TestCountMachines(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) resp, err := s.sendRequest("HEAD", path.Join(testUserAccount, "machines"), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) fmt.Printf("Got response %q\n", resp) assertBody(c, resp, &lc.ErrorResponse{Body: "1"}) } */ func (s *CloudAPIHTTPSuite) TestGetMachine(c *gc.C) { var expected cloudapi.Machine m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) s.getMachine(c, m.Id, &expected) c.Assert(expected.Name, gc.Equals, testMachineName) c.Assert(expected.Package, gc.Equals, testPackage) c.Assert(expected.Image, gc.Equals, testImage) } func (s *CloudAPIHTTPSuite) TestCreateMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) c.Assert(m.Name, gc.Equals, testMachineName) c.Assert(m.Package, gc.Equals, testPackage) c.Assert(m.Image, gc.Equals, testImage) c.Assert(m.Type, gc.Equals, "virtualmachine") c.Assert(len(m.IPs), gc.Equals, 2) c.Assert(m.State, gc.Equals, "running") } func (s *CloudAPIHTTPSuite) TestStartMachine(c *gc.C) { var expected cloudapi.Machine m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) resp, err := s.sendRequest("POST", fmt.Sprintf("%s?action=start", path.Join(testUserAccount, "machines", m.Id)), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted) s.getMachine(c, m.Id, &expected) c.Assert(expected.State, gc.Equals, "running") } func (s *CloudAPIHTTPSuite) TestStopMachine(c *gc.C) { var expected cloudapi.Machine m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) resp, err := s.sendRequest("POST", fmt.Sprintf("%s?action=stop", path.Join(testUserAccount, "machines", m.Id)), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted) s.getMachine(c, m.Id, &expected) c.Assert(expected.State, gc.Equals, "stopped") } func (s *CloudAPIHTTPSuite) TestRebootMachine(c *gc.C) { var expected cloudapi.Machine m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) resp, err := s.sendRequest("POST", fmt.Sprintf("%s?action=reboot", path.Join(testUserAccount, "machines", m.Id)), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted) s.getMachine(c, m.Id, &expected) c.Assert(expected.State, gc.Equals, "running") } func (s *CloudAPIHTTPSuite) TestResizeMachine(c *gc.C) { var expected cloudapi.Machine m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) resp, err := s.sendRequest("POST", fmt.Sprintf("%s?action=resize&package=Medium", path.Join(testUserAccount, "machines", m.Id)), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted) s.getMachine(c, m.Id, &expected) c.Assert(expected.Package, gc.Equals, "Medium") c.Assert(expected.Memory, gc.Equals, 2048) c.Assert(expected.Disk, gc.Equals, 32768) } func (s *CloudAPIHTTPSuite) TestRenameMachine(c *gc.C) { var expected cloudapi.Machine m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) resp, err := s.sendRequest("POST", fmt.Sprintf("%s?action=rename&name=new-test-name", path.Join(testUserAccount, "machines", m.Id)), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted) s.getMachine(c, m.Id, &expected) c.Assert(expected.Name, gc.Equals, "new-test-name") } func (s *CloudAPIHTTPSuite) TestListMachinesFirewallRules(c *gc.C) { var expected []cloudapi.FirewallRule m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) resp, err := s.sendRequest("GET", path.Join(testUserAccount, "machines", m.Id, "fwrules"), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) } func (s *CloudAPIHTTPSuite) TestEnableFirewallMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) resp, err := s.sendRequest("POST", fmt.Sprintf("%s?action=enable_firewall", path.Join(testUserAccount, "machines", m.Id)), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted) } func (s *CloudAPIHTTPSuite) TestDisableFirewallMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) defer s.deleteMachine(c, m.Id) resp, err := s.sendRequest("POST", fmt.Sprintf("%s?action=disable_firewall", path.Join(testUserAccount, "machines", m.Id)), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted) } func (s *CloudAPIHTTPSuite) TestDeleteMachine(c *gc.C) { m := s.createMachine(c, testMachineName, testPackage, testImage, nil, nil) s.deleteMachine(c, m.Id) } // Tests for FirewallRules API func (s *CloudAPIHTTPSuite) TestCreateFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) // cleanup s.deleteFwRule(c, testFwRule.Id) } func (s *CloudAPIHTTPSuite) TestListFirewallRules(c *gc.C) { var expected []cloudapi.FirewallRule testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) resp, err := s.sendRequest("GET", path.Join(testUserAccount, "fwrules"), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) } func (s *CloudAPIHTTPSuite) TestGetFirewallRule(c *gc.C) { var expected cloudapi.FirewallRule testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) resp, err := s.sendRequest("GET", path.Join(testUserAccount, "fwrules", testFwRule.Id), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) } func (s *CloudAPIHTTPSuite) TestUpdateFirewallRule(c *gc.C) { var expected cloudapi.FirewallRule testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) opts := cloudapi.CreateFwRuleOpts{Rule: testUpdatedFwRule, Enabled: true} optsByte, err := json.Marshal(opts) c.Assert(err, gc.IsNil) resp, err := s.sendRequest("POST", path.Join(testUserAccount, "fwrules", testFwRule.Id), optsByte, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Rule, gc.Equals, testUpdatedFwRule) } func (s *CloudAPIHTTPSuite) TestEnableFirewallRule(c *gc.C) { var expected cloudapi.FirewallRule testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) resp, err := s.sendRequest("POST", path.Join(testUserAccount, "fwrules", testFwRule.Id, "enable"), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Enabled, gc.Equals, true) } func (s *CloudAPIHTTPSuite) TestDisableFirewallRule(c *gc.C) { var expected cloudapi.FirewallRule testFwRule := s.createFirewallRule(c) defer s.deleteFwRule(c, testFwRule.Id) resp, err := s.sendRequest("POST", path.Join(testUserAccount, "fwrules", testFwRule.Id, "disable"), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Enabled, gc.Equals, false) } func (s *CloudAPIHTTPSuite) TestDeleteFirewallRule(c *gc.C) { testFwRule := s.createFirewallRule(c) s.deleteFwRule(c, testFwRule.Id) } // tests for Networks API func (s *CloudAPIHTTPSuite) TestListNetworks(c *gc.C) { var expected []cloudapi.Network resp, err := s.sendRequest("GET", path.Join(testUserAccount, "networks"), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) } func (s *CloudAPIHTTPSuite) TestGetNetwork(c *gc.C) { var expected cloudapi.Network resp, err := s.sendRequest("GET", path.Join(testUserAccount, "networks", testNetworkId), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected, gc.DeepEquals, cloudapi.Network{ Id: testNetworkId, Name: "Test-Joyent-Public", Public: true, Description: "", }) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/localservices/hook/������������������������������������0000755�0000153�0000161�00000000000�12321735720�024477� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/localservices/hook/service_test.go���������������������0000644�0000153�0000161�00000004751�12321735720�027534� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package hook import ( "fmt" gc "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { gc.TestingT(t) } var _ = gc.Suite(&ServiceSuite{}) type ServiceSuite struct { ts *testService } func (s *ServiceSuite) SetUpTest(c *gc.C) { s.ts = newTestService() // This hook is called based on the function name. s.ts.RegisterControlPoint("foo", functionControlHook) // This hook is called based on a user specified hook name. s.ts.RegisterControlPoint("foobar", namedControlHook) } type testService struct { TestService label string } func newTestService() *testService { return &testService{ TestService: TestService{ ControlHooks: make(map[string]ControlProcessor), }, } } func functionControlHook(s ServiceControl, args ...interface{}) error { label := args[0].(string) returnError := args[1].(bool) if returnError { return fmt.Errorf("An error occurred") } s.(*testService).label = label return nil } func namedControlHook(s ServiceControl, args ...interface{}) error { s.(*testService).label = "foobar" return nil } func (s *testService) foo(label string, returnError bool) error { if err := s.ProcessFunctionHook(s, label, returnError); err != nil { return err } return nil } func (s *testService) bar() error { if err := s.ProcessControlHook("foobar", s); err != nil { return err } return nil } func (s *ServiceSuite) TestFunctionHookNoError(c *gc.C) { err := s.ts.foo("success", false) c.Assert(err, gc.IsNil) c.Assert(s.ts.label, gc.Equals, "success") } func (s *ServiceSuite) TestHookWithError(c *gc.C) { err := s.ts.foo("success", true) c.Assert(err, gc.Not(gc.IsNil)) c.Assert(s.ts.label, gc.Equals, "") } func (s *ServiceSuite) TestNamedHook(c *gc.C) { err := s.ts.bar() c.Assert(err, gc.IsNil) c.Assert(s.ts.label, gc.Equals, "foobar") } func (s *ServiceSuite) TestHookCleanup(c *gc.C) { // Manually delete the existing control point. s.ts.RegisterControlPoint("foo", nil) // Register a new hook and ensure it works. cleanup := s.ts.RegisterControlPoint("foo", functionControlHook) err := s.ts.foo("cleanuptest", false) c.Assert(err, gc.IsNil) c.Assert(s.ts.label, gc.Equals, "cleanuptest") // Use the cleanup func to remove the hook and check the result. cleanup() err = s.ts.foo("again", false) c.Assert(err, gc.IsNil) c.Assert(s.ts.label, gc.Equals, "cleanuptest") // Ensure that only the specified hook was removed and the other remaining one still works. err = s.ts.bar() c.Assert(err, gc.IsNil) c.Assert(s.ts.label, gc.Equals, "foobar") } �����������������������juju-core_1.18.1/src/github.com/joyent/gosdc/localservices/hook/service.go��������������������������0000644�0000153�0000161�00000006216�12321735720�026473� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package hook import ( "runtime" "strings" ) type TestService struct { ServiceControl // Hooks to run when specified control points are reached in the service business logic. ControlHooks map[string]ControlProcessor } // ControlProcessor defines a function that is run when a specified control point is reached in the service // business logic. The function receives the service instance so internal state can be inspected, plus for any // arguments passed to the currently executing service function. type ControlProcessor func(sc ServiceControl, args ...interface{}) error // ControlHookCleanup defines a function used to remove a control hook. type ControlHookCleanup func() // ServiceControl instances allow hooks to be registered for execution at the specified point of execution. // The control point name can be a function name or a logical execution point meaningful to the service. // If name is "", the hook for the currently executing function is executed. // Returns a function which can be used to remove the hook. type ServiceControl interface { RegisterControlPoint(name string, controller ControlProcessor) ControlHookCleanup } // currentServiceMethodName returns the method executing on the service when ProcessControlHook was invoked. func (s *TestService) currentServiceMethodName() string { pc, _, _, ok := runtime.Caller(2) if !ok { panic("current method name cannot be found") } return unqualifiedMethodName(pc) } func unqualifiedMethodName(pc uintptr) string { f := runtime.FuncForPC(pc) fullName := f.Name() nameParts := strings.Split(fullName, ".") return nameParts[len(nameParts)-1] } // ProcessControlHook retrieves the ControlProcessor for the specified hook name and runs it, returning any error. // Use it like this to invoke a hook registered for some arbitrary control point: // if err := n.ProcessControlHook("foobar", <serviceinstance>, <somearg1>, <somearg2>); err != nil { // return err // } func (s *TestService) ProcessControlHook(hookName string, sc ServiceControl, args ...interface{}) error { if s.ControlHooks == nil { return nil } if hook, ok := s.ControlHooks[hookName]; ok { return hook(sc, args...) } return nil } // ProcessFunctionHook runs the ControlProcessor for the current function, returning any error. // Use it like this: // if err := n.ProcessFunctionHook(<serviceinstance>, <somearg1>, <somearg2>); err != nil { // return err // } func (s *TestService) ProcessFunctionHook(sc ServiceControl, args ...interface{}) error { hookName := s.currentServiceMethodName() return s.ProcessControlHook(hookName, sc, args...) } // RegisterControlPoint assigns the specified controller to the named hook. If nil, any existing controller for the // hook is removed. // hookName is the name of a function on the service or some arbitrarily named control point. func (s *TestService) RegisterControlPoint(hookName string, controller ControlProcessor) ControlHookCleanup { if s.ControlHooks == nil { s.ControlHooks = make(map[string]ControlProcessor) } if controller == nil { delete(s.ControlHooks, hookName) } else { s.ControlHooks[hookName] = controller } return func() { s.RegisterControlPoint(hookName, nil) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/localservices/localservice.go��������������������������0000644�0000153�0000161�00000001723�12321735720�026544� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gosdc - Go library to interact with the Joyent CloudAPI // // Double testing service // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package localservices import ( "crypto/rand" "fmt" "io" "net/http" "github.com/joyent/gosdc/localservices/hook" ) // An HttpService provides the HTTP API for a service double. type HttpService interface { SetupHTTP(mux *http.ServeMux) } // A ServiceInstance is an Joyent Cloud service. type ServiceInstance struct { hook.TestService Scheme string Hostname string UserAccount string } // NewUUID generates a random UUID according to RFC 4122 func NewUUID() (string, error) { uuid := make([]byte, 16) n, err := io.ReadFull(rand.Reader, uuid) if n != len(uuid) || err != nil { return "", err } uuid[8] = uuid[8]&^0xc0 | 0x80 uuid[6] = uuid[6]&^0xf0 | 0x40 return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil } ���������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/README.md����������������������������������������������0000644�0000153�0000161�00000000014�12321735720�022153� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gosdc ===== ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/COPYING������������������������������������������������0000644�0000153�0000161�00000104513�12321735720�021740� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gosdc/.gitignore���������������������������������������������0000644�0000153�0000161�00000000431�12321735720�022667� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe # IntelliJ files .idea *.iml���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/�����������������������������������������������������0000755�0000153�0000161�00000000000�12321735717�021236� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/COPYING.LESSER���������������������������������������0000644�0000153�0000161�00000016743�12321735717�023300� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �����������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/manta/�����������������������������������������������0000755�0000153�0000161�00000000000�12321735717�022336� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/manta/manta_test.go����������������������������������0000644�0000153�0000161�00000001306�12321735717�025024� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gomanta - Go library to interact with Joyent Manta // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package manta_test import ( "flag" "github.com/joyent/gocommon/jpc" gc "launchpad.net/gocheck" "testing" ) var live = flag.Bool("live", false, "Include live Manta tests") var keyName = flag.String("key.name", "", "Specify the full path to the private key, defaults to ~/.ssh/id_rsa") func Test(t *testing.T) { if *live { creds, err := jpc.CompleteCredentialsFromEnv(*keyName) if err != nil { t.Fatalf("Error setting up test suite: %s", err.Error()) } registerMantaTests(creds) } registerLocalTests(*keyName) gc.TestingT(t) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/manta/local_test.go����������������������������������0000644�0000153�0000161�00000015625�12321735717�025027� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gomanta - Go library to interact with Joyent Manta // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package manta_test import ( "fmt" "io/ioutil" "net/http" "net/http/httptest" "os" "strings" gc "launchpad.net/gocheck" "github.com/joyent/gocommon/client" localmanta "github.com/joyent/gomanta/localservices/manta" "github.com/joyent/gomanta/manta" "github.com/joyent/gosign/auth" ) var privateKey []byte func registerLocalTests(keyName string) { var localKeyFile string if keyName == "" { localKeyFile = os.Getenv("HOME") + "/.ssh/id_rsa" } else { localKeyFile = keyName } privateKey, _ = ioutil.ReadFile(localKeyFile) gc.Suite(&LocalTests{}) } type LocalTests struct { LiveTests Server *httptest.Server Mux *http.ServeMux oldHandler http.Handler manta *localmanta.Manta } func (s *LocalTests) SetUpSuite(c *gc.C) { // Set up the HTTP server. s.Server = httptest.NewServer(nil) s.oldHandler = s.Server.Config.Handler s.Mux = http.NewServeMux() s.Server.Config.Handler = s.Mux // Set up a Joyent Manta service. authentication := auth.Auth{User: "localtest", PrivateKey: string(privateKey), Algorithm: "rsa-sha256"} s.creds = &auth.Credentials{ UserAuthentication: authentication, MantaKeyId: "", MantaEndpoint: auth.Endpoint{URL: s.Server.URL}, } s.manta = localmanta.New(s.creds.MantaEndpoint.URL, s.creds.UserAuthentication.User) s.manta.SetupHTTP(s.Mux) } func (s *LocalTests) TearDownSuite(c *gc.C) { s.Mux = nil s.Server.Config.Handler = s.oldHandler s.Server.Close() } func (s *LocalTests) SetUpTest(c *gc.C) { client := client.NewClient(s.creds.MantaEndpoint.URL, "", s.creds, &manta.Logger) c.Assert(client, gc.NotNil) s.testClient = manta.New(client) c.Assert(s.testClient, gc.NotNil) } // Helper method to create a test directory func (s *LocalTests) createDirectory(c *gc.C, path string) { err := s.testClient.PutDirectory(path) c.Assert(err, gc.IsNil) } // Helper method to create a test object func (s *LocalTests) createObject(c *gc.C, path, objName string) { err := s.testClient.PutObject(path, objName, []byte("Test Manta API")) c.Assert(err, gc.IsNil) } // Helper method to delete a test directory func (s *LocalTests) deleteDirectory(c *gc.C, path string) { err := s.testClient.DeleteDirectory(path) c.Assert(err, gc.IsNil) } // Helper method to delete a test object func (s *LocalTests) deleteObject(c *gc.C, path, objName string) { err := s.testClient.DeleteObject(path, objName) c.Assert(err, gc.IsNil) } // Helper method to create a test job func (s *LocalTests) createJob(c *gc.C, jobName string) string { phases := []manta.Phase{ {Type: "map", Exec: "wc", Init: ""}, {Type: "reduce", Exec: "awk '{ l += $1; w += $2; c += $3 } END { print l, w, c }'", Init: ""}, } jobUri, err := s.testClient.CreateJob(manta.CreateJobOpts{Name: jobName, Phases: phases}) c.Assert(err, gc.IsNil) c.Assert(jobUri, gc.NotNil) return strings.Split(jobUri, "/")[3] } // Storage API func (s *LocalTests) TestPutDirectory(c *gc.C) { s.createDirectory(c, "test") // cleanup s.deleteDirectory(c, "test") } func (s *LocalTests) TestListDirectory(c *gc.C) { s.createDirectory(c, "test") defer s.deleteDirectory(c, "test") s.createObject(c, "test", "obj") defer s.deleteObject(c, "test", "obj") opts := manta.ListDirectoryOpts{} dirs, err := s.testClient.ListDirectory("test", opts) c.Assert(err, gc.IsNil) c.Assert(dirs, gc.NotNil) } func (s *LocalTests) TestDeleteDirectory(c *gc.C) { s.createDirectory(c, "test") s.deleteDirectory(c, "test") } func (s *LocalTests) TestPutObject(c *gc.C) { s.createDirectory(c, "dir") defer s.deleteDirectory(c, "dir") s.createObject(c, "dir", "obj") defer s.deleteObject(c, "dir", "obj") } func (s *LocalTests) TestGetObject(c *gc.C) { s.createDirectory(c, "dir") defer s.deleteDirectory(c, "dir") s.createObject(c, "dir", "obj") defer s.deleteObject(c, "dir", "obj") obj, err := s.testClient.GetObject("dir", "obj") c.Assert(err, gc.IsNil) c.Assert(obj, gc.NotNil) c.Check(string(obj), gc.Equals, "Test Manta API") } func (s *LocalTests) TestDeleteObject(c *gc.C) { s.createDirectory(c, "dir") defer s.deleteDirectory(c, "dir") s.createObject(c, "dir", "obj") s.deleteObject(c, "dir", "obj") } func (s *LocalTests) TestPutSnapLink(c *gc.C) { s.createDirectory(c, "linkdir") defer s.deleteDirectory(c, "linkdir") s.createObject(c, "linkdir", "obj") defer s.deleteObject(c, "linkdir", "obj") location := fmt.Sprintf("/%s/%s", s.creds.UserAuthentication.User, "stor/linkdir/obj") err := s.testClient.PutSnapLink("linkdir", "objlnk", location) c.Assert(err, gc.IsNil) // cleanup s.deleteObject(c, "linkdir", "objlnk") } // Jobs API func (s *LocalTests) TestCreateJob(c *gc.C) { s.createJob(c, "test-job") } func (s *LocalTests) TestListLiveJobs(c *gc.C) { s.createJob(c, "test-job") jobs, err := s.testClient.ListJobs(true) c.Assert(err, gc.IsNil) c.Assert(jobs, gc.NotNil) c.Assert(len(jobs) >= 1, gc.Equals, true) } func (s *LocalTests) TestListAllJobs(c *gc.C) { s.createJob(c, "test-job") jobs, err := s.testClient.ListJobs(false) c.Assert(err, gc.IsNil) c.Assert(jobs, gc.NotNil) } func (s *LocalTests) TestCancelJob(c *gc.C) { jobId := s.createJob(c, "test-job") err := s.testClient.CancelJob(jobId) c.Assert(err, gc.IsNil) } func (s *LocalTests) TestAddJobInputs(c *gc.C) { var inputs = `/` + s.creds.UserAuthentication.User + `/stor/testjob/obj1 /` + s.creds.UserAuthentication.User + `/stor/testjob/obj2 ` s.createDirectory(c, "testjob") defer s.deleteDirectory(c, "testjob") s.createObject(c, "testjob", "obj1") defer s.deleteObject(c, "testjob", "obj1") s.createObject(c, "testjob", "obj2") defer s.deleteObject(c, "testjob", "obj2") jobId := s.createJob(c, "test-job") err := s.testClient.AddJobInputs(jobId, strings.NewReader(inputs)) c.Assert(err, gc.IsNil) } func (s *LocalTests) TestEndJobInputs(c *gc.C) { jobId := s.createJob(c, "test-job") err := s.testClient.EndJobInputs(jobId) c.Assert(err, gc.IsNil) } func (s *LocalTests) TestGetJob(c *gc.C) { jobId := s.createJob(c, "test-job") job, err := s.testClient.GetJob(jobId) c.Assert(err, gc.IsNil) c.Assert(job, gc.NotNil) } func (s *LocalTests) TestGetJobInput(c *gc.C) { jobId := s.createJob(c, "test-job") input, err := s.testClient.GetJobInput(jobId) c.Assert(err, gc.IsNil) c.Assert(input, gc.NotNil) } func (s *LocalTests) TestGetJobOutput(c *gc.C) { jobId := s.createJob(c, "test-job") output, err := s.testClient.GetJobOutput(jobId) c.Assert(err, gc.IsNil) c.Assert(output, gc.NotNil) } func (s *LocalTests) TestGetJobFailures(c *gc.C) { jobId := s.createJob(c, "test-job") fail, err := s.testClient.GetJobFailures(jobId) c.Assert(err, gc.IsNil) c.Assert(fail, gc.Equals, "") } func (s *LocalTests) TestGetJobErrors(c *gc.C) { jobId := s.createJob(c, "test-job") errs, err := s.testClient.GetJobErrors(jobId) c.Assert(err, gc.IsNil) c.Assert(errs, gc.IsNil) } �����������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/manta/manta.go���������������������������������������0000644�0000153�0000161�00000036052�12321735717�023773� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* The gomanta/manta package interacts with the Manta API (http://apidocs.joyent.com/manta/api.html). Licensed under LGPL v3. Copyright (c) 2013 Joyent Inc. Written by Daniele Stroppa <daniele.stroppa@joyent.com> */ package manta import ( "bytes" "fmt" "io" "io/ioutil" "net/http" "path" "time" "github.com/juju/loggo" "github.com/joyent/gocommon/client" "github.com/joyent/gocommon/errors" jh "github.com/joyent/gocommon/http" ) // Logger for this package var Logger = loggo.GetLogger("gomanta.manta") const ( // The default version of the Manta API to use DefaultAPIVersion = "7.1" // Manta API URL parts apiStorage = "stor" apiJobs = "jobs" apiJobsLive = "live" apiJobsIn = "in" apiJobsOut = "out" apiJobsFail = "fail" apiJobsErr = "err" apiJobsEnd = "end" apiJobsCancel = "cancel" apiJobsStatus = "status" ) // Client provides a means to access Joyent Manta type Client struct { client client.Client } // New creates a new Client. func New(client client.Client) *Client { return &Client{client} } // request represents an API request type request struct { method string url string reqValue interface{} reqHeader http.Header reqReader io.Reader reqLength int resp interface{} respHeader *http.Header expectedStatus int } // Helper method to send an API request func (c *Client) sendRequest(req request) (*jh.ResponseData, error) { request := jh.RequestData{ ReqValue: req.reqValue, ReqHeaders: req.reqHeader, ReqReader: req.reqReader, ReqLength: req.reqLength, } if req.expectedStatus == 0 { req.expectedStatus = http.StatusOK } respData := jh.ResponseData{ RespValue: req.resp, RespHeaders: req.respHeader, ExpectedStatus: []int{req.expectedStatus}, } err := c.client.SendRequest(req.method, req.url, "", &request, &respData) return &respData, err } // Helper method to create the API URL func makeURL(parts ...string) string { return path.Join(parts...) } // ListDirectoryOpts represent the option that can be specified // when listing a directory. type ListDirectoryOpts struct { Limit int `json:"limit"` // Limit to the number of records returned (default and max is 1000) Marker string `json:"marker"` // Key name at which to start the next listing } // Entry represents an object stored in Manta, either a file or a directory type Entry struct { Name string `json:"name"` // Entry name Etag string `json:"etag,omitempty"` // If type is 'object', object UUID Size int `json:"size,omitempty"` // If type is 'object', object size (content-length) Type string `json:"type"` // Entry type, one of 'directory' or 'object' Mtime string `json:"mtime"` // ISO8601 timestamp of the last update } // Creates a directory at the specified path. Any parent directory must exist. // See API docs: http://apidocs.joyent.com/manta/api.html#PutDirectory func (c *Client) PutDirectory(path string) error { requestHeaders := make(http.Header) requestHeaders.Set("Content-Type", "application/json; type=directory") requestHeaders.Set("Accept", "*/*") req := request{ method: client.PUT, url: makeURL(apiStorage, path), reqHeader: requestHeaders, expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to create directory: %s", path) } return nil } // Returns the content of the specified directory, using the specified options. // See API docs: http://apidocs.joyent.com/manta/api.html#ListDirectory func (c *Client) ListDirectory(directory string, opts ListDirectoryOpts) ([]Entry, error) { var resp []Entry requestHeaders := make(http.Header) requestHeaders.Set("Accept", "*/*") req := request{ method: client.GET, url: makeURL(apiStorage, directory), reqHeader: requestHeaders, resp: &resp, reqValue: opts, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to list directory %s", directory) } return resp, nil } // Deletes the specified directory. Directory must be empty. // See API docs: http://apidocs.joyent.com/manta/api.html#DeleteDirectory func (c *Client) DeleteDirectory(path string) error { req := request{ method: client.DELETE, url: makeURL(apiStorage, path), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete directory %s", path) } return nil } // Creates an object at the specified path. Any parent directory must exist. // See API docs: http://apidocs.joyent.com/manta/api.html#PutObject func (c *Client) PutObject(path, objectName string, object []byte) error { r := bytes.NewReader(object) req := request{ method: client.PUT, url: makeURL(apiStorage, path, objectName), reqReader: r, reqLength: len(object), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to create object: %s/%s", path, objectName) } return nil } // Retrieves the specified object from the specified location. // See API docs: http://apidocs.joyent.com/manta/api.html#GetObject func (c *Client) GetObject(path, objectName string) ([]byte, error) { var resp []byte requestHeaders := make(http.Header) requestHeaders.Set("Accept", "*/*") req := request{ method: client.GET, url: makeURL(apiStorage, path, objectName), reqHeader: requestHeaders, resp: &resp, } respData, err := c.sendRequest(req) if err != nil { return nil, errors.Newf(err, "failed to get object %s/%s", path, objectName) } res, _ := respData.RespValue.([]byte) return res, nil } // Deletes the specified object from the specified location. // See API docs: http://apidocs.joyent.com/manta/api.html#DeleteObject func (c *Client) DeleteObject(path, objectName string) error { req := request{ method: client.DELETE, url: makeURL(apiStorage, path, objectName), expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to delete object %s/%s", path, objectName) } return nil } // Creates a link (similar to a Unix hard link) from location to path/linkName. // See API docs: http://apidocs.joyent.com/manta/api.html#PutSnapLink func (c *Client) PutSnapLink(path, linkName, location string) error { requestHeaders := make(http.Header) requestHeaders.Set("Accept", "application/json; type=link") requestHeaders.Set("Location", location) req := request{ method: client.PUT, url: makeURL(apiStorage, path, linkName), reqHeader: requestHeaders, expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to create snap link: %s/%s", path, linkName) } return nil } // CreateJobOpts represent the option that can be specified // when creating a job. type CreateJobOpts struct { Name string `json:"name,omitempty"` // Job Name (optional) Phases []Phase `json:"phases"` // Tasks to execute as part of this job } // Job represents the status of a job. type Job struct { Id string // Job unique identifier Name string `json:"name,omitempty"` // Job Name State string // Job state Cancelled bool // Whether the job has been cancelled or not InputDone bool // Whether the inputs for the job is still open or not Stats JobStats `json:"stats,omitempty"` // Job statistics TimeCreated string // Time the job was created at TimeDone string `json:"timeDone,omitempty"` // Time the job was completed TimeArchiveStarted string `json:"timeArchiveStarted,omitempty"` // Time the job archiving started TimeArchiveDone string `json:"timeArchiveDone,omitempty"` // Time the job archiving completed Phases []Phase `json:"phases"` // Job tasks Options interface{} // Job options } // JobStats represents statistics about a job type JobStats struct { Errors int // Number or errors Outputs int // Number of output produced Retries int // Number of retries Tasks int // Total number of task in the job TasksDone int // number of tasks done } // Phase represents a task to be executed as part of a Job type Phase struct { Type string `json:"type,omitempty"` // Task type, one of 'map' or 'reduce' (optional) Assets []string `json:"assets,omitempty"` // An array of objects to be placed in the compute zones (optional) Exec string `json:"exec"` // The actual shell statement to execute Init string `json:"init"` // Shell statement to execute in each compute zone before any tasks are executed Count int `json:"count,omitempty"` // If type is 'reduce', an optional number of reducers for this phase (default is 1) Memory int `json:"memory,omitempty"` // Amount of DRAM to give to your compute zone (in Mb, optional) Disk int `json:"disk,omitempty"` // Amount of disk space to give to your compute zone (in Gb, optional) } // JobError represents an error occurred during a job execution type JobError struct { Id string // Job Id Phase string // Phase number of the failure What string // A human readable summary of what failed Code string // Error code Message string // Human readable error message Stderr string // A key that saved the stderr for the given command (optional) Key string // The input key being processed when the task failed (optional) } // Creates a job with the given options. // See API docs: http://apidocs.joyent.com/manta/api.html#CreateJob func (c *Client) CreateJob(opts CreateJobOpts) (string, error) { var resp string var respHeader http.Header req := request{ method: client.POST, url: apiJobs, reqValue: opts, respHeader: &respHeader, resp: &resp, expectedStatus: http.StatusCreated, } respData, err := c.sendRequest(req) if err != nil { return "", errors.Newf(err, "failed to create job with name: %s", opts.Name) } return respData.RespHeaders.Get("Location"), nil } // Submits inputs to an already created job. // See API docs: http://apidocs.joyent.com/manta/api.html#AddJobInputs func (c *Client) AddJobInputs(jobId string, jobInputs io.Reader) error { inputData, errI := ioutil.ReadAll(jobInputs) if errI != nil { return errors.Newf(errI, "failed to read inputs for job %s", jobId) } requestHeaders := make(http.Header) requestHeaders.Set("Accept", "*/*") requestHeaders.Set("Content-Type", "text/plain") req := request{ method: client.POST, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsIn), reqValue: string(inputData), reqHeader: requestHeaders, expectedStatus: http.StatusNoContent, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to add inputs to job %s", jobId) } return nil } // This closes input for a job, and finalize the job. // See API docs: http://apidocs.joyent.com/manta/api.html#EndJobInput func (c *Client) EndJobInputs(jobId string) error { req := request{ method: client.POST, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsIn, apiJobsEnd), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to end inputs for job %s", jobId) } return nil } // This cancels a job from doing any further work. // Cancellation is asynchronous and "best effort"; there is no guarantee the job will actually stop // See API docs: http://apidocs.joyent.com/manta/api.html#CancelJob func (c *Client) CancelJob(jobId string) error { req := request{ method: client.POST, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsCancel), expectedStatus: http.StatusAccepted, } if _, err := c.sendRequest(req); err != nil { return errors.Newf(err, "failed to cancel job %s", jobId) } return nil } // Returns the list of jobs. // Note you can filter the set of jobs down to only live jobs by setting the liveOnly flag. // See API docs: http://apidocs.joyent.com/manta/api.html#ListJobs func (c *Client) ListJobs(liveOnly bool) ([]Entry, error) { var resp []Entry var url string if liveOnly { url = fmt.Sprintf("%s?state=running", apiJobs) } else { url = apiJobs } req := request{ method: client.GET, url: url, resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to list jobs") } return resp, nil } // Gets the high-level job container object for a given job. // See API docs: http://apidocs.joyent.com/manta/api.html#GetJob func (c *Client) GetJob(jobId string) (Job, error) { var resp Job req := request{ method: client.GET, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsStatus), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return Job{}, errors.Newf(err, "failed to get job with id: %s", jobId) } return resp, nil } // Returns the current "live" set of outputs from a given job. // See API docs: http://apidocs.joyent.com/manta/api.html#GetJobOutput func (c *Client) GetJobOutput(jobId string) (string, error) { var resp string req := request{ method: client.GET, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsOut), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return "", errors.Newf(err, "failed to get output for job with id: %s", jobId) } return resp, nil } // Returns the submitted input objects for a given job, available while the job is running. // See API docs: http://apidocs.joyent.com/manta/api.html#GetJobInput func (c *Client) GetJobInput(jobId string) (string, error) { var resp string req := request{ method: client.GET, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsIn), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return "", errors.Newf(err, "failed to get input for job with id: %s", jobId) } return resp, nil } // Returns the current "live" set of failures from a given job. // See API docs: http://apidocs.joyent.com/manta/api.html#GetJobFailures func (c *Client) GetJobFailures(jobId string) (interface{}, error) { var resp interface{} req := request{ method: client.GET, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsFail), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get failures for job with id: %s", jobId) } return resp, nil } // Returns the current "live" set of errors from a given job. // See API docs: http://apidocs.joyent.com/manta/api.html#GetJobErrors func (c *Client) GetJobErrors(jobId string) ([]JobError, error) { var resp []JobError req := request{ method: client.GET, url: makeURL(apiJobs, jobId, apiJobsLive, apiJobsErr), resp: &resp, } if _, err := c.sendRequest(req); err != nil { return nil, errors.Newf(err, "failed to get errors for job with id: %s", jobId) } return resp, nil } // Returns a signed URL to retrieve the object at path. func (c *Client) SignURL(path string, expires time.Time) (string, error) { return c.client.SignURL(path, expires) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/manta/live_test.go�����������������������������������0000644�0000153�0000161�00000014517�12321735717�024673� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gomanta - Go library to interact with Joyent Manta // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package manta_test import ( "fmt" "net/http" "time" "github.com/joyent/gocommon/client" "github.com/joyent/gomanta/manta" "github.com/joyent/gosign/auth" gc "launchpad.net/gocheck" "strings" ) func registerMantaTests(creds *auth.Credentials) { gc.Suite(&LiveTests{creds: creds}) } type LiveTests struct { creds *auth.Credentials testClient *manta.Client } func (s *LiveTests) SetUpTest(c *gc.C) { client := client.NewClient(s.creds.MantaEndpoint.URL, "", s.creds, &manta.Logger) c.Assert(client, gc.NotNil) s.testClient = manta.New(client) c.Assert(s.testClient, gc.NotNil) } // Helper method to create a test directory func (s *LiveTests) createDirectory(c *gc.C, path string) { err := s.testClient.PutDirectory(path) c.Assert(err, gc.IsNil) } // Helper method to create a test object func (s *LiveTests) createObject(c *gc.C, path, objName string) { err := s.testClient.PutObject(path, objName, []byte("Test Manta API")) c.Assert(err, gc.IsNil) } // Helper method to delete a test directory func (s *LiveTests) deleteDirectory(c *gc.C, path string) { err := s.testClient.DeleteDirectory(path) c.Assert(err, gc.IsNil) } // Helper method to delete a test object func (s *LiveTests) deleteObject(c *gc.C, path, objName string) { err := s.testClient.DeleteObject(path, objName) c.Assert(err, gc.IsNil) } // Helper method to create a test job func (s *LiveTests) createJob(c *gc.C, jobName string) string { phases := []manta.Phase{ {Type: "map", Exec: "wc", Init: ""}, {Type: "reduce", Exec: "awk '{ l += $1; w += $2; c += $3 } END { print l, w, c }'", Init: ""}, } jobUri, err := s.testClient.CreateJob(manta.CreateJobOpts{Name: jobName, Phases: phases}) c.Assert(err, gc.IsNil) c.Assert(jobUri, gc.NotNil) return strings.Split(jobUri, "/")[3] } // Storage API func (s *LiveTests) TestPutDirectory(c *gc.C) { s.createDirectory(c, "test") // cleanup s.deleteDirectory(c, "test") } func (s *LiveTests) TestListDirectory(c *gc.C) { s.createDirectory(c, "test") defer s.deleteDirectory(c, "test") s.createObject(c, "test", "obj") defer s.deleteObject(c, "test", "obj") opts := manta.ListDirectoryOpts{} dirs, err := s.testClient.ListDirectory("test", opts) c.Assert(err, gc.IsNil) c.Assert(dirs, gc.NotNil) } func (s *LiveTests) TestDeleteDirectory(c *gc.C) { s.createDirectory(c, "test") s.deleteDirectory(c, "test") } func (s *LiveTests) TestPutObject(c *gc.C) { s.createDirectory(c, "dir") defer s.deleteDirectory(c, "dir") s.createObject(c, "dir", "obj") defer s.deleteObject(c, "dir", "obj") } func (s *LiveTests) TestGetObject(c *gc.C) { s.createDirectory(c, "dir") defer s.deleteDirectory(c, "dir") s.createObject(c, "dir", "obj") defer s.deleteObject(c, "dir", "obj") obj, err := s.testClient.GetObject("dir", "obj") c.Assert(err, gc.IsNil) c.Assert(obj, gc.NotNil) c.Check(string(obj), gc.Equals, "Test Manta API") } func (s *LiveTests) TestDeleteObject(c *gc.C) { s.createDirectory(c, "dir") defer s.deleteDirectory(c, "dir") s.createObject(c, "dir", "obj") s.deleteObject(c, "dir", "obj") } func (s *LiveTests) TestPutSnapLink(c *gc.C) { s.createDirectory(c, "linkdir") defer s.deleteDirectory(c, "linkdir") s.createObject(c, "linkdir", "obj") defer s.deleteObject(c, "linkdir", "obj") location := fmt.Sprintf("%s/%s", s.creds.UserAuthentication.User, "stor/linkdir/obj") err := s.testClient.PutSnapLink("linkdir", "objlnk", location) c.Assert(err, gc.IsNil) // cleanup s.deleteObject(c, "linkdir", "objlnk") } func (s *LiveTests) TestSignURL(c *gc.C) { s.createDirectory(c, "sign") defer s.deleteDirectory(c, "sign") s.createObject(c, "sign", "object") defer s.deleteObject(c, "sign", "object") location := fmt.Sprintf("/%s/%s", s.creds.UserAuthentication.User, "stor/sign/object") url, err := s.testClient.SignURL(location, time.Now().Add(time.Minute*5)) c.Assert(err, gc.IsNil) c.Assert(url, gc.Not(gc.Equals), "") resp, err := http.Get(url) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) } // Jobs API func (s *LiveTests) TestCreateJob(c *gc.C) { s.createJob(c, "test-job") } func (s *LiveTests) TestListLiveJobs(c *gc.C) { s.createJob(c, "test-job") jobs, err := s.testClient.ListJobs(true) c.Assert(err, gc.IsNil) c.Assert(jobs, gc.NotNil) c.Assert(len(jobs) >= 1, gc.Equals, true) } func (s *LiveTests) TestListAllJobs(c *gc.C) { s.createJob(c, "test-job") jobs, err := s.testClient.ListJobs(false) c.Assert(err, gc.IsNil) c.Assert(jobs, gc.NotNil) } func (s *LiveTests) TestCancelJob(c *gc.C) { jobId := s.createJob(c, "test-job") err := s.testClient.CancelJob(jobId) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestAddJobInputs(c *gc.C) { var inputs = `/` + s.creds.UserAuthentication.User + `/stor/testjob/obj1 /` + s.creds.UserAuthentication.User + `/stor/testjob/obj2 ` s.createDirectory(c, "testjob") defer s.deleteDirectory(c, "testjob") s.createObject(c, "testjob", "obj1") defer s.deleteObject(c, "testjob", "obj1") s.createObject(c, "testjob", "obj2") defer s.deleteObject(c, "testjob", "obj2") jobId := s.createJob(c, "test-job") err := s.testClient.AddJobInputs(jobId, strings.NewReader(inputs)) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestEndJobInputs(c *gc.C) { jobId := s.createJob(c, "test-job") err := s.testClient.EndJobInputs(jobId) c.Assert(err, gc.IsNil) } func (s *LiveTests) TestGetJob(c *gc.C) { jobId := s.createJob(c, "test-job") job, err := s.testClient.GetJob(jobId) c.Assert(err, gc.IsNil) c.Assert(job, gc.NotNil) } func (s *LiveTests) TestGetJobInput(c *gc.C) { jobId := s.createJob(c, "test-job") input, err := s.testClient.GetJobInput(jobId) c.Assert(err, gc.IsNil) c.Assert(input, gc.NotNil) } func (s *LiveTests) TestGetJobOutput(c *gc.C) { jobId := s.createJob(c, "test-job") output, err := s.testClient.GetJobOutput(jobId) c.Assert(err, gc.IsNil) c.Assert(output, gc.NotNil) } func (s *LiveTests) TestGetJobFailures(c *gc.C) { jobId := s.createJob(c, "test-job") fail, err := s.testClient.GetJobFailures(jobId) c.Assert(err, gc.IsNil) c.Assert(fail, gc.Equals, nil) } func (s *LiveTests) TestGetJobErrors(c *gc.C) { jobId := s.createJob(c, "test-job") errs, err := s.testClient.GetJobErrors(jobId) c.Assert(err, gc.IsNil) c.Assert(errs, gc.IsNil) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/LICENSE����������������������������������������������0000644�0000153�0000161�00000001222�12321735717�022240� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������GoManta - Go Library for Joyent Manta Copyright (c) 2013, Joyent Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. See both COPYING and COPYING.LESSER for the full terms of the GNU Lesser General Public License. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/gomanta.go�������������������������������������������0000644�0000153�0000161�00000000701�12321735717�023211� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* The gomanta package enables Go programs to interact with the Joyent Manta service. The gomanta package is structured as follow: - gomanta/localservices. This package provides local services to be used for testing. - gomanta/manta. This package interacts with the Manta API (http://apidocs.joyent.com/manta/). Licensed under LGPL v3. Copyright (c) 2013 Joyent Inc. Written by Daniele Stroppa <daniele.stroppa@joyent.com> */ package gomanta ���������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/localservices/���������������������������������������0000755�0000153�0000161�00000000000�12321735717�024074� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/localservices/manta/���������������������������������0000755�0000153�0000161�00000000000�12321735717�025174� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/localservices/manta/service_test.go������������������0000644�0000153�0000161�00000023340�12321735717�030224� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gomanta - Go library to interact with Joyent Manta // // Manta double testing service - internal direct API test // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package manta_test import ( "fmt" gc "launchpad.net/gocheck" "strings" "testing" "encoding/json" lm "github.com/joyent/gomanta/localservices/manta" "github.com/joyent/gomanta/manta" ) type MantaSuite struct { service *lm.Manta } const ( testServiceURL = "https://go-test.manta.joyent.com" testUserAccount = "gouser" object = "1. Go Test -- Go Test -- GoTest\n2. Go Test -- Go Test -- GoTest" ) var _ = gc.Suite(&MantaSuite{}) func Test(t *testing.T) { gc.TestingT(t) } func (s *MantaSuite) SetUpSuite(c *gc.C) { s.service = lm.New(testServiceURL, testUserAccount) } // Helpers func getObject() ([]byte, error) { var bytes []byte r := strings.NewReader(object) if _, err := r.Read(bytes); err != nil { return nil, err } return bytes, nil } func (s *MantaSuite) createDirectory(c *gc.C, path string) { err := s.service.PutDirectory(path) c.Assert(err, gc.IsNil) } func (s *MantaSuite) deleteDirectory(c *gc.C, path string) { err := s.service.DeleteDirectory(path) c.Assert(err, gc.IsNil) } func (s *MantaSuite) createObject(c *gc.C, path, objName string) { obj, err := getObject() c.Assert(err, gc.IsNil) err = s.service.PutObject(path, objName, obj) c.Assert(err, gc.IsNil) } func (s *MantaSuite) deleteObject(c *gc.C, path string) { err := s.service.DeleteObject(path) c.Assert(err, gc.IsNil) } func createTestJob(c *gc.C, jobName string) []byte { phases := []manta.Phase{{Type: "map", Exec: "wc", Init: ""}, {Type: "reduce", Exec: "awk '{ l += $1; w += $2; c += $3 } END { print l, w, c }'", Init: ""}} job, err := json.Marshal(manta.CreateJobOpts{Name: jobName, Phases: phases}) c.Assert(err, gc.IsNil) return job } func (s *MantaSuite) createJob(c *gc.C, jobName string) string { jobUri, err := s.service.CreateJob(createTestJob(c, jobName)) c.Assert(err, gc.IsNil) c.Assert(jobUri, gc.NotNil) return strings.Split(jobUri, "/")[3] } // Storage APIs func (s *MantaSuite) TestPutDirectory(c *gc.C) { s.createDirectory(c, "test") } func (s *MantaSuite) TestPutDirectoryWithParent(c *gc.C) { s.createDirectory(c, "test") s.createDirectory(c, "test/innerdir") } func (s *MantaSuite) TestPutDirectoryNoParent(c *gc.C) { err := s.service.PutDirectory("nodir/test") c.Assert(err, gc.ErrorMatches, "/gouser/stor/nodir was not found") } func (s *MantaSuite) TestListDirectoryEmpty(c *gc.C) { s.createDirectory(c, "empty") dirs, err := s.service.ListDirectory("empty", "", 0) c.Assert(err, gc.IsNil) c.Assert(dirs, gc.HasLen, 0) } func (s *MantaSuite) TestListDirectoryNoExists(c *gc.C) { _, err := s.service.ListDirectory("nodir", "", 0) c.Assert(err, gc.ErrorMatches, "/gouser/stor/nodir was not found") } func (s *MantaSuite) TestListDirectory(c *gc.C) { s.createDirectory(c, "dir") for i := 0; i < 5; i++ { s.createObject(c, "dir", fmt.Sprintf("obj%d", i)) } dirs, err := s.service.ListDirectory("dir", "", 0) c.Assert(err, gc.IsNil) c.Assert(dirs, gc.HasLen, 5) } func (s *MantaSuite) TestListDirectoryWithLimit(c *gc.C) { s.createDirectory(c, "limitdir") for i := 0; i < 500; i++ { s.createObject(c, "limitdir", fmt.Sprintf("obj%03d", i)) } dirs, err := s.service.ListDirectory("limitdir", "", 0) c.Assert(err, gc.IsNil) c.Assert(dirs, gc.HasLen, 256) dirs, err = s.service.ListDirectory("limitdir", "", 10) c.Assert(err, gc.IsNil) c.Assert(dirs, gc.HasLen, 10) dirs, err = s.service.ListDirectory("limitdir", "", 300) c.Assert(err, gc.IsNil) c.Assert(dirs, gc.HasLen, 300) } func (s *MantaSuite) TestListDirectoryWithMarker(c *gc.C) { s.createDirectory(c, "markerdir") for i := 0; i < 500; i++ { s.createObject(c, "markerdir", fmt.Sprintf("obj%03d", i)) } dirs, err := s.service.ListDirectory("markerdir", "obj400", 0) c.Assert(err, gc.IsNil) c.Assert(dirs, gc.HasLen, 100) c.Assert(dirs[0].Name, gc.Equals, "obj400") } func (s *MantaSuite) TestDeleteDirectoryNotEmpty(c *gc.C) { s.createDirectory(c, "notempty") s.createObject(c, "notempty", "obj") err := s.service.DeleteDirectory("notempty") c.Assert(err, gc.ErrorMatches, "BadRequestError") } func (s *MantaSuite) TestDeleteDirectory(c *gc.C) { s.createDirectory(c, "deletedir") s.deleteDirectory(c, "deletedir") } func (s *MantaSuite) TestDeleteDirectoryNoExists(c *gc.C) { s.deleteDirectory(c, "nodir") } func (s *MantaSuite) TestPutObject(c *gc.C) { s.createDirectory(c, "objdir") s.createObject(c, "objdir", "object") } func (s *MantaSuite) TestPutObjectDirectoryWithParent(c *gc.C) { s.createDirectory(c, "parent") s.createDirectory(c, "parent/objdir") s.createObject(c, "parent/objdir", "object") } func (s *MantaSuite) TestPutObjectDirectoryNoParent(c *gc.C) { obj, err := getObject() c.Assert(err, gc.IsNil) err = s.service.PutObject("nodir", "obj", obj) c.Assert(err, gc.ErrorMatches, "/gouser/stor/nodir was not found") } func (s *MantaSuite) TestIsObjectTrue(c *gc.C) { s.createDirectory(c, "objdir") s.createObject(c, "objdir", "obj") isObj := s.service.IsObject("/gouser/stor/objdir/obj") c.Assert(isObj, gc.Equals, true) } func (s *MantaSuite) TestIsObjectFalse(c *gc.C) { isObj := s.service.IsObject("/gouser/stor/nodir/obj") c.Assert(isObj, gc.Equals, false) } func (s *MantaSuite) TestGetObject(c *gc.C) { s.createDirectory(c, "dir1") s.createObject(c, "dir1", "obj") expected, _ := getObject() obj, err := s.service.GetObject("dir1/obj") c.Assert(err, gc.IsNil) c.Assert(obj, gc.DeepEquals, expected) } func (s *MantaSuite) TestGetObjectWrongPath(c *gc.C) { obj, err := s.service.GetObject("nodir/obj") c.Assert(err, gc.ErrorMatches, "/gouser/stor/nodir/obj was not found") c.Assert(obj, gc.IsNil) } func (s *MantaSuite) TestGetObjectWrongName(c *gc.C) { obj, err := s.service.GetObject("noobject") c.Assert(err, gc.ErrorMatches, "/gouser/stor/noobject was not found") c.Assert(obj, gc.IsNil) } func (s *MantaSuite) TestDeleteObject(c *gc.C) { s.createDirectory(c, "delete") s.createObject(c, "delete", "obj") s.deleteObject(c, "delete/obj") } func (s *MantaSuite) TestDeleteObjectWrongPath(c *gc.C) { err := s.service.DeleteObject("nodir/obj") c.Assert(err, gc.ErrorMatches, "/gouser/stor/nodir/obj was not found") } func (s *MantaSuite) TestDeleteObjectWrongName(c *gc.C) { err := s.service.DeleteObject("noobj") c.Assert(err, gc.ErrorMatches, "/gouser/stor/noobj was not found") } func (s *MantaSuite) TestPutSnapLink(c *gc.C) { s.createDirectory(c, "linkdir") s.createObject(c, "linkdir", "obj") err := s.service.PutSnapLink("linkdir", "link", "/gouser/stor/linkdir/obj") c.Assert(err, gc.IsNil) } func (s *MantaSuite) TestPutSnapLinkNoLocation(c *gc.C) { s.createDirectory(c, "linkdir") s.createObject(c, "linkdir", "obj") err := s.service.PutSnapLink("linkdir", "link", "/gouser/stor/linkdir/noobj") c.Assert(err, gc.ErrorMatches, "/gouser/stor/linkdir/noobj was not found") } func (s *MantaSuite) TestPutSnapLinkDirectoryWithParent(c *gc.C) { s.createDirectory(c, "link1") s.createDirectory(c, "link1/linkdir") s.createObject(c, "link1/linkdir", "obj") err := s.service.PutSnapLink("link1/linkdir", "link", "/gouser/stor/link1/linkdir/obj") c.Assert(err, gc.IsNil) } func (s *MantaSuite) TestPutSnapLinkDirectoryNoParent(c *gc.C) { s.createDirectory(c, "linkdir") s.createObject(c, "linkdir", "obj") err := s.service.PutSnapLink("nodir", "link", "/gouser/stor/linkdir/obj") c.Assert(err, gc.ErrorMatches, "/gouser/stor/nodir was not found") } // Jobs API func (s *MantaSuite) TestCreateJob(c *gc.C) { s.createJob(c, "test-job") } func (s *MantaSuite) TestListAllJobs(c *gc.C) { s.createJob(c, "test-job") jobs, err := s.service.ListJobs(false) c.Assert(err, gc.IsNil) c.Assert(jobs, gc.NotNil) } func (s *MantaSuite) TestListLiveJobs(c *gc.C) { s.createJob(c, "test-job") jobs, err := s.service.ListJobs(true) c.Assert(err, gc.IsNil) c.Assert(jobs, gc.NotNil) //c.Assert(jobs, gc.Equals, 1) } func (s *MantaSuite) TestCancelJob(c *gc.C) { jobId := s.createJob(c, "test-job") err := s.service.CancelJob(jobId) c.Assert(err, gc.IsNil) } func (s *MantaSuite) TestJ05_AddJobInputs(c *gc.C) { var inBytes []byte var inputs = `/` + testUserAccount + `/stor/testjob/obj1 /` + testUserAccount + `/stor/testjob/obj2 ` r := strings.NewReader(inputs) if _, err := r.Read(inBytes); err != nil { c.Skip("Could not read input, skipping...") } s.createDirectory(c, "testjob") s.createObject(c, "testjob", "obj1") s.createObject(c, "testjob", "obj2") jobId := s.createJob(c, "test-job") err := s.service.AddJobInputs(jobId, inBytes) c.Assert(err, gc.IsNil) } func (s *MantaSuite) TestJ06_EndJobInputs(c *gc.C) { jobId := s.createJob(c, "test-job") err := s.service.EndJobInput(jobId) c.Assert(err, gc.IsNil) } func (s *MantaSuite) TestJ07_GetJob(c *gc.C) { jobId := s.createJob(c, "test-job") job, err := s.service.GetJob(jobId) c.Assert(err, gc.IsNil) c.Assert(job, gc.NotNil) } func (s *MantaSuite) TestJ08_GetJobInput(c *gc.C) { jobId := s.createJob(c, "test-job") input, err := s.service.GetJobInput(jobId) c.Assert(err, gc.IsNil) c.Assert(input, gc.NotNil) //fmt.Println(input) } func (s *MantaSuite) TestJ09_GetJobOutput(c *gc.C) { jobId := s.createJob(c, "test-job") output, err := s.service.GetJobOutput(jobId) c.Assert(err, gc.IsNil) c.Assert(output, gc.NotNil) //fmt.Println(output) } func (s *MantaSuite) TestJ10_GetJobFailures(c *gc.C) { jobId := s.createJob(c, "test-job") fail, err := s.service.GetJobFailures(jobId) c.Assert(err, gc.IsNil) c.Assert(fail, gc.Equals, "") //fmt.Println(fail) } func (s *MantaSuite) TestJ11_GetJobErrors(c *gc.C) { jobId := s.createJob(c, "test-job") errs, err := s.service.GetJobErrors(jobId) c.Assert(err, gc.IsNil) c.Assert(errs, gc.IsNil) //fmt.Println(errs) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/localservices/manta/service_http.go������������������0000644�0000153�0000161�00000022401�12321735717�030221� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gomanta - Go library to interact with Joyent Manta // // Manta double testing service - HTTP API implementation // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package manta import ( //"bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "strconv" "strings" "github.com/joyent/gomanta/manta" ) // ErrorResponse defines a single HTTP error response. type ErrorResponse struct { Code int Body string contentType string errorText string headers map[string]string manta *Manta } var ( ErrNotAllowed = &ErrorResponse{ http.StatusMethodNotAllowed, "Method is not allowed", "text/plain; charset=UTF-8", "MethodNotAllowedError", nil, nil, } ErrNotFound = &ErrorResponse{ http.StatusNotFound, "Resource Not Found", "text/plain; charset=UTF-8", "NotFoundError", nil, nil, } ErrBadRequest = &ErrorResponse{ http.StatusBadRequest, "Malformed request url", "text/plain; charset=UTF-8", "BadRequestError", nil, nil, } ) func (e *ErrorResponse) Error() string { return e.errorText } func (e *ErrorResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) { if e.contentType != "" { w.Header().Set("Content-Type", e.contentType) } body := e.Body if e.headers != nil { for h, v := range e.headers { w.Header().Set(h, v) } } // workaround for https://code.google.com/p/go/issues/detail?id=4454 w.Header().Set("Content-Length", strconv.Itoa(len(body))) if e.Code != 0 { w.WriteHeader(e.Code) } if len(body) > 0 { w.Write([]byte(body)) } } type mantaHandler struct { manta *Manta method func(m *Manta, w http.ResponseWriter, r *http.Request) error } func (h *mantaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path // handle trailing slash in the path if strings.HasSuffix(path, "/") && path != "/" { ErrNotFound.ServeHTTP(w, r) return } err := h.method(h.manta, w, r) if err == nil { return } var resp http.Handler resp, _ = err.(http.Handler) if resp == nil { resp = &ErrorResponse{ http.StatusInternalServerError, `{"internalServerError":{"message":"Unkown Error",code:500}}`, "application/json", err.Error(), nil, h.manta, } } resp.ServeHTTP(w, r) } func writeResponse(w http.ResponseWriter, code int, body []byte) { // workaround for https://code.google.com/p/go/issues/detail?id=4454 w.Header().Set("Content-Length", strconv.Itoa(len(body))) w.WriteHeader(code) w.Write(body) } // sendJSON sends the specified response serialized as JSON. func sendJSON(code int, resp interface{}, w http.ResponseWriter, r *http.Request) error { data, err := json.Marshal(resp) if err != nil { return err } writeResponse(w, code, data) return nil } func getJobId(url string) string { tokens := strings.Split(url, "/") return tokens[3] } func (manta *Manta) handler(method func(m *Manta, w http.ResponseWriter, r *http.Request) error) http.Handler { return &mantaHandler{manta, method} } // handleStorage handles the storage HTTP API. func (m *Manta) handleStorage(w http.ResponseWriter, r *http.Request) error { prefix := fmt.Sprintf("/%s/stor/", m.ServiceInstance.UserAccount) object := strings.TrimPrefix(r.URL.Path, prefix) switch r.Method { case "GET": if m.IsObject(r.URL.Path) { var resp []byte //GetObject obj, err := m.GetObject(object) if err != nil { return err } if obj == nil { obj = []byte{} } // Check if request came from client or signed URL //if r.URL.RawQuery != "" { // d := json.NewDecoder(bytes.NewReader(obj)) // d.Decode(&resp) //} else { resp = obj //} // not using sendJson to avoid double json encoding writeResponse(w, http.StatusOK, resp) return nil } else if m.IsDirectory(r.URL.Path) { //ListDirectory var ( marker string limit int ) opts := &manta.ListDirectoryOpts{} body, errB := ioutil.ReadAll(r.Body) if errB != nil { return errB } if len(body) > 0 { if errJ := json.Unmarshal(body, opts); errJ != nil { return errJ } marker = opts.Marker limit = opts.Limit } entries, err := m.ListDirectory(object, marker, limit) if err != nil { return err } if entries == nil { entries = []manta.Entry{} } resp := entries return sendJSON(http.StatusOK, resp, w, r) } else { return ErrNotFound } case "POST": return ErrNotAllowed case "PUT": if r.Header.Get("Content-Type") == "application/json; type=directory" { // PutDirectory err := m.PutDirectory(object) if err != nil { return err } return sendJSON(http.StatusNoContent, nil, w, r) } else if r.Header.Get("Location") != "" { // PutSnaplink path := object[:strings.LastIndex(object, "/")] objName := object[strings.LastIndex(object, "/")+1:] err := m.PutSnapLink(path, objName, r.Header.Get("Location")) if err != nil { return err } return sendJSON(http.StatusNoContent, nil, w, r) } else { // PutObject path := object[:strings.LastIndex(object, "/")] objName := object[strings.LastIndex(object, "/")+1:] defer r.Body.Close() objectData, err := ioutil.ReadAll(r.Body) if err != nil { return err } err = m.PutObject(path, objName, objectData) if err != nil { return err } return sendJSON(http.StatusNoContent, nil, w, r) } case "DELETE": if m.IsObject(r.URL.Path) { //DeleteObject err := m.DeleteObject(object) if err != nil { return err } return sendJSON(http.StatusNoContent, nil, w, r) } else if m.IsDirectory(r.URL.Path) { //DeleteDirectory err := m.DeleteDirectory(object) if err != nil { return err } return sendJSON(http.StatusNoContent, nil, w, r) } else { return ErrNotFound } } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleJob handles the Job HTTP API. func (m *Manta) handleJobs(w http.ResponseWriter, r *http.Request) error { var live = false switch r.Method { case "GET": if strings.HasSuffix(r.URL.Path, "jobs") { // ListJobs if state := r.FormValue("state"); state == "running" { live = true } jobs, err := m.ListJobs(live) if err != nil { return err } if len(jobs) == 0 { jobs = []manta.Entry{} } resp := jobs return sendJSON(http.StatusOK, resp, w, r) } else if strings.HasSuffix(r.URL.Path, "status") { // GetJob job, err := m.GetJob(getJobId(r.URL.Path)) if err != nil { return err } if job == nil { job = &manta.Job{} } resp := job return sendJSON(http.StatusOK, resp, w, r) } else if strings.HasSuffix(r.URL.Path, "out") { // GetJobOutput out, err := m.GetJobOutput(getJobId(r.URL.Path)) if err != nil { return err } resp := out return sendJSON(http.StatusOK, resp, w, r) } else if strings.HasSuffix(r.URL.Path, "in") { // GetJobInput in, err := m.GetJobInput(getJobId(r.URL.Path)) if err != nil { return err } resp := in return sendJSON(http.StatusOK, resp, w, r) } else if strings.HasSuffix(r.URL.Path, "fail") { // GetJobFailures fail, err := m.GetJobFailures(getJobId(r.URL.Path)) if err != nil { return err } resp := fail return sendJSON(http.StatusOK, resp, w, r) } else if strings.HasSuffix(r.URL.Path, "err") { // GetJobErrors jobErr, err := m.GetJobErrors(getJobId(r.URL.Path)) if err != nil { return err } //if jobErr == nil { // jobErr = []manta.JobError{} //} resp := jobErr return sendJSON(http.StatusOK, resp, w, r) } else { return ErrNotAllowed } case "POST": if strings.HasSuffix(r.URL.Path, "jobs") { body, errb := ioutil.ReadAll(r.Body) if errb != nil { return errb } if len(body) == 0 { return ErrBadRequest } jobId, err := m.CreateJob(body) if err != nil { return err } w.Header().Add("Location", jobId) return sendJSON(http.StatusCreated, nil, w, r) } else if strings.HasSuffix(r.URL.Path, "cancel") { // CancelJob err := m.CancelJob(getJobId(r.URL.Path)) if err != nil { return err } return sendJSON(http.StatusAccepted, nil, w, r) } else if strings.HasSuffix(r.URL.Path, "end") { // EndJobInputs err := m.EndJobInput(getJobId(r.URL.Path)) if err != nil { return err } return sendJSON(http.StatusAccepted, nil, w, r) } else if strings.HasSuffix(r.URL.Path, "in") { // AddJobInputs body, err := ioutil.ReadAll(r.Body) if err != nil { return err } if len(body) == 0 { return ErrBadRequest } err = m.AddJobInputs(getJobId(r.URL.Path), body) if err != nil { return err } return sendJSON(http.StatusNoContent, nil, w, r) } else { return ErrNotAllowed } case "PUT": return ErrNotAllowed case "DELETE": return ErrNotAllowed } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // setupHTTP attaches all the needed handlers to provide the HTTP API. func (m *Manta) SetupHTTP(mux *http.ServeMux) { handlers := map[string]http.Handler{ "/": ErrNotFound, "/$user/": ErrBadRequest, "/$user/stor": m.handler((*Manta).handleStorage), "/$user/jobs": m.handler((*Manta).handleJobs), } for path, h := range handlers { path = strings.Replace(path, "$user", m.ServiceInstance.UserAccount, 1) if !strings.HasSuffix(path, "/") { mux.Handle(path+"/", h) } mux.Handle(path, h) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/localservices/manta/service.go�����������������������0000644�0000153�0000161�00000027411�12321735717�027170� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gomanta - Go library to interact with Joyent Manta // // Manta double testing service - internal direct API implementation // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package manta import ( "encoding/json" "fmt" "net/url" "sort" "strings" "time" "github.com/joyent/gomanta/localservices" "github.com/joyent/gomanta/manta" ) const ( storagePrefix = "/%s/stor/%s" jobsPrefix = "/%s/jobs/%s" separator = "/" typeDirectory = "directory" typeObject = "object" ) type Manta struct { localservices.ServiceInstance objects map[string]manta.Entry objectsData map[string][]byte jobs map[string]*manta.Job } func New(serviceURL, userAccount string) *Manta { URL, err := url.Parse(serviceURL) if err != nil { panic(err) } hostname := URL.Host if !strings.HasSuffix(hostname, separator) { hostname += separator } mantaDirectories := make(map[string]manta.Entry) path := fmt.Sprintf("/%s", userAccount) mantaDirectories[path] = createDirectory(userAccount) path = fmt.Sprintf("/%s/stor", userAccount) mantaDirectories[path] = createDirectory("stor") path = fmt.Sprintf("/%s/jobs", userAccount) mantaDirectories[path] = createDirectory("jobs") mantaService := &Manta{ objects: mantaDirectories, objectsData: make(map[string][]byte), jobs: make(map[string]*manta.Job), ServiceInstance: localservices.ServiceInstance{ Scheme: URL.Scheme, Hostname: hostname, UserAccount: userAccount, }, } return mantaService } func createDirectory(directoryName string) manta.Entry { return manta.Entry{ Name: directoryName, Type: typeDirectory, Mtime: time.Now().Format(time.RFC3339), } } func createJobObject(objName string, objData []byte) (manta.Entry, error) { etag, err := localservices.NewUUID() if err == nil { return manta.Entry{ Name: objName, Type: typeObject, Mtime: time.Now().Format(time.RFC3339), Etag: etag, Size: len(objData), }, nil } return manta.Entry{}, err } func (m *Manta) IsObject(name string) bool { _, exist := m.objectsData[name] return exist } func (m *Manta) IsDirectory(name string) bool { _, exist := m.objects[name] return !m.IsObject(name) && exist } // Directories APIs func (m *Manta) ListDirectory(path, marker string, limit int) ([]manta.Entry, error) { if err := m.ProcessFunctionHook(m, path, marker, limit); err != nil { return nil, err } realPath := fmt.Sprintf(storagePrefix, m.ServiceInstance.UserAccount, path) if limit == 0 { limit = 256 } if _, ok := m.objects[realPath]; !ok { return nil, fmt.Errorf("%s was not found", realPath) } var sortedKeys []string for k := range m.objects { sortedKeys = append(sortedKeys, k) } sort.Strings(sortedKeys) if !strings.HasSuffix(realPath, separator) { realPath = realPath + separator } var entries []manta.Entry var entriesKeys []string sortedLoop: for _, key := range sortedKeys { if strings.Contains(key, realPath) { for _, k := range entriesKeys { if strings.Contains(key, k) { continue sortedLoop } } entriesKeys = append(entriesKeys, key) } } for _, k := range entriesKeys { if marker != "" && marker > k[strings.LastIndex(k, "/")+1:] { continue } entries = append(entries, m.objects[k]) if len(entries) >= limit { break } } return entries, nil } func getParentDirs(userAccount, path string) []string { var parents []string tokens := strings.Split(path, separator) for index, _ := range tokens { parents = append(parents, fmt.Sprintf(storagePrefix, userAccount, strings.Join(tokens[:(index+1)], separator))) } return parents } func (m *Manta) PutDirectory(path string) error { if err := m.ProcessFunctionHook(m, path); err != nil { return err } realPath := fmt.Sprintf(storagePrefix, m.ServiceInstance.UserAccount, path) // Check if parent dirs exist if strings.Contains(path, separator) { ppath := path[:strings.LastIndex(path, separator)] parents := getParentDirs(m.ServiceInstance.UserAccount, ppath) for _, p := range parents { if _, ok := m.objects[p]; !ok { return fmt.Errorf("%s was not found", p) } } } dir := manta.Entry{ Name: path[(strings.LastIndex(path, separator) + 1):], Type: typeDirectory, Mtime: time.Now().Format(time.RFC3339), } m.objects[realPath] = dir return nil } func (m *Manta) DeleteDirectory(path string) error { if err := m.ProcessFunctionHook(m, path); err != nil { return err } realPath := fmt.Sprintf(storagePrefix, m.ServiceInstance.UserAccount, path) // Check if empty ppath := realPath + separator for k, _ := range m.objects { if strings.Contains(k, ppath) { return ErrBadRequest } } delete(m.objects, realPath) return nil } // Objects APIs func (m *Manta) PutObject(path, objName string, objData []byte) error { if err := m.ProcessFunctionHook(m, path, objName, objData); err != nil { return err } realPath := fmt.Sprintf(storagePrefix, m.ServiceInstance.UserAccount, path) // Check if parent dirs exist parents := getParentDirs(m.ServiceInstance.UserAccount, path) for _, p := range parents { if _, ok := m.objects[p]; !ok { return fmt.Errorf("%s was not found", realPath) } } etag, err := localservices.NewUUID() if err != nil { return err } obj := manta.Entry{ Name: objName, Type: typeObject, Mtime: time.Now().Format(time.RFC3339), Etag: etag, Size: len(objData), } objId := fmt.Sprintf("%s/%s", realPath, objName) m.objects[objId] = obj m.objectsData[objId] = objData return nil } func (m *Manta) GetObject(objPath string) ([]byte, error) { if err := m.ProcessFunctionHook(m, objPath); err != nil { return nil, err } objId := fmt.Sprintf(storagePrefix, m.ServiceInstance.UserAccount, objPath) if _, ok := m.objects[objId]; ok { // TODO: Headers! return m.objectsData[objId], nil } return nil, fmt.Errorf("%s was not found", objId) } func (m *Manta) DeleteObject(objPath string) error { if err := m.ProcessFunctionHook(m, objPath); err != nil { return err } objId := fmt.Sprintf(storagePrefix, m.ServiceInstance.UserAccount, objPath) if _, ok := m.objects[objId]; ok { delete(m.objects, objId) delete(m.objectsData, objId) return nil } return fmt.Errorf("%s was not found", objId) } // Link APIs func (m *Manta) PutSnapLink(path, linkName, location string) error { if err := m.ProcessFunctionHook(m, path, linkName, location); err != nil { return err } realPath := fmt.Sprintf(storagePrefix, m.ServiceInstance.UserAccount, path) // Check if parent dirs exist parents := getParentDirs(m.ServiceInstance.UserAccount, path) for _, p := range parents { if _, ok := m.objects[p]; !ok { return fmt.Errorf("%s was not found", realPath) } } // Check if location exist if _, ok := m.objects[location]; !ok { return fmt.Errorf("%s was not found", location) } etag, err := localservices.NewUUID() if err != nil { return err } obj := manta.Entry{ Name: linkName, Type: typeObject, Mtime: time.Now().Format(time.RFC3339), Etag: etag, Size: len(m.objectsData[location]), } objId := fmt.Sprintf("%s/%s", realPath, linkName) m.objects[objId] = obj m.objectsData[objId] = m.objectsData[location] return nil } // Job APIs func (m *Manta) ListJobs(live bool) ([]manta.Entry, error) { var jobs []manta.Entry if err := m.ProcessFunctionHook(m, live); err != nil { return nil, err } for _, job := range m.jobs { if live && (job.Cancelled || job.TimeDone != "") { continue } jobKey := fmt.Sprintf(jobsPrefix, m.ServiceInstance.UserAccount, job.Id) jobs = append(jobs, m.objects[jobKey]) } return jobs, nil } func (m *Manta) CreateJob(job []byte) (string, error) { if err := m.ProcessFunctionHook(m, job); err != nil { return "", err } jsonJob := new(manta.Job) err := json.Unmarshal(job, jsonJob) if err != nil { return "", err } jobId, err := localservices.NewUUID() if err != nil { return "", err } jsonJob.Id = jobId jsonJob.State = "running" jsonJob.Cancelled = false jsonJob.InputDone = false jsonJob.TimeCreated = time.Now().Format(time.RFC3339) //create directories realPath := fmt.Sprintf(jobsPrefix, m.ServiceInstance.UserAccount, jobId) m.objects[realPath] = createDirectory(jobId) realPath = fmt.Sprintf(jobsPrefix, m.ServiceInstance.UserAccount, fmt.Sprintf("%s/stor", jobId)) m.objects[realPath] = createDirectory("stor") m.jobs[jsonJob.Id] = jsonJob return fmt.Sprintf("/%s/jobs/%s", m.ServiceInstance.UserAccount, jobId), nil } func (m *Manta) GetJob(id string) (*manta.Job, error) { if err := m.ProcessFunctionHook(m, id); err != nil { return nil, err } if job, ok := m.jobs[id]; ok { return job, nil } return nil, fmt.Errorf("/%s/jobs/%s/job.json was not found", m.ServiceInstance.UserAccount, id) } func (m *Manta) CancelJob(id string) error { if err := m.ProcessFunctionHook(m, id); err != nil { return err } if job, ok := m.jobs[id]; ok { if !job.InputDone { job.Cancelled = true job.InputDone = true job.TimeDone = time.Now().Format(time.RFC3339) } else { return fmt.Errorf("/%s/jobs/%s/live/cancel does not exist", m.ServiceInstance.UserAccount, id) } return nil } return fmt.Errorf("/%s/jobs/%s/job.json was not found", m.ServiceInstance.UserAccount, id) } func (m *Manta) AddJobInputs(id string, jobInputs []byte) error { if err := m.ProcessFunctionHook(m, id); err != nil { return err } if job, ok := m.jobs[id]; ok { var err error if !job.InputDone { // add inputs objId := fmt.Sprintf("/%s/jobs/%s/in.txt", m.ServiceInstance.UserAccount, id) m.objects[objId], err = createJobObject("in.txt", jobInputs) if err != nil { return err } m.objectsData[objId] = jobInputs return nil } else { return fmt.Errorf("/%s/jobs/%s/live/in does not exist", m.ServiceInstance.UserAccount, id) } } return fmt.Errorf("/%s/jobs/%s/job.json was not found", m.ServiceInstance.UserAccount, id) } func (m *Manta) EndJobInput(id string) error { if err := m.ProcessFunctionHook(m, id); err != nil { return err } if job, ok := m.jobs[id]; ok { if !job.InputDone { job.InputDone = true } else { return fmt.Errorf("/%s/jobs/%s/live/in/end does not exist", m.ServiceInstance.UserAccount, id) } return nil } return fmt.Errorf("/%s/jobs/%s/job.json was not found", m.ServiceInstance.UserAccount, id) } func (m *Manta) GetJobOutput(id string) (string, error) { if err := m.ProcessFunctionHook(m, id); err != nil { return "", err } if job, ok := m.jobs[id]; ok { index := len(job.Phases) - 1 phaseType := job.Phases[index].Type outputId, err := localservices.NewUUID() if err != nil { return "", err } jobOutput := fmt.Sprintf("/%s/jobs/%s/stor/%s.%d.%s", m.ServiceInstance.UserAccount, id, phaseType, index, outputId) return jobOutput, nil } return "", fmt.Errorf("/%s/jobs/%s/job.json was not found", m.ServiceInstance.UserAccount, id) } func (m *Manta) GetJobInput(id string) (string, error) { if err := m.ProcessFunctionHook(m, id); err != nil { return "", err } if _, ok := m.jobs[id]; ok { objId := fmt.Sprintf("/%s/jobs/%s/in.txt", m.ServiceInstance.UserAccount, id) if _, ok := m.objects[objId]; ok { return string(m.objectsData[objId]), nil } return "", nil } return "", fmt.Errorf("/%s/jobs/%s/job.json was not found", m.ServiceInstance.UserAccount, id) } func (m *Manta) GetJobFailures(id string) (string, error) { if err := m.ProcessFunctionHook(m, id); err != nil { return "", err } if _, ok := m.jobs[id]; ok { return "", nil } return "", fmt.Errorf("/%s/jobs/%s/job.json was not found", m.ServiceInstance.UserAccount, id) } func (m *Manta) GetJobErrors(id string) ([]manta.JobError, error) { if err := m.ProcessFunctionHook(m, id); err != nil { return nil, err } if _, ok := m.jobs[id]; ok { return nil, nil } return nil, fmt.Errorf("/%s/jobs/%s/job.json was not found", m.ServiceInstance.UserAccount, id) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/localservices/manta/service_http_test.go�������������0000644�0000153�0000161�00000032127�12321735717�031266� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gomanta - Go library to interact with Joyent Manta // // Manta double testing service - HTTP API tests // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package manta_test import ( "bytes" "encoding/json" "io/ioutil" gc "launchpad.net/gocheck" "net/http" "strconv" "strings" "fmt" "github.com/joyent/gocommon/testing" lm "github.com/joyent/gomanta/localservices/manta" "github.com/joyent/gomanta/manta" ) type MantaHTTPSuite struct { testing.HTTPSuite service *lm.Manta } var _ = gc.Suite(&MantaHTTPSuite{}) type MantaHTTPSSuite struct { testing.HTTPSuite service *lm.Manta } var _ = gc.Suite(&MantaHTTPSSuite{HTTPSuite: testing.HTTPSuite{UseTLS: true}}) const ( fakeStorPrefix = "/fakeuser/stor" fakeJobsPrefix = "/fakeuser/jobs" ) func (s *MantaHTTPSuite) SetUpSuite(c *gc.C) { s.HTTPSuite.SetUpSuite(c) c.Assert(s.Server.URL[:7], gc.Equals, "http://") s.service = lm.New(s.Server.URL, "fakeuser") } func (s *MantaHTTPSuite) TearDownSuite(c *gc.C) { s.HTTPSuite.TearDownSuite(c) } func (s *MantaHTTPSuite) SetUpTest(c *gc.C) { s.HTTPSuite.SetUpTest(c) s.service.SetupHTTP(s.Mux) } func (s *MantaHTTPSuite) TearDownTest(c *gc.C) { s.HTTPSuite.TearDownTest(c) } // assertJSON asserts the passed http.Response's body can be // unmarshalled into the given expected object, populating it with the // successfully parsed data. func assertJSON(c *gc.C, resp *http.Response, expected interface{}) { body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() c.Assert(err, gc.IsNil) err = json.Unmarshal(body, &expected) c.Assert(err, gc.IsNil) } // assertBody asserts the passed http.Response's body matches the // expected response, replacing any variables in the expected body. func assertBody(c *gc.C, resp *http.Response, expected *lm.ErrorResponse) { body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() c.Assert(err, gc.IsNil) expBody := expected.Body // cast to string for easier asserts debugging c.Assert(string(body), gc.Equals, string(expBody)) } // sendRequest constructs an HTTP request from the parameters and // sends it, returning the response or an error. func (s *MantaHTTPSuite) sendRequest(method, path string, body []byte, headers http.Header) (*http.Response, error) { if headers == nil { headers = make(http.Header) } requestURL := "http://" + s.service.Hostname + strings.TrimLeft(path, "/") req, err := http.NewRequest(method, requestURL, bytes.NewReader(body)) if err != nil { return nil, err } req.Close = true for header, values := range headers { for _, value := range values { req.Header.Add(header, value) } } // workaround for https://code.google.com/p/go/issues/detail?id=4454 req.Header.Set("Content-Length", strconv.Itoa(len(body))) return http.DefaultClient.Do(req) } // jsonRequest serializes the passed body object to JSON and sends a // the request with authRequest(). func (s *MantaHTTPSuite) jsonRequest(method, path string, body interface{}, headers http.Header) (*http.Response, error) { jsonBody, err := json.Marshal(body) if err != nil { return nil, err } return s.sendRequest(method, path, jsonBody, headers) } // Helpers func (s *MantaHTTPSuite) createDirectory(c *gc.C, path string) { reqHeaders := make(http.Header) reqHeaders.Set("Content-Type", "application/json; type=directory") resp, err := s.sendRequest("PUT", fmt.Sprintf("%s/%s", fakeStorPrefix, path), nil, reqHeaders) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) } func (s *MantaHTTPSuite) createObject(c *gc.C, objPath string) { resp, err := s.sendRequest("PUT", fmt.Sprintf("%s/%s", fakeStorPrefix, objPath), []byte(object), nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) } func (s *MantaHTTPSuite) delete(c *gc.C, path string) { resp, err := s.sendRequest("DELETE", fmt.Sprintf("%s/%s", fakeStorPrefix, path), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) } func getTestJob(c *gc.C, jobName string) []byte { phases := []manta.Phase{{Type: "map", Exec: "wc", Init: ""}, {Type: "reduce", Exec: "awk '{ l += $1; w += $2; c += $3 } END { print l, w, c }'", Init: ""}} opts := manta.CreateJobOpts{Name: jobName, Phases: phases} optsByte, err := json.Marshal(opts) c.Assert(err, gc.IsNil) return optsByte } func (s *MantaHTTPSuite) createJob(c *gc.C, jobName string) string { resp, err := s.sendRequest("POST", fakeJobsPrefix, getTestJob(c, jobName), nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) return strings.Split(resp.Header.Get("Location"), "/")[3] } // SimpleTest defines a simple request without a body and expected response. type SimpleTest struct { method string url string headers http.Header expect *lm.ErrorResponse } func (s *MantaHTTPSuite) simpleTests() []SimpleTest { var simpleTests = []SimpleTest{ { method: "GET", url: "/", headers: make(http.Header), expect: lm.ErrNotFound, }, { method: "POST", url: "/", headers: make(http.Header), expect: lm.ErrNotFound, }, { method: "DELETE", url: "/", headers: make(http.Header), expect: lm.ErrNotFound, }, { method: "PUT", url: "/", headers: make(http.Header), expect: lm.ErrNotFound, }, { method: "GET", url: "/any", headers: make(http.Header), expect: lm.ErrNotFound, }, { method: "POST", url: "/any", headers: make(http.Header), expect: lm.ErrNotFound, }, { method: "DELETE", url: "/any", headers: make(http.Header), expect: lm.ErrNotFound, }, { method: "PUT", url: "/any", headers: make(http.Header), expect: lm.ErrNotFound, }, { method: "POST", url: "/fakeuser/stor", headers: make(http.Header), expect: lm.ErrNotAllowed, }, { method: "DELETE", url: "/fakeuser/jobs", headers: make(http.Header), expect: lm.ErrNotAllowed, }, { method: "PUT", url: "/fakeuser/jobs", headers: make(http.Header), expect: lm.ErrNotAllowed, }, } return simpleTests } func (s *MantaHTTPSuite) TestSimpleRequestTests(c *gc.C) { simpleTests := s.simpleTests() for i, t := range simpleTests { c.Logf("#%d. %s %s -> %d", i, t.method, t.url, t.expect.Code) if t.headers == nil { t.headers = make(http.Header) } var ( resp *http.Response err error ) resp, err = s.sendRequest(t.method, t.url, nil, t.headers) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, t.expect.Code) assertBody(c, resp, t.expect) } } // Storage API func (s *MantaHTTPSuite) TestPutDirectory(c *gc.C) { s.createDirectory(c, "test") s.delete(c, "test") } func (s *MantaHTTPSuite) TestPutDirectoryWithParent(c *gc.C) { s.createDirectory(c, "test") defer s.delete(c, "test") s.createDirectory(c, "test/innerdir") s.delete(c, "test/innerdir") } func (s *MantaHTTPSuite) TestListDirectory(c *gc.C) { var expected []manta.Entry s.createDirectory(c, "test") defer s.delete(c, "test") s.createObject(c, "test/object") defer s.delete(c, "test/object") resp, err := s.sendRequest("GET", "/fakeuser/stor/test", nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected, gc.HasLen, 1) } func (s *MantaHTTPSuite) TestListDirectoryWithOpts(c *gc.C) { var expected []manta.Entry s.createDirectory(c, "dir") defer s.delete(c, "dir") s.createObject(c, "dir/object1") defer s.delete(c, "dir/object1") s.createObject(c, "dir/object2") defer s.delete(c, "dir/object2") s.createObject(c, "dir/object3") defer s.delete(c, "dir/object3") opts := manta.ListDirectoryOpts{Limit: 1, Marker: "object2"} optsByte, errB := json.Marshal(opts) c.Assert(errB, gc.IsNil) resp, err := s.sendRequest("GET", "/fakeuser/stor/dir", optsByte, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected, gc.HasLen, 1) } func (s *MantaHTTPSuite) TestDeleteDirectory(c *gc.C) { s.createDirectory(c, "test") defer s.delete(c, "test") s.createDirectory(c, "test/innerdir") s.delete(c, "test/innerdir") } func (s *MantaHTTPSuite) TestPutObject(c *gc.C) { s.createDirectory(c, "dir") defer s.delete(c, "dir") s.createObject(c, "dir/object") s.delete(c, "dir/object") } func (s *MantaHTTPSuite) TestGetObject(c *gc.C) { s.createDirectory(c, "dir") defer s.delete(c, "dir") s.createObject(c, "dir/object") defer s.delete(c, "dir/object") resp, err := s.sendRequest("GET", "/fakeuser/stor/dir/object", nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) // TODO: assert for headers } func (s *MantaHTTPSuite) TestDeleteObject(c *gc.C) { s.createDirectory(c, "dir") defer s.delete(c, "dir") s.createObject(c, "dir/object") s.delete(c, "dir/object") } func (s *MantaHTTPSuite) TestPutSnaplink(c *gc.C) { s.createDirectory(c, "dir") defer s.delete(c, "dir") s.createObject(c, "dir/object") defer s.delete(c, "dir/object") defer s.delete(c, "dir/link") reqHeaders := make(http.Header) reqHeaders.Set("Location", "/fakeuser/stor/dir/object") resp, err := s.sendRequest("PUT", "/fakeuser/stor/dir/link", nil, reqHeaders) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) } // Jobs API func (s *MantaHTTPSuite) TestCreateJob(c *gc.C) { s.createJob(c, "test-job") } func (s *MantaHTTPSuite) TestListAllJobs(c *gc.C) { var expected []manta.Entry s.createJob(c, "test-job") resp, err := s.sendRequest("GET", fakeJobsPrefix, nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) //c.Assert(len(expected), gc.Equals, 2) } func (s *MantaHTTPSuite) TestListLiveJobs(c *gc.C) { var expected []manta.Entry s.createJob(c, "test-job") resp, err := s.sendRequest("GET", fmt.Sprintf("%s?state=running", fakeJobsPrefix), nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) //c.Assert(len(expected), gc.Equals, 1) } func (s *MantaHTTPSuite) TestCancelJob(c *gc.C) { jobId := s.createJob(c, "test-job") url := fmt.Sprintf("%s/%s/live/cancel", fakeJobsPrefix, jobId) resp, err := s.sendRequest("POST", url, nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted) } func (s *MantaHTTPSuite) TestAddJobInputs(c *gc.C) { var inputs = `/fakeuser/stor/testjob/obj1 /fakeuser/stor/testjob/obj2 ` s.createDirectory(c, "testjob") s.createObject(c, "testjob/obj1") s.createObject(c, "testjob/obj2") jobId := s.createJob(c, "test-job") url := fmt.Sprintf("%s/%s/live/in", fakeJobsPrefix, jobId) resp, err := s.sendRequest("POST", url, []byte(inputs), nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusNoContent) } func (s *MantaHTTPSuite) TestEndJobInputs(c *gc.C) { jobId := s.createJob(c, "test-job") url := fmt.Sprintf("%s/%s/live/in/end", fakeJobsPrefix, jobId) resp, err := s.sendRequest("POST", url, nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusAccepted) } func (s *MantaHTTPSuite) TestGetJob(c *gc.C) { var expected manta.Job jobId := s.createJob(c, "test-job") url := fmt.Sprintf("%s/%s/live/status", fakeJobsPrefix, jobId) resp, err := s.sendRequest("GET", url, nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Id, gc.Equals, jobId) c.Assert(expected.Name, gc.Equals, "test-job") c.Assert(expected.Cancelled, gc.Equals, false) c.Assert(expected.InputDone, gc.Equals, false) c.Assert(expected.Phases, gc.HasLen, 2) } func (s *MantaHTTPSuite) TestGetJobInput(c *gc.C) { var expected string jobId := s.createJob(c, "test-job") url := fmt.Sprintf("%s/%s/live/in", fakeJobsPrefix, jobId) resp, err := s.sendRequest("GET", url, nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) fmt.Println(expected) c.Assert(expected, gc.HasLen, 0) } func (s *MantaHTTPSuite) TestGetJobOutput(c *gc.C) { var expected string jobId := s.createJob(c, "test-job") url := fmt.Sprintf("%s/%s/live/out", fakeJobsPrefix, jobId) resp, err := s.sendRequest("GET", url, nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(strings.Split(expected, "\n"), gc.HasLen, 1) } func (s *MantaHTTPSuite) TestGetJobFailures(c *gc.C) { var expected string jobId := s.createJob(c, "test-job") url := fmt.Sprintf("%s/%s/live/fail", fakeJobsPrefix, jobId) resp, err := s.sendRequest("GET", url, nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected, gc.Equals, "") } func (s *MantaHTTPSuite) TestGetJobErrors(c *gc.C) { var expected []manta.JobError jobId := s.createJob(c, "test-job") url := fmt.Sprintf("%s/%s/live/err", fakeJobsPrefix, jobId) resp, err := s.sendRequest("GET", url, nil, nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected, gc.HasLen, 0) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/localservices/hook/����������������������������������0000755�0000153�0000161�00000000000�12321735717�025034� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/localservices/hook/service_test.go�������������������0000644�0000153�0000161�00000004751�12321735717�030071� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package hook import ( "fmt" gc "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { gc.TestingT(t) } var _ = gc.Suite(&ServiceSuite{}) type ServiceSuite struct { ts *testService } func (s *ServiceSuite) SetUpTest(c *gc.C) { s.ts = newTestService() // This hook is called based on the function name. s.ts.RegisterControlPoint("foo", functionControlHook) // This hook is called based on a user specified hook name. s.ts.RegisterControlPoint("foobar", namedControlHook) } type testService struct { TestService label string } func newTestService() *testService { return &testService{ TestService: TestService{ ControlHooks: make(map[string]ControlProcessor), }, } } func functionControlHook(s ServiceControl, args ...interface{}) error { label := args[0].(string) returnError := args[1].(bool) if returnError { return fmt.Errorf("An error occurred") } s.(*testService).label = label return nil } func namedControlHook(s ServiceControl, args ...interface{}) error { s.(*testService).label = "foobar" return nil } func (s *testService) foo(label string, returnError bool) error { if err := s.ProcessFunctionHook(s, label, returnError); err != nil { return err } return nil } func (s *testService) bar() error { if err := s.ProcessControlHook("foobar", s); err != nil { return err } return nil } func (s *ServiceSuite) TestFunctionHookNoError(c *gc.C) { err := s.ts.foo("success", false) c.Assert(err, gc.IsNil) c.Assert(s.ts.label, gc.Equals, "success") } func (s *ServiceSuite) TestHookWithError(c *gc.C) { err := s.ts.foo("success", true) c.Assert(err, gc.Not(gc.IsNil)) c.Assert(s.ts.label, gc.Equals, "") } func (s *ServiceSuite) TestNamedHook(c *gc.C) { err := s.ts.bar() c.Assert(err, gc.IsNil) c.Assert(s.ts.label, gc.Equals, "foobar") } func (s *ServiceSuite) TestHookCleanup(c *gc.C) { // Manually delete the existing control point. s.ts.RegisterControlPoint("foo", nil) // Register a new hook and ensure it works. cleanup := s.ts.RegisterControlPoint("foo", functionControlHook) err := s.ts.foo("cleanuptest", false) c.Assert(err, gc.IsNil) c.Assert(s.ts.label, gc.Equals, "cleanuptest") // Use the cleanup func to remove the hook and check the result. cleanup() err = s.ts.foo("again", false) c.Assert(err, gc.IsNil) c.Assert(s.ts.label, gc.Equals, "cleanuptest") // Ensure that only the specified hook was removed and the other remaining one still works. err = s.ts.bar() c.Assert(err, gc.IsNil) c.Assert(s.ts.label, gc.Equals, "foobar") } �����������������������juju-core_1.18.1/src/github.com/joyent/gomanta/localservices/hook/service.go������������������������0000644�0000153�0000161�00000006216�12321735717�027030� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package hook import ( "runtime" "strings" ) type TestService struct { ServiceControl // Hooks to run when specified control points are reached in the service business logic. ControlHooks map[string]ControlProcessor } // ControlProcessor defines a function that is run when a specified control point is reached in the service // business logic. The function receives the service instance so internal state can be inspected, plus for any // arguments passed to the currently executing service function. type ControlProcessor func(sc ServiceControl, args ...interface{}) error // ControlHookCleanup defines a function used to remove a control hook. type ControlHookCleanup func() // ServiceControl instances allow hooks to be registered for execution at the specified point of execution. // The control point name can be a function name or a logical execution point meaningful to the service. // If name is "", the hook for the currently executing function is executed. // Returns a function which can be used to remove the hook. type ServiceControl interface { RegisterControlPoint(name string, controller ControlProcessor) ControlHookCleanup } // currentServiceMethodName returns the method executing on the service when ProcessControlHook was invoked. func (s *TestService) currentServiceMethodName() string { pc, _, _, ok := runtime.Caller(2) if !ok { panic("current method name cannot be found") } return unqualifiedMethodName(pc) } func unqualifiedMethodName(pc uintptr) string { f := runtime.FuncForPC(pc) fullName := f.Name() nameParts := strings.Split(fullName, ".") return nameParts[len(nameParts)-1] } // ProcessControlHook retrieves the ControlProcessor for the specified hook name and runs it, returning any error. // Use it like this to invoke a hook registered for some arbitrary control point: // if err := n.ProcessControlHook("foobar", <serviceinstance>, <somearg1>, <somearg2>); err != nil { // return err // } func (s *TestService) ProcessControlHook(hookName string, sc ServiceControl, args ...interface{}) error { if s.ControlHooks == nil { return nil } if hook, ok := s.ControlHooks[hookName]; ok { return hook(sc, args...) } return nil } // ProcessFunctionHook runs the ControlProcessor for the current function, returning any error. // Use it like this: // if err := n.ProcessFunctionHook(<serviceinstance>, <somearg1>, <somearg2>); err != nil { // return err // } func (s *TestService) ProcessFunctionHook(sc ServiceControl, args ...interface{}) error { hookName := s.currentServiceMethodName() return s.ProcessControlHook(hookName, sc, args...) } // RegisterControlPoint assigns the specified controller to the named hook. If nil, any existing controller for the // hook is removed. // hookName is the name of a function on the service or some arbitrarily named control point. func (s *TestService) RegisterControlPoint(hookName string, controller ControlProcessor) ControlHookCleanup { if s.ControlHooks == nil { s.ControlHooks = make(map[string]ControlProcessor) } if controller == nil { delete(s.ControlHooks, hookName) } else { s.ControlHooks[hookName] = controller } return func() { s.RegisterControlPoint(hookName, nil) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/localservices/localservice.go������������������������0000644�0000153�0000161�00000001752�12321735717�027103� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gomanta - Go library to interact with Joyent Manta // // Double testing service // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package localservices import ( "crypto/rand" "fmt" "io" "net/http" "github.com/joyent/gomanta/localservices/hook" ) // An HttpService provides the HTTP API for a service double. type HttpService interface { SetupHTTP(mux *http.ServeMux) } // A ServiceInstance is an Joyent Cloud service, one of manta or cloudapi. type ServiceInstance struct { hook.TestService Scheme string Hostname string UserAccount string } // NewUUID generates a random UUID according to RFC 4122 func NewUUID() (string, error) { uuid := make([]byte, 16) n, err := io.ReadFull(rand.Reader, uuid) if n != len(uuid) || err != nil { return "", err } uuid[8] = uuid[8]&^0xc0 | 0x80 uuid[6] = uuid[6]&^0xf0 | 0x40 return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil } ����������������������juju-core_1.18.1/src/github.com/joyent/gomanta/README.md��������������������������������������������0000644�0000153�0000161�00000002055�12321735717�022517� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gomanta ======= The gomanta package enables Go programs to interact with the Joyent Manta service. ## Installation Use `go-get` to install gomanta ``` go get github.com/joyent/gomanta ``` ## Packages The gomanta package is structured as follow: - gomanta/localservices. This package provides local services to be used for testing. - gomanta/manta. This package interacts with the Manta API (http://apidocs.joyent.com/manta/). ## Documentation Documentation can be found on godoc. - [http://godoc.org/github.com/joyent/gomanta](http://godoc.org/github.com/joyent/gomanta) - [http://godoc.org/github.com/joyent/gomanta/localservices] (http://godoc.org/github.com/joyent/gomanta/localservices) - [http://godoc.org/github.com/joyent/gomanta/manta](http://godoc.org/github.com/joyent/gomanta/manta) ## Testing Make sure you have the dependencies ``` go get "launchpad.net/gocheck" ``` To Run all tests ``` go test ./... ``` ## License Licensed under [LGPL v3](LICENSE). Copyright (c) 2013 Joyent Inc. Written by Daniele Stroppa <daniele.stroppa@joyent.com>�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/gomanta_test.go��������������������������������������0000644�0000153�0000161�00000000534�12321735717�024254� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // gomanta - Go library to interact with Joyent Manta // // // Copyright (c) 2013 Joyent Inc. // // Written by Daniele Stroppa <daniele.stroppa@joyent.com> // package gomanta import ( gc "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { gc.TestingT(t) } type GoMantaTestSuite struct { } var _ = gc.Suite(&GoMantaTestSuite{}) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/COPYING����������������������������������������������0000644�0000153�0000161�00000104513�12321735717�022275� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/joyent/gomanta/.gitignore�������������������������������������������0000644�0000153�0000161�00000000432�12321735717�023225� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe # IntelliJ files .idea *.iml ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/��������������������������������������������������������������0000755�0000153�0000161�00000000000�12321735762�017407� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/��������������������������������������������������������0000755�0000153�0000161�00000000000�12321735762�020516� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/COPYING.LESSER������������������������������������������0000644�0000153�0000161�00000016743�12321735762�022560� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �����������������������������juju-core_1.18.1/src/github.com/loggo/loggo/LICENSE�������������������������������������������������0000644�0000153�0000161�00000001214�12321735762�021521� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������loggo - A logging library for Go Copyright 2013, Canonical Ltd. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. See both COPYING and COPYING.LESSER for the full terms of the GNU Lesser General Public License. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/testwriter.go�������������������������������������������0000644�0000153�0000161�00000001607�12321735762�023265� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo import ( "path" "time" ) // TestLogValues represents a single logging call. type TestLogValues struct { Level Level Module string Filename string Line int Timestamp time.Time Message string } // TestWriter is a useful Writer for testing purposes. Each component of the // logging message is stored in the Log array. type TestWriter struct { Log []TestLogValues } // Write saves the params as members in the TestLogValues struct appended to the Log array. func (writer *TestWriter) Write(level Level, module, filename string, line int, timestamp time.Time, message string) { if writer.Log == nil { writer.Log = []TestLogValues{} } writer.Log = append(writer.Log, TestLogValues{level, module, path.Base(filename), line, timestamp, message}) } // Clear removes any saved log messages. func (writer *TestWriter) Clear() { writer.Log = []TestLogValues{} } �������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/logger.go�����������������������������������������������0000644�0000153�0000161�00000027360�12321735762�022334� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Module level logging for Go // // This package provides an alternative to the standard library log package. // // The actual logging functions never return errors. If you are logging // something, you really don't want to be worried about the logging // having trouble. // // Modules have names that are defined by dotted strings. // e.g. "first.second.third" // // There is a root module that has the name "". Each module // (except the root module) has a parent, identified by the part of // the name without the last dotted value. // e.g. the parent of "first.second.third" is "first.second" // the parent of "first.second" is "first" // the parent of "first" is "" (the root module) // // Each module can specify its own severity level. Logging calls that are of // a lower severity than the module's effective severity level are not written // out. // // Loggers are created using the GetLogger function. // e.g. logger := loggo.GetLogger("foo.bar") // // By default there is one writer registered, which will write to Stderr, // and the root module, which will only emit warnings and above. // If you want to continue using the default // logger, but have it emit all logging levels you need to do the following. // // writer, _, err := loggo.RemoveWriter("default") // // err is non-nil if and only if the name isn't found. // loggo.RegisterWriter("default", writer, loggo.TRACE) package loggo import ( "fmt" "runtime" "sort" "strings" "sync" "sync/atomic" "time" ) // Level holds a severity level. type Level uint32 // The severity levels. Higher values are more considered more // important. const ( UNSPECIFIED Level = iota TRACE DEBUG INFO WARNING ERROR CRITICAL ) // A Logger represents a logging module. It has an associated logging // level which can be changed; messages of lesser severity will // be dropped. Loggers have a hierarchical relationship - see // the package documentation. // // The zero Logger value is usable - any messages logged // to it will be sent to the root Logger. type Logger struct { impl *module } type module struct { name string level Level parent *module } // Initially the modules map only contains the root module. var ( root = &module{level: WARNING} modulesMutex sync.Mutex modules = map[string]*module{ "": root, } ) func (level Level) String() string { switch level { case UNSPECIFIED: return "UNSPECIFIED" case TRACE: return "TRACE" case DEBUG: return "DEBUG" case INFO: return "INFO" case WARNING: return "WARNING" case ERROR: return "ERROR" case CRITICAL: return "CRITICAL" } return "<unknown>" } // get atomically gets the value of the given level. func (level *Level) get() Level { return Level(atomic.LoadUint32((*uint32)(level))) } // set atomically sets the value of the receiver // to the given level. func (level *Level) set(newLevel Level) { atomic.StoreUint32((*uint32)(level), uint32(newLevel)) } // getLoggerInternal assumes that the modulesMutex is locked. func getLoggerInternal(name string) Logger { impl, found := modules[name] if found { return Logger{impl} } parentName := "" if i := strings.LastIndex(name, "."); i >= 0 { parentName = name[0:i] } parent := getLoggerInternal(parentName).impl impl = &module{name, UNSPECIFIED, parent} modules[name] = impl return Logger{impl} } // GetLogger returns a Logger for the given module name, // creating it and its parents if necessary. func GetLogger(name string) Logger { // Lowercase the module name, and look for it in the modules map. name = strings.ToLower(name) modulesMutex.Lock() defer modulesMutex.Unlock() return getLoggerInternal(name) } // LoggerInfo returns information about the configured loggers and their logging // levels. The information is returned in the format expected by // ConfigureModules. Loggers with UNSPECIFIED level will not // be included. func LoggerInfo() string { output := []string{} // output in alphabetical order. keys := []string{} modulesMutex.Lock() defer modulesMutex.Unlock() for key := range modules { keys = append(keys, key) } sort.Strings(keys) for _, name := range keys { mod := modules[name] severity := mod.level if severity == UNSPECIFIED { continue } output = append(output, fmt.Sprintf("%s=%s", mod.Name(), severity)) } return strings.Join(output, ";") } // ParseConfigurationString parses a logger configuration string into a map of // logger names and their associated log level. This method is provided to // allow other programs to pre-validate a configuration string rather than // just calling ConfigureLoggers. // // Loggers are colon- or semicolon-separated; each module is specified as // <modulename>=<level>. White space outside of module names and levels is // ignored. The root module is specified with the name "<root>". // // An example specification: // `<root>=ERROR; foo.bar=WARNING` func ParseConfigurationString(specification string) (map[string]Level, error) { values := strings.FieldsFunc(specification, func(r rune) bool { return r == ';' || r == ':' }) levels := make(map[string]Level) for _, value := range values { s := strings.SplitN(value, "=", 2) if len(s) < 2 { return nil, fmt.Errorf("logger specification expected '=', found %q", value) } name := strings.TrimSpace(s[0]) levelStr := strings.TrimSpace(s[1]) if name == "" || levelStr == "" { return nil, fmt.Errorf("logger specification %q has blank name or level", value) } if name == "<root>" { name = "" } level, ok := ParseLevel(levelStr) if !ok { return nil, fmt.Errorf("unknown severity level %q", levelStr) } levels[name] = level } return levels, nil } // ConfigureLoggers configures loggers according to the given string // specification, which specifies a set of modules and their associated // logging levels. Loggers are colon- or semicolon-separated; each // module is specified as <modulename>=<level>. White space outside of // module names and levels is ignored. The root module is specified // with the name "<root>". // // An example specification: // `<root>=ERROR; foo.bar=WARNING` func ConfigureLoggers(specification string) error { if specification == "" { return nil } levels, err := ParseConfigurationString(specification) if err != nil { return err } for name, level := range levels { GetLogger(name).SetLogLevel(level) } return nil } // ResetLogging iterates through the known modules and sets the levels of all // to UNSPECIFIED, except for <root> which is set to WARNING. func ResetLoggers() { modulesMutex.Lock() defer modulesMutex.Unlock() for name, module := range modules { if name == "" { module.level.set(WARNING) } else { module.level.set(UNSPECIFIED) } } } // ParseLevel converts a string representation of a logging level to a // Level. It returns the level and whether it was valid or not. func ParseLevel(level string) (Level, bool) { level = strings.ToUpper(level) switch level { case "UNSPECIFIED": return UNSPECIFIED, true case "TRACE": return TRACE, true case "DEBUG": return DEBUG, true case "INFO": return INFO, true case "WARN", "WARNING": return WARNING, true case "ERROR": return ERROR, true case "CRITICAL": return CRITICAL, true } return UNSPECIFIED, false } func (logger Logger) getModule() *module { if logger.impl == nil { return root } return logger.impl } // Name returns the logger's module name. func (logger Logger) Name() string { return logger.getModule().Name() } // LogLevel returns the configured log level of the logger. func (logger Logger) LogLevel() Level { return logger.getModule().level.get() } func (module *module) getEffectiveLogLevel() Level { // Note: the root module is guaranteed to have a // specified logging level, so acts as a suitable sentinel // for this loop. for { if level := module.level.get(); level != UNSPECIFIED { return level } module = module.parent } panic("unreachable") } func (module *module) Name() string { if module.name == "" { return "<root>" } return module.name } // EffectiveLogLevel returns the effective log level of // the receiver - that is, messages with a lesser severity // level will be discarded. // // If the log level of the receiver is unspecified, // it will be taken from the effective log level of its // parent. func (logger Logger) EffectiveLogLevel() Level { return logger.getModule().getEffectiveLogLevel() } // SetLogLevel sets the severity level of the given logger. // The root logger cannot be set to UNSPECIFIED level. // See EffectiveLogLevel for how this affects the // actual messages logged. func (logger Logger) SetLogLevel(level Level) { module := logger.getModule() // The root module can't be unspecified. if module.name == "" && level == UNSPECIFIED { level = WARNING } module.level.set(level) } // Logf logs a printf-formatted message at the given level. // A message will be discarded if level is less than the // the effective log level of the logger. // Note that the writers may also filter out messages that // are less than their registered minimum severity level. func (logger Logger) Logf(level Level, message string, args ...interface{}) { if logger.getModule().getEffectiveLogLevel() > level || !WillWrite(level) || level < TRACE || level > CRITICAL { return } // Gather time, and filename, line number. now := time.Now() // get this early. // Param to Caller is the call depth. Since this method is called from // the Logger methods, we want the place that those were called from. _, file, line, ok := runtime.Caller(2) if !ok { file = "???" line = 0 } // Trim newline off format string, following usual // Go logging conventions. if len(message) > 0 && message[len(message)-1] == '\n' { message = message[0 : len(message)-1] } formattedMessage := fmt.Sprintf(message, args...) writeToWriters(level, logger.impl.name, file, line, now, formattedMessage) } // Criticalf logs the printf-formatted message at critical level. func (logger Logger) Criticalf(message string, args ...interface{}) { logger.Logf(CRITICAL, message, args...) } // Errorf logs the printf-formatted message at error level. func (logger Logger) Errorf(message string, args ...interface{}) { logger.Logf(ERROR, message, args...) } // Warningf logs the printf-formatted message at warning level. func (logger Logger) Warningf(message string, args ...interface{}) { logger.Logf(WARNING, message, args...) } // Infof logs the printf-formatted message at info level. func (logger Logger) Infof(message string, args ...interface{}) { logger.Logf(INFO, message, args...) } // Debugf logs the printf-formatted message at debug level. func (logger Logger) Debugf(message string, args ...interface{}) { logger.Logf(DEBUG, message, args...) } // Tracef logs the printf-formatted message at trace level. func (logger Logger) Tracef(message string, args ...interface{}) { logger.Logf(TRACE, message, args...) } // IsLevelEnabled returns whether debugging is enabled // for the given log level. func (logger Logger) IsLevelEnabled(level Level) bool { return logger.getModule().getEffectiveLogLevel() <= level } // IsErrorEnabled returns whether debugging is enabled // at error level. func (logger Logger) IsErrorEnabled() bool { return logger.IsLevelEnabled(ERROR) } // IsWarningEnabled returns whether debugging is enabled // at warning level. func (logger Logger) IsWarningEnabled() bool { return logger.IsLevelEnabled(WARNING) } // IsInfoEnabled returns whether debugging is enabled // at info level. func (logger Logger) IsInfoEnabled() bool { return logger.IsLevelEnabled(INFO) } // IsDebugEnabled returns whether debugging is enabled // at debug level. func (logger Logger) IsDebugEnabled() bool { return logger.IsLevelEnabled(DEBUG) } // IsTraceEnabled returns whether debugging is enabled // at trace level. func (logger Logger) IsTraceEnabled() bool { return logger.IsLevelEnabled(TRACE) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/formatter_test.go���������������������������������������0000644�0000153�0000161�00000001133�12321735762�024105� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo_test import ( "time" gc "launchpad.net/gocheck" "github.com/loggo/loggo" ) type formatterSuite struct{} var _ = gc.Suite(&formatterSuite{}) func (*formatterSuite) TestDefaultFormat(c *gc.C) { location, err := time.LoadLocation("UTC") c.Assert(err, gc.IsNil) testTime := time.Date(2013, 5, 3, 10, 53, 24, 123456, location) formatter := &loggo.DefaultFormatter{} formatted := formatter.Format(loggo.WARNING, "test.module", "some/deep/filename", 42, testTime, "hello world!") c.Assert(formatted, gc.Equals, "2013-05-03 10:53:24 WARNING test.module filename:42 hello world!") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/formatter.go��������������������������������������������0000644�0000153�0000161�00000001652�12321735762�023054� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo import ( "fmt" "path/filepath" "time" ) // Formatter defines the single method Format, which takes the logging // information, and converts it to a string. type Formatter interface { Format(level Level, module, filename string, line int, timestamp time.Time, message string) string } // DefaultFormatter provides a simple concatenation of all the components. type DefaultFormatter struct{} // Format returns the parameters separated by spaces except for filename and // line which are separated by a colon. The timestamp is shown to second // resolution in UTC. func (*DefaultFormatter) Format(level Level, module, filename string, line int, timestamp time.Time, message string) string { ts := timestamp.In(time.UTC).Format("2006-01-02 15:04:05") // Just get the basename from the filename filename = filepath.Base(filename) return fmt.Sprintf("%s %s %s %s:%d %s", ts, level, module, filename, line, message) } ��������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/writer_test.go������������������������������������������0000644�0000153�0000161�00000017212�12321735762�023423� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo_test import ( "fmt" "time" gc "launchpad.net/gocheck" "github.com/loggo/loggo" ) type writerBasicsSuite struct{} var _ = gc.Suite(&writerBasicsSuite{}) func (s *writerBasicsSuite) TearDownTest(c *gc.C) { loggo.ResetWriters() } func (*writerBasicsSuite) TestRemoveDefaultWriter(c *gc.C) { defaultWriter, level, err := loggo.RemoveWriter("default") c.Assert(err, gc.IsNil) c.Assert(level, gc.Equals, loggo.TRACE) c.Assert(defaultWriter, gc.NotNil) // Trying again fails. defaultWriter, level, err = loggo.RemoveWriter("default") c.Assert(err, gc.ErrorMatches, `Writer "default" is not registered`) c.Assert(level, gc.Equals, loggo.UNSPECIFIED) c.Assert(defaultWriter, gc.IsNil) } func (*writerBasicsSuite) TestRegisterWriterExistingName(c *gc.C) { err := loggo.RegisterWriter("default", &loggo.TestWriter{}, loggo.INFO) c.Assert(err, gc.ErrorMatches, `there is already a Writer registered with the name "default"`) } func (*writerBasicsSuite) TestRegisterNilWriter(c *gc.C) { err := loggo.RegisterWriter("nil", nil, loggo.INFO) c.Assert(err, gc.ErrorMatches, `Writer cannot be nil`) } func (*writerBasicsSuite) TestRegisterWriterTypedNil(c *gc.C) { // If the interface is a typed nil, we have to trust the user. var writer *loggo.TestWriter err := loggo.RegisterWriter("nil", writer, loggo.INFO) c.Assert(err, gc.IsNil) } func (*writerBasicsSuite) TestReplaceDefaultWriter(c *gc.C) { oldWriter, err := loggo.ReplaceDefaultWriter(&loggo.TestWriter{}) c.Assert(oldWriter, gc.NotNil) c.Assert(err, gc.IsNil) } func (*writerBasicsSuite) TestReplaceDefaultWriterWithNil(c *gc.C) { oldWriter, err := loggo.ReplaceDefaultWriter(nil) c.Assert(oldWriter, gc.IsNil) c.Assert(err, gc.ErrorMatches, "Writer cannot be nil") } func (*writerBasicsSuite) TestReplaceDefaultWriterNoDefault(c *gc.C) { loggo.RemoveWriter("default") oldWriter, err := loggo.ReplaceDefaultWriter(&loggo.TestWriter{}) c.Assert(oldWriter, gc.IsNil) c.Assert(err, gc.ErrorMatches, `there is no "default" writer`) } func (s *writerBasicsSuite) TestWillWrite(c *gc.C) { // By default, the root logger watches TRACE messages c.Assert(loggo.WillWrite(loggo.TRACE), gc.Equals, true) // Note: ReplaceDefaultWriter doesn't let us change the default log // level :( writer, _, err := loggo.RemoveWriter("default") c.Assert(err, gc.IsNil) c.Assert(writer, gc.NotNil) err = loggo.RegisterWriter("default", writer, loggo.CRITICAL) c.Assert(err, gc.IsNil) c.Assert(loggo.WillWrite(loggo.TRACE), gc.Equals, false) c.Assert(loggo.WillWrite(loggo.DEBUG), gc.Equals, false) c.Assert(loggo.WillWrite(loggo.INFO), gc.Equals, false) c.Assert(loggo.WillWrite(loggo.WARNING), gc.Equals, false) c.Assert(loggo.WillWrite(loggo.CRITICAL), gc.Equals, true) } type writerSuite struct { logger loggo.Logger } var _ = gc.Suite(&writerSuite{}) func (s *writerSuite) SetUpTest(c *gc.C) { loggo.ResetLoggers() loggo.RemoveWriter("default") s.logger = loggo.GetLogger("test.writer") // Make it so the logger itself writes all messages. s.logger.SetLogLevel(loggo.TRACE) } func (s *writerSuite) TearDownTest(c *gc.C) { loggo.ResetWriters() } func (s *writerSuite) TearDownSuite(c *gc.C) { loggo.ResetLoggers() } func (s *writerSuite) TestWritingCapturesFileAndLineAndModule(c *gc.C) { writer := &loggo.TestWriter{} err := loggo.RegisterWriter("test", writer, loggo.INFO) c.Assert(err, gc.IsNil) s.logger.Infof("Info message") // WARNING: test checks the line number of the above logger lines, this // will mean that if the above line moves, the test will fail unless // updated. c.Assert(writer.Log, gc.HasLen, 1) c.Assert(writer.Log[0].Filename, gc.Equals, "writer_test.go") c.Assert(writer.Log[0].Line, gc.Equals, 113) c.Assert(writer.Log[0].Module, gc.Equals, "test.writer") } func (s *writerSuite) TestWritingLimitWarning(c *gc.C) { writer := &loggo.TestWriter{} err := loggo.RegisterWriter("test", writer, loggo.WARNING) c.Assert(err, gc.IsNil) start := time.Now() s.logger.Criticalf("Something critical.") s.logger.Errorf("An error.") s.logger.Warningf("A warning message") s.logger.Infof("Info message") s.logger.Tracef("Trace the function") end := time.Now() c.Assert(writer.Log, gc.HasLen, 3) c.Assert(writer.Log[0].Level, gc.Equals, loggo.CRITICAL) c.Assert(writer.Log[0].Message, gc.Equals, "Something critical.") c.Assert(writer.Log[0].Timestamp, Between(start, end)) c.Assert(writer.Log[1].Level, gc.Equals, loggo.ERROR) c.Assert(writer.Log[1].Message, gc.Equals, "An error.") c.Assert(writer.Log[1].Timestamp, Between(start, end)) c.Assert(writer.Log[2].Level, gc.Equals, loggo.WARNING) c.Assert(writer.Log[2].Message, gc.Equals, "A warning message") c.Assert(writer.Log[2].Timestamp, Between(start, end)) } func (s *writerSuite) TestWritingLimitTrace(c *gc.C) { writer := &loggo.TestWriter{} err := loggo.RegisterWriter("test", writer, loggo.TRACE) c.Assert(err, gc.IsNil) start := time.Now() s.logger.Criticalf("Something critical.") s.logger.Errorf("An error.") s.logger.Warningf("A warning message") s.logger.Infof("Info message") s.logger.Tracef("Trace the function") end := time.Now() c.Assert(writer.Log, gc.HasLen, 5) c.Assert(writer.Log[0].Level, gc.Equals, loggo.CRITICAL) c.Assert(writer.Log[0].Message, gc.Equals, "Something critical.") c.Assert(writer.Log[0].Timestamp, Between(start, end)) c.Assert(writer.Log[1].Level, gc.Equals, loggo.ERROR) c.Assert(writer.Log[1].Message, gc.Equals, "An error.") c.Assert(writer.Log[1].Timestamp, Between(start, end)) c.Assert(writer.Log[2].Level, gc.Equals, loggo.WARNING) c.Assert(writer.Log[2].Message, gc.Equals, "A warning message") c.Assert(writer.Log[2].Timestamp, Between(start, end)) c.Assert(writer.Log[3].Level, gc.Equals, loggo.INFO) c.Assert(writer.Log[3].Message, gc.Equals, "Info message") c.Assert(writer.Log[3].Timestamp, Between(start, end)) c.Assert(writer.Log[4].Level, gc.Equals, loggo.TRACE) c.Assert(writer.Log[4].Message, gc.Equals, "Trace the function") c.Assert(writer.Log[4].Timestamp, Between(start, end)) } func (s *writerSuite) TestMultipleWriters(c *gc.C) { errorWriter := &loggo.TestWriter{} err := loggo.RegisterWriter("error", errorWriter, loggo.ERROR) c.Assert(err, gc.IsNil) warningWriter := &loggo.TestWriter{} err = loggo.RegisterWriter("warning", warningWriter, loggo.WARNING) c.Assert(err, gc.IsNil) infoWriter := &loggo.TestWriter{} err = loggo.RegisterWriter("info", infoWriter, loggo.INFO) c.Assert(err, gc.IsNil) traceWriter := &loggo.TestWriter{} err = loggo.RegisterWriter("trace", traceWriter, loggo.TRACE) c.Assert(err, gc.IsNil) s.logger.Errorf("An error.") s.logger.Warningf("A warning message") s.logger.Infof("Info message") s.logger.Tracef("Trace the function") c.Assert(errorWriter.Log, gc.HasLen, 1) c.Assert(warningWriter.Log, gc.HasLen, 2) c.Assert(infoWriter.Log, gc.HasLen, 3) c.Assert(traceWriter.Log, gc.HasLen, 4) } func Between(start, end time.Time) gc.Checker { if end.Before(start) { return &betweenChecker{end, start} } return &betweenChecker{start, end} } type betweenChecker struct { start, end time.Time } func (checker *betweenChecker) Info() *gc.CheckerInfo { info := gc.CheckerInfo{ Name: "Between", Params: []string{"obtained"}, } return &info } func (checker *betweenChecker) Check(params []interface{}, names []string) (result bool, error string) { when, ok := params[0].(time.Time) if !ok { return false, "obtained value type must be time.Time" } if when.Before(checker.start) { return false, fmt.Sprintf("obtained value %#v type must before start value of %#v", when, checker.start) } if when.After(checker.end) { return false, fmt.Sprintf("obtained value %#v type must after end value of %#v", when, checker.end) } return true, "" } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/README.md�����������������������������������������������0000644�0000153�0000161�00000002675�12321735762�022007� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������loggo - hierarchical loggers for Go =================================== This package provides an alternative to the standard library log package. The actual logging functions never return errors. If you are logging something, you really don't want to be worried about the logging having trouble. Modules have names that are defined by dotted strings. ``` "first.second.third" ``` There is a root module that has the name `""`. Each module (except the root module) has a parent, identified by the part of the name without the last dotted value. * the parent of `"first.second.third"` is `"first.second"` * the parent of `"first.second"` is `"first"` * the parent of `"first"` is `""` (the root module) Each module can specify its own severity level. Logging calls that are of a lower severity than the module's effective severity level are not written out. Loggers are created using the GetLogger function. ``` logger := loggo.GetLogger("foo.bar") ``` By default there is one writer registered, which will write to Stderr, and the root module, which will only emit warnings and above. Use the usual `Sprintf` syntax for: ``` logger.Criticalf(...) logger.Errorf(...) logger.Warningf(...) logger.Infof(...) logger.Debugf(...) logger.Tracef(...) ``` If in doubt, look at the tests. They are quite good at observing the expected behaviour. The loggers themselves are go-routine safe. The locking is on the output to the writers, and transparent to the caller. �������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/logger_test.go������������������������������������������0000644�0000153�0000161�00000032000�12321735762�023356� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo_test import ( "io/ioutil" "os" "testing" gc "launchpad.net/gocheck" "github.com/loggo/loggo" ) func Test(t *testing.T) { gc.TestingT(t) } type loggerSuite struct{} var _ = gc.Suite(&loggerSuite{}) func (*loggerSuite) SetUpTest(c *gc.C) { loggo.ResetLoggers() } func (*loggerSuite) TestRootLogger(c *gc.C) { root := loggo.Logger{} c.Assert(root.Name(), gc.Equals, "<root>") c.Assert(root.IsErrorEnabled(), gc.Equals, true) c.Assert(root.IsWarningEnabled(), gc.Equals, true) c.Assert(root.IsInfoEnabled(), gc.Equals, false) c.Assert(root.IsDebugEnabled(), gc.Equals, false) c.Assert(root.IsTraceEnabled(), gc.Equals, false) } func (*loggerSuite) TestModuleName(c *gc.C) { logger := loggo.GetLogger("loggo.testing") c.Assert(logger.Name(), gc.Equals, "loggo.testing") } func (*loggerSuite) TestSetLevel(c *gc.C) { logger := loggo.GetLogger("testing") c.Assert(logger.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.WARNING) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, true) c.Assert(logger.IsInfoEnabled(), gc.Equals, false) c.Assert(logger.IsDebugEnabled(), gc.Equals, false) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) logger.SetLogLevel(loggo.TRACE) c.Assert(logger.LogLevel(), gc.Equals, loggo.TRACE) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.TRACE) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, true) c.Assert(logger.IsInfoEnabled(), gc.Equals, true) c.Assert(logger.IsDebugEnabled(), gc.Equals, true) c.Assert(logger.IsTraceEnabled(), gc.Equals, true) logger.SetLogLevel(loggo.DEBUG) c.Assert(logger.LogLevel(), gc.Equals, loggo.DEBUG) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.DEBUG) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, true) c.Assert(logger.IsInfoEnabled(), gc.Equals, true) c.Assert(logger.IsDebugEnabled(), gc.Equals, true) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) logger.SetLogLevel(loggo.INFO) c.Assert(logger.LogLevel(), gc.Equals, loggo.INFO) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.INFO) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, true) c.Assert(logger.IsInfoEnabled(), gc.Equals, true) c.Assert(logger.IsDebugEnabled(), gc.Equals, false) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) logger.SetLogLevel(loggo.WARNING) c.Assert(logger.LogLevel(), gc.Equals, loggo.WARNING) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.WARNING) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, true) c.Assert(logger.IsInfoEnabled(), gc.Equals, false) c.Assert(logger.IsDebugEnabled(), gc.Equals, false) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) logger.SetLogLevel(loggo.ERROR) c.Assert(logger.LogLevel(), gc.Equals, loggo.ERROR) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(logger.IsErrorEnabled(), gc.Equals, true) c.Assert(logger.IsWarningEnabled(), gc.Equals, false) c.Assert(logger.IsInfoEnabled(), gc.Equals, false) c.Assert(logger.IsDebugEnabled(), gc.Equals, false) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) // This is added for completeness, but not really expected to be used. logger.SetLogLevel(loggo.CRITICAL) c.Assert(logger.LogLevel(), gc.Equals, loggo.CRITICAL) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.CRITICAL) c.Assert(logger.IsErrorEnabled(), gc.Equals, false) c.Assert(logger.IsWarningEnabled(), gc.Equals, false) c.Assert(logger.IsInfoEnabled(), gc.Equals, false) c.Assert(logger.IsDebugEnabled(), gc.Equals, false) c.Assert(logger.IsTraceEnabled(), gc.Equals, false) logger.SetLogLevel(loggo.UNSPECIFIED) c.Assert(logger.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(logger.EffectiveLogLevel(), gc.Equals, loggo.WARNING) } func (*loggerSuite) TestLevelsSharedForSameModule(c *gc.C) { logger1 := loggo.GetLogger("testing.module") logger2 := loggo.GetLogger("testing.module") logger1.SetLogLevel(loggo.INFO) c.Assert(logger1.IsInfoEnabled(), gc.Equals, true) c.Assert(logger2.IsInfoEnabled(), gc.Equals, true) } func (*loggerSuite) TestModuleLowered(c *gc.C) { logger1 := loggo.GetLogger("TESTING.MODULE") logger2 := loggo.GetLogger("Testing") c.Assert(logger1.Name(), gc.Equals, "testing.module") c.Assert(logger2.Name(), gc.Equals, "testing") } func (*loggerSuite) TestLevelsInherited(c *gc.C) { root := loggo.GetLogger("") first := loggo.GetLogger("first") second := loggo.GetLogger("first.second") root.SetLogLevel(loggo.ERROR) c.Assert(root.LogLevel(), gc.Equals, loggo.ERROR) c.Assert(root.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(first.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(first.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(second.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(second.EffectiveLogLevel(), gc.Equals, loggo.ERROR) first.SetLogLevel(loggo.DEBUG) c.Assert(root.LogLevel(), gc.Equals, loggo.ERROR) c.Assert(root.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(first.LogLevel(), gc.Equals, loggo.DEBUG) c.Assert(first.EffectiveLogLevel(), gc.Equals, loggo.DEBUG) c.Assert(second.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(second.EffectiveLogLevel(), gc.Equals, loggo.DEBUG) second.SetLogLevel(loggo.INFO) c.Assert(root.LogLevel(), gc.Equals, loggo.ERROR) c.Assert(root.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(first.LogLevel(), gc.Equals, loggo.DEBUG) c.Assert(first.EffectiveLogLevel(), gc.Equals, loggo.DEBUG) c.Assert(second.LogLevel(), gc.Equals, loggo.INFO) c.Assert(second.EffectiveLogLevel(), gc.Equals, loggo.INFO) first.SetLogLevel(loggo.UNSPECIFIED) c.Assert(root.LogLevel(), gc.Equals, loggo.ERROR) c.Assert(root.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(first.LogLevel(), gc.Equals, loggo.UNSPECIFIED) c.Assert(first.EffectiveLogLevel(), gc.Equals, loggo.ERROR) c.Assert(second.LogLevel(), gc.Equals, loggo.INFO) c.Assert(second.EffectiveLogLevel(), gc.Equals, loggo.INFO) } var parseLevelTests = []struct { str string level loggo.Level fail bool }{{ str: "trace", level: loggo.TRACE, }, { str: "TrAce", level: loggo.TRACE, }, { str: "TRACE", level: loggo.TRACE, }, { str: "debug", level: loggo.DEBUG, }, { str: "DEBUG", level: loggo.DEBUG, }, { str: "info", level: loggo.INFO, }, { str: "INFO", level: loggo.INFO, }, { str: "warn", level: loggo.WARNING, }, { str: "WARN", level: loggo.WARNING, }, { str: "warning", level: loggo.WARNING, }, { str: "WARNING", level: loggo.WARNING, }, { str: "error", level: loggo.ERROR, }, { str: "ERROR", level: loggo.ERROR, }, { str: "critical", level: loggo.CRITICAL, }, { str: "not_specified", fail: true, }, { str: "other", fail: true, }, { str: "", fail: true, }} func (*loggerSuite) TestParseLevel(c *gc.C) { for _, test := range parseLevelTests { level, ok := loggo.ParseLevel(test.str) c.Assert(level, gc.Equals, test.level) c.Assert(ok, gc.Equals, !test.fail) } } var levelStringValueTests = map[loggo.Level]string{ loggo.UNSPECIFIED: "UNSPECIFIED", loggo.DEBUG: "DEBUG", loggo.TRACE: "TRACE", loggo.INFO: "INFO", loggo.WARNING: "WARNING", loggo.ERROR: "ERROR", loggo.CRITICAL: "CRITICAL", loggo.Level(42): "<unknown>", // other values are unknown } func (*loggerSuite) TestLevelStringValue(c *gc.C) { for level, str := range levelStringValueTests { c.Assert(level.String(), gc.Equals, str) } } var configureLoggersTests = []struct { spec string info string err string }{{ spec: "", info: "<root>=WARNING", }, { spec: "<root>=UNSPECIFIED", info: "<root>=WARNING", }, { spec: "<root>=DEBUG", info: "<root>=DEBUG", }, { spec: "test.module=debug", info: "<root>=WARNING;test.module=DEBUG", }, { spec: "module=info; sub.module=debug; other.module=warning", info: "<root>=WARNING;module=INFO;other.module=WARNING;sub.module=DEBUG", }, { spec: " foo.bar \t\r\n= \t\r\nCRITICAL \t\r\n; \t\r\nfoo \r\t\n = DEBUG", info: "<root>=WARNING;foo=DEBUG;foo.bar=CRITICAL", }, { spec: "foo;bar", info: "<root>=WARNING", err: `logger specification expected '=', found "foo"`, }, { spec: "=foo", info: "<root>=WARNING", err: `logger specification "=foo" has blank name or level`, }, { spec: "foo=", info: "<root>=WARNING", err: `logger specification "foo=" has blank name or level`, }, { spec: "=", info: "<root>=WARNING", err: `logger specification "=" has blank name or level`, }, { spec: "foo=unknown", info: "<root>=WARNING", err: `unknown severity level "unknown"`, }, { // Test that nothing is changed even when the // first part of the specification parses ok. spec: "module=info; foo=unknown", info: "<root>=WARNING", err: `unknown severity level "unknown"`, }} func (*loggerSuite) TestConfigureLoggers(c *gc.C) { for i, test := range configureLoggersTests { c.Logf("test %d: %q", i, test.spec) loggo.ResetLoggers() err := loggo.ConfigureLoggers(test.spec) c.Check(loggo.LoggerInfo(), gc.Equals, test.info) if test.err != "" { c.Assert(err, gc.ErrorMatches, test.err) continue } c.Assert(err, gc.IsNil) // Test that it's idempotent. err = loggo.ConfigureLoggers(test.spec) c.Assert(err, gc.IsNil) c.Assert(loggo.LoggerInfo(), gc.Equals, test.info) // Test that calling ConfigureLoggers with the // output of LoggerInfo works too. err = loggo.ConfigureLoggers(test.info) c.Assert(err, gc.IsNil) c.Assert(loggo.LoggerInfo(), gc.Equals, test.info) } } type logwriterSuite struct { logger loggo.Logger writer *loggo.TestWriter } var _ = gc.Suite(&logwriterSuite{}) func (s *logwriterSuite) SetUpTest(c *gc.C) { loggo.ResetLoggers() loggo.RemoveWriter("default") s.writer = &loggo.TestWriter{} err := loggo.RegisterWriter("test", s.writer, loggo.TRACE) c.Assert(err, gc.IsNil) s.logger = loggo.GetLogger("test.writer") // Make it so the logger itself writes all messages. s.logger.SetLogLevel(loggo.TRACE) } func (s *logwriterSuite) TearDownTest(c *gc.C) { loggo.ResetWriters() } func (s *logwriterSuite) TestLogDoesntLogWeirdLevels(c *gc.C) { s.logger.Logf(loggo.UNSPECIFIED, "message") c.Assert(s.writer.Log, gc.HasLen, 0) s.logger.Logf(loggo.Level(42), "message") c.Assert(s.writer.Log, gc.HasLen, 0) s.logger.Logf(loggo.CRITICAL+loggo.Level(1), "message") c.Assert(s.writer.Log, gc.HasLen, 0) } func (s *logwriterSuite) TestMessageFormatting(c *gc.C) { s.logger.Logf(loggo.INFO, "some %s included", "formatting") c.Assert(s.writer.Log, gc.HasLen, 1) c.Assert(s.writer.Log[0].Message, gc.Equals, "some formatting included") c.Assert(s.writer.Log[0].Level, gc.Equals, loggo.INFO) } func (s *logwriterSuite) BenchmarkLoggingNoWriters(c *gc.C) { // No writers loggo.RemoveWriter("test") for i := 0; i < c.N; i++ { s.logger.Warningf("just a simple warning for %d", i) } } func (s *logwriterSuite) BenchmarkLoggingNoWritersNoFormat(c *gc.C) { // No writers loggo.RemoveWriter("test") for i := 0; i < c.N; i++ { s.logger.Warningf("just a simple warning") } } func (s *logwriterSuite) BenchmarkLoggingTestWriters(c *gc.C) { for i := 0; i < c.N; i++ { s.logger.Warningf("just a simple warning for %d", i) } c.Assert(s.writer.Log, gc.HasLen, c.N) } func setupTempFileWriter(c *gc.C) (logFile *os.File, cleanup func()) { loggo.RemoveWriter("test") logFile, err := ioutil.TempFile("", "loggo-test") c.Assert(err, gc.IsNil) cleanup = func() { logFile.Close() os.Remove(logFile.Name()) } writer := loggo.NewSimpleWriter(logFile, &loggo.DefaultFormatter{}) err = loggo.RegisterWriter("testfile", writer, loggo.TRACE) c.Assert(err, gc.IsNil) return } func (s *logwriterSuite) BenchmarkLoggingDiskWriter(c *gc.C) { logFile, cleanup := setupTempFileWriter(c) defer cleanup() msg := "just a simple warning for %d" for i := 0; i < c.N; i++ { s.logger.Warningf(msg, i) } offset, err := logFile.Seek(0, os.SEEK_CUR) c.Assert(err, gc.IsNil) c.Assert((offset > int64(len(msg))*int64(c.N)), gc.Equals, true, gc.Commentf("Not enough data was written to the log file.")) } func (s *logwriterSuite) BenchmarkLoggingDiskWriterNoMessages(c *gc.C) { logFile, cleanup := setupTempFileWriter(c) defer cleanup() // Change the log level writer, _, err := loggo.RemoveWriter("testfile") c.Assert(err, gc.IsNil) loggo.RegisterWriter("testfile", writer, loggo.WARNING) msg := "just a simple warning for %d" for i := 0; i < c.N; i++ { s.logger.Debugf(msg, i) } offset, err := logFile.Seek(0, os.SEEK_CUR) c.Assert(err, gc.IsNil) c.Assert(offset, gc.Equals, int64(0), gc.Commentf("Data was written to the log file.")) } func (s *logwriterSuite) BenchmarkLoggingDiskWriterNoMessagesLogLevel(c *gc.C) { logFile, cleanup := setupTempFileWriter(c) defer cleanup() // Change the log level s.logger.SetLogLevel(loggo.WARNING) msg := "just a simple warning for %d" for i := 0; i < c.N; i++ { s.logger.Debugf(msg, i) } offset, err := logFile.Seek(0, os.SEEK_CUR) c.Assert(err, gc.IsNil) c.Assert(offset, gc.Equals, int64(0), gc.Commentf("Data was written to the log file.")) } juju-core_1.18.1/src/github.com/loggo/loggo/writer.go�����������������������������������������������0000644�0000153�0000161�00000010406�12321735762�022362� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package loggo import ( "fmt" "io" "os" "sync" "time" ) // Writer is implemented by any recipient of log messages. type Writer interface { // Write writes a message to the Writer with the given // level and module name. The filename and line hold // the file name and line number of the code that is // generating the log message; the time stamp holds // the time the log message was generated, and // message holds the log message itself. Write(level Level, name, filename string, line int, timestamp time.Time, message string) } type registeredWriter struct { writer Writer level Level } // defaultName is the name of a writer that is registered // by default that writes to stderr. const defaultName = "default" var ( writerMutex sync.Mutex writers = map[string]*registeredWriter{ defaultName: &registeredWriter{ writer: NewSimpleWriter(os.Stderr, &DefaultFormatter{}), level: TRACE, }, } globalMinLevel = TRACE ) // ResetWriters puts the list of writers back into the initial state. func ResetWriters() { writerMutex.Lock() defer writerMutex.Unlock() writers = map[string]*registeredWriter{ "default": &registeredWriter{ writer: NewSimpleWriter(os.Stderr, &DefaultFormatter{}), level: TRACE, }, } findMinLevel() } // ReplaceDefaultWriter is a convenience method that does the equivalent of // RemoveWriter and then RegisterWriter with the name "default". The previous // default writer, if any is returned. func ReplaceDefaultWriter(writer Writer) (Writer, error) { if writer == nil { return nil, fmt.Errorf("Writer cannot be nil") } writerMutex.Lock() defer writerMutex.Unlock() reg, found := writers[defaultName] if !found { return nil, fmt.Errorf("there is no %q writer", defaultName) } oldWriter := reg.writer reg.writer = writer return oldWriter, nil } // RegisterWriter adds the writer to the list of writers that get notified // when logging. When registering, the caller specifies the minimum logging // level that will be written, and a name for the writer. If there is already // a registered writer with that name, an error is returned. func RegisterWriter(name string, writer Writer, minLevel Level) error { if writer == nil { return fmt.Errorf("Writer cannot be nil") } writerMutex.Lock() defer writerMutex.Unlock() if _, found := writers[name]; found { return fmt.Errorf("there is already a Writer registered with the name %q", name) } writers[name] = &registeredWriter{writer: writer, level: minLevel} findMinLevel() return nil } // RemoveWriter removes the Writer identified by 'name' and returns it. // If the Writer is not found, an error is returned. func RemoveWriter(name string) (Writer, Level, error) { writerMutex.Lock() defer writerMutex.Unlock() registered, found := writers[name] if !found { return nil, UNSPECIFIED, fmt.Errorf("Writer %q is not registered", name) } delete(writers, name) findMinLevel() return registered.writer, registered.level, nil } func findMinLevel() { // We assume the lock is already held minLevel := CRITICAL for _, registered := range writers { if registered.level < minLevel { minLevel = registered.level } } globalMinLevel.set(minLevel) } // WillWrite returns whether there are any writers registered // at or above the given severity level. If it returns // false, a log message at the given level will be discarded. func WillWrite(level Level) bool { return level >= globalMinLevel.get() } func writeToWriters(level Level, module, filename string, line int, timestamp time.Time, message string) { writerMutex.Lock() defer writerMutex.Unlock() for _, registered := range writers { if level >= registered.level { registered.writer.Write(level, module, filename, line, timestamp, message) } } } type simpleWriter struct { writer io.Writer formatter Formatter } // NewSimpleWriter returns a new writer that writes // log messages to the given io.Writer formatting the // messages with the given formatter. func NewSimpleWriter(writer io.Writer, formatter Formatter) Writer { return &simpleWriter{writer, formatter} } func (simple *simpleWriter) Write(level Level, module, filename string, line int, timestamp time.Time, message string) { logLine := simple.formatter.Format(level, module, filename, line, timestamp, message) fmt.Fprintln(simple.writer, logLine) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/COPYING�������������������������������������������������0000644�0000153�0000161�00000104513�12321735762�021555� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/.gitignore����������������������������������������������0000644�0000153�0000161�00000000020�12321735762�022476� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������example/example ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/example/������������������������������������������������0000755�0000153�0000161�00000000000�12321735762�022151� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/example/second.go���������������������������������������0000644�0000153�0000161�00000000631�12321735762�023753� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "github.com/loggo/loggo" ) var second = loggo.GetLogger("second") func SecondCritical(message string) { second.Criticalf(message) } func SecondError(message string) { second.Errorf(message) } func SecondWarning(message string) { second.Warningf(message) } func SecondInfo(message string) { second.Infof(message) } func SecondTrace(message string) { second.Tracef(message) } �������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/example/main.go�����������������������������������������0000644�0000153�0000161�00000001405�12321735762�023424� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "fmt" "os" "github.com/loggo/loggo" ) var logger = loggo.GetLogger("main") var rootLogger = loggo.GetLogger("") func main() { args := os.Args if len(args) > 1 { loggo.ConfigureLoggers(args[1]) } else { fmt.Println("Add a parameter to configure the logging:") fmt.Println("E.g. \"<root>=INFO;first=TRACE\"") } fmt.Println("\nCurrent logging levels:") fmt.Println(loggo.LoggerInfo()) fmt.Println("") rootLogger.Infof("Start of test.") FirstCritical("first critical") FirstError("first error") FirstWarning("first warning") FirstInfo("first info") FirstTrace("first trace") SecondCritical("first critical") SecondError("first error") SecondWarning("first warning") SecondInfo("first info") SecondTrace("first trace") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/github.com/loggo/loggo/example/first.go����������������������������������������0000644�0000153�0000161�00000000615�12321735762�023631� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "github.com/loggo/loggo" ) var first = loggo.GetLogger("first") func FirstCritical(message string) { first.Criticalf(message) } func FirstError(message string) { first.Errorf(message) } func FirstWarning(message string) { first.Warningf(message) } func FirstInfo(message string) { first.Infof(message) } func FirstTrace(message string) { first.Tracef(message) } �������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/�����������������������������������������������������������������0000755�0000153�0000161�00000000000�12321736005�016754� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gnuflag/���������������������������������������������������������0000755�0000153�0000161�00000000000�12321735666�020413� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gnuflag/export_test.go�������������������������������������������0000644�0000153�0000161�00000001211�12321735666�023315� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 gnuflag import ( "os" ) // Additional routines compiled into the package only during testing. // ResetForTesting clears all flag state and sets the usage function as directed. // After calling ResetForTesting, parse errors in flag handling will not // exit the program. func ResetForTesting(usage func()) { commandLine = NewFlagSet(os.Args[0], ContinueOnError) Usage = usage } // CommandLine returns the default FlagSet. func CommandLine() *FlagSet { return commandLine } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gnuflag/flag_test.go���������������������������������������������0000644�0000153�0000161�00000027146�12321735666�022724� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package gnuflag_test import ( "bytes" "fmt" . "launchpad.net/gnuflag" "os" "reflect" "sort" "strings" "testing" "time" ) var ( test_bool = Bool("test_bool", false, "bool value") test_int = Int("test_int", 0, "int value") test_int64 = Int64("test_int64", 0, "int64 value") test_uint = Uint("test_uint", 0, "uint value") test_uint64 = Uint64("test_uint64", 0, "uint64 value") test_string = String("test_string", "0", "string value") test_float64 = Float64("test_float64", 0, "float64 value") test_duration = Duration("test_duration", 0, "time.Duration value") ) func boolString(s string) string { if s == "0" { return "false" } return "true" } func TestEverything(t *testing.T) { m := make(map[string]*Flag) desired := "0" visitor := func(f *Flag) { if len(f.Name) > 5 && f.Name[0:5] == "test_" { m[f.Name] = f ok := false switch { case f.Value.String() == desired: ok = true case f.Name == "test_bool" && f.Value.String() == boolString(desired): ok = true case f.Name == "test_duration" && f.Value.String() == desired+"s": ok = true } if !ok { t.Error("Visit: bad value", f.Value.String(), "for", f.Name) } } } VisitAll(visitor) if len(m) != 8 { t.Error("VisitAll misses some flags") for k, v := range m { t.Log(k, *v) } } m = make(map[string]*Flag) Visit(visitor) if len(m) != 0 { t.Errorf("Visit sees unset flags") for k, v := range m { t.Log(k, *v) } } // Now set all flags Set("test_bool", "true") Set("test_int", "1") Set("test_int64", "1") Set("test_uint", "1") Set("test_uint64", "1") Set("test_string", "1") Set("test_float64", "1") Set("test_duration", "1s") desired = "1" Visit(visitor) if len(m) != 8 { t.Error("Visit fails after set") for k, v := range m { t.Log(k, *v) } } // Now test they're visited in sort order. var flagNames []string Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) }) if !sort.StringsAreSorted(flagNames) { t.Errorf("flag names not sorted: %v", flagNames) } } func TestUsage(t *testing.T) { called := false ResetForTesting(func() { called = true }) f := CommandLine() f.SetOutput(nullWriter{}) if f.Parse(true, []string{"-x"}) == nil { t.Error("parse did not fail for unknown flag") } if !called { t.Error("did not call Usage for unknown flag") } } var parseTests = []struct { about string intersperse bool args []string vals map[string]interface{} remaining []string error string }{{ about: "regular args", intersperse: true, args: []string{ "--bool2", "--int", "22", "--int64", "0x23", "--uint", "24", "--uint64", "25", "--string", "hello", "--float64", "2718e28", "--duration", "2m", "one - extra - argument", }, vals: map[string]interface{}{ "bool": false, "bool2": true, "int": 22, "int64": int64(0x23), "uint": uint(24), "uint64": uint64(25), "string": "hello", "float64": 2718e28, "duration": 2 * 60 * time.Second, }, remaining: []string{ "one - extra - argument", }, }, { about: "playing with -", intersperse: true, args: []string{ "-a", "-", "-bc", "2", "-de1s", "-f2s", "-g", "3s", "--h", "--long", "--long2", "-4s", "3", "4", "--", "-5", }, vals: map[string]interface{}{ "a": true, "b": true, "c": true, "d": true, "e": "1s", "f": "2s", "g": "3s", "h": true, "long": true, "long2": "-4s", "z": "default", "www": 99, }, remaining: []string{ "-", "2", "3", "4", "-5", }, }, { about: "flag after explicit --", intersperse: true, args: []string{ "-a", "--", "-b", }, vals: map[string]interface{}{ "a": true, "b": false, }, remaining: []string{ "-b", }, }, { about: "flag after end", args: []string{ "-a", "foo", "-b", }, vals: map[string]interface{}{ "a": true, "b": false, }, remaining: []string{ "foo", "-b", }, }, { about: "arg and flag after explicit end", args: []string{ "-a", "--", "foo", "-b", }, vals: map[string]interface{}{ "a": true, "b": false, }, remaining: []string{ "foo", "-b", }, }, { about: "boolean args, explicitly and non-explicitly given", args: []string{ "--a=false", "--b=true", "--c", }, vals: map[string]interface{}{ "a": false, "b": true, "c": true, }, }, { about: "using =", args: []string{ "--arble=bar", "--bletch=", "--a=something", "-b=other", "-cdand more", "--curdle=--milk", "--sandwich", "=", "--darn=", "=arg", }, vals: map[string]interface{}{ "arble": "bar", "bletch": "", "a": "something", "b": "=other", "c": true, "d": "and more", "curdle": "--milk", "sandwich": "=", "darn": "", }, remaining: []string{"=arg"}, }, { about: "empty flag #1", args: []string{ "--=bar", }, error: `empty flag in argument "--=bar"`, }, { about: "single-letter equals", args: []string{ "-=bar", }, error: `flag provided but not defined: -=`, }, { about: "empty flag #2", args: []string{ "--=", }, error: `empty flag in argument "--="`, }, { about: "no equals", args: []string{ "-=", }, error: `flag provided but not defined: -=`, }, { args: []string{ "-a=true", }, vals: map[string]interface{}{ "a": true, }, error: `invalid value "=true" for flag -a: strconv.ParseBool: parsing "=true": invalid syntax`, }, { intersperse: true, args: []string{ "-a", "-b", }, vals: map[string]interface{}{ "a": true, }, error: "flag provided but not defined: -b", }, { intersperse: true, args: []string{ "-a", }, vals: map[string]interface{}{ "a": "default", }, error: "flag needs an argument: -a", }, { intersperse: true, args: []string{ "-a", "b", }, vals: map[string]interface{}{ "a": 0, }, error: `invalid value "b" for flag -a: strconv.ParseInt: parsing "b": invalid syntax`, }, } func testParse(newFlagSet func() *FlagSet, t *testing.T) { for i, g := range parseTests { t.Logf("test %d. %s", i, g.about) f := newFlagSet() flags := make(map[string]interface{}) for name, val := range g.vals { switch val.(type) { case bool: flags[name] = f.Bool(name, false, "bool value "+name) case string: flags[name] = f.String(name, "default", "string value "+name) case int: flags[name] = f.Int(name, 99, "int value "+name) case uint: flags[name] = f.Uint(name, 0, "uint value") case uint64: flags[name] = f.Uint64(name, 0, "uint64 value") case int64: flags[name] = f.Int64(name, 0, "uint64 value") case float64: flags[name] = f.Float64(name, 0, "float64 value") case time.Duration: flags[name] = f.Duration(name, 5*time.Second, "duration value") default: t.Fatalf("unhandled type %T", val) } } err := f.Parse(g.intersperse, g.args) if g.error != "" { if err == nil { t.Errorf("expected error %q got nil", g.error) } else if err.Error() != g.error { t.Errorf("expected error %q got %q", g.error, err.Error()) } continue } for name, val := range g.vals { actual := reflect.ValueOf(flags[name]).Elem().Interface() if val != actual { t.Errorf("flag %q, expected %v got %v", name, val, actual) } } if len(f.Args()) != len(g.remaining) { t.Fatalf("remaining args, expected %q got %q", g.remaining, f.Args()) } for j, a := range f.Args() { if a != g.remaining[j] { t.Errorf("arg %d, expected %q got %q", j, g.remaining[i], a) } } } } func TestParse(t *testing.T) { testParse(func() *FlagSet { ResetForTesting(func() {}) f := CommandLine() f.SetOutput(nullWriter{}) return f }, t) } func TestFlagSetParse(t *testing.T) { testParse(func() *FlagSet { f := NewFlagSet("test", ContinueOnError) f.SetOutput(nullWriter{}) return f }, t) } // Declare a user-defined flag type. type flagVar []string func (f *flagVar) String() string { return fmt.Sprint([]string(*f)) } func (f *flagVar) Set(value string) error { *f = append(*f, value) return nil } func TestUserDefined(t *testing.T) { var flags FlagSet flags.Init("test", ContinueOnError) var v flagVar flags.Var(&v, "v", "usage") if err := flags.Parse(true, []string{"-v", "1", "-v", "2", "-v3"}); err != nil { t.Error(err) } if len(v) != 3 { t.Fatal("expected 3 args; got ", len(v)) } expect := "[1 2 3]" if v.String() != expect { t.Errorf("expected value %q got %q", expect, v.String()) } } func TestSetOutput(t *testing.T) { var flags FlagSet var buf bytes.Buffer flags.SetOutput(&buf) flags.Init("test", ContinueOnError) flags.Parse(true, []string{"-unknown"}) if out := buf.String(); !strings.Contains(out, "-unknown") { t.Logf("expected output mentioning unknown; got %q", out) } } // This tests that one can reset the flags. This still works but not well, and is // superseded by FlagSet. func TestChangingArgs(t *testing.T) { ResetForTesting(func() { t.Fatal("bad parse") }) oldArgs := os.Args defer func() { os.Args = oldArgs }() os.Args = []string{"cmd", "--before", "subcmd", "--after", "args"} before := Bool("before", false, "") if err := CommandLine().Parse(false, os.Args[1:]); err != nil { t.Fatal(err) } cmd := Arg(0) os.Args = Args() after := Bool("after", false, "") Parse(false) args := Args() if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) } } // Test that -help invokes the usage message and returns ErrHelp. func TestHelp(t *testing.T) { var helpCalled = false fs := NewFlagSet("help test", ContinueOnError) fs.SetOutput(nullWriter{}) fs.Usage = func() { helpCalled = true } var flag bool fs.BoolVar(&flag, "flag", false, "regular flag") // Regular flag invocation should work err := fs.Parse(true, []string{"--flag"}) if err != nil { t.Fatal("expected no error; got ", err) } if !flag { t.Error("flag was not set by --flag") } if helpCalled { t.Error("help called for regular flag") helpCalled = false // reset for next test } // Help flag should work as expected. err = fs.Parse(true, []string{"--help"}) if err == nil { t.Fatal("error expected") } if err != ErrHelp { t.Fatal("expected ErrHelp; got ", err) } if !helpCalled { t.Fatal("help was not called") } // If we define a help flag, that should override. var help bool fs.BoolVar(&help, "help", false, "help flag") helpCalled = false err = fs.Parse(true, []string{"--help"}) if err != nil { t.Fatal("expected no error for defined --help; got ", err) } if helpCalled { t.Fatal("help was called; should not have been for defined help flag") } } type nullWriter struct{} func (nullWriter) Write(buf []byte) (int, error) { return len(buf), nil } func TestPrintDefaults(t *testing.T) { f := NewFlagSet("print test", ContinueOnError) f.SetOutput(nullWriter{}) var b bool var c int var d string var e float64 f.IntVar(&c, "trapclap", 99, "usage not shown") f.IntVar(&c, "c", 99, "c usage") f.BoolVar(&b, "bal", false, "usage not shown") f.BoolVar(&b, "x", false, "usage not shown") f.BoolVar(&b, "b", false, "b usage") f.BoolVar(&b, "balalaika", false, "usage not shown") f.StringVar(&d, "d", "d default", "d usage") f.Float64Var(&e, "elephant", 3.14, "elephant usage") var buf bytes.Buffer f.SetOutput(&buf) f.PrintDefaults() f.SetOutput(nullWriter{}) expect := `-b, -x, --bal, --balalaika (= false) b usage -c, --trapclap (= 99) c usage -d (= "d default") d usage --elephant (= 3.14) elephant usage ` if buf.String() != expect { t.Errorf("expect %q got %q", expect, buf.String()) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gnuflag/flag.go��������������������������������������������������0000644�0000153�0000161�00000066612�12321735666�021666� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. /* Package flag implements command-line flag parsing in the GNU style. It is almost exactly the same as the standard flag package, the only difference being the extra argument to Parse. Command line flag syntax: -f // single letter flag -fg // two single letter flags together --flag // multiple letter flag --flag x // non-boolean flags only -f x // non-boolean flags only -fx // if f is a non-boolean flag, x is its argument. The last three forms are not permitted for boolean flags because the meaning of the command cmd -f * will change if there is a file called 0, false, etc. There is currently no way to turn off a boolean flag. Flag parsing stops after the terminator "--", or just before the first non-flag argument ("-" is a non-flag argument) if the interspersed argument to Parse is false. */ package gnuflag import ( "bytes" "errors" "fmt" "io" "os" "sort" "strconv" "strings" "time" "unicode/utf8" ) // ErrHelp is the error returned if the flag -help is invoked but no such flag is defined. var ErrHelp = errors.New("flag: help requested") // -- bool Value type boolValue bool func newBoolValue(val bool, p *bool) *boolValue { *p = val return (*boolValue)(p) } func (b *boolValue) Set(s string) error { v, err := strconv.ParseBool(s) *b = boolValue(v) return err } func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) } // -- int Value type intValue int func newIntValue(val int, p *int) *intValue { *p = val return (*intValue)(p) } func (i *intValue) Set(s string) error { v, err := strconv.ParseInt(s, 0, 64) *i = intValue(v) return err } func (i *intValue) String() string { return fmt.Sprintf("%v", *i) } // -- int64 Value type int64Value int64 func newInt64Value(val int64, p *int64) *int64Value { *p = val return (*int64Value)(p) } func (i *int64Value) Set(s string) error { v, err := strconv.ParseInt(s, 0, 64) *i = int64Value(v) return err } func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) } // -- uint Value type uintValue uint func newUintValue(val uint, p *uint) *uintValue { *p = val return (*uintValue)(p) } func (i *uintValue) Set(s string) error { v, err := strconv.ParseUint(s, 0, 64) *i = uintValue(v) return err } func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) } // -- uint64 Value type uint64Value uint64 func newUint64Value(val uint64, p *uint64) *uint64Value { *p = val return (*uint64Value)(p) } func (i *uint64Value) Set(s string) error { v, err := strconv.ParseUint(s, 0, 64) *i = uint64Value(v) return err } func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) } // -- string Value type stringValue string func newStringValue(val string, p *string) *stringValue { *p = val return (*stringValue)(p) } func (s *stringValue) Set(val string) error { *s = stringValue(val) return nil } func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) } // -- float64 Value type float64Value float64 func newFloat64Value(val float64, p *float64) *float64Value { *p = val return (*float64Value)(p) } func (f *float64Value) Set(s string) error { v, err := strconv.ParseFloat(s, 64) *f = float64Value(v) return err } func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) } // -- time.Duration Value type durationValue time.Duration func newDurationValue(val time.Duration, p *time.Duration) *durationValue { *p = val return (*durationValue)(p) } func (d *durationValue) Set(s string) error { v, err := time.ParseDuration(s) *d = durationValue(v) return err } func (d *durationValue) String() string { return (*time.Duration)(d).String() } // Value is the interface to the dynamic value stored in a flag. // (The default value is represented as a string.) type Value interface { String() string Set(string) error } // ErrorHandling defines how to handle flag parsing errors. type ErrorHandling int const ( ContinueOnError ErrorHandling = iota ExitOnError PanicOnError ) // A FlagSet represents a set of defined flags. type FlagSet struct { // Usage is the function called when an error occurs while parsing flags. // The field is a function (not a method) that may be changed to point to // a custom error handler. Usage func() name string parsed bool actual map[string]*Flag formal map[string]*Flag args []string // arguments after flags procArgs []string // arguments being processed (gnu only) procFlag string // flag being processed (gnu only) allowIntersperse bool // (gnu only) exitOnError bool // does the program exit if there's an error? errorHandling ErrorHandling output io.Writer // nil means stderr; use out() accessor } // A Flag represents the state of a flag. type Flag struct { Name string // name as it appears on command line Usage string // help message Value Value // value as set DefValue string // default value (as text); for usage message } // sortFlags returns the flags as a slice in lexicographical sorted order. func sortFlags(flags map[string]*Flag) []*Flag { list := make(sort.StringSlice, len(flags)) i := 0 for _, f := range flags { list[i] = f.Name i++ } list.Sort() result := make([]*Flag, len(list)) for i, name := range list { result[i] = flags[name] } return result } func (f *FlagSet) out() io.Writer { if f.output == nil { return os.Stderr } return f.output } // SetOutput sets the destination for usage and error messages. // If output is nil, os.Stderr is used. func (f *FlagSet) SetOutput(output io.Writer) { f.output = output } // VisitAll visits the flags in lexicographical order, calling fn for each. // It visits all flags, even those not set. func (f *FlagSet) VisitAll(fn func(*Flag)) { for _, flag := range sortFlags(f.formal) { fn(flag) } } // VisitAll visits the command-line flags in lexicographical order, calling // fn for each. It visits all flags, even those not set. func VisitAll(fn func(*Flag)) { commandLine.VisitAll(fn) } // Visit visits the flags in lexicographical order, calling fn for each. // It visits only those flags that have been set. func (f *FlagSet) Visit(fn func(*Flag)) { for _, flag := range sortFlags(f.actual) { fn(flag) } } // Visit visits the command-line flags in lexicographical order, calling fn // for each. It visits only those flags that have been set. func Visit(fn func(*Flag)) { commandLine.Visit(fn) } // Lookup returns the Flag structure of the named flag, returning nil if none exists. func (f *FlagSet) Lookup(name string) *Flag { return f.formal[name] } // Lookup returns the Flag structure of the named command-line flag, // returning nil if none exists. func Lookup(name string) *Flag { return commandLine.formal[name] } // Set sets the value of the named flag. func (f *FlagSet) Set(name, value string) error { flag, ok := f.formal[name] if !ok { return fmt.Errorf("no such flag -%v", name) } err := flag.Value.Set(value) if err != nil { return err } if f.actual == nil { f.actual = make(map[string]*Flag) } f.actual[name] = flag return nil } // Set sets the value of the named command-line flag. func Set(name, value string) error { return commandLine.Set(name, value) } // flagsByLength is a slice of flags implementing sort.Interface, // sorting primarily by the length of the flag, and secondarily // alphabetically. type flagsByLength []*Flag func (f flagsByLength) Less(i, j int) bool { s1, s2 := f[i].Name, f[j].Name if len(s1) != len(s2) { return len(s1) < len(s2) } return s1 < s2 } func (f flagsByLength) Swap(i, j int) { f[i], f[j] = f[j], f[i] } func (f flagsByLength) Len() int { return len(f) } // flagsByName is a slice of slices of flags implementing sort.Interface, // alphabetically sorting by the name of the first flag in each slice. type flagsByName [][]*Flag func (f flagsByName) Less(i, j int) bool { return f[i][0].Name < f[j][0].Name } func (f flagsByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } func (f flagsByName) Len() int { return len(f) } // PrintDefaults prints, to standard error unless configured // otherwise, the default values of all defined flags in the set. // If there is more than one name for a given flag, the usage information and // default value from the shortest will be printed (or the least alphabetically // if there are several equally short flag names). func (f *FlagSet) PrintDefaults() { // group together all flags for a given value flags := make(map[interface{}]flagsByLength) f.VisitAll(func(f *Flag) { flags[f.Value] = append(flags[f.Value], f) }) // sort the output flags by shortest name for each group. var byName flagsByName for _, f := range flags { sort.Sort(f) byName = append(byName, f) } sort.Sort(byName) var line bytes.Buffer for _, fs := range byName { line.Reset() for i, f := range fs { if i > 0 { line.WriteString(", ") } line.WriteString(flagWithMinus(f.Name)) } format := "%s (= %s)\n %s\n" if _, ok := fs[0].Value.(*stringValue); ok { // put quotes on the value format = "%s (= %q)\n %s\n" } fmt.Fprintf(f.out(), format, line.Bytes(), fs[0].DefValue, fs[0].Usage) } } // PrintDefaults prints to standard error the default values of all defined command-line flags. func PrintDefaults() { commandLine.PrintDefaults() } // defaultUsage is the default function to print a usage message. func defaultUsage(f *FlagSet) { fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) f.PrintDefaults() } // NOTE: Usage is not just defaultUsage(commandLine) // because it serves (via godoc flag Usage) as the example // for how to write your own usage function. // Usage prints to standard error a usage message documenting all defined command-line flags. // The function is a variable that may be changed to point to a custom function. var Usage = func() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) PrintDefaults() } // NFlag returns the number of flags that have been set. func (f *FlagSet) NFlag() int { return len(f.actual) } // NFlag returns the number of command-line flags that have been set. func NFlag() int { return len(commandLine.actual) } // Arg returns the i'th argument. Arg(0) is the first remaining argument // after flags have been processed. func (f *FlagSet) Arg(i int) string { if i < 0 || i >= len(f.args) { return "" } return f.args[i] } // Arg returns the i'th command-line argument. Arg(0) is the first remaining argument // after flags have been processed. func Arg(i int) string { return commandLine.Arg(i) } // NArg is the number of arguments remaining after flags have been processed. func (f *FlagSet) NArg() int { return len(f.args) } // NArg is the number of arguments remaining after flags have been processed. func NArg() int { return len(commandLine.args) } // Args returns the non-flag arguments. func (f *FlagSet) Args() []string { return f.args } // Args returns the non-flag command-line arguments. func Args() []string { return commandLine.args } // BoolVar defines a bool flag with specified name, default value, and usage string. // The argument p points to a bool variable in which to store the value of the flag. func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) { f.Var(newBoolValue(value, p), name, usage) } // BoolVar defines a bool flag with specified name, default value, and usage string. // The argument p points to a bool variable in which to store the value of the flag. func BoolVar(p *bool, name string, value bool, usage string) { commandLine.Var(newBoolValue(value, p), name, usage) } // Bool defines a bool flag with specified name, default value, and usage string. // The return value is the address of a bool variable that stores the value of the flag. func (f *FlagSet) Bool(name string, value bool, usage string) *bool { p := new(bool) f.BoolVar(p, name, value, usage) return p } // Bool defines a bool flag with specified name, default value, and usage string. // The return value is the address of a bool variable that stores the value of the flag. func Bool(name string, value bool, usage string) *bool { return commandLine.Bool(name, value, usage) } // IntVar defines an int flag with specified name, default value, and usage string. // The argument p points to an int variable in which to store the value of the flag. func (f *FlagSet) IntVar(p *int, name string, value int, usage string) { f.Var(newIntValue(value, p), name, usage) } // IntVar defines an int flag with specified name, default value, and usage string. // The argument p points to an int variable in which to store the value of the flag. func IntVar(p *int, name string, value int, usage string) { commandLine.Var(newIntValue(value, p), name, usage) } // Int defines an int flag with specified name, default value, and usage string. // The return value is the address of an int variable that stores the value of the flag. func (f *FlagSet) Int(name string, value int, usage string) *int { p := new(int) f.IntVar(p, name, value, usage) return p } // Int defines an int flag with specified name, default value, and usage string. // The return value is the address of an int variable that stores the value of the flag. func Int(name string, value int, usage string) *int { return commandLine.Int(name, value, usage) } // Int64Var defines an int64 flag with specified name, default value, and usage string. // The argument p points to an int64 variable in which to store the value of the flag. func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) { f.Var(newInt64Value(value, p), name, usage) } // Int64Var defines an int64 flag with specified name, default value, and usage string. // The argument p points to an int64 variable in which to store the value of the flag. func Int64Var(p *int64, name string, value int64, usage string) { commandLine.Var(newInt64Value(value, p), name, usage) } // Int64 defines an int64 flag with specified name, default value, and usage string. // The return value is the address of an int64 variable that stores the value of the flag. func (f *FlagSet) Int64(name string, value int64, usage string) *int64 { p := new(int64) f.Int64Var(p, name, value, usage) return p } // Int64 defines an int64 flag with specified name, default value, and usage string. // The return value is the address of an int64 variable that stores the value of the flag. func Int64(name string, value int64, usage string) *int64 { return commandLine.Int64(name, value, usage) } // UintVar defines a uint flag with specified name, default value, and usage string. // The argument p points to a uint variable in which to store the value of the flag. func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) { f.Var(newUintValue(value, p), name, usage) } // UintVar defines a uint flag with specified name, default value, and usage string. // The argument p points to a uint variable in which to store the value of the flag. func UintVar(p *uint, name string, value uint, usage string) { commandLine.Var(newUintValue(value, p), name, usage) } // Uint defines a uint flag with specified name, default value, and usage string. // The return value is the address of a uint variable that stores the value of the flag. func (f *FlagSet) Uint(name string, value uint, usage string) *uint { p := new(uint) f.UintVar(p, name, value, usage) return p } // Uint defines a uint flag with specified name, default value, and usage string. // The return value is the address of a uint variable that stores the value of the flag. func Uint(name string, value uint, usage string) *uint { return commandLine.Uint(name, value, usage) } // Uint64Var defines a uint64 flag with specified name, default value, and usage string. // The argument p points to a uint64 variable in which to store the value of the flag. func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) { f.Var(newUint64Value(value, p), name, usage) } // Uint64Var defines a uint64 flag with specified name, default value, and usage string. // The argument p points to a uint64 variable in which to store the value of the flag. func Uint64Var(p *uint64, name string, value uint64, usage string) { commandLine.Var(newUint64Value(value, p), name, usage) } // Uint64 defines a uint64 flag with specified name, default value, and usage string. // The return value is the address of a uint64 variable that stores the value of the flag. func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 { p := new(uint64) f.Uint64Var(p, name, value, usage) return p } // Uint64 defines a uint64 flag with specified name, default value, and usage string. // The return value is the address of a uint64 variable that stores the value of the flag. func Uint64(name string, value uint64, usage string) *uint64 { return commandLine.Uint64(name, value, usage) } // StringVar defines a string flag with specified name, default value, and usage string. // The argument p points to a string variable in which to store the value of the flag. func (f *FlagSet) StringVar(p *string, name string, value string, usage string) { f.Var(newStringValue(value, p), name, usage) } // StringVar defines a string flag with specified name, default value, and usage string. // The argument p points to a string variable in which to store the value of the flag. func StringVar(p *string, name string, value string, usage string) { commandLine.Var(newStringValue(value, p), name, usage) } // String defines a string flag with specified name, default value, and usage string. // The return value is the address of a string variable that stores the value of the flag. func (f *FlagSet) String(name string, value string, usage string) *string { p := new(string) f.StringVar(p, name, value, usage) return p } // String defines a string flag with specified name, default value, and usage string. // The return value is the address of a string variable that stores the value of the flag. func String(name string, value string, usage string) *string { return commandLine.String(name, value, usage) } // Float64Var defines a float64 flag with specified name, default value, and usage string. // The argument p points to a float64 variable in which to store the value of the flag. func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) { f.Var(newFloat64Value(value, p), name, usage) } // Float64Var defines a float64 flag with specified name, default value, and usage string. // The argument p points to a float64 variable in which to store the value of the flag. func Float64Var(p *float64, name string, value float64, usage string) { commandLine.Var(newFloat64Value(value, p), name, usage) } // Float64 defines a float64 flag with specified name, default value, and usage string. // The return value is the address of a float64 variable that stores the value of the flag. func (f *FlagSet) Float64(name string, value float64, usage string) *float64 { p := new(float64) f.Float64Var(p, name, value, usage) return p } // Float64 defines a float64 flag with specified name, default value, and usage string. // The return value is the address of a float64 variable that stores the value of the flag. func Float64(name string, value float64, usage string) *float64 { return commandLine.Float64(name, value, usage) } // DurationVar defines a time.Duration flag with specified name, default value, and usage string. // The argument p points to a time.Duration variable in which to store the value of the flag. func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) { f.Var(newDurationValue(value, p), name, usage) } // DurationVar defines a time.Duration flag with specified name, default value, and usage string. // The argument p points to a time.Duration variable in which to store the value of the flag. func DurationVar(p *time.Duration, name string, value time.Duration, usage string) { commandLine.Var(newDurationValue(value, p), name, usage) } // Duration defines a time.Duration flag with specified name, default value, and usage string. // The return value is the address of a time.Duration variable that stores the value of the flag. func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration { p := new(time.Duration) f.DurationVar(p, name, value, usage) return p } // Duration defines a time.Duration flag with specified name, default value, and usage string. // The return value is the address of a time.Duration variable that stores the value of the flag. func Duration(name string, value time.Duration, usage string) *time.Duration { return commandLine.Duration(name, value, usage) } // Var defines a flag with the specified name and usage string. The type and // value of the flag are represented by the first argument, of type Value, which // typically holds a user-defined implementation of Value. For instance, the // caller could create a flag that turns a comma-separated string into a slice // of strings by giving the slice the methods of Value; in particular, Set would // decompose the comma-separated string into the slice. func (f *FlagSet) Var(value Value, name string, usage string) { // Remember the default value as a string; it won't change. flag := &Flag{name, usage, value, value.String()} _, alreadythere := f.formal[name] if alreadythere { fmt.Fprintf(f.out(), "%s flag redefined: %s\n", f.name, name) panic("flag redefinition") // Happens only if flags are declared with identical names } if f.formal == nil { f.formal = make(map[string]*Flag) } f.formal[name] = flag } // Var defines a flag with the specified name and usage string. The type and // value of the flag are represented by the first argument, of type Value, which // typically holds a user-defined implementation of Value. For instance, the // caller could create a flag that turns a comma-separated string into a slice // of strings by giving the slice the methods of Value; in particular, Set would // decompose the comma-separated string into the slice. func Var(value Value, name string, usage string) { commandLine.Var(value, name, usage) } // failf prints to standard error a formatted error and usage message and // returns the error. func (f *FlagSet) failf(format string, a ...interface{}) error { err := fmt.Errorf(format, a...) fmt.Fprintln(f.out(), err) f.usage() return err } // usage calls the Usage method for the flag set, or the usage function if // the flag set is commandLine. func (f *FlagSet) usage() { if f == commandLine { Usage() } else if f.Usage == nil { defaultUsage(f) } else { f.Usage() } } func (f *FlagSet) parseOneGnu() (flagName string, long, finished bool, err error) { if len(f.procArgs) == 0 { finished = true return } // processing previously encountered single-rune flag if flag := f.procFlag; len(flag) > 0 { _, n := utf8.DecodeRuneInString(flag) f.procFlag = flag[n:] flagName = flag[0:n] return } a := f.procArgs[0] // one non-flag argument if a == "-" || a == "" || a[0] != '-' { if f.allowIntersperse { f.args = append(f.args, a) f.procArgs = f.procArgs[1:] return } f.args = append(f.args, f.procArgs...) f.procArgs = nil finished = true return } // end of flags if f.procArgs[0] == "--" { f.args = append(f.args, f.procArgs[1:]...) f.procArgs = nil finished = true return } // long flag signified with "--" prefix if a[1] == '-' { long = true i := strings.Index(a, "=") if i < 0 { f.procArgs = f.procArgs[1:] flagName = a[2:] return } flagName = a[2:i] if flagName == "" { err = fmt.Errorf("empty flag in argument %q", a) return } f.procArgs = f.procArgs[1:] f.procFlag = a[i:] return } // some number of single-rune flags a = a[1:] _, n := utf8.DecodeRuneInString(a) flagName = a[0:n] f.procFlag = a[n:] f.procArgs = f.procArgs[1:] return } func flagWithMinus(name string) string { if len(name) > 1 { return "--" + name } return "-" + name } func (f *FlagSet) parseGnuFlagArg(name string, long bool) (finished bool, err error) { m := f.formal flag, alreadythere := m[name] // BUG if !alreadythere { if name == "help" || name == "h" { // special case for nice help message. f.usage() return false, ErrHelp } // TODO print --xxx when flag is more than one rune. return false, f.failf("flag provided but not defined: %s", flagWithMinus(name)) } if fv, ok := flag.Value.(*boolValue); ok && !strings.HasPrefix(f.procFlag, "=") { // special case: doesn't need an arg, and an arg hasn't // been provided explicitly. fv.Set("true") } else { // It must have a value, which might be the next argument. var hasValue bool var value string if f.procFlag != "" { // value directly follows flag value = f.procFlag if long { if value[0] != '=' { panic("no leading '=' in long flag") } value = value[1:] } hasValue = true f.procFlag = "" } if !hasValue && len(f.procArgs) > 0 { // value is the next arg hasValue = true value, f.procArgs = f.procArgs[0], f.procArgs[1:] } if !hasValue { return false, f.failf("flag needs an argument: %s", flagWithMinus(name)) } if err := flag.Value.Set(value); err != nil { return false, f.failf("invalid value %q for flag %s: %v", value, flagWithMinus(name), err) } } if f.actual == nil { f.actual = make(map[string]*Flag) } f.actual[name] = flag return } // Parse parses flag definitions from the argument list, which should not // include the command name. Must be called after all flags in the FlagSet // are defined and before flags are accessed by the program. // The return value will be ErrHelp if --help was set but not defined. // If allowIntersperse is set, arguments and flags can be interspersed, that // is flags can follow positional arguments. func (f *FlagSet) Parse(allowIntersperse bool, arguments []string) error { f.parsed = true f.procArgs = arguments f.procFlag = "" f.args = nil f.allowIntersperse = allowIntersperse for { name, long, finished, err := f.parseOneGnu() if !finished { if name != "" { finished, err = f.parseGnuFlagArg(name, long) } } if err != nil { switch f.errorHandling { case ContinueOnError: return err case ExitOnError: os.Exit(2) case PanicOnError: panic(err) } } if !finished { continue } if err == nil { break } } return nil } // Parsed reports whether f.Parse has been called. func (f *FlagSet) Parsed() bool { return f.parsed } // Parse parses the command-line flags from os.Args[1:]. Must be called // after all flags are defined and before flags are accessed by the program. // If allowIntersperse is set, arguments and flags can be interspersed, that // is flags can follow positional arguments. func Parse(allowIntersperse bool) { // Ignore errors; commandLine is set for ExitOnError. commandLine.Parse(allowIntersperse, os.Args[1:]) } // Parsed returns true if the command-line flags have been parsed. func Parsed() bool { return commandLine.Parsed() } // The default set of command-line flags, parsed from os.Args. var commandLine = NewFlagSet(os.Args[0], ExitOnError) // NewFlagSet returns a new, empty flag set with the specified name and // error handling property. func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { f := &FlagSet{ name: name, errorHandling: errorHandling, } return f } // Init sets the name and error handling property for a flag set. // By default, the zero FlagSet uses an empty name and the // ContinueOnError error handling policy. func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { f.name = name f.errorHandling = errorHandling } ����������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/tomb/������������������������������������������������������������0000755�0000153�0000161�00000000000�12321735702�017720� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/tomb/LICENSE�����������������������������������������������������0000644�0000153�0000161�00000003110�12321735702�020720� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tomb - support for clean goroutine termination in Go. Copyright (c) 2010-2011 - Gustavo Niemeyer <gustavo@niemeyer.net> All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/tomb/tomb_test.go������������������������������������������������0000644�0000153�0000161�00000004741�12321735702�022255� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package tomb_test import ( "errors" "launchpad.net/tomb" "reflect" "testing" ) func TestNewTomb(t *testing.T) { tb := &tomb.Tomb{} testState(t, tb, false, false, tomb.ErrStillAlive) tb.Done() testState(t, tb, true, true, nil) } func TestKill(t *testing.T) { // a nil reason flags the goroutine as dying tb := &tomb.Tomb{} tb.Kill(nil) testState(t, tb, true, false, nil) // a non-nil reason now will override Kill err := errors.New("some error") tb.Kill(err) testState(t, tb, true, false, err) // another non-nil reason won't replace the first one tb.Kill(errors.New("ignore me")) testState(t, tb, true, false, err) tb.Done() testState(t, tb, true, true, err) } func TestKillf(t *testing.T) { tb := &tomb.Tomb{} err := tb.Killf("BO%s", "OM") if s := err.Error(); s != "BOOM" { t.Fatalf(`Killf("BO%s", "OM"): want "BOOM", got %q`, s) } testState(t, tb, true, false, err) // another non-nil reason won't replace the first one tb.Killf("ignore me") testState(t, tb, true, false, err) tb.Done() testState(t, tb, true, true, err) } func TestErrDying(t *testing.T) { // ErrDying being used properly, after a clean death. tb := &tomb.Tomb{} tb.Kill(nil) tb.Kill(tomb.ErrDying) testState(t, tb, true, false, nil) // ErrDying being used properly, after an errorful death. err := errors.New("some error") tb.Kill(err) tb.Kill(tomb.ErrDying) testState(t, tb, true, false, err) // ErrDying being used badly, with an alive tomb. tb = &tomb.Tomb{} defer func() { err := recover() if err != "tomb: Kill with ErrDying while still alive" { t.Fatalf("Wrong panic on Kill(ErrDying): %v", err) } testState(t, tb, false, false, tomb.ErrStillAlive) }() tb.Kill(tomb.ErrDying) } func testState(t *testing.T, tb *tomb.Tomb, wantDying, wantDead bool, wantErr error) { select { case <-tb.Dying(): if !wantDying { t.Error("<-Dying: should block") } default: if wantDying { t.Error("<-Dying: should not block") } } seemsDead := false select { case <-tb.Dead(): if !wantDead { t.Error("<-Dead: should block") } seemsDead = true default: if wantDead { t.Error("<-Dead: should not block") } } if err := tb.Err(); err != wantErr { t.Errorf("Err: want %#v, got %#v", wantErr, err) } if wantDead && seemsDead { waitErr := tb.Wait() switch { case waitErr == tomb.ErrStillAlive: t.Errorf("Wait should not return ErrStillAlive") case !reflect.DeepEqual(waitErr, wantErr): t.Errorf("Wait: want %#v, got %#v", wantErr, waitErr) } } } �������������������������������juju-core_1.18.1/src/launchpad.net/tomb/tomb.go�����������������������������������������������������0000644�0000153�0000161�00000013161�12321735702�021212� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright (c) 2011 - Gustavo Niemeyer <gustavo@niemeyer.net> // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the copyright holder nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // The tomb package helps with clean goroutine termination. // // See the Tomb type for details. package tomb import ( "errors" "fmt" "sync" ) // A Tomb tracks the lifecycle of a goroutine as alive, dying or dead, // and the reason for its death. // // The zero value of a Tomb assumes that a goroutine is about to be // created or already alive. Once Kill or Killf is called with an // argument that informs the reason for death, the goroutine is in // a dying state and is expected to terminate soon. Right before the // goroutine function or method returns, Done must be called to inform // that the goroutine is indeed dead and about to stop running. // // A Tomb exposes Dying and Dead channels. These channels are closed // when the Tomb state changes in the respective way. They enable // explicit blocking until the state changes, and also to selectively // unblock select statements accordingly. // // When the tomb state changes to dying and there's still logic going // on within the goroutine, nested functions and methods may choose to // return ErrDying as their error value, as this error won't alter the // tomb state if provied to the Kill method. This is a convenient way to // follow standard Go practices in the context of a dying tomb. // // For background and a detailed example, see the following blog post: // // http://blog.labix.org/2011/10/09/death-of-goroutines-under-control // // For a more complex code snippet demonstrating the use of multiple // goroutines with a single Tomb, see: // // http://play.golang.org/p/Xh7qWsDPZP // type Tomb struct { m sync.Mutex dying chan struct{} dead chan struct{} reason error } var ( ErrStillAlive = errors.New("tomb: still alive") ErrDying = errors.New("tomb: dying") ) func (t *Tomb) init() { t.m.Lock() if t.dead == nil { t.dead = make(chan struct{}) t.dying = make(chan struct{}) t.reason = ErrStillAlive } t.m.Unlock() } // Dead returns the channel that can be used to wait // until t.Done has been called. func (t *Tomb) Dead() <-chan struct{} { t.init() return t.dead } // Dying returns the channel that can be used to wait // until t.Kill or t.Done has been called. func (t *Tomb) Dying() <-chan struct{} { t.init() return t.dying } // Wait blocks until the goroutine is in a dead state and returns the // reason for its death. func (t *Tomb) Wait() error { t.init() <-t.dead t.m.Lock() reason := t.reason t.m.Unlock() return reason } // Done flags the goroutine as dead, and should be called a single time // right before the goroutine function or method returns. // If the goroutine was not already in a dying state before Done is // called, it will be flagged as dying and dead at once with no // error. func (t *Tomb) Done() { t.Kill(nil) close(t.dead) } // Kill flags the goroutine as dying for the given reason. // Kill may be called multiple times, but only the first // non-nil error is recorded as the reason for termination. // // If reason is ErrDying, the previous reason isn't replaced // even if it is nil. It's a runtime error to call Kill with // ErrDying if t is not in a dying state. func (t *Tomb) Kill(reason error) { t.init() t.m.Lock() defer t.m.Unlock() if reason == ErrDying { if t.reason == ErrStillAlive { panic("tomb: Kill with ErrDying while still alive") } return } if t.reason == nil || t.reason == ErrStillAlive { t.reason = reason } // If the receive on t.dying succeeds, then // it can only be because we have already closed it. // If it blocks, then we know that it needs to be closed. select { case <-t.dying: default: close(t.dying) } } // Killf works like Kill, but builds the reason providing the received // arguments to fmt.Errorf. The generated error is also returned. func (t *Tomb) Killf(f string, a ...interface{}) error { err := fmt.Errorf(f, a...) t.Kill(err) return err } // Err returns the reason for the goroutine death provided via Kill // or Killf, or ErrStillAlive when the goroutine is still alive. func (t *Tomb) Err() (reason error) { t.init() t.m.Lock() reason = t.reason t.m.Unlock() return } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/tomb/.lbox�������������������������������������������������������0000644�0000153�0000161�00000000031�12321735702�020657� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������propose -cr -for=lp:tomb �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/tomb/Makefile����������������������������������������������������0000644�0000153�0000161�00000000624�12321735702�021362� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������include $(GOROOT)/src/Make.inc all: package TARG=launchpad.net/tomb GOFILES=\ tomb.go\ GOFMT=gofmt BADFMT:=$(shell $(GOFMT) -l $(GOFILES) $(CGOFILES) $(wildcard *_test.go) 2> /dev/null) gofmt: $(BADFMT) @for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done ifneq ($(BADFMT),) ifneq ($(MAKECMDGOALS),gofmt) $(warning WARNING: make gofmt: $(BADFMT)) endif endif include $(GOROOT)/src/Make.pkg ������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/���������������������������������������������������������0000755�0000153�0000161�00000000000�12321736015�020360� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/fixture_test.go������������������������������������������0000644�0000153�0000161�00000033232�12321735711�023441� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Tests for the behavior of the test fixture system. package gocheck_test import ( . "launchpad.net/gocheck" ) // ----------------------------------------------------------------------- // Fixture test suite. type FixtureS struct{} var fixtureS = Suite(&FixtureS{}) func (s *FixtureS) TestCountSuite(c *C) { suitesRun += 1 } // ----------------------------------------------------------------------- // Basic fixture ordering verification. func (s *FixtureS) TestOrder(c *C) { helper := FixtureHelper{} Run(&helper, nil) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "Test1") c.Check(helper.calls[3], Equals, "TearDownTest") c.Check(helper.calls[4], Equals, "SetUpTest") c.Check(helper.calls[5], Equals, "Test2") c.Check(helper.calls[6], Equals, "TearDownTest") c.Check(helper.calls[7], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 8) } // ----------------------------------------------------------------------- // Check the behavior when panics occur within tests and fixtures. func (s *FixtureS) TestPanicOnTest(c *C) { helper := FixtureHelper{panicOn: "Test1"} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "Test1") c.Check(helper.calls[3], Equals, "TearDownTest") c.Check(helper.calls[4], Equals, "SetUpTest") c.Check(helper.calls[5], Equals, "Test2") c.Check(helper.calls[6], Equals, "TearDownTest") c.Check(helper.calls[7], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 8) expected := "^\n-+\n" + "PANIC: gocheck_test\\.go:[0-9]+: FixtureHelper.Test1\n\n" + "\\.\\.\\. Panic: Test1 \\(PC=[xA-F0-9]+\\)\n\n" + ".+:[0-9]+\n" + " in panic\n" + ".*gocheck_test.go:[0-9]+\n" + " in FixtureHelper.trace\n" + ".*gocheck_test.go:[0-9]+\n" + " in FixtureHelper.Test1\n$" c.Check(output.value, Matches, expected) } func (s *FixtureS) TestPanicOnSetUpTest(c *C) { helper := FixtureHelper{panicOn: "SetUpTest"} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "TearDownTest") c.Check(helper.calls[3], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 4) expected := "^\n-+\n" + "PANIC: gocheck_test\\.go:[0-9]+: " + "FixtureHelper\\.SetUpTest\n\n" + "\\.\\.\\. Panic: SetUpTest \\(PC=[xA-F0-9]+\\)\n\n" + ".+:[0-9]+\n" + " in panic\n" + ".*gocheck_test.go:[0-9]+\n" + " in FixtureHelper.trace\n" + ".*gocheck_test.go:[0-9]+\n" + " in FixtureHelper.SetUpTest\n" + "\n-+\n" + "PANIC: gocheck_test\\.go:[0-9]+: " + "FixtureHelper\\.Test1\n\n" + "\\.\\.\\. Panic: Fixture has panicked " + "\\(see related PANIC\\)\n$" c.Check(output.value, Matches, expected) } func (s *FixtureS) TestPanicOnTearDownTest(c *C) { helper := FixtureHelper{panicOn: "TearDownTest"} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "Test1") c.Check(helper.calls[3], Equals, "TearDownTest") c.Check(helper.calls[4], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 5) expected := "^\n-+\n" + "PANIC: gocheck_test\\.go:[0-9]+: " + "FixtureHelper.TearDownTest\n\n" + "\\.\\.\\. Panic: TearDownTest \\(PC=[xA-F0-9]+\\)\n\n" + ".+:[0-9]+\n" + " in panic\n" + ".*gocheck_test.go:[0-9]+\n" + " in FixtureHelper.trace\n" + ".*gocheck_test.go:[0-9]+\n" + " in FixtureHelper.TearDownTest\n" + "\n-+\n" + "PANIC: gocheck_test\\.go:[0-9]+: " + "FixtureHelper\\.Test1\n\n" + "\\.\\.\\. Panic: Fixture has panicked " + "\\(see related PANIC\\)\n$" c.Check(output.value, Matches, expected) } func (s *FixtureS) TestPanicOnSetUpSuite(c *C) { helper := FixtureHelper{panicOn: "SetUpSuite"} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 2) expected := "^\n-+\n" + "PANIC: gocheck_test\\.go:[0-9]+: " + "FixtureHelper.SetUpSuite\n\n" + "\\.\\.\\. Panic: SetUpSuite \\(PC=[xA-F0-9]+\\)\n\n" + ".+:[0-9]+\n" + " in panic\n" + ".*gocheck_test.go:[0-9]+\n" + " in FixtureHelper.trace\n" + ".*gocheck_test.go:[0-9]+\n" + " in FixtureHelper.SetUpSuite\n$" c.Check(output.value, Matches, expected) } func (s *FixtureS) TestPanicOnTearDownSuite(c *C) { helper := FixtureHelper{panicOn: "TearDownSuite"} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "Test1") c.Check(helper.calls[3], Equals, "TearDownTest") c.Check(helper.calls[4], Equals, "SetUpTest") c.Check(helper.calls[5], Equals, "Test2") c.Check(helper.calls[6], Equals, "TearDownTest") c.Check(helper.calls[7], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 8) expected := "^\n-+\n" + "PANIC: gocheck_test\\.go:[0-9]+: " + "FixtureHelper.TearDownSuite\n\n" + "\\.\\.\\. Panic: TearDownSuite \\(PC=[xA-F0-9]+\\)\n\n" + ".+:[0-9]+\n" + " in panic\n" + ".*gocheck_test.go:[0-9]+\n" + " in FixtureHelper.trace\n" + ".*gocheck_test.go:[0-9]+\n" + " in FixtureHelper.TearDownSuite\n$" c.Check(output.value, Matches, expected) } // ----------------------------------------------------------------------- // A wrong argument on a test or fixture will produce a nice error. func (s *FixtureS) TestPanicOnWrongTestArg(c *C) { helper := WrongTestArgHelper{} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "TearDownTest") c.Check(helper.calls[3], Equals, "SetUpTest") c.Check(helper.calls[4], Equals, "Test2") c.Check(helper.calls[5], Equals, "TearDownTest") c.Check(helper.calls[6], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 7) expected := "^\n-+\n" + "PANIC: fixture_test\\.go:[0-9]+: " + "WrongTestArgHelper\\.Test1\n\n" + "\\.\\.\\. Panic: WrongTestArgHelper\\.Test1 argument " + "should be \\*gocheck\\.C\n" c.Check(output.value, Matches, expected) } func (s *FixtureS) TestPanicOnWrongSetUpTestArg(c *C) { helper := WrongSetUpTestArgHelper{} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(len(helper.calls), Equals, 0) expected := "^\n-+\n" + "PANIC: fixture_test\\.go:[0-9]+: " + "WrongSetUpTestArgHelper\\.SetUpTest\n\n" + "\\.\\.\\. Panic: WrongSetUpTestArgHelper\\.SetUpTest argument " + "should be \\*gocheck\\.C\n" c.Check(output.value, Matches, expected) } func (s *FixtureS) TestPanicOnWrongSetUpSuiteArg(c *C) { helper := WrongSetUpSuiteArgHelper{} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(len(helper.calls), Equals, 0) expected := "^\n-+\n" + "PANIC: fixture_test\\.go:[0-9]+: " + "WrongSetUpSuiteArgHelper\\.SetUpSuite\n\n" + "\\.\\.\\. Panic: WrongSetUpSuiteArgHelper\\.SetUpSuite argument " + "should be \\*gocheck\\.C\n" c.Check(output.value, Matches, expected) } // ----------------------------------------------------------------------- // Nice errors also when tests or fixture have wrong arg count. func (s *FixtureS) TestPanicOnWrongTestArgCount(c *C) { helper := WrongTestArgCountHelper{} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "TearDownTest") c.Check(helper.calls[3], Equals, "SetUpTest") c.Check(helper.calls[4], Equals, "Test2") c.Check(helper.calls[5], Equals, "TearDownTest") c.Check(helper.calls[6], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 7) expected := "^\n-+\n" + "PANIC: fixture_test\\.go:[0-9]+: " + "WrongTestArgCountHelper\\.Test1\n\n" + "\\.\\.\\. Panic: WrongTestArgCountHelper\\.Test1 argument " + "should be \\*gocheck\\.C\n" c.Check(output.value, Matches, expected) } func (s *FixtureS) TestPanicOnWrongSetUpTestArgCount(c *C) { helper := WrongSetUpTestArgCountHelper{} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(len(helper.calls), Equals, 0) expected := "^\n-+\n" + "PANIC: fixture_test\\.go:[0-9]+: " + "WrongSetUpTestArgCountHelper\\.SetUpTest\n\n" + "\\.\\.\\. Panic: WrongSetUpTestArgCountHelper\\.SetUpTest argument " + "should be \\*gocheck\\.C\n" c.Check(output.value, Matches, expected) } func (s *FixtureS) TestPanicOnWrongSetUpSuiteArgCount(c *C) { helper := WrongSetUpSuiteArgCountHelper{} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(len(helper.calls), Equals, 0) expected := "^\n-+\n" + "PANIC: fixture_test\\.go:[0-9]+: " + "WrongSetUpSuiteArgCountHelper\\.SetUpSuite\n\n" + "\\.\\.\\. Panic: WrongSetUpSuiteArgCountHelper" + "\\.SetUpSuite argument should be \\*gocheck\\.C\n" c.Check(output.value, Matches, expected) } // ----------------------------------------------------------------------- // Helper test suites with wrong function arguments. type WrongTestArgHelper struct { FixtureHelper } func (s *WrongTestArgHelper) Test1(t int) { } type WrongSetUpTestArgHelper struct { FixtureHelper } func (s *WrongSetUpTestArgHelper) SetUpTest(t int) { } type WrongSetUpSuiteArgHelper struct { FixtureHelper } func (s *WrongSetUpSuiteArgHelper) SetUpSuite(t int) { } type WrongTestArgCountHelper struct { FixtureHelper } func (s *WrongTestArgCountHelper) Test1(c *C, i int) { } type WrongSetUpTestArgCountHelper struct { FixtureHelper } func (s *WrongSetUpTestArgCountHelper) SetUpTest(c *C, i int) { } type WrongSetUpSuiteArgCountHelper struct { FixtureHelper } func (s *WrongSetUpSuiteArgCountHelper) SetUpSuite(c *C, i int) { } // ----------------------------------------------------------------------- // Ensure fixture doesn't run without tests. type NoTestsHelper struct { hasRun bool } func (s *NoTestsHelper) SetUpSuite(c *C) { s.hasRun = true } func (s *NoTestsHelper) TearDownSuite(c *C) { s.hasRun = true } func (s *FixtureS) TestFixtureDoesntRunWithoutTests(c *C) { helper := NoTestsHelper{} output := String{} Run(&helper, &RunConf{Output: &output}) c.Check(helper.hasRun, Equals, false) } // ----------------------------------------------------------------------- // Verify that checks and assertions work correctly inside the fixture. type FixtureCheckHelper struct { fail string completed bool } func (s *FixtureCheckHelper) SetUpSuite(c *C) { switch s.fail { case "SetUpSuiteAssert": c.Assert(false, Equals, true) case "SetUpSuiteCheck": c.Check(false, Equals, true) } s.completed = true } func (s *FixtureCheckHelper) SetUpTest(c *C) { switch s.fail { case "SetUpTestAssert": c.Assert(false, Equals, true) case "SetUpTestCheck": c.Check(false, Equals, true) } s.completed = true } func (s *FixtureCheckHelper) Test(c *C) { // Do nothing. } func (s *FixtureS) TestSetUpSuiteCheck(c *C) { helper := FixtureCheckHelper{fail: "SetUpSuiteCheck"} output := String{} Run(&helper, &RunConf{Output: &output}) c.Assert(output.value, Matches, "\n---+\n"+ "FAIL: fixture_test\\.go:[0-9]+: "+ "FixtureCheckHelper\\.SetUpSuite\n\n"+ "fixture_test\\.go:[0-9]+:\n"+ " c\\.Check\\(false, Equals, true\\)\n"+ "\\.+ obtained bool = false\n"+ "\\.+ expected bool = true\n\n") c.Assert(helper.completed, Equals, true) } func (s *FixtureS) TestSetUpSuiteAssert(c *C) { helper := FixtureCheckHelper{fail: "SetUpSuiteAssert"} output := String{} Run(&helper, &RunConf{Output: &output}) c.Assert(output.value, Matches, "\n---+\n"+ "FAIL: fixture_test\\.go:[0-9]+: "+ "FixtureCheckHelper\\.SetUpSuite\n\n"+ "fixture_test\\.go:[0-9]+:\n"+ " c\\.Assert\\(false, Equals, true\\)\n"+ "\\.+ obtained bool = false\n"+ "\\.+ expected bool = true\n\n") c.Assert(helper.completed, Equals, false) } // ----------------------------------------------------------------------- // Verify that logging within SetUpTest() persists within the test log itself. type FixtureLogHelper struct { c *C } func (s *FixtureLogHelper) SetUpTest(c *C) { s.c = c c.Log("1") } func (s *FixtureLogHelper) Test(c *C) { c.Log("2") s.c.Log("3") c.Log("4") c.Fail() } func (s *FixtureLogHelper) TearDownTest(c *C) { s.c.Log("5") } func (s *FixtureS) TestFixtureLogging(c *C) { helper := FixtureLogHelper{} output := String{} Run(&helper, &RunConf{Output: &output}) c.Assert(output.value, Matches, "\n---+\n"+ "FAIL: fixture_test\\.go:[0-9]+: "+ "FixtureLogHelper\\.Test\n\n"+ "1\n2\n3\n4\n5\n") } // ----------------------------------------------------------------------- // Skip() within fixture methods. func (s *FixtureS) TestSkipSuite(c *C) { helper := FixtureHelper{skip: true, skipOnN: 0} output := String{} result := Run(&helper, &RunConf{Output: &output}) c.Assert(output.value, Equals, "") c.Assert(helper.calls[0], Equals, "SetUpSuite") c.Assert(helper.calls[1], Equals, "TearDownSuite") c.Assert(len(helper.calls), Equals, 2) c.Assert(result.Skipped, Equals, 2) } func (s *FixtureS) TestSkipTest(c *C) { helper := FixtureHelper{skip: true, skipOnN: 1} output := String{} result := Run(&helper, &RunConf{Output: &output}) c.Assert(helper.calls[0], Equals, "SetUpSuite") c.Assert(helper.calls[1], Equals, "SetUpTest") c.Assert(helper.calls[2], Equals, "SetUpTest") c.Assert(helper.calls[3], Equals, "Test2") c.Assert(helper.calls[4], Equals, "TearDownTest") c.Assert(helper.calls[5], Equals, "TearDownSuite") c.Assert(len(helper.calls), Equals, 6) c.Assert(result.Skipped, Equals, 1) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/checkers.go����������������������������������������������0000644�0000153�0000161�00000030341�12321735711�022501� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gocheck import ( "fmt" "reflect" "regexp" ) // ----------------------------------------------------------------------- // CommentInterface and Commentf helper, to attach extra information to checks. type comment struct { format string args []interface{} } // Commentf returns an infomational value to use with Assert or Check calls. // If the checker test fails, the provided arguments will be passed to // fmt.Sprintf, and will be presented next to the logged failure. // // For example: // // c.Assert(v, Equals, 42, Commentf("Iteration #%d failed.", i)) // // Note that if the comment is constant, a better option is to // simply use a normal comment right above or next to the line, as // it will also get printed with any errors: // // c.Assert(l, Equals, 8192) // Ensure buffer size is correct (bug #123) // func Commentf(format string, args ...interface{}) CommentInterface { return &comment{format, args} } // CommentInterface must be implemented by types that attach extra // information to failed checks. See the Commentf function for details. type CommentInterface interface { CheckCommentString() string } func (c *comment) CheckCommentString() string { return fmt.Sprintf(c.format, c.args...) } // ----------------------------------------------------------------------- // The Checker interface. // The Checker interface must be provided by checkers used with // the Assert and Check verification methods. type Checker interface { Info() *CheckerInfo Check(params []interface{}, names []string) (result bool, error string) } // See the Checker interface. type CheckerInfo struct { Name string Params []string } func (info *CheckerInfo) Info() *CheckerInfo { return info } // ----------------------------------------------------------------------- // Not checker logic inverter. // The Not checker inverts the logic of the provided checker. The // resulting checker will succeed where the original one failed, and // vice-versa. // // For example: // // c.Assert(a, Not(Equals), b) // func Not(checker Checker) Checker { return &notChecker{checker} } type notChecker struct { sub Checker } func (checker *notChecker) Info() *CheckerInfo { info := *checker.sub.Info() info.Name = "Not(" + info.Name + ")" return &info } func (checker *notChecker) Check(params []interface{}, names []string) (result bool, error string) { result, error = checker.sub.Check(params, names) result = !result return } // ----------------------------------------------------------------------- // IsNil checker. type isNilChecker struct { *CheckerInfo } // The IsNil checker tests whether the obtained value is nil. // // For example: // // c.Assert(err, IsNil) // var IsNil Checker = &isNilChecker{ &CheckerInfo{Name: "IsNil", Params: []string{"value"}}, } func (checker *isNilChecker) Check(params []interface{}, names []string) (result bool, error string) { return isNil(params[0]), "" } func isNil(obtained interface{}) (result bool) { if obtained == nil { result = true } else { switch v := reflect.ValueOf(obtained); v.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: return v.IsNil() } } return } // ----------------------------------------------------------------------- // NotNil checker. Alias for Not(IsNil), since it's so common. type notNilChecker struct { *CheckerInfo } // The NotNil checker verifies that the obtained value is not nil. // // For example: // // c.Assert(iface, NotNil) // // This is an alias for Not(IsNil), made available since it's a // fairly common check. // var NotNil Checker = &notNilChecker{ &CheckerInfo{Name: "NotNil", Params: []string{"value"}}, } func (checker *notNilChecker) Check(params []interface{}, names []string) (result bool, error string) { return !isNil(params[0]), "" } // ----------------------------------------------------------------------- // Equals checker. type equalsChecker struct { *CheckerInfo } // The Equals checker verifies that the obtained value is equal to // the expected value, according to usual Go semantics for ==. // // For example: // // c.Assert(value, Equals, 42) // var Equals Checker = &equalsChecker{ &CheckerInfo{Name: "Equals", Params: []string{"obtained", "expected"}}, } func (checker *equalsChecker) Check(params []interface{}, names []string) (result bool, error string) { defer func() { if v := recover(); v != nil { result = false error = fmt.Sprint(v) } }() return params[0] == params[1], "" } // ----------------------------------------------------------------------- // DeepEquals checker. type deepEqualsChecker struct { *CheckerInfo } // The DeepEquals checker verifies that the obtained value is deep-equal to // the expected value. The check will work correctly even when facing // slices, interfaces, and values of different types (which always fail // the test). // // For example: // // c.Assert(value, DeepEquals, 42) // c.Assert(array, DeepEquals, []string{"hi", "there"}) // var DeepEquals Checker = &deepEqualsChecker{ &CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}}, } func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) { return reflect.DeepEqual(params[0], params[1]), "" } // ----------------------------------------------------------------------- // HasLen checker. type hasLenChecker struct { *CheckerInfo } // The HasLen checker verifies that the obtained value has the // provided length. In many cases this is superior to using Equals // in conjuction with the len function because in case the check // fails the value itself will be printed, instead of its length, // providing more details for figuring the problem. // // For example: // // c.Assert(list, HasLen, 5) // var HasLen Checker = &hasLenChecker{ &CheckerInfo{Name: "HasLen", Params: []string{"obtained", "n"}}, } func (checker *hasLenChecker) Check(params []interface{}, names []string) (result bool, error string) { n, ok := params[1].(int) if !ok { return false, "n must be an int" } value := reflect.ValueOf(params[0]) switch value.Kind() { case reflect.Map, reflect.Array, reflect.Slice, reflect.Chan, reflect.String: default: return false, "obtained value type has no length" } return value.Len() == n, "" } // ----------------------------------------------------------------------- // ErrorMatches checker. type errorMatchesChecker struct { *CheckerInfo } // The ErrorMatches checker verifies that the error value // is non nil and matches the regular expression provided. // // For example: // // c.Assert(err, ErrorMatches, "perm.*denied") // var ErrorMatches Checker = errorMatchesChecker{ &CheckerInfo{Name: "ErrorMatches", Params: []string{"value", "regex"}}, } func (checker errorMatchesChecker) Check(params []interface{}, names []string) (result bool, errStr string) { if params[0] == nil { return false, "Error value is nil" } err, ok := params[0].(error) if !ok { return false, "Value is not an error" } params[0] = err.Error() names[0] = "error" return matches(params[0], params[1]) } // ----------------------------------------------------------------------- // Matches checker. type matchesChecker struct { *CheckerInfo } // The Matches checker verifies that the string provided as the obtained // value (or the string resulting from obtained.String()) matches the // regular expression provided. // // For example: // // c.Assert(err, Matches, "perm.*denied") // var Matches Checker = &matchesChecker{ &CheckerInfo{Name: "Matches", Params: []string{"value", "regex"}}, } func (checker *matchesChecker) Check(params []interface{}, names []string) (result bool, error string) { return matches(params[0], params[1]) } func matches(value, regex interface{}) (result bool, error string) { reStr, ok := regex.(string) if !ok { return false, "Regex must be a string" } valueStr, valueIsStr := value.(string) if !valueIsStr { if valueWithStr, valueHasStr := value.(fmt.Stringer); valueHasStr { valueStr, valueIsStr = valueWithStr.String(), true } } if valueIsStr { matches, err := regexp.MatchString("^"+reStr+"$", valueStr) if err != nil { return false, "Can't compile regex: " + err.Error() } return matches, "" } return false, "Obtained value is not a string and has no .String()" } // ----------------------------------------------------------------------- // Panics checker. type panicsChecker struct { *CheckerInfo } // The Panics checker verifies that calling the provided zero-argument // function will cause a panic which is deep-equal to the provided value. // // For example: // // c.Assert(func() { f(1, 2) }, Panics, &SomeErrorType{"BOOM"}). // // var Panics Checker = &panicsChecker{ &CheckerInfo{Name: "Panics", Params: []string{"function", "expected"}}, } func (checker *panicsChecker) Check(params []interface{}, names []string) (result bool, error string) { f := reflect.ValueOf(params[0]) if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { return false, "Function must take zero arguments" } defer func() { // If the function has not panicked, then don't do the check. if error != "" { return } params[0] = recover() names[0] = "panic" result = reflect.DeepEqual(params[0], params[1]) }() f.Call(nil) return false, "Function has not panicked" } type panicMatchesChecker struct { *CheckerInfo } // The PanicMatches checker verifies that calling the provided zero-argument // function will cause a panic with an error value matching // the regular expression provided. // // For example: // // c.Assert(func() { f(1, 2) }, PanicMatches, `open.*: no such file or directory`). // // var PanicMatches Checker = &panicMatchesChecker{ &CheckerInfo{Name: "PanicMatches", Params: []string{"function", "expected"}}, } func (checker *panicMatchesChecker) Check(params []interface{}, names []string) (result bool, errmsg string) { f := reflect.ValueOf(params[0]) if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { return false, "Function must take zero arguments" } defer func() { // If the function has not panicked, then don't do the check. if errmsg != "" { return } obtained := recover() names[0] = "panic" if e, ok := obtained.(error); ok { params[0] = e.Error() } else if _, ok := obtained.(string); ok { params[0] = obtained } else { errmsg = "Panic value is not a string or an error" return } result, errmsg = matches(params[0], params[1]) }() f.Call(nil) return false, "Function has not panicked" } // ----------------------------------------------------------------------- // FitsTypeOf checker. type fitsTypeChecker struct { *CheckerInfo } // The FitsTypeOf checker verifies that the obtained value is // assignable to a variable with the same type as the provided // sample value. // // For example: // // c.Assert(value, FitsTypeOf, int64(0)) // c.Assert(value, FitsTypeOf, os.Error(nil)) // var FitsTypeOf Checker = &fitsTypeChecker{ &CheckerInfo{Name: "FitsTypeOf", Params: []string{"obtained", "sample"}}, } func (checker *fitsTypeChecker) Check(params []interface{}, names []string) (result bool, error string) { obtained := reflect.ValueOf(params[0]) sample := reflect.ValueOf(params[1]) if !obtained.IsValid() { return false, "" } if !sample.IsValid() { return false, "Invalid sample value" } return obtained.Type().AssignableTo(sample.Type()), "" } // ----------------------------------------------------------------------- // Implements checker. type implementsChecker struct { *CheckerInfo } // The Implements checker verifies that the obtained value // implements the interface specified via a pointer to an interface // variable. // // For example: // // var e os.Error // c.Assert(err, Implements, &e) // var Implements Checker = &implementsChecker{ &CheckerInfo{Name: "Implements", Params: []string{"obtained", "ifaceptr"}}, } func (checker *implementsChecker) Check(params []interface{}, names []string) (result bool, error string) { obtained := reflect.ValueOf(params[0]) ifaceptr := reflect.ValueOf(params[1]) if !obtained.IsValid() { return false, "" } if !ifaceptr.IsValid() || ifaceptr.Kind() != reflect.Ptr || ifaceptr.Elem().Kind() != reflect.Interface { return false, "ifaceptr should be a pointer to an interface variable" } return obtained.Type().Implements(ifaceptr.Elem().Type()), "" } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/LICENSE��������������������������������������������������0000644�0000153�0000161�00000003066�12321736015�021372� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gocheck - A rich testing framework for Go Copyright (c) 2010, Gustavo Niemeyer <gustavo@niemeyer.net> All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/export_test.go�������������������������������������������0000644�0000153�0000161�00000000261�12321735711�023270� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gocheck func PrintLine(filename string, line int) (string, error) { return printLine(filename, line) } func Indent(s, with string) string { return indent(s, with) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/TODO�����������������������������������������������������0000644�0000153�0000161�00000000070�12321735711�021047� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������- Assert(slice, Contains, item) - Parallel test support ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/helpers.go�����������������������������������������������0000644�0000153�0000161�00000016157�12321736015�022363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gocheck import ( "fmt" "strings" "time" ) // ----------------------------------------------------------------------- // Basic succeeding/failing logic. // Return true if the currently running test has already failed. func (c *C) Failed() bool { return c.status == failedSt } // Mark the currently running test as failed. Something ought to have been // previously logged so that the developer knows what went wrong. The higher // level helper functions will fail the test and do the logging properly. func (c *C) Fail() { c.status = failedSt } // Mark the currently running test as failed, and stop running the test. // Something ought to have been previously logged so that the developer // knows what went wrong. The higher level helper functions will fail the // test and do the logging properly. func (c *C) FailNow() { c.Fail() c.stopNow() } // Mark the currently running test as succeeded, undoing any previous // failures. func (c *C) Succeed() { c.status = succeededSt } // Mark the currently running test as succeeded, undoing any previous // failures, and stop running the test. func (c *C) SucceedNow() { c.Succeed() c.stopNow() } // Expect the currently running test to fail, for the given reason. If the // test does not fail, an error will be reported to raise the attention to // this fact. The reason string is just a summary of why the given test is // supposed to fail. This method is useful to temporarily disable tests // which cover well known problems until a better time to fix the problem // is found, without forgetting about the fact that a failure still exists. func (c *C) ExpectFailure(reason string) { if reason == "" { panic("Missing reason why the test is expected to fail") } c.mustFail = true c.reason = reason } // Skip the running test, for the given reason. If used within SetUpTest, // the individual test being set up will be skipped, and in SetUpSuite it // will cause the whole suite to be skipped. func (c *C) Skip(reason string) { if reason == "" { panic("Missing reason why the test is being skipped") } c.reason = reason c.status = skippedSt c.stopNow() } // ----------------------------------------------------------------------- // Basic logging. // Return the current test error output. func (c *C) GetTestLog() string { return c.logb.String() } // Log some information into the test error output. The provided arguments // will be assembled together into a string using fmt.Sprint(). func (c *C) Log(args ...interface{}) { c.log(args...) } // Log some information into the test error output. The provided arguments // will be assembled together into a string using fmt.Sprintf(). func (c *C) Logf(format string, args ...interface{}) { c.logf(format, args...) } // Output enables *C to be used as a logger in functions that require only // the minimum interface of *log.Logger. func (c *C) Output(calldepth int, s string) error { ns := time.Now().Sub(time.Time{}).Nanoseconds() t := float64(ns%100e9) / 1e9 c.Logf("[LOG] %.05f %s", t, s) return nil } // Log an error into the test error output, and mark the test as failed. // The provided arguments will be assembled together into a string using // fmt.Sprint(). func (c *C) Error(args ...interface{}) { c.logCaller(1) c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...))) c.logNewLine() c.Fail() } // Log an error into the test error output, and mark the test as failed. // The provided arguments will be assembled together into a string using // fmt.Sprintf(). func (c *C) Errorf(format string, args ...interface{}) { c.logCaller(1) c.logString(fmt.Sprintf("Error: "+format, args...)) c.logNewLine() c.Fail() } // Log an error into the test error output, mark the test as failed, and // stop the test execution. The provided arguments will be assembled // together into a string using fmt.Sprint(). func (c *C) Fatal(args ...interface{}) { c.logCaller(1) c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...))) c.logNewLine() c.FailNow() } // Log an error into the test error output, mark the test as failed, and // stop the test execution. The provided arguments will be assembled // together into a string using fmt.Sprintf(). func (c *C) Fatalf(format string, args ...interface{}) { c.logCaller(1) c.logString(fmt.Sprint("Error: ", fmt.Sprintf(format, args...))) c.logNewLine() c.FailNow() } // ----------------------------------------------------------------------- // Generic checks and assertions based on checkers. // Verify if the first value matches with the expected value. What // matching means is defined by the provided checker. In case they do not // match, an error will be logged, the test will be marked as failed, and // the test execution will continue. Some checkers may not need the expected // argument (e.g. IsNil). In either case, any extra arguments provided to // the function will be logged next to the reported problem when the // matching fails. This is a handy way to provide problem-specific hints. func (c *C) Check(obtained interface{}, checker Checker, args ...interface{}) bool { return c.internalCheck("Check", obtained, checker, args...) } // Ensure that the first value matches with the expected value. What // matching means is defined by the provided checker. In case they do not // match, an error will be logged, the test will be marked as failed, and // the test execution will stop. Some checkers may not need the expected // argument (e.g. IsNil). In either case, any extra arguments provided to // the function will be logged next to the reported problem when the // matching fails. This is a handy way to provide problem-specific hints. func (c *C) Assert(obtained interface{}, checker Checker, args ...interface{}) { if !c.internalCheck("Assert", obtained, checker, args...) { c.stopNow() } } func (c *C) internalCheck(funcName string, obtained interface{}, checker Checker, args ...interface{}) bool { if checker == nil { c.logCaller(2) c.logString(fmt.Sprintf("%s(obtained, nil!?, ...):", funcName)) c.logString("Oops.. you've provided a nil checker!") c.logNewLine() c.Fail() return false } // If the last argument is a bug info, extract it out. var comment CommentInterface if len(args) > 0 { if c, ok := args[len(args)-1].(CommentInterface); ok { comment = c args = args[:len(args)-1] } } params := append([]interface{}{obtained}, args...) info := checker.Info() if len(params) != len(info.Params) { names := append([]string{info.Params[0], info.Name}, info.Params[1:]...) c.logCaller(2) c.logString(fmt.Sprintf("%s(%s):", funcName, strings.Join(names, ", "))) c.logString(fmt.Sprintf("Wrong number of parameters for %s: want %d, got %d", info.Name, len(names), len(params)+1)) c.logNewLine() c.Fail() return false } // Copy since it may be mutated by Check. names := append([]string{}, info.Params...) // Do the actual check. result, error := checker.Check(params, names) if !result || error != "" { c.logCaller(2) for i := 0; i != len(params); i++ { c.logValue(names[i], params[i]) } if comment != nil { c.logString(comment.CheckCommentString()) } if error != "" { c.logString(error) } c.logNewLine() c.Fail() return false } return true } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/bootstrap_test.go����������������������������������������0000644�0000153�0000161�00000004302�12321736015�023762� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// These initial tests are for bootstrapping. They verify that we can // basically use the testing infrastructure itself to check if the test // system is working. // // These tests use will break down the test runner badly in case of // errors because if they simply fail, we can't be sure the developer // will ever see anything (because failing means the failing system // somehow isn't working! :-) // // Do not assume *any* internal functionality works as expected besides // what's actually tested here. package gocheck_test import ( "fmt" "launchpad.net/gocheck" "strings" ) type BootstrapS struct{} var boostrapS = gocheck.Suite(&BootstrapS{}) func (s *BootstrapS) TestCountSuite(c *gocheck.C) { suitesRun += 1 } func (s *BootstrapS) TestFailedAndFail(c *gocheck.C) { if c.Failed() { critical("c.Failed() must be false first!") } c.Fail() if !c.Failed() { critical("c.Fail() didn't put the test in a failed state!") } c.Succeed() } func (s *BootstrapS) TestFailedAndSucceed(c *gocheck.C) { c.Fail() c.Succeed() if c.Failed() { critical("c.Succeed() didn't put the test back in a non-failed state") } } func (s *BootstrapS) TestLogAndGetTestLog(c *gocheck.C) { c.Log("Hello there!") log := c.GetTestLog() if log != "Hello there!\n" { critical(fmt.Sprintf("Log() or GetTestLog() is not working! Got: %#v", log)) } } func (s *BootstrapS) TestLogfAndGetTestLog(c *gocheck.C) { c.Logf("Hello %v", "there!") log := c.GetTestLog() if log != "Hello there!\n" { critical(fmt.Sprintf("Logf() or GetTestLog() is not working! Got: %#v", log)) } } func (s *BootstrapS) TestRunShowsErrors(c *gocheck.C) { output := String{} gocheck.Run(&FailHelper{}, &gocheck.RunConf{Output: &output}) if strings.Index(output.value, "Expected failure!") == -1 { critical(fmt.Sprintf("RunWithWriter() output did not contain the "+ "expected failure! Got: %#v", output.value)) } } func (s *BootstrapS) TestRunDoesntShowSuccesses(c *gocheck.C) { output := String{} gocheck.Run(&SuccessHelper{}, &gocheck.RunConf{Output: &output}) if strings.Index(output.value, "Expected success!") != -1 { critical(fmt.Sprintf("RunWithWriter() output contained a successful "+ "test! Got: %#v", output.value)) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/helpers_test.go������������������������������������������0000644�0000153�0000161�00000037237�12321735711�023426� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// These tests verify the inner workings of the helper methods associated // with gocheck.T. package gocheck_test import ( "launchpad.net/gocheck" "os" "reflect" "runtime" "sync" ) var helpersS = gocheck.Suite(&HelpersS{}) type HelpersS struct{} func (s *HelpersS) TestCountSuite(c *gocheck.C) { suitesRun += 1 } // ----------------------------------------------------------------------- // Fake checker and bug info to verify the behavior of Assert() and Check(). type MyChecker struct { info *gocheck.CheckerInfo params []interface{} names []string result bool error string } func (checker *MyChecker) Info() *gocheck.CheckerInfo { if checker.info == nil { return &gocheck.CheckerInfo{Name: "MyChecker", Params: []string{"myobtained", "myexpected"}} } return checker.info } func (checker *MyChecker) Check(params []interface{}, names []string) (bool, string) { rparams := checker.params rnames := checker.names checker.params = append([]interface{}{}, params...) checker.names = append([]string{}, names...) if rparams != nil { copy(params, rparams) } if rnames != nil { copy(names, rnames) } return checker.result, checker.error } type myCommentType string func (c myCommentType) CheckCommentString() string { return string(c) } func myComment(s string) myCommentType { return myCommentType(s) } // ----------------------------------------------------------------------- // Ensure a real checker actually works fine. func (s *HelpersS) TestCheckerInterface(c *gocheck.C) { testHelperSuccess(c, "Check(1, Equals, 1)", true, func() interface{} { return c.Check(1, gocheck.Equals, 1) }) } // ----------------------------------------------------------------------- // Tests for Check(), mostly the same as for Assert() following these. func (s *HelpersS) TestCheckSucceedWithExpected(c *gocheck.C) { checker := &MyChecker{result: true} testHelperSuccess(c, "Check(1, checker, 2)", true, func() interface{} { return c.Check(1, checker, 2) }) if !reflect.DeepEqual(checker.params, []interface{}{1, 2}) { c.Fatalf("Bad params for check: %#v", checker.params) } } func (s *HelpersS) TestCheckSucceedWithoutExpected(c *gocheck.C) { checker := &MyChecker{result: true, info: &gocheck.CheckerInfo{Params: []string{"myvalue"}}} testHelperSuccess(c, "Check(1, checker)", true, func() interface{} { return c.Check(1, checker) }) if !reflect.DeepEqual(checker.params, []interface{}{1}) { c.Fatalf("Bad params for check: %#v", checker.params) } } func (s *HelpersS) TestCheckFailWithExpected(c *gocheck.C) { checker := &MyChecker{result: false} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " return c\\.Check\\(1, checker, 2\\)\n" + "\\.+ myobtained int = 1\n" + "\\.+ myexpected int = 2\n\n" testHelperFailure(c, "Check(1, checker, 2)", false, false, log, func() interface{} { return c.Check(1, checker, 2) }) } func (s *HelpersS) TestCheckFailWithExpectedAndComment(c *gocheck.C) { checker := &MyChecker{result: false} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " return c\\.Check\\(1, checker, 2, myComment\\(\"Hello world!\"\\)\\)\n" + "\\.+ myobtained int = 1\n" + "\\.+ myexpected int = 2\n" + "\\.+ Hello world!\n\n" testHelperFailure(c, "Check(1, checker, 2, msg)", false, false, log, func() interface{} { return c.Check(1, checker, 2, myComment("Hello world!")) }) } func (s *HelpersS) TestCheckFailWithExpectedAndStaticComment(c *gocheck.C) { checker := &MyChecker{result: false} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " // Nice leading comment\\.\n" + " return c\\.Check\\(1, checker, 2\\) // Hello there\n" + "\\.+ myobtained int = 1\n" + "\\.+ myexpected int = 2\n\n" testHelperFailure(c, "Check(1, checker, 2, msg)", false, false, log, func() interface{} { // Nice leading comment. return c.Check(1, checker, 2) // Hello there }) } func (s *HelpersS) TestCheckFailWithoutExpected(c *gocheck.C) { checker := &MyChecker{result: false, info: &gocheck.CheckerInfo{Params: []string{"myvalue"}}} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " return c\\.Check\\(1, checker\\)\n" + "\\.+ myvalue int = 1\n\n" testHelperFailure(c, "Check(1, checker)", false, false, log, func() interface{} { return c.Check(1, checker) }) } func (s *HelpersS) TestCheckFailWithoutExpectedAndMessage(c *gocheck.C) { checker := &MyChecker{result: false, info: &gocheck.CheckerInfo{Params: []string{"myvalue"}}} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " return c\\.Check\\(1, checker, myComment\\(\"Hello world!\"\\)\\)\n" + "\\.+ myvalue int = 1\n" + "\\.+ Hello world!\n\n" testHelperFailure(c, "Check(1, checker, msg)", false, false, log, func() interface{} { return c.Check(1, checker, myComment("Hello world!")) }) } func (s *HelpersS) TestCheckWithMissingExpected(c *gocheck.C) { checker := &MyChecker{result: true} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " return c\\.Check\\(1, checker\\)\n" + "\\.+ Check\\(myobtained, MyChecker, myexpected\\):\n" + "\\.+ Wrong number of parameters for MyChecker: " + "want 3, got 2\n\n" testHelperFailure(c, "Check(1, checker, !?)", false, false, log, func() interface{} { return c.Check(1, checker) }) } func (s *HelpersS) TestCheckWithTooManyExpected(c *gocheck.C) { checker := &MyChecker{result: true} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " return c\\.Check\\(1, checker, 2, 3\\)\n" + "\\.+ Check\\(myobtained, MyChecker, myexpected\\):\n" + "\\.+ Wrong number of parameters for MyChecker: " + "want 3, got 4\n\n" testHelperFailure(c, "Check(1, checker, 2, 3)", false, false, log, func() interface{} { return c.Check(1, checker, 2, 3) }) } func (s *HelpersS) TestCheckWithError(c *gocheck.C) { checker := &MyChecker{result: false, error: "Some not so cool data provided!"} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " return c\\.Check\\(1, checker, 2\\)\n" + "\\.+ myobtained int = 1\n" + "\\.+ myexpected int = 2\n" + "\\.+ Some not so cool data provided!\n\n" testHelperFailure(c, "Check(1, checker, 2)", false, false, log, func() interface{} { return c.Check(1, checker, 2) }) } func (s *HelpersS) TestCheckWithNilChecker(c *gocheck.C) { log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " return c\\.Check\\(1, nil\\)\n" + "\\.+ Check\\(obtained, nil!\\?, \\.\\.\\.\\):\n" + "\\.+ Oops\\.\\. you've provided a nil checker!\n\n" testHelperFailure(c, "Check(obtained, nil)", false, false, log, func() interface{} { return c.Check(1, nil) }) } func (s *HelpersS) TestCheckWithParamsAndNamesMutation(c *gocheck.C) { checker := &MyChecker{result: false, params: []interface{}{3, 4}, names: []string{"newobtained", "newexpected"}} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " return c\\.Check\\(1, checker, 2\\)\n" + "\\.+ newobtained int = 3\n" + "\\.+ newexpected int = 4\n\n" testHelperFailure(c, "Check(1, checker, 2) with mutation", false, false, log, func() interface{} { return c.Check(1, checker, 2) }) } // ----------------------------------------------------------------------- // Tests for Assert(), mostly the same as for Check() above. func (s *HelpersS) TestAssertSucceedWithExpected(c *gocheck.C) { checker := &MyChecker{result: true} testHelperSuccess(c, "Assert(1, checker, 2)", nil, func() interface{} { c.Assert(1, checker, 2) return nil }) if !reflect.DeepEqual(checker.params, []interface{}{1, 2}) { c.Fatalf("Bad params for check: %#v", checker.params) } } func (s *HelpersS) TestAssertSucceedWithoutExpected(c *gocheck.C) { checker := &MyChecker{result: true, info: &gocheck.CheckerInfo{Params: []string{"myvalue"}}} testHelperSuccess(c, "Assert(1, checker)", nil, func() interface{} { c.Assert(1, checker) return nil }) if !reflect.DeepEqual(checker.params, []interface{}{1}) { c.Fatalf("Bad params for check: %#v", checker.params) } } func (s *HelpersS) TestAssertFailWithExpected(c *gocheck.C) { checker := &MyChecker{result: false} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " c\\.Assert\\(1, checker, 2\\)\n" + "\\.+ myobtained int = 1\n" + "\\.+ myexpected int = 2\n\n" testHelperFailure(c, "Assert(1, checker, 2)", nil, true, log, func() interface{} { c.Assert(1, checker, 2) return nil }) } func (s *HelpersS) TestAssertFailWithExpectedAndMessage(c *gocheck.C) { checker := &MyChecker{result: false} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " c\\.Assert\\(1, checker, 2, myComment\\(\"Hello world!\"\\)\\)\n" + "\\.+ myobtained int = 1\n" + "\\.+ myexpected int = 2\n" + "\\.+ Hello world!\n\n" testHelperFailure(c, "Assert(1, checker, 2, msg)", nil, true, log, func() interface{} { c.Assert(1, checker, 2, myComment("Hello world!")) return nil }) } func (s *HelpersS) TestAssertFailWithoutExpected(c *gocheck.C) { checker := &MyChecker{result: false, info: &gocheck.CheckerInfo{Params: []string{"myvalue"}}} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " c\\.Assert\\(1, checker\\)\n" + "\\.+ myvalue int = 1\n\n" testHelperFailure(c, "Assert(1, checker)", nil, true, log, func() interface{} { c.Assert(1, checker) return nil }) } func (s *HelpersS) TestAssertFailWithoutExpectedAndMessage(c *gocheck.C) { checker := &MyChecker{result: false, info: &gocheck.CheckerInfo{Params: []string{"myvalue"}}} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " c\\.Assert\\(1, checker, myComment\\(\"Hello world!\"\\)\\)\n" + "\\.+ myvalue int = 1\n" + "\\.+ Hello world!\n\n" testHelperFailure(c, "Assert(1, checker, msg)", nil, true, log, func() interface{} { c.Assert(1, checker, myComment("Hello world!")) return nil }) } func (s *HelpersS) TestAssertWithMissingExpected(c *gocheck.C) { checker := &MyChecker{result: true} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " c\\.Assert\\(1, checker\\)\n" + "\\.+ Assert\\(myobtained, MyChecker, myexpected\\):\n" + "\\.+ Wrong number of parameters for MyChecker: " + "want 3, got 2\n\n" testHelperFailure(c, "Assert(1, checker, !?)", nil, true, log, func() interface{} { c.Assert(1, checker) return nil }) } func (s *HelpersS) TestAssertWithError(c *gocheck.C) { checker := &MyChecker{result: false, error: "Some not so cool data provided!"} log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " c\\.Assert\\(1, checker, 2\\)\n" + "\\.+ myobtained int = 1\n" + "\\.+ myexpected int = 2\n" + "\\.+ Some not so cool data provided!\n\n" testHelperFailure(c, "Assert(1, checker, 2)", nil, true, log, func() interface{} { c.Assert(1, checker, 2) return nil }) } func (s *HelpersS) TestAssertWithNilChecker(c *gocheck.C) { log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + " c\\.Assert\\(1, nil\\)\n" + "\\.+ Assert\\(obtained, nil!\\?, \\.\\.\\.\\):\n" + "\\.+ Oops\\.\\. you've provided a nil checker!\n\n" testHelperFailure(c, "Assert(obtained, nil)", nil, true, log, func() interface{} { c.Assert(1, nil) return nil }) } // ----------------------------------------------------------------------- // Ensure that values logged work properly in some interesting cases. func (s *HelpersS) TestValueLoggingWithArrays(c *gocheck.C) { checker := &MyChecker{result: false} log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + " return c\\.Check\\(\\[\\]byte{1, 2}, checker, \\[\\]byte{1, 3}\\)\n" + "\\.+ myobtained \\[\\]uint8 = \\[\\]byte{0x1, 0x2}\n" + "\\.+ myexpected \\[\\]uint8 = \\[\\]byte{0x1, 0x3}\n\n" testHelperFailure(c, "Check([]byte{1}, chk, []byte{3})", false, false, log, func() interface{} { return c.Check([]byte{1, 2}, checker, []byte{1, 3}) }) } func (s *HelpersS) TestValueLoggingWithMultiLine(c *gocheck.C) { checker := &MyChecker{result: false} log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + " return c\\.Check\\(\"a\\\\nb\\\\n\", checker, \"a\\\\nb\\\\nc\"\\)\n" + "\\.+ myobtained string = \"\" \\+\n" + "\\.+ \"a\\\\n\" \\+\n" + "\\.+ \"b\\\\n\"\n" + "\\.+ myexpected string = \"\" \\+\n" + "\\.+ \"a\\\\n\" \\+\n" + "\\.+ \"b\\\\n\" \\+\n" + "\\.+ \"c\"\n\n" testHelperFailure(c, `Check("a\nb\n", chk, "a\nb\nc")`, false, false, log, func() interface{} { return c.Check("a\nb\n", checker, "a\nb\nc") }) } func (s *HelpersS) TestValueLoggingWithMultiLineException(c *gocheck.C) { // If the newline is at the end of the string, don't log as multi-line. checker := &MyChecker{result: false} log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + " return c\\.Check\\(\"a b\\\\n\", checker, \"a\\\\nb\"\\)\n" + "\\.+ myobtained string = \"a b\\\\n\"\n" + "\\.+ myexpected string = \"\" \\+\n" + "\\.+ \"a\\\\n\" \\+\n" + "\\.+ \"b\"\n\n" testHelperFailure(c, `Check("a b\n", chk, "a\nb")`, false, false, log, func() interface{} { return c.Check("a b\n", checker, "a\nb") }) } // ----------------------------------------------------------------------- // MakeDir() tests. type MkDirHelper struct { path1 string path2 string isDir1 bool isDir2 bool isDir3 bool isDir4 bool } func (s *MkDirHelper) SetUpSuite(c *gocheck.C) { s.path1 = c.MkDir() s.isDir1 = isDir(s.path1) } func (s *MkDirHelper) Test(c *gocheck.C) { s.path2 = c.MkDir() s.isDir2 = isDir(s.path2) } func (s *MkDirHelper) TearDownSuite(c *gocheck.C) { s.isDir3 = isDir(s.path1) s.isDir4 = isDir(s.path2) } func (s *HelpersS) TestMkDir(c *gocheck.C) { helper := MkDirHelper{} output := String{} gocheck.Run(&helper, &gocheck.RunConf{Output: &output}) c.Assert(output.value, gocheck.Equals, "") c.Check(helper.isDir1, gocheck.Equals, true) c.Check(helper.isDir2, gocheck.Equals, true) c.Check(helper.isDir3, gocheck.Equals, true) c.Check(helper.isDir4, gocheck.Equals, true) c.Check(helper.path1, gocheck.Not(gocheck.Equals), helper.path2) c.Check(isDir(helper.path1), gocheck.Equals, false) c.Check(isDir(helper.path2), gocheck.Equals, false) } func isDir(path string) bool { if stat, err := os.Stat(path); err == nil { return stat.IsDir() } return false } // Concurrent logging should not corrupt the underling buffer. // Use go test -race to detect the race in this test. func (s *HelpersS) TestConcurrentLogging(c *gocheck.C) { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU())) var start, stop sync.WaitGroup start.Add(1) for i, n := 0, runtime.NumCPU()*2; i < n; i++ { stop.Add(1) go func(i int) { start.Wait() for j := 0; j < 30; j++ { c.Logf("Worker %d: line %d", i, j) } stop.Done() }(i) } start.Done() stop.Wait() } // ----------------------------------------------------------------------- // A couple of helper functions to test helper functions. :-) func testHelperSuccess(c *gocheck.C, name string, expectedResult interface{}, closure func() interface{}) { var result interface{} defer (func() { if err := recover(); err != nil { panic(err) } checkState(c, result, &expectedState{ name: name, result: expectedResult, failed: false, log: "", }) })() result = closure() } func testHelperFailure(c *gocheck.C, name string, expectedResult interface{}, shouldStop bool, log string, closure func() interface{}) { var result interface{} defer (func() { if err := recover(); err != nil { panic(err) } checkState(c, result, &expectedState{ name: name, result: expectedResult, failed: true, log: log, }) })() result = closure() if shouldStop { c.Logf("%s didn't stop when it should", name) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/run_test.go����������������������������������������������0000644�0000153�0000161�00000027211�12321736015�022555� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// These tests verify the test running logic. package gocheck_test import ( "errors" . "launchpad.net/gocheck" "sync" ) var runnerS = Suite(&RunS{}) type RunS struct{} func (s *RunS) TestCountSuite(c *C) { suitesRun += 1 } // ----------------------------------------------------------------------- // Tests ensuring result counting works properly. func (s *RunS) TestSuccess(c *C) { output := String{} result := Run(&SuccessHelper{}, &RunConf{Output: &output}) c.Check(result.Succeeded, Equals, 1) c.Check(result.Failed, Equals, 0) c.Check(result.Skipped, Equals, 0) c.Check(result.Panicked, Equals, 0) c.Check(result.FixturePanicked, Equals, 0) c.Check(result.Missed, Equals, 0) c.Check(result.RunError, IsNil) } func (s *RunS) TestFailure(c *C) { output := String{} result := Run(&FailHelper{}, &RunConf{Output: &output}) c.Check(result.Succeeded, Equals, 0) c.Check(result.Failed, Equals, 1) c.Check(result.Skipped, Equals, 0) c.Check(result.Panicked, Equals, 0) c.Check(result.FixturePanicked, Equals, 0) c.Check(result.Missed, Equals, 0) c.Check(result.RunError, IsNil) } func (s *RunS) TestFixture(c *C) { output := String{} result := Run(&FixtureHelper{}, &RunConf{Output: &output}) c.Check(result.Succeeded, Equals, 2) c.Check(result.Failed, Equals, 0) c.Check(result.Skipped, Equals, 0) c.Check(result.Panicked, Equals, 0) c.Check(result.FixturePanicked, Equals, 0) c.Check(result.Missed, Equals, 0) c.Check(result.RunError, IsNil) } func (s *RunS) TestPanicOnTest(c *C) { output := String{} helper := &FixtureHelper{panicOn: "Test1"} result := Run(helper, &RunConf{Output: &output}) c.Check(result.Succeeded, Equals, 1) c.Check(result.Failed, Equals, 0) c.Check(result.Skipped, Equals, 0) c.Check(result.Panicked, Equals, 1) c.Check(result.FixturePanicked, Equals, 0) c.Check(result.Missed, Equals, 0) c.Check(result.RunError, IsNil) } func (s *RunS) TestPanicOnSetUpTest(c *C) { output := String{} helper := &FixtureHelper{panicOn: "SetUpTest"} result := Run(helper, &RunConf{Output: &output}) c.Check(result.Succeeded, Equals, 0) c.Check(result.Failed, Equals, 0) c.Check(result.Skipped, Equals, 0) c.Check(result.Panicked, Equals, 0) c.Check(result.FixturePanicked, Equals, 1) c.Check(result.Missed, Equals, 2) c.Check(result.RunError, IsNil) } func (s *RunS) TestPanicOnSetUpSuite(c *C) { output := String{} helper := &FixtureHelper{panicOn: "SetUpSuite"} result := Run(helper, &RunConf{Output: &output}) c.Check(result.Succeeded, Equals, 0) c.Check(result.Failed, Equals, 0) c.Check(result.Skipped, Equals, 0) c.Check(result.Panicked, Equals, 0) c.Check(result.FixturePanicked, Equals, 1) c.Check(result.Missed, Equals, 2) c.Check(result.RunError, IsNil) } // ----------------------------------------------------------------------- // Check result aggregation. func (s *RunS) TestAdd(c *C) { result := &Result{ Succeeded: 1, Skipped: 2, Failed: 3, Panicked: 4, FixturePanicked: 5, Missed: 6, ExpectedFailures: 7, } result.Add(&Result{ Succeeded: 10, Skipped: 20, Failed: 30, Panicked: 40, FixturePanicked: 50, Missed: 60, ExpectedFailures: 70, }) c.Check(result.Succeeded, Equals, 11) c.Check(result.Skipped, Equals, 22) c.Check(result.Failed, Equals, 33) c.Check(result.Panicked, Equals, 44) c.Check(result.FixturePanicked, Equals, 55) c.Check(result.Missed, Equals, 66) c.Check(result.ExpectedFailures, Equals, 77) c.Check(result.RunError, IsNil) } // ----------------------------------------------------------------------- // Check the Passed() method. func (s *RunS) TestPassed(c *C) { c.Assert((&Result{}).Passed(), Equals, true) c.Assert((&Result{Succeeded: 1}).Passed(), Equals, true) c.Assert((&Result{Skipped: 1}).Passed(), Equals, true) c.Assert((&Result{Failed: 1}).Passed(), Equals, false) c.Assert((&Result{Panicked: 1}).Passed(), Equals, false) c.Assert((&Result{FixturePanicked: 1}).Passed(), Equals, false) c.Assert((&Result{Missed: 1}).Passed(), Equals, false) c.Assert((&Result{RunError: errors.New("!")}).Passed(), Equals, false) } // ----------------------------------------------------------------------- // Check that result printing is working correctly. func (s *RunS) TestPrintSuccess(c *C) { result := &Result{Succeeded: 5} c.Check(result.String(), Equals, "OK: 5 passed") } func (s *RunS) TestPrintFailure(c *C) { result := &Result{Failed: 5} c.Check(result.String(), Equals, "OOPS: 0 passed, 5 FAILED") } func (s *RunS) TestPrintSkipped(c *C) { result := &Result{Skipped: 5} c.Check(result.String(), Equals, "OK: 0 passed, 5 skipped") } func (s *RunS) TestPrintExpectedFailures(c *C) { result := &Result{ExpectedFailures: 5} c.Check(result.String(), Equals, "OK: 0 passed, 5 expected failures") } func (s *RunS) TestPrintPanicked(c *C) { result := &Result{Panicked: 5} c.Check(result.String(), Equals, "OOPS: 0 passed, 5 PANICKED") } func (s *RunS) TestPrintFixturePanicked(c *C) { result := &Result{FixturePanicked: 5} c.Check(result.String(), Equals, "OOPS: 0 passed, 5 FIXTURE-PANICKED") } func (s *RunS) TestPrintMissed(c *C) { result := &Result{Missed: 5} c.Check(result.String(), Equals, "OOPS: 0 passed, 5 MISSED") } func (s *RunS) TestPrintAll(c *C) { result := &Result{Succeeded: 1, Skipped: 2, ExpectedFailures: 3, Panicked: 4, FixturePanicked: 5, Missed: 6} c.Check(result.String(), Equals, "OOPS: 1 passed, 2 skipped, 3 expected failures, 4 PANICKED, "+ "5 FIXTURE-PANICKED, 6 MISSED") } func (s *RunS) TestPrintRunError(c *C) { result := &Result{Succeeded: 1, Failed: 1, RunError: errors.New("Kaboom!")} c.Check(result.String(), Equals, "ERROR: Kaboom!") } // ----------------------------------------------------------------------- // Verify that the method pattern flag works correctly. func (s *RunS) TestFilterTestName(c *C) { helper := FixtureHelper{} output := String{} runConf := RunConf{Output: &output, Filter: "Test[91]"} Run(&helper, &runConf) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "Test1") c.Check(helper.calls[3], Equals, "TearDownTest") c.Check(helper.calls[4], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 5) } func (s *RunS) TestFilterTestNameWithAll(c *C) { helper := FixtureHelper{} output := String{} runConf := RunConf{Output: &output, Filter: ".*"} Run(&helper, &runConf) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "Test1") c.Check(helper.calls[3], Equals, "TearDownTest") c.Check(helper.calls[4], Equals, "SetUpTest") c.Check(helper.calls[5], Equals, "Test2") c.Check(helper.calls[6], Equals, "TearDownTest") c.Check(helper.calls[7], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 8) } func (s *RunS) TestFilterSuiteName(c *C) { helper := FixtureHelper{} output := String{} runConf := RunConf{Output: &output, Filter: "FixtureHelper"} Run(&helper, &runConf) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "Test1") c.Check(helper.calls[3], Equals, "TearDownTest") c.Check(helper.calls[4], Equals, "SetUpTest") c.Check(helper.calls[5], Equals, "Test2") c.Check(helper.calls[6], Equals, "TearDownTest") c.Check(helper.calls[7], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 8) } func (s *RunS) TestFilterSuiteNameAndTestName(c *C) { helper := FixtureHelper{} output := String{} runConf := RunConf{Output: &output, Filter: "FixtureHelper\\.Test2"} Run(&helper, &runConf) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "Test2") c.Check(helper.calls[3], Equals, "TearDownTest") c.Check(helper.calls[4], Equals, "TearDownSuite") c.Check(len(helper.calls), Equals, 5) } func (s *RunS) TestFilterAllOut(c *C) { helper := FixtureHelper{} output := String{} runConf := RunConf{Output: &output, Filter: "NotFound"} Run(&helper, &runConf) c.Check(len(helper.calls), Equals, 0) } func (s *RunS) TestRequirePartialMatch(c *C) { helper := FixtureHelper{} output := String{} runConf := RunConf{Output: &output, Filter: "est"} Run(&helper, &runConf) c.Check(len(helper.calls), Equals, 8) } func (s *RunS) TestFilterError(c *C) { helper := FixtureHelper{} output := String{} runConf := RunConf{Output: &output, Filter: "]["} result := Run(&helper, &runConf) c.Check(result.String(), Equals, "ERROR: Bad filter expression: error parsing regexp: missing closing ]: `[`") c.Check(len(helper.calls), Equals, 0) } // ----------------------------------------------------------------------- // Verify that List works correctly. func (s *RunS) TestListFiltered(c *C) { names := List(&FixtureHelper{}, &RunConf{Filter: "1"}) c.Assert(names, DeepEquals, []string{ "FixtureHelper.Test1", }) } func (s *RunS) TestList(c *C) { names := List(&FixtureHelper{}, &RunConf{}) c.Assert(names, DeepEquals, []string{ "FixtureHelper.Test1", "FixtureHelper.Test2", }) } // ----------------------------------------------------------------------- // Verify that verbose mode prints tests which pass as well. func (s *RunS) TestVerboseMode(c *C) { helper := FixtureHelper{} output := String{} runConf := RunConf{Output: &output, Verbose: true} Run(&helper, &runConf) expected := "PASS: gocheck_test\\.go:[0-9]+: FixtureHelper\\.Test1\t *[.0-9]+s\n" + "PASS: gocheck_test\\.go:[0-9]+: FixtureHelper\\.Test2\t *[.0-9]+s\n" c.Assert(output.value, Matches, expected) } func (s *RunS) TestVerboseModeWithFailBeforePass(c *C) { helper := FixtureHelper{panicOn: "Test1"} output := String{} runConf := RunConf{Output: &output, Verbose: true} Run(&helper, &runConf) expected := "(?s).*PANIC.*\n-+\n" + // Should have an extra line. "PASS: gocheck_test\\.go:[0-9]+: FixtureHelper\\.Test2\t *[.0-9]+s\n" c.Assert(output.value, Matches, expected) } // ----------------------------------------------------------------------- // Verify the stream output mode. In this mode there's no output caching. type StreamHelper struct { l2 sync.Mutex l3 sync.Mutex } func (s *StreamHelper) SetUpSuite(c *C) { c.Log("0") } func (s *StreamHelper) Test1(c *C) { c.Log("1") s.l2.Lock() s.l3.Lock() go func() { s.l2.Lock() // Wait for "2". c.Log("3") s.l3.Unlock() }() } func (s *StreamHelper) Test2(c *C) { c.Log("2") s.l2.Unlock() s.l3.Lock() // Wait for "3". c.Fail() c.Log("4") } func (s *RunS) TestStreamMode(c *C) { helper := &StreamHelper{} output := String{} runConf := RunConf{Output: &output, Stream: true} Run(helper, &runConf) expected := "START: run_test\\.go:[0-9]+: StreamHelper\\.SetUpSuite\n0\n" + "PASS: run_test\\.go:[0-9]+: StreamHelper\\.SetUpSuite\t *[.0-9]+s\n\n" + "START: run_test\\.go:[0-9]+: StreamHelper\\.Test1\n1\n" + "PASS: run_test\\.go:[0-9]+: StreamHelper\\.Test1\t *[.0-9]+s\n\n" + "START: run_test\\.go:[0-9]+: StreamHelper\\.Test2\n2\n3\n4\n" + "FAIL: run_test\\.go:[0-9]+: StreamHelper\\.Test2\n\n" c.Assert(output.value, Matches, expected) } type StreamMissHelper struct{} func (s *StreamMissHelper) SetUpSuite(c *C) { c.Log("0") c.Fail() } func (s *StreamMissHelper) Test1(c *C) { c.Log("1") } func (s *RunS) TestStreamModeWithMiss(c *C) { helper := &StreamMissHelper{} output := String{} runConf := RunConf{Output: &output, Stream: true} Run(helper, &runConf) expected := "START: run_test\\.go:[0-9]+: StreamMissHelper\\.SetUpSuite\n0\n" + "FAIL: run_test\\.go:[0-9]+: StreamMissHelper\\.SetUpSuite\n\n" + "START: run_test\\.go:[0-9]+: StreamMissHelper\\.Test1\n" + "MISS: run_test\\.go:[0-9]+: StreamMissHelper\\.Test1\n\n" c.Assert(output.value, Matches, expected) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/foundation_test.go���������������������������������������0000644�0000153�0000161�00000021755�12321736015�024126� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// These tests check that the foundations of gocheck are working properly. // They already assume that fundamental failing is working already, though, // since this was tested in bootstrap_test.go. Even then, some care may // still have to be taken when using external functions, since they should // of course not rely on functionality tested here. package gocheck_test import ( "fmt" "launchpad.net/gocheck" "log" "os" "regexp" "strings" ) // ----------------------------------------------------------------------- // Foundation test suite. type FoundationS struct{} var foundationS = gocheck.Suite(&FoundationS{}) func (s *FoundationS) TestCountSuite(c *gocheck.C) { suitesRun += 1 } func (s *FoundationS) TestErrorf(c *gocheck.C) { // Do not use checkState() here. It depends on Errorf() working. expectedLog := fmt.Sprintf("foundation_test.go:%d:\n"+ " c.Errorf(\"Error %%v!\", \"message\")\n"+ "... Error: Error message!\n\n", getMyLine()+1) c.Errorf("Error %v!", "message") failed := c.Failed() c.Succeed() if log := c.GetTestLog(); log != expectedLog { c.Logf("Errorf() logged %#v rather than %#v", log, expectedLog) c.Fail() } if !failed { c.Logf("Errorf() didn't put the test in a failed state") c.Fail() } } func (s *FoundationS) TestError(c *gocheck.C) { expectedLog := fmt.Sprintf("foundation_test.go:%d:\n"+ " c\\.Error\\(\"Error \", \"message!\"\\)\n"+ "\\.\\.\\. Error: Error message!\n\n", getMyLine()+1) c.Error("Error ", "message!") checkState(c, nil, &expectedState{ name: "Error(`Error `, `message!`)", failed: true, log: expectedLog, }) } func (s *FoundationS) TestFailNow(c *gocheck.C) { defer (func() { if !c.Failed() { c.Error("FailNow() didn't fail the test") } else { c.Succeed() if c.GetTestLog() != "" { c.Error("Something got logged:\n" + c.GetTestLog()) } } })() c.FailNow() c.Log("FailNow() didn't stop the test") } func (s *FoundationS) TestSucceedNow(c *gocheck.C) { defer (func() { if c.Failed() { c.Error("SucceedNow() didn't succeed the test") } if c.GetTestLog() != "" { c.Error("Something got logged:\n" + c.GetTestLog()) } })() c.Fail() c.SucceedNow() c.Log("SucceedNow() didn't stop the test") } func (s *FoundationS) TestFailureHeader(c *gocheck.C) { output := String{} failHelper := FailHelper{} gocheck.Run(&failHelper, &gocheck.RunConf{Output: &output}) header := fmt.Sprintf(""+ "\n-----------------------------------"+ "-----------------------------------\n"+ "FAIL: gocheck_test.go:%d: FailHelper.TestLogAndFail\n", failHelper.testLine) if strings.Index(output.value, header) == -1 { c.Errorf(""+ "Failure didn't print a proper header.\n"+ "... Got:\n%s... Expected something with:\n%s", output.value, header) } } func (s *FoundationS) TestFatal(c *gocheck.C) { var line int defer (func() { if !c.Failed() { c.Error("Fatal() didn't fail the test") } else { c.Succeed() expected := fmt.Sprintf("foundation_test.go:%d:\n"+ " c.Fatal(\"Die \", \"now!\")\n"+ "... Error: Die now!\n\n", line) if c.GetTestLog() != expected { c.Error("Incorrect log:", c.GetTestLog()) } } })() line = getMyLine() + 1 c.Fatal("Die ", "now!") c.Log("Fatal() didn't stop the test") } func (s *FoundationS) TestFatalf(c *gocheck.C) { var line int defer (func() { if !c.Failed() { c.Error("Fatalf() didn't fail the test") } else { c.Succeed() expected := fmt.Sprintf("foundation_test.go:%d:\n"+ " c.Fatalf(\"Die %%s!\", \"now\")\n"+ "... Error: Die now!\n\n", line) if c.GetTestLog() != expected { c.Error("Incorrect log:", c.GetTestLog()) } } })() line = getMyLine() + 1 c.Fatalf("Die %s!", "now") c.Log("Fatalf() didn't stop the test") } func (s *FoundationS) TestCallerLoggingInsideTest(c *gocheck.C) { log := fmt.Sprintf(""+ "foundation_test.go:%d:\n"+ " result := c.Check\\(10, gocheck.Equals, 20\\)\n"+ "\\.\\.\\. obtained int = 10\n"+ "\\.\\.\\. expected int = 20\n\n", getMyLine()+1) result := c.Check(10, gocheck.Equals, 20) checkState(c, result, &expectedState{ name: "Check(10, Equals, 20)", result: false, failed: true, log: log, }) } func (s *FoundationS) TestCallerLoggingInDifferentFile(c *gocheck.C) { result, line := checkEqualWrapper(c, 10, 20) testLine := getMyLine() - 1 log := fmt.Sprintf(""+ "foundation_test.go:%d:\n"+ " result, line := checkEqualWrapper\\(c, 10, 20\\)\n"+ "gocheck_test.go:%d:\n"+ " return c.Check\\(obtained, gocheck.Equals, expected\\), getMyLine\\(\\)\n"+ "\\.\\.\\. obtained int = 10\n"+ "\\.\\.\\. expected int = 20\n\n", testLine, line) checkState(c, result, &expectedState{ name: "Check(10, Equals, 20)", result: false, failed: true, log: log, }) } // ----------------------------------------------------------------------- // ExpectFailure() inverts the logic of failure. type ExpectFailureSucceedHelper struct{} func (s *ExpectFailureSucceedHelper) TestSucceed(c *gocheck.C) { c.ExpectFailure("It booms!") c.Error("Boom!") } type ExpectFailureFailHelper struct{} func (s *ExpectFailureFailHelper) TestFail(c *gocheck.C) { c.ExpectFailure("Bug #XYZ") } func (s *FoundationS) TestExpectFailureFail(c *gocheck.C) { helper := ExpectFailureFailHelper{} output := String{} result := gocheck.Run(&helper, &gocheck.RunConf{Output: &output}) expected := "" + "^\n-+\n" + "FAIL: foundation_test\\.go:[0-9]+:" + " ExpectFailureFailHelper\\.TestFail\n\n" + "\\.\\.\\. Error: Test succeeded, but was expected to fail\n" + "\\.\\.\\. Reason: Bug #XYZ\n$" matched, err := regexp.MatchString(expected, output.value) if err != nil { c.Error("Bad expression: ", expected) } else if !matched { c.Error("ExpectFailure() didn't log properly:\n", output.value) } c.Assert(result.ExpectedFailures, gocheck.Equals, 0) } func (s *FoundationS) TestExpectFailureSucceed(c *gocheck.C) { helper := ExpectFailureSucceedHelper{} output := String{} result := gocheck.Run(&helper, &gocheck.RunConf{Output: &output}) c.Assert(output.value, gocheck.Equals, "") c.Assert(result.ExpectedFailures, gocheck.Equals, 1) } func (s *FoundationS) TestExpectFailureSucceedVerbose(c *gocheck.C) { helper := ExpectFailureSucceedHelper{} output := String{} result := gocheck.Run(&helper, &gocheck.RunConf{Output: &output, Verbose: true}) expected := "" + "FAIL EXPECTED: foundation_test\\.go:[0-9]+:" + " ExpectFailureSucceedHelper\\.TestSucceed \\(It booms!\\)\t *[.0-9]+s\n" matched, err := regexp.MatchString(expected, output.value) if err != nil { c.Error("Bad expression: ", expected) } else if !matched { c.Error("ExpectFailure() didn't log properly:\n", output.value) } c.Assert(result.ExpectedFailures, gocheck.Equals, 1) } // ----------------------------------------------------------------------- // Skip() allows stopping a test without positive/negative results. type SkipTestHelper struct{} func (s *SkipTestHelper) TestFail(c *gocheck.C) { c.Skip("Wrong platform or whatever") c.Error("Boom!") } func (s *FoundationS) TestSkip(c *gocheck.C) { helper := SkipTestHelper{} output := String{} gocheck.Run(&helper, &gocheck.RunConf{Output: &output}) if output.value != "" { c.Error("Skip() logged something:\n", output.value) } } func (s *FoundationS) TestSkipVerbose(c *gocheck.C) { helper := SkipTestHelper{} output := String{} gocheck.Run(&helper, &gocheck.RunConf{Output: &output, Verbose: true}) expected := "SKIP: foundation_test\\.go:[0-9]+: SkipTestHelper\\.TestFail" + " \\(Wrong platform or whatever\\)" matched, err := regexp.MatchString(expected, output.value) if err != nil { c.Error("Bad expression: ", expected) } else if !matched { c.Error("Skip() didn't log properly:\n", output.value) } } // ----------------------------------------------------------------------- // Check minimum *log.Logger interface provided by *gocheck.C. type minLogger interface { Output(calldepth int, s string) error } func (s *BootstrapS) TestMinLogger(c *gocheck.C) { var logger minLogger logger = log.New(os.Stderr, "", 0) logger = c logger.Output(0, "Hello there") expected := "\\[LOG\\] [.0-9]+ Hello there\n" output := c.GetTestLog() matched, err := regexp.MatchString(expected, output) if err != nil { c.Error("Bad expression: ", expected) } else if !matched { c.Error("Output() didn't log properly:\n", output) } } // ----------------------------------------------------------------------- // Ensure that suites with embedded types are working fine, including the // the workaround for issue 906. type EmbeddedInternalS struct { called bool } type EmbeddedS struct { EmbeddedInternalS } var embeddedS = gocheck.Suite(&EmbeddedS{}) func (s *EmbeddedS) TestCountSuite(c *gocheck.C) { suitesRun += 1 } func (s *EmbeddedInternalS) TestMethod(c *gocheck.C) { c.Error("TestMethod() of the embedded type was called!?") } func (s *EmbeddedS) TestMethod(c *gocheck.C) { // http://code.google.com/p/go/issues/detail?id=906 c.Check(s.called, gocheck.Equals, false) // Go issue 906 is affecting the runner? s.called = true } �������������������juju-core_1.18.1/src/launchpad.net/gocheck/gocheck_test.go������������������������������������������0000644�0000153�0000161�00000011653�12321736015�023357� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// This file contains just a few generic helpers which are used by the // other test files. package gocheck_test import ( "flag" "fmt" "launchpad.net/gocheck" "os" "regexp" "runtime" "testing" "time" ) // We count the number of suites run at least to get a vague hint that the // test suite is behaving as it should. Otherwise a bug introduced at the // very core of the system could go unperceived. const suitesRunExpected = 8 var suitesRun int = 0 func Test(t *testing.T) { gocheck.TestingT(t) if suitesRun != suitesRunExpected && flag.Lookup("gocheck.f").Value.String() == "" { critical(fmt.Sprintf("Expected %d suites to run rather than %d", suitesRunExpected, suitesRun)) } } // ----------------------------------------------------------------------- // Helper functions. // Break down badly. This is used in test cases which can't yet assume // that the fundamental bits are working. func critical(error string) { fmt.Fprintln(os.Stderr, "CRITICAL: "+error) os.Exit(1) } // Return the file line where it's called. func getMyLine() int { if _, _, line, ok := runtime.Caller(1); ok { return line } return -1 } // ----------------------------------------------------------------------- // Helper type implementing a basic io.Writer for testing output. // Type implementing the io.Writer interface for analyzing output. type String struct { value string } // The only function required by the io.Writer interface. Will append // written data to the String.value string. func (s *String) Write(p []byte) (n int, err error) { s.value += string(p) return len(p), nil } // Trivial wrapper to test errors happening on a different file // than the test itself. func checkEqualWrapper(c *gocheck.C, obtained, expected interface{}) (result bool, line int) { return c.Check(obtained, gocheck.Equals, expected), getMyLine() } // ----------------------------------------------------------------------- // Helper suite for testing basic fail behavior. type FailHelper struct { testLine int } func (s *FailHelper) TestLogAndFail(c *gocheck.C) { s.testLine = getMyLine() - 1 c.Log("Expected failure!") c.Fail() } // ----------------------------------------------------------------------- // Helper suite for testing basic success behavior. type SuccessHelper struct{} func (s *SuccessHelper) TestLogAndSucceed(c *gocheck.C) { c.Log("Expected success!") } // ----------------------------------------------------------------------- // Helper suite for testing ordering and behavior of fixture. type FixtureHelper struct { calls []string panicOn string skip bool skipOnN int sleepOn string sleep time.Duration bytes int64 } func (s *FixtureHelper) trace(name string, c *gocheck.C) { s.calls = append(s.calls, name) if name == s.panicOn { panic(name) } if s.sleep > 0 && s.sleepOn == name { time.Sleep(s.sleep) } if s.skip && s.skipOnN == len(s.calls)-1 { c.Skip("skipOnN == n") } } func (s *FixtureHelper) SetUpSuite(c *gocheck.C) { s.trace("SetUpSuite", c) } func (s *FixtureHelper) TearDownSuite(c *gocheck.C) { s.trace("TearDownSuite", c) } func (s *FixtureHelper) SetUpTest(c *gocheck.C) { s.trace("SetUpTest", c) } func (s *FixtureHelper) TearDownTest(c *gocheck.C) { s.trace("TearDownTest", c) } func (s *FixtureHelper) Test1(c *gocheck.C) { s.trace("Test1", c) } func (s *FixtureHelper) Test2(c *gocheck.C) { s.trace("Test2", c) } func (s *FixtureHelper) Benchmark1(c *gocheck.C) { s.trace("Benchmark1", c) for i := 0; i < c.N; i++ { time.Sleep(s.sleep) } } func (s *FixtureHelper) Benchmark2(c *gocheck.C) { s.trace("Benchmark2", c) c.SetBytes(1024) for i := 0; i < c.N; i++ { time.Sleep(s.sleep) } } // ----------------------------------------------------------------------- // Helper which checks the state of the test and ensures that it matches // the given expectations. Depends on c.Errorf() working, so shouldn't // be used to test this one function. type expectedState struct { name string result interface{} failed bool log string } // Verify the state of the test. Note that since this also verifies if // the test is supposed to be in a failed state, no other checks should // be done in addition to what is being tested. func checkState(c *gocheck.C, result interface{}, expected *expectedState) { failed := c.Failed() c.Succeed() log := c.GetTestLog() matched, matchError := regexp.MatchString("^"+expected.log+"$", log) if matchError != nil { c.Errorf("Error in matching expression used in testing %s", expected.name) } else if !matched { c.Errorf("%s logged:\n----------\n%s----------\n\nExpected:\n----------\n%s\n----------", expected.name, log, expected.log) } if result != expected.result { c.Errorf("%s returned %#v rather than %#v", expected.name, result, expected.result) } if failed != expected.failed { if failed { c.Errorf("%s has failed when it shouldn't", expected.name) } else { c.Errorf("%s has not failed when it should", expected.name) } } } �������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/checkers_test.go�����������������������������������������0000644�0000153�0000161�00000024506�12321735711�023546� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gocheck_test import ( "errors" "launchpad.net/gocheck" "reflect" "runtime" ) type CheckersS struct{} var _ = gocheck.Suite(&CheckersS{}) func testInfo(c *gocheck.C, checker gocheck.Checker, name string, paramNames []string) { info := checker.Info() if info.Name != name { c.Fatalf("Got name %s, expected %s", info.Name, name) } if !reflect.DeepEqual(info.Params, paramNames) { c.Fatalf("Got param names %#v, expected %#v", info.Params, paramNames) } } func testCheck(c *gocheck.C, checker gocheck.Checker, result bool, error string, params ...interface{}) ([]interface{}, []string) { info := checker.Info() if len(params) != len(info.Params) { c.Fatalf("unexpected param count in test; expected %d got %d", len(info.Params), len(params)) } names := append([]string{}, info.Params...) result_, error_ := checker.Check(params, names) if result_ != result || error_ != error { c.Fatalf("%s.Check(%#v) returned (%#v, %#v) rather than (%#v, %#v)", info.Name, params, result_, error_, result, error) } return params, names } func (s *CheckersS) TestComment(c *gocheck.C) { bug := gocheck.Commentf("a %d bc", 42) comment := bug.CheckCommentString() if comment != "a 42 bc" { c.Fatalf("Commentf returned %#v", comment) } } func (s *CheckersS) TestIsNil(c *gocheck.C) { testInfo(c, gocheck.IsNil, "IsNil", []string{"value"}) testCheck(c, gocheck.IsNil, true, "", nil) testCheck(c, gocheck.IsNil, false, "", "a") testCheck(c, gocheck.IsNil, true, "", (chan int)(nil)) testCheck(c, gocheck.IsNil, false, "", make(chan int)) testCheck(c, gocheck.IsNil, true, "", (error)(nil)) testCheck(c, gocheck.IsNil, false, "", errors.New("")) testCheck(c, gocheck.IsNil, true, "", ([]int)(nil)) testCheck(c, gocheck.IsNil, false, "", make([]int, 1)) testCheck(c, gocheck.IsNil, false, "", int(0)) } func (s *CheckersS) TestNotNil(c *gocheck.C) { testInfo(c, gocheck.NotNil, "NotNil", []string{"value"}) testCheck(c, gocheck.NotNil, false, "", nil) testCheck(c, gocheck.NotNil, true, "", "a") testCheck(c, gocheck.NotNil, false, "", (chan int)(nil)) testCheck(c, gocheck.NotNil, true, "", make(chan int)) testCheck(c, gocheck.NotNil, false, "", (error)(nil)) testCheck(c, gocheck.NotNil, true, "", errors.New("")) testCheck(c, gocheck.NotNil, false, "", ([]int)(nil)) testCheck(c, gocheck.NotNil, true, "", make([]int, 1)) } func (s *CheckersS) TestNot(c *gocheck.C) { testInfo(c, gocheck.Not(gocheck.IsNil), "Not(IsNil)", []string{"value"}) testCheck(c, gocheck.Not(gocheck.IsNil), false, "", nil) testCheck(c, gocheck.Not(gocheck.IsNil), true, "", "a") } type simpleStruct struct { i int } func (s *CheckersS) TestEquals(c *gocheck.C) { testInfo(c, gocheck.Equals, "Equals", []string{"obtained", "expected"}) // The simplest. testCheck(c, gocheck.Equals, true, "", 42, 42) testCheck(c, gocheck.Equals, false, "", 42, 43) // Different native types. testCheck(c, gocheck.Equals, false, "", int32(42), int64(42)) // With nil. testCheck(c, gocheck.Equals, false, "", 42, nil) // Slices testCheck(c, gocheck.Equals, false, "runtime error: comparing uncomparable type []uint8", []byte{1, 2}, []byte{1, 2}) // Struct values testCheck(c, gocheck.Equals, true, "", simpleStruct{1}, simpleStruct{1}) testCheck(c, gocheck.Equals, false, "", simpleStruct{1}, simpleStruct{2}) // Struct pointers testCheck(c, gocheck.Equals, false, "", &simpleStruct{1}, &simpleStruct{1}) testCheck(c, gocheck.Equals, false, "", &simpleStruct{1}, &simpleStruct{2}) } func (s *CheckersS) TestDeepEquals(c *gocheck.C) { testInfo(c, gocheck.DeepEquals, "DeepEquals", []string{"obtained", "expected"}) // The simplest. testCheck(c, gocheck.DeepEquals, true, "", 42, 42) testCheck(c, gocheck.DeepEquals, false, "", 42, 43) // Different native types. testCheck(c, gocheck.DeepEquals, false, "", int32(42), int64(42)) // With nil. testCheck(c, gocheck.DeepEquals, false, "", 42, nil) // Slices testCheck(c, gocheck.DeepEquals, true, "", []byte{1, 2}, []byte{1, 2}) testCheck(c, gocheck.DeepEquals, false, "", []byte{1, 2}, []byte{1, 3}) // Struct values testCheck(c, gocheck.DeepEquals, true, "", simpleStruct{1}, simpleStruct{1}) testCheck(c, gocheck.DeepEquals, false, "", simpleStruct{1}, simpleStruct{2}) // Struct pointers testCheck(c, gocheck.DeepEquals, true, "", &simpleStruct{1}, &simpleStruct{1}) testCheck(c, gocheck.DeepEquals, false, "", &simpleStruct{1}, &simpleStruct{2}) } func (s *CheckersS) TestHasLen(c *gocheck.C) { testInfo(c, gocheck.HasLen, "HasLen", []string{"obtained", "n"}) testCheck(c, gocheck.HasLen, true, "", "abcd", 4) testCheck(c, gocheck.HasLen, true, "", []int{1, 2}, 2) testCheck(c, gocheck.HasLen, false, "", []int{1, 2}, 3) testCheck(c, gocheck.HasLen, false, "n must be an int", []int{1, 2}, "2") testCheck(c, gocheck.HasLen, false, "obtained value type has no length", nil, 2) } func (s *CheckersS) TestErrorMatches(c *gocheck.C) { testInfo(c, gocheck.ErrorMatches, "ErrorMatches", []string{"value", "regex"}) testCheck(c, gocheck.ErrorMatches, false, "Error value is nil", nil, "some error") testCheck(c, gocheck.ErrorMatches, false, "Value is not an error", 1, "some error") testCheck(c, gocheck.ErrorMatches, true, "", errors.New("some error"), "some error") testCheck(c, gocheck.ErrorMatches, true, "", errors.New("some error"), "so.*or") // Verify params mutation params, names := testCheck(c, gocheck.ErrorMatches, false, "", errors.New("some error"), "other error") c.Assert(params[0], gocheck.Equals, "some error") c.Assert(names[0], gocheck.Equals, "error") } func (s *CheckersS) TestMatches(c *gocheck.C) { testInfo(c, gocheck.Matches, "Matches", []string{"value", "regex"}) // Simple matching testCheck(c, gocheck.Matches, true, "", "abc", "abc") testCheck(c, gocheck.Matches, true, "", "abc", "a.c") // Must match fully testCheck(c, gocheck.Matches, false, "", "abc", "ab") testCheck(c, gocheck.Matches, false, "", "abc", "bc") // String()-enabled values accepted testCheck(c, gocheck.Matches, true, "", reflect.ValueOf("abc"), "a.c") testCheck(c, gocheck.Matches, false, "", reflect.ValueOf("abc"), "a.d") // Some error conditions. testCheck(c, gocheck.Matches, false, "Obtained value is not a string and has no .String()", 1, "a.c") testCheck(c, gocheck.Matches, false, "Can't compile regex: error parsing regexp: missing closing ]: `[c$`", "abc", "a[c") } func (s *CheckersS) TestPanics(c *gocheck.C) { testInfo(c, gocheck.Panics, "Panics", []string{"function", "expected"}) // Some errors. testCheck(c, gocheck.Panics, false, "Function has not panicked", func() bool { return false }, "BOOM") testCheck(c, gocheck.Panics, false, "Function must take zero arguments", 1, "BOOM") // Plain strings. testCheck(c, gocheck.Panics, true, "", func() { panic("BOOM") }, "BOOM") testCheck(c, gocheck.Panics, false, "", func() { panic("KABOOM") }, "BOOM") testCheck(c, gocheck.Panics, true, "", func() bool { panic("BOOM") }, "BOOM") // Error values. testCheck(c, gocheck.Panics, true, "", func() { panic(errors.New("BOOM")) }, errors.New("BOOM")) testCheck(c, gocheck.Panics, false, "", func() { panic(errors.New("KABOOM")) }, errors.New("BOOM")) type deep struct{ i int } // Deep value testCheck(c, gocheck.Panics, true, "", func() { panic(&deep{99}) }, &deep{99}) // Verify params/names mutation params, names := testCheck(c, gocheck.Panics, false, "", func() { panic(errors.New("KABOOM")) }, errors.New("BOOM")) c.Assert(params[0], gocheck.ErrorMatches, "KABOOM") c.Assert(names[0], gocheck.Equals, "panic") // Verify a nil panic testCheck(c, gocheck.Panics, true, "", func() { panic(nil) }, nil) testCheck(c, gocheck.Panics, false, "", func() { panic(nil) }, "NOPE") } func (s *CheckersS) TestPanicMatches(c *gocheck.C) { testInfo(c, gocheck.PanicMatches, "PanicMatches", []string{"function", "expected"}) // Error matching. testCheck(c, gocheck.PanicMatches, true, "", func() { panic(errors.New("BOOM")) }, "BO.M") testCheck(c, gocheck.PanicMatches, false, "", func() { panic(errors.New("KABOOM")) }, "BO.M") // Some errors. testCheck(c, gocheck.PanicMatches, false, "Function has not panicked", func() bool { return false }, "BOOM") testCheck(c, gocheck.PanicMatches, false, "Function must take zero arguments", 1, "BOOM") // Plain strings. testCheck(c, gocheck.PanicMatches, true, "", func() { panic("BOOM") }, "BO.M") testCheck(c, gocheck.PanicMatches, false, "", func() { panic("KABOOM") }, "BOOM") testCheck(c, gocheck.PanicMatches, true, "", func() bool { panic("BOOM") }, "BO.M") // Verify params/names mutation params, names := testCheck(c, gocheck.PanicMatches, false, "", func() { panic(errors.New("KABOOM")) }, "BOOM") c.Assert(params[0], gocheck.Equals, "KABOOM") c.Assert(names[0], gocheck.Equals, "panic") // Verify a nil panic testCheck(c, gocheck.PanicMatches, false, "Panic value is not a string or an error", func() { panic(nil) }, "") } func (s *CheckersS) TestFitsTypeOf(c *gocheck.C) { testInfo(c, gocheck.FitsTypeOf, "FitsTypeOf", []string{"obtained", "sample"}) // Basic types testCheck(c, gocheck.FitsTypeOf, true, "", 1, 0) testCheck(c, gocheck.FitsTypeOf, false, "", 1, int64(0)) // Aliases testCheck(c, gocheck.FitsTypeOf, false, "", 1, errors.New("")) testCheck(c, gocheck.FitsTypeOf, false, "", "error", errors.New("")) testCheck(c, gocheck.FitsTypeOf, true, "", errors.New("error"), errors.New("")) // Structures testCheck(c, gocheck.FitsTypeOf, false, "", 1, simpleStruct{}) testCheck(c, gocheck.FitsTypeOf, false, "", simpleStruct{42}, &simpleStruct{}) testCheck(c, gocheck.FitsTypeOf, true, "", simpleStruct{42}, simpleStruct{}) testCheck(c, gocheck.FitsTypeOf, true, "", &simpleStruct{42}, &simpleStruct{}) // Some bad values testCheck(c, gocheck.FitsTypeOf, false, "Invalid sample value", 1, interface{}(nil)) testCheck(c, gocheck.FitsTypeOf, false, "", interface{}(nil), 0) } func (s *CheckersS) TestImplements(c *gocheck.C) { testInfo(c, gocheck.Implements, "Implements", []string{"obtained", "ifaceptr"}) var e error var re runtime.Error testCheck(c, gocheck.Implements, true, "", errors.New(""), &e) testCheck(c, gocheck.Implements, false, "", errors.New(""), &re) // Some bad values testCheck(c, gocheck.Implements, false, "ifaceptr should be a pointer to an interface variable", 0, errors.New("")) testCheck(c, gocheck.Implements, false, "ifaceptr should be a pointer to an interface variable", 0, interface{}(nil)) testCheck(c, gocheck.Implements, false, "", interface{}(nil), &e) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/.bzrignore�����������������������������������������������0000644�0000153�0000161�00000000033�12321735711�022360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������_* [856].out [856].out.exe �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/run.go���������������������������������������������������0000644�0000153�0000161�00000010056�12321736015�021515� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gocheck import ( "bufio" "flag" "fmt" "os" "testing" "time" ) // ----------------------------------------------------------------------- // Test suite registry. var allSuites []interface{} // Register the given value as a test suite to be run. Any methods starting // with the Test prefix in the given value will be considered as a test to // be run. func Suite(suite interface{}) interface{} { allSuites = append(allSuites, suite) return suite } // ----------------------------------------------------------------------- // Public running interface. var ( filterFlag = flag.String("gocheck.f", "", "Regular expression selecting which tests and/or suites to run") verboseFlag = flag.Bool("gocheck.v", false, "Verbose mode") streamFlag = flag.Bool("gocheck.vv", false, "Super verbose mode (disables output caching)") benchFlag = flag.Bool("gocheck.b", false, "Run benchmarks") benchTime = flag.Duration("gocheck.btime", 1 * time.Second, "approximate run time for each benchmark") listFlag = flag.Bool("gocheck.list", false, "List the names of all tests that will be run") ) // Run all test suites registered with the Suite() function, printing // results to stdout, and reporting any failures back to the 'testing' // module. func TestingT(testingT *testing.T) { conf := &RunConf{ Filter: *filterFlag, Verbose: *verboseFlag, Stream: *streamFlag, Benchmark: *benchFlag, BenchmarkTime: *benchTime, } if *listFlag { w := bufio.NewWriter(os.Stdout) for _, name := range ListAll(conf) { fmt.Fprintln(w, name) } w.Flush() return } result := RunAll(conf) println(result.String()) if !result.Passed() { testingT.Fail() } } // RunAll runs all test suites registered with the Suite() function, using the // given run configuration. func RunAll(runConf *RunConf) *Result { result := Result{} for _, suite := range allSuites { result.Add(Run(suite, runConf)) } return &result } // Run runs the given test suite using the provided run configuration. func Run(suite interface{}, runConf *RunConf) *Result { runner := newSuiteRunner(suite, runConf) return runner.run() } // ListAll returns the names of all the test functions registered with the // Suite function that will be run with the provided run configuration. func ListAll(runConf *RunConf) []string { var names []string for _, suite := range allSuites { names = append(names, List(suite, runConf)...) } return names } // List prints the names of the test functions in the given // suite that will be run with the provided run configuration // to the given Writer. func List(suite interface{}, runConf *RunConf) []string { var names []string runner := newSuiteRunner(suite, runConf) for _, t := range runner.tests { names = append(names, t.String()) } return names } // ----------------------------------------------------------------------- // Result methods. func (r *Result) Add(other *Result) { r.Succeeded += other.Succeeded r.Skipped += other.Skipped r.Failed += other.Failed r.Panicked += other.Panicked r.FixturePanicked += other.FixturePanicked r.ExpectedFailures += other.ExpectedFailures r.Missed += other.Missed } func (r *Result) Passed() bool { return (r.Failed == 0 && r.Panicked == 0 && r.FixturePanicked == 0 && r.Missed == 0 && r.RunError == nil) } func (r *Result) String() string { if r.RunError != nil { return "ERROR: " + r.RunError.Error() } var value string if r.Failed == 0 && r.Panicked == 0 && r.FixturePanicked == 0 && r.Missed == 0 { value = "OK: " } else { value = "OOPS: " } value += fmt.Sprintf("%d passed", r.Succeeded) if r.Skipped != 0 { value += fmt.Sprintf(", %d skipped", r.Skipped) } if r.ExpectedFailures != 0 { value += fmt.Sprintf(", %d expected failures", r.ExpectedFailures) } if r.Failed != 0 { value += fmt.Sprintf(", %d FAILED", r.Failed) } if r.Panicked != 0 { value += fmt.Sprintf(", %d PANICKED", r.Panicked) } if r.FixturePanicked != 0 { value += fmt.Sprintf(", %d FIXTURE-PANICKED", r.FixturePanicked) } if r.Missed != 0 { value += fmt.Sprintf(", %d MISSED", r.Missed) } return value } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/printer_test.go������������������������������������������0000644�0000153�0000161�00000004634�12321735711�023442� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gocheck_test import ( . "launchpad.net/gocheck" ) var _ = Suite(&PrinterS{}) type PrinterS struct{} func (s *PrinterS) TestCountSuite(c *C) { suitesRun += 1 } var printTestFuncLine int func init() { printTestFuncLine = getMyLine() + 3 } func printTestFunc() { println(1) // Comment1 if 2 == 2 { // Comment2 println(3) // Comment3 } switch 5 { case 6: println(6) // Comment6 println(7) } switch interface{}(9).(type) {// Comment9 case int: println(10) println(11) } select { case <-(chan bool)(nil): println(14) println(15) default: println(16) println(17) } println(19, 20) _ = func() { println(21) println(22) } println(24, func() { println(25) }) // Leading comment // with multiple lines. println(29) // Comment29 } var printLineTests = []struct { line int output string }{ {1, "println(1) // Comment1"}, {2, "if 2 == 2 { // Comment2\n ...\n}"}, {3, "println(3) // Comment3"}, {5, "switch 5 {\n...\n}"}, {6, "case 6:\n println(6) // Comment6\n ..."}, {7, "println(7)"}, {9, "switch interface{}(9).(type) { // Comment9\n...\n}"}, {10, "case int:\n println(10)\n ..."}, {14, "case <-(chan bool)(nil):\n println(14)\n ..."}, {15, "println(15)"}, {16, "default:\n println(16)\n ..."}, {17, "println(17)"}, {19, "println(19,\n 20)"}, {20, "println(19,\n 20)"}, {21, "_ = func() {\n println(21)\n println(22)\n}"}, {22, "println(22)"}, {24, "println(24, func() {\n println(25)\n})"}, {25, "println(25)"}, {26, "println(24, func() {\n println(25)\n})"}, {29, "// Leading comment\n// with multiple lines.\nprintln(29) // Comment29"}, } func (s *PrinterS) TestPrintLine(c *C) { for _, test := range printLineTests { output, err := PrintLine("printer_test.go", printTestFuncLine+test.line) c.Assert(err, IsNil) c.Assert(output, Equals, test.output) } } var indentTests = []struct { in, out string }{ {"", ""}, {"\n", "\n"}, {"a", ">>>a"}, {"a\n", ">>>a\n"}, {"a\nb", ">>>a\n>>>b"}, {" ", ">>> "}, } func (s *PrinterS) TestIndent(c *C) { for _, test := range indentTests { out := Indent(test.in, ">>>") c.Assert(out, Equals, test.out) } } ����������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/Makefile�������������������������������������������������0000644�0000153�0000161�00000001204�12321735711�022017� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������include $(GOROOT)/src/Make.inc TARG=launchpad.net/gocheck GOFILES=\ gocheck.go\ helpers.go\ run.go\ checkers.go\ printer.go\ #TARGDIR=$(GOPATH)/pkg/$(GOOS)_$(GOARCH) #GCIMPORTS=$(patsubst %,-I %/pkg/$(GOOS)_$(GOARCH),$(subst :, ,$(GOPATH))) #LDIMPORTS=$(patsubst %,-L %/pkg/$(GOOS)_$(GOARCH),$(subst :, ,$(GOPATH))) include $(GOROOT)/src/Make.pkg GOFMT=gofmt BADFMT=$(shell $(GOFMT) -l $(GOFILES) $(filter-out printer_test.go,$(wildcard *_test.go))) gofmt: $(BADFMT) @for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done ifneq ($(BADFMT),) ifneq ($(MAKECMDGOALS),gofmt) #$(warning WARNING: make gofmt: $(BADFMT)) endif endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/printer.go�����������������������������������������������0000644�0000153�0000161�00000007367�12321735711�022411� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gocheck import ( "bytes" "go/ast" "go/parser" "go/printer" "go/token" "os" ) func indent(s, with string) (r string) { eol := true for i := 0; i != len(s); i++ { c := s[i] switch { case eol && c == '\n' || c == '\r': case c == '\n' || c == '\r': eol = true case eol: eol = false s = s[:i] + with + s[i:] i += len(with) } } return s } func printLine(filename string, line int) (string, error) { fset := token.NewFileSet() file, err := os.Open(filename) if err != nil { return "", err } fnode, err := parser.ParseFile(fset, filename, file, parser.ParseComments) if err != nil { return "", err } config := &printer.Config{Mode: printer.UseSpaces, Tabwidth: 4} lp := &linePrinter{fset: fset, fnode: fnode, line: line, config: config} ast.Walk(lp, fnode) result := lp.output.Bytes() // Comments leave \n at the end. n := len(result) for n > 0 && result[n-1] == '\n' { n-- } return string(result[:n]), nil } type linePrinter struct { config *printer.Config fset *token.FileSet fnode *ast.File line int output bytes.Buffer stmt ast.Stmt } func (lp *linePrinter) emit() bool { if lp.stmt != nil { lp.trim(lp.stmt) lp.printWithComments(lp.stmt) lp.stmt = nil return true } return false } func (lp *linePrinter) printWithComments(n ast.Node) { nfirst := lp.fset.Position(n.Pos()).Line nlast := lp.fset.Position(n.End()).Line for _, g := range lp.fnode.Comments { cfirst := lp.fset.Position(g.Pos()).Line clast := lp.fset.Position(g.End()).Line if clast == nfirst-1 && lp.fset.Position(n.Pos()).Column == lp.fset.Position(g.Pos()).Column { for _, c := range g.List { lp.output.WriteString(c.Text) lp.output.WriteByte('\n') } } if cfirst >= nfirst && cfirst <= nlast && n.End() <= g.List[0].Slash { // The printer will not include the comment if it starts past // the node itself. Trick it into printing by overlapping the // slash with the end of the statement. g.List[0].Slash = n.End() - 1 } } node := &printer.CommentedNode{n, lp.fnode.Comments} lp.config.Fprint(&lp.output, lp.fset, node) } func (lp *linePrinter) Visit(n ast.Node) (w ast.Visitor) { if n == nil { if lp.output.Len() == 0 { lp.emit() } return nil } first := lp.fset.Position(n.Pos()).Line last := lp.fset.Position(n.End()).Line if first <= lp.line && last >= lp.line { // Print the innermost statement containing the line. if stmt, ok := n.(ast.Stmt); ok { if _, ok := n.(*ast.BlockStmt); !ok { lp.stmt = stmt } } if first == lp.line && lp.emit() { return nil } return lp } return nil } func (lp *linePrinter) trim(n ast.Node) bool { stmt, ok := n.(ast.Stmt) if !ok { return true } line := lp.fset.Position(n.Pos()).Line if line != lp.line { return false } switch stmt := stmt.(type) { case *ast.IfStmt: stmt.Body = lp.trimBlock(stmt.Body) case *ast.SwitchStmt: stmt.Body = lp.trimBlock(stmt.Body) case *ast.TypeSwitchStmt: stmt.Body = lp.trimBlock(stmt.Body) case *ast.CaseClause: stmt.Body = lp.trimList(stmt.Body) case *ast.CommClause: stmt.Body = lp.trimList(stmt.Body) case *ast.BlockStmt: stmt.List = lp.trimList(stmt.List) } return true } func (lp *linePrinter) trimBlock(stmt *ast.BlockStmt) *ast.BlockStmt { if !lp.trim(stmt) { return lp.emptyBlock(stmt) } stmt.Rbrace = stmt.Lbrace return stmt } func (lp *linePrinter) trimList(stmts []ast.Stmt) []ast.Stmt { for i := 0; i != len(stmts); i++ { if !lp.trim(stmts[i]) { stmts[i] = lp.emptyStmt(stmts[i]) break } } return stmts } func (lp *linePrinter) emptyStmt(n ast.Node) *ast.ExprStmt { return &ast.ExprStmt{&ast.Ellipsis{n.Pos(), nil}} } func (lp *linePrinter) emptyBlock(n ast.Node) *ast.BlockStmt { p := n.Pos() return &ast.BlockStmt{p, []ast.Stmt{lp.emptyStmt(n)}, p} } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/benchmark.go���������������������������������������������0000644�0000153�0000161�00000005651�12321735711�022652� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package gocheck import ( "fmt" "time" ) // testingB is a type passed to Benchmark functions to manage benchmark // timing and to specify the number of iterations to run. type timer struct { start time.Time // Time test or benchmark started duration time.Duration N int bytes int64 timerOn bool benchTime time.Duration } // StartTimer starts timing a test. This function is called automatically // before a benchmark starts, but it can also used to resume timing after // a call to StopTimer. func (c *C) StartTimer() { if !c.timerOn { c.start = time.Now() c.timerOn = true } } // StopTimer stops timing a test. This can be used to pause the timer // while performing complex initialization that you don't // want to measure. func (c *C) StopTimer() { if c.timerOn { c.duration += time.Now().Sub(c.start) c.timerOn = false } } // ResetTimer sets the elapsed benchmark time to zero. // It does not affect whether the timer is running. func (c *C) ResetTimer() { if c.timerOn { c.start = time.Now() } c.duration = 0 } // SetBytes informs the number of bytes that the benchmark processes // on each iteration. If this is called in a benchmark it will also // report MB/s. func (c *C) SetBytes(n int64) { c.bytes = n } func (c *C) nsPerOp() int64 { if c.N <= 0 { return 0 } return c.duration.Nanoseconds() / int64(c.N) } func (c *C) mbPerSec() float64 { if c.bytes <= 0 || c.duration <= 0 || c.N <= 0 { return 0 } return (float64(c.bytes) * float64(c.N) / 1e6) / c.duration.Seconds() } func (c *C) timerString() string { if c.N <= 0 { return fmt.Sprintf("%3.3fs", float64(c.duration.Nanoseconds())/1e9) } mbs := c.mbPerSec() mb := "" if mbs != 0 { mb = fmt.Sprintf("\t%7.2f MB/s", mbs) } nsop := c.nsPerOp() ns := fmt.Sprintf("%10d ns/op", nsop) if c.N > 0 && nsop < 100 { // The format specifiers here make sure that // the ones digits line up for all three possible formats. if nsop < 10 { ns = fmt.Sprintf("%13.2f ns/op", float64(c.duration.Nanoseconds())/float64(c.N)) } else { ns = fmt.Sprintf("%12.1f ns/op", float64(c.duration.Nanoseconds())/float64(c.N)) } } return fmt.Sprintf("%8d\t%s%s", c.N, ns, mb) } func min(x, y int) int { if x > y { return y } return x } func max(x, y int) int { if x < y { return y } return x } // roundDown10 rounds a number down to the nearest power of 10. func roundDown10(n int) int { var tens = 0 // tens = floor(log_10(n)) for n > 10 { n = n / 10 tens++ } // result = 10^tens result := 1 for i := 0; i < tens; i++ { result *= 10 } return result } // roundUp rounds x up to a number of the form [1eX, 2eX, 5eX]. func roundUp(n int) int { base := roundDown10(n) if n < (2 * base) { return 2 * base } if n < (5 * base) { return 5 * base } return 10 * base } ���������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/gocheck.go�����������������������������������������������0000644�0000153�0000161�00000053620�12321736015�022320� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gocheck import ( "bytes" "errors" "fmt" "io" "math/rand" "os" "path" "path/filepath" "reflect" "regexp" "runtime" "strconv" "strings" "sync" "time" ) // ----------------------------------------------------------------------- // Internal type which deals with suite method calling. const ( fixtureKd = iota testKd ) type funcKind int const ( succeededSt = iota failedSt skippedSt panickedSt fixturePanickedSt missedSt ) type funcStatus int // A method value can't reach its own Method structure. type methodType struct { reflect.Value Info reflect.Method } func newMethod(receiver reflect.Value, i int) *methodType { return &methodType{receiver.Method(i), receiver.Type().Method(i)} } func (method *methodType) PC() uintptr { return method.Info.Func.Pointer() } func (method *methodType) suiteName() string { t := method.Info.Type.In(0) if t.Kind() == reflect.Ptr { t = t.Elem() } return t.Name() } func (method *methodType) String() string { return method.suiteName()+"."+method.Info.Name } func (method *methodType) matches(re *regexp.Regexp) bool { return (re.MatchString(method.Info.Name) || re.MatchString(method.suiteName()) || re.MatchString(method.String())) } type C struct { method *methodType kind funcKind status funcStatus logb *logger logw io.Writer done chan *C reason string mustFail bool tempDir *tempDir timer } func (c *C) stopNow() { runtime.Goexit() } // logger is a concurrency safe byte.Buffer type logger struct { sync.Mutex writer bytes.Buffer } func (l *logger) Write(buf []byte) (int, error) { l.Lock() defer l.Unlock() return l.writer.Write(buf) } func (l *logger) WriteTo(w io.Writer) (int64, error) { l.Lock() defer l.Unlock() return l.writer.WriteTo(w) } func (l *logger) String() string { l.Lock() defer l.Unlock() return l.writer.String() } // ----------------------------------------------------------------------- // Handling of temporary files and directories. type tempDir struct { sync.Mutex _path string _counter int } func (td *tempDir) newPath() string { td.Lock() defer td.Unlock() if td._path == "" { var err error for i := 0; i != 100; i++ { path := fmt.Sprintf("%s/gocheck-%d", os.TempDir(), rand.Int()) if err = os.Mkdir(path, 0700); err == nil { td._path = path break } } if td._path == "" { panic("Couldn't create temporary directory: " + err.Error()) } } result := path.Join(td._path, strconv.Itoa(td._counter)) td._counter += 1 return result } func (td *tempDir) removeAll() { td.Lock() defer td.Unlock() if td._path != "" { err := os.RemoveAll(td._path) if err != nil { fmt.Fprintf(os.Stderr, "WARNING: Error cleaning up temporaries: "+err.Error()) } } } // Create a new temporary directory which is automatically removed after // the suite finishes running. func (c *C) MkDir() string { path := c.tempDir.newPath() if err := os.Mkdir(path, 0700); err != nil { panic(fmt.Sprintf("Couldn't create temporary directory %s: %s", path, err.Error())) } return path } // ----------------------------------------------------------------------- // Low-level logging functions. func (c *C) log(args ...interface{}) { c.writeLog([]byte(fmt.Sprint(args...) + "\n")) } func (c *C) logf(format string, args ...interface{}) { c.writeLog([]byte(fmt.Sprintf(format+"\n", args...))) } func (c *C) logNewLine() { c.writeLog([]byte{'\n'}) } func (c *C) writeLog(buf []byte) { c.logb.Write(buf) if c.logw != nil { c.logw.Write(buf) } } func hasStringOrError(x interface{}) (ok bool) { _, ok = x.(fmt.Stringer) if ok { return } _, ok = x.(error) return } func (c *C) logValue(label string, value interface{}) { if label == "" { if hasStringOrError(value) { c.logf("... %#v (%q)", value, value) } else { c.logf("... %#v", value) } } else if value == nil { c.logf("... %s = nil", label) } else { if hasStringOrError(value) { fv := fmt.Sprintf("%#v", value) qv := fmt.Sprintf("%q", value) if fv != qv { c.logf("... %s %s = %s (%s)", label, reflect.TypeOf(value), fv, qv) return } } if s, ok := value.(string); ok && isMultiLine(s) { c.logf(`... %s %s = "" +`, label, reflect.TypeOf(value)) c.logMultiLine(s) } else { c.logf("... %s %s = %#v", label, reflect.TypeOf(value), value) } } } func (c *C) logMultiLine(s string) { b := make([]byte, 0, len(s)*2) i := 0 n := len(s) for i < n { j := i + 1 for j < n && s[j-1] != '\n' { j++ } b = append(b, "... "...) b = strconv.AppendQuote(b, s[i:j]) if j < n { b = append(b, " +"...) } b = append(b, '\n') i = j } c.writeLog(b) } func isMultiLine(s string) bool { for i := 0; i+1 < len(s); i++ { if s[i] == '\n' { return true } } return false } func (c *C) logString(issue string) { c.log("... ", issue) } func (c *C) logCaller(skip int) { // This is a bit heavier than it ought to be. skip += 1 // Our own frame. pc, callerFile, callerLine, ok := runtime.Caller(skip) if !ok { return } var testFile string var testLine int testFunc := runtime.FuncForPC(c.method.PC()) if runtime.FuncForPC(pc) != testFunc { for { skip += 1 if pc, file, line, ok := runtime.Caller(skip); ok { // Note that the test line may be different on // distinct calls for the same test. Showing // the "internal" line is helpful when debugging. if runtime.FuncForPC(pc) == testFunc { testFile, testLine = file, line break } } else { break } } } if testFile != "" && (testFile != callerFile || testLine != callerLine) { c.logCode(testFile, testLine) } c.logCode(callerFile, callerLine) } func (c *C) logCode(path string, line int) { c.logf("%s:%d:", nicePath(path), line) code, err := printLine(path, line) if code == "" { code = "..." // XXX Open the file and take the raw line. if err != nil { code += err.Error() } } c.log(indent(code, " ")) } var valueGo = filepath.Join("reflect", "value.go") func (c *C) logPanic(skip int, value interface{}) { skip += 1 // Our own frame. initialSkip := skip for { if pc, file, line, ok := runtime.Caller(skip); ok { if skip == initialSkip { c.logf("... Panic: %s (PC=0x%X)\n", value, pc) } name := niceFuncName(pc) path := nicePath(file) if name == "Value.call" && strings.HasSuffix(path, valueGo) { break } c.logf("%s:%d\n in %s", nicePath(file), line, name) } else { break } skip += 1 } } func (c *C) logSoftPanic(issue string) { c.log("... Panic: ", issue) } func (c *C) logArgPanic(method *methodType, expectedType string) { c.logf("... Panic: %s argument should be %s", niceFuncName(method.PC()), expectedType) } // ----------------------------------------------------------------------- // Some simple formatting helpers. var initWD, initWDErr = os.Getwd() func init() { if initWDErr == nil { initWD = strings.Replace(initWD, "\\", "/", -1) + "/" } } func nicePath(path string) string { if initWDErr == nil { if strings.HasPrefix(path, initWD) { return path[len(initWD):] } } return path } func niceFuncPath(pc uintptr) string { function := runtime.FuncForPC(pc) if function != nil { filename, line := function.FileLine(pc) return fmt.Sprintf("%s:%d", nicePath(filename), line) } return "<unknown path>" } func niceFuncName(pc uintptr) string { function := runtime.FuncForPC(pc) if function != nil { name := path.Base(function.Name()) if i := strings.Index(name, "."); i > 0 { name = name[i+1:] } if strings.HasPrefix(name, "(*") { if i := strings.Index(name, ")"); i > 0 { name = name[2:i] + name[i+1:] } } if i := strings.LastIndex(name, ".*"); i != -1 { name = name[:i] + "." + name[i+2:] } if i := strings.LastIndex(name, "·"); i != -1 { name = name[:i] + "." + name[i+2:] } return name } return "<unknown function>" } // ----------------------------------------------------------------------- // Result tracker to aggregate call results. type Result struct { Succeeded int Failed int Skipped int Panicked int FixturePanicked int ExpectedFailures int Missed int // Not even tried to run, related to a panic in the fixture. RunError error // Houston, we've got a problem. } type resultTracker struct { result Result _lastWasProblem bool _waiting int _missed int _expectChan chan *C _doneChan chan *C _stopChan chan bool } func newResultTracker() *resultTracker { return &resultTracker{_expectChan: make(chan *C), // Synchronous _doneChan: make(chan *C, 32), // Asynchronous _stopChan: make(chan bool)} // Synchronous } func (tracker *resultTracker) start() { go tracker._loopRoutine() } func (tracker *resultTracker) waitAndStop() { <-tracker._stopChan } func (tracker *resultTracker) expectCall(c *C) { tracker._expectChan <- c } func (tracker *resultTracker) callDone(c *C) { tracker._doneChan <- c } func (tracker *resultTracker) _loopRoutine() { for { var c *C if tracker._waiting > 0 { // Calls still running. Can't stop. select { // XXX Reindent this (not now to make diff clear) case c = <-tracker._expectChan: tracker._waiting += 1 case c = <-tracker._doneChan: tracker._waiting -= 1 switch c.status { case succeededSt: if c.kind == testKd { if c.mustFail { tracker.result.ExpectedFailures++ } else { tracker.result.Succeeded++ } } case failedSt: tracker.result.Failed++ case panickedSt: if c.kind == fixtureKd { tracker.result.FixturePanicked++ } else { tracker.result.Panicked++ } case fixturePanickedSt: // Track it as missed, since the panic // was on the fixture, not on the test. tracker.result.Missed++ case missedSt: tracker.result.Missed++ case skippedSt: if c.kind == testKd { tracker.result.Skipped++ } } } } else { // No calls. Can stop, but no done calls here. select { case tracker._stopChan <- true: return case c = <-tracker._expectChan: tracker._waiting += 1 case c = <-tracker._doneChan: panic("Tracker got an unexpected done call.") } } } } // ----------------------------------------------------------------------- // The underlying suite runner. type suiteRunner struct { suite interface{} setUpSuite, tearDownSuite *methodType setUpTest, tearDownTest *methodType tests []*methodType tracker *resultTracker tempDir *tempDir output *outputWriter reportedProblemLast bool benchTime time.Duration } type RunConf struct { Output io.Writer Stream bool Verbose bool Filter string Benchmark bool BenchmarkTime time.Duration // Defaults to 1 second } // Create a new suiteRunner able to run all methods in the given suite. func newSuiteRunner(suite interface{}, runConf *RunConf) *suiteRunner { var conf RunConf if runConf != nil { conf = *runConf } if conf.Output == nil { conf.Output = os.Stdout } if conf.Benchmark { conf.Verbose = true } suiteType := reflect.TypeOf(suite) suiteNumMethods := suiteType.NumMethod() suiteValue := reflect.ValueOf(suite) runner := &suiteRunner{ suite: suite, output: newOutputWriter(conf.Output, conf.Stream, conf.Verbose), tracker: newResultTracker(), benchTime: conf.BenchmarkTime, } runner.tests = make([]*methodType, 0, suiteNumMethods) runner.tempDir = new(tempDir) if runner.benchTime == 0 { runner.benchTime = 1 * time.Second } var filterRegexp *regexp.Regexp if conf.Filter != "" { if regexp, err := regexp.Compile(conf.Filter); err != nil { msg := "Bad filter expression: " + err.Error() runner.tracker.result.RunError = errors.New(msg) return runner } else { filterRegexp = regexp } } for i := 0; i != suiteNumMethods; i++ { method := newMethod(suiteValue, i) switch method.Info.Name { case "SetUpSuite": runner.setUpSuite = method case "TearDownSuite": runner.tearDownSuite = method case "SetUpTest": runner.setUpTest = method case "TearDownTest": runner.tearDownTest = method default: prefix := "Test" if conf.Benchmark { prefix = "Benchmark" } if !strings.HasPrefix(method.Info.Name, prefix) { continue } if filterRegexp == nil || method.matches(filterRegexp) { runner.tests = append(runner.tests, method) } } } return runner } // Run all methods in the given suite. func (runner *suiteRunner) run() *Result { if runner.tracker.result.RunError == nil && len(runner.tests) > 0 { runner.tracker.start() if runner.checkFixtureArgs() { c := runner.runFixture(runner.setUpSuite, nil) if c == nil || c.status == succeededSt { for i := 0; i != len(runner.tests); i++ { c := runner.runTest(runner.tests[i]) if c.status == fixturePanickedSt { runner.skipTests(missedSt, runner.tests[i+1:]) break } } } else if c != nil && c.status == skippedSt { runner.skipTests(skippedSt, runner.tests) } else { runner.skipTests(missedSt, runner.tests) } runner.runFixture(runner.tearDownSuite, nil) } else { runner.skipTests(missedSt, runner.tests) } runner.tracker.waitAndStop() runner.tempDir.removeAll() } return &runner.tracker.result } // Create a call object with the given suite method, and fork a // goroutine with the provided dispatcher for running it. func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, logb *logger, dispatcher func(c *C)) *C { var logw io.Writer if runner.output.Stream { logw = runner.output } if logb == nil { logb = new(logger) } c := &C{ method: method, kind: kind, logb: logb, logw: logw, tempDir: runner.tempDir, done: make(chan *C, 1), timer: timer{benchTime: runner.benchTime}, } runner.tracker.expectCall(c) go (func() { runner.reportCallStarted(c) defer runner.callDone(c) dispatcher(c) })() return c } // Same as forkCall(), but wait for call to finish before returning. func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, logb *logger, dispatcher func(c *C)) *C { c := runner.forkCall(method, kind, logb, dispatcher) <-c.done return c } // Handle a finished call. If there were any panics, update the call status // accordingly. Then, mark the call as done and report to the tracker. func (runner *suiteRunner) callDone(c *C) { value := recover() if value != nil { switch v := value.(type) { case *fixturePanic: if v.status == skippedSt { c.status = skippedSt } else { c.logSoftPanic("Fixture has panicked (see related PANIC)") c.status = fixturePanickedSt } default: c.logPanic(1, value) c.status = panickedSt } } if c.mustFail { switch c.status { case failedSt: c.status = succeededSt case succeededSt: c.status = failedSt c.logString("Error: Test succeeded, but was expected to fail") c.logString("Reason: " + c.reason) } } runner.reportCallDone(c) c.done <- c } // Runs a fixture call synchronously. The fixture will still be run in a // goroutine like all suite methods, but this method will not return // while the fixture goroutine is not done, because the fixture must be // run in a desired order. func (runner *suiteRunner) runFixture(method *methodType, logb *logger) *C { if method != nil { c := runner.runFunc(method, fixtureKd, logb, func(c *C) { c.ResetTimer() c.StartTimer() defer c.StopTimer() c.method.Call([]reflect.Value{reflect.ValueOf(c)}) }) return c } return nil } // Run the fixture method with runFixture(), but panic with a fixturePanic{} // in case the fixture method panics. This makes it easier to track the // fixture panic together with other call panics within forkTest(). func (runner *suiteRunner) runFixtureWithPanic(method *methodType, logb *logger, skipped *bool) *C { if skipped != nil && *skipped { return nil } c := runner.runFixture(method, logb) if c != nil && c.status != succeededSt { if skipped != nil { *skipped = c.status == skippedSt } panic(&fixturePanic{c.status, method}) } return c } type fixturePanic struct { status funcStatus method *methodType } // Run the suite test method, together with the test-specific fixture, // asynchronously. func (runner *suiteRunner) forkTest(method *methodType) *C { return runner.forkCall(method, testKd, nil, func(c *C) { var skipped bool defer runner.runFixtureWithPanic(runner.tearDownTest, nil, &skipped) defer c.StopTimer() benchN := 1 for { runner.runFixtureWithPanic(runner.setUpTest, c.logb, &skipped) mt := c.method.Type() if mt.NumIn() != 1 || mt.In(0) != reflect.TypeOf(c) { // Rather than a plain panic, provide a more helpful message when // the argument type is incorrect. c.status = panickedSt c.logArgPanic(c.method, "*gocheck.C") return } if strings.HasPrefix(c.method.Info.Name, "Test") { c.ResetTimer() c.StartTimer() c.method.Call([]reflect.Value{reflect.ValueOf(c)}) return } if !strings.HasPrefix(c.method.Info.Name, "Benchmark") { panic("unexpected method prefix: " + c.method.Info.Name) } runtime.GC() c.N = benchN c.ResetTimer() c.StartTimer() c.method.Call([]reflect.Value{reflect.ValueOf(c)}) c.StopTimer() if c.status != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 { return } perOpN := int(1e9) if c.nsPerOp() != 0 { perOpN = int(c.benchTime.Nanoseconds() / c.nsPerOp()) } // Logic taken from the stock testing package: // - Run more iterations than we think we'll need for a second (1.5x). // - Don't grow too fast in case we had timing errors previously. // - Be sure to run at least one more than last time. benchN = max(min(perOpN+perOpN/2, 100*benchN), benchN+1) benchN = roundUp(benchN) skipped = true // Don't run the deferred one if this panics. runner.runFixtureWithPanic(runner.tearDownTest, nil, nil) skipped = false } }) } // Same as forkTest(), but wait for the test to finish before returning. func (runner *suiteRunner) runTest(method *methodType) *C { c := runner.forkTest(method) <-c.done return c } // Helper to mark tests as skipped or missed. A bit heavy for what // it does, but it enables homogeneous handling of tracking, including // nice verbose output. func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) { for _, method := range methods { runner.runFunc(method, testKd, nil, func(c *C) { c.status = status }) } } // Verify if the fixture arguments are *gocheck.C. In case of errors, // log the error as a panic in the fixture method call, and return false. func (runner *suiteRunner) checkFixtureArgs() bool { succeeded := true argType := reflect.TypeOf(&C{}) for _, method := range []*methodType{runner.setUpSuite, runner.tearDownSuite, runner.setUpTest, runner.tearDownTest} { if method != nil { mt := method.Type() if mt.NumIn() != 1 || mt.In(0) != argType { succeeded = false runner.runFunc(method, fixtureKd, nil, func(c *C) { c.logArgPanic(method, "*gocheck.C") c.status = panickedSt }) } } } return succeeded } func (runner *suiteRunner) reportCallStarted(c *C) { runner.output.WriteCallStarted("START", c) } func (runner *suiteRunner) reportCallDone(c *C) { runner.tracker.callDone(c) switch c.status { case succeededSt: if c.mustFail { runner.output.WriteCallSuccess("FAIL EXPECTED", c) } else { runner.output.WriteCallSuccess("PASS", c) } case skippedSt: runner.output.WriteCallSuccess("SKIP", c) case failedSt: runner.output.WriteCallProblem("FAIL", c) case panickedSt: runner.output.WriteCallProblem("PANIC", c) case fixturePanickedSt: // That's a testKd call reporting that its fixture // has panicked. The fixture call which caused the // panic itself was tracked above. We'll report to // aid debugging. runner.output.WriteCallProblem("PANIC", c) case missedSt: runner.output.WriteCallSuccess("MISS", c) } } // ----------------------------------------------------------------------- // Output writer manages atomic output writing according to settings. type outputWriter struct { m sync.Mutex writer io.Writer wroteCallProblemLast bool Stream bool Verbose bool } func newOutputWriter(writer io.Writer, stream, verbose bool) *outputWriter { return &outputWriter{writer: writer, Stream: stream, Verbose: verbose} } func (ow *outputWriter) Write(content []byte) (n int, err error) { ow.m.Lock() n, err = ow.writer.Write(content) ow.m.Unlock() return } func (ow *outputWriter) WriteCallStarted(label string, c *C) { if ow.Stream { header := renderCallHeader(label, c, "", "\n") ow.m.Lock() ow.writer.Write([]byte(header)) ow.m.Unlock() } } func (ow *outputWriter) WriteCallProblem(label string, c *C) { var prefix string if !ow.Stream { prefix = "\n-----------------------------------" + "-----------------------------------\n" } header := renderCallHeader(label, c, prefix, "\n\n") ow.m.Lock() ow.wroteCallProblemLast = true ow.writer.Write([]byte(header)) if !ow.Stream { c.logb.WriteTo(ow.writer) } ow.m.Unlock() } func (ow *outputWriter) WriteCallSuccess(label string, c *C) { if ow.Stream || (ow.Verbose && c.kind == testKd) { // TODO Use a buffer here. var suffix string if c.reason != "" { suffix = " (" + c.reason + ")" } if c.status == succeededSt { suffix += "\t" + c.timerString() } suffix += "\n" if ow.Stream { suffix += "\n" } header := renderCallHeader(label, c, "", suffix) ow.m.Lock() // Resist temptation of using line as prefix above due to race. if !ow.Stream && ow.wroteCallProblemLast { header = "\n-----------------------------------" + "-----------------------------------\n" + header } ow.wroteCallProblemLast = false ow.writer.Write([]byte(header)) ow.m.Unlock() } } func renderCallHeader(label string, c *C, prefix, suffix string) string { pc := c.method.PC() return fmt.Sprintf("%s%s: %s: %s%s", prefix, label, niceFuncPath(pc), niceFuncName(pc), suffix) } ����������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/.gitignore�����������������������������������������������0000644�0000153�0000161�00000000033�12321735711�022346� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������_* *.swp *.[568] [568].out �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gocheck/benchmark_test.go����������������������������������������0000644�0000153�0000161�00000004237�12321736015�023706� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// These tests verify the test running logic. package gocheck_test import ( . "launchpad.net/gocheck" "time" ) var benchmarkS = Suite(&BenchmarkS{}) type BenchmarkS struct{} func (s *BenchmarkS) TestCountSuite(c *C) { suitesRun += 1 } func (s *BenchmarkS) TestBasicTestTiming(c *C) { helper := FixtureHelper{sleepOn: "Test1", sleep: 1000000 * time.Nanosecond} output := String{} runConf := RunConf{Output: &output, Verbose: true} Run(&helper, &runConf) expected := "PASS: gocheck_test\\.go:[0-9]+: FixtureHelper\\.Test1\t0\\.001s\n" + "PASS: gocheck_test\\.go:[0-9]+: FixtureHelper\\.Test2\t0\\.000s\n" c.Assert(output.value, Matches, expected) } func (s *BenchmarkS) TestStreamTestTiming(c *C) { helper := FixtureHelper{sleepOn: "SetUpSuite", sleep: 1000000 * time.Nanosecond} output := String{} runConf := RunConf{Output: &output, Stream: true} Run(&helper, &runConf) expected := "(?s).*\nPASS: gocheck_test\\.go:[0-9]+: FixtureHelper\\.SetUpSuite\t *0\\.001s\n.*" c.Assert(output.value, Matches, expected) } func (s *BenchmarkS) TestBenchmark(c *C) { helper := FixtureHelper{sleep: 100000} output := String{} runConf := RunConf{ Output: &output, Benchmark: true, BenchmarkTime: 10000000, Filter: "Benchmark1", } Run(&helper, &runConf) c.Check(helper.calls[0], Equals, "SetUpSuite") c.Check(helper.calls[1], Equals, "SetUpTest") c.Check(helper.calls[2], Equals, "Benchmark1") c.Check(helper.calls[3], Equals, "TearDownTest") c.Check(helper.calls[4], Equals, "SetUpTest") c.Check(helper.calls[5], Equals, "Benchmark1") c.Check(helper.calls[6], Equals, "TearDownTest") // ... and more. expected := "PASS: gocheck_test\\.go:[0-9]+: FixtureHelper\\.Benchmark1\t *100\t *[12][0-9]{5} ns/op\n" c.Assert(output.value, Matches, expected) } func (s *BenchmarkS) TestBenchmarkBytes(c *C) { helper := FixtureHelper{sleep: 100000} output := String{} runConf := RunConf{ Output: &output, Benchmark: true, BenchmarkTime: 10000000, Filter: "Benchmark2", } Run(&helper, &runConf) expected := "PASS: gocheck_test\\.go:[0-9]+: FixtureHelper\\.Benchmark2\t *100\t *[12][0-9]{5} ns/op\t *[4-9]\\.[0-9]{2} MB/s\n" c.Assert(output.value, Matches, expected) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/golxc/�����������������������������������������������������������0000755�0000153�0000161�00000000000�12321735734�020100� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/golxc/COPYING.LESSER���������������������������������������������0000644�0000153�0000161�00000016743�12321735734�022142� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �����������������������������juju-core_1.18.1/src/launchpad.net/golxc/LICENSE����������������������������������������������������0000644�0000153�0000161�00000001254�12321735734�021107� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������golxc - Go package to interact with Linux Containers (LXC). Copyright 2012-2013, Canonical Ltd. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. See both COPYING and COPYING.LESSER for the full terms of the GNU Lesser General Public License. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/golxc/export_test.go���������������������������������������������0000644�0000153�0000161�00000000675�12321735734�023017� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. package golxc // ContainerHome returns the name of the container directory. func ContainerHome(c Container) string { return c.(*container).containerHome() } // SetConfPath allows the manipulation of the LXC // default configuration file. func SetConfPath(cp string) string { orig := confPath confPath = cp return orig } �������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/golxc/config.go��������������������������������������������������0000644�0000153�0000161�00000001606�12321735734�021677� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. package golxc import ( "io/ioutil" "regexp" ) var ( confPath string = "/etc/default/lxc" addrRE *regexp.Regexp = regexp.MustCompile(`\n\s*LXC_ADDR="(\d+.\d+.\d+.\d+)"`) bridgeRE *regexp.Regexp = regexp.MustCompile(`\n\s*LXC_BRIDGE="(\w+)"`) ) // ReadConf reads the LXC network address and bridge interface // out of the configuration file /etc/default/lxc. func ReadConf() (map[string]string, error) { conf := make(map[string]string) confData, err := ioutil.ReadFile(confPath) if err != nil { return nil, err } fetchValue := func(field string, re *regexp.Regexp) { groups := re.FindStringSubmatch(string(confData)) if len(groups) == 2 { conf[field] = groups[1] } } fetchValue("address", addrRE) fetchValue("bridge", bridgeRE) return conf, nil } ��������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/golxc/golxc_test.go����������������������������������������������0000644�0000153�0000161�00000026665�12321735734�022621� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. package golxc_test import ( "io/ioutil" "os" "os/user" "path/filepath" "github.com/juju/testing" jc "github.com/juju/testing/checkers" "github.com/juju/testing/logging" . "launchpad.net/gocheck" "launchpad.net/golxc" ) var lxcfile = `# MIRROR to be used by ubuntu template at container creation: # Leaving it undefined is fine #MIRROR="http://archive.ubuntu.com/ubuntu" # or #MIRROR="http://<host-ip-addr>:3142/archive.ubuntu.com/ubuntu" # LXC_AUTO - whether or not to start containers symlinked under # /etc/lxc/auto LXC_AUTO="true" # Leave USE_LXC_BRIDGE as "true" if you want to use lxcbr0 for your # containers. Set to "false" if you'll use virbr0 or another existing # bridge, or mavlan to your host's NIC. USE_LXC_BRIDGE="true" # LXC_BRIDGE and LXC_ADDR are changed against original for # testing purposes. # LXC_BRIDGE="lxcbr1" LXC_BRIDGE="lxcbr9" # LXC_ADDR="10.0.1.1" LXC_ADDR="10.0.9.1" LXC_NETMASK="255.255.255.0" LXC_NETWORK="10.0.9.0/24" LXC_DHCP_RANGE="10.0.9.2,10.0.9.254" LXC_DHCP_MAX="253" # And for testing LXC_BRIDGE="lxcbr99" and LXC_ADDR="10.0.99.1". LXC_SHUTDOWN_TIMEOUT=120` var lxcconf = map[string]string{ "address": "10.0.9.1", "bridge": "lxcbr9", } type ConfigSuite struct{} var _ = Suite(&ConfigSuite{}) func (s *ConfigSuite) TestReadConf(c *C) { // Test reading the configuration. cf := filepath.Join(c.MkDir(), "lxc-test") c.Assert(ioutil.WriteFile(cf, []byte(lxcfile), 0555), IsNil) defer golxc.SetConfPath(golxc.SetConfPath(cf)) conf, err := golxc.ReadConf() c.Assert(err, IsNil) c.Assert(conf, DeepEquals, lxcconf) } func (s *ConfigSuite) TestReadNotExistingDefaultEnvironment(c *C) { // Test reading a not existing environment. defer golxc.SetConfPath(golxc.SetConfPath(filepath.Join(c.MkDir(), "foo"))) _, err := golxc.ReadConf() c.Assert(err, ErrorMatches, "open .*: no such file or directory") } func (s *ConfigSuite) TestNetworkAttributes(c *C) { // Test reading the network attribute form an environment. cf := filepath.Join(c.MkDir(), "lxc-test") c.Assert(ioutil.WriteFile(cf, []byte(lxcfile), 0555), IsNil) defer golxc.SetConfPath(golxc.SetConfPath(cf)) addr, bridge, err := golxc.NetworkAttributes() c.Assert(err, IsNil) c.Assert(addr, Equals, "10.0.9.1") c.Assert(bridge, Equals, "lxcbr9") } type NetworkSuite struct{} var _ = Suite(&NetworkSuite{}) func (s *NetworkSuite) SetUpSuite(c *C) { u, err := user.Current() c.Assert(err, IsNil) if u.Uid != "0" { // Has to be run as root! c.Skip("tests must run as root") } } func (s *NetworkSuite) TestStartStopNetwork(c *C) { // Test starting and stoping of the LXC network. initialRunning, err := golxc.IsNetworkRunning() c.Assert(err, IsNil) defer func() { if initialRunning { c.Assert(golxc.StartNetwork(), IsNil) } }() c.Assert(golxc.StartNetwork(), IsNil) running, err := golxc.IsNetworkRunning() c.Assert(err, IsNil) c.Assert(running, Equals, true) c.Assert(golxc.StopNetwork(), IsNil) running, err = golxc.IsNetworkRunning() c.Assert(err, IsNil) c.Assert(running, Equals, false) } func (s *NetworkSuite) TestNotExistingNetworkAttributes(c *C) { // Test reading of network attributes from a not existing environment. defer golxc.SetConfPath(golxc.SetConfPath(filepath.Join(c.MkDir(), "foo"))) _, _, err := golxc.NetworkAttributes() c.Assert(err, ErrorMatches, "open .*: no such file or directory") } type LXCSuite struct { factory golxc.ContainerFactory } var _ = Suite(&LXCSuite{golxc.Factory()}) func (s *LXCSuite) SetUpSuite(c *C) { u, err := user.Current() c.Assert(err, IsNil) if u.Uid != "0" { // Has to be run as root! c.Skip("tests must run as root") } } func (s *LXCSuite) createContainer(c *C) golxc.Container { container := s.factory.New("golxc") c.Assert(container.IsConstructed(), Equals, false) err := container.Create("", "ubuntu", nil, nil) c.Assert(err, IsNil) c.Assert(container.IsConstructed(), Equals, true) return container } func (s *LXCSuite) TestCreateDestroy(c *C) { // Test clean creation and destroying of a container. lc := s.factory.New("golxc") c.Assert(lc.IsConstructed(), Equals, false) home := golxc.ContainerHome(lc) _, err := os.Stat(home) c.Assert(err, ErrorMatches, "stat .*: no such file or directory") err = lc.Create("", "ubuntu", nil, nil) c.Assert(err, IsNil) c.Assert(lc.IsConstructed(), Equals, true) defer func() { err = lc.Destroy() c.Assert(err, IsNil) _, err = os.Stat(home) c.Assert(err, ErrorMatches, "stat .*: no such file or directory") }() fi, err := os.Stat(golxc.ContainerHome(lc)) c.Assert(err, IsNil) c.Assert(fi.IsDir(), Equals, true) } func (s *LXCSuite) TestCreateTwice(c *C) { // Test that a container cannot be created twice. lc1 := s.createContainer(c) c.Assert(lc1.IsConstructed(), Equals, true) defer func() { c.Assert(lc1.Destroy(), IsNil) }() lc2 := s.factory.New("golxc") err := lc2.Create("", "ubuntu", nil, nil) c.Assert(err, ErrorMatches, "container .* is already created") } func (s *LXCSuite) TestCreateIllegalTemplate(c *C) { // Test that a container creation fails correctly in // case of an illegal template. lc := s.factory.New("golxc") c.Assert(lc.IsConstructed(), Equals, false) err := lc.Create("", "name-of-a-not-existing-template-for-golxc", nil, nil) c.Assert(err, ErrorMatches, `error executing "lxc-create": .*bad template.*`) c.Assert(lc.IsConstructed(), Equals, false) } func (s *LXCSuite) TestDestroyNotCreated(c *C) { // Test that a non-existing container can't be destroyed. lc := s.factory.New("golxc") c.Assert(lc.IsConstructed(), Equals, false) err := lc.Destroy() c.Assert(err, ErrorMatches, "container .* is not yet created") } func contains(lcs []golxc.Container, lc golxc.Container) bool { for _, clc := range lcs { if clc.Name() == lc.Name() { return true } } return false } func (s *LXCSuite) TestList(c *C) { // Test the listing of created containers. lcs, err := s.factory.List() oldLen := len(lcs) c.Assert(err, IsNil) c.Assert(oldLen >= 0, Equals, true) lc := s.createContainer(c) defer func() { c.Assert(lc.Destroy(), IsNil) }() lcs, _ = s.factory.List() newLen := len(lcs) c.Assert(newLen == oldLen+1, Equals, true) c.Assert(contains(lcs, lc), Equals, true) } func (s *LXCSuite) TestClone(c *C) { // Test the cloning of an existing container. lc1 := s.createContainer(c) defer func() { c.Assert(lc1.Destroy(), IsNil) }() lcs, _ := s.factory.List() oldLen := len(lcs) lc2, err := lc1.Clone("golxcclone", nil, nil) c.Assert(err, IsNil) c.Assert(lc2.IsConstructed(), Equals, true) defer func() { c.Assert(lc2.Destroy(), IsNil) }() lcs, _ = s.factory.List() newLen := len(lcs) c.Assert(newLen == oldLen+1, Equals, true) c.Assert(contains(lcs, lc1), Equals, true) c.Assert(contains(lcs, lc2), Equals, true) } func (s *LXCSuite) TestCloneNotCreated(c *C) { // Test the cloning of a non-existing container. lc := s.factory.New("golxc") c.Assert(lc.IsConstructed(), Equals, false) _, err := lc.Clone("golxcclone", nil, nil) c.Assert(err, ErrorMatches, "container .* is not yet created") } func (s *LXCSuite) TestStartStop(c *C) { // Test starting and stopping a container. lc := s.createContainer(c) defer func() { c.Assert(lc.Destroy(), IsNil) }() c.Assert(lc.Start("", ""), IsNil) c.Assert(lc.IsRunning(), Equals, true) c.Assert(lc.Stop(), IsNil) c.Assert(lc.IsRunning(), Equals, false) } func (s *LXCSuite) TestStartNotCreated(c *C) { // Test that a non-existing container can't be started. lc := s.factory.New("golxc") c.Assert(lc.IsConstructed(), Equals, false) c.Assert(lc.Start("", ""), ErrorMatches, "container .* is not yet created") } func (s *LXCSuite) TestStopNotRunning(c *C) { // Test that a not running container can't be stopped. lc := s.createContainer(c) defer func() { c.Assert(lc.Destroy(), IsNil) }() c.Assert(lc.Stop(), IsNil) } func (s *LXCSuite) TestWait(c *C) { // Test waiting for one of a number of states of a container. // ATTN: Using a not reached state blocks the test until timeout! lc := s.factory.New("golxc") c.Assert(lc.IsConstructed(), Equals, false) c.Assert(lc.Wait(), ErrorMatches, "no states specified") c.Assert(lc.Wait(golxc.StateStopped), IsNil) c.Assert(lc.Wait(golxc.StateStopped, golxc.StateRunning), IsNil) c.Assert(lc.Create("", "ubuntu", nil, nil), IsNil) defer func() { c.Assert(lc.Destroy(), IsNil) }() go func() { c.Assert(lc.Start("", ""), IsNil) }() c.Assert(lc.Wait(golxc.StateRunning), IsNil) } func (s *LXCSuite) TestFreezeUnfreeze(c *C) { // Test the freezing and unfreezing of a started container. lc := s.createContainer(c) defer func() { c.Assert(lc.Destroy(), IsNil) }() c.Assert(lc.Start("", ""), IsNil) defer func() { c.Assert(lc.Stop(), IsNil) }() c.Assert(lc.IsRunning(), Equals, true) c.Assert(lc.Freeze(), IsNil) c.Assert(lc.IsRunning(), Equals, false) c.Assert(lc.Unfreeze(), IsNil) c.Assert(lc.IsRunning(), Equals, true) } func (s *LXCSuite) TestFreezeNotStarted(c *C) { // Test that a not running container can't be frozen. lc := s.createContainer(c) defer func() { c.Assert(lc.Destroy(), IsNil) }() c.Assert(lc.Freeze(), ErrorMatches, "container .* is not running") } func (s *LXCSuite) TestFreezeNotCreated(c *C) { // Test that a non-existing container can't be frozen. lc := s.factory.New("golxc") c.Assert(lc.IsConstructed(), Equals, false) c.Assert(lc.Freeze(), ErrorMatches, "container .* is not yet created") } func (s *LXCSuite) TestUnfreezeNotCreated(c *C) { // Test that a non-existing container can't be unfrozen. lc := s.factory.New("golxc") c.Assert(lc.IsConstructed(), Equals, false) c.Assert(lc.Unfreeze(), ErrorMatches, "container .* is not yet created") } func (s *LXCSuite) TestUnfreezeNotFrozen(c *C) { // Test that a running container can't be unfrozen. lc := s.createContainer(c) defer func() { c.Assert(lc.Destroy(), IsNil) }() c.Assert(lc.Start("", ""), IsNil) defer func() { c.Assert(lc.Stop(), IsNil) }() c.Assert(lc.Unfreeze(), ErrorMatches, "container .* is not frozen") } type commandArgs struct { logging.LoggingSuite } var _ = Suite(&commandArgs{}) func (s *commandArgs) TestCreateArgs(c *C) { s.PatchValue(&golxc.ContainerDir, c.MkDir()) testing.PatchExecutableAsEchoArgs(c, s, "lxc-create") factory := golxc.Factory() container := factory.New("test") err := container.Create( "config-file", "template", []string{"extra-1", "extra-2"}, []string{"template-1", "template-2"}, ) c.Assert(err, IsNil) testing.AssertEchoArgs( c, "lxc-create", "-n", "test", "-t", "template", "-f", "config-file", "extra-1", "extra-2", "--", "template-1", "template-2") } func (s *commandArgs) TestCloneArgs(c *C) { dir := c.MkDir() s.PatchValue(&golxc.ContainerDir, dir) // Patch lxc-info too as clone checks to see if it is running. testing.PatchExecutableAsEchoArgs(c, s, "lxc-info") testing.PatchExecutableAsEchoArgs(c, s, "lxc-clone") factory := golxc.Factory() container := factory.New("test") // Make the rootfs for the "test" container so it thinks it is created. rootfs := filepath.Join(dir, "test", "rootfs") err := os.MkdirAll(rootfs, 0755) c.Assert(err, IsNil) c.Assert(rootfs, jc.IsDirectory) c.Assert(container.IsConstructed(), jc.IsTrue) clone, err := container.Clone( "name", []string{"extra-1", "extra-2"}, []string{"template-1", "template-2"}, ) c.Assert(err, IsNil) testing.AssertEchoArgs( c, "lxc-clone", "-o", "test", "-n", "name", "extra-1", "extra-2", "--", "template-1", "template-2") c.Assert(clone.Name(), Equals, "name") } ���������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/golxc/golxc.go���������������������������������������������������0000644�0000153�0000161�00000030635�12321735734�021552� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. // golxc - Go package to interact with Linux Containers (LXC). // // https://launchpad.net/golxc/ // package golxc import ( "fmt" "os" "os/exec" "path" "strconv" "strings" "github.com/juju/loggo" ) var logger = loggo.GetLogger("golxc") // Error reports the failure of a LXC command. type Error struct { Name string Err error Output []string } func (e Error) Error() string { if e.Output == nil { return fmt.Sprintf("error executing %q: %v", e.Name, e.Err) } if len(e.Output) == 1 { return fmt.Sprintf("error executing %q: %v", e.Name, e.Output[0]) } return fmt.Sprintf("error executing %q: %s", e.Name, strings.Join(e.Output, "; ")) } // State represents a container state. type State string const ( StateUnknown State = "UNKNOWN" StateStopped State = "STOPPED" StateStarting State = "STARTING" StateRunning State = "RUNNING" StateAborting State = "ABORTING" StateStopping State = "STOPPING" ) // LogLevel represents a container's log level. type LogLevel string const ( LogDebug LogLevel = "DEBUG" LogInfo LogLevel = "INFO" LogNotice LogLevel = "NOTICE" LogWarning LogLevel = "WARN" LogError LogLevel = "ERROR" LogCritical LogLevel = "CRIT" LogFatal LogLevel = "FATAL" ) // Container represents a linux container instance and provides // operations to create, maintain and destroy the container. type Container interface { // Name returns the name of the container. Name() string // Create creates a new container based on the given template. Create(configFile, template string, extraArgs []string, templateArgs []string) error // Start runs the container as a daemon. Start(configFile, consoleFile string) error // Stop terminates the running container. Stop() error // Clone creates a copy of the container, giving the copy the specified name. Clone(name string, extraArgs []string, templateArgs []string) (Container, error) // Freeze freezes all the container's processes. Freeze() error // Unfreeze thaws all frozen container's processes. Unfreeze() error // Destroy stops and removes the container. Destroy() error // Wait waits for one of the specified container states. Wait(states ...State) error // Info returns the status and the process id of the container. Info() (State, int, error) // IsConstructed checks if the container image exists. IsConstructed() bool // IsRunning checks if the state of the container is 'RUNNING'. IsRunning() bool // String returns information about the container, like the name, state, // and process id. String() string // LogFile returns the current filename used for the LogFile. LogFile() string // LogLevel returns the current logging level (only used if the // LogFile is not ""). LogLevel() LogLevel // SetLogFile sets both the LogFile and LogLevel. SetLogFile(filename string, level LogLevel) } // ContainerFactory represents the methods used to create Containers. type ContainerFactory interface { // New returns a container instance which can then be used for operations // like Create(), Start(), Stop() or Destroy(). New(string) Container // List returns all the existing containers on the system. List() ([]Container, error) } // Factory provides the standard ContainerFactory. func Factory() ContainerFactory { return &containerFactory{} } const DefaultLXCDir = "/var/lib/lxc" var ContainerDir = DefaultLXCDir type container struct { name string logFile string logLevel LogLevel // Newer LXC libraries can have containers in non-default locations. The // containerDir is the directory that is the 'home' of this container. containerDir string } type containerFactory struct{} func (*containerFactory) New(name string) Container { return &container{ name: name, logLevel: LogWarning, containerDir: ContainerDir, } } // List returns all the existing containers on the system. func (factory *containerFactory) List() ([]Container, error) { out, err := run("lxc-ls", "-1") if err != nil { return nil, err } names := nameSet(out) containers := make([]Container, len(names)) for i, name := range names { containers[i] = factory.New(name) } return containers, nil } // Name returns the name of the container. func (c *container) Name() string { return c.name } // LogFile returns the current filename used for the LogFile. func (c *container) LogFile() string { return c.logFile } // LogLevel returns the current logging level, this is only used if the // LogFile is not "". func (c *container) LogLevel() LogLevel { return c.logLevel } // SetLogFile sets both the LogFile and LogLevel. func (c *container) SetLogFile(filename string, level LogLevel) { c.logFile = filename c.logLevel = level } // Create creates a new container based on the given template. func (c *container) Create(configFile, template string, extraArgs []string, templateArgs []string) error { if c.IsConstructed() { return fmt.Errorf("container %q is already created", c.Name()) } args := []string{ "-n", c.name, "-t", template, } if configFile != "" { args = append(args, "-f", configFile) } if len(extraArgs) != 0 { args = append(args, extraArgs...) } if len(templateArgs) != 0 { // Must be done in two steps due to current language implementation details. args = append(args, "--") args = append(args, templateArgs...) } _, err := run("lxc-create", args...) if err != nil { return err } return nil } // Start runs the container as a daemon. func (c *container) Start(configFile, consoleFile string) error { if !c.IsConstructed() { return fmt.Errorf("container %q is not yet created", c.name) } args := []string{ "--daemon", "-n", c.name, } if configFile != "" { args = append(args, "-f", configFile) } if consoleFile != "" { args = append(args, "-c", consoleFile) } if c.logFile != "" { args = append(args, "-o", c.logFile, "-l", string(c.logLevel)) } _, err := run("lxc-start", args...) if err != nil { return err } if err := c.Wait(StateRunning, StateStopped); err != nil { return err } if !c.IsRunning() { return fmt.Errorf("container failed to start") } return nil } // Stop terminates the running container. func (c *container) Stop() error { if !c.IsConstructed() { return fmt.Errorf("container %q is not yet created", c.name) } // If the container is not running, we are done. if !c.IsRunning() { return nil } args := []string{ "-n", c.name, } if c.logFile != "" { args = append(args, "-o", c.logFile, "-l", string(c.logLevel)) } _, err := run("lxc-stop", args...) if err != nil { return err } return c.Wait(StateStopped) } // Clone creates a copy of the container, it gets the given name. func (c *container) Clone(name string, extraArgs []string, templateArgs []string) (Container, error) { if !c.IsConstructed() { return nil, fmt.Errorf("container %q is not yet created", c.name) } if c.IsRunning() { return nil, fmt.Errorf("cannot clone a running container") } cc := &container{ name: name, logLevel: c.logLevel, containerDir: c.containerDir, } if cc.IsConstructed() { return cc, nil } args := []string{ "-o", c.name, "-n", name, } if len(extraArgs) != 0 { args = append(args, extraArgs...) } if len(templateArgs) != 0 { // Must be done in two steps due to current language implementation details. args = append(args, "--") args = append(args, templateArgs...) } _, err := run("lxc-clone", args...) if err != nil { return nil, err } return cc, nil } // Freeze freezes all the container's processes. func (c *container) Freeze() error { if !c.IsConstructed() { return fmt.Errorf("container %q is not yet created", c.name) } if !c.IsRunning() { return fmt.Errorf("container %q is not running", c.name) } args := []string{ "-n", c.name, } if c.logFile != "" { args = append(args, "-o", c.logFile, "-l", string(c.logLevel)) } _, err := run("lxc-freeze", args...) if err != nil { return err } return nil } // Unfreeze thaws all frozen container's processes. func (c *container) Unfreeze() error { if !c.IsConstructed() { return fmt.Errorf("container %q is not yet created", c.name) } if c.IsRunning() { return fmt.Errorf("container %q is not frozen", c.name) } args := []string{ "-n", c.name, } if c.logFile != "" { args = append(args, "-o", c.logFile, "-l", string(c.logLevel)) } _, err := run("lxc-unfreeze", args...) if err != nil { return err } return nil } // Destroy stops and removes the container. func (c *container) Destroy() error { if !c.IsConstructed() { return fmt.Errorf("container %q is not yet created", c.name) } if err := c.Stop(); err != nil { return err } _, err := run("lxc-destroy", "-n", c.name) if err != nil { return err } return nil } // Wait waits for one of the specified container states. func (c *container) Wait(states ...State) error { if len(states) == 0 { return fmt.Errorf("no states specified") } stateStrs := make([]string, len(states)) for i, state := range states { stateStrs[i] = string(state) } waitStates := strings.Join(stateStrs, "|") _, err := run("lxc-wait", "-n", c.name, "-s", waitStates) if err != nil { return err } return nil } // Info returns the status and the process id of the container. func (c *container) Info() (State, int, error) { out, err := run("lxc-info", "-n", c.name) if err != nil { return StateUnknown, -1, err } kv := keyValues(out, ": ") state := State(kv["state"]) pid, err := strconv.Atoi(kv["pid"]) if err != nil { return StateUnknown, -1, fmt.Errorf("cannot read the pid: %v", err) } return state, pid, nil } // IsConstructed checks if the container image exists. func (c *container) IsConstructed() bool { fi, err := os.Stat(c.rootfs()) if err != nil { return false } return fi.IsDir() } // IsRunning checks if the state of the container is 'RUNNING'. func (c *container) IsRunning() bool { state, _, err := c.Info() if err != nil { return false } return state == StateRunning } // String returns information about the container. func (c *container) String() string { state, pid, err := c.Info() if err != nil { return fmt.Sprintf("cannot retrieve container info for %q: %v", c.name, err) } return fmt.Sprintf("container %q (%s, pid %d)", c.name, state, pid) } // containerHome returns the name of the container directory. func (c *container) containerHome() string { return path.Join(c.containerDir, c.name) } // rootfs returns the name of the directory containing the // root filesystem of the container. func (c *container) rootfs() string { return path.Join(c.containerHome(), "rootfs") } // run executes the passed command and returns the out. func run(name string, args ...string) (string, error) { logger := loggo.GetLogger(fmt.Sprintf("golxc.run.%s", name)) logger.Tracef("run: %s %v", name, args) cmd := exec.Command(name, args...) // LXC tools do not use stdout and stderr in a predictable // way; based on experimentation, the most convenient // solution is to combine them and leave the client to // determine sanity as best it can. out, err := cmd.CombinedOutput() result := string(out) if err != nil { logger.Tracef("run failed output: %s", result) return "", runError(name, err, out) } logger.Tracef("run successful output: %s", result) return result, nil } // runError creates an error if run fails. func runError(name string, err error, out []byte) error { e := &Error{name, err, nil} for _, l := range strings.Split(string(out), "\n") { if strings.HasPrefix(l, name+": ") { // LXC tools do not always print their output with // the command name as prefix. The name is part of // the error struct, so stip it from the output if // printed. l = l[len(name)+2:] } if l != "" { e.Output = append(e.Output, l) } } return e } // keyValues retrieves key/value pairs out of a command out. func keyValues(raw string, sep string) map[string]string { kv := map[string]string{} lines := strings.Split(raw, "\n") for _, line := range lines { parts := strings.SplitN(line, sep, 2) if len(parts) == 2 { kv[strings.ToLower(parts[0])] = strings.TrimSpace(parts[1]) } } return kv } // nameSet retrieves a set of names out of a command out. func nameSet(raw string) []string { collector := map[string]struct{}{} set := []string{} lines := strings.Split(raw, "\n") for _, line := range lines { name := strings.TrimSpace(line) if name != "" { collector[name] = struct{}{} } } for name := range collector { set = append(set, name) } return set } ���������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/golxc/network.go�������������������������������������������������0000644�0000153�0000161�00000003500�12321735734�022116� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. package golxc import ( "fmt" "strings" ) const ( defaultAddr = "10.0.3.1" defaultBridge = "lxcbr0" ) // StartNetwork starts the lxc network subsystem. func StartNetwork() error { goal, _, err := networkStatus() if err != nil { return err } if goal != "start" { _, err := run("start", "lxc-net") if err != nil { return err } } return nil } // StopNetwork stops the lxc network subsystem. func StopNetwork() error { goal, _, err := networkStatus() if err != nil { return err } if goal != "stop" { _, err := run("stop", "lxc-net") if err != nil { return err } } return nil } // IsNotworkRunning checks if the lxc network subsystem // is running. func IsNetworkRunning() (bool, error) { _, status, err := networkStatus() if err != nil { return false, err } return status == "running", nil } // NetworkAttributes returns the lxc network attributes: // starting IP address and bridge name. func NetworkAttributes() (addr, bridge string, err error) { config, err := ReadConf() if err != nil { return "", "", err } addr = config["address"] if addr == "" { addr = defaultAddr } bridge = config["bridge"] if bridge == "" { bridge = defaultBridge } return addr, bridge, nil } // networkStatus returns the status of the lxc network subsystem. func networkStatus() (goal, status string, err error) { output, err := run("status", "lxc-net") if err != nil { return "", "", err } fields := strings.Fields(output) if len(fields) != 2 { return "", "", fmt.Errorf("unexpected status output: %q", output) } fields = strings.Split(fields[1], "/") if len(fields) != 2 { return "", "", fmt.Errorf("unexpected status output: %q", output) } return fields[0], fields[1], nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/golxc/package_test.go��������������������������������������������0000644�0000153�0000161�00000000345�12321735734�023063� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. package golxc_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/golxc/golxc_test.sh����������������������������������������������0000755�0000153�0000161�00000000243�12321735734�022611� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh sudo sh -c " export GOMAXPROCS=\"$GOMAXPROCS\" export GOPATH=\"$GOPATH\" export GOROOT=\"$GOROOT\" export PATH=\"$PATH\" go test $* "�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/golxc/COPYING����������������������������������������������������0000644�0000153�0000161�00000104513�12321735734�021137� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/�����������������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�020104� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/COPYING.LESSER���������������������������������������������0000644�0000153�0000161�00000016743�12321735747�022146� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �����������������������������juju-core_1.18.1/src/launchpad.net/goose/goose.go���������������������������������������������������0000644�0000153�0000161�00000000177�12321735747�021554� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. package goose �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/LICENSE����������������������������������������������������0000644�0000153�0000161�00000001235�12321735747�021112� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Goose - Go bindings for talking to OpenStack Copyright 2012-2013, Canonical Ltd. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. See both COPYING and COPYING.LESSER for the full terms of the GNU Lesser General Public License. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/glance/����������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�021335� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/glance/glance.go�������������������������������������������0000644�0000153�0000161�00000005446�12321735747�023126� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// goose/glance - Go package to interact with OpenStack Image Service (Glance) API. // See http://docs.openstack.org/api/openstack-image-service/2.0/content/. package glance import ( "fmt" "launchpad.net/goose/client" "launchpad.net/goose/errors" goosehttp "launchpad.net/goose/http" "net/http" ) // API URL parts. const ( apiImages = "/images" apiImagesDetail = "/images/detail" ) // Client provides a means to access the OpenStack Image Service. type Client struct { client client.Client } // New creates a new Client. func New(client client.Client) *Client { return &Client{client} } // Link describes a link to an image in OpenStack. type Link struct { Href string Rel string Type string } // Image describes an OpenStack image. type Image struct { Id string Name string Links []Link } // ListImages lists IDs, names, and links for available images. func (c *Client) ListImages() ([]Image, error) { var resp struct { Images []Image } requestData := goosehttp.RequestData{RespValue: &resp, ExpectedStatus: []int{http.StatusOK}} err := c.client.SendRequest(client.GET, "compute", apiImages, &requestData) if err != nil { return nil, errors.Newf(err, "failed to get list of images") } return resp.Images, nil } // ImageMetadata describes metadata of an image type ImageMetadata struct { Architecture string State string `json:"image_state"` Location string `json:"image_location"` KernelId interface{} `json:"kernel_id"` ProjectId interface{} `json:"project_id"` RAMDiskId interface{} `json:"ramdisk_id"` OwnerId interface{} `json:"owner_id"` } // ImageDetail describes extended information about an image. type ImageDetail struct { Id string Name string Created string Updated string Progress int Status string MinimumRAM int `json:"minRam"` MinimumDisk int `json:"minDisk"` Links []Link Metadata ImageMetadata } // ListImageDetails lists all details for available images. func (c *Client) ListImagesDetail() ([]ImageDetail, error) { var resp struct { Images []ImageDetail } requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.GET, "compute", apiImagesDetail, &requestData) if err != nil { return nil, errors.Newf(err, "failed to get list of image details") } return resp.Images, nil } // GetImageDetail lists details of the specified image. func (c *Client) GetImageDetail(imageId string) (*ImageDetail, error) { var resp struct { Image ImageDetail } url := fmt.Sprintf("%s/%s", apiImages, imageId) requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.GET, "compute", url, &requestData) if err != nil { return nil, errors.Newf(err, "failed to get details of imageId: %s", imageId) } return &resp.Image, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/glance/glance_test.go��������������������������������������0000644�0000153�0000161�00000004146�12321735747�024161� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package glance_test import ( "flag" . "launchpad.net/gocheck" "launchpad.net/goose/client" "launchpad.net/goose/glance" "launchpad.net/goose/identity" "testing" ) func Test(t *testing.T) { TestingT(t) } var live = flag.Bool("live", false, "Include live OpenStack (Canonistack) tests") type GlanceSuite struct { glance *glance.Client } func (s *GlanceSuite) SetUpSuite(c *C) { if !*live { c.Skip("-live not provided") } cred, err := identity.CompleteCredentialsFromEnv() c.Assert(err, IsNil) client := client.NewClient(cred, identity.AuthUserPass, nil) c.Assert(err, IsNil) s.glance = glance.New(client) } var suite = Suite(&GlanceSuite{}) func (s *GlanceSuite) TestListImages(c *C) { images, err := s.glance.ListImages() c.Assert(err, IsNil) c.Assert(images, Not(HasLen), 0) for _, ir := range images { c.Assert(ir.Id, Not(Equals), "") c.Assert(ir.Name, Not(Equals), "") for _, l := range ir.Links { c.Assert(l.Href, Matches, "https?://.*") c.Assert(l.Rel, Matches, "self|bookmark|alternate") } } } func (s *GlanceSuite) TestListImagesDetail(c *C) { images, err := s.glance.ListImagesDetail() c.Assert(err, IsNil) c.Assert(images, Not(HasLen), 0) for _, ir := range images { c.Assert(ir.Created, Matches, `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*`) c.Assert(ir.Updated, Matches, `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*`) c.Assert(ir.Id, Not(Equals), "") c.Assert(ir.Status, Not(Equals), "") c.Assert(ir.Name, Not(Equals), "") for _, l := range ir.Links { c.Assert(l.Href, Matches, "https?://.*") c.Assert(l.Rel, Matches, "self|bookmark|alternate") } m := ir.Metadata c.Assert(m.Architecture, Matches, "i386|x86_64|") c.Assert(m.State, Matches, "active|available|") } } func (s *GlanceSuite) TestGetImageDetail(c *C) { images, err := s.glance.ListImagesDetail() c.Assert(err, IsNil) firstImage := images[0] ir, err := s.glance.GetImageDetail(firstImage.Id) c.Assert(err, IsNil) c.Assert(ir.Created, Matches, firstImage.Created) c.Assert(ir.Updated, Matches, firstImage.Updated) c.Assert(ir.Name, Equals, firstImage.Name) c.Assert(ir.Status, Equals, firstImage.Status) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/goose_test.go����������������������������������������������0000644�0000153�0000161�00000000254�12321735747�022607� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goose import ( . "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { TestingT(t) } type GooseTestSuite struct { } var _ = Suite(&GooseTestSuite{}) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/version_test.go��������������������������������������������0000644�0000153�0000161�00000000527�12321735747�023163� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. package goose import ( . "launchpad.net/gocheck" ) type VersionTestSuite struct { } var _ = Suite(&VersionTestSuite{}) func (s *VersionTestSuite) TestStringMatches(c *C) { c.Assert(Version, Equals, VersionNumber.String()) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/��������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�021735� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/identity.go���������������������������������������0000644�0000153�0000161�00000006637�12321735747�024131� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// goose/identity - Go package to interact with OpenStack Identity (Keystone) API. package identity import ( "fmt" "os" "reflect" goosehttp "launchpad.net/goose/http" ) // AuthMode defines the authentication method to use (see Auth* // constants below). type AuthMode int const ( AuthLegacy = AuthMode(iota) // Legacy authentication AuthUserPass // Username + password authentication AuthKeyPair // Access/secret key pair authentication ) func (a AuthMode) String() string { switch a { case AuthKeyPair: return "Access/Secret Key Authentication" case AuthLegacy: return "Legacy Authentication" case AuthUserPass: return "Username/password Authentication" } panic(fmt.Errorf("Unknown athentication type: %d", a)) } type ServiceURLs map[string]string // AuthDetails defines all the necessary information, needed for an // authenticated session with OpenStack. type AuthDetails struct { Token string TenantId string UserId string RegionServiceURLs map[string]ServiceURLs // Service type to endpoint URLs for each region } // Credentials defines necessary parameters for authentication. type Credentials struct { URL string // The URL to authenticate against User string // The username to authenticate as Secrets string // The secrets to pass Region string // Region to send requests to TenantName string // The tenant information for this connection } // Authenticator is implemented by each authentication method. type Authenticator interface { Auth(creds *Credentials) (*AuthDetails, error) } // getConfig returns the value of the first available environment // variable, among the given ones. func getConfig(envVars ...string) (value string) { value = "" for _, v := range envVars { value = os.Getenv(v) if value != "" { break } } return } // CredentialsFromEnv creates and initializes the credentials from the // environment variables. func CredentialsFromEnv() *Credentials { return &Credentials{ URL: getConfig("OS_AUTH_URL"), User: getConfig("OS_USERNAME", "NOVA_USERNAME", "OS_ACCESS_KEY", "NOVA_API_KEY"), Secrets: getConfig("OS_PASSWORD", "NOVA_PASSWORD", "OS_SECRET_KEY", "EC2_SECRET_KEYS", "AWS_SECRET_ACCESS_KEY"), Region: getConfig("OS_REGION_NAME", "NOVA_REGION"), TenantName: getConfig("OS_TENANT_NAME", "NOVA_PROJECT_ID"), } } // CompleteCredentialsFromEnv gets and verifies all the required // authentication parameters have values in the environment. func CompleteCredentialsFromEnv() (cred *Credentials, err error) { cred = CredentialsFromEnv() v := reflect.ValueOf(cred).Elem() t := v.Type() for i := 0; i < v.NumField(); i++ { f := v.Field(i) if f.String() == "" { err = fmt.Errorf("required environment variable not set for credentials attribute: %s", t.Field(i).Name) } } return } // NewAuthenticator creates an authenticator matching the supplied AuthMode. // The httpclient is allowed to be nil, the Authenticator will just use the // default http.Client func NewAuthenticator(authMode AuthMode, httpClient *goosehttp.Client) Authenticator { if httpClient == nil { httpClient = goosehttp.New() } switch authMode { default: panic(fmt.Errorf("Invalid identity authorisation mode: %d", authMode)) case AuthLegacy: return &Legacy{client: httpClient} case AuthUserPass: return &UserPass{client: httpClient} case AuthKeyPair: return &KeyPair{client: httpClient} } } �������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/identity_test.go����������������������������������0000644�0000153�0000161�00000013673�12321735747�025166� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identity import ( . "launchpad.net/gocheck" goosehttp "launchpad.net/goose/http" "launchpad.net/goose/testing/envsuite" "os" ) type CredentialsTestSuite struct { // Isolate all of these tests from the real Environ. envsuite.EnvSuite } type NewAuthenticatorSuite struct{} var _ = Suite(&CredentialsTestSuite{}) var _ = Suite(&NewAuthenticatorSuite{}) func (s *CredentialsTestSuite) TestCredentialsFromEnv(c *C) { var scenarios = []struct { summary string env map[string]string username string password string tenant string region string authURL string }{ {summary: "Old 'NOVA' style creds", env: map[string]string{ "NOVA_USERNAME": "test-user", "NOVA_PASSWORD": "test-pass", "NOVA_API_KEY": "test-access-key", "EC2_SECRET_KEYS": "test-secret-key", "NOVA_PROJECT_ID": "tenant-name", "NOVA_REGION": "region", }, username: "test-user", password: "test-pass", tenant: "tenant-name", region: "region", }, {summary: "New 'OS' style environment", env: map[string]string{ "OS_USERNAME": "test-user", "OS_PASSWORD": "test-pass", "OS_ACCESS_KEY": "test-access-key", "OS_SECRET_KEY": "test-secret-key", "OS_TENANT_NAME": "tenant-name", "OS_REGION_NAME": "region", }, username: "test-user", password: "test-pass", tenant: "tenant-name", region: "region", }, } for _, scenario := range scenarios { for key, value := range scenario.env { os.Setenv(key, value) } creds := CredentialsFromEnv() c.Check(creds.URL, Equals, scenario.authURL) c.Check(creds.User, Equals, scenario.username) c.Check(creds.Secrets, Equals, scenario.password) c.Check(creds.Region, Equals, scenario.region) c.Check(creds.TenantName, Equals, scenario.tenant) } } func (s *CredentialsTestSuite) TestCompleteCredentialsFromEnvValid(c *C) { env := map[string]string{ "OS_AUTH_URL": "http://auth", "OS_USERNAME": "test-user", "OS_PASSWORD": "test-pass", "OS_ACCESS_KEY": "test-access-key", "OS_SECRET_KEY": "test-secret-key", "OS_TENANT_NAME": "tenant-name", "OS_REGION_NAME": "region", } for key, value := range env { os.Setenv(key, value) } creds, err := CompleteCredentialsFromEnv() c.Assert(err, IsNil) c.Check(creds.URL, Equals, "http://auth") c.Check(creds.User, Equals, "test-user") c.Check(creds.Secrets, Equals, "test-pass") c.Check(creds.Region, Equals, "region") c.Check(creds.TenantName, Equals, "tenant-name") } // An error is returned if not all required environment variables are set. func (s *CredentialsTestSuite) TestCompleteCredentialsFromEnvInvalid(c *C) { env := map[string]string{ "OS_AUTH_URL": "http://auth", "OS_USERNAME": "test-user", "OS_ACCESS_KEY": "test-access-key", "OS_TENANT_NAME": "tenant-name", "OS_REGION_NAME": "region", } for key, value := range env { os.Setenv(key, value) } _, err := CompleteCredentialsFromEnv() c.Assert(err, Not(IsNil)) c.Assert(err.Error(), Matches, "required environment variable not set.*: Secrets") } func (s *CredentialsTestSuite) TestCompleteCredentialsFromEnvKeypair(c *C) { env := map[string]string{ "OS_AUTH_URL": "http://auth", "OS_USERNAME": "", "OS_PASSWORD": "", "OS_ACCESS_KEY": "test-access-key", "OS_SECRET_KEY": "test-secret-key", "OS_TENANT_NAME": "tenant-name", "OS_REGION_NAME": "region", } for key, value := range env { os.Setenv(key, value) } creds, err := CompleteCredentialsFromEnv() c.Assert(err, IsNil) c.Check(creds.URL, Equals, "http://auth") c.Check(creds.User, Equals, "test-access-key") c.Check(creds.Secrets, Equals, "test-secret-key") c.Check(creds.Region, Equals, "region") c.Check(creds.TenantName, Equals, "tenant-name") } func (s *CredentialsTestSuite) TestCompleteCredentialsFromEnvKeypairCompatibleEnvVars(c *C) { env := map[string]string{ "OS_AUTH_URL": "http://auth", "OS_USERNAME": "", "OS_PASSWORD": "", "NOVA_API_KEY": "test-access-key", "EC2_SECRET_KEYS": "test-secret-key", "OS_TENANT_NAME": "tenant-name", "OS_REGION_NAME": "region", } for key, value := range env { os.Setenv(key, value) } creds, err := CompleteCredentialsFromEnv() c.Assert(err, IsNil) c.Check(creds.URL, Equals, "http://auth") c.Check(creds.User, Equals, "test-access-key") c.Check(creds.Secrets, Equals, "test-secret-key") c.Check(creds.Region, Equals, "region") c.Check(creds.TenantName, Equals, "tenant-name") } func (s *NewAuthenticatorSuite) TestUserPassNoHTTPClient(c *C) { auth := NewAuthenticator(AuthUserPass, nil) userAuth, ok := auth.(*UserPass) c.Assert(ok, Equals, true) c.Assert(userAuth.client, NotNil) } func (s *NewAuthenticatorSuite) TestUserPassCustomHTTPClient(c *C) { httpClient := goosehttp.New() auth := NewAuthenticator(AuthUserPass, httpClient) userAuth, ok := auth.(*UserPass) c.Assert(ok, Equals, true) c.Assert(userAuth.client, Equals, httpClient) } func (s *NewAuthenticatorSuite) TestKeyPairNoHTTPClient(c *C) { auth := NewAuthenticator(AuthKeyPair, nil) keyPairAuth, ok := auth.(*KeyPair) c.Assert(ok, Equals, true) c.Assert(keyPairAuth.client, NotNil) } func (s *NewAuthenticatorSuite) TestKeyPairCustomHTTPClient(c *C) { httpClient := goosehttp.New() auth := NewAuthenticator(AuthKeyPair, httpClient) keyPairAuth, ok := auth.(*KeyPair) c.Assert(ok, Equals, true) c.Assert(keyPairAuth.client, Equals, httpClient) } func (s *NewAuthenticatorSuite) TestLegacyNoHTTPClient(c *C) { auth := NewAuthenticator(AuthLegacy, nil) legacyAuth, ok := auth.(*Legacy) c.Assert(ok, Equals, true) c.Assert(legacyAuth.client, NotNil) } func (s *NewAuthenticatorSuite) TestLegacyCustomHTTPClient(c *C) { httpClient := goosehttp.New() auth := NewAuthenticator(AuthLegacy, httpClient) legacyAuth, ok := auth.(*Legacy) c.Assert(ok, Equals, true) c.Assert(legacyAuth.client, Equals, httpClient) } func (s *NewAuthenticatorSuite) TestUnknownMode(c *C) { c.Assert(func() { NewAuthenticator(1235, nil) }, PanicMatches, "Invalid identity authorisation mode: 1235") } ���������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/legacy.go�����������������������������������������0000644�0000153�0000161�00000003067�12321735747�023536� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identity import ( "fmt" "io/ioutil" goosehttp "launchpad.net/goose/http" "net/http" ) type Legacy struct { client *goosehttp.Client } func (l *Legacy) Auth(creds *Credentials) (*AuthDetails, error) { if l.client == nil { l.client = goosehttp.New() } request, err := http.NewRequest("GET", creds.URL, nil) if err != nil { return nil, err } request.Header.Set("X-Auth-User", creds.User) request.Header.Set("X-Auth-Key", creds.Secrets) response, err := l.client.Do(request) defer response.Body.Close() if err != nil { return nil, err } if response.StatusCode != http.StatusNoContent { content, _ := ioutil.ReadAll(response.Body) return nil, fmt.Errorf("Failed to Authenticate (code %d %s): %s", response.StatusCode, response.Status, content) } details := &AuthDetails{} details.Token = response.Header.Get("X-Auth-Token") if details.Token == "" { return nil, fmt.Errorf("Did not get valid Token from auth request") } details.RegionServiceURLs = make(map[string]ServiceURLs) serviceURLs := make(ServiceURLs) // Legacy authentication doesn't require a region so use "". details.RegionServiceURLs[""] = serviceURLs nova_url := response.Header.Get("X-Server-Management-Url") if nova_url == "" { return nil, fmt.Errorf("Did not get valid nova management URL from auth request") } serviceURLs["compute"] = nova_url swift_url := response.Header.Get("X-Storage-Url") if swift_url == "" { return nil, fmt.Errorf("Did not get valid swift management URL from auth request") } serviceURLs["object-store"] = swift_url return details, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/keystone.go���������������������������������������0000644�0000153�0000161�00000004627�12321735747�024136� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identity import ( "fmt" goosehttp "launchpad.net/goose/http" ) type endpoint struct { AdminURL string `json:"adminURL"` InternalURL string `json:"internalURL"` PublicURL string `json:"publicURL"` Region string `json:"region"` } type serviceResponse struct { Name string `json:"name"` Type string `json:"type"` Endpoints []endpoint } type tokenResponse struct { Expires string `json:"expires"` Id string `json:"id"` // Actual token string Tenant struct { Id string `json:"id"` Name string `json:"name"` // Description is a pointer since it may be null and this breaks Go < 1.1 Description *string `json:"description"` Enabled bool `json:"enabled"` } `json:"tenant"` } type roleResponse struct { Id string `json:"id"` Name string `json:"name"` TenantId string `json:"tenantId"` } type userResponse struct { Id string `json:"id"` Name string `json:"name"` Roles []roleResponse `json:"roles"` } type accessWrapper struct { Access accessResponse `json:"access"` } type accessResponse struct { ServiceCatalog []serviceResponse `json:"serviceCatalog"` Token tokenResponse `json:"token"` User userResponse `json:"user"` } // keystoneAuth authenticates to OpenStack cloud using keystone v2 authentication. // // Uses `client` to submit HTTP requests to `URL` // and posts `auth_data` as JSON. func keystoneAuth(client *goosehttp.Client, auth_data interface{}, URL string) (*AuthDetails, error) { var accessWrapper accessWrapper requestData := goosehttp.RequestData{ReqValue: auth_data, RespValue: &accessWrapper} err := client.JsonRequest("POST", URL, "", &requestData, nil) if err != nil { return nil, err } details := &AuthDetails{} access := accessWrapper.Access respToken := access.Token if respToken.Id == "" { return nil, fmt.Errorf("authentication failed") } details.Token = respToken.Id details.TenantId = respToken.Tenant.Id details.UserId = access.User.Id details.RegionServiceURLs = make(map[string]ServiceURLs, len(access.ServiceCatalog)) for _, service := range access.ServiceCatalog { for i, e := range service.Endpoints { endpointURLs, ok := details.RegionServiceURLs[e.Region] if !ok { endpointURLs = make(ServiceURLs) details.RegionServiceURLs[e.Region] = endpointURLs } endpointURLs[service.Type] = service.Endpoints[i].PublicURL } } return details, nil } ���������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/userpass.go���������������������������������������0000644�0000153�0000161�00000001434�12321735747�024133� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identity import ( goosehttp "launchpad.net/goose/http" ) type passwordCredentials struct { Username string `json:"username"` Password string `json:"password"` } type authRequest struct { PasswordCredentials passwordCredentials `json:"passwordCredentials"` TenantName string `json:"tenantName"` } type authWrapper struct { Auth authRequest `json:"auth"` } type UserPass struct { client *goosehttp.Client } func (u *UserPass) Auth(creds *Credentials) (*AuthDetails, error) { if u.client == nil { u.client = goosehttp.New() } auth := authWrapper{Auth: authRequest{ PasswordCredentials: passwordCredentials{ Username: creds.User, Password: creds.Secrets, }, TenantName: creds.TenantName}} return keystoneAuth(u.client, auth, creds.URL) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/legacy_test.go������������������������������������0000644�0000153�0000161�00000002351�12321735747�024570� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identity import ( . "launchpad.net/gocheck" "launchpad.net/goose/testing/httpsuite" "launchpad.net/goose/testservices/identityservice" ) type LegacyTestSuite struct { httpsuite.HTTPSuite } var _ = Suite(&LegacyTestSuite{}) func (s *LegacyTestSuite) TestAuthAgainstServer(c *C) { service := identityservice.NewLegacy() s.Mux.Handle("/", service) userInfo := service.AddUser("joe-user", "secrets", "tenant") service.SetManagementURL("http://management.test.invalid/url") var l Authenticator = &Legacy{} creds := Credentials{User: "joe-user", URL: s.Server.URL, Secrets: "secrets"} auth, err := l.Auth(&creds) c.Assert(err, IsNil) c.Assert(auth.Token, Equals, userInfo.Token) c.Assert( auth.RegionServiceURLs[""], DeepEquals, ServiceURLs{"compute": "http://management.test.invalid/url/compute", "object-store": "http://management.test.invalid/url/object-store"}) } func (s *LegacyTestSuite) TestBadAuth(c *C) { service := identityservice.NewLegacy() s.Mux.Handle("/", service) _ = service.AddUser("joe-user", "secrets", "tenant") var l Authenticator = &Legacy{} creds := Credentials{User: "joe-user", URL: s.Server.URL, Secrets: "bad-secrets"} auth, err := l.Auth(&creds) c.Assert(err, NotNil) c.Assert(auth, IsNil) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/local_test.go�������������������������������������0000644�0000153�0000161�00000004074�12321735747�024422� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identity_test import ( . "launchpad.net/gocheck" "launchpad.net/goose/identity" "launchpad.net/goose/testservices/openstackservice" "net/http" "net/http/httptest" "net/url" "strings" ) func registerLocalTests() { Suite(&localLiveSuite{}) } // localLiveSuite runs tests from LiveTests using a fake // nova server that runs within the test process itself. type localLiveSuite struct { LiveTests // The following attributes are for using testing doubles. Server *httptest.Server Mux *http.ServeMux oldHandler http.Handler } func (s *localLiveSuite) SetUpSuite(c *C) { c.Logf("Using identity and nova service test doubles") // Set up the HTTP server. s.Server = httptest.NewServer(nil) s.oldHandler = s.Server.Config.Handler s.Mux = http.NewServeMux() s.Server.Config.Handler = s.Mux // Set up an Openstack service. s.cred = &identity.Credentials{ URL: s.Server.URL, User: "fred", Secrets: "secret", Region: "zone1.some region", TenantName: "tenant", } openstack := openstackservice.New(s.cred, identity.AuthUserPass) openstack.SetupHTTP(s.Mux) s.LiveTests.SetUpSuite(c) } func (s *localLiveSuite) TearDownSuite(c *C) { s.LiveTests.TearDownSuite(c) s.Mux = nil s.Server.Config.Handler = s.oldHandler s.Server.Close() } func (s *localLiveSuite) SetUpTest(c *C) { s.LiveTests.SetUpTest(c) } func (s *localLiveSuite) TearDownTest(c *C) { s.LiveTests.TearDownTest(c) } // Additional tests to be run against the service double only go here. func (s *localLiveSuite) TestProductStreamsEndpoint(c *C) { err := s.client.Authenticate() c.Assert(err, IsNil) serviceURL, err := s.client.MakeServiceURL("product-streams", nil) c.Assert(err, IsNil) _, err = url.Parse(serviceURL) c.Assert(err, IsNil) c.Assert(strings.HasSuffix(serviceURL, "/imagemetadata"), Equals, true) } func (s *localLiveSuite) TestJujuToolsEndpoint(c *C) { err := s.client.Authenticate() c.Assert(err, IsNil) serviceURL, err := s.client.MakeServiceURL("juju-tools", nil) c.Assert(err, IsNil) _, err = url.Parse(serviceURL) c.Assert(err, IsNil) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/live_test.go��������������������������������������0000644�0000153�0000161�00000001553�12321735747�024266� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identity_test import ( . "launchpad.net/gocheck" "launchpad.net/goose/client" "launchpad.net/goose/identity" "net/url" ) func registerOpenStackTests(cred *identity.Credentials) { Suite(&LiveTests{ cred: cred, }) } type LiveTests struct { cred *identity.Credentials client client.AuthenticatingClient } func (s *LiveTests) SetUpSuite(c *C) { s.client = client.NewClient(s.cred, identity.AuthUserPass, nil) } func (s *LiveTests) TearDownSuite(c *C) { } func (s *LiveTests) SetUpTest(c *C) { // noop, called by local test suite. } func (s *LiveTests) TearDownTest(c *C) { // noop, called by local test suite. } func (s *LiveTests) TestAuth(c *C) { err := s.client.Authenticate() c.Assert(err, IsNil) serviceURL, err := s.client.MakeServiceURL("compute", []string{}) c.Assert(err, IsNil) _, err = url.Parse(serviceURL) c.Assert(err, IsNil) } �����������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/keypair.go����������������������������������������0000644�0000153�0000161�00000001736�12321735747�023737� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identity import ( goosehttp "launchpad.net/goose/http" ) // KeyPair allows OpenStack cloud authentication using an access and // secret key. // // It implements Authenticator interface by providing the Auth method. type KeyPair struct { client *goosehttp.Client } type keypairCredentials struct { AccessKey string `json:"accessKey"` SecretKey string `json:"secretKey"` } type authKeypairRequest struct { KeypairCredentials keypairCredentials `json:"apiAccessKeyCredentials"` TenantName string `json:"tenantName"` } type authKeypairWrapper struct { Auth authKeypairRequest `json:"auth"` } func (u *KeyPair) Auth(creds *Credentials) (*AuthDetails, error) { if u.client == nil { u.client = goosehttp.New() } auth := authKeypairWrapper{Auth: authKeypairRequest{ KeypairCredentials: keypairCredentials{ AccessKey: creds.User, SecretKey: creds.Secrets, }, TenantName: creds.TenantName}} return keystoneAuth(u.client, auth, creds.URL) } ����������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/setup_test.go�������������������������������������0000644�0000153�0000161�00000000670�12321735747�024466� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identity_test import ( "flag" . "launchpad.net/gocheck" "launchpad.net/goose/identity" "testing" ) var live = flag.Bool("live", false, "Include live OpenStack (Canonistack) tests") func Test(t *testing.T) { if *live { cred, err := identity.CompleteCredentialsFromEnv() if err != nil { t.Fatalf("Error setting up test suite: %s", err.Error()) } registerOpenStackTests(cred) } registerLocalTests() TestingT(t) } ������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/identity/userpass_test.go����������������������������������0000644�0000153�0000161�00000004157�12321735747�025177� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identity import ( . "launchpad.net/gocheck" "launchpad.net/goose/testing/httpsuite" "launchpad.net/goose/testservices/identityservice" ) type UserPassTestSuite struct { httpsuite.HTTPSuite } var _ = Suite(&UserPassTestSuite{}) func (s *UserPassTestSuite) TestAuthAgainstServer(c *C) { service := identityservice.NewUserPass() service.SetupHTTP(s.Mux) userInfo := service.AddUser("joe-user", "secrets", "tenant") var l Authenticator = &UserPass{} creds := Credentials{User: "joe-user", URL: s.Server.URL + "/tokens", Secrets: "secrets"} auth, err := l.Auth(&creds) c.Assert(err, IsNil) c.Assert(auth.Token, Equals, userInfo.Token) c.Assert(auth.TenantId, Equals, userInfo.TenantId) } // Test that the region -> service endpoint map is correctly populated. func (s *UserPassTestSuite) TestRegionMatch(c *C) { service := identityservice.NewUserPass() service.SetupHTTP(s.Mux) userInfo := service.AddUser("joe-user", "secrets", "tenant") serviceDef := identityservice.Service{"swift", "object-store", []identityservice.Endpoint{ identityservice.Endpoint{PublicURL: "http://swift", Region: "RegionOne"}, }} service.AddService(serviceDef) serviceDef = identityservice.Service{"nova", "compute", []identityservice.Endpoint{ identityservice.Endpoint{PublicURL: "http://nova", Region: "zone1.RegionOne"}, }} service.AddService(serviceDef) serviceDef = identityservice.Service{"nova", "compute", []identityservice.Endpoint{ identityservice.Endpoint{PublicURL: "http://nova2", Region: "zone2.RegionOne"}, }} service.AddService(serviceDef) creds := Credentials{ User: "joe-user", URL: s.Server.URL + "/tokens", Secrets: "secrets", Region: "zone1.RegionOne", } var l Authenticator = &UserPass{} auth, err := l.Auth(&creds) c.Assert(err, IsNil) c.Assert(auth.RegionServiceURLs["RegionOne"]["object-store"], Equals, "http://swift") c.Assert(auth.RegionServiceURLs["zone1.RegionOne"]["compute"], Equals, "http://nova") c.Assert(auth.RegionServiceURLs["zone2.RegionOne"]["compute"], Equals, "http://nova2") c.Assert(auth.Token, Equals, userInfo.Token) c.Assert(auth.TenantId, Equals, userInfo.TenantId) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/sync/������������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�021060� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/sync/timeout.go��������������������������������������������0000644�0000153�0000161�00000000534�12321735747�023077� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package sync import ( "time" ) // RunWithTimeout runs the specified function and returns true if it completes before timeout, else false. func RunWithTimeout(timeout time.Duration, f func()) bool { ch := make(chan struct{}) go func() { f() close(ch) }() select { case <-ch: return true case <-time.After(timeout): } return false } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/sync/timeout_test.go���������������������������������������0000644�0000153�0000161�00000000750�12321735747�024136� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package sync import ( "testing" "time" ) func TestRunSuccess(t *testing.T) { timeout := 1 * time.Millisecond var ranOk bool ok := RunWithTimeout(timeout, func() { ranOk = true }) if !ok || !ranOk { t.Fail() } } func TestRunTimeout(t *testing.T) { timeout := 1 * time.Millisecond var ranOk bool sig := make(chan struct{}) ok := RunWithTimeout(timeout, func() { // Block until we timeout. <-sig ranOk = true }) sig <- struct{}{} if ok || ranOk { t.Fail() } } ������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/����������������������������������������������0000755�0000153�0000161�00000000000�12321735747�022627� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/cmd/������������������������������������������0000755�0000153�0000161�00000000000�12321735747�023372� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/cmd/main.go�����������������������������������0000644�0000153�0000161�00000003120�12321735747�024641� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/goose/testservices/identityservice" "log" "net/http" "strings" ) type userInfo struct { user, secret string } type userValues struct { users []userInfo } func (uv *userValues) Set(s string) error { vals := strings.Split(s, ":") if len(vals) != 2 { return fmt.Errorf("Invalid --user option, should be: user:secret") } uv.users = append(uv.users, userInfo{ user: vals[0], secret: vals[1], }) return nil } func (uv *userValues) String() string { return fmt.Sprintf("%v", uv.users) } var provider = gnuflag.String("provider", "userpass", "provide the name of the identity service to run") var serveAddr = gnuflag.String("addr", "localhost:8080", "serve the provider on the given address.") var users userValues func init() { gnuflag.Var(&users, "user", "supply to add a user to the identity provider. Can be supplied multiple times. Should be of the form \"user:secret:token\".") } var providerMap = map[string]identityservice.IdentityService{ "legacy": identityservice.NewLegacy(), "userpass": identityservice.NewUserPass(), } func providers() []string { out := make([]string, 0, len(providerMap)) for provider := range providerMap { out = append(out, provider) } return out } func main() { gnuflag.Parse(true) p, ok := providerMap[*provider] if !ok { log.Fatalf("No such provider: %s, pick one of: %v", provider, providers()) } mux := http.NewServeMux() p.SetupHTTP(mux) for _, u := range users.users { p.AddUser(u.user, u.secret, "tenant") } log.Fatal(http.ListenAndServe(*serveAddr, mux)) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/������������������������������0000755�0000153�0000161�00000000000�12321735747�026041� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/service_test.go���������������0000644�0000153�0000161�00000001223�12321735747�031065� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( . "launchpad.net/gocheck" "launchpad.net/goose/testing/httpsuite" ) // All tests in the IdentityServiceSuite run against each IdentityService // implementation. type IdentityServiceSuite struct { httpsuite.HTTPSuite service IdentityService } var _ = Suite(&IdentityServiceSuite{service: NewUserPass()}) var _ = Suite(&IdentityServiceSuite{service: NewLegacy()}) func (s *IdentityServiceSuite) TestAddUserGivesNewToken(c *C) { userInfo1 := s.service.AddUser("user-1", "password-1", "tenant") userInfo2 := s.service.AddUser("user-2", "password-2", "tenant") c.Assert(userInfo1.Token, Not(Equals), userInfo2.Token) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/keypair_test.go���������������0000644�0000153�0000161�00000007151�12321735747�031077� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( "encoding/json" "fmt" "io/ioutil" . "launchpad.net/gocheck" "launchpad.net/goose/testing/httpsuite" "net/http" "strings" ) type KeyPairSuite struct { httpsuite.HTTPSuite } var _ = Suite(&KeyPairSuite{}) func makeKeyPair(user, secret string) (identity *KeyPair) { identity = NewKeyPair() // Ensure that it conforms to the interface var _ IdentityService = identity if user != "" { identity.AddUser(user, secret, "tenant") } return } func (s *KeyPairSuite) setupKeyPair(user, secret string) { var identity *KeyPair identity = makeKeyPair(user, secret) identity.SetupHTTP(s.Mux) return } func (s *KeyPairSuite) setupKeyPairWithServices(user, secret string, services []Service) { var identity *KeyPair identity = makeKeyPair(user, secret) for _, service := range services { identity.AddService(service) } identity.SetupHTTP(s.Mux) return } const authKeyPairTemplate = `{ "auth": { "tenantName": "tenant-something", "apiAccessKeyCredentials": { "accessKey": "%s", "secretKey": "%s" } } }` func keyPairAuthRequest(URL, access, secret string) (*http.Response, error) { client := &http.DefaultClient body := strings.NewReader(fmt.Sprintf(authKeyPairTemplate, access, secret)) request, err := http.NewRequest("POST", URL+"/tokens", body) request.Header.Set("Content-Type", "application/json") if err != nil { return nil, err } return client.Do(request) } func (s *KeyPairSuite) TestNotJSON(c *C) { // We do everything in keyPairAuthRequest, except set the Content-Type s.setupKeyPair("user", "secret") client := &http.DefaultClient body := strings.NewReader(fmt.Sprintf(authTemplate, "user", "secret")) request, err := http.NewRequest("POST", s.Server.URL+"/tokens", body) c.Assert(err, IsNil) res, err := client.Do(request) defer res.Body.Close() c.Assert(err, IsNil) CheckErrorResponse(c, res, http.StatusBadRequest, notJSON) } func (s *KeyPairSuite) TestBadJSON(c *C) { // We do everything in keyPairAuthRequest, except set the Content-Type s.setupKeyPair("user", "secret") res, err := keyPairAuthRequest(s.Server.URL, `garbage"in`, "secret") defer res.Body.Close() c.Assert(err, IsNil) CheckErrorResponse(c, res, http.StatusBadRequest, notJSON) } func (s *KeyPairSuite) TestNoSuchUser(c *C) { s.setupKeyPair("user", "secret") res, err := keyPairAuthRequest(s.Server.URL, "not-user", "secret") defer res.Body.Close() c.Assert(err, IsNil) CheckErrorResponse(c, res, http.StatusUnauthorized, notAuthorized) } func (s *KeyPairSuite) TestBadPassword(c *C) { s.setupKeyPair("user", "secret") res, err := keyPairAuthRequest(s.Server.URL, "user", "not-secret") defer res.Body.Close() c.Assert(err, IsNil) CheckErrorResponse(c, res, http.StatusUnauthorized, invalidUser) } func (s *KeyPairSuite) TestValidAuthorization(c *C) { compute_url := "http://testing.invalid/compute" s.setupKeyPairWithServices("user", "secret", []Service{ {"nova", "compute", []Endpoint{ {PublicURL: compute_url}, }}}) res, err := keyPairAuthRequest(s.Server.URL, "user", "secret") defer res.Body.Close() c.Assert(err, IsNil) c.Check(res.StatusCode, Equals, http.StatusOK) c.Check(res.Header.Get("Content-Type"), Equals, "application/json") content, err := ioutil.ReadAll(res.Body) c.Assert(err, IsNil) var response AccessResponse err = json.Unmarshal(content, &response) c.Assert(err, IsNil) c.Check(response.Access.Token.Id, NotNil) novaURL := "" for _, service := range response.Access.ServiceCatalog { if service.Type == "compute" { novaURL = service.Endpoints[0].PublicURL break } } c.Assert(novaURL, Equals, compute_url) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/util_test.go������������������0000644�0000153�0000161�00000005315�12321735747�030410� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( "bytes" "crypto/rand" "fmt" "io" . "launchpad.net/gocheck" "testing/iotest" ) type UtilSuite struct{} var _ = Suite(&UtilSuite{}) func (s *UtilSuite) TestRandomHexTokenHasLength(c *C) { val := randomHexToken() c.Assert(val, HasLen, 32) } func (s *UtilSuite) TestRandomHexTokenIsHex(c *C) { val := randomHexToken() for i, b := range val { switch { case (b >= 'a' && b <= 'f') || (b >= '0' && b <= '9'): continue default: c.Logf("char %d of %s was not in the right range", i, val) c.Fail() } } } func (s *UtilSuite) TestDefaultReader(c *C) { raw := make([]byte, 6) c.Assert(string(raw), Equals, "\x00\x00\x00\x00\x00\x00") n, err := io.ReadFull(randReader, raw) c.Assert(err, IsNil) c.Assert(n, Equals, 6) c.Assert(string(raw), Not(Equals), "\x00\x00\x00\x00\x00\x00") } func (s *UtilSuite) TestSetReader(c *C) { orig := randReader // This test will be mutating global state (randReader), ensure that we // restore it sanely even if tests fail defer func() { randReader = orig }() // "randomize" everything to the letter 'n' nRandom := bytes.NewBufferString("nnnnnnnnnnnnnnnnnnnnnnn") c.Assert(randReader, Equals, rand.Reader) cleanup := setReader(nRandom) c.Assert(randReader, Equals, nRandom) raw := make([]byte, 6) n, err := io.ReadFull(randReader, raw) c.Assert(err, IsNil) c.Assert(n, Equals, 6) c.Assert(string(raw), Equals, "nnnnnn") cleanup() c.Assert(randReader, Equals, rand.Reader) } // Change how we get random data, the default is to use crypto/rand // This mostly exists to be able to test error side effects // The return value is a function you can call to restore the previous // randomizer func setReader(r io.Reader) (restore func()) { old := randReader randReader = r return func() { randReader = old } } func (s *UtilSuite) TestNotEnoughRandomBytes(c *C) { // No error, just not enough bytes shortRand := bytes.NewBufferString("xx") cleanup := setReader(shortRand) defer cleanup() c.Assert(randomHexToken, PanicMatches, "failed to read 16 random bytes \\(read 2 bytes\\): unexpected EOF") } type ErrReader struct{} func (e ErrReader) Read(b []byte) (n int, err error) { b[0] = 'x' b[1] = 'x' b[2] = 'x' return 3, fmt.Errorf("Not enough bytes") } func (s *UtilSuite) TestRandomBytesError(c *C) { // No error, just not enough bytes cleanup := setReader(ErrReader{}) defer cleanup() c.Assert(randomHexToken, PanicMatches, "failed to read 16 random bytes \\(read 3 bytes\\): Not enough bytes") } func (s *UtilSuite) TestSlowBytes(c *C) { // Even when we have to read one byte at a time, we can still get our // hex token defer setReader(iotest.OneByteReader(rand.Reader))() val := randomHexToken() c.Assert(val, HasLen, 32) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/identityservice.go������������0000644�0000153�0000161�00000001015�12321735747�031577� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import "net/http" // An IdentityService provides user authentication for an Openstack instance. type IdentityService interface { AddUser(user, secret, tenant string) *UserInfo FindUser(token string) (*UserInfo, error) RegisterServiceProvider(name, serviceType string, serviceProvider ServiceProvider) AddService(service Service) SetupHTTP(mux *http.ServeMux) } // A ServiceProvider is an Openstack module which has service endpoints. type ServiceProvider interface { Endpoints() []Endpoint } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/legacy.go���������������������0000644�0000153�0000161�00000002560�12321735747�027637� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( "net/http" ) type Legacy struct { Users managementURL string } func NewLegacy() *Legacy { service := &Legacy{} service.users = make(map[string]UserInfo) service.tenants = make(map[string]string) return service } func (lis *Legacy) RegisterServiceProvider(name, serviceType string, serviceProvider ServiceProvider) { // NOOP for legacy identity service. } func (lis *Legacy) AddService(service Service) { // NOOP for legacy identity service. } func (lis *Legacy) SetManagementURL(URL string) { lis.managementURL = URL } // setupHTTP attaches all the needed handlers to provide the HTTP API. func (lis *Legacy) SetupHTTP(mux *http.ServeMux) { mux.Handle("/", lis) } func (lis *Legacy) ServeHTTP(w http.ResponseWriter, r *http.Request) { username := r.Header.Get("X-Auth-User") userInfo, ok := lis.users[username] if !ok { w.WriteHeader(http.StatusUnauthorized) return } auth_key := r.Header.Get("X-Auth-Key") if auth_key != userInfo.secret { w.WriteHeader(http.StatusUnauthorized) return } if userInfo.Token == "" { userInfo.Token = randomHexToken() lis.users[username] = userInfo } header := w.Header() header.Set("X-Auth-Token", userInfo.Token) header.Set("X-Server-Management-Url", lis.managementURL+"/compute") header.Set("X-Storage-Url", lis.managementURL+"/object-store") w.WriteHeader(http.StatusNoContent) } ������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/userpass.go�������������������0000644�0000153�0000161�00000016500�12321735747�030237� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( "encoding/json" "fmt" "io/ioutil" "launchpad.net/goose/testservices/hook" "net/http" ) // Implement the v2 User Pass form of identity (Keystone) type ErrorResponse struct { Message string `json:"message"` Code int `json:"code"` Title string `json:"title"` } type ErrorWrapper struct { Error ErrorResponse `json:"error"` } type UserPassRequest struct { Auth struct { PasswordCredentials struct { Username string `json:"username"` Password string `json:"password"` } `json:"passwordCredentials"` TenantName string `json:"tenantName"` } `json:"auth"` } type Endpoint struct { AdminURL string `json:"adminURL"` InternalURL string `json:"internalURL"` PublicURL string `json:"publicURL"` Region string `json:"region"` } type Service struct { Name string `json:"name"` Type string `json:"type"` Endpoints []Endpoint } type TokenResponse struct { Expires string `json:"expires"` // should this be a date object? Id string `json:"id"` // Actual token string Tenant struct { Id string `json:"id"` Name string `json:"name"` Description *string `json:"description"` } `json:"tenant"` } type RoleResponse struct { Id string `json:"id"` Name string `json:"name"` TenantId string `json:"tenantId"` } type UserResponse struct { Id string `json:"id"` Name string `json:"name"` Roles []RoleResponse `json:"roles"` } type AccessResponse struct { Access struct { ServiceCatalog []Service `json:"serviceCatalog"` Token TokenResponse `json:"token"` User UserResponse `json:"user"` } `json:"access"` } // Taken from: http://docs.openstack.org/api/quick-start/content/index.html#Getting-Credentials-a00665 var exampleResponse = `{ "access": { "serviceCatalog": [ { "endpoints": [ { "adminURL": "https://nova-api.trystack.org:9774/v1.1/1", "internalURL": "https://nova-api.trystack.org:9774/v1.1/1", "publicURL": "https://nova-api.trystack.org:9774/v1.1/1", "region": "RegionOne" } ], "name": "nova", "type": "compute" }, { "endpoints": [ { "adminURL": "https://GLANCE_API_IS_NOT_DISCLOSED/v1.1/1", "internalURL": "https://GLANCE_API_IS_NOT_DISCLOSED/v1.1/1", "publicURL": "https://GLANCE_API_IS_NOT_DISCLOSED/v1.1/1", "region": "RegionOne" } ], "name": "glance", "type": "image" }, { "endpoints": [ { "adminURL": "https://nova-api.trystack.org:5443/v2.0", "internalURL": "https://keystone.trystack.org:5000/v2.0", "publicURL": "https://keystone.trystack.org:5000/v2.0", "region": "RegionOne" } ], "name": "keystone", "type": "identity" } ], "token": { "expires": "2012-02-15T19:32:21", "id": "5df9d45d-d198-4222-9b4c-7a280aa35666", "tenant": { "id": "1", "name": "admin", "description": null } }, "user": { "id": "14", "name": "annegentle", "roles": [ { "id": "2", "name": "Member", "tenantId": "1" } ] } } }` type UserPass struct { hook.TestService Users services []Service } func NewUserPass() *UserPass { userpass := &UserPass{ services: make([]Service, 0), } userpass.users = make(map[string]UserInfo) userpass.tenants = make(map[string]string) return userpass } func (u *UserPass) RegisterServiceProvider(name, serviceType string, serviceProvider ServiceProvider) { service := Service{name, serviceType, serviceProvider.Endpoints()} u.AddService(service) } func (u *UserPass) AddService(service Service) { u.services = append(u.services, service) } var internalError = []byte(`{ "error": { "message": "Internal failure", "code": 500, "title": Internal Server Error" } }`) func (u *UserPass) ReturnFailure(w http.ResponseWriter, status int, message string) { e := ErrorWrapper{ Error: ErrorResponse{ Message: message, Code: status, Title: http.StatusText(status), }, } if content, err := json.Marshal(e); err != nil { w.Header().Set("Content-Length", fmt.Sprintf("%d", len(internalError))) w.WriteHeader(http.StatusInternalServerError) w.Write(internalError) } else { w.Header().Set("Content-Length", fmt.Sprintf("%d", len(content))) w.WriteHeader(status) w.Write(content) } } // Taken from an actual responses, however it may vary based on actual Openstack implementation const ( notJSON = ("Expecting to find application/json in Content-Type header." + " The server could not comply with the request since it is either malformed" + " or otherwise incorrect. The client is assumed to be in error.") ) func (u *UserPass) ServeHTTP(w http.ResponseWriter, r *http.Request) { var req UserPassRequest // Testing against Canonistack, all responses are application/json, even failures w.Header().Set("Content-Type", "application/json") if r.Header.Get("Content-Type") != "application/json" { u.ReturnFailure(w, http.StatusBadRequest, notJSON) return } if content, err := ioutil.ReadAll(r.Body); err != nil { w.WriteHeader(http.StatusBadRequest) return } else { if err := json.Unmarshal(content, &req); err != nil { u.ReturnFailure(w, http.StatusBadRequest, notJSON) return } } userInfo, errmsg := u.authenticate(req.Auth.PasswordCredentials.Username, req.Auth.PasswordCredentials.Password) if errmsg != "" { u.ReturnFailure(w, http.StatusUnauthorized, errmsg) return } res, err := u.generateAccessResponse(userInfo) if err != nil { u.ReturnFailure(w, http.StatusInternalServerError, err.Error()) return } if content, err := json.Marshal(res); err != nil { u.ReturnFailure(w, http.StatusInternalServerError, err.Error()) return } else { w.WriteHeader(http.StatusOK) w.Write(content) return } panic("All paths should have already returned") } func (u *UserPass) generateAccessResponse(userInfo *UserInfo) (*AccessResponse, error) { res := AccessResponse{} // We pre-populate the response with genuine entries so that it looks sane. // XXX: We should really build up valid state for this instead, at the // very least, we should manage the URLs better. if err := json.Unmarshal([]byte(exampleResponse), &res); err != nil { return nil, err } res.Access.ServiceCatalog = u.services res.Access.Token.Id = userInfo.Token res.Access.Token.Tenant.Id = userInfo.TenantId res.Access.User.Id = userInfo.Id if err := u.ProcessControlHook("authorisation", u, &res, userInfo); err != nil { return nil, err } return &res, nil } // setupHTTP attaches all the needed handlers to provide the HTTP API. func (u *UserPass) SetupHTTP(mux *http.ServeMux) { mux.Handle("/tokens", u) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/legacy_test.go����������������0000644�0000153�0000161�00000005442�12321735747�030700� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( "io/ioutil" . "launchpad.net/gocheck" "launchpad.net/goose/testing/httpsuite" "net/http" ) type LegacySuite struct { httpsuite.HTTPSuite } var _ = Suite(&LegacySuite{}) func (s *LegacySuite) setupLegacy(user, secret string) (token, managementURL string) { managementURL = s.Server.URL identity := NewLegacy() // Ensure that it conforms to the interface var _ IdentityService = identity identity.SetManagementURL(managementURL) identity.SetupHTTP(s.Mux) if user != "" { userInfo := identity.AddUser(user, secret, "tenant") token = userInfo.Token } return } func LegacyAuthRequest(URL, user, key string) (*http.Response, error) { client := &http.DefaultClient request, err := http.NewRequest("GET", URL, nil) if err != nil { return nil, err } if user != "" { request.Header.Set("X-Auth-User", user) } if key != "" { request.Header.Set("X-Auth-Key", key) } return client.Do(request) } func AssertUnauthorized(c *C, response *http.Response) { content, err := ioutil.ReadAll(response.Body) c.Assert(err, IsNil) response.Body.Close() c.Check(response.Header.Get("X-Auth-Token"), Equals, "") c.Check(response.Header.Get("X-Server-Management-Url"), Equals, "") c.Check(string(content), Equals, "") c.Check(response.StatusCode, Equals, http.StatusUnauthorized) } func (s *LegacySuite) TestLegacyFailedAuth(c *C) { s.setupLegacy("", "") // No headers set for Authentication response, err := LegacyAuthRequest(s.Server.URL, "", "") c.Assert(err, IsNil) AssertUnauthorized(c, response) } func (s *LegacySuite) TestLegacyFailedOnlyUser(c *C) { s.setupLegacy("", "") // Missing secret key response, err := LegacyAuthRequest(s.Server.URL, "user", "") c.Assert(err, IsNil) AssertUnauthorized(c, response) } func (s *LegacySuite) TestLegacyNoSuchUser(c *C) { s.setupLegacy("user", "key") // No user matching the username response, err := LegacyAuthRequest(s.Server.URL, "notuser", "key") c.Assert(err, IsNil) AssertUnauthorized(c, response) } func (s *LegacySuite) TestLegacyInvalidAuth(c *C) { s.setupLegacy("user", "secret-key") // Wrong key response, err := LegacyAuthRequest(s.Server.URL, "user", "bad-key") c.Assert(err, IsNil) AssertUnauthorized(c, response) } func (s *LegacySuite) TestLegacyAuth(c *C) { token, serverURL := s.setupLegacy("user", "secret-key") response, err := LegacyAuthRequest(s.Server.URL, "user", "secret-key") c.Assert(err, IsNil) content, err := ioutil.ReadAll(response.Body) response.Body.Close() c.Check(response.Header.Get("X-Auth-Token"), Equals, token) c.Check(response.Header.Get("X-Server-Management-Url"), Equals, serverURL+"/compute") c.Check(response.Header.Get("X-Storage-Url"), Equals, serverURL+"/object-store") c.Check(string(content), Equals, "") c.Check(response.StatusCode, Equals, http.StatusNoContent) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/keypair.go��������������������0000644�0000153�0000161�00000006463�12321735747�030045� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( "encoding/json" "fmt" "io/ioutil" "launchpad.net/goose/testservices/hook" "net/http" ) // Implement the v2 Key Pair form of identity based on Keystone type KeyPairRequest struct { Auth struct { ApiAccessKeyCredentials struct { AccessKey string `json:"accessKey"` SecretKey string `json:"secretKey"` } `json:"apiAccessKeyCredentials"` TenantName string `json:"tenantName"` } `json:"auth"` } type KeyPair struct { hook.TestService Users services []Service } func NewKeyPair() *KeyPair { return &KeyPair{ Users: Users{ users: make(map[string]UserInfo), tenants: make(map[string]string), }, } } func (u *KeyPair) RegisterServiceProvider(name, serviceType string, serviceProvider ServiceProvider) { service := Service{name, serviceType, serviceProvider.Endpoints()} u.AddService(service) } func (u *KeyPair) AddService(service Service) { u.services = append(u.services, service) } func (u *KeyPair) ReturnFailure(w http.ResponseWriter, status int, message string) { e := ErrorWrapper{ Error: ErrorResponse{ Message: message, Code: status, Title: http.StatusText(status), }, } if content, err := json.Marshal(e); err != nil { w.Header().Set("Content-Length", fmt.Sprintf("%d", len(internalError))) w.WriteHeader(http.StatusInternalServerError) w.Write(internalError) } else { w.Header().Set("Content-Length", fmt.Sprintf("%d", len(content))) w.WriteHeader(status) w.Write(content) } } func (u *KeyPair) ServeHTTP(w http.ResponseWriter, r *http.Request) { var req KeyPairRequest // Testing against Canonistack, all responses are application/json, even failures w.Header().Set("Content-Type", "application/json") if r.Header.Get("Content-Type") != "application/json" { u.ReturnFailure(w, http.StatusBadRequest, notJSON) return } if content, err := ioutil.ReadAll(r.Body); err != nil { w.WriteHeader(http.StatusBadRequest) return } else { if err := json.Unmarshal(content, &req); err != nil { u.ReturnFailure(w, http.StatusBadRequest, notJSON) return } } userInfo, errmsg := u.authenticate(req.Auth.ApiAccessKeyCredentials.AccessKey, req.Auth.ApiAccessKeyCredentials.SecretKey) if errmsg != "" { u.ReturnFailure(w, http.StatusUnauthorized, errmsg) return } res, err := u.generateAccessResponse(userInfo) if err != nil { u.ReturnFailure(w, http.StatusInternalServerError, err.Error()) return } if content, err := json.Marshal(res); err != nil { u.ReturnFailure(w, http.StatusInternalServerError, err.Error()) return } else { w.WriteHeader(http.StatusOK) w.Write(content) return } panic("unreachable") } func (u *KeyPair) generateAccessResponse(userInfo *UserInfo) (*AccessResponse, error) { res := AccessResponse{} // We pre-populate the response with genuine entries so that it looks sane. if err := json.Unmarshal([]byte(exampleResponse), &res); err != nil { return nil, err } res.Access.ServiceCatalog = u.services res.Access.Token.Id = userInfo.Token res.Access.Token.Tenant.Id = userInfo.TenantId res.Access.User.Id = userInfo.Id if err := u.ProcessControlHook("authorisation", u, &res, userInfo); err != nil { return nil, err } return &res, nil } // setupHTTP attaches all the needed handlers to provide the HTTP API. func (u *KeyPair) SetupHTTP(mux *http.ServeMux) { mux.Handle("/tokens", u) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/util.go�����������������������0000644�0000153�0000161�00000001152�12321735747�027344� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( "crypto/rand" "encoding/hex" "fmt" "io" ) type UserInfo struct { Id string TenantId string Token string secret string } var randReader = rand.Reader // Generate a bit of random hex data for func randomHexToken() string { raw_bytes := make([]byte, 16) n, err := io.ReadFull(randReader, raw_bytes) if err != nil { panic(fmt.Sprintf( "failed to read 16 random bytes (read %d bytes): %s", n, err.Error())) } hex_bytes := make([]byte, 32) // hex.Encode can't fail, no error checking needed. hex.Encode(hex_bytes, raw_bytes) return string(hex_bytes) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/setup_test.go�����������������0000644�0000153�0000161�00000000164�12321735747�030570� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( . "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { TestingT(t) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/users.go����������������������0000644�0000153�0000161�00000002562�12321735747�027536� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( "fmt" "strconv" ) type Users struct { nextUserId int nextTenantId int users map[string]UserInfo tenants map[string]string } func (u *Users) addTenant(tenant string) string { for id, tenantName := range u.tenants { if tenant == tenantName { return id } } u.nextTenantId++ id := strconv.Itoa(u.nextTenantId) u.tenants[id] = tenant return id } func (u *Users) AddUser(user, secret, tenant string) *UserInfo { tenantId := u.addTenant(tenant) u.nextUserId++ userInfo := &UserInfo{secret: secret, Id: strconv.Itoa(u.nextUserId), TenantId: tenantId} u.users[user] = *userInfo userInfo, _ = u.authenticate(user, secret) return userInfo } func (u *Users) FindUser(token string) (*UserInfo, error) { for _, userInfo := range u.users { if userInfo.Token == token { return &userInfo, nil } } return nil, fmt.Errorf("No user with token %v exists", token) } const ( notAuthorized = "The request you have made requires authentication." invalidUser = "Invalid user / password" ) func (u *Users) authenticate(username, password string) (*UserInfo, string) { userInfo, ok := u.users[username] if !ok { return nil, notAuthorized } if userInfo.secret != password { return nil, invalidUser } if userInfo.Token == "" { userInfo.Token = randomHexToken() u.users[username] = userInfo } return &userInfo, "" } ����������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/identityservice/userpass_test.go��������������0000644�0000153�0000161�00000010133�12321735747�031272� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package identityservice import ( "encoding/json" "fmt" "io/ioutil" . "launchpad.net/gocheck" "launchpad.net/goose/testing/httpsuite" "net/http" "strings" ) type UserPassSuite struct { httpsuite.HTTPSuite } var _ = Suite(&UserPassSuite{}) func makeUserPass(user, secret string) (identity *UserPass) { identity = NewUserPass() // Ensure that it conforms to the interface var _ IdentityService = identity if user != "" { identity.AddUser(user, secret, "tenant") } return } func (s *UserPassSuite) setupUserPass(user, secret string) { var identity *UserPass identity = makeUserPass(user, secret) identity.SetupHTTP(s.Mux) return } func (s *UserPassSuite) setupUserPassWithServices(user, secret string, services []Service) { var identity *UserPass identity = makeUserPass(user, secret) for _, service := range services { identity.AddService(service) } identity.SetupHTTP(s.Mux) return } var authTemplate = `{ "auth": { "tenantName": "tenant-something", "passwordCredentials": { "username": "%s", "password": "%s" } } }` func userPassAuthRequest(URL, user, key string) (*http.Response, error) { client := &http.DefaultClient body := strings.NewReader(fmt.Sprintf(authTemplate, user, key)) request, err := http.NewRequest("POST", URL+"/tokens", body) request.Header.Set("Content-Type", "application/json") if err != nil { return nil, err } return client.Do(request) } func CheckErrorResponse(c *C, r *http.Response, status int, msg string) { c.Check(r.StatusCode, Equals, status) c.Assert(r.Header.Get("Content-Type"), Equals, "application/json") body, err := ioutil.ReadAll(r.Body) c.Assert(err, IsNil) var errmsg ErrorWrapper err = json.Unmarshal(body, &errmsg) c.Assert(err, IsNil) c.Check(errmsg.Error.Code, Equals, status) c.Check(errmsg.Error.Title, Equals, http.StatusText(status)) if msg != "" { c.Check(errmsg.Error.Message, Equals, msg) } } func (s *UserPassSuite) TestNotJSON(c *C) { // We do everything in userPassAuthRequest, except set the Content-Type s.setupUserPass("user", "secret") client := &http.DefaultClient body := strings.NewReader(fmt.Sprintf(authTemplate, "user", "secret")) request, err := http.NewRequest("POST", s.Server.URL+"/tokens", body) c.Assert(err, IsNil) res, err := client.Do(request) defer res.Body.Close() c.Assert(err, IsNil) CheckErrorResponse(c, res, http.StatusBadRequest, notJSON) } func (s *UserPassSuite) TestBadJSON(c *C) { // We do everything in userPassAuthRequest, except set the Content-Type s.setupUserPass("user", "secret") res, err := userPassAuthRequest(s.Server.URL, "garbage\"in", "secret") defer res.Body.Close() c.Assert(err, IsNil) CheckErrorResponse(c, res, http.StatusBadRequest, notJSON) } func (s *UserPassSuite) TestNoSuchUser(c *C) { s.setupUserPass("user", "secret") res, err := userPassAuthRequest(s.Server.URL, "not-user", "secret") defer res.Body.Close() c.Assert(err, IsNil) CheckErrorResponse(c, res, http.StatusUnauthorized, notAuthorized) } func (s *UserPassSuite) TestBadPassword(c *C) { s.setupUserPass("user", "secret") res, err := userPassAuthRequest(s.Server.URL, "user", "not-secret") defer res.Body.Close() c.Assert(err, IsNil) CheckErrorResponse(c, res, http.StatusUnauthorized, invalidUser) } func (s *UserPassSuite) TestValidAuthorization(c *C) { compute_url := "http://testing.invalid/compute" s.setupUserPassWithServices("user", "secret", []Service{ {"nova", "compute", []Endpoint{ {PublicURL: compute_url}, }}}) res, err := userPassAuthRequest(s.Server.URL, "user", "secret") defer res.Body.Close() c.Assert(err, IsNil) c.Check(res.StatusCode, Equals, http.StatusOK) c.Check(res.Header.Get("Content-Type"), Equals, "application/json") content, err := ioutil.ReadAll(res.Body) c.Assert(err, IsNil) var response AccessResponse err = json.Unmarshal(content, &response) c.Assert(err, IsNil) c.Check(response.Access.Token.Id, NotNil) novaURL := "" for _, service := range response.Access.ServiceCatalog { if service.Type == "compute" { novaURL = service.Endpoints[0].PublicURL break } } c.Assert(novaURL, Equals, compute_url) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/service.go������������������������������������0000644�0000153�0000161�00000001750�12321735747�024621� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package testservices import ( "errors" "launchpad.net/goose/testservices/hook" "launchpad.net/goose/testservices/identityservice" "net/http" ) // An HttpService provides the HTTP API for a service double. type HttpService interface { SetupHTTP(mux *http.ServeMux) } // A ServiceInstance is an Openstack module, one of nova, swift, glance. type ServiceInstance struct { identityservice.ServiceProvider hook.TestService IdentityService identityservice.IdentityService Scheme string Hostname string VersionPath string TenantId string Region string } // Internal Openstack errors. var RateLimitExceededError = errors.New("retry limit exceeded") // NoMoreFloatingIPs corresponds to "HTTP 404 Zero floating ips available." var NoMoreFloatingIPs = errors.New("zero floating ips available") // IPLimitExceeded corresponds to "HTTP 413 Maximum number of floating ips exceeded" var IPLimitExceeded = errors.New("maximum number of floating ips exceeded") ������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/hook/�����������������������������������������0000755�0000153�0000161�00000000000�12321736014�023553� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/hook/service_test.go��������������������������0000644�0000153�0000161�00000004654�12321735747�026626� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package hook import ( "fmt" . "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { TestingT(t) } var _ = Suite(&ServiceSuite{}) type ServiceSuite struct { ts *testService } func (s *ServiceSuite) SetUpTest(c *C) { s.ts = newTestService() // This hook is called based on the function name. s.ts.RegisterControlPoint("foo", functionControlHook) // This hook is called based on a user specified hook name. s.ts.RegisterControlPoint("foobar", namedControlHook) } type testService struct { TestService label string } func newTestService() *testService { return &testService{ TestService: TestService{ ControlHooks: make(map[string]ControlProcessor), }, } } func functionControlHook(s ServiceControl, args ...interface{}) error { label := args[0].(string) returnError := args[1].(bool) if returnError { return fmt.Errorf("An error occurred") } s.(*testService).label = label return nil } func namedControlHook(s ServiceControl, args ...interface{}) error { s.(*testService).label = "foobar" return nil } func (s *testService) foo(label string, returnError bool) error { if err := s.ProcessFunctionHook(s, label, returnError); err != nil { return err } return nil } func (s *testService) bar() error { if err := s.ProcessControlHook("foobar", s); err != nil { return err } return nil } func (s *ServiceSuite) TestFunctionHookNoError(c *C) { err := s.ts.foo("success", false) c.Assert(err, IsNil) c.Assert(s.ts.label, Equals, "success") } func (s *ServiceSuite) TestHookWithError(c *C) { err := s.ts.foo("success", true) c.Assert(err, Not(IsNil)) c.Assert(s.ts.label, Equals, "") } func (s *ServiceSuite) TestNamedHook(c *C) { err := s.ts.bar() c.Assert(err, IsNil) c.Assert(s.ts.label, Equals, "foobar") } func (s *ServiceSuite) TestHookCleanup(c *C) { // Manually delete the existing control point. s.ts.RegisterControlPoint("foo", nil) // Register a new hook and ensure it works. cleanup := s.ts.RegisterControlPoint("foo", functionControlHook) err := s.ts.foo("cleanuptest", false) c.Assert(err, IsNil) c.Assert(s.ts.label, Equals, "cleanuptest") // Use the cleanup func to remove the hook and check the result. cleanup() err = s.ts.foo("again", false) c.Assert(err, IsNil) c.Assert(s.ts.label, Equals, "cleanuptest") // Ensure that only the specified hook was removed and the other remaining one still works. err = s.ts.bar() c.Assert(err, IsNil) c.Assert(s.ts.label, Equals, "foobar") } ������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/hook/service.go�������������������������������0000644�0000153�0000161�00000005215�12321735747�025561� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package hook type TestService struct { ServiceControl // Hooks to run when specified control points are reached in the service business logic. ControlHooks map[string]ControlProcessor } // ControlProcessor defines a function that is run when a specified control point is reached in the service // business logic. The function receives the service instance so internal state can be inspected, plus for any // arguments passed to the currently executing service function. type ControlProcessor func(sc ServiceControl, args ...interface{}) error // ControlHookCleanup defines a function used to remove a control hook. type ControlHookCleanup func() // ServiceControl instances allow hooks to be registered for execution at the specified point of execution. // The control point name can be a function name or a logical execution point meaningful to the service. // If name is "", the hook for the currently executing function is executed. // Returns a function which can be used to remove the hook. type ServiceControl interface { RegisterControlPoint(name string, controller ControlProcessor) ControlHookCleanup } // ProcessControlHook retrieves the ControlProcessor for the specified hook name and runs it, returning any error. // Use it like this to invoke a hook registered for some arbitrary control point: // if err := n.ProcessControlHook("foobar", <serviceinstance>, <somearg1>, <somearg2>); err != nil { // return err // } func (s *TestService) ProcessControlHook(hookName string, sc ServiceControl, args ...interface{}) error { if s.ControlHooks == nil { return nil } if hook, ok := s.ControlHooks[hookName]; ok { return hook(sc, args...) } return nil } // ProcessFunctionHook runs the ControlProcessor for the current function, returning any error. // Use it like this: // if err := n.ProcessFunctionHook(<serviceinstance>, <somearg1>, <somearg2>); err != nil { // return err // } func (s *TestService) ProcessFunctionHook(sc ServiceControl, args ...interface{}) error { hookName := s.currentServiceMethodName() return s.ProcessControlHook(hookName, sc, args...) } // RegisterControlPoint assigns the specified controller to the named hook. If nil, any existing controller for the // hook is removed. // hookName is the name of a function on the service or some arbitrarily named control point. func (s *TestService) RegisterControlPoint(hookName string, controller ControlProcessor) ControlHookCleanup { if s.ControlHooks == nil { s.ControlHooks = make(map[string]ControlProcessor) } if controller == nil { delete(s.ControlHooks, hookName) } else { s.ControlHooks[hookName] = controller } return func() { s.RegisterControlPoint(hookName, nil) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/hook/service_gc.go����������������������������0000644�0000153�0000161�00000001040�12321735747�026222� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// +build !gccgo package hook import ( "runtime" "strings" ) // currentServiceMethodName returns the method executing on the service when ProcessControlHook was invoked. func (s *TestService) currentServiceMethodName() string { pc, _, _, ok := runtime.Caller(2) if !ok { panic("current method name cannot be found") } return unqualifiedMethodName(pc) } func unqualifiedMethodName(pc uintptr) string { f := runtime.FuncForPC(pc) fullName := f.Name() nameParts := strings.Split(fullName, ".") return nameParts[len(nameParts)-1] } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/hook/service_gccgo.go�������������������������0000644�0000153�0000161�00000002144�12321736014�026705� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// +build gccgo package hook import ( "runtime" "strings" ) // currentServiceMethodName returns the method executing on the service when ProcessControlHook was invoked. func (s *TestService) currentServiceMethodName() string { // We have to go deeper into the stack with gccgo because in a situation like: // type Inner { } // func (i *Inner) meth {} // type Outer { Inner } // o = &Outer{} // o.meth() // gccgo generates a method called "meth" on *Outer, and this shows up // on the stack as seen by runtime.Caller (this might be a gccgo bug). pc, _, _, ok := runtime.Caller(3) if !ok { panic("current method name cannot be found") } return unqualifiedMethodName(pc) } func unqualifiedMethodName(pc uintptr) string { f := runtime.FuncForPC(pc) fullName := f.Name() // This is very fragile. fullName will be something like: // launchpad.net_goose_testservices_novaservice.removeServer.pN49_launchpad.net_goose_testservices_novaservice.Nova // so if the number of dots in the full package path changes, // this will need to too... nameParts := strings.Split(fullName, ".") return nameParts[2] } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/swiftservice/���������������������������������0000755�0000153�0000161�00000000000�12321735747�025344� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/swiftservice/service_test.go������������������0000644�0000153�0000161�00000007055�12321735747�030401� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Swift double testing service - internal direct API tests package swiftservice import ( "fmt" . "launchpad.net/gocheck" ) type SwiftServiceSuite struct { service *Swift } var region = "region" // not really used here var hostname = "http://localhost" // not really used here var versionPath = "v2" // not really used here var tenantId = "tenant" // not really used here var _ = Suite(&SwiftServiceSuite{}) func (s *SwiftServiceSuite) SetUpSuite(c *C) { s.service = New(hostname, versionPath, tenantId, region, nil) } func (s *SwiftServiceSuite) TestAddHasRemoveContainer(c *C) { ok := s.service.HasContainer("test") c.Assert(ok, Equals, false) err := s.service.AddContainer("test") c.Assert(err, IsNil) ok = s.service.HasContainer("test") c.Assert(ok, Equals, true) err = s.service.RemoveContainer("test") c.Assert(err, IsNil) ok = s.service.HasContainer("test") c.Assert(ok, Equals, false) } func (s *SwiftServiceSuite) TestAddGetRemoveObject(c *C) { _, err := s.service.GetObject("test", "obj") c.Assert(err, Not(IsNil)) err = s.service.AddContainer("test") c.Assert(err, IsNil) ok := s.service.HasContainer("test") c.Assert(ok, Equals, true) data := []byte("test data") err = s.service.AddObject("test", "obj", data) c.Assert(err, IsNil) objdata, err := s.service.GetObject("test", "obj") c.Assert(err, IsNil) c.Assert(objdata, DeepEquals, data) err = s.service.RemoveObject("test", "obj") c.Assert(err, IsNil) _, err = s.service.GetObject("test", "obj") c.Assert(err, Not(IsNil)) err = s.service.RemoveContainer("test") c.Assert(err, IsNil) ok = s.service.HasContainer("test") c.Assert(ok, Equals, false) } func (s *SwiftServiceSuite) TestRemoveContainerWithObjects(c *C) { ok := s.service.HasContainer("test") c.Assert(ok, Equals, false) err := s.service.AddObject("test", "obj", []byte("test data")) c.Assert(err, IsNil) err = s.service.RemoveContainer("test") c.Assert(err, IsNil) _, err = s.service.GetObject("test", "obj") c.Assert(err, Not(IsNil)) } func (s *SwiftServiceSuite) TestGetURL(c *C) { ok := s.service.HasContainer("test") c.Assert(ok, Equals, false) err := s.service.AddContainer("test") c.Assert(err, IsNil) data := []byte("test data") err = s.service.AddObject("test", "obj", data) c.Assert(err, IsNil) url, err := s.service.GetURL("test", "obj") c.Assert(err, IsNil) c.Assert(url, Equals, fmt.Sprintf("%s/%s/%s/test/obj", hostname, versionPath, tenantId)) err = s.service.RemoveContainer("test") c.Assert(err, IsNil) ok = s.service.HasContainer("test") c.Assert(ok, Equals, false) } func (s *SwiftServiceSuite) TestListContainer(c *C) { err := s.service.AddContainer("test") c.Assert(err, IsNil) data := []byte("test data") err = s.service.AddObject("test", "obj", data) c.Assert(err, IsNil) containerData, err := s.service.ListContainer("test", nil) c.Assert(err, IsNil) c.Assert(len(containerData), Equals, 1) c.Assert(containerData[0].Name, Equals, "obj") err = s.service.RemoveContainer("test") c.Assert(err, IsNil) } func (s *SwiftServiceSuite) TestListContainerWithPrefix(c *C) { err := s.service.AddContainer("test") c.Assert(err, IsNil) data := []byte("test data") err = s.service.AddObject("test", "foo", data) c.Assert(err, IsNil) err = s.service.AddObject("test", "foobar", data) c.Assert(err, IsNil) containerData, err := s.service.ListContainer("test", map[string]string{"prefix": "foob"}) c.Assert(err, IsNil) c.Assert(len(containerData), Equals, 1) c.Assert(containerData[0].Name, Equals, "foobar") err = s.service.RemoveContainer("test") c.Assert(err, IsNil) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/swiftservice/service_http.go������������������0000644�0000153�0000161�00000012027�12321735747�030374� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Swift double testing service - HTTP API implementation package swiftservice import ( "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "strings" ) // verbatim real Swift responses const ( notFoundResponse = `404 Not Found The resource could not be found. ` createdResponse = `201 Created ` acceptedResponse = `202 Accepted The request is accepted for processing. ` ) // handleContainers processes HTTP requests for container management. func (s *Swift) handleContainers(container string, w http.ResponseWriter, r *http.Request) { var err error w.Header().Set("Content-Type", "text/html; charset=UTF-8") exists := s.HasContainer(container) if !exists && r.Method != "PUT" { w.WriteHeader(http.StatusNotFound) w.Write([]byte(notFoundResponse)) return } switch r.Method { case "GET": urlParams, err := url.ParseQuery(r.URL.RawQuery) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } params := make(map[string]string, len(urlParams)) for k := range urlParams { params[k] = urlParams.Get(k) } contents, err := s.ListContainer(container, params) var objdata []byte if err == nil { objdata, err = json.Marshal(contents) } if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) } else { w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json; charset=UF-8") w.Write([]byte(objdata)) } case "DELETE": if err = s.RemoveContainer(container); err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) } else { w.Header().Set("Content-Length", "0") w.WriteHeader(http.StatusNoContent) } case "PUT": if exists { w.WriteHeader(http.StatusAccepted) w.Write([]byte(acceptedResponse)) } else { if err = s.AddContainer(container); err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) } else { w.WriteHeader(http.StatusCreated) w.Write([]byte(createdResponse)) } } case "POST": // [sodre]: we don't implement changing ACLs, so this always succeeds. w.WriteHeader(http.StatusAccepted) w.Write([]byte(createdResponse)) default: panic("not implemented request type: " + r.Method) } } // handleObjects processes HTTP requests for object management. func (s *Swift) handleObjects(container, object string, w http.ResponseWriter, r *http.Request) { var err error w.Header().Set("Content-Type", "text/html; charset=UTF-8") if exists := s.HasContainer(container); !exists { w.WriteHeader(http.StatusNotFound) w.Write([]byte(notFoundResponse)) return } objdata, err := s.GetObject(container, object) if err != nil && r.Method != "PUT" { w.WriteHeader(http.StatusNotFound) w.Write([]byte(notFoundResponse)) return } exists := err == nil switch r.Method { case "GET": w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json; charset=UF-8") w.Write([]byte(objdata)) case "DELETE": if err = s.RemoveObject(container, object); err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) } else { w.Header().Set("Content-Length", "0") w.WriteHeader(http.StatusNoContent) } case "PUT": bodydata, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } if exists { err = s.RemoveObject(container, object) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) } } if err = s.AddObject(container, object, bodydata); err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) } else { w.WriteHeader(http.StatusCreated) w.Write([]byte(createdResponse)) } default: panic("not implemented request type: " + r.Method) } } // ServeHTTP is the main entry point in the HTTP request processing. func (s *Swift) ServeHTTP(w http.ResponseWriter, r *http.Request) { // TODO(wallyworld) - 2013-02-11 bug=1121682 // we need to support container ACLs so we can have pubic containers. // For public containers, the token is not required to access the files. For now, if the request // does not provide a token, we will let it through and assume a public container is being accessed. token := r.Header.Get("X-Auth-Token") _, err := s.IdentityService.FindUser(token) if token != "" && err != nil { w.WriteHeader(http.StatusUnauthorized) return } path := strings.TrimRight(r.URL.Path, "/") path = strings.Trim(path, "/") parts := strings.SplitN(path, "/", 4) parts = parts[2:] if len(parts) == 1 { container := parts[0] s.handleContainers(container, w, r) } else if len(parts) == 2 { container := parts[0] object := parts[1] s.handleObjects(container, object, w, r) } else { panic("not implemented request: " + r.URL.Path) } } // setupHTTP attaches all the needed handlers to provide the HTTP API. func (s *Swift) SetupHTTP(mux *http.ServeMux) { path := fmt.Sprintf("/%s/%s/", s.VersionPath, s.TenantId) mux.Handle(path, s) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/swiftservice/service.go�����������������������0000644�0000153�0000161�00000012645�12321735747�027343� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Swift double testing service - internal direct API implementation package swiftservice import ( "fmt" "launchpad.net/goose/swift" "launchpad.net/goose/testservices" "launchpad.net/goose/testservices/identityservice" "net/url" "sort" "strings" "time" ) type object map[string][]byte var _ testservices.HttpService = (*Swift)(nil) var _ identityservice.ServiceProvider = (*Swift)(nil) type Swift struct { testservices.ServiceInstance containers map[string]object } // New creates an instance of the Swift object, given the parameters. func New(hostURL, versionPath, tenantId, region string, identityService identityservice.IdentityService) *Swift { URL, err := url.Parse(hostURL) if err != nil { panic(err) } hostname := URL.Host if !strings.HasSuffix(hostname, "/") { hostname += "/" } swift := &Swift{ containers: make(map[string]object), ServiceInstance: testservices.ServiceInstance{ IdentityService: identityService, Scheme: URL.Scheme, Hostname: hostname, VersionPath: versionPath, TenantId: tenantId, Region: region, }, } if identityService != nil { identityService.RegisterServiceProvider("swift", "object-store", swift) } return swift } func (s *Swift) endpointURL(path string) string { ep := s.Scheme + "://" + s.Hostname + s.VersionPath + "/" + s.TenantId if path != "" { ep += "/" + strings.TrimLeft(path, "/") } return ep } func (s *Swift) Endpoints() []identityservice.Endpoint { ep := identityservice.Endpoint{ AdminURL: s.endpointURL(""), InternalURL: s.endpointURL(""), PublicURL: s.endpointURL(""), Region: s.Region, } return []identityservice.Endpoint{ep} } // HasContainer verifies the given container exists or not. func (s *Swift) HasContainer(name string) bool { _, ok := s.containers[name] return ok } // GetObject retrieves a given object from its container, returning // the object data or an error. func (s *Swift) GetObject(container, name string) ([]byte, error) { if err := s.ProcessFunctionHook(s, container, name); err != nil { return nil, err } data, ok := s.containers[container][name] if !ok { return nil, fmt.Errorf("no such object %q in container %q", name, container) } return data, nil } // AddContainer creates a new container with the given name, if it // does not exist. Otherwise an error is returned. func (s *Swift) AddContainer(name string) error { if err := s.ProcessFunctionHook(s, name); err != nil { return err } if s.HasContainer(name) { return fmt.Errorf("container already exists %q", name) } s.containers[name] = make(object) return nil } // ListContainer lists the objects in the given container. // params contains filtering attributes: prefix, delimiter, marker. // Only prefix is currently supported. func (s *Swift) ListContainer(name string, params map[string]string) ([]swift.ContainerContents, error) { if err := s.ProcessFunctionHook(s, name); err != nil { return nil, err } if ok := s.HasContainer(name); !ok { return nil, fmt.Errorf("no such container %q", name) } items := s.containers[name] sorted := make([]string, 0, len(items)) prefix := params["prefix"] for filename := range items { if prefix != "" && !strings.HasPrefix(filename, prefix) { continue } sorted = append(sorted, filename) } sort.Strings(sorted) contents := make([]swift.ContainerContents, len(sorted)) var i = 0 for _, filename := range sorted { contents[i] = swift.ContainerContents{ Name: filename, Hash: "", // not implemented LengthBytes: len(items[filename]), ContentType: "application/octet-stream", LastModified: time.Now().Format("2006-01-02 15:04:05"), //not implemented } i++ } return contents, nil } // AddObject creates a new object with the given name in the specified // container, setting the object's data. It's an error if the object // already exists. If the container does not exist, it will be // created. func (s *Swift) AddObject(container, name string, data []byte) error { if err := s.ProcessFunctionHook(s, container, name); err != nil { return err } if _, err := s.GetObject(container, name); err == nil { return fmt.Errorf( "object %q in container %q already exists", name, container) } if ok := s.HasContainer(container); !ok { if err := s.AddContainer(container); err != nil { return err } } s.containers[container][name] = data return nil } // RemoveContainer deletes an existing container with the given name. func (s *Swift) RemoveContainer(name string) error { if err := s.ProcessFunctionHook(s, name); err != nil { return err } if ok := s.HasContainer(name); !ok { return fmt.Errorf("no such container %q", name) } delete(s.containers, name) return nil } // RemoveObject deletes an existing object in a given container. func (s *Swift) RemoveObject(container, name string) error { if err := s.ProcessFunctionHook(s, container, name); err != nil { return err } if _, err := s.GetObject(container, name); err != nil { return err } delete(s.containers[container], name) return nil } // GetURL returns the full URL, which can be used to GET the // object. An error occurs if the object does not exist. func (s *Swift) GetURL(container, object string) (string, error) { if err := s.ProcessFunctionHook(s, container, object); err != nil { return "", err } if _, err := s.GetObject(container, object); err != nil { return "", err } return s.endpointURL(fmt.Sprintf("%s/%s", container, object)), nil } �������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/swiftservice/setup_test.go��������������������0000644�0000153�0000161�00000000161�12321735747�030070� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package swiftservice import ( . "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { TestingT(t) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/swiftservice/service_http_test.go�������������0000644�0000153�0000161�00000021740�12321735747�031435� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Swift double testing service - HTTP API tests package swiftservice import ( "bytes" "encoding/json" "io/ioutil" . "launchpad.net/gocheck" "launchpad.net/goose/swift" "launchpad.net/goose/testing/httpsuite" "launchpad.net/goose/testservices/identityservice" "net/http" "net/url" ) type SwiftHTTPSuite struct { httpsuite.HTTPSuite service *Swift token string } var _ = Suite(&SwiftHTTPSuite{}) type SwiftHTTPSSuite struct { httpsuite.HTTPSuite service *Swift token string } var _ = Suite(&SwiftHTTPSSuite{HTTPSuite: httpsuite.HTTPSuite{UseTLS: true}}) func (s *SwiftHTTPSuite) SetUpSuite(c *C) { s.HTTPSuite.SetUpSuite(c) identityDouble := identityservice.NewUserPass() s.service = New(s.Server.URL, versionPath, tenantId, region, identityDouble) userInfo := identityDouble.AddUser("fred", "secret", "tenant") s.token = userInfo.Token } func (s *SwiftHTTPSuite) SetUpTest(c *C) { s.HTTPSuite.SetUpTest(c) s.service.SetupHTTP(s.Mux) } func (s *SwiftHTTPSuite) TearDownTest(c *C) { s.HTTPSuite.TearDownTest(c) } func (s *SwiftHTTPSuite) TearDownSuite(c *C) { s.HTTPSuite.TearDownSuite(c) } func (s *SwiftHTTPSuite) sendRequest(c *C, method, path string, body []byte, expectedStatusCode int) (resp *http.Response) { return s.sendRequestWithParams(c, method, path, nil, body, expectedStatusCode) } func (s *SwiftHTTPSuite) sendRequestWithParams(c *C, method, path string, params map[string]string, body []byte, expectedStatusCode int) (resp *http.Response) { var req *http.Request var err error URL := s.service.endpointURL(path) if len(params) > 0 { urlParams := make(url.Values, len(params)) for k, v := range params { urlParams.Set(k, v) } URL += "?" + urlParams.Encode() } if body != nil { req, err = http.NewRequest(method, URL, bytes.NewBuffer(body)) } else { req, err = http.NewRequest(method, URL, nil) } c.Assert(err, IsNil) if s.token != "" { req.Header.Add("X-Auth-Token", s.token) } client := &http.DefaultClient resp, err = client.Do(req) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, expectedStatusCode) return resp } func (s *SwiftHTTPSuite) ensureNotContainer(name string, c *C) { ok := s.service.HasContainer("test") c.Assert(ok, Equals, false) } func (s *SwiftHTTPSuite) ensureContainer(name string, c *C) { s.ensureNotContainer(name, c) err := s.service.AddContainer("test") c.Assert(err, IsNil) } func (s *SwiftHTTPSuite) removeContainer(name string, c *C) { ok := s.service.HasContainer("test") c.Assert(ok, Equals, true) err := s.service.RemoveContainer("test") c.Assert(err, IsNil) } func (s *SwiftHTTPSuite) ensureNotObject(container, object string, c *C) { _, err := s.service.GetObject(container, object) c.Assert(err, Not(IsNil)) } func (s *SwiftHTTPSuite) ensureObject(container, object string, data []byte, c *C) { s.ensureNotObject(container, object, c) err := s.service.AddObject(container, object, data) c.Assert(err, IsNil) } func (s *SwiftHTTPSuite) ensureObjectData(container, object string, data []byte, c *C) { objdata, err := s.service.GetObject(container, object) c.Assert(err, IsNil) c.Assert(objdata, DeepEquals, data) } func (s *SwiftHTTPSuite) removeObject(container, object string, c *C) { err := s.service.RemoveObject(container, object) c.Assert(err, IsNil) s.ensureNotObject(container, object, c) } func (s *SwiftHTTPSuite) TestPUTContainerMissingCreated(c *C) { s.ensureNotContainer("test", c) s.sendRequest(c, "PUT", "test", nil, http.StatusCreated) s.removeContainer("test", c) } func (s *SwiftHTTPSuite) TestPUTContainerExistsAccepted(c *C) { s.ensureContainer("test", c) s.sendRequest(c, "PUT", "test", nil, http.StatusAccepted) s.removeContainer("test", c) } func (s *SwiftHTTPSuite) TestGETContainerMissingNotFound(c *C) { s.ensureNotContainer("test", c) s.sendRequest(c, "GET", "test", nil, http.StatusNotFound) s.ensureNotContainer("test", c) } func (s *SwiftHTTPSuite) TestGETContainerExistsOK(c *C) { s.ensureContainer("test", c) data := []byte("test data") s.ensureObject("test", "obj", data, c) resp := s.sendRequest(c, "GET", "test", nil, http.StatusOK) defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) c.Assert(err, IsNil) var containerData []swift.ContainerContents err = json.Unmarshal(body, &containerData) c.Assert(err, IsNil) c.Assert(len(containerData), Equals, 1) c.Assert(containerData[0].Name, Equals, "obj") s.removeContainer("test", c) } func (s *SwiftHTTPSuite) TestGETContainerWithPrefix(c *C) { s.ensureContainer("test", c) data := []byte("test data") s.ensureObject("test", "foo", data, c) s.ensureObject("test", "foobar", data, c) resp := s.sendRequestWithParams(c, "GET", "test", map[string]string{"prefix": "foob"}, nil, http.StatusOK) defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) c.Assert(err, IsNil) var containerData []swift.ContainerContents err = json.Unmarshal(body, &containerData) c.Assert(err, IsNil) c.Assert(len(containerData), Equals, 1) c.Assert(containerData[0].Name, Equals, "foobar") s.removeContainer("test", c) } func (s *SwiftHTTPSuite) TestDELETEContainerMissingNotFound(c *C) { s.ensureNotContainer("test", c) s.sendRequest(c, "DELETE", "test", nil, http.StatusNotFound) } func (s *SwiftHTTPSuite) TestDELETEContainerExistsNoContent(c *C) { s.ensureContainer("test", c) s.sendRequest(c, "DELETE", "test", nil, http.StatusNoContent) s.ensureNotContainer("test", c) } func (s *SwiftHTTPSuite) TestPUTObjectMissingCreated(c *C) { s.ensureContainer("test", c) s.ensureNotObject("test", "obj", c) data := []byte("test data") s.sendRequest(c, "PUT", "test/obj", data, http.StatusCreated) s.ensureObjectData("test", "obj", data, c) s.removeContainer("test", c) } func (s *SwiftHTTPSuite) TestPUTObjectExistsCreated(c *C) { data := []byte("test data") s.ensureContainer("test", c) s.ensureObject("test", "obj", data, c) newdata := []byte("new test data") s.sendRequest(c, "PUT", "test/obj", newdata, http.StatusCreated) s.ensureObjectData("test", "obj", newdata, c) s.removeContainer("test", c) } func (s *SwiftHTTPSuite) TestPUTObjectContainerMissingNotFound(c *C) { s.ensureNotContainer("test", c) data := []byte("test data") s.sendRequest(c, "PUT", "test/obj", data, http.StatusNotFound) s.ensureNotContainer("test", c) } func (s *SwiftHTTPSuite) TestGETObjectMissingNotFound(c *C) { s.ensureContainer("test", c) s.ensureNotObject("test", "obj", c) s.sendRequest(c, "GET", "test/obj", nil, http.StatusNotFound) s.removeContainer("test", c) } func (s *SwiftHTTPSuite) TestGETObjectContainerMissingNotFound(c *C) { s.ensureNotContainer("test", c) s.sendRequest(c, "GET", "test/obj", nil, http.StatusNotFound) s.ensureNotContainer("test", c) } func (s *SwiftHTTPSuite) TestGETObjectExistsOK(c *C) { data := []byte("test data") s.ensureContainer("test", c) s.ensureObject("test", "obj", data, c) resp := s.sendRequest(c, "GET", "test/obj", nil, http.StatusOK) s.ensureObjectData("test", "obj", data, c) defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) c.Assert(err, IsNil) c.Assert(body, DeepEquals, data) s.removeContainer("test", c) } func (s *SwiftHTTPSuite) TestDELETEObjectMissingNotFound(c *C) { s.ensureContainer("test", c) s.ensureNotObject("test", "obj", c) s.sendRequest(c, "DELETE", "test/obj", nil, http.StatusNotFound) s.removeContainer("test", c) } func (s *SwiftHTTPSuite) TestDELETEObjectContainerMissingNotFound(c *C) { s.ensureNotContainer("test", c) s.sendRequest(c, "DELETE", "test/obj", nil, http.StatusNotFound) s.ensureNotContainer("test", c) } func (s *SwiftHTTPSuite) TestDELETEObjectExistsNoContent(c *C) { data := []byte("test data") s.ensureContainer("test", c) s.ensureObject("test", "obj", data, c) s.sendRequest(c, "DELETE", "test/obj", nil, http.StatusNoContent) s.removeContainer("test", c) } func (s *SwiftHTTPSuite) TestUnauthorizedFails(c *C) { oldtoken := s.token defer func() { s.token = oldtoken }() // TODO(wallyworld) - 2013-02-11 bug=1121682 // until ACLs are supported, empty tokens are assumed to be used when we need to access a public container. // token = "" // s.sendRequest(c, "GET", "test", nil, http.StatusUnauthorized) s.token = "invalid" s.sendRequest(c, "PUT", "test", nil, http.StatusUnauthorized) s.sendRequest(c, "DELETE", "test", nil, http.StatusUnauthorized) } func (s *SwiftHTTPSSuite) SetUpSuite(c *C) { s.HTTPSuite.SetUpSuite(c) identityDouble := identityservice.NewUserPass() userInfo := identityDouble.AddUser("fred", "secret", "tenant") s.token = userInfo.Token c.Assert(s.Server.URL[:8], Equals, "https://") s.service = New(s.Server.URL, versionPath, userInfo.TenantId, region, identityDouble) } func (s *SwiftHTTPSSuite) TearDownSuite(c *C) { s.HTTPSuite.TearDownSuite(c) } func (s *SwiftHTTPSSuite) SetUpTest(c *C) { s.HTTPSuite.SetUpTest(c) s.service.SetupHTTP(s.Mux) } func (s *SwiftHTTPSSuite) TearDownTest(c *C) { s.HTTPSuite.TearDownTest(c) } func (s *SwiftHTTPSSuite) TestHasHTTPSServiceURL(c *C) { endpoints := s.service.Endpoints() c.Assert(endpoints[0].PublicURL[:8], Equals, "https://") } ��������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/novaservice/����������������������������������0000755�0000153�0000161�00000000000�12321736014�025137� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/novaservice/service_test.go�������������������0000644�0000153�0000161�00000105536�12321736014�030177� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Nova double testing service - internal direct API tests package novaservice import ( "fmt" . "launchpad.net/gocheck" "launchpad.net/goose/nova" "launchpad.net/goose/testservices/hook" ) type NovaSuite struct { service *Nova } const ( versionPath = "v2" hostname = "http://example.com" region = "region" ) var _ = Suite(&NovaSuite{}) func (s *NovaSuite) SetUpSuite(c *C) { s.service = New(hostname, versionPath, "tenant", region, nil) } func (s *NovaSuite) ensureNoFlavor(c *C, flavor nova.FlavorDetail) { _, err := s.service.flavor(flavor.Id) c.Assert(err, ErrorMatches, fmt.Sprintf("no such flavor %q", flavor.Id)) } func (s *NovaSuite) ensureNoServer(c *C, server nova.ServerDetail) { _, err := s.service.server(server.Id) c.Assert(err, ErrorMatches, fmt.Sprintf("no such server %q", server.Id)) } func (s *NovaSuite) ensureNoGroup(c *C, group nova.SecurityGroup) { _, err := s.service.securityGroup(group.Id) c.Assert(err, ErrorMatches, fmt.Sprintf("no such security group %s", group.Id)) } func (s *NovaSuite) ensureNoRule(c *C, rule nova.SecurityGroupRule) { _, err := s.service.securityGroupRule(rule.Id) c.Assert(err, ErrorMatches, fmt.Sprintf("no such security group rule %s", rule.Id)) } func (s *NovaSuite) ensureNoIP(c *C, ip nova.FloatingIP) { _, err := s.service.floatingIP(ip.Id) c.Assert(err, ErrorMatches, fmt.Sprintf("no such floating IP %s", ip.Id)) } func (s *NovaSuite) createFlavor(c *C, flavor nova.FlavorDetail) { s.ensureNoFlavor(c, flavor) err := s.service.addFlavor(flavor) c.Assert(err, IsNil) } func (s *NovaSuite) createServer(c *C, server nova.ServerDetail) { s.ensureNoServer(c, server) err := s.service.addServer(server) c.Assert(err, IsNil) } func (s *NovaSuite) createGroup(c *C, group nova.SecurityGroup) { s.ensureNoGroup(c, group) err := s.service.addSecurityGroup(group) c.Assert(err, IsNil) } func (s *NovaSuite) createIP(c *C, ip nova.FloatingIP) { s.ensureNoIP(c, ip) err := s.service.addFloatingIP(ip) c.Assert(err, IsNil) } func (s *NovaSuite) deleteFlavor(c *C, flavor nova.FlavorDetail) { err := s.service.removeFlavor(flavor.Id) c.Assert(err, IsNil) s.ensureNoFlavor(c, flavor) } func (s *NovaSuite) deleteServer(c *C, server nova.ServerDetail) { err := s.service.removeServer(server.Id) c.Assert(err, IsNil) s.ensureNoServer(c, server) } func (s *NovaSuite) deleteGroup(c *C, group nova.SecurityGroup) { err := s.service.removeSecurityGroup(group.Id) c.Assert(err, IsNil) s.ensureNoGroup(c, group) } func (s *NovaSuite) deleteRule(c *C, rule nova.SecurityGroupRule) { err := s.service.removeSecurityGroupRule(rule.Id) c.Assert(err, IsNil) s.ensureNoRule(c, rule) } func (s *NovaSuite) deleteIP(c *C, ip nova.FloatingIP) { err := s.service.removeFloatingIP(ip.Id) c.Assert(err, IsNil) s.ensureNoIP(c, ip) } func (s *NovaSuite) TestAddRemoveFlavor(c *C) { flavor := nova.FlavorDetail{Id: "test"} s.createFlavor(c, flavor) s.deleteFlavor(c, flavor) } func (s *NovaSuite) TestBuildLinksAndAddFlavor(c *C) { flavor := nova.FlavorDetail{Id: "test"} s.service.buildFlavorLinks(&flavor) s.createFlavor(c, flavor) defer s.deleteFlavor(c, flavor) fl, _ := s.service.flavor(flavor.Id) url := "/flavors/" + flavor.Id links := []nova.Link{ nova.Link{Href: s.service.endpointURL(true, url), Rel: "self"}, nova.Link{Href: s.service.endpointURL(false, url), Rel: "bookmark"}, } c.Assert(fl.Links, DeepEquals, links) } func (s *NovaSuite) TestAddFlavorWithLinks(c *C) { flavor := nova.FlavorDetail{ Id: "test", Links: []nova.Link{ {Href: "href", Rel: "rel"}, }, } s.createFlavor(c, flavor) defer s.deleteFlavor(c, flavor) fl, _ := s.service.flavor(flavor.Id) c.Assert(*fl, DeepEquals, flavor) } func (s *NovaSuite) TestAddFlavorTwiceFails(c *C) { flavor := nova.FlavorDetail{Id: "test"} s.createFlavor(c, flavor) defer s.deleteFlavor(c, flavor) err := s.service.addFlavor(flavor) c.Assert(err, ErrorMatches, `a flavor with id "test" already exists`) } func (s *NovaSuite) TestRemoveFlavorTwiceFails(c *C) { flavor := nova.FlavorDetail{Id: "test"} s.createFlavor(c, flavor) s.deleteFlavor(c, flavor) err := s.service.removeFlavor(flavor.Id) c.Assert(err, ErrorMatches, `no such flavor "test"`) } func (s *NovaSuite) TestAllFlavors(c *C) { // The test service has 2 default flavours. flavors := s.service.allFlavors() c.Assert(flavors, HasLen, 3) for _, fl := range flavors { c.Assert(fl.Name == "m1.tiny" || fl.Name == "m1.small" || fl.Name == "m1.medium", Equals, true) } } func (s *NovaSuite) TestAllFlavorsAsEntities(c *C) { // The test service has 2 default flavours. entities := s.service.allFlavorsAsEntities() c.Assert(entities, HasLen, 3) for _, fl := range entities { c.Assert(fl.Name == "m1.tiny" || fl.Name == "m1.small" || fl.Name == "m1.medium", Equals, true) } } func (s *NovaSuite) TestGetFlavor(c *C) { flavor := nova.FlavorDetail{ Id: "test", Name: "flavor", RAM: 128, VCPUs: 2, Disk: 123, } s.createFlavor(c, flavor) defer s.deleteFlavor(c, flavor) fl, _ := s.service.flavor(flavor.Id) c.Assert(*fl, DeepEquals, flavor) } func (s *NovaSuite) TestGetFlavorAsEntity(c *C) { entity := nova.Entity{ Id: "test", Name: "flavor", } flavor := nova.FlavorDetail{ Id: entity.Id, Name: entity.Name, } s.createFlavor(c, flavor) defer s.deleteFlavor(c, flavor) ent, _ := s.service.flavorAsEntity(flavor.Id) c.Assert(*ent, DeepEquals, entity) } func (s *NovaSuite) TestAddRemoveServer(c *C) { server := nova.ServerDetail{Id: "test"} s.createServer(c, server) s.deleteServer(c, server) } func (s *NovaSuite) TestBuildLinksAndAddServer(c *C) { server := nova.ServerDetail{Id: "test"} s.service.buildServerLinks(&server) s.createServer(c, server) defer s.deleteServer(c, server) sr, _ := s.service.server(server.Id) url := "/servers/" + server.Id links := []nova.Link{ {Href: s.service.endpointURL(true, url), Rel: "self"}, {Href: s.service.endpointURL(false, url), Rel: "bookmark"}, } c.Assert(sr.Links, DeepEquals, links) } func (s *NovaSuite) TestAddServerWithLinks(c *C) { server := nova.ServerDetail{ Id: "test", Links: []nova.Link{ {Href: "href", Rel: "rel"}, }, } s.createServer(c, server) defer s.deleteServer(c, server) sr, _ := s.service.server(server.Id) c.Assert(*sr, DeepEquals, server) } func (s *NovaSuite) TestAddServerTwiceFails(c *C) { server := nova.ServerDetail{Id: "test"} s.createServer(c, server) defer s.deleteServer(c, server) err := s.service.addServer(server) c.Assert(err, ErrorMatches, `a server with id "test" already exists`) } // A control point can be used to change the status of the added server. func (s *NovaSuite) TestAddServerControlPoint(c *C) { cleanup := s.service.RegisterControlPoint( "addServer", func(sc hook.ServiceControl, args ...interface{}) error { details := args[0].(*nova.ServerDetail) details.Status = nova.StatusBuildSpawning return nil }, ) defer cleanup() server := &nova.ServerDetail{ Id: "test", Status: nova.StatusActive, } s.createServer(c, *server) defer s.deleteServer(c, *server) server, _ = s.service.server(server.Id) c.Assert(server.Status, Equals, nova.StatusBuildSpawning) } func (s *NovaSuite) TestRemoveServerTwiceFails(c *C) { server := nova.ServerDetail{Id: "test"} s.createServer(c, server) s.deleteServer(c, server) err := s.service.removeServer(server.Id) c.Assert(err, ErrorMatches, `no such server "test"`) } func (s *NovaSuite) TestAllServers(c *C) { servers := s.service.allServers(nil) c.Assert(servers, HasLen, 0) servers = []nova.ServerDetail{ {Id: "sr1"}, {Id: "sr2"}, } s.createServer(c, servers[0]) defer s.deleteServer(c, servers[1]) s.createServer(c, servers[1]) defer s.deleteServer(c, servers[0]) sr := s.service.allServers(nil) c.Assert(sr, HasLen, len(servers)) if sr[0].Id != servers[0].Id { sr[0], sr[1] = sr[1], sr[0] } c.Assert(sr, DeepEquals, servers) } func (s *NovaSuite) TestAllServersWithFilters(c *C) { servers := s.service.allServers(nil) c.Assert(servers, HasLen, 0) servers = []nova.ServerDetail{ {Id: "sr1", Name: "test", Status: nova.StatusActive}, {Id: "sr2", Name: "other", Status: nova.StatusBuild}, {Id: "sr3", Name: "foo", Status: nova.StatusRescue}, } for _, server := range servers { s.createServer(c, server) defer s.deleteServer(c, server) } f := filter{ nova.FilterStatus: nova.StatusRescue, } sr := s.service.allServers(f) c.Assert(sr, HasLen, 1) c.Assert(sr[0], DeepEquals, servers[2]) f[nova.FilterStatus] = nova.StatusBuild sr = s.service.allServers(f) c.Assert(sr, HasLen, 1) c.Assert(sr[0], DeepEquals, servers[1]) f = filter{ nova.FilterServer: "test", } sr = s.service.allServers(f) c.Assert(sr, HasLen, 1) c.Assert(sr[0], DeepEquals, servers[0]) f[nova.FilterServer] = "other" sr = s.service.allServers(f) c.Assert(sr, HasLen, 1) c.Assert(sr[0], DeepEquals, servers[1]) f[nova.FilterServer] = "foo" f[nova.FilterStatus] = nova.StatusRescue sr = s.service.allServers(f) c.Assert(sr, HasLen, 1) c.Assert(sr[0], DeepEquals, servers[2]) } func (s *NovaSuite) TestAllServersWithEmptyFilter(c *C) { servers := s.service.allServers(nil) c.Assert(servers, HasLen, 0) servers = []nova.ServerDetail{ {Id: "sr1", Name: "srv1"}, {Id: "sr2", Name: "srv2"}, } for _, server := range servers { s.createServer(c, server) defer s.deleteServer(c, server) } sr := s.service.allServers(nil) c.Assert(sr, HasLen, 2) if sr[0].Id != servers[0].Id { sr[0], sr[1] = sr[1], sr[0] } c.Assert(sr, DeepEquals, servers) } func (s *NovaSuite) TestAllServersWithRegexFilters(c *C) { servers := s.service.allServers(nil) c.Assert(servers, HasLen, 0) servers = []nova.ServerDetail{ {Id: "sr1", Name: "foobarbaz"}, {Id: "sr2", Name: "foo123baz"}, {Id: "sr3", Name: "123barbaz"}, {Id: "sr4", Name: "foobar123"}, } for _, server := range servers { s.createServer(c, server) defer s.deleteServer(c, server) } f := filter{ nova.FilterServer: `foo.*baz`, } sr := s.service.allServers(f) c.Assert(sr, HasLen, 2) if sr[0].Id != servers[0].Id { sr[0], sr[1] = sr[1], sr[0] } c.Assert(sr, DeepEquals, servers[:2]) f[nova.FilterServer] = `^[a-z]+[0-9]+` sr = s.service.allServers(f) c.Assert(sr, HasLen, 2) if sr[0].Id != servers[1].Id { sr[0], sr[1] = sr[1], sr[0] } c.Assert(sr[0], DeepEquals, servers[1]) c.Assert(sr[1], DeepEquals, servers[3]) } func (s *NovaSuite) TestAllServersWithMultipleFilters(c *C) { servers := s.service.allServers(nil) c.Assert(servers, HasLen, 0) servers = []nova.ServerDetail{ {Id: "sr1", Name: "s1", Status: nova.StatusActive}, {Id: "sr2", Name: "s2", Status: nova.StatusActive}, {Id: "sr3", Name: "s3", Status: nova.StatusActive}, } for _, server := range servers { s.createServer(c, server) defer s.deleteServer(c, server) } f := filter{ nova.FilterStatus: nova.StatusActive, nova.FilterServer: `.*2.*`, } sr := s.service.allServers(f) c.Assert(sr, HasLen, 1) c.Assert(sr[0], DeepEquals, servers[1]) f[nova.FilterStatus] = nova.StatusBuild sr = s.service.allServers(f) c.Assert(sr, HasLen, 0) f[nova.FilterStatus] = nova.StatusPassword f[nova.FilterServer] = `.*[23].*` sr = s.service.allServers(f) c.Assert(sr, HasLen, 0) f[nova.FilterStatus] = nova.StatusActive sr = s.service.allServers(f) c.Assert(sr, HasLen, 2) if sr[0].Id != servers[1].Id { sr[0], sr[1] = sr[1], sr[0] } c.Assert(sr, DeepEquals, servers[1:]) } func (s *NovaSuite) TestAllServersAsEntities(c *C) { entities := s.service.allServersAsEntities(nil) c.Assert(entities, HasLen, 0) entities = []nova.Entity{ {Id: "sr1"}, {Id: "sr2"}, } servers := []nova.ServerDetail{ {Id: entities[0].Id}, {Id: entities[1].Id}, } s.createServer(c, servers[0]) defer s.deleteServer(c, servers[0]) s.createServer(c, servers[1]) defer s.deleteServer(c, servers[1]) ent := s.service.allServersAsEntities(nil) c.Assert(ent, HasLen, len(entities)) if ent[0].Id != entities[0].Id { ent[0], ent[1] = ent[1], ent[0] } c.Assert(ent, DeepEquals, entities) } func (s *NovaSuite) TestAllServersAsEntitiesWithFilters(c *C) { servers := s.service.allServers(nil) c.Assert(servers, HasLen, 0) servers = []nova.ServerDetail{ {Id: "sr1", Name: "test", Status: nova.StatusActive}, {Id: "sr2", Name: "other", Status: nova.StatusBuild}, {Id: "sr3", Name: "foo", Status: nova.StatusRescue}, } entities := []nova.Entity{} for _, server := range servers { s.createServer(c, server) defer s.deleteServer(c, server) entities = append(entities, nova.Entity{ Id: server.Id, Name: server.Name, Links: server.Links, }) } f := filter{ nova.FilterStatus: nova.StatusRescue, } ent := s.service.allServersAsEntities(f) c.Assert(ent, HasLen, 1) c.Assert(ent[0], DeepEquals, entities[2]) f[nova.FilterStatus] = nova.StatusBuild ent = s.service.allServersAsEntities(f) c.Assert(ent, HasLen, 1) c.Assert(ent[0], DeepEquals, entities[1]) f = filter{ nova.FilterServer: "test", } ent = s.service.allServersAsEntities(f) c.Assert(ent, HasLen, 1) c.Assert(ent[0], DeepEquals, entities[0]) f[nova.FilterServer] = "other" ent = s.service.allServersAsEntities(f) c.Assert(ent, HasLen, 1) c.Assert(ent[0], DeepEquals, entities[1]) f[nova.FilterServer] = "foo" f[nova.FilterStatus] = nova.StatusRescue ent = s.service.allServersAsEntities(f) c.Assert(ent, HasLen, 1) c.Assert(ent[0], DeepEquals, entities[2]) } func (s *NovaSuite) TestGetServer(c *C) { server := nova.ServerDetail{ Id: "test", Name: "server", AddressIPv4: "1.2.3.4", AddressIPv6: "1::fff", Created: "1/1/1", Flavor: nova.Entity{Id: "fl1", Name: "flavor1"}, Image: nova.Entity{Id: "im1", Name: "image1"}, HostId: "myhost", Progress: 123, Status: "st", TenantId: "tenant", Updated: "2/3/4", UserId: "user", } s.createServer(c, server) defer s.deleteServer(c, server) sr, _ := s.service.server(server.Id) c.Assert(*sr, DeepEquals, server) } func (s *NovaSuite) TestGetServerAsEntity(c *C) { entity := nova.Entity{ Id: "test", Name: "server", } server := nova.ServerDetail{ Id: entity.Id, Name: entity.Name, } s.createServer(c, server) defer s.deleteServer(c, server) ent, _ := s.service.serverAsEntity(server.Id) c.Assert(*ent, DeepEquals, entity) } func (s *NovaSuite) TestGetServerByName(c *C) { named, err := s.service.serverByName("test") c.Assert(err, ErrorMatches, `no such server named "test"`) servers := []nova.ServerDetail{ {Id: "sr1", Name: "test"}, {Id: "sr2", Name: "test"}, {Id: "sr3", Name: "not test"}, } for _, server := range servers { s.createServer(c, server) defer s.deleteServer(c, server) } named, err = s.service.serverByName("test") c.Assert(err, IsNil) // order is not guaranteed, so check both possible results if named.Id == servers[0].Id { c.Assert(*named, DeepEquals, servers[0]) } else { c.Assert(*named, DeepEquals, servers[1]) } } func (s *NovaSuite) TestAddRemoveSecurityGroup(c *C) { group := nova.SecurityGroup{Id: "1"} s.createGroup(c, group) s.deleteGroup(c, group) } func (s *NovaSuite) TestAddSecurityGroupWithRules(c *C) { group := nova.SecurityGroup{ Id: "1", Name: "test", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{ {Id: "10", ParentGroupId: "1"}, {Id: "20", ParentGroupId: "1"}, }, } s.createGroup(c, group) defer s.deleteGroup(c, group) gr, _ := s.service.securityGroup(group.Id) c.Assert(*gr, DeepEquals, group) } func (s *NovaSuite) TestAddSecurityGroupTwiceFails(c *C) { group := nova.SecurityGroup{Id: "1", Name: "test"} s.createGroup(c, group) defer s.deleteGroup(c, group) err := s.service.addSecurityGroup(group) c.Assert(err, ErrorMatches, "a security group with id 1 already exists") } func (s *NovaSuite) TestRemoveSecurityGroupTwiceFails(c *C) { group := nova.SecurityGroup{Id: "1", Name: "test"} s.createGroup(c, group) s.deleteGroup(c, group) err := s.service.removeSecurityGroup(group.Id) c.Assert(err, ErrorMatches, "no such security group 1") } func (s *NovaSuite) TestAllSecurityGroups(c *C) { groups := s.service.allSecurityGroups() // There is always a default security group. c.Assert(groups, HasLen, 1) groups = []nova.SecurityGroup{ { Id: "1", Name: "one", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{}, }, { Id: "2", Name: "two", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{}, }, } s.createGroup(c, groups[0]) defer s.deleteGroup(c, groups[0]) s.createGroup(c, groups[1]) defer s.deleteGroup(c, groups[1]) gr := s.service.allSecurityGroups() c.Assert(gr, HasLen, len(groups)+1) checkGroupsInList(c, groups, gr) } func (s *NovaSuite) TestGetSecurityGroup(c *C) { group := nova.SecurityGroup{ Id: "42", TenantId: s.service.TenantId, Name: "group", Description: "desc", Rules: []nova.SecurityGroupRule{}, } s.createGroup(c, group) defer s.deleteGroup(c, group) gr, _ := s.service.securityGroup(group.Id) c.Assert(*gr, DeepEquals, group) } func (s *NovaSuite) TestGetSecurityGroupByName(c *C) { group := nova.SecurityGroup{ Id: "1", Name: "test", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{}, } s.ensureNoGroup(c, group) gr, err := s.service.securityGroupByName(group.Name) c.Assert(err, ErrorMatches, `no such security group named "test"`) s.createGroup(c, group) defer s.deleteGroup(c, group) gr, err = s.service.securityGroupByName(group.Name) c.Assert(err, IsNil) c.Assert(*gr, DeepEquals, group) } func (s *NovaSuite) TestAddHasRemoveSecurityGroupRule(c *C) { group := nova.SecurityGroup{Id: "1"} ri := nova.RuleInfo{ParentGroupId: group.Id} rule := nova.SecurityGroupRule{Id: "10", ParentGroupId: group.Id} s.ensureNoGroup(c, group) s.ensureNoRule(c, rule) ok := s.service.hasSecurityGroupRule(group.Id, rule.Id) c.Assert(ok, Equals, false) s.createGroup(c, group) err := s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, IsNil) ok = s.service.hasSecurityGroupRule(group.Id, rule.Id) c.Assert(ok, Equals, true) s.deleteGroup(c, group) ok = s.service.hasSecurityGroupRule("-1", rule.Id) c.Assert(ok, Equals, true) ok = s.service.hasSecurityGroupRule(group.Id, rule.Id) c.Assert(ok, Equals, false) s.deleteRule(c, rule) ok = s.service.hasSecurityGroupRule("-1", rule.Id) c.Assert(ok, Equals, false) } func (s *NovaSuite) TestAddGetIngressSecurityGroupRule(c *C) { group := nova.SecurityGroup{Id: "1"} s.createGroup(c, group) defer s.deleteGroup(c, group) ri := nova.RuleInfo{ FromPort: 1234, ToPort: 4321, IPProtocol: "tcp", Cidr: "1.2.3.4/5", ParentGroupId: group.Id, } rule := nova.SecurityGroupRule{ Id: "10", ParentGroupId: group.Id, FromPort: &ri.FromPort, ToPort: &ri.ToPort, IPProtocol: &ri.IPProtocol, IPRange: make(map[string]string), } rule.IPRange["cidr"] = ri.Cidr s.ensureNoRule(c, rule) err := s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, IsNil) defer s.deleteRule(c, rule) ru, err := s.service.securityGroupRule(rule.Id) c.Assert(err, IsNil) c.Assert(ru.Id, Equals, rule.Id) c.Assert(ru.ParentGroupId, Equals, rule.ParentGroupId) c.Assert(*ru.FromPort, Equals, *rule.FromPort) c.Assert(*ru.ToPort, Equals, *rule.ToPort) c.Assert(*ru.IPProtocol, Equals, *rule.IPProtocol) c.Assert(ru.IPRange, DeepEquals, rule.IPRange) } func (s *NovaSuite) TestAddGetGroupSecurityGroupRule(c *C) { srcGroup := nova.SecurityGroup{Id: "1", Name: "source", TenantId: s.service.TenantId} tgtGroup := nova.SecurityGroup{Id: "2", Name: "target"} s.createGroup(c, srcGroup) defer s.deleteGroup(c, srcGroup) s.createGroup(c, tgtGroup) defer s.deleteGroup(c, tgtGroup) ri := nova.RuleInfo{ FromPort: 1234, ToPort: 4321, IPProtocol: "tcp", GroupId: &srcGroup.Id, ParentGroupId: tgtGroup.Id, } rule := nova.SecurityGroupRule{ Id: "10", ParentGroupId: tgtGroup.Id, FromPort: &ri.FromPort, ToPort: &ri.ToPort, IPProtocol: &ri.IPProtocol, Group: nova.SecurityGroupRef{ TenantId: srcGroup.TenantId, Name: srcGroup.Name, }, } s.ensureNoRule(c, rule) err := s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, IsNil) defer s.deleteRule(c, rule) ru, err := s.service.securityGroupRule(rule.Id) c.Assert(err, IsNil) c.Assert(ru.Id, Equals, rule.Id) c.Assert(ru.ParentGroupId, Equals, rule.ParentGroupId) c.Assert(*ru.FromPort, Equals, *rule.FromPort) c.Assert(*ru.ToPort, Equals, *rule.ToPort) c.Assert(*ru.IPProtocol, Equals, *rule.IPProtocol) c.Assert(ru.Group, DeepEquals, rule.Group) } func (s *NovaSuite) TestAddSecurityGroupRuleTwiceFails(c *C) { group := nova.SecurityGroup{Id: "1"} s.createGroup(c, group) defer s.deleteGroup(c, group) ri := nova.RuleInfo{ParentGroupId: group.Id} rule := nova.SecurityGroupRule{Id: "10"} s.ensureNoRule(c, rule) err := s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, IsNil) defer s.deleteRule(c, rule) err = s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, ErrorMatches, "a security group rule with id 10 already exists") } func (s *NovaSuite) TestAddSecurityGroupRuleToParentTwiceFails(c *C) { group := nova.SecurityGroup{ Id: "1", Rules: []nova.SecurityGroupRule{ {Id: "10"}, }, } s.createGroup(c, group) defer s.deleteGroup(c, group) ri := nova.RuleInfo{ParentGroupId: group.Id} rule := nova.SecurityGroupRule{Id: "10"} err := s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, ErrorMatches, "cannot add twice rule 10 to security group 1") } func (s *NovaSuite) TestAddSecurityGroupRuleWithInvalidParentFails(c *C) { invalidGroup := nova.SecurityGroup{Id: "1"} s.ensureNoGroup(c, invalidGroup) ri := nova.RuleInfo{ParentGroupId: invalidGroup.Id} rule := nova.SecurityGroupRule{Id: "10"} s.ensureNoRule(c, rule) err := s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, ErrorMatches, "no such security group 1") } func (s *NovaSuite) TestAddGroupSecurityGroupRuleWithInvalidSourceFails(c *C) { group := nova.SecurityGroup{Id: "1"} s.createGroup(c, group) defer s.deleteGroup(c, group) invalidGroupId := "42" ri := nova.RuleInfo{ ParentGroupId: group.Id, GroupId: &invalidGroupId, } rule := nova.SecurityGroupRule{Id: "10"} s.ensureNoRule(c, rule) err := s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, ErrorMatches, "unknown source security group 42") } func (s *NovaSuite) TestAddSecurityGroupRuleUpdatesParent(c *C) { group := nova.SecurityGroup{ Id: "1", Name: "test", TenantId: s.service.TenantId, } s.createGroup(c, group) defer s.deleteGroup(c, group) ri := nova.RuleInfo{ParentGroupId: group.Id} rule := nova.SecurityGroupRule{ Id: "10", ParentGroupId: group.Id, Group: nova.SecurityGroupRef{}, } s.ensureNoRule(c, rule) err := s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, IsNil) defer s.deleteRule(c, rule) group.Rules = []nova.SecurityGroupRule{rule} gr, err := s.service.securityGroup(group.Id) c.Assert(err, IsNil) c.Assert(*gr, DeepEquals, group) } func (s *NovaSuite) TestAddSecurityGroupRuleKeepsNegativePort(c *C) { group := nova.SecurityGroup{ Id: "1", Name: "test", TenantId: s.service.TenantId, } s.createGroup(c, group) defer s.deleteGroup(c, group) ri := nova.RuleInfo{ IPProtocol: "icmp", FromPort: -1, ToPort: -1, Cidr: "0.0.0.0/0", ParentGroupId: group.Id, } rule := nova.SecurityGroupRule{ Id: "10", ParentGroupId: group.Id, FromPort: &ri.FromPort, ToPort: &ri.ToPort, IPProtocol: &ri.IPProtocol, IPRange: map[string]string{"cidr": "0.0.0.0/0"}, } s.ensureNoRule(c, rule) err := s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, IsNil) defer s.deleteRule(c, rule) returnedGroup, err := s.service.securityGroup(group.Id) c.Assert(err, IsNil) c.Assert(returnedGroup.Rules, DeepEquals, []nova.SecurityGroupRule{rule}) } func (s *NovaSuite) TestRemoveSecurityGroupRuleTwiceFails(c *C) { group := nova.SecurityGroup{Id: "1"} s.createGroup(c, group) defer s.deleteGroup(c, group) ri := nova.RuleInfo{ParentGroupId: group.Id} rule := nova.SecurityGroupRule{Id: "10"} s.ensureNoRule(c, rule) err := s.service.addSecurityGroupRule(rule.Id, ri) c.Assert(err, IsNil) s.deleteRule(c, rule) err = s.service.removeSecurityGroupRule(rule.Id) c.Assert(err, ErrorMatches, "no such security group rule 10") } func (s *NovaSuite) TestAddHasRemoveServerSecurityGroup(c *C) { server := nova.ServerDetail{Id: "sr1"} group := nova.SecurityGroup{Id: "1"} s.ensureNoServer(c, server) s.ensureNoGroup(c, group) ok := s.service.hasServerSecurityGroup(server.Id, group.Id) c.Assert(ok, Equals, false) s.createServer(c, server) defer s.deleteServer(c, server) ok = s.service.hasServerSecurityGroup(server.Id, group.Id) c.Assert(ok, Equals, false) s.createGroup(c, group) defer s.deleteGroup(c, group) ok = s.service.hasServerSecurityGroup(server.Id, group.Id) c.Assert(ok, Equals, false) err := s.service.addServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) ok = s.service.hasServerSecurityGroup(server.Id, group.Id) c.Assert(ok, Equals, true) err = s.service.removeServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) ok = s.service.hasServerSecurityGroup(server.Id, group.Id) c.Assert(ok, Equals, false) } func (s *NovaSuite) TestAddServerSecurityGroupWithInvalidServerFails(c *C) { server := nova.ServerDetail{Id: "sr1"} group := nova.SecurityGroup{Id: "1"} s.ensureNoServer(c, server) s.createGroup(c, group) defer s.deleteGroup(c, group) err := s.service.addServerSecurityGroup(server.Id, group.Id) c.Assert(err, ErrorMatches, `no such server "sr1"`) } func (s *NovaSuite) TestAddServerSecurityGroupWithInvalidGroupFails(c *C) { group := nova.SecurityGroup{Id: "1"} server := nova.ServerDetail{Id: "sr1"} s.ensureNoGroup(c, group) s.createServer(c, server) defer s.deleteServer(c, server) err := s.service.addServerSecurityGroup(server.Id, group.Id) c.Assert(err, ErrorMatches, "no such security group 1") } func (s *NovaSuite) TestAddServerSecurityGroupTwiceFails(c *C) { server := nova.ServerDetail{Id: "sr1"} group := nova.SecurityGroup{Id: "1"} s.createServer(c, server) defer s.deleteServer(c, server) s.createGroup(c, group) defer s.deleteGroup(c, group) err := s.service.addServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) err = s.service.addServerSecurityGroup(server.Id, group.Id) c.Assert(err, ErrorMatches, `server "sr1" already belongs to group 1`) err = s.service.removeServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) } func (s *NovaSuite) TestAllServerSecurityGroups(c *C) { server := nova.ServerDetail{Id: "sr1"} srvGroups := s.service.allServerSecurityGroups(server.Id) c.Assert(srvGroups, HasLen, 0) s.createServer(c, server) defer s.deleteServer(c, server) groups := []nova.SecurityGroup{ { Id: "1", Name: "gr1", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{}, }, { Id: "2", Name: "gr2", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{}, }, } for _, group := range groups { s.createGroup(c, group) defer s.deleteGroup(c, group) err := s.service.addServerSecurityGroup(server.Id, group.Id) defer s.service.removeServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) } srvGroups = s.service.allServerSecurityGroups(server.Id) c.Assert(srvGroups, HasLen, len(groups)) if srvGroups[0].Id != groups[0].Id { srvGroups[0], srvGroups[1] = srvGroups[1], srvGroups[0] } c.Assert(srvGroups, DeepEquals, groups) } func (s *NovaSuite) TestRemoveServerSecurityGroupWithInvalidServerFails(c *C) { server := nova.ServerDetail{Id: "sr1"} group := nova.SecurityGroup{Id: "1"} s.createServer(c, server) s.createGroup(c, group) defer s.deleteGroup(c, group) err := s.service.addServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) s.deleteServer(c, server) err = s.service.removeServerSecurityGroup(server.Id, group.Id) c.Assert(err, ErrorMatches, `no such server "sr1"`) s.createServer(c, server) defer s.deleteServer(c, server) err = s.service.removeServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) } func (s *NovaSuite) TestRemoveServerSecurityGroupWithInvalidGroupFails(c *C) { group := nova.SecurityGroup{Id: "1"} server := nova.ServerDetail{Id: "sr1"} s.createGroup(c, group) s.createServer(c, server) defer s.deleteServer(c, server) err := s.service.addServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) s.deleteGroup(c, group) err = s.service.removeServerSecurityGroup(server.Id, group.Id) c.Assert(err, ErrorMatches, "no such security group 1") s.createGroup(c, group) defer s.deleteGroup(c, group) err = s.service.removeServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) } func (s *NovaSuite) TestRemoveServerSecurityGroupTwiceFails(c *C) { server := nova.ServerDetail{Id: "sr1"} group := nova.SecurityGroup{Id: "1"} s.createServer(c, server) defer s.deleteServer(c, server) s.createGroup(c, group) defer s.deleteGroup(c, group) err := s.service.addServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) err = s.service.removeServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) err = s.service.removeServerSecurityGroup(server.Id, group.Id) c.Assert(err, ErrorMatches, `server "sr1" does not belong to group 1`) } func (s *NovaSuite) TestAddHasRemoveFloatingIP(c *C) { ip := nova.FloatingIP{Id: "1", IP: "1.2.3.4"} s.ensureNoIP(c, ip) ok := s.service.hasFloatingIP(ip.IP) c.Assert(ok, Equals, false) s.createIP(c, ip) ok = s.service.hasFloatingIP("invalid IP") c.Assert(ok, Equals, false) ok = s.service.hasFloatingIP(ip.IP) c.Assert(ok, Equals, true) s.deleteIP(c, ip) ok = s.service.hasFloatingIP(ip.IP) c.Assert(ok, Equals, false) } func (s *NovaSuite) TestAddFloatingIPTwiceFails(c *C) { ip := nova.FloatingIP{Id: "1"} s.createIP(c, ip) defer s.deleteIP(c, ip) err := s.service.addFloatingIP(ip) c.Assert(err, ErrorMatches, "a floating IP with id 1 already exists") } func (s *NovaSuite) TestRemoveFloatingIPTwiceFails(c *C) { ip := nova.FloatingIP{Id: "1"} s.createIP(c, ip) s.deleteIP(c, ip) err := s.service.removeFloatingIP(ip.Id) c.Assert(err, ErrorMatches, "no such floating IP 1") } func (s *NovaSuite) TestAllFloatingIPs(c *C) { fips := s.service.allFloatingIPs() c.Assert(fips, HasLen, 0) fips = []nova.FloatingIP{ {Id: "1"}, {Id: "2"}, } s.createIP(c, fips[0]) defer s.deleteIP(c, fips[0]) s.createIP(c, fips[1]) defer s.deleteIP(c, fips[1]) ips := s.service.allFloatingIPs() c.Assert(ips, HasLen, len(fips)) if ips[0].Id != fips[0].Id { ips[0], ips[1] = ips[1], ips[0] } c.Assert(ips, DeepEquals, fips) } func (s *NovaSuite) TestGetFloatingIP(c *C) { inst := "sr1" fixedIP := "4.3.2.1" fip := nova.FloatingIP{ Id: "1", IP: "1.2.3.4", Pool: "pool", InstanceId: &inst, FixedIP: &fixedIP, } s.createIP(c, fip) defer s.deleteIP(c, fip) ip, _ := s.service.floatingIP(fip.Id) c.Assert(*ip, DeepEquals, fip) } func (s *NovaSuite) TestGetFloatingIPByAddr(c *C) { fip := nova.FloatingIP{Id: "1", IP: "1.2.3.4"} s.ensureNoIP(c, fip) ip, err := s.service.floatingIPByAddr(fip.IP) c.Assert(err, NotNil) s.createIP(c, fip) defer s.deleteIP(c, fip) ip, err = s.service.floatingIPByAddr(fip.IP) c.Assert(err, IsNil) c.Assert(*ip, DeepEquals, fip) _, err = s.service.floatingIPByAddr("invalid") c.Assert(err, ErrorMatches, `no such floating IP with address "invalid"`) } func (s *NovaSuite) TestAddHasRemoveServerFloatingIP(c *C) { server := nova.ServerDetail{Id: "sr1"} fip := nova.FloatingIP{Id: "1", IP: "1.2.3.4"} s.ensureNoServer(c, server) s.ensureNoIP(c, fip) ok := s.service.hasServerFloatingIP(server.Id, fip.IP) c.Assert(ok, Equals, false) s.createServer(c, server) defer s.deleteServer(c, server) ok = s.service.hasServerFloatingIP(server.Id, fip.IP) c.Assert(ok, Equals, false) s.createIP(c, fip) defer s.deleteIP(c, fip) ok = s.service.hasServerFloatingIP(server.Id, fip.IP) c.Assert(ok, Equals, false) err := s.service.addServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) ok = s.service.hasServerFloatingIP(server.Id, fip.IP) c.Assert(ok, Equals, true) err = s.service.removeServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) ok = s.service.hasServerFloatingIP(server.Id, fip.IP) c.Assert(ok, Equals, false) } func (s *NovaSuite) TestAddServerFloatingIPWithInvalidServerFails(c *C) { server := nova.ServerDetail{Id: "sr1"} fip := nova.FloatingIP{Id: "1"} s.ensureNoServer(c, server) s.createIP(c, fip) defer s.deleteIP(c, fip) err := s.service.addServerFloatingIP(server.Id, fip.Id) c.Assert(err, ErrorMatches, `no such server "sr1"`) } func (s *NovaSuite) TestAddServerFloatingIPWithInvalidIPFails(c *C) { fip := nova.FloatingIP{Id: "1"} server := nova.ServerDetail{Id: "sr1"} s.ensureNoIP(c, fip) s.createServer(c, server) defer s.deleteServer(c, server) err := s.service.addServerFloatingIP(server.Id, fip.Id) c.Assert(err, ErrorMatches, "no such floating IP 1") } func (s *NovaSuite) TestAddServerFloatingIPTwiceFails(c *C) { server := nova.ServerDetail{Id: "sr1"} fip := nova.FloatingIP{Id: "1"} s.createServer(c, server) defer s.deleteServer(c, server) s.createIP(c, fip) defer s.deleteIP(c, fip) err := s.service.addServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) err = s.service.addServerFloatingIP(server.Id, fip.Id) c.Assert(err, ErrorMatches, `server "sr1" already has floating IP 1`) err = s.service.removeServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) } func (s *NovaSuite) TestRemoveServerFloatingIPWithInvalidServerFails(c *C) { server := nova.ServerDetail{Id: "sr1"} fip := nova.FloatingIP{Id: "1"} s.createServer(c, server) s.createIP(c, fip) defer s.deleteIP(c, fip) err := s.service.addServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) s.deleteServer(c, server) err = s.service.removeServerFloatingIP(server.Id, fip.Id) c.Assert(err, ErrorMatches, `no such server "sr1"`) s.createServer(c, server) defer s.deleteServer(c, server) err = s.service.removeServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) } func (s *NovaSuite) TestRemoveServerFloatingIPWithInvalidIPFails(c *C) { fip := nova.FloatingIP{Id: "1"} server := nova.ServerDetail{Id: "sr1"} s.createIP(c, fip) s.createServer(c, server) defer s.deleteServer(c, server) err := s.service.addServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) s.deleteIP(c, fip) err = s.service.removeServerFloatingIP(server.Id, fip.Id) c.Assert(err, ErrorMatches, "no such floating IP 1") s.createIP(c, fip) defer s.deleteIP(c, fip) err = s.service.removeServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) } func (s *NovaSuite) TestRemoveServerFloatingIPTwiceFails(c *C) { server := nova.ServerDetail{Id: "sr1"} fip := nova.FloatingIP{Id: "1"} s.createServer(c, server) defer s.deleteServer(c, server) s.createIP(c, fip) defer s.deleteIP(c, fip) err := s.service.addServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) err = s.service.removeServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) err = s.service.removeServerFloatingIP(server.Id, fip.Id) c.Assert(err, ErrorMatches, `server "sr1" does not have floating IP 1`) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/novaservice/service_http.go�������������������0000644�0000153�0000161�00000071735�12321735747�030216� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Nova double testing service - HTTP API implementation package novaservice import ( "crypto/rand" "encoding/json" "fmt" "io" "io/ioutil" "launchpad.net/goose/nova" "launchpad.net/goose/testservices" "launchpad.net/goose/testservices/identityservice" "net/http" "path" "strconv" "strings" "time" ) const authToken = "X-Auth-Token" // errorResponse defines a single HTTP error response. type errorResponse struct { code int body string contentType string errorText string headers map[string]string nova *Nova } // verbatim real Nova responses (as errors). var ( errUnauthorized = &errorResponse{ http.StatusUnauthorized, `401 Unauthorized This server could not verify that you are authorized to access the ` + `document you requested. Either you supplied the wrong ` + `credentials (e.g., bad password), or your browser does ` + `not understand how to supply the credentials required. Authentication required `, "text/plain; charset=UTF-8", "unauthorized request", nil, nil, } errForbidden = &errorResponse{ http.StatusForbidden, `{"forbidden": {"message": "Policy doesn't allow compute_extension:` + `flavormanage to be performed.", "code": 403}}`, "application/json; charset=UTF-8", "forbidden flavors request", nil, nil, } errBadRequest = &errorResponse{ http.StatusBadRequest, `{"badRequest": {"message": "Malformed request url", "code": 400}}`, "application/json; charset=UTF-8", "bad request base path or URL", nil, nil, } errBadRequest2 = &errorResponse{ http.StatusBadRequest, `{"badRequest": {"message": "The server could not comply with the ` + `request since it is either malformed or otherwise incorrect.", "code": 400}}`, "application/json; charset=UTF-8", "bad request URL", nil, nil, } errBadRequest3 = &errorResponse{ http.StatusBadRequest, `{"badRequest": {"message": "Malformed request body", "code": 400}}`, "application/json; charset=UTF-8", "bad request body", nil, nil, } errBadRequestDuplicateValue = &errorResponse{ http.StatusBadRequest, `{"badRequest": {"message": "entity already exists", "code": 400}}`, "application/json; charset=UTF-8", "duplicate value", nil, nil, } errBadRequestSrvName = &errorResponse{ http.StatusBadRequest, `{"badRequest": {"message": "Server name is not defined", "code": 400}}`, "application/json; charset=UTF-8", "bad request - missing server name", nil, nil, } errBadRequestSrvFlavor = &errorResponse{ http.StatusBadRequest, `{"badRequest": {"message": "Missing flavorRef attribute", "code": 400}}`, "application/json; charset=UTF-8", "bad request - missing flavorRef", nil, nil, } errBadRequestSrvImage = &errorResponse{ http.StatusBadRequest, `{"badRequest": {"message": "Missing imageRef attribute", "code": 400}}`, "application/json; charset=UTF-8", "bad request - missing imageRef", nil, nil, } errNotFound = &errorResponse{ http.StatusNotFound, `404 Not Found The resource could not be found. `, "text/plain; charset=UTF-8", "resource not found", nil, nil, } errNotFoundJSON = &errorResponse{ http.StatusNotFound, `{"itemNotFound": {"message": "The resource could not be found.", "code": 404}}`, "application/json; charset=UTF-8", "resource not found", nil, nil, } errNotFoundJSONSG = &errorResponse{ http.StatusNotFound, `{"itemNotFound": {"message": "Security group $ID$ not found.", "code": 404}}`, "application/json; charset=UTF-8", "", nil, nil, } errNotFoundJSONSGR = &errorResponse{ http.StatusNotFound, `{"itemNotFound": {"message": "Rule ($ID$) not found.", "code": 404}}`, "application/json; charset=UTF-8", "security rule not found", nil, nil, } errMultipleChoices = &errorResponse{ http.StatusMultipleChoices, `{"choices": [{"status": "CURRENT", "media-types": [{"base": ` + `"application/xml", "type": "application/vnd.openstack.compute+` + `xml;version=2"}, {"base": "application/json", "type": "application/` + `vnd.openstack.compute+json;version=2"}], "id": "v2.0", "links": ` + `[{"href": "$ENDPOINT$$URL$", "rel": "self"}]}]}`, "application/json", "multiple URL redirection choices", nil, nil, } errNoVersion = &errorResponse{ http.StatusOK, `{"versions": [{"status": "CURRENT", "updated": "2011-01-21` + `T11:33:21Z", "id": "v2.0", "links": [{"href": "$ENDPOINT$", "rel": "self"}]}]}`, "application/json", "no version specified in URL", nil, nil, } errVersionsLinks = &errorResponse{ http.StatusOK, `{"version": {"status": "CURRENT", "updated": "2011-01-21T11` + `:33:21Z", "media-types": [{"base": "application/xml", "type": ` + `"application/vnd.openstack.compute+xml;version=2"}, {"base": ` + `"application/json", "type": "application/vnd.openstack.compute` + `+json;version=2"}], "id": "v2.0", "links": [{"href": "$ENDPOINT$"` + `, "rel": "self"}, {"href": "http://docs.openstack.org/api/openstack` + `-compute/1.1/os-compute-devguide-1.1.pdf", "type": "application/pdf` + `", "rel": "describedby"}, {"href": "http://docs.openstack.org/api/` + `openstack-compute/1.1/wadl/os-compute-1.1.wadl", "type": ` + `"application/vnd.sun.wadl+xml", "rel": "describedby"}]}}`, "application/json", "version missing from URL", nil, nil, } errNotImplemented = &errorResponse{ http.StatusNotImplemented, "501 Not Implemented", "text/plain; charset=UTF-8", "not implemented", nil, nil, } errNoGroupId = &errorResponse{ errorText: "no security group id given", } errRateLimitExceeded = &errorResponse{ http.StatusRequestEntityTooLarge, "", "text/plain; charset=UTF-8", "too many requests", // RFC says that Retry-After should be an int, but we don't want to wait an entire second during the test suite. map[string]string{"Retry-After": "0.001"}, nil, } errNoMoreFloatingIPs = &errorResponse{ http.StatusNotFound, "Zero floating ips available.", "text/plain; charset=UTF-8", "zero floating ips available", nil, nil, } errIPLimitExceeded = &errorResponse{ http.StatusRequestEntityTooLarge, "Maximum number of floating ips exceeded.", "text/plain; charset=UTF-8", "maximum number of floating ips exceeded", nil, nil, } ) func (e *errorResponse) Error() string { return e.errorText } // requestBody returns the body for the error response, replacing // $ENDPOINT$, $URL$, $ID$, and $ERROR$ in e.body with the values from // the request. func (e *errorResponse) requestBody(r *http.Request) []byte { url := strings.TrimLeft(r.URL.Path, "/") body := e.body if body != "" { if e.nova != nil { body = strings.Replace(body, "$ENDPOINT$", e.nova.endpointURL(true, "/"), -1) } body = strings.Replace(body, "$URL$", url, -1) body = strings.Replace(body, "$ERROR$", e.Error(), -1) if slash := strings.LastIndex(url, "/"); slash != -1 { body = strings.Replace(body, "$ID$", url[slash+1:], -1) } } return []byte(body) } func (e *errorResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) { if e.contentType != "" { w.Header().Set("Content-Type", e.contentType) } body := e.requestBody(r) if e.headers != nil { for h, v := range e.headers { w.Header().Set(h, v) } } // workaround for https://code.google.com/p/go/issues/detail?id=4454 w.Header().Set("Content-Length", strconv.Itoa(len(body))) if e.code != 0 { w.WriteHeader(e.code) } if len(body) > 0 { w.Write(body) } } type novaHandler struct { n *Nova method func(n *Nova, w http.ResponseWriter, r *http.Request) error } func userInfo(i identityservice.IdentityService, r *http.Request) (*identityservice.UserInfo, error) { return i.FindUser(r.Header.Get(authToken)) } func (h *novaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path // handle invalid X-Auth-Token header _, err := userInfo(h.n.IdentityService, r) if err != nil { errUnauthorized.ServeHTTP(w, r) return } // handle trailing slash in the path if strings.HasSuffix(path, "/") && path != "/" { errNotFound.ServeHTTP(w, r) return } err = h.method(h.n, w, r) if err == nil { return } var resp http.Handler if err == testservices.RateLimitExceededError { resp = errRateLimitExceeded } else if err == testservices.NoMoreFloatingIPs { resp = errNoMoreFloatingIPs } else if err == testservices.IPLimitExceeded { resp = errIPLimitExceeded } else { resp, _ = err.(http.Handler) if resp == nil { resp = &errorResponse{ http.StatusInternalServerError, `{"internalServerError":{"message":"$ERROR$",code:500}}`, "application/json", err.Error(), nil, h.n, } } } resp.ServeHTTP(w, r) } func writeResponse(w http.ResponseWriter, code int, body []byte) { // workaround for https://code.google.com/p/go/issues/detail?id=4454 w.Header().Set("Content-Length", strconv.Itoa(len(body))) w.WriteHeader(code) w.Write(body) } // sendJSON sends the specified response serialized as JSON. func sendJSON(code int, resp interface{}, w http.ResponseWriter, r *http.Request) error { data, err := json.Marshal(resp) if err != nil { return err } writeResponse(w, code, data) return nil } func (n *Nova) handler(method func(n *Nova, w http.ResponseWriter, r *http.Request) error) http.Handler { return &novaHandler{n, method} } func (n *Nova) handleRoot(w http.ResponseWriter, r *http.Request) error { if r.URL.Path == "/" { return errNoVersion } return errMultipleChoices } // handleFlavors handles the flavors HTTP API. func (n *Nova) handleFlavors(w http.ResponseWriter, r *http.Request) error { switch r.Method { case "GET": if flavorId := path.Base(r.URL.Path); flavorId != "flavors" { flavor, err := n.flavor(flavorId) if err != nil { return errNotFound } resp := struct { Flavor nova.FlavorDetail `json:"flavor"` }{*flavor} return sendJSON(http.StatusOK, resp, w, r) } entities := n.allFlavorsAsEntities() if len(entities) == 0 { entities = []nova.Entity{} } resp := struct { Flavors []nova.Entity `json:"flavors"` }{entities} return sendJSON(http.StatusOK, resp, w, r) case "POST": if flavorId := path.Base(r.URL.Path); flavorId != "flavors" { return errNotFound } body, err := ioutil.ReadAll(r.Body) if err != nil { return err } if len(body) == 0 { return errBadRequest2 } return errNotImplemented case "PUT": if flavorId := path.Base(r.URL.Path); flavorId != "flavors" { return errNotFoundJSON } return errNotFound case "DELETE": if flavorId := path.Base(r.URL.Path); flavorId != "flavors" { return errForbidden } return errNotFound } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleFlavorsDetail handles the flavors/detail HTTP API. func (n *Nova) handleFlavorsDetail(w http.ResponseWriter, r *http.Request) error { switch r.Method { case "GET": if flavorId := path.Base(r.URL.Path); flavorId != "detail" { return errNotFound } flavors := n.allFlavors() if len(flavors) == 0 { flavors = []nova.FlavorDetail{} } resp := struct { Flavors []nova.FlavorDetail `json:"flavors"` }{flavors} return sendJSON(http.StatusOK, resp, w, r) case "POST": return errNotFound case "PUT": if flavorId := path.Base(r.URL.Path); flavorId != "detail" { return errNotFound } return errNotFoundJSON case "DELETE": if flavorId := path.Base(r.URL.Path); flavorId != "detail" { return errNotFound } return errForbidden } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleServerActions handles the servers/<id>/action HTTP API. func (n *Nova) handleServerActions(server *nova.ServerDetail, w http.ResponseWriter, r *http.Request) error { if server == nil { return errNotFound } body, err := ioutil.ReadAll(r.Body) if err != nil || len(body) == 0 { return errNotFound } var action struct { AddSecurityGroup *struct { Name string } RemoveSecurityGroup *struct { Name string } AddFloatingIP *struct { Address string } RemoveFloatingIP *struct { Address string } } if err := json.Unmarshal(body, &action); err != nil { return err } switch { case action.AddSecurityGroup != nil: name := action.AddSecurityGroup.Name group, err := n.securityGroupByName(name) if err != nil || n.hasServerSecurityGroup(server.Id, group.Id) { return errNotFound } if err := n.addServerSecurityGroup(server.Id, group.Id); err != nil { return err } writeResponse(w, http.StatusAccepted, nil) return nil case action.RemoveSecurityGroup != nil: name := action.RemoveSecurityGroup.Name group, err := n.securityGroupByName(name) if err != nil || !n.hasServerSecurityGroup(server.Id, group.Id) { return errNotFound } if err := n.removeServerSecurityGroup(server.Id, group.Id); err != nil { return err } writeResponse(w, http.StatusAccepted, nil) return nil case action.AddFloatingIP != nil: addr := action.AddFloatingIP.Address if n.hasServerFloatingIP(server.Id, addr) { return errNotFound } fip, err := n.floatingIPByAddr(addr) if err != nil { return errNotFound } if err := n.addServerFloatingIP(server.Id, fip.Id); err != nil { return err } writeResponse(w, http.StatusAccepted, nil) return nil case action.RemoveFloatingIP != nil: addr := action.RemoveFloatingIP.Address if !n.hasServerFloatingIP(server.Id, addr) { return errNotFound } fip, err := n.floatingIPByAddr(addr) if err != nil { return errNotFound } if err := n.removeServerFloatingIP(server.Id, fip.Id); err != nil { return err } writeResponse(w, http.StatusAccepted, nil) return nil } return fmt.Errorf("unknown server action: %q", string(body)) } // newUUID generates a random UUID conforming to RFC 4122. func newUUID() (string, error) { uuid := make([]byte, 16) if _, err := io.ReadFull(rand.Reader, uuid); err != nil { return "", err } uuid[8] = uuid[8]&^0xc0 | 0x80 // variant bits; see section 4.1.1. uuid[6] = uuid[6]&^0xf0 | 0x40 // version 4; see section 4.1.3. return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil } // noGroupError constructs a bad request response for an invalid group. func noGroupError(groupName, tenantId string) error { return &errorResponse{ http.StatusBadRequest, `{"badRequest": {"message": "Security group ` + groupName + ` not found for project ` + tenantId + `.", "code": 400}}`, "application/json; charset=UTF-8", "bad request URL", nil, nil, } } // handleRunServer handles creating and running a server. func (n *Nova) handleRunServer(body []byte, w http.ResponseWriter, r *http.Request) error { var req struct { Server struct { FlavorRef string ImageRef string Name string Metadata map[string]string SecurityGroups []map[string]string `json:"security_groups"` Networks []map[string]string } } if err := json.Unmarshal(body, &req); err != nil { return errBadRequest3 } if req.Server.Name == "" { return errBadRequestSrvName } if req.Server.ImageRef == "" { return errBadRequestSrvImage } if req.Server.FlavorRef == "" { return errBadRequestSrvFlavor } n.nextServerId++ id := strconv.Itoa(n.nextServerId) uuid, err := newUUID() if err != nil { return err } var groups []string if len(req.Server.SecurityGroups) > 0 { for _, group := range req.Server.SecurityGroups { groupName := group["name"] if sg, err := n.securityGroupByName(groupName); err != nil { return noGroupError(groupName, n.TenantId) } else { groups = append(groups, sg.Id) } } } // TODO(gz) some kind of sane handling of networks for _, network := range req.Server.Networks { networkId := network["uuid"] _, ok := n.networks[networkId] if !ok { return errNotFoundJSON } } // TODO(dimitern) - 2013-02-11 bug=1121684 // make sure flavor/image exist (if needed) flavor := nova.FlavorDetail{Id: req.Server.FlavorRef} n.buildFlavorLinks(&flavor) flavorEnt := nova.Entity{Id: flavor.Id, Links: flavor.Links} image := nova.Entity{Id: req.Server.ImageRef} timestr := time.Now().Format(time.RFC3339) userInfo, _ := userInfo(n.IdentityService, r) server := nova.ServerDetail{ Id: id, UUID: uuid, Name: req.Server.Name, TenantId: n.TenantId, UserId: userInfo.Id, HostId: "1", Image: image, Flavor: flavorEnt, Status: nova.StatusActive, Created: timestr, Updated: timestr, Addresses: make(map[string][]nova.IPAddress), } nextServer := len(n.allServers(nil)) + 1 n.buildServerLinks(&server) // set some IP addresses addr := fmt.Sprintf("127.10.0.%d", nextServer) server.Addresses["public"] = []nova.IPAddress{{4, addr}, {6, "::dead:beef:f00d"}} addr = fmt.Sprintf("127.0.0.%d", nextServer) server.Addresses["private"] = []nova.IPAddress{{4, addr}, {6, "::face::000f"}} if err := n.addServer(server); err != nil { return err } var resp struct { Server struct { SecurityGroups []map[string]string `json:"security_groups"` Id string `json:"id"` Links []nova.Link `json:"links"` AdminPass string `json:"adminPass"` } `json:"server"` } if len(req.Server.SecurityGroups) > 0 { for _, gid := range groups { if err := n.addServerSecurityGroup(id, gid); err != nil { return err } } resp.Server.SecurityGroups = req.Server.SecurityGroups } else { resp.Server.SecurityGroups = []map[string]string{{"name": "default"}} } resp.Server.Id = id resp.Server.Links = server.Links resp.Server.AdminPass = "secret" return sendJSON(http.StatusAccepted, resp, w, r) } // handleServers handles the servers HTTP API. func (n *Nova) handleServers(w http.ResponseWriter, r *http.Request) error { switch r.Method { case "GET": if suffix := path.Base(r.URL.Path); suffix != "servers" { groups := false serverId := "" if suffix == "os-security-groups" { // handle GET /servers/<id>/os-security-groups serverId = path.Base(strings.Replace(r.URL.Path, "/os-security-groups", "", 1)) groups = true } else { serverId = suffix } server, err := n.server(serverId) if err != nil { return errNotFoundJSON } if groups { srvGroups := n.allServerSecurityGroups(serverId) if len(srvGroups) == 0 { srvGroups = []nova.SecurityGroup{} } resp := struct { Groups []nova.SecurityGroup `json:"security_groups"` }{srvGroups} return sendJSON(http.StatusOK, resp, w, r) } resp := struct { Server nova.ServerDetail `json:"server"` }{*server} return sendJSON(http.StatusOK, resp, w, r) } f := make(filter) if err := r.ParseForm(); err == nil && len(r.Form) > 0 { for filterKey, filterValues := range r.Form { for _, value := range filterValues { f[filterKey] = value } } } entities := n.allServersAsEntities(f) if len(entities) == 0 { entities = []nova.Entity{} } resp := struct { Servers []nova.Entity `json:"servers"` }{entities} return sendJSON(http.StatusOK, resp, w, r) case "POST": if suffix := path.Base(r.URL.Path); suffix != "servers" { serverId := "" if suffix == "action" { // handle POST /servers/<id>/action serverId = path.Base(strings.Replace(r.URL.Path, "/action", "", 1)) server, _ := n.server(serverId) return n.handleServerActions(server, w, r) } else { serverId = suffix } return errNotFound } body, err := ioutil.ReadAll(r.Body) if err != nil { return err } if len(body) == 0 { return errBadRequest2 } return n.handleRunServer(body, w, r) case "PUT": if serverId := path.Base(r.URL.Path); serverId != "servers" { return errBadRequest2 } return errNotFound case "DELETE": if serverId := path.Base(r.URL.Path); serverId != "servers" { if _, err := n.server(serverId); err != nil { return errNotFoundJSON } if err := n.removeServer(serverId); err != nil { return err } writeResponse(w, http.StatusNoContent, nil) return nil } return errNotFound } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleServersDetail handles the servers/detail HTTP API. func (n *Nova) handleServersDetail(w http.ResponseWriter, r *http.Request) error { switch r.Method { case "GET": if serverId := path.Base(r.URL.Path); serverId != "detail" { return errNotFound } f := make(filter) if err := r.ParseForm(); err == nil && len(r.Form) > 0 { for filterKey, filterValues := range r.Form { for _, value := range filterValues { f[filterKey] = value } } } servers := n.allServers(f) if len(servers) == 0 { servers = []nova.ServerDetail{} } resp := struct { Servers []nova.ServerDetail `json:"servers"` }{servers} return sendJSON(http.StatusOK, resp, w, r) case "POST": return errNotFound case "PUT": if serverId := path.Base(r.URL.Path); serverId != "detail" { return errNotFound } return errBadRequest2 case "DELETE": if serverId := path.Base(r.URL.Path); serverId != "detail" { return errNotFound } return errNotFoundJSON } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // processGroupId returns the group id from the given request. // If there was no group id specified in the path, it returns errNoGroupId func (n *Nova) processGroupId(w http.ResponseWriter, r *http.Request) (*nova.SecurityGroup, error) { if groupId := path.Base(r.URL.Path); groupId != "os-security-groups" { group, err := n.securityGroup(groupId) if err != nil { return nil, errNotFoundJSONSG } return group, nil } return nil, errNoGroupId } // handleSecurityGroups handles the os-security-groups HTTP API. func (n *Nova) handleSecurityGroups(w http.ResponseWriter, r *http.Request) error { switch r.Method { case "GET": group, err := n.processGroupId(w, r) if err == errNoGroupId { groups := n.allSecurityGroups() if len(groups) == 0 { groups = []nova.SecurityGroup{} } resp := struct { Groups []nova.SecurityGroup `json:"security_groups"` }{groups} return sendJSON(http.StatusOK, resp, w, r) } if err != nil { return err } resp := struct { Group nova.SecurityGroup `json:"security_group"` }{*group} return sendJSON(http.StatusOK, resp, w, r) case "POST": if groupId := path.Base(r.URL.Path); groupId != "os-security-groups" { return errNotFound } body, err := ioutil.ReadAll(r.Body) if err != nil || len(body) == 0 { return errBadRequest2 } var req struct { Group struct { Name string Description string } `json:"security_group"` } if err := json.Unmarshal(body, &req); err != nil { return err } else { _, err := n.securityGroupByName(req.Group.Name) if err == nil { return errBadRequestDuplicateValue } n.nextGroupId++ nextId := strconv.Itoa(n.nextGroupId) err = n.addSecurityGroup(nova.SecurityGroup{ Id: nextId, Name: req.Group.Name, Description: req.Group.Description, TenantId: n.TenantId, }) if err != nil { return err } group, err := n.securityGroup(nextId) if err != nil { return err } var resp struct { Group nova.SecurityGroup `json:"security_group"` } resp.Group = *group return sendJSON(http.StatusOK, resp, w, r) } case "PUT": if groupId := path.Base(r.URL.Path); groupId != "os-security-groups" { return errNotFoundJSON } return errNotFound case "DELETE": if group, err := n.processGroupId(w, r); group != nil { if err := n.removeSecurityGroup(group.Id); err != nil { return err } writeResponse(w, http.StatusAccepted, nil) return nil } else if err == errNoGroupId { return errNotFound } else { return err } } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleSecurityGroupRules handles the os-security-group-rules HTTP API. func (n *Nova) handleSecurityGroupRules(w http.ResponseWriter, r *http.Request) error { switch r.Method { case "GET": return errNotFoundJSON case "POST": if ruleId := path.Base(r.URL.Path); ruleId != "os-security-group-rules" { return errNotFound } body, err := ioutil.ReadAll(r.Body) if err != nil || len(body) == 0 { return errBadRequest2 } var req struct { Rule nova.RuleInfo `json:"security_group_rule"` } if err = json.Unmarshal(body, &req); err != nil { return err } inrule := req.Rule group, err := n.securityGroup(inrule.ParentGroupId) if err != nil { return err // TODO: should be a 4XX error with details } for _, r := range group.Rules { // TODO: this logic is actually wrong, not what nova does at all // why are we reimplementing half of nova/api/openstack in go again? if r.IPProtocol != nil && *r.IPProtocol == inrule.IPProtocol && r.FromPort != nil && *r.FromPort == inrule.FromPort && r.ToPort != nil && *r.ToPort == inrule.ToPort { // TODO: Use a proper helper and sane error type return &errorResponse{ http.StatusBadRequest, fmt.Sprintf(`{"badRequest": {"message": "This rule already exists in group %d", "code": 400}}`, group.Id), "application/json; charset=UTF-8", "rule already exists", nil, nil, } } } n.nextRuleId++ nextId := strconv.Itoa(n.nextRuleId) err = n.addSecurityGroupRule(nextId, req.Rule) if err != nil { return err } rule, err := n.securityGroupRule(nextId) if err != nil { return err } var resp struct { Rule nova.SecurityGroupRule `json:"security_group_rule"` } resp.Rule = *rule return sendJSON(http.StatusOK, resp, w, r) case "PUT": if ruleId := path.Base(r.URL.Path); ruleId != "os-security-group-rules" { return errNotFoundJSON } return errNotFound case "DELETE": if ruleId := path.Base(r.URL.Path); ruleId != "os-security-group-rules" { if _, err := n.securityGroupRule(ruleId); err != nil { return errNotFoundJSONSGR } if err := n.removeSecurityGroupRule(ruleId); err != nil { return err } writeResponse(w, http.StatusAccepted, nil) return nil } return errNotFound } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleFloatingIPs handles the os-floating-ips HTTP API. func (n *Nova) handleFloatingIPs(w http.ResponseWriter, r *http.Request) error { switch r.Method { case "GET": if ipId := path.Base(r.URL.Path); ipId != "os-floating-ips" { fip, err := n.floatingIP(ipId) if err != nil { return errNotFoundJSON } resp := struct { IP nova.FloatingIP `json:"floating_ip"` }{*fip} return sendJSON(http.StatusOK, resp, w, r) } fips := n.allFloatingIPs() if len(fips) == 0 { fips = []nova.FloatingIP{} } resp := struct { IPs []nova.FloatingIP `json:"floating_ips"` }{fips} return sendJSON(http.StatusOK, resp, w, r) case "POST": if ipId := path.Base(r.URL.Path); ipId != "os-floating-ips" { return errNotFound } n.nextIPId++ addr := fmt.Sprintf("10.0.0.%d", n.nextIPId) nextId := strconv.Itoa(n.nextIPId) fip := nova.FloatingIP{Id: nextId, IP: addr, Pool: "nova"} err := n.addFloatingIP(fip) if err != nil { return err } resp := struct { IP nova.FloatingIP `json:"floating_ip"` }{fip} return sendJSON(http.StatusOK, resp, w, r) case "PUT": if ipId := path.Base(r.URL.Path); ipId != "os-floating-ips" { return errNotFoundJSON } return errNotFound case "DELETE": if ipId := path.Base(r.URL.Path); ipId != "os-floating-ips" { if err := n.removeFloatingIP(ipId); err == nil { writeResponse(w, http.StatusAccepted, nil) return nil } return errNotFoundJSON } return errNotFound } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // handleNetworks handles the os-networks HTTP API. func (n *Nova) handleNetworks(w http.ResponseWriter, r *http.Request) error { switch r.Method { case "GET": if ipId := path.Base(r.URL.Path); ipId != "os-networks" { // TODO(gz): handle listing a single group return errNotFoundJSON } nets := n.allNetworks() if len(nets) == 0 { nets = []nova.Network{} } resp := struct { Network []nova.Network `json:"networks"` }{nets} return sendJSON(http.StatusOK, resp, w, r) // TODO(gz): proper handling of other methods } return fmt.Errorf("unknown request method %q for %s", r.Method, r.URL.Path) } // setupHTTP attaches all the needed handlers to provide the HTTP API. func (n *Nova) SetupHTTP(mux *http.ServeMux) { handlers := map[string]http.Handler{ "/": n.handler((*Nova).handleRoot), "/$v/": errBadRequest, "/$v/$t/": errNotFound, "/$v/$t/flavors": n.handler((*Nova).handleFlavors), "/$v/$t/flavors/detail": n.handler((*Nova).handleFlavorsDetail), "/$v/$t/servers": n.handler((*Nova).handleServers), "/$v/$t/servers/detail": n.handler((*Nova).handleServersDetail), "/$v/$t/os-security-groups": n.handler((*Nova).handleSecurityGroups), "/$v/$t/os-security-group-rules": n.handler((*Nova).handleSecurityGroupRules), "/$v/$t/os-floating-ips": n.handler((*Nova).handleFloatingIPs), "/$v/$t/os-networks": n.handler((*Nova).handleNetworks), } for path, h := range handlers { path = strings.Replace(path, "$v", n.VersionPath, 1) path = strings.Replace(path, "$t", n.TenantId, 1) if !strings.HasSuffix(path, "/") { mux.Handle(path+"/", h) } mux.Handle(path, h) } } �����������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/novaservice/service.go������������������������0000644�0000153�0000161�00000052050�12321736014�027130� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Nova double testing service - internal direct API implementation package novaservice import ( "fmt" "launchpad.net/goose/nova" "launchpad.net/goose/testservices" "launchpad.net/goose/testservices/identityservice" "net/url" "regexp" "strings" ) var _ testservices.HttpService = (*Nova)(nil) var _ identityservice.ServiceProvider = (*Nova)(nil) // Nova implements a OpenStack Nova testing service and // contains the service double's internal state. type Nova struct { testservices.ServiceInstance flavors map[string]nova.FlavorDetail servers map[string]nova.ServerDetail groups map[string]nova.SecurityGroup rules map[string]nova.SecurityGroupRule floatingIPs map[string]nova.FloatingIP networks map[string]nova.Network serverGroups map[string][]string serverIPs map[string][]string nextServerId int nextGroupId int nextRuleId int nextIPId int } // endpoint returns either a versioned or non-versioned service // endpoint URL from the given path. func (n *Nova) endpointURL(version bool, path string) string { ep := n.Scheme + "://" + n.Hostname if version { ep += n.VersionPath + "/" } ep += n.TenantId if path != "" { ep += "/" + strings.TrimLeft(path, "/") } return ep } func (n *Nova) Endpoints() []identityservice.Endpoint { ep := identityservice.Endpoint{ AdminURL: n.endpointURL(true, ""), InternalURL: n.endpointURL(true, ""), PublicURL: n.endpointURL(true, ""), Region: n.Region, } return []identityservice.Endpoint{ep} } // New creates an instance of the Nova object, given the parameters. func New(hostURL, versionPath, tenantId, region string, identityService identityservice.IdentityService) *Nova { URL, err := url.Parse(hostURL) if err != nil { panic(err) } hostname := URL.Host if !strings.HasSuffix(hostname, "/") { hostname += "/" } // Real openstack instances have flavours "out of the box". So we add some here. defaultFlavors := []nova.FlavorDetail{ {Id: "1", Name: "m1.tiny", RAM: 512, VCPUs: 1}, {Id: "2", Name: "m1.small", RAM: 2048, VCPUs: 1}, {Id: "3", Name: "m1.medium", RAM: 4096, VCPUs: 2}, } // Real openstack instances have a default security group "out of the box". So we add it here. defaultSecurityGroups := []nova.SecurityGroup{ {Id: "999", Name: "default", Description: "default group"}, } novaService := &Nova{ flavors: make(map[string]nova.FlavorDetail), servers: make(map[string]nova.ServerDetail), groups: make(map[string]nova.SecurityGroup), rules: make(map[string]nova.SecurityGroupRule), floatingIPs: make(map[string]nova.FloatingIP), networks: make(map[string]nova.Network), serverGroups: make(map[string][]string), serverIPs: make(map[string][]string), ServiceInstance: testservices.ServiceInstance{ IdentityService: identityService, Scheme: URL.Scheme, Hostname: hostname, VersionPath: versionPath, TenantId: tenantId, Region: region, }, } if identityService != nil { identityService.RegisterServiceProvider("nova", "compute", novaService) } for i, flavor := range defaultFlavors { novaService.buildFlavorLinks(&flavor) defaultFlavors[i] = flavor err := novaService.addFlavor(flavor) if err != nil { panic(err) } } for _, group := range defaultSecurityGroups { err := novaService.addSecurityGroup(group) if err != nil { panic(err) } } // Add a sample default network var netId = "1" novaService.networks[netId] = nova.Network{ Id: netId, Label: "net", Cidr: "10.0.0.0/24", } return novaService } // buildFlavorLinks populates the Links field of the passed // FlavorDetail as needed by OpenStack HTTP API. Call this // before addFlavor(). func (n *Nova) buildFlavorLinks(flavor *nova.FlavorDetail) { url := "/flavors/" + flavor.Id flavor.Links = []nova.Link{ nova.Link{Href: n.endpointURL(true, url), Rel: "self"}, nova.Link{Href: n.endpointURL(false, url), Rel: "bookmark"}, } } // addFlavor creates a new flavor. func (n *Nova) addFlavor(flavor nova.FlavorDetail) error { if err := n.ProcessFunctionHook(n, flavor); err != nil { return err } if _, err := n.flavor(flavor.Id); err == nil { return fmt.Errorf("a flavor with id %q already exists", flavor.Id) } n.flavors[flavor.Id] = flavor return nil } // flavor retrieves an existing flavor by ID. func (n *Nova) flavor(flavorId string) (*nova.FlavorDetail, error) { if err := n.ProcessFunctionHook(n, flavorId); err != nil { return nil, err } flavor, ok := n.flavors[flavorId] if !ok { return nil, fmt.Errorf("no such flavor %q", flavorId) } return &flavor, nil } // flavorAsEntity returns the stored FlavorDetail as Entity. func (n *Nova) flavorAsEntity(flavorId string) (*nova.Entity, error) { if err := n.ProcessFunctionHook(n, flavorId); err != nil { return nil, err } flavor, err := n.flavor(flavorId) if err != nil { return nil, err } return &nova.Entity{ Id: flavor.Id, Name: flavor.Name, Links: flavor.Links, }, nil } // allFlavors returns a list of all existing flavors. func (n *Nova) allFlavors() []nova.FlavorDetail { var flavors []nova.FlavorDetail for _, flavor := range n.flavors { flavors = append(flavors, flavor) } return flavors } // allFlavorsAsEntities returns all flavors as Entity structs. func (n *Nova) allFlavorsAsEntities() []nova.Entity { var entities []nova.Entity for _, flavor := range n.flavors { entities = append(entities, nova.Entity{ Id: flavor.Id, Name: flavor.Name, Links: flavor.Links, }) } return entities } // removeFlavor deletes an existing flavor. func (n *Nova) removeFlavor(flavorId string) error { if err := n.ProcessFunctionHook(n, flavorId); err != nil { return err } if _, err := n.flavor(flavorId); err != nil { return err } delete(n.flavors, flavorId) return nil } // buildServerLinks populates the Links field of the passed // ServerDetail as needed by OpenStack HTTP API. Call this // before addServer(). func (n *Nova) buildServerLinks(server *nova.ServerDetail) { url := "/servers/" + server.Id server.Links = []nova.Link{ nova.Link{Href: n.endpointURL(true, url), Rel: "self"}, nova.Link{Href: n.endpointURL(false, url), Rel: "bookmark"}, } } // addServer creates a new server. func (n *Nova) addServer(server nova.ServerDetail) error { if err := n.ProcessFunctionHook(n, &server); err != nil { return err } if _, err := n.server(server.Id); err == nil { return fmt.Errorf("a server with id %q already exists", server.Id) } n.servers[server.Id] = server return nil } // server retrieves an existing server by ID. func (n *Nova) server(serverId string) (*nova.ServerDetail, error) { if err := n.ProcessFunctionHook(n, serverId); err != nil { return nil, err } server, ok := n.servers[serverId] if !ok { return nil, fmt.Errorf("no such server %q", serverId) } return &server, nil } // serverByName retrieves the first existing server with the given name. func (n *Nova) serverByName(name string) (*nova.ServerDetail, error) { if err := n.ProcessFunctionHook(n, name); err != nil { return nil, err } for _, server := range n.servers { if server.Name == name { return &server, nil } } return nil, fmt.Errorf("no such server named %q", name) } // serverAsEntity returns the stored ServerDetail as Entity. func (n *Nova) serverAsEntity(serverId string) (*nova.Entity, error) { if err := n.ProcessFunctionHook(n, serverId); err != nil { return nil, err } server, err := n.server(serverId) if err != nil { return nil, err } return &nova.Entity{ Id: server.Id, UUID: server.UUID, Name: server.Name, Links: server.Links, }, nil } // filter is used internally by matchServers. type filter map[string]string // matchServers returns a list of matching servers, after applying the // given filter. Each separate filter is combined with a logical AND. // Each filter can have only one value. A nil filter matches all servers. // // This is tested to match OpenStack behavior. Regular expression // matching is supported for FilterServer only, and the supported // syntax is limited to whatever DB backend is used (see SQL // REGEXP/RLIKE). // // Example: // // f := filter{ // nova.FilterStatus: nova.StatusActive, // nova.FilterServer: `foo.*`, // } // // This will match all servers with status "ACTIVE", and names starting // with "foo". func (n *Nova) matchServers(f filter) []nova.ServerDetail { var servers []nova.ServerDetail for _, server := range n.servers { servers = append(servers, server) } if len(f) == 0 { return servers // empty filter matches everything } if status := f[nova.FilterStatus]; status != "" { matched := []nova.ServerDetail{} for _, server := range servers { if server.Status == status { matched = append(matched, server) } } if len(matched) == 0 { // no match, so no need to look further return nil } servers = matched } if nameRex := f[nova.FilterServer]; nameRex != "" { matched := []nova.ServerDetail{} rex, err := regexp.Compile(nameRex) if err != nil { fmt.Printf("cannot compile regexp filter %q: %v\n", nameRex, err) // effectively nothing matches return nil } for _, server := range servers { if rex.MatchString(server.Name) { matched = append(matched, server) } } if len(matched) == 0 { // no match, here so ignore other results return nil } servers = matched } return servers // TODO(dimitern) - 2013-02-11 bug=1121690 // implement FilterFlavor, FilterImage, FilterMarker, FilterLimit and FilterChangesSince } // allServers returns a list of all existing servers. // Filtering is supported, see filter type for more info. func (n *Nova) allServers(f filter) []nova.ServerDetail { return n.matchServers(f) } // allServersAsEntities returns all servers as Entity structs. // Filtering is supported, see filter type for more info. func (n *Nova) allServersAsEntities(f filter) []nova.Entity { var entities []nova.Entity servers := n.matchServers(f) for _, server := range servers { entities = append(entities, nova.Entity{ Id: server.Id, UUID: server.UUID, Name: server.Name, Links: server.Links, }) } return entities } // removeServer deletes an existing server. func (n *Nova) removeServer(serverId string) error { if err := n.ProcessFunctionHook(n, serverId); err != nil { return err } if _, err := n.server(serverId); err != nil { return err } delete(n.servers, serverId) return nil } // addSecurityGroup creates a new security group. func (n *Nova) addSecurityGroup(group nova.SecurityGroup) error { if err := n.ProcessFunctionHook(n, group); err != nil { return err } if _, err := n.securityGroup(group.Id); err == nil { return fmt.Errorf("a security group with id %s already exists", group.Id) } group.TenantId = n.TenantId if group.Rules == nil { group.Rules = []nova.SecurityGroupRule{} } n.groups[group.Id] = group return nil } // securityGroup retrieves an existing group by ID. func (n *Nova) securityGroup(groupId string) (*nova.SecurityGroup, error) { if err := n.ProcessFunctionHook(n, groupId); err != nil { return nil, err } group, ok := n.groups[groupId] if !ok { return nil, fmt.Errorf("no such security group %s", groupId) } return &group, nil } // securityGroupByName retrieves an existing named group. func (n *Nova) securityGroupByName(groupName string) (*nova.SecurityGroup, error) { if err := n.ProcessFunctionHook(n, groupName); err != nil { return nil, err } for _, group := range n.groups { if group.Name == groupName { return &group, nil } } return nil, fmt.Errorf("no such security group named %q", groupName) } // allSecurityGroups returns a list of all existing groups. func (n *Nova) allSecurityGroups() []nova.SecurityGroup { var groups []nova.SecurityGroup for _, group := range n.groups { groups = append(groups, group) } return groups } // removeSecurityGroup deletes an existing group. func (n *Nova) removeSecurityGroup(groupId string) error { if err := n.ProcessFunctionHook(n, groupId); err != nil { return err } if _, err := n.securityGroup(groupId); err != nil { return err } delete(n.groups, groupId) return nil } // addSecurityGroupRule creates a new rule in an existing group. // This can be either an ingress or a group rule (see the notes // about nova.RuleInfo). func (n *Nova) addSecurityGroupRule(ruleId string, rule nova.RuleInfo) error { if err := n.ProcessFunctionHook(n, ruleId, rule); err != nil { return err } if _, err := n.securityGroupRule(ruleId); err == nil { return fmt.Errorf("a security group rule with id %s already exists", ruleId) } group, err := n.securityGroup(rule.ParentGroupId) if err != nil { return err } for _, ru := range group.Rules { if ru.Id == ruleId { return fmt.Errorf("cannot add twice rule %s to security group %s", ru.Id, group.Id) } } var zeroSecurityGroupRef nova.SecurityGroupRef newrule := nova.SecurityGroupRule{ ParentGroupId: rule.ParentGroupId, Id: ruleId, Group: zeroSecurityGroupRef, } if rule.GroupId != nil { sourceGroup, err := n.securityGroup(*rule.GroupId) if err != nil { return fmt.Errorf("unknown source security group %s", *rule.GroupId) } newrule.Group = nova.SecurityGroupRef{ TenantId: sourceGroup.TenantId, Name: sourceGroup.Name, } } if rule.FromPort != 0 { newrule.FromPort = &rule.FromPort } if rule.ToPort != 0 { newrule.ToPort = &rule.ToPort } if rule.IPProtocol != "" { newrule.IPProtocol = &rule.IPProtocol } if rule.Cidr != "" { newrule.IPRange = make(map[string]string) newrule.IPRange["cidr"] = rule.Cidr } group.Rules = append(group.Rules, newrule) n.groups[group.Id] = *group n.rules[newrule.Id] = newrule return nil } // hasSecurityGroupRule returns whether the given group contains the given rule, // or (when groupId="-1") whether the given rule exists. func (n *Nova) hasSecurityGroupRule(groupId, ruleId string) bool { rule, ok := n.rules[ruleId] _, err := n.securityGroup(groupId) return ok && (groupId == "-1" || (err == nil && rule.ParentGroupId == groupId)) } // securityGroupRule retrieves an existing rule by ID. func (n *Nova) securityGroupRule(ruleId string) (*nova.SecurityGroupRule, error) { if err := n.ProcessFunctionHook(n, ruleId); err != nil { return nil, err } rule, ok := n.rules[ruleId] if !ok { return nil, fmt.Errorf("no such security group rule %s", ruleId) } return &rule, nil } // removeSecurityGroupRule deletes an existing rule from its group. func (n *Nova) removeSecurityGroupRule(ruleId string) error { if err := n.ProcessFunctionHook(n, ruleId); err != nil { return err } rule, err := n.securityGroupRule(ruleId) if err != nil { return err } if group, err := n.securityGroup(rule.ParentGroupId); err == nil { idx := -1 for ri, ru := range group.Rules { if ru.Id == ruleId { idx = ri break } } if idx != -1 { group.Rules = append(group.Rules[:idx], group.Rules[idx+1:]...) n.groups[group.Id] = *group } // Silently ignore missing rules... } // ...or groups delete(n.rules, ruleId) return nil } // addServerSecurityGroup attaches an existing server to a group. func (n *Nova) addServerSecurityGroup(serverId string, groupId string) error { if err := n.ProcessFunctionHook(n, serverId, groupId); err != nil { return err } if _, err := n.server(serverId); err != nil { return err } if _, err := n.securityGroup(groupId); err != nil { return err } groups, ok := n.serverGroups[serverId] if ok { for _, gid := range groups { if gid == groupId { return fmt.Errorf("server %q already belongs to group %s", serverId, groupId) } } } groups = append(groups, groupId) n.serverGroups[serverId] = groups return nil } // hasServerSecurityGroup returns whether the given server belongs to the group. func (n *Nova) hasServerSecurityGroup(serverId string, groupId string) bool { if _, err := n.server(serverId); err != nil { return false } if _, err := n.securityGroup(groupId); err != nil { return false } groups, ok := n.serverGroups[serverId] if !ok { return false } for _, gid := range groups { if gid == groupId { return true } } return false } // allServerSecurityGroups returns all security groups attached to the // given server. func (n *Nova) allServerSecurityGroups(serverId string) []nova.SecurityGroup { var groups []nova.SecurityGroup for _, gid := range n.serverGroups[serverId] { group, err := n.securityGroup(gid) if err != nil { return nil } groups = append(groups, *group) } return groups } // removeServerSecurityGroup detaches an existing server from a group. func (n *Nova) removeServerSecurityGroup(serverId string, groupId string) error { if err := n.ProcessFunctionHook(n, serverId, groupId); err != nil { return err } if _, err := n.server(serverId); err != nil { return err } if _, err := n.securityGroup(groupId); err != nil { return err } groups, ok := n.serverGroups[serverId] if !ok { return fmt.Errorf("server %q does not belong to any groups", serverId) } idx := -1 for gi, gid := range groups { if gid == groupId { idx = gi break } } if idx == -1 { return fmt.Errorf("server %q does not belong to group %s", serverId, groupId) } groups = append(groups[:idx], groups[idx+1:]...) n.serverGroups[serverId] = groups return nil } // addFloatingIP creates a new floating IP address in the pool. func (n *Nova) addFloatingIP(ip nova.FloatingIP) error { if err := n.ProcessFunctionHook(n, ip); err != nil { return err } if _, err := n.floatingIP(ip.Id); err == nil { return fmt.Errorf("a floating IP with id %s already exists", ip.Id) } n.floatingIPs[ip.Id] = ip return nil } // hasFloatingIP returns whether the given floating IP address exists. func (n *Nova) hasFloatingIP(address string) bool { if len(n.floatingIPs) == 0 { return false } for _, fip := range n.floatingIPs { if fip.IP == address { return true } } return false } // floatingIP retrieves the floating IP by ID. func (n *Nova) floatingIP(ipId string) (*nova.FloatingIP, error) { if err := n.ProcessFunctionHook(n, ipId); err != nil { return nil, err } ip, ok := n.floatingIPs[ipId] if !ok { return nil, fmt.Errorf("no such floating IP %s", ipId) } return &ip, nil } // floatingIPByAddr retrieves the floating IP by address. func (n *Nova) floatingIPByAddr(address string) (*nova.FloatingIP, error) { if err := n.ProcessFunctionHook(n, address); err != nil { return nil, err } for _, fip := range n.floatingIPs { if fip.IP == address { return &fip, nil } } return nil, fmt.Errorf("no such floating IP with address %q", address) } // allFloatingIPs returns a list of all created floating IPs. func (n *Nova) allFloatingIPs() []nova.FloatingIP { var fips []nova.FloatingIP for _, fip := range n.floatingIPs { fips = append(fips, fip) } return fips } // removeFloatingIP deletes an existing floating IP by ID. func (n *Nova) removeFloatingIP(ipId string) error { if err := n.ProcessFunctionHook(n, ipId); err != nil { return err } if _, err := n.floatingIP(ipId); err != nil { return err } delete(n.floatingIPs, ipId) return nil } // addServerFloatingIP attaches an existing floating IP to a server. func (n *Nova) addServerFloatingIP(serverId string, ipId string) error { if err := n.ProcessFunctionHook(n, serverId, ipId); err != nil { return err } if _, err := n.server(serverId); err != nil { return err } if fip, err := n.floatingIP(ipId); err != nil { return err } else { fixedIP := "4.3.2.1" // not important really, unused fip.FixedIP = &fixedIP fip.InstanceId = &serverId n.floatingIPs[ipId] = *fip } fips, ok := n.serverIPs[serverId] if ok { for _, fipId := range fips { if fipId == ipId { return fmt.Errorf("server %q already has floating IP %s", serverId, ipId) } } } fips = append(fips, ipId) n.serverIPs[serverId] = fips return nil } // hasServerFloatingIP verifies the given floating IP belongs to a server. func (n *Nova) hasServerFloatingIP(serverId, address string) bool { if _, err := n.server(serverId); err != nil || !n.hasFloatingIP(address) { return false } fips, ok := n.serverIPs[serverId] if !ok { return false } for _, fipId := range fips { fip := n.floatingIPs[fipId] if fip.IP == address { return true } } return false } // removeServerFloatingIP deletes an attached floating IP from a server. func (n *Nova) removeServerFloatingIP(serverId string, ipId string) error { if err := n.ProcessFunctionHook(n, serverId); err != nil { return err } if _, err := n.server(serverId); err != nil { return err } if fip, err := n.floatingIP(ipId); err != nil { return err } else { fip.FixedIP = nil fip.InstanceId = nil n.floatingIPs[ipId] = *fip } fips, ok := n.serverIPs[serverId] if !ok { return fmt.Errorf("server %q does not have any floating IPs to remove", serverId) } idx := -1 for fi, fipId := range fips { if fipId == ipId { idx = fi break } } if idx == -1 { return fmt.Errorf("server %q does not have floating IP %s", serverId, ipId) } fips = append(fips[:idx], fips[idx+1:]...) n.serverIPs[serverId] = fips return nil } // allNetworks returns a list of all existing networks. func (n *Nova) allNetworks() (networks []nova.Network) { for _, net := range n.networks { networks = append(networks, net) } return networks } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/novaservice/setup_test.go���������������������0000644�0000153�0000161�00000000710�12321735747�027677� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package novaservice import ( . "launchpad.net/gocheck" "launchpad.net/goose/nova" "testing" ) func Test(t *testing.T) { TestingT(t) } // checkGroupsInList checks that every group in groups is in groupList. func checkGroupsInList(c *C, groups []nova.SecurityGroup, groupList []nova.SecurityGroup) { for _, g := range groups { for _, gr := range groupList { if g.Id == gr.Id { c.Assert(g, DeepEquals, gr) return } } c.Fail() } } ��������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/novaservice/service_http_test.go��������������0000644�0000153�0000161�00000104523�12321735747�031245� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Nova double testing service - HTTP API tests package novaservice import ( "bytes" "encoding/json" "fmt" "io/ioutil" . "launchpad.net/gocheck" "launchpad.net/goose/nova" "launchpad.net/goose/testing/httpsuite" "launchpad.net/goose/testservices/identityservice" "net/http" "sort" "strconv" "strings" ) type NovaHTTPSuite struct { httpsuite.HTTPSuite service *Nova token string } var _ = Suite(&NovaHTTPSuite{}) type NovaHTTPSSuite struct { httpsuite.HTTPSuite service *Nova token string } var _ = Suite(&NovaHTTPSSuite{HTTPSuite: httpsuite.HTTPSuite{UseTLS: true}}) func (s *NovaHTTPSuite) SetUpSuite(c *C) { s.HTTPSuite.SetUpSuite(c) identityDouble := identityservice.NewUserPass() userInfo := identityDouble.AddUser("fred", "secret", "tenant") s.token = userInfo.Token s.service = New(s.Server.URL, versionPath, userInfo.TenantId, region, identityDouble) } func (s *NovaHTTPSuite) TearDownSuite(c *C) { s.HTTPSuite.TearDownSuite(c) } func (s *NovaHTTPSuite) SetUpTest(c *C) { s.HTTPSuite.SetUpTest(c) s.service.SetupHTTP(s.Mux) } func (s *NovaHTTPSuite) TearDownTest(c *C) { s.HTTPSuite.TearDownTest(c) } // assertJSON asserts the passed http.Response's body can be // unmarshalled into the given expected object, populating it with the // successfully parsed data. func assertJSON(c *C, resp *http.Response, expected interface{}) { body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() c.Assert(err, IsNil) err = json.Unmarshal(body, &expected) c.Assert(err, IsNil) } // assertBody asserts the passed http.Response's body matches the // expected response, replacing any variables in the expected body. func assertBody(c *C, resp *http.Response, expected *errorResponse) { body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() c.Assert(err, IsNil) expBody := expected.requestBody(resp.Request) // cast to string for easier asserts debugging c.Assert(string(body), Equals, string(expBody)) } // sendRequest constructs an HTTP request from the parameters and // sends it, returning the response or an error. func (s *NovaHTTPSuite) sendRequest(method, url string, body []byte, headers http.Header) (*http.Response, error) { if !strings.HasPrefix(url, "http") { url = "http://" + s.service.Hostname + strings.TrimLeft(url, "/") } req, err := http.NewRequest(method, url, bytes.NewReader(body)) if err != nil { return nil, err } for header, values := range headers { for _, value := range values { req.Header.Add(header, value) } } // workaround for https://code.google.com/p/go/issues/detail?id=4454 req.Header.Set("Content-Length", strconv.Itoa(len(body))) return http.DefaultClient.Do(req) } // authRequest is a shortcut for sending requests with pre-set token // header and correct version prefix and tenant ID in the URL. func (s *NovaHTTPSuite) authRequest(method, path string, body []byte, headers http.Header) (*http.Response, error) { if headers == nil { headers = make(http.Header) } headers.Set(authToken, s.token) url := s.service.endpointURL(true, path) return s.sendRequest(method, url, body, headers) } // jsonRequest serializes the passed body object to JSON and sends a // the request with authRequest(). func (s *NovaHTTPSuite) jsonRequest(method, path string, body interface{}, headers http.Header) (*http.Response, error) { jsonBody, err := json.Marshal(body) if err != nil { return nil, err } return s.authRequest(method, path, jsonBody, headers) } // setHeader creates http.Header map, sets the given header, and // returns the map. func setHeader(header, value string) http.Header { h := make(http.Header) h.Set(header, value) return h } // SimpleTest defines a simple request without a body and expected response. type SimpleTest struct { unauth bool method string url string headers http.Header expect *errorResponse } func (s *NovaHTTPSuite) simpleTests() []SimpleTest { var simpleTests = []SimpleTest{ { unauth: true, method: "GET", url: "/any", headers: make(http.Header), expect: errUnauthorized, }, { unauth: true, method: "POST", url: "/any", headers: setHeader(authToken, "phony"), expect: errUnauthorized, }, { unauth: true, method: "GET", url: "/", headers: setHeader(authToken, s.token), expect: errNoVersion, }, { unauth: true, method: "GET", url: "/any", headers: setHeader(authToken, s.token), expect: errMultipleChoices, }, { unauth: true, method: "POST", url: "/any/unknown/one", headers: setHeader(authToken, s.token), expect: errMultipleChoices, }, { method: "POST", url: "/any/unknown/one", expect: errNotFound, }, { unauth: true, method: "GET", url: versionPath + "/phony_token", headers: setHeader(authToken, s.token), expect: errBadRequest, }, { method: "GET", url: "/flavors/", expect: errNotFound, }, { method: "GET", url: "/flavors/invalid", expect: errNotFound, }, { method: "POST", url: "/flavors", expect: errBadRequest2, }, { method: "POST", url: "/flavors/invalid", expect: errNotFound, }, { method: "PUT", url: "/flavors", expect: errNotFound, }, { method: "PUT", url: "/flavors/invalid", expect: errNotFoundJSON, }, { method: "DELETE", url: "/flavors", expect: errNotFound, }, { method: "DELETE", url: "/flavors/invalid", expect: errForbidden, }, { method: "GET", url: "/flavors/detail/invalid", expect: errNotFound, }, { method: "POST", url: "/flavors/detail", expect: errNotFound, }, { method: "POST", url: "/flavors/detail/invalid", expect: errNotFound, }, { method: "PUT", url: "/flavors/detail", expect: errNotFoundJSON, }, { method: "PUT", url: "/flavors/detail/invalid", expect: errNotFound, }, { method: "DELETE", url: "/flavors/detail", expect: errForbidden, }, { method: "DELETE", url: "/flavors/detail/invalid", expect: errNotFound, }, { method: "GET", url: "/servers/invalid", expect: errNotFoundJSON, }, { method: "POST", url: "/servers", expect: errBadRequest2, }, { method: "POST", url: "/servers/invalid", expect: errNotFound, }, { method: "PUT", url: "/servers", expect: errNotFound, }, { method: "PUT", url: "/servers/invalid", expect: errBadRequest2, }, { method: "DELETE", url: "/servers", expect: errNotFound, }, { method: "DELETE", url: "/servers/invalid", expect: errNotFoundJSON, }, { method: "GET", url: "/servers/detail/invalid", expect: errNotFound, }, { method: "POST", url: "/servers/detail", expect: errNotFound, }, { method: "POST", url: "/servers/detail/invalid", expect: errNotFound, }, { method: "PUT", url: "/servers/detail", expect: errBadRequest2, }, { method: "PUT", url: "/servers/detail/invalid", expect: errNotFound, }, { method: "DELETE", url: "/servers/detail", expect: errNotFoundJSON, }, { method: "DELETE", url: "/servers/detail/invalid", expect: errNotFound, }, { method: "GET", url: "/os-security-groups/42", expect: errNotFoundJSONSG, }, { method: "POST", url: "/os-security-groups", expect: errBadRequest2, }, { method: "POST", url: "/os-security-groups/invalid", expect: errNotFound, }, { method: "PUT", url: "/os-security-groups", expect: errNotFound, }, { method: "PUT", url: "/os-security-groups/invalid", expect: errNotFoundJSON, }, { method: "DELETE", url: "/os-security-groups", expect: errNotFound, }, { method: "DELETE", url: "/os-security-groups/42", expect: errNotFoundJSONSG, }, { method: "GET", url: "/os-security-group-rules", expect: errNotFoundJSON, }, { method: "GET", url: "/os-security-group-rules/invalid", expect: errNotFoundJSON, }, { method: "GET", url: "/os-security-group-rules/42", expect: errNotFoundJSON, }, { method: "POST", url: "/os-security-group-rules", expect: errBadRequest2, }, { method: "POST", url: "/os-security-group-rules/invalid", expect: errNotFound, }, { method: "PUT", url: "/os-security-group-rules", expect: errNotFound, }, { method: "PUT", url: "/os-security-group-rules/invalid", expect: errNotFoundJSON, }, { method: "DELETE", url: "/os-security-group-rules", expect: errNotFound, }, { method: "DELETE", url: "/os-security-group-rules/42", expect: errNotFoundJSONSGR, }, { method: "GET", url: "/os-floating-ips/42", expect: errNotFoundJSON, }, { method: "POST", url: "/os-floating-ips/invalid", expect: errNotFound, }, { method: "PUT", url: "/os-floating-ips", expect: errNotFound, }, { method: "PUT", url: "/os-floating-ips/invalid", expect: errNotFoundJSON, }, { method: "DELETE", url: "/os-floating-ips", expect: errNotFound, }, { method: "DELETE", url: "/os-floating-ips/invalid", expect: errNotFoundJSON, }, } return simpleTests } func (s *NovaHTTPSuite) TestSimpleRequestTests(c *C) { simpleTests := s.simpleTests() for i, t := range simpleTests { c.Logf("#%d. %s %s -> %d", i, t.method, t.url, t.expect.code) if t.headers == nil { t.headers = make(http.Header) t.headers.Set(authToken, s.token) } var ( resp *http.Response err error ) if t.unauth { resp, err = s.sendRequest(t.method, t.url, nil, t.headers) } else { resp, err = s.authRequest(t.method, t.url, nil, t.headers) } c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, t.expect.code) assertBody(c, resp, t.expect) } fmt.Printf("total: %d\n", len(simpleTests)) } func (s *NovaHTTPSuite) TestGetFlavors(c *C) { // The test service has 3 default flavours. var expected struct { Flavors []nova.Entity } resp, err := s.authRequest("GET", "/flavors", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Flavors, HasLen, 3) entities := s.service.allFlavorsAsEntities() c.Assert(entities, HasLen, 3) sort.Sort(nova.EntitySortBy{"Id", expected.Flavors}) sort.Sort(nova.EntitySortBy{"Id", entities}) c.Assert(expected.Flavors, DeepEquals, entities) var expectedFlavor struct { Flavor nova.FlavorDetail } resp, err = s.authRequest("GET", "/flavors/1", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expectedFlavor) c.Assert(expectedFlavor.Flavor.Name, Equals, "m1.tiny") } func (s *NovaHTTPSuite) TestGetFlavorsDetail(c *C) { // The test service has 3 default flavours. flavors := s.service.allFlavors() c.Assert(flavors, HasLen, 3) var expected struct { Flavors []nova.FlavorDetail } resp, err := s.authRequest("GET", "/flavors/detail", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Flavors, HasLen, 3) sort.Sort(nova.FlavorDetailSortBy{"Id", expected.Flavors}) sort.Sort(nova.FlavorDetailSortBy{"Id", flavors}) c.Assert(expected.Flavors, DeepEquals, flavors) resp, err = s.authRequest("GET", "/flavors/detail/1", nil, nil) c.Assert(err, IsNil) assertBody(c, resp, errNotFound) } func (s *NovaHTTPSuite) TestGetServers(c *C) { entities := s.service.allServersAsEntities(nil) c.Assert(entities, HasLen, 0) var expected struct { Servers []nova.Entity } resp, err := s.authRequest("GET", "/servers", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Servers, HasLen, 0) servers := []nova.ServerDetail{ {Id: "sr1", Name: "server 1"}, {Id: "sr2", Name: "server 2"}, } for i, server := range servers { s.service.buildServerLinks(&server) servers[i] = server err := s.service.addServer(server) c.Assert(err, IsNil) defer s.service.removeServer(server.Id) } entities = s.service.allServersAsEntities(nil) resp, err = s.authRequest("GET", "/servers", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Servers, HasLen, 2) if expected.Servers[0].Id != entities[0].Id { expected.Servers[0], expected.Servers[1] = expected.Servers[1], expected.Servers[0] } c.Assert(expected.Servers, DeepEquals, entities) var expectedServer struct { Server nova.ServerDetail } resp, err = s.authRequest("GET", "/servers/sr1", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expectedServer) c.Assert(expectedServer.Server, DeepEquals, servers[0]) } func (s *NovaHTTPSuite) TestGetServersWithFilters(c *C) { entities := s.service.allServersAsEntities(nil) c.Assert(entities, HasLen, 0) var expected struct { Servers []nova.Entity } url := "/servers?status=RESCUE&status=BUILD&name=srv2&name=srv1" resp, err := s.authRequest("GET", url, nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Servers, HasLen, 0) servers := []nova.ServerDetail{ {Id: "sr1", Name: "srv1", Status: nova.StatusBuild}, {Id: "sr2", Name: "srv2", Status: nova.StatusRescue}, {Id: "sr3", Name: "srv3", Status: nova.StatusActive}, } for i, server := range servers { s.service.buildServerLinks(&server) servers[i] = server err := s.service.addServer(server) c.Assert(err, IsNil) defer s.service.removeServer(server.Id) } resp, err = s.authRequest("GET", url, nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Servers, HasLen, 1) c.Assert(expected.Servers[0].Id, Equals, servers[0].Id) c.Assert(expected.Servers[0].Name, Equals, servers[0].Name) } func (s *NovaHTTPSuite) TestNewUUID(c *C) { uuid, err := newUUID() c.Assert(err, IsNil) var p1, p2, p3, p4, p5 string num, err := fmt.Sscanf(uuid, "%8x-%4x-%4x-%4x-%12x", &p1, &p2, &p3, &p4, &p5) c.Assert(err, IsNil) c.Assert(num, Equals, 5) uuid2, err := newUUID() c.Assert(err, IsNil) c.Assert(uuid2, Not(Equals), uuid) } func (s *NovaHTTPSuite) assertAddresses(c *C, serverId string) { server, err := s.service.server(serverId) c.Assert(err, IsNil) c.Assert(server.Addresses, HasLen, 2) c.Assert(server.Addresses["public"], HasLen, 2) c.Assert(server.Addresses["private"], HasLen, 2) for network, addresses := range server.Addresses { for _, addr := range addresses { if addr.Version == 4 && network == "public" { c.Assert(addr.Address, Matches, `127\.10\.0\.\d{1,3}`) } else if addr.Version == 4 && network == "private" { c.Assert(addr.Address, Matches, `127\.0\.0\.\d{1,3}`) } } } } func (s *NovaHTTPSuite) TestRunServer(c *C) { entities := s.service.allServersAsEntities(nil) c.Assert(entities, HasLen, 0) var req struct { Server struct { FlavorRef string `json:"flavorRef"` ImageRef string `json:"imageRef"` Name string `json:"name"` SecurityGroups []map[string]string `json:"security_groups"` } `json:"server"` } resp, err := s.jsonRequest("POST", "/servers", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusBadRequest) assertBody(c, resp, errBadRequestSrvName) req.Server.Name = "srv1" resp, err = s.jsonRequest("POST", "/servers", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusBadRequest) assertBody(c, resp, errBadRequestSrvImage) req.Server.ImageRef = "image" resp, err = s.jsonRequest("POST", "/servers", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusBadRequest) assertBody(c, resp, errBadRequestSrvFlavor) req.Server.FlavorRef = "flavor" var expected struct { Server struct { SecurityGroups []map[string]string `json:"security_groups"` Id string Links []nova.Link AdminPass string } } resp, err = s.jsonRequest("POST", "/servers", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusAccepted) assertJSON(c, resp, &expected) c.Assert(expected.Server.SecurityGroups, HasLen, 1) c.Assert(expected.Server.SecurityGroups[0]["name"], Equals, "default") c.Assert(expected.Server.Id, Not(Equals), "") c.Assert(expected.Server.Links, HasLen, 2) c.Assert(expected.Server.AdminPass, Not(Equals), "") s.assertAddresses(c, expected.Server.Id) srv, err := s.service.server(expected.Server.Id) c.Assert(err, IsNil) c.Assert(srv.Links, DeepEquals, expected.Server.Links) s.service.removeServer(srv.Id) req.Server.Name = "test2" req.Server.SecurityGroups = []map[string]string{ {"name": "default"}, {"name": "group1"}, {"name": "group2"}, } err = s.service.addSecurityGroup(nova.SecurityGroup{Id: "1", Name: "group1"}) c.Assert(err, IsNil) defer s.service.removeSecurityGroup("1") err = s.service.addSecurityGroup(nova.SecurityGroup{Id: "2", Name: "group2"}) c.Assert(err, IsNil) defer s.service.removeSecurityGroup("2") resp, err = s.jsonRequest("POST", "/servers", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusAccepted) assertJSON(c, resp, &expected) c.Assert(expected.Server.SecurityGroups, DeepEquals, req.Server.SecurityGroups) srv, err = s.service.server(expected.Server.Id) c.Assert(err, IsNil) ok := s.service.hasServerSecurityGroup(srv.Id, "1") c.Assert(ok, Equals, true) ok = s.service.hasServerSecurityGroup(srv.Id, "2") c.Assert(ok, Equals, true) ok = s.service.hasServerSecurityGroup(srv.Id, "999") c.Assert(ok, Equals, true) s.service.removeServerSecurityGroup(srv.Id, "1") s.service.removeServerSecurityGroup(srv.Id, "2") s.service.removeServerSecurityGroup(srv.Id, "999") s.service.removeServer(srv.Id) } func (s *NovaHTTPSuite) TestDeleteServer(c *C) { server := nova.ServerDetail{Id: "sr1"} _, err := s.service.server(server.Id) c.Assert(err, NotNil) err = s.service.addServer(server) c.Assert(err, IsNil) defer s.service.removeServer(server.Id) resp, err := s.authRequest("DELETE", "/servers/sr1", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusNoContent) _, err = s.service.server(server.Id) c.Assert(err, NotNil) } func (s *NovaHTTPSuite) TestGetServersDetail(c *C) { servers := s.service.allServers(nil) c.Assert(servers, HasLen, 0) var expected struct { Servers []nova.ServerDetail `json:"servers"` } resp, err := s.authRequest("GET", "/servers/detail", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Servers, HasLen, 0) servers = []nova.ServerDetail{ {Id: "sr1", Name: "server 1"}, {Id: "sr2", Name: "server 2"}, } for i, server := range servers { s.service.buildServerLinks(&server) servers[i] = server err := s.service.addServer(server) c.Assert(err, IsNil) defer s.service.removeServer(server.Id) } resp, err = s.authRequest("GET", "/servers/detail", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Servers, HasLen, 2) if expected.Servers[0].Id != servers[0].Id { expected.Servers[0], expected.Servers[1] = expected.Servers[1], expected.Servers[0] } c.Assert(expected.Servers, DeepEquals, servers) resp, err = s.authRequest("GET", "/servers/detail/sr1", nil, nil) c.Assert(err, IsNil) assertBody(c, resp, errNotFound) } func (s *NovaHTTPSuite) TestGetServersDetailWithFilters(c *C) { servers := s.service.allServers(nil) c.Assert(servers, HasLen, 0) var expected struct { Servers []nova.ServerDetail `json:"servers"` } url := "/servers/detail?status=RESCUE&status=BUILD&name=srv2&name=srv1" resp, err := s.authRequest("GET", url, nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Servers, HasLen, 0) servers = []nova.ServerDetail{ {Id: "sr1", Name: "srv1", Status: nova.StatusBuild}, {Id: "sr2", Name: "srv2", Status: nova.StatusRescue}, {Id: "sr3", Name: "srv3", Status: nova.StatusActive}, } for i, server := range servers { s.service.buildServerLinks(&server) servers[i] = server err := s.service.addServer(server) c.Assert(err, IsNil) defer s.service.removeServer(server.Id) } resp, err = s.authRequest("GET", url, nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Servers, HasLen, 1) c.Assert(expected.Servers[0], DeepEquals, servers[0]) } func (s *NovaHTTPSuite) TestGetSecurityGroups(c *C) { // There is always a default security group. groups := s.service.allSecurityGroups() c.Assert(groups, HasLen, 1) var expected struct { Groups []nova.SecurityGroup `json:"security_groups"` } resp, err := s.authRequest("GET", "/os-security-groups", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Groups, HasLen, 1) groups = []nova.SecurityGroup{ { Id: "1", Name: "group 1", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{}, }, { Id: "2", Name: "group 2", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{}, }, } for _, group := range groups { err := s.service.addSecurityGroup(group) c.Assert(err, IsNil) defer s.service.removeSecurityGroup(group.Id) } resp, err = s.authRequest("GET", "/os-security-groups", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Groups, HasLen, len(groups)+1) checkGroupsInList(c, groups, expected.Groups) var expectedGroup struct { Group nova.SecurityGroup `json:"security_group"` } resp, err = s.authRequest("GET", "/os-security-groups/1", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expectedGroup) c.Assert(expectedGroup.Group, DeepEquals, groups[0]) } func (s *NovaHTTPSuite) TestAddSecurityGroup(c *C) { group := nova.SecurityGroup{ Id: "1", Name: "group 1", Description: "desc", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{}, } _, err := s.service.securityGroup(group.Id) c.Assert(err, NotNil) var req struct { Group struct { Name string `json:"name"` Description string `json:"description"` } `json:"security_group"` } req.Group.Name = group.Name req.Group.Description = group.Description var expected struct { Group nova.SecurityGroup `json:"security_group"` } resp, err := s.jsonRequest("POST", "/os-security-groups", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Group, DeepEquals, group) err = s.service.removeSecurityGroup(group.Id) c.Assert(err, IsNil) } func (s *NovaHTTPSuite) TestDeleteSecurityGroup(c *C) { group := nova.SecurityGroup{Id: "1", Name: "group 1"} _, err := s.service.securityGroup(group.Id) c.Assert(err, NotNil) err = s.service.addSecurityGroup(group) c.Assert(err, IsNil) defer s.service.removeSecurityGroup(group.Id) resp, err := s.authRequest("DELETE", "/os-security-groups/1", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusAccepted) _, err = s.service.securityGroup(group.Id) c.Assert(err, NotNil) } func (s *NovaHTTPSuite) TestAddSecurityGroupRule(c *C) { group1 := nova.SecurityGroup{Id: "1", Name: "src"} group2 := nova.SecurityGroup{Id: "2", Name: "tgt"} err := s.service.addSecurityGroup(group1) c.Assert(err, IsNil) defer s.service.removeSecurityGroup(group1.Id) err = s.service.addSecurityGroup(group2) c.Assert(err, IsNil) defer s.service.removeSecurityGroup(group2.Id) riIngress := nova.RuleInfo{ ParentGroupId: "1", FromPort: 1234, ToPort: 4321, IPProtocol: "tcp", Cidr: "1.2.3.4/5", } riGroup := nova.RuleInfo{ ParentGroupId: group2.Id, GroupId: &group1.Id, } iprange := make(map[string]string) iprange["cidr"] = riIngress.Cidr rule1 := nova.SecurityGroupRule{ Id: "1", ParentGroupId: group1.Id, FromPort: &riIngress.FromPort, ToPort: &riIngress.ToPort, IPProtocol: &riIngress.IPProtocol, IPRange: iprange, } rule2 := nova.SecurityGroupRule{ Id: "2", ParentGroupId: group2.Id, Group: nova.SecurityGroupRef{ Name: group1.Name, TenantId: s.service.TenantId, }, } ok := s.service.hasSecurityGroupRule(group1.Id, rule1.Id) c.Assert(ok, Equals, false) ok = s.service.hasSecurityGroupRule(group2.Id, rule2.Id) c.Assert(ok, Equals, false) var req struct { Rule nova.RuleInfo `json:"security_group_rule"` } req.Rule = riIngress var expected struct { Rule nova.SecurityGroupRule `json:"security_group_rule"` } resp, err := s.jsonRequest("POST", "/os-security-group-rules", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Rule.Id, Equals, rule1.Id) c.Assert(expected.Rule.ParentGroupId, Equals, rule1.ParentGroupId) c.Assert(expected.Rule.Group, Equals, nova.SecurityGroupRef{}) c.Assert(*expected.Rule.FromPort, Equals, *rule1.FromPort) c.Assert(*expected.Rule.ToPort, Equals, *rule1.ToPort) c.Assert(*expected.Rule.IPProtocol, Equals, *rule1.IPProtocol) c.Assert(expected.Rule.IPRange, DeepEquals, rule1.IPRange) defer s.service.removeSecurityGroupRule(rule1.Id) req.Rule = riGroup resp, err = s.jsonRequest("POST", "/os-security-group-rules", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.Rule.Id, Equals, rule2.Id) c.Assert(expected.Rule.ParentGroupId, Equals, rule2.ParentGroupId) c.Assert(expected.Rule.Group, DeepEquals, rule2.Group) err = s.service.removeSecurityGroupRule(rule2.Id) c.Assert(err, IsNil) } func (s *NovaHTTPSuite) TestDeleteSecurityGroupRule(c *C) { group1 := nova.SecurityGroup{Id: "1", Name: "src"} group2 := nova.SecurityGroup{Id: "2", Name: "tgt"} err := s.service.addSecurityGroup(group1) c.Assert(err, IsNil) defer s.service.removeSecurityGroup(group1.Id) err = s.service.addSecurityGroup(group2) c.Assert(err, IsNil) defer s.service.removeSecurityGroup(group2.Id) riGroup := nova.RuleInfo{ ParentGroupId: group2.Id, GroupId: &group1.Id, } rule := nova.SecurityGroupRule{ Id: "1", ParentGroupId: group2.Id, Group: nova.SecurityGroupRef{ Name: group1.Name, TenantId: group1.TenantId, }, } err = s.service.addSecurityGroupRule(rule.Id, riGroup) c.Assert(err, IsNil) resp, err := s.authRequest("DELETE", "/os-security-group-rules/1", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusAccepted) ok := s.service.hasSecurityGroupRule(group2.Id, rule.Id) c.Assert(ok, Equals, false) } func (s *NovaHTTPSuite) TestAddServerSecurityGroup(c *C) { group := nova.SecurityGroup{Id: "1", Name: "group"} err := s.service.addSecurityGroup(group) c.Assert(err, IsNil) defer s.service.removeSecurityGroup(group.Id) server := nova.ServerDetail{Id: "sr1"} err = s.service.addServer(server) c.Assert(err, IsNil) defer s.service.removeServer(server.Id) ok := s.service.hasServerSecurityGroup(server.Id, group.Id) c.Assert(ok, Equals, false) var req struct { Group struct { Name string `json:"name"` } `json:"addSecurityGroup"` } req.Group.Name = group.Name resp, err := s.jsonRequest("POST", "/servers/"+server.Id+"/action", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusAccepted) ok = s.service.hasServerSecurityGroup(server.Id, group.Id) c.Assert(ok, Equals, true) err = s.service.removeServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) } func (s *NovaHTTPSuite) TestGetServerSecurityGroups(c *C) { server := nova.ServerDetail{Id: "sr1"} groups := []nova.SecurityGroup{ { Id: "1", Name: "group1", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{}, }, { Id: "2", Name: "group2", TenantId: s.service.TenantId, Rules: []nova.SecurityGroupRule{}, }, } srvGroups := s.service.allServerSecurityGroups(server.Id) c.Assert(srvGroups, HasLen, 0) err := s.service.addServer(server) c.Assert(err, IsNil) defer s.service.removeServer(server.Id) for _, group := range groups { err = s.service.addSecurityGroup(group) c.Assert(err, IsNil) defer s.service.removeSecurityGroup(group.Id) err = s.service.addServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) defer s.service.removeServerSecurityGroup(server.Id, group.Id) } srvGroups = s.service.allServerSecurityGroups(server.Id) var expected struct { Groups []nova.SecurityGroup `json:"security_groups"` } resp, err := s.authRequest("GET", "/servers/"+server.Id+"/os-security-groups", nil, nil) c.Assert(err, IsNil) assertJSON(c, resp, &expected) c.Assert(expected.Groups, DeepEquals, groups) } func (s *NovaHTTPSuite) TestDeleteServerSecurityGroup(c *C) { group := nova.SecurityGroup{Id: "1", Name: "group"} err := s.service.addSecurityGroup(group) c.Assert(err, IsNil) defer s.service.removeSecurityGroup(group.Id) server := nova.ServerDetail{Id: "sr1"} err = s.service.addServer(server) c.Assert(err, IsNil) defer s.service.removeServer(server.Id) ok := s.service.hasServerSecurityGroup(server.Id, group.Id) c.Assert(ok, Equals, false) err = s.service.addServerSecurityGroup(server.Id, group.Id) c.Assert(err, IsNil) var req struct { Group struct { Name string `json:"name"` } `json:"removeSecurityGroup"` } req.Group.Name = group.Name resp, err := s.jsonRequest("POST", "/servers/"+server.Id+"/action", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusAccepted) ok = s.service.hasServerSecurityGroup(server.Id, group.Id) c.Assert(ok, Equals, false) } func (s *NovaHTTPSuite) TestPostFloatingIP(c *C) { fip := nova.FloatingIP{Id: "1", IP: "10.0.0.1", Pool: "nova"} c.Assert(s.service.allFloatingIPs(), HasLen, 0) var expected struct { IP nova.FloatingIP `json:"floating_ip"` } resp, err := s.authRequest("POST", "/os-floating-ips", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.IP, DeepEquals, fip) err = s.service.removeFloatingIP(fip.Id) c.Assert(err, IsNil) } func (s *NovaHTTPSuite) TestGetFloatingIPs(c *C) { c.Assert(s.service.allFloatingIPs(), HasLen, 0) var expected struct { IPs []nova.FloatingIP `json:"floating_ips"` } resp, err := s.authRequest("GET", "/os-floating-ips", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) c.Assert(expected.IPs, HasLen, 0) fips := []nova.FloatingIP{ {Id: "1", IP: "1.2.3.4", Pool: "nova"}, {Id: "2", IP: "4.3.2.1", Pool: "nova"}, } for _, fip := range fips { err := s.service.addFloatingIP(fip) defer s.service.removeFloatingIP(fip.Id) c.Assert(err, IsNil) } resp, err = s.authRequest("GET", "/os-floating-ips", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expected) if expected.IPs[0].Id != fips[0].Id { expected.IPs[0], expected.IPs[1] = expected.IPs[1], expected.IPs[0] } c.Assert(expected.IPs, DeepEquals, fips) var expectedIP struct { IP nova.FloatingIP `json:"floating_ip"` } resp, err = s.authRequest("GET", "/os-floating-ips/1", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusOK) assertJSON(c, resp, &expectedIP) c.Assert(expectedIP.IP, DeepEquals, fips[0]) } func (s *NovaHTTPSuite) TestDeleteFloatingIP(c *C) { fip := nova.FloatingIP{Id: "1", IP: "10.0.0.1", Pool: "nova"} err := s.service.addFloatingIP(fip) c.Assert(err, IsNil) defer s.service.removeFloatingIP(fip.Id) resp, err := s.authRequest("DELETE", "/os-floating-ips/1", nil, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusAccepted) _, err = s.service.floatingIP(fip.Id) c.Assert(err, NotNil) } func (s *NovaHTTPSuite) TestAddServerFloatingIP(c *C) { fip := nova.FloatingIP{Id: "1", IP: "1.2.3.4"} server := nova.ServerDetail{Id: "sr1"} err := s.service.addFloatingIP(fip) c.Assert(err, IsNil) defer s.service.removeFloatingIP(fip.Id) err = s.service.addServer(server) c.Assert(err, IsNil) defer s.service.removeServer(server.Id) c.Assert(s.service.hasServerFloatingIP(server.Id, fip.IP), Equals, false) var req struct { AddFloatingIP struct { Address string `json:"address"` } `json:"addFloatingIp"` } req.AddFloatingIP.Address = fip.IP resp, err := s.jsonRequest("POST", "/servers/"+server.Id+"/action", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusAccepted) c.Assert(s.service.hasServerFloatingIP(server.Id, fip.IP), Equals, true) err = s.service.removeServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) } func (s *NovaHTTPSuite) TestRemoveServerFloatingIP(c *C) { fip := nova.FloatingIP{Id: "1", IP: "1.2.3.4"} server := nova.ServerDetail{Id: "sr1"} err := s.service.addFloatingIP(fip) c.Assert(err, IsNil) defer s.service.removeFloatingIP(fip.Id) err = s.service.addServer(server) c.Assert(err, IsNil) defer s.service.removeServer(server.Id) err = s.service.addServerFloatingIP(server.Id, fip.Id) c.Assert(err, IsNil) defer s.service.removeServerFloatingIP(server.Id, fip.Id) c.Assert(s.service.hasServerFloatingIP(server.Id, fip.IP), Equals, true) var req struct { RemoveFloatingIP struct { Address string `json:"address"` } `json:"removeFloatingIp"` } req.RemoveFloatingIP.Address = fip.IP resp, err := s.jsonRequest("POST", "/servers/"+server.Id+"/action", req, nil) c.Assert(err, IsNil) c.Assert(resp.StatusCode, Equals, http.StatusAccepted) c.Assert(s.service.hasServerFloatingIP(server.Id, fip.IP), Equals, false) } func (s *NovaHTTPSSuite) SetUpSuite(c *C) { s.HTTPSuite.SetUpSuite(c) identityDouble := identityservice.NewUserPass() userInfo := identityDouble.AddUser("fred", "secret", "tenant") s.token = userInfo.Token c.Assert(s.Server.URL[:8], Equals, "https://") s.service = New(s.Server.URL, versionPath, userInfo.TenantId, region, identityDouble) } func (s *NovaHTTPSSuite) TearDownSuite(c *C) { s.HTTPSuite.TearDownSuite(c) } func (s *NovaHTTPSSuite) SetUpTest(c *C) { s.HTTPSuite.SetUpTest(c) s.service.SetupHTTP(s.Mux) } func (s *NovaHTTPSSuite) TearDownTest(c *C) { s.HTTPSuite.TearDownTest(c) } func (s *NovaHTTPSSuite) TestHasHTTPSServiceURL(c *C) { endpoints := s.service.Endpoints() c.Assert(endpoints[0].PublicURL[:8], Equals, "https://") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/openstackservice/�����������������������������0000755�0000153�0000161�00000000000�12321735747�026177� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testservices/openstackservice/openstack.go�����������������0000644�0000153�0000161�00000005044�12321735747�030520� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package openstackservice import ( "fmt" "launchpad.net/goose/identity" "launchpad.net/goose/testservices/identityservice" "launchpad.net/goose/testservices/novaservice" "launchpad.net/goose/testservices/swiftservice" "net/http" "strings" ) // Openstack provides an Openstack service double implementation. type Openstack struct { Identity identityservice.IdentityService Nova *novaservice.Nova Swift *swiftservice.Swift } // New creates an instance of a full Openstack service double. // An initial user with the specified credentials is registered with the identity service. func New(cred *identity.Credentials, authMode identity.AuthMode) *Openstack { var openstack Openstack if authMode == identity.AuthKeyPair { openstack = Openstack{ Identity: identityservice.NewKeyPair(), } } else { openstack = Openstack{ Identity: identityservice.NewUserPass(), } } userInfo := openstack.Identity.AddUser(cred.User, cred.Secrets, cred.TenantName) if cred.TenantName == "" { panic("Openstack service double requires a tenant to be specified.") } openstack.Nova = novaservice.New(cred.URL, "v2", userInfo.TenantId, cred.Region, openstack.Identity) // Create the swift service using only the region base so we emulate real world deployments. regionParts := strings.Split(cred.Region, ".") baseRegion := regionParts[len(regionParts)-1] openstack.Swift = swiftservice.New(cred.URL, "v1", userInfo.TenantId, baseRegion, openstack.Identity) // Create container and add image metadata endpoint so that product-streams URLs are included // in the keystone catalog. err := openstack.Swift.AddContainer("imagemetadata") if err != nil { panic(fmt.Errorf("setting up image metadata container: %v", err)) } url := openstack.Swift.Endpoints()[0].PublicURL serviceDef := identityservice.Service{"simplestreams", "product-streams", []identityservice.Endpoint{ identityservice.Endpoint{PublicURL: url + "/imagemetadata", Region: cred.Region}, }} openstack.Identity.AddService(serviceDef) // Add public bucket endpoint so that juju-tools URLs are included in the keystone catalog. serviceDef = identityservice.Service{"juju", "juju-tools", []identityservice.Endpoint{ identityservice.Endpoint{PublicURL: url, Region: cred.Region}, }} openstack.Identity.AddService(serviceDef) return &openstack } // SetupHTTP attaches all the needed handlers to provide the HTTP API for the Openstack service.. func (openstack *Openstack) SetupHTTP(mux *http.ServeMux) { openstack.Identity.SetupHTTP(mux) openstack.Nova.SetupHTTP(mux) openstack.Swift.SetupHTTP(mux) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/.lbox.check������������������������������������������������0000755�0000153�0000161�00000000551�12321735747�022131� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh set -e BADFMT=`find * -name '*.go' | xargs gofmt -l` if [ -n "$BADFMT" ]; then BADFMT=`echo "$BADFMT" | sed "s/^/ /"` echo "gofmt is sad:\n\n$BADFMT" exit 1 fi go build ./... VERSION=`go version | awk '{print $3}'` if [ $VERSION == 'devel' ]; then go tool vet \ -methods \ -printf \ -rangeloops \ -printfuncs ErrorContextf:1 \ . fi �������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/version.go�������������������������������������������������0000644�0000153�0000161�00000000636�12321735747�022125� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. package goose import ( "fmt" ) type VersionNum struct { Major int Minor int Micro int } func (v *VersionNum) String() string { return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Micro) } var VersionNumber = VersionNum{ Major: 0, Minor: 1, Micro: 0, } var Version = VersionNumber.String() ��������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/.bzrignore�������������������������������������������������0000644�0000153�0000161�00000000047�12321735747�022107� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./tags testservices/testservices* TAGS �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/.lbox������������������������������������������������������0000644�0000153�0000161�00000000032�12321735747�021044� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������propose -cr -for lp:goose ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/client/����������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�021362� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/client/client_test.go��������������������������������������0000644�0000153�0000161�00000002163�12321735747�024230� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package client_test import ( "flag" . "launchpad.net/gocheck" "launchpad.net/goose/identity" "testing" ) var live = flag.Bool("live", false, "Include live OpenStack (Canonistack) tests") var liveAuthMode = flag.String( "live-auth-mode", "userpass", "The authentication mode to use when running live tests [all|legacy|userpass|keypair]") func Test(t *testing.T) { var allAuthModes = []identity.AuthMode{identity.AuthLegacy, identity.AuthUserPass, identity.AuthKeyPair} var liveAuthModes []identity.AuthMode switch *liveAuthMode { default: t.Fatalf("Invalid auth method specified: %s", *liveAuthMode) case "all": liveAuthModes = allAuthModes case "": case "keypair": liveAuthModes = []identity.AuthMode{identity.AuthKeyPair} case "userpass": liveAuthModes = []identity.AuthMode{identity.AuthUserPass} case "legacy": liveAuthModes = []identity.AuthMode{identity.AuthLegacy} } if *live { cred, err := identity.CompleteCredentialsFromEnv() if err != nil { t.Fatalf("Error setting up test suite: %v", err) } registerOpenStackTests(cred, liveAuthModes) } registerLocalTests(allAuthModes) TestingT(t) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/client/export_test.go��������������������������������������0000644�0000153�0000161�00000000650�12321735747�024272� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package client import ( "launchpad.net/goose/identity" "time" ) type AuthCleanup func() func SetAuthenticationTimeout(timeout time.Duration) AuthCleanup { origTimeout := authenticationTimeout authenticationTimeout = timeout return func() { authenticationTimeout = origTimeout } } func SetAuthenticator(client AuthenticatingClient, auth identity.Authenticator) { client.(*authenticatingClient).authMode = auth } ����������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/client/local_test.go���������������������������������������0000644�0000153�0000161�00000032277�12321735747�024055� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package client_test import ( "encoding/json" "fmt" . "launchpad.net/gocheck" "launchpad.net/goose/client" "launchpad.net/goose/errors" "launchpad.net/goose/identity" "launchpad.net/goose/swift" "launchpad.net/goose/testing/httpsuite" "launchpad.net/goose/testservices" "launchpad.net/goose/testservices/identityservice" "launchpad.net/goose/testservices/openstackservice" "net/url" "runtime" "sync" "time" ) func registerLocalTests(authModes []identity.AuthMode) { for _, authMode := range authModes { Suite(&localLiveSuite{ LiveTests: LiveTests{ authMode: authMode, }, }) } Suite(&localHTTPSSuite{HTTPSuite: httpsuite.HTTPSuite{UseTLS: true}}) } // localLiveSuite runs tests from LiveTests using a fake // identity server that runs within the test process itself. type localLiveSuite struct { LiveTests // The following attributes are for using testing doubles. httpsuite.HTTPSuite service testservices.HttpService } func (s *localLiveSuite) SetUpSuite(c *C) { c.Logf("Using identity service test double") s.HTTPSuite.SetUpSuite(c) s.cred = &identity.Credentials{ URL: s.Server.URL, User: "fred", Secrets: "secret", Region: "zone1.some region", TenantName: "tenant", } switch s.authMode { default: panic("Invalid authentication method") case identity.AuthKeyPair: // The openstack test service sets up keypair authentication. s.service = openstackservice.New(s.cred, identity.AuthKeyPair) // Add an additional endpoint so region filtering can be properly tested. serviceDef := identityservice.Service{"nova", "compute", []identityservice.Endpoint{ identityservice.Endpoint{PublicURL: "http://nova2", Region: "zone2.RegionOne"}, }} s.service.(*openstackservice.Openstack).Identity.AddService(serviceDef) case identity.AuthUserPass: // The openstack test service sets up userpass authentication. s.service = openstackservice.New(s.cred, identity.AuthUserPass) // Add an additional endpoint so region filtering can be properly tested. serviceDef := identityservice.Service{"nova", "compute", []identityservice.Endpoint{ identityservice.Endpoint{PublicURL: "http://nova2", Region: "zone2.RegionOne"}, }} s.service.(*openstackservice.Openstack).Identity.AddService(serviceDef) case identity.AuthLegacy: legacy := identityservice.NewLegacy() legacy.AddUser(s.cred.User, s.cred.Secrets, s.cred.TenantName) legacy.SetManagementURL("http://management.test.invalid/url") s.service = legacy } s.LiveTests.SetUpSuite(c) } func (s *localLiveSuite) TearDownSuite(c *C) { s.LiveTests.TearDownSuite(c) s.HTTPSuite.TearDownSuite(c) } func (s *localLiveSuite) SetUpTest(c *C) { s.HTTPSuite.SetUpTest(c) s.service.SetupHTTP(s.Mux) s.LiveTests.SetUpTest(c) } func (s *localLiveSuite) TearDownTest(c *C) { s.LiveTests.TearDownTest(c) s.HTTPSuite.TearDownTest(c) } // Additional tests to be run against the service double only go here. func (s *localLiveSuite) TestInvalidRegion(c *C) { if s.authMode == identity.AuthLegacy { c.Skip("legacy authentication doesn't use regions") } creds := &identity.Credentials{ User: "fred", URL: s.Server.URL, Secrets: "secret", Region: "invalid", } cl := client.NewClient(creds, s.authMode, nil) err := cl.Authenticate() c.Assert(err.Error(), Matches, "(.|\n)*invalid region(.|\n)*") } // Test service lookup with inexact region matching. func (s *localLiveSuite) TestInexactRegionMatch(c *C) { if s.authMode == identity.AuthLegacy { c.Skip("legacy authentication doesn't use regions") } cl := client.NewClient(s.cred, s.authMode, nil) err := cl.Authenticate() serviceURL, err := cl.MakeServiceURL("compute", []string{}) c.Assert(err, IsNil) _, err = url.Parse(serviceURL) c.Assert(err, IsNil) serviceURL, err = cl.MakeServiceURL("object-store", []string{}) c.Assert(err, IsNil) _, err = url.Parse(serviceURL) c.Assert(err, IsNil) } type fakeAuthenticator struct { mu sync.Mutex nrCallers int // authStart is used as a gate to signal the fake authenticator that it can start. authStart chan struct{} } func newAuthenticator(bufsize int) *fakeAuthenticator { return &fakeAuthenticator{ authStart: make(chan struct{}, bufsize), } } func (auth *fakeAuthenticator) Auth(creds *identity.Credentials) (*identity.AuthDetails, error) { auth.mu.Lock() auth.nrCallers++ auth.mu.Unlock() // Wait till the test says the authenticator can proceed. <-auth.authStart runtime.Gosched() defer func() { auth.mu.Lock() auth.nrCallers-- auth.mu.Unlock() }() auth.mu.Lock() tooManyCallers := auth.nrCallers > 1 auth.mu.Unlock() if tooManyCallers { return nil, fmt.Errorf("Too many callers of Auth function") } URLs := make(map[string]identity.ServiceURLs) endpoints := make(map[string]string) endpoints["compute"] = "http://localhost" URLs[creds.Region] = endpoints return &identity.AuthDetails{ Token: "token", TenantId: "tenant", UserId: "1", RegionServiceURLs: URLs, }, nil } func (s *localLiveSuite) TestAuthenticationTimeout(c *C) { cl := client.NewClient(s.cred, s.authMode, nil) defer client.SetAuthenticationTimeout(1 * time.Millisecond)() auth := newAuthenticator(0) client.SetAuthenticator(cl, auth) var err error err = cl.Authenticate() // Wake up the authenticator after we have timed out. auth.authStart <- struct{}{} c.Assert(errors.IsTimeout(err), Equals, true) } func (s *localLiveSuite) assertAuthenticationSuccess(c *C) client.Client { cl := client.NewClient(s.cred, s.authMode, nil) cl.SetRequiredServiceTypes([]string{"compute"}) defer client.SetAuthenticationTimeout(1 * time.Millisecond)() auth := newAuthenticator(1) client.SetAuthenticator(cl, auth) // Signal that the authenticator can proceed immediately. auth.authStart <- struct{}{} err := cl.Authenticate() c.Assert(err, IsNil) // It completed with no error but check it also ran correctly. c.Assert(cl.IsAuthenticated(), Equals, true) return cl } func (s *localLiveSuite) TestAuthenticationSuccess(c *C) { cl := s.assertAuthenticationSuccess(c) URL, err := cl.MakeServiceURL("compute", nil) c.Assert(err, IsNil) c.Assert(URL, Equals, "http://localhost") } func (s *localLiveSuite) TestMakeServiceURL(c *C) { cl := s.assertAuthenticationSuccess(c) URL, err := cl.MakeServiceURL("compute", []string{"foo"}) c.Assert(err, IsNil) c.Assert(URL, Equals, "http://localhost/foo") } func (s *localLiveSuite) TestMakeServiceURLRetainsTrailingSlash(c *C) { cl := s.assertAuthenticationSuccess(c) URL, err := cl.MakeServiceURL("compute", []string{"foo", "bar/"}) c.Assert(err, IsNil) c.Assert(URL, Equals, "http://localhost/foo/bar/") } func checkAuthentication(cl client.AuthenticatingClient) error { err := cl.Authenticate() if err != nil { return err } URL, err := cl.MakeServiceURL("compute", nil) if err != nil { return err } if URL != "http://localhost" { return fmt.Errorf("Unexpected URL: %s", URL) } return nil } func (s *localLiveSuite) TestAuthenticationForbidsMultipleCallers(c *C) { if s.authMode == identity.AuthLegacy { c.Skip("legacy authentication") } cl := client.NewClient(s.cred, s.authMode, nil) cl.SetRequiredServiceTypes([]string{"compute"}) auth := newAuthenticator(2) client.SetAuthenticator(cl, auth) // Signal that the authenticator can proceed immediately. auth.authStart <- struct{}{} auth.authStart <- struct{}{} var allDone sync.WaitGroup allDone.Add(2) var err1, err2 error go func() { err1 = checkAuthentication(cl) allDone.Done() }() go func() { err2 = checkAuthentication(cl) allDone.Done() }() allDone.Wait() c.Assert(err1, IsNil) c.Assert(err2, IsNil) } type configurableAuth struct { regionsURLs map[string]identity.ServiceURLs } func NewConfigurableAuth(regionsURLData string) *configurableAuth { auth := &configurableAuth{} err := json.Unmarshal([]byte(regionsURLData), &auth.regionsURLs) if err != nil { panic(err) } return auth } func (auth *configurableAuth) Auth(creds *identity.Credentials) (*identity.AuthDetails, error) { return &identity.AuthDetails{ Token: "token", TenantId: "tenant", UserId: "1", RegionServiceURLs: auth.regionsURLs, }, nil } type authRegionTest struct { region string regionURLInfo string errorMsg string } var missingEndpointMsgf = "(.|\n)*the configured region %q does not allow access to all required services, namely: %s(.|\n)*access to these services is missing: %s" var missingEndpointSuggestRegionMsgf = "(.|\n)*the configured region %q does not allow access to all required services, namely: %s(.|\n)*access to these services is missing: %s(.|\n)*one of these regions may be suitable instead: %s" var invalidRegionMsgf = "(.|\n)*invalid region %q" var authRegionTests = []authRegionTest{ authRegionTest{ "a.region.1", `{"a.region.1":{"compute":"http://foo"}}`, fmt.Sprintf(missingEndpointMsgf, "a.region.1", "compute, object-store", "object-store"), }, authRegionTest{ "b.region.1", `{"a.region.1":{"compute":"http://foo"}}`, fmt.Sprintf(invalidRegionMsgf, "b.region.1"), }, authRegionTest{ "b.region.1", `{"a.region.1":{"compute":"http://foo"}, "region.1":{"object-store":"http://foobar"}}`, fmt.Sprintf(missingEndpointSuggestRegionMsgf, "b.region.1", "compute, object-store", "compute", "a.region.1"), }, authRegionTest{ "region.1", `{"a.region.1":{"compute":"http://foo"}, "region.1":{"object-store":"http://foobar"}}`, fmt.Sprintf(missingEndpointSuggestRegionMsgf, "region.1", "compute, object-store", "compute", "a.region.1"), }, } func (s *localLiveSuite) TestNonAccessibleServiceType(c *C) { if s.authMode == identity.AuthLegacy { c.Skip("legacy authentication") } for _, at := range authRegionTests { s.cred.Region = at.region cl := client.NewClient(s.cred, s.authMode, nil) auth := NewConfigurableAuth(at.regionURLInfo) client.SetAuthenticator(cl, auth) err := cl.Authenticate() c.Assert(err, ErrorMatches, at.errorMsg) } } type localHTTPSSuite struct { // The following attributes are for using testing doubles. httpsuite.HTTPSuite service testservices.HttpService cred *identity.Credentials } func (s *localHTTPSSuite) SetUpSuite(c *C) { c.Logf("Using identity service test double") s.HTTPSuite.SetUpSuite(c) c.Assert(s.Server.URL[:8], Equals, "https://") s.cred = &identity.Credentials{ URL: s.Server.URL, User: "fred", Secrets: "secret", Region: "zone1.some region", TenantName: "tenant", } // The openstack test service sets up userpass authentication. s.service = openstackservice.New(s.cred, identity.AuthUserPass) // Add an additional endpoint so region filtering can be properly tested. serviceDef := identityservice.Service{"nova", "compute", []identityservice.Endpoint{ identityservice.Endpoint{PublicURL: "https://nova2", Region: "zone2.RegionOne"}, }} s.service.(*openstackservice.Openstack).Identity.AddService(serviceDef) } func (s *localHTTPSSuite) TearDownSuite(c *C) { s.HTTPSuite.TearDownSuite(c) } func (s *localHTTPSSuite) SetUpTest(c *C) { s.HTTPSuite.SetUpTest(c) s.service.SetupHTTP(s.Mux) } func (s *localHTTPSSuite) TearDownTest(c *C) { s.HTTPSuite.TearDownTest(c) } func (s *localHTTPSSuite) TestDefaultClientRefusesSelfSigned(c *C) { cl := client.NewClient(s.cred, identity.AuthUserPass, nil) err := cl.Authenticate() c.Assert(err, ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority") } func (s *localHTTPSSuite) TestNonValidatingClientAcceptsSelfSigned(c *C) { cl := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil) err := cl.Authenticate() c.Assert(err, IsNil) // Requests into this client should be https:// URLs swiftURL, err := cl.MakeServiceURL("object-store", []string{"test_container"}) c.Assert(err, IsNil) c.Assert(swiftURL[:8], Equals, "https://") // We use swiftClient.CreateContainer to test a Binary request swiftClient := swift.New(cl) c.Assert(swiftClient.CreateContainer("test_container", swift.Private), IsNil) // And we use List to test the JsonRequest contents, err := swiftClient.List("test_container", "", "", "", 0) c.Assert(err, IsNil) c.Check(contents, DeepEquals, []swift.ContainerContents{}) } func (s *localHTTPSSuite) setupPublicContainer(c *C) string { // First set up a container that can be read publically authClient := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil) authSwift := swift.New(authClient) err := authSwift.CreateContainer("test_container", swift.PublicRead) c.Assert(err, IsNil) baseURL, err := authClient.MakeServiceURL("object-store", nil) c.Assert(err, IsNil) c.Assert(baseURL[:8], Equals, "https://") return baseURL } func (s *localHTTPSSuite) TestDefaultPublicClientRefusesSelfSigned(c *C) { baseURL := s.setupPublicContainer(c) swiftClient := swift.New(client.NewPublicClient(baseURL, nil)) contents, err := swiftClient.List("test_container", "", "", "", 0) c.Assert(err, ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority") c.Assert(contents, DeepEquals, []swift.ContainerContents(nil)) } func (s *localHTTPSSuite) TestNonValidatingPublicClientAcceptsSelfSigned(c *C) { baseURL := s.setupPublicContainer(c) swiftClient := swift.New(client.NewNonValidatingPublicClient(baseURL, nil)) contents, err := swiftClient.List("test_container", "", "", "", 0) c.Assert(err, IsNil) c.Assert(contents, DeepEquals, []swift.ContainerContents{}) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/client/live_test.go����������������������������������������0000644�0000153�0000161�00000002750�12321735747�023713� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package client_test import ( . "launchpad.net/gocheck" "launchpad.net/goose/client" "launchpad.net/goose/identity" ) func registerOpenStackTests(cred *identity.Credentials, authModes []identity.AuthMode) { for _, authMode := range authModes { Suite(&LiveTests{ cred: cred, authMode: authMode, }) } } type LiveTests struct { cred *identity.Credentials authMode identity.AuthMode } func (s *LiveTests) SetUpSuite(c *C) { c.Logf("Running tests with authentication method %v", s.authMode) } func (s *LiveTests) TearDownSuite(c *C) { // noop, called by local test suite. } func (s *LiveTests) SetUpTest(c *C) { // noop, called by local test suite. } func (s *LiveTests) TearDownTest(c *C) { // noop, called by local test suite. } func (s *LiveTests) TestAuthenticateFail(c *C) { cred := *s.cred cred.User = "fred" cred.Secrets = "broken" cred.Region = "" osclient := client.NewClient(&cred, s.authMode, nil) c.Assert(osclient.IsAuthenticated(), Equals, false) err := osclient.Authenticate() c.Assert(err, ErrorMatches, "authentication failed(\n|.)*") } func (s *LiveTests) TestAuthenticate(c *C) { cl := client.NewClient(s.cred, s.authMode, nil) err := cl.Authenticate() c.Assert(err, IsNil) c.Assert(cl.IsAuthenticated(), Equals, true) // Check service endpoints are discovered url, err := cl.MakeServiceURL("compute", nil) c.Check(err, IsNil) c.Check(url, NotNil) url, err = cl.MakeServiceURL("object-store", nil) c.Check(err, IsNil) c.Check(url, NotNil) } ������������������������juju-core_1.18.1/src/launchpad.net/goose/client/client.go�������������������������������������������0000644�0000153�0000161�00000031606�12321735747�023175� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package client import ( "errors" "fmt" gooseerrors "launchpad.net/goose/errors" goosehttp "launchpad.net/goose/http" "launchpad.net/goose/identity" goosesync "launchpad.net/goose/sync" "log" "sort" "strings" "sync" "time" ) const ( apiTokens = "/tokens" // The HTTP request methods. GET = "GET" POST = "POST" PUT = "PUT" DELETE = "DELETE" HEAD = "HEAD" COPY = "COPY" ) // Client implementations sends service requests to an OpenStack deployment. type Client interface { SendRequest(method, svcType, apiCall string, requestData *goosehttp.RequestData) (err error) // MakeServiceURL prepares a full URL to a service endpoint, with optional // URL parts. It uses the first endpoint it can find for the given service type. MakeServiceURL(serviceType string, parts []string) (string, error) } // AuthenticatingClient sends service requests to an OpenStack deployment after first validating // a user's credentials. type AuthenticatingClient interface { Client SetRequiredServiceTypes(requiredServiceTypes []string) Authenticate() error IsAuthenticated() bool Token() string UserId() string TenantId() string } // A single http client is shared between all Goose clients. var sharedHttpClient = goosehttp.New() // This client sends requests without authenticating. type client struct { mu sync.Mutex logger *log.Logger baseURL string httpClient *goosehttp.Client } var _ Client = (*client)(nil) // This client authenticates before sending requests. type authenticatingClient struct { client creds *identity.Credentials authMode identity.Authenticator auth AuthenticatingClient // Service type to endpoint URLs for each available region regionServiceURLs map[string]identity.ServiceURLs // Service type to endpoint URLs for the authenticated region serviceURLs identity.ServiceURLs // The service types which must be available after authentication, // or else services which use this client will not be able to function as expected. requiredServiceTypes []string tokenId string tenantId string userId string } var _ AuthenticatingClient = (*authenticatingClient)(nil) func NewPublicClient(baseURL string, logger *log.Logger) Client { client := client{baseURL: baseURL, logger: logger, httpClient: sharedHttpClient} return &client } func NewNonValidatingPublicClient(baseURL string, logger *log.Logger) Client { return &client{ baseURL: baseURL, logger: logger, httpClient: goosehttp.NewNonSSLValidating(), } } var defaultRequiredServiceTypes = []string{"compute", "object-store"} func newClient(creds *identity.Credentials, auth_method identity.AuthMode, httpClient *goosehttp.Client, logger *log.Logger) AuthenticatingClient { client_creds := *creds client_creds.URL = client_creds.URL + apiTokens client := authenticatingClient{ creds: &client_creds, requiredServiceTypes: defaultRequiredServiceTypes, client: client{logger: logger, httpClient: httpClient}, } client.auth = &client client.authMode = identity.NewAuthenticator(auth_method, httpClient) return &client } func NewClient(creds *identity.Credentials, auth_method identity.AuthMode, logger *log.Logger) AuthenticatingClient { return newClient(creds, auth_method, sharedHttpClient, logger) } func NewNonValidatingClient(creds *identity.Credentials, auth_method identity.AuthMode, logger *log.Logger) AuthenticatingClient { return newClient(creds, auth_method, goosehttp.NewNonSSLValidating(), logger) } func (c *client) sendRequest(method, url, token string, requestData *goosehttp.RequestData) (err error) { if requestData.ReqValue != nil || requestData.RespValue != nil { err = c.httpClient.JsonRequest(method, url, token, requestData, c.logger) } else { err = c.httpClient.BinaryRequest(method, url, token, requestData, c.logger) } return } func (c *client) SendRequest(method, svcType, apiCall string, requestData *goosehttp.RequestData) error { url, _ := c.MakeServiceURL(svcType, []string{apiCall}) return c.sendRequest(method, url, "", requestData) } func makeURL(base string, parts []string) string { if !strings.HasSuffix(base, "/") && len(parts) > 0 { base += "/" } return base + strings.Join(parts, "/") } func (c *client) MakeServiceURL(serviceType string, parts []string) (string, error) { return makeURL(c.baseURL, parts), nil } func (c *authenticatingClient) SetRequiredServiceTypes(requiredServiceTypes []string) { c.requiredServiceTypes = requiredServiceTypes } func (c *authenticatingClient) SendRequest(method, svcType, apiCall string, requestData *goosehttp.RequestData) (err error) { err = c.sendAuthRequest(method, svcType, apiCall, requestData) if gooseerrors.IsUnauthorised(err) { c.setToken("") err = c.sendAuthRequest(method, svcType, apiCall, requestData) } return } func (c *authenticatingClient) sendAuthRequest(method, svcType, apiCall string, requestData *goosehttp.RequestData) (err error) { if err = c.Authenticate(); err != nil { return } url, err := c.MakeServiceURL(svcType, []string{apiCall}) if err != nil { return } return c.sendRequest(method, url, c.Token(), requestData) } func (c *authenticatingClient) MakeServiceURL(serviceType string, parts []string) (string, error) { if !c.IsAuthenticated() { return "", errors.New("cannot get endpoint URL without being authenticated") } url, ok := c.serviceURLs[serviceType] if !ok { return "", errors.New("no endpoints known for service type: " + serviceType) } return makeURL(url, parts), nil } // Return the relevant service endpoint URLs for this client's region. // The region comes from the client credentials. func (c *authenticatingClient) createServiceURLs() error { var serviceURLs identity.ServiceURLs = nil var otherServiceTypeRegions map[string][]string = make(map[string][]string) for region, urls := range c.regionServiceURLs { if regionMatches(c.creds.Region, region) { if serviceURLs == nil { serviceURLs = make(identity.ServiceURLs) } for serviceType, endpointURL := range urls { serviceURLs[serviceType] = endpointURL } } else { for serviceType := range urls { regions := otherServiceTypeRegions[serviceType] if regions == nil { regions = []string{} } otherServiceTypeRegions[serviceType] = append(regions, region) } } } var errorPrefix string var possibleRegions, missingServiceTypes []string if serviceURLs == nil { var knownRegions []string for r := range c.regionServiceURLs { knownRegions = append(knownRegions, r) } missingServiceTypes, possibleRegions = c.possibleRegions([]string{}) errorPrefix = fmt.Sprintf("invalid region %q", c.creds.Region) } else { existingServiceTypes := []string{} for serviceType, _ := range serviceURLs { if containsString(c.requiredServiceTypes, serviceType) { existingServiceTypes = append(existingServiceTypes, serviceType) } } missingServiceTypes, possibleRegions = c.possibleRegions(existingServiceTypes) errorPrefix = fmt.Sprintf("the configured region %q does not allow access to all required services, namely: %s\n"+ "access to these services is missing: %s", c.creds.Region, strings.Join(c.requiredServiceTypes, ", "), strings.Join(missingServiceTypes, ", ")) } if len(missingServiceTypes) > 0 { if len(possibleRegions) > 0 { return fmt.Errorf("%s\none of these regions may be suitable instead: %s", errorPrefix, strings.Join(possibleRegions, ", ")) } else { return errors.New(errorPrefix) } } c.serviceURLs = serviceURLs return nil } // possibleRegions returns a list of regions, any of which will allow the client to access the required service types. // This method is called when a client authenticates and the configured region does not allow access to all the // required service types. The service types which are accessible, accessibleServiceTypes, is passed in and the // method returns what the missing service types are as well as valid regions. func (c *authenticatingClient) possibleRegions(accessibleServiceTypes []string) (missingServiceTypes []string, possibleRegions []string) { var serviceTypeRegions map[string][]string // Figure out the missing service types and build up a map of all service types to regions // obtained from the authentication response. for _, serviceType := range c.requiredServiceTypes { if !containsString(accessibleServiceTypes, serviceType) { missingServiceTypes = append(missingServiceTypes, serviceType) serviceTypeRegions = c.extractServiceTypeRegions() } } // Look at the region lists for each missing service type and determine which subset of those could // be used to allow access to all required service types. The most specific regions are used. if len(missingServiceTypes) == 1 { possibleRegions = serviceTypeRegions[missingServiceTypes[0]] } else { for _, serviceType := range missingServiceTypes { for _, serviceTypeCompare := range missingServiceTypes { if serviceType == serviceTypeCompare { continue } possibleRegions = appendPossibleRegions(serviceType, serviceTypeCompare, serviceTypeRegions, possibleRegions) } } } sort.Strings(possibleRegions) return } // utility function to extract map of service types -> region func (c *authenticatingClient) extractServiceTypeRegions() map[string][]string { serviceTypeRegions := make(map[string][]string) for region, serviceURLs := range c.regionServiceURLs { for regionServiceType := range serviceURLs { regions := serviceTypeRegions[regionServiceType] if !containsString(regions, region) { serviceTypeRegions[regionServiceType] = append(regions, region) } } } return serviceTypeRegions } // extract the common regions for each service type and append them to the possible regions slice. func appendPossibleRegions(serviceType, serviceTypeCompare string, serviceTypeRegions map[string][]string, possibleRegions []string) []string { regions := serviceTypeRegions[serviceType] for _, region := range regions { regionsCompare := serviceTypeRegions[serviceTypeCompare] if !containsBaseRegion(regionsCompare, region) && containsSuperRegion(regionsCompare, region) { possibleRegions = append(possibleRegions, region) } } return possibleRegions } // utility function to see if element exists in values slice. func containsString(values []string, element string) bool { for _, value := range values { if value == element { return true } } return false } // containsBaseRegion returns true if any of the regions in values are based on region. // see client.regionMatches. func containsBaseRegion(values []string, region string) bool { for _, value := range values { if regionMatches(value, region) && region != value { return true } } return false } // containsSuperRegion returns true if region is based on any of the regions in values. // see client.regionMatches. func containsSuperRegion(values []string, region string) bool { for _, value := range values { if regionMatches(region, value) || region == value { return true } } return false } func regionMatches(userRegion, endpointRegion string) bool { // The user specified region (from the credentials config) matches // the endpoint region if the user region equals or ends with the endpoint region. // eg user region "az-1.region-a.geo-1" matches endpoint region "region-a.geo-1" return strings.HasSuffix(userRegion, endpointRegion) } func (c *authenticatingClient) setToken(tokenId string) { c.mu.Lock() c.tokenId = tokenId c.mu.Unlock() } func (c *authenticatingClient) Token() string { c.mu.Lock() defer c.mu.Unlock() return c.tokenId } func (c *authenticatingClient) UserId() string { return c.userId } func (c *authenticatingClient) TenantId() string { return c.tenantId } func (c *authenticatingClient) IsAuthenticated() bool { c.mu.Lock() defer c.mu.Unlock() return c.tokenId != "" } var authenticationTimeout = time.Duration(60) * time.Second func (c *authenticatingClient) Authenticate() (err error) { ok := goosesync.RunWithTimeout(authenticationTimeout, func() { err = c.doAuthenticate() }) if !ok { err = gooseerrors.NewTimeoutf( nil, "", "Authentication response not received in %s.", authenticationTimeout) } return err } func (c *authenticatingClient) doAuthenticate() error { c.mu.Lock() defer c.mu.Unlock() if c.creds == nil || c.tokenId != "" { return nil } if c.authMode == nil { return fmt.Errorf("Authentication method has not been specified") } var ( authDetails *identity.AuthDetails err error ) if authDetails, err = c.authMode.Auth(c.creds); err != nil { return gooseerrors.Newf(err, "authentication failed") } c.regionServiceURLs = authDetails.RegionServiceURLs if err := c.createServiceURLs(); err != nil { return gooseerrors.Newf(err, "cannot create service URLs") } c.tenantId = authDetails.TenantId c.userId = authDetails.UserId // A valid token indicates authorisation has been successful, so it needs to be set last. It must be set // after the service URLs have been extracted. c.tokenId = authDetails.Token return nil } ��������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/errors/����������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�021420� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/errors/errors_test.go��������������������������������������0000644�0000153�0000161�00000005054�12321735747�024326� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package errors_test import ( . "launchpad.net/gocheck" "launchpad.net/goose/errors" "testing" ) func Test(t *testing.T) { TestingT(t) } type ErrorsSuite struct { } var _ = Suite(&ErrorsSuite{}) func (s *ErrorsSuite) TestCreateSimpleNotFoundError(c *C) { context := "context" err := errors.NewNotFoundf(nil, context, "") c.Assert(errors.IsNotFound(err), Equals, true) c.Assert(err.Error(), Equals, "Not found: context") } func (s *ErrorsSuite) TestCreateNotFoundError(c *C) { context := "context" err := errors.NewNotFoundf(nil, context, "It was not found: %s", context) c.Assert(errors.IsNotFound(err), Equals, true) c.Assert(err.Error(), Equals, "It was not found: context") } func (s *ErrorsSuite) TestCreateSimpleDuplicateValueError(c *C) { context := "context" err := errors.NewDuplicateValuef(nil, context, "") c.Assert(errors.IsDuplicateValue(err), Equals, true) c.Assert(err.Error(), Equals, "Duplicate: context") } func (s *ErrorsSuite) TestCreateDuplicateValueError(c *C) { context := "context" err := errors.NewDuplicateValuef(nil, context, "It was duplicate: %s", context) c.Assert(errors.IsDuplicateValue(err), Equals, true) c.Assert(err.Error(), Equals, "It was duplicate: context") } func (s *ErrorsSuite) TestCreateSimpleUnauthorisedfError(c *C) { context := "context" err := errors.NewUnauthorisedf(nil, context, "") c.Assert(errors.IsUnauthorised(err), Equals, true) c.Assert(err.Error(), Equals, "Unauthorised: context") } func (s *ErrorsSuite) TestCreateUnauthorisedfError(c *C) { context := "context" err := errors.NewUnauthorisedf(nil, context, "It was unauthorised: %s", context) c.Assert(errors.IsUnauthorised(err), Equals, true) c.Assert(err.Error(), Equals, "It was unauthorised: context") } func (s *ErrorsSuite) TestErrorCause(c *C) { rootCause := errors.NewNotFoundf(nil, "some value", "") // Construct a new error, based on a not found root cause. err := errors.Newf(rootCause, "an error occurred") c.Assert(err.Cause(), Equals, rootCause) // Check the other error attributes. c.Assert(err.Error(), Equals, "an error occurred\ncaused by: Not found: some value") } func (s *ErrorsSuite) TestErrorIsType(c *C) { rootCause := errors.NewNotFoundf(nil, "some value", "") // Construct a new error, based on a not found root cause. err := errors.Newf(rootCause, "an error occurred") // Check that the error is not falsely identified as something it is not. c.Assert(errors.IsDuplicateValue(err), Equals, false) // Check that the error is correctly identified as a not found error. c.Assert(errors.IsNotFound(err), Equals, true) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/errors/errors.go�������������������������������������������0000644�0000153�0000161�00000007275�12321735747�023276� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// This package provides an Error implementation which knows about types of error, and which has support // for error causes. package errors import "fmt" type Code string const ( // Public available error types. // These errors are provided because they are specifically required by business logic in the callers. UnspecifiedError = Code("Unspecified") NotFoundError = Code("NotFound") DuplicateValueError = Code("DuplicateValue") TimeoutError = Code("Timeout") UnauthorisedError = Code("Unauthorised") ) // Error instances store an optional error cause. type Error interface { error Cause() error } type gooseError struct { error errcode Code cause error } // Type checks. var _ Error = (*gooseError)(nil) // Code returns the error code. func (err *gooseError) code() Code { if err.errcode != UnspecifiedError { return err.errcode } if e, ok := err.cause.(*gooseError); ok { return e.code() } return UnspecifiedError } // Cause returns the error cause. func (err *gooseError) Cause() error { return err.cause } // CausedBy returns true if this error or its cause are of the specified error code. func (err *gooseError) causedBy(code Code) bool { if err.code() == code { return true } if cause, ok := err.cause.(*gooseError); ok { return cause.code() == code } return false } // Error fulfills the error interface, taking account of any caused by error. func (err *gooseError) Error() string { if err.cause != nil { return fmt.Sprintf("%v\ncaused by: %v", err.error, err.cause) } return err.error.Error() } func IsNotFound(err error) bool { if e, ok := err.(*gooseError); ok { return e.causedBy(NotFoundError) } return false } func IsDuplicateValue(err error) bool { if e, ok := err.(*gooseError); ok { return e.causedBy(DuplicateValueError) } return false } func IsTimeout(err error) bool { if e, ok := err.(*gooseError); ok { return e.causedBy(TimeoutError) } return false } func IsUnauthorised(err error) bool { if e, ok := err.(*gooseError); ok { return e.causedBy(UnauthorisedError) } return false } // New creates a new Error instance with the specified cause. func makeErrorf(code Code, cause error, format string, args ...interface{}) Error { return &gooseError{ errcode: code, error: fmt.Errorf(format, args...), cause: cause, } } // New creates a new Unspecified Error instance with the specified cause. func Newf(cause error, format string, args ...interface{}) Error { return makeErrorf(UnspecifiedError, cause, format, args...) } // New creates a new NotFound Error instance with the specified cause. func NewNotFoundf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Not found: %s", context) } return makeErrorf(NotFoundError, cause, format, args...) } // New creates a new DuplicateValue Error instance with the specified cause. func NewDuplicateValuef(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Duplicate: %s", context) } return makeErrorf(DuplicateValueError, cause, format, args...) } // New creates a new Timeout Error instance with the specified cause. func NewTimeoutf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Timeout: %s", context) } return makeErrorf(TimeoutError, cause, format, args...) } // New creates a new Unauthorised Error instance with the specified cause. func NewUnauthorisedf(cause error, context interface{}, format string, args ...interface{}) Error { if format == "" { format = fmt.Sprintf("Unauthorised: %s", context) } return makeErrorf(UnauthorisedError, cause, format, args...) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/tools/�����������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�021244� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/tools/secgroup-delete-all/���������������������������������0000755�0000153�0000161�00000000000�12321735747�025101� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/tools/secgroup-delete-all/main.go��������������������������0000644�0000153�0000161�00000003151�12321735747�026354� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "fmt" "io" "launchpad.net/gnuflag" "launchpad.net/goose/client" "launchpad.net/goose/identity" "launchpad.net/goose/nova" "os" ) // DeleteAll destroys all security groups except the default func DeleteAll(w io.Writer, osn *nova.Client) (err error) { groups, err := osn.ListSecurityGroups() if err != nil { return err } deleted := 0 failed := 0 for _, group := range groups { if group.Name != "default" { err := osn.DeleteSecurityGroup(group.Id) if err != nil { failed++ } else { deleted++ } } } if deleted != 0 { fmt.Fprintf(w, "%d security groups deleted.\n", deleted) } else if failed == 0 { fmt.Fprint(w, "No security groups to delete.\n") } if failed != 0 { fmt.Fprintf(w, "%d security groups could not be deleted.\n", failed) } return nil } func createNovaClient(authMode identity.AuthMode) (osn *nova.Client, err error) { creds, err := identity.CompleteCredentialsFromEnv() if err != nil { return nil, err } osc := client.NewClient(creds, authMode, nil) return nova.New(osc), nil } var authModeFlag = gnuflag.String("auth-mode", "userpass", "type of authentication to use") var authModes = map[string]identity.AuthMode{ "userpass": identity.AuthUserPass, "legacy": identity.AuthLegacy, } func main() { gnuflag.Parse(true) mode, ok := authModes[*authModeFlag] if !ok { fmt.Fprintf(os.Stderr, "error: no such auth-mode %q\n", *authModeFlag) os.Exit(1) } novaclient, err := createNovaClient(mode) if err == nil { err = DeleteAll(os.Stdout, novaclient) } if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/tools/secgroup-delete-all/main_test.go���������������������0000644�0000153�0000161�00000005330�12321735747�027414� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main_test import ( "bytes" "fmt" . "launchpad.net/gocheck" "launchpad.net/goose/client" "launchpad.net/goose/identity" "launchpad.net/goose/nova" "launchpad.net/goose/testing/httpsuite" "launchpad.net/goose/testservices/hook" "launchpad.net/goose/testservices/openstackservice" tool "launchpad.net/goose/tools/secgroup-delete-all" "testing" ) func Test(t *testing.T) { TestingT(t) } const ( username = "auser" password = "apass" region = "aregion" tenant = "1" ) type ToolSuite struct { httpsuite.HTTPSuite creds *identity.Credentials } var _ = Suite(&ToolSuite{}) // GZ 2013-01-21: Should require EnvSuite for this, but clashes with HTTPSuite func createNovaClient(creds *identity.Credentials) *nova.Client { osc := client.NewClient(creds, identity.AuthUserPass, nil) return nova.New(osc) } func (s *ToolSuite) makeServices(c *C) (*openstackservice.Openstack, *nova.Client) { creds := &identity.Credentials{ URL: s.Server.URL, User: username, Secrets: password, Region: region, TenantName: tenant, } openstack := openstackservice.New(creds, identity.AuthUserPass) openstack.SetupHTTP(s.Mux) return openstack, createNovaClient(creds) } func (s *ToolSuite) TestNoGroups(c *C) { _, nova := s.makeServices(c) var buf bytes.Buffer err := tool.DeleteAll(&buf, nova) c.Assert(err, IsNil) c.Assert(string(buf.Bytes()), Equals, "No security groups to delete.\n") } func (s *ToolSuite) TestTwoGroups(c *C) { _, novaClient := s.makeServices(c) novaClient.CreateSecurityGroup("group-a", "A group") novaClient.CreateSecurityGroup("group-b", "Another group") var buf bytes.Buffer err := tool.DeleteAll(&buf, novaClient) c.Assert(err, IsNil) c.Assert(string(buf.Bytes()), Equals, "2 security groups deleted.\n") } // This group is one for which we will simulate a deletion error in the following test. var doNotDelete *nova.SecurityGroup // deleteGroupError hook raises an error if a group with id 2 is deleted. func deleteGroupError(s hook.ServiceControl, args ...interface{}) error { groupId := args[0].(string) if groupId == doNotDelete.Id { return fmt.Errorf("cannot delete group %d", groupId) } return nil } func (s *ToolSuite) TestUndeleteableGroup(c *C) { os, novaClient := s.makeServices(c) novaClient.CreateSecurityGroup("group-a", "A group") doNotDelete, _ = novaClient.CreateSecurityGroup("group-b", "Another group") novaClient.CreateSecurityGroup("group-c", "Yet another group") cleanup := os.Nova.RegisterControlPoint("removeSecurityGroup", deleteGroupError) defer cleanup() var buf bytes.Buffer err := tool.DeleteAll(&buf, novaClient) c.Assert(err, IsNil) c.Assert(string(buf.Bytes()), Equals, "2 security groups deleted.\n1 security groups could not be deleted.\n") } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/http/������������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�021063� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/http/client_test.go����������������������������������������0000644�0000153�0000161�00000013613�12321735747�023733� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package http import ( "bytes" "fmt" "io/ioutil" . "launchpad.net/gocheck" "launchpad.net/goose/testing/httpsuite" "net/http" "testing" ) func Test(t *testing.T) { TestingT(t) } type LoopingHTTPSuite struct { httpsuite.HTTPSuite } func (s *LoopingHTTPSuite) setupLoopbackRequest() (*http.Header, chan string, *Client) { var headers http.Header bodyChan := make(chan string, 1) handler := func(resp http.ResponseWriter, req *http.Request) { headers = req.Header bodyBytes, _ := ioutil.ReadAll(req.Body) req.Body.Close() bodyChan <- string(bodyBytes) resp.Header().Add("Content-Length", "0") resp.WriteHeader(http.StatusNoContent) resp.Write([]byte{}) } s.Mux.HandleFunc("/", handler) client := New() return &headers, bodyChan, client } type HTTPClientTestSuite struct { LoopingHTTPSuite } type HTTPSClientTestSuite struct { LoopingHTTPSuite } var _ = Suite(&HTTPClientTestSuite{}) var _ = Suite(&HTTPSClientTestSuite{LoopingHTTPSuite{httpsuite.HTTPSuite{UseTLS: true}}}) func (s *HTTPClientTestSuite) assertHeaderValues(c *C, token string) { emptyHeaders := http.Header{} headers := createHeaders(emptyHeaders, "content-type", token) contentTypes := []string{"content-type"} headerData := map[string][]string{ "Content-Type": contentTypes, "Accept": contentTypes, "User-Agent": []string{gooseAgent()}} if token != "" { headerData["X-Auth-Token"] = []string{token} } expectedHeaders := http.Header(headerData) c.Assert(headers, DeepEquals, expectedHeaders) c.Assert(emptyHeaders, DeepEquals, http.Header{}) } func (s *HTTPClientTestSuite) TestCreateHeadersNoToken(c *C) { s.assertHeaderValues(c, "") } func (s *HTTPClientTestSuite) TestCreateHeadersWithToken(c *C) { s.assertHeaderValues(c, "token") } func (s *HTTPClientTestSuite) TestCreateHeadersCopiesSupplied(c *C) { initialHeaders := make(http.Header) initialHeaders["Foo"] = []string{"Bar"} contentType := contentTypeJSON contentTypes := []string{contentType} headers := createHeaders(initialHeaders, contentType, "") // it should not change the headers passed in c.Assert(initialHeaders, DeepEquals, http.Header{"Foo": []string{"Bar"}}) // The initial headers should be in the output c.Assert(headers, DeepEquals, http.Header{"Foo": []string{"Bar"}, "Content-Type": contentTypes, "Accept": contentTypes, "User-Agent": []string{gooseAgent()}}) } func (s *HTTPClientTestSuite) TestBinaryRequestSetsUserAgent(c *C) { headers, _, client := s.setupLoopbackRequest() req := &RequestData{ExpectedStatus: []int{http.StatusNoContent}} err := client.BinaryRequest("POST", s.Server.URL, "", req, nil) c.Assert(err, IsNil) agent := headers.Get("User-Agent") c.Check(agent, Not(Equals), "") c.Check(agent, Equals, gooseAgent()) } func (s *HTTPClientTestSuite) TestJSONRequestSetsUserAgent(c *C) { headers, _, client := s.setupLoopbackRequest() req := &RequestData{ExpectedStatus: []int{http.StatusNoContent}} err := client.JsonRequest("POST", s.Server.URL, "", req, nil) c.Assert(err, IsNil) agent := headers.Get("User-Agent") c.Check(agent, Not(Equals), "") c.Check(agent, Equals, gooseAgent()) } func (s *HTTPClientTestSuite) TestBinaryRequestSetsContentLength(c *C) { headers, bodyChan, client := s.setupLoopbackRequest() content := "binary\ncontent\n" req := &RequestData{ ExpectedStatus: []int{http.StatusNoContent}, ReqReader: bytes.NewBufferString(content), ReqLength: len(content), } err := client.BinaryRequest("POST", s.Server.URL, "", req, nil) c.Assert(err, IsNil) encoding := headers.Get("Transfer-Encoding") c.Check(encoding, Equals, "") length := headers.Get("Content-Length") c.Check(length, Equals, fmt.Sprintf("%d", len(content))) body, ok := <-bodyChan c.Assert(ok, Equals, true) c.Check(body, Equals, content) } func (s *HTTPClientTestSuite) TestJSONRequestSetsContentLength(c *C) { headers, bodyChan, client := s.setupLoopbackRequest() reqMap := map[string]string{"key": "value"} req := &RequestData{ ExpectedStatus: []int{http.StatusNoContent}, ReqValue: reqMap, } err := client.JsonRequest("POST", s.Server.URL, "", req, nil) c.Assert(err, IsNil) encoding := headers.Get("Transfer-Encoding") c.Check(encoding, Equals, "") length := headers.Get("Content-Length") body, ok := <-bodyChan c.Assert(ok, Equals, true) c.Check(body, Not(Equals), "") c.Check(length, Equals, fmt.Sprintf("%d", len(body))) } func (s *HTTPClientTestSuite) TestBinaryRequestSetsToken(c *C) { headers, _, client := s.setupLoopbackRequest() req := &RequestData{ExpectedStatus: []int{http.StatusNoContent}} err := client.BinaryRequest("POST", s.Server.URL, "token", req, nil) c.Assert(err, IsNil) agent := headers.Get("X-Auth-Token") c.Check(agent, Equals, "token") } func (s *HTTPClientTestSuite) TestJSONRequestSetsToken(c *C) { headers, _, client := s.setupLoopbackRequest() req := &RequestData{ExpectedStatus: []int{http.StatusNoContent}} err := client.JsonRequest("POST", s.Server.URL, "token", req, nil) c.Assert(err, IsNil) agent := headers.Get("X-Auth-Token") c.Check(agent, Equals, "token") } func (s *HTTPClientTestSuite) TestHttpTransport(c *C) { transport := http.DefaultTransport.(*http.Transport) c.Assert(transport.DisableKeepAlives, Equals, true) } func (s *HTTPSClientTestSuite) TestDefaultClientRejectSelfSigned(c *C) { _, _, client := s.setupLoopbackRequest() req := &RequestData{ExpectedStatus: []int{http.StatusNoContent}} err := client.BinaryRequest("POST", s.Server.URL, "", req, nil) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "(.|\\n)*x509: certificate signed by unknown authority") } func (s *HTTPSClientTestSuite) TestInsecureClientAllowsSelfSigned(c *C) { headers, _, _ := s.setupLoopbackRequest() client := NewNonSSLValidating() req := &RequestData{ExpectedStatus: []int{http.StatusNoContent}} err := client.BinaryRequest("POST", s.Server.URL, "", req, nil) c.Assert(err, IsNil) agent := headers.Get("User-Agent") c.Check(agent, Not(Equals), "") c.Check(agent, Equals, gooseAgent()) } ���������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/http/client.go���������������������������������������������0000644�0000153�0000161�00000022114�12321735747�022670� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// An HTTP Client which sends json and binary requests, handling data marshalling and response processing. package http import ( "bytes" "crypto/tls" "encoding/json" "fmt" "io" "io/ioutil" "launchpad.net/goose" "launchpad.net/goose/errors" "log" "net/http" "net/url" "regexp" "strconv" "sync" "time" ) const ( contentTypeJSON = "application/json" contentTypeOctetStream = "application/octet-stream" ) func init() { // See https://code.google.com/p/go/issues/detail?id=4677 // We need to force the connection to close each time so that we don't // hit the above Go bug. roundTripper := http.DefaultClient.Transport if transport, ok := roundTripper.(*http.Transport); ok { transport.DisableKeepAlives = true } http.DefaultTransport.(*http.Transport).DisableKeepAlives = true } type Client struct { http.Client maxSendAttempts int } type ErrorResponse struct { Message string `json:"message"` Code int `json:"code"` Title string `json:"title"` } func (e *ErrorResponse) Error() string { return fmt.Sprintf("Failed: %d %s: %s", e.Code, e.Title, e.Message) } type ErrorWrapper struct { Error ErrorResponse `json:"error"` } type RequestData struct { ReqHeaders http.Header Params *url.Values ExpectedStatus []int ReqValue interface{} RespValue interface{} ReqReader io.Reader ReqLength int RespReader io.ReadCloser } const ( // The maximum number of times to try sending a request before we give up // (assuming any unsuccessful attempts can be sensibly tried again). MaxSendAttempts = 3 ) var insecureClient *http.Client var insecureClientMutex sync.Mutex // New returns a new goose http *Client using the default net/http client. func New() *Client { return &Client{*http.DefaultClient, MaxSendAttempts} } func NewNonSSLValidating() *Client { insecureClientMutex.Lock() httpClient := insecureClient if httpClient == nil { insecureConfig := &tls.Config{InsecureSkipVerify: true} insecureTransport := &http.Transport{TLSClientConfig: insecureConfig} insecureClient = &http.Client{Transport: insecureTransport} httpClient = insecureClient } insecureClientMutex.Unlock() return &Client{*httpClient, MaxSendAttempts} } func gooseAgent() string { return fmt.Sprintf("goose (%s)", goose.Version) } func createHeaders(extraHeaders http.Header, contentType, authToken string) http.Header { headers := make(http.Header) if extraHeaders != nil { for header, values := range extraHeaders { for _, value := range values { headers.Add(header, value) } } } if authToken != "" { headers.Set("X-Auth-Token", authToken) } headers.Add("Content-Type", contentType) headers.Add("Accept", contentType) headers.Add("User-Agent", gooseAgent()) return headers } // JsonRequest JSON encodes and sends the object in reqData.ReqValue (if any) to the specified URL. // Optional method arguments are passed using the RequestData object. // Relevant RequestData fields: // ReqHeaders: additional HTTP header values to add to the request. // ExpectedStatus: the allowed HTTP response status values, else an error is returned. // ReqValue: the data object to send. // RespValue: the data object to decode the result into. func (c *Client) JsonRequest(method, url, token string, reqData *RequestData, logger *log.Logger) (err error) { err = nil var body []byte if reqData.Params != nil { url += "?" + reqData.Params.Encode() } if reqData.ReqValue != nil { body, err = json.Marshal(reqData.ReqValue) if err != nil { err = errors.Newf(err, "failed marshalling the request body") return } } headers := createHeaders(reqData.ReqHeaders, contentTypeJSON, token) respBody, err := c.sendRequest( method, url, bytes.NewReader(body), len(body), headers, reqData.ExpectedStatus, logger) if err != nil { return } defer respBody.Close() respData, err := ioutil.ReadAll(respBody) if err != nil { err = errors.Newf(err, "failed reading the response body") return } if len(respData) > 0 { if reqData.RespValue != nil { err = json.Unmarshal(respData, &reqData.RespValue) if err != nil { err = errors.Newf(err, "failed unmarshaling the response body: %s", respData) } } } return } // Sends the byte array in reqData.ReqValue (if any) to the specified URL. // Optional method arguments are passed using the RequestData object. // Relevant RequestData fields: // ReqHeaders: additional HTTP header values to add to the request. // ExpectedStatus: the allowed HTTP response status values, else an error is returned. // ReqReader: an io.Reader providing the bytes to send. // RespReader: assigned an io.ReadCloser instance used to read the returned data.. func (c *Client) BinaryRequest(method, url, token string, reqData *RequestData, logger *log.Logger) (err error) { err = nil if reqData.Params != nil { url += "?" + reqData.Params.Encode() } headers := createHeaders(reqData.ReqHeaders, contentTypeOctetStream, token) respBody, err := c.sendRequest( method, url, reqData.ReqReader, reqData.ReqLength, headers, reqData.ExpectedStatus, logger) if err != nil { return } if reqData.RespReader != nil { reqData.RespReader = respBody } return } // Sends the specified request to URL and checks that the HTTP response status is as expected. // reqReader: a reader returning the data to send. // length: the number of bytes to send. // headers: HTTP headers to include with the request. // expectedStatus: a slice of allowed response status codes. func (c *Client) sendRequest(method, URL string, reqReader io.Reader, length int, headers http.Header, expectedStatus []int, logger *log.Logger) (rc io.ReadCloser, err error) { reqData := make([]byte, length) if reqReader != nil { nrRead, err := io.ReadFull(reqReader, reqData) if err != nil { err = errors.Newf(err, "failed reading the request data, read %v of %v bytes", nrRead, length) return rc, err } } rawResp, err := c.sendRateLimitedRequest(method, URL, headers, reqData, logger) if err != nil { return } foundStatus := false if len(expectedStatus) == 0 { expectedStatus = []int{http.StatusOK} } for _, status := range expectedStatus { if rawResp.StatusCode == status { foundStatus = true break } } if !foundStatus && len(expectedStatus) > 0 { err = handleError(URL, rawResp) rawResp.Body.Close() return } return rawResp.Body, err } func (c *Client) sendRateLimitedRequest(method, URL string, headers http.Header, reqData []byte, logger *log.Logger) (resp *http.Response, err error) { for i := 0; i < c.maxSendAttempts; i++ { var reqReader io.Reader if reqData != nil { reqReader = bytes.NewReader(reqData) } req, err := http.NewRequest(method, URL, reqReader) if err != nil { err = errors.Newf(err, "failed creating the request %s", URL) return nil, err } for header, values := range headers { for _, value := range values { req.Header.Add(header, value) } } req.ContentLength = int64(len(reqData)) resp, err = c.Do(req) if err != nil { return nil, errors.Newf(err, "failed executing the request %s", URL) } if resp.StatusCode != http.StatusRequestEntityTooLarge || resp.Header.Get("Retry-After") == "" { return resp, nil } resp.Body.Close() retryAfter, err := strconv.ParseFloat(resp.Header.Get("Retry-After"), 32) if err != nil { return nil, errors.Newf(err, "Invalid Retry-After header %s", URL) } if retryAfter == 0 { return nil, errors.Newf(err, "Resource limit exeeded at URL %s", URL) } if logger != nil { logger.Printf("Too many requests, retrying in %dms.", int(retryAfter*1000)) } time.Sleep(time.Duration(retryAfter) * time.Second) } return nil, errors.Newf(err, "Maximum number of attempts (%d) reached sending request to %s", c.maxSendAttempts, URL) } type HttpError struct { StatusCode int Data map[string][]string url string responseMessage string } func (e *HttpError) Error() string { return fmt.Sprintf("request (%s) returned unexpected status: %d; error info: %v", e.url, e.StatusCode, e.responseMessage, ) } // The HTTP response status code was not one of those expected, so we construct an error. // NotFound (404) codes have their own NotFound error type. // We also make a guess at duplicate value errors. func handleError(URL string, resp *http.Response) error { errBytes, _ := ioutil.ReadAll(resp.Body) errInfo := string(errBytes) // Check if we have a JSON representation of the failure, if so decode it. if resp.Header.Get("Content-Type") == contentTypeJSON { var wrappedErr ErrorWrapper if err := json.Unmarshal(errBytes, &wrappedErr); err == nil { errInfo = wrappedErr.Error.Error() } } httpError := &HttpError{ resp.StatusCode, map[string][]string(resp.Header), URL, errInfo, } switch resp.StatusCode { case http.StatusNotFound: return errors.NewNotFoundf(httpError, "", "Resource at %s not found", URL) case http.StatusForbidden, http.StatusUnauthorized: return errors.NewUnauthorisedf(httpError, "", "Unauthorised URL %s", URL) case http.StatusBadRequest: dupExp, _ := regexp.Compile(".*already exists.*") if dupExp.Match(errBytes) { return errors.NewDuplicateValuef(httpError, "", string(errBytes)) } } return httpError } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/COPYING����������������������������������������������������0000644�0000153�0000161�00000104513�12321735747�021143� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testing/���������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�021561� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testing/httpsuite/�����������������������������������������0000755�0000153�0000161�00000000000�12321735747�023612� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testing/httpsuite/httpsuite.go�����������������������������0000644�0000153�0000161�00000002077�12321735747�026200� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package httpsuite // This package provides an HTTPSuite infrastructure that lets you bring up an // HTTP server. The server will handle requests based on whatever Handlers are // attached to HTTPSuite.Mux. This Mux is reset after every test case, and the // server is shut down at the end of the test suite. import ( . "launchpad.net/gocheck" "net/http" "net/http/httptest" ) var _ = Suite(&HTTPSuite{}) type HTTPSuite struct { Server *httptest.Server Mux *http.ServeMux oldHandler http.Handler UseTLS bool } func (s *HTTPSuite) SetUpSuite(c *C) { // fmt.Printf("Starting New Server\n") if s.UseTLS { s.Server = httptest.NewTLSServer(nil) } else { s.Server = httptest.NewServer(nil) } } func (s *HTTPSuite) SetUpTest(c *C) { s.oldHandler = s.Server.Config.Handler s.Mux = http.NewServeMux() s.Server.Config.Handler = s.Mux } func (s *HTTPSuite) TearDownTest(c *C) { s.Mux = nil s.Server.Config.Handler = s.oldHandler } func (s *HTTPSuite) TearDownSuite(c *C) { if s.Server != nil { // fmt.Printf("Stopping Server\n") s.Server.Close() } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testing/httpsuite/httpsuite_test.go������������������������0000644�0000153�0000161�00000003447�12321735747�027241� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package httpsuite import ( "crypto/tls" "crypto/x509" "io/ioutil" . "launchpad.net/gocheck" "net/http" "net/url" "reflect" "testing" ) type HTTPTestSuite struct { HTTPSuite } type HTTPSTestSuite struct { HTTPSuite } func Test(t *testing.T) { TestingT(t) } var _ = Suite(&HTTPTestSuite{}) var _ = Suite(&HTTPSTestSuite{HTTPSuite{UseTLS: true}}) type HelloHandler struct{} func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(200) w.Write([]byte("Hello World\n")) } func (s *HTTPTestSuite) TestHelloWorld(c *C) { s.Mux.Handle("/", &HelloHandler{}) // fmt.Printf("Running HelloWorld\n") response, err := http.Get(s.Server.URL) c.Check(err, IsNil) content, err := ioutil.ReadAll(response.Body) response.Body.Close() c.Check(err, IsNil) c.Check(response.Status, Equals, "200 OK") c.Check(response.StatusCode, Equals, 200) c.Check(string(content), Equals, "Hello World\n") } func (s *HTTPSTestSuite) TestHelloWorldWithTLS(c *C) { s.Mux.Handle("/", &HelloHandler{}) c.Check(s.Server.URL[:8], Equals, "https://") response, err := http.Get(s.Server.URL) // Default http.Get fails because the cert is self-signed c.Assert(err, NotNil) c.Assert(reflect.TypeOf(err.(*url.Error).Err), Equals, reflect.TypeOf(x509.UnknownAuthorityError{})) // Connect again with a Client that doesn't validate the cert insecureClient := &http.Client{Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}} response, err = insecureClient.Get(s.Server.URL) c.Assert(err, IsNil) content, err := ioutil.ReadAll(response.Body) response.Body.Close() c.Check(err, IsNil) c.Check(response.Status, Equals, "200 OK") c.Check(response.StatusCode, Equals, 200) c.Check(string(content), Equals, "Hello World\n") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testing/envsuite/������������������������������������������0000755�0000153�0000161�00000000000�12321735747�023423� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testing/envsuite/envsuite.go�������������������������������0000644�0000153�0000161�00000001152�12321735747�025613� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package envsuite // Provides an EnvSuite type which makes sure this test suite gets an isolated // environment settings. Settings will be saved on start and then cleared, and // reset on tear down. import ( . "launchpad.net/gocheck" "os" "strings" ) type EnvSuite struct { environ []string } func (s *EnvSuite) SetUpSuite(c *C) { s.environ = os.Environ() } func (s *EnvSuite) SetUpTest(c *C) { os.Clearenv() } func (s *EnvSuite) TearDownTest(c *C) { for _, envstring := range s.environ { kv := strings.SplitN(envstring, "=", 2) os.Setenv(kv[0], kv[1]) } } func (s *EnvSuite) TearDownSuite(c *C) { } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/testing/envsuite/envsuite_test.go��������������������������0000644�0000153�0000161�00000002345�12321735747�026657� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package envsuite import ( . "launchpad.net/gocheck" "os" "testing" ) type EnvTestSuite struct { EnvSuite } func Test(t *testing.T) { TestingT(t) } var _ = Suite(&EnvTestSuite{}) func (s *EnvTestSuite) TestGrabsCurrentEnvironment(c *C) { envsuite := &EnvSuite{} // EnvTestSuite is an EnvSuite, so we should have already isolated // ourselves from the world. So we set a single env value, and we // assert that SetUpSuite is able to see that. os.Setenv("TEST_KEY", "test-value") envsuite.SetUpSuite(c) c.Assert(envsuite.environ, DeepEquals, []string{"TEST_KEY=test-value"}) } func (s *EnvTestSuite) TestClearsEnvironment(c *C) { envsuite := &EnvSuite{} os.Setenv("TEST_KEY", "test-value") envsuite.SetUpSuite(c) // SetUpTest should reset the current environment back to being // completely empty. envsuite.SetUpTest(c) c.Assert(os.Getenv("TEST_KEY"), Equals, "") c.Assert(os.Environ(), DeepEquals, []string{}) } func (s *EnvTestSuite) TestRestoresEnvironment(c *C) { envsuite := &EnvSuite{} os.Setenv("TEST_KEY", "test-value") envsuite.SetUpSuite(c) envsuite.SetUpTest(c) envsuite.TearDownTest(c) c.Assert(os.Getenv("TEST_KEY"), Equals, "test-value") c.Assert(os.Environ(), DeepEquals, []string{"TEST_KEY=test-value"}) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/nova/������������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�021047� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/nova/json.go�����������������������������������������������0000644�0000153�0000161�00000016234�12321735747�022355� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// JSON marshaling and unmarshaling support for Openstack compute data structures. // This package deals with the difference between the API and on-the-wire data types. // Differences include encoding entity IDs as string vs int, depending on the Openstack // variant used. // // The marshaling support is included primarily for use by the test doubles. It needs to be // included here since Go requires methods to implemented in the same package as their receiver. package nova import ( "encoding/json" "fmt" "strconv" ) const ( idTag = "id" instanceIdTag = "instance_id" groupIdTag = "group_id" parentGroupIdTag = "parent_group_id" ) var useNumericIds bool = false // convertId returns the id as either a string or an int depending on what // implementation of Openstack we are emulating. func convertId(id string) interface{} { if !useNumericIds { return id } result, err := strconv.Atoi(id) if err != nil { panic(err) } return result } // getIdAsString extracts the field with the specified tag from the json data // and returns it converted to a string. func getIdAsString(b []byte, tag string) (string, error) { var out map[string]interface{} if err := json.Unmarshal(b, &out); err != nil { return "", err } if val, ok := out[tag]; !ok { return "", nil } else { if floatVal, ok := val.(float64); ok { return fmt.Sprint(int(floatVal)), nil } else { return fmt.Sprint(val), nil } } panic("unreachable") } // appendJSON marshals the given attribute value and appends it as an encoded value to the given json data. // The newly encode (attr, value) is inserted just before the closing "}" in the json data. func appendJSON(data []byte, attr string, value interface{}) ([]byte, error) { newData, err := json.Marshal(&value) if err != nil { return nil, err } strData := string(data) result := fmt.Sprintf(`%s, "%s":%s}`, strData[:len(strData)-1], attr, string(newData)) return []byte(result), nil } type jsonEntity Entity func (entity *Entity) UnmarshalJSON(b []byte) error { var je jsonEntity = jsonEntity(*entity) var err error if err = json.Unmarshal(b, &je); err != nil { return err } if je.Id, err = getIdAsString(b, idTag); err != nil { return err } *entity = Entity(je) return nil } func (entity Entity) MarshalJSON() ([]byte, error) { var je jsonEntity = jsonEntity(entity) data, err := json.Marshal(&je) if err != nil { return nil, err } id := convertId(entity.Id) return appendJSON(data, idTag, id) } type jsonFlavorDetail FlavorDetail func (flavorDetail *FlavorDetail) UnmarshalJSON(b []byte) error { var jfd jsonFlavorDetail = jsonFlavorDetail(*flavorDetail) var err error if err = json.Unmarshal(b, &jfd); err != nil { return err } if jfd.Id, err = getIdAsString(b, idTag); err != nil { return err } *flavorDetail = FlavorDetail(jfd) return nil } func (flavorDetail FlavorDetail) MarshalJSON() ([]byte, error) { var jfd jsonFlavorDetail = jsonFlavorDetail(flavorDetail) data, err := json.Marshal(&jfd) if err != nil { return nil, err } id := convertId(flavorDetail.Id) return appendJSON(data, idTag, id) } type jsonServerDetail ServerDetail func (serverDetail *ServerDetail) UnmarshalJSON(b []byte) error { var jsd jsonServerDetail = jsonServerDetail(*serverDetail) var err error if err = json.Unmarshal(b, &jsd); err != nil { return err } if jsd.Id, err = getIdAsString(b, idTag); err != nil { return err } *serverDetail = ServerDetail(jsd) return nil } func (serverDetail ServerDetail) MarshalJSON() ([]byte, error) { var jsd jsonServerDetail = jsonServerDetail(serverDetail) data, err := json.Marshal(&jsd) if err != nil { return nil, err } id := convertId(serverDetail.Id) return appendJSON(data, idTag, id) } type jsonFloatingIP FloatingIP func (floatingIP *FloatingIP) UnmarshalJSON(b []byte) error { var jfip jsonFloatingIP = jsonFloatingIP(*floatingIP) var err error if err = json.Unmarshal(b, &jfip); err != nil { return err } if instIdStr, err := getIdAsString(b, instanceIdTag); err != nil { return err } else if instIdStr != "" { strId := instIdStr jfip.InstanceId = &strId } if jfip.Id, err = getIdAsString(b, idTag); err != nil { return err } *floatingIP = FloatingIP(jfip) return nil } func (floatingIP FloatingIP) MarshalJSON() ([]byte, error) { var jfip jsonFloatingIP = jsonFloatingIP(floatingIP) data, err := json.Marshal(&jfip) if err != nil { return nil, err } id := convertId(floatingIP.Id) data, err = appendJSON(data, idTag, id) if err != nil { return nil, err } if floatingIP.InstanceId == nil { return data, nil } instId := convertId(*floatingIP.InstanceId) return appendJSON(data, instanceIdTag, instId) } type jsonSecurityGroup SecurityGroup func (securityGroup *SecurityGroup) UnmarshalJSON(b []byte) error { var jsg jsonSecurityGroup = jsonSecurityGroup(*securityGroup) var err error if err = json.Unmarshal(b, &jsg); err != nil { return err } if jsg.Id, err = getIdAsString(b, idTag); err != nil { return err } *securityGroup = SecurityGroup(jsg) return nil } func (securityGroup SecurityGroup) MarshalJSON() ([]byte, error) { var jsg jsonSecurityGroup = jsonSecurityGroup(securityGroup) data, err := json.Marshal(&jsg) if err != nil { return nil, err } id := convertId(securityGroup.Id) return appendJSON(data, idTag, id) } type jsonSecurityGroupRule SecurityGroupRule func (securityGroupRule *SecurityGroupRule) UnmarshalJSON(b []byte) error { var jsgr jsonSecurityGroupRule = jsonSecurityGroupRule(*securityGroupRule) var err error if err = json.Unmarshal(b, &jsgr); err != nil { return err } if jsgr.Id, err = getIdAsString(b, idTag); err != nil { return err } if jsgr.ParentGroupId, err = getIdAsString(b, parentGroupIdTag); err != nil { return err } *securityGroupRule = SecurityGroupRule(jsgr) return nil } func (securityGroupRule SecurityGroupRule) MarshalJSON() ([]byte, error) { var jsgr jsonSecurityGroupRule = jsonSecurityGroupRule(securityGroupRule) data, err := json.Marshal(&jsgr) if err != nil { return nil, err } id := convertId(securityGroupRule.Id) data, err = appendJSON(data, idTag, id) if err != nil { return nil, err } if securityGroupRule.ParentGroupId == "" { return data, nil } id = convertId(securityGroupRule.ParentGroupId) return appendJSON(data, parentGroupIdTag, id) } type jsonRuleInfo RuleInfo func (ruleInfo *RuleInfo) UnmarshalJSON(b []byte) error { var jri jsonRuleInfo = jsonRuleInfo(*ruleInfo) var err error if err = json.Unmarshal(b, &jri); err != nil { return err } if jri.ParentGroupId, err = getIdAsString(b, parentGroupIdTag); err != nil { return err } if groupId, err := getIdAsString(b, groupIdTag); err != nil { return err } else if groupId != "" { strId := groupId jri.GroupId = &strId } *ruleInfo = RuleInfo(jri) return nil } func (ruleInfo RuleInfo) MarshalJSON() ([]byte, error) { var jri jsonRuleInfo = jsonRuleInfo(ruleInfo) data, err := json.Marshal(&jri) if err != nil { return nil, err } id := convertId(ruleInfo.ParentGroupId) data, err = appendJSON(data, parentGroupIdTag, id) if err != nil { return nil, err } if ruleInfo.GroupId == nil { return data, nil } id = convertId(*ruleInfo.GroupId) return appendJSON(data, groupIdTag, id) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/nova/export_test.go����������������������������������������0000644�0000153�0000161�00000000104�12321735747�023751� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nova func UseNumericIds(val bool) { useNumericIds = val } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/nova/networks.go�������������������������������������������0000644�0000153�0000161�00000002557�12321735747�023263� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. // Nova api calls for managing networks, which may use either the old // nova-network code or delegate through to the neutron api. // See documentation at: // <http://docs.openstack.org/api/openstack-compute/2/content/ext-os-networks.html> package nova import ( "launchpad.net/goose/client" "launchpad.net/goose/errors" goosehttp "launchpad.net/goose/http" ) const ( apiNetworks = "os-networks" // The os-tenant-networks extension is a newer addition aimed at exposing // management of networks to unprivileged accounts. Not used at present. apiTenantNetworks = "os-tenant-networks" ) // Network contains details about a labeled network type Network struct { Id string `json:"id"` // UUID of the resource Label string `json:"label"` // User-provided name for the network range Cidr string `json:"cidr"` // IP range covered by the network } // ListNetworks gives details on available networks func (c *Client) ListNetworks() ([]Network, error) { var resp struct { Networks []Network `json:"networks"` } requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.GET, "compute", apiNetworks, &requestData) if err != nil { return nil, errors.Newf(err, "failed to get list of networks") } return resp.Networks, nil } �������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/nova/nova_test.go������������������������������������������0000644�0000153�0000161�00000003570�12321735747�023405� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nova_test import ( "flag" . "launchpad.net/gocheck" "launchpad.net/goose/identity" "reflect" "testing" ) // imageDetails specify parameters used to start a test machine for the live tests. type imageDetails struct { flavor string imageId string vendor string } // Out-of-the-box, we support live testing using Canonistack or HP Cloud. var testConstraints = map[string]imageDetails{ "canonistack": imageDetails{ flavor: "m1.tiny", imageId: "f2ca48ce-30d5-4f1f-9075-12e64510368d"}, "hpcloud": imageDetails{ flavor: "standard.xsmall", imageId: "81078"}, } var live = flag.Bool("live", false, "Include live OpenStack tests") var vendor = flag.String("vendor", "", "The Openstack vendor to test against") var imageId = flag.String("image", "", "The image id for which a test service is to be started") var flavor = flag.String("flavor", "", "The flavor of the test service") func Test(t *testing.T) { if *live { // We can either specify a vendor, or imageId and flavor separately. var testImageDetails imageDetails if *vendor != "" { var ok bool if testImageDetails, ok = testConstraints[*vendor]; !ok { keys := reflect.ValueOf(testConstraints).MapKeys() t.Fatalf("Unknown vendor %s. Must be one of %s", *vendor, keys) } testImageDetails.vendor = *vendor } else { if *imageId == "" { t.Fatalf("Must specify image id to use for test instance, "+ "eg %s for Canonistack", "-image c876e5fe-abb0-41f0-8f29-f0b47481f523") } if *flavor == "" { t.Fatalf("Must specify flavor to use for test instance, "+ "eg %s for Canonistack", "-flavor m1.tiny") } testImageDetails = imageDetails{*flavor, *imageId, ""} } cred, err := identity.CompleteCredentialsFromEnv() if err != nil { t.Fatalf("Error setting up test suite: %s", err.Error()) } registerOpenStackTests(cred, testImageDetails) } registerLocalTests() TestingT(t) } ����������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/nova/local_test.go�����������������������������������������0000644�0000153�0000161�00000016525�12321735747�023540� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nova_test import ( "bytes" . "launchpad.net/gocheck" "launchpad.net/goose/client" "launchpad.net/goose/errors" goosehttp "launchpad.net/goose/http" "launchpad.net/goose/identity" "launchpad.net/goose/nova" "launchpad.net/goose/testservices" "launchpad.net/goose/testservices/hook" "launchpad.net/goose/testservices/identityservice" "launchpad.net/goose/testservices/openstackservice" "log" "net/http" "net/http/httptest" "strings" ) func registerLocalTests() { // Test using numeric ids. Suite(&localLiveSuite{ useNumericIds: true, }) // Test using string ids. Suite(&localLiveSuite{ useNumericIds: false, }) } // localLiveSuite runs tests from LiveTests using a fake // nova server that runs within the test process itself. type localLiveSuite struct { LiveTests useNumericIds bool // The following attributes are for using testing doubles. Server *httptest.Server Mux *http.ServeMux oldHandler http.Handler openstack *openstackservice.Openstack retryErrorCount int // The current retry error count. retryErrorCountToSend int // The number of retry errors to send. noMoreIPs bool // If true, addFloatingIP will return ErrNoMoreFloatingIPs ipLimitExceeded bool // If true, addFloatingIP will return ErrIPLimitExceeded badTokens int // If > 0, authHook will set an invalid token in the AccessResponse data. } func (s *localLiveSuite) SetUpSuite(c *C) { var idInfo string if s.useNumericIds { idInfo = "with numeric ids" } else { idInfo = "with string ids" } c.Logf("Using identity and nova service test doubles %s", idInfo) nova.UseNumericIds(s.useNumericIds) // Set up the HTTP server. s.Server = httptest.NewServer(nil) s.oldHandler = s.Server.Config.Handler s.Mux = http.NewServeMux() s.Server.Config.Handler = s.Mux // Set up an Openstack service. s.cred = &identity.Credentials{ URL: s.Server.URL, User: "fred", Secrets: "secret", Region: "some region", TenantName: "tenant", } s.openstack = openstackservice.New(s.cred, identity.AuthUserPass) s.openstack.SetupHTTP(s.Mux) s.testFlavor = "m1.small" s.testImageId = "1" s.LiveTests.SetUpSuite(c) } func (s *localLiveSuite) TearDownSuite(c *C) { s.LiveTests.TearDownSuite(c) s.Mux = nil s.Server.Config.Handler = s.oldHandler s.Server.Close() } func (s *localLiveSuite) SetUpTest(c *C) { s.retryErrorCount = 0 s.LiveTests.SetUpTest(c) } func (s *localLiveSuite) TearDownTest(c *C) { s.LiveTests.TearDownTest(c) } // Additional tests to be run against the service double only go here. func (s *localLiveSuite) retryLimitHook(sc hook.ServiceControl) hook.ControlProcessor { return func(sc hook.ServiceControl, args ...interface{}) error { sendError := s.retryErrorCount < s.retryErrorCountToSend if sendError { s.retryErrorCount++ return testservices.RateLimitExceededError } return nil } } func (s *localLiveSuite) setupClient(c *C, logger *log.Logger) *nova.Client { client := client.NewClient(s.cred, identity.AuthUserPass, logger) return nova.New(client) } func (s *localLiveSuite) setupRetryErrorTest(c *C, logger *log.Logger) (*nova.Client, *nova.SecurityGroup) { novaClient := s.setupClient(c, logger) // Delete the artifact if it already exists. testGroup, err := novaClient.SecurityGroupByName("test_group") if err != nil { c.Assert(errors.IsNotFound(err), Equals, true) } else { novaClient.DeleteSecurityGroup(testGroup.Id) c.Assert(err, IsNil) } testGroup, err = novaClient.CreateSecurityGroup("test_group", "test") c.Assert(err, IsNil) return novaClient, testGroup } // TestRateLimitRetry checks that when we make too many requests and receive a Retry-After response, the retry // occurs and the request ultimately succeeds. func (s *localLiveSuite) TestRateLimitRetry(c *C) { // Capture the logged output so we can check for retry messages. var logout bytes.Buffer logger := log.New(&logout, "", log.LstdFlags) novaClient, testGroup := s.setupRetryErrorTest(c, logger) s.retryErrorCountToSend = goosehttp.MaxSendAttempts - 1 s.openstack.Nova.RegisterControlPoint("removeSecurityGroup", s.retryLimitHook(s.openstack.Nova)) defer s.openstack.Nova.RegisterControlPoint("removeSecurityGroup", nil) err := novaClient.DeleteSecurityGroup(testGroup.Id) c.Assert(err, IsNil) // Ensure we got at least one retry message. output := logout.String() c.Assert(strings.Contains(output, "Too many requests, retrying in"), Equals, true) } // TestRateLimitRetryExceeded checks that an error is raised if too many retry responses are received from the server. func (s *localLiveSuite) TestRateLimitRetryExceeded(c *C) { novaClient, testGroup := s.setupRetryErrorTest(c, nil) s.retryErrorCountToSend = goosehttp.MaxSendAttempts s.openstack.Nova.RegisterControlPoint("removeSecurityGroup", s.retryLimitHook(s.openstack.Nova)) defer s.openstack.Nova.RegisterControlPoint("removeSecurityGroup", nil) err := novaClient.DeleteSecurityGroup(testGroup.Id) c.Assert(err, Not(IsNil)) c.Assert(err.Error(), Matches, "(.|\n)*Maximum number of attempts.*") } func (s *localLiveSuite) addFloatingIPHook(sc hook.ServiceControl) hook.ControlProcessor { return func(sc hook.ServiceControl, args ...interface{}) error { if s.noMoreIPs { return testservices.NoMoreFloatingIPs } else if s.ipLimitExceeded { return testservices.IPLimitExceeded } return nil } } func (s *localLiveSuite) TestAddFloatingIPErrors(c *C) { novaClient := s.setupClient(c, nil) fips, err := novaClient.ListFloatingIPs() c.Assert(err, IsNil) c.Assert(fips, HasLen, 0) cleanup := s.openstack.Nova.RegisterControlPoint("addFloatingIP", s.addFloatingIPHook(s.openstack.Nova)) defer cleanup() s.noMoreIPs = true fip, err := novaClient.AllocateFloatingIP() c.Assert(err, ErrorMatches, "(.|\n)*Zero floating ips available.*") c.Assert(fip, IsNil) s.noMoreIPs = false s.ipLimitExceeded = true fip, err = novaClient.AllocateFloatingIP() c.Assert(err, ErrorMatches, "(.|\n)*Maximum number of floating ips exceeded.*") c.Assert(fip, IsNil) s.ipLimitExceeded = false fip, err = novaClient.AllocateFloatingIP() c.Assert(err, IsNil) c.Assert(fip.IP, Not(Equals), "") } func (s *localLiveSuite) authHook(sc hook.ServiceControl) hook.ControlProcessor { return func(sc hook.ServiceControl, args ...interface{}) error { res := args[0].(*identityservice.AccessResponse) if s.badTokens > 0 { res.Access.Token.Id = "xxx" s.badTokens-- } return nil } } func (s *localLiveSuite) TestReauthenticate(c *C) { novaClient := s.setupClient(c, nil) up := s.openstack.Identity.(*identityservice.UserPass) cleanup := up.RegisterControlPoint("authorisation", s.authHook(up)) defer cleanup() // An invalid token is returned after the first authentication step, resulting in the ListServers call // returning a 401. Subsequent authentication calls return the correct token so the auth retry does it's job. s.badTokens = 1 _, err := novaClient.ListServers(nil) c.Assert(err, IsNil) } func (s *localLiveSuite) TestReauthenticateFailure(c *C) { novaClient := s.setupClient(c, nil) up := s.openstack.Identity.(*identityservice.UserPass) cleanup := up.RegisterControlPoint("authorisation", s.authHook(up)) defer cleanup() // If the re-authentication fails, ensure an Unauthorised error is returned. s.badTokens = 2 _, err := novaClient.ListServers(nil) c.Assert(errors.IsUnauthorised(err), Equals, true) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/nova/live_test.go������������������������������������������0000644�0000153�0000161�00000035160�12321735747�023401� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nova_test import ( "bytes" "fmt" . "launchpad.net/gocheck" "launchpad.net/goose/client" "launchpad.net/goose/errors" "launchpad.net/goose/identity" "launchpad.net/goose/nova" "log" "strings" "time" ) const ( // A made up name we use for the test server instance. testImageName = "nova_test_server" ) func registerOpenStackTests(cred *identity.Credentials, testImageDetails imageDetails) { Suite(&LiveTests{ cred: cred, testImageId: testImageDetails.imageId, testFlavor: testImageDetails.flavor, vendor: testImageDetails.vendor, }) } type LiveTests struct { cred *identity.Credentials client client.AuthenticatingClient nova *nova.Client testServer *nova.Entity userId string tenantId string testImageId string testFlavor string testFlavorId string vendor string useNumericIds bool } func (s *LiveTests) SetUpSuite(c *C) { s.client = client.NewClient(s.cred, identity.AuthUserPass, nil) s.nova = nova.New(s.client) var err error s.testFlavorId, err = s.findFlavorId(s.testFlavor) c.Assert(err, IsNil) s.testServer, err = s.createInstance(c, testImageName) c.Assert(err, IsNil) s.waitTestServerToStart(c) // These will not be filled in until a client has authorised which will happen creating the instance above. s.userId = s.client.UserId() s.tenantId = s.client.TenantId() } func (s *LiveTests) findFlavorId(flavorName string) (string, error) { flavors, err := s.nova.ListFlavors() if err != nil { return "", err } var flavorId string for _, flavor := range flavors { if flavor.Name == flavorName { flavorId = flavor.Id break } } if flavorId == "" { return "", fmt.Errorf("No such flavor %s", flavorName) } return flavorId, nil } func (s *LiveTests) TearDownSuite(c *C) { if s.testServer != nil { err := s.nova.DeleteServer(s.testServer.Id) c.Check(err, IsNil) } } func (s *LiveTests) SetUpTest(c *C) { // noop, called by local test suite. } func (s *LiveTests) TearDownTest(c *C) { // noop, called by local test suite. } func (s *LiveTests) createInstance(c *C, name string) (instance *nova.Entity, err error) { opts := nova.RunServerOpts{ Name: name, FlavorId: s.testFlavorId, ImageId: s.testImageId, UserData: nil, } instance, err = s.nova.RunServer(opts) if err != nil { return nil, err } return instance, nil } // Assert that the server record matches the details of the test server image. func (s *LiveTests) assertServerDetails(c *C, sr *nova.ServerDetail) { c.Check(sr.Id, Equals, s.testServer.Id) c.Check(sr.Name, Equals, testImageName) c.Check(sr.Flavor.Id, Equals, s.testFlavorId) c.Check(sr.Image.Id, Equals, s.testImageId) } func (s *LiveTests) TestListFlavors(c *C) { flavors, err := s.nova.ListFlavors() c.Assert(err, IsNil) if len(flavors) < 1 { c.Fatalf("no flavors to list") } for _, f := range flavors { c.Check(f.Id, Not(Equals), "") c.Check(f.Name, Not(Equals), "") for _, l := range f.Links { c.Check(l.Href, Matches, "https?://.*") c.Check(l.Rel, Matches, "self|bookmark") } } } func (s *LiveTests) TestListFlavorsDetail(c *C) { flavors, err := s.nova.ListFlavorsDetail() c.Assert(err, IsNil) if len(flavors) < 1 { c.Fatalf("no flavors (details) to list") } for _, f := range flavors { c.Check(f.Name, Not(Equals), "") c.Check(f.Id, Not(Equals), "") if f.RAM < 0 || f.VCPUs < 0 || f.Disk < 0 { c.Fatalf("invalid flavor found: %#v", f) } } } func (s *LiveTests) TestListServers(c *C) { servers, err := s.nova.ListServers(nil) c.Assert(err, IsNil) foundTest := false for _, sr := range servers { c.Check(sr.Id, Not(Equals), "") c.Check(sr.Name, Not(Equals), "") if sr.Id == s.testServer.Id { c.Check(sr.Name, Equals, testImageName) foundTest = true } for _, l := range sr.Links { c.Check(l.Href, Matches, "https?://.*") c.Check(l.Rel, Matches, "self|bookmark") } } if !foundTest { c.Fatalf("test server (%s) not found in server list", s.testServer.Id) } } func (s *LiveTests) TestListServersWithFilter(c *C) { inst, err := s.createInstance(c, "filtered_server") c.Assert(err, IsNil) defer s.nova.DeleteServer(inst.Id) filter := nova.NewFilter() filter.Set(nova.FilterServer, "filtered_server") servers, err := s.nova.ListServers(filter) c.Assert(err, IsNil) found := false for _, sr := range servers { if sr.Id == inst.Id { c.Assert(sr.Name, Equals, "filtered_server") found = true } } if !found { c.Fatalf("server (%s) not found in filtered server list %v", inst.Id, servers) } } func (s *LiveTests) TestListServersDetail(c *C) { servers, err := s.nova.ListServersDetail(nil) c.Assert(err, IsNil) if len(servers) < 1 { c.Fatalf("no servers to list (expected at least 1)") } foundTest := false for _, sr := range servers { // not checking for Addresses, because it could be missing c.Check(sr.Created, Matches, `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*`) c.Check(sr.Updated, Matches, `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*`) c.Check(sr.Id, Not(Equals), "") c.Check(sr.HostId, Not(Equals), "") c.Check(sr.TenantId, Equals, s.tenantId) c.Check(sr.UserId, Equals, s.userId) c.Check(sr.Status, Not(Equals), "") c.Check(sr.Name, Not(Equals), "") if sr.Id == s.testServer.Id { s.assertServerDetails(c, &sr) foundTest = true } for _, l := range sr.Links { c.Check(l.Href, Matches, "https?://.*") c.Check(l.Rel, Matches, "self|bookmark") } c.Check(sr.Flavor.Id, Not(Equals), "") for _, f := range sr.Flavor.Links { c.Check(f.Href, Matches, "https?://.*") c.Check(f.Rel, Matches, "self|bookmark") } c.Check(sr.Image.Id, Not(Equals), "") for _, i := range sr.Image.Links { c.Check(i.Href, Matches, "https?://.*") c.Check(i.Rel, Matches, "self|bookmark") } } if !foundTest { c.Fatalf("test server (%s) not found in server list (details)", s.testServer.Id) } } func (s *LiveTests) TestListServersDetailWithFilter(c *C) { inst, err := s.createInstance(c, "filtered_server") c.Assert(err, IsNil) defer s.nova.DeleteServer(inst.Id) filter := nova.NewFilter() filter.Set(nova.FilterServer, "filtered_server") servers, err := s.nova.ListServersDetail(filter) c.Assert(err, IsNil) found := false for _, sr := range servers { if sr.Id == inst.Id { c.Assert(sr.Name, Equals, "filtered_server") found = true } } if !found { c.Fatalf("server (%s) not found in filtered server details %v", inst.Id, servers) } } func (s *LiveTests) TestListSecurityGroups(c *C) { groups, err := s.nova.ListSecurityGroups() c.Assert(err, IsNil) if len(groups) < 1 { c.Fatalf("no security groups found (expected at least 1)") } for _, g := range groups { c.Check(g.TenantId, Equals, s.tenantId) c.Check(g.Name, Not(Equals), "") c.Check(g.Description, Not(Equals), "") c.Check(g.Rules, NotNil) } } func (s *LiveTests) TestCreateAndDeleteSecurityGroup(c *C) { group, err := s.nova.CreateSecurityGroup("test_secgroup", "test_desc") c.Assert(err, IsNil) c.Check(group.Name, Equals, "test_secgroup") c.Check(group.Description, Equals, "test_desc") groups, err := s.nova.ListSecurityGroups() found := false for _, g := range groups { if g.Id == group.Id { found = true break } } if found { err = s.nova.DeleteSecurityGroup(group.Id) c.Check(err, IsNil) } else { c.Fatalf("test security group (%d) not found", group.Id) } } func (s *LiveTests) TestDuplicateSecurityGroupError(c *C) { group, err := s.nova.CreateSecurityGroup("test_dupgroup", "test_desc") c.Assert(err, IsNil) defer s.nova.DeleteSecurityGroup(group.Id) group, err = s.nova.CreateSecurityGroup("test_dupgroup", "test_desc") c.Assert(errors.IsDuplicateValue(err), Equals, true) } func (s *LiveTests) TestCreateAndDeleteSecurityGroupRules(c *C) { group1, err := s.nova.CreateSecurityGroup("test_secgroup1", "test_desc") c.Assert(err, IsNil) group2, err := s.nova.CreateSecurityGroup("test_secgroup2", "test_desc") c.Assert(err, IsNil) // First type of rule - port range + protocol ri := nova.RuleInfo{ IPProtocol: "tcp", FromPort: 1234, ToPort: 4321, Cidr: "10.0.0.0/8", ParentGroupId: group1.Id, } rule, err := s.nova.CreateSecurityGroupRule(ri) c.Assert(err, IsNil) c.Check(*rule.FromPort, Equals, 1234) c.Check(*rule.ToPort, Equals, 4321) c.Check(rule.ParentGroupId, Equals, group1.Id) c.Check(*rule.IPProtocol, Equals, "tcp") c.Check(rule.Group, Equals, nova.SecurityGroupRef{}) err = s.nova.DeleteSecurityGroupRule(rule.Id) c.Check(err, IsNil) // Second type of rule - inherited from another group ri = nova.RuleInfo{ GroupId: &group2.Id, ParentGroupId: group1.Id, } rule, err = s.nova.CreateSecurityGroupRule(ri) c.Assert(err, IsNil) c.Check(rule.ParentGroupId, Equals, group1.Id) c.Check(rule.Group, NotNil) c.Check(rule.Group.TenantId, Equals, s.tenantId) c.Check(rule.Group.Name, Equals, "test_secgroup2") err = s.nova.DeleteSecurityGroupRule(rule.Id) c.Check(err, IsNil) err = s.nova.DeleteSecurityGroup(group1.Id) c.Check(err, IsNil) err = s.nova.DeleteSecurityGroup(group2.Id) c.Check(err, IsNil) } func (s *LiveTests) TestGetServer(c *C) { server, err := s.nova.GetServer(s.testServer.Id) c.Assert(err, IsNil) s.assertServerDetails(c, server) } func (s *LiveTests) waitTestServerToStart(c *C) { // Wait until the test server is actually running c.Logf("waiting the test server %s to start...", s.testServer.Id) for { server, err := s.nova.GetServer(s.testServer.Id) c.Assert(err, IsNil) if server.Status == nova.StatusActive { break } // We dont' want to flood the connection while polling the server waiting for it to start. c.Logf("server has status %s, waiting 10 seconds before polling again...", server.Status) time.Sleep(10 * time.Second) } c.Logf("started") } func (s *LiveTests) TestServerAddGetRemoveSecurityGroup(c *C) { group, err := s.nova.CreateSecurityGroup("test_server_secgroup", "test desc") if err != nil { c.Assert(errors.IsDuplicateValue(err), Equals, true) group, err = s.nova.SecurityGroupByName("test_server_secgroup") c.Assert(err, IsNil) } s.waitTestServerToStart(c) err = s.nova.AddServerSecurityGroup(s.testServer.Id, group.Name) c.Assert(err, IsNil) groups, err := s.nova.GetServerSecurityGroups(s.testServer.Id) c.Assert(err, IsNil) found := false for _, g := range groups { if g.Id == group.Id || g.Name == group.Name { found = true break } } err = s.nova.RemoveServerSecurityGroup(s.testServer.Id, group.Name) c.Check(err, IsNil) err = s.nova.DeleteSecurityGroup(group.Id) c.Assert(err, IsNil) if !found { c.Fail() } } func (s *LiveTests) TestFloatingIPs(c *C) { ip, err := s.nova.AllocateFloatingIP() c.Assert(err, IsNil) defer s.nova.DeleteFloatingIP(ip.Id) c.Check(ip.IP, Not(Equals), "") c.Check(ip.FixedIP, IsNil) c.Check(ip.InstanceId, IsNil) ips, err := s.nova.ListFloatingIPs() c.Assert(err, IsNil) if len(ips) < 1 { c.Errorf("no floating IPs found (expected at least 1)") } else { found := false for _, i := range ips { c.Check(i.IP, Not(Equals), "") if i.Id == ip.Id { c.Check(i.IP, Equals, ip.IP) c.Check(i.Pool, Equals, ip.Pool) found = true } } if !found { c.Errorf("expected to find added floating IP: %#v", ip) } fip, err := s.nova.GetFloatingIP(ip.Id) c.Assert(err, IsNil) c.Check(fip.Id, Equals, ip.Id) c.Check(fip.IP, Equals, ip.IP) c.Check(fip.Pool, Equals, ip.Pool) } } func (s *LiveTests) TestServerFloatingIPs(c *C) { ip, err := s.nova.AllocateFloatingIP() c.Assert(err, IsNil) defer s.nova.DeleteFloatingIP(ip.Id) c.Check(ip.IP, Matches, `\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`) s.waitTestServerToStart(c) err = s.nova.AddServerFloatingIP(s.testServer.Id, ip.IP) c.Assert(err, IsNil) // TODO (wallyworld) - 2013-02-11 bug=1121666 // where we are creating a real server, test that the IP address created above // can be used to connect to the server defer s.nova.RemoveServerFloatingIP(s.testServer.Id, ip.IP) fip, err := s.nova.GetFloatingIP(ip.Id) c.Assert(err, IsNil) c.Check(*fip.FixedIP, Matches, `\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`) c.Check(*fip.InstanceId, Equals, s.testServer.Id) err = s.nova.RemoveServerFloatingIP(s.testServer.Id, ip.IP) c.Check(err, IsNil) fip, err = s.nova.GetFloatingIP(ip.Id) c.Assert(err, IsNil) c.Check(fip.FixedIP, IsNil) c.Check(fip.InstanceId, IsNil) } // TestRateLimitRetry checks that when we make too many requests and receive a Retry-After response, the retry // occurs and the request ultimately succeeds. func (s *LiveTests) TestRateLimitRetry(c *C) { if s.vendor != "canonistack" { c.Skip("TestRateLimitRetry is only run for Canonistack") } // Capture the logged output so we can check for retry messages. var logout bytes.Buffer logger := log.New(&logout, "", log.LstdFlags) client := client.NewClient(s.cred, identity.AuthUserPass, logger) novaClient := nova.New(client) // Delete the artifact if it already exists. testGroup, err := novaClient.SecurityGroupByName("test_group") if err != nil { c.Assert(errors.IsNotFound(err), Equals, true) } else { novaClient.DeleteSecurityGroup(testGroup.Id) c.Assert(err, IsNil) } // Create some artifacts a number of times in succession and ensure each time is successful, // even with retries being required. As soon as we see a retry message, the test has passed // and we exit. for i := 0; i < 50; i++ { testGroup, err = novaClient.CreateSecurityGroup("test_group", "test") c.Assert(err, IsNil) novaClient.DeleteSecurityGroup(testGroup.Id) c.Assert(err, IsNil) output := logout.String() if strings.Contains(output, "Too many requests, retrying in") == true { return } } // No retry message logged so test has failed. c.Fail() } func (s *LiveTests) TestRegexpInstanceFilters(c *C) { serverNames := []string{ "foobar123", "foo123baz", "123barbaz", } for _, name := range serverNames { inst, err := s.createInstance(c, name) c.Assert(err, IsNil) defer s.nova.DeleteServer(inst.Id) } filter := nova.NewFilter() filter.Set(nova.FilterServer, `foo.*baz`) servers, err := s.nova.ListServersDetail(filter) c.Assert(err, IsNil) c.Assert(servers, HasLen, 1) c.Assert(servers[0].Name, Equals, serverNames[1]) filter.Set(nova.FilterServer, `[0-9]+[a-z]+`) servers, err = s.nova.ListServersDetail(filter) c.Assert(err, IsNil) c.Assert(servers, HasLen, 2) if servers[0].Name != serverNames[1] { servers[0], servers[1] = servers[1], servers[0] } c.Assert(servers[0].Name, Equals, serverNames[1]) c.Assert(servers[1].Name, Equals, serverNames[2]) } func (s *LiveTests) TestListNetworks(c *C) { networks, err := s.nova.ListNetworks() c.Assert(err, IsNil) for _, network := range networks { c.Check(network.Id, Not(Equals), "") c.Check(network.Label, Not(Equals), "") c.Assert(network.Cidr, Matches, `\d{1,3}(\.+\d{1,3}){3}\/\d+`) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/nova/json_test.go������������������������������������������0000644�0000153�0000161�00000003026�12321735747�023407� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package nova_test import ( "encoding/json" . "launchpad.net/gocheck" "launchpad.net/goose/nova" ) type JsonSuite struct { } var _ = Suite(&JsonSuite{}) func (s *JsonSuite) SetUpSuite(c *C) { nova.UseNumericIds(true) } func (s *JsonSuite) assertMarshallRoundtrip(c *C, value interface{}, unmarshalled interface{}) { data, err := json.Marshal(value) if err != nil { panic(err) } err = json.Unmarshal(data, &unmarshalled) if err != nil { panic(err) } c.Assert(unmarshalled, DeepEquals, value) } // The following tests all check that unmarshalling of Ids with values > 1E6 // works properly. func (s *JsonSuite) TestMarshallEntityLargeIntId(c *C) { entity := nova.Entity{Id: "2000000", Name: "test"} var unmarshalled nova.Entity s.assertMarshallRoundtrip(c, &entity, &unmarshalled) } func (s *JsonSuite) TestMarshallFlavorDetailLargeIntId(c *C) { fd := nova.FlavorDetail{Id: "2000000", Name: "test"} var unmarshalled nova.FlavorDetail s.assertMarshallRoundtrip(c, &fd, &unmarshalled) } func (s *JsonSuite) TestMarshallServerDetailLargeIntId(c *C) { fd := nova.Entity{Id: "2000000", Name: "test"} im := nova.Entity{Id: "2000000", Name: "test"} sd := nova.ServerDetail{Id: "2000000", Name: "test", Flavor: fd, Image: im} var unmarshalled nova.ServerDetail s.assertMarshallRoundtrip(c, &sd, &unmarshalled) } func (s *JsonSuite) TestMarshallFloatingIPLargeIntId(c *C) { id := "3000000" fip := nova.FloatingIP{Id: "2000000", InstanceId: &id} var unmarshalled nova.FloatingIP s.assertMarshallRoundtrip(c, &fip, &unmarshalled) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/nova/nova.go�����������������������������������������������0000644�0000153�0000161�00000056471�12321735747�022356� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// goose/nova - Go package to interact with OpenStack Compute (Nova) API. // See http://docs.openstack.org/api/openstack-compute/2/content/. package nova import ( "fmt" "launchpad.net/goose/client" "launchpad.net/goose/errors" goosehttp "launchpad.net/goose/http" "net/http" "net/url" "reflect" ) // API URL parts. const ( apiFlavors = "flavors" apiFlavorsDetail = "flavors/detail" apiServers = "servers" apiServersDetail = "servers/detail" apiSecurityGroups = "os-security-groups" apiSecurityGroupRules = "os-security-group-rules" apiFloatingIPs = "os-floating-ips" ) // Server status values. const ( StatusActive = "ACTIVE" // The server is active. StatusBuild = "BUILD" // The server has not finished the original build process. StatusBuildSpawning = "BUILD(spawning)" // The server has not finished the original build process but networking works (HP Cloud specific) StatusDeleted = "DELETED" // The server is deleted. StatusError = "ERROR" // The server is in error. StatusHardReboot = "HARD_REBOOT" // The server is hard rebooting. StatusPassword = "PASSWORD" // The password is being reset on the server. StatusReboot = "REBOOT" // The server is in a soft reboot state. StatusRebuild = "REBUILD" // The server is currently being rebuilt from an image. StatusRescue = "RESCUE" // The server is in rescue mode. StatusResize = "RESIZE" // Server is performing the differential copy of data that changed during its initial copy. StatusShutoff = "SHUTOFF" // The virtual machine (VM) was powered down by the user, but not through the OpenStack Compute API. StatusSuspended = "SUSPENDED" // The server is suspended, either by request or necessity. StatusUnknown = "UNKNOWN" // The state of the server is unknown. Contact your cloud provider. StatusVerifyResize = "VERIFY_RESIZE" // System is awaiting confirmation that the server is operational after a move or resize. ) // Filter keys. const ( FilterStatus = "status" // The server status. See Server Status Values. FilterImage = "image" // The image reference specified as an ID or full URL. FilterFlavor = "flavor" // The flavor reference specified as an ID or full URL. FilterServer = "name" // The server name. FilterMarker = "marker" // The ID of the last item in the previous list. FilterLimit = "limit" // The page size. FilterChangesSince = "changes-since" // The changes-since time. The list contains servers that have been deleted since the changes-since time. ) // Client provides a means to access the OpenStack Compute Service. type Client struct { client client.Client } // New creates a new Client. func New(client client.Client) *Client { return &Client{client} } // ---------------------------------------------------------------------------- // Filtering helper. // // Filter builds filtering parameters to be used in an OpenStack query which supports // filtering. For example: // // filter := NewFilter() // filter.Set(nova.FilterServer, "server_name") // filter.Set(nova.FilterStatus, nova.StatusBuild) // resp, err := nova.ListServers(filter) // type Filter struct { v url.Values } // NewFilter creates a new Filter. func NewFilter() *Filter { return &Filter{make(url.Values)} } func (f *Filter) Set(filter, value string) { f.v.Set(filter, value) } // Link describes a link to a flavor or server. type Link struct { Href string Rel string Type string } // Entity describe a basic information about a flavor or server. type Entity struct { Id string `json:"-"` UUID string `json:"uuid"` Links []Link `json:"links"` Name string `json:"name"` } func stringValue(item interface{}, attr string) string { return reflect.ValueOf(item).FieldByName(attr).String() } // Allow Entity slices to be sorted by named attribute. type EntitySortBy struct { Attr string Entities []Entity } func (e EntitySortBy) Len() int { return len(e.Entities) } func (e EntitySortBy) Less(i, j int) bool { return stringValue(e.Entities[i], e.Attr) < stringValue(e.Entities[j], e.Attr) } func (e EntitySortBy) Swap(i, j int) { e.Entities[i], e.Entities[j] = e.Entities[j], e.Entities[i] } // ListFlavours lists IDs, names, and links for available flavors. func (c *Client) ListFlavors() ([]Entity, error) { var resp struct { Flavors []Entity } requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.GET, "compute", apiFlavors, &requestData) if err != nil { return nil, errors.Newf(err, "failed to get list of flavours") } return resp.Flavors, nil } // FlavorDetail describes detailed information about a flavor. type FlavorDetail struct { Name string RAM int // Available RAM, in MB VCPUs int // Number of virtual CPU (cores) Disk int // Available root partition space, in GB Id string `json:"-"` Links []Link } // Allow FlavorDetail slices to be sorted by named attribute. type FlavorDetailSortBy struct { Attr string FlavorDetails []FlavorDetail } func (e FlavorDetailSortBy) Len() int { return len(e.FlavorDetails) } func (e FlavorDetailSortBy) Less(i, j int) bool { return stringValue(e.FlavorDetails[i], e.Attr) < stringValue(e.FlavorDetails[j], e.Attr) } func (e FlavorDetailSortBy) Swap(i, j int) { e.FlavorDetails[i], e.FlavorDetails[j] = e.FlavorDetails[j], e.FlavorDetails[i] } // ListFlavorsDetail lists all details for available flavors. func (c *Client) ListFlavorsDetail() ([]FlavorDetail, error) { var resp struct { Flavors []FlavorDetail } requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.GET, "compute", apiFlavorsDetail, &requestData) if err != nil { return nil, errors.Newf(err, "failed to get list of flavour details") } return resp.Flavors, nil } // ListServers lists IDs, names, and links for all servers. func (c *Client) ListServers(filter *Filter) ([]Entity, error) { var resp struct { Servers []Entity } var params *url.Values if filter != nil { params = &filter.v } requestData := goosehttp.RequestData{RespValue: &resp, Params: params, ExpectedStatus: []int{http.StatusOK}} err := c.client.SendRequest(client.GET, "compute", apiServers, &requestData) if err != nil { return nil, errors.Newf(err, "failed to get list of servers") } return resp.Servers, nil } // IPAddress describes a single IPv4/6 address of a server. type IPAddress struct { Version int `json:"version"` Address string `json:"addr"` } // ServerDetail describes a server in more detail. // See: http://docs.openstack.org/api/openstack-compute/2/content/Extensions-d1e1444.html#ServersCBSJ type ServerDetail struct { // AddressIPv4 and AddressIPv6 hold the first public IPv4 or IPv6 // address of the server, or "" if no floating IP is assigned. AddressIPv4 string AddressIPv6 string // Addresses holds the list of all IP addresses assigned to this // server, grouped by "network" name ("public", "private" or a // custom name). Addresses map[string][]IPAddress // Created holds the creation timestamp of the server // in RFC3339 format. Created string Flavor Entity HostId string Id string `json:"-"` UUID string Image Entity Links []Link Name string // HP Cloud returns security groups in server details. Groups []Entity `json:"security_groups"` // Progress holds the completion percentage of // the current operation Progress int // Status holds the current status of the server, // one of the Status* constants. Status string TenantId string `json:"tenant_id"` // Updated holds the timestamp of the last update // to the server in RFC3339 format. Updated string UserId string `json:"user_id"` } // ListServersDetail lists all details for available servers. func (c *Client) ListServersDetail(filter *Filter) ([]ServerDetail, error) { var resp struct { Servers []ServerDetail } var params *url.Values if filter != nil { params = &filter.v } requestData := goosehttp.RequestData{RespValue: &resp, Params: params} err := c.client.SendRequest(client.GET, "compute", apiServersDetail, &requestData) if err != nil { return nil, errors.Newf(err, "failed to get list of server details") } return resp.Servers, nil } // GetServer lists details for the specified server. func (c *Client) GetServer(serverId string) (*ServerDetail, error) { var resp struct { Server ServerDetail } url := fmt.Sprintf("%s/%s", apiServers, serverId) requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.GET, "compute", url, &requestData) if err != nil { return nil, errors.Newf(err, "failed to get details for serverId: %s", serverId) } return &resp.Server, nil } // DeleteServer terminates the specified server. func (c *Client) DeleteServer(serverId string) error { var resp struct { Server ServerDetail } url := fmt.Sprintf("%s/%s", apiServers, serverId) requestData := goosehttp.RequestData{RespValue: &resp, ExpectedStatus: []int{http.StatusNoContent}} err := c.client.SendRequest(client.DELETE, "compute", url, &requestData) if err != nil { err = errors.Newf(err, "failed to delete server with serverId: %s", serverId) } return err } type SecurityGroupName struct { Name string `json:"name"` } // ServerNetworks sets what networks a server should be connected to on boot. // - FixedIp may be supplied only when NetworkId is also given. // - PortId may be supplied only if neither NetworkId or FixedIp is set. type ServerNetworks struct { NetworkId string `json:"uuid,omitempty"` FixedIp string `json:"fixed_ip,omitempty"` PortId string `json:"port,omitempty"` } // RunServerOpts defines required and optional arguments for RunServer(). type RunServerOpts struct { Name string `json:"name"` // Required FlavorId string `json:"flavorRef"` // Required ImageId string `json:"imageRef"` // Required UserData []byte `json:"user_data"` // Optional SecurityGroupNames []SecurityGroupName `json:"security_groups"` // Optional Networks []ServerNetworks `json:"networks"` // Optional } // RunServer creates a new server, based on the given RunServerOpts. func (c *Client) RunServer(opts RunServerOpts) (*Entity, error) { var req struct { Server RunServerOpts `json:"server"` } req.Server = opts // opts.UserData gets serialized to base64-encoded string automatically var resp struct { Server Entity `json:"server"` } requestData := goosehttp.RequestData{ReqValue: req, RespValue: &resp, ExpectedStatus: []int{http.StatusAccepted}} err := c.client.SendRequest(client.POST, "compute", apiServers, &requestData) if err != nil { return nil, errors.Newf(err, "failed to run a server with %#v", opts) } return &resp.Server, nil } // SecurityGroupRef refers to an existing named security group type SecurityGroupRef struct { TenantId string `json:"tenant_id"` Name string `json:"name"` } // SecurityGroupRule describes a rule of a security group. There are 2 // basic rule types: ingress and group rules (see RuleInfo struct). type SecurityGroupRule struct { FromPort *int `json:"from_port"` // Can be nil IPProtocol *string `json:"ip_protocol"` // Can be nil ToPort *int `json:"to_port"` // Can be nil ParentGroupId string `json:"-"` IPRange map[string]string `json:"ip_range"` // Can be empty Id string `json:"-"` Group SecurityGroupRef } // SecurityGroup describes a single security group in OpenStack. type SecurityGroup struct { Rules []SecurityGroupRule TenantId string `json:"tenant_id"` Id string `json:"-"` Name string Description string } // ListSecurityGroups lists IDs, names, and other details for all security groups. func (c *Client) ListSecurityGroups() ([]SecurityGroup, error) { var resp struct { Groups []SecurityGroup `json:"security_groups"` } requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.GET, "compute", apiSecurityGroups, &requestData) if err != nil { return nil, errors.Newf(err, "failed to list security groups") } return resp.Groups, nil } // GetSecurityGroupByName returns the named security group. // Note: due to lack of filtering support when querying security groups, this is not an efficient implementation // but it's all we can do for now. func (c *Client) SecurityGroupByName(name string) (*SecurityGroup, error) { // OpenStack does not support group filtering, so we need to load them all and manually search by name. groups, err := c.ListSecurityGroups() if err != nil { return nil, err } for _, group := range groups { if group.Name == name { return &group, nil } } return nil, errors.NewNotFoundf(nil, "", "Security group %s not found.", name) } // GetServerSecurityGroups list security groups for a specific server. func (c *Client) GetServerSecurityGroups(serverId string) ([]SecurityGroup, error) { var resp struct { Groups []SecurityGroup `json:"security_groups"` } url := fmt.Sprintf("%s/%s/%s", apiServers, serverId, apiSecurityGroups) requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.GET, "compute", url, &requestData) if err != nil { // Sadly HP Cloud lacks the necessary API and also doesn't provide full SecurityGroup lookup. // The best we can do for now is to use just the Id and Name from the group entities. if errors.IsNotFound(err) { serverDetails, err := c.GetServer(serverId) if err == nil { result := make([]SecurityGroup, len(serverDetails.Groups)) for i, e := range serverDetails.Groups { result[i] = SecurityGroup{ Id: e.Id, Name: e.Name, } } return result, nil } } return nil, errors.Newf(err, "failed to list server (%s) security groups", serverId) } return resp.Groups, nil } // CreateSecurityGroup creates a new security group. func (c *Client) CreateSecurityGroup(name, description string) (*SecurityGroup, error) { var req struct { SecurityGroup struct { Name string `json:"name"` Description string `json:"description"` } `json:"security_group"` } req.SecurityGroup.Name = name req.SecurityGroup.Description = description var resp struct { SecurityGroup SecurityGroup `json:"security_group"` } requestData := goosehttp.RequestData{ReqValue: req, RespValue: &resp, ExpectedStatus: []int{http.StatusOK}} err := c.client.SendRequest(client.POST, "compute", apiSecurityGroups, &requestData) if err != nil { return nil, errors.Newf(err, "failed to create a security group with name: %s", name) } return &resp.SecurityGroup, nil } // DeleteSecurityGroup deletes the specified security group. func (c *Client) DeleteSecurityGroup(groupId string) error { url := fmt.Sprintf("%s/%s", apiSecurityGroups, groupId) requestData := goosehttp.RequestData{ExpectedStatus: []int{http.StatusAccepted}} err := c.client.SendRequest(client.DELETE, "compute", url, &requestData) if err != nil { err = errors.Newf(err, "failed to delete security group with id: %s", groupId) } return err } // RuleInfo allows the callers of CreateSecurityGroupRule() to // create 2 types of security group rules: ingress rules and group // rules. The difference stems from how the "source" is defined. // It can be either: // 1. Ingress rules - specified directly with any valid subnet mask // in CIDR format (e.g. "192.168.0.0/16"); // 2. Group rules - specified indirectly by giving a source group, // which can be any user's group (different tenant ID). // // Every rule works as an iptables ACCEPT rule, thus a group/ with no // rules does not allow ingress at all. Rules can be added and removed // while the server(s) are running. The set of security groups that // apply to a server is changed only when the server is // started. Adding or removing a security group on a running server // will not take effect until that server is restarted. However, // changing rules of existing groups will take effect immediately. // // For more information: // http://docs.openstack.org/developer/nova/nova.concepts.html#concept-security-groups // Nova source: https://github.com/openstack/nova.git type RuleInfo struct { /// IPProtocol is optional, and if specified must be "tcp", "udp" or // "icmp" (in this case, both FromPort and ToPort can be -1). IPProtocol string `json:"ip_protocol"` // FromPort and ToPort are both optional, and if specifed must be // integers between 1 and 65535 (valid TCP port numbers). -1 is a // special value, meaning "use default" (e.g. for ICMP). FromPort int `json:"from_port"` ToPort int `json:"to_port"` // Cidr cannot be specified with GroupId. Ingress rules need a valid // subnet mast in CIDR format here, while if GroupID is specifed, it // means you're adding a group rule, specifying source group ID, which // must exist already and can be equal to ParentGroupId). // need Cidr, while Cidr string `json:"cidr"` GroupId *string `json:"-"` // ParentGroupId is always required and specifies the group to which // the rule is added. ParentGroupId string `json:"-"` } // CreateSecurityGroupRule creates a security group rule. // It can either be an ingress rule or group rule (see the // description of RuleInfo). func (c *Client) CreateSecurityGroupRule(ruleInfo RuleInfo) (*SecurityGroupRule, error) { var req struct { SecurityGroupRule RuleInfo `json:"security_group_rule"` } req.SecurityGroupRule = ruleInfo var resp struct { SecurityGroupRule SecurityGroupRule `json:"security_group_rule"` } requestData := goosehttp.RequestData{ReqValue: req, RespValue: &resp} err := c.client.SendRequest(client.POST, "compute", apiSecurityGroupRules, &requestData) if err != nil { return nil, errors.Newf(err, "failed to create a rule for the security group with id: %v", ruleInfo.GroupId) } return &resp.SecurityGroupRule, nil } // DeleteSecurityGroupRule deletes the specified security group rule. func (c *Client) DeleteSecurityGroupRule(ruleId string) error { url := fmt.Sprintf("%s/%s", apiSecurityGroupRules, ruleId) requestData := goosehttp.RequestData{ExpectedStatus: []int{http.StatusAccepted}} err := c.client.SendRequest(client.DELETE, "compute", url, &requestData) if err != nil { err = errors.Newf(err, "failed to delete security group rule with id: %s", ruleId) } return err } // AddServerSecurityGroup adds a security group to the specified server. func (c *Client) AddServerSecurityGroup(serverId, groupName string) error { var req struct { AddSecurityGroup struct { Name string `json:"name"` } `json:"addSecurityGroup"` } req.AddSecurityGroup.Name = groupName url := fmt.Sprintf("%s/%s/action", apiServers, serverId) requestData := goosehttp.RequestData{ReqValue: req, ExpectedStatus: []int{http.StatusAccepted}} err := c.client.SendRequest(client.POST, "compute", url, &requestData) if err != nil { err = errors.Newf(err, "failed to add security group '%s' to server with id: %s", groupName, serverId) } return err } // RemoveServerSecurityGroup removes a security group from the specified server. func (c *Client) RemoveServerSecurityGroup(serverId, groupName string) error { var req struct { RemoveSecurityGroup struct { Name string `json:"name"` } `json:"removeSecurityGroup"` } req.RemoveSecurityGroup.Name = groupName url := fmt.Sprintf("%s/%s/action", apiServers, serverId) requestData := goosehttp.RequestData{ReqValue: req, ExpectedStatus: []int{http.StatusAccepted}} err := c.client.SendRequest(client.POST, "compute", url, &requestData) if err != nil { err = errors.Newf(err, "failed to remove security group '%s' from server with id: %s", groupName, serverId) } return err } // FloatingIP describes a floating (public) IP address, which can be // assigned to a server, thus allowing connections from outside. type FloatingIP struct { // FixedIP holds the private IP address of the machine (when assigned) FixedIP *string `json:"fixed_ip"` Id string `json:"-"` // InstanceId holds the instance id of the machine, if this FIP is assigned to one InstanceId *string `json:"-"` IP string `json:"ip"` Pool string `json:"pool"` } // ListFloatingIPs lists floating IP addresses associated with the tenant or account. func (c *Client) ListFloatingIPs() ([]FloatingIP, error) { var resp struct { FloatingIPs []FloatingIP `json:"floating_ips"` } requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.GET, "compute", apiFloatingIPs, &requestData) if err != nil { return nil, errors.Newf(err, "failed to list floating ips") } return resp.FloatingIPs, nil } // GetFloatingIP lists details of the floating IP address associated with specified id. func (c *Client) GetFloatingIP(ipId string) (*FloatingIP, error) { var resp struct { FloatingIP FloatingIP `json:"floating_ip"` } url := fmt.Sprintf("%s/%s", apiFloatingIPs, ipId) requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.GET, "compute", url, &requestData) if err != nil { return nil, errors.Newf(err, "failed to get floating ip %s details", ipId) } return &resp.FloatingIP, nil } // AllocateFloatingIP allocates a new floating IP address to a tenant or account. func (c *Client) AllocateFloatingIP() (*FloatingIP, error) { var resp struct { FloatingIP FloatingIP `json:"floating_ip"` } requestData := goosehttp.RequestData{RespValue: &resp} err := c.client.SendRequest(client.POST, "compute", apiFloatingIPs, &requestData) if err != nil { return nil, errors.Newf(err, "failed to allocate a floating ip") } return &resp.FloatingIP, nil } // DeleteFloatingIP deallocates the floating IP address associated with the specified id. func (c *Client) DeleteFloatingIP(ipId string) error { url := fmt.Sprintf("%s/%s", apiFloatingIPs, ipId) requestData := goosehttp.RequestData{ExpectedStatus: []int{http.StatusAccepted}} err := c.client.SendRequest(client.DELETE, "compute", url, &requestData) if err != nil { err = errors.Newf(err, "failed to delete floating ip %s details", ipId) } return err } // AddServerFloatingIP assigns a floating IP address to the specified server. func (c *Client) AddServerFloatingIP(serverId, address string) error { var req struct { AddFloatingIP struct { Address string `json:"address"` } `json:"addFloatingIp"` } req.AddFloatingIP.Address = address url := fmt.Sprintf("%s/%s/action", apiServers, serverId) requestData := goosehttp.RequestData{ReqValue: req, ExpectedStatus: []int{http.StatusAccepted}} err := c.client.SendRequest(client.POST, "compute", url, &requestData) if err != nil { err = errors.Newf(err, "failed to add floating ip %s to server with id: %s", address, serverId) } return err } // RemoveServerFloatingIP removes a floating IP address from the specified server. func (c *Client) RemoveServerFloatingIP(serverId, address string) error { var req struct { RemoveFloatingIP struct { Address string `json:"address"` } `json:"removeFloatingIp"` } req.RemoveFloatingIP.Address = address url := fmt.Sprintf("%s/%s/action", apiServers, serverId) requestData := goosehttp.RequestData{ReqValue: req, ExpectedStatus: []int{http.StatusAccepted}} err := c.client.SendRequest(client.POST, "compute", url, &requestData) if err != nil { err = errors.Newf(err, "failed to remove floating ip %s from server with id: %s", address, serverId) } return err } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/swift/�����������������������������������������������������0000755�0000153�0000161�00000000000�12321735747�021240� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/swift/swift.go���������������������������������������������0000644�0000153�0000161�00000015357�12321735747�022736� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// goose/swift - Go package to interact with OpenStack Object-Storage (Swift) API. // See http://docs.openstack.org/api/openstack-object-storage/1.0/content/. package swift import ( "bytes" "fmt" "io" "io/ioutil" "launchpad.net/goose/client" "launchpad.net/goose/errors" goosehttp "launchpad.net/goose/http" "net/http" "net/url" "time" ) // Client provides a means to access the OpenStack Object Storage Service. type Client struct { client client.Client } // New creates a new Client. func New(client client.Client) *Client { return &Client{client} } type ACL string const ( Private = ACL("") PublicRead = ACL(".r:*,.rlistings") ) // CreateContainer creates a container with the given name. func (c *Client) CreateContainer(containerName string, acl ACL) error { // [sodre]: Due to a possible bug in ceph-radosgw, we need to split the // creation of the bucket and the changing its ACL. requestData := goosehttp.RequestData{ExpectedStatus: []int{http.StatusAccepted, http.StatusCreated}} err := c.client.SendRequest(client.PUT, "object-store", containerName, &requestData) if err != nil { err = maybeNotFound(err, "failed to create container: %s", containerName) return err } // Normally accessing a container or objects within requires a token // for the tenant. Setting an ACL using the X-Container-Read header // can be used to allow unauthenticated HTTP access. headers := make(http.Header) headers.Add("X-Container-Read", string(acl)) requestData = goosehttp.RequestData{ReqHeaders: headers, ExpectedStatus: []int{http.StatusAccepted, http.StatusNoContent}} err = c.client.SendRequest(client.POST, "object-store", containerName, &requestData) if err != nil { err = maybeNotFound(err, "failed to update container read acl: %s", containerName) } return err } // DeleteContainer deletes the specified container. func (c *Client) DeleteContainer(containerName string) error { requestData := goosehttp.RequestData{ExpectedStatus: []int{http.StatusNoContent}} err := c.client.SendRequest(client.DELETE, "object-store", containerName, &requestData) if err != nil { err = maybeNotFound(err, "failed to delete container: %s", containerName) } return err } func (c *Client) touchObject(requestData *goosehttp.RequestData, op, containerName, objectName string) error { path := fmt.Sprintf("%s/%s", containerName, objectName) err := c.client.SendRequest(op, "object-store", path, requestData) if err != nil { err = maybeNotFound(err, "failed to %s object %s from container %s", op, objectName, containerName) } return err } // HeadObject retrieves object metadata and other standard HTTP headers. func (c *Client) HeadObject(containerName, objectName string) (headers http.Header, err error) { requestData := goosehttp.RequestData{ReqHeaders: headers} err = c.touchObject(&requestData, client.HEAD, containerName, objectName) return headers, err } // GetObject retrieves the specified object's data. func (c *Client) GetObject(containerName, objectName string) (obj []byte, err error) { rc, err := c.GetReader(containerName, objectName) if err != nil { return } defer rc.Close() return ioutil.ReadAll(rc) } // The following defines a ReadCloser implementation which reads no data. // It is used instead of returning a nil pointer, which is the same as http.Request.Body. var emptyReadCloser noData type noData struct { io.ReadCloser } // GetObject retrieves the specified object's data. func (c *Client) GetReader(containerName, objectName string) (rc io.ReadCloser, err error) { requestData := goosehttp.RequestData{RespReader: &emptyReadCloser} err = c.touchObject(&requestData, client.GET, containerName, objectName) return requestData.RespReader, err } // DeleteObject removes an object from the storage system permanently. func (c *Client) DeleteObject(containerName, objectName string) error { requestData := goosehttp.RequestData{ExpectedStatus: []int{http.StatusNoContent}} err := c.touchObject(&requestData, client.DELETE, containerName, objectName) return err } // PutObject writes, or overwrites, an object's content and metadata. func (c *Client) PutObject(containerName, objectName string, data []byte) error { r := bytes.NewReader(data) return c.PutReader(containerName, objectName, r, int64(len(data))) } // PutReader writes, or overwrites, an object's content and metadata. func (c *Client) PutReader(containerName, objectName string, r io.Reader, length int64) error { requestData := goosehttp.RequestData{ReqReader: r, ReqLength: int(length), ExpectedStatus: []int{http.StatusCreated}} err := c.touchObject(&requestData, client.PUT, containerName, objectName) return err } // ContainerContents describes a single container and its contents. type ContainerContents struct { Name string `json:"name"` Hash string `json:"hash"` LengthBytes int `json:"bytes"` ContentType string `json:"content_type"` LastModified string `json:"last_modified"` } // GetObject retrieves the specified object's data. func (c *Client) List(containerName, prefix, delim, marker string, limit int) (contents []ContainerContents, err error) { params := make(url.Values) params.Add("prefix", prefix) params.Add("delimiter", delim) params.Add("marker", marker) params.Add("format", "json") if limit > 0 { params.Add("limit", fmt.Sprintf("%d", limit)) } requestData := goosehttp.RequestData{Params: &params, RespValue: &contents} err = c.client.SendRequest(client.GET, "object-store", containerName, &requestData) if err != nil { err = errors.Newf(err, "failed to list contents of container: %s", containerName) } return } // URL returns a non-signed URL that allows retrieving the object at path. // It only works if the object is publicly readable (see SignedURL). func (c *Client) URL(containerName, file string) (string, error) { return c.client.MakeServiceURL("object-store", []string{containerName, file}) } // SignedURL returns a signed URL that allows anyone holding the URL // to retrieve the object at path. The signature is valid until expires. func (c *Client) SignedURL(containerName, file string, expires time.Time) (string, error) { // expiresUnix := expires.Unix() // TODO(wallyworld) - 2013-02-11 bug=1121677 // retrieve the signed URL, for now just return the public URL rawURL, err := c.URL(containerName, file) if err != nil { return "", err } return rawURL, nil } func maybeNotFound(err error, format string, arg ...interface{}) error { if !errors.IsNotFound(err) { if error, ok := err.(*goosehttp.HttpError); ok { // The OpenStack API says that attempts to operate on non existent containers or objects return a status code // of 412 (StatusPreconditionFailed). if error.StatusCode == http.StatusPreconditionFailed { err = errors.NewNotFoundf(err, "", format, arg...) } } } return errors.Newf(err, format, arg...) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/swift/swift_test.go����������������������������������������0000644�0000153�0000161�00000000665�12321735747�023771� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package swift_test import ( "flag" . "launchpad.net/gocheck" "launchpad.net/goose/identity" "testing" ) var live = flag.Bool("live", false, "Include live OpenStack (Canonistack) tests") func Test(t *testing.T) { if *live { cred, err := identity.CompleteCredentialsFromEnv() if err != nil { t.Fatalf("Error setting up test suite: %s", err.Error()) } registerOpenStackTests(cred) } registerLocalTests() TestingT(t) } ���������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/swift/local_test.go����������������������������������������0000644�0000153�0000161�00000003136�12321735747�023723� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package swift_test import ( . "launchpad.net/gocheck" "launchpad.net/goose/identity" "launchpad.net/goose/testing/httpsuite" "launchpad.net/goose/testservices/openstackservice" ) func registerLocalTests() { Suite(&localLiveSuite{}) } // localLiveSuite runs tests from LiveTests using a fake // swift server that runs within the test process itself. type localLiveSuite struct { LiveTests LiveTestsPublicContainer // The following attributes are for using testing doubles. httpsuite.HTTPSuite openstack *openstackservice.Openstack } func (s *localLiveSuite) SetUpSuite(c *C) { c.Logf("Using identity and swift service test doubles") s.HTTPSuite.SetUpSuite(c) // Set up an Openstack service. s.LiveTests.cred = &identity.Credentials{ URL: s.Server.URL, User: "fred", Secrets: "secret", Region: "some region", TenantName: "tenant", } s.LiveTestsPublicContainer.cred = s.LiveTests.cred s.openstack = openstackservice.New(s.LiveTests.cred, identity.AuthUserPass) s.LiveTests.SetUpSuite(c) s.LiveTestsPublicContainer.SetUpSuite(c) } func (s *localLiveSuite) TearDownSuite(c *C) { s.LiveTests.TearDownSuite(c) s.LiveTestsPublicContainer.TearDownSuite(c) s.HTTPSuite.TearDownSuite(c) } func (s *localLiveSuite) SetUpTest(c *C) { s.HTTPSuite.SetUpTest(c) s.openstack.SetupHTTP(s.Mux) s.LiveTests.SetUpTest(c) s.LiveTestsPublicContainer.SetUpTest(c) } func (s *localLiveSuite) TearDownTest(c *C) { s.LiveTests.TearDownTest(c) s.LiveTestsPublicContainer.TearDownTest(c) s.HTTPSuite.TearDownTest(c) } // Additional tests to be run against the service double only go here. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/swift/live_test.go�����������������������������������������0000644�0000153�0000161�00000015040�12321735747�023565� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package swift_test import ( "bytes" "crypto/rand" "fmt" "io" "io/ioutil" . "launchpad.net/gocheck" "launchpad.net/goose/client" "launchpad.net/goose/errors" "launchpad.net/goose/identity" "launchpad.net/goose/swift" "net/http" ) func registerOpenStackTests(cred *identity.Credentials) { Suite(&LiveTests{ cred: cred, }) Suite(&LiveTestsPublicContainer{ cred: cred, }) } func randomName() string { buf := make([]byte, 8) _, err := io.ReadFull(rand.Reader, buf) if err != nil { panic(fmt.Sprintf("error from crypto rand: %v", err)) } return fmt.Sprintf("%x", buf) } type LiveTests struct { cred *identity.Credentials client client.AuthenticatingClient swift *swift.Client containerName string } func (s *LiveTests) SetUpSuite(c *C) { s.containerName = "test_container" + randomName() s.client = client.NewClient(s.cred, identity.AuthUserPass, nil) s.swift = swift.New(s.client) } func (s *LiveTests) TearDownSuite(c *C) { // noop, called by local test suite. } func (s *LiveTests) SetUpTest(c *C) { assertCreateContainer(c, s.containerName, s.swift, swift.Private) } func (s *LiveTests) TearDownTest(c *C) { err := s.swift.DeleteContainer(s.containerName) c.Check(err, IsNil) } func assertCreateContainer(c *C, container string, s *swift.Client, acl swift.ACL) { // The test container may exist already, so try and delete it. // If the result is a NotFound error, we don't care. err := s.DeleteContainer(container) if err != nil { c.Check(errors.IsNotFound(err), Equals, true) } err = s.CreateContainer(container, acl) c.Assert(err, IsNil) } func (s *LiveTests) TestObject(c *C) { object := "test_obj1" data := "...some data..." err := s.swift.PutObject(s.containerName, object, []byte(data)) c.Check(err, IsNil) objdata, err := s.swift.GetObject(s.containerName, object) c.Check(err, IsNil) c.Check(string(objdata), Equals, data) err = s.swift.DeleteObject(s.containerName, object) c.Assert(err, IsNil) } func (s *LiveTests) TestObjectReader(c *C) { object := "test_obj2" data := "...some data..." err := s.swift.PutReader(s.containerName, object, bytes.NewReader([]byte(data)), int64(len(data))) c.Check(err, IsNil) r, err := s.swift.GetReader(s.containerName, object) c.Check(err, IsNil) readData, err := ioutil.ReadAll(r) c.Check(err, IsNil) r.Close() c.Check(string(readData), Equals, data) err = s.swift.DeleteObject(s.containerName, object) c.Assert(err, IsNil) } func (s *LiveTests) TestList(c *C) { data := "...some data..." var files []string = make([]string, 2) var fileNames map[string]bool = make(map[string]bool) for i := 0; i < 2; i++ { files[i] = fmt.Sprintf("test_obj%d", i) fileNames[files[i]] = true err := s.swift.PutObject(s.containerName, files[i], []byte(data)) c.Check(err, IsNil) } items, err := s.swift.List(s.containerName, "", "", "", 0) c.Check(err, IsNil) c.Check(len(items), Equals, len(files)) for _, item := range items { c.Check(fileNames[item.Name], Equals, true) } for i := 0; i < len(files); i++ { s.swift.DeleteObject(s.containerName, files[i]) } } func (s *LiveTests) TestURL(c *C) { object := "test_obj1" data := "...some data..." err := s.swift.PutObject(s.containerName, object, []byte(data)) c.Check(err, IsNil) url, err := s.swift.URL(s.containerName, object) c.Check(err, IsNil) httpClient := http.DefaultClient req, err := http.NewRequest("GET", url, nil) req.Header.Add("X-Auth-Token", s.client.Token()) c.Check(err, IsNil) resp, err := httpClient.Do(req) defer resp.Body.Close() c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusOK) objdata, err := ioutil.ReadAll(resp.Body) c.Check(err, IsNil) c.Check(string(objdata), Equals, data) err = s.swift.DeleteObject(s.containerName, object) c.Assert(err, IsNil) } type LiveTestsPublicContainer struct { cred *identity.Credentials client client.AuthenticatingClient publicClient client.Client swift *swift.Client publicSwift *swift.Client containerName string } func (s *LiveTestsPublicContainer) SetUpSuite(c *C) { s.containerName = "test_container" + randomName() s.client = client.NewClient(s.cred, identity.AuthUserPass, nil) s.swift = swift.New(s.client) } func (s *LiveTestsPublicContainer) TearDownSuite(c *C) { // noop, called by local test suite. } func (s *LiveTestsPublicContainer) SetUpTest(c *C) { err := s.client.Authenticate() c.Assert(err, IsNil) baseURL, err := s.client.MakeServiceURL("object-store", nil) c.Assert(err, IsNil) s.publicClient = client.NewPublicClient(baseURL, nil) s.publicSwift = swift.New(s.publicClient) assertCreateContainer(c, s.containerName, s.swift, swift.PublicRead) } func (s *LiveTestsPublicContainer) TearDownTest(c *C) { err := s.swift.DeleteContainer(s.containerName) c.Check(err, IsNil) } func (s *LiveTestsPublicContainer) TestPublicObjectReader(c *C) { object := "test_obj2" data := "...some data..." err := s.swift.PutReader(s.containerName, object, bytes.NewReader([]byte(data)), int64(len(data))) c.Check(err, IsNil) r, err := s.publicSwift.GetReader(s.containerName, object) c.Check(err, IsNil) readData, err := ioutil.ReadAll(r) c.Check(err, IsNil) r.Close() c.Check(string(readData), Equals, data) err = s.swift.DeleteObject(s.containerName, object) c.Assert(err, IsNil) } func (s *LiveTestsPublicContainer) TestPublicList(c *C) { data := "...some data..." var files []string = make([]string, 2) var fileNames map[string]bool = make(map[string]bool) for i := 0; i < 2; i++ { files[i] = fmt.Sprintf("test_obj%d", i) fileNames[files[i]] = true err := s.swift.PutObject(s.containerName, files[i], []byte(data)) c.Check(err, IsNil) } items, err := s.publicSwift.List(s.containerName, "", "", "", 0) c.Check(err, IsNil) c.Check(len(items), Equals, len(files)) for _, item := range items { c.Check(fileNames[item.Name], Equals, true) } for i := 0; i < len(files); i++ { s.swift.DeleteObject(s.containerName, files[i]) } } func (s *LiveTestsPublicContainer) TestPublicURL(c *C) { object := "test_obj1" data := "...some data..." err := s.swift.PutObject(s.containerName, object, []byte(data)) c.Check(err, IsNil) url, err := s.swift.URL(s.containerName, object) c.Check(err, IsNil) httpClient := http.DefaultClient req, err := http.NewRequest("GET", url, nil) c.Check(err, IsNil) resp, err := httpClient.Do(req) defer resp.Body.Close() c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusOK) objdata, err := ioutil.ReadAll(resp.Body) c.Check(err, IsNil) c.Check(string(objdata), Equals, data) err = s.swift.DeleteObject(s.containerName, object) c.Assert(err, IsNil) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goose/test.py����������������������������������������������������0000755�0000153�0000161�00000015660�12321735747�021450� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python import os import subprocess import sys KNOWN_LIVE_SUITES = [ 'client', 'glance', 'identity', 'nova', 'swift', ] def ensure_tarmac_log_dir(): """Hack-around tarmac not creating its own log directory.""" try: os.makedirs(os.path.expanduser("~/logs/")) except OSError: # Could be already exists, or cannot create, either way, just continue pass def create_tarmac_repository(): """Try to ensure a shared repository for the code.""" try: from bzrlib import ( branch, controldir, errors, transport, repository, reconfigure, ) except ImportError: sys.stderr.write('Could not import bzrlib to ensure a repository\n') return try: b, _ = branch.Branch.open_containing('.') except: sys.stderr.write('Could not open local branch\n') return # By the time we get here, we've already branched everything from # launchpad. So if we aren't in a shared repository, we create one, and # fetch all the data into it, so it doesn't have to be fetched again. if b.repository.is_shared(): return pwd = os.getcwd() expected_dir = 'src/launchpad.net/' offset = pwd.rfind(expected_dir) if offset == -1: sys.stderr.write('Could not find %r to create a shared repo\n') return path = pwd[:offset+len(expected_dir)] try: repository.Repository.open(path) except (errors.NoRepositoryPresent, errors.NotBranchError): pass # Good, the repo didn't exist else: # We must have already created the repo. return repo_fmt = controldir.format_registry.make_bzrdir('default') trans = transport.get_transport(path) info = repo_fmt.initialize_on_transport_ex(trans, create_prefix=False, make_working_trees=True, shared_repo=True, force_new_repo=True, use_existing_dir=True, repo_format_name=repo_fmt.repository_format.get_format_string()) repo = info[0] sys.stderr.write('Reconfiguring to use a shared repository\n') reconfiguration = reconfigure.Reconfigure.to_use_shared(b.bzrdir) try: reconfiguration.apply(False) except errors.NoRepositoryPresent: sys.stderr.write('tarmac did a lightweight checkout,' ' not fetching into the repo.\n') def ensure_juju_core_dependencies(): """Ensure that juju-core and all dependencies have been installed.""" # Note: This potentially overwrites goose while it is updating the world. # However, if we are targetting the trunk branch of goose, that should have # already been updated to the latest version by tarmac. # I don't quite see a way to reconcile that we want the latest juju-core # and all of the other dependencies, but we don't want to touch goose # itself. One option would be to have a split GOPATH. One installs the # latest juju-core and everything else. The other is where the # goose-under-test resides. So we don't add the goose-under-test to GOPATH, # call "go get", then add it to the GOPATH for the rest of the testing. cmd = ['go', 'get', '-u', '-x', 'launchpad.net/juju-core/...'] sys.stderr.write('Running: %s\n' % (' '.join(cmd),)) retcode = subprocess.call(cmd) if retcode != 0: sys.stderr.write('WARN: Failed to update launchpad.net/juju-core\n') def tarmac_setup(opts): """Do all the bits of setup that need to happen for the tarmac bot.""" ensure_tarmac_log_dir() create_tarmac_repository() def setup_gopath(): pwd = os.getcwd() if sys.platform == 'win32': pwd = pwd.replace('\\', '/') offset = pwd.rfind('src/launchpad.net/goose') if offset == -1: sys.stderr.write('Could not find "src/launchpad.net/goose" in cwd: %s\n' % (pwd,)) sys.stderr.write('Unable to automatically set GOPATH\n') return add_gopath = pwd[:offset].rstrip('/') gopath = os.environ.get("GOPATH") if gopath: if add_gopath in gopath: return # Put this path first, so we know we are running these tests gopath = add_gopath + os.pathsep + gopath else: gopath = add_gopath sys.stderr.write('Setting GOPATH to: %s\n' % (gopath,)) os.environ['GOPATH'] = gopath def run_cmd(cmd): cmd_str = ' '.join(cmd) sys.stderr.write('Running: %s\n' % (cmd_str,)) retcode = subprocess.call(cmd) if retcode != 0: sys.stderr.write('FAIL: failed running: %s\n' % (cmd_str,)) return retcode def run_go_fmt(opts): return run_cmd(['go', 'fmt', './...']) def run_go_build(opts): return run_cmd(['go', 'build', './...']) def run_go_test(opts): # Note: I wish we could run this with '-gocheck.v' return run_cmd(['go', 'test', './...']) def run_juju_core_tests(opts): """Run the juju-core test suite""" orig_wd = os.getcwd() try: sys.stderr.write('Switching to juju-core\n') os.chdir('../juju-core') retval = run_cmd(['go', 'build', './...']) if retval != 0: return retval return run_cmd(['go', 'test', './...']) finally: os.chdir(orig_wd) def run_live_tests(opts): """Run all of the live tests.""" orig_wd = os.getcwd() final_retcode = 0 for d in KNOWN_LIVE_SUITES: try: cmd = ['go', 'test', '-live', '-gocheck.v'] sys.stderr.write('Running: %s in %s\n' % (' '.join(cmd), d)) os.chdir(d) retcode = subprocess.call(cmd) if retcode != 0: sys.stderr.write('FAIL: Running live tests in %s\n' % (d,)) final_retcode = retcode finally: os.chdir(orig_wd) return final_retcode def main(args): import argparse p = argparse.ArgumentParser(description='Run the goose test suite') p.add_argument('--verbose', action='store_true', help='Be chatty') p.add_argument('--version', action='version', version='%(prog)s 0.1') p.add_argument('--tarmac', action='store_true', help="Pass this if the script is running as the tarmac bot." " This is used for stuff like ensuring repositories and" " logging directories are initialized.") p.add_argument('--juju-core', action='store_true', help="Run the juju-core trunk tests as well as the goose tests.") p.add_argument('--live', action='store_true', help="Run tests against a live service.") opts = p.parse_args(args) setup_gopath() if opts.tarmac: tarmac_setup(opts) to_run = [run_go_fmt, run_go_build, run_go_test] if opts.juju_core: ensure_juju_core_dependencies() to_run.append(run_juju_core_tests) if opts.live: to_run.append(run_live_tests) for func in to_run: retcode = func(opts) if retcode != 0: return retcode if __name__ == '__main__': import sys sys.exit(main(sys.argv[1:])) ��������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/�����������������������������������������������������������0000755�0000153�0000161�00000000000�12321736015�020052� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/endpoints_test.go������������������������������������������0000644�0000153�0000161�00000006720�12321735761�023457� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" . "launchpad.net/gocheck" "net/url" ) type endpointsSuite struct{} var _ = Suite(&endpointsSuite{}) func (*endpointsSuite) TestGetEndpointReturnsEndpointsForKnownRegions(c *C) { internationalLocations := []string{ "West Europe", "East Asia", "East US 2", "Southeast Asia", "East US", "Central US", "West US", "North Europe", } internationalEndpoint := APIEndpoint("https://core.windows.net/") for _, location := range internationalLocations { c.Check(GetEndpoint(location), Equals, internationalEndpoint) } // The mainland-China locations have a different endpoint. // (Actually the East Asia data centre is said to be in Hong Kong, but it // acts as international). mainlandChinaLocations := []string{ "China East", "China North", } mainlandChinaEndpoint := APIEndpoint("https://core.chinacloudapi.cn/") for _, location := range mainlandChinaLocations { c.Check(GetEndpoint(location), Equals, mainlandChinaEndpoint) } } func (*endpointsSuite) TestGetEndpointMakesGoodGuessesForUknownRegions(c *C) { c.Check( GetEndpoint("South San Marino Highlands"), Equals, GetEndpoint("West US")) c.Check( GetEndpoint("Central China West"), Equals, GetEndpoint("China East")) } func (*endpointsSuite) TestPrefixHostPrefixesSubdomain(c *C) { c.Check( prefixHost("foo", "http://example.com"), Equals, "http://foo.example.com") } func (*endpointsSuite) TestPrefixHostPreservesOtherURLComponents(c *C) { c.Check( prefixHost("foo", "http://example.com/"), Equals, "http://foo.example.com/") c.Check( prefixHost("foo", "nntp://example.com"), Equals, "nntp://foo.example.com") c.Check( prefixHost("foo", "http://user@example.com"), Equals, "http://user@foo.example.com") c.Check( prefixHost("foo", "http://example.com:999"), Equals, "http://foo.example.com:999") c.Check( prefixHost("foo", "http://example.com/path"), Equals, "http://foo.example.com/path") } func (*endpointsSuite) TestPrefixHostEscapes(c *C) { host := "5%=1/20?" c.Check( prefixHost(host, "http://example.com"), Equals, fmt.Sprintf("http://%s.example.com", url.QueryEscape(host))) } func (*endpointsSuite) TestManagementAPICombinesWithGetEndpoint(c *C) { c.Check( GetEndpoint("West US").ManagementAPI(), Equals, "https://management.core.windows.net/") c.Check( GetEndpoint("China East").ManagementAPI(), Equals, "https://management.core.chinacloudapi.cn/") } func (*endpointsSuite) TestBlobStorageAPIIncludesAccountName(c *C) { c.Check( APIEndpoint("http://example.com").BlobStorageAPI("myaccount"), Equals, "http://myaccount.blob.example.com") } func (*endpointsSuite) TestBlobStorageAPICombinesWithGetEndpoint(c *C) { c.Check( GetEndpoint("West US").BlobStorageAPI("account"), Equals, "https://account.blob.core.windows.net/") c.Check( GetEndpoint("China East").BlobStorageAPI("account"), Equals, "https://account.blob.core.chinacloudapi.cn/") } ������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/x509session.go���������������������������������������������0000644�0000153�0000161�00000014206�12321735761�022524� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "errors" "fmt" "launchpad.net/gwacl/fork/http" "launchpad.net/gwacl/fork/tls" "net/url" "strings" ) type x509Session struct { subscriptionId string certFile string client *http.Client baseURL *url.URL retryPolicy RetryPolicy } // httpRedirectErr is a unique error used to prevent // net/http from automatically following redirects. // See commentary on CheckRedirect in newX509Session. var httpRedirectErr = errors.New("redirect") // newX509Session creates and returns a new x509Session based on credentials // and X509 certificate files. // For testing purposes, certFile can be passed as the empty string and it // will be ignored. func newX509Session(subscriptionId, certFile, location string, retryPolicy RetryPolicy) (*x509Session, error) { certs := []tls.Certificate{} if certFile != "" { // cert, err := tls.LoadX509KeyPair(certFile, certFile) if err != nil { return nil, err } certs = append(certs, cert) } client := http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ Certificates: certs, }, // See https://code.google.com/p/go/issues/detail?id=4677 // We need to force the connection to close each time so that we don't // hit the above Go bug. DisableKeepAlives: true, }, // See https://code.google.com/p/go/issues/detail?id=4800 // We need to follow temporary redirects (307s), while // retaining headers. We also need to follow redirects // for POST and DELETE automatically. CheckRedirect: func(req *http.Request, via []*http.Request) error { return httpRedirectErr }, } endpointURL := GetEndpoint(location).ManagementAPI() baseURL, err := url.Parse(endpointURL) if err != nil { panic(fmt.Errorf("cannot parse Azure endpoint URL '%s' - %v", endpointURL, err)) } session := x509Session{ subscriptionId: subscriptionId, certFile: certFile, client: &client, baseURL: baseURL, retryPolicy: retryPolicy, } return &session, nil } // composeURL puts together a URL for an item on the Azure API based on // the starting point used by the session, and a given relative path from // there. func (session *x509Session) composeURL(path string) string { if strings.HasPrefix(path, "/") { panic(fmt.Errorf("got absolute API path '%s' instead of relative one", path)) } escapedID := url.QueryEscape(session.subscriptionId) pathURL, err := url.Parse(escapedID + "/" + path) if err != nil { panic(err) } return session.baseURL.ResolveReference(pathURL).String() } // _X509Dispatcher is the function used to dispatch requests. We call the // function through this pointer, not directly, so that tests can inject // fakes. var _X509Dispatcher = performX509Request // getServerError returns a ServerError matching the given server response // status, or nil if the server response indicates success. func (session *x509Session) getServerError(status int, body []byte, description string) error { if status < http.StatusOK || status >= http.StatusMultipleChoices { return newHTTPError(status, body, description) } return nil } // get performs a GET request to the Azure management API. // It returns the response body and/or an error. If the error is a // ServerError, the returned body will be the one received from the server. // For any other kind of error, the returned body will be nil. func (session *x509Session) get(path, apiVersion string) (*x509Response, error) { request := newX509RequestGET(session.composeURL(path), apiVersion) response, err := _X509Dispatcher(session, request) if err != nil { return nil, err } err = session.getServerError(response.StatusCode, response.Body, "GET request failed") return response, err } // post performs a POST request to the Azure management API. // It returns the response body and/or an error. If the error is a // ServerError, the returned body will be the one received from the server. // For any other kind of error, the returned body will be nil. // Be aware that Azure may perform POST operations asynchronously. If you are // not sure, call blockUntilCompleted() on the response. func (session *x509Session) post(path, apiVersion string, body []byte, contentType string) (*x509Response, error) { request := newX509RequestPOST(session.composeURL(path), apiVersion, body, contentType) response, err := _X509Dispatcher(session, request) if err != nil { return nil, err } err = session.getServerError(response.StatusCode, response.Body, "POST request failed") return response, err } // delete performs a DELETE request to the Azure management API. // Be aware that Azure may perform DELETE operations asynchronously. If you // are not sure, call blockUntilCompleted() on the response. func (session *x509Session) delete(path, apiVersion string) (*x509Response, error) { request := newX509RequestDELETE(session.composeURL(path), apiVersion) response, err := _X509Dispatcher(session, request) if err != nil { return response, err } err = session.getServerError(response.StatusCode, response.Body, "DELETE request failed") return response, err } // put performs a PUT request to the Azure management API. // Be aware that Azure may perform PUT operations asynchronously. If you are // not sure, call blockUntilCompleted() on the response. func (session *x509Session) put(path, apiVersion string, body []byte, contentType string) (*x509Response, error) { request := newX509RequestPUT(session.composeURL(path), apiVersion, body, contentType) response, err := _X509Dispatcher(session, request) if err != nil { return nil, err } err = session.getServerError(response.StatusCode, response.Body, "PUT request failed") return response, err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/testing.go�������������������������������������������������0000644�0000153�0000161�00000005605�12321735761�022073� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "net/http" ) // NewTestStorageContext returns a StorageContext object built using // the given *http.Client object. It's meant to be used in the tests // of other applications using gwacl to create a StorageContext that will // interact with a fake client object. func NewTestStorageContext(client *http.Client) *StorageContext { storageContext := &StorageContext{} storageContext.client = client storageContext.AzureEndpoint = "http://127.0.0.1/" return storageContext } // PatchManagementAPIResponses patches gwacl's ManagementAPI objects so that // they can be used in tests. Calling PatchManagementAPIResponses will make // the ManagementAPI objects talk to a fake http server instead of talking to // the Azure server and get the pre-canned responses from a fake http server. // Use the returned X509Requests to inspect the issued requests. // It's meant to be used in the tests of other applications using gwacl's // ManagementAPI objects. func PatchManagementAPIResponses(responses []DispatcherResponse) *[]*X509Request { rigPreparedResponseDispatcher(responses) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) return &recordedRequests } // NewDispatcherResponse creates a DispatcherResponse that can then be used by // PatchManagementAPIResponses to simulate responses from the Azure server. // It's meant to be used in the tests of other applications using gwacl's // ManagementAPI objects. func NewDispatcherResponse(body []byte, statusCode int, errorObject error) DispatcherResponse { return DispatcherResponse{ response: &x509Response{ Body: body, StatusCode: statusCode, }, errorObject: errorObject} } // MockingTransport is used as an http.Client.Transport for testing. It // records the sequence of requests, and returns a predetermined sequence of // Responses and errors. type MockingTransport struct { Exchanges []*MockingTransportExchange ExchangeCount int } // MockingTransport implements the http.RoundTripper interface. var _ http.RoundTripper = &MockingTransport{} func (t *MockingTransport) AddExchange(response *http.Response, error error) { exchange := MockingTransportExchange{Response: response, Error: error} t.Exchanges = append(t.Exchanges, &exchange) } func (t *MockingTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { exchange := t.Exchanges[t.ExchangeCount] t.ExchangeCount += 1 exchange.Request = req return exchange.Response, exchange.Error } // MockingTransportExchange is a recording of a request and a response over // HTTP. type MockingTransportExchange struct { Request *http.Request Response *http.Response Error error } ���������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/names_test.go����������������������������������������������0000644�0000153�0000161�00000007773�12321735761�022570� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( . "launchpad.net/gocheck" "strings" ) type namesSuite struct{} var _ = Suite(&namesSuite{}) func (*namesSuite) TestPickOneReturnsOneCharacter(c *C) { c.Check(len(pickOne("abcd")), Equals, 1) } func (*namesSuite) TestMakeRandomIdentifierObeysLength(c *C) { length := 6 + random.Intn(50) c.Check(len(makeRandomIdentifier("x", length)), Equals, length) } // makeRandomIdentifier ensures that there are at least 5 random characters in // an identifier. func (*namesSuite) TestMakeRandomIdentifierEnsuresSomeRandomness(c *C) { c.Check(makeRandomIdentifier("1234-", 10), Matches, "1234-[a-z0-9]{5}") c.Check( func() { makeRandomIdentifier("12345-", 10) }, PanicMatches, "prefix '12345-' is too long; space is needed for at least 5 random characters, only 4 given") } func (*namesSuite) TestMakeRandomIdentifierRandomizes(c *C) { // There is a minute chance that this will start failing just because // the randomizer repeats a pattern of results. If so, seed it. c.Check( makeRandomIdentifier("x", 100), Not(Equals), makeRandomIdentifier("x", 100)) } func (*namesSuite) TestMakeRandomIdentifierPicksDifferentCharacters(c *C) { // There is a minute chance that this will start failing just because // the randomizer repeats a pattern of results. If so, seed it. chars := make(map[rune]bool) for _, chr := range makeRandomIdentifier("", 100) { chars[chr] = true } c.Check(len(chars), Not(Equals), 1) } func (*namesSuite) TestMakeRandomIdentifierUsesPrefix(c *C) { c.Check(makeRandomIdentifier("prefix", 11), Matches, "prefix.*") } func (*namesSuite) TestMakeRandomIdentifierUsesOnlyAcceptedCharacters(c *C) { c.Check(makeRandomIdentifier("", 100), Matches, "[0-9a-z]*") } func (*namesSuite) TestMakeRandomIdentifierAcceptsEmptyPrefix(c *C) { // In particular, the first character must still be a letter. c.Check(makeRandomIdentifier("", 5), Matches, "[a-z].*") } func (*namesSuite) TestMakeRandomDiskName(c *C) { c.Check(MakeRandomDiskName(""), Not(HasLen), 0) } func (*namesSuite) TestMakeRandomRoleName(c *C) { c.Check(MakeRandomRoleName(""), Not(HasLen), 0) } func (*namesSuite) TestMakeRandomVirtualNetworkName(c *C) { c.Check(MakeRandomVirtualNetworkName(""), Not(HasLen), 0) } func (*namesSuite) TestMakeRandomHostedServiceName(c *C) { c.Check(MakeRandomHostedServiceName(""), Not(HasLen), 0) } func (*namesSuite) TestMakeRandomHostedUsesALimitedNumberOfRandomChars(c *C) { prefix := "prefix" expectedSize := len(prefix) + HostedServiceNameRandomChars c.Check(MakeRandomHostedServiceName(prefix), HasLen, expectedSize) } func (*namesSuite) TestMakeRandomHostedRejectsLongPrefix(c *C) { tooLongPrefix := makeRandomIdentifier("", HostedServiceNameMaximumPrefixSize+1) c.Check( func() { MakeRandomHostedServiceName(tooLongPrefix) }, PanicMatches, ".*is too long.*") } func (*namesSuite) TestMakeRandomHostedAcceptsLongestPrefix(c *C) { prefix := makeRandomIdentifier("", HostedServiceNameMaximumPrefixSize) c.Check(MakeRandomHostedServiceName(prefix), HasLen, HostedServiceNameMaxiumSize) } func assertIsAzureValidPassword(c *C, password string) { c.Check(MakeRandomPassword(), HasLen, passwordSize) if !strings.ContainsAny(password, upperCaseLetters) { c.Errorf("Password %v does not contain a single upper-case letter!", password) } if !strings.ContainsAny(password, letters) { c.Errorf("Password %v does not contain a single lower-case letter!", password) } if !strings.ContainsAny(password, digits) { c.Errorf("Password %v does not contain a single digit!", password) } } func (*namesSuite) TestMakeRandomPassword(c *C) { for index := 0; index < 100; index += 1 { password := MakeRandomPassword() assertIsAzureValidPassword(c, password) } } �����juju-core_1.18.1/src/launchpad.net/gwacl/LICENSE����������������������������������������������������0000644�0000153�0000161�00000001302�12321735761�021062� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������GWACL - Go API for talking to Windows Azure. Copyright 2012-2013, Canonical Ltd. Except where explicitly noted, all files herein are part of GWACL. GWACL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. See COPYING for the full terms of the GNU Lesser General Public License. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/retry_policy_test.go���������������������������������������0000644�0000153�0000161�00000012121�12321735761�024170� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" . "launchpad.net/gocheck" forkedHttp "launchpad.net/gwacl/fork/http" "net/http" "time" ) type retryPolicySuite struct{} var _ = Suite(&retryPolicySuite{}) func (*retryPolicySuite) TestNoRetryPolicyDoesNotRetry(c *C) { c.Check(NoRetryPolicy.NbRetries, Equals, 0) } func (*retryPolicySuite) TestDefaultPolicyIsNoRetryPolicy(c *C) { c.Check(NoRetryPolicy, DeepEquals, RetryPolicy{}) } func (*retryPolicySuite) TestIsRetryCodeChecksStatusCode(c *C) { c.Check( RetryPolicy{HttpStatusCodes: []int{http.StatusConflict}}.isRetryCode(http.StatusConflict), Equals, true) c.Check( RetryPolicy{HttpStatusCodes: []int{}}.isRetryCode(http.StatusOK), Equals, false) c.Check( RetryPolicy{HttpStatusCodes: []int{http.StatusConflict}}.isRetryCode(http.StatusOK), Equals, false) } func (*retryPolicySuite) TestGetRetryHelperReturnsHelper(c *C) { policy := RetryPolicy{NbRetries: 7, HttpStatusCodes: []int{http.StatusConflict}, Delay: time.Minute} helper := policy.getRetryHelper() c.Check(*helper.policy, DeepEquals, policy) c.Check(helper.retriesLeft, Equals, policy.NbRetries) } type retryHelperSuite struct{} var _ = Suite(&retryHelperSuite{}) func (*retryHelperSuite) TestShouldRetryExhaustsRetries(c *C) { nbTries := 3 policy := RetryPolicy{NbRetries: nbTries, HttpStatusCodes: []int{http.StatusConflict}, Delay: time.Nanosecond} helper := policy.getRetryHelper() retries := []bool{} for i := 0; i < nbTries+1; i++ { retries = append(retries, helper.shouldRetry(http.StatusConflict)) } expectedRetries := []bool{true, true, true, false} c.Check(retries, DeepEquals, expectedRetries) } func (*retryHelperSuite) TestShouldRetryReturnsFalseIfCodeNotInHttpStatusCodes(c *C) { policy := RetryPolicy{NbRetries: 10, HttpStatusCodes: []int{http.StatusConflict}, Delay: time.Nanosecond} helper := policy.getRetryHelper() c.Check(helper.shouldRetry(http.StatusOK), Equals, false) } type retrierSuite struct{} var _ = Suite(&retrierSuite{}) func (*retrierSuite) TestGetRetrier(c *C) { client := &http.Client{} policy := RetryPolicy{NbRetries: 10, HttpStatusCodes: []int{http.StatusConflict}, Delay: time.Nanosecond} retrier := policy.getRetrier(client) c.Check(*retrier.policy, DeepEquals, policy) c.Check(retrier.client, DeepEquals, client) } func (*retrierSuite) TestRetryRequest(c *C) { nbTries := 3 transport := &MockingTransport{} client := &http.Client{Transport: transport} for i := 0; i < nbTries; i++ { response := makeHttpResponse(http.StatusConflict, "") transport.AddExchange(response, nil) } response := makeHttpResponse(http.StatusOK, "") transport.AddExchange(response, nil) policy := RetryPolicy{NbRetries: nbTries, HttpStatusCodes: []int{http.StatusConflict}, Delay: time.Nanosecond} retrier := policy.getRetrier(client) req, err := http.NewRequest("GET", "http://example.com/", nil) c.Assert(err, IsNil) resp, err := retrier.RetryRequest(req) c.Assert(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusOK) c.Check(transport.ExchangeCount, Equals, nbTries+1) } func (*retrierSuite) TestRetryRequestBailsOutWhenError(c *C) { nbTries := 3 transport := &MockingTransport{} client := &http.Client{Transport: transport} transport.AddExchange(nil, fmt.Errorf("request error")) policy := RetryPolicy{NbRetries: nbTries, HttpStatusCodes: []int{http.StatusConflict}, Delay: time.Nanosecond} retrier := policy.getRetrier(client) req, err := http.NewRequest("GET", "http://example.com/", nil) c.Assert(err, IsNil) _, err = retrier.RetryRequest(req) c.Check(err, ErrorMatches, ".*request error.*") c.Check(transport.ExchangeCount, Equals, 1) } type forkedHttpRetrierSuite struct{} var _ = Suite(&forkedHttpRetrierSuite{}) func (*forkedHttpRetrierSuite) TestGetRetrier(c *C) { client := &forkedHttp.Client{} policy := RetryPolicy{NbRetries: 10, HttpStatusCodes: []int{forkedHttp.StatusConflict}, Delay: time.Nanosecond} retrier := policy.getForkedHttpRetrier(client) c.Check(*retrier.policy, DeepEquals, policy) c.Check(retrier.client, DeepEquals, client) } func (*forkedHttpRetrierSuite) TestRetryRequest(c *C) { nbTries := 3 nbRequests := nbTries + 1 client := &forkedHttp.Client{} httpRequests := make(chan *Request, nbRequests) server := makeRecordingHTTPServer(httpRequests, http.StatusConflict, nil, nil) defer server.Close() policy := RetryPolicy{NbRetries: nbTries, HttpStatusCodes: []int{forkedHttp.StatusConflict}, Delay: time.Nanosecond} retrier := policy.getForkedHttpRetrier(client) req, err := forkedHttp.NewRequest("GET", server.URL, nil) c.Assert(err, IsNil) resp, err := retrier.RetryRequest(req) c.Assert(err, IsNil) c.Check(resp.StatusCode, Equals, forkedHttp.StatusConflict) c.Check(len(httpRequests), Equals, nbTries+1) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/helpers_apiobjects_test.go���������������������������������0000644�0000153�0000161�00000010146�12321735761�025316� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). // // Test helpers to fake objects that go into, or come out of, the Azure API. package gwacl func populateEndpoint(endpoint *InputEndpoint) *InputEndpoint { if endpoint.LoadBalancedEndpointSetName == "" { endpoint.LoadBalancedEndpointSetName = MakeRandomString(10) } if endpoint.LocalPort == 0 { endpoint.LocalPort = int(MakeRandomPort()) } if endpoint.Name == "" { endpoint.Name = MakeRandomString(10) } if endpoint.Port == 0 { endpoint.Port = int(MakeRandomPort()) } if endpoint.LoadBalancerProbe == nil { endpoint.LoadBalancerProbe = &LoadBalancerProbe{} } if endpoint.LoadBalancerProbe.Path == "" { endpoint.LoadBalancerProbe.Path = MakeRandomString(10) } if endpoint.LoadBalancerProbe.Port == 0 { endpoint.LoadBalancerProbe.Port = int(MakeRandomPort()) } if endpoint.LoadBalancerProbe.Protocol == "" { endpoint.LoadBalancerProbe.Protocol = MakeRandomString(10) } if endpoint.Protocol == "" { endpoint.Protocol = MakeRandomString(10) } if endpoint.VIP == "" { endpoint.VIP = MakeRandomString(10) } return endpoint } func makeLinuxProvisioningConfiguration() *ConfigurationSet { hostname := MakeRandomString(10) username := MakeRandomString(10) password := MakeRandomString(10) customdata := MakeRandomString(10) disableSSH := BoolToString(MakeRandomBool()) return NewLinuxProvisioningConfigurationSet(hostname, username, password, customdata, disableSSH) } func makeOSVirtualHardDisk() *OSVirtualHardDisk { HostCaching := BoolToString(MakeRandomBool()) DiskLabel := MakeRandomString(10) DiskName := MakeRandomString(10) MediaLink := MakeRandomString(10) SourceImageName := MakeRandomString(10) return &OSVirtualHardDisk{ HostCaching: HostCaching, DiskLabel: DiskLabel, DiskName: DiskName, MediaLink: MediaLink, SourceImageName: SourceImageName} } func makeRole() *Role { RoleSize := "ExtraSmall" RoleName := MakeRandomString(10) RoleType := "PersistentVMRole" config := makeLinuxProvisioningConfiguration() configset := []ConfigurationSet{*config} return &Role{ RoleSize: RoleSize, RoleName: RoleName, RoleType: RoleType, ConfigurationSets: configset} } func makeDnsServer() *DnsServer { name := MakeRandomString(10) address := MakeRandomString(10) return &DnsServer{ Name: name, Address: address} } func makeDeployment() *Deployment { Name := MakeRandomString(10) DeploymentSlot := "Staging" Label := MakeRandomString(10) VirtualNetworkName := MakeRandomString(10) role := makeRole() RoleList := []Role{*role} Dns := []DnsServer{*makeDnsServer()} return &Deployment{ XMLNS: XMLNS, XMLNS_I: XMLNS_I, Name: Name, DeploymentSlot: DeploymentSlot, Label: Label, RoleList: RoleList, VirtualNetworkName: VirtualNetworkName, DNS: Dns, } } func makeCreateStorageServiceInput() *CreateStorageServiceInput { ServiceName := MakeRandomString(10) Description := MakeRandomString(10) Label := MakeRandomString(10) AffinityGroup := MakeRandomString(10) Location := MakeRandomString(10) GeoReplicationEnabled := BoolToString(MakeRandomBool()) ExtendedProperties := []ExtendedProperty{{ Name: MakeRandomString(10), Value: MakeRandomString(10)}} return &CreateStorageServiceInput{ XMLNS: XMLNS, ServiceName: ServiceName, Description: Description, Label: Label, AffinityGroup: AffinityGroup, Location: Location, GeoReplicationEnabled: GeoReplicationEnabled, ExtendedProperties: ExtendedProperties} } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/testing_test.go��������������������������������������������0000644�0000153�0000161�00000004065�12321735761�023131� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" . "launchpad.net/gocheck" "net/http" ) type testTesting struct{} var _ = Suite(&testTesting{}) func (*testTesting) TestNewTestStorageContextCreatesCompleteContext(c *C) { client := &http.Client{Transport: &TestTransport{}} context := NewTestStorageContext(client) context.Account = "myaccount" c.Check(context.Account, Equals, "myaccount") c.Check(context.getAccountURL(), Matches, ".*myaccount.*") } func (*testTesting) TestNewTestStorageContextWorksWithTransport(c *C) { errorMessage := "canned-error" error := fmt.Errorf(errorMessage) transport := &TestTransport{Error: error} client := &http.Client{Transport: transport} context := NewTestStorageContext(client) request := &ListContainersRequest{Marker: ""} _, err := context.ListContainers(request) c.Check(err, ErrorMatches, ".*"+errorMessage+".*") } func (*testTesting) TestNewDispatcherResponse(c *C) { body := []byte("test body") statusCode := http.StatusOK errorObject := fmt.Errorf("canned-error") dispatcherResponse := NewDispatcherResponse(body, statusCode, errorObject) c.Check(dispatcherResponse.errorObject, Equals, errorObject) c.Check(dispatcherResponse.response.Body, DeepEquals, body) c.Check(dispatcherResponse.response.StatusCode, Equals, statusCode) } func (*testTesting) TestPatchManagementAPIResponses(c *C) { response := NewDispatcherResponse([]byte("<Images></Images>"), http.StatusOK, nil) responses := []DispatcherResponse{response, response} requests := PatchManagementAPIResponses(responses) api := makeAPI(c) _, err := api.ListOSImages() c.Assert(err, IsNil) _, err = api.ListOSImages() c.Assert(err, IsNil) c.Assert(len(*requests), Equals, 2) c.Check((*requests)[0].URL, Equals, api.session.composeURL("services/images")) c.Check((*requests)[1].URL, Equals, api.session.composeURL("services/images")) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/management_test.go�����������������������������������������0000644�0000153�0000161�00000144120�12321736014�023555� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" . "launchpad.net/gocheck" "net/http" ) type managementAPISuite struct{} var _ = Suite(&managementAPISuite{}) // makeNamedRoleInstances creates an array of RoleInstance objects, each with // the respective given name. func makeNamedRoleInstances(names ...string) []RoleInstance { instances := make([]RoleInstance, 0) for _, name := range names { instances = append(instances, RoleInstance{RoleName: name}) } return instances } // makeNamedDeployments creates an array of Deployment objects, each with // the respective given name. func makeNamedDeployments(names ...string) []Deployment { deployments := make([]Deployment, 0) for _, name := range names { deployments = append(deployments, Deployment{Name: name}) } return deployments } // makeHostedService creates a HostedService with the given deployments. func makeHostedService(deployments []Deployment) HostedService { desc := HostedServiceDescriptor{ServiceName: "S1"} return HostedService{ HostedServiceDescriptor: desc, Deployments: deployments, } } // makeOKXMLResponse creates a DispatcherResponse with status code OK, and // an XML-serialized version of the given object. // The response is wrapped in a slice because that's slightly easier for // the callers. func makeOKXMLResponse(c *C, bodyObject AzureObject) []DispatcherResponse { body, err := bodyObject.Serialize() c.Assert(err, IsNil) return []DispatcherResponse{ { response: &x509Response{ StatusCode: http.StatusOK, Body: []byte(body), }, }, } } // TestListInstances goes through the happy path for ListInstances. func (suite *managementAPISuite) TestListInstances(c *C) { service := makeHostedService( []Deployment{ {RoleInstanceList: makeNamedRoleInstances("one", "two")}, {RoleInstanceList: makeNamedRoleInstances("three", "four")}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, makeOKXMLResponse(c, service)) // Exercise ListInstances. api := makeAPI(c) request := &ListInstancesRequest{ServiceName: service.ServiceName} instances, err := api.ListInstances(request) c.Assert(err, IsNil) // We get the expected instances back. c.Check(instances, DeepEquals, []RoleInstance{ service.Deployments[0].RoleInstanceList[0], service.Deployments[0].RoleInstanceList[1], service.Deployments[1].RoleInstanceList[0], service.Deployments[1].RoleInstanceList[1], }) // The only request is for the service's properties c.Assert(record, Not(HasLen), 0) expectedURL := fmt.Sprintf( "%ssubscriptionId/services/hostedservices/%s?embed-detail=true", defaultManagement, service.ServiceName) c.Check(record[0].URL, Equals, expectedURL) c.Check(record[0].Method, Equals, "GET") } func (suite *managementAPISuite) TestListInstancesFailsGettingDetails(c *C) { rigPreparedResponseDispatcher( []DispatcherResponse{{response: &x509Response{StatusCode: http.StatusNotFound}}}, ) api := makeAPI(c) request := &ListInstancesRequest{ServiceName: "SomeService"} instances, err := api.ListInstances(request) c.Check(instances, IsNil) c.Assert(err, NotNil) c.Assert(err.Error(), Equals, "GET request failed (404: Not Found)") } // TestListAllDeployments goes through the happy path for ListAllDeployments. func (suite *managementAPISuite) TestListAllDeployments(c *C) { service := makeHostedService(makeNamedDeployments("one", "two")) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, makeOKXMLResponse(c, service)) // Exercise ListDeployments. api := makeAPI(c) request := &ListAllDeploymentsRequest{ServiceName: service.ServiceName} deployments, err := api.ListAllDeployments(request) c.Assert(err, IsNil) // We get the complete set of deployments back. c.Check(deployments, DeepEquals, service.Deployments) // Only one request to the API is made. c.Assert(record, HasLen, 1) } // TestListDeployments tests ListDeployments, including filtering by name. func (suite *managementAPISuite) TestListDeployments(c *C) { service := makeHostedService(makeNamedDeployments("Arthur", "Bobby")) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, makeOKXMLResponse(c, service)) // Exercise ListDeployments. api := makeAPI(c) request := &ListDeploymentsRequest{ ServiceName: service.ServiceName, DeploymentNames: []string{"Arthur"}, } deployments, err := api.ListDeployments(request) c.Assert(err, IsNil) // Only the first deployment - named "Arthur" - is returned. c.Check(deployments, DeepEquals, []Deployment{service.Deployments[0]}) // Only one request to the API is made. c.Assert(record, HasLen, 1) } func (suite *managementAPISuite) TestListDeploymentsWithoutNamesReturnsNothing(c *C) { service := makeHostedService(makeNamedDeployments("One", "Two")) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, makeOKXMLResponse(c, service)) // Exercise ListDeployments. api := makeAPI(c) request := &ListDeploymentsRequest{ ServiceName: service.ServiceName, DeploymentNames: []string{}, } deployments, err := api.ListDeployments(request) c.Assert(err, IsNil) // No deployments are returned. c.Check(deployments, HasLen, 0) } func makeHostedServiceDescriptor() *HostedServiceDescriptor { url := MakeRandomString(10) serviceName := MakeRandomString(10) return &HostedServiceDescriptor{ServiceName: serviceName, URL: url} } func (suite *managementAPISuite) TestListSpecificHostedServices(c *C) { service1 := makeHostedServiceDescriptor() service2 := makeHostedServiceDescriptor() list := HostedServiceDescriptorList{HostedServices: []HostedServiceDescriptor{*service1, *service2}} XML, err := list.Serialize() c.Assert(err, IsNil) fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(XML), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) api := makeAPI(c) request := &ListSpecificHostedServicesRequest{ ServiceNames: []string{service1.ServiceName}, } descriptors, err := api.ListSpecificHostedServices(request) // Only the first service is returned. c.Check(descriptors, DeepEquals, []HostedServiceDescriptor{*service1}) // Only one request to the API is made. c.Assert(recordedRequests, HasLen, 1) } func (suite *managementAPISuite) TestListPrefixedHostedServices(c *C) { prefix := "prefix" service1 := &HostedServiceDescriptor{ServiceName: prefix + "service1"} service2 := makeHostedServiceDescriptor() list := HostedServiceDescriptorList{HostedServices: []HostedServiceDescriptor{*service1, *service2}} XML, err := list.Serialize() c.Assert(err, IsNil) fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(XML), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) api := makeAPI(c) request := &ListPrefixedHostedServicesRequest{ ServiceNamePrefix: prefix, } descriptors, err := api.ListPrefixedHostedServices(request) // Only the first service is returned. c.Check(descriptors, DeepEquals, []HostedServiceDescriptor{*service1}) // Only one request to the API is made. c.Assert(recordedRequests, HasLen, 1) } func (suite *managementAPISuite) TestListSpecificHostedServicesWithoutNamesReturnsNothing(c *C) { service1 := makeHostedServiceDescriptor() service2 := makeHostedServiceDescriptor() list := HostedServiceDescriptorList{HostedServices: []HostedServiceDescriptor{*service1, *service2}} XML, err := list.Serialize() c.Assert(err, IsNil) fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(XML), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) api := makeAPI(c) request := &ListSpecificHostedServicesRequest{ ServiceNames: []string{}, } descriptors, err := api.ListSpecificHostedServices(request) c.Check(descriptors, DeepEquals, []HostedServiceDescriptor{}) // Only one request to the API is made. c.Assert(recordedRequests, HasLen, 1) } var exampleOkayResponse = DispatcherResponse{ response: &x509Response{StatusCode: http.StatusOK}, } var exampleFailResponse = DispatcherResponse{ response: &x509Response{StatusCode: http.StatusInternalServerError}, } var exampleNotFoundResponse = DispatcherResponse{ response: &x509Response{StatusCode: http.StatusNotFound}, } type suiteDestroyDeployment struct{} var _ = Suite(&suiteDestroyDeployment{}) func (suite *suiteDestroyDeployment) makeExampleDeployment() *Deployment { return &Deployment{ RoleInstanceList: makeNamedRoleInstances("one", "two"), RoleList: []Role{ {OSVirtualHardDisk: []OSVirtualHardDisk{ {DiskName: "disk1"}, {DiskName: "disk2"}}}, {OSVirtualHardDisk: []OSVirtualHardDisk{ {DiskName: "disk1"}, {DiskName: "disk3"}}}, }, } } func (suite *suiteDestroyDeployment) TestHappyPath(c *C) { var responses []DispatcherResponse // Prepare. exampleDeployment := suite.makeExampleDeployment() responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...) // For deleting the deployment. responses = append(responses, exampleOkayResponse) // For deleting disks. responses = append(responses, exampleOkayResponse, exampleOkayResponse, exampleOkayResponse) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyDeployment. api := makeAPI(c) request := &DestroyDeploymentRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", } err := api.DestroyDeployment(request) c.Assert(err, IsNil) c.Check(record, HasLen, 5) assertGetDeploymentRequest(c, api, &GetDeploymentRequest{ request.ServiceName, request.DeploymentName}, record[0]) assertDeleteDeploymentRequest(c, api, request.ServiceName, request.DeploymentName, record[1]) assertDeleteDiskRequest(c, api, "disk1", record[2], true) assertDeleteDiskRequest(c, api, "disk2", record[3], true) assertDeleteDiskRequest(c, api, "disk3", record[4], true) } func (suite *suiteDestroyDeployment) TestOkayWhenDeploymentNotFound(c *C) { var responses []DispatcherResponse // Prepare. responses = []DispatcherResponse{exampleNotFoundResponse} record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyDeployment. api := makeAPI(c) request := &DestroyDeploymentRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", } err := api.DestroyDeployment(request) c.Assert(err, IsNil) c.Check(record, HasLen, 1) } func (suite *suiteDestroyDeployment) TestOkayWhenAssetsNotFound(c *C) { var responses []DispatcherResponse // Prepare. exampleDeployment := suite.makeExampleDeployment() responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...) // For deleting the deployment. responses = append(responses, exampleNotFoundResponse) // For deleting the disks. responses = append(responses, exampleNotFoundResponse, exampleNotFoundResponse, exampleNotFoundResponse) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyDeployment. api := makeAPI(c) request := &DestroyDeploymentRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", } err := api.DestroyDeployment(request) c.Assert(err, IsNil) c.Check(record, HasLen, 5) assertGetDeploymentRequest(c, api, &GetDeploymentRequest{ request.ServiceName, request.DeploymentName}, record[0]) assertDeleteDeploymentRequest(c, api, request.ServiceName, request.DeploymentName, record[1]) assertDeleteDiskRequest(c, api, "disk1", record[2], true) assertDeleteDiskRequest(c, api, "disk2", record[3], true) assertDeleteDiskRequest(c, api, "disk3", record[4], true) } func (suite *suiteDestroyDeployment) TestFailsGettingDeployment(c *C) { var responses []DispatcherResponse // Prepare. responses = []DispatcherResponse{exampleFailResponse} record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyDeployment. api := makeAPI(c) request := &DestroyDeploymentRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", } err := api.DestroyDeployment(request) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") c.Check(record, HasLen, 1) } func (suite *suiteDestroyDeployment) TestFailsDeletingDisk(c *C) { var responses []DispatcherResponse // Prepare. exampleDeployment := suite.makeExampleDeployment() responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...) // For deleting disks. responses = append(responses, exampleFailResponse) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyDeployment. api := makeAPI(c) request := &DestroyDeploymentRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", } err := api.DestroyDeployment(request) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "DELETE request failed [(]500: Internal Server Error[)]") c.Check(record, HasLen, 2) } func (suite *suiteDestroyDeployment) TestFailsDeletingDeployment(c *C) { var responses []DispatcherResponse // Prepare. exampleDeployment := suite.makeExampleDeployment() responses = append(responses, makeOKXMLResponse(c, exampleDeployment)...) // For deleting disks. responses = append(responses, exampleOkayResponse, exampleOkayResponse, exampleOkayResponse) // For other requests. responses = append(responses, exampleFailResponse) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyDeployment. api := makeAPI(c) request := &DestroyDeploymentRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", } err := api.DestroyDeployment(request) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "DELETE request failed [(]500: Internal Server Error[)]") c.Check(record, HasLen, 5) } type suiteDestroyHostedService struct{} var _ = Suite(&suiteDestroyHostedService{}) func (suite *suiteDestroyHostedService) makeExampleHostedService(deploymentNames ...string) *HostedService { var exampleHostedService = &HostedService{} for _, deploymentName := range deploymentNames { exampleHostedService.Deployments = append( exampleHostedService.Deployments, Deployment{Name: deploymentName}) } return exampleHostedService } func (suite *suiteDestroyHostedService) TestHappyPath(c *C) { var responses []DispatcherResponse // DestroyHostedService first gets the hosted service proerties. exampleHostedService := suite.makeExampleHostedService("one", "two") responses = append(responses, makeOKXMLResponse(c, exampleHostedService)...) // It calls DestroyDeployment, which first gets the deployment's // properties, deletes any assets contained therein (none in this case) // then deletes the deployment. for _, deployment := range exampleHostedService.Deployments { responses = append(responses, makeOKXMLResponse(c, &deployment)...) responses = append(responses, exampleOkayResponse) } // For deleting the service itself. responses = append(responses, exampleOkayResponse) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyHostedService. api := makeAPI(c) request := &DestroyHostedServiceRequest{ ServiceName: "service-name", } err := api.DestroyHostedService(request) c.Assert(err, IsNil) c.Check(record, HasLen, 6) // The first request is for the hosted service. assertGetHostedServicePropertiesRequest(c, api, request.ServiceName, true, record[0]) // The second and third requests fetch then delete deployment "one"; see // DestroyDeployment for an explanation of this behaviour. assertGetDeploymentRequest(c, api, &GetDeploymentRequest{ request.ServiceName, "one"}, record[1]) assertDeleteDeploymentRequest(c, api, request.ServiceName, "one", record[2]) // The fourth and fifth requests are a repaat of the previous two, but for // deployment "two". assertGetDeploymentRequest(c, api, &GetDeploymentRequest{ request.ServiceName, "two"}, record[3]) assertDeleteDeploymentRequest(c, api, request.ServiceName, "two", record[4]) // The last request deletes the hosted service. assertDeleteHostedServiceRequest(c, api, request.ServiceName, record[5]) } func (suite *suiteDestroyHostedService) TestOkayWhenHostedServiceNotFound(c *C) { var responses []DispatcherResponse // Prepare. responses = []DispatcherResponse{exampleNotFoundResponse} record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyHostedService. api := makeAPI(c) request := &DestroyHostedServiceRequest{ServiceName: "service-name"} err := api.DestroyHostedService(request) c.Assert(err, IsNil) c.Check(record, HasLen, 1) } func (suite *suiteDestroyHostedService) TestOkayWhenDeploymentsNotFound(c *C) { var responses []DispatcherResponse // Prepare. exampleHostedService := suite.makeExampleHostedService("one", "two") responses = append(responses, makeOKXMLResponse(c, exampleHostedService)...) // Someone else has destroyed the deployments in the meantime. responses = append(responses, exampleNotFoundResponse, exampleNotFoundResponse) // Success deleting the hosted service. responses = append(responses, exampleOkayResponse) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyHostedService. api := makeAPI(c) request := &DestroyHostedServiceRequest{ServiceName: "service-name"} err := api.DestroyHostedService(request) c.Assert(err, IsNil) c.Check(record, HasLen, 4) assertGetDeploymentRequest(c, api, &GetDeploymentRequest{ request.ServiceName, "one"}, record[1]) assertGetDeploymentRequest(c, api, &GetDeploymentRequest{ request.ServiceName, "two"}, record[2]) assertDeleteHostedServiceRequest(c, api, request.ServiceName, record[3]) } func (suite *suiteDestroyHostedService) TestOkayWhenHostedServiceNotFoundWhenDeleting(c *C) { var responses []DispatcherResponse // Prepare. exampleHostedService := suite.makeExampleHostedService("one", "two") responses = append(responses, makeOKXMLResponse(c, exampleHostedService)...) // Someone else has destroyed the deployments in the meantime. responses = append(responses, exampleNotFoundResponse, exampleNotFoundResponse) // Someone else has destroyed the hosted service in the meantime. responses = append(responses, exampleNotFoundResponse) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyHostedService. api := makeAPI(c) request := &DestroyHostedServiceRequest{ServiceName: "service-name"} err := api.DestroyHostedService(request) c.Assert(err, IsNil) c.Check(record, HasLen, 4) assertGetDeploymentRequest(c, api, &GetDeploymentRequest{ request.ServiceName, "one"}, record[1]) assertGetDeploymentRequest(c, api, &GetDeploymentRequest{ request.ServiceName, "two"}, record[2]) assertDeleteHostedServiceRequest(c, api, request.ServiceName, record[3]) } func (suite *suiteDestroyHostedService) TestFailsGettingHostedService(c *C) { var responses []DispatcherResponse // Prepare. responses = []DispatcherResponse{exampleFailResponse} record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyHostedService. api := makeAPI(c) request := &DestroyHostedServiceRequest{ServiceName: "service-name"} err := api.DestroyHostedService(request) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") c.Check(record, HasLen, 1) } func (suite *suiteDestroyHostedService) TestFailsDestroyingDeployment(c *C) { var responses []DispatcherResponse // Prepare. exampleHostedService := suite.makeExampleHostedService("one", "two") responses = append(responses, makeOKXMLResponse(c, exampleHostedService)...) responses = append(responses, exampleFailResponse) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyHostedService. api := makeAPI(c) request := &DestroyHostedServiceRequest{ServiceName: "service-name"} err := api.DestroyHostedService(request) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") c.Check(record, HasLen, 2) } func (suite *suiteDestroyHostedService) TestFailsDeletingHostedService(c *C) { var responses []DispatcherResponse // Prepare. exampleHostedService := suite.makeExampleHostedService("one", "two") responses = append(responses, makeOKXMLResponse(c, exampleHostedService)...) // Deployments not found, but that's okay. responses = append(responses, exampleNotFoundResponse, exampleNotFoundResponse) // When deleting hosted service. responses = append(responses, exampleFailResponse) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) // Exercise DestroyHostedService. api := makeAPI(c) request := &DestroyHostedServiceRequest{ServiceName: "service-name"} err := api.DestroyHostedService(request) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "DELETE request failed [(]500: Internal Server Error[)]") c.Check(record, HasLen, 4) } type suiteAddVirtualNetworkSite struct{} var _ = Suite(&suiteAddVirtualNetworkSite{}) func (suite *suiteAddVirtualNetworkSite) TestWhenConfigCannotBeFetched(c *C) { responses := []DispatcherResponse{ {response: &x509Response{StatusCode: http.StatusInternalServerError}}, } record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) err := api.AddVirtualNetworkSite(nil) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") c.Check(record, HasLen, 1) assertGetNetworkConfigurationRequest(c, api, record[0]) } func (suite *suiteAddVirtualNetworkSite) TestWhenConfigDoesNotExist(c *C) { responses := []DispatcherResponse{ // No configuration found. {response: &x509Response{StatusCode: http.StatusNotFound}}, // Accept upload of new configuration. {response: &x509Response{StatusCode: http.StatusOK}}, } record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) virtualNetwork := &VirtualNetworkSite{Name: MakeRandomVirtualNetworkName("test-")} err := api.AddVirtualNetworkSite(virtualNetwork) c.Assert(err, IsNil) c.Check(record, HasLen, 2) assertGetNetworkConfigurationRequest(c, api, record[0]) expected := &NetworkConfiguration{ XMLNS: XMLNS_NC, VirtualNetworkSites: &[]VirtualNetworkSite{*virtualNetwork}, } expectedBody, err := expected.Serialize() c.Assert(err, IsNil) assertSetNetworkConfigurationRequest(c, api, []byte(expectedBody), record[1]) } func (suite *suiteAddVirtualNetworkSite) TestWhenNoPreexistingVirtualNetworkSites(c *C) { // Prepare a basic, empty, configuration. existingConfig := &NetworkConfiguration{XMLNS: XMLNS_NC} responses := makeOKXMLResponse(c, existingConfig) responses = append(responses, DispatcherResponse{ // Accept upload of new configuration. response: &x509Response{StatusCode: http.StatusOK}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) virtualNetwork := &VirtualNetworkSite{Name: MakeRandomVirtualNetworkName("test-")} err := api.AddVirtualNetworkSite(virtualNetwork) c.Assert(err, IsNil) c.Check(record, HasLen, 2) assertGetNetworkConfigurationRequest(c, api, record[0]) expected := &NetworkConfiguration{ XMLNS: XMLNS_NC, VirtualNetworkSites: &[]VirtualNetworkSite{*virtualNetwork}, } expectedBody, err := expected.Serialize() c.Assert(err, IsNil) assertSetNetworkConfigurationRequest(c, api, []byte(expectedBody), record[1]) } func (suite *suiteAddVirtualNetworkSite) TestWhenPreexistingVirtualNetworkSites(c *C) { // Prepare a configuration with a single virtual network. existingConfig := &NetworkConfiguration{ XMLNS: XMLNS_NC, VirtualNetworkSites: &[]VirtualNetworkSite{ {Name: MakeRandomVirtualNetworkName("test-")}, }, } responses := makeOKXMLResponse(c, existingConfig) responses = append(responses, DispatcherResponse{ response: &x509Response{StatusCode: http.StatusOK}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) virtualNetwork := &VirtualNetworkSite{Name: MakeRandomVirtualNetworkName("test-")} err := api.AddVirtualNetworkSite(virtualNetwork) c.Assert(err, IsNil) c.Check(record, HasLen, 2) assertGetNetworkConfigurationRequest(c, api, record[0]) expectedSites := append( *existingConfig.VirtualNetworkSites, *virtualNetwork) expected := &NetworkConfiguration{ XMLNS: XMLNS_NC, VirtualNetworkSites: &expectedSites, } expectedBody, err := expected.Serialize() c.Assert(err, IsNil) assertSetNetworkConfigurationRequest(c, api, []byte(expectedBody), record[1]) } func (suite *suiteAddVirtualNetworkSite) TestWhenPreexistingVirtualNetworkSiteWithSameName(c *C) { // Prepare a configuration with a single virtual network. existingConfig := &NetworkConfiguration{ XMLNS: XMLNS_NC, VirtualNetworkSites: &[]VirtualNetworkSite{ {Name: "virtual-network-bob"}, }, } responses := makeOKXMLResponse(c, existingConfig) rigPreparedResponseDispatcher(responses) api := makeAPI(c) virtualNetwork := &VirtualNetworkSite{Name: "virtual-network-bob"} err := api.AddVirtualNetworkSite(virtualNetwork) c.Check(err, ErrorMatches, "could not add virtual network: \"virtual-network-bob\" already exists") } func (suite *suiteAddVirtualNetworkSite) TestWhenConfigCannotBePushed(c *C) { responses := []DispatcherResponse{ // No configuration found. {response: &x509Response{StatusCode: http.StatusNotFound}}, // Cannot accept upload of new configuration. {response: &x509Response{StatusCode: http.StatusInternalServerError}}, } rigPreparedResponseDispatcher(responses) virtualNetwork := &VirtualNetworkSite{Name: MakeRandomVirtualNetworkName("test-")} err := makeAPI(c).AddVirtualNetworkSite(virtualNetwork) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]") } type suiteRemoveVirtualNetworkSite struct{} var _ = Suite(&suiteRemoveVirtualNetworkSite{}) func (suite *suiteRemoveVirtualNetworkSite) TestWhenConfigCannotBeFetched(c *C) { responses := []DispatcherResponse{ {response: &x509Response{StatusCode: http.StatusInternalServerError}}, } record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) err := api.RemoveVirtualNetworkSite("virtual-network-o-doom") c.Check(err, ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") c.Check(record, HasLen, 1) assertGetNetworkConfigurationRequest(c, api, record[0]) } func (suite *suiteRemoveVirtualNetworkSite) TestWhenConfigDoesNotExist(c *C) { responses := []DispatcherResponse{ {response: &x509Response{StatusCode: http.StatusNotFound}}, } record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) err := api.RemoveVirtualNetworkSite("virtual-network-in-my-eyes") c.Assert(err, IsNil) c.Check(record, HasLen, 1) assertGetNetworkConfigurationRequest(c, api, record[0]) } func (suite *suiteRemoveVirtualNetworkSite) TestWhenNoPreexistingVirtualNetworkSites(c *C) { // Prepare a basic, empty, configuration. existingConfig := &NetworkConfiguration{XMLNS: XMLNS_NC} responses := makeOKXMLResponse(c, existingConfig) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) err := api.RemoveVirtualNetworkSite("virtual-network-on-my-shoes") c.Assert(err, IsNil) c.Check(record, HasLen, 1) assertGetNetworkConfigurationRequest(c, api, record[0]) } func (suite *suiteRemoveVirtualNetworkSite) TestWhenPreexistingVirtualNetworkSites(c *C) { // Prepare a configuration with a single virtual network. existingConfig := &NetworkConfiguration{ XMLNS: XMLNS_NC, VirtualNetworkSites: &[]VirtualNetworkSite{ {Name: MakeRandomVirtualNetworkName("test-")}, }, } responses := makeOKXMLResponse(c, existingConfig) responses = append(responses, DispatcherResponse{ response: &x509Response{StatusCode: http.StatusOK}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) virtualNetwork := &VirtualNetworkSite{Name: MakeRandomVirtualNetworkName("test-")} err := api.RemoveVirtualNetworkSite(virtualNetwork.Name) c.Assert(err, IsNil) c.Check(record, HasLen, 1) assertGetNetworkConfigurationRequest(c, api, record[0]) // It didn't do anything, so no upload. } func (suite *suiteRemoveVirtualNetworkSite) TestWhenPreexistingVirtualNetworkSiteWithSameName(c *C) { // Prepare a configuration with a single virtual network. existingConfig := &NetworkConfiguration{ XMLNS: XMLNS_NC, VirtualNetworkSites: &[]VirtualNetworkSite{ {Name: "virtual-network-bob"}, }, } responses := makeOKXMLResponse(c, existingConfig) responses = append(responses, DispatcherResponse{ response: &x509Response{StatusCode: http.StatusOK}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) virtualNetwork := &VirtualNetworkSite{Name: "virtual-network-bob"} err := api.RemoveVirtualNetworkSite(virtualNetwork.Name) c.Assert(err, IsNil) c.Check(record, HasLen, 2) assertGetNetworkConfigurationRequest(c, api, record[0]) expected := &NetworkConfiguration{ XMLNS: XMLNS_NC, VirtualNetworkSites: &[]VirtualNetworkSite{}, } expectedBody, err := expected.Serialize() c.Assert(err, IsNil) assertSetNetworkConfigurationRequest(c, api, []byte(expectedBody), record[1]) } func (suite *suiteRemoveVirtualNetworkSite) TestWhenConfigCannotBePushed(c *C) { existingConfig := &NetworkConfiguration{ XMLNS: XMLNS_NC, VirtualNetworkSites: &[]VirtualNetworkSite{ {Name: "virtual-network-all-over"}, }, } responses := makeOKXMLResponse(c, existingConfig) responses = append(responses, DispatcherResponse{ response: &x509Response{StatusCode: http.StatusInternalServerError}, }) rigPreparedResponseDispatcher(responses) err := makeAPI(c).RemoveVirtualNetworkSite("virtual-network-all-over") c.Assert(err, NotNil) c.Check(err, ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]") } type suiteListRoleEndpoints struct{} var _ = Suite(&suiteListRoleEndpoints{}) func (suite *suiteListRoleEndpoints) TestWhenNoExistingEndpoints(c *C) { var err error existingRole := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, }, }, } responses := makeOKXMLResponse(c, existingRole) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) request := &ListRoleEndpointsRequest{ ServiceName: "foo", DeploymentName: "foo", RoleName: "foo"} endpoints, err := api.ListRoleEndpoints(request) c.Assert(err, IsNil) c.Assert(endpoints, DeepEquals, []InputEndpoint{}) c.Check(record, HasLen, 1) // Check GetRole was performed. assertGetRoleRequest( c, api, record[0], request.ServiceName, request.DeploymentName, request.RoleName) } func (suite *suiteListRoleEndpoints) TestWhenExistingEndpoints(c *C) { var err error endpoints := &[]InputEndpoint{ { LocalPort: 123, Name: "test123", Port: 1123, }, { LocalPort: 456, Name: "test456", Port: 4456, }} existingRole := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, InputEndpoints: endpoints, }, }, } responses := makeOKXMLResponse(c, existingRole) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) request := &ListRoleEndpointsRequest{ ServiceName: "foo", DeploymentName: "foo", RoleName: "foo"} listedEndpoints, err := api.ListRoleEndpoints(request) c.Assert(err, IsNil) c.Assert(listedEndpoints, DeepEquals, *endpoints) c.Check(record, HasLen, 1) // Check GetRole was performed. assertGetRoleRequest( c, api, record[0], request.ServiceName, request.DeploymentName, request.RoleName) } func (suite *suiteListRoleEndpoints) TestWhenGetRoleFails(c *C) { responses := []DispatcherResponse{ // No role found. {response: &x509Response{StatusCode: http.StatusNotFound}}, } record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) request := &ListRoleEndpointsRequest{ ServiceName: "foo", DeploymentName: "foo", RoleName: "foo"} _, err := api.ListRoleEndpoints(request) c.Check(err, ErrorMatches, "GET request failed [(]404: Not Found[)]") c.Check(record, HasLen, 1) assertGetRoleRequest( c, api, record[0], request.ServiceName, request.DeploymentName, request.RoleName) } type suiteAddRoleEndpoints struct{} var _ = Suite(&suiteAddRoleEndpoints{}) func (suite *suiteAddRoleEndpoints) TestWhenNoPreexistingEndpoints(c *C) { var err error existingRole := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, }, }, } responses := makeOKXMLResponse(c, existingRole) responses = append(responses, DispatcherResponse{ // Accept upload of new endpoints response: &x509Response{StatusCode: http.StatusOK}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) endpoints := []InputEndpoint{ { LocalPort: 999, Name: "test999", Port: 1999, }, } request := &AddRoleEndpointsRequest{ ServiceName: "foo", DeploymentName: "foo", RoleName: "foo", InputEndpoints: endpoints} err = api.AddRoleEndpoints(request) c.Assert(err, IsNil) c.Check(record, HasLen, 2) // Check GetRole was performed. assertGetRoleRequest( c, api, record[0], request.ServiceName, request.DeploymentName, request.RoleName) // Check UpdateRole was performed. existingRole.ConfigurationSets[0].InputEndpoints = &endpoints expectedXML, err := existingRole.Serialize() c.Assert(err, IsNil) assertUpdateRoleRequest( c, api, record[1], request.ServiceName, request.DeploymentName, request.RoleName, expectedXML) } func (suite *suiteAddRoleEndpoints) TestWhenPreexistingEndpoints(c *C) { var err error existingRole := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, InputEndpoints: &[]InputEndpoint{ { LocalPort: 123, Name: "test123", Port: 1123, }, }, }, }, } responses := makeOKXMLResponse(c, existingRole) responses = append(responses, DispatcherResponse{ // Accept upload of new endpoints response: &x509Response{StatusCode: http.StatusOK}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) endpoints := []InputEndpoint{ { LocalPort: 999, Name: "test999", Port: 1999, }, } request := &AddRoleEndpointsRequest{ ServiceName: "foo", DeploymentName: "foo", RoleName: "foo", InputEndpoints: endpoints} err = api.AddRoleEndpoints(request) c.Assert(err, IsNil) c.Check(record, HasLen, 2) // Check GetRole was performed. assertGetRoleRequest( c, api, record[0], request.ServiceName, request.DeploymentName, request.RoleName) // Check UpdateRole was performed. allEndpoints := append( *existingRole.ConfigurationSets[0].InputEndpoints, endpoints...) existingRole.ConfigurationSets[0].InputEndpoints = &allEndpoints expectedXML, err := existingRole.Serialize() c.Assert(err, IsNil) assertUpdateRoleRequest( c, api, record[1], request.ServiceName, request.DeploymentName, request.RoleName, expectedXML) } func (suite *suiteAddRoleEndpoints) TestWhenGetRoleFails(c *C) { responses := []DispatcherResponse{ // No role found. {response: &x509Response{StatusCode: http.StatusNotFound}}, } record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) request := &AddRoleEndpointsRequest{ ServiceName: "foo", DeploymentName: "foo", RoleName: "foo"} err := api.AddRoleEndpoints(request) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "GET request failed [(]404: Not Found[)]") c.Check(record, HasLen, 1) assertGetRoleRequest( c, api, record[0], request.ServiceName, request.DeploymentName, request.RoleName) } func (suite *suiteAddRoleEndpoints) TestWhenUpdateFails(c *C) { var err error existingRole := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, }, }, } responses := makeOKXMLResponse(c, existingRole) responses = append(responses, DispatcherResponse{ // Cannot accept upload of new role endpoint response: &x509Response{StatusCode: http.StatusInternalServerError}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) endpoints := []InputEndpoint{ { LocalPort: 999, Name: "test999", Port: 1999, }, } request := &AddRoleEndpointsRequest{ ServiceName: "foo", DeploymentName: "foo", RoleName: "foo", InputEndpoints: endpoints} err = api.AddRoleEndpoints(request) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]") c.Check(record, HasLen, 2) } type suiteRemoveRoleEndpoints struct{} var _ = Suite(&suiteRemoveRoleEndpoints{}) func (suite *suiteRemoveRoleEndpoints) TestWhenNoPreexistingEndpoints(c *C) { var err error existingRole := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, }, }, } responses := makeOKXMLResponse(c, existingRole) responses = append(responses, DispatcherResponse{ // Accept upload of new endpoints response: &x509Response{StatusCode: http.StatusOK}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) endpoints := []InputEndpoint{ { LocalPort: 999, Name: "test999", Port: 1999, }, } request := &RemoveRoleEndpointsRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", RoleName: "role-name", InputEndpoints: endpoints, } err = api.RemoveRoleEndpoints(request) c.Assert(err, IsNil) c.Check(record, HasLen, 2) // Check GetRole was performed. assertGetRoleRequest( c, api, record[0], request.ServiceName, request.DeploymentName, request.RoleName) // Check UpdateRole was performed. expectedXML, err := existingRole.Serialize() c.Assert(err, IsNil) assertUpdateRoleRequest( c, api, record[1], request.ServiceName, request.DeploymentName, request.RoleName, expectedXML) } func (suite *suiteRemoveRoleEndpoints) TestWhenEndpointIsDefined(c *C) { var err error existingRole := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, InputEndpoints: &[]InputEndpoint{ { LocalPort: 123, Name: "test123", Port: 1123, }, { LocalPort: 456, Name: "test456", Port: 4456, }, }, }, }, } responses := makeOKXMLResponse(c, existingRole) responses = append(responses, DispatcherResponse{ // Accept upload of new endpoints response: &x509Response{StatusCode: http.StatusOK}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) request := &RemoveRoleEndpointsRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", RoleName: "role-name", // Remove the first of the existing endppints. InputEndpoints: (*existingRole.ConfigurationSets[0].InputEndpoints)[:1], } err = api.RemoveRoleEndpoints(request) c.Assert(err, IsNil) c.Check(record, HasLen, 2) // Check GetRole was performed. assertGetRoleRequest( c, api, record[0], request.ServiceName, request.DeploymentName, request.RoleName) // Check UpdateRole was performed. expected := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, InputEndpoints: &[]InputEndpoint{ (*existingRole.ConfigurationSets[0].InputEndpoints)[1], }, }, }, } expectedXML, err := expected.Serialize() c.Assert(err, IsNil) assertUpdateRoleRequest( c, api, record[1], request.ServiceName, request.DeploymentName, request.RoleName, expectedXML) } func (suite *suiteRemoveRoleEndpoints) TestWhenAllEndpointsAreRemoved(c *C) { var err error existingRole := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, InputEndpoints: &[]InputEndpoint{ { LocalPort: 123, Name: "test123", Port: 1123, }, }, }, }, } responses := makeOKXMLResponse(c, existingRole) responses = append(responses, DispatcherResponse{ // Accept upload of new endpoints response: &x509Response{StatusCode: http.StatusOK}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) request := &RemoveRoleEndpointsRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", RoleName: "role-name", // Remove the first of the existing endppints. InputEndpoints: *existingRole.ConfigurationSets[0].InputEndpoints, } err = api.RemoveRoleEndpoints(request) c.Assert(err, IsNil) c.Check(record, HasLen, 2) // Check GetRole was performed. assertGetRoleRequest( c, api, record[0], request.ServiceName, request.DeploymentName, request.RoleName) // Check UpdateRole was performed. expected := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, // InputEndpoints is nil, not the empty slice. InputEndpoints: nil, }, }, } expectedXML, err := expected.Serialize() c.Assert(err, IsNil) assertUpdateRoleRequest( c, api, record[1], request.ServiceName, request.DeploymentName, request.RoleName, expectedXML) } func (suite *suiteRemoveRoleEndpoints) TestWhenGetRoleFails(c *C) { responses := []DispatcherResponse{ // No role found. {response: &x509Response{StatusCode: http.StatusNotFound}}, } record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) request := &RemoveRoleEndpointsRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", RoleName: "role-name", } err := api.RemoveRoleEndpoints(request) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "GET request failed [(]404: Not Found[)]") c.Check(record, HasLen, 1) assertGetRoleRequest( c, api, record[0], request.ServiceName, request.DeploymentName, request.RoleName) } func (suite *suiteRemoveRoleEndpoints) TestWhenUpdateFails(c *C) { var err error existingRole := &PersistentVMRole{ ConfigurationSets: []ConfigurationSet{ {ConfigurationSetType: CONFIG_SET_NETWORK}, }, } responses := makeOKXMLResponse(c, existingRole) responses = append(responses, DispatcherResponse{ // Cannot accept upload of new role endpoint response: &x509Response{StatusCode: http.StatusInternalServerError}, }) record := []*X509Request{} rigRecordingPreparedResponseDispatcher(&record, responses) api := makeAPI(c) request := &RemoveRoleEndpointsRequest{ ServiceName: "service-name", DeploymentName: "deployment-name", RoleName: "role-name", } err = api.RemoveRoleEndpoints(request) c.Assert(err, NotNil) c.Check(err, ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]") c.Check(record, HasLen, 2) } type suiteCompareInputEndpoints struct{} var _ = Suite(&suiteCompareInputEndpoints{}) func (suite *suiteCompareInputEndpoints) TestEqualWhenEmpty(c *C) { a := &InputEndpoint{} b := &InputEndpoint{} c.Assert(CompareInputEndpoints(a, b), Equals, true) } func (suite *suiteCompareInputEndpoints) TestEquality(c *C) { checkComparison := func(a, b InputEndpoint, expected bool) { c.Check(CompareInputEndpoints(&a, &b), Equals, expected) } // Name has no influence on comparison. checkComparison( InputEndpoint{Name: "foo"}, InputEndpoint{Name: "bar"}, true) // LoadBalancerProbe has no influence on comparison. checkComparison( InputEndpoint{ LoadBalancerProbe: &LoadBalancerProbe{Path: "foo"}, }, InputEndpoint{ LoadBalancerProbe: &LoadBalancerProbe{Path: "bar"}, }, true, ) // Port influences comparisons. checkComparison( InputEndpoint{Port: 1234}, InputEndpoint{Port: 1234}, true) checkComparison( InputEndpoint{Port: 1234}, InputEndpoint{Port: 5678}, false) // Protocol influences comparisons. checkComparison( InputEndpoint{Protocol: "TCP"}, InputEndpoint{Protocol: "TCP"}, true) checkComparison( InputEndpoint{Protocol: "TCP"}, InputEndpoint{Protocol: "UDP"}, false) // VIP influences comparisons. checkComparison( InputEndpoint{VIP: "1.2.3.4"}, InputEndpoint{VIP: "1.2.3.4"}, true) checkComparison( InputEndpoint{VIP: "1.2.3.4"}, InputEndpoint{VIP: "5.6.7.8"}, false) // LoadBalancedEndpointSetName influences comparisons. checkComparison( InputEndpoint{LoadBalancedEndpointSetName: "foo"}, InputEndpoint{LoadBalancedEndpointSetName: "foo"}, true) checkComparison( InputEndpoint{LoadBalancedEndpointSetName: "foo"}, InputEndpoint{LoadBalancedEndpointSetName: "bar"}, false) // LocalPort influences comparisons only when LoadBalancedEndpointSetName // is not the empty string. checkComparison( InputEndpoint{LocalPort: 1234}, InputEndpoint{LocalPort: 5678}, true) checkComparison( InputEndpoint{LoadBalancedEndpointSetName: "foo", LocalPort: 1234}, InputEndpoint{LoadBalancedEndpointSetName: "foo", LocalPort: 5678}, false) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/utils_test.go����������������������������������������������0000644�0000153�0000161�00000007170�12321735761�022614� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "io" "io/ioutil" . "launchpad.net/gocheck" "net/url" "strings" ) type UtilsSuite struct{} var _ = Suite(&UtilsSuite{}) func (suite *UtilsSuite) TestCheckPathComponents(c *C) { checkPathComponents("fred", "bob", "123", "a..b") // All okay. c.Check( func() { checkPathComponents("foo^bar") }, PanicMatches, "'foo\\^bar' contains URI special characters") c.Check( func() { checkPathComponents("foo/bar") }, PanicMatches, "'foo/bar' contains URI special characters") c.Check( func() { checkPathComponents("..") }, PanicMatches, "'[.][.]' is not allowed") } func (*UtilsSuite) TestReadAndCloseReturnsEmptyStringForNil(c *C) { data, err := readAndClose(nil) c.Assert(err, IsNil) c.Check(string(data), Equals, "") } func (*UtilsSuite) TestReadAndCloseReturnsContents(c *C) { content := "Stream contents." stream := ioutil.NopCloser(strings.NewReader(content)) data, err := readAndClose(stream) c.Assert(err, IsNil) c.Check(string(data), Equals, content) } // fakeStream is a very simple fake implementation of io.ReadCloser. It // acts like an empty stream, but it tracks whether it's been closed yet. type fakeStream struct { closed bool } func (stream *fakeStream) Read([]byte) (int, error) { if stream.closed { panic("Read() from closed fakeStream") } return 0, io.EOF } func (stream *fakeStream) Close() error { stream.closed = true return nil } func (*UtilsSuite) TestReadAndCloseCloses(c *C) { stream := &fakeStream{} _, err := readAndClose(stream) c.Assert(err, IsNil) c.Check(stream.closed, Equals, true) } type TestAddURLQueryParams struct{} var _ = Suite(&TestAddURLQueryParams{}) func (*TestAddURLQueryParams) TestUsesBaseURL(c *C) { baseURL := "http://example.com" extendedURL := addURLQueryParams(baseURL, "key", "value") parsedURL, err := url.Parse(extendedURL) c.Assert(err, IsNil) c.Check(parsedURL.Scheme, Equals, "http") c.Check(parsedURL.Host, Equals, "example.com") } func (suite *TestAddURLQueryParams) TestEscapesParams(c *C) { key := "key&key" value := "value%value" uri := addURLQueryParams("http://example.com", key, value) parsedURL, err := url.Parse(uri) c.Assert(err, IsNil) c.Check(parsedURL.Query()[key], DeepEquals, []string{value}) } func (suite *TestAddURLQueryParams) TestAddsToExistingParams(c *C) { uri := addURLQueryParams("http://example.com?a=one", "b", "two") parsedURL, err := url.Parse(uri) c.Assert(err, IsNil) c.Check(parsedURL.Query(), DeepEquals, url.Values{ "a": {"one"}, "b": {"two"}, }) } func (suite *TestAddURLQueryParams) TestAppendsRepeatedParams(c *C) { uri := addURLQueryParams("http://example.com?foo=bar", "foo", "bar") c.Check(uri, Equals, "http://example.com?foo=bar&foo=bar") } func (suite *TestAddURLQueryParams) TestAddsMultipleParams(c *C) { uri := addURLQueryParams("http://example.com", "one", "un", "two", "deux") parsedURL, err := url.Parse(uri) c.Assert(err, IsNil) c.Check(parsedURL.Query(), DeepEquals, url.Values{ "one": {"un"}, "two": {"deux"}, }) } func (suite *TestAddURLQueryParams) TestRejectsOddNumberOfParams(c *C) { defer func() { err := recover() c.Check(err, ErrorMatches, ".*got 1 parameter.*") }() addURLQueryParams("http://example.com", "key") c.Assert("This should have panicked", Equals, "But it didn't.") } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/shared_signature_test.go�����������������������������������0000644�0000153�0000161�00000005046�12321735761�025003� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "encoding/base64" . "launchpad.net/gocheck" "time" ) type sharedSignatureSuite struct{} var _ = Suite(&sharedSignatureSuite{}) func (*sharedSignatureSuite) TestComposeSharedSignature(c *C) { params := &sharedSignatureParams{ permission: "r", signedExpiry: "2015-02-12", path: "/path/to/file", accountName: "name", signedIdentifier: "identifier", signedVersion: "2012-02-12", signedRessource: "/the/ressource", accountKey: base64.StdEncoding.EncodeToString([]byte("key")), } signature, err := params.composeSharedSignature() c.Assert(err, IsNil) c.Check(signature, Equals, "C/COJt8UagHJR2LBT1129bhDChtgfLGFqfZ0YQpBdF0=") } func (*sharedSignatureSuite) TestComposeAccessQueryValues(c *C) { params := &sharedSignatureParams{ permission: "r", signedExpiry: "2015-02-12", path: "/path/to/file", accountName: "name", signedIdentifier: "identifier", signedVersion: "2012-02-12", signedRessource: "/the/ressource", accountKey: base64.StdEncoding.EncodeToString([]byte("key")), } values, err := params.composeAccessQueryValues() c.Assert(err, IsNil) c.Check(values.Get("sv"), Equals, params.signedVersion) c.Check(values.Get("se"), Equals, params.signedExpiry) c.Check(values.Get("sr"), Equals, params.signedRessource) c.Check(values.Get("sp"), Equals, params.permission) c.Check(values.Get("sig"), Not(HasLen), 0) } func (*sharedSignatureSuite) TestGetReadBlobAccessValues(c *C) { container := "container" filename := "/path/to/file" accountName := "name" accountKey := base64.StdEncoding.EncodeToString([]byte("key")) expires, err := time.Parse("Monday, 02-Jan-06 15:04:05 MST", time.RFC850) c.Assert(err, IsNil) values, err := getReadBlobAccessValues(container, filename, accountName, accountKey, expires) c.Assert(err, IsNil) c.Check(values.Get("sv"), Equals, "2012-02-12") expiryDateString := values.Get("se") expectedExpiryDateString := expires.UTC().Format(time.RFC3339) c.Check(expiryDateString, Equals, expectedExpiryDateString) c.Check(values.Get("sr"), Equals, "b") c.Check(values.Get("sp"), Equals, "r") c.Check(values.Get("sig"), Equals, "HK7xmxiUY/vBNkaIWoJkIcv27g/+QFjwKVgO/I3yWmI=") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/helpers_factory_test.go������������������������������������0000644�0000153�0000161�00000002761�12321735761�024646� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). // // Factories for various types of objects that tests need to create. package gwacl // This should be refactored at some point, it does not belong in here. // Perhaps we can add it to gocheck, or start a testtools-like project. const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz" // MakeRandomString returns an arbitrary string of alphanumerical characters. // TODO: This isn't really a random string, more of a random identifier. func MakeRandomString(length int) string { return string(MakeRandomByteSlice(length)) } // MakeRandomString returns a slice of arbitrary bytes, all corresponding to // alphanumerical characters in ASCII. // TODO: This isn't really very random. Good tests need zero and "high" values. func MakeRandomByteSlice(length int) []byte { dest := make([]byte, length) for i := range dest { num := random.Intn(len(chars)) randChar := chars[num] dest[i] = randChar } return dest } // MakeRandomBool returns an arbitrary bool value (true or false). func MakeRandomBool() bool { v := random.Intn(2) if v == 0 { return false } return true } // MakeRandomPort returns a port number between 1 and 65535 inclusive. func MakeRandomPort() uint16 { port := uint16(random.Intn(1 << 16)) if port == 0 { return MakeRandomPort() } return port } ���������������juju-core_1.18.1/src/launchpad.net/gwacl/deletedisk.go����������������������������������������������0000644�0000153�0000161�00000004513�12321735761�022530� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). // A poller object used to delete a disk. // // It takes an indeterminate time for a disk previously attached to a // deleted VM to become "not in use" and thus be available for deletion. // When we receive the "disk is still attached" error, we try again every // 10 seconds until it succeeds, with a timeout of 30 minutes). // This bug might be related to: // http://social.msdn.microsoft.com/Forums/en-US/WAVirtualMachinesforWindows/thread/4394c75d-59ff-4634-8212-2ad71bf6fbd5/ // // Once this bug is fixed in Windows Azure, this file and the related tests // can safely be removed, and ManagementAPI._DeleteDisk() can replace the // current implementation of ManagementAPI.DeleteDisk() (which uses this // poller). package gwacl import ( "fmt" "regexp" "time" ) var deleteDiskTimeout = 30 * time.Minute var deleteDiskInterval = 10 * time.Second type diskDeletePoller struct { api *ManagementAPI diskName string deleteBlob bool } var _ poller = &diskDeletePoller{} func (poller diskDeletePoller) poll() (*x509Response, error) { return nil, poller.api._DeleteDisk(poller.diskName, poller.deleteBlob) } // isInUseError returns whether or not the given string is of the "disk in use" // type. // Here is a real-world example of the error in question: // "BadRequest - A disk with name gwacldiske5w7lkj is currently in use // by virtual machine gwaclrolemvo1yab running within hosted service // gwacl623yosxtppsa9577xy5, deployment gwaclmachinewes4n64f. (http // code 400: Bad Request)" func isInUseError(errString string, diskName string) bool { pattern := fmt.Sprintf("BadRequest - A disk with name %s is currently in use by virtual machine.*", regexp.QuoteMeta(diskName)) reg := regexp.MustCompile(pattern) return reg.MatchString(errString) } func (poller diskDeletePoller) isDone(response *x509Response, pollerErr error) (bool, error) { if pollerErr == nil { return true, nil } if isInUseError(pollerErr.Error(), poller.diskName) { // The error is of the "disk in use" type: continue polling. return false, nil } // The error is *not* of the "disk in use" type: stop polling and return // the error. return true, pollerErr } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/x509dispatcher.go������������������������������������������0000644�0000153�0000161�00000010511�12321736014�023152� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "bytes" "fmt" "io/ioutil" "launchpad.net/gwacl/fork/http" . "launchpad.net/gwacl/logging" "net/url" ) type X509Request struct { APIVersion string Method string URL string Payload []byte ContentType string } // newX509RequestGET initializes an X509Request for a GET. You may still need // to set further values. func newX509RequestGET(url, apiVersion string) *X509Request { return &X509Request{ Method: "GET", URL: url, APIVersion: apiVersion, } } // newX509RequestPOST initializes an X509Request for a POST. You may still // need to set further values. func newX509RequestPOST(url, apiVersion string, payload []byte, contentType string) *X509Request { return &X509Request{ Method: "POST", URL: url, APIVersion: apiVersion, Payload: payload, ContentType: contentType, } } // newX509RequestDELETE initializes an X509Request for a DELETE. func newX509RequestDELETE(url, apiVersion string) *X509Request { return &X509Request{ Method: "DELETE", URL: url, APIVersion: apiVersion, } } // newX509RequestPUT initializes an X509Request for a PUT. You may still // need to set further values. func newX509RequestPUT(url, apiVersion string, payload []byte, contentType string) *X509Request { return &X509Request{ Method: "PUT", URL: url, APIVersion: apiVersion, Payload: payload, ContentType: contentType, } } type x509Response struct { StatusCode int // TODO: What exactly do we get back? How will we know its encoding? Body []byte Header http.Header } func performX509Request(session *x509Session, request *X509Request) (*x509Response, error) { response := &x509Response{} Debugf("Request: %s %s", request.Method, request.URL) if len(request.Payload) > 0 { Debugf("Request body:\n%s", request.Payload) } bodyReader := ioutil.NopCloser(bytes.NewReader(request.Payload)) httpRequest, err := http.NewRequest(request.Method, request.URL, bodyReader) if err != nil { return nil, err } httpRequest.Header.Set("Content-Type", request.ContentType) httpRequest.Header.Set("x-ms-version", request.APIVersion) retrier := session.retryPolicy.getForkedHttpRetrier(session.client) httpResponse, err := handleRequestRedirects(retrier, httpRequest) if err != nil { return nil, err } response.StatusCode = httpResponse.StatusCode response.Body, err = readAndClose(httpResponse.Body) if err != nil { return nil, err } response.Header = httpResponse.Header Debugf("Response: %d %s", response.StatusCode, http.StatusText(response.StatusCode)) if response.Header != nil { buf := bytes.Buffer{} response.Header.Write(&buf) Debugf("Response headers:\n%s", buf.String()) } if len(response.Body) > 0 { Debugf("Response body:\n%s", response.Body) } return response, nil } func handleRequestRedirects(retrier *forkedHttpRetrier, request *http.Request) (*http.Response, error) { const maxRedirects = 10 // Handle temporary redirects. redirects := -1 for { redirects++ if redirects >= maxRedirects { return nil, fmt.Errorf("stopped after %d redirects", redirects) } response, err := retrier.RetryRequest(request) // For GET and HEAD, we cause the request execution // to return httpRedirectErr if a temporary redirect // is returned, and then deal with it here. if err, ok := err.(*url.Error); ok && err.Err == httpRedirectErr { request.URL, err.Err = url.Parse(err.URL) if err.Err != nil { return nil, err } continue } // For other methods, we must check the response code. if err == nil && response.StatusCode == http.StatusTemporaryRedirect { request.URL, err = response.Location() if err != nil { return nil, err } continue } return response, err } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/management_base.go�����������������������������������������0000644�0000153�0000161�00000051473�12321736014�023520� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "encoding/xml" "fmt" "net/http" "strings" "time" ) // Note: each API call is required to include a version string in the request header. // These may often be the same string, but need to be kept as strings rather than being // pulled out and replaced with a constant, each API call may be individually changed. type ManagementAPI struct { session *x509Session // The interval used when polling the server. // Set this to 0 to prevent polling from happening. When polling is // disabled, the API methods return immediately after the request has // been issued so the caller's code will have to manually deal with the // possibility that the triggered asynchronous operation might still // not be completed. PollerInterval time.Duration // The duration after which the polling is terminated. PollerTimeout time.Duration } // The default interval used when polling the server to get the status of a // running operation. const DefaultPollerInterval = 10 * time.Second // The default duration after which the polling is terminated. const DefaultPollerTimeout = 20 * time.Minute // NewManagementAPIWithRetryPolicy creates an object used to interact with // Windows Azure's API. // http://msdn.microsoft.com/en-us/library/windowsazure/ff800682.aspx func NewManagementAPIWithRetryPolicy(subscriptionId, certFile, location string, policy RetryPolicy) (*ManagementAPI, error) { session, err := newX509Session(subscriptionId, certFile, location, policy) if err != nil { return nil, err } api := ManagementAPI{session, DefaultPollerInterval, DefaultPollerTimeout} return &api, nil } // NewManagementAPI creates an object used to interact with Windows Azure's API. // http://msdn.microsoft.com/en-us/library/windowsazure/ff800682.aspx func NewManagementAPI(subscriptionId, certFile, location string) (*ManagementAPI, error) { return NewManagementAPIWithRetryPolicy(subscriptionId, certFile, location, NoRetryPolicy) } var operationIDHeaderName = http.CanonicalHeaderKey("x-ms-request-id") // getOperationID extracts the Windows Azure operation ID from the headers // of the given x509Response. func getOperationID(response *x509Response) (string, error) { header := response.Header[operationIDHeaderName] if header != nil && len(header) != 0 { return header[0], nil } err := fmt.Errorf("no operation header (%v) found in response", operationIDHeaderName) return "", err } func (api *ManagementAPI) GetRetryPolicy() RetryPolicy { return api.session.retryPolicy } // blockUntilCompleted blocks and polls for completion of an Azure operation. // The "response" parameter is the result of the request that started the // operation. If the response says that the operation is running // asynchronously, this function will block and poll until the operation is // finished. On the other hand, if the response was a failure or a synchronous // result, the function returns immediately. func (api *ManagementAPI) blockUntilCompleted(response *x509Response) error { switch response.StatusCode { case http.StatusAccepted: // Asynchronous. Fall out of the switch and start blocking. case http.StatusOK, http.StatusCreated, http.StatusNoContent: // Simple success. Sometimes it happens; enjoy it. return nil default: // Request failed, synchronously. return newHTTPError(response.StatusCode, response.Body, "request failed") } if api.PollerInterval == 0 { // Polling has been disabled for test purposes. Return immediately. return nil } operationID, err := getOperationID(response) if err != nil { return fmt.Errorf("could not interpret asynchronous response: %v", err) } poller := newOperationPoller(api, operationID) operation, err := performOperationPolling(poller, api.PollerInterval, api.PollerTimeout) if err != nil { return err } if operation.Status != SucceededOperationStatus { return newAzureErrorFromOperation(operation) } return nil } // ListOSImages retrieves the list of available operating system disk images // from the Azure management API. // Images are returned in the order in which Azure lists them. // http://msdn.microsoft.com/en-us/library/windowsazure/jj157191.aspx func (api *ManagementAPI) ListOSImages() (*Images, error) { response, err := api.session.get("services/images", "2013-03-01") if err != nil { return nil, err } images := Images{} err = xml.Unmarshal(response.Body, &images) return &images, err } // ListHostedServices loads a list of HostedServiceDescriptor objects from the // Azure management API. // HostedServiceDescriptor objects contains a small subset of the fields present in // HostedService objects. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460781.aspx func (api *ManagementAPI) ListHostedServices() ([]HostedServiceDescriptor, error) { res, err := api.session.get("services/hostedservices", "2013-10-01") if err != nil { return nil, err } hostedServices := HostedServiceDescriptorList{} err = xml.Unmarshal(res.Body, &hostedServices) return hostedServices.HostedServices, err } // UpdateHostedService updates the provided values on the named service. // Use NewUpdateHostedService() to create an UpdateHostedService params object. // See http://msdn.microsoft.com/en-us/library/windowsazure/gg441303.aspx func (api *ManagementAPI) UpdateHostedService(serviceName string, params *UpdateHostedService) error { var err error checkPathComponents(serviceName) URI := "services/hostedservices/" + serviceName body, err := params.Serialize() if err != nil { return err } response, err := api.session.put(URI, "2013-10-01", []byte(body), "application/xml") if err != nil { return err } return api.blockUntilCompleted(response) } // GetHostedServiceProperties loads a HostedService object from the Azure // management API. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460806.aspx func (api *ManagementAPI) GetHostedServiceProperties( serviceName string, embedDetail bool) (*HostedService, error) { checkPathComponents(serviceName) URI := "services/hostedservices/" + serviceName + "?embed-detail=" switch embedDetail { case true: URI += "true" case false: URI += "false" } res, err := api.session.get(URI, "2013-10-01") if err != nil { return nil, err } hostedService := HostedService{} err = xml.Unmarshal(res.Body, &hostedService) return &hostedService, err } // AddHostedService adds a hosted service. // This is an asynchronous operation on Azure, but this call blocks until the // operation is completed. // This is actually called CreateHostedService in the Azure documentation. // See http://msdn.microsoft.com/en-us/library/windowsazure/gg441304.aspx func (api *ManagementAPI) AddHostedService(definition *CreateHostedService) error { URI := "services/hostedservices" body, err := marshalXML(definition) if err != nil { return err } response, err := api.session.post(URI, "2013-10-01", []byte(body), "application/xml") if err != nil { return err } return api.blockUntilCompleted(response) } // CheckHostedServiceNameAvailability looks to see if the supplied name is // acceptable to use as a cloud service name. It returns nil if it is available // or an error containing the reason if it is not. Names may not be acceptable // based on reserved words, trademarks and profanity. // See http://msdn.microsoft.com/en-us/library/windowsazure/jj154116.aspx func (api *ManagementAPI) CheckHostedServiceNameAvailability(name string) error { var err error response, err := api.session.get( "services/hostedservices/operations/isavailable/"+name, "2013-10-01") if err != nil { return err } availability := &AvailabilityResponse{} err = availability.Deserialize(response.Body) if err != nil { return err } if strings.ToLower(availability.Result) == "true" { return nil } return fmt.Errorf(availability.Reason) } // DeleteHostedService deletes the named hosted service. // See http://msdn.microsoft.com/en-us/library/windowsazure/gg441305.aspx func (api *ManagementAPI) DeleteHostedService(serviceName string) error { response, err := api.session.delete("services/hostedservices/"+serviceName, "2010-10-28") if err != nil { if IsNotFoundError(err) { return nil } return err } return api.blockUntilCompleted(response) } // AddDeployment adds a virtual machine deployment. // This is an asynchronous operation on Azure, but this call blocks until the // operation is completed. // This is actually called CreateDeployment in the Azure documentation. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460813.aspx func (api *ManagementAPI) AddDeployment(definition *Deployment, serviceName string) error { checkPathComponents(serviceName) URI := "services/hostedservices/" + serviceName + "/deployments" body, err := marshalXML(definition) if err != nil { return err } response, err := api.session.post(URI, "2013-10-01", []byte(body), "application/xml") if err != nil { return err } return api.blockUntilCompleted(response) } // DeleteDeployment deletes the named deployment from the named hosted service. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460815.aspx func (api *ManagementAPI) DeleteDeployment(serviceName string, deploymentName string) error { path := "services/hostedservices/" + serviceName + "/deployments/" + deploymentName response, err := api.session.delete(path, "2013-10-01") if err != nil { if IsNotFoundError(err) { return nil } return err } return api.blockUntilCompleted(response) } type GetDeploymentRequest struct { ServiceName string DeploymentName string } // GetDeployment returns a Deployment object for the named hosted service and // deployment name. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460804.aspx func (api *ManagementAPI) GetDeployment(request *GetDeploymentRequest) (*Deployment, error) { checkPathComponents(request.ServiceName) checkPathComponents(request.DeploymentName) path := "services/hostedservices/" + request.ServiceName + "/deployments/" + request.DeploymentName response, err := api.session.get(path, "2013-10-01") if err != nil { return nil, err } deployment := Deployment{} err = deployment.Deserialize(response.Body) if err != nil { return nil, err } return &deployment, nil } // AddStorageAccount starts the creation of a storage account. This is // called a storage service in the Azure API, but nomenclature seems to // have changed. // This is an asynchronous operation on Azure, but the call blocks until the // operation is completed. // This is actually called CreateStorageAccount in the Azure documentation. // See http://msdn.microsoft.com/en-us/library/windowsazure/hh264518.aspx func (api *ManagementAPI) AddStorageAccount(definition *CreateStorageServiceInput) error { uri := "services/storageservices" body, err := marshalXML(definition) if err != nil { return err } response, err := api.session.post(uri, "2013-10-01", []byte(body), "application/xml") if err != nil { return err } return api.blockUntilCompleted(response) } // DeleteStorageAccount deletes a storage account. // See http://msdn.microsoft.com/en-us/library/windowsazure/hh264517.aspx func (api *ManagementAPI) DeleteStorageAccount(storageAccountName string) error { response, err := api.session.delete("services/storageservices/"+storageAccountName, "2011-06-01") if err != nil { if IsNotFoundError(err) { return nil } return err } return api.blockUntilCompleted(response) } // GetStorageAccountKeys retrieves a storage account's primary and secondary // access keys from the Azure service. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460785.aspx func (api *ManagementAPI) GetStorageAccountKeys(accountName string) (*StorageAccountKeys, error) { url := "services/storageservices/" + accountName + "/keys" res, err := api.session.get(url, "2009-10-01") if err != nil { return nil, err } keys := StorageAccountKeys{} err = keys.Deserialize(res.Body) if err != nil { return nil, err } return &keys, nil } type DeleteDiskRequest struct { DiskName string // Name of the disk to delete. DeleteBlob bool // Whether to delete the associated blob storage. } // DeleteDisk deletes the named OS/data disk. // See http://msdn.microsoft.com/en-us/library/windowsazure/jj157200.aspx func (api *ManagementAPI) DeleteDisk(request *DeleteDiskRequest) error { // Use the disk deletion poller to work around a bug in Windows Azure. // See the documentation in the file deletedisk.go for details. poller := diskDeletePoller{api, request.DiskName, request.DeleteBlob} _, err := performPolling(poller, deleteDiskInterval, deleteDiskTimeout) return err } func (api *ManagementAPI) _DeleteDisk(diskName string, deleteBlob bool) error { url := "services/disks/" + diskName if deleteBlob { url = addURLQueryParams(url, "comp", "media") } response, err := api.session.delete(url, "2012-08-01") if err != nil { if IsNotFoundError(err) { return nil } return err } return api.blockUntilCompleted(response) } // Perform an operation on the specified role (as defined by serviceName, // deploymentName and roleName) This is an asynchronous operation on Azure, but // the call blocks until the operation is completed. func (api *ManagementAPI) performRoleOperation(serviceName, deploymentName, roleName, apiVersion string, operation *RoleOperation) error { checkPathComponents(serviceName, deploymentName, roleName) URI := "services/hostedservices/" + serviceName + "/deployments/" + deploymentName + "/roleinstances/" + roleName + "/Operations" body, err := marshalXML(operation) if err != nil { return err } response, err := api.session.post(URI, apiVersion, []byte(body), "application/xml") if err != nil { return err } return api.blockUntilCompleted(response) } type performRoleOperationRequest struct { ServiceName string DeploymentName string RoleName string } type StartRoleRequest performRoleOperationRequest // StartRole starts the named Role. // See http://msdn.microsoft.com/en-us/library/windowsazure/jj157189.aspx func (api *ManagementAPI) StartRole(request *StartRoleRequest) error { return api.performRoleOperation( request.ServiceName, request.DeploymentName, request.RoleName, "2013-10-01", startRoleOperation) } type RestartRoleRequest performRoleOperationRequest // RestartRole restarts the named Role. // See http://msdn.microsoft.com/en-us/library/windowsazure/jj157197.aspx func (api *ManagementAPI) RestartRole(request *RestartRoleRequest) error { return api.performRoleOperation( request.ServiceName, request.DeploymentName, request.RoleName, "2013-10-01", restartRoleOperation) } type ShutdownRoleRequest performRoleOperationRequest // ShutdownRole shuts down the named Role. // See http://msdn.microsoft.com/en-us/library/windowsazure/jj157195.aspx func (api *ManagementAPI) ShutdownRole(request *ShutdownRoleRequest) error { return api.performRoleOperation( request.ServiceName, request.DeploymentName, request.RoleName, "2013-10-01", shutdownRoleOperation) } type GetRoleRequest performRoleOperationRequest // GetRole requests the role data for the specified role. // See http://msdn.microsoft.com/en-us/library/windowsazure/jj157193.aspx func (api *ManagementAPI) GetRole(request *GetRoleRequest) (*PersistentVMRole, error) { checkPathComponents( request.ServiceName, request.DeploymentName, request.RoleName) url := ("services/hostedservices/" + request.ServiceName + "/deployments/" + request.DeploymentName + "/roles/" + request.RoleName) response, err := api.session.get(url, "2013-10-01") if err != nil { return nil, err } role := PersistentVMRole{} err = role.Deserialize(response.Body) if err != nil { return nil, err } return &role, nil } type UpdateRoleRequest struct { // It would be nice to inherit ServiceName, DeploymentName and RoleName // from performRoleOperationRequest... alas, struct embedding is too // clunky, so copy-n-paste it is. My kingdom for a macro! ServiceName string DeploymentName string RoleName string PersistentVMRole *PersistentVMRole } // UpdateRole pushes a PersistentVMRole document back up to Azure for the // specified role. See // http://msdn.microsoft.com/en-us/library/windowsazure/jj157187.aspx func (api *ManagementAPI) UpdateRole(request *UpdateRoleRequest) error { checkPathComponents( request.ServiceName, request.DeploymentName, request.RoleName) url := ("services/hostedservices/" + request.ServiceName + "/deployments/" + request.DeploymentName + "/roles/" + request.RoleName) role, err := request.PersistentVMRole.Serialize() if err != nil { return err } response, err := api.session.put(url, "2013-10-01", []byte(role), "application/xml") if err != nil { return err } return api.blockUntilCompleted(response) } type CreateAffinityGroupRequest struct { CreateAffinityGroup *CreateAffinityGroup } // CreateAffinityGroup sends a request to make a new affinity group. // See http://msdn.microsoft.com/en-us/library/windowsazure/gg715317.aspx func (api *ManagementAPI) CreateAffinityGroup(request *CreateAffinityGroupRequest) error { var err error url := "affinitygroups" body, err := request.CreateAffinityGroup.Serialize() if err != nil { return err } response, err := api.session.post(url, "2013-10-01", []byte(body), "application/xml") if err != nil { return err } return api.blockUntilCompleted(response) } type UpdateAffinityGroupRequest struct { Name string UpdateAffinityGroup *UpdateAffinityGroup } // UpdateAffinityGroup sends a request to update the named affinity group. // See http://msdn.microsoft.com/en-us/library/windowsazure/gg715316.aspx func (api *ManagementAPI) UpdateAffinityGroup(request *UpdateAffinityGroupRequest) error { var err error checkPathComponents(request.Name) url := "affinitygroups/" + request.Name body, err := request.UpdateAffinityGroup.Serialize() if err != nil { return err } response, err := api.session.put(url, "2011-02-25", []byte(body), "application/xml") if err != nil { return err } return api.blockUntilCompleted(response) } type DeleteAffinityGroupRequest struct { Name string } // DeleteAffinityGroup requests a deletion of the named affinity group. // See http://msdn.microsoft.com/en-us/library/windowsazure/gg715314.aspx func (api *ManagementAPI) DeleteAffinityGroup(request *DeleteAffinityGroupRequest) error { checkPathComponents(request.Name) url := "affinitygroups/" + request.Name response, err := api.session.delete(url, "2011-02-25") if err != nil { if IsNotFoundError(err) { return nil } return err } return api.blockUntilCompleted(response) } // GetNetworkConfiguration gets the network configuration for this // subscription. If there is no network configuration the configuration will // be nil. // See http://msdn.microsoft.com/en-us/library/windowsazure/jj157196.aspx func (api *ManagementAPI) GetNetworkConfiguration() (*NetworkConfiguration, error) { response, err := api.session.get("services/networking/media", "2013-10-01") if err != nil { if IsNotFoundError(err) { return nil, nil } return nil, err } networkConfig := NetworkConfiguration{} err = networkConfig.Deserialize(response.Body) if err != nil { return nil, err } return &networkConfig, nil } // SetNetworkConfiguration sets the network configuration for this // subscription. See: // http://msdn.microsoft.com/en-us/library/windowsazure/jj157181.aspx func (api *ManagementAPI) SetNetworkConfiguration(cfg *NetworkConfiguration) error { var err error body, err := cfg.Serialize() if err != nil { return err } response, err := api.session.put( "services/networking/media", "2013-10-01", []byte(body), "application/octet-stream") if err != nil { return err } return api.blockUntilCompleted(response) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/utils.go���������������������������������������������������0000644�0000153�0000161�00000003456�12321735761�021560� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" "io" "io/ioutil" "net/url" ) // checkPathComponents checks that none of the passed components contains any // special characters, where special means "needs percent-encoding in a URI", // does not contain any forward slashes, and is not the string "..". func checkPathComponents(components ...string) { for _, component := range components { if component != url.QueryEscape(component) { panic(fmt.Errorf("'%s' contains URI special characters", component)) } if component == ".." { panic(fmt.Errorf("'..' is not allowed")) } } } // readAndClose reads and closes the given ReadCloser. // // Trying to read from a nil simply returns nil, no error. func readAndClose(stream io.ReadCloser) ([]byte, error) { if stream == nil { return nil, nil } defer stream.Close() return ioutil.ReadAll(stream) } // addURLQueryParams adds query parameters to a URL (and escapes as needed). // Parameters are URL, [key, value, [key, value, [...]]]. // The original URL must be correct, i.e. it should parse without error. func addURLQueryParams(originalURL string, params ...string) string { if len(params)%2 != 0 { panic(fmt.Errorf("got %d parameter argument(s), instead of matched key/value pairs", len(params))) } parsedURL, err := url.Parse(originalURL) if err != nil { panic(err) } query := parsedURL.Query() for index := 0; index < len(params); index += 2 { key := params[index] value := params[index+1] query.Add(key, value) } parsedURL.RawQuery = query.Encode() return parsedURL.String() } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/xmlobjects_test.go�����������������������������������������0000644�0000153�0000161�00000250420�12321736014�023614� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "encoding/base64" "encoding/xml" "fmt" . "launchpad.net/gocheck" "launchpad.net/gwacl/dedent" "sort" "strings" ) type xmlSuite struct{} var _ = Suite(&xmlSuite{}) // // Tests for Marshallers // func (suite *xmlSuite) TestConfigurationSet(c *C) { config := makeLinuxProvisioningConfiguration() xml, err := config.Serialize() c.Assert(err, IsNil) template := dedent.Dedent(` <ConfigurationSet> <ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType> <HostName>%s</HostName> <UserName>%s</UserName> <UserPassword>%s</UserPassword> <CustomData>%s</CustomData> <DisableSshPasswordAuthentication>%v</DisableSshPasswordAuthentication> </ConfigurationSet>`) expected := fmt.Sprintf(template, config.Hostname, config.Username, config.Password, config.CustomData, config.DisableSSHPasswordAuthentication) c.Check(strings.TrimSpace(xml), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestInputEndpoint(c *C) { endpoint := populateEndpoint(&InputEndpoint{}) xml, err := endpoint.Serialize() c.Assert(err, IsNil) template := dedent.Dedent(` <InputEndpoint> <LoadBalancedEndpointSetName>%s</LoadBalancedEndpointSetName> <LocalPort>%v</LocalPort> <Name>%s</Name> <Port>%v</Port> <LoadBalancerProbe> <Path>%s</Path> <Port>%d</Port> <Protocol>%s</Protocol> </LoadBalancerProbe> <Protocol>%s</Protocol> <Vip>%s</Vip> </InputEndpoint>`) expected := fmt.Sprintf(template, endpoint.LoadBalancedEndpointSetName, endpoint.LocalPort, endpoint.Name, endpoint.Port, endpoint.LoadBalancerProbe.Path, endpoint.LoadBalancerProbe.Port, endpoint.LoadBalancerProbe.Protocol, endpoint.Protocol, endpoint.VIP) c.Check(strings.TrimSpace(xml), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestOSVirtualHardDisk(c *C) { disk := makeOSVirtualHardDisk() xml, err := disk.Serialize() c.Assert(err, IsNil) template := dedent.Dedent(` <OSVirtualHardDisk> <HostCaching>%s</HostCaching> <DiskLabel>%s</DiskLabel> <DiskName>%s</DiskName> <MediaLink>%s</MediaLink> <SourceImageName>%s</SourceImageName> </OSVirtualHardDisk>`) expected := fmt.Sprintf(template, disk.HostCaching, disk.DiskLabel, disk.DiskName, disk.MediaLink, disk.SourceImageName) c.Check(strings.TrimSpace(xml), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestConfigurationSetNetworkConfiguration(c *C) { endpoint1 := populateEndpoint(&InputEndpoint{}) endpoint2 := populateEndpoint(&InputEndpoint{}) endpoints := []InputEndpoint{*endpoint1, *endpoint2} subnet1 := MakeRandomString(10) subnet2 := MakeRandomString(10) config := NewNetworkConfigurationSet(endpoints, []string{subnet1, subnet2}) xml, err := config.Serialize() c.Assert(err, IsNil) template := dedent.Dedent(` <ConfigurationSet> <ConfigurationSetType>NetworkConfiguration</ConfigurationSetType> <InputEndpoints> <InputEndpoint> <LoadBalancedEndpointSetName>%s</LoadBalancedEndpointSetName> <LocalPort>%v</LocalPort> <Name>%s</Name> <Port>%v</Port> <LoadBalancerProbe> <Path>%s</Path> <Port>%d</Port> <Protocol>%s</Protocol> </LoadBalancerProbe> <Protocol>%s</Protocol> <Vip>%s</Vip> </InputEndpoint> <InputEndpoint> <LoadBalancedEndpointSetName>%s</LoadBalancedEndpointSetName> <LocalPort>%v</LocalPort> <Name>%s</Name> <Port>%v</Port> <LoadBalancerProbe> <Path>%s</Path> <Port>%d</Port> <Protocol>%s</Protocol> </LoadBalancerProbe> <Protocol>%s</Protocol> <Vip>%s</Vip> </InputEndpoint> </InputEndpoints> <SubnetNames> <SubnetName>%s</SubnetName> <SubnetName>%s</SubnetName> </SubnetNames> </ConfigurationSet>`) expected := fmt.Sprintf(template, endpoint1.LoadBalancedEndpointSetName, endpoint1.LocalPort, endpoint1.Name, endpoint1.Port, endpoint1.LoadBalancerProbe.Path, endpoint1.LoadBalancerProbe.Port, endpoint1.LoadBalancerProbe.Protocol, endpoint1.Protocol, endpoint1.VIP, endpoint2.LoadBalancedEndpointSetName, endpoint2.LocalPort, endpoint2.Name, endpoint2.Port, endpoint2.LoadBalancerProbe.Path, endpoint2.LoadBalancerProbe.Port, endpoint2.LoadBalancerProbe.Protocol, endpoint2.Protocol, endpoint2.VIP, subnet1, subnet2) c.Check(strings.TrimSpace(xml), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestRole(c *C) { role := makeRole() config := role.ConfigurationSets[0] xml, err := role.Serialize() c.Assert(err, IsNil) template := dedent.Dedent(` <Role> <RoleName>%s</RoleName> <RoleType>PersistentVMRole</RoleType> <ConfigurationSets> <ConfigurationSet> <ConfigurationSetType>%s</ConfigurationSetType> <HostName>%s</HostName> <UserName>%s</UserName> <UserPassword>%s</UserPassword> <CustomData>%s</CustomData> <DisableSshPasswordAuthentication>%v</DisableSshPasswordAuthentication> </ConfigurationSet> </ConfigurationSets> <RoleSize>%s</RoleSize> </Role>`) expected := fmt.Sprintf(template, role.RoleName, config.ConfigurationSetType, config.Hostname, config.Username, config.Password, config.CustomData, config.DisableSSHPasswordAuthentication, role.RoleSize) c.Check(strings.TrimSpace(xml), Equals, strings.TrimSpace(expected)) } func makePersistentVMRole(rolename string) string { // This template is from // http://msdn.microsoft.com/en-us/library/windowsazure/jj157193.aspx template := dedent.Dedent(` <PersistentVMRole xmlns="http://schemas.microsoft.com/windowsazure"> <RoleName>%s</RoleName> <OsVersion>operating-system-version</OsVersion> <RoleType>PersistentVMRole</RoleType> <ConfigurationSets> <ConfigurationSet> <ConfigurationSetType>NetworkConfiguration</ConfigurationSetType> <InputEndpoints> <InputEndpoint> <LoadBalancedEndpointSetName>name-of-load-balanced-endpoint-set</LoadBalancedEndpointSetName> <LocalPort>1</LocalPort> <Name>name-of-input-endpoint</Name> <Port>1</Port> <LoadBalancerProbe> <Path>path-of-probe</Path> <Port>1234</Port> <Protocol>protocol-of-input-endpoint-1</Protocol> </LoadBalancerProbe> <Protocol>TCP|UDP</Protocol> <Vip>virtual-ip-address-of-input-endpoint-1</Vip> </InputEndpoint> <InputEndpoint> <LoadBalancedEndpointSetName>name-of-load-balanced-endpoint-set</LoadBalancedEndpointSetName> <LocalPort>2</LocalPort> <Name>name-of-input-endpoint</Name> <Port>2</Port> <LoadBalancerProbe> <Path>path-of-probe</Path> <Port>5678</Port> <Protocol>protocol-of-input-endpoint-2</Protocol> </LoadBalancerProbe> <Protocol>TCP|UDP</Protocol> <Vip>virtual-ip-address-of-input-endpoint-2</Vip> </InputEndpoint> </InputEndpoints> <SubnetNames> <SubnetName>name-of-subnet</SubnetName> </SubnetNames> </ConfigurationSet> </ConfigurationSets> <AvailabilitySetName>name-of-availability-set</AvailabilitySetName> <DataVirtualHardDisks> <DataVirtualHardDisk> <HostCaching>host-caching-mode-of-data-disk</HostCaching> <DiskName>new-or-existing-disk-name</DiskName> <Lun>logical-unit-number-of-data-disk</Lun> <LogicalDiskSizeInGB>size-of-data-disk</LogicalDiskSizeInGB> <MediaLink>path-to-vhd</MediaLink> </DataVirtualHardDisk> </DataVirtualHardDisks> <OSVirtualHardDisk> <HostCaching>host-caching-mode-of-os-disk</HostCaching> <DiskName>name-of-os-disk</DiskName> <MediaLink>path-to-vhd</MediaLink> <SourceImageName>image-used-to-create-os-disk</SourceImageName> <OS>operating-system-on-os-disk</OS> </OSVirtualHardDisk> <RoleSize>size-of-instance</RoleSize> <DefaultWinRmCertificateThumbprint>winrm-cert-thumbprint</DefaultWinRmCertificateThumbprint> </PersistentVMRole>`) return fmt.Sprintf(template, rolename) } func (suite *xmlSuite) TestPersistentVMRoleDeserialize(c *C) { expected := &PersistentVMRole{ XMLNS: XMLNS, RoleName: "name-of-the-vm", OsVersion: "operating-system-version", RoleType: "PersistentVMRole", ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, InputEndpoints: &[]InputEndpoint{ { LoadBalancedEndpointSetName: "name-of-load-balanced-endpoint-set", LocalPort: 1, Name: "name-of-input-endpoint", Port: 1, LoadBalancerProbe: &LoadBalancerProbe{ Path: "path-of-probe", Port: 1234, Protocol: "protocol-of-input-endpoint-1", }, Protocol: "TCP|UDP", VIP: "virtual-ip-address-of-input-endpoint-1", }, { LoadBalancedEndpointSetName: "name-of-load-balanced-endpoint-set", LocalPort: 2, Name: "name-of-input-endpoint", Port: 2, LoadBalancerProbe: &LoadBalancerProbe{ Path: "path-of-probe", Port: 5678, Protocol: "protocol-of-input-endpoint-2", }, Protocol: "TCP|UDP", VIP: "virtual-ip-address-of-input-endpoint-2", }, }, SubnetNames: &[]string{"name-of-subnet"}, }, }, AvailabilitySetName: "name-of-availability-set", DataVirtualHardDisks: &[]DataVirtualHardDisk{ { HostCaching: "host-caching-mode-of-data-disk", DiskName: "new-or-existing-disk-name", LUN: "logical-unit-number-of-data-disk", LogicalDiskSizeInGB: "size-of-data-disk", MediaLink: "path-to-vhd", }, }, OSVirtualHardDisk: OSVirtualHardDisk{ HostCaching: "host-caching-mode-of-os-disk", DiskName: "name-of-os-disk", MediaLink: "path-to-vhd", SourceImageName: "image-used-to-create-os-disk", OS: "operating-system-on-os-disk", }, RoleSize: "size-of-instance", DefaultWinRmCertificateThumbprint: "winrm-cert-thumbprint", } template := makePersistentVMRole("name-of-the-vm") observed := &PersistentVMRole{} err := observed.Deserialize([]byte(template)) c.Assert(err, IsNil) c.Assert(observed, DeepEquals, expected) } func (suite *xmlSuite) TestPersistentVMRoleSerialize(c *C) { role := &PersistentVMRole{ XMLNS: XMLNS, RoleName: "name-of-the-vm", OsVersion: "operating-system-version", RoleType: "PersistentVMRole", ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, InputEndpoints: &[]InputEndpoint{ { LoadBalancedEndpointSetName: "name-of-load-balanced-endpoint-set", LocalPort: 1, Name: "name-of-input-endpoint", Port: 1, LoadBalancerProbe: &LoadBalancerProbe{ Path: "path-of-probe", Port: 1234, Protocol: "protocol-of-input-endpoint-1", }, Protocol: "TCP|UDP", VIP: "virtual-ip-address-of-input-endpoint-1", }, { LoadBalancedEndpointSetName: "name-of-load-balanced-endpoint-set", LocalPort: 2, Name: "name-of-input-endpoint", Port: 2, LoadBalancerProbe: &LoadBalancerProbe{ Path: "path-of-probe", Port: 5678, Protocol: "protocol-of-input-endpoint-2", }, Protocol: "TCP|UDP", VIP: "virtual-ip-address-of-input-endpoint-2", }, }, SubnetNames: &[]string{"name-of-subnet"}, }, }, AvailabilitySetName: "name-of-availability-set", DataVirtualHardDisks: &[]DataVirtualHardDisk{ { HostCaching: "host-caching-mode-of-data-disk", DiskName: "new-or-existing-disk-name", LUN: "logical-unit-number-of-data-disk", LogicalDiskSizeInGB: "size-of-data-disk", MediaLink: "path-to-vhd", }, }, OSVirtualHardDisk: OSVirtualHardDisk{ HostCaching: "host-caching-mode-of-os-disk", DiskName: "name-of-os-disk", MediaLink: "path-to-vhd", SourceImageName: "image-used-to-create-os-disk", OS: "operating-system-on-os-disk", }, RoleSize: "size-of-instance", DefaultWinRmCertificateThumbprint: "winrm-cert-thumbprint", } expected := makePersistentVMRole("name-of-the-vm") observed, err := role.Serialize() c.Assert(err, IsNil) c.Assert(strings.TrimSpace(observed), DeepEquals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestNetworkConfigurationSerialize(c *C) { // Template from // http://msdn.microsoft.com/en-us/library/windowsazure/jj157181.aspx expected := dedent.Dedent(` <NetworkConfiguration xmlns="http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration"> <VirtualNetworkConfiguration> <Dns> <DnsServers> <DnsServer name="dns-server-name" IPAddress="IPV4-address-of-the-server"></DnsServer> </DnsServers> </Dns> <LocalNetworkSites> <LocalNetworkSite name="local-site-name"> <AddressSpace> <AddressPrefix>CIDR-identifier</AddressPrefix> </AddressSpace> <VPNGatewayAddress>IPV4-address-of-the-vpn-gateway</VPNGatewayAddress> </LocalNetworkSite> </LocalNetworkSites> <VirtualNetworkSites> <VirtualNetworkSite name="virtual-network-name" AffinityGroup="affinity-group-name"> <AddressSpace> <AddressPrefix>CIDR-identifier</AddressPrefix> </AddressSpace> <Subnets> <Subnet name="subnet-name"> <AddressPrefix>CIDR-identifier</AddressPrefix> </Subnet> </Subnets> <DnsServersRef> <DnsServerRef name="primary-DNS-name"></DnsServerRef> </DnsServersRef> <Gateway profile="Small"> <VPNClientAddressPool> <AddressPrefix>CIDR-identifier</AddressPrefix> </VPNClientAddressPool> <ConnectionsToLocalNetwork> <LocalNetworkSiteRef name="local-site-name"> <Connection type="connection-type"></Connection> </LocalNetworkSiteRef> </ConnectionsToLocalNetwork> </Gateway> </VirtualNetworkSite> </VirtualNetworkSites> </VirtualNetworkConfiguration> </NetworkConfiguration>`) input := NetworkConfiguration{ XMLNS: XMLNS_NC, DNS: &[]VirtualNetDnsServer{ { Name: "dns-server-name", IPAddress: "IPV4-address-of-the-server", }, }, LocalNetworkSites: &[]LocalNetworkSite{ { Name: "local-site-name", AddressSpacePrefixes: []string{ "CIDR-identifier", }, VPNGatewayAddress: "IPV4-address-of-the-vpn-gateway", }, }, VirtualNetworkSites: &[]VirtualNetworkSite{ { Name: "virtual-network-name", AffinityGroup: "affinity-group-name", AddressSpacePrefixes: []string{ "CIDR-identifier", }, Subnets: &[]Subnet{ { Name: "subnet-name", AddressPrefix: "CIDR-identifier", }, }, DnsServersRef: &[]DnsServerRef{ { Name: "primary-DNS-name", }, }, Gateway: &Gateway{ Profile: "Small", VPNClientAddressPoolPrefixes: []string{ "CIDR-identifier", }, LocalNetworkSiteRef: LocalNetworkSiteRef{ Name: "local-site-name", Connection: LocalNetworkSiteRefConnection{ Type: "connection-type", }, }, }, }, }, } observed, err := input.Serialize() c.Assert(err, IsNil) c.Assert(strings.TrimSpace(observed), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestNetworkConfigurationSerializeMinimal(c *C) { expected := fmt.Sprintf( "<NetworkConfiguration xmlns=\"%s\"></NetworkConfiguration>", XMLNS_NC) input := NetworkConfiguration{XMLNS: XMLNS_NC} observed, err := input.Serialize() c.Assert(err, IsNil) c.Assert(strings.TrimSpace(observed), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestNetworkConfigurationSerializeSimpleVirtualNetworkSite(c *C) { expected := dedent.Dedent(` <NetworkConfiguration xmlns="http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration"> <VirtualNetworkConfiguration> <VirtualNetworkSites> <VirtualNetworkSite name="virtual-network-name" AffinityGroup="affinity-group-name"> <AddressSpace> <AddressPrefix>CIDR-identifier</AddressPrefix> </AddressSpace> </VirtualNetworkSite> </VirtualNetworkSites> </VirtualNetworkConfiguration> </NetworkConfiguration>`) input := NetworkConfiguration{ XMLNS: XMLNS_NC, VirtualNetworkSites: &[]VirtualNetworkSite{ { Name: "virtual-network-name", AffinityGroup: "affinity-group-name", AddressSpacePrefixes: []string{ "CIDR-identifier", }, }, }, } observed, err := input.Serialize() c.Assert(err, IsNil) c.Assert(strings.TrimSpace(observed), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestCreateAffinityGroup(c *C) { expected := dedent.Dedent(` <CreateAffinityGroup xmlns="http://schemas.microsoft.com/windowsazure"> <Name>affinity-group-name</Name> <Label>base64-encoded-affinity-group-label</Label> <Description>affinity-group-description</Description> <Location>location</Location> </CreateAffinityGroup>`) input := CreateAffinityGroup{ XMLNS: XMLNS, Name: "affinity-group-name", Label: "base64-encoded-affinity-group-label", Description: "affinity-group-description", Location: "location"} observed, err := input.Serialize() c.Assert(err, IsNil) c.Assert(strings.TrimSpace(observed), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestNewCreateAffinityGroup(c *C) { name := "name" label := "label" description := "description" location := "location" ag := NewCreateAffinityGroup(name, label, description, location) base64label := base64.StdEncoding.EncodeToString([]byte(label)) c.Check(ag.XMLNS, Equals, XMLNS) c.Check(ag.Name, Equals, name) c.Check(ag.Label, Equals, base64label) c.Check(ag.Description, Equals, description) c.Check(ag.Location, Equals, location) } func (suite *xmlSuite) TestUpdateAffinityGroup(c *C) { expected := dedent.Dedent(` <UpdateAffinityGroup xmlns="http://schemas.microsoft.com/windowsazure"> <Label>base64-encoded-affinity-group-label</Label> <Description>affinity-group-description</Description> </UpdateAffinityGroup>`) input := UpdateAffinityGroup{ XMLNS: XMLNS, Label: "base64-encoded-affinity-group-label", Description: "affinity-group-description"} observed, err := input.Serialize() c.Assert(err, IsNil) c.Assert(strings.TrimSpace(observed), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestNewUpdateAffinityGroup(c *C) { label := "label" description := "description" ag := NewUpdateAffinityGroup(label, description) base64label := base64.StdEncoding.EncodeToString([]byte(label)) c.Check(ag.XMLNS, Equals, XMLNS) c.Check(ag.Label, Equals, base64label) c.Check(ag.Description, Equals, description) } func (suite *xmlSuite) TestNetworkConfigurationDeserialize(c *C) { // Template from // http://msdn.microsoft.com/en-us/library/windowsazure/jj157196.aspx input := ` <NetworkConfiguration xmlns="http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration"> <VirtualNetworkConfiguration> <Dns> <DnsServers> <DnsServer name="dns-server-name" IPAddress="IPV4-address-of-the-server"></DnsServer> </DnsServers> </Dns> <LocalNetworkSites> <LocalNetworkSite name="local-site-name"> <AddressSpace> <AddressPrefix>CIDR-identifier</AddressPrefix> </AddressSpace> <VPNGatewayAddress>IPV4-address-of-the-vpn-gateway</VPNGatewayAddress> </LocalNetworkSite> </LocalNetworkSites> <VirtualNetworkSites> <VirtualNetworkSite name="virtual-network-name" AffinityGroup="affinity-group-name"> <Label>label-for-the-site</Label> <AddressSpace> <AddressPrefix>CIDR-identifier</AddressPrefix> </AddressSpace> <Subnets> <Subnet name="subnet-name"> <AddressPrefix>CIDR-identifier</AddressPrefix> </Subnet> </Subnets> <DnsServersRef> <DnsServerRef name="primary-DNS-name"></DnsServerRef> </DnsServersRef> <Gateway profile="Small"> <VPNClientAddressPool> <AddressPrefix>CIDR-identifier</AddressPrefix> </VPNClientAddressPool> <ConnectionsToLocalNetwork> <LocalNetworkSiteRef name="local-site-name"> <Connection type="connection-type"></Connection> </LocalNetworkSiteRef> </ConnectionsToLocalNetwork> </Gateway> </VirtualNetworkSite> </VirtualNetworkSites> </VirtualNetworkConfiguration> </NetworkConfiguration>` expected := &NetworkConfiguration{ XMLNS: XMLNS_NC, DNS: &[]VirtualNetDnsServer{ { Name: "dns-server-name", IPAddress: "IPV4-address-of-the-server", }, }, LocalNetworkSites: &[]LocalNetworkSite{ { Name: "local-site-name", AddressSpacePrefixes: []string{ "CIDR-identifier", }, VPNGatewayAddress: "IPV4-address-of-the-vpn-gateway", }, }, VirtualNetworkSites: &[]VirtualNetworkSite{ { Name: "virtual-network-name", AffinityGroup: "affinity-group-name", AddressSpacePrefixes: []string{ "CIDR-identifier", }, Subnets: &[]Subnet{ { Name: "subnet-name", AddressPrefix: "CIDR-identifier", }, }, DnsServersRef: &[]DnsServerRef{ { Name: "primary-DNS-name", }, }, Gateway: &Gateway{ Profile: "Small", VPNClientAddressPoolPrefixes: []string{ "CIDR-identifier", }, LocalNetworkSiteRef: LocalNetworkSiteRef{ Name: "local-site-name", Connection: LocalNetworkSiteRefConnection{ Type: "connection-type", }, }, }, }, }, } networkConfig := &NetworkConfiguration{} err := networkConfig.Deserialize([]byte(input)) c.Assert(err, IsNil) // Check sub-components of the overall structure. c.Check(networkConfig.DNS, DeepEquals, expected.DNS) c.Check(networkConfig.LocalNetworkSites, DeepEquals, expected.LocalNetworkSites) c.Check(networkConfig.VirtualNetworkSites, DeepEquals, expected.VirtualNetworkSites) // Check the whole thing. c.Check(networkConfig, DeepEquals, expected) } func (suite *xmlSuite) TestDeployment(c *C) { deployment := makeDeployment() dns := deployment.DNS[0] role := deployment.RoleList[0] config := role.ConfigurationSets[0] xml, err := deployment.Serialize() c.Assert(err, IsNil) template := dedent.Dedent(` <Deployment xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <Name>%s</Name> <DeploymentSlot>%s</DeploymentSlot> <Label>%s</Label> <RoleInstanceList></RoleInstanceList> <RoleList> <Role> <RoleName>%s</RoleName> <RoleType>PersistentVMRole</RoleType> <ConfigurationSets> <ConfigurationSet> <ConfigurationSetType>%s</ConfigurationSetType> <HostName>%s</HostName> <UserName>%s</UserName> <UserPassword>%s</UserPassword> <CustomData>%s</CustomData> <DisableSshPasswordAuthentication>%v</DisableSshPasswordAuthentication> </ConfigurationSet> </ConfigurationSets> <RoleSize>%s</RoleSize> </Role> </RoleList> <VirtualNetworkName>%s</VirtualNetworkName> <Dns> <DnsServers> <DnsServer> <Name>%s</Name> <Address>%s</Address> </DnsServer> </DnsServers> </Dns> <ExtendedProperties></ExtendedProperties> </Deployment>`) expected := fmt.Sprintf(template, deployment.Name, deployment.DeploymentSlot, deployment.Label, role.RoleName, config.ConfigurationSetType, config.Hostname, config.Username, config.Password, config.CustomData, config.DisableSSHPasswordAuthentication, role.RoleSize, deployment.VirtualNetworkName, dns.Name, dns.Address) c.Check(strings.TrimSpace(xml), Equals, strings.TrimSpace(expected)) } // From http://msdn.microsoft.com/en-us/library/windowsazure/ee460804.aspx var deploymentXML = ` <?xml version="1.0" encoding="utf-8"?> <Deployment xmlns="http://schemas.microsoft.com/windowsazure"> <Name>name-of-deployment</Name> <DeploymentSlot>current-deployment-environment</DeploymentSlot> <PrivateID>deployment-id</PrivateID> <Status>status-of-deployment</Status> <Label>base64-encoded-name-of-deployment</Label> <Url>http://name-of-deployment.cloudapp.net</Url> <Configuration>base-64-encoded-configuration-file</Configuration> <RoleInstanceList> <RoleInstance> <RoleName>name-of-role</RoleName> <InstanceName>name-of-role-instance</InstanceName> <InstanceStatus>status-of-role-instance</InstanceStatus> <InstanceUpgradeDomain>update-domain-of-role-instance</InstanceUpgradeDomain> <InstanceFaultDomain>fault-domain-of-role-instance</InstanceFaultDomain> <InstanceSize>size-of-role-instance</InstanceSize> <InstanceStateDetails>state-of-role-instance</InstanceStateDetails> <InstanceErrorCode>error-code-returned-for-role-instance</InstanceErrorCode> <IpAddress>ip-address-of-role-instance</IpAddress> <InstanceEndpoints> <InstanceEndpoint> <Name>name-of-endpoint</Name> <Vip>virtual-ip-address-of-instance-endpoint</Vip> <PublicPort>1234</PublicPort> <LocalPort>5678</LocalPort> <Protocol>protocol-of-instance-endpoint</Protocol> </InstanceEndpoint> </InstanceEndpoints> <PowerState>state-of-role-instance</PowerState> <HostName>dns-name-of-service</HostName> <RemoteAccessCertificateThumbprint>cert-thumbprint-for-remote-access</RemoteAccessCertificateThumbprint> </RoleInstance> </RoleInstanceList> <UpgradeStatus> <UpgradeType>auto|manual</UpgradeType> <CurrentUpgradeDomainState>before|during</CurrentUpgradeDomainState> <CurrentUpgradeDomain>n</CurrentUpgradeDomain> </UpgradeStatus> <UpgradeDomainCount>number-of-upgrade-domains-in-deployment</UpgradeDomainCount> <RoleList> <Role> <RoleName>name-of-role</RoleName> <OsVersion>operating-system-version</OsVersion> <ConfigurationSets> <ConfigurationSet> <ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType> <DisableSshPasswordAuthentication>false</DisableSshPasswordAuthentication> </ConfigurationSet> </ConfigurationSets> </Role> <Role> <RoleName>name-of-role</RoleName> <OsVersion>operating-system-version</OsVersion> <RoleType>PersistentVMRole</RoleType> <ConfigurationSets> <ConfigurationSet> <ConfigurationSetType>NetworkConfiguration</ConfigurationSetType> <InputEndpoints> <InputEndpoint> <Port>2222</Port> <LocalPort>111</LocalPort> <Protocol>TCP</Protocol> <Name>test-name</Name> </InputEndpoint> </InputEndpoints> <SubnetNames> <SubnetName>name-of-subnet</SubnetName> </SubnetNames> </ConfigurationSet> </ConfigurationSets> <AvailabilitySetName>name-of-availability-set</AvailabilitySetName> <DataVirtualHardDisks> <DataVirtualHardDisk> <HostCaching>host-caching-mode-of-data-disk</HostCaching> <DiskName>name-of-data-disk</DiskName> <Lun>logical-unit-number-of-data-disk</Lun> <LogicalDiskSizeInGB>size-of-data-disk</LogicalDiskSizeInGB> <MediaLink>path-to-vhd</MediaLink> </DataVirtualHardDisk> </DataVirtualHardDisks> <OSVirtualHardDisk> <HostCaching>host-caching-mode-of-os-disk</HostCaching> <DiskName>name-of-os-disk</DiskName> <MediaLink>path-to-vhd</MediaLink> <SourceImageName>image-used-to-create-os-disk</SourceImageName> <OS>operating-system-on-os-disk</OS> </OSVirtualHardDisk> <RoleSize>size-of-instance</RoleSize> </Role> </RoleList> <SdkVersion>sdk-version-used-to-create-package</SdkVersion> <Locked>status-of-deployment-write-allowed</Locked> <RollbackAllowed>rollback-operation-allowed</RollbackAllowed> <VirtualNetworkName>name-of-virtual-network</VirtualNetworkName> <Dns> <DnsServers> <DnsServer> <Name>name-of-dns-server</Name> <Address>address-of-dns-server</Address> </DnsServer> </DnsServers> </Dns> <ExtendedProperties> <ExtendedProperty> <Name>name-of-property</Name> <Value>value-of-property</Value> </ExtendedProperty> </ExtendedProperties> <PersistentVMDowntime> <StartTime>start-of-downtime</StartTime> <EndTime>end-of-downtime</EndTime> <Status>status-of-downtime</Status> </PersistentVMDowntime> <VirtualIPs> <VirtualIP> <Address>virtual-ip-address-of-deployment</Address> </VirtualIP> </VirtualIPs> <ExtensionConfiguration> <AllRoles> <Extension> <Id>identifier-of-extension</Id> </Extension> ... </AllRoles> <NamedRoles> <Role> <RoleName>role_name1</RoleName> <Extensions> <Extension> <Id>identifier-of-extension</Id> </Extension> ... </Extensions> </Role> </NamedRoles> </ExtensionConfiguration> </Deployment> ` func (suite *xmlSuite) TestDeploymentWRTGetDeployment(c *C) { expected := &Deployment{ XMLNS: "http://schemas.microsoft.com/windowsazure", Name: "name-of-deployment", DeploymentSlot: "current-deployment-environment", PrivateID: "deployment-id", Status: "status-of-deployment", Label: "base64-encoded-name-of-deployment", URL: "http://name-of-deployment.cloudapp.net", Configuration: "base-64-encoded-configuration-file", RoleInstanceList: []RoleInstance{ { RoleName: "name-of-role", InstanceName: "name-of-role-instance", InstanceStatus: "status-of-role-instance", InstanceUpgradeDomain: "update-domain-of-role-instance", InstanceFaultDomain: "fault-domain-of-role-instance", InstanceSize: "size-of-role-instance", InstanceStateDetails: "state-of-role-instance", InstanceErrorCode: "error-code-returned-for-role-instance", IPAddress: "ip-address-of-role-instance", InstanceEndpoints: []InstanceEndpoint{ { Name: "name-of-endpoint", VIP: "virtual-ip-address-of-instance-endpoint", PublicPort: 1234, LocalPort: 5678, Protocol: "protocol-of-instance-endpoint", }, }, PowerState: "state-of-role-instance", HostName: "dns-name-of-service", RemoteAccessCertificateThumbprint: "cert-thumbprint-for-remote-access", }, }, UpgradeDomainCount: "number-of-upgrade-domains-in-deployment", RoleList: []Role{ { RoleName: "name-of-role", ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: "LinuxProvisioningConfiguration", DisableSSHPasswordAuthentication: "false", }, }, }, { RoleName: "name-of-role", RoleType: "PersistentVMRole", ConfigurationSets: []ConfigurationSet{ { ConfigurationSetType: CONFIG_SET_NETWORK, InputEndpoints: &[]InputEndpoint{ { Name: "test-name", Port: 2222, LocalPort: 111, Protocol: "TCP", }, }, SubnetNames: &[]string{"name-of-subnet"}, }, }, OSVirtualHardDisk: []OSVirtualHardDisk{ { HostCaching: "host-caching-mode-of-os-disk", DiskName: "name-of-os-disk", MediaLink: "path-to-vhd", SourceImageName: "image-used-to-create-os-disk", OS: "operating-system-on-os-disk", }, }, RoleSize: "size-of-instance", }, }, SDKVersion: "sdk-version-used-to-create-package", Locked: "status-of-deployment-write-allowed", RollbackAllowed: "rollback-operation-allowed", VirtualNetworkName: "name-of-virtual-network", DNS: []DnsServer{ { Name: "name-of-dns-server", Address: "address-of-dns-server", }, }, ExtendedProperties: []ExtendedProperty{ { Name: "name-of-property", Value: "value-of-property", }, }, } observed := &Deployment{} err := observed.Deserialize([]byte(deploymentXML)) c.Assert(err, IsNil) c.Assert(observed, DeepEquals, expected) } func (suite *xmlSuite) TestDeploymentGetFQDNExtractsFQDN(c *C) { deployment := &Deployment{} err := deployment.Deserialize([]byte(deploymentXML)) c.Assert(err, IsNil) fqdn, err := deployment.GetFQDN() c.Assert(err, IsNil) c.Assert(fqdn, Equals, "name-of-deployment.cloudapp.net") } var deploymentXMLEmptyURL = ` <?xml version="1.0" encoding="utf-8"?> <Deployment xmlns="http://schemas.microsoft.com/windowsazure"> <Name>name-of-deployment</Name> <Label>base64-encoded-name-of-deployment</Label> <Url></Url> </Deployment> ` func (suite *xmlSuite) TestDeploymentGetFQDNErrorsIfURLIsEmpty(c *C) { deployment := &Deployment{} err := deployment.Deserialize([]byte(deploymentXMLEmptyURL)) c.Assert(err, IsNil) _, err = deployment.GetFQDN() c.Check(err, ErrorMatches, ".*URL field is empty.*") } var deploymentXMLFaultyURL = ` <?xml version="1.0" encoding="utf-8"?> <Deployment xmlns="http://schemas.microsoft.com/windowsazure"> <Name>name-of-deployment</Name> <Label>base64-encoded-name-of-deployment</Label> <Url>%z</Url> </Deployment> ` func (suite *xmlSuite) TestDeploymentGetFQDNErrorsIfURLCannotBeParsed(c *C) { deployment := &Deployment{} err := deployment.Deserialize([]byte(deploymentXMLFaultyURL)) c.Assert(err, IsNil) _, err = deployment.GetFQDN() c.Check(err, ErrorMatches, ".*invalid URL.*") } func (suite *xmlSuite) TestNewDeploymentForCreateVMDeployment(c *C) { name := "deploymentName" deploymentSlot := "staging" label := "deploymentLabel" vhd := NewOSVirtualHardDisk("hostCaching", "diskLabel", "diskName", "mediaLink", "sourceImageName", "os") roles := []Role{*NewRole("size", "name", []ConfigurationSet{}, []OSVirtualHardDisk{*vhd})} virtualNetworkName := "network" deployment := NewDeploymentForCreateVMDeployment(name, deploymentSlot, label, roles, virtualNetworkName) c.Check(deployment.XMLNS, Equals, XMLNS) c.Check(deployment.XMLNS_I, Equals, XMLNS_I) c.Check(deployment.Name, Equals, name) c.Check(deployment.DeploymentSlot, Equals, deploymentSlot) c.Check(deployment.RoleList, DeepEquals, roles) decodedLabel, err := base64.StdEncoding.DecodeString(deployment.Label) c.Assert(err, IsNil) c.Check(string(decodedLabel), Equals, label) c.Check(deployment.VirtualNetworkName, Equals, virtualNetworkName) } func (suite *xmlSuite) TestCreateVirtualHardDiskMediaLinkHappyPath(c *C) { mediaLink := CreateVirtualHardDiskMediaLink("storage-name", "storage/path") c.Check(mediaLink, Equals, "http://storage-name.blob.core.windows.net/storage/path") } func (suite *xmlSuite) TestCreateVirtualHardDiskMediaLinkChecksParams(c *C) { c.Check( func() { CreateVirtualHardDiskMediaLink("foo^bar", "valid") }, PanicMatches, "'foo\\^bar' contains URI special characters") c.Check( func() { CreateVirtualHardDiskMediaLink("valid", "a/foo^bar/test") }, PanicMatches, "'foo\\^bar' contains URI special characters") } func (suite *xmlSuite) TestCreateStorageServiceInput(c *C) { s := makeCreateStorageServiceInput() extProperty := s.ExtendedProperties[0] xml, err := s.Serialize() c.Assert(err, IsNil) template := dedent.Dedent(` <CreateStorageServiceInput xmlns="http://schemas.microsoft.com/windowsazure"> <ServiceName>%s</ServiceName> <Label>%s</Label> <Description>%s</Description> <Location>%s</Location> <AffinityGroup>%s</AffinityGroup> <GeoReplicationEnabled>%s</GeoReplicationEnabled> <ExtendedProperties> <ExtendedProperty> <Name>%s</Name> <Value>%s</Value> </ExtendedProperty> </ExtendedProperties> </CreateStorageServiceInput>`) expected := fmt.Sprintf(template, s.ServiceName, s.Label, s.Description, s.Location, s.AffinityGroup, s.GeoReplicationEnabled, extProperty.Name, extProperty.Value) c.Assert(strings.TrimSpace(xml), Equals, strings.TrimSpace(expected)) } // // Tests for Unmarshallers // func (suite *xmlSuite) TestStorageServicesUnmarshal(c *C) { inputTemplate := ` <?xml version="1.0" encoding="utf-8"?> <StorageServices xmlns="http://schemas.microsoft.com/windowsazure"> <StorageService> <Url>%s</Url> <ServiceName>%s</ServiceName> <StorageServiceProperties> <Description>%s</Description> <AffinityGroup>%s</AffinityGroup> <Label>%s</Label> <Status>%s</Status> <Endpoints> <Endpoint>%s</Endpoint> <Endpoint>%s</Endpoint> <Endpoint>%s</Endpoint> </Endpoints> <GeoReplicationEnabled>%s</GeoReplicationEnabled> <GeoPrimaryRegion>%s</GeoPrimaryRegion> <StatusOfPrimary>%s</StatusOfPrimary> <LastGeoFailoverTime>%s</LastGeoFailoverTime> <GeoSecondaryRegion>%s</GeoSecondaryRegion> <StatusOfSecondary>%s</StatusOfSecondary> <ExtendedProperties> <ExtendedProperty> <Name>%s</Name> <Value>%s</Value> </ExtendedProperty> <ExtendedProperty> <Name>%s</Name> <Value>%s</Value> </ExtendedProperty> </ExtendedProperties> </StorageServiceProperties> </StorageService> </StorageServices>` url := MakeRandomString(10) servicename := MakeRandomString(10) desc := MakeRandomString(10) affinity := MakeRandomString(10) label := MakeRandomString(10) status := MakeRandomString(10) blobEndpoint := MakeRandomString(10) queueEndpoint := MakeRandomString(10) tableEndpoint := MakeRandomString(10) geoRepl := BoolToString(MakeRandomBool()) geoRegion := MakeRandomString(10) statusPrimary := MakeRandomString(10) failoverTime := MakeRandomString(10) geoSecRegion := MakeRandomString(10) statusSec := MakeRandomString(10) p1Name := MakeRandomString(10) p1Val := MakeRandomString(10) p2Name := MakeRandomString(10) p2Val := MakeRandomString(10) input := fmt.Sprintf(inputTemplate, url, servicename, desc, affinity, label, status, blobEndpoint, queueEndpoint, tableEndpoint, geoRepl, geoRegion, statusPrimary, failoverTime, geoSecRegion, statusSec, p1Name, p1Val, p2Name, p2Val) data := []byte(input) services := &StorageServices{} err := services.Deserialize(data) c.Assert(err, IsNil) c.Check(len(services.StorageServices), Equals, 1) s := services.StorageServices[0] // Oh jeez, here we go.... c.Check(s.URL, Equals, url) c.Check(s.ServiceName, Equals, servicename) c.Check(s.Description, Equals, desc) c.Check(s.AffinityGroup, Equals, affinity) c.Check(s.Label, Equals, label) c.Check(s.Status, Equals, status) c.Check(s.GeoReplicationEnabled, Equals, geoRepl) c.Check(s.GeoPrimaryRegion, Equals, geoRegion) c.Check(s.StatusOfPrimary, Equals, statusPrimary) c.Check(s.LastGeoFailoverTime, Equals, failoverTime) c.Check(s.GeoSecondaryRegion, Equals, geoSecRegion) c.Check(s.StatusOfSecondary, Equals, statusSec) endpoints := s.Endpoints c.Check(len(endpoints), Equals, 3) c.Check(endpoints[0], Equals, blobEndpoint) c.Check(endpoints[1], Equals, queueEndpoint) c.Check(endpoints[2], Equals, tableEndpoint) properties := s.ExtendedProperties c.Check(properties[0].Name, Equals, p1Name) c.Check(properties[0].Value, Equals, p1Val) c.Check(properties[1].Name, Equals, p2Name) c.Check(properties[1].Value, Equals, p2Val) } func (suite *xmlSuite) TestBlobEnumerationResuts(c *C) { input := ` <?xml version="1.0" encoding="utf-8"?> <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer"> <Prefix>prefix</Prefix> <Marker>marker</Marker> <MaxResults>maxresults</MaxResults> <Delimiter>delimiter</Delimiter> <Blobs> <Blob> <Name>blob-name</Name> <Snapshot>snapshot-date-time</Snapshot> <Url>blob-address</Url> <Properties> <Last-Modified>last-modified</Last-Modified> <Etag>etag</Etag> <Content-Length>size-in-bytes</Content-Length> <Content-Type>blob-content-type</Content-Type> <Content-Encoding /> <Content-Language /> <Content-MD5 /> <Cache-Control /> <x-ms-blob-sequence-number>sequence-number</x-ms-blob-sequence-number> <BlobType>blobtype</BlobType> <LeaseStatus>leasestatus</LeaseStatus> <LeaseState>leasestate</LeaseState> <LeaseDuration>leasesduration</LeaseDuration> <CopyId>id</CopyId> <CopyStatus>copystatus</CopyStatus> <CopySource>copysource</CopySource> <CopyProgress>copyprogress</CopyProgress> <CopyCompletionTime>copycompletiontime</CopyCompletionTime> <CopyStatusDescription>copydesc</CopyStatusDescription> </Properties> <Metadata> <MetaName1>metadataname1</MetaName1> <MetaName2>metadataname2</MetaName2> </Metadata> </Blob> <BlobPrefix> <Name>blob-prefix</Name> </BlobPrefix> </Blobs> <NextMarker /> </EnumerationResults>` data := []byte(input) r := &BlobEnumerationResults{} err := r.Deserialize(data) c.Assert(err, IsNil) c.Check(r.ContainerName, Equals, "http://myaccount.blob.core.windows.net/mycontainer") c.Check(r.Prefix, Equals, "prefix") c.Check(r.Marker, Equals, "marker") c.Check(r.MaxResults, Equals, "maxresults") c.Check(r.Delimiter, Equals, "delimiter") c.Check(r.NextMarker, Equals, "") b := r.Blobs[0] c.Check(b.Name, Equals, "blob-name") c.Check(b.Snapshot, Equals, "snapshot-date-time") c.Check(b.URL, Equals, "blob-address") c.Check(b.LastModified, Equals, "last-modified") c.Check(b.ETag, Equals, "etag") c.Check(b.ContentLength, Equals, "size-in-bytes") c.Check(b.ContentType, Equals, "blob-content-type") c.Check(b.BlobSequenceNumber, Equals, "sequence-number") c.Check(b.BlobType, Equals, "blobtype") c.Check(b.LeaseStatus, Equals, "leasestatus") c.Check(b.LeaseState, Equals, "leasestate") c.Check(b.LeaseDuration, Equals, "leasesduration") c.Check(b.CopyID, Equals, "id") c.Check(b.CopyStatus, Equals, "copystatus") c.Check(b.CopySource, Equals, "copysource") c.Check(b.CopyProgress, Equals, "copyprogress") c.Check(b.CopyCompletionTime, Equals, "copycompletiontime") c.Check(b.CopyStatusDescription, Equals, "copydesc") m1 := b.Metadata.Items[0] m2 := b.Metadata.Items[1] c.Check(m1.Name(), Equals, "MetaName1") c.Check(m1.Value, Equals, "metadataname1") c.Check(m2.Name(), Equals, "MetaName2") c.Check(m2.Value, Equals, "metadataname2") prefix := r.BlobPrefixes[0] c.Check(prefix, Equals, "blob-prefix") } func (suite *xmlSuite) TestStorageAccountKeysUnmarshal(c *C) { template := ` <?xml version="1.0" encoding="utf-8"?> <StorageService xmlns="http://schemas.microsoft.com/windowsazure"> <Url>%s</Url> <StorageServiceKeys> <Primary>%s</Primary> <Secondary>%s</Secondary> </StorageServiceKeys> </StorageService>` url := MakeRandomString(10) key1 := MakeRandomString(10) key2 := MakeRandomString(10) input := fmt.Sprintf(template, url, key1, key2) data := []byte(input) keys := &StorageAccountKeys{} err := keys.Deserialize(data) c.Assert(err, IsNil) c.Check(keys.URL, Equals, url) c.Check(keys.Primary, Equals, key1) c.Check(keys.Secondary, Equals, key2) } // Tests for object factory functions. func (suite *xmlSuite) TestNewRole(c *C) { rolesize := MakeRandomString(10) rolename := MakeRandomString(10) config := makeLinuxProvisioningConfiguration() configset := []ConfigurationSet{*config} vhd := NewOSVirtualHardDisk("hostCaching", "diskLabel", "diskName", "mediaLink", "sourceImageName", "os") role := NewRole(rolesize, rolename, configset, []OSVirtualHardDisk{*vhd}) c.Check(role.RoleSize, Equals, rolesize) c.Check(role.RoleName, Equals, rolename) c.Check(role.ConfigurationSets, DeepEquals, configset) c.Check(role.RoleType, Equals, "PersistentVMRole") } func (suite *xmlSuite) TestNewLinuxProvisioningConfiguration(c *C) { hostname := MakeRandomString(10) username := MakeRandomString(10) password := MakeRandomString(10) disablessh := BoolToString(MakeRandomBool()) customdata := MakeRandomString(10) config := NewLinuxProvisioningConfigurationSet( hostname, username, password, customdata, disablessh) c.Check(config.Hostname, Equals, hostname) c.Check(config.Username, Equals, username) c.Check(config.Password, Equals, password) c.Check(config.CustomData, Equals, customdata) c.Check(config.DisableSSHPasswordAuthentication, Equals, disablessh) c.Check(config.ConfigurationSetType, Equals, "LinuxProvisioningConfiguration") } func (suite *xmlSuite) TestNewNetworkConfiguration(c *C) { name := "name1" port := 242 localPort := 922 protocol := "TCP" bName := "bname1" inputendpoint := InputEndpoint{ LoadBalancedEndpointSetName: bName, LocalPort: localPort, Name: name, Port: port, Protocol: protocol} config := NewNetworkConfigurationSet([]InputEndpoint{inputendpoint}, []string{"subnet-1", "subnet-2"}) inputEndpoints := *config.InputEndpoints c.Check(inputEndpoints, HasLen, 1) inputEndpoint := inputEndpoints[0] c.Check(inputEndpoint.Name, Equals, name) c.Check(inputEndpoint.Port, Equals, port) c.Check(inputEndpoint.Protocol, Equals, protocol) c.Check(inputEndpoint.LoadBalancedEndpointSetName, Equals, bName) c.Check(inputEndpoint.LocalPort, Equals, localPort) c.Check(config.ConfigurationSetType, Equals, CONFIG_SET_NETWORK) c.Check(*config.SubnetNames, DeepEquals, []string{"subnet-1", "subnet-2"}) } func (suite *xmlSuite) TestNewOSVirtualHardDisk(c *C) { var hostcaching HostCachingType = "ReadWrite" disklabel := MakeRandomString(10) diskname := MakeRandomString(10) MediaLink := MakeRandomString(10) SourceImageName := MakeRandomString(10) OS := MakeRandomString(10) disk := NewOSVirtualHardDisk( hostcaching, disklabel, diskname, MediaLink, SourceImageName, OS) c.Check(disk.HostCaching, Equals, string(hostcaching)) c.Check(disk.DiskLabel, Equals, disklabel) c.Check(disk.DiskName, Equals, diskname) c.Check(disk.MediaLink, Equals, MediaLink) c.Check(disk.SourceImageName, Equals, SourceImageName) c.Check(disk.OS, Equals, OS) } // Properties XML subtree for ListContainers Storage API call. func (suite *xmlSuite) TestProperties(c *C) { input := ` <?xml version="1.0" encoding="utf-8"?> <Properties> <Last-Modified>date/time-value</Last-Modified> <Etag>etag-value</Etag> <LeaseStatus>lease-status-value</LeaseStatus> <LeaseState>lease-state-value</LeaseState> <LeaseDuration>lease-duration-value</LeaseDuration> </Properties>` observed := &Properties{} err := xml.Unmarshal([]byte(input), observed) c.Assert(err, IsNil) expected := &Properties{ LastModified: "date/time-value", ETag: "etag-value", LeaseStatus: "lease-status-value", LeaseState: "lease-state-value", LeaseDuration: "lease-duration-value", } c.Assert(observed, DeepEquals, expected) } // Metadata XML subtree for ListContainers Storage API call. func (suite *xmlSuite) TestMetadata(c *C) { input := ` <?xml version="1.0" encoding="utf-8"?> <Metadata> <metadata-name>metadata-value</metadata-name> </Metadata>` observed := &Metadata{} err := xml.Unmarshal([]byte(input), observed) c.Assert(err, IsNil) expected := &Metadata{ Items: []MetadataItem{ { XMLName: xml.Name{Local: "metadata-name"}, Value: "metadata-value", }, }, } c.Assert(observed, DeepEquals, expected) } // Container XML subtree for ListContainers Storage API call. func (suite *xmlSuite) TestContainer(c *C) { input := ` <?xml version="1.0" encoding="utf-8"?> <Container> <Name>name-value</Name> <URL>url-value</URL> <Properties> <Last-Modified>date/time-value</Last-Modified> <Etag>etag-value</Etag> <LeaseStatus>lease-status-value</LeaseStatus> <LeaseState>lease-state-value</LeaseState> <LeaseDuration>lease-duration-value</LeaseDuration> </Properties> <Metadata> <metadata-name>metadata-value</metadata-name> </Metadata> </Container>` observed := &Container{} err := xml.Unmarshal([]byte(input), observed) c.Assert(err, IsNil) expected := &Container{ XMLName: xml.Name{Local: "Container"}, Name: "name-value", URL: "url-value", Properties: Properties{ LastModified: "date/time-value", ETag: "etag-value", LeaseStatus: "lease-status-value", LeaseState: "lease-state-value", LeaseDuration: "lease-duration-value", }, Metadata: Metadata{ Items: []MetadataItem{ { XMLName: xml.Name{Local: "metadata-name"}, Value: "metadata-value", }, }, }, } c.Assert(observed, DeepEquals, expected) } // EnumerationResults XML tree for ListContainers Storage API call. func (suite *xmlSuite) TestContainerEnumerationResults(c *C) { input := ` <?xml version="1.0" encoding="utf-8"?> <EnumerationResults AccountName="http://myaccount.blob.core.windows.net"> <Prefix>prefix-value</Prefix> <Marker>marker-value</Marker> <MaxResults>max-results-value</MaxResults> <Containers> <Container> <Name>name-value</Name> <URL>url-value</URL> <Properties> <Last-Modified>date/time-value</Last-Modified> <Etag>etag-value</Etag> <LeaseStatus>lease-status-value</LeaseStatus> <LeaseState>lease-state-value</LeaseState> <LeaseDuration>lease-duration-value</LeaseDuration> </Properties> <Metadata> <metadata-name>metadata-value</metadata-name> </Metadata> </Container> </Containers> <NextMarker>next-marker-value</NextMarker> </EnumerationResults>` observed := &ContainerEnumerationResults{} err := observed.Deserialize([]byte(input)) c.Assert(err, IsNil) expected := &ContainerEnumerationResults{ XMLName: xml.Name{Local: "EnumerationResults"}, Prefix: "prefix-value", Marker: "marker-value", MaxResults: "max-results-value", Containers: []Container{ { XMLName: xml.Name{Local: "Container"}, Name: "name-value", URL: "url-value", Properties: Properties{ LastModified: "date/time-value", ETag: "etag-value", LeaseStatus: "lease-status-value", LeaseState: "lease-state-value", LeaseDuration: "lease-duration-value", }, Metadata: Metadata{ Items: []MetadataItem{ { XMLName: xml.Name{Local: "metadata-name"}, Value: "metadata-value", }, }, }, }, }, NextMarker: "next-marker-value", } c.Assert(observed, DeepEquals, expected) c.Assert(observed.Containers[0].Metadata.Items[0].Name(), Equals, "metadata-name") } func (suite *xmlSuite) TestHostedService(c *C) { input := ` <?xml version="1.0" encoding="utf-8"?> <HostedService xmlns="http://schemas.microsoft.com/windowsazure"> <Url>hosted-service-url</Url> <ServiceName>hosted-service-name</ServiceName> <HostedServiceProperties> <Description>description</Description> <AffinityGroup>name-of-affinity-group</AffinityGroup> <Location>location-of-service</Location > <Label>base-64-encoded-name-of-service</Label> <Status>current-status-of-service</Status> <DateCreated>creation-date-of-service</DateCreated> <DateLastModified>last-modification-date-of-service</DateLastModified> <ExtendedProperties> <ExtendedProperty> <Name>name-of-property</Name> <Value>value-of-property</Value> </ExtendedProperty> </ExtendedProperties> </HostedServiceProperties> <Deployments> <Deployment xmlns="http://schemas.microsoft.com/windowsazure"> <Name>name-of-deployment</Name> </Deployment> </Deployments> </HostedService> ` expected := &HostedService{ XMLNS: "http://schemas.microsoft.com/windowsazure", HostedServiceDescriptor: HostedServiceDescriptor{ URL: "hosted-service-url", ServiceName: "hosted-service-name", Description: "description", AffinityGroup: "name-of-affinity-group", Location: "location-of-service", Label: "base-64-encoded-name-of-service", Status: "current-status-of-service", DateCreated: "creation-date-of-service", DateLastModified: "last-modification-date-of-service", ExtendedProperties: []ExtendedProperty{ { Name: "name-of-property", Value: "value-of-property", }, }}, Deployments: []Deployment{{ XMLNS: "http://schemas.microsoft.com/windowsazure", Name: "name-of-deployment", }}, } observed := &HostedService{} err := observed.Deserialize([]byte(input)) c.Assert(err, IsNil) c.Assert(observed, DeepEquals, expected) } func makeHostedServiceDescriptorList(url string) string { input := ` <?xml version="1.0" encoding="utf-8"?> <HostedServices xmlns="http://schemas.microsoft.com/windowsazure"> <HostedService> <Url>%s</Url> <ServiceName>hosted-service-name</ServiceName> <HostedServiceProperties> <Description>description</Description> <AffinityGroup>affinity-group</AffinityGroup> <Location>service-location</Location> <Label>label</Label> <Status>status</Status> <DateCreated>date-created</DateCreated> <DateLastModified>date-modified</DateLastModified> <ExtendedProperties> <ExtendedProperty> <Name>property-name</Name> <Value>property-value</Value> </ExtendedProperty> </ExtendedProperties> </HostedServiceProperties> </HostedService> </HostedServices> ` return fmt.Sprintf(input, url) } func (suite *xmlSuite) TestHostedServiceDescriptorList(c *C) { input := makeHostedServiceDescriptorList("hosted-service-address") expected := &HostedServiceDescriptorList{ XMLName: xml.Name{ Space: "http://schemas.microsoft.com/windowsazure", Local: "HostedServices"}, XMLNS: "http://schemas.microsoft.com/windowsazure", HostedServices: []HostedServiceDescriptor{ { URL: "hosted-service-address", ServiceName: "hosted-service-name", Description: "description", AffinityGroup: "affinity-group", Location: "service-location", Label: "label", Status: "status", DateCreated: "date-created", DateLastModified: "date-modified", ExtendedProperties: []ExtendedProperty{ { Name: "property-name", Value: "property-value", }, }, }, }, } observed := &HostedServiceDescriptorList{} err := observed.Deserialize([]byte(input)) c.Assert(err, IsNil) c.Assert(observed, DeepEquals, expected) } func (suite *xmlSuite) TestHostedServiceDescriptorGetLabel(c *C) { serviceDesc := HostedServiceDescriptor{Label: ""} label := MakeRandomString(10) base64Label := base64.StdEncoding.EncodeToString([]byte(label)) serviceDesc.Label = base64Label decodedLabel, err := serviceDesc.GetLabel() c.Assert(err, IsNil) c.Check(decodedLabel, DeepEquals, label) } // TestCreateStorageService demonstrates that CreateHostedService is a // suitable container for the CreateHostedService XML tree that are required // for the Create Hosted Service API call. func (suite *xmlSuite) TestCreateHostedService(c *C) { // From http://msdn.microsoft.com/en-us/library/windowsazure/gg441304.aspx input := ` <?xml version="1.0" encoding="utf-8"?> <CreateHostedService xmlns="http://schemas.microsoft.com/windowsazure"> <ServiceName>service-name</ServiceName> <Label>base64-encoded-service-label</Label> <Description>description</Description> <Location>location</Location> <AffinityGroup>affinity-group</AffinityGroup> <ExtendedProperties> <ExtendedProperty> <Name>property-name</Name> <Value>property-value</Value> </ExtendedProperty> </ExtendedProperties> </CreateHostedService> ` expected := &CreateHostedService{ XMLNS: XMLNS, ServiceName: "service-name", Label: "base64-encoded-service-label", Description: "description", Location: "location", AffinityGroup: "affinity-group", ExtendedProperties: []ExtendedProperty{ { Name: "property-name", Value: "property-value", }, }, } observed := &CreateHostedService{} err := observed.Deserialize([]byte(input)) c.Assert(err, IsNil) c.Assert(observed, DeepEquals, expected) } func (suite *xmlSuite) TestNewCreateHostedServiceWithLocation(c *C) { serviceName := "serviceName" label := "label" location := "location" createdHostedService := NewCreateHostedServiceWithLocation(serviceName, label, location) base64label := base64.StdEncoding.EncodeToString([]byte(label)) c.Check(createdHostedService.ServiceName, DeepEquals, serviceName) c.Check(createdHostedService.Label, DeepEquals, base64label) c.Check(createdHostedService.Location, DeepEquals, location) } func (suite *xmlSuite) TestNewCreateStorageServiceInputWithLocation(c *C) { cssi := NewCreateStorageServiceInputWithLocation("name", "label", "location", "false") c.Check(cssi.XMLNS, Equals, XMLNS) c.Check(cssi.ServiceName, Equals, "name") c.Check(cssi.Label, Equals, base64.StdEncoding.EncodeToString([]byte("label"))) c.Check(cssi.Location, Equals, "location") c.Check(cssi.GeoReplicationEnabled, Equals, "false") } func (*xmlSuite) TestAvailabilityResponse(c *C) { input := ` <?xml version="1.0" encoding="utf-8"?> <AvailabilityResponse xmlns="http://schemas.microsoft.com/windowsazure"> <Result>name-availability</Result> <Reason>reason</Reason> </AvailabilityResponse>` expected := &AvailabilityResponse{ XMLNS: XMLNS, Result: "name-availability", Reason: "reason", } observed := &AvailabilityResponse{} err := observed.Deserialize([]byte(input)) c.Assert(err, IsNil) c.Assert(observed, DeepEquals, expected) } func makeUpdateHostedService(label, description string, property ExtendedProperty) string { template := dedent.Dedent(` <UpdateHostedService xmlns="http://schemas.microsoft.com/windowsazure"> <Label>%s</Label> <Description>%s</Description> <ExtendedProperties> <ExtendedProperty> <Name>%s</Name> <Value>%s</Value> </ExtendedProperty> </ExtendedProperties> </UpdateHostedService>`) return fmt.Sprintf(template, label, description, property.Name, property.Value) } func (suite *xmlSuite) TestUpdateHostedService(c *C) { label := MakeRandomString(10) description := MakeRandomString(10) property := ExtendedProperty{ Name: "property-name", Value: "property-value", } expected := makeUpdateHostedService(label, description, property) input := UpdateHostedService{ XMLNS: XMLNS, Label: label, Description: description, ExtendedProperties: []ExtendedProperty{ property, }, } observed, err := input.Serialize() c.Assert(err, IsNil) c.Assert(strings.TrimSpace(observed), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestNewUpdateHostedService(c *C) { label := MakeRandomString(10) description := MakeRandomString(10) properties := []ExtendedProperty{ { Name: MakeRandomString(10), Value: MakeRandomString(10), }, } updateHostedService := NewUpdateHostedService( label, description, properties) c.Check( updateHostedService.Label, Equals, base64.StdEncoding.EncodeToString([]byte(label))) c.Check(updateHostedService.Description, Equals, description) c.Check(updateHostedService.ExtendedProperties, DeepEquals, properties) } func (suite *xmlSuite) TestBlockListSerialize(c *C) { blockList := &BlockList{ XMLName: xml.Name{Local: "BlockList"}, } blockList.Add(BlockListCommitted, "first-base64-encoded-block-id") blockList.Add(BlockListUncommitted, "second-base64-encoded-block-id") blockList.Add(BlockListLatest, "third-base64-encoded-block-id") observed, err := blockList.Serialize() c.Assert(err, IsNil) expected := dedent.Dedent(` <BlockList> <Committed>Zmlyc3QtYmFzZTY0LWVuY29kZWQtYmxvY2staWQ=</Committed> <Uncommitted>c2Vjb25kLWJhc2U2NC1lbmNvZGVkLWJsb2NrLWlk</Uncommitted> <Latest>dGhpcmQtYmFzZTY0LWVuY29kZWQtYmxvY2staWQ=</Latest> </BlockList>`) c.Assert(strings.TrimSpace(string(observed)), Equals, strings.TrimSpace(expected)) } func (suite *xmlSuite) TestGetBlockListDeserialize(c *C) { input := ` <?xml version="1.0" encoding="utf-8"?> <BlockList> <CommittedBlocks> <Block> <Name>BlockId001</Name> <Size>4194304</Size> </Block> </CommittedBlocks> <UncommittedBlocks> <Block> <Name>BlockId002</Name> <Size>1024</Size> </Block> </UncommittedBlocks> </BlockList>` observed := GetBlockList{} err := observed.Deserialize([]byte(input)) c.Assert(err, IsNil) expected := GetBlockList{ XMLName: xml.Name{Local: "BlockList"}, CommittedBlocks: []Block{ { Name: "BlockId001", Size: "4194304"}, }, UncommittedBlocks: []Block{ { Name: "BlockId002", Size: "1024"}, }, } c.Check(observed, DeepEquals, expected) } func makeOperationXML(operationType string) string { XMLTemplate := dedent.Dedent(` <%s xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <OperationType>%s</OperationType> </%s>`) return fmt.Sprintf(XMLTemplate, operationType, operationType, operationType) } func (suite *managementAPISuite) TestStartRoleOperation(c *C) { expectedXML := makeOperationXML("StartRoleOperation") xml, err := marshalXML(startRoleOperation) c.Assert(err, IsNil) c.Check(strings.TrimSpace(string(xml)), Equals, strings.TrimSpace(expectedXML)) } func (suite *managementAPISuite) TestRestartRoleOperation(c *C) { expectedXML := makeOperationXML("RestartRoleOperation") xml, err := marshalXML(restartRoleOperation) c.Assert(err, IsNil) c.Check(strings.TrimSpace(string(xml)), Equals, strings.TrimSpace(expectedXML)) } func (suite *managementAPISuite) TestShutdownRoleOperation(c *C) { expectedXML := makeOperationXML("ShutdownRoleOperation") xml, err := marshalXML(shutdownRoleOperation) c.Assert(err, IsNil) c.Check(strings.TrimSpace(string(xml)), Equals, strings.TrimSpace(expectedXML)) } // TestOSImageWRTAddOSImage demonstrates the OSImage is a suitable container // for the OSImage XML trees that are required for the Add OS Image API call. func (suite *xmlSuite) TestOSImageWRTAddOSImage(c *C) { // From http://msdn.microsoft.com/en-us/library/windowsazure/jj157192.aspx input := ` <OSImage xmlns="http://schemas.microsoft.com/windowsazure"> <Label>image-label</Label> <MediaLink>uri-of-the-containing-blob</MediaLink> <Name>image-name</Name> <OS>Linux|Windows</OS> <Eula>image-eula</Eula> <Description>image-description</Description> <ImageFamily>image-family</ImageFamily> <PublishedDate>published-date</PublishedDate> <IsPremium>true/false</IsPremium> <ShowInGui>true/false</ShowInGui> <PrivacyUri>http://www.example.com/privacypolicy.html</PrivacyUri> <IconUri>http://www.example.com/favicon.png</IconUri> <RecommendedVMSize>Small/Large/Medium/ExtraLarge</RecommendedVMSize> <SmallIconUri>http://www.example.com/smallfavicon.png</SmallIconUri> <Language>language-of-image</Language> </OSImage> ` expected := &OSImage{ Label: "image-label", MediaLink: "uri-of-the-containing-blob", Name: "image-name", OS: "Linux|Windows", EULA: "image-eula", Description: "image-description", ImageFamily: "image-family", PublishedDate: "published-date", IsPremium: "true/false", ShowInGUI: "true/false", PrivacyURI: "http://www.example.com/privacypolicy.html", IconURI: "http://www.example.com/favicon.png", RecommendedVMSize: "Small/Large/Medium/ExtraLarge", SmallIconURI: "http://www.example.com/smallfavicon.png", Language: "language-of-image", } osimage := &OSImage{} err := osimage.Deserialize([]byte(input)) c.Assert(err, IsNil) c.Assert(osimage, DeepEquals, expected) } // TestOSImageWRTListOSImages demonstrates that OSImage is a suitable // container for the OSImage XML subtrees that are returned from the List OS // Images API call. func (suite *xmlSuite) TestOSImageWRTListOSImages(c *C) { // From http://msdn.microsoft.com/en-us/library/windowsazure/jj157191.aspx input := ` <OSImage xmlns="http://schemas.microsoft.com/windowsazure"> <AffinityGroup>name-of-the-affinity-group</AffinityGroup> <Category>category-of-the-image</Category> <Label>image-description</Label> <Location>geo-location-of-the-stored-image</Location> <LogicalSizeInGB>123.456</LogicalSizeInGB> <MediaLink>url-of-the-containing-blob</MediaLink> <Name>image-name</Name> <OS>operating-system-of-the-image</OS> <Eula>image-eula</Eula> <Description>image-description</Description> <ImageFamily>image-family</ImageFamily> <ShowInGui>true|false</ShowInGui> <PublishedDate>published-date</PublishedDate> <IsPremium>true|false</IsPremium> <PrivacyUri>uri-of-privacy-policy</PrivacyUri> <RecommendedVMSize>size-of-the-virtual-machine</RecommendedVMSize> <PublisherName>publisher-identifier</PublisherName> <PricingDetailLink>pricing-details</PricingDetailLink> <SmallIconUri>uri-of-icon</SmallIconUri> <Language>language-of-image</Language> </OSImage> ` expected := &OSImage{ AffinityGroup: "name-of-the-affinity-group", Category: "category-of-the-image", Label: "image-description", Location: "geo-location-of-the-stored-image", LogicalSizeInGB: 123.456, MediaLink: "url-of-the-containing-blob", Name: "image-name", OS: "operating-system-of-the-image", EULA: "image-eula", Description: "image-description", ImageFamily: "image-family", ShowInGUI: "true|false", PublishedDate: "published-date", IsPremium: "true|false", PrivacyURI: "uri-of-privacy-policy", RecommendedVMSize: "size-of-the-virtual-machine", PublisherName: "publisher-identifier", PricingDetailLink: "pricing-details", SmallIconURI: "uri-of-icon", Language: "language-of-image", } osimage := &OSImage{} err := osimage.Deserialize([]byte(input)) c.Assert(err, IsNil) c.Assert(osimage, DeepEquals, expected) } // TestOSImageWRTUpdateOSImage demonstrates the OSImage is a suitable // container for the OSImage XML trees that are required for the Update OS // Image API call. func (suite *xmlSuite) TestOSImageWRTUpdateOSImage(c *C) { // From http://msdn.microsoft.com/en-us/library/windowsazure/jj157198.aspx input := ` <OSImage xmlns="http://schemas.microsoft.com/windowsazure"> <Label>image-label</Label> <Eula>image-eula</Eula> <Description>Image-Description</Description> <ImageFamily>Image-Family</ImageFamily> <PublishedDate>published-date</PublishedDate> <IsPremium>true/false</IsPremium> <ShowInGui>true/false</ShowInGui> <PrivacyUri>http://www.example.com/privacypolicy.html</PrivacyUri> <IconUri>http://www.example.com/favicon.png</IconUri> <RecommendedVMSize>Small/Large/Medium/ExtraLarge</RecommendedVMSize> <SmallIconUri>http://www.example.com/smallfavicon.png</SmallIconUri> <Language>language-of-image</Language> </OSImage> ` expected := &OSImage{ Label: "image-label", EULA: "image-eula", Description: "Image-Description", ImageFamily: "Image-Family", PublishedDate: "published-date", IsPremium: "true/false", ShowInGUI: "true/false", PrivacyURI: "http://www.example.com/privacypolicy.html", IconURI: "http://www.example.com/favicon.png", RecommendedVMSize: "Small/Large/Medium/ExtraLarge", SmallIconURI: "http://www.example.com/smallfavicon.png", Language: "language-of-image", } osimage := &OSImage{} err := osimage.Deserialize([]byte(input)) c.Assert(err, IsNil) c.Assert(osimage, DeepEquals, expected) } func (suite *xmlSuite) TestOSImageHasLocation(c *C) { image := &OSImage{ Location: "East Asia;Southeast Asia;North Europe;West Europe;East US;West US", } var testValues = []struct { location string expectedResult bool }{ {"East Asia", true}, {"West US", true}, {"Unknown location", false}, } for _, test := range testValues { c.Check(image.hasLocation(test.location), Equals, test.expectedResult) } } func (suite *xmlSuite) TestIsDailyBuild(c *C) { c.Check((&OSImage{Label: "Ubuntu Server 12.04.2 LTS DAILY"}).isDailyBuild(), Equals, true) c.Check((&OSImage{Label: "Ubuntu Server 12.04.2 LTS"}).isDailyBuild(), Equals, false) c.Check((&OSImage{Label: "Ubuntu Server 13.04"}).isDailyBuild(), Equals, false) } func (suite *xmlSuite) TestSortImages(c *C) { input := ` <?xml version="1.0"?> <Images xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <OSImage> <Label>Label 1</Label> <PublishedDate>2012-04-25T00:00:00Z</PublishedDate> </OSImage> <OSImage> <Label>Label 2</Label> <PublishedDate>2013-02-15T00:00:00Z</PublishedDate> </OSImage> <OSImage> <Label>Label 3</Label> <PublishedDate>2013-04-13T00:00:00Z</PublishedDate> </OSImage> <OSImage> <Label>Label 4</Label> <PublishedDate>2013-03-15T00:00:00Z</PublishedDate> </OSImage> </Images>` images := &Images{} err := images.Deserialize([]byte(input)) c.Assert(err, IsNil) sort.Sort(images) labels := []string{ (*images).Images[0].Label, (*images).Images[1].Label, (*images).Images[2].Label, (*images).Images[3].Label, } c.Check(labels, DeepEquals, []string{"Label 3", "Label 4", "Label 2", "Label 1"}) } func (suite *xmlSuite) TestGetLatestUbuntuImage(c *C) { // This is real-world XML input. input := ` <?xml version="1.0"?> <Images xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <OSImage> <Category>Canonical</Category> <Label>Ubuntu Server 12.04.2 LTS</Label> <Location>East Asia;Southeast Asia;North Europe;West Europe;East US;West US</Location> <LogicalSizeInGB>30</LogicalSizeInGB> <Name>b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-12_04_2-LTS-amd64-server-20130415-en-us-30GB</Name> <OS>Linux</OS> <ImageFamily>Ubuntu Server 12.04 LTS</ImageFamily> <PublishedDate>2013-04-15T00:00:00Z</PublishedDate> <IsPremium>false</IsPremium> <PublisherName>Canonical</PublisherName> </OSImage> <OSImage> <Category>Canonical</Category> <Label>Ubuntu Server 12.10</Label> <Location>East Asia;Southeast Asia;North Europe;West Europe;East US;West US</Location> <LogicalSizeInGB>30</LogicalSizeInGB> <Name>b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-12_10-amd64-server-20130414-en-us-30GB</Name> <OS>Linux</OS> <ImageFamily>Ubuntu 12.10</ImageFamily> <PublishedDate>2013-04-15T00:00:00Z</PublishedDate> <PublisherName>Canonical</PublisherName> </OSImage> <OSImage> <Category>Canonical</Category> <Label>Ubuntu Server 13.04 DAILY</Label> <Location>East Asia;Southeast Asia;North Europe;West Europe;East US;West US</Location> <LogicalSizeInGB>30</LogicalSizeInGB> <Name>fake-name__Ubuntu-13_04-amd64-server-20130423-en-us-30GB</Name> <OS>Linux</OS> <ImageFamily>Ubuntu Server 13.04</ImageFamily> <PublishedDate>2013-06-25T00:00:00Z</PublishedDate> <PublisherName>Canonical</PublisherName> </OSImage> <OSImage> <Category>Canonical</Category> <Label>Ubuntu Server 13.04</Label> <Location>East Asia;Southeast Asia;North Europe;West Europe;East US;West US</Location> <LogicalSizeInGB>30</LogicalSizeInGB> <Name>b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_04-amd64-server-20130423-en-us-30GB</Name> <OS>Linux</OS> <ImageFamily>Ubuntu Server 13.04</ImageFamily> <PublishedDate>2013-04-25T00:00:00Z</PublishedDate> <PublisherName>Canonical</PublisherName> </OSImage> <OSImage> <Category>Canonical</Category> <Label>Ubuntu Server 13.04 bogus publisher name</Label> <Location>East Asia;Southeast Asia;North Europe;West Europe;East US;West US</Location> <LogicalSizeInGB>30</LogicalSizeInGB> <Name>b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_04-amd64-server-20130423-en-us-30GB</Name> <OS>Linux</OS> <PublishedDate>2013-05-25T00:00:00Z</PublishedDate> <ImageFamily>Ubuntu Server 13.04</ImageFamily> <PublisherName>Bogus publisher name</PublisherName> </OSImage> </Images>` images := &Images{} err := images.Deserialize([]byte(input)) c.Assert(err, IsNil) var testValues = []struct { releaseName string location string expectedError error expectedLabel string }{ {"13.04", "West US", nil, "Ubuntu Server 13.04"}, {"12.04", "West US", nil, "Ubuntu Server 12.04.2 LTS"}, {"bogus-name", "Unknown location", fmt.Errorf("No matching images found"), ""}, } for _, test := range testValues { image, err := images.GetLatestUbuntuImage(test.releaseName, test.location) c.Check(err, DeepEquals, test.expectedError) if image != nil { c.Check(image.Label, Equals, test.expectedLabel) } } } func (suite *xmlSuite) TestOperation(c *C) { // From http://msdn.microsoft.com/en-us/library/windowsazure/ee460783.aspx input := ` <?xml version="1.0" encoding="utf-8"?> <Operation xmlns="http://schemas.microsoft.com/windowsazure"> <ID>request-id</ID> <Status>InProgress|Succeeded|Failed</Status> <!--Response includes HTTP status code only if the operation succeeded or failed --> <HttpStatusCode>200</HttpStatusCode> <!--Response includes additional error information only if the operation failed --> <Error> <Code>error-code</Code> <Message>error-message</Message> </Error> </Operation> ` expected := &Operation{ ID: "request-id", Status: "InProgress|Succeeded|Failed", HTTPStatusCode: 200, ErrorCode: "error-code", ErrorMessage: "error-message", } observed := &Operation{} err := observed.Deserialize([]byte(input)) c.Assert(err, IsNil) c.Assert(observed, DeepEquals, expected) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/storage.go�������������������������������������������������0000644�0000153�0000161�00000017115�12321735761�022061� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl // This file contains higher level operations necessary to work with the Azure // file storage API. import ( "bytes" "encoding/base64" "fmt" "io" . "launchpad.net/gwacl/logging" "strconv" "strings" ) // UploadBlockBlob uses PutBlock and PutBlockList API operations to upload // arbitrarily large files, 1MB at a time. func (context *StorageContext) UploadBlockBlob( container, filename string, data io.Reader) error { buffer := make([]byte, 1024*1024) // 1MB buffer blockList := &BlockList{} // Upload the file in chunks. for blockNum := int64(0); ; blockNum++ { blockSize, err := data.Read(buffer) if err == io.EOF { break } if err != nil { return err } block := bytes.NewReader(buffer[:blockSize]) blockID := strconv.FormatInt(blockNum, 36) // Block IDs must be a consistent length, so pad it out. blockID = fmt.Sprintf("%030s", blockID) Debugf("Uploading block %d (size=%d, id=%s).\n", blockNum, blockSize, blockID) err = context.PutBlock(container, filename, blockID, block) if err != nil { return err } blockList.Add(BlockListLatest, blockID) } // Commit those blocks by writing the block list. Debugf("Committing %d blocks.\n", len(blockList.Items)) return context.PutBlockList(container, filename, blockList) } // ListAllBlobs requests from the API a list of blobs in a container. func (context *StorageContext) ListAllBlobs(request *ListBlobsRequest) (*BlobEnumerationResults, error) { blobs := make([]Blob, 0) var batch *BlobEnumerationResults // Request the initial result, using the empty marker. Then, for as long // as the result has a nonempty NextMarker, request the next batch using // that marker. // This loop is very similar to the one in ListAllContainers(). for marker, nextMarker := "", "x"; nextMarker != ""; marker = nextMarker { var err error // Don't use := here or you'll shadow variables from the function's // outer scopes. request.Marker = marker batch, err = context.ListBlobs(request) if err != nil { return nil, err } // The response may contain a NextMarker field, to let us request a // subsequent batch of results. The XML parser won't trim whitespace out // of the marker tag, so we do that here. nextMarker = strings.TrimSpace(batch.NextMarker) blobs = append(blobs, batch.Blobs...) } // There's more in a BlobsEnumerationResults than just the blobs. // Return the latest batch, but give it the full cumulative blobs list // instead of just the last batch. // To the caller, this will look like they made one call to Azure's // List Blobs method, but batch size was unlimited. batch.Blobs = blobs return batch, nil } type DeleteAllBlobsRequest struct { Container string // Other params possible, add later. } // RemoveAllBlobs requests a deletion of all the blobs in a container. // The blobs are not deleted immediately, so when this call returns they // may still be present for a while. func (context *StorageContext) DeleteAllBlobs(request *DeleteAllBlobsRequest) error { blobs, err := context.ListAllBlobs(&ListBlobsRequest{ Container: request.Container}) if err != nil { return err } for _, blob := range blobs.Blobs { err := context.DeleteBlob(request.Container, blob.Name) if err != nil { return err } } return nil } // ListAllContainers requests from the storage service a list of containers // in the storage account. func (context *StorageContext) ListAllContainers() (*ContainerEnumerationResults, error) { containers := make([]Container, 0) var batch *ContainerEnumerationResults // Request the initial result, using the empty marker. Then, for as long // as the result has a nonempty NextMarker, request the next batch using // that marker. for marker, nextMarker := "", "x"; nextMarker != ""; marker = nextMarker { var err error // Don't use := here or you'll shadow variables from the function's // outer scopes. request := &ListContainersRequest{Marker: marker} batch, err = context.ListContainers(request) if err != nil { return nil, err } // The response may contain a NextMarker field, to let us request a // subsequent batch of results. The XML parser won't trim whitespace out // of the marker tag, so we do that here. nextMarker = strings.TrimSpace(batch.NextMarker) containers = append(containers, batch.Containers...) } // There's more in a ContainerEnumerationResults than just the containers. // Return the latest batch, but give it the full cumulative containers list // instead of just the last batch. // To the caller, this will look like they made one call to Azure's // List Containers method, but batch size was unlimited. batch.Containers = containers return batch, nil } type CreateVHDRequest struct { Container string // Container name in the storage account Filename string // Specify the filename in which to store the VHD FilesystemData io.Reader // A formatted filesystem, e.g. iso9660. Size int // How many bytes from the Filesystem data to upload. *Must* be a multiple of 512. } // CreateInstanceDataVHD will take the supplied filesystem data and create an // Azure VHD in a page blob out of it. The data cannot be bigger than // gwacl.VHD_SIZE-512. This is intended to be used as a way of passing // arbitrary data to a new instance - create a disk here and then attach it to // the new instance. func (context *StorageContext) CreateInstanceDataVHD(req *CreateVHDRequest) error { // We need several steps: // 1. Create an empty page blob of exactly VHD_SIZE bytes (see // vhd_footer.go) // 2. Upload VHD_FOOTER to the last page of the blob. // 3. Upload the supplied FilesystemData from the start of the blob. var err error if req.Size%512 != 0 { return fmt.Errorf("Size must be a multiple of 512") } if req.Size > VHD_SIZE-512 { // Protect against writing over the VHD footer. return fmt.Errorf("Size cannot be bigger than %d", VHD_SIZE-512) } // Step 1. err = context.PutBlob(&PutBlobRequest{ Container: req.Container, BlobType: "page", Filename: req.Filename, Size: VHD_SIZE, }) if err != nil { return err } // Step 2. data, err := base64.StdEncoding.DecodeString(VHD_FOOTER) if err != nil { // This really shouldn't ever happen since there's a test to make sure // it can be decoded. panic(err) } dataReader := bytes.NewReader(data) err = context.PutPage(&PutPageRequest{ Container: req.Container, Filename: req.Filename, StartRange: VHD_SIZE - 512, // last page of the blob EndRange: VHD_SIZE - 1, Data: dataReader, }) if err != nil { return err } // Step 3. err = context.PutPage(&PutPageRequest{ Container: req.Container, Filename: req.Filename, StartRange: 0, EndRange: req.Size - 1, Data: req.FilesystemData, }) if err != nil { return err } return nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/rolesizes.go�����������������������������������������������0000644�0000153�0000161�00000010043�12321735761�022425� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013-2014 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). // Define the role sizes available in Azure. package gwacl // RoleSize is a representation of the machine specs available in the Azure // documentation here: // http://msdn.microsoft.com/en-us/library/windowsazure/dn197896.aspx and // // Pricing from here: // http://www.windowsazure.com/en-us/pricing/details/cloud-services/ // // Detailed specifications here: // http://msdn.microsoft.com/en-us/library/windowsazure/dn197896.aspx // // Our specifications may be inaccurate or out of date. When in doubt, check! // // The Disk Space values are only the maxumim permitted; actual space is // determined by the OS image being used. // // Sizes and costs last updated 2014-01-31. type RoleSize struct { Name string CpuCores uint64 Mem uint64 // In MB OSDiskSpaceCloud uint64 // In MB OSDiskSpaceVirt uint64 // In MB MaxDataDisks uint64 // 1TB each Cost uint64 // USD cents per hour } const ( // MB is the unit in which we specify sizes, so it's 1. // But please include it anyway, so that units are always explicit. MB = 1 GB = 1024 * MB TB = 1024 * GB ) var RoleSizes = []RoleSize{ { Name: "ExtraSmall", CpuCores: 1, // shared Mem: 768 * MB, OSDiskSpaceCloud: 19 * GB, OSDiskSpaceVirt: 20 * GB, MaxDataDisks: 1, Cost: 2, }, { Name: "Small", CpuCores: 1, Mem: 1.75 * GB, OSDiskSpaceCloud: 224 * GB, OSDiskSpaceVirt: 70 * GB, MaxDataDisks: 2, Cost: 8, }, { Name: "Medium", CpuCores: 2, Mem: 3.5 * GB, OSDiskSpaceCloud: 489 * GB, OSDiskSpaceVirt: 135 * GB, MaxDataDisks: 4, Cost: 16, }, { Name: "Large", CpuCores: 4, Mem: 7 * GB, OSDiskSpaceCloud: 999 * GB, OSDiskSpaceVirt: 285 * GB, MaxDataDisks: 8, Cost: 32, }, { Name: "ExtraLarge", CpuCores: 8, Mem: 14 * GB, OSDiskSpaceCloud: 2039 * GB, OSDiskSpaceVirt: 605 * GB, MaxDataDisks: 16, Cost: 64, }, { Name: "A5", CpuCores: 2, Mem: 14 * GB, OSDiskSpaceCloud: 489 * GB, OSDiskSpaceVirt: 135 * GB, MaxDataDisks: 4, Cost: 35, }, { Name: "A6", CpuCores: 4, Mem: 28 * GB, OSDiskSpaceCloud: 999 * GB, OSDiskSpaceVirt: 285 * GB, MaxDataDisks: 8, Cost: 71, }, { Name: "A7", CpuCores: 8, Mem: 56 * GB, OSDiskSpaceCloud: 2039 * GB, OSDiskSpaceVirt: 605 * GB, MaxDataDisks: 16, Cost: 141, }, { Name: "A8", CpuCores: 8, Mem: 56 * GB, OSDiskSpaceCloud: 2039 * GB, // Estimate; not yet announced. OSDiskSpaceVirt: 605 * GB, // Estimate; not yet announced. MaxDataDisks: 16, // Estimate; not yet announced. Cost: 245, }, { Name: "A9", CpuCores: 16, Mem: 112 * GB, OSDiskSpaceCloud: 2039 * GB, // Estimate; not yet announced. OSDiskSpaceVirt: 605 * GB, // Estimate; not yet announced. MaxDataDisks: 16, // Estimate; not yet announced. Cost: 490, }, } var RoleNameMap map[string]RoleSize = make(map[string]RoleSize) func init() { for _, rolesize := range RoleSizes { RoleNameMap[rolesize.Name] = rolesize } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/management_base_test.go������������������������������������0000644�0000153�0000161�00000142472�12321736014�024557� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "encoding/base64" "encoding/xml" "errors" "fmt" "io/ioutil" . "launchpad.net/gocheck" "launchpad.net/gwacl/dedent" "launchpad.net/gwacl/fork/http" "net/url" "strings" "time" ) type managementBaseAPISuite struct { x509DispatcherFixture oldPollerInterval time.Duration } var _ = Suite(&managementBaseAPISuite{}) func makeX509ResponseWithOperationHeader(operationID string) *x509Response { header := http.Header{} header.Set(operationIDHeaderName, operationID) response := x509Response{ StatusCode: http.StatusAccepted, Header: header, } return &response } func makeAPI(c *C) *ManagementAPI { subscriptionId := "subscriptionId" api, err := NewManagementAPI(subscriptionId, "", "West US") c.Assert(err, IsNil) // Polling is disabled by default. api.PollerInterval = 0 return api } func (suite *managementBaseAPISuite) TestGetOperationIDExtractsHeader(c *C) { operationID := "operationID" response := makeX509ResponseWithOperationHeader(operationID) returnedOperationID, err := getOperationID(response) c.Assert(err, IsNil) c.Check(returnedOperationID, Equals, operationID) } func (suite *managementBaseAPISuite) TestBlockUntilCompletedSucceedsOnSyncSuccess(c *C) { // Set of expected error returns for success statuses. // (They all map to nil. It makes it easier further down to report // unexpected errors in a helpful way). expectedErrors := map[int]error{ http.StatusOK: nil, http.StatusCreated: nil, http.StatusNoContent: nil, } api := makeAPI(c) api.PollerInterval = time.Nanosecond receivedErrors := make(map[int]error) for status := range expectedErrors { err := api.blockUntilCompleted(&x509Response{StatusCode: status}) receivedErrors[status] = err } c.Check(receivedErrors, DeepEquals, expectedErrors) } func (suite *managementBaseAPISuite) TestBlockUntilCompletedReturnsHTTPErrorOnSyncFailure(c *C) { // Set of failure statuses, and whether they're supposed to return // HTTPError. // (They all map to true. It makes it easier further down to report // failures in a helpful way). expectedErrors := map[int]bool{ http.StatusBadRequest: true, http.StatusUnauthorized: true, http.StatusForbidden: true, http.StatusNotFound: true, http.StatusConflict: true, http.StatusInternalServerError: true, http.StatusNotImplemented: true, } api := makeAPI(c) api.PollerInterval = time.Nanosecond receivedErrors := make(map[int]bool) for status := range expectedErrors { err := api.blockUntilCompleted(&x509Response{StatusCode: status}) _, ok := err.(HTTPError) receivedErrors[status] = ok } c.Check(receivedErrors, DeepEquals, expectedErrors) } func (suite *managementBaseAPISuite) TestBlockUntilCompletedPollsOnAsyncOperation(c *C) { const operationID = "async-operation-id" // We set the dispatcher up for failure, and prove that // blockUntilCompleted() makes a polling request. failure := errors.New("Simulated failure") rigFailingDispatcher(failure) requests := make([]*X509Request, 0) rigRecordingDispatcher(&requests) api := makeAPI(c) api.PollerInterval = time.Nanosecond accepted := makeX509ResponseWithOperationHeader(operationID) err := api.blockUntilCompleted(accepted) c.Check(err, Equals, failure) c.Assert(len(requests), Equals, 1) requestURL := requests[0].URL urlParts := strings.Split(requestURL, "/") polledOperationID := urlParts[len(urlParts)-1] c.Check(polledOperationID, Equals, operationID) } func (suite *managementBaseAPISuite) TestBlockUntilCompletedErrorsIfAsyncOperationFails(c *C) { response := DispatcherResponse{ response: &x509Response{ Body: []byte(fmt.Sprintf(operationXMLTemplate, "Failed")), StatusCode: http.StatusOK, }, errorObject: nil} responses := []DispatcherResponse{response} rigPreparedResponseDispatcher(responses) operationID := "operationID" operationResponse := makeX509ResponseWithOperationHeader(operationID) api := makeAPI(c) api.PollerInterval = time.Nanosecond err := api.blockUntilCompleted(operationResponse) c.Check(err, ErrorMatches, ".*asynchronous operation failed.*") } func (suite *managementBaseAPISuite) TestBlockUntilCompletedErrorsOnInvalidXML(c *C) { response := DispatcherResponse{ response: &x509Response{ Body: []byte(">invalidXML<"), StatusCode: http.StatusOK, }, errorObject: nil} responses := []DispatcherResponse{response} rigPreparedResponseDispatcher(responses) operationID := "operationID" operationResponse := makeX509ResponseWithOperationHeader(operationID) api := makeAPI(c) api.PollerInterval = time.Nanosecond err := api.blockUntilCompleted(operationResponse) c.Check(err, FitsTypeOf, new(xml.SyntaxError)) } func (suite *managementBaseAPISuite) TestBlockUntilCompletedErrorsIfPollingFails(c *C) { response := DispatcherResponse{ response: &x509Response{}, errorObject: fmt.Errorf("unexpected error")} responses := []DispatcherResponse{response} rigPreparedResponseDispatcher(responses) operationID := "operationID" operationResponse := makeX509ResponseWithOperationHeader(operationID) api := makeAPI(c) api.PollerInterval = time.Nanosecond err := api.blockUntilCompleted(operationResponse) c.Check(err, ErrorMatches, ".*unexpected error.*") } func (suite *managementBaseAPISuite) TestBlockUntilCompletedErrorsCanTimeout(c *C) { response := &x509Response{ Body: []byte(fmt.Sprintf(operationXMLTemplate, InProgressOperationStatus)), StatusCode: http.StatusOK, } rigFixedResponseDispatcher(response) operationID := "operationID" operationResponse := makeX509ResponseWithOperationHeader(operationID) api := makeAPI(c) api.PollerInterval = time.Nanosecond api.PollerTimeout = 2 * time.Nanosecond err := api.blockUntilCompleted(operationResponse) c.Check(err, ErrorMatches, ".*polling timed out.*") } func (suite *managementBaseAPISuite) TestBlockUntilCompletedSucceedsIfAsyncOperationSucceeds(c *C) { response := DispatcherResponse{ response: &x509Response{ Body: []byte(fmt.Sprintf(operationXMLTemplate, "Succeeded")), StatusCode: http.StatusOK, }, errorObject: nil} responses := []DispatcherResponse{response} rigPreparedResponseDispatcher(responses) operationID := "operationID" operationResponse := makeX509ResponseWithOperationHeader(operationID) api := makeAPI(c) api.PollerInterval = time.Nanosecond err := api.blockUntilCompleted(operationResponse) c.Assert(err, IsNil) } var testCert = dedent.Dedent(` -----BEGIN PRIVATE KEY----- MIIBCgIBADANBgkqhkiG9w0BAQEFAASB9TCB8gIBAAIxAKQGQxP1i0VfCWn4KmMP taUFn8sMBKjP/9vHnUYdZRvvmoJCA1C6arBUDp8s2DNX+QIDAQABAjBLRqhwN4dU LfqHDKJ/Vg1aD8u3Buv4gYRBxdFR5PveyqHSt5eJ4g/x/4ndsvr2OqUCGQDNfNlD zxHCiEAwZZAPaAkn8jDkFupTljcCGQDMWCujiVZ1NNuBD/N32Yt8P9JDiNzZa08C GBW7VXLxbExpgnhb1V97vjQmTfthXQjYAwIYSTEjoFXm4+Bk5xuBh2IidgSeGZaC FFY9AhkAsteo31cyQw2xJ80SWrmsIw+ps7Cvt5W9 -----END PRIVATE KEY----- -----BEGIN CERTIFICATE----- MIIBDzCByqADAgECAgkAgIBb3+lSwzEwDQYJKoZIhvcNAQEFBQAwFTETMBEGA1UE AxQKQEhvc3ROYW1lQDAeFw0xMzA3MTkxNjA1NTRaFw0yMzA3MTcxNjA1NTRaMBUx EzARBgNVBAMUCkBIb3N0TmFtZUAwTDANBgkqhkiG9w0BAQEFAAM7ADA4AjEApAZD E/WLRV8JafgqYw+1pQWfywwEqM//28edRh1lG++agkIDULpqsFQOnyzYM1f5AgMB AAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADMQABKfn08tKfzzqMMD2w PI2fs3bw5bRH8tmGjrsJeEdp9crCBS8I3hKcxCkTTRTowdY= -----END CERTIFICATE----- `[1:]) func (suite *managementBaseAPISuite) TestNewManagementAPI(c *C) { subscriptionId := "subscriptionId" certDir := c.MkDir() certFile := certDir + "/cert.pem" err := ioutil.WriteFile(certFile, []byte(testCert), 0600) c.Assert(err, IsNil) api, err := NewManagementAPI(subscriptionId, certFile, "West US") c.Assert(err, IsNil) c.Assert(api.session.subscriptionId, DeepEquals, subscriptionId) c.Assert(api.session.certFile, DeepEquals, certFile) c.Assert(api.session.retryPolicy, DeepEquals, NoRetryPolicy) } func (suite *managementBaseAPISuite) TestNewManagementAPIWithRetryPolicy(c *C) { subscriptionId := "subscriptionId" certDir := c.MkDir() certFile := certDir + "/cert.pem" err := ioutil.WriteFile(certFile, []byte(testCert), 0600) c.Assert(err, IsNil) retryPolicy := RetryPolicy{NbRetries: 5, HttpStatusCodes: []int{409}, Delay: time.Minute} api, err := NewManagementAPIWithRetryPolicy(subscriptionId, certFile, "West US", retryPolicy) c.Assert(err, IsNil) c.Assert(api.session.subscriptionId, DeepEquals, subscriptionId) c.Assert(api.session.certFile, DeepEquals, certFile) c.Assert(api.session.retryPolicy, DeepEquals, retryPolicy) c.Assert(api.GetRetryPolicy(), DeepEquals, retryPolicy) } func (suite *managementBaseAPISuite) TestNewManagementAPISetsDefaultPollerInterval(c *C) { api, err := NewManagementAPI("subscriptionId", "", "West US") c.Assert(err, IsNil) c.Assert(api.PollerInterval, Equals, DefaultPollerInterval) } func (suite *managementBaseAPISuite) TestNewManagementAPISetsDefaultPollerTimeout(c *C) { api, err := NewManagementAPI("subscriptionId", "", "West US") c.Assert(err, IsNil) c.Assert(api.PollerTimeout, Equals, DefaultPollerTimeout) } // setUpDispatcher sets up a request dispatcher that: // - records requests // - returns empty responses func (suite *managementBaseAPISuite) setUpDispatcher() *[]*X509Request { fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte{}, } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) return &recordedRequests } // checkOneRequest asserts that the given slice contains one request, with the // given characteristics. func checkOneRequest(c *C, recordedRequests *[]*X509Request, URL, version string, payload []byte, Method string) { requests := *recordedRequests c.Assert(len(requests), Equals, 1) request := requests[0] checkRequest(c, request, URL, version, payload, Method) } func checkRequest(c *C, request *X509Request, URL, version string, payload []byte, Method string) { c.Check(request.URL, Equals, URL) c.Check( strings.TrimSpace(string(request.Payload)), Equals, strings.TrimSpace(string(payload))) c.Check(request.Method, Equals, Method) c.Check(request.APIVersion, Equals, version) } func (suite *managementBaseAPISuite) TestGetOperationFailsIfNoHeader(c *C) { response := x509Response{ StatusCode: http.StatusOK, } _, err := getOperationID(&response) c.Check(err, ErrorMatches, ".*no operation header.*") } func (suite *managementBaseAPISuite) TestListOSImagesRequestsListing(c *C) { api := makeAPI(c) rigFixedResponseDispatcher(&x509Response{StatusCode: http.StatusOK, Body: []byte("<Images></Images>")}) requests := make([]*X509Request, 0) rigRecordingDispatcher(&requests) _, err := api.ListOSImages() c.Assert(err, IsNil) c.Assert(len(requests), Equals, 1) c.Check(requests[0].URL, Equals, api.session.composeURL("services/images")) } func (suite *managementBaseAPISuite) TestListOSImagesReturnsImages(c *C) { expectedImage := OSImage{ LogicalSizeInGB: 199.0, Label: MakeRandomString(10), MediaLink: "http://storage.example.com/" + MakeRandomString(10), Name: MakeRandomString(10), OS: "Linux", EULA: "http://eula.example.com/" + MakeRandomString(10), Description: MakeRandomString(10), RecommendedVMSize: "Medium", } body := fmt.Sprintf(` <Images> <OSImage> <LogicalSizeInGB>%f</LogicalSizeInGB> <Label>%s</Label> <MediaLink>%s</MediaLink> <Name>%s</Name> <OS>%s</OS> <Eula>%s</Eula> <Description>%s</Description> <RecommendedVMSize>%s</RecommendedVMSize> </OSImage> </Images> `, expectedImage.LogicalSizeInGB, expectedImage.Label, expectedImage.MediaLink, expectedImage.Name, expectedImage.OS, expectedImage.EULA, expectedImage.Description, expectedImage.RecommendedVMSize) api := makeAPI(c) rigFixedResponseDispatcher(&x509Response{ StatusCode: http.StatusOK, Body: []byte(body), }) listing, err := api.ListOSImages() c.Assert(err, IsNil) c.Assert(len(listing.Images), Equals, 1) c.Check(listing.Images[0], DeepEquals, expectedImage) } func (suite *managementBaseAPISuite) TestListOSImagesPreservesOrdering(c *C) { imageNames := []string{ MakeRandomString(5), MakeRandomString(5), MakeRandomString(5), } body := fmt.Sprintf(` <Images> <OSImage><Name>%s</Name></OSImage> <OSImage><Name>%s</Name></OSImage> <OSImage><Name>%s</Name></OSImage> </Images> `, imageNames[0], imageNames[1], imageNames[2]) api := makeAPI(c) rigFixedResponseDispatcher(&x509Response{StatusCode: http.StatusOK, Body: []byte(body)}) listing, err := api.ListOSImages() c.Assert(err, IsNil) c.Assert(len(listing.Images), Equals, 3) receivedNames := make([]string, 3) for index := range listing.Images { receivedNames[index] = listing.Images[index].Name } c.Check(receivedNames, DeepEquals, imageNames) } func (suite *managementBaseAPISuite) TestListHostedServices(c *C) { api := makeAPI(c) url := MakeRandomString(10) fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(makeHostedServiceDescriptorList(url)), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) descriptors, err := api.ListHostedServices() c.Assert(err, IsNil) expectedURL := defaultManagement + api.session.subscriptionId + "/services/hostedservices" checkOneRequest(c, &recordedRequests, expectedURL, "2013-10-01", nil, "GET") c.Assert(descriptors[0].URL, Equals, url) } func (suite *managementBaseAPISuite) TestUpdateHostedService(c *C) { api := makeAPI(c) randomLabel := MakeRandomString(10) randomDescription := MakeRandomString(10) property := ExtendedProperty{ Name: "property-name", Value: "property-value", } base64Label := base64.StdEncoding.EncodeToString([]byte(randomLabel)) requestPayload := []byte(makeUpdateHostedService(base64Label, randomDescription, property)) fixedResponse := x509Response{ StatusCode: http.StatusOK, } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) serviceName := MakeRandomString(10) properties := []ExtendedProperty{ property, } update := NewUpdateHostedService(randomLabel, randomDescription, properties) err := api.UpdateHostedService(serviceName, update) c.Assert(err, IsNil) expectedURL := defaultManagement + api.session.subscriptionId + "/services/hostedservices/" + serviceName checkOneRequest(c, &recordedRequests, expectedURL, "2013-10-01", requestPayload, "PUT") } func assertGetHostedServicePropertiesRequest(c *C, api *ManagementAPI, serviceName string, embedDetail bool, httpRequest *X509Request) { var query string if embedDetail { query = "embed-detail=true" } else { query = "embed-detail=false" } expectedURL := fmt.Sprintf("%s%s/services/hostedservices/%s?%s", defaultManagement, api.session.subscriptionId, serviceName, query) checkRequest(c, httpRequest, expectedURL, "2013-10-01", nil, "GET") } func (suite *managementBaseAPISuite) TestGetHostedServiceProperties_withoutDetails(c *C) { api := makeAPI(c) body := ` <?xml version="1.0" encoding="utf-8"?> <HostedService xmlns="http://schemas.microsoft.com/windowsazure"> <Url>hosted-service-url</Url> <ServiceName>hosted-service-name</ServiceName> <HostedServiceProperties> <Description>description</Description> <AffinityGroup>name-of-affinity-group</AffinityGroup> <Location>location-of-service</Location > <Label>base-64-encoded-name-of-service</Label> <Status>current-status-of-service</Status> <DateCreated>creation-date-of-service</DateCreated> <DateLastModified>last-modification-date-of-service</DateLastModified> <ExtendedProperties> <ExtendedProperty> <Name>name-of-property</Name> <Value>value-of-property</Value> </ExtendedProperty> </ExtendedProperties> </HostedServiceProperties> </HostedService> ` fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(body), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) serviceName := "serviceName" properties, err := api.GetHostedServiceProperties(serviceName, false) c.Assert(err, IsNil) c.Check(recordedRequests, HasLen, 1) assertGetHostedServicePropertiesRequest(c, api, serviceName, false, recordedRequests[0]) c.Check(properties.URL, Equals, "hosted-service-url") c.Check(properties.ServiceName, Equals, "hosted-service-name") c.Check(properties.Description, Equals, "description") // Details were explicitly not requested, so this is empty. c.Check(len(properties.Deployments), Equals, 0) } func (suite *managementBaseAPISuite) TestGetHostedServiceProperties_withDetails(c *C) { api := makeAPI(c) body := ` <?xml version="1.0" encoding="utf-8"?> <HostedService xmlns="http://schemas.microsoft.com/windowsazure"> <Url>hosted-service-url</Url> <ServiceName>hosted-service-name</ServiceName> <HostedServiceProperties> <Description>description-of-service</Description> <AffinityGroup>name-of-affinity-group</AffinityGroup> <Location>location-of-service</Location> <Label>base-64-encoded-name-of-service</Label> <Status>current-status-of-service</Status> <DateCreated>creation-date-of-service</DateCreated> <DateLastModified>last-modification-date-of-service</DateLastModified> <ExtendedProperties> <ExtendedProperty> <Name>name-of-property</Name> <Value>value-of-property</Value> </ExtendedProperty> </ExtendedProperties> </HostedServiceProperties> <Deployments> <Deployment xmlns="http://schemas.microsoft.com/windowsazure"> <Name>name-of-deployment</Name> </Deployment> </Deployments> </HostedService> ` fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(body), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) serviceName := "serviceName" properties, err := api.GetHostedServiceProperties(serviceName, true) c.Assert(err, IsNil) c.Check(recordedRequests, HasLen, 1) assertGetHostedServicePropertiesRequest(c, api, serviceName, true, recordedRequests[0]) c.Check(properties.URL, Equals, "hosted-service-url") c.Check(properties.ServiceName, Equals, "hosted-service-name") c.Check(properties.Description, Equals, "description-of-service") c.Check(len(properties.Deployments), Equals, 1) c.Check(properties.Deployments[0].Name, Equals, "name-of-deployment") } func (suite *managementBaseAPISuite) TestAddHostedService(c *C) { api := makeAPI(c) recordedRequests := setUpDispatcher("operationID") createHostedService := NewCreateHostedServiceWithLocation("testName", "testLabel", "East US") err := api.AddHostedService(createHostedService) c.Assert(err, IsNil) expectedURL := defaultManagement + api.session.subscriptionId + "/services/hostedservices" expectedPayload, err := marshalXML(createHostedService) c.Assert(err, IsNil) checkOneRequest(c, recordedRequests, expectedURL, "2013-10-01", expectedPayload, "POST") } func makeAvailabilityResponse(result, reason string) string { return fmt.Sprintf(` <?xml version="1.0" encoding="utf-8"?> <AvailabilityResponse xmlns="http://schemas.microsoft.com/windowsazure"> <Result>%s</Result> <Reason>%s</Reason> </AvailabilityResponse>`, result, reason) } func (*managementBaseAPISuite) TestAddHostedServiceWithOKName(c *C) { api := makeAPI(c) body := makeAvailabilityResponse("true", "") fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(body), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) serviceName := "service-name" err := api.CheckHostedServiceNameAvailability(serviceName) c.Assert(err, IsNil) expectedURL := (defaultManagement + api.session.subscriptionId + "/services/hostedservices/operations/isavailable/" + serviceName) checkOneRequest(c, &recordedRequests, expectedURL, "2013-10-01", nil, "GET") } func (*managementBaseAPISuite) TestAddHostedServiceWithBadName(c *C) { api := makeAPI(c) reason := "This is a false test response" body := makeAvailabilityResponse("false", reason) fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(body), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) serviceName := "service-name" err := api.CheckHostedServiceNameAvailability(serviceName) c.Assert(err, ErrorMatches, reason) c.Check(recordedRequests, HasLen, 1) expectedURL := (defaultManagement + api.session.subscriptionId + "/services/hostedservices/operations/isavailable/" + serviceName) checkOneRequest(c, &recordedRequests, expectedURL, "2013-10-01", nil, "GET") } func (*managementBaseAPISuite) TestAddHostedServiceWithServerError(c *C) { api := makeAPI(c) fixedResponse := x509Response{ StatusCode: http.StatusBadRequest, } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) serviceName := "service-name" err := api.CheckHostedServiceNameAvailability(serviceName) c.Assert(err, ErrorMatches, ".*Bad Request.*") } func (*managementBaseAPISuite) TestAddHostedServiceWithBadXML(c *C) { api := makeAPI(c) body := ` <AvailabilityResponse> <Result>foo</Result> <Reason>unclosed tag </AvailabilityResponse>` fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(body), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) serviceName := "service-name" err := api.CheckHostedServiceNameAvailability(serviceName) c.Assert(err, ErrorMatches, ".*XML syntax error.*") } func assertDeleteHostedServiceRequest(c *C, api *ManagementAPI, serviceName string, httpRequest *X509Request) { expectedURL := fmt.Sprintf("%s%s/services/hostedservices/%s", defaultManagement, api.session.subscriptionId, serviceName) checkRequest(c, httpRequest, expectedURL, "2010-10-28", nil, "DELETE") } func (suite *managementBaseAPISuite) TestDeleteHostedService(c *C) { api := makeAPI(c) recordedRequests := setUpDispatcher("operationID") hostedServiceName := "testName" err := api.DeleteHostedService(hostedServiceName) c.Assert(err, IsNil) c.Check(*recordedRequests, HasLen, 1) assertDeleteHostedServiceRequest(c, api, hostedServiceName, (*recordedRequests)[0]) } func (suite *managementBaseAPISuite) TestDeleteHostedServiceWhenServiceDoesNotExist(c *C) { rigFixedResponseDispatcher(&x509Response{StatusCode: http.StatusNotFound}) err := makeAPI(c).DeleteHostedService("hosted-service-name") c.Assert(err, IsNil) } func (suite *managementBaseAPISuite) TestAddDeployment(c *C) { api := makeAPI(c) recordedRequests := setUpDispatcher("operationID") serviceName := "serviceName" configurationSet := NewLinuxProvisioningConfigurationSet("testHostname12345", "test", "test123#@!", "user-data", "false") vhd := NewOSVirtualHardDisk("hostCaching", "diskLabel", "diskName", "http://mediaLink", "sourceImageName", "os") role := NewRole("ExtraSmall", "test-role-123", []ConfigurationSet{*configurationSet}, []OSVirtualHardDisk{*vhd}) deployment := NewDeploymentForCreateVMDeployment("test-machine-name", "Staging", "testLabel", []Role{*role}, "testNetwork") err := api.AddDeployment(deployment, serviceName) c.Assert(err, IsNil) expectedURL := defaultManagement + api.session.subscriptionId + "/services/hostedservices/" + serviceName + "/deployments" expectedPayload, err := marshalXML(deployment) c.Assert(err, IsNil) checkOneRequest(c, recordedRequests, expectedURL, "2013-10-01", expectedPayload, "POST") } func assertDeleteDeploymentRequest(c *C, api *ManagementAPI, hostedServiceName, deploymentName string, httpRequest *X509Request) { expectedURL := fmt.Sprintf( "%s%s/services/hostedservices/%s/deployments/%s", defaultManagement, api.session.subscriptionId, hostedServiceName, deploymentName) checkRequest(c, httpRequest, expectedURL, "2013-10-01", nil, "DELETE") } func (suite *managementBaseAPISuite) TestDeleteDeployment(c *C) { api := makeAPI(c) recordedRequests := setUpDispatcher("operationID") hostedServiceName := "testHosterServiceName" deploymentName := "testDeploymentName" err := api.DeleteDeployment(hostedServiceName, deploymentName) c.Assert(err, IsNil) c.Assert(*recordedRequests, HasLen, 1) assertDeleteDeploymentRequest(c, api, hostedServiceName, deploymentName, (*recordedRequests)[0]) } func (suite *managementBaseAPISuite) TestDeleteDeploymentWhenDeploymentDoesNotExist(c *C) { rigFixedResponseDispatcher(&x509Response{StatusCode: http.StatusNotFound}) err := makeAPI(c).DeleteDeployment("hosted-service-name", "deployment-name") c.Assert(err, IsNil) } var getDeploymentResponse = ` <?xml version="1.0"?> <Deployment xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">   <Name>gwaclmachinekjn8minr</Name>   <DeploymentSlot>Staging</DeploymentSlot>   <PrivateID>53b117c3126a4f1b8b23bc36c2c94dd1</PrivateID>   <Status>Running</Status>   <Label>WjNkaFkyeHRZV05vYVc1bGEycHVPRzFwYm5JPQ==</Label>   <Url>http://53b117c3126a4f1b8b23bc36c2c94dd1.cloudapp.net/</Url>   <Configuration>PFNlcnZpY2VDb25maWd1cmF0aW9uIHhtbG5zOnhzZD0iaHR0cDovL3d3dy53 My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTU xTY2hlbWEtaW5zdGFuY2UiIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL1NlcnZp Y2VIb3N0aW5nLzIwMDgvMTAvU2VydmljZUNvbmZpZ3VyYXRpb24iPg0KICA8Um9sZSBuYW1lPSJnd2 FjbHJvbGVoYXVxODFyIj4NCiAgICA8SW5zdGFuY2VzIGNvdW50PSIxIiAvPg0KICA8L1JvbGU+DQo8 L1NlcnZpY2VDb25maWd1cmF0aW9uPg==</Configuration>   <RoleInstanceList>     <RoleInstance>       <RoleName>gwaclrolehauq81r</RoleName>       <InstanceName>gwaclrolehauq81r</InstanceName>       <InstanceStatus>ReadyRole</InstanceStatus>       <InstanceUpgradeDomain>0</InstanceUpgradeDomain>       <InstanceFaultDomain>0</InstanceFaultDomain>       <InstanceSize>ExtraSmall</InstanceSize>       <InstanceStateDetails/>       <IpAddress>10.241.158.13</IpAddress>       <PowerState>Started</PowerState>       <HostName>gwaclhostsnx7m1co57n</HostName>       <RemoteAccessCertificateThumbprint>68db67cd8a6047a6cf6da0f92a7ee67d</RemoteAccessCertificateThumbprint>     </RoleInstance>   </RoleInstanceList>   <UpgradeDomainCount>1</UpgradeDomainCount>   <RoleList>     <Role i:type="PersistentVMRole">       <RoleName>gwaclrolehauq81r</RoleName>       <OsVersion/>       <RoleType>PersistentVMRole</RoleType>       <ConfigurationSets>         <ConfigurationSet i:type="NetworkConfigurationSet">           <ConfigurationSetType>NetworkConfiguration</ConfigurationSetType>           <SubnetNames/>         </ConfigurationSet>       </ConfigurationSets>       <DataVirtualHardDisks/>       <OSVirtualHardDisk>         <HostCaching>ReadWrite</HostCaching>         <DiskLabel>gwaclauonntmontirrz9rgltt8d5f4evtjeagbcx7kf8umqhs3t421m21t798ebw</DiskLabel>         <DiskName>gwacldiskdvmvahc</DiskName>         <MediaLink>http://gwacl3133mh3fs9jck6yk0dh.blob.core.windows.net/vhds/gwacldisk79vobmh.vhd</MediaLink>         <SourceImageName>b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu_DAILY_BUILD-precise-12_04_2-LTS-amd64-server-20130624-en-us-30GB</SourceImageName>         <OS>Linux</OS>       </OSVirtualHardDisk>       <RoleSize>ExtraSmall</RoleSize>     </Role>   </RoleList>   <SdkVersion/>   <Locked>false</Locked>   <RollbackAllowed>false</RollbackAllowed>   <CreatedTime>2013-06-25T14:35:22Z</CreatedTime>   <LastModifiedTime>2013-06-25T14:48:54Z</LastModifiedTime>   <ExtendedProperties/>   <PersistentVMDowntime>     <StartTime>2013-05-08T22:00:00Z</StartTime>     <EndTime>2013-05-10T06:00:00Z</EndTime>     <Status>PersistentVMUpdateCompleted</Status>   </PersistentVMDowntime>   <VirtualIPs>     <VirtualIP>       <Address>137.117.72.69</Address>       <IsDnsProgrammed>true</IsDnsProgrammed>       <Name>__PseudoBackEndContractVip</Name>     </VirtualIP>   </VirtualIPs> </Deployment> ` func assertGetDeploymentRequest(c *C, api *ManagementAPI, request *GetDeploymentRequest, httpRequest *X509Request) { expectedURL := fmt.Sprintf( "%s%s/services/hostedservices/%s/deployments/%s", defaultManagement, api.session.subscriptionId, request.ServiceName, request.DeploymentName) checkRequest(c, httpRequest, expectedURL, "2013-10-01", nil, "GET") } func (suite *managementBaseAPISuite) TestGetDeployment(c *C) { api := makeAPI(c) fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(getDeploymentResponse), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) serviceName := "serviceName" deploymentName := "gwaclmachinekjn8minr" request := &GetDeploymentRequest{ServiceName: serviceName, DeploymentName: deploymentName} deployment, err := api.GetDeployment(request) c.Assert(err, IsNil) c.Assert(recordedRequests, HasLen, 1) assertGetDeploymentRequest(c, api, request, recordedRequests[0]) c.Check(deployment.Name, Equals, deploymentName) } func (suite *managementBaseAPISuite) TestAddStorageAccount(c *C) { api := makeAPI(c) header := http.Header{} header.Set("X-Ms-Request-Id", "operationID") fixedResponse := x509Response{ StatusCode: http.StatusAccepted, Header: header, } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) cssi := NewCreateStorageServiceInputWithLocation("name", "label", "East US", "false") err := api.AddStorageAccount(cssi) c.Assert(err, IsNil) expectedURL := defaultManagement + api.session.subscriptionId + "/services/storageservices" expectedPayload, err := marshalXML(cssi) c.Assert(err, IsNil) checkOneRequest(c, &recordedRequests, expectedURL, "2013-10-01", expectedPayload, "POST") } func (suite *managementBaseAPISuite) TestDeleteStorageAccount(c *C) { const accountName = "myaccount" api := makeAPI(c) accountURL := api.session.composeURL("services/storageservices/" + accountName) recordedRequests := setUpDispatcher("operationID") err := api.DeleteStorageAccount(accountName) c.Assert(err, IsNil) checkOneRequest(c, recordedRequests, accountURL, "2011-06-01", nil, "DELETE") } func (suite *managementBaseAPISuite) TestDeleteStorageAccountWhenAccountDoesNotExist(c *C) { rigFixedResponseDispatcher(&x509Response{StatusCode: http.StatusNotFound}) err := makeAPI(c).DeleteStorageAccount("account-name") c.Assert(err, IsNil) } func (suite *managementBaseAPISuite) TestGetStorageAccountKeys(c *C) { const ( accountName = "accountname" primaryKey = "primarykey" secondaryKey = "secondarykey" ) api := makeAPI(c) url := api.session.composeURL("services/storageservices/" + accountName) body := fmt.Sprintf( `<StorageService> <Url>%s</Url> <StorageServiceKeys> <Primary>%s</Primary> <Secondary>%s</Secondary> </StorageServiceKeys> </StorageService>`, url, primaryKey, secondaryKey) rigFixedResponseDispatcher(&x509Response{ StatusCode: http.StatusOK, Body: []byte(body), }) keys, err := api.GetStorageAccountKeys("account") c.Assert(err, IsNil) c.Check(keys.Primary, Equals, primaryKey) c.Check(keys.Secondary, Equals, secondaryKey) c.Check(keys.URL, Equals, url) } func assertDeleteDiskRequest(c *C, api *ManagementAPI, diskName string, httpRequest *X509Request, deleteBlob bool) { expectedURL := fmt.Sprintf("%s%s/services/disks/%s", defaultManagement, api.session.subscriptionId, diskName) if deleteBlob { expectedURL += "?comp=media" } checkRequest(c, httpRequest, expectedURL, "2012-08-01", nil, "DELETE") } func (suite *managementBaseAPISuite) TestDeleteDisk(c *C) { // The current implementation of DeleteDisk() works around a bug in // Windows Azure by polling the server. See the documentation in the file // deletedisk.go for details. // Change the polling interval to speed up the tests: deleteDiskInterval = time.Nanosecond api := makeAPI(c) diskName := "diskName" for _, deleteBlob := range [...]bool{false, true} { recordedRequests := setUpDispatcher("operationID") err := api.DeleteDisk(&DeleteDiskRequest{ DiskName: diskName, DeleteBlob: deleteBlob, }) c.Assert(err, IsNil) c.Assert(*recordedRequests, HasLen, 1) assertDeleteDiskRequest(c, api, diskName, (*recordedRequests)[0], deleteBlob) } } func (suite *managementBaseAPISuite) TestDeleteDiskWhenDiskDoesNotExist(c *C) { rigFixedResponseDispatcher(&x509Response{StatusCode: http.StatusNotFound}) err := makeAPI(c).DeleteDisk(&DeleteDiskRequest{DiskName: "disk-name"}) c.Assert(err, IsNil) } func (suite *managementBaseAPISuite) TestDeleteDiskWithDeleteBlob(c *C) { // Setting deleteBlob=true should append comp=media as a url query value. deleteDiskInterval = time.Nanosecond api := makeAPI(c) recordedRequests := setUpDispatcher("operationID") diskName := "diskName" err := api.DeleteDisk(&DeleteDiskRequest{ DiskName: diskName, DeleteBlob: true}) c.Assert(err, IsNil) originalURL := (*recordedRequests)[0].URL parsedURL, err := url.Parse(originalURL) c.Assert(err, IsNil) values := parsedURL.Query() c.Assert(err, IsNil) c.Check(values["comp"], DeepEquals, []string{"media"}) } func (suite *managementBaseAPISuite) TestPerformNodeOperation(c *C) { api := makeAPI(c) recordedRequests := setUpDispatcher("operationID") serviceName := "serviceName" deploymentName := "deploymentName" roleName := "roleName" operation := newRoleOperation("RandomOperation") version := "test-version" err := api.performRoleOperation(serviceName, deploymentName, roleName, version, operation) c.Assert(err, IsNil) expectedURL := defaultManagement + api.session.subscriptionId + "/services/hostedservices/" + serviceName + "/deployments/" + deploymentName + "/roleinstances/" + roleName + "/Operations" expectedPayload, err := marshalXML(operation) c.Assert(err, IsNil) checkOneRequest(c, recordedRequests, expectedURL, version, expectedPayload, "POST") } func (suite *managementBaseAPISuite) TestStartRole(c *C) { api := makeAPI(c) recordedRequests := setUpDispatcher("operationID") request := &StartRoleRequest{"serviceName", "deploymentName", "roleName"} err := api.StartRole(request) c.Assert(err, IsNil) expectedURL := (defaultManagement + api.session.subscriptionId + "/services/hostedservices/" + request.ServiceName + "/deployments/" + request.DeploymentName + "/roleinstances/" + request.RoleName + "/Operations") expectedPayload, err := marshalXML(startRoleOperation) c.Assert(err, IsNil) checkOneRequest(c, recordedRequests, expectedURL, "2013-10-01", expectedPayload, "POST") } func (suite *managementBaseAPISuite) TestRestartRole(c *C) { api := makeAPI(c) recordedRequests := setUpDispatcher("operationID") request := &RestartRoleRequest{"serviceName", "deploymentName", "roleName"} err := api.RestartRole(request) c.Assert(err, IsNil) expectedURL := (defaultManagement + api.session.subscriptionId + "/services/hostedservices/" + request.ServiceName + "/deployments/" + request.DeploymentName + "/roleinstances/" + request.RoleName + "/Operations") expectedPayload, err := marshalXML(restartRoleOperation) c.Assert(err, IsNil) checkOneRequest(c, recordedRequests, expectedURL, "2013-10-01", expectedPayload, "POST") } func assertShutdownRoleRequest(c *C, api *ManagementAPI, request *ShutdownRoleRequest, httpRequest *X509Request) { expectedURL := fmt.Sprintf( "%s%s/services/hostedservices/%s/deployments/%s/roleinstances/%s/Operations", defaultManagement, api.session.subscriptionId, request.ServiceName, request.DeploymentName, request.RoleName) expectedPayload, err := marshalXML(shutdownRoleOperation) c.Assert(err, IsNil) checkRequest(c, httpRequest, expectedURL, "2013-10-01", expectedPayload, "POST") } func (suite *managementBaseAPISuite) TestShutdownRole(c *C) { api := makeAPI(c) recordedRequests := setUpDispatcher("operationID") request := &ShutdownRoleRequest{"serviceName", "deploymentName", "roleName"} err := api.ShutdownRole(request) c.Assert(err, IsNil) c.Assert(*recordedRequests, HasLen, 1) assertShutdownRoleRequest(c, api, request, (*recordedRequests)[0]) } func assertGetRoleRequest(c *C, api *ManagementAPI, httpRequest *X509Request, serviceName, deploymentName, roleName string) { expectedURL := (defaultManagement + api.session.subscriptionId + "/services/hostedservices/" + serviceName + "/deployments/" + deploymentName + "/roles/" + roleName) checkRequest(c, httpRequest, expectedURL, "2013-10-01", nil, "GET") } func (suite *managementBaseAPISuite) TestGetRole(c *C) { api := makeAPI(c) request := &GetRoleRequest{"serviceName", "deploymentName", "roleName"} fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte(makePersistentVMRole("rolename")), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) role, err := api.GetRole(request) c.Assert(err, IsNil) assertGetRoleRequest( c, api, recordedRequests[0], request.ServiceName, request.DeploymentName, request.RoleName) c.Check(role.RoleName, Equals, "rolename") } func assertUpdateRoleRequest(c *C, api *ManagementAPI, httpRequest *X509Request, serviceName, deploymentName, roleName, expectedXML string) { expectedURL := (defaultManagement + api.session.subscriptionId + "/services/hostedservices/" + serviceName + "/deployments/" + deploymentName + "/roles/" + roleName) checkRequest( c, httpRequest, expectedURL, "2013-10-01", []byte(expectedXML), "PUT") c.Assert(httpRequest.ContentType, Equals, "application/xml") } func (suite *managementBaseAPISuite) TestUpdateRole(c *C) { api := makeAPI(c) request := &UpdateRoleRequest{ ServiceName: "serviceName", DeploymentName: "deploymentName", RoleName: "roleName", PersistentVMRole: &PersistentVMRole{ RoleName: "newRoleNamePerhaps", }, } rigFixedResponseDispatcher(&x509Response{StatusCode: http.StatusOK}) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) err := api.UpdateRole(request) c.Assert(err, IsNil) expectedXML, err := request.PersistentVMRole.Serialize() c.Assert(err, IsNil) assertUpdateRoleRequest( c, api, recordedRequests[0], request.ServiceName, request.DeploymentName, request.RoleName, expectedXML) } func (suite *managementBaseAPISuite) TestUpdateRoleBlocksUntilComplete(c *C) { api := makeAPI(c) api.PollerInterval = time.Nanosecond request := &UpdateRoleRequest{ ServiceName: "serviceName", DeploymentName: "deploymentName", RoleName: "roleName", PersistentVMRole: &PersistentVMRole{ RoleName: "newRoleNamePerhaps", }, } responses := []DispatcherResponse{ // First response is 202 with an X-MS-Request-ID header. {makeX509ResponseWithOperationHeader("foobar"), nil}, // Second response is XML to say that the request above has completed. { &x509Response{ Body: []byte(fmt.Sprintf(operationXMLTemplate, "Succeeded")), StatusCode: http.StatusOK, }, nil, }, } recordedRequests := []*X509Request{} rigRecordingPreparedResponseDispatcher(&recordedRequests, responses) err := api.UpdateRole(request) c.Assert(err, IsNil) c.Assert(recordedRequests, HasLen, 2) expectedXML, err := request.PersistentVMRole.Serialize() c.Assert(err, IsNil) assertUpdateRoleRequest( c, api, recordedRequests[0], request.ServiceName, request.DeploymentName, request.RoleName, expectedXML) // Second request is to get status of operation "foobar". c.Check(recordedRequests[1].Method, Equals, "GET") c.Check(recordedRequests[1].URL, Matches, ".*/operations/foobar") } func (suite *managementBaseAPISuite) TestCreateAffinityGroup(c *C) { api := makeAPI(c) cag := NewCreateAffinityGroup( "name", "label", "description", "location") request := CreateAffinityGroupRequest{ CreateAffinityGroup: cag} fixedResponse := x509Response{ StatusCode: http.StatusCreated} rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) err := api.CreateAffinityGroup(&request) c.Assert(err, IsNil) expectedURL := defaultManagement + api.session.subscriptionId + "/affinitygroups" expectedBody, _ := cag.Serialize() checkOneRequest(c, &recordedRequests, expectedURL, "2013-10-01", []byte(expectedBody), "POST") } func (suite *managementBaseAPISuite) TestUpdateAffinityGroup(c *C) { api := makeAPI(c) uag := NewUpdateAffinityGroup("label", "description") request := UpdateAffinityGroupRequest{ Name: "groupname", UpdateAffinityGroup: uag} fixedResponse := x509Response{ StatusCode: http.StatusCreated} rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) err := api.UpdateAffinityGroup(&request) c.Assert(err, IsNil) expectedURL := (defaultManagement + api.session.subscriptionId + "/affinitygroups/" + request.Name) expectedBody, _ := uag.Serialize() checkOneRequest(c, &recordedRequests, expectedURL, "2011-02-25", []byte(expectedBody), "PUT") } func (suite *managementBaseAPISuite) TestDeleteAffinityGroup(c *C) { api := makeAPI(c) request := DeleteAffinityGroupRequest{ Name: "groupname"} fixedResponse := x509Response{ StatusCode: http.StatusCreated} rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) err := api.DeleteAffinityGroup(&request) c.Assert(err, IsNil) expectedURL := (defaultManagement + api.session.subscriptionId + "/affinitygroups/" + request.Name) checkOneRequest(c, &recordedRequests, expectedURL, "2011-02-25", nil, "DELETE") } func (suite *managementBaseAPISuite) TestDeleteAffinityGroupWhenGroupDoesNotExist(c *C) { rigFixedResponseDispatcher(&x509Response{StatusCode: http.StatusNotFound}) request := DeleteAffinityGroupRequest{Name: "groupname"} err := makeAPI(c).DeleteAffinityGroup(&request) c.Assert(err, IsNil) } func makeNetworkConfiguration() *NetworkConfiguration { return &NetworkConfiguration{ XMLNS: XMLNS_NC, DNS: &[]VirtualNetDnsServer{ { Name: "dns-server-name", IPAddress: "IPV4-address-of-the-server", }, }, LocalNetworkSites: &[]LocalNetworkSite{ { Name: "local-site-name", AddressSpacePrefixes: []string{ "CIDR-identifier", }, VPNGatewayAddress: "IPV4-address-of-the-vpn-gateway", }, }, VirtualNetworkSites: &[]VirtualNetworkSite{ { Name: "virtual-network-name", AffinityGroup: "affinity-group-name", AddressSpacePrefixes: []string{ "CIDR-identifier", }, Subnets: &[]Subnet{ { Name: "subnet-name", AddressPrefix: "CIDR-identifier", }, }, DnsServersRef: &[]DnsServerRef{ { Name: "primary-DNS-name", }, }, Gateway: &Gateway{ Profile: "Small", VPNClientAddressPoolPrefixes: []string{ "CIDR-identifier", }, LocalNetworkSiteRef: LocalNetworkSiteRef{ Name: "local-site-name", Connection: LocalNetworkSiteRefConnection{ Type: "connection-type", }, }, }, }, }, } } func assertGetNetworkConfigurationRequest(c *C, api *ManagementAPI, httpRequest *X509Request) { expectedURL := fmt.Sprintf( "%s%s/services/networking/media", defaultManagement, api.session.subscriptionId) checkRequest(c, httpRequest, expectedURL, "2013-10-01", nil, "GET") } func (suite *managementBaseAPISuite) TestGetNetworkConfiguration(c *C) { expected := makeNetworkConfiguration() expectedXML, err := expected.Serialize() c.Assert(err, IsNil) rigFixedResponseDispatcher(&x509Response{ StatusCode: http.StatusOK, Body: []byte(expectedXML)}) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) api := makeAPI(c) observed, err := api.GetNetworkConfiguration() c.Assert(err, IsNil) c.Assert(recordedRequests, HasLen, 1) assertGetNetworkConfigurationRequest(c, api, recordedRequests[0]) c.Assert(observed, DeepEquals, expected) } func (suite *managementBaseAPISuite) TestGetNetworkConfigurationNotFound(c *C) { rigFixedResponseDispatcher(&x509Response{ StatusCode: http.StatusNotFound}) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) api := makeAPI(c) observed, err := api.GetNetworkConfiguration() c.Assert(observed, IsNil) c.Assert(err, IsNil) c.Assert(recordedRequests, HasLen, 1) assertGetNetworkConfigurationRequest(c, api, recordedRequests[0]) } func assertSetNetworkConfigurationRequest(c *C, api *ManagementAPI, body []byte, httpRequest *X509Request) { expectedURL := fmt.Sprintf( "%s%s/services/networking/media", defaultManagement, api.session.subscriptionId) checkRequest(c, httpRequest, expectedURL, "2013-10-01", body, "PUT") // Azure chokes when the content type is text/xml or similar. c.Assert(httpRequest.ContentType, Equals, "application/octet-stream") } func (suite *managementBaseAPISuite) TestSetNetworkConfiguration(c *C) { api := makeAPI(c) fixedResponse := x509Response{StatusCode: http.StatusOK} rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) request := makeNetworkConfiguration() requestXML, err := request.Serialize() c.Assert(err, IsNil) requestPayload := []byte(requestXML) err = api.SetNetworkConfiguration(request) c.Assert(err, IsNil) c.Assert(recordedRequests, HasLen, 1) assertSetNetworkConfigurationRequest(c, api, requestPayload, recordedRequests[0]) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/vhd_footer_test.go�����������������������������������������0000644�0000153�0000161�00000000740�12321735761�023607� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "encoding/base64" . "launchpad.net/gocheck" ) type testVHDFooter struct{} var _ = Suite(&testVHDFooter{}) func (s *testVHDFooter) TestValidDecode(c *C) { var err error decoded, err := base64.StdEncoding.DecodeString(VHD_FOOTER) c.Assert(err, IsNil) c.Assert(512, Equals, len(decoded)) } ��������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/storage_base.go��������������������������������������������0000644�0000153�0000161�00000063623�12321735761�023060� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl // This file contains the operations necessary to work with the Azure // file storage API. For more details, see // http://msdn.microsoft.com/en-us/library/windowsazure/dd179355.aspx // TODO Improve function documentation: the Go documentation convention is for // function documentation to start out with the name of the function. This may // have special significance for godoc. import ( "bytes" "crypto/hmac" "crypto/sha256" "encoding/base64" "errors" "fmt" "io" "io/ioutil" "net/http" "net/url" "sort" "strings" "time" ) var headersToSign = []string{ "Content-Encoding", "Content-Language", "Content-Length", "Content-MD5", "Content-Type", "Date", "If-Modified-Since", "If-Match", "If-None-Match", "If-Unmodified-Since", "Range", } func init() { // See https://code.google.com/p/go/issues/detail?id=4677 // We need to force the connection to close each time so that we don't // hit the above Go bug. roundTripper := http.DefaultClient.Transport if transport, ok := roundTripper.(*http.Transport); ok { transport.DisableKeepAlives = true } http.DefaultTransport.(*http.Transport).DisableKeepAlives = true } // sign returns the base64-encoded HMAC-SHA256 signature of the given string // using the given base64-encoded key. func sign(accountKey, signable string) (string, error) { // Allegedly, this is already UTF8 encoded. decodedKey, err := base64.StdEncoding.DecodeString(accountKey) if err != nil { return "", fmt.Errorf("invalid account key: %s", err) } hash := hmac.New(sha256.New, decodedKey) _, err = hash.Write([]byte(signable)) if err != nil { return "", fmt.Errorf("failed to write hash: %s", err) } var hashed []byte hashed = hash.Sum(hashed) b64Hashed := base64.StdEncoding.EncodeToString(hashed) return b64Hashed, nil } // Calculate the value required for an Authorization header. func composeAuthHeader(req *http.Request, accountName, accountKey string) (string, error) { signable := composeStringToSign(req, accountName) b64Hashed, err := sign(accountKey, signable) if err != nil { return "", err } return fmt.Sprintf("SharedKey %s:%s", accountName, b64Hashed), nil } // Calculate the string that needs to be HMAC signed. It is comprised of // the headers in headersToSign, x-ms-* headers and the URI params. func composeStringToSign(req *http.Request, accountName string) string { // TODO: whitespace should be normalised in value strings. return fmt.Sprintf( "%s\n%s%s%s", req.Method, composeHeaders(req), composeCanonicalizedHeaders(req), composeCanonicalizedResource(req, accountName)) } // toLowerKeys lower cases all map keys. If two keys exist, that differ // by the case of their keys, the values will be concatenated. func toLowerKeys(values url.Values) map[string][]string { m := make(map[string][]string) for k, v := range values { k = strings.ToLower(k) m[k] = append(m[k], v...) } for _, v := range m { sort.Strings(v) } return m } // Encode the URI params as required by the API. They are lower-cased, // sorted and formatted as param:value,value,...\nparam:value... func encodeParams(values map[string][]string) string { var keys []string values = toLowerKeys(values) for k := range values { keys = append(keys, k) } sort.Strings(keys) var result []string for _, v := range keys { result = append(result, fmt.Sprintf("%v:%s", v, strings.Join(values[v], ","))) } return strings.Join(result, "\n") } // Calculate the headers required in the string to sign. func composeHeaders(req *http.Request) string { var result []string for _, headerName := range headersToSign { result = append(result, req.Header.Get(headerName)+"\n") } return strings.Join(result, "") } // Calculate the x-ms-* headers, encode as for encodeParams. func composeCanonicalizedHeaders(req *http.Request) string { var results []string for headerName, values := range req.Header { headerName = strings.ToLower(headerName) if strings.HasPrefix(headerName, "x-ms-") { results = append(results, fmt.Sprintf("%v:%s\n", headerName, strings.Join(values, ","))) } } sort.Strings(results) return strings.Join(results, "") } // Calculate the URI params and encode them in the string. // See http://msdn.microsoft.com/en-us/library/windowsazure/dd179428.aspx // for details of this encoding. func composeCanonicalizedResource(req *http.Request, accountName string) string { path := req.URL.Path if !strings.HasPrefix(path, "/") { path = "/" + path } values := req.URL.Query() valuesLower := toLowerKeys(values) paramString := encodeParams(valuesLower) result := "/" + accountName + path if paramString != "" { result += "\n" + paramString } return result } // Take the passed msVersion string and add it to the request headers. func addVersionHeader(req *http.Request, msVersion string) { req.Header.Set("x-ms-version", msVersion) } // Calculate the mD5sum and content length for the request payload and add // as the Content-MD5 header and Content-Length header respectively. func addContentHeaders(req *http.Request) { if req.Body == nil { if req.Method == "PUT" || req.Method == "POST" { // This cannot be set for a GET, likewise it *must* be set for // PUT and POST. req.Header.Set("Content-Length", "0") } return } reqdata, err := ioutil.ReadAll(req.Body) if err != nil { panic(fmt.Errorf("Unable to read request body: %s", err)) } // Replace the request's data because we just destroyed it by reading it. req.Body = ioutil.NopCloser(bytes.NewReader(reqdata)) req.Header.Set("Content-Length", fmt.Sprintf("%d", len(reqdata))) // Stop Go's http lib from chunking the data because Azure will return // an authorization error if it's chunked. req.ContentLength = int64(len(reqdata)) } // Add a Date: header in RFC1123 format. func addDateHeader(req *http.Request) { now := time.Now().UTC().Format(time.RFC1123) // The Azure API requires "GMT" and not "UTC". now = strings.Replace(now, "UTC", "GMT", 1) req.Header.Set("Date", now) } // signRequest adds the Authorization: header to a Request. // Don't make any further changes to the request before sending it, or the // signature will not be valid. func (context *StorageContext) signRequest(req *http.Request) error { // Only sign the request if the key is not empty. if context.Key != "" { header, err := composeAuthHeader(req, context.Account, context.Key) if err != nil { return err } req.Header.Set("Authorization", header) } return nil } // StorageContext keeps track of the mandatory parameters required to send a // request to the storage services API. It also has an HTTP Client to allow // overriding for custom behaviour, during testing for example. type StorageContext struct { // Account is a storage account name. Account string // Key authenticates the storage account. Access will be anonymous if this // is left empty. Key string // AzureEndpoint specifies a base service endpoint URL for the Azure APIs. // This field is required. AzureEndpoint APIEndpoint client *http.Client RetryPolicy RetryPolicy } // getClient is used when sending a request. If a custom client is specified // in context.client it is returned, otherwise net.http.DefaultClient is // returned. func (context *StorageContext) getClient() *http.Client { if context.client == nil { return http.DefaultClient } return context.client } // Any object that deserializes XML must meet this interface. type Deserializer interface { Deserialize([]byte) error } // requestParams is a Parameter Object for performRequest(). type requestParams struct { Method string // HTTP method, e.g. "GET" or "PUT". URL string // Resource locator, e.g. "http://example.com/my/resource". Body io.Reader // Optional request body. APIVersion string // Expected Azure API version, e.g. "2012-02-12". ExtraHeaders http.Header // Optional extra request headers. Result Deserializer // Optional object to parse API response into. ExpectedStatus HTTPStatus // Expected response status, e.g. http.StatusOK. } // Check performs a basic sanity check on the request. This will only catch // a few superficial problems that you can spot at compile time, to save a // debugging cycle for the most basic mistakes. func (params *requestParams) Check() { const panicPrefix = "invalid request: " if params.Method == "" { panic(errors.New(panicPrefix + "HTTP method not specified")) } if params.URL == "" { panic(errors.New(panicPrefix + "URL not specified")) } if params.APIVersion == "" { panic(errors.New(panicPrefix + "API version not specified")) } if params.ExpectedStatus == 0 { panic(errors.New(panicPrefix + "expected HTTP status not specified")) } methods := map[string]bool{"GET": true, "PUT": true, "POST": true, "DELETE": true} if _, ok := methods[params.Method]; !ok { panic(fmt.Errorf(panicPrefix+"unsupported HTTP method '%s'", params.Method)) } } // performRequest issues an HTTP request to Azure. // // It returns the response body contents and the response headers. func (context *StorageContext) performRequest(params requestParams) ([]byte, http.Header, error) { params.Check() req, err := http.NewRequest(params.Method, params.URL, params.Body) if err != nil { return nil, nil, err } // net/http has no way of adding headers en-masse, hence this abomination. for header, values := range params.ExtraHeaders { for _, value := range values { req.Header.Add(header, value) } } addVersionHeader(req, params.APIVersion) addDateHeader(req) addContentHeaders(req) if err := context.signRequest(req); err != nil { return nil, nil, err } return context.send(req, params.Result, params.ExpectedStatus) } // Send a request to the storage service and process the response. // The "res" parameter is typically an XML struct that will deserialize the // raw XML into the struct data. The http Response object's body is returned. // // If the response's HTTP status code is not the same as "expectedStatus" // then an HTTPError will be returned as the error. func (context *StorageContext) send(req *http.Request, res Deserializer, expectedStatus HTTPStatus) ([]byte, http.Header, error) { client := context.getClient() retrier := context.RetryPolicy.getRetrier(client) resp, err := retrier.RetryRequest(req) if err != nil { return nil, nil, err } body, err := readAndClose(resp.Body) if err != nil { return nil, nil, fmt.Errorf("failed to read response data: %v", err) } if resp.StatusCode != int(expectedStatus) { msg := newHTTPError(resp.StatusCode, body, "Azure request failed") return body, resp.Header, msg } // If the caller didn't supply an object to deserialize the message into // then just return. if res == nil { return body, resp.Header, nil } // TODO: Also deserialize response headers into the "res" object. err = res.Deserialize(body) if err != nil { msg := fmt.Errorf("Failed to deserialize data: %s", err) return body, resp.Header, msg } return body, resp.Header, nil } // getAccountURL returns the base URL for the context's storage account. // (The result ends in a slash.) func (context *StorageContext) getAccountURL() string { if context.AzureEndpoint == APIEndpoint("") { panic(errors.New("no AzureEndpoint specified in gwacl.StorageContext")) } return context.AzureEndpoint.BlobStorageAPI(context.Account) } // getContainerURL returns the URL for a given storage container. // (The result does not end in a slash.) func (context *StorageContext) getContainerURL(container string) string { return strings.TrimRight(context.getAccountURL(), "/") + "/" + url.QueryEscape(container) } // GetFileURL returns the URL for a given file in the given container. // (The result does not end in a slash.) func (context *StorageContext) GetFileURL(container, filename string) string { return context.getContainerURL(container) + "/" + url.QueryEscape(filename) } // GetAnonymousFileURL returns an anonymously-accessible URL for a given file // in the given container. func (context *StorageContext) GetAnonymousFileURL(container, filename string, expires time.Time) (string, error) { url := context.GetFileURL(container, filename) values, err := getReadBlobAccessValues(container, filename, context.Account, context.Key, expires) if err != nil { return "", err } return fmt.Sprintf("%s?%s", url, values.Encode()), nil } type ListContainersRequest struct { Marker string } // ListContainers calls the "List Containers" operation on the storage // API, and returns a single batch of results. // The marker argument should be empty for a new List Containers request. for // subsequent calls to get additional batches of the same result, pass the // NextMarker from the previous call's result. func (context *StorageContext) ListContainers(request *ListContainersRequest) (*ContainerEnumerationResults, error) { uri := addURLQueryParams(context.getAccountURL(), "comp", "list") if request.Marker != "" { uri = addURLQueryParams(uri, "marker", request.Marker) } containers := ContainerEnumerationResults{} _, _, err := context.performRequest(requestParams{ Method: "GET", URL: uri, APIVersion: "2012-02-12", Result: &containers, ExpectedStatus: http.StatusOK, }) if err != nil { msg := "request for containers list failed: " return nil, extendError(err, msg) } return &containers, nil } type ListBlobsRequest struct { Container string Marker string Prefix string } // ListBlobs calls the "List Blobs" operation on the storage API, and returns // a single batch of results. // The request.Marker argument should be empty for a new List Blobs request. // For subsequent calls to get additional batches of the same result, pass the // NextMarker from the previous call's result. func (context *StorageContext) ListBlobs(request *ListBlobsRequest) (*BlobEnumerationResults, error) { uri := addURLQueryParams( context.getContainerURL(request.Container), "restype", "container", "comp", "list") if request.Marker != "" { uri = addURLQueryParams(uri, "marker", request.Marker) } if request.Prefix != "" { uri = addURLQueryParams(uri, "prefix", request.Prefix) } blobs := BlobEnumerationResults{} _, _, err := context.performRequest(requestParams{ Method: "GET", URL: uri, APIVersion: "2012-02-12", Result: &blobs, ExpectedStatus: http.StatusOK, }) if err != nil { msg := "request for blobs list failed: " return nil, extendError(err, msg) } return &blobs, err } // Send a request to the storage service to create a new container. If the // request fails, error is non-nil. func (context *StorageContext) CreateContainer(container string) error { uri := addURLQueryParams( context.getContainerURL(container), "restype", "container") _, _, err := context.performRequest(requestParams{ Method: "PUT", URL: uri, APIVersion: "2012-02-12", ExpectedStatus: http.StatusCreated, }) if err != nil { msg := fmt.Sprintf("failed to create container %s: ", container) return extendError(err, msg) } return nil } // Send a request to the storage service to delete a container. It will also // delete all the blobs inside it. func (context *StorageContext) DeleteContainer(container string) error { uri := addURLQueryParams( context.getContainerURL(container), "restype", "container") _, _, err := context.performRequest(requestParams{ Method: "DELETE", URL: uri, APIVersion: "2012-02-12", ExpectedStatus: http.StatusAccepted, }) if err != nil { // If the error is an Azure 404 error, return silently: the container // does not exist. if IsNotFoundError(err) { return nil } msg := fmt.Sprintf("failed to delete container %s: ", container) return extendError(err, msg) } return nil } // Send a request to the storage service to retrieve a container's properties. // Also doubles as a handy way to see if a container exists. func (context *StorageContext) GetContainerProperties(container string) (*Properties, error) { uri := addURLQueryParams( context.getContainerURL(container), "restype", "container") params := requestParams{ Method: "GET", URL: uri, APIVersion: "2012-02-12", ExpectedStatus: http.StatusOK, } _, headers, err := context.performRequest(params) if err != nil { msg := fmt.Sprintf("failed to find container %s: ", container) return nil, extendError(err, msg) } props := &Properties{ LastModified: headers.Get(http.CanonicalHeaderKey("Last-Modified")), ETag: headers.Get(http.CanonicalHeaderKey("ETag")), LeaseStatus: headers.Get(http.CanonicalHeaderKey("X-Ms-Lease-Status")), LeaseState: headers.Get(http.CanonicalHeaderKey("X-Ms-Lease-State")), LeaseDuration: headers.Get(http.CanonicalHeaderKey("X-Ms-Lease-Duration")), } return props, nil } type PutBlobRequest struct { Container string // Container name in the storage account BlobType string // Pass "page" or "block" Filename string // Filename for the new blob Size int // Size for the new blob. Only required for page blobs. } // Send a request to create a space to upload a blob. Note that this does not // do the uploading, it just makes an empty file. func (context *StorageContext) PutBlob(req *PutBlobRequest) error { var blobType string switch req.BlobType { case "page": blobType = "PageBlob" if req.Size == 0 { return fmt.Errorf("Must supply a size for a page blob") } if req.Size%512 != 0 { return fmt.Errorf("Size must be a multiple of 512 bytes") } case "block": blobType = "BlockBlob" default: panic("blockType must be 'page' or 'block'") } extraHeaders := http.Header{} extraHeaders.Add("x-ms-blob-type", blobType) if req.BlobType == "page" { size := fmt.Sprintf("%d", req.Size) extraHeaders.Add("x-ms-blob-content-length", size) } _, _, err := context.performRequest(requestParams{ Method: "PUT", URL: context.GetFileURL(req.Container, req.Filename), APIVersion: "2012-02-12", ExtraHeaders: extraHeaders, ExpectedStatus: http.StatusCreated, }) if err != nil { msg := fmt.Sprintf("failed to create blob %s: ", req.Filename) return extendError(err, msg) } return nil } type PutPageRequest struct { Container string // Container name in the storage account Filename string // The blob's file name StartRange int // Must be modulo 512, or an error is returned. EndRange int // Must be (modulo 512)-1, or an error is returned. Data io.Reader // The data to upload to the page. } // Send a request to add a range of data into a page blob. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee691975.aspx func (context *StorageContext) PutPage(req *PutPageRequest) error { validStart := (req.StartRange % 512) == 0 validEnd := (req.EndRange % 512) == 511 if !(validStart && validEnd) { return fmt.Errorf( "StartRange must be a multiple of 512, EndRange must be one less than a multiple of 512") } uri := addURLQueryParams( context.GetFileURL(req.Container, req.Filename), "comp", "page") extraHeaders := http.Header{} rangeData := fmt.Sprintf("bytes=%d-%d", req.StartRange, req.EndRange) extraHeaders.Add("x-ms-range", rangeData) extraHeaders.Add("x-ms-page-write", "update") _, _, err := context.performRequest(requestParams{ Method: "PUT", URL: uri, Body: req.Data, APIVersion: "2012-02-12", ExtraHeaders: extraHeaders, ExpectedStatus: http.StatusCreated, }) if err != nil { msg := fmt.Sprintf("failed to put page for file %s: ", req.Filename) return extendError(err, msg) } return nil } // Send a request to fetch the list of blocks that have been uploaded as part // of a block blob. func (context *StorageContext) GetBlockList(container, filename string) (*GetBlockList, error) { uri := addURLQueryParams( context.GetFileURL(container, filename), "comp", "blocklist", "blocklisttype", "all") bl := GetBlockList{} _, _, err := context.performRequest(requestParams{ Method: "GET", URL: uri, APIVersion: "2012-02-12", Result: &bl, ExpectedStatus: http.StatusOK, }) if err != nil { msg := fmt.Sprintf("request for block list in file %s failed: ", filename) return nil, extendError(err, msg) } return &bl, nil } // Send a request to create a new block. The request payload contains the // data block to upload. func (context *StorageContext) PutBlock(container, filename, id string, data io.Reader) error { base64ID := base64.StdEncoding.EncodeToString([]byte(id)) uri := addURLQueryParams( context.GetFileURL(container, filename), "comp", "block", "blockid", base64ID) _, _, err := context.performRequest(requestParams{ Method: "PUT", URL: uri, Body: data, APIVersion: "2012-02-12", ExpectedStatus: http.StatusCreated, }) if err != nil { msg := fmt.Sprintf("failed to put block %s for file %s: ", id, filename) return extendError(err, msg) } return nil } // Send a request to piece together blocks into a list that specifies a blob. func (context *StorageContext) PutBlockList(container, filename string, blocklist *BlockList) error { uri := addURLQueryParams( context.GetFileURL(container, filename), "comp", "blocklist") data, err := blocklist.Serialize() if err != nil { return err } dataReader := bytes.NewReader(data) _, _, err = context.performRequest(requestParams{ Method: "PUT", URL: uri, Body: dataReader, APIVersion: "2012-02-12", ExpectedStatus: http.StatusCreated, }) if err != nil { msg := fmt.Sprintf("failed to put blocklist for file %s: ", filename) return extendError(err, msg) } return nil } // Delete the specified blob from the given container. Deleting a non-existant // blob will return without an error. func (context *StorageContext) DeleteBlob(container, filename string) error { _, _, err := context.performRequest(requestParams{ Method: "DELETE", URL: context.GetFileURL(container, filename), APIVersion: "2012-02-12", ExpectedStatus: http.StatusAccepted, }) if err != nil { // If the error is an Azure 404 error, return silently: the blob does // not exist. if IsNotFoundError(err) { return nil } msg := fmt.Sprintf("failed to delete blob %s: ", filename) return extendError(err, msg) } return nil } // Get the specified blob from the given container. func (context *StorageContext) GetBlob(container, filename string) (io.ReadCloser, error) { body, _, err := context.performRequest(requestParams{ Method: "GET", URL: context.GetFileURL(container, filename), APIVersion: "2012-02-12", ExpectedStatus: http.StatusOK, }) if err != nil { msg := fmt.Sprintf("failed to get blob %q: ", filename) return nil, extendError(err, msg) } return ioutil.NopCloser(bytes.NewBuffer(body)), nil } type SetContainerACLRequest struct { Container string // Container name in the storage account Access string // "container", "blob", or "private" } // SetContainerACL sets the specified container's access rights. // See http://msdn.microsoft.com/en-us/library/windowsazure/dd179391.aspx func (context *StorageContext) SetContainerACL(req *SetContainerACLRequest) error { uri := addURLQueryParams( context.getContainerURL(req.Container), "restype", "container", "comp", "acl") extraHeaders := http.Header{} switch req.Access { case "container", "blob": extraHeaders.Add("x-ms-blob-public-access", req.Access) case "private": // Don't add a header, Azure resets to private if it's omitted. default: panic("Access must be one of 'container', 'blob' or 'private'") } _, _, err := context.performRequest(requestParams{ Method: "PUT", URL: uri, APIVersion: "2009-09-19", ExtraHeaders: extraHeaders, ExpectedStatus: http.StatusOK, }) if err != nil { msg := fmt.Sprintf("failed to set ACL for container %s: ", req.Container) return extendError(err, msg) } return nil } �������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/������������������������������������������������������0000755�0000153�0000161�00000000000�12321735761�021022� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/LICENSE�����������������������������������������������0000644�0000153�0000161�00000002707�12321735761�022035� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ���������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/��������������������������������������������������0000755�0000153�0000161�00000000000�12321735761�021624� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/key_agreement.go����������������������������������0000644�0000153�0000161�00000021135�12321735761�024774� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 tls import ( "crypto" "crypto/elliptic" "crypto/md5" "crypto/rsa" "crypto/sha1" "crypto/x509" "errors" "io" "math/big" ) // rsaKeyAgreement implements the standard TLS key agreement where the client // encrypts the pre-master secret to the server's public key. type rsaKeyAgreement struct{} func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { return nil, nil } func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { preMasterSecret := make([]byte, 48) _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) if err != nil { return nil, err } if len(ckx.ciphertext) < 2 { return nil, errors.New("bad ClientKeyExchange") } ciphertext := ckx.ciphertext if version != versionSSL30 { ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) if ciphertextLen != len(ckx.ciphertext)-2 { return nil, errors.New("bad ClientKeyExchange") } ciphertext = ckx.ciphertext[2:] } err = rsa.DecryptPKCS1v15SessionKey(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret) if err != nil { return nil, err } // We don't check the version number in the premaster secret. For one, // by checking it, we would leak information about the validity of the // encrypted pre-master secret. Secondly, it provides only a small // benefit against a downgrade attack and some implementations send the // wrong version anyway. See the discussion at the end of section // 7.4.7.1 of RFC 4346. return preMasterSecret, nil } func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { return errors.New("unexpected ServerKeyExchange") } func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { preMasterSecret := make([]byte, 48) preMasterSecret[0] = byte(clientHello.vers >> 8) preMasterSecret[1] = byte(clientHello.vers) _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) if err != nil { return nil, nil, err } encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), preMasterSecret) if err != nil { return nil, nil, err } ckx := new(clientKeyExchangeMsg) ckx.ciphertext = make([]byte, len(encrypted)+2) ckx.ciphertext[0] = byte(len(encrypted) >> 8) ckx.ciphertext[1] = byte(len(encrypted)) copy(ckx.ciphertext[2:], encrypted) return preMasterSecret, ckx, nil } // md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the // concatenation of an MD5 and SHA1 hash. func md5SHA1Hash(slices ...[]byte) []byte { md5sha1 := make([]byte, md5.Size+sha1.Size) hmd5 := md5.New() for _, slice := range slices { hmd5.Write(slice) } copy(md5sha1, hmd5.Sum(nil)) hsha1 := sha1.New() for _, slice := range slices { hsha1.Write(slice) } copy(md5sha1[md5.Size:], hsha1.Sum(nil)) return md5sha1 } // ecdheRSAKeyAgreement implements a TLS key agreement where the server // generates a ephemeral EC public/private key pair and signs it. The // pre-master secret is then calculated using ECDH. type ecdheRSAKeyAgreement struct { privateKey []byte curve elliptic.Curve x, y *big.Int } func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { var curveid uint16 Curve: for _, c := range clientHello.supportedCurves { switch c { case curveP256: ka.curve = elliptic.P256() curveid = c break Curve case curveP384: ka.curve = elliptic.P384() curveid = c break Curve case curveP521: ka.curve = elliptic.P521() curveid = c break Curve } } if curveid == 0 { return nil, errors.New("tls: no supported elliptic curves offered") } var x, y *big.Int var err error ka.privateKey, x, y, err = elliptic.GenerateKey(ka.curve, config.rand()) if err != nil { return nil, err } ecdhePublic := elliptic.Marshal(ka.curve, x, y) // http://tools.ietf.org/html/rfc4492#section-5.4 serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic)) serverECDHParams[0] = 3 // named curve serverECDHParams[1] = byte(curveid >> 8) serverECDHParams[2] = byte(curveid) serverECDHParams[3] = byte(len(ecdhePublic)) copy(serverECDHParams[4:], ecdhePublic) md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams) sig, err := rsa.SignPKCS1v15(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1) if err != nil { return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) } skx := new(serverKeyExchangeMsg) skx.key = make([]byte, len(serverECDHParams)+2+len(sig)) copy(skx.key, serverECDHParams) k := skx.key[len(serverECDHParams):] k[0] = byte(len(sig) >> 8) k[1] = byte(len(sig)) copy(k[2:], sig) return skx, nil } func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { return nil, errors.New("bad ClientKeyExchange") } x, y := elliptic.Unmarshal(ka.curve, ckx.ciphertext[1:]) if x == nil { return nil, errors.New("bad ClientKeyExchange") } x, _ = ka.curve.ScalarMult(x, y, ka.privateKey) preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3) xBytes := x.Bytes() copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) return preMasterSecret, nil } var errServerKeyExchange = errors.New("invalid ServerKeyExchange") func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { if len(skx.key) < 4 { return errServerKeyExchange } if skx.key[0] != 3 { // named curve return errors.New("server selected unsupported curve") } curveid := uint16(skx.key[1])<<8 | uint16(skx.key[2]) switch curveid { case curveP256: ka.curve = elliptic.P256() case curveP384: ka.curve = elliptic.P384() case curveP521: ka.curve = elliptic.P521() default: return errors.New("server selected unsupported curve") } publicLen := int(skx.key[3]) if publicLen+4 > len(skx.key) { return errServerKeyExchange } ka.x, ka.y = elliptic.Unmarshal(ka.curve, skx.key[4:4+publicLen]) if ka.x == nil { return errServerKeyExchange } serverECDHParams := skx.key[:4+publicLen] sig := skx.key[4+publicLen:] if len(sig) < 2 { return errServerKeyExchange } sigLen := int(sig[0])<<8 | int(sig[1]) if sigLen+2 != len(sig) { return errServerKeyExchange } sig = sig[2:] md5sha1 := md5SHA1Hash(clientHello.random, serverHello.random, serverECDHParams) return rsa.VerifyPKCS1v15(cert.PublicKey.(*rsa.PublicKey), crypto.MD5SHA1, md5sha1, sig) } func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { if ka.curve == nil { return nil, nil, errors.New("missing ServerKeyExchange message") } priv, mx, my, err := elliptic.GenerateKey(ka.curve, config.rand()) if err != nil { return nil, nil, err } x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv) preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3) xBytes := x.Bytes() copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) serialized := elliptic.Marshal(ka.curve, mx, my) ckx := new(clientKeyExchangeMsg) ckx.ciphertext = make([]byte, 1+len(serialized)) ckx.ciphertext[0] = byte(len(serialized)) copy(ckx.ciphertext[1:], serialized) return preMasterSecret, ckx, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/handshake_server.go�������������������������������0000644�0000153�0000161�00000026204�12321735761�025473� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package tls import ( "crypto" "crypto/rsa" "crypto/subtle" "crypto/x509" "errors" "io" ) func (c *Conn) serverHandshake() error { config := c.config msg, err := c.readHandshake() if err != nil { return err } clientHello, ok := msg.(*clientHelloMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) } vers, ok := mutualVersion(clientHello.vers) if !ok { return c.sendAlert(alertProtocolVersion) } c.vers = vers c.haveVers = true finishedHash := newFinishedHash(vers) finishedHash.Write(clientHello.marshal()) hello := new(serverHelloMsg) supportedCurve := false Curves: for _, curve := range clientHello.supportedCurves { switch curve { case curveP256, curveP384, curveP521: supportedCurve = true break Curves } } supportedPointFormat := false for _, pointFormat := range clientHello.supportedPoints { if pointFormat == pointFormatUncompressed { supportedPointFormat = true break } } ellipticOk := supportedCurve && supportedPointFormat var suite *cipherSuite FindCipherSuite: for _, id := range clientHello.cipherSuites { for _, supported := range config.cipherSuites() { if id == supported { var candidate *cipherSuite for _, s := range cipherSuites { if s.id == id { candidate = s break } } if candidate == nil { continue } // Don't select a ciphersuite which we can't // support for this client. if candidate.elliptic && !ellipticOk { continue } suite = candidate break FindCipherSuite } } } foundCompression := false // We only support null compression, so check that the client offered it. for _, compression := range clientHello.compressionMethods { if compression == compressionNone { foundCompression = true break } } if suite == nil || !foundCompression { return c.sendAlert(alertHandshakeFailure) } hello.vers = vers hello.cipherSuite = suite.id t := uint32(config.time().Unix()) hello.random = make([]byte, 32) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) hello.random[2] = byte(t >> 8) hello.random[3] = byte(t) _, err = io.ReadFull(config.rand(), hello.random[4:]) if err != nil { return c.sendAlert(alertInternalError) } hello.compressionMethod = compressionNone if clientHello.nextProtoNeg { hello.nextProtoNeg = true hello.nextProtos = config.NextProtos } if len(config.Certificates) == 0 { return c.sendAlert(alertInternalError) } cert := &config.Certificates[0] if len(clientHello.serverName) > 0 { c.serverName = clientHello.serverName cert = config.getCertificateForName(clientHello.serverName) } if clientHello.ocspStapling && len(cert.OCSPStaple) > 0 { hello.ocspStapling = true } finishedHash.Write(hello.marshal()) c.writeRecord(recordTypeHandshake, hello.marshal()) certMsg := new(certificateMsg) certMsg.certificates = cert.Certificate finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) if hello.ocspStapling { certStatus := new(certificateStatusMsg) certStatus.statusType = statusTypeOCSP certStatus.response = cert.OCSPStaple finishedHash.Write(certStatus.marshal()) c.writeRecord(recordTypeHandshake, certStatus.marshal()) } keyAgreement := suite.ka() skx, err := keyAgreement.generateServerKeyExchange(config, cert, clientHello, hello) if err != nil { c.sendAlert(alertHandshakeFailure) return err } if skx != nil { finishedHash.Write(skx.marshal()) c.writeRecord(recordTypeHandshake, skx.marshal()) } if config.ClientAuth >= RequestClientCert { // Request a client certificate certReq := new(certificateRequestMsg) certReq.certificateTypes = []byte{certTypeRSASign} // An empty list of certificateAuthorities signals to // the client that it may send any certificate in response // to our request. When we know the CAs we trust, then // we can send them down, so that the client can choose // an appropriate certificate to give to us. if config.ClientCAs != nil { certReq.certificateAuthorities = config.ClientCAs.Subjects() } finishedHash.Write(certReq.marshal()) c.writeRecord(recordTypeHandshake, certReq.marshal()) } helloDone := new(serverHelloDoneMsg) finishedHash.Write(helloDone.marshal()) c.writeRecord(recordTypeHandshake, helloDone.marshal()) var pub *rsa.PublicKey // public key for client auth, if any msg, err = c.readHandshake() if err != nil { return err } // If we requested a client certificate, then the client must send a // certificate message, even if it's empty. if config.ClientAuth >= RequestClientCert { if certMsg, ok = msg.(*certificateMsg); !ok { return c.sendAlert(alertHandshakeFailure) } finishedHash.Write(certMsg.marshal()) if len(certMsg.certificates) == 0 { // The client didn't actually send a certificate switch config.ClientAuth { case RequireAnyClientCert, RequireAndVerifyClientCert: c.sendAlert(alertBadCertificate) return errors.New("tls: client didn't provide a certificate") } } certs := make([]*x509.Certificate, len(certMsg.certificates)) for i, asn1Data := range certMsg.certificates { if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { c.sendAlert(alertBadCertificate) return errors.New("tls: failed to parse client certificate: " + err.Error()) } } if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { opts := x509.VerifyOptions{ Roots: c.config.ClientCAs, CurrentTime: c.config.time(), Intermediates: x509.NewCertPool(), } for i, cert := range certs { if i == 0 { continue } opts.Intermediates.AddCert(cert) } chains, err := certs[0].Verify(opts) if err != nil { c.sendAlert(alertBadCertificate) return errors.New("tls: failed to verify client's certificate: " + err.Error()) } ok := false for _, ku := range certs[0].ExtKeyUsage { if ku == x509.ExtKeyUsageClientAuth { ok = true break } } if !ok { c.sendAlert(alertHandshakeFailure) return errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication") } c.verifiedChains = chains } if len(certs) > 0 { if pub, ok = certs[0].PublicKey.(*rsa.PublicKey); !ok { return c.sendAlert(alertUnsupportedCertificate) } c.peerCertificates = certs } msg, err = c.readHandshake() if err != nil { return err } } // Get client key exchange ckx, ok := msg.(*clientKeyExchangeMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) } finishedHash.Write(ckx.marshal()) // If we received a client cert in response to our certificate request message, // the client will send us a certificateVerifyMsg immediately after the // clientKeyExchangeMsg. This message is a MD5SHA1 digest of all preceding // handshake-layer messages that is signed using the private key corresponding // to the client's certificate. This allows us to verify that the client is in // possession of the private key of the certificate. if len(c.peerCertificates) > 0 { msg, err = c.readHandshake() if err != nil { return err } certVerify, ok := msg.(*certificateVerifyMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) } digest := make([]byte, 0, 36) digest = finishedHash.serverMD5.Sum(digest) digest = finishedHash.serverSHA1.Sum(digest) err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) if err != nil { c.sendAlert(alertBadCertificate) return errors.New("could not validate signature of connection nonces: " + err.Error()) } finishedHash.Write(certVerify.marshal()) } preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers) if err != nil { c.sendAlert(alertHandshakeFailure) return err } masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */) clientHash := suite.mac(c.vers, clientMAC) c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) c.readRecord(recordTypeChangeCipherSpec) if err := c.error(); err != nil { return err } if hello.nextProtoNeg { msg, err = c.readHandshake() if err != nil { return err } nextProto, ok := msg.(*nextProtoMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) } finishedHash.Write(nextProto.marshal()) c.clientProtocol = nextProto.proto } msg, err = c.readHandshake() if err != nil { return err } clientFinished, ok := msg.(*finishedMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) } verify := finishedHash.clientSum(masterSecret) if len(verify) != len(clientFinished.verifyData) || subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 { return c.sendAlert(alertHandshakeFailure) } finishedHash.Write(clientFinished.marshal()) serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */) serverHash := suite.mac(c.vers, serverMAC) c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) finished := new(finishedMsg) finished.verifyData = finishedHash.serverSum(masterSecret) c.writeRecord(recordTypeHandshake, finished.marshal()) c.handshakeComplete = true c.cipherSuite = suite.id return nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/handshake_messages.go�����������������������������0000644�0000153�0000161�00000062555�12321735761�026005� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package tls import "bytes" type clientHelloMsg struct { raw []byte vers uint16 random []byte sessionId []byte cipherSuites []uint16 compressionMethods []uint8 nextProtoNeg bool serverName string ocspStapling bool supportedCurves []uint16 supportedPoints []uint8 } func (m *clientHelloMsg) equal(i interface{}) bool { m1, ok := i.(*clientHelloMsg) if !ok { return false } return bytes.Equal(m.raw, m1.raw) && m.vers == m1.vers && bytes.Equal(m.random, m1.random) && bytes.Equal(m.sessionId, m1.sessionId) && eqUint16s(m.cipherSuites, m1.cipherSuites) && bytes.Equal(m.compressionMethods, m1.compressionMethods) && m.nextProtoNeg == m1.nextProtoNeg && m.serverName == m1.serverName && m.ocspStapling == m1.ocspStapling && eqUint16s(m.supportedCurves, m1.supportedCurves) && bytes.Equal(m.supportedPoints, m1.supportedPoints) } func (m *clientHelloMsg) marshal() []byte { if m.raw != nil { return m.raw } length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods) numExtensions := 0 extensionsLength := 0 if m.nextProtoNeg { numExtensions++ } if m.ocspStapling { extensionsLength += 1 + 2 + 2 numExtensions++ } if len(m.serverName) > 0 { extensionsLength += 5 + len(m.serverName) numExtensions++ } if len(m.supportedCurves) > 0 { extensionsLength += 2 + 2*len(m.supportedCurves) numExtensions++ } if len(m.supportedPoints) > 0 { extensionsLength += 1 + len(m.supportedPoints) numExtensions++ } if numExtensions > 0 { extensionsLength += 4 * numExtensions length += 2 + extensionsLength } x := make([]byte, 4+length) x[0] = typeClientHello x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) x[4] = uint8(m.vers >> 8) x[5] = uint8(m.vers) copy(x[6:38], m.random) x[38] = uint8(len(m.sessionId)) copy(x[39:39+len(m.sessionId)], m.sessionId) y := x[39+len(m.sessionId):] y[0] = uint8(len(m.cipherSuites) >> 7) y[1] = uint8(len(m.cipherSuites) << 1) for i, suite := range m.cipherSuites { y[2+i*2] = uint8(suite >> 8) y[3+i*2] = uint8(suite) } z := y[2+len(m.cipherSuites)*2:] z[0] = uint8(len(m.compressionMethods)) copy(z[1:], m.compressionMethods) z = z[1+len(m.compressionMethods):] if numExtensions > 0 { z[0] = byte(extensionsLength >> 8) z[1] = byte(extensionsLength) z = z[2:] } if m.nextProtoNeg { z[0] = byte(extensionNextProtoNeg >> 8) z[1] = byte(extensionNextProtoNeg) // The length is always 0 z = z[4:] } if len(m.serverName) > 0 { z[0] = byte(extensionServerName >> 8) z[1] = byte(extensionServerName) l := len(m.serverName) + 5 z[2] = byte(l >> 8) z[3] = byte(l) z = z[4:] // RFC 3546, section 3.1 // // struct { // NameType name_type; // select (name_type) { // case host_name: HostName; // } name; // } ServerName; // // enum { // host_name(0), (255) // } NameType; // // opaque HostName<1..2^16-1>; // // struct { // ServerName server_name_list<1..2^16-1> // } ServerNameList; z[0] = byte((len(m.serverName) + 3) >> 8) z[1] = byte(len(m.serverName) + 3) z[3] = byte(len(m.serverName) >> 8) z[4] = byte(len(m.serverName)) copy(z[5:], []byte(m.serverName)) z = z[l:] } if m.ocspStapling { // RFC 4366, section 3.6 z[0] = byte(extensionStatusRequest >> 8) z[1] = byte(extensionStatusRequest) z[2] = 0 z[3] = 5 z[4] = 1 // OCSP type // Two zero valued uint16s for the two lengths. z = z[9:] } if len(m.supportedCurves) > 0 { // http://tools.ietf.org/html/rfc4492#section-5.5.1 z[0] = byte(extensionSupportedCurves >> 8) z[1] = byte(extensionSupportedCurves) l := 2 + 2*len(m.supportedCurves) z[2] = byte(l >> 8) z[3] = byte(l) l -= 2 z[4] = byte(l >> 8) z[5] = byte(l) z = z[6:] for _, curve := range m.supportedCurves { z[0] = byte(curve >> 8) z[1] = byte(curve) z = z[2:] } } if len(m.supportedPoints) > 0 { // http://tools.ietf.org/html/rfc4492#section-5.5.2 z[0] = byte(extensionSupportedPoints >> 8) z[1] = byte(extensionSupportedPoints) l := 1 + len(m.supportedPoints) z[2] = byte(l >> 8) z[3] = byte(l) l-- z[4] = byte(l) z = z[5:] for _, pointFormat := range m.supportedPoints { z[0] = byte(pointFormat) z = z[1:] } } m.raw = x return x } func (m *clientHelloMsg) unmarshal(data []byte) bool { if len(data) < 42 { return false } m.raw = data m.vers = uint16(data[4])<<8 | uint16(data[5]) m.random = data[6:38] sessionIdLen := int(data[38]) if sessionIdLen > 32 || len(data) < 39+sessionIdLen { return false } m.sessionId = data[39 : 39+sessionIdLen] data = data[39+sessionIdLen:] if len(data) < 2 { return false } // cipherSuiteLen is the number of bytes of cipher suite numbers. Since // they are uint16s, the number must be even. cipherSuiteLen := int(data[0])<<8 | int(data[1]) if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { return false } numCipherSuites := cipherSuiteLen / 2 m.cipherSuites = make([]uint16, numCipherSuites) for i := 0; i < numCipherSuites; i++ { m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i]) } data = data[2+cipherSuiteLen:] if len(data) < 1 { return false } compressionMethodsLen := int(data[0]) if len(data) < 1+compressionMethodsLen { return false } m.compressionMethods = data[1 : 1+compressionMethodsLen] data = data[1+compressionMethodsLen:] m.nextProtoNeg = false m.serverName = "" m.ocspStapling = false if len(data) == 0 { // ClientHello is optionally followed by extension data return true } if len(data) < 2 { return false } extensionsLength := int(data[0])<<8 | int(data[1]) data = data[2:] if extensionsLength != len(data) { return false } for len(data) != 0 { if len(data) < 4 { return false } extension := uint16(data[0])<<8 | uint16(data[1]) length := int(data[2])<<8 | int(data[3]) data = data[4:] if len(data) < length { return false } switch extension { case extensionServerName: if length < 2 { return false } numNames := int(data[0])<<8 | int(data[1]) d := data[2:] for i := 0; i < numNames; i++ { if len(d) < 3 { return false } nameType := d[0] nameLen := int(d[1])<<8 | int(d[2]) d = d[3:] if len(d) < nameLen { return false } if nameType == 0 { m.serverName = string(d[0:nameLen]) break } d = d[nameLen:] } case extensionNextProtoNeg: if length > 0 { return false } m.nextProtoNeg = true case extensionStatusRequest: m.ocspStapling = length > 0 && data[0] == statusTypeOCSP case extensionSupportedCurves: // http://tools.ietf.org/html/rfc4492#section-5.5.1 if length < 2 { return false } l := int(data[0])<<8 | int(data[1]) if l%2 == 1 || length != l+2 { return false } numCurves := l / 2 m.supportedCurves = make([]uint16, numCurves) d := data[2:] for i := 0; i < numCurves; i++ { m.supportedCurves[i] = uint16(d[0])<<8 | uint16(d[1]) d = d[2:] } case extensionSupportedPoints: // http://tools.ietf.org/html/rfc4492#section-5.5.2 if length < 1 { return false } l := int(data[0]) if length != l+1 { return false } m.supportedPoints = make([]uint8, l) copy(m.supportedPoints, data[1:]) } data = data[length:] } return true } type serverHelloMsg struct { raw []byte vers uint16 random []byte sessionId []byte cipherSuite uint16 compressionMethod uint8 nextProtoNeg bool nextProtos []string ocspStapling bool } func (m *serverHelloMsg) equal(i interface{}) bool { m1, ok := i.(*serverHelloMsg) if !ok { return false } return bytes.Equal(m.raw, m1.raw) && m.vers == m1.vers && bytes.Equal(m.random, m1.random) && bytes.Equal(m.sessionId, m1.sessionId) && m.cipherSuite == m1.cipherSuite && m.compressionMethod == m1.compressionMethod && m.nextProtoNeg == m1.nextProtoNeg && eqStrings(m.nextProtos, m1.nextProtos) && m.ocspStapling == m1.ocspStapling } func (m *serverHelloMsg) marshal() []byte { if m.raw != nil { return m.raw } length := 38 + len(m.sessionId) numExtensions := 0 extensionsLength := 0 nextProtoLen := 0 if m.nextProtoNeg { numExtensions++ for _, v := range m.nextProtos { nextProtoLen += len(v) } nextProtoLen += len(m.nextProtos) extensionsLength += nextProtoLen } if m.ocspStapling { numExtensions++ } if numExtensions > 0 { extensionsLength += 4 * numExtensions length += 2 + extensionsLength } x := make([]byte, 4+length) x[0] = typeServerHello x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) x[4] = uint8(m.vers >> 8) x[5] = uint8(m.vers) copy(x[6:38], m.random) x[38] = uint8(len(m.sessionId)) copy(x[39:39+len(m.sessionId)], m.sessionId) z := x[39+len(m.sessionId):] z[0] = uint8(m.cipherSuite >> 8) z[1] = uint8(m.cipherSuite) z[2] = uint8(m.compressionMethod) z = z[3:] if numExtensions > 0 { z[0] = byte(extensionsLength >> 8) z[1] = byte(extensionsLength) z = z[2:] } if m.nextProtoNeg { z[0] = byte(extensionNextProtoNeg >> 8) z[1] = byte(extensionNextProtoNeg) z[2] = byte(nextProtoLen >> 8) z[3] = byte(nextProtoLen) z = z[4:] for _, v := range m.nextProtos { l := len(v) if l > 255 { l = 255 } z[0] = byte(l) copy(z[1:], []byte(v[0:l])) z = z[1+l:] } } if m.ocspStapling { z[0] = byte(extensionStatusRequest >> 8) z[1] = byte(extensionStatusRequest) z = z[4:] } m.raw = x return x } func (m *serverHelloMsg) unmarshal(data []byte) bool { if len(data) < 42 { return false } m.raw = data m.vers = uint16(data[4])<<8 | uint16(data[5]) m.random = data[6:38] sessionIdLen := int(data[38]) if sessionIdLen > 32 || len(data) < 39+sessionIdLen { return false } m.sessionId = data[39 : 39+sessionIdLen] data = data[39+sessionIdLen:] if len(data) < 3 { return false } m.cipherSuite = uint16(data[0])<<8 | uint16(data[1]) m.compressionMethod = data[2] data = data[3:] m.nextProtoNeg = false m.nextProtos = nil m.ocspStapling = false if len(data) == 0 { // ServerHello is optionally followed by extension data return true } if len(data) < 2 { return false } extensionsLength := int(data[0])<<8 | int(data[1]) data = data[2:] if len(data) != extensionsLength { return false } for len(data) != 0 { if len(data) < 4 { return false } extension := uint16(data[0])<<8 | uint16(data[1]) length := int(data[2])<<8 | int(data[3]) data = data[4:] if len(data) < length { return false } switch extension { case extensionNextProtoNeg: m.nextProtoNeg = true d := data for len(d) > 0 { l := int(d[0]) d = d[1:] if l == 0 || l > len(d) { return false } m.nextProtos = append(m.nextProtos, string(d[0:l])) d = d[l:] } case extensionStatusRequest: if length > 0 { return false } m.ocspStapling = true } data = data[length:] } return true } type certificateMsg struct { raw []byte certificates [][]byte } func (m *certificateMsg) equal(i interface{}) bool { m1, ok := i.(*certificateMsg) if !ok { return false } return bytes.Equal(m.raw, m1.raw) && eqByteSlices(m.certificates, m1.certificates) } func (m *certificateMsg) marshal() (x []byte) { if m.raw != nil { return m.raw } var i int for _, slice := range m.certificates { i += len(slice) } length := 3 + 3*len(m.certificates) + i x = make([]byte, 4+length) x[0] = typeCertificate x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) certificateOctets := length - 3 x[4] = uint8(certificateOctets >> 16) x[5] = uint8(certificateOctets >> 8) x[6] = uint8(certificateOctets) y := x[7:] for _, slice := range m.certificates { y[0] = uint8(len(slice) >> 16) y[1] = uint8(len(slice) >> 8) y[2] = uint8(len(slice)) copy(y[3:], slice) y = y[3+len(slice):] } m.raw = x return } func (m *certificateMsg) unmarshal(data []byte) bool { if len(data) < 7 { return false } m.raw = data certsLen := uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6]) if uint32(len(data)) != certsLen+7 { return false } numCerts := 0 d := data[7:] for certsLen > 0 { if len(d) < 4 { return false } certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2]) if uint32(len(d)) < 3+certLen { return false } d = d[3+certLen:] certsLen -= 3 + certLen numCerts++ } m.certificates = make([][]byte, numCerts) d = data[7:] for i := 0; i < numCerts; i++ { certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2]) m.certificates[i] = d[3 : 3+certLen] d = d[3+certLen:] } return true } type serverKeyExchangeMsg struct { raw []byte key []byte } func (m *serverKeyExchangeMsg) equal(i interface{}) bool { m1, ok := i.(*serverKeyExchangeMsg) if !ok { return false } return bytes.Equal(m.raw, m1.raw) && bytes.Equal(m.key, m1.key) } func (m *serverKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw } length := len(m.key) x := make([]byte, length+4) x[0] = typeServerKeyExchange x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) copy(x[4:], m.key) m.raw = x return x } func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool { m.raw = data if len(data) < 4 { return false } m.key = data[4:] return true } type certificateStatusMsg struct { raw []byte statusType uint8 response []byte } func (m *certificateStatusMsg) equal(i interface{}) bool { m1, ok := i.(*certificateStatusMsg) if !ok { return false } return bytes.Equal(m.raw, m1.raw) && m.statusType == m1.statusType && bytes.Equal(m.response, m1.response) } func (m *certificateStatusMsg) marshal() []byte { if m.raw != nil { return m.raw } var x []byte if m.statusType == statusTypeOCSP { x = make([]byte, 4+4+len(m.response)) x[0] = typeCertificateStatus l := len(m.response) + 4 x[1] = byte(l >> 16) x[2] = byte(l >> 8) x[3] = byte(l) x[4] = statusTypeOCSP l -= 4 x[5] = byte(l >> 16) x[6] = byte(l >> 8) x[7] = byte(l) copy(x[8:], m.response) } else { x = []byte{typeCertificateStatus, 0, 0, 1, m.statusType} } m.raw = x return x } func (m *certificateStatusMsg) unmarshal(data []byte) bool { m.raw = data if len(data) < 5 { return false } m.statusType = data[4] m.response = nil if m.statusType == statusTypeOCSP { if len(data) < 8 { return false } respLen := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7]) if uint32(len(data)) != 4+4+respLen { return false } m.response = data[8:] } return true } type serverHelloDoneMsg struct{} func (m *serverHelloDoneMsg) equal(i interface{}) bool { _, ok := i.(*serverHelloDoneMsg) return ok } func (m *serverHelloDoneMsg) marshal() []byte { x := make([]byte, 4) x[0] = typeServerHelloDone return x } func (m *serverHelloDoneMsg) unmarshal(data []byte) bool { return len(data) == 4 } type clientKeyExchangeMsg struct { raw []byte ciphertext []byte } func (m *clientKeyExchangeMsg) equal(i interface{}) bool { m1, ok := i.(*clientKeyExchangeMsg) if !ok { return false } return bytes.Equal(m.raw, m1.raw) && bytes.Equal(m.ciphertext, m1.ciphertext) } func (m *clientKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw } length := len(m.ciphertext) x := make([]byte, length+4) x[0] = typeClientKeyExchange x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) copy(x[4:], m.ciphertext) m.raw = x return x } func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool { m.raw = data if len(data) < 4 { return false } l := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) if l != len(data)-4 { return false } m.ciphertext = data[4:] return true } type finishedMsg struct { raw []byte verifyData []byte } func (m *finishedMsg) equal(i interface{}) bool { m1, ok := i.(*finishedMsg) if !ok { return false } return bytes.Equal(m.raw, m1.raw) && bytes.Equal(m.verifyData, m1.verifyData) } func (m *finishedMsg) marshal() (x []byte) { if m.raw != nil { return m.raw } x = make([]byte, 4+len(m.verifyData)) x[0] = typeFinished x[3] = byte(len(m.verifyData)) copy(x[4:], m.verifyData) m.raw = x return } func (m *finishedMsg) unmarshal(data []byte) bool { m.raw = data if len(data) < 4 { return false } m.verifyData = data[4:] return true } type nextProtoMsg struct { raw []byte proto string } func (m *nextProtoMsg) equal(i interface{}) bool { m1, ok := i.(*nextProtoMsg) if !ok { return false } return bytes.Equal(m.raw, m1.raw) && m.proto == m1.proto } func (m *nextProtoMsg) marshal() []byte { if m.raw != nil { return m.raw } l := len(m.proto) if l > 255 { l = 255 } padding := 32 - (l+2)%32 length := l + padding + 2 x := make([]byte, length+4) x[0] = typeNextProtocol x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) y := x[4:] y[0] = byte(l) copy(y[1:], []byte(m.proto[0:l])) y = y[1+l:] y[0] = byte(padding) m.raw = x return x } func (m *nextProtoMsg) unmarshal(data []byte) bool { m.raw = data if len(data) < 5 { return false } data = data[4:] protoLen := int(data[0]) data = data[1:] if len(data) < protoLen { return false } m.proto = string(data[0:protoLen]) data = data[protoLen:] if len(data) < 1 { return false } paddingLen := int(data[0]) data = data[1:] if len(data) != paddingLen { return false } return true } type certificateRequestMsg struct { raw []byte certificateTypes []byte certificateAuthorities [][]byte } func (m *certificateRequestMsg) equal(i interface{}) bool { m1, ok := i.(*certificateRequestMsg) if !ok { return false } return bytes.Equal(m.raw, m1.raw) && bytes.Equal(m.certificateTypes, m1.certificateTypes) && eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) } func (m *certificateRequestMsg) marshal() (x []byte) { if m.raw != nil { return m.raw } // See http://tools.ietf.org/html/rfc4346#section-7.4.4 length := 1 + len(m.certificateTypes) + 2 casLength := 0 for _, ca := range m.certificateAuthorities { casLength += 2 + len(ca) } length += casLength x = make([]byte, 4+length) x[0] = typeCertificateRequest x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) x[4] = uint8(len(m.certificateTypes)) copy(x[5:], m.certificateTypes) y := x[5+len(m.certificateTypes):] y[0] = uint8(casLength >> 8) y[1] = uint8(casLength) y = y[2:] for _, ca := range m.certificateAuthorities { y[0] = uint8(len(ca) >> 8) y[1] = uint8(len(ca)) y = y[2:] copy(y, ca) y = y[len(ca):] } m.raw = x return } func (m *certificateRequestMsg) unmarshal(data []byte) bool { m.raw = data if len(data) < 5 { return false } length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) if uint32(len(data))-4 != length { return false } numCertTypes := int(data[4]) data = data[5:] if numCertTypes == 0 || len(data) <= numCertTypes { return false } m.certificateTypes = make([]byte, numCertTypes) if copy(m.certificateTypes, data) != numCertTypes { return false } data = data[numCertTypes:] if len(data) < 2 { return false } casLength := uint16(data[0])<<8 | uint16(data[1]) data = data[2:] if len(data) < int(casLength) { return false } cas := make([]byte, casLength) copy(cas, data) data = data[casLength:] m.certificateAuthorities = nil for len(cas) > 0 { if len(cas) < 2 { return false } caLen := uint16(cas[0])<<8 | uint16(cas[1]) cas = cas[2:] if len(cas) < int(caLen) { return false } m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen]) cas = cas[caLen:] } if len(data) > 0 { return false } return true } type certificateVerifyMsg struct { raw []byte signature []byte } func (m *certificateVerifyMsg) equal(i interface{}) bool { m1, ok := i.(*certificateVerifyMsg) if !ok { return false } return bytes.Equal(m.raw, m1.raw) && bytes.Equal(m.signature, m1.signature) } func (m *certificateVerifyMsg) marshal() (x []byte) { if m.raw != nil { return m.raw } // See http://tools.ietf.org/html/rfc4346#section-7.4.8 siglength := len(m.signature) length := 2 + siglength x = make([]byte, 4+length) x[0] = typeCertificateVerify x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) x[4] = uint8(siglength >> 8) x[5] = uint8(siglength) copy(x[6:], m.signature) m.raw = x return } func (m *certificateVerifyMsg) unmarshal(data []byte) bool { m.raw = data if len(data) < 6 { return false } length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) if uint32(len(data))-4 != length { return false } siglength := int(data[4])<<8 + int(data[5]) if len(data)-6 != siglength { return false } m.signature = data[6:] return true } type helloRequestMsg struct { } func (*helloRequestMsg) marshal() []byte { return []byte{typeHelloRequest, 0, 0, 0} } func (*helloRequestMsg) unmarshal(data []byte) bool { return len(data) == 4 } func eqUint16s(x, y []uint16) bool { if len(x) != len(y) { return false } for i, v := range x { if y[i] != v { return false } } return true } func eqStrings(x, y []string) bool { if len(x) != len(y) { return false } for i, v := range x { if y[i] != v { return false } } return true } func eqByteSlices(x, y [][]byte) bool { if len(x) != len(y) { return false } for i, v := range x { if !bytes.Equal(v, y[i]) { return false } } return true } ���������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/prf.go��������������������������������������������0000644�0000153�0000161�00000016145�12321735761�022751� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package tls import ( "crypto/hmac" "crypto/md5" "crypto/sha1" "hash" ) // Split a premaster secret in two as specified in RFC 4346, section 5. func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { s1 = secret[0 : (len(secret)+1)/2] s2 = secret[len(secret)/2:] return } // pHash implements the P_hash function, as defined in RFC 4346, section 5. func pHash(result, secret, seed []byte, hash func() hash.Hash) { h := hmac.New(hash, secret) h.Write(seed) a := h.Sum(nil) j := 0 for j < len(result) { h.Reset() h.Write(a) h.Write(seed) b := h.Sum(nil) todo := len(b) if j+todo > len(result) { todo = len(result) - j } copy(result[j:j+todo], b) j += todo h.Reset() h.Write(a) a = h.Sum(nil) } } // pRF10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, section 5. func pRF10(result, secret, label, seed []byte) { hashSHA1 := sha1.New hashMD5 := md5.New labelAndSeed := make([]byte, len(label)+len(seed)) copy(labelAndSeed, label) copy(labelAndSeed[len(label):], seed) s1, s2 := splitPreMasterSecret(secret) pHash(result, s1, labelAndSeed, hashMD5) result2 := make([]byte, len(result)) pHash(result2, s2, labelAndSeed, hashSHA1) for i, b := range result2 { result[i] ^= b } } // pRF30 implements the SSL 3.0 pseudo-random function, as defined in // www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6. func pRF30(result, secret, label, seed []byte) { hashSHA1 := sha1.New() hashMD5 := md5.New() done := 0 i := 0 // RFC5246 section 6.3 says that the largest PRF output needed is 128 // bytes. Since no more ciphersuites will be added to SSLv3, this will // remain true. Each iteration gives us 16 bytes so 10 iterations will // be sufficient. var b [11]byte for done < len(result) { for j := 0; j <= i; j++ { b[j] = 'A' + byte(i) } hashSHA1.Reset() hashSHA1.Write(b[:i+1]) hashSHA1.Write(secret) hashSHA1.Write(seed) digest := hashSHA1.Sum(nil) hashMD5.Reset() hashMD5.Write(secret) hashMD5.Write(digest) done += copy(result[done:], hashMD5.Sum(nil)) i++ } } const ( tlsRandomLength = 32 // Length of a random nonce in TLS 1.1. masterSecretLength = 48 // Length of a master secret in TLS 1.1. finishedVerifyLength = 12 // Length of verify_data in a Finished message. ) var masterSecretLabel = []byte("master secret") var keyExpansionLabel = []byte("key expansion") var clientFinishedLabel = []byte("client finished") var serverFinishedLabel = []byte("server finished") // keysFromPreMasterSecret generates the connection keys from the pre master // secret, given the lengths of the MAC key, cipher key and IV, as defined in // RFC 2246, section 6.3. func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { prf := pRF10 if version == versionSSL30 { prf = pRF30 } var seed [tlsRandomLength * 2]byte copy(seed[0:len(clientRandom)], clientRandom) copy(seed[len(clientRandom):], serverRandom) masterSecret = make([]byte, masterSecretLength) prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) copy(seed[0:len(clientRandom)], serverRandom) copy(seed[len(serverRandom):], clientRandom) n := 2*macLen + 2*keyLen + 2*ivLen keyMaterial := make([]byte, n) prf(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) clientMAC = keyMaterial[:macLen] keyMaterial = keyMaterial[macLen:] serverMAC = keyMaterial[:macLen] keyMaterial = keyMaterial[macLen:] clientKey = keyMaterial[:keyLen] keyMaterial = keyMaterial[keyLen:] serverKey = keyMaterial[:keyLen] keyMaterial = keyMaterial[keyLen:] clientIV = keyMaterial[:ivLen] keyMaterial = keyMaterial[ivLen:] serverIV = keyMaterial[:ivLen] return } func newFinishedHash(version uint16) finishedHash { return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New(), version} } // A finishedHash calculates the hash of a set of handshake messages suitable // for including in a Finished message. type finishedHash struct { clientMD5 hash.Hash clientSHA1 hash.Hash serverMD5 hash.Hash serverSHA1 hash.Hash version uint16 } func (h finishedHash) Write(msg []byte) (n int, err error) { h.clientMD5.Write(msg) h.clientSHA1.Write(msg) h.serverMD5.Write(msg) h.serverSHA1.Write(msg) return len(msg), nil } // finishedSum10 calculates the contents of the verify_data member of a TLSv1 // Finished message given the MD5 and SHA1 hashes of a set of handshake // messages. func finishedSum10(md5, sha1, label, masterSecret []byte) []byte { seed := make([]byte, len(md5)+len(sha1)) copy(seed, md5) copy(seed[len(md5):], sha1) out := make([]byte, finishedVerifyLength) pRF10(out, masterSecret, label, seed) return out } // finishedSum30 calculates the contents of the verify_data member of a SSLv3 // Finished message given the MD5 and SHA1 hashes of a set of handshake // messages. func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []byte { md5.Write(magic[:]) md5.Write(masterSecret) md5.Write(ssl30Pad1[:]) md5Digest := md5.Sum(nil) md5.Reset() md5.Write(masterSecret) md5.Write(ssl30Pad2[:]) md5.Write(md5Digest) md5Digest = md5.Sum(nil) sha1.Write(magic[:]) sha1.Write(masterSecret) sha1.Write(ssl30Pad1[:40]) sha1Digest := sha1.Sum(nil) sha1.Reset() sha1.Write(masterSecret) sha1.Write(ssl30Pad2[:40]) sha1.Write(sha1Digest) sha1Digest = sha1.Sum(nil) ret := make([]byte, len(md5Digest)+len(sha1Digest)) copy(ret, md5Digest) copy(ret[len(md5Digest):], sha1Digest) return ret } var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54} var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52} // clientSum returns the contents of the verify_data member of a client's // Finished message. func (h finishedHash) clientSum(masterSecret []byte) []byte { if h.version == versionSSL30 { return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic) } md5 := h.clientMD5.Sum(nil) sha1 := h.clientSHA1.Sum(nil) return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret) } // serverSum returns the contents of the verify_data member of a server's // Finished message. func (h finishedHash) serverSum(masterSecret []byte) []byte { if h.version == versionSSL30 { return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic) } md5 := h.serverMD5.Sum(nil) sha1 := h.serverSHA1.Sum(nil) return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/conn.go�������������������������������������������0000644�0000153�0000161�00000061263�12321735761�023120� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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. // TLS low level connection and record layer package tls import ( "bytes" "crypto/cipher" "crypto/subtle" "crypto/x509" "errors" "io" "net" "sync" "time" ) // A Conn represents a secured connection. // It implements the net.Conn interface. type Conn struct { // constant conn net.Conn isClient bool // constant after handshake; protected by handshakeMutex handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex vers uint16 // TLS version haveVers bool // version has been negotiated config *Config // configuration passed to constructor handshakeComplete bool cipherSuite uint16 ocspResponse []byte // stapled OCSP response peerCertificates []*x509.Certificate // verifiedChains contains the certificate chains that we built, as // opposed to the ones presented by the server. verifiedChains [][]*x509.Certificate // serverName contains the server name indicated by the client, if any. serverName string clientProtocol string clientProtocolFallback bool // first permanent error errMutex sync.Mutex err error // input/output in, out halfConn // in.Mutex < out.Mutex rawInput *block // raw input, right off the wire input *block // application data waiting to be read hand bytes.Buffer // handshake data waiting to be read tmp [16]byte } func (c *Conn) setError(err error) error { c.errMutex.Lock() defer c.errMutex.Unlock() if c.err == nil { c.err = err } return err } func (c *Conn) error() error { c.errMutex.Lock() defer c.errMutex.Unlock() return c.err } // Access to net.Conn methods. // Cannot just embed net.Conn because that would // export the struct field too. // LocalAddr returns the local network address. func (c *Conn) LocalAddr() net.Addr { return c.conn.LocalAddr() } // RemoteAddr returns the remote network address. func (c *Conn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } // SetDeadline sets the read and write deadlines associated with the connection. // A zero value for t means Read and Write will not time out. // After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. func (c *Conn) SetDeadline(t time.Time) error { return c.conn.SetDeadline(t) } // SetReadDeadline sets the read deadline on the underlying connection. // A zero value for t means Read will not time out. func (c *Conn) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) } // SetWriteDeadline sets the write deadline on the underlying conneciton. // A zero value for t means Write will not time out. // After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. func (c *Conn) SetWriteDeadline(t time.Time) error { return c.conn.SetWriteDeadline(t) } // A halfConn represents one direction of the record layer // connection, either sending or receiving. type halfConn struct { sync.Mutex version uint16 // protocol version cipher interface{} // cipher algorithm mac macFunction seq [8]byte // 64-bit sequence number bfree *block // list of free blocks nextCipher interface{} // next encryption state nextMac macFunction // next MAC algorithm // used to save allocating a new buffer for each MAC. inDigestBuf, outDigestBuf []byte } // prepareCipherSpec sets the encryption and MAC states // that a subsequent changeCipherSpec will use. func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) { hc.version = version hc.nextCipher = cipher hc.nextMac = mac } // changeCipherSpec changes the encryption and MAC states // to the ones previously passed to prepareCipherSpec. func (hc *halfConn) changeCipherSpec() error { if hc.nextCipher == nil { return alertInternalError } hc.cipher = hc.nextCipher hc.mac = hc.nextMac hc.nextCipher = nil hc.nextMac = nil for i := range hc.seq { hc.seq[i] = 0 } return nil } // incSeq increments the sequence number. func (hc *halfConn) incSeq() { for i := 7; i >= 0; i-- { hc.seq[i]++ if hc.seq[i] != 0 { return } } // Not allowed to let sequence number wrap. // Instead, must renegotiate before it does. // Not likely enough to bother. panic("TLS: sequence number wraparound") } // resetSeq resets the sequence number to zero. func (hc *halfConn) resetSeq() { for i := range hc.seq { hc.seq[i] = 0 } } // removePadding returns an unpadded slice, in constant time, which is a prefix // of the input. It also returns a byte which is equal to 255 if the padding // was valid and 0 otherwise. See RFC 2246, section 6.2.3.2 func removePadding(payload []byte) ([]byte, byte) { if len(payload) < 1 { return payload, 0 } paddingLen := payload[len(payload)-1] t := uint(len(payload)-1) - uint(paddingLen) // if len(payload) >= (paddingLen - 1) then the MSB of t is zero good := byte(int32(^t) >> 31) toCheck := 255 // the maximum possible padding length // The length of the padded data is public, so we can use an if here if toCheck+1 > len(payload) { toCheck = len(payload) - 1 } for i := 0; i < toCheck; i++ { t := uint(paddingLen) - uint(i) // if i <= paddingLen then the MSB of t is zero mask := byte(int32(^t) >> 31) b := payload[len(payload)-1-i] good &^= mask&paddingLen ^ mask&b } // We AND together the bits of good and replicate the result across // all the bits. good &= good << 4 good &= good << 2 good &= good << 1 good = uint8(int8(good) >> 7) toRemove := good&paddingLen + 1 return payload[:len(payload)-int(toRemove)], good } // removePaddingSSL30 is a replacement for removePadding in the case that the // protocol version is SSLv3. In this version, the contents of the padding // are random and cannot be checked. func removePaddingSSL30(payload []byte) ([]byte, byte) { if len(payload) < 1 { return payload, 0 } paddingLen := int(payload[len(payload)-1]) + 1 if paddingLen > len(payload) { return payload, 0 } return payload[:len(payload)-paddingLen], 255 } func roundUp(a, b int) int { return a + (b-a%b)%b } // decrypt checks and strips the mac and decrypts the data in b. func (hc *halfConn) decrypt(b *block) (bool, alert) { // pull out payload payload := b.data[recordHeaderLen:] macSize := 0 if hc.mac != nil { macSize = hc.mac.Size() } paddingGood := byte(255) // decrypt if hc.cipher != nil { switch c := hc.cipher.(type) { case cipher.Stream: c.XORKeyStream(payload, payload) case cipher.BlockMode: blockSize := c.BlockSize() if len(payload)%blockSize != 0 || len(payload) < roundUp(macSize+1, blockSize) { return false, alertBadRecordMAC } c.CryptBlocks(payload, payload) if hc.version == versionSSL30 { payload, paddingGood = removePaddingSSL30(payload) } else { payload, paddingGood = removePadding(payload) } b.resize(recordHeaderLen + len(payload)) // note that we still have a timing side-channel in the // MAC check, below. An attacker can align the record // so that a correct padding will cause one less hash // block to be calculated. Then they can iteratively // decrypt a record by breaking each byte. See // "Password Interception in a SSL/TLS Channel", Brice // Canvel et al. // // However, our behavior matches OpenSSL, so we leak // only as much as they do. default: panic("unknown cipher type") } } // check, strip mac if hc.mac != nil { if len(payload) < macSize { return false, alertBadRecordMAC } // strip mac off payload, b.data n := len(payload) - macSize b.data[3] = byte(n >> 8) b.data[4] = byte(n) b.resize(recordHeaderLen + n) remoteMAC := payload[n:] localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data) hc.incSeq() if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 { return false, alertBadRecordMAC } hc.inDigestBuf = localMAC } return true, 0 } // padToBlockSize calculates the needed padding block, if any, for a payload. // On exit, prefix aliases payload and extends to the end of the last full // block of payload. finalBlock is a fresh slice which contains the contents of // any suffix of payload as well as the needed padding to make finalBlock a // full block. func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) { overrun := len(payload) % blockSize paddingLen := blockSize - overrun prefix = payload[:len(payload)-overrun] finalBlock = make([]byte, blockSize) copy(finalBlock, payload[len(payload)-overrun:]) for i := overrun; i < blockSize; i++ { finalBlock[i] = byte(paddingLen - 1) } return } // encrypt encrypts and macs the data in b. func (hc *halfConn) encrypt(b *block) (bool, alert) { // mac if hc.mac != nil { mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data) hc.incSeq() n := len(b.data) b.resize(n + len(mac)) copy(b.data[n:], mac) hc.outDigestBuf = mac } payload := b.data[recordHeaderLen:] // encrypt if hc.cipher != nil { switch c := hc.cipher.(type) { case cipher.Stream: c.XORKeyStream(payload, payload) case cipher.BlockMode: prefix, finalBlock := padToBlockSize(payload, c.BlockSize()) b.resize(recordHeaderLen + len(prefix) + len(finalBlock)) c.CryptBlocks(b.data[recordHeaderLen:], prefix) c.CryptBlocks(b.data[recordHeaderLen+len(prefix):], finalBlock) default: panic("unknown cipher type") } } // update length to include MAC and any block padding needed. n := len(b.data) - recordHeaderLen b.data[3] = byte(n >> 8) b.data[4] = byte(n) return true, 0 } // A block is a simple data buffer. type block struct { data []byte off int // index for Read link *block } // resize resizes block to be n bytes, growing if necessary. func (b *block) resize(n int) { if n > cap(b.data) { b.reserve(n) } b.data = b.data[0:n] } // reserve makes sure that block contains a capacity of at least n bytes. func (b *block) reserve(n int) { if cap(b.data) >= n { return } m := cap(b.data) if m == 0 { m = 1024 } for m < n { m *= 2 } data := make([]byte, len(b.data), m) copy(data, b.data) b.data = data } // readFromUntil reads from r into b until b contains at least n bytes // or else returns an error. func (b *block) readFromUntil(r io.Reader, n int) error { // quick case if len(b.data) >= n { return nil } // read until have enough. b.reserve(n) for { m, err := r.Read(b.data[len(b.data):cap(b.data)]) b.data = b.data[0 : len(b.data)+m] if len(b.data) >= n { break } if err != nil { return err } } return nil } func (b *block) Read(p []byte) (n int, err error) { n = copy(p, b.data[b.off:]) b.off += n return } // newBlock allocates a new block, from hc's free list if possible. func (hc *halfConn) newBlock() *block { b := hc.bfree if b == nil { return new(block) } hc.bfree = b.link b.link = nil b.resize(0) return b } // freeBlock returns a block to hc's free list. // The protocol is such that each side only has a block or two on // its free list at a time, so there's no need to worry about // trimming the list, etc. func (hc *halfConn) freeBlock(b *block) { b.link = hc.bfree hc.bfree = b } // splitBlock splits a block after the first n bytes, // returning a block with those n bytes and a // block with the remainder. the latter may be nil. func (hc *halfConn) splitBlock(b *block, n int) (*block, *block) { if len(b.data) <= n { return b, nil } bb := hc.newBlock() bb.resize(len(b.data) - n) copy(bb.data, b.data[n:]) b.data = b.data[0:n] return b, bb } // readRecord reads the next TLS record from the connection // and updates the record layer state. // c.in.Mutex <= L; c.input == nil. func (c *Conn) readRecord(want recordType) error { // Caller must be in sync with connection: // handshake data if handshake not yet completed, // else application data. switch want { default: return c.sendAlert(alertInternalError) case recordTypeHandshake, recordTypeChangeCipherSpec: if c.handshakeComplete { return c.sendAlert(alertInternalError) } case recordTypeApplicationData: if !c.handshakeComplete { return c.sendAlert(alertInternalError) } } Again: if c.rawInput == nil { c.rawInput = c.in.newBlock() } b := c.rawInput // Read header, payload. if err := b.readFromUntil(c.conn, recordHeaderLen); err != nil { // RFC suggests that EOF without an alertCloseNotify is // an error, but popular web sites seem to do this, // so we can't make it an error. // if err == io.EOF { // err = io.ErrUnexpectedEOF // } if e, ok := err.(net.Error); !ok || !e.Temporary() { c.setError(err) } return err } typ := recordType(b.data[0]) vers := uint16(b.data[1])<<8 | uint16(b.data[2]) n := int(b.data[3])<<8 | int(b.data[4]) if c.haveVers && vers != c.vers { return c.sendAlert(alertProtocolVersion) } if n > maxCiphertext { return c.sendAlert(alertRecordOverflow) } if !c.haveVers { // First message, be extra suspicious: // this might not be a TLS client. // Bail out before reading a full 'body', if possible. // The current max version is 3.1. // If the version is >= 16.0, it's probably not real. // Similarly, a clientHello message encodes in // well under a kilobyte. If the length is >= 12 kB, // it's probably not real. if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 { return c.sendAlert(alertUnexpectedMessage) } } if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } if e, ok := err.(net.Error); !ok || !e.Temporary() { c.setError(err) } return err } // Process message. b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) b.off = recordHeaderLen if ok, err := c.in.decrypt(b); !ok { return c.sendAlert(err) } data := b.data[b.off:] if len(data) > maxPlaintext { c.sendAlert(alertRecordOverflow) c.in.freeBlock(b) return c.error() } switch typ { default: c.sendAlert(alertUnexpectedMessage) case recordTypeAlert: if len(data) != 2 { c.sendAlert(alertUnexpectedMessage) break } if alert(data[1]) == alertCloseNotify { c.setError(io.EOF) break } switch data[0] { case alertLevelWarning: // drop on the floor c.in.freeBlock(b) goto Again case alertLevelError: c.setError(&net.OpError{Op: "remote error", Err: alert(data[1])}) default: c.sendAlert(alertUnexpectedMessage) } case recordTypeChangeCipherSpec: if typ != want || len(data) != 1 || data[0] != 1 { c.sendAlert(alertUnexpectedMessage) break } err := c.in.changeCipherSpec() if err != nil { c.sendAlert(err.(alert)) } case recordTypeApplicationData: if typ != want { c.sendAlert(alertUnexpectedMessage) break } c.input = b b = nil case recordTypeHandshake: // TODO(rsc): Should at least pick off connection close. if typ != want && !c.isClient { return c.sendAlert(alertNoRenegotiation) } c.hand.Write(data) } if b != nil { c.in.freeBlock(b) } return c.error() } // sendAlert sends a TLS alert message. // c.out.Mutex <= L. func (c *Conn) sendAlertLocked(err alert) error { c.tmp[0] = alertLevelError if err == alertNoRenegotiation { c.tmp[0] = alertLevelWarning } c.tmp[1] = byte(err) c.writeRecord(recordTypeAlert, c.tmp[0:2]) // closeNotify is a special case in that it isn't an error: if err != alertCloseNotify { return c.setError(&net.OpError{Op: "local error", Err: err}) } return nil } // sendAlert sends a TLS alert message. // L < c.out.Mutex. func (c *Conn) sendAlert(err alert) error { c.out.Lock() defer c.out.Unlock() return c.sendAlertLocked(err) } // writeRecord writes a TLS record with the given type and payload // to the connection and updates the record layer state. // c.out.Mutex <= L. func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) { b := c.out.newBlock() for len(data) > 0 { m := len(data) if m > maxPlaintext { m = maxPlaintext } b.resize(recordHeaderLen + m) b.data[0] = byte(typ) vers := c.vers if vers == 0 { vers = maxVersion } b.data[1] = byte(vers >> 8) b.data[2] = byte(vers) b.data[3] = byte(m >> 8) b.data[4] = byte(m) copy(b.data[recordHeaderLen:], data) c.out.encrypt(b) _, err = c.conn.Write(b.data) if err != nil { break } n += m data = data[m:] } c.out.freeBlock(b) if typ == recordTypeChangeCipherSpec { err = c.out.changeCipherSpec() if err != nil { // Cannot call sendAlert directly, // because we already hold c.out.Mutex. c.tmp[0] = alertLevelError c.tmp[1] = byte(err.(alert)) c.writeRecord(recordTypeAlert, c.tmp[0:2]) c.err = &net.OpError{Op: "local error", Err: err} return n, c.err } } return } // readHandshake reads the next handshake message from // the record layer. // c.in.Mutex < L; c.out.Mutex < L. func (c *Conn) readHandshake() (interface{}, error) { for c.hand.Len() < 4 { if c.err != nil { return nil, c.err } if err := c.readRecord(recordTypeHandshake); err != nil { return nil, err } } data := c.hand.Bytes() n := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) if n > maxHandshake { c.sendAlert(alertInternalError) return nil, c.err } for c.hand.Len() < 4+n { if c.err != nil { return nil, c.err } if err := c.readRecord(recordTypeHandshake); err != nil { return nil, err } } data = c.hand.Next(4 + n) var m handshakeMessage switch data[0] { case typeHelloRequest: m = new(helloRequestMsg) case typeClientHello: m = new(clientHelloMsg) case typeServerHello: m = new(serverHelloMsg) case typeCertificate: m = new(certificateMsg) case typeCertificateRequest: m = new(certificateRequestMsg) case typeCertificateStatus: m = new(certificateStatusMsg) case typeServerKeyExchange: m = new(serverKeyExchangeMsg) case typeServerHelloDone: m = new(serverHelloDoneMsg) case typeClientKeyExchange: m = new(clientKeyExchangeMsg) case typeCertificateVerify: m = new(certificateVerifyMsg) case typeNextProtocol: m = new(nextProtoMsg) case typeFinished: m = new(finishedMsg) default: c.sendAlert(alertUnexpectedMessage) return nil, alertUnexpectedMessage } // The handshake message unmarshallers // expect to be able to keep references to data, // so pass in a fresh copy that won't be overwritten. data = append([]byte(nil), data...) if !m.unmarshal(data) { c.sendAlert(alertUnexpectedMessage) return nil, alertUnexpectedMessage } return m, nil } // Write writes data to the connection. func (c *Conn) Write(b []byte) (int, error) { if c.err != nil { return 0, c.err } if c.err = c.Handshake(); c.err != nil { return 0, c.err } c.out.Lock() defer c.out.Unlock() if !c.handshakeComplete { return 0, alertInternalError } var n int n, c.err = c.writeRecord(recordTypeApplicationData, b) return n, c.err } func (c *Conn) handleRenegotiation() error { c.handshakeComplete = false if !c.isClient { panic("renegotiation should only happen for a client") } msg, err := c.readHandshake() if err != nil { return err } _, ok := msg.(*helloRequestMsg) if !ok { c.sendAlert(alertUnexpectedMessage) return alertUnexpectedMessage } return c.Handshake() } // Read can be made to time out and return a net.Error with Timeout() == true // after a fixed time limit; see SetDeadline and SetReadDeadline. func (c *Conn) Read(b []byte) (n int, err error) { if err = c.Handshake(); err != nil { return } c.in.Lock() defer c.in.Unlock() for c.input == nil && c.err == nil { if err := c.readRecord(recordTypeApplicationData); err != nil { // Soft error, like EAGAIN return 0, err } if c.hand.Len() > 0 { // We received handshake bytes, indicating the start of // a renegotiation. if err := c.handleRenegotiation(); err != nil { return 0, err } continue } } if c.err != nil { return 0, c.err } n, err = c.input.Read(b) if c.input.off >= len(c.input.data) { c.in.freeBlock(c.input) c.input = nil } return n, nil } // Close closes the connection. func (c *Conn) Close() error { var alertErr error c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() if c.handshakeComplete { alertErr = c.sendAlert(alertCloseNotify) } if err := c.conn.Close(); err != nil { return err } return alertErr } // Handshake runs the client or server handshake // protocol if it has not yet been run. // Most uses of this package need not call Handshake // explicitly: the first Read or Write will call it automatically. func (c *Conn) Handshake() error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() if err := c.error(); err != nil { return err } if c.handshakeComplete { return nil } if c.isClient { return c.clientHandshake() } return c.serverHandshake() } // ConnectionState returns basic TLS details about the connection. func (c *Conn) ConnectionState() ConnectionState { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() var state ConnectionState state.HandshakeComplete = c.handshakeComplete if c.handshakeComplete { state.NegotiatedProtocol = c.clientProtocol state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback state.CipherSuite = c.cipherSuite state.PeerCertificates = c.peerCertificates state.VerifiedChains = c.verifiedChains state.ServerName = c.serverName } return state } // OCSPResponse returns the stapled OCSP response from the TLS server, if // any. (Only valid for client connections.) func (c *Conn) OCSPResponse() []byte { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() return c.ocspResponse } // VerifyHostname checks that the peer certificate chain is valid for // connecting to host. If so, it returns nil; if not, it returns an error // describing the problem. func (c *Conn) VerifyHostname(host string) error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() if !c.isClient { return errors.New("VerifyHostname called on TLS server connection") } if !c.handshakeComplete { return errors.New("TLS handshake has not yet been performed") } return c.peerCertificates[0].VerifyHostname(host) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/handshake_client.go�������������������������������0000644�0000153�0000161�00000025333�12321735761�025445� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package tls import ( "bytes" "crypto" "crypto/rsa" "crypto/subtle" "crypto/x509" "errors" "io" "strconv" ) func (c *Conn) clientHandshake() error { finishedHash := newFinishedHash(versionTLS10) if c.config == nil { c.config = defaultConfig() } hello := &clientHelloMsg{ vers: maxVersion, cipherSuites: c.config.cipherSuites(), compressionMethods: []uint8{compressionNone}, random: make([]byte, 32), ocspStapling: true, serverName: c.config.ServerName, supportedCurves: []uint16{curveP256, curveP384, curveP521}, supportedPoints: []uint8{pointFormatUncompressed}, nextProtoNeg: len(c.config.NextProtos) > 0, } t := uint32(c.config.time().Unix()) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) hello.random[2] = byte(t >> 8) hello.random[3] = byte(t) _, err := io.ReadFull(c.config.rand(), hello.random[4:]) if err != nil { c.sendAlert(alertInternalError) return errors.New("short read from Rand") } finishedHash.Write(hello.marshal()) c.writeRecord(recordTypeHandshake, hello.marshal()) msg, err := c.readHandshake() if err != nil { return err } serverHello, ok := msg.(*serverHelloMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) } finishedHash.Write(serverHello.marshal()) vers, ok := mutualVersion(serverHello.vers) if !ok || vers < versionTLS10 { // TLS 1.0 is the minimum version supported as a client. return c.sendAlert(alertProtocolVersion) } c.vers = vers c.haveVers = true if serverHello.compressionMethod != compressionNone { return c.sendAlert(alertUnexpectedMessage) } if !hello.nextProtoNeg && serverHello.nextProtoNeg { c.sendAlert(alertHandshakeFailure) return errors.New("server advertised unrequested NPN") } suite := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) if suite == nil { return c.sendAlert(alertHandshakeFailure) } msg, err = c.readHandshake() if err != nil { return err } certMsg, ok := msg.(*certificateMsg) if !ok || len(certMsg.certificates) == 0 { return c.sendAlert(alertUnexpectedMessage) } finishedHash.Write(certMsg.marshal()) certs := make([]*x509.Certificate, len(certMsg.certificates)) for i, asn1Data := range certMsg.certificates { cert, err := x509.ParseCertificate(asn1Data) if err != nil { c.sendAlert(alertBadCertificate) return errors.New("failed to parse certificate from server: " + err.Error()) } certs[i] = cert } if !c.config.InsecureSkipVerify { opts := x509.VerifyOptions{ Roots: c.config.RootCAs, CurrentTime: c.config.time(), DNSName: c.config.ServerName, Intermediates: x509.NewCertPool(), } for i, cert := range certs { if i == 0 { continue } opts.Intermediates.AddCert(cert) } c.verifiedChains, err = certs[0].Verify(opts) if err != nil { c.sendAlert(alertBadCertificate) return err } } if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok { return c.sendAlert(alertUnsupportedCertificate) } c.peerCertificates = certs if serverHello.ocspStapling { msg, err = c.readHandshake() if err != nil { return err } cs, ok := msg.(*certificateStatusMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) } finishedHash.Write(cs.marshal()) if cs.statusType == statusTypeOCSP { c.ocspResponse = cs.response } } msg, err = c.readHandshake() if err != nil { return err } keyAgreement := suite.ka() skx, ok := msg.(*serverKeyExchangeMsg) if ok { finishedHash.Write(skx.marshal()) err = keyAgreement.processServerKeyExchange(c.config, hello, serverHello, certs[0], skx) if err != nil { c.sendAlert(alertUnexpectedMessage) return err } msg, err = c.readHandshake() if err != nil { return err } } var certToSend *Certificate var certRequested bool certReq, ok := msg.(*certificateRequestMsg) if ok { certRequested = true // RFC 4346 on the certificateAuthorities field: // A list of the distinguished names of acceptable certificate // authorities. These distinguished names may specify a desired // distinguished name for a root CA or for a subordinate CA; // thus, this message can be used to describe both known roots // and a desired authorization space. If the // certificate_authorities list is empty then the client MAY // send any certificate of the appropriate // ClientCertificateType, unless there is some external // arrangement to the contrary. finishedHash.Write(certReq.marshal()) // For now, we only know how to sign challenges with RSA rsaAvail := false for _, certType := range certReq.certificateTypes { if certType == certTypeRSASign { rsaAvail = true break } } // We need to search our list of client certs for one // where SignatureAlgorithm is RSA and the Issuer is in // certReq.certificateAuthorities findCert: for i, cert := range c.config.Certificates { if !rsaAvail { continue } leaf := cert.Leaf if leaf == nil { if leaf, err = x509.ParseCertificate(cert.Certificate[0]); err != nil { c.sendAlert(alertInternalError) return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) } } if leaf.PublicKeyAlgorithm != x509.RSA { continue } if len(certReq.certificateAuthorities) == 0 { // they gave us an empty list, so just take the // first RSA cert from c.config.Certificates certToSend = &cert break } for _, ca := range certReq.certificateAuthorities { if bytes.Equal(leaf.RawIssuer, ca) { certToSend = &cert break findCert } } } msg, err = c.readHandshake() if err != nil { return err } } shd, ok := msg.(*serverHelloDoneMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) } finishedHash.Write(shd.marshal()) // If the server requested a certificate then we have to send a // Certificate message, even if it's empty because we don't have a // certificate to send. if certRequested { certMsg = new(certificateMsg) if certToSend != nil { certMsg.certificates = certToSend.Certificate } finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) } preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hello, certs[0]) if err != nil { c.sendAlert(alertInternalError) return err } if ckx != nil { finishedHash.Write(ckx.marshal()) c.writeRecord(recordTypeHandshake, ckx.marshal()) } if certToSend != nil { certVerify := new(certificateVerifyMsg) digest := make([]byte, 0, 36) digest = finishedHash.serverMD5.Sum(digest) digest = finishedHash.serverSHA1.Sum(digest) signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest) if err != nil { return c.sendAlert(alertInternalError) } certVerify.signature = signed finishedHash.Write(certVerify.marshal()) c.writeRecord(recordTypeHandshake, certVerify.marshal()) } masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */) clientHash := suite.mac(c.vers, clientMAC) c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) if serverHello.nextProtoNeg { nextProto := new(nextProtoMsg) proto, fallback := mutualProtocol(c.config.NextProtos, serverHello.nextProtos) nextProto.proto = proto c.clientProtocol = proto c.clientProtocolFallback = fallback finishedHash.Write(nextProto.marshal()) c.writeRecord(recordTypeHandshake, nextProto.marshal()) } finished := new(finishedMsg) finished.verifyData = finishedHash.clientSum(masterSecret) finishedHash.Write(finished.marshal()) c.writeRecord(recordTypeHandshake, finished.marshal()) serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */) serverHash := suite.mac(c.vers, serverMAC) c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) c.readRecord(recordTypeChangeCipherSpec) if c.err != nil { return c.err } msg, err = c.readHandshake() if err != nil { return err } serverFinished, ok := msg.(*finishedMsg) if !ok { return c.sendAlert(alertUnexpectedMessage) } verify := finishedHash.serverSum(masterSecret) if len(verify) != len(serverFinished.verifyData) || subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 { return c.sendAlert(alertHandshakeFailure) } c.handshakeComplete = true c.cipherSuite = suite.id return nil } // mutualProtocol finds the mutual Next Protocol Negotiation protocol given the // set of client and server supported protocols. The set of client supported // protocols must not be empty. It returns the resulting protocol and flag // indicating if the fallback case was reached. func mutualProtocol(clientProtos, serverProtos []string) (string, bool) { for _, s := range serverProtos { for _, c := range clientProtos { if s == c { return s, false } } } return clientProtos[0], true } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/generate_cert.go����������������������������������0000644�0000153�0000161�00000004053�12321735761�024764� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // +build ignore // Generate a self-signed X.509 certificate for a TLS server. Outputs to // 'cert.pem' and 'key.pem' and will overwrite existing files. package main import ( "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "flag" "log" "math/big" "os" "time" ) var hostName *string = flag.String("host", "127.0.0.1", "Hostname to generate a certificate for") func main() { flag.Parse() priv, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil { log.Fatalf("failed to generate private key: %s", err) return } now := time.Now() template := x509.Certificate{ SerialNumber: new(big.Int).SetInt64(0), Subject: pkix.Name{ CommonName: *hostName, Organization: []string{"Acme Co"}, }, NotBefore: now.Add(-5 * time.Minute).UTC(), NotAfter: now.AddDate(1, 0, 0).UTC(), // valid for 1 year. SubjectKeyId: []byte{1, 2, 3, 4}, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, } derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) if err != nil { log.Fatalf("Failed to create certificate: %s", err) return } certOut, err := os.Create("cert.pem") if err != nil { log.Fatalf("failed to open cert.pem for writing: %s", err) return } pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) certOut.Close() log.Print("written cert.pem\n") keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { log.Print("failed to open key.pem for writing:", err) return } pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) keyOut.Close() log.Print("written key.pem\n") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/tls.go��������������������������������������������0000644�0000153�0000161�00000013204�12321735761�022755� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // Package tls partially implements TLS 1.0, as specified in RFC 2246. package tls import ( "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "io/ioutil" "net" "strings" ) // Server returns a new TLS server side connection // using conn as the underlying transport. // The configuration config must be non-nil and must have // at least one certificate. func Server(conn net.Conn, config *Config) *Conn { return &Conn{conn: conn, config: config} } // Client returns a new TLS client side connection // using conn as the underlying transport. // Client interprets a nil configuration as equivalent to // the zero configuration; see the documentation of Config // for the defaults. func Client(conn net.Conn, config *Config) *Conn { return &Conn{conn: conn, config: config, isClient: true} } // A listener implements a network listener (net.Listener) for TLS connections. type listener struct { net.Listener config *Config } // Accept waits for and returns the next incoming TLS connection. // The returned connection c is a *tls.Conn. func (l *listener) Accept() (c net.Conn, err error) { c, err = l.Listener.Accept() if err != nil { return } c = Server(c, l.config) return } // NewListener creates a Listener which accepts connections from an inner // Listener and wraps each connection with Server. // The configuration config must be non-nil and must have // at least one certificate. func NewListener(inner net.Listener, config *Config) net.Listener { l := new(listener) l.Listener = inner l.config = config return l } // Listen creates a TLS listener accepting connections on the // given network address using net.Listen. // The configuration config must be non-nil and must have // at least one certificate. func Listen(network, laddr string, config *Config) (net.Listener, error) { if config == nil || len(config.Certificates) == 0 { return nil, errors.New("tls.Listen: no certificates in configuration") } l, err := net.Listen(network, laddr) if err != nil { return nil, err } return NewListener(l, config), nil } // Dial connects to the given network address using net.Dial // and then initiates a TLS handshake, returning the resulting // TLS connection. // Dial interprets a nil configuration as equivalent to // the zero configuration; see the documentation of Config // for the defaults. func Dial(network, addr string, config *Config) (*Conn, error) { raddr := addr c, err := net.Dial(network, raddr) if err != nil { return nil, err } colonPos := strings.LastIndex(raddr, ":") if colonPos == -1 { colonPos = len(raddr) } hostname := raddr[:colonPos] if config == nil { config = defaultConfig() } // If no ServerName is set, infer the ServerName // from the hostname we're connecting to. if config.ServerName == "" { // Make a copy to avoid polluting argument or default. c := *config c.ServerName = hostname config = &c } conn := Client(c, config) if err = conn.Handshake(); err != nil { c.Close() return nil, err } return conn, nil } // LoadX509KeyPair reads and parses a public/private key pair from a pair of // files. The files must contain PEM encoded data. func LoadX509KeyPair(certFile, keyFile string) (cert Certificate, err error) { certPEMBlock, err := ioutil.ReadFile(certFile) if err != nil { return } keyPEMBlock, err := ioutil.ReadFile(keyFile) if err != nil { return } return X509KeyPair(certPEMBlock, keyPEMBlock) } // X509KeyPair parses a public/private key pair from a pair of // PEM encoded data. func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) { var certDERBlock *pem.Block for { certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) if certDERBlock == nil { break } if certDERBlock.Type == "CERTIFICATE" { cert.Certificate = append(cert.Certificate, certDERBlock.Bytes) } } if len(cert.Certificate) == 0 { err = errors.New("crypto/tls: failed to parse certificate PEM data") return } keyDERBlock, _ := pem.Decode(keyPEMBlock) if keyDERBlock == nil { err = errors.New("crypto/tls: failed to parse key PEM data") return } // OpenSSL 0.9.8 generates PKCS#1 private keys by default, while // OpenSSL 1.0.0 generates PKCS#8 keys. We try both. var key *rsa.PrivateKey if key, err = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes); err != nil { var privKey interface{} if privKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes); err != nil { err = errors.New("crypto/tls: failed to parse key: " + err.Error()) return } var ok bool if key, ok = privKey.(*rsa.PrivateKey); !ok { err = errors.New("crypto/tls: found non-RSA private key in PKCS#8 wrapping") return } } cert.PrivateKey = key // We don't need to parse the public key for TLS, but we so do anyway // to check that it looks sane and matches the private key. x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) if err != nil { return } if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 { err = errors.New("crypto/tls: private key does not match public key") return } return } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/common.go�����������������������������������������0000644�0000153�0000161�00000022607�12321735761�023452� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package tls import ( "crypto" "crypto/rand" "crypto/x509" "io" "strings" "sync" "time" ) const ( maxPlaintext = 16384 // maximum plaintext payload length maxCiphertext = 16384 + 2048 // maximum ciphertext payload length recordHeaderLen = 5 // record header length maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) versionSSL30 = 0x0300 versionTLS10 = 0x0301 minVersion = versionSSL30 maxVersion = versionTLS10 ) // TLS record types. type recordType uint8 const ( recordTypeChangeCipherSpec recordType = 20 recordTypeAlert recordType = 21 recordTypeHandshake recordType = 22 recordTypeApplicationData recordType = 23 ) // TLS handshake message types. const ( typeHelloRequest uint8 = 0 typeClientHello uint8 = 1 typeServerHello uint8 = 2 typeCertificate uint8 = 11 typeServerKeyExchange uint8 = 12 typeCertificateRequest uint8 = 13 typeServerHelloDone uint8 = 14 typeCertificateVerify uint8 = 15 typeClientKeyExchange uint8 = 16 typeFinished uint8 = 20 typeCertificateStatus uint8 = 22 typeNextProtocol uint8 = 67 // Not IANA assigned ) // TLS compression types. const ( compressionNone uint8 = 0 ) // TLS extension numbers var ( extensionServerName uint16 = 0 extensionStatusRequest uint16 = 5 extensionSupportedCurves uint16 = 10 extensionSupportedPoints uint16 = 11 extensionNextProtoNeg uint16 = 13172 // not IANA assigned ) // TLS Elliptic Curves // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8 var ( curveP256 uint16 = 23 curveP384 uint16 = 24 curveP521 uint16 = 25 ) // TLS Elliptic Curve Point Formats // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9 var ( pointFormatUncompressed uint8 = 0 ) // TLS CertificateStatusType (RFC 3546) const ( statusTypeOCSP uint8 = 1 ) // Certificate types (for certificateRequestMsg) const ( certTypeRSASign = 1 // A certificate containing an RSA key certTypeDSSSign = 2 // A certificate containing a DSA key certTypeRSAFixedDH = 3 // A certificate containing a static DH key certTypeDSSFixedDH = 4 // A certificate containing a static DH key // Rest of these are reserved by the TLS spec ) // ConnectionState records basic TLS details about the connection. type ConnectionState struct { HandshakeComplete bool CipherSuite uint16 NegotiatedProtocol string NegotiatedProtocolIsMutual bool // ServerName contains the server name indicated by the client, if any. // (Only valid for server connections.) ServerName string // the certificate chain that was presented by the other side PeerCertificates []*x509.Certificate // the verified certificate chains built from PeerCertificates. VerifiedChains [][]*x509.Certificate } // ClientAuthType declares the policy the server will follow for // TLS Client Authentication. type ClientAuthType int const ( NoClientCert ClientAuthType = iota RequestClientCert RequireAnyClientCert VerifyClientCertIfGiven RequireAndVerifyClientCert ) // A Config structure is used to configure a TLS client or server. After one // has been passed to a TLS function it must not be modified. type Config struct { // Rand provides the source of entropy for nonces and RSA blinding. // If Rand is nil, TLS uses the cryptographic random reader in package // crypto/rand. Rand io.Reader // Time returns the current time as the number of seconds since the epoch. // If Time is nil, TLS uses time.Now. Time func() time.Time // Certificates contains one or more certificate chains // to present to the other side of the connection. // Server configurations must include at least one certificate. Certificates []Certificate // NameToCertificate maps from a certificate name to an element of // Certificates. Note that a certificate name can be of the form // '*.example.com' and so doesn't have to be a domain name as such. // See Config.BuildNameToCertificate // The nil value causes the first element of Certificates to be used // for all connections. NameToCertificate map[string]*Certificate // RootCAs defines the set of root certificate authorities // that clients use when verifying server certificates. // If RootCAs is nil, TLS uses the host's root CA set. RootCAs *x509.CertPool // NextProtos is a list of supported, application level protocols. NextProtos []string // ServerName is included in the client's handshake to support virtual // hosting. ServerName string // ClientAuth determines the server's policy for // TLS Client Authentication. The default is NoClientCert. ClientAuth ClientAuthType // ClientCAs defines the set of root certificate authorities // that servers use if required to verify a client certificate // by the policy in ClientAuth. ClientCAs *x509.CertPool // InsecureSkipVerify controls whether a client verifies the // server's certificate chain and host name. // If InsecureSkipVerify is true, TLS accepts any certificate // presented by the server and any host name in that certificate. // In this mode, TLS is susceptible to man-in-the-middle attacks. // This should be used only for testing. InsecureSkipVerify bool // CipherSuites is a list of supported cipher suites. If CipherSuites // is nil, TLS uses a list of suites supported by the implementation. CipherSuites []uint16 } func (c *Config) rand() io.Reader { r := c.Rand if r == nil { return rand.Reader } return r } func (c *Config) time() time.Time { t := c.Time if t == nil { t = time.Now } return t() } func (c *Config) cipherSuites() []uint16 { s := c.CipherSuites if s == nil { s = defaultCipherSuites() } return s } // getCertificateForName returns the best certificate for the given name, // defaulting to the first element of c.Certificates if there are no good // options. func (c *Config) getCertificateForName(name string) *Certificate { if len(c.Certificates) == 1 || c.NameToCertificate == nil { // There's only one choice, so no point doing any work. return &c.Certificates[0] } name = strings.ToLower(name) for len(name) > 0 && name[len(name)-1] == '.' { name = name[:len(name)-1] } if cert, ok := c.NameToCertificate[name]; ok { return cert } // try replacing labels in the name with wildcards until we get a // match. labels := strings.Split(name, ".") for i := range labels { labels[i] = "*" candidate := strings.Join(labels, ".") if cert, ok := c.NameToCertificate[candidate]; ok { return cert } } // If nothing matches, return the first certificate. return &c.Certificates[0] } // BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate // from the CommonName and SubjectAlternateName fields of each of the leaf // certificates. func (c *Config) BuildNameToCertificate() { c.NameToCertificate = make(map[string]*Certificate) for i := range c.Certificates { cert := &c.Certificates[i] x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) if err != nil { continue } if len(x509Cert.Subject.CommonName) > 0 { c.NameToCertificate[x509Cert.Subject.CommonName] = cert } for _, san := range x509Cert.DNSNames { c.NameToCertificate[san] = cert } } } // A Certificate is a chain of one or more certificates, leaf first. type Certificate struct { Certificate [][]byte PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey // OCSPStaple contains an optional OCSP response which will be served // to clients that request it. OCSPStaple []byte // Leaf is the parsed form of the leaf certificate, which may be // initialized using x509.ParseCertificate to reduce per-handshake // processing for TLS clients doing client authentication. If nil, the // leaf certificate will be parsed as needed. Leaf *x509.Certificate } // A TLS record. type record struct { contentType recordType major, minor uint8 payload []byte } type handshakeMessage interface { marshal() []byte unmarshal([]byte) bool } // mutualVersion returns the protocol version to use given the advertised // version of the peer. func mutualVersion(vers uint16) (uint16, bool) { if vers < minVersion { return 0, false } if vers > maxVersion { vers = maxVersion } return vers, true } var emptyConfig Config func defaultConfig() *Config { return &emptyConfig } var ( once sync.Once varDefaultCipherSuites []uint16 ) func defaultCipherSuites() []uint16 { once.Do(initDefaultCipherSuites) return varDefaultCipherSuites } func initDefaultCipherSuites() { varDefaultCipherSuites = make([]uint16, len(cipherSuites)) for i, suite := range cipherSuites { varDefaultCipherSuites[i] = suite.id } } �������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/alert.go������������������������������������������0000644�0000153�0000161�00000005233�12321735761�023265� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package tls import "strconv" type alert uint8 const ( // alert level alertLevelWarning = 1 alertLevelError = 2 ) const ( alertCloseNotify alert = 0 alertUnexpectedMessage alert = 10 alertBadRecordMAC alert = 20 alertDecryptionFailed alert = 21 alertRecordOverflow alert = 22 alertDecompressionFailure alert = 30 alertHandshakeFailure alert = 40 alertBadCertificate alert = 42 alertUnsupportedCertificate alert = 43 alertCertificateRevoked alert = 44 alertCertificateExpired alert = 45 alertCertificateUnknown alert = 46 alertIllegalParameter alert = 47 alertUnknownCA alert = 48 alertAccessDenied alert = 49 alertDecodeError alert = 50 alertDecryptError alert = 51 alertProtocolVersion alert = 70 alertInsufficientSecurity alert = 71 alertInternalError alert = 80 alertUserCanceled alert = 90 alertNoRenegotiation alert = 100 ) var alertText = map[alert]string{ alertCloseNotify: "close notify", alertUnexpectedMessage: "unexpected message", alertBadRecordMAC: "bad record MAC", alertDecryptionFailed: "decryption failed", alertRecordOverflow: "record overflow", alertDecompressionFailure: "decompression failure", alertHandshakeFailure: "handshake failure", alertBadCertificate: "bad certificate", alertUnsupportedCertificate: "unsupported certificate", alertCertificateRevoked: "revoked certificate", alertCertificateExpired: "expired certificate", alertCertificateUnknown: "unknown certificate", alertIllegalParameter: "illegal parameter", alertUnknownCA: "unknown certificate authority", alertAccessDenied: "access denied", alertDecodeError: "error decoding message", alertDecryptError: "error decrypting message", alertProtocolVersion: "protocol version not supported", alertInsufficientSecurity: "insufficient security level", alertInternalError: "internal error", alertUserCanceled: "user canceled", alertNoRenegotiation: "no renegotiation", } func (e alert) String() string { s, ok := alertText[e] if ok { return s } return "alert(" + strconv.Itoa(int(e)) + ")" } func (e alert) Error() string { return e.String() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/parse-gnutls-cli-debug-log.py���������������������0000644�0000153�0000161�00000003541�12321735761�027235� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright 2010 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 used to parse the debug log from gnutls-cli and generate a # script of the handshake. This script is included in handshake_server_test.go. # See the comments there for details. import sys blocks = [] READ = 1 WRITE = 2 currentBlockType = 0 currentBlock = [] for line in sys.stdin.readlines(): line = line[:-1] if line.startswith("|<7>| WRITE: "): if currentBlockType != WRITE: if len(currentBlock) > 0: blocks.append(currentBlock) currentBlock = [] currentBlockType = WRITE elif line.startswith("|<7>| READ: "): if currentBlockType != READ: if len(currentBlock) > 0: blocks.append(currentBlock) currentBlock = [] currentBlockType = READ elif line.startswith("|<7>| 0"): line = line[13:] line = line.strip() bs = line.split() for b in bs: currentBlock.append(int(b, 16)) elif line.startswith("|<7>| RB-PEEK: Read 1 bytes"): currentBlock = currentBlock[:-1] if len(currentBlock) > 0: blocks.append(currentBlock) for block in blocks: sys.stdout.write("\t{\n") i = 0 for b in block: if i % 8 == 0: sys.stdout.write("\t\t") sys.stdout.write("0x%02x," % b) if i % 8 == 7: sys.stdout.write("\n") else: sys.stdout.write(" ") i += 1 sys.stdout.write("\n\t},\n\n") ���������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/tls/cipher_suites.go����������������������������������0000644�0000153�0000161�00000014267�12321735761�025033� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 tls import ( "crypto/aes" "crypto/cipher" "crypto/des" "crypto/hmac" "crypto/rc4" "crypto/sha1" "crypto/x509" "hash" ) // a keyAgreement implements the client and server side of a TLS key agreement // protocol by generating and processing key exchange messages. type keyAgreement interface { // On the server side, the first two methods are called in order. // In the case that the key agreement protocol doesn't use a // ServerKeyExchange message, generateServerKeyExchange can return nil, // nil. generateServerKeyExchange(*Config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error) processClientKeyExchange(*Config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error) // On the client side, the next two methods are called in order. // This method may not be called if the server doesn't send a // ServerKeyExchange message. processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) } // A cipherSuite is a specific combination of key agreement, cipher and MAC // function. All cipher suites currently assume RSA key agreement. type cipherSuite struct { id uint16 // the lengths, in bytes, of the key material needed for each component. keyLen int macLen int ivLen int ka func() keyAgreement // If elliptic is set, a server will only consider this ciphersuite if // the ClientHello indicated that the client supports an elliptic curve // and point format that we can handle. elliptic bool cipher func(key, iv []byte, isRead bool) interface{} mac func(version uint16, macKey []byte) macFunction } var cipherSuites = []*cipherSuite{ {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1}, {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1}, {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1}, {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1}, {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1}, {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, } func cipherRC4(key, iv []byte, isRead bool) interface{} { cipher, _ := rc4.NewCipher(key) return cipher } func cipher3DES(key, iv []byte, isRead bool) interface{} { block, _ := des.NewTripleDESCipher(key) if isRead { return cipher.NewCBCDecrypter(block, iv) } return cipher.NewCBCEncrypter(block, iv) } func cipherAES(key, iv []byte, isRead bool) interface{} { block, _ := aes.NewCipher(key) if isRead { return cipher.NewCBCDecrypter(block, iv) } return cipher.NewCBCEncrypter(block, iv) } // macSHA1 returns a macFunction for the given protocol version. func macSHA1(version uint16, key []byte) macFunction { if version == versionSSL30 { mac := ssl30MAC{ h: sha1.New(), key: make([]byte, len(key)), } copy(mac.key, key) return mac } return tls10MAC{hmac.New(sha1.New, key)} } type macFunction interface { Size() int MAC(digestBuf, seq, data []byte) []byte } // ssl30MAC implements the SSLv3 MAC function, as defined in // www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 5.2.3.1 type ssl30MAC struct { h hash.Hash key []byte } func (s ssl30MAC) Size() int { return s.h.Size() } var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36} var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c} func (s ssl30MAC) MAC(digestBuf, seq, record []byte) []byte { padLength := 48 if s.h.Size() == 20 { padLength = 40 } s.h.Reset() s.h.Write(s.key) s.h.Write(ssl30Pad1[:padLength]) s.h.Write(seq) s.h.Write(record[:1]) s.h.Write(record[3:5]) s.h.Write(record[recordHeaderLen:]) digestBuf = s.h.Sum(digestBuf[:0]) s.h.Reset() s.h.Write(s.key) s.h.Write(ssl30Pad2[:padLength]) s.h.Write(digestBuf) return s.h.Sum(digestBuf[:0]) } // tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3. type tls10MAC struct { h hash.Hash } func (s tls10MAC) Size() int { return s.h.Size() } func (s tls10MAC) MAC(digestBuf, seq, record []byte) []byte { s.h.Reset() s.h.Write(seq) s.h.Write(record) return s.h.Sum(digestBuf[:0]) } func rsaKA() keyAgreement { return rsaKeyAgreement{} } func ecdheRSAKA() keyAgreement { return new(ecdheRSAKeyAgreement) } // mutualCipherSuite returns a cipherSuite given a list of supported // ciphersuites and the id requested by the peer. func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { for _, id := range have { if id == want { for _, suite := range cipherSuites { if suite.id == want { return suite } } return nil } } return nil } // A list of the possible cipher suite ids. Taken from // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml const ( TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/go-tls-renegotiation.patch����������������������������0000644�0000153�0000161�00000005724�12321735761�026125� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������diff -r c242bbf5fa8c src/pkg/crypto/tls/common.go --- a/src/pkg/crypto/tls/common.go Wed Jul 17 14:03:27 2013 -0400 +++ b/src/pkg/crypto/tls/common.go Thu Jul 18 13:45:43 2013 -0400 @@ -44,6 +44,7 @@ // TLS handshake message types. const ( + typeHelloRequest uint8 = 0 typeClientHello uint8 = 1 typeServerHello uint8 = 2 typeNewSessionTicket uint8 = 4 diff -r c242bbf5fa8c src/pkg/crypto/tls/conn.go --- a/src/pkg/crypto/tls/conn.go Wed Jul 17 14:03:27 2013 -0400 +++ b/src/pkg/crypto/tls/conn.go Thu Jul 18 13:45:43 2013 -0400 @@ -146,6 +146,9 @@ hc.mac = hc.nextMac hc.nextCipher = nil hc.nextMac = nil + for i := range hc.seq { + hc.seq[i] = 0 + } return nil } @@ -478,7 +481,7 @@ func (c *Conn) readRecord(want recordType) error { // Caller must be in sync with connection: // handshake data if handshake not yet completed, - // else application data. (We don't support renegotiation.) + // else application data. switch want { default: return c.sendAlert(alertInternalError) @@ -611,7 +614,7 @@ case recordTypeHandshake: // TODO(rsc): Should at least pick off connection close. - if typ != want { + if typ != want && !c.isClient { return c.sendAlert(alertNoRenegotiation) } c.hand.Write(data) @@ -741,6 +744,8 @@ data = c.hand.Next(4 + n) var m handshakeMessage switch data[0] { + case typeHelloRequest: + m = new(helloRequestMsg) case typeClientHello: m = new(clientHelloMsg) case typeServerHello: @@ -825,6 +830,25 @@ return n + m, c.setError(err) } +func (c *Conn) handleRenegotiation() error { + c.handshakeComplete = false + if !c.isClient { + panic("renegotiation should only happen for a client") + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + _, ok := msg.(*helloRequestMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return alertUnexpectedMessage + } + + return c.Handshake() +} + // Read can be made to time out and return a net.Error with Timeout() == true // after a fixed time limit; see SetDeadline and SetReadDeadline. func (c *Conn) Read(b []byte) (n int, err error) { @@ -844,6 +868,14 @@ // Soft error, like EAGAIN return 0, err } + if c.hand.Len() > 0 { + // We received handshake bytes, indicating the start of + // a renegotiation. + if err := c.handleRenegotiation(); err != nil { + return 0, err + } + continue + } } if err := c.error(); err != nil { return 0, err diff -r c242bbf5fa8c src/pkg/crypto/tls/handshake_messages.go --- a/src/pkg/crypto/tls/handshake_messages.go Wed Jul 17 14:03:27 2013 -0400 +++ b/src/pkg/crypto/tls/handshake_messages.go Thu Jul 18 13:45:43 2013 -0400 @@ -1243,6 +1243,17 @@ return true } +type helloRequestMsg struct { +} + +func (*helloRequestMsg) marshal() []byte { + return []byte{typeHelloRequest, 0, 0, 0} +} + +func (*helloRequestMsg) unmarshal(data []byte) bool { + return len(data) == 4 +} + func eqUint16s(x, y []uint16) bool { if len(x) != len(y) { return false ��������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/README������������������������������������������������0000644�0000153�0000161�00000001017�12321735761�021701� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This directory contains a fork of Go's standard libraries net/http and crypto/tls. This fork is required to support the TLS renegociation which is triggered by the Windows Azure Server when establishing an https connexion. TLS renegociation is currently not supported by Go's standard library. The fork is based on go version 2:1.0.2-2. The library crypto/tls is patched to support TLS renegociation (see the patch file "go-tls-renegotiation.patch"). The library net/http is patched to use the forked version of crypto/tls. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735761�022001� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/transfer.go��������������������������������������0000644�0000153�0000161�00000045412�12321735761�024162� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package http import ( "bufio" "bytes" "errors" "fmt" "io" "io/ioutil" "net/textproto" "strconv" "strings" ) // transferWriter inspects the fields of a user-supplied Request or Response, // sanitizes them without changing the user object and provides methods for // writing the respective header, body and trailer in wire format. type transferWriter struct { Method string Body io.Reader BodyCloser io.Closer ResponseToHEAD bool ContentLength int64 // -1 means unknown, 0 means exactly none Close bool TransferEncoding []string Trailer Header } func newTransferWriter(r interface{}) (t *transferWriter, err error) { t = &transferWriter{} // Extract relevant fields atLeastHTTP11 := false switch rr := r.(type) { case *Request: if rr.ContentLength != 0 && rr.Body == nil { return nil, fmt.Errorf("http: Request.ContentLength=%d with nil Body", rr.ContentLength) } t.Method = rr.Method t.Body = rr.Body t.BodyCloser = rr.Body t.ContentLength = rr.ContentLength t.Close = rr.Close t.TransferEncoding = rr.TransferEncoding t.Trailer = rr.Trailer atLeastHTTP11 = rr.ProtoAtLeast(1, 1) if t.Body != nil && len(t.TransferEncoding) == 0 && atLeastHTTP11 { if t.ContentLength == 0 { // Test to see if it's actually zero or just unset. var buf [1]byte n, _ := io.ReadFull(t.Body, buf[:]) if n == 1 { // Oh, guess there is data in this Body Reader after all. // The ContentLength field just wasn't set. // Stich the Body back together again, re-attaching our // consumed byte. t.ContentLength = -1 t.Body = io.MultiReader(bytes.NewBuffer(buf[:]), t.Body) } else { // Body is actually empty. t.Body = nil t.BodyCloser = nil } } if t.ContentLength < 0 { t.TransferEncoding = []string{"chunked"} } } case *Response: if rr.Request != nil { t.Method = rr.Request.Method } t.Body = rr.Body t.BodyCloser = rr.Body t.ContentLength = rr.ContentLength t.Close = rr.Close t.TransferEncoding = rr.TransferEncoding t.Trailer = rr.Trailer atLeastHTTP11 = rr.ProtoAtLeast(1, 1) t.ResponseToHEAD = noBodyExpected(t.Method) } // Sanitize Body,ContentLength,TransferEncoding if t.ResponseToHEAD { t.Body = nil t.TransferEncoding = nil // ContentLength is expected to hold Content-Length if t.ContentLength < 0 { return nil, ErrMissingContentLength } } else { if !atLeastHTTP11 || t.Body == nil { t.TransferEncoding = nil } if chunked(t.TransferEncoding) { t.ContentLength = -1 } else if t.Body == nil { // no chunking, no body t.ContentLength = 0 } } // Sanitize Trailer if !chunked(t.TransferEncoding) { t.Trailer = nil } return t, nil } func noBodyExpected(requestMethod string) bool { return requestMethod == "HEAD" } func (t *transferWriter) shouldSendContentLength() bool { if chunked(t.TransferEncoding) { return false } if t.ContentLength > 0 { return true } if t.ResponseToHEAD { return true } // Many servers expect a Content-Length for these methods if t.Method == "POST" || t.Method == "PUT" { return true } if t.ContentLength == 0 && isIdentity(t.TransferEncoding) { return true } return false } func (t *transferWriter) WriteHeader(w io.Writer) (err error) { if t.Close { _, err = io.WriteString(w, "Connection: close\r\n") if err != nil { return } } // Write Content-Length and/or Transfer-Encoding whose values are a // function of the sanitized field triple (Body, ContentLength, // TransferEncoding) if t.shouldSendContentLength() { io.WriteString(w, "Content-Length: ") _, err = io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n") if err != nil { return } } else if chunked(t.TransferEncoding) { _, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n") if err != nil { return } } // Write Trailer header if t.Trailer != nil { // TODO: At some point, there should be a generic mechanism for // writing long headers, using HTTP line splitting io.WriteString(w, "Trailer: ") needComma := false for k := range t.Trailer { k = CanonicalHeaderKey(k) switch k { case "Transfer-Encoding", "Trailer", "Content-Length": return &badStringError{"invalid Trailer key", k} } if needComma { io.WriteString(w, ",") } io.WriteString(w, k) needComma = true } _, err = io.WriteString(w, "\r\n") } return } func (t *transferWriter) WriteBody(w io.Writer) (err error) { var ncopy int64 // Write body if t.Body != nil { if chunked(t.TransferEncoding) { cw := newChunkedWriter(w) _, err = io.Copy(cw, t.Body) if err == nil { err = cw.Close() } } else if t.ContentLength == -1 { ncopy, err = io.Copy(w, t.Body) } else { ncopy, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength)) nextra, err := io.Copy(ioutil.Discard, t.Body) if err != nil { return err } ncopy += nextra } if err != nil { return err } if err = t.BodyCloser.Close(); err != nil { return err } } if t.ContentLength != -1 && t.ContentLength != ncopy { return fmt.Errorf("http: Request.ContentLength=%d with Body length %d", t.ContentLength, ncopy) } // TODO(petar): Place trailer writer code here. if chunked(t.TransferEncoding) { // Last chunk, empty trailer _, err = io.WriteString(w, "\r\n") } return } type transferReader struct { // Input Header Header StatusCode int RequestMethod string ProtoMajor int ProtoMinor int // Output Body io.ReadCloser ContentLength int64 TransferEncoding []string Close bool Trailer Header } // bodyAllowedForStatus returns whether a given response status code // permits a body. See RFC2616, section 4.4. func bodyAllowedForStatus(status int) bool { switch { case status >= 100 && status <= 199: return false case status == 204: return false case status == 304: return false } return true } // msg is *Request or *Response. func readTransfer(msg interface{}, r *bufio.Reader) (err error) { t := &transferReader{} // Unify input isResponse := false switch rr := msg.(type) { case *Response: t.Header = rr.Header t.StatusCode = rr.StatusCode t.RequestMethod = rr.Request.Method t.ProtoMajor = rr.ProtoMajor t.ProtoMinor = rr.ProtoMinor t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header) isResponse = true case *Request: t.Header = rr.Header t.ProtoMajor = rr.ProtoMajor t.ProtoMinor = rr.ProtoMinor // Transfer semantics for Requests are exactly like those for // Responses with status code 200, responding to a GET method t.StatusCode = 200 t.RequestMethod = "GET" default: panic("unexpected type") } // Default to HTTP/1.1 if t.ProtoMajor == 0 && t.ProtoMinor == 0 { t.ProtoMajor, t.ProtoMinor = 1, 1 } // Transfer encoding, content length t.TransferEncoding, err = fixTransferEncoding(t.RequestMethod, t.Header) if err != nil { return err } t.ContentLength, err = fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding) if err != nil { return err } // Trailer t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding) if err != nil { return err } // If there is no Content-Length or chunked Transfer-Encoding on a *Response // and the status is not 1xx, 204 or 304, then the body is unbounded. // See RFC2616, section 4.4. switch msg.(type) { case *Response: if t.ContentLength == -1 && !chunked(t.TransferEncoding) && bodyAllowedForStatus(t.StatusCode) { // Unbounded body. t.Close = true } } // Prepare body reader. ContentLength < 0 means chunked encoding // or close connection when finished, since multipart is not supported yet switch { case chunked(t.TransferEncoding): t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close} case t.ContentLength >= 0: // TODO: limit the Content-Length. This is an easy DoS vector. t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close} default: // t.ContentLength < 0, i.e. "Content-Length" not mentioned in header if t.Close { // Close semantics (i.e. HTTP/1.0) t.Body = &body{Reader: r, closing: t.Close} } else { // Persistent connection (i.e. HTTP/1.1) t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close} } } // Unify output switch rr := msg.(type) { case *Request: rr.Body = t.Body rr.ContentLength = t.ContentLength rr.TransferEncoding = t.TransferEncoding rr.Close = t.Close rr.Trailer = t.Trailer case *Response: rr.Body = t.Body rr.ContentLength = t.ContentLength rr.TransferEncoding = t.TransferEncoding rr.Close = t.Close rr.Trailer = t.Trailer } return nil } // Checks whether chunked is part of the encodings stack func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" } // Checks whether the encoding is explicitly "identity". func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" } // Sanitize transfer encoding func fixTransferEncoding(requestMethod string, header Header) ([]string, error) { raw, present := header["Transfer-Encoding"] if !present { return nil, nil } delete(header, "Transfer-Encoding") // Head responses have no bodies, so the transfer encoding // should be ignored. if requestMethod == "HEAD" { return nil, nil } encodings := strings.Split(raw[0], ",") te := make([]string, 0, len(encodings)) // TODO: Even though we only support "identity" and "chunked" // encodings, the loop below is designed with foresight. One // invariant that must be maintained is that, if present, // chunked encoding must always come first. for _, encoding := range encodings { encoding = strings.ToLower(strings.TrimSpace(encoding)) // "identity" encoding is not recorded if encoding == "identity" { break } if encoding != "chunked" { return nil, &badStringError{"unsupported transfer encoding", encoding} } te = te[0 : len(te)+1] te[len(te)-1] = encoding } if len(te) > 1 { return nil, &badStringError{"too many transfer encodings", strings.Join(te, ",")} } if len(te) > 0 { // Chunked encoding trumps Content-Length. See RFC 2616 // Section 4.4. Currently len(te) > 0 implies chunked // encoding. delete(header, "Content-Length") return te, nil } return nil, nil } // Determine the expected body length, using RFC 2616 Section 4.4. This // function is not a method, because ultimately it should be shared by // ReadResponse and ReadRequest. func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, error) { // Logic based on response type or status if noBodyExpected(requestMethod) { return 0, nil } if status/100 == 1 { return 0, nil } switch status { case 204, 304: return 0, nil } // Logic based on Transfer-Encoding if chunked(te) { return -1, nil } // Logic based on Content-Length cl := strings.TrimSpace(header.Get("Content-Length")) if cl != "" { n, err := strconv.ParseInt(cl, 10, 64) if err != nil || n < 0 { return -1, &badStringError{"bad Content-Length", cl} } return n, nil } else { header.Del("Content-Length") } if !isResponse && requestMethod == "GET" { // RFC 2616 doesn't explicitly permit nor forbid an // entity-body on a GET request so we permit one if // declared, but we default to 0 here (not -1 below) // if there's no mention of a body. return 0, nil } // Logic based on media type. The purpose of the following code is just // to detect whether the unsupported "multipart/byteranges" is being // used. A proper Content-Type parser is needed in the future. if strings.Contains(strings.ToLower(header.Get("Content-Type")), "multipart/byteranges") { return -1, ErrNotSupported } // Body-EOF logic based on other methods (like closing, or chunked coding) return -1, nil } // Determine whether to hang up after sending a request and body, or // receiving a response and body // 'header' is the request headers func shouldClose(major, minor int, header Header) bool { if major < 1 { return true } else if major == 1 && minor == 0 { if !strings.Contains(strings.ToLower(header.Get("Connection")), "keep-alive") { return true } return false } else { // TODO: Should split on commas, toss surrounding white space, // and check each field. if strings.ToLower(header.Get("Connection")) == "close" { header.Del("Connection") return true } } return false } // Parse the trailer header func fixTrailer(header Header, te []string) (Header, error) { raw := header.Get("Trailer") if raw == "" { return nil, nil } header.Del("Trailer") trailer := make(Header) keys := strings.Split(raw, ",") for _, key := range keys { key = CanonicalHeaderKey(strings.TrimSpace(key)) switch key { case "Transfer-Encoding", "Trailer", "Content-Length": return nil, &badStringError{"bad trailer key", key} } trailer.Del(key) } if len(trailer) == 0 { return nil, nil } if !chunked(te) { // Trailer and no chunking return nil, ErrUnexpectedTrailer } return trailer, nil } // body turns a Reader into a ReadCloser. // Close ensures that the body has been fully read // and then reads the trailer if necessary. type body struct { io.Reader hdr interface{} // non-nil (Response or Request) value means read trailer r *bufio.Reader // underlying wire-format reader for the trailer closing bool // is the connection to be closed after reading body? closed bool res *response // response writer for server requests, else nil } // ErrBodyReadAfterClose is returned when reading a Request Body after // the body has been closed. This typically happens when the body is // read after an HTTP Handler calls WriteHeader or Write on its // ResponseWriter. var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed request Body") func (b *body) Read(p []byte) (n int, err error) { if b.closed { return 0, ErrBodyReadAfterClose } n, err = b.Reader.Read(p) // Read the final trailer once we hit EOF. if err == io.EOF && b.hdr != nil { if e := b.readTrailer(); e != nil { err = e } b.hdr = nil } return n, err } var ( singleCRLF = []byte("\r\n") doubleCRLF = []byte("\r\n\r\n") ) func seeUpcomingDoubleCRLF(r *bufio.Reader) bool { for peekSize := 4; ; peekSize++ { // This loop stops when Peek returns an error, // which it does when r's buffer has been filled. buf, err := r.Peek(peekSize) if bytes.HasSuffix(buf, doubleCRLF) { return true } if err != nil { break } } return false } func (b *body) readTrailer() error { // The common case, since nobody uses trailers. buf, _ := b.r.Peek(2) if bytes.Equal(buf, singleCRLF) { b.r.ReadByte() b.r.ReadByte() return nil } // Make sure there's a header terminator coming up, to prevent // a DoS with an unbounded size Trailer. It's not easy to // slip in a LimitReader here, as textproto.NewReader requires // a concrete *bufio.Reader. Also, we can't get all the way // back up to our conn's LimitedReader that *might* be backing // this bufio.Reader. Instead, a hack: we iteratively Peek up // to the bufio.Reader's max size, looking for a double CRLF. // This limits the trailer to the underlying buffer size, typically 4kB. if !seeUpcomingDoubleCRLF(b.r) { return errors.New("http: suspiciously long trailer after chunked body") } hdr, err := textproto.NewReader(b.r).ReadMIMEHeader() if err != nil { return err } switch rr := b.hdr.(type) { case *Request: rr.Trailer = Header(hdr) case *Response: rr.Trailer = Header(hdr) } return nil } func (b *body) Close() error { if b.closed { return nil } defer func() { b.closed = true }() if b.hdr == nil && b.closing { // no trailer and closing the connection next. // no point in reading to EOF. return nil } // In a server request, don't continue reading from the client // if we've already hit the maximum body size set by the // handler. If this is set, that also means the TCP connection // is about to be closed, so getting to the next HTTP request // in the stream is not necessary. if b.res != nil && b.res.requestBodyLimitHit { return nil } // Fully consume the body, which will also lead to us reading // the trailer headers after the body, if present. if _, err := io.Copy(ioutil.Discard, b); err != nil { return err } return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/lex.go�������������������������������������������0000644�0000153�0000161�00000007001�12321735761�023116� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package http // This file deals with lexical matters of HTTP func isSeparator(c byte) bool { switch c { case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t': return true } return false } func isCtl(c byte) bool { return (0 <= c && c <= 31) || c == 127 } func isChar(c byte) bool { return 0 <= c && c <= 127 } func isAnyText(c byte) bool { return !isCtl(c) } func isQdText(c byte) bool { return isAnyText(c) && c != '"' } func isToken(c byte) bool { return isChar(c) && !isCtl(c) && !isSeparator(c) } // Valid escaped sequences are not specified in RFC 2616, so for now, we assume // that they coincide with the common sense ones used by GO. Malformed // characters should probably not be treated as errors by a robust (forgiving) // parser, so we replace them with the '?' character. func httpUnquotePair(b byte) byte { // skip the first byte, which should always be '\' switch b { case 'a': return '\a' case 'b': return '\b' case 'f': return '\f' case 'n': return '\n' case 'r': return '\r' case 't': return '\t' case 'v': return '\v' case '\\': return '\\' case '\'': return '\'' case '"': return '"' } return '?' } // raw must begin with a valid quoted string. Only the first quoted string is // parsed and is unquoted in result. eaten is the number of bytes parsed, or -1 // upon failure. func httpUnquote(raw []byte) (eaten int, result string) { buf := make([]byte, len(raw)) if raw[0] != '"' { return -1, "" } eaten = 1 j := 0 // # of bytes written in buf for i := 1; i < len(raw); i++ { switch b := raw[i]; b { case '"': eaten++ buf = buf[0:j] return i + 1, string(buf) case '\\': if len(raw) < i+2 { return -1, "" } buf[j] = httpUnquotePair(raw[i+1]) eaten += 2 j++ i++ default: if isQdText(b) { buf[j] = b } else { buf[j] = '?' } eaten++ j++ } } return -1, "" } // This is a best effort parse, so errors are not returned, instead not all of // the input string might be parsed. result is always non-nil. func httpSplitFieldValue(fv string) (eaten int, result []string) { result = make([]string, 0, len(fv)) raw := []byte(fv) i := 0 chunk := "" for i < len(raw) { b := raw[i] switch { case b == '"': eaten, unq := httpUnquote(raw[i:]) if eaten < 0 { return i, result } else { i += eaten chunk += unq } case isSeparator(b): if chunk != "" { result = result[0 : len(result)+1] result[len(result)-1] = chunk chunk = "" } i++ case isToken(b): chunk += string(b) i++ case b == '\n' || b == '\r': i++ default: chunk += "?" i++ } } if chunk != "" { result = result[0 : len(result)+1] result[len(result)-1] = chunk chunk = "" } return i, result } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/header.go����������������������������������������0000644�0000153�0000161�00000004603�12321735761�023563� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2010 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 http import ( "fmt" "io" "net/textproto" "sort" "strings" ) // A Header represents the key-value pairs in an HTTP header. type Header map[string][]string // Add adds the key, value pair to the header. // It appends to any existing values associated with key. func (h Header) Add(key, value string) { textproto.MIMEHeader(h).Add(key, value) } // Set sets the header entries associated with key to // the single element value. It replaces any existing // values associated with key. func (h Header) Set(key, value string) { textproto.MIMEHeader(h).Set(key, value) } // Get gets the first value associated with the given key. // If there are no values associated with the key, Get returns "". // To access multiple values of a key, access the map directly // with CanonicalHeaderKey. func (h Header) Get(key string) string { return textproto.MIMEHeader(h).Get(key) } // Del deletes the values associated with key. func (h Header) Del(key string) { textproto.MIMEHeader(h).Del(key) } // Write writes a header in wire format. func (h Header) Write(w io.Writer) error { return h.WriteSubset(w, nil) } var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ") // WriteSubset writes a header in wire format. // If exclude is not nil, keys where exclude[key] == true are not written. func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error { keys := make([]string, 0, len(h)) for k := range h { if exclude == nil || !exclude[k] { keys = append(keys, k) } } sort.Strings(keys) for _, k := range keys { for _, v := range h[k] { v = headerNewlineToSpace.Replace(v) v = strings.TrimSpace(v) if _, err := fmt.Fprintf(w, "%s: %s\r\n", k, v); err != nil { return err } } } return nil } // CanonicalHeaderKey returns the canonical format of the // header key s. The canonicalization converts the first // letter and any letter following a hyphen to upper case; // the rest are converted to lowercase. For example, the // canonical key for "accept-encoding" is "Accept-Encoding". func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) } �����������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/filetransport.go���������������������������������0000644�0000153�0000161�00000006246�12321735761�025234� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 http import ( "fmt" "io" ) // fileTransport implements RoundTripper for the 'file' protocol. type fileTransport struct { fh fileHandler } // NewFileTransport returns a new RoundTripper, serving the provided // FileSystem. The returned RoundTripper ignores the URL host in its // incoming requests, as well as most other properties of the // request. // // The typical use case for NewFileTransport is to register the "file" // protocol with a Transport, as in: // // t := &http.Transport{} // t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) // c := &http.Client{Transport: t} // res, err := c.Get("file:///etc/passwd") // ... func NewFileTransport(fs FileSystem) RoundTripper { return fileTransport{fileHandler{fs}} } func (t fileTransport) RoundTrip(req *Request) (resp *Response, err error) { // We start ServeHTTP in a goroutine, which may take a long // time if the file is large. The newPopulateResponseWriter // call returns a channel which either ServeHTTP or finish() // sends our *Response on, once the *Response itself has been // populated (even if the body itself is still being // written to the res.Body, a pipe) rw, resc := newPopulateResponseWriter() go func() { t.fh.ServeHTTP(rw, req) rw.finish() }() return <-resc, nil } func newPopulateResponseWriter() (*populateResponse, <-chan *Response) { pr, pw := io.Pipe() rw := &populateResponse{ ch: make(chan *Response), pw: pw, res: &Response{ Proto: "HTTP/1.0", ProtoMajor: 1, Header: make(Header), Close: true, Body: pr, }, } return rw, rw.ch } // populateResponse is a ResponseWriter that populates the *Response // in res, and writes its body to a pipe connected to the response // body. Once writes begin or finish() is called, the response is sent // on ch. type populateResponse struct { res *Response ch chan *Response wroteHeader bool hasContent bool sentResponse bool pw *io.PipeWriter } func (pr *populateResponse) finish() { if !pr.wroteHeader { pr.WriteHeader(500) } if !pr.sentResponse { pr.sendResponse() } pr.pw.Close() } func (pr *populateResponse) sendResponse() { if pr.sentResponse { return } pr.sentResponse = true if pr.hasContent { pr.res.ContentLength = -1 } pr.ch <- pr.res } func (pr *populateResponse) Header() Header { return pr.res.Header } func (pr *populateResponse) WriteHeader(code int) { if pr.wroteHeader { return } pr.wroteHeader = true pr.res.StatusCode = code pr.res.Status = fmt.Sprintf("%d %s", code, StatusText(code)) } func (pr *populateResponse) Write(p []byte) (n int, err error) { if !pr.wroteHeader { pr.WriteHeader(StatusOK) } pr.hasContent = true if !pr.sentResponse { pr.sendResponse() } return pr.pw.Write(p) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/doc.go�������������������������������������������0000644�0000153�0000161�00000004323�12321735761�023077� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 http provides HTTP client and server implementations. Get, Head, Post, and PostForm make HTTP requests: resp, err := http.Get("http://example.com/") ... resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf) ... resp, err := http.PostForm("http://example.com/form", url.Values{"key": {"Value"}, "id": {"123"}}) The client must close the response body when finished with it: resp, err := http.Get("http://example.com/") if err != nil { // handle error } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) // ... For control over HTTP client headers, redirect policy, and other settings, create a Client: client := &http.Client{ CheckRedirect: redirectPolicyFunc, } resp, err := client.Get("http://example.com") // ... req, err := http.NewRequest("GET", "http://example.com", nil) // ... req.Header.Add("If-None-Match", `W/"wyzzy"`) resp, err := client.Do(req) // ... For control over proxies, TLS configuration, keep-alives, compression, and other settings, create a Transport: tr := &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: pool}, DisableCompression: true, } client := &http.Client{Transport: tr} resp, err := client.Get("https://example.com") Clients and Transports are safe for concurrent use by multiple goroutines and for efficiency should only be created once and re-used. ListenAndServe starts an HTTP server with a given address and handler. The handler is usually nil, which means to use DefaultServeMux. Handle and HandleFunc add handlers to DefaultServeMux: http.Handle("/foo", fooHandler) http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) log.Fatal(http.ListenAndServe(":8080", nil)) More control over the server's behavior is available by creating a custom Server: s := &http.Server{ Addr: ":8080", Handler: myHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } log.Fatal(s.ListenAndServe()) */ package http �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/server.go����������������������������������������0000644�0000153�0000161�00000115437�12321735761�023651� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // HTTP server. See RFC 2616. // TODO(rsc): // logging package http import ( "bufio" "bytes" "crypto/tls" "errors" "fmt" "io" "io/ioutil" "log" "net" "net/url" "path" "runtime/debug" "strconv" "strings" "sync" "time" ) // Errors introduced by the HTTP server. var ( ErrWriteAfterFlush = errors.New("Conn.Write called after Flush") ErrBodyNotAllowed = errors.New("http: request method or response status code does not allow body") ErrHijacked = errors.New("Conn has been hijacked") ErrContentLength = errors.New("Conn.Write wrote more than the declared Content-Length") ) // Objects implementing the Handler interface can be // registered to serve a particular path or subtree // in the HTTP server. // // ServeHTTP should write reply headers and data to the ResponseWriter // and then return. Returning signals that the request is finished // and that the HTTP server can move on to the next request on // the connection. type Handler interface { ServeHTTP(ResponseWriter, *Request) } // A ResponseWriter interface is used by an HTTP handler to // construct an HTTP response. type ResponseWriter interface { // Header returns the header map that will be sent by WriteHeader. // Changing the header after a call to WriteHeader (or Write) has // no effect. Header() Header // Write writes the data to the connection as part of an HTTP reply. // If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) // before writing the data. If the Header does not contain a // Content-Type line, Write adds a Content-Type set to the result of passing // the initial 512 bytes of written data to DetectContentType. Write([]byte) (int, error) // WriteHeader sends an HTTP response header with status code. // If WriteHeader is not called explicitly, the first call to Write // will trigger an implicit WriteHeader(http.StatusOK). // Thus explicit calls to WriteHeader are mainly used to // send error codes. WriteHeader(int) } // The Flusher interface is implemented by ResponseWriters that allow // an HTTP handler to flush buffered data to the client. // // Note that even for ResponseWriters that support Flush, // if the client is connected through an HTTP proxy, // the buffered data may not reach the client until the response // completes. type Flusher interface { // Flush sends any buffered data to the client. Flush() } // The Hijacker interface is implemented by ResponseWriters that allow // an HTTP handler to take over the connection. type Hijacker interface { // Hijack lets the caller take over the connection. // After a call to Hijack(), the HTTP server library // will not do anything else with the connection. // It becomes the caller's responsibility to manage // and close the connection. Hijack() (net.Conn, *bufio.ReadWriter, error) } // A conn represents the server side of an HTTP connection. type conn struct { remoteAddr string // network address of remote side server *Server // the Server on which the connection arrived rwc net.Conn // i/o connection lr *io.LimitedReader // io.LimitReader(rwc) buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->rwc hijacked bool // connection has been hijacked by handler tlsState *tls.ConnectionState // or nil when not using TLS body []byte } // A response represents the server side of an HTTP response. type response struct { conn *conn req *Request // request for this response chunking bool // using chunked transfer encoding for reply body wroteHeader bool // reply header has been written wroteContinue bool // 100 Continue response was written header Header // reply header parameters written int64 // number of bytes written in body contentLength int64 // explicitly-declared Content-Length; or -1 status int // status code passed to WriteHeader needSniff bool // need to sniff to find Content-Type // close connection after this reply. set on request and // updated after response from handler if there's a // "Connection: keep-alive" response header and a // Content-Length. closeAfterReply bool // requestBodyLimitHit is set by requestTooLarge when // maxBytesReader hits its max size. It is checked in // WriteHeader, to make sure we don't consume the the // remaining request body to try to advance to the next HTTP // request. Instead, when this is set, we stop doing // subsequent requests on this connection and stop reading // input from it. requestBodyLimitHit bool } // requestTooLarge is called by maxBytesReader when too much input has // been read from the client. func (w *response) requestTooLarge() { w.closeAfterReply = true w.requestBodyLimitHit = true if !w.wroteHeader { w.Header().Set("Connection", "close") } } type writerOnly struct { io.Writer } func (w *response) ReadFrom(src io.Reader) (n int64, err error) { // Call WriteHeader before checking w.chunking if it hasn't // been called yet, since WriteHeader is what sets w.chunking. if !w.wroteHeader { w.WriteHeader(StatusOK) } if !w.chunking && w.bodyAllowed() && !w.needSniff { w.Flush() if rf, ok := w.conn.rwc.(io.ReaderFrom); ok { n, err = rf.ReadFrom(src) w.written += n return } } // Fall back to default io.Copy implementation. // Use wrapper to hide w.ReadFrom from io.Copy. return io.Copy(writerOnly{w}, src) } // noLimit is an effective infinite upper bound for io.LimitedReader const noLimit int64 = (1 << 63) - 1 // Create new connection from rwc. func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) { c = new(conn) c.remoteAddr = rwc.RemoteAddr().String() c.server = srv c.rwc = rwc c.body = make([]byte, sniffLen) c.lr = io.LimitReader(rwc, noLimit).(*io.LimitedReader) br := bufio.NewReader(c.lr) bw := bufio.NewWriter(rwc) c.buf = bufio.NewReadWriter(br, bw) return c, nil } // DefaultMaxHeaderBytes is the maximum permitted size of the headers // in an HTTP request. // This can be overridden by setting Server.MaxHeaderBytes. const DefaultMaxHeaderBytes = 1 << 20 // 1 MB func (srv *Server) maxHeaderBytes() int { if srv.MaxHeaderBytes > 0 { return srv.MaxHeaderBytes } return DefaultMaxHeaderBytes } // wrapper around io.ReaderCloser which on first read, sends an // HTTP/1.1 100 Continue header type expectContinueReader struct { resp *response readCloser io.ReadCloser closed bool } func (ecr *expectContinueReader) Read(p []byte) (n int, err error) { if ecr.closed { return 0, errors.New("http: Read after Close on request Body") } if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked { ecr.resp.wroteContinue = true io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n") ecr.resp.conn.buf.Flush() } return ecr.readCloser.Read(p) } func (ecr *expectContinueReader) Close() error { ecr.closed = true return ecr.readCloser.Close() } // TimeFormat is the time format to use with // time.Parse and time.Time.Format when parsing // or generating times in HTTP headers. // It is like time.RFC1123 but hard codes GMT as the time zone. const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" var errTooLarge = errors.New("http: request too large") // Read next request from connection. func (c *conn) readRequest() (w *response, err error) { if c.hijacked { return nil, ErrHijacked } c.lr.N = int64(c.server.maxHeaderBytes()) + 4096 /* bufio slop */ var req *Request if req, err = ReadRequest(c.buf.Reader); err != nil { if c.lr.N == 0 { return nil, errTooLarge } return nil, err } c.lr.N = noLimit req.RemoteAddr = c.remoteAddr req.TLS = c.tlsState w = new(response) w.conn = c w.req = req w.header = make(Header) w.contentLength = -1 c.body = c.body[:0] return w, nil } func (w *response) Header() Header { return w.header } // maxPostHandlerReadBytes is the max number of Request.Body bytes not // consumed by a handler that the server will read from the client // in order to keep a connection alive. If there are more bytes than // this then the server to be paranoid instead sends a "Connection: // close" response. // // This number is approximately what a typical machine's TCP buffer // size is anyway. (if we have the bytes on the machine, we might as // well read them) const maxPostHandlerReadBytes = 256 << 10 func (w *response) WriteHeader(code int) { if w.conn.hijacked { log.Print("http: response.WriteHeader on hijacked connection") return } if w.wroteHeader { log.Print("http: multiple response.WriteHeader calls") return } w.wroteHeader = true w.status = code // Check for a explicit (and valid) Content-Length header. var hasCL bool var contentLength int64 if clenStr := w.header.Get("Content-Length"); clenStr != "" { var err error contentLength, err = strconv.ParseInt(clenStr, 10, 64) if err == nil { hasCL = true } else { log.Printf("http: invalid Content-Length of %q sent", clenStr) w.header.Del("Content-Length") } } if w.req.wantsHttp10KeepAlive() && (w.req.Method == "HEAD" || hasCL) { _, connectionHeaderSet := w.header["Connection"] if !connectionHeaderSet { w.header.Set("Connection", "keep-alive") } } else if !w.req.ProtoAtLeast(1, 1) { // Client did not ask to keep connection alive. w.closeAfterReply = true } if w.header.Get("Connection") == "close" { w.closeAfterReply = true } // Per RFC 2616, we should consume the request body before // replying, if the handler hasn't already done so. But we // don't want to do an unbounded amount of reading here for // DoS reasons, so we only try up to a threshold. if w.req.ContentLength != 0 && !w.closeAfterReply { ecr, isExpecter := w.req.Body.(*expectContinueReader) if !isExpecter || ecr.resp.wroteContinue { n, _ := io.CopyN(ioutil.Discard, w.req.Body, maxPostHandlerReadBytes+1) if n >= maxPostHandlerReadBytes { w.requestTooLarge() w.header.Set("Connection", "close") } else { w.req.Body.Close() } } } if code == StatusNotModified { // Must not have body. for _, header := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} { if w.header.Get(header) != "" { // TODO: return an error if WriteHeader gets a return parameter // or set a flag on w to make future Writes() write an error page? // for now just log and drop the header. log.Printf("http: StatusNotModified response with header %q defined", header) w.header.Del(header) } } } else { // If no content type, apply sniffing algorithm to body. if w.header.Get("Content-Type") == "" && w.req.Method != "HEAD" { w.needSniff = true } } if _, ok := w.header["Date"]; !ok { w.Header().Set("Date", time.Now().UTC().Format(TimeFormat)) } te := w.header.Get("Transfer-Encoding") hasTE := te != "" if hasCL && hasTE && te != "identity" { // TODO: return an error if WriteHeader gets a return parameter // For now just ignore the Content-Length. log.Printf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d", te, contentLength) w.header.Del("Content-Length") hasCL = false } if w.req.Method == "HEAD" || code == StatusNotModified { // do nothing } else if hasCL { w.contentLength = contentLength w.header.Del("Transfer-Encoding") } else if w.req.ProtoAtLeast(1, 1) { // HTTP/1.1 or greater: use chunked transfer encoding // to avoid closing the connection at EOF. // TODO: this blows away any custom or stacked Transfer-Encoding they // might have set. Deal with that as need arises once we have a valid // use case. w.chunking = true w.header.Set("Transfer-Encoding", "chunked") } else { // HTTP version < 1.1: cannot do chunked transfer // encoding and we don't know the Content-Length so // signal EOF by closing connection. w.closeAfterReply = true w.header.Del("Transfer-Encoding") // in case already set } // Cannot use Content-Length with non-identity Transfer-Encoding. if w.chunking { w.header.Del("Content-Length") } if !w.req.ProtoAtLeast(1, 0) { return } proto := "HTTP/1.0" if w.req.ProtoAtLeast(1, 1) { proto = "HTTP/1.1" } codestring := strconv.Itoa(code) text, ok := statusText[code] if !ok { text = "status code " + codestring } io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n") w.header.Write(w.conn.buf) // If we need to sniff the body, leave the header open. // Otherwise, end it here. if !w.needSniff { io.WriteString(w.conn.buf, "\r\n") } } // sniff uses the first block of written data, // stored in w.conn.body, to decide the Content-Type // for the HTTP body. func (w *response) sniff() { if !w.needSniff { return } w.needSniff = false data := w.conn.body fmt.Fprintf(w.conn.buf, "Content-Type: %s\r\n\r\n", DetectContentType(data)) if len(data) == 0 { return } if w.chunking { fmt.Fprintf(w.conn.buf, "%x\r\n", len(data)) } _, err := w.conn.buf.Write(data) if w.chunking && err == nil { io.WriteString(w.conn.buf, "\r\n") } } // bodyAllowed returns true if a Write is allowed for this response type. // It's illegal to call this before the header has been flushed. func (w *response) bodyAllowed() bool { if !w.wroteHeader { panic("") } return w.status != StatusNotModified && w.req.Method != "HEAD" } func (w *response) Write(data []byte) (n int, err error) { if w.conn.hijacked { log.Print("http: response.Write on hijacked connection") return 0, ErrHijacked } if !w.wroteHeader { w.WriteHeader(StatusOK) } if len(data) == 0 { return 0, nil } if !w.bodyAllowed() { return 0, ErrBodyNotAllowed } w.written += int64(len(data)) // ignoring errors, for errorKludge if w.contentLength != -1 && w.written > w.contentLength { return 0, ErrContentLength } var m int if w.needSniff { // We need to sniff the beginning of the output to // determine the content type. Accumulate the // initial writes in w.conn.body. // Cap m so that append won't allocate. m = cap(w.conn.body) - len(w.conn.body) if m > len(data) { m = len(data) } w.conn.body = append(w.conn.body, data[:m]...) data = data[m:] if len(data) == 0 { // Copied everything into the buffer. // Wait for next write. return m, nil } // Filled the buffer; more data remains. // Sniff the content (flushes the buffer) // and then proceed with the remainder // of the data as a normal Write. // Calling sniff clears needSniff. w.sniff() } // TODO(rsc): if chunking happened after the buffering, // then there would be fewer chunk headers. // On the other hand, it would make hijacking more difficult. if w.chunking { fmt.Fprintf(w.conn.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt } n, err = w.conn.buf.Write(data) if err == nil && w.chunking { if n != len(data) { err = io.ErrShortWrite } if err == nil { io.WriteString(w.conn.buf, "\r\n") } } return m + n, err } func (w *response) finishRequest() { // If this was an HTTP/1.0 request with keep-alive and we sent a Content-Length // back, we can make this a keep-alive response ... if w.req.wantsHttp10KeepAlive() { sentLength := w.header.Get("Content-Length") != "" if sentLength && w.header.Get("Connection") == "keep-alive" { w.closeAfterReply = false } } if !w.wroteHeader { w.WriteHeader(StatusOK) } if w.needSniff { w.sniff() } if w.chunking { io.WriteString(w.conn.buf, "0\r\n") // trailer key/value pairs, followed by blank line io.WriteString(w.conn.buf, "\r\n") } w.conn.buf.Flush() // Close the body, unless we're about to close the whole TCP connection // anyway. if !w.closeAfterReply { w.req.Body.Close() } if w.req.MultipartForm != nil { w.req.MultipartForm.RemoveAll() } if w.contentLength != -1 && w.contentLength != w.written { // Did not write enough. Avoid getting out of sync. w.closeAfterReply = true } } func (w *response) Flush() { if !w.wroteHeader { w.WriteHeader(StatusOK) } w.sniff() w.conn.buf.Flush() } // Close the connection. func (c *conn) close() { if c.buf != nil { c.buf.Flush() c.buf = nil } if c.rwc != nil { c.rwc.Close() c.rwc = nil } } // Serve a new connection. func (c *conn) serve() { defer func() { err := recover() if err == nil { return } var buf bytes.Buffer fmt.Fprintf(&buf, "http: panic serving %v: %v\n", c.remoteAddr, err) buf.Write(debug.Stack()) log.Print(buf.String()) if c.rwc != nil { // may be nil if connection hijacked c.rwc.Close() } }() if tlsConn, ok := c.rwc.(*tls.Conn); ok { if err := tlsConn.Handshake(); err != nil { c.close() return } c.tlsState = new(tls.ConnectionState) *c.tlsState = tlsConn.ConnectionState() } for { w, err := c.readRequest() if err != nil { msg := "400 Bad Request" if err == errTooLarge { // Their HTTP client may or may not be // able to read this if we're // responding to them and hanging up // while they're still writing their // request. Undefined behavior. msg = "413 Request Entity Too Large" } else if err == io.EOF { break // Don't reply } else if neterr, ok := err.(net.Error); ok && neterr.Timeout() { break // Don't reply } fmt.Fprintf(c.rwc, "HTTP/1.1 %s\r\n\r\n", msg) break } // Expect 100 Continue support req := w.req if req.expectsContinue() { if req.ProtoAtLeast(1, 1) { // Wrap the Body reader with one that replies on the connection req.Body = &expectContinueReader{readCloser: req.Body, resp: w} } if req.ContentLength == 0 { w.Header().Set("Connection", "close") w.WriteHeader(StatusBadRequest) w.finishRequest() break } req.Header.Del("Expect") } else if req.Header.Get("Expect") != "" { // TODO(bradfitz): let ServeHTTP handlers handle // requests with non-standard expectation[s]? Seems // theoretical at best, and doesn't fit into the // current ServeHTTP model anyway. We'd need to // make the ResponseWriter an optional // "ExpectReplier" interface or something. // // For now we'll just obey RFC 2616 14.20 which says // "If a server receives a request containing an // Expect field that includes an expectation- // extension that it does not support, it MUST // respond with a 417 (Expectation Failed) status." w.Header().Set("Connection", "close") w.WriteHeader(StatusExpectationFailed) w.finishRequest() break } handler := c.server.Handler if handler == nil { handler = DefaultServeMux } // HTTP cannot have multiple simultaneous active requests.[*] // Until the server replies to this request, it can't read another, // so we might as well run the handler in this goroutine. // [*] Not strictly true: HTTP pipelining. We could let them all process // in parallel even if their responses need to be serialized. handler.ServeHTTP(w, w.req) if c.hijacked { return } w.finishRequest() if w.closeAfterReply { break } } c.close() } // Hijack implements the Hijacker.Hijack method. Our response is both a ResponseWriter // and a Hijacker. func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) { if w.conn.hijacked { return nil, nil, ErrHijacked } w.conn.hijacked = true rwc = w.conn.rwc buf = w.conn.buf w.conn.rwc = nil w.conn.buf = nil return } // The HandlerFunc type is an adapter to allow the use of // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler object that calls f. type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } // Helper handlers // Error replies to the request with the specified error message and HTTP code. func Error(w ResponseWriter, error string, code int) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(code) fmt.Fprintln(w, error) } // NotFound replies to the request with an HTTP 404 not found error. func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) } // NotFoundHandler returns a simple request handler // that replies to each request with a ``404 page not found'' reply. func NotFoundHandler() Handler { return HandlerFunc(NotFound) } // StripPrefix returns a handler that serves HTTP requests // by removing the given prefix from the request URL's Path // and invoking the handler h. StripPrefix handles a // request for a path that doesn't begin with prefix by // replying with an HTTP 404 not found error. func StripPrefix(prefix string, h Handler) Handler { return HandlerFunc(func(w ResponseWriter, r *Request) { if !strings.HasPrefix(r.URL.Path, prefix) { NotFound(w, r) return } r.URL.Path = r.URL.Path[len(prefix):] h.ServeHTTP(w, r) }) } // Redirect replies to the request with a redirect to url, // which may be a path relative to the request path. func Redirect(w ResponseWriter, r *Request, urlStr string, code int) { if u, err := url.Parse(urlStr); err == nil { // If url was relative, make absolute by // combining with request path. // The browser would probably do this for us, // but doing it ourselves is more reliable. // NOTE(rsc): RFC 2616 says that the Location // line must be an absolute URI, like // "http://www.google.com/redirect/", // not a path like "/redirect/". // Unfortunately, we don't know what to // put in the host name section to get the // client to connect to us again, so we can't // know the right absolute URI to send back. // Because of this problem, no one pays attention // to the RFC; they all send back just a new path. // So do we. oldpath := r.URL.Path if oldpath == "" { // should not happen, but avoid a crash if it does oldpath = "/" } if u.Scheme == "" { // no leading http://server if urlStr == "" || urlStr[0] != '/' { // make relative path absolute olddir, _ := path.Split(oldpath) urlStr = olddir + urlStr } var query string if i := strings.Index(urlStr, "?"); i != -1 { urlStr, query = urlStr[:i], urlStr[i:] } // clean up but preserve trailing slash trailing := urlStr[len(urlStr)-1] == '/' urlStr = path.Clean(urlStr) if trailing && urlStr[len(urlStr)-1] != '/' { urlStr += "/" } urlStr += query } } w.Header().Set("Location", urlStr) w.WriteHeader(code) // RFC2616 recommends that a short note "SHOULD" be included in the // response because older user agents may not understand 301/307. // Shouldn't send the response for POST or HEAD; that leaves GET. if r.Method == "GET" { note := "<a href=\"" + htmlEscape(urlStr) + "\">" + statusText[code] + "</a>.\n" fmt.Fprintln(w, note) } } var htmlReplacer = strings.NewReplacer( "&", "&amp;", "<", "&lt;", ">", "&gt;", // "&#34;" is shorter than "&quot;". `"`, "&#34;", // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5. "'", "&#39;", ) func htmlEscape(s string) string { return htmlReplacer.Replace(s) } // Redirect to a fixed URL type redirectHandler struct { url string code int } func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request) { Redirect(w, r, rh.url, rh.code) } // RedirectHandler returns a request handler that redirects // each request it receives to the given url using the given // status code. func RedirectHandler(url string, code int) Handler { return &redirectHandler{url, code} } // ServeMux is an HTTP request multiplexer. // It matches the URL of each incoming request against a list of registered // patterns and calls the handler for the pattern that // most closely matches the URL. // // Patterns named fixed, rooted paths, like "/favicon.ico", // or rooted subtrees, like "/images/" (note the trailing slash). // Longer patterns take precedence over shorter ones, so that // if there are handlers registered for both "/images/" // and "/images/thumbnails/", the latter handler will be // called for paths beginning "/images/thumbnails/" and the // former will receiver requests for any other paths in the // "/images/" subtree. // // Patterns may optionally begin with a host name, restricting matches to // URLs on that host only. Host-specific patterns take precedence over // general patterns, so that a handler might register for the two patterns // "/codesearch" and "codesearch.google.com/" without also taking over // requests for "http://www.google.com/". // // ServeMux also takes care of sanitizing the URL request path, // redirecting any request containing . or .. elements to an // equivalent .- and ..-free URL. type ServeMux struct { mu sync.RWMutex m map[string]muxEntry } type muxEntry struct { explicit bool h Handler } // NewServeMux allocates and returns a new ServeMux. func NewServeMux() *ServeMux { return &ServeMux{m: make(map[string]muxEntry)} } // DefaultServeMux is the default ServeMux used by Serve. var DefaultServeMux = NewServeMux() // Does path match pattern? func pathMatch(pattern, path string) bool { if len(pattern) == 0 { // should not happen return false } n := len(pattern) if pattern[n-1] != '/' { return pattern == path } return len(path) >= n && path[0:n] == pattern } // Return the canonical path for p, eliminating . and .. elements. func cleanPath(p string) string { if p == "" { return "/" } if p[0] != '/' { p = "/" + p } np := path.Clean(p) // path.Clean removes trailing slash except for root; // put the trailing slash back if necessary. if p[len(p)-1] == '/' && np != "/" { np += "/" } return np } // Find a handler on a handler map given a path string // Most-specific (longest) pattern wins func (mux *ServeMux) match(path string) Handler { var h Handler var n = 0 for k, v := range mux.m { if !pathMatch(k, path) { continue } if h == nil || len(k) > n { n = len(k) h = v.h } } return h } // handler returns the handler to use for the request r. func (mux *ServeMux) handler(r *Request) Handler { mux.mu.RLock() defer mux.mu.RUnlock() // Host-specific pattern takes precedence over generic ones h := mux.match(r.Host + r.URL.Path) if h == nil { h = mux.match(r.URL.Path) } if h == nil { h = NotFoundHandler() } return h } // ServeHTTP dispatches the request to the handler whose // pattern most closely matches the request URL. func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { // Clean path to canonical form and redirect. if p := cleanPath(r.URL.Path); p != r.URL.Path { w.Header().Set("Location", p) w.WriteHeader(StatusMovedPermanently) return } mux.handler(r).ServeHTTP(w, r) } // Handle registers the handler for the given pattern. // If a handler already exists for pattern, Handle panics. func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() if pattern == "" { panic("http: invalid pattern " + pattern) } if handler == nil { panic("http: nil handler") } if mux.m[pattern].explicit { panic("http: multiple registrations for " + pattern) } mux.m[pattern] = muxEntry{explicit: true, h: handler} // Helpful behavior: // If pattern is /tree/, insert an implicit permanent redirect for /tree. // It can be overridden by an explicit registration. n := len(pattern) if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit { mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(pattern, StatusMovedPermanently)} } } // HandleFunc registers the handler function for the given pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) } // Handle registers the handler for the given pattern // in the DefaultServeMux. // The documentation for ServeMux explains how patterns are matched. func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } // HandleFunc registers the handler function for the given pattern // in the DefaultServeMux. // The documentation for ServeMux explains how patterns are matched. func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) } // Serve accepts incoming HTTP connections on the listener l, // creating a new service thread for each. The service threads // read requests and then call handler to reply to them. // Handler is typically nil, in which case the DefaultServeMux is used. func Serve(l net.Listener, handler Handler) error { srv := &Server{Handler: handler} return srv.Serve(l) } // A Server defines parameters for running an HTTP server. type Server struct { Addr string // TCP address to listen on, ":http" if empty Handler Handler // handler to invoke, http.DefaultServeMux if nil ReadTimeout time.Duration // maximum duration before timing out read of the request WriteTimeout time.Duration // maximum duration before timing out write of the response MaxHeaderBytes int // maximum size of request headers, DefaultMaxHeaderBytes if 0 TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS } // ListenAndServe listens on the TCP network address srv.Addr and then // calls Serve to handle requests on incoming connections. If // srv.Addr is blank, ":http" is used. func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":http" } l, e := net.Listen("tcp", addr) if e != nil { return e } return srv.Serve(l) } // Serve accepts incoming connections on the Listener l, creating a // new service thread for each. The service threads read requests and // then call srv.Handler to reply to them. func (srv *Server) Serve(l net.Listener) error { defer l.Close() var tempDelay time.Duration // how long to sleep on accept failure for { rw, e := l.Accept() if e != nil { if ne, ok := e.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay) time.Sleep(tempDelay) continue } return e } tempDelay = 0 if srv.ReadTimeout != 0 { rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout)) } if srv.WriteTimeout != 0 { rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout)) } c, err := srv.newConn(rw) if err != nil { continue } go c.serve() } panic("not reached") } // ListenAndServe listens on the TCP network address addr // and then calls Serve with handler to handle requests // on incoming connections. Handler is typically nil, // in which case the DefaultServeMux is used. // // A trivial example server is: // // package main // // import ( // "io" // "net/http" // "log" // ) // // // hello world, the web server // func HelloServer(w http.ResponseWriter, req *http.Request) { // io.WriteString(w, "hello, world!\n") // } // // func main() { // http.HandleFunc("/hello", HelloServer) // err := http.ListenAndServe(":12345", nil) // if err != nil { // log.Fatal("ListenAndServe: ", err) // } // } func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() } // ListenAndServeTLS acts identically to ListenAndServe, except that it // expects HTTPS connections. Additionally, files containing a certificate and // matching private key for the server must be provided. If the certificate // is signed by a certificate authority, the certFile should be the concatenation // of the server's certificate followed by the CA's certificate. // // A trivial example server is: // // import ( // "log" // "net/http" // ) // // func handler(w http.ResponseWriter, req *http.Request) { // w.Header().Set("Content-Type", "text/plain") // w.Write([]byte("This is an example server.\n")) // } // // func main() { // http.HandleFunc("/", handler) // log.Printf("About to listen on 10443. Go to https://127.0.0.1:10443/") // err := http.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil) // if err != nil { // log.Fatal(err) // } // } // // One can use generate_cert.go in crypto/tls to generate cert.pem and key.pem. func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServeTLS(certFile, keyFile) } // ListenAndServeTLS listens on the TCP network address srv.Addr and // then calls Serve to handle requests on incoming TLS connections. // // Filenames containing a certificate and matching private key for // the server must be provided. If the certificate is signed by a // certificate authority, the certFile should be the concatenation // of the server's certificate followed by the CA's certificate. // // If srv.Addr is blank, ":https" is used. func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error { addr := srv.Addr if addr == "" { addr = ":https" } config := &tls.Config{} if srv.TLSConfig != nil { *config = *srv.TLSConfig } if config.NextProtos == nil { config.NextProtos = []string{"http/1.1"} } var err error config.Certificates = make([]tls.Certificate, 1) config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return err } conn, err := net.Listen("tcp", addr) if err != nil { return err } tlsListener := tls.NewListener(conn, config) return srv.Serve(tlsListener) } // TimeoutHandler returns a Handler that runs h with the given time limit. // // The new Handler calls h.ServeHTTP to handle each request, but if a // call runs for more than ns nanoseconds, the handler responds with // a 503 Service Unavailable error and the given message in its body. // (If msg is empty, a suitable default message will be sent.) // After such a timeout, writes by h to its ResponseWriter will return // ErrHandlerTimeout. func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler { f := func() <-chan time.Time { return time.After(dt) } return &timeoutHandler{h, f, msg} } // ErrHandlerTimeout is returned on ResponseWriter Write calls // in handlers which have timed out. var ErrHandlerTimeout = errors.New("http: Handler timeout") type timeoutHandler struct { handler Handler timeout func() <-chan time.Time // returns channel producing a timeout body string } func (h *timeoutHandler) errorBody() string { if h.body != "" { return h.body } return "<html><head><title>Timeout</title></head><body><h1>Timeout</h1></body></html>" } func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) { done := make(chan bool) tw := &timeoutWriter{w: w} go func() { h.handler.ServeHTTP(tw, r) done <- true }() select { case <-done: return case <-h.timeout(): tw.mu.Lock() defer tw.mu.Unlock() if !tw.wroteHeader { tw.w.WriteHeader(StatusServiceUnavailable) tw.w.Write([]byte(h.errorBody())) } tw.timedOut = true } } type timeoutWriter struct { w ResponseWriter mu sync.Mutex timedOut bool wroteHeader bool } func (tw *timeoutWriter) Header() Header { return tw.w.Header() } func (tw *timeoutWriter) Write(p []byte) (int, error) { tw.mu.Lock() timedOut := tw.timedOut tw.mu.Unlock() if timedOut { return 0, ErrHandlerTimeout } return tw.w.Write(p) } func (tw *timeoutWriter) WriteHeader(code int) { tw.mu.Lock() if tw.timedOut || tw.wroteHeader { tw.mu.Unlock() return } tw.wroteHeader = true tw.mu.Unlock() tw.w.WriteHeader(code) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/transport.go�������������������������������������0000644�0000153�0000161�00000053524�12321735761�024375� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // HTTP client implementation. See RFC 2616. // // This is the low-level Transport implementation of RoundTripper. // The high-level interface is in client.go. package http import ( "bufio" "compress/gzip" "encoding/base64" "errors" "fmt" "io" "io/ioutil" "launchpad.net/gwacl/fork/tls" "log" "net" "net/url" "os" "strings" "sync" ) // DefaultTransport is the default implementation of Transport and is // used by DefaultClient. It establishes a new network connection for // each call to Do and uses HTTP proxies as directed by the // $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy) // environment variables. var DefaultTransport RoundTripper = &Transport{Proxy: ProxyFromEnvironment} // DefaultMaxIdleConnsPerHost is the default value of Transport's // MaxIdleConnsPerHost. const DefaultMaxIdleConnsPerHost = 2 // Transport is an implementation of RoundTripper that supports http, // https, and http proxies (for either http or https with CONNECT). // Transport can also cache connections for future re-use. type Transport struct { lk sync.Mutex idleConn map[string][]*persistConn altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper // TODO: tunable on global max cached connections // TODO: tunable on timeout on cached connections // TODO: optional pipelining // Proxy specifies a function to return a proxy for a given // Request. If the function returns a non-nil error, the // request is aborted with the provided error. // If Proxy is nil or returns a nil *URL, no proxy is used. Proxy func(*Request) (*url.URL, error) // Dial specifies the dial function for creating TCP // connections. // If Dial is nil, net.Dial is used. Dial func(net, addr string) (c net.Conn, err error) // TLSClientConfig specifies the TLS configuration to use with // tls.Client. If nil, the default configuration is used. TLSClientConfig *tls.Config DisableKeepAlives bool DisableCompression bool // MaxIdleConnsPerHost, if non-zero, controls the maximum idle // (keep-alive) to keep to keep per-host. If zero, // DefaultMaxIdleConnsPerHost is used. MaxIdleConnsPerHost int } // ProxyFromEnvironment returns the URL of the proxy to use for a // given request, as indicated by the environment variables // $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy). // An error is returned if the proxy environment is invalid. // A nil URL and nil error are returned if no proxy is defined in the // environment, or a proxy should not be used for the given request. func ProxyFromEnvironment(req *Request) (*url.URL, error) { proxy := getenvEitherCase("HTTP_PROXY") if proxy == "" { return nil, nil } if !useProxy(canonicalAddr(req.URL)) { return nil, nil } proxyURL, err := url.Parse(proxy) if err != nil || proxyURL.Scheme == "" { if u, err := url.Parse("http://" + proxy); err == nil { proxyURL = u err = nil } } if err != nil { return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err) } return proxyURL, nil } // ProxyURL returns a proxy function (for use in a Transport) // that always returns the same URL. func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) { return func(*Request) (*url.URL, error) { return fixedURL, nil } } // transportRequest is a wrapper around a *Request that adds // optional extra headers to write. type transportRequest struct { *Request // original request, not to be mutated extra Header // extra headers to write, or nil } func (tr *transportRequest) extraHeaders() Header { if tr.extra == nil { tr.extra = make(Header) } return tr.extra } // RoundTrip implements the RoundTripper interface. func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) { if req.URL == nil { return nil, errors.New("http: nil Request.URL") } if req.Header == nil { return nil, errors.New("http: nil Request.Header") } if req.URL.Scheme != "http" && req.URL.Scheme != "https" { t.lk.Lock() var rt RoundTripper if t.altProto != nil { rt = t.altProto[req.URL.Scheme] } t.lk.Unlock() if rt == nil { return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme} } return rt.RoundTrip(req) } treq := &transportRequest{Request: req} cm, err := t.connectMethodForRequest(treq) if err != nil { return nil, err } // Get the cached or newly-created connection to either the // host (for http or https), the http proxy, or the http proxy // pre-CONNECTed to https server. In any case, we'll be ready // to send it requests. pconn, err := t.getConn(cm) if err != nil { return nil, err } return pconn.roundTrip(treq) } // RegisterProtocol registers a new protocol with scheme. // The Transport will pass requests using the given scheme to rt. // It is rt's responsibility to simulate HTTP request semantics. // // RegisterProtocol can be used by other packages to provide // implementations of protocol schemes like "ftp" or "file". func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) { if scheme == "http" || scheme == "https" { panic("protocol " + scheme + " already registered") } t.lk.Lock() defer t.lk.Unlock() if t.altProto == nil { t.altProto = make(map[string]RoundTripper) } if _, exists := t.altProto[scheme]; exists { panic("protocol " + scheme + " already registered") } t.altProto[scheme] = rt } // CloseIdleConnections closes any connections which were previously // connected from previous requests but are now sitting idle in // a "keep-alive" state. It does not interrupt any connections currently // in use. func (t *Transport) CloseIdleConnections() { t.lk.Lock() defer t.lk.Unlock() if t.idleConn == nil { return } for _, conns := range t.idleConn { for _, pconn := range conns { pconn.close() } } t.idleConn = make(map[string][]*persistConn) } // // Private implementation past this point. // func getenvEitherCase(k string) string { if v := os.Getenv(strings.ToUpper(k)); v != "" { return v } return os.Getenv(strings.ToLower(k)) } func (t *Transport) connectMethodForRequest(treq *transportRequest) (*connectMethod, error) { cm := &connectMethod{ targetScheme: treq.URL.Scheme, targetAddr: canonicalAddr(treq.URL), } if t.Proxy != nil { var err error cm.proxyURL, err = t.Proxy(treq.Request) if err != nil { return nil, err } } return cm, nil } // proxyAuth returns the Proxy-Authorization header to set // on requests, if applicable. func (cm *connectMethod) proxyAuth() string { if cm.proxyURL == nil { return "" } if u := cm.proxyURL.User; u != nil { return "Basic " + base64.URLEncoding.EncodeToString([]byte(u.String())) } return "" } // putIdleConn adds pconn to the list of idle persistent connections awaiting // a new request. // If pconn is no longer needed or not in a good state, putIdleConn // returns false. func (t *Transport) putIdleConn(pconn *persistConn) bool { t.lk.Lock() defer t.lk.Unlock() if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 { pconn.close() return false } if pconn.isBroken() { return false } key := pconn.cacheKey max := t.MaxIdleConnsPerHost if max == 0 { max = DefaultMaxIdleConnsPerHost } if len(t.idleConn[key]) >= max { pconn.close() return false } t.idleConn[key] = append(t.idleConn[key], pconn) return true } func (t *Transport) getIdleConn(cm *connectMethod) (pconn *persistConn) { t.lk.Lock() defer t.lk.Unlock() if t.idleConn == nil { t.idleConn = make(map[string][]*persistConn) } key := cm.String() for { pconns, ok := t.idleConn[key] if !ok { return nil } if len(pconns) == 1 { pconn = pconns[0] delete(t.idleConn, key) } else { // 2 or more cached connections; pop last // TODO: queue? pconn = pconns[len(pconns)-1] t.idleConn[key] = pconns[0 : len(pconns)-1] } if !pconn.isBroken() { return } } return } func (t *Transport) dial(network, addr string) (c net.Conn, err error) { if t.Dial != nil { return t.Dial(network, addr) } return net.Dial(network, addr) } // getConn dials and creates a new persistConn to the target as // specified in the connectMethod. This includes doing a proxy CONNECT // and/or setting up TLS. If this doesn't return an error, the persistConn // is ready to write requests to. func (t *Transport) getConn(cm *connectMethod) (*persistConn, error) { if pc := t.getIdleConn(cm); pc != nil { return pc, nil } conn, err := t.dial("tcp", cm.addr()) if err != nil { if cm.proxyURL != nil { err = fmt.Errorf("http: error connecting to proxy %s: %v", cm.proxyURL, err) } return nil, err } pa := cm.proxyAuth() pconn := &persistConn{ t: t, cacheKey: cm.String(), conn: conn, reqch: make(chan requestAndChan, 50), } switch { case cm.proxyURL == nil: // Do nothing. case cm.targetScheme == "http": pconn.isProxy = true if pa != "" { pconn.mutateHeaderFunc = func(h Header) { h.Set("Proxy-Authorization", pa) } } case cm.targetScheme == "https": connectReq := &Request{ Method: "CONNECT", URL: &url.URL{Opaque: cm.targetAddr}, Host: cm.targetAddr, Header: make(Header), } if pa != "" { connectReq.Header.Set("Proxy-Authorization", pa) } connectReq.Write(conn) // Read response. // Okay to use and discard buffered reader here, because // TLS server will not speak until spoken to. br := bufio.NewReader(conn) resp, err := ReadResponse(br, connectReq) if err != nil { conn.Close() return nil, err } if resp.StatusCode != 200 { f := strings.SplitN(resp.Status, " ", 2) conn.Close() return nil, errors.New(f[1]) } } if cm.targetScheme == "https" { // Initiate TLS and check remote host name against certificate. conn = tls.Client(conn, t.TLSClientConfig) if err = conn.(*tls.Conn).Handshake(); err != nil { return nil, err } if t.TLSClientConfig == nil || !t.TLSClientConfig.InsecureSkipVerify { if err = conn.(*tls.Conn).VerifyHostname(cm.tlsHost()); err != nil { return nil, err } } pconn.conn = conn } pconn.br = bufio.NewReader(pconn.conn) pconn.bw = bufio.NewWriter(pconn.conn) go pconn.readLoop() return pconn, nil } // useProxy returns true if requests to addr should use a proxy, // according to the NO_PROXY or no_proxy environment variable. // addr is always a canonicalAddr with a host and port. func useProxy(addr string) bool { if len(addr) == 0 { return true } host, _, err := net.SplitHostPort(addr) if err != nil { return false } if host == "localhost" { return false } if ip := net.ParseIP(host); ip != nil { if ip.IsLoopback() { return false } } no_proxy := getenvEitherCase("NO_PROXY") if no_proxy == "*" { return false } addr = strings.ToLower(strings.TrimSpace(addr)) if hasPort(addr) { addr = addr[:strings.LastIndex(addr, ":")] } for _, p := range strings.Split(no_proxy, ",") { p = strings.ToLower(strings.TrimSpace(p)) if len(p) == 0 { continue } if hasPort(p) { p = p[:strings.LastIndex(p, ":")] } if addr == p || (p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:])) { return false } } return true } // connectMethod is the map key (in its String form) for keeping persistent // TCP connections alive for subsequent HTTP requests. // // A connect method may be of the following types: // // Cache key form Description // ----------------- ------------------------- // ||http|foo.com http directly to server, no proxy // ||https|foo.com https directly to server, no proxy // http://proxy.com|https|foo.com http to proxy, then CONNECT to foo.com // http://proxy.com|http http to proxy, http to anywhere after that // // Note: no support to https to the proxy yet. // type connectMethod struct { proxyURL *url.URL // nil for no proxy, else full proxy URL targetScheme string // "http" or "https" targetAddr string // Not used if proxy + http targetScheme (4th example in table) } func (ck *connectMethod) String() string { proxyStr := "" targetAddr := ck.targetAddr if ck.proxyURL != nil { proxyStr = ck.proxyURL.String() if ck.targetScheme == "http" { targetAddr = "" } } return strings.Join([]string{proxyStr, ck.targetScheme, targetAddr}, "|") } // addr returns the first hop "host:port" to which we need to TCP connect. func (cm *connectMethod) addr() string { if cm.proxyURL != nil { return canonicalAddr(cm.proxyURL) } return cm.targetAddr } // tlsHost returns the host name to match against the peer's // TLS certificate. func (cm *connectMethod) tlsHost() string { h := cm.targetAddr if hasPort(h) { h = h[:strings.LastIndex(h, ":")] } return h } // persistConn wraps a connection, usually a persistent one // (but may be used for non-keep-alive requests as well) type persistConn struct { t *Transport cacheKey string // its connectMethod.String() conn net.Conn br *bufio.Reader // from conn bw *bufio.Writer // to conn reqch chan requestAndChan // written by roundTrip(); read by readLoop() isProxy bool // mutateHeaderFunc is an optional func to modify extra // headers on each outbound request before it's written. (the // original Request given to RoundTrip is not modified) mutateHeaderFunc func(Header) lk sync.Mutex // guards numExpectedResponses and broken numExpectedResponses int broken bool // an error has happened on this connection; marked broken so it's not reused. } func (pc *persistConn) isBroken() bool { pc.lk.Lock() defer pc.lk.Unlock() return pc.broken } var remoteSideClosedFunc func(error) bool // or nil to use default func remoteSideClosed(err error) bool { if err == io.EOF { return true } if remoteSideClosedFunc != nil { return remoteSideClosedFunc(err) } return false } func (pc *persistConn) readLoop() { alive := true var lastbody io.ReadCloser // last response body, if any, read on this connection for alive { pb, err := pc.br.Peek(1) pc.lk.Lock() if pc.numExpectedResponses == 0 { pc.closeLocked() pc.lk.Unlock() if len(pb) > 0 { log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v", string(pb), err) } return } pc.lk.Unlock() rc := <-pc.reqch // Advance past the previous response's body, if the // caller hasn't done so. if lastbody != nil { lastbody.Close() // assumed idempotent lastbody = nil } resp, err := ReadResponse(pc.br, rc.req) if err != nil { pc.close() } else { hasBody := rc.req.Method != "HEAD" && resp.ContentLength != 0 if rc.addedGzip && hasBody && resp.Header.Get("Content-Encoding") == "gzip" { resp.Header.Del("Content-Encoding") resp.Header.Del("Content-Length") resp.ContentLength = -1 gzReader, zerr := gzip.NewReader(resp.Body) if zerr != nil { pc.close() err = zerr } else { resp.Body = &readFirstCloseBoth{&discardOnCloseReadCloser{gzReader}, resp.Body} } } resp.Body = &bodyEOFSignal{body: resp.Body} } if err != nil || resp.Close || rc.req.Close { alive = false } hasBody := resp != nil && resp.ContentLength != 0 var waitForBodyRead chan bool if alive { if hasBody { lastbody = resp.Body waitForBodyRead = make(chan bool) resp.Body.(*bodyEOFSignal).fn = func() { if !pc.t.putIdleConn(pc) { alive = false } waitForBodyRead <- true } } else { // When there's no response body, we immediately // reuse the TCP connection (putIdleConn), but // we need to prevent ClientConn.Read from // closing the Response.Body on the next // loop, otherwise it might close the body // before the client code has had a chance to // read it (even though it'll just be 0, EOF). lastbody = nil if !pc.t.putIdleConn(pc) { alive = false } } } rc.ch <- responseAndError{resp, err} // Wait for the just-returned response body to be fully consumed // before we race and peek on the underlying bufio reader. if waitForBodyRead != nil { <-waitForBodyRead } else if !alive { // If waitForBodyRead is nil, and we're not alive, we // must close the connection before we leave the loop. pc.close() } } } type responseAndError struct { res *Response err error } type requestAndChan struct { req *Request ch chan responseAndError // did the Transport (as opposed to the client code) add an // Accept-Encoding gzip header? only if it we set it do // we transparently decode the gzip. addedGzip bool } func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) { if pc.mutateHeaderFunc != nil { pc.mutateHeaderFunc(req.extraHeaders()) } // Ask for a compressed version if the caller didn't set their // own value for Accept-Encoding. We only attempted to // uncompress the gzip stream if we were the layer that // requested it. requestedGzip := false if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" { // Request gzip only, not deflate. Deflate is ambiguous and // not as universally supported anyway. // See: http://www.gzip.org/zlib/zlib_faq.html#faq38 requestedGzip = true req.extraHeaders().Set("Accept-Encoding", "gzip") } pc.lk.Lock() pc.numExpectedResponses++ pc.lk.Unlock() err = req.Request.write(pc.bw, pc.isProxy, req.extra) if err != nil { pc.close() return } pc.bw.Flush() ch := make(chan responseAndError, 1) pc.reqch <- requestAndChan{req.Request, ch, requestedGzip} re := <-ch pc.lk.Lock() pc.numExpectedResponses-- pc.lk.Unlock() return re.res, re.err } func (pc *persistConn) close() { pc.lk.Lock() defer pc.lk.Unlock() pc.closeLocked() } func (pc *persistConn) closeLocked() { pc.broken = true pc.conn.Close() pc.mutateHeaderFunc = nil } var portMap = map[string]string{ "http": "80", "https": "443", } // canonicalAddr returns url.Host but always with a ":port" suffix func canonicalAddr(url *url.URL) string { addr := url.Host if !hasPort(addr) { return addr + ":" + portMap[url.Scheme] } return addr } func responseIsKeepAlive(res *Response) bool { // TODO: implement. for now just always shutting down the connection. return false } // bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most // once, right before the final Read() or Close() call returns, but after // EOF has been seen. type bodyEOFSignal struct { body io.ReadCloser fn func() isClosed bool } func (es *bodyEOFSignal) Read(p []byte) (n int, err error) { n, err = es.body.Read(p) if es.isClosed && n > 0 { panic("http: unexpected bodyEOFSignal Read after Close; see issue 1725") } if err == io.EOF && es.fn != nil { es.fn() es.fn = nil } return } func (es *bodyEOFSignal) Close() (err error) { if es.isClosed { return nil } es.isClosed = true err = es.body.Close() if err == nil && es.fn != nil { es.fn() es.fn = nil } return } type readFirstCloseBoth struct { io.ReadCloser io.Closer } func (r *readFirstCloseBoth) Close() error { if err := r.ReadCloser.Close(); err != nil { r.Closer.Close() return err } if err := r.Closer.Close(); err != nil { return err } return nil } // discardOnCloseReadCloser consumes all its input on Close. type discardOnCloseReadCloser struct { io.ReadCloser } func (d *discardOnCloseReadCloser) Close() error { io.Copy(ioutil.Discard, d.ReadCloser) // ignore errors; likely invalid or already closed return d.ReadCloser.Close() } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/chunked.go���������������������������������������0000644�0000153�0000161�00000011244�12321735761�023753� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // The wire protocol for HTTP's "chunked" Transfer-Encoding. // This code is duplicated in httputil/chunked.go. // Please make any changes in both files. package http import ( "bufio" "bytes" "errors" "io" "strconv" ) const maxLineLength = 4096 // assumed <= bufio.defaultBufSize var ErrLineTooLong = errors.New("header line too long") // newChunkedReader returns a new chunkedReader that translates the data read from r // out of HTTP "chunked" format before returning it. // The chunkedReader returns io.EOF when the final 0-length chunk is read. // // newChunkedReader is not needed by normal applications. The http package // automatically decodes chunking when reading response bodies. func newChunkedReader(r io.Reader) io.Reader { br, ok := r.(*bufio.Reader) if !ok { br = bufio.NewReader(r) } return &chunkedReader{r: br} } type chunkedReader struct { r *bufio.Reader n uint64 // unread bytes in chunk err error } func (cr *chunkedReader) beginChunk() { // chunk-size CRLF var line string line, cr.err = readLine(cr.r) if cr.err != nil { return } cr.n, cr.err = strconv.ParseUint(line, 16, 64) if cr.err != nil { return } if cr.n == 0 { cr.err = io.EOF } } func (cr *chunkedReader) Read(b []uint8) (n int, err error) { if cr.err != nil { return 0, cr.err } if cr.n == 0 { cr.beginChunk() if cr.err != nil { return 0, cr.err } } if uint64(len(b)) > cr.n { b = b[0:cr.n] } n, cr.err = cr.r.Read(b) cr.n -= uint64(n) if cr.n == 0 && cr.err == nil { // end of chunk (CRLF) b := make([]byte, 2) if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { if b[0] != '\r' || b[1] != '\n' { cr.err = errors.New("malformed chunked encoding") } } } return n, cr.err } // Read a line of bytes (up to \n) from b. // Give up if the line exceeds maxLineLength. // The returned bytes are a pointer into storage in // the bufio, so they are only valid until the next bufio read. func readLineBytes(b *bufio.Reader) (p []byte, err error) { if p, err = b.ReadSlice('\n'); err != nil { // We always know when EOF is coming. // If the caller asked for a line, there should be a line. if err == io.EOF { err = io.ErrUnexpectedEOF } else if err == bufio.ErrBufferFull { err = ErrLineTooLong } return nil, err } if len(p) >= maxLineLength { return nil, ErrLineTooLong } // Chop off trailing white space. p = bytes.TrimRight(p, " \r\t\n") return p, nil } // readLineBytes, but convert the bytes into a string. func readLine(b *bufio.Reader) (s string, err error) { p, e := readLineBytes(b) if e != nil { return "", e } return string(p), nil } // newChunkedWriter returns a new chunkedWriter that translates writes into HTTP // "chunked" format before writing them to w. Closing the returned chunkedWriter // sends the final 0-length chunk that marks the end of the stream. // // newChunkedWriter is not needed by normal applications. The http // package adds chunking automatically if handlers don't set a // Content-Length header. Using newChunkedWriter inside a handler // would result in double chunking or chunking with a Content-Length // length, both of which are wrong. func newChunkedWriter(w io.Writer) io.WriteCloser { return &chunkedWriter{w} } // Writing to chunkedWriter translates to writing in HTTP chunked Transfer // Encoding wire format to the underlying Wire chunkedWriter. type chunkedWriter struct { Wire io.Writer } // Write the contents of data as one chunk to Wire. // NOTE: Note that the corresponding chunk-writing procedure in Conn.Write has // a bug since it does not check for success of io.WriteString func (cw *chunkedWriter) Write(data []byte) (n int, err error) { // Don't send 0-length data. It looks like EOF for chunked encoding. if len(data) == 0 { return 0, nil } head := strconv.FormatInt(int64(len(data)), 16) + "\r\n" if _, err = io.WriteString(cw.Wire, head); err != nil { return 0, err } if n, err = cw.Wire.Write(data); err != nil { return } if n != len(data) { err = io.ErrShortWrite return } _, err = io.WriteString(cw.Wire, "\r\n") return } func (cw *chunkedWriter) Close() error { _, err := io.WriteString(cw.Wire, "0\r\n") return err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/cookie.go����������������������������������������0000644�0000153�0000161�00000017074�12321735761�023612� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package http import ( "bytes" "fmt" "strconv" "strings" "time" ) // This implementation is done according to RFC 6265: // // http://tools.ietf.org/html/rfc6265 // A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an // HTTP response or the Cookie header of an HTTP request. type Cookie struct { Name string Value string Path string Domain string Expires time.Time RawExpires string // MaxAge=0 means no 'Max-Age' attribute specified. // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0' // MaxAge>0 means Max-Age attribute present and given in seconds MaxAge int Secure bool HttpOnly bool Raw string Unparsed []string // Raw text of unparsed attribute-value pairs } // readSetCookies parses all "Set-Cookie" values from // the header h and returns the successfully parsed Cookies. func readSetCookies(h Header) []*Cookie { cookies := []*Cookie{} for _, line := range h["Set-Cookie"] { parts := strings.Split(strings.TrimSpace(line), ";") if len(parts) == 1 && parts[0] == "" { continue } parts[0] = strings.TrimSpace(parts[0]) j := strings.Index(parts[0], "=") if j < 0 { continue } name, value := parts[0][:j], parts[0][j+1:] if !isCookieNameValid(name) { continue } value, success := parseCookieValue(value) if !success { continue } c := &Cookie{ Name: name, Value: value, Raw: line, } for i := 1; i < len(parts); i++ { parts[i] = strings.TrimSpace(parts[i]) if len(parts[i]) == 0 { continue } attr, val := parts[i], "" if j := strings.Index(attr, "="); j >= 0 { attr, val = attr[:j], attr[j+1:] } lowerAttr := strings.ToLower(attr) parseCookieValueFn := parseCookieValue if lowerAttr == "expires" { parseCookieValueFn = parseCookieExpiresValue } val, success = parseCookieValueFn(val) if !success { c.Unparsed = append(c.Unparsed, parts[i]) continue } switch lowerAttr { case "secure": c.Secure = true continue case "httponly": c.HttpOnly = true continue case "domain": c.Domain = val // TODO: Add domain parsing continue case "max-age": secs, err := strconv.Atoi(val) if err != nil || secs != 0 && val[0] == '0' { break } if secs <= 0 { c.MaxAge = -1 } else { c.MaxAge = secs } continue case "expires": c.RawExpires = val exptime, err := time.Parse(time.RFC1123, val) if err != nil { exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", val) if err != nil { c.Expires = time.Time{} break } } c.Expires = exptime.UTC() continue case "path": c.Path = val // TODO: Add path parsing continue } c.Unparsed = append(c.Unparsed, parts[i]) } cookies = append(cookies, c) } return cookies } // SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers. func SetCookie(w ResponseWriter, cookie *Cookie) { w.Header().Add("Set-Cookie", cookie.String()) } // String returns the serialization of the cookie for use in a Cookie // header (if only Name and Value are set) or a Set-Cookie response // header (if other fields are set). func (c *Cookie) String() string { var b bytes.Buffer fmt.Fprintf(&b, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value)) if len(c.Path) > 0 { fmt.Fprintf(&b, "; Path=%s", sanitizeValue(c.Path)) } if len(c.Domain) > 0 { fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(c.Domain)) } if c.Expires.Unix() > 0 { fmt.Fprintf(&b, "; Expires=%s", c.Expires.UTC().Format(time.RFC1123)) } if c.MaxAge > 0 { fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge) } else if c.MaxAge < 0 { fmt.Fprintf(&b, "; Max-Age=0") } if c.HttpOnly { fmt.Fprintf(&b, "; HttpOnly") } if c.Secure { fmt.Fprintf(&b, "; Secure") } return b.String() } // readCookies parses all "Cookie" values from the header h and // returns the successfully parsed Cookies. // // if filter isn't empty, only cookies of that name are returned func readCookies(h Header, filter string) []*Cookie { cookies := []*Cookie{} lines, ok := h["Cookie"] if !ok { return cookies } for _, line := range lines { parts := strings.Split(strings.TrimSpace(line), ";") if len(parts) == 1 && parts[0] == "" { continue } // Per-line attributes parsedPairs := 0 for i := 0; i < len(parts); i++ { parts[i] = strings.TrimSpace(parts[i]) if len(parts[i]) == 0 { continue } name, val := parts[i], "" if j := strings.Index(name, "="); j >= 0 { name, val = name[:j], name[j+1:] } if !isCookieNameValid(name) { continue } if filter != "" && filter != name { continue } val, success := parseCookieValue(val) if !success { continue } cookies = append(cookies, &Cookie{Name: name, Value: val}) parsedPairs++ } } return cookies } var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-") func sanitizeName(n string) string { return cookieNameSanitizer.Replace(n) } var cookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ") func sanitizeValue(v string) string { return cookieValueSanitizer.Replace(v) } func unquoteCookieValue(v string) string { if len(v) > 1 && v[0] == '"' && v[len(v)-1] == '"' { return v[1 : len(v)-1] } return v } func isCookieByte(c byte) bool { switch { case c == 0x21, 0x23 <= c && c <= 0x2b, 0x2d <= c && c <= 0x3a, 0x3c <= c && c <= 0x5b, 0x5d <= c && c <= 0x7e: return true } return false } func isCookieExpiresByte(c byte) (ok bool) { return isCookieByte(c) || c == ',' || c == ' ' } func parseCookieValue(raw string) (string, bool) { return parseCookieValueUsing(raw, isCookieByte) } func parseCookieExpiresValue(raw string) (string, bool) { return parseCookieValueUsing(raw, isCookieExpiresByte) } func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool) { raw = unquoteCookieValue(raw) for i := 0; i < len(raw); i++ { if !validByte(raw[i]) { return "", false } } return raw, true } func isCookieNameValid(raw string) bool { for _, c := range raw { if !isToken(byte(c)) { return false } } return true } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/client.go����������������������������������������0000644�0000153�0000161�00000025453�12321735761�023617� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // HTTP client. See RFC 2616. // // This is the high-level Client interface. // The low-level implementation is in transport.go. package http import ( "encoding/base64" "errors" "fmt" "io" "net/url" "strings" ) // A Client is an HTTP client. Its zero value (DefaultClient) is a usable client // that uses DefaultTransport. // // The Client's Transport typically has internal state (cached // TCP connections), so Clients should be reused instead of created as // needed. Clients are safe for concurrent use by multiple goroutines. type Client struct { // Transport specifies the mechanism by which individual // HTTP requests are made. // If nil, DefaultTransport is used. Transport RoundTripper // CheckRedirect specifies the policy for handling redirects. // If CheckRedirect is not nil, the client calls it before // following an HTTP redirect. The arguments req and via // are the upcoming request and the requests made already, // oldest first. If CheckRedirect returns an error, the client // returns that error instead of issue the Request req. // // If CheckRedirect is nil, the Client uses its default policy, // which is to stop after 10 consecutive requests. CheckRedirect func(req *Request, via []*Request) error // Jar specifies the cookie jar. // If Jar is nil, cookies are not sent in requests and ignored // in responses. Jar CookieJar } // DefaultClient is the default Client and is used by Get, Head, and Post. var DefaultClient = &Client{} // RoundTripper is an interface representing the ability to execute a // single HTTP transaction, obtaining the Response for a given Request. // // A RoundTripper must be safe for concurrent use by multiple // goroutines. type RoundTripper interface { // RoundTrip executes a single HTTP transaction, returning // the Response for the request req. RoundTrip should not // attempt to interpret the response. In particular, // RoundTrip must return err == nil if it obtained a response, // regardless of the response's HTTP status code. A non-nil // err should be reserved for failure to obtain a response. // Similarly, RoundTrip should not attempt to handle // higher-level protocol details such as redirects, // authentication, or cookies. // // RoundTrip should not modify the request, except for // consuming the Body. The request's URL and Header fields // are guaranteed to be initialized. RoundTrip(*Request) (*Response, error) } // Given a string of the form "host", "host:port", or "[ipv6::address]:port", // return true if the string includes a port. func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } // Used in Send to implement io.ReadCloser by bundling together the // bufio.Reader through which we read the response, and the underlying // network connection. type readClose struct { io.Reader io.Closer } // Do sends an HTTP request and returns an HTTP response, following // policy (e.g. redirects, cookies, auth) as configured on the client. // // A non-nil response always contains a non-nil resp.Body. // // Callers should close resp.Body when done reading from it. If // resp.Body is not closed, the Client's underlying RoundTripper // (typically Transport) may not be able to re-use a persistent TCP // connection to the server for a subsequent "keep-alive" request. // // Generally Get, Post, or PostForm will be used instead of Do. func (c *Client) Do(req *Request) (resp *Response, err error) { if req.Method == "GET" || req.Method == "HEAD" { return c.doFollowingRedirects(req) } return send(req, c.Transport) } // send issues an HTTP request. Caller should close resp.Body when done reading from it. func send(req *Request, t RoundTripper) (resp *Response, err error) { if t == nil { t = DefaultTransport if t == nil { err = errors.New("http: no Client.Transport or DefaultTransport") return } } if req.URL == nil { return nil, errors.New("http: nil Request.URL") } if req.RequestURI != "" { return nil, errors.New("http: Request.RequestURI can't be set in client requests.") } // Most the callers of send (Get, Post, et al) don't need // Headers, leaving it uninitialized. We guarantee to the // Transport that this has been initialized, though. if req.Header == nil { req.Header = make(Header) } if u := req.URL.User; u != nil { req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(u.String()))) } return t.RoundTrip(req) } // True if the specified HTTP status code is one for which the Get utility should // automatically redirect. func shouldRedirect(statusCode int) bool { switch statusCode { case StatusMovedPermanently, StatusFound, StatusSeeOther, StatusTemporaryRedirect: return true } return false } // Get issues a GET to the specified URL. If the response is one of the following // redirect codes, Get follows the redirect, up to a maximum of 10 redirects: // // 301 (Moved Permanently) // 302 (Found) // 303 (See Other) // 307 (Temporary Redirect) // // Caller should close r.Body when done reading from it. // // Get is a wrapper around DefaultClient.Get. func Get(url string) (r *Response, err error) { return DefaultClient.Get(url) } // Get issues a GET to the specified URL. If the response is one of the // following redirect codes, Get follows the redirect after calling the // Client's CheckRedirect function. // // 301 (Moved Permanently) // 302 (Found) // 303 (See Other) // 307 (Temporary Redirect) // // Caller should close r.Body when done reading from it. func (c *Client) Get(url string) (r *Response, err error) { req, err := NewRequest("GET", url, nil) if err != nil { return nil, err } return c.doFollowingRedirects(req) } func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err error) { // TODO: if/when we add cookie support, the redirected request shouldn't // necessarily supply the same cookies as the original. var base *url.URL redirectChecker := c.CheckRedirect if redirectChecker == nil { redirectChecker = defaultCheckRedirect } var via []*Request if ireq.URL == nil { return nil, errors.New("http: nil Request.URL") } jar := c.Jar if jar == nil { jar = blackHoleJar{} } req := ireq urlStr := "" // next relative or absolute URL to fetch (after first request) for redirect := 0; ; redirect++ { if redirect != 0 { req = new(Request) req.Method = ireq.Method req.Header = make(Header) req.URL, err = base.Parse(urlStr) if err != nil { break } if len(via) > 0 { // Add the Referer header. lastReq := via[len(via)-1] if lastReq.URL.Scheme != "https" { req.Header.Set("Referer", lastReq.URL.String()) } err = redirectChecker(req, via) if err != nil { break } } } for _, cookie := range jar.Cookies(req.URL) { req.AddCookie(cookie) } urlStr = req.URL.String() if r, err = send(req, c.Transport); err != nil { break } if c := r.Cookies(); len(c) > 0 { jar.SetCookies(req.URL, c) } if shouldRedirect(r.StatusCode) { r.Body.Close() if urlStr = r.Header.Get("Location"); urlStr == "" { err = errors.New(fmt.Sprintf("%d response missing Location header", r.StatusCode)) break } base = req.URL via = append(via, req) continue } return } method := ireq.Method err = &url.Error{ Op: method[0:1] + strings.ToLower(method[1:]), URL: urlStr, Err: err, } return } func defaultCheckRedirect(req *Request, via []*Request) error { if len(via) >= 10 { return errors.New("stopped after 10 redirects") } return nil } // Post issues a POST to the specified URL. // // Caller should close r.Body when done reading from it. // // Post is a wrapper around DefaultClient.Post func Post(url string, bodyType string, body io.Reader) (r *Response, err error) { return DefaultClient.Post(url, bodyType, body) } // Post issues a POST to the specified URL. // // Caller should close r.Body when done reading from it. func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error) { req, err := NewRequest("POST", url, body) if err != nil { return nil, err } req.Header.Set("Content-Type", bodyType) if c.Jar != nil { for _, cookie := range c.Jar.Cookies(req.URL) { req.AddCookie(cookie) } } r, err = send(req, c.Transport) if err == nil && c.Jar != nil { c.Jar.SetCookies(req.URL, r.Cookies()) } return r, err } // PostForm issues a POST to the specified URL, // with data's keys and values urlencoded as the request body. // // Caller should close r.Body when done reading from it. // // PostForm is a wrapper around DefaultClient.PostForm func PostForm(url string, data url.Values) (r *Response, err error) { return DefaultClient.PostForm(url, data) } // PostForm issues a POST to the specified URL, // with data's keys and values urlencoded as the request body. // // Caller should close r.Body when done reading from it. func (c *Client) PostForm(url string, data url.Values) (r *Response, err error) { return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) } // Head issues a HEAD to the specified URL. If the response is one of the // following redirect codes, Head follows the redirect after calling the // Client's CheckRedirect function. // // 301 (Moved Permanently) // 302 (Found) // 303 (See Other) // 307 (Temporary Redirect) // // Head is a wrapper around DefaultClient.Head func Head(url string) (r *Response, err error) { return DefaultClient.Head(url) } // Head issues a HEAD to the specified URL. If the response is one of the // following redirect codes, Head follows the redirect after calling the // Client's CheckRedirect function. // // 301 (Moved Permanently) // 302 (Found) // 303 (See Other) // 307 (Temporary Redirect) func (c *Client) Head(url string) (r *Response, err error) { req, err := NewRequest("HEAD", url, nil) if err != nil { return nil, err } return c.doFollowingRedirects(req) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/jar.go�������������������������������������������0000644�0000153�0000161�00000002000�12321735761�023074� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 http import ( "net/url" ) // A CookieJar manages storage and use of cookies in HTTP requests. // // Implementations of CookieJar must be safe for concurrent use by multiple // goroutines. type CookieJar interface { // SetCookies handles the receipt of the cookies in a reply for the // given URL. It may or may not choose to save the cookies, depending // on the jar's policy and implementation. SetCookies(u *url.URL, cookies []*Cookie) // Cookies returns the cookies to send in a request for the given URL. // It is up to the implementation to honor the standard cookie use // restrictions such as in RFC 6265. Cookies(u *url.URL) []*Cookie } type blackHoleJar struct{} func (blackHoleJar) SetCookies(u *url.URL, cookies []*Cookie) {} func (blackHoleJar) Cookies(u *url.URL) []*Cookie { return nil } juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/response.go��������������������������������������0000644�0000153�0000161�00000015420�12321735761�024170� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // HTTP Response reading and parsing. package http import ( "bufio" "errors" "io" "net/textproto" "net/url" "strconv" "strings" ) var respExcludeHeader = map[string]bool{ "Content-Length": true, "Transfer-Encoding": true, "Trailer": true, } // Response represents the response from an HTTP request. // type Response struct { Status string // e.g. "200 OK" StatusCode int // e.g. 200 Proto string // e.g. "HTTP/1.0" ProtoMajor int // e.g. 1 ProtoMinor int // e.g. 0 // Header maps header keys to values. If the response had multiple // headers with the same key, they will be concatenated, with comma // delimiters. (Section 4.2 of RFC 2616 requires that multiple headers // be semantically equivalent to a comma-delimited sequence.) Values // duplicated by other fields in this struct (e.g., ContentLength) are // omitted from Header. // // Keys in the map are canonicalized (see CanonicalHeaderKey). Header Header // Body represents the response body. // // The http Client and Transport guarantee that Body is always // non-nil, even on responses without a body or responses with // a zero-lengthed body. Body io.ReadCloser // ContentLength records the length of the associated content. The // value -1 indicates that the length is unknown. Unless RequestMethod // is "HEAD", values >= 0 indicate that the given number of bytes may // be read from Body. ContentLength int64 // Contains transfer encodings from outer-most to inner-most. Value is // nil, means that "identity" encoding is used. TransferEncoding []string // Close records whether the header directed that the connection be // closed after reading Body. The value is advice for clients: neither // ReadResponse nor Response.Write ever closes a connection. Close bool // Trailer maps trailer keys to values, in the same // format as the header. Trailer Header // The Request that was sent to obtain this Response. // Request's Body is nil (having already been consumed). // This is only populated for Client requests. Request *Request } // Cookies parses and returns the cookies set in the Set-Cookie headers. func (r *Response) Cookies() []*Cookie { return readSetCookies(r.Header) } var ErrNoLocation = errors.New("http: no Location header in response") // Location returns the URL of the response's "Location" header, // if present. Relative redirects are resolved relative to // the Response's Request. ErrNoLocation is returned if no // Location header is present. func (r *Response) Location() (*url.URL, error) { lv := r.Header.Get("Location") if lv == "" { return nil, ErrNoLocation } if r.Request != nil && r.Request.URL != nil { return r.Request.URL.Parse(lv) } return url.Parse(lv) } // ReadResponse reads and returns an HTTP response from r. The // req parameter specifies the Request that corresponds to // this Response. Clients must call resp.Body.Close when finished // reading resp.Body. After that call, clients can inspect // resp.Trailer to find key/value pairs included in the response // trailer. func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err error) { tp := textproto.NewReader(r) resp = new(Response) resp.Request = req resp.Request.Method = strings.ToUpper(resp.Request.Method) // Parse the first line of the response. line, err := tp.ReadLine() if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return nil, err } f := strings.SplitN(line, " ", 3) if len(f) < 2 { return nil, &badStringError{"malformed HTTP response", line} } reasonPhrase := "" if len(f) > 2 { reasonPhrase = f[2] } resp.Status = f[1] + " " + reasonPhrase resp.StatusCode, err = strconv.Atoi(f[1]) if err != nil { return nil, &badStringError{"malformed HTTP status code", f[1]} } resp.Proto = f[0] var ok bool if resp.ProtoMajor, resp.ProtoMinor, ok = ParseHTTPVersion(resp.Proto); !ok { return nil, &badStringError{"malformed HTTP version", resp.Proto} } // Parse the response headers. mimeHeader, err := tp.ReadMIMEHeader() if err != nil { return nil, err } resp.Header = Header(mimeHeader) fixPragmaCacheControl(resp.Header) err = readTransfer(resp, r) if err != nil { return nil, err } return resp, nil } // RFC2616: Should treat // Pragma: no-cache // like // Cache-Control: no-cache func fixPragmaCacheControl(header Header) { if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" { if _, presentcc := header["Cache-Control"]; !presentcc { header["Cache-Control"] = []string{"no-cache"} } } } // ProtoAtLeast returns whether the HTTP protocol used // in the response is at least major.minor. func (r *Response) ProtoAtLeast(major, minor int) bool { return r.ProtoMajor > major || r.ProtoMajor == major && r.ProtoMinor >= minor } // Writes the response (header, body and trailer) in wire format. This method // consults the following fields of the response: // // StatusCode // ProtoMajor // ProtoMinor // RequestMethod // TransferEncoding // Trailer // Body // ContentLength // Header, values for non-canonical keys will have unpredictable behavior // func (r *Response) Write(w io.Writer) error { // RequestMethod should be upper-case if r.Request != nil { r.Request.Method = strings.ToUpper(r.Request.Method) } // Status line text := r.Status if text == "" { var ok bool text, ok = statusText[r.StatusCode] if !ok { text = "status code " + strconv.Itoa(r.StatusCode) } } protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor) statusCode := strconv.Itoa(r.StatusCode) + " " if strings.HasPrefix(text, statusCode) { text = text[len(statusCode):] } io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n") // Process Body,ContentLength,Close,Trailer tw, err := newTransferWriter(r) if err != nil { return err } err = tw.WriteHeader(w) if err != nil { return err } // Rest of header err = r.Header.WriteSubset(w, respExcludeHeader) if err != nil { return err } // End-of-header io.WriteString(w, "\r\n") // Write body and trailer err = tw.WriteBody(w) if err != nil { return err } // Success return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/triv.go������������������������������������������0000644�0000153�0000161�00000007040�12321735761�023315� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // +build ignore package main import ( "bytes" "expvar" "flag" "fmt" "io" "log" "net/http" "os" "os/exec" "strconv" "sync" ) // hello world, the web server var helloRequests = expvar.NewInt("hello-requests") func HelloServer(w http.ResponseWriter, req *http.Request) { helloRequests.Add(1) io.WriteString(w, "hello, world!\n") } // Simple counter server. POSTing to it will set the value. type Counter struct { mu sync.Mutex // protects n n int } // This makes Counter satisfy the expvar.Var interface, so we can export // it directly. func (ctr *Counter) String() string { ctr.mu.Lock() defer ctr.mu.Unlock() return fmt.Sprintf("%d", ctr.n) } func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { ctr.mu.Lock() defer ctr.mu.Unlock() switch req.Method { case "GET": ctr.n++ case "POST": buf := new(bytes.Buffer) io.Copy(buf, req.Body) body := buf.String() if n, err := strconv.Atoi(body); err != nil { fmt.Fprintf(w, "bad POST: %v\nbody: [%v]\n", err, body) } else { ctr.n = n fmt.Fprint(w, "counter reset\n") } } fmt.Fprintf(w, "counter = %d\n", ctr.n) } // simple flag server var booleanflag = flag.Bool("boolean", true, "another flag for testing") func FlagServer(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") fmt.Fprint(w, "Flags:\n") flag.VisitAll(func(f *flag.Flag) { if f.Value.String() != f.DefValue { fmt.Fprintf(w, "%s = %s [default = %s]\n", f.Name, f.Value.String(), f.DefValue) } else { fmt.Fprintf(w, "%s = %s\n", f.Name, f.Value.String()) } }) } // simple argument server func ArgServer(w http.ResponseWriter, req *http.Request) { for _, s := range os.Args { fmt.Fprint(w, s, " ") } } // a channel (just for the fun of it) type Chan chan int func ChanCreate() Chan { c := make(Chan) go func(c Chan) { for x := 0; ; x++ { c <- x } }(c) return c } func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) { io.WriteString(w, fmt.Sprintf("channel send #%d\n", <-ch)) } // exec a program, redirecting output func DateServer(rw http.ResponseWriter, req *http.Request) { rw.Header().Set("Content-Type", "text/plain; charset=utf-8") date, err := exec.Command("/bin/date").Output() if err != nil { http.Error(rw, err.Error(), 500) return } rw.Write(date) } func Logger(w http.ResponseWriter, req *http.Request) { log.Print(req.URL) http.Error(w, "oops", 404) } var webroot = flag.String("root", os.Getenv("HOME"), "web root directory") func main() { flag.Parse() // The counter is published as a variable directly. ctr := new(Counter) expvar.Publish("counter", ctr) http.Handle("/counter", ctr) http.Handle("/", http.HandlerFunc(Logger)) http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot)))) http.Handle("/chan", ChanCreate()) http.HandleFunc("/flags", FlagServer) http.HandleFunc("/args", ArgServer) http.HandleFunc("/go/hello", HelloServer) http.HandleFunc("/date", DateServer) err := http.ListenAndServe(":12345", nil) if err != nil { log.Panicln("ListenAndServe:", err) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/status.go����������������������������������������0000644�0000153�0000161�00000010364�12321735761�023657� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. package http // HTTP status codes, defined in RFC 2616. const ( StatusContinue = 100 StatusSwitchingProtocols = 101 StatusOK = 200 StatusCreated = 201 StatusAccepted = 202 StatusNonAuthoritativeInfo = 203 StatusNoContent = 204 StatusResetContent = 205 StatusPartialContent = 206 StatusMultipleChoices = 300 StatusMovedPermanently = 301 StatusFound = 302 StatusSeeOther = 303 StatusNotModified = 304 StatusUseProxy = 305 StatusTemporaryRedirect = 307 StatusBadRequest = 400 StatusUnauthorized = 401 StatusPaymentRequired = 402 StatusForbidden = 403 StatusNotFound = 404 StatusMethodNotAllowed = 405 StatusNotAcceptable = 406 StatusProxyAuthRequired = 407 StatusRequestTimeout = 408 StatusConflict = 409 StatusGone = 410 StatusLengthRequired = 411 StatusPreconditionFailed = 412 StatusRequestEntityTooLarge = 413 StatusRequestURITooLong = 414 StatusUnsupportedMediaType = 415 StatusRequestedRangeNotSatisfiable = 416 StatusExpectationFailed = 417 StatusTeapot = 418 StatusInternalServerError = 500 StatusNotImplemented = 501 StatusBadGateway = 502 StatusServiceUnavailable = 503 StatusGatewayTimeout = 504 StatusHTTPVersionNotSupported = 505 ) var statusText = map[int]string{ StatusContinue: "Continue", StatusSwitchingProtocols: "Switching Protocols", StatusOK: "OK", StatusCreated: "Created", StatusAccepted: "Accepted", StatusNonAuthoritativeInfo: "Non-Authoritative Information", StatusNoContent: "No Content", StatusResetContent: "Reset Content", StatusPartialContent: "Partial Content", StatusMultipleChoices: "Multiple Choices", StatusMovedPermanently: "Moved Permanently", StatusFound: "Found", StatusSeeOther: "See Other", StatusNotModified: "Not Modified", StatusUseProxy: "Use Proxy", StatusTemporaryRedirect: "Temporary Redirect", StatusBadRequest: "Bad Request", StatusUnauthorized: "Unauthorized", StatusPaymentRequired: "Payment Required", StatusForbidden: "Forbidden", StatusNotFound: "Not Found", StatusMethodNotAllowed: "Method Not Allowed", StatusNotAcceptable: "Not Acceptable", StatusProxyAuthRequired: "Proxy Authentication Required", StatusRequestTimeout: "Request Timeout", StatusConflict: "Conflict", StatusGone: "Gone", StatusLengthRequired: "Length Required", StatusPreconditionFailed: "Precondition Failed", StatusRequestEntityTooLarge: "Request Entity Too Large", StatusRequestURITooLong: "Request URI Too Long", StatusUnsupportedMediaType: "Unsupported Media Type", StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable", StatusExpectationFailed: "Expectation Failed", StatusTeapot: "I'm a teapot", StatusInternalServerError: "Internal Server Error", StatusNotImplemented: "Not Implemented", StatusBadGateway: "Bad Gateway", StatusServiceUnavailable: "Service Unavailable", StatusGatewayTimeout: "Gateway Timeout", StatusHTTPVersionNotSupported: "HTTP Version Not Supported", } // StatusText returns a text for the HTTP status code. It returns the empty // string if the code is unknown. func StatusText(code int) string { return statusText[code] } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/request.go���������������������������������������0000644�0000153�0000161�00000055663�12321735761�024037� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // HTTP Request reading and parsing. package http import ( "bufio" "bytes" "crypto/tls" "encoding/base64" "errors" "fmt" "io" "io/ioutil" "mime" "mime/multipart" "net/textproto" "net/url" "strings" ) const ( maxValueLength = 4096 maxHeaderLines = 1024 chunkSize = 4 << 10 // 4 KB chunks defaultMaxMemory = 32 << 20 // 32 MB ) // ErrMissingFile is returned by FormFile when the provided file field name // is either not present in the request or not a file field. var ErrMissingFile = errors.New("http: no such file") // HTTP request parsing errors. type ProtocolError struct { ErrorString string } func (err *ProtocolError) Error() string { return err.ErrorString } var ( ErrHeaderTooLong = &ProtocolError{"header too long"} ErrShortBody = &ProtocolError{"entity body too short"} ErrNotSupported = &ProtocolError{"feature not supported"} ErrUnexpectedTrailer = &ProtocolError{"trailer header without chunked transfer encoding"} ErrMissingContentLength = &ProtocolError{"missing ContentLength in HEAD response"} ErrNotMultipart = &ProtocolError{"request Content-Type isn't multipart/form-data"} ErrMissingBoundary = &ProtocolError{"no multipart boundary param Content-Type"} ) type badStringError struct { what string str string } func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) } // Headers that Request.Write handles itself and should be skipped. var reqWriteExcludeHeader = map[string]bool{ "Host": true, // not in Header map anyway "User-Agent": true, "Content-Length": true, "Transfer-Encoding": true, "Trailer": true, } // A Request represents an HTTP request received by a server // or to be sent by a client. type Request struct { Method string // GET, POST, PUT, etc. URL *url.URL // The protocol version for incoming requests. // Outgoing requests always use HTTP/1.1. Proto string // "HTTP/1.0" ProtoMajor int // 1 ProtoMinor int // 0 // A header maps request lines to their values. // If the header says // // accept-encoding: gzip, deflate // Accept-Language: en-us // Connection: keep-alive // // then // // Header = map[string][]string{ // "Accept-Encoding": {"gzip, deflate"}, // "Accept-Language": {"en-us"}, // "Connection": {"keep-alive"}, // } // // HTTP defines that header names are case-insensitive. // The request parser implements this by canonicalizing the // name, making the first character and any characters // following a hyphen uppercase and the rest lowercase. Header Header // The message body. Body io.ReadCloser // ContentLength records the length of the associated content. // The value -1 indicates that the length is unknown. // Values >= 0 indicate that the given number of bytes may // be read from Body. // For outgoing requests, a value of 0 means unknown if Body is not nil. ContentLength int64 // TransferEncoding lists the transfer encodings from outermost to // innermost. An empty list denotes the "identity" encoding. // TransferEncoding can usually be ignored; chunked encoding is // automatically added and removed as necessary when sending and // receiving requests. TransferEncoding []string // Close indicates whether to close the connection after // replying to this request. Close bool // The host on which the URL is sought. // Per RFC 2616, this is either the value of the Host: header // or the host name given in the URL itself. Host string // Form contains the parsed form data, including both the URL // field's query parameters and the POST or PUT form data. // This field is only available after ParseForm is called. // The HTTP client ignores Form and uses Body instead. Form url.Values // MultipartForm is the parsed multipart form, including file uploads. // This field is only available after ParseMultipartForm is called. // The HTTP client ignores MultipartForm and uses Body instead. MultipartForm *multipart.Form // Trailer maps trailer keys to values. Like for Header, if the // response has multiple trailer lines with the same key, they will be // concatenated, delimited by commas. // For server requests, Trailer is only populated after Body has been // closed or fully consumed. // Trailer support is only partially complete. Trailer Header // RemoteAddr allows HTTP servers and other software to record // the network address that sent the request, usually for // logging. This field is not filled in by ReadRequest and // has no defined format. The HTTP server in this package // sets RemoteAddr to an "IP:port" address before invoking a // handler. // This field is ignored by the HTTP client. RemoteAddr string // RequestURI is the unmodified Request-URI of the // Request-Line (RFC 2616, Section 5.1) as sent by the client // to a server. Usually the URL field should be used instead. // It is an error to set this field in an HTTP client request. RequestURI string // TLS allows HTTP servers and other software to record // information about the TLS connection on which the request // was received. This field is not filled in by ReadRequest. // The HTTP server in this package sets the field for // TLS-enabled connections before invoking a handler; // otherwise it leaves the field nil. // This field is ignored by the HTTP client. TLS *tls.ConnectionState } // ProtoAtLeast returns whether the HTTP protocol used // in the request is at least major.minor. func (r *Request) ProtoAtLeast(major, minor int) bool { return r.ProtoMajor > major || r.ProtoMajor == major && r.ProtoMinor >= minor } // UserAgent returns the client's User-Agent, if sent in the request. func (r *Request) UserAgent() string { return r.Header.Get("User-Agent") } // Cookies parses and returns the HTTP cookies sent with the request. func (r *Request) Cookies() []*Cookie { return readCookies(r.Header, "") } var ErrNoCookie = errors.New("http: named cookie not present") // Cookie returns the named cookie provided in the request or // ErrNoCookie if not found. func (r *Request) Cookie(name string) (*Cookie, error) { for _, c := range readCookies(r.Header, name) { return c, nil } return nil, ErrNoCookie } // AddCookie adds a cookie to the request. Per RFC 6265 section 5.4, // AddCookie does not attach more than one Cookie header field. That // means all cookies, if any, are written into the same line, // separated by semicolon. func (r *Request) AddCookie(c *Cookie) { s := fmt.Sprintf("%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value)) if c := r.Header.Get("Cookie"); c != "" { r.Header.Set("Cookie", c+"; "+s) } else { r.Header.Set("Cookie", s) } } // Referer returns the referring URL, if sent in the request. // // Referer is misspelled as in the request itself, a mistake from the // earliest days of HTTP. This value can also be fetched from the // Header map as Header["Referer"]; the benefit of making it available // as a method is that the compiler can diagnose programs that use the // alternate (correct English) spelling req.Referrer() but cannot // diagnose programs that use Header["Referrer"]. func (r *Request) Referer() string { return r.Header.Get("Referer") } // multipartByReader is a sentinel value. // Its presence in Request.MultipartForm indicates that parsing of the request // body has been handed off to a MultipartReader instead of ParseMultipartFrom. var multipartByReader = &multipart.Form{ Value: make(map[string][]string), File: make(map[string][]*multipart.FileHeader), } // MultipartReader returns a MIME multipart reader if this is a // multipart/form-data POST request, else returns nil and an error. // Use this function instead of ParseMultipartForm to // process the request body as a stream. func (r *Request) MultipartReader() (*multipart.Reader, error) { if r.MultipartForm == multipartByReader { return nil, errors.New("http: MultipartReader called twice") } if r.MultipartForm != nil { return nil, errors.New("http: multipart handled by ParseMultipartForm") } r.MultipartForm = multipartByReader return r.multipartReader() } func (r *Request) multipartReader() (*multipart.Reader, error) { v := r.Header.Get("Content-Type") if v == "" { return nil, ErrNotMultipart } d, params, err := mime.ParseMediaType(v) if err != nil || d != "multipart/form-data" { return nil, ErrNotMultipart } boundary, ok := params["boundary"] if !ok { return nil, ErrMissingBoundary } return multipart.NewReader(r.Body, boundary), nil } // Return value if nonempty, def otherwise. func valueOrDefault(value, def string) string { if value != "" { return value } return def } const defaultUserAgent = "Go http package" // Write writes an HTTP/1.1 request -- header and body -- in wire format. // This method consults the following fields of the request: // Host // URL // Method (defaults to "GET") // Header // ContentLength // TransferEncoding // Body // // If Body is present, Content-Length is <= 0 and TransferEncoding // hasn't been set to "identity", Write adds "Transfer-Encoding: // chunked" to the header. Body is closed after it is sent. func (r *Request) Write(w io.Writer) error { return r.write(w, false, nil) } // WriteProxy is like Write but writes the request in the form // expected by an HTTP proxy. In particular, WriteProxy writes the // initial Request-URI line of the request with an absolute URI, per // section 5.1.2 of RFC 2616, including the scheme and host. // In either case, WriteProxy also writes a Host header, using // either r.Host or r.URL.Host. func (r *Request) WriteProxy(w io.Writer) error { return r.write(w, true, nil) } // extraHeaders may be nil func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) error { host := req.Host if host == "" { if req.URL == nil { return errors.New("http: Request.Write on Request with no Host or URL set") } host = req.URL.Host } ruri := req.URL.RequestURI() if usingProxy && req.URL.Scheme != "" && req.URL.Opaque == "" { ruri = req.URL.Scheme + "://" + host + ruri } else if req.Method == "CONNECT" && req.URL.Path == "" { // CONNECT requests normally give just the host and port, not a full URL. ruri = host } // TODO(bradfitz): escape at least newlines in ruri? bw := bufio.NewWriter(w) fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), ruri) // Header lines fmt.Fprintf(bw, "Host: %s\r\n", host) // Use the defaultUserAgent unless the Header contains one, which // may be blank to not send the header. userAgent := defaultUserAgent if req.Header != nil { if ua := req.Header["User-Agent"]; len(ua) > 0 { userAgent = ua[0] } } if userAgent != "" { fmt.Fprintf(bw, "User-Agent: %s\r\n", userAgent) } // Process Body,ContentLength,Close,Trailer tw, err := newTransferWriter(req) if err != nil { return err } err = tw.WriteHeader(bw) if err != nil { return err } // TODO: split long values? (If so, should share code with Conn.Write) err = req.Header.WriteSubset(bw, reqWriteExcludeHeader) if err != nil { return err } if extraHeaders != nil { err = extraHeaders.Write(bw) if err != nil { return err } } io.WriteString(bw, "\r\n") // Write body and trailer err = tw.WriteBody(bw) if err != nil { return err } return bw.Flush() } // Convert decimal at s[i:len(s)] to integer, // returning value, string position where the digits stopped, // and whether there was a valid number (digits, not too big). func atoi(s string, i int) (n, i1 int, ok bool) { const Big = 1000000 if i >= len(s) || s[i] < '0' || s[i] > '9' { return 0, 0, false } n = 0 for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { n = n*10 + int(s[i]-'0') if n > Big { return 0, 0, false } } return n, i, true } // ParseHTTPVersion parses a HTTP version string. // "HTTP/1.0" returns (1, 0, true). func ParseHTTPVersion(vers string) (major, minor int, ok bool) { if len(vers) < 5 || vers[0:5] != "HTTP/" { return 0, 0, false } major, i, ok := atoi(vers, 5) if !ok || i >= len(vers) || vers[i] != '.' { return 0, 0, false } minor, i, ok = atoi(vers, i+1) if !ok || i != len(vers) { return 0, 0, false } return major, minor, true } // NewRequest returns a new Request given a method, URL, and optional body. func NewRequest(method, urlStr string, body io.Reader) (*Request, error) { u, err := url.Parse(urlStr) if err != nil { return nil, err } rc, ok := body.(io.ReadCloser) if !ok && body != nil { rc = ioutil.NopCloser(body) } req := &Request{ Method: method, URL: u, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: make(Header), Body: rc, Host: u.Host, } if body != nil { switch v := body.(type) { case *strings.Reader: req.ContentLength = int64(v.Len()) case *bytes.Buffer: req.ContentLength = int64(v.Len()) } } return req, nil } // SetBasicAuth sets the request's Authorization header to use HTTP // Basic Authentication with the provided username and password. // // With HTTP Basic Authentication the provided username and password // are not encrypted. func (r *Request) SetBasicAuth(username, password string) { s := username + ":" + password r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(s))) } // ReadRequest reads and parses a request from b. func ReadRequest(b *bufio.Reader) (req *Request, err error) { tp := textproto.NewReader(b) req = new(Request) // First line: GET /index.html HTTP/1.0 var s string if s, err = tp.ReadLine(); err != nil { return nil, err } defer func() { if err == io.EOF { err = io.ErrUnexpectedEOF } }() var f []string if f = strings.SplitN(s, " ", 3); len(f) < 3 { return nil, &badStringError{"malformed HTTP request", s} } req.Method, req.RequestURI, req.Proto = f[0], f[1], f[2] rawurl := req.RequestURI var ok bool if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok { return nil, &badStringError{"malformed HTTP version", req.Proto} } // CONNECT requests are used two different ways, and neither uses a full URL: // The standard use is to tunnel HTTPS through an HTTP proxy. // It looks like "CONNECT www.google.com:443 HTTP/1.1", and the parameter is // just the authority section of a URL. This information should go in req.URL.Host. // // The net/rpc package also uses CONNECT, but there the parameter is a path // that starts with a slash. It can be parsed with the regular URL parser, // and the path will end up in req.URL.Path, where it needs to be in order for // RPC to work. justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/") if justAuthority { rawurl = "http://" + rawurl } if req.URL, err = url.ParseRequestURI(rawurl); err != nil { return nil, err } if justAuthority { // Strip the bogus "http://" back off. req.URL.Scheme = "" } // Subsequent lines: Key: value. mimeHeader, err := tp.ReadMIMEHeader() if err != nil { return nil, err } req.Header = Header(mimeHeader) // RFC2616: Must treat // GET /index.html HTTP/1.1 // Host: www.google.com // and // GET http://www.google.com/index.html HTTP/1.1 // Host: doesntmatter // the same. In the second case, any Host line is ignored. req.Host = req.URL.Host if req.Host == "" { req.Host = req.Header.Get("Host") } req.Header.Del("Host") fixPragmaCacheControl(req.Header) // TODO: Parse specific header values: // Accept // Accept-Encoding // Accept-Language // Authorization // Cache-Control // Connection // Date // Expect // From // If-Match // If-Modified-Since // If-None-Match // If-Range // If-Unmodified-Since // Max-Forwards // Proxy-Authorization // Referer [sic] // TE (transfer-codings) // Trailer // Transfer-Encoding // Upgrade // User-Agent // Via // Warning err = readTransfer(req, b) if err != nil { return nil, err } return req, nil } // MaxBytesReader is similar to io.LimitReader but is intended for // limiting the size of incoming request bodies. In contrast to // io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a // non-EOF error for a Read beyond the limit, and Closes the // underlying reader when its Close method is called. // // MaxBytesReader prevents clients from accidentally or maliciously // sending a large request and wasting server resources. func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { return &maxBytesReader{w: w, r: r, n: n} } type maxBytesReader struct { w ResponseWriter r io.ReadCloser // underlying reader n int64 // max bytes remaining stopped bool } func (l *maxBytesReader) Read(p []byte) (n int, err error) { if l.n <= 0 { if !l.stopped { l.stopped = true if res, ok := l.w.(*response); ok { res.requestTooLarge() } } return 0, errors.New("http: request body too large") } if int64(len(p)) > l.n { p = p[:l.n] } n, err = l.r.Read(p) l.n -= int64(n) return } func (l *maxBytesReader) Close() error { return l.r.Close() } // ParseForm parses the raw query from the URL. // // For POST or PUT requests, it also parses the request body as a form. // If the request Body's size has not already been limited by MaxBytesReader, // the size is capped at 10MB. // // ParseMultipartForm calls ParseForm automatically. // It is idempotent. func (r *Request) ParseForm() (err error) { if r.Form != nil { return } if r.URL != nil { r.Form, err = url.ParseQuery(r.URL.RawQuery) } if r.Method == "POST" || r.Method == "PUT" { if r.Body == nil { return errors.New("missing form body") } ct := r.Header.Get("Content-Type") ct, _, err = mime.ParseMediaType(ct) switch { case ct == "application/x-www-form-urlencoded": var reader io.Reader = r.Body maxFormSize := int64(1<<63 - 1) if _, ok := r.Body.(*maxBytesReader); !ok { maxFormSize = int64(10 << 20) // 10 MB is a lot of text. reader = io.LimitReader(r.Body, maxFormSize+1) } b, e := ioutil.ReadAll(reader) if e != nil { if err == nil { err = e } break } if int64(len(b)) > maxFormSize { return errors.New("http: POST too large") } var newValues url.Values newValues, e = url.ParseQuery(string(b)) if err == nil { err = e } if r.Form == nil { r.Form = make(url.Values) } // Copy values into r.Form. TODO: make this smoother. for k, vs := range newValues { for _, value := range vs { r.Form.Add(k, value) } } case ct == "multipart/form-data": // handled by ParseMultipartForm (which is calling us, or should be) // TODO(bradfitz): there are too many possible // orders to call too many functions here. // Clean this up and write more tests. // request_test.go contains the start of this, // in TestRequestMultipartCallOrder. } } return err } // ParseMultipartForm parses a request body as multipart/form-data. // The whole request body is parsed and up to a total of maxMemory bytes of // its file parts are stored in memory, with the remainder stored on // disk in temporary files. // ParseMultipartForm calls ParseForm if necessary. // After one call to ParseMultipartForm, subsequent calls have no effect. func (r *Request) ParseMultipartForm(maxMemory int64) error { if r.MultipartForm == multipartByReader { return errors.New("http: multipart handled by MultipartReader") } if r.Form == nil { err := r.ParseForm() if err != nil { return err } } if r.MultipartForm != nil { return nil } mr, err := r.multipartReader() if err == ErrNotMultipart { return nil } else if err != nil { return err } f, err := mr.ReadForm(maxMemory) if err != nil { return err } for k, v := range f.Value { r.Form[k] = append(r.Form[k], v...) } r.MultipartForm = f return nil } // FormValue returns the first value for the named component of the query. // FormValue calls ParseMultipartForm and ParseForm if necessary. func (r *Request) FormValue(key string) string { if r.Form == nil { r.ParseMultipartForm(defaultMaxMemory) } if vs := r.Form[key]; len(vs) > 0 { return vs[0] } return "" } // FormFile returns the first file for the provided form key. // FormFile calls ParseMultipartForm and ParseForm if necessary. func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) { if r.MultipartForm == multipartByReader { return nil, nil, errors.New("http: multipart handled by MultipartReader") } if r.MultipartForm == nil { err := r.ParseMultipartForm(defaultMaxMemory) if err != nil { return nil, nil, err } } if r.MultipartForm != nil && r.MultipartForm.File != nil { if fhs := r.MultipartForm.File[key]; len(fhs) > 0 { f, err := fhs[0].Open() return f, fhs[0], err } } return nil, nil, ErrMissingFile } func (r *Request) expectsContinue() bool { return strings.ToLower(r.Header.Get("Expect")) == "100-continue" } func (r *Request) wantsHttp10KeepAlive() bool { if r.ProtoMajor != 1 || r.ProtoMinor != 0 { return false } return strings.Contains(strings.ToLower(r.Header.Get("Connection")), "keep-alive") } �����������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/sniff.go�����������������������������������������0000644�0000153�0000161�00000013524�12321735761�023442� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 http import ( "bytes" "encoding/binary" ) // The algorithm uses at most sniffLen bytes to make its decision. const sniffLen = 512 // DetectContentType implements the algorithm described // at http://mimesniff.spec.whatwg.org/ to determine the // Content-Type of the given data. It considers at most the // first 512 bytes of data. DetectContentType always returns // a valid MIME type: if it cannot determine a more specific one, it // returns "application/octet-stream". func DetectContentType(data []byte) string { if len(data) > sniffLen { data = data[:sniffLen] } // Index of the first non-whitespace byte in data. firstNonWS := 0 for ; firstNonWS < len(data) && isWS(data[firstNonWS]); firstNonWS++ { } for _, sig := range sniffSignatures { if ct := sig.match(data, firstNonWS); ct != "" { return ct } } return "application/octet-stream" // fallback } func isWS(b byte) bool { return bytes.IndexByte([]byte("\t\n\x0C\r "), b) != -1 } type sniffSig interface { // match returns the MIME type of the data, or "" if unknown. match(data []byte, firstNonWS int) string } // Data matching the table in section 6. var sniffSignatures = []sniffSig{ htmlSig("<!DOCTYPE HTML"), htmlSig("<HTML"), htmlSig("<HEAD"), htmlSig("<SCRIPT"), htmlSig("<IFRAME"), htmlSig("<H1"), htmlSig("<DIV"), htmlSig("<FONT"), htmlSig("<TABLE"), htmlSig("<A"), htmlSig("<STYLE"), htmlSig("<TITLE"), htmlSig("<B"), htmlSig("<BODY"), htmlSig("<BR"), htmlSig("<P"), htmlSig("<!--"), &maskedSig{mask: []byte("\xFF\xFF\xFF\xFF\xFF"), pat: []byte("<?xml"), skipWS: true, ct: "text/xml; charset=utf-8"}, &exactSig{[]byte("%PDF-"), "application/pdf"}, &exactSig{[]byte("%!PS-Adobe-"), "application/postscript"}, // UTF BOMs. &maskedSig{mask: []byte("\xFF\xFF\x00\x00"), pat: []byte("\xFE\xFF\x00\x00"), ct: "text/plain; charset=utf-16be"}, &maskedSig{mask: []byte("\xFF\xFF\x00\x00"), pat: []byte("\xFF\xFE\x00\x00"), ct: "text/plain; charset=utf-16le"}, &maskedSig{mask: []byte("\xFF\xFF\xFF\x00"), pat: []byte("\xEF\xBB\xBF\x00"), ct: "text/plain; charset=utf-8"}, &exactSig{[]byte("GIF87a"), "image/gif"}, &exactSig{[]byte("GIF89a"), "image/gif"}, &exactSig{[]byte("\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"), "image/png"}, &exactSig{[]byte("\xFF\xD8\xFF"), "image/jpeg"}, &exactSig{[]byte("BM"), "image/bmp"}, &maskedSig{ mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF"), pat: []byte("RIFF\x00\x00\x00\x00WEBPVP"), ct: "image/webp", }, &exactSig{[]byte("\x00\x00\x01\x00"), "image/vnd.microsoft.icon"}, &exactSig{[]byte("\x4F\x67\x67\x53\x00"), "application/ogg"}, &maskedSig{ mask: []byte("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF"), pat: []byte("RIFF\x00\x00\x00\x00WAVE"), ct: "audio/wave", }, &exactSig{[]byte("\x1A\x45\xDF\xA3"), "video/webm"}, &exactSig{[]byte("\x52\x61\x72\x20\x1A\x07\x00"), "application/x-rar-compressed"}, &exactSig{[]byte("\x50\x4B\x03\x04"), "application/zip"}, &exactSig{[]byte("\x1F\x8B\x08"), "application/x-gzip"}, // TODO(dsymonds): Re-enable this when the spec is sorted w.r.t. MP4. //mp4Sig(0), textSig(0), // should be last } type exactSig struct { sig []byte ct string } func (e *exactSig) match(data []byte, firstNonWS int) string { if bytes.HasPrefix(data, e.sig) { return e.ct } return "" } type maskedSig struct { mask, pat []byte skipWS bool ct string } func (m *maskedSig) match(data []byte, firstNonWS int) string { if m.skipWS { data = data[firstNonWS:] } if len(data) < len(m.mask) { return "" } for i, mask := range m.mask { db := data[i] & mask if db != m.pat[i] { return "" } } return m.ct } type htmlSig []byte func (h htmlSig) match(data []byte, firstNonWS int) string { data = data[firstNonWS:] if len(data) < len(h)+1 { return "" } for i, b := range h { db := data[i] if 'A' <= b && b <= 'Z' { db &= 0xDF } if b != db { return "" } } // Next byte must be space or right angle bracket. if db := data[len(h)]; db != ' ' && db != '>' { return "" } return "text/html; charset=utf-8" } type mp4Sig int func (mp4Sig) match(data []byte, firstNonWS int) string { // c.f. section 6.1. if len(data) < 8 { return "" } boxSize := int(binary.BigEndian.Uint32(data[:4])) if boxSize%4 != 0 || len(data) < boxSize { return "" } if !bytes.Equal(data[4:8], []byte("ftyp")) { return "" } for st := 8; st < boxSize; st += 4 { if st == 12 { // minor version number continue } seg := string(data[st : st+3]) switch seg { case "mp4", "iso", "M4V", "M4P", "M4B": return "video/mp4" /* The remainder are not in the spec. case "M4A": return "audio/mp4" case "3gp": return "video/3gpp" case "jp2": return "image/jp2" // JPEG 2000 */ } } return "" } type textSig int func (textSig) match(data []byte, firstNonWS int) string { // c.f. section 5, step 4. for _, b := range data[firstNonWS:] { switch { case 0x00 <= b && b <= 0x08, b == 0x0B, 0x0E <= b && b <= 0x1A, 0x1C <= b && b <= 0x1F: return "" } } return "text/plain; charset=utf-8" } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/fork/http/fs.go��������������������������������������������0000644�0000153�0000161�00000026237�12321735761�022752� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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. // HTTP file system request handler package http import ( "errors" "fmt" "io" "mime" "os" "path" "path/filepath" "strconv" "strings" "time" ) // A Dir implements http.FileSystem using the native file // system restricted to a specific directory tree. // // An empty Dir is treated as ".". type Dir string func (d Dir) Open(name string) (File, error) { if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 { return nil, errors.New("http: invalid character in file path") } dir := string(d) if dir == "" { dir = "." } f, err := os.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))) if err != nil { return nil, err } return f, nil } // A FileSystem implements access to a collection of named files. // The elements in a file path are separated by slash ('/', U+002F) // characters, regardless of host operating system convention. type FileSystem interface { Open(name string) (File, error) } // A File is returned by a FileSystem's Open method and can be // served by the FileServer implementation. type File interface { Close() error Stat() (os.FileInfo, error) Readdir(count int) ([]os.FileInfo, error) Read([]byte) (int, error) Seek(offset int64, whence int) (int64, error) } func dirList(w ResponseWriter, f File) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprintf(w, "<pre>\n") for { dirs, err := f.Readdir(100) if err != nil || len(dirs) == 0 { break } for _, d := range dirs { name := d.Name() if d.IsDir() { name += "/" } // TODO htmlescape fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", name, name) } } fmt.Fprintf(w, "</pre>\n") } // ServeContent replies to the request using the content in the // provided ReadSeeker. The main benefit of ServeContent over io.Copy // is that it handles Range requests properly, sets the MIME type, and // handles If-Modified-Since requests. // // If the response's Content-Type header is not set, ServeContent // first tries to deduce the type from name's file extension and, // if that fails, falls back to reading the first block of the content // and passing it to DetectContentType. // The name is otherwise unused; in particular it can be empty and is // never sent in the response. // // If modtime is not the zero time, ServeContent includes it in a // Last-Modified header in the response. If the request includes an // If-Modified-Since header, ServeContent uses modtime to decide // whether the content needs to be sent at all. // // The content's Seek method must work: ServeContent uses // a seek to the end of the content to determine its size. // // Note that *os.File implements the io.ReadSeeker interface. func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) { size, err := content.Seek(0, os.SEEK_END) if err != nil { Error(w, "seeker can't seek", StatusInternalServerError) return } _, err = content.Seek(0, os.SEEK_SET) if err != nil { Error(w, "seeker can't seek", StatusInternalServerError) return } serveContent(w, req, name, modtime, size, content) } // if name is empty, filename is unknown. (used for mime type, before sniffing) // if modtime.IsZero(), modtime is unknown. // content must be seeked to the beginning of the file. func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, size int64, content io.ReadSeeker) { if checkLastModified(w, r, modtime) { return } code := StatusOK // If Content-Type isn't set, use the file's extension to find it. if w.Header().Get("Content-Type") == "" { ctype := mime.TypeByExtension(filepath.Ext(name)) if ctype == "" { // read a chunk to decide between utf-8 text and binary var buf [1024]byte n, _ := io.ReadFull(content, buf[:]) b := buf[:n] ctype = DetectContentType(b) _, err := content.Seek(0, os.SEEK_SET) // rewind to output whole file if err != nil { Error(w, "seeker can't seek", StatusInternalServerError) return } } w.Header().Set("Content-Type", ctype) } // handle Content-Range header. // TODO(adg): handle multiple ranges sendSize := size if size >= 0 { ranges, err := parseRange(r.Header.Get("Range"), size) if err == nil && len(ranges) > 1 { err = errors.New("multiple ranges not supported") } if err != nil { Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) return } if len(ranges) == 1 { ra := ranges[0] if _, err := content.Seek(ra.start, os.SEEK_SET); err != nil { Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) return } sendSize = ra.length code = StatusPartialContent w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, size)) } w.Header().Set("Accept-Ranges", "bytes") if w.Header().Get("Content-Encoding") == "" { w.Header().Set("Content-Length", strconv.FormatInt(sendSize, 10)) } } w.WriteHeader(code) if r.Method != "HEAD" { if sendSize == -1 { io.Copy(w, content) } else { io.CopyN(w, content, sendSize) } } } // modtime is the modification time of the resource to be served, or IsZero(). // return value is whether this request is now complete. func checkLastModified(w ResponseWriter, r *Request, modtime time.Time) bool { if modtime.IsZero() { return false } // The Date-Modified header truncates sub-second precision, so // use mtime < t+1s instead of mtime <= t to check for unmodified. if t, err := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) { w.WriteHeader(StatusNotModified) return true } w.Header().Set("Last-Modified", modtime.UTC().Format(TimeFormat)) return false } // name is '/'-separated, not filepath.Separator. func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) { const indexPage = "/index.html" // redirect .../index.html to .../ // can't use Redirect() because that would make the path absolute, // which would be a problem running under StripPrefix if strings.HasSuffix(r.URL.Path, indexPage) { localRedirect(w, r, "./") return } f, err := fs.Open(name) if err != nil { // TODO expose actual error? NotFound(w, r) return } defer f.Close() d, err1 := f.Stat() if err1 != nil { // TODO expose actual error? NotFound(w, r) return } if redirect { // redirect to canonical path: / at end of directory url // r.URL.Path always begins with / url := r.URL.Path if d.IsDir() { if url[len(url)-1] != '/' { localRedirect(w, r, path.Base(url)+"/") return } } else { if url[len(url)-1] == '/' { localRedirect(w, r, "../"+path.Base(url)) return } } } // use contents of index.html for directory, if present if d.IsDir() { if checkLastModified(w, r, d.ModTime()) { return } index := name + indexPage ff, err := fs.Open(index) if err == nil { defer ff.Close() dd, err := ff.Stat() if err == nil { name = index d = dd f = ff } } } if d.IsDir() { dirList(w, f) return } serveContent(w, r, d.Name(), d.ModTime(), d.Size(), f) } // localRedirect gives a Moved Permanently response. // It does not convert relative paths to absolute paths like Redirect does. func localRedirect(w ResponseWriter, r *Request, newPath string) { if q := r.URL.RawQuery; q != "" { newPath += "?" + q } w.Header().Set("Location", newPath) w.WriteHeader(StatusMovedPermanently) } // ServeFile replies to the request with the contents of the named file or directory. func ServeFile(w ResponseWriter, r *Request, name string) { dir, file := filepath.Split(name) serveFile(w, r, Dir(dir), file, false) } type fileHandler struct { root FileSystem } // FileServer returns a handler that serves HTTP requests // with the contents of the file system rooted at root. // // To use the operating system's file system implementation, // use http.Dir: // // http.Handle("/", http.FileServer(http.Dir("/tmp"))) func FileServer(root FileSystem) Handler { return &fileHandler{root} } func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) { upath := r.URL.Path if !strings.HasPrefix(upath, "/") { upath = "/" + upath r.URL.Path = upath } serveFile(w, r, f.root, path.Clean(upath), true) } // httpRange specifies the byte range to be sent to the client. type httpRange struct { start, length int64 } // parseRange parses a Range header string as per RFC 2616. func parseRange(s string, size int64) ([]httpRange, error) { if s == "" { return nil, nil // header not present } const b = "bytes=" if !strings.HasPrefix(s, b) { return nil, errors.New("invalid range") } var ranges []httpRange for _, ra := range strings.Split(s[len(b):], ",") { i := strings.Index(ra, "-") if i < 0 { return nil, errors.New("invalid range") } start, end := ra[:i], ra[i+1:] var r httpRange if start == "" { // If no start is specified, end specifies the // range start relative to the end of the file. i, err := strconv.ParseInt(end, 10, 64) if err != nil { return nil, errors.New("invalid range") } if i > size { i = size } r.start = size - i r.length = size - r.start } else { i, err := strconv.ParseInt(start, 10, 64) if err != nil || i > size || i < 0 { return nil, errors.New("invalid range") } r.start = i if end == "" { // If no end is specified, range extends to end of the file. r.length = size - r.start } else { i, err := strconv.ParseInt(end, 10, 64) if err != nil || r.start > i { return nil, errors.New("invalid range") } if i >= size { i = size - 1 } r.length = i - r.start + 1 } } ranges = append(ranges, r) } return ranges, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/helpers_misc_test.go���������������������������������������0000644�0000153�0000161�00000001533�12321735761�024126� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "encoding/base64" "fmt" "io" ) // b64 is shorthand for base64-encoding a string. func b64(s string) string { return base64.StdEncoding.EncodeToString([]byte(s)) } // A Reader and ReadCloser that EOFs immediately. var Empty io.ReadCloser = makeResponseBody("") // BoolToString represents a boolean value as a string ("true" or "false"). func BoolToString(v bool) string { return fmt.Sprintf("%t", v) } // StringToBool parses a string containing a boolean (case-insensitive). func StringToBool(v string) (b bool) { items, err := fmt.Sscanf(v, "%t", &b) if err != nil || items != 1 { panic(fmt.Errorf("can't convert '%s' to a bool", v)) } return } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/helpers_http_test.go���������������������������������������0000644�0000153�0000161�00000003616�12321735761�024156� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). // // Test helpers for dealing with http requests through the http package. package gwacl import ( "encoding/base64" "fmt" "io" "io/ioutil" "net/http" "strings" ) // TestTransport is used as an http.Client.Transport for testing. It records // the latest request, and returns a predetermined Response and error. type TestTransport struct { Request *http.Request Response *http.Response Error error } // TestTransport implements the http.RoundTripper interface. var _ http.RoundTripper = &TestTransport{} func (t *TestTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { t.Request = req return t.Response, t.Error } // makeFakeCreatedResponse returns an HTTP response with the Created status. func makeFakeCreatedResponse() *http.Response { return &http.Response{ Status: fmt.Sprintf("%d", http.StatusCreated), StatusCode: http.StatusCreated, Body: Empty, } } // makeResponseBody creates an http response body containing the given string. // Use this to initialize an http.Response.Body with a given string, without // having to care about the type details. func makeResponseBody(content string) io.ReadCloser { return ioutil.NopCloser(strings.NewReader(content)) } // Convenience factory to create a StorageContext with a random name and // random base64-encoded key. func makeStorageContext(transport http.RoundTripper) *StorageContext { context := &StorageContext{ Account: MakeRandomString(10), Key: base64.StdEncoding.EncodeToString(MakeRandomByteSlice(10)), AzureEndpoint: APIEndpoint("http://" + MakeRandomString(5) + ".example.com/"), } context.client = &http.Client{Transport: transport} return context } ������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/.bzrignore�������������������������������������������������0000644�0000153�0000161�00000000020�12321735761�022053� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������./example/*/run ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/poller_test.go���������������������������������������������0000644�0000153�0000161�00000016657�12321735761�022763� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "encoding/xml" "fmt" . "launchpad.net/gocheck" "launchpad.net/gwacl/dedent" "net/http" "time" ) type pollerSuite struct{} var _ = Suite(&pollerSuite{}) func (suite *pollerSuite) makeAPI(c *C) *ManagementAPI { subscriptionId := "subscriptionId" subscriptionId = subscriptionId api, err := NewManagementAPI(subscriptionId, "", "West US") c.Assert(err, IsNil) return api } // testPoller is a struct which implements the Poller interface. It records // the number of calls to testPoller.Poll(). type testPoller struct { recordedPollCalls int notDoneCalls int errorCalls int } var testPollerResponse = x509Response{} var testPollerError = fmt.Errorf("Test error") // newTestPoller return a pointer to a testPoller object that will return // false when IsDone() will be called 'notDoneCalls' number of times and that // will error when Poll() will be called 'errorCalls' number of times. func newTestPoller(notDoneCalls int, errorCalls int) *testPoller { return &testPoller{0, notDoneCalls, errorCalls} } func (poller *testPoller) poll() (*x509Response, error) { if poller.errorCalls > 0 { poller.errorCalls -= 1 return nil, testPollerError } poller.recordedPollCalls += 1 return &testPollerResponse, nil } func (poller *testPoller) isDone(response *x509Response, pollerError error) (bool, error) { if pollerError != nil { return true, pollerError } if poller.notDoneCalls == 0 { return true, nil } poller.notDoneCalls = poller.notDoneCalls - 1 return false, nil } func (suite *pollerSuite) TestperformPollingPollsOnceImmediately(c *C) { poller := newTestPoller(0, 0) interval := time.Second * 10 start := time.Now() response, err := performPolling(poller, interval, interval*2) c.Assert(err, Equals, nil) c.Assert(time.Since(start) < interval, Equals, true) c.Assert(response, DeepEquals, &testPollerResponse) } func (suite *pollerSuite) TestperformPollingReturnsError(c *C) { poller := newTestPoller(0, 1) response, err := performPolling(poller, time.Nanosecond, time.Minute) c.Assert(err, Equals, testPollerError) c.Assert(response, IsNil) } func (suite *pollerSuite) TestperformPollingTimesout(c *C) { poller := newTestPoller(10, 0) response, err := performPolling(poller, time.Millisecond, 5*time.Millisecond) c.Assert(response, IsNil) c.Check(err, ErrorMatches, ".*polling timed out waiting for an asynchronous operation.*") } func (suite *pollerSuite) TestperformPollingRetries(c *C) { poller := newTestPoller(2, 0) response, err := performPolling(poller, time.Nanosecond, time.Minute) c.Assert(err, IsNil) c.Assert(response, DeepEquals, &testPollerResponse) // Poll() has been called 3 times: two calls for which IsDone() returned // false and one for which IsDone() return true. c.Assert(poller.recordedPollCalls, Equals, 3) } func (suite *pollerSuite) TestnewOperationPoller(c *C) { api := suite.makeAPI(c) operationID := "operationID" poller := newOperationPoller(api, operationID) operationPollerInstance := poller.(operationPoller) c.Check(operationPollerInstance.api, Equals, api) c.Check(operationPollerInstance.operationID, Equals, operationID) } func (suite *pollerSuite) TestOperationPollerPoll(c *C) { api := suite.makeAPI(c) operationID := "operationID" poller := newOperationPoller(api, operationID) recordedRequests := setUpDispatcher("operationID") _, err := poller.poll() c.Assert(err, IsNil) expectedURL := defaultManagement + api.session.subscriptionId + "/operations/" + operationID checkOneRequest(c, recordedRequests, expectedURL, "2009-10-01", nil, "GET") } var operationXMLTemplate = dedent.Dedent(` <?xml version="1.0" encoding="utf-8"?> <Operation xmlns="http://schemas.microsoft.com/windowsazure"> <ID>bogus-request-id</ID> <Status>%s</Status> </Operation> `) func (suite *pollerSuite) TestOperationPollerIsDoneReturnsTrueIfOperationDone(c *C) { poller := newOperationPoller(suite.makeAPI(c), "operationID") operationStatuses := []string{"Succeeded", "Failed"} for _, operationStatus := range operationStatuses { body := fmt.Sprintf(operationXMLTemplate, operationStatus) response := x509Response{ Body: []byte(body), StatusCode: http.StatusOK, } isDone, err := poller.isDone(&response, nil) c.Assert(err, IsNil) c.Assert(isDone, Equals, true) } } func (suite *pollerSuite) TestOperationPollerIsDoneReturnsFalse(c *C) { poller := newOperationPoller(suite.makeAPI(c), "operationID") notDoneResponses := []x509Response{ // 'InProgress' response. { Body: []byte(fmt.Sprintf(operationXMLTemplate, "InProgress")), StatusCode: http.StatusOK, }, // Error statuses. {StatusCode: http.StatusNotFound}, {StatusCode: http.StatusBadRequest}, {StatusCode: http.StatusInternalServerError}, } for _, response := range notDoneResponses { isDone, _ := poller.isDone(&response, nil) c.Assert(isDone, Equals, false) } } func (suite *pollerSuite) TestOperationPollerIsDoneReturnsXMLParsingError(c *C) { poller := newOperationPoller(suite.makeAPI(c), "operationID") // Invalid XML content. response := x509Response{ Body: []byte("><invalid XML"), StatusCode: http.StatusOK, } _, err := poller.isDone(&response, nil) c.Assert(err, NotNil) c.Check(err, FitsTypeOf, new(xml.SyntaxError)) } func (suite *pollerSuite) TestStartOperationPollingRetries(c *C) { // This is an end-to-end test of the operation poller; there is a certain // amount of duplication with the unit tests for performPolling() but // it's probably worth it to thoroughly test performOperationPolling(). // Fake 2 responses in sequence: a 'InProgress' response and then a // 'Succeeded' response. firstResponse := DispatcherResponse{ response: &x509Response{ Body: []byte(fmt.Sprintf(operationXMLTemplate, "InProgress")), StatusCode: http.StatusOK, }, errorObject: nil} secondResponse := DispatcherResponse{ response: &x509Response{ Body: []byte(fmt.Sprintf(operationXMLTemplate, "Succeeded")), StatusCode: http.StatusOK, }, errorObject: nil} responses := []DispatcherResponse{firstResponse, secondResponse} rigPreparedResponseDispatcher(responses) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) // Setup poller and start it. operationID := "operationID" poller := newOperationPoller(suite.makeAPI(c), operationID) response, err := performPolling(poller, time.Nanosecond, time.Minute) c.Assert(err, IsNil) c.Assert(response, DeepEquals, secondResponse.response) operationPollerInstance := poller.(operationPoller) expectedURL := defaultManagement + operationPollerInstance.api.session.subscriptionId + "/operations/" + operationID c.Assert(len(recordedRequests), Equals, 2) checkRequest(c, recordedRequests[0], expectedURL, "2009-10-01", nil, "GET") checkRequest(c, recordedRequests[1], expectedURL, "2009-10-01", nil, "GET") } ���������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/vhd_footer.go����������������������������������������������0000644�0000153�0000161�00000003351�12321735761�022551� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl // This is a pre-defined, base64-encoded 512-byte footer for a Virtual Hard // Disk (VHD) file. The footer sets the size of the VHD to a fixed 20Mb // (20972032 bytes) and is intended to be uploaded as the last page in a blob // of that size. // // The end use of this is to have a quick way of defining a fixed-size VHD // that can be attached to a VM instance. The rest of the file can be // sparse-filled as necessary with a filesystem to create a final, valid, // mountable disk. // // In case you were wondering *why* you would want to do this, it's the only // way of making additional data available to a new instance at boot time. // // If you want to generate a new one of these (if you need a new size for // example), the easiest way is to use VirtualBox to define a new one, and // then do 'tail -c 512 | base64' on that file. const VHD_SIZE = 20972032 // This is 20Mib + 512 bytes const VHD_FOOTER = ` Y29uZWN0aXgAAAACAAEAAP//////////GVKuuHZib3gABAACV2kyawAAAAABQAAAAAAAAAFAAAAC WgQRAAAAAv//5y4OEjVapHY7QpuodZNf77j6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=` ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/x509dispatcher_test.go�������������������������������������0000644�0000153�0000161�00000022676�12321736014�024230� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gwacl import ( "io/ioutil" . "launchpad.net/gocheck" "net/http" "net/http/httptest" "time" ) type x509DispatcherSuite struct{} var _ = Suite(&x509DispatcherSuite{}) type Request struct { *http.Request BodyContent []byte } // makeRecordingHTTPServer creates an http server (don't forget to Close() it when done) // that serves at the given base URL, copies incoming requests into the given // channel, and finally returns the given status code. If body is not nil, it // will be returned as the request body. func makeRecordingHTTPServer(requests chan *Request, status int, body []byte, headers http.Header) *httptest.Server { var server *httptest.Server returnRequest := func(w http.ResponseWriter, r *http.Request) { // Capture all the request body content for later inspection. requestBody, err := ioutil.ReadAll(r.Body) if err != nil { panic(err) } requests <- &Request{r, requestBody} // Set a default Location so we can test redirect loops easily. w.Header().Set("Location", server.URL+r.URL.Path) for header, values := range headers { for _, value := range values { w.Header().Set(header, value) } } w.WriteHeader(status) if body != nil { w.Write(body) } } serveMux := http.NewServeMux() serveMux.HandleFunc("/", returnRequest) server = httptest.NewServer(serveMux) return server } func (*x509DispatcherSuite) TestGetRequestDoesHTTPGET(c *C) { httpRequests := make(chan *Request, 1) server := makeRecordingHTTPServer(httpRequests, http.StatusOK, nil, nil) defer server.Close() // No real certificate needed since we're testing on http, not https. session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) path := "/foo/bar" version := "test-version" request := newX509RequestGET(server.URL+path, version) response, err := performX509Request(session, request) c.Assert(err, IsNil) c.Assert(response.StatusCode, Equals, http.StatusOK) httpRequest := <-httpRequests c.Check(httpRequest.Method, Equals, "GET") c.Check(httpRequest.Header[http.CanonicalHeaderKey("X-Ms-Version")], DeepEquals, []string{version}) c.Check(httpRequest.URL.String(), Equals, path) c.Check(httpRequest.BodyContent, HasLen, 0) } func (*x509DispatcherSuite) TestRetryPolicyCausesRequestsToBeRetried(c *C) { nbRetries := 2 nbRequests := nbRetries + 1 httpRequests := make(chan *Request, nbRequests) server := makeRecordingHTTPServer(httpRequests, http.StatusConflict, nil, nil) defer server.Close() // No real certificate needed since we're testing on http, not https. retryPolicy := RetryPolicy{NbRetries: nbRetries, HttpStatusCodes: []int{http.StatusConflict}, Delay: time.Nanosecond} session, err := newX509Session("subscriptionid", "", "West US", retryPolicy) c.Assert(err, IsNil) path := "/foo/bar" version := "test-version" request := newX509RequestGET(server.URL+path, version) response, err := performX509Request(session, request) c.Assert(err, IsNil) c.Assert(response.StatusCode, Equals, http.StatusConflict) // nbRequests request were performed. c.Check(httpRequests, HasLen, nbRequests) } func (*x509DispatcherSuite) TestPostRequestDoesHTTPPOST(c *C) { httpRequests := make(chan *Request, 1) requestBody := []byte{1, 2, 3} responseBody := []byte{4, 5, 6} requestContentType := "bogusContentType" server := makeRecordingHTTPServer(httpRequests, http.StatusOK, responseBody, nil) defer server.Close() // No real certificate needed since we're testing on http, not https. session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) path := "/foo/bar" version := "test-version" request := newX509RequestPOST(server.URL+path, version, requestBody, requestContentType) response, err := performX509Request(session, request) c.Assert(err, IsNil) c.Assert(response.StatusCode, Equals, http.StatusOK) c.Check(response.Body, DeepEquals, responseBody) httpRequest := <-httpRequests c.Check(httpRequest.Header[http.CanonicalHeaderKey("Content-Type")], DeepEquals, []string{requestContentType}) c.Check(httpRequest.Header[http.CanonicalHeaderKey("X-Ms-Version")], DeepEquals, []string{request.APIVersion}) c.Check(httpRequest.Method, Equals, "POST") c.Check(httpRequest.URL.String(), Equals, path) c.Check(httpRequest.BodyContent, DeepEquals, requestBody) } func (*x509DispatcherSuite) TestDeleteRequestDoesHTTPDELETE(c *C) { httpRequests := make(chan *Request, 1) server := makeRecordingHTTPServer(httpRequests, http.StatusOK, nil, nil) defer server.Close() // No real certificate needed since we're testing on http, not https. session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) path := "/foo/bar" version := "test-version" request := newX509RequestDELETE(server.URL+path, version) response, err := performX509Request(session, request) c.Assert(err, IsNil) c.Assert(response.StatusCode, Equals, http.StatusOK) httpRequest := <-httpRequests c.Check(httpRequest.Method, Equals, "DELETE") c.Check(httpRequest.Header[http.CanonicalHeaderKey("X-Ms-Version")], DeepEquals, []string{version}) c.Check(httpRequest.URL.String(), Equals, path) c.Check(httpRequest.BodyContent, HasLen, 0) } func (*x509DispatcherSuite) TestPutRequestDoesHTTPPUT(c *C) { httpRequests := make(chan *Request, 1) requestBody := []byte{1, 2, 3} responseBody := []byte{4, 5, 6} server := makeRecordingHTTPServer(httpRequests, http.StatusOK, responseBody, nil) defer server.Close() // No real certificate needed since we're testing on http, not https. session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) path := "/foo/bar" version := "test-version" request := newX509RequestPUT(server.URL+path, version, requestBody, "application/octet-stream") response, err := performX509Request(session, request) c.Assert(err, IsNil) c.Assert(response.StatusCode, Equals, http.StatusOK) c.Check(response.Body, DeepEquals, responseBody) httpRequest := <-httpRequests c.Check(httpRequest.Method, Equals, "PUT") c.Check(httpRequest.Header[http.CanonicalHeaderKey("X-Ms-Version")], DeepEquals, []string{version}) c.Check(httpRequest.URL.String(), Equals, path) c.Check(httpRequest.BodyContent, DeepEquals, requestBody) } func (*x509DispatcherSuite) TestRequestRegistersHeader(c *C) { customHeader := http.CanonicalHeaderKey("x-gwacl-test") customValue := []string{"present"} returnRequest := func(w http.ResponseWriter, r *http.Request) { w.Header()[customHeader] = customValue w.WriteHeader(http.StatusOK) } serveMux := http.NewServeMux() serveMux.HandleFunc("/", returnRequest) server := httptest.NewServer(serveMux) defer server.Close() session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) path := "/foo/bar" request := newX509RequestGET(server.URL+path, "testversion") response, err := performX509Request(session, request) c.Assert(err, IsNil) c.Check(response.Header[customHeader], DeepEquals, customValue) } func (*x509DispatcherSuite) TestRequestsFollowRedirects(c *C) { httpRequests := make(chan *Request, 2) serverConflict := makeRecordingHTTPServer(httpRequests, http.StatusConflict, nil, nil) defer serverConflict.Close() redirPath := "/else/where" responseHeaders := make(http.Header) responseHeaders.Set("Location", serverConflict.URL+redirPath) serverRedir := makeRecordingHTTPServer(httpRequests, http.StatusTemporaryRedirect, nil, responseHeaders) defer serverRedir.Close() session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) path := "/foo/bar" version := "test-version" // Test both GET and DELETE: DELETE does not normally // automatically follow redirects, however Azure requires // us to. requests := []*X509Request{ newX509RequestGET(serverRedir.URL+path, version), newX509RequestDELETE(serverRedir.URL+path, version), } for _, request := range requests { response, err := performX509Request(session, request) c.Assert(err, IsNil) c.Assert(response.StatusCode, Equals, http.StatusConflict) c.Assert(httpRequests, HasLen, 2) c.Assert((<-httpRequests).URL.String(), Equals, path) c.Assert((<-httpRequests).URL.String(), Equals, redirPath) } } func (*x509DispatcherSuite) TestRequestsLimitRedirects(c *C) { httpRequests := make(chan *Request, 10) serverRedir := makeRecordingHTTPServer(httpRequests, http.StatusTemporaryRedirect, nil, nil) defer serverRedir.Close() session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) path := "/foo/bar" version := "test-version" request := newX509RequestGET(serverRedir.URL+path, version) response, err := performX509Request(session, request) c.Assert(err, ErrorMatches, "stopped after 10 redirects") c.Assert(response, IsNil) c.Assert(httpRequests, HasLen, 10) close(httpRequests) for req := range httpRequests { c.Assert(req.URL.String(), Equals, path) } } ������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/logging/���������������������������������������������������0000755�0000153�0000161�00000000000�12321735761�021507� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/logging/logging_test.go������������������������������������0000644�0000153�0000161�00000001735�12321735761�024531� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package logging import ( . "launchpad.net/gocheck" "testing" ) var originalLevel = level func restoreLevel() { level = originalLevel } type testLogging struct{} var _ = Suite(&testLogging{}) func (suite *testLogging) TestSetLevel(c *C) { defer restoreLevel() // The names of the logging constants are recognised arguments to // setLevel(). level = -1 setLevel("DEBUG") c.Check(level, Equals, DEBUG) setLevel("INFO") c.Check(level, Equals, INFO) setLevel("WARN") c.Check(level, Equals, WARN) setLevel("ERROR") c.Check(level, Equals, ERROR) // Unrecognised arguments are ignored. level = -1 setLevel("FOOBAR") c.Check(level, Equals, -1) setLevel("123") c.Check(level, Equals, -1) } // Master loader for all tests. func Test(t *testing.T) { TestingT(t) } �����������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/logging/logging.go�����������������������������������������0000644�0000153�0000161�00000002571�12321735761�023471� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package logging import ( "log" "os" ) const ( DEBUG = 10 * (iota + 1) INFO WARN ERROR ) var level = WARN func init() { setLevel(os.Getenv("LOGLEVEL")) } func setLevel(levelName string) { switch levelName { case "DEBUG": level = DEBUG case "INFO": level = INFO case "WARN": level = WARN case "ERROR": level = ERROR } } func Debug(args ...interface{}) { if level <= DEBUG { log.Println(args...) } } func Debugf(format string, args ...interface{}) { if level <= DEBUG { log.Printf(format, args...) } } func Info(args ...interface{}) { if level <= INFO { log.Println(args...) } } func Infof(format string, args ...interface{}) { if level <= INFO { log.Printf(format, args...) } } func Warn(args ...interface{}) { if level <= WARN { log.Println(args...) } } func Warnf(format string, args ...interface{}) { if level <= WARN { log.Printf(format, args...) } } func Error(args ...interface{}) { if level <= ERROR { log.Println(args...) } } func Errorf(format string, args ...interface{}) { if level <= ERROR { log.Printf(format, args...) } } ���������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/poller.go��������������������������������������������������0000644�0000153�0000161�00000010534�12321735761�021710� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" "time" ) // Generic poller interface/methods. // A poller exposes two methods to query a remote server and decide when // the response given by the server means that the polling is finished. type poller interface { poll() (*x509Response, error) isDone(*x509Response, error) (bool, error) } // performPolling calls the poll() method of the given 'poller' object every // 'interval' until poller.isDone() returns true. func performPolling(poller poller, interval time.Duration, timeout time.Duration) (*x509Response, error) { timeoutChannel := time.After(timeout) ticker := time.Tick(interval) // Function to do a single poll, checking for timeout. The bool returned // indicates if polling is finished, one way or another. poll := func() (bool, *x509Response, error) { // This may need to tolerate some transient failures, such as network // failures that may go away after a few retries. select { case <-timeoutChannel: return true, nil, fmt.Errorf("polling timed out waiting for an asynchronous operation") default: response, pollerErr := poller.poll() done, err := poller.isDone(response, pollerErr) if err != nil { return true, nil, err } if done { return true, response, nil } } return false, nil, nil } // Do an initial poll. done, response, err := poll() if done { return response, err } // Poll every interval. for _ = range ticker { done, response, err := poll() if done { return response, err } } // This code cannot be reached but Go insists on having a return or a panic // statement at the end of this method. Sigh. panic("invalid poller state!") } // Operation poller structs/methods. // performOperationPolling calls performPolling on the given arguments and converts // the returned object into an *Operation. func performOperationPolling(poller poller, interval time.Duration, timeout time.Duration) (*Operation, error) { response, err := performPolling(poller, interval, timeout) if err != nil { return nil, err } operation := Operation{} err = operation.Deserialize(response.Body) return &operation, err } // operationPoller is an object implementing the poller interface, used to // poll the Window Azure server until the operation referenced by the given // operationID is completed. type operationPoller struct { api *ManagementAPI operationID string } var _ poller = &operationPoller{} // newOperationPoller returns a poller object associated with the given // management API object and the given operationID. It can track (by polling // the server) the status of the operation associated with the provided // operationID string. func newOperationPoller(api *ManagementAPI, operationID string) poller { return operationPoller{api: api, operationID: operationID} } // Poll issues a blocking request to microsoft Azure to fetch the information // related to the operation associated with the poller. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460783.aspx func (poller operationPoller) poll() (*x509Response, error) { URI := "operations/" + poller.operationID return poller.api.session.get(URI, "2009-10-01") } // IsDone returns true if the given response has a status code indicating // success and if the returned XML response corresponds to a valid Operation // with a status indicating that the operation is completed. func (poller operationPoller) isDone(response *x509Response, pollerError error) (bool, error) { // TODO: Add a timeout so that polling won't continue forever if the // server cannot be reached. if pollerError != nil { return true, pollerError } if response.StatusCode >= 200 && response.StatusCode < 300 { operation := Operation{} err := operation.Deserialize(response.Body) if err != nil { return false, err } status := operation.Status done := (status != "" && status != InProgressOperationStatus) return done, nil } return false, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/rolesizes_test.go������������������������������������������0000644�0000153�0000161�00000000555�12321735761�023473� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( . "launchpad.net/gocheck" ) type rolesizeSuite struct{} var _ = Suite(&rolesizeSuite{}) func (suite *rolesizeSuite) TestMapIsCreated(c *C) { c.Check(RoleNameMap, HasLen, len(RoleSizes)) } ���������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/xmlobjects.go����������������������������������������������0000644�0000153�0000161�00000110655�12321736014�022562� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "encoding/base64" "encoding/xml" "fmt" "net/url" "regexp" "sort" "strings" "time" ) // It's impossible to have any kind of common method inherited into all the // various serializable objects because the receiver of the method is the // wrong class which confuses the xml marshaller. Hence, this mess. type AzureObject interface { Serialize() (string, error) } // marshalXML is a wrapper for serializing objects to XML in the visual layout // that gwacl prefers. func marshalXML(obj interface{}) ([]byte, error) { return xml.MarshalIndent(obj, "", " ") } func toxml(obj AzureObject) (string, error) { out, err := marshalXML(obj) if err != nil { return "", err } return string(out), nil } // // ConfigurationSet bits // const ( CONFIG_SET_LINUX_PROVISIONING = "LinuxProvisioningConfiguration" CONFIG_SET_NETWORK = "NetworkConfiguration" ) // A ConfigurationSet object can be different things depending on its 'type'. // The types we currently support are: // - LinuxProvisioningConfigurationSet: configuration of a Linux VM // - NetworkConfiguration: configuration of the network of a VM type ConfigurationSet struct { ConfigurationSetType string `xml:"ConfigurationSetType"` // "ConfigurationSet" // LinuxProvisioningConfiguration fields. Hostname string `xml:"HostName,omitempty"` Username string `xml:"UserName,omitempty"` Password string `xml:"UserPassword,omitempty"` CustomData string `xml:"CustomData,omitempty"` DisableSSHPasswordAuthentication string `xml:"DisableSshPasswordAuthentication,omitempty"` // NetworkConfiguration fields. // We use slice pointers to work around a Go bug: // https://code.google.com/p/go/issues/detail?id=4168 // We need the whole 'InputEndpoints' and 'SubnetNames' element to be omitted // when no InputEndpoint objects are present (this happens when the // ConfigurationSet object has a LinuxProvisioningConfiguration type for // instance). InputEndpoints *[]InputEndpoint `xml:"InputEndpoints>InputEndpoint,omitempty"` SubnetNames *[]string `xml:"SubnetNames>SubnetName,omitempty"` } func (c *ConfigurationSet) inputEndpoints() []InputEndpoint { return *c.InputEndpoints } func (c *ConfigurationSet) Serialize() (string, error) { return toxml(c) } // NewLinuxProvisioningConfiguration creates and returns a ConfigurationSet of // type "LinuxProvisioningConfiguration" which is used when deploying a Linux // VM instance. Note that CustomData is passed to Azure *as-is* which also // stores it as passed, so consider base64 encoding it. func NewLinuxProvisioningConfigurationSet( Hostname, Username, Password, CustomData string, DisableSSHPasswordAuthentication string) *ConfigurationSet { return &ConfigurationSet{ ConfigurationSetType: CONFIG_SET_LINUX_PROVISIONING, Hostname: Hostname, Username: Username, Password: Password, CustomData: CustomData, DisableSSHPasswordAuthentication: DisableSSHPasswordAuthentication, } } // NewNetworkConfiguration creates a ConfigurationSet of type "NetworkConfiguration". func NewNetworkConfigurationSet( inputEndpoints []InputEndpoint, subnetNames []string) *ConfigurationSet { return &ConfigurationSet{ ConfigurationSetType: CONFIG_SET_NETWORK, InputEndpoints: &inputEndpoints, SubnetNames: &subnetNames, } } // // InputEndpoint bits // type LoadBalancerProbe struct { Path string `xml:"Path"` Port int `xml:"Port"` // Not uint16; see https://bugs.launchpad.net/juju-core/+bug/1201880 Protocol string `xml:"Protocol"` } type InputEndpoint struct { LoadBalancedEndpointSetName string `xml:"LoadBalancedEndpointSetName,omitempty"` LocalPort int `xml:"LocalPort"` // Not uint16; see https://bugs.launchpad.net/juju-core/+bug/1201880 Name string `xml:"Name"` Port int `xml:"Port"` // Not uint16; see https://bugs.launchpad.net/juju-core/+bug/1201880 LoadBalancerProbe *LoadBalancerProbe `xml:"LoadBalancerProbe,omitempty"` Protocol string `xml:"Protocol"` // TCP or UDP VIP string `xml:"Vip,omitempty"` } func (c *InputEndpoint) Serialize() (string, error) { return toxml(c) } // // Images bits // // Images is a series of OSImages. type Images struct { Images []OSImage `xml:"OSImage"` } func (i *Images) Deserialize(data []byte) error { return xml.Unmarshal(data, i) } var canonicalPublisherName = "Canonical" var imageFamilyFormatRegexp = "^Ubuntu Server %s.*$" func (images *Images) Len() int { return len(images.Images) } func (images *Images) Swap(i, j int) { images.Images[i], images.Images[j] = images.Images[j], images.Images[i] } // Less returns true if the image at index i is newer than the one at index j, comparing by // PublishedDate. // This function is used by sort.Sort(). func (images *Images) Less(i, j int) bool { // We need to implement the sort interface so Less cannot return an error. We panic if // one of the dates cannot be parse and the calling method will recover this. dateStringI := images.Images[i].PublishedDate dateI, err := time.Parse(time.RFC3339, dateStringI) if err != nil { panic(fmt.Errorf("Failed to parse image's 'PublishedDate': %s", dateStringI)) } dateStringJ := images.Images[j].PublishedDate dateJ, err := time.Parse(time.RFC3339, dateStringJ) if err != nil { panic(fmt.Errorf("Failed to parse image's 'PublishedDate': %s", dateStringJ)) } return dateI.After(dateJ) } // GetLatestUbuntuImage returns the most recent released available OSImage, // for the given release name and location. The 'releaseName' parameter is // the Ubuntu version number present in the 'ImageFamily' tag present in // Azure's representation of an OS Image (e.g. '12.04', '12.10'). func (images *Images) GetLatestUbuntuImage(releaseName string, location string) (image *OSImage, err error) { // The Less method defined above can panic if one of the published dates cannot be parsed, // this code recovers from that and transforms that into an error. defer func() { if recoveredErr := recover(); recoveredErr != nil { image = nil err = recoveredErr.(error) } }() matcherRegexp := regexp.MustCompile(fmt.Sprintf(imageFamilyFormatRegexp, releaseName)) matchingImages := Images{} for _, image := range images.Images { if image.PublisherName == canonicalPublisherName && matcherRegexp.MatchString(image.ImageFamily) && image.hasLocation(location) && !image.isDailyBuild() { matchingImages.Images = append(matchingImages.Images, image) } } if matchingImages.Len() == 0 { return nil, fmt.Errorf("No matching images found") } sort.Sort(&matchingImages) return &matchingImages.Images[0], nil } // // OSImage bits // // OSImage represents a disk image containing an operating system. // Confusingly, the Azure API documentation also calls it a VM image. type OSImage struct { AffinityGroup string `xml:"AffinityGroup,omitempty"` Category string `xml:"Category"` Label string `xml:"Label"` Location string `xml:"Location"` LogicalSizeInGB float32 `xml:"LogicalSizeInGB"` MediaLink string `xml:"MediaLink"` Name string `xml:"Name"` OS string `xml:"OS"` EULA string `xml:"Eula,omitempty"` Description string `xml:"Description,omitempty"` ImageFamily string `xml:"ImageFamily,omitempty"` PublishedDate string `xml:"PublishedDate,omitempty"` IsPremium string `xml:"IsPremium,omitempty"` PrivacyURI string `xml:"PrivacyUri,omitempty"` PricingDetailLink string `xml:"PricingDetailLink,omitempty"` IconURI string `xml:"IconUri,omitempty"` RecommendedVMSize string `xml:"RecommendedVMSize,omitempty"` PublisherName string `xml:"PublisherName"` ShowInGUI string `xml:"ShowInGui"` SmallIconURI string `xml:"SmallIconUri,omitempty"` Language string `xml:"Language"` } func (image *OSImage) hasLocation(location string) bool { locations := strings.Split(image.Location, ";") for _, loc := range locations { if loc == location { return true } } return false } // isDailyBuild returns whether this image is a daily build. func (image *OSImage) isDailyBuild() bool { return strings.Contains(image.Label, "DAILY") } func (i *OSImage) Deserialize(data []byte) error { return xml.Unmarshal(data, i) } // // DataVirtualHardDisk // type DataVirtualHardDisk struct { HostCaching string `xml:"HostCaching"` DiskName string `xml:"DiskName"` LUN string `xml:"Lun"` LogicalDiskSizeInGB string `xml:"LogicalDiskSizeInGB"` MediaLink string `xml:"MediaLink"` } // // OSVirtualHardDisk bits // type HostCachingType string const ( HostCachingRO HostCachingType = "ReadOnly" HostCachingRW HostCachingType = "ReadWrite" ) type OSVirtualHardDisk struct { HostCaching string `xml:"HostCaching,omitempty"` DiskLabel string `xml:"DiskLabel,omitempty"` DiskName string `xml:"DiskName,omitempty"` MediaLink string `xml:"MediaLink,omitempty"` SourceImageName string `xml:"SourceImageName,omitempty"` OS string `xml:"OS,omitempty"` } func (c *OSVirtualHardDisk) Serialize() (string, error) { return toxml(c) } func NewOSVirtualHardDisk( HostCaching HostCachingType, DiskLabel, DiskName, MediaLink, SourceImageName, OS string) *OSVirtualHardDisk { return &OSVirtualHardDisk{ HostCaching: string(HostCaching), DiskLabel: DiskLabel, DiskName: DiskName, SourceImageName: SourceImageName, MediaLink: MediaLink, OS: OS, } } // CreateVirtualHardDiskMediaLink creates a media link string used to specify // the location of a physical blob in the given Windows Azure storage account. // Example: http://example.blob.core.windows.net/disks/mydatadisk.vhd func CreateVirtualHardDiskMediaLink(StorageName, StoragePath string) string { pathComponents := strings.Split(StoragePath, "/") components := append(pathComponents, StorageName) checkPathComponents(components...) return fmt.Sprintf("http://%s.blob.core.windows.net/%s", StorageName, StoragePath) } type Role struct { RoleName string `xml:"RoleName"` RoleType string `xml:"RoleType"` // Always "PersistentVMRole" ConfigurationSets []ConfigurationSet `xml:"ConfigurationSets>ConfigurationSet"` OSVirtualHardDisk []OSVirtualHardDisk `xml:"OSVirtualHardDisk"` RoleSize string `xml:"RoleSize"` } // // Role bits // func (c *Role) Serialize() (string, error) { return toxml(c) } func NewRole(RoleSize string, RoleName string, ConfigurationSets []ConfigurationSet, vhds []OSVirtualHardDisk) *Role { return &Role{ RoleSize: RoleSize, RoleName: RoleName, RoleType: "PersistentVMRole", ConfigurationSets: ConfigurationSets, OSVirtualHardDisk: vhds, } } // // DnsServer bits // type DnsServer struct { Name string `xml:"Name"` Address string `xml:"Address"` } func (c *DnsServer) Serialize() (string, error) { return toxml(c) } // // Hosted service bits // // HostedService represents a cloud service in Azure. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460806.aspx type HostedService struct { HostedServiceDescriptor XMLNS string `xml:"xmlns,attr"` Deployments []Deployment `xml:"Deployments>Deployment"` } func (c HostedService) Serialize() (string, error) { return toxml(c) } func (c *HostedService) Deserialize(data []byte) error { return xml.Unmarshal(data, c) } type HostedServiceDescriptorList struct { XMLName xml.Name `xml:"HostedServices"` XMLNS string `xml:"xmlns,attr"` HostedServices []HostedServiceDescriptor `xml:"HostedService"` } func (c *HostedServiceDescriptorList) Serialize() (string, error) { return toxml(c) } func (c *HostedServiceDescriptorList) Deserialize(data []byte) error { return xml.Unmarshal(data, c) } // HostedServiceDescriptor contains a subset of the details in HostedService, // and is used when describing a list of HostedServices. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460781.aspx type HostedServiceDescriptor struct { URL string `xml:"Url"` ServiceName string `xml:"ServiceName"` Description string `xml:"HostedServiceProperties>Description"` AffinityGroup string `xml:"HostedServiceProperties>AffinityGroup"` Location string `xml:"HostedServiceProperties>Location"` Label string `xml:"HostedServiceProperties>Label"` Status string `xml:"HostedServiceProperties>Status"` DateCreated string `xml:"HostedServiceProperties>DateCreated"` DateLastModified string `xml:"HostedServiceProperties>DateLastModified"` ExtendedProperties []ExtendedProperty `xml:"HostedServiceProperties>ExtendedProperties>ExtendedProperty"` } func (c HostedServiceDescriptor) Serialize() (string, error) { return toxml(c) } func (service *HostedServiceDescriptor) GetLabel() (string, error) { label, err := base64.StdEncoding.DecodeString(service.Label) if err != nil { return "", err } return string(label), nil } type CreateHostedService struct { XMLNS string `xml:"xmlns,attr"` ServiceName string `xml:"ServiceName"` Label string `xml:"Label"` // base64-encoded Description string `xml:"Description"` Location string `xml:"Location,omitempty"` AffinityGroup string `xml:"AffinityGroup,omitempty"` ExtendedProperties []ExtendedProperty `xml:"ExtendedProperties>ExtendedProperty"` } func NewCreateHostedServiceWithLocation(serviceName, label, location string) *CreateHostedService { base64label := base64.StdEncoding.EncodeToString([]byte(label)) return &CreateHostedService{ XMLNS: XMLNS, ServiceName: serviceName, Label: base64label, Location: location, } } func (s *CreateHostedService) Deserialize(data []byte) error { return xml.Unmarshal(data, s) } // AvailabilityResponse is the reply from a Check Hosted Service Name // Availability operation. type AvailabilityResponse struct { XMLNS string `xml:"xmlns,attr"` Result string `xml:"Result"` Reason string `xml:"Reason"` } func (a *AvailabilityResponse) Deserialize(data []byte) error { return xml.Unmarshal(data, a) } // UpdateHostedService contains the details necessary to call the // UpdateHostedService management API call. // See http://msdn.microsoft.com/en-us/library/windowsazure/gg441303.aspx type UpdateHostedService struct { XMLNS string `xml:"xmlns,attr"` Label string `xml:"Label,omitempty"` // base64-encoded Description string `xml:"Description,omitempty"` ExtendedProperties []ExtendedProperty `xml:"ExtendedProperties>ExtendedProperty,omitempty"` } func (u *UpdateHostedService) Serialize() (string, error) { return toxml(u) } func NewUpdateHostedService(label, description string, properties []ExtendedProperty) *UpdateHostedService { base64label := base64.StdEncoding.EncodeToString([]byte(label)) return &UpdateHostedService{ XMLNS: XMLNS, Label: base64label, Description: description, ExtendedProperties: properties, } } // // Deployment bits // // Deployment is used both as input for the "Create Virtual Machine Deployment" // call, and as a return value for "Get Deployment." type Deployment struct { XMLNS string `xml:"xmlns,attr"` XMLNS_I string `xml:"xmlns:i,attr"` Name string `xml:"Name"` // DeploymentSlot is either "Production" or "Staging". DeploymentSlot string `xml:"DeploymentSlot"` PrivateID string `xml:"PrivateID,omitempty"` // Only used for "Get Deployment." Status string `xml:"Status,omitempty"` // Only used for "Get Deployment." Label string `xml:"Label"` URL string `xml:"Url,omitempty"` // Only used for "Get Deployment." Configuration string `xml:"Configuration,omitempty"` // Only used for "Get Deployment." RoleInstanceList []RoleInstance `xml:"RoleInstanceList>RoleInstance"` UpgradeDomainCount string `xml:"UpgradeDomainCount,omitempty"` // Only used for "Get Deployment." RoleList []Role `xml:"RoleList>Role"` SDKVersion string `xml:"SdkVersion,omitempty"` // Only used for "Get Deployment." Locked string `xml:"Locked,omitempty"` // Only used for "Get Deployment." RollbackAllowed string `xml:"RollbackAllowed,omitempty"` // Only used for "Get Deployment." VirtualNetworkName string `xml:VirtualNetworkName,omitempty"` DNS []DnsServer `xml:"Dns>DnsServers>DnsServer",omitempty` ExtendedProperties []ExtendedProperty `xml:"ExtendedProperties>ExtendedProperty,omitempty"` // Only used for "Get Deployment." } func (deployment *Deployment) GetFQDN() (string, error) { if deployment.URL == "" { return "", fmt.Errorf("Deployment's URL field is empty") } parsedURL, err := url.Parse(deployment.URL) if err != nil { return "", err } return parsedURL.Host, nil } func (s *Deployment) Deserialize(data []byte) error { return xml.Unmarshal(data, s) } func (c *Deployment) Serialize() (string, error) { return toxml(c) } // RoleInstance is a component of a Deployment. type RoleInstance struct { RoleName string `xml:"RoleName"` InstanceName string `xml:"InstanceName"` InstanceStatus string `xml:"InstanceStatus"` InstanceUpgradeDomain string `xml:"InstanceUpgradeDomain"` InstanceFaultDomain string `xml:"InstanceFaultDomain"` InstanceSize string `xml:"InstanceSize"` InstanceStateDetails string `xml:"InstanceStateDetails"` InstanceErrorCode string `xml:"InstanceErrorCode"` IPAddress string `xml:"IpAddress"` InstanceEndpoints []InstanceEndpoint `xml:"InstanceEndpoints>InstanceEndpoint"` PowerState string `xml:"PowerState"` HostName string `xml:"HostName"` RemoteAccessCertificateThumbprint string `xml:"RemoteAccessCertificateThumbprint"` } // InstanceEndpoint is a component of a RoleInstance. type InstanceEndpoint struct { Name string `xml:"Name"` VIP string `xml:"Vip"` PublicPort int `xml:"PublicPort"` // Not uint16; see https://bugs.launchpad.net/juju-core/+bug/1201880 LocalPort int `xml:"LocalPort"` // Not uint16; see https://bugs.launchpad.net/juju-core/+bug/1201880 Protocol string `xml:"Protocol"` } // newDeploymentForCreateVMDeployment creates a Deployment object for the // purpose of passing it to "Create Virtual Machine Deployment." // You may still want to set the optional DNS attribute. func NewDeploymentForCreateVMDeployment(name, deploymentSlot, label string, roles []Role, virtualNetworkName string) *Deployment { deployment := Deployment{ XMLNS: XMLNS, XMLNS_I: XMLNS_I, Name: name, DeploymentSlot: deploymentSlot, Label: base64.StdEncoding.EncodeToString([]byte(label)), RoleList: roles, VirtualNetworkName: virtualNetworkName, } return &deployment } const XMLNS = "http://schemas.microsoft.com/windowsazure" const XMLNS_I = "http://www.w3.org/2001/XMLSchema-instance" const XMLNS_NC = "http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration" // // Role Operations bits // type RoleOperation struct { XMLName xml.Name XMLNS string `xml:"xmlns,attr"` XMLNS_I string `xml:"xmlns:i,attr"` OperationType string `xml:"OperationType"` } func newRoleOperation(operationType string) *RoleOperation { operation := RoleOperation{ XMLNS: XMLNS, XMLNS_I: XMLNS_I, OperationType: operationType, } operation.XMLName.Local = operationType return &operation } // The Start Role operation starts a virtual machine. // http://msdn.microsoft.com/en-us/library/windowsazure/jj157189.aspx var startRoleOperation = newRoleOperation("StartRoleOperation") // The Shutdown Role operation shuts down a virtual machine. // http://msdn.microsoft.com/en-us/library/windowsazure/jj157195.aspx var shutdownRoleOperation = newRoleOperation("ShutdownRoleOperation") // The Restart role operation restarts a virtual machine. // http://msdn.microsoft.com/en-us/library/windowsazure/jj157197.aspx var restartRoleOperation = newRoleOperation("RestartRoleOperation") // // PersistentVMRole, as used by GetRole, UpdateRole, etc. // type PersistentVMRole struct { XMLNS string `xml:"xmlns,attr"` RoleName string `xml:"RoleName"` OsVersion string `xml:"OsVersion"` RoleType string `xml:"RoleType"` // Always PersistentVMRole ConfigurationSets []ConfigurationSet `xml:"ConfigurationSets>ConfigurationSet"` AvailabilitySetName string `xml:"AvailabilitySetName"` DataVirtualHardDisks *[]DataVirtualHardDisk `xml:"DataVirtualHardDisks>DataVirtualHardDisk,omitempty"` OSVirtualHardDisk OSVirtualHardDisk `xml:"OSVirtualHardDisk"` RoleSize string `xml:"RoleSize"` DefaultWinRmCertificateThumbprint string `xml:"DefaultWinRmCertificateThumbprint"` } func (role *PersistentVMRole) Deserialize(data []byte) error { return xml.Unmarshal(data, role) } func (role *PersistentVMRole) Serialize() (string, error) { return toxml(role) } // // Virtual Networks // type VirtualNetDnsServer struct { XMLName string `xml:"DnsServer"` Name string `xml:"name,attr"` IPAddress string `xml:"IPAddress,attr"` } type LocalNetworkSite struct { XMLName string `xml:"LocalNetworkSite"` Name string `xml:"name,attr"` AddressSpacePrefixes []string `xml:"AddressSpace>AddressPrefix"` VPNGatewayAddress string `xml:"VPNGatewayAddress"` } type Subnet struct { XMLName string `xml:"Subnet"` Name string `xml:"name,attr"` AddressPrefix string `xml:"AddressPrefix"` } type LocalNetworkSiteRefConnection struct { XMLName string `xml:"Connection"` Type string `xml:"type,attr"` } type LocalNetworkSiteRef struct { XMLName string `xml:"LocalNetworkSiteRef"` Name string `xml:"name,attr"` Connection LocalNetworkSiteRefConnection `xml:"Connection"` } type Gateway struct { XMLName string `xml:"Gateway"` Profile string `xml:"profile,attr"` VPNClientAddressPoolPrefixes []string `xml:"VPNClientAddressPool>AddressPrefix"` LocalNetworkSiteRef LocalNetworkSiteRef `xml:"ConnectionsToLocalNetwork>LocalNetworkSiteRef"` } type DnsServerRef struct { XMLName string `xml:"DnsServerRef"` Name string `xml:"name,attr"` } type VirtualNetworkSite struct { Name string `xml:"name,attr"` AffinityGroup string `xml:"AffinityGroup,attr"` AddressSpacePrefixes []string `xml:"AddressSpace>AddressPrefix"` Subnets *[]Subnet `xml:"Subnets>Subnet",omitempty` DnsServersRef *[]DnsServerRef `xml:"DnsServersRef>DnsServerRef",omitempty` Gateway *Gateway `xml:"Gateway",omitempty` } type NetworkConfiguration struct { XMLNS string `xml:"xmlns,attr"` DNS *[]VirtualNetDnsServer `xml:"VirtualNetworkConfiguration>Dns>DnsServers>DnsServer",omitempty` LocalNetworkSites *[]LocalNetworkSite `xml:"VirtualNetworkConfiguration>LocalNetworkSites>LocalNetworkSite",omitempty` VirtualNetworkSites *[]VirtualNetworkSite `xml:"VirtualNetworkConfiguration>VirtualNetworkSites>VirtualNetworkSite",omitempty` } func (nc *NetworkConfiguration) Serialize() (string, error) { return toxml(nc) } func (nc *NetworkConfiguration) Deserialize(data []byte) error { return xml.Unmarshal(data, nc) } // // Affinity Group // // See http://msdn.microsoft.com/en-us/library/windowsazure/gg715317.aspx type CreateAffinityGroup struct { XMLNS string `xml:"xmlns,attr"` Name string `xml:"Name"` Label string `xml:"Label"` // Must be base64 encoded. Description string `xml:"Description",omitempty` Location string `xml:"Location"` // Value comes from ListLocations. } func (c *CreateAffinityGroup) Serialize() (string, error) { return toxml(c) } func NewCreateAffinityGroup(name, label, description, location string) *CreateAffinityGroup { base64label := base64.StdEncoding.EncodeToString([]byte(label)) return &CreateAffinityGroup{ XMLNS: XMLNS, Name: name, Label: base64label, Description: description, Location: location, } } // See http://msdn.microsoft.com/en-us/library/windowsazure/gg715316.aspx type UpdateAffinityGroup struct { XMLNS string `xml:"xmlns,attr"` Label string `xml:"Label"` // Must be base64 encoded. Description string `xml:"Description",omitempty` } func (u *UpdateAffinityGroup) Serialize() (string, error) { return toxml(u) } func NewUpdateAffinityGroup(label, description string) *UpdateAffinityGroup { base64label := base64.StdEncoding.EncodeToString([]byte(label)) return &UpdateAffinityGroup{ XMLNS: XMLNS, Label: base64label, Description: description, } } // // Storage Services bits // type ExtendedProperty struct { Name string `xml:"Name"` Value string `xml:"Value"` } type StorageService struct { // List Storage Accounts. // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460787.aspx URL string `xml:"Url"` ServiceName string `xml:"ServiceName"` Description string `xml:"StorageServiceProperties>Description"` AffinityGroup string `xml:"StorageServiceProperties>AffinityGroup"` Label string `xml:"StorageServiceProperties>Label"` // base64 Status string `xml:"StorageServiceProperties>Status"` Endpoints []string `xml:"StorageServiceProperties>Endpoints>Endpoint"` GeoReplicationEnabled string `xml:"StorageServiceProperties>GeoReplicationEnabled"` GeoPrimaryRegion string `xml:"StorageServiceProperties>GeoPrimaryRegion"` StatusOfPrimary string `xml:"StorageServiceProperties>StatusOfPrimary"` LastGeoFailoverTime string `xml:"StorageServiceProperties>LastGeoFailoverTime"` GeoSecondaryRegion string `xml:"StorageServiceProperties>GeoSecondaryRegion"` StatusOfSecondary string `xml:"StorageServiceProperties>StatusOfSecondary"` ExtendedProperties []ExtendedProperty `xml:"StorageServiceProperties>ExtendedProperties>ExtendedProperty"` // TODO: Add accessors for non-string data encoded as strings. } type StorageServices struct { XMLNS string `xml:"xmlns,attr"` StorageServices []StorageService `xml:"StorageService"` } func (s *StorageServices) Deserialize(data []byte) error { return xml.Unmarshal(data, s) } // CreateStorageServiceInput is a request to create a storage account. // (Azure's "storage services" seem to have been renamed to "storage accounts" // but the old terminology is still evident in the API). type CreateStorageServiceInput struct { // See http://msdn.microsoft.com/en-us/library/windowsazure/hh264518.aspx XMLNS string `xml:"xmlns,attr"` ServiceName string `xml:"ServiceName"` Label string `xml:"Label"` Description string `xml:"Description,omitempty"` Location string `xml:"Location"` AffinityGroup string `xml:"AffinityGroup,omitempty"` GeoReplicationEnabled string `xml:"GeoReplicationEnabled,omitempty"` ExtendedProperties []ExtendedProperty `xml:"ExtendedProperties>ExtendedProperty"` } func (c *CreateStorageServiceInput) Serialize() (string, error) { return toxml(c) } // NewCreateStorageServiceInputWithLocation creates a location-based // CreateStorageServiceInput, with all required fields filled out. func NewCreateStorageServiceInputWithLocation(name, label, location string, geoReplicationEnabled string) *CreateStorageServiceInput { return &CreateStorageServiceInput{ XMLNS: XMLNS, ServiceName: name, Label: base64.StdEncoding.EncodeToString([]byte(label)), Location: location, GeoReplicationEnabled: geoReplicationEnabled, } } type MetadataItem struct { XMLName xml.Name Value string `xml:",chardata"` } func (item *MetadataItem) Name() string { return item.XMLName.Local } type Metadata struct { Items []MetadataItem `xml:",any"` } type Blob struct { Name string `xml:"Name"` Snapshot string `xml:"Snapshot"` URL string `xml:"Url"` LastModified string `xml:"Properties>Last-Modified"` ETag string `xml:"Properties>Etag"` ContentLength string `xml:"Properties>Content-Length"` ContentType string `xml:"Properties>Content-Type"` BlobSequenceNumber string `xml:"Properties>x-ms-blob-sequence-number"` BlobType string `xml:"Properties>BlobType"` LeaseStatus string `xml:"Properties>LeaseStatus"` LeaseState string `xml:"Properties>LeaseState"` LeaseDuration string `xml:"Properties>LeaseDuration"` CopyID string `xml:"Properties>CopyId"` CopyStatus string `xml:"Properties>CopyStatus"` CopySource string `xml:"Properties>CopySource"` CopyProgress string `xml:"Properties>CopyProgress"` CopyCompletionTime string `xml:"Properties>CopyCompletionTime"` CopyStatusDescription string `xml:"Properties>CopyStatusDescription"` Metadata Metadata `xml:"Metadata"` } type BlobEnumerationResults struct { // http://msdn.microsoft.com/en-us/library/windowsazure/dd135734.aspx XMLName xml.Name `xml:"EnumerationResults"` ContainerName string `xml:"ContainerName,attr"` Prefix string `xml:"Prefix"` Marker string `xml:"Marker"` MaxResults string `xml:"MaxResults"` Delimiter string `xml:"Delimiter"` Blobs []Blob `xml:"Blobs>Blob"` BlobPrefixes []string `xml:"Blobs>BlobPrefix>Name"` NextMarker string `xml:"NextMarker"` } func (b *BlobEnumerationResults) Deserialize(data []byte) error { return xml.Unmarshal(data, b) } type StorageAccountKeys struct { // See http://msdn.microsoft.com/en-us/library/windowsazure/ee460785.aspx XMLName xml.Name `xml:"StorageService"` URL string `xml:"Url"` Primary string `xml:"StorageServiceKeys>Primary"` Secondary string `xml:"StorageServiceKeys>Secondary"` } func (s *StorageAccountKeys) Deserialize(data []byte) error { return xml.Unmarshal(data, s) } type ContainerEnumerationResults struct { // See http://msdn.microsoft.com/en-us/library/windowsazure/dd179352.aspx XMLName xml.Name `xml:"EnumerationResults"` Prefix string `xml:"Prefix"` Marker string `xml:"Marker"` MaxResults string `xml:"MaxResults"` Containers []Container `xml:"Containers>Container"` NextMarker string `xml:"NextMarker"` } func (s *ContainerEnumerationResults) Deserialize(data []byte) error { return xml.Unmarshal(data, s) } type Container struct { XMLName xml.Name `xml:"Container"` Name string `xml:"Name"` URL string `xml:"URL"` Properties Properties `xml:"Properties"` Metadata Metadata `xml:"Metadata"` } type Properties struct { LastModified string `xml:"Last-Modified"` ETag string `xml:"Etag"` LeaseStatus string `xml:"LeaseStatus"` LeaseState string `xml:"LeaseState"` LeaseDuration string `xml:"LeaseDuration"` } // An enumeration-lite type to define from which list (committed, uncommitted, // latest) to get a block during a PutBlockList Storage API operation. type BlockListType string const ( BlockListUncommitted BlockListType = "Uncommitted" BlockListCommitted BlockListType = "Committed" BlockListLatest BlockListType = "Latest" ) // Payload for the PutBlockList operation. type BlockList struct { XMLName xml.Name `xml:"BlockList"` Items []*BlockListItem } func (s *BlockList) Serialize() ([]byte, error) { return marshalXML(s) } // Add a BlockListItem to a BlockList. func (s *BlockList) Add(blockType BlockListType, blockID string) { base64ID := base64.StdEncoding.EncodeToString([]byte(blockID)) item := NewBlockListItem(blockType, base64ID) s.Items = append(s.Items, item) } type BlockListItem struct { XMLName xml.Name BlockID string `xml:",chardata"` } // Create a new BlockListItem. func NewBlockListItem(blockType BlockListType, blockID string) *BlockListItem { return &BlockListItem{ XMLName: xml.Name{Local: string(blockType)}, BlockID: blockID, } } func (item *BlockListItem) Type() BlockListType { switch BlockListType(item.XMLName.Local) { case BlockListUncommitted: return BlockListUncommitted case BlockListCommitted: return BlockListCommitted case BlockListLatest: return BlockListLatest } panic(fmt.Errorf("type not recognized: %s", item.XMLName.Local)) } // GetBlockList result struct. type Block struct { Name string `xml:"Name"` Size string `xml:"Size"` } type GetBlockList struct { XMLName xml.Name `xml:"BlockList"` CommittedBlocks []Block `xml:"CommittedBlocks>Block"` UncommittedBlocks []Block `xml:"UncommittedBlocks>Block"` } func (g *GetBlockList) Deserialize(data []byte) error { return xml.Unmarshal(data, g) } // // Operation Services bits // const ( InProgressOperationStatus = "InProgress" SucceededOperationStatus = "Succeeded" FailedOperationStatus = "Failed" ) type Operation struct { ID string `xml:"ID"` Status string `xml:"Status"` HTTPStatusCode int `xml:"HttpStatusCode"` ErrorCode string `xml:"Error>Code"` ErrorMessage string `xml:"Error>Message"` } func (o *Operation) Deserialize(data []byte) error { return xml.Unmarshal(data, o) } �����������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/README�����������������������������������������������������0000644�0000153�0000161�00000003446�12321735761�020750� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=========================================== GWACL - The Go Windows Azure Client Library =========================================== How to generate an x509 key to talk to Azure -------------------------------------------- Azure requires that API clients use an x509 certificate to authenticate to the management API. Create the certificate with:: openssl req -config /usr/share/ssl-cert/ssleay.cnf -x509 -nodes \ -days 3650 -newkey rsa:2048 -keyout azure.pem -out azure.pem Note the use of ``ssleay.cnf``. It just contains some crappy defaults so you don't get prompted for certificate data. You can leave it out if you want, but then you'll need to enter country, organisation, etc. Azure wants you to upload a ``.cer`` file (which is in DER format). Here's how you can extract a ``.cer`` file from the ``.pem``:: openssl x509 -inform pem -in azure.pem -outform der -out azure.cer You can now upload ``azure.cer`` to Azure as a management certificate. Using the key in GWACL ---------------------- GWACL requires the key in the .pem file, so make sure you keep that file around. The .cer file can be deleted as you won't need it again, and it's easy to regenerate if you want to re-upload it. Example programs ---------------- Storage ^^^^^^^ The storage example is a stand-alone tool which allows the user to manipulate a storage account:: go run example/storage/run.go --help Management ^^^^^^^^^^ The management example is a piece of code that starts up a new role instance, optionally pauses so you can play with it, and then shuts everything down again. It is intended to be useful for testing the library itself, but also serves as an example of how to use the GWACL API:: go run example/management/run.go -cert <your pem file> -subscriptionid <your Azure subscription ID> [-wait] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/retry_policy.go��������������������������������������������0000644�0000153�0000161�00000007640�12321735761�023143� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( forkedHttp "launchpad.net/gwacl/fork/http" "net/http" "time" ) // A RetryPolicy object encapsulates all the information needed to define how // requests should be retried when particular response codes are returned by // the Windows Azure server. type RetryPolicy struct { // The number of times a request could be retried. This does not account // for the initial request so a value of 3 means that the request might be // performed 4 times in total. NbRetries int // The HTTP status codes of the response for which the request should be // retried. HttpStatusCodes []int // How long the client should wait between retries. Delay time.Duration } var ( NoRetryPolicy = RetryPolicy{NbRetries: 0} ) // isRetryCode returns whether or not the given http status code indicates that // the request should be retried according to this policy. func (policy RetryPolicy) isRetryCode(httpStatusCode int) bool { for _, code := range policy.HttpStatusCodes { if code == httpStatusCode { return true } } return false } func (policy RetryPolicy) getRetryHelper() *retryHelper { return &retryHelper{retriesLeft: policy.NbRetries, policy: &policy} } // A retryHelper is a utility object used to enforce a retry policy when // performing requests. type retryHelper struct { // The maximum number of retries left to perform. retriesLeft int // The `RetryPolicy` enforced by this retrier. policy *RetryPolicy } // shouldRetry returns whether or not a request governed by the underlying // retry policy should be retried. When it returns 'true', `shouldRetry` also // waits for the specified amount of time, as dictated by the retry policy. func (ret *retryHelper) shouldRetry(httpStatusCode int) bool { if ret.retriesLeft > 0 && ret.policy.isRetryCode(httpStatusCode) { ret.retriesLeft-- return true } return false } // A retrier is a struct used to repeat a request as governed by a retry // policy. retrier is usually created using RetryPolicy.getRetrier(). type retrier struct { *retryHelper // The client used to perform requests. client *http.Client } func (ret *retrier) RetryRequest(request *http.Request) (*http.Response, error) { for { response, err := ret.client.Do(request) if err != nil { return nil, err } if !ret.shouldRetry(response.StatusCode) { return response, nil } time.Sleep(ret.policy.Delay) } } // getRetrier returns a `retrier` object used to enforce the retry policy. func (policy RetryPolicy) getRetrier(client *http.Client) *retrier { helper := policy.getRetryHelper() return &retrier{retryHelper: helper, client: client} } // A forkedHttpRetrier is a struct used to repeat a request as governed by a // retry policy. forkedHttpRetrier is usually created using // RetryPolicy.getForkedHttpRetrier(). It's the same as the `retrier` struct // except it deals with the forked version of the http package. type forkedHttpRetrier struct { *retryHelper // The client used to perform requests. client *forkedHttp.Client } func (ret *forkedHttpRetrier) RetryRequest(request *forkedHttp.Request) (*forkedHttp.Response, error) { for { response, err := ret.client.Do(request) if err != nil { return nil, err } if !ret.shouldRetry(response.StatusCode) { return response, nil } time.Sleep(ret.policy.Delay) } } // getRetrier returns a `retrier` object used to enforce the retry policy. func (policy RetryPolicy) getForkedHttpRetrier(client *forkedHttp.Client) *forkedHttpRetrier { helper := policy.getRetryHelper() return &forkedHttpRetrier{retryHelper: helper, client: client} } ������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/endpoints.go�����������������������������������������������0000644�0000153�0000161�00000004213�12321735761�022413� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" "net/url" "strings" ) // APIEndpoint describes the base URL for accesing Windows Azure's APIs. // // Azure will have subdomains on this URL's domain, such as blob.<domain> for // storage, with further sub-domains for storage accounts; management.<domain> // for the management API; and possibly more such as queue.<domain>, // table.<domain>. APIEndpoint defines methods to obtain these URLs. type APIEndpoint string // GetEndpoint returns the API endpoint for the given location. This is // hard-coded, so some guesswork may be involved. func GetEndpoint(location string) APIEndpoint { if strings.Contains(location, "China") { // Mainland China is a special case. It has its own endpoint. return "https://core.chinacloudapi.cn/" } // The rest of the world shares a single endpoint. return "https://core.windows.net/" } // prefixHost prefixes the hostname part of a URL with a subdomain. For // example, prefixHost("foo", "http://example.com") becomes // "http://foo.example.com". // // The URL must be well-formed, and contain a hostname. func prefixHost(host, originalURL string) string { parsedURL, err := url.Parse(originalURL) if err != nil { panic(fmt.Errorf("failed to parse URL %s - %v", originalURL, err)) } if parsedURL.Host == "" { panic(fmt.Errorf("no hostname in URL '%s'", originalURL)) } // Escape manually. Strangely, turning a url.URL into a string does not // do this for you. parsedURL.Host = url.QueryEscape(host) + "." + parsedURL.Host return parsedURL.String() } // ManagementAPI returns the URL for the endpoint's management API. func (endpoint APIEndpoint) ManagementAPI() string { return prefixHost("management", string(endpoint)) } // BlobStorageAPI returns a URL for the endpoint's blob storage API, for // requests on the given account. func (endpoint APIEndpoint) BlobStorageAPI(account string) string { return prefixHost(account, prefixHost("blob", string(endpoint))) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/storage_test.go��������������������������������������������0000644�0000153�0000161�00000045643�12321735761�023127� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "bytes" "encoding/base64" "fmt" "io/ioutil" . "launchpad.net/gocheck" "net/http" "net/url" "strings" ) type testUploadBlockBlob struct{} var _ = Suite(&testUploadBlockBlob{}) func (suite *testUploadBlockBlob) TestSmallFile(c *C) { transport := &MockingTransport{} context := makeStorageContext(transport) // UploadBlockBlob uses PutBlock to upload the data. transport.AddExchange(makeFakeCreatedResponse(), nil) // UploadBlockBlob then sends the list of blocks with PutBlockList. transport.AddExchange(makeFakeCreatedResponse(), nil) // Upload a random blob of data. data := uploadRandomBlob(c, context, 10, "MyContainer", "MyFile") // There were two exchanges. c.Assert(transport.ExchangeCount, Equals, 2) // The first request is a Put Block with the block data. fileURL := context.GetFileURL("MyContainer", "MyFile") assertBlockSent(c, context, data, b64("000000000000000000000000000000"), transport.Exchanges[0], fileURL) // The second request is Put Block List to commit the block above. assertBlockListSent(c, context, []string{b64("000000000000000000000000000000")}, transport.Exchanges[1], fileURL) } func (suite *testUploadBlockBlob) TestLargeFile(c *C) { transport := &MockingTransport{} context := makeStorageContext(transport) // UploadBlockBlob uses PutBlock twice to upload the data. transport.AddExchange(makeFakeCreatedResponse(), nil) transport.AddExchange(makeFakeCreatedResponse(), nil) // UploadBlockBlob then sends the list of blocks with PutBlockList. transport.AddExchange(makeFakeCreatedResponse(), nil) // Upload a large random blob of data. data := uploadRandomBlob(c, context, 1348*1024, "MyContainer", "MyFile") // There were three exchanges. c.Assert(transport.ExchangeCount, Equals, 3) // The first two requests are Put Block with chunks of the block data. The // weird looking block IDs are base64 encodings of the strings "0" and "1". fileURL := context.GetFileURL("MyContainer", "MyFile") assertBlockSent(c, context, data[:1024*1024], b64("000000000000000000000000000000"), transport.Exchanges[0], fileURL) assertBlockSent(c, context, data[1024*1024:], b64("000000000000000000000000000001"), transport.Exchanges[1], fileURL) // The second request is Put Block List to commit the block above. assertBlockListSent(c, context, []string{b64("000000000000000000000000000000"), b64("000000000000000000000000000001")}, transport.Exchanges[2], fileURL) } func uploadRandomBlob(c *C, context *StorageContext, size int, container, filename string) []byte { data := MakeRandomByteSlice(size) err := context.UploadBlockBlob( container, filename, bytes.NewReader(data)) c.Assert(err, IsNil) return data } func assertBlockSent( c *C, context *StorageContext, data []byte, blockID string, exchange *MockingTransportExchange, fileURL string) { c.Check(exchange.Request.URL.String(), Matches, fileURL+"?.*") c.Check(exchange.Request.URL.Query(), DeepEquals, url.Values{ "comp": {"block"}, "blockid": {blockID}, }) body, err := ioutil.ReadAll(exchange.Request.Body) c.Assert(err, IsNil) // DeepEquals is painfully slow when comparing larger structures, so we // compare the expected (data) and observed (body) slices directly. c.Assert(len(body), Equals, len(data)) for i := range body { // c.Assert also noticably slows things down; this condition is an // optimisation of the c.Assert call contained within. if body[i] != data[i] { c.Assert(body[i], Equals, data[i]) } } } func assertBlockListSent( c *C, context *StorageContext, blockIDs []string, exchange *MockingTransportExchange, fileURL string) { c.Check(exchange.Request.URL.String(), Matches, fileURL+"?.*") c.Check(exchange.Request.URL.Query(), DeepEquals, url.Values{ "comp": {"blocklist"}, }) body, err := ioutil.ReadAll(exchange.Request.Body) c.Check(err, IsNil) expected := "<BlockList>\n" for _, blockID := range blockIDs { expected += " <Latest>" + blockID + "</Latest>\n" } expected += "</BlockList>" c.Check(strings.TrimSpace(string(body)), Equals, strings.TrimSpace(expected)) } type testListAllBlobs struct{} var _ = Suite(&testListAllBlobs{}) // The ListAllBlobs Storage API call returns a BlobEnumerationResults struct // on success. func (suite *testListAllBlobs) Test(c *C) { responseBody := ` <?xml version="1.0" encoding="utf-8"?> <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer"> <Prefix>prefix</Prefix> <Marker>marker</Marker> <MaxResults>maxresults</MaxResults> <Delimiter>delimiter</Delimiter> <Blobs> <Blob> <Name>blob-name</Name> <Snapshot>snapshot-date-time</Snapshot> <Url>blob-address</Url> <Properties> <Last-Modified>last-modified</Last-Modified> <Etag>etag</Etag> <Content-Length>size-in-bytes</Content-Length> <Content-Type>blob-content-type</Content-Type> <Content-Encoding /> <Content-Language /> <Content-MD5 /> <Cache-Control /> <x-ms-blob-sequence-number>sequence-number</x-ms-blob-sequence-number> <BlobType>blobtype</BlobType> <LeaseStatus>leasestatus</LeaseStatus> <LeaseState>leasestate</LeaseState> <LeaseDuration>leasesduration</LeaseDuration> <CopyId>id</CopyId> <CopyStatus>copystatus</CopyStatus> <CopySource>copysource</CopySource> <CopyProgress>copyprogress</CopyProgress> <CopyCompletionTime>copycompletiontime</CopyCompletionTime> <CopyStatusDescription>copydesc</CopyStatusDescription> </Properties> <Metadata> <MetaName1>metadataname1</MetaName1> <MetaName2>metadataname2</MetaName2> </Metadata> </Blob> <BlobPrefix> <Name>blob-prefix</Name> </BlobPrefix> </Blobs> <NextMarker /> </EnumerationResults>` response := &http.Response{ Status: fmt.Sprintf("%d", http.StatusOK), StatusCode: http.StatusOK, Body: makeResponseBody(responseBody), } transport := &TestTransport{Response: response} context := makeStorageContext(transport) request := &ListBlobsRequest{Container: "container"} results, err := context.ListAllBlobs(request) c.Assert(err, IsNil) c.Check(transport.Request.URL.String(), Matches, context.getContainerURL("container")+"?.*") c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{ "restype": {"container"}, "comp": {"list"}, }) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") c.Assert(results, NotNil) } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *testListAllBlobs) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) request := &ListBlobsRequest{Container: "container"} _, err := context.ListAllBlobs(request) c.Assert(err, NotNil) } // Server-side errors are propagated back to the caller. func (suite *testListAllBlobs) TestErrorResponse(c *C) { response := &http.Response{ Status: fmt.Sprintf("%d", http.StatusNotFound), StatusCode: http.StatusNotFound, } context := makeStorageContext(&TestTransport{Response: response}) request := &ListBlobsRequest{Container: "container"} _, err := context.ListAllBlobs(request) c.Assert(err, NotNil) } // ListAllBlobs combines multiple batches of output. func (suite *testListAllBlobs) TestBatchedResult(c *C) { firstBlob := "blob1" lastBlob := "blob2" marker := "moreplease" firstBatch := http.Response{ StatusCode: http.StatusOK, Body: makeResponseBody(fmt.Sprintf(` <EnumerationResults> <Blobs> <Blob> <Name>%s</Name> </Blob> </Blobs> <NextMarker>%s</NextMarker> </EnumerationResults> `, firstBlob, marker)), } lastBatch := http.Response{ StatusCode: http.StatusOK, Body: makeResponseBody(fmt.Sprintf(` <EnumerationResults> <Blobs> <Blob> <Name>%s</Name> </Blob> </Blobs> </EnumerationResults> `, lastBlob)), } transport := &MockingTransport{} transport.AddExchange(&firstBatch, nil) transport.AddExchange(&lastBatch, nil) context := makeStorageContext(transport) request := &ListBlobsRequest{Container: "mycontainer"} blobs, err := context.ListAllBlobs(request) c.Assert(err, IsNil) c.Check(len(blobs.Blobs), Equals, 2) c.Check(blobs.Blobs[0].Name, Equals, firstBlob) c.Check(blobs.Blobs[1].Name, Equals, lastBlob) } type testListAllContainers struct{} var _ = Suite(&testListAllContainers{}) // The ListAllContainers Storage API call returns a ContainerEnumerationResults // struct on success. func (suite *testListAllContainers) Test(c *C) { responseBody := ` <?xml version="1.0" encoding="utf-8"?> <EnumerationResults AccountName="http://myaccount.blob.core.windows.net"> <Prefix>prefix-value</Prefix> <Marker>marker-value</Marker> <MaxResults>max-results-value</MaxResults> <Containers> <Container> <Name>name-value</Name> <URL>url-value</URL> <Properties> <Last-Modified>date/time-value</Last-Modified> <Etag>etag-value</Etag> <LeaseStatus>lease-status-value</LeaseStatus> <LeaseState>lease-state-value</LeaseState> <LeaseDuration>lease-duration-value</LeaseDuration> </Properties> <Metadata> <metadata-name>metadata-value</metadata-name> </Metadata> </Container> </Containers> <NextMarker/> </EnumerationResults>` response := &http.Response{ Status: fmt.Sprintf("%d", http.StatusOK), StatusCode: http.StatusOK, Body: makeResponseBody(responseBody), } transport := &TestTransport{Response: response} context := makeStorageContext(transport) context.AzureEndpoint = "http://example.com/" results, err := context.ListAllContainers() c.Assert(err, IsNil) c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf( "http://%s.blob.example.com/?comp=list", context.Account)) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") c.Assert(results, NotNil) c.Assert(results.Containers[0].Name, Equals, "name-value") } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *testListAllContainers) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) _, err := context.ListAllContainers() c.Assert(err, NotNil) } // Server-side errors are propagated back to the caller. func (suite *testListAllContainers) TestErrorResponse(c *C) { response := &http.Response{ Status: fmt.Sprintf("%d", http.StatusNotFound), StatusCode: http.StatusNotFound, } context := makeStorageContext(&TestTransport{Response: response}) _, err := context.ListAllContainers() c.Assert(err, NotNil) } // ListAllContainers combines multiple batches of output. func (suite *testListAllContainers) TestBatchedResult(c *C) { firstContainer := "container1" lastContainer := "container2" marker := "moreplease" firstBatch := http.Response{ StatusCode: http.StatusOK, Body: makeResponseBody(fmt.Sprintf(` <EnumerationResults> <Containers> <Container> <Name>%s</Name> <URL>container-address</URL> </Container> </Containers> <NextMarker>%s</NextMarker> </EnumerationResults> `, firstContainer, marker)), } lastBatch := http.Response{ StatusCode: http.StatusOK, Body: makeResponseBody(fmt.Sprintf(` <EnumerationResults> <Containers> <Container> <Name>%s</Name> <URL>container-address</URL> </Container> </Containers> </EnumerationResults> `, lastContainer)), } transport := &MockingTransport{} transport.AddExchange(&firstBatch, nil) transport.AddExchange(&lastBatch, nil) context := makeStorageContext(transport) containers, err := context.ListAllContainers() c.Assert(err, IsNil) c.Check(len(containers.Containers), Equals, 2) c.Check(containers.Containers[0].Name, Equals, firstContainer) c.Check(containers.Containers[1].Name, Equals, lastContainer) } type testDeleteAllBlobs struct{} var _ = Suite(&testDeleteAllBlobs{}) func (s *testDeleteAllBlobs) makeListingResponse() *http.Response { return &http.Response{ Status: fmt.Sprintf("%d", http.StatusOK), StatusCode: http.StatusOK, Body: makeResponseBody(` <?xml version="1.0" encoding="utf-8"?> <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer"> <Prefix>prefix</Prefix> <Marker>marker</Marker> <MaxResults>maxresults</MaxResults> <Delimiter>delimiter</Delimiter> <Blobs> <Blob> <Name>blob-name</Name> </Blob> <Blob> <Name>blob-name2</Name> </Blob> </Blobs> <NextMarker /> </EnumerationResults>`), } } func (s *testDeleteAllBlobs) TestHappyPath(c *C) { listResponse := s.makeListingResponse() deleteResponse := &http.Response{ Status: fmt.Sprintf("%d", http.StatusAccepted), StatusCode: http.StatusAccepted, } transport := &MockingTransport{} transport.AddExchange(listResponse, nil) transport.AddExchange(deleteResponse, nil) transport.AddExchange(deleteResponse, nil) context := makeStorageContext(transport) err := context.DeleteAllBlobs(&DeleteAllBlobsRequest{Container: "container"}) c.Assert(err, IsNil) c.Assert(transport.ExchangeCount, Equals, 3) // Check the ListAllBlobs exchange. c.Check( transport.Exchanges[0].Request.URL.String(), Matches, context.getContainerURL("container")+"[?].*") c.Check(transport.Exchanges[0].Request.URL.Query(), DeepEquals, url.Values{ "restype": {"container"}, "comp": {"list"}, }) // Check the DeleteBlob exchanges. c.Check( transport.Exchanges[1].Request.URL.String(), Equals, context.GetFileURL("container", "blob-name")) c.Check(transport.Exchanges[1].Request.Method, Equals, "DELETE") c.Check( transport.Exchanges[2].Request.URL.String(), Equals, context.GetFileURL("container", "blob-name2")) c.Check(transport.Exchanges[2].Request.Method, Equals, "DELETE") } func (s *testDeleteAllBlobs) TestErrorsListing(c *C) { transport := &MockingTransport{} transport.AddExchange(&http.Response{ Status: fmt.Sprintf("%d", http.StatusNotFound), StatusCode: http.StatusNotFound}, nil) context := makeStorageContext(transport) err := context.DeleteAllBlobs(&DeleteAllBlobsRequest{Container: "c"}) c.Assert(err, ErrorMatches, `request for blobs list failed: Azure request failed \(404: Not Found\)`) } func (s *testDeleteAllBlobs) TestErrorsDeleting(c *C) { transport := &MockingTransport{} listResponse := s.makeListingResponse() transport.AddExchange(listResponse, nil) transport.AddExchange(&http.Response{ Status: fmt.Sprintf("%d", http.StatusBadRequest), StatusCode: http.StatusBadRequest}, nil) context := makeStorageContext(transport) err := context.DeleteAllBlobs(&DeleteAllBlobsRequest{Container: "c"}) c.Assert(err, ErrorMatches, `failed to delete blob blob-name: Azure request failed \(400: Bad Request\)`) } type testCreateInstanceDataVHD struct{} var _ = Suite(&testCreateInstanceDataVHD{}) func (s *testCreateInstanceDataVHD) TestHappyPath(c *C) { response := http.Response{ Status: fmt.Sprintf("%d", http.StatusOK), StatusCode: http.StatusCreated, } transport := &MockingTransport{} transport.AddExchange(&response, nil) // putblob response transport.AddExchange(&response, nil) // first putpage transport.AddExchange(&response, nil) // second putpage context := makeStorageContext(transport) randomData := MakeRandomByteSlice(512) dataReader := bytes.NewReader(randomData) var err error err = context.CreateInstanceDataVHD(&CreateVHDRequest{ Container: "container", Filename: "filename", FilesystemData: dataReader, Size: 512}) c.Assert(err, IsNil) // Check the PutBlob exchange. c.Check( transport.Exchanges[0].Request.Header.Get("x-ms-blob-type"), Equals, "PageBlob") expectedSize := fmt.Sprintf("%d", VHD_SIZE) c.Check( transport.Exchanges[0].Request.Header.Get("x-ms-blob-content-length"), Equals, expectedSize) // Check the PutPage for the footer exchange. data, err := ioutil.ReadAll(transport.Exchanges[1].Request.Body) c.Assert(err, IsNil) expectedData, err := base64.StdEncoding.DecodeString(VHD_FOOTER) c.Assert(err, IsNil) c.Check(data, DeepEquals, expectedData) expectedRange := fmt.Sprintf("bytes=%d-%d", VHD_SIZE-512, VHD_SIZE-1) c.Check( transport.Exchanges[1].Request.Header.Get("x-ms-range"), Equals, expectedRange) // Check the PutPage for the data exchange. data, err = ioutil.ReadAll(transport.Exchanges[2].Request.Body) c.Assert(err, IsNil) c.Check(data, DeepEquals, randomData) c.Check( transport.Exchanges[2].Request.Header.Get("x-ms-range"), Equals, "bytes=0-511") } func (s *testCreateInstanceDataVHD) TestSizeConstraints(c *C) { var err error context := makeStorageContext(&TestTransport{}) err = context.CreateInstanceDataVHD(&CreateVHDRequest{Size: 10}) c.Check(err, ErrorMatches, "Size must be a multiple of 512") err = context.CreateInstanceDataVHD(&CreateVHDRequest{ Size: VHD_SIZE}) errString := fmt.Sprintf("Size cannot be bigger than %d", VHD_SIZE-512) c.Check(err, ErrorMatches, errString) } ���������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/Makefile���������������������������������������������������0000644�0000153�0000161�00000001547�12321735761�021530� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Build, and run tests. Be careful of the ordering of flags here; get # it wrong and things can disappear into the void without warning. # Yes, both -v and -gocheck.v=true are needed. check: examples go test -v ./... -gocheck.v=true debug-test: go test -c -gcflags "-N -l" gdb gwacl.test $(RM) gwacl.test all_source := $(shell find . -name '*.go' ! -name '*_test.go') example_source := $(wildcard example/*/run.go) example_binaries := $(patsubst %.go,%,$(example_source)) # Clean up binaries. clean: $(RM) $(example_binaries) # Reformat the source files to match our layout standards. # This includes gofmt's "simplify" option to streamline the source code. format: ./utilities/format -s # Build the examples (we have no tests for them). examples: $(example_binaries) %: %.go $(all_source) go build -o $@ $< .PHONY: check clean format examples debug_check ���������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/storage_base_test.go���������������������������������������0000644�0000153�0000161�00000157601�12321735761�024117� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "bytes" "encoding/base64" "errors" "fmt" "io/ioutil" . "launchpad.net/gocheck" "launchpad.net/gwacl/dedent" "net/http" "net/url" "strings" "time" ) type testComposeHeaders struct{} var _ = Suite(&testComposeHeaders{}) func makeHttpResponse(status int, body string) *http.Response { return &http.Response{ Status: fmt.Sprintf("%d", status), StatusCode: status, Body: makeResponseBody(body), } } func (suite *testComposeHeaders) TestNoHeaders(c *C) { req, err := http.NewRequest("GET", "http://example.com", nil) c.Assert(err, IsNil) observed := composeHeaders(req) expected := "\n\n\n\n\n\n\n\n\n\n\n" c.Assert(observed, Equals, expected) } func (suite *testComposeHeaders) TestCreatesHeaders(c *C) { req, err := http.NewRequest("GET", "http://example.com", nil) c.Assert(err, IsNil) var items []string for i, headerName := range headersToSign { v := fmt.Sprintf("%d", i) req.Header.Set(headerName, v) items = append(items, v+"\n") } expected := strings.Join(items, "") observed := composeHeaders(req) c.Assert(observed, Equals, expected) } func (suite *testComposeHeaders) TestCanonicalizedHeaders(c *C) { req, err := http.NewRequest("GET", "http://example.com", nil) c.Assert(err, IsNil) req.Header.Set("x-ms-why", "aye") req.Header.Set("x-ms-foo", "bar") req.Header.Set("invalid", "blah") expected := "x-ms-foo:bar\nx-ms-why:aye\n" observed := composeCanonicalizedHeaders(req) c.Check(observed, Equals, expected) } type TestRetryRequests struct{} var _ = Suite(&TestRetryRequests{}) func (suite *TestRetryRequests) TestRequestIsRetried(c *C) { transport := &MockingTransport{} body := []byte("data") transport.AddExchange(&http.Response{StatusCode: http.StatusConflict, Body: Empty}, nil) transport.AddExchange(&http.Response{StatusCode: http.StatusConflict, Body: Empty}, nil) transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: makeResponseBody(string(body))}, nil) retryPolicy := RetryPolicy{NbRetries: 3, HttpStatusCodes: []int{http.StatusConflict}, Delay: time.Nanosecond} context := makeStorageContext(transport) context.RetryPolicy = retryPolicy req, err := http.NewRequest("GET", "http://example.com", nil) c.Assert(err, IsNil) resBody, _, err := context.send(req, nil, http.StatusOK) c.Assert(err, IsNil) c.Assert(transport.ExchangeCount, Equals, 3) c.Check(resBody, DeepEquals, body) } type TestComposeCanonicalizedResource struct{} var _ = Suite(&TestComposeCanonicalizedResource{}) func (suite *TestComposeCanonicalizedResource) TestPrependsSlash(c *C) { req, err := http.NewRequest("GET", "http://example.com", nil) c.Assert(err, IsNil) path := MakeRandomString(10) req.URL.Path = path accountName := MakeRandomString(10) observed := composeCanonicalizedResource(req, accountName) expected := "/" + accountName + "/" + path c.Assert(observed, Equals, expected) } func (suite *TestComposeCanonicalizedResource) TestCreatesResource(c *C) { path := MakeRandomString(5) req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/%s", path), nil) c.Assert(err, IsNil) accountName := MakeRandomString(10) observed := composeCanonicalizedResource(req, accountName) expected := "/" + accountName + "/" + path c.Assert(observed, Equals, expected) } func (suite *TestComposeCanonicalizedResource) TestQueryParams(c *C) { req, err := http.NewRequest( "GET", "http://example.com/?Kevin=Perry&foo=bar", nil) c.Assert(err, IsNil) accountName := MakeRandomString(10) observed := composeCanonicalizedResource(req, accountName) expected := "/" + accountName + "/\n" + "foo:bar" + "\n" + "kevin:Perry" c.Assert(observed, Equals, expected) } func (suite *TestComposeCanonicalizedResource) TestToLowerKeys(c *C) { values := url.Values{ "foo": []string{"bar", "baz"}, "alpha": []string{"gamma", "delta"}, "quux": []string{"flobble"}, "BIG": []string{"Big", "Data"}, "big": []string{"Big", "Little"}, } observed := toLowerKeys(values) expected := map[string][]string{ "foo": {"bar", "baz"}, "alpha": {"delta", "gamma"}, "quux": {"flobble"}, "big": {"Big", "Big", "Data", "Little"}, } c.Check(observed, DeepEquals, expected) } func (suite *TestComposeCanonicalizedResource) TestEncodeParams(c *C) { input := map[string][]string{ "foo": {"bar", "baz"}, "alpha": {"delta", "gamma"}, "quux": {"flobble"}, "big": {"Big", "Big", "Data", "Little"}, } observed := encodeParams(input) expected := ("alpha:delta,gamma\nbig:Big,Big,Data,Little\n" + "foo:bar,baz\nquux:flobble") c.Assert(observed, Equals, expected) } func (suite *TestComposeCanonicalizedResource) TestEncodeParamsEmpty(c *C) { input := map[string][]string{} observed := encodeParams(input) expected := "" c.Assert(observed, Equals, expected) } type TestComposeStringToSign struct{} var _ = Suite(&TestComposeStringToSign{}) func (suite *TestComposeStringToSign) TestFullRequest(c *C) { req, err := http.NewRequest( "GET", "http://example.com/mypath?Kevin=Perry&foo=bar", nil) c.Assert(err, IsNil) for i, headerName := range headersToSign { req.Header.Set(headerName, fmt.Sprintf("%v", i)) } req.Header.Set("x-ms-testing", "foo") expected := "GET\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\nx-ms-testing:foo\n/myaccount/mypath\nfoo:bar\nkevin:Perry" observed := composeStringToSign(req, "myaccount") c.Assert(observed, Equals, expected) } type TestSign struct{} var _ = Suite(&TestSign{}) func (suite *TestSign) TestSign(c *C) { key := base64.StdEncoding.EncodeToString([]byte("dummykey")) signable := "a-string-to-sign" observed, err := sign(key, signable) c.Assert(err, IsNil) expected := "5j1DSsm07IEh3u9JQQd0KPwtM6pEGChzrAF7Zf/LxLc=" c.Assert(observed, Equals, expected) } type TestComposeAuthHeader struct{} var _ = Suite(&TestComposeAuthHeader{}) func (suite *TestComposeAuthHeader) TestCreatesHeaderString(c *C) { req, err := http.NewRequest( "GET", "http://example.com/mypath?Kevin=Perry&foo=bar", nil) c.Assert(err, IsNil) key := base64.StdEncoding.EncodeToString([]byte("dummykey")) observed, err := composeAuthHeader(req, "myname", key) c.Assert(err, IsNil) expected := "SharedKey myname:Xf9hWQ99mM0IyEOL6rNeAUdTQlixVqiYnt2TpLCCpY0=" c.Assert(observed, Equals, expected) } type TestSignRequest struct{} var _ = Suite(&TestSignRequest{}) func (suite *TestSignRequest) TestAddsHeaderToRequest(c *C) { req, err := http.NewRequest( "GET", "http://example.com/mypath?Kevin=Perry&foo=bar", nil) c.Assert(err, IsNil) c.Assert(req.Header.Get("Authorization"), Equals, "") key := base64.StdEncoding.EncodeToString([]byte("dummykey")) context := StorageContext{client: nil, Account: "myname", Key: key} context.signRequest(req) expected := "SharedKey myname:Xf9hWQ99mM0IyEOL6rNeAUdTQlixVqiYnt2TpLCCpY0=" c.Assert(req.Header.Get("Authorization"), Equals, expected) } func (suite *TestSignRequest) TestDoesNotAddHeaderIfEmptyKey(c *C) { req, err := http.NewRequest( "GET", "http://example.com/mypath?Kevin=Perry&foo=bar", nil) c.Assert(err, IsNil) c.Assert(req.Header.Get("Authorization"), Equals, "") context := StorageContext{client: nil, Account: "myname", Key: ""} context.signRequest(req) c.Assert(req.Header.Get("Authorization"), Equals, "") } type TestRequestHeaders struct{} var _ = Suite(&TestRequestHeaders{}) func (suite *TestRequestHeaders) TestAddsVersionHeaderToRequest(c *C) { req, err := http.NewRequest("GET", "http://example.com/", nil) c.Assert(err, IsNil) addVersionHeader(req, "2012-02-12") c.Assert(req.Header.Get("x-ms-version"), Equals, "2012-02-12") } func (suite *TestRequestHeaders) TestContentHeader(c *C) { data := "test data" req, err := http.NewRequest("PUT", "http://example.com/", strings.NewReader(data)) c.Assert(err, IsNil) addContentHeaders(req) c.Assert( req.Header.Get("Content-Length"), Equals, fmt.Sprintf("%d", len(data))) // Ensure that reading the request data didn't destroy it. reqdata, _ := ioutil.ReadAll(req.Body) c.Assert(data, Equals, string(reqdata)) } func (suite *TestRequestHeaders) TestLengthHeaderNotSetForGET(c *C) { req, err := http.NewRequest("GET", "http://example.com/", nil) c.Assert(err, IsNil) addContentHeaders(req) _, lengthPresent := req.Header[http.CanonicalHeaderKey("Content-Length")] c.Assert(lengthPresent, Equals, false) } func (suite *TestRequestHeaders) TestContentHeaderWithNoBody(c *C) { req, err := http.NewRequest("PUT", "http://example.com/", nil) c.Assert(err, IsNil) addContentHeaders(req) _, md5Present := req.Header[http.CanonicalHeaderKey("Content-MD5")] c.Check(md5Present, Equals, false) content := req.Header.Get("Content-Length") c.Check(content, Equals, "0") } func (suite *TestRequestHeaders) TestDateHeader(c *C) { req, err := http.NewRequest("GET", "http://example.com/", nil) c.Assert(err, IsNil) c.Assert(req.Header.Get("Date"), Equals, "") addDateHeader(req) observed := req.Header.Get("Date") observedTime, err := time.Parse(time.RFC1123, observed) c.Assert(err, IsNil) difference := time.Now().UTC().Sub(observedTime) if difference.Minutes() > 1.0 { c.FailNow() } } type TestStorageContext struct{} var _ = Suite(&TestStorageContext{}) // makeNastyURLUnfriendlyString returns a string that really needs escaping // before it can be included in a URL. func makeNastyURLUnfriendlyString() string { return MakeRandomString(3) + "?&" + MakeRandomString(3) + "$%" } func (suite *TestStorageContext) TestGetAccountURLCombinesAccountAndEndpoint(c *C) { context := StorageContext{ Account: "myaccount", AzureEndpoint: "http://example.com", } c.Check( context.getAccountURL(), Equals, "http://myaccount.blob.example.com") } func (suite *TestStorageContext) TestGetAccountURLEscapesHostname(c *C) { account := makeNastyURLUnfriendlyString() context := StorageContext{ Account: account, AzureEndpoint: "http://example.com", } c.Check( context.getAccountURL(), Equals, "http://"+url.QueryEscape(account)+".blob.example.com") } func (*TestStorageContext) TestGetAccountURLRequiresEndpoint(c *C) { context := StorageContext{Account: "myaccount"} c.Check( context.getAccountURL, Panics, errors.New("no AzureEndpoint specified in gwacl.StorageContext")) } func (suite *TestStorageContext) TestGetContainerURLAddsContainer(c *C) { account := makeNastyURLUnfriendlyString() container := makeNastyURLUnfriendlyString() context := StorageContext{ Account: account, AzureEndpoint: "http://example.com/", } c.Check( context.getContainerURL(container), Equals, "http://"+url.QueryEscape(account)+".blob.example.com/"+url.QueryEscape(container)) } func (suite *TestStorageContext) TestGetContainerURLAddsSlashIfNeeded(c *C) { context := StorageContext{ Account: "account", AzureEndpoint: "http://example.com", } c.Check( context.getContainerURL("container"), Equals, "http://account.blob.example.com/container") } func (suite *TestStorageContext) TestGetFileURL(c *C) { account := makeNastyURLUnfriendlyString() container := makeNastyURLUnfriendlyString() file := makeNastyURLUnfriendlyString() context := StorageContext{ Account: account, AzureEndpoint: "http://example.com/", } c.Check( context.GetFileURL(container, file), Equals, "http://"+url.QueryEscape(account)+".blob.example.com/"+url.QueryEscape(container)+"/"+url.QueryEscape(file)) } func (suite *TestStorageContext) TestGetSignedFileURL(c *C) { account := "account" container := "container" file := "/a/file" key := base64.StdEncoding.EncodeToString([]byte("dummykey")) context := StorageContext{ Account: account, Key: key, AzureEndpoint: "http://example.com/", } expires := time.Now() signedURL, err := context.GetAnonymousFileURL(container, file, expires) c.Assert(err, IsNil) // The only difference with the non-anon URL is the query string. parsed, err := url.Parse(signedURL) c.Assert(err, IsNil) fileURL, err := url.Parse(context.GetFileURL(container, file)) c.Assert(err, IsNil) c.Check(parsed.Scheme, Equals, fileURL.Scheme) c.Check(parsed.Host, Equals, fileURL.Host) c.Check(parsed.Path, Equals, fileURL.Path) values, err := url.ParseQuery(parsed.RawQuery) c.Assert(err, IsNil) signature := values.Get("sig") readValues, err := getReadBlobAccessValues(container, file, account, key, expires) c.Assert(err, IsNil) expectedSignature := readValues.Get("sig") c.Check(signature, Equals, expectedSignature) } func (suite *TestStorageContext) TestGetClientReturnsDefaultClient(c *C) { context := &StorageContext{client: nil} c.Assert(context.getClient(), Equals, http.DefaultClient) } func (suite *TestStorageContext) TestGetClientReturnsSpecifiedClient(c *C) { context := &StorageContext{client: &http.Client{}} c.Assert(context.getClient(), Not(Equals), http.DefaultClient) c.Assert(context.getClient(), Equals, context.client) } type TestListContainers struct{} var _ = Suite(&TestListContainers{}) // The ListContainers Storage API call returns a ContainerEnumerationResults // struct on success. func (suite *TestListContainers) Test(c *C) { responseBody := ` <?xml version="1.0" encoding="utf-8"?> <EnumerationResults AccountName="https://myaccount.blob.core.windows.net"> <Prefix>prefix-value</Prefix> <Marker>marker-value</Marker> <MaxResults>max-results-value</MaxResults> <Containers> <Container> <Name>name-value</Name> <URL>url-value</URL> <Properties> <Last-Modified>date/time-value</Last-Modified> <Etag>etag-value</Etag> <LeaseStatus>lease-status-value</LeaseStatus> <LeaseState>lease-state-value</LeaseState> <LeaseDuration>lease-duration-value</LeaseDuration> </Properties> <Metadata> <metadata-name>metadata-value</metadata-name> </Metadata> </Container> </Containers> <NextMarker/> </EnumerationResults>` response := makeHttpResponse(http.StatusOK, responseBody) transport := &TestTransport{Response: response} context := makeStorageContext(transport) context.AzureEndpoint = "http://example.com/" request := &ListContainersRequest{Marker: ""} results, err := context.ListContainers(request) c.Assert(err, IsNil) c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf( "http://%s.blob.example.com/?comp=list", context.Account)) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") c.Assert(results, NotNil) c.Assert(results.Containers[0].Name, Equals, "name-value") } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestListContainers) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) request := &ListContainersRequest{Marker: ""} _, err := context.ListContainers(request) c.Assert(err, NotNil) } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestListContainers) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) request := &ListContainersRequest{Marker: ""} _, err := context.ListContainers(request) serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) } func (suite *TestListContainers) TestListContainersBatchPassesMarker(c *C) { transport := &MockingTransport{} transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil) context := makeStorageContext(transport) // Call ListContainers. This will fail because of the empty // response, but no matter. We only care about the request. request := &ListContainersRequest{Marker: "thismarkerhere"} _, err := context.ListContainers(request) c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*") c.Assert(transport.ExchangeCount, Equals, 1) query := transport.Exchanges[0].Request.URL.RawQuery values, err := url.ParseQuery(query) c.Assert(err, IsNil) c.Check(values["marker"], DeepEquals, []string{"thismarkerhere"}) } func (suite *TestListContainers) TestListContainersBatchDoesNotPassEmptyMarker(c *C) { transport := &MockingTransport{} transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil) context := makeStorageContext(transport) // The error is OK. We only care about the request. request := &ListContainersRequest{Marker: ""} _, err := context.ListContainers(request) c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*") c.Assert(transport.ExchangeCount, Equals, 1) query := transport.Exchanges[0].Request.URL.RawQuery values, err := url.ParseQuery(query) c.Assert(err, IsNil) marker, present := values["marker"] c.Check(present, Equals, false) c.Check(marker, DeepEquals, []string(nil)) } func (suite *TestListContainers) TestListContainersBatchEscapesMarker(c *C) { transport := &MockingTransport{} transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil) context := makeStorageContext(transport) // The error is OK. We only care about the request. request := &ListContainersRequest{Marker: "x&y"} _, err := context.ListContainers(request) c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*") c.Assert(transport.ExchangeCount, Equals, 1) query := transport.Exchanges[0].Request.URL.RawQuery values, err := url.ParseQuery(query) c.Assert(err, IsNil) c.Check(values["marker"], DeepEquals, []string{"x&y"}) } type TestListBlobs struct{} var _ = Suite(&TestListBlobs{}) // The ListBlobs Storage API call returns a BlobEnumerationResults struct on // success. func (suite *TestListBlobs) Test(c *C) { responseBody := ` <?xml version="1.0" encoding="utf-8"?> <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer"> <Prefix>prefix</Prefix> <Marker>marker</Marker> <MaxResults>maxresults</MaxResults> <Delimiter>delimiter</Delimiter> <Blobs> <Blob> <Name>blob-name</Name> <Snapshot>snapshot-date-time</Snapshot> <Url>blob-address</Url> <Properties> <Last-Modified>last-modified</Last-Modified> <Etag>etag</Etag> <Content-Length>size-in-bytes</Content-Length> <Content-Type>blob-content-type</Content-Type> <Content-Encoding /> <Content-Language /> <Content-MD5 /> <Cache-Control /> <x-ms-blob-sequence-number>sequence-number</x-ms-blob-sequence-number> <BlobType>blobtype</BlobType> <LeaseStatus>leasestatus</LeaseStatus> <LeaseState>leasestate</LeaseState> <LeaseDuration>leasesduration</LeaseDuration> <CopyId>id</CopyId> <CopyStatus>copystatus</CopyStatus> <CopySource>copysource</CopySource> <CopyProgress>copyprogress</CopyProgress> <CopyCompletionTime>copycompletiontime</CopyCompletionTime> <CopyStatusDescription>copydesc</CopyStatusDescription> </Properties> <Metadata> <MetaName1>metadataname1</MetaName1> <MetaName2>metadataname2</MetaName2> </Metadata> </Blob> <BlobPrefix> <Name>blob-prefix</Name> </BlobPrefix> </Blobs> <NextMarker /> </EnumerationResults>` response := makeHttpResponse(http.StatusOK, responseBody) transport := &TestTransport{Response: response} context := makeStorageContext(transport) request := &ListBlobsRequest{Container: "container"} results, err := context.ListBlobs(request) c.Assert(err, IsNil) c.Check(transport.Request.URL.String(), Matches, context.getContainerURL("container")+"?.*") c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{ "restype": {"container"}, "comp": {"list"}, }) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") c.Assert(results, NotNil) } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestListBlobs) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) request := &ListBlobsRequest{Container: "container"} _, err := context.ListBlobs(request) c.Assert(err, NotNil) } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestListBlobs) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) request := &ListBlobsRequest{Container: "container"} _, err := context.ListBlobs(request) serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) } func (suite *TestListBlobs) TestListBlobsPassesMarker(c *C) { transport := &MockingTransport{} transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil) context := makeStorageContext(transport) // Call ListBlobs. This will fail because of the empty // response, but no matter. We only care about the request. request := &ListBlobsRequest{Container: "mycontainer", Marker: "thismarkerhere"} _, err := context.ListBlobs(request) c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*") c.Assert(transport.ExchangeCount, Equals, 1) query := transport.Exchanges[0].Request.URL.RawQuery values, err := url.ParseQuery(query) c.Assert(err, IsNil) c.Check(values["marker"], DeepEquals, []string{"thismarkerhere"}) } func (suite *TestListBlobs) TestListBlobsDoesNotPassEmptyMarker(c *C) { transport := &MockingTransport{} transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil) context := makeStorageContext(transport) // The error is OK. We only care about the request. request := &ListBlobsRequest{Container: "mycontainer"} _, err := context.ListBlobs(request) c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*") c.Assert(transport.ExchangeCount, Equals, 1) query := transport.Exchanges[0].Request.URL.RawQuery values, err := url.ParseQuery(query) c.Assert(err, IsNil) marker, present := values["marker"] c.Check(present, Equals, false) c.Check(marker, DeepEquals, []string(nil)) } func (suite *TestListBlobs) TestListBlobsPassesPrefix(c *C) { transport := &MockingTransport{} transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil) context := makeStorageContext(transport) // Call ListBlobs. This will fail because of the empty // response, but no matter. We only care about the request. request := &ListBlobsRequest{Container: "mycontainer", Prefix: "thisprefixhere"} _, err := context.ListBlobs(request) c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*") c.Assert(transport.ExchangeCount, Equals, 1) query := transport.Exchanges[0].Request.URL.RawQuery values, err := url.ParseQuery(query) c.Assert(err, IsNil) c.Check(values["prefix"], DeepEquals, []string{"thisprefixhere"}) } func (suite *TestListBlobs) TestListBlobsDoesNotPassEmptyPrefix(c *C) { transport := &MockingTransport{} transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil) context := makeStorageContext(transport) // The error is OK. We only care about the request. request := &ListBlobsRequest{Container: "mycontainer"} _, err := context.ListBlobs(request) c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*") c.Assert(transport.ExchangeCount, Equals, 1) query := transport.Exchanges[0].Request.URL.RawQuery values, err := url.ParseQuery(query) c.Assert(err, IsNil) prefix, present := values["prefix"] c.Check(present, Equals, false) c.Check(prefix, DeepEquals, []string(nil)) } type TestCreateContainer struct{} var _ = Suite(&TestCreateContainer{}) // The CreateContainer Storage API call returns without error when the // container has been created successfully. func (suite *TestCreateContainer) Test(c *C) { response := makeHttpResponse(http.StatusCreated, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) context.AzureEndpoint = "http://example.com/" containerName := MakeRandomString(10) err := context.CreateContainer(containerName) c.Assert(err, IsNil) c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf( "http://%s.blob.example.com/%s?restype=container", context.Account, containerName)) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestCreateContainer) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) err := context.CreateContainer("container") c.Assert(err, NotNil) } // Server-side errors are propagated back to the caller. func (suite *TestCreateContainer) TestErrorResponse(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) err := context.CreateContainer("container") c.Assert(err, NotNil) } // Server-side errors are propagated back to the caller. func (suite *TestCreateContainer) TestNotCreatedResponse(c *C) { response := makeHttpResponse(http.StatusOK, "") context := makeStorageContext(&TestTransport{Response: response}) err := context.CreateContainer("container") c.Assert(err, NotNil) } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestCreateContainer) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) err := context.CreateContainer("container") serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) } type TestDeleteContainer struct{} var _ = Suite(&TestDeleteContainer{}) // The DeleteContainer Storage API call returns without error when the // container has been created successfully. func (suite *TestDeleteContainer) Test(c *C) { response := makeHttpResponse(http.StatusAccepted, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) context.AzureEndpoint = "http://example.com/" containerName := MakeRandomString(10) err := context.DeleteContainer(containerName) c.Assert(err, IsNil) c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf( "http://%s.blob.example.com/%s?restype=container", context.Account, containerName)) c.Check(transport.Request.Method, Equals, "DELETE") c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestDeleteContainer) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) err := context.DeleteContainer("container") c.Assert(err, ErrorMatches, ".*canned-error.*") } // Server-side errors are propagated back to the caller. func (suite *TestDeleteContainer) TestNotCreatedResponse(c *C) { response := makeHttpResponse(http.StatusOK, "") context := makeStorageContext(&TestTransport{Response: response}) err := context.DeleteContainer("container") c.Assert(err, ErrorMatches, ".*Azure request failed.*") } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestDeleteContainer) TestServerError(c *C) { response := makeHttpResponse(http.StatusMethodNotAllowed, "not allowed") context := makeStorageContext(&TestTransport{Response: response}) err := context.DeleteContainer("container") serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusMethodNotAllowed) } func (suite *TestDeleteContainer) TestDeleteNotExistentContainerDoesNotFail(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) err := context.DeleteContainer("container") c.Assert(err, IsNil) } type TestGetContainerProperties struct{} var _ = Suite(&TestGetContainerProperties{}) // The GetContainerProperties Storage API call returns without error when the // container has been created successfully. func (suite *TestGetContainerProperties) Test(c *C) { header := make(http.Header) header.Add("Last-Modified", "last-modified") header.Add("ETag", "etag") header.Add("X-Ms-Lease-Status", "status") header.Add("X-Ms-Lease-State", "state") header.Add("X-Ms-Lease-Duration", "duration") response := &http.Response{ Status: fmt.Sprintf("%d", http.StatusOK), StatusCode: http.StatusOK, Body: makeResponseBody(""), Header: header, } transport := &TestTransport{Response: response} context := makeStorageContext(transport) context.AzureEndpoint = "http://example.com/" containerName := MakeRandomString(10) props, err := context.GetContainerProperties(containerName) c.Assert(err, IsNil) c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf( "http://%s.blob.example.com/%s?restype=container", context.Account, containerName)) c.Check(transport.Request.Method, Equals, "GET") c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") c.Check(props.LastModified, Equals, "last-modified") c.Check(props.ETag, Equals, "etag") c.Check(props.LeaseStatus, Equals, "status") c.Check(props.LeaseState, Equals, "state") c.Check(props.LeaseDuration, Equals, "duration") } func (suite *TestGetContainerProperties) TestWithoutAllHeaders(c *C) { response := &http.Response{ Status: fmt.Sprintf("%d", http.StatusOK), StatusCode: http.StatusOK, Body: makeResponseBody(""), } transport := &TestTransport{Response: response} context := makeStorageContext(transport) containerName := MakeRandomString(10) props, err := context.GetContainerProperties(containerName) c.Assert(err, IsNil) c.Check(props.LastModified, Equals, "") c.Check(props.ETag, Equals, "") c.Check(props.LeaseStatus, Equals, "") c.Check(props.LeaseState, Equals, "") c.Check(props.LeaseDuration, Equals, "") } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestGetContainerProperties) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) _, err := context.GetContainerProperties("container") c.Assert(err, ErrorMatches, ".*canned-error.*") } // Server-side errors are propagated back to the caller. func (suite *TestGetContainerProperties) TestErrorResponse(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) _, err := context.GetContainerProperties("container") c.Assert(err, ErrorMatches, ".*Not Found.*") } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestGetContainerProperties) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) _, err := context.GetContainerProperties("container") serverError, ok := err.(*ServerError) c.Assert(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) } type TestPutPage struct{} var _ = Suite(&TestPutPage{}) // Basic happy path testing. func (suite *TestPutPage) TestHappyPath(c *C) { response := makeHttpResponse(http.StatusCreated, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) randomData := MakeRandomByteSlice(10) dataReader := bytes.NewReader(randomData) err := context.PutPage(&PutPageRequest{ Container: "container", Filename: "filename", StartRange: 0, EndRange: 511, Data: dataReader}) c.Assert(err, IsNil) // Ensure that container was set right. c.Check(transport.Request.URL.String(), Matches, context.GetFileURL("container", "filename")+"?.*") // Ensure that the Authorization header is set. c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") // Check the range is set. c.Check(transport.Request.Header.Get("x-ms-range"), Equals, "bytes=0-511") // Check special page write header. c.Check(transport.Request.Header.Get("x-ms-page-write"), Equals, "update") // "?comp=page" should be part of the URL. c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{ "comp": {"page"}, }) // Check the data payload. data, err := ioutil.ReadAll(transport.Request.Body) c.Assert(err, IsNil) c.Check(data, DeepEquals, randomData) } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestPutPage) TestError(c *C) { cannedError := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: cannedError}) err := context.PutPage(&PutPageRequest{ Container: "container", Filename: "filename", StartRange: 0, EndRange: 511, Data: nil}) c.Assert(err, NotNil) } // Server-side errors are propagated back to the caller. func (suite *TestPutPage) TestErrorResponse(c *C) { responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>" response := makeHttpResponse(102, responseBody) context := makeStorageContext(&TestTransport{Response: response}) err := context.PutPage(&PutPageRequest{ Container: "container", Filename: "filename", StartRange: 0, EndRange: 511, Data: nil}) c.Assert(err, NotNil) c.Check(err, ErrorMatches, ".*102.*") c.Check(err, ErrorMatches, ".*Frotzed.*") c.Check(err, ErrorMatches, ".*failed to put blob.*") } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestPutPage) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) err := context.PutPage(&PutPageRequest{ Container: "container", Filename: "filename", StartRange: 0, EndRange: 511, Data: nil}) serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) } // Range values outside the limits should get rejected. func (suite *TestPutPage) TestRangeLimits(c *C) { context := makeStorageContext(&TestTransport{}) err := context.PutPage(&PutPageRequest{ StartRange: 513, EndRange: 555}) c.Assert(err, NotNil) c.Check(err, ErrorMatches, ".*StartRange must be a multiple of 512, EndRange must be one less than a multiple of 512.*") } type TestPutBlob struct{} var _ = Suite(&TestPutBlob{}) // Test basic PutBlob happy path functionality. func (suite *TestPutBlob) TestPutBlockBlob(c *C) { response := makeHttpResponse(http.StatusCreated, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) err := context.PutBlob(&PutBlobRequest{ Container: "container", BlobType: "block", Filename: "blobname"}) c.Assert(err, IsNil) // Ensure that container was set right. c.Check(transport.Request.URL.String(), Equals, context.GetFileURL("container", "blobname")) // Ensure that the Authorization header is set. c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") // The blob type should be a block. c.Check(transport.Request.Header.Get("x-ms-blob-type"), Equals, "BlockBlob") } // PutBlob should set x-ms-blob-type to PageBlob for Page Blobs. func (suite *TestPutBlob) TestPutPageBlob(c *C) { response := makeHttpResponse(http.StatusCreated, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) err := context.PutBlob(&PutBlobRequest{ Container: "container", BlobType: "page", Filename: "blobname", Size: 512}) c.Assert(err, IsNil) c.Check(transport.Request.Header.Get("x-ms-blob-type"), Equals, "PageBlob") c.Check(transport.Request.Header.Get("x-ms-blob-content-length"), Equals, "512") } // PutBlob for a page should return an error when Size is not specified. func (suite *TestPutBlob) TestPutPageBlobWithSizeOmitted(c *C) { context := makeStorageContext(&TestTransport{}) err := context.PutBlob(&PutBlobRequest{ Container: "container", BlobType: "page", Filename: "blob"}) c.Assert(err, ErrorMatches, "Must supply a size for a page blob") } // PutBlob for a page should return an error when Size is not a multiple // of 512 bytes. func (suite *TestPutBlob) TestPutPageBlobWithInvalidSiuze(c *C) { context := makeStorageContext(&TestTransport{}) err := context.PutBlob(&PutBlobRequest{ Container: "container", BlobType: "page", Filename: "blob", Size: 1015}) c.Assert(err, ErrorMatches, "Size must be a multiple of 512 bytes") } // Passing a BlobType other than page or block results in a panic. func (suite *TestPutBlob) TestBlobType(c *C) { defer func() { err := recover() c.Assert(err, Equals, "blockType must be 'page' or 'block'") }() context := makeStorageContext(&TestTransport{}) context.PutBlob(&PutBlobRequest{ Container: "container", BlobType: "invalid-blob-type", Filename: "blobname"}) c.Assert("This should have panicked", Equals, "But it didn't.") } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestPutBlob) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) err := context.PutBlob(&PutBlobRequest{ Container: "container", BlobType: "block", Filename: "blobname"}) c.Assert(err, NotNil) } // Server-side errors are propagated back to the caller. func (suite *TestPutBlob) TestErrorResponse(c *C) { responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>" response := makeHttpResponse(102, responseBody) context := makeStorageContext(&TestTransport{Response: response}) err := context.PutBlob(&PutBlobRequest{ Container: "container", BlobType: "block", Filename: "blobname"}) c.Assert(err, NotNil) c.Check(err, ErrorMatches, ".*102.*") c.Check(err, ErrorMatches, ".*Frotzed.*") c.Check(err, ErrorMatches, ".*failed to put blob.*") } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestPutBlob) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) err := context.PutBlob(&PutBlobRequest{ Container: "container", BlobType: "block", Filename: "blobname"}) serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) } type TestPutBlock struct{} var _ = Suite(&TestPutBlock{}) func (suite *TestPutBlock) Test(c *C) { response := makeHttpResponse(http.StatusCreated, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) blockid := "\x1b\xea\xf7Mv\xb5\xddH\xebm" randomData := MakeRandomByteSlice(10) dataReader := bytes.NewReader(randomData) err := context.PutBlock("container", "blobname", blockid, dataReader) c.Assert(err, IsNil) // The blockid should have been base64 encoded and url escaped. base64ID := base64.StdEncoding.EncodeToString([]byte(blockid)) c.Check(transport.Request.URL.String(), Matches, context.GetFileURL("container", "blobname")+"?.*") c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{ "comp": {"block"}, "blockid": {base64ID}, }) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") data, err := ioutil.ReadAll(transport.Request.Body) c.Assert(err, IsNil) c.Check(data, DeepEquals, randomData) } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestPutBlock) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) dataReader := bytes.NewReader(MakeRandomByteSlice(10)) err := context.PutBlock("container", "blobname", "blockid", dataReader) c.Assert(err, NotNil) } // Server-side errors are propagated back to the caller. func (suite *TestPutBlock) TestErrorResponse(c *C) { responseBody := "<Error><Code>Frotzed</Code><Message>failed to put block</Message></Error>" response := makeHttpResponse(102, responseBody) context := makeStorageContext(&TestTransport{Response: response}) dataReader := bytes.NewReader(MakeRandomByteSlice(10)) err := context.PutBlock("container", "blobname", "blockid", dataReader) c.Assert(err, NotNil) c.Check(err, ErrorMatches, ".*102.*") c.Check(err, ErrorMatches, ".*Frotzed.*") c.Check(err, ErrorMatches, ".*failed to put block.*") } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestPutBlock) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) dataReader := bytes.NewReader(MakeRandomByteSlice(10)) err := context.PutBlock("container", "blobname", "blockid", dataReader) serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) } type TestPutBlockList struct{} var _ = Suite(&TestPutBlockList{}) func (suite *TestPutBlockList) Test(c *C) { response := makeHttpResponse(http.StatusCreated, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) context.AzureEndpoint = "http://example.com/" blocklist := &BlockList{} blocklist.Add(BlockListLatest, "b1") blocklist.Add(BlockListLatest, "b2") err := context.PutBlockList("container", "blobname", blocklist) c.Assert(err, IsNil) c.Check(transport.Request.Method, Equals, "PUT") c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf( "http://%s.blob.example.com/container/blobname?comp=blocklist", context.Account)) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") data, err := ioutil.ReadAll(transport.Request.Body) c.Assert(err, IsNil) expected := dedent.Dedent(` <BlockList> <Latest>YjE=</Latest> <Latest>YjI=</Latest> </BlockList>`) c.Check(strings.TrimSpace(string(data)), Equals, strings.TrimSpace(expected)) } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestPutBlockList) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) blocklist := &BlockList{} err := context.PutBlockList("container", "blobname", blocklist) c.Assert(err, NotNil) } // Server-side errors are propagated back to the caller. func (suite *TestPutBlockList) TestErrorResponse(c *C) { responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blocklist</Message></Error>" response := makeHttpResponse(102, responseBody) context := makeStorageContext(&TestTransport{Response: response}) blocklist := &BlockList{} err := context.PutBlockList("container", "blobname", blocklist) c.Assert(err, NotNil) c.Check(err, ErrorMatches, ".*102.*") c.Check(err, ErrorMatches, ".*Frotzed.*") c.Check(err, ErrorMatches, ".*failed to put blocklist.*") } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestPutBlockList) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) blocklist := &BlockList{} err := context.PutBlockList("container", "blobname", blocklist) serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) } type TestGetBlockList struct{} var _ = Suite(&TestGetBlockList{}) // The GetBlockList Storage API call returns a GetBlockList struct on // success. func (suite *TestGetBlockList) Test(c *C) { responseBody := ` <?xml version="1.0" encoding="utf-8"?> <BlockList> <CommittedBlocks> <Block> <Name>BlockId001</Name> <Size>4194304</Size> </Block> </CommittedBlocks> <UncommittedBlocks> <Block> <Name>BlockId002</Name> <Size>1024</Size> </Block> </UncommittedBlocks> </BlockList>` response := makeHttpResponse(http.StatusOK, responseBody) transport := &TestTransport{Response: response} context := makeStorageContext(transport) results, err := context.GetBlockList("container", "myfilename") c.Assert(err, IsNil) c.Check(transport.Request.URL.String(), Matches, context.GetFileURL("container", "myfilename")+"?.*") c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{ "comp": {"blocklist"}, "blocklisttype": {"all"}, }) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") c.Assert(results, NotNil) } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestGetBlockList) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) _, err := context.GetBlockList("container", "myfilename") c.Assert(err, NotNil) } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestGetBlockList) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) _, err := context.GetBlockList("container", "blobname") serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) } type TestDeleteBlob struct{} var _ = Suite(&TestDeleteBlob{}) func (suite *TestDeleteBlob) Test(c *C) { response := makeHttpResponse(http.StatusAccepted, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) err := context.DeleteBlob("container", "blobname") c.Assert(err, IsNil) c.Check(transport.Request.Method, Equals, "DELETE") c.Check(transport.Request.URL.String(), Equals, context.GetFileURL("container", "blobname")) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") c.Check(transport.Request.Body, IsNil) } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestDeleteBlob) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) err := context.DeleteBlob("container", "blobname") c.Assert(err, NotNil) } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestDeleteBlob) TestServerError(c *C) { // We're not using http.StatusNotFound for the test here because // 404 errors are handled in a special way by DeleteBlob(). See the test // TestDeleteNotExistentBlobDoesNotFail. response := makeHttpResponse(http.StatusMethodNotAllowed, "not allowed") context := makeStorageContext(&TestTransport{Response: response}) err := context.DeleteBlob("container", "blobname") serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusMethodNotAllowed) } func (suite *TestDeleteBlob) TestDeleteNotExistentBlobDoesNotFail(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) err := context.DeleteBlob("container", "blobname") c.Assert(err, IsNil) } // Server-side errors are propagated back to the caller. func (suite *TestDeleteBlob) TestErrorResponse(c *C) { responseBody := "<Error><Code>Frotzed</Code><Message>failed to delete blob</Message></Error>" response := makeHttpResponse(146, responseBody) context := makeStorageContext(&TestTransport{Response: response}) err := context.DeleteBlob("container", "blobname") c.Assert(err, NotNil) c.Check(err, ErrorMatches, ".*146.*") c.Check(err, ErrorMatches, ".*Frotzed.*") c.Check(err, ErrorMatches, ".*failed to delete blob.*") } type TestGetBlob struct{} var _ = Suite(&TestGetBlob{}) func (suite *TestGetBlob) Test(c *C) { responseBody := "blob-in-a-can" response := makeHttpResponse(http.StatusOK, responseBody) transport := &TestTransport{Response: response} context := makeStorageContext(transport) reader, err := context.GetBlob("container", "blobname") c.Assert(err, IsNil) c.Assert(reader, NotNil) defer reader.Close() c.Check(transport.Request.Method, Equals, "GET") c.Check(transport.Request.URL.String(), Equals, context.GetFileURL("container", "blobname")) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") data, err := ioutil.ReadAll(reader) c.Assert(err, IsNil) c.Check(string(data), Equals, responseBody) } // Client-side errors from the HTTP client are propagated back to the caller. func (suite *TestGetBlob) TestError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) reader, err := context.GetBlob("container", "blobname") c.Check(reader, IsNil) c.Assert(err, NotNil) } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestGetBlob) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) reader, err := context.GetBlob("container", "blobname") c.Check(reader, IsNil) c.Assert(err, NotNil) serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) c.Check(IsNotFoundError(err), Equals, true) } // Server-side errors are propagated back to the caller. func (suite *TestGetBlob) TestErrorResponse(c *C) { response := &http.Response{ Status: "246 Frotzed", StatusCode: 246, Body: makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to get blob</Message></Error>"), } context := makeStorageContext(&TestTransport{Response: response}) reader, err := context.GetBlob("container", "blobname") c.Check(reader, IsNil) c.Assert(err, NotNil) c.Check(err, ErrorMatches, ".*246.*") c.Check(err, ErrorMatches, ".*Frotzed.*") c.Check(err, ErrorMatches, ".*failed to get blob.*") } type TestSetContainerACL struct{} var _ = Suite(&TestSetContainerACL{}) func (suite *TestSetContainerACL) TestHappyPath(c *C) { response := makeHttpResponse(http.StatusOK, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) context.AzureEndpoint = "http://example.com/" err := context.SetContainerACL(&SetContainerACLRequest{ Container: "mycontainer", Access: "container"}) c.Assert(err, IsNil) c.Check(transport.Request.Method, Equals, "PUT") c.Check(transport.Request.URL.String(), Matches, fmt.Sprintf( "http://%s.blob.example.com/mycontainer?.*", context.Account)) c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{ "comp": {"acl"}, "restype": {"container"}, }) c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "") c.Check(transport.Request.Header.Get("x-ms-blob-public-access"), Equals, "container") } func (suite *TestSetContainerACL) TestAcceptsBlobAccess(c *C) { response := makeHttpResponse(http.StatusOK, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) err := context.SetContainerACL(&SetContainerACLRequest{ Container: "mycontainer", Access: "blob"}) c.Assert(err, IsNil) c.Check(transport.Request.Header.Get("x-ms-blob-public-access"), Equals, "blob") } func (suite *TestSetContainerACL) TestAccessHeaderOmittedWhenPrivate(c *C) { response := makeHttpResponse(http.StatusOK, "") transport := &TestTransport{Response: response} context := makeStorageContext(transport) err := context.SetContainerACL(&SetContainerACLRequest{ Container: "mycontainer", Access: "private"}) c.Assert(err, IsNil) c.Check(transport.Request.Header.Get("x-ms-blob-public-access"), Equals, "") } func (suite *TestSetContainerACL) TestInvalidAccessTypePanics(c *C) { defer func() { err := recover() c.Assert(err, Equals, "Access must be one of 'container', 'blob' or 'private'") }() context := makeStorageContext(&TestTransport{}) context.SetContainerACL(&SetContainerACLRequest{ Container: "mycontainer", Access: "thisisnotvalid"}) c.Assert("This test failed", Equals, "because there was no panic") } func (suite *TestSetContainerACL) TestClientSideError(c *C) { error := fmt.Errorf("canned-error") context := makeStorageContext(&TestTransport{Error: error}) err := context.SetContainerACL(&SetContainerACLRequest{ Container: "mycontainer", Access: "private"}) c.Assert(err, NotNil) } // Azure HTTP errors (for instance 404 responses) are propagated back to // the caller as ServerError objects. func (suite *TestSetContainerACL) TestServerError(c *C) { response := makeHttpResponse(http.StatusNotFound, "not found") context := makeStorageContext(&TestTransport{Response: response}) err := context.SetContainerACL(&SetContainerACLRequest{ Container: "mycontainer", Access: "private"}) c.Assert(err, NotNil) serverError, ok := err.(*ServerError) c.Check(ok, Equals, true) c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound) c.Check(IsNotFoundError(err), Equals, true) } �������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/shared_signature.go����������������������������������������0000644�0000153�0000161�00000005705�12321735761�023746� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" "net/url" "time" ) // sharedSignatureParams is a object which encapsulate all the parameters // required to delegate access to a Windows Azure object using the // "Shared Access Signature" mechanism. type sharedSignatureParams struct { permission string signedStart string signedExpiry string path string accountName string signedIdentifier string signedVersion string signedRessource string accountKey string } // composeSharedSignature composes the "Shared Access Signature" as described // in the paragraph "Constructing the Signature String" in // http://msdn.microsoft.com/en-us/library/windowsazure/dn140255.aspx func (params *sharedSignatureParams) composeSharedSignature() (string, error) { // Compose the string to sign. canonicalizedResource := fmt.Sprintf("/%s%s", params.accountName, params.path) stringToSign := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", params.permission, params.signedStart, params.signedExpiry, canonicalizedResource, params.signedIdentifier, params.signedVersion) // Create the signature. signature, err := sign(params.accountKey, stringToSign) if err != nil { return "", err } return signature, nil } // composeAccessQueryValues returns the values that correspond to the query // string used to build a Shared Access Signature URI as described in // http://msdn.microsoft.com/en-us/library/windowsazure/dn140255.aspx func (params *sharedSignatureParams) composeAccessQueryValues() (url.Values, error) { signature, err := params.composeSharedSignature() if err != nil { return nil, err } // Compose the "Shared Access Signature" query string. values := url.Values{} values.Set("sv", params.signedVersion) values.Set("se", params.signedExpiry) values.Set("sr", params.signedRessource) values.Set("sp", params.permission) values.Set("sig", signature) return values, nil } // getReadBlobAccessValues returns the values that correspond to the query // string used to build a Shared Access Signature URI. The signature grants // read access to the given blob. func getReadBlobAccessValues(container, filename, accountName, accountKey string, expires time.Time) (url.Values, error) { expiryDateString := expires.UTC().Format(time.RFC3339) path := fmt.Sprintf("/%s/%s", container, filename) signatureParams := &sharedSignatureParams{ permission: "r", signedExpiry: expiryDateString, signedStart: "", path: path, accountName: accountName, signedIdentifier: "", signedVersion: "2012-02-12", signedRessource: "b", accountKey: accountKey, } return signatureParams.composeAccessQueryValues() } �����������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/httperror.go�����������������������������������������������0000644�0000153�0000161�00000010226�12321735761�022442� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gwacl import ( "encoding/xml" "errors" "fmt" "net/http" ) // HTTPError is an extended version of the standard "error" interface. It // adds an HTTP status code. type HTTPError interface { error StatusCode() int } // HTTPStatus is an HTTP status code. type HTTPStatus int // Status returns the HTTP status code as an int. func (s HTTPStatus) StatusCode() int { return int(s) } // AzureError is an HTTPError returned by the Azure API. It contains an // error message, and an Azure-defined error code. type AzureError struct { error `xml:"-"` HTTPStatus `xml:"-"` Code string `xml:"Code"` Message string `xml:"Message"` } // *AzureError implements HTTPError. var _ HTTPError = &AzureError{} func (e *AzureError) Error() string { description := e.error.Error() status := e.StatusCode() name := http.StatusText(status) return fmt.Sprintf("%s: %s - %s (http code %d: %s)", description, e.Code, e.Message, status, name) } // ServerError is a generic HTTPError, without any further helpful information // from the server that we can count on. type ServerError struct { error HTTPStatus } // *ServerError implements HTTPError. var _ HTTPError = &ServerError{} func (e *ServerError) Error() string { description := e.error.Error() status := e.StatusCode() name := http.StatusText(status) return fmt.Sprintf("%s (%d: %s)", description, status, name) } // newHTTPError returns the appropriate HTTPError implementation for a given // HTTP response. // It takes a status code and response body, rather than just a standard // http.Response object. func newHTTPError(status int, body []byte, description string) HTTPError { httpStatus := HTTPStatus(status) baseErr := errors.New(description) azureError := AzureError{error: baseErr, HTTPStatus: httpStatus} err := xml.Unmarshal(body, &azureError) if err != nil { // It's OK if the response body wasn't actually XML... That just means // it wasn't a proper AzureError. We have another error type for that. return &ServerError{error: baseErr, HTTPStatus: httpStatus} } return &azureError } // newAzureErrorFromOperation composes an HTTPError based on an Operation // struct, i.e. the result of an asynchronous operation. func newAzureErrorFromOperation(outcome *Operation) *AzureError { if outcome.Status != FailedOperationStatus { msg := fmt.Errorf("interpreting Azure %s as an asynchronous failure", outcome.Status) panic(msg) } return &AzureError{ error: errors.New("asynchronous operation failed"), HTTPStatus: HTTPStatus(outcome.HTTPStatusCode), Code: outcome.ErrorCode, Message: outcome.ErrorMessage, } } // extendError returns an error whos description is the concatenation of // the given message plus the error string from the original error. // It preserves the value of the error types it knows about (currently only // ServerError). // // The main purpose of this method is to offer a unified way to // extend the information present in errors while still not losing the // additioning information present on specific errors gwacl knows out to extend // in a more meaningful way. func extendError(err error, message string) error { switch err := err.(type) { case *ServerError: extendedError := *err extendedError.error = fmt.Errorf(message+"%v", err.error) return &extendedError case *AzureError: extendedError := *err extendedError.error = fmt.Errorf(message+"%v", err.error) return &extendedError default: return fmt.Errorf(message+"%v", err) } // This code cannot be reached but Go insists on having a return or a panic // statement at the end of this method. Sigh. panic("invalid extendError state!") } // IsNotFoundError returns whether or not the given error is an error (as // returned by a gwacl method) which corresponds to a 'Not Found' error // returned by Windows Azure. func IsNotFoundError(err error) bool { httpError, ok := err.(HTTPError) if ok { return httpError.StatusCode() == http.StatusNotFound } return false } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/httperror_test.go������������������������������������������0000644�0000153�0000161�00000012164�12321735761�023504� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package gwacl import ( "errors" "fmt" . "launchpad.net/gocheck" "net/http" ) type httpErrorSuite struct{} var _ = Suite(&httpErrorSuite{}) func (suite *httpErrorSuite) TestNewHTTPErrorParsesAzureError(c *C) { description := "upload failed" status := 415 code := "CannotUpload" message := "Unknown data format" xml := fmt.Sprintf(`<Error> <Code>%s</Code> <Message>%s</Message> </Error>`, code, message) httpErr := newHTTPError(status, []byte(xml), description) azureErr, ok := httpErr.(*AzureError) c.Assert(ok, Equals, true) c.Check(azureErr.StatusCode(), Equals, status) c.Check(azureErr.Code, Equals, code) c.Check(azureErr.Message, Equals, message) c.Check(httpErr, ErrorMatches, ".*"+description+".*") c.Check(httpErr, ErrorMatches, ".*415: Unsupported Media Type.*") } func (suite *httpErrorSuite) TestNewHTTPErrorResortsToServerError(c *C) { description := "could not talk to server" status := 505 httpErr := newHTTPError(status, []byte{}, description) _, ok := httpErr.(*ServerError) c.Assert(ok, Equals, true) c.Check(httpErr.StatusCode(), Equals, status) c.Check(httpErr, ErrorMatches, ".*505: HTTP Version Not Supported.*") c.Check(httpErr, ErrorMatches, ".*"+description+".*") } func (suite *httpErrorSuite) TestAzureErrorComposesError(c *C) { description := "something failed" status := 410 httpErr := AzureError{ error: errors.New(description), HTTPStatus: HTTPStatus(status), Code: "MissingError", Message: "Your object has disappeared", } c.Check(httpErr.Error(), Equals, "something failed: MissingError - Your object has disappeared (http code 410: Gone)") } func (suite *httpErrorSuite) TestServerErrorComposesError(c *C) { description := "something failed" status := 501 httpErr := ServerError{ error: errors.New(description), HTTPStatus: HTTPStatus(status), } c.Check(httpErr.Error(), Equals, "something failed (501: Not Implemented)") } func (suite *httpErrorSuite) TestNewAzureErrorFromOperation(c *C) { status := http.StatusConflict code := MakeRandomString(7) message := MakeRandomString(20) // Test body copied from Azure documentation, mutatis mutandi. body := fmt.Sprintf(` <?xml version="1.0" encoding="utf-8"?> <Operation xmlns="http://schemas.microsoft.com/windowsazure"> <ID>%s</ID> <Status>Failed</Status> <HttpStatusCode>%d</HttpStatusCode> <Error> <Code>%s</Code> <Message>%s</Message> </Error> </Operation> `, MakeRandomString(5), status, code, message) operation := Operation{} operation.Deserialize([]byte(body)) err := newAzureErrorFromOperation(&operation) c.Check(err.HTTPStatus, Equals, HTTPStatus(status)) c.Check(err.Code, Equals, code) c.Check(err.Message, Equals, message) } func (suite *httpErrorSuite) TestExtendErrorExtendsGenericError(c *C) { errorString := "an-error" error := fmt.Errorf(errorString) additionalErrorMsg := "additional message" newError := extendError(error, additionalErrorMsg) c.Check(newError.Error(), Equals, fmt.Sprintf("%s%s", additionalErrorMsg, error.Error())) } func (suite *httpErrorSuite) TestExtendErrorExtendsServerError(c *C) { err := &ServerError{ error: errors.New("could not talk to server"), HTTPStatus: HTTPStatus(http.StatusGatewayTimeout), } additionalErrorMsg := "additional message: " newError := extendError(err, additionalErrorMsg) newServerError, ok := newError.(*ServerError) c.Assert(ok, Equals, true) c.Check(newError.Error(), Equals, additionalErrorMsg+err.Error()) c.Check(newServerError.HTTPStatus, Equals, err.HTTPStatus) } func (suite *httpErrorSuite) TestExtendErrorExtendsAzureError(c *C) { err := &AzureError{ error: errors.New("could not talk to server"), HTTPStatus: HTTPStatus(http.StatusGatewayTimeout), } additionalErrorMsg := "additional message: " newError := extendError(err, additionalErrorMsg) newAzureError, ok := newError.(*AzureError) c.Assert(ok, Equals, true) c.Check(newError.Error(), Equals, additionalErrorMsg+err.Error()) c.Check(newAzureError.HTTPStatus, Equals, err.HTTPStatus) } func (suite *httpErrorSuite) TestIsNotFound(c *C) { var testValues = []struct { err error expectedResult bool }{ {fmt.Errorf("generic error"), false}, {&AzureError{HTTPStatus: HTTPStatus(http.StatusOK)}, false}, {&AzureError{HTTPStatus: HTTPStatus(http.StatusNotFound)}, true}, {&AzureError{HTTPStatus: HTTPStatus(http.StatusInternalServerError)}, false}, {&ServerError{HTTPStatus: HTTPStatus(http.StatusOK)}, false}, {&ServerError{HTTPStatus: HTTPStatus(http.StatusNotFound)}, true}, {&ServerError{HTTPStatus: HTTPStatus(http.StatusInternalServerError)}, false}, } for _, test := range testValues { c.Check(IsNotFoundError(test.err), Equals, test.expectedResult) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/utilities/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735761�022074� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/utilities/format�������������������������������������������0000755�0000153�0000161�00000000530�12321735761�023310� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /bin/sh # # Reformat Go source code in the current directory, specifying gwacl's custom # layout preferences. # Any additional parameters you pass will be passed on to gofmt, before the # input files. So "-h" will give you command-line help, and "-s" will activate # gofmt's "simplify" option. exec gofmt -w -tabs=false -tabwidth=4 "$@" . ������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/deletedisk_test.go�����������������������������������������0000644�0000153�0000161�00000007431�12321735761�023571� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" . "launchpad.net/gocheck" "net/http" "time" ) type deleteDiskSuite struct{} var _ = Suite(&deleteDiskSuite{}) // Real-world error messages and names. const ( diskInUseErrorTemplate = "BadRequest - A disk with name %s is currently in use by virtual machine gwaclrolemvo1yab running within hosted service gwacl623yosxtppsa9577xy5, deployment gwaclmachinewes4n64f. (http code 400: Bad Request)" diskName = "gwacldiske5w7lkj" diskDoesNotExistError = "DELETE request failed: ResourceNotFound - The disk with the specified name does not exist. (http code 404: Not Found)" ) func (suite *deleteDiskSuite) TestIsInUseError(c *C) { var testValues = []struct { errorString string diskName string expectedResult bool }{ {fmt.Sprintf(diskInUseErrorTemplate, diskName), diskName, true}, {fmt.Sprintf(diskInUseErrorTemplate, diskName), "another-disk", false}, {"unknown error", diskName, false}, {diskDoesNotExistError, diskName, false}, } for _, test := range testValues { c.Check(isInUseError(test.errorString, test.diskName), Equals, test.expectedResult) } } func (suite *deleteDiskSuite) TestIsDoneReturnsTrueIfNilError(c *C) { poller := diskDeletePoller{nil, "", false} randomResponse := x509Response{StatusCode: http.StatusAccepted} done, err := poller.isDone(&randomResponse, nil) c.Check(done, Equals, true) c.Check(err, IsNil) } func (suite *deleteDiskSuite) TestIsDoneReturnsFalseIfDiskInUseError(c *C) { diskName := "gwacldiske5w7lkj" diskInUseError := fmt.Errorf(diskInUseErrorTemplate, diskName) poller := diskDeletePoller{nil, diskName, false} done, err := poller.isDone(nil, diskInUseError) c.Check(done, Equals, false) c.Check(err, IsNil) } func (suite *deleteDiskSuite) TestIsDoneReturnsTrueIfAnotherError(c *C) { anotherError := fmt.Errorf("Unknown error") poller := diskDeletePoller{nil, "disk-name", false} done, err := poller.isDone(nil, anotherError) c.Check(done, Equals, true) c.Check(err, Equals, anotherError) } func (suite *deleteDiskSuite) TestPollCallsDeleteDisk(c *C) { api := makeAPI(c) recordedRequests := setUpDispatcher("operationID") diskName := "gwacldiske5w7lkj" poller := diskDeletePoller{api, diskName, false} response, err := poller.poll() c.Assert(response, IsNil) c.Assert(err, IsNil) expectedURL := api.session.composeURL("services/disks/" + diskName) checkOneRequest(c, recordedRequests, expectedURL, "2012-08-01", nil, "DELETE") } func (suite *deleteDiskSuite) TestManagementAPIDeleteDiskPolls(c *C) { firstResponse := DispatcherResponse{ response: &x509Response{}, errorObject: fmt.Errorf(diskInUseErrorTemplate, diskName)} secondResponse := DispatcherResponse{ response: &x509Response{StatusCode: http.StatusOK}, errorObject: nil} responses := []DispatcherResponse{firstResponse, secondResponse} rigPreparedResponseDispatcher(responses) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) api := makeAPI(c) diskName := "gwacldiske5w7lkj" poller := diskDeletePoller{api, diskName, false} response, err := performPolling(poller, time.Nanosecond, time.Minute) c.Assert(response, IsNil) c.Assert(err, IsNil) expectedURL := api.session.composeURL("services/disks/" + diskName) c.Check(len(recordedRequests), Equals, 2) checkRequest(c, recordedRequests[0], expectedURL, "2012-08-01", nil, "DELETE") checkRequest(c, recordedRequests[1], expectedURL, "2012-08-01", nil, "DELETE") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/random.go��������������������������������������������������0000644�0000153�0000161�00000001200�12321735761�021661� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "math/rand" "time" ) // A GWACL-specific source of pseudo-randomness. We use this so we can safely // seed it without affecting application-global state in the math package. var random *rand.Rand func init() { // Seed the pseudo-random number generator. Without this, each run // will get the same sequence of results from the math/rand package. seed := int64(time.Now().Nanosecond()) source := rand.NewSource(seed) random = rand.New(source) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/management.go����������������������������������������������0000644�0000153�0000161�00000036067�12321736014�022530� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" "sort" "strings" ) type ListInstancesRequest struct { ServiceName string } // ListInstances returns a slice of all instances for all deployments for the // given hosted service name. func (api *ManagementAPI) ListInstances(request *ListInstancesRequest) ([]RoleInstance, error) { instances := []RoleInstance{} properties, err := api.GetHostedServiceProperties(request.ServiceName, true) if err != nil { return nil, err } for _, deployment := range properties.Deployments { instances = append(instances, deployment.RoleInstanceList...) } return instances, nil } // ListAllDeploymentsRequest is a parameter object for ListAllDeployments. type ListAllDeploymentsRequest struct { // ServiceName restricts the listing to the given service. ServiceName string } // ListAllDeployments returns a slice containing all deployments that match // the request. func (api *ManagementAPI) ListAllDeployments(request *ListAllDeploymentsRequest) ([]Deployment, error) { properties, err := api.GetHostedServiceProperties(request.ServiceName, true) if err != nil { return nil, err } return properties.Deployments, nil } // ListDeploymentsRequest is a parameter object for ListDeployments. type ListDeploymentsRequest struct { // ServiceName restricts the listing to the given service. ServiceName string // DeploymentNames is a set (its value type is ignored) that restricts the // listing to those deployments which it contains. DeploymentNames []string } // ListDeployments returns a slice containing specific deployments, insofar // as they match the request. func (api *ManagementAPI) ListDeployments(request *ListDeploymentsRequest) ([]Deployment, error) { properties, err := api.GetHostedServiceProperties(request.ServiceName, true) if err != nil { return nil, err } // Filter the deployment list according to the given names. filter := make(map[string]bool) for _, name := range request.DeploymentNames { filter[name] = true } deployments := []Deployment{} for _, deployment := range properties.Deployments { if _, ok := filter[deployment.Name]; ok { deployments = append(deployments, deployment) } } return deployments, nil } type ListSpecificHostedServicesRequest struct { ServiceNames []string } // ListSpecificHostedServices returns a slice containing specific // HostedServiceDescriptor objects, insofar as they match the request. func (api *ManagementAPI) ListSpecificHostedServices(request *ListSpecificHostedServicesRequest) ([]HostedServiceDescriptor, error) { allServices, err := api.ListHostedServices() if err != nil { return nil, err } // Filter the service list according to the given names. filter := make(map[string]bool) for _, name := range request.ServiceNames { filter[name] = true } services := []HostedServiceDescriptor{} for _, service := range allServices { if _, ok := filter[service.ServiceName]; ok { services = append(services, service) } } return services, nil } type ListPrefixedHostedServicesRequest struct { ServiceNamePrefix string } // ListPrefixedHostedServices returns a slice containing specific // HostedServiceDescriptor objects, insofar as they match the request. func (api *ManagementAPI) ListPrefixedHostedServices(request *ListPrefixedHostedServicesRequest) ([]HostedServiceDescriptor, error) { services, err := api.ListHostedServices() if err != nil { return nil, err } resServices := []HostedServiceDescriptor{} for _, service := range services { if strings.HasPrefix(service.ServiceName, request.ServiceNamePrefix) { resServices = append(resServices, service) } } services = resServices return services, nil } type DestroyDeploymentRequest struct { ServiceName string DeploymentName string } // DestroyDeployment brings down all resources within a deployment - running // instances, disks, etc. - and deletes the deployment itself. func (api *ManagementAPI) DestroyDeployment(request *DestroyDeploymentRequest) error { deployment, err := api.GetDeployment(&GetDeploymentRequest{ ServiceName: request.ServiceName, DeploymentName: request.DeploymentName, }) if err != nil { if IsNotFoundError(err) { return nil } return err } // 1. Get the list of the VM disks. diskNameMap := make(map[string]bool) for _, role := range deployment.RoleList { for _, osVHD := range role.OSVirtualHardDisk { diskNameMap[osVHD.DiskName] = true } } // 2. Delete deployment. This will delete all the role instances inside // this deployment as a side effect. err = api.DeleteDeployment(request.ServiceName, request.DeploymentName) if err != nil && !IsNotFoundError(err) { return err } // Sort the disk names to aid testing. diskNames := []string{} for diskName := range diskNameMap { diskNames = append(diskNames, diskName) } sort.Strings(diskNames) // 3. Delete the disks. for _, diskName := range diskNames { err = api.DeleteDisk(&DeleteDiskRequest{ DiskName: diskName, DeleteBlob: true}) if err != nil && !IsNotFoundError(err) { return err } } // Done. return nil } type DestroyHostedServiceRequest struct { ServiceName string } // DestroyHostedService destroys all of the hosted service's contained // deployments then deletes the hosted service itself. func (api *ManagementAPI) DestroyHostedService(request *DestroyHostedServiceRequest) error { // 1. Get properties. properties, err := api.GetHostedServiceProperties(request.ServiceName, true) if err != nil { if IsNotFoundError(err) { return nil } return err } // 2. Delete deployments. for _, deployment := range properties.Deployments { err := api.DestroyDeployment(&DestroyDeploymentRequest{ ServiceName: request.ServiceName, DeploymentName: deployment.Name, }) if err != nil { return err } } // 3. Delete service. err = api.DeleteHostedService(request.ServiceName) if err != nil && !IsNotFoundError(err) { return err } // Done. return nil } func (api *ManagementAPI) AddVirtualNetworkSite(site *VirtualNetworkSite) error { // Obtain the current network config, which we will then modify. networkConfig, err := api.GetNetworkConfiguration() if err != nil { return err } if networkConfig == nil { // There's no config yet. networkConfig = &NetworkConfiguration{XMLNS: XMLNS_NC} } if networkConfig.VirtualNetworkSites == nil { networkConfig.VirtualNetworkSites = &[]VirtualNetworkSite{} } // Check to see if this network already exists. for _, existingSite := range *networkConfig.VirtualNetworkSites { if existingSite.Name == site.Name { // Network already defined. return fmt.Errorf("could not add virtual network: %q already exists", site.Name) } } // Add the network to the configuration. virtualNetworkSites := append(*networkConfig.VirtualNetworkSites, *site) networkConfig.VirtualNetworkSites = &virtualNetworkSites // Put it back up to Azure. There's a race here... return api.SetNetworkConfiguration(networkConfig) } func (api *ManagementAPI) RemoveVirtualNetworkSite(siteName string) error { // Obtain the current network config, which we will then modify. networkConfig, err := api.GetNetworkConfiguration() if err != nil { return err } if networkConfig == nil || networkConfig.VirtualNetworkSites == nil { // There's no config, nothing to do. return nil } // Remove all references to the specified virtual network site name. virtualNetworkSites := []VirtualNetworkSite{} for _, existingSite := range *networkConfig.VirtualNetworkSites { if existingSite.Name != siteName { virtualNetworkSites = append(virtualNetworkSites, existingSite) } } if len(virtualNetworkSites) < len(*networkConfig.VirtualNetworkSites) { // Put it back up to Azure. There's a race here... networkConfig.VirtualNetworkSites = &virtualNetworkSites return api.SetNetworkConfiguration(networkConfig) } return nil } type ListRoleEndpointsRequest struct { ServiceName string DeploymentName string RoleName string } // ListRoleEndpoints lists the open endpoints for the named service/deployment/role name. func (api *ManagementAPI) ListRoleEndpoints(request *ListRoleEndpointsRequest) ([]InputEndpoint, error) { var err error vmRole, err := api.GetRole(&GetRoleRequest{ ServiceName: request.ServiceName, DeploymentName: request.DeploymentName, RoleName: request.RoleName}) if err != nil { return nil, err } for i, configSet := range vmRole.ConfigurationSets { if configSet.ConfigurationSetType == CONFIG_SET_NETWORK { endpointsP := vmRole.ConfigurationSets[i].InputEndpoints if endpointsP != nil { return *endpointsP, nil } } } return []InputEndpoint{}, nil } type AddRoleEndpointsRequest struct { ServiceName string DeploymentName string RoleName string InputEndpoints []InputEndpoint } // AddRoleEndpoints appends the supplied endpoints to the existing endpoints // for the named service/deployment/role name. Note that the Azure API // leaves this open to a race condition between fetching and updating the role. func (api *ManagementAPI) AddRoleEndpoints(request *AddRoleEndpointsRequest) error { var err error vmRole, err := api.GetRole(&GetRoleRequest{ ServiceName: request.ServiceName, DeploymentName: request.DeploymentName, RoleName: request.RoleName}) if err != nil { return err } for i, configSet := range vmRole.ConfigurationSets { // TODO: Is NetworkConfiguration always present? if configSet.ConfigurationSetType == CONFIG_SET_NETWORK { endpointsP := vmRole.ConfigurationSets[i].InputEndpoints if endpointsP == nil { // No endpoints set at all, initialise it to be empty. vmRole.ConfigurationSets[i].InputEndpoints = &[]InputEndpoint{} } // Append to existing endpoints. // TODO: Check clashing endpoint. LocalPort/Name/Port unique? endpoints := append( *vmRole.ConfigurationSets[i].InputEndpoints, request.InputEndpoints...) vmRole.ConfigurationSets[i].InputEndpoints = &endpoints break // Only one NetworkConfiguration so exit loop now. } } // Enjoy this race condition. err = api.UpdateRole(&UpdateRoleRequest{ ServiceName: request.ServiceName, DeploymentName: request.DeploymentName, RoleName: request.RoleName, PersistentVMRole: vmRole}) if err != nil { return err } return nil } // CompareInputEndpoints attempts to compare two InputEndpoint objects in a // way that's congruent with how Windows's Azure considers them. The name is // always ignored, as is LoadBalancerProbe. When LoadBalancedEndpointSetName // is set (not the empty string), all the fields - LocalPort, Port, Protocol, // VIP and LoadBalancedEndpointSetName - are used for the comparison. When // LoadBalancedEndpointSetName is the empty string, all except LocalPort - // effectively Port, Protocol and VIP - are used for comparison. func CompareInputEndpoints(a, b *InputEndpoint) bool { if a.LoadBalancedEndpointSetName == "" { return a.Port == b.Port && a.Protocol == b.Protocol && a.VIP == b.VIP } else { return (a.LoadBalancedEndpointSetName == b.LoadBalancedEndpointSetName && a.LocalPort == b.LocalPort && a.Port == b.Port && a.Protocol == b.Protocol && a.VIP == b.VIP) } } type RemoveRoleEndpointsRequest struct { ServiceName string DeploymentName string RoleName string InputEndpoints []InputEndpoint } // RemoveRoleEndpoints attempts to remove the given endpoints from the // specified role. It uses `CompareInputEndpoints` to determine when there's a // match between the given endpoint and one already configured. func (api *ManagementAPI) RemoveRoleEndpoints(request *RemoveRoleEndpointsRequest) error { filterRequest := filterRoleEndpointsRequest{ ServiceName: request.ServiceName, DeploymentName: request.DeploymentName, RoleName: request.RoleName, Filter: func(a *InputEndpoint) bool { for _, b := range request.InputEndpoints { if CompareInputEndpoints(a, &b) { return false } } return true }, } return api.filterRoleEndpoints(&filterRequest) } // Returns true to keep the endpoint defined in the role's configuration, // false to remove it. It is also welcome to mutate endpoints; they are passed // by reference. type inputEndpointFilter func(*InputEndpoint) bool type filterRoleEndpointsRequest struct { ServiceName string DeploymentName string RoleName string Filter inputEndpointFilter } // filterRoleEndpoints is a general role endpoint filtering function. It is // private because it is only a support function for RemoveRoleEndpoints, and // is not tested directly. func (api *ManagementAPI) filterRoleEndpoints(request *filterRoleEndpointsRequest) error { role, err := api.GetRole(&GetRoleRequest{ ServiceName: request.ServiceName, DeploymentName: request.DeploymentName, RoleName: request.RoleName, }) if err != nil { return err } for index, configSet := range role.ConfigurationSets { if configSet.ConfigurationSetType == CONFIG_SET_NETWORK { if configSet.InputEndpoints != nil { endpoints := []InputEndpoint{} for _, existingEndpoint := range *configSet.InputEndpoints { if request.Filter(&existingEndpoint) { endpoints = append(endpoints, existingEndpoint) } } if len(endpoints) == 0 { configSet.InputEndpoints = nil } else { configSet.InputEndpoints = &endpoints } } } // Update the role; implicit copying is a nuisance. role.ConfigurationSets[index] = configSet } return api.UpdateRole(&UpdateRoleRequest{ ServiceName: request.ServiceName, DeploymentName: request.DeploymentName, RoleName: request.RoleName, PersistentVMRole: role, }) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/dedent/����������������������������������������������������0000755�0000153�0000161�00000000000�12321735761�021324� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/dedent/dedent.go�������������������������������������������0000644�0000153�0000161�00000004247�12321735761�023125� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package dedent import ( "regexp" "strings" ) const emptyString = "" var reLine = regexp.MustCompile(`(?m-s)^.*$`) // Split the given text into lines. func splitLines(text string) []string { return reLine.FindAllString(text, -1) } // Match leading whitespace or tabs. \p{Zs} is a Unicode character class: // http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#General_Category var reLeadingWhitespace = regexp.MustCompile(`^[\p{Zs}\t]+`) // Find the longest leading margin common between the given lines. func calculateMargin(lines []string) string { var margin string var first bool = true for _, line := range lines { indent := reLeadingWhitespace.FindString(line) switch { case len(indent) == len(line): // The line is either empty or whitespace and will be ignored for // the purposes of calculating the margin. case first: // This is the first line with an indent, so start from here. margin = indent first = false case strings.HasPrefix(indent, margin): // This line's indent is longer or equal to the margin. The // current margin remains unalterered. case strings.HasPrefix(margin, indent): // This line's indent is compatible with the margin but shorter // (strictly it could be equal, however that condition is handled // earlier in this switch). The current indent becomes the margin. margin = indent default: // There is no common margin so stop scanning. return emptyString } } return margin } // Remove a prefix from each line, if present. func trimPrefix(lines []string, prefix string) { trim := len(prefix) for i, line := range lines { if strings.HasPrefix(line, prefix) { lines[i] = line[trim:] } } } func Dedent(text string) string { lines := splitLines(text) trimPrefix(lines, calculateMargin(lines)) return strings.Join(lines, "\n") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/dedent/dedent_test.go��������������������������������������0000644�0000153�0000161�00000005305�12321735761�024160� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package dedent import ( . "launchpad.net/gocheck" "testing" ) type dedentSuite struct{} var _ = Suite(&dedentSuite{}) // Dedent() does nothing with the empty string. func (suite *dedentSuite) TestEmptyString(c *C) { input := "" expected := input observed := Dedent(input) c.Check(observed, Equals, expected) } // Dedent() does nothing to a single line without an indent. func (suite *dedentSuite) TestSingleLine(c *C) { input := "This is a single line." expected := input observed := Dedent(input) c.Check(observed, Equals, expected) } // Dedent() removes all leading whitespace from single lines. func (suite *dedentSuite) TestSingleLineWithIndent(c *C) { input := " This is a single line." expected := "This is a single line." observed := Dedent(input) c.Check(observed, Equals, expected) } // Dedent() does nothing when none of the lines are indented. func (suite *dedentSuite) TestLines(c *C) { input := "One\nTwo\n" expected := input observed := Dedent(input) c.Check(observed, Equals, expected) } // Dedent() does nothing when *any* line is not indented. func (suite *dedentSuite) TestLinesWithSomeIndents(c *C) { input := "One\n Two\n" expected := input observed := Dedent(input) c.Check(observed, Equals, expected) } // Dedent() removes the common leading indent from each line. func (suite *dedentSuite) TestLinesWithIndents(c *C) { input := " One\n Two\n" expected := "One\n Two\n" observed := Dedent(input) c.Check(observed, Equals, expected) } // Dedent() ignores all-whitespace lines for the purposes of margin // calculation. However, the margin *is* trimmed from these lines, if they // begin with it. func (suite *dedentSuite) TestLinesWithEmptyLine(c *C) { input := " One\n \n Three\n" expected := "One\n \nThree\n" observed := Dedent(input) c.Check(observed, Equals, expected) } // Dedent() ignores blank lines for the purposes of margin calculation, // including the first line. func (suite *dedentSuite) TestLinesWithEmptyFirstLine(c *C) { input := "\n Two\n Three\n" expected := "\nTwo\nThree\n" observed := Dedent(input) c.Check(observed, Equals, expected) } // Dedent() treats spaces and tabs as completely different; no number of // spaces is equivalent to a tab. func (suite *dedentSuite) TestLinesWithTabsAndSpaces(c *C) { input := "\tOne\n Two\n" expected := input observed := Dedent(input) c.Check(observed, Equals, expected) } // Master loader for all tests. func Test(t *testing.T) { TestingT(t) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/COPYING����������������������������������������������������0000644�0000153�0000161�00000016743�12321735761�021127� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �����������������������������juju-core_1.18.1/src/launchpad.net/gwacl/HACKING.txt������������������������������������������������0000644�0000153�0000161�00000012217�12321735761�021671� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������================================================== Hacking in GWACL (Go Windows Azure Client Library) ================================================== (this doc is a huge WIP) Submitting changes ------------------ `GWACL`_ is hosted on Launchpad using `Bazaar`_. Submitting a change requires you to create a merge proposal against the trunk branch and a core committer will then review the branch. Once the branch is accepted, it will be landed by the reviewer. All branch submissions must be formatted using ``make format``. They must also have a successful test run with ``make check``. New features must always be accompanied by new tests. .. _GWACL: https://launchpad.net/gwacl .. _Bazaar: http://bazaar.canonical.com/ Overview of Azure ----------------- Computing services ^^^^^^^^^^^^^^^^^^ Azure was originally designed as SaaS for Microsoft Windows and later amended to allow individual virtual machines to run up with Linux distributions. Some remnants of this SaaS architecture remain today, and understanding them is crucial to understanding how GWACL works when spinning up virtual instances. There are three main components to any virtual instance: * A hosted service * A deployment * A role instance Hosted services are the "top level" of a virtual resource. Each one contains up to two deployments, and has its own DNS entry and firewall settings (known as "endpoints" in Azure). The name of the service forms the DNS entry as "<name>.cloudapp.net". Deployments are Azure's abstraction of whether something is running on its staging or production environment. They are only exposed in the API and not the web management UI. Deployments contain one or more role instances. Role instances are virtual machines. Many instances may exist in a deployment (and hence hosted service) but if there is more than one they are intended to be running components from the same application and they will all share the same DNS entry and open endpoints. Thus, a hosted service exposes a single application on the internet and may be composed of multiple role instances for load balancing and differing components. For this reason, if you want several separate applications, you must create a separate service, deployment and role instance for each. Networking across hosted services ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Each service can only see as much of each other service as any public observer does, however it's possible to place them in a private network so they are effectively on a share LAN segment with no firewall. In Azure this is called a "virtual network". The virtual network must be defined before any services that use it are created, and then associated at service creation time. The virtual network can be assigned any valid networking range which is then private to all the virtual instances defined to use it. Storage services ^^^^^^^^^^^^^^^^ Azure supports data storage which is accessed separately to role instances and hosted services. This is organised into several components: * A storage account * Containers within an account * Blobs inside containers A storage account can be created via the web management UI or API. Its name forms the DNS entry for that storage as "<name>.blob.core.windows.net". A container forms the next, and only, level of indirection under the account. You cannot have a container under a container. Containers control the default privacy of files therein. Blobs are the actual files in the storage. They can be of two main types: * Block blobs * Page blobs Block blobs are used for sequential access and are optimised for streaming. Page blobs are used for random access and allow access to ranges of bytes in a blob. The full URL to a file in a storage account looks like: https://<accountname>.blob.core.windows.net/<containername>/<blobname> The http version of the same URL would work too, but is prone to spurious authentication failures when accessed through a proxy. Therefore gwacl accesses the storage API through https; this may become configurable later if there is demand. RESTful API access ------------------ There are two API endpoints for Azure, the management API and the storage API. Each also uses its own authentication method: * x509 certificates for the management API * HMAC signed request for the storage API The GWACL code hides most of this complexity from you, it just requires the cerificate data for the management API access, and the storage key for storage API access. The storage key can be fetched either from the management UI, or management API call. Generating x509 certificates is explained in the :doc:`README <README>` GWACL's API philosophy ---------------------- API functions in the library should take a single struct parameter, which itself contains one or more parameters. Existing functions that do not follow this rule are historic and should not be copied. This brings several advantages:: 1. Keyword parameters improve callsite readability. 2. It allows for parameters to be defaulted if not supplied. 3. It's much easier to change the API later without breaking existing code, it just needs re-compiling in the case where you add new, optional, parameters. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/x509session_test.go����������������������������������������0000644�0000153�0000161�00000027471�12321735761�023573� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "fmt" . "launchpad.net/gocheck" "math/big" "net/http" "os" "time" ) // defaultManagement is the international management API for Azure. // (Mainland China gets a different URL). const defaultManagement = "https://management.core.windows.net/" // x509DispatcherFixture records the current x509 dispatcher before a test, // and restores it after. This gives your test the freedom to replace the // dispatcher with test doubles, using any of the rig*Dispatcher functions. // Call the fixture's SetUpTest/TearDownTest methods before/after your test, // or if you have no other setup/teardown methods, just embed the fixture in // your test suite. type x509DispatcherFixture struct { oldDispatcher func(*x509Session, *X509Request) (*x509Response, error) } func (suite *x509DispatcherFixture) SetUpTest(c *C) { // Record the original X509 dispatcher. Will be restored at the end of // each test. suite.oldDispatcher = _X509Dispatcher } func (suite *x509DispatcherFixture) TearDownTest(c *C) { // Restore old dispatcher. _X509Dispatcher = suite.oldDispatcher } type x509SessionSuite struct { x509DispatcherFixture } var _ = Suite(&x509SessionSuite{}) // Create a cert and pem file in a temporary dir in /tmp and return the // names of the files. The caller is responsible for cleaning up the files. func makeX509Certificate() (string, string) { // Code is shamelessly stolen from // http://golang.org/src/pkg/crypto/tls/generate_cert.go priv, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil { panic(fmt.Errorf("Failed to generate rsa key: %v", err)) } // Create a template for x509.CreateCertificate. now := time.Now() template := x509.Certificate{ SerialNumber: new(big.Int).SetInt64(0), Subject: pkix.Name{ CommonName: "localhost", Organization: []string{"Bogocorp"}, }, NotBefore: now.Add(-5 * time.Minute).UTC(), NotAfter: now.AddDate(1, 0, 0).UTC(), // valid for 1 year. SubjectKeyId: []byte{1, 2, 3, 4}, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, } // Create the certificate itself. derBytes, err := x509.CreateCertificate( rand.Reader, &template, &template, &priv.PublicKey, priv) if err != nil { panic(fmt.Errorf("Failed to generate x509 certificate: %v", err)) } // Write the certificate file out. dirname := os.TempDir() + "/" + MakeRandomString(10) os.Mkdir(dirname, 0700) certFile := dirname + "/cert.pem" certOut, err := os.Create(certFile) if err != nil { panic(fmt.Errorf("Failed to create %s: %v", certFile, err)) } pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) certOut.Close() // Write the key file out. keyFile := dirname + "/key.pem" keyOut, err := os.OpenFile( keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { panic(fmt.Errorf("Failed to create %s: %v", keyFile, err)) } pem.Encode( keyOut, &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) keyOut.Close() return certFile, keyFile } func (suite *x509SessionSuite) TestNewX509Session(c *C) { session, err := newX509Session("subscriptionid", "", "China East", NoRetryPolicy) c.Assert(err, IsNil) c.Assert(session.baseURL, NotNil) c.Check(session.baseURL.String(), Equals, GetEndpoint("China East").ManagementAPI()) } func (suite *x509SessionSuite) TestComposeURLComposesURLWithRelativePath(c *C) { const subscriptionID = "subscriptionid" const path = "foo/bar" session, err := newX509Session(subscriptionID, "", "West US", NoRetryPolicy) c.Assert(err, IsNil) url := session.composeURL(path) c.Check(url, Matches, defaultManagement+subscriptionID+"/"+path) } func (suite *x509SessionSuite) TestComposeURLRejectsAbsolutePath(c *C) { defer func() { err := recover() c.Assert(err, NotNil) c.Check(err, ErrorMatches, ".*absolute.*path.*") }() session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) // This panics because we're passing an absolute path. session.composeURL("/foo") } func (suite *x509SessionSuite) TestGetServerErrorProducesServerError(c *C) { msg := "huhwhat" status := http.StatusNotFound session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) err = session.getServerError(status, []byte{}, msg) c.Assert(err, NotNil) c.Check(err, ErrorMatches, ".*"+msg+".*") serverError := err.(*ServerError) c.Check(serverError.StatusCode(), Equals, status) } func (suite *x509SessionSuite) TestGetServerErrorLikes20x(c *C) { goodCodes := []int{ http.StatusOK, http.StatusNoContent, } session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) for _, status := range goodCodes { c.Check(session.getServerError(status, []byte{}, ""), IsNil) } } func (suite *x509SessionSuite) TestGetServerReturnsErrorsForFailures(c *C) { badCodes := []int{ http.StatusSwitchingProtocols, http.StatusBadRequest, http.StatusPaymentRequired, http.StatusForbidden, http.StatusGone, http.StatusInternalServerError, http.StatusNotImplemented, } session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) c.Assert(err, IsNil) for _, status := range badCodes { c.Check(session.getServerError(status, []byte{}, ""), NotNil) } } func (suite *x509SessionSuite) TestGetIssuesRequest(c *C) { subscriptionID := "subscriptionID" uri := "resource" session, err := newX509Session(subscriptionID, "", "West US", NoRetryPolicy) c.Assert(err, IsNil) // Record incoming requests, and have them return a given reply. fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte("Response body"), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) version := "test-version" receivedResponse, err := session.get(uri, version) c.Assert(err, IsNil) c.Assert(len(recordedRequests), Equals, 1) request := recordedRequests[0] c.Check(request.URL, Equals, defaultManagement+subscriptionID+"/"+uri) c.Check(request.Method, Equals, "GET") c.Check(request.APIVersion, Equals, version) c.Check(*receivedResponse, DeepEquals, fixedResponse) } func (suite *x509SessionSuite) TestGetReportsClientSideError(c *C) { session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) msg := "could not dispatch request" rigFailingDispatcher(fmt.Errorf(msg)) body, err := session.get("flop", "version") c.Assert(err, NotNil) c.Check(body, IsNil) c.Check(err, ErrorMatches, ".*"+msg+".*") } func (suite *x509SessionSuite) TestGetReportsServerSideError(c *C) { session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) fixedResponse := x509Response{ StatusCode: http.StatusForbidden, Body: []byte("Body"), } rigFixedResponseDispatcher(&fixedResponse) response, err := session.get("fail", "version") c.Assert(err, NotNil) serverError := err.(*ServerError) c.Check(serverError.StatusCode(), Equals, fixedResponse.StatusCode) c.Check(*response, DeepEquals, fixedResponse) } func (suite *x509SessionSuite) TestPostIssuesRequest(c *C) { subscriptionID := "subscriptionID" uri := "resource" version := "test-version" requestBody := []byte("Request body") requestContentType := "bogusContentType" session, err := newX509Session(subscriptionID, "", "West US", NoRetryPolicy) c.Assert(err, IsNil) // Record incoming requests, and have them return a given reply. fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte("Response body"), } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) receivedResponse, err := session.post(uri, version, requestBody, requestContentType) c.Assert(err, IsNil) c.Assert(len(recordedRequests), Equals, 1) request := recordedRequests[0] c.Check(request.URL, Equals, defaultManagement+subscriptionID+"/"+uri) c.Check(request.Method, Equals, "POST") c.Check(request.APIVersion, Equals, version) c.Check(request.ContentType, Equals, requestContentType) c.Check(request.Payload, DeepEquals, requestBody) c.Check(*receivedResponse, DeepEquals, fixedResponse) } func (suite *x509SessionSuite) TestPostReportsClientSideError(c *C) { session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) msg := "could not dispatch request" rigFailingDispatcher(fmt.Errorf(msg)) body, err := session.post("flop", "version", []byte("body"), "contentType") c.Assert(err, NotNil) c.Check(body, IsNil) c.Check(err, ErrorMatches, ".*"+msg+".*") } func (suite *x509SessionSuite) TestPostReportsServerSideError(c *C) { session, err := newX509Session("subscriptionid", "", "West US", NoRetryPolicy) fixedResponse := x509Response{ StatusCode: http.StatusForbidden, Body: []byte("Body"), } rigFixedResponseDispatcher(&fixedResponse) reponse, err := session.post("fail", "version", []byte("request body"), "contentType") c.Assert(err, NotNil) serverError := err.(*ServerError) c.Check(serverError.StatusCode(), Equals, fixedResponse.StatusCode) c.Check(*reponse, DeepEquals, fixedResponse) } func (suite *x509SessionSuite) TestDeleteIssuesRequest(c *C) { subscriptionID := "subscriptionID" uri := "resource" version := "test-version" session, err := newX509Session(subscriptionID, "", "West US", NoRetryPolicy) c.Assert(err, IsNil) // Record incoming requests, and have them return a given reply. fixedResponse := x509Response{StatusCode: http.StatusOK} rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) response, err := session.delete(uri, version) c.Assert(err, IsNil) c.Check(*response, DeepEquals, fixedResponse) c.Assert(len(recordedRequests), Equals, 1) request := recordedRequests[0] c.Check(request.URL, Equals, defaultManagement+subscriptionID+"/"+uri) c.Check(request.Method, Equals, "DELETE") c.Check(request.APIVersion, Equals, version) } func (suite *x509SessionSuite) TestPutIssuesRequest(c *C) { subscriptionID := "subscriptionID" uri := "resource" version := "test-version" requestBody := []byte("Request body") session, err := newX509Session(subscriptionID, "", "West US", NoRetryPolicy) c.Assert(err, IsNil) // Record incoming requests, and have them return a given reply. fixedResponse := x509Response{ StatusCode: http.StatusOK, } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) _, err = session.put(uri, version, requestBody, "text/plain") c.Assert(err, IsNil) c.Assert(len(recordedRequests), Equals, 1) request := recordedRequests[0] c.Check(request.URL, Equals, defaultManagement+subscriptionID+"/"+uri) c.Check(request.Method, Equals, "PUT") c.Check(request.APIVersion, Equals, version) c.Check(request.Payload, DeepEquals, requestBody) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/example/���������������������������������������������������0000755�0000153�0000161�00000000000�12321735761�021514� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/example/management/����������������������������������������0000755�0000153�0000161�00000000000�12321736015�023621� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/example/management/run.go����������������������������������0000644�0000153�0000161�00000024676�12321736014�024772� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). /* This is an example on how the Azure Go library can be used to interact with the Windows Azure Service. Note that this is a provided only as an example and that real code should probably do something more sensible with errors than ignoring them or panicking. */ package main import ( "encoding/base64" "flag" "fmt" "launchpad.net/gwacl" . "launchpad.net/gwacl/logging" "math/rand" "os" "time" ) var certFile string var subscriptionID string var pause bool var location string func getParams() error { flag.StringVar(&certFile, "cert", "", "Name of your management certificate file (in PEM format).") flag.StringVar(&subscriptionID, "subscriptionid", "", "Your Azure subscription ID.") flag.BoolVar(&pause, "pause", false, "Wait for user input after the VM is brought up (useful for further testing)") flag.StringVar(&location, "location", "North Europe", "Azure cloud location, e.g. 'West US' or 'China East'") flag.Parse() if certFile == "" { return fmt.Errorf("No .pem certificate specified. Use the -cert option.") } if subscriptionID == "" { return fmt.Errorf("No subscription ID specified. Use the -subscriptionid option.") } return nil } func checkError(err error) { if err != nil { panic(err) } } // makeRandomIdentifier creates an arbitrary identifier of the given length, // consisting of only ASCII digits and lower-case ASCII letters. // The identifier will start with the given prefix. The prefix must be no // longer than the specified length, or there'll be trouble. func makeRandomIdentifier(prefix string, length int) string { // Only digits and lower-case ASCII letters are accepted. const chars = "abcdefghijklmnopqrstuvwxyz0123456789" if len(prefix) > length { panic(fmt.Errorf("prefix '%s' is more than the requested %d characters long", prefix, length)) } id := prefix for len(id) < length { id += string(chars[rand.Intn(len(chars))]) } return id } func main() { rand.Seed(int64(time.Now().Nanosecond())) err := getParams() if err != nil { Info(err) os.Exit(1) } api, err := gwacl.NewManagementAPI(subscriptionID, certFile, location) checkError(err) ExerciseHostedServicesAPI(api) Info("All done.") } func ExerciseHostedServicesAPI(api *gwacl.ManagementAPI) { var err error location := "West US" release := "13.04" affinityGroupName := gwacl.MakeRandomHostname("affinitygroup") Info("Creating an affinity group...") cag := gwacl.NewCreateAffinityGroup(affinityGroupName, "affinity-label", "affinity-description", location) err = api.CreateAffinityGroup(&gwacl.CreateAffinityGroupRequest{ CreateAffinityGroup: cag}) checkError(err) Infof("Created affinity group %s\n", affinityGroupName) defer func() { Infof("Deleting affinity group %s\n", affinityGroupName) err := api.DeleteAffinityGroup(&gwacl.DeleteAffinityGroupRequest{ Name: affinityGroupName}) checkError(err) Infof("Done deleting affinity group %s\n", affinityGroupName) }() virtualNetworkName := gwacl.MakeRandomVirtualNetworkName("virtual-net-") Infof("Creating virtual network %s...\n", virtualNetworkName) virtualNetwork := gwacl.VirtualNetworkSite{ Name: virtualNetworkName, AffinityGroup: affinityGroupName, AddressSpacePrefixes: []string{ "10.0.0.0/8", }, } err = api.AddVirtualNetworkSite(&virtualNetwork) checkError(err) Info("Done creating virtual network") defer func() { Infof("Deleting virtual network %s...\n", virtualNetworkName) err := api.RemoveVirtualNetworkSite(virtualNetworkName) checkError(err) Infof("Done deleting virtual network %s\n", virtualNetworkName) }() networkConfig, err := api.GetNetworkConfiguration() checkError(err) if networkConfig == nil { Info("No network configuration is set") } else { xml, err := networkConfig.Serialize() checkError(err) Info(xml) } Infof("Getting OS Image for release '%s' and location '%s'...\n", release, location) images, err := api.ListOSImages() checkError(err) image, err := images.GetLatestUbuntuImage(release, location) checkError(err) sourceImageName := image.Name Infof("Got image named '%s'\n", sourceImageName) Info("Done getting OS Image\n") hostServiceName := gwacl.MakeRandomHostedServiceName("gwacl") Infof("Creating a hosted service: '%s'...\n", hostServiceName) createHostedService := gwacl.NewCreateHostedServiceWithLocation(hostServiceName, "testLabel", location) createHostedService.AffinityGroup = affinityGroupName err = api.AddHostedService(createHostedService) checkError(err) Info("Done creating a hosted service\n") defer func() { Info("Destroying hosted service...") // FIXME: Check error api.DestroyHostedService(&gwacl.DestroyHostedServiceRequest{ ServiceName: hostServiceName}) Info("Done destroying hosted service\n") }() Info("Listing hosted services...") hostedServices, err := api.ListHostedServices() checkError(err) Infof("Got %d hosted service(s)\n", len(hostedServices)) if len(hostedServices) > 0 { hostedService := hostedServices[0] detailedHostedService, err := api.GetHostedServiceProperties(hostedService.ServiceName, true) checkError(err) Infof("Hosted service '%s' contains %d deployments\n", hostedService.ServiceName, len(detailedHostedService.Deployments)) // Do the same again with ListAllDeployments. deployments, err := api.ListAllDeployments(&gwacl.ListAllDeploymentsRequest{ServiceName: hostedService.ServiceName}) checkError(err) if len(deployments) != len(detailedHostedService.Deployments) { Errorf( "Mismatch in reported deployments: %d != %d", len(deployments), len(detailedHostedService.Deployments)) } } Info("Done listing hosted services\n") Info("Adding VM deployment...") hostname := gwacl.MakeRandomHostname("gwaclhost") // Random passwords are no use to man nor beast here if you want to // test with your instance, so we'll use a fixed one. It's not really a // security hazard in such a short-lived private instance. password := "Ubuntu123" username := "ubuntu" vhdName := gwacl.MakeRandomDiskName("gwacldisk") userdata := base64.StdEncoding.EncodeToString([]byte("TEST_USER_DATA")) linuxConfigurationSet := gwacl.NewLinuxProvisioningConfigurationSet( hostname, username, password, userdata, "false") inputendpoint := gwacl.InputEndpoint{LocalPort: 22, Name: "sshport", Port: 22, Protocol: "TCP"} networkConfigurationSet := gwacl.NewNetworkConfigurationSet([]gwacl.InputEndpoint{inputendpoint}, nil) storageAccount := makeRandomIdentifier("gwacl", 24) storageLabel := makeRandomIdentifier("gwacl", 64) Infof("Requesting storage account with name '%s' and label '%s'...\n", storageAccount, storageLabel) cssi := gwacl.NewCreateStorageServiceInputWithLocation(storageAccount, storageLabel, location, "false") err = api.AddStorageAccount(cssi) checkError(err) Info("Done requesting storage account\n") defer func() { Infof("Deleting storage account %s...\n", storageAccount) // FIXME: Check error api.DeleteStorageAccount(storageAccount) Info("Done deleting storage account\n") }() mediaLink := gwacl.CreateVirtualHardDiskMediaLink(storageAccount, fmt.Sprintf("vhds/%s.vhd", vhdName)) diskName := makeRandomIdentifier("gwacldisk", 16) diskLabel := makeRandomIdentifier("gwacl", 64) vhd := gwacl.NewOSVirtualHardDisk("", diskLabel, diskName, mediaLink, sourceImageName, "Linux") roleName := gwacl.MakeRandomRoleName("gwaclrole") role := gwacl.NewRole("ExtraSmall", roleName, []gwacl.ConfigurationSet{*linuxConfigurationSet, *networkConfigurationSet}, []gwacl.OSVirtualHardDisk{*vhd}) machineName := makeRandomIdentifier("gwaclmachine", 20) deployment := gwacl.NewDeploymentForCreateVMDeployment( machineName, "Production", machineName, []gwacl.Role{*role}, virtualNetworkName) err = api.AddDeployment(deployment, hostServiceName) checkError(err) Info("Done adding VM deployment\n") Info("Starting VM...") err = api.StartRole(&gwacl.StartRoleRequest{hostServiceName, deployment.Name, role.RoleName}) checkError(err) Info("Done starting VM\n") Info("Listing VM...") instances, err := api.ListInstances(&gwacl.ListInstancesRequest{hostServiceName}) checkError(err) Infof("Got %d instance(s)\n", len(instances)) Info("Done listing VM\n") Info("Getting deployment info...") request := &gwacl.GetDeploymentRequest{ServiceName: hostServiceName, DeploymentName: machineName} deploy, err := api.GetDeployment(request) checkError(err) fqdn, err := deploy.GetFQDN() checkError(err) Info("Got deployment info\n") Info("Adding role input endpoint...") endpoint := gwacl.InputEndpoint{ Name: gwacl.MakeRandomHostname("endpoint-"), Port: 1080, LocalPort: 80, Protocol: "TCP", } err = api.AddRoleEndpoints(&gwacl.AddRoleEndpointsRequest{ ServiceName: hostServiceName, DeploymentName: deployment.Name, RoleName: role.RoleName, InputEndpoints: []gwacl.InputEndpoint{endpoint}, }) checkError(err) Info("Added role input endpoint\n") defer func() { Info("Removing role input endpoint...") err := api.RemoveRoleEndpoints(&gwacl.RemoveRoleEndpointsRequest{ ServiceName: hostServiceName, DeploymentName: deployment.Name, RoleName: role.RoleName, InputEndpoints: []gwacl.InputEndpoint{endpoint}, }) checkError(err) Info("Removed role input endpoint\n") }() if pause { var wait string fmt.Println("host:", fqdn) fmt.Println("username:", username) fmt.Println("password:", password) fmt.Println("") fmt.Println("Pausing so you can play with the newly-created VM") fmt.Println("To clear up, type something and press enter to continue") fmt.Scan(&wait) } } ������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/example/storage/�������������������������������������������0000755�0000153�0000161�00000000000�12321735761�023160� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/example/storage/run.go�������������������������������������0000644�0000153�0000161�00000026730�12321735761�024323� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). // This is an example of how to use GWACL to interact with the Azure storage // services API. // // Note that it is provided "as-is" and contains very little error handling. // Real code should handle errors. package main import ( "flag" "fmt" "io/ioutil" "launchpad.net/gwacl" "os" "strings" ) func badOperationError() error { return fmt.Errorf("Must specify one of %v", operationNames) } // operation is something you can instruct this program to do, by specifying // its name on the command line. type operation struct { // name is the operation name as used on the command line. name string // description holds a description of what the operation does. description string // example illustrates how the operation is used. example string // requiredArgs lists the command-line options that are required for this // operation. requiredArgs []string // validate is an optional callback to perform more detailed checking on // the operation's arguments. validate func() error // implementation is a function that performs the operation. If it fails, // it just panics. implementation func(gwacl.StorageContext) } // operations defines what operations are available to be invoked from the // command line. var operations = []operation{ { name: "listcontainers", description: "Show existing storage containers", example: "listcontainers", implementation: listcontainers, }, { name: "list", description: "List files in a container", example: "-container=<container> list", requiredArgs: []string{"container"}, implementation: list, }, { name: "containeracl", description: "Set access on a container", example: "-container=<container> -acl <container|blob|private> containeracl", requiredArgs: []string{"container", "key", "acl"}, validate: func() error { if acl != "container" && acl != "blob" && acl != "private" { return fmt.Errorf( "Usage: containeracl -container=<container> <container|blob|private>") } return nil }, implementation: containeracl, }, { name: "getblob", description: "Get a file from a container (it's returned on stdout)", example: "-container=<container> -filename=<filename> getblob", requiredArgs: []string{"container", "filename"}, implementation: getblob, }, { name: "addblock", description: "Upload a file to a block blob", example: "-container=<container> -filename=<filename> addblock", requiredArgs: []string{"key", "container", "filename"}, implementation: addblock, }, { name: "deleteblob", description: "Delete a blob", example: "-container=<container> -filename=<filename> deleteblob", requiredArgs: []string{"key", "container", "filename"}, implementation: deleteblob, }, { name: "putblob", description: "Create an empty page blob", example: "-container=<container> -blobname=<blobname> -size=<bytes> " + "-blobtype=\"page\" putblob", requiredArgs: []string{"key", "blobname", "blobtype", "container", "size"}, implementation: putblob, }, { name: "putpage", description: "Upload a file to a page blob's page. The range parameters must " + "be (modulo 512)-(modulo 512 -1), eg: -pagerange=0-511", example: "-container=<container> -blobname=<blobname> -pagerange=<N-N> " + "-filename=<local file> putpage", requiredArgs: []string{"key", "blobname", "container", "pagerange", "filename"}, implementation: putpage, }, } // operationsByName maps each opeation name to an operation struct that // describes it. var operationsByName map[string]operation // operationNames lists just the names of the oeprations, in the order in which // they are listed in "operations." var operationNames []string func init() { operationsByName = make(map[string]operation, len(operations)) for _, op := range operations { operationsByName[op.name] = op } operationNames = make([]string, len(operations)) for index, op := range operations { operationNames[index] = op.name } } // Variables set by command-line options. var ( help bool account string location string key string filename string container string prefix string blobname string blobtype string size int pagerange string acl string ) // argumentGiven returns whether the named argument was specified on the // command line. func argumentGiven(name string) bool { // This is stupid. There must be a way to ask the flag module directly! switch name { case "account": return account != "" case "location": return location != "" case "key": return key != "" case "container": return container != "" case "filename": return filename != "" case "prefix": return prefix != "" case "blobname": return blobname != "" case "blobtype": return blobtype != "" case "size": return size != 0 case "pagerange": return pagerange != "" case "acl": return acl != "" } panic(fmt.Errorf("internal error: unknown command-line option: %s", name)) } func getParams() (string, error) { flag.BoolVar(&help, "h", false, "Show usage and exit") flag.StringVar(&account, "account", "", "Storage account name") flag.StringVar(&location, "location", "", "Azure location, e.g. \"West US\", \"China East\", or \"North Europe\"") flag.StringVar(&key, "key", "", "A valid storage account key (base64 encoded), defaults to the empty string (i.e. anonymous access)") flag.StringVar(&container, "container", "", "Name of the container to use") flag.StringVar(&filename, "filename", "", "File containing blob/page to upload/download") flag.StringVar(&prefix, "prefix", "", "Prefix to match when listing blobs") flag.StringVar(&blobname, "blobname", "", "Name of blob in container") flag.StringVar(&blobtype, "blobtype", "", "Type of blob, 'page' or 'block'") flag.IntVar(&size, "size", 0, "Size of blob to create for a page 'putblob'") flag.StringVar(&pagerange, "pagerange", "", "When uploading to a page blob, this specifies what range in the blob. Use the format 'start-end', e.g. -pagerange 1024-2048") flag.StringVar(&acl, "acl", "", "When using 'containeracl', specify an ACL type") flag.Parse() if help { return "", nil } opName := flag.Arg(0) if opName == "" { return "", fmt.Errorf("No operation specified") } requiredArgs := []string{"account", "location"} for _, arg := range requiredArgs { if !argumentGiven(arg) { return "", fmt.Errorf("Must supply %q parameter.", arg) } } if len(flag.Args()) != 1 { return "", badOperationError() } op, isDefined := operationsByName[opName] if !isDefined { return "", badOperationError() } for _, arg := range op.requiredArgs { if !argumentGiven(arg) { return "", fmt.Errorf("%q requires these options: %v", op.name, op.requiredArgs) } } if op.validate != nil { err := op.validate() if err != nil { return "", err } } return op.name, nil } func Usage() { fmt.Fprintf( os.Stderr, "Usage:\n %s [args] <%s>\n", os.Args[0], strings.Join(operationNames, "|")) flag.PrintDefaults() fmt.Fprintf(os.Stderr, ` This is an example of how to interact with the Azure storage service. It is not a complete example but it does give a useful way to do do some basic operations. The -account param must always be supplied and -key must be supplied for operations that change things, (get these from the Azure web UI) otherwise anonymous access is made. Additionally there are the following command invocation parameters: `) for _, op := range operations { fmt.Fprintf(os.Stderr, "\n %s:\n %s\n", op.description, op.example) } } func dumpError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "ERROR:") fmt.Fprintf(os.Stderr, "%s\n", err) } } func listcontainers(storage gwacl.StorageContext) { res, e := storage.ListAllContainers() if e != nil { dumpError(e) return } for _, c := range res.Containers { // TODO: embellish with the other container data fmt.Println(c.Name) } } func containeracl(storage gwacl.StorageContext) { err := storage.SetContainerACL(&gwacl.SetContainerACLRequest{ Container: container, Access: acl, }) dumpError(err) } func list(storage gwacl.StorageContext) { request := &gwacl.ListBlobsRequest{ Container: container, Prefix: prefix} res, err := storage.ListAllBlobs(request) if err != nil { dumpError(err) return } for _, b := range res.Blobs { fmt.Printf("%s, %s, %s\n", b.ContentLength, b.LastModified, b.Name) } } func addblock(storage gwacl.StorageContext) { var err error file, err := os.Open(filename) if err != nil { dumpError(err) return } defer file.Close() err = storage.UploadBlockBlob(container, filename, file) if err != nil { dumpError(err) return } } func deleteblob(storage gwacl.StorageContext) { err := storage.DeleteBlob(container, filename) dumpError(err) } func getblob(storage gwacl.StorageContext) { var err error file, err := storage.GetBlob(container, filename) if err != nil { dumpError(err) return } data, err := ioutil.ReadAll(file) if err != nil { dumpError(err) return } os.Stdout.Write(data) } func putblob(storage gwacl.StorageContext) { err := storage.PutBlob(&gwacl.PutBlobRequest{ Container: container, BlobType: blobtype, Filename: blobname, Size: size, }) dumpError(err) } func putpage(storage gwacl.StorageContext) { var err error file, err := os.Open(filename) if err != nil { dumpError(err) return } defer file.Close() var start, end int fmt.Sscanf(pagerange, "%d-%d", &start, &end) err = storage.PutPage(&gwacl.PutPageRequest{ Container: container, Filename: blobname, StartRange: start, EndRange: end, Data: file, }) if err != nil { dumpError(err) return } } func main() { flag.Usage = Usage var err error op, err := getParams() if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err.Error()) fmt.Fprintf(os.Stderr, "Use -h for help with using this program.\n") os.Exit(1) } if help { Usage() os.Exit(0) } storage := gwacl.StorageContext{ Account: account, Key: key, AzureEndpoint: gwacl.GetEndpoint(location), } perform := operationsByName[op].implementation perform(storage) } ����������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/names.go���������������������������������������������������0000644�0000153�0000161�00000012471�12321735761�021520� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( "fmt" ) // pickOne returns a random choice of one of the characters in chars. func pickOne(chars string) string { index := random.Intn(len(chars)) return string(chars[index]) } const ( upperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" letters = "abcdefghijklmnopqrstuvwxyz" digits = "0123456789" ) // makeRandomIdentifier creates an arbitrary identifier of the given length, // consisting of only ASCII digits and lower-case ASCII letters. // The identifier will start with the given prefix. The prefix must be no // longer than the specified length, or there'll be trouble. func makeRandomIdentifier(prefix string, length int) string { // Only digits and lower-case ASCII letters are accepted. const ( chars = letters + digits ) if len(prefix) > length { panic(fmt.Errorf("prefix '%s' is more than the requested %d characters long", prefix, length)) } if len(prefix)+5 > length { panic(fmt.Errorf( "prefix '%s' is too long; space is needed for at least 5 random characters, only %d given", prefix, length-len(prefix))) } if len(prefix) == 0 { // No prefix. Still have to start with a letter, so pick one. prefix = pickOne(letters) } id := prefix for len(id) < length { id += pickOne(chars) } return id } const ( // We don't know of any documentation on how long a hosted-service name can // be, but this is the maximum length that worked in experiments. HostedServiceNameMaxiumSize = 63 // The number of random characters used when generating random Hosted // Service names. HostedServiceNameRandomChars = 10 // The maximum length allowed for a Hosted Service name prefix (as passed // to MakeRandomHostedServiceName().) HostedServiceNameMaximumPrefixSize = HostedServiceNameMaxiumSize - HostedServiceNameRandomChars ) // MakeRandomHostedServiceName generates a pseudo-random name for a hosted // service, with the given prefix. // // The prefix must be as short as possible, be entirely in ASCII, start with // a lower-case letter, and contain only lower-case letters and digits after // that. func MakeRandomHostedServiceName(prefix string) string { // We don't know of any documentation on long a hosted-service name can // be, but this is the maximum length that worked in experiments. size := len(prefix) + HostedServiceNameRandomChars if size > HostedServiceNameMaxiumSize { panic(fmt.Errorf("prefix '%s' is too long; it can be at most %d characters", prefix, HostedServiceNameMaximumPrefixSize)) } return makeRandomIdentifier(prefix, size) } // MakeRandomHostname generates a pseudo-random hostname for a virtual machine, // with the given prefix. // // The prefix must be as short as possible, be entirely in ASCII, start with // a lower-case letter, and contain only lower-case letters and digits after // that. func MakeRandomHostname(prefix string) string { // Azure documentation says the hostname can be between 1 and 64 // letters long, but in practice we found it didn't work with anything // over 55 letters long. return makeRandomIdentifier(prefix, 55) } // MakeRandomDiskName generates a pseudo-random disk name for a virtual machine // with the given prefix. // // The prefix must be as short as possible, be entirely in ASCII, start with // a lower-case letter, and contain only lower-case letters and digits after // that. func MakeRandomDiskName(prefix string) string { // Azure documentation does not say what the maximum size of a disk name // is. Testing indicate that 50 works. return makeRandomIdentifier(prefix, 50) } // MakeRandomRoleName generates a pseudo-random role name for a virtual machine // with the given prefix. // // The prefix must be as short as possible, be entirely in ASCII, start with // a lower-case letter, and contain only lower-case letters and digits after // that. func MakeRandomRoleName(prefix string) string { // Azure documentation does not say what the maximum size of a role name // is. Testing indicate that 50 works. return makeRandomIdentifier(prefix, 50) } // MakeRandomVirtualNetworkName generates a pseudo-random name for a virtual // network with the given prefix. // // The prefix must be as short as possible, be entirely in ASCII, start with // a lower-case letter, and contain only lower-case letters and digits after // that. func MakeRandomVirtualNetworkName(prefix string) string { return makeRandomIdentifier(prefix, 20) } const ( // Valid passwords must be 6-72 characters long. passwordSize = 50 ) // MakeRandomPassword generates a pseudo-random password for a Linux Virtual // Machine. func MakeRandomPassword() string { const chars = letters + digits + upperCaseLetters upperCaseLetter := pickOne(upperCaseLetters) letter := pickOne(letters) digit := pickOne(digits) // Make sure the password has at least one letter, one upper-case letter // and a digit to meet Azure's password complexity requirements. password := letter + upperCaseLetter + digit for len(password) < passwordSize { password += pickOne(chars) } return password } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/testhelpers_x509dispatch.go��������������������������������0000644�0000153�0000161�00000006147�12321735761�025267� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). // // Helpers for testing with x509 requests. These help inject fake responses // into the x509 request dispatcher. package gwacl import ( "launchpad.net/gwacl/fork/http" ) // rigRecordingDispatcher sets up a request dispatcher that records incoming // requests by appending them to *record. It returns the result of whatever // dispatcher was already active. // If you also want the dispatcher to return a particular result, rig it for // that result first (using one of the other rig...Dispatcher functions) and // then chain the recording dispatcher in front of it. func rigRecordingDispatcher(record *[]*X509Request) { previousDispatcher := _X509Dispatcher _X509Dispatcher = func(session *x509Session, request *X509Request) (*x509Response, error) { *record = append(*record, request) return previousDispatcher(session, request) } } // rigFixedResponseDispatcher sets up a request dispatcher that always returns // a prepared response. func rigFixedResponseDispatcher(response *x509Response) { _X509Dispatcher = func(*x509Session, *X509Request) (*x509Response, error) { return response, nil } } // rigFailingDispatcher sets up a request dispatcher that returns a given // error. func rigFailingDispatcher(failure error) { _X509Dispatcher = func(*x509Session, *X509Request) (*x509Response, error) { return nil, failure } } type DispatcherResponse struct { response *x509Response errorObject error } // rigPreparedResponseDispatcher sets up a request dispatcher that returns, // for each consecutive request, the next of a series of prepared responses. func rigPreparedResponseDispatcher(responses []DispatcherResponse) { index := 0 _X509Dispatcher = func(*x509Session, *X509Request) (*x509Response, error) { response := responses[index] index += 1 return response.response, response.errorObject } } // rigRecordingPreparedResponseDispatcher sets up a request dispatcher that // returns, for each consecutive request, the next of a series of prepared // responses, and records each request. func rigRecordingPreparedResponseDispatcher(record *[]*X509Request, responses []DispatcherResponse) { index := 0 _X509Dispatcher = func(session *x509Session, request *X509Request) (*x509Response, error) { *record = append(*record, request) response := responses[index] index += 1 return response.response, response.errorObject } } // setUpDispatcher sets up a request dispatcher that: // - records requests // - returns empty responses func setUpDispatcher(operationID string) *[]*X509Request { header := http.Header{} header.Set("X-Ms-Request-Id", operationID) fixedResponse := x509Response{ StatusCode: http.StatusOK, Body: []byte{}, Header: header, } rigFixedResponseDispatcher(&fixedResponse) recordedRequests := make([]*X509Request, 0) rigRecordingDispatcher(&recordedRequests) return &recordedRequests } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gwacl/master_test.go���������������������������������������������0000644�0000153�0000161�00000000441�12321735761�022741� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gwacl import ( . "launchpad.net/gocheck" "testing" ) // Master loader for all tests. func Test(t *testing.T) { TestingT(t) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/�������������������������������������������������������0000755�0000153�0000161�00000000000�12321736016�020717� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/COPYING.LESSER�����������������������������������������0000644�0000153�0000161�00000016743�12321735741�022765� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �����������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/util_test.go�������������������������������������������0000644�0000153�0000161�00000002141�12321735741�023264� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( . "launchpad.net/gocheck" ) func (suite *GomaasapiTestSuite) TestJoinURLsAppendsPathToBaseURL(c *C) { c.Check(JoinURLs("http://example.com/", "foo"), Equals, "http://example.com/foo") } func (suite *GomaasapiTestSuite) TestJoinURLsAddsSlashIfNeeded(c *C) { c.Check(JoinURLs("http://example.com/foo", "bar"), Equals, "http://example.com/foo/bar") } func (suite *GomaasapiTestSuite) TestJoinURLsNormalizesDoubleSlash(c *C) { c.Check(JoinURLs("http://example.com/base/", "/szot"), Equals, "http://example.com/base/szot") } func (suite *GomaasapiTestSuite) TestEnsureTrailingSlashAppendsSlashIfMissing(c *C) { c.Check(EnsureTrailingSlash("test"), Equals, "test/") } func (suite *GomaasapiTestSuite) TestEnsureTrailingSlashDoesNotAppendIfPresent(c *C) { c.Check(EnsureTrailingSlash("test/"), Equals, "test/") } func (suite *GomaasapiTestSuite) TestEnsureTrailingSlashReturnsSlashIfEmpty(c *C) { c.Check(EnsureTrailingSlash(""), Equals, "/") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/testing.go���������������������������������������������0000644�0000153�0000161�00000002522�12321735741�022730� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "fmt" "net/http" "net/http/httptest" ) type singleServingServer struct { *httptest.Server requestContent *string requestHeader *http.Header } // newSingleServingServer creates a single-serving test http server which will // return only one response as defined by the passed arguments. func newSingleServingServer(uri string, response string, code int) *singleServingServer { var requestContent string var requestHeader http.Header var requested bool handler := func(writer http.ResponseWriter, request *http.Request) { if requested { http.Error(writer, "Already requested", http.StatusServiceUnavailable) } res, err := readAndClose(request.Body) if err != nil { panic(err) } requestContent = string(res) requestHeader = request.Header if request.URL.String() != uri { errorMsg := fmt.Sprintf("Error 404: page not found (expected '%v', got '%v').", uri, request.URL.String()) http.Error(writer, errorMsg, http.StatusNotFound) } else { writer.WriteHeader(code) fmt.Fprint(writer, response) } requested = true } server := httptest.NewServer(http.HandlerFunc(handler)) return &singleServingServer{server, &requestContent, &requestHeader} } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/client_test.go�����������������������������������������0000644�0000153�0000161�00000020716�12321735741�023575� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "bytes" "fmt" "io/ioutil" . "launchpad.net/gocheck" "net/http" "net/url" "strings" ) type ClientSuite struct{} var _ = Suite(&ClientSuite{}) func (*ClientSuite) TestReadAndCloseReturnsEmptyStringForNil(c *C) { data, err := readAndClose(nil) c.Assert(err, IsNil) c.Check(string(data), Equals, "") } func (*ClientSuite) TestReadAndCloseReturnsContents(c *C) { content := "Stream contents." stream := ioutil.NopCloser(strings.NewReader(content)) data, err := readAndClose(stream) c.Assert(err, IsNil) c.Check(string(data), Equals, content) } func (suite *ClientSuite) TestClientdispatchRequestReturnsServerError(c *C) { URI := "/some/url/?param1=test" expectedResult := "expected:result" server := newSingleServingServer(URI, expectedResult, http.StatusBadRequest) defer server.Close() client, err := NewAnonymousClient(server.URL, "1.0") c.Assert(err, IsNil) request, err := http.NewRequest("GET", server.URL+URI, nil) result, err := client.dispatchRequest(request) expectedErrorString := fmt.Sprintf("gomaasapi: got error back from server: 400 Bad Request (%v)", expectedResult) c.Check(err.Error(), Equals, expectedErrorString) c.Check(err.(ServerError).StatusCode, Equals, 400) c.Check(string(result), Equals, expectedResult) } func (suite *ClientSuite) TestClientDispatchRequestReturnsNonServerError(c *C) { client, err := NewAnonymousClient("/foo", "1.0") c.Assert(err, IsNil) // Create a bad request that will fail to dispatch. request, err := http.NewRequest("GET", "/", nil) c.Assert(err, IsNil) result, err := client.dispatchRequest(request) // This type of failure is an error, but not a ServerError. c.Check(err, NotNil) c.Check(err, Not(FitsTypeOf), ServerError{}) // For this kind of error, result is guaranteed to be nil. c.Check(result, IsNil) } func (suite *ClientSuite) TestClientdispatchRequestSignsRequest(c *C) { URI := "/some/url/?param1=test" expectedResult := "expected:result" server := newSingleServingServer(URI, expectedResult, http.StatusOK) defer server.Close() client, err := NewAuthenticatedClient(server.URL, "the:api:key", "1.0") c.Assert(err, IsNil) request, err := http.NewRequest("GET", server.URL+URI, nil) c.Assert(err, IsNil) result, err := client.dispatchRequest(request) c.Check(err, IsNil) c.Check(string(result), Equals, expectedResult) c.Check((*server.requestHeader)["Authorization"][0], Matches, "^OAuth .*") } func (suite *ClientSuite) TestClientGetFormatsGetParameters(c *C) { URI, err := url.Parse("/some/url") c.Assert(err, IsNil) expectedResult := "expected:result" params := url.Values{"test": {"123"}} fullURI := URI.String() + "?test=123" server := newSingleServingServer(fullURI, expectedResult, http.StatusOK) defer server.Close() client, err := NewAnonymousClient(server.URL, "1.0") c.Assert(err, IsNil) result, err := client.Get(URI, "", params) c.Check(err, IsNil) c.Check(string(result), Equals, expectedResult) } func (suite *ClientSuite) TestClientGetFormatsOperationAsGetParameter(c *C) { URI, err := url.Parse("/some/url") c.Assert(err, IsNil) expectedResult := "expected:result" fullURI := URI.String() + "?op=list" server := newSingleServingServer(fullURI, expectedResult, http.StatusOK) defer server.Close() client, err := NewAnonymousClient(server.URL, "1.0") c.Assert(err, IsNil) result, err := client.Get(URI, "list", nil) c.Check(err, IsNil) c.Check(string(result), Equals, expectedResult) } func (suite *ClientSuite) TestClientPostSendsRequestWithParams(c *C) { URI, err := url.Parse("/some/url") c.Check(err, IsNil) expectedResult := "expected:result" fullURI := URI.String() + "?op=list" params := url.Values{"test": {"123"}} server := newSingleServingServer(fullURI, expectedResult, http.StatusOK) defer server.Close() client, err := NewAnonymousClient(server.URL, "1.0") c.Check(err, IsNil) result, err := client.Post(URI, "list", params, nil) c.Check(err, IsNil) c.Check(string(result), Equals, expectedResult) postedValues, err := url.ParseQuery(*server.requestContent) c.Check(err, IsNil) expectedPostedValues, err := url.ParseQuery("test=123") c.Check(err, IsNil) c.Check(postedValues, DeepEquals, expectedPostedValues) } // extractFileContent extracts from the request built using 'requestContent', // 'requestHeader' and 'requestURL', the file named 'filename'. func extractFileContent(requestContent string, requestHeader *http.Header, requestURL string, filename string) ([]byte, error) { // Recreate the request from server.requestContent to use the parsing // utility from the http package (http.Request.FormFile). request, err := http.NewRequest("POST", requestURL, bytes.NewBufferString(requestContent)) if err != nil { return nil, err } request.Header.Set("Content-Type", requestHeader.Get("Content-Type")) file, _, err := request.FormFile("testfile") if err != nil { return nil, err } fileContent, err := ioutil.ReadAll(file) if err != nil { return nil, err } return fileContent, nil } func (suite *ClientSuite) TestClientPostSendsMultipartRequest(c *C) { URI, err := url.Parse("/some/url") c.Assert(err, IsNil) expectedResult := "expected:result" fullURI := URI.String() + "?op=add" server := newSingleServingServer(fullURI, expectedResult, http.StatusOK) defer server.Close() client, err := NewAnonymousClient(server.URL, "1.0") c.Assert(err, IsNil) fileContent := []byte("content") files := map[string][]byte{"testfile": fileContent} result, err := client.Post(URI, "add", nil, files) c.Check(err, IsNil) c.Check(string(result), Equals, expectedResult) receivedFileContent, err := extractFileContent(*server.requestContent, server.requestHeader, fullURI, "testfile") c.Assert(err, IsNil) c.Check(receivedFileContent, DeepEquals, fileContent) } func (suite *ClientSuite) TestClientPutSendsRequest(c *C) { URI, err := url.Parse("/some/url") c.Assert(err, IsNil) expectedResult := "expected:result" params := url.Values{"test": {"123"}} server := newSingleServingServer(URI.String(), expectedResult, http.StatusOK) defer server.Close() client, err := NewAnonymousClient(server.URL, "1.0") c.Assert(err, IsNil) result, err := client.Put(URI, params) c.Check(err, IsNil) c.Check(string(result), Equals, expectedResult) c.Check(*server.requestContent, Equals, "test=123") } func (suite *ClientSuite) TestClientDeleteSendsRequest(c *C) { URI, err := url.Parse("/some/url") c.Assert(err, IsNil) expectedResult := "expected:result" server := newSingleServingServer(URI.String(), expectedResult, http.StatusOK) defer server.Close() client, err := NewAnonymousClient(server.URL, "1.0") c.Assert(err, IsNil) err = client.Delete(URI) c.Check(err, IsNil) } func (suite *ClientSuite) TestNewAnonymousClientEnsuresTrailingSlash(c *C) { client, err := NewAnonymousClient("http://example.com/", "1.0") c.Check(err, IsNil) expectedURL, err := url.Parse("http://example.com/api/1.0/") c.Assert(err, IsNil) c.Check(client.APIURL, DeepEquals, expectedURL) } func (suite *ClientSuite) TestNewAuthenticatedClientEnsuresTrailingSlash(c *C) { client, err := NewAuthenticatedClient("http://example.com/", "a:b:c", "1.0") c.Check(err, IsNil) expectedURL, err := url.Parse("http://example.com/api/1.0/") c.Assert(err, IsNil) c.Check(client.APIURL, DeepEquals, expectedURL) } func (suite *ClientSuite) TestNewAuthenticatedClientParsesApiKey(c *C) { // NewAuthenticatedClient returns a plainTextOAuthSigneri configured // to use the given API key. consumerKey := "consumerKey" tokenKey := "tokenKey" tokenSecret := "tokenSecret" keyElements := []string{consumerKey, tokenKey, tokenSecret} apiKey := strings.Join(keyElements, ":") client, err := NewAuthenticatedClient("http://example.com/", apiKey, "1.0") c.Check(err, IsNil) signer := client.Signer.(*plainTextOAuthSigner) c.Check(signer.token.ConsumerKey, Equals, consumerKey) c.Check(signer.token.TokenKey, Equals, tokenKey) c.Check(signer.token.TokenSecret, Equals, tokenSecret) } func (suite *ClientSuite) TestNewAuthenticatedClientFailsIfInvalidKey(c *C) { client, err := NewAuthenticatedClient("", "invalid-key", "1.0") c.Check(err, ErrorMatches, "invalid API key.*") c.Check(client, IsNil) } func (suite *ClientSuite) TestcomposeAPIURLReturnsURL(c *C) { apiurl, err := composeAPIURL("http://example.com/MAAS", "1.0") c.Assert(err, IsNil) expectedURL, err := url.Parse("http://example.com/MAAS/api/1.0/") c.Check(expectedURL, DeepEquals, apiurl) } ��������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/oauth.go�����������������������������������������������0000644�0000153�0000161�00000004062�12321735741�022374� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "crypto/rand" "fmt" "math/big" "net/http" "net/url" "strconv" "strings" "time" ) var nonceMax = big.NewInt(100000000) func generateNonce() (string, error) { randInt, err := rand.Int(rand.Reader, nonceMax) if err != nil { return "", err } return strconv.Itoa(int(randInt.Int64())), nil } func generateTimestamp() string { return strconv.Itoa(int(time.Now().Unix())) } type OAuthSigner interface { OAuthSign(request *http.Request) error } type OAuthToken struct { ConsumerKey string ConsumerSecret string TokenKey string TokenSecret string } // Trick to ensure *plainTextOAuthSigner implements the OAuthSigner interface. var _ OAuthSigner = (*plainTextOAuthSigner)(nil) type plainTextOAuthSigner struct { token *OAuthToken realm string } func NewPlainTestOAuthSigner(token *OAuthToken, realm string) (OAuthSigner, error) { return &plainTextOAuthSigner{token, realm}, nil } // OAuthSignPLAINTEXT signs the provided request using the OAuth PLAINTEXT // method: http://oauth.net/core/1.0/#anchor22. func (signer plainTextOAuthSigner) OAuthSign(request *http.Request) error { signature := signer.token.ConsumerSecret + `&` + signer.token.TokenSecret nonce, err := generateNonce() if err != nil { return err } authData := map[string]string{ "realm": signer.realm, "oauth_consumer_key": signer.token.ConsumerKey, "oauth_token": signer.token.TokenKey, "oauth_signature_method": "PLAINTEXT", "oauth_signature": signature, "oauth_timestamp": generateTimestamp(), "oauth_nonce": nonce, "oauth_version": "1.0", } // Build OAuth header. var authHeader []string for key, value := range authData { authHeader = append(authHeader, fmt.Sprintf(`%s="%s"`, key, url.QueryEscape(value))) } strHeader := "OAuth " + strings.Join(authHeader, ", ") request.Header.Add("Authorization", strHeader) return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/maasobject.go������������������������������������������0000644�0000153�0000161�00000014060�12321735741�023363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "encoding/json" "errors" "fmt" "net/url" ) // MAASObject represents a MAAS object as returned by the MAAS API, such as a // Node or a Tag. // You can extract a MAASObject out of a JSONObject using // JSONObject.GetMAASObject. A MAAS API call will usually return either a // MAASObject or a list of MAASObjects. The list itself would be wrapped in // a JSONObject, so if an API call returns a list of objects "l," you first // obtain the array using l.GetArray(). Then, for each item "i" in the array, // obtain the matching MAASObject using i.GetMAASObject(). type MAASObject struct { values map[string]JSONObject client Client uri *url.URL } // newJSONMAASObject creates a new MAAS object. It will panic if the given map // does not contain a valid URL for the 'resource_uri' key. func newJSONMAASObject(jmap map[string]interface{}, client Client) MAASObject { obj, err := maasify(client, jmap).GetMAASObject() if err != nil { panic(err) } return obj } // MarshalJSON tells the standard json package how to serialize a MAASObject. func (obj MAASObject) MarshalJSON() ([]byte, error) { return json.Marshal(obj.GetMap()) } // With MarshalJSON, MAASObject implements json.Marshaler. var _ json.Marshaler = (*MAASObject)(nil) func marshalNode(node MAASObject) string { res, _ := json.Marshal(node) return string(res) } var noResourceURI = errors.New("not a MAAS object: no 'resource_uri' key") // extractURI obtains the "resource_uri" string from a JSONObject map. func extractURI(attrs map[string]JSONObject) (*url.URL, error) { uriEntry, ok := attrs[resourceURI] if !ok { return nil, noResourceURI } uri, err := uriEntry.GetString() if err != nil { return nil, fmt.Errorf("invalid resource_uri: %v", uri) } resourceURL, err := url.Parse(uri) if err != nil { return nil, fmt.Errorf("resource_uri does not contain a valid URL: %v", uri) } return resourceURL, nil } // JSONObject getter for a MAAS object. From a decoding perspective, a // MAASObject is just like a map except it contains a key "resource_uri", and // it keeps track of the Client you got it from so that you can invoke API // methods directly on their MAAS objects. func (obj JSONObject) GetMAASObject() (MAASObject, error) { attrs, err := obj.GetMap() if err != nil { return MAASObject{}, err } uri, err := extractURI(attrs) if err != nil { return MAASObject{}, err } return MAASObject{values: attrs, client: obj.client, uri: uri}, nil } // GetField extracts a string field from this MAAS object. func (obj MAASObject) GetField(name string) (string, error) { return obj.values[name].GetString() } // URI is the resource URI for this MAAS object. It is an absolute path, but // without a network part. func (obj MAASObject) URI() *url.URL { // Duplicate the URL. uri, err := url.Parse(obj.uri.String()) if err != nil { panic(err) } return uri } // URL returns a full absolute URL (including network part) for this MAAS // object on the API. func (obj MAASObject) URL() *url.URL { return obj.client.GetURL(obj.URI()) } // GetMap returns all of the object's attributes in the form of a map. func (obj MAASObject) GetMap() map[string]JSONObject { return obj.values } // GetSubObject returns a new MAASObject representing the API resource found // at a given sub-path of the current object's resource URI. func (obj MAASObject) GetSubObject(name string) MAASObject { uri := obj.URI() newURL := url.URL{Path: name} resUrl := uri.ResolveReference(&newURL) resUrl.Path = EnsureTrailingSlash(resUrl.Path) input := map[string]interface{}{resourceURI: resUrl.String()} return newJSONMAASObject(input, obj.client) } var NotImplemented = errors.New("Not implemented") // Get retrieves a fresh copy of this MAAS object from the API. func (obj MAASObject) Get() (MAASObject, error) { uri := obj.URI() result, err := obj.client.Get(uri, "", url.Values{}) if err != nil { return MAASObject{}, err } jsonObj, err := Parse(obj.client, result) if err != nil { return MAASObject{}, err } return jsonObj.GetMAASObject() } // Post overwrites this object's existing value on the API with those given // in "params." It returns the object's new value as received from the API. func (obj MAASObject) Post(params url.Values) (JSONObject, error) { uri := obj.URI() result, err := obj.client.Post(uri, "", params, nil) if err != nil { return JSONObject{}, err } return Parse(obj.client, result) } // Update modifies this object on the API, based on the values given in // "params." It returns the object's new value as received from the API. func (obj MAASObject) Update(params url.Values) (MAASObject, error) { uri := obj.URI() result, err := obj.client.Put(uri, params) if err != nil { return MAASObject{}, err } jsonObj, err := Parse(obj.client, result) if err != nil { return MAASObject{}, err } return jsonObj.GetMAASObject() } // Delete removes this object on the API. func (obj MAASObject) Delete() error { uri := obj.URI() return obj.client.Delete(uri) } // CallGet invokes an idempotent API method on this object. func (obj MAASObject) CallGet(operation string, params url.Values) (JSONObject, error) { uri := obj.URI() result, err := obj.client.Get(uri, operation, params) if err != nil { return JSONObject{}, err } return Parse(obj.client, result) } // CallPost invokes a non-idempotent API method on this object. func (obj MAASObject) CallPost(operation string, params url.Values) (JSONObject, error) { return obj.CallPostFiles(operation, params, nil) } // CallPostFiles invokes a non-idempotent API method on this object. It is // similar to CallPost but has an extra parameter, 'files', which should // contain the files that will be uploaded to the API. func (obj MAASObject) CallPostFiles(operation string, params url.Values, files map[string][]byte) (JSONObject, error) { uri := obj.URI() result, err := obj.client.Post(uri, operation, params, files) if err != nil { return JSONObject{}, err } return Parse(obj.client, result) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/gomaasapi.go�������������������������������������������0000644�0000153�0000161�00000000240�12321735741�023207� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/util.go������������������������������������������������0000644�0000153�0000161�00000001672�12321735741�022235� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "strings" ) // JoinURLs joins a base URL and a subpath together. // Regardless of whether baseURL ends in a trailing slash (or even multiple // trailing slashes), or whether there are any leading slashes at the begining // of path, the two will always be joined together by a single slash. func JoinURLs(baseURL, path string) string { return strings.TrimRight(baseURL, "/") + "/" + strings.TrimLeft(path, "/") } // EnsureTrailingSlash appends a slash at the end of the given string unless // there already is one. // This is used to create the kind of normalized URLs that Django expects. // (to avoid Django's redirection when an URL does not ends with a slash.) func EnsureTrailingSlash(URL string) string { if strings.HasSuffix(URL, "/") { return URL } return URL + "/" } ����������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/jsonobject_test.go�������������������������������������0000644�0000153�0000161�00000032264�12321735741�024460� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "encoding/json" "fmt" . "launchpad.net/gocheck" ) type JSONObjectSuite struct { } var _ = Suite(&JSONObjectSuite{}) // maasify() converts nil. func (suite *JSONObjectSuite) TestMaasifyConvertsNil(c *C) { c.Check(maasify(Client{}, nil).IsNil(), Equals, true) } // maasify() converts strings. func (suite *JSONObjectSuite) TestMaasifyConvertsString(c *C) { const text = "Hello" out, err := maasify(Client{}, text).GetString() c.Assert(err, IsNil) c.Check(out, Equals, text) } // maasify() converts float64 numbers. func (suite *JSONObjectSuite) TestMaasifyConvertsNumber(c *C) { const number = 3.1415926535 num, err := maasify(Client{}, number).GetFloat64() c.Assert(err, IsNil) c.Check(num, Equals, number) } // maasify() converts array slices. func (suite *JSONObjectSuite) TestMaasifyConvertsArray(c *C) { original := []interface{}{3.0, 2.0, 1.0} output, err := maasify(Client{}, original).GetArray() c.Assert(err, IsNil) c.Check(len(output), Equals, len(original)) } // When maasify() converts an array slice, the result contains JSONObjects. func (suite *JSONObjectSuite) TestMaasifyArrayContainsJSONObjects(c *C) { arr, err := maasify(Client{}, []interface{}{9.9}).GetArray() c.Assert(err, IsNil) var _ JSONObject = arr[0] entry, err := arr[0].GetFloat64() c.Assert(err, IsNil) c.Check(entry, Equals, 9.9) } // maasify() converts maps. func (suite *JSONObjectSuite) TestMaasifyConvertsMap(c *C) { original := map[string]interface{}{"1": "one", "2": "two", "3": "three"} output, err := maasify(Client{}, original).GetMap() c.Assert(err, IsNil) c.Check(len(output), Equals, len(original)) } // When maasify() converts a map, the result contains JSONObjects. func (suite *JSONObjectSuite) TestMaasifyMapContainsJSONObjects(c *C) { jsonobj := maasify(Client{}, map[string]interface{}{"key": "value"}) mp, err := jsonobj.GetMap() var _ JSONObject = mp["key"] c.Assert(err, IsNil) entry, err := mp["key"].GetString() c.Check(entry, Equals, "value") } // maasify() converts MAAS objects. func (suite *JSONObjectSuite) TestMaasifyConvertsMAASObject(c *C) { original := map[string]interface{}{ "resource_uri": "http://example.com/foo", "size": "3", } obj, err := maasify(Client{}, original).GetMAASObject() c.Assert(err, IsNil) c.Check(len(obj.GetMap()), Equals, len(original)) size, err := obj.GetMap()["size"].GetString() c.Assert(err, IsNil) c.Check(size, Equals, "3") } // maasify() passes its client to a MAASObject it creates. func (suite *JSONObjectSuite) TestMaasifyPassesClientToMAASObject(c *C) { client := Client{} original := map[string]interface{}{"resource_uri": "/foo"} output, err := maasify(client, original).GetMAASObject() c.Assert(err, IsNil) c.Check(output.client, Equals, client) } // maasify() passes its client into an array of MAASObjects it creates. func (suite *JSONObjectSuite) TestMaasifyPassesClientIntoArray(c *C) { client := Client{} obj := map[string]interface{}{"resource_uri": "/foo"} list := []interface{}{obj} jsonobj, err := maasify(client, list).GetArray() c.Assert(err, IsNil) out, err := jsonobj[0].GetMAASObject() c.Assert(err, IsNil) c.Check(out.client, Equals, client) } // maasify() passes its client into a map of MAASObjects it creates. func (suite *JSONObjectSuite) TestMaasifyPassesClientIntoMap(c *C) { client := Client{} obj := map[string]interface{}{"resource_uri": "/foo"} mp := map[string]interface{}{"key": obj} jsonobj, err := maasify(client, mp).GetMap() c.Assert(err, IsNil) out, err := jsonobj["key"].GetMAASObject() c.Assert(err, IsNil) c.Check(out.client, Equals, client) } // maasify() passes its client all the way down into any MAASObjects in the // object structure it creates. func (suite *JSONObjectSuite) TestMaasifyPassesClientAllTheWay(c *C) { client := Client{} obj := map[string]interface{}{"resource_uri": "/foo"} mp := map[string]interface{}{"key": obj} list := []interface{}{mp} jsonobj, err := maasify(client, list).GetArray() c.Assert(err, IsNil) outerMap, err := jsonobj[0].GetMap() c.Assert(err, IsNil) out, err := outerMap["key"].GetMAASObject() c.Assert(err, IsNil) c.Check(out.client, Equals, client) } // maasify() converts Booleans. func (suite *JSONObjectSuite) TestMaasifyConvertsBool(c *C) { t, err := maasify(Client{}, true).GetBool() c.Assert(err, IsNil) f, err := maasify(Client{}, false).GetBool() c.Assert(err, IsNil) c.Check(t, Equals, true) c.Check(f, Equals, false) } // Parse takes you from a JSON blob to a JSONObject. func (suite *JSONObjectSuite) TestParseMaasifiesJSONBlob(c *C) { blob := []byte("[12]") obj, err := Parse(Client{}, blob) c.Assert(err, IsNil) arr, err := obj.GetArray() c.Assert(err, IsNil) out, err := arr[0].GetFloat64() c.Assert(err, IsNil) c.Check(out, Equals, 12.0) } func (suite *JSONObjectSuite) TestParseKeepsBinaryOriginal(c *C) { blob := []byte(`"Hi"`) obj, err := Parse(Client{}, blob) c.Assert(err, IsNil) text, err := obj.GetString() c.Assert(err, IsNil) c.Check(text, Equals, "Hi") binary, err := obj.GetBytes() c.Assert(err, IsNil) c.Check(binary, DeepEquals, blob) } func (suite *JSONObjectSuite) TestParseTreatsInvalidJSONAsBinary(c *C) { blob := []byte("?x]}y![{z") obj, err := Parse(Client{}, blob) c.Assert(err, IsNil) c.Check(obj.IsNil(), Equals, false) c.Check(obj.value, IsNil) binary, err := obj.GetBytes() c.Assert(err, IsNil) c.Check(binary, DeepEquals, blob) } func (suite *JSONObjectSuite) TestParseTreatsInvalidUTF8AsBinary(c *C) { // Arbitrary data that is definitely not UTF-8. blob := []byte{220, 8, 129} obj, err := Parse(Client{}, blob) c.Assert(err, IsNil) c.Check(obj.IsNil(), Equals, false) c.Check(obj.value, IsNil) binary, err := obj.GetBytes() c.Assert(err, IsNil) c.Check(binary, DeepEquals, blob) } func (suite *JSONObjectSuite) TestParseTreatsEmptyJSONAsBinary(c *C) { blob := []byte{} obj, err := Parse(Client{}, blob) c.Assert(err, IsNil) c.Check(obj.IsNil(), Equals, false) data, err := obj.GetBytes() c.Assert(err, IsNil) c.Check(data, DeepEquals, blob) } func (suite *JSONObjectSuite) TestParsePanicsOnNilJSON(c *C) { defer func() { failure := recover() c.Assert(failure, NotNil) c.Check(failure.(error).Error(), Matches, ".*nil input") }() Parse(Client{}, nil) } func (suite *JSONObjectSuite) TestParseNullProducesIsNil(c *C) { blob := []byte("null") obj, err := Parse(Client{}, blob) c.Assert(err, IsNil) c.Check(obj.IsNil(), Equals, true) } func (suite *JSONObjectSuite) TestParseNonNullProducesNonIsNil(c *C) { blob := []byte("1") obj, err := Parse(Client{}, blob) c.Assert(err, IsNil) c.Check(obj.IsNil(), Equals, false) } func (suite *JSONObjectSuite) TestParseSpacedNullProducesIsNil(c *C) { blob := []byte(" null ") obj, err := Parse(Client{}, blob) c.Assert(err, IsNil) c.Check(obj.IsNil(), Equals, true) } // String-type JSONObjects convert only to string. func (suite *JSONObjectSuite) TestConversionsString(c *C) { obj := maasify(Client{}, "Test string") value, err := obj.GetString() c.Check(err, IsNil) c.Check(value, Equals, "Test string") _, err = obj.GetFloat64() c.Check(err, NotNil) _, err = obj.GetMap() c.Check(err, NotNil) _, err = obj.GetMAASObject() c.Check(err, NotNil) _, err = obj.GetArray() c.Check(err, NotNil) _, err = obj.GetBool() c.Check(err, NotNil) } // Number-type JSONObjects convert only to float64. func (suite *JSONObjectSuite) TestConversionsFloat64(c *C) { obj := maasify(Client{}, 1.1) value, err := obj.GetFloat64() c.Check(err, IsNil) c.Check(value, Equals, 1.1) _, err = obj.GetString() c.Check(err, NotNil) _, err = obj.GetMap() c.Check(err, NotNil) _, err = obj.GetMAASObject() c.Check(err, NotNil) _, err = obj.GetArray() c.Check(err, NotNil) _, err = obj.GetBool() c.Check(err, NotNil) } // Map-type JSONObjects convert only to map. func (suite *JSONObjectSuite) TestConversionsMap(c *C) { obj := maasify(Client{}, map[string]interface{}{"x": "y"}) value, err := obj.GetMap() c.Check(err, IsNil) text, err := value["x"].GetString() c.Check(err, IsNil) c.Check(text, Equals, "y") _, err = obj.GetString() c.Check(err, NotNil) _, err = obj.GetFloat64() c.Check(err, NotNil) _, err = obj.GetMAASObject() c.Check(err, NotNil) _, err = obj.GetArray() c.Check(err, NotNil) _, err = obj.GetBool() c.Check(err, NotNil) } // Array-type JSONObjects convert only to array. func (suite *JSONObjectSuite) TestConversionsArray(c *C) { obj := maasify(Client{}, []interface{}{"item"}) value, err := obj.GetArray() c.Check(err, IsNil) text, err := value[0].GetString() c.Check(err, IsNil) c.Check(text, Equals, "item") _, err = obj.GetString() c.Check(err, NotNil) _, err = obj.GetFloat64() c.Check(err, NotNil) _, err = obj.GetMap() c.Check(err, NotNil) _, err = obj.GetMAASObject() c.Check(err, NotNil) _, err = obj.GetBool() c.Check(err, NotNil) } // Boolean-type JSONObjects convert only to bool. func (suite *JSONObjectSuite) TestConversionsBool(c *C) { obj := maasify(Client{}, false) value, err := obj.GetBool() c.Check(err, IsNil) c.Check(value, Equals, false) _, err = obj.GetString() c.Check(err, NotNil) _, err = obj.GetFloat64() c.Check(err, NotNil) _, err = obj.GetMap() c.Check(err, NotNil) _, err = obj.GetMAASObject() c.Check(err, NotNil) _, err = obj.GetArray() c.Check(err, NotNil) } func (suite *JSONObjectSuite) TestNilSerializesToJSON(c *C) { output, err := json.Marshal(maasify(Client{}, nil)) c.Assert(err, IsNil) c.Check(output, DeepEquals, []byte("null")) } func (suite *JSONObjectSuite) TestEmptyStringSerializesToJSON(c *C) { output, err := json.Marshal(maasify(Client{}, "")) c.Assert(err, IsNil) c.Check(string(output), Equals, `""`) } func (suite *JSONObjectSuite) TestStringSerializesToJSON(c *C) { text := "Text wrapped in JSON" output, err := json.Marshal(maasify(Client{}, text)) c.Assert(err, IsNil) c.Check(output, DeepEquals, []byte(fmt.Sprintf(`"%s"`, text))) } func (suite *JSONObjectSuite) TestStringIsEscapedInJSON(c *C) { text := `\"Quote,\" \\backslash, and \'apostrophe\'.` output, err := json.Marshal(maasify(Client{}, text)) c.Assert(err, IsNil) var deserialized string err = json.Unmarshal(output, &deserialized) c.Assert(err, IsNil) c.Check(deserialized, Equals, text) } func (suite *JSONObjectSuite) TestFloat64SerializesToJSON(c *C) { number := 3.1415926535 output, err := json.Marshal(maasify(Client{}, number)) c.Assert(err, IsNil) var deserialized float64 err = json.Unmarshal(output, &deserialized) c.Assert(err, IsNil) c.Check(deserialized, Equals, number) } func (suite *JSONObjectSuite) TestEmptyMapSerializesToJSON(c *C) { mp := map[string]interface{}{} output, err := json.Marshal(maasify(Client{}, mp)) c.Assert(err, IsNil) var deserialized interface{} err = json.Unmarshal(output, &deserialized) c.Assert(err, IsNil) c.Check(deserialized.(map[string]interface{}), DeepEquals, mp) } func (suite *JSONObjectSuite) TestMapSerializesToJSON(c *C) { // Sample data: counting in Japanese. mp := map[string]interface{}{"one": "ichi", "two": "nii", "three": "san"} output, err := json.Marshal(maasify(Client{}, mp)) c.Assert(err, IsNil) var deserialized interface{} err = json.Unmarshal(output, &deserialized) c.Assert(err, IsNil) c.Check(deserialized.(map[string]interface{}), DeepEquals, mp) } func (suite *JSONObjectSuite) TestEmptyArraySerializesToJSON(c *C) { arr := []interface{}{} output, err := json.Marshal(maasify(Client{}, arr)) c.Assert(err, IsNil) var deserialized interface{} err = json.Unmarshal(output, &deserialized) c.Assert(err, IsNil) // The deserialized value is a slice, and it contains no elements. // Can't do a regular comparison here because at least in the current // json implementation, an empty list deserializes as a nil slice, // not as an empty slice! // (It doesn't work that way for maps though, for some reason). c.Check(len(deserialized.([]interface{})), Equals, len(arr)) } func (suite *JSONObjectSuite) TestArrayOfStringsSerializesToJSON(c *C) { value := "item" output, err := json.Marshal(maasify(Client{}, []interface{}{value})) c.Assert(err, IsNil) var deserialized []string err = json.Unmarshal(output, &deserialized) c.Assert(err, IsNil) c.Check(deserialized, DeepEquals, []string{value}) } func (suite *JSONObjectSuite) TestArrayOfNumbersSerializesToJSON(c *C) { value := 9.0 output, err := json.Marshal(maasify(Client{}, []interface{}{value})) c.Assert(err, IsNil) var deserialized []float64 err = json.Unmarshal(output, &deserialized) c.Assert(err, IsNil) c.Check(deserialized, DeepEquals, []float64{value}) } func (suite *JSONObjectSuite) TestArrayPreservesOrderInJSON(c *C) { // Sample data: counting in Korean. arr := []interface{}{"jong", "il", "ee", "sam"} output, err := json.Marshal(maasify(Client{}, arr)) c.Assert(err, IsNil) var deserialized []interface{} err = json.Unmarshal(output, &deserialized) c.Assert(err, IsNil) c.Check(deserialized, DeepEquals, arr) } func (suite *JSONObjectSuite) TestBoolSerializesToJSON(c *C) { f, err := json.Marshal(maasify(Client{}, false)) c.Assert(err, IsNil) t, err := json.Marshal(maasify(Client{}, true)) c.Assert(err, IsNil) c.Check(f, DeepEquals, []byte("false")) c.Check(t, DeepEquals, []byte("true")) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/README�������������������������������������������������0000644�0000153�0000161�00000000476�12321735741�021612� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.. -*- mode: rst -*- ****************************** MAAS API client library for Go ****************************** This library serves as a minimal client for communicating with the MAAS web API in Go programs. For more information see the `project homepage`_. .. _project homepage: https://launchpad.net/gomaasapi ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/maas_test.go�������������������������������������������0000644�0000153�0000161�00000001022�12321735741�023225� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( . "launchpad.net/gocheck" "net/url" ) type MAASSuite struct{} var _ = Suite(&MAASSuite{}) func (suite *MAASSuite) TestNewMAASUsesBaseURLFromClient(c *C) { baseURLString := "https://server.com:888/" baseURL, _ := url.Parse(baseURLString) client := Client{APIURL: baseURL} maas := NewMAAS(client) URL := maas.URL() c.Check(URL, DeepEquals, baseURL) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/Makefile�����������������������������������������������0000644�0000153�0000161�00000001033�12321735741�022360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Build, and run tests. check: examples go test ./... example_source := $(wildcard example/*.go) example_binaries := $(patsubst %.go,%,$(example_source)) # Clean up binaries. clean: $(RM) $(example_binaries) # Reformat the source files to match our layout standards. format: gofmt -w . # Invoke gofmt's "simplify" option to streamline the source code. simplify: gofmt -w -s . # Build the examples (we have no tests for them). examples: $(example_binaries) %: %.go go build -o $@ $< .PHONY: check clean format examples simplify �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/client.go����������������������������������������������0000644�0000153�0000161�00000020371�12321735741�022533� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "bytes" "fmt" "io" "io/ioutil" "mime/multipart" "net/http" "net/url" "strings" ) // Client represents a way to communicating with a MAAS API instance. // It is stateless, so it can have concurrent requests in progress. type Client struct { APIURL *url.URL Signer OAuthSigner } // ServerError is an http error (or at least, a non-2xx result) received from // the server. It contains the numerical HTTP status code as well as an error // string. type ServerError struct { error StatusCode int } // readAndClose reads and closes the given ReadCloser. // // Trying to read from a nil simply returns nil, no error. func readAndClose(stream io.ReadCloser) ([]byte, error) { if stream == nil { return nil, nil } defer stream.Close() return ioutil.ReadAll(stream) } // dispatchRequest sends a request to the server, and interprets the response. // Client-side errors will return an empty response and a non-nil error. For // server-side errors however (i.e. responses with a non 2XX status code), the // returned error will be ServerError and the returned body will reflect the // server's response. func (client Client) dispatchRequest(request *http.Request) ([]byte, error) { client.Signer.OAuthSign(request) httpClient := http.Client{} // See https://code.google.com/p/go/issues/detail?id=4677 // We need to force the connection to close each time so that we don't // hit the above Go bug. request.Close = true response, err := httpClient.Do(request) if err != nil { return nil, err } body, err := readAndClose(response.Body) if err != nil { return nil, err } if response.StatusCode < 200 || response.StatusCode > 299 { msg := fmt.Errorf("gomaasapi: got error back from server: %v (%v)", response.Status, string(body)) return body, ServerError{error: msg, StatusCode: response.StatusCode} } return body, nil } // GetURL returns the URL to a given resource on the API, based on its URI. // The resource URI may be absolute or relative; either way the result is a // full absolute URL including the network part. func (client Client) GetURL(uri *url.URL) *url.URL { return client.APIURL.ResolveReference(uri) } // Get performs an HTTP "GET" to the API. This may be either an API method // invocation (if you pass its name in "operation") or plain resource // retrieval (if you leave "operation" blank). func (client Client) Get(uri *url.URL, operation string, parameters url.Values) ([]byte, error) { if parameters == nil { parameters = make(url.Values) } opParameter := parameters.Get("op") if opParameter != "" { msg := fmt.Errorf("reserved parameter 'op' passed (with value '%s')", opParameter) return nil, msg } if operation != "" { parameters.Set("op", operation) } queryUrl := client.GetURL(uri) queryUrl.RawQuery = parameters.Encode() request, err := http.NewRequest("GET", queryUrl.String(), nil) if err != nil { return nil, err } return client.dispatchRequest(request) } // writeMultiPartFiles writes the given files as parts of a multipart message // using the given writer. func writeMultiPartFiles(writer *multipart.Writer, files map[string][]byte) error { for fileName, fileContent := range files { fw, err := writer.CreateFormFile(fileName, fileName) if err != nil { return err } io.Copy(fw, bytes.NewBuffer(fileContent)) } return nil } // writeMultiPartParams writes the given parameters as parts of a multipart // message using the given writer. func writeMultiPartParams(writer *multipart.Writer, parameters url.Values) error { for key, values := range parameters { for _, value := range values { fw, err := writer.CreateFormField(key) if err != nil { return err } buffer := bytes.NewBufferString(value) io.Copy(fw, buffer) } } return nil } // nonIdempotentRequestFiles implements the common functionality of PUT and // POST requests (but not GET or DELETE requests) when uploading files is // needed. func (client Client) nonIdempotentRequestFiles(method string, uri *url.URL, parameters url.Values, files map[string][]byte) ([]byte, error) { buf := new(bytes.Buffer) writer := multipart.NewWriter(buf) err := writeMultiPartFiles(writer, files) if err != nil { return nil, err } err = writeMultiPartParams(writer, parameters) if err != nil { return nil, err } writer.Close() url := client.GetURL(uri) request, err := http.NewRequest(method, url.String(), buf) if err != nil { return nil, err } request.Header.Set("Content-Type", writer.FormDataContentType()) return client.dispatchRequest(request) } // nonIdempotentRequest implements the common functionality of PUT and POST // requests (but not GET or DELETE requests). func (client Client) nonIdempotentRequest(method string, uri *url.URL, parameters url.Values) ([]byte, error) { url := client.GetURL(uri) request, err := http.NewRequest(method, url.String(), strings.NewReader(string(parameters.Encode()))) if err != nil { return nil, err } request.Header.Set("Content-Type", "application/x-www-form-urlencoded") return client.dispatchRequest(request) } // Post performs an HTTP "POST" to the API. This may be either an API method // invocation (if you pass its name in "operation") or plain resource // retrieval (if you leave "operation" blank). func (client Client) Post(uri *url.URL, operation string, parameters url.Values, files map[string][]byte) ([]byte, error) { queryParams := url.Values{"op": {operation}} uri.RawQuery = queryParams.Encode() if files != nil { return client.nonIdempotentRequestFiles("POST", uri, parameters, files) } return client.nonIdempotentRequest("POST", uri, parameters) } // Put updates an object on the API, using an HTTP "PUT" request. func (client Client) Put(uri *url.URL, parameters url.Values) ([]byte, error) { return client.nonIdempotentRequest("PUT", uri, parameters) } // Delete deletes an object on the API, using an HTTP "DELETE" request. func (client Client) Delete(uri *url.URL) error { url := client.GetURL(uri) request, err := http.NewRequest("DELETE", url.String(), strings.NewReader("")) if err != nil { return err } _, err = client.dispatchRequest(request) if err != nil { return err } return nil } // Anonymous "signature method" implementation. type anonSigner struct{} func (signer anonSigner) OAuthSign(request *http.Request) error { return nil } // *anonSigner implements the OAuthSigner interface. var _ OAuthSigner = anonSigner{} func composeAPIURL(BaseURL string, apiVersion string) (*url.URL, error) { baseurl := EnsureTrailingSlash(BaseURL) apiurl := fmt.Sprintf("%sapi/%s/", baseurl, apiVersion) return url.Parse(apiurl) } // NewAnonymousClient creates a client that issues anonymous requests. // BaseURL should refer to the root of the MAAS server path, e.g. // http://my.maas.server.example.com/MAAS/ // apiVersion should contain the version of the MAAS API that you want to use. func NewAnonymousClient(BaseURL string, apiVersion string) (*Client, error) { parsedBaseURL, err := composeAPIURL(BaseURL, apiVersion) if err != nil { return nil, err } return &Client{Signer: &anonSigner{}, APIURL: parsedBaseURL}, nil } // NewAuthenticatedClient parses the given MAAS API key into the individual // OAuth tokens and creates an Client that will use these tokens to sign the // requests it issues. // BaseURL should refer to the root of the MAAS server path, e.g. // http://my.maas.server.example.com/MAAS/ // apiVersion should contain the version of the MAAS API that you want to use. func NewAuthenticatedClient(BaseURL string, apiKey string, apiVersion string) (*Client, error) { elements := strings.Split(apiKey, ":") if len(elements) != 3 { errString := "invalid API key %q; expected \"<consumer secret>:<token key>:<token secret>\"" return nil, fmt.Errorf(errString, apiKey) } token := &OAuthToken{ ConsumerKey: elements[0], // The consumer secret is the empty string in MAAS' authentication. ConsumerSecret: "", TokenKey: elements[1], TokenSecret: elements[2], } signer, err := NewPlainTestOAuthSigner(token, "MAAS API") if err != nil { return nil, err } parsedBaseURL, err := composeAPIURL(BaseURL, apiVersion) if err != nil { return nil, err } return &Client{Signer: signer, APIURL: parsedBaseURL}, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/enum.go������������������������������������������������0000644�0000153�0000161�00000001720�12321735741�022216� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi const ( // NodeStatus* values represent the vocabulary of a Node‘s possible statuses. // The node has been created and has a system ID assigned to it. NodeStatusDeclared = "0" //Testing and other commissioning steps are taking place. NodeStatusCommissioning = "1" // Smoke or burn-in testing has a found a problem. NodeStatusFailedTests = "2" // The node can’t be contacted. NodeStatusMissing = "3" // The node is in the general pool ready to be deployed. NodeStatusReady = "4" // The node is ready for named deployment. NodeStatusReserved = "5" // The node is powering a service from a charm or is ready for use with a fresh Ubuntu install. NodeStatusAllocated = "6" // The node has been removed from service manually until an admin overrides the retirement. NodeStatusRetired = "7" ) ������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/maas.go������������������������������������������������0000644�0000153�0000161�00000000613�12321735741�022173� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi // NewMAAS returns an interface to the MAAS API as a *MAASObject. func NewMAAS(client Client) *MAASObject { attrs := map[string]interface{}{resourceURI: client.APIURL.String()} obj := newJSONMAASObject(attrs, client) return &obj } ���������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/jsonobject.go������������������������������������������0000644�0000153�0000161�00000015753�12321735741�023425� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "encoding/json" "errors" "fmt" ) // JSONObject is a wrapper around a JSON structure which provides // methods to extract data from that structure. // A JSONObject provides a simple structure consisting of the data types // defined in JSON: string, number, object, list, and bool. To get the // value you want out of a JSONObject, you must know (or figure out) which // kind of value you have, and then call the appropriate Get*() method to // get at it. Reading an item as the wrong type will return an error. // For instance, if your JSONObject consists of a number, call GetFloat64() // to get the value as a float64. If it's a list, call GetArray() to get // a slice of JSONObjects. To read any given item from the slice, you'll // need to "Get" that as the right type as well. // There is one exception: a MAASObject is really a special kind of map, // so you can read it as either. // Reading a null item is also an error. So before you try obj.Get*(), // first check obj.IsNil(). type JSONObject struct { // Parsed value. May actually be any of the types a JSONObject can // wrap, except raw bytes. If the object can only be interpreted // as raw bytes, this will be nil. value interface{} // Raw bytes, if this object was parsed directly from an API response. // Is nil for sub-objects found within other objects. An object that // was parsed directly from a response can be both raw bytes and some // other value at the same time. // For example, "[]" looks like a JSON list, so you can read it as an // array. But it may also be the raw contents of a file that just // happens to look like JSON, and so you can read it as raw bytes as // well. bytes []byte // Client for further communication with the API. client Client // Is this a JSON null? isNull bool } // Our JSON processor distinguishes a MAASObject from a jsonMap by the fact // that it contains a key "resource_uri". (A regular map might contain the // same key through sheer coincide, but never mind: you can still treat it // as a jsonMap and never notice the difference.) const resourceURI = "resource_uri" // maasify turns a completely untyped json.Unmarshal result into a JSONObject // (with the appropriate implementation of course). This function is // recursive. Maps and arrays are deep-copied, with each individual value // being converted to a JSONObject type. func maasify(client Client, value interface{}) JSONObject { if value == nil { return JSONObject{isNull: true} } switch value.(type) { case string, float64, bool: return JSONObject{value: value} case map[string]interface{}: original := value.(map[string]interface{}) result := make(map[string]JSONObject, len(original)) for key, value := range original { result[key] = maasify(client, value) } return JSONObject{value: result, client: client} case []interface{}: original := value.([]interface{}) result := make([]JSONObject, len(original)) for index, value := range original { result[index] = maasify(client, value) } return JSONObject{value: result} } msg := fmt.Sprintf("Unknown JSON type, can't be converted to JSONObject: %v", value) panic(msg) } // Parse a JSON blob into a JSONObject. func Parse(client Client, input []byte) (JSONObject, error) { var obj JSONObject if input == nil { panic(errors.New("Parse() called with nil input")) } var parsed interface{} err := json.Unmarshal(input, &parsed) if err == nil { obj = maasify(client, parsed) obj.bytes = input } else { switch err.(type) { case *json.InvalidUTF8Error: case *json.SyntaxError: // This isn't JSON. Treat it as raw binary data. default: return obj, err } obj = JSONObject{value: nil, client: client, bytes: input} } return obj, nil } // Return error value for failed type conversion. func failConversion(wantedType string, obj JSONObject) error { msg := fmt.Sprintf("Requested %v, got %T.", wantedType, obj.value) return errors.New(msg) } // MarshalJSON tells the standard json package how to serialize a JSONObject // back to JSON. func (obj JSONObject) MarshalJSON() ([]byte, error) { if obj.IsNil() { return json.Marshal(nil) } return json.Marshal(obj.value) } // With MarshalJSON, JSONObject implements json.Marshaler. var _ json.Marshaler = (*JSONObject)(nil) // IsNil tells you whether a JSONObject is a JSON "null." // There is one irregularity. If the original JSON blob was actually raw // data, not JSON, then its IsNil will return false because the object // contains the binary data as a non-nil value. But, if the original JSON // blob consisted of a null, then IsNil returns true even though you can // still retrieve binary data from it. func (obj JSONObject) IsNil() bool { if obj.value != nil { return false } if obj.bytes == nil { return true } // This may be a JSON null. We can't expect every JSON null to look // the same; there may be leading or trailing space. return obj.isNull } // GetString retrieves the object's value as a string. If the value wasn't // a JSON string, that's an error. func (obj JSONObject) GetString() (value string, err error) { value, ok := obj.value.(string) if !ok { err = failConversion("string", obj) } return } // GetFloat64 retrieves the object's value as a float64. If the value wasn't // a JSON number, that's an error. func (obj JSONObject) GetFloat64() (value float64, err error) { value, ok := obj.value.(float64) if !ok { err = failConversion("float64", obj) } return } // GetMap retrieves the object's value as a map. If the value wasn't a JSON // object, that's an error. func (obj JSONObject) GetMap() (value map[string]JSONObject, err error) { value, ok := obj.value.(map[string]JSONObject) if !ok { err = failConversion("map", obj) } return } // GetArray retrieves the object's value as an array. If the value wasn't a // JSON list, that's an error. func (obj JSONObject) GetArray() (value []JSONObject, err error) { value, ok := obj.value.([]JSONObject) if !ok { err = failConversion("array", obj) } return } // GetBool retrieves the object's value as a bool. If the value wasn't a JSON // bool, that's an error. func (obj JSONObject) GetBool() (value bool, err error) { value, ok := obj.value.(bool) if !ok { err = failConversion("bool", obj) } return } // GetBytes retrieves the object's value as raw bytes. A JSONObject that was // parsed from the original input (as opposed to one that's embedded in // another JSONObject) can contain both the raw bytes and the parsed JSON // value, but either can be the case without the other. // If this object wasn't parsed directly from the original input, that's an // error. // If the object was parsed from an original input that just said "null", then // IsNil will return true but the raw bytes are still available from GetBytes. func (obj JSONObject) GetBytes() ([]byte, error) { if obj.bytes == nil { return nil, failConversion("bytes", obj) } return obj.bytes, nil } ���������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/testservice.go�����������������������������������������0000644�0000153�0000161�00000037644�12321736016�023624� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "bufio" "encoding/base64" "encoding/json" "fmt" "io/ioutil" "mime/multipart" "net/http" "net/http/httptest" "net/url" "regexp" "sort" "strings" ) // TestMAASObject is a fake MAAS server MAASObject. type TestMAASObject struct { MAASObject TestServer *TestServer } // checkError is a shorthand helper that panics if err is not nil. func checkError(err error) { if err != nil { panic(err) } } // NewTestMAAS returns a TestMAASObject that implements the MAASObject // interface and thus can be used as a test object instead of the one returned // by gomaasapi.NewMAAS(). func NewTestMAAS(version string) *TestMAASObject { server := NewTestServer(version) authClient, err := NewAnonymousClient(server.URL, version) checkError(err) maas := NewMAAS(*authClient) return &TestMAASObject{*maas, server} } // Close shuts down the test server. func (testMAASObject *TestMAASObject) Close() { testMAASObject.TestServer.Close() } // A TestServer is an HTTP server listening on a system-chosen port on the // local loopback interface, which simulates the behavior of a MAAS server. // It is intendend for use in end-to-end HTTP tests using the gomaasapi // library. type TestServer struct { *httptest.Server serveMux *http.ServeMux client Client nodes map[string]MAASObject ownedNodes map[string]bool // mapping system_id -> list of operations performed. nodeOperations map[string][]string // mapping system_id -> list of Values passed when performing // operations nodeOperationRequestValues map[string][]url.Values files map[string]MAASObject version string } func getNodeURI(version, systemId string) string { return fmt.Sprintf("/api/%s/nodes/%s/", version, systemId) } func getFileURI(version, filename string) string { uri := url.URL{} uri.Path = fmt.Sprintf("/api/%s/files/%s/", version, filename) return uri.String() } // Clear clears all the fake data stored and recorded by the test server // (nodes, recorded operations, etc.). func (server *TestServer) Clear() { server.nodes = make(map[string]MAASObject) server.ownedNodes = make(map[string]bool) server.nodeOperations = make(map[string][]string) server.nodeOperationRequestValues = make(map[string][]url.Values) server.files = make(map[string]MAASObject) } // NodeOperations returns the map containing the list of the operations // performed for each node. func (server *TestServer) NodeOperations() map[string][]string { return server.nodeOperations } // NodeOperationRequestValues returns the map containing the list of the // url.Values extracted from the request used when performing operations // on nodes. func (server *TestServer) NodeOperationRequestValues() map[string][]url.Values { return server.nodeOperationRequestValues } func (server *TestServer) addNodeOperation(systemId, operation string, request *http.Request) { operations, present := server.nodeOperations[systemId] operationRequestValues, present2 := server.nodeOperationRequestValues[systemId] if present != present2 { panic("inconsistent state: nodeOperations and nodeOperationRequestValues don't have the same keys.") } requestValues := url.Values{} if request.Body != nil && request.Header.Get("Content-Type") == "application/x-www-form-urlencoded" { body, err := readAndClose(request.Body) if err != nil { panic(err) } requestValues, err = url.ParseQuery(string(body)) if err != nil { panic(err) } } if !present { operations = []string{operation} operationRequestValues = []url.Values{requestValues} } else { operations = append(operations, operation) operationRequestValues = append(operationRequestValues, requestValues) } server.nodeOperations[systemId] = operations server.nodeOperationRequestValues[systemId] = operationRequestValues } // NewNode creates a MAAS node. The provided string should be a valid json // string representing a map and contain a string value for the key // 'system_id'. e.g. `{"system_id": "mysystemid"}`. // If one of these conditions is not met, NewNode panics. func (server *TestServer) NewNode(jsonText string) MAASObject { var attrs map[string]interface{} err := json.Unmarshal([]byte(jsonText), &attrs) checkError(err) systemIdEntry, hasSystemId := attrs["system_id"] if !hasSystemId { panic("The given map json string does not contain a 'system_id' value.") } systemId := systemIdEntry.(string) attrs[resourceURI] = getNodeURI(server.version, systemId) if _, hasStatus := attrs["status"]; !hasStatus { attrs["status"] = NodeStatusAllocated } obj := newJSONMAASObject(attrs, server.client) server.nodes[systemId] = obj return obj } // Nodes returns a map associating all the nodes' system ids with the nodes' // objects. func (server *TestServer) Nodes() map[string]MAASObject { return server.nodes } // OwnedNodes returns a map whose keys represent the nodes that are currently // allocated. func (server *TestServer) OwnedNodes() map[string]bool { return server.ownedNodes } // NewFile creates a file in the test MAAS server. func (server *TestServer) NewFile(filename string, filecontent []byte) MAASObject { attrs := make(map[string]interface{}) attrs[resourceURI] = getFileURI(server.version, filename) base64Content := base64.StdEncoding.EncodeToString(filecontent) attrs["content"] = base64Content attrs["filename"] = filename // Allocate an arbitrary URL here. It would be nice if the caller // could do this, but that would change the API and require many // changes. escapedName := url.QueryEscape(filename) attrs["anon_resource_uri"] = "/maas/1.0/files/?op=get_by_key&key=" + escapedName + "_key" obj := newJSONMAASObject(attrs, server.client) server.files[filename] = obj return obj } func (server *TestServer) Files() map[string]MAASObject { return server.files } // ChangeNode updates a node with the given key/value. func (server *TestServer) ChangeNode(systemId, key, value string) { node, found := server.nodes[systemId] if !found { panic("No node with such 'system_id'.") } node.GetMap()[key] = maasify(server.client, value) } func getTopLevelNodesURL(version string) string { return fmt.Sprintf("/api/%s/nodes/", version) } func getNodeURLRE(version string) *regexp.Regexp { reString := fmt.Sprintf("^/api/%s/nodes/([^/]*)/$", regexp.QuoteMeta(version)) return regexp.MustCompile(reString) } func getFilesURL(version string) string { return fmt.Sprintf("/api/%s/files/", version) } func getFileURLRE(version string) *regexp.Regexp { reString := fmt.Sprintf("^/api/%s/files/(.*)/$", regexp.QuoteMeta(version)) return regexp.MustCompile(reString) } // NewTestServer starts and returns a new MAAS test server. The caller should call Close when finished, to shut it down. func NewTestServer(version string) *TestServer { server := &TestServer{version: version} serveMux := http.NewServeMux() nodesURL := getTopLevelNodesURL(server.version) // Register handler for '/api/<version>/nodes/*'. serveMux.HandleFunc(nodesURL, func(w http.ResponseWriter, r *http.Request) { nodesHandler(server, w, r) }) filesURL := getFilesURL(server.version) // Register handler for '/api/<version>/files/*'. serveMux.HandleFunc(filesURL, func(w http.ResponseWriter, r *http.Request) { filesHandler(server, w, r) }) newServer := httptest.NewServer(serveMux) client, err := NewAnonymousClient(newServer.URL, "1.0") checkError(err) server.Server = newServer server.serveMux = serveMux server.client = *client server.Clear() return server } // nodesHandler handles requests for '/api/<version>/nodes/*'. func nodesHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { values, err := url.ParseQuery(r.URL.RawQuery) checkError(err) op := values.Get("op") nodeURLRE := getNodeURLRE(server.version) nodeURLMatch := nodeURLRE.FindStringSubmatch(r.URL.Path) nodesURL := getTopLevelNodesURL(server.version) switch { case r.URL.Path == nodesURL: nodesTopLevelHandler(server, w, r, op) case nodeURLMatch != nil: // Request for a single node. nodeHandler(server, w, r, nodeURLMatch[1], op) default: // Default handler: not found. http.NotFoundHandler().ServeHTTP(w, r) } } // nodeHandler handles requests for '/api/<version>/nodes/<system_id>/'. func nodeHandler(server *TestServer, w http.ResponseWriter, r *http.Request, systemId string, operation string) { node, ok := server.nodes[systemId] if !ok { http.NotFoundHandler().ServeHTTP(w, r) return } if r.Method == "GET" { if operation == "" { w.WriteHeader(http.StatusOK) fmt.Fprint(w, marshalNode(node)) return } else { w.WriteHeader(http.StatusBadRequest) return } } if r.Method == "POST" { // The only operations supported are "start", "stop" and "release". if operation == "start" || operation == "stop" || operation == "release" { // Record operation on node. server.addNodeOperation(systemId, operation, r) if operation == "release" { delete(server.OwnedNodes(), systemId) } w.WriteHeader(http.StatusOK) fmt.Fprint(w, marshalNode(node)) return } else { w.WriteHeader(http.StatusBadRequest) return } } if r.Method == "DELETE" { delete(server.nodes, systemId) w.WriteHeader(http.StatusOK) return } http.NotFoundHandler().ServeHTTP(w, r) } func contains(slice []string, val string) bool { for _, item := range slice { if item == val { return true } } return false } // nodeListingHandler handles requests for '/nodes/'. func nodeListingHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { values, err := url.ParseQuery(r.URL.RawQuery) checkError(err) ids, hasId := values["id"] var convertedNodes = []map[string]JSONObject{} for systemId, node := range server.nodes { if !hasId || contains(ids, systemId) { convertedNodes = append(convertedNodes, node.GetMap()) } } res, err := json.Marshal(convertedNodes) checkError(err) w.WriteHeader(http.StatusOK) fmt.Fprint(w, string(res)) } // findFreeNode looks for a node that is currently available. func findFreeNode(server *TestServer) *MAASObject { for systemID, node := range server.Nodes() { _, present := server.OwnedNodes()[systemID] if !present { return &node } } return nil } // nodesAcquireHandler simulates acquiring a node. func nodesAcquireHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { node := findFreeNode(server) if node == nil { w.WriteHeader(http.StatusConflict) } else { systemId, err := node.GetField("system_id") checkError(err) server.OwnedNodes()[systemId] = true res, err := json.Marshal(node) checkError(err) // Record operation. server.addNodeOperation(systemId, "acquire", r) w.WriteHeader(http.StatusOK) fmt.Fprint(w, string(res)) } } // nodesTopLevelHandler handles a request for /api/<version>/nodes/ // (with no node id following as part of the path). func nodesTopLevelHandler(server *TestServer, w http.ResponseWriter, r *http.Request, op string) { switch { case r.Method == "GET" && op == "list": // Node listing operation. nodeListingHandler(server, w, r) case r.Method == "POST" && op == "acquire": nodesAcquireHandler(server, w, r) default: w.WriteHeader(http.StatusBadRequest) } } // filesHandler handles requests for '/api/<version>/files/*'. func filesHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { values, err := url.ParseQuery(r.URL.RawQuery) checkError(err) op := values.Get("op") fileURLRE := getFileURLRE(server.version) fileURLMatch := fileURLRE.FindStringSubmatch(r.URL.Path) fileListingURL := getFilesURL(server.version) switch { case r.Method == "GET" && op == "list" && r.URL.Path == fileListingURL: // File listing operation. fileListingHandler(server, w, r) case op == "get" && r.Method == "GET" && r.URL.Path == fileListingURL: getFileHandler(server, w, r) case op == "add" && r.Method == "POST" && r.URL.Path == fileListingURL: addFileHandler(server, w, r) case fileURLMatch != nil: // Request for a single file. fileHandler(server, w, r, fileURLMatch[1], op) default: // Default handler: not found. http.NotFoundHandler().ServeHTTP(w, r) } } // listFilenames returns the names of those uploaded files whose names start // with the given prefix, sorted lexicographically. func listFilenames(server *TestServer, prefix string) []string { var filenames = make([]string, 0) for filename := range server.files { if strings.HasPrefix(filename, prefix) { filenames = append(filenames, filename) } } sort.Strings(filenames) return filenames } // stripFileContent copies a map of attributes representing an uploaded file, // but with the "content" attribute removed. func stripContent(original map[string]JSONObject) map[string]JSONObject { newMap := make(map[string]JSONObject, len(original)-1) for key, value := range original { if key != "content" { newMap[key] = value } } return newMap } // fileListingHandler handles requests for '/api/<version>/files/?op=list'. func fileListingHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { values, err := url.ParseQuery(r.URL.RawQuery) checkError(err) prefix := values.Get("prefix") filenames := listFilenames(server, prefix) // Build a sorted list of the files as map[string]JSONObject objects. convertedFiles := make([]map[string]JSONObject, 0) for _, filename := range filenames { // The "content" attribute is not in the listing. fileMap := stripContent(server.files[filename].GetMap()) convertedFiles = append(convertedFiles, fileMap) } res, err := json.Marshal(convertedFiles) checkError(err) w.WriteHeader(http.StatusOK) fmt.Fprint(w, string(res)) } // fileHandler handles requests for '/api/<version>/files/<filename>/'. func fileHandler(server *TestServer, w http.ResponseWriter, r *http.Request, filename string, operation string) { switch { case r.Method == "DELETE": delete(server.files, filename) w.WriteHeader(http.StatusOK) case r.Method == "GET": // Retrieve a file's information (including content) as a JSON // object. file, ok := server.files[filename] if !ok { http.NotFoundHandler().ServeHTTP(w, r) return } jsonText, err := json.Marshal(file) if err != nil { panic(err) } w.WriteHeader(http.StatusOK) w.Write(jsonText) default: // Default handler: not found. http.NotFoundHandler().ServeHTTP(w, r) } } // InternalError replies to the request with an HTTP 500 internal error. func InternalError(w http.ResponseWriter, r *http.Request, err error) { http.Error(w, err.Error(), http.StatusInternalServerError) } // getFileHandler handles requests for // '/api/<version>/files/?op=get&filename=filename'. func getFileHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { values, err := url.ParseQuery(r.URL.RawQuery) checkError(err) filename := values.Get("filename") file, found := server.files[filename] if !found { http.NotFoundHandler().ServeHTTP(w, r) return } base64Content, err := file.GetField("content") if err != nil { InternalError(w, r, err) return } content, err := base64.StdEncoding.DecodeString(base64Content) if err != nil { InternalError(w, r, err) return } w.Write(content) } func readMultipart(upload *multipart.FileHeader) ([]byte, error) { file, err := upload.Open() if err != nil { return nil, err } defer file.Close() reader := bufio.NewReader(file) return ioutil.ReadAll(reader) } // filesHandler handles requests for '/api/<version>/files/?op=add&filename=filename'. func addFileHandler(server *TestServer, w http.ResponseWriter, r *http.Request) { err := r.ParseMultipartForm(10000000) checkError(err) filename := r.Form.Get("filename") if filename == "" { panic("upload has no filename") } uploads := r.MultipartForm.File if len(uploads) != 1 { panic("the payload should contain one file and one file only") } var upload *multipart.FileHeader for _, uploadContent := range uploads { upload = uploadContent[0] } content, err := readMultipart(upload) checkError(err) server.NewFile(filename, content) w.WriteHeader(http.StatusOK) } ��������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/templates/���������������������������������������������0000755�0000153�0000161�00000000000�12321735741�022721� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/templates/source.go������������������������������������0000644�0000153�0000161�00000000240�12321735741�024544� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/templates/source_test.go�������������������������������0000644�0000153�0000161�00000000611�12321735741�025605� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( . "launchpad.net/gocheck" ) type MyTestSuite struct{} var _ = Suite(&MyTestSuite{}) // TODO: Replace with real test functions. Give them real names. func (suite *MyTestSuite) TestXXX(c *C) { c.Check(2+2, Equals, 4) } �����������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/COPYING������������������������������������������������0000644�0000153�0000161�00000104513�12321735741�021762� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/gomaasapi_test.go��������������������������������������0000644�0000153�0000161�00000000506�12321735741�024253� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( . "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { TestingT(t) } type GomaasapiTestSuite struct { } var _ = Suite(&GomaasapiTestSuite{}) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/maasobject_test.go�������������������������������������0000644�0000153�0000161�00000014520�12321735741�024423� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "encoding/json" "fmt" . "launchpad.net/gocheck" "math/rand" "net/url" ) type MAASObjectSuite struct{} var _ = Suite(&MAASObjectSuite{}) func makeFakeResourceURI() string { return "http://example.com/" + fmt.Sprint(rand.Int31()) } // JSONObjects containing MAAS objects convert only to map or to MAASObject. func (suite *MAASObjectSuite) TestConversionsMAASObject(c *C) { input := map[string]interface{}{resourceURI: "someplace"} obj := maasify(Client{}, input) mp, err := obj.GetMap() c.Check(err, IsNil) text, err := mp[resourceURI].GetString() c.Check(err, IsNil) c.Check(text, Equals, "someplace") var maasobj MAASObject maasobj, err = obj.GetMAASObject() c.Assert(err, IsNil) c.Check(maasobj, NotNil) _, err = obj.GetString() c.Check(err, NotNil) _, err = obj.GetFloat64() c.Check(err, NotNil) _, err = obj.GetArray() c.Check(err, NotNil) _, err = obj.GetBool() c.Check(err, NotNil) } func (suite *MAASObjectSuite) TestNewJSONMAASObjectPanicsIfNoResourceURI(c *C) { defer func() { recoveredError := recover() c.Check(recoveredError, NotNil) msg := recoveredError.(error).Error() c.Check(msg, Matches, ".*no 'resource_uri' key.*") }() input := map[string]interface{}{"test": "test"} newJSONMAASObject(input, Client{}) } func (suite *MAASObjectSuite) TestNewJSONMAASObjectPanicsIfResourceURINotString(c *C) { defer func() { recoveredError := recover() c.Check(recoveredError, NotNil) msg := recoveredError.(error).Error() c.Check(msg, Matches, ".*invalid resource_uri.*") }() input := map[string]interface{}{resourceURI: 77.77} newJSONMAASObject(input, Client{}) } func (suite *MAASObjectSuite) TestNewJSONMAASObjectPanicsIfResourceURINotURL(c *C) { defer func() { recoveredError := recover() c.Check(recoveredError, NotNil) msg := recoveredError.(error).Error() c.Check(msg, Matches, ".*resource_uri.*valid URL.*") }() input := map[string]interface{}{resourceURI: "%z"} newJSONMAASObject(input, Client{}) } func (suite *MAASObjectSuite) TestNewJSONMAASObjectSetsUpURI(c *C) { URI, err := url.Parse("http://example.com/a/resource") c.Assert(err, IsNil) attrs := map[string]interface{}{resourceURI: URI.String()} obj := newJSONMAASObject(attrs, Client{}) c.Check(obj.uri, DeepEquals, URI) } func (suite *MAASObjectSuite) TestURL(c *C) { baseURL, err := url.Parse("http://example.com/") c.Assert(err, IsNil) uri := "http://example.com/a/resource" resourceURL, err := url.Parse(uri) c.Assert(err, IsNil) input := map[string]interface{}{resourceURI: uri} client := Client{APIURL: baseURL} obj := newJSONMAASObject(input, client) URL := obj.URL() c.Check(URL, DeepEquals, resourceURL) } // makeFakeMAASObject creates a MAASObject for some imaginary resource. // There is no actual HTTP service or resource attached. // serviceURL is the base URL of the service, and resourceURI is the path for // the object, relative to serviceURL. func makeFakeMAASObject(serviceURL, resourcePath string) MAASObject { baseURL, err := url.Parse(serviceURL) if err != nil { panic(fmt.Errorf("creation of fake object failed: %v", err)) } uri := serviceURL + resourcePath input := map[string]interface{}{resourceURI: uri} client := Client{APIURL: baseURL} return newJSONMAASObject(input, client) } // Passing GetSubObject a relative path effectively concatenates that path to // the original object's resource URI. func (suite *MAASObjectSuite) TestGetSubObjectRelative(c *C) { obj := makeFakeMAASObject("http://example.com/", "a/resource/") subObj := obj.GetSubObject("test") subURL := subObj.URL() // uri ends with a slash and subName starts with one, but the two paths // should be concatenated as "http://example.com/a/resource/test/". expectedSubURL, err := url.Parse("http://example.com/a/resource/test/") c.Assert(err, IsNil) c.Check(subURL, DeepEquals, expectedSubURL) } // Passing GetSubObject an absolute path effectively substitutes that path for // the path component in the original object's resource URI. func (suite *MAASObjectSuite) TestGetSubObjectAbsolute(c *C) { obj := makeFakeMAASObject("http://example.com/", "a/resource/") subObj := obj.GetSubObject("/b/test") subURL := subObj.URL() expectedSubURL, err := url.Parse("http://example.com/b/test/") c.Assert(err, IsNil) c.Check(subURL, DeepEquals, expectedSubURL) } // An absolute path passed to GetSubObject is rooted at the server root, not // at the service root. So every absolute resource URI must repeat the part // of the path that leads to the service root. This does not double that part // of the URI. func (suite *MAASObjectSuite) TestGetSubObjectAbsoluteDoesNotDoubleServiceRoot(c *C) { obj := makeFakeMAASObject("http://example.com/service", "a/resource/") subObj := obj.GetSubObject("/service/test") subURL := subObj.URL() // The "/service" part is not repeated; it must be included. expectedSubURL, err := url.Parse("http://example.com/service/test/") c.Assert(err, IsNil) c.Check(subURL, DeepEquals, expectedSubURL) } // The argument to GetSubObject is a relative path, not a URL. So it won't // take a query part. The special characters that mark a query are escaped // so they are recognized as parts of the path. func (suite *MAASObjectSuite) TestGetSubObjectTakesPathNotURL(c *C) { obj := makeFakeMAASObject("http://example.com/", "x/") subObj := obj.GetSubObject("/y?z") c.Check(subObj.URL().String(), Equals, "http://example.com/y%3Fz/") } func (suite *MAASObjectSuite) TestGetField(c *C) { uri := "http://example.com/a/resource" fieldName := "field name" fieldValue := "a value" input := map[string]interface{}{ resourceURI: uri, fieldName: fieldValue, } obj := newJSONMAASObject(input, Client{}) value, err := obj.GetField(fieldName) c.Check(err, IsNil) c.Check(value, Equals, fieldValue) } func (suite *MAASObjectSuite) TestSerializesToJSON(c *C) { attrs := map[string]interface{}{ resourceURI: "http://maas.example.com/", "counter": 5.0, "active": true, "macs": map[string]interface{}{"eth0": "AA:BB:CC:DD:EE:FF"}, } obj := maasify(Client{}, attrs) output, err := json.Marshal(obj) c.Assert(err, IsNil) var deserialized map[string]interface{} err = json.Unmarshal(output, &deserialized) c.Assert(err, IsNil) c.Check(deserialized, DeepEquals, attrs) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/example/�����������������������������������������������0000755�0000153�0000161�00000000000�12321735741�022356� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/example/live_example.go��������������������������������0000644�0000153�0000161�00000011753�12321735741�025366� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). /* This is an example on how the Go library gomaasapi can be used to interact with a real MAAS server. Note that this is a provided only as an example and that real code should probably do something more sensible with errors than ignoring them or panicking. */ package main import ( "bytes" "fmt" "launchpad.net/gomaasapi" "net/url" ) var apiKey string var apiURL string var apiVersion string func getParams() { fmt.Println("Warning: this will create a node on the MAAS server; it should be deleted at the end of the run but if something goes wrong, that test node might be left over. You've been warned.") fmt.Print("Enter API key: ") _, err := fmt.Scanf("%s", &apiKey) if err != nil { panic(err) } fmt.Print("Enter API URL: ") _, err = fmt.Scanf("%s", &apiURL) if err != nil { panic(err) } fmt.Print("Enter API version: ") _, err = fmt.Scanf("%s", &apiVersion) if err != nil { panic(err) } } func checkError(err error) { if err != nil { panic(err) } } func main() { getParams() // Create API server endpoint. authClient, err := gomaasapi.NewAuthenticatedClient(apiURL, apiKey, apiVersion) checkError(err) maas := gomaasapi.NewMAAS(*authClient) // Exercise the API. ManipulateNodes(maas) ManipulateFiles(maas) fmt.Println("All done.") } // ManipulateFiles exercises the /api/1.0/files/ API endpoint. Most precisely, // it uploads a files and then fetches it, making sure the received content // is the same as the one that was sent. func ManipulateFiles(maas *gomaasapi.MAASObject) { files := maas.GetSubObject("files") fileContent := []byte("test file content") fileName := "filename" filesToUpload := map[string][]byte{"file": fileContent} // Upload a file. fmt.Println("Uploading a file...") _, err := files.CallPostFiles("add", url.Values{"filename": {fileName}}, filesToUpload) checkError(err) fmt.Println("File sent.") // Fetch the file. fmt.Println("Fetching the file...") fileResult, err := files.CallGet("get", url.Values{"filename": {fileName}}) checkError(err) receivedFileContent, err := fileResult.GetBytes() checkError(err) if bytes.Compare(receivedFileContent, fileContent) != 0 { panic("Received content differs from the content sent!") } fmt.Println("Got file.") // Fetch list of files. listFiles, err := files.CallGet("list", url.Values{}) checkError(err) listFilesArray, err := listFiles.GetArray() checkError(err) fmt.Printf("We've got %v file(s)\n", len(listFilesArray)) // Delete the file. fmt.Println("Deleting the file...") fileObject, err := listFilesArray[0].GetMAASObject() checkError(err) errDelete := fileObject.Delete() checkError(errDelete) // Count the files. listFiles, err = files.CallGet("list", url.Values{}) checkError(err) listFilesArray, err = listFiles.GetArray() checkError(err) fmt.Printf("We've got %v file(s)\n", len(listFilesArray)) } // ManipulateFiles exercises the /api/1.0/nodes/ API endpoint. Most precisely, // it lists the existing nodes, creates a new node, updates it and then // deletes it. func ManipulateNodes(maas *gomaasapi.MAASObject) { nodeListing := maas.GetSubObject("nodes") // List nodes. fmt.Println("Fetching list of nodes...") listNodeObjects, err := nodeListing.CallGet("list", url.Values{}) checkError(err) listNodes, err := listNodeObjects.GetArray() checkError(err) fmt.Printf("Got list of %v nodes\n", len(listNodes)) for index, nodeObj := range listNodes { node, err := nodeObj.GetMAASObject() checkError(err) hostname, err := node.GetField("hostname") checkError(err) fmt.Printf("Node #%d is named '%v' (%v)\n", index, hostname, node.URL()) } // Create a node. fmt.Println("Creating a new node...") params := url.Values{"architecture": {"i386/generic"}, "mac_addresses": {"AA:BB:CC:DD:EE:FF"}} newNodeObj, err := nodeListing.CallPost("new", params) checkError(err) newNode, err := newNodeObj.GetMAASObject() checkError(err) newNodeName, err := newNode.GetField("hostname") checkError(err) fmt.Printf("New node created: %s (%s)\n", newNodeName, newNode.URL()) // Update the new node. fmt.Println("Updating the new node...") updateParams := url.Values{"hostname": {"mynewname"}} newNodeObj2, err := newNode.Update(updateParams) checkError(err) newNodeName2, err := newNodeObj2.GetField("hostname") checkError(err) fmt.Printf("New node updated, now named: %s\n", newNodeName2) // Count the nodes. listNodeObjects2, err := nodeListing.CallGet("list", url.Values{}) checkError(err) listNodes2, err := listNodeObjects2.GetArray() checkError(err) fmt.Printf("We've got %v nodes\n", len(listNodes2)) // Delete the new node. fmt.Println("Deleting the new node...") errDelete := newNode.Delete() checkError(errDelete) // Count the nodes. listNodeObjects3, err := nodeListing.CallGet("list", url.Values{}) checkError(err) listNodes3, err := listNodeObjects3.GetArray() checkError(err) fmt.Printf("We've got %v nodes\n", len(listNodes3)) } ���������������������juju-core_1.18.1/src/launchpad.net/gomaasapi/testservice_test.go������������������������������������0000644�0000153�0000161�00000045157�12321736016�024661� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. This software is licensed under the // GNU Lesser General Public License version 3 (see the file COPYING). package gomaasapi import ( "bytes" "encoding/base64" "encoding/json" "fmt" "io" . "launchpad.net/gocheck" "mime/multipart" "net/http" "net/url" "strings" ) type TestServerSuite struct { server *TestServer } var _ = Suite(&TestServerSuite{}) func (suite *TestServerSuite) SetUpTest(c *C) { server := NewTestServer("1.0") suite.server = server } func (suite *TestServerSuite) TearDownTest(c *C) { suite.server.Close() } func (suite *TestServerSuite) TestNewTestServerReturnsTestServer(c *C) { handler := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusAccepted) } suite.server.serveMux.HandleFunc("/test/", handler) resp, err := http.Get(suite.server.Server.URL + "/test/") c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusAccepted) } func (suite *TestServerSuite) TestGetResourceURI(c *C) { c.Check(getNodeURI("version", "test"), Equals, "/api/version/nodes/test/") } func (suite *TestServerSuite) TestInvalidOperationOnNodesIsBadRequest(c *C) { badURL := getTopLevelNodesURL(suite.server.version) + "?op=procrastinate" response, err := http.Get(suite.server.Server.URL + badURL) c.Assert(err, IsNil) c.Check(response.StatusCode, Equals, http.StatusBadRequest) } func (suite *TestServerSuite) TestHandlesNodeListingUnknownPath(c *C) { invalidPath := fmt.Sprintf("/api/%s/nodes/invalid/path/", suite.server.version) resp, err := http.Get(suite.server.Server.URL + invalidPath) c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusNotFound) } func (suite *TestServerSuite) TestNewNode(c *C) { input := `{"system_id": "mysystemid"}` newNode := suite.server.NewNode(input) c.Check(len(suite.server.nodes), Equals, 1) c.Check(suite.server.nodes["mysystemid"], DeepEquals, newNode) } func (suite *TestServerSuite) TestNodesReturnsNodes(c *C) { input := `{"system_id": "mysystemid"}` newNode := suite.server.NewNode(input) nodesMap := suite.server.Nodes() c.Check(len(nodesMap), Equals, 1) c.Check(nodesMap["mysystemid"], DeepEquals, newNode) } func (suite *TestServerSuite) TestChangeNode(c *C) { input := `{"system_id": "mysystemid"}` suite.server.NewNode(input) suite.server.ChangeNode("mysystemid", "newfield", "newvalue") node, _ := suite.server.nodes["mysystemid"] field, err := node.GetField("newfield") c.Assert(err, IsNil) c.Check(field, Equals, "newvalue") } func (suite *TestServerSuite) TestClearClearsData(c *C) { input := `{"system_id": "mysystemid"}` suite.server.NewNode(input) suite.server.addNodeOperation("mysystemid", "start", &http.Request{}) suite.server.Clear() c.Check(len(suite.server.nodes), Equals, 0) c.Check(len(suite.server.nodeOperations), Equals, 0) c.Check(len(suite.server.nodeOperationRequestValues), Equals, 0) } func (suite *TestServerSuite) TestAddNodeOperationPopulatesOperations(c *C) { input := `{"system_id": "mysystemid"}` suite.server.NewNode(input) suite.server.addNodeOperation("mysystemid", "start", &http.Request{}) suite.server.addNodeOperation("mysystemid", "stop", &http.Request{}) nodeOperations := suite.server.NodeOperations() operations := nodeOperations["mysystemid"] c.Check(operations, DeepEquals, []string{"start", "stop"}) } func (suite *TestServerSuite) TestAddNodeOperationPopulatesOperationRequestValues(c *C) { input := `{"system_id": "mysystemid"}` suite.server.NewNode(input) reader := strings.NewReader("key=value") request, err := http.NewRequest("POST", "http://example.com/", reader) request.Header.Set("Content-Type", "application/x-www-form-urlencoded") c.Assert(err, IsNil) suite.server.addNodeOperation("mysystemid", "start", request) values := suite.server.NodeOperationRequestValues() value := values["mysystemid"] c.Check(len(value), Equals, 1) c.Check(value[0], DeepEquals, url.Values{"key": []string{"value"}}) } func (suite *TestServerSuite) TestNewNodeRequiresJSONString(c *C) { input := `invalid:json` defer func() { recoveredError := recover().(*json.SyntaxError) c.Check(recoveredError, NotNil) c.Check(recoveredError.Error(), Matches, ".*invalid character.*") }() suite.server.NewNode(input) } func (suite *TestServerSuite) TestNewNodeRequiresSystemIdKey(c *C) { input := `{"test": "test"}` defer func() { recoveredError := recover() c.Check(recoveredError, NotNil) c.Check(recoveredError, Matches, ".*does not contain a 'system_id' value.") }() suite.server.NewNode(input) } func (suite *TestServerSuite) TestHandlesNodeRequestNotFound(c *C) { getURI := fmt.Sprintf("/api/%s/nodes/test/", suite.server.version) resp, err := http.Get(suite.server.Server.URL + getURI) c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusNotFound) } func (suite *TestServerSuite) TestHandlesNodeUnknownOperation(c *C) { input := `{"system_id": "mysystemid"}` suite.server.NewNode(input) postURI := fmt.Sprintf("/api/%s/nodes/mysystemid/?op=unknown/", suite.server.version) respStart, err := http.Post(suite.server.Server.URL+postURI, "", nil) c.Check(err, IsNil) c.Check(respStart.StatusCode, Equals, http.StatusBadRequest) } func (suite *TestServerSuite) TestHandlesNodeDelete(c *C) { input := `{"system_id": "mysystemid"}` suite.server.NewNode(input) deleteURI := fmt.Sprintf("/api/%s/nodes/mysystemid/?op=mysystemid", suite.server.version) req, err := http.NewRequest("DELETE", suite.server.Server.URL+deleteURI, nil) var client http.Client resp, err := client.Do(req) c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusOK) c.Check(len(suite.server.nodes), Equals, 0) } func uploadTo(url, fileName string, fileContent []byte) (*http.Response, error) { buf := new(bytes.Buffer) w := multipart.NewWriter(buf) fw, err := w.CreateFormFile(fileName, fileName) if err != nil { panic(err) } io.Copy(fw, bytes.NewBuffer(fileContent)) w.Close() req, err := http.NewRequest("POST", url, buf) if err != nil { panic(err) } req.Header.Set("Content-Type", w.FormDataContentType()) client := &http.Client{} return client.Do(req) } func (suite *TestServerSuite) TestHandlesUploadFile(c *C) { fileContent := []byte("test file content") postURL := suite.server.Server.URL + fmt.Sprintf("/api/%s/files/?op=add&filename=filename", suite.server.version) resp, err := uploadTo(postURL, "upload", fileContent) c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusOK) c.Check(len(suite.server.files), Equals, 1) file, ok := suite.server.files["filename"] c.Assert(ok, Equals, true) field, err := file.GetField("content") c.Assert(err, IsNil) c.Check(field, Equals, base64.StdEncoding.EncodeToString(fileContent)) } func (suite *TestServerSuite) TestNewFileEscapesName(c *C) { obj := suite.server.NewFile("aa?bb", []byte("bytes")) resourceURI := obj.URI() c.Check(strings.Contains(resourceURI.String(), "aa?bb"), Equals, false) c.Check(strings.Contains(resourceURI.Path, "aa?bb"), Equals, true) anonURI, err := obj.GetField("anon_resource_uri") c.Assert(err, IsNil) c.Check(strings.Contains(anonURI, "aa?bb"), Equals, false) c.Check(strings.Contains(anonURI, url.QueryEscape("aa?bb")), Equals, true) } func (suite *TestServerSuite) TestHandlesFile(c *C) { const filename = "my-file" const fileContent = "test file content" file := suite.server.NewFile(filename, []byte(fileContent)) getURI := fmt.Sprintf("/api/%s/files/%s/", suite.server.version, filename) fileURI, err := file.GetField("anon_resource_uri") c.Assert(err, IsNil) resp, err := http.Get(suite.server.Server.URL + getURI) c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusOK) content, err := readAndClose(resp.Body) c.Assert(err, IsNil) var obj map[string]interface{} err = json.Unmarshal(content, &obj) c.Assert(err, IsNil) anon_url, ok := obj["anon_resource_uri"] c.Check(ok, Equals, true) c.Check(anon_url.(string), Equals, fileURI) base64Content, ok := obj["content"] c.Check(ok, Equals, true) decodedContent, err := base64.StdEncoding.DecodeString(base64Content.(string)) c.Assert(err, IsNil) c.Check(string(decodedContent), Equals, fileContent) } func (suite *TestServerSuite) TestHandlesGetFile(c *C) { fileContent := []byte("test file content") fileName := "filename" suite.server.NewFile(fileName, fileContent) getURI := fmt.Sprintf("/api/%s/files/?op=get&filename=filename", suite.server.version) resp, err := http.Get(suite.server.Server.URL + getURI) c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusOK) content, err := readAndClose(resp.Body) c.Check(err, IsNil) c.Check(string(content), Equals, string(fileContent)) c.Check(content, DeepEquals, fileContent) } func (suite *TestServerSuite) TestHandlesListReturnsSortedFilenames(c *C) { fileName1 := "filename1" suite.server.NewFile(fileName1, []byte("test file content")) fileName2 := "filename2" suite.server.NewFile(fileName2, []byte("test file content")) getURI := fmt.Sprintf("/api/%s/files/?op=list", suite.server.version) resp, err := http.Get(suite.server.Server.URL + getURI) c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusOK) content, err := readAndClose(resp.Body) c.Assert(err, IsNil) var files []map[string]string err = json.Unmarshal(content, &files) c.Assert(err, IsNil) c.Check(len(files), Equals, 2) c.Check(files[0]["filename"], Equals, fileName1) c.Check(files[1]["filename"], Equals, fileName2) } func (suite *TestServerSuite) TestHandlesListFiltersFiles(c *C) { fileName1 := "filename1" suite.server.NewFile(fileName1, []byte("test file content")) fileName2 := "prefixFilename" suite.server.NewFile(fileName2, []byte("test file content")) getURI := fmt.Sprintf("/api/%s/files/?op=list&prefix=prefix", suite.server.version) resp, err := http.Get(suite.server.Server.URL + getURI) c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusOK) content, err := readAndClose(resp.Body) c.Assert(err, IsNil) var files []map[string]string err = json.Unmarshal(content, &files) c.Assert(err, IsNil) c.Check(len(files), Equals, 1) c.Check(files[0]["filename"], Equals, fileName2) } func (suite *TestServerSuite) TestHandlesListOmitsContent(c *C) { const filename = "myfile" fileContent := []byte("test file content") suite.server.NewFile(filename, fileContent) getURI := fmt.Sprintf("/api/%s/files/?op=list", suite.server.version) resp, err := http.Get(suite.server.Server.URL + getURI) c.Assert(err, IsNil) content, err := readAndClose(resp.Body) c.Assert(err, IsNil) var files []map[string]string err = json.Unmarshal(content, &files) // The resulting dict does not have a "content" entry. file := files[0] _, ok := file["content"] c.Check(ok, Equals, false) // But the original as stored in the test service still has it. contentAfter, err := suite.server.files[filename].GetField("content") c.Assert(err, IsNil) bytes, err := base64.StdEncoding.DecodeString(contentAfter) c.Assert(err, IsNil) c.Check(string(bytes), Equals, string(fileContent)) } func (suite *TestServerSuite) TestDeleteFile(c *C) { fileName1 := "filename1" suite.server.NewFile(fileName1, []byte("test file content")) deleteURI := fmt.Sprintf("/api/%s/files/filename1/", suite.server.version) req, err := http.NewRequest("DELETE", suite.server.Server.URL+deleteURI, nil) c.Check(err, IsNil) var client http.Client resp, err := client.Do(req) c.Check(err, IsNil) c.Check(resp.StatusCode, Equals, http.StatusOK) c.Check(suite.server.Files(), DeepEquals, map[string]MAASObject{}) } // TestMAASObjectSuite validates that the object created by // NewTestMAAS can be used by the gomaasapi library as if it were a real // MAAS server. type TestMAASObjectSuite struct { TestMAASObject *TestMAASObject } var _ = Suite(&TestMAASObjectSuite{}) func (s *TestMAASObjectSuite) SetUpSuite(c *C) { s.TestMAASObject = NewTestMAAS("1.0") } func (s *TestMAASObjectSuite) TearDownSuite(c *C) { s.TestMAASObject.Close() } func (s *TestMAASObjectSuite) TearDownTest(c *C) { s.TestMAASObject.TestServer.Clear() } func (suite *TestMAASObjectSuite) TestListNodes(c *C) { input := `{"system_id": "mysystemid"}` suite.TestMAASObject.TestServer.NewNode(input) nodeListing := suite.TestMAASObject.GetSubObject("nodes") listNodeObjects, err := nodeListing.CallGet("list", url.Values{}) c.Check(err, IsNil) listNodes, err := listNodeObjects.GetArray() c.Assert(err, IsNil) c.Check(len(listNodes), Equals, 1) node, err := listNodes[0].GetMAASObject() c.Assert(err, IsNil) systemId, err := node.GetField("system_id") c.Assert(err, IsNil) c.Check(systemId, Equals, "mysystemid") resourceURI, _ := node.GetField(resourceURI) apiVersion := suite.TestMAASObject.TestServer.version expectedResourceURI := fmt.Sprintf("/api/%s/nodes/mysystemid/", apiVersion) c.Check(resourceURI, Equals, expectedResourceURI) } func (suite *TestMAASObjectSuite) TestListNodesNoNodes(c *C) { nodeListing := suite.TestMAASObject.GetSubObject("nodes") listNodeObjects, err := nodeListing.CallGet("list", url.Values{}) c.Check(err, IsNil) listNodes, err := listNodeObjects.GetArray() c.Check(err, IsNil) c.Check(listNodes, DeepEquals, []JSONObject{}) } func (suite *TestMAASObjectSuite) TestListNodesSelectedNodes(c *C) { input := `{"system_id": "mysystemid"}` suite.TestMAASObject.TestServer.NewNode(input) input2 := `{"system_id": "mysystemid2"}` suite.TestMAASObject.TestServer.NewNode(input2) nodeListing := suite.TestMAASObject.GetSubObject("nodes") listNodeObjects, err := nodeListing.CallGet("list", url.Values{"id": {"mysystemid2"}}) c.Check(err, IsNil) listNodes, err := listNodeObjects.GetArray() c.Check(err, IsNil) c.Check(len(listNodes), Equals, 1) node, _ := listNodes[0].GetMAASObject() systemId, _ := node.GetField("system_id") c.Check(systemId, Equals, "mysystemid2") } func (suite *TestMAASObjectSuite) TestDeleteNode(c *C) { input := `{"system_id": "mysystemid"}` node := suite.TestMAASObject.TestServer.NewNode(input) err := node.Delete() c.Check(err, IsNil) c.Check(suite.TestMAASObject.TestServer.Nodes(), DeepEquals, map[string]MAASObject{}) } func (suite *TestMAASObjectSuite) TestOperationsOnNode(c *C) { input := `{"system_id": "mysystemid"}` node := suite.TestMAASObject.TestServer.NewNode(input) operations := []string{"start", "stop", "release"} for _, operation := range operations { _, err := node.CallPost(operation, url.Values{}) c.Check(err, IsNil) } } func (suite *TestMAASObjectSuite) TestOperationsOnNodeGetRecorded(c *C) { input := `{"system_id": "mysystemid"}` node := suite.TestMAASObject.TestServer.NewNode(input) _, err := node.CallPost("start", url.Values{}) c.Check(err, IsNil) nodeOperations := suite.TestMAASObject.TestServer.NodeOperations() operations := nodeOperations["mysystemid"] c.Check(operations, DeepEquals, []string{"start"}) } func (suite *TestMAASObjectSuite) TestAcquireOperationGetsRecorded(c *C) { input := `{"system_id": "mysystemid"}` suite.TestMAASObject.TestServer.NewNode(input) nodesObj := suite.TestMAASObject.GetSubObject("nodes/") params := url.Values{"key": []string{"value"}} jsonResponse, err := nodesObj.CallPost("acquire", params) c.Assert(err, IsNil) acquiredNode, err := jsonResponse.GetMAASObject() c.Assert(err, IsNil) systemId, err := acquiredNode.GetField("system_id") c.Assert(err, IsNil) // The 'acquire' operation has been recorded. nodeOperations := suite.TestMAASObject.TestServer.NodeOperations() operations := nodeOperations[systemId] c.Check(operations, DeepEquals, []string{"acquire"}) // The parameters used to 'acquire' the node have been recorded as well. values := suite.TestMAASObject.TestServer.NodeOperationRequestValues() value := values[systemId] c.Check(len(value), Equals, 1) c.Check(value[0], DeepEquals, params) } func (suite *TestMAASObjectSuite) TestUploadFile(c *C) { const filename = "myfile.txt" const fileContent = "uploaded contents" files := suite.TestMAASObject.GetSubObject("files") params := url.Values{"filename": {filename}} filesMap := map[string][]byte{"file": []byte(fileContent)} // Upload a file. _, err := files.CallPostFiles("add", params, filesMap) c.Assert(err, IsNil) // The file can now be downloaded. downloadedFile, err := files.CallGet("get", params) c.Assert(err, IsNil) bytes, err := downloadedFile.GetBytes() c.Assert(err, IsNil) c.Check(string(bytes), Equals, fileContent) } func (suite *TestMAASObjectSuite) TestFileNamesMayContainSlashes(c *C) { const filename = "filename/with/slashes/in/it" const fileContent = "file contents" files := suite.TestMAASObject.GetSubObject("files") params := url.Values{"filename": {filename}} filesMap := map[string][]byte{"file": []byte(fileContent)} _, err := files.CallPostFiles("add", params, filesMap) c.Assert(err, IsNil) file, err := files.GetSubObject(filename).Get() c.Assert(err, IsNil) field, err := file.GetField("content") c.Assert(err, IsNil) c.Check(field, Equals, base64.StdEncoding.EncodeToString([]byte(fileContent))) } func (suite *TestMAASObjectSuite) TestAcquireNodeGrabsAvailableNode(c *C) { input := `{"system_id": "nodeid"}` suite.TestMAASObject.TestServer.NewNode(input) nodesObj := suite.TestMAASObject.GetSubObject("nodes/") jsonResponse, err := nodesObj.CallPost("acquire", nil) c.Assert(err, IsNil) acquiredNode, err := jsonResponse.GetMAASObject() c.Assert(err, IsNil) systemID, err := acquiredNode.GetField("system_id") c.Assert(err, IsNil) c.Check(systemID, Equals, "nodeid") _, owned := suite.TestMAASObject.TestServer.OwnedNodes()[systemID] c.Check(owned, Equals, true) } func (suite *TestMAASObjectSuite) TestAcquireNodeNeedsANode(c *C) { nodesObj := suite.TestMAASObject.GetSubObject("nodes/") _, err := nodesObj.CallPost("acquire", nil) c.Check(err.(ServerError).StatusCode, Equals, http.StatusConflict) } func (suite *TestMAASObjectSuite) TestAcquireNodeIgnoresOwnedNodes(c *C) { input := `{"system_id": "nodeid"}` suite.TestMAASObject.TestServer.NewNode(input) nodesObj := suite.TestMAASObject.GetSubObject("nodes/") // Ensure that the one node in the MAAS is not available. _, err := nodesObj.CallPost("acquire", nil) c.Assert(err, IsNil) _, err = nodesObj.CallPost("acquire", nil) c.Check(err.(ServerError).StatusCode, Equals, http.StatusConflict) } func (suite *TestMAASObjectSuite) TestReleaseNodeReleasesAcquiredNode(c *C) { input := `{"system_id": "nodeid"}` suite.TestMAASObject.TestServer.NewNode(input) nodesObj := suite.TestMAASObject.GetSubObject("nodes/") jsonResponse, err := nodesObj.CallPost("acquire", nil) c.Assert(err, IsNil) acquiredNode, err := jsonResponse.GetMAASObject() c.Assert(err, IsNil) systemID, err := acquiredNode.GetField("system_id") c.Assert(err, IsNil) nodeObj := nodesObj.GetSubObject(systemID) _, err = nodeObj.CallPost("release", nil) c.Assert(err, IsNil) _, owned := suite.TestMAASObject.TestServer.OwnedNodes()[systemID] c.Check(owned, Equals, false) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/�����������������������������������������������������������0000755�0000153�0000161�00000000000�12321735726�020102� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/testutil/��������������������������������������������������0000755�0000153�0000161�00000000000�12321735726�021757� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/testutil/http.go�������������������������������������������0000644�0000153�0000161�00000010160�12321735726�023263� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package testutil import ( "bytes" "fmt" "io/ioutil" "net" "net/http" "net/url" "os" "time" ) type HTTPServer struct { URL string Timeout time.Duration started bool listener net.Listener request chan *http.Request response chan ResponseFunc } type Response struct { Status int Headers map[string]string Body string } func NewHTTPServer() *HTTPServer { return &HTTPServer{URL: "http://localhost:4444", Timeout: 5 * time.Second} } type ResponseFunc func(path string) Response func (s *HTTPServer) Start() { if s.started { return } s.started = true s.request = make(chan *http.Request, 1024) s.response = make(chan ResponseFunc, 1024) u, err := url.Parse(s.URL) if err != nil { panic(err) } s.listener, err = net.Listen("tcp", u.Host) if err != nil { panic(err) } go http.Serve(s.listener, s) s.Response(203, nil, "") for { // Wait for it to be up. resp, err := http.Get(s.URL) if err == nil && resp.StatusCode == 203 { break } time.Sleep(1e8) } s.WaitRequest() // Consume dummy request. } func (s *HTTPServer) Stop() { if s.listener == nil { return } err := s.listener.Close() if err != nil { panic(err) } s.listener = nil } // Flush discards all pending requests and responses. func (s *HTTPServer) Flush() { for { select { case <-s.request: case <-s.response: default: return } } } func body(req *http.Request) string { data, err := ioutil.ReadAll(req.Body) if err != nil { panic(err) } return string(data) } func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { req.ParseMultipartForm(1e6) data, err := ioutil.ReadAll(req.Body) if err != nil { panic(err) } req.Body = ioutil.NopCloser(bytes.NewBuffer(data)) s.request <- req var resp Response select { case respFunc := <-s.response: resp = respFunc(req.URL.Path) case <-time.After(s.Timeout): const msg = "ERROR: Timeout waiting for test to prepare a response\n" fmt.Fprintf(os.Stderr, msg) resp = Response{500, nil, msg} } if resp.Headers != nil { h := w.Header() for k, v := range resp.Headers { h.Set(k, v) } } if resp.Status != 0 { w.WriteHeader(resp.Status) } w.Write([]byte(resp.Body)) } // WaitRequests returns the next n requests made to the http server from // the queue. If not enough requests were previously made, it waits until // the timeout value for them to be made. func (s *HTTPServer) WaitRequests(n int) []*http.Request { reqs := make([]*http.Request, 0, n) for i := 0; i < n; i++ { select { case req := <-s.request: reqs = append(reqs, req) case <-time.After(s.Timeout): panic("Timeout waiting for request") } } return reqs } // WaitRequest returns the next request made to the http server from // the queue. If no requests were previously made, it waits until the // timeout value for one to be made. func (s *HTTPServer) WaitRequest() *http.Request { return s.WaitRequests(1)[0] } // ResponseFunc prepares the test server to respond the following n // requests using f to build each response. func (s *HTTPServer) ResponseFunc(n int, f ResponseFunc) { for i := 0; i < n; i++ { s.response <- f } } // ResponseMap maps request paths to responses. type ResponseMap map[string]Response // ResponseMap prepares the test server to respond the following n // requests using the m to obtain the responses. func (s *HTTPServer) ResponseMap(n int, m ResponseMap) { f := func(path string) Response { for rpath, resp := range m { if rpath == path { return resp } } body := "Path not found in response map: " + path return Response{Status: 500, Body: body} } s.ResponseFunc(n, f) } // Responses prepares the test server to respond the following n requests // using the provided response parameters. func (s *HTTPServer) Responses(n int, status int, headers map[string]string, body string) { f := func(path string) Response { return Response{status, headers, body} } s.ResponseFunc(n, f) } // Response prepares the test server to respond the following request // using the provided response parameters. func (s *HTTPServer) Response(status int, headers map[string]string, body string) { s.Responses(1, status, headers, body) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/testutil/suite.go������������������������������������������0000644�0000153�0000161�00000001066�12321735726�023442� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package testutil import ( "flag" "launchpad.net/goamz/aws" . "launchpad.net/gocheck" ) // Amazon must be used by all tested packages to determine whether to // run functional tests against the real AWS servers. var Amazon bool func init() { flag.BoolVar(&Amazon, "amazon", false, "Enable tests against amazon server") } type LiveSuite struct { auth aws.Auth } func (s *LiveSuite) SetUpSuite(c *C) { if !Amazon { c.Skip("amazon tests not enabled (-amazon flag)") } auth, err := aws.EnvAuth() if err != nil { c.Fatal(err.Error()) } s.auth = auth } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/LICENSE����������������������������������������������������0000644�0000153�0000161�00000021053�12321735726�021110� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This software is licensed under the LGPLv3, included below. As a special exception to the GNU Lesser General Public License version 3 ("LGPL3"), the copyright holders of this Library give you permission to convey to a third party a Combined Work that links statically or dynamically to this Library without providing any Minimal Corresponding Source or Minimal Application Code as set out in 4d or providing the installation information set out in section 4e, provided that you comply with the other provisions of LGPL3 and provided that you meet, for the Application the terms and conditions of the license(s) which apply to the Application. Except as stated in this special exception, the provisions of LGPL3 will continue to comply in full to this Library. If you modify this Library, you may apply this exception to your version of this Library, but you are not obliged to do so. If you do not wish to do so, delete this exception statement from your version. This exception does not (and cannot) modify any license terms which apply to the Application, with which you must still comply. GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/�������������������������������������������������������0000755�0000153�0000161�00000000000�12321736016�020544� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/export_test.go�����������������������������������������0000644�0000153�0000161�00000000566�12321736016�023462� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ec2 import ( "launchpad.net/goamz/aws" "time" ) func Sign(auth aws.Auth, method, path string, params map[string]string, host string) { sign(auth, method, path, params, host) } func fixedTime() time.Time { return time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC) } func FakeTime(fakeIt bool) { if fakeIt { timeNow = fixedTime } else { timeNow = time.Now } } ������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/responses_test.go��������������������������������������0000644�0000153�0000161�00000046416�12321736016�024166� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ec2_test var ErrorDump = ` <?xml version="1.0" encoding="UTF-8"?> <Response><Errors><Error><Code>UnsupportedOperation</Code> <Message>AMIs with an instance-store root device are not supported for the instance type 't1.micro'.</Message> </Error></Errors><RequestID>0503f4e9-bbd6-483c-b54f-c4ae9f3b30f4</RequestID></Response> ` // http://goo.gl/Mcm3b var RunInstancesExample = ` <RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <reservationId>r-47a5402e</reservationId> <ownerId>999988887777</ownerId> <groupSet> <item> <groupId>sg-67ad940e</groupId> <groupName>default</groupName> </item> </groupSet> <instancesSet> <item> <instanceId>i-2ba64342</instanceId> <imageId>ami-60a54009</imageId> <instanceState> <code>0</code> <name>pending</name> </instanceState> <privateDnsName></privateDnsName> <dnsName></dnsName> <keyName>example-key-name</keyName> <amiLaunchIndex>0</amiLaunchIndex> <instanceType>m1.small</instanceType> <launchTime>2007-08-07T11:51:50.000Z</launchTime> <placement> <availabilityZone>us-east-1b</availabilityZone> </placement> <monitoring> <state>enabled</state> </monitoring> <virtualizationType>paravirtual</virtualizationType> <clientToken/> <tagSet/> <hypervisor>xen</hypervisor> </item> <item> <instanceId>i-2bc64242</instanceId> <imageId>ami-60a54009</imageId> <instanceState> <code>0</code> <name>pending</name> </instanceState> <privateDnsName></privateDnsName> <dnsName></dnsName> <keyName>example-key-name</keyName> <amiLaunchIndex>1</amiLaunchIndex> <instanceType>m1.small</instanceType> <launchTime>2007-08-07T11:51:50.000Z</launchTime> <placement> <availabilityZone>us-east-1b</availabilityZone> </placement> <monitoring> <state>enabled</state> </monitoring> <virtualizationType>paravirtual</virtualizationType> <clientToken/> <tagSet/> <hypervisor>xen</hypervisor> </item> <item> <instanceId>i-2be64332</instanceId> <imageId>ami-60a54009</imageId> <instanceState> <code>0</code> <name>pending</name> </instanceState> <privateDnsName></privateDnsName> <dnsName></dnsName> <keyName>example-key-name</keyName> <amiLaunchIndex>2</amiLaunchIndex> <instanceType>m1.small</instanceType> <launchTime>2007-08-07T11:51:50.000Z</launchTime> <placement> <availabilityZone>us-east-1b</availabilityZone> </placement> <monitoring> <state>enabled</state> </monitoring> <virtualizationType>paravirtual</virtualizationType> <clientToken/> <tagSet/> <hypervisor>xen</hypervisor> </item> </instancesSet> </RunInstancesResponse> ` // http://goo.gl/3BKHj var TerminateInstancesExample = ` <TerminateInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <instancesSet> <item> <instanceId>i-3ea74257</instanceId> <currentState> <code>32</code> <name>shutting-down</name> </currentState> <previousState> <code>16</code> <name>running</name> </previousState> </item> </instancesSet> </TerminateInstancesResponse> ` // http://goo.gl/mLbmw var DescribeInstancesExample1 = ` <DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>98e3c9a4-848c-4d6d-8e8a-b1bdEXAMPLE</requestId> <reservationSet> <item> <reservationId>r-b27e30d9</reservationId> <ownerId>999988887777</ownerId> <groupSet> <item> <groupId>sg-67ad940e</groupId> <groupName>default</groupName> </item> </groupSet> <instancesSet> <item> <instanceId>i-c5cd56af</instanceId> <imageId>ami-1a2b3c4d</imageId> <instanceState> <code>16</code> <name>running</name> </instanceState> <privateDnsName>domU-12-31-39-10-56-34.compute-1.internal</privateDnsName> <dnsName>ec2-174-129-165-232.compute-1.amazonaws.com</dnsName> <reason/> <keyName>GSG_Keypair</keyName> <amiLaunchIndex>0</amiLaunchIndex> <productCodes/> <instanceType>m1.small</instanceType> <launchTime>2010-08-17T01:15:18.000Z</launchTime> <placement> <availabilityZone>us-east-1b</availabilityZone> <groupName/> </placement> <kernelId>aki-94c527fd</kernelId> <ramdiskId>ari-96c527ff</ramdiskId> <monitoring> <state>disabled</state> </monitoring> <privateIpAddress>10.198.85.190</privateIpAddress> <ipAddress>174.129.165.232</ipAddress> <architecture>i386</architecture> <rootDeviceType>ebs</rootDeviceType> <rootDeviceName>/dev/sda1</rootDeviceName> <blockDeviceMapping> <item> <deviceName>/dev/sda1</deviceName> <ebs> <volumeId>vol-a082c1c9</volumeId> <status>attached</status> <attachTime>2010-08-17T01:15:21.000Z</attachTime> <deleteOnTermination>false</deleteOnTermination> </ebs> </item> </blockDeviceMapping> <instanceLifecycle>spot</instanceLifecycle> <spotInstanceRequestId>sir-7a688402</spotInstanceRequestId> <virtualizationType>paravirtual</virtualizationType> <clientToken/> <tagSet/> <hypervisor>xen</hypervisor> <groupSet> <item> <groupId>sg-67ad940e</groupId> <groupName>default</groupName> </item> </groupSet> </item> </instancesSet> <requesterId>854251627541</requesterId> </item> <item> <reservationId>r-b67e30dd</reservationId> <ownerId>999988887777</ownerId> <groupSet> <item> <groupId>sg-67ad940e</groupId> <groupName>default</groupName> </item> </groupSet> <instancesSet> <item> <instanceId>i-d9cd56b3</instanceId> <imageId>ami-1a2b3c4d</imageId> <instanceState> <code>16</code> <name>running</name> </instanceState> <privateDnsName>domU-12-31-39-10-54-E5.compute-1.internal</privateDnsName> <dnsName>ec2-184-73-58-78.compute-1.amazonaws.com</dnsName> <reason/> <keyName>GSG_Keypair</keyName> <amiLaunchIndex>0</amiLaunchIndex> <productCodes/> <instanceType>m1.large</instanceType> <launchTime>2010-08-17T01:15:19.000Z</launchTime> <placement> <availabilityZone>us-east-1b</availabilityZone> <groupName/> </placement> <kernelId>aki-94c527fd</kernelId> <ramdiskId>ari-96c527ff</ramdiskId> <monitoring> <state>disabled</state> </monitoring> <privateIpAddress>10.198.87.19</privateIpAddress> <ipAddress>184.73.58.78</ipAddress> <architecture>i386</architecture> <rootDeviceType>ebs</rootDeviceType> <rootDeviceName>/dev/sda1</rootDeviceName> <blockDeviceMapping> <item> <deviceName>/dev/sda1</deviceName> <ebs> <volumeId>vol-a282c1cb</volumeId> <status>attached</status> <attachTime>2010-08-17T01:15:23.000Z</attachTime> <deleteOnTermination>false</deleteOnTermination> </ebs> </item> </blockDeviceMapping> <instanceLifecycle>spot</instanceLifecycle> <spotInstanceRequestId>sir-55a3aa02</spotInstanceRequestId> <virtualizationType>paravirtual</virtualizationType> <clientToken/> <tagSet/> <hypervisor>xen</hypervisor> </item> </instancesSet> <requesterId>854251627541</requesterId> </item> </reservationSet> </DescribeInstancesResponse> ` // http://goo.gl/mLbmw var DescribeInstancesExample2 = ` <DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <reservationSet> <item> <reservationId>r-bc7e30d7</reservationId> <ownerId>999988887777</ownerId> <groupSet> <item> <groupId>sg-67ad940e</groupId> <groupName>default</groupName> </item> </groupSet> <instancesSet> <item> <instanceId>i-c7cd56ad</instanceId> <imageId>ami-b232d0db</imageId> <instanceState> <code>16</code> <name>running</name> </instanceState> <privateDnsName>domU-12-31-39-01-76-06.compute-1.internal</privateDnsName> <dnsName>ec2-72-44-52-124.compute-1.amazonaws.com</dnsName> <keyName>GSG_Keypair</keyName> <amiLaunchIndex>0</amiLaunchIndex> <productCodes/> <instanceType>m1.small</instanceType> <launchTime>2010-08-17T01:15:16.000Z</launchTime> <placement> <availabilityZone>us-east-1b</availabilityZone> </placement> <kernelId>aki-94c527fd</kernelId> <ramdiskId>ari-96c527ff</ramdiskId> <monitoring> <state>disabled</state> </monitoring> <privateIpAddress>10.255.121.240</privateIpAddress> <ipAddress>72.44.52.124</ipAddress> <architecture>i386</architecture> <rootDeviceType>ebs</rootDeviceType> <rootDeviceName>/dev/sda1</rootDeviceName> <blockDeviceMapping> <item> <deviceName>/dev/sda1</deviceName> <ebs> <volumeId>vol-a482c1cd</volumeId> <status>attached</status> <attachTime>2010-08-17T01:15:26.000Z</attachTime> <deleteOnTermination>true</deleteOnTermination> </ebs> </item> </blockDeviceMapping> <virtualizationType>paravirtual</virtualizationType> <clientToken/> <tagSet> <item> <key>webserver</key> <value></value> </item> <item> <key>stack</key> <value>Production</value> </item> </tagSet> <hypervisor>xen</hypervisor> </item> </instancesSet> </item> </reservationSet> </DescribeInstancesResponse> ` // http://goo.gl/V0U25 var DescribeImagesExample = ` <DescribeImagesResponse xmlns="http://ec2.amazonaws.com/doc/2012-08-15/"> <requestId>4a4a27a2-2e7c-475d-b35b-ca822EXAMPLE</requestId> <imagesSet> <item> <imageId>ami-a2469acf</imageId> <imageLocation>aws-marketplace/example-marketplace-amzn-ami.1</imageLocation> <imageState>available</imageState> <imageOwnerId>123456789999</imageOwnerId> <isPublic>true</isPublic> <productCodes> <item> <productCode>a1b2c3d4e5f6g7h8i9j10k11</productCode> <type>marketplace</type> </item> </productCodes> <architecture>i386</architecture> <imageType>machine</imageType> <kernelId>aki-805ea7e9</kernelId> <imageOwnerAlias>aws-marketplace</imageOwnerAlias> <name>example-marketplace-amzn-ami.1</name> <description>Amazon Linux AMI i386 EBS</description> <rootDeviceType>ebs</rootDeviceType> <rootDeviceName>/dev/sda1</rootDeviceName> <blockDeviceMapping> <item> <deviceName>/dev/sda1</deviceName> <ebs> <snapshotId>snap-787e9403</snapshotId> <volumeSize>8</volumeSize> <deleteOnTermination>true</deleteOnTermination> </ebs> </item> </blockDeviceMapping> <virtualizationType>paravirtual</virtualizationType> <hypervisor>xen</hypervisor> </item> </imagesSet> </DescribeImagesResponse> ` // http://goo.gl/ttcda var CreateSnapshotExample = ` <CreateSnapshotResponse xmlns="http://ec2.amazonaws.com/doc/2012-10-01/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <snapshotId>snap-78a54011</snapshotId> <volumeId>vol-4d826724</volumeId> <status>pending</status> <startTime>2008-05-07T12:51:50.000Z</startTime> <progress>60%</progress> <ownerId>111122223333</ownerId> <volumeSize>10</volumeSize> <description>Daily Backup</description> </CreateSnapshotResponse> ` // http://goo.gl/vwU1y var DeleteSnapshotExample = ` <DeleteSnapshotResponse xmlns="http://ec2.amazonaws.com/doc/2012-10-01/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <return>true</return> </DeleteSnapshotResponse> ` // http://goo.gl/nkovs var DescribeSnapshotsExample = ` <DescribeSnapshotsResponse xmlns="http://ec2.amazonaws.com/doc/2012-10-01/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <snapshotSet> <item> <snapshotId>snap-1a2b3c4d</snapshotId> <volumeId>vol-8875daef</volumeId> <status>pending</status> <startTime>2010-07-29T04:12:01.000Z</startTime> <progress>30%</progress> <ownerId>111122223333</ownerId> <volumeSize>15</volumeSize> <description>Daily Backup</description> <tagSet> <item> <key>Purpose</key> <value>demo_db_14_backup</value> </item> </tagSet> </item> </snapshotSet> </DescribeSnapshotsResponse> ` // http://goo.gl/Eo7Yl var CreateSecurityGroupExample = ` <CreateSecurityGroupResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <return>true</return> <groupId>sg-67ad940e</groupId> </CreateSecurityGroupResponse> ` // http://goo.gl/k12Uy var DescribeSecurityGroupsExample = ` <DescribeSecurityGroupsResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <securityGroupInfo> <item> <ownerId>999988887777</ownerId> <groupName>WebServers</groupName> <groupId>sg-67ad940e</groupId> <groupDescription>Web Servers</groupDescription> <ipPermissions> <item> <ipProtocol>tcp</ipProtocol> <fromPort>80</fromPort> <toPort>80</toPort> <groups/> <ipRanges> <item> <cidrIp>0.0.0.0/0</cidrIp> </item> </ipRanges> </item> </ipPermissions> </item> <item> <ownerId>999988887777</ownerId> <groupName>RangedPortsBySource</groupName> <groupId>sg-76abc467</groupId> <groupDescription>Group A</groupDescription> <ipPermissions> <item> <ipProtocol>tcp</ipProtocol> <fromPort>6000</fromPort> <toPort>7000</toPort> <groups/> <ipRanges/> </item> </ipPermissions> </item> </securityGroupInfo> </DescribeSecurityGroupsResponse> ` // A dump which includes groups within ip permissions. var DescribeSecurityGroupsDump = ` <?xml version="1.0" encoding="UTF-8"?> <DescribeSecurityGroupsResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>87b92b57-cc6e-48b2-943f-f6f0e5c9f46c</requestId> <securityGroupInfo> <item> <ownerId>12345</ownerId> <groupName>default</groupName> <groupDescription>default group</groupDescription> <ipPermissions> <item> <ipProtocol>icmp</ipProtocol> <fromPort>-1</fromPort> <toPort>-1</toPort> <groups> <item> <userId>12345</userId> <groupName>default</groupName> <groupId>sg-67ad940e</groupId> </item> </groups> <ipRanges/> </item> <item> <ipProtocol>tcp</ipProtocol> <fromPort>0</fromPort> <toPort>65535</toPort> <groups> <item> <userId>12345</userId> <groupName>other</groupName> <groupId>sg-76abc467</groupId> </item> </groups> <ipRanges/> </item> </ipPermissions> </item> </securityGroupInfo> </DescribeSecurityGroupsResponse> ` // http://goo.gl/QJJDO var DeleteSecurityGroupExample = ` <DeleteSecurityGroupResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <return>true</return> </DeleteSecurityGroupResponse> ` // http://goo.gl/u2sDJ var AuthorizeSecurityGroupIngressExample = ` <AuthorizeSecurityGroupIngressResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <return>true</return> </AuthorizeSecurityGroupIngressResponse> ` // http://goo.gl/Mz7xr var RevokeSecurityGroupIngressExample = ` <RevokeSecurityGroupIngressResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <return>true</return> </RevokeSecurityGroupIngressResponse> ` // http://goo.gl/Vmkqc var CreateTagsExample = ` <CreateTagsResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <return>true</return> </CreateTagsResponse> ` // http://goo.gl/awKeF var StartInstancesExample = ` <StartInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <instancesSet> <item> <instanceId>i-10a64379</instanceId> <currentState> <code>0</code> <name>pending</name> </currentState> <previousState> <code>80</code> <name>stopped</name> </previousState> </item> </instancesSet> </StartInstancesResponse> ` // http://goo.gl/436dJ var StopInstancesExample = ` <StopInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <instancesSet> <item> <instanceId>i-10a64379</instanceId> <currentState> <code>64</code> <name>stopping</name> </currentState> <previousState> <code>16</code> <name>running</name> </previousState> </item> </instancesSet> </StopInstancesResponse> ` // http://goo.gl/baoUf var RebootInstancesExample = ` <RebootInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> <return>true</return> </RebootInstancesResponse> ` ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/ec2_test.go��������������������������������������������0000644�0000153�0000161�00000065740�12321736016�022617� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ec2_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/ec2" "launchpad.net/goamz/testutil" . "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { TestingT(t) } var _ = Suite(&S{}) type S struct { ec2 *ec2.EC2 } var testServer = testutil.NewHTTPServer() func (s *S) SetUpSuite(c *C) { testServer.Start() auth := aws.Auth{"abc", "123"} s.ec2 = ec2.New(auth, aws.Region{EC2Endpoint: testServer.URL}) } func (s *S) TearDownSuite(c *C) { testServer.Stop() } func (s *S) TearDownTest(c *C) { testServer.Flush() } func (s *S) TestRunInstancesErrorDump(c *C) { testServer.Response(400, nil, ErrorDump) options := ec2.RunInstances{ ImageId: "ami-a6f504cf", // Ubuntu Maverick, i386, instance store InstanceType: "t1.micro", // Doesn't work with micro, results in 400. } msg := `AMIs with an instance-store root device are not supported for the instance type 't1\.micro'\.` resp, err := s.ec2.RunInstances(&options) testServer.WaitRequest() c.Assert(resp, IsNil) c.Assert(err, ErrorMatches, msg+` \(UnsupportedOperation\)`) ec2err, ok := err.(*ec2.Error) c.Assert(ok, Equals, true) c.Assert(ec2err.StatusCode, Equals, 400) c.Assert(ec2err.Code, Equals, "UnsupportedOperation") c.Assert(ec2err.Message, Matches, msg) c.Assert(ec2err.RequestId, Equals, "0503f4e9-bbd6-483c-b54f-c4ae9f3b30f4") } func (s *S) TestRunInstancesErrorWithoutXML(c *C) { testServer.Response(500, nil, "") options := ec2.RunInstances{ImageId: "image-id"} resp, err := s.ec2.RunInstances(&options) testServer.WaitRequest() c.Assert(resp, IsNil) c.Assert(err, ErrorMatches, "500 Internal Server Error") ec2err, ok := err.(*ec2.Error) c.Assert(ok, Equals, true) c.Assert(ec2err.StatusCode, Equals, 500) c.Assert(ec2err.Code, Equals, "") c.Assert(ec2err.Message, Equals, "500 Internal Server Error") c.Assert(ec2err.RequestId, Equals, "") } func (s *S) TestRunInstancesExample(c *C) { testServer.Response(200, nil, RunInstancesExample) options := ec2.RunInstances{ KeyName: "my-keys", ImageId: "image-id", InstanceType: "inst-type", SecurityGroups: []ec2.SecurityGroup{{Name: "g1"}, {Id: "g2"}, {Name: "g3"}, {Id: "g4"}}, UserData: []byte("1234"), KernelId: "kernel-id", RamdiskId: "ramdisk-id", AvailZone: "zone", PlacementGroupName: "group", Monitoring: true, SubnetId: "subnet-id", DisableAPITermination: true, ShutdownBehavior: "terminate", PrivateIPAddress: "10.0.0.25", BlockDeviceMappings: []ec2.BlockDeviceMapping{{ DeviceName: "device-name", VirtualName: "virtual-name", SnapshotId: "snapshot-id", VolumeType: "volume-type", VolumeSize: 10, DeleteOnTermination: true, IOPS: 1000, }}, } resp, err := s.ec2.RunInstances(&options) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"RunInstances"}) c.Assert(req.Form["ImageId"], DeepEquals, []string{"image-id"}) c.Assert(req.Form["MinCount"], DeepEquals, []string{"1"}) c.Assert(req.Form["MaxCount"], DeepEquals, []string{"1"}) c.Assert(req.Form["KeyName"], DeepEquals, []string{"my-keys"}) c.Assert(req.Form["InstanceType"], DeepEquals, []string{"inst-type"}) c.Assert(req.Form["SecurityGroup.1"], DeepEquals, []string{"g1"}) c.Assert(req.Form["SecurityGroup.2"], DeepEquals, []string{"g3"}) c.Assert(req.Form["SecurityGroupId.1"], DeepEquals, []string{"g2"}) c.Assert(req.Form["SecurityGroupId.2"], DeepEquals, []string{"g4"}) c.Assert(req.Form["UserData"], DeepEquals, []string{"MTIzNA=="}) c.Assert(req.Form["KernelId"], DeepEquals, []string{"kernel-id"}) c.Assert(req.Form["RamdiskId"], DeepEquals, []string{"ramdisk-id"}) c.Assert(req.Form["Placement.AvailabilityZone"], DeepEquals, []string{"zone"}) c.Assert(req.Form["Placement.GroupName"], DeepEquals, []string{"group"}) c.Assert(req.Form["Monitoring.Enabled"], DeepEquals, []string{"true"}) c.Assert(req.Form["SubnetId"], DeepEquals, []string{"subnet-id"}) c.Assert(req.Form["DisableApiTermination"], DeepEquals, []string{"true"}) c.Assert(req.Form["InstanceInitiatedShutdownBehavior"], DeepEquals, []string{"terminate"}) c.Assert(req.Form["PrivateIpAddress"], DeepEquals, []string{"10.0.0.25"}) c.Assert(req.Form["BlockDeviceMapping.1.DeviceName"], DeepEquals, []string{"device-name"}) c.Assert(req.Form["BlockDeviceMapping.1.VirtualName"], DeepEquals, []string{"virtual-name"}) c.Assert(req.Form["BlockDeviceMapping.1.Ebs.SnapshotId"], DeepEquals, []string{"snapshot-id"}) c.Assert(req.Form["BlockDeviceMapping.1.Ebs.VolumeType"], DeepEquals, []string{"volume-type"}) c.Assert(req.Form["BlockDeviceMapping.1.Ebs.VolumeSize"], DeepEquals, []string{"10"}) c.Assert(req.Form["BlockDeviceMapping.1.Ebs.Iops"], DeepEquals, []string{"1000"}) c.Assert(req.Form["BlockDeviceMapping.1.Ebs.DeleteOnTermination"], DeepEquals, []string{"true"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") c.Assert(resp.ReservationId, Equals, "r-47a5402e") c.Assert(resp.OwnerId, Equals, "999988887777") c.Assert(resp.SecurityGroups, DeepEquals, []ec2.SecurityGroup{{Name: "default", Id: "sg-67ad940e"}}) c.Assert(resp.Instances, HasLen, 3) i0 := resp.Instances[0] c.Assert(i0.InstanceId, Equals, "i-2ba64342") c.Assert(i0.InstanceType, Equals, "m1.small") c.Assert(i0.ImageId, Equals, "ami-60a54009") c.Assert(i0.Monitoring, Equals, "enabled") c.Assert(i0.KeyName, Equals, "example-key-name") c.Assert(i0.AMILaunchIndex, Equals, 0) c.Assert(i0.VirtType, Equals, "paravirtual") c.Assert(i0.Hypervisor, Equals, "xen") i1 := resp.Instances[1] c.Assert(i1.InstanceId, Equals, "i-2bc64242") c.Assert(i1.InstanceType, Equals, "m1.small") c.Assert(i1.ImageId, Equals, "ami-60a54009") c.Assert(i1.Monitoring, Equals, "enabled") c.Assert(i1.KeyName, Equals, "example-key-name") c.Assert(i1.AMILaunchIndex, Equals, 1) c.Assert(i1.VirtType, Equals, "paravirtual") c.Assert(i1.Hypervisor, Equals, "xen") i2 := resp.Instances[2] c.Assert(i2.InstanceId, Equals, "i-2be64332") c.Assert(i2.InstanceType, Equals, "m1.small") c.Assert(i2.ImageId, Equals, "ami-60a54009") c.Assert(i2.Monitoring, Equals, "enabled") c.Assert(i2.KeyName, Equals, "example-key-name") c.Assert(i2.AMILaunchIndex, Equals, 2) c.Assert(i2.VirtType, Equals, "paravirtual") c.Assert(i2.Hypervisor, Equals, "xen") } func (s *S) TestTerminateInstancesExample(c *C) { testServer.Response(200, nil, TerminateInstancesExample) resp, err := s.ec2.TerminateInstances([]string{"i-1", "i-2"}) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"TerminateInstances"}) c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-1"}) c.Assert(req.Form["InstanceId.2"], DeepEquals, []string{"i-2"}) c.Assert(req.Form["UserData"], IsNil) c.Assert(req.Form["KernelId"], IsNil) c.Assert(req.Form["RamdiskId"], IsNil) c.Assert(req.Form["Placement.AvailabilityZone"], IsNil) c.Assert(req.Form["Placement.GroupName"], IsNil) c.Assert(req.Form["Monitoring.Enabled"], IsNil) c.Assert(req.Form["SubnetId"], IsNil) c.Assert(req.Form["DisableApiTermination"], IsNil) c.Assert(req.Form["InstanceInitiatedShutdownBehavior"], IsNil) c.Assert(req.Form["PrivateIpAddress"], IsNil) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") c.Assert(resp.StateChanges, HasLen, 1) c.Assert(resp.StateChanges[0].InstanceId, Equals, "i-3ea74257") c.Assert(resp.StateChanges[0].CurrentState.Code, Equals, 32) c.Assert(resp.StateChanges[0].CurrentState.Name, Equals, "shutting-down") c.Assert(resp.StateChanges[0].PreviousState.Code, Equals, 16) c.Assert(resp.StateChanges[0].PreviousState.Name, Equals, "running") } func (s *S) TestDescribeInstancesExample1(c *C) { testServer.Response(200, nil, DescribeInstancesExample1) filter := ec2.NewFilter() filter.Add("key1", "value1") filter.Add("key2", "value2", "value3") resp, err := s.ec2.Instances([]string{"i-1", "i-2"}, nil) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeInstances"}) c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-1"}) c.Assert(req.Form["InstanceId.2"], DeepEquals, []string{"i-2"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "98e3c9a4-848c-4d6d-8e8a-b1bdEXAMPLE") c.Assert(resp.Reservations, HasLen, 2) expectedGroups := []ec2.SecurityGroup{{Name: "default", Id: "sg-67ad940e"}} r0 := resp.Reservations[0] c.Assert(r0.ReservationId, Equals, "r-b27e30d9") c.Assert(r0.OwnerId, Equals, "999988887777") c.Assert(r0.RequesterId, Equals, "854251627541") c.Assert(r0.SecurityGroups, DeepEquals, expectedGroups) c.Assert(r0.Instances, HasLen, 1) r0i := r0.Instances[0] c.Assert(r0i.InstanceId, Equals, "i-c5cd56af") c.Assert(r0i.PrivateDNSName, Equals, "domU-12-31-39-10-56-34.compute-1.internal") c.Assert(r0i.DNSName, Equals, "ec2-174-129-165-232.compute-1.amazonaws.com") c.Assert(r0i.PrivateIPAddress, Equals, "10.198.85.190") c.Assert(r0i.IPAddress, Equals, "174.129.165.232") c.Assert(r0i.AvailZone, Equals, "us-east-1b") c.Assert(r0i.SecurityGroups, DeepEquals, expectedGroups) } func (s *S) TestDescribeInstancesExample2(c *C) { testServer.Response(200, nil, DescribeInstancesExample2) filter := ec2.NewFilter() filter.Add("key1", "value1") filter.Add("key2", "value2", "value3") resp, err := s.ec2.Instances([]string{"i-1", "i-2"}, filter) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeInstances"}) c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-1"}) c.Assert(req.Form["InstanceId.2"], DeepEquals, []string{"i-2"}) c.Assert(req.Form["Filter.1.Name"], DeepEquals, []string{"key1"}) c.Assert(req.Form["Filter.1.Value.1"], DeepEquals, []string{"value1"}) c.Assert(req.Form["Filter.1.Value.2"], IsNil) c.Assert(req.Form["Filter.2.Name"], DeepEquals, []string{"key2"}) c.Assert(req.Form["Filter.2.Value.1"], DeepEquals, []string{"value2"}) c.Assert(req.Form["Filter.2.Value.2"], DeepEquals, []string{"value3"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") c.Assert(resp.Reservations, HasLen, 1) r0 := resp.Reservations[0] r0i := r0.Instances[0] c.Assert(r0i.State.Code, Equals, 16) c.Assert(r0i.State.Name, Equals, "running") r0t0 := r0i.Tags[0] r0t1 := r0i.Tags[1] c.Assert(r0t0.Key, Equals, "webserver") c.Assert(r0t0.Value, Equals, "") c.Assert(r0t1.Key, Equals, "stack") c.Assert(r0t1.Value, Equals, "Production") } func (s *S) TestDescribeImagesExample(c *C) { testServer.Response(200, nil, DescribeImagesExample) filter := ec2.NewFilter() filter.Add("key1", "value1") filter.Add("key2", "value2", "value3") resp, err := s.ec2.Images([]string{"ami-1", "ami-2"}, filter) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeImages"}) c.Assert(req.Form["ImageId.1"], DeepEquals, []string{"ami-1"}) c.Assert(req.Form["ImageId.2"], DeepEquals, []string{"ami-2"}) c.Assert(req.Form["Filter.1.Name"], DeepEquals, []string{"key1"}) c.Assert(req.Form["Filter.1.Value.1"], DeepEquals, []string{"value1"}) c.Assert(req.Form["Filter.1.Value.2"], IsNil) c.Assert(req.Form["Filter.2.Name"], DeepEquals, []string{"key2"}) c.Assert(req.Form["Filter.2.Value.1"], DeepEquals, []string{"value2"}) c.Assert(req.Form["Filter.2.Value.2"], DeepEquals, []string{"value3"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "4a4a27a2-2e7c-475d-b35b-ca822EXAMPLE") c.Assert(resp.Images, HasLen, 1) i0 := resp.Images[0] c.Assert(i0.Id, Equals, "ami-a2469acf") c.Assert(i0.Type, Equals, "machine") c.Assert(i0.Name, Equals, "example-marketplace-amzn-ami.1") c.Assert(i0.Description, Equals, "Amazon Linux AMI i386 EBS") c.Assert(i0.Location, Equals, "aws-marketplace/example-marketplace-amzn-ami.1") c.Assert(i0.State, Equals, "available") c.Assert(i0.Public, Equals, true) c.Assert(i0.OwnerId, Equals, "123456789999") c.Assert(i0.OwnerAlias, Equals, "aws-marketplace") c.Assert(i0.Architecture, Equals, "i386") c.Assert(i0.KernelId, Equals, "aki-805ea7e9") c.Assert(i0.RootDeviceType, Equals, "ebs") c.Assert(i0.RootDeviceName, Equals, "/dev/sda1") c.Assert(i0.VirtualizationType, Equals, "paravirtual") c.Assert(i0.Hypervisor, Equals, "xen") c.Assert(i0.BlockDevices, HasLen, 1) c.Assert(i0.BlockDevices[0].DeviceName, Equals, "/dev/sda1") c.Assert(i0.BlockDevices[0].SnapshotId, Equals, "snap-787e9403") c.Assert(i0.BlockDevices[0].VolumeSize, Equals, int64(8)) c.Assert(i0.BlockDevices[0].DeleteOnTermination, Equals, true) } func (s *S) TestCreateSnapshotExample(c *C) { testServer.Response(200, nil, CreateSnapshotExample) resp, err := s.ec2.CreateSnapshot("vol-4d826724", "Daily Backup") req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"CreateSnapshot"}) c.Assert(req.Form["VolumeId"], DeepEquals, []string{"vol-4d826724"}) c.Assert(req.Form["Description"], DeepEquals, []string{"Daily Backup"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") c.Assert(resp.Snapshot.Id, Equals, "snap-78a54011") c.Assert(resp.Snapshot.VolumeId, Equals, "vol-4d826724") c.Assert(resp.Snapshot.Status, Equals, "pending") c.Assert(resp.Snapshot.StartTime, Equals, "2008-05-07T12:51:50.000Z") c.Assert(resp.Snapshot.Progress, Equals, "60%") c.Assert(resp.Snapshot.OwnerId, Equals, "111122223333") c.Assert(resp.Snapshot.VolumeSize, Equals, "10") c.Assert(resp.Snapshot.Description, Equals, "Daily Backup") } func (s *S) TestDeleteSnapshotsExample(c *C) { testServer.Response(200, nil, DeleteSnapshotExample) resp, err := s.ec2.DeleteSnapshots([]string{"snap-78a54011"}) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteSnapshot"}) c.Assert(req.Form["SnapshotId.1"], DeepEquals, []string{"snap-78a54011"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") } func (s *S) TestDescribeSnapshotsExample(c *C) { testServer.Response(200, nil, DescribeSnapshotsExample) filter := ec2.NewFilter() filter.Add("key1", "value1") filter.Add("key2", "value2", "value3") resp, err := s.ec2.Snapshots([]string{"snap-1", "snap-2"}, filter) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeSnapshots"}) c.Assert(req.Form["SnapshotId.1"], DeepEquals, []string{"snap-1"}) c.Assert(req.Form["SnapshotId.2"], DeepEquals, []string{"snap-2"}) c.Assert(req.Form["Filter.1.Name"], DeepEquals, []string{"key1"}) c.Assert(req.Form["Filter.1.Value.1"], DeepEquals, []string{"value1"}) c.Assert(req.Form["Filter.1.Value.2"], IsNil) c.Assert(req.Form["Filter.2.Name"], DeepEquals, []string{"key2"}) c.Assert(req.Form["Filter.2.Value.1"], DeepEquals, []string{"value2"}) c.Assert(req.Form["Filter.2.Value.2"], DeepEquals, []string{"value3"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") c.Assert(resp.Snapshots, HasLen, 1) s0 := resp.Snapshots[0] c.Assert(s0.Id, Equals, "snap-1a2b3c4d") c.Assert(s0.VolumeId, Equals, "vol-8875daef") c.Assert(s0.VolumeSize, Equals, "15") c.Assert(s0.Status, Equals, "pending") c.Assert(s0.StartTime, Equals, "2010-07-29T04:12:01.000Z") c.Assert(s0.Progress, Equals, "30%") c.Assert(s0.OwnerId, Equals, "111122223333") c.Assert(s0.Description, Equals, "Daily Backup") c.Assert(s0.Tags, HasLen, 1) c.Assert(s0.Tags[0].Key, Equals, "Purpose") c.Assert(s0.Tags[0].Value, Equals, "demo_db_14_backup") } func (s *S) TestCreateSecurityGroupExample(c *C) { testServer.Response(200, nil, CreateSecurityGroupExample) resp, err := s.ec2.CreateSecurityGroup("websrv", "Web Servers") req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"CreateSecurityGroup"}) c.Assert(req.Form["GroupName"], DeepEquals, []string{"websrv"}) c.Assert(req.Form["GroupDescription"], DeepEquals, []string{"Web Servers"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") c.Assert(resp.Name, Equals, "websrv") c.Assert(resp.Id, Equals, "sg-67ad940e") } func (s *S) TestDescribeSecurityGroupsExample(c *C) { testServer.Response(200, nil, DescribeSecurityGroupsExample) resp, err := s.ec2.SecurityGroups([]ec2.SecurityGroup{{Name: "WebServers"}, {Name: "RangedPortsBySource"}}, nil) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeSecurityGroups"}) c.Assert(req.Form["GroupName.1"], DeepEquals, []string{"WebServers"}) c.Assert(req.Form["GroupName.2"], DeepEquals, []string{"RangedPortsBySource"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") c.Assert(resp.Groups, HasLen, 2) g0 := resp.Groups[0] c.Assert(g0.OwnerId, Equals, "999988887777") c.Assert(g0.Name, Equals, "WebServers") c.Assert(g0.Id, Equals, "sg-67ad940e") c.Assert(g0.Description, Equals, "Web Servers") c.Assert(g0.IPPerms, HasLen, 1) g0ipp := g0.IPPerms[0] c.Assert(g0ipp.Protocol, Equals, "tcp") c.Assert(g0ipp.FromPort, Equals, 80) c.Assert(g0ipp.ToPort, Equals, 80) c.Assert(g0ipp.SourceIPs, DeepEquals, []string{"0.0.0.0/0"}) g1 := resp.Groups[1] c.Assert(g1.OwnerId, Equals, "999988887777") c.Assert(g1.Name, Equals, "RangedPortsBySource") c.Assert(g1.Id, Equals, "sg-76abc467") c.Assert(g1.Description, Equals, "Group A") c.Assert(g1.IPPerms, HasLen, 1) g1ipp := g1.IPPerms[0] c.Assert(g1ipp.Protocol, Equals, "tcp") c.Assert(g1ipp.FromPort, Equals, 6000) c.Assert(g1ipp.ToPort, Equals, 7000) c.Assert(g1ipp.SourceIPs, IsNil) } func (s *S) TestDescribeSecurityGroupsExampleWithFilter(c *C) { testServer.Response(200, nil, DescribeSecurityGroupsExample) filter := ec2.NewFilter() filter.Add("ip-permission.protocol", "tcp") filter.Add("ip-permission.from-port", "22") filter.Add("ip-permission.to-port", "22") filter.Add("ip-permission.group-name", "app_server_group", "database_group") _, err := s.ec2.SecurityGroups(nil, filter) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeSecurityGroups"}) c.Assert(req.Form["Filter.1.Name"], DeepEquals, []string{"ip-permission.from-port"}) c.Assert(req.Form["Filter.1.Value.1"], DeepEquals, []string{"22"}) c.Assert(req.Form["Filter.2.Name"], DeepEquals, []string{"ip-permission.group-name"}) c.Assert(req.Form["Filter.2.Value.1"], DeepEquals, []string{"app_server_group"}) c.Assert(req.Form["Filter.2.Value.2"], DeepEquals, []string{"database_group"}) c.Assert(req.Form["Filter.3.Name"], DeepEquals, []string{"ip-permission.protocol"}) c.Assert(req.Form["Filter.3.Value.1"], DeepEquals, []string{"tcp"}) c.Assert(req.Form["Filter.4.Name"], DeepEquals, []string{"ip-permission.to-port"}) c.Assert(req.Form["Filter.4.Value.1"], DeepEquals, []string{"22"}) c.Assert(err, IsNil) } func (s *S) TestDescribeSecurityGroupsDumpWithGroup(c *C) { testServer.Response(200, nil, DescribeSecurityGroupsDump) resp, err := s.ec2.SecurityGroups(nil, nil) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"DescribeSecurityGroups"}) c.Assert(err, IsNil) c.Check(resp.Groups, HasLen, 1) c.Check(resp.Groups[0].IPPerms, HasLen, 2) ipp0 := resp.Groups[0].IPPerms[0] c.Assert(ipp0.SourceIPs, IsNil) c.Check(ipp0.Protocol, Equals, "icmp") c.Assert(ipp0.SourceGroups, HasLen, 1) c.Check(ipp0.SourceGroups[0].OwnerId, Equals, "12345") c.Check(ipp0.SourceGroups[0].Name, Equals, "default") c.Check(ipp0.SourceGroups[0].Id, Equals, "sg-67ad940e") ipp1 := resp.Groups[0].IPPerms[1] c.Check(ipp1.Protocol, Equals, "tcp") c.Assert(ipp0.SourceIPs, IsNil) c.Assert(ipp0.SourceGroups, HasLen, 1) c.Check(ipp1.SourceGroups[0].Id, Equals, "sg-76abc467") c.Check(ipp1.SourceGroups[0].OwnerId, Equals, "12345") c.Check(ipp1.SourceGroups[0].Name, Equals, "other") } func (s *S) TestDeleteSecurityGroupExample(c *C) { testServer.Response(200, nil, DeleteSecurityGroupExample) resp, err := s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: "websrv"}) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"DeleteSecurityGroup"}) c.Assert(req.Form["GroupName"], DeepEquals, []string{"websrv"}) c.Assert(req.Form["GroupId"], IsNil) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") } func (s *S) TestDeleteSecurityGroupExampleWithId(c *C) { testServer.Response(200, nil, DeleteSecurityGroupExample) // ignore return and error - we're only want to check the parameter handling. s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Id: "sg-67ad940e", Name: "ignored"}) req := testServer.WaitRequest() c.Assert(req.Form["GroupName"], IsNil) c.Assert(req.Form["GroupId"], DeepEquals, []string{"sg-67ad940e"}) } func (s *S) TestAuthorizeSecurityGroupExample1(c *C) { testServer.Response(200, nil, AuthorizeSecurityGroupIngressExample) perms := []ec2.IPPerm{{ Protocol: "tcp", FromPort: 80, ToPort: 80, SourceIPs: []string{"205.192.0.0/16", "205.159.0.0/16"}, }} resp, err := s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: "websrv"}, perms) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"AuthorizeSecurityGroupIngress"}) c.Assert(req.Form["GroupName"], DeepEquals, []string{"websrv"}) c.Assert(req.Form["IpPermissions.1.IpProtocol"], DeepEquals, []string{"tcp"}) c.Assert(req.Form["IpPermissions.1.FromPort"], DeepEquals, []string{"80"}) c.Assert(req.Form["IpPermissions.1.ToPort"], DeepEquals, []string{"80"}) c.Assert(req.Form["IpPermissions.1.IpRanges.1.CidrIp"], DeepEquals, []string{"205.192.0.0/16"}) c.Assert(req.Form["IpPermissions.1.IpRanges.2.CidrIp"], DeepEquals, []string{"205.159.0.0/16"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") } func (s *S) TestAuthorizeSecurityGroupExample1WithId(c *C) { testServer.Response(200, nil, AuthorizeSecurityGroupIngressExample) perms := []ec2.IPPerm{{ Protocol: "tcp", FromPort: 80, ToPort: 80, SourceIPs: []string{"205.192.0.0/16", "205.159.0.0/16"}, }} // ignore return and error - we're only want to check the parameter handling. s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Id: "sg-67ad940e", Name: "ignored"}, perms) req := testServer.WaitRequest() c.Assert(req.Form["GroupName"], IsNil) c.Assert(req.Form["GroupId"], DeepEquals, []string{"sg-67ad940e"}) } func (s *S) TestAuthorizeSecurityGroupExample2(c *C) { testServer.Response(200, nil, AuthorizeSecurityGroupIngressExample) perms := []ec2.IPPerm{{ Protocol: "tcp", FromPort: 80, ToPort: 81, SourceGroups: []ec2.UserSecurityGroup{ {OwnerId: "999988887777", Name: "OtherAccountGroup"}, {Id: "sg-67ad940e"}, }, }} resp, err := s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: "websrv"}, perms) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"AuthorizeSecurityGroupIngress"}) c.Assert(req.Form["GroupName"], DeepEquals, []string{"websrv"}) c.Assert(req.Form["IpPermissions.1.IpProtocol"], DeepEquals, []string{"tcp"}) c.Assert(req.Form["IpPermissions.1.FromPort"], DeepEquals, []string{"80"}) c.Assert(req.Form["IpPermissions.1.ToPort"], DeepEquals, []string{"81"}) c.Assert(req.Form["IpPermissions.1.Groups.1.UserId"], DeepEquals, []string{"999988887777"}) c.Assert(req.Form["IpPermissions.1.Groups.1.GroupName"], DeepEquals, []string{"OtherAccountGroup"}) c.Assert(req.Form["IpPermissions.1.Groups.2.UserId"], IsNil) c.Assert(req.Form["IpPermissions.1.Groups.2.GroupName"], IsNil) c.Assert(req.Form["IpPermissions.1.Groups.2.GroupId"], DeepEquals, []string{"sg-67ad940e"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") } func (s *S) TestRevokeSecurityGroupExample(c *C) { // RevokeSecurityGroup is implemented by the same code as AuthorizeSecurityGroup // so there's no need to duplicate all the tests. testServer.Response(200, nil, RevokeSecurityGroupIngressExample) resp, err := s.ec2.RevokeSecurityGroup(ec2.SecurityGroup{Name: "websrv"}, nil) req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"RevokeSecurityGroupIngress"}) c.Assert(req.Form["GroupName"], DeepEquals, []string{"websrv"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") } func (s *S) TestCreateTags(c *C) { testServer.Response(200, nil, CreateTagsExample) resp, err := s.ec2.CreateTags([]string{"ami-1a2b3c4d", "i-7f4d3a2b"}, []ec2.Tag{{"webserver", ""}, {"stack", "Production"}}) req := testServer.WaitRequest() c.Assert(req.Form["ResourceId.1"], DeepEquals, []string{"ami-1a2b3c4d"}) c.Assert(req.Form["ResourceId.2"], DeepEquals, []string{"i-7f4d3a2b"}) c.Assert(req.Form["Tag.1.Key"], DeepEquals, []string{"webserver"}) c.Assert(req.Form["Tag.1.Value"], DeepEquals, []string{""}) c.Assert(req.Form["Tag.2.Key"], DeepEquals, []string{"stack"}) c.Assert(req.Form["Tag.2.Value"], DeepEquals, []string{"Production"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") } func (s *S) TestStartInstances(c *C) { testServer.Response(200, nil, StartInstancesExample) resp, err := s.ec2.StartInstances("i-10a64379") req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"StartInstances"}) c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-10a64379"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") s0 := resp.StateChanges[0] c.Assert(s0.InstanceId, Equals, "i-10a64379") c.Assert(s0.CurrentState.Code, Equals, 0) c.Assert(s0.CurrentState.Name, Equals, "pending") c.Assert(s0.PreviousState.Code, Equals, 80) c.Assert(s0.PreviousState.Name, Equals, "stopped") } func (s *S) TestStopInstances(c *C) { testServer.Response(200, nil, StopInstancesExample) resp, err := s.ec2.StopInstances("i-10a64379") req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"StopInstances"}) c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-10a64379"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") s0 := resp.StateChanges[0] c.Assert(s0.InstanceId, Equals, "i-10a64379") c.Assert(s0.CurrentState.Code, Equals, 64) c.Assert(s0.CurrentState.Name, Equals, "stopping") c.Assert(s0.PreviousState.Code, Equals, 16) c.Assert(s0.PreviousState.Name, Equals, "running") } func (s *S) TestRebootInstances(c *C) { testServer.Response(200, nil, RebootInstancesExample) resp, err := s.ec2.RebootInstances("i-10a64379") req := testServer.WaitRequest() c.Assert(req.Form["Action"], DeepEquals, []string{"RebootInstances"}) c.Assert(req.Form["InstanceId.1"], DeepEquals, []string{"i-10a64379"}) c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE") } func (s *S) TestSignatureWithEndpointPath(c *C) { ec2.FakeTime(true) defer ec2.FakeTime(false) testServer.Response(200, nil, RebootInstancesExample) // https://bugs.launchpad.net/goamz/+bug/1022749 ec2 := ec2.New(s.ec2.Auth, aws.Region{EC2Endpoint: testServer.URL + "/services/Cloud"}) _, err := ec2.RebootInstances("i-10a64379") c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Form["Signature"], DeepEquals, []string{"gdG/vEm+c6ehhhfkrJy3+wuVzw/rzKR42TYelMwti7M="}) } ��������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/sign.go������������������������������������������������0000644�0000153�0000161�00000002326�12321735726�022045� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ec2 import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "launchpad.net/goamz/aws" "sort" "strings" ) // ---------------------------------------------------------------------------- // EC2 signing (http://goo.gl/fQmAN) var b64 = base64.StdEncoding func sign(auth aws.Auth, method, path string, params map[string]string, host string) { params["AWSAccessKeyId"] = auth.AccessKey params["SignatureVersion"] = "2" params["SignatureMethod"] = "HmacSHA256" // AWS specifies that the parameters in a signed request must // be provided in the natural order of the keys. This is distinct // from the natural order of the encoded value of key=value. // Percent and equals affect the sorting order. var keys, sarray []string for k, _ := range params { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(params[k])) } joined := strings.Join(sarray, "&") payload := method + "\n" + host + "\n" + path + "\n" + joined hash := hmac.New(sha256.New, []byte(auth.SecretKey)) hash.Write([]byte(payload)) signature := make([]byte, b64.EncodedLen(hash.Size())) b64.Encode(signature, hash.Sum(nil)) params["Signature"] = string(signature) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/ec2t_test.go�������������������������������������������0000644�0000153�0000161�00000042013�12321736016�022767� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ec2_test import ( "fmt" "net" "regexp" "sort" "launchpad.net/goamz/aws" "launchpad.net/goamz/ec2" "launchpad.net/goamz/ec2/ec2test" "launchpad.net/goamz/testutil" . "launchpad.net/gocheck" ) // LocalServer represents a local ec2test fake server. type LocalServer struct { auth aws.Auth region aws.Region srv *ec2test.Server } func (s *LocalServer) SetUp(c *C) { srv, err := ec2test.NewServer() c.Assert(err, IsNil) c.Assert(srv, NotNil) s.srv = srv s.region = aws.Region{EC2Endpoint: srv.URL()} } // LocalServerSuite defines tests that will run // against the local ec2test server. It includes // selected tests from ClientTests; // when the ec2test functionality is sufficient, it should // include all of them, and ClientTests can be simply embedded. type LocalServerSuite struct { srv LocalServer ServerTests clientTests ClientTests } var _ = Suite(&LocalServerSuite{}) func (s *LocalServerSuite) SetUpSuite(c *C) { s.srv.SetUp(c) s.ServerTests.ec2 = ec2.New(s.srv.auth, s.srv.region) s.clientTests.ec2 = ec2.New(s.srv.auth, s.srv.region) } func (s *LocalServerSuite) TestRunAndTerminate(c *C) { s.clientTests.TestRunAndTerminate(c) } func (s *LocalServerSuite) TestSecurityGroups(c *C) { s.clientTests.TestSecurityGroups(c) } // TestUserData is not defined on ServerTests because it // requires the ec2test server to function. func (s *LocalServerSuite) TestUserData(c *C) { data := make([]byte, 256) for i := range data { data[i] = byte(i) } inst, err := s.ec2.RunInstances(&ec2.RunInstances{ ImageId: imageId, InstanceType: "t1.micro", UserData: data, }) c.Assert(err, IsNil) c.Assert(inst, NotNil) id := inst.Instances[0].InstanceId defer s.ec2.TerminateInstances([]string{id}) tinst := s.srv.srv.Instance(id) c.Assert(tinst, NotNil) c.Assert(tinst.UserData, DeepEquals, data) } func (s *LocalServerSuite) TestInstanceInfo(c *C) { list, err := s.ec2.RunInstances(&ec2.RunInstances{ ImageId: imageId, InstanceType: "t1.micro", }) c.Assert(err, IsNil) inst := list.Instances[0] c.Assert(inst, NotNil) id := inst.InstanceId defer s.ec2.TerminateInstances([]string{id}) masked := func(addr string) string { return net.ParseIP(addr).Mask(net.CIDRMask(24, 32)).String() } c.Check(masked(inst.IPAddress), Equals, "8.0.0.0") c.Check(masked(inst.PrivateIPAddress), Equals, "127.0.0.0") c.Check(inst.DNSName, Equals, id+".testing.invalid") c.Check(inst.PrivateDNSName, Equals, id+".internal.invalid") } // AmazonServerSuite runs the ec2test server tests against a live EC2 server. // It will only be activated if the -all flag is specified. type AmazonServerSuite struct { srv AmazonServer ServerTests } var _ = Suite(&AmazonServerSuite{}) func (s *AmazonServerSuite) SetUpSuite(c *C) { if !testutil.Amazon { c.Skip("AmazonServerSuite tests not enabled") } s.srv.SetUp(c) s.ServerTests.ec2 = ec2.New(s.srv.auth, aws.USEast) } // ServerTests defines a set of tests designed to test // the ec2test local fake ec2 server. // It is not used as a test suite in itself, but embedded within // another type. type ServerTests struct { ec2 *ec2.EC2 } func terminateInstances(c *C, e *ec2.EC2, insts []*ec2.Instance) { var ids []string for _, inst := range insts { if inst != nil { ids = append(ids, inst.InstanceId) } } _, err := e.TerminateInstances(ids) c.Check(err, IsNil, Commentf("%d INSTANCES LEFT RUNNING!!!", len(ids))) } func (s *ServerTests) makeTestGroup(c *C, name, descr string) ec2.SecurityGroup { // Clean it up if a previous test left it around. _, err := s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) if err != nil && err.(*ec2.Error).Code != "InvalidGroup.NotFound" { c.Fatalf("delete security group: %v", err) } resp, err := s.ec2.CreateSecurityGroup(name, descr) c.Assert(err, IsNil) c.Assert(resp.Name, Equals, name) return resp.SecurityGroup } func (s *ServerTests) TestIPPerms(c *C) { g0 := s.makeTestGroup(c, "goamz-test0", "ec2test group 0") defer s.ec2.DeleteSecurityGroup(g0) g1 := s.makeTestGroup(c, "goamz-test1", "ec2test group 1") defer s.ec2.DeleteSecurityGroup(g1) resp, err := s.ec2.SecurityGroups([]ec2.SecurityGroup{g0, g1}, nil) c.Assert(err, IsNil) c.Assert(resp.Groups, HasLen, 2) c.Assert(resp.Groups[0].IPPerms, HasLen, 0) c.Assert(resp.Groups[1].IPPerms, HasLen, 0) ownerId := resp.Groups[0].OwnerId // test some invalid parameters // TODO more _, err = s.ec2.AuthorizeSecurityGroup(g0, []ec2.IPPerm{{ Protocol: "tcp", FromPort: 0, ToPort: 1024, SourceIPs: []string{"z127.0.0.1/24"}, }}) c.Assert(err, NotNil) c.Check(err.(*ec2.Error).Code, Equals, "InvalidPermission.Malformed") // Check that AuthorizeSecurityGroup adds the correct authorizations. _, err = s.ec2.AuthorizeSecurityGroup(g0, []ec2.IPPerm{{ Protocol: "tcp", FromPort: 2000, ToPort: 2001, SourceIPs: []string{"127.0.0.0/24"}, SourceGroups: []ec2.UserSecurityGroup{{ Name: g1.Name, }, { Id: g0.Id, }}, }, { Protocol: "tcp", FromPort: 2000, ToPort: 2001, SourceIPs: []string{"200.1.1.34/32"}, }}) c.Assert(err, IsNil) resp, err = s.ec2.SecurityGroups([]ec2.SecurityGroup{g0}, nil) c.Assert(err, IsNil) c.Assert(resp.Groups, HasLen, 1) c.Assert(resp.Groups[0].IPPerms, HasLen, 1) perm := resp.Groups[0].IPPerms[0] srcg := perm.SourceGroups c.Assert(srcg, HasLen, 2) // Normalize so we don't care about returned order. if srcg[0].Name == g1.Name { srcg[0], srcg[1] = srcg[1], srcg[0] } c.Check(srcg[0].Name, Equals, g0.Name) c.Check(srcg[0].Id, Equals, g0.Id) c.Check(srcg[0].OwnerId, Equals, ownerId) c.Check(srcg[1].Name, Equals, g1.Name) c.Check(srcg[1].Id, Equals, g1.Id) c.Check(srcg[1].OwnerId, Equals, ownerId) sort.Strings(perm.SourceIPs) c.Check(perm.SourceIPs, DeepEquals, []string{"127.0.0.0/24", "200.1.1.34/32"}) // Check that we can't delete g1 (because g0 is using it) _, err = s.ec2.DeleteSecurityGroup(g1) c.Assert(err, NotNil) c.Check(err.(*ec2.Error).Code, Equals, "InvalidGroup.InUse") _, err = s.ec2.RevokeSecurityGroup(g0, []ec2.IPPerm{{ Protocol: "tcp", FromPort: 2000, ToPort: 2001, SourceGroups: []ec2.UserSecurityGroup{{Id: g1.Id}}, }, { Protocol: "tcp", FromPort: 2000, ToPort: 2001, SourceIPs: []string{"200.1.1.34/32"}, }}) c.Assert(err, IsNil) resp, err = s.ec2.SecurityGroups([]ec2.SecurityGroup{g0}, nil) c.Assert(err, IsNil) c.Assert(resp.Groups, HasLen, 1) c.Assert(resp.Groups[0].IPPerms, HasLen, 1) perm = resp.Groups[0].IPPerms[0] srcg = perm.SourceGroups c.Assert(srcg, HasLen, 1) c.Check(srcg[0].Name, Equals, g0.Name) c.Check(srcg[0].Id, Equals, g0.Id) c.Check(srcg[0].OwnerId, Equals, ownerId) c.Check(perm.SourceIPs, DeepEquals, []string{"127.0.0.0/24"}) // We should be able to delete g1 now because we've removed its only use. _, err = s.ec2.DeleteSecurityGroup(g1) c.Assert(err, IsNil) _, err = s.ec2.DeleteSecurityGroup(g0) c.Assert(err, IsNil) f := ec2.NewFilter() f.Add("group-id", g0.Id, g1.Id) resp, err = s.ec2.SecurityGroups(nil, f) c.Assert(err, IsNil) c.Assert(resp.Groups, HasLen, 0) } func (s *ServerTests) TestDuplicateIPPerm(c *C) { name := "goamz-test" descr := "goamz security group for tests" // Clean it up, if a previous test left it around and avoid leaving it around. s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) defer s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) resp1, err := s.ec2.CreateSecurityGroup(name, descr) c.Assert(err, IsNil) c.Assert(resp1.Name, Equals, name) perms := []ec2.IPPerm{{ Protocol: "tcp", FromPort: 200, ToPort: 1024, SourceIPs: []string{"127.0.0.1/24"}, }, { Protocol: "tcp", FromPort: 0, ToPort: 100, SourceIPs: []string{"127.0.0.1/24"}, }} _, err = s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms[0:1]) c.Assert(err, IsNil) _, err = s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms[0:2]) c.Assert(err, ErrorMatches, `.*\(InvalidPermission.Duplicate\)`) } type filterSpec struct { name string values []string } func (s *ServerTests) TestInstanceFiltering(c *C) { groupResp, err := s.ec2.CreateSecurityGroup(sessionName("testgroup1"), "testgroup one description") c.Assert(err, IsNil) group1 := groupResp.SecurityGroup defer s.ec2.DeleteSecurityGroup(group1) groupResp, err = s.ec2.CreateSecurityGroup(sessionName("testgroup2"), "testgroup two description") c.Assert(err, IsNil) group2 := groupResp.SecurityGroup defer s.ec2.DeleteSecurityGroup(group2) insts := make([]*ec2.Instance, 3) inst, err := s.ec2.RunInstances(&ec2.RunInstances{ MinCount: 2, ImageId: imageId, InstanceType: "t1.micro", SecurityGroups: []ec2.SecurityGroup{group1}, }) c.Assert(err, IsNil) insts[0] = &inst.Instances[0] insts[1] = &inst.Instances[1] defer terminateInstances(c, s.ec2, insts) imageId2 := "ami-e358958a" // Natty server, i386, EBS store inst, err = s.ec2.RunInstances(&ec2.RunInstances{ ImageId: imageId2, InstanceType: "t1.micro", SecurityGroups: []ec2.SecurityGroup{group2}, }) c.Assert(err, IsNil) insts[2] = &inst.Instances[0] ids := func(indices ...int) (instIds []string) { for _, index := range indices { instIds = append(instIds, insts[index].InstanceId) } return } tests := []struct { about string instanceIds []string // instanceIds argument to Instances method. filters []filterSpec // filters argument to Instances method. resultIds []string // set of instance ids of expected results. allowExtra bool // resultIds may be incomplete. err string // expected error. }{ { about: "check that Instances returns all instances", resultIds: ids(0, 1, 2), allowExtra: true, }, { about: "check that specifying two instance ids returns them", instanceIds: ids(0, 2), resultIds: ids(0, 2), }, { about: "check that specifying a non-existent instance id gives an error", instanceIds: append(ids(0), "i-deadbeef"), err: `.*\(InvalidInstanceID\.NotFound\)`, }, { about: "check that a filter allowed both instances returns both of them", filters: []filterSpec{ {"instance-id", ids(0, 2)}, }, resultIds: ids(0, 2), }, { about: "check that a filter allowing only one instance returns it", filters: []filterSpec{ {"instance-id", ids(1)}, }, resultIds: ids(1), }, { about: "check that a filter allowing no instances returns none", filters: []filterSpec{ {"instance-id", []string{"i-deadbeef12345"}}, }, }, { about: "check that filtering on group id works", filters: []filterSpec{ {"group-id", []string{group1.Id}}, }, resultIds: ids(0, 1), }, { about: "check that filtering on group id with instance prefix works", filters: []filterSpec{ {"instance.group-id", []string{group1.Id}}, }, resultIds: ids(0, 1), }, { about: "check that filtering on group name works", filters: []filterSpec{ {"group-name", []string{group1.Name}}, }, resultIds: ids(0, 1), }, { about: "check that filtering on group name with instance prefix works", filters: []filterSpec{ {"instance.group-name", []string{group1.Name}}, }, resultIds: ids(0, 1), }, { about: "check that filtering on image id works", filters: []filterSpec{ {"image-id", []string{imageId}}, }, resultIds: ids(0, 1), allowExtra: true, }, { about: "combination filters 1", filters: []filterSpec{ {"image-id", []string{imageId, imageId2}}, {"group-name", []string{group1.Name}}, }, resultIds: ids(0, 1), }, { about: "combination filters 2", filters: []filterSpec{ {"image-id", []string{imageId2}}, {"group-name", []string{group1.Name}}, }, }, } for i, t := range tests { c.Logf("%d. %s", i, t.about) var f *ec2.Filter if t.filters != nil { f = ec2.NewFilter() for _, spec := range t.filters { f.Add(spec.name, spec.values...) } } resp, err := s.ec2.Instances(t.instanceIds, f) if t.err != "" { c.Check(err, ErrorMatches, t.err) continue } c.Assert(err, IsNil) insts := make(map[string]*ec2.Instance) for _, r := range resp.Reservations { for j := range r.Instances { inst := &r.Instances[j] c.Check(insts[inst.InstanceId], IsNil, Commentf("duplicate instance id: %q", inst.InstanceId)) insts[inst.InstanceId] = inst } } if !t.allowExtra { c.Check(insts, HasLen, len(t.resultIds), Commentf("expected %d instances got %#v", len(t.resultIds), insts)) } for j, id := range t.resultIds { c.Check(insts[id], NotNil, Commentf("instance id %d (%q) not found; got %#v", j, id, insts)) } } } func idsOnly(gs []ec2.SecurityGroup) []ec2.SecurityGroup { for i := range gs { gs[i].Name = "" } return gs } func namesOnly(gs []ec2.SecurityGroup) []ec2.SecurityGroup { for i := range gs { gs[i].Id = "" } return gs } func (s *ServerTests) TestGroupFiltering(c *C) { g := make([]ec2.SecurityGroup, 4) for i := range g { resp, err := s.ec2.CreateSecurityGroup(sessionName(fmt.Sprintf("testgroup%d", i)), fmt.Sprintf("testdescription%d", i)) c.Assert(err, IsNil) g[i] = resp.SecurityGroup c.Logf("group %d: %v", i, g[i]) defer s.ec2.DeleteSecurityGroup(g[i]) } perms := [][]ec2.IPPerm{ {{ Protocol: "tcp", FromPort: 100, ToPort: 200, SourceIPs: []string{"1.2.3.4/32"}, }}, {{ Protocol: "tcp", FromPort: 200, ToPort: 300, SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}}, }}, {{ Protocol: "udp", FromPort: 200, ToPort: 400, SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}}, }}, } for i, ps := range perms { _, err := s.ec2.AuthorizeSecurityGroup(g[i], ps) c.Assert(err, IsNil) } groups := func(indices ...int) (gs []ec2.SecurityGroup) { for _, index := range indices { gs = append(gs, g[index]) } return } type groupTest struct { about string groups []ec2.SecurityGroup // groupIds argument to SecurityGroups method. filters []filterSpec // filters argument to SecurityGroups method. results []ec2.SecurityGroup // set of expected result groups. allowExtra bool // specified results may be incomplete. err string // expected error. } filterCheck := func(name, val string, gs []ec2.SecurityGroup) groupTest { return groupTest{ about: "filter check " + name, filters: []filterSpec{{name, []string{val}}}, results: gs, allowExtra: true, } } tests := []groupTest{ { about: "check that SecurityGroups returns all groups", results: groups(0, 1, 2, 3), allowExtra: true, }, { about: "check that specifying two group ids returns them", groups: idsOnly(groups(0, 2)), results: groups(0, 2), }, { about: "check that specifying names only works", groups: namesOnly(groups(0, 2)), results: groups(0, 2), }, { about: "check that specifying a non-existent group id gives an error", groups: append(groups(0), ec2.SecurityGroup{Id: "sg-eeeeeeeee"}), err: `.*\(InvalidGroup\.NotFound\)`, }, { about: "check that a filter allowed two groups returns both of them", filters: []filterSpec{ {"group-id", []string{g[0].Id, g[2].Id}}, }, results: groups(0, 2), }, { about: "check that the previous filter works when specifying a list of ids", groups: groups(1, 2), filters: []filterSpec{ {"group-id", []string{g[0].Id, g[2].Id}}, }, results: groups(2), }, { about: "check that a filter allowing no groups returns none", filters: []filterSpec{ {"group-id", []string{"sg-eeeeeeeee"}}, }, }, filterCheck("description", "testdescription1", groups(1)), filterCheck("group-name", g[2].Name, groups(2)), filterCheck("ip-permission.cidr", "1.2.3.4/32", groups(0)), filterCheck("ip-permission.group-name", g[1].Name, groups(1, 2)), filterCheck("ip-permission.protocol", "udp", groups(2)), filterCheck("ip-permission.from-port", "200", groups(1, 2)), filterCheck("ip-permission.to-port", "200", groups(0)), // TODO owner-id } for i, t := range tests { c.Logf("%d. %s", i, t.about) var f *ec2.Filter if t.filters != nil { f = ec2.NewFilter() for _, spec := range t.filters { f.Add(spec.name, spec.values...) } } resp, err := s.ec2.SecurityGroups(t.groups, f) if t.err != "" { c.Check(err, ErrorMatches, t.err) continue } c.Assert(err, IsNil) groups := make(map[string]*ec2.SecurityGroup) for j := range resp.Groups { group := &resp.Groups[j].SecurityGroup c.Check(groups[group.Id], IsNil, Commentf("duplicate group id: %q", group.Id)) groups[group.Id] = group } // If extra groups may be returned, eliminate all groups that // we did not create in this session apart from the default group. if t.allowExtra { namePat := regexp.MustCompile(sessionName("testgroup[0-9]")) for id, g := range groups { if !namePat.MatchString(g.Name) { delete(groups, id) } } } c.Check(groups, HasLen, len(t.results)) for j, g := range t.results { rg := groups[g.Id] c.Assert(rg, NotNil, Commentf("group %d (%v) not found; got %#v", j, g, groups)) c.Check(rg.Name, Equals, g.Name, Commentf("group %d (%v)", j, g)) } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/ec2i_test.go�������������������������������������������0000644�0000153�0000161�00000013026�12321735726�022765� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ec2_test import ( "crypto/rand" "fmt" "launchpad.net/goamz/aws" "launchpad.net/goamz/ec2" "launchpad.net/goamz/testutil" . "launchpad.net/gocheck" ) // AmazonServer represents an Amazon EC2 server. type AmazonServer struct { auth aws.Auth } func (s *AmazonServer) SetUp(c *C) { auth, err := aws.EnvAuth() if err != nil { c.Fatal(err.Error()) } s.auth = auth } // Suite cost per run: 0.02 USD var _ = Suite(&AmazonClientSuite{}) // AmazonClientSuite tests the client against a live EC2 server. type AmazonClientSuite struct { srv AmazonServer ClientTests } func (s *AmazonClientSuite) SetUpSuite(c *C) { if !testutil.Amazon { c.Skip("AmazonClientSuite tests not enabled") } s.srv.SetUp(c) s.ec2 = ec2.New(s.srv.auth, aws.USEast) } // ClientTests defines integration tests designed to test the client. // It is not used as a test suite in itself, but embedded within // another type. type ClientTests struct { ec2 *ec2.EC2 } var imageId = "ami-ccf405a5" // Ubuntu Maverick, i386, EBS store // Cost: 0.00 USD func (s *ClientTests) TestRunInstancesError(c *C) { options := ec2.RunInstances{ ImageId: "ami-a6f504cf", // Ubuntu Maverick, i386, instance store InstanceType: "t1.micro", // Doesn't work with micro, results in 400. } resp, err := s.ec2.RunInstances(&options) c.Assert(resp, IsNil) c.Assert(err, ErrorMatches, "AMI.*root device.*not supported.*") ec2err, ok := err.(*ec2.Error) c.Assert(ok, Equals, true) c.Assert(ec2err.StatusCode, Equals, 400) c.Assert(ec2err.Code, Equals, "UnsupportedOperation") c.Assert(ec2err.Message, Matches, "AMI.*root device.*not supported.*") c.Assert(ec2err.RequestId, Matches, ".+") } // Cost: 0.02 USD func (s *ClientTests) TestRunAndTerminate(c *C) { options := ec2.RunInstances{ ImageId: imageId, InstanceType: "t1.micro", } resp1, err := s.ec2.RunInstances(&options) c.Assert(err, IsNil) c.Check(resp1.ReservationId, Matches, "r-[0-9a-f]*") c.Check(resp1.OwnerId, Matches, "[0-9]+") c.Check(resp1.Instances, HasLen, 1) c.Check(resp1.Instances[0].InstanceType, Equals, "t1.micro") instId := resp1.Instances[0].InstanceId resp2, err := s.ec2.Instances([]string{instId}, nil) c.Assert(err, IsNil) if c.Check(resp2.Reservations, HasLen, 1) && c.Check(len(resp2.Reservations[0].Instances), Equals, 1) { inst := resp2.Reservations[0].Instances[0] c.Check(inst.InstanceId, Equals, instId) } resp3, err := s.ec2.TerminateInstances([]string{instId}) c.Assert(err, IsNil) c.Check(resp3.StateChanges, HasLen, 1) c.Check(resp3.StateChanges[0].InstanceId, Equals, instId) c.Check(resp3.StateChanges[0].CurrentState.Name, Equals, "shutting-down") c.Check(resp3.StateChanges[0].CurrentState.Code, Equals, 32) } // Cost: 0.00 USD func (s *ClientTests) TestSecurityGroups(c *C) { name := "goamz-test" descr := "goamz security group for tests" // Clean it up, if a previous test left it around and avoid leaving it around. s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) defer s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) resp1, err := s.ec2.CreateSecurityGroup(name, descr) c.Assert(err, IsNil) c.Assert(resp1.RequestId, Matches, ".+") c.Assert(resp1.Name, Equals, name) c.Assert(resp1.Id, Matches, ".+") resp1, err = s.ec2.CreateSecurityGroup(name, descr) ec2err, _ := err.(*ec2.Error) c.Assert(resp1, IsNil) c.Assert(ec2err, NotNil) c.Assert(ec2err.Code, Equals, "InvalidGroup.Duplicate") perms := []ec2.IPPerm{{ Protocol: "tcp", FromPort: 0, ToPort: 1024, SourceIPs: []string{"127.0.0.1/24"}, }} resp2, err := s.ec2.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms) c.Assert(err, IsNil) c.Assert(resp2.RequestId, Matches, ".+") resp3, err := s.ec2.SecurityGroups(ec2.SecurityGroupNames(name), nil) c.Assert(err, IsNil) c.Assert(resp3.RequestId, Matches, ".+") c.Assert(resp3.Groups, HasLen, 1) g0 := resp3.Groups[0] c.Assert(g0.Name, Equals, name) c.Assert(g0.Description, Equals, descr) c.Assert(g0.IPPerms, HasLen, 1) c.Assert(g0.IPPerms[0].Protocol, Equals, "tcp") c.Assert(g0.IPPerms[0].FromPort, Equals, 0) c.Assert(g0.IPPerms[0].ToPort, Equals, 1024) c.Assert(g0.IPPerms[0].SourceIPs, DeepEquals, []string{"127.0.0.1/24"}) resp2, err = s.ec2.DeleteSecurityGroup(ec2.SecurityGroup{Name: name}) c.Assert(err, IsNil) c.Assert(resp2.RequestId, Matches, ".+") } var sessionId = func() string { buf := make([]byte, 8) // if we have no randomness, we'll just make do, so ignore the error. rand.Read(buf) return fmt.Sprintf("%x", buf) }() // sessionName reutrns a name that is probably // unique to this test session. func sessionName(prefix string) string { return prefix + "-" + sessionId } var allRegions = []aws.Region{ aws.USEast, aws.USWest, aws.EUWest, aws.APSoutheast, aws.APNortheast, } // Communicate with all EC2 endpoints to see if they are alive. func (s *ClientTests) TestRegions(c *C) { name := sessionName("goamz-region-test") perms := []ec2.IPPerm{{ Protocol: "tcp", FromPort: 80, ToPort: 80, SourceIPs: []string{"127.0.0.1/32"}, }} errs := make(chan error, len(allRegions)) for _, region := range allRegions { go func(r aws.Region) { e := ec2.New(s.ec2.Auth, r) _, err := e.AuthorizeSecurityGroup(ec2.SecurityGroup{Name: name}, perms) errs <- err }(region) } for _ = range allRegions { err := <-errs if err != nil { ec2_err, ok := err.(*ec2.Error) if ok { c.Check(ec2_err.Code, Matches, "InvalidGroup.NotFound") } else { c.Errorf("Non-EC2 error: %s", err) } } else { c.Errorf("Test should have errored but it seems to have succeeded") } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/ec2test/�����������������������������������������������0000755�0000153�0000161�00000000000�12321736016�022115� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/ec2test/server.go��������������������������������������0000644�0000153�0000161�00000066756�12321736016�023776� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// The ec2test package implements a fake EC2 provider with // the capability of inducing errors on any given operation, // and retrospectively determining what operations have been // carried out. package ec2test import ( "encoding/base64" "encoding/xml" "fmt" "io" "launchpad.net/goamz/ec2" "net" "net/http" "net/url" "regexp" "strconv" "strings" "sync" ) var b64 = base64.StdEncoding // Action represents a request that changes the ec2 state. type Action struct { RequestId string // Request holds the requested action as a url.Values instance Request url.Values // If the action succeeded, Response holds the value that // was marshalled to build the XML response for the request. Response interface{} // If the action failed, Err holds an error giving details of the failure. Err *ec2.Error } // TODO possible other things: // - some virtual time stamp interface, so a client // can ask for all actions after a certain virtual time. // Server implements an EC2 simulator for use in testing. type Server struct { url string listener net.Listener mu sync.Mutex reqs []*Action instances map[string]*Instance // id -> instance reservations map[string]*reservation // id -> reservation groups map[string]*securityGroup // id -> group maxId counter reqId counter reservationId counter groupId counter initialInstanceState ec2.InstanceState } // reservation holds a simulated ec2 reservation. type reservation struct { id string instances map[string]*Instance groups []*securityGroup } // instance holds a simulated ec2 instance type Instance struct { seq int dnsNameSet bool // UserData holds the data that was passed to the RunInstances request // when the instance was started. UserData []byte imageId string reservation *reservation instType string state ec2.InstanceState } // permKey represents permission for a given security // group or IP address (but not both) to access a given range of // ports. Equality of permKeys is used in the implementation of // permission sets, relying on the uniqueness of securityGroup // instances. type permKey struct { protocol string fromPort int toPort int group *securityGroup ipAddr string } // securityGroup holds a simulated ec2 security group. // Instances of securityGroup should only be created through // Server.createSecurityGroup to ensure that groups can be // compared by pointer value. type securityGroup struct { id string name string description string perms map[permKey]bool } func (g *securityGroup) ec2SecurityGroup() ec2.SecurityGroup { return ec2.SecurityGroup{ Name: g.name, Id: g.id, } } func (g *securityGroup) matchAttr(attr, value string) (ok bool, err error) { switch attr { case "description": return g.description == value, nil case "group-id": return g.id == value, nil case "group-name": return g.name == value, nil case "ip-permission.cidr": return g.hasPerm(func(k permKey) bool { return k.ipAddr == value }), nil case "ip-permission.group-name": return g.hasPerm(func(k permKey) bool { return k.group != nil && k.group.name == value }), nil case "ip-permission.from-port": port, err := strconv.Atoi(value) if err != nil { return false, err } return g.hasPerm(func(k permKey) bool { return k.fromPort == port }), nil case "ip-permission.to-port": port, err := strconv.Atoi(value) if err != nil { return false, err } return g.hasPerm(func(k permKey) bool { return k.toPort == port }), nil case "ip-permission.protocol": return g.hasPerm(func(k permKey) bool { return k.protocol == value }), nil case "owner-id": return value == ownerId, nil } return false, fmt.Errorf("unknown attribute %q", attr) } func (g *securityGroup) hasPerm(test func(k permKey) bool) bool { for k := range g.perms { if test(k) { return true } } return false } // ec2Perms returns the list of EC2 permissions granted // to g. It groups permissions by port range and protocol. func (g *securityGroup) ec2Perms() (perms []ec2.IPPerm) { // The grouping is held in result. We use permKey for convenience, // (ensuring that the group and ipAddr of each key is zero). For // each protocol/port range combination, we build up the permission // set in the associated value. result := make(map[permKey]*ec2.IPPerm) for k := range g.perms { groupKey := k groupKey.group = nil groupKey.ipAddr = "" ec2p := result[groupKey] if ec2p == nil { ec2p = &ec2.IPPerm{ Protocol: k.protocol, FromPort: k.fromPort, ToPort: k.toPort, } result[groupKey] = ec2p } if k.group != nil { ec2p.SourceGroups = append(ec2p.SourceGroups, ec2.UserSecurityGroup{ Id: k.group.id, Name: k.group.name, OwnerId: ownerId, }) } else { ec2p.SourceIPs = append(ec2p.SourceIPs, k.ipAddr) } } for _, ec2p := range result { perms = append(perms, *ec2p) } return } var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) interface{}{ "RunInstances": (*Server).runInstances, "TerminateInstances": (*Server).terminateInstances, "DescribeInstances": (*Server).describeInstances, "CreateSecurityGroup": (*Server).createSecurityGroup, "DescribeSecurityGroups": (*Server).describeSecurityGroups, "DeleteSecurityGroup": (*Server).deleteSecurityGroup, "AuthorizeSecurityGroupIngress": (*Server).authorizeSecurityGroupIngress, "RevokeSecurityGroupIngress": (*Server).revokeSecurityGroupIngress, } const ownerId = "9876" // newAction allocates a new action and adds it to the // recorded list of server actions. func (srv *Server) newAction() *Action { srv.mu.Lock() defer srv.mu.Unlock() a := new(Action) srv.reqs = append(srv.reqs, a) return a } // NewServer returns a new server. func NewServer() (*Server, error) { srv := &Server{ instances: make(map[string]*Instance), groups: make(map[string]*securityGroup), reservations: make(map[string]*reservation), initialInstanceState: Pending, } // Add default security group. g := &securityGroup{ name: "default", description: "default group", id: fmt.Sprintf("sg-%d", srv.groupId.next()), } g.perms = map[permKey]bool{ permKey{ protocol: "icmp", fromPort: -1, toPort: -1, group: g, }: true, permKey{ protocol: "tcp", fromPort: 0, toPort: 65535, group: g, }: true, permKey{ protocol: "udp", fromPort: 0, toPort: 65535, group: g, }: true, } srv.groups[g.id] = g l, err := net.Listen("tcp", "localhost:0") if err != nil { return nil, fmt.Errorf("cannot listen on localhost: %v", err) } srv.listener = l srv.url = "http://" + l.Addr().String() // we use HandlerFunc rather than *Server directly so that we // can avoid exporting HandlerFunc from *Server. go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { srv.serveHTTP(w, req) })) return srv, nil } // Quit closes down the server. func (srv *Server) Quit() { srv.listener.Close() } // SetInitialInstanceState sets the state that any new instances will be started in. func (srv *Server) SetInitialInstanceState(state ec2.InstanceState) { srv.mu.Lock() srv.initialInstanceState = state srv.mu.Unlock() } // URL returns the URL of the server. func (srv *Server) URL() string { return srv.url } // serveHTTP serves the EC2 protocol. func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) { req.ParseForm() a := srv.newAction() a.RequestId = fmt.Sprintf("req%d", srv.reqId.next()) a.Request = req.Form // Methods on Server that deal with parsing user data // may fail. To save on error handling code, we allow these // methods to call fatalf, which will panic with an *ec2.Error // which will be caught here and returned // to the client as a properly formed EC2 error. defer func() { switch err := recover().(type) { case *ec2.Error: a.Err = err err.RequestId = a.RequestId writeError(w, err) case nil: default: panic(err) } }() f := actions[req.Form.Get("Action")] if f == nil { fatalf(400, "InvalidParameterValue", "Unrecognized Action") } response := f(srv, w, req, a.RequestId) a.Response = response w.Header().Set("Content-Type", `xml version="1.0" encoding="UTF-8"`) xmlMarshal(w, response) } // Instance returns the instance for the given instance id. // It returns nil if there is no such instance. func (srv *Server) Instance(id string) *Instance { srv.mu.Lock() defer srv.mu.Unlock() return srv.instances[id] } // writeError writes an appropriate error response. // TODO how should we deal with errors when the // error itself is potentially generated by backend-agnostic // code? func writeError(w http.ResponseWriter, err *ec2.Error) { // Error encapsulates an error returned by EC2. // TODO merge with ec2.Error when xml supports ignoring a field. type ec2error struct { Code string // EC2 error code ("UnsupportedOperation", ...) Message string // The human-oriented error message RequestId string } type Response struct { RequestId string Errors []ec2error `xml:"Errors>Error"` } w.Header().Set("Content-Type", `xml version="1.0" encoding="UTF-8"`) w.WriteHeader(err.StatusCode) xmlMarshal(w, Response{ RequestId: err.RequestId, Errors: []ec2error{{ Code: err.Code, Message: err.Message, }}, }) } // xmlMarshal is the same as xml.Marshal except that // it panics on error. The marshalling should not fail, // but we want to know if it does. func xmlMarshal(w io.Writer, x interface{}) { if err := xml.NewEncoder(w).Encode(x); err != nil { panic(fmt.Errorf("error marshalling %#v: %v", x, err)) } } // formToGroups parses a set of SecurityGroup form values // as found in a RunInstances request, and returns the resulting // slice of security groups. // It calls fatalf if a group is not found. func (srv *Server) formToGroups(form url.Values) []*securityGroup { var groups []*securityGroup for name, values := range form { switch { case strings.HasPrefix(name, "SecurityGroupId."): if g := srv.groups[values[0]]; g != nil { groups = append(groups, g) } else { fatalf(400, "InvalidGroup.NotFound", "unknown group id %q", values[0]) } case strings.HasPrefix(name, "SecurityGroup."): var found *securityGroup for _, g := range srv.groups { if g.name == values[0] { found = g } } if found == nil { fatalf(400, "InvalidGroup.NotFound", "unknown group name %q", values[0]) } groups = append(groups, found) } } return groups } // runInstances implements the EC2 RunInstances entry point. func (srv *Server) runInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} { min := atoi(req.Form.Get("MinCount")) max := atoi(req.Form.Get("MaxCount")) if min < 0 || max < 1 { fatalf(400, "InvalidParameterValue", "bad values for MinCount or MaxCount") } if min > max { fatalf(400, "InvalidParameterCombination", "MinCount is greater than MaxCount") } var userData []byte if data := req.Form.Get("UserData"); data != "" { var err error userData, err = b64.DecodeString(data) if err != nil { fatalf(400, "InvalidParameterValue", "bad UserData value: %v", err) } } // TODO attributes still to consider: // ImageId: accept anything, we can verify later // KeyName ? // InstanceType ? // KernelId ? // RamdiskId ? // AvailZone ? // GroupName tag // Monitoring ignore? // SubnetId ? // DisableAPITermination bool // ShutdownBehavior string // PrivateIPAddress string srv.mu.Lock() defer srv.mu.Unlock() // make sure that form fields are correct before creating the reservation. instType := req.Form.Get("InstanceType") imageId := req.Form.Get("ImageId") r := srv.newReservation(srv.formToGroups(req.Form)) var resp ec2.RunInstancesResp resp.RequestId = reqId resp.ReservationId = r.id resp.OwnerId = ownerId for i := 0; i < max; i++ { inst := srv.newInstance(r, instType, imageId, srv.initialInstanceState) inst.UserData = userData resp.Instances = append(resp.Instances, inst.ec2instance()) } return &resp } func (srv *Server) group(group ec2.SecurityGroup) *securityGroup { if group.Id != "" { return srv.groups[group.Id] } for _, g := range srv.groups { if g.name == group.Name { return g } } return nil } // NewInstances creates n new instances in srv with the given instance type, // image ID, initial state and security groups. If any group does not already // exist, it will be created. NewInstances returns the ids of the new instances. func (srv *Server) NewInstances(n int, instType string, imageId string, state ec2.InstanceState, groups []ec2.SecurityGroup) []string { srv.mu.Lock() defer srv.mu.Unlock() rgroups := make([]*securityGroup, len(groups)) for i, group := range groups { g := srv.group(group) if g == nil { fatalf(400, "InvalidGroup.NotFound", "no such group %v", g) } rgroups[i] = g } r := srv.newReservation(rgroups) ids := make([]string, n) for i := 0; i < n; i++ { inst := srv.newInstance(r, instType, imageId, state) ids[i] = inst.id() } return ids } func (srv *Server) newInstance(r *reservation, instType string, imageId string, state ec2.InstanceState) *Instance { inst := &Instance{ seq: srv.maxId.next(), instType: instType, imageId: imageId, state: state, reservation: r, } id := inst.id() srv.instances[id] = inst r.instances[id] = inst return inst } func (srv *Server) newReservation(groups []*securityGroup) *reservation { r := &reservation{ id: fmt.Sprintf("r-%d", srv.reservationId.next()), instances: make(map[string]*Instance), groups: groups, } srv.reservations[r.id] = r return r } func (srv *Server) terminateInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} { srv.mu.Lock() defer srv.mu.Unlock() var resp ec2.TerminateInstancesResp resp.RequestId = reqId var insts []*Instance for attr, vals := range req.Form { if strings.HasPrefix(attr, "InstanceId.") { id := vals[0] inst := srv.instances[id] if inst == nil { fatalf(400, "InvalidInstanceID.NotFound", "no such instance id %q", id) } insts = append(insts, inst) } } for _, inst := range insts { resp.StateChanges = append(resp.StateChanges, inst.terminate()) } return &resp } func (inst *Instance) id() string { return fmt.Sprintf("i-%d", inst.seq) } func (inst *Instance) terminate() (d ec2.InstanceStateChange) { d.PreviousState = inst.state inst.state = ShuttingDown d.CurrentState = inst.state d.InstanceId = inst.id() return d } func (inst *Instance) ec2instance() ec2.Instance { id := inst.id() // The first time the instance is returned, its DNSName // will be empty. The client should then refresh the instance. var dnsName string if inst.dnsNameSet { dnsName = fmt.Sprintf("%s.testing.invalid", id) } else { inst.dnsNameSet = true } return ec2.Instance{ InstanceId: id, InstanceType: inst.instType, ImageId: inst.imageId, DNSName: dnsName, PrivateDNSName: fmt.Sprintf("%s.internal.invalid", id), IPAddress: fmt.Sprintf("8.0.0.%d", inst.seq%256), PrivateIPAddress: fmt.Sprintf("127.0.0.%d", inst.seq%256), State: inst.state, // TODO the rest } } func (inst *Instance) matchAttr(attr, value string) (ok bool, err error) { switch attr { case "architecture": return value == "i386", nil case "instance-id": return inst.id() == value, nil case "instance.group-id", "group-id": for _, g := range inst.reservation.groups { if g.id == value { return true, nil } } return false, nil case "instance.group-name", "group-name": for _, g := range inst.reservation.groups { if g.name == value { return true, nil } } return false, nil case "image-id": return value == inst.imageId, nil case "instance-state-code": code, err := strconv.Atoi(value) if err != nil { return false, err } return code&0xff == inst.state.Code, nil case "instance-state-name": return value == inst.state.Name, nil } return false, fmt.Errorf("unknown attribute %q", attr) } var ( Pending = ec2.InstanceState{0, "pending"} Running = ec2.InstanceState{16, "running"} ShuttingDown = ec2.InstanceState{32, "shutting-down"} Terminated = ec2.InstanceState{16, "terminated"} Stopped = ec2.InstanceState{16, "stopped"} ) func (srv *Server) createSecurityGroup(w http.ResponseWriter, req *http.Request, reqId string) interface{} { name := req.Form.Get("GroupName") if name == "" { fatalf(400, "InvalidParameterValue", "empty security group name") } srv.mu.Lock() defer srv.mu.Unlock() if srv.group(ec2.SecurityGroup{Name: name}) != nil { fatalf(400, "InvalidGroup.Duplicate", "group %q already exists", name) } g := &securityGroup{ name: name, description: req.Form.Get("GroupDescription"), id: fmt.Sprintf("sg-%d", srv.groupId.next()), perms: make(map[permKey]bool), } srv.groups[g.id] = g // we define a local type for this because ec2.CreateSecurityGroupResp // contains SecurityGroup, but the response to this request // should not contain the security group name. type CreateSecurityGroupResponse struct { RequestId string `xml:"requestId"` Return bool `xml:"return"` GroupId string `xml:"groupId"` } r := &CreateSecurityGroupResponse{ RequestId: reqId, Return: true, GroupId: g.id, } return r } func (srv *Server) notImplemented(w http.ResponseWriter, req *http.Request, reqId string) interface{} { fatalf(500, "InternalError", "not implemented") panic("not reached") } func (srv *Server) describeInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} { srv.mu.Lock() defer srv.mu.Unlock() insts := make(map[*Instance]bool) for name, vals := range req.Form { if !strings.HasPrefix(name, "InstanceId.") { continue } inst := srv.instances[vals[0]] if inst == nil { fatalf(400, "InvalidInstanceID.NotFound", "instance %q not found", vals[0]) } insts[inst] = true } f := newFilter(req.Form) var resp ec2.InstancesResp resp.RequestId = reqId for _, r := range srv.reservations { var instances []ec2.Instance var groups []ec2.SecurityGroup for _, g := range r.groups { groups = append(groups, g.ec2SecurityGroup()) } for _, inst := range r.instances { if len(insts) > 0 && !insts[inst] { continue } ok, err := f.ok(inst) if ok { instance := inst.ec2instance() instance.SecurityGroups = groups instances = append(instances, instance) } else if err != nil { fatalf(400, "InvalidParameterValue", "describe instances: %v", err) } } if len(instances) > 0 { resp.Reservations = append(resp.Reservations, ec2.Reservation{ ReservationId: r.id, OwnerId: ownerId, Instances: instances, SecurityGroups: groups, }) } } return &resp } func (srv *Server) describeSecurityGroups(w http.ResponseWriter, req *http.Request, reqId string) interface{} { // BUG similar bug to describeInstances, but for GroupName and GroupId srv.mu.Lock() defer srv.mu.Unlock() var groups []*securityGroup for name, vals := range req.Form { var g ec2.SecurityGroup switch { case strings.HasPrefix(name, "GroupName."): g.Name = vals[0] case strings.HasPrefix(name, "GroupId."): g.Id = vals[0] default: continue } sg := srv.group(g) if sg == nil { fatalf(400, "InvalidGroup.NotFound", "no such group %v", g) } groups = append(groups, sg) } if len(groups) == 0 { for _, g := range srv.groups { groups = append(groups, g) } } f := newFilter(req.Form) var resp ec2.SecurityGroupsResp resp.RequestId = reqId for _, group := range groups { ok, err := f.ok(group) if ok { resp.Groups = append(resp.Groups, ec2.SecurityGroupInfo{ OwnerId: ownerId, SecurityGroup: group.ec2SecurityGroup(), Description: group.description, IPPerms: group.ec2Perms(), }) } else if err != nil { fatalf(400, "InvalidParameterValue", "describe security groups: %v", err) } } return &resp } func (srv *Server) authorizeSecurityGroupIngress(w http.ResponseWriter, req *http.Request, reqId string) interface{} { srv.mu.Lock() defer srv.mu.Unlock() g := srv.group(ec2.SecurityGroup{ Name: req.Form.Get("GroupName"), Id: req.Form.Get("GroupId"), }) if g == nil { fatalf(400, "InvalidGroup.NotFound", "group not found") } perms := srv.parsePerms(req) for _, p := range perms { if g.perms[p] { fatalf(400, "InvalidPermission.Duplicate", "Permission has already been authorized on the specified group") } } for _, p := range perms { g.perms[p] = true } return &ec2.SimpleResp{ XMLName: xml.Name{"", "AuthorizeSecurityGroupIngressResponse"}, RequestId: reqId, } } func (srv *Server) revokeSecurityGroupIngress(w http.ResponseWriter, req *http.Request, reqId string) interface{} { srv.mu.Lock() defer srv.mu.Unlock() g := srv.group(ec2.SecurityGroup{ Name: req.Form.Get("GroupName"), Id: req.Form.Get("GroupId"), }) if g == nil { fatalf(400, "InvalidGroup.NotFound", "group not found") } perms := srv.parsePerms(req) // Note EC2 does not give an error if asked to revoke an authorization // that does not exist. for _, p := range perms { delete(g.perms, p) } return &ec2.SimpleResp{ XMLName: xml.Name{"", "RevokeSecurityGroupIngressResponse"}, RequestId: reqId, } } var secGroupPat = regexp.MustCompile(`^sg-[a-z0-9]+$`) var ipPat = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$`) var ownerIdPat = regexp.MustCompile(`^[0-9]+$`) // parsePerms returns a slice of permKey values extracted // from the permission fields in req. func (srv *Server) parsePerms(req *http.Request) []permKey { // perms maps an index found in the form to its associated // IPPerm. For instance, the form value with key // "IpPermissions.3.FromPort" will be stored in perms[3].FromPort perms := make(map[int]ec2.IPPerm) type subgroupKey struct { id1, id2 int } // Each IPPerm can have many source security groups. The form key // for a source security group contains two indices: the index // of the IPPerm and the sub-index of the security group. The // sourceGroups map maps from a subgroupKey containing these // two indices to the associated security group. For instance, // the form value with key "IPPermissions.3.Groups.2.GroupName" // will be stored in sourceGroups[subgroupKey{3, 2}].Name. sourceGroups := make(map[subgroupKey]ec2.UserSecurityGroup) // For each value in the form we store its associated information in the // above maps. The maps are necessary because the form keys may // arrive in any order, and the indices are not // necessarily sequential or even small. for name, vals := range req.Form { val := vals[0] var id1 int var rest string if x, _ := fmt.Sscanf(name, "IpPermissions.%d.%s", &id1, &rest); x != 2 { continue } ec2p := perms[id1] switch { case rest == "FromPort": ec2p.FromPort = atoi(val) case rest == "ToPort": ec2p.ToPort = atoi(val) case rest == "IpProtocol": switch val { case "tcp", "udp", "icmp": ec2p.Protocol = val default: // check it's a well formed number atoi(val) ec2p.Protocol = val } case strings.HasPrefix(rest, "Groups."): k := subgroupKey{id1: id1} if x, _ := fmt.Sscanf(rest[len("Groups."):], "%d.%s", &k.id2, &rest); x != 2 { continue } g := sourceGroups[k] switch rest { case "UserId": // BUG if the user id is blank, this does not conform to the // way that EC2 handles it - a specified but blank owner id // can cause RevokeSecurityGroupIngress to fail with // "group not found" even if the security group id has been // correctly specified. // By failing here, we ensure that we fail early in this case. if !ownerIdPat.MatchString(val) { fatalf(400, "InvalidUserID.Malformed", "Invalid user ID: %q", val) } g.OwnerId = val case "GroupName": g.Name = val case "GroupId": if !secGroupPat.MatchString(val) { fatalf(400, "InvalidGroupId.Malformed", "Invalid group ID: %q", val) } g.Id = val default: fatalf(400, "UnknownParameter", "unknown parameter %q", name) } sourceGroups[k] = g case strings.HasPrefix(rest, "IpRanges."): var id2 int if x, _ := fmt.Sscanf(rest[len("IpRanges."):], "%d.%s", &id2, &rest); x != 2 { continue } switch rest { case "CidrIp": if !ipPat.MatchString(val) { fatalf(400, "InvalidPermission.Malformed", "Invalid IP range: %q", val) } ec2p.SourceIPs = append(ec2p.SourceIPs, val) default: fatalf(400, "UnknownParameter", "unknown parameter %q", name) } default: fatalf(400, "UnknownParameter", "unknown parameter %q", name) } perms[id1] = ec2p } // Associate each set of source groups with its IPPerm. for k, g := range sourceGroups { p := perms[k.id1] p.SourceGroups = append(p.SourceGroups, g) perms[k.id1] = p } // Now that we have built up the IPPerms we need, we check for // parameter errors and build up a permKey for each permission, // looking up security groups from srv as we do so. var result []permKey for _, p := range perms { if p.FromPort > p.ToPort { fatalf(400, "InvalidParameterValue", "invalid port range") } k := permKey{ protocol: p.Protocol, fromPort: p.FromPort, toPort: p.ToPort, } for _, g := range p.SourceGroups { if g.OwnerId != "" && g.OwnerId != ownerId { fatalf(400, "InvalidGroup.NotFound", "group %q not found", g.Name) } var ec2g ec2.SecurityGroup switch { case g.Id != "": ec2g.Id = g.Id case g.Name != "": ec2g.Name = g.Name } k.group = srv.group(ec2g) if k.group == nil { fatalf(400, "InvalidGroup.NotFound", "group %v not found", g) } result = append(result, k) } k.group = nil for _, ip := range p.SourceIPs { k.ipAddr = ip result = append(result, k) } } return result } func (srv *Server) deleteSecurityGroup(w http.ResponseWriter, req *http.Request, reqId string) interface{} { srv.mu.Lock() defer srv.mu.Unlock() g := srv.group(ec2.SecurityGroup{ Name: req.Form.Get("GroupName"), Id: req.Form.Get("GroupId"), }) if g == nil { fatalf(400, "InvalidGroup.NotFound", "group not found") } for _, r := range srv.reservations { for _, h := range r.groups { if h == g && r.hasRunningMachine() { fatalf(500, "InvalidGroup.InUse", "group is currently in use by a running instance") } } } for _, sg := range srv.groups { // If a group refers to itself, it's ok to delete it. if sg == g { continue } for k := range sg.perms { if k.group == g { fatalf(500, "InvalidGroup.InUse", "group is currently in use by group %q", sg.id) } } } delete(srv.groups, g.id) return &ec2.SimpleResp{ XMLName: xml.Name{"", "DeleteSecurityGroupResponse"}, RequestId: reqId, } } func (r *reservation) hasRunningMachine() bool { for _, inst := range r.instances { if inst.state.Code != ShuttingDown.Code && inst.state.Code != Terminated.Code { return true } } return false } type counter int func (c *counter) next() (i int) { i = int(*c) (*c)++ return } // atoi is like strconv.Atoi but is fatal if the // string is not well formed. func atoi(s string) int { i, err := strconv.Atoi(s) if err != nil { fatalf(400, "InvalidParameterValue", "bad number: %v", err) } return i } func fatalf(statusCode int, code string, f string, a ...interface{}) { panic(&ec2.Error{ StatusCode: statusCode, Code: code, Message: fmt.Sprintf(f, a...), }) } ������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/ec2test/filter.go��������������������������������������0000644�0000153�0000161�00000004207�12321735726�023743� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ec2test import ( "fmt" "net/url" "strings" ) // filter holds an ec2 filter. A filter maps an attribute to a set of // possible values for that attribute. For an item to pass through the // filter, every attribute of the item mentioned in the filter must match // at least one of its given values. type filter map[string][]string // newFilter creates a new filter from the Filter fields in the url form. // // The filtering is specified through a map of name=>values, where the // name is a well-defined key identifying the data to be matched, // and the list of values holds the possible values the filtered // item can take for the key to be included in the // result set. For example: // // Filter.1.Name=instance-type // Filter.1.Value.1=m1.small // Filter.1.Value.2=m1.large // func newFilter(form url.Values) filter { // TODO return an error if the fields are not well formed? names := make(map[int]string) values := make(map[int][]string) maxId := 0 for name, fvalues := range form { var rest string var id int if x, _ := fmt.Sscanf(name, "Filter.%d.%s", &id, &rest); x != 2 { continue } if id > maxId { maxId = id } if rest == "Name" { names[id] = fvalues[0] continue } if !strings.HasPrefix(rest, "Value.") { continue } values[id] = append(values[id], fvalues[0]) } f := make(filter) for id, name := range names { f[name] = values[id] } return f } func notDigit(r rune) bool { return r < '0' || r > '9' } // filterable represents an object that can be passed through a filter. type filterable interface { // matchAttr returns true if given attribute of the // object matches value. It returns an error if the // attribute is not recognised or the value is malformed. matchAttr(attr, value string) (bool, error) } // ok returns true if x passes through the filter. func (f filter) ok(x filterable) (bool, error) { next: for a, vs := range f { for _, v := range vs { if ok, err := x.matchAttr(a, v); ok { continue next } else if err != nil { return false, fmt.Errorf("bad attribute or value %q=%q for type %T: %v", a, v, x, err) } } return false, nil } return true, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/ec2.go�������������������������������������������������0000644�0000153�0000161�00000064107�12321736016�021554� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // goamz - Go packages to interact with the Amazon Web Services. // // https://wiki.ubuntu.com/goamz // // Copyright (c) 2011 Canonical Ltd. // // Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com> // package ec2 import ( "crypto/rand" "encoding/hex" "encoding/xml" "fmt" "launchpad.net/goamz/aws" "log" "net/http" "net/http/httputil" "net/url" "sort" "strconv" "time" ) const debug = false // The EC2 type encapsulates operations with a specific EC2 region. type EC2 struct { aws.Auth aws.Region private byte // Reserve the right of using private data. } // New creates a new EC2. func New(auth aws.Auth, region aws.Region) *EC2 { return &EC2{auth, region, 0} } // ---------------------------------------------------------------------------- // Filtering helper. // Filter builds filtering parameters to be used in an EC2 query which supports // filtering. For example: // // filter := NewFilter() // filter.Add("architecture", "i386") // filter.Add("launch-index", "0") // resp, err := ec2.Instances(nil, filter) // type Filter struct { m map[string][]string } // NewFilter creates a new Filter. func NewFilter() *Filter { return &Filter{make(map[string][]string)} } // Add appends a filtering parameter with the given name and value(s). func (f *Filter) Add(name string, value ...string) { f.m[name] = append(f.m[name], value...) } func (f *Filter) addParams(params map[string]string) { if f != nil { a := make([]string, len(f.m)) i := 0 for k := range f.m { a[i] = k i++ } sort.StringSlice(a).Sort() for i, k := range a { prefix := "Filter." + strconv.Itoa(i+1) params[prefix+".Name"] = k for j, v := range f.m[k] { params[prefix+".Value."+strconv.Itoa(j+1)] = v } } } } // ---------------------------------------------------------------------------- // Request dispatching logic. // Error encapsulates an error returned by EC2. // // See http://goo.gl/VZGuC for more details. type Error struct { // HTTP status code (200, 403, ...) StatusCode int // EC2 error code ("UnsupportedOperation", ...) Code string // The human-oriented error message Message string RequestId string `xml:"RequestID"` } func (err *Error) Error() string { if err.Code == "" { return err.Message } return fmt.Sprintf("%s (%s)", err.Message, err.Code) } // For now a single error inst is being exposed. In the future it may be useful // to provide access to all of them, but rather than doing it as an array/slice, // use a *next pointer, so that it's backward compatible and it continues to be // easy to handle the first error, which is what most people will want. type xmlErrors struct { RequestId string `xml:"RequestID"` Errors []Error `xml:"Errors>Error"` } var timeNow = time.Now func (ec2 *EC2) query(params map[string]string, resp interface{}) error { params["Version"] = "2011-12-15" params["Timestamp"] = timeNow().In(time.UTC).Format(time.RFC3339) endpoint, err := url.Parse(ec2.Region.EC2Endpoint) if err != nil { return err } if endpoint.Path == "" { endpoint.Path = "/" } sign(ec2.Auth, "GET", endpoint.Path, params, endpoint.Host) endpoint.RawQuery = multimap(params).Encode() if debug { log.Printf("get { %v } -> {\n", endpoint.String()) } r, err := http.Get(endpoint.String()) if err != nil { return err } defer r.Body.Close() if debug { dump, _ := httputil.DumpResponse(r, true) log.Printf("response:\n") log.Printf("%v\n}\n", string(dump)) } if r.StatusCode != 200 { return buildError(r) } err = xml.NewDecoder(r.Body).Decode(resp) return err } func multimap(p map[string]string) url.Values { q := make(url.Values, len(p)) for k, v := range p { q[k] = []string{v} } return q } func buildError(r *http.Response) error { errors := xmlErrors{} xml.NewDecoder(r.Body).Decode(&errors) var err Error if len(errors.Errors) > 0 { err = errors.Errors[0] } err.RequestId = errors.RequestId err.StatusCode = r.StatusCode if err.Message == "" { err.Message = r.Status } return &err } func makeParams(action string) map[string]string { params := make(map[string]string) params["Action"] = action return params } func addParamsList(params map[string]string, label string, ids []string) { for i, id := range ids { params[label+"."+strconv.Itoa(i+1)] = id } } // ---------------------------------------------------------------------------- // Instance management functions and types. // The RunInstances type encapsulates options for the respective request in EC2. // // See http://goo.gl/Mcm3b for more details. type RunInstances struct { ImageId string MinCount int MaxCount int KeyName string InstanceType string SecurityGroups []SecurityGroup KernelId string RamdiskId string UserData []byte AvailZone string PlacementGroupName string Monitoring bool SubnetId string DisableAPITermination bool ShutdownBehavior string PrivateIPAddress string BlockDeviceMappings []BlockDeviceMapping } // Response to a RunInstances request. // // See http://goo.gl/Mcm3b for more details. type RunInstancesResp struct { RequestId string `xml:"requestId"` ReservationId string `xml:"reservationId"` OwnerId string `xml:"ownerId"` SecurityGroups []SecurityGroup `xml:"groupSet>item"` Instances []Instance `xml:"instancesSet>item"` } // Instance encapsulates a running instance in EC2. // // See http://goo.gl/OCH8a for more details. type Instance struct { InstanceId string `xml:"instanceId"` InstanceType string `xml:"instanceType"` ImageId string `xml:"imageId"` PrivateDNSName string `xml:"privateDnsName"` DNSName string `xml:"dnsName"` IPAddress string `xml:"ipAddress"` PrivateIPAddress string `xml:"privateIpAddress"` KeyName string `xml:"keyName"` AMILaunchIndex int `xml:"amiLaunchIndex"` Hypervisor string `xml:"hypervisor"` VirtType string `xml:"virtualizationType"` Monitoring string `xml:"monitoring>state"` AvailZone string `xml:"placement>availabilityZone"` PlacementGroupName string `xml:"placement>groupName"` State InstanceState `xml:"instanceState"` Tags []Tag `xml:"tagSet>item"` SecurityGroups []SecurityGroup `xml:"groupSet>item"` } // RunInstances starts new instances in EC2. // If options.MinCount and options.MaxCount are both zero, a single instance // will be started; otherwise if options.MaxCount is zero, options.MinCount // will be used insteead. // // See http://goo.gl/Mcm3b for more details. func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err error) { params := makeParams("RunInstances") params["ImageId"] = options.ImageId params["InstanceType"] = options.InstanceType var min, max int if options.MinCount == 0 && options.MaxCount == 0 { min = 1 max = 1 } else if options.MaxCount == 0 { min = options.MinCount max = min } else { min = options.MinCount max = options.MaxCount } params["MinCount"] = strconv.Itoa(min) params["MaxCount"] = strconv.Itoa(max) i, j := 1, 1 for _, g := range options.SecurityGroups { if g.Id != "" { params["SecurityGroupId."+strconv.Itoa(i)] = g.Id i++ } else { params["SecurityGroup."+strconv.Itoa(j)] = g.Name j++ } } for i, b := range options.BlockDeviceMappings { n := strconv.Itoa(i + 1) if b.DeviceName != "" { params["BlockDeviceMapping."+n+".DeviceName"] = b.DeviceName } if b.VirtualName != "" { params["BlockDeviceMapping."+n+".VirtualName"] = b.VirtualName } if b.SnapshotId != "" { params["BlockDeviceMapping."+n+".Ebs.SnapshotId"] = b.SnapshotId } if b.VolumeType != "" { params["BlockDeviceMapping."+n+".Ebs.VolumeType"] = b.VolumeType } if b.VolumeSize > 0 { params["BlockDeviceMapping."+n+".Ebs.VolumeSize"] = strconv.FormatInt(b.VolumeSize, 10) } if b.IOPS > 0 { params["BlockDeviceMapping."+n+".Ebs.Iops"] = strconv.FormatInt(b.IOPS, 10) } if b.DeleteOnTermination { params["BlockDeviceMapping."+n+".Ebs.DeleteOnTermination"] = "true" } } token, err := clientToken() if err != nil { return nil, err } params["ClientToken"] = token if options.KeyName != "" { params["KeyName"] = options.KeyName } if options.KernelId != "" { params["KernelId"] = options.KernelId } if options.RamdiskId != "" { params["RamdiskId"] = options.RamdiskId } if options.UserData != nil { userData := make([]byte, b64.EncodedLen(len(options.UserData))) b64.Encode(userData, options.UserData) params["UserData"] = string(userData) } if options.AvailZone != "" { params["Placement.AvailabilityZone"] = options.AvailZone } if options.PlacementGroupName != "" { params["Placement.GroupName"] = options.PlacementGroupName } if options.Monitoring { params["Monitoring.Enabled"] = "true" } if options.SubnetId != "" { params["SubnetId"] = options.SubnetId } if options.DisableAPITermination { params["DisableApiTermination"] = "true" } if options.ShutdownBehavior != "" { params["InstanceInitiatedShutdownBehavior"] = options.ShutdownBehavior } if options.PrivateIPAddress != "" { params["PrivateIpAddress"] = options.PrivateIPAddress } resp = &RunInstancesResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return } func clientToken() (string, error) { // Maximum EC2 client token size is 64 bytes. // Each byte expands to two when hex encoded. buf := make([]byte, 32) _, err := rand.Read(buf) if err != nil { return "", err } return hex.EncodeToString(buf), nil } // Response to a TerminateInstances request. // // See http://goo.gl/3BKHj for more details. type TerminateInstancesResp struct { RequestId string `xml:"requestId"` StateChanges []InstanceStateChange `xml:"instancesSet>item"` } // InstanceState encapsulates the state of an instance in EC2. // // See http://goo.gl/y3ZBq for more details. type InstanceState struct { Code int `xml:"code"` // Watch out, bits 15-8 have unpublished meaning. Name string `xml:"name"` } // InstanceStateChange informs of the previous and current states // for an instance when a state change is requested. type InstanceStateChange struct { InstanceId string `xml:"instanceId"` CurrentState InstanceState `xml:"currentState"` PreviousState InstanceState `xml:"previousState"` } // TerminateInstances requests the termination of instances when the given ids. // // See http://goo.gl/3BKHj for more details. func (ec2 *EC2) TerminateInstances(instIds []string) (resp *TerminateInstancesResp, err error) { params := makeParams("TerminateInstances") addParamsList(params, "InstanceId", instIds) resp = &TerminateInstancesResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return } // Response to a DescribeInstances request. // // See http://goo.gl/mLbmw for more details. type InstancesResp struct { RequestId string `xml:"requestId"` Reservations []Reservation `xml:"reservationSet>item"` } // Reservation represents details about a reservation in EC2. // // See http://goo.gl/0ItPT for more details. type Reservation struct { ReservationId string `xml:"reservationId"` OwnerId string `xml:"ownerId"` RequesterId string `xml:"requesterId"` SecurityGroups []SecurityGroup `xml:"groupSet>item"` Instances []Instance `xml:"instancesSet>item"` } // Instances returns details about instances in EC2. Both parameters // are optional, and if provided will limit the instances returned to those // matching the given instance ids or filtering rules. // // See http://goo.gl/4No7c for more details. func (ec2 *EC2) Instances(instIds []string, filter *Filter) (resp *InstancesResp, err error) { params := makeParams("DescribeInstances") addParamsList(params, "InstanceId", instIds) filter.addParams(params) resp = &InstancesResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return } // ---------------------------------------------------------------------------- // Image and snapshot management functions and types. // Response to a DescribeImages request. // // See http://goo.gl/hLnyg for more details. type ImagesResp struct { RequestId string `xml:"requestId"` Images []Image `xml:"imagesSet>item"` } // BlockDeviceMapping represents the association of a block device with an image. // // See http://goo.gl/wnDBf for more details. type BlockDeviceMapping struct { DeviceName string `xml:"deviceName"` VirtualName string `xml:"virtualName"` SnapshotId string `xml:"ebs>snapshotId"` VolumeType string `xml:"ebs>volumeType"` VolumeSize int64 `xml:"ebs>volumeSize"` // Size is given in GB DeleteOnTermination bool `xml:"ebs>deleteOnTermination"` // The number of I/O operations per second (IOPS) that the volume supports. IOPS int64 `xml:"ebs>iops"` } // Image represents details about an image. // // See http://goo.gl/iSqJG for more details. type Image struct { Id string `xml:"imageId"` Name string `xml:"name"` Description string `xml:"description"` Type string `xml:"imageType"` State string `xml:"imageState"` Location string `xml:"imageLocation"` Public bool `xml:"isPublic"` Architecture string `xml:"architecture"` Platform string `xml:"platform"` ProductCodes []string `xml:"productCode>item>productCode"` KernelId string `xml:"kernelId"` RamdiskId string `xml:"ramdiskId"` StateReason string `xml:"stateReason"` OwnerId string `xml:"imageOwnerId"` OwnerAlias string `xml:"imageOwnerAlias"` RootDeviceType string `xml:"rootDeviceType"` RootDeviceName string `xml:"rootDeviceName"` VirtualizationType string `xml:"virtualizationType"` Hypervisor string `xml:"hypervisor"` BlockDevices []BlockDeviceMapping `xml:"blockDeviceMapping>item"` } // Images returns details about available images. // The ids and filter parameters, if provided, will limit the images returned. // For example, to get all the private images associated with this account set // the boolean filter "is-private" to true. // // Note: calling this function with nil ids and filter parameters will result in // a very large number of images being returned. // // See http://goo.gl/SRBhW for more details. func (ec2 *EC2) Images(ids []string, filter *Filter) (resp *ImagesResp, err error) { params := makeParams("DescribeImages") for i, id := range ids { params["ImageId."+strconv.Itoa(i+1)] = id } filter.addParams(params) resp = &ImagesResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return } // Response to a CreateSnapshot request. // // See http://goo.gl/ttcda for more details. type CreateSnapshotResp struct { RequestId string `xml:"requestId"` Snapshot } // CreateSnapshot creates a volume snapshot and stores it in S3. // // See http://goo.gl/ttcda for more details. func (ec2 *EC2) CreateSnapshot(volumeId, description string) (resp *CreateSnapshotResp, err error) { params := makeParams("CreateSnapshot") params["VolumeId"] = volumeId params["Description"] = description resp = &CreateSnapshotResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return } // DeleteSnapshots deletes the volume snapshots with the given ids. // // Note: If you make periodic snapshots of a volume, the snapshots are // incremental so that only the blocks on the device that have changed // since your last snapshot are incrementally saved in the new snapshot. // Even though snapshots are saved incrementally, the snapshot deletion // process is designed so that you need to retain only the most recent // snapshot in order to restore the volume. // // See http://goo.gl/vwU1y for more details. func (ec2 *EC2) DeleteSnapshots(ids []string) (resp *SimpleResp, err error) { params := makeParams("DeleteSnapshot") for i, id := range ids { params["SnapshotId."+strconv.Itoa(i+1)] = id } resp = &SimpleResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return } // Response to a DescribeSnapshots request. // // See http://goo.gl/nClDT for more details. type SnapshotsResp struct { RequestId string `xml:"requestId"` Snapshots []Snapshot `xml:"snapshotSet>item"` } // Snapshot represents details about a volume snapshot. // // See http://goo.gl/nkovs for more details. type Snapshot struct { Id string `xml:"snapshotId"` VolumeId string `xml:"volumeId"` VolumeSize string `xml:"volumeSize"` Status string `xml:"status"` StartTime string `xml:"startTime"` Description string `xml:"description"` Progress string `xml:"progress"` OwnerId string `xml:"ownerId"` OwnerAlias string `xml:"ownerAlias"` Tags []Tag `xml:"tagSet>item"` } // Snapshots returns details about volume snapshots available to the user. // The ids and filter parameters, if provided, limit the snapshots returned. // // See http://goo.gl/ogJL4 for more details. func (ec2 *EC2) Snapshots(ids []string, filter *Filter) (resp *SnapshotsResp, err error) { params := makeParams("DescribeSnapshots") for i, id := range ids { params["SnapshotId."+strconv.Itoa(i+1)] = id } filter.addParams(params) resp = &SnapshotsResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return } // ---------------------------------------------------------------------------- // Security group management functions and types. // SimpleResp represents a response to an EC2 request which on success will // return no other information besides a request id. type SimpleResp struct { XMLName xml.Name RequestId string `xml:"requestId"` } // CreateSecurityGroupResp represents a response to a CreateSecurityGroup request. type CreateSecurityGroupResp struct { SecurityGroup RequestId string `xml:"requestId"` } // CreateSecurityGroup run a CreateSecurityGroup request in EC2, with the provided // name and description. // // See http://goo.gl/Eo7Yl for more details. func (ec2 *EC2) CreateSecurityGroup(name, description string) (resp *CreateSecurityGroupResp, err error) { params := makeParams("CreateSecurityGroup") params["GroupName"] = name params["GroupDescription"] = description resp = &CreateSecurityGroupResp{} err = ec2.query(params, resp) if err != nil { return nil, err } resp.Name = name return resp, nil } // SecurityGroupsResp represents a response to a DescribeSecurityGroups // request in EC2. // // See http://goo.gl/k12Uy for more details. type SecurityGroupsResp struct { RequestId string `xml:"requestId"` Groups []SecurityGroupInfo `xml:"securityGroupInfo>item"` } // SecurityGroup encapsulates details for a security group in EC2. // // See http://goo.gl/CIdyP for more details. type SecurityGroupInfo struct { SecurityGroup OwnerId string `xml:"ownerId"` Description string `xml:"groupDescription"` IPPerms []IPPerm `xml:"ipPermissions>item"` } // IPPerm represents an allowance within an EC2 security group. // // See http://goo.gl/4oTxv for more details. type IPPerm struct { Protocol string `xml:"ipProtocol"` FromPort int `xml:"fromPort"` ToPort int `xml:"toPort"` SourceIPs []string `xml:"ipRanges>item>cidrIp"` SourceGroups []UserSecurityGroup `xml:"groups>item"` } // UserSecurityGroup holds a security group and the owner // of that group. type UserSecurityGroup struct { Id string `xml:"groupId"` Name string `xml:"groupName"` OwnerId string `xml:"userId"` } // SecurityGroup represents an EC2 security group. // If SecurityGroup is used as a parameter, then one of Id or Name // may be empty. If both are set, then Id is used. type SecurityGroup struct { Id string `xml:"groupId"` Name string `xml:"groupName"` } // SecurityGroupNames is a convenience function that // returns a slice of security groups with the given names. func SecurityGroupNames(names ...string) []SecurityGroup { g := make([]SecurityGroup, len(names)) for i, name := range names { g[i] = SecurityGroup{Name: name} } return g } // SecurityGroupNames is a convenience function that // returns a slice of security groups with the given ids. func SecurityGroupIds(ids ...string) []SecurityGroup { g := make([]SecurityGroup, len(ids)) for i, id := range ids { g[i] = SecurityGroup{Id: id} } return g } // SecurityGroups returns details about security groups in EC2. Both parameters // are optional, and if provided will limit the security groups returned to those // matching the given groups or filtering rules. // // See http://goo.gl/k12Uy for more details. func (ec2 *EC2) SecurityGroups(groups []SecurityGroup, filter *Filter) (resp *SecurityGroupsResp, err error) { params := makeParams("DescribeSecurityGroups") i, j := 1, 1 for _, g := range groups { if g.Id != "" { params["GroupId."+strconv.Itoa(i)] = g.Id i++ } else { params["GroupName."+strconv.Itoa(j)] = g.Name j++ } } filter.addParams(params) resp = &SecurityGroupsResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return resp, nil } // DeleteSecurityGroup removes the given security group in EC2. // // See http://goo.gl/QJJDO for more details. func (ec2 *EC2) DeleteSecurityGroup(group SecurityGroup) (resp *SimpleResp, err error) { params := makeParams("DeleteSecurityGroup") if group.Id != "" { params["GroupId"] = group.Id } else { params["GroupName"] = group.Name } resp = &SimpleResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return resp, nil } // AuthorizeSecurityGroup creates an allowance for clients matching the provided // rules to access instances within the given security group. // // See http://goo.gl/u2sDJ for more details. func (ec2 *EC2) AuthorizeSecurityGroup(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) { return ec2.authOrRevoke("AuthorizeSecurityGroupIngress", group, perms) } // RevokeSecurityGroup revokes permissions from a group. // // See http://goo.gl/ZgdxA for more details. func (ec2 *EC2) RevokeSecurityGroup(group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) { return ec2.authOrRevoke("RevokeSecurityGroupIngress", group, perms) } func (ec2 *EC2) authOrRevoke(op string, group SecurityGroup, perms []IPPerm) (resp *SimpleResp, err error) { params := makeParams(op) if group.Id != "" { params["GroupId"] = group.Id } else { params["GroupName"] = group.Name } for i, perm := range perms { prefix := "IpPermissions." + strconv.Itoa(i+1) params[prefix+".IpProtocol"] = perm.Protocol params[prefix+".FromPort"] = strconv.Itoa(perm.FromPort) params[prefix+".ToPort"] = strconv.Itoa(perm.ToPort) for j, ip := range perm.SourceIPs { params[prefix+".IpRanges."+strconv.Itoa(j+1)+".CidrIp"] = ip } for j, g := range perm.SourceGroups { subprefix := prefix + ".Groups." + strconv.Itoa(j+1) if g.OwnerId != "" { params[subprefix+".UserId"] = g.OwnerId } if g.Id != "" { params[subprefix+".GroupId"] = g.Id } else { params[subprefix+".GroupName"] = g.Name } } } resp = &SimpleResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return resp, nil } // ResourceTag represents key-value metadata used to classify and organize // EC2 instances. // // See http://goo.gl/bncl3 for more details type Tag struct { Key string `xml:"key"` Value string `xml:"value"` } // CreateTags adds or overwrites one or more tags for the specified instance ids. // // See http://goo.gl/Vmkqc for more details func (ec2 *EC2) CreateTags(instIds []string, tags []Tag) (resp *SimpleResp, err error) { params := makeParams("CreateTags") addParamsList(params, "ResourceId", instIds) for j, tag := range tags { params["Tag."+strconv.Itoa(j+1)+".Key"] = tag.Key params["Tag."+strconv.Itoa(j+1)+".Value"] = tag.Value } resp = &SimpleResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return resp, nil } // Response to a StartInstances request. // // See http://goo.gl/awKeF for more details. type StartInstanceResp struct { RequestId string `xml:"requestId"` StateChanges []InstanceStateChange `xml:"instancesSet>item"` } // Response to a StopInstances request. // // See http://goo.gl/436dJ for more details. type StopInstanceResp struct { RequestId string `xml:"requestId"` StateChanges []InstanceStateChange `xml:"instancesSet>item"` } // StartInstances starts an Amazon EBS-backed AMI that you've previously stopped. // // See http://goo.gl/awKeF for more details. func (ec2 *EC2) StartInstances(ids ...string) (resp *StartInstanceResp, err error) { params := makeParams("StartInstances") addParamsList(params, "InstanceId", ids) resp = &StartInstanceResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return resp, nil } // StopInstances requests stopping one or more Amazon EBS-backed instances. // // See http://goo.gl/436dJ for more details. func (ec2 *EC2) StopInstances(ids ...string) (resp *StopInstanceResp, err error) { params := makeParams("StopInstances") addParamsList(params, "InstanceId", ids) resp = &StopInstanceResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return resp, nil } // RebootInstance requests a reboot of one or more instances. This operation is asynchronous; // it only queues a request to reboot the specified instance(s). The operation will succeed // if the instances are valid and belong to you. // // Requests to reboot terminated instances are ignored. // // See http://goo.gl/baoUf for more details. func (ec2 *EC2) RebootInstances(ids ...string) (resp *SimpleResp, err error) { params := makeParams("RebootInstances") addParamsList(params, "InstanceId", ids) resp = &SimpleResp{} err = ec2.query(params, resp) if err != nil { return nil, err } return resp, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/ec2/sign_test.go�������������������������������������������0000644�0000153�0000161�00000003707�12321735726�023110� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package ec2_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/ec2" . "launchpad.net/gocheck" ) // EC2 ReST authentication docs: http://goo.gl/fQmAN var testAuth = aws.Auth{"user", "secret"} func (s *S) TestBasicSignature(c *C) { params := map[string]string{} ec2.Sign(testAuth, "GET", "/path", params, "localhost") c.Assert(params["SignatureVersion"], Equals, "2") c.Assert(params["SignatureMethod"], Equals, "HmacSHA256") expected := "6lSe5QyXum0jMVc7cOUz32/52ZnL7N5RyKRk/09yiK4=" c.Assert(params["Signature"], Equals, expected) } func (s *S) TestParamSignature(c *C) { params := map[string]string{ "param1": "value1", "param2": "value2", "param3": "value3", } ec2.Sign(testAuth, "GET", "/path", params, "localhost") expected := "XWOR4+0lmK8bD8CGDGZ4kfuSPbb2JibLJiCl/OPu1oU=" c.Assert(params["Signature"], Equals, expected) } func (s *S) TestManyParams(c *C) { params := map[string]string{ "param1": "value10", "param2": "value2", "param3": "value3", "param4": "value4", "param5": "value5", "param6": "value6", "param7": "value7", "param8": "value8", "param9": "value9", "param10": "value1", } ec2.Sign(testAuth, "GET", "/path", params, "localhost") expected := "di0sjxIvezUgQ1SIL6i+C/H8lL+U0CQ9frLIak8jkVg=" c.Assert(params["Signature"], Equals, expected) } func (s *S) TestEscaping(c *C) { params := map[string]string{"Nonce": "+ +"} ec2.Sign(testAuth, "GET", "/path", params, "localhost") c.Assert(params["Nonce"], Equals, "+ +") expected := "bqffDELReIqwjg/W0DnsnVUmfLK4wXVLO4/LuG+1VFA=" c.Assert(params["Signature"], Equals, expected) } func (s *S) TestSignatureExample1(c *C) { params := map[string]string{ "Timestamp": "2009-02-01T12:53:20+00:00", "Version": "2007-11-07", "Action": "ListDomains", } ec2.Sign(aws.Auth{"access", "secret"}, "GET", "/", params, "sdb.amazonaws.com") expected := "okj96/5ucWBSc1uR2zXVfm6mDHtgfNv657rRtt/aunQ=" c.Assert(params["Signature"], Equals, expected) } ���������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/�������������������������������������������������������0000755�0000153�0000161�00000000000�12321735726�020676� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/mturk/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735726�022040� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/mturk/export_test.go�����������������������������������0000644�0000153�0000161�00000000300�12321735726�024740� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package mturk import ( "launchpad.net/goamz/aws" ) func Sign(auth aws.Auth, service, method, timestamp string, params map[string]string) { sign(auth, service, method, timestamp, params) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/mturk/responses_test.go��������������������������������0000644�0000153�0000161�00000002703�12321735726�025451� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package mturk_test var BasicHitResponse = `<?xml version="1.0"?> <CreateHITResponse><OperationRequest><RequestId>643b794b-66b6-4427-bb8a-4d3df5c9a20e</RequestId></OperationRequest><HIT><Request><IsValid>True</IsValid></Request><HITId>28J4IXKO2L927XKJTHO34OCDNASCDW</HITId><HITTypeId>2XZ7D1X3V0FKQVW7LU51S7PKKGFKDF</HITTypeId></HIT></CreateHITResponse> ` var SearchHITResponse = `<?xml version="1.0"?> <SearchHITsResponse><OperationRequest><RequestId>38862d9c-f015-4177-a2d3-924110a9d6f2</RequestId></OperationRequest><SearchHITsResult><Request><IsValid>True</IsValid></Request><NumResults>1</NumResults><TotalNumResults>1</TotalNumResults><PageNumber>1</PageNumber><HIT><HITId>2BU26DG67D1XTE823B3OQ2JF2XWF83</HITId><HITTypeId>22OWJ5OPB0YV6IGL5727KP9U38P5XR</HITTypeId><CreationTime>2011-12-28T19:56:20Z</CreationTime><Title>test hit</Title><Description>please disregard, testing only</Description><HITStatus>Reviewable</HITStatus><MaxAssignments>1</MaxAssignments><Reward><Amount>0.01</Amount><CurrencyCode>USD</CurrencyCode><FormattedPrice>$0.01</FormattedPrice></Reward><AutoApprovalDelayInSeconds>2592000</AutoApprovalDelayInSeconds><Expiration>2011-12-28T19:56:50Z</Expiration><AssignmentDurationInSeconds>30</AssignmentDurationInSeconds><NumberOfAssignmentsPending>0</NumberOfAssignmentsPending><NumberOfAssignmentsAvailable>1</NumberOfAssignmentsAvailable><NumberOfAssignmentsCompleted>0</NumberOfAssignmentsCompleted></HIT></SearchHITsResult></SearchHITsResponse> ` �������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/mturk/sign.go������������������������������������������0000644�0000153�0000161�00000001142�12321735726�023325� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package mturk import ( "crypto/hmac" "crypto/sha1" "encoding/base64" "launchpad.net/goamz/aws" ) var b64 = base64.StdEncoding // ---------------------------------------------------------------------------- // Mechanical Turk signing (http://goo.gl/wrzfn) func sign(auth aws.Auth, service, method, timestamp string, params map[string]string) { payload := service + method + timestamp hash := hmac.New(sha1.New, []byte(auth.SecretKey)) hash.Write([]byte(payload)) signature := make([]byte, b64.EncodedLen(hash.Size())) b64.Encode(signature, hash.Sum(nil)) params["Signature"] = string(signature) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/mturk/mturk_test.go������������������������������������0000644�0000153�0000161�00000005043�12321735726�024572� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package mturk_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/exp/mturk" "launchpad.net/goamz/testutil" . "launchpad.net/gocheck" "net/url" "testing" ) func Test(t *testing.T) { TestingT(t) } var _ = Suite(&S{}) type S struct { mturk *mturk.MTurk } var testServer = testutil.NewHTTPServer() func (s *S) SetUpSuite(c *C) { testServer.Start() auth := aws.Auth{"abc", "123"} u, err := url.Parse(testServer.URL) if err != nil { panic(err.Error()) } s.mturk = &mturk.MTurk{ Auth: auth, URL: u, } } func (s *S) TearDownSuite(c *C) { testServer.Stop() } func (s *S) TearDownTest(c *C) { testServer.Flush() } func (s *S) TestCreateHIT(c *C) { testServer.Response(200, nil, BasicHitResponse) question := mturk.ExternalQuestion{ ExternalURL: "http://www.amazon.com", FrameHeight: 200, } reward := mturk.Price{ Amount: "0.01", CurrencyCode: "USD", } hit, err := s.mturk.CreateHIT("title", "description", question, reward, 1, 2, "key1,key2", 3, nil, "annotation") testServer.WaitRequest() c.Assert(err, IsNil) c.Assert(hit, NotNil) c.Assert(hit.HITId, Equals, "28J4IXKO2L927XKJTHO34OCDNASCDW") c.Assert(hit.HITTypeId, Equals, "2XZ7D1X3V0FKQVW7LU51S7PKKGFKDF") } func (s *S) TestSearchHITs(c *C) { testServer.Response(200, nil, SearchHITResponse) hitResult, err := s.mturk.SearchHITs() c.Assert(err, IsNil) c.Assert(hitResult, NotNil) c.Assert(hitResult.NumResults, Equals, uint(1)) c.Assert(hitResult.PageNumber, Equals, uint(1)) c.Assert(hitResult.TotalNumResults, Equals, uint(1)) c.Assert(len(hitResult.HITs), Equals, 1) c.Assert(hitResult.HITs[0].HITId, Equals, "2BU26DG67D1XTE823B3OQ2JF2XWF83") c.Assert(hitResult.HITs[0].HITTypeId, Equals, "22OWJ5OPB0YV6IGL5727KP9U38P5XR") c.Assert(hitResult.HITs[0].CreationTime, Equals, "2011-12-28T19:56:20Z") c.Assert(hitResult.HITs[0].Title, Equals, "test hit") c.Assert(hitResult.HITs[0].Description, Equals, "please disregard, testing only") c.Assert(hitResult.HITs[0].HITStatus, Equals, "Reviewable") c.Assert(hitResult.HITs[0].MaxAssignments, Equals, uint(1)) c.Assert(hitResult.HITs[0].Reward.Amount, Equals, "0.01") c.Assert(hitResult.HITs[0].Reward.CurrencyCode, Equals, "USD") c.Assert(hitResult.HITs[0].AutoApprovalDelayInSeconds, Equals, uint(2592000)) c.Assert(hitResult.HITs[0].AssignmentDurationInSeconds, Equals, uint(30)) c.Assert(hitResult.HITs[0].NumberOfAssignmentsPending, Equals, uint(0)) c.Assert(hitResult.HITs[0].NumberOfAssignmentsAvailable, Equals, uint(1)) c.Assert(hitResult.HITs[0].NumberOfAssignmentsCompleted, Equals, uint(0)) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/mturk/mturk.go�����������������������������������������0000644�0000153�0000161�00000020247�12321735726�023536� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // goamz - Go packages to interact with the Amazon Web Services. // // https://wiki.ubuntu.com/goamz // // Copyright (c) 2011 Canonical Ltd. // // Written by Graham Miller <graham.miller@gmail.com> // This package is in an experimental state, and does not currently // follow conventions and style of the rest of goamz or common // Go conventions. It must be polished before it's considered a // first-class package in goamz. package mturk import ( "encoding/xml" "errors" "fmt" "launchpad.net/goamz/aws" "net/http" //"net/http/httputil" "net/url" "strconv" "time" ) type MTurk struct { aws.Auth URL *url.URL } func New(auth aws.Auth) *MTurk { mt := &MTurk{Auth: auth} var err error mt.URL, err = url.Parse("http://mechanicalturk.amazonaws.com/") if err != nil { panic(err.Error()) } return mt } // ---------------------------------------------------------------------------- // Request dispatching logic. // Error encapsulates an error returned by MTurk. type Error struct { StatusCode int // HTTP status code (200, 403, ...) Code string // EC2 error code ("UnsupportedOperation", ...) Message string // The human-oriented error message RequestId string } func (err *Error) Error() string { return err.Message } // The request stanza included in several response types, for example // in a "CreateHITResponse". http://goo.gl/qGeKf type xmlRequest struct { RequestId string IsValid string Errors []Error `xml:"Errors>Error"` } // Common price structure used in requests and responses // http://goo.gl/tE4AV type Price struct { Amount string CurrencyCode string FormattedPrice string } // Really just a country string // http://goo.gl/mU4uG type Locale string // Data structure used to specify requirements for the worker // used in CreateHIT, for example // http://goo.gl/LvRo9 type QualificationRequirement struct { QualificationTypeId string Comparator string IntegerValue int LocaleValue Locale RequiredToPreview string } // Data structure holding the contents of an "external" // question. http://goo.gl/NP8Aa type ExternalQuestion struct { XMLName xml.Name `xml:"http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/ExternalQuestion.xsd ExternalQuestion"` ExternalURL string FrameHeight int } // The data structure representing a "human interface task" (HIT) // Currently only supports "external" questions, because Go // structs don't support union types. http://goo.gl/NP8Aa // This type is returned, for example, from SearchHITs // http://goo.gl/PskcX type HIT struct { Request xmlRequest HITId string HITTypeId string CreationTime string Title string Description string Keywords string HITStatus string Reward Price LifetimeInSeconds uint AssignmentDurationInSeconds uint MaxAssignments uint AutoApprovalDelayInSeconds uint QualificationRequirement QualificationRequirement Question ExternalQuestion RequesterAnnotation string NumberofSimilarHITs uint HITReviewStatus string NumberOfAssignmentsPending uint NumberOfAssignmentsAvailable uint NumberOfAssignmentsCompleted uint } // The main data structure returned by SearchHITs // http://goo.gl/PskcX type SearchHITsResult struct { NumResults uint PageNumber uint TotalNumResults uint HITs []HIT `xml:"HIT"` } // The wrapper data structure returned by SearchHITs // http://goo.gl/PskcX type SearchHITsResponse struct { RequestId string `xml:"OperationRequest>RequestId"` SearchHITsResult SearchHITsResult } // The wrapper data structure returned by CreateHIT // http://goo.gl/PskcX type CreateHITResponse struct { RequestId string `xml:"OperationRequest>RequestId"` HIT HIT } // Corresponds to the "CreateHIT" operation of the Mechanical Turk // API. http://goo.gl/cDBRc Currently only supports "external" // questions (see "HIT" struct above). If "keywords", "maxAssignments", // "qualificationRequirement" or "requesterAnnotation" are the zero // value for their types, they will not be included in the request. func (mt *MTurk) CreateHIT(title, description string, question ExternalQuestion, reward Price, assignmentDurationInSeconds, lifetimeInSeconds uint, keywords string, maxAssignments uint, qualificationRequirement *QualificationRequirement, requesterAnnotation string) (h *HIT, err error) { params := make(map[string]string) params["Title"] = title params["Description"] = description params["Question"], err = xmlEncode(&question) if err != nil { return } params["Reward.1.Amount"] = reward.Amount params["Reward.1.CurrencyCode"] = reward.CurrencyCode params["AssignmentDurationInSeconds"] = strconv.FormatUint(uint64(assignmentDurationInSeconds), 10) params["LifetimeInSeconds"] = strconv.FormatUint(uint64(lifetimeInSeconds), 10) if keywords != "" { params["Keywords"] = keywords } if maxAssignments != 0 { params["MaxAssignments"] = strconv.FormatUint(uint64(maxAssignments), 10) } if qualificationRequirement != nil { params["QualificationRequirement"], err = xmlEncode(qualificationRequirement) if err != nil { return } } if requesterAnnotation != "" { params["RequesterAnnotation"] = requesterAnnotation } var response CreateHITResponse err = mt.query(params, "CreateHIT", &response) if err == nil { h = &response.HIT } return } // Corresponds to the "CreateHIT" operation of the Mechanical Turk // API, using an existing "hit type". http://goo.gl/cDBRc Currently only // supports "external" questions (see "HIT" struct above). If // "maxAssignments" or "requesterAnnotation" are the zero value for // their types, they will not be included in the request. func (mt *MTurk) CreateHITOfType(hitTypeId string, q ExternalQuestion, lifetimeInSeconds uint, maxAssignments uint, requesterAnnotation string) (h *HIT, err error) { params := make(map[string]string) params["HITTypeId"] = hitTypeId params["Question"], err = xmlEncode(&q) if err != nil { return } params["LifetimeInSeconds"] = strconv.FormatUint(uint64(lifetimeInSeconds), 10) if maxAssignments != 0 { params["MaxAssignments"] = strconv.FormatUint(uint64(maxAssignments), 10) } if requesterAnnotation != "" { params["RequesterAnnotation"] = requesterAnnotation } var response CreateHITResponse err = mt.query(params, "CreateHIT", &response) if err == nil { h = &response.HIT } return } // Corresponds to "SearchHITs" operation of Mechanical Turk. http://goo.gl/PskcX // Currenlty supports none of the optional parameters. func (mt *MTurk) SearchHITs() (s *SearchHITsResult, err error) { params := make(map[string]string) var response SearchHITsResponse err = mt.query(params, "SearchHITs", &response) if err == nil { s = &response.SearchHITsResult } return } // Adds common parameters to the "params" map, signs the request, // adds the signature to the "params" map and sends the request // to the server. It then unmarshals the response in to the "resp" // parameter using xml.Unmarshal() func (mt *MTurk) query(params map[string]string, operation string, resp interface{}) error { service := "AWSMechanicalTurkRequester" timestamp := time.Now().UTC().Format("2006-01-02T15:04:05Z") params["AWSAccessKeyId"] = mt.Auth.AccessKey params["Service"] = service params["Timestamp"] = timestamp params["Operation"] = operation // make a copy url := *mt.URL sign(mt.Auth, service, operation, timestamp, params) url.RawQuery = multimap(params).Encode() r, err := http.Get(url.String()) if err != nil { return err } //dump, _ := httputil.DumpResponse(r, true) //println("DUMP:\n", string(dump)) if r.StatusCode != 200 { return errors.New(fmt.Sprintf("%d: unexpected status code", r.StatusCode)) } dec := xml.NewDecoder(r.Body) err = dec.Decode(resp) r.Body.Close() return err } func multimap(p map[string]string) url.Values { q := make(url.Values, len(p)) for k, v := range p { q[k] = []string{v} } return q } func xmlEncode(i interface{}) (s string, err error) { var buf []byte buf, err = xml.Marshal(i) if err != nil { return } s = string(buf) return } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/mturk/sign_test.go�������������������������������������0000644�0000153�0000161�00000001015�12321735726�024363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package mturk_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/exp/mturk" . "launchpad.net/gocheck" ) // Mechanical Turk REST authentication docs: http://goo.gl/wrzfn var testAuth = aws.Auth{"user", "secret"} // == fIJy9wCApBNL2R4J2WjJGtIBFX4= func (s *S) TestBasicSignature(c *C) { params := map[string]string{} mturk.Sign(testAuth, "AWSMechanicalTurkRequester", "CreateHIT", "2012-02-16T20:30:47Z", params) expected := "b/TnvzrdeD/L/EyzdFrznPXhido=" c.Assert(params["Signature"], Equals, expected) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sns/���������������������������������������������������0000755�0000153�0000161�00000000000�12321735726�021501� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sns/sns.go���������������������������������������������0000644�0000153�0000161�00000024221�12321735726�022634� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // goamz - Go packages to interact with the Amazon Web Services. // // https://wiki.ubuntu.com/goamz // // Copyright (c) 2011 Memeo Inc. // // Written by Prudhvi Krishna Surapaneni <me@prudhvi.net> // This package is in an experimental state, and does not currently // follow conventions and style of the rest of goamz or common // Go conventions. It must be polished before it's considered a // first-class package in goamz. package sns // BUG(niemeyer): Package needs significant clean up. // BUG(niemeyer): Topic values in responses are not being initialized // properly, since they're supposed to reference *SNS. // BUG(niemeyer): Package needs documentation. // BUG(niemeyer): Message.Message should be "Payload []byte" // BUG(niemeyer): Message.SNS must be dropped. import ( "encoding/xml" "errors" "launchpad.net/goamz/aws" "net/http" "net/url" "strconv" "time" ) // The SNS type encapsulates operation with an SNS region. type SNS struct { aws.Auth aws.Region private byte // Reserve the right of using private data. } type Topic struct { SNS *SNS TopicArn string } func New(auth aws.Auth, region aws.Region) *SNS { return &SNS{auth, region, 0} } type Message struct { SNS *SNS Topic *Topic Message [8192]byte Subject string } type Subscription struct { Endpoint string Owner string Protocol string SubscriptionArn string TopicArn string } func (topic *Topic) Message(message [8192]byte, subject string) *Message { return &Message{topic.SNS, topic, message, subject} } type ResponseMetadata struct { RequestId string `xml:"ResponseMetadata>RequestId"` BoxUsage float64 `xml:"ResponseMetadata>BoxUsage"` } type ListTopicsResp struct { Topics []Topic `xml:"ListTopicsResult>Topics>member"` NextToken string ResponseMetadata } type CreateTopicResp struct { Topic Topic `xml:"CreateTopicResult"` ResponseMetadata } type DeleteTopicResp struct { ResponseMetadata } type ListSubscriptionsResp struct { Subscriptions []Subscription `xml:"ListSubscriptionsResult>Subscriptions>member"` NextToken string ResponseMetadata } type AttributeEntry struct { Key string `xml:"key"` Value string `xml:"value"` } type GetTopicAttributesResp struct { Attributes []AttributeEntry `xml:"GetTopicAttributesResult>Attributes>entry"` ResponseMetadata } func makeParams(action string) map[string]string { params := make(map[string]string) params["Action"] = action return params } // ListTopics // // See http://goo.gl/lfrMK for more details. func (sns *SNS) ListTopics(NextToken *string) (resp *ListTopicsResp, err error) { resp = &ListTopicsResp{} params := makeParams("ListTopics") if NextToken != nil { params["NextToken"] = *NextToken } err = sns.query(nil, nil, params, resp) return } // CreateTopic // // See http://goo.gl/m9aAt for more details. func (sns *SNS) CreateTopic(Name string) (resp *CreateTopicResp, err error) { resp = &CreateTopicResp{} params := makeParams("CreateTopic") params["Name"] = Name err = sns.query(nil, nil, params, resp) return } // DeleteTopic // // See http://goo.gl/OXNcY for more details. func (sns *SNS) DeleteTopic(topic Topic) (resp *DeleteTopicResp, err error) { resp = &DeleteTopicResp{} params := makeParams("DeleteTopic") params["TopicArn"] = topic.TopicArn err = sns.query(nil, nil, params, resp) return } // Delete // // Helper function for deleting a topic func (topic *Topic) Delete() (resp *DeleteTopicResp, err error) { return topic.SNS.DeleteTopic(*topic) } // ListSubscriptions // // See http://goo.gl/k3aGn for more details. func (sns *SNS) ListSubscriptions(NextToken *string) (resp *ListSubscriptionsResp, err error) { resp = &ListSubscriptionsResp{} params := makeParams("ListSubscriptions") if NextToken != nil { params["NextToken"] = *NextToken } err = sns.query(nil, nil, params, resp) return } // GetTopicAttributes // // See http://goo.gl/WXRoX for more details. func (sns *SNS) GetTopicAttributes(TopicArn string) (resp *GetTopicAttributesResp, err error) { resp = &GetTopicAttributesResp{} params := makeParams("GetTopicAttributes") params["TopicArn"] = TopicArn err = sns.query(nil, nil, params, resp) return } type PublishOpt struct { Message string MessageStructure string Subject string TopicArn string } type PublishResp struct { MessageId string `xml:"PublishResult>MessageId"` ResponseMetadata } // Publish // // See http://goo.gl/AY2D8 for more details. func (sns *SNS) Publish(options *PublishOpt) (resp *PublishResp, err error) { resp = &PublishResp{} params := makeParams("Publish") if options.Subject != "" { params["Subject"] = options.Subject } if options.MessageStructure != "" { params["MessageStructure"] = options.MessageStructure } if options.Message != "" { params["Message"] = options.Message } if options.TopicArn != "" { params["TopicArn"] = options.TopicArn } err = sns.query(nil, nil, params, resp) return } type SetTopicAttributesResponse struct { ResponseMetadata } // SetTopicAttributes // // See http://goo.gl/oVYW7 for more details. func (sns *SNS) SetTopicAttributes(AttributeName, AttributeValue, TopicArn string) (resp *SetTopicAttributesResponse, err error) { resp = &SetTopicAttributesResponse{} params := makeParams("SetTopicAttributes") if AttributeName == "" || TopicArn == "" { return nil, errors.New("Invalid Attribute Name or TopicArn") } params["AttributeName"] = AttributeName params["AttributeValue"] = AttributeValue params["TopicArn"] = TopicArn err = sns.query(nil, nil, params, resp) return } type SubscribeResponse struct { SubscriptionArn string `xml:"SubscribeResult>SubscriptionArn"` ResponseMetadata } // Subscribe // // See http://goo.gl/c3iGS for more details. func (sns *SNS) Subscribe(Endpoint, Protocol, TopicArn string) (resp *SubscribeResponse, err error) { resp = &SubscribeResponse{} params := makeParams("Subscribe") params["Endpoint"] = Endpoint params["Protocol"] = Protocol params["TopicArn"] = TopicArn err = sns.query(nil, nil, params, resp) return } type UnsubscribeResponse struct { ResponseMetadata } // Unsubscribe // // See http://goo.gl/4l5Ge for more details. func (sns *SNS) Unsubscribe(SubscriptionArn string) (resp *UnsubscribeResponse, err error) { resp = &UnsubscribeResponse{} params := makeParams("Unsubscribe") params["SubscriptionArn"] = SubscriptionArn err = sns.query(nil, nil, params, resp) return } type ConfirmSubscriptionResponse struct { SubscriptionArn string `xml:"ConfirmSubscriptionResult>SubscriptionArn"` ResponseMetadata } type ConfirmSubscriptionOpt struct { AuthenticateOnUnsubscribe string Token string TopicArn string } // ConfirmSubscription // // See http://goo.gl/3hXzH for more details. func (sns *SNS) ConfirmSubscription(options *ConfirmSubscriptionOpt) (resp *ConfirmSubscriptionResponse, err error) { resp = &ConfirmSubscriptionResponse{} params := makeParams("ConfirmSubscription") if options.AuthenticateOnUnsubscribe != "" { params["AuthenticateOnUnsubscribe"] = options.AuthenticateOnUnsubscribe } params["Token"] = options.Token params["TopicArn"] = options.TopicArn err = sns.query(nil, nil, params, resp) return } type Permission struct { ActionName string AccountId string } type AddPermissionResponse struct { ResponseMetadata } // AddPermission // // See http://goo.gl/mbY4a for more details. func (sns *SNS) AddPermission(permissions []Permission, Label, TopicArn string) (resp *AddPermissionResponse, err error) { resp = &AddPermissionResponse{} params := makeParams("AddPermission") for i, p := range permissions { params["AWSAccountId.member."+strconv.Itoa(i+1)] = p.AccountId params["ActionName.member."+strconv.Itoa(i+1)] = p.ActionName } params["Label"] = Label params["TopicArn"] = TopicArn err = sns.query(nil, nil, params, resp) return } type RemovePermissionResponse struct { ResponseMetadata } // RemovePermission // // See http://goo.gl/wGl5j for more details. func (sns *SNS) RemovePermission(Label, TopicArn string) (resp *RemovePermissionResponse, err error) { resp = &RemovePermissionResponse{} params := makeParams("RemovePermission") params["Label"] = Label params["TopicArn"] = TopicArn err = sns.query(nil, nil, params, resp) return } type ListSubscriptionByTopicResponse struct { Subscriptions []Subscription `xml:"ListSubscriptionsByTopicResult>Subscriptions>member"` ResponseMetadata } type ListSubscriptionByTopicOpt struct { NextToken string TopicArn string } // ListSubscriptionByTopic // // See http://goo.gl/LaVcC for more details. func (sns *SNS) ListSubscriptionByTopic(options *ListSubscriptionByTopicOpt) (resp *ListSubscriptionByTopicResponse, err error) { resp = &ListSubscriptionByTopicResponse{} params := makeParams("ListSbubscriptionByTopic") if options.NextToken != "" { params["NextToken"] = options.NextToken } params["TopicArn"] = options.TopicArn err = sns.query(nil, nil, params, resp) return } type Error struct { StatusCode int Code string Message string RequestId string } func (err *Error) Error() string { return err.Message } type xmlErrors struct { RequestId string Errors []Error `xml:"Errors>Error"` } func (sns *SNS) query(topic *Topic, message *Message, params map[string]string, resp interface{}) error { params["Timestamp"] = time.Now().UTC().Format(time.RFC3339) u, err := url.Parse(sns.Region.SNSEndpoint) if err != nil { return err } sign(sns.Auth, "GET", "/", params, u.Host) u.RawQuery = multimap(params).Encode() r, err := http.Get(u.String()) if err != nil { return err } defer r.Body.Close() //dump, _ := http.DumpResponse(r, true) //println("DUMP:\n", string(dump)) //return nil if r.StatusCode != 200 { return buildError(r) } err = xml.NewDecoder(r.Body).Decode(resp) return err } func buildError(r *http.Response) error { errors := xmlErrors{} xml.NewDecoder(r.Body).Decode(&errors) var err Error if len(errors.Errors) > 0 { err = errors.Errors[0] } err.RequestId = errors.RequestId err.StatusCode = r.StatusCode if err.Message == "" { err.Message = r.Status } return &err } func multimap(p map[string]string) url.Values { q := make(url.Values, len(p)) for k, v := range p { q[k] = []string{v} } return q } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sns/responses_test.go����������������������������������0000644�0000153�0000161�00000013127�12321735726�025114� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package sns_test var TestListTopicsXmlOK = ` <?xml version="1.0"?> <ListTopicsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <ListTopicsResult> <Topics> <member> <TopicArn>arn:aws:sns:us-west-1:331995417492:Transcoding</TopicArn> </member> </Topics> </ListTopicsResult> <ResponseMetadata> <RequestId>bd10b26c-e30e-11e0-ba29-93c3aca2f103</RequestId> </ResponseMetadata> </ListTopicsResponse> ` var TestCreateTopicXmlOK = ` <?xml version="1.0"?> <CreateTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <CreateTopicResult> <TopicArn>arn:aws:sns:us-east-1:123456789012:My-Topic</TopicArn> </CreateTopicResult> <ResponseMetadata> <RequestId>a8dec8b3-33a4-11df-8963-01868b7c937a</RequestId> </ResponseMetadata> </CreateTopicResponse> ` var TestDeleteTopicXmlOK = ` <DeleteTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <ResponseMetadata> <RequestId>f3aa9ac9-3c3d-11df-8235-9dab105e9c32</RequestId> </ResponseMetadata> </DeleteTopicResponse> ` var TestListSubscriptionsXmlOK = ` <ListSubscriptionsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <ListSubscriptionsResult> <Subscriptions> <member> <TopicArn>arn:aws:sns:us-east-1:698519295917:My-Topic</TopicArn> <Protocol>email</Protocol> <SubscriptionArn>arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca</SubscriptionArn> <Owner>123456789012</Owner> <Endpoint>example@amazon.com</Endpoint> </member> </Subscriptions> </ListSubscriptionsResult> <ResponseMetadata> <RequestId>384ac68d-3775-11df-8963-01868b7c937a</RequestId> </ResponseMetadata> </ListSubscriptionsResponse> ` var TestGetTopicAttributesXmlOK = ` <GetTopicAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <GetTopicAttributesResult> <Attributes> <entry> <key>Owner</key> <value>123456789012</value> </entry> <entry> <key>Policy</key> <value>{"Version":"2008-10-17","Id":"us-east-1/698519295917/test__default_policy_ID","Statement" : [{"Effect":"Allow","Sid":"us-east-1/698519295917/test__default_statement_ID","Principal" : {"AWS": "*"},"Action":["SNS:GetTopicAttributes","SNS:SetTopicAttributes","SNS:AddPermission","SNS:RemovePermission","SNS:DeleteTopic","SNS:Subscribe","SNS:ListSubscriptionsByTopic","SNS:Publish","SNS:Receive"],"Resource":"arn:aws:sns:us-east-1:698519295917:test","Condition" : {"StringLike" : {"AWS:SourceArn": "arn:aws:*:*:698519295917:*"}}}]}</value> </entry> <entry> <key>TopicArn</key> <value>arn:aws:sns:us-east-1:123456789012:My-Topic</value> </entry> </Attributes> </GetTopicAttributesResult> <ResponseMetadata> <RequestId>057f074c-33a7-11df-9540-99d0768312d3</RequestId> </ResponseMetadata> </GetTopicAttributesResponse> ` var TestPublishXmlOK = ` <PublishResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <PublishResult> <MessageId>94f20ce6-13c5-43a0-9a9e-ca52d816e90b</MessageId> </PublishResult> <ResponseMetadata> <RequestId>f187a3c1-376f-11df-8963-01868b7c937a</RequestId> </ResponseMetadata> </PublishResponse> ` var TestSetTopicAttributesXmlOK = ` <SetTopicAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <ResponseMetadata> <RequestId>a8763b99-33a7-11df-a9b7-05d48da6f042</RequestId> </ResponseMetadata> </SetTopicAttributesResponse> ` var TestSubscribeXmlOK = ` <SubscribeResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <SubscribeResult> <SubscriptionArn>pending confirmation</SubscriptionArn> </SubscribeResult> <ResponseMetadata> <RequestId>a169c740-3766-11df-8963-01868b7c937a</RequestId> </ResponseMetadata> </SubscribeResponse> ` var TestUnsubscribeXmlOK = ` <UnsubscribeResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <ResponseMetadata> <RequestId>18e0ac39-3776-11df-84c0-b93cc1666b84</RequestId> </ResponseMetadata> </UnsubscribeResponse> ` var TestConfirmSubscriptionXmlOK = ` <ConfirmSubscriptionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <ConfirmSubscriptionResult> <SubscriptionArn>arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca</SubscriptionArn> </ConfirmSubscriptionResult> <ResponseMetadata> <RequestId>7a50221f-3774-11df-a9b7-05d48da6f042</RequestId> </ResponseMetadata> </ConfirmSubscriptionResponse> ` var TestAddPermissionXmlOK = ` <AddPermissionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <ResponseMetadata> <RequestId>6a213e4e-33a8-11df-9540-99d0768312d3</RequestId> </ResponseMetadata> </AddPermissionResponse> ` var TestRemovePermissionXmlOK = ` <RemovePermissionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <ResponseMetadata> <RequestId>d170b150-33a8-11df-995a-2d6fbe836cc1</RequestId> </ResponseMetadata> </RemovePermissionResponse> ` var TestListSubscriptionsByTopicXmlOK = ` <ListSubscriptionsByTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/"> <ListSubscriptionsByTopicResult> <Subscriptions> <member> <TopicArn>arn:aws:sns:us-east-1:123456789012:My-Topic</TopicArn> <Protocol>email</Protocol> <SubscriptionArn>arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca</SubscriptionArn> <Owner>123456789012</Owner> <Endpoint>example@amazon.com</Endpoint> </member> </Subscriptions> </ListSubscriptionsByTopicResult> <ResponseMetadata> <RequestId>b9275252-3774-11df-9540-99d0768312d3</RequestId> </ResponseMetadata> </ListSubscriptionsByTopicResponse> ` �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sns/sign.go��������������������������������������������0000644�0000153�0000161�00000003507�12321735726�022775� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package sns import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "launchpad.net/goamz/aws" "sort" "strings" ) var b64 = base64.StdEncoding /* func sign(auth aws.Auth, method, path string, params url.Values, headers http.Header) { var host string for k, v := range headers { k = strings.ToLower(k) switch k { case "host": host = v[0] } } params["AWSAccessKeyId"] = []string{auth.AccessKey} params["SignatureVersion"] = []string{"2"} params["SignatureMethod"] = []string{"HmacSHA256"} var sarry []string for k, v := range params { sarry = append(sarry, aws.Encode(k) + "=" + aws.Encode(v[0])) } sort.StringSlice(sarry).Sort() joined := strings.Join(sarry, "&") payload := strings.Join([]string{method, host, "/", joined}, "\n") hash := hmac.NewSHA256([]byte(auth.SecretKey)) hash.Write([]byte(payload)) signature := make([]byte, b64.EncodedLen(hash.Size())) b64.Encode(signature, hash.Sum()) params["Signature"] = []string{"AWS " + string(signature)} println("Payload:", payload) println("Signature:", strings.Join(params["Signature"], "|")) }*/ func sign(auth aws.Auth, method, path string, params map[string]string, host string) { params["AWSAccessKeyId"] = auth.AccessKey params["SignatureVersion"] = "2" params["SignatureMethod"] = "HmacSHA256" var sarray []string for k, v := range params { sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v)) } sort.StringSlice(sarray).Sort() joined := strings.Join(sarray, "&") payload := method + "\n" + host + "\n" + path + "\n" + joined hash := hmac.New(sha256.New, []byte(auth.SecretKey)) hash.Write([]byte(payload)) signature := make([]byte, b64.EncodedLen(hash.Size())) b64.Encode(signature, hash.Sum(nil)) params["Signature"] = string(signature) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sns/README���������������������������������������������0000644�0000153�0000161�00000000063�12321735726�022360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Amazon Simple Notification Service API for Golang. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sns/Makefile�������������������������������������������0000644�0000153�0000161�00000000565�12321735726�023147� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������include $(GOROOT)/src/Make.inc TARG=launchpad.net/goamz/sns GOFILES=\ sns.go\ sign.go\ include $(GOROOT)/src/Make.pkg GOFMT=gofmt BADFMT=$(shell $(GOFMT) -l $(GOFILES) 2> /dev/null) gofmt: $(BADFMT) @for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done ifneq ($(BADFMT),) ifneq ($(MAKECMDGOALS), gofmt) #$(warning WARNING: make gofmt: $(BADFMT)) endif endif �������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sns/sns_test.go����������������������������������������0000644�0000153�0000161�00000021026�12321735726�023673� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package sns_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/exp/sns" "launchpad.net/goamz/testutil" . "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { TestingT(t) } var _ = Suite(&S{}) type S struct { sns *sns.SNS } var testServer = testutil.NewHTTPServer() func (s *S) SetUpSuite(c *C) { testServer.Start() auth := aws.Auth{"abc", "123"} s.sns = sns.New(auth, aws.Region{SNSEndpoint: testServer.URL}) } func (s *S) TearDownSuite(c *C) { testServer.Stop() } func (s *S) TearDownTest(c *C) { testServer.Flush() } func (s *S) TestListTopicsOK(c *C) { testServer.Response(200, nil, TestListTopicsXmlOK) resp, err := s.sns.ListTopics(nil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.ResponseMetadata.RequestId, Equals, "bd10b26c-e30e-11e0-ba29-93c3aca2f103") c.Assert(err, IsNil) } func (s *S) TestCreateTopic(c *C) { testServer.Response(200, nil, TestCreateTopicXmlOK) resp, err := s.sns.CreateTopic("My-Topic") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.Topic.TopicArn, Equals, "arn:aws:sns:us-east-1:123456789012:My-Topic") c.Assert(resp.ResponseMetadata.RequestId, Equals, "a8dec8b3-33a4-11df-8963-01868b7c937a") c.Assert(err, IsNil) } func (s *S) TestDeleteTopic(c *C) { testServer.Response(200, nil, TestDeleteTopicXmlOK) t := sns.Topic{nil, "arn:aws:sns:us-east-1:123456789012:My-Topic"} resp, err := s.sns.DeleteTopic(t) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.ResponseMetadata.RequestId, Equals, "f3aa9ac9-3c3d-11df-8235-9dab105e9c32") c.Assert(err, IsNil) } func (s *S) TestListSubscriptions(c *C) { testServer.Response(200, nil, TestListSubscriptionsXmlOK) resp, err := s.sns.ListSubscriptions(nil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(len(resp.Subscriptions), Not(Equals), 0) c.Assert(resp.Subscriptions[0].Protocol, Equals, "email") c.Assert(resp.Subscriptions[0].Endpoint, Equals, "example@amazon.com") c.Assert(resp.Subscriptions[0].SubscriptionArn, Equals, "arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca") c.Assert(resp.Subscriptions[0].TopicArn, Equals, "arn:aws:sns:us-east-1:698519295917:My-Topic") c.Assert(resp.Subscriptions[0].Owner, Equals, "123456789012") c.Assert(err, IsNil) } func (s *S) TestGetTopicAttributes(c *C) { testServer.Response(200, nil, TestGetTopicAttributesXmlOK) resp, err := s.sns.GetTopicAttributes("arn:aws:sns:us-east-1:123456789012:My-Topic") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(len(resp.Attributes), Not(Equals), 0) c.Assert(resp.Attributes[0].Key, Equals, "Owner") c.Assert(resp.Attributes[0].Value, Equals, "123456789012") c.Assert(resp.Attributes[1].Key, Equals, "Policy") c.Assert(resp.Attributes[1].Value, Equals, `{"Version":"2008-10-17","Id":"us-east-1/698519295917/test__default_policy_ID","Statement" : [{"Effect":"Allow","Sid":"us-east-1/698519295917/test__default_statement_ID","Principal" : {"AWS": "*"},"Action":["SNS:GetTopicAttributes","SNS:SetTopicAttributes","SNS:AddPermission","SNS:RemovePermission","SNS:DeleteTopic","SNS:Subscribe","SNS:ListSubscriptionsByTopic","SNS:Publish","SNS:Receive"],"Resource":"arn:aws:sns:us-east-1:698519295917:test","Condition" : {"StringLike" : {"AWS:SourceArn": "arn:aws:*:*:698519295917:*"}}}]}`) c.Assert(resp.ResponseMetadata.RequestId, Equals, "057f074c-33a7-11df-9540-99d0768312d3") c.Assert(err, IsNil) } func (s *S) TestPublish(c *C) { testServer.Response(200, nil, TestPublishXmlOK) pubOpt := &sns.PublishOpt{"foobar", "", "subject", "arn:aws:sns:us-east-1:123456789012:My-Topic"} resp, err := s.sns.Publish(pubOpt) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.MessageId, Equals, "94f20ce6-13c5-43a0-9a9e-ca52d816e90b") c.Assert(resp.ResponseMetadata.RequestId, Equals, "f187a3c1-376f-11df-8963-01868b7c937a") c.Assert(err, IsNil) } func (s *S) TestSetTopicAttributes(c *C) { testServer.Response(200, nil, TestSetTopicAttributesXmlOK) resp, err := s.sns.SetTopicAttributes("DisplayName", "MyTopicName", "arn:aws:sns:us-east-1:123456789012:My-Topic") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.ResponseMetadata.RequestId, Equals, "a8763b99-33a7-11df-a9b7-05d48da6f042") c.Assert(err, IsNil) } func (s *S) TestSubscribe(c *C) { testServer.Response(200, nil, TestSubscribeXmlOK) resp, err := s.sns.Subscribe("example@amazon.com", "email", "arn:aws:sns:us-east-1:123456789012:My-Topic") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.SubscriptionArn, Equals, "pending confirmation") c.Assert(resp.ResponseMetadata.RequestId, Equals, "a169c740-3766-11df-8963-01868b7c937a") c.Assert(err, IsNil) } func (s *S) TestUnsubscribe(c *C) { testServer.Response(200, nil, TestUnsubscribeXmlOK) resp, err := s.sns.Unsubscribe("arn:aws:sns:us-east-1:123456789012:My-Topic:a169c740-3766-11df-8963-01868b7c937a") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.ResponseMetadata.RequestId, Equals, "18e0ac39-3776-11df-84c0-b93cc1666b84") c.Assert(err, IsNil) } func (s *S) TestConfirmSubscription(c *C) { testServer.Response(200, nil, TestConfirmSubscriptionXmlOK) opt := &sns.ConfirmSubscriptionOpt{"", "51b2ff3edb475b7d91550e0ab6edf0c1de2a34e6ebaf6", "arn:aws:sns:us-east-1:123456789012:My-Topic"} resp, err := s.sns.ConfirmSubscription(opt) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.SubscriptionArn, Equals, "arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca") c.Assert(resp.ResponseMetadata.RequestId, Equals, "7a50221f-3774-11df-a9b7-05d48da6f042") c.Assert(err, IsNil) } func (s *S) TestAddPermission(c *C) { testServer.Response(200, nil, TestAddPermissionXmlOK) perm := make([]sns.Permission, 2) perm[0].ActionName = "Publish" perm[1].ActionName = "GetTopicAttributes" perm[0].AccountId = "987654321000" perm[1].AccountId = "876543210000" resp, err := s.sns.AddPermission(perm, "NewPermission", "arn:aws:sns:us-east-1:123456789012:My-Topic") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.RequestId, Equals, "6a213e4e-33a8-11df-9540-99d0768312d3") c.Assert(err, IsNil) } func (s *S) TestRemovePermission(c *C) { testServer.Response(200, nil, TestRemovePermissionXmlOK) resp, err := s.sns.RemovePermission("NewPermission", "arn:aws:sns:us-east-1:123456789012:My-Topic") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.RequestId, Equals, "d170b150-33a8-11df-995a-2d6fbe836cc1") c.Assert(err, IsNil) } func (s *S) TestListSubscriptionByTopic(c *C) { testServer.Response(200, nil, TestListSubscriptionsByTopicXmlOK) opt := &sns.ListSubscriptionByTopicOpt{"", "arn:aws:sns:us-east-1:123456789012:My-Topic"} resp, err := s.sns.ListSubscriptionByTopic(opt) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(len(resp.Subscriptions), Not(Equals), 0) c.Assert(resp.Subscriptions[0].TopicArn, Equals, "arn:aws:sns:us-east-1:123456789012:My-Topic") c.Assert(resp.Subscriptions[0].SubscriptionArn, Equals, "arn:aws:sns:us-east-1:123456789012:My-Topic:80289ba6-0fd4-4079-afb4-ce8c8260f0ca") c.Assert(resp.Subscriptions[0].Owner, Equals, "123456789012") c.Assert(resp.Subscriptions[0].Endpoint, Equals, "example@amazon.com") c.Assert(resp.Subscriptions[0].Protocol, Equals, "email") c.Assert(err, IsNil) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sdb/���������������������������������������������������0000755�0000153�0000161�00000000000�12321735726�021446� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sdb/export_test.go�������������������������������������0000644�0000153�0000161�00000000312�12321735726�024351� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package sdb import ( "launchpad.net/goamz/aws" ) func Sign(auth aws.Auth, method, path string, params map[string][]string, headers map[string][]string) { sign(auth, method, path, params, headers) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sdb/responses_test.go����������������������������������0000644�0000153�0000161�00000007740�12321735726�025065� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package sdb_test var TestCreateDomainXmlOK = ` <?xml version="1.0"?> <CreateDomainResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/"> <ResponseMetadata> <RequestId>63264005-7a5f-e01a-a224-395c63b89f6d</RequestId> <BoxUsage>0.0055590279</BoxUsage> </ResponseMetadata> </CreateDomainResponse> ` var TestListDomainsXmlOK = ` <?xml version="1.0"?> <ListDomainsResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/"> <ListDomainsResult> <DomainName>Account</DomainName> <DomainName>Domain</DomainName> <DomainName>Record</DomainName> </ListDomainsResult> <ResponseMetadata> <RequestId>15fcaf55-9914-63c2-21f3-951e31193790</RequestId> <BoxUsage>0.0000071759</BoxUsage> </ResponseMetadata> </ListDomainsResponse> ` var TestListDomainsWithNextTokenXmlOK = ` <?xml version="1.0"?> <ListDomainsResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/"> <ListDomainsResult> <DomainName>Domain1-200706011651</DomainName> <DomainName>Domain2-200706011652</DomainName> <NextToken>TWV0ZXJpbmdUZXN0RG9tYWluMS0yMDA3MDYwMTE2NTY=</NextToken> </ListDomainsResult> <ResponseMetadata> <RequestId>eb13162f-1b95-4511-8b12-489b86acfd28</RequestId> <BoxUsage>0.0000219907</BoxUsage> </ResponseMetadata> </ListDomainsResponse> ` var TestDeleteDomainXmlOK = ` <?xml version="1.0"?> <DeleteDomainResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/"> <ResponseMetadata> <RequestId>039e1e25-9a64-2a74-93da-2fda36122a97</RequestId> <BoxUsage>0.0055590278</BoxUsage> </ResponseMetadata> </DeleteDomainResponse> ` var TestDomainMetadataXmlNoSuchDomain = ` <?xml version="1.0"?> <Response xmlns="http://sdb.amazonaws.com/doc/2009-04-15/"> <Errors> <Error> <Code>NoSuchDomain</Code> <Message>The specified domain does not exist.</Message> <BoxUsage>0.0000071759</BoxUsage> </Error> </Errors> <RequestID>e050cea2-a772-f90e-2cb0-98ebd42c2898</RequestID> </Response> ` var TestPutAttrsXmlOK = ` <?xml version="1.0"?> <PutAttributesResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/"> <ResponseMetadata> <RequestId>490206ce-8292-456c-a00f-61b335eb202b</RequestId> <BoxUsage>0.0000219907</BoxUsage> </ResponseMetadata> </PutAttributesResponse> ` var TestAttrsXmlOK = ` <?xml version="1.0"?> <GetAttributesResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/"> <GetAttributesResult> <Attribute><Name>Color</Name><Value>Blue</Value></Attribute> <Attribute><Name>Size</Name><Value>Med</Value></Attribute> </GetAttributesResult> <ResponseMetadata> <RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId> <BoxUsage>0.0000219942</BoxUsage> </ResponseMetadata> </GetAttributesResponse> ` var TestSelectXmlOK = ` <?xml version="1.0"?> <SelectResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/"> <SelectResult> <Item> <Name>Item_03</Name> <Attribute><Name>Category</Name><Value>Clothes</Value></Attribute> <Attribute><Name>Subcategory</Name><Value>Pants</Value></Attribute> <Attribute><Name>Name</Name><Value>Sweatpants</Value></Attribute> <Attribute><Name>Color</Name><Value>Blue</Value></Attribute> <Attribute><Name>Color</Name><Value>Yellow</Value></Attribute> <Attribute><Name>Color</Name><Value>Pink</Value></Attribute> <Attribute><Name>Size</Name><Value>Large</Value></Attribute> </Item> <Item> <Name>Item_06</Name> <Attribute><Name>Category</Name><Value>Motorcycle Parts</Value></Attribute> <Attribute><Name>Subcategory</Name><Value>Bodywork</Value></Attribute> <Attribute><Name>Name</Name><Value>Fender Eliminator</Value></Attribute> <Attribute><Name>Color</Name><Value>Blue</Value></Attribute> <Attribute><Name>Make</Name><Value>Yamaha</Value></Attribute> <Attribute><Name>Model</Name><Value>R1</Value></Attribute> </Item> </SelectResult> <ResponseMetadata> <RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId> <BoxUsage>0.0000219907</BoxUsage> </ResponseMetadata> </SelectResponse> ` ��������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sdb/sign.go��������������������������������������������0000644�0000153�0000161�00000002466�12321735726�022745� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package sdb import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "launchpad.net/goamz/aws" "net/http" "net/url" "sort" "strings" ) var b64 = base64.StdEncoding // ---------------------------------------------------------------------------- // SimpleDB signing (http://goo.gl/CaY81) func sign(auth aws.Auth, method, path string, params url.Values, headers http.Header) { var host string for k, v := range headers { k = strings.ToLower(k) switch k { case "host": host = v[0] } } // set up some defaults used for signing the request params["AWSAccessKeyId"] = []string{auth.AccessKey} params["SignatureVersion"] = []string{"2"} params["SignatureMethod"] = []string{"HmacSHA256"} // join up all the incoming params var sarray []string for k, v := range params { sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v[0])) } sort.StringSlice(sarray).Sort() joined := strings.Join(sarray, "&") // create the payload, sign it and create the signature payload := strings.Join([]string{method, host, "/", joined}, "\n") hash := hmac.New(sha256.New, []byte(auth.SecretKey)) hash.Write([]byte(payload)) signature := make([]byte, b64.EncodedLen(hash.Size())) b64.Encode(signature, hash.Sum(nil)) // add the signature to the outgoing params params["Signature"] = []string{string(signature)} } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sdb/sdb_test.go����������������������������������������0000644�0000153�0000161�00000016364�12321735726�023616� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package sdb_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/exp/sdb" "launchpad.net/goamz/testutil" . "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { TestingT(t) } var _ = Suite(&S{}) type S struct { sdb *sdb.SDB } var testServer = testutil.NewHTTPServer() func (s *S) SetUpSuite(c *C) { testServer.Start() auth := aws.Auth{"abc", "123"} s.sdb = sdb.New(auth, aws.Region{SDBEndpoint: testServer.URL}) } func (s *S) TearDownSuite(c *C) { testServer.Stop() } func (s *S) TearDownTest(c *C) { testServer.Flush() } func (s *S) TestCreateDomainOK(c *C) { testServer.Response(200, nil, TestCreateDomainXmlOK) domain := s.sdb.Domain("domain") resp, err := domain.CreateDomain() req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.ResponseMetadata.RequestId, Equals, "63264005-7a5f-e01a-a224-395c63b89f6d") c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0055590279) c.Assert(err, IsNil) } func (s *S) TestListDomainsOK(c *C) { testServer.Response(200, nil, TestListDomainsXmlOK) resp, err := s.sdb.ListDomains() req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.ResponseMetadata.RequestId, Equals, "15fcaf55-9914-63c2-21f3-951e31193790") c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000071759) c.Assert(resp.Domains, DeepEquals, []string{"Account", "Domain", "Record"}) c.Assert(err, IsNil) } func (s *S) TestListDomainsWithNextTokenXmlOK(c *C) { testServer.Response(200, nil, TestListDomainsWithNextTokenXmlOK) resp, err := s.sdb.ListDomains() req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.ResponseMetadata.RequestId, Equals, "eb13162f-1b95-4511-8b12-489b86acfd28") c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907) c.Assert(resp.Domains, DeepEquals, []string{"Domain1-200706011651", "Domain2-200706011652"}) c.Assert(resp.NextToken, Equals, "TWV0ZXJpbmdUZXN0RG9tYWluMS0yMDA3MDYwMTE2NTY=") c.Assert(err, IsNil) } func (s *S) TestDeleteDomainOK(c *C) { testServer.Response(200, nil, TestDeleteDomainXmlOK) domain := s.sdb.Domain("domain") resp, err := domain.DeleteDomain() req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(resp.ResponseMetadata.RequestId, Equals, "039e1e25-9a64-2a74-93da-2fda36122a97") c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0055590278) c.Assert(err, IsNil) } func (s *S) TestPutAttrsOK(c *C) { testServer.Response(200, nil, TestPutAttrsXmlOK) domain := s.sdb.Domain("MyDomain") item := domain.Item("Item123") putAttrs := new(sdb.PutAttrs) putAttrs.Add("FirstName", "john") putAttrs.Add("LastName", "smith") putAttrs.Replace("MiddleName", "jacob") putAttrs.IfValue("FirstName", "john") putAttrs.IfMissing("FirstName") resp, err := item.PutAttrs(putAttrs) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Form["Action"], DeepEquals, []string{"PutAttributes"}) c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"}) c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"}) c.Assert(req.Form["Attribute.1.Name"], DeepEquals, []string{"FirstName"}) c.Assert(req.Form["Attribute.1.Value"], DeepEquals, []string{"john"}) c.Assert(req.Form["Attribute.2.Name"], DeepEquals, []string{"LastName"}) c.Assert(req.Form["Attribute.2.Value"], DeepEquals, []string{"smith"}) c.Assert(req.Form["Attribute.3.Name"], DeepEquals, []string{"MiddleName"}) c.Assert(req.Form["Attribute.3.Value"], DeepEquals, []string{"jacob"}) c.Assert(req.Form["Attribute.3.Replace"], DeepEquals, []string{"true"}) c.Assert(req.Form["Expected.1.Name"], DeepEquals, []string{"FirstName"}) c.Assert(req.Form["Expected.1.Value"], DeepEquals, []string{"john"}) c.Assert(req.Form["Expected.1.Exists"], DeepEquals, []string{"false"}) c.Assert(err, IsNil) c.Assert(resp.ResponseMetadata.RequestId, Equals, "490206ce-8292-456c-a00f-61b335eb202b") c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907) } func (s *S) TestAttrsOK(c *C) { testServer.Response(200, nil, TestAttrsXmlOK) domain := s.sdb.Domain("MyDomain") item := domain.Item("Item123") resp, err := item.Attrs(nil, true) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(req.Form["Action"], DeepEquals, []string{"GetAttributes"}) c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"}) c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"}) c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"}) c.Assert(resp.Attrs[0].Name, Equals, "Color") c.Assert(resp.Attrs[0].Value, Equals, "Blue") c.Assert(resp.Attrs[1].Name, Equals, "Size") c.Assert(resp.Attrs[1].Value, Equals, "Med") c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d") c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219942) c.Assert(err, IsNil) } func (s *S) TestAttrsSelectOK(c *C) { testServer.Response(200, nil, TestAttrsXmlOK) domain := s.sdb.Domain("MyDomain") item := domain.Item("Item123") resp, err := item.Attrs([]string{"Color", "Size"}, true) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(req.Form["Action"], DeepEquals, []string{"GetAttributes"}) c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"}) c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"}) c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"}) c.Assert(req.Form["AttributeName.1"], DeepEquals, []string{"Color"}) c.Assert(req.Form["AttributeName.2"], DeepEquals, []string{"Size"}) c.Assert(resp.Attrs[0].Name, Equals, "Color") c.Assert(resp.Attrs[0].Value, Equals, "Blue") c.Assert(resp.Attrs[1].Name, Equals, "Size") c.Assert(resp.Attrs[1].Value, Equals, "Med") c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d") c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219942) c.Assert(err, IsNil) } func (s *S) TestSelectOK(c *C) { testServer.Response(200, nil, TestSelectXmlOK) resp, err := s.sdb.Select("select Color from MyDomain where Color like 'Blue%'", true) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(req.Form["Action"], DeepEquals, []string{"Select"}) c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"}) c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d") c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907) c.Assert(len(resp.Items), Equals, 2) c.Assert(resp.Items[0].Name, Equals, "Item_03") c.Assert(resp.Items[1].Name, Equals, "Item_06") c.Assert(resp.Items[0].Attrs[0].Name, Equals, "Category") c.Assert(resp.Items[0].Attrs[0].Value, Equals, "Clothes") c.Assert(err, IsNil) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sdb/sdb.go���������������������������������������������0000644�0000153�0000161�00000025402�12321735726�022550� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // goamz - Go packages to interact with the Amazon Web Services. // // https://wiki.ubuntu.com/goamz // // Copyright (c) 2011 AppsAttic Ltd. // // sdb package written by: // // Andrew Chilton <chilts@appsattic.com> // Brad Rydzewski <brad.rydzewski@gmail.com> // This package is in an experimental state, and does not currently // follow conventions and style of the rest of goamz or common // Go conventions. It must be polished before it's considered a // first-class package in goamz. package sdb // BUG: SelectResp isn't properly organized. It must change. // import ( "encoding/xml" "launchpad.net/goamz/aws" "log" "net/http" "net/http/httputil" "net/url" "strconv" "time" ) const debug = false // The SDB type encapsulates operations with a specific SimpleDB region. type SDB struct { aws.Auth aws.Region private byte // Reserve the right of using private data. } // New creates a new SDB. func New(auth aws.Auth, region aws.Region) *SDB { return &SDB{auth, region, 0} } // The Domain type represents a collection of items that are described // by name-value attributes. type Domain struct { *SDB Name string } // Domain returns a Domain with the given name. func (sdb *SDB) Domain(name string) *Domain { return &Domain{sdb, name} } // The Item type represent individual objects that contain one or more // name-value attributes stored within a SDB Domain as rows. type Item struct { *SDB *Domain Name string } // Item returns an Item with the given name. func (domain *Domain) Item(name string) *Item { return &Item{domain.SDB, domain, name} } // The Attr type represent categories of data that can be assigned to items. type Attr struct { Name string Value string } // ---------------------------------------------------------------------------- // Service-level operations. // --- ListDomains // Response to a ListDomains request. // // See http://goo.gl/3u0Cf for more details. type ListDomainsResp struct { Domains []string `xml:"ListDomainsResult>DomainName"` NextToken string `xml:"ListDomainsResult>NextToken"` ResponseMetadata ResponseMetadata } // ListDomains lists all domains in sdb. // // See http://goo.gl/Dsw15 for more details. func (sdb *SDB) ListDomains() (resp *ListDomainsResp, err error) { return sdb.ListDomainsN(0, "") } // ListDomainsN lists domains in sdb up to maxDomains. // If nextToken is not empty, domains listed will start at the given token. // // See http://goo.gl/Dsw15 for more details. func (sdb *SDB) ListDomainsN(maxDomains int, nextToken string) (resp *ListDomainsResp, err error) { params := makeParams("ListDomains") if maxDomains != 0 { params["MaxNumberOfDomains"] = []string{strconv.Itoa(maxDomains)} } if nextToken != "" { params["NextToken"] = []string{nextToken} } resp = &ListDomainsResp{} err = sdb.query(nil, nil, params, nil, resp) return } // --- SelectExpression // Response to a Select request. // // See http://goo.gl/GTsSZ for more details. type SelectResp struct { Items []struct { Name string Attrs []Attr `xml:"Attribute"` } `xml:"SelectResult>Item"` ResponseMetadata ResponseMetadata } // Select returns a set of items and attributes that match expr. // Select is similar to the standard SQL SELECT statement. // // See http://goo.gl/GTsSZ for more details. func (sdb *SDB) Select(expr string, consistent bool) (resp *SelectResp, err error) { resp = &SelectResp{} params := makeParams("Select") params["SelectExpression"] = []string{expr} if consistent { params["ConsistentRead"] = []string{"true"} } err = sdb.query(nil, nil, params, nil, resp) return } // ---------------------------------------------------------------------------- // Domain-level operations. // --- CreateDomain // CreateDomain creates a new domain. // // See http://goo.gl/jDjGH for more details. func (domain *Domain) CreateDomain() (resp *SimpleResp, err error) { params := makeParams("CreateDomain") resp = &SimpleResp{} err = domain.SDB.query(domain, nil, params, nil, resp) return } // DeleteDomain deletes an existing domain. // // See http://goo.gl/S0dCL for more details. func (domain *Domain) DeleteDomain() (resp *SimpleResp, err error) { params := makeParams("DeleteDomain") resp = &SimpleResp{} err = domain.SDB.query(domain, nil, params, nil, resp) return } // ---------------------------------------------------------------------------- // Item-level operations. type PutAttrs struct { attrs []Attr expected []Attr replace map[string]bool missing map[string]bool } func (pa *PutAttrs) Add(name, value string) { pa.attrs = append(pa.attrs, Attr{name, value}) } func (pa *PutAttrs) Replace(name, value string) { pa.Add(name, value) if pa.replace == nil { pa.replace = make(map[string]bool) } pa.replace[name] = true } // The PutAttrs request will only succeed if the existing // item in SimpleDB contains a matching name / value pair. func (pa *PutAttrs) IfValue(name, value string) { pa.expected = append(pa.expected, Attr{name, value}) } // Flag to test the existence of an attribute while performing // conditional updates. X can be any positive integer or 0. // // This should set Expected.N.Name=name and Expected.N.Exists=false func (pa *PutAttrs) IfMissing(name string) { if pa.missing == nil { pa.missing = make(map[string]bool) } pa.missing[name] = true } // PutAttrs adds attrs to item. // // See http://goo.gl/yTAV4 for more details. func (item *Item) PutAttrs(attrs *PutAttrs) (resp *SimpleResp, err error) { params := makeParams("PutAttributes") resp = &SimpleResp{} // copy these attrs over to the parameters itemNum := 1 for _, attr := range attrs.attrs { itemNumStr := strconv.Itoa(itemNum) // do the name, value and replace params["Attribute."+itemNumStr+".Name"] = []string{attr.Name} params["Attribute."+itemNumStr+".Value"] = []string{attr.Value} if _, ok := attrs.replace[attr.Name]; ok { params["Attribute."+itemNumStr+".Replace"] = []string{"true"} } itemNum++ } //append expected values to params expectedNum := 1 for _, attr := range attrs.expected { expectedNumStr := strconv.Itoa(expectedNum) params["Expected."+expectedNumStr+".Name"] = []string{attr.Name} params["Expected."+expectedNumStr+".Value"] = []string{attr.Value} if attrs.missing[attr.Name] { params["Expected."+expectedNumStr+".Exists"] = []string{"false"} } expectedNum++ } err = item.query(params, nil, resp) if err != nil { return nil, err } return } // Response to an Attrs request. // // See http://goo.gl/45X1M for more details. type AttrsResp struct { Attrs []Attr `xml:"GetAttributesResult>Attribute"` ResponseMetadata ResponseMetadata } // Attrs returns one or more of the named attributes, or // all of item's attributes if names is nil. // If consistent is true, previous writes will necessarily // be observed. // // See http://goo.gl/45X1M for more details. func (item *Item) Attrs(names []string, consistent bool) (resp *AttrsResp, err error) { params := makeParams("GetAttributes") params["ItemName"] = []string{item.Name} if consistent { params["ConsistentRead"] = []string{"true"} } // Copy these attributes over to the parameters for i, name := range names { params["AttributeName."+strconv.Itoa(i+1)] = []string{name} } resp = &AttrsResp{} err = item.query(params, nil, resp) if err != nil { return nil, err } return } // ---------------------------------------------------------------------------- // Generic data structures for all requests/responses. // Error encapsulates an error returned by SDB. type Error struct { StatusCode int // HTTP status code (200, 403, ...) StatusMsg string // HTTP status message ("Service Unavailable", "Bad Request", ...) Code string // SimpleDB error code ("InvalidParameterValue", ...) Message string // The human-oriented error message RequestId string // A unique ID for this request BoxUsage float64 // The measure of machine utilization for this request. } func (err *Error) Error() string { return err.Message } // SimpleResp represents a response to an SDB request which on success // will return no other information besides ResponseMetadata. type SimpleResp struct { ResponseMetadata ResponseMetadata } // ResponseMetadata type ResponseMetadata struct { RequestId string // A unique ID for tracking the request BoxUsage float64 // The measure of machine utilization for this request. } func buildError(r *http.Response) error { err := Error{} err.StatusCode = r.StatusCode err.StatusMsg = r.Status xml.NewDecoder(r.Body).Decode(&err) return &err } // ---------------------------------------------------------------------------- // Request dispatching logic. func (item *Item) query(params url.Values, headers http.Header, resp interface{}) error { return item.Domain.SDB.query(item.Domain, item, params, headers, resp) } func (domain *Domain) query(item *Item, params url.Values, headers http.Header, resp interface{}) error { return domain.SDB.query(domain, item, params, headers, resp) } func (sdb *SDB) query(domain *Domain, item *Item, params url.Values, headers http.Header, resp interface{}) error { // all SimpleDB operations have path="/" method := "GET" path := "/" // if we have been given no headers or params, create them if headers == nil { headers = map[string][]string{} } if params == nil { params = map[string][]string{} } // setup some default parameters params["Version"] = []string{"2009-04-15"} params["Timestamp"] = []string{time.Now().UTC().Format(time.RFC3339)} // set the DomainName param (every request must have one) if domain != nil { params["DomainName"] = []string{domain.Name} } // set the ItemName if we have one if item != nil { params["ItemName"] = []string{item.Name} } // check the endpoint URL u, err := url.Parse(sdb.Region.SDBEndpoint) if err != nil { return err } headers["Host"] = []string{u.Host} sign(sdb.Auth, method, path, params, headers) u.Path = path if len(params) > 0 { u.RawQuery = params.Encode() } req := http.Request{ URL: u, Method: method, ProtoMajor: 1, ProtoMinor: 1, Close: true, Header: headers, } if v, ok := headers["Content-Length"]; ok { req.ContentLength, _ = strconv.ParseInt(v[0], 10, 64) delete(headers, "Content-Length") } r, err := http.DefaultClient.Do(&req) if err != nil { return err } defer r.Body.Close() if debug { dump, _ := httputil.DumpResponse(r, true) log.Printf("response:\n") log.Printf("%v\n}\n", string(dump)) } // status code is always 200 when successful (since we're always doing a GET) if r.StatusCode != 200 { return buildError(r) } // everything was fine, so unmarshal the XML and return what it's err is (if any) err = xml.NewDecoder(r.Body).Decode(resp) return err } func makeParams(action string) map[string][]string { params := make(map[string][]string) params["Action"] = []string{action} return params } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/exp/sdb/sign_test.go���������������������������������������0000644�0000153�0000161�00000001442�12321735726�023775� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package sdb_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/exp/sdb" . "launchpad.net/gocheck" ) // SimpleDB ReST authentication docs: http://goo.gl/CaY81 var testAuth = aws.Auth{"access-key-id-s8eBOWuU", "secret-access-key-UkQjTLd9"} func (s *S) TestSignExampleDomainCreate(c *C) { method := "GET" params := map[string][]string{ "Action": {"CreateDomain"}, "DomainName": {"MyDomain"}, "Timestamp": {"2011-08-20T07:23:57+12:00"}, "Version": {"2009-04-15"}, } headers := map[string][]string{ "Host": {"sdb.amazonaws.com"}, } sdb.Sign(testAuth, method, "", params, headers) expected := "ot2JaeeqMRJqgAqW67hkzUlffgxdOz4RykbrECB+tDU=" c.Assert(params["Signature"], DeepEquals, []string{expected}) } // Do a few test methods which takes combinations of params ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/��������������������������������������������������������0000755�0000153�0000161�00000000000�12321736016�020420� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/s3test/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735726�021654� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/s3test/server.go����������������������������������������0000644�0000153�0000161�00000037012�12321735726�023514� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package s3test import ( "bytes" "crypto/md5" "encoding/hex" "encoding/xml" "fmt" "io" "io/ioutil" "launchpad.net/goamz/s3" "log" "net" "net/http" "net/url" "regexp" "sort" "strconv" "strings" "sync" "time" ) const debug = false type s3Error struct { statusCode int XMLName struct{} `xml:"Error"` Code string Message string BucketName string RequestId string HostId string } type action struct { srv *Server w http.ResponseWriter req *http.Request reqId string } // Config controls the internal behaviour of the Server. A nil config is the default // and behaves as if all configurations assume their default behaviour. Once passed // to NewServer, the configuration must not be modified. type Config struct { // Send409Conflict controls how the Server will respond to calls to PUT on a // previously existing bucket. The default is false, and corresponds to the // us-east-1 s3 enpoint. Setting this value to true emulates the behaviour of // all other regions. // http://docs.amazonwebservices.com/AmazonS3/latest/API/ErrorResponses.html Send409Conflict bool } func (c *Config) send409Conflict() bool { if c != nil { return c.Send409Conflict } return false } // Server is a fake S3 server for testing purposes. // All of the data for the server is kept in memory. type Server struct { url string reqId int listener net.Listener mu sync.Mutex buckets map[string]*bucket config *Config } type bucket struct { name string acl s3.ACL ctime time.Time objects map[string]*object } type object struct { name string mtime time.Time meta http.Header // metadata to return with requests. checksum []byte // also held as Content-MD5 in meta. data []byte } // A resource encapsulates the subject of an HTTP request. // The resource referred to may or may not exist // when the request is made. type resource interface { put(a *action) interface{} get(a *action) interface{} post(a *action) interface{} delete(a *action) interface{} } func NewServer(config *Config) (*Server, error) { l, err := net.Listen("tcp", "localhost:0") if err != nil { return nil, fmt.Errorf("cannot listen on localhost: %v", err) } srv := &Server{ listener: l, url: "http://" + l.Addr().String(), buckets: make(map[string]*bucket), config: config, } go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { srv.serveHTTP(w, req) })) return srv, nil } // Quit closes down the server. func (srv *Server) Quit() { srv.listener.Close() } // URL returns a URL for the server. func (srv *Server) URL() string { return srv.url } func fatalf(code int, codeStr string, errf string, a ...interface{}) { panic(&s3Error{ statusCode: code, Code: codeStr, Message: fmt.Sprintf(errf, a...), }) } // serveHTTP serves the S3 protocol. func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) { // ignore error from ParseForm as it's usually spurious. req.ParseForm() srv.mu.Lock() defer srv.mu.Unlock() if debug { log.Printf("s3test %q %q", req.Method, req.URL) } a := &action{ srv: srv, w: w, req: req, reqId: fmt.Sprintf("%09X", srv.reqId), } srv.reqId++ var r resource defer func() { switch err := recover().(type) { case *s3Error: switch r := r.(type) { case objectResource: err.BucketName = r.bucket.name case bucketResource: err.BucketName = r.name } err.RequestId = a.reqId // TODO HostId w.Header().Set("Content-Type", `xml version="1.0" encoding="UTF-8"`) w.WriteHeader(err.statusCode) xmlMarshal(w, err) case nil: default: panic(err) } }() r = srv.resourceForURL(req.URL) var resp interface{} switch req.Method { case "PUT": resp = r.put(a) case "GET", "HEAD": resp = r.get(a) case "DELETE": resp = r.delete(a) case "POST": resp = r.post(a) default: fatalf(400, "MethodNotAllowed", "unknown http request method %q", req.Method) } if resp != nil && req.Method != "HEAD" { xmlMarshal(w, resp) } } // xmlMarshal is the same as xml.Marshal except that // it panics on error. The marshalling should not fail, // but we want to know if it does. func xmlMarshal(w io.Writer, x interface{}) { if err := xml.NewEncoder(w).Encode(x); err != nil { panic(fmt.Errorf("error marshalling %#v: %v", x, err)) } } // In a fully implemented test server, each of these would have // its own resource type. var unimplementedBucketResourceNames = map[string]bool{ "acl": true, "lifecycle": true, "policy": true, "location": true, "logging": true, "notification": true, "versions": true, "requestPayment": true, "versioning": true, "website": true, "uploads": true, } var unimplementedObjectResourceNames = map[string]bool{ "uploadId": true, "acl": true, "torrent": true, "uploads": true, } var pathRegexp = regexp.MustCompile("/(([^/]+)(/(.*))?)?") // resourceForURL returns a resource object for the given URL. func (srv *Server) resourceForURL(u *url.URL) (r resource) { m := pathRegexp.FindStringSubmatch(u.Path) if m == nil { fatalf(404, "InvalidURI", "Couldn't parse the specified URI") } bucketName := m[2] objectName := m[4] if bucketName == "" { return nullResource{} // root } b := bucketResource{ name: bucketName, bucket: srv.buckets[bucketName], } q := u.Query() if objectName == "" { for name := range q { if unimplementedBucketResourceNames[name] { return nullResource{} } } return b } if b.bucket == nil { fatalf(404, "NoSuchBucket", "The specified bucket does not exist") } objr := objectResource{ name: objectName, version: q.Get("versionId"), bucket: b.bucket, } for name := range q { if unimplementedObjectResourceNames[name] { return nullResource{} } } if obj := objr.bucket.objects[objr.name]; obj != nil { objr.object = obj } return objr } // nullResource has error stubs for all resource methods. type nullResource struct{} func notAllowed() interface{} { fatalf(400, "MethodNotAllowed", "The specified method is not allowed against this resource") return nil } func (nullResource) put(a *action) interface{} { return notAllowed() } func (nullResource) get(a *action) interface{} { return notAllowed() } func (nullResource) post(a *action) interface{} { return notAllowed() } func (nullResource) delete(a *action) interface{} { return notAllowed() } const timeFormat = "2006-01-02T15:04:05.000Z07:00" type bucketResource struct { name string bucket *bucket // non-nil if the bucket already exists. } // GET on a bucket lists the objects in the bucket. // http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGET.html func (r bucketResource) get(a *action) interface{} { if r.bucket == nil { fatalf(404, "NoSuchBucket", "The specified bucket does not exist") } delimiter := a.req.Form.Get("delimiter") marker := a.req.Form.Get("marker") maxKeys := -1 if s := a.req.Form.Get("max-keys"); s != "" { i, err := strconv.Atoi(s) if err != nil || i < 0 { fatalf(400, "invalid value for max-keys: %q", s) } maxKeys = i } prefix := a.req.Form.Get("prefix") a.w.Header().Set("Content-Type", "application/xml") if a.req.Method == "HEAD" { return nil } var objs orderedObjects // first get all matching objects and arrange them in alphabetical order. for name, obj := range r.bucket.objects { if strings.HasPrefix(name, prefix) { objs = append(objs, obj) } } sort.Sort(objs) if maxKeys <= 0 { maxKeys = 1000 } resp := &s3.ListResp{ Name: r.bucket.name, Prefix: prefix, Delimiter: delimiter, Marker: marker, MaxKeys: maxKeys, } var prefixes []string for _, obj := range objs { if !strings.HasPrefix(obj.name, prefix) { continue } name := obj.name isPrefix := false if delimiter != "" { if i := strings.Index(obj.name[len(prefix):], delimiter); i >= 0 { name = obj.name[:len(prefix)+i+len(delimiter)] if prefixes != nil && prefixes[len(prefixes)-1] == name { continue } isPrefix = true } } if name <= marker { continue } if len(resp.Contents)+len(prefixes) >= maxKeys { resp.IsTruncated = true break } if isPrefix { prefixes = append(prefixes, name) } else { // Contents contains only keys not found in CommonPrefixes resp.Contents = append(resp.Contents, obj.s3Key()) } } resp.CommonPrefixes = prefixes return resp } // orderedObjects holds a slice of objects that can be sorted // by name. type orderedObjects []*object func (s orderedObjects) Len() int { return len(s) } func (s orderedObjects) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s orderedObjects) Less(i, j int) bool { return s[i].name < s[j].name } func (obj *object) s3Key() s3.Key { return s3.Key{ Key: obj.name, LastModified: obj.mtime.Format(timeFormat), Size: int64(len(obj.data)), ETag: fmt.Sprintf(`"%x"`, obj.checksum), // TODO StorageClass // TODO Owner } } // DELETE on a bucket deletes the bucket if it's not empty. func (r bucketResource) delete(a *action) interface{} { b := r.bucket if b == nil { fatalf(404, "NoSuchBucket", "The specified bucket does not exist") } if len(b.objects) > 0 { fatalf(400, "BucketNotEmpty", "The bucket you tried to delete is not empty") } delete(a.srv.buckets, b.name) return nil } // PUT on a bucket creates the bucket. // http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUT.html func (r bucketResource) put(a *action) interface{} { var created bool if r.bucket == nil { if !validBucketName(r.name) { fatalf(400, "InvalidBucketName", "The specified bucket is not valid") } if loc := locationConstraint(a); loc == "" { fatalf(400, "InvalidRequets", "The unspecified location constraint is incompatible for the region specific endpoint this request was sent to.") } // TODO validate acl r.bucket = &bucket{ name: r.name, // TODO default acl objects: make(map[string]*object), } a.srv.buckets[r.name] = r.bucket created = true } if !created && a.srv.config.send409Conflict() { fatalf(409, "BucketAlreadyOwnedByYou", "Your previous request to create the named bucket succeeded and you already own it.") } r.bucket.acl = s3.ACL(a.req.Header.Get("x-amz-acl")) return nil } func (bucketResource) post(a *action) interface{} { fatalf(400, "Method", "bucket POST method not available") return nil } // validBucketName returns whether name is a valid bucket name. // Here are the rules, from: // http://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev/BucketRestrictions.html // // Can contain lowercase letters, numbers, periods (.), underscores (_), // and dashes (-). You can use uppercase letters for buckets only in the // US Standard region. // // Must start with a number or letter // // Must be between 3 and 255 characters long // // There's one extra rule (Must not be formatted as an IP address (e.g., 192.168.5.4) // but the real S3 server does not seem to check that rule, so we will not // check it either. // func validBucketName(name string) bool { if len(name) < 3 || len(name) > 255 { return false } r := name[0] if !(r >= '0' && r <= '9' || r >= 'a' && r <= 'z') { return false } for _, r := range name { switch { case r >= '0' && r <= '9': case r >= 'a' && r <= 'z': case r == '_' || r == '-': case r == '.': default: return false } } return true } var responseParams = map[string]bool{ "content-type": true, "content-language": true, "expires": true, "cache-control": true, "content-disposition": true, "content-encoding": true, } type objectResource struct { name string version string bucket *bucket // always non-nil. object *object // may be nil. } // GET on an object gets the contents of the object. // http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html func (objr objectResource) get(a *action) interface{} { obj := objr.object if obj == nil { fatalf(404, "NoSuchKey", "The specified key does not exist.") } h := a.w.Header() // add metadata for name, d := range obj.meta { h[name] = d } // override header values in response to request parameters. for name, vals := range a.req.Form { if strings.HasPrefix(name, "response-") { name = name[len("response-"):] if !responseParams[name] { continue } h.Set(name, vals[0]) } } if r := a.req.Header.Get("Range"); r != "" { fatalf(400, "NotImplemented", "range unimplemented") } // TODO Last-Modified-Since // TODO If-Modified-Since // TODO If-Unmodified-Since // TODO If-Match // TODO If-None-Match // TODO Connection: close ?? // TODO x-amz-request-id h.Set("Content-Length", fmt.Sprint(len(obj.data))) h.Set("ETag", hex.EncodeToString(obj.checksum)) h.Set("Last-Modified", obj.mtime.Format(time.RFC1123)) if a.req.Method == "HEAD" { return nil } // TODO avoid holding the lock when writing data. _, err := a.w.Write(obj.data) if err != nil { // we can't do much except just log the fact. log.Printf("error writing data: %v", err) } return nil } var metaHeaders = map[string]bool{ "Content-MD5": true, "x-amz-acl": true, "Content-Type": true, "Content-Encoding": true, "Content-Disposition": true, } // PUT on an object creates the object. func (objr objectResource) put(a *action) interface{} { // TODO Cache-Control header // TODO Expires header // TODO x-amz-server-side-encryption // TODO x-amz-storage-class // TODO is this correct, or should we erase all previous metadata? obj := objr.object if obj == nil { obj = &object{ name: objr.name, meta: make(http.Header), } } var expectHash []byte if c := a.req.Header.Get("Content-MD5"); c != "" { var err error expectHash, err = hex.DecodeString(c) if err != nil || len(expectHash) != md5.Size { fatalf(400, "InvalidDigest", "The Content-MD5 you specified was invalid") } } sum := md5.New() // TODO avoid holding lock while reading data. data, err := ioutil.ReadAll(io.TeeReader(a.req.Body, sum)) if err != nil { fatalf(400, "TODO", "read error") } gotHash := sum.Sum(nil) if expectHash != nil && bytes.Compare(gotHash, expectHash) != 0 { fatalf(400, "BadDigest", "The Content-MD5 you specified did not match what we received") } if a.req.ContentLength >= 0 && int64(len(data)) != a.req.ContentLength { fatalf(400, "IncompleteBody", "You did not provide the number of bytes specified by the Content-Length HTTP header") } // PUT request has been successful - save data and metadata for key, values := range a.req.Header { key = http.CanonicalHeaderKey(key) if metaHeaders[key] || strings.HasPrefix(key, "X-Amz-Meta-") { obj.meta[key] = values } } obj.data = data obj.checksum = gotHash obj.mtime = time.Now() objr.bucket.objects[objr.name] = obj return nil } func (objr objectResource) delete(a *action) interface{} { delete(objr.bucket.objects, objr.name) return nil } func (objr objectResource) post(a *action) interface{} { fatalf(400, "MethodNotAllowed", "The specified method is not allowed against this resource") return nil } type CreateBucketConfiguration struct { LocationConstraint string } // locationConstraint parses the <CreateBucketConfiguration /> request body (if present). // If there is no body, an empty string will be returned. func locationConstraint(a *action) string { var body bytes.Buffer if _, err := io.Copy(&body, a.req.Body); err != nil { fatalf(400, "InvalidRequest", err.Error()) } if body.Len() == 0 { return "" } var loc CreateBucketConfiguration if err := xml.NewDecoder(&body).Decode(&loc); err != nil { fatalf(400, "InvalidRequest", err.Error()) } return loc.LocationConstraint } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/export_test.go������������������������������������������0000644�0000153�0000161�00000000774�12321735726�023346� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package s3 import ( "launchpad.net/goamz/aws" ) var originalStrategy = attempts func SetAttemptStrategy(s *aws.AttemptStrategy) { if s == nil { attempts = originalStrategy } else { attempts = *s } } func AttemptStrategy() aws.AttemptStrategy { return attempts } func Sign(auth aws.Auth, method, path string, params, headers map[string][]string) { sign(auth, method, path, params, headers) } func SetListPartsMax(n int) { listPartsMax = n } func SetListMultiMax(n int) { listMultiMax = n } ����juju-core_1.18.1/src/launchpad.net/goamz/s3/s3t_test.go���������������������������������������������0000644�0000153�0000161�00000003434�12321735726�022532� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package s3_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/s3" "launchpad.net/goamz/s3/s3test" . "launchpad.net/gocheck" ) type LocalServer struct { auth aws.Auth region aws.Region srv *s3test.Server config *s3test.Config } func (s *LocalServer) SetUp(c *C) { srv, err := s3test.NewServer(s.config) c.Assert(err, IsNil) c.Assert(srv, NotNil) s.srv = srv s.region = aws.Region{ Name: "faux-region-1", S3Endpoint: srv.URL(), S3LocationConstraint: true, // s3test server requires a LocationConstraint } } // LocalServerSuite defines tests that will run // against the local s3test server. It includes // selected tests from ClientTests; // when the s3test functionality is sufficient, it should // include all of them, and ClientTests can be simply embedded. type LocalServerSuite struct { srv LocalServer clientTests ClientTests } var ( // run tests twice, once in us-east-1 mode, once not. _ = Suite(&LocalServerSuite{}) _ = Suite(&LocalServerSuite{ srv: LocalServer{ config: &s3test.Config{ Send409Conflict: true, }, }, }) ) func (s *LocalServerSuite) SetUpSuite(c *C) { s.srv.SetUp(c) s.clientTests.s3 = s3.New(s.srv.auth, s.srv.region) // TODO Sadly the fake server ignores auth completely right now. :-( s.clientTests.authIsBroken = true s.clientTests.Cleanup() } func (s *LocalServerSuite) TearDownTest(c *C) { s.clientTests.Cleanup() } func (s *LocalServerSuite) TestBasicFunctionality(c *C) { s.clientTests.TestBasicFunctionality(c) } func (s *LocalServerSuite) TestGetNotFound(c *C) { s.clientTests.TestGetNotFound(c) } func (s *LocalServerSuite) TestBucketList(c *C) { s.clientTests.TestBucketList(c) } func (s *LocalServerSuite) TestDoublePutBucket(c *C) { s.clientTests.TestDoublePutBucket(c) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/responses_test.go���������������������������������������0000644�0000153�0000161�00000013454�12321735726�024045� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package s3_test var GetObjectErrorDump = ` <?xml version="1.0" encoding="UTF-8"?> <Error><Code>NoSuchBucket</Code><Message>The specified bucket does not exist</Message> <BucketName>non-existent-bucket</BucketName><RequestId>3F1B667FAD71C3D8</RequestId> <HostId>L4ee/zrm1irFXY5F45fKXIRdOf9ktsKY/8TDVawuMK2jWRb1RF84i1uBzkdNqS5D</HostId></Error> ` var GetListResultDump1 = ` <?xml version="1.0" encoding="UTF-8"?> <ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01"> <Name>quotes</Name> <Prefix>N</Prefix> <IsTruncated>false</IsTruncated> <Contents> <Key>Nelson</Key> <LastModified>2006-01-01T12:00:00.000Z</LastModified> <ETag>&quot;828ef3fdfa96f00ad9f27c383fc9ac7f&quot;</ETag> <Size>5</Size> <StorageClass>STANDARD</StorageClass> <Owner> <ID>bcaf161ca5fb16fd081034f</ID> <DisplayName>webfile</DisplayName> </Owner> </Contents> <Contents> <Key>Neo</Key> <LastModified>2006-01-01T12:00:00.000Z</LastModified> <ETag>&quot;828ef3fdfa96f00ad9f27c383fc9ac7f&quot;</ETag> <Size>4</Size> <StorageClass>STANDARD</StorageClass> <Owner> <ID>bcaf1ffd86a5fb16fd081034f</ID> <DisplayName>webfile</DisplayName> </Owner> </Contents> </ListBucketResult> ` var GetListResultDump2 = ` <ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <Name>example-bucket</Name> <Prefix>photos/2006/</Prefix> <Marker>some-marker</Marker> <MaxKeys>1000</MaxKeys> <Delimiter>/</Delimiter> <IsTruncated>false</IsTruncated> <CommonPrefixes> <Prefix>photos/2006/feb/</Prefix> </CommonPrefixes> <CommonPrefixes> <Prefix>photos/2006/jan/</Prefix> </CommonPrefixes> </ListBucketResult> ` var InitMultiResultDump = ` <?xml version="1.0" encoding="UTF-8"?> <InitiateMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <Bucket>sample</Bucket> <Key>multi</Key> <UploadId>JNbR_cMdwnGiD12jKAd6WK2PUkfj2VxA7i4nCwjE6t71nI9Tl3eVDPFlU0nOixhftH7I17ZPGkV3QA.l7ZD.QQ--</UploadId> </InitiateMultipartUploadResult> ` var ListPartsResultDump1 = ` <?xml version="1.0" encoding="UTF-8"?> <ListPartsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <Bucket>sample</Bucket> <Key>multi</Key> <UploadId>JNbR_cMdwnGiD12jKAd6WK2PUkfj2VxA7i4nCwjE6t71nI9Tl3eVDPFlU0nOixhftH7I17ZPGkV3QA.l7ZD.QQ--</UploadId> <Initiator> <ID>bb5c0f63b0b25f2d099c</ID> <DisplayName>joe</DisplayName> </Initiator> <Owner> <ID>bb5c0f63b0b25f2d099c</ID> <DisplayName>joe</DisplayName> </Owner> <StorageClass>STANDARD</StorageClass> <PartNumberMarker>0</PartNumberMarker> <NextPartNumberMarker>2</NextPartNumberMarker> <MaxParts>2</MaxParts> <IsTruncated>true</IsTruncated> <Part> <PartNumber>1</PartNumber> <LastModified>2013-01-30T13:45:51.000Z</LastModified> <ETag>&quot;ffc88b4ca90a355f8ddba6b2c3b2af5c&quot;</ETag> <Size>5</Size> </Part> <Part> <PartNumber>2</PartNumber> <LastModified>2013-01-30T13:45:52.000Z</LastModified> <ETag>&quot;d067a0fa9dc61a6e7195ca99696b5a89&quot;</ETag> <Size>5</Size> </Part> </ListPartsResult> ` var ListPartsResultDump2 = ` <?xml version="1.0" encoding="UTF-8"?> <ListPartsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <Bucket>sample</Bucket> <Key>multi</Key> <UploadId>JNbR_cMdwnGiD12jKAd6WK2PUkfj2VxA7i4nCwjE6t71nI9Tl3eVDPFlU0nOixhftH7I17ZPGkV3QA.l7ZD.QQ--</UploadId> <Initiator> <ID>bb5c0f63b0b25f2d099c</ID> <DisplayName>joe</DisplayName> </Initiator> <Owner> <ID>bb5c0f63b0b25f2d099c</ID> <DisplayName>joe</DisplayName> </Owner> <StorageClass>STANDARD</StorageClass> <PartNumberMarker>2</PartNumberMarker> <NextPartNumberMarker>3</NextPartNumberMarker> <MaxParts>2</MaxParts> <IsTruncated>false</IsTruncated> <Part> <PartNumber>3</PartNumber> <LastModified>2013-01-30T13:46:50.000Z</LastModified> <ETag>&quot;49dcd91231f801159e893fb5c6674985&quot;</ETag> <Size>5</Size> </Part> </ListPartsResult> ` var ListMultiResultDump = ` <?xml version="1.0"?> <ListMultipartUploadsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <Bucket>goamz-test-bucket-us-east-1-akiajk3wyewhctyqbf7a</Bucket> <KeyMarker/> <UploadIdMarker/> <NextKeyMarker>multi1</NextKeyMarker> <NextUploadIdMarker>iUVug89pPvSswrikD72p8uO62EzhNtpDxRmwC5WSiWDdK9SfzmDqe3xpP1kMWimyimSnz4uzFc3waVM5ufrKYQ--</NextUploadIdMarker> <Delimiter>/</Delimiter> <MaxUploads>1000</MaxUploads> <IsTruncated>false</IsTruncated> <Upload> <Key>multi1</Key> <UploadId>iUVug89pPvSswrikD</UploadId> <Initiator> <ID>bb5c0f63b0b25f2d0</ID> <DisplayName>gustavoniemeyer</DisplayName> </Initiator> <Owner> <ID>bb5c0f63b0b25f2d0</ID> <DisplayName>gustavoniemeyer</DisplayName> </Owner> <StorageClass>STANDARD</StorageClass> <Initiated>2013-01-30T18:15:47.000Z</Initiated> </Upload> <Upload> <Key>multi2</Key> <UploadId>DkirwsSvPp98guVUi</UploadId> <Initiator> <ID>bb5c0f63b0b25f2d0</ID> <DisplayName>joe</DisplayName> </Initiator> <Owner> <ID>bb5c0f63b0b25f2d0</ID> <DisplayName>joe</DisplayName> </Owner> <StorageClass>STANDARD</StorageClass> <Initiated>2013-01-30T18:15:47.000Z</Initiated> </Upload> <CommonPrefixes> <Prefix>a/</Prefix> </CommonPrefixes> <CommonPrefixes> <Prefix>b/</Prefix> </CommonPrefixes> </ListMultipartUploadsResult> ` var NoSuchUploadErrorDump = ` <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>NoSuchUpload</Code> <Message>Not relevant</Message> <BucketName>sample</BucketName> <RequestId>3F1B667FAD71C3D8</RequestId> <HostId>kjhwqk</HostId> </Error> ` var InternalErrorDump = ` <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>InternalError</Code> <Message>Not relevant</Message> <BucketName>sample</BucketName> <RequestId>3F1B667FAD71C3D8</RequestId> <HostId>kjhwqk</HostId> </Error> ` ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/sign.go�������������������������������������������������0000644�0000153�0000161�00000005463�12321735726�021726� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package s3 import ( "crypto/hmac" "crypto/sha1" "encoding/base64" "launchpad.net/goamz/aws" "log" "sort" "strings" ) var b64 = base64.StdEncoding // ---------------------------------------------------------------------------- // S3 signing (http://goo.gl/G1LrK) var s3ParamsToSign = map[string]bool{ "acl": true, "location": true, "logging": true, "notification": true, "partNumber": true, "policy": true, "requestPayment": true, "torrent": true, "uploadId": true, "uploads": true, "versionId": true, "versioning": true, "versions": true, "response-content-type": true, "response-content-language": true, "response-expires": true, "response-cache-control": true, "response-content-disposition": true, "response-content-encoding": true, } func sign(auth aws.Auth, method, canonicalPath string, params, headers map[string][]string) { var md5, ctype, date, xamz string var xamzDate bool var sarray []string for k, v := range headers { k = strings.ToLower(k) switch k { case "content-md5": md5 = v[0] case "content-type": ctype = v[0] case "date": if !xamzDate { date = v[0] } default: if strings.HasPrefix(k, "x-amz-") { vall := strings.Join(v, ",") sarray = append(sarray, k+":"+vall) if k == "x-amz-date" { xamzDate = true date = "" } } } } if len(sarray) > 0 { sort.StringSlice(sarray).Sort() xamz = strings.Join(sarray, "\n") + "\n" } expires := false if v, ok := params["Expires"]; ok { // Query string request authentication alternative. expires = true date = v[0] params["AWSAccessKeyId"] = []string{auth.AccessKey} } sarray = sarray[0:0] for k, v := range params { if s3ParamsToSign[k] { for _, vi := range v { if vi == "" { sarray = append(sarray, k) } else { // "When signing you do not encode these values." sarray = append(sarray, k+"="+vi) } } } } if len(sarray) > 0 { sort.StringSlice(sarray).Sort() canonicalPath = canonicalPath + "?" + strings.Join(sarray, "&") } payload := method + "\n" + md5 + "\n" + ctype + "\n" + date + "\n" + xamz + canonicalPath hash := hmac.New(sha1.New, []byte(auth.SecretKey)) hash.Write([]byte(payload)) signature := make([]byte, b64.EncodedLen(hash.Size())) b64.Encode(signature, hash.Sum(nil)) if expires { params["Signature"] = []string{string(signature)} } else { headers["Authorization"] = []string{"AWS " + auth.AccessKey + ":" + string(signature)} } if debug { log.Printf("Signature payload: %q", payload) log.Printf("Signature: %q", signature) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/multi.go������������������������������������������������0000644�0000153�0000161�00000025561�12321735726�022121� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package s3 import ( "bytes" "crypto/md5" "encoding/base64" "encoding/hex" "encoding/xml" "errors" "io" "sort" "strconv" ) // Multi represents an unfinished multipart upload. // // Multipart uploads allow sending big objects in smaller chunks. // After all parts have been sent, the upload must be explicitly // completed by calling Complete with the list of parts. // // See http://goo.gl/vJfTG for an overview of multipart uploads. type Multi struct { Bucket *Bucket Key string UploadId string } // That's the default. Here just for testing. var listMultiMax = 1000 type listMultiResp struct { NextKeyMarker string NextUploadIdMarker string IsTruncated bool Upload []Multi CommonPrefixes []string `xml:"CommonPrefixes>Prefix"` } // ListMulti returns the list of unfinished multipart uploads in b. // // The prefix parameter limits the response to keys that begin with the // specified prefix. You can use prefixes to separate a bucket into different // groupings of keys (to get the feeling of folders, for example). // // The delim parameter causes the response to group all of the keys that // share a common prefix up to the next delimiter in a single entry within // the CommonPrefixes field. You can use delimiters to separate a bucket // into different groupings of keys, similar to how folders would work. // // See http://goo.gl/ePioY for details. func (b *Bucket) ListMulti(prefix, delim string) (multis []*Multi, prefixes []string, err error) { params := map[string][]string{ "uploads": {""}, "max-uploads": {strconv.FormatInt(int64(listMultiMax), 10)}, "prefix": {prefix}, "delimiter": {delim}, } for attempt := attempts.Start(); attempt.Next(); { req := &request{ method: "GET", bucket: b.Name, params: params, } var resp listMultiResp err := b.S3.query(req, &resp) if shouldRetry(err) && attempt.HasNext() { continue } if err != nil { return nil, nil, err } for i := range resp.Upload { multi := &resp.Upload[i] multi.Bucket = b multis = append(multis, multi) } prefixes = append(prefixes, resp.CommonPrefixes...) if !resp.IsTruncated { return multis, prefixes, nil } params["key-marker"] = []string{resp.NextKeyMarker} params["upload-id-marker"] = []string{resp.NextUploadIdMarker} attempt = attempts.Start() // Last request worked. } panic("unreachable") } // Multi returns a multipart upload handler for the provided key // inside b. If a multipart upload exists for key, it is returned, // otherwise a new multipart upload is initiated with contType and perm. func (b *Bucket) Multi(key, contType string, perm ACL) (*Multi, error) { multis, _, err := b.ListMulti(key, "") if err != nil && !hasCode(err, "NoSuchUpload") { return nil, err } for _, m := range multis { if m.Key == key { return m, nil } } return b.InitMulti(key, contType, perm) } // InitMulti initializes a new multipart upload at the provided // key inside b and returns a value for manipulating it. // // See http://goo.gl/XP8kL for details. func (b *Bucket) InitMulti(key string, contType string, perm ACL) (*Multi, error) { headers := map[string][]string{ "Content-Type": {contType}, "Content-Length": {"0"}, "x-amz-acl": {string(perm)}, } params := map[string][]string{ "uploads": {""}, } req := &request{ method: "POST", bucket: b.Name, path: key, headers: headers, params: params, } var err error var resp struct { UploadId string `xml:"UploadId"` } for attempt := attempts.Start(); attempt.Next(); { err = b.S3.query(req, &resp) if !shouldRetry(err) { break } } if err != nil { return nil, err } return &Multi{Bucket: b, Key: key, UploadId: resp.UploadId}, nil } // PutPart sends part n of the multipart upload, reading all the content from r. // Each part, except for the last one, must be at least 5MB in size. // // See http://goo.gl/pqZer for details. func (m *Multi) PutPart(n int, r io.ReadSeeker) (Part, error) { partSize, _, md5b64, err := seekerInfo(r) if err != nil { return Part{}, err } return m.putPart(n, r, partSize, md5b64) } func (m *Multi) putPart(n int, r io.ReadSeeker, partSize int64, md5b64 string) (Part, error) { headers := map[string][]string{ "Content-Length": {strconv.FormatInt(partSize, 10)}, "Content-MD5": {md5b64}, } params := map[string][]string{ "uploadId": {m.UploadId}, "partNumber": {strconv.FormatInt(int64(n), 10)}, } for attempt := attempts.Start(); attempt.Next(); { _, err := r.Seek(0, 0) if err != nil { return Part{}, err } req := &request{ method: "PUT", bucket: m.Bucket.Name, path: m.Key, headers: headers, params: params, payload: r, } err = m.Bucket.S3.prepare(req) if err != nil { return Part{}, err } hresp, err := m.Bucket.S3.run(req) if shouldRetry(err) && attempt.HasNext() { continue } if err != nil { return Part{}, err } hresp.Body.Close() etag := hresp.Header.Get("ETag") if etag == "" { return Part{}, errors.New("part upload succeeded with no ETag") } return Part{n, etag, partSize}, nil } panic("unreachable") } func seekerInfo(r io.ReadSeeker) (size int64, md5hex string, md5b64 string, err error) { _, err = r.Seek(0, 0) if err != nil { return 0, "", "", err } digest := md5.New() size, err = io.Copy(digest, r) if err != nil { return 0, "", "", err } sum := digest.Sum(nil) md5hex = hex.EncodeToString(sum) md5b64 = base64.StdEncoding.EncodeToString(sum) return size, md5hex, md5b64, nil } type Part struct { N int `xml:"PartNumber"` ETag string Size int64 } type partSlice []Part func (s partSlice) Len() int { return len(s) } func (s partSlice) Less(i, j int) bool { return s[i].N < s[j].N } func (s partSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } type listPartsResp struct { NextPartNumberMarker string IsTruncated bool Part []Part } // That's the default. Here just for testing. var listPartsMax = 1000 // ListParts returns the list of previously uploaded parts in m, // ordered by part number. // // See http://goo.gl/ePioY for details. func (m *Multi) ListParts() ([]Part, error) { params := map[string][]string{ "uploadId": {m.UploadId}, "max-parts": {strconv.FormatInt(int64(listPartsMax), 10)}, } var parts partSlice for attempt := attempts.Start(); attempt.Next(); { req := &request{ method: "GET", bucket: m.Bucket.Name, path: m.Key, params: params, } var resp listPartsResp err := m.Bucket.S3.query(req, &resp) if shouldRetry(err) && attempt.HasNext() { continue } if err != nil { return nil, err } parts = append(parts, resp.Part...) if !resp.IsTruncated { sort.Sort(parts) return parts, nil } params["part-number-marker"] = []string{resp.NextPartNumberMarker} attempt = attempts.Start() // Last request worked. } panic("unreachable") } type ReaderAtSeeker interface { io.ReaderAt io.ReadSeeker } // PutAll sends all of r via a multipart upload with parts no larger // than partSize bytes, which must be set to at least 5MB. // Parts previously uploaded are either reused if their checksum // and size match the new part, or otherwise overwritten with the // new content. // PutAll returns all the parts of m (reused or not). func (m *Multi) PutAll(r ReaderAtSeeker, partSize int64) ([]Part, error) { old, err := m.ListParts() if err != nil && !hasCode(err, "NoSuchUpload") { return nil, err } reuse := 0 // Index of next old part to consider reusing. current := 1 // Part number of latest good part handled. totalSize, err := r.Seek(0, 2) if err != nil { return nil, err } first := true // Must send at least one empty part if the file is empty. var result []Part NextSection: for offset := int64(0); offset < totalSize || first; offset += partSize { first = false if offset+partSize > totalSize { partSize = totalSize - offset } section := io.NewSectionReader(r, offset, partSize) _, md5hex, md5b64, err := seekerInfo(section) if err != nil { return nil, err } for reuse < len(old) && old[reuse].N <= current { // Looks like this part was already sent. part := &old[reuse] etag := `"` + md5hex + `"` if part.N == current && part.Size == partSize && part.ETag == etag { // Checksum matches. Reuse the old part. result = append(result, *part) current++ continue NextSection } reuse++ } // Part wasn't found or doesn't match. Send it. part, err := m.putPart(current, section, partSize, md5b64) if err != nil { return nil, err } result = append(result, part) current++ } return result, nil } type completeUpload struct { XMLName xml.Name `xml:"CompleteMultipartUpload"` Parts completeParts `xml:"Part"` } type completePart struct { PartNumber int ETag string } type completeParts []completePart func (p completeParts) Len() int { return len(p) } func (p completeParts) Less(i, j int) bool { return p[i].PartNumber < p[j].PartNumber } func (p completeParts) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Complete assembles the given previously uploaded parts into the // final object. This operation may take several minutes. // // See http://goo.gl/2Z7Tw for details. func (m *Multi) Complete(parts []Part) error { params := map[string][]string{ "uploadId": {m.UploadId}, } c := completeUpload{} for _, p := range parts { c.Parts = append(c.Parts, completePart{p.N, p.ETag}) } sort.Sort(c.Parts) data, err := xml.Marshal(&c) if err != nil { return err } for attempt := attempts.Start(); attempt.Next(); { req := &request{ method: "POST", bucket: m.Bucket.Name, path: m.Key, params: params, payload: bytes.NewReader(data), } err := m.Bucket.S3.query(req, nil) if shouldRetry(err) && attempt.HasNext() { continue } return err } panic("unreachable") } // Abort deletes an unifinished multipart upload and any previously // uploaded parts for it. // // After a multipart upload is aborted, no additional parts can be // uploaded using it. However, if any part uploads are currently in // progress, those part uploads might or might not succeed. As a result, // it might be necessary to abort a given multipart upload multiple // times in order to completely free all storage consumed by all parts. // // NOTE: If the described scenario happens to you, please report back to // the goamz authors with details. In the future such retrying should be // handled internally, but it's not clear what happens precisely (Is an // error returned? Is the issue completely undetectable?). // // See http://goo.gl/dnyJw for details. func (m *Multi) Abort() error { params := map[string][]string{ "uploadId": {m.UploadId}, } for attempt := attempts.Start(); attempt.Next(); { req := &request{ method: "DELETE", bucket: m.Bucket.Name, path: m.Key, params: params, } err := m.Bucket.S3.query(req, nil) if shouldRetry(err) && attempt.HasNext() { continue } return err } panic("unreachable") } �����������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/s3_test.go����������������������������������������������0000644�0000153�0000161�00000020212�12321735726�022337� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package s3_test import ( "bytes" "io/ioutil" "net/http" "testing" "launchpad.net/goamz/aws" "launchpad.net/goamz/s3" "launchpad.net/goamz/testutil" . "launchpad.net/gocheck" "time" ) func Test(t *testing.T) { TestingT(t) } type S struct { s3 *s3.S3 } var _ = Suite(&S{}) var testServer = testutil.NewHTTPServer() func (s *S) SetUpSuite(c *C) { testServer.Start() auth := aws.Auth{"abc", "123"} s.s3 = s3.New(auth, aws.Region{Name: "faux-region-1", S3Endpoint: testServer.URL}) } func (s *S) TearDownSuite(c *C) { s3.SetAttemptStrategy(nil) testServer.Stop() } func (s *S) SetUpTest(c *C) { attempts := aws.AttemptStrategy{ Total: 300 * time.Millisecond, Delay: 100 * time.Millisecond, } s3.SetAttemptStrategy(&attempts) } func (s *S) TearDownTest(c *C) { testServer.Flush() } // PutBucket docs: http://goo.gl/kBTCu func (s *S) TestPutBucket(c *C) { testServer.Response(200, nil, "") b := s.s3.Bucket("bucket") err := b.PutBucket(s3.Private) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "PUT") c.Assert(req.URL.Path, Equals, "/bucket/") c.Assert(req.Header["Date"], Not(Equals), "") } // DeleteBucket docs: http://goo.gl/GoBrY func (s *S) TestDelBucket(c *C) { testServer.Response(204, nil, "") b := s.s3.Bucket("bucket") err := b.DelBucket() c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "DELETE") c.Assert(req.URL.Path, Equals, "/bucket/") c.Assert(req.Header["Date"], Not(Equals), "") } // GetObject docs: http://goo.gl/isCO7 func (s *S) TestGet(c *C) { testServer.Response(200, nil, "content") b := s.s3.Bucket("bucket") data, err := b.Get("name") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/bucket/name") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(err, IsNil) c.Assert(string(data), Equals, "content") } func (s *S) TestURL(c *C) { testServer.Response(200, nil, "content") b := s.s3.Bucket("bucket") url := b.URL("name") r, err := http.Get(url) c.Assert(err, IsNil) data, err := ioutil.ReadAll(r.Body) r.Body.Close() c.Assert(err, IsNil) c.Assert(string(data), Equals, "content") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/bucket/name") } func (s *S) TestGetReader(c *C) { testServer.Response(200, nil, "content") b := s.s3.Bucket("bucket") rc, err := b.GetReader("name") c.Assert(err, IsNil) data, err := ioutil.ReadAll(rc) rc.Close() c.Assert(err, IsNil) c.Assert(string(data), Equals, "content") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/bucket/name") c.Assert(req.Header["Date"], Not(Equals), "") } func (s *S) TestGetNotFound(c *C) { for i := 0; i < 10; i++ { testServer.Response(404, nil, GetObjectErrorDump) } b := s.s3.Bucket("non-existent-bucket") data, err := b.Get("non-existent") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/non-existent-bucket/non-existent") c.Assert(req.Header["Date"], Not(Equals), "") s3err, _ := err.(*s3.Error) c.Assert(s3err, NotNil) c.Assert(s3err.StatusCode, Equals, 404) c.Assert(s3err.BucketName, Equals, "non-existent-bucket") c.Assert(s3err.RequestId, Equals, "3F1B667FAD71C3D8") c.Assert(s3err.HostId, Equals, "L4ee/zrm1irFXY5F45fKXIRdOf9ktsKY/8TDVawuMK2jWRb1RF84i1uBzkdNqS5D") c.Assert(s3err.Code, Equals, "NoSuchBucket") c.Assert(s3err.Message, Equals, "The specified bucket does not exist") c.Assert(s3err.Error(), Equals, "The specified bucket does not exist") c.Assert(data, IsNil) } // PutObject docs: http://goo.gl/FEBPD func (s *S) TestPutObject(c *C) { testServer.Response(200, nil, "") b := s.s3.Bucket("bucket") err := b.Put("name", []byte("content"), "content-type", s3.Private) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "PUT") c.Assert(req.URL.Path, Equals, "/bucket/name") c.Assert(req.Header["Date"], Not(DeepEquals), []string{""}) c.Assert(req.Header["Content-Type"], DeepEquals, []string{"content-type"}) c.Assert(req.Header["Content-Length"], DeepEquals, []string{"7"}) //c.Assert(req.Header["Content-MD5"], DeepEquals, "...") c.Assert(req.Header["X-Amz-Acl"], DeepEquals, []string{"private"}) } func (s *S) TestPutReader(c *C) { testServer.Response(200, nil, "") b := s.s3.Bucket("bucket") buf := bytes.NewBufferString("content") err := b.PutReader("name", buf, int64(buf.Len()), "content-type", s3.Private) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "PUT") c.Assert(req.URL.Path, Equals, "/bucket/name") c.Assert(req.Header["Date"], Not(DeepEquals), []string{""}) c.Assert(req.Header["Content-Type"], DeepEquals, []string{"content-type"}) c.Assert(req.Header["Content-Length"], DeepEquals, []string{"7"}) //c.Assert(req.Header["Content-MD5"], Equals, "...") c.Assert(req.Header["X-Amz-Acl"], DeepEquals, []string{"private"}) } // DelObject docs: http://goo.gl/APeTt func (s *S) TestDelObject(c *C) { testServer.Response(200, nil, "") b := s.s3.Bucket("bucket") err := b.Del("name") c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "DELETE") c.Assert(req.URL.Path, Equals, "/bucket/name") c.Assert(req.Header["Date"], Not(Equals), "") } // Bucket List Objects docs: http://goo.gl/YjQTc func (s *S) TestList(c *C) { testServer.Response(200, nil, GetListResultDump1) b := s.s3.Bucket("quotes") data, err := b.List("N", "", "", 0) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/quotes/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(req.Form["prefix"], DeepEquals, []string{"N"}) c.Assert(req.Form["delimiter"], DeepEquals, []string{""}) c.Assert(req.Form["marker"], DeepEquals, []string{""}) c.Assert(req.Form["max-keys"], DeepEquals, []string(nil)) c.Assert(data.Name, Equals, "quotes") c.Assert(data.Prefix, Equals, "N") c.Assert(data.IsTruncated, Equals, false) c.Assert(len(data.Contents), Equals, 2) c.Assert(data.Contents[0].Key, Equals, "Nelson") c.Assert(data.Contents[0].LastModified, Equals, "2006-01-01T12:00:00.000Z") c.Assert(data.Contents[0].ETag, Equals, `"828ef3fdfa96f00ad9f27c383fc9ac7f"`) c.Assert(data.Contents[0].Size, Equals, int64(5)) c.Assert(data.Contents[0].StorageClass, Equals, "STANDARD") c.Assert(data.Contents[0].Owner.ID, Equals, "bcaf161ca5fb16fd081034f") c.Assert(data.Contents[0].Owner.DisplayName, Equals, "webfile") c.Assert(data.Contents[1].Key, Equals, "Neo") c.Assert(data.Contents[1].LastModified, Equals, "2006-01-01T12:00:00.000Z") c.Assert(data.Contents[1].ETag, Equals, `"828ef3fdfa96f00ad9f27c383fc9ac7f"`) c.Assert(data.Contents[1].Size, Equals, int64(4)) c.Assert(data.Contents[1].StorageClass, Equals, "STANDARD") c.Assert(data.Contents[1].Owner.ID, Equals, "bcaf1ffd86a5fb16fd081034f") c.Assert(data.Contents[1].Owner.DisplayName, Equals, "webfile") } func (s *S) TestListWithDelimiter(c *C) { testServer.Response(200, nil, GetListResultDump2) b := s.s3.Bucket("quotes") data, err := b.List("photos/2006/", "/", "some-marker", 1000) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/quotes/") c.Assert(req.Header["Date"], Not(Equals), "") c.Assert(req.Form["prefix"], DeepEquals, []string{"photos/2006/"}) c.Assert(req.Form["delimiter"], DeepEquals, []string{"/"}) c.Assert(req.Form["marker"], DeepEquals, []string{"some-marker"}) c.Assert(req.Form["max-keys"], DeepEquals, []string{"1000"}) c.Assert(data.Name, Equals, "example-bucket") c.Assert(data.Prefix, Equals, "photos/2006/") c.Assert(data.Delimiter, Equals, "/") c.Assert(data.Marker, Equals, "some-marker") c.Assert(data.IsTruncated, Equals, false) c.Assert(len(data.Contents), Equals, 0) c.Assert(data.CommonPrefixes, DeepEquals, []string{"photos/2006/feb/", "photos/2006/jan/"}) } func (s *S) TestRetryAttempts(c *C) { s3.SetAttemptStrategy(nil) orig := s3.AttemptStrategy() s3.RetryAttempts(false) c.Assert(s3.AttemptStrategy(), Equals, aws.AttemptStrategy{}) s3.RetryAttempts(true) c.Assert(s3.AttemptStrategy(), Equals, orig) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/s3.go���������������������������������������������������0000644�0000153�0000161�00000033074�12321736016�021303� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // goamz - Go packages to interact with the Amazon Web Services. // // https://wiki.ubuntu.com/goamz // // Copyright (c) 2011 Canonical Ltd. // // Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com> // package s3 import ( "bytes" "encoding/xml" "fmt" "io" "io/ioutil" "launchpad.net/goamz/aws" "log" "net" "net/http" "net/http/httputil" "net/url" "strconv" "strings" "time" ) const debug = false // The S3 type encapsulates operations with an S3 region. type S3 struct { aws.Auth aws.Region private byte // Reserve the right of using private data. } // The Bucket type encapsulates operations with an S3 bucket. type Bucket struct { *S3 Name string } // The Owner type represents the owner of the object in an S3 bucket. type Owner struct { ID string DisplayName string } var ( attempts = defaultAttempts defaultAttempts = aws.AttemptStrategy{ Min: 5, Total: 5 * time.Second, Delay: 200 * time.Millisecond, } ) // RetryAttempts sets whether failing S3 requests may be retried to cope // with eventual consistency or temporary failures. It should not be // called while operations are in progress. func RetryAttempts(retry bool) { if retry { attempts = defaultAttempts } else { attempts = aws.AttemptStrategy{} } } // New creates a new S3. func New(auth aws.Auth, region aws.Region) *S3 { return &S3{auth, region, 0} } // Bucket returns a Bucket with the given name. func (s3 *S3) Bucket(name string) *Bucket { if s3.Region.S3BucketEndpoint != "" || s3.Region.S3LowercaseBucket { name = strings.ToLower(name) } return &Bucket{s3, name} } var createBucketConfiguration = `<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <LocationConstraint>%s</LocationConstraint> </CreateBucketConfiguration>` // locationConstraint returns an io.Reader specifying a LocationConstraint if // required for the region. // // See http://goo.gl/bh9Kq for details. func (s3 *S3) locationConstraint() io.Reader { constraint := "" if s3.Region.S3LocationConstraint { constraint = fmt.Sprintf(createBucketConfiguration, s3.Region.Name) } return strings.NewReader(constraint) } type ACL string const ( Private = ACL("private") PublicRead = ACL("public-read") PublicReadWrite = ACL("public-read-write") AuthenticatedRead = ACL("authenticated-read") BucketOwnerRead = ACL("bucket-owner-read") BucketOwnerFull = ACL("bucket-owner-full-control") ) // PutBucket creates a new bucket. // // See http://goo.gl/ndjnR for details. func (b *Bucket) PutBucket(perm ACL) error { headers := map[string][]string{ "x-amz-acl": {string(perm)}, } req := &request{ method: "PUT", bucket: b.Name, path: "/", headers: headers, payload: b.locationConstraint(), } return b.S3.query(req, nil) } // DelBucket removes an existing S3 bucket. All objects in the bucket must // be removed before the bucket itself can be removed. // // See http://goo.gl/GoBrY for details. func (b *Bucket) DelBucket() (err error) { req := &request{ method: "DELETE", bucket: b.Name, path: "/", } for attempt := attempts.Start(); attempt.Next(); { err = b.S3.query(req, nil) if !shouldRetry(err) { break } } return err } // Get retrieves an object from an S3 bucket. // // See http://goo.gl/isCO7 for details. func (b *Bucket) Get(path string) (data []byte, err error) { body, err := b.GetReader(path) if err != nil { return nil, err } data, err = ioutil.ReadAll(body) body.Close() return data, err } // GetReader retrieves an object from an S3 bucket. // It is the caller's responsibility to call Close on rc when // finished reading. func (b *Bucket) GetReader(path string) (rc io.ReadCloser, err error) { req := &request{ bucket: b.Name, path: path, } err = b.S3.prepare(req) if err != nil { return nil, err } for attempt := attempts.Start(); attempt.Next(); { hresp, err := b.S3.run(req) if shouldRetry(err) && attempt.HasNext() { continue } if err != nil { return nil, err } return hresp.Body, nil } panic("unreachable") } // Put inserts an object into the S3 bucket. // // See http://goo.gl/FEBPD for details. func (b *Bucket) Put(path string, data []byte, contType string, perm ACL) error { body := bytes.NewBuffer(data) return b.PutReader(path, body, int64(len(data)), contType, perm) } // PutReader inserts an object into the S3 bucket by consuming data // from r until EOF. func (b *Bucket) PutReader(path string, r io.Reader, length int64, contType string, perm ACL) error { headers := map[string][]string{ "Content-Length": {strconv.FormatInt(length, 10)}, "Content-Type": {contType}, "x-amz-acl": {string(perm)}, } req := &request{ method: "PUT", bucket: b.Name, path: path, headers: headers, payload: r, } return b.S3.query(req, nil) } // Del removes an object from the S3 bucket. // // See http://goo.gl/APeTt for details. func (b *Bucket) Del(path string) error { req := &request{ method: "DELETE", bucket: b.Name, path: path, } return b.S3.query(req, nil) } // The ListResp type holds the results of a List bucket operation. type ListResp struct { Name string Prefix string Delimiter string Marker string MaxKeys int // IsTruncated is true if the results have been truncated because // there are more keys and prefixes than can fit in MaxKeys. // N.B. this is the opposite sense to that documented (incorrectly) in // http://goo.gl/YjQTc IsTruncated bool Contents []Key CommonPrefixes []string `xml:">Prefix"` } // The Key type represents an item stored in an S3 bucket. type Key struct { Key string LastModified string Size int64 // ETag gives the hex-encoded MD5 sum of the contents, // surrounded with double-quotes. ETag string StorageClass string Owner Owner } // List returns information about objects in an S3 bucket. // // The prefix parameter limits the response to keys that begin with the // specified prefix. // // The delim parameter causes the response to group all of the keys that // share a common prefix up to the next delimiter in a single entry within // the CommonPrefixes field. You can use delimiters to separate a bucket // into different groupings of keys, similar to how folders would work. // // The marker parameter specifies the key to start with when listing objects // in a bucket. Amazon S3 lists objects in alphabetical order and // will return keys alphabetically greater than the marker. // // The max parameter specifies how many keys + common prefixes to return in // the response. The default is 1000. // // For example, given these keys in a bucket: // // index.html // index2.html // photos/2006/January/sample.jpg // photos/2006/February/sample2.jpg // photos/2006/February/sample3.jpg // photos/2006/February/sample4.jpg // // Listing this bucket with delimiter set to "/" would yield the // following result: // // &ListResp{ // Name: "sample-bucket", // MaxKeys: 1000, // Delimiter: "/", // Contents: []Key{ // {Key: "index.html", "index2.html"}, // }, // CommonPrefixes: []string{ // "photos/", // }, // } // // Listing the same bucket with delimiter set to "/" and prefix set to // "photos/2006/" would yield the following result: // // &ListResp{ // Name: "sample-bucket", // MaxKeys: 1000, // Delimiter: "/", // Prefix: "photos/2006/", // CommonPrefixes: []string{ // "photos/2006/February/", // "photos/2006/January/", // }, // } // // See http://goo.gl/YjQTc for details. func (b *Bucket) List(prefix, delim, marker string, max int) (result *ListResp, err error) { params := map[string][]string{ "prefix": {prefix}, "delimiter": {delim}, "marker": {marker}, } if max != 0 { params["max-keys"] = []string{strconv.FormatInt(int64(max), 10)} } req := &request{ bucket: b.Name, params: params, } result = &ListResp{} for attempt := attempts.Start(); attempt.Next(); { err = b.S3.query(req, result) if !shouldRetry(err) { break } } if err != nil { return nil, err } return result, nil } // URL returns a non-signed URL that allows retriving the // object at path. It only works if the object is publicly // readable (see SignedURL). func (b *Bucket) URL(path string) string { req := &request{ bucket: b.Name, path: path, } err := b.S3.prepare(req) if err != nil { panic(err) } u, err := req.url() if err != nil { panic(err) } u.RawQuery = "" return u.String() } // SignedURL returns a signed URL that allows anyone holding the URL // to retrieve the object at path. The signature is valid until expires. func (b *Bucket) SignedURL(path string, expires time.Time) string { req := &request{ bucket: b.Name, path: path, params: url.Values{"Expires": {strconv.FormatInt(expires.Unix(), 10)}}, } err := b.S3.prepare(req) if err != nil { panic(err) } u, err := req.url() if err != nil { panic(err) } return u.String() } type request struct { method string bucket string path string signpath string params url.Values headers http.Header baseurl string payload io.Reader prepared bool } func (req *request) url() (*url.URL, error) { u, err := url.Parse(req.baseurl) if err != nil { return nil, fmt.Errorf("bad S3 endpoint URL %q: %v", req.baseurl, err) } u.RawQuery = req.params.Encode() u.Path = req.path return u, nil } // query prepares and runs the req request. // If resp is not nil, the XML data contained in the response // body will be unmarshalled on it. func (s3 *S3) query(req *request, resp interface{}) error { err := s3.prepare(req) if err != nil { return err } hresp, err := s3.run(req) if err != nil { return err } if resp != nil { err = xml.NewDecoder(hresp.Body).Decode(resp) } hresp.Body.Close() return nil } // prepare sets up req to be delivered to S3. func (s3 *S3) prepare(req *request) error { if !req.prepared { req.prepared = true if req.method == "" { req.method = "GET" } // Copy so they can be mutated without affecting on retries. params := make(url.Values) headers := make(http.Header) for k, v := range req.params { params[k] = v } for k, v := range req.headers { headers[k] = v } req.params = params req.headers = headers if !strings.HasPrefix(req.path, "/") { req.path = "/" + req.path } req.signpath = req.path if req.bucket != "" { req.baseurl = s3.Region.S3BucketEndpoint if req.baseurl == "" { // Use the path method to address the bucket. req.baseurl = s3.Region.S3Endpoint req.path = "/" + req.bucket + req.path } else { // Just in case, prevent injection. if strings.IndexAny(req.bucket, "/:@") >= 0 { return fmt.Errorf("bad S3 bucket: %q", req.bucket) } req.baseurl = strings.Replace(req.baseurl, "${bucket}", req.bucket, -1) } req.signpath = "/" + req.bucket + req.signpath } } // Always sign again as it's not clear how far the // server has handled a previous attempt. u, err := url.Parse(req.baseurl) if err != nil { return fmt.Errorf("bad S3 endpoint URL %q: %v", req.baseurl, err) } req.headers["Host"] = []string{u.Host} req.headers["Date"] = []string{time.Now().In(time.UTC).Format(time.RFC1123)} sign(s3.Auth, req.method, req.signpath, req.params, req.headers) return nil } // run sends req and returns the http response from the server. func (s3 *S3) run(req *request) (*http.Response, error) { if debug { log.Printf("Running S3 request: %#v", req) } u, err := req.url() if err != nil { return nil, err } hreq := http.Request{ URL: u, Method: req.method, ProtoMajor: 1, ProtoMinor: 1, Close: true, Header: req.headers, } if v, ok := req.headers["Content-Length"]; ok { hreq.ContentLength, _ = strconv.ParseInt(v[0], 10, 64) delete(req.headers, "Content-Length") } if req.payload != nil { hreq.Body = ioutil.NopCloser(req.payload) } hresp, err := http.DefaultClient.Do(&hreq) if err != nil { return nil, err } if debug { dump, _ := httputil.DumpResponse(hresp, true) log.Printf("} -> %s\n", dump) } if hresp.StatusCode != 200 && hresp.StatusCode != 204 { return nil, buildError(hresp) } return hresp, err } // Error represents an error in an operation with S3. type Error struct { StatusCode int // HTTP status code (200, 403, ...) Code string // EC2 error code ("UnsupportedOperation", ...) Message string // The human-oriented error message BucketName string RequestId string HostId string } func (e *Error) Error() string { return e.Message } func buildError(r *http.Response) error { if debug { log.Printf("got error (status code %v)", r.StatusCode) data, err := ioutil.ReadAll(r.Body) if err != nil { log.Printf("\tread error: %v", err) } else { log.Printf("\tdata:\n%s\n\n", data) } r.Body = ioutil.NopCloser(bytes.NewBuffer(data)) } err := Error{} // TODO return error if Unmarshal fails? xml.NewDecoder(r.Body).Decode(&err) r.Body.Close() err.StatusCode = r.StatusCode if err.Message == "" { err.Message = r.Status } if debug { log.Printf("err: %#v\n", err) } return &err } func shouldRetry(err error) bool { if err == nil { return false } switch err { case io.ErrUnexpectedEOF, io.EOF: return true } switch e := err.(type) { case *net.DNSError: return true case *net.OpError: switch e.Op { case "read", "write": return true } case *Error: switch e.Code { case "InternalError", "NoSuchUpload", "NoSuchBucket": return true } } return false } func hasCode(err error, code string) bool { s3err, ok := err.(*Error) return ok && s3err.Code == code } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/s3i_test.go���������������������������������������������0000644�0000153�0000161�00000035254�12321735726�022524� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package s3_test import ( "bytes" "crypto/md5" "fmt" "io/ioutil" "net/http" "strings" "launchpad.net/goamz/aws" "launchpad.net/goamz/s3" "launchpad.net/goamz/testutil" . "launchpad.net/gocheck" "net" "sort" "time" ) // AmazonServer represents an Amazon S3 server. type AmazonServer struct { auth aws.Auth } func (s *AmazonServer) SetUp(c *C) { auth, err := aws.EnvAuth() if err != nil { c.Fatal(err.Error()) } s.auth = auth } var _ = Suite(&AmazonClientSuite{Region: aws.USEast}) var _ = Suite(&AmazonClientSuite{Region: aws.EUWest}) var _ = Suite(&AmazonDomainClientSuite{Region: aws.USEast}) // AmazonClientSuite tests the client against a live S3 server. type AmazonClientSuite struct { aws.Region srv AmazonServer ClientTests } func (s *AmazonClientSuite) SetUpSuite(c *C) { if !testutil.Amazon { c.Skip("live tests against AWS disabled (no -amazon)") } s.srv.SetUp(c) s.s3 = s3.New(s.srv.auth, s.Region) // In case tests were interrupted in the middle before. s.ClientTests.Cleanup() } func (s *AmazonClientSuite) TearDownTest(c *C) { s.ClientTests.Cleanup() } // AmazonDomainClientSuite tests the client against a live S3 // server using bucket names in the endpoint domain name rather // than the request path. type AmazonDomainClientSuite struct { aws.Region srv AmazonServer ClientTests } func (s *AmazonDomainClientSuite) SetUpSuite(c *C) { if !testutil.Amazon { c.Skip("live tests against AWS disabled (no -amazon)") } s.srv.SetUp(c) region := s.Region region.S3BucketEndpoint = "https://${bucket}.s3.amazonaws.com" s.s3 = s3.New(s.srv.auth, region) s.ClientTests.Cleanup() } func (s *AmazonDomainClientSuite) TearDownTest(c *C) { s.ClientTests.Cleanup() } // ClientTests defines integration tests designed to test the client. // It is not used as a test suite in itself, but embedded within // another type. type ClientTests struct { s3 *s3.S3 authIsBroken bool } func (s *ClientTests) Cleanup() { killBucket(testBucket(s.s3)) } func testBucket(s *s3.S3) *s3.Bucket { // Watch out! If this function is corrupted and made to match with something // people own, killBucket will happily remove *everything* inside the bucket. key := s.Auth.AccessKey if len(key) >= 8 { key = s.Auth.AccessKey[:8] } return s.Bucket(fmt.Sprintf("goamz-%s-%s", s.Region.Name, key)) } var attempts = aws.AttemptStrategy{ Min: 5, Total: 20 * time.Second, Delay: 100 * time.Millisecond, } func killBucket(b *s3.Bucket) { var err error for attempt := attempts.Start(); attempt.Next(); { err = b.DelBucket() if err == nil { return } if _, ok := err.(*net.DNSError); ok { return } e, ok := err.(*s3.Error) if ok && e.Code == "NoSuchBucket" { return } if ok && e.Code == "BucketNotEmpty" { // Errors are ignored here. Just retry. resp, err := b.List("", "", "", 1000) if err == nil { for _, key := range resp.Contents { _ = b.Del(key.Key) } } multis, _, _ := b.ListMulti("", "") for _, m := range multis { _ = m.Abort() } } } message := "cannot delete test bucket" if err != nil { message += ": " + err.Error() } panic(message) } func get(url string) ([]byte, error) { for attempt := attempts.Start(); attempt.Next(); { resp, err := http.Get(url) if err != nil { if attempt.HasNext() { continue } return nil, err } data, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if err != nil { if attempt.HasNext() { continue } return nil, err } return data, err } panic("unreachable") } func (s *ClientTests) TestBasicFunctionality(c *C) { b := testBucket(s.s3) err := b.PutBucket(s3.PublicRead) c.Assert(err, IsNil) err = b.Put("name", []byte("yo!"), "text/plain", s3.PublicRead) c.Assert(err, IsNil) defer b.Del("name") data, err := b.Get("name") c.Assert(err, IsNil) c.Assert(string(data), Equals, "yo!") data, err = get(b.URL("name")) c.Assert(err, IsNil) c.Assert(string(data), Equals, "yo!") buf := bytes.NewBufferString("hey!") err = b.PutReader("name2", buf, int64(buf.Len()), "text/plain", s3.Private) c.Assert(err, IsNil) defer b.Del("name2") rc, err := b.GetReader("name2") c.Assert(err, IsNil) data, err = ioutil.ReadAll(rc) c.Check(err, IsNil) c.Check(string(data), Equals, "hey!") rc.Close() data, err = get(b.SignedURL("name2", time.Now().Add(time.Hour))) c.Assert(err, IsNil) c.Assert(string(data), Equals, "hey!") if !s.authIsBroken { data, err = get(b.SignedURL("name2", time.Now().Add(-time.Hour))) c.Assert(err, IsNil) c.Assert(string(data), Matches, "(?s).*AccessDenied.*") } err = b.DelBucket() c.Assert(err, NotNil) s3err, ok := err.(*s3.Error) c.Assert(ok, Equals, true) c.Assert(s3err.Code, Equals, "BucketNotEmpty") c.Assert(s3err.BucketName, Equals, b.Name) c.Assert(s3err.Message, Equals, "The bucket you tried to delete is not empty") err = b.Del("name") c.Assert(err, IsNil) err = b.Del("name2") c.Assert(err, IsNil) err = b.DelBucket() c.Assert(err, IsNil) } func (s *ClientTests) TestGetNotFound(c *C) { b := s.s3.Bucket("goamz-" + s.s3.Auth.AccessKey) data, err := b.Get("non-existent") s3err, _ := err.(*s3.Error) c.Assert(s3err, NotNil) c.Assert(s3err.StatusCode, Equals, 404) c.Assert(s3err.Code, Equals, "NoSuchBucket") c.Assert(s3err.Message, Equals, "The specified bucket does not exist") c.Assert(data, IsNil) } // Communicate with all endpoints to see if they are alive. func (s *ClientTests) TestRegions(c *C) { errs := make(chan error, len(aws.Regions)) for _, region := range aws.Regions { go func(r aws.Region) { s := s3.New(s.s3.Auth, r) b := s.Bucket("goamz-" + s.Auth.AccessKey) _, err := b.Get("non-existent") errs <- err }(region) } for _ = range aws.Regions { err := <-errs if err != nil { s3_err, ok := err.(*s3.Error) if ok { c.Check(s3_err.Code, Matches, "NoSuchBucket") } else if _, ok = err.(*net.DNSError); ok { // Okay as well. } else { c.Errorf("Non-S3 error: %s", err) } } else { c.Errorf("Test should have errored but it seems to have succeeded") } } } var objectNames = []string{ "index.html", "index2.html", "photos/2006/February/sample2.jpg", "photos/2006/February/sample3.jpg", "photos/2006/February/sample4.jpg", "photos/2006/January/sample.jpg", "test/bar", "test/foo", } func keys(names ...string) []s3.Key { ks := make([]s3.Key, len(names)) for i, name := range names { ks[i].Key = name } return ks } // As the ListResp specifies all the parameters to the // request too, we use it to specify request parameters // and expected results. The Contents field is // used only for the key names inside it. var listTests = []s3.ListResp{ // normal list. { Contents: keys(objectNames...), }, { Marker: objectNames[0], Contents: keys(objectNames[1:]...), }, { Marker: objectNames[0] + "a", Contents: keys(objectNames[1:]...), }, { Marker: "z", }, // limited results. { MaxKeys: 2, Contents: keys(objectNames[0:2]...), IsTruncated: true, }, { MaxKeys: 2, Marker: objectNames[0], Contents: keys(objectNames[1:3]...), IsTruncated: true, }, { MaxKeys: 2, Marker: objectNames[len(objectNames)-2], Contents: keys(objectNames[len(objectNames)-1:]...), }, // with delimiter { Delimiter: "/", CommonPrefixes: []string{"photos/", "test/"}, Contents: keys("index.html", "index2.html"), }, { Delimiter: "/", Prefix: "photos/2006/", CommonPrefixes: []string{"photos/2006/February/", "photos/2006/January/"}, }, { Delimiter: "/", Prefix: "t", CommonPrefixes: []string{"test/"}, }, { Delimiter: "/", MaxKeys: 1, Contents: keys("index.html"), IsTruncated: true, }, { Delimiter: "/", MaxKeys: 1, Marker: "index2.html", CommonPrefixes: []string{"photos/"}, IsTruncated: true, }, { Delimiter: "/", MaxKeys: 1, Marker: "photos/", CommonPrefixes: []string{"test/"}, IsTruncated: false, }, { Delimiter: "Feb", CommonPrefixes: []string{"photos/2006/Feb"}, Contents: keys("index.html", "index2.html", "photos/2006/January/sample.jpg", "test/bar", "test/foo"), }, } func (s *ClientTests) TestDoublePutBucket(c *C) { b := testBucket(s.s3) err := b.PutBucket(s3.PublicRead) c.Assert(err, IsNil) err = b.PutBucket(s3.PublicRead) if err != nil { c.Assert(err, FitsTypeOf, new(s3.Error)) c.Assert(err.(*s3.Error).Code, Equals, "BucketAlreadyOwnedByYou") } } func (s *ClientTests) TestBucketList(c *C) { b := testBucket(s.s3) err := b.PutBucket(s3.Private) c.Assert(err, IsNil) objData := make(map[string][]byte) for i, path := range objectNames { data := []byte(strings.Repeat("a", i)) err := b.Put(path, data, "text/plain", s3.Private) c.Assert(err, IsNil) defer b.Del(path) objData[path] = data } for i, t := range listTests { c.Logf("test %d", i) resp, err := b.List(t.Prefix, t.Delimiter, t.Marker, t.MaxKeys) c.Assert(err, IsNil) c.Check(resp.Name, Equals, b.Name) c.Check(resp.Delimiter, Equals, t.Delimiter) c.Check(resp.IsTruncated, Equals, t.IsTruncated) c.Check(resp.CommonPrefixes, DeepEquals, t.CommonPrefixes) checkContents(c, resp.Contents, objData, t.Contents) } } func etag(data []byte) string { sum := md5.New() sum.Write(data) return fmt.Sprintf(`"%x"`, sum.Sum(nil)) } func checkContents(c *C, contents []s3.Key, data map[string][]byte, expected []s3.Key) { c.Assert(contents, HasLen, len(expected)) for i, k := range contents { c.Check(k.Key, Equals, expected[i].Key) // TODO mtime c.Check(k.Size, Equals, int64(len(data[k.Key]))) c.Check(k.ETag, Equals, etag(data[k.Key])) } } func (s *ClientTests) TestMultiInitPutList(c *C) { b := testBucket(s.s3) err := b.PutBucket(s3.Private) c.Assert(err, IsNil) multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) c.Assert(multi.UploadId, Matches, ".+") defer multi.Abort() var sent []s3.Part for i := 0; i < 5; i++ { p, err := multi.PutPart(i+1, strings.NewReader(fmt.Sprintf("<part %d>", i+1))) c.Assert(err, IsNil) c.Assert(p.N, Equals, i+1) c.Assert(p.Size, Equals, int64(8)) c.Assert(p.ETag, Matches, ".+") sent = append(sent, p) } s3.SetListPartsMax(2) parts, err := multi.ListParts() c.Assert(err, IsNil) c.Assert(parts, HasLen, len(sent)) for i := range parts { c.Assert(parts[i].N, Equals, sent[i].N) c.Assert(parts[i].Size, Equals, sent[i].Size) c.Assert(parts[i].ETag, Equals, sent[i].ETag) } err = multi.Complete(parts) s3err, failed := err.(*s3.Error) c.Assert(failed, Equals, true) c.Assert(s3err.Code, Equals, "EntityTooSmall") err = multi.Abort() c.Assert(err, IsNil) _, err = multi.ListParts() s3err, ok := err.(*s3.Error) c.Assert(ok, Equals, true) c.Assert(s3err.Code, Equals, "NoSuchUpload") } // This may take a minute or more due to the minimum size accepted S3 // on multipart upload parts. func (s *ClientTests) TestMultiComplete(c *C) { b := testBucket(s.s3) err := b.PutBucket(s3.Private) c.Assert(err, IsNil) multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) c.Assert(multi.UploadId, Matches, ".+") defer multi.Abort() // Minimum size S3 accepts for all but the last part is 5MB. data1 := make([]byte, 5*1024*1024) data2 := []byte("<part 2>") part1, err := multi.PutPart(1, bytes.NewReader(data1)) c.Assert(err, IsNil) part2, err := multi.PutPart(2, bytes.NewReader(data2)) c.Assert(err, IsNil) // Purposefully reversed. The order requirement must be handled. err = multi.Complete([]s3.Part{part2, part1}) c.Assert(err, IsNil) data, err := b.Get("multi") c.Assert(err, IsNil) c.Assert(len(data), Equals, len(data1)+len(data2)) for i := range data1 { if data[i] != data1[i] { c.Fatalf("uploaded object at byte %d: want %d, got %d", data1[i], data[i]) } } c.Assert(string(data[len(data1):]), Equals, string(data2)) } type multiList []*s3.Multi func (l multiList) Len() int { return len(l) } func (l multiList) Less(i, j int) bool { return l[i].Key < l[j].Key } func (l multiList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (s *ClientTests) TestListMulti(c *C) { b := testBucket(s.s3) err := b.PutBucket(s3.Private) c.Assert(err, IsNil) // Ensure an empty state before testing its behavior. multis, _, err := b.ListMulti("", "") for _, m := range multis { err := m.Abort() c.Assert(err, IsNil) } keys := []string{ "a/multi2", "a/multi3", "b/multi4", "multi1", } for _, key := range keys { m, err := b.InitMulti(key, "", s3.Private) c.Assert(err, IsNil) defer m.Abort() } // Amazon's implementation of the multiple-request listing for // multipart uploads in progress seems broken in multiple ways. // (next tokens are not provided, etc). //s3.SetListMultiMax(2) multis, prefixes, err := b.ListMulti("", "") c.Assert(err, IsNil) for attempt := attempts.Start(); attempt.Next() && len(multis) < len(keys); { multis, prefixes, err = b.ListMulti("", "") c.Assert(err, IsNil) } sort.Sort(multiList(multis)) c.Assert(prefixes, IsNil) var gotKeys []string for _, m := range multis { gotKeys = append(gotKeys, m.Key) } c.Assert(gotKeys, DeepEquals, keys) for _, m := range multis { c.Assert(m.Bucket, Equals, b) c.Assert(m.UploadId, Matches, ".+") } multis, prefixes, err = b.ListMulti("", "/") for attempt := attempts.Start(); attempt.Next() && len(prefixes) < 2; { multis, prefixes, err = b.ListMulti("", "") c.Assert(err, IsNil) } c.Assert(err, IsNil) c.Assert(prefixes, DeepEquals, []string{"a/", "b/"}) c.Assert(multis, HasLen, 1) c.Assert(multis[0].Bucket, Equals, b) c.Assert(multis[0].Key, Equals, "multi1") c.Assert(multis[0].UploadId, Matches, ".+") for attempt := attempts.Start(); attempt.Next() && len(multis) < 2; { multis, prefixes, err = b.ListMulti("", "") c.Assert(err, IsNil) } multis, prefixes, err = b.ListMulti("a/", "/") c.Assert(err, IsNil) c.Assert(prefixes, IsNil) c.Assert(multis, HasLen, 2) c.Assert(multis[0].Bucket, Equals, b) c.Assert(multis[0].Key, Equals, "a/multi2") c.Assert(multis[0].UploadId, Matches, ".+") c.Assert(multis[1].Bucket, Equals, b) c.Assert(multis[1].Key, Equals, "a/multi3") c.Assert(multis[1].UploadId, Matches, ".+") } func (s *ClientTests) TestMultiPutAllZeroLength(c *C) { b := testBucket(s.s3) err := b.PutBucket(s3.Private) c.Assert(err, IsNil) multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) defer multi.Abort() // This tests an edge case. Amazon requires at least one // part for multiprat uploads to work, even the part is empty. parts, err := multi.PutAll(strings.NewReader(""), 5*1024*1024) c.Assert(err, IsNil) c.Assert(parts, HasLen, 1) c.Assert(parts[0].Size, Equals, int64(0)) c.Assert(parts[0].ETag, Equals, `"d41d8cd98f00b204e9800998ecf8427e"`) err = multi.Complete(parts) c.Assert(err, IsNil) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/multi_test.go�������������������������������������������0000644�0000153�0000161�00000027330�12321735726�023154� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package s3_test import ( "encoding/xml" "io" "io/ioutil" "launchpad.net/goamz/s3" . "launchpad.net/gocheck" "strings" ) func (s *S) TestInitMulti(c *C) { testServer.Response(200, nil, InitMultiResultDump) b := s.s3.Bucket("sample") multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Header["Content-Type"], DeepEquals, []string{"text/plain"}) c.Assert(req.Header["X-Amz-Acl"], DeepEquals, []string{"private"}) c.Assert(req.Form["uploads"], DeepEquals, []string{""}) c.Assert(multi.UploadId, Matches, "JNbR_[A-Za-z0-9.]+QQ--") } func (s *S) TestMultiNoPreviousUpload(c *C) { // Don't retry the NoSuchUpload error. s3.RetryAttempts(false) testServer.Response(404, nil, NoSuchUploadErrorDump) testServer.Response(200, nil, InitMultiResultDump) b := s.s3.Bucket("sample") multi, err := b.Multi("multi", "text/plain", s3.Private) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/sample/") c.Assert(req.Form["uploads"], DeepEquals, []string{""}) c.Assert(req.Form["prefix"], DeepEquals, []string{"multi"}) req = testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form["uploads"], DeepEquals, []string{""}) c.Assert(multi.UploadId, Matches, "JNbR_[A-Za-z0-9.]+QQ--") } func (s *S) TestMultiReturnOld(c *C) { testServer.Response(200, nil, ListMultiResultDump) b := s.s3.Bucket("sample") multi, err := b.Multi("multi1", "text/plain", s3.Private) c.Assert(err, IsNil) c.Assert(multi.Key, Equals, "multi1") c.Assert(multi.UploadId, Equals, "iUVug89pPvSswrikD") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/sample/") c.Assert(req.Form["uploads"], DeepEquals, []string{""}) c.Assert(req.Form["prefix"], DeepEquals, []string{"multi1"}) } func (s *S) TestListParts(c *C) { testServer.Response(200, nil, InitMultiResultDump) testServer.Response(200, nil, ListPartsResultDump1) testServer.Response(404, nil, NoSuchUploadErrorDump) // :-( testServer.Response(200, nil, ListPartsResultDump2) b := s.s3.Bucket("sample") multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) parts, err := multi.ListParts() c.Assert(err, IsNil) c.Assert(parts, HasLen, 3) c.Assert(parts[0].N, Equals, 1) c.Assert(parts[0].Size, Equals, int64(5)) c.Assert(parts[0].ETag, Equals, `"ffc88b4ca90a355f8ddba6b2c3b2af5c"`) c.Assert(parts[1].N, Equals, 2) c.Assert(parts[1].Size, Equals, int64(5)) c.Assert(parts[1].ETag, Equals, `"d067a0fa9dc61a6e7195ca99696b5a89"`) c.Assert(parts[2].N, Equals, 3) c.Assert(parts[2].Size, Equals, int64(5)) c.Assert(parts[2].ETag, Equals, `"49dcd91231f801159e893fb5c6674985"`) testServer.WaitRequest() req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form.Get("uploadId"), Matches, "JNbR_[A-Za-z0-9.]+QQ--") c.Assert(req.Form["max-parts"], DeepEquals, []string{"1000"}) testServer.WaitRequest() // The internal error. req = testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form.Get("uploadId"), Matches, "JNbR_[A-Za-z0-9.]+QQ--") c.Assert(req.Form["max-parts"], DeepEquals, []string{"1000"}) c.Assert(req.Form["part-number-marker"], DeepEquals, []string{"2"}) } func (s *S) TestPutPart(c *C) { headers := map[string]string{ "ETag": `"26f90efd10d614f100252ff56d88dad8"`, } testServer.Response(200, nil, InitMultiResultDump) testServer.Response(200, headers, "") b := s.s3.Bucket("sample") multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) part, err := multi.PutPart(1, strings.NewReader("<part 1>")) c.Assert(err, IsNil) c.Assert(part.N, Equals, 1) c.Assert(part.Size, Equals, int64(8)) c.Assert(part.ETag, Equals, headers["ETag"]) testServer.WaitRequest() req := testServer.WaitRequest() c.Assert(req.Method, Equals, "PUT") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form.Get("uploadId"), Matches, "JNbR_[A-Za-z0-9.]+QQ--") c.Assert(req.Form["partNumber"], DeepEquals, []string{"1"}) c.Assert(req.Header["Content-Length"], DeepEquals, []string{"8"}) c.Assert(req.Header["Content-Md5"], DeepEquals, []string{"JvkO/RDWFPEAJS/1bYja2A=="}) } func readAll(r io.Reader) string { data, err := ioutil.ReadAll(r) if err != nil { panic(err) } return string(data) } func (s *S) TestPutAllNoPreviousUpload(c *C) { // Don't retry the NoSuchUpload error. s3.RetryAttempts(false) etag1 := map[string]string{"ETag": `"etag1"`} etag2 := map[string]string{"ETag": `"etag2"`} etag3 := map[string]string{"ETag": `"etag3"`} testServer.Response(200, nil, InitMultiResultDump) testServer.Response(404, nil, NoSuchUploadErrorDump) testServer.Response(200, etag1, "") testServer.Response(200, etag2, "") testServer.Response(200, etag3, "") b := s.s3.Bucket("sample") multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) parts, err := multi.PutAll(strings.NewReader("part1part2last"), 5) c.Assert(parts, HasLen, 3) c.Assert(parts[0].ETag, Equals, `"etag1"`) c.Assert(parts[1].ETag, Equals, `"etag2"`) c.Assert(parts[2].ETag, Equals, `"etag3"`) c.Assert(err, IsNil) // Init testServer.WaitRequest() // List old parts. Won't find anything. req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/sample/multi") // Send part 1. req = testServer.WaitRequest() c.Assert(req.Method, Equals, "PUT") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form["partNumber"], DeepEquals, []string{"1"}) c.Assert(req.Header["Content-Length"], DeepEquals, []string{"5"}) c.Assert(readAll(req.Body), Equals, "part1") // Send part 2. req = testServer.WaitRequest() c.Assert(req.Method, Equals, "PUT") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form["partNumber"], DeepEquals, []string{"2"}) c.Assert(req.Header["Content-Length"], DeepEquals, []string{"5"}) c.Assert(readAll(req.Body), Equals, "part2") // Send part 3 with shorter body. req = testServer.WaitRequest() c.Assert(req.Method, Equals, "PUT") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form["partNumber"], DeepEquals, []string{"3"}) c.Assert(req.Header["Content-Length"], DeepEquals, []string{"4"}) c.Assert(readAll(req.Body), Equals, "last") } func (s *S) TestPutAllZeroSizeFile(c *C) { // Don't retry the NoSuchUpload error. s3.RetryAttempts(false) etag1 := map[string]string{"ETag": `"etag1"`} testServer.Response(200, nil, InitMultiResultDump) testServer.Response(404, nil, NoSuchUploadErrorDump) testServer.Response(200, etag1, "") b := s.s3.Bucket("sample") multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) // Must send at least one part, so that completing it will work. parts, err := multi.PutAll(strings.NewReader(""), 5) c.Assert(parts, HasLen, 1) c.Assert(parts[0].ETag, Equals, `"etag1"`) c.Assert(err, IsNil) // Init testServer.WaitRequest() // List old parts. Won't find anything. req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/sample/multi") // Send empty part. req = testServer.WaitRequest() c.Assert(req.Method, Equals, "PUT") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form["partNumber"], DeepEquals, []string{"1"}) c.Assert(req.Header["Content-Length"], DeepEquals, []string{"0"}) c.Assert(readAll(req.Body), Equals, "") } func (s *S) TestPutAllResume(c *C) { etag2 := map[string]string{"ETag": `"etag2"`} testServer.Response(200, nil, InitMultiResultDump) testServer.Response(200, nil, ListPartsResultDump1) testServer.Response(200, nil, ListPartsResultDump2) testServer.Response(200, etag2, "") b := s.s3.Bucket("sample") multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) // "part1" and "part3" match the checksums in ResultDump1. // The middle one is a mismatch (it refers to "part2"). parts, err := multi.PutAll(strings.NewReader("part1partXpart3"), 5) c.Assert(parts, HasLen, 3) c.Assert(parts[0].N, Equals, 1) c.Assert(parts[0].Size, Equals, int64(5)) c.Assert(parts[0].ETag, Equals, `"ffc88b4ca90a355f8ddba6b2c3b2af5c"`) c.Assert(parts[1].N, Equals, 2) c.Assert(parts[1].Size, Equals, int64(5)) c.Assert(parts[1].ETag, Equals, `"etag2"`) c.Assert(parts[2].N, Equals, 3) c.Assert(parts[2].Size, Equals, int64(5)) c.Assert(parts[2].ETag, Equals, `"49dcd91231f801159e893fb5c6674985"`) c.Assert(err, IsNil) // Init testServer.WaitRequest() // List old parts, broken in two requests. for i := 0; i < 2; i++ { req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/sample/multi") } // Send part 2, as it didn't match the checksum. req := testServer.WaitRequest() c.Assert(req.Method, Equals, "PUT") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form["partNumber"], DeepEquals, []string{"2"}) c.Assert(req.Header["Content-Length"], DeepEquals, []string{"5"}) c.Assert(readAll(req.Body), Equals, "partX") } func (s *S) TestMultiComplete(c *C) { testServer.Response(200, nil, InitMultiResultDump) // Note the 200 response. Completing will hold the connection on some // kind of long poll, and may return a late error even after a 200. testServer.Response(200, nil, InternalErrorDump) testServer.Response(200, nil, "") b := s.s3.Bucket("sample") multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) err = multi.Complete([]s3.Part{{2, `"ETag2"`, 32}, {1, `"ETag1"`, 64}}) c.Assert(err, IsNil) testServer.WaitRequest() req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form.Get("uploadId"), Matches, "JNbR_[A-Za-z0-9.]+QQ--") var payload struct { XMLName xml.Name Part []struct { PartNumber int ETag string } } dec := xml.NewDecoder(req.Body) err = dec.Decode(&payload) c.Assert(err, IsNil) c.Assert(payload.XMLName.Local, Equals, "CompleteMultipartUpload") c.Assert(len(payload.Part), Equals, 2) c.Assert(payload.Part[0].PartNumber, Equals, 1) c.Assert(payload.Part[0].ETag, Equals, `"ETag1"`) c.Assert(payload.Part[1].PartNumber, Equals, 2) c.Assert(payload.Part[1].ETag, Equals, `"ETag2"`) } func (s *S) TestMultiAbort(c *C) { testServer.Response(200, nil, InitMultiResultDump) testServer.Response(200, nil, "") b := s.s3.Bucket("sample") multi, err := b.InitMulti("multi", "text/plain", s3.Private) c.Assert(err, IsNil) err = multi.Abort() c.Assert(err, IsNil) testServer.WaitRequest() req := testServer.WaitRequest() c.Assert(req.Method, Equals, "DELETE") c.Assert(req.URL.Path, Equals, "/sample/multi") c.Assert(req.Form.Get("uploadId"), Matches, "JNbR_[A-Za-z0-9.]+QQ--") } func (s *S) TestListMulti(c *C) { testServer.Response(200, nil, ListMultiResultDump) b := s.s3.Bucket("sample") multis, prefixes, err := b.ListMulti("", "/") c.Assert(err, IsNil) c.Assert(prefixes, DeepEquals, []string{"a/", "b/"}) c.Assert(multis, HasLen, 2) c.Assert(multis[0].Key, Equals, "multi1") c.Assert(multis[0].UploadId, Equals, "iUVug89pPvSswrikD") c.Assert(multis[1].Key, Equals, "multi2") c.Assert(multis[1].UploadId, Equals, "DkirwsSvPp98guVUi") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/sample/") c.Assert(req.Form["uploads"], DeepEquals, []string{""}) c.Assert(req.Form["prefix"], DeepEquals, []string{""}) c.Assert(req.Form["delimiter"], DeepEquals, []string{"/"}) c.Assert(req.Form["max-uploads"], DeepEquals, []string{"1000"}) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/s3/sign_test.go��������������������������������������������0000644�0000153�0000161�00000010725�12321735726�022762� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package s3_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/s3" . "launchpad.net/gocheck" ) // S3 ReST authentication docs: http://goo.gl/G1LrK var testAuth = aws.Auth{"0PN5J17HBGZHT7JJ3X82", "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o"} func (s *S) TestSignExampleObjectGet(c *C) { method := "GET" path := "/johnsmith/photos/puppy.jpg" headers := map[string][]string{ "Host": {"johnsmith.s3.amazonaws.com"}, "Date": {"Tue, 27 Mar 2007 19:36:42 +0000"}, } s3.Sign(testAuth, method, path, nil, headers) expected := "AWS 0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbA=" c.Assert(headers["Authorization"], DeepEquals, []string{expected}) } func (s *S) TestSignExampleObjectPut(c *C) { method := "PUT" path := "/johnsmith/photos/puppy.jpg" headers := map[string][]string{ "Host": {"johnsmith.s3.amazonaws.com"}, "Date": {"Tue, 27 Mar 2007 21:15:45 +0000"}, "Content-Type": {"image/jpeg"}, "Content-Length": {"94328"}, } s3.Sign(testAuth, method, path, nil, headers) expected := "AWS 0PN5J17HBGZHT7JJ3X82:hcicpDDvL9SsO6AkvxqmIWkmOuQ=" c.Assert(headers["Authorization"], DeepEquals, []string{expected}) } func (s *S) TestSignExampleList(c *C) { method := "GET" path := "/johnsmith/" params := map[string][]string{ "prefix": {"photos"}, "max-keys": {"50"}, "marker": {"puppy"}, } headers := map[string][]string{ "Host": {"johnsmith.s3.amazonaws.com"}, "Date": {"Tue, 27 Mar 2007 19:42:41 +0000"}, "User-Agent": {"Mozilla/5.0"}, } s3.Sign(testAuth, method, path, params, headers) expected := "AWS 0PN5J17HBGZHT7JJ3X82:jsRt/rhG+Vtp88HrYL706QhE4w4=" c.Assert(headers["Authorization"], DeepEquals, []string{expected}) } func (s *S) TestSignExampleFetch(c *C) { method := "GET" path := "/johnsmith/" params := map[string][]string{ "acl": {""}, } headers := map[string][]string{ "Host": {"johnsmith.s3.amazonaws.com"}, "Date": {"Tue, 27 Mar 2007 19:44:46 +0000"}, } s3.Sign(testAuth, method, path, params, headers) expected := "AWS 0PN5J17HBGZHT7JJ3X82:thdUi9VAkzhkniLj96JIrOPGi0g=" c.Assert(headers["Authorization"], DeepEquals, []string{expected}) } func (s *S) TestSignExampleDelete(c *C) { method := "DELETE" path := "/johnsmith/photos/puppy.jpg" params := map[string][]string{} headers := map[string][]string{ "Host": {"s3.amazonaws.com"}, "Date": {"Tue, 27 Mar 2007 21:20:27 +0000"}, "User-Agent": {"dotnet"}, "x-amz-date": {"Tue, 27 Mar 2007 21:20:26 +0000"}, } s3.Sign(testAuth, method, path, params, headers) expected := "AWS 0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5Ip83xlYzk=" c.Assert(headers["Authorization"], DeepEquals, []string{expected}) } func (s *S) TestSignExampleUpload(c *C) { method := "PUT" path := "/static.johnsmith.net/db-backup.dat.gz" params := map[string][]string{} headers := map[string][]string{ "Host": {"static.johnsmith.net:8080"}, "Date": {"Tue, 27 Mar 2007 21:06:08 +0000"}, "User-Agent": {"curl/7.15.5"}, "x-amz-acl": {"public-read"}, "content-type": {"application/x-download"}, "Content-MD5": {"4gJE4saaMU4BqNR0kLY+lw=="}, "X-Amz-Meta-ReviewedBy": {"joe@johnsmith.net,jane@johnsmith.net"}, "X-Amz-Meta-FileChecksum": {"0x02661779"}, "X-Amz-Meta-ChecksumAlgorithm": {"crc32"}, "Content-Disposition": {"attachment; filename=database.dat"}, "Content-Encoding": {"gzip"}, "Content-Length": {"5913339"}, } s3.Sign(testAuth, method, path, params, headers) expected := "AWS 0PN5J17HBGZHT7JJ3X82:C0FlOtU8Ylb9KDTpZqYkZPX91iI=" c.Assert(headers["Authorization"], DeepEquals, []string{expected}) } func (s *S) TestSignExampleListAllMyBuckets(c *C) { method := "GET" path := "/" headers := map[string][]string{ "Host": {"s3.amazonaws.com"}, "Date": {"Wed, 28 Mar 2007 01:29:59 +0000"}, } s3.Sign(testAuth, method, path, nil, headers) expected := "AWS 0PN5J17HBGZHT7JJ3X82:Db+gepJSUbZKwpx1FR0DLtEYoZA=" c.Assert(headers["Authorization"], DeepEquals, []string{expected}) } func (s *S) TestSignExampleUnicodeKeys(c *C) { method := "GET" path := "/dictionary/fran%C3%A7ais/pr%c3%a9f%c3%a8re" headers := map[string][]string{ "Host": {"s3.amazonaws.com"}, "Date": {"Wed, 28 Mar 2007 01:49:49 +0000"}, } s3.Sign(testAuth, method, path, nil, headers) expected := "AWS 0PN5J17HBGZHT7JJ3X82:dxhSBHoI6eVSPcXJqEghlUzZMnY=" c.Assert(headers["Authorization"], DeepEquals, []string{expected}) } �������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/.lbox������������������������������������������������������0000644�0000153�0000161�00000000032�12321735726�021042� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������propose -for=lp:goamz -cr ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/aws/�������������������������������������������������������0000755�0000153�0000161�00000000000�12321736016�020665� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/aws/attempt.go���������������������������������������������0000644�0000153�0000161�00000003264�12321735726�022706� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package aws import ( "time" ) // AttemptStrategy represents a strategy for waiting for an action // to complete successfully. This is an internal type used by the // implementation of other goamz packages. type AttemptStrategy struct { Total time.Duration // total duration of attempt. Delay time.Duration // interval between each try in the burst. Min int // minimum number of retries; overrides Total } type Attempt struct { strategy AttemptStrategy last time.Time end time.Time force bool count int } // Start begins a new sequence of attempts for the given strategy. func (s AttemptStrategy) Start() *Attempt { now := time.Now() return &Attempt{ strategy: s, last: now, end: now.Add(s.Total), force: true, } } // Next waits until it is time to perform the next attempt or returns // false if it is time to stop trying. func (a *Attempt) Next() bool { now := time.Now() sleep := a.nextSleep(now) if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count { return false } a.force = false if sleep > 0 && a.count > 0 { time.Sleep(sleep) now = time.Now() } a.count++ a.last = now return true } func (a *Attempt) nextSleep(now time.Time) time.Duration { sleep := a.strategy.Delay - now.Sub(a.last) if sleep < 0 { return 0 } return sleep } // HasNext returns whether another attempt will be made if the current // one fails. If it returns true, the following call to Next is // guaranteed to return true. func (a *Attempt) HasNext() bool { if a.force || a.strategy.Min > a.count { return true } now := time.Now() if now.Add(a.nextSleep(now)).Before(a.end) { a.force = true return true } return false } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/aws/attempt_test.go����������������������������������������0000644�0000153�0000161�00000002742�12321735726�023745� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package aws_test import ( "launchpad.net/goamz/aws" . "launchpad.net/gocheck" "time" ) func (S) TestAttemptTiming(c *C) { testAttempt := aws.AttemptStrategy{ Total: 0.25e9, Delay: 0.1e9, } want := []time.Duration{0, 0.1e9, 0.2e9, 0.2e9} got := make([]time.Duration, 0, len(want)) // avoid allocation when testing timing t0 := time.Now() for a := testAttempt.Start(); a.Next(); { got = append(got, time.Now().Sub(t0)) } got = append(got, time.Now().Sub(t0)) c.Assert(got, HasLen, len(want)) const margin = 0.01e9 for i, got := range want { lo := want[i] - margin hi := want[i] + margin if got < lo || got > hi { c.Errorf("attempt %d want %g got %g", i, want[i].Seconds(), got.Seconds()) } } } func (S) TestAttemptNextHasNext(c *C) { a := aws.AttemptStrategy{}.Start() c.Assert(a.Next(), Equals, true) c.Assert(a.Next(), Equals, false) a = aws.AttemptStrategy{}.Start() c.Assert(a.Next(), Equals, true) c.Assert(a.HasNext(), Equals, false) c.Assert(a.Next(), Equals, false) a = aws.AttemptStrategy{Total: 2e8}.Start() c.Assert(a.Next(), Equals, true) c.Assert(a.HasNext(), Equals, true) time.Sleep(2e8) c.Assert(a.HasNext(), Equals, true) c.Assert(a.Next(), Equals, true) c.Assert(a.Next(), Equals, false) a = aws.AttemptStrategy{Total: 1e8, Min: 2}.Start() time.Sleep(1e8) c.Assert(a.Next(), Equals, true) c.Assert(a.HasNext(), Equals, true) c.Assert(a.Next(), Equals, true) c.Assert(a.HasNext(), Equals, false) c.Assert(a.Next(), Equals, false) } ������������������������������juju-core_1.18.1/src/launchpad.net/goamz/aws/aws.go�������������������������������������������������0000644�0000153�0000161�00000012102�12321736016�022002� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// // goamz - Go packages to interact with the Amazon Web Services. // // https://wiki.ubuntu.com/goamz // // Copyright (c) 2011 Canonical Ltd. // // Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com> // package aws import ( "errors" "os" ) // Region defines the URLs where AWS services may be accessed. // // See http://goo.gl/d8BP1 for more details. type Region struct { Name string // the canonical name of this region. EC2Endpoint string S3Endpoint string S3BucketEndpoint string // Not needed by AWS S3. Use ${bucket} for bucket name. S3LocationConstraint bool // true if this region requires a LocationConstraint declaration. S3LowercaseBucket bool // true if the region requires bucket names to be lower case. SDBEndpoint string SNSEndpoint string SQSEndpoint string IAMEndpoint string } var USEast = Region{ "us-east-1", "https://ec2.us-east-1.amazonaws.com", "https://s3.amazonaws.com", "", false, false, "https://sdb.amazonaws.com", "https://sns.us-east-1.amazonaws.com", "https://sqs.us-east-1.amazonaws.com", "https://iam.amazonaws.com", } var USWest = Region{ "us-west-1", "https://ec2.us-west-1.amazonaws.com", "https://s3-us-west-1.amazonaws.com", "", true, true, "https://sdb.us-west-1.amazonaws.com", "https://sns.us-west-1.amazonaws.com", "https://sqs.us-west-1.amazonaws.com", "https://iam.amazonaws.com", } var USWest2 = Region{ "us-west-2", "https://ec2.us-west-2.amazonaws.com", "https://s3-us-west-2.amazonaws.com", "", true, true, "https://sdb.us-west-2.amazonaws.com", "https://sns.us-west-2.amazonaws.com", "https://sqs.us-west-2.amazonaws.com", "https://iam.amazonaws.com", } var EUWest = Region{ "eu-west-1", "https://ec2.eu-west-1.amazonaws.com", "https://s3-eu-west-1.amazonaws.com", "", true, true, "https://sdb.eu-west-1.amazonaws.com", "https://sns.eu-west-1.amazonaws.com", "https://sqs.eu-west-1.amazonaws.com", "https://iam.amazonaws.com", } var APSoutheast = Region{ "ap-southeast-1", "https://ec2.ap-southeast-1.amazonaws.com", "https://s3-ap-southeast-1.amazonaws.com", "", true, true, "https://sdb.ap-southeast-1.amazonaws.com", "https://sns.ap-southeast-1.amazonaws.com", "https://sqs.ap-southeast-1.amazonaws.com", "https://iam.amazonaws.com", } var APSoutheast2 = Region{ "ap-southeast-2", "https://ec2.ap-southeast-2.amazonaws.com", "https://s3-ap-southeast-2.amazonaws.com", "", true, true, "https://sdb.ap-southeast-2.amazonaws.com", "https://sns.ap-southeast-2.amazonaws.com", "https://sqs.ap-southeast-2.amazonaws.com", "https://iam.amazonaws.com", } var APNortheast = Region{ "ap-northeast-1", "https://ec2.ap-northeast-1.amazonaws.com", "https://s3-ap-northeast-1.amazonaws.com", "", true, true, "https://sdb.ap-northeast-1.amazonaws.com", "https://sns.ap-northeast-1.amazonaws.com", "https://sqs.ap-northeast-1.amazonaws.com", "https://iam.amazonaws.com", } var SAEast = Region{ "sa-east-1", "https://ec2.sa-east-1.amazonaws.com", "https://s3-sa-east-1.amazonaws.com", "", true, true, "https://sdb.sa-east-1.amazonaws.com", "https://sns.sa-east-1.amazonaws.com", "https://sqs.sa-east-1.amazonaws.com", "https://iam.amazonaws.com", } var Regions = map[string]Region{ APNortheast.Name: APNortheast, APSoutheast.Name: APSoutheast, APSoutheast2.Name: APSoutheast2, EUWest.Name: EUWest, USEast.Name: USEast, USWest.Name: USWest, USWest2.Name: USWest2, SAEast.Name: SAEast, } type Auth struct { AccessKey, SecretKey string } var unreserved = make([]bool, 128) var hex = "0123456789ABCDEF" func init() { // RFC3986 u := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-_.~" for _, c := range u { unreserved[c] = true } } // EnvAuth creates an Auth based on environment information. // The AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment // variables are used as the first preference, but EC2_ACCESS_KEY // and EC2_SECRET_KEY environment variables are also supported. func EnvAuth() (auth Auth, err error) { auth.AccessKey = os.Getenv("AWS_ACCESS_KEY_ID") auth.SecretKey = os.Getenv("AWS_SECRET_ACCESS_KEY") // We fallback to EC2_ env variables if the AWS_ variants are not used. if auth.AccessKey == "" && auth.SecretKey == "" { auth.AccessKey = os.Getenv("EC2_ACCESS_KEY") auth.SecretKey = os.Getenv("EC2_SECRET_KEY") } if auth.AccessKey == "" { err = errors.New("AWS_ACCESS_KEY_ID not found in environment") } if auth.SecretKey == "" { err = errors.New("AWS_SECRET_ACCESS_KEY not found in environment") } return } // Encode takes a string and URI-encodes it in a way suitable // to be used in AWS signatures. func Encode(s string) string { encode := false for i := 0; i != len(s); i++ { c := s[i] if c > 127 || !unreserved[c] { encode = true break } } if !encode { return s } e := make([]byte, len(s)*3) ei := 0 for i := 0; i != len(s); i++ { c := s[i] if c > 127 || !unreserved[c] { e[ei] = '%' e[ei+1] = hex[c>>4] e[ei+2] = hex[c&0xF] ei += 3 } else { e[ei] = c ei += 1 } } return string(e[:ei]) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/aws/aws_test.go��������������������������������������������0000644�0000153�0000161�00000002773�12321735726�023065� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package aws_test import ( "launchpad.net/goamz/aws" . "launchpad.net/gocheck" "os" "strings" "testing" ) func Test(t *testing.T) { TestingT(t) } var _ = Suite(&S{}) type S struct { environ []string } func (s *S) SetUpSuite(c *C) { s.environ = os.Environ() } func (s *S) TearDownTest(c *C) { os.Clearenv() for _, kv := range s.environ { l := strings.SplitN(kv, "=", 2) os.Setenv(l[0], l[1]) } } func (s *S) TestEnvAuthNoSecret(c *C) { os.Clearenv() _, err := aws.EnvAuth() c.Assert(err, ErrorMatches, "AWS_SECRET_ACCESS_KEY not found in environment") } func (s *S) TestEnvAuthNoAccess(c *C) { os.Clearenv() os.Setenv("AWS_SECRET_ACCESS_KEY", "foo") _, err := aws.EnvAuth() c.Assert(err, ErrorMatches, "AWS_ACCESS_KEY_ID not found in environment") } func (s *S) TestEnvAuth(c *C) { os.Clearenv() os.Setenv("AWS_SECRET_ACCESS_KEY", "secret") os.Setenv("AWS_ACCESS_KEY_ID", "access") auth, err := aws.EnvAuth() c.Assert(err, IsNil) c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"}) } func (s *S) TestEnvAuthLegacy(c *C) { os.Clearenv() os.Setenv("EC2_SECRET_KEY", "secret") os.Setenv("EC2_ACCESS_KEY", "access") auth, err := aws.EnvAuth() c.Assert(err, IsNil) c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"}) } func (s *S) TestEncode(c *C) { c.Assert(aws.Encode("foo"), Equals, "foo") c.Assert(aws.Encode("/"), Equals, "%2F") } func (s *S) TestRegionsAreNamed(c *C) { for n, r := range aws.Regions { c.Assert(n, Equals, r.Name) } } �����juju-core_1.18.1/src/launchpad.net/goamz/iam/�������������������������������������������������������0000755�0000153�0000161�00000000000�12321735726�020650� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/iam/iam_test.go��������������������������������������������0000644�0000153�0000161�00000023335�12321735726�023012� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iam_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/iam" "launchpad.net/goamz/testutil" . "launchpad.net/gocheck" "strings" "testing" ) func Test(t *testing.T) { TestingT(t) } type S struct { iam *iam.IAM } var _ = Suite(&S{}) var testServer = testutil.NewHTTPServer() func (s *S) SetUpSuite(c *C) { testServer.Start() auth := aws.Auth{"abc", "123"} s.iam = iam.New(auth, aws.Region{IAMEndpoint: testServer.URL}) } func (s *S) TearDownSuite(c *C) { testServer.Stop() } func (s *S) TearDownTest(c *C) { testServer.Flush() } func (s *S) TestCreateUser(c *C) { testServer.Response(200, nil, CreateUserExample) resp, err := s.iam.CreateUser("Bob", "/division_abc/subdivision_xyz/") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "CreateUser") c.Assert(values.Get("UserName"), Equals, "Bob") c.Assert(values.Get("Path"), Equals, "/division_abc/subdivision_xyz/") c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") expected := iam.User{ Path: "/division_abc/subdivision_xyz/", Name: "Bob", Id: "AIDACKCEVSQ6C2EXAMPLE", Arn: "arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob", } c.Assert(resp.User, DeepEquals, expected) } func (s *S) TestCreateUserConflict(c *C) { testServer.Response(409, nil, DuplicateUserExample) resp, err := s.iam.CreateUser("Bob", "/division_abc/subdivision_xyz/") testServer.WaitRequest() c.Assert(resp, IsNil) c.Assert(err, NotNil) e, ok := err.(*iam.Error) c.Assert(ok, Equals, true) c.Assert(e.Message, Equals, "User with name Bob already exists.") c.Assert(e.Code, Equals, "EntityAlreadyExists") } func (s *S) TestGetUser(c *C) { testServer.Response(200, nil, GetUserExample) resp, err := s.iam.GetUser("Bob") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "GetUser") c.Assert(values.Get("UserName"), Equals, "Bob") c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") expected := iam.User{ Path: "/division_abc/subdivision_xyz/", Name: "Bob", Id: "AIDACKCEVSQ6C2EXAMPLE", Arn: "arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob", } c.Assert(resp.User, DeepEquals, expected) } func (s *S) TestDeleteUser(c *C) { testServer.Response(200, nil, RequestIdExample) resp, err := s.iam.DeleteUser("Bob") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "DeleteUser") c.Assert(values.Get("UserName"), Equals, "Bob") c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") } func (s *S) TestCreateGroup(c *C) { testServer.Response(200, nil, CreateGroupExample) resp, err := s.iam.CreateGroup("Admins", "/admins/") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "CreateGroup") c.Assert(values.Get("GroupName"), Equals, "Admins") c.Assert(values.Get("Path"), Equals, "/admins/") c.Assert(err, IsNil) c.Assert(resp.Group.Path, Equals, "/admins/") c.Assert(resp.Group.Name, Equals, "Admins") c.Assert(resp.Group.Id, Equals, "AGPACKCEVSQ6C2EXAMPLE") c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") } func (s *S) TestCreateGroupWithoutPath(c *C) { testServer.Response(200, nil, CreateGroupExample) _, err := s.iam.CreateGroup("Managers", "") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "CreateGroup") c.Assert(err, IsNil) _, ok := map[string][]string(values)["Path"] c.Assert(ok, Equals, false) } func (s *S) TestDeleteGroup(c *C) { testServer.Response(200, nil, RequestIdExample) resp, err := s.iam.DeleteGroup("Admins") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "DeleteGroup") c.Assert(values.Get("GroupName"), Equals, "Admins") c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") } func (s *S) TestListGroups(c *C) { testServer.Response(200, nil, ListGroupsExample) resp, err := s.iam.Groups("/division_abc/") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "ListGroups") c.Assert(values.Get("PathPrefix"), Equals, "/division_abc/") c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") expected := []iam.Group{ { Path: "/division_abc/subdivision_xyz/", Name: "Admins", Id: "AGPACKCEVSQ6C2EXAMPLE", Arn: "arn:aws:iam::123456789012:group/Admins", }, { Path: "/division_abc/subdivision_xyz/product_1234/engineering/", Name: "Test", Id: "AGP2MAB8DPLSRHEXAMPLE", Arn: "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/engineering/Test", }, { Path: "/division_abc/subdivision_xyz/product_1234/", Name: "Managers", Id: "AGPIODR4TAW7CSEXAMPLE", Arn: "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/Managers", }, } c.Assert(resp.Groups, DeepEquals, expected) } func (s *S) TestListGroupsWithoutPathPrefix(c *C) { testServer.Response(200, nil, ListGroupsExample) _, err := s.iam.Groups("") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "ListGroups") c.Assert(err, IsNil) _, ok := map[string][]string(values)["PathPrefix"] c.Assert(ok, Equals, false) } func (s *S) TestCreateAccessKey(c *C) { testServer.Response(200, nil, CreateAccessKeyExample) resp, err := s.iam.CreateAccessKey("Bob") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "CreateAccessKey") c.Assert(values.Get("UserName"), Equals, "Bob") c.Assert(err, IsNil) c.Assert(resp.AccessKey.UserName, Equals, "Bob") c.Assert(resp.AccessKey.Id, Equals, "AKIAIOSFODNN7EXAMPLE") c.Assert(resp.AccessKey.Secret, Equals, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY") c.Assert(resp.AccessKey.Status, Equals, "Active") } func (s *S) TestDeleteAccessKey(c *C) { testServer.Response(200, nil, RequestIdExample) resp, err := s.iam.DeleteAccessKey("ysa8hasdhasdsi", "Bob") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "DeleteAccessKey") c.Assert(values.Get("AccessKeyId"), Equals, "ysa8hasdhasdsi") c.Assert(values.Get("UserName"), Equals, "Bob") c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") } func (s *S) TestDeleteAccessKeyBlankUserName(c *C) { testServer.Response(200, nil, RequestIdExample) _, err := s.iam.DeleteAccessKey("ysa8hasdhasdsi", "") c.Assert(err, IsNil) values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "DeleteAccessKey") c.Assert(values.Get("AccessKeyId"), Equals, "ysa8hasdhasdsi") _, ok := map[string][]string(values)["UserName"] c.Assert(ok, Equals, false) } func (s *S) TestAccessKeys(c *C) { testServer.Response(200, nil, ListAccessKeyExample) resp, err := s.iam.AccessKeys("Bob") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "ListAccessKeys") c.Assert(values.Get("UserName"), Equals, "Bob") c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") c.Assert(resp.AccessKeys, HasLen, 2) c.Assert(resp.AccessKeys[0].Id, Equals, "AKIAIOSFODNN7EXAMPLE") c.Assert(resp.AccessKeys[0].UserName, Equals, "Bob") c.Assert(resp.AccessKeys[0].Status, Equals, "Active") c.Assert(resp.AccessKeys[1].Id, Equals, "AKIAI44QH8DHBEXAMPLE") c.Assert(resp.AccessKeys[1].UserName, Equals, "Bob") c.Assert(resp.AccessKeys[1].Status, Equals, "Inactive") } func (s *S) TestAccessKeysBlankUserName(c *C) { testServer.Response(200, nil, ListAccessKeyExample) _, err := s.iam.AccessKeys("") c.Assert(err, IsNil) values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "ListAccessKeys") _, ok := map[string][]string(values)["UserName"] c.Assert(ok, Equals, false) } func (s *S) TestGetUserPolicy(c *C) { testServer.Response(200, nil, GetUserPolicyExample) resp, err := s.iam.GetUserPolicy("Bob", "AllAccessPolicy") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "GetUserPolicy") c.Assert(values.Get("UserName"), Equals, "Bob") c.Assert(values.Get("PolicyName"), Equals, "AllAccessPolicy") c.Assert(err, IsNil) c.Assert(resp.Policy.UserName, Equals, "Bob") c.Assert(resp.Policy.Name, Equals, "AllAccessPolicy") c.Assert(strings.TrimSpace(resp.Policy.Document), Equals, `{"Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}`) c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") } func (s *S) TestPutUserPolicy(c *C) { document := `{ "Statement": [ { "Action": [ "s3:*" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::8shsns19s90ajahadsj/*", "arn:aws:s3:::8shsns19s90ajahadsj" ] }] }` testServer.Response(200, nil, RequestIdExample) resp, err := s.iam.PutUserPolicy("Bob", "AllAccessPolicy", document) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.FormValue("Action"), Equals, "PutUserPolicy") c.Assert(req.FormValue("PolicyName"), Equals, "AllAccessPolicy") c.Assert(req.FormValue("UserName"), Equals, "Bob") c.Assert(req.FormValue("PolicyDocument"), Equals, document) c.Assert(req.FormValue("Version"), Equals, "2010-05-08") c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") } func (s *S) TestDeleteUserPolicy(c *C) { testServer.Response(200, nil, RequestIdExample) resp, err := s.iam.DeleteUserPolicy("Bob", "AllAccessPolicy") values := testServer.WaitRequest().URL.Query() c.Assert(values.Get("Action"), Equals, "DeleteUserPolicy") c.Assert(values.Get("PolicyName"), Equals, "AllAccessPolicy") c.Assert(values.Get("UserName"), Equals, "Bob") c.Assert(err, IsNil) c.Assert(resp.RequestId, Equals, "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/iam/responses_test.go��������������������������������������0000644�0000153�0000161�00000011206�12321735726�024257� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iam_test // http://goo.gl/EUIvl var CreateUserExample = ` <CreateUserResponse> <CreateUserResult> <User> <Path>/division_abc/subdivision_xyz/</Path> <UserName>Bob</UserName> <UserId>AIDACKCEVSQ6C2EXAMPLE</UserId> <Arn>arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob</Arn> </User> </CreateUserResult> <ResponseMetadata> <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> </ResponseMetadata> </CreateUserResponse> ` var DuplicateUserExample = ` <ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> <Error> <Type>Sender</Type> <Code>EntityAlreadyExists</Code> <Message>User with name Bob already exists.</Message> </Error> <RequestId>1d5f5000-1316-11e2-a60f-91a8e6fb6d21</RequestId> </ErrorResponse> ` var GetUserExample = ` <GetUserResponse> <GetUserResult> <User> <Path>/division_abc/subdivision_xyz/</Path> <UserName>Bob</UserName> <UserId>AIDACKCEVSQ6C2EXAMPLE</UserId> <Arn>arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob</Arn> </User> </GetUserResult> <ResponseMetadata> <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> </ResponseMetadata> </GetUserResponse> ` var CreateGroupExample = ` <CreateGroupResponse> <CreateGroupResult> <Group> <Path>/admins/</Path> <GroupName>Admins</GroupName> <GroupId>AGPACKCEVSQ6C2EXAMPLE</GroupId> <Arn>arn:aws:iam::123456789012:group/Admins</Arn> </Group> </CreateGroupResult> <ResponseMetadata> <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> </ResponseMetadata> </CreateGroupResponse> ` var ListGroupsExample = ` <ListGroupsResponse> <ListGroupsResult> <Groups> <member> <Path>/division_abc/subdivision_xyz/</Path> <GroupName>Admins</GroupName> <GroupId>AGPACKCEVSQ6C2EXAMPLE</GroupId> <Arn>arn:aws:iam::123456789012:group/Admins</Arn> </member> <member> <Path>/division_abc/subdivision_xyz/product_1234/engineering/</Path> <GroupName>Test</GroupName> <GroupId>AGP2MAB8DPLSRHEXAMPLE</GroupId> <Arn>arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/engineering/Test</Arn> </member> <member> <Path>/division_abc/subdivision_xyz/product_1234/</Path> <GroupName>Managers</GroupName> <GroupId>AGPIODR4TAW7CSEXAMPLE</GroupId> <Arn>arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/Managers</Arn> </member> </Groups> <IsTruncated>false</IsTruncated> </ListGroupsResult> <ResponseMetadata> <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> </ResponseMetadata> </ListGroupsResponse> ` var RequestIdExample = ` <AddUserToGroupResponse> <ResponseMetadata> <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> </ResponseMetadata> </AddUserToGroupResponse> ` var CreateAccessKeyExample = ` <CreateAccessKeyResponse> <CreateAccessKeyResult> <AccessKey> <UserName>Bob</UserName> <AccessKeyId>AKIAIOSFODNN7EXAMPLE</AccessKeyId> <Status>Active</Status> <SecretAccessKey>wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY</SecretAccessKey> </AccessKey> </CreateAccessKeyResult> <ResponseMetadata> <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> </ResponseMetadata> </CreateAccessKeyResponse> ` var ListAccessKeyExample = ` <ListAccessKeysResponse> <ListAccessKeysResult> <UserName>Bob</UserName> <AccessKeyMetadata> <member> <UserName>Bob</UserName> <AccessKeyId>AKIAIOSFODNN7EXAMPLE</AccessKeyId> <Status>Active</Status> </member> <member> <UserName>Bob</UserName> <AccessKeyId>AKIAI44QH8DHBEXAMPLE</AccessKeyId> <Status>Inactive</Status> </member> </AccessKeyMetadata> <IsTruncated>false</IsTruncated> </ListAccessKeysResult> <ResponseMetadata> <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> </ResponseMetadata> </ListAccessKeysResponse> ` var GetUserPolicyExample = ` <GetUserPolicyResponse> <GetUserPolicyResult> <UserName>Bob</UserName> <PolicyName>AllAccessPolicy</PolicyName> <PolicyDocument> {"Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]} </PolicyDocument> </GetUserPolicyResult> <ResponseMetadata> <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> </ResponseMetadata> </GetUserPolicyResponse> ` ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/iam/iam.go�������������������������������������������������0000644�0000153�0000161�00000024777�12321735726�021766� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// The iam package provides types and functions for interaction with the AWS // Identity and Access Management (IAM) service. package iam import ( "encoding/xml" "launchpad.net/goamz/aws" "net/http" "net/url" "strconv" "strings" "time" ) // The IAM type encapsulates operations operations with the IAM endpoint. type IAM struct { aws.Auth aws.Region } // New creates a new IAM instance. func New(auth aws.Auth, region aws.Region) *IAM { return &IAM{auth, region} } func (iam *IAM) query(params map[string]string, resp interface{}) error { params["Version"] = "2010-05-08" params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339) endpoint, err := url.Parse(iam.IAMEndpoint) if err != nil { return err } sign(iam.Auth, "GET", "/", params, endpoint.Host) endpoint.RawQuery = multimap(params).Encode() r, err := http.Get(endpoint.String()) if err != nil { return err } defer r.Body.Close() if r.StatusCode > 200 { return buildError(r) } return xml.NewDecoder(r.Body).Decode(resp) } func (iam *IAM) postQuery(params map[string]string, resp interface{}) error { endpoint, err := url.Parse(iam.IAMEndpoint) if err != nil { return err } params["Version"] = "2010-05-08" params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339) sign(iam.Auth, "POST", "/", params, endpoint.Host) encoded := multimap(params).Encode() body := strings.NewReader(encoded) req, err := http.NewRequest("POST", endpoint.String(), body) if err != nil { return err } req.Header.Set("Host", endpoint.Host) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Length", strconv.Itoa(len(encoded))) r, err := http.DefaultClient.Do(req) if err != nil { return err } defer r.Body.Close() if r.StatusCode > 200 { return buildError(r) } return xml.NewDecoder(r.Body).Decode(resp) } func buildError(r *http.Response) error { var ( err Error errors xmlErrors ) xml.NewDecoder(r.Body).Decode(&errors) if len(errors.Errors) > 0 { err = errors.Errors[0] } err.StatusCode = r.StatusCode if err.Message == "" { err.Message = r.Status } return &err } func multimap(p map[string]string) url.Values { q := make(url.Values, len(p)) for k, v := range p { q[k] = []string{v} } return q } // Response to a CreateUser request. // // See http://goo.gl/JS9Gz for more details. type CreateUserResp struct { RequestId string `xml:"ResponseMetadata>RequestId"` User User `xml:"CreateUserResult>User"` } // User encapsulates a user managed by IAM. // // See http://goo.gl/BwIQ3 for more details. type User struct { Arn string Path string Id string `xml:"UserId"` Name string `xml:"UserName"` } // CreateUser creates a new user in IAM. // // See http://goo.gl/JS9Gz for more details. func (iam *IAM) CreateUser(name, path string) (*CreateUserResp, error) { params := map[string]string{ "Action": "CreateUser", "Path": path, "UserName": name, } resp := new(CreateUserResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil } // Response for GetUser requests. // // See http://goo.gl/ZnzRN for more details. type GetUserResp struct { RequestId string `xml:"ResponseMetadata>RequestId"` User User `xml:"GetUserResult>User"` } // GetUser gets a user from IAM. // // See http://goo.gl/ZnzRN for more details. func (iam *IAM) GetUser(name string) (*GetUserResp, error) { params := map[string]string{ "Action": "GetUser", "UserName": name, } resp := new(GetUserResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil } // DeleteUser deletes a user from IAM. // // See http://goo.gl/jBuCG for more details. func (iam *IAM) DeleteUser(name string) (*SimpleResp, error) { params := map[string]string{ "Action": "DeleteUser", "UserName": name, } resp := new(SimpleResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil } // Response to a CreateGroup request. // // See http://goo.gl/n7NNQ for more details. type CreateGroupResp struct { Group Group `xml:"CreateGroupResult>Group"` RequestId string `xml:"ResponseMetadata>RequestId"` } // Group encapsulates a group managed by IAM. // // See http://goo.gl/ae7Vs for more details. type Group struct { Arn string Id string `xml:"GroupId"` Name string `xml:"GroupName"` Path string } // CreateGroup creates a new group in IAM. // // The path parameter can be used to identify which division or part of the // organization the user belongs to. // // If path is unset ("") it defaults to "/". // // See http://goo.gl/n7NNQ for more details. func (iam *IAM) CreateGroup(name string, path string) (*CreateGroupResp, error) { params := map[string]string{ "Action": "CreateGroup", "GroupName": name, } if path != "" { params["Path"] = path } resp := new(CreateGroupResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil } // Response to a ListGroups request. // // See http://goo.gl/W2TRj for more details. type GroupsResp struct { Groups []Group `xml:"ListGroupsResult>Groups>member"` RequestId string `xml:"ResponseMetadata>RequestId"` } // Groups list the groups that have the specified path prefix. // // The parameter pathPrefix is optional. If pathPrefix is "", all groups are // returned. // // See http://goo.gl/W2TRj for more details. func (iam *IAM) Groups(pathPrefix string) (*GroupsResp, error) { params := map[string]string{ "Action": "ListGroups", } if pathPrefix != "" { params["PathPrefix"] = pathPrefix } resp := new(GroupsResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil } // DeleteGroup deletes a group from IAM. // // See http://goo.gl/d5i2i for more details. func (iam *IAM) DeleteGroup(name string) (*SimpleResp, error) { params := map[string]string{ "Action": "DeleteGroup", "GroupName": name, } resp := new(SimpleResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil } // Response to a CreateAccessKey request. // // See http://goo.gl/L46Py for more details. type CreateAccessKeyResp struct { RequestId string `xml:"ResponseMetadata>RequestId"` AccessKey AccessKey `xml:"CreateAccessKeyResult>AccessKey"` } // AccessKey encapsulates an access key generated for a user. // // See http://goo.gl/LHgZR for more details. type AccessKey struct { UserName string Id string `xml:"AccessKeyId"` Secret string `xml:"SecretAccessKey,omitempty"` Status string } // CreateAccessKey creates a new access key in IAM. // // See http://goo.gl/L46Py for more details. func (iam *IAM) CreateAccessKey(userName string) (*CreateAccessKeyResp, error) { params := map[string]string{ "Action": "CreateAccessKey", "UserName": userName, } resp := new(CreateAccessKeyResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil } // Response to AccessKeys request. // // See http://goo.gl/Vjozx for more details. type AccessKeysResp struct { RequestId string `xml:"ResponseMetadata>RequestId"` AccessKeys []AccessKey `xml:"ListAccessKeysResult>AccessKeyMetadata>member"` } // AccessKeys lists all acccess keys associated with a user. // // The userName parameter is optional. If set to "", the userName is determined // implicitly based on the AWS Access Key ID used to sign the request. // // See http://goo.gl/Vjozx for more details. func (iam *IAM) AccessKeys(userName string) (*AccessKeysResp, error) { params := map[string]string{ "Action": "ListAccessKeys", } if userName != "" { params["UserName"] = userName } resp := new(AccessKeysResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil } // DeleteAccessKey deletes an access key from IAM. // // The userName parameter is optional. If set to "", the userName is determined // implicitly based on the AWS Access Key ID used to sign the request. // // See http://goo.gl/hPGhw for more details. func (iam *IAM) DeleteAccessKey(id, userName string) (*SimpleResp, error) { params := map[string]string{ "Action": "DeleteAccessKey", "AccessKeyId": id, } if userName != "" { params["UserName"] = userName } resp := new(SimpleResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil } // Response to a GetUserPolicy request. // // See http://goo.gl/BH04O for more details. type GetUserPolicyResp struct { Policy UserPolicy `xml:"GetUserPolicyResult"` RequestId string `xml:"ResponseMetadata>RequestId"` } // UserPolicy encapsulates an IAM group policy. // // See http://goo.gl/C7hgS for more details. type UserPolicy struct { Name string `xml:"PolicyName"` UserName string `xml:"UserName"` Document string `xml:"PolicyDocument"` } // GetUserPolicy gets a user policy in IAM. // // See http://goo.gl/BH04O for more details. func (iam *IAM) GetUserPolicy(userName, policyName string) (*GetUserPolicyResp, error) { params := map[string]string{ "Action": "GetUserPolicy", "UserName": userName, "PolicyName": policyName, } resp := new(GetUserPolicyResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil return nil, nil } // PutUserPolicy creates a user policy in IAM. // // See http://goo.gl/ldCO8 for more details. func (iam *IAM) PutUserPolicy(userName, policyName, policyDocument string) (*SimpleResp, error) { params := map[string]string{ "Action": "PutUserPolicy", "UserName": userName, "PolicyName": policyName, "PolicyDocument": policyDocument, } resp := new(SimpleResp) if err := iam.postQuery(params, resp); err != nil { return nil, err } return resp, nil } // DeleteUserPolicy deletes a user policy from IAM. // // See http://goo.gl/7Jncn for more details. func (iam *IAM) DeleteUserPolicy(userName, policyName string) (*SimpleResp, error) { params := map[string]string{ "Action": "DeleteUserPolicy", "PolicyName": policyName, "UserName": userName, } resp := new(SimpleResp) if err := iam.query(params, resp); err != nil { return nil, err } return resp, nil } type SimpleResp struct { RequestId string `xml:"ResponseMetadata>RequestId"` } type xmlErrors struct { Errors []Error `xml:"Error"` } // Error encapsulates an IAM error. type Error struct { // HTTP status code of the error. StatusCode int // AWS code of the error. Code string // Message explaining the error. Message string } func (e *Error) Error() string { var prefix string if e.Code != "" { prefix = e.Code + ": " } if prefix == "" && e.StatusCode > 0 { prefix = strconv.Itoa(e.StatusCode) + ": " } return prefix + e.Message } �juju-core_1.18.1/src/launchpad.net/goamz/iam/iamtest/�����������������������������������������������0000755�0000153�0000161�00000000000�12321735726�022316� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/iam/iamtest/server.go��������������������������������������0000644�0000153�0000161�00000026232�12321735726�024160� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Package iamtest implements a fake IAM provider with the capability of // inducing errors on any given operation, and retrospectively determining what // operations have been carried out. package iamtest import ( "encoding/json" "encoding/xml" "fmt" "launchpad.net/goamz/iam" "net" "net/http" "strings" "sync" ) type action struct { srv *Server w http.ResponseWriter req *http.Request reqId string } // Server implements an IAM simulator for use in tests. type Server struct { reqId int url string listener net.Listener users []iam.User groups []iam.Group accessKeys []iam.AccessKey userPolicies []iam.UserPolicy mutex sync.Mutex } func NewServer() (*Server, error) { l, err := net.Listen("tcp", "localhost:0") if err != nil { return nil, fmt.Errorf("cannot listen on localhost: %v", err) } srv := &Server{ listener: l, url: "http://" + l.Addr().String(), } go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { srv.serveHTTP(w, req) })) return srv, nil } // Quit closes down the server. func (srv *Server) Quit() error { return srv.listener.Close() } // URL returns a URL for the server. func (srv *Server) URL() string { return srv.url } type xmlErrors struct { XMLName string `xml:"ErrorResponse"` Error iam.Error } func (srv *Server) error(w http.ResponseWriter, err *iam.Error) { w.WriteHeader(err.StatusCode) xmlErr := xmlErrors{Error: *err} if e := xml.NewEncoder(w).Encode(xmlErr); e != nil { panic(e) } } func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) { req.ParseForm() srv.mutex.Lock() defer srv.mutex.Unlock() action := req.FormValue("Action") if action == "" { srv.error(w, &iam.Error{ StatusCode: 400, Code: "MissingAction", Message: "Missing action", }) } if a, ok := actions[action]; ok { reqId := fmt.Sprintf("req%0X", srv.reqId) srv.reqId++ if resp, err := a(srv, w, req, reqId); err == nil { if err := xml.NewEncoder(w).Encode(resp); err != nil { panic(err) } } else { switch err.(type) { case *iam.Error: srv.error(w, err.(*iam.Error)) default: panic(err) } } } else { srv.error(w, &iam.Error{ StatusCode: 400, Code: "InvalidAction", Message: "Invalid action: " + action, }) } } func (srv *Server) createUser(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"UserName"}); err != nil { return nil, err } path := req.FormValue("Path") if path == "" { path = "/" } name := req.FormValue("UserName") for _, user := range srv.users { if user.Name == name { return nil, &iam.Error{ StatusCode: 409, Code: "EntityAlreadyExists", Message: fmt.Sprintf("User with name %s already exists.", name), } } } user := iam.User{ Id: "USER" + reqId + "EXAMPLE", Arn: fmt.Sprintf("arn:aws:iam:::123456789012:user%s%s", path, name), Name: name, Path: path, } srv.users = append(srv.users, user) return iam.CreateUserResp{ RequestId: reqId, User: user, }, nil } func (srv *Server) getUser(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"UserName"}); err != nil { return nil, err } name := req.FormValue("UserName") index, err := srv.findUser(name) if err != nil { return nil, err } return iam.GetUserResp{RequestId: reqId, User: srv.users[index]}, nil } func (srv *Server) deleteUser(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"UserName"}); err != nil { return nil, err } name := req.FormValue("UserName") index, err := srv.findUser(name) if err != nil { return nil, err } copy(srv.users[index:], srv.users[index+1:]) srv.users = srv.users[:len(srv.users)-1] return iam.SimpleResp{RequestId: reqId}, nil } func (srv *Server) createAccessKey(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"UserName"}); err != nil { return nil, err } userName := req.FormValue("UserName") if _, err := srv.findUser(userName); err != nil { return nil, err } key := iam.AccessKey{ Id: fmt.Sprintf("%s%d", userName, len(srv.accessKeys)), Secret: "", UserName: userName, Status: "Active", } srv.accessKeys = append(srv.accessKeys, key) return iam.CreateAccessKeyResp{RequestId: reqId, AccessKey: key}, nil } func (srv *Server) deleteAccessKey(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"AccessKeyId", "UserName"}); err != nil { return nil, err } key := req.FormValue("AccessKeyId") index := -1 for i, ak := range srv.accessKeys { if ak.Id == key { index = i break } } if index < 0 { return nil, &iam.Error{ StatusCode: 404, Code: "NoSuchEntity", Message: "No such key.", } } copy(srv.accessKeys[index:], srv.accessKeys[index+1:]) srv.accessKeys = srv.accessKeys[:len(srv.accessKeys)-1] return iam.SimpleResp{RequestId: reqId}, nil } func (srv *Server) listAccessKeys(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"UserName"}); err != nil { return nil, err } userName := req.FormValue("UserName") if _, err := srv.findUser(userName); err != nil { return nil, err } var keys []iam.AccessKey for _, k := range srv.accessKeys { if k.UserName == userName { keys = append(keys, k) } } return iam.AccessKeysResp{ RequestId: reqId, AccessKeys: keys, }, nil } func (srv *Server) createGroup(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"GroupName"}); err != nil { return nil, err } name := req.FormValue("GroupName") path := req.FormValue("Path") for _, group := range srv.groups { if group.Name == name { return nil, &iam.Error{ StatusCode: 409, Code: "EntityAlreadyExists", Message: fmt.Sprintf("Group with name %s already exists.", name), } } } group := iam.Group{ Id: "GROUP " + reqId + "EXAMPLE", Arn: fmt.Sprintf("arn:aws:iam:::123456789012:group%s%s", path, name), Name: name, Path: path, } srv.groups = append(srv.groups, group) return iam.CreateGroupResp{ RequestId: reqId, Group: group, }, nil } func (srv *Server) listGroups(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { pathPrefix := req.FormValue("PathPrefix") if pathPrefix == "" { return iam.GroupsResp{ RequestId: reqId, Groups: srv.groups, }, nil } var groups []iam.Group for _, group := range srv.groups { if strings.HasPrefix(group.Path, pathPrefix) { groups = append(groups, group) } } return iam.GroupsResp{ RequestId: reqId, Groups: groups, }, nil } func (srv *Server) deleteGroup(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"GroupName"}); err != nil { return nil, err } name := req.FormValue("GroupName") index := -1 for i, group := range srv.groups { if group.Name == name { index = i break } } if index == -1 { return nil, &iam.Error{ StatusCode: 404, Code: "NoSuchEntity", Message: fmt.Sprintf("The group with name %s cannot be found.", name), } } copy(srv.groups[index:], srv.groups[index+1:]) srv.groups = srv.groups[:len(srv.groups)-1] return iam.SimpleResp{RequestId: reqId}, nil } func (srv *Server) putUserPolicy(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"UserName", "PolicyDocument", "PolicyName"}); err != nil { return nil, err } var exists bool policyName := req.FormValue("PolicyName") userName := req.FormValue("UserName") for _, policy := range srv.userPolicies { if policyName == policy.Name && userName == policy.UserName { exists = true break } } if !exists { policy := iam.UserPolicy{ Name: policyName, UserName: userName, Document: req.FormValue("PolicyDocument"), } var dumb interface{} if err := json.Unmarshal([]byte(policy.Document), &dumb); err != nil { return nil, &iam.Error{ StatusCode: 400, Code: "MalformedPolicyDocument", Message: "Malformed policy document", } } srv.userPolicies = append(srv.userPolicies, policy) } return iam.SimpleResp{RequestId: reqId}, nil } func (srv *Server) deleteUserPolicy(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"UserName", "PolicyName"}); err != nil { return nil, err } policyName := req.FormValue("PolicyName") userName := req.FormValue("UserName") index := -1 for i, policy := range srv.userPolicies { if policyName == policy.Name && userName == policy.UserName { index = i break } } if index < 0 { return nil, &iam.Error{ StatusCode: 404, Code: "NoSuchEntity", Message: "No such user policy", } } copy(srv.userPolicies[index:], srv.userPolicies[index+1:]) srv.userPolicies = srv.userPolicies[:len(srv.userPolicies)-1] return iam.SimpleResp{RequestId: reqId}, nil } func (srv *Server) getUserPolicy(w http.ResponseWriter, req *http.Request, reqId string) (interface{}, error) { if err := srv.validate(req, []string{"UserName", "PolicyName"}); err != nil { return nil, err } policyName := req.FormValue("PolicyName") userName := req.FormValue("UserName") index := -1 for i, policy := range srv.userPolicies { if policyName == policy.Name && userName == policy.UserName { index = i break } } if index < 0 { return nil, &iam.Error{ StatusCode: 404, Code: "NoSuchEntity", Message: "No such user policy", } } return iam.GetUserPolicyResp{ Policy: srv.userPolicies[index], RequestId: reqId, }, nil } func (srv *Server) findUser(userName string) (int, error) { var ( err error index = -1 ) for i, user := range srv.users { if user.Name == userName { index = i break } } if index < 0 { err = &iam.Error{ StatusCode: 404, Code: "NoSuchEntity", Message: fmt.Sprintf("The user with name %s cannot be found.", userName), } } return index, err } // Validates the presence of required request parameters. func (srv *Server) validate(req *http.Request, required []string) error { for _, r := range required { if req.FormValue(r) == "" { return &iam.Error{ StatusCode: 400, Code: "InvalidParameterCombination", Message: fmt.Sprintf("%s is required.", r), } } } return nil } var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) (interface{}, error){ "CreateUser": (*Server).createUser, "DeleteUser": (*Server).deleteUser, "GetUser": (*Server).getUser, "CreateAccessKey": (*Server).createAccessKey, "DeleteAccessKey": (*Server).deleteAccessKey, "ListAccessKeys": (*Server).listAccessKeys, "PutUserPolicy": (*Server).putUserPolicy, "DeleteUserPolicy": (*Server).deleteUserPolicy, "GetUserPolicy": (*Server).getUserPolicy, "CreateGroup": (*Server).createGroup, "DeleteGroup": (*Server).deleteGroup, "ListGroups": (*Server).listGroups, } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/iam/sign.go������������������������������������������������0000644�0000153�0000161�00000001664�12321735726�022146� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iam import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "launchpad.net/goamz/aws" "sort" "strings" ) // ---------------------------------------------------------------------------- // Version 2 signing (http://goo.gl/RSRp5) var b64 = base64.StdEncoding func sign(auth aws.Auth, method, path string, params map[string]string, host string) { params["AWSAccessKeyId"] = auth.AccessKey params["SignatureVersion"] = "2" params["SignatureMethod"] = "HmacSHA256" var sarray []string for k, v := range params { sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v)) } sort.StringSlice(sarray).Sort() joined := strings.Join(sarray, "&") payload := method + "\n" + host + "\n" + path + "\n" + joined hash := hmac.New(sha256.New, []byte(auth.SecretKey)) hash.Write([]byte(payload)) signature := make([]byte, b64.EncodedLen(hash.Size())) b64.Encode(signature, hash.Sum(nil)) params["Signature"] = string(signature) } ����������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/iam/iamt_test.go�������������������������������������������0000644�0000153�0000161�00000001453�12321735726�023173� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iam_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/iam" "launchpad.net/goamz/iam/iamtest" . "launchpad.net/gocheck" ) // LocalServer represents a local ec2test fake server. type LocalServer struct { auth aws.Auth region aws.Region srv *iamtest.Server } func (s *LocalServer) SetUp(c *C) { srv, err := iamtest.NewServer() c.Assert(err, IsNil) c.Assert(srv, NotNil) s.srv = srv s.region = aws.Region{IAMEndpoint: srv.URL()} } // LocalServerSuite defines tests that will run // against the local iamtest server. It includes // tests from ClientTests. type LocalServerSuite struct { srv LocalServer ClientTests } var _ = Suite(&LocalServerSuite{}) func (s *LocalServerSuite) SetUpSuite(c *C) { s.srv.SetUp(c) s.ClientTests.iam = iam.New(s.srv.auth, s.srv.region) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goamz/iam/iami_test.go�������������������������������������������0000644�0000153�0000161�00000015064�12321735726�023163� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package iam_test import ( "launchpad.net/goamz/aws" "launchpad.net/goamz/iam" "launchpad.net/goamz/testutil" . "launchpad.net/gocheck" "net/url" ) // AmazonServer represents an Amazon AWS server. type AmazonServer struct { auth aws.Auth } func (s *AmazonServer) SetUp(c *C) { auth, err := aws.EnvAuth() if err != nil { c.Fatal(err) } s.auth = auth } var _ = Suite(&AmazonClientSuite{}) // AmazonClientSuite tests the client against a live AWS server. type AmazonClientSuite struct { srv AmazonServer ClientTests } func (s *AmazonClientSuite) SetUpSuite(c *C) { if !testutil.Amazon { c.Skip("AmazonClientSuite tests not enabled") } s.srv.SetUp(c) s.iam = iam.New(s.srv.auth, aws.USEast) } // ClientTests defines integration tests designed to test the client. // It is not used as a test suite in itself, but embedded within // another type. type ClientTests struct { iam *iam.IAM } func (s *ClientTests) TestCreateAndDeleteUser(c *C) { createResp, err := s.iam.CreateUser("gopher", "/gopher/") c.Assert(err, IsNil) getResp, err := s.iam.GetUser("gopher") c.Assert(err, IsNil) c.Assert(createResp.User, DeepEquals, getResp.User) _, err = s.iam.DeleteUser("gopher") c.Assert(err, IsNil) } func (s *ClientTests) TestCreateUserError(c *C) { _, err := s.iam.CreateUser("gopher", "/gopher/") c.Assert(err, IsNil) defer s.iam.DeleteUser("gopher") _, err = s.iam.CreateUser("gopher", "/") iamErr, ok := err.(*iam.Error) c.Assert(ok, Equals, true) c.Assert(iamErr.StatusCode, Equals, 409) c.Assert(iamErr.Code, Equals, "EntityAlreadyExists") c.Assert(iamErr.Message, Equals, "User with name gopher already exists.") } func (s *ClientTests) TestDeleteUserError(c *C) { _, err := s.iam.DeleteUser("gopher") iamErr, ok := err.(*iam.Error) c.Assert(ok, Equals, true) c.Assert(iamErr.StatusCode, Equals, 404) c.Assert(iamErr.Code, Equals, "NoSuchEntity") c.Assert(iamErr.Message, Equals, "The user with name gopher cannot be found.") } func (s *ClientTests) TestGetUserError(c *C) { _, err := s.iam.GetUser("gopher") iamErr, ok := err.(*iam.Error) c.Assert(ok, Equals, true) c.Assert(iamErr.StatusCode, Equals, 404) c.Assert(iamErr.Code, Equals, "NoSuchEntity") c.Assert(iamErr.Message, Equals, "The user with name gopher cannot be found.") } func (s *ClientTests) TestCreateListAndDeleteAccessKey(c *C) { createUserResp, err := s.iam.CreateUser("gopher", "/gopher/") c.Assert(err, IsNil) defer s.iam.DeleteUser(createUserResp.User.Name) createKeyResp, err := s.iam.CreateAccessKey(createUserResp.User.Name) c.Assert(err, IsNil) listKeyResp, err := s.iam.AccessKeys(createUserResp.User.Name) c.Assert(err, IsNil) c.Assert(listKeyResp.AccessKeys, HasLen, 1) createKeyResp.AccessKey.Secret = "" c.Assert(listKeyResp.AccessKeys[0], DeepEquals, createKeyResp.AccessKey) _, err = s.iam.DeleteAccessKey(createKeyResp.AccessKey.Id, createUserResp.User.Name) c.Assert(err, IsNil) } func (s *ClientTests) TestCreateAccessKeyError(c *C) { _, err := s.iam.CreateAccessKey("unknowngopher") c.Assert(err, NotNil) iamErr, ok := err.(*iam.Error) c.Assert(ok, Equals, true) c.Assert(iamErr.StatusCode, Equals, 404) c.Assert(iamErr.Code, Equals, "NoSuchEntity") c.Assert(iamErr.Message, Equals, "The user with name unknowngopher cannot be found.") } func (s *ClientTests) TestListAccessKeysUserNotFound(c *C) { _, err := s.iam.AccessKeys("unknowngopher") c.Assert(err, NotNil) iamErr, ok := err.(*iam.Error) c.Assert(ok, Equals, true) c.Assert(iamErr.StatusCode, Equals, 404) c.Assert(iamErr.Code, Equals, "NoSuchEntity") c.Assert(iamErr.Message, Equals, "The user with name unknowngopher cannot be found.") } func (s *ClientTests) TestListAccessKeysUserWithoutKeys(c *C) { createUserResp, err := s.iam.CreateUser("gopher", "/") c.Assert(err, IsNil) defer s.iam.DeleteUser(createUserResp.User.Name) resp, err := s.iam.AccessKeys(createUserResp.User.Name) c.Assert(err, IsNil) c.Assert(resp.AccessKeys, HasLen, 0) } func (s *ClientTests) TestCreateListAndDeleteGroup(c *C) { cResp1, err := s.iam.CreateGroup("Finances", "/finances/") c.Assert(err, IsNil) cResp2, err := s.iam.CreateGroup("DevelopmentManagers", "/development/managers/") c.Assert(err, IsNil) lResp, err := s.iam.Groups("/development/") c.Assert(err, IsNil) c.Assert(lResp.Groups, HasLen, 1) c.Assert(cResp2.Group, DeepEquals, lResp.Groups[0]) lResp, err = s.iam.Groups("") c.Assert(err, IsNil) c.Assert(lResp.Groups, HasLen, 2) if lResp.Groups[0].Name == cResp1.Group.Name { c.Assert([]iam.Group{cResp1.Group, cResp2.Group}, DeepEquals, lResp.Groups) } else { c.Assert([]iam.Group{cResp2.Group, cResp1.Group}, DeepEquals, lResp.Groups) } _, err = s.iam.DeleteGroup("DevelopmentManagers") c.Assert(err, IsNil) lResp, err = s.iam.Groups("/development/") c.Assert(err, IsNil) c.Assert(lResp.Groups, HasLen, 0) _, err = s.iam.DeleteGroup("Finances") c.Assert(err, IsNil) } func (s *ClientTests) TestCreateGroupError(c *C) { _, err := s.iam.CreateGroup("Finances", "/finances/") c.Assert(err, IsNil) defer s.iam.DeleteGroup("Finances") _, err = s.iam.CreateGroup("Finances", "/something-else/") iamErr, ok := err.(*iam.Error) c.Assert(ok, Equals, true) c.Assert(iamErr.StatusCode, Equals, 409) c.Assert(iamErr.Code, Equals, "EntityAlreadyExists") c.Assert(iamErr.Message, Equals, "Group with name Finances already exists.") } func (s *ClientTests) TestDeleteGroupError(c *C) { _, err := s.iam.DeleteGroup("Finances") iamErr, ok := err.(*iam.Error) c.Assert(ok, Equals, true) c.Assert(iamErr.StatusCode, Equals, 404) c.Assert(iamErr.Code, Equals, "NoSuchEntity") c.Assert(iamErr.Message, Equals, "The group with name Finances cannot be found.") } func (s *ClientTests) TestPutGetAndDeleteUserPolicy(c *C) { userResp, err := s.iam.CreateUser("gopher", "/gopher/") c.Assert(err, IsNil) defer s.iam.DeleteUser(userResp.User.Name) document := `{ "Statement": [ { "Action": [ "s3:*" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::8shsns19s90ajahadsj/*", "arn:aws:s3:::8shsns19s90ajahadsj" ] }] }` _, err = s.iam.PutUserPolicy(userResp.User.Name, "EverythingS3", document) c.Assert(err, IsNil) resp, err := s.iam.GetUserPolicy(userResp.User.Name, "EverythingS3") c.Assert(err, IsNil) c.Assert(resp.Policy.Name, Equals, "EverythingS3") c.Assert(resp.Policy.UserName, Equals, userResp.User.Name) gotDocument, err := url.QueryUnescape(resp.Policy.Document) c.Assert(err, IsNil) c.Assert(gotDocument, Equals, document) _, err = s.iam.DeleteUserPolicy(userResp.User.Name, "EverythingS3") c.Assert(err, IsNil) _, err = s.iam.GetUserPolicy(userResp.User.Name, "EverythingS3") c.Assert(err, NotNil) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/godeps/����������������������������������������������������������0000755�0000153�0000161�00000000000�12321736007�020237� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/godeps/.lbox.check�����������������������������������������������0000755�0000153�0000161�00000001022�12321736007�022256� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash set -e BADFMT=`find * -name '*.go' | xargs gofmt -l` if [ -n "$BADFMT" ]; then BADFMT=`echo "$BADFMT" | sed "s/^/ /"` echo -e "gofmt is sad:\n\n$BADFMT" exit 1 fi VERSION=`go version | awk '{print $3}'` if [ $VERSION == 'devel' ]; then go tool vet \ -methods \ -printf \ -rangeloops \ -printfuncs 'ErrorContextf:1,notFoundf:0,badReqErrorf:0,Commitf:0,Snapshotf:0,Debugf:0' \ . fi # check this branch builds cleanly # uncomment when there's more than one command! # go build launchpad.net/godeps/... ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/godeps/.lbox�����������������������������������������������������0000644�0000153�0000161�00000000041�12321736007�021177� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������propose -cr -for lp:godeps/trunk �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/godeps/godeps.go�������������������������������������������������0000644�0000153�0000161�00000037237�12321736007�022063� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "bufio" "bytes" "encoding/hex" "errors" "flag" "fmt" "go/build" "io" "os" "os/exec" "path/filepath" "regexp" "sort" "strings" "sync" ) var revFile = flag.String("u", "", "update dependencies") var testDeps = flag.Bool("t", false, "include testing dependencies in output") var printCommands = flag.Bool("x", false, "show executed commands") var dryRun = flag.Bool("n", false, "print but do not execute update commands") var exitCode = 0 var buildContext = build.Default var usage = ` Usage: godeps [-x] [-t] [pkg ...] godeps -u file [-x] [-n] In the first form of usage, godeps prints to standard output a list of all the source dependencies of the named packages (or the package in the current directory if none is given). If there is ambiguity in the source-control systems used, godeps will print all the available versions and an error, exiting with a false status. It is up to the user to remove lines from the output to make the output suitable for input to godeps -u. In the second form, godeps updates source to versions specified by the -u file argument, which should hold version information in the same form printed by godeps. It is an error if the file contains more than one line for the same package root. If the -n flag is specified, update commands will be printed but not executed. `[1:] func main() { flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s", usage) os.Exit(2) } flag.Parse() if *revFile != "" { if flag.NArg() != 0 { flag.Usage() } update(*revFile) } else { pkgs := flag.Args() if len(pkgs) == 0 { pkgs = []string{"."} } for _, info := range list(pkgs, *testDeps) { fmt.Println(info) } } os.Exit(exitCode) } func update(file string) { projects, err := parseDepFile(file) if err != nil { errorf("cannot parse %q: %v", file, err) return } // First get info on all the projects, make sure their working // directories are all clean and prune out the ones which // don't need updating. failed := false for proj, info := range projects { currentInfo, err := info.vcs.Info(info.dir) if err != nil { errorf("cannot get information on %q: %v", info.dir, err) failed = true continue } if !currentInfo.clean { errorf("%q is not clean", info.dir) failed = true continue } if currentInfo.revid == info.revid { // No need to update. delete(projects, proj) } } if failed { return } for _, info := range projects { err := info.vcs.Update(info.dir, info.revid) if err != nil { errorf("cannot update %q: %v", info.dir, err) return } fmt.Printf("%q now at %s\n", info.dir, info.revid) } } func parseDepFile(file string) (map[string]*depInfo, error) { f, err := os.Open(file) if err != nil { return nil, err } defer f.Close() deps := make(map[string]*depInfo) r := bufio.NewReader(f) for { line, err := r.ReadString('\n') if err == io.EOF { break } if err != nil { return nil, fmt.Errorf("read error: %v", err) } if line[len(line)-1] == '\n' { line = line[0 : len(line)-1] } info, err := parseDepInfo(line) if err != nil { return nil, fmt.Errorf("cannot parse %q: %v", line, err) } if deps[info.project] != nil { return nil, fmt.Errorf("project %q has more than one entry", info.project) } deps[info.project] = info info.dir, err = projectToDir(info.project) if err != nil { return nil, fmt.Errorf("cannot find directory for %q: %v", info.project, err) } } return deps, nil } func list(pkgs []string, testDeps bool) []*depInfo { infoByDir := make(map[string][]*depInfo) // We want to ignore the go core source and the projects // for the root packages. Do this by getting leaf dependency info // for all those things and adding them to an ignore list. ignoreDirs := map[string]bool{ filepath.Clean(buildContext.GOROOT): true, } for _, pkgPath := range pkgs { pkg, err := buildContext.Import(pkgPath, ".", build.FindOnly) if err != nil { errorf("cannot find %q: %v", pkgPath, err) continue } if !findVCSInfo(pkg.Dir, infoByDir) { ignoreDirs[pkg.Dir] = true } } // Ignore the packages directly specified on the // command line, as we want to print the versions // of their dependencies, not their versions themselves. for dir := range infoByDir { ignoreDirs[dir] = true } walkDeps(pkgs, testDeps, func(pkg *build.Package, err error) bool { if err != nil { errorf("cannot import %q: %v", pkg.Name, err) return false } if !findVCSInfo(pkg.Dir, infoByDir) && !ignoreDirs[pkg.Dir] { errorf("no version control system found for %q", pkg.Dir) } return true }) // We make a new map because dependency information // can be ambiguous not only through there being two // or more metadata directories in one directory, but // also because there can be different packages with // the same project name under different GOPATH // elements. infoByProject := make(map[string][]*depInfo) for dir, infos := range infoByDir { proj, err := dirToProject(dir) if err != nil && !ignoreDirs[dir] { errorf("cannot get relative repo root for %q: %v", dir, err) continue } infoByProject[proj] = append(infoByProject[proj], infos...) } var deps depInfoSlice for proj, infos := range infoByProject { if len(infos) > 1 { for _, info := range infos { errorf("ambiguous VCS (%s) for %q at %q", info.vcs.Kind(), proj, info.dir) } } for _, info := range infos { if ignoreDirs[info.dir] { continue } if !info.clean { errorf("%s repository at %q is not clean; revision id may not reflect the code", info.vcs.Kind(), info.dir) } info.project = proj deps = append(deps, info) } } sort.Sort(deps) return deps } func dirToProject(dir string) (string, error) { if ok, _ := relativeToParent(buildContext.GOROOT, dir); ok { return "go", nil } for _, p := range filepath.SplitList(buildContext.GOPATH) { if ok, rel := relativeToParent(filepath.Join(p, "src"), dir); ok { return rel, nil } } return "", fmt.Errorf("project directory not found in GOPATH or GOROOT") } func projectToDir(proj string) (string, error) { for _, p := range filepath.SplitList(buildContext.GOPATH) { dir := filepath.Join(p, "src", filepath.FromSlash(proj)) info, err := os.Stat(dir) if err == nil && info.IsDir() { return dir, nil } } return "", fmt.Errorf("not found in GOPATH") } // relativeToParent returns whether the child // path is under (or the same as) the parent path, // and if so returns the trailing portion of the // child path that is under the parent path. func relativeToParent(parent, child string) (ok bool, rel string) { parent = filepath.Clean(parent) child = filepath.Clean(child) if parent == child { return true, "" } if !strings.HasPrefix(child, parent+"/") { return false, "" } return true, child[len(parent)+1:] } type depInfoSlice []*depInfo func (s depInfoSlice) Len() int { return len(s) } func (s depInfoSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s depInfoSlice) Less(i, j int) bool { p, q := s[i], s[j] if p.project != q.project { return p.project < q.project } if p.vcs.Kind() != q.vcs.Kind() { return p.vcs.Kind() < q.vcs.Kind() } return p.dir < q.dir } type depInfo struct { project string dir string vcs VCS VCSInfo } func (info *depInfo) String() string { return fmt.Sprintf("%s\t%s\t%s\t%s", info.project, info.vcs.Kind(), info.revid, info.revno) } // parseDepInfo parses a dependency info line as printed by // depInfo.String. func parseDepInfo(s string) (*depInfo, error) { fields := strings.Split(s, "\t") if len(fields) != 4 { return nil, fmt.Errorf("expected 4 tab-separated fields, got %d", len(fields)) } info := &depInfo{ project: fields[0], vcs: kindToVCS[fields[1]], VCSInfo: VCSInfo{ revid: fields[2], revno: fields[3], }, } if info.vcs == nil { return nil, fmt.Errorf("unknown VCS kind %q", fields[1]) } if info.project == "" { return nil, fmt.Errorf("empty project field") } if info.revid == "" { return nil, fmt.Errorf("empty revision id") } return info, nil } // findVCSInfo searches for VCS info for the given directory // and adds any found to infoByDir, searching each parent // directory in turn. It returns whether any information was // found. func findVCSInfo(dir string, infoByDir map[string][]*depInfo) bool { dir, err := filepath.Abs(dir) if err != nil { errorf("cannot find absolute path of %q", dir) return false } dirs := parents(dir) // Check from the root down that there is no // existing information for any parent directory. for i := len(dirs) - 1; i >= 0; i-- { if info := infoByDir[dirs[i]]; info != nil { return true } } // Check from dir upwards to find a VCS directory for _, dir := range dirs { nfound := 0 for metaDir, vcs := range metadataDirs { if dirInfo, err := os.Stat(filepath.Join(dir, metaDir)); err == nil && dirInfo.IsDir() { info, err := vcs.Info(dir) if err != nil { errorf("cannot get version information for %q: %v", dir, err) continue } infoByDir[dir] = append(infoByDir[dir], &depInfo{ dir: dir, vcs: vcs, VCSInfo: info, }) nfound++ } } if nfound > 0 { return true } } return false } // parents returns the given path and all its parents. // For instance, given /usr/rog/foo, // it will return []string{"/usr/rog/foo", "/usr/rog", "/usr", "/"} func parents(path string) []string { var all []string path = filepath.Clean(path) for { all = append(all, path) parent := filepath.Dir(path) if parent == path { break } path = parent } return all } type walkContext struct { checked map[string]bool includeTests bool visit func(*build.Package, error) bool } // walkDeps traverses the import dependency tree of the // given package, calling the given function for each dependency, // including the package for pkgPath itself. If the function // returns true, the dependencies of the given package // will themselves be visited. // The includeTests flag specifies whether test-related dependencies // will be considered when walking the hierarchy. // Each package will be visited at most once. func walkDeps(paths []string, includeTests bool, visit func(*build.Package, error) bool) { ctxt := &walkContext{ checked: make(map[string]bool), includeTests: includeTests, visit: visit, } for _, path := range paths { ctxt.walkDeps(path) } } func (ctxt *walkContext) walkDeps(pkgPath string) { if pkgPath == "C" { return } if ctxt.checked[pkgPath] { // The package has already been, is or being, checked return } // BUG(rog) This ignores files that are excluded by // as part of the current build. Unfortunately // we can't use UseAllFiles as that includes other // files that break the build (for instance unrelated // helper commands in package main). // The solution is to avoid using build.Import but it's convenient // at the moment. pkg, err := buildContext.Import(pkgPath, ".", 0) ctxt.checked[pkg.ImportPath] = true descend := ctxt.visit(pkg, err) if err != nil || !descend { return } // N.B. is it worth eliminating duplicates here? var allImports []string allImports = append(allImports, pkg.Imports...) if ctxt.includeTests { allImports = append(allImports, pkg.TestImports...) allImports = append(allImports, pkg.XTestImports...) } for _, impPath := range allImports { ctxt.walkDeps(impPath) } } type VCS interface { Kind() string Info(dir string) (VCSInfo, error) Update(dir, revid string) error } type VCSInfo struct { revid string revno string // optional clean bool } var metadataDirs = map[string]VCS{ ".bzr": bzrVCS{}, ".hg": hgVCS{}, ".git": gitVCS{}, } var kindToVCS = map[string]VCS{ "bzr": bzrVCS{}, "hg": hgVCS{}, "git": gitVCS{}, } type gitVCS struct{} func (gitVCS) Kind() string { return "git" } func (gitVCS) Info(dir string) (VCSInfo, error) { out, err := runCmd(dir, "git", "rev-parse", "HEAD") if err != nil { return VCSInfo{}, err } revid := strings.TrimSpace(out) // validate the revision hash revhash, err := hex.DecodeString(revid) if err != nil || len(revhash) == 0 { return VCSInfo{}, fmt.Errorf("git rev-parse provided invalid revision %q", revid) } // `git status --porcelain` outputs one line per changed or untracked file. out, err = runCmd(dir, "git", "status", "--porcelain") if err != nil { return VCSInfo{}, err } return VCSInfo{ revid: revid, // Empty output (with rc=0) indicates no changes in working copy. clean: out == "", }, nil } func (gitVCS) Update(dir string, revid string) error { _, err := runUpdateCmd(dir, "git", "checkout", revid) return err } type bzrVCS struct{} func (bzrVCS) Kind() string { return "bzr" } var validBzrInfo = regexp.MustCompile(`^([0-9]+) ([^ \t]+)$`) var shelveLine = regexp.MustCompile(`^[0-9]+ (shelves exist|shelf exists)\.`) func (bzrVCS) Info(dir string) (VCSInfo, error) { out, err := runCmd(dir, "bzr", "revision-info", "--tree") if err != nil { return VCSInfo{}, err } m := validBzrInfo.FindStringSubmatch(strings.TrimSpace(out)) if m == nil { return VCSInfo{}, fmt.Errorf("bzr revision-info has unexpected result %q", out) } out, err = runCmd(dir, "bzr", "status", "-S") if err != nil { return VCSInfo{}, err } clean := true statusLines := strings.Split(out, "\n") for _, line := range statusLines { if line == "" || shelveLine.MatchString(line) { continue } clean = false break } return VCSInfo{ revid: m[2], revno: m[1], clean: clean, }, nil } func (bzrVCS) Update(dir string, revid string) error { _, err := runUpdateCmd(dir, "bzr", "update", "-r", "revid:"+revid) return err } var validHgInfo = regexp.MustCompile(`^([a-f0-9]+) ([0-9]+)$`) type hgVCS struct{} func (hgVCS) Info(dir string) (VCSInfo, error) { out, err := runCmd(dir, "hg", "log", "-l", "1", "-r", ".", "--template", "{node} {rev}") if err != nil { return VCSInfo{}, err } m := validHgInfo.FindStringSubmatch(strings.TrimSpace(out)) if m == nil { return VCSInfo{}, fmt.Errorf("hg identify has unexpected result %q", out) } out, err = runCmd(dir, "hg", "status") if err != nil { return VCSInfo{}, err } // TODO(rog) check that tree is clean return VCSInfo{ revid: m[1], revno: m[2], clean: out == "", }, nil } func (hgVCS) Kind() string { return "hg" } func (hgVCS) Update(dir string, revid string) error { _, err := runUpdateCmd(dir, "hg", "update", revid) return err } func runUpdateCmd(dir string, name string, args ...string) (string, error) { if *dryRun { printShellCommand(dir, name, args) return "", nil } return runCmd(dir, name, args...) } func runCmd(dir string, name string, args ...string) (string, error) { var outData, errData bytes.Buffer if *printCommands { printShellCommand(dir, name, args) } c := exec.Command(name, args...) c.Stdout = &outData c.Stderr = &errData c.Dir = dir err := c.Run() if err == nil { return outData.String(), nil } if _, ok := err.(*exec.ExitError); ok && errData.Len() > 0 { return "", errors.New(strings.TrimSpace(errData.String())) } return "", fmt.Errorf("cannot run %q: %v", append([]string{name}, args...), err) } var errorf = func(f string, a ...interface{}) { fmt.Fprintf(os.Stderr, "godeps: %s\n", fmt.Sprintf(f, a...)) exitCode = 1 } var ( outputDirMutex sync.Mutex outputDir string ) func printShellCommand(dir, name string, args []string) { outputDirMutex.Lock() defer outputDirMutex.Unlock() if dir != outputDir { fmt.Fprintf(os.Stderr, "cd %s\n", shquote(dir)) outputDir = dir } var buf bytes.Buffer buf.WriteString(name) for _, arg := range args { buf.WriteString(" ") buf.WriteString(shquote(arg)) } fmt.Fprintf(os.Stderr, "%s\n", buf.Bytes()) } func shquote(s string) string { // single-quote becomes single-quote, double-quote, single-quote, double-quote, single-quote return `'` + strings.Replace(s, `'`, `'"'"'`, -1) + `'` } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/godeps/godeps_test.go��������������������������������������������0000644�0000153�0000161�00000015276�12321736007�023121� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package main import ( "bufio" "fmt" "go/build" . "launchpad.net/gocheck" "os" "path/filepath" "sort" "strings" "sync" "testing" ) func TestPackage(t *testing.T) { TestingT(t) } type suite struct { savedBuildContext build.Context savedErrorf func(string, ...interface{}) errors []string } var _ = Suite(&suite{}) func (s *suite) SetUpTest(c *C) { s.savedBuildContext = buildContext s.savedErrorf = errorf errorf = func(f string, a ...interface{}) { s.errors = append(s.errors, fmt.Sprintf(f, a...)) } } func (s *suite) TearDownTest(c *C) { buildContext = s.savedBuildContext errorf = s.savedErrorf s.errors = nil } type listResult struct { project string } var listTests = []struct { about string args []string testDeps bool result string errors []string }{{ about: "easy case", args: []string{"foo/foo1"}, result: ` bar bzr 1 foo hg 0 foo/foo2 hg 0 `[1:], }, { about: "with test dependencies", args: []string{"foo/foo1"}, testDeps: true, result: ` bar bzr 1 baz bzr 1 foo hg 0 foo/foo2 hg 0 khroomph bzr 1 `[1:], }, { about: "ambiguous dependency", args: []string{"ambiguous1"}, result: ` multirepo bzr 1 multirepo hg 0 `[1:], errors: []string{ `ambiguous VCS (bzr) for "multirepo" at "$tmp/p1/src/multirepo"`, `ambiguous VCS (hg) for "multirepo" at "$tmp/p1/src/multirepo"`, `bzr repository at "$tmp/p1/src/multirepo" is not clean; revision id may not reflect the code`, `hg repository at "$tmp/p1/src/multirepo" is not clean; revision id may not reflect the code`, }}, { about: "ambiguous dependency across different GOPATH elements", args: []string{"ambiguous2"}, result: ` multirepo bzr 1 multirepo hg 0 multirepo hg 0 `[1:], errors: []string{ `ambiguous VCS (bzr) for "multirepo" at "$tmp/p1/src/multirepo"`, `ambiguous VCS (hg) for "multirepo" at "$tmp/p1/src/multirepo"`, `ambiguous VCS (hg) for "multirepo" at "$tmp/p2/src/multirepo"`, `bzr repository at "$tmp/p1/src/multirepo" is not clean; revision id may not reflect the code`, `hg repository at "$tmp/p1/src/multirepo" is not clean; revision id may not reflect the code`, }}, { about: "unclean hg", args: []string{"hgunclean-root"}, result: ` hgunclean hg 0 `[1:], errors: []string{ `hg repository at "$tmp/p1/src/hgunclean" is not clean; revision id may not reflect the code`, }, }} func (s *suite) TestList(c *C) { dir := c.MkDir() gopath := []string{filepath.Join(dir, "p1"), filepath.Join(dir, "p2")} writePackages(c, gopath[0], "v1", map[string]packageSpec{ "foo/foo1": { deps: []string{"foo/foo2"}, testDeps: []string{"baz/baz1"}, xTestDeps: []string{"khroomph/khr"}, }, "foo/foo2": { deps: []string{"bar/bar1"}, }, "baz/baz1": {}, "khroomph/khr": {}, "ambiguous1": { deps: []string{"multirepo/x"}, }, "ambiguous2": { deps: []string{"multirepo/x", "multirepo/y"}, }, "multirepo/x": {}, "bzrunclean-root": { deps: []string{"bzrunclean"}, }, "bzrunclean": {}, "hgunclean-root": { deps: []string{"hgunclean"}, }, "hgunclean": {}, }) writePackages(c, gopath[1], "v1", map[string]packageSpec{ "bar/bar1": { deps: []string{"foo/foo3", "bar/bar2"}, }, "bar/bar2": { deps: []string{"bar/bar3"}, }, "bar/bar3": {}, "bar/bar4": {}, "foo/foo1": {}, "foo/foo3": {}, "multirepo/y": {}, }) var wg sync.WaitGroup goInitRepo := func(kind string, rootDir string, pkg string) { wg.Add(1) go func() { defer wg.Done() initRepo(c, kind, rootDir, pkg) }() } goInitRepo("bzr", gopath[0], "foo/foo1") goInitRepo("hg", gopath[0], "foo/foo2") goInitRepo("bzr", gopath[0], "baz") goInitRepo("bzr", gopath[0], "khroomph") goInitRepo("bzr", gopath[0], "ambiguous1") // deliberately omit ambiguous2 goInitRepo("bzr", gopath[0], "multirepo") goInitRepo("hg", gopath[0], "multirepo") goInitRepo("bzr", gopath[0], "bzrunclean") goInitRepo("hg", gopath[0], "hgunclean") goInitRepo("bzr", gopath[1], "bar") goInitRepo("hg", gopath[1], "foo") goInitRepo("hg", gopath[1], "multirepo") wg.Wait() // unclean repos for _, pkg := range []string{"hgunclean", "bzrunclean"} { f, err := os.Create(filepath.Join(pkgDir(gopath[0], pkg), "extra")) c.Assert(err, IsNil) f.Close() } buildContext.GOPATH = strings.Join(gopath, string(filepath.ListSeparator)) for i, test := range listTests { c.Logf("test %d. %s", i, test.about) s.errors = nil deps := list(test.args, test.testDeps) c.Assert(s.errors, HasLen, len(test.errors)) for i, e := range s.errors { s.errors[i] = strings.Replace(e, dir, "$tmp", -1) } sort.Strings(test.errors) for i, e := range s.errors { c.Check(e, Equals, test.errors[i], Commentf("error %d", i)) } // Check that rev ids are non-empty, but don't check specific values. result := "" for i, info := range deps { c.Check(info.revid, Not(Equals), "", Commentf("info %d: %v", i, info)) info.revid = "" result += fmt.Sprintf("%s %s %s\n", info.project, info.vcs.Kind(), info.revno) } c.Check(result, Equals, test.result) } } func pkgDir(rootDir string, pkg string) string { return filepath.Join(rootDir, "src", filepath.FromSlash(pkg)) } func initRepo(c *C, kind, rootDir, pkg string) { // This relies on the fact that hg, bzr and git // all use the same command to initialize a directory. dir := pkgDir(rootDir, pkg) _, err := runCmd(dir, kind, "init") if !c.Check(err, IsNil) { return } _, err = runCmd(dir, kind, "add", dir) if !c.Check(err, IsNil) { return } commitRepo(c, dir, kind, "initial commit") } func commitRepo(c *C, dir, kind string, message string) { // This relies on the fact that hg, bzr and git // all use the same command to initialize a directory. _, err := runCmd(dir, kind, "commit", "-m", message) c.Check(err, IsNil) } type packageSpec struct { deps []string testDeps []string xTestDeps []string } func writePackages(c *C, rootDir string, version string, pkgs map[string]packageSpec) { for name, pkg := range pkgs { dir := pkgDir(rootDir, name) err := os.MkdirAll(dir, 0777) c.Assert(err, IsNil) writeFile := func(fileName, pkgIdent string, deps []string) { err := writePackageFile(filepath.Join(dir, fileName), pkgIdent, version, deps) c.Assert(err, IsNil) } writeFile("x.go", "x", pkg.deps) writeFile("internal_test.go", "x", pkg.testDeps) writeFile("x_test.go", "x_test", pkg.xTestDeps) } } func writePackageFile(fileName string, pkgIdent string, version string, deps []string) error { f, err := os.Create(fileName) if err != nil { return err } defer f.Close() w := bufio.NewWriter(f) fmt.Fprintf(w, "package %s\nimport (\n", pkgIdent) for _, dep := range deps { fmt.Fprintf(w, "\t_ %q\n", dep) } fmt.Fprintf(w, ")\n") fmt.Fprintf(w, "const Version = %q\n", version) return w.Flush() } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/������������������������������������������������������������0000755�0000153�0000161�00000000000�12321736016�017676� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/build.go����������������������������������������������������0000644�0000153�0000161�00000011543�12321735716�021336� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad import ( "fmt" "net/url" ) // A BuildState holds the state a package build can be found in. type BuildState string const ( BSNeedsBuilding BuildState = "Needs building" BSSuccessfullyBuilt BuildState = "Successfully built" BSFailedToBuild BuildState = "Failed to build" BSDependencyWait BuildState = "Dependency wait" BSChrootProblem BuildState = "Chroot problem" BSBuildForSupersededSource BuildState = "Build for superseded source" BSCurrentlyBuilding BuildState = "Currently building" BSFailedToUpload BuildState = "Failed to upload" BSCurrentlyUploading BuildState = "Currently uploading" ) // A Pocket represents the various distribution pockets where packages end up. type Pocket string const ( PocketAny Pocket = "" PocketRelease Pocket = "Release" PocketSecurity Pocket = "Security" PocketUpdates Pocket = "Updates" PocketProposed Pocket = "Proposed" PocketBackports Pocket = "Backports" ) // The Build type describes a package build. type Build struct { *Value } // The BuildList type represents a list of package Build objects. type BuildList struct { *Value } // For iterates over the list of package builds and calls f for each one. // If f returns a non-nil error, iteration will stop and the error will be // returned as the result of For. func (bl *BuildList) For(f func(b *Build) error) error { return bl.Value.For(func(v *Value) error { return f(&Build{v}) }) } // Build returns the identified package build. func (root *Root) Build(distro string, source string, version string, id int) (*Build, error) { distro = url.QueryEscape(distro) source = url.QueryEscape(source) version = url.QueryEscape(version) path := fmt.Sprintf("/%s/+source/%s/%s/+build/%d/", distro, source, version, id) v, err := root.Location(path).Get(nil) if err != nil { return nil, err } return &Build{v}, nil } // Title returns the build title. func (build *Build) Title() string { return build.StringField("title") } // Arch returns the architecture of build. func (build *Build) Arch() string { return build.StringField("arch_tag") } // Retry sends a failed build back to the builder farm. func (build *Build) Retry() error { _, err := build.Post(Params{"ws.op": "retry"}) return err } // WebPage returns the URL for accessing this build in a browser. func (build *Build) WebPage() string { return build.StringField("web_link") } // State returns the state of build. func (build *Build) State() BuildState { return BuildState(build.StringField("buildstate")) } // BuildLogURL returns the URL for the build log file. func (build *Build) BuildLogURL() string { return build.StringField("build_log_url") } // UploadLogURL returns the URL for the upload log if there was an upload failure. func (build *Build) UploadLogURL() string { return build.StringField("upload_log_url") } // Created returns the timestamp when the build farm job was created. func (build *Build) Created() string { return build.StringField("datecreated") } // Finished returns the timestamp when the build farm job was finished. func (build *Build) Finished() string { return build.StringField("datebuilt") } // The Publication type holds a source package's publication record. type Publication struct { *Value } // Publication returns the source publication record corresponding to build. func (build *Build) Publication() (*Publication, error) { v, err := build.Link("current_source_publication_link").Get(nil) if err != nil { return nil, err } return &Publication{v}, nil } // PackageName returns the name of the published source package. func (p *Publication) PackageName() string { return p.StringField("source_package_name") } // PackageName returns the version of the published source package. func (p *Publication) PackageVersion() string { return p.StringField("source_package_version") } // DistroSeries returns the distro series published into. func (p *Publication) DistroSeries() (*DistroSeries, error) { v, err := p.Link("distro_series_link").Get(nil) if err != nil { return nil, err } return &DistroSeries{v}, nil } // Archive returns the archive published into. func (p *Publication) Archive() (*Archive, error) { v, err := p.Link("archive_link").Get(nil) if err != nil { return nil, err } return &Archive{v}, nil } // Component returns the component name published into. func (p *Publication) Component() string { return p.StringField("component_name") } // PublicationList represents a list of Publication objects. type PublicationList struct { *Value } // For iterates over the list of publication objects and calls f for // each one. If f returns a non-nil error, iteration will stop and the // error will be returned as the result of For. func (list *PublicationList) For(f func(s *Publication) error) error { return list.Value.For(func(v *Value) error { return f(&Publication{v}) }) } �������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/project_test.go���������������������������������������������0000644�0000153�0000161�00000013471�12321735716�022746� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" ) func (s *ModelS) TestProject(c *C) { m := M{ "name": "thename", "display_name": "Display Name", "title": "Title", "summary": "Summary", "description": "Description", "web_link": "http://page", "development_focus_link": testServer.URL + "/focus_link", } project := &lpad.Project{lpad.NewValue(nil, "", "", m)} c.Assert(project.Name(), Equals, "thename") c.Assert(project.DisplayName(), Equals, "Display Name") c.Assert(project.Title(), Equals, "Title") c.Assert(project.Summary(), Equals, "Summary") c.Assert(project.Description(), Equals, "Description") c.Assert(project.WebPage(), Equals, "http://page") project.SetName("newname") project.SetDisplayName("New Display Name") project.SetTitle("New Title") project.SetSummary("New summary") project.SetDescription("New description") c.Assert(project.Name(), Equals, "newname") c.Assert(project.DisplayName(), Equals, "New Display Name") c.Assert(project.Title(), Equals, "New Title") c.Assert(project.Summary(), Equals, "New summary") c.Assert(project.Description(), Equals, "New description") testServer.PrepareResponse(200, jsonType, `{"name": "seriesname"}`) series, err := project.FocusSeries() c.Assert(err, IsNil) c.Assert(series.Name(), Equals, "seriesname") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/focus_link") } func (s *ModelS) TestMilestone(c *C) { m := M{ "name": "thename", "code_name": "thecodename", "title": "Title", "summary": "Summary", "date_targeted": "2011-08-31", "is_active": true, "web_link": "http://page", } ms := &lpad.Milestone{lpad.NewValue(nil, "", "", m)} c.Assert(ms.Name(), Equals, "thename") c.Assert(ms.CodeName(), Equals, "thecodename") c.Assert(ms.Title(), Equals, "Title") c.Assert(ms.Summary(), Equals, "Summary") c.Assert(ms.Date(), Equals, "2011-08-31") c.Assert(ms.Active(), Equals, true) c.Assert(ms.WebPage(), Equals, "http://page") ms.SetName("newname") ms.SetCodeName("newcodename") ms.SetTitle("New Title") ms.SetSummary("New summary") ms.SetDate("2011-09-01") ms.SetActive(false) c.Assert(ms.Name(), Equals, "newname") c.Assert(ms.CodeName(), Equals, "newcodename") c.Assert(ms.Title(), Equals, "New Title") c.Assert(ms.Summary(), Equals, "New summary") c.Assert(ms.Date(), Equals, "2011-09-01") c.Assert(ms.Active(), Equals, false) } func (s *ModelS) TestProjectSeries(c *C) { m := M{ "name": "thename", "title": "Title", "summary": "Summary", "is_active": true, "web_link": "http://page", "branch_link": testServer.URL + "/branch_link", } series := &lpad.ProjectSeries{lpad.NewValue(nil, "", "", m)} c.Assert(series.Name(), Equals, "thename") c.Assert(series.Title(), Equals, "Title") c.Assert(series.Summary(), Equals, "Summary") c.Assert(series.Active(), Equals, true) c.Assert(series.WebPage(), Equals, "http://page") series.SetName("newname") series.SetTitle("New Title") series.SetSummary("New summary") series.SetActive(false) c.Assert(series.Name(), Equals, "newname") c.Assert(series.Title(), Equals, "New Title") c.Assert(series.Summary(), Equals, "New summary") c.Assert(series.Active(), Equals, false) testServer.PrepareResponse(200, jsonType, `{"unique_name": "lp:thebranch"}`) b, err := series.Branch() c.Assert(err, IsNil) c.Assert(b.UniqueName(), Equals, "lp:thebranch") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/branch_link") b = &lpad.Branch{lpad.NewValue(nil, "", "/new_branch_link", nil)} series.SetBranch(b) c.Assert(series.StringField("branch_link"), Equals, "/new_branch_link") } func (s *ModelS) TestRootProject(c *C) { data := `{ "name": "Name", "title": "Title", "description": "Description" }` testServer.PrepareResponse(200, jsonType, data) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} project, err := root.Project("myproj") c.Assert(err, IsNil) c.Assert(project.Name(), Equals, "Name") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/myproj") } func (s *ModelS) TestProjectActiveMilestones(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [{ "self_link": "http://self0", "name": "Name0" }, { "self_link": "http://self1", "name": "Name1" }] }` testServer.PrepareResponse(200, jsonType, data) m := M{"active_milestones_collection_link": testServer.URL + "/col_link"} project := &lpad.Project{lpad.NewValue(nil, testServer.URL, "", m)} list, err := project.ActiveMilestones() c.Assert(err, IsNil) c.Assert(list.TotalSize(), Equals, 2) names := []string{} list.For(func(ms *lpad.Milestone) error { names = append(names, ms.Name()) return nil }) c.Assert(names, DeepEquals, []string{"Name0", "Name1"}) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/col_link") } func (s *ModelS) TestProjectAllSeries(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [{ "self_link": "http://self0", "name": "Name0" }, { "self_link": "http://self1", "name": "Name1" }] }` testServer.PrepareResponse(200, jsonType, data) m := M{"series_collection_link": testServer.URL + "/col_link"} project := &lpad.Project{lpad.NewValue(nil, testServer.URL, "", m)} list, err := project.AllSeries() c.Assert(err, IsNil) c.Assert(list.TotalSize(), Equals, 2) names := []string{} list.For(func(s *lpad.ProjectSeries) error { names = append(names, s.Name()) return nil }) c.Assert(names, DeepEquals, []string{"Name0", "Name1"}) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/col_link") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/branch_test.go����������������������������������������������0000644�0000153�0000161�00000021704�12321736016�022525� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" ) func (s *ModelS) TestBranch(c *C) { m := M{ "bzr_identity": "lp:~joe/ensemble", "unique_name": "~joe/ensemble/some-branch", "web_link": "http://page", } branch := &lpad.Branch{lpad.NewValue(nil, "", "", m)} c.Assert(branch.Id(), Equals, "lp:~joe/ensemble") c.Assert(branch.UniqueName(), Equals, "~joe/ensemble/some-branch") c.Assert(branch.WebPage(), Equals, "http://page") c.Assert(branch.OwnerName(), Equals, "joe") c.Assert(branch.ProjectName(), Equals, "ensemble") } func (s *ModelS) TestRootBranch(c *C) { data := `{"unique_name": "~branch"}` testServer.PrepareResponse(200, jsonType, data) root := lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} branch, err := root.Branch("lp:~joe/project/branch-name") c.Assert(err, IsNil) c.Assert(branch.UniqueName(), Equals, "~branch") req := testServer.WaitRequest() c.Assert(req.URL.Path, Equals, "/branches") c.Assert(req.Form["ws.op"], DeepEquals, []string{"getByUrl"}) c.Assert(req.Form["url"], DeepEquals, []string{"lp:~joe/project/branch-name"}) testServer.PrepareResponse(200, jsonType, data) _, err = root.Branch("lp:~joe/+junk/foo") c.Assert(err, IsNil) req = testServer.WaitRequest() c.Assert(req.Form["url"], DeepEquals, []string{"lp:~joe/+junk/foo"}) testServer.PrepareResponse(200, jsonType, data) _, err = root.Branch("lp:~joe/%2Bjunk/foo") c.Assert(err, IsNil) req = testServer.WaitRequest() c.Assert(req.Form["url"], DeepEquals, []string{"lp:~joe/+junk/foo"}) testServer.PrepareResponse(200, jsonType, data) _, err = root.Branch("bzr+ssh://bazaar.launchpad.net/%2Bbranch/foo") c.Assert(err, IsNil) req = testServer.WaitRequest() c.Assert(req.Form["url"], DeepEquals, []string{"lp:foo"}) testServer.PrepareResponse(200, jsonType, data) _, err = root.Branch("bzr+ssh://bazaar.launchpad.net/+branch/foo") c.Assert(err, IsNil) req = testServer.WaitRequest() c.Assert(req.Form["url"], DeepEquals, []string{"lp:foo"}) } func (s *ModelS) TestMergeProposal(c *C) { m := M{ "description": "Description", "commit_message": "Commit message", "queue_status": "Needs review", "address": "some@email.com", "web_link": "http://page", "prerequisite_branch_link": testServer.URL + "/prereq_link", "target_branch_link": testServer.URL + "/target_link", "source_branch_link": testServer.URL + "/source_link", } mp := &lpad.MergeProposal{lpad.NewValue(nil, "", "", m)} c.Assert(mp.Description(), Equals, "Description") c.Assert(mp.CommitMessage(), Equals, "Commit message") c.Assert(mp.Status(), Equals, lpad.StNeedsReview) c.Assert(mp.Email(), Equals, "some@email.com") c.Assert(mp.WebPage(), Equals, "http://page") mp.SetDescription("New description") mp.SetCommitMessage("New message") c.Assert(mp.Description(), Equals, "New description") c.Assert(mp.CommitMessage(), Equals, "New message") testServer.PrepareResponse(200, jsonType, `{"unique_name": "branch1"}`) testServer.PrepareResponse(200, jsonType, `{"unique_name": "branch2"}`) testServer.PrepareResponse(200, jsonType, `{"unique_name": "branch3"}`) b1, err := mp.Target() c.Assert(err, IsNil) c.Assert(b1.UniqueName(), Equals, "branch1") b2, err := mp.PreReq() c.Assert(err, IsNil) c.Assert(b2.UniqueName(), Equals, "branch2") b3, err := mp.Source() c.Assert(err, IsNil) c.Assert(b3.UniqueName(), Equals, "branch3") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/target_link") req = testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/prereq_link") req = testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/source_link") } func (s *ModelS) TestMergeProposalSetStatus(c *C) { testServer.PrepareResponse(200, jsonType, `{}`) mp := &lpad.MergeProposal{lpad.NewValue(nil, testServer.URL, testServer.URL+"/mp", nil)} err := mp.SetStatus(lpad.StWorkInProgress) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/mp") c.Assert(req.Form["ws.op"], DeepEquals, []string{"setStatus"}) c.Assert(req.Form["status"], DeepEquals, []string{"Work in progress"}) } func (s *ModelS) TestMergeProposalAddComment(c *C) { testServer.PrepareResponse(200, jsonType, `{}`) testServer.PrepareResponse(200, jsonType, `{}`) mp := &lpad.MergeProposal{lpad.NewValue(nil, testServer.URL, testServer.URL+"/mp", nil)} err := mp.AddComment("Subject", "", lpad.VoteNone, "") c.Assert(err, IsNil) err = mp.AddComment("Subject", "Message.", lpad.VoteNeedsFixing, "QA") c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/mp") c.Assert(req.Form["ws.op"], DeepEquals, []string{"createComment"}) c.Assert(req.Form["subject"], DeepEquals, []string{"Subject"}) req = testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/mp") c.Assert(req.Form["ws.op"], DeepEquals, []string{"createComment"}) c.Assert(req.Form["subject"], DeepEquals, []string{"Subject"}) c.Assert(req.Form["content"], DeepEquals, []string{"Message."}) c.Assert(req.Form["vote"], DeepEquals, []string{"Needs Fixing"}) c.Assert(req.Form["review_type"], DeepEquals, []string{"QA"}) } func (s *ModelS) TestBranchProposeMerge(c *C) { data := `{"description": "Description"}` testServer.PrepareResponse(200, jsonType, data) branch := &lpad.Branch{lpad.NewValue(nil, testServer.URL, testServer.URL+"/~joe/ensemble/some-branch", nil)} target := &lpad.Branch{lpad.NewValue(nil, testServer.URL, testServer.URL+"/~ensemble/ensemble/trunk", nil)} stub := &lpad.MergeStub{ Description: "Description", CommitMessage: "Commit message", NeedsReview: true, Target: target, } mp, err := branch.ProposeMerge(stub) c.Assert(err, IsNil) c.Assert(mp.Description(), Equals, "Description") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/~joe/ensemble/some-branch") c.Assert(req.Form["commit_message"], DeepEquals, []string{"Commit message"}) c.Assert(req.Form["initial_comment"], DeepEquals, []string{"Description"}) c.Assert(req.Form["needs_review"], DeepEquals, []string{"true"}) c.Assert(req.Form["target_branch"], DeepEquals, []string{target.AbsLoc()}) } func (s *ModelS) TestBranchProposeMergePreReq(c *C) { data := `{"description": "Description"}` testServer.PrepareResponse(200, jsonType, data) branch := &lpad.Branch{lpad.NewValue(nil, testServer.URL, testServer.URL+"/~joe/ensemble/some-branch", nil)} target := &lpad.Branch{lpad.NewValue(nil, testServer.URL, testServer.URL+"~ensemble/ensemble/trunk", nil)} prereq := &lpad.Branch{lpad.NewValue(nil, testServer.URL, testServer.URL+"~ensemble/ensemble/prereq", nil)} stub := &lpad.MergeStub{ Target: target, PreReq: prereq, } mp, err := branch.ProposeMerge(stub) c.Assert(err, IsNil) c.Assert(mp.Description(), Equals, "Description") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/~joe/ensemble/some-branch") c.Assert(req.Form["commit_message"], IsNil) c.Assert(req.Form["initial_comment"], IsNil) c.Assert(req.Form["needs_review"], DeepEquals, []string{"false"}) c.Assert(req.Form["target_branch"], DeepEquals, []string{target.AbsLoc()}) c.Assert(req.Form["prerequisite_branch"], DeepEquals, []string{prereq.AbsLoc()}) } const mpList = `{ "total_size": 2, "start": 0, "entries": [{ "self_link": "http://self0", "description": "Desc0" }, { "self_link": "http://self1", "description": "Desc1" }] }` func checkMPList(c *C, list *lpad.MergeProposalList) { descs := []string{} list.For(func(mp *lpad.MergeProposal) error { descs = append(descs, mp.Description()) return nil }) c.Assert(descs, DeepEquals, []string{"Desc0", "Desc1"}) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/link") } func (s *ModelS) TestLandingTargets(c *C) { testServer.PrepareResponse(200, jsonType, mpList) m := M{"landing_targets_collection_link": testServer.URL + "/link"} branch := &lpad.Branch{lpad.NewValue(nil, "", "", m)} list, err := branch.LandingTargets() c.Assert(err, IsNil) checkMPList(c, list) } func (s *ModelS) TestLandingCandidates(c *C) { testServer.PrepareResponse(200, jsonType, mpList) m := M{"landing_candidates_collection_link": testServer.URL + "/link"} branch := &lpad.Branch{lpad.NewValue(nil, "", "", m)} list, err := branch.LandingCandidates() c.Assert(err, IsNil) checkMPList(c, list) } func (s *ModelS) TestBranchOwner(c *C) { testServer.PrepareResponse(200, jsonType, `{"display_name": "Joe"}`) m := M{"owner_link": testServer.URL + "/link"} branch := &lpad.Branch{lpad.NewValue(nil, "", "", m)} owner, err := branch.Owner() c.Assert(err, IsNil) c.Assert(owner.DisplayName(), Equals, "Joe") req := testServer.WaitRequest() c.Assert(req.URL.Path, Equals, "/link") } ������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/bug.go������������������������������������������������������0000644�0000153�0000161�00000014767�12321735716�021027� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad import ( "strconv" "strings" ) // A BugStub holds details necessary for creating a new bug in Launchpad. type BugStub struct { Title string // Required Description string // Required Target AnyValue // Project, source package, or distribution Private bool SecurityRelated bool Tags []string } // CreateBug creates a new bug with an appropriate bug task and returns it. func (root *Root) Bug(id int) (*Bug, error) { v, err := root.Location("/bugs/" + strconv.Itoa(id)).Get(nil) if err != nil { return nil, err } return &Bug{v}, nil } // CreateBug creates a new bug with an appropriate bug task and returns it. func (root *Root) CreateBug(stub *BugStub) (*Bug, error) { params := Params{ "ws.op": "createBug", "title": stub.Title, "description": stub.Description, "target": stub.Target.AbsLoc(), } if len(stub.Tags) > 0 { params["tags"] = strings.Join(stub.Tags, " ") } if stub.Private { params["private"] = "true" } if stub.SecurityRelated { params["security_related"] = "true" } v, err := root.Location("/bugs").Post(params) if err != nil { return nil, err } return &Bug{v}, nil } // The Bug type represents a bug in Launchpad. type Bug struct { *Value } // Id returns the bug numeric identifier (the bug # itself). func (bug *Bug) Id() int { return bug.IntField("id") } // Title returns the short bug summary. func (bug *Bug) Title() string { return bug.StringField("title") } // Description returns the main bug description. func (bug *Bug) Description() string { return bug.StringField("description") } // Tags returns the set of tags associated with the bug. func (bug *Bug) Tags() []string { return bug.StringListField("tags") } // Private returns true if the bug is flagged as private. func (bug *Bug) Private() bool { return bug.BoolField("private") } // SecurityRelated returns true if the bug describes sensitive // information about a security vulnerability. func (bug *Bug) SecurityRelated() bool { return bug.BoolField("security_related") } // WebPage returns the URL for accessing this bug in a browser. func (bug *Bug) WebPage() string { return bug.StringField("web_link") } // SetTitle changes the bug title. // Patch must be called to commit all changes. func (bug *Bug) SetTitle(title string) { bug.SetField("title", title) } // SetDescription changes the bug description. // Patch must be called to commit all changes. func (bug *Bug) SetDescription(description string) { bug.SetField("description", description) } // SetTags changes the bug tags. // Patch must be called to commit all changes. func (bug *Bug) SetTags(tags []string) { bug.SetField("tags", tags) } // SetPrivate changes the bug private flag. // Patch must be called to commit all changes. func (bug *Bug) SetPrivate(private bool) { bug.SetField("private", private) } // SetSecurityRelated sets to related the flag that tells if // a bug is security sensitive or not. // Patch must be called to commit all changes. func (bug *Bug) SetSecurityRelated(related bool) { bug.SetField("security_related", related) } // LinkBranch associates a branch with this bug. func (bug *Bug) LinkBranch(branch *Branch) error { params := Params{ "ws.op": "linkBranch", "branch": branch.AbsLoc(), } _, err := bug.Post(params) return err } // A BugTask represents the association of a bug with a project // or source package, and the related information. type BugTask struct { *Value } type BugImportance string const ( ImUnknown BugImportance = "Unknown" ImCritical BugImportance = "Critical" ImHigh BugImportance = "High" ImMedium BugImportance = "Medium" ImLow BugImportance = "Low" ImWishlist BugImportance = "Wishlist" ImUndecided BugImportance = "Undecided" ) type BugStatus string const ( StUnknown BugStatus = "Unknown" StNew BugStatus = "New" StIncomplete BugStatus = "Incomplete" StOpinion BugStatus = "Opinion" StInvalid BugStatus = "Invalid" StWontFix BugStatus = "Won't fix" StExpired BugStatus = "Expired" StConfirmed BugStatus = "Confirmed" StTriaged BugStatus = "Triaged" StInProgress BugStatus = "In Progress" StFixCommitted BugStatus = "Fix Committed" StFixReleased BugStatus = "Fix Released" ) // Status returns the current status for the bug task. See // the Status type for supported values. func (task *BugTask) Status() BugStatus { return BugStatus(task.StringField("status")) } // Importance returns the current importance for the bug task. See // the Importance type for supported values. func (task *BugTask) Importance() BugImportance { return BugImportance(task.StringField("importance")) } // Assignee returns the person currently assigned to work on the task. func (task *BugTask) Assignee() (*Person, error) { v, err := task.Link("assignee_link").Get(nil) if err != nil { return nil, err } return &Person{v}, nil } // Milestone returns the milestone the task is currently targeted at. func (task *BugTask) Milestone() (*Milestone, error) { v, err := task.Link("milestone_link").Get(nil) if err != nil { return nil, err } return &Milestone{v}, nil } // SetStatus changes the current status for the bug task. See // the Status type for supported values. func (task *BugTask) SetStatus(status BugStatus) { task.SetField("status", string(status)) } // Importance changes the current importance for the bug task. See // the Importance type for supported values. func (task *BugTask) SetImportance(importance BugImportance) { task.SetField("importance", string(importance)) } // SetAssignee changes the person currently assigned to work on the task. func (task *BugTask) SetAssignee(person *Person) { task.SetField("assignee_link", person.AbsLoc()) } // SetMilestone changes the milestone the task is currently targeted at. func (task *BugTask) SetMilestone(ms *Milestone) { task.SetField("milestone_link", ms.AbsLoc()) } // BugTaskList represents a list of BugTasks for iteration. type BugTaskList struct { *Value } // For iterates over the list of bug tasks and calls f for each one. // If f returns a non-nil error, iteration will stop and the error will // be returned as the result of For. func (list *BugTaskList) For(f func(bt *BugTask) error) error { return list.Value.For(func(v *Value) error { f(&BugTask{v}) return nil }) } // Tasks returns the list of bug tasks associated with the bug. func (bug *Bug) Tasks() (*BugTaskList, error) { v, err := bug.Link("bug_tasks_collection_link").Get(nil) if err != nil { return nil, err } return &BugTaskList{v}, nil } ���������juju-core_1.18.1/src/launchpad.net/lpad/person_test.go����������������������������������������������0000644�0000153�0000161�00000017267�12321735716�022615� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" ) func (s *ModelS) TestRootMe(c *C) { testServer.PrepareResponse(200, jsonType, `{"display_name": "Joe"}`) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} me, err := root.Me() c.Assert(err, IsNil) c.Assert(me.DisplayName(), Equals, "Joe") req := testServer.WaitRequest() c.Assert(req.URL.Path, Equals, "/people/+me") } func (s *ModelS) TestRootMemberPerson(c *C) { data := `{"display_name": "Joe"}` testServer.PrepareResponse(200, jsonType, data) testServer.PrepareResponse(200, jsonType, data) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} member, err := root.Member("joe") c.Assert(err, IsNil) person, ok := member.(*lpad.Person) c.Assert(ok, Equals, true) c.Assert(person.DisplayName(), Equals, "Joe") req := testServer.WaitRequest() c.Assert(req.URL.Path, Equals, "/~joe") } func (s *ModelS) TestRootMemberTeam(c *C) { data := `{"display_name": "Ensemble", "is_team": true}` testServer.PrepareResponse(200, jsonType, data) testServer.PrepareResponse(200, jsonType, data) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} member, err := root.Member("ensemble") c.Assert(err, IsNil) team, ok := member.(*lpad.Team) c.Assert(ok, Equals, true) c.Assert(team.DisplayName(), Equals, "Ensemble") req := testServer.WaitRequest() c.Assert(req.URL.Path, Equals, "/~ensemble") } func (s *ModelS) TestRootFindMembers(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [{ "self_link": "http://self0", "display_name": "Name0", "is_team": false }, { "self_link": "http://self1", "display_name": "Name1", "is_team": true }] }` testServer.PrepareResponse(200, jsonType, data) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} list, err := root.FindMembers("someuser") c.Assert(err, IsNil) c.Assert(list.TotalSize(), Equals, 2) names := []string{} list.For(func(v lpad.Member) error { if v.BoolField("is_team") { t := v.(*lpad.Team) names = append(names, t.DisplayName()) } else { p := v.(*lpad.Person) names = append(names, p.DisplayName()) } return nil }) c.Assert(names, DeepEquals, []string{"Name0", "Name1"}) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/people") c.Assert(req.Form["ws.op"], DeepEquals, []string{"find"}) c.Assert(req.Form["text"], DeepEquals, []string{"someuser"}) } func (s *ModelS) TestRootFindPeople(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [{ "self_link": "http://self0", "display_name": "Name0" }, { "self_link": "http://self1", "display_name": "Name1" }] }` testServer.PrepareResponse(200, jsonType, data) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} list, err := root.FindPeople("someuser") c.Assert(err, IsNil) c.Assert(list.TotalSize(), Equals, 2) names := []string{} list.For(func(p *lpad.Person) error { names = append(names, p.DisplayName()) return nil }) c.Assert(names, DeepEquals, []string{"Name0", "Name1"}) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/people") c.Assert(req.Form["ws.op"], DeepEquals, []string{"findPerson"}) c.Assert(req.Form["text"], DeepEquals, []string{"someuser"}) } func (s *ModelS) TestRootFindTeams(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [{ "self_link": "http://self0", "display_name": "Name0", "is_team": true }, { "self_link": "http://self1", "display_name": "Name1", "is_team": true }] }` testServer.PrepareResponse(200, jsonType, data) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} list, err := root.FindTeams("someuser") c.Assert(err, IsNil) c.Assert(list.TotalSize(), Equals, 2) names := []string{} list.For(func(t *lpad.Team) error { names = append(names, t.DisplayName()) return nil }) c.Assert(names, DeepEquals, []string{"Name0", "Name1"}) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/people") c.Assert(req.Form["ws.op"], DeepEquals, []string{"findTeam"}) c.Assert(req.Form["text"], DeepEquals, []string{"someuser"}) } func (s *ModelS) TestPerson(c *C) { m := M{ "name": "joe", "display_name": "Joe", "web_link": "http://page", } person := &lpad.Person{lpad.NewValue(nil, "", "", m)} c.Assert(person.Name(), Equals, "joe") c.Assert(person.DisplayName(), Equals, "Joe") c.Assert(person.WebPage(), Equals, "http://page") person.SetName("newname") person.SetDisplayName("New Name") c.Assert(person.Name(), Equals, "newname") c.Assert(person.DisplayName(), Equals, "New Name") } func (s *ModelS) TestTeam(c *C) { m := M{ "name": "myteam", "display_name": "My Team", "web_link": "http://page", } team := &lpad.Team{lpad.NewValue(nil, "", "", m)} c.Assert(team.Name(), Equals, "myteam") c.Assert(team.DisplayName(), Equals, "My Team") c.Assert(team.WebPage(), Equals, "http://page") team.SetName("ateam") team.SetDisplayName("A Team") c.Assert(team.Name(), Equals, "ateam") c.Assert(team.DisplayName(), Equals, "A Team") } func (s *ModelS) TestIRCNick(c *C) { m := M{ "resource_type_link": "https://api.launchpad.net/1.0/#irc_id", "self_link": "https://api.launchpad.net/1.0/~lpad-test/+ircnick/28983", "person_link": "https://api.launchpad.net/1.0/~lpad-test", "web_link": "https://api.launchpad.net/~lpad-test/+ircnick/28983", "nickname": "canonical-nick", "network": "irc.canonical.com", "http_etag": "\"the-etag\"", } nick := &lpad.IRCNick{lpad.NewValue(nil, "", "", m)} c.Assert(nick.Nick(), Equals, "canonical-nick") c.Assert(nick.Network(), Equals, "irc.canonical.com") } func (s *ModelS) TestIRCNickChange(c *C) { nick := &lpad.IRCNick{lpad.NewValue(nil, "", "", nil)} nick.SetNick("mynick") nick.SetNetwork("mynetwork") c.Assert(nick.Nick(), Equals, "mynick") c.Assert(nick.Network(), Equals, "mynetwork") } func (s *ModelS) TestPersonNicks(c *C) { m := M{ "irc_nicknames_collection_link": testServer.URL + "/~lpad-test/irc_nicknames", } data := `{ "total_size": 2, "start": 0, "entries": [{ "resource_type_link": "https://api.launchpad.net/1.0/#irc_id", "network": "irc.canonical.com", "person_link": "https://api.launchpad.net/1.0/~lpad-test", "web_link": "https://api.launchpad.net/~lpad-test/+ircnick/28983", "http_etag": "\"the-etag1\"", "self_link": "https://api.launchpad.net/1.0/~lpad-test/+ircnick/28983", "nickname": "canonical-nick" }, { "resource_type_link": "https://api.launchpad.net/1.0/#irc_id", "network": "irc.freenode.net", "person_link": "https://api.launchpad.net/1.0/~lpad-test", "web_link": "https://api.launchpad.net/~lpad-test/+ircnick/28982", "http_etag": "\"the-etag2\"", "self_link": "https://api.launchpad.net/1.0/~lpad-test/+ircnick/28982", "nickname": "freenode-nick" }], "resource_type_link": "https://api.launchpad.net/1.0/#irc_id-page-resource" }` testServer.PrepareResponse(200, jsonType, data) person := &lpad.Person{lpad.NewValue(nil, "", "", m)} nicks, err := person.IRCNicks() c.Assert(err, IsNil) c.Assert(len(nicks), Equals, 2) c.Assert(nicks[0].Nick(), Equals, "canonical-nick") c.Assert(nicks[1].Nick(), Equals, "freenode-nick") } func (s *ModelS) TestPersonPreferredEmail(c *C) { testServer.PrepareResponse(200, jsonType, `{"email": "the@email.com"}`) m := M{"preferred_email_address_link": testServer.URL + "/link"} person := &lpad.Person{lpad.NewValue(nil, "", "", m)} email, err := person.PreferredEmail() c.Assert(err, IsNil) c.Assert(email, Equals, "the@email.com") req := testServer.WaitRequest() c.Assert(req.URL.Path, Equals, "/link") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/source.go���������������������������������������������������0000644�0000153�0000161�00000004107�12321735716�021535� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad // SourcePackage represents a source package associated to // a particular distribution series. type SourcePackage struct { *Value } // Name returns the package name. func (s *SourcePackage) Name() string { return s.StringField("name") } // DisplayName returns the package display name. func (s *SourcePackage) DisplayName() string { return s.StringField("displayname") } // LatestComponent returns the name of the component where the // source package was last published. func (s *SourcePackage) LatestComponent() string { return s.StringField("latest_published_component_name") } // WebPage returns the URL for accessing this source package in a browser. func (s *SourcePackage) WebPage() string { return s.StringField("web_link") } // Distro returns the distribution for this source package. func (s *SourcePackage) Distro() (*Distro, error) { d, err := s.Link("distribution_link").Get(nil) if err != nil { return nil, err } return &Distro{d}, nil } // DistroSeries returns the distribution series for the source package. func (s *SourcePackage) DistroSeries() (*DistroSeries, error) { d, err := s.Link("distroseries_link").Get(nil) if err != nil { return nil, err } return &DistroSeries{d}, nil } // DistroSourcePackage represents a source package in a distribution. type DistroSourcePackage struct { *Value } // Name returns the package name. func (s *DistroSourcePackage) Name() string { return s.StringField("name") } // DisplayName returns the package display name. func (s *DistroSourcePackage) DisplayName() string { return s.StringField("display_name") } // Title returns the package title. func (s *DistroSourcePackage) Title() string { return s.StringField("title") } // WebPage returns the URL for accessing this source package in a browser. func (s *DistroSourcePackage) WebPage() string { return s.StringField("web_link") } // Distro returns the distribution of this source package. func (s *DistroSourcePackage) Distro() (*Distro, error) { d, err := s.Link("distribution_link").Get(nil) if err != nil { return nil, err } return &Distro{d}, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/oauth.go����������������������������������������������������0000644�0000153�0000161�00000016435�12321735716�021364� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad import ( "encoding/json" "errors" "fmt" "io/ioutil" "math/rand" "net/http" "net/url" "os" "os/exec" "strconv" "strings" "time" ) // The OAuth type enables authenticated sessions to be established with // Launchpad using the oauth protocol. See StoredOAuth and ConsoleOAuth // for more features added on top of this type. // // For more details, see Launchpad's documentation on the subject: // // https://help.launchpad.net/API/SigningRequests // type OAuth struct { BaseURL string // Defaults to https://(staging.)launchpad.net/ AuthURL string // Set by Login before Callback is called Callback func(*OAuth) error // Called by Login to get user to AuthURL CallbackURL string // Optional. AuthURL will redirect here after confirmation Token, TokenSecret string // Credentials obtained Consumer string // Consumer name. Defaults to "https://launchpad.net/lpad" Anonymous bool // Don't try to login } func (oauth *OAuth) consumer() string { if oauth.Consumer == "" { return "https://launchpad.net/lpad" } return oauth.Consumer } func (oauth *OAuth) requestToken(path string, form url.Values) (err error) { r, err := http.PostForm(oauth.BaseURL+path, form) if err != nil { return } data, err := ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { return } query, err := url.ParseQuery(string(data)) if err != nil { return } token, ok := query["oauth_token"] if !ok || len(token) == 0 { return errors.New("oauth_token missing from " + path + " response: " + string(data)) } secret, ok := query["oauth_token_secret"] if !ok || len(secret) == 0 { return errors.New("oauth_token_secret missing from " + path + " response: " + string(data)) } oauth.Token = token[0] oauth.TokenSecret = secret[0] return } func (oauth *OAuth) Login(baseURL string) error { if oauth.BaseURL == "" { url, err := url.Parse(baseURL) if err != nil { return err } // https://api.launchpad.net/1.0/ => https://launchpad.net/ if strings.HasPrefix(url.Host, "api.") { url.Host = url.Host[4:] } url.Path = "" oauth.BaseURL = url.String() } if oauth.BaseURL[len(oauth.BaseURL)-1] == '/' { oauth.BaseURL = oauth.BaseURL[:len(oauth.BaseURL)-1] } if oauth.Anonymous || oauth.TokenSecret != "" && oauth.AuthURL == "" { return nil // Ready to sign. } form := url.Values{ "oauth_consumer_key": []string{oauth.consumer()}, "oauth_signature_method": []string{"PLAINTEXT"}, "oauth_signature": []string{"&"}, } if err := oauth.requestToken("/+request-token", form); err != nil { return err } authQuery := url.Values{} authQuery["oauth_token"] = []string{oauth.Token} if oauth.CallbackURL != "" { authQuery["oauth_callback"] = []string{oauth.CallbackURL} } oauth.AuthURL = oauth.BaseURL + "/+authorize-token?" + authQuery.Encode() if oauth.Callback != nil { if err := oauth.Callback(oauth); err != nil { return err } } form["oauth_token"] = []string{oauth.Token} form["oauth_signature"] = []string{"&" + oauth.TokenSecret} if err := oauth.requestToken("/+access-token", form); err != nil { return err } oauth.AuthURL = "" return nil } func (oauth *OAuth) Sign(req *http.Request) error { if !oauth.Anonymous { if oauth.Token == "" { return errors.New("OAuth can't Sign without a token (missing Login?)") } if oauth.TokenSecret == "" { return errors.New("OAuth can't Sign without a token secret (missing Login?)") } } auth := `OAuth realm="https://api.launchpad.net/", ` + `oauth_consumer_key="` + url.QueryEscape(oauth.consumer()) + `", ` + `oauth_token="` + url.QueryEscape(oauth.Token) + `", ` + `oauth_signature_method="PLAINTEXT", ` + `oauth_signature="` + url.QueryEscape(`&`+oauth.TokenSecret) + `", ` + `oauth_timestamp="` + strconv.FormatInt(time.Now().Unix(), 10) + `", ` + `oauth_nonce="` + strconv.Itoa(int(rand.Int31())) + `", ` + `oauth_version="1.0"` req.Header.Add("Authorization", auth) return nil } // The StoredOAuth type behaves like OAuth, but will cache a successful // authentication in ~/.lpad_oauth and reuse it in future Login requests. // // See the OAuth type for details on how to construct values of this type, // and see the Login method for a convenient way to make use of them. type StoredOAuth OAuth // We might use an embedded type to avoid wrapping the methods, but that // would prevent people from building StoredOAuth values with explicit // fields such as StoredOAuth{Callback: ...}. type oauthDump struct { Token, TokenSecret string } func (oauth *StoredOAuth) Login(baseURL string) error { if oauth.TokenSecret == "" && oauth.read() == nil { return nil } err := (*OAuth)(oauth).Login(baseURL) if err != nil { return err } return oauth.write() } func (oauth *StoredOAuth) Sign(req *http.Request) error { return (*OAuth)(oauth).Sign(req) } func (oauth *StoredOAuth) read() error { path := os.ExpandEnv("$HOME/.lpad_oauth") if oauth.TokenSecret != "" { return nil } file, err := os.Open(path) if err != nil { return err } data, err := ioutil.ReadAll(file) file.Close() if err != nil { return err } dump := &oauthDump{} err = json.Unmarshal(data, dump) if err != nil { return err } oauth.Token = dump.Token oauth.TokenSecret = dump.TokenSecret return nil } func (oauth *StoredOAuth) write() error { path := os.ExpandEnv("$HOME/.lpad_oauth") file, err := os.Create(path) if err != nil { return err } defer file.Close() data, err := json.Marshal(&oauthDump{oauth.Token, oauth.TokenSecret}) if err != nil { return err } _, err = file.Write(data) return err } // The ConsoleOAuth type will cache successful authentications like // StoredOAuth and will also open a browser for the user to confirm // authentication and wait for confirmation with a console message // on standard output. // // See the OAuth type for details on how to construct values of this type, // and see the Login method for a convenient way to make use of them. type ConsoleOAuth StoredOAuth // We might use an embedded type to avoid wrapping the methods, but that // would prevent people from building ConsoleOAuth values with explicit // fields such as ConsoleOAuth{Callback: ...}. func (oauth *ConsoleOAuth) Login(baseURL string) error { oauth.Callback = fireBrowser err := (*StoredOAuth)(oauth).Login(baseURL) if err != nil { return err } return nil } func (oauth *ConsoleOAuth) Sign(req *http.Request) error { return (*StoredOAuth)(oauth).Sign(req) } func fireBrowser(oauth *OAuth) error { browser, err := findBrowser() if err == nil { args := []string{browser, oauth.AuthURL} p, err := os.StartProcess(args[0], args, &os.ProcAttr{}) if err == nil { go func() { p.Wait() }() // Avoid zombies. } else { browser = "" } } if browser != "" { fmt.Printf("Go to your browser now and authorize access to Launchpad.\n") } else { fmt.Printf("Open the following URL in your browser:\n %s\n", oauth.AuthURL) } fmt.Printf("Press [ENTER] after authorization is confirmed... ") b := make([]byte, 1) os.Stdin.Read(b) return nil } func findBrowser() (path string, err error) { path, err = exec.LookPath("sensible-browser") if err == nil { return path, nil } browser := os.Getenv("BROWSER") if browser == "" { return "", err } return exec.LookPath(browser) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/model_test.go�����������������������������������������������0000644�0000153�0000161�00000000316�12321735716�022372� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" ) var _ = Suite(&ModelS{}) var _ = Suite(&ModelI{}) type ModelS struct { HTTPSuite } type ModelI struct { SuiteI } type M map[string]interface{} ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/distro.go���������������������������������������������������0000644�0000153�0000161�00000026442�12321735716�021547� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad import ( "fmt" "net/url" "time" ) // Distro returns a distribution with the given name. func (root *Root) Distro(name string) (*Distro, error) { r, err := root.Location("/" + url.QueryEscape(name)).Get(nil) if err != nil { return nil, err } return &Distro{r}, nil } // Distros returns the list of all distributions registered in Launchpad. func (root *Root) Distros() (*DistroList, error) { list, err := root.Location("/distros/").Get(nil) if err != nil { return nil, err } return &DistroList{list}, nil } // The Distro type represents a distribution in Launchpad. type Distro struct { *Value } // The DistroList type represents a list of Distro objects. type DistroList struct { *Value } // For iterates over the list of distributions and calls f for each one. // If f returns a non-nil error, iteration will stop and the error will be // returned as the result of For. func (list *DistroList) For(f func(d *Distro) error) { list.Value.For(func(v *Value) error { return f(&Distro{v}) }) } // Name returns the distribution name, which is composed of at least one // lowercase letter or number, followed by letters, numbers, dots, // hyphens or pluses. This is a short name used in URLs. func (d *Distro) Name() string { return d.StringField("name") } // DisplayName returns the distribution name as it would be displayed // in a paragraph. For example, a distribution's title might be // "The Foo Distro" and its display name could be "Foo". func (d *Distro) DisplayName() string { return d.StringField("display_name") } // Title returns the distribution title as it might be used in isolation. func (d *Distro) Title() string { return d.StringField("title") } // Summary returns the distribution summary, which is a short paragraph // to introduce the distribution's goals and highlights. func (d *Distro) Summary() string { return d.StringField("summary") } // Description returns the distribution description. func (d *Distro) Description() string { return d.StringField("description") } // WebPage returns the URL for accessing this distribution in a browser. func (d *Distro) WebPage() string { return d.StringField("web_link") } // BlueprintTarget marks *Distro as being a target for blueprints. func (p *Distro) BlueprintTarget() {} type BranchTip struct { UniqueName string Revision string OfficialSeries []string } // BranchTips returns a list of all branches registered under the given // distribution changed after the since time. If since is the zero time, // all branch tips in the distribution are returned. func (d *Distro) BranchTips(since time.Time) (tips []BranchTip, err error) { params := Params{"ws.op": "getBranchTips"} if !since.IsZero() { params["since"] = since.In(time.UTC).Format(time.RFC3339) } v, err := d.Location("").Get(params) if err != nil { return nil, err } l, ok := v.Map()["value"].([]interface{}) if !ok { return nil, fmt.Errorf(`map is missing "value" field`) } for i := range l { li, ok := l[i].([]interface{}) if !ok || len(li) != 3 { return nil, fmt.Errorf("unsupported branch tip item: %#v", l[i]) } url, ok1 := li[0].(string) rev, ok2 := li[1].(string) series, ok3 := li[2].([]interface{}) if !ok2 { if li[1] == nil { // Branch without revisions. ok2 = true } } if !(ok1 && ok2 && ok3) { return nil, fmt.Errorf("unsupported branch tip item: %#v", l[i]) } sseries := []string{} for i := range series { s, ok := series[i].(string) if !ok { return nil, fmt.Errorf("unsupported branch tip item: %#v", l[i]) } sseries = append(sseries, s) } tips = append(tips, BranchTip{url, rev, sseries}) } return tips, nil } // SetName changes the distribution name, which must be composed of at // least one lowercase letter or number, followed by letters, numbers, // dots, hyphens or pluses. This is a short name used in URLs. // Patch must be called to commit all changes. func (d *Distro) SetName(name string) { d.SetField("name", name) } // SetDisplayName changes the distribution name as it would be displayed // in a paragraph. For example, a distribution's title might be // "The Foo Distro" and its display name could be "Foo". // Patch must be called to commit all changes. func (d *Distro) SetDisplayName(name string) { d.SetField("display_name", name) } // SetTitle changes the distribution title as it would be displayed // in isolation. For example, the distribution title might be // "The Foo Distro" and display name could be "Foo". // Patch must be called to commit all changes. func (d *Distro) SetTitle(title string) { d.SetField("title", title) } // SetSummary changes the distribution summary, which is a short paragraph // to introduce the distribution's goals and highlights. // Patch must be called to commit all changes. func (d *Distro) SetSummary(title string) { d.SetField("summary", title) } // SetDescription changes the distributions's description. // Patch must be called to commit all changes. func (d *Distro) SetDescription(description string) { d.SetField("description", description) } // ActiveMilestones returns the list of active milestones associated with // the distribution, ordered by the target date. func (d *Distro) ActiveMilestones() (*MilestoneList, error) { r, err := d.Link("active_milestones_collection_link").Get(nil) if err != nil { return nil, err } return &MilestoneList{r}, nil } // Series returns the named Series of this distribution. func (d *Distro) Series(name string) (*DistroSeries, error) { s, err := d.Location(url.QueryEscape(name)).Get(nil) if err != nil { return nil, err } return &DistroSeries{s}, nil } // AllSeries returns the list of series associated with the distribution. func (d *Distro) AllSeries() (*DistroSeriesList, error) { r, err := d.Link("series_collection_link").Get(nil) if err != nil { return nil, err } return &DistroSeriesList{r}, nil } // Archives returns the list of archives associated with the distribution. func (d *Distro) Archives() (*ArchiveList, error) { r, err := d.Link("archives_collection_link").Get(nil) if err != nil { return nil, err } return &ArchiveList{r}, nil } // Archive returns the named archive associated with the distribution func (d *Distro) Archive(name string) (*Archive, error) { v, err := d.Location("").Get(Params{"ws.op": "getArchive", "name": name}) if err != nil { return nil, err } return &Archive{v}, nil } // FocusDistroSeries returns the distribution series set as the current // development focus. func (d *Distro) FocusSeries() (*DistroSeries, error) { r, err := d.Link("current_series_link").Get(nil) if err != nil { return nil, err } return &DistroSeries{r}, nil } // The DistroSeries type represents a series associated with a distribution. type DistroSeries struct { *Value } // Name returns the series name, which is a unique name that identifies // it and is used in URLs. It consists of only lowercase letters, digits, // and simple punctuation. For example, "2.0" or "trunk". func (s *DistroSeries) Name() string { return s.StringField("name") } // DisplayName returns the distribution series display name (e.g. "Oneiric"). func (d *DistroSeries) DisplayName() string { return d.StringField("displayname") } // FullSeriesName returns the distribution series name as it would be displayed // in a paragraph (e.g. "Oneiric Ocelot"). func (d *DistroSeries) FullSeriesName() string { return d.StringField("fullseriesname") } // Title returns the series context title for pages. func (s *DistroSeries) Title() string { return s.StringField("title") } // Summary returns the summary for this distribution series. func (s *DistroSeries) Summary() string { return s.StringField("summary") } // WebPage returns the URL for accessing this distribution series in a browser. func (s *DistroSeries) WebPage() string { return s.StringField("web_link") } // Active returns true if this distribution series is still in active development. func (s *DistroSeries) Active() bool { return s.BoolField("active") } // Description returns the distribution series' description. func (d DistroSeries) Description() string { return d.StringField("description") } // TODO: These have no tests. // //type TagMatching string // //const ( // MatchAny TagMatching = "Any" // MatchAll TagMatching = "All" //) // //// SearchTasks returns the list of bug tasks associated with this //// distribution that match the given criteria. //func (d *Distro) SearchTasks(tags string, matching TagMatching) (*BugTaskList, error) { // params := Params{ // "ws.op": "searchTasks", // "tags": tags, // "tags_combinator": string(matching), // } // v, err := d.Location("").Get(params) // if err != nil { // return nil, err // } // return &BugTaskList{v}, nil //} // //// SearchTasks returns the list of bug tasks associated with this //// distribution series that match the given criteria. //func (d *DistroSeries) SearchTasks(tags string, matching TagMatching) (*BugTaskList, error) { // params := Params{ // "ws.op": "searchTasks", // "tags": tags, // "tags_combinator": string(match), // } // v, err := d.Location("").Get(params) // if err != nil { // return nil, err // } // return &BugTaskList{v}, nil //} // //// Builds returns a list of all the Build objects for this distribution //// series for the source packages matching the given criteria. //func (d *DistroSeries) Builds(buildState BuildState, pocket Pocket, sourceName string) (*BuildList, error) { // params := Params{ // "ws.op": "getBuildRecords", // "build_state": string(buildState), // "source_name": sourceName, // } // if pocket != PocketAny { // params["pocket"] = string(pocket) // } // v, err := d.Location("").Get(params) // if err != nil { // return nil, err // } // return &BuildList{v}, nil //} // DistroSourcePackage returns the DistroSourcePackage with the given name. func (d *Distro) DistroSourcePackage(name string) (*DistroSourcePackage, error) { params := Params{"ws.op": "getSourcePackage", "name": name} v, err := d.Location("").Get(params) if err != nil { return nil, err } return &DistroSourcePackage{v}, nil } // SourcePackage returns the SourcePackage with the given name. func (d *DistroSeries) SourcePackage(name string) (*SourcePackage, error) { params := Params{"ws.op": "getSourcePackage", "name": name} v, err := d.Location("").Get(params) if err != nil { return nil, err } return &SourcePackage{v}, nil } // SetName changes the series name, which must consists of only letters, // numbers, and simple punctuation. For example: "2.0" or "trunk". func (s *DistroSeries) SetName(name string) { s.SetField("name", name) } // SetTitle changes the series title. func (s *DistroSeries) SetTitle(title string) { s.SetField("title", title) } // SetSummary changes the summary for this distribution series. func (s *DistroSeries) SetSummary(summary string) { s.SetField("summary", summary) } // SetActive sets whether the series is still in active development or not. func (s *DistroSeries) SetActive(active bool) { s.SetField("active", active) } // The DistroSeriesList represents a list of distribution series. type DistroSeriesList struct { *Value } // For iterates over the list of series and calls f for each one. // If f returns a non-nil error, iteration will stop and the error will // be returned as the result of For. func (list *DistroSeriesList) For(f func(s *DistroSeries) error) error { return list.Value.For(func(r *Value) error { return f(&DistroSeries{r}) }) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/builder.go��������������������������������������������������0000644�0000153�0000161�00000003724�12321735716�021667� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad // API for: https://launchpad.net/builders // // Not all info presented on that page is available via the LP API though. // Builders returns all the builders. func (root *Root) Builders() (*BuilderList, error) { v, err := root.Location("/builders").Get(nil) if err != nil { return nil, err } return &BuilderList{v}, nil } // Builder returns a builder by its name. func (root *Root) Builder(name string) (*Builder, error) { v, err := root.Location("/builders").Get(Params{"ws.op": "getByName", "name": name}) if err != nil { return nil, err } return &Builder{v}, nil } // The Builder type stands for an individual machine that builds packages. type Builder struct { *Value } // Name returns the builder's name. func (b *Builder) Name() string { return b.StringField("name") } // Title returns the builder slave title. func (b *Builder) Title() string { return b.StringField("title") } // Active returns whether the builder is enabled. func (b *Builder) Active() bool { return b.BoolField("active") } // BuilderOK returns whether the builder is working fine. func (b *Builder) BuilderOK() bool { return b.BoolField("builderok") } // Virtualized returns whether the builder is virtualized Xen instance. func (b *Builder) Virtualized() bool { return b.BoolField("virtualized") } // VMHost returns the machine hostname hosting the builder. func (b *Builder) VMHost() string { return b.StringField("vm_host") } // WebPage returns the URL for accessing this builder in a browser. func (b *Builder) WebPage() string { return b.StringField("web_link") } // A BuilderList represents a list of Builder objects. type BuilderList struct { *Value } // For iterates over the list of builders and calls f for each one. // If f returns a non-nil error, iteration will stop and the error // will be returned as the result of For. func (list *BuilderList) For(f func(b *Builder) error) error { return list.Value.For(func(v *Value) error { return f(&Builder{v}) }) } ��������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/suite_test.go�����������������������������������������������0000644�0000153�0000161�00000005647�12321735716�022437� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( "bytes" "flag" "fmt" "io/ioutil" . "launchpad.net/gocheck" "net/http" "net/url" "os" "testing" "time" ) func Test(t *testing.T) { TestingT(t) } var integration = flag.Bool("i", false, "Enable integration tests") type SuiteI struct{} func (s *SuiteI) SetUpSuite(c *C) { if !*integration { c.Skip("Integration tests not enabled (-i flag)") } } type HTTPSuite struct{} var testServer = NewTestHTTPServer("http://localhost:4444", 5 * time.Second) func (s *HTTPSuite) SetUpSuite(c *C) { testServer.Start() } func (s *HTTPSuite) TearDownTest(c *C) { testServer.Flush() } type TestHTTPServer struct { URL string Timeout time.Duration started bool request chan *http.Request response chan *testResponse pending chan bool } type testResponse struct { Status int Headers map[string]string Body string } func NewTestHTTPServer(url_ string, timeout time.Duration) *TestHTTPServer { return &TestHTTPServer{URL: url_, Timeout: timeout} } func (s *TestHTTPServer) Start() { if s.started { return } s.started = true s.request = make(chan *http.Request, 64) s.response = make(chan *testResponse, 64) s.pending = make(chan bool, 64) url_, _ := url.Parse(s.URL) go http.ListenAndServe(url_.Host, s) s.PrepareResponse(203, nil, "") for { // Wait for it to be up. resp, err := http.Get(s.URL) if err == nil && resp.StatusCode == 203 { break } fmt.Fprintf(os.Stderr, "\nWaiting for fake server to be up... ") time.Sleep(1e8) } fmt.Fprintf(os.Stderr, "done\n\n") s.WaitRequest() // Consume dummy request. } // FlushRequests discards requests which were not yet consumed by WaitRequest. func (s *TestHTTPServer) Flush() { for { select { case <-s.request: case <-s.response: default: return } } } func body(req *http.Request) string { data, err := ioutil.ReadAll(req.Body) if err != nil { panic(err) } return string(data) } func (s *TestHTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { req.ParseForm() data, err := ioutil.ReadAll(req.Body) if err != nil { panic(err) } s.request <- req var resp *testResponse select { case resp = <-s.response: case <-time.After(s.Timeout): fmt.Fprintf(os.Stderr, "ERROR: Timeout waiting for test to provide response\n") resp = &testResponse{500, nil, ""} } if resp.Headers != nil { h := w.Header() for k, v := range resp.Headers { h.Set(k, v) } } if resp.Status != 0 { w.WriteHeader(resp.Status) } w.Write([]byte(resp.Body)) // WriteHeader consumes the body per RFC2616. Restore it. req.Body = ioutil.NopCloser(bytes.NewBuffer(data)) } func (s *TestHTTPServer) WaitRequest() *http.Request { select { case req := <-s.request: return req case <-time.After(s.Timeout): panic("Timeout waiting for request") } panic("unreached") } func (s *TestHTTPServer) PrepareResponse(status int, headers map[string]string, body string) { s.response <- &testResponse{status, headers, body} } �����������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/session.go��������������������������������������������������0000644�0000153�0000161�00000004576�12321735716�021732� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// This simple example demonstrates how to get started using lpad to // communicate with Launchpad in a console application: // // root, err := lpad.Login(lpad.Production, &lpad.ConsoleOAuth{}) // if err != nil { // panic(err) // } // me, err := root.Me() // if err != nil { // panic(err) // } // fmt.Println(me.DisplayName()) // package lpad import "net/http" // The Auth interface is implemented by types which are able to Login and // authenticate requests made against Launchpad. type Auth interface { Login(baseURL string) (err error) Sign(req *http.Request) (err error) } type APIBase string const ( Production APIBase = "https://api.launchpad.net/devel/" Staging APIBase = "https://api.staging.launchpad.net/devel/" ) // The Session type represents a session of communication with Launchpad, // and carries the authenticator necessary to validate requests in the // given session. Creating sessions explicitly is generally not necessary. // See the Login method for a convenient way to use lpad to access the // Launchpad API. type Session struct { auth Auth } // Create a new session using the auth authenticator. Creating sessions // explicitly is generally not necessary. See the Login method for a // convenient way to use lpad to access the Launchpad API. func NewSession(auth Auth) *Session { return &Session{auth} } func (s *Session) Sign(req *http.Request) (err error) { return s.auth.Sign(req) } // Login returns a Root object with a new session authenticated in Launchpad // using the auth authenticator. This is the primary method to start using // the Launchpad API. // // This simple example demonstrates how to get a user's name in a console // application: // // oauth := &lpad.ConsoleOAuth{Consumer: "your-app"} // root, err := lpad.Login(lpad.Production, oauth) // check(err) // me, err := root.Me() // check(err) // fmt.Println(me.DisplayName()) // // Alternatively, it is possible to communicate with Launchpad anonymously: // // oauth := &lpad.ConsoleOAuth{Consumer: "your-app", Anonymous: true} // root, err := lpad.Login(lpad.Production, oauth) // check(err) // func Login(baseurl APIBase, auth Auth) (*Root, error) { baseloc := string(baseurl) if err := auth.Login(baseloc); err != nil { return nil, err } return &Root{&Value{session: NewSession(auth), baseloc: baseloc, loc: baseloc}}, nil } ����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/archive.go��������������������������������������������������0000644�0000153�0000161�00000003745�12321735716�021665� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad // Archive represents a package archive. type Archive struct { *Value } // Name returns the name of this archive. func (a *Archive) Name() string { return a.StringField("name") } // DisplayName returns the user friendly name of this archive. func (a *Archive) DisplayName() string { return a.StringField("displayname") } // Description returns a description string for this archive. func (a *Archive) Description() string { return a.StringField("description") } // Distro returns the distribution that uses this archive. func (a *Archive) Distro() (*Distro, error) { v, err := a.Link("distribution_link").Get(nil) if err != nil { return nil, err } return &Distro{v}, nil } // WebPage returns the URL for accessing this archive in a browser. func (a *Archive) WebPage() string { return a.StringField("web_link") } type PublishStatus string const ( PubPending PublishStatus = "Pending" PubPublished PublishStatus = "Published" PubSuperseded PublishStatus = "Superseded" PubDeleted PublishStatus = "Deleted" PubObsolete PublishStatus = "Obsolete" ) // Publication returns the publication history for the sourceName // source package in this archive that has the given status. func (a *Archive) Publication(sourceName string, status PublishStatus) (*PublicationList, error) { params := Params{ "ws.op": "getPublishedSources", "source_name": sourceName, "exact_match": "true", "pocket": "Release", "status": string(status), } v, err := a.Location("").Get(params) if err != nil { return nil, err } return &PublicationList{v}, nil } // ArchiveList represents a list of Archive objects. type ArchiveList struct { *Value } // For iterates over the list of archive objects and calls f for each one. // If f returns a non-nil error, iteration will stop and the error will be // returned as the result of For. func (list *ArchiveList) For(f func(a *Archive) error) error { return list.Value.For(func(v *Value) error { return f(&Archive{v}) }) } ���������������������������juju-core_1.18.1/src/launchpad.net/lpad/blueprint_test.go�������������������������������������������0000644�0000153�0000161�00000006300�12321735716�023275� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" ) func (s *ModelS) TestRootBlueprint(c *C) { testServer.PrepareResponse(200, jsonType, `{"name": "bp-name"}`) testServer.PrepareResponse(200, jsonType, `{"name": "bp-name"}`) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} project := &lpad.Project{lpad.NewValue(nil, "", "", M{"name": "myproject"})} distro := &lpad.Distro{lpad.NewValue(nil, "", "", M{"name": "mydistro"})} blueprint, err := root.Blueprint(project, "bp-param") c.Assert(err, IsNil) c.Assert(blueprint.Name(), Equals, "bp-name") req := testServer.WaitRequest() c.Assert(req.URL.Path, Equals, "/myproject/+spec/bp-param") blueprint, err = root.Blueprint(distro, "bp-param") c.Assert(err, IsNil) c.Assert(blueprint.Name(), Equals, "bp-name") req = testServer.WaitRequest() c.Assert(req.URL.Path, Equals, "/mydistro/+spec/bp-param") } func (s *ModelS) TestBlueprint(c *C) { m := M{ "name": "thename", "title": "Title", "summary": "Summary", "whiteboard": "Whiteboard", "web_link": "http://page", } project := &lpad.Blueprint{lpad.NewValue(nil, "", "", m)} c.Assert(project.Name(), Equals, "thename") c.Assert(project.Title(), Equals, "Title") c.Assert(project.Summary(), Equals, "Summary") c.Assert(project.Whiteboard(), Equals, "Whiteboard") c.Assert(project.WebPage(), Equals, "http://page") project.SetName("newname") project.SetTitle("New Title") project.SetSummary("New summary") project.SetWhiteboard("New whiteboard") c.Assert(project.Name(), Equals, "newname") c.Assert(project.Title(), Equals, "New Title") c.Assert(project.Summary(), Equals, "New summary") c.Assert(project.Whiteboard(), Equals, "New whiteboard") //testServer.PrepareResponse(200, jsonType, `{"name": "seriesname"}`) //series, err := project.FocusSeries() //c.Assert(err, IsNil) //c.Assert(series.Name(), Equals, "seriesname") //req := testServer.WaitRequest() //c.Assert(req.Method, Equals, "GET") //c.Assert(req.URL.Path, Equals, "/focus_link") } func (s *ModelS) TestBlueprintLinkBranch(c *C) { testServer.PrepareResponse(200, jsonType, `{}`) bp := &lpad.Blueprint{lpad.NewValue(nil, "", testServer.URL+"/project/+spec/the-bp", nil)} branch := &lpad.Branch{lpad.NewValue(nil, testServer.URL, testServer.URL+"~joe/ensemble/some-branch", nil)} err := bp.LinkBranch(branch) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/project/+spec/the-bp") c.Assert(req.Form["ws.op"], DeepEquals, []string{"linkBranch"}) c.Assert(req.Form["branch"], DeepEquals, []string{branch.AbsLoc()}) } func (s *ModelS) TestBlueprintLinkBug(c *C) { testServer.PrepareResponse(200, jsonType, `{}`) bp := &lpad.Blueprint{lpad.NewValue(nil, "", testServer.URL+"/project/+spec/the-bp", nil)} bug := &lpad.Bug{lpad.NewValue(nil, testServer.URL, testServer.URL+"~joe/ensemble/some-bug", nil)} err := bp.LinkBug(bug) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/project/+spec/the-bp") c.Assert(req.Form["ws.op"], DeepEquals, []string{"linkBug"}) c.Assert(req.Form["bug"], DeepEquals, []string{bug.AbsLoc()}) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/build_test.go�����������������������������������������������0000644�0000153�0000161�00000005541�12321735716�022376� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" ) func (s *ModelS) TestBuild(c *C) { m := M{ "title": "thetitle", "arch_tag": "armel", "buildstate": "Failed to build", "build_log_url": "http://logurl", "upload_log_url": "http://uploadurl", "web_link": "http://page", "datecreated": "2011-10-10T00:00:00", "datebuilt": "2011-10-10T00:00:10", "current_source_publication_link": testServer.URL + "/pub_link", } build := &lpad.Build{lpad.NewValue(nil, "", "", m)} c.Assert(build.Title(), Equals, "thetitle") c.Assert(build.Arch(), Equals, "armel") c.Assert(build.State(), Equals, lpad.BuildState("Failed to build")) c.Assert(build.BuildLogURL(), Equals, "http://logurl") c.Assert(build.UploadLogURL(), Equals, "http://uploadurl") c.Assert(build.WebPage(), Equals, "http://page") c.Assert(build.Created(), Equals, "2011-10-10T00:00:00") c.Assert(build.Finished(), Equals, "2011-10-10T00:00:10") testServer.PrepareResponse(200, jsonType, `{"source_package_name": "packagename"}`) p, err := build.Publication() c.Assert(err, IsNil) c.Assert(p.PackageName(), Equals, "packagename") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/pub_link") } func (s *ModelS) TestBuildRetry(c *C) { testServer.PrepareResponse(200, jsonType, "{}") build := &lpad.Build{lpad.NewValue(nil, testServer.URL, testServer.URL + "/build", nil)} err := build.Retry() c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/build") c.Assert(req.Form["ws.op"], DeepEquals, []string{"retry"}) } func (s *ModelS) TestPublication(c *C) { m := M{ "source_package_name": "pkgname", "source_package_version": "pkgversion", "component_name": "main", "distro_series_link": testServer.URL + "/distro_series_link", "archive_link": testServer.URL + "/archive_link", } p := &lpad.Publication{lpad.NewValue(nil, "", "", m)} c.Assert(p.PackageName(), Equals, "pkgname") c.Assert(p.PackageVersion(), Equals, "pkgversion") c.Assert(p.Component(), Equals, "main") testServer.PrepareResponse(200, jsonType, `{"name": "archivename"}`) archive, err := p.Archive() c.Assert(err, IsNil) c.Assert(archive.Name(), Equals, "archivename") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/archive_link") testServer.PrepareResponse(200, jsonType, `{"name": "seriesname"}`) series, err := p.DistroSeries() c.Assert(err, IsNil) c.Assert(series.Name(), Equals, "seriesname") req = testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/distro_series_link") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/blueprint.go������������������������������������������������0000644�0000153�0000161�00000006062�12321735716�022243� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad import ( "fmt" ) // BlueprintTarget is implemented by types that may be used as // targets for blueprints, such as *Project and *Distro. type BlueprintTarget interface { Name() string BlueprintTarget() } // Blueprint returns the named blueprint associated with target. func (root *Root) Blueprint(target BlueprintTarget, name string) (*Blueprint, error) { v, err := root.Location(fmt.Sprintf("/%s/+spec/%s", target.Name(), name)).Get(nil) if err != nil { return nil, err } return &Blueprint{v}, nil } // The Blueprint type represents a blueprint in Launchpad. type Blueprint struct { *Value } // Name returns the blueprint name. May contain lower-case letters, numbers, // and dashes. It will be used in the specification url. // Examples: mozilla-type-ahead-find, postgres-smart-serial. func (bp *Blueprint) Name() string { return bp.StringField("name") } // SetName changes the blueprint name which must consist of lower-case // letters, numbers, and dashes. It will be used in the specification url. // Examples: mozilla-type-ahead-find, postgres-smart-serial. // Patch must be called to commit all changes. func (bp *Blueprint) SetName(name string) { bp.SetField("name", name) } // Title returns the blueprint title that should describe the feature // as clearly as possible, in up to 70 characters. This title is // displayed in every feature list or report. func (bp *Blueprint) Title() string { return bp.StringField("title") } // SetTitle sets the blueprint title. The title must describe the feature // as clearly as possible, in up to 70 characters. This title is displayed // in every feature list or report. func (bp *Blueprint) SetTitle(title string) { bp.SetField("title", title) } // Summary returns the blueprint summary which should consist of a single // paragraph description of the feature. func (bp *Blueprint) Summary() string { return bp.StringField("summary") } // SetSummary changes the blueprint summary which must consist of a single // paragraph description of the feature. func (bp *Blueprint) SetSummary(summary string) { bp.SetField("summary", summary) } // Whiteboard returns the blueprint whiteboard which contains any notes // on the status of this specification. func (bp *Blueprint) Whiteboard() string { return bp.StringField("whiteboard") } // SetWhiteboard changes the blueprint whiteboard that may contain any // notes on the status of this specification. func (bp *Blueprint) SetWhiteboard(whiteboard string) { bp.SetField("whiteboard", whiteboard) } // WebPage returns the URL for accessing this blueprint in a browser. func (bp *Blueprint) WebPage() string { return bp.StringField("web_link") } // LinkBranch associates a branch with this blueprint. func (bp *Blueprint) LinkBranch(branch *Branch) error { params := Params{ "ws.op": "linkBranch", "branch": branch.AbsLoc(), } _, err := bp.Post(params) return err } // LinkBug associates a bug with this blueprint. func (bp *Blueprint) LinkBug(bug *Bug) error { params := Params{ "ws.op": "linkBug", "bug": bug.AbsLoc(), } _, err := bp.Post(params) return err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/oauth_test.go�����������������������������������������������0000644�0000153�0000161�00000020335�12321735716�022415� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( "encoding/json" "errors" "io/ioutil" . "launchpad.net/gocheck" "launchpad.net/lpad" "net/http" "net/url" "os" "path/filepath" "strings" ) var _ = Suite(&OAuthS{}) var _ = Suite(&OAuthI{}) type OAuthS struct { HTTPSuite } type OAuthI struct { SuiteI } func (s *OAuthS) TestRequestToken(c *C) { gotAuthURL := "" callback := func(oauth *lpad.OAuth) error { gotAuthURL = oauth.AuthURL return errors.New("STOP!") } oauth := lpad.OAuth{ Callback: callback, } testServer.PrepareResponse(200, nil, "oauth_token=mytoken&oauth_token_secret=mysecret") err := oauth.Login(testServer.URL) c.Assert(err, ErrorMatches, "STOP!") c.Assert(gotAuthURL, Equals, testServer.URL+"/+authorize-token?oauth_token=mytoken") c.Assert(oauth.BaseURL, Equals, testServer.URL) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/+request-token") c.Assert(req.Form["oauth_consumer_key"], DeepEquals, []string{"https://launchpad.net/lpad"}) c.Assert(req.Form["oauth_signature_method"], DeepEquals, []string{"PLAINTEXT"}) c.Assert(req.Form["oauth_signature"], DeepEquals, []string{"&"}) c.Assert(oauth.Token, Equals, "mytoken") c.Assert(oauth.TokenSecret, Equals, "mysecret") } func (s *OAuthS) TestBaseURLStripping(c *C) { // https://api.launchpad.net/1.0/ as a BaseURL must // yield a https://launchpad.net/ BaseURL for auth. callback := func(oauth *lpad.OAuth) error { return errors.New("STOP!") } oauth := lpad.OAuth{ Callback: callback, } testServer.PrepareResponse(200, nil, "oauth_token=mytoken&oauth_token_secret=mysecret") url_, err := url.Parse(testServer.URL) c.Assert(err, IsNil) url_.Host = "api." + url_.Host url_.Path = "/1.0/" c.Assert(url_.String(), Matches, `http://api\..*/1\.0/`) err = oauth.Login(url_.String()) c.Assert(err, ErrorMatches, "STOP!") c.Assert(oauth.BaseURL, Equals, testServer.URL) } func (s *OAuthS) TestRequestTokenWithConsumer(c *C) { callback := func(oauth *lpad.OAuth) error { return errors.New("STOP!") } oauth := lpad.OAuth{ Callback: callback, Consumer: "myconsumer", } testServer.PrepareResponse(200, nil, "oauth_token=mytoken&oauth_token_secret=mysecret") err := oauth.Login(testServer.URL) c.Assert(err, ErrorMatches, "STOP!") req := testServer.WaitRequest() c.Assert(req.Form["oauth_consumer_key"], DeepEquals, []string{"myconsumer"}) } func (s *OAuthS) TestCallbackURL(c *C) { gotAuthURL := "" callback := func(oauth *lpad.OAuth) error { gotAuthURL = oauth.AuthURL return errors.New("STOP!") } oauth := lpad.OAuth{ CallbackURL: "http://example.com", Callback: callback, } testServer.PrepareResponse(200, nil, "oauth_token=mytoken&oauth_token_secret=mysecret") err := oauth.Login(testServer.URL) c.Assert(err, ErrorMatches, "STOP!") u, err := url.Parse(gotAuthURL) c.Assert(err, IsNil) c.Assert(u.Path, Equals, "/+authorize-token") q, err := url.ParseQuery(u.RawQuery) c.Assert(err, IsNil) c.Assert(q["oauth_token"], DeepEquals, []string{"mytoken"}) c.Assert(q["oauth_callback"], DeepEquals, []string{"http://example.com"}) } func (s *OAuthS) TestAccessToken(c *C) { testServer.PrepareResponse(200, nil, "oauth_token=mytoken1&oauth_token_secret=mysecret1") testServer.PrepareResponse(200, nil, "oauth_token=mytoken2&oauth_token_secret=mysecret2") oauth := lpad.OAuth{} err := oauth.Login(testServer.URL) c.Assert(err, IsNil) req1 := testServer.WaitRequest() c.Assert(req1.URL.Path, Equals, "/+request-token") req2 := testServer.WaitRequest() c.Assert(req2.Method, Equals, "POST") c.Assert(req2.URL.Path, Equals, "/+access-token") c.Assert(req2.Form["oauth_token"], DeepEquals, []string{"mytoken1"}) c.Assert(req2.Form["oauth_signature"], DeepEquals, []string{"&mysecret1"}) c.Assert(oauth.Token, Equals, "mytoken2") c.Assert(oauth.TokenSecret, Equals, "mysecret2") c.Assert(oauth.AuthURL, Equals, "") } func (s *OAuthS) TestSign(c *C) { oauth := lpad.OAuth{ Token: "my token", TokenSecret: "my secret", } req, err := http.NewRequest("GET", "http://example.com/path", nil) c.Assert(err, IsNil) err = oauth.Sign(req) c.Assert(err, IsNil) auth := req.Header.Get("Authorization") parts := strings.Split(auth, ", ") c.Assert(parts[0], Equals, `OAuth realm="https://api.launchpad.net/"`) c.Assert(parts[1], Equals, `oauth_consumer_key="https%3A%2F%2Flaunchpad.net%2Flpad"`) c.Assert(parts[2], Equals, `oauth_token="my+token"`) c.Assert(parts[3], Equals, `oauth_signature_method="PLAINTEXT"`) c.Assert(parts[4], Equals, `oauth_signature="%26my+secret"`) c.Assert(parts[5], Matches, `oauth_timestamp="[0-9]+"`) c.Assert(parts[6], Matches, `oauth_nonce="[0-9]+"`) c.Assert(parts[7], Equals, `oauth_version="1.0"`) } func (s *OAuthS) TestSignWithConsumer(c *C) { oauth := lpad.OAuth{ Token: "my token", TokenSecret: "my secret", Consumer: "my consumer", } req, err := http.NewRequest("GET", "http://example.com/path", nil) c.Assert(err, IsNil) err = oauth.Sign(req) c.Assert(err, IsNil) auth := req.Header.Get("Authorization") c.Assert(auth, Matches, `.* oauth_consumer_key="my\+consumer".*`) } func (s *OAuthS) TestSignWithAnonymous(c *C) { oauth := lpad.OAuth{ Consumer: "my consumer", Anonymous: true, } req, err := http.NewRequest("GET", "http://example.com/path", nil) c.Assert(err, IsNil) err = oauth.Sign(req) c.Assert(err, IsNil) auth := req.Header.Get("Authorization") c.Assert(auth, Matches, `.* oauth_consumer_key="my\+consumer".*`) c.Assert(auth, Matches, `.* oauth_token="".*`) c.Assert(auth, Matches, `.* oauth_signature="%26".*`) } func (s *OAuthS) TestSignError(c *C) { err := (&lpad.OAuth{}).Sign(nil) c.Assert(err, ErrorMatches, `OAuth can't Sign without a token \(missing Login\?\)`) err = (&lpad.OAuth{Token: "mytoken"}).Sign(nil) c.Assert(err, ErrorMatches, `OAuth can't Sign without a token secret \(missing Login\?\)`) } func (s *OAuthS) TestDontLoginWithExistingSecret(c *C) { callback := func(oauth *lpad.OAuth) error { c.Error("Callback called!") return errors.New("STOP!") } oauth := lpad.OAuth{ Token: "initialtoken", TokenSecret: "initialsecret", Callback: callback, } err := oauth.Login(testServer.URL) c.Assert(err, IsNil) } func (s *OAuthS) TestDontLoginWithAnonymous(c *C) { callback := func(oauth *lpad.OAuth) error { c.Error("Callback called!") return errors.New("STOP!") } oauth := lpad.OAuth{ Anonymous: true, Callback: callback, } err := oauth.Login(testServer.URL) c.Assert(err, IsNil) } func fakeHome(c *C) (dir string, restore func()) { realHome := os.Getenv("HOME") fakeHome := c.MkDir() restore = func() { os.Setenv("HOME", realHome) } os.Setenv("HOME", fakeHome) return fakeHome, restore } func (s *OAuthS) TestStoredOAuthLoginWithStored(c *C) { home, restore := fakeHome(c) defer restore() file, err := os.Create(filepath.Join(home, ".lpad_oauth")) c.Assert(err, IsNil) data, err := json.Marshal(M{"Token": "mytoken", "TokenSecret": "mysecret"}) c.Assert(err, IsNil) file.Write(data) file.Close() oauth := lpad.StoredOAuth{} oauth.Login("baseURL") c.Assert(oauth.Token, Equals, "mytoken") c.Assert(oauth.TokenSecret, Equals, "mysecret") } func (s *OAuthS) TestStoredOAuthLogin(c *C) { home, restore := fakeHome(c) defer restore() testServer.PrepareResponse(200, nil, "oauth_token=mytoken1&oauth_token_secret=mysecret1") testServer.PrepareResponse(200, nil, "oauth_token=mytoken2&oauth_token_secret=mysecret2") oauth := lpad.StoredOAuth{} err := oauth.Login(testServer.URL) c.Assert(err, IsNil) req1 := testServer.WaitRequest() c.Assert(req1.URL.Path, Equals, "/+request-token") req2 := testServer.WaitRequest() c.Assert(req2.Method, Equals, "POST") c.Assert(req2.URL.Path, Equals, "/+access-token") c.Assert(oauth.Token, Equals, "mytoken2") c.Assert(oauth.TokenSecret, Equals, "mysecret2") c.Assert(oauth.AuthURL, Equals, "") data, err := ioutil.ReadFile(filepath.Join(home, ".lpad_oauth")) c.Assert(err, IsNil) result := lpad.OAuth{} err = json.Unmarshal(data, &result) c.Assert(err, IsNil) c.Assert(result.Token, Equals, "mytoken2") c.Assert(result.TokenSecret, Equals, "mysecret2") } func (s *OAuthS) TestStoredOAuthSignForwards(c *C) { err := (&lpad.StoredOAuth{}).Sign(nil) c.Assert(err, ErrorMatches, `OAuth can't Sign without a token \(missing Login\?\)`) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/distro_test.go����������������������������������������������0000644�0000153�0000161�00000013462�12321735716�022604� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" "time" ) func (s *ModelS) TestDistro(c *C) { m := M{ "name": "thename", "display_name": "Display Name", "title": "Title", "summary": "Summary", "description": "Description", "web_link": "http://page", "current_series_link": testServer.URL + "/focus_link", } distro := &lpad.Distro{lpad.NewValue(nil, "", "", m)} c.Assert(distro.Name(), Equals, "thename") c.Assert(distro.DisplayName(), Equals, "Display Name") c.Assert(distro.Title(), Equals, "Title") c.Assert(distro.Summary(), Equals, "Summary") c.Assert(distro.Description(), Equals, "Description") c.Assert(distro.WebPage(), Equals, "http://page") distro.SetName("newname") distro.SetDisplayName("New Display Name") distro.SetTitle("New Title") distro.SetSummary("New summary") distro.SetDescription("New description") c.Assert(distro.Name(), Equals, "newname") c.Assert(distro.DisplayName(), Equals, "New Display Name") c.Assert(distro.Title(), Equals, "New Title") c.Assert(distro.Summary(), Equals, "New summary") c.Assert(distro.Description(), Equals, "New description") testServer.PrepareResponse(200, jsonType, `{"name": "seriesname"}`) series, err := distro.FocusSeries() c.Assert(err, IsNil) c.Assert(series.Name(), Equals, "seriesname") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/focus_link") } func (s *ModelS) TestDistroSeries(c *C) { m := M{ "name": "thename", "displayname": "Display Name", "fullseriesname": "Full Series Name", "title": "Title", "summary": "Summary", "description": "Description", "active": true, "web_link": "http://page", } series := &lpad.DistroSeries{lpad.NewValue(nil, "", "", m)} c.Assert(series.Name(), Equals, "thename") c.Assert(series.DisplayName(), Equals, "Display Name") c.Assert(series.FullSeriesName(), Equals, "Full Series Name") c.Assert(series.Title(), Equals, "Title") c.Assert(series.Summary(), Equals, "Summary") c.Assert(series.Description(), Equals, "Description") c.Assert(series.Active(), Equals, true) c.Assert(series.WebPage(), Equals, "http://page") series.SetName("newname") series.SetTitle("New Title") series.SetSummary("New summary") series.SetActive(false) c.Assert(series.Name(), Equals, "newname") c.Assert(series.Title(), Equals, "New Title") c.Assert(series.Summary(), Equals, "New summary") c.Assert(series.Active(), Equals, false) } func (s *ModelS) TestRootDistro(c *C) { data := `{ "name": "Name", "title": "Title", "description": "Description" }` testServer.PrepareResponse(200, jsonType, data) root := lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} distro, err := root.Distro("myproj") c.Assert(err, IsNil) c.Assert(distro.Name(), Equals, "Name") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/myproj") } func (s *ModelS) TestDistroActiveMilestones(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [{ "self_link": "http://self0", "name": "Name0" }, { "self_link": "http://self1", "name": "Name1" }] }` testServer.PrepareResponse(200, jsonType, data) m := M{ "active_milestones_collection_link": testServer.URL + "/col_link", } distro := lpad.Distro{lpad.NewValue(nil, testServer.URL, "", m)} list, err := distro.ActiveMilestones() c.Assert(err, IsNil) c.Assert(list.TotalSize(), Equals, 2) names := []string{} list.For(func(ms *lpad.Milestone) error { names = append(names, ms.Name()) return nil }) c.Assert(names, DeepEquals, []string{"Name0", "Name1"}) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/col_link") } func (s *ModelS) TestDistroAllSeries(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [{ "self_link": "http://self0", "name": "Name0" }, { "self_link": "http://self1", "name": "Name1" }] }` testServer.PrepareResponse(200, jsonType, data) m := M{ "series_collection_link": testServer.URL + "/col_link", } distro := lpad.Distro{lpad.NewValue(nil, testServer.URL, "", m)} list, err := distro.AllSeries() c.Assert(err, IsNil) c.Assert(list.TotalSize(), Equals, 2) names := []string{} list.For(func(s *lpad.DistroSeries) error { names = append(names, s.Name()) return nil }) c.Assert(names, DeepEquals, []string{"Name0", "Name1"}) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/col_link") } func (s *ModelS) TestBranchTips(c *C) { data := `[["lp:a", "rev1", ["series1", "series2"]], ["lp:b", "rev2", []]]` testServer.PrepareResponse(200, jsonType, data) distro := lpad.Distro{lpad.NewValue(nil, testServer.URL, testServer.URL+"/distro", nil)} tips, err := distro.BranchTips(time.Time{}) c.Assert(err, IsNil) c.Assert(tips, DeepEquals, []lpad.BranchTip{ {"lp:a", "rev1", []string{"series1", "series2"}}, {"lp:b", "rev2", []string{}}, }) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/distro") c.Assert(req.Form["ws.op"], DeepEquals, []string{"getBranchTips"}) c.Assert(req.Form["since"], DeepEquals, []string(nil)) } func (s *ModelS) TestBranchTipsWithSince(c *C) { testServer.PrepareResponse(200, jsonType, "[]") distro := lpad.Distro{lpad.NewValue(nil, testServer.URL, testServer.URL+"/distro", nil)} tips, err := distro.BranchTips(time.Unix(1316567786, 0)) c.Assert(err, IsNil) c.Assert(tips, DeepEquals, []lpad.BranchTip(nil)) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/distro") c.Assert(req.Form["ws.op"], DeepEquals, []string{"getBranchTips"}) c.Assert(req.Form["since"], DeepEquals, []string{"2011-09-21T01:16:26Z"}) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/source_test.go����������������������������������������������0000644�0000153�0000161�00000004445�12321735716�022601� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" ) func (s *ModelS) TestSourcePackage(c *C) { m := M{ "name": "thename", "displayname": "Display Name", "latest_published_component_name": "universe", "web_link": "http://page", "self_link": "http://selfpage", "distribution_link": testServer.URL + "/distribution_link", "distroseries_link": testServer.URL + "/distroseries_link", } source := &lpad.SourcePackage{lpad.NewValue(nil, "", "", m)} c.Assert(source.Name(), Equals, "thename") c.Assert(source.DisplayName(), Equals, "Display Name") c.Assert(source.LatestComponent(), Equals, "universe") c.Assert(source.WebPage(), Equals, "http://page") testServer.PrepareResponse(200, jsonType, `{"name": "distroname"}`) distro, err := source.Distro() c.Assert(err, IsNil) c.Assert(distro.Name(), Equals, "distroname") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/distribution_link") testServer.PrepareResponse(200, jsonType, `{"name": "seriesname"}`) series, err := source.DistroSeries() c.Assert(err, IsNil) c.Assert(series.Name(), Equals, "seriesname") req = testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/distroseries_link") } func (s *ModelS) TestDistroSourcePackage(c *C) { m := M{ "name": "thename", "display_name": "Display Name", "title": "title", "web_link": "http://page", "self_link": "http://selfpage", "distribution_link": testServer.URL + "/distribution_link", } source := &lpad.DistroSourcePackage{lpad.NewValue(nil, "", "", m)} c.Assert(source.Name(), Equals, "thename") c.Assert(source.DisplayName(), Equals, "Display Name") c.Assert(source.Title(), Equals, "title") c.Assert(source.WebPage(), Equals, "http://page") testServer.PrepareResponse(200, jsonType, `{"name": "distroname"}`) distro, err := source.Distro() c.Assert(err, IsNil) c.Assert(distro.Name(), Equals, "distroname") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/distribution_link") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/project.go��������������������������������������������������0000644�0000153�0000161�00000021235�12321735716�021704� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad import "net/url" // Project returns a project with the given name. func (root *Root) Project(name string) (*Project, error) { r, err := root.Location("/" + url.QueryEscape(name)).Get(nil) if err != nil { return nil, err } return &Project{r}, nil } // The Project type represents a project in Launchpad. type Project struct { *Value } // Name returns the project name, which is composed of at least one // lowercase letter or number, followed by letters, numbers, dots, // hyphens or pluses. This is a short name used in URLs. func (p *Project) Name() string { return p.StringField("name") } // DisplayName returns the project name as it would be displayed // in a paragraph. For example, a project's title might be // "The Foo Project" and its display name could be "Foo". func (p *Project) DisplayName() string { return p.StringField("display_name") } // Title returns the project title as it might be used in isolation. // For example, a project's title might be "The Foo Project" and its // display name could be "Foo". func (p *Project) Title() string { return p.StringField("title") } // Summary returns the project summary, which is a short paragraph // to introduce the project's work. func (p *Project) Summary() string { return p.StringField("summary") } // Description returns the project description. func (p *Project) Description() string { return p.StringField("description") } // WebPage returns the URL for accessing this project in a browser. func (p *Project) WebPage() string { return p.StringField("web_link") } // SetName changes the project name, which must be composed of at // least one lowercase letter or number, followed by letters, numbers, // dots, hyphens or pluses. This is a short name used in URLs. // Patch must be called to commit all changes. func (p *Project) SetName(name string) { p.SetField("name", name) } // SetDisplayName changes the project name as it would be displayed // in a paragraph. For example, a project's title might be // "The Foo Project" and its display name could be "Foo". // Patch must be called to commit all changes. func (p *Project) SetDisplayName(name string) { p.SetField("display_name", name) } // SetTitle changes the project title as it would be displayed // in isolation. For example, the project title might be // "The Foo Project" and display name could be "Foo". // Patch must be called to commit all changes. func (p *Project) SetTitle(title string) { p.SetField("title", title) } // SetSummary changes the project summary, which is a short paragraph // to introduce the project's work. // Patch must be called to commit all changes. func (p *Project) SetSummary(title string) { p.SetField("summary", title) } // SetDescription changes the project's description. // Patch must be called to commit all changes. func (p *Project) SetDescription(description string) { p.SetField("description", description) } // ActiveMilestones returns the list of active milestones associated with // the project, ordered by the target date. func (p *Project) ActiveMilestones() (*MilestoneList, error) { r, err := p.Link("active_milestones_collection_link").Get(nil) if err != nil { return nil, err } return &MilestoneList{r}, nil } // AllSeries returns the list of series associated with the project. func (p *Project) AllSeries() (*ProjectSeriesList, error) { r, err := p.Link("series_collection_link").Get(nil) if err != nil { return nil, err } return &ProjectSeriesList{r}, nil } // FocusSeries returns the development series set as the current // development focus. func (p *Project) FocusSeries() (*ProjectSeries, error) { r, err := p.Link("development_focus_link").Get(nil) if err != nil { return nil, err } return &ProjectSeries{r}, nil } // BlueprintTarget marks *Project as being a target for blueprints. func (p *Project) BlueprintTarget() {} // The Milestone type represents a milestone associated with a project // or distribution. type Milestone struct { *Value } // Name returns the milestone name, which consists of only // letters, numbers, and simple punctuation. func (ms *Milestone) Name() string { return ms.StringField("name") } // CodeName returns the alternative name for the milestone, if any. func (ms *Milestone) CodeName() string { return ms.StringField("code_name") } // Title returns the milestone context title for pages. func (ms *Milestone) Title() string { return ms.StringField("title") } // Summary returns the summary of features and status of this milestone. func (ms *Milestone) Summary() string { return ms.StringField("summary") } // WebPage returns the URL for accessing this milestone in a browser. func (ms *Milestone) WebPage() string { return ms.StringField("web_link") } // Active returns true if the milestone is still in active development. func (ms *Milestone) Active() bool { return ms.BoolField("is_active") } // Date returns the target date for the milestone. func (ms *Milestone) Date() string { return ms.StringField("date_targeted") } // SetName changes the milestone name, which must consists of // only letters, numbers, and simple punctuation. func (ms *Milestone) SetName(name string) { ms.SetField("name", name) } // SetCodeName sets the alternative name for the milestone. func (ms *Milestone) SetCodeName(name string) { ms.SetField("code_name", name) } // SetTitle changes the milestone's context title for pages. func (ms *Milestone) SetTitle(title string) { ms.SetField("title", title) } // SetSummary sets the summary of features and status of this milestone. func (ms *Milestone) SetSummary(summary string) { ms.SetField("summary", summary) } // SetActive sets whether the milestone is still in active // development or not. func (ms Milestone) SetActive(active bool) { ms.SetField("is_active", active) } // SetDate changes the target date for the milestone. func (ms *Milestone) SetDate(date string) { ms.SetField("date_targeted", date) } // The MilestoneList type represents a list of milestones that // may be iterated over. type MilestoneList struct { *Value } // For iterates over the list of milestones and calls f for each one. // If f returns a non-nil error, iteration will stop and the error will // be returned as the result of For. func (list *MilestoneList) For(f func(m *Milestone) error) error { return list.Value.For(func(r *Value) error { return f(&Milestone{r}) }) } // The ProjectSeries type represents a series associated with a project. type ProjectSeries struct { *Value } // Name returns the series name, which is a unique name that identifies // it and is used in URLs. It consists of only lowercase letters, digits, // and simple punctuation. For example, "2.0" or "trunk". func (s *ProjectSeries) Name() string { return s.StringField("name") } // Title returns the series context title for pages. func (s *ProjectSeries) Title() string { return s.StringField("title") } // Summary returns the summary for this project series. func (s *ProjectSeries) Summary() string { return s.StringField("summary") } // WebPage returns the URL for accessing this project series in a browser. func (s *ProjectSeries) WebPage() string { return s.StringField("web_link") } // Active returns true if this project series is still in active development. func (s *ProjectSeries) Active() bool { return s.BoolField("is_active") } // Branch returns the Bazaar branch associated with this project series. func (s *ProjectSeries) Branch() (*Branch, error) { r, err := s.Link("branch_link").Get(nil) if err != nil { return nil, err } return &Branch{r}, nil } // SetName changes the series name, which must consists of only letters, // numbers, and simple punctuation. For example: "2.0" or "trunk". func (s *ProjectSeries) SetName(name string) { s.SetField("name", name) } // SetTitle changes the series title. func (s *ProjectSeries) SetTitle(title string) { s.SetField("title", title) } // SetSummary changes the summary for this project series. func (s *ProjectSeries) SetSummary(summary string) { s.SetField("summary", summary) } // SetActive sets whether the series is still in active development or not. func (s *ProjectSeries) SetActive(active bool) { s.SetField("is_active", active) } // SetBranch changes the Bazaar branch associated with this project series. func (s *ProjectSeries) SetBranch(branch *Branch) { s.SetField("branch_link", branch.AbsLoc()) } // The ProjectSeriesList represents a list of project series. type ProjectSeriesList struct { *Value } // For iterates over the list of series and calls f for each one. // If f returns a non-nil error, iteration will stop and the error will // be returned as the result of For. func (list *ProjectSeriesList) For(f func(s *ProjectSeries) error) error { return list.Value.For(func(r *Value) error { return f(&ProjectSeries{r}) }) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/session_test.go���������������������������������������������0000644�0000153�0000161�00000003031�12321735716�022752� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" "net/http" ) var _ = Suite(&SessionS{}) var _ = Suite(&SessionI{}) type SessionS struct { HTTPSuite } type SessionI struct { SuiteI } type dummyAuth struct { loginBaseURL string loginErr error signReq *http.Request signErr error } func (a *dummyAuth) Login(baseURL string) error { a.loginBaseURL = baseURL return a.loginErr } func (a *dummyAuth) Sign(r *http.Request) error { a.signReq = r return a.signErr } func (s *SessionS) TestLogin(c *C) { testServer.PrepareResponse(200, jsonType, `{"ok": true}`) auth := &dummyAuth{} root, err := lpad.Login(lpad.APIBase(testServer.URL), auth) c.Assert(err, IsNil) c.Assert(auth.loginBaseURL, Equals, testServer.URL) c.Assert(root.BaseLoc(), Equals, testServer.URL) c.Assert(root.AbsLoc(), Equals, testServer.URL) c.Assert(len(root.Map()), Equals, 0) _, err = root.Get(nil) c.Assert(err, IsNil) c.Assert(root.Map()["ok"], Equals, true) c.Assert(auth.signReq, NotNil) c.Assert(auth.signReq.URL.String(), Equals, testServer.URL) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/") } var lpadAuth = &lpad.OAuth{ Token: "SfVJpl7pJgSLJX9cm0wj", TokenSecret: "CXJGg1t5gTdjDqtFG0HNBFQn8WLWq8QQ3B2sHh9NmgLxQ6kGl9m123gQLZpDF8HFxQzk8HV78c9sGHQb", } func (s *SessionI) TestLogin(c *C) { root, err := lpad.Login(lpad.Production, lpadAuth) me, err := root.Me() c.Assert(err, IsNil) c.Assert(me.DisplayName(), Equals, "Lpad Test User") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/value_test.go�����������������������������������������������0000644�0000153�0000161�00000041103�12321735716�022405� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( "encoding/json" "errors" "fmt" . "launchpad.net/gocheck" "launchpad.net/lpad" "net/url" "strconv" ) var _ = Suite(&ValueS{}) var _ = Suite(&ValueI{}) type ValueS struct { HTTPSuite } type ValueI struct { SuiteI } var jsonType = map[string]string{ "Content-Type": "application/json", } func (s *ValueS) TestIsValid(c *C) { var v *lpad.Value c.Assert(v.IsValid(), Equals, false) v = lpad.NewValue(nil, "", "", nil) c.Assert(v.IsValid(), Equals, true) } func (s *ValueS) TestMapInit(c *C) { v := lpad.NewValue(nil, "", "", nil) m := v.Map() c.Assert(m, NotNil) m["a"] = 1 c.Assert(v.Map()["a"], Equals, 1) } func (s *ValueS) TestFieldMethods(c *C) { m := M{ "n": nil, "s": "string", "f": 42.1, "b": true, "l": []interface{}{"1", "2", 3}, } v := lpad.NewValue(nil, "", "", m) c.Assert(v.StringField("s"), Equals, "string") c.Assert(v.StringField("n"), Equals, "") c.Assert(v.StringField("x"), Equals, "") c.Assert(v.IntField("f"), Equals, 42) c.Assert(v.IntField("n"), Equals, 0) c.Assert(v.IntField("x"), Equals, 0) c.Assert(v.FloatField("f"), Equals, 42.1) c.Assert(v.FloatField("n"), Equals, 0.0) c.Assert(v.FloatField("x"), Equals, 0.0) c.Assert(v.BoolField("b"), Equals, true) c.Assert(v.BoolField("n"), Equals, false) c.Assert(v.BoolField("x"), Equals, false) c.Assert(v.StringListField("l"), DeepEquals, []string{"1", "2"}) } func (s *ValueS) TestGet(c *C) { testServer.PrepareResponse(200, jsonType, `{"a": 1, "b": [1, 2]}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) o, err := v.Get(nil) c.Assert(err, IsNil) c.Assert(&o, DeepEquals, &v) c.Assert(v.Map()["a"], Equals, float64(1)) c.Assert(v.Map()["b"], DeepEquals, []interface{}{float64(1), float64(2)}) c.Assert(v.AbsLoc(), Equals, testServer.URL+"/myvalue") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/myvalue") c.Assert(req.Header.Get("Accept"), Equals, "application/json") } func (s *ValueS) TestGetNull(c *C) { // In certain cases, like branch's getByUrl ws.op, Launchpad returns // 200 + null for what is actually a not found object. testServer.PrepareResponse(200, jsonType, "null") v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) o, err := v.Get(nil) c.Assert(err, Equals, lpad.ErrNotFound) c.Assert(o, IsNil) } func (s *ValueS) TestGetList(c *C) { testServer.PrepareResponse(200, jsonType, `["a", "b", "c"]`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err := v.Get(nil) c.Assert(err, IsNil) c.Assert(v.Map()["value"], DeepEquals, []interface{}{"a", "b", "c"}) } func (s *ValueS) TestGetAbsLoc(c *C) { data := `{"a": 1, "self_link": "` + testServer.URL + `/self_link"}` testServer.PrepareResponse(200, jsonType, data) testServer.PrepareResponse(200, jsonType, data) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err := v.Get(nil) c.Assert(err, IsNil) c.Assert(v.AbsLoc(), Equals, testServer.URL+"/self_link") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/myvalue") _, err = v.Get(nil) c.Assert(err, IsNil) c.Assert(v.AbsLoc(), Equals, testServer.URL+"/self_link") req = testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/self_link") } func (s *ValueS) TestGetWithParams(c *C) { testServer.PrepareResponse(200, jsonType, `{"ok": true}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err := v.Get(lpad.Params{"k": "v"}) c.Assert(err, IsNil) c.Assert(v.AbsLoc(), Equals, testServer.URL+"/myvalue") c.Assert(v.Map()["ok"], Equals, true) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/myvalue") c.Assert(req.URL.RawQuery, Equals, "k=v") } func (s *ValueS) TestGetWithParamsMerging(c *C) { testServer.PrepareResponse(200, jsonType, `{"ok": true}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue?k2=v2", nil) _, err := v.Get(lpad.Params{"k1": "v1"}) c.Assert(err, IsNil) c.Assert(v.Map()["ok"], Equals, true) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/myvalue") params, err := url.ParseQuery(req.URL.RawQuery) c.Assert(err, IsNil) c.Assert(params["k1"], DeepEquals, []string{"v1"}) c.Assert(params["k2"], DeepEquals, []string{"v2"}) } func (s *ValueS) TestGetSign(c *C) { oauth := &lpad.OAuth{Token: "mytoken", TokenSecret: "mytokensecret"} session := lpad.NewSession(oauth) testServer.PrepareResponse(200, jsonType, `{"ok": true}`) v := lpad.NewValue(session, "", testServer.URL+"/myvalue", nil) _, err := v.Get(nil) c.Assert(err, IsNil) c.Assert(v.Map()["ok"], Equals, true) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/myvalue") c.Assert(req.Header["Authorization"], NotNil) c.Assert(req.Header["Authorization"][0], Matches, "OAuth.*") } func (s *ValueS) TestGetRedirect(c *C) { headers := map[string]string{ "Location": testServer.URL + "/myothervalue", } testServer.PrepareResponse(303, headers, "") testServer.PrepareResponse(200, jsonType, `{"ok": true}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err := v.Get(nil) c.Assert(err, IsNil) c.Assert(v.AbsLoc(), Equals, testServer.URL+"/myothervalue") c.Assert(v.Map()["ok"], Equals, true) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/myvalue") } func (s *ValueS) TestGetRedirectWithParams(c *C) { headers := map[string]string{ "Location": testServer.URL + "/myothervalue?p=1", } testServer.PrepareResponse(303, headers, "") testServer.PrepareResponse(200, jsonType, `{"ok": true}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err := v.Get(lpad.Params{"k": "v"}) c.Assert(err, IsNil) c.Assert(v.AbsLoc(), Equals, testServer.URL+"/myothervalue?p=1") c.Assert(v.Map()["ok"], Equals, true) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/myvalue") c.Assert(req.Form.Get("k"), Equals, "v") req = testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/myothervalue") c.Assert(req.Form.Get("k"), Equals, "") } func (s *ValueS) TestGetNonJSONContent(c *C) { headers := map[string]string{ "Content-Type": "text/plain", } testServer.PrepareResponse(200, headers, "NOT JSON") v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err := v.Get(nil) c.Assert(err, ErrorMatches, "Non-JSON content-type: text/plain.*") } func (s *ValueS) TestGetError(c *C) { testServer.PrepareResponse(500, jsonType, `{"what": "ever"}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err := v.Get(nil) c.Assert(err, ErrorMatches, `Server returned 500 and body: {"what": "ever"}`) testServer.PrepareResponse(500, jsonType, "") v = lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err = v.Get(nil) c.Assert(err, ErrorMatches, `Server returned 500 and no body.`) testServer.PrepareResponse(404, jsonType, "") v = lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err = v.Get(nil) c.Assert(err, Equals, lpad.ErrNotFound) } func (s *ValueS) TestGetRedirectWithoutLocation(c *C) { headers := map[string]string{ "Content-Type": "application/json", // Should be ignored. } testServer.PrepareResponse(303, headers, `{"ok": true}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err := v.Get(nil) c.Assert(err, ErrorMatches, "Get : 303 response missing Location header") } func (s *ValueS) TestPost(c *C) { testServer.PrepareResponse(200, jsonType, `{"ok": true}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) other, err := v.Post(nil) c.Assert(err, IsNil) c.Assert(v.Map(), DeepEquals, map[string]interface{}{}) c.Assert(other.Map()["ok"], Equals, true) c.Assert(other.AbsLoc(), Equals, v.AbsLoc()) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/myvalue") } func (s *ValueS) TestPostWithParams(c *C) { testServer.PrepareResponse(200, jsonType, `{"ok": true, "self_link": "/self"}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) other, err := v.Post(lpad.Params{"k": "v"}) c.Assert(err, IsNil) c.Assert(other.AbsLoc(), Equals, "/self") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/myvalue") c.Assert(req.Form["k"], DeepEquals, []string{"v"}) } func (s *ValueS) TestPostWithSelfLinkOnOriginal(c *C) { m := M{"self_link": testServer.URL+"/self"} testServer.PrepareResponse(200, jsonType, `{"ok": true}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", m) other, err := v.Post(lpad.Params{"k": "v"}) c.Assert(err, IsNil) c.Assert(other.AbsLoc(), Equals, testServer.URL+"/self") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/self") c.Assert(req.Form["k"], DeepEquals, []string{"v"}) } func (s *ValueS) TestPostCreation(c *C) { headers := map[string]string{ "Location": testServer.URL + "/newvalue", "Content-Type": "application/json", // Should be ignored. } testServer.PrepareResponse(201, headers, `{"ok": false}`) testServer.PrepareResponse(200, jsonType, `{"ok": true}`) v := lpad.NewValue(nil, testServer.URL, testServer.URL+"/myvalue", nil) other, err := v.Post(nil) c.Assert(err, IsNil) c.Assert(len(v.Map()), Equals, 0) c.Assert(other.BaseLoc(), Equals, testServer.URL) c.Assert(other.AbsLoc(), Equals, testServer.URL+"/newvalue") c.Assert(other.Map()["ok"], Equals, true) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/myvalue") req = testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/newvalue") c.Assert(len(req.Form), Equals, 0) } func (s *ValueS) TestPostSign(c *C) { oauth := &lpad.OAuth{Token: "mytoken", TokenSecret: "mytokensecret"} session := lpad.NewSession(oauth) testServer.PrepareResponse(200, jsonType, `{"ok": true}`) v := lpad.NewValue(session, "", testServer.URL+"/myvalue", nil) other, err := v.Post(nil) c.Assert(err, IsNil) c.Assert(len(v.Map()), Equals, 0) c.Assert(other.Map()["ok"], Equals, true) c.Assert(other.AbsLoc(), Equals, v.AbsLoc()) c.Assert(other.Session(), Equals, v.Session()) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/myvalue") c.Assert(req.Header["Authorization"], NotNil) c.Assert(req.Header["Authorization"][0], Matches, "OAuth.*") } func (s *ValueS) TestPatch(c *C) { testServer.PrepareResponse(200, jsonType, `{"a": 1, "b": 2}`) testServer.PrepareResponse(200, nil, "") v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err := v.Get(nil) c.Assert(err, IsNil) v.SetField("a", 3) v.SetField("c", "string") v.SetField("d", true) v.SetField("e", []string{"a", "b"}) c.Assert(v.Map()["a"], Equals, 3.0) c.Assert(v.Map()["b"], Equals, 2.0) c.Assert(v.Map()["c"], Equals, "string") c.Assert(v.Map()["d"], Equals, true) c.Assert(v.Map()["e"], DeepEquals, []interface{}{"a", "b"}) err = v.Patch() c.Assert(err, IsNil) req1 := testServer.WaitRequest() c.Assert(req1.Method, Equals, "GET") c.Assert(req1.URL.Path, Equals, "/myvalue") req2 := testServer.WaitRequest() c.Assert(req2.Method, Equals, "PATCH") c.Assert(req2.URL.Path, Equals, "/myvalue") c.Assert(req2.Header.Get("Accept"), Equals, "application/json") c.Assert(req2.Header.Get("Content-Type"), Equals, "application/json") var m M err = json.Unmarshal([]byte(body(req2)), &m) c.Assert(err, IsNil) c.Assert(m, DeepEquals, M{"a": 3.0, "c": "string", "d": true, "e": []interface{}{"a", "b"}}) } func (s *ValueS) TestPatchWithContent(c *C) { testServer.PrepareResponse(200, jsonType, `{"a": 1, "b": 2}`) testServer.PrepareResponse(209, jsonType, `{"new": "content"}`) v := lpad.NewValue(nil, "", testServer.URL+"/myvalue", nil) _, err := v.Get(nil) c.Assert(err, IsNil) v.SetField("a", 3) err = v.Patch() c.Assert(err, IsNil) c.Assert(v.Map(), DeepEquals, map[string]interface{}{"new": "content"}) req1 := testServer.WaitRequest() c.Assert(req1.Method, Equals, "GET") c.Assert(req1.URL.Path, Equals, "/myvalue") req2 := testServer.WaitRequest() c.Assert(req2.Method, Equals, "PATCH") c.Assert(req2.URL.Path, Equals, "/myvalue") c.Assert(req2.Header.Get("Accept"), Equals, "application/json") c.Assert(req2.Header.Get("Content-Type"), Equals, "application/json") var m M err = json.Unmarshal([]byte(body(req2)), &m) c.Assert(err, IsNil) c.Assert(m, DeepEquals, M{"a": 3.0}) } type locationTest struct { BaseLoc, Loc, RelLoc, AbsLoc string } var locationTests = []locationTest{ {"http://e.c/base/", "http://e.c/base/more/foo", "bar", "http://e.c/base/more/foo/bar"}, {"http://e.c/base/", "http://e.c/base/more/foo", "../bar", "http://e.c/base/more/bar"}, {"http://e.c/base/", "http://e.c/base/more/foo", "/bar", "http://e.c/base/bar"}, {"http://e.c/base", "http://e.c/base/more/foo", "/bar", "http://e.c/base/bar"}, } func (s *ValueS) TestLocation(c *C) { oauth := &lpad.OAuth{Token: "mytoken", TokenSecret: "mytokensecret"} session := lpad.NewSession(oauth) for _, test := range locationTests { r1 := lpad.NewValue(session, test.BaseLoc, test.Loc, nil) r2 := r1.Location(test.RelLoc) c.Assert(r2.AbsLoc(), Equals, test.AbsLoc) c.Assert(r2.BaseLoc(), Equals, test.BaseLoc) c.Assert(r2.Session(), Equals, session) } } func (s *ValueS) TestLink(c *C) { oauth := &lpad.OAuth{Token: "mytoken", TokenSecret: "mytokensecret"} session := lpad.NewSession(oauth) for _, test := range locationTests { m := map[string]interface{}{"some_link": test.AbsLoc} v1 := lpad.NewValue(session, test.BaseLoc, test.Loc, m) v2 := v1.Link("some_link") c.Assert(v2, NotNil) c.Assert(v2.AbsLoc(), Equals, test.AbsLoc) c.Assert(v2.BaseLoc(), Equals, test.BaseLoc) c.Assert(v2.Session(), Equals, session) v3 := v1.Link("bad_link") c.Assert(v3, IsNil) } } func (s *ValueS) TestNilValueHandlign(c *C) { // This is meaningful so Link can return a single // value and be used as Link("link").Get(nil), etc. nv := (*lpad.Value)(nil) v, err := nv.Get(nil) c.Assert(v, IsNil) c.Assert(err, Equals, lpad.ErrNotFound) v, err = nv.Post(nil) c.Assert(v, IsNil) c.Assert(err, Equals, lpad.ErrNotFound) err = nv.Patch() c.Assert(err, Equals, lpad.ErrNotFound) } func (s *ValueS) TestCollection(c *C) { data0 := `{ "total_size": 5, "start": 1, "next_collection_link": "%s", "entries": [{"self_link": "http://self1"}, {"self_link": "http://self2"}] }` data1 := `{ "total_size": 5, "start": 3, "entries": [{"self_link": "http://self3"}, {"self_link": "http://self4"}] }` testServer.PrepareResponse(200, jsonType, fmt.Sprintf(data0, testServer.URL+"/next?n=10")) testServer.PrepareResponse(200, jsonType, data1) v := lpad.NewValue(nil, "", testServer.URL+"/mycol", nil) _, err := v.Get(nil) c.Assert(err, IsNil) c.Assert(v.TotalSize(), Equals, 5) c.Assert(v.StartIndex(), Equals, 1) i := 1 err = v.For(func(v *lpad.Value) error { c.Assert(v.Map()["self_link"], Equals, "http://self"+strconv.Itoa(i)) i++ return nil }) c.Assert(err, IsNil) c.Assert(i, Equals, 5) testServer.WaitRequest() req1 := testServer.WaitRequest() c.Assert(req1.Form["n"], DeepEquals, []string{"10"}) } func (s *ValueS) TestCollectionGetError(c *C) { data := `{ "total_size": 2, "start": 0, "next_collection_link": "%s", "entries": [{"self_link": "http://self1"}] }` testServer.PrepareResponse(200, jsonType, fmt.Sprintf(data, testServer.URL+"/next")) testServer.PrepareResponse(500, jsonType, "") v := lpad.NewValue(nil, "", testServer.URL+"/mycol", nil) _, err := v.Get(nil) c.Assert(err, IsNil) i := 0 err = v.For(func(v *lpad.Value) error { i++ return nil }) c.Assert(err, ErrorMatches, ".* returned 500 .*") c.Assert(i, Equals, 1) } func (s *ValueS) TestCollectionNoEntries(c *C) { data := `{"total_size": 2, "start": 0}` testServer.PrepareResponse(200, jsonType, data) v := lpad.NewValue(nil, "", testServer.URL+"/mycol", nil) _, err := v.Get(nil) c.Assert(err, IsNil) i := 0 err = v.For(func(v *lpad.Value) error { i++ return nil }) c.Assert(err, ErrorMatches, "No entries found in value") c.Assert(i, Equals, 0) } func (s *ValueS) TestCollectionIterError(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [{"self_link": "http://self1"}, {"self_link": "http://self2"}] }` testServer.PrepareResponse(200, jsonType, data) v := lpad.NewValue(nil, "", testServer.URL+"/mycol", nil) _, err := v.Get(nil) c.Assert(err, IsNil) i := 0 err = v.For(func(v *lpad.Value) error { i++ return errors.New("Stop!") }) c.Assert(err, ErrorMatches, "Stop!") c.Assert(i, Equals, 1) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/Makefile����������������������������������������������������0000644�0000153�0000161�00000001203�12321735716�021340� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������include $(GOROOT)/src/Make.inc TARG=launchpad.net/lpad GOFILES=\ archive.go\ blueprint.go\ build.go\ builder.go\ branch.go\ bug.go\ oauth.go\ person.go\ project.go\ distro.go\ source.go\ session.go\ value.go\ include $(GOROOT)/src/Make.pkg runexample: _obj/launchpad.net/lpad.a $(GC) -I _obj -o example.$(O) example.go $(LD) -L _obj -o example example.$(O) ./example GOFMT=gofmt BADFMT=$(shell $(GOFMT) -l $(GOFILES) $(wildcard *_test.go)) gofmt: $(BADFMT) @for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done ifneq ($(BADFMT),) ifneq ($(MAKECMDGOALS),gofmt) #$(warning WARNING: make gofmt: $(BADFMT)) endif endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/bug_test.go�������������������������������������������������0000644�0000153�0000161�00000014622�12321735716�022054� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" ) func (s *ModelS) TestBug(c *C) { m := M{ "id": 123456.0, "title": "Title", "description": "Description", "tags": []interface{}{"a", "b", "c"}, "private": true, "security_related": true, "web_link": "http://page", } bug := &lpad.Bug{lpad.NewValue(nil, "", "", m)} c.Assert(bug.Id(), Equals, 123456) c.Assert(bug.Title(), Equals, "Title") c.Assert(bug.Description(), Equals, "Description") c.Assert(bug.Tags(), DeepEquals, []string{"a", "b", "c"}) c.Assert(bug.Private(), Equals, true) c.Assert(bug.SecurityRelated(), Equals, true) c.Assert(bug.WebPage(), Equals, "http://page") bug.SetTitle("New title") bug.SetDescription("New description") bug.SetTags([]string{"new", "tags"}) bug.SetPrivate(false) bug.SetSecurityRelated(false) c.Assert(bug.Title(), Equals, "New title") c.Assert(bug.Description(), Equals, "New description") c.Assert(bug.Tags(), DeepEquals, []string{"new", "tags"}) c.Assert(bug.Private(), Equals, false) c.Assert(bug.SecurityRelated(), Equals, false) } func (s *ModelS) TestBugTask(c *C) { m := M{ "assignee_link": testServer.URL + "/assignee_link", "milestone_link": testServer.URL + "/milestone_link", "status": "New", "importance": "High", } task := &lpad.BugTask{lpad.NewValue(nil, "", "", m)} c.Assert(task.Status(), Equals, lpad.StNew) c.Assert(task.Importance(), Equals, lpad.ImHigh) task.SetStatus(lpad.StInProgress) task.SetImportance(lpad.ImCritical) c.Assert(task.Status(), Equals, lpad.StInProgress) c.Assert(task.Importance(), Equals, lpad.ImCritical) testServer.PrepareResponse(200, jsonType, `{"display_name": "Joe"}`) testServer.PrepareResponse(200, jsonType, `{"name": "mymiles"}`) assignee, err := task.Assignee() c.Assert(err, IsNil) c.Assert(assignee.DisplayName(), Equals, "Joe") milestone, err := task.Milestone() c.Assert(err, IsNil) c.Assert(milestone.Name(), Equals, "mymiles") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/assignee_link") req = testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/milestone_link") milestone = &lpad.Milestone{lpad.NewValue(nil, "", "/new_milestone_link", nil)} assignee = &lpad.Person{lpad.NewValue(nil, "", "/new_assignee_link", nil)} task.SetMilestone(milestone) task.SetAssignee(assignee) c.Assert(task.StringField("milestone_link"), Equals, "/new_milestone_link") c.Assert(task.StringField("assignee_link"), Equals, "/new_assignee_link") } func (s *ModelS) TestRootBug(c *C) { data := `{ "id": 123456, "title": "Title", "description": "Description", "private": true, "security_related": true, "tags": "a b c" }` testServer.PrepareResponse(200, jsonType, data) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} bug, err := root.Bug(123456) c.Assert(err, IsNil) c.Assert(bug.Title(), Equals, "Title") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/bugs/123456") } func (s *ModelS) TestRootCreateBug(c *C) { data := `{ "id": 123456, "title": "Title", "description": "Description", "private": true, "security_related": true, "tags": "a b c" }` testServer.PrepareResponse(200, jsonType, data) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} stub := &lpad.BugStub{ Title: "Title", Description: "Description.", Private: true, SecurityRelated: true, Tags: []string{"a", "b", "c"}, Target: lpad.NewValue(nil, "", "http://target", nil), } bug, err := root.CreateBug(stub) c.Assert(err, IsNil) c.Assert(bug.Title(), Equals, "Title") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/bugs") c.Assert(req.Form["ws.op"], DeepEquals, []string{"createBug"}) c.Assert(req.Form["title"], DeepEquals, []string{"Title"}) c.Assert(req.Form["description"], DeepEquals, []string{"Description."}) c.Assert(req.Form["private"], DeepEquals, []string{"true"}) c.Assert(req.Form["security_related"], DeepEquals, []string{"true"}) c.Assert(req.Form["tags"], DeepEquals, []string{"a b c"}) c.Assert(req.Form["target"], DeepEquals, []string{"http://target"}) } func (s *ModelS) TestRootCreateBugNoTags(c *C) { // Launchpad blows up if an empty tags value is provided. :-( data := `{ "id": 123456, "title": "Title" }` testServer.PrepareResponse(200, jsonType, data) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} stub := &lpad.BugStub{ Title: "Title", Description: "Description.", Target: lpad.NewValue(nil, "", "http://target", nil), } bug, err := root.CreateBug(stub) c.Assert(err, IsNil) c.Assert(bug.Title(), Equals, "Title") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/bugs") c.Assert(req.Form["ws.op"], DeepEquals, []string{"createBug"}) _, ok := req.Form["tags"] c.Assert(ok, Equals, false) } func (s *ModelS) TestBugLinkBranch(c *C) { testServer.PrepareResponse(200, jsonType, `{}`) bug := &lpad.Bug{lpad.NewValue(nil, "", testServer.URL+"/bugs/123456", nil)} branch := &lpad.Branch{lpad.NewValue(nil, testServer.URL, testServer.URL+"~joe/ensemble/some-branch", nil)} err := bug.LinkBranch(branch) c.Assert(err, IsNil) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "POST") c.Assert(req.URL.Path, Equals, "/bugs/123456") c.Assert(req.Form["ws.op"], DeepEquals, []string{"linkBranch"}) c.Assert(req.Form["branch"], DeepEquals, []string{branch.AbsLoc()}) } func (s *ModelS) TestBugTasks(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [{ "self_link": "http://self0", "status": "New" }, { "self_link": "http://self1", "status": "Unknown" }] }` testServer.PrepareResponse(200, jsonType, data) m := M{"bug_tasks_collection_link": testServer.URL + "/col_link"} bug := &lpad.Bug{lpad.NewValue(nil, testServer.URL, "", m)} list, err := bug.Tasks() c.Assert(err, IsNil) c.Assert(list.TotalSize(), Equals, 2) status := []lpad.BugStatus{} list.For(func(task *lpad.BugTask) error { status = append(status, task.Status()) return nil }) c.Assert(status, DeepEquals, []lpad.BugStatus{lpad.StNew, lpad.StUnknown}) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/col_link") } ��������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/person.go���������������������������������������������������0000644�0000153�0000161�00000016645�12321735716�021555� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad import ( "net/url" ) // The Root type provides the entrance for the Launchpad API. type Root struct { *Value } // Me returns the Person authenticated into Lauchpad in the current session. func (root *Root) Me() (*Person, error) { me, err := root.Location("/people/+me").Get(nil) if err != nil { return nil, err } return &Person{me}, nil } // Member returns the Team or Person with the provided name or username. func (root *Root) Member(name string) (Member, error) { v, err := root.Location("/~" + url.QueryEscape(name)).Get(nil) if err != nil { return nil, err } if v.BoolField("is_team") { return &Team{v}, nil } return &Person{v}, nil } // FindPeople returns a PersonList containing all Person accounts whose // Name, DisplayName or email address match text. func (root *Root) FindPeople(text string) (*PersonList, error) { v, err := root.Location("/people").Get(Params{"ws.op": "findPerson", "text": text}) if err != nil { return nil, err } return &PersonList{v}, nil } // FindTeams returns a TeamList containing all Team accounts whose // Name, DisplayName or email address match text. func (root *Root) FindTeams(text string) (*TeamList, error) { v, err := root.Location("/people").Get(Params{"ws.op": "findTeam", "text": text}) if err != nil { return nil, err } return &TeamList{v}, nil } // FindMembers returns a MemberList containing all Person or Team accounts // whose Name, DisplayName or email address match text. func (root *Root) FindMembers(text string) (*MemberList, error) { v, err := root.Location("/people").Get(Params{"ws.op": "find", "text": text}) if err != nil { return nil, err } return &MemberList{v}, nil } // The MemberList type encapsulates a mixed list containing Person and Team // elements for iteration. type MemberList struct { *Value } // For iterates over the list of people and teams and calls f for each one. // If f returns a non-nil error, iteration will stop and the error will be // returned as the result of For. func (list *MemberList) For(f func(v Member) error) error { return list.Value.For(func(v *Value) error { if v.BoolField("is_team") { return f(&Team{v}) } return f(&Person{v}) }) } // The PersonList type encapsulates a list of Person elements for iteration. type PersonList struct { *Value } // For iterates over the list of people and calls f for each one. If f // returns a non-nil error, iteration will stop and the error will be // returned as the result of For. func (list *PersonList) For(f func(p *Person) error) error { return list.Value.For(func(v *Value) error { return f(&Person{v}) }) } // The TeamList type encapsulates a list of Team elements for iteration. type TeamList struct { *Value } // For iterates over the list of teams and calls f for each one. If f // returns a non-nil error, iteration will stop and the error will be // returned as the result of For. func (list *TeamList) For(f func(t *Team) error) error { return list.Value.For(func(v *Value) error { return f(&Team{v}) }) } // Member is an interface implemented by both Person and Team. type Member interface { AnyValue DisplayName() string SetDisplayName(name string) Name() string SetName(name string) WebPage() string // Member is a marker function for types satisfying the // the Member interface. This is necessary for now because // the methods above are fairly common across several types, // but this will likely be dropped in the future. Member() } // The Person type represents a person in Launchpad. type Person struct { *Value } var _ Member = (*Person)(nil) // Member is a marker method so Person satisfies the Member interface. func (person *Person) Member() {} // DisplayName returns the person's name as it would be displayed // throughout Launchpad. Most people use their full name. func (person *Person) DisplayName() string { return person.StringField("display_name") } // SetDisplayName changes the person's name as it would be displayed // throughout Launchpad. Most people use their full name. // Patch must be called to commit all changes. func (person *Person) SetDisplayName(name string) { person.SetField("display_name", name) } // Name returns the person's short unique name, beginning with a // lower-case letter or number, and containing only letters, numbers, // dots, hyphens, or plus signs. func (person *Person) Name() string { return person.StringField("name") } // SetName changes the person's short unique name. // The name must begin with a lower-case letter or number, and // contain only letters, numbers, dots, hyphens, or plus signs. func (person *Person) SetName(name string) { person.SetField("name", name) } // WebPage returns the URL for accessing this person's page in a browser. func (person *Person) WebPage() string { return person.StringField("web_link") } // PreferredEmail returns the Person's preferred email. If the user // disabled public access to email addresses, this method returns an // *Error with StatusCode of 404. func (person *Person) PreferredEmail() (string, error) { // WTF.. seriously!? e, err := person.Link("preferred_email_address_link").Get(nil) if err != nil { return "", err } return e.StringField("email"), nil } // IRCNicks returns a list of all IRC nicks for the person. func (person *Person) IRCNicks() (nicks []*IRCNick, err error) { list, err := person.Link("irc_nicknames_collection_link").Get(nil) if err != nil { return nil, err } list.For(func(v *Value) error { nicks = append(nicks, &IRCNick{v}) return nil }) return } type IRCNick struct { *Value } // Nick returns the person's nick on an IRC network. func (nick *IRCNick) Nick() string { return nick.StringField("nickname") } // SetNick changes the person's nick on an IRC network. // Patch must be called to commit all changes. func (nick *IRCNick) SetNick(n string) { nick.SetField("nickname", n) } // Network returns the IRC network this nick is associated to. func (nick *IRCNick) Network() string { return nick.StringField("network") } // SetNetwork changes the IRC network this nick is associated to. // Patch must be called to commit all changes. func (nick *IRCNick) SetNetwork(n string) { nick.SetField("network", n) } // The Team type encapsulates access to details about a team in Launchpad. type Team struct { *Value } var _ Member = (*Team)(nil) // Member is a marker method so Team satisfies the Member interface. func (team *Team) Member() {} // Name returns the team's name. This is a short unique name, beginning with a // lower-case letter or number, and containing only letters, numbers, dots, // hyphens, or plus signs. func (team *Team) Name() string { return team.StringField("name") } // SetName changes the team's name. This is a short unique name, beginning // with a lower-case letter or number, and containing only letters, numbers, // dots, hyphens, or plus signs. Patch must be called to commit all changes. func (team *Team) SetName(name string) { team.SetField("name", name) } // DisplayName returns the team's name as it would be displayed // throughout Launchpad. func (team *Team) DisplayName() string { return team.StringField("display_name") } // SetDisplayName changes the team's name as it would be displayed // throughout Launchpad. Patch must be called to commit all changes. func (team *Team) SetDisplayName(name string) { team.SetField("display_name", name) } // WebPage returns the URL for accessing this team's page in a browser. func (team *Team) WebPage() string { return team.StringField("web_link") } �������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/branch.go���������������������������������������������������0000644�0000153�0000161�00000020126�12321736016�021463� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad import ( "errors" "strings" ) var weirdPrefixes = []string{ // Launchpad failed to handle this one in getByUrl. "bzr+ssh://bazaar.launchpad.net/+branch/", } // Branch returns a branch for the provided URL. The URL can be in // the short form lp: notation, or the web address rooted at // http://bazaar.launchpad.net/ func (root *Root) Branch(burl string) (*Branch, error) { // getByUrl doesn't like escaped URLs. burl = strings.Replace(burl, "%2B", "+", -1) for _, prefix := range weirdPrefixes { if strings.HasPrefix(burl, prefix) { burl = "lp:" + burl[len(prefix):] break } } v, err := root.Location("/branches").Get(Params{"ws.op": "getByUrl", "url": burl}) if err != nil { return nil, err } return &Branch{v}, nil } // The Branch type represents a project in Launchpad. type Branch struct { *Value } // Id returns the shortest version for the branch name. If the branch // is the development focus for a project, a lp:project form will be // returned. If it's the development focus for a series, then a // lp:project/series is returned. Otherwise, the unique name for the // branch in the form lp:~user/project/branch-name is returned. func (b *Branch) Id() string { return b.StringField("bzr_identity") } // UniqueName returns the unique branch name, in the // form lp:~user/project/branch-name. func (b *Branch) UniqueName() string { return b.StringField("unique_name") } // Owner returns the Person that owns this branch. func (b *Branch) Owner() (*Person, error) { p, err := b.Link("owner_link").Get(nil) if err != nil { return nil, err } return &Person{p}, nil } // OwnerName returns the name from the owner of this branch. func (b *Branch) OwnerName() string { un := b.UniqueName() if len(un) > 0 && un[0] == '~' { for i := 0; i < len(un); i++ { if un[i] == '/' { return un[1:i] } } } panic("can't find owner name in unique_name: " + un) } // ProjectName returns the name for the project this branch is part of. func (b *Branch) ProjectName() string { un := b.UniqueName() if len(un) > 0 && un[0] == '~' { var i, j int for i = 0; i < len(un); i++ { if un[i] == '/' { break } } i++ if i < len(un) { for j = i; j < len(un); j++ { if un[j] == '/' { break } } return un[i:j] } } panic("can't find project name in unique_name: " + un) } // WebPage returns the URL for accessing this branch in a browser. func (b *Branch) WebPage() string { return b.StringField("web_link") } // LandingCandidates returns a list of all the merge proposals that // have this branch as the target of the proposed change. func (b *Branch) LandingCandidates() (*MergeProposalList, error) { v, err := b.Link("landing_candidates_collection_link").Get(nil) if err != nil { return nil, err } return &MergeProposalList{v}, nil } // LandingTargets returns a list of all the merge proposals that // have this branch as the source of the proposed change. func (b *Branch) LandingTargets() (*MergeProposalList, error) { v, err := b.Link("landing_targets_collection_link").Get(nil) if err != nil { return nil, err } return &MergeProposalList{v}, nil } type MergeStub struct { Description string CommitMessage string NeedsReview bool Target *Branch PreReq *Branch } // ProposeMerge proposes this branch for merging on another branch by // creating the respective merge proposal. func (b *Branch) ProposeMerge(stub *MergeStub) (mp *MergeProposal, err error) { if stub.Target == nil { return nil, errors.New("Missing target branch") } params := Params{ "ws.op": "createMergeProposal", "target_branch": stub.Target.AbsLoc(), } if stub.Description != "" { params["initial_comment"] = stub.Description } if stub.CommitMessage != "" { params["commit_message"] = stub.CommitMessage } if stub.NeedsReview { params["needs_review"] = "true" } else { params["needs_review"] = "false" } if stub.PreReq != nil { params["prerequisite_branch"] = stub.PreReq.AbsLoc() } v, err := b.Post(params) if err != nil { return nil, err } return &MergeProposal{v}, nil } type MergeProposal struct { *Value } // Description returns the detailed description of the changes being // proposed in the source branch of the merge proposal. func (mp *MergeProposal) Description() string { return mp.StringField("description") } // SetDescription changes the detailed description of the changes being // proposed in the source branch of the merge proposal. func (mp *MergeProposal) SetDescription(description string) { mp.SetField("description", description) } type MergeProposalStatus string const ( StWorkInProgress MergeProposalStatus = "Work in progress" StNeedsReview MergeProposalStatus = "Needs review" StApproved MergeProposalStatus = "Approved" StRejected MergeProposalStatus = "Rejected" StMerged MergeProposalStatus = "Merged" StFailedToMerge MergeProposalStatus = "Code failed to merge" StQueued MergeProposalStatus = "Queued" StSuperseded MergeProposalStatus = "Superseded" ) // Status returns the current status of the merge proposal. // E.g. Needs Review, Work In Progress, etc. func (mp *MergeProposal) Status() MergeProposalStatus { return MergeProposalStatus(mp.StringField("queue_status")) } // SetStatus changes the current status of the merge proposal. func (mp *MergeProposal) SetStatus(status MergeProposalStatus) error { _, err := mp.Post(Params{"ws.op": "setStatus", "status": string(status)}) return err } // CommitMessage returns the commit message to be used when merging // the source branch onto the target branch. func (mp *MergeProposal) CommitMessage() string { return mp.StringField("commit_message") } // SetCommitMessage changes the commit message to be used when // merging the source branch onto the target branch. func (mp *MergeProposal) SetCommitMessage(msg string) { mp.SetField("commit_message", msg) } // Email returns the unique email that may be used to add new comments // to the merge proposal conversation. func (mp *MergeProposal) Email() string { return mp.StringField("address") } // Source returns the source branch that has additional code to land. func (mp *MergeProposal) Source() (*Branch, error) { v, err := mp.Link("source_branch_link").Get(nil) if err != nil { return nil, err } return &Branch{v}, nil } // Target returns the branch where code will land on once merged. func (mp *MergeProposal) Target() (*Branch, error) { v, err := mp.Link("target_branch_link").Get(nil) if err != nil { return nil, err } return &Branch{v}, nil } // PreReq returns the branch is the base (merged or not) for the code // within the target branch. func (mp *MergeProposal) PreReq() (*Branch, error) { v, err := mp.Link("prerequisite_branch_link").Get(nil) if err != nil { return nil, err } return &Branch{v}, nil } // WebPage returns the URL for accessing this merge proposal // in a browser. func (mp *MergeProposal) WebPage() string { return mp.StringField("web_link") } type ProposalVote string const ( VoteNone ProposalVote = "" VoteApprove ProposalVote = "Approve" VoteNeedsFixing ProposalVote = "Needs Fixing" VoteNeedsInfo ProposalVote = "Needs Information" VoteAbstain ProposalVote = "Abstain" VoteDisapprove ProposalVote = "Disapprove" VoteResubmit ProposalVote = "Resubmit" ) // AddComment adds a new comment to mp. func (mp *MergeProposal) AddComment(subject, message string, vote ProposalVote, reviewType string) error { params := Params{ "ws.op": "createComment", "subject": subject, } if message != "" { params["content"] = message } if vote != VoteNone { params["vote"] = string(vote) } if reviewType != "" { params["review_type"] = reviewType } _, err := mp.Post(params) return err } // The MergeProposalList represents a list of MergeProposal objects. type MergeProposalList struct { *Value } // For iterates over the list of merge proposals and calls f for each one. // If f returns a non-nil error, iteration will stop and the error will be // returned as the result of For. func (list *MergeProposalList) For(f func(t *MergeProposal) error) error { return list.Value.For(func(v *Value) error { return f(&MergeProposal{v}) }) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/builder_test.go���������������������������������������������0000644�0000153�0000161�00000003654�12321735716�022730� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" ) func (s *ModelS) TestBuilder(c *C) { m := M{ "name": "thename", "title": "Title", "active": true, "builderok": true, "virtualized": "false", "vm_host": "foobar", "web_link": "http://page", } builder := &lpad.Builder{lpad.NewValue(nil, "", "", m)} c.Assert(builder.Name(), Equals, "thename") c.Assert(builder.Title(), Equals, "Title") c.Assert(builder.Active(), Equals, true) c.Assert(builder.BuilderOK(), Equals, true) c.Assert(builder.Virtualized(), Equals, false) c.Assert(builder.VMHost(), Equals, "foobar") c.Assert(builder.WebPage(), Equals, "http://page") } func (s *ModelS) TestRootBuildersAndBuilderList(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [ {"name": "builder1", "builderok": true}, {"name": "builder2", "builderok": true} ] }` testServer.PrepareResponse(200, jsonType, data) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} builders, err := root.Builders() c.Assert(err, IsNil) var l []*lpad.Builder builders.For(func(builder *lpad.Builder) error { l = append(l, builder) return nil }) c.Assert(len(l), Equals, 2) c.Assert(l[0].Name(), Equals, "builder1") c.Assert(l[1].Name(), Equals, "builder2") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/builders") } func (s *ModelS) TestRootBuilder(c *C) { testServer.PrepareResponse(200, jsonType, `{"name": "builder1"}`) root := &lpad.Root{lpad.NewValue(nil, testServer.URL, "", nil)} builder, err := root.Builder("builder1") c.Assert(err, IsNil) c.Assert(builder.Name(), Equals, "builder1") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/builders") c.Assert(req.Form["ws.op"], DeepEquals, []string{"getByName"}) c.Assert(req.Form["name"], DeepEquals, []string{"builder1"}) } ������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/value.go����������������������������������������������������0000644�0000153�0000161�00000030173�12321735716�021353� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad import ( "bytes" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "net/http/httputil" "net/url" "os" "path" "strconv" "strings" ) // The Params type is a helper to pass parameter into the Value request // methods. It may be used as: // // value.Get(lpad.Params{"name": "value"}) // type Params map[string]string type Error struct { StatusCode int // HTTP status code (500, 403, ...) Body []byte // Body of response } func (e *Error) Error() string { if len(e.Body) == 0 { return fmt.Sprintf("Server returned %d and no body.", e.StatusCode) } return fmt.Sprintf("Server returned %d and body: %s", e.StatusCode, e.Body) } // The AnyValue interface is implemented by *Value and thus by all the // more specific value types supported. See the Value type for the // meaning of these methods. type AnyValue interface { IsValid() bool Session() *Session BaseLoc() string AbsLoc() string Map() map[string]interface{} StringField(key string) string StringListField(key string) []string IntField(key string) int FloatField(key string) float64 BoolField(key string) bool SetField(key string, value interface{}) Location(loc string) *Value Link(key string) *Value Get(params Params) (*Value, error) Post(params Params) (*Value, error) Patch() error TotalSize() int StartIndex() int For(func(v *Value) error) error } // The Value type is the underlying dynamic layer used as the foundation of // all the more specific value types that support the Launchpad model. // Besides being used internally to implement these types, the methods of // this type also enable accessing new features available in Launchpad which // were not yet made available in lpad thorugh more convenient methods. type Value struct { session *Session baseloc string loc string m map[string]interface{} patch map[string]interface{} } // NewValue creates a new Value with the provided details. Creating values // explicitly is generally not necessary. If you're trying to access a // location in the Launchpad API which is not covered by the supported // types yet, see the Link and Location methods on the Value type for more // convenient ways to create values. func NewValue(session *Session, baseloc, loc string, m map[string]interface{}) *Value { return &Value{session, baseloc, loc, m, nil} } // IsValid returns true if the value is initialized and thus not nil. This // provided mainly as a convenience for all the types that embed a *Value. func (v *Value) IsValid() bool { return v != nil } // Session returns the session for the interaction with Launchpad. // This session is used to sign any requests delivered to Launchpad. func (v *Value) Session() *Session { return v.session } // BaseLoc returns the API-oriented URL base for the session. Absolute // paths provided to Location and Link will be rooted at this place. func (v *Value) BaseLoc() string { return v.baseloc } // AbsLoc returns the API-oriented URL of this value. func (v *Value) AbsLoc() string { if self := v.StringField("self_link"); self != "" { return self } return v.loc } // Map returns the dynamic map with the content of this value. func (v *Value) Map() map[string]interface{} { if v.m == nil { v.m = make(map[string]interface{}) } return v.m } // StringField returns the named value field if it exists and is // set to a string value, or the empty string otherwise. func (v *Value) StringField(key string) string { if v, ok := v.Map()[key].(string); ok { return v } return "" } // StringListField returns the named value field if it exists and is // set to a string list value, or an empty list otherwise. func (v *Value) StringListField(key string) []string { var result []string if items, ok := v.Map()[key].([]interface{}); ok { for _, item := range items { if s, ok := item.(string); ok { result = append(result, s) } } } return result } // IntField returns the named value field if it exists and is // set to an int value, or zero otherwise. func (v *Value) IntField(key string) int { if v, ok := v.Map()[key].(float64); ok { return int(v) } return 0 } // FloatField returns the named value field if it exists and is // set to a float64 value, or zero otherwise. func (v *Value) FloatField(key string) float64 { if v, ok := v.Map()[key].(float64); ok { return v } return 0 } // BoolField returns the named value field if it exists and is // set to a bool value, or false otherwise. func (v *Value) BoolField(key string) bool { if v, ok := v.Map()[key].(bool); ok { return v } return false } // SetField changes the named field with the provided value. func (v *Value) SetField(key string, value interface{}) { if v.patch == nil { v.patch = make(map[string]interface{}) } p := v.patch m := v.Map() var newv interface{} switch v := value.(type) { case int: newv = float64(v) case string: newv = v case bool: newv = v case []string: var l []interface{} for _, item := range v { l = append(l, item) } newv = l default: panic(fmt.Sprintf("Unsupported value type for SetField: %#v", value)) } p[key] = newv m[key] = newv } func (v *Value) join(part string) string { if part == "" { return v.loc } if strings.HasPrefix(part, "http://") || strings.HasPrefix(part, "https://") { return part } base := v.baseloc if !strings.HasPrefix(part, "/") { base = v.loc } u, err := url.Parse(base) if err != nil { panic("Invalid URL: " + base) } u.Path = path.Join(u.Path, part) return u.String() } // Location returns a new value for a location which may be a // full URL, or an absolute path (based on the value's BaseLoc), // or a path relative to the value itself (based on the // value's URL). func (v *Value) Location(loc string) *Value { return &Value{session: v.session, baseloc: v.baseloc, loc: v.join(loc)} } // Link calls Location with a URL available in the given key // of the current value's Map. It returns nil if the requested // key isn't found in the value. This is a convenient way to // navigate through *_link fields in values. func (v *Value) Link(key string) *Value { link, ok := v.m[key].(string) if !ok { return nil } return v.Location(link) } // ErrNotFound is returned when the HTTP API returns 404 or a nil // value is found in a resulting field or a field being operated on. var ErrNotFound = errors.New("resource not found") // Get issues an HTTP GET to retrieve the content of this value, // and returns itself and an error in case of problems. If params // is not nil, it will provided as the query for the GET request. // // Since Get returns the value itself, it may be used as: // // v, err := other.Link("some_link").Get(nil) // func (v *Value) Get(params Params) (same *Value, err error) { return v.do("GET", params, nil) } // Post issues an HTTP POST to perform a given action at the URL // specified by this value. If params is not nil, it will // provided as the parameters for the POST request. func (v *Value) Post(params Params) (other *Value, err error) { return v.do("POST", params, nil) } // Patch issues an HTTP PATCH request to modify the server value // with the local changes. func (v *Value) Patch() error { if v == nil { return ErrNotFound } data, err := json.Marshal(v.patch) if err != nil { return err } _, err = v.do("PATCH", nil, data) return err } // TotalSize returns the total number of entries in a collection. func (v *Value) TotalSize() int { return v.IntField("total_size") } // StartIndex returns the offset of the first value in a collection. func (v *Value) StartIndex() int { return v.IntField("start") } // For iterates over every element in a collection and calls the // provided function for each entry. If the function returns a // non-nil err value, the iteration will stop. Watch out for // very large collections! func (v *Value) For(f func(*Value) error) (err error) { for { entries, ok := v.Map()["entries"].([]interface{}) if !ok { return errors.New("No entries found in value") } for _, entry := range entries { m, ok := entry.(map[string]interface{}) if !ok { continue } link, _ := m["self_link"].(string) err := f(&Value{session: v.session, baseloc: v.baseloc, loc: link, m: m}) if err != nil { return err } } nextv := v.Link("next_collection_link") if nextv == nil { break } v, err = nextv.Get(nil) if err != nil { return err } } return nil } func (v *Value) do(method string, params Params, body []byte) (value *Value, err error) { if v == nil { return nil, ErrNotFound } value = v if method == "POST" { // Must use AbsLoc so it takes self_link into account. value = &Value{baseloc: v.baseloc, loc: v.AbsLoc(), session: v.session} } req, err := http.NewRequest(method, value.AbsLoc(), nil) if err != nil { return nil, err } err = v.prepare(req, params, body) if err != nil { return nil, err } if debugOn { if err := printRequestDump(req); err != nil { return nil, err } } client := http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { value.loc = req.URL.String() v.prepare(req, nil, nil) return nil }, } resp, err := client.Do(req) if err != nil { return nil, err } if debugOn { if err := printResponseDump(resp); err != nil { return nil, err } } body, berr := ioutil.ReadAll(resp.Body) resp.Body.Close() if method == "POST" && resp.StatusCode == 201 { value.loc = resp.Header.Get("Location") if value.loc == "" { return nil, errors.New("Server returned 201 without Location") } return value.do("GET", nil, nil) } if resp.StatusCode != http.StatusOK && resp.StatusCode != 209 { if resp.StatusCode == 404 { return nil, ErrNotFound } return nil, &Error{resp.StatusCode, body} } if method == "PATCH" && resp.StatusCode != 209 { return nil, nil } ctype := resp.Header.Get("Content-Type") if ctype != "application/json" { return nil, errors.New("Non-JSON content-type: " + ctype) } if method == "GET" && len(body) > 0 && body[0] == 'n' && string(body) == "null" { return nil, ErrNotFound } if berr != nil { return nil, berr } value.m = make(map[string]interface{}) if len(body) > 0 && body[0] == '[' { body = append([]byte(`{"value":`), body...) body = append(body, '}') } return value, json.Unmarshal(body, &value.m) } func (v *Value) prepare(req *http.Request, params Params, body []byte) error { req.Header["Accept"] = []string{"application/json"} query := multimap(params).Encode() ctype := "application/json" if req.Method == "POST" { body = []byte(query) query = "" ctype = "application/x-www-form-urlencoded" } else if req.URL.RawQuery == "" { req.URL.RawQuery = query } else if query != "" { req.URL.RawQuery += "&" + query } if body != nil { req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) req.Header["Content-Type"] = []string{ctype} req.Header["Content-Length"] = []string{strconv.Itoa(len(body))} req.ContentLength = int64(len(body)) } if v.session != nil { return v.session.Sign(req) } return nil } func multimap(params map[string]string) url.Values { m := make(url.Values, len(params)) for k, v := range params { m[k] = []string{v} } return m } var debugOn bool // SetDebug enables debugging. With debug on requests and responses will all be // dumped into the standard error output. func SetDebug(debug bool) { debugOn = debug } func printRequestDump(req *http.Request) error { if req.Body == nil { req.Body = ioutil.NopCloser(bytes.NewBuffer(nil)) } data, err := ioutil.ReadAll(req.Body) if err != nil { return err } req.Body.Close() req.Body = ioutil.NopCloser(bytes.NewBuffer(data)) dump, err := httputil.DumpRequest(req, true) req.Body = ioutil.NopCloser(bytes.NewBuffer(data)) if err != nil { dump = []byte(err.Error()) } fmt.Fprintf(os.Stderr, "===== DEBUG =====\n%s\n=================\n", dump) return nil } func printResponseDump(resp *http.Response) error { data, err := ioutil.ReadAll(resp.Body) if err != nil { return err } resp.Body.Close() resp.Body = ioutil.NopCloser(bytes.NewBuffer(data)) dump, err := httputil.DumpResponse(resp, true) resp.Body = ioutil.NopCloser(bytes.NewBuffer(data)) if err != nil { dump = []byte(err.Error()) } fmt.Fprintf(os.Stderr, "===== DEBUG =====\n%s\n=================\n", dump) return nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/lpad/archive_test.go���������������������������������������������0000644�0000153�0000161�00000003413�12321735716�022714� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package lpad_test import ( . "launchpad.net/gocheck" "launchpad.net/lpad" ) func (s *ModelS) TestArchive(c *C) { m := M{ "name": "thename", "displayname": "The Name", "description": "The Description", "web_link": "http://page", "distribution_link": testServer.URL + "/distribution_link", } archive := &lpad.Archive{lpad.NewValue(nil, "", "", m)} c.Assert(archive.Name(), Equals, "thename") c.Assert(archive.DisplayName(), Equals, "The Name") c.Assert(archive.Description(), Equals, "The Description") c.Assert(archive.WebPage(), Equals, "http://page") testServer.PrepareResponse(200, jsonType, `{"name": "distroname"}`) distro, err := archive.Distro() c.Assert(err, IsNil) c.Assert(distro.Name(), Equals, "distroname") req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/distribution_link") } func (s *ModelS) TestArchivePublication(c *C) { data := `{ "total_size": 2, "start": 0, "entries": [ {"source_package_name": "whatever", "source_package_version": "1.0" }, {"source_package_name": "whatever", "source_package_version": "1.1" } ] }` testServer.PrepareResponse(200, jsonType, data) archive := &lpad.Archive{lpad.NewValue(nil, testServer.URL, testServer.URL+"/archive", nil)} phlist, err := archive.Publication("whatever", "Published") c.Assert(err, IsNil) phlist.For(func(ph *lpad.Publication) error { c.Assert(ph.PackageName(), Equals, "whatever") return nil }) req := testServer.WaitRequest() c.Assert(req.Method, Equals, "GET") c.Assert(req.URL.Path, Equals, "/archive") c.Assert(req.Form["ws.op"], DeepEquals, []string{"getPublishedSources"}) c.Assert(req.Form["source_name"], DeepEquals, []string{"whatever"}) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/�������������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�020652� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/���������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�021417� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/commands.txt���������������������������������������0000644�0000153�0000161�00000004171�12321735642�023777� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Commands and Sub-commands ========================= The base `Command` interface is found in `cmd/cmd.go`. Commands need to provide an `Info` method that returns an Info struct. The info struct contains: name, args, purpose and a detailed description. This information is used to provide the default help for the command. In the same package, there is `CommandBase` whose purpose is to be composed into new commands, and provides a default no-op SetFlags implementation, a default Init method that checks for no extra args, and a default Help method. Supercommands ============= `Supercommand`s are commands that do many things, and have "sub-commands" that provide this functionality. Git and Bazaar are common examples of "supercommands". Subcommands must also provide the `Command` interface, and are registered using the `Register` method. The name and aliases are registered with the supercommand. If there is a duplicate name registered, the whole thing panics. Supercommands need to be created with the `NewSuperCommand` function in order to provide a fully constructed object. The 'help' subcommand --------------------- All supercommand instances get a help command. This provides the basic help functionality to get all the registered commands, with the addition of also being able to provide non-command help topics which can be added. Help topics have a `name` which is what is matched from the command line, a `short` one line description that is shown when `<cmd> help topics` is called, and a `long` text that is output when the topic is requested. Topics are added using the `AddHelpTopic` method. Execution ========= The `Main` method in the cmd package handles the execution of a command. A new `gnuflag.FlagSet` is created and passed to the command in `SetFlags`. This is for the command to register the flags that it knows how to handle. The args are then parsed, and passed through to the `Init` method for the command to decide what to do with the positional arguments. The command is then `Run` and passed in an execution `Context` that defines the standard input and output streams, and has the current working directory. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/entity-creation.txt��������������������������������0000644�0000153�0000161�00000004134�12321735642�025313� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Entity Creation =============== This document describes the circumstances in which fundamental state entities are created, from the perspective of the CLI. juju deploy ----------- The `juju deploy` command always creates services, may create relations, and may also create units and machines. * New services can always be added. * If the created service's charm defines any peer relations, a (runtime) peer relation will be created for each. BUG: this is not done in the same transaction as service creation; a connection failure at the wrong time will create a broken and unfixable service (because peer relations cannot be manipulated via the CLI). * If the created service's charm is not subordinate, some number of units will be created; this number is controlled via the "--num-units" parameter which defaults to 1. * If units were created, machines may also be created, as below. juju add-unit ------------- The `juju add-unit` command applies only to principal services. It always creates units, and may create machines. Different providers assign units to machines in different ways, and so machine creation can vary: for example, the ec2 provider creates a new machine for each unit that cannot be placed on an existing machine without assigned units; but the local provider (will) only ever deal with a single machine, and will deploy every unit onto the host system. * New units can only be added to Alive services. juju add-relation ----------------- The `juju add-relation` command creates relations, and may -- if the relation has container scope, by virtue of one or more endpoints having container scope -- indirectly cause the creation of subordinate units. Subordinate units are in fact created by principal unit agents, at the point when they enter scope of a container-scoped relation and find that no suitable subordinate already exists. * New relations can only be added between Alive services. * New subordinate units will only be added as a consequence of an Alive principal unit's participation in an Alive relation (implying an Alive subordinate service). ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/bazaar-usage.txt�����������������������������������0000644�0000153�0000161�00000007422�12321735642�024542� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Bazaar Basics ============= An alternative to using `cobzr` is to use the normal `bzr` with light-weight checkouts (see `bzr help checkouts`). The first step is to create a repository that contains the juju-core trunk and other working branches. The Repository ============== See `bzr help repositories` for more info on repositories. For this example, we'll use ~/src as a location for the repository. $ cd ~/src $ bzr init-repo juju-core This will create a repository that has working trees (the actual files and directories - see `bzr help working-trees`. Now put trunk in there: $ cd juju-core $ bzr branch lp:juju-core trunk Working in $GOPATH ================== Now that we have trunk of juju-core elsewhere, we now need to use it inside $GOPATH. These steps assume that you have juju-core already available in $GOPATH/src. $ cd $GOPATH/src/launchpad.net/juju-core $ bzr reconfigure --lightweight-checkout --bind-to ~/src/juju-core/trunk Now when you look at that branch, you should see the following $ bzr info Lightweight checkout (format: 2a) Location: light checkout root: . checkout of branch: /home/<you>/src/juju-core/trunk shared repository: /home/<you>/src/juju-core Making pushes easier ==================== You can specify information in the bazaar locations file which it uses to determine the locations of the public and push locations for a branch. Inside your ~/.bazaar/locations.conf file, add the following (not including the curly braces). {{{ [/home/eric/src] public_branch = bzr+ssh://bazaar.launchpad.net/~eric-the-viking public_branch:policy = appendpath push_location = lp:~eric-the-viking push_location:policy = appendpath }}} And replace 'eric' with your login id, and 'eric-the-viking' with your launchpad id. The `appendpath` policy means that the directories under ~/src are added to the path, so ~/src/juju-core/trunk would be pushed to (by default) lp:~eric-the-viking/juju-core/trunk. What this means is that when you create a new branch `new-work`, and go `bzr push` it goes to `lp:~eric-the-viking/juju-core/new-work`. Making a branch to work in ========================== Inside the $GOPATH/src/launchpad.net/juju-core directory, you can create a new branch to work on using: $ bzr switch -b new-work This creates a new branch in `~/src/juju-core` called `new-work` and switches the working tree to use that. Commits are now on that new branch, and push sends it to launchpad to the `new-work` branch. Everything else works the same. Useful aliases ============== $ bzr alias commit="commit --strict" This will mean that whenever you use commit, it adds the `--strict` flag. What this means is that it will not allow you to commit if there are unknown files. This is very useful when you create new files but forget to add them prior to commit. If you do have unknown files and want to override the strict behaviour for one commit, then you can go... $ bzr commit --no-strict -m "Blah blah" Another useful alias is: $ bzr alias ll="log --line -r-10..-1" Will give you something like the following: {{{ $ bzr ll 956: Tim Penhey 2013-03-06 Add some documentation around lightweight checkout usage. 955: Dave Cheney 2013-03-05 [merge] environs/ec2: try to get tests working on raring 954: Roger Peppe 2013-03-04 [merge] juju: add NewConnFromState 953: Dimiter Naydenov 2013-03-01 [merge] state, uniter: Units now use charm URLs 952: Francesco Banconi 2013-03-01 [merge] Implement the API unexpose command. 951: William Reade 2013-03-01 [merge] environs: drop InstanceIdAccessor hack 950: Brad Crittenden 2013-02-28 [merge] Add the 'expose' command to the API. 949: John A Meinel 2013-02-28 [merge] revert only r943 948: William Reade 2013-02-28 [merge] history: rewind 947: Ian Booth 2013-02-28 [merge] Better unauthorised errors }}} ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/system-ssh-key.txt���������������������������������0000644�0000153�0000161�00000002221�12321735642�025075� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������The idea of having a system SSH key is to support a number of real and potential use-cases. * The system ssh key could be used to monitor the bootstrap process, and this would benefit the new users that don't have an existing SSH key * Allows the api server machines to ssh to other machines in the environment * could be used to set up ssh tunnels through a single public facing IP address on the server * allows juju-run commands to be run on remote machiens Juju already creates a private key for serving the mongo database. It was an option to also use this key, but in the end, having different keys for different purposes just seems like a more robust idea. A system key is generated when the environment is bootstrapped, and uploaded as part of the cloud-init machine creation process. The public key part is added to the authorized keys list. This means that we need to generate an identity file and the authorized key line prior to creating the new machine. If subsequent state server machines are created, they also need to have the system identity file on them. Actually, it is most likely the API server jobs that we really care about. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/hacking-state.txt����������������������������������0000644�0000153�0000161�00000015716�12321735642�024727� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Hacking the juju-core/state package =================================== This document remains a work in progress; it's an attempt to capture the various conventions and things to bear in mind that aren't necessarily written down anywhere else. return values: ok vs err ------------------------ By convention, anything that could reasonably fail must use a separate channel to communicate the failure. Broadly speaking, methods that can fail in more than one way must return an error; those that can only fail in one way (eg Machine.InstanceId: the value is either valid or missing) are expected to return a bool for consistency's sake, even if the type of the return value is such that failure can be signalled in- band. changes to entities ------------------- Entity objects reflect remote state that may change at any time, but we don't want to make that too obvious. By convention, the only methods that should change an entity's in-memory document are as follows: * Refresh(), which should update every field. * Methods that set fields remotely should update only those fields locally. The upshot of this is that it's not appropriate to call Refresh on any argument to a state method, including receivers; if you're in a context in which that would be helpful, you should clone the entity first. This is simple enough that there's no Clone() method, but it would be kinda nice to implement them in our Copious Free Time; I think there are places outside state that would also find it useful. care and feeding of mgo/txn --------------------------- Just about all our writes to mongodb are mediated by the mgo/txn package, and using this correctly demands some care. Not all the code has been written in a fully aware state, and cases in which existing practice is divergent from the advice given below should be regarded with some suspicion. The txn package lets you make watchable changes to mongodb via lists of operations with the following critical properties: * transactions can apply to more than one document (this is rather the point of having them). * transactions will complete if every assert in the transaction passes; they will not run at all if any assert fails. * multi-document transactions are *not* atomic; the operations are applied in the order specified by the list. * operations, and hence assertions, can only be applied to documents with ids that are known at the time the operation list is built; this means that it takes extra work to specify a condition like "no unit document newer than X exists". The second point above deserves further discussion. Whenever you are implementing a state change, you should consider the impact of the following possible mongodb states on your code: * if mongodb is already in a state consistent with the transaction having already been run -- which is *always* possible -- you should return immediately without error. * if mongodb is in a state that indicates the transaction is not valid -- eg trying to add a unit to a dying service -- you should return immediately with a descriptive error. Each of the above situations should generally be checked as part of preparing a new []txn.Op, but in some cases it's convenient to trust (to begin with) that the entity's in-memory state reflects reality. Regardless, your job is to build a list of operations which assert that: * the transaction still needs to be applied. * the transaction is actively valid. * facts on which the transaction list's form depends remain true. If you're really lucky you'll get to write a transaction in which the third requirement collapses to nothing, but that's not really the norm. In practice, you need to be prepared for the run to return txn.ErrAborted; if this happens, you need to check for previous success (and return nil) or for known cause of invalidity (and return an error describing it). If neither of these cases apply, you should assume it's an assertion failure of the third kind; in that case, you should build a new transaction based on more recent state and try again. If ErrAborteds just keep coming, give up; there's an ErrExcessiveContention that helps to describe the situation. watching entities, and select groups thereof -------------------------------------------- The mgo/txn log enables very convenient notifications of changes to particular documents and groups thereof. The state/watcher package converts the txn event log into events and send them down client- supplied channels for further processing; the state code itself implements watchers in terms of these events. All the internally-relevant watching code is implemented in the file state/watcher.go. These constructs can be broadly divided into groups as follows: * single-document watchers: dead simple, they notify of every change to a given doc. SettingsWatcher bucks the convention of NotifyWatcher in that it reads whole *Settings~s to send down the channel rather than just sending notifications (we probably shouldn't do that, but I don't think there's time to fix it right now). * entity group watchers: pretty simple, very common; they notify of every change to the Life field among a group of entities in the same collection, with group membership determined in a wide variety of ways. * relation watchers: of a similar nature to entity group watchers, but generating carefully-ordered events from observed changes to several different collections, none of which have Life fields. Implementation of new watchers is not necessarily a simple task, unless it's a genuinely trivial refactoring (or, given lack of generics, copy- and-paste job). Unless you already know exactly what you're doing, please start a discussion about what you're trying to do before you embark on further work in this area. transactions and reference counts --------------------------------- As described above, it's difficult to assert things like "no units of service X exist". It can be done, but not without additional work, and we're generally using reference counts to enable this sort of thing. In general, reference counts should not be stored within entities: such changes are part of the operation of juju and are not important enough to be reflected as a constant stream of events. We didn't figure this out until recently, though, so relationDoc has a UnitCount, and serviceDoc has both a UnitCount and a RelationCount. This doesn't matter for the relation -- nothing watches relation docs directly -- but I fear it matters quite hard for service, because every added or removed unit or relation will be an unnecessary event sent to every unit agent. We'll deal with that when it's a problem rather than just a concern; but new reference counts should not be thoughtlessly added to entity documents, and opportunities to separate them from existing docs should be considered seriously. [TODO: write about globalKey and what it's good for... constraints, settings, statuses, etc; occasionaly profitable use of fast prefix lookup on indexed fields.] ��������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/charms-in-action.txt�������������������������������0000644�0000153�0000161�00000042432�12321735642�025334� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Charms in action ================ This document describes the behaviour of the go implementation of the unit agent, whose behaviour differs in some respects from that of the python implementation. This information is largely relevant to charm authors, and potentially to developers interested in the unit agent. Hooks ----- A service unit's direct action is entirely defined by its charm's hooks. Hooks are executable files in a charm's hooks directory; hooks with particular names will be invoked by the juju unit agent at particular times, and thereby cause changes to the world. Whenever a hook-worthy event takes place, the unit agent tries to run the hook with the appropriate name. If the hook doesn't exist, the agent continues without complaint; if it does, it is invoked without arguments in a specific environment, and its output is written to the unit agent's log. If it returns a non-zero exit code, the agent enters an error state and awaits resolution; otherwise it continues to process environment changes as before. In general, a unit will run hooks in a clear sequence, about which a number of useful guarantees are made. All such guarantees come with the caveat that there is [TODO: will be: `remove-unit --force`] a mechanism for forcible termination of a unit, and that a unit so terminated will just stop, dead, and completely fail to run anything else ever again. This shouldn't actually be a big deal in practice. Errors in hooks --------------- Hooks should ideally be idempotent, so that they can fail and be re-executed from scratch without trouble. As a hook author, you don't have complete control over the times your hook might be stopped: if the unit agent process is killed for any reason while running a hook, then when it recovers it will treat that hook as having failed -- just as if it had returned a non-zero exit code -- and request user intervention. It is unrealistic to expect great sophistication on the part of the average user, and as a charm author you should expect that users will attempt to re-execute failed hooks before attempting to investigate or understand the situation. You should therefore make every effort to ensure your hooks are idempotent when aborted and restarted. [TODO: I have a vague feeling that `juju resolved` actually defaults to "just pretend the hook ran successfully" mode. I'm not sure that's really the best default, but I'm also not sure we're in a position to change the UI that much.] The most sophisticated charms will consider the nature of their operations with care, and will be prepared to internally retry any operations they suspect of having failed transiently, to ensure that they only request user intervention in the most trying circumstances; and will also be careful to log any relevant information or advice before signalling the error. [TODO: I just thought; it would be really nice to have a juju-fail hook tool, which would allow charm authors to explicity set the unit's error status to something a bit more sophisticated than "X hook failed". Wishlist, really.] Charm deployment ---------------- * A charm is deployed into a directory that is entirely owned and controlled by juju. * At certain times, control of the directory is ceded to the charm (by running a hook) or to the user (by entering an error state). * At these times, and only at these times, should the charm directory be used by anything other than juju itself. The most important consequence of this is that it is a mistake to conflate the state of the charm with the state of the software deployed by the charm: it's fine to store *charm* state in the charm directory, but the charm must deploy its actual software elsewhere on the system. To put it another way: deleting the charm directory should not impact the software deployed by the charm in any way; and there is currently no mechanism by which deployed software can safely feed information back into the charm and/or expect that it will be acted upon in a timely way. [TODO: this sucks a bit. We have plans for a tool called `juju-run`, which would allow an arbitrary script to be invoked as though it were a hook at any time (well, it'd block until no other hook were running, but still). Probably isn't even that hard but it's still rolling around my brain, might either click soon or be overridden by higher priorities and be left for ages. I'm less sure, but have a suspicion, that `juju ssh <unit>` should also default to a juju-run environment: primarily because, without this, in the context of forced upgrades, the system cannot offer *any* guarantees about what it might suddenly do to the charm directory while the user's doing things with it. The alternative is to allow unguarded ssh, but tell people that they have to use something like `juju-run --interactive` before they modify the charm dir; this feels somewhat user-hostile, though.] Execution environment --------------------- Every hook is run in the deployed charm directory, in an environment with the following characteristics: * $PATH is prefixed by a directory containing command line tools through which the hooks can interact with juju. * $CHARM_DIR holds the path to the charm directory. * $JUJU_UNIT_NAME holds the name of the local unit. * $JUJU_CONTEXT_ID and $JUJU_AGENT_SOCKET are set (but should not be messed with: the command line tools won't work without them). * $JUJU_API_ADDRESSES holds a space separated list of juju API addresses. * $JUJU_ENV_NAME holds the human friendly name of the current environment. Hook tools ---------- All hooks can directly use the following tools: * juju-log (write arguments direct to juju's log (potentially redundant, hook output is all logged anyway, but --debug may remain useful)) * unit-get (returns the local unit's private-address or public-address) * open-port (marks the supplied port/protocol as ready to open when the service is exposed) * close-port (reverses the effect of open-port) * config-get (get current service configuration values) * relation-get (get the settings of some related unit) * relation-set (write the local unit's relation settings) * relation-ids (list all relations using a given charm relation) * relation-list (list all units of a related service) Within the context of a single hook execution, the above tools present a sandboxed view of the system with the following properties: * Any data retrieved corresponds to the real value of the underlying state at some point in time. * Once state data has been observed within a given hook execution, further requests for the same data will produce the same results, unless that data has been explicitly changed with relation-set. * Data changed by relation-set is only written to global state when the hook completes without error; changes made by a failing hook will be discarded and never observed by any other part of the system. * Not actually sandboxed: open-port and close-port operate directly on state. [TODO: lp:1089304 - might be a little tricky.] Hook kinds ---------- There are 5 `unit hooks` with predefined names that can be implemented by any charm: * install * config-changed * start * upgrade-charm * stop For every relation defined by a charm, an additional 4 `relation hooks` can be implemented, named after the charm relation: * <name>-relation-joined * <name>-relation-changed * <name>-relation-departed * <name>-relation-broken Unit hooks ---------- The `install` hook always runs once, and only once, before any other hook. The `config-changed` hook always runs once immediately after the install hook, and likewise after the upgrade-charm hook. It also runs whenever the service configuration changes, and when recovering from transient unit agent errors. The `start` hook always runs once immediately after the first config-changed hook; there are currently no other circumstances in which it will be called, but this may change in the future. The `upgrade-charm` hook always runs once immediately after the charm directory contents have been changed by an unforced charm upgrade operation, and *may* do so after a forced upgrade; but will *not* be run after a forced upgrade from an existing error state. (Consequently, neither will the config-changed hook that would ordinarily follow the upgrade-charm.) The `stop` hook is the last hook to be run before the unit is destroyed. In the future, it may be called in other situations. In normal operation, a unit will run at least the install, start, config-changed and stop hooks over the course of its lifetime. It should be noted that, while all hook tools are available to all hooks, the relation-* tools are not useful to the install, start, and stop hooks; this is because the first two are run before the unit has any opportunity to participate in any relations, and the stop hooks will not be run while the unit is still participating in one. Relation hooks -------------- For each charm relation, any or all of the 4 relation hooks can be implemented. Relation hooks operate in an environment slightly different to that of unit hooks, in the following ways: * JUJU_RELATION is set to the name of the charm relation. This is of limited value, because every relation hook already "knows" what charm relation it was written for; that is, in the "foo-relation-joined" hook, JUJU_RELATION is "foo". * JUJU_RELATION_ID is more useful, because it serves as unique identifier for a particular relation, and thereby allows the charm to handle distinct relations over a single endpoint. In hooks for the "foo" charm relation, JUJU_RELATION_ID always has the form "foo:<id>", where id uniquely but opaquely identifies the runtime relation currently in play. * The relation-* hook tools, which ordinarily require that a relation be specified, assume they're being called with respect to the current relation. The default can of course be overridden as usual. Furthermore, all relation hooks except relation-broken are notifications about some specific unit of a related service, and operate in an environment with the following additional properties: * JUJU_REMOTE_UNIT is set to the name of the current related unit. * The relation-get hook tool, which ordinarily requires that a related unit be specified, assumes that it is being called with respect to the current related unit. The default can of course be overridden as usual. For every relation in which a unit partcipates, hooks for the appropriate charm relation are run according to the following rules. The "relation-joined" hook always runs once when a related unit is first seen. The "relation-changed" hook for a given unit always runs once immediately following the relation-joined hook for that unit, and subsequently whenever the related unit changes its settings (by calling relation-set and exiting without error). Note that "immediately" only applies within the context of this particular runtime relation -- that is, when "foo-relation-joined" is run for unit "bar/99" in relation id "foo:123", the only guarantee is that the next hook to be run *in relation id "foo:123"* will be "foo-relation-changed" for "bar/99". Unit hooks may intervene, as may hooks for other relations, and even for other "foo" relations. The "relation-departed" hook for a given unit always runs once when a related unit is no longer related. After the "relation-departed" hook has run, no further notifications will be received from that unit; however, its settings will remain accessible via relation-get for the complete lifetime of the relation. The "relation-broken" hook is not specific to any unit, and always runs once when the local unit is ready to depart the relation itself. Before this hook is run, a relation-departed hook will be executed for every unit known to be related; it will never run while the relation appears to have members, but it may be the first and only hook to run for a given relation. The stop hook will not run while relations remain to be broken. Relations in depth ------------------ A unit's `scope` consists of the group of units that are transitively connected to that unit within a particular relation. So, for a globally-scoped relation, that means every unit of each service in the relation; for a locally-scoped relation, it means only those sets of units which are deployed alongside one another. That is to say: a globally-scoped relation has a single unit scope, whilst a locally-scoped relation has one for each principal unit. When a unit becomes aware that it is a member of a relation, its only self- directed action is to `join` its scope within that relation. This involves two steps: * Write initial relation settings (just one value, "private-address"), to ensure that they will be available to observers before they're triggered by the next step; * Signal its existence, and role in the relation, to the rest of the system. The unit then starts observing and reacting to any other units in its scope which are playing a role in which it is interested. To be specific: * Each provider unit observes every requirer unit * Each requirer unit observes every provider unit * Each peer unit observes every other peer unit Now, suppose that some unit as the very first unit to join the relation; and let's say it's a requirer. No provider units are present, so no hooks will fire. But, when a provider unit joins the relation, the requirer and provider become aware of each other almost simultaneously. (Similarly, the first two units in a peer relation become aware of each other almost simultaneously.) So, concurrently, the units on each side of the relation run their relation-joined and relation-changed hooks with respect to their counterpart. The intent is that they communicate appropriate information to each other to set up some sort of connection, by using the relation-set and relation-get hook tools; but neither unit is safe to assume that any particular setting has yet been set by its counterpart. This sounds kinda tricky to deal with, but merely requires suitable respect for the relation-get tool: it is important to realise that relation-get is *never* guaranteed to contain any values at all, because we have decided that it's perfectly legitimate for a unit to delete its own private-address value. [TODO: There is a school of thought that maintains that we should add an independent "juju-private-address" setting that *is* guaranteed, but for now the reality is that relation-get can *always* fail to produce any given value. However, in the name of sanity, it's probably reasonable to treat a missing private-address as an error, and assume that `relation-get private-address` is always safe. For all other values, we must operate with the understanding that relation-get can always fail.] In one specific kind of hook, this is easy to deal with. A relation-changed hook can always exit without error when the current remote unit is missing data, because the hook is guaranteed to be run again when that data changes -- and, assuming the remote unit is running a charm that agrees on how to implement the interface, the data *will* change and the hook *will* be run again. In *all* other cases -- unit hooks, relation hooks for a different relation, relation hooks for a different remote unit in the same relation, and even relation hooks other than -changed for the *same* remote unit -- there is no such guarantee. These hooks all run on their own schedule, and there is no reason to expect them to be re-run on a predictable schedule, or in some cases ever again. This means that all such hooks need to be able to handle missing relation data, and to complete successfully; they mustn't fail, because the user is powerless to resolve the situation, and they can't even wait for state to change, because they all see their own sandboxed composite snapshot of fairly-recent state, which never changes. So, outside a vey narrow range of circumstances, relation-get should be treated with particular care. The corresponding advice for relation-set is very simple by comparison: relation-set should be called early and often. Because the unit agent serializes hook execution, there is never any danger of concurrent changes to the data, and so a null setting change can be safely ignored, and will not cause other units to react. Departing relations ------------------- A unit will depart a relation when either the relation or the unit itself is marked for termination. In either case, it follows the same sequence: * For every known related unit -- those which have joined and not yet departed -- run the relation-departed hook. * Run the relation-broken hook. * `depart` from its scope in the relation. The unit's departure from its scope will in turn be detected by units of the related service, and cause them to run relation-departed hooks. A unit's relation settings persist beyond its own departure from the relation; the final unit to depart a relation marked for termination is responsible for destroying the relation and all associated data. Debugging charms ---------------- Facilities are currently not good. * juju ssh * juju debug-hooks [TODO: not implemented] * juju debug-log [TODO: not implemented] It may be helpful to note that the charm directory is a git repository that holds the complete hook-by-hook history of the deployment. This property is not guaranteed, and charms should not depend upon it, but humans who need to dig around in charm directories should be aware of it. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/api.txt��������������������������������������������0000644�0000153�0000161�00000023332�12321735642�022747� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������* API Design The overall aim is to make agents and clients connect through a network API rather than directly to the underlying database as is the case currently. This will have a few advantages: - Operations that involve multiple round trips to the database can be made more efficient because the API server is likely to be closer to the database server. - We can decide on, and enforce, an appropriate authorization policy for each operation. - The API can be made easy to use from multiple languages and client types. There are two general kinds of operation on the API: simple requests and watch requests. I'll deal with simple requests first. * Simple requests A simple request takes some parameters, possibly makes some changes to the state database, and returns some results or an error. When the request returns no data, it would theoretically be possible to have the API server operate on the request without returning a reply, but then the client would not know when the request has completed or if it completed successfully. Therefore, I think it's better if all requests return a reply. Here is the list of all the State requests that are currently used by the juju agents: XXX link We will need to implement at least these requests (possibly slightly changed, but hopefully as little as possible, to ensure as little churn as possible in the agent code when moving to using the API) 41 out of the 59 requests are operating directly on a single state entity, expressed as the receiver object in the Go API. For this reason I believe it's appropriate to phrase the API requests in this way - as requests on particular entities in the state. This leads to certain implementation advantages (see "Implementation" below) and means there's a close correspondence between the API protocol and the API as implemented in Go (and hopefully other languages too). To make the protocol accessible, we define all messages to be in JSON format and we use a secure websocket for transport. For security, we currently rely on a server-side certificate and passwords sent over the connection to identify the client, but it should be straightforward to enable the server to do client certificate checking if desired. Here's a sample request to change the instance id associated with a machine, and its reply. (I'll show JSON in rjson form, to keep the noise down, see http://godoc.org/launchpad.net/rjson). Client->Server { RequestId: 1234 Type: "Machine" Id: "99" Request: "SetInstanceId" Params: { InstanceId: "i-43e55e5" } } Server->Client { RequestId: 1234 Error: "" Result: { } } We use the RequestId field to associate the request and its reply. The API client must not re-use a request id until it has received the request's reply (the easiest way to do that is simply to increment the request id each time). We allow multiple requests to be outstanding on a connection at once, and their replies can be received in any order. In the request, the Id field may be omitted to specify an empty Id, and Params may be omitted to specify no request parameters. Similarly, in the response, the Error field may be omitted to signify no error, and the Result field may be omitted to signify no result. To save space below, I've omitted fields accordingly. The Type field identifies the type of entity to act on, and the Id field its identifier. Currently I envisage the following types of entities: Admin Admin (a singleton) is used by a client when identifying itself to the server. It is the only thing that can be accessed before the client has authenticated. Client ClientWatcher Client (a singleton) is the access point for all the GUI client and other user-facing methods. This is only usable by clients, not by agents. State Machine Unit Relation RelationUnit Service Pinger MachineWatcher UnitWatcher LifecycleWatcher ServiceUnitsWatcher ServiceRelationsWatcher RelationScopeWatcher UnitsWatcher ConfigWatcher NotifyWatcher MachineUnitsWatcher These correspond directly to types exported by the juju state package. They are usable only by agents, not clients. The Request field specifies the action to perform, and Params holds the parameters to that request. In the reply message, the RequestId field must match that of the request. If the request failed, then the Error field holds the description of the error (it is possible we might add a Code field later, to help diagnosing specific kinds of error). The Result field holds the results of the request (in this case there are none, so it's empty). That completes the overview of simple requests, so on to watching. * Watching To watch something in the state, we invoke a Watch request, which returns a handle to a watcher object, that can then be used to find out when changes happen by calling its Next method. To stop a watcher, we call Stop on it. For example, if an agent wishes to watch machine 99, the conversation with the API server looks something like this: Client->Server { RequestId: 1000 Type: "Machine" Id: "99" Request: "Watch" } Server->Client { RequestId: 1000 Response: { NotifyWatcherId: "1" } } At this point, the watcher is registered. Subsequent Next calls will only return when the entity has changed. Client->Server { RequestId: 1001 Type: "NotifyWatcher" Id: "1" Request: "Next" } This reply will only sent when something has changed. Note that for this particular watcher, no data is sent with the Next response. This can vary according to the particular kind of watcher - some watchers may return deltas, for example, or the latest value of the thing being watched. Server->Client { RequestId: 1001 } The client can carry on sending Next requests for as long as it chooses, each one returning only when the machine has changed since the previous Next request. Client->Server { RequestId: 1002 Type: "NotifyWatcher" Id: "1" Request: "Next" } Finally, the client decides to stop the watcher. This causes any outstanding Next request to return too - in no particular order with respect to the Stop reply. Client->Server { RequestId: 1003 Type: "NotifyWatcher" Id: "1" Request: "Stop" } Server->Client { RequestId: 1002 } Server->Client { RequestId: 1003 } As you can see, we use exactly the same RPC mechanism for watching as for simple requests. An alternative would have been to push watch change notifications to clients without waiting for an explicit request. Both schemes have advantages and disadvantages. I've gone with the above scheme mainly because it makes the protocol more obviously correct in the face of clients that are not reading data fast enough - in the face of a client with a slow network connection, we will not continue saturating its link with changes that cannot be passed through the pipe fast enough. Because Juju is state-based rather than event-based, the number of possible changes is bounded by the size of the system, so even if a client is very slow at reading the number of changes pushed down to it will not grow without bound. Allocating a watcher per client also implies that the server must keep some per-client state, but preliminary measurements indicate that the cost of that is unlikely to be prohibitive. Using exactly the same mechanism for all interactions with the API has advantages in simplicity too. * Authentication and authorization The API server is authenticated by TLS handshake before the websocket connection is initiated; the client should check that the server's certificate is signed by a trusted CA (in particular the CA that's created as a part of the bootstrap process). One wrinkle here is that before bootstrapping, we don't know the DNS name of the API server (in general, from a high-availability standpoint, we want to be able to serve the API from any number of servers), so we cannot put it into the certificate that we generate for the API server. This doesn't sit well with the way that www authentication usually works - hopefully there's a way around it in node. The client authenticates to the server currently by providing a user name and password in a Login request: Client->Server { RequestId: 1 Type: "Admin" Request: "Login" Params: { "Tag": "machine-1", Password: "a2eaa54323ae", } } Server->Client { RequestId: 1 } Until the user has successfully logged in, the Login request is the only one that the server will respond to - all other requests yield a "permission denied" error. The exact form of the Login request is subject to change, depending on what kind user authentication we might end up with - it may even end up as two or more requests, going through different stages of some authentication process. When logged in, requests are authorized both at the type level (to filter out obviously inappropriate requests, such as a client trying to access the agent API) and at the request level (allowing a more fine-grained approach). * Versioning I'm not currently sure of the best approach to versioning. One possibility is to have a Version request that allows the client to specify a desired version number; the server could then reply with a lower (or equal) version. The server would then serve the version of the protocol that it replied with. Unfortunately, this adds an extra round trip to the session setup. This could be mitigated by sending both the Version and the Login requests at the same time. * Implementation The Go stack consists of the following levels (high to low): client interface ("launchpad.net/juju-core/state/api".State) rpc package ("launchpad.net/juju-core/rpc".Client) ----- (json transport over secure websockets, implemented by 3rd party code) rpc package ("launchpad.net/juju-core/rpc".Server) server implementation ("launchpad.net/juju-core/state/api".Server) server backend ("launchpad.net/juju-core/state") mongo data store ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/glossary.txt���������������������������������������0000644�0000153�0000161�00000041431�12321735642�024041� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Juju Glossary ============= This document introduces and briefly explains a number of terms that are used throughout the juju documentation, roughly broken down by conceptual area. The intent of this document is to make the reader aware of the available modes of interaction with juju, and how the various commands and concepts interact. It makes no attempt to be comprehensive; just accurate and opinionated. Charms ------ A `charm` is a collection of code and data that embodies best practice in the deployment of a particular piece (or collection) of software. Examples of software that has been charmed include: * wordpress (blogging platform) * mediawiki (wiki platform) * mysql (relational database) * mongodb (non-relational database) * hadoop (heavyweight data crunching) * glance, keystone, nova-compute, etc (openstack components) * minecraft (game server) A charm consists of the following components: * metadata (describes the charm's purpose and capabilities) * configuration (describes the ways in which a user can tune the software) * hooks (executables, invoked by juju, that configure and deploy the software) * revision (an integer identifying separate versions of the same charm) * any additional code or data useful to the hooks or the deployed software A `charm directory` is a filesystem directory containing the aforementioned components of a charm in standard locations. (Any additional code/data can go anywhere not reserved for the other components.) A `charm bundle` is a charm directory serialized as a zip file. This format is used for storage and distribution only; when a charm is deployed it is always unbundled into a charm directory, within which all hooks are executed. A `repository` is a collection of charms that can be deployed by juju. The `charm store` is the default repository, which serves only curated bundled charms. A `local repository` is a repository located on the same system as the juju client. A `charm URL` is a string that identifies the provenance and the intended deployment target of a charm, and may also specify the charm's revision. Charms are identified by their charm URLs. Environments ------------ A `machine` is a computing resource on which components of juju, and of the software deployed by juju, can run. The following command is used to manipulate machines directly. * juju terminate-machine (soon to alias destroy-machine) [TODO: not implemented] An `environment` is a deployment of juju. It always includes at least one machine responsible for maintaining the system's state, and potentially provisioning additional machines in response to state changes. The following commands are used to manipulate and inspect the environment. * juju bootstrap * juju status [TODO: somewhat incomplete] * juju upgrade-juju * juju destroy-environment An `environment provider` mediates between juju and the substrate on which an environment runs. Each provider allows juju to run against a different backend: * ec2 (Amazon EC2 and S3) * maas (bare metal) [TODO: work in progress] * local (LXC containers on client machine) [TODO: work in progress] * openstack (many public and private clouds) [TODO: work in progress] An `environment configuration` describes how to create and connect to an environment from a specific provider. An environment configuration must always specify at least the following information: * name (to identify the environment) * type (to specify the provider) * admin-secret (a "password" identifying an client with administrative- level access to system state) * authorized-keys (SSH public keys identifying users allowed to connect to machines in the environment) ...and may accept or even require additional keys depending on the provider type; but the full details of environment configuration are outside the scope of this document. [TODO: there should/will be commands for inspecting and manipulating the configuration of an environment while it's running; the names "env-get" and "env-set" have been mooted, but I'm not sure that's sane: I can't see any reason not to use the existing "get" and "set", which currently only work for services. But I could just be missing something obvious. Note that certain settings, such as "name" and "type", and "region" in the ec2 provider, will need to be immutable, and I'm not sure whether that's implemented yet.] An `environments file` is a YAML file containing environment configurations. Juju cannot operate without a valid environments file, which must be saved at `~/.juju/environments.yaml` on the client machine. The simplest functional environments file looks like this: environments: aws: type: ec2 admin-secret: <password string> control-bucket: <globally unique S3 bucket name> The "name" of the single defined environment is determined by its key in the environments map ("aws"), and the "authorized-keys" can be safely omitted so long as the client machine has a public key with a standard name saved in `~/.ssh`. Because it's using the "ec2" provider type, it also has to specify a "control-bucket" key; the "access-key" and "secret-key" settings are also required, but can be omitted if the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables are set in the client process. The "region" setting defaults to "us-east-1"; there are other possible settings but they're not relevant here. [NOTE: there is much more to environment configuration than has been discussed here. "authorized-keys-path" and "default-series" both apply to every provider, the ec2 provider offers many not mentioned here, and it will only get worse with each new provider...] Services -------- A `service` is a deployment of a charm in an environment. The following commands are used to manipulate services: * juju deploy * juju destroy-service [TODO: not implemented] A service's `configuration` is defined by its charm, and allows the user to inspect and tune the service's operation using the following commands: * juju get * juju set A `unit` is the fundamental component of a service; services are composed of units. Each unit represents an instance of the charm's software, deployed to some machine in the service's environment. The following commands are used to manipulate units: * juju add-unit * juju remove-unit (soon to alias destroy-unit) [TODO: units are currently not, but will shortly be, deployed to LXC containers within their machines; this prevents units on the same machine from interfering with one another (except when each attempts to open the same port; we haven't worked out how to deal with that yet).] A service is `exposed` if it is theoretically accessible from the public internet (or, at least, from outside its environment). The fact of a service's exposure does not necessarily imply that any units of that service are actually accessible; actual access is mediated by the service's units, which are responsible for specifying the ports that should actually be opened when the service is exposed. Service exposure is controlled with the following commands: * juju expose * juju unexpose [NOTE: the "firewall-mode" environment configuration setting comes into play here, but that may be a topic for a more detailed document, along with the varying levels of firewalling support in the various providers.] A service's charm can be `upgraded` when the revision of that charm available from its original repository is greater than that or the service's current charm, and when the following conditions hold: * all configuration keys that exist in both versions of the charm must have the same data type (addition or removal of config settings is fine, and it's ok for defaults to change, but (for example) a string cannot become an integer). * any normal relations in which the service is participating must also be available, unchanged, in the newer version of the charm (addition or removal of charm relations is, in general, fine). * [TODO: something to do with storage compatibility, but storage doesn't exist yet] The following command is used to upgrade a service's charm: * juju upgrade-charm [TODO: not implemented] [TODO: --revision and --switch params, that do not exist in the python version, are mooted; they would respectively allow up/downgrades to specific revisions, and crossgrades to entirely different charms, but priority/agreement is unclear.] Sometimes (although, hopefully, rarely) a unit will encounter an `error` that requires human intervention. These cases are as follows: * a charm hook returned a non-zero exit code * the unit process was killed while running a charm hook * a charm upgrade failed due to conflicting content in the charm directory In each of these situations, the unit stops responding to most external events, and waits for an administrator to resolve the problem. The administrator may need to log into the machine with the failed unit to determine and resolve the problem, or he may be able to resolve the problem automatically by trying to re-run the failed hook. The following commands are useful in error recovery: * juju ssh * juju scp * juju resolved A `forced upgrade` is a form of upgrade that ignores unit error states, and upgrades them anyway. Forced upgrades are subtle and quick to anger; they are only recommended if you have sole control of your environment and a clear understanding of the upgrade process as it applies to your specific charm. Relations --------- An `interface` is an informally-agreed protocol for transferring information between service units. Examples include: * http (hostname, port) * mysql (host, database, user, password, slave) * ceph-client (key, auth, rid) [TODO: I don't think rid is appropriate, we should take a look and figure out if it could be dropped] A `role` describes the manner in which a charm uses an interface. A role can have one of three values: "provider", "requirer", or "peer". A `(charm) relation` is an entry in a charm's metadata that indicates that it can fulfil some role over some interface. For example, the "relations" section of a charm's metadata might look like this (taken from wordpress): requires: db: interface: mysql nfs: interface: mount cache: interface: memcache provides: website: interface: http peers: loadbalancer: interface: reversenginx The above defines five (charm) relations, named "db", "nfs", "cache", "website", and "loadbalancer". "db" is a "requirer" over the "mysql" interface; "website" is a "provider" over the "http" interface. An `endpoint` is the combination of a service with one of its charm's relations. The set of a service's endpoints define the possible connections involving that service. Within an environment, an endpoint is uniquely identifiable when expressed in the form `<service-name>:<charm-relation-name>`. A "normal" `relation` is a connection between the endpoints of two services. The services' charms must respectively "provide" and "require" relations with identical interfaces. When two services are participating in a relation, each unit of each service can communicate with every unit of the other service. Normal relations can be manipulated with the following commands: * juju add-relation * juju remove-relation (soon to alias destroy-relation) A `(peer) relation` is a connection within a service defined by a single endpoint with the "peer" role. If such an endpoint exists, the peer relation is automatically created when the service is deployed; each unit of the service can communicate with every other unit of the service. Peer relations cannot be manipulated in the UI, but are displayed in `juju status` output. The word `relation`, when used casually, *may* refer to a charm relation or a peer relation, but is most likely to refer to a "normal" relation. When manipulating relations, it is important to understand that a single endpoint can participate in multiple relations. For example, a mysql:server endpoint can quite happily participate in relations with both a wikimedia:db endpoint and a wordpress:db endpoint (or even in multiple relations with separate services running their own charms: for example, wordpress1:db and wordpress2:db). In each case, the mysql charm is responsible for creating separate users and databases for each separate relation. [TODO: charm relations also have a concept of "limit" which probably ought to come in here, but no code respects it at the moment. Oh, and a "required" field too, and I'm not sure whether anyone fully recalls its precise intended semantics... not sure what to do about this.] Subordinates ------------ [TODO: subordinates are not yet implemented in go, but the vast majority of the building blocks are already integrated.] A `subordinate charm` is a charm which declares itself to be subordinate. A `subordinate service` is a service running a subordinate charm. A `subordinate unit` is a unit of a subordinate service. All other charms, services, and units are `principal` units, by virtue of not being subordinate. A charm relation's `scope` controls which units within a relation are visible to one another. Most relations have "global" scope, which is the default; a subordinate charm must define at least one relation with "local" scope. A normal or peer relation's `scope` is the narrowest scope amongst its endpoints; a normal relation's scope is "global" unless either endpoint has "local" scope, in which case it the whole relation has "local" scope, while a peer relation's single endpoint must always in practice have "global" scope. When a subordinate service is deployed, no units are created; the add-unit and remove-unit [TODO: lp:1091634] commands do not apply to subordinate services. Instead, subordinate units are deployed as a side-effect of the creation of locally-scoped relations between the subordinate service and others, and recalled as a result of the destruction of the services or relations they are dependent upon [TODO: lp:1091865]. To clarify: when a new relation is added and all the following conditions apply: * one service is a subordinate; * one service is a principal; * the relation between the two is locally scoped, by virtue of at least one of the endpoints having local scope: ...a new unit of the subordinate service will be created and deployed alongside each unit of the principal service (unless one already exists). Each principal unit is responsible for its own associated subordinate, which runs alongside the principal with essentially identical privileges. Due to the local scoping of the relation, each subordinate unit responds only to the principal unit that deployed it, and vice versa. [TODO: to clarify: once units are deployed inside their own containers, subordinate units will be installed inside their principal unit's container. But we don't have containers yet.] A subordinate service can of course participate in globally-scoped relations as well; such relations can be added as normal, and the subordinate units participate in those relations just as in any other global relation. [NOTE: there's also a magic implicit charm relation, called "juju-info", which provides the "juju-info" interface and allows subordinates to establish relations with arbitrary principals. Couldn't really figure out how to work it in nicely.] Constraints ----------- [TODO: constraints are not yet implemented in go; a substantial amount of work needs to be done] Some environment providers offer the ability to provision machines with varying characteristics. An environment provider defines a vocabulary of `constraints` to control the computing resources that are available from the provider; by specifying global environment constraints and overriding them where necessary at the service level, a user can ensure that her workloads are only run on machines with appropriate characteristics. Available constraints vary by provider, and are manipulated and inspected using the following commands: * juju set-constraints (not implemented) * juju get-constraints (not implemented) Storage ------- [TODO: storage remains an informally-specified concept distributed across several brains; don't even try to document it.] Entity lifecycles ----------------- When services, relations, units and machines are added and destroyed by the client, changes to the environment will take some time to occur: in particular, destruction is rarely instantaneous. Destroyed entities will continue to be displayed in juju status output but marked as "dying" [TODO: they are not] until the underlying resources have been removed. This is not really worth worrying about in general, but it does mean that after a service or relation has been destroyed you will need to wait until they have been *removed* before you're able to add a new service or relation with the same name. It's not an issue for units or machines, because their names are assigned internally by juju and are guaranteed to be unique. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/death-and-destruction.txt��������������������������0000644�0000153�0000161�00000023250�12321735642�026363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Death and Destruction ===================== This document describes in detail the operations associated with the destruction and removal of the fundamental state entities, and what agents are responsible for those operations. Each entity has an associated destroy-* command. The precise implications of destruction differ by entity, but there are common features: * Only Alive entities can be destroyed; if destruction is already in progress, as evidenced by an entity not being Alive, its "destruction" is a no-op. * Entities might be removed immediately they are destroyed, but this is not guaranteed. * If an entity is not removed immediately it is destroyed, its eventual removal is very likely; but it is not currently guaranteed, for the following reasons: * Hardware failure, even when detected and corrected by a Provisioner, can lead to unremovable relations, because the redeployed unit doesn't know what relations it's in. This would be fixable by making the unit agent always leave the scope of relations when they're detected; or, probably better, by using actual remote scope membership state to track relation membership (rather than using the existence of a local directory, whose true intent is to track the membership of *other* units, as a proxy). This is actually a pretty serious BUG and should be addressed soon; neither proposed solution is very challenging. * Undetected hardware failure is annoying, and can block progress at any time, but can be observed via additional monitoring and resolved via out- of-band termination of borked resources, which should be sufficient to get the system moving again (assuming the above bug is fixed). * Unknown problems in juju, in which agents fail to fulfil the duties laid out in this document, could block progress at any time. Assuming a version of the agent code which does not exhibit the problem exists, it should always be possible to work around this situation by upgrading the agent; and, if that fails, by terminating the underlying provider resources out-of-band, as above, and waiting for the new agent version to be deployed on a fresh system (with the same caveat as above). * In light of the preceding two points, we don't *have* to implement "--force" options for `juju destroy-machine` and `juju destroy-unit`. This is good, because it will be tricky to implement them well. In general, the user can just forget about entities once she's destroyed them; the only caveat is that she may not create new services with the same name, or new relations identical to the destroyed ones, until those entities have finally been removed. In rough order of complexity, here's what happens when each entity kind is destroyed. Note that in general the appropriate action is contingent on mutable remote state, and many operations must be expressed as a transaction involving several documents: the state API must be prepared to handle aborted transactions and either diagnose definite failure or retry until the operation succeeds (or, perhaps, finally error out pleading excessive contention). juju destroy-machine -------------------- Destroying a machine involves a single transaction defined as follows: * If the machine is not Alive, abort without error. * If the machine is the last one with JobManageEnviron, or has any assigned units, abort with an appropriate error. * Set the machine to Dying. When a machine becomes Dying, the following operation occurs: * The machine's agent sets the machine to Dead. When a machine becomes Dead, the following operations occur: * The machine's agent terminates itself and refuses to run again. * A Provisioner (a task running in some other machine agent) observes the death, decommissions the machine's resources, and removes the machine. Removing a machine involves a single transaction defined as follows: * If the machine is not Dead, abort with an appropriate error. * Delete the machine document. juju destroy-unit ----------------- Destroying a unit involves a single transaction defined as follows: * If the unit is not Alive, abort without error. * Set the unit to Dying. When a unit becomes Dying, the following operations occur: * The unit's agent leaves the scopes of all its relations. Note that this is a potentially complex sequence of operations and may take some time; in particular, any hooks that fail while the unit is leaving relations and stopping the charm will suspend this sequence until resolved (just like when the unit is Alive). * The unit's agent then sets the unit to Dead. When a unit becomes Dead, the following operations occur: * The unit's agent terminates itself and refuses to run again. * The agent of the entity that deployed the unit (that is: a machine agent, for a principal unit; or, for a subordinate unit, the agent of a principal unit) observes the death, recalls the unit, and removes it. Removing a unit involves a single transaction, defined as follows: * If the unit is a principal unit, unassign the unit from its machine. * If the unit is a subordinate unit, unassign it from its principal unit. * Delete the unit document. * If its service is Alive, or has at least two units, or is in at least one relation, decrement the service's unit count; otherwise remove the service. juju destroy-relation --------------------- Destroying a relation involves a single transaction defined as follows: * If the relation is not Alive, abort without error. * If any unit is in scope, set the relation to Dying. * Otherwise: * If the relation destruction is a direct user request, decrement the relation counts of both services. * If the relation destruction is an immediate consequence of service destruction, decrement the reference count of the counterpart service alone. (This is because the service destruction logic is responsible for the relation count of the service being destroyed.) * Delete the relation document. * Mark the relation's unit settings documents for future cleanup. * This is done by creating a single document for the attention of some other part of the system (BUG: which doesn't exist), that is then responsible for mass-deleting the (potentially large number of) settings documents. This completely bypasses the mgo/txn mechanism, but we don't care because those documents are guaranteed to be unreferenced and unwatched, by virtue of the relation's prior removal. When a relation is set to Dying, the following operations occur: * Every unit agent whose unit has entered the scope of that relation observes the change and causes its unit to leave scope. * If the relation has container scope, and no other container-scoped relation between its services is Alive, the unit agents of the subordinate units in the relation will observe the change and destroy their units. The Dying relation's document is finally removed in the same transaction in which the last unit leaves its scope. Because this situation involves the relation already being Dying, its services may also be Dying, and so the operations involved are subtly different to those taken above (when we know for sure that the relation -- and hence both services -- are still Alive). * Here, "the service" refers to the service of the unit departing scope, and "the counterpart service" refers to the other service in the relation. * Decrement the relation count of the unit's service (we know that service is not ready to be removed, because its unit is responsible for this transaction and the service clearly therefore has a unit count greater than zero). * Delete the relation document. * Mark the relation's unit settings documents for future cleanup. * If the counterpart service (the one that is not the unit's service) is Alive, or has at least one unit, or is in at least two relations, decrement its relation count; otherwise remove the counterpart service. juju destroy-service -------------------- Destroying a service involves a single transaction defined as follows: * If the service is not Alive, abort without error. * If the service is in any relations, do the following for each one: * If the relation is already Dying, skip it. * If the relation is Alive, destroy the relation without modifying the service's relation count. If the relation's destruction implies its removal, increment a local removed-relations counter instead. * If the service's unit count is greater than 0, or if the value of the aforementioned removal counter is less than the service's relation count, we know that some entity will still hold a reference to the service after the transaction completes, so we set the service to Dying and decrement its relation count by the value of the removal counter. * Otherwise, remove the service immediately, because we know that no reference to the service will survive the transaction. When a service becomes Dying, the following operations occur: * Every unit agent of the service observes the change and destroys its unit. The Dying service's document is finally removed in the same transaction that removes the last entity referencing that service. This could be either the removal of the last unit in the service, or the removal of the last relation the service is in, as described above. To remove a service, the following operations must occur in a single transaction: * Remove the service document. * Remove the service's settings document. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/juju-core-release-process.txt����������������������0000644�0000153�0000161�00000010773�12321735642�027200� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Introduction ------------ This document outlines the steps for producing a juju-core release. Step 1. Prepare release notes ============================= The release notes are prepared in Google document form, for easlier colaboration with juju-core and provider authors. A sample release note document is here https://docs.google.com/a/canonical.com/document/d/1Vnf2_sDtxZYaFRE7B0hY_f9FHxAOgximUj6UaJ5NyuE/edit# Generally new documents are created using the 'make copy' function. This is generally done earlier in the week with a release on the weekend, this is purely by convention. Look on https://canonical.leankitkanban.com/ and launchpad for items which have been fixed during the release. Leankit cards in the Merged lane should be documented in the release notes if applicable -- many cards are for internal refactoring or work which has no customer visible effect; they can be omitted. LP Bugs marked fixed comitted should be similarly documented and moved to fixed released. Bugs in progress should be moved to the next milestone (and milestone created if necessary). note: this process does admit the real possiblity that commits which have no leankit card or LP bug, will be missed. Step 2. Tag the release ======================= Most juju-core components do not have tags, but goose and juju-core do. Tag the juju-core and goose repos with `juju-x.y.z` tag, if you don't have permission to tag directly on the repo talk to jam who runs the bot. Don't try to submit a merge proposal with a tag, it doesn't work. Step 3. Build the release ========================= For stable releases, skip this step and proceed to the tarball step. For development releases (x.y.z, where y is odd) they are packaged via a launchpad build recipe. https://code.launchpad.net/~dave-cheney/+recipe/juju-core Update the tag on juju-core and goose to match the tag produced in step 2 then kick off the build. Step 4. Build release tarball ============================= Use the script located in scripts/build-release-tarball to produce a .tar.gz which contains all the source dependencies. Sign it and upload to LP. For stable release, the server team will feed this to the saucy release process and backport it previous series. Step 5. Update the version ========================== Once a release has been built againsts a version you must update the version number in version/version.go propose it and commit it. This moves the development version to the next release number in the series. Step 6. Upload tools to the s3 public bucket ============================================ For each artifact produced by the build recipe or the the server teams' release process, run scripts/release-public-tools/release-public-tools.bash $DEB_URL. This will download the .deb, extract the /usr/bin/jujud binary and upload it with the correct name to the s3 bucket. This setup requires credentials for the s3 bucket and the s3up tool (available on lp), currently, mgz and dfc have these credentials, the bucket is owned by Gustavo Niemeyer. Step 7. TEST! ============= apt-get update && apt-get install juju-core should install the latest release on your system. Test this release by bootstrapping an environment in ec2 (and hp cloud if you have access), and do a basic wordpress + mysql deployment. If you can relate wordpress and mysql, expose wordpress and get to the public address on the wordpress setup screen, this release is a success. If this step fails then this release is a failure and the release number is unused. Do not reuse release numbers. It is ok to have gaps in the sequence, we've done it before, water is still wet. The previous paragraph is mostly relevant for devel releases. For stable releases they are branched from a known working devel branch and then fed through the launchpad build process then backported into ppa:juju/stable so there is far less chance that they will be a failure. Step 7. Publish the release notes to launchpad, closing the milestone ===================================================================== Publish the text of the release notes to LP, close the milestone. Step 8. Announce the release ============================ Announce the release to juju-dev@lists.ubuntu.com juju@lists.ubuntu.com and canonical-juju@lists.canonical.com, copying and pasting the body of the release notes. A sample release note is here https://lists.ubuntu.com/archives/juju-dev/2013-August/001338.html Step 9. Upload tools from the s3 bucket to all other CPCs ========================================================= Procedure unknown. �����juju-core_1.18.1/src/launchpad.net/juju-core/doc/provisioning.txt�����������������������������������0000644�0000153�0000161�00000011475�12321735642�024731� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������What We Run, and Why ==================== Expressed as compactly as possible, the Provisioner is responsible for making sure that non-Dead machine entities in state have agents running on live instances; and for making sure that Dead machines, and stray instances, are removed and cleaned up. However, the choice of exactly what we deploy involves some subtleties. At the Provisioner level, it's simple: the series and the constraints we pass to the Environ.StartInstance come from the machine entity. But how did they get there? Series ------ Individual charms are released for different possible target series; juju should guarantee that charms for series X are only ever run on series X. Every service, unit, and machine has a series that's set at creation time and subsequently immutable. Units take their series from their service, and can only be assigned to machines with matching series. Subordinate units cannot be assigned directly to machines; they are created by their principals, on the same machine, in response to the creation of subordinate relations. We therefore restrict subordinate relations such that they can only be created between services with matching series. Constraints ----------- Constraints are stored for environments, services, units, and machines, but unit constraints are not currently exposed because they're not needed outside state, and are likely to just cause trouble and confusion if we expose them. From the point of a user, there are environment constraints and service constraints, and sensible manipulations of them lead to predictable unit deployment decisions. The mechanism is as follows: * when a unit is added, the current environment and service constraints are collapsed into a single value and stored for the unit. (To be clear: at the moment the unit is created, the current service and environment constraints will be combined such that every constraint not set on the service is taken from the environment (or left unset, if not specified at all). * when a machine is being added in order to host a given unit, it copies its constraints directly from the unit. * when a machine is being added without a unit associated -- for example, when adding additional state servers -- it copies its constraints directly from the environment. In this way the following sequence of operations becomes predictable: $ juju deploy --constraints mem=2G wordpress $ juju set-constraints --service wordpress mem=3G $ juju add-unit wordpress -n 2 ...in that exactly one machine will be provisioned with the first set of constraints, and exactly two of them will be provisioned using the second set. This is much friendlier to the users than delaying the unit constraint capture and potentially suffering subtle and annoying races. Subordinate units cannot have constraints, because their deployment is controlled by their principal units. There's only ever one machine to which that subordinate could (and must) be deployed, and to restrict that further by means of constraints will only confuse people. Machine Status and Provisioning Errors (current) ------------------------------------------------ In the light of time pressure, a unit assigned to a machine that has not been provisioned can be removed directly by calling `juju destroy-unit`. Any provisioning error can thus be "resolved" in an unsophisticated but moderately effective way: $ juju destroy-unit borken/0 ...in that at least broken units don't clutter up the service and prevent its removal. However: $ juju destroy-machine 1 ...does not yet cause an unprovisioned machine to be removed from state (whether directly, or indirectly via the provisioner; the best place to implement this functionality is not clear). Machine Status and Provisioning Errors (WIP) -------------------------------------------- [TODO: figure this out; not yet implemented, somewhat speculative... in particular, use of "resolved" may be inappropriate. Consider adding a "retry" CLI tool...] When the provisioner fails to start a machine, it should ensure that (1) the machine has no instance id set and (2) the machine has an error status set that communicates the nature of the problem. This must be visible in the output of `juju status`; and we must supply suitable tools to the user so as to allow her to respond appropriately. If the user believes a machine's provisioning error to be transient, she can do a simple `juju resolved 14` which will set some state to make machine 14 eligible for the provisioner's attention again. It may otherwise be that the unit ended up snapshotting a service/environ config pair that really isn't satsifiable. In that case, the user can try (say) `juju resolved 14 --constraints "mem=2G cpu-power=400"`, which allows her to completely replace the machine's constraints as well as marking the machine for reprovisioning attention. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/bazaar-pipelines.txt�������������������������������0000644�0000153�0000161�00000005342�12321735642�025425� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Bazaar Pipelines ================ Pipelines are implemented using a bazaar plugin. $ mkdir -p ~/.bazaar/plugins $ bzr branch lp:bzr-pipeline ~/.bazaar/plugins/pipeline Basic info for pipelines can be found using `bzr help pipeline`. Pipelines require lightweight checkouts, but that is how `cobzr` and how the recommendations are specified in the `bazaar-usage.txt` document. Why use pipelines ================= Pipelines could be thought of as a doubly linked list of dependent branches. Often when working you need to break up the implementation, either because the work can be more easily reviewed as a collection of small independent changes, or the work can be landed incrementally. Another reason is to avoid mixing new work with other refactoring that occurs during the process of writing the new work. Often when adding new features, other parts of code need to change. The branch is easier to review if the prerequisite changes happen seperately. Sometimes you don't know you want to refactor things until half of it is done already. In this situation you can create a new pipe before the current one, and move the changes into it. $ bzr add-pipe --before some-refactoring-work $ bzr merge -i :next This enters you into an interactive merge of the changes from the next branch in the pipeline. Merging trunk ============= When merging trunk into a pipeline, you should move to the first branch in the pipeline. $ bzr switch :first $ bzr merge <trunk> # resolve any conflicts that may be there $ bzr commit -m "Merge trunk" $ bzr pump The pump command is effectively merging each pipe into the next pipe and commits without changing the current active pipe. The pump command starts with the active pipe. If there are conflicts from any particular merge, the pumping stops, and the active branch is set to be the branch that had the conflicts ready for you to fix the conflicts. Useful aliases ============== $ bzr alias pipes="show-pipeline" Show the branches in the pipeline. All branches are considered a pipeline with one branch, so you can run this on any branch (actually a lightweight checkout). The current pipe is shown with an `*` at the start of the line. $ bzr alias next="switch-pipe :next" $ bzr alias prev="switch-pipe :prev" These two aliases allow you to move around the pipeline using: $ bzr next # move to the next branch in the pipeline $ bzr prev # move to the previous branch in the pipeline $ bzr alias pdiff="diff -r branch::prev" Show me the differences that this branch has introduced compared to the previous branch in the pipeline. $ bzr alias unpumped="missing --mine :next" Show the revisions that are in the current branch that are not yet in the next branch in the pipeline. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/simplestreams-metadata.txt�������������������������0000644�0000153�0000161�00000025663�12321735642�026655� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Introduction ============ When Juju bootstraps, it needs two critical pieces of information: 1. The uuid of the image to use when starting new compute instances 2. The URL from which to download the correct version of a tools tarball The necessary information is stored in a json metadata format called simplestreams. The simplestreams format is used to describe related items in a structural fashion. See the Launchpad project lp:simplestreams for more details. For supported public clouds like Amazon, HP Cloud etc, no action is required by the end user so the following information is more for those interested in what happens under the covers. Those setting up a private cloud, or who want to change how things work (eg use a different Ubuntu image), need to pay closer attention. Basic Workflow ============== Whether images or tools, Juju uses a search path to try and find suitable metadata. The path components (in order of lookup) are: 1. User supplied location (specified by tools-metadata-url or image-metadata-url config settings) 2. The environment's cloud storage 3. Provider specific locations (eg keystone endpoint if on Openstack) 4. A web location with metadata for supported public clouds (https://streams.canonical.com/juju) Metadata may be inline signed, or unsigned. We indicate a metadata file is signed by using a '.sjson' extension. Each location in the path is first searched for signed metadata, and if none is found, unsigned metadata is attempted before moving onto the next path location. Juju ships with public keys used to validate the integrity of image and tools metadata obtained from https://streams.canonical.com/juju. So out of the box, Juju will "Just Work" with any supported public cloud, using signed metadata. Setting up metadata for a private (eg Openstack) cloud requires metadata to be generated using tools which ship with Juju (more below). Image Metadata Contents ======================= Image metadata uses a simplestreams content type of "image-ids". The product id is formed as follows: "com.ubuntu.cloud:server:<series_version>:<arch>" eg "com.ubuntu.cloud:server:13.10:amd64" Non-released images (eg beta, daily etc) have product ids like: "com.ubuntu.cloud.daily:server:13.10:amd64" The metadata index and product files are required to be in the following directory tree (relative to the URL associated with each path component): <path_url> |-streams |-v1 |-index.(s)json |-product-foo.(s)json |-product-bar.(s)json The index file must be called "index.(s)json" (sjson for signed). The various product files are named according to the Path values contained in the index file. Tools Metadata Contents ======================= Tools metadata uses a simplestreams content type of "content-download". The product id is formed as follows: "com.ubuntu.juju:<series_version>:<arch>" eg "com.ubuntu.juju:12.04:amd64" The metadata index and product files are required to be in the following directory tree (relative to the URL associated with each path component). In addition, tools tarballs which Juju needs to download are also expected. <path_url> |-streams | |-v1 | |-index.(s)json | |-product-foo.(s)json | |-product-bar.(s)json | |-releases |-tools-abc.tar.gz |-tools-def.tar.gz |-tools-xyz.tar.gz The index file must be called "index.(s)json" (sjson for signed). The product file and tools tarball name(s) match whatever is in the index/product files. Configuration ============= For supported public clouds, no extra configuration is required; things work out-of-the-box. However, for testing purposes, or for non-supported cloud deployments, Juju needs to know where to find the tools and which image to run. Even for supported public clouds where all required metadata is available, the user can put their own metadata in the search path to override what is provided by the cloud. 1. User specified URLs These are initially specified in the environments.yaml file (and then subsequently copied to the jenv file when the environment is bootstrapped). For images, use "image-metadata-url"; for tools, use "tools-metadata-url". The URLs can point to a world readable container/bucket in the cloud, an address served by a http server, or even a shared directory accessible by all node instances running in the cloud. eg assume an Apache http server with base URL https://juju-metadata, providing access to information at <base>/images and <base>/tools. The Juju environment yaml file could have the following entries (one or both): tools-metadata-url: https://juju-metadata/tools image-metadata-url: https://juju-metadata/images The required files in each location is as per the directory layout described earlier. For a shared directory, use a URL of the form "file:///sharedpath". 2. Cloud storage If no matching metadata is found in the user specified URL, environment's cloud storage is searched. No user configuration is required here - all Juju environments are set up with cloud storage which is used to store state information, charms etc. Cloud storage setup is provider dependent; for Amazon and Openstack clouds, the storage is defined by the "control-bucket" value, for Azure, the "storage-account-name" value is relevant. The (optional) directory structure inside the cloud storage is as follows: <cloud storage top level> |-tools | |-streams | |-v1 | |-releases | |-images |-streams |-v1 Of course, if only custom image metadata is required, the tools directory will not be required, and vice versa. As a sidebar, note that if juju bootstrap is run with the --upload-tools option, the tools and metadata are placed according to the above structure. That's why the tools are then available for Juju to use. 3. Provider specific storage Providers may allow additional locations to search for metadata and tools. For Openstack, keystone endpoints may be created by the cloud administrator. These are defined as follows: juju-tools : the <path_url> value as described above in Tools Metadata Contents product-streams : the <path_url> value as described above in Image Metadata Contents 4. Central web location (https://streams.canonical.com/juju) This is the default location used to search for image and tools metadata and is used if no matches are found earlier in any of the above locations. No user configuration is required. Deploying Private Clouds ======================== There are two main issues when deploying a private cloud: 1. Images ids will be specific to the cloud 2. Often, outside internet access is blocked Issue 1 means that image id metadata needs to be generated and made available. Issue 2 means that tools need to be mirrored locally to make them accessible. Juju tools exist to help with generating and validating image and tools metadata. For tools, it is often easiest to just mirror https://streams.canonical.com/juju/tools. However image metadata cannot be simply mirrored because the image ids are taken from the cloud storage provider, so this need to be generated and validated using the commands described below. The available Juju metadata tools can be seen by using the help command: juju help metadata A summary of the overall workflow is (more detail next): - create a local directory in which to store image and tools metadata - generate image metadata to local directory - optionally download tools to local directory/tools Then either - juju bootstrap --metadata-source <local_directory> or - optionally, copy image metadata to somewhere in the metadata search path - optionally, mirror tools to somewhere in the metadata search path - optionally, configure tools-metadata-url and/or image-metadata-url If the bootstrap --metadata-source directory option is used, any image metadata and tools found in the specified directory will be uploaded automatically to the cloud storage for that deployment. This is useful for situations where image and tools metadata do not need to be shared amongst several users, since each Juju environment will upload its own separate copy of the required files. Using the image-metadata-url and tools-metadata-url to point to publicly accessible locations is useful when several Juju environments are to be deployed on a private cloud and the metadata should be shared. 1. Image metadata Generate image metadata using juju metadata generate-image -d <metadata_dir> As a minimum, the above command needs to know the image id to use and a directory in which to write the files. Other required parameters like region, series, architecture etc are taken from the current Juju environment (or an environment specified with the -e option). These parameters can also be overridden on the command line. The image metadata command can be run multiple times with different regions, series, architecture, and it will keep adding to the metadata files. Once all required image ids have been added, the index and product json files are ready to use. These can be uploaded to a location in the Juju metadata search path or the bootstrap --metadata-source option may be used. Examples: 1. image-metadata-url - upload contents of <metadata_dir> to http://somelocation - set image-metadata-url to http://somelocation/images 2. bootstrap option - juju bootstrap --metadata-source <metadata_dir> To ensure that the image metadata has been generated and uploaded correctly, use the validation command to ensure an image id can be discovered for a given scenario (region series, arch): juju metadata validate-images If run without parameters, the validation command will take all required details from the current Juju environment (or as specified by -e) and print the image id it would use to spin up an instance. Alternatively, series, region, architecture etc can be specified on the command line to override values in the environment config. 2. Tools metadata Generally, tools and related metadata is mirrored from https://streams.canonical.com/juju/tools. However, it is possible to manually generate metadata for a custom built tools tarball using: juju generate-tools -d <metadata_dir> where the required tools tarballs are first placed in a directory <metadata_dir>/tools/releases. Then, the contents of <metadata_dir> can be uploaded to a location in the Juju metadata search path or the bootstrap --metadata-source option may be used. Examples: 1. tools-metadata-url - upload contents of <metadata_dir> to http://somelocation - set tools-metadata-url to http://somelocation/tools 2. bootstrap option - juju bootstrap --metadata-source <tools_dir> Note that image and tools metadata are generally written into the same local directory and the bootstrap --metadata-source option will upload both types of metadata. As with image metadata, the validation command is used to ensure tools are available for Juju to use: juju metadata validate-tools The same comments apply. Run the validation tool without parameters to use details from the Juju environment, or override values as required on the command line. See juju help metadata validate-tools for more details. �����������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/doc/lifecycles.txt�������������������������������������0000644�0000153�0000161�00000032621�12321735642�024321� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Lifecycles ========== In juju, certain fundamental state entities have "lifecycles". These entities are: * Machines * Units * Services * Relations ...and there are only 3 possible states for the above things: * Alive (An entity is Alive when it is first created.) * Dying (An entity becomes Dying when the user indicates that it should be destroyed, and remains so while there are impediments to its removal.) * Dead (an entity becomes Dead when there are no further impediments to its removal; at this point it may be removed from the database at any time. Some entities may become Dead and are removed as a single operation, and are hence never directly observed to be "Dead", but should still be so considered.) There are two fundamental truths in this system: * All such things start existence Alive. * No such thing can ever change to an earlier state. Beyond the above rules, lifecycle shifts occur at different times for different kinds of entities. Machines -------- * Like everything else, a machine starts out Alive. `juju bootstrap` aside, the user interface does not allow for direct creation of machines, but `juju deploy` and `juju add-unit` may create machines as a consequence of unit creation. * If a machine has the JobManageEnviron job, it cannot become Dying or Dead. Other jobs do not affect the lifecycle directly. * If a machine has the JobHostUnits job, principal units can be assigned to it while it is Alive. * While principal units are assigned to a machine, its lifecycle cannot change and `juju destroy-machine` will fail. * When no principal units are assigned, `juju destroy-machine` will set the machine to Dying. (Future plans: allow a machine to become Dying when it has principal units, so long as they are not Alive. For now it's extra complexity with little direct benefit.) * Once a machine has been set to Dying, the corresponding Machine Agent (MA) is responsible for setting it to Dead. (Future plans: when Dying units are assigned, wait for them to become Dead and remove them completely before making the machine Dead; not an issue now because the machine can't yet become Dying with units assigned.) * Once a machine has been set to Dead, the agent for some other machine (with JobManageEnviron) will release the underlying instance back to the provider and remove the machine entity from state. (Future uncertainty: should the provisioner provision an instance for a Dying machine? At the moment, no, because a Dying machine can't have any units in the first place; in the future, er, maybe, because those Dying units may be attached to persistent storage and should thus be allowed to continue to shut down cleanly as they would usually do. Maybe.) Units ----- * A principal unit can be created directly with `juju deploy` or `juju add-unit`. * While a principal unit is Alive, it can be assigned to a machine. * While a principal unit is Alive, it can enter the scopes of Alive relations, which may cause the creation of subordinate units; so, indirectly, `juju add-relation` can also cause the creation of units. * A unit can become Dying at any time, but may not become Dead while any unit subordinate to it exists, or while the unit is in scope for any relation. * A principal unit can become Dying in one of two ways: * `juju destroy-unit` (This doesn't work on subordinates; see below.) * `juju destroy-service` (This does work on subordinates, but happens indirectly in either case: the Unit Agents (UAs) for each unit of a service set their corresponding units to Dying when they detect their service Dying; this is because we try to assume 100k-scale and we can't use mgo/txn to do a bulk update of 100k units: that makes for a txn with at least 100k operations, and that's just crazy.) * A subordinate must also become Dying when either: * its principal becomes Dying, via `juju destroy-unit`; or * the last Alive relation between its service and its principal's service is no longer Alive. This may come about via `juju destroy-relation`. * When any unit is Dying, its UA is responsible for removing impediments to the unit becoming Dead, and then making it so. To do so, the UA must: * Depart from all its relations in an orderly fashion. * Wait for all its subordinates to become Dead, and remove them from state. * Set its unit to Dead. * As just noted, when a subordinate unit is Dead, it is removed from state by its principal's UA; the relationship is the same as that of a principal unit to its assigned machine agent, and of a machine to the JobManageEnviron machine agent. Services -------- * Services are created with `juju deploy`. Services with duplicate names are not allowed (units and machine with duplicate names are not possible: their identifiers are assigned by juju). * Unlike units and machines, services have no corresponding agent. * In addition, services become Dead and are removed from the database in a single atomic operation. * When a service is Alive, units may be added to it, and relations can be added using the service's endpoints. * A service can be destroyed at any time, via `juju destroy-service`. This causes all the units to become Dying, as discussed above, and will also cause all relations in which the service is participating to become Dying or be removed. * If a destroyed service has no units, and all its relations are eligible for immediate removal, then the service will also be removed immediately rather than being set to Dying. * If no associated relations exist, the service is removed by the MA which removes the last unit of that service from state. * If no units of the service remain, but its relations still exist, the responsibility for removing the service falls to the last UA to leave scope for that relation. (Yes, this is a UA for a unit of a totally different service.) Relations --------- * A relation is created with `juju add-relation`. No two relations with the same canonical name can exist. (The canonical relation name form is "<requirer-endpoint> <provider-endpoint>", where each endpoint takes the form "<service-name>:<charm-relation-name>".) * Thanks to convention, the above is not strictly true: it is possible for a subordinate charm to require a container-scoped "juju-info" relation. These restrictions mean that the name can never cause actual ambiguity; nonetheless, support should be phased out smoothly (see lp:1100076). * A relation, like a service, has no corresponding agent; and becomes Dead and is removed from the database in a single operation. * Similarly to a service, a relation cannot be created while an identical relation exists in state (in which identity is determined by equality of canonical relation name -- a sequence of endpoint pairs sorted by role). * While a relation is Alive, units of services in that relation can enter its scope; that is, the UAs for those units can signal to the system that they are participating in the relation. * A relation can be destroyed with either `juju destroy-relation` or `juju destroy-service`. * When a relation is destroyed with no units in scope, it will immediately become Dead and be removed from state, rather than being set to Dying. * When a relation becomes Dying, the UAs of units that have entered its scope are responsible for cleanly departing the relation by running hooks and then leaving relation scope (signalling that they are no longer participating). * When the last unit leaves the scope of a Dying relation, it must remove the relation from state. * As noted above, the Dying relation may be the only thing keeping a Dying service (different to that of the acting UA) from removal; so, relation removal may also imply service removal. References ---------- OK, that was a bit of a hail of bullets, and the motivations for the above are perhaps not always clear. To consider it from another angle: * Subordinate units reference principal units. * Principal units reference machines. * All units reference their services. * All units reference the relations whose scopes they have joined. * All relations reference the services they are part of. In every case above, where X references Y, the life state of an X may be sufficient to prevent a change in the life state of a Y; and, conversely, a life change in an X may be sufficient to cause a life change in a Y. (In only one case does the reverse hold -- that is, setting a service or relation to Dying will cause appropriate units' agents to individually set their units to Dying -- and this is just an implementation detail.) The following scrawl may help you to visualize the references in play: +-----------+ +---------+ +-->| principal |------>| machine | | +-----------+ +---------+ | | | | | +--------------+ | | | | V V | +----------+ +---------+ | | relation |------>| service | | +----------+ +---------+ | A A | | | | | +--------------+ | | | | +-------------+ +---| subordinate | +-------------+ ...but is important to remember that it's only one view of the relationships involved, and that the user-centric view is quite different; from a user's perspective the influences appear to travel in the opposite direction: * (destroying a machine "would" destroy its principals but that's disallowed) * destroying a principal destroys all its subordinates * (destroying a subordinate directly is impossible) * destroying a service destroys all its units and relations * destroying a container relation destroys all subordinates in the relation * (destroying a global relation destroys nothing else) ...and it takes a combination of these viewpoints to understand the detailed interactions laid out above. Agents ------ It may also be instructive to consider the responsibilities of the unit and machine agents. The unit agent is responsible for: * detecting Alive relations incorporating its service and entering their scopes (if a principal, this may involve creating subordinates). * detecting Dying relations whose scope it has entered and leaving their scope (this involves removing any relations or services that thereby become unreferenced). * detecting undeployed Alive subordinates and deploying them. * detecting undeployed non-Alive subordinates and removing them (this raises similar questions to those alluded to above re Dying units on Dying machines: but, without persistent storage, there's no point deploying a Dying unit just to wait for its agent to set itself to Dead). * detecting deployed Dead subordinates, recalling them, and removing them. * detecting its service's Dying state, and setting its own Dying state. * if a subordinate, detecting that no relations with its principal are Alive, and setting its own Dying state. * detecting its own Dying state, and: * leaving all its relation scopes; * waiting for all its subordinates to be removed; * setting its own Dead state. A machine agent's responsibilities are determined by its jobs. There are only two jobs in existence at the moment; an MA whose machine has JobHostUnits is responsible for: * detecting undeployed Alive principals assigned to it and deploying them. * detecting undeployed non-Alive principals assigned to it and removing them (recall that unit removal may imply service removal). * detecting deployed Dead principals assigned to it, recalling them, and removing them. * detecting deployed principals not assigned to it, and recalling them. * detecting its machine's Dying state, and setting it to Dead. ...while one whose machine has JobManageEnviron is responsible for: * detecting Alive machines without instance IDs and provisioning provider instances to run their agents. * detecting non-Alive machines without instance IDs and removing them. * detecting Dead machines with instance IDs, decommissioning the instance, and removing the machine. Machines can in theory have multiple jobs, but in current practice do not. Implementation -------------- All state change operations are mediated by the mgo/txn package, which provides multi-document transactions aginst MongoDB. This allows us to enforce the many conditions described above without experiencing races, so long as we are mindful when implementing them. Lifecycle support is not complete: relation lifecycles are, mostly, as are large parts of the unit and machine agent; but substantial parts of the machine, unit and service entity implementation still lack sophistication. This situation is being actively addressed. Beyond the plans detailed above, it is important to note that an agent that is failing to meet its responsibilities can have a somewhat distressing impact on the rest of the system. To counteract this, we intend to implement a --force flag to destroy-unit (and destroy-machine?) that forcibly sets an entity to Dead while maintaining consistency and sanity across all references. The best approach to this problem has yet to be agreed; we're not short of options, but none are exceptionally compelling. ���������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/���������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�021415� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/filevar.go�����������������������������������������0000644�0000153�0000161�00000001233�12321735642�023406� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd import ( "errors" "io/ioutil" ) // FileVar represents a path to a file. type FileVar struct { Path string } var ErrNoPath = errors.New("path not set") // Set stores the chosen path name in f.Path. func (f *FileVar) Set(v string) error { f.Path = v return nil } // Read returns the contents of the file. func (f *FileVar) Read(ctx *Context) ([]byte, error) { if f.Path == "" { return nil, ErrNoPath } return ioutil.ReadFile(ctx.AbsPath(f.Path)) } // String returns the path to the file. func (f *FileVar) String() string { return f.Path } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/util_test.go���������������������������������������0000644�0000153�0000161�00000003131�12321735642�023771� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd_test import ( "bytes" "errors" "fmt" "io" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) func bufferString(stream io.Writer) string { return stream.(*bytes.Buffer).String() } // TestCommand is used by several different tests. type TestCommand struct { cmd.CommandBase Name string Option string Minimal bool Aliases []string } func (c *TestCommand) Info() *cmd.Info { if c.Minimal { return &cmd.Info{Name: c.Name} } return &cmd.Info{ Name: c.Name, Args: "<something>", Purpose: c.Name + " the juju", Doc: c.Name + "-doc", Aliases: c.Aliases, } } func (c *TestCommand) SetFlags(f *gnuflag.FlagSet) { if !c.Minimal { f.StringVar(&c.Option, "option", "", "option-doc") } } func (c *TestCommand) Init(args []string) error { return cmd.CheckEmpty(args) } func (c *TestCommand) Run(ctx *cmd.Context) error { switch c.Option { case "error": return errors.New("BAM!") case "silent-error": return cmd.ErrSilent case "echo": _, err := io.Copy(ctx.Stdout, ctx.Stdin) return err default: fmt.Fprintln(ctx.Stdout, c.Option) } return nil } // minimalHelp and fullHelp are the expected help strings for a TestCommand // with Name "verb", with and without Minimal set. var minimalHelp = "usage: verb\n" var optionHelp = `usage: verb [options] <something> purpose: verb the juju options: --option (= "") option-doc ` var fullHelp = `usage: verb [options] <something> purpose: verb the juju options: --option (= "") option-doc verb-doc ` ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/output.go������������������������������������������0000644�0000153�0000161�00000010756�12321735642�023330� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd import ( "encoding/json" "fmt" "io" "os" "reflect" "sort" "strings" "launchpad.net/gnuflag" "launchpad.net/goyaml" ) // Formatter converts an arbitrary object into a []byte. type Formatter func(value interface{}) ([]byte, error) // FormatYaml marshals value to a yaml-formatted []byte, unless value is nil. func FormatYaml(value interface{}) ([]byte, error) { if value == nil { return nil, nil } result, err := goyaml.Marshal(value) if err != nil { return nil, err } for i := len(result) - 1; i > 0; i-- { if result[i] != '\n' { break } result = result[:i] } return result, nil } // FormatJson marshals value to a json-formatted []byte. var FormatJson = json.Marshal // FormatSmart marshals value into a []byte according to the following rules: // * string: untouched // * bool: converted to `True` or `False` (to match pyjuju) // * int or float: converted to sensible strings // * []string: joined by `\n`s into a single string // * anything else: delegate to FormatYaml func FormatSmart(value interface{}) ([]byte, error) { if value == nil { return nil, nil } v := reflect.ValueOf(value) switch kind := v.Kind(); kind { case reflect.String: return []byte(value.(string)), nil case reflect.Array, reflect.Slice: if v.Type().Elem().Kind() == reflect.String { return []byte(strings.Join(value.([]string), "\n")), nil } case reflect.Bool: if value.(bool) { return []byte("True"), nil } return []byte("False"), nil case reflect.Map, reflect.Float32, reflect.Float64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: default: return nil, fmt.Errorf("cannot marshal %#v", value) } return FormatYaml(value) } // DefaultFormatters holds the formatters that can be // specified with the --format flag. var DefaultFormatters = map[string]Formatter{ "smart": FormatSmart, "yaml": FormatYaml, "json": FormatJson, } // formatterValue implements gnuflag.Value for the --format flag. type formatterValue struct { name string formatters map[string]Formatter } // newFormatterValue returns a new formatterValue. The initial Formatter name // must be present in formatters. func newFormatterValue(initial string, formatters map[string]Formatter) *formatterValue { v := &formatterValue{formatters: formatters} if err := v.Set(initial); err != nil { panic(err) } return v } // Set stores the chosen formatter name in v.name. func (v *formatterValue) Set(value string) error { if v.formatters[value] == nil { return fmt.Errorf("unknown format %q", value) } v.name = value return nil } // String returns the chosen formatter name. func (v *formatterValue) String() string { return v.name } // doc returns documentation for the --format flag. func (v *formatterValue) doc() string { choices := make([]string, len(v.formatters)) i := 0 for name := range v.formatters { choices[i] = name i++ } sort.Strings(choices) return "specify output format (" + strings.Join(choices, "|") + ")" } // format runs the chosen formatter on value. func (v *formatterValue) format(value interface{}) ([]byte, error) { return v.formatters[v.name](value) } // Output is responsible for interpreting output-related command line flags // and writing a value to a file or to stdout as directed. type Output struct { formatter *formatterValue outPath string } // AddFlags injects the --format and --output command line flags into f. func (c *Output) AddFlags(f *gnuflag.FlagSet, defaultFormatter string, formatters map[string]Formatter) { c.formatter = newFormatterValue(defaultFormatter, formatters) f.Var(c.formatter, "format", c.formatter.doc()) f.StringVar(&c.outPath, "o", "", "specify an output file") f.StringVar(&c.outPath, "output", "", "") } // Write formats and outputs the value as directed by the --format and // --output command line flags. func (c *Output) Write(ctx *Context, value interface{}) (err error) { var target io.Writer if c.outPath == "" { target = ctx.Stdout } else { path := ctx.AbsPath(c.outPath) if target, err = os.Create(path); err != nil { return } } bytes, err := c.formatter.format(value) if err != nil { return } if len(bytes) > 0 { _, err = target.Write(bytes) if err == nil { _, err = target.Write([]byte{'\n'}) } } return } func (c *Output) Name() string { return c.formatter.name } ������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/names_test.go��������������������������������������0000644�0000153�0000161�00000002121�12321735642�024115� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" ) type namesSuite struct { } var _ = gc.Suite(&namesSuite{}) func (*namesSuite) TestNameChecks(c *gc.C) { assertMachineOrNewContainer := func(s string, expect bool) { c.Assert(cmd.IsMachineOrNewContainer(s), gc.Equals, expect) } assertMachineOrNewContainer("0", true) assertMachineOrNewContainer("00", false) assertMachineOrNewContainer("1", true) assertMachineOrNewContainer("0/lxc/0", true) assertMachineOrNewContainer("lxc:0", true) assertMachineOrNewContainer("lxc:lxc:0", false) assertMachineOrNewContainer("kvm:0/lxc/1", true) assertMachineOrNewContainer("lxc:", false) assertMachineOrNewContainer(":lxc", false) assertMachineOrNewContainer("0/lxc/", false) assertMachineOrNewContainer("0/lxc", false) assertMachineOrNewContainer("kvm:0/lxc", false) assertMachineOrNewContainer("0/lxc/01", false) assertMachineOrNewContainer("0/lxc/10", true) assertMachineOrNewContainer("0/kvm/4", true) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/export_test.go�������������������������������������0000644�0000153�0000161�00000000351�12321735776�024346� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd var ( GetDefaultEnvironment = getDefaultEnvironment GetCurrentEnvironmentFilePath = getCurrentEnvironmentFilePath ) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charmload/�����������������������������������������0000755�0000153�0000161�00000000000�12321735642�023362� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charmload/config.yaml������������������������������0000644�0000153�0000161�00000000033�12321735642�025507� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mongo-url: localhost:60017 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charmload/main.go����������������������������������0000644�0000153�0000161�00000002003�12321735642�024630� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "path/filepath" "launchpad.net/lpad" "launchpad.net/juju-core/store" ) func main() { err := load() if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } } func load() error { var confPath string if len(os.Args) == 2 { if _, err := os.Stat(os.Args[1]); err == nil { confPath = os.Args[1] } } if confPath == "" { return fmt.Errorf("usage: %s <config path>", filepath.Base(os.Args[0])) } conf, err := store.ReadConfig(confPath) if err != nil { return err } if conf.MongoURL == "" { return fmt.Errorf("missing mongo-url in config file") } s, err := store.Open(conf.MongoURL) if err != nil { return err } defer s.Close() err = store.PublishCharmsDistro(s, lpad.Production) if _, ok := err.(store.PublishBranchErrors); ok { // Ignore branch errors since they're commonplace here. // They're logged, though. return nil } return err } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/����������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022372� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/destroyenvironment.go�������������������������0000644�0000153�0000161�00000007742�12321735642�026724� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bufio" "errors" "fmt" "io" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" ) var NoEnvironmentError = errors.New("no environment specified") var DoubleEnvironmentError = errors.New("you cannot supply both -e and the envname as a positional argument") // DestroyEnvironmentCommand destroys an environment. type DestroyEnvironmentCommand struct { cmd.CommandBase envName string assumeYes bool force bool } func (c *DestroyEnvironmentCommand) Info() *cmd.Info { return &cmd.Info{ Name: "destroy-environment", Args: "<environment name>", Purpose: "terminate all machines and other associated resources for an environment", } } func (c *DestroyEnvironmentCommand) SetFlags(f *gnuflag.FlagSet) { f.BoolVar(&c.assumeYes, "y", false, "Do not ask for confirmation") f.BoolVar(&c.assumeYes, "yes", false, "") f.BoolVar(&c.force, "force", false, "Forcefully destroy the environment, directly through the environment provider") f.StringVar(&c.envName, "e", "", "juju environment to operate in") f.StringVar(&c.envName, "environment", "", "juju environment to operate in") } func (c *DestroyEnvironmentCommand) Run(ctx *cmd.Context) (result error) { store, err := configstore.Default() if err != nil { return fmt.Errorf("cannot open environment info storage: %v", err) } environ, err := environs.NewFromName(c.envName, store) if err != nil { if environs.IsEmptyConfig(err) { // Delete the .jenv file and call it done. ctx.Infof("removing empty environment file") return environs.DestroyInfo(c.envName, store) } return err } if !c.assumeYes { fmt.Fprintf(ctx.Stdout, destroyEnvMsg, environ.Name(), environ.Config().Type()) scanner := bufio.NewScanner(ctx.Stdin) scanner.Scan() err := scanner.Err() if err != nil && err != io.EOF { return fmt.Errorf("Environment destruction aborted: %s", err) } answer := strings.ToLower(scanner.Text()) if answer != "y" && answer != "yes" { return errors.New("environment destruction aborted") } } // If --force is supplied, then don't attempt to use the API. // This is necessary to destroy broken environments, where the // API server is inaccessible or faulty. if !c.force { defer func() { if result == nil { return } logger.Errorf(`failed to destroy environment %q If the environment is unusable, then you may run juju destroy-environment --force to forcefully destroy the environment. Upon doing so, review your environment provider console for any resources that need to be cleaned up. `, c.envName) }() conn, err := juju.NewAPIConn(environ, api.DefaultDialOpts()) if err != nil { return err } defer conn.Close() err = conn.State.Client().DestroyEnvironment() if err != nil && !params.IsCodeNotImplemented(err) { return fmt.Errorf("destroying environment: %v", err) } } return environs.Destroy(environ, store) } func (c *DestroyEnvironmentCommand) Init(args []string) error { if c.envName != "" { logger.Warningf("-e/--environment flag is deprecated in 1.18, " + "please supply environment as a positional parameter") // They supplied the -e flag if len(args) == 0 { // We're happy, we have enough information return nil } // You can't supply -e ENV and ENV as a positional argument return DoubleEnvironmentError } else { // No -e flag means they must supply the environment positionally switch len(args) { case 0: return NoEnvironmentError case 1: c.envName = args[0] return nil default: return cmd.CheckEmpty(args[1:]) } } } var destroyEnvMsg = ` WARNING! this command will destroy the %q environment (type: %s) This includes all machines, services, data and other resources. Continue [y/N]? `[1:] ������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/debughooks.go���������������������������������0000644�0000153�0000161�00000007724�12321735776�025110� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "encoding/base64" "errors" "fmt" "sort" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" unitdebug "launchpad.net/juju-core/worker/uniter/debug" ) // DebugHooksCommand is responsible for launching a ssh shell on a given unit or machine. type DebugHooksCommand struct { SSHCommand hooks []string } const debugHooksDoc = ` Interactively debug a hook remotely on a service unit. ` func (c *DebugHooksCommand) Info() *cmd.Info { return &cmd.Info{ Name: "debug-hooks", Args: "<unit name> [hook names]", Purpose: "launch a tmux session to debug a hook", Doc: debugHooksDoc, } } func (c *DebugHooksCommand) Init(args []string) error { if len(args) < 1 { return errors.New("no unit name specified") } c.Target = args[0] if !names.IsUnit(c.Target) { return fmt.Errorf("%q is not a valid unit name", c.Target) } // If any of the hooks is "*", then debug all hooks. c.hooks = append([]string{}, args[1:]...) for _, h := range c.hooks { if h == "*" { c.hooks = nil break } } return nil } // getRelationNames1dot16 gets the list of relation hooks directly from the // database, in a fashion compatible with the API server in Juju 1.16 (which // doesn't have the ServiceCharmRelations API). This function can be removed // when we no longer maintain compatibility with 1.16 func (c *DebugHooksCommand) getRelationNames1dot16() ([]string, error) { err := c.ensureRawConn() if err != nil { return nil, err } unit, err := c.rawConn.State.Unit(c.Target) if err != nil { return nil, err } service, err := unit.Service() if err != nil { return nil, err } endpoints, err := service.Endpoints() if err != nil { return nil, err } relations := make([]string, len(endpoints)) for i, endpoint := range endpoints { relations[i] = endpoint.Relation.Name } return relations, nil } func (c *DebugHooksCommand) getRelationNames(serviceName string) ([]string, error) { relations, err := c.apiClient.ServiceCharmRelations(serviceName) if params.IsCodeNotImplemented(err) { logger.Infof("API server does not support Client.ServiceCharmRelations falling back to 1.16 compatibility mode (direct DB access)") return c.getRelationNames1dot16() } if err != nil { return nil, err } return relations, err } func (c *DebugHooksCommand) validateHooks() error { if len(c.hooks) == 0 { return nil } service := names.UnitService(c.Target) relations, err := c.getRelationNames(service) if err != nil { return err } validHooks := make(map[string]bool) for _, hook := range hooks.UnitHooks() { validHooks[string(hook)] = true } for _, relation := range relations { for _, hook := range hooks.RelationHooks() { hook := fmt.Sprintf("%s-%s", relation, hook) validHooks[hook] = true } } for _, hook := range c.hooks { if !validHooks[hook] { names := make([]string, 0, len(validHooks)) for hookName, _ := range validHooks { names = append(names, hookName) } sort.Strings(names) logger.Infof("unknown hook %s, valid hook names: %v", hook, names) return fmt.Errorf("unit %q does not contain hook %q", c.Target, hook) } } return nil } // Run ensures c.Target is a unit, and resolves its address, // and connects to it via SSH to execute the debug-hooks // script. func (c *DebugHooksCommand) Run(ctx *cmd.Context) error { var err error c.apiClient, err = c.initAPIClient() if err != nil { return err } defer c.apiClient.Close() err = c.validateHooks() if err != nil { return err } debugctx := unitdebug.NewHooksContext(c.Target) script := base64.StdEncoding.EncodeToString([]byte(unitdebug.ClientScript(debugctx, c.hooks))) innercmd := fmt.Sprintf(`F=$(mktemp); echo %s | base64 -d > $F; . $F`, script) args := []string{fmt.Sprintf("sudo /bin/bash -c '%s'", innercmd)} c.Args = args return c.SSHCommand.Run(ctx) } ��������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/authorisedkeys_import.go����������������������0000644�0000153�0000161�00000003063�12321735776�027403� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" ) var importKeysDoc = ` Import new authorised ssh keys to allow the holder of those keys to log on to Juju nodes or machines. The keys are imported using ssh-import-id. ` // ImportKeysCommand is used to add new authorized ssh keys for a user. type ImportKeysCommand struct { cmd.EnvCommandBase user string sshKeyIds []string } func (c *ImportKeysCommand) Info() *cmd.Info { return &cmd.Info{ Name: "import", Args: "<ssh key id> [...]", Doc: importKeysDoc, Purpose: "using ssh-import-id, import new authorized ssh keys for a Juju user", } } func (c *ImportKeysCommand) Init(args []string) error { switch len(args) { case 0: return errors.New("no ssh key id specified") default: c.sshKeyIds = args } return nil } func (c *ImportKeysCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.StringVar(&c.user, "user", "admin", "the user for which to import the keys") } func (c *ImportKeysCommand) Run(context *cmd.Context) error { client, err := juju.NewKeyManagerClient(c.EnvName) if err != nil { return err } defer client.Close() results, err := client.ImportKeys(c.user, c.sshKeyIds...) if err != nil { return err } for i, result := range results { if result.Error != nil { fmt.Fprintf(context.Stderr, "cannot import key id %q: %v\n", c.sshKeyIds[i], result.Error) } } return nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/authorisedkeys_add.go�������������������������0000644�0000153�0000161�00000002667�12321735776�026632� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" ) var addKeysDoc = ` Add new authorised ssh keys to allow the holder of those keys to log on to Juju nodes or machines. ` // AddKeysCommand is used to add a new authorized ssh key for a user. type AddKeysCommand struct { cmd.EnvCommandBase user string sshKeys []string } func (c *AddKeysCommand) Info() *cmd.Info { return &cmd.Info{ Name: "add", Args: "<ssh key> [...]", Doc: addKeysDoc, Purpose: "add new authorized ssh keys for a Juju user", } } func (c *AddKeysCommand) Init(args []string) error { switch len(args) { case 0: return errors.New("no ssh key specified") default: c.sshKeys = args } return nil } func (c *AddKeysCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.StringVar(&c.user, "user", "admin", "the user for which to add the keys") } func (c *AddKeysCommand) Run(context *cmd.Context) error { client, err := juju.NewKeyManagerClient(c.EnvName) if err != nil { return err } defer client.Close() results, err := client.AddKeys(c.user, c.sshKeys...) if err != nil { return err } for i, result := range results { if result.Error != nil { fmt.Fprintf(context.Stderr, "cannot add key %q: %v\n", c.sshKeys[i], result.Error) } } return nil } �������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/addrelation_test.go���������������������������0000644�0000153�0000161�00000007516�12321735642�026272� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/testing" ) type AddRelationSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&AddRelationSuite{}) func runAddRelation(c *gc.C, args ...string) error { _, err := testing.RunCommand(c, &AddRelationCommand{}, args) return err } var msWpAlreadyExists = `cannot add relation "wp:db ms:server": relation already exists` var msLgAlreadyExists = `cannot add relation "lg:info ms:juju-info": relation already exists` var wpLgAlreadyExists = `cannot add relation "lg:logging-directory wp:logging-dir": relation already exists` var wpLgAlreadyExistsJuju = `cannot add relation "lg:info wp:juju-info": relation already exists` var addRelationTests = []struct { args []string err string }{ { args: []string{"rk", "ms"}, err: "no relations found", }, { err: "a relation must involve two services", }, { args: []string{"rk"}, err: "a relation must involve two services", }, { args: []string{"rk:ring"}, err: "a relation must involve two services", }, { args: []string{"ping:pong", "tic:tac", "icki:wacki"}, err: "a relation must involve two services", }, // Add a real relation, and check various ways of failing to re-add it. { args: []string{"ms", "wp"}, }, { args: []string{"ms", "wp"}, err: msWpAlreadyExists, }, { args: []string{"wp", "ms"}, err: msWpAlreadyExists, }, { args: []string{"ms", "wp:db"}, err: msWpAlreadyExists, }, { args: []string{"ms:server", "wp"}, err: msWpAlreadyExists, }, { args: []string{"ms:server", "wp:db"}, err: msWpAlreadyExists, }, // Add a real relation using an implicit endpoint. { args: []string{"ms", "lg"}, }, { args: []string{"ms", "lg"}, err: msLgAlreadyExists, }, { args: []string{"lg", "ms"}, err: msLgAlreadyExists, }, { args: []string{"ms:juju-info", "lg"}, err: msLgAlreadyExists, }, { args: []string{"ms", "lg:info"}, err: msLgAlreadyExists, }, { args: []string{"ms:juju-info", "lg:info"}, err: msLgAlreadyExists, }, // Add a real relation using an explicit endpoint, avoiding the potential implicit one. { args: []string{"wp", "lg"}, }, { args: []string{"wp", "lg"}, err: wpLgAlreadyExists, }, { args: []string{"lg", "wp"}, err: wpLgAlreadyExists, }, { args: []string{"wp:logging-dir", "lg"}, err: wpLgAlreadyExists, }, { args: []string{"wp", "lg:logging-directory"}, err: wpLgAlreadyExists, }, { args: []string{"wp:logging-dir", "lg:logging-directory"}, err: wpLgAlreadyExists, }, // Check we can still use the implicit endpoint if specified explicitly. { args: []string{"wp:juju-info", "lg"}, }, { args: []string{"wp:juju-info", "lg"}, err: wpLgAlreadyExistsJuju, }, { args: []string{"lg", "wp:juju-info"}, err: wpLgAlreadyExistsJuju, }, { args: []string{"wp:juju-info", "lg"}, err: wpLgAlreadyExistsJuju, }, { args: []string{"wp", "lg:info"}, err: wpLgAlreadyExistsJuju, }, { args: []string{"wp:juju-info", "lg:info"}, err: wpLgAlreadyExistsJuju, }, } func (s *AddRelationSuite) TestAddRelation(c *gc.C) { testing.Charms.BundlePath(s.SeriesPath, "wordpress") err := runDeploy(c, "local:wordpress", "wp") c.Assert(err, gc.IsNil) testing.Charms.BundlePath(s.SeriesPath, "mysql") err = runDeploy(c, "local:mysql", "ms") c.Assert(err, gc.IsNil) testing.Charms.BundlePath(s.SeriesPath, "riak") err = runDeploy(c, "local:riak", "rk") c.Assert(err, gc.IsNil) testing.Charms.BundlePath(s.SeriesPath, "logging") err = runDeploy(c, "local:logging", "lg") c.Assert(err, gc.IsNil) for i, t := range addRelationTests { c.Logf("test %d: %v", i, t.args) err := runAddRelation(c, t.args...) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) } } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/retryprovisioning.go��������������������������0000644�0000153�0000161�00000002614�12321735776�026563� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" ) // RetryProvisioningCommand updates machines' error status to tell // the provisoner that it should try to re-provision the machine. type RetryProvisioningCommand struct { cmd.EnvCommandBase Machines []string } func (c *RetryProvisioningCommand) Info() *cmd.Info { return &cmd.Info{ Name: "retry-provisioning", Args: "<machine> [...]", Purpose: "retries provisioning for failed machines", } } func (c *RetryProvisioningCommand) Init(args []string) error { if len(args) == 0 { return fmt.Errorf("no machine specified") } c.Machines = make([]string, len(args)) for i, arg := range args { if !names.IsMachine(arg) { return fmt.Errorf("invalid machine %q", arg) } c.Machines[i] = names.MachineTag(arg) } return nil } func (c *RetryProvisioningCommand) Run(context *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() results, err := client.RetryProvisioning(c.Machines...) if err != nil { return err } for i, result := range results { if result.Error != nil { fmt.Fprintf(context.Stderr, "cannot retry provisioning %q: %v\n", c.Machines[i], result.Error) } } return nil } ��������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/endpoint_test.go������������������������������0000644�0000153�0000161�00000001354�12321735642�025616� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" ) type EndpointSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&EndpointSuite{}) func (s *EndpointSuite) TestEndpoint(c *gc.C) { ctx := coretesting.Context(c) code := cmd.Main(&EndpointCommand{}, ctx, []string{}) c.Check(code, gc.Equals, 0) c.Assert(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "") output := string(ctx.Stdout.(*bytes.Buffer).Bytes()) info := s.APIInfo(c) c.Assert(output, gc.Equals, fmt.Sprintf("%s\n", info.Addrs[0])) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/addunit_test.go�������������������������������0000644�0000153�0000161�00000010131�12321735642�025417� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/instance" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/testing" ) type AddUnitSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&AddUnitSuite{}) var initAddUnitErrorTests = []struct { args []string err string }{ { args: []string{"some-service-name", "-n", "0"}, err: `--num-units must be a positive integer`, }, { args: []string{"some-service-name", "--to", "bigglesplop"}, err: `invalid --to parameter "bigglesplop"`, }, { args: []string{"some-service-name", "-n", "2", "--to", "123"}, err: `cannot use --num-units > 1 with --to`, }, } func (s *AddUnitSuite) TestInitErrors(c *gc.C) { for i, t := range initAddUnitErrorTests { c.Logf("test %d", i) err := testing.InitCommand(&AddUnitCommand{}, t.args) c.Check(err, gc.ErrorMatches, t.err) } } func runAddUnit(c *gc.C, args ...string) error { _, err := testing.RunCommand(c, &AddUnitCommand{}, args) return err } func (s *AddUnitSuite) setupService(c *gc.C) *charm.URL { testing.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "some-service-name") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "some-service-name", curl, 1, 0) return curl } func (s *AddUnitSuite) TestAddUnit(c *gc.C) { curl := s.setupService(c) err := runAddUnit(c, "some-service-name") c.Assert(err, gc.IsNil) s.AssertService(c, "some-service-name", curl, 2, 0) err = runAddUnit(c, "--num-units", "2", "some-service-name") c.Assert(err, gc.IsNil) s.AssertService(c, "some-service-name", curl, 4, 0) } // assertForceMachine ensures that the result of assigning a unit with --to // is as expected. func (s *AddUnitSuite) assertForceMachine(c *gc.C, svc *state.Service, expectedNumMachines, unitNum int, machineId string) { units, err := svc.AllUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, expectedNumMachines) mid, err := units[unitNum].AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, machineId) } func (s *AddUnitSuite) TestForceMachine(c *gc.C) { curl := s.setupService(c) machine, err := s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, gc.IsNil) machine2, err := s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, gc.IsNil) err = runAddUnit(c, "some-service-name", "--to", machine2.Id()) c.Assert(err, gc.IsNil) err = runAddUnit(c, "some-service-name", "--to", machine.Id()) c.Assert(err, gc.IsNil) svc, _ := s.AssertService(c, "some-service-name", curl, 3, 0) s.assertForceMachine(c, svc, 3, 1, machine2.Id()) s.assertForceMachine(c, svc, 3, 2, machine.Id()) } func (s *AddUnitSuite) TestForceMachineExistingContainer(c *gc.C) { curl := s.setupService(c) machine, err := s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, gc.IsNil) template := state.MachineTemplate{ Series: "precise", Jobs: []state.MachineJob{state.JobHostUnits}, } container, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) err = runAddUnit(c, "some-service-name", "--to", container.Id()) c.Assert(err, gc.IsNil) err = runAddUnit(c, "some-service-name", "--to", machine.Id()) c.Assert(err, gc.IsNil) svc, _ := s.AssertService(c, "some-service-name", curl, 3, 0) s.assertForceMachine(c, svc, 3, 1, container.Id()) s.assertForceMachine(c, svc, 3, 2, machine.Id()) } func (s *AddUnitSuite) TestForceMachineNewContainer(c *gc.C) { curl := s.setupService(c) machine, err := s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, gc.IsNil) err = runAddUnit(c, "some-service-name", "--to", "lxc:"+machine.Id()) c.Assert(err, gc.IsNil) err = runAddUnit(c, "some-service-name", "--to", machine.Id()) c.Assert(err, gc.IsNil) svc, _ := s.AssertService(c, "some-service-name", curl, 3, 0) s.assertForceMachine(c, svc, 3, 1, machine.Id()+"/lxc/0") s.assertForceMachine(c, svc, 3, 2, machine.Id()) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/resolved.go�����������������������������������0000644�0000153�0000161�00000002357�12321735776�024576� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" ) // ResolvedCommand marks a unit in an error state as ready to continue. type ResolvedCommand struct { cmd.EnvCommandBase UnitName string Retry bool } func (c *ResolvedCommand) Info() *cmd.Info { return &cmd.Info{ Name: "resolved", Args: "<unit>", Purpose: "marks unit errors resolved", } } func (c *ResolvedCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.BoolVar(&c.Retry, "r", false, "re-execute failed hooks") f.BoolVar(&c.Retry, "retry", false, "") } func (c *ResolvedCommand) Init(args []string) error { if len(args) > 0 { c.UnitName = args[0] if !names.IsUnit(c.UnitName) { return fmt.Errorf("invalid unit name %q", c.UnitName) } args = args[1:] } else { return fmt.Errorf("no unit specified") } return cmd.CheckEmpty(args) } func (c *ResolvedCommand) Run(_ *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() return client.Resolved(c.UnitName, c.Retry) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/destroyunit_test.go���������������������������0000644�0000153�0000161�00000002214�12321735642�026363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/testing" ) type DestroyUnitSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&DestroyUnitSuite{}) func runDestroyUnit(c *gc.C, args ...string) error { _, err := testing.RunCommand(c, &DestroyUnitCommand{}, args) return err } func (s *DestroyUnitSuite) TestDestroyUnit(c *gc.C) { testing.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "-n", "2", "local:dummy", "dummy") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") svc, _ := s.AssertService(c, "dummy", curl, 2, 0) err = runDestroyUnit(c, "dummy/0", "dummy/1", "dummy/2", "sillybilly/17") c.Assert(err, gc.ErrorMatches, `some units were not destroyed: unit "dummy/2" does not exist; unit "sillybilly/17" does not exist`) units, err := svc.AllUnits() c.Assert(err, gc.IsNil) for _, u := range units { c.Assert(u.Life(), gc.Equals, state.Dying) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/retryprovisioning_test.go���������������������0000644�0000153�0000161�00000004161�12321735642�027611� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" ) type retryProvisioningSuite struct { jujutesting.JujuConnSuite } var _ = gc.Suite(&retryProvisioningSuite{}) var resolvedMachineTests = []struct { args []string err string stdErr string }{ { err: `no machine specified`, }, { args: []string{"jeremy-fisher"}, err: `invalid machine "jeremy-fisher"`, }, { args: []string{"42"}, stdErr: `cannot retry provisioning "machine-42": machine 42 not found`, }, { args: []string{"1"}, stdErr: `cannot retry provisioning "machine-1": machine "machine-1" is not in an error state`, }, { args: []string{"0"}, }, { args: []string{"0", "1"}, stdErr: `cannot retry provisioning "machine-1": machine "machine-1" is not in an error state`, }, } func (s *retryProvisioningSuite) TestResolved(c *gc.C) { m, err := s.State.AddOneMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobManageEnviron}, }) c.Assert(err, gc.IsNil) err = m.SetStatus(params.StatusError, "broken", nil) c.Assert(err, gc.IsNil) _, err = s.State.AddOneMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }) c.Assert(err, gc.IsNil) for i, t := range resolvedMachineTests { c.Logf("test %d: %v", i, t.args) context, err := testing.RunCommand(c, &RetryProvisioningCommand{}, t.args) if t.err != "" { c.Check(err, gc.ErrorMatches, t.err) continue } else { c.Check(err, gc.IsNil) } output := testing.Stderr(context) stripped := strings.Replace(output, "\n", "", -1) c.Check(stripped, gc.Equals, t.stdErr) if t.args[0] == "0" { status, info, data, err := m.Status() c.Check(err, gc.IsNil) c.Check(status, gc.Equals, params.StatusError) c.Check(info, gc.Equals, "broken") c.Check(data["transient"], jc.IsTrue) } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/unset.go��������������������������������������0000644�0000153�0000161�00000004641�12321735776�024107� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "launchpad.net/gnuflag" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/state/api/params" ) // UnsetCommand sets configuration values of a service back // to their default. type UnsetCommand struct { cmd.EnvCommandBase ServiceName string Options []string } const unsetDoc = ` Set one or more configuration options for the specified service to their default. See also the set commmand to set one or more configuration options for a specified service. ` func (c *UnsetCommand) Info() *cmd.Info { return &cmd.Info{ Name: "unset", Args: "<service> name ...", Purpose: "set service config options back to their default", Doc: unsetDoc, } } func (c *UnsetCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) } func (c *UnsetCommand) Init(args []string) error { if len(args) == 0 { return errors.New("no service name specified") } c.ServiceName = args[0] c.Options = args[1:] if len(c.Options) == 0 { return errors.New("no configuration options specified") } return nil } // run1dot16 runs 'juju unset' using a direct DB connection to maintain // compatibility with an API server running 1.16 or older (when ServiceUnset // was not available). This fallback can be removed when we no longer maintain // 1.16 compatibility. // This was copied directly from the code in UnsetCommand.Run in 1.16 func (c *UnsetCommand) run1dot16() error { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() service, err := conn.State.Service(c.ServiceName) if err != nil { return err } if len(c.Options) > 0 { settings := make(charm.Settings) for _, option := range c.Options { settings[option] = nil } return service.UpdateConfigSettings(settings) } else { return nil } } // Run resets the configuration of a service. func (c *UnsetCommand) Run(ctx *cmd.Context) error { apiclient, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer apiclient.Close() err = apiclient.ServiceUnset(c.ServiceName, c.Options) if params.IsCodeNotImplemented(err) { logger.Infof("ServiceUnset not supported by the API server, " + "falling back to 1.16 compatibility mode (direct DB access)") err = c.run1dot16() } return err } �����������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/unset_test.go���������������������������������0000644�0000153�0000161�00000005444�12321735642�025140� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" ) type UnsetSuite struct { testing.JujuConnSuite svc *state.Service dir string } var _ = gc.Suite(&UnsetSuite{}) func (s *UnsetSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) ch := s.AddTestingCharm(c, "dummy") svc := s.AddTestingService(c, "dummy-service", ch) s.svc = svc s.dir = c.MkDir() setupConfigFile(c, s.dir) } func (s *UnsetSuite) TestUnsetOptionOneByOneSuccess(c *gc.C) { // Set options as preparation. assertSetSuccess(c, s.dir, s.svc, []string{ "username=hello", "outlook=hello@world.tld", }, charm.Settings{ "username": "hello", "outlook": "hello@world.tld", }) // Unset one by one. assertUnsetSuccess(c, s.dir, s.svc, []string{"username"}, charm.Settings{ "outlook": "hello@world.tld", }) assertUnsetSuccess(c, s.dir, s.svc, []string{"outlook"}, charm.Settings{}) } func (s *UnsetSuite) TestUnsetOptionMultipleAtOnceSuccess(c *gc.C) { // Set options as preparation. assertSetSuccess(c, s.dir, s.svc, []string{ "username=hello", "outlook=hello@world.tld", }, charm.Settings{ "username": "hello", "outlook": "hello@world.tld", }) // Unset multiple options at once. assertUnsetSuccess(c, s.dir, s.svc, []string{"username", "outlook"}, charm.Settings{}) } func (s *UnsetSuite) TestUnsetOptionFail(c *gc.C) { assertUnsetFail(c, s.dir, []string{}, "error: no configuration options specified\n") assertUnsetFail(c, s.dir, []string{"invalid"}, "error: unknown option \"invalid\"\n") assertUnsetFail(c, s.dir, []string{"username=bar"}, "error: unknown option \"username=bar\"\n") assertUnsetFail(c, s.dir, []string{ "username", "outlook", "invalid", }, "error: unknown option \"invalid\"\n") } // assertUnsetSuccess unsets configuration options and checks the expected settings. func assertUnsetSuccess(c *gc.C, dir string, svc *state.Service, args []string, expect charm.Settings) { ctx := coretesting.ContextForDir(c, dir) code := cmd.Main(&UnsetCommand{}, ctx, append([]string{"dummy-service"}, args...)) c.Check(code, gc.Equals, 0) settings, err := svc.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, expect) } // assertUnsetFail unsets configuration options and checks the expected error. func assertUnsetFail(c *gc.C, dir string, args []string, err string) { ctx := coretesting.ContextForDir(c, dir) code := cmd.Main(&UnsetCommand{}, ctx, append([]string{"dummy-service"}, args...)) c.Check(code, gc.Not(gc.Equals), 0) c.Assert(ctx.Stderr.(*bytes.Buffer).String(), gc.Matches, err) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/helptool.go�����������������������������������0000644�0000153�0000161�00000004726�12321735642�024573� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/worker/uniter/jujuc" ) // dummyHookContext implements jujuc.Context, // as expected by jujuc.NewCommand. type dummyHookContext struct{} func (dummyHookContext) UnitName() string { return "" } func (dummyHookContext) PublicAddress() (string, bool) { return "", false } func (dummyHookContext) PrivateAddress() (string, bool) { return "", false } func (dummyHookContext) OpenPort(protocol string, port int) error { return nil } func (dummyHookContext) ClosePort(protocol string, port int) error { return nil } func (dummyHookContext) ConfigSettings() (charm.Settings, error) { return charm.NewConfig().DefaultSettings(), nil } func (dummyHookContext) HookRelation() (jujuc.ContextRelation, bool) { return nil, false } func (dummyHookContext) RemoteUnitName() (string, bool) { return "", false } func (dummyHookContext) Relation(id int) (jujuc.ContextRelation, bool) { return nil, false } func (dummyHookContext) RelationIds() []int { return []int{} } func (dummyHookContext) OwnerTag() string { return "" } type HelpToolCommand struct { cmd.CommandBase tool string } func (t *HelpToolCommand) Info() *cmd.Info { return &cmd.Info{ Name: "help-tool", Args: "[tool]", Purpose: "show help on a juju charm tool", } } func (t *HelpToolCommand) Init(args []string) error { tool, err := cmd.ZeroOrOneArgs(args) if err == nil { t.tool = tool } return err } func (c *HelpToolCommand) Run(ctx *cmd.Context) error { var hookctx dummyHookContext if c.tool == "" { // Ripped from SuperCommand. We could Run() a SuperCommand // with "help commands", but then the implicit "help" command // shows up. names := jujuc.CommandNames() cmds := make([]cmd.Command, 0, len(names)) longest := 0 for _, name := range names { if c, err := jujuc.NewCommand(hookctx, name); err == nil { if len(name) > longest { longest = len(name) } cmds = append(cmds, c) } } for _, c := range cmds { info := c.Info() fmt.Fprintf(ctx.Stdout, "%-*s %s\n", longest, info.Name, info.Purpose) } } else { c, err := jujuc.NewCommand(hookctx, c.tool) if err != nil { return err } info := c.Info() f := gnuflag.NewFlagSet(info.Name, gnuflag.ContinueOnError) c.SetFlags(f) ctx.Stdout.Write(info.Help(f)) } return nil } ������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/unexpose_test.go������������������������������0000644�0000153�0000161�00000002475�12321735642�025651� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/testing" ) type UnexposeSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&UnexposeSuite{}) func runUnexpose(c *gc.C, args ...string) error { _, err := testing.RunCommand(c, &UnexposeCommand{}, args) return err } func (s *UnexposeSuite) assertExposed(c *gc.C, service string, expected bool) { svc, err := s.State.Service(service) c.Assert(err, gc.IsNil) actual := svc.IsExposed() c.Assert(actual, gc.Equals, expected) } func (s *UnexposeSuite) TestUnexpose(c *gc.C) { testing.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "some-service-name") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "some-service-name", curl, 1, 0) err = runExpose(c, "some-service-name") c.Assert(err, gc.IsNil) s.assertExposed(c, "some-service-name", true) err = runUnexpose(c, "some-service-name") c.Assert(err, gc.IsNil) s.assertExposed(c, "some-service-name", false) err = runUnexpose(c, "nonexistent-service") c.Assert(err, gc.ErrorMatches, `service "nonexistent-service" not found`) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/plugin_test.go��������������������������������0000644�0000153�0000161�00000016442�12321735642�025300� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "fmt" "io/ioutil" "os" "text/template" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type PluginSuite struct { testbase.LoggingSuite oldPath string home *testing.FakeHome } var _ = gc.Suite(&PluginSuite{}) func (suite *PluginSuite) SetUpTest(c *gc.C) { suite.LoggingSuite.SetUpTest(c) suite.oldPath = os.Getenv("PATH") suite.home = testing.MakeSampleHome(c) os.Setenv("PATH", "/bin:"+testing.HomePath()) } func (suite *PluginSuite) TearDownTest(c *gc.C) { suite.home.Restore() os.Setenv("PATH", suite.oldPath) suite.LoggingSuite.TearDownTest(c) } func (*PluginSuite) TestFindPlugins(c *gc.C) { plugins := findPlugins() c.Assert(plugins, gc.DeepEquals, []string{}) } func (suite *PluginSuite) TestFindPluginsOrder(c *gc.C) { suite.makePlugin("foo", 0744) suite.makePlugin("bar", 0654) suite.makePlugin("baz", 0645) plugins := findPlugins() c.Assert(plugins, gc.DeepEquals, []string{"juju-bar", "juju-baz", "juju-foo"}) } func (suite *PluginSuite) TestFindPluginsIgnoreNotExec(c *gc.C) { suite.makePlugin("foo", 0644) suite.makePlugin("bar", 0666) plugins := findPlugins() c.Assert(plugins, gc.DeepEquals, []string{}) } func (suite *PluginSuite) TestRunPluginExising(c *gc.C) { suite.makePlugin("foo", 0755) ctx := testing.Context(c) err := RunPlugin(ctx, "foo", []string{"some params"}) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(ctx), gc.Equals, "foo some params\n") c.Assert(testing.Stderr(ctx), gc.Equals, "") } func (suite *PluginSuite) TestRunPluginWithFailing(c *gc.C) { suite.makeFailingPlugin("foo", 2) ctx := testing.Context(c) err := RunPlugin(ctx, "foo", []string{"some params"}) c.Assert(err, gc.ErrorMatches, "exit status 2") c.Assert(testing.Stdout(ctx), gc.Equals, "failing\n") c.Assert(testing.Stderr(ctx), gc.Equals, "") } func (suite *PluginSuite) TestGatherDescriptionsInParallel(c *gc.C) { // Make plugins that will deadlock if we don't start them in parallel. // Each plugin depends on another one being started before they will // complete. They make a full loop, so no sequential ordering will ever // succeed. suite.makeFullPlugin(PluginParams{Name: "foo", Creates: "foo", DependsOn: "bar"}) suite.makeFullPlugin(PluginParams{Name: "bar", Creates: "bar", DependsOn: "baz"}) suite.makeFullPlugin(PluginParams{Name: "baz", Creates: "baz", DependsOn: "error"}) suite.makeFullPlugin(PluginParams{Name: "error", ExitStatus: 1, Creates: "error", DependsOn: "foo"}) // If the code was wrong, GetPluginDescriptions would deadlock, // so timeout after a short while resultChan := make(chan []PluginDescription) go func() { resultChan <- GetPluginDescriptions() }() // 10 seconds is arbitrary but should always be generously long. Test // actually only takes about 15ms in practice. But 10s allows for system hiccups, etc. waitTime := 10 * time.Second var results []PluginDescription select { case results = <-resultChan: break case <-time.After(waitTime): c.Fatalf("Took too more than %fs to complete.", waitTime.Seconds()) } c.Assert(results, gc.HasLen, 4) c.Assert(results[0].name, gc.Equals, "bar") c.Assert(results[0].description, gc.Equals, "bar description") c.Assert(results[1].name, gc.Equals, "baz") c.Assert(results[1].description, gc.Equals, "baz description") c.Assert(results[2].name, gc.Equals, "error") c.Assert(results[2].description, gc.Equals, "error occurred running 'juju-error --description'") c.Assert(results[3].name, gc.Equals, "foo") c.Assert(results[3].description, gc.Equals, "foo description") } func (suite *PluginSuite) TestHelpPluginsWithNoPlugins(c *gc.C) { output := badrun(c, 0, "help", "plugins") c.Assert(output, jc.HasPrefix, PluginTopicText) c.Assert(output, jc.HasSuffix, "\n\nNo plugins found.\n") } func (suite *PluginSuite) TestHelpPluginsWithPlugins(c *gc.C) { suite.makeFullPlugin(PluginParams{Name: "foo"}) suite.makeFullPlugin(PluginParams{Name: "bar"}) output := badrun(c, 0, "help", "plugins") c.Assert(output, jc.HasPrefix, PluginTopicText) expectedPlugins := ` bar bar description foo foo description ` c.Assert(output, jc.HasSuffix, expectedPlugins) } func (suite *PluginSuite) TestHelpPluginName(c *gc.C) { suite.makeFullPlugin(PluginParams{Name: "foo"}) output := badrun(c, 0, "help", "foo") expectedHelp := `foo longer help something useful ` c.Assert(output, gc.Matches, expectedHelp) } func (suite *PluginSuite) TestHelpPluginNameNotAPlugin(c *gc.C) { output := badrun(c, 0, "help", "foo") expectedHelp := "ERROR unknown command or topic for foo\n" c.Assert(output, gc.Matches, expectedHelp) } func (suite *PluginSuite) TestHelpAsArg(c *gc.C) { suite.makeFullPlugin(PluginParams{Name: "foo"}) output := badrun(c, 0, "foo", "--help") expectedHelp := `foo longer help something useful ` c.Assert(output, gc.Matches, expectedHelp) } func (suite *PluginSuite) TestDebugAsArg(c *gc.C) { suite.makeFullPlugin(PluginParams{Name: "foo"}) output := badrun(c, 0, "foo", "--debug") expectedDebug := "some debug\n" c.Assert(output, gc.Matches, expectedDebug) } func (suite *PluginSuite) TestJujuEnvVars(c *gc.C) { suite.makeFullPlugin(PluginParams{Name: "foo"}) output := badrun(c, 0, "foo", "-e", "myenv", "-p", "pluginarg") expectedDebug := `foo -e myenv -p pluginarg\n.*env is: myenv\n.*home is: .*\.juju\n` c.Assert(output, gc.Matches, expectedDebug) } func (suite *PluginSuite) makePlugin(name string, perm os.FileMode) { content := fmt.Sprintf("#!/bin/bash --norc\necho %s $*", name) filename := testing.HomePath(JujuPluginPrefix + name) ioutil.WriteFile(filename, []byte(content), perm) } func (suite *PluginSuite) makeFailingPlugin(name string, exitStatus int) { content := fmt.Sprintf("#!/bin/bash --norc\necho failing\nexit %d", exitStatus) filename := testing.HomePath(JujuPluginPrefix + name) ioutil.WriteFile(filename, []byte(content), 0755) } type PluginParams struct { Name string ExitStatus int Creates string DependsOn string } const pluginTemplate = `#!/bin/bash --norc if [ "$1" = "--description" ]; then if [ -n "{{.Creates}}" ]; then touch "{{.Creates}}" fi if [ -n "{{.DependsOn}}" ]; then # Sleep 10ms while waiting to allow other stuff to do work while [ ! -e "{{.DependsOn}}" ]; do sleep 0.010; done fi echo "{{.Name}} description" exit {{.ExitStatus}} fi if [ "$1" = "--help" ]; then echo "{{.Name}} longer help" echo "" echo "something useful" exit {{.ExitStatus}} fi if [ "$1" = "--debug" ]; then echo "some debug" exit {{.ExitStatus}} fi echo {{.Name}} $* echo "env is: " $JUJU_ENV echo "home is: " $JUJU_HOME exit {{.ExitStatus}} ` func (suite *PluginSuite) makeFullPlugin(params PluginParams) { // Create a new template and parse the plugin into it. t := template.Must(template.New("plugin").Parse(pluginTemplate)) content := &bytes.Buffer{} filename := testing.HomePath("juju-" + params.Name) // Create the files in the temp dirs, so we don't pollute the working space if params.Creates != "" { params.Creates = testing.HomePath(params.Creates) } if params.DependsOn != "" { params.DependsOn = testing.HomePath(params.DependsOn) } t.Execute(content, params) ioutil.WriteFile(filename, content.Bytes(), 0755) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/synctools.go����������������������������������0000644�0000153�0000161�00000006630�12321735776�025006� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "github.com/juju/loggo" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/sync" "launchpad.net/juju-core/version" ) var syncTools = sync.SyncTools // SyncToolsCommand copies all the tools from the us-east-1 bucket to the local // bucket. type SyncToolsCommand struct { cmd.EnvCommandBase allVersions bool versionStr string majorVersion int minorVersion int dryRun bool dev bool public bool source string localDir string destination string } var _ cmd.Command = (*SyncToolsCommand)(nil) func (c *SyncToolsCommand) Info() *cmd.Info { return &cmd.Info{ Name: "sync-tools", Purpose: "copy tools from the official tool store into a local environment", Doc: ` This copies the Juju tools tarball from the official tools store (located at https://streams.canonical.com/juju) into your environment. This is generally done when you want Juju to be able to run without having to access the Internet. Alternatively you can specify a local directory as source. Sometimes this is because the environment does not have public access, and sometimes you just want to avoid having to access data outside of the local cloud. `, } } func (c *SyncToolsCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.BoolVar(&c.allVersions, "all", false, "copy all versions, not just the latest") f.StringVar(&c.versionStr, "version", "", "copy a specific major[.minor] version") f.BoolVar(&c.dryRun, "dry-run", false, "don't copy, just print what would be copied") f.BoolVar(&c.dev, "dev", false, "consider development versions as well as released ones") f.BoolVar(&c.public, "public", false, "tools are for a public cloud, so generate mirrors information") f.StringVar(&c.source, "source", "", "local source directory") f.StringVar(&c.localDir, "local-dir", "", "local destination directory") f.StringVar(&c.destination, "destination", "", "local destination directory") } func (c *SyncToolsCommand) Init(args []string) error { if c.destination != "" { // Override localDir with destination as localDir now replaces destination c.localDir = c.destination logger.Warningf("Use of the --destination flag is deprecated in 1.18. Please use --local-dir instead.") } if c.versionStr != "" { var err error if c.majorVersion, c.minorVersion, err = version.ParseMajorMinor(c.versionStr); err != nil { return err } } return cmd.CheckEmpty(args) } func (c *SyncToolsCommand) Run(ctx *cmd.Context) (resultErr error) { // Register writer for output on screen. loggo.RegisterWriter("synctools", cmd.NewCommandLogWriter("juju.environs.sync", ctx.Stdout, ctx.Stderr), loggo.INFO) defer loggo.RemoveWriter("synctools") environ, cleanup, err := environFromName(ctx, c.EnvName, &resultErr, "Sync-tools") if err != nil { return err } defer cleanup() target := environ.Storage() if c.localDir != "" { target, err = filestorage.NewFileStorageWriter(c.localDir) if err != nil { return err } } // Prepare syncing. sctx := &sync.SyncContext{ Target: target, AllVersions: c.allVersions, MajorVersion: c.majorVersion, MinorVersion: c.minorVersion, DryRun: c.dryRun, Dev: c.dev, Public: c.public, Source: c.source, } return syncTools(sctx) } ��������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/publish.go������������������������������������0000644�0000153�0000161�00000011573�12321735776�024421� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "strings" "time" "launchpad.net/gnuflag" "launchpad.net/juju-core/bzr" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/log" ) type PublishCommand struct { cmd.EnvCommandBase URL string CharmPath string // changePushLocation allows translating the branch location // for testing purposes. changePushLocation func(loc string) string pollDelay time.Duration } const publishDoc = ` <charm url> can be a charm URL, or an unambiguously condensed form of it; the following forms are accepted: For cs:precise/mysql cs:precise/mysql precise/mysql For cs:~user/precise/mysql cs:~user/precise/mysql There is no default series, so one must be provided explicitly when informing a charm URL. If the URL isn't provided, an attempt will be made to infer it from the current branch push URL. ` func (c *PublishCommand) Info() *cmd.Info { return &cmd.Info{ Name: "publish", Args: "[<charm url>]", Purpose: "publish charm to the store", Doc: publishDoc, } } func (c *PublishCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.StringVar(&c.CharmPath, "from", ".", "path for charm to be published") } func (c *PublishCommand) Init(args []string) error { if len(args) == 0 { return nil } c.URL = args[0] return cmd.CheckEmpty(args[1:]) } func (c *PublishCommand) ChangePushLocation(change func(string) string) { c.changePushLocation = change } func (c *PublishCommand) SetPollDelay(delay time.Duration) { c.pollDelay = delay } // Wording guideline to avoid confusion: charms have *URLs*, branches have *locations*. func (c *PublishCommand) Run(ctx *cmd.Context) (err error) { branch := bzr.New(ctx.AbsPath(c.CharmPath)) if _, err := os.Stat(branch.Join(".bzr")); err != nil { return fmt.Errorf("not a charm branch: %s", branch.Location()) } if err := branch.CheckClean(); err != nil { return err } var curl *charm.URL if c.URL == "" { if err == nil { loc, err := branch.PushLocation() if err != nil { return fmt.Errorf("no charm URL provided and cannot infer from current directory (no push location)") } curl, err = charm.Store.CharmURL(loc) if err != nil { return fmt.Errorf("cannot infer charm URL from branch location: %q", loc) } } } else { curl, err = charm.InferURL(c.URL, "") if err != nil { return err } } pushLocation := charm.Store.BranchLocation(curl) if c.changePushLocation != nil { pushLocation = c.changePushLocation(pushLocation) } repo, err := charm.InferRepository(curl.Reference, "/not/important") if err != nil { return err } if repo != charm.Store { return fmt.Errorf("charm URL must reference the juju charm store") } localDigest, err := branch.RevisionId() if err != nil { return fmt.Errorf("cannot obtain local digest: %v", err) } log.Infof("local digest is %s", localDigest) ch, err := charm.ReadDir(branch.Location()) if err != nil { return err } if ch.Meta().Name != curl.Name { return fmt.Errorf("charm name in metadata must match name in URL: %q != %q", ch.Meta().Name, curl.Name) } oldEvent, err := charm.Store.Event(curl, localDigest) if _, ok := err.(*charm.NotFoundError); ok { oldEvent, err = charm.Store.Event(curl, "") if _, ok := err.(*charm.NotFoundError); ok { log.Infof("charm %s is not yet in the store", curl) err = nil } } if err != nil { return fmt.Errorf("cannot obtain event details from the store: %s", err) } if oldEvent != nil && oldEvent.Digest == localDigest { return handleEvent(ctx, curl, oldEvent) } log.Infof("sending charm to the charm store...") err = branch.Push(&bzr.PushAttr{Location: pushLocation, Remember: true}) if err != nil { return err } log.Infof("charm sent; waiting for it to be published...") for { time.Sleep(c.pollDelay) newEvent, err := charm.Store.Event(curl, "") if _, ok := err.(*charm.NotFoundError); ok { continue } if err != nil { return fmt.Errorf("cannot obtain event details from the store: %s", err) } if oldEvent != nil && oldEvent.Digest == newEvent.Digest { continue } if newEvent.Digest != localDigest { // TODO Check if the published digest is in the local history. return fmt.Errorf("charm changed but not to local charm digest; publishing race?") } return handleEvent(ctx, curl, newEvent) } return nil } func handleEvent(ctx *cmd.Context, curl *charm.URL, event *charm.EventResponse) error { switch event.Kind { case "published": curlRev := curl.WithRevision(event.Revision) log.Infof("charm published at %s as %s", event.Time, curlRev) fmt.Fprintln(ctx.Stdout, curlRev) case "publish-error": return fmt.Errorf("charm could not be published: %s", strings.Join(event.Errors, "; ")) default: return fmt.Errorf("unknown event kind %q for charm %s", event.Kind, curl) } return nil } �������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/switch.go�������������������������������������0000644�0000153�0000161�00000006314�12321735776�024251� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "fmt" "os" "sort" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" ) type SwitchCommand struct { cmd.CommandBase EnvName string List bool } var switchDoc = ` Show or change the default juju environment name. If no command line parameters are passed, switch will output the current environment as defined by the file $JUJU_HOME/current-environment. If a command line parameter is passed in, that value will is stored in the current environment file if it represents a valid environment name as specified in the environments.yaml file. ` func (c *SwitchCommand) Info() *cmd.Info { return &cmd.Info{ Name: "switch", Args: "[environment name]", Purpose: "show or change the default juju environment name", Doc: switchDoc, Aliases: []string{"env"}, } } func (c *SwitchCommand) SetFlags(f *gnuflag.FlagSet) { f.BoolVar(&c.List, "l", false, "list the environment names") f.BoolVar(&c.List, "list", false, "") } func (c *SwitchCommand) Init(args []string) (err error) { c.EnvName, err = cmd.ZeroOrOneArgs(args) return } func validEnvironmentName(name string, names []string) bool { for _, n := range names { if name == n { return true } } return false } func (c *SwitchCommand) Run(ctx *cmd.Context) error { // Switch is an alternative way of dealing with environments than using // the JUJU_ENV environment setting, and as such, doesn't play too well. // If JUJU_ENV is set we should report that as the current environment, // and not allow switching when it is set. // Passing through the empty string reads the default environments.yaml file. environments, err := environs.ReadEnvirons("") if err != nil { return errors.New("couldn't read the environment") } names := environments.Names() sort.Strings(names) if c.List { // List all environments. if c.EnvName != "" { return errors.New("cannot switch and list at the same time") } for _, name := range names { fmt.Fprintf(ctx.Stdout, "%s\n", name) } return nil } jujuEnv := os.Getenv("JUJU_ENV") if jujuEnv != "" { if c.EnvName == "" { fmt.Fprintf(ctx.Stdout, "%s\n", jujuEnv) return nil } else { return fmt.Errorf("cannot switch when JUJU_ENV is overriding the environment (set to %q)", jujuEnv) } } currentEnv := cmd.ReadCurrentEnvironment() if currentEnv == "" { currentEnv = environments.Default } // Handle the different operation modes. switch { case c.EnvName == "" && currentEnv == "": // Nothing specified and nothing to switch to. return errors.New("no currently specified environment") case c.EnvName == "": // Simply print the current environment. fmt.Fprintf(ctx.Stdout, "%s\n", currentEnv) default: // Switch the environment. if !validEnvironmentName(c.EnvName, names) { return fmt.Errorf("%q is not a name of an existing defined environment", c.EnvName) } if err := cmd.WriteCurrentEnvironment(c.EnvName); err != nil { return err } if currentEnv == "" { fmt.Fprintf(ctx.Stdout, "-> %s\n", c.EnvName) } else { fmt.Fprintf(ctx.Stdout, "%s -> %s\n", currentEnv, c.EnvName) } } return nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/deploy.go�������������������������������������0000644�0000153�0000161�00000020141�12321735776�024236� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "fmt" "os" "launchpad.net/gnuflag" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" ) type DeployCommand struct { cmd.EnvCommandBase UnitCommandBase CharmName string ServiceName string Config cmd.FileVar Constraints constraints.Value BumpRevision bool // Remove this once the 1.16 support is dropped. RepoPath string // defaults to JUJU_REPOSITORY } const deployDoc = ` <charm name> can be a charm URL, or an unambiguously condensed form of it; assuming a current series of "precise", the following forms will be accepted: For cs:precise/mysql mysql precise/mysql For cs:~user/precise/mysql cs:~user/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 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 <service name>, if omitted, will be derived from <charm name>. Constraints can be specified when using deploy by specifying the --constraints flag. When used with deploy, service-specific constraints are set so that later machines provisioned with add-unit will use the same constraints (unless changed by set-constraints). Charms can be deployed to a specific machine using the --to argument. Examples: juju deploy mysql --to 23 (Deploy to machine 23) juju deploy mysql --to 24/lxc/3 (Deploy to lxc container 3 on host machine 24) juju deploy mysql --to lxc:25 (Deploy to a new lxc container on host machine 25) juju deploy mysql -n 5 --constraints mem=8G (deploy 5 instances of mysql with at least 8 GB of RAM each) See Also: juju help constraints juju help set-constraints juju help get-constraints ` func (c *DeployCommand) Info() *cmd.Info { return &cmd.Info{ Name: "deploy", Args: "<charm name> [<service name>]", Purpose: "deploy a new service", Doc: deployDoc, } } func (c *DeployCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) c.UnitCommandBase.SetFlags(f) f.IntVar(&c.NumUnits, "n", 1, "number of service units to deploy for principal charms") f.BoolVar(&c.BumpRevision, "u", false, "increment local charm directory revision (DEPRECATED)") f.BoolVar(&c.BumpRevision, "upgrade", false, "") f.Var(&c.Config, "config", "path to yaml-formatted service config") f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "set service constraints") f.StringVar(&c.RepoPath, "repository", os.Getenv(osenv.JujuRepositoryEnvKey), "local charm repository") } func (c *DeployCommand) Init(args []string) error { switch len(args) { case 2: if !names.IsService(args[1]) { return fmt.Errorf("invalid service name %q", args[1]) } c.ServiceName = args[1] fallthrough case 1: if _, err := charm.InferURL(args[0], "fake"); err != nil { return fmt.Errorf("invalid charm name %q", args[0]) } c.CharmName = args[0] case 0: return errors.New("no charm specified") default: return cmd.CheckEmpty(args[2:]) } return c.UnitCommandBase.Init(args) } func (c *DeployCommand) Run(ctx *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() attrs, err := client.EnvironmentGet() if params.IsCodeNotImplemented(err) { logger.Infof("EnvironmentGet not supported by the API server, " + "falling back to 1.16 compatibility mode (direct DB access)") return c.run1dot16(ctx) } if err != nil { return err } conf, err := config.New(config.NoDefaults, attrs) if err != nil { return err } curl, err := resolveCharmURL(c.CharmName, client, conf) if err != nil { return err } repo, err := charm.InferRepository(curl.Reference, ctx.AbsPath(c.RepoPath)) if err != nil { return err } repo = config.SpecializeCharmRepo(repo, conf) curl, err = addCharmViaAPI(client, ctx, curl, repo) if err != nil { return err } if c.BumpRevision { ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") } charmInfo, err := client.CharmInfo(curl.String()) if err != nil { return err } numUnits := c.NumUnits if charmInfo.Meta.Subordinate { if !constraints.IsEmpty(&c.Constraints) { return errors.New("cannot use --constraints with subordinate service") } if numUnits == 1 && c.ToMachineSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate service") } } serviceName := c.ServiceName if serviceName == "" { serviceName = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(ctx) if err != nil { return err } } return client.ServiceDeploy( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.ToMachineSpec, ) } // run1dot16 implements the deploy command in 1.16 compatibility mode, // with direct state access. Remove this when support for 1.16 is // dropped. func (c *DeployCommand) run1dot16(ctx *cmd.Context) error { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() conf, err := conn.State.EnvironConfig() if err != nil { return err } curl, err := resolveCharmURL1dot16(c.CharmName, conf) if err != nil { return err } repo, err := charm.InferRepository(curl.Reference, c.RepoPath) if err != nil { return err } repo = config.SpecializeCharmRepo(repo, conf) // TODO(fwereade) it's annoying to roundtrip the bytes through the client // here, but it's the original behaviour and not convenient to change. // PutCharm will always be required in some form for local charms; and we // will need an EnsureStoreCharm method somewhere that gets the state.Charm // for use in the following checks. ch, err := conn.PutCharm(curl, repo, c.BumpRevision) if err != nil { return err } numUnits := c.NumUnits if ch.Meta().Subordinate { if !constraints.IsEmpty(&c.Constraints) { return errors.New("cannot use --constraints with subordinate service") } if numUnits == 1 && c.ToMachineSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate service") } } serviceName := c.ServiceName if serviceName == "" { serviceName = ch.Meta().Name } var settings charm.Settings if c.Config.Path != "" { configYAML, err := c.Config.Read(ctx) if err != nil { return err } settings, err = ch.Config().ParseSettingsYAML(configYAML, serviceName) if err != nil { return err } } _, err = juju.DeployService(conn.State, juju.DeployServiceParams{ ServiceName: serviceName, Charm: ch, NumUnits: numUnits, ConfigSettings: settings, Constraints: c.Constraints, ToMachineSpec: c.ToMachineSpec, }) return err } // addCharmViaAPI calls the appropriate client API calls to add the // given charm URL to state. Also displays the charm URL of the added // charm on stdout. func addCharmViaAPI(client *api.Client, ctx *cmd.Context, curl *charm.URL, repo charm.Repository) (*charm.URL, error) { if curl.Revision < 0 { latest, err := charm.Latest(repo, curl) if err != nil { return nil, err } curl = curl.WithRevision(latest) } switch curl.Schema { case "local": ch, err := repo.Get(curl) if err != nil { return nil, err } stateCurl, err := client.AddLocalCharm(curl, ch) if err != nil { return nil, err } curl = stateCurl case "cs": err := client.AddCharm(curl) if err != nil { return nil, err } default: return nil, fmt.Errorf("unsupported charm URL schema: %q", curl.Schema) } ctx.Infof("Added charm %q to the environment.", curl) return curl, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/constraints.go��������������������������������0000644�0000153�0000161�00000013554�12321735776�025323� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" ) const getConstraintsDoc = ` get-constraints returns a list of constraints that have been set on the environment using juju set-constraints. You can also view constraints set for a specific service by using juju get-constraints <service>. See Also: juju help constraints juju help set-constraints ` const setConstraintsDoc = ` set-constraints sets machine constraints on the system, which are used as the default constraints for all new machines provisioned in the environment (unless overridden). You can also set constraints on a specific service by using juju set-constraints <service>. Constraints set on a service are combined with environment constraints for commands (such as juju deploy) that provision machines for services. Where environment and service constraints overlap, the service constraints take precedence. Examples: set-constraints mem=8G (all new machines in the environment must have at least 8GB of RAM) set-constraints --service wordpress mem=4G (all new wordpress machines can ignore the 8G constraint above, and require only 4G) See Also: juju help constraints juju help get-constraints juju help deploy juju help add-machine juju help add-unit ` // GetConstraintsCommand shows the constraints for a service or environment. type GetConstraintsCommand struct { cmd.EnvCommandBase ServiceName string out cmd.Output } func (c *GetConstraintsCommand) Info() *cmd.Info { return &cmd.Info{ Name: "get-constraints", Args: "[<service>]", Purpose: "view constraints on the environment or a service", Doc: getConstraintsDoc, } } func formatConstraints(value interface{}) ([]byte, error) { return []byte(value.(constraints.Value).String()), nil } func (c *GetConstraintsCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) c.out.AddFlags(f, "constraints", map[string]cmd.Formatter{ "constraints": formatConstraints, "yaml": cmd.FormatYaml, "json": cmd.FormatJson, }) } func (c *GetConstraintsCommand) Init(args []string) error { if len(args) > 0 { if !names.IsService(args[0]) { return fmt.Errorf("invalid service name %q", args[0]) } c.ServiceName, args = args[0], args[1:] } return cmd.CheckEmpty(args) } // getEnvironConstraints1dot16 uses direct DB access to get the Environment // constraints against an API server running 1.16 or older (when GetEnvironmentConstraints // was not available). This fallback can be removed when we no longer maintain // 1.16 compatibility. // This only does the GetEnvironmentConstraints portion of Run, since // GetServiceConstraints was already implemented. func (c *GetConstraintsCommand) getEnvironConstraints1dot16() (constraints.Value, error) { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return constraints.Value{}, err } defer conn.Close() return conn.State.EnvironConstraints() } func (c *GetConstraintsCommand) Run(ctx *cmd.Context) error { apiclient, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer apiclient.Close() var cons constraints.Value if c.ServiceName == "" { cons, err = apiclient.GetEnvironmentConstraints() if params.IsCodeNotImplemented(err) { logger.Infof("GetEnvironmentConstraints not supported by the API server, " + "falling back to 1.16 compatibility mode (direct DB access)") cons, err = c.getEnvironConstraints1dot16() } } else { cons, err = apiclient.GetServiceConstraints(c.ServiceName) } if err != nil { return err } return c.out.Write(ctx, cons) } // SetConstraintsCommand shows the constraints for a service or environment. type SetConstraintsCommand struct { cmd.EnvCommandBase ServiceName string Constraints constraints.Value } func (c *SetConstraintsCommand) Info() *cmd.Info { return &cmd.Info{ Name: "set-constraints", Args: "[key=[value] ...]", Purpose: "set constraints on the environment or a service", Doc: setConstraintsDoc, } } func (c *SetConstraintsCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.StringVar(&c.ServiceName, "s", "", "set service constraints") f.StringVar(&c.ServiceName, "service", "", "") } func (c *SetConstraintsCommand) Init(args []string) (err error) { if c.ServiceName != "" && !names.IsService(c.ServiceName) { return fmt.Errorf("invalid service name %q", c.ServiceName) } c.Constraints, err = constraints.Parse(args...) return err } // setEnvironConstraints1dot16 uses direct DB access to get the Environment // constraints against an API server running 1.16 or older (when SetEnvironmentConstraints // was not available). This fallback can be removed when we no longer maintain // 1.16 compatibility. // This only does the SetEnvironmentConstraints portion of Run, since // SetServiceConstraints was already implemented. func (c *SetConstraintsCommand) setEnvironConstraints1dot16() error { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() return conn.State.SetEnvironConstraints(c.Constraints) } func (c *SetConstraintsCommand) Run(_ *cmd.Context) (err error) { apiclient, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer apiclient.Close() if c.ServiceName == "" { err = apiclient.SetEnvironmentConstraints(c.Constraints) if params.IsCodeNotImplemented(err) { logger.Infof("SetEnvironmentConstraints not supported by the API server, " + "falling back to 1.16 compatibility mode (direct DB access)") err = c.setEnvironConstraints1dot16() } return err } return apiclient.SetServiceConstraints(c.ServiceName, c.Constraints) } ����������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/constraints_test.go���������������������������0000644�0000153�0000161�00000011021�12321735642�026335� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" ) type ConstraintsCommandsSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&ConstraintsCommandsSuite{}) func runCmdLine(c *gc.C, com cmd.Command, args ...string) (code int, stdout, stderr string) { ctx := coretesting.Context(c) code = cmd.Main(com, ctx, args) stdout = ctx.Stdout.(*bytes.Buffer).String() stderr = ctx.Stderr.(*bytes.Buffer).String() c.Logf("args: %#v\ncode: %d\nstdout: %q\nstderr: %q", args, code, stdout, stderr) return } func uint64p(val uint64) *uint64 { return &val } func assertSet(c *gc.C, args ...string) { rcode, rstdout, rstderr := runCmdLine(c, &SetConstraintsCommand{}, args...) c.Assert(rcode, gc.Equals, 0) c.Assert(rstdout, gc.Equals, "") c.Assert(rstderr, gc.Equals, "") } func (s *ConstraintsCommandsSuite) TestSetEnviron(c *gc.C) { // Set constraints. assertSet(c, "mem=4G", "cpu-power=250") cons, err := s.State.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, constraints.Value{ CpuPower: uint64p(250), Mem: uint64p(4096), }) // Clear constraints. assertSet(c) cons, err = s.State.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(&cons, jc.Satisfies, constraints.IsEmpty) } func (s *ConstraintsCommandsSuite) TestSetService(c *gc.C) { svc := s.AddTestingService(c, "svc", s.AddTestingCharm(c, "dummy")) // Set constraints. assertSet(c, "-s", "svc", "mem=4G", "cpu-power=250") cons, err := svc.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, constraints.Value{ CpuPower: uint64p(250), Mem: uint64p(4096), }) // Clear constraints. assertSet(c, "-s", "svc") cons, err = svc.Constraints() c.Assert(err, gc.IsNil) c.Assert(&cons, jc.Satisfies, constraints.IsEmpty) } func assertSetError(c *gc.C, code int, stderr string, args ...string) { rcode, rstdout, rstderr := runCmdLine(c, &SetConstraintsCommand{}, args...) c.Assert(rcode, gc.Equals, code) c.Assert(rstdout, gc.Equals, "") c.Assert(rstderr, gc.Matches, "error: "+stderr+"\n") } func (s *ConstraintsCommandsSuite) TestSetErrors(c *gc.C) { assertSetError(c, 2, `invalid service name "badname-0"`, "-s", "badname-0") assertSetError(c, 2, `malformed constraint "="`, "=") assertSetError(c, 2, `malformed constraint "="`, "-s", "s", "=") assertSetError(c, 1, `service "missing" not found`, "-s", "missing") } func assertGet(c *gc.C, stdout string, args ...string) { rcode, rstdout, rstderr := runCmdLine(c, &GetConstraintsCommand{}, args...) c.Assert(rcode, gc.Equals, 0) c.Assert(rstdout, gc.Equals, stdout) c.Assert(rstderr, gc.Equals, "") } func (s *ConstraintsCommandsSuite) TestGetEnvironEmpty(c *gc.C) { assertGet(c, "") } func (s *ConstraintsCommandsSuite) TestGetEnvironValues(c *gc.C) { cons := constraints.Value{CpuCores: uint64p(64)} err := s.State.SetEnvironConstraints(cons) c.Assert(err, gc.IsNil) assertGet(c, "cpu-cores=64\n") } func (s *ConstraintsCommandsSuite) TestGetServiceEmpty(c *gc.C) { s.AddTestingService(c, "svc", s.AddTestingCharm(c, "dummy")) assertGet(c, "", "svc") } func (s *ConstraintsCommandsSuite) TestGetServiceValues(c *gc.C) { svc := s.AddTestingService(c, "svc", s.AddTestingCharm(c, "dummy")) err := svc.SetConstraints(constraints.Value{CpuCores: uint64p(64)}) c.Assert(err, gc.IsNil) assertGet(c, "cpu-cores=64\n", "svc") } func (s *ConstraintsCommandsSuite) TestGetFormats(c *gc.C) { cons := constraints.Value{CpuCores: uint64p(64), CpuPower: uint64p(0)} err := s.State.SetEnvironConstraints(cons) c.Assert(err, gc.IsNil) assertGet(c, "cpu-cores=64 cpu-power=\n", "--format", "constraints") assertGet(c, "cpu-cores: 64\ncpu-power: 0\n", "--format", "yaml") assertGet(c, `{"cpu-cores":64,"cpu-power":0}`+"\n", "--format", "json") } func assertGetError(c *gc.C, code int, stderr string, args ...string) { rcode, rstdout, rstderr := runCmdLine(c, &GetConstraintsCommand{}, args...) c.Assert(rcode, gc.Equals, code) c.Assert(rstdout, gc.Equals, "") c.Assert(rstderr, gc.Matches, "error: "+stderr+"\n") } func (s *ConstraintsCommandsSuite) TestGetErrors(c *gc.C) { assertGetError(c, 2, `invalid service name "badname-0"`, "badname-0") assertGetError(c, 2, `unrecognized args: \["blether"\]`, "goodname", "blether") assertGetError(c, 1, `service "missing" not found`, "missing") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/debuglog_test.go������������������������������0000644�0000153�0000161�00000004442�12321735776�025577� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" ) type DebugLogSuite struct { } var _ = gc.Suite(&DebugLogSuite{}) func runDebugLog(c *gc.C, args ...string) (*DebugLogCommand, error) { cmd := &DebugLogCommand{ sshCmd: &dummySSHCommand{}, } _, err := testing.RunCommand(c, cmd, args) return cmd, err } type dummySSHCommand struct { SSHCommand runCalled bool } func (c *dummySSHCommand) Run(ctx *cmd.Context) error { c.runCalled = true return nil } // debug-log is implemented by invoking juju ssh with the correct arguments. // This test helper checks for the expected invocation. func (s *DebugLogSuite) assertDebugLogInvokesSSHCommand(c *gc.C, expected string, args ...string) { defer testing.MakeEmptyFakeHome(c).Restore() debugLogCmd, err := runDebugLog(c, args...) c.Assert(err, gc.IsNil) debugCmd := debugLogCmd.sshCmd.(*dummySSHCommand) c.Assert(debugCmd.runCalled, gc.Equals, true) c.Assert(debugCmd.Target, gc.Equals, "0") c.Assert([]string{expected}, gc.DeepEquals, debugCmd.Args) } const logLocation = "/var/log/juju/all-machines.log" func (s *DebugLogSuite) TestDebugLog(c *gc.C) { const expected = "tail -n 10 -f " + logLocation s.assertDebugLogInvokesSSHCommand(c, expected) } func (s *DebugLogSuite) TestDebugLogFrom(c *gc.C) { const expected = "tail -n +1 -f " + logLocation s.assertDebugLogInvokesSSHCommand(c, expected, "-n", "+1") s.assertDebugLogInvokesSSHCommand(c, expected, "--lines=+1") } func (s *DebugLogSuite) TestDebugLogLast(c *gc.C) { const expected = "tail -n 100 -f " + logLocation s.assertDebugLogInvokesSSHCommand(c, expected, "-n", "100") s.assertDebugLogInvokesSSHCommand(c, expected, "--lines=100") } func (s *DebugLogSuite) TestDebugLogValidation(c *gc.C) { defer testing.MakeEmptyFakeHome(c).Restore() _, err := runDebugLog(c, "-n", "0") c.Assert(err, gc.ErrorMatches, "invalid value \"0\" for flag -n: invalid number of lines") _, err = runDebugLog(c, "-n", "-1") c.Assert(err, gc.ErrorMatches, "invalid value \"-1\" for flag -n: invalid number of lines") _, err = runDebugLog(c, "-n", "fnord") c.Assert(err, gc.ErrorMatches, "invalid value \"fnord\" for flag -n: invalid number of lines") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/scp.go����������������������������������������0000644�0000153�0000161�00000006331�12321735776�023534� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "strings" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/utils/ssh" ) // SCPCommand is responsible for launching a scp command to copy files to/from remote machine(s) type SCPCommand struct { SSHCommon } const scpDoc = ` Launch an scp command to copy files. Each argument <file1> ... <file2> is either local file path or remote locations of the form <target>:<path>, where <target> can be either a machine id as listed by "juju status" in the "machines" section or a unit name as listed in the "services" section. Any extra arguments to scp can be passed after at the end. In case OpenSSH scp command cannot be found in the system PATH environment variable, this command is also not available for use. Please refer to the man page of scp(1) for the supported extra arguments. Examples: Copy a single file from machine 2 to the local machine: juju scp 2:/var/log/syslog . Copy 2 files from two units to the local backup/ directory, passing -v to scp as an extra argument: 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 -r mongodb/0:/var/log/mongodb/ remote-logs/ Copy a local file to the second apache unit of the environment "testing": juju scp -e testing foo.txt apache2/1: ` func (c *SCPCommand) Info() *cmd.Info { return &cmd.Info{ Name: "scp", Args: "<file1> ... <file2> [scp-option...]", Purpose: "launch a scp command to copy files to/from remote machine(s)", Doc: scpDoc, } } func (c *SCPCommand) Init(args []string) error { if len(args) < 2 { return fmt.Errorf("at least two arguments required") } c.Args = args 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 { var err error c.apiClient, err = c.initAPIClient() if err != nil { return err } defer c.apiClient.Close() args, err := expandArgs(c.Args, c.hostFromTarget) if err != nil { return err } return ssh.Copy(args, nil) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/bootstrap_test.go�����������������������������0000644�0000153�0000161�00000057255�12321735776�026036� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "fmt" "strings" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/imagemetadata" imtesting "launchpad.net/juju-core/environs/imagemetadata/testing" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/sync" envtesting "launchpad.net/juju-core/environs/testing" envtools "launchpad.net/juju-core/environs/tools" ttesting "launchpad.net/juju-core/environs/tools/testing" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/provider/dummy" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) type BootstrapSuite struct { testbase.LoggingSuite coretesting.MgoSuite envtesting.ToolsFixture } var _ = gc.Suite(&BootstrapSuite{}) func (s *BootstrapSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *BootstrapSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) // Set up a local source with tools. sourceDir := createToolsSource(c, vAll) s.PatchValue(&envtools.DefaultBaseURL, sourceDir) } func (s *BootstrapSuite) TearDownSuite(c *gc.C) { s.MgoSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *BootstrapSuite) TearDownTest(c *gc.C) { s.ToolsFixture.TearDownTest(c) s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) dummy.Reset() } type bootstrapRetryTest struct { info string args []string expectedAllowRetry []bool err string // If version != "", version.Current will be // set to it for the duration of the test. version string // If addVersionToSource is true, then "version" // above will be populated in the tools source. addVersionToSource bool } var noToolsAvailableMessage = "cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment.*" var toolsNotFoundMessage = "cannot find bootstrap tools: tools not found" var bootstrapRetryTests = []bootstrapRetryTest{{ info: "no tools uploaded, first check has no retries; no matching binary in source; no second attempt", expectedAllowRetry: []bool{false}, err: noToolsAvailableMessage, version: "1.16.0-precise-amd64", }, { info: "no tools uploaded, first check has no retries; matching binary in source; check after upload has retries", expectedAllowRetry: []bool{false, true}, err: toolsNotFoundMessage, version: "1.17.0-precise-amd64", // dev version to force upload addVersionToSource: true, }, { info: "no tools uploaded, first check has no retries; no matching binary in source; check after upload has retries", expectedAllowRetry: []bool{false, true}, err: toolsNotFoundMessage, version: "1.15.1-precise-amd64", // dev version to force upload }, { info: "new tools uploaded, so we want to allow retries to give them a chance at showing up", args: []string{"--upload-tools"}, expectedAllowRetry: []bool{true}, err: noToolsAvailableMessage, }} // Test test checks that bootstrap calls FindTools with the expected allowRetry flag. func (s *BootstrapSuite) TestAllowRetries(c *gc.C) { for i, test := range bootstrapRetryTests { c.Logf("test %d: %s\n", i, test.info) s.runAllowRetriesTest(c, test) } } func (s *BootstrapSuite) runAllowRetriesTest(c *gc.C, test bootstrapRetryTest) { toolsVersions := envtesting.VAll if test.version != "" { useVersion := strings.Replace(test.version, "%LTS%", config.LatestLtsSeries(), 1) testVersion := version.MustParseBinary(useVersion) s.PatchValue(&version.Current, testVersion) if test.addVersionToSource { toolsVersions = append([]version.Binary{}, toolsVersions...) toolsVersions = append(toolsVersions, testVersion) } } _, fake := makeEmptyFakeHome(c) defer fake.Restore() sourceDir := createToolsSource(c, toolsVersions) s.PatchValue(&envtools.DefaultBaseURL, sourceDir) var findToolsRetryValues []bool mockFindTools := func(cloudInst environs.ConfigGetter, majorVersion, minorVersion int, filter coretools.Filter, allowRetry bool) (list coretools.List, err error) { findToolsRetryValues = append(findToolsRetryValues, allowRetry) return nil, errors.NotFoundf("tools") } restore := envtools.TestingPatchBootstrapFindTools(mockFindTools) defer restore() _, errc := runCommand(nullContext(c), new(BootstrapCommand), test.args...) err := <-errc c.Check(findToolsRetryValues, gc.DeepEquals, test.expectedAllowRetry) stripped := strings.Replace(err.Error(), "\n", "", -1) c.Check(stripped, gc.Matches, test.err) } // mockUploadTools simulates the effect of tools.Upload, but skips the time- // consuming build from source. // TODO(fwereade) better factor agent/tools such that build logic is // exposed and can itself be neatly mocked? func mockUploadTools(stor storage.Storage, forceVersion *version.Number, series ...string) (*coretools.Tools, error) { vers := version.Current if forceVersion != nil { vers.Number = *forceVersion } versions := []version.Binary{vers} for _, series := range series { if series != version.Current.Series { newVers := vers newVers.Series = series versions = append(versions, newVers) } } agentTools, err := envtesting.UploadFakeToolsVersions(stor, versions...) if err != nil { return nil, err } return agentTools[0], nil } func (s *BootstrapSuite) TestTest(c *gc.C) { s.PatchValue(&sync.Upload, mockUploadTools) for i, test := range bootstrapTests { c.Logf("\ntest %d: %s", i, test.info) test.run(c) } } type bootstrapTest struct { info string // binary version string used to set version.Current version string sync bool args []string err string // binary version strings for expected tools; if set, no default tools // will be uploaded before running the test. uploads []string constraints constraints.Value hostArch string } func (test bootstrapTest) run(c *gc.C) { // Create home with dummy provider and remove all // of its envtools. env, fake := makeEmptyFakeHome(c) defer fake.Restore() if test.version != "" { useVersion := strings.Replace(test.version, "%LTS%", config.LatestLtsSeries(), 1) origVersion := version.Current version.Current = version.MustParseBinary(useVersion) defer func() { version.Current = origVersion }() } if test.hostArch != "" { origVersion := arch.HostArch arch.HostArch = func() string { return test.hostArch } defer func() { arch.HostArch = origVersion }() } uploadCount := len(test.uploads) if uploadCount == 0 { usefulVersion := version.Current usefulVersion.Series = config.PreferredSeries(env.Config()) envtesting.AssertUploadFakeToolsVersions(c, env.Storage(), usefulVersion) } // Run command and check for uploads. opc, errc := runCommand(nullContext(c), new(BootstrapCommand), test.args...) // Check for remaining operations/errors. if test.err != "" { err := <-errc stripped := strings.Replace(err.Error(), "\n", "", -1) c.Check(stripped, gc.Matches, test.err) return } if !c.Check(<-errc, gc.IsNil) { return } if uploadCount > 0 { for i := 0; i < uploadCount; i++ { c.Check((<-opc).(dummy.OpPutFile).Env, gc.Equals, "peckham") } list, err := envtools.FindTools( env, version.Current.Major, version.Current.Minor, coretools.Filter{}, envtools.DoNotAllowRetry) c.Check(err, gc.IsNil) c.Logf("found: " + list.String()) urls := list.URLs() c.Check(urls, gc.HasLen, len(test.uploads)) for _, v := range test.uploads { v := strings.Replace(v, "%LTS%", config.LatestLtsSeries(), 1) c.Logf("seeking: " + v) vers := version.MustParseBinary(v) _, found := urls[vers] c.Check(found, gc.Equals, true) } } if len(test.uploads) > 0 { indexFile := (<-opc).(dummy.OpPutFile) c.Check(indexFile.FileName, gc.Equals, "tools/streams/v1/index.json") productFile := (<-opc).(dummy.OpPutFile) c.Check(productFile.FileName, gc.Equals, "tools/streams/v1/com.ubuntu.juju:released:tools.json") } opPutBootstrapVerifyFile := (<-opc).(dummy.OpPutFile) c.Check(opPutBootstrapVerifyFile.Env, gc.Equals, "peckham") c.Check(opPutBootstrapVerifyFile.FileName, gc.Equals, environs.VerificationFilename) opPutBootstrapInitFile := (<-opc).(dummy.OpPutFile) c.Check(opPutBootstrapInitFile.Env, gc.Equals, "peckham") c.Check(opPutBootstrapInitFile.FileName, gc.Equals, "provider-state") opBootstrap := (<-opc).(dummy.OpBootstrap) c.Check(opBootstrap.Env, gc.Equals, "peckham") c.Check(opBootstrap.Constraints, gc.DeepEquals, test.constraints) store, err := configstore.Default() c.Assert(err, gc.IsNil) // Check a CA cert/key was generated by reloading the environment. env, err = environs.NewFromName("peckham", store) c.Assert(err, gc.IsNil) _, hasCert := env.Config().CACert() c.Check(hasCert, gc.Equals, true) _, hasKey := env.Config().CAPrivateKey() c.Check(hasKey, gc.Equals, true) } var bootstrapTests = []bootstrapTest{{ info: "no args, no error, no uploads, no constraints", }, { info: "bad arg", args: []string{"twiddle"}, err: `unrecognized args: \["twiddle"\]`, }, { info: "bad --constraints", args: []string{"--constraints", "bad=wrong"}, err: `invalid value "bad=wrong" for flag --constraints: unknown constraint "bad"`, }, { info: "bad --series", args: []string{"--series", "1bad1"}, err: `invalid value "1bad1" for flag --series: invalid series name "1bad1"`, }, { info: "lonely --series", args: []string{"--series", "fine"}, err: `--series requires --upload-tools`, }, { info: "bad environment", version: "1.2.3-%LTS%-amd64", args: []string{"-e", "brokenenv"}, err: `dummy.Bootstrap is broken`, }, { info: "constraints", args: []string{"--constraints", "mem=4G cpu-cores=4"}, constraints: constraints.MustParse("mem=4G cpu-cores=4"), }, { info: "--upload-tools picks all reasonable series", version: "1.2.3-saucy-amd64", args: []string{"--upload-tools"}, uploads: []string{ "1.2.3.1-saucy-amd64", // from version.Current "1.2.3.1-raring-amd64", // from env.Config().DefaultSeries() "1.2.3.1-%LTS%-amd64", // from environs/config.DefaultSeries }, }, { info: "--upload-tools uses arch from constraint if it matches current version", version: "1.3.3-saucy-ppc64", hostArch: "ppc64", args: []string{"--upload-tools", "--constraints", "arch=ppc64"}, uploads: []string{ "1.3.3.1-saucy-ppc64", // from version.Current "1.3.3.1-raring-ppc64", // from env.Config().DefaultSeries() "1.3.3.1-%LTS%-ppc64", // from environs/config.DefaultSeries }, constraints: constraints.MustParse("arch=ppc64"), }, { info: "--upload-tools only uploads each file once", version: "1.2.3-%LTS%-amd64", args: []string{"--upload-tools"}, uploads: []string{ "1.2.3.1-raring-amd64", "1.2.3.1-%LTS%-amd64", }, }, { info: "--upload-tools rejects invalid series", version: "1.2.3-saucy-amd64", args: []string{"--upload-tools", "--series", "ping,ping,pong"}, err: `invalid series "ping"`, }, { info: "--upload-tools rejects mismatched arch", version: "1.3.3-saucy-amd64", args: []string{"--upload-tools", "--constraints", "arch=ppc64"}, err: `cannot build tools for "ppc64" using a machine running on "amd64"`, }, { info: "--upload-tools rejects non-supported arch", version: "1.3.3-saucy-arm64", hostArch: "arm64", args: []string{"--upload-tools"}, err: `environment "peckham" of type dummy does not support instances running on "arm64"`, }, { info: "--upload-tools always bumps build number", version: "1.2.3.4-raring-amd64", args: []string{"--upload-tools"}, uploads: []string{ "1.2.3.5-raring-amd64", "1.2.3.5-%LTS%-amd64", }, }} func (s *BootstrapSuite) TestBootstrapTwice(c *gc.C) { env, fake := makeEmptyFakeHome(c) defer fake.Restore() defaultSeriesVersion := version.Current defaultSeriesVersion.Series = config.PreferredSeries(env.Config()) // Force a dev version by having an odd minor version number. // This is because we have not uploaded any tools and auto // upload is only enabled for dev versions. defaultSeriesVersion.Minor = 11 s.PatchValue(&version.Current, defaultSeriesVersion) ctx := coretesting.Context(c) code := cmd.Main(&BootstrapCommand{}, ctx, nil) c.Check(code, gc.Equals, 0) ctx2 := coretesting.Context(c) code2 := cmd.Main(&BootstrapCommand{}, ctx2, nil) c.Check(code2, gc.Equals, 1) expectedErrText := "Bootstrap failed, destroying environment\n" expectedErrText += "error: environment is already bootstrapped\n" c.Check(coretesting.Stderr(ctx2), gc.Equals, expectedErrText) c.Check(coretesting.Stdout(ctx2), gc.Equals, "") } func (s *BootstrapSuite) TestBootstrapJenvWarning(c *gc.C) { env, fake := makeEmptyFakeHome(c) defer fake.Restore() defaultSeriesVersion := version.Current defaultSeriesVersion.Series = config.PreferredSeries(env.Config()) // Force a dev version by having an odd minor version number. // This is because we have not uploaded any tools and auto // upload is only enabled for dev versions. defaultSeriesVersion.Minor = 11 s.PatchValue(&version.Current, defaultSeriesVersion) store, err := configstore.Default() c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) environs.PrepareFromName("peckham", ctx, store) logger := "jenv.warning.test" testWriter := &loggo.TestWriter{} loggo.RegisterWriter(logger, testWriter, loggo.WARNING) defer loggo.RemoveWriter(logger) _, errc := runCommand(ctx, new(BootstrapCommand), "-e", "peckham") c.Assert(<-errc, gc.IsNil) c.Assert(testWriter.Log, jc.LogMatches, []string{"ignoring environments.yaml: using bootstrap config in .*"}) } func (s *BootstrapSuite) TestInvalidLocalSource(c *gc.C) { s.PatchValue(&version.Current.Number, version.MustParse("1.2.0")) env, fake := makeEmptyFakeHome(c) defer fake.Restore() // Bootstrap the environment with an invalid source. // The command returns with an error. ctx := coretesting.Context(c) code := cmd.Main(&BootstrapCommand{}, ctx, []string{"--metadata-source", c.MkDir()}) c.Check(code, gc.Equals, 1) // Now check that there are no tools available. _, err := envtools.FindTools( env, version.Current.Major, version.Current.Minor, coretools.Filter{}, envtools.DoNotAllowRetry) c.Assert(err, gc.FitsTypeOf, errors.NotFoundf("")) } // createImageMetadata creates some image metadata in a local directory. func createImageMetadata(c *gc.C) (string, []*imagemetadata.ImageMetadata) { // Generate some image metadata. im := []*imagemetadata.ImageMetadata{ { Id: "1234", Arch: "amd64", Version: "13.04", RegionName: "region", Endpoint: "endpoint", }, } cloudSpec := &simplestreams.CloudSpec{ Region: "region", Endpoint: "endpoint", } sourceDir := c.MkDir() sourceStor, err := filestorage.NewFileStorageWriter(sourceDir) c.Assert(err, gc.IsNil) err = imagemetadata.MergeAndWriteMetadata("raring", im, cloudSpec, sourceStor) c.Assert(err, gc.IsNil) return sourceDir, im } // checkImageMetadata checks that the environment contains the expected image metadata. func checkImageMetadata(c *gc.C, stor storage.StorageReader, expected []*imagemetadata.ImageMetadata) { metadata := imtesting.ParseMetadataFromStorage(c, stor) c.Assert(metadata, gc.HasLen, 1) c.Assert(expected[0], gc.DeepEquals, metadata[0]) } func (s *BootstrapSuite) TestUploadLocalImageMetadata(c *gc.C) { sourceDir, expected := createImageMetadata(c) env, fake := makeEmptyFakeHome(c) defer fake.Restore() // Bootstrap the environment with the valid source. // Force a dev version by having an odd minor version number. // This is because we have not uploaded any tools and auto // upload is only enabled for dev versions. devVersion := version.Current devVersion.Minor = 11 s.PatchValue(&version.Current, devVersion) ctx := coretesting.Context(c) code := cmd.Main(&BootstrapCommand{}, ctx, []string{"--metadata-source", sourceDir}) c.Check(code, gc.Equals, 0) c.Assert(imagemetadata.DefaultBaseURL, gc.Equals, imagemetadata.UbuntuCloudImagesURL) // Now check the image metadata has been uploaded. checkImageMetadata(c, env.Storage(), expected) } func (s *BootstrapSuite) TestAutoSyncLocalSource(c *gc.C) { sourceDir := createToolsSource(c, vAll) s.PatchValue(&version.Current.Number, version.MustParse("1.2.0")) env, fake := makeEmptyFakeHome(c) defer fake.Restore() // Bootstrap the environment with the valid source. // The bootstrapping has to show no error, because the tools // are automatically synchronized. ctx := coretesting.Context(c) code := cmd.Main(&BootstrapCommand{}, ctx, []string{"--metadata-source", sourceDir}) c.Check(code, gc.Equals, 0) // Now check the available tools which are the 1.2.0 envtools. checkTools(c, env, v120All) } func (s *BootstrapSuite) setupAutoUploadTest(c *gc.C, vers, series string) environs.Environ { s.PatchValue(&sync.Upload, mockUploadTools) sourceDir := createToolsSource(c, vAll) s.PatchValue(&envtools.DefaultBaseURL, sourceDir) // Change the tools location to be the test location and also // the version and ensure their later restoring. // Set the current version to be something for which there are no tools // so we can test that an upload is forced. origVersion := version.Current version.Current.Number = version.MustParse(vers) version.Current.Series = series s.AddCleanup(func(*gc.C) { version.Current = origVersion }) // Create home with dummy provider and remove all // of its envtools. env, fake := makeEmptyFakeHome(c) s.AddCleanup(func(*gc.C) { fake.Restore() }) return env } func (s *BootstrapSuite) TestAutoUploadAfterFailedSync(c *gc.C) { otherSeries := "precise" if otherSeries == version.Current.Series { otherSeries = "raring" } env := s.setupAutoUploadTest(c, "1.7.3", otherSeries) // Run command and check for that upload has been run for tools matching the current juju version. opc, errc := runCommand(nullContext(c), new(BootstrapCommand)) c.Assert(<-errc, gc.IsNil) c.Assert((<-opc).(dummy.OpPutFile).Env, gc.Equals, "peckham") list, err := envtools.FindTools(env, version.Current.Major, version.Current.Minor, coretools.Filter{}, false) c.Assert(err, gc.IsNil) c.Logf("found: " + list.String()) urls := list.URLs() expectedUrlCount := 2 // There will be distinct tools for each of these if they are different if config.LatestLtsSeries() != coretesting.FakeDefaultSeries { expectedUrlCount++ } c.Assert(urls, gc.HasLen, expectedUrlCount) expectedVers := []version.Binary{ version.MustParseBinary(fmt.Sprintf("1.7.3.1-%s-%s", otherSeries, version.Current.Arch)), version.MustParseBinary(fmt.Sprintf("1.7.3.1-%s-%s", version.Current.Series, version.Current.Arch)), } for _, vers := range expectedVers { c.Logf("seeking: " + vers.String()) _, found := urls[vers] c.Check(found, gc.Equals, true) } } func (s *BootstrapSuite) TestAutoUploadOnlyForDev(c *gc.C) { s.setupAutoUploadTest(c, "1.8.3", "precise") _, errc := runCommand(nullContext(c), new(BootstrapCommand)) err := <-errc stripped := strings.Replace(err.Error(), "\n", "", -1) c.Assert(stripped, gc.Matches, noToolsAvailableMessage) } func (s *BootstrapSuite) TestMissingToolsError(c *gc.C) { s.setupAutoUploadTest(c, "1.8.3", "precise") context := coretesting.Context(c) code := cmd.Main(&BootstrapCommand{}, context, nil) c.Assert(code, gc.Equals, 1) errText := context.Stderr.(*bytes.Buffer).String() expectedErrText := "Bootstrap failed, destroying environment\n" expectedErrText += "error: cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment(.|\n)*" c.Assert(errText, gc.Matches, expectedErrText) } func uploadToolsAlwaysFails(stor storage.Storage, forceVersion *version.Number, series ...string) (*coretools.Tools, error) { return nil, fmt.Errorf("an error") } func (s *BootstrapSuite) TestMissingToolsUploadFailedError(c *gc.C) { s.setupAutoUploadTest(c, "1.7.3", "precise") s.PatchValue(&sync.Upload, uploadToolsAlwaysFails) context := coretesting.Context(c) code := cmd.Main(&BootstrapCommand{}, context, nil) c.Assert(code, gc.Equals, 1) errText := context.Stderr.(*bytes.Buffer).String() expectedErrText := "uploading tools for series \\[precise " if config.LatestLtsSeries() != coretesting.FakeDefaultSeries { expectedErrText += config.LatestLtsSeries() + " " } expectedErrText += "raring\\]\n" expectedErrText += "Bootstrap failed, destroying environment\n" expectedErrText += "error: cannot upload bootstrap tools: an error\n" c.Assert(errText, gc.Matches, expectedErrText) } func (s *BootstrapSuite) TestBootstrapDestroy(c *gc.C) { _, fake := makeEmptyFakeHome(c) defer fake.Restore() devVersion := version.Current // Force a dev version by having an odd minor version number. // This is because we have not uploaded any tools and auto // upload is only enabled for dev versions. devVersion.Minor = 11 s.PatchValue(&version.Current, devVersion) opc, errc := runCommand(nullContext(c), new(BootstrapCommand), "-e", "brokenenv") err := <-errc c.Assert(err, gc.ErrorMatches, "dummy.Bootstrap is broken") var opDestroy *dummy.OpDestroy for opDestroy == nil { select { case op := <-opc: switch op := op.(type) { case dummy.OpDestroy: opDestroy = &op } default: c.Error("expected call to env.Destroy") return } } c.Assert(opDestroy.Error, gc.ErrorMatches, "dummy.Destroy is broken") } // createToolsSource writes the mock tools and metadata into a temporary // directory and returns it. func createToolsSource(c *gc.C, versions []version.Binary) string { versionStrings := make([]string, len(versions)) for i, vers := range versions { versionStrings[i] = vers.String() } source := c.MkDir() ttesting.MakeTools(c, source, "releases", versionStrings) return source } // makeEmptyFakeHome creates a faked home without envtools. func makeEmptyFakeHome(c *gc.C) (environs.Environ, *coretesting.FakeHome) { fake := coretesting.MakeFakeHome(c, envConfig) dummy.Reset() store, err := configstore.Default() c.Assert(err, gc.IsNil) env, err := environs.PrepareFromName("peckham", nullContext(c), store) c.Assert(err, gc.IsNil) envtesting.RemoveAllTools(c, env) return env, fake } // checkTools check if the environment contains the passed envtools. func checkTools(c *gc.C, env environs.Environ, expected []version.Binary) { list, err := envtools.FindTools( env, version.Current.Major, version.Current.Minor, coretools.Filter{}, envtools.DoNotAllowRetry) c.Check(err, gc.IsNil) c.Logf("found: " + list.String()) urls := list.URLs() c.Check(urls, gc.HasLen, len(expected)) } var ( v100d64 = version.MustParseBinary("1.0.0-raring-amd64") v100p64 = version.MustParseBinary("1.0.0-precise-amd64") v100q32 = version.MustParseBinary("1.0.0-quantal-i386") v100q64 = version.MustParseBinary("1.0.0-quantal-amd64") v120d64 = version.MustParseBinary("1.2.0-raring-amd64") v120p64 = version.MustParseBinary("1.2.0-precise-amd64") v120q32 = version.MustParseBinary("1.2.0-quantal-i386") v120q64 = version.MustParseBinary("1.2.0-quantal-amd64") v190p32 = version.MustParseBinary("1.9.0-precise-i386") v190q64 = version.MustParseBinary("1.9.0-quantal-amd64") v200p64 = version.MustParseBinary("2.0.0-precise-amd64") v100All = []version.Binary{ v100d64, v100p64, v100q64, v100q32, } v120All = []version.Binary{ v120d64, v120p64, v120q64, v120q32, } vAll = []version.Binary{ v100d64, v100p64, v100q32, v100q64, v120d64, v120p64, v120q32, v120q64, v190p32, v190q64, v200p64, } ) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/helptool_test.go������������������������������0000644�0000153�0000161�00000002452�12321735642�025624� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "strings" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/jujuc" ) type HelpToolSuite struct { home *testing.FakeHome } var _ = gc.Suite(&HelpToolSuite{}) func (suite *HelpToolSuite) SetUpTest(c *gc.C) { suite.home = testing.MakeSampleHome(c) } func (suite *HelpToolSuite) TearDownTest(c *gc.C) { suite.home.Restore() } func (suite *HelpToolSuite) TestHelpToolHelp(c *gc.C) { output := badrun(c, 0, "help", "help-tool") c.Assert(output, gc.Equals, `usage: juju help-tool [tool] purpose: show help on a juju charm tool `) } func (suite *HelpToolSuite) TestHelpTool(c *gc.C) { expectedNames := jujuc.CommandNames() output := badrun(c, 0, "help-tool") lines := strings.Split(strings.TrimSpace(output), "\n") for i, line := range lines { lines[i] = strings.Fields(line)[0] } c.Assert(lines, gc.DeepEquals, expectedNames) } func (suite *HelpToolSuite) TestHelpToolName(c *gc.C) { output := badrun(c, 0, "help-tool", "relation-get") expectedHelp := `usage: relation-get \[options\] <key> <unit id> purpose: get relation settings options: (.|\n)* relation-get prints the value(.|\n)*` c.Assert(output, gc.Matches, expectedHelp) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/upgradejuju.go��������������������������������0000644�0000153�0000161�00000036412�12321735776�025277� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( stderrors "errors" "fmt" "os" "path" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/sync" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) // UpgradeJujuCommand upgrades the agents in a juju installation. type UpgradeJujuCommand struct { cmd.EnvCommandBase vers string Version version.Number UploadTools bool Series []string } var upgradeJujuDoc = ` The upgrade-juju command upgrades a running environment by setting a version number for all juju agents to run. By default, it chooses the most recent supported version compatible with the command-line tools version. A development version is defined to be any version with an odd minor version or a nonzero build component (for example version 2.1.1, 3.3.0 and 2.0.0.1 are development versions; 2.0.3 and 3.4.1 are not). A development version may be chosen in two cases: - when the current agent version is a development one and there is a more recent version available with the same major.minor numbers; - when an explicit --version major.minor is given (e.g. --version 1.17, or 1.17.2, but not just 1) For development use, the --upload-tools flag specifies that the juju tools will packaged (or compiled locally, if no jujud binaries exists, for which you will need the golang packages installed) and uploaded before the version is set. Currently the tools will be uploaded as if they had the version of the current juju tool, unless specified otherwise by the --version flag. When run without arguments. upgrade-juju will try to upgrade to the following versions, in order of preference, depending on the current value of the environment's agent-version setting: - The highest patch.build version of the *next* stable major.minor version. - The highest patch.build version of the *current* major.minor version. Both of these depend on tools availability, which some situations (no outgoing internet access) and provider types (such as maas) require that you manage yourself; see the documentation for "sync-tools". ` func (c *UpgradeJujuCommand) Info() *cmd.Info { return &cmd.Info{ Name: "upgrade-juju", Purpose: "upgrade the tools in a juju environment", Doc: upgradeJujuDoc, } } func (c *UpgradeJujuCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.StringVar(&c.vers, "version", "", "upgrade to specific version") f.BoolVar(&c.UploadTools, "upload-tools", false, "upload local version of tools") f.Var(seriesVar{&c.Series}, "series", "upload tools for supplied comma-separated series list") } func (c *UpgradeJujuCommand) Init(args []string) error { if c.vers != "" { vers, err := version.Parse(c.vers) if err != nil { return err } if vers.Major != version.Current.Major { return fmt.Errorf("cannot upgrade to version incompatible with CLI") } if c.UploadTools && vers.Build != 0 { // TODO(fwereade): when we start taking versions from actual built // code, we should disable --version when used with --upload-tools. // For now, it's the only way to experiment with version upgrade // behaviour live, so the only restriction is that Build cannot // be used (because its value needs to be chosen internally so as // not to collide with existing tools). return fmt.Errorf("cannot specify build number when uploading tools") } c.Version = vers } if len(c.Series) > 0 && !c.UploadTools { return fmt.Errorf("--series requires --upload-tools") } return cmd.CheckEmpty(args) } var errUpToDate = stderrors.New("no upgrades available") // Run changes the version proposed for the juju envtools. func (c *UpgradeJujuCommand) Run(_ *cmd.Context) (err error) { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() defer func() { if err == errUpToDate { logger.Infof(err.Error()) err = nil } }() // Determine the version to upgrade to, uploading tools if necessary. attrs, err := client.EnvironmentGet() if params.IsCodeNotImplemented(err) { return c.run1dot16() } if err != nil { return err } cfg, err := config.New(config.NoDefaults, attrs) if err != nil { return err } context, err := c.initVersions(client, cfg) if err != nil { return err } if c.UploadTools { series := bootstrap.SeriesToUpload(cfg, c.Series) if err := context.uploadTools(series); err != nil { return err } } if err := context.validate(); err != nil { return err } logger.Infof("upgrade version chosen: %s", context.chosen) // TODO(fwereade): this list may be incomplete, pending envtools.Upload change. logger.Infof("available tools: %s", context.tools) if err := client.SetEnvironAgentVersion(context.chosen); err != nil { return err } logger.Infof("started upgrade to %s", context.chosen) return nil } // initVersions collects state relevant to an upgrade decision. The returned // agent and client versions, and the list of currently available tools, will // always be accurate; the chosen version, and the flag indicating development // mode, may remain blank until uploadTools or validate is called. func (c *UpgradeJujuCommand) initVersions(client *api.Client, cfg *config.Config) (*upgradeContext, error) { agent, ok := cfg.AgentVersion() if !ok { // Can't happen. In theory. return nil, fmt.Errorf("incomplete environment configuration") } if c.Version == agent { return nil, errUpToDate } clientVersion := version.Current.Number findResult, err := client.FindTools(clientVersion.Major, -1, "", "") var availableTools coretools.List if params.IsCodeNotImplemented(err) { availableTools, err = findTools1dot17(cfg) } else { availableTools = findResult.List } if err != nil { return nil, err } err = findResult.Error if findResult.Error != nil { if !params.IsCodeNotFound(err) { return nil, err } if !c.UploadTools { // No tools found and we shouldn't upload any, so if we are not asking for a // major upgrade, pretend there is no more recent version available. if c.Version == version.Zero && agent.Major == clientVersion.Major { return nil, errUpToDate } return nil, err } } return &upgradeContext{ agent: agent, client: clientVersion, chosen: c.Version, tools: availableTools, apiClient: client, config: cfg, }, nil } // findTools1dot17 allows 1.17.x versions to be upgraded. func findTools1dot17(cfg *config.Config) (coretools.List, error) { logger.Warningf("running find tools in 1.17 compatibility mode") env, err := environs.New(cfg) if err != nil { return nil, err } clientVersion := version.Current.Number return envtools.FindTools(env, clientVersion.Major, -1, coretools.Filter{}, envtools.DoNotAllowRetry) } // upgradeContext holds the version information for making upgrade decisions. type upgradeContext struct { agent version.Number client version.Number chosen version.Number tools coretools.List config *config.Config apiClient *api.Client } // uploadTools compiles jujud from $GOPATH and uploads it into the supplied // storage. If no version has been explicitly chosen, the version number // reported by the built tools will be based on the client version number. // In any case, the version number reported will have a build component higher // than that of any otherwise-matching available envtools. // uploadTools resets the chosen version and replaces the available tools // with the ones just uploaded. func (context *upgradeContext) uploadTools(series []string) (err error) { // TODO(fwereade): this is kinda crack: we should not assume that // version.Current matches whatever source happens to be built. The // ideal would be: // 1) compile jujud from $GOPATH into some build dir // 2) get actual version with `jujud version` // 3) check actual version for compatibility with CLI tools // 4) generate unique build version with reference to available tools // 5) force-version that unique version into the dir directly // 6) archive and upload the build dir // ...but there's no way we have time for that now. In the meantime, // considering the use cases, this should work well enough; but it // won't detect an incompatible major-version change, which is a shame. if context.chosen == version.Zero { context.chosen = context.client } context.chosen = uploadVersion(context.chosen, context.tools) builtTools, err := sync.BuildToolsTarball(&context.chosen) if err != nil { return err } defer os.RemoveAll(builtTools.Dir) var uploaded *coretools.Tools toolsPath := path.Join(builtTools.Dir, builtTools.StorageName) logger.Infof("uploading tools %v (%dkB) to Juju state server", builtTools.Version, (builtTools.Size+512)/1024) uploaded, err = context.apiClient.UploadTools(toolsPath, builtTools.Version, series...) if params.IsCodeNotImplemented(err) { uploaded, err = context.uploadTools1dot17(builtTools, series...) } if err != nil { return err } context.tools = coretools.List{uploaded} return nil } func (context *upgradeContext) uploadTools1dot17(builtTools *sync.BuiltTools, series ...string) (*coretools.Tools, error) { logger.Warningf("running upload tools in 1.17 compatibility mode") env, err := environs.New(context.config) if err != nil { return nil, err } return sync.SyncBuiltTools(env.Storage(), builtTools, series...) } // validate chooses an upgrade version, if one has not already been chosen, // and ensures the tools list contains no entries that do not have that version. // If validate returns no error, the environment agent-version can be set to // the value of the chosen field. func (context *upgradeContext) validate() (err error) { if context.chosen == version.Zero { // No explicitly specified version, so find the version to which we // need to upgrade. If the CLI and agent major versions match, we find // next available stable release to upgrade to by incrementing the // minor version, starting from the current agent version and doing // major.minor+1 or +2 as needed. If the CLI has a greater major version, // we just use the CLI version as is. nextVersion := context.agent if nextVersion.Major == context.client.Major { if context.agent.IsDev() { nextVersion.Minor += 1 } else { nextVersion.Minor += 2 } } else { nextVersion = context.client } newestNextStable, found := context.tools.NewestCompatible(nextVersion) if found { logger.Debugf("found a more recent stable version %s", newestNextStable) context.chosen = newestNextStable } else { newestCurrent, found := context.tools.NewestCompatible(context.agent) if found { logger.Debugf("found more recent current version %s", newestCurrent) context.chosen = newestCurrent } else { if context.agent.Major != context.client.Major { return fmt.Errorf("no compatible tools available") } else { return fmt.Errorf("no more recent supported versions available") } } } } else { // If not completely specified already, pick a single tools version. filter := coretools.Filter{Number: context.chosen, Released: !context.chosen.IsDev()} if context.tools, err = context.tools.Match(filter); err != nil { return err } context.chosen, context.tools = context.tools.Newest() } if context.chosen == context.agent { return errUpToDate } // Disallow major.minor version downgrades. if context.chosen.Major < context.agent.Major || context.chosen.Major == context.agent.Major && context.chosen.Minor < context.agent.Minor { // TODO(fwereade): I'm a bit concerned about old agent/CLI tools even // *connecting* to environments with higher agent-versions; but ofc they // have to connect in order to discover they shouldn't. However, once // any of our tools detect an incompatible version, they should act to // minimize damage: the CLI should abort politely, and the agents should // run an Upgrader but no other tasks. return fmt.Errorf("cannot change version from %s to %s", context.agent, context.chosen) } return nil } // uploadVersion returns a copy of the supplied version with a build number // higher than any of the supplied tools that share its major, minor and patch. func uploadVersion(vers version.Number, existing coretools.List) version.Number { vers.Build++ for _, t := range existing { if t.Version.Major != vers.Major || t.Version.Minor != vers.Minor || t.Version.Patch != vers.Patch { continue } if t.Version.Build >= vers.Build { vers.Build = t.Version.Build + 1 } } return vers } // run1dot16 implements the command without access to the API. This is // needed for compatibility, so 1.16 can be upgraded to newer // releases. It should be removed in 1.18. func (c *UpgradeJujuCommand) run1dot16() error { logger.Warningf("running in 1.16 compatibility mode") conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() defer func() { if err == errUpToDate { logger.Infof(err.Error()) err = nil } }() // Determine the version to upgrade to, uploading tools if necessary. env := conn.Environ cfg, err := conn.State.EnvironConfig() if err != nil { return err } context, err := c.initVersions1dot16(cfg, env) if err != nil { return err } if c.UploadTools { series := bootstrap.SeriesToUpload(cfg, c.Series) if err := context.uploadTools1dot16(env.Storage(), series); err != nil { return err } } if err := context.validate(); err != nil { return err } logger.Infof("upgrade version chosen: %s", context.chosen) logger.Infof("available tools: %s", context.tools) if err := conn.State.SetEnvironAgentVersion(context.chosen); err != nil { return err } logger.Infof("started upgrade to %s", context.chosen) return nil } func (c *UpgradeJujuCommand) initVersions1dot16(cfg *config.Config, env environs.Environ) (*upgradeContext, error) { agent, ok := cfg.AgentVersion() if !ok { // Can't happen. In theory. return nil, fmt.Errorf("incomplete environment configuration") } if c.Version == agent { return nil, errUpToDate } client := version.Current.Number available, err := envtools.FindTools(env, client.Major, -1, coretools.Filter{}, envtools.DoNotAllowRetry) if err != nil { if !errors.IsNotFoundError(err) { return nil, err } if !c.UploadTools { // No tools found and we shouldn't upload any, so if we are not asking for a // major upgrade, pretend there is no more recent version available. if c.Version == version.Zero && agent.Major == client.Major { return nil, errUpToDate } return nil, err } } return &upgradeContext{ agent: agent, client: client, chosen: c.Version, tools: available, }, nil } func (context *upgradeContext) uploadTools1dot16(storage storage.Storage, series []string) error { if context.chosen == version.Zero { context.chosen = context.client } context.chosen = uploadVersion(context.chosen, context.tools) uploaded, err := sync.Upload(storage, &context.chosen, series...) if err != nil { return err } context.tools = coretools.List{uploaded} return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/destroyrelation_test.go�����������������������0000644�0000153�0000161�00000002506�12321735642�027225� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/testing" ) type DestroyRelationSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&DestroyRelationSuite{}) func runDestroyRelation(c *gc.C, args ...string) error { _, err := testing.RunCommand(c, &DestroyRelationCommand{}, args) return err } func (s *DestroyRelationSuite) TestDestroyRelation(c *gc.C) { testing.Charms.BundlePath(s.SeriesPath, "riak") err := runDeploy(c, "local:riak", "riak") c.Assert(err, gc.IsNil) testing.Charms.BundlePath(s.SeriesPath, "logging") err = runDeploy(c, "local:logging", "logging") c.Assert(err, gc.IsNil) runAddRelation(c, "riak", "logging") // Destroy a relation that exists. err = runDestroyRelation(c, "logging", "riak") c.Assert(err, gc.IsNil) // Destroy a relation that used to exist. err = runDestroyRelation(c, "riak", "logging") c.Assert(err, gc.ErrorMatches, `relation "logging:info riak:juju-info" not found`) // Invalid removes. err = runDestroyRelation(c, "ping", "pong") c.Assert(err, gc.ErrorMatches, `service "ping" not found`) err = runDestroyRelation(c, "riak") c.Assert(err, gc.ErrorMatches, `a relation must involve two services`) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/expose_test.go��������������������������������0000644�0000153�0000161�00000002241�12321735642�025275� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/testing" ) type ExposeSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&ExposeSuite{}) func runExpose(c *gc.C, args ...string) error { _, err := testing.RunCommand(c, &ExposeCommand{}, args) return err } func (s *ExposeSuite) assertExposed(c *gc.C, service string) { svc, err := s.State.Service(service) c.Assert(err, gc.IsNil) exposed := svc.IsExposed() c.Assert(exposed, gc.Equals, true) } func (s *ExposeSuite) TestExpose(c *gc.C) { testing.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "some-service-name") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "some-service-name", curl, 1, 0) err = runExpose(c, "some-service-name") c.Assert(err, gc.IsNil) s.assertExposed(c, "some-service-name") err = runExpose(c, "nonexistent-service") c.Assert(err, gc.ErrorMatches, `service "nonexistent-service" not found`) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/run_test.go�����������������������������������0000644�0000153�0000161�00000027024�12321735776�024614� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils/exec" ) type RunSuite struct { testing.FakeHomeSuite } var _ = gc.Suite(&RunSuite{}) func (*RunSuite) TestTargetArgParsing(c *gc.C) { for i, test := range []struct { message string args []string all bool machines []string units []string services []string commands string errMatch string }{{ message: "no args", errMatch: "no commands specified", }, { message: "no target", args: []string{"sudo reboot"}, errMatch: "You must specify a target, either through --all, --machine, --service or --unit", }, { message: "too many args", args: []string{"--all", "sudo reboot", "oops"}, errMatch: `unrecognized args: \["oops"\]`, }, { message: "command to all machines", args: []string{"--all", "sudo reboot"}, all: true, commands: "sudo reboot", }, { message: "all and defined machines", args: []string{"--all", "--machine=1,2", "sudo reboot"}, errMatch: `You cannot specify --all and individual machines`, }, { message: "command to machines 1, 2, and 1/kvm/0", args: []string{"--machine=1,2,1/kvm/0", "sudo reboot"}, commands: "sudo reboot", machines: []string{"1", "2", "1/kvm/0"}, }, { message: "bad machine names", args: []string{"--machine=foo,machine-2", "sudo reboot"}, errMatch: "" + "The following run targets are not valid:\n" + " \"foo\" is not a valid machine id\n" + " \"machine-2\" is not a valid machine id", }, { message: "all and defined services", args: []string{"--all", "--service=wordpress,mysql", "sudo reboot"}, errMatch: `You cannot specify --all and individual services`, }, { message: "command to services wordpress and mysql", args: []string{"--service=wordpress,mysql", "sudo reboot"}, commands: "sudo reboot", services: []string{"wordpress", "mysql"}, }, { message: "bad service names", args: []string{"--service", "foo,2,foo/0", "sudo reboot"}, errMatch: "" + "The following run targets are not valid:\n" + " \"2\" is not a valid service name\n" + " \"foo/0\" is not a valid service name", }, { message: "all and defined units", args: []string{"--all", "--unit=wordpress/0,mysql/1", "sudo reboot"}, errMatch: `You cannot specify --all and individual units`, }, { message: "command to valid units", args: []string{"--unit=wordpress/0,wordpress/1,mysql/0", "sudo reboot"}, commands: "sudo reboot", units: []string{"wordpress/0", "wordpress/1", "mysql/0"}, }, { message: "bad unit names", args: []string{"--unit", "foo,2,foo/0", "sudo reboot"}, errMatch: "" + "The following run targets are not valid:\n" + " \"foo\" is not a valid unit name\n" + " \"2\" is not a valid unit name", }, { message: "command to mixed valid targets", args: []string{"--machine=0", "--unit=wordpress/0,wordpress/1", "--service=mysql", "sudo reboot"}, commands: "sudo reboot", machines: []string{"0"}, services: []string{"mysql"}, units: []string{"wordpress/0", "wordpress/1"}, }} { c.Log(fmt.Sprintf("%v: %s", i, test.message)) runCmd := &RunCommand{} testing.TestInit(c, runCmd, test.args, test.errMatch) if test.errMatch == "" { c.Check(runCmd.all, gc.Equals, test.all) c.Check(runCmd.machines, gc.DeepEquals, test.machines) c.Check(runCmd.services, gc.DeepEquals, test.services) c.Check(runCmd.units, gc.DeepEquals, test.units) c.Check(runCmd.commands, gc.Equals, test.commands) } } } func (*RunSuite) TestTimeoutArgParsing(c *gc.C) { for i, test := range []struct { message string args []string errMatch string timeout time.Duration }{{ message: "default time", args: []string{"--all", "sudo reboot"}, timeout: 5 * time.Minute, }, { message: "invalid time", args: []string{"--timeout=foo", "--all", "sudo reboot"}, errMatch: `invalid value "foo" for flag --timeout: time: invalid duration foo`, }, { message: "two hours", args: []string{"--timeout=2h", "--all", "sudo reboot"}, timeout: 2 * time.Hour, }, { message: "3 minutes 30 seconds", args: []string{"--timeout=3m30s", "--all", "sudo reboot"}, timeout: (3 * time.Minute) + (30 * time.Second), }} { c.Log(fmt.Sprintf("%v: %s", i, test.message)) runCmd := &RunCommand{} testing.TestInit(c, runCmd, test.args, test.errMatch) if test.errMatch == "" { c.Check(runCmd.timeout, gc.Equals, test.timeout) } } } func (s *RunSuite) TestConvertRunResults(c *gc.C) { for i, test := range []struct { message string results []params.RunResult expected interface{} }{{ message: "empty", expected: []interface{}{}, }, { message: "minimum is machine id and stdout", results: []params.RunResult{ makeRunResult(mockResponse{machineId: "1"}), }, expected: []interface{}{ map[string]interface{}{ "MachineId": "1", "Stdout": "", }}, }, { message: "other fields are copied if there", results: []params.RunResult{ makeRunResult(mockResponse{ machineId: "1", stdout: "stdout", stderr: "stderr", code: 42, unitId: "unit/0", error: "error", }), }, expected: []interface{}{ map[string]interface{}{ "MachineId": "1", "Stdout": "stdout", "Stderr": "stderr", "ReturnCode": 42, "UnitId": "unit/0", "Error": "error", }}, }, { message: "stdout and stderr are base64 encoded if not valid utf8", results: []params.RunResult{ params.RunResult{ ExecResponse: exec.ExecResponse{ Stdout: []byte{0xff}, Stderr: []byte{0xfe}, }, MachineId: "jake", }, }, expected: []interface{}{ map[string]interface{}{ "MachineId": "jake", "Stdout": "/w==", "Stdout.encoding": "base64", "Stderr": "/g==", "Stderr.encoding": "base64", }}, }, { message: "more than one", results: []params.RunResult{ makeRunResult(mockResponse{machineId: "1"}), makeRunResult(mockResponse{machineId: "2"}), makeRunResult(mockResponse{machineId: "3"}), }, expected: []interface{}{ map[string]interface{}{ "MachineId": "1", "Stdout": "", }, map[string]interface{}{ "MachineId": "2", "Stdout": "", }, map[string]interface{}{ "MachineId": "3", "Stdout": "", }, }, }} { c.Log(fmt.Sprintf("%v: %s", i, test.message)) result := ConvertRunResults(test.results) c.Check(result, jc.DeepEquals, test.expected) } } func (s *RunSuite) TestRunForMachineAndUnit(c *gc.C) { mock := s.setupMockAPI() machineResponse := mockResponse{ stdout: "megatron\n", machineId: "0", } unitResponse := mockResponse{ stdout: "bumblebee", machineId: "1", unitId: "unit/0", } mock.setResponse("0", machineResponse) mock.setResponse("unit/0", unitResponse) unformatted := ConvertRunResults([]params.RunResult{ makeRunResult(machineResponse), makeRunResult(unitResponse), }) jsonFormatted, err := cmd.FormatJson(unformatted) c.Assert(err, gc.IsNil) context, err := testing.RunCommand(c, &RunCommand{}, []string{ "--format=json", "--machine=0", "--unit=unit/0", "hostname", }) c.Assert(err, gc.IsNil) c.Check(testing.Stdout(context), gc.Equals, string(jsonFormatted)+"\n") } func (s *RunSuite) TestAllMachines(c *gc.C) { mock := s.setupMockAPI() mock.setMachinesAlive("0", "1") response0 := mockResponse{ stdout: "megatron\n", machineId: "0", } response1 := mockResponse{ error: "command timed out", machineId: "1", } mock.setResponse("0", response0) unformatted := ConvertRunResults([]params.RunResult{ makeRunResult(response0), makeRunResult(response1), }) jsonFormatted, err := cmd.FormatJson(unformatted) c.Assert(err, gc.IsNil) context, err := testing.RunCommand(c, &RunCommand{}, []string{ "--format=json", "--all", "hostname", }) c.Assert(err, gc.IsNil) c.Check(testing.Stdout(context), gc.Equals, string(jsonFormatted)+"\n") } func (s *RunSuite) TestSingleResponse(c *gc.C) { mock := s.setupMockAPI() mock.setMachinesAlive("0") mockResponse := mockResponse{ stdout: "stdout\n", stderr: "stderr\n", code: 42, machineId: "0", } mock.setResponse("0", mockResponse) unformatted := ConvertRunResults([]params.RunResult{ makeRunResult(mockResponse)}) yamlFormatted, err := cmd.FormatYaml(unformatted) c.Assert(err, gc.IsNil) jsonFormatted, err := cmd.FormatJson(unformatted) c.Assert(err, gc.IsNil) for i, test := range []struct { message string format string stdout string stderr string errorMatch string }{{ message: "smart (default)", stdout: "stdout\n", stderr: "stderr\n", errorMatch: "rc: 42", }, { message: "yaml output", format: "yaml", stdout: string(yamlFormatted) + "\n", }, { message: "json output", format: "json", stdout: string(jsonFormatted) + "\n", }} { c.Log(fmt.Sprintf("%v: %s", i, test.message)) args := []string{} if test.format != "" { args = append(args, "--format", test.format) } args = append(args, "--all", "ignored") context, err := testing.RunCommand(c, &RunCommand{}, args) if test.errorMatch != "" { c.Check(err, gc.ErrorMatches, test.errorMatch) } else { c.Check(err, gc.IsNil) } c.Check(testing.Stdout(context), gc.Equals, test.stdout) c.Check(testing.Stderr(context), gc.Equals, test.stderr) } } func (s *RunSuite) setupMockAPI() *mockRunAPI { mock := &mockRunAPI{} s.PatchValue(&getAPIClient, func(name string) (RunClient, error) { return mock, nil }) return mock } type mockRunAPI struct { stdout string stderr string code int // machines, services, units machines map[string]bool responses map[string]params.RunResult } type mockResponse struct { stdout string stderr string code int error string machineId string unitId string } var _ RunClient = (*mockRunAPI)(nil) func (m *mockRunAPI) setMachinesAlive(ids ...string) { if m.machines == nil { m.machines = make(map[string]bool) } for _, id := range ids { m.machines[id] = true } } func makeRunResult(mock mockResponse) params.RunResult { return params.RunResult{ ExecResponse: exec.ExecResponse{ Stdout: []byte(mock.stdout), Stderr: []byte(mock.stderr), Code: mock.code, }, MachineId: mock.machineId, UnitId: mock.unitId, Error: mock.error, } } func (m *mockRunAPI) setResponse(id string, mock mockResponse) { if m.responses == nil { m.responses = make(map[string]params.RunResult) } m.responses[id] = makeRunResult(mock) } func (*mockRunAPI) Close() error { return nil } func (m *mockRunAPI) RunOnAllMachines(commands string, timeout time.Duration) ([]params.RunResult, error) { var result []params.RunResult for machine := range m.machines { response, found := m.responses[machine] if !found { // Consider this a timeout response = params.RunResult{MachineId: machine, Error: "command timed out"} } result = append(result, response) } return result, nil } func (m *mockRunAPI) Run(runParams params.RunParams) ([]params.RunResult, error) { var result []params.RunResult // Just add in ids that match in order. for _, id := range runParams.Machines { response, found := m.responses[id] if found { result = append(result, response) } } // mock ignores services for _, id := range runParams.Units { response, found := m.responses[id] if found { result = append(result, response) } } return result, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/set.go����������������������������������������0000644�0000153�0000161�00000006761�12321735776�023551� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "fmt" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/state/api/params" ) // SetCommand updates the configuration of a service. type SetCommand struct { cmd.EnvCommandBase ServiceName string SettingsStrings map[string]string SettingsYAML cmd.FileVar } const setDoc = ` Set one or more configuration options for the specified service. See also the unset command which sets one or more configuration options for a specified service to their default value. ` func (c *SetCommand) Info() *cmd.Info { return &cmd.Info{ Name: "set", Args: "<service> name=value ...", Purpose: "set service config options", Doc: "Set one or more configuration options for the specified service.", } } func (c *SetCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.Var(&c.SettingsYAML, "config", "path to yaml-formatted service config") } func (c *SetCommand) Init(args []string) error { if len(args) == 0 || len(strings.Split(args[0], "=")) > 1 { return errors.New("no service name specified") } if c.SettingsYAML.Path != "" && len(args) > 1 { return errors.New("cannot specify --config when using key=value arguments") } c.ServiceName = args[0] settings, err := parse(args[1:]) if err != nil { return err } c.SettingsStrings = settings return nil } // serviceSet1dot16 does the final ServiceSet step using direct DB access // compatibility with an API server running 1.16 or older (when ServiceUnset // was not available). This fallback can be removed when we no longer maintain // 1.16 compatibility. // This was copied directly from the code in SetCommand.Run in 1.16 func (c *SetCommand) serviceSet1dot16() error { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() service, err := conn.State.Service(c.ServiceName) if err != nil { return err } ch, _, err := service.Charm() if err != nil { return err } // We don't need the multiple logic here, because that should have // already been taken care of by the API code (which *was* in 1.16). settings, err := ch.Config().ParseSettingsStrings(c.SettingsStrings) if err != nil { return err } return service.UpdateConfigSettings(settings) } // Run updates the configuration of a service. func (c *SetCommand) Run(ctx *cmd.Context) error { api, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer api.Close() if c.SettingsYAML.Path != "" { b, err := c.SettingsYAML.Read(ctx) if err != nil { return err } return api.ServiceSetYAML(c.ServiceName, string(b)) } else if len(c.SettingsStrings) == 0 { return nil } err = api.ServiceSet(c.ServiceName, c.SettingsStrings) if params.IsCodeNotImplemented(err) { logger.Infof("NewServiceSetForClientAPI not supported by the API server, " + "falling back to 1.16 compatibility mode (direct DB access)") err = c.serviceSet1dot16() } return err } // parse parses the option k=v strings into a map of options to be // updated in the config. Keys with empty values are returned separately // and should be removed. func parse(options []string) (map[string]string, error) { kv := make(map[string]string) for _, o := range options { s := strings.SplitN(o, "=", 2) if len(s) != 2 || s[0] == "" { return nil, fmt.Errorf("invalid option: %q", o) } kv[s[0]] = s[1] } return kv, nil } ���������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/upgradecharm.go�������������������������������0000644�0000153�0000161�00000017004�12321735776�025410� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "fmt" "os" "launchpad.net/gnuflag" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" ) // UpgradeCharm is responsible for upgrading a service's charm. type UpgradeCharmCommand struct { cmd.EnvCommandBase ServiceName string Force bool RepoPath string // defaults to JUJU_REPOSITORY SwitchURL string Revision int // defaults to -1 (latest) } const upgradeCharmDoc = ` When no flags are set, the service's charm will be upgraded to the latest revision available in the repository from which it was originally deployed. An explicit revision can be chosen with the --revision flag. If the charm came from a local repository, its path will be assumed to be $JUJU_REPOSITORY unless overridden by --repository. The local repository behaviour is tuned specifically to the workflow of a charm author working on a single client machine; use of local repositories from multiple clients is not supported and may lead to confusing behaviour. Each local charm gets uploaded with the revision specified in the charm, if possible, otherwise it gets a unique revision (highest in state + 1). The --switch flag allows you to replace the charm with an entirely different one. The new charm's URL and revision are inferred as they would be when running a deploy command. Please note that --switch is dangerous, because juju only has limited information with which to determine compatibility; the operation will succeed, regardless of potential havoc, so long as the following conditions hold: - The new charm must declare all relations that the service is currently participating in. - All config settings shared by the old and new charms must have the same types. The new charm may add new relations and configuration settings. --switch and --revision are mutually exclusive. To specify a given revision number with --switch, give it in the charm URL, for instance "cs:wordpress-5" would specify revision number 5 of the wordpress charm. Use of the --force flag is not generally recommended; units upgraded while in an error state will not have upgrade-charm hooks executed, and may cause unexpected behavior. ` func (c *UpgradeCharmCommand) Info() *cmd.Info { return &cmd.Info{ Name: "upgrade-charm", Args: "<service>", Purpose: "upgrade a service's charm", Doc: upgradeCharmDoc, } } func (c *UpgradeCharmCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.BoolVar(&c.Force, "force", false, "upgrade all units immediately, even if in error state") f.StringVar(&c.RepoPath, "repository", os.Getenv("JUJU_REPOSITORY"), "local charm repository path") f.StringVar(&c.SwitchURL, "switch", "", "crossgrade to a different charm") f.IntVar(&c.Revision, "revision", -1, "explicit revision of current charm") } func (c *UpgradeCharmCommand) Init(args []string) error { switch len(args) { case 1: if !names.IsService(args[0]) { return fmt.Errorf("invalid service name %q", args[0]) } c.ServiceName = args[0] case 0: return errors.New("no service specified") default: return cmd.CheckEmpty(args[1:]) } if c.SwitchURL != "" && c.Revision != -1 { return fmt.Errorf("--switch and --revision are mutually exclusive") } return nil } // Run connects to the specified environment and starts the charm // upgrade process. func (c *UpgradeCharmCommand) Run(ctx *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() oldURL, err := client.ServiceGetCharmURL(c.ServiceName) if params.IsCodeNotImplemented(err) { logger.Infof("ServiceGetCharmURL is not implemented by the API server, switching to 1.16 compatibility mode (direct DB connection).") return c.run1dot16(ctx) } if err != nil { return err } attrs, err := client.EnvironmentGet() if err != nil { return err } conf, err := config.New(config.NoDefaults, attrs) if err != nil { return err } var newURL *charm.URL if c.SwitchURL != "" { newURL, err = resolveCharmURL(c.SwitchURL, client, conf) if err != nil { return err } } else { // No new URL specified, but revision might have been. newURL = oldURL.WithRevision(c.Revision) } repo, err := charm.InferRepository(newURL.Reference, ctx.AbsPath(c.RepoPath)) if err != nil { return err } repo = config.SpecializeCharmRepo(repo, conf) // If no explicit revision was set with either SwitchURL // or Revision flags, discover the latest. explicitRevision := true if newURL.Revision == -1 { explicitRevision = false latest, err := charm.Latest(repo, newURL) if err != nil { return err } newURL = newURL.WithRevision(latest) } if *newURL == *oldURL { if explicitRevision { return fmt.Errorf("already running specified charm %q", newURL) } else if newURL.Schema == "cs" { // No point in trying to upgrade a charm store charm when // we just determined that's the latest revision // available. return fmt.Errorf("already running latest charm %q", newURL) } } addedURL, err := addCharmViaAPI(client, ctx, newURL, repo) if err != nil { return err } return client.ServiceSetCharm(c.ServiceName, addedURL.String(), c.Force) } // run1dot16 perfoms the charm upgrade using a 1.16 compatible code // path, with a direct state connection. Remove once the support for // 1.16 is dropped. func (c *UpgradeCharmCommand) run1dot16(ctx *cmd.Context) error { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() service, err := conn.State.Service(c.ServiceName) if err != nil { return err } conf, err := conn.State.EnvironConfig() if err != nil { return err } oldURL, _ := service.CharmURL() var newURL *charm.URL if c.SwitchURL != "" { // A new charm URL was explicitly specified. newURL, err = resolveCharmURL1dot16(c.SwitchURL, conf) if err != nil { return err } } else { // No new URL specified, but revision might have been. newURL = oldURL.WithRevision(c.Revision) } repo, err := charm.InferRepository(newURL.Reference, ctx.AbsPath(c.RepoPath)) if err != nil { return err } repo = config.SpecializeCharmRepo(repo, conf) // If no explicit revision was set with either SwitchURL // or Revision flags, discover the latest. explicitRevision := true if newURL.Revision == -1 { explicitRevision = false latest, err := charm.Latest(repo, newURL) if err != nil { return err } newURL = newURL.WithRevision(latest) } bumpRevision := false if *newURL == *oldURL { if explicitRevision { return fmt.Errorf("already running specified charm %q", newURL) } // Only try bumping the revision when necessary (local dir charm). if _, isLocal := repo.(*charm.LocalRepository); !isLocal { // TODO(dimitern): If the --force flag is set to something // different to before, we might actually want to allow this // case (and the other error below). LP bug #1174287 return fmt.Errorf("already running latest charm %q", newURL) } // This is a local repository. if ch, err := repo.Get(newURL); err != nil { return err } else if _, bumpRevision = ch.(*charm.Dir); !bumpRevision { // Only bump the revision when it's a directory. return fmt.Errorf("cannot increment revision of charm %q: not a directory", newURL) } } sch, err := conn.PutCharm(newURL, repo, bumpRevision) if err != nil { return err } return service.SetCharm(sch, c.Force) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/addrelation.go��������������������������������0000644�0000153�0000161�00000001706�12321735776�025236� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" ) // AddRelationCommand adds a relation between two service endpoints. type AddRelationCommand struct { cmd.EnvCommandBase Endpoints []string } func (c *AddRelationCommand) Info() *cmd.Info { return &cmd.Info{ Name: "add-relation", Args: "<service1>[:<relation name1>] <service2>[:<relation name2>]", Purpose: "add a relation between two services", } } func (c *AddRelationCommand) Init(args []string) error { if len(args) != 2 { return fmt.Errorf("a relation must involve two services") } c.Endpoints = args return nil } func (c *AddRelationCommand) Run(_ *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() _, err = client.AddRelation(c.Endpoints...) return err } ����������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/debuglog.go�����������������������������������0000644�0000153�0000161�00000004603�12321735776�024537� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "strconv" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) type DebugLogCommand struct { cmd.CommandBase // The debug log command simply invokes juju ssh with the required arguments. sshCmd cmd.Command lines linesValue } // defaultLineCount is the default number of lines to // display, from the end of the consolidated log. const defaultLineCount = 10 // linesValue implements gnuflag.Value, and represents // a -n/--lines flag value compatible with "tail". // // A negative value (-K) corresponds to --lines=K, // i.e. the last K lines; a positive value (+K) // corresponds to --lines=+K, i.e. from line K onwards. type linesValue int func (v *linesValue) String() string { if *v > 0 { return fmt.Sprintf("+%d", *v) } return fmt.Sprint(-*v) } func (v *linesValue) Set(value string) error { if len(value) > 0 { sign := -1 if value[0] == '+' { value = value[1:] sign = 1 } n, err := strconv.ParseInt(value, 10, 0) if err == nil && n > 0 { *v = linesValue(sign * int(n)) return nil } // err is quite verbose, and doesn't convey // any additional useful information. } return fmt.Errorf("invalid number of lines") } const debuglogDoc = ` Launch an ssh shell on the state server machine and tail the consolidated log file. The consolidated log file contains log messages from all nodes in the environment. ` func (c *DebugLogCommand) Info() *cmd.Info { return &cmd.Info{ Name: "debug-log", Purpose: "display the consolidated log file", Doc: debuglogDoc, } } func (c *DebugLogCommand) SetFlags(f *gnuflag.FlagSet) { c.sshCmd.SetFlags(f) c.lines = -defaultLineCount f.Var(&c.lines, "n", "output the last K lines; or use -n +K to output lines starting with the Kth") f.Var(&c.lines, "lines", "") } func (c *DebugLogCommand) AllowInterspersedFlags() bool { return true } func (c *DebugLogCommand) Init(args []string) error { tailcmd := fmt.Sprintf("tail -n %s -f /var/log/juju/all-machines.log", &c.lines) args = append([]string{"0"}, args...) args = append(args, tailcmd) return c.sshCmd.Init(args) } // Run uses "juju ssh" to log into the state server node // and tails the consolidated log file which captures log // messages from all nodes. func (c *DebugLogCommand) Run(ctx *cmd.Context) error { return c.sshCmd.Run(ctx) } �����������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/upgradecharm_test.go��������������������������0000644�0000153�0000161�00000015476�12321735642�026452� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "io/ioutil" "os" "path" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" charmtesting "launchpad.net/juju-core/charm/testing" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/testing" ) type UpgradeCharmErrorsSuite struct { jujutesting.RepoSuite } func (s *UpgradeCharmErrorsSuite) SetUpTest(c *gc.C) { s.RepoSuite.SetUpTest(c) mockstore := charmtesting.NewMockStore(c, map[string]int{}) s.AddCleanup(func(*gc.C) { mockstore.Close() }) s.PatchValue(&charm.Store, &charm.CharmStore{ BaseURL: mockstore.Address(), }) } var _ = gc.Suite(&UpgradeCharmErrorsSuite{}) func runUpgradeCharm(c *gc.C, args ...string) error { _, err := testing.RunCommand(c, &UpgradeCharmCommand{}, args) return err } func (s *UpgradeCharmErrorsSuite) TestInvalidArgs(c *gc.C) { err := runUpgradeCharm(c) c.Assert(err, gc.ErrorMatches, "no service specified") err = runUpgradeCharm(c, "invalid:name") c.Assert(err, gc.ErrorMatches, `invalid service name "invalid:name"`) err = runUpgradeCharm(c, "foo", "bar") c.Assert(err, gc.ErrorMatches, `unrecognized args: \["bar"\]`) } func (s *UpgradeCharmErrorsSuite) TestWithInvalidRepository(c *gc.C) { testing.Charms.ClonedDirPath(s.SeriesPath, "riak") err := runDeploy(c, "local:riak", "riak") c.Assert(err, gc.IsNil) err = runUpgradeCharm(c, "riak", "--repository=blah") c.Assert(err, gc.ErrorMatches, `no repository found at ".*blah"`) // Reset JUJU_REPOSITORY explicitly, because repoSuite.SetUpTest // overwrites it (TearDownTest will revert it again). os.Setenv("JUJU_REPOSITORY", "") err = runUpgradeCharm(c, "riak", "--repository=") c.Assert(err, gc.ErrorMatches, `charm not found in ".*": local:precise/riak`) } func (s *UpgradeCharmErrorsSuite) TestInvalidService(c *gc.C) { err := runUpgradeCharm(c, "phony") c.Assert(err, gc.ErrorMatches, `service "phony" not found`) } func (s *UpgradeCharmErrorsSuite) deployService(c *gc.C) { testing.Charms.ClonedDirPath(s.SeriesPath, "riak") err := runDeploy(c, "local:riak", "riak") c.Assert(err, gc.IsNil) } func (s *UpgradeCharmErrorsSuite) TestInvalidSwitchURL(c *gc.C) { s.deployService(c) err := runUpgradeCharm(c, "riak", "--switch=blah") c.Assert(err, gc.ErrorMatches, "charm not found: cs:precise/blah") err = runUpgradeCharm(c, "riak", "--switch=cs:missing/one") c.Assert(err, gc.ErrorMatches, "charm not found: cs:missing/one") // TODO(dimitern): add tests with incompatible charms } func (s *UpgradeCharmErrorsSuite) TestSwitchAndRevisionFails(c *gc.C) { s.deployService(c) err := runUpgradeCharm(c, "riak", "--switch=riak", "--revision=2") c.Assert(err, gc.ErrorMatches, "--switch and --revision are mutually exclusive") } func (s *UpgradeCharmErrorsSuite) TestInvalidRevision(c *gc.C) { s.deployService(c) err := runUpgradeCharm(c, "riak", "--revision=blah") c.Assert(err, gc.ErrorMatches, `invalid value "blah" for flag --revision: strconv.ParseInt: parsing "blah": invalid syntax`) } type UpgradeCharmSuccessSuite struct { jujutesting.RepoSuite path string riak *state.Service } var _ = gc.Suite(&UpgradeCharmSuccessSuite{}) func (s *UpgradeCharmSuccessSuite) SetUpTest(c *gc.C) { s.RepoSuite.SetUpTest(c) s.path = testing.Charms.ClonedDirPath(s.SeriesPath, "riak") err := runDeploy(c, "local:riak", "riak") c.Assert(err, gc.IsNil) s.riak, err = s.State.Service("riak") c.Assert(err, gc.IsNil) ch, forced, err := s.riak.Charm() c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, 7) c.Assert(forced, gc.Equals, false) } func (s *UpgradeCharmSuccessSuite) assertUpgraded(c *gc.C, revision int, forced bool) *charm.URL { err := s.riak.Refresh() c.Assert(err, gc.IsNil) ch, force, err := s.riak.Charm() c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, revision) c.Assert(force, gc.Equals, forced) s.AssertCharmUploaded(c, ch.URL()) return ch.URL() } func (s *UpgradeCharmSuccessSuite) assertLocalRevision(c *gc.C, revision int, path string) { dir, err := charm.ReadDir(path) c.Assert(err, gc.IsNil) c.Assert(dir.Revision(), gc.Equals, revision) } func (s *UpgradeCharmSuccessSuite) TestLocalRevisionUnchanged(c *gc.C) { err := runUpgradeCharm(c, "riak") c.Assert(err, gc.IsNil) s.assertUpgraded(c, 8, false) // Even though the remote revision is bumped, the local one should // be unchanged. s.assertLocalRevision(c, 7, s.path) } func (s *UpgradeCharmSuccessSuite) TestRespectsLocalRevisionWhenPossible(c *gc.C) { dir, err := charm.ReadDir(s.path) c.Assert(err, gc.IsNil) err = dir.SetDiskRevision(42) c.Assert(err, gc.IsNil) err = runUpgradeCharm(c, "riak") c.Assert(err, gc.IsNil) s.assertUpgraded(c, 42, false) s.assertLocalRevision(c, 42, s.path) } func (s *UpgradeCharmSuccessSuite) TestUpgradesWithBundle(c *gc.C) { dir, err := charm.ReadDir(s.path) c.Assert(err, gc.IsNil) dir.SetRevision(42) buf := &bytes.Buffer{} err = dir.BundleTo(buf) c.Assert(err, gc.IsNil) bundlePath := path.Join(s.SeriesPath, "riak.charm") err = ioutil.WriteFile(bundlePath, buf.Bytes(), 0644) c.Assert(err, gc.IsNil) err = runUpgradeCharm(c, "riak") c.Assert(err, gc.IsNil) s.assertUpgraded(c, 42, false) s.assertLocalRevision(c, 7, s.path) } func (s *UpgradeCharmSuccessSuite) TestForcedUpgrade(c *gc.C) { err := runUpgradeCharm(c, "riak", "--force") c.Assert(err, gc.IsNil) s.assertUpgraded(c, 8, true) // Local revision is not changed. s.assertLocalRevision(c, 7, s.path) } var myriakMeta = []byte(` name: myriak summary: "K/V storage engine" description: "Scalable K/V Store in Erlang with Clocks :-)" provides: endpoint: interface: http admin: interface: http peers: ring: interface: riak `) func (s *UpgradeCharmSuccessSuite) TestSwitch(c *gc.C) { myriakPath := testing.Charms.RenamedClonedDirPath(s.SeriesPath, "riak", "myriak") err := ioutil.WriteFile(path.Join(myriakPath, "metadata.yaml"), myriakMeta, 0644) c.Assert(err, gc.IsNil) // Test with local repo and no explicit revsion. err = runUpgradeCharm(c, "riak", "--switch=local:myriak") c.Assert(err, gc.IsNil) curl := s.assertUpgraded(c, 7, false) c.Assert(curl.String(), gc.Equals, "local:precise/myriak-7") s.assertLocalRevision(c, 7, myriakPath) // Now try the same with explicit revision - should fail. err = runUpgradeCharm(c, "riak", "--switch=local:myriak-7") c.Assert(err, gc.ErrorMatches, `already running specified charm "local:precise/myriak-7"`) // Change the revision to 42 and upgrade to it with explicit revision. err = ioutil.WriteFile(path.Join(myriakPath, "revision"), []byte("42"), 0644) c.Assert(err, gc.IsNil) err = runUpgradeCharm(c, "riak", "--switch=local:myriak-42") c.Assert(err, gc.IsNil) curl = s.assertUpgraded(c, 42, false) c.Assert(curl.String(), gc.Equals, "local:precise/myriak-42") s.assertLocalRevision(c, 42, myriakPath) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/destroyservice_test.go������������������������0000644�0000153�0000161�00000002674�12321735642�027056� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/testing" ) type DestroyServiceSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&DestroyServiceSuite{}) func runDestroyService(c *gc.C, args ...string) error { _, err := testing.RunCommand(c, &DestroyServiceCommand{}, args) return err } func (s *DestroyServiceSuite) TestSuccess(c *gc.C) { // Destroy a service that exists. testing.Charms.BundlePath(s.SeriesPath, "riak") err := runDeploy(c, "local:riak", "riak") c.Assert(err, gc.IsNil) err = runDestroyService(c, "riak") c.Assert(err, gc.IsNil) riak, err := s.State.Service("riak") c.Assert(err, gc.IsNil) c.Assert(riak.Life(), gc.Equals, state.Dying) } func (s *DestroyServiceSuite) TestFailure(c *gc.C) { // Destroy a service that does not exist. err := runDestroyService(c, "gargleblaster") c.Assert(err, gc.ErrorMatches, `service "gargleblaster" not found`) } func (s *DestroyServiceSuite) TestInvalidArgs(c *gc.C) { err := runDestroyService(c) c.Assert(err, gc.ErrorMatches, `no service specified`) err = runDestroyService(c, "ping", "pong") c.Assert(err, gc.ErrorMatches, `unrecognized args: \["pong"\]`) err = runDestroyService(c, "invalid:name") c.Assert(err, gc.ErrorMatches, `invalid service name "invalid:name"`) } ��������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/bootstrap.go����������������������������������0000644�0000153�0000161�00000013177�12321735776�024772� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/provider" ) const bootstrapDoc = ` bootstrap starts a new environment of the current type (it will return an error if the environment has already been bootstrapped). Bootstrapping an environment will provision a new machine in the environment and run the juju state server on that machine. If constraints are specified in the bootstrap command, they will apply to the machine provisioned for the juju state server. They will also be set as default constraints on the environment for all future machines, exactly as if the constraints were set with juju set-constraints. Bootstrap initializes the cloud environment synchronously and displays information about the current installation steps. The time for bootstrap to complete varies across cloud providers from a few seconds to several minutes. Once bootstrap has completed, you can run other juju commands against your environment. You can change the default timeout and retry delays used during the bootstrap by changing the following settings in your environments.yaml (all values represent number of seconds): # How long to wait for a connection to the state server. bootstrap-timeout: 600 # default: 10 minutes # How long to wait between connection attempts to a state server address. bootstrap-retry-delay: 5 # default: 5 seconds # How often to refresh state server addresses from the API server. bootstrap-addresses-delay: 10 # default: 10 seconds Private clouds may need to specify their own custom image metadata, and possibly upload Juju tools to cloud storage if no outgoing Internet access is available. In this case, use the --metadata-source paramater to tell bootstrap a local directory from which to upload tools and/or image metadata. See Also: juju help switch juju help constraints juju help set-constraints ` // BootstrapCommand is responsible for launching the first machine in a juju // environment, and setting up everything necessary to continue working. type BootstrapCommand struct { cmd.EnvCommandBase Constraints constraints.Value UploadTools bool Series []string MetadataSource string } func (c *BootstrapCommand) Info() *cmd.Info { return &cmd.Info{ Name: "bootstrap", Purpose: "start up an environment from scratch", Doc: bootstrapDoc, } } func (c *BootstrapCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "set environment constraints") f.BoolVar(&c.UploadTools, "upload-tools", false, "upload local version of tools before bootstrapping") f.Var(seriesVar{&c.Series}, "series", "upload tools for supplied comma-separated series list") f.StringVar(&c.MetadataSource, "metadata-source", "", "local path to use as tools and/or metadata source") } func (c *BootstrapCommand) Init(args []string) (err error) { if len(c.Series) > 0 && !c.UploadTools { return fmt.Errorf("--series requires --upload-tools") } return cmd.CheckEmpty(args) } // Run connects to the environment specified on the command line and bootstraps // a juju in that environment if none already exists. If there is as yet no environments.yaml file, // the user is informed how to create one. func (c *BootstrapCommand) Run(ctx *cmd.Context) (resultErr error) { environ, cleanup, err := environFromName(ctx, c.EnvName, &resultErr, "Bootstrap") if err != nil { return err } defer cleanup() if err := bootstrap.EnsureNotBootstrapped(environ); err != nil { return err } // Block interruption during bootstrap. Providers may also // register for interrupt notification so they can exit early. interrupted := make(chan os.Signal, 1) defer close(interrupted) ctx.InterruptNotify(interrupted) defer ctx.StopInterruptNotify(interrupted) go func() { for _ = range interrupted { ctx.Infof("Interrupt signalled: waiting for bootstrap to exit") } }() // If --metadata-source is specified, override the default tools metadata source so // SyncTools can use it, and also upload any image metadata. if c.MetadataSource != "" { metadataDir := ctx.AbsPath(c.MetadataSource) logger.Infof("Setting default tools and image metadata sources: %s", metadataDir) tools.DefaultBaseURL = metadataDir if err := imagemetadata.UploadImageMetadata(environ.Storage(), metadataDir); err != nil { // Do not error if image metadata directory doesn't exist. if !os.IsNotExist(err) { return fmt.Errorf("uploading image metadata: %v", err) } } else { logger.Infof("custom image metadata uploaded") } } // TODO (wallyworld): 2013-09-20 bug 1227931 // We can set a custom tools data source instead of doing an // unnecessary upload. if environ.Config().Type() == provider.Local { c.UploadTools = true } if c.UploadTools { err = bootstrap.UploadTools(ctx, environ, c.Constraints.Arch, true, c.Series...) if err != nil { return err } } return bootstrap.Bootstrap(ctx, environ, c.Constraints) } type seriesVar struct { target *[]string } func (v seriesVar) Set(value string) error { names := strings.Split(value, ",") for _, name := range names { if !charm.IsValidSeries(name) { return fmt.Errorf("invalid series name %q", name) } } *v.target = names return nil } func (v seriesVar) String() string { return strings.Join(*v.target, ",") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/get.go����������������������������������������0000644�0000153�0000161�00000002657�12321735776�023535� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" ) // GetCommand retrieves the configuration of a service. type GetCommand struct { cmd.EnvCommandBase ServiceName string out cmd.Output } func (c *GetCommand) Info() *cmd.Info { return &cmd.Info{ Name: "get", Args: "<service>", Purpose: "get service configuration options", } } func (c *GetCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) // TODO(dfc) add json formatting ? c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{ "yaml": cmd.FormatYaml, }) } func (c *GetCommand) Init(args []string) error { // TODO(dfc) add --schema-only if len(args) == 0 { return errors.New("no service name specified") } c.ServiceName = args[0] return cmd.CheckEmpty(args[1:]) } // Run fetches the configuration of the service and formats // the result as a YAML string. func (c *GetCommand) Run(ctx *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() results, err := client.ServiceGet(c.ServiceName) if err != nil { return err } resultsMap := map[string]interface{}{ "service": results.Service, "charm": results.Charm, "settings": results.Config, } return c.out.Write(ctx, resultsMap) } ���������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/deploy_test.go��������������������������������0000644�0000153�0000161�00000020670�12321735776�025304� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" ) type DeploySuite struct { testing.RepoSuite } var _ = gc.Suite(&DeploySuite{}) func runDeploy(c *gc.C, args ...string) error { _, err := coretesting.RunCommand(c, &DeployCommand{}, args) return err } var initErrorTests = []struct { args []string err string }{ { args: nil, err: `no charm specified`, }, { args: []string{"craz~ness"}, err: `invalid charm name "craz~ness"`, }, { args: []string{"craziness", "burble-1"}, err: `invalid service name "burble-1"`, }, { args: []string{"craziness", "burble1", "-n", "0"}, err: `--num-units must be a positive integer`, }, { args: []string{"craziness", "burble1", "--to", "bigglesplop"}, err: `invalid --to parameter "bigglesplop"`, }, { args: []string{"craziness", "burble1", "-n", "2", "--to", "123"}, err: `cannot use --num-units > 1 with --to`, }, { args: []string{"craziness", "burble1", "--constraints", "gibber=plop"}, err: `invalid value "gibber=plop" for flag --constraints: unknown constraint "gibber"`, }, } func (s *DeploySuite) TestInitErrors(c *gc.C) { for i, t := range initErrorTests { c.Logf("test %d", i) err := coretesting.InitCommand(&DeployCommand{}, t.args) c.Assert(err, gc.ErrorMatches, t.err) } } func (s *DeploySuite) TestNoCharm(c *gc.C) { err := runDeploy(c, "local:unknown-123") c.Assert(err, gc.ErrorMatches, `charm not found in ".*": local:precise/unknown-123`) } func (s *DeploySuite) TestCharmDir(c *gc.C) { coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "dummy", curl, 1, 0) } func (s *DeploySuite) TestUpgradeReportsDeprecated(c *gc.C) { coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy") ctx, err := coretesting.RunCommand(c, &DeployCommand{}, []string{"local:dummy", "-u"}) c.Assert(err, gc.IsNil) c.Assert(coretesting.Stdout(ctx), gc.Equals, "") output := strings.Split(coretesting.Stderr(ctx), "\n") c.Check(output[0], gc.Matches, `Added charm ".*" to the environment.`) c.Check(output[1], gc.Equals, "--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") } func (s *DeploySuite) TestUpgradeCharmDir(c *gc.C) { // Add the charm, so the url will exist and a new revision will be // picked in ServiceDeploy. dummyCharm := s.AddTestingCharm(c, "dummy") dirPath := coretesting.Charms.ClonedDirPath(s.SeriesPath, "dummy") err := runDeploy(c, "local:quantal/dummy") c.Assert(err, gc.IsNil) upgradedRev := dummyCharm.Revision() + 1 curl := dummyCharm.URL().WithRevision(upgradedRev) s.AssertService(c, "dummy", curl, 1, 0) // Check the charm dir was left untouched. ch, err := charm.ReadDir(dirPath) c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, 1) } func (s *DeploySuite) TestCharmBundle(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "some-service-name") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "some-service-name", curl, 1, 0) } func (s *DeploySuite) TestSubordinateCharm(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "logging") err := runDeploy(c, "local:logging") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/logging-1") s.AssertService(c, "logging", curl, 0, 0) } func (s *DeploySuite) TestConfig(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") path := setupConfigFile(c, c.MkDir()) err := runDeploy(c, "local:dummy", "dummy-service", "--config", path) c.Assert(err, gc.IsNil) service, err := s.State.Service("dummy-service") c.Assert(err, gc.IsNil) settings, err := service.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{ "skill-level": int64(9000), "username": "admin001", }) } func (s *DeploySuite) TestConfigError(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") path := setupConfigFile(c, c.MkDir()) err := runDeploy(c, "local:dummy", "other-service", "--config", path) c.Assert(err, gc.ErrorMatches, `no settings found for "other-service"`) _, err = s.State.Service("other-service") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *DeploySuite) TestConstraints(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "--constraints", "mem=2G cpu-cores=2") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") service, _ := s.AssertService(c, "dummy", curl, 1, 0) cons, err := service.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2")) } func (s *DeploySuite) TestSubordinateConstraints(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "logging") err := runDeploy(c, "local:logging", "--constraints", "mem=1G") c.Assert(err, gc.ErrorMatches, "cannot use --constraints with subordinate service") } func (s *DeploySuite) TestNumUnits(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "local:dummy", "-n", "13") c.Assert(err, gc.IsNil) curl := charm.MustParseURL("local:precise/dummy-1") s.AssertService(c, "dummy", curl, 13, 0) } func (s *DeploySuite) TestNumUnitsSubordinate(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "logging") err := runDeploy(c, "--num-units", "3", "local:logging") c.Assert(err, gc.ErrorMatches, "cannot use --num-units or --to with subordinate service") _, err = s.State.Service("dummy") c.Assert(err, gc.ErrorMatches, `service "dummy" not found`) } func (s *DeploySuite) assertForceMachine(c *gc.C, machineId string) { svc, err := s.State.Service("portlandia") c.Assert(err, gc.IsNil) units, err := svc.AllUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 1) mid, err := units[0].AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, machineId) } func (s *DeploySuite) TestForceMachine(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") machine, err := s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, gc.IsNil) err = runDeploy(c, "--to", machine.Id(), "local:dummy", "portlandia") c.Assert(err, gc.IsNil) s.assertForceMachine(c, machine.Id()) } func (s *DeploySuite) TestForceMachineExistingContainer(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") template := state.MachineTemplate{ Series: "precise", Jobs: []state.MachineJob{state.JobHostUnits}, } container, err := s.State.AddMachineInsideNewMachine(template, template, instance.LXC) c.Assert(err, gc.IsNil) err = runDeploy(c, "--to", container.Id(), "local:dummy", "portlandia") c.Assert(err, gc.IsNil) s.assertForceMachine(c, container.Id()) machines, err := s.State.AllMachines() c.Assert(err, gc.IsNil) c.Assert(machines, gc.HasLen, 2) } func (s *DeploySuite) TestForceMachineNewContainer(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") machine, err := s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, gc.IsNil) err = runDeploy(c, "--to", "lxc:"+machine.Id(), "local:dummy", "portlandia") c.Assert(err, gc.IsNil) s.assertForceMachine(c, machine.Id()+"/lxc/0") machines, err := s.State.AllMachines() c.Assert(err, gc.IsNil) c.Assert(machines, gc.HasLen, 2) } func (s *DeploySuite) TestForceMachineNotFound(c *gc.C) { coretesting.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "--to", "42", "local:dummy", "portlandia") c.Assert(err, gc.ErrorMatches, `cannot assign unit "portlandia/0" to machine: machine 42 not found`) _, err = s.State.Service("dummy") c.Assert(err, gc.ErrorMatches, `service "dummy" not found`) } func (s *DeploySuite) TestForceMachineSubordinate(c *gc.C) { machine, err := s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, gc.IsNil) coretesting.Charms.BundlePath(s.SeriesPath, "logging") err = runDeploy(c, "--to", machine.Id(), "local:logging") c.Assert(err, gc.ErrorMatches, "cannot use --num-units or --to with subordinate service") _, err = s.State.Service("dummy") c.Assert(err, gc.ErrorMatches, `service "dummy" not found`) } ������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/endpoint.go�����������������������������������0000644�0000153�0000161�00000002523�12321735776�024566� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/configstore" ) // EndpointCommand returns the API endpoints type EndpointCommand struct { cmd.EnvCommandBase out cmd.Output } const endpointDoc = ` Returns a list of the API servers formatted as host:port Default output format returns an api server per line. Examples: $ juju api-endpoints 10.0.3.1:17070 $ ` func (c *EndpointCommand) Info() *cmd.Info { return &cmd.Info{ Name: "api-endpoints", Args: "", Purpose: "Print the API server addresses", Doc: endpointDoc, } } func (c *EndpointCommand) Init(args []string) error { return cmd.CheckEmpty(args) } func (c *EndpointCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) c.out.AddFlags(f, "smart", cmd.DefaultFormatters) } // Print out the addresses of the API server endpoints. func (c *EndpointCommand) Run(ctx *cmd.Context) error { store, err := configstore.Default() if err != nil { return err } environ, err := environs.NewFromName(c.EnvName, store) if err != nil { return err } _, api_info, err := environ.StateInfo() if err != nil { return err } return c.out.Write(ctx, api_info.Addrs) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/destroymachine.go�����������������������������0000644�0000153�0000161�00000005023�12321735776�025762� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/statecmd" ) // DestroyMachineCommand causes an existing machine to be destroyed. type DestroyMachineCommand struct { cmd.EnvCommandBase MachineIds []string Force bool } const destroyMachineDoc = ` Machines that are responsible for the environment cannot be destroyed. Machines running units or containers can only be destroyed with the --force flag; doing so will also destroy all those units and containers without giving them any opportunity to shut down cleanly. ` func (c *DestroyMachineCommand) Info() *cmd.Info { return &cmd.Info{ Name: "destroy-machine", Args: "<machine> ...", Purpose: "destroy machines", Doc: destroyMachineDoc, Aliases: []string{"remove-machine", "terminate-machine"}, } } func (c *DestroyMachineCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.BoolVar(&c.Force, "force", false, "completely remove machine and all dependencies") } func (c *DestroyMachineCommand) Init(args []string) error { if len(args) == 0 { return fmt.Errorf("no machines specified") } for _, id := range args { if !names.IsMachine(id) { return fmt.Errorf("invalid machine id %q", id) } } c.MachineIds = args return nil } func (c *DestroyMachineCommand) run1dot16() error { if c.Force { return fmt.Errorf("destroy-machine --force is not supported in Juju servers older than 1.16.4") } conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() // TODO: When this run1dot16 code is removed, we should remove the // method in state as well (as long as add-machine also no longer // needs it.) return statecmd.DestroyMachines1dot16(conn.State, c.MachineIds...) } func (c *DestroyMachineCommand) Run(_ *cmd.Context) error { apiclient, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer apiclient.Close() if c.Force { err = apiclient.ForceDestroyMachines(c.MachineIds...) } else { err = apiclient.DestroyMachines(c.MachineIds...) } // Juju 1.16.3 and older did not have DestroyMachines as an API command. if params.IsCodeNotImplemented(err) { logger.Infof("DestroyMachines not supported by the API server, " + "falling back to <=1.16.3 compatibility") return c.run1dot16() } return err } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/plugin.go�������������������������������������0000644�0000153�0000161�00000012574�12321735776�024253� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "sort" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/log" ) const JujuPluginPrefix = "juju-" // This is a very rudimentary method used to extract common Juju // arguments from the full list passed to the plugin. Currently, // there is only one such argument: -e env // If more than just -e is required, the method can be improved then. func extractJujuArgs(args []string) []string { var jujuArgs []string nrArgs := len(args) for nextArg := 0; nextArg < nrArgs; { arg := args[nextArg] nextArg++ if arg != "-e" { continue } jujuArgs = append(jujuArgs, arg) if nextArg < nrArgs { jujuArgs = append(jujuArgs, args[nextArg]) nextArg++ } } return jujuArgs } func RunPlugin(ctx *cmd.Context, subcommand string, args []string) error { cmdName := JujuPluginPrefix + subcommand plugin := &PluginCommand{name: cmdName} // We process common flags supported by Juju commands. // To do this, we extract only those supported flags from the // argument list to avoid confusing flags.Parse(). flags := gnuflag.NewFlagSet(cmdName, gnuflag.ContinueOnError) flags.SetOutput(ioutil.Discard) plugin.SetFlags(flags) jujuArgs := extractJujuArgs(args) err := flags.Parse(false, jujuArgs) if err != nil { return err } plugin.Init(args) err = plugin.Run(ctx) _, execError := err.(*exec.Error) // exec.Error results are for when the executable isn't found, in // those cases, drop through. if !execError { return err } return &cmd.UnrecognizedCommand{subcommand} } type PluginCommand struct { cmd.EnvCommandBase name string args []string } // Info is just a stub so that PluginCommand implements cmd.Command. // Since this is never actually called, we can happily return nil. func (*PluginCommand) Info() *cmd.Info { return nil } func (c *PluginCommand) Init(args []string) error { c.args = args return nil } func (c *PluginCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) } func (c *PluginCommand) Run(ctx *cmd.Context) error { command := exec.Command(c.name, c.args...) command.Env = append(os.Environ(), []string{ osenv.JujuHomeEnvKey + "=" + osenv.JujuHome(), osenv.JujuEnvEnvKey + "=" + c.EnvironName()}..., ) // Now hook up stdin, stdout, stderr command.Stdin = ctx.Stdin command.Stdout = ctx.Stdout command.Stderr = ctx.Stderr // And run it! return command.Run() } type PluginDescription struct { name string description string } const PluginTopicText = `Juju Plugins Plugins are implemented as stand-alone executable files somewhere in the user's PATH. The executable command must be of the format juju-<plugin name>. ` func PluginHelpTopic() string { output := &bytes.Buffer{} fmt.Fprintf(output, PluginTopicText) existingPlugins := GetPluginDescriptions() if len(existingPlugins) == 0 { fmt.Fprintf(output, "No plugins found.\n") } else { longest := 0 for _, plugin := range existingPlugins { if len(plugin.name) > longest { longest = len(plugin.name) } } for _, plugin := range existingPlugins { fmt.Fprintf(output, "%-*s %s\n", longest, plugin.name, plugin.description) } } return output.String() } // GetPluginDescriptions runs each plugin with "--description". The calls to // the plugins are run in parallel, so the function should only take as long // as the longest call. func GetPluginDescriptions() []PluginDescription { plugins := findPlugins() results := []PluginDescription{} if len(plugins) == 0 { return results } // create a channel with enough backing for each plugin description := make(chan PluginDescription, len(plugins)) // exec the command, and wait only for the timeout before killing the process for _, plugin := range plugins { go func(plugin string) { result := PluginDescription{name: plugin} defer func() { description <- result }() desccmd := exec.Command(plugin, "--description") output, err := desccmd.CombinedOutput() if err == nil { // trim to only get the first line result.description = strings.SplitN(string(output), "\n", 2)[0] } else { result.description = fmt.Sprintf("error occurred running '%s --description'", plugin) log.Errorf("'%s --description': %s", plugin, err) } }(plugin) } resultMap := map[string]PluginDescription{} // gather the results at the end for _ = range plugins { result := <-description resultMap[result.name] = result } // plugins array is already sorted, use this to get the results in order for _, plugin := range plugins { // Strip the 'juju-' off the start of the plugin name in the results result := resultMap[plugin] result.name = result.name[len(JujuPluginPrefix):] results = append(results, result) } return results } // findPlugins searches the current PATH for executable files that start with // JujuPluginPrefix. func findPlugins() []string { path := os.Getenv("PATH") plugins := []string{} for _, name := range filepath.SplitList(path) { entries, err := ioutil.ReadDir(name) if err != nil { continue } for _, entry := range entries { if strings.HasPrefix(entry.Name(), JujuPluginPrefix) && (entry.Mode()&0111) != 0 { plugins = append(plugins, entry.Name()) } } } sort.Strings(plugins) return plugins } ������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/addmachine.go���������������������������������0000644�0000153�0000161�00000014355�12321735776�025031� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/manual" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" ) // sshHostPrefix is the prefix for a machine to be "manually provisioned". const sshHostPrefix = "ssh:" var addMachineDoc = ` If no container is specified, a new machine will be provisioned. If a container is specified, a new machine will be provisioned with that container. To add a container to an existing machine, use the <container>:<machinenumber> format. When adding a new machine, you may specify constraints for the machine to be provisioned. Constraints cannot be combined with deploying a container to an existing machine. Currently, the only supported container type is lxc. Machines are created in a clean state and ready to have units deployed. This command also supports manual provisioning of existing machines via SSH. The target machine must be able to communicate with the API server, and be able to access the environment storage. Examples: juju add-machine (starts a new machine) juju add-machine lxc (starts a new machine with an lxc container) juju add-machine lxc:4 (starts a new lxc container on machine 4) juju add-machine --constraints mem=8G (starts a machine with at least 8GB RAM) juju add-machine ssh:user@10.10.0.3 (manually provisions a machine with ssh) See Also: juju help constraints ` // AddMachineCommand starts a new machine and registers it in the environment. type AddMachineCommand struct { cmd.EnvCommandBase // If specified, use this series, else use the environment default-series Series string // If specified, these constraints are merged with those already in the environment. Constraints constraints.Value MachineId string ContainerType instance.ContainerType SSHHost string } func (c *AddMachineCommand) Info() *cmd.Info { return &cmd.Info{ Name: "add-machine", Args: "[<container>:machine | <container> | ssh:[user@]host]", Purpose: "start a new, empty machine and optionally a container, or add a container to a machine", Doc: addMachineDoc, } } func (c *AddMachineCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.StringVar(&c.Series, "series", "", "the charm series") f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "additional machine constraints") } func (c *AddMachineCommand) Init(args []string) error { if c.Constraints.Container != nil { return fmt.Errorf("container constraint %q not allowed when adding a machine", *c.Constraints.Container) } containerSpec, err := cmd.ZeroOrOneArgs(args) if err != nil { return err } if containerSpec == "" { return nil } if strings.HasPrefix(containerSpec, sshHostPrefix) { c.SSHHost = containerSpec[len(sshHostPrefix):] } else { // container arg can either be 'type:machine' or 'type' if c.ContainerType, err = instance.ParseContainerType(containerSpec); err != nil { if names.IsMachine(containerSpec) || !cmd.IsMachineOrNewContainer(containerSpec) { return fmt.Errorf("malformed container argument %q", containerSpec) } sep := strings.Index(containerSpec, ":") c.MachineId = containerSpec[sep+1:] c.ContainerType, err = instance.ParseContainerType(containerSpec[:sep]) } } return err } // addMachine1dot16 runs Client.AddMachines using a direct DB connection to maintain // compatibility with an API server running 1.16 or older (when AddMachines // was not available). This fallback can be removed when we no longer maintain // 1.16 compatibility. // This was copied directly from the code in AddMachineCommand.Run in 1.16 func (c *AddMachineCommand) addMachine1dot16() (string, error) { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return "", err } defer conn.Close() series := c.Series if series == "" { conf, err := conn.State.EnvironConfig() if err != nil { return "", err } series = config.PreferredSeries(conf) } template := state.MachineTemplate{ Series: series, Constraints: c.Constraints, Jobs: []state.MachineJob{state.JobHostUnits}, } var m *state.Machine switch { case c.ContainerType == "": m, err = conn.State.AddOneMachine(template) case c.MachineId != "": m, err = conn.State.AddMachineInsideMachine(template, c.MachineId, c.ContainerType) default: m, err = conn.State.AddMachineInsideNewMachine(template, template, c.ContainerType) } if err != nil { return "", err } return m.String(), err } func (c *AddMachineCommand) Run(ctx *cmd.Context) error { if c.SSHHost != "" { args := manual.ProvisionMachineArgs{ Host: c.SSHHost, EnvName: c.EnvName, Stdin: ctx.Stdin, Stdout: ctx.Stdout, Stderr: ctx.Stderr, } _, err := manual.ProvisionMachine(args) return err } client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() machineParams := params.AddMachineParams{ ParentId: c.MachineId, ContainerType: c.ContainerType, Series: c.Series, Constraints: c.Constraints, Jobs: []params.MachineJob{params.JobHostUnits}, } results, err := client.AddMachines([]params.AddMachineParams{machineParams}) var machineId string if params.IsCodeNotImplemented(err) { logger.Infof("AddMachines not supported by the API server, " + "falling back to 1.16 compatibility mode (direct DB access)") machineId, err = c.addMachine1dot16() } else if err != nil { return err } else { // Currently, only one machine is added, but in future there may be several added in one call. machineInfo := results[0] var machineErr *params.Error machineId, machineErr = machineInfo.Machine, machineInfo.Error if machineErr != nil { err = machineErr } } if err != nil { return err } if c.ContainerType == "" { logger.Infof("created machine %v", machineId) } else { logger.Infof("created %q container on machine %v", c.ContainerType, machineId) } return nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/environment.go��������������������������������0000644�0000153�0000161�00000015274�12321735776�025321� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/state/api/params" ) // GetEnvironmentCommand is able to output either the entire environment or // the requested value in a format of the user's choosing. type GetEnvironmentCommand struct { cmd.EnvCommandBase key string out cmd.Output } const getEnvHelpDoc = ` If no extra args passed on the command line, all configuration keys and values for the environment are output using the selected formatter. A single environment value can be output by adding the environment key name to the end of the command line. Example: juju get-environment default-series (returns the default series for the environment) ` func (c *GetEnvironmentCommand) Info() *cmd.Info { return &cmd.Info{ Name: "get-environment", Args: "[<environment key>]", Purpose: "view environment values", Doc: strings.TrimSpace(getEnvHelpDoc), Aliases: []string{"get-env"}, } } func (c *GetEnvironmentCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) c.out.AddFlags(f, "smart", cmd.DefaultFormatters) } func (c *GetEnvironmentCommand) Init(args []string) (err error) { c.key, err = cmd.ZeroOrOneArgs(args) return } // environmentGet1dot16 runs matches client.EnvironmentGet using a direct DB // connection to maintain compatibility with an API server running 1.16 or // older (when EnvironmentGet was not available). This fallback can be removed // when we no longer maintain 1.16 compatibility. func (c *GetEnvironmentCommand) environmentGet1dot16() (map[string]interface{}, error) { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return nil, err } defer conn.Close() // Get the existing environment config from the state. config, err := conn.State.EnvironConfig() if err != nil { return nil, err } attrs := config.AllAttrs() return attrs, nil } func (c *GetEnvironmentCommand) Run(ctx *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() attrs, err := client.EnvironmentGet() if params.IsCodeNotImplemented(err) { logger.Infof("EnvironmentGet not supported by the API server, " + "falling back to 1.16 compatibility mode (direct DB access)") attrs, err = c.environmentGet1dot16() } if err != nil { return err } if c.key != "" { if value, found := attrs[c.key]; found { return c.out.Write(ctx, value) } return fmt.Errorf("Key %q not found in %q environment.", c.key, attrs["name"]) } // If key is empty, write out the whole lot. return c.out.Write(ctx, attrs) } type attributes map[string]interface{} // SetEnvironment type SetEnvironmentCommand struct { cmd.EnvCommandBase values attributes } const setEnvHelpDoc = ` Updates the environment of a running Juju instance. Multiple key/value pairs can be passed on as command line arguments. ` func (c *SetEnvironmentCommand) Info() *cmd.Info { return &cmd.Info{ Name: "set-environment", Args: "key=[value] ...", Purpose: "replace environment values", Doc: strings.TrimSpace(setEnvHelpDoc), Aliases: []string{"set-env"}, } } // SetFlags handled entirely by cmd.EnvCommandBase func (c *SetEnvironmentCommand) Init(args []string) (err error) { if len(args) == 0 { return fmt.Errorf("No key, value pairs specified") } // TODO(thumper) look to have a common library of functions for dealing // with key=value pairs. c.values = make(attributes) for i, arg := range args { bits := strings.SplitN(arg, "=", 2) if len(bits) < 2 { return fmt.Errorf(`Missing "=" in arg %d: %q`, i+1, arg) } key := bits[0] if key == "agent-version" { return fmt.Errorf("agent-version must be set via upgrade-juju") } if _, exists := c.values[key]; exists { return fmt.Errorf(`Key %q specified more than once`, key) } c.values[key] = bits[1] } return nil } // run1dot16 runs matches client.EnvironmentSet using a direct DB // connection to maintain compatibility with an API server running 1.16 or // older (when EnvironmentSet was not available). This fallback can be removed // when we no longer maintain 1.16 compatibility. // This content was copied from SetEnvironmentCommand.Run in 1.16 func (c *SetEnvironmentCommand) run1dot16() error { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() // Update state config with new values return conn.State.UpdateEnvironConfig(c.values, nil, nil) } func (c *SetEnvironmentCommand) Run(ctx *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() err = client.EnvironmentSet(c.values) if params.IsCodeNotImplemented(err) { logger.Infof("EnvironmentSet not supported by the API server, " + "falling back to 1.16 compatibility mode (direct DB access)") err = c.run1dot16() } return err } // UnsetEnvironment type UnsetEnvironmentCommand struct { cmd.EnvCommandBase keys []string } const unsetEnvHelpDoc = ` Reset one or more the environment configuration attributes to its default value in a running Juju instance. Attributes without defaults are removed, and attempting to remove a required attribute with no default will result in an error. Multiple attributes may be removed at once; keys are space-separated. ` func (c *UnsetEnvironmentCommand) Info() *cmd.Info { return &cmd.Info{ Name: "unset-environment", Args: "<environment key> ...", Purpose: "unset environment values", Doc: strings.TrimSpace(unsetEnvHelpDoc), Aliases: []string{"unset-env"}, } } func (c *UnsetEnvironmentCommand) Init(args []string) (err error) { if len(args) == 0 { return fmt.Errorf("No keys specified") } c.keys = args return nil } // run1dot16 runs matches client.EnvironmentUnset using a direct DB // connection to maintain compatibility with an API server running 1.16 or // older (when EnvironmentUnset was not available). This fallback can be removed // when we no longer maintain 1.16 compatibility. func (c *UnsetEnvironmentCommand) run1dot16() error { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return err } defer conn.Close() return conn.State.UpdateEnvironConfig(nil, c.keys, nil) } func (c *UnsetEnvironmentCommand) Run(ctx *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() err = client.EnvironmentUnset(c.keys...) if params.IsCodeNotImplemented(err) { logger.Infof("EnvironmentUnset not supported by the API server, " + "falling back to 1.16 compatibility mode (direct DB access)") err = c.run1dot16() } return err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/destroyrelation.go����������������������������0000644�0000153�0000161�00000002006�12321735776�026171� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" ) // DestroyRelationCommand causes an existing service relation to be shut down. type DestroyRelationCommand struct { cmd.EnvCommandBase Endpoints []string } func (c *DestroyRelationCommand) Info() *cmd.Info { return &cmd.Info{ Name: "destroy-relation", Args: "<service1>[:<relation name1>] <service2>[:<relation name2>]", Purpose: "destroy a relation between two services", Aliases: []string{"remove-relation"}, } } func (c *DestroyRelationCommand) Init(args []string) error { if len(args) != 2 { return fmt.Errorf("a relation must involve two services") } c.Endpoints = args return nil } func (c *DestroyRelationCommand) Run(_ *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() return client.DestroyRelation(c.Endpoints...) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/run.go����������������������������������������0000644�0000153�0000161�00000014434�12321735776�023556� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "encoding/base64" "errors" "fmt" "strings" "time" "unicode/utf8" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" ) // RunCommand is responsible for running arbitrary commands on remote machines. type RunCommand struct { cmd.EnvCommandBase out cmd.Output all bool timeout time.Duration machines []string services []string units []string commands string } const runDoc = ` Run the commands on the specified targets. Targets are specified using either machine ids, service names or unit names. At least one target specifier is needed. Multiple values can be set for --machine, --service, and --unit by using comma separated values. If the target is a machine, the command is run as the "ubuntu" user on the remote machine. If the target is a service, the command is run on all units for that service. For example, if there was a service "mysql" and that service had two units, "mysql/0" and "mysql/1", then --service mysql is equivalent to --unit mysql/0,mysql/1 Commands run for services or units are executed in a 'hook context' for the unit. --all is provided as a simple way to run the command on all the machines in the environment. If you specify --all you cannot provide additional targets. ` func (c *RunCommand) Info() *cmd.Info { return &cmd.Info{ Name: "run", Args: "<commands>", Purpose: "run the commands on the remote targets specified", Doc: runDoc, } } func (c *RunCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) c.out.AddFlags(f, "smart", cmd.DefaultFormatters) f.BoolVar(&c.all, "all", false, "run the commands on all the machines") f.DurationVar(&c.timeout, "timeout", 5*time.Minute, "how long to wait before the remote command is considered to have failed") f.Var(cmd.NewStringsValue(nil, &c.machines), "machine", "one or more machine ids") f.Var(cmd.NewStringsValue(nil, &c.services), "service", "one or more service names") f.Var(cmd.NewStringsValue(nil, &c.units), "unit", "one or more unit ids") } func (c *RunCommand) Init(args []string) error { if len(args) == 0 { return errors.New("no commands specified") } c.commands, args = args[0], args[1:] if c.all { if len(c.machines) != 0 { return fmt.Errorf("You cannot specify --all and individual machines") } if len(c.services) != 0 { return fmt.Errorf("You cannot specify --all and individual services") } if len(c.units) != 0 { return fmt.Errorf("You cannot specify --all and individual units") } } else { if len(c.machines) == 0 && len(c.services) == 0 && len(c.units) == 0 { return fmt.Errorf("You must specify a target, either through --all, --machine, --service or --unit") } } var nameErrors []string for _, machineId := range c.machines { if !names.IsMachine(machineId) { nameErrors = append(nameErrors, fmt.Sprintf(" %q is not a valid machine id", machineId)) } } for _, service := range c.services { if !names.IsService(service) { nameErrors = append(nameErrors, fmt.Sprintf(" %q is not a valid service name", service)) } } for _, unit := range c.units { if !names.IsUnit(unit) { nameErrors = append(nameErrors, fmt.Sprintf(" %q is not a valid unit name", unit)) } } if len(nameErrors) > 0 { return fmt.Errorf("The following run targets are not valid:\n%s", strings.Join(nameErrors, "\n")) } return cmd.CheckEmpty(args) } func encodeBytes(input []byte) (value string, encoding string) { if utf8.Valid(input) { value = string(input) encoding = "utf8" } else { value = base64.StdEncoding.EncodeToString(input) encoding = "base64" } return value, encoding } func storeOutput(values map[string]interface{}, key string, input []byte) { value, encoding := encodeBytes(input) values[key] = value if encoding != "utf8" { values[key+".encoding"] = encoding } } // ConvertRunResults takes the results from the api and creates a map // suitable for format converstion to YAML or JSON. func ConvertRunResults(runResults []params.RunResult) interface{} { var results = make([]interface{}, len(runResults)) for i, result := range runResults { // We always want to have a string for stdout, but only show stderr, // code and error if they are there. values := make(map[string]interface{}) values["MachineId"] = result.MachineId if result.UnitId != "" { values["UnitId"] = result.UnitId } storeOutput(values, "Stdout", result.Stdout) if len(result.Stderr) > 0 { storeOutput(values, "Stderr", result.Stderr) } if result.Code != 0 { values["ReturnCode"] = result.Code } if result.Error != "" { values["Error"] = result.Error } results[i] = values } return results } func (c *RunCommand) Run(ctx *cmd.Context) error { client, err := getAPIClient(c.EnvName) if err != nil { return err } defer client.Close() var runResults []params.RunResult if c.all { runResults, err = client.RunOnAllMachines(c.commands, c.timeout) } else { params := params.RunParams{ Commands: c.commands, Timeout: c.timeout, Machines: c.machines, Services: c.services, Units: c.units, } runResults, err = client.Run(params) } if err != nil { return err } // If we are just dealing with one result, AND we are using the smart // format, then pretend we were running it locally. if len(runResults) == 1 && c.out.Name() == "smart" { result := runResults[0] ctx.Stdout.Write(result.Stdout) ctx.Stderr.Write(result.Stderr) if result.Error != "" { // Convert the error string back into an error object. return fmt.Errorf("%s", result.Error) } if result.Code != 0 { return cmd.NewRcPassthroughError(result.Code) } return nil } c.out.Write(ctx, ConvertRunResults(runResults)) return nil } // In order to be able to easily mock out the API side for testing, // the API client is got using a function. type RunClient interface { Close() error RunOnAllMachines(commands string, timeout time.Duration) ([]params.RunResult, error) Run(run params.RunParams) ([]params.RunResult, error) } // Here we need the signature to be correct for the interface. var getAPIClient = func(name string) (RunClient, error) { return juju.NewAPIClientFromName(name) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/switch_test.go��������������������������������0000644�0000153�0000161�00000010613�12321735776�025305� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "os" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" _ "launchpad.net/juju-core/juju" "launchpad.net/juju-core/testing" ) type SwitchSimpleSuite struct { } var _ = gc.Suite(&SwitchSimpleSuite{}) func (*SwitchSimpleSuite) TestNoEnvironment(c *gc.C) { defer testing.MakeEmptyFakeHome(c).Restore() _, err := testing.RunCommand(c, &SwitchCommand{}, nil) c.Assert(err, gc.ErrorMatches, "couldn't read the environment") } func (*SwitchSimpleSuite) TestNoDefault(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault).Restore() _, err := testing.RunCommand(c, &SwitchCommand{}, nil) c.Assert(err, gc.ErrorMatches, "no currently specified environment") } func (*SwitchSimpleSuite) TestShowsDefault(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfig).Restore() context, err := testing.RunCommand(c, &SwitchCommand{}, nil) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(context), gc.Equals, "erewhemos\n") } func (*SwitchSimpleSuite) TestCurrentEnvironmentHasPrecidence(c *gc.C) { home := testing.MakeFakeHome(c, testing.MultipleEnvConfig) defer home.Restore() home.AddFiles(c, []testing.TestFile{{".juju/current-environment", "fubar"}}) context, err := testing.RunCommand(c, &SwitchCommand{}, nil) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(context), gc.Equals, "fubar\n") } func (*SwitchSimpleSuite) TestShowsJujuEnv(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfig).Restore() os.Setenv("JUJU_ENV", "using-env") context, err := testing.RunCommand(c, &SwitchCommand{}, nil) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(context), gc.Equals, "using-env\n") } func (*SwitchSimpleSuite) TestJujuEnvOverCurrentEnvironment(c *gc.C) { home := testing.MakeFakeHome(c, testing.MultipleEnvConfig) defer home.Restore() home.AddFiles(c, []testing.TestFile{{".juju/current-environment", "fubar"}}) os.Setenv("JUJU_ENV", "using-env") context, err := testing.RunCommand(c, &SwitchCommand{}, nil) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(context), gc.Equals, "using-env\n") } func (*SwitchSimpleSuite) TestSettingWritesFile(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfig).Restore() context, err := testing.RunCommand(c, &SwitchCommand{}, []string{"erewhemos-2"}) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(context), gc.Equals, "erewhemos -> erewhemos-2\n") c.Assert(cmd.ReadCurrentEnvironment(), gc.Equals, "erewhemos-2") } func (*SwitchSimpleSuite) TestSettingToUnknown(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfig).Restore() _, err := testing.RunCommand(c, &SwitchCommand{}, []string{"unknown"}) c.Assert(err, gc.ErrorMatches, `"unknown" is not a name of an existing defined environment`) } func (*SwitchSimpleSuite) TestSettingWhenJujuEnvSet(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfig).Restore() os.Setenv("JUJU_ENV", "using-env") _, err := testing.RunCommand(c, &SwitchCommand{}, []string{"erewhemos-2"}) c.Assert(err, gc.ErrorMatches, `cannot switch when JUJU_ENV is overriding the environment \(set to "using-env"\)`) } const expectedEnvironments = `erewhemos erewhemos-2 ` func (*SwitchSimpleSuite) TestListEnvironments(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfig).Restore() context, err := testing.RunCommand(c, &SwitchCommand{}, []string{"--list"}) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(context), gc.Equals, expectedEnvironments) } func (*SwitchSimpleSuite) TestListEnvironmentsOSJujuEnvSet(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfig).Restore() os.Setenv("JUJU_ENV", "using-env") context, err := testing.RunCommand(c, &SwitchCommand{}, []string{"--list"}) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(context), gc.Equals, expectedEnvironments) } func (*SwitchSimpleSuite) TestListEnvironmentsAndChange(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfig).Restore() _, err := testing.RunCommand(c, &SwitchCommand{}, []string{"--list", "erewhemos-2"}) c.Assert(err, gc.ErrorMatches, "cannot switch and list at the same time") } func (*SwitchSimpleSuite) TestTooManyParams(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfig).Restore() _, err := testing.RunCommand(c, &SwitchCommand{}, []string{"foo", "bar"}) c.Assert(err, gc.ErrorMatches, `unrecognized args: ."bar".`) } ���������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/unexpose.go�����������������������������������0000644�0000153�0000161�00000001735�12321735776�024620� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" ) // UnexposeCommand is responsible exposing services. type UnexposeCommand struct { cmd.EnvCommandBase ServiceName string } func (c *UnexposeCommand) Info() *cmd.Info { return &cmd.Info{ Name: "unexpose", Args: "<service>", Purpose: "unexpose a service", } } func (c *UnexposeCommand) Init(args []string) error { if len(args) == 0 { return errors.New("no service name specified") } c.ServiceName = args[0] return cmd.CheckEmpty(args[1:]) } // Run changes the juju-managed firewall to hide any // ports that were also explicitly marked by units as closed. func (c *UnexposeCommand) Run(_ *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() return client.ServiceUnexpose(c.ServiceName) } �����������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/init.go���������������������������������������0000644�0000153�0000161�00000003574�12321735642�023710� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" ) // InitCommand is used to write out a boilerplate environments.yaml file. type InitCommand struct { cmd.CommandBase WriteFile bool Show bool } func (c *InitCommand) Info() *cmd.Info { return &cmd.Info{ Name: "init", Purpose: "generate boilerplate configuration for juju environments", Aliases: []string{"generate-config"}, } } func (c *InitCommand) SetFlags(f *gnuflag.FlagSet) { f.BoolVar(&c.WriteFile, "f", false, "force overwriting environments.yaml file even if it exists (ignored if --show flag specified)") f.BoolVar(&c.Show, "show", false, "print the generated configuration data to stdout instead of writing it to a file") } var errJujuEnvExists = fmt.Errorf(`A juju environment configuration already exists. Use -f to overwrite the existing environments.yaml. `) // Run checks to see if there is already an environments.yaml file. In one does not exist already, // a boilerplate version is created so that the user can edit it to get started. func (c *InitCommand) Run(context *cmd.Context) error { out := context.Stdout config := environs.BoilerplateConfig() if c.Show { fmt.Fprint(out, config) return nil } _, err := environs.ReadEnvirons("") if err == nil && !c.WriteFile { return errJujuEnvExists } if err != nil && !environs.IsNoEnv(err) { return err } filename, err := environs.WriteEnvirons("", config) if err != nil { return fmt.Errorf("A boilerplate environment configuration file could not be created: %s", err.Error()) } fmt.Fprintf(out, "A boilerplate environment configuration file has been written to %s.\n", filename) fmt.Fprint(out, "Edit the file to configure your juju environment and run bootstrap.\n") return nil } ������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/help_topics.go��������������������������������0000644�0000153�0000161�00000045656�12321735642�025265� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main const helpBasics = ` Juju -- devops distilled https://juju.ubuntu.com/ Juju provides easy, intelligent service orchestration on top of environments such as Amazon EC2, HP Cloud, OpenStack, MaaS, or your own local machine. Basic commands: juju init generate boilerplate configuration for juju environments juju bootstrap start up an environment from scratch juju deploy deploy a new service juju add-relation add a relation between two services juju expose expose a service juju help bootstrap more help on e.g. bootstrap command juju help commands list all commands juju help glossary glossary of terms juju help topics list all help topics Provider information: juju help azure-provider use on Windows Azure juju help ec2-provider use on Amazon EC2 juju help hpcloud-provider use on HP Cloud juju help local-provider use on this computer juju help openstack-provider use on OpenStack ` const helpProviderStart = ` Start by generating a generic configuration file for Juju, using the command: juju init This will create the '~/.juju/' directory (or $JUJU_HOME, if set) if it doesn't already exist and generate a file, 'environments.yaml' in that directory. ` const helpProviderEnd = ` See Also: juju help init juju help bootstrap ` const helpLocalProvider = ` The local provider is a Linux-only Juju environment that uses LXC containers as a virtual cloud on the local machine. Because of this, lxc and mongodb are required for the local provider to work. If you don't already have lxc and mongodb installed, run the following commands: sudo apt-get update sudo apt-get install lxc mongodb-server After that you might get error for SSH authorized/public key not found. ERROR SSH authorized/public key not found. ssh-keygen -t rsa Now you need to tell Juju to use the local provider and then bootstrap: juju switch local juju bootstrap The first time this runs it might take a bit, as it's doing a netinstall for the container, it's around a 300 megabyte download. Subsequent bootstraps should be much quicker. You'll be asked for your 'sudo' password, which is needed because only root can create LXC containers. When you need to destroy the environment, do 'juju destroy-environment local' and you could be asked for your 'sudo' password again. You deploy charms from the charm store using the following commands: juju deploy mysql juju deploy wordpress juju add-relation wordpress mysql As of trusty, the local provider will prefer to use lxc-clone to create the machines. A 'template' container is created with the name juju-<series>-tempalte where <series> is the OS series, for example 'juju-precise-template'. You can override the use of clone by specifying use-clone: true or use-clone: false in the configuration for your local provider. If you have the main container directory mounted on a btrfs partition, then the clone will be using btrfs snapshots to create the containers. This means that the clones use up much less disk space. If you do not have btrfs, lxc will attempt to use aufs (which is an overlay type filesystem). You can explicitly ask Juju to create full containers and not overlays by specifying the following in the provider configuration: use-clone-aufs: false References: http://askubuntu.com/questions/65359/how-do-i-configure-juju-for-local-usage https://juju.ubuntu.com/docs/getting-started.html ` const helpOpenstackProvider = ` Here's an example OpenStack configuration: sample_openstack: type: openstack # Specifies whether the use of a floating IP address is required to # give the nodes a public IP address. Some installations assign public # IP addresses by default without requiring a floating IP address. # use-floating-ip: false # Specifies whether new machine instances should have the "default" # Openstack security group assigned. # use-default-secgroup: false # Usually set via the env variable OS_AUTH_URL, but can be specified here # auth-url: https://yourkeystoneurl:443/v2.0/ # The following are used for userpass authentication (the default) # auth-mode: userpass # Usually set via the env variable OS_USERNAME, but can be specified here # username: <your username> # Usually set via the env variable OS_PASSWORD, but can be specified here # password: <secret> # Usually set via the env variable OS_TENANT_NAME, but can be specified here # tenant-name: <your tenant name> # Usually set via the env variable OS_REGION_NAME, but can be specified here # region: <your region> If you have set the described OS_* environment variables, you only need "type:". References: http://juju.ubuntu.com/docs/provider-configuration-openstack.html http://askubuntu.com/questions/132411/how-can-i-configure-juju-for-deployment-on-openstack Other OpenStack Based Clouds: This answer is for generic OpenStack support, if you're using an OpenStack-based provider check these questions out for provider-specific information: https://juju.ubuntu.com/docs/config-hpcloud.html ` const helpEC2Provider = ` Configuring the EC2 environment requires telling Juju about your AWS access key and secret key. To do this, you can either set the 'AWS_ACCESS_KEY_ID' and 'AWS_SECRET_ACCESS_KEY' environment variables[1] (as usual for other EC2 tools) or you can add access-key and secret-key options to your environments.yaml. These are already in place in the generated config, you just need to uncomment them out. For example: sample_ec2: type: ec2 # access-key: YOUR-ACCESS-KEY-GOES-HERE # secret-key: YOUR-SECRET-KEY-GOES-HERE See the EC2 provider documentation[2] for more options. Note If you already have an AWS account, you can determine your access key by visiting your account page[3], clicking "Security Credentials" and then clicking "Access Credentials". You'll be taken to a table that lists your access keys and has a "show" link for each access key that will reveal the associated secret key. And that's it, you're ready to go! References: [1]: http://askubuntu.com/questions/730/how-do-i-set-environment-variables [2]: https://juju.ubuntu.com/docs/provider-configuration-ec2.html [3]: http://aws.amazon.com/account More information: https://juju.ubuntu.com/docs/getting-started.html https://juju.ubuntu.com/docs/provider-configuration-ec2.html http://askubuntu.com/questions/225513/how-do-i-configure-juju-to-use-amazon-web-services-aws ` const helpHPCloud = ` HP Cloud is an Openstack cloud provider. To deploy to it, use an openstack environment type for Juju, which would look something like this: sample_hpcloud: type: openstack tenant-name: "juju-project1" auth-url: https://region-a.geo-1.identity.hpcloudsvc.com:35357/v2.0/ auth-mode: userpass username: "xxxyour-hpcloud-usernamexxx" password: "xxxpasswordxxx" region: az-1.region-a.geo-1 See the online help for more information: https://juju.ubuntu.com/docs/config-hpcloud.html ` const helpAzureProvider = ` A generic Windows Azure environment looks like this: sample_azure: type: azure # Location for instances, e.g. West US, North Europe. location: West US # http://msdn.microsoft.com/en-us/library/windowsazure # Windows Azure Management info. management-subscription-id: 886413e1-3b8a-5382-9b90-0c9aee199e5d management-certificate-path: /home/me/azure.pem # Windows Azure Storage info. storage-account-name: juju0useast0 # Override OS image selection with a fixed image for all deployments. # Most useful for developers. # force-image-name: b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-DEVELOPMENT-20130713-Juju_ALPHA-en-us-30GB # Pick a simplestreams stream to select OS images from: daily or released # images, or any other stream available on simplestreams. Leave blank for # released images. # image-stream: "" This is the environments.yaml configuration file needed to run on Windows Azure. You will need to set the management-subscription-id, management-certificate- path, and storage-account-name. Note: Other than location, the defaults are recommended, but can be updated to your preference. See the online help for more information: https://juju.ubuntu.com/docs/config-azure.html ` const helpConstraints = ` Constraints constrain the possible instances that may be started by juju commands. They are usually passed as a flag to commands that provision a new machine (such as bootstrap, deploy, and add-machine). Each constraint defines a minimum acceptable value for a characteristic of a machine. Juju will provision the least expensive machine that fulfills all the constraints specified. Note that these values are the minimum, and the actual machine used may exceed these specifications if one that exactly matches does not exist. If a constraint is defined that cannot be fulfilled by any machine in the environment, no machine will be provisioned, and an error will be printed in the machine's entry in juju status. Constraint defaults can be set on an environment or on specific services by using the set-constraints command (see juju help set-constraints). Constraints set on the environment or on a service can be viewed by using the get- constraints command. In addition, you can specify constraints when executing a command by using the --constraints flag (for commands that support it). Constraints specified on the environment and service will be combined to determine the full list of constraints on the machine(s) to be provisioned by the command. Service-specific constraints will override environment-specific constraints, which override the juju default constraints. Constraints are specified as key value pairs separated by an equals sign, with multiple constraints delimited by a space. Constraint Types: arch Arch defines the CPU architecture that the machine must have. Currently recognized architectures: amd64 (default) i386 arm cpu-cores Cpu-cores is a whole number that defines the number of effective cores the machine must have available. mem Mem is a float with an optional suffix that defines the minimum amount of RAM that the machine must have. The value is rounded up to the next whole megabyte. The default units are megabytes, but you can use a size suffix to use other units: M megabytes (default) G gigabytes (1024 megabytes) T terabytes (1024 gigabytes) P petabytes (1024 terabytes) root-disk Root-Disk is a float that defines the amount of space in megabytes that must be available in the machine's root partition. For providers that have configurable root disk sizes (such as EC2) an instance with the specified amount of disk space in the root partition may be requested. Root disk size defaults to megabytes and may be specified in the same manner as the mem constraint. container Container defines that the machine must be a container of the specified type. A container of that type may be created by juju to fulfill the request. Currently supported containers: none - (default) no container lxc - an lxc container kvm - a kvm container cpu-power Cpu-power is a whole number that defines the speed of the machine's CPU, where 100 CpuPower is considered to be equivalent to 1 Amazon ECU (or, roughly, a single 2007-era Xeon). Cpu-power is currently only supported by the Amazon EC2 environment. tags Tags defines the list of tags that the machine must have applied to it. Multiple tags must be delimited by a comma. Tags are currently only supported by the MaaS environment. Example: juju add-machine --constraints "arch=amd64 mem=8G tags=foo,bar" See Also: juju help set-constraints juju help get-constraints juju help deploy juju help add-unit juju help add-machine juju help bootstrap ` const helpGlossary = ` Bootstrap To boostrap an environment means initializing it so that Services may be deployed on it. Charm A Charm provides the definition of the service, including its metadata, dependencies to other services, packages necessary, as well as the logic for management of the application. It is the layer that integrates an external application component like Postgres or WordPress into Juju. A Juju Service may generally be seen as the composition of its Juju Charm and the upstream application (traditionally made available through its package). Charm URL A Charm URL is a resource locator for a charm, with the following format and restrictions: <schema>:[~<user>/]<collection>/<name>[-<revision>] schema must be either "cs", for a charm from the Juju charm store, or "local", for a charm from a local repository. user is only valid in charm store URLs, and allows you to source charms from individual users (rather than from the main charm store); it must be a valid Launchpad user name. collection denotes a charm's purpose and status, and is derived from the Ubuntu series targeted by its contained charms: examples include "precise", "quantal", "oneiric-universe". name is just the name of the charm; it must start and end with lowercase (ascii) letters, and can otherwise contain any combination of lowercase letters, digits, and "-"s. revision, if specified, points to a specific revision of the charm pointed to by the rest of the URL. It must be a non-negative integer. Endpoint The combination of a service name and a relation name. Environment An Environment is a configured location where Services can be deployed onto. An Environment typically has a name, which can usually be omitted when there's a single Environment configured, or when a default is explicitly defined. Depending on the type of Environment, it may have to be bootstrapped before interactions with it may take place (e.g. EC2). The local environment configuration is defined in the ~/.juju/environments.yaml file. Machine Agent Software which runs inside each machine that is part of an Environment, and is able to handle the needs of deploying and managing Service Units in this machine. Provisioning Agent Software responsible for automatically allocating and terminating machines in an Environment, as necessary for the requested configuration. Relation Relations are the way in which Juju enables Services to communicate to each other, and the way in which the topology of Services is assembled. The Charm defines which Relations a given Service may establish, and what kind of interface these Relations require. In many cases, the establishment of a Relation will result into an actual TCP connection being created between the Service Units, but that's not necessarily the case. Relations may also be established to inform Services of configuration parameters, to request monitoring information, or any other details which the Charm author has chosen to make available. Repository A location where multiple charms are stored. Repositories may be as simple as a directory structure on a local disk, or as complex as a rich smart server supporting remote searching and so on. Service Juju operates in terms of services. A service is any application (or set of applications) that is integrated into the framework as an individual component which should generally be joined with other components to perform a more complex goal. As an example, WordPress could be deployed as a service and, to perform its tasks properly, might communicate with a database service and a load balancer service. Service Configuration There are many different settings in a Juju deployment, but the term Service Configuration refers to the settings which a user can define to customize the behavior of a Service. The behavior of a Service when its Service Configuration changes is entirely defined by its Charm. Service Unit A running instance of a given Juju Service. Simple Services may be deployed with a single Service Unit, but it is possible for an individual Service to have multiple Service Units running in independent machines. All Service Units for a given Service will share the same Charm, the same relations, and the same user-provided configuration. For instance, one may deploy a single MongoDB Service, and specify that it should run 3 Units, so that the replica set is resilient to failures. Internally, even though the replica set shares the same user-provided configuration, each Unit may be performing different roles within the replica set, as defined by the Charm. Service Unit Agent Software which manages all the lifecycle of a single Service Unit. ` const helpLogging = ` Juju has logging available for both client and server components. Most users' exposure to the logging mechanism is through either the 'debug-log' command, or through the log file stored on the bootstrap node at /var/log/juju/all-machines.log. All the agents have their own log files on the individual machines. So for the bootstrap node, there is the machine agent log file at /var/log/juju/machine-0.log. When a unit is deployed on a machine, a unit agent is started. This agent also logs to /var/log/juju and the name of the log file is based on the id of the unit, so for wordpress/0 the log file is unit-wordpress-0.log. Juju uses rsyslog to forward the content of all the log files on the machine back to the bootstrap node, and they are accumulated into the all-machines.log file. Each line is prefixed with the source agent tag (also the same as the filename without the extension). Juju has a hierarchical logging system internally, and as a user you can control how much information is logged out. Output from the charm hook execution comes under the log name "unit". By default Juju makes sure that this information is logged out at the DEBUG level. If you explicitly specify a value for unit, then this is used instead. Juju internal logging comes under the log name "juju". Different areas of the codebase have different anmes. For example: providers are under juju.provider workers are under juju.worker database parts are under juju.state All the agents are started with all logging set to DEBUG. Which means you see all the internal juju logging statements until the logging worker starts and updates the logging configuration to be what is stored for the environment. You can set the logging levels using a number of different mechanisms. environments.yaml - all environments support 'logging-config' as a key - logging-config: ... environment variable - export JUJU_LOGGING_CONFIG='...' setting the logging-config at bootstrap time - juju bootstrap --logging-config='...' juju set-environment logging-config='...' Configuration values are separated by semicolons. Examples: juju set-environment logging-config "juju=WARNING; unit=INFO" Developers may well like: export JUJU_LOGGING_CONFIG='juju=INFO; juju.current.work.area=TRACE' Valid logging levels: CRITICAL ERROR WARNING INFO DEBUG TRACE ` ����������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/ssh.go����������������������������������������0000644�0000153�0000161�00000013330�12321735776�023541� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "fmt" "time" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" ) // SSHCommand is responsible for launching a ssh shell on a given unit or machine. type SSHCommand struct { SSHCommon } // SSHCommon provides common methods for SSHCommand, SCPCommand and DebugHooksCommand. type SSHCommon struct { cmd.EnvCommandBase Target string Args []string apiClient *api.Client // Only used for compatibility with 1.16 rawConn *juju.Conn } const sshDoc = ` Launch an ssh shell on the machine identified by the <target> parameter. <target> can be either a machine id as listed by "juju status" in the "machines" section or a unit name as listed in the "services" section. Any extra parameters are passsed as extra parameters to the ssh command. Examples: Connect to machine 0: juju ssh 0 Connect to machine 1 and run 'uname -a': juju ssh 1 uname -a Connect to the first mysql unit: juju ssh mysql/0 Connect to the first mysql unit and run 'ls -la /var/log/juju': juju ssh mysql/0 ls -la /var/log/juju ` func (c *SSHCommand) Info() *cmd.Info { return &cmd.Info{ Name: "ssh", Args: "<target> [<ssh args>...]", Purpose: "launch an ssh shell on a given unit or machine", Doc: sshDoc, } } func (c *SSHCommand) Init(args []string) error { if len(args) == 0 { return errors.New("no target name specified") } c.Target, c.Args = args[0], args[1:] return nil } // Run resolves c.Target to a machine, to the address of a i // machine or unit forks ssh passing any arguments provided. func (c *SSHCommand) Run(ctx *cmd.Context) error { if c.apiClient == nil { var err error c.apiClient, err = c.initAPIClient() if err != nil { return err } defer c.apiClient.Close() } host, err := c.hostFromTarget(c.Target) if err != nil { return err } var options ssh.Options options.EnablePTY() cmd := ssh.Command("ubuntu@"+host, c.Args, &options) cmd.Stdin = ctx.Stdin cmd.Stdout = ctx.Stdout cmd.Stderr = ctx.Stderr return cmd.Run() } // initAPIClient initialises the API connection. // It is the caller's responsibility to close the connection. func (c *SSHCommon) initAPIClient() (*api.Client, error) { var err error c.apiClient, err = juju.NewAPIClientFromName(c.EnvName) return c.apiClient, err } // attemptStarter is an interface corresponding to utils.AttemptStrategy type attemptStarter interface { Start() attempt } type attempt interface { Next() bool } type attemptStrategy utils.AttemptStrategy func (s attemptStrategy) Start() attempt { return utils.AttemptStrategy(s).Start() } var sshHostFromTargetAttemptStrategy attemptStarter = attemptStrategy{ Total: 5 * time.Second, Delay: 500 * time.Millisecond, } // ensureRawConn ensures that c.rawConn is valid (or returns an error) // This is only for compatibility with a 1.16 API server (that doesn't have // some of the API added more recently.) It can be removed once we no longer // need compatibility with direct access to the state database func (c *SSHCommon) ensureRawConn() error { if c.rawConn != nil { return nil } var err error c.rawConn, err = juju.NewConnFromName(c.EnvName) return err } func (c *SSHCommon) hostFromTarget1dot16(target string) (string, error) { err := c.ensureRawConn() if err != nil { return "", err } // is the target the id of a machine ? if names.IsMachine(target) { logger.Infof("looking up address for machine %s...", target) // This is not the exact code from the 1.16 client // (machinePublicAddress), however it is the code used in the // apiserver behind the PublicAddress call. (1.16 didn't know // about SelectPublicAddress) // The old code watched for changes on the Machine until it had // an InstanceId and then would return the instance.WaitDNS() machine, err := c.rawConn.State.Machine(target) if err != nil { return "", err } addr := instance.SelectPublicAddress(machine.Addresses()) if addr == "" { return "", fmt.Errorf("machine %q has no public address", machine) } return addr, nil } // maybe the target is a unit ? if names.IsUnit(target) { logger.Infof("looking up address for unit %q...", c.Target) unit, err := c.rawConn.State.Unit(target) if err != nil { return "", err } addr, ok := unit.PublicAddress() if !ok { return "", fmt.Errorf("unit %q has no public address", unit) } return addr, nil } return "", fmt.Errorf("unknown unit or machine %q", target) } func (c *SSHCommon) hostFromTarget(target string) (string, error) { var addr string var err error var useStateConn bool // A target may not initially have an address (e.g. the // address updater hasn't yet run), so we must do this in // a loop. for a := sshHostFromTargetAttemptStrategy.Start(); a.Next(); { if !useStateConn { addr, err = c.apiClient.PublicAddress(target) if params.IsCodeNotImplemented(err) { logger.Infof("API server does not support Client.PublicAddress falling back to 1.16 compatibility mode (direct DB access)") useStateConn = true } } if useStateConn { addr, err = c.hostFromTarget1dot16(target) } if err == nil { break } } if err != nil { return "", err } logger.Infof("Resolved public address of %q: %q", target, addr) return addr, nil } // AllowInterspersedFlags for ssh/scp is set to false so that // flags after the unit name are passed through to ssh, for eg. // `juju ssh -v service-name/0 uname -a`. func (c *SSHCommon) AllowInterspersedFlags() bool { return false } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/scp_test.go�����������������������������������0000644�0000153�0000161�00000014207�12321735776�024574� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "fmt" "io/ioutil" "net/url" "path/filepath" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/instance" coretesting "launchpad.net/juju-core/testing" ) var _ = gc.Suite(&SCPSuite{}) var _ = gc.Suite(&expandArgsSuite{}) type SCPSuite struct { SSHCommonSuite } type expandArgsSuite struct{} var scpTests = []struct { about string args []string result string error string }{ { "scp from machine 0 to current dir", []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 + "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 + "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 + "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", "", }, { "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 + "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", }, } func (s *SCPSuite) TestSCPCommand(c *gc.C) { m := s.makeMachines(4, c, true) ch := coretesting.Charms.Dir("dummy") curl := charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", ch.Meta().Name, ch.Revision()), ) bundleURL, err := url.Parse("http://bundles.testing.invalid/dummy-1") c.Assert(err, gc.IsNil) dummyCharm, err := s.State.AddCharm(ch, curl, bundleURL, "dummy-1-sha256") c.Assert(err, gc.IsNil) srv := s.AddTestingService(c, "mysql", dummyCharm) s.addUnit(srv, m[0], c) srv = s.AddTestingService(c, "mongodb", dummyCharm) s.addUnit(srv, m[1], c) s.addUnit(srv, m[2], c) // Simulate machine 3 has a public IPv6 address. ipv6Addr := instance.Address{ Value: "2001:db8::", Type: instance.Ipv4Address, // ..because SelectPublicAddress ignores IPv6 addresses NetworkScope: instance.NetworkPublic, } err = m[3].SetAddresses([]instance.Address{ipv6Addr}) c.Assert(err, gc.IsNil) srv = s.AddTestingService(c, "ipv6-svc", dummyCharm) s.addUnit(srv, m[3], c) for i, t := range scpTests { c.Logf("test %d: %s -> %s\n", i, t.about, t.args) ctx := coretesting.Context(c) scpcmd := &SCPCommand{} err := scpcmd.Init(t.args) c.Check(err, gc.IsNil) err = scpcmd.Run(ctx) if t.error != "" { c.Check(err, gc.ErrorMatches, t.error) c.Check(t.result, gc.Equals, "") } else { c.Check(err, gc.IsNil) // we suppress stdout from scp c.Check(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "") c.Check(ctx.Stdout.(*bytes.Buffer).String(), gc.Equals, "") data, err := ioutil.ReadFile(filepath.Join(s.bin, "scp.args")) c.Check(err, gc.IsNil) c.Check(string(data), gc.Equals, t.result) } } } 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) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/addmachine_test.go����������������������������0000644�0000153�0000161�00000007503�12321735642�026055� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "strconv" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/testing" ) type AddMachineSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&AddMachineSuite{}) func runAddMachine(c *gc.C, args ...string) error { _, err := testing.RunCommand(c, &AddMachineCommand{}, args) return err } func (s *AddMachineSuite) TestAddMachine(c *gc.C) { err := runAddMachine(c) c.Assert(err, gc.IsNil) m, err := s.State.Machine("0") c.Assert(err, gc.IsNil) c.Assert(m.Life(), gc.Equals, state.Alive) c.Assert(m.Series(), gc.DeepEquals, "precise") mcons, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(&mcons, jc.Satisfies, constraints.IsEmpty) } func (s *AddMachineSuite) TestAddMachineWithSeries(c *gc.C) { err := runAddMachine(c, "--series", "series") c.Assert(err, gc.IsNil) m, err := s.State.Machine("0") c.Assert(err, gc.IsNil) c.Assert(m.Series(), gc.DeepEquals, "series") } func (s *AddMachineSuite) TestAddMachineWithConstraints(c *gc.C) { err := runAddMachine(c, "--constraints", "mem=4G") c.Assert(err, gc.IsNil) m, err := s.State.Machine("0") c.Assert(err, gc.IsNil) mcons, err := m.Constraints() c.Assert(err, gc.IsNil) expectedCons := constraints.MustParse("mem=4G") c.Assert(mcons, gc.DeepEquals, expectedCons) } func (s *AddMachineSuite) _assertAddContainer(c *gc.C, parentId, containerId string, ctype instance.ContainerType) { m, err := s.State.Machine(parentId) c.Assert(err, gc.IsNil) containers, err := m.Containers() c.Assert(err, gc.IsNil) c.Assert(containers, gc.DeepEquals, []string{containerId}) container, err := s.State.Machine(containerId) c.Assert(err, gc.IsNil) containers, err = container.Containers() c.Assert(err, gc.IsNil) c.Assert(containers, gc.DeepEquals, []string(nil)) c.Assert(container.ContainerType(), gc.Equals, ctype) } func (s *AddMachineSuite) TestAddContainerToNewMachine(c *gc.C) { for i, ctype := range instance.ContainerTypes { c.Logf("test %d: %s", i, ctype) err := runAddMachine(c, string(ctype)) c.Assert(err, gc.IsNil) s._assertAddContainer(c, strconv.Itoa(i), fmt.Sprintf("%d/%s/0", i, ctype), ctype) } } func (s *AddMachineSuite) TestAddContainerToExistingMachine(c *gc.C) { err := runAddMachine(c) c.Assert(err, gc.IsNil) for i, container := range instance.ContainerTypes { machineNum := strconv.Itoa(i + 1) err = runAddMachine(c) c.Assert(err, gc.IsNil) err := runAddMachine(c, fmt.Sprintf("%s:%s", container, machineNum)) c.Assert(err, gc.IsNil) s._assertAddContainer(c, machineNum, fmt.Sprintf("%s/%s/0", machineNum, container), container) } } func (s *AddMachineSuite) TestAddUnsupportedContainerToMachine(c *gc.C) { err := runAddMachine(c) c.Assert(err, gc.IsNil) m, err := s.State.Machine("0") c.Assert(err, gc.IsNil) m.SetSupportedContainers([]instance.ContainerType{instance.KVM}) err = runAddMachine(c, "lxc:0") c.Assert(err, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host lxc containers") } func (s *AddMachineSuite) TestAddMachineErrors(c *gc.C) { err := runAddMachine(c, ":lxc") c.Assert(err, gc.ErrorMatches, `malformed container argument ":lxc"`) err = runAddMachine(c, "lxc:") c.Assert(err, gc.ErrorMatches, `malformed container argument "lxc:"`) err = runAddMachine(c, "2") c.Assert(err, gc.ErrorMatches, `malformed container argument "2"`) err = runAddMachine(c, "foo") c.Assert(err, gc.ErrorMatches, `malformed container argument "foo"`) err = runAddMachine(c, "lxc", "--constraints", "container=lxc") c.Assert(err, gc.ErrorMatches, `container constraint "lxc" not allowed when adding a machine`) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/publish_test.go�������������������������������0000644�0000153�0000161�00000032620�12321735776�025454� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" gc "launchpad.net/gocheck" "launchpad.net/juju-core/bzr" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) // Sadly, this is a very slow test suite, heavily dominated by calls to bzr. type PublishSuite struct { testbase.LoggingSuite testing.HTTPSuite home *testing.FakeHome dir string oldBaseURL string branch *bzr.Branch } var _ = gc.Suite(&PublishSuite{}) func touch(c *gc.C, filename string) { f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644) c.Assert(err, gc.IsNil) f.Close() } func addMeta(c *gc.C, branch *bzr.Branch, meta string) { if meta == "" { meta = "name: wordpress\nsummary: Some summary\ndescription: Some description.\n" } f, err := os.Create(branch.Join("metadata.yaml")) c.Assert(err, gc.IsNil) _, err = f.Write([]byte(meta)) f.Close() c.Assert(err, gc.IsNil) err = branch.Add("metadata.yaml") c.Assert(err, gc.IsNil) err = branch.Commit("Added metadata.yaml.") c.Assert(err, gc.IsNil) } func (s *PublishSuite) runPublish(c *gc.C, args ...string) (*cmd.Context, error) { return testing.RunCommandInDir(c, &PublishCommand{}, args, s.dir) } const pollDelay = testing.ShortWait func (s *PublishSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.HTTPSuite.SetUpSuite(c) s.oldBaseURL = charm.Store.BaseURL charm.Store.BaseURL = s.URL("") } func (s *PublishSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) s.HTTPSuite.TearDownSuite(c) charm.Store.BaseURL = s.oldBaseURL } func (s *PublishSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.HTTPSuite.SetUpTest(c) s.home = testing.MakeFakeHomeWithFiles(c, []testing.TestFile{ { Name: ".bazaar/bazaar.conf", Data: "[DEFAULT]\nemail = Test <testing@testing.invalid>\n", }, }) s.dir = c.MkDir() s.branch = bzr.New(s.dir) err := s.branch.Init() c.Assert(err, gc.IsNil) } func (s *PublishSuite) TearDownTest(c *gc.C) { s.home.Restore() s.HTTPSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *PublishSuite) TestNoBranch(c *gc.C) { dir := c.MkDir() _, err := testing.RunCommandInDir(c, &PublishCommand{}, []string{"cs:precise/wordpress"}, dir) c.Assert(err, gc.ErrorMatches, fmt.Sprintf("not a charm branch: %s", dir)) } func (s *PublishSuite) TestEmpty(c *gc.C) { _, err := s.runPublish(c, "cs:precise/wordpress") c.Assert(err, gc.ErrorMatches, `cannot obtain local digest: branch has no content`) } func (s *PublishSuite) TestFrom(c *gc.C) { _, err := testing.RunCommandInDir(c, &PublishCommand{}, []string{"--from", s.dir, "cs:precise/wordpress"}, c.MkDir()) c.Assert(err, gc.ErrorMatches, `cannot obtain local digest: branch has no content`) } func (s *PublishSuite) TestMissingSeries(c *gc.C) { _, err := s.runPublish(c, "cs:wordpress") c.Assert(err, gc.ErrorMatches, `cannot infer charm URL for "cs:wordpress": no series provided`) } func (s *PublishSuite) TestNotClean(c *gc.C) { touch(c, s.branch.Join("file")) _, err := s.runPublish(c, "cs:precise/wordpress") c.Assert(err, gc.ErrorMatches, `branch is not clean \(bzr status\)`) } func (s *PublishSuite) TestNoPushLocation(c *gc.C) { addMeta(c, s.branch, "") _, err := s.runPublish(c) c.Assert(err, gc.ErrorMatches, `no charm URL provided and cannot infer from current directory \(no push location\)`) } func (s *PublishSuite) TestUnknownPushLocation(c *gc.C) { addMeta(c, s.branch, "") err := s.branch.Push(&bzr.PushAttr{Location: c.MkDir() + "/foo", Remember: true}) c.Assert(err, gc.IsNil) _, err = s.runPublish(c) c.Assert(err, gc.ErrorMatches, `cannot infer charm URL from branch location: ".*/foo"`) } func (s *PublishSuite) TestWrongRepository(c *gc.C) { addMeta(c, s.branch, "") _, err := s.runPublish(c, "local:precise/wordpress") c.Assert(err, gc.ErrorMatches, "charm URL must reference the juju charm store") } func (s *PublishSuite) TestInferURL(c *gc.C) { addMeta(c, s.branch, "") cmd := &PublishCommand{} cmd.ChangePushLocation(func(location string) string { c.Assert(location, gc.Equals, "lp:charms/precise/wordpress") c.SucceedNow() panic("unreachable") }) _, err := testing.RunCommandInDir(c, cmd, []string{"precise/wordpress"}, s.dir) c.Assert(err, gc.IsNil) c.Fatal("shouldn't get here; location closure didn't run?") } func (s *PublishSuite) TestBrokenCharm(c *gc.C) { addMeta(c, s.branch, "name: wordpress\nsummary: Some summary\n") _, err := s.runPublish(c, "cs:precise/wordpress") c.Assert(err, gc.ErrorMatches, "metadata: description: expected string, got nothing") } func (s *PublishSuite) TestWrongName(c *gc.C) { addMeta(c, s.branch, "") _, err := s.runPublish(c, "cs:precise/mysql") c.Assert(err, gc.ErrorMatches, `charm name in metadata must match name in URL: "wordpress" != "mysql"`) } func (s *PublishSuite) TestPreExistingPublished(c *gc.C) { addMeta(c, s.branch, "") // Pretend the store has seen the digest before, and it has succeeded. digest, err := s.branch.RevisionId() c.Assert(err, gc.IsNil) body := `{"cs:precise/wordpress": {"kind": "published", "digest": %q, "revision": 42}}` testing.Server.Response(200, nil, []byte(fmt.Sprintf(body, digest))) ctx, err := s.runPublish(c, "cs:precise/wordpress") c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(ctx), gc.Equals, "cs:precise/wordpress-42\n") req := testing.Server.WaitRequest() c.Assert(req.URL.Path, gc.Equals, "/charm-event") c.Assert(req.Form.Get("charms"), gc.Equals, "cs:precise/wordpress@"+digest) } func (s *PublishSuite) TestPreExistingPublishedEdge(c *gc.C) { addMeta(c, s.branch, "") // If it doesn't find the right digest on the first try, it asks again for // any digest at all to keep the tip in mind. There's a small chance that // on the second request the tip has changed and matches the digest we're // looking for, in which case we have the answer already. digest, err := s.branch.RevisionId() c.Assert(err, gc.IsNil) var body string body = `{"cs:precise/wordpress": {"errors": ["entry not found"]}}` testing.Server.Response(200, nil, []byte(body)) body = `{"cs:precise/wordpress": {"kind": "published", "digest": %q, "revision": 42}}` testing.Server.Response(200, nil, []byte(fmt.Sprintf(body, digest))) ctx, err := s.runPublish(c, "cs:precise/wordpress") c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(ctx), gc.Equals, "cs:precise/wordpress-42\n") req := testing.Server.WaitRequest() c.Assert(req.URL.Path, gc.Equals, "/charm-event") c.Assert(req.Form.Get("charms"), gc.Equals, "cs:precise/wordpress@"+digest) req = testing.Server.WaitRequest() c.Assert(req.URL.Path, gc.Equals, "/charm-event") c.Assert(req.Form.Get("charms"), gc.Equals, "cs:precise/wordpress") } func (s *PublishSuite) TestPreExistingPublishError(c *gc.C) { addMeta(c, s.branch, "") // Pretend the store has seen the digest before, and it has failed. digest, err := s.branch.RevisionId() c.Assert(err, gc.IsNil) body := `{"cs:precise/wordpress": {"kind": "publish-error", "digest": %q, "errors": ["an error"]}}` testing.Server.Response(200, nil, []byte(fmt.Sprintf(body, digest))) _, err = s.runPublish(c, "cs:precise/wordpress") c.Assert(err, gc.ErrorMatches, "charm could not be published: an error") req := testing.Server.WaitRequest() c.Assert(req.URL.Path, gc.Equals, "/charm-event") c.Assert(req.Form.Get("charms"), gc.Equals, "cs:precise/wordpress@"+digest) } func (s *PublishSuite) TestFullPublish(c *gc.C) { addMeta(c, s.branch, "") digest, err := s.branch.RevisionId() c.Assert(err, gc.IsNil) pushBranch := bzr.New(c.MkDir()) err = pushBranch.Init() c.Assert(err, gc.IsNil) cmd := &PublishCommand{} cmd.ChangePushLocation(func(location string) string { c.Assert(location, gc.Equals, "lp:~user/charms/precise/wordpress/trunk") return pushBranch.Location() }) cmd.SetPollDelay(testing.ShortWait) var body string // The local digest isn't found. body = `{"cs:~user/precise/wordpress": {"kind": "", "errors": ["entry not found"]}}` testing.Server.Response(200, nil, []byte(body)) // But the charm exists with an arbitrary non-matching digest. body = `{"cs:~user/precise/wordpress": {"kind": "published", "digest": "other-digest"}}` testing.Server.Response(200, nil, []byte(body)) // After the branch is pushed we fake the publishing delay. body = `{"cs:~user/precise/wordpress": {"kind": "published", "digest": "other-digest"}}` testing.Server.Response(200, nil, []byte(body)) // And finally report success. body = `{"cs:~user/precise/wordpress": {"kind": "published", "digest": %q, "revision": 42}}` testing.Server.Response(200, nil, []byte(fmt.Sprintf(body, digest))) ctx, err := testing.RunCommandInDir(c, cmd, []string{"cs:~user/precise/wordpress"}, s.dir) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(ctx), gc.Equals, "cs:~user/precise/wordpress-42\n") // Ensure the branch was actually pushed. pushDigest, err := pushBranch.RevisionId() c.Assert(err, gc.IsNil) c.Assert(pushDigest, gc.Equals, digest) // And that all the requests were sent with the proper data. req := testing.Server.WaitRequest() c.Assert(req.URL.Path, gc.Equals, "/charm-event") c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress@"+digest) for i := 0; i < 3; i++ { // The second request grabs tip to see the current state, and the // following requests are done after pushing to see when it changes. req = testing.Server.WaitRequest() c.Assert(req.URL.Path, gc.Equals, "/charm-event") c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress") } } func (s *PublishSuite) TestFullPublishError(c *gc.C) { addMeta(c, s.branch, "") digest, err := s.branch.RevisionId() c.Assert(err, gc.IsNil) pushBranch := bzr.New(c.MkDir()) err = pushBranch.Init() c.Assert(err, gc.IsNil) cmd := &PublishCommand{} cmd.ChangePushLocation(func(location string) string { c.Assert(location, gc.Equals, "lp:~user/charms/precise/wordpress/trunk") return pushBranch.Location() }) cmd.SetPollDelay(pollDelay) var body string // The local digest isn't found. body = `{"cs:~user/precise/wordpress": {"kind": "", "errors": ["entry not found"]}}` testing.Server.Response(200, nil, []byte(body)) // And tip isn't found either, meaning the charm was never published. testing.Server.Response(200, nil, []byte(body)) // After the branch is pushed we fake the publishing delay. testing.Server.Response(200, nil, []byte(body)) // And finally report success. body = `{"cs:~user/precise/wordpress": {"kind": "published", "digest": %q, "revision": 42}}` testing.Server.Response(200, nil, []byte(fmt.Sprintf(body, digest))) ctx, err := testing.RunCommandInDir(c, cmd, []string{"cs:~user/precise/wordpress"}, s.dir) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(ctx), gc.Equals, "cs:~user/precise/wordpress-42\n") // Ensure the branch was actually pushed. pushDigest, err := pushBranch.RevisionId() c.Assert(err, gc.IsNil) c.Assert(pushDigest, gc.Equals, digest) // And that all the requests were sent with the proper data. req := testing.Server.WaitRequest() c.Assert(req.URL.Path, gc.Equals, "/charm-event") c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress@"+digest) for i := 0; i < 3; i++ { // The second request grabs tip to see the current state, and the // following requests are done after pushing to see when it changes. req = testing.Server.WaitRequest() c.Assert(req.URL.Path, gc.Equals, "/charm-event") c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress") } } func (s *PublishSuite) TestFullPublishRace(c *gc.C) { addMeta(c, s.branch, "") digest, err := s.branch.RevisionId() c.Assert(err, gc.IsNil) pushBranch := bzr.New(c.MkDir()) err = pushBranch.Init() c.Assert(err, gc.IsNil) cmd := &PublishCommand{} cmd.ChangePushLocation(func(location string) string { c.Assert(location, gc.Equals, "lp:~user/charms/precise/wordpress/trunk") return pushBranch.Location() }) cmd.SetPollDelay(pollDelay) var body string // The local digest isn't found. body = `{"cs:~user/precise/wordpress": {"kind": "", "errors": ["entry not found"]}}` testing.Server.Response(200, nil, []byte(body)) // And tip isn't found either, meaning the charm was never published. testing.Server.Response(200, nil, []byte(body)) // After the branch is pushed we fake the publishing delay. testing.Server.Response(200, nil, []byte(body)) // But, surprisingly, the digest changed to something else entirely. body = `{"cs:~user/precise/wordpress": {"kind": "published", "digest": "surprising-digest", "revision": 42}}` testing.Server.Response(200, nil, []byte(body)) _, err = testing.RunCommandInDir(c, cmd, []string{"cs:~user/precise/wordpress"}, s.dir) c.Assert(err, gc.ErrorMatches, `charm changed but not to local charm digest; publishing race\?`) // Ensure the branch was actually pushed. pushDigest, err := pushBranch.RevisionId() c.Assert(err, gc.IsNil) c.Assert(pushDigest, gc.Equals, digest) // And that all the requests were sent with the proper data. req := testing.Server.WaitRequest() c.Assert(req.URL.Path, gc.Equals, "/charm-event") c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress@"+digest) for i := 0; i < 3; i++ { // The second request grabs tip to see the current state, and the // following requests are done after pushing to see when it changes. req = testing.Server.WaitRequest() c.Assert(req.URL.Path, gc.Equals, "/charm-event") c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress") } } ����������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/destroyenvironment_test.go��������������������0000644�0000153�0000161�00000016161�12321735642�027756� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/dummy" coretesting "launchpad.net/juju-core/testing" ) type destroyEnvSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&destroyEnvSuite{}) func (s *destroyEnvSuite) TestDestroyEnvironmentCommand(c *gc.C) { // Prepare the environment so we can destroy it. _, err := environs.PrepareFromName("dummyenv", nullContext(c), s.ConfigStore) c.Assert(err, gc.IsNil) // check environment is mandatory opc, errc := runCommand(nullContext(c), new(DestroyEnvironmentCommand)) c.Check(<-errc, gc.Equals, NoEnvironmentError) // normal destroy opc, errc = runCommand(nullContext(c), new(DestroyEnvironmentCommand), "dummyenv", "--yes") c.Check(<-errc, gc.IsNil) c.Check((<-opc).(dummy.OpDestroy).Env, gc.Equals, "dummyenv") // Verify that the environment information has been removed. _, err = s.ConfigStore.ReadInfo("dummyenv") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *destroyEnvSuite) TestDestroyEnvironmentCommandEFlag(c *gc.C) { // Prepare the environment so we can destroy it. _, err := environs.PrepareFromName("dummyenv", nullContext(c), s.ConfigStore) c.Assert(err, gc.IsNil) // check that either environment or the flag is mandatory opc, errc := runCommand(nullContext(c), new(DestroyEnvironmentCommand)) c.Check(<-errc, gc.Equals, NoEnvironmentError) // We don't allow them to supply both entries at the same time opc, errc = runCommand(nullContext(c), new(DestroyEnvironmentCommand), "-e", "dummyenv", "dummyenv", "--yes") c.Check(<-errc, gc.Equals, DoubleEnvironmentError) // We treat --environment the same way opc, errc = runCommand(nullContext(c), new(DestroyEnvironmentCommand), "--environment", "dummyenv", "dummyenv", "--yes") c.Check(<-errc, gc.Equals, DoubleEnvironmentError) // destroy using the -e flag opc, errc = runCommand(nullContext(c), new(DestroyEnvironmentCommand), "-e", "dummyenv", "--yes") c.Check(<-errc, gc.IsNil) c.Check((<-opc).(dummy.OpDestroy).Env, gc.Equals, "dummyenv") // Verify that the environment information has been removed. _, err = s.ConfigStore.ReadInfo("dummyenv") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *destroyEnvSuite) TestDestroyEnvironmentCommandEmptyJenv(c *gc.C) { _, err := s.ConfigStore.CreateInfo("emptyenv") c.Assert(err, gc.IsNil) context, err := coretesting.RunCommand(c, new(DestroyEnvironmentCommand), []string{"-e", "emptyenv"}) c.Assert(err, gc.IsNil) c.Assert(coretesting.Stderr(context), gc.Equals, "removing empty environment file\n") } func (s *destroyEnvSuite) TestDestroyEnvironmentCommandBroken(c *gc.C) { oldinfo, err := s.ConfigStore.ReadInfo("dummyenv") c.Assert(err, gc.IsNil) bootstrapConfig := oldinfo.BootstrapConfig() apiEndpoint := oldinfo.APIEndpoint() apiCredentials := oldinfo.APICredentials() err = oldinfo.Destroy() c.Assert(err, gc.IsNil) newinfo, err := s.ConfigStore.CreateInfo("dummyenv") c.Assert(err, gc.IsNil) bootstrapConfig["broken"] = "Destroy" newinfo.SetBootstrapConfig(bootstrapConfig) newinfo.SetAPIEndpoint(apiEndpoint) newinfo.SetAPICredentials(apiCredentials) err = newinfo.Write() c.Assert(err, gc.IsNil) // Prepare the environment so we can destroy it. _, err = environs.PrepareFromName("dummyenv", nullContext(c), s.ConfigStore) c.Assert(err, gc.IsNil) // destroy with broken environment opc, errc := runCommand(nullContext(c), new(DestroyEnvironmentCommand), "dummyenv", "--yes") op, ok := (<-opc).(dummy.OpDestroy) c.Assert(ok, jc.IsTrue) c.Assert(op.Error, gc.ErrorMatches, "dummy.Destroy is broken") c.Check(<-errc, gc.Equals, op.Error) c.Check(<-opc, gc.IsNil) } func (*destroyEnvSuite) TestDestroyEnvironmentCommandConfirmationFlag(c *gc.C) { com := new(DestroyEnvironmentCommand) c.Check(coretesting.InitCommand(com, []string{"dummyenv"}), gc.IsNil) c.Check(com.assumeYes, gc.Equals, false) com = new(DestroyEnvironmentCommand) c.Check(coretesting.InitCommand(com, []string{"dummyenv", "-y"}), gc.IsNil) c.Check(com.assumeYes, gc.Equals, true) com = new(DestroyEnvironmentCommand) c.Check(coretesting.InitCommand(com, []string{"dummyenv", "--yes"}), gc.IsNil) c.Check(com.assumeYes, gc.Equals, true) } func (s *destroyEnvSuite) TestDestroyEnvironmentCommandConfirmation(c *gc.C) { var stdin, stdout bytes.Buffer ctx, err := cmd.DefaultContext() c.Assert(err, gc.IsNil) ctx.Stdout = &stdout ctx.Stdin = &stdin // Prepare the environment so we can destroy it. env, err := environs.PrepareFromName("dummyenv", nullContext(c), s.ConfigStore) c.Assert(err, gc.IsNil) assertEnvironNotDestroyed(c, env, s.ConfigStore) // Ensure confirmation is requested if "-y" is not specified. stdin.WriteString("n") opc, errc := runCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv") c.Check(<-errc, gc.ErrorMatches, "environment destruction aborted") c.Check(<-opc, gc.IsNil) c.Check(stdout.String(), gc.Matches, "WARNING!.*dummyenv.*\\(type: dummy\\)(.|\n)*") assertEnvironNotDestroyed(c, env, s.ConfigStore) // EOF on stdin: equivalent to answering no. stdin.Reset() stdout.Reset() opc, errc = runCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv") c.Check(<-opc, gc.IsNil) c.Check(<-errc, gc.ErrorMatches, "environment destruction aborted") assertEnvironNotDestroyed(c, env, s.ConfigStore) // "--yes" passed: no confirmation request. stdin.Reset() stdout.Reset() opc, errc = runCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv", "--yes") c.Check(<-errc, gc.IsNil) c.Check((<-opc).(dummy.OpDestroy).Env, gc.Equals, "dummyenv") c.Check(stdout.String(), gc.Equals, "") assertEnvironDestroyed(c, env, s.ConfigStore) // Any of casing of "y" and "yes" will confirm. for _, answer := range []string{"y", "Y", "yes", "YES"} { // Prepare the environment so we can destroy it. s.Reset(c) env, err := environs.PrepareFromName("dummyenv", nullContext(c), s.ConfigStore) c.Assert(err, gc.IsNil) stdin.Reset() stdout.Reset() stdin.WriteString(answer) opc, errc = runCommand(ctx, new(DestroyEnvironmentCommand), "dummyenv") c.Check(<-errc, gc.IsNil) c.Check((<-opc).(dummy.OpDestroy).Env, gc.Equals, "dummyenv") c.Check(stdout.String(), gc.Matches, "WARNING!.*dummyenv.*\\(type: dummy\\)(.|\n)*") assertEnvironDestroyed(c, env, s.ConfigStore) } } func assertEnvironDestroyed(c *gc.C, env environs.Environ, store configstore.Storage) { _, err := store.ReadInfo(env.Name()) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) _, err = env.Instances([]instance.Id{"invalid"}) c.Assert(err, gc.ErrorMatches, "environment has been destroyed") } func assertEnvironNotDestroyed(c *gc.C, env environs.Environ, store configstore.Storage) { info, err := store.ReadInfo(env.Name()) c.Assert(err, gc.IsNil) c.Assert(info.Initialized(), jc.IsTrue) _, err = environs.NewFromName(env.Name(), store) c.Assert(err, gc.IsNil) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/destroyunit.go��������������������������������0000644�0000153�0000161�00000002236�12321735776�025340� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "fmt" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" ) // DestroyUnitCommand is responsible for destroying service units. type DestroyUnitCommand struct { cmd.EnvCommandBase UnitNames []string } func (c *DestroyUnitCommand) Info() *cmd.Info { return &cmd.Info{ Name: "destroy-unit", Args: "<unit> [...]", Purpose: "destroy service units", Aliases: []string{"remove-unit"}, } } func (c *DestroyUnitCommand) Init(args []string) error { c.UnitNames = args if len(c.UnitNames) == 0 { return errors.New("no units specified") } for _, name := range c.UnitNames { if !names.IsUnit(name) { return fmt.Errorf("invalid unit name %q", name) } } return nil } // Run connects to the environment specified on the command line and destroys // units therein. func (c *DestroyUnitCommand) Run(_ *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() return client.DestroyServiceUnits(c.UnitNames...) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/authorisedkeys_delete.go����������������������0000644�0000153�0000161�00000003200�12321735776�027324� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" ) var deleteKeysDoc = ` Delete existing authorised ssh keys to remove ssh access for the holder of those keys. The keys to delete are found by specifying either the "comment" portion of the ssh key, typically something like "user@host", or the key fingerprint found by using ssh-keygen. ` // DeleteKeysCommand is used to delete authorized ssh keys for a user. type DeleteKeysCommand struct { cmd.EnvCommandBase user string keyIds []string } func (c *DeleteKeysCommand) Info() *cmd.Info { return &cmd.Info{ Name: "delete", Args: "<ssh key id> [...]", Doc: deleteKeysDoc, Purpose: "delete authorized ssh keys for a Juju user", } } func (c *DeleteKeysCommand) Init(args []string) error { switch len(args) { case 0: return errors.New("no ssh key id specified") default: c.keyIds = args } return nil } func (c *DeleteKeysCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.StringVar(&c.user, "user", "admin", "the user for which to delete the keys") } func (c *DeleteKeysCommand) Run(context *cmd.Context) error { client, err := juju.NewKeyManagerClient(c.EnvName) if err != nil { return err } defer client.Close() results, err := client.DeleteKeys(c.user, c.keyIds...) if err != nil { return err } for i, result := range results { if result.Error != nil { fmt.Fprintf(context.Stderr, "cannot delete key id %q: %v\n", c.keyIds[i], result.Error) } } return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/init_test.go����������������������������������0000644�0000153�0000161�00000006443�12321735642�024745� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "io/ioutil" "strings" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" ) type InitSuite struct { } var _ = gc.Suite(&InitSuite{}) // The environments.yaml is created by default if it // does not already exist. func (*InitSuite) TestBoilerPlateEnvironment(c *gc.C) { defer testing.MakeEmptyFakeHome(c).Restore() ctx := testing.Context(c) code := cmd.Main(&InitCommand{}, ctx, nil) c.Check(code, gc.Equals, 0) outStr := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(outStr, "\n", "", -1) c.Check(strippedOut, gc.Matches, ".*A boilerplate environment configuration file has been written.*") environpath := testing.HomePath(".juju", "environments.yaml") data, err := ioutil.ReadFile(environpath) c.Assert(err, gc.IsNil) strippedData := strings.Replace(string(data), "\n", "", -1) c.Assert(strippedData, gc.Matches, ".*# This is the Juju config file, which you can use.*") } // The boilerplate is sent to stdout with --show, and the environments.yaml // is not created. func (*InitSuite) TestBoilerPlatePrinted(c *gc.C) { defer testing.MakeEmptyFakeHome(c).Restore() ctx := testing.Context(c) code := cmd.Main(&InitCommand{}, ctx, []string{"--show"}) c.Check(code, gc.Equals, 0) outStr := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(outStr, "\n", "", -1) c.Check(strippedOut, gc.Matches, ".*# This is the Juju config file, which you can use.*") environpath := testing.HomePath(".juju", "environments.yaml") _, err := ioutil.ReadFile(environpath) c.Assert(err, gc.NotNil) } const existingEnv = ` environments: test: type: dummy state-server: false authorized-keys: i-am-a-key ` // An existing environments.yaml will not be overwritten without // the explicit -f option. func (*InitSuite) TestExistingEnvironmentNotOverwritten(c *gc.C) { defer testing.MakeFakeHome(c, existingEnv, "existing").Restore() ctx := testing.Context(c) code := cmd.Main(&InitCommand{}, ctx, nil) c.Check(code, gc.Equals, 1) errOut := ctx.Stderr.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, ".*A juju environment configuration already exists.*") environpath := testing.HomePath(".juju", "environments.yaml") data, err := ioutil.ReadFile(environpath) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, existingEnv) } // An existing environments.yaml will be overwritten when -f is // given explicitly. func (*InitSuite) TestExistingEnvironmentOverwritten(c *gc.C) { defer testing.MakeFakeHome(c, existingEnv, "existing").Restore() ctx := testing.Context(c) code := cmd.Main(&InitCommand{}, ctx, []string{"-f"}) c.Check(code, gc.Equals, 0) stdOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(stdOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, ".*A boilerplate environment configuration file has been written.*") environpath := testing.HomePath(".juju", "environments.yaml") data, err := ioutil.ReadFile(environpath) c.Assert(err, gc.IsNil) strippedData := strings.Replace(string(data), "\n", "", -1) c.Assert(strippedData, gc.Matches, ".*# This is the Juju config file, which you can use.*") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/main.go���������������������������������������0000644�0000153�0000161�00000013503�12321735776�023672� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "github.com/juju/loggo" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/juju" // Import the providers. _ "launchpad.net/juju-core/provider/all" ) var logger = loggo.GetLogger("juju.cmd.juju") var jujuDoc = ` juju provides easy, intelligent service orchestration on top of cloud infrastructure providers such as Amazon EC2, HP Cloud, MaaS, OpenStack, Windows Azure, or your local machine. https://juju.ubuntu.com/ ` var x = []byte("\x96\x8c\x99\x8a\x9c\x94\x96\x91\x98\xdf\x9e\x92\x9e\x85\x96\x91\x98\xf5") // Main registers subcommands for the juju executable, and hands over control // to the cmd package. This function is not redundant with main, because it // provides an entry point for testing with arbitrary command line arguments. func Main(args []string) { ctx, err := cmd.DefaultContext() if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(2) } if err = juju.InitJujuHome(); err != nil { fmt.Fprintf(os.Stderr, "error: %s\n", err) os.Exit(2) } for i := range x { x[i] ^= 255 } if len(args) == 2 && args[1] == string(x[0:2]) { os.Stdout.Write(x[2:]) os.Exit(0) } jujucmd := cmd.NewSuperCommand(cmd.SuperCommandParams{ Name: "juju", Doc: jujuDoc, Log: &cmd.Log{}, MissingCallback: RunPlugin, }) jujucmd.AddHelpTopic("basics", "Basic commands", helpBasics) jujucmd.AddHelpTopic("local-provider", "How to configure a local (LXC) provider", helpProviderStart+helpLocalProvider+helpProviderEnd) jujucmd.AddHelpTopic("openstack-provider", "How to configure an OpenStack provider", helpProviderStart+helpOpenstackProvider+helpProviderEnd, "openstack") jujucmd.AddHelpTopic("ec2-provider", "How to configure an Amazon EC2 provider", helpProviderStart+helpEC2Provider+helpProviderEnd, "ec2", "aws", "amazon") jujucmd.AddHelpTopic("hpcloud-provider", "How to configure an HP Cloud provider", helpProviderStart+helpHPCloud+helpProviderEnd, "hpcloud", "hp-cloud") jujucmd.AddHelpTopic("azure-provider", "How to configure a Windows Azure provider", helpProviderStart+helpAzureProvider+helpProviderEnd, "azure") jujucmd.AddHelpTopic("constraints", "How to use commands with constraints", helpConstraints) jujucmd.AddHelpTopic("glossary", "Glossary of terms", helpGlossary) jujucmd.AddHelpTopic("logging", "How Juju handles logging", helpLogging) jujucmd.AddHelpTopicCallback("plugins", "Show Juju plugins", PluginHelpTopic) // Creation commands. jujucmd.Register(wrap(&BootstrapCommand{})) jujucmd.Register(wrap(&AddMachineCommand{})) jujucmd.Register(wrap(&DeployCommand{})) jujucmd.Register(wrap(&AddRelationCommand{})) jujucmd.Register(wrap(&AddUnitCommand{})) // Destruction commands. jujucmd.Register(wrap(&DestroyMachineCommand{})) jujucmd.Register(wrap(&DestroyRelationCommand{})) jujucmd.Register(wrap(&DestroyServiceCommand{})) jujucmd.Register(wrap(&DestroyUnitCommand{})) jujucmd.Register(wrap(&DestroyEnvironmentCommand{})) // Reporting commands. jujucmd.Register(wrap(&StatusCommand{})) jujucmd.Register(wrap(&SwitchCommand{})) jujucmd.Register(wrap(&EndpointCommand{})) // Error resolution and debugging commands. jujucmd.Register(wrap(&RunCommand{})) jujucmd.Register(wrap(&SCPCommand{})) jujucmd.Register(wrap(&SSHCommand{})) jujucmd.Register(wrap(&ResolvedCommand{})) jujucmd.Register(wrap(&DebugLogCommand{sshCmd: &SSHCommand{}})) jujucmd.Register(wrap(&DebugHooksCommand{})) jujucmd.Register(wrap(&RetryProvisioningCommand{})) // Configuration commands. jujucmd.Register(wrap(&InitCommand{})) jujucmd.Register(wrap(&GetCommand{})) jujucmd.Register(wrap(&SetCommand{})) jujucmd.Register(wrap(&UnsetCommand{})) jujucmd.Register(wrap(&GetConstraintsCommand{})) jujucmd.Register(wrap(&SetConstraintsCommand{})) jujucmd.Register(wrap(&GetEnvironmentCommand{})) jujucmd.Register(wrap(&SetEnvironmentCommand{})) jujucmd.Register(wrap(&UnsetEnvironmentCommand{})) jujucmd.Register(wrap(&ExposeCommand{})) jujucmd.Register(wrap(&SyncToolsCommand{})) jujucmd.Register(wrap(&UnexposeCommand{})) jujucmd.Register(wrap(&UpgradeJujuCommand{})) jujucmd.Register(wrap(&UpgradeCharmCommand{})) // Charm publishing commands. jujucmd.Register(wrap(&PublishCommand{})) // Charm tool commands. jujucmd.Register(wrap(&HelpToolCommand{})) // Manage authorised ssh keys. jujucmd.Register(wrap(NewAuthorisedKeysCommand())) // Common commands. jujucmd.Register(wrap(&cmd.VersionCommand{})) os.Exit(cmd.Main(jujucmd, ctx, args[1:])) } // wrap encapsulates code that wraps some of the commands in a helper class // that handles some common errors func wrap(c cmd.Command) cmd.Command { if ec, ok := c.(envCmd); ok { return envCmdWrapper{ec} } return c } // envCmd is a Command that interacts with the juju client environment type envCmd interface { cmd.Command EnvironName() string } // envCmdWrapper is a struct that wraps an environment command and lets us handle // errors returned from Run before they're returned to the main function type envCmdWrapper struct { envCmd } // Run in envCmdWrapper gives us an opportunity to handle errors after the command is // run. This is used to give informative messages to the user. func (c envCmdWrapper) Run(ctx *cmd.Context) error { err := c.envCmd.Run(ctx) if environs.IsNoEnv(err) && c.EnvironName() == "" { fmt.Fprintln(ctx.Stderr, "No juju environment configuration file exists.") fmt.Fprintln(ctx.Stderr, err) fmt.Fprintln(ctx.Stderr, "Please create a configuration by running:") fmt.Fprintln(ctx.Stderr, " juju init") fmt.Fprintln(ctx.Stderr, "then edit the file to configure your juju environment.") fmt.Fprintln(ctx.Stderr, "You can then re-run the command.") return cmd.ErrSilent } return err } func main() { Main(os.Args) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/authorisedkeys_list.go������������������������0000644�0000153�0000161�00000003065�12321735776�027046� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/utils/ssh" ) var listKeysDoc = ` List a user's authorised ssh keys, allowing the holders of those keys to log on to Juju nodes. By default, just the key fingerprint is printed. Use --full to display the entire key. ` // ListKeysCommand is used to list the authorized ssh keys. type ListKeysCommand struct { cmd.EnvCommandBase showFullKey bool user string } func (c *ListKeysCommand) Info() *cmd.Info { return &cmd.Info{ Name: "list", Doc: listKeysDoc, Purpose: "list authorised ssh keys for a specified user", } } func (c *ListKeysCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.BoolVar(&c.showFullKey, "full", false, "show full key instead of just the key fingerprint") f.StringVar(&c.user, "user", "admin", "the user for which to list the keys") } func (c *ListKeysCommand) Run(context *cmd.Context) error { client, err := juju.NewKeyManagerClient(c.EnvName) if err != nil { return err } defer client.Close() mode := ssh.Fingerprints if c.showFullKey { mode = ssh.FullKeys } results, err := client.ListKeys(mode, c.user) if err != nil { return err } result := results[0] if result.Error != nil { return result.Error } fmt.Fprintf(context.Stdout, "Keys for user %s:\n", c.user) fmt.Fprintln(context.Stdout, strings.Join(result.Result, "\n")) return nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/resolved_test.go������������������������������0000644�0000153�0000161�00000004645�12321735642�025627� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" ) type ResolvedSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&ResolvedSuite{}) func runResolved(c *gc.C, args []string) error { _, err := testing.RunCommand(c, &ResolvedCommand{}, args) return err } var resolvedTests = []struct { args []string err string unit string mode state.ResolvedMode }{ { err: `no unit specified`, }, { args: []string{"jeremy-fisher"}, err: `invalid unit name "jeremy-fisher"`, }, { args: []string{"jeremy-fisher/99"}, err: `unit "jeremy-fisher/99" not found`, }, { args: []string{"dummy/0"}, err: `unit "dummy/0" is not in an error state`, unit: "dummy/0", mode: state.ResolvedNone, }, { args: []string{"dummy/1", "--retry"}, err: `unit "dummy/1" is not in an error state`, unit: "dummy/1", mode: state.ResolvedNone, }, { args: []string{"dummy/2"}, unit: "dummy/2", mode: state.ResolvedNoHooks, }, { args: []string{"dummy/2", "--retry"}, err: `cannot set resolved mode for unit "dummy/2": already resolved`, unit: "dummy/2", mode: state.ResolvedNoHooks, }, { args: []string{"dummy/3", "--retry"}, unit: "dummy/3", mode: state.ResolvedRetryHooks, }, { args: []string{"dummy/3"}, err: `cannot set resolved mode for unit "dummy/3": already resolved`, unit: "dummy/3", mode: state.ResolvedRetryHooks, }, { args: []string{"dummy/4", "roflcopter"}, err: `unrecognized args: \["roflcopter"\]`, }, } func (s *ResolvedSuite) TestResolved(c *gc.C) { testing.Charms.BundlePath(s.SeriesPath, "dummy") err := runDeploy(c, "-n", "5", "local:dummy", "dummy") c.Assert(err, gc.IsNil) for _, name := range []string{"dummy/2", "dummy/3", "dummy/4"} { u, err := s.State.Unit(name) c.Assert(err, gc.IsNil) err = u.SetStatus(params.StatusError, "lol borken", nil) c.Assert(err, gc.IsNil) } for i, t := range resolvedTests { c.Logf("test %d: %v", i, t.args) err := runResolved(c, t.args) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) } else { c.Assert(err, gc.IsNil) } if t.unit != "" { unit, err := s.State.Unit(t.unit) c.Assert(err, gc.IsNil) c.Assert(unit.Resolved(), gc.Equals, t.mode) } } } �������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/debughooks_test.go����������������������������0000644�0000153�0000161�00000016215�12321735642�026132� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "regexp" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" coretesting "launchpad.net/juju-core/testing" ) var _ = gc.Suite(&DebugHooksSuite{}) type DebugHooksSuite struct { SSHCommonSuite } const debugHooksArgs = sshArgs var debugHooksTests = []struct { info string args []string code int result string stderr string }{{ args: []string{"mysql/0"}, result: regexp.QuoteMeta(debugHooksArgs + "ubuntu@dummyenv-0.dns sudo /bin/bash -c 'F=$(mktemp); echo IyEvYmluL2Jhc2gKKAojIExvY2sgdGhlIGp1anUtPHVuaXQ+LWRlYnVnIGxvY2tmaWxlLgpmbG9jayAtbiA4IHx8IChlY2hvICJGYWlsZWQgdG8gYWNxdWlyZSAvdG1wL2p1anUtdW5pdC1teXNxbC0wLWRlYnVnLWhvb2tzOiB1bml0IGlzIGFscmVhZHkgYmVpbmcgZGVidWdnZWQiIDI+JjE7IGV4aXQgMSkKKAojIENsb3NlIHRoZSBpbmhlcml0ZWQgbG9jayBGRCwgb3IgdG11eCB3aWxsIGtlZXAgaXQgb3Blbi4KZXhlYyA4PiYtCgojIFdyaXRlIG91dCB0aGUgZGVidWctaG9va3MgYXJncy4KZWNobyAiZTMwSyIgfCBiYXNlNjQgLWQgPiAvdG1wL2p1anUtdW5pdC1teXNxbC0wLWRlYnVnLWhvb2tzCgojIExvY2sgdGhlIGp1anUtPHVuaXQ+LWRlYnVnLWV4aXQgbG9ja2ZpbGUuCmZsb2NrIC1uIDkgfHwgZXhpdCAxCgojIFdhaXQgZm9yIHRtdXggdG8gYmUgaW5zdGFsbGVkLgp3aGlsZSBbICEgLWYgL3Vzci9iaW4vdG11eCBdOyBkbwogICAgc2xlZXAgMQpkb25lCgppZiBbICEgLWYgfi8udG11eC5jb25mIF07IHRoZW4KICAgICAgICBpZiBbIC1mIC91c3Ivc2hhcmUvYnlvYnUvcHJvZmlsZXMvdG11eCBdOyB0aGVuCiAgICAgICAgICAgICAgICAjIFVzZSBieW9idS90bXV4IHByb2ZpbGUgZm9yIGZhbWlsaWFyIGtleWJpbmRpbmdzIGFuZCBicmFuZGluZwogICAgICAgICAgICAgICAgZWNobyAic291cmNlLWZpbGUgL3Vzci9zaGFyZS9ieW9idS9wcm9maWxlcy90bXV4IiA+IH4vLnRtdXguY29uZgogICAgICAgIGVsc2UKICAgICAgICAgICAgICAgICMgT3RoZXJ3aXNlLCB1c2UgdGhlIGxlZ2FjeSBqdWp1L3RtdXggY29uZmlndXJhdGlvbgogICAgICAgICAgICAgICAgY2F0ID4gfi8udG11eC5jb25mIDw8RU5ECiAgICAgICAgICAgICAgICAKIyBTdGF0dXMgYmFyCnNldC1vcHRpb24gLWcgc3RhdHVzLWJnIGJsYWNrCnNldC1vcHRpb24gLWcgc3RhdHVzLWZnIHdoaXRlCgpzZXQtd2luZG93LW9wdGlvbiAtZyB3aW5kb3ctc3RhdHVzLWN1cnJlbnQtYmcgcmVkCnNldC13aW5kb3ctb3B0aW9uIC1nIHdpbmRvdy1zdGF0dXMtY3VycmVudC1hdHRyIGJyaWdodAoKc2V0LW9wdGlvbiAtZyBzdGF0dXMtcmlnaHQgJycKCiMgUGFuZXMKc2V0LW9wdGlvbiAtZyBwYW5lLWJvcmRlci1mZyB3aGl0ZQpzZXQtb3B0aW9uIC1nIHBhbmUtYWN0aXZlLWJvcmRlci1mZyB3aGl0ZQoKIyBNb25pdG9yIGFjdGl2aXR5IG9uIHdpbmRvd3MKc2V0LXdpbmRvdy1vcHRpb24gLWcgbW9uaXRvci1hY3Rpdml0eSBvbgoKIyBTY3JlZW4gYmluZGluZ3MsIHNpbmNlIHBlb3BsZSBhcmUgbW9yZSBmYW1pbGlhciB3aXRoIHRoYXQuCnNldC1vcHRpb24gLWcgcHJlZml4IEMtYQpiaW5kIEMtYSBsYXN0LXdpbmRvdwpiaW5kIGEgc2VuZC1rZXkgQy1hCgpiaW5kIHwgc3BsaXQtd2luZG93IC1oCmJpbmQgLSBzcGxpdC13aW5kb3cgLXYKCiMgRml4IENUUkwtUEdVUC9QR0RPV04gZm9yIHZpbQpzZXQtd2luZG93LW9wdGlvbiAtZyB4dGVybS1rZXlzIG9uCgojIFByZXZlbnQgRVNDIGtleSBmcm9tIGFkZGluZyBkZWxheSBhbmQgYnJlYWtpbmcgVmltJ3MgRVNDID4gYXJyb3cga2V5CnNldC1vcHRpb24gLXMgZXNjYXBlLXRpbWUgMAoKRU5ECiAgICAgICAgZmkKZmkKCigKICAgICMgQ2xvc2UgdGhlIGluaGVyaXRlZCBsb2NrIEZELCBvciB0bXV4IHdpbGwga2VlcCBpdCBvcGVuLgogICAgZXhlYyA5PiYtCiAgICBleGVjIHRtdXggbmV3LXNlc3Npb24gLXMgbXlzcWwvMAopCikgOT4vdG1wL2p1anUtdW5pdC1teXNxbC0wLWRlYnVnLWhvb2tzLWV4aXQKKSA4Pi90bXAvanVqdS11bml0LW15c3FsLTAtZGVidWctaG9va3MKZXhpdCAkPwo= | base64 -d > $F; . $F'\n"), }, { args: []string{"mongodb/1"}, result: regexp.QuoteMeta(debugHooksArgs + "ubuntu@dummyenv-2.dns sudo /bin/bash -c 'F=$(mktemp); echo IyEvYmluL2Jhc2gKKAojIExvY2sgdGhlIGp1anUtPHVuaXQ+LWRlYnVnIGxvY2tmaWxlLgpmbG9jayAtbiA4IHx8IChlY2hvICJGYWlsZWQgdG8gYWNxdWlyZSAvdG1wL2p1anUtdW5pdC1tb25nb2RiLTEtZGVidWctaG9va3M6IHVuaXQgaXMgYWxyZWFkeSBiZWluZyBkZWJ1Z2dlZCIgMj4mMTsgZXhpdCAxKQooCiMgQ2xvc2UgdGhlIGluaGVyaXRlZCBsb2NrIEZELCBvciB0bXV4IHdpbGwga2VlcCBpdCBvcGVuLgpleGVjIDg+Ji0KCiMgV3JpdGUgb3V0IHRoZSBkZWJ1Zy1ob29rcyBhcmdzLgplY2hvICJlMzBLIiB8IGJhc2U2NCAtZCA+IC90bXAvanVqdS11bml0LW1vbmdvZGItMS1kZWJ1Zy1ob29rcwoKIyBMb2NrIHRoZSBqdWp1LTx1bml0Pi1kZWJ1Zy1leGl0IGxvY2tmaWxlLgpmbG9jayAtbiA5IHx8IGV4aXQgMQoKIyBXYWl0IGZvciB0bXV4IHRvIGJlIGluc3RhbGxlZC4Kd2hpbGUgWyAhIC1mIC91c3IvYmluL3RtdXggXTsgZG8KICAgIHNsZWVwIDEKZG9uZQoKaWYgWyAhIC1mIH4vLnRtdXguY29uZiBdOyB0aGVuCiAgICAgICAgaWYgWyAtZiAvdXNyL3NoYXJlL2J5b2J1L3Byb2ZpbGVzL3RtdXggXTsgdGhlbgogICAgICAgICAgICAgICAgIyBVc2UgYnlvYnUvdG11eCBwcm9maWxlIGZvciBmYW1pbGlhciBrZXliaW5kaW5ncyBhbmQgYnJhbmRpbmcKICAgICAgICAgICAgICAgIGVjaG8gInNvdXJjZS1maWxlIC91c3Ivc2hhcmUvYnlvYnUvcHJvZmlsZXMvdG11eCIgPiB+Ly50bXV4LmNvbmYKICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICAjIE90aGVyd2lzZSwgdXNlIHRoZSBsZWdhY3kganVqdS90bXV4IGNvbmZpZ3VyYXRpb24KICAgICAgICAgICAgICAgIGNhdCA+IH4vLnRtdXguY29uZiA8PEVORAogICAgICAgICAgICAgICAgCiMgU3RhdHVzIGJhcgpzZXQtb3B0aW9uIC1nIHN0YXR1cy1iZyBibGFjawpzZXQtb3B0aW9uIC1nIHN0YXR1cy1mZyB3aGl0ZQoKc2V0LXdpbmRvdy1vcHRpb24gLWcgd2luZG93LXN0YXR1cy1jdXJyZW50LWJnIHJlZApzZXQtd2luZG93LW9wdGlvbiAtZyB3aW5kb3ctc3RhdHVzLWN1cnJlbnQtYXR0ciBicmlnaHQKCnNldC1vcHRpb24gLWcgc3RhdHVzLXJpZ2h0ICcnCgojIFBhbmVzCnNldC1vcHRpb24gLWcgcGFuZS1ib3JkZXItZmcgd2hpdGUKc2V0LW9wdGlvbiAtZyBwYW5lLWFjdGl2ZS1ib3JkZXItZmcgd2hpdGUKCiMgTW9uaXRvciBhY3Rpdml0eSBvbiB3aW5kb3dzCnNldC13aW5kb3ctb3B0aW9uIC1nIG1vbml0b3ItYWN0aXZpdHkgb24KCiMgU2NyZWVuIGJpbmRpbmdzLCBzaW5jZSBwZW9wbGUgYXJlIG1vcmUgZmFtaWxpYXIgd2l0aCB0aGF0LgpzZXQtb3B0aW9uIC1nIHByZWZpeCBDLWEKYmluZCBDLWEgbGFzdC13aW5kb3cKYmluZCBhIHNlbmQta2V5IEMtYQoKYmluZCB8IHNwbGl0LXdpbmRvdyAtaApiaW5kIC0gc3BsaXQtd2luZG93IC12CgojIEZpeCBDVFJMLVBHVVAvUEdET1dOIGZvciB2aW0Kc2V0LXdpbmRvdy1vcHRpb24gLWcgeHRlcm0ta2V5cyBvbgoKIyBQcmV2ZW50IEVTQyBrZXkgZnJvbSBhZGRpbmcgZGVsYXkgYW5kIGJyZWFraW5nIFZpbSdzIEVTQyA+IGFycm93IGtleQpzZXQtb3B0aW9uIC1zIGVzY2FwZS10aW1lIDAKCkVORAogICAgICAgIGZpCmZpCgooCiAgICAjIENsb3NlIHRoZSBpbmhlcml0ZWQgbG9jayBGRCwgb3IgdG11eCB3aWxsIGtlZXAgaXQgb3Blbi4KICAgIGV4ZWMgOT4mLQogICAgZXhlYyB0bXV4IG5ldy1zZXNzaW9uIC1zIG1vbmdvZGIvMQopCikgOT4vdG1wL2p1anUtdW5pdC1tb25nb2RiLTEtZGVidWctaG9va3MtZXhpdAopIDg+L3RtcC9qdWp1LXVuaXQtbW9uZ29kYi0xLWRlYnVnLWhvb2tzCmV4aXQgJD8K | base64 -d > $F; . $F'\n"), }, { info: `"*" is a valid hook name: it means hook everything`, args: []string{"mysql/0", "*"}, result: ".*\n", }, { info: `"*" mixed with named hooks is equivalent to "*"`, args: []string{"mysql/0", "*", "relation-get"}, result: ".*\n", }, { info: `multiple named hooks may be specified`, args: []string{"mysql/0", "start", "stop"}, result: ".*\n", }, { info: `relation hooks have the relation name prefixed`, args: []string{"mysql/0", "juju-info-relation-joined"}, result: ".*\n", }, { info: `invalid unit syntax`, args: []string{"mysql"}, code: 2, stderr: `error: "mysql" is not a valid unit name` + "\n", }, { info: `invalid unit`, args: []string{"nonexistent/123"}, code: 1, stderr: `error: unit "nonexistent/123" not found` + "\n", }, { info: `invalid hook`, args: []string{"mysql/0", "invalid-hook"}, code: 1, stderr: `error: unit "mysql/0" does not contain hook "invalid-hook"` + "\n", }} func (s *DebugHooksSuite) TestDebugHooksCommand(c *gc.C) { machines := s.makeMachines(3, c, true) dummy := s.AddTestingCharm(c, "dummy") srv := s.AddTestingService(c, "mysql", dummy) s.addUnit(srv, machines[0], c) srv = s.AddTestingService(c, "mongodb", dummy) s.addUnit(srv, machines[1], c) s.addUnit(srv, machines[2], c) for i, t := range debugHooksTests { c.Logf("test %d: %s\n\t%s\n", i, t.info, t.args) ctx := coretesting.Context(c) code := cmd.Main(&DebugHooksCommand{}, ctx, t.args) c.Check(code, gc.Equals, t.code) c.Check(ctx.Stderr.(*bytes.Buffer).String(), gc.Matches, t.stderr) c.Check(ctx.Stdout.(*bytes.Buffer).String(), gc.Matches, t.result) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/synctools_test.go�����������������������������0000644�0000153�0000161�00000012755�12321735642�026042� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "time" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/sync" "launchpad.net/juju-core/provider/dummy" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type syncToolsSuite struct { testbase.LoggingSuite home *coretesting.FakeHome configStore configstore.Storage localStorage string origSyncTools func(*sync.SyncContext) error } var _ = gc.Suite(&syncToolsSuite{}) func (s *syncToolsSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) // Create a target environments.yaml and make sure its environment is empty. s.home = coretesting.MakeFakeHome(c, ` environments: test-target: type: dummy state-server: false authorized-keys: "not-really-one" `) var err error s.configStore, err = configstore.Default() c.Assert(err, gc.IsNil) s.origSyncTools = syncTools } func (s *syncToolsSuite) TearDownTest(c *gc.C) { syncTools = s.origSyncTools dummy.Reset() s.home.Restore() s.LoggingSuite.TearDownTest(c) } func (s *syncToolsSuite) Reset(c *gc.C) { s.TearDownTest(c) s.SetUpTest(c) } func runSyncToolsCommand(c *gc.C, args ...string) (*cmd.Context, error) { return coretesting.RunCommand(c, &SyncToolsCommand{}, args) } func wait(signal chan struct{}) error { select { case <-signal: return nil case <-time.After(25 * time.Millisecond): return errors.New("timeout") } } var syncToolsCommandTests = []struct { description string args []string sctx *sync.SyncContext }{ { description: "environment as only argument", args: []string{"-e", "test-target"}, sctx: &sync.SyncContext{}, }, { description: "specifying also the synchronization source", args: []string{"-e", "test-target", "--source", "/foo/bar"}, sctx: &sync.SyncContext{ Source: "/foo/bar", }, }, { description: "synchronize all version including development", args: []string{"-e", "test-target", "--all", "--dev"}, sctx: &sync.SyncContext{ AllVersions: true, Dev: true, }, }, { description: "just make a dry run", args: []string{"-e", "test-target", "--dry-run"}, sctx: &sync.SyncContext{ DryRun: true, }, }, { description: "specific public", args: []string{"-e", "test-target", "--public"}, sctx: &sync.SyncContext{ Public: true, }, }, { description: "specify version", args: []string{"-e", "test-target", "--version", "1.2"}, sctx: &sync.SyncContext{ MajorVersion: 1, MinorVersion: 2, }, }, } func (s *syncToolsSuite) TestSyncToolsCommand(c *gc.C) { for i, test := range syncToolsCommandTests { c.Logf("test %d: %s", i, test.description) targetEnv, err := environs.PrepareFromName("test-target", nullContext(c), s.configStore) c.Assert(err, gc.IsNil) called := false syncTools = func(sctx *sync.SyncContext) error { c.Assert(sctx.AllVersions, gc.Equals, test.sctx.AllVersions) c.Assert(sctx.MajorVersion, gc.Equals, test.sctx.MajorVersion) c.Assert(sctx.MinorVersion, gc.Equals, test.sctx.MinorVersion) c.Assert(sctx.DryRun, gc.Equals, test.sctx.DryRun) c.Assert(sctx.Dev, gc.Equals, test.sctx.Dev) c.Assert(sctx.Public, gc.Equals, test.sctx.Public) c.Assert(sctx.Source, gc.Equals, test.sctx.Source) c.Assert(dummy.IsSameStorage(sctx.Target, targetEnv.Storage()), jc.IsTrue) called = true return nil } ctx, err := runSyncToolsCommand(c, test.args...) c.Assert(err, gc.IsNil) c.Assert(ctx, gc.NotNil) c.Assert(called, jc.IsTrue) s.Reset(c) } } func (s *syncToolsSuite) TestSyncToolsCommandTargetDirectory(c *gc.C) { called := false dir := c.MkDir() syncTools = func(sctx *sync.SyncContext) error { c.Assert(sctx.AllVersions, gc.Equals, false) c.Assert(sctx.DryRun, gc.Equals, false) c.Assert(sctx.Dev, gc.Equals, false) c.Assert(sctx.Source, gc.Equals, "") url, err := sctx.Target.URL("") c.Assert(err, gc.IsNil) c.Assert(url, gc.Equals, "file://"+dir) called = true return nil } ctx, err := runSyncToolsCommand(c, "-e", "test-target", "--local-dir", dir) c.Assert(err, gc.IsNil) c.Assert(ctx, gc.NotNil) c.Assert(called, jc.IsTrue) s.Reset(c) } func (s *syncToolsSuite) TestSyncToolsCommandDeprecatedDestination(c *gc.C) { called := false dir := c.MkDir() syncTools = func(sctx *sync.SyncContext) error { c.Assert(sctx.AllVersions, gc.Equals, false) c.Assert(sctx.DryRun, gc.Equals, false) c.Assert(sctx.Dev, gc.Equals, false) c.Assert(sctx.Source, gc.Equals, "") url, err := sctx.Target.URL("") c.Assert(err, gc.IsNil) c.Assert(url, gc.Equals, "file://"+dir) called = true return nil } // Register writer. tw := &loggo.TestWriter{} c.Assert(loggo.RegisterWriter("deprecated-tester", tw, loggo.DEBUG), gc.IsNil) defer loggo.RemoveWriter("deprecated-tester") // Add deprecated message to be checked. messages := []jc.SimpleMessage{ {loggo.WARNING, "Use of the --destination flag is deprecated in 1.18. Please use --local-dir instead."}, } // Run sync-tools command with --destination flag. ctx, err := runSyncToolsCommand(c, "-e", "test-target", "--destination", dir) c.Assert(err, gc.IsNil) c.Assert(ctx, gc.NotNil) c.Assert(called, jc.IsTrue) // Check deprecated message was logged. c.Check(tw.Log, jc.LogMatches, messages) s.Reset(c) } �������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/common.go�������������������������������������0000644�0000153�0000161�00000006042�12321735776�024236� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state/api" ) // destroyPreparedEnviron destroys the environment and logs an error if it fails. func destroyPreparedEnviron(ctx *cmd.Context, env environs.Environ, store configstore.Storage, err *error, action string) { if *err == nil { return } ctx.Infof("%s failed, destroying environment", action) if err := environs.Destroy(env, store); err != nil { logger.Errorf("%s failed, and the environment could not be destroyed: %v", action, err) } } // environFromName loads an existing environment or prepares a new one. func environFromName( ctx *cmd.Context, envName string, resultErr *error, action string) (environs.Environ, func(), error) { store, err := configstore.Default() if err != nil { return nil, nil, err } var existing bool if environInfo, err := store.ReadInfo(envName); !errors.IsNotFoundError(err) { existing = true logger.Warningf("ignoring environments.yaml: using bootstrap config in %s", environInfo.Location()) } environ, err := environs.PrepareFromName(envName, ctx, store) if err != nil { return nil, nil, err } cleanup := func() { if !existing { destroyPreparedEnviron(ctx, environ, store, resultErr, action) } } return environ, cleanup, nil } // resolveCharmURL returns a resolved charm URL, given a charm location string. // If the series is not resolved, the environment default-series is used, or if // not set, the series is resolved with the state server. func resolveCharmURL(url string, client *api.Client, conf *config.Config) (*charm.URL, error) { ref, series, err := charm.ParseReference(url) if err != nil { return nil, err } // If series is not set, use configured default series if series == "" { if defaultSeries, ok := conf.DefaultSeries(); ok { series = defaultSeries } } // 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 } // resolveCharmURL1dot16 returns a resolved charm URL for older state servers // that do not support ResolveCharm. The default series "precise" is // appropriate for these environments. func resolveCharmURL1dot16(url string, conf *config.Config) (*charm.URL, error) { ref, series, err := charm.ParseReference(url) if err != nil { return nil, err } if series == "" { series = config.PreferredSeries(conf) } return &charm.URL{Reference: ref, Series: series}, err } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/destroyservice.go�����������������������������0000644�0000153�0000161�00000002205�12321735776�026015� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" ) // DestroyServiceCommand causes an existing service to be destroyed. type DestroyServiceCommand struct { cmd.EnvCommandBase ServiceName string } func (c *DestroyServiceCommand) Info() *cmd.Info { return &cmd.Info{ Name: "destroy-service", Args: "<service>", Purpose: "destroy a service", Doc: "Destroying a service will destroy all its units and relations.", Aliases: []string{"remove-service"}, } } func (c *DestroyServiceCommand) Init(args []string) error { if len(args) == 0 { return fmt.Errorf("no service specified") } if !names.IsService(args[0]) { return fmt.Errorf("invalid service name %q", args[0]) } c.ServiceName, args = args[0], args[1:] return cmd.CheckEmpty(args) } func (c *DestroyServiceCommand) Run(_ *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() return client.ServiceDestroy(c.ServiceName) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/ssh_test.go�����������������������������������0000644�0000153�0000161�00000012141�12321735776�024577� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "fmt" "net/url" "os" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" ) var _ = gc.Suite(&SSHSuite{}) type SSHSuite struct { SSHCommonSuite } type SSHCommonSuite struct { testing.JujuConnSuite bin string } // fakecommand outputs its arguments to stdout for verification var fakecommand = `#!/bin/bash echo $@ | tee $0.args ` func (s *SSHCommonSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.bin = c.MkDir() s.PatchEnvPathPrepend(s.bin) for _, name := range []string{"ssh", "scp"} { f, err := os.OpenFile(filepath.Join(s.bin, name), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777) c.Assert(err, gc.IsNil) _, err = f.Write([]byte(fakecommand)) c.Assert(err, gc.IsNil) err = f.Close() c.Assert(err, gc.IsNil) } } const ( commonArgs = `-o StrictHostKeyChecking no -o PasswordAuthentication no ` sshArgs = commonArgs + `-t -t ` ) var sshTests = []struct { about string args []string result string }{ { "connect to machine 0", []string{"ssh", "0"}, sshArgs + "ubuntu@dummyenv-0.dns\n", }, { "connect to machine 0 and pass extra arguments", []string{"ssh", "0", "uname", "-a"}, sshArgs + "ubuntu@dummyenv-0.dns uname -a\n", }, { "connect to unit mysql/0", []string{"ssh", "mysql/0"}, sshArgs + "ubuntu@dummyenv-0.dns\n", }, { "connect to unit mongodb/1 and pass extra arguments", []string{"ssh", "mongodb/1", "ls", "/"}, sshArgs + "ubuntu@dummyenv-2.dns ls /\n", }, } func (s *SSHSuite) TestSSHCommand(c *gc.C) { m := s.makeMachines(3, c, true) ch := coretesting.Charms.Dir("dummy") curl := charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", ch.Meta().Name, ch.Revision()), ) bundleURL, err := url.Parse("http://bundles.testing.invalid/dummy-1") c.Assert(err, gc.IsNil) dummy, err := s.State.AddCharm(ch, curl, bundleURL, "dummy-1-sha256") c.Assert(err, gc.IsNil) srv := s.AddTestingService(c, "mysql", dummy) s.addUnit(srv, m[0], c) srv = s.AddTestingService(c, "mongodb", dummy) s.addUnit(srv, m[1], c) s.addUnit(srv, m[2], c) for i, t := range sshTests { c.Logf("test %d: %s -> %s\n", i, t.about, t.args) ctx := coretesting.Context(c) jujucmd := cmd.NewSuperCommand(cmd.SuperCommandParams{}) jujucmd.Register(&SSHCommand{}) code := cmd.Main(jujucmd, ctx, t.args) c.Check(code, gc.Equals, 0) c.Check(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "") c.Check(ctx.Stdout.(*bytes.Buffer).String(), gc.Equals, t.result) } } type callbackAttemptStarter struct { next func() bool } func (s *callbackAttemptStarter) Start() attempt { return callbackAttempt{next: s.next} } type callbackAttempt struct { next func() bool } func (a callbackAttempt) Next() bool { return a.next() } func (s *SSHSuite) TestSSHCommandHostAddressRetry(c *gc.C) { m := s.makeMachines(1, c, false) ctx := coretesting.Context(c) jujucmd := cmd.NewSuperCommand(cmd.SuperCommandParams{}) jujucmd.Register(&SSHCommand{}) var called int next := func() bool { called++ return called < 2 } attemptStarter := &callbackAttemptStarter{next: next} s.PatchValue(&sshHostFromTargetAttemptStrategy, attemptStarter) // Ensure that the ssh command waits for a public address, or the attempt // strategy's Done method returns false. code := cmd.Main(jujucmd, ctx, []string{"ssh", "0"}) c.Check(code, gc.Equals, 1) c.Assert(called, gc.Equals, 2) called = 0 attemptStarter.next = func() bool { called++ s.setAddress(m[0], c) return false } code = cmd.Main(jujucmd, ctx, []string{"ssh", "0"}) c.Check(code, gc.Equals, 0) c.Assert(called, gc.Equals, 1) } func (s *SSHCommonSuite) setAddress(m *state.Machine, c *gc.C) { addr := instance.NewAddress(fmt.Sprintf("dummyenv-%s.dns", m.Id())) addr.NetworkScope = instance.NetworkPublic err := m.SetAddresses([]instance.Address{addr}) c.Assert(err, gc.IsNil) } func (s *SSHCommonSuite) makeMachines(n int, c *gc.C, setAddress bool) []*state.Machine { var machines = make([]*state.Machine, n) for i := 0; i < n; i++ { m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) if setAddress { s.setAddress(m, c) } // must set an instance id as the ssh command uses that as a signal the // machine has been provisioned inst, md := testing.AssertStartInstance(c, s.Conn.Environ, m.Id()) c.Assert(m.SetProvisioned(inst.Id(), "fake_nonce", md), gc.IsNil) machines[i] = m } return machines } func (s *SSHCommonSuite) addUnit(srv *state.Service, m *state.Machine, c *gc.C) { u, err := srv.AddUnit() c.Assert(err, gc.IsNil) err = u.AssignToMachine(m) c.Assert(err, gc.IsNil) // fudge unit.SetPublicAddress id, err := m.InstanceId() c.Assert(err, gc.IsNil) insts, err := s.Conn.Environ.Instances([]instance.Id{id}) c.Assert(err, gc.IsNil) addr, err := insts[0].WaitDNSName() c.Assert(err, gc.IsNil) err = u.SetPublicAddress(addr) c.Assert(err, gc.IsNil) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/status.go�������������������������������������0000644�0000153�0000161�00000023152�12321735776�024272� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "encoding/json" "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/statecmd" ) type StatusCommand struct { cmd.EnvCommandBase out cmd.Output patterns []string } var statusDoc = ` This command will report on the runtime state of various system entities. Service or unit names may be specified to filter the status to only those services and units that match, along with the related machines, services and units. If a subordinate unit is matched, then its principal unit will be displayed. If a principal unit is matched, then all of its subordinates will be displayed. Wildcards ('*') may be specified in service/unit names to match any sequence of characters. For example, 'nova-*' will match any service whose name begins with 'nova-': 'nova-compute', 'nova-volume', etc. ` func (c *StatusCommand) Info() *cmd.Info { return &cmd.Info{ Name: "status", Args: "[pattern ...]", Purpose: "output status information about an environment", Doc: statusDoc, Aliases: []string{"stat"}, } } func (c *StatusCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) c.out.AddFlags(f, "yaml", map[string]cmd.Formatter{ "yaml": cmd.FormatYaml, "json": cmd.FormatJson, }) } func (c *StatusCommand) Init(args []string) error { c.patterns = args return nil } var connectionError = `Unable to connect to environment "%s". Please check your credentials or use 'juju bootstrap' to create a new environment. Error details: %v ` func (c *StatusCommand) getStatus1dot16() (*api.Status, error) { conn, err := juju.NewConnFromName(c.EnvName) if err != nil { return nil, fmt.Errorf(connectionError, c.EnvName, err) } defer conn.Close() return statecmd.Status(conn, c.patterns) } func (c *StatusCommand) Run(ctx *cmd.Context) error { // Just verify the pattern validity client side, do not use the matcher _, err := statecmd.NewUnitMatcher(c.patterns) if err != nil { return err } apiclient, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return fmt.Errorf(connectionError, c.EnvName, err) } defer apiclient.Close() status, err := apiclient.Status(c.patterns) if params.IsCodeNotImplemented(err) { logger.Infof("Status not supported by the API server, " + "falling back to 1.16 compatibility mode " + "(direct DB access)") status, err = c.getStatus1dot16() } // Display any error, but continue to print status if some was returned if err != nil { fmt.Fprintf(ctx.Stderr, "%v\n", err) } result := formatStatus(status) return c.out.Write(ctx, result) } type formattedStatus struct { Environment string `json:"environment"` Machines map[string]machineStatus `json:"machines"` Services map[string]serviceStatus `json:"services"` } type errorStatus struct { StatusError string `json:"status-error" yaml:"status-error"` } type machineStatus struct { Err error `json:"-" yaml:",omitempty"` AgentState params.Status `json:"agent-state,omitempty" yaml:"agent-state,omitempty"` AgentStateInfo string `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"` AgentVersion string `json:"agent-version,omitempty" yaml:"agent-version,omitempty"` DNSName string `json:"dns-name,omitempty" yaml:"dns-name,omitempty"` InstanceId instance.Id `json:"instance-id,omitempty" yaml:"instance-id,omitempty"` InstanceState string `json:"instance-state,omitempty" yaml:"instance-state,omitempty"` Life string `json:"life,omitempty" yaml:"life,omitempty"` Series string `json:"series,omitempty" yaml:"series,omitempty"` Id string `json:"-" yaml:"-"` Containers map[string]machineStatus `json:"containers,omitempty" yaml:"containers,omitempty"` Hardware string `json:"hardware,omitempty" yaml:"hardware,omitempty"` } // A goyaml bug means we can't declare these types // locally to the GetYAML methods. type machineStatusNoMarshal machineStatus func (s machineStatus) MarshalJSON() ([]byte, error) { if s.Err != nil { return json.Marshal(errorStatus{s.Err.Error()}) } return json.Marshal(machineStatusNoMarshal(s)) } func (s machineStatus) GetYAML() (tag string, value interface{}) { if s.Err != nil { return "", errorStatus{s.Err.Error()} } // TODO(rog) rename mNoMethods to noMethods (and also in // the other GetYAML methods) when people are using the non-buggy // goyaml version. type mNoMethods machineStatus return "", mNoMethods(s) } type serviceStatus struct { Err error `json:"-" yaml:",omitempty"` Charm string `json:"charm" yaml:"charm"` CanUpgradeTo string `json:"can-upgrade-to,omitempty" yaml:"can-upgrade-to,omitempty"` Exposed bool `json:"exposed" yaml:"exposed"` Life string `json:"life,omitempty" yaml:"life,omitempty"` Relations map[string][]string `json:"relations,omitempty" yaml:"relations,omitempty"` Networks map[string][]string `json:"networks,omitempty" yaml:"networks,omitempty"` SubordinateTo []string `json:"subordinate-to,omitempty" yaml:"subordinate-to,omitempty"` Units map[string]unitStatus `json:"units,omitempty" yaml:"units,omitempty"` } type serviceStatusNoMarshal serviceStatus func (s serviceStatus) MarshalJSON() ([]byte, error) { if s.Err != nil { return json.Marshal(errorStatus{s.Err.Error()}) } type sNoMethods serviceStatus return json.Marshal(sNoMethods(s)) } func (s serviceStatus) GetYAML() (tag string, value interface{}) { if s.Err != nil { return "", errorStatus{s.Err.Error()} } type sNoMethods serviceStatus return "", sNoMethods(s) } type unitStatus struct { Err error `json:"-" yaml:",omitempty"` Charm string `json:"upgrading-from,omitempty" yaml:"upgrading-from,omitempty"` AgentState params.Status `json:"agent-state,omitempty" yaml:"agent-state,omitempty"` AgentStateInfo string `json:"agent-state-info,omitempty" yaml:"agent-state-info,omitempty"` AgentVersion string `json:"agent-version,omitempty" yaml:"agent-version,omitempty"` Life string `json:"life,omitempty" yaml:"life,omitempty"` Machine string `json:"machine,omitempty" yaml:"machine,omitempty"` OpenedPorts []string `json:"open-ports,omitempty" yaml:"open-ports,omitempty"` PublicAddress string `json:"public-address,omitempty" yaml:"public-address,omitempty"` Subordinates map[string]unitStatus `json:"subordinates,omitempty" yaml:"subordinates,omitempty"` } type unitStatusNoMarshal unitStatus func (s unitStatus) MarshalJSON() ([]byte, error) { if s.Err != nil { return json.Marshal(errorStatus{s.Err.Error()}) } return json.Marshal(unitStatusNoMarshal(s)) } func (s unitStatus) GetYAML() (tag string, value interface{}) { if s.Err != nil { return "", errorStatus{s.Err.Error()} } type uNoMethods unitStatus return "", unitStatusNoMarshal(s) } func formatStatus(status *api.Status) formattedStatus { if status == nil { return formattedStatus{} } out := formattedStatus{ Environment: status.EnvironmentName, Machines: make(map[string]machineStatus), Services: make(map[string]serviceStatus), } for k, m := range status.Machines { out.Machines[k] = formatMachine(m) } for k, s := range status.Services { out.Services[k] = formatService(s) } return out } func formatMachine(machine api.MachineStatus) machineStatus { out := machineStatus{ Err: machine.Err, AgentState: machine.AgentState, AgentStateInfo: machine.AgentStateInfo, AgentVersion: machine.AgentVersion, DNSName: machine.DNSName, InstanceId: machine.InstanceId, InstanceState: machine.InstanceState, Life: machine.Life, Series: machine.Series, Id: machine.Id, Containers: make(map[string]machineStatus), Hardware: machine.Hardware, } for k, m := range machine.Containers { out.Containers[k] = formatMachine(m) } return out } func formatService(service api.ServiceStatus) serviceStatus { out := serviceStatus{ Err: service.Err, Charm: service.Charm, Exposed: service.Exposed, Life: service.Life, Relations: service.Relations, Networks: make(map[string][]string), CanUpgradeTo: service.CanUpgradeTo, SubordinateTo: service.SubordinateTo, Units: make(map[string]unitStatus), } if len(service.Networks.Enabled) > 0 { out.Networks["enabled"] = service.Networks.Enabled } if len(service.Networks.Disabled) > 0 { out.Networks["disabled"] = service.Networks.Disabled } for k, m := range service.Units { out.Units[k] = formatUnit(m) } return out } func formatUnit(unit api.UnitStatus) unitStatus { out := unitStatus{ Err: unit.Err, AgentState: unit.AgentState, AgentStateInfo: unit.AgentStateInfo, AgentVersion: unit.AgentVersion, Life: unit.Life, Machine: unit.Machine, OpenedPorts: unit.OpenedPorts, PublicAddress: unit.PublicAddress, Charm: unit.Charm, Subordinates: make(map[string]unitStatus), } for k, m := range unit.Subordinates { out.Subordinates[k] = formatUnit(m) } return out } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/authorisedkeys.go�����������������������������0000644�0000153�0000161�00000001646�12321735642�026006� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) var authKeysDoc = ` "juju authorized-keys" is used to manage the ssh keys allowed to log on to nodes in the Juju environment. ` type AuthorisedKeysCommand struct { *cmd.SuperCommand } func NewAuthorisedKeysCommand() cmd.Command { sshkeyscmd := &AuthorisedKeysCommand{ SuperCommand: cmd.NewSuperCommand(cmd.SuperCommandParams{ Name: "authorised-keys", Doc: authKeysDoc, UsagePrefix: "juju", Purpose: "manage authorised ssh keys", }), } sshkeyscmd.Register(&AddKeysCommand{}) sshkeyscmd.Register(&DeleteKeysCommand{}) sshkeyscmd.Register(&ImportKeysCommand{}) sshkeyscmd.Register(&ListKeysCommand{}) return sshkeyscmd } func (c *AuthorisedKeysCommand) SetFlags(f *gnuflag.FlagSet) { c.SetCommonFlags(f) } ������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/cmd_test.go�����������������������������������0000644�0000153�0000161�00000024452�12321735776�024555� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "io" "io/ioutil" "os" "reflect" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/dummy" coretesting "launchpad.net/juju-core/testing" ) type CmdSuite struct { testing.JujuConnSuite home *coretesting.FakeHome } var _ = gc.Suite(&CmdSuite{}) const envConfig = ` default: peckham environments: peckham: type: dummy state-server: false admin-secret: arble authorized-keys: i-am-a-key default-series: raring walthamstow: type: dummy state-server: false authorized-keys: i-am-a-key brokenenv: type: dummy broken: Bootstrap Destroy state-server: false authorized-keys: i-am-a-key ` func (s *CmdSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.home = coretesting.MakeFakeHome(c, envConfig, "peckham", "walthamstow", "brokenenv") } func (s *CmdSuite) TearDownTest(c *gc.C) { s.home.Restore() s.JujuConnSuite.TearDownTest(c) } // testInit checks that a command initialises correctly // with the given set of arguments. func testInit(c *gc.C, com cmd.Command, args []string, errPat string) { err := coretesting.InitCommand(com, args) if errPat != "" { c.Assert(err, gc.ErrorMatches, errPat) } else { c.Assert(err, gc.IsNil) } } // assertConnName asserts that the Command is using // the given environment name. // Since every command has a different type, // we use reflection to look at the value of the // Conn field in the value. func assertConnName(c *gc.C, com cmd.Command, name string) { v := reflect.ValueOf(com).Elem().FieldByName("EnvName") c.Assert(v, jc.Satisfies, reflect.Value.IsValid) c.Assert(v.Interface(), gc.Equals, name) } // All members of EnvironmentInitTests are tested for the -environment and -e // flags, and that extra arguments will cause parsing to fail. var EnvironmentInitTests = []func() (cmd.Command, []string){ func() (cmd.Command, []string) { return new(BootstrapCommand), nil }, func() (cmd.Command, []string) { return new(DeployCommand), []string{"charm-name", "service-name"} }, func() (cmd.Command, []string) { return new(StatusCommand), nil }, } // TestEnvironmentInit tests that all commands which accept // the --environment variable initialise their // environment name correctly. func (*CmdSuite) TestEnvironmentInit(c *gc.C) { for i, cmdFunc := range EnvironmentInitTests { c.Logf("test %d", i) com, args := cmdFunc() testInit(c, com, args, "") assertConnName(c, com, "") com, args = cmdFunc() testInit(c, com, append(args, "-e", "walthamstow"), "") assertConnName(c, com, "walthamstow") com, args = cmdFunc() testInit(c, com, append(args, "--environment", "walthamstow"), "") assertConnName(c, com, "walthamstow") // JUJU_ENV is the final place the environment can be overriden com, args = cmdFunc() oldenv := os.Getenv(osenv.JujuEnvEnvKey) os.Setenv(osenv.JujuEnvEnvKey, "walthamstow") testInit(c, com, args, "") os.Setenv(osenv.JujuEnvEnvKey, oldenv) assertConnName(c, com, "walthamstow") com, args = cmdFunc() if _, ok := com.(*StatusCommand); !ok { testInit(c, com, append(args, "hotdog"), "unrecognized args.*") } } } func nullContext(c *gc.C) *cmd.Context { ctx, err := cmd.DefaultContext() c.Assert(err, gc.IsNil) ctx.Stdin = io.LimitReader(nil, 0) ctx.Stdout = ioutil.Discard ctx.Stderr = ioutil.Discard return ctx } func runCommand(ctx *cmd.Context, com cmd.Command, args ...string) (opc chan dummy.Operation, errc chan error) { if ctx == nil { panic("ctx == nil") } errc = make(chan error, 1) opc = make(chan dummy.Operation, 200) dummy.Listen(opc) go func() { // signal that we're done with this ops channel. defer dummy.Listen(nil) err := coretesting.InitCommand(com, args) if err != nil { errc <- err return } err = com.Run(ctx) errc <- err }() return } var deployTests = []struct { args []string com *DeployCommand }{ { []string{"charm-name"}, &DeployCommand{}, }, { []string{"charm-name", "service-name"}, &DeployCommand{ServiceName: "service-name"}, }, { []string{"--repository", "/path/to/another-repo", "charm-name"}, &DeployCommand{RepoPath: "/path/to/another-repo"}, }, { []string{"--upgrade", "charm-name"}, &DeployCommand{BumpRevision: true}, }, { []string{"-u", "charm-name"}, &DeployCommand{BumpRevision: true}, }, { []string{"--num-units", "33", "charm-name"}, &DeployCommand{UnitCommandBase: UnitCommandBase{NumUnits: 33}}, }, { []string{"-n", "104", "charm-name"}, &DeployCommand{UnitCommandBase: UnitCommandBase{NumUnits: 104}}, }, } func initExpectations(com *DeployCommand) { if com.CharmName == "" { com.CharmName = "charm-name" } if com.NumUnits == 0 { com.NumUnits = 1 } if com.RepoPath == "" { com.RepoPath = "/path/to/repo" } } func initDeployCommand(args ...string) (*DeployCommand, error) { com := &DeployCommand{} return com, coretesting.InitCommand(com, args) } func (*CmdSuite) TestDeployCommandInit(c *gc.C) { defer os.Setenv(osenv.JujuRepositoryEnvKey, os.Getenv(osenv.JujuRepositoryEnvKey)) os.Setenv(osenv.JujuRepositoryEnvKey, "/path/to/repo") for _, t := range deployTests { initExpectations(t.com) com, err := initDeployCommand(t.args...) c.Assert(err, gc.IsNil) c.Assert(com, gc.DeepEquals, t.com) } // test relative --config path ctx := coretesting.Context(c) expected := []byte("test: data") path := ctx.AbsPath("testconfig.yaml") file, err := os.Create(path) c.Assert(err, gc.IsNil) _, err = file.Write(expected) c.Assert(err, gc.IsNil) file.Close() com, err := initDeployCommand("--config", "testconfig.yaml", "charm-name") c.Assert(err, gc.IsNil) actual, err := com.Config.Read(ctx) c.Assert(err, gc.IsNil) c.Assert(expected, gc.DeepEquals, actual) // missing args _, err = initDeployCommand() c.Assert(err, gc.ErrorMatches, "no charm specified") // environment tested elsewhere } func initAddUnitCommand(args ...string) (*AddUnitCommand, error) { com := &AddUnitCommand{} return com, coretesting.InitCommand(com, args) } func (*CmdSuite) TestAddUnitCommandInit(c *gc.C) { // missing args _, err := initAddUnitCommand() c.Assert(err, gc.ErrorMatches, "no service specified") // bad unit count _, err = initDeployCommand("charm-name", "--num-units", "0") c.Assert(err, gc.ErrorMatches, "--num-units must be a positive integer") _, err = initDeployCommand("charm-name", "-n", "0") c.Assert(err, gc.ErrorMatches, "--num-units must be a positive integer") // environment tested elsewhere } func initExposeCommand(args ...string) (*ExposeCommand, error) { com := &ExposeCommand{} return com, coretesting.InitCommand(com, args) } func (*CmdSuite) TestExposeCommandInit(c *gc.C) { // missing args _, err := initExposeCommand() c.Assert(err, gc.ErrorMatches, "no service name specified") // environment tested elsewhere } func initUnexposeCommand(args ...string) (*UnexposeCommand, error) { com := &UnexposeCommand{} return com, coretesting.InitCommand(com, args) } func (*CmdSuite) TestUnexposeCommandInit(c *gc.C) { // missing args _, err := initUnexposeCommand() c.Assert(err, gc.ErrorMatches, "no service name specified") // environment tested elsewhere } func initSSHCommand(args ...string) (*SSHCommand, error) { com := &SSHCommand{} return com, coretesting.InitCommand(com, args) } func (*CmdSuite) TestSSHCommandInit(c *gc.C) { // missing args _, err := initSSHCommand() c.Assert(err, gc.ErrorMatches, "no target name specified") } func initSCPCommand(args ...string) (*SCPCommand, error) { com := &SCPCommand{} return com, coretesting.InitCommand(com, args) } func (*CmdSuite) TestSCPCommandInit(c *gc.C) { // missing args _, err := initSCPCommand() c.Assert(err, gc.ErrorMatches, "at least two arguments required") // not enough args _, err = initSCPCommand("mysql/0:foo") c.Assert(err, gc.ErrorMatches, "at least two arguments required") } func initGetCommand(args ...string) (*GetCommand, error) { com := &GetCommand{} return com, coretesting.InitCommand(com, args) } func (*CmdSuite) TestGetCommandInit(c *gc.C) { // missing args _, err := initGetCommand() c.Assert(err, gc.ErrorMatches, "no service name specified") } func initSetCommand(args ...string) (*SetCommand, error) { com := &SetCommand{} return com, coretesting.InitCommand(com, args) } func (*CmdSuite) TestSetCommandInit(c *gc.C) { // missing args _, err := initSetCommand() c.Assert(err, gc.ErrorMatches, "no service name specified") // missing service name _, err = initSetCommand("name=cow") c.Assert(err, gc.ErrorMatches, "no service name specified") // test --config path expected := []byte("this: is some test data") ctx := coretesting.Context(c) path := ctx.AbsPath("testconfig.yaml") file, err := os.Create(path) c.Assert(err, gc.IsNil) _, err = file.Write(expected) c.Assert(err, gc.IsNil) file.Close() com, err := initSetCommand("--config", "testconfig.yaml", "service") c.Assert(err, gc.IsNil) c.Assert(com.SettingsYAML.Path, gc.Equals, "testconfig.yaml") actual, err := com.SettingsYAML.Read(ctx) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, expected) // --config path, but no service com, err = initSetCommand("--config", "testconfig") c.Assert(err, gc.ErrorMatches, "no service name specified") // --config and options specified com, err = initSetCommand("service", "--config", "testconfig", "bees=") c.Assert(err, gc.ErrorMatches, "cannot specify --config when using key=value arguments") } func initUnsetCommand(args ...string) (*UnsetCommand, error) { com := &UnsetCommand{} return com, coretesting.InitCommand(com, args) } func (*CmdSuite) TestUnsetCommandInit(c *gc.C) { // missing args _, err := initUnsetCommand() c.Assert(err, gc.ErrorMatches, "no service name specified") } func initDestroyUnitCommand(args ...string) (*DestroyUnitCommand, error) { com := &DestroyUnitCommand{} return com, coretesting.InitCommand(com, args) } func (*CmdSuite) TestDestroyUnitCommandInit(c *gc.C) { // missing args _, err := initDestroyUnitCommand() c.Assert(err, gc.ErrorMatches, "no units specified") // not a unit _, err = initDestroyUnitCommand("seven/nine") c.Assert(err, gc.ErrorMatches, `invalid unit name "seven/nine"`) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/environment_test.go���������������������������0000644�0000153�0000161�00000015726�12321735642�026352� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/dummy" _ "launchpad.net/juju-core/provider/local" "launchpad.net/juju-core/testing" ) type GetEnvironmentSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&GetEnvironmentSuite{}) var singleValueTests = []struct { key string output string err string }{ { key: "type", output: "dummy", }, { key: "name", output: "dummyenv", }, { key: "authorized-keys", output: dummy.SampleConfig()["authorized-keys"].(string), }, { key: "unknown", err: `Key "unknown" not found in "dummyenv" environment.`, }, } func (s *GetEnvironmentSuite) TestSingleValue(c *gc.C) { for _, t := range singleValueTests { context, err := testing.RunCommand(c, &GetEnvironmentCommand{}, []string{t.key}) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) } else { output := strings.TrimSpace(testing.Stdout(context)) c.Assert(err, gc.IsNil) c.Assert(output, gc.Equals, t.output) } } } func (s *GetEnvironmentSuite) TestTooManyArgs(c *gc.C) { _, err := testing.RunCommand(c, &GetEnvironmentCommand{}, []string{"name", "type"}) c.Assert(err, gc.ErrorMatches, `unrecognized args: \["type"\]`) } func (s *GetEnvironmentSuite) TestAllValues(c *gc.C) { context, _ := testing.RunCommand(c, &GetEnvironmentCommand{}, []string{}) output := strings.TrimSpace(testing.Stdout(context)) // Make sure that all the environment keys are there. The admin // secret and CA private key are never pushed into the // environment. for key := range s.Conn.Environ.Config().AllAttrs() { c.Logf("test for key %q", key) any := `(.|\n)*` pattern := fmt.Sprintf(`(?m)^%s:`, key) c.Check(output, gc.Matches, any+pattern+any) } } type SetEnvironmentSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&SetEnvironmentSuite{}) var setEnvInitTests = []struct { args []string expected attributes err string }{ { args: []string{}, err: "No key, value pairs specified", }, { args: []string{"agent-version=1.2.3"}, err: `agent-version must be set via upgrade-juju`, }, { args: []string{"missing"}, err: `Missing "=" in arg 1: "missing"`, }, { args: []string{"key=value"}, expected: attributes{ "key": "value", }, }, { args: []string{"key=value", "key=other"}, err: `Key "key" specified more than once`, }, { args: []string{"key=value", "other=embedded=equal"}, expected: attributes{ "key": "value", "other": "embedded=equal", }, }, } func (s *SetEnvironmentSuite) TestInit(c *gc.C) { for _, t := range setEnvInitTests { command := &SetEnvironmentCommand{} testing.TestInit(c, command, t.args, t.err) if t.expected != nil { c.Assert(command.values, gc.DeepEquals, t.expected) } } } func (s *SetEnvironmentSuite) TestChangeDefaultSeries(c *gc.C) { // default-series not set stateConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) series, ok := stateConfig.DefaultSeries() c.Assert(ok, gc.Equals, true) c.Assert(series, gc.Equals, "precise") // default-series set in RepoSuite.SetUpTest _, err = testing.RunCommand(c, &SetEnvironmentCommand{}, []string{"default-series=raring"}) c.Assert(err, gc.IsNil) stateConfig, err = s.State.EnvironConfig() c.Assert(err, gc.IsNil) series, ok = stateConfig.DefaultSeries() c.Assert(ok, gc.Equals, true) c.Assert(series, gc.Equals, "raring") c.Assert(config.PreferredSeries(stateConfig), gc.Equals, "raring") } func (s *SetEnvironmentSuite) TestChangeBooleanAttribute(c *gc.C) { _, err := testing.RunCommand(c, &SetEnvironmentCommand{}, []string{"ssl-hostname-verification=false"}) c.Assert(err, gc.IsNil) stateConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(stateConfig.SSLHostnameVerification(), gc.Equals, false) } func (s *SetEnvironmentSuite) TestChangeMultipleValues(c *gc.C) { _, err := testing.RunCommand(c, &SetEnvironmentCommand{}, []string{"default-series=spartan", "broken=nope", "secret=sekrit"}) c.Assert(err, gc.IsNil) stateConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) attrs := stateConfig.AllAttrs() c.Assert(attrs["default-series"].(string), gc.Equals, "spartan") c.Assert(attrs["broken"].(string), gc.Equals, "nope") c.Assert(attrs["secret"].(string), gc.Equals, "sekrit") } func (s *SetEnvironmentSuite) TestChangeAsCommandPair(c *gc.C) { _, err := testing.RunCommand(c, &SetEnvironmentCommand{}, []string{"default-series=raring"}) c.Assert(err, gc.IsNil) context, err := testing.RunCommand(c, &GetEnvironmentCommand{}, []string{"default-series"}) c.Assert(err, gc.IsNil) output := strings.TrimSpace(testing.Stdout(context)) c.Assert(output, gc.Equals, "raring") } var immutableConfigTests = map[string]string{ "name": "foo", "type": "local", "firewall-mode": "global", "state-port": "1", "api-port": "666", } func (s *SetEnvironmentSuite) TestImmutableConfigValues(c *gc.C) { for name, value := range immutableConfigTests { param := fmt.Sprintf("%s=%s", name, value) _, err := testing.RunCommand(c, &SetEnvironmentCommand{}, []string{param}) errorPattern := fmt.Sprintf("cannot change %s from .* to [\"]?%v[\"]?", name, value) c.Assert(err, gc.ErrorMatches, errorPattern) } } type UnsetEnvironmentSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&UnsetEnvironmentSuite{}) var unsetEnvTests = []struct { args []string err string expected attributes unexpected []string }{ { args: []string{}, err: "No keys specified", }, { args: []string{"xyz", "xyz"}, unexpected: []string{"xyz"}, }, { args: []string{"type", "xyz"}, err: "type: expected string, got nothing", expected: attributes{ "type": "dummy", "xyz": 123, }, }, { args: []string{"syslog-port"}, expected: attributes{ "syslog-port": config.DefaultSyslogPort, }, }, { args: []string{"xyz2", "xyz"}, unexpected: []string{"xyz"}, }, } func (s *UnsetEnvironmentSuite) initConfig(c *gc.C) { err := s.State.UpdateEnvironConfig(map[string]interface{}{ "syslog-port": 1234, "xyz": 123, }, nil, nil) c.Assert(err, gc.IsNil) } func (s *UnsetEnvironmentSuite) TestUnsetEnvironment(c *gc.C) { for _, t := range unsetEnvTests { c.Logf("testing unset-env %v", t.args) s.initConfig(c) _, err := testing.RunCommand(c, &UnsetEnvironmentCommand{}, t.args) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) } else { c.Assert(err, gc.IsNil) } if len(t.expected)+len(t.unexpected) != 0 { stateConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) for k, v := range t.expected { vstate, ok := stateConfig.AllAttrs()[k] c.Assert(ok, jc.IsTrue) c.Assert(vstate, gc.Equals, v) } for _, k := range t.unexpected { _, ok := stateConfig.AllAttrs()[k] c.Assert(ok, jc.IsFalse) } } } } ������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/expose.go�������������������������������������0000644�0000153�0000161�00000001715�12321735776�024253� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" ) // ExposeCommand is responsible exposing services. type ExposeCommand struct { cmd.EnvCommandBase ServiceName string } func (c *ExposeCommand) Info() *cmd.Info { return &cmd.Info{ Name: "expose", Args: "<service>", Purpose: "expose a service", } } func (c *ExposeCommand) Init(args []string) error { if len(args) == 0 { return errors.New("no service name specified") } c.ServiceName = args[0] return cmd.CheckEmpty(args[1:]) } // Run changes the juju-managed firewall to expose any // ports that were also explicitly marked by units as open. func (c *ExposeCommand) Run(_ *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() return client.ServiceExpose(c.ServiceName) } ���������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/set_test.go�����������������������������������0000644�0000153�0000161�00000005456�12321735642�024600� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "io/ioutil" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" ) type SetSuite struct { testing.JujuConnSuite svc *state.Service dir string } var _ = gc.Suite(&SetSuite{}) func (s *SetSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) ch := s.AddTestingCharm(c, "dummy") svc := s.AddTestingService(c, "dummy-service", ch) s.svc = svc s.dir = c.MkDir() setupConfigFile(c, s.dir) } func (s *SetSuite) TestSetOptionSuccess(c *gc.C) { assertSetSuccess(c, s.dir, s.svc, []string{ "username=hello", "outlook=hello@world.tld", }, charm.Settings{ "username": "hello", "outlook": "hello@world.tld", }) assertSetSuccess(c, s.dir, s.svc, []string{ "username=hello=foo", }, charm.Settings{ "username": "hello=foo", "outlook": "hello@world.tld", }) } func (s *SetSuite) TestSetOptionFail(c *gc.C) { assertSetFail(c, s.dir, []string{"foo", "bar"}, "error: invalid option: \"foo\"\n") assertSetFail(c, s.dir, []string{"=bar"}, "error: invalid option: \"=bar\"\n") } func (s *SetSuite) TestSetConfig(c *gc.C) { assertSetFail(c, s.dir, []string{ "--config", "missing.yaml", }, "error.*no such file or directory\n") assertSetSuccess(c, s.dir, s.svc, []string{ "--config", "testconfig.yaml", }, charm.Settings{ "username": "admin001", "skill-level": int64(9000), }) } // assertSetSuccess sets configuration options and checks the expected settings. func assertSetSuccess(c *gc.C, dir string, svc *state.Service, args []string, expect charm.Settings) { ctx := coretesting.ContextForDir(c, dir) code := cmd.Main(&SetCommand{}, ctx, append([]string{"dummy-service"}, args...)) c.Check(code, gc.Equals, 0) settings, err := svc.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, expect) } // assertSetFail sets configuration options and checks the expected error. func assertSetFail(c *gc.C, dir string, args []string, err string) { ctx := coretesting.ContextForDir(c, dir) code := cmd.Main(&SetCommand{}, ctx, append([]string{"dummy-service"}, args...)) c.Check(code, gc.Not(gc.Equals), 0) c.Assert(ctx.Stderr.(*bytes.Buffer).String(), gc.Matches, err) } // setupConfigFile creates a configuration file for testing set // with the --config argument specifying a configuration file. func setupConfigFile(c *gc.C, dir string) string { ctx := coretesting.ContextForDir(c, dir) path := ctx.AbsPath("testconfig.yaml") content := []byte("dummy-service:\n skill-level: 9000\n username: admin001\n\n") err := ioutil.WriteFile(path, content, 0666) c.Assert(err, gc.IsNil) return path } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/status_test.go��������������������������������0000644�0000153�0000161�00000147023�12321735776�025335� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "encoding/json" "fmt" "net/url" "strings" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/presence" coretesting "launchpad.net/juju-core/testing" "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) stdout = ctx.Stdout.(*bytes.Buffer).Bytes() stderr = ctx.Stderr.(*bytes.Buffer).Bytes() return } type StatusSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&StatusSuite{}) type M map[string]interface{} type L []interface{} type testCase struct { summary string steps []stepper } func test(summary string, steps ...stepper) testCase { return testCase{summary, steps} } type stepper interface { step(c *gc.C, ctx *context) } type context struct { st *state.State conn *juju.Conn charms map[string]*state.Charm pingers map[string]*presence.Pinger } func (s *StatusSuite) newContext() *context { st := s.Conn.Environ.(testing.GetStater).GetStateInAPIServer() // We make changes in the API server's state so that // our changes to presence are immediately noticed // in the status. return &context{ st: st, conn: s.Conn, charms: make(map[string]*state.Charm), pingers: make(map[string]*presence.Pinger), } } func (s *StatusSuite) resetContext(c *gc.C, ctx *context) { for _, up := range ctx.pingers { err := up.Kill() c.Check(err, gc.IsNil) } s.JujuConnSuite.Reset(c) } func (ctx *context) run(c *gc.C, steps []stepper) { for i, s := range steps { c.Logf("step %d", i) c.Logf("%#v", s) s.step(c, ctx) } } type aliver interface { AgentAlive() (bool, error) SetAgentAlive() (*presence.Pinger, error) WaitAgentAlive(time.Duration) error } func (ctx *context) setAgentAlive(c *gc.C, a aliver) *presence.Pinger { pinger, err := a.SetAgentAlive() c.Assert(err, gc.IsNil) ctx.st.StartSync() err = a.WaitAgentAlive(coretesting.LongWait) c.Assert(err, gc.IsNil) agentAlive, err := a.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(agentAlive, gc.Equals, true) return pinger } // shortcuts for expected output. var ( machine0 = M{ "agent-state": "started", "dns-name": "dummyenv-0.dns", "instance-id": "dummyenv-0", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", } machine1 = M{ "agent-state": "started", "dns-name": "dummyenv-1.dns", "instance-id": "dummyenv-1", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", } machine2 = M{ "agent-state": "started", "dns-name": "dummyenv-2.dns", "instance-id": "dummyenv-2", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", } machine3 = M{ "agent-state": "started", "dns-name": "dummyenv-3.dns", "instance-id": "dummyenv-3", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", } machine4 = M{ "agent-state": "started", "dns-name": "dummyenv-4.dns", "instance-id": "dummyenv-4", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", } machine1WithContainers = M{ "agent-state": "started", "containers": M{ "1/lxc/0": M{ "agent-state": "started", "containers": M{ "1/lxc/0/lxc/0": M{ "agent-state": "started", "dns-name": "dummyenv-3.dns", "instance-id": "dummyenv-3", "series": "quantal", }, }, "dns-name": "dummyenv-2.dns", "instance-id": "dummyenv-2", "series": "quantal", }, "1/lxc/1": M{ "instance-id": "pending", "series": "quantal", }, }, "dns-name": "dummyenv-1.dns", "instance-id": "dummyenv-1", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", } machine1WithContainersScoped = M{ "agent-state": "started", "containers": M{ "1/lxc/0": M{ "agent-state": "started", "dns-name": "dummyenv-2.dns", "instance-id": "dummyenv-2", "series": "quantal", }, }, "dns-name": "dummyenv-1.dns", "instance-id": "dummyenv-1", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", } unexposedService = M{ "charm": "cs:quantal/dummy-1", "exposed": false, } exposedService = M{ "charm": "cs:quantal/dummy-1", "exposed": true, } ) type outputFormat struct { name string marshal func(v interface{}) ([]byte, error) unmarshal func(data []byte, v interface{}) error } // statusFormats list all output formats supported by status command. var statusFormats = []outputFormat{ {"yaml", goyaml.Marshal, goyaml.Unmarshal}, {"json", json.Marshal, json.Unmarshal}, } var machineCons = constraints.MustParse("cpu-cores=2 mem=8G root-disk=8G") var statusTests = []testCase{ // Status tests test( "bootstrap and starting a single instance", // unlikely, as you can't run juju status in real life without // machine/0 bootstrapped. expect{ "empty state", M{ "environment": "dummyenv", "machines": M{}, "services": M{}, }, }, addMachine{machineId: "0", job: state.JobManageEnviron}, expect{ "simulate juju bootstrap by adding machine/0 to the state", M{ "environment": "dummyenv", "machines": M{ "0": M{ "instance-id": "pending", "series": "quantal", }, }, "services": M{}, }, }, startAliveMachine{"0"}, setAddresses{"0", []instance.Address{ instance.NewAddress("10.0.0.1"), newPublicAddress("dummyenv-0.dns"), }}, expect{ "simulate the PA starting an instance in response to the state change", M{ "environment": "dummyenv", "machines": M{ "0": M{ "agent-state": "pending", "dns-name": "dummyenv-0.dns", "instance-id": "dummyenv-0", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", }, }, "services": M{}, }, }, setMachineStatus{"0", params.StatusStarted, ""}, expect{ "simulate the MA started and set the machine status", M{ "environment": "dummyenv", "machines": M{ "0": machine0, }, "services": M{}, }, }, setTools{"0", version.MustParseBinary("1.2.3-gutsy-ppc")}, expect{ "simulate the MA setting the version", M{ "environment": "dummyenv", "machines": M{ "0": M{ "dns-name": "dummyenv-0.dns", "instance-id": "dummyenv-0", "agent-version": "1.2.3", "agent-state": "started", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", }, }, "services": M{}, }, }, ), test( "deploy two services with networks", addMachine{machineId: "0", job: state.JobManageEnviron}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, setAddresses{"0", []instance.Address{ instance.NewAddress("10.0.0.1"), newPublicAddress("dummyenv-0.dns"), }}, addCharm{"dummy"}, addService{ name: "networks-service", charm: "dummy", withNetworks: []string{"net1", "net2"}, withoutNetworks: []string{"net3", "net4"}, }, addService{ name: "no-networks-service", charm: "dummy", withoutNetworks: []string{"mynet"}, }, expect{ "simulate just the two services and a bootstrap node", M{ "environment": "dummyenv", "machines": M{ "0": machine0, }, "services": M{ "networks-service": M{ "charm": "cs:quantal/dummy-1", "exposed": false, "networks": M{ "enabled": L{"net1", "net2"}, "disabled": L{"net3", "net4"}, }, }, "no-networks-service": M{ "charm": "cs:quantal/dummy-1", "exposed": false, "networks": M{ "disabled": L{"mynet"}, }, }, }, }, }, ), test( "instance with different hardware characteristics", addMachine{machineId: "0", cons: machineCons, job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{ instance.NewAddress("10.0.0.1"), newPublicAddress("dummyenv-0.dns"), }}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, expect{ "machine 0 has specific hardware characteristics", M{ "environment": "dummyenv", "machines": M{ "0": M{ "agent-state": "started", "dns-name": "dummyenv-0.dns", "instance-id": "dummyenv-0", "series": "quantal", "hardware": "arch=amd64 cpu-cores=2 mem=8192M root-disk=8192M", }, }, "services": M{}, }, }, ), test( "instance without addresses", addMachine{machineId: "0", cons: machineCons, job: state.JobManageEnviron}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, expect{ "machine 0 has no dns-name", M{ "environment": "dummyenv", "machines": M{ "0": M{ "agent-state": "started", "instance-id": "dummyenv-0", "series": "quantal", "hardware": "arch=amd64 cpu-cores=2 mem=8192M root-disk=8192M", }, }, "services": M{}, }, }, ), test( "test pending and missing machines", addMachine{machineId: "0", job: state.JobManageEnviron}, expect{ "machine 0 reports pending", M{ "environment": "dummyenv", "machines": M{ "0": M{ "instance-id": "pending", "series": "quantal", }, }, "services": M{}, }, }, startMissingMachine{"0"}, expect{ "machine 0 reports missing", M{ "environment": "dummyenv", "machines": M{ "0": M{ "instance-state": "missing", "instance-id": "i-missing", "agent-state": "pending", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", }, }, "services": M{}, }, }, ), test( "add two services and expose one, then add 2 more machines and some units", addMachine{machineId: "0", job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{instance.NewAddress("dummyenv-0.dns")}}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, addCharm{"dummy"}, addService{name: "dummy-service", charm: "dummy"}, addService{name: "exposed-service", charm: "dummy"}, expect{ "no services exposed yet", M{ "environment": "dummyenv", "machines": M{ "0": machine0, }, "services": M{ "dummy-service": unexposedService, "exposed-service": unexposedService, }, }, }, setServiceExposed{"exposed-service", true}, expect{ "one exposed service", M{ "environment": "dummyenv", "machines": M{ "0": machine0, }, "services": M{ "dummy-service": unexposedService, "exposed-service": exposedService, }, }, }, addMachine{machineId: "1", job: state.JobHostUnits}, setAddresses{"1", []instance.Address{instance.NewAddress("dummyenv-1.dns")}}, startAliveMachine{"1"}, setMachineStatus{"1", params.StatusStarted, ""}, addMachine{machineId: "2", job: state.JobHostUnits}, setAddresses{"2", []instance.Address{instance.NewAddress("dummyenv-2.dns")}}, startAliveMachine{"2"}, setMachineStatus{"2", params.StatusStarted, ""}, expect{ "two more machines added", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1, "2": machine2, }, "services": M{ "dummy-service": unexposedService, "exposed-service": exposedService, }, }, }, addUnit{"dummy-service", "1"}, addAliveUnit{"exposed-service", "2"}, setUnitStatus{"exposed-service/0", params.StatusError, "You Require More Vespene Gas"}, // Open multiple ports with different protocols, // ensure they're sorted on protocol, then number. openUnitPort{"exposed-service/0", "udp", 10}, openUnitPort{"exposed-service/0", "udp", 2}, openUnitPort{"exposed-service/0", "tcp", 3}, openUnitPort{"exposed-service/0", "tcp", 2}, // Simulate some status with no info, while the agent is down. setUnitStatus{"dummy-service/0", params.StatusStarted, ""}, expect{ "add two units, one alive (in error state), one down", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1, "2": machine2, }, "services": M{ "exposed-service": M{ "charm": "cs:quantal/dummy-1", "exposed": true, "units": M{ "exposed-service/0": M{ "machine": "2", "agent-state": "error", "agent-state-info": "You Require More Vespene Gas", "open-ports": L{ "2/tcp", "3/tcp", "2/udp", "10/udp", }, "public-address": "dummyenv-2.dns", }, }, }, "dummy-service": M{ "charm": "cs:quantal/dummy-1", "exposed": false, "units": M{ "dummy-service/0": M{ "machine": "1", "agent-state": "down", "agent-state-info": "(started)", "public-address": "dummyenv-1.dns", }, }, }, }, }, }, addMachine{machineId: "3", job: state.JobHostUnits}, startMachine{"3"}, // Simulate some status with info, while the agent is down. setAddresses{"3", []instance.Address{instance.NewAddress("dummyenv-3.dns")}}, setMachineStatus{"3", params.StatusStopped, "Really?"}, addMachine{machineId: "4", job: state.JobHostUnits}, setAddresses{"4", []instance.Address{instance.NewAddress("dummyenv-4.dns")}}, startAliveMachine{"4"}, setMachineStatus{"4", params.StatusError, "Beware the red toys"}, ensureDyingUnit{"dummy-service/0"}, addMachine{machineId: "5", job: state.JobHostUnits}, ensureDeadMachine{"5"}, expect{ "add three more machine, one with a dead agent, one in error state and one dead itself; also one dying unit", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1, "2": machine2, "3": M{ "dns-name": "dummyenv-3.dns", "instance-id": "dummyenv-3", "agent-state": "down", "agent-state-info": "(stopped: Really?)", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", }, "4": M{ "dns-name": "dummyenv-4.dns", "instance-id": "dummyenv-4", "agent-state": "error", "agent-state-info": "Beware the red toys", "series": "quantal", "hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M", }, "5": M{ "life": "dead", "instance-id": "pending", "series": "quantal", }, }, "services": M{ "exposed-service": M{ "charm": "cs:quantal/dummy-1", "exposed": true, "units": M{ "exposed-service/0": M{ "machine": "2", "agent-state": "error", "agent-state-info": "You Require More Vespene Gas", "open-ports": L{ "2/tcp", "3/tcp", "2/udp", "10/udp", }, "public-address": "dummyenv-2.dns", }, }, }, "dummy-service": M{ "charm": "cs:quantal/dummy-1", "exposed": false, "units": M{ "dummy-service/0": M{ "machine": "1", "life": "dying", "agent-state": "down", "agent-state-info": "(started)", "public-address": "dummyenv-1.dns", }, }, }, }, }, }, scopedExpect{ "scope status on dummy-service/0 unit", []string{"dummy-service/0"}, M{ "environment": "dummyenv", "machines": M{ "1": machine1, }, "services": M{ "dummy-service": M{ "charm": "cs:quantal/dummy-1", "exposed": false, "units": M{ "dummy-service/0": M{ "machine": "1", "life": "dying", "agent-state": "down", "agent-state-info": "(started)", "public-address": "dummyenv-1.dns", }, }, }, }, }, }, scopedExpect{ "scope status on exposed-service service", []string{"exposed-service"}, M{ "environment": "dummyenv", "machines": M{ "2": machine2, }, "services": M{ "exposed-service": M{ "charm": "cs:quantal/dummy-1", "exposed": true, "units": M{ "exposed-service/0": M{ "machine": "2", "agent-state": "error", "agent-state-info": "You Require More Vespene Gas", "open-ports": L{ "2/tcp", "3/tcp", "2/udp", "10/udp", }, "public-address": "dummyenv-2.dns", }, }, }, }, }, }, scopedExpect{ "scope status on service pattern", []string{"d*-service"}, M{ "environment": "dummyenv", "machines": M{ "1": machine1, }, "services": M{ "dummy-service": M{ "charm": "cs:quantal/dummy-1", "exposed": false, "units": M{ "dummy-service/0": M{ "machine": "1", "life": "dying", "agent-state": "down", "agent-state-info": "(started)", "public-address": "dummyenv-1.dns", }, }, }, }, }, }, scopedExpect{ "scope status on unit pattern", []string{"e*posed-service/*"}, M{ "environment": "dummyenv", "machines": M{ "2": machine2, }, "services": M{ "exposed-service": M{ "charm": "cs:quantal/dummy-1", "exposed": true, "units": M{ "exposed-service/0": M{ "machine": "2", "agent-state": "error", "agent-state-info": "You Require More Vespene Gas", "open-ports": L{ "2/tcp", "3/tcp", "2/udp", "10/udp", }, "public-address": "dummyenv-2.dns", }, }, }, }, }, }, scopedExpect{ "scope status on combination of service and unit patterns", []string{"exposed-service", "dummy-service", "e*posed-service/*", "dummy-service/*"}, M{ "environment": "dummyenv", "machines": M{ "1": machine1, "2": machine2, }, "services": M{ "dummy-service": M{ "charm": "cs:quantal/dummy-1", "exposed": false, "units": M{ "dummy-service/0": M{ "machine": "1", "life": "dying", "agent-state": "down", "agent-state-info": "(started)", "public-address": "dummyenv-1.dns", }, }, }, "exposed-service": M{ "charm": "cs:quantal/dummy-1", "exposed": true, "units": M{ "exposed-service/0": M{ "machine": "2", "agent-state": "error", "agent-state-info": "You Require More Vespene Gas", "open-ports": L{ "2/tcp", "3/tcp", "2/udp", "10/udp", }, "public-address": "dummyenv-2.dns", }, }, }, }, }, }, ), test( "add a dying service", addCharm{"dummy"}, addService{name: "dummy-service", charm: "dummy"}, addMachine{machineId: "0", job: state.JobHostUnits}, addUnit{"dummy-service", "0"}, ensureDyingService{"dummy-service"}, expect{ "service shows life==dying", M{ "environment": "dummyenv", "machines": M{ "0": M{ "instance-id": "pending", "series": "quantal", }, }, "services": M{ "dummy-service": M{ "charm": "cs:quantal/dummy-1", "exposed": false, "life": "dying", "units": M{ "dummy-service/0": M{ "machine": "0", "agent-state": "pending", }, }, }, }, }, }, ), // Relation tests test( "complex scenario with multiple related services", addMachine{machineId: "0", job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{instance.NewAddress("dummyenv-0.dns")}}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, addCharm{"wordpress"}, addCharm{"mysql"}, addCharm{"varnish"}, addService{name: "project", charm: "wordpress"}, setServiceExposed{"project", true}, addMachine{machineId: "1", job: state.JobHostUnits}, setAddresses{"1", []instance.Address{instance.NewAddress("dummyenv-1.dns")}}, startAliveMachine{"1"}, setMachineStatus{"1", params.StatusStarted, ""}, addAliveUnit{"project", "1"}, setUnitStatus{"project/0", params.StatusStarted, ""}, addService{name: "mysql", charm: "mysql"}, setServiceExposed{"mysql", true}, addMachine{machineId: "2", job: state.JobHostUnits}, setAddresses{"2", []instance.Address{instance.NewAddress("dummyenv-2.dns")}}, startAliveMachine{"2"}, setMachineStatus{"2", params.StatusStarted, ""}, addAliveUnit{"mysql", "2"}, setUnitStatus{"mysql/0", params.StatusStarted, ""}, addService{name: "varnish", charm: "varnish"}, setServiceExposed{"varnish", true}, addMachine{machineId: "3", job: state.JobHostUnits}, setAddresses{"3", []instance.Address{instance.NewAddress("dummyenv-3.dns")}}, startAliveMachine{"3"}, setMachineStatus{"3", params.StatusStarted, ""}, addUnit{"varnish", "3"}, addService{name: "private", charm: "wordpress"}, setServiceExposed{"private", true}, addMachine{machineId: "4", job: state.JobHostUnits}, setAddresses{"4", []instance.Address{instance.NewAddress("dummyenv-4.dns")}}, startAliveMachine{"4"}, setMachineStatus{"4", params.StatusStarted, ""}, addUnit{"private", "4"}, relateServices{"project", "mysql"}, relateServices{"project", "varnish"}, relateServices{"private", "mysql"}, expect{ "multiples services with relations between some of them", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1, "2": machine2, "3": machine3, "4": machine4, }, "services": M{ "project": M{ "charm": "cs:quantal/wordpress-3", "exposed": true, "units": M{ "project/0": M{ "machine": "1", "agent-state": "started", "public-address": "dummyenv-1.dns", }, }, "relations": M{ "db": L{"mysql"}, "cache": L{"varnish"}, }, }, "mysql": M{ "charm": "cs:quantal/mysql-1", "exposed": true, "units": M{ "mysql/0": M{ "machine": "2", "agent-state": "started", "public-address": "dummyenv-2.dns", }, }, "relations": M{ "server": L{"private", "project"}, }, }, "varnish": M{ "charm": "cs:quantal/varnish-1", "exposed": true, "units": M{ "varnish/0": M{ "machine": "3", "agent-state": "pending", "public-address": "dummyenv-3.dns", }, }, "relations": M{ "webcache": L{"project"}, }, }, "private": M{ "charm": "cs:quantal/wordpress-3", "exposed": true, "units": M{ "private/0": M{ "machine": "4", "agent-state": "pending", "public-address": "dummyenv-4.dns", }, }, "relations": M{ "db": L{"mysql"}, }, }, }, }, }, ), test( "simple peer scenario", addMachine{machineId: "0", job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{instance.NewAddress("dummyenv-0.dns")}}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, addCharm{"riak"}, addCharm{"wordpress"}, addService{name: "riak", charm: "riak"}, setServiceExposed{"riak", true}, addMachine{machineId: "1", job: state.JobHostUnits}, setAddresses{"1", []instance.Address{instance.NewAddress("dummyenv-1.dns")}}, startAliveMachine{"1"}, setMachineStatus{"1", params.StatusStarted, ""}, addAliveUnit{"riak", "1"}, setUnitStatus{"riak/0", params.StatusStarted, ""}, addMachine{machineId: "2", job: state.JobHostUnits}, setAddresses{"2", []instance.Address{instance.NewAddress("dummyenv-2.dns")}}, startAliveMachine{"2"}, setMachineStatus{"2", params.StatusStarted, ""}, addAliveUnit{"riak", "2"}, setUnitStatus{"riak/1", params.StatusStarted, ""}, addMachine{machineId: "3", job: state.JobHostUnits}, setAddresses{"3", []instance.Address{instance.NewAddress("dummyenv-3.dns")}}, startAliveMachine{"3"}, setMachineStatus{"3", params.StatusStarted, ""}, addAliveUnit{"riak", "3"}, setUnitStatus{"riak/2", params.StatusStarted, ""}, expect{ "multiples related peer units", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1, "2": machine2, "3": machine3, }, "services": M{ "riak": M{ "charm": "cs:quantal/riak-7", "exposed": true, "units": M{ "riak/0": M{ "machine": "1", "agent-state": "started", "public-address": "dummyenv-1.dns", }, "riak/1": M{ "machine": "2", "agent-state": "started", "public-address": "dummyenv-2.dns", }, "riak/2": M{ "machine": "3", "agent-state": "started", "public-address": "dummyenv-3.dns", }, }, "relations": M{ "ring": L{"riak"}, }, }, }, }, }, ), // Subordinate tests test( "one service with one subordinate service", addMachine{machineId: "0", job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{instance.NewAddress("dummyenv-0.dns")}}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, addCharm{"wordpress"}, addCharm{"mysql"}, addCharm{"logging"}, addService{name: "wordpress", charm: "wordpress"}, setServiceExposed{"wordpress", true}, addMachine{machineId: "1", job: state.JobHostUnits}, setAddresses{"1", []instance.Address{instance.NewAddress("dummyenv-1.dns")}}, startAliveMachine{"1"}, setMachineStatus{"1", params.StatusStarted, ""}, addAliveUnit{"wordpress", "1"}, setUnitStatus{"wordpress/0", params.StatusStarted, ""}, addService{name: "mysql", charm: "mysql"}, setServiceExposed{"mysql", true}, addMachine{machineId: "2", job: state.JobHostUnits}, setAddresses{"2", []instance.Address{instance.NewAddress("dummyenv-2.dns")}}, startAliveMachine{"2"}, setMachineStatus{"2", params.StatusStarted, ""}, addAliveUnit{"mysql", "2"}, setUnitStatus{"mysql/0", params.StatusStarted, ""}, addService{name: "logging", charm: "logging"}, setServiceExposed{"logging", true}, relateServices{"wordpress", "mysql"}, relateServices{"wordpress", "logging"}, relateServices{"mysql", "logging"}, addSubordinate{"wordpress/0", "logging"}, addSubordinate{"mysql/0", "logging"}, setUnitsAlive{"logging"}, setUnitStatus{"logging/0", params.StatusStarted, ""}, setUnitStatus{"logging/1", params.StatusError, "somehow lost in all those logs"}, expect{ "multiples related peer units", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1, "2": machine2, }, "services": M{ "wordpress": M{ "charm": "cs:quantal/wordpress-3", "exposed": true, "units": M{ "wordpress/0": M{ "machine": "1", "agent-state": "started", "subordinates": M{ "logging/0": M{ "agent-state": "started", "public-address": "dummyenv-1.dns", }, }, "public-address": "dummyenv-1.dns", }, }, "relations": M{ "db": L{"mysql"}, "logging-dir": L{"logging"}, }, }, "mysql": M{ "charm": "cs:quantal/mysql-1", "exposed": true, "units": M{ "mysql/0": M{ "machine": "2", "agent-state": "started", "subordinates": M{ "logging/1": M{ "agent-state": "error", "agent-state-info": "somehow lost in all those logs", "public-address": "dummyenv-2.dns", }, }, "public-address": "dummyenv-2.dns", }, }, "relations": M{ "server": L{"wordpress"}, "juju-info": L{"logging"}, }, }, "logging": M{ "charm": "cs:quantal/logging-1", "exposed": true, "relations": M{ "logging-directory": L{"wordpress"}, "info": L{"mysql"}, }, "subordinate-to": L{"mysql", "wordpress"}, }, }, }, }, // scoped on 'logging' scopedExpect{ "subordinates scoped on logging", []string{"logging"}, M{ "environment": "dummyenv", "machines": M{ "1": machine1, "2": machine2, }, "services": M{ "wordpress": M{ "charm": "cs:quantal/wordpress-3", "exposed": true, "units": M{ "wordpress/0": M{ "machine": "1", "agent-state": "started", "subordinates": M{ "logging/0": M{ "agent-state": "started", "public-address": "dummyenv-1.dns", }, }, "public-address": "dummyenv-1.dns", }, }, "relations": M{ "db": L{"mysql"}, "logging-dir": L{"logging"}, }, }, "mysql": M{ "charm": "cs:quantal/mysql-1", "exposed": true, "units": M{ "mysql/0": M{ "machine": "2", "agent-state": "started", "subordinates": M{ "logging/1": M{ "agent-state": "error", "agent-state-info": "somehow lost in all those logs", "public-address": "dummyenv-2.dns", }, }, "public-address": "dummyenv-2.dns", }, }, "relations": M{ "server": L{"wordpress"}, "juju-info": L{"logging"}, }, }, "logging": M{ "charm": "cs:quantal/logging-1", "exposed": true, "relations": M{ "logging-directory": L{"wordpress"}, "info": L{"mysql"}, }, "subordinate-to": L{"mysql", "wordpress"}, }, }, }, }, // scoped on wordpress/0 scopedExpect{ "subordinates scoped on logging", []string{"wordpress/0"}, M{ "environment": "dummyenv", "machines": M{ "1": machine1, }, "services": M{ "wordpress": M{ "charm": "cs:quantal/wordpress-3", "exposed": true, "units": M{ "wordpress/0": M{ "machine": "1", "agent-state": "started", "subordinates": M{ "logging/0": M{ "agent-state": "started", "public-address": "dummyenv-1.dns", }, }, "public-address": "dummyenv-1.dns", }, }, "relations": M{ "db": L{"mysql"}, "logging-dir": L{"logging"}, }, }, "logging": M{ "charm": "cs:quantal/logging-1", "exposed": true, "relations": M{ "logging-directory": L{"wordpress"}, "info": L{"mysql"}, }, "subordinate-to": L{"mysql", "wordpress"}, }, }, }, }, ), test( "one service with two subordinate services", addMachine{machineId: "0", job: state.JobManageEnviron}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, addCharm{"wordpress"}, addCharm{"logging"}, addCharm{"monitoring"}, addService{name: "wordpress", charm: "wordpress"}, setServiceExposed{"wordpress", true}, addMachine{machineId: "1", job: state.JobHostUnits}, setAddresses{"1", []instance.Address{instance.NewAddress("dummyenv-1.dns")}}, startAliveMachine{"1"}, setMachineStatus{"1", params.StatusStarted, ""}, addAliveUnit{"wordpress", "1"}, setUnitStatus{"wordpress/0", params.StatusStarted, ""}, addService{name: "logging", charm: "logging"}, setServiceExposed{"logging", true}, addService{name: "monitoring", charm: "monitoring"}, setServiceExposed{"monitoring", true}, relateServices{"wordpress", "logging"}, relateServices{"wordpress", "monitoring"}, addSubordinate{"wordpress/0", "logging"}, addSubordinate{"wordpress/0", "monitoring"}, setUnitsAlive{"logging"}, setUnitStatus{"logging/0", params.StatusStarted, ""}, setUnitsAlive{"monitoring"}, setUnitStatus{"monitoring/0", params.StatusStarted, ""}, // scoped on monitoring; make sure logging doesn't show up. scopedExpect{ "subordinates scoped on:", []string{"monitoring"}, M{ "environment": "dummyenv", "machines": M{ "1": machine1, }, "services": M{ "wordpress": M{ "charm": "cs:quantal/wordpress-3", "exposed": true, "units": M{ "wordpress/0": M{ "machine": "1", "agent-state": "started", "subordinates": M{ "monitoring/0": M{ "agent-state": "started", "public-address": "dummyenv-1.dns", }, }, "public-address": "dummyenv-1.dns", }, }, "relations": M{ "logging-dir": L{"logging"}, "monitoring-port": L{"monitoring"}, }, }, "monitoring": M{ "charm": "cs:quantal/monitoring-0", "exposed": true, "relations": M{ "monitoring-port": L{"wordpress"}, }, "subordinate-to": L{"wordpress"}, }, }, }, }, ), test( "machines with containers", addMachine{machineId: "0", job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{instance.NewAddress("dummyenv-0.dns")}}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, addCharm{"mysql"}, addService{name: "mysql", charm: "mysql"}, setServiceExposed{"mysql", true}, addMachine{machineId: "1", job: state.JobHostUnits}, setAddresses{"1", []instance.Address{instance.NewAddress("dummyenv-1.dns")}}, startAliveMachine{"1"}, setMachineStatus{"1", params.StatusStarted, ""}, addAliveUnit{"mysql", "1"}, setUnitStatus{"mysql/0", params.StatusStarted, ""}, // A container on machine 1. addContainer{"1", "1/lxc/0", state.JobHostUnits}, setAddresses{"1/lxc/0", []instance.Address{instance.NewAddress("dummyenv-2.dns")}}, startAliveMachine{"1/lxc/0"}, setMachineStatus{"1/lxc/0", params.StatusStarted, ""}, addAliveUnit{"mysql", "1/lxc/0"}, setUnitStatus{"mysql/1", params.StatusStarted, ""}, addContainer{"1", "1/lxc/1", state.JobHostUnits}, // A nested container. addContainer{"1/lxc/0", "1/lxc/0/lxc/0", state.JobHostUnits}, setAddresses{"1/lxc/0/lxc/0", []instance.Address{instance.NewAddress("dummyenv-3.dns")}}, startAliveMachine{"1/lxc/0/lxc/0"}, setMachineStatus{"1/lxc/0/lxc/0", params.StatusStarted, ""}, expect{ "machines with nested containers", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1WithContainers, }, "services": M{ "mysql": M{ "charm": "cs:quantal/mysql-1", "exposed": true, "units": M{ "mysql/0": M{ "machine": "1", "agent-state": "started", "public-address": "dummyenv-1.dns", }, "mysql/1": M{ "machine": "1/lxc/0", "agent-state": "started", "public-address": "dummyenv-2.dns", }, }, }, }, }, }, // once again, with a scope on mysql/1 scopedExpect{ "machines with nested containers", []string{"mysql/1"}, M{ "environment": "dummyenv", "machines": M{ "1": machine1WithContainersScoped, }, "services": M{ "mysql": M{ "charm": "cs:quantal/mysql-1", "exposed": true, "units": M{ "mysql/1": M{ "machine": "1/lxc/0", "agent-state": "started", "public-address": "dummyenv-2.dns", }, }, }, }, }, }, ), test( "service with out of date charm", addMachine{machineId: "0", job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{instance.NewAddress("dummyenv-0.dns")}}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, addMachine{machineId: "1", job: state.JobHostUnits}, setAddresses{"1", []instance.Address{instance.NewAddress("dummyenv-1.dns")}}, startAliveMachine{"1"}, setMachineStatus{"1", params.StatusStarted, ""}, addCharm{"mysql"}, addService{name: "mysql", charm: "mysql"}, setServiceExposed{"mysql", true}, addCharmPlaceholder{"mysql", 23}, addAliveUnit{"mysql", "1"}, expect{ "services and units with correct charm status", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1, }, "services": M{ "mysql": M{ "charm": "cs:quantal/mysql-1", "can-upgrade-to": "cs:quantal/mysql-23", "exposed": true, "units": M{ "mysql/0": M{ "machine": "1", "agent-state": "pending", "public-address": "dummyenv-1.dns", }, }, }, }, }, }, ), test( "unit with out of date charm", addMachine{machineId: "0", job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{instance.NewAddress("dummyenv-0.dns")}}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, addMachine{machineId: "1", job: state.JobHostUnits}, setAddresses{"1", []instance.Address{instance.NewAddress("dummyenv-1.dns")}}, startAliveMachine{"1"}, setMachineStatus{"1", params.StatusStarted, ""}, addCharm{"mysql"}, addService{name: "mysql", charm: "mysql"}, setServiceExposed{"mysql", true}, addAliveUnit{"mysql", "1"}, setUnitCharmURL{"mysql/0", "cs:quantal/mysql-1"}, addCharmWithRevision{addCharm{"mysql"}, "local", 1}, setServiceCharm{"mysql", "local:quantal/mysql-1"}, expect{ "services and units with correct charm status", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1, }, "services": M{ "mysql": M{ "charm": "local:quantal/mysql-1", "exposed": true, "units": M{ "mysql/0": M{ "machine": "1", "agent-state": "started", "upgrading-from": "cs:quantal/mysql-1", "public-address": "dummyenv-1.dns", }, }, }, }, }, }, ), test( "service and unit with out of date charms", addMachine{machineId: "0", job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{instance.NewAddress("dummyenv-0.dns")}}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, addMachine{machineId: "1", job: state.JobHostUnits}, setAddresses{"1", []instance.Address{instance.NewAddress("dummyenv-1.dns")}}, startAliveMachine{"1"}, setMachineStatus{"1", params.StatusStarted, ""}, addCharm{"mysql"}, addService{name: "mysql", charm: "mysql"}, setServiceExposed{"mysql", true}, addAliveUnit{"mysql", "1"}, setUnitCharmURL{"mysql/0", "cs:quantal/mysql-1"}, addCharmWithRevision{addCharm{"mysql"}, "cs", 2}, setServiceCharm{"mysql", "cs:quantal/mysql-2"}, addCharmPlaceholder{"mysql", 23}, expect{ "services and units with correct charm status", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1, }, "services": M{ "mysql": M{ "charm": "cs:quantal/mysql-2", "can-upgrade-to": "cs:quantal/mysql-23", "exposed": true, "units": M{ "mysql/0": M{ "machine": "1", "agent-state": "started", "upgrading-from": "cs:quantal/mysql-1", "public-address": "dummyenv-1.dns", }, }, }, }, }, }, ), test( "service with local charm not shown as out of date", addMachine{machineId: "0", job: state.JobManageEnviron}, setAddresses{"0", []instance.Address{instance.NewAddress("dummyenv-0.dns")}}, startAliveMachine{"0"}, setMachineStatus{"0", params.StatusStarted, ""}, addMachine{machineId: "1", job: state.JobHostUnits}, setAddresses{"1", []instance.Address{instance.NewAddress("dummyenv-1.dns")}}, startAliveMachine{"1"}, setMachineStatus{"1", params.StatusStarted, ""}, addCharm{"mysql"}, addService{name: "mysql", charm: "mysql"}, setServiceExposed{"mysql", true}, addAliveUnit{"mysql", "1"}, setUnitCharmURL{"mysql/0", "cs:quantal/mysql-1"}, addCharmWithRevision{addCharm{"mysql"}, "local", 1}, setServiceCharm{"mysql", "local:quantal/mysql-1"}, addCharmPlaceholder{"mysql", 23}, expect{ "services and units with correct charm status", M{ "environment": "dummyenv", "machines": M{ "0": machine0, "1": machine1, }, "services": M{ "mysql": M{ "charm": "local:quantal/mysql-1", "exposed": true, "units": M{ "mysql/0": M{ "machine": "1", "agent-state": "started", "upgrading-from": "cs:quantal/mysql-1", "public-address": "dummyenv-1.dns", }, }, }, }, }, }, ), } // TODO(dfc) test failing components by destructively mutating the state under the hood type addMachine struct { machineId string cons constraints.Value job state.MachineJob } func (am addMachine) step(c *gc.C, ctx *context) { m, err := ctx.st.AddOneMachine(state.MachineTemplate{ Series: "quantal", Constraints: am.cons, Jobs: []state.MachineJob{am.job}, }) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, am.machineId) } type addContainer struct { parentId string machineId string job state.MachineJob } func (ac addContainer) step(c *gc.C, ctx *context) { template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{ac.job}, } m, err := ctx.st.AddMachineInsideMachine(template, ac.parentId, instance.LXC) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, ac.machineId) } type startMachine struct { machineId string } func (sm startMachine) step(c *gc.C, ctx *context) { m, err := ctx.st.Machine(sm.machineId) c.Assert(err, gc.IsNil) cons, err := m.Constraints() c.Assert(err, gc.IsNil) inst, hc := testing.AssertStartInstanceWithConstraints(c, ctx.conn.Environ, m.Id(), cons) err = m.SetProvisioned(inst.Id(), "fake_nonce", hc) c.Assert(err, gc.IsNil) } type startMissingMachine struct { machineId string } func (sm startMissingMachine) step(c *gc.C, ctx *context) { m, err := ctx.st.Machine(sm.machineId) c.Assert(err, gc.IsNil) cons, err := m.Constraints() c.Assert(err, gc.IsNil) _, hc := testing.AssertStartInstanceWithConstraints(c, ctx.conn.Environ, m.Id(), cons) err = m.SetProvisioned("i-missing", "fake_nonce", hc) c.Assert(err, gc.IsNil) err = m.SetInstanceStatus("missing") c.Assert(err, gc.IsNil) } type startAliveMachine struct { machineId string } func (sam startAliveMachine) step(c *gc.C, ctx *context) { m, err := ctx.st.Machine(sam.machineId) c.Assert(err, gc.IsNil) pinger := ctx.setAgentAlive(c, m) cons, err := m.Constraints() c.Assert(err, gc.IsNil) inst, hc := testing.AssertStartInstanceWithConstraints(c, ctx.conn.Environ, m.Id(), cons) err = m.SetProvisioned(inst.Id(), "fake_nonce", hc) c.Assert(err, gc.IsNil) ctx.pingers[m.Id()] = pinger } type setAddresses struct { machineId string addresses []instance.Address } func (sa setAddresses) step(c *gc.C, ctx *context) { m, err := ctx.st.Machine(sa.machineId) c.Assert(err, gc.IsNil) err = m.SetAddresses(sa.addresses) c.Assert(err, gc.IsNil) } type setTools struct { machineId string version version.Binary } func (st setTools) step(c *gc.C, ctx *context) { m, err := ctx.st.Machine(st.machineId) c.Assert(err, gc.IsNil) err = m.SetAgentVersion(st.version) c.Assert(err, gc.IsNil) } type addCharm struct { name string } func (ac addCharm) addCharmStep(c *gc.C, ctx *context, scheme string, rev int) { ch := coretesting.Charms.Dir(ac.name) name := ch.Meta().Name curl := charm.MustParseURL(fmt.Sprintf("%s:quantal/%s-%d", scheme, name, rev)) bundleURL, err := url.Parse(fmt.Sprintf("http://bundles.testing.invalid/%s-%d", name, rev)) c.Assert(err, gc.IsNil) dummy, err := ctx.st.AddCharm(ch, curl, bundleURL, fmt.Sprintf("%s-%d-sha256", name, rev)) c.Assert(err, gc.IsNil) ctx.charms[ac.name] = dummy } func (ac addCharm) step(c *gc.C, ctx *context) { ch := coretesting.Charms.Dir(ac.name) ac.addCharmStep(c, ctx, "cs", ch.Revision()) } type addCharmWithRevision struct { addCharm scheme string rev int } func (ac addCharmWithRevision) step(c *gc.C, ctx *context) { ac.addCharmStep(c, ctx, ac.scheme, ac.rev) } type addService struct { name string charm string withNetworks []string withoutNetworks []string } func (as addService) step(c *gc.C, ctx *context) { ch, ok := ctx.charms[as.charm] c.Assert(ok, gc.Equals, true) _, err := ctx.st.AddService(as.name, "user-admin", ch, as.withNetworks, as.withoutNetworks) c.Assert(err, gc.IsNil) } type setServiceExposed struct { name string exposed bool } func (sse setServiceExposed) step(c *gc.C, ctx *context) { s, err := ctx.st.Service(sse.name) c.Assert(err, gc.IsNil) if sse.exposed { err = s.SetExposed() c.Assert(err, gc.IsNil) } } type setServiceCharm struct { name string charm string } func (ssc setServiceCharm) step(c *gc.C, ctx *context) { ch, err := ctx.st.Charm(charm.MustParseURL(ssc.charm)) c.Assert(err, gc.IsNil) s, err := ctx.st.Service(ssc.name) c.Assert(err, gc.IsNil) err = s.SetCharm(ch, false) c.Assert(err, gc.IsNil) } type addCharmPlaceholder struct { name string rev int } func (ac addCharmPlaceholder) step(c *gc.C, ctx *context) { ch := coretesting.Charms.Dir(ac.name) name := ch.Meta().Name curl := charm.MustParseURL(fmt.Sprintf("cs:quantal/%s-%d", name, ac.rev)) err := ctx.st.AddStoreCharmPlaceholder(curl) c.Assert(err, gc.IsNil) } type addUnit struct { serviceName string machineId string } func (au addUnit) step(c *gc.C, ctx *context) { s, err := ctx.st.Service(au.serviceName) c.Assert(err, gc.IsNil) u, err := s.AddUnit() c.Assert(err, gc.IsNil) m, err := ctx.st.Machine(au.machineId) c.Assert(err, gc.IsNil) err = u.AssignToMachine(m) c.Assert(err, gc.IsNil) } type addAliveUnit struct { serviceName string machineId string } func (aau addAliveUnit) step(c *gc.C, ctx *context) { s, err := ctx.st.Service(aau.serviceName) c.Assert(err, gc.IsNil) u, err := s.AddUnit() c.Assert(err, gc.IsNil) pinger := ctx.setAgentAlive(c, u) m, err := ctx.st.Machine(aau.machineId) c.Assert(err, gc.IsNil) err = u.AssignToMachine(m) c.Assert(err, gc.IsNil) ctx.pingers[u.Name()] = pinger } type setUnitsAlive struct { serviceName string } func (sua setUnitsAlive) step(c *gc.C, ctx *context) { s, err := ctx.st.Service(sua.serviceName) c.Assert(err, gc.IsNil) us, err := s.AllUnits() c.Assert(err, gc.IsNil) for _, u := range us { ctx.pingers[u.Name()] = ctx.setAgentAlive(c, u) } } type setUnitStatus struct { unitName string status params.Status statusInfo string } func (sus setUnitStatus) step(c *gc.C, ctx *context) { u, err := ctx.st.Unit(sus.unitName) c.Assert(err, gc.IsNil) err = u.SetStatus(sus.status, sus.statusInfo, nil) c.Assert(err, gc.IsNil) } type setUnitCharmURL struct { unitName string charm string } func (uc setUnitCharmURL) step(c *gc.C, ctx *context) { u, err := ctx.st.Unit(uc.unitName) c.Assert(err, gc.IsNil) curl := charm.MustParseURL(uc.charm) err = u.SetCharmURL(curl) c.Assert(err, gc.IsNil) err = u.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) } type openUnitPort struct { unitName string protocol string number int } func (oup openUnitPort) step(c *gc.C, ctx *context) { u, err := ctx.st.Unit(oup.unitName) c.Assert(err, gc.IsNil) err = u.OpenPort(oup.protocol, oup.number) c.Assert(err, gc.IsNil) } type ensureDyingUnit struct { unitName string } func (e ensureDyingUnit) step(c *gc.C, ctx *context) { u, err := ctx.st.Unit(e.unitName) c.Assert(err, gc.IsNil) err = u.Destroy() c.Assert(err, gc.IsNil) c.Assert(u.Life(), gc.Equals, state.Dying) } type ensureDyingService struct { serviceName string } func (e ensureDyingService) step(c *gc.C, ctx *context) { svc, err := ctx.st.Service(e.serviceName) c.Assert(err, gc.IsNil) err = svc.Destroy() c.Assert(err, gc.IsNil) err = svc.Refresh() c.Assert(err, gc.IsNil) c.Assert(svc.Life(), gc.Equals, state.Dying) } type ensureDeadMachine struct { machineId string } func (e ensureDeadMachine) step(c *gc.C, ctx *context) { m, err := ctx.st.Machine(e.machineId) c.Assert(err, gc.IsNil) err = m.EnsureDead() c.Assert(err, gc.IsNil) c.Assert(m.Life(), gc.Equals, state.Dead) } type setMachineStatus struct { machineId string status params.Status statusInfo string } func (sms setMachineStatus) step(c *gc.C, ctx *context) { m, err := ctx.st.Machine(sms.machineId) c.Assert(err, gc.IsNil) err = m.SetStatus(sms.status, sms.statusInfo, nil) c.Assert(err, gc.IsNil) } type relateServices struct { ep1, ep2 string } func (rs relateServices) step(c *gc.C, ctx *context) { eps, err := ctx.st.InferEndpoints([]string{rs.ep1, rs.ep2}) c.Assert(err, gc.IsNil) _, err = ctx.st.AddRelation(eps...) c.Assert(err, gc.IsNil) } type addSubordinate struct { prinUnit string subService string } func (as addSubordinate) step(c *gc.C, ctx *context) { u, err := ctx.st.Unit(as.prinUnit) c.Assert(err, gc.IsNil) eps, err := ctx.st.InferEndpoints([]string{u.ServiceName(), as.subService}) c.Assert(err, gc.IsNil) rel, err := ctx.st.EndpointsRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(u) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) } type scopedExpect struct { what string scope []string output M } type expect struct { what string output M } func (e scopedExpect) step(c *gc.C, ctx *context) { c.Logf("expect: %s %s", e.what, strings.Join(e.scope, " ")) // Now execute the command for each format. for _, format := range statusFormats { c.Logf("format %q", format.name) // Run command with the required format. args := append([]string{"--format", format.name}, e.scope...) code, stdout, stderr := runStatus(c, args...) c.Assert(code, gc.Equals, 0) if !c.Check(stderr, gc.HasLen, 0) { c.Fatalf("status failed: %s", string(stderr)) } // Prepare the output in the same format. buf, err := format.marshal(e.output) c.Assert(err, gc.IsNil) expected := make(M) err = format.unmarshal(buf, &expected) c.Assert(err, gc.IsNil) // Check the output is as expected. actual := make(M) err = format.unmarshal(stdout, &actual) c.Assert(err, gc.IsNil) c.Assert(actual, jc.DeepEquals, expected) } } func (e expect) step(c *gc.C, ctx *context) { scopedExpect{e.what, nil, e.output}.step(c, ctx) } func (s *StatusSuite) TestStatusAllFormats(c *gc.C) { for i, t := range statusTests { c.Logf("test %d: %s", i, t.summary) func() { // Prepare context and run all steps to setup. ctx := s.newContext() defer s.resetContext(c, ctx) ctx.run(c, t.steps) }() } } func (s *StatusSuite) TestStatusFilterErrors(c *gc.C) { steps := []stepper{ addMachine{machineId: "0", job: state.JobManageEnviron}, addMachine{machineId: "1", job: state.JobHostUnits}, addCharm{"mysql"}, addService{name: "mysql", charm: "mysql"}, addAliveUnit{"mysql", "1"}, } ctx := s.newContext() defer s.resetContext(c, ctx) ctx.run(c, steps) // Status filters can only fail if the patterns are invalid. code, _, stderr := runStatus(c, "[*") c.Assert(code, gc.Not(gc.Equals), 0) c.Assert(string(stderr), gc.Equals, `error: pattern "[*" contains invalid characters`+"\n") code, _, stderr = runStatus(c, "//") c.Assert(code, gc.Not(gc.Equals), 0) c.Assert(string(stderr), gc.Equals, `error: pattern "//" contains too many '/' characters`+"\n") // Pattern validity is checked eagerly; if a bad pattern // proceeds a valid, matching pattern, then the bad pattern // will still cause an error. code, _, stderr = runStatus(c, "*", "[*") c.Assert(code, gc.Not(gc.Equals), 0) c.Assert(string(stderr), gc.Equals, `error: pattern "[*" contains invalid characters`+"\n") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/get_test.go�����������������������������������0000644�0000153�0000161�00000004612�12321735642�024555� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" ) type GetSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&GetSuite{}) var getTests = []struct { service string expected map[string]interface{} }{ { "dummy-service", map[string]interface{}{ "service": "dummy-service", "charm": "dummy", "settings": map[string]interface{}{ "title": map[string]interface{}{ "description": "A descriptive title used for the service.", "type": "string", "value": "Nearly There", }, "skill-level": map[string]interface{}{ "description": "A number indicating skill.", "type": "int", "default": true, }, "username": map[string]interface{}{ "description": "The name of the initial account (given admin permissions).", "type": "string", "value": "admin001", "default": true, }, "outlook": map[string]interface{}{ "description": "No default outlook.", "type": "string", "default": true, }, }, }, }, // TODO(dfc) add additional services (need more charms) // TODO(dfc) add set tests } func (s *GetSuite) TestGetConfig(c *gc.C) { sch := s.AddTestingCharm(c, "dummy") svc := s.AddTestingService(c, "dummy-service", sch) err := svc.UpdateConfigSettings(charm.Settings{"title": "Nearly There"}) c.Assert(err, gc.IsNil) for _, t := range getTests { ctx := coretesting.Context(c) code := cmd.Main(&GetCommand{}, ctx, []string{t.service}) c.Check(code, gc.Equals, 0) c.Assert(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "") // round trip via goyaml to avoid being sucked into a quagmire of // map[interface{}]interface{} vs map[string]interface{}. This is // also required if we add json support to this command. buf, err := goyaml.Marshal(t.expected) c.Assert(err, gc.IsNil) expected := make(map[string]interface{}) err = goyaml.Unmarshal(buf, &expected) c.Assert(err, gc.IsNil) actual := make(map[string]interface{}) err = goyaml.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &actual) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, expected) } } ����������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/main_test.go����������������������������������0000644�0000153�0000161�00000022423�12321735642�024722� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "flag" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strings" stdtesting "testing" "launchpad.net/gnuflag" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju/osenv" _ "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/version" ) func TestPackage(t *stdtesting.T) { testing.MgoTestPackage(t) } type MainSuite struct { testing.FakeHomeSuite } var _ = gc.Suite(&MainSuite{}) var ( flagRunMain = flag.Bool("run-main", false, "Run the application's main function for recursive testing") ) // Reentrancy point for testing (something as close as possible to) the juju // tool itself. func TestRunMain(t *stdtesting.T) { if *flagRunMain { Main(flag.Args()) } } func badrun(c *gc.C, exit int, args ...string) string { localArgs := append([]string{"-test.run", "TestRunMain", "-run-main", "--", "juju"}, args...) ps := exec.Command(os.Args[0], localArgs...) ps.Env = append(os.Environ(), osenv.JujuHomeEnvKey+"="+osenv.JujuHome()) output, err := ps.CombinedOutput() c.Logf("command output: %q", output) if exit != 0 { c.Assert(err, gc.ErrorMatches, fmt.Sprintf("exit status %d", exit)) } return string(output) } func helpText(command cmd.Command, name string) string { buff := &bytes.Buffer{} info := command.Info() info.Name = name f := gnuflag.NewFlagSet(info.Name, gnuflag.ContinueOnError) command.SetFlags(f) buff.Write(info.Help(f)) return buff.String() } func deployHelpText() string { return helpText(&DeployCommand{}, "juju deploy") } func syncToolsHelpText() string { return helpText(&SyncToolsCommand{}, "juju sync-tools") } func (s *MainSuite) TestRunMain(c *gc.C) { defer testing.MakeSampleHome(c).Restore() // The test array structure needs to be inline here as some of the // expected values below use deployHelpText(). This constructs the deploy // command and runs gets the help for it. When the deploy command is // setting the flags (which is needed for the help text) it is accessing // osenv.JujuHome(), which panics if SetJujuHome has not been called. // The FakeHome from testing does this. for i, t := range []struct { summary string args []string code int out string }{{ summary: "no params shows help", args: []string{}, code: 0, out: strings.TrimLeft(helpBasics, "\n"), }, { summary: "juju help is the same as juju", args: []string{"help"}, code: 0, out: strings.TrimLeft(helpBasics, "\n"), }, { summary: "juju --help works too", args: []string{"--help"}, code: 0, out: strings.TrimLeft(helpBasics, "\n"), }, { summary: "juju help basics is the same as juju", args: []string{"help", "basics"}, code: 0, out: strings.TrimLeft(helpBasics, "\n"), }, { summary: "juju help foo doesn't exist", args: []string{"help", "foo"}, code: 1, out: "ERROR unknown command or topic for foo\n", }, { summary: "juju help deploy shows the default help without global options", args: []string{"help", "deploy"}, code: 0, out: deployHelpText(), }, { summary: "juju --help deploy shows the same help as 'help deploy'", args: []string{"--help", "deploy"}, code: 0, out: deployHelpText(), }, { summary: "juju deploy --help shows the same help as 'help deploy'", args: []string{"deploy", "--help"}, code: 0, out: deployHelpText(), }, { summary: "unknown command", args: []string{"discombobulate"}, code: 1, out: "ERROR unrecognized command: juju discombobulate\n", }, { summary: "unknown option before command", args: []string{"--cheese", "bootstrap"}, code: 2, out: "error: flag provided but not defined: --cheese\n", }, { summary: "unknown option after command", args: []string{"bootstrap", "--cheese"}, code: 2, out: "error: flag provided but not defined: --cheese\n", }, { summary: "known option, but specified before command", args: []string{"--environment", "blah", "bootstrap"}, code: 2, out: "error: flag provided but not defined: --environment\n", }, { summary: "juju sync-tools registered properly", args: []string{"sync-tools", "--help"}, code: 0, out: syncToolsHelpText(), }, { summary: "check version command registered properly", args: []string{"version"}, code: 0, out: version.Current.String() + "\n", }, } { c.Logf("test %d: %s", i, t.summary) out := badrun(c, t.code, t.args...) c.Assert(out, gc.Equals, t.out) } } var brokenConfig = ` ' ` // breakJuju writes a dummy environment with incomplete configuration. // environMethod is called. func breakJuju(c *gc.C, environMethod string) (msg string) { path := osenv.JujuHomePath("environments.yaml") err := ioutil.WriteFile(path, []byte(brokenConfig), 0666) c.Assert(err, gc.IsNil) return `cannot parse "[^"]*": YAML error.*` } func (s *MainSuite) TestActualRunJujuArgsBeforeCommand(c *gc.C) { c.Skip("breaks test isolation: lp:1233601") defer testing.MakeFakeHomeNoEnvironments(c, "one").Restore() // Check global args work when specified before command msg := breakJuju(c, "Bootstrap") logpath := filepath.Join(c.MkDir(), "log") out := badrun(c, 1, "--log-file", logpath, "bootstrap") fullmsg := fmt.Sprintf(`(.|\n)*ERROR .*%s\n`, msg) c.Assert(out, gc.Matches, fullmsg) content, err := ioutil.ReadFile(logpath) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Matches, fullmsg) } func (s *MainSuite) TestActualRunJujuArgsAfterCommand(c *gc.C) { c.Skip("breaks test isolation: lp:1233601") defer testing.MakeFakeHomeNoEnvironments(c, "one").Restore() // Check global args work when specified after command msg := breakJuju(c, "Bootstrap") logpath := filepath.Join(c.MkDir(), "log") out := badrun(c, 1, "bootstrap", "--log-file", logpath) fullmsg := fmt.Sprintf(`(.|\n)*ERROR .*%s\n`, msg) c.Assert(out, gc.Matches, fullmsg) content, err := ioutil.ReadFile(logpath) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Matches, fullmsg) } var commandNames = []string{ "add-machine", "add-relation", "add-unit", "api-endpoints", "authorised-keys", "bootstrap", "debug-hooks", "debug-log", "deploy", "destroy-environment", "destroy-machine", "destroy-relation", "destroy-service", "destroy-unit", "env", // alias for switch "expose", "generate-config", // alias for init "get", "get-constraints", "get-env", // alias for get-environment "get-environment", "help", "help-tool", "init", "publish", "remove-machine", // alias for destroy-machine "remove-relation", // alias for destroy-relation "remove-service", // alias for destroy-service "remove-unit", // alias for destroy-unit "resolved", "retry-provisioning", "run", "scp", "set", "set-constraints", "set-env", // alias for set-environment "set-environment", "ssh", "stat", // alias for status "status", "switch", "sync-tools", "terminate-machine", // alias for destroy-machine "unexpose", "unset", "unset-env", // alias for unset-environment "unset-environment", "upgrade-charm", "upgrade-juju", "version", } func (s *MainSuite) TestHelpCommands(c *gc.C) { // Check that we have correctly registered all the commands // by checking the help output. defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir())) out := badrun(c, 0, "help", "commands") lines := strings.Split(out, "\n") var names []string for _, line := range lines { f := strings.Fields(line) if len(f) == 0 { continue } names = append(names, f[0]) } // The names should be output in alphabetical order, so don't sort. c.Assert(names, gc.DeepEquals, commandNames) } var topicNames = []string{ "azure-provider", "basics", "commands", "constraints", "ec2-provider", "global-options", "glossary", "hpcloud-provider", "local-provider", "logging", "openstack-provider", "plugins", "topics", } func (s *MainSuite) TestHelpTopics(c *gc.C) { // Check that we have correctly registered all the topics // by checking the help output. defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir())) out := badrun(c, 0, "help", "topics") lines := strings.Split(out, "\n") var names []string for _, line := range lines { f := strings.Fields(line) if len(f) == 0 { continue } names = append(names, f[0]) } // The names should be output in alphabetical order, so don't sort. c.Assert(names, gc.DeepEquals, topicNames) } var globalFlags = []string{ "--debug .*", "--description .*", "-h, --help .*", "--log-file .*", "--logging-config .*", "-q, --quiet .*", "--show-log .*", "-v, --verbose .*", } func (s *MainSuite) TestHelpGlobalOptions(c *gc.C) { // Check that we have correctly registered all the topics // by checking the help output. defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir())) out := badrun(c, 0, "help", "global-options") c.Assert(out, gc.Matches, `Global Options These options may be used with any command, and may appear in front of any command\.(.|\n)*`) lines := strings.Split(out, "\n") var flags []string for _, line := range lines { f := strings.Fields(line) if len(f) == 0 || line[0] != '-' { continue } flags = append(flags, line) } c.Assert(len(flags), gc.Equals, len(globalFlags)) for i, line := range flags { c.Assert(line, gc.Matches, globalFlags[i]) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/destroymachine_test.go������������������������0000644�0000153�0000161�00000010066�12321735642�027014� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/testing" ) type DestroyMachineSuite struct { jujutesting.RepoSuite } var _ = gc.Suite(&DestroyMachineSuite{}) func runDestroyMachine(c *gc.C, args ...string) error { _, err := testing.RunCommand(c, &DestroyMachineCommand{}, args) return err } func (s *DestroyMachineSuite) TestDestroyMachineWithUnit(c *gc.C) { // Create a machine running a unit. testing.Charms.BundlePath(s.SeriesPath, "riak") err := runDeploy(c, "local:riak", "riak") c.Assert(err, gc.IsNil) // Get the state entities to allow sane testing. u, err := s.State.Unit("riak/0") c.Assert(err, gc.IsNil) mid, err := u.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, "0") // Try to destroy the machine and fail. err = runDestroyMachine(c, "0") c.Assert(err, gc.ErrorMatches, `no machines were destroyed: machine 0 has unit "riak/0" assigned`) } func (s *DestroyMachineSuite) TestDestroyEmptyMachine(c *gc.C) { // Destroy an empty machine alongside a state server; only the empty machine // gets destroyed. m0, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = runDestroyMachine(c, "0", "1") c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 1 does not exist`) err = m0.Refresh() c.Assert(err, gc.IsNil) c.Assert(m0.Life(), gc.Equals, state.Dying) // Destroying a destroyed machine is a no-op. err = runDestroyMachine(c, "0") c.Assert(err, gc.IsNil) err = m0.Refresh() c.Assert(err, gc.IsNil) c.Assert(m0.Life(), gc.Equals, state.Dying) } func (s *DestroyMachineSuite) TestDestroyDeadMachine(c *gc.C) { // Destroying a Dead machine is a no-op; destroying it alongside a JobManageEnviron m0, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) // machine complains only about the JME machine. m1, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = m1.EnsureDead() c.Assert(err, gc.IsNil) err = runDestroyMachine(c, "0", "1") c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment`) err = m1.Refresh() c.Assert(err, gc.IsNil) c.Assert(m1.Life(), gc.Equals, state.Dead) err = m1.Refresh() c.Assert(err, gc.IsNil) c.Assert(m0.Life(), gc.Equals, state.Alive) } func (s *DestroyMachineSuite) TestForce(c *gc.C) { // Create a manager machine. m0, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) // Create a machine running a unit. testing.Charms.BundlePath(s.SeriesPath, "riak") err = runDeploy(c, "local:riak", "riak") c.Assert(err, gc.IsNil) // Get the state entities to allow sane testing. u, err := s.State.Unit("riak/0") c.Assert(err, gc.IsNil) m1, err := s.State.Machine("1") c.Assert(err, gc.IsNil) // Try to force-destroy the machines. err = runDestroyMachine(c, "0", "1", "--force") c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment`) // Clean up, check state. err = s.State.Cleanup() c.Assert(err, gc.IsNil) err = u.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = m1.Refresh() c.Assert(err, gc.IsNil) c.Assert(m1.Life(), gc.Equals, state.Dead) err = m0.Refresh() c.Assert(err, gc.IsNil) c.Assert(m0.Life(), gc.Equals, state.Alive) } func (s *DestroyMachineSuite) TestBadArgs(c *gc.C) { // Check invalid args. err := runDestroyMachine(c) c.Assert(err, gc.ErrorMatches, `no machines specified`) err = runDestroyMachine(c, "1", "2", "nonsense", "rubbish") c.Assert(err, gc.ErrorMatches, `invalid machine id "nonsense"`) } func (s *DestroyMachineSuite) TestEnvironmentArg(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = runDestroyMachine(c, "0", "-e", "dummyenv") c.Assert(err, gc.IsNil) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/authorisedkeys_test.go������������������������0000644�0000153�0000161�00000020041�12321735642�027033� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "strings" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/osenv" jujutesting "launchpad.net/juju-core/juju/testing" keymanagerserver "launchpad.net/juju-core/state/apiserver/keymanager" keymanagertesting "launchpad.net/juju-core/state/apiserver/keymanager/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" sshtesting "launchpad.net/juju-core/utils/ssh/testing" ) type AuthorisedKeysSuite struct { testbase.LoggingSuite jujuHome *coretesting.FakeHome } var _ = gc.Suite(&AuthorisedKeysSuite{}) var authKeysCommandNames = []string{ "add", "delete", "help", "import", "list", } func (s *AuthorisedKeysSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.jujuHome = coretesting.MakeEmptyFakeHome(c) } func (s *AuthorisedKeysSuite) TearDownTest(c *gc.C) { s.jujuHome.Restore() s.LoggingSuite.TearDownTest(c) } func (s *AuthorisedKeysSuite) TestHelpCommands(c *gc.C) { // Check that we have correctly registered all the sub commands // by checking the help output. out := badrun(c, 0, "authorised-keys", "--help") lines := strings.Split(out, "\n") var names []string subcommandsFound := false for _, line := range lines { f := strings.Fields(line) if len(f) == 1 && f[0] == "commands:" { subcommandsFound = true continue } if !subcommandsFound || len(f) == 0 || !strings.HasPrefix(line, " ") { continue } names = append(names, f[0]) } // The names should be output in alphabetical order, so don't sort. c.Assert(names, gc.DeepEquals, authKeysCommandNames) } func (s *AuthorisedKeysSuite) assertHelpOutput(c *gc.C, cmd, args string) { if args != "" { args = " " + args } expected := fmt.Sprintf("usage: juju authorised-keys %s [options]%s", cmd, args) out := badrun(c, 0, "authorised-keys", cmd, "--help") lines := strings.Split(out, "\n") c.Assert(lines[0], gc.Equals, expected) } func (s *AuthorisedKeysSuite) TestHelpList(c *gc.C) { s.assertHelpOutput(c, "list", "") } func (s *AuthorisedKeysSuite) TestHelpAdd(c *gc.C) { s.assertHelpOutput(c, "add", "<ssh key> [...]") } func (s *AuthorisedKeysSuite) TestHelpDelete(c *gc.C) { s.assertHelpOutput(c, "delete", "<ssh key id> [...]") } func (s *AuthorisedKeysSuite) TestHelpImport(c *gc.C) { s.assertHelpOutput(c, "import", "<ssh key id> [...]") } type keySuiteBase struct { jujutesting.JujuConnSuite } func (s *keySuiteBase) SetUpSuite(c *gc.C) { s.JujuConnSuite.SetUpSuite(c) s.PatchEnvironment(osenv.JujuEnvEnvKey, "dummyenv") } func (s *keySuiteBase) setAuthorisedKeys(c *gc.C, keys ...string) { keyString := strings.Join(keys, "\n") err := s.State.UpdateEnvironConfig(map[string]interface{}{"authorized-keys": keyString}, nil, nil) c.Assert(err, gc.IsNil) envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(envConfig.AuthorizedKeys(), gc.Equals, keyString) } func (s *keySuiteBase) assertEnvironKeys(c *gc.C, expected ...string) { envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) keys := envConfig.AuthorizedKeys() c.Assert(keys, gc.Equals, strings.Join(expected, "\n")) } type ListKeysSuite struct { keySuiteBase } var _ = gc.Suite(&ListKeysSuite{}) func (s *ListKeysSuite) TestListKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key + " another@host" s.setAuthorisedKeys(c, key1, key2) context, err := coretesting.RunCommand(c, &ListKeysCommand{}, []string{}) c.Assert(err, gc.IsNil) output := strings.TrimSpace(coretesting.Stdout(context)) c.Assert(err, gc.IsNil) c.Assert(output, gc.Matches, "Keys for user admin:\n.*\\(user@host\\)\n.*\\(another@host\\)") } func (s *ListKeysSuite) TestListFullKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key + " another@host" s.setAuthorisedKeys(c, key1, key2) context, err := coretesting.RunCommand(c, &ListKeysCommand{}, []string{"--full"}) c.Assert(err, gc.IsNil) output := strings.TrimSpace(coretesting.Stdout(context)) c.Assert(err, gc.IsNil) c.Assert(output, gc.Matches, "Keys for user admin:\n.*user@host\n.*another@host") } func (s *ListKeysSuite) TestListKeysNonDefaultUser(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key + " another@host" s.setAuthorisedKeys(c, key1, key2) _, err := s.State.AddUser("fred", "password") c.Assert(err, gc.IsNil) context, err := coretesting.RunCommand(c, &ListKeysCommand{}, []string{"--user", "fred"}) c.Assert(err, gc.IsNil) output := strings.TrimSpace(coretesting.Stdout(context)) c.Assert(err, gc.IsNil) c.Assert(output, gc.Matches, "Keys for user fred:\n.*\\(user@host\\)\n.*\\(another@host\\)") } func (s *ListKeysSuite) TestTooManyArgs(c *gc.C) { _, err := coretesting.RunCommand(c, &ListKeysCommand{}, []string{"foo"}) c.Assert(err, gc.ErrorMatches, `unrecognized args: \["foo"\]`) } type AddKeySuite struct { keySuiteBase } var _ = gc.Suite(&AddKeySuite{}) func (s *AddKeySuite) TestAddKey(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" s.setAuthorisedKeys(c, key1) key2 := sshtesting.ValidKeyTwo.Key + " another@host" context, err := coretesting.RunCommand(c, &AddKeysCommand{}, []string{key2, "invalid-key"}) c.Assert(err, gc.IsNil) c.Assert(coretesting.Stderr(context), gc.Matches, `cannot add key "invalid-key".*\n`) s.assertEnvironKeys(c, key1, key2) } func (s *AddKeySuite) TestAddKeyNonDefaultUser(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" s.setAuthorisedKeys(c, key1) _, err := s.State.AddUser("fred", "password") c.Assert(err, gc.IsNil) key2 := sshtesting.ValidKeyTwo.Key + " another@host" context, err := coretesting.RunCommand(c, &AddKeysCommand{}, []string{"--user", "fred", key2}) c.Assert(err, gc.IsNil) c.Assert(coretesting.Stderr(context), gc.Equals, "") s.assertEnvironKeys(c, key1, key2) } type DeleteKeySuite struct { keySuiteBase } var _ = gc.Suite(&DeleteKeySuite{}) func (s *DeleteKeySuite) TestDeleteKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key + " another@host" s.setAuthorisedKeys(c, key1, key2) context, err := coretesting.RunCommand( c, &DeleteKeysCommand{}, []string{sshtesting.ValidKeyTwo.Fingerprint, "invalid-key"}) c.Assert(err, gc.IsNil) c.Assert(coretesting.Stderr(context), gc.Matches, `cannot delete key id "invalid-key".*\n`) s.assertEnvironKeys(c, key1) } func (s *DeleteKeySuite) TestDeleteKeyNonDefaultUser(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key + " another@host" s.setAuthorisedKeys(c, key1, key2) _, err := s.State.AddUser("fred", "password") c.Assert(err, gc.IsNil) context, err := coretesting.RunCommand( c, &DeleteKeysCommand{}, []string{"--user", "fred", sshtesting.ValidKeyTwo.Fingerprint}) c.Assert(err, gc.IsNil) c.Assert(coretesting.Stderr(context), gc.Equals, "") s.assertEnvironKeys(c, key1) } type ImportKeySuite struct { keySuiteBase } var _ = gc.Suite(&ImportKeySuite{}) func (s *ImportKeySuite) SetUpTest(c *gc.C) { s.keySuiteBase.SetUpTest(c) s.PatchValue(&keymanagerserver.RunSSHImportId, keymanagertesting.FakeImport) } func (s *ImportKeySuite) TestImportKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" s.setAuthorisedKeys(c, key1) context, err := coretesting.RunCommand(c, &ImportKeysCommand{}, []string{"lp:validuser", "invalid-key"}) c.Assert(err, gc.IsNil) c.Assert(coretesting.Stderr(context), gc.Matches, `cannot import key id "invalid-key".*\n`) s.assertEnvironKeys(c, key1, sshtesting.ValidKeyThree.Key) } func (s *ImportKeySuite) TestImportKeyNonDefaultUser(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" s.setAuthorisedKeys(c, key1) _, err := s.State.AddUser("fred", "password") c.Assert(err, gc.IsNil) context, err := coretesting.RunCommand(c, &ImportKeysCommand{}, []string{"--user", "fred", "lp:validuser"}) c.Assert(err, gc.IsNil) c.Assert(coretesting.Stderr(context), gc.Equals, "") s.assertEnvironKeys(c, key1, sshtesting.ValidKeyThree.Key) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/upgradejuju_test.go���������������������������0000644�0000153�0000161�00000035637�12321735642�026336� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "archive/tar" "bytes" "compress/gzip" "io" "io/ioutil" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/sync" envtesting "launchpad.net/juju-core/environs/testing" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/version" ) type UpgradeJujuSuite struct { testing.JujuConnSuite toolsDir string } var _ = gc.Suite(&UpgradeJujuSuite{}) var upgradeJujuTests = []struct { about string tools []string currentVersion string agentVersion string args []string expectInitErr string expectErr string expectVersion string expectUploaded []string }{{ about: "unwanted extra argument", currentVersion: "1.0.0-quantal-amd64", args: []string{"foo"}, expectInitErr: "unrecognized args:.*", }, { about: "removed arg --dev specified", currentVersion: "1.0.0-quantal-amd64", args: []string{"--dev"}, expectInitErr: "flag provided but not defined: --dev", }, { about: "invalid --version value", currentVersion: "1.0.0-quantal-amd64", args: []string{"--version", "invalid-version"}, expectInitErr: "invalid version .*", }, { about: "just major version, no minor specified", currentVersion: "4.2.0-quantal-amd64", args: []string{"--version", "4"}, expectInitErr: `invalid version "4"`, }, { about: "major version upgrade to incompatible version", currentVersion: "2.0.0-quantal-amd64", args: []string{"--version", "5.2.0"}, expectInitErr: "cannot upgrade to version incompatible with CLI", }, { about: "major version downgrade to incompatible version", currentVersion: "4.2.0-quantal-amd64", args: []string{"--version", "3.2.0"}, expectInitErr: "cannot upgrade to version incompatible with CLI", }, { about: "invalid --series", currentVersion: "4.2.0-quantal-amd64", args: []string{"--series", "precise&quantal"}, expectInitErr: `invalid value "precise&quantal" for flag --series: .*`, }, { about: "--series without --upload-tools", currentVersion: "4.2.0-quantal-amd64", args: []string{"--series", "precise,quantal"}, expectInitErr: "--series requires --upload-tools", }, { about: "--upload-tools with inappropriate version 1", currentVersion: "4.2.0-quantal-amd64", args: []string{"--upload-tools", "--version", "3.1.0"}, expectInitErr: "cannot upgrade to version incompatible with CLI", }, { about: "--upload-tools with inappropriate version 2", currentVersion: "3.2.7-quantal-amd64", args: []string{"--upload-tools", "--version", "3.2.8.4"}, expectInitErr: "cannot specify build number when uploading tools", }, { about: "latest supported stable release", tools: []string{"2.2.0-quantal-amd64", "2.2.2-quantal-i386", "2.2.3-quantal-amd64"}, currentVersion: "2.0.0-quantal-amd64", agentVersion: "2.0.0", expectVersion: "2.2.3", }, { about: "latest current release", tools: []string{"2.0.5-quantal-amd64", "2.0.1-quantal-i386", "2.3.3-quantal-amd64"}, currentVersion: "2.0.0-quantal-amd64", agentVersion: "2.0.0", expectVersion: "2.0.5", }, { about: "latest current release matching CLI, major version", tools: []string{"3.2.0-quantal-amd64"}, currentVersion: "3.2.0-quantal-amd64", agentVersion: "2.8.2", expectVersion: "3.2.0", }, { about: "latest current release matching CLI, major version, no matching major tools", tools: []string{"2.8.2-quantal-amd64"}, currentVersion: "3.2.0-quantal-amd64", agentVersion: "2.8.2", expectErr: "no matching tools available", }, { about: "latest current release matching CLI, major version, no matching tools", tools: []string{"3.3.0-quantal-amd64"}, currentVersion: "3.2.0-quantal-amd64", agentVersion: "2.8.2", expectErr: "no compatible tools available", }, { about: "no next supported available", tools: []string{"2.1.0-quantal-amd64", "2.1.5-quantal-i386", "2.3.3-quantal-amd64"}, currentVersion: "2.0.0-quantal-amd64", agentVersion: "2.0.0", expectErr: "no more recent supported versions available", }, { about: "latest supported stable, when client is dev", tools: []string{"2.1.1-quantal-amd64", "2.2.0-quantal-amd64", "2.3.0-quantal-amd64", "3.0.1-quantal-amd64"}, currentVersion: "2.1.0-quantal-amd64", agentVersion: "2.0.0", expectVersion: "2.2.0", }, { about: "latest current, when agent is dev", tools: []string{"2.1.1-quantal-amd64", "2.2.0-quantal-amd64", "2.3.0-quantal-amd64", "3.0.1-quantal-amd64"}, currentVersion: "2.0.0-quantal-amd64", agentVersion: "2.1.0", expectVersion: "2.2.0", }, { about: "specified version", tools: []string{"2.3.0-quantal-amd64"}, currentVersion: "2.0.0-quantal-amd64", agentVersion: "2.0.0", args: []string{"--version", "2.3.0"}, expectVersion: "2.3.0", }, { about: "specified major version", tools: []string{"3.2.0-quantal-amd64"}, currentVersion: "3.2.0-quantal-amd64", agentVersion: "2.8.2", args: []string{"--version", "3.2.0"}, expectVersion: "3.2.0", }, { about: "specified version missing, but already set", currentVersion: "3.0.0-quantal-amd64", agentVersion: "3.0.0", args: []string{"--version", "3.0.0"}, expectVersion: "3.0.0", }, { about: "specified version, no tools", currentVersion: "3.0.0-quantal-amd64", agentVersion: "3.0.0", args: []string{"--version", "3.2.0"}, expectErr: "no tools available", }, { about: "specified version, no matching major version", tools: []string{"4.2.0-quantal-amd64"}, currentVersion: "3.0.0-quantal-amd64", agentVersion: "3.0.0", args: []string{"--version", "3.2.0"}, expectErr: "no matching tools available", }, { about: "specified version, no matching minor version", tools: []string{"3.4.0-quantal-amd64"}, currentVersion: "3.0.0-quantal-amd64", agentVersion: "3.0.0", args: []string{"--version", "3.2.0"}, expectErr: "no matching tools available", }, { about: "specified version, no matching patch version", tools: []string{"3.2.5-quantal-amd64"}, currentVersion: "3.0.0-quantal-amd64", agentVersion: "3.0.0", args: []string{"--version", "3.2.0"}, expectErr: "no matching tools available", }, { about: "specified version, no matching build version", tools: []string{"3.2.0.2-quantal-amd64"}, currentVersion: "3.0.0-quantal-amd64", agentVersion: "3.0.0", args: []string{"--version", "3.2.0"}, expectErr: "no matching tools available", }, { about: "major version downgrade to incompatible version", tools: []string{"3.2.0-quantal-amd64"}, currentVersion: "3.2.0-quantal-amd64", agentVersion: "4.2.0", args: []string{"--version", "3.2.0"}, expectErr: "cannot change version from 4.2.0 to 3.2.0", }, { about: "minor version downgrade to incompatible version", tools: []string{"3.2.0-quantal-amd64"}, currentVersion: "3.2.0-quantal-amd64", agentVersion: "3.3.0", args: []string{"--version", "3.2.0"}, expectErr: "cannot change version from 3.3.0 to 3.2.0", }, { about: "nothing available", currentVersion: "2.0.0-quantal-amd64", agentVersion: "2.0.0", expectVersion: "2.0.0", }, { about: "nothing available 2", currentVersion: "2.0.0-quantal-amd64", tools: []string{"3.2.0-quantal-amd64"}, agentVersion: "2.0.0", expectVersion: "2.0.0", }, { about: "upload with default series", currentVersion: "2.2.0-quantal-amd64", agentVersion: "2.0.0", args: []string{"--upload-tools"}, expectVersion: "2.2.0.1", expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-%LTS%-amd64", "2.2.0.1-raring-amd64"}, }, { about: "upload with explicit version", currentVersion: "2.2.0-quantal-amd64", agentVersion: "2.0.0", args: []string{"--upload-tools", "--version", "2.7.3"}, expectVersion: "2.7.3.1", expectUploaded: []string{"2.7.3.1-quantal-amd64", "2.7.3.1-%LTS%-amd64", "2.7.3.1-raring-amd64"}, }, { about: "upload with explicit series", currentVersion: "2.2.0-quantal-amd64", agentVersion: "2.0.0", args: []string{"--upload-tools", "--series", "raring"}, expectVersion: "2.2.0.1", expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-raring-amd64"}, }, { about: "upload dev version, currently on release version", currentVersion: "2.1.0-quantal-amd64", agentVersion: "2.0.0", args: []string{"--upload-tools"}, expectVersion: "2.1.0.1", expectUploaded: []string{"2.1.0.1-quantal-amd64", "2.1.0.1-%LTS%-amd64", "2.1.0.1-raring-amd64"}, }, { about: "upload bumps version when necessary", tools: []string{"2.4.6-quantal-amd64", "2.4.8-quantal-amd64"}, currentVersion: "2.4.6-quantal-amd64", agentVersion: "2.4.0", args: []string{"--upload-tools"}, expectVersion: "2.4.6.1", expectUploaded: []string{"2.4.6.1-quantal-amd64", "2.4.6.1-%LTS%-amd64", "2.4.6.1-raring-amd64"}, }, { about: "upload re-bumps version when necessary", tools: []string{"2.4.6-quantal-amd64", "2.4.6.2-saucy-i386", "2.4.8-quantal-amd64"}, currentVersion: "2.4.6-quantal-amd64", agentVersion: "2.4.6.2", args: []string{"--upload-tools"}, expectVersion: "2.4.6.3", expectUploaded: []string{"2.4.6.3-quantal-amd64", "2.4.6.3-%LTS%-amd64", "2.4.6.3-raring-amd64"}, }, { about: "upload with explicit version bumps when necessary", currentVersion: "2.2.0-quantal-amd64", tools: []string{"2.7.3.1-quantal-amd64"}, agentVersion: "2.0.0", args: []string{"--upload-tools", "--version", "2.7.3"}, expectVersion: "2.7.3.2", expectUploaded: []string{"2.7.3.2-quantal-amd64", "2.7.3.2-%LTS%-amd64", "2.7.3.2-raring-amd64"}, }} // getMockBuildTools returns a sync.BuildToolsTarballFunc implementation which generates // a fake tools tarball. func (s *UpgradeJujuSuite) getMockBuildTools(c *gc.C) sync.BuildToolsTarballFunc { return func(forceVersion *version.Number) (*sync.BuiltTools, error) { // UploadFakeToolsVersions requires a storage to write to. stor, err := filestorage.NewFileStorageWriter(s.toolsDir) c.Assert(err, gc.IsNil) vers := version.Current if forceVersion != nil { vers.Number = *forceVersion } versions := []version.Binary{vers} uploadedTools, err := envtesting.UploadFakeToolsVersions(stor, versions...) c.Assert(err, gc.IsNil) agentTools := uploadedTools[0] return &sync.BuiltTools{ Dir: s.toolsDir, StorageName: envtools.StorageName(vers), Version: vers, Size: agentTools.Size, Sha256Hash: agentTools.SHA256, }, nil } } func (s *UpgradeJujuSuite) TestUpgradeJuju(c *gc.C) { s.PatchValue(&sync.BuildToolsTarball, s.getMockBuildTools(c)) oldVersion := version.Current defer func() { version.Current = oldVersion }() for i, test := range upgradeJujuTests { c.Logf("\ntest %d: %s", i, test.about) s.Reset(c) // Set up apparent CLI version and initialize the command. version.Current = version.MustParseBinary(test.currentVersion) com := &UpgradeJujuCommand{} if err := coretesting.InitCommand(com, test.args); err != nil { if test.expectInitErr != "" { c.Check(err, gc.ErrorMatches, test.expectInitErr) } else { c.Check(err, gc.IsNil) } continue } // Set up state and environ, and run the command. toolsDir := c.MkDir() updateAttrs := map[string]interface{}{ "agent-version": test.agentVersion, "tools-metadata-url": "file://" + toolsDir, } err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil) c.Assert(err, gc.IsNil) versions := make([]version.Binary, len(test.tools)) for i, v := range test.tools { versions[i] = version.MustParseBinary(v) } if len(versions) > 0 { envtesting.MustUploadFakeToolsVersions(s.Conn.Environ.Storage(), versions...) stor, err := filestorage.NewFileStorageWriter(toolsDir) c.Assert(err, gc.IsNil) envtesting.MustUploadFakeToolsVersions(stor, versions...) } err = com.Run(coretesting.Context(c)) if test.expectErr != "" { c.Check(err, gc.ErrorMatches, test.expectErr) continue } else if !c.Check(err, gc.IsNil) { continue } // Check expected changes to environ/state. cfg, err := s.State.EnvironConfig() c.Check(err, gc.IsNil) agentVersion, ok := cfg.AgentVersion() c.Check(ok, gc.Equals, true) c.Check(agentVersion, gc.Equals, version.MustParse(test.expectVersion)) for _, uploaded := range test.expectUploaded { // Substitute latest LTS for placeholder in expected series for uploaded tools uploaded = strings.Replace(uploaded, "%LTS%", config.LatestLtsSeries(), 1) vers := version.MustParseBinary(uploaded) r, err := storage.Get(s.Conn.Environ.Storage(), envtools.StorageName(vers)) if !c.Check(err, gc.IsNil) { continue } data, err := ioutil.ReadAll(r) r.Close() c.Check(err, gc.IsNil) expectContent := version.Current expectContent.Number = agentVersion checkToolsContent(c, data, "jujud contents "+expectContent.String()) } } } func checkToolsContent(c *gc.C, data []byte, uploaded string) { zr, err := gzip.NewReader(bytes.NewReader(data)) c.Check(err, gc.IsNil) defer zr.Close() tr := tar.NewReader(zr) found := false for { hdr, err := tr.Next() if err == io.EOF { break } c.Check(err, gc.IsNil) if strings.ContainsAny(hdr.Name, "/\\") { c.Fail() } if hdr.Typeflag != tar.TypeReg { c.Fail() } content, err := ioutil.ReadAll(tr) c.Check(err, gc.IsNil) c.Check(string(content), gc.Equals, uploaded) found = true } c.Check(found, jc.IsTrue) } // JujuConnSuite very helpfully uploads some default // tools to the environment's storage. We don't want // 'em there; but we do want a consistent default-series // in the environment state. func (s *UpgradeJujuSuite) Reset(c *gc.C) { s.JujuConnSuite.Reset(c) envtesting.RemoveTools(c, s.Conn.Environ.Storage()) updateAttrs := map[string]interface{}{ "default-series": "raring", "agent-version": "1.2.3", } err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil) c.Assert(err, gc.IsNil) s.toolsDir = c.MkDir() } func (s *UpgradeJujuSuite) TestUpgradeJujuWithRealUpload(c *gc.C) { s.Reset(c) _, err := coretesting.RunCommand(c, &UpgradeJujuCommand{}, []string{"--upload-tools"}) c.Assert(err, gc.IsNil) vers := version.Current vers.Build = 1 tools, err := envtools.FindInstanceTools(s.Conn.Environ, vers.Number, vers.Series, &vers.Arch) c.Assert(err, gc.IsNil) c.Assert(len(tools), gc.Equals, 1) } �������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/juju/addunit.go������������������������������������0000644�0000153�0000161�00000005652�12321735776�024404� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" ) // UnitCommandBase provides support for commands which deploy units. It handles the parsing // and validation of --to and --num-units arguments. type UnitCommandBase struct { ToMachineSpec string NumUnits int } func (c *UnitCommandBase) SetFlags(f *gnuflag.FlagSet) { f.IntVar(&c.NumUnits, "num-units", 1, "") f.StringVar(&c.ToMachineSpec, "to", "", "the machine or container to deploy the unit in, bypasses constraints") } func (c *UnitCommandBase) Init(args []string) error { if c.NumUnits < 1 { return errors.New("--num-units must be a positive integer") } if c.ToMachineSpec != "" { if c.NumUnits > 1 { return errors.New("cannot use --num-units > 1 with --to") } if !cmd.IsMachineOrNewContainer(c.ToMachineSpec) { return fmt.Errorf("invalid --to parameter %q", c.ToMachineSpec) } } return nil } // AddUnitCommand is responsible adding additional units to a service. type AddUnitCommand struct { cmd.EnvCommandBase UnitCommandBase ServiceName string } const addUnitDoc = ` Adding units to an existing service is a way to scale out an environment by deploying more instances of a service. Add-unit must be called on services that have already been deployed via juju deploy. By default, services are deployed to newly provisioned machines. Alternatively, service units can be added to a specific existing machine using the --to argument. Examples: juju add-unit mysql -n 5 (Add 5 mysql units on 5 new machines) juju add-unit mysql --to 23 (Add a mysql unit to machine 23) juju add-unit mysql --to 24/lxc/3 (Add unit to lxc container 3 on host machine 24) juju add-unit mysql --to lxc:25 (Add unit to a new lxc container on host machine 25) ` func (c *AddUnitCommand) Info() *cmd.Info { return &cmd.Info{ Name: "add-unit", Args: "<service name>", Purpose: "add one or more units of an already-deployed service", Doc: addUnitDoc, } } func (c *AddUnitCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) c.UnitCommandBase.SetFlags(f) f.IntVar(&c.NumUnits, "n", 1, "number of service units to add") } func (c *AddUnitCommand) Init(args []string) error { switch len(args) { case 1: c.ServiceName = args[0] case 0: return errors.New("no service specified") } if err := cmd.CheckEmpty(args[1:]); err != nil { return err } return c.UnitCommandBase.Init(args) } // Run connects to the environment specified on the command line // and calls AddServiceUnits for the given service. func (c *AddUnitCommand) Run(_ *cmd.Context) error { apiclient, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer apiclient.Close() _, err = apiclient.AddServiceUnits(c.ServiceName, c.NumUnits, c.ToMachineSpec) return err } ��������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/output_test.go�������������������������������������0000644�0000153�0000161�00000007131�12321735642�024360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd_test import ( "launchpad.net/gnuflag" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" ) // OutputCommand is a command that uses the output.go formatters. type OutputCommand struct { cmd.CommandBase out cmd.Output value interface{} } func (c *OutputCommand) Info() *cmd.Info { return &cmd.Info{ Name: "output", Args: "<something>", Purpose: "I like to output", Doc: "output", } } func (c *OutputCommand) SetFlags(f *gnuflag.FlagSet) { c.out.AddFlags(f, "smart", cmd.DefaultFormatters) } func (c *OutputCommand) Init(args []string) error { return cmd.CheckEmpty(args) } func (c *OutputCommand) Run(ctx *cmd.Context) error { return c.out.Write(ctx, c.value) } // use a struct to control field ordering. var defaultValue = struct { Juju int Puppet bool }{1, false} var outputTests = map[string][]struct { value interface{} output string }{ "": { {nil, ""}, {"", ""}, {1, "1\n"}, {-1, "-1\n"}, {1.1, "1.1\n"}, {true, "True\n"}, {false, "False\n"}, {"hello", "hello\n"}, {"\n\n\n", "\n\n\n\n"}, {"foo: bar", "foo: bar\n"}, {[]string{"blam", "dink"}, "blam\ndink\n"}, {map[interface{}]interface{}{"foo": "bar"}, "foo: bar\n"}, }, "smart": { {nil, ""}, {"", ""}, {1, "1\n"}, {-1, "-1\n"}, {1.1, "1.1\n"}, {true, "True\n"}, {false, "False\n"}, {"hello", "hello\n"}, {"\n\n\n", "\n\n\n\n"}, {"foo: bar", "foo: bar\n"}, {[]string{"blam", "dink"}, "blam\ndink\n"}, {map[interface{}]interface{}{"foo": "bar"}, "foo: bar\n"}, }, "json": { {nil, "null\n"}, {"", `""` + "\n"}, {1, "1\n"}, {-1, "-1\n"}, {1.1, "1.1\n"}, {true, "true\n"}, {false, "false\n"}, {"hello", `"hello"` + "\n"}, {"\n\n\n", `"\n\n\n"` + "\n"}, {"foo: bar", `"foo: bar"` + "\n"}, {[]string{}, `[]` + "\n"}, {[]string{"blam", "dink"}, `["blam","dink"]` + "\n"}, {defaultValue, `{"Juju":1,"Puppet":false}` + "\n"}, }, "yaml": { {nil, ""}, {"", `""` + "\n"}, {1, "1\n"}, {-1, "-1\n"}, {1.1, "1.1\n"}, {true, "true\n"}, {false, "false\n"}, {"hello", "hello\n"}, {"\n\n\n", "'\n\n\n\n'\n"}, {"foo: bar", "'foo: bar'\n"}, {[]string{"blam", "dink"}, "- blam\n- dink\n"}, {defaultValue, "juju: 1\npuppet: false\n"}, }, } func (s *CmdSuite) TestOutputFormat(c *gc.C) { for format, tests := range outputTests { c.Logf("format %s", format) var args []string if format != "" { args = []string{"--format", format} } for i, t := range tests { c.Logf(" test %d", i) ctx := testing.Context(c) result := cmd.Main(&OutputCommand{value: t.value}, ctx, args) c.Check(result, gc.Equals, 0) c.Check(bufferString(ctx.Stdout), gc.Equals, t.output) c.Check(bufferString(ctx.Stderr), gc.Equals, "") } } } func (s *CmdSuite) TestUnknownOutputFormat(c *gc.C) { ctx := testing.Context(c) result := cmd.Main(&OutputCommand{}, ctx, []string{"--format", "cuneiform"}) c.Check(result, gc.Equals, 2) c.Check(bufferString(ctx.Stdout), gc.Equals, "") c.Check(bufferString(ctx.Stderr), gc.Matches, ".*: unknown format \"cuneiform\"\n") } // Py juju allowed both --format json and --format=json. This test verifies that juju is // being built against a version of the gnuflag library (rev 14 or above) that supports // this argument format. // LP #1059921 func (s *CmdSuite) TestFormatAlternativeSyntax(c *gc.C) { ctx := testing.Context(c) result := cmd.Main(&OutputCommand{}, ctx, []string{"--format=json"}) c.Assert(result, gc.Equals, 0) c.Assert(bufferString(ctx.Stdout), gc.Equals, "null\n") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/version_test.go������������������������������������0000644�0000153�0000161�00000002434�12321735642�024506� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd import ( "bytes" "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/version" ) type VersionSuite struct{} var _ = gc.Suite(&VersionSuite{}) func (s *VersionSuite) TestVersion(c *gc.C) { var stdout, stderr bytes.Buffer ctx := &Context{ Stdout: &stdout, Stderr: &stderr, } code := Main(&VersionCommand{}, ctx, nil) c.Check(code, gc.Equals, 0) c.Assert(stderr.String(), gc.Equals, "") c.Assert(stdout.String(), gc.Equals, version.Current.String()+"\n") } func (s *VersionSuite) TestVersionExtraArgs(c *gc.C) { var stdout, stderr bytes.Buffer ctx := &Context{ Stdout: &stdout, Stderr: &stderr, } code := Main(&VersionCommand{}, ctx, []string{"foo"}) c.Check(code, gc.Equals, 2) c.Assert(stdout.String(), gc.Equals, "") c.Assert(stderr.String(), gc.Matches, "error: unrecognized args.*\n") } func (s *VersionSuite) TestVersionJson(c *gc.C) { var stdout, stderr bytes.Buffer ctx := &Context{ Stdout: &stdout, Stderr: &stderr, } code := Main(&VersionCommand{}, ctx, []string{"--format", "json"}) c.Check(code, gc.Equals, 0) c.Assert(stderr.String(), gc.Equals, "") c.Assert(stdout.String(), gc.Equals, fmt.Sprintf("%q\n", version.Current.String())) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/environmentcommand.go������������������������������0000644�0000153�0000161�00000004233�12321735776�025674� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd import ( "fmt" "io/ioutil" "os" "path/filepath" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/juju/osenv" ) const CurrentEnvironmentFilename = "current-environment" // The purpose of EnvCommandBase is to provide a default member and flag // setting for commands that deal across different environments. type EnvCommandBase struct { CommandBase EnvName string } func getCurrentEnvironmentFilePath() string { return filepath.Join(osenv.JujuHome(), CurrentEnvironmentFilename) } // Read the file $JUJU_HOME/current-environment and return the value stored // there. If the file doesn't exist, or there is a problem reading the file, // an empty string is returned. func ReadCurrentEnvironment() string { current, err := ioutil.ReadFile(getCurrentEnvironmentFilePath()) // The file not being there, or not readable isn't really an error for us // here. We treat it as "can't tell, so you get the default". if err != nil { return "" } return strings.TrimSpace(string(current)) } // Write the envName to the file $JUJU_HOME/current-environment file. func WriteCurrentEnvironment(envName string) error { path := getCurrentEnvironmentFilePath() err := ioutil.WriteFile(path, []byte(envName+"\n"), 0644) if err != nil { return fmt.Errorf("unable to write to the environment file: %q, %s", path, err) } return nil } // There is simple ordering for the default environment. Firstly check the // JUJU_ENV environment variable. If that is set, it gets used. If it isn't // set, look in the $JUJU_HOME/current-environment file. func getDefaultEnvironment() string { defaultEnv := os.Getenv(osenv.JujuEnvEnvKey) if defaultEnv != "" { return defaultEnv } return ReadCurrentEnvironment() } func (c *EnvCommandBase) SetFlags(f *gnuflag.FlagSet) { defaultEnv := getDefaultEnvironment() f.StringVar(&c.EnvName, "e", defaultEnv, "juju environment to operate in") f.StringVar(&c.EnvName, "environment", defaultEnv, "") } // EnvironName returns the name of the environment for this command func (c *EnvCommandBase) EnvironName() string { return c.EnvName } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charm-admin/���������������������������������������0000755�0000153�0000161�00000000000�12321735642�023610� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charm-admin/deletecharm.go�������������������������0000644�0000153�0000161�00000002652�12321735642�026421� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/store" ) type DeleteCharmCommand struct { ConfigCommand Url string } func (c *DeleteCharmCommand) Info() *cmd.Info { return &cmd.Info{ Name: "delete-charm", Purpose: "delete a published charm from the charm store", } } func (c *DeleteCharmCommand) SetFlags(f *gnuflag.FlagSet) { c.ConfigCommand.SetFlags(f) f.StringVar(&c.Url, "url", "", "charm URL") } func (c *DeleteCharmCommand) Init(args []string) error { // Check flags err := c.ConfigCommand.Init(args) if err != nil { return err } if c.Url == "" { return fmt.Errorf("--url is required") } return nil } func (c *DeleteCharmCommand) Run(ctx *cmd.Context) error { // Read config err := c.ConfigCommand.ReadConfig(ctx) if err != nil { return err } // Parse the charm URL charmUrl, err := charm.ParseURL(c.Url) if err != nil { return err } // Open the charm store storage s, err := store.Open(c.Config.MongoURL) if err != nil { return err } defer s.Close() // Delete the charm by URL _, err = s.DeleteCharm(charmUrl) if err != nil { return err } fmt.Fprintln(ctx.Stdout, "Charm", charmUrl, "deleted.") return nil } func (c *DeleteCharmCommand) AllowInterspersedFlags() bool { return true } ��������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charm-admin/config_test.go�������������������������0000644�0000153�0000161�00000002634�12321735642�026450� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "path" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type ConfigSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&ConfigSuite{}) const testConfig = ` mongo-url: localhost:23456 foo: 1 bar: false ` func (s *ConfigSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) } func (s *ConfigSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) } type SomeConfigCommand struct { ConfigCommand } func (c *SomeConfigCommand) Info() *cmd.Info { return &cmd.Info{ Name: "some-cmd", Purpose: "something in particular that requires configuration", } } func (c *SomeConfigCommand) Run(ctx *cmd.Context) error { return c.ReadConfig(ctx) } func (s *ConfigSuite) TestReadConfig(c *gc.C) { confDir := c.MkDir() f, err := os.Create(path.Join(confDir, "charmd.conf")) c.Assert(err, gc.IsNil) cfgPath := f.Name() { defer f.Close() fmt.Fprint(f, testConfig) } config := &SomeConfigCommand{} args := []string{"--config", cfgPath} err = testing.InitCommand(config, args) c.Assert(err, gc.IsNil) _, err = testing.RunCommand(c, config, args) c.Assert(err, gc.IsNil) c.Assert(config.Config, gc.NotNil) c.Assert(config.Config.MongoURL, gc.Equals, "localhost:23456") } ����������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charm-admin/config.go������������������������������0000644�0000153�0000161�00000001537�12321735642�025412� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/store" ) // ConfigCommand defines a command which requires a YAML config file. type ConfigCommand struct { cmd.CommandBase ConfigPath string Config *store.Config } type CharmdConfig struct { MongoUrl string `yaml:"mongo-url"` } func (c *ConfigCommand) SetFlags(f *gnuflag.FlagSet) { f.StringVar(&c.ConfigPath, "config", "", "charmd configuration file") } func (c *ConfigCommand) Init(args []string) error { if c.ConfigPath == "" { return fmt.Errorf("--config is required") } return nil } func (c *ConfigCommand) ReadConfig(ctx *cmd.Context) (err error) { c.Config, err = store.ReadConfig(ctx.AbsPath(c.ConfigPath)) return err } �����������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charm-admin/suite_test.go��������������������������0000644�0000153�0000161�00000000374�12321735642�026333� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func Test(t *stdtesting.T) { testing.MgoTestPackageSsl(t, false) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charm-admin/deletecharm_test.go��������������������0000644�0000153�0000161�00000004322�12321735642�027454� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "path" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/store" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type DeleteCharmSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&DeleteCharmSuite{}) const testDeleteCharm = ` mongo-url: localhost:23456 ` func (s *DeleteCharmSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) } func (s *DeleteCharmSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) } func (s *DeleteCharmSuite) TestInit(c *gc.C) { config := &DeleteCharmCommand{} err := testing.InitCommand(config, []string{"--config", "/etc/charmd.conf", "--url", "cs:go"}) c.Assert(err, gc.IsNil) c.Assert(config.ConfigPath, gc.Equals, "/etc/charmd.conf") c.Assert(config.Url, gc.Equals, "cs:go") } func (s *DeleteCharmSuite) TestRun(c *gc.C) { // Derive config file from test mongo port confDir := c.MkDir() f, err := os.Create(path.Join(confDir, "charmd.conf")) c.Assert(err, gc.IsNil) configPath := f.Name() { defer f.Close() fmt.Fprintf(f, "mongo-url: %s\n", testing.MgoServer.Addr()) } // Delete charm that does not exist, not found error. config := &DeleteCharmCommand{} out, err := testing.RunCommand(c, config, []string{"--config", configPath, "--url", "cs:unreleased/foo"}) fmt.Println(out) c.Assert(err, gc.NotNil) // Publish that charm now url := charm.MustParseURL("cs:unreleased/foo") { s, err := store.Open(testing.MgoServer.Addr()) defer s.Close() c.Assert(err, gc.IsNil) pub, err := s.CharmPublisher([]*charm.URL{url}, "such-digest-much-unique") c.Assert(err, gc.IsNil) err = pub.Publish(testing.Charms.ClonedDir(c.MkDir(), "dummy")) c.Assert(err, gc.IsNil) } // Delete charm, should now succeed _, err = testing.RunCommand(c, config, []string{"--config", configPath, "--url", "cs:unreleased/foo"}) c.Assert(err, gc.IsNil) c.Assert(config.Config, gc.NotNil) // Confirm that the charm is gone { s, err := store.Open(testing.MgoServer.Addr()) defer s.Close() c.Assert(err, gc.IsNil) _, err = s.CharmInfo(url) c.Assert(err, gc.NotNil) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charm-admin/main.go��������������������������������0000644�0000153�0000161�00000000751�12321735642�025066� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "launchpad.net/juju-core/cmd" ) func main() { ctx, err := cmd.DefaultContext() if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(2) } admcmd := cmd.NewSuperCommand(cmd.SuperCommandParams{ Name: "charm-admin", Log: &cmd.Log{}, }) admcmd.Register(&DeleteCharmCommand{}) os.Exit(cmd.Main(admcmd, ctx, os.Args[1:])) } �����������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/cmd.go���������������������������������������������0000644�0000153�0000161�00000017207�12321735776�022541� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd import ( "bytes" "errors" "fmt" "io" "io/ioutil" "os" "os/signal" "path/filepath" "strings" "launchpad.net/gnuflag" ) type rcPassthroughError struct { code int } func (e *rcPassthroughError) Error() string { return fmt.Sprintf("rc: %v", e.code) } func IsRcPassthroughError(err error) bool { _, ok := err.(*rcPassthroughError) return ok } // NewRcPassthroughError creates an error that will have the code used at the // return code from the cmd.Main function rather than the default of 1 if // there is an error. func NewRcPassthroughError(code int) error { return &rcPassthroughError{code} } // ErrSilent can be returned from Run to signal that Main should exit with // code 1 without producing error output. var ErrSilent = errors.New("cmd: error out silently") // Command is implemented by types that interpret command-line arguments. type Command interface { // IsSuperCommand returns true if the command is a super command. IsSuperCommand() bool // Info returns information about the Command. Info() *Info // SetFlags adds command specific flags to the flag set. SetFlags(f *gnuflag.FlagSet) // Init initializes the Command before running. Init(args []string) error // Run will execute the Command as directed by the options and positional // arguments passed to Init. Run(ctx *Context) error // AllowInterspersedFlags returns whether the command allows flag // arguments to be interspersed with non-flag arguments. AllowInterspersedFlags() bool } // CommandBase provides the default implementation for SetFlags, Init, and Help. type CommandBase struct{} // IsSuperCommand implements Command.IsSuperCommand func (c *CommandBase) IsSuperCommand() bool { return false } // SetFlags does nothing in the simplest case. func (c *CommandBase) SetFlags(f *gnuflag.FlagSet) {} // Init in the simplest case makes sure there are no args. func (c *CommandBase) Init(args []string) error { return CheckEmpty(args) } // AllowInterspersedFlags returns true by default. Some subcommands // may want to override this. func (c *CommandBase) AllowInterspersedFlags() bool { return true } // Context represents the run context of a Command. Command implementations // should interpret file names relative to Dir (see AbsPath below), and print // output and errors to Stdout and Stderr respectively. type Context struct { Dir string Stdin io.Reader Stdout io.Writer Stderr io.Writer quiet bool verbose bool } func (ctx *Context) write(format string, params ...interface{}) { output := fmt.Sprintf(format, params...) if !strings.HasSuffix(output, "\n") { output = output + "\n" } fmt.Fprint(ctx.Stderr, output) } // Infof will write the formatted string to Stderr if quiet is false, but if // quiet is true the message is logged. func (ctx *Context) Infof(format string, params ...interface{}) { if ctx.quiet { logger.Infof(format, params...) } else { ctx.write(format, params...) } } // Verbosef will write the formatted string to Stderr if the verbose is true, // and to the logger if not. func (ctx *Context) Verbosef(format string, params ...interface{}) { if ctx.verbose { ctx.write(format, params...) } else { logger.Infof(format, params...) } } // AbsPath returns an absolute representation of path, with relative paths // interpreted as relative to ctx.Dir. func (ctx *Context) AbsPath(path string) string { if filepath.IsAbs(path) { return path } return filepath.Join(ctx.Dir, path) } // GetStdin satisfies environs.BootstrapContext func (ctx *Context) GetStdin() io.Reader { return ctx.Stdin } // GetStdout satisfies environs.BootstrapContext func (ctx *Context) GetStdout() io.Writer { return ctx.Stdout } // GetStderr satisfies environs.BootstrapContext func (ctx *Context) GetStderr() io.Writer { return ctx.Stderr } // InterruptNotify satisfies environs.BootstrapContext func (ctx *Context) InterruptNotify(c chan<- os.Signal) { signal.Notify(c, os.Interrupt) } // StopInterruptNotify satisfies environs.BootstrapContext func (ctx *Context) StopInterruptNotify(c chan<- os.Signal) { signal.Stop(c) } // Info holds some of the usage documentation of a Command. type Info struct { // Name is the Command's name. Name string // Args describes the command's expected positional arguments. Args string // Purpose is a short explanation of the Command's purpose. Purpose string // Doc is the long documentation for the Command. Doc string // Aliases are other names for the Command. Aliases []string } // Help renders i's content, along with documentation for any // flags defined in f. It calls f.SetOutput(ioutil.Discard). func (i *Info) Help(f *gnuflag.FlagSet) []byte { buf := &bytes.Buffer{} fmt.Fprintf(buf, "usage: %s", i.Name) hasOptions := false f.VisitAll(func(f *gnuflag.Flag) { hasOptions = true }) if hasOptions { fmt.Fprintf(buf, " [options]") } if i.Args != "" { fmt.Fprintf(buf, " %s", i.Args) } fmt.Fprintf(buf, "\n") if i.Purpose != "" { fmt.Fprintf(buf, "purpose: %s\n", i.Purpose) } if hasOptions { fmt.Fprintf(buf, "\noptions:\n") f.SetOutput(buf) f.PrintDefaults() } f.SetOutput(ioutil.Discard) if i.Doc != "" { fmt.Fprintf(buf, "\n%s\n", strings.TrimSpace(i.Doc)) } if len(i.Aliases) > 0 { fmt.Fprintf(buf, "\naliases: %s\n", strings.Join(i.Aliases, ", ")) } return buf.Bytes() } // Errors from commands can be either ErrHelp, which means "show the help" or // some other error related to needed flags missing, or needed positional args // missing, in which case we should print the error and return a non-zero // return code. func handleCommandError(c Command, ctx *Context, err error, f *gnuflag.FlagSet) (int, bool) { if err == gnuflag.ErrHelp { ctx.Stdout.Write(c.Info().Help(f)) return 0, true } if err != nil { fmt.Fprintf(ctx.Stderr, "error: %v\n", err) return 2, true } return 0, false } // Main runs the given Command in the supplied Context with the given // arguments, which should not include the command name. It returns a code // suitable for passing to os.Exit. func Main(c Command, ctx *Context, args []string) int { f := gnuflag.NewFlagSet(c.Info().Name, gnuflag.ContinueOnError) f.SetOutput(ioutil.Discard) c.SetFlags(f) if rc, done := handleCommandError(c, ctx, f.Parse(c.AllowInterspersedFlags(), args), f); done { return rc } // Since SuperCommands can also return gnuflag.ErrHelp errors, we need to // handle both those types of errors as well as "real" errors. if rc, done := handleCommandError(c, ctx, c.Init(f.Args()), f); done { return rc } if err := c.Run(ctx); err != nil { if IsRcPassthroughError(err) { return err.(*rcPassthroughError).code } if err != ErrSilent { fmt.Fprintf(ctx.Stderr, "error: %v\n", err) } return 1 } return 0 } // DefaultContext returns a Context suitable for use in non-hosted situations. func DefaultContext() (*Context, error) { dir, err := os.Getwd() if err != nil { return nil, err } abs, err := filepath.Abs(dir) if err != nil { return nil, err } return &Context{ Dir: abs, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, }, nil } // CheckEmpty is a utility function that returns an error if args is not empty. func CheckEmpty(args []string) error { if len(args) != 0 { return fmt.Errorf("unrecognized args: %q", args) } return nil } // ZeroOrOneArgs checks to see that there are zero or one args, and returns // the value of the arg if provided, or the empty string if not. func ZeroOrOneArgs(args []string) (string, error) { var result string if len(args) > 0 { result, args = args[0], args[1:] } if err := CheckEmpty(args); err != nil { return "", err } return result, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/supercommand.go������������������������������������0000644�0000153�0000161�00000032506�12321735776�024472� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd import ( "bytes" "fmt" "io/ioutil" "runtime" "sort" "strings" "github.com/juju/loggo" "launchpad.net/gnuflag" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.cmd") type topic struct { short string long func() string // Help aliases are not output when topics are listed, but are used // to search for the help topic alias bool } type UnrecognizedCommand struct { Name string } func (e *UnrecognizedCommand) Error() string { return fmt.Sprintf("unrecognized command: %s", e.Name) } // MissingCallback defines a function that will be used by the SuperCommand if // the requested subcommand isn't found. type MissingCallback func(ctx *Context, subcommand string, args []string) error // SuperCommandParams provides a way to have default parameter to the // `NewSuperCommand` call. type SuperCommandParams struct { UsagePrefix string Name string Purpose string Doc string Log *Log MissingCallback MissingCallback } // NewSuperCommand creates and initializes a new `SuperCommand`, and returns // the fully initialized structure. func NewSuperCommand(params SuperCommandParams) *SuperCommand { command := &SuperCommand{ Name: params.Name, Purpose: params.Purpose, Doc: params.Doc, Log: params.Log, usagePrefix: params.UsagePrefix, missingCallback: params.MissingCallback} command.init() return command } // SuperCommand is a Command that selects a subcommand and assumes its // properties; any command line arguments that were not used in selecting // the subcommand are passed down to it, and to Run a SuperCommand is to run // its selected subcommand. type SuperCommand struct { CommandBase Name string Purpose string Doc string Log *Log usagePrefix string subcmds map[string]Command commonflags *gnuflag.FlagSet flags *gnuflag.FlagSet subcmd Command showHelp bool showDescription bool showVersion bool missingCallback MissingCallback } // IsSuperCommand implements Command.IsSuperCommand func (c *SuperCommand) IsSuperCommand() bool { return true } // Because Go doesn't have constructors that initialize the object into a // ready state. func (c *SuperCommand) init() { if c.subcmds != nil { return } help := &helpCommand{ super: c, } help.init() c.subcmds = map[string]Command{ "help": help, } } // AddHelpTopic adds a new help topic with the description being the short // param, and the full text being the long param. The description is shown in // 'help topics', and the full text is shown when the command 'help <name>' is // called. func (c *SuperCommand) AddHelpTopic(name, short, long string, aliases ...string) { c.subcmds["help"].(*helpCommand).addTopic(name, short, echo(long), aliases...) } // AddHelpTopicCallback adds a new help topic with the description being the // short param, and the full text being defined by the callback function. func (c *SuperCommand) AddHelpTopicCallback(name, short string, longCallback func() string) { c.subcmds["help"].(*helpCommand).addTopic(name, short, longCallback) } // Register makes a subcommand available for use on the command line. The // command will be available via its own name, and via any supplied aliases. func (c *SuperCommand) Register(subcmd Command) { info := subcmd.Info() c.insert(info.Name, subcmd) for _, name := range info.Aliases { c.insert(name, subcmd) } } func (c *SuperCommand) insert(name string, subcmd Command) { if _, found := c.subcmds[name]; found || name == "help" { panic(fmt.Sprintf("command already registered: %s", name)) } c.subcmds[name] = subcmd } // describeCommands returns a short description of each registered subcommand. func (c *SuperCommand) describeCommands(simple bool) string { var lineFormat = " %-*s - %s" var outputFormat = "commands:\n%s" if simple { lineFormat = "%-*s %s" outputFormat = "%s" } cmds := make([]string, len(c.subcmds)) i := 0 longest := 0 for name := range c.subcmds { if len(name) > longest { longest = len(name) } cmds[i] = name i++ } sort.Strings(cmds) for i, name := range cmds { info := c.subcmds[name].Info() purpose := info.Purpose if name != info.Name { purpose = "alias for " + info.Name } cmds[i] = fmt.Sprintf(lineFormat, longest, name, purpose) } return fmt.Sprintf(outputFormat, strings.Join(cmds, "\n")) } // Info returns a description of the currently selected subcommand, or of the // SuperCommand itself if no subcommand has been specified. func (c *SuperCommand) Info() *Info { if c.subcmd != nil { info := *c.subcmd.Info() info.Name = fmt.Sprintf("%s %s", c.Name, info.Name) return &info } docParts := []string{} if doc := strings.TrimSpace(c.Doc); doc != "" { docParts = append(docParts, doc) } if cmds := c.describeCommands(false); cmds != "" { docParts = append(docParts, cmds) } return &Info{ Name: c.Name, Args: "<command> ...", Purpose: c.Purpose, Doc: strings.Join(docParts, "\n\n"), } } const helpPurpose = "show help on a command or other topic" // SetCommonFlags creates a new "commonflags" flagset, whose // flags are shared with the argument f; this enables us to // add non-global flags to f, which do not carry into subcommands. func (c *SuperCommand) SetCommonFlags(f *gnuflag.FlagSet) { if c.Log != nil { c.Log.AddFlags(f) } f.BoolVar(&c.showHelp, "h", false, helpPurpose) f.BoolVar(&c.showHelp, "help", false, "") // In the case where we are providing the basis for a plugin, // plugins are required to support the --description argument. // The Purpose attribute will be printed (if defined), allowing // plugins to provide a sensible line of text for 'juju help plugins'. f.BoolVar(&c.showDescription, "description", false, "") c.commonflags = gnuflag.NewFlagSet(c.Info().Name, gnuflag.ContinueOnError) c.commonflags.SetOutput(ioutil.Discard) f.VisitAll(func(flag *gnuflag.Flag) { c.commonflags.Var(flag.Value, flag.Name, flag.Usage) }) } // SetFlags adds the options that apply to all commands, particularly those // due to logging. func (c *SuperCommand) SetFlags(f *gnuflag.FlagSet) { c.SetCommonFlags(f) // Only flags set by SetCommonFlags are passed on to subcommands. // Any flags added below only take effect when no subcommand is // specified (e.g. juju --version). f.BoolVar(&c.showVersion, "version", false, "Show the version of juju") c.flags = f } // For a SuperCommand, we want to parse the args with // allowIntersperse=false. This will mean that the args may contain other // options that haven't been defined yet, and that only options that relate // to the SuperCommand itself can come prior to the subcommand name. func (c *SuperCommand) AllowInterspersedFlags() bool { return false } // Init initializes the command for running. func (c *SuperCommand) Init(args []string) error { if c.showDescription { return CheckEmpty(args) } if len(args) == 0 { c.subcmd = c.subcmds["help"] return nil } found := false // Look for the command. if c.subcmd, found = c.subcmds[args[0]]; !found { if c.missingCallback != nil { c.subcmd = &missingCommand{ callback: c.missingCallback, superName: c.Name, name: args[0], args: args[1:], } // Yes return here, no Init called on missing Command. return nil } return fmt.Errorf("unrecognized command: %s %s", c.Name, args[0]) } args = args[1:] if c.subcmd.IsSuperCommand() { f := gnuflag.NewFlagSet(c.Info().Name, gnuflag.ContinueOnError) f.SetOutput(ioutil.Discard) c.subcmd.SetFlags(f) } else { c.subcmd.SetFlags(c.commonflags) } if err := c.commonflags.Parse(c.subcmd.AllowInterspersedFlags(), args); err != nil { return err } args = c.commonflags.Args() if c.showHelp { // We want to treat help for the command the same way we would if we went "help foo". args = []string{c.subcmd.Info().Name} c.subcmd = c.subcmds["help"] } return c.subcmd.Init(args) } // Run executes the subcommand that was selected in Init. func (c *SuperCommand) Run(ctx *Context) error { if c.showDescription { if c.Purpose != "" { fmt.Fprintf(ctx.Stdout, "%s\n", c.Purpose) } else { fmt.Fprintf(ctx.Stdout, "%s: no description available\n", c.Info().Name) } return nil } if c.subcmd == nil { panic("Run: missing subcommand; Init failed or not called") } if c.Log != nil { if err := c.Log.Start(ctx); err != nil { return err } } logger.Infof("running juju-%s [%s]", version.Current, runtime.Compiler) err := c.subcmd.Run(ctx) if err != nil && err != ErrSilent { logger.Errorf("%v", err) // Now that this has been logged, don't log again in cmd.Main. if !IsRcPassthroughError(err) { err = ErrSilent } } else { logger.Infof("command finished") } return err } type missingCommand struct { CommandBase callback MissingCallback superName string name string args []string } // Missing commands only need to supply Info for the interface, but this is // never called. func (c *missingCommand) Info() *Info { return nil } func (c *missingCommand) Run(ctx *Context) error { err := c.callback(ctx, c.name, c.args) _, isUnrecognized := err.(*UnrecognizedCommand) if !isUnrecognized { return err } return &UnrecognizedCommand{c.superName + " " + c.name} } type helpCommand struct { CommandBase super *SuperCommand topic string topicArgs []string topics map[string]topic } func (c *helpCommand) init() { c.topics = map[string]topic{ "commands": { short: "Basic help for all commands", long: func() string { return c.super.describeCommands(true) }, }, "global-options": { short: "Options common to all commands", long: func() string { return c.globalOptions() }, }, "topics": { short: "Topic list", long: func() string { return c.topicList() }, }, } } func echo(s string) func() string { return func() string { return s } } func (c *helpCommand) addTopic(name, short string, long func() string, aliases ...string) { if _, found := c.topics[name]; found { panic(fmt.Sprintf("help topic already added: %s", name)) } c.topics[name] = topic{short, long, false} for _, alias := range aliases { if _, found := c.topics[alias]; found { panic(fmt.Sprintf("help topic already added: %s", alias)) } c.topics[alias] = topic{short, long, true} } } func (c *helpCommand) globalOptions() string { buf := &bytes.Buffer{} fmt.Fprintf(buf, `Global Options These options may be used with any command, and may appear in front of any command. `) f := gnuflag.NewFlagSet("", gnuflag.ContinueOnError) c.super.SetCommonFlags(f) f.SetOutput(buf) f.PrintDefaults() return buf.String() } func (c *helpCommand) topicList() string { var topics []string longest := 0 for name, topic := range c.topics { if topic.alias { continue } if len(name) > longest { longest = len(name) } topics = append(topics, name) } sort.Strings(topics) for i, name := range topics { shortHelp := c.topics[name].short topics[i] = fmt.Sprintf("%-*s %s", longest, name, shortHelp) } return fmt.Sprintf("%s", strings.Join(topics, "\n")) } func (c *helpCommand) Info() *Info { return &Info{ Name: "help", Args: "[topic]", Purpose: helpPurpose, Doc: ` See also: topics `, } } func (c *helpCommand) Init(args []string) error { switch len(args) { case 0: case 1: c.topic = args[0] default: if c.super.missingCallback == nil { return fmt.Errorf("extra arguments to command help: %q", args[1:]) } else { c.topic = args[0] c.topicArgs = args[1:] } } return nil } func (c *helpCommand) Run(ctx *Context) error { if c.super.showVersion { var v VersionCommand v.SetFlags(c.super.flags) v.Init(nil) return v.Run(ctx) } // If there is no help topic specified, print basic usage. if c.topic == "" { if _, ok := c.topics["basics"]; ok { c.topic = "basics" } else { // At this point, "help" is selected as the SuperCommand's // sub-command, but we want the info to be printed // as if there was nothing selected. c.super.subcmd = nil info := c.super.Info() f := gnuflag.NewFlagSet(info.Name, gnuflag.ContinueOnError) c.SetFlags(f) ctx.Stdout.Write(info.Help(f)) return nil } } // If the topic is a registered subcommand, then run the help command with it if helpcmd, ok := c.super.subcmds[c.topic]; ok { info := helpcmd.Info() info.Name = fmt.Sprintf("%s %s", c.super.Name, info.Name) if c.super.usagePrefix != "" { info.Name = fmt.Sprintf("%s %s", c.super.usagePrefix, info.Name) } f := gnuflag.NewFlagSet(info.Name, gnuflag.ContinueOnError) helpcmd.SetFlags(f) ctx.Stdout.Write(info.Help(f)) return nil } // Look to see if the topic is a registered topic. topic, ok := c.topics[c.topic] if ok { fmt.Fprintf(ctx.Stdout, "%s\n", strings.TrimSpace(topic.long())) return nil } // If we have a missing callback, call that with --help if c.super.missingCallback != nil { helpArgs := []string{"--help"} if len(c.topicArgs) > 0 { helpArgs = append(helpArgs, c.topicArgs...) } subcmd := &missingCommand{ callback: c.super.missingCallback, superName: c.super.Name, name: c.topic, args: helpArgs, } err := subcmd.Run(ctx) _, isUnrecognized := err.(*UnrecognizedCommand) if !isUnrecognized { return err } } return fmt.Errorf("unknown command or topic for %s", c.topic) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/version.go�����������������������������������������0000644�0000153�0000161�00000001206�12321735642�023443� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd import ( "launchpad.net/gnuflag" "launchpad.net/juju-core/version" ) // VersionCommand is a cmd.Command that prints the current version. type VersionCommand struct { CommandBase out Output } func (v *VersionCommand) Info() *Info { return &Info{ Name: "version", Purpose: "print the current version", } } func (v *VersionCommand) SetFlags(f *gnuflag.FlagSet) { v.out.AddFlags(f, "smart", DefaultFormatters) } func (v *VersionCommand) Run(ctxt *Context) error { return v.out.Write(ctxt, version.Current.String()) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/�������������������������������������������0000755�0000153�0000161�00000000000�12321735641�023110� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/local/�������������������������������������0000755�0000153�0000161�00000000000�12321735643�024204� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/local/export_test.go�����������������������0000644�0000153�0000161�00000000442�12321735642�027112� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local var ( // Variable is the address so we can PatchValue CheckIfRoot = &checkIfRoot // function exports for tests RunAsRoot = runAsRoot JujuLocalPlugin = jujuLocalPlugin ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/local/package_test.go����������������������0000644�0000153�0000161�00000000332�12321735642�027162� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local_test import ( testing "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/local/juju-local/��������������������������0000755�0000153�0000161�00000000000�12321735642�026250� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/local/juju-local/main.go�������������������0000644�0000153�0000161�00000000442�12321735642�027523� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "os" "launchpad.net/juju-core/cmd/plugins/local" // Import only the local provider. _ "launchpad.net/juju-core/provider/local" ) func main() { local.Main(os.Args) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/local/main.go������������������������������0000644�0000153�0000161�00000003423�12321735642�025460� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local import ( "os" "os/exec" "github.com/juju/loggo" "launchpad.net/juju-core/cmd" ) var logger = loggo.GetLogger("juju.plugins.local") const localDoc = ` Juju local is used to provide extra commands that assist with the local provider. See Also: juju help local-provider ` func jujuLocalPlugin() cmd.Command { plugin := cmd.NewSuperCommand(cmd.SuperCommandParams{ Name: "juju local", UsagePrefix: "juju", Doc: localDoc, Purpose: "local provider specific commands", Log: &cmd.Log{}, }) return plugin } // Main registers subcommands for the juju-local executable. func Main(args []string) { ctx, err := cmd.DefaultContext() if err != nil { logger.Debugf("error: %v\n", err) os.Exit(2) } plugin := jujuLocalPlugin() os.Exit(cmd.Main(plugin, ctx, args[1:])) } var checkIfRoot = func() bool { return os.Getuid() == 0 } // runAsRoot ensures that the executable is running as root. // If checkIfRoot returns true, the call function is called, // otherwise executable is executed using sudo and the extra args // passed through. func runAsRoot(executable string, args []string, context *cmd.Context, call func(*cmd.Context) error) error { if checkIfRoot() { logger.Debugf("running as root") return call(context) } logger.Debugf("running as user") fullpath, err := exec.LookPath(executable) if err != nil { return err } sudoArgs := []string{"--preserve-env", fullpath} sudoArgs = append(sudoArgs, args...) command := exec.Command("sudo", sudoArgs...) // Now hook up stdin, stdout, stderr command.Stdin = context.Stdin command.Stdout = context.Stdout command.Stderr = context.Stderr // And run it! return command.Run() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/local/main_test.go�������������������������0000644�0000153�0000161�00000004210�12321735642�026512� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local_test import ( "fmt" "os/exec" "strings" "github.com/juju/testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/cmd/plugins/local" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type mainSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&mainSuite{}) func (*mainSuite) TestRegisteredCommands(c *gc.C) { expectedSubcommands := []string{ "help", // TODO: add some as they get registered } plugin := local.JujuLocalPlugin() ctx, err := coretesting.RunCommand(c, plugin, []string{"help", "commands"}) c.Assert(err, gc.IsNil) lines := strings.Split(coretesting.Stdout(ctx), "\n") var names []string for _, line := range lines { f := strings.Fields(line) if len(f) == 0 { continue } names = append(names, f[0]) } // The names should be output in alphabetical order, so don't sort. c.Assert(names, gc.DeepEquals, expectedSubcommands) } func (s *mainSuite) TestRunAsRootCallsFuncIfRoot(c *gc.C) { s.PatchValue(local.CheckIfRoot, func() bool { return true }) called := false call := func(*cmd.Context) error { called = true return nil } args := []string{"ignored..."} err := local.RunAsRoot("juju-magic", args, coretesting.Context(c), call) c.Assert(err, gc.IsNil) c.Assert(called, jc.IsTrue) } func (s *mainSuite) TestRunAsRootCallsSudoIfNotRoot(c *gc.C) { s.PatchValue(local.CheckIfRoot, func() bool { return false }) testing.PatchExecutableAsEchoArgs(c, s, "sudo") // the command needs to be in the path... testing.PatchExecutableAsEchoArgs(c, s, "juju-magic") magicPath, err := exec.LookPath("juju-magic") c.Assert(err, gc.IsNil) callIgnored := func(*cmd.Context) error { panic("unreachable") } args := []string{"passed"} context := coretesting.Context(c) err = local.RunAsRoot("juju-magic", args, context, callIgnored) c.Assert(err, gc.IsNil) expected := fmt.Sprintf("sudo \"--preserve-env\" %q \"passed\"\n", magicPath) c.Assert(coretesting.Stdout(context), gc.Equals, expected) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-restore/������������������������������0000755�0000153�0000161�00000000000�12321736000�025534� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-restore/restore.go��������������������0000644�0000153�0000161�00000032725�12321735776�027602� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "archive/tar" "bytes" "compress/gzip" "fmt" "io" "io/ioutil" "os" "path" "text/template" "github.com/juju/loggo" "launchpad.net/gnuflag" "launchpad.net/goyaml" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" _ "launchpad.net/juju-core/provider/all" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" ) func main() { Main(os.Args) } func Main(args []string) { ctx, err := cmd.DefaultContext() if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(2) } if err := juju.InitJujuHome(); err != nil { fmt.Fprintf(os.Stderr, "error: %s\n", err) os.Exit(2) } os.Exit(cmd.Main(&restoreCommand{}, ctx, args[1:])) } var logger = loggo.GetLogger("juju.plugins.restore") const restoreDoc = ` Restore restores a backup created with juju backup by creating a new juju bootstrap instance and arranging it so that the existing instances in the environment talk to it. It verifies that the existing bootstrap instance is not running. The given constraints will be used to choose the new instance. ` type restoreCommand struct { cmd.EnvCommandBase Log cmd.Log Constraints constraints.Value backupFile string showDescription bool } func (c *restoreCommand) Info() *cmd.Info { return &cmd.Info{ Name: "juju-restore", Purpose: "Restore a backup made with juju backup", Args: "<backupfile.tar.gz>", Doc: restoreDoc, } } func (c *restoreCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "set environment constraints") f.BoolVar(&c.showDescription, "description", false, "show the purpose of this plugin") c.Log.AddFlags(f) } func (c *restoreCommand) Init(args []string) error { if c.showDescription { return cmd.CheckEmpty(args) } if len(args) == 0 { return fmt.Errorf("no backup file specified") } c.backupFile = args[0] return cmd.CheckEmpty(args[1:]) } var updateBootstrapMachineTemplate = mustParseTemplate(` set -e -x tar xzf juju-backup.tgz test -d juju-backup initctl stop jujud-machine-0 initctl stop juju-db rm -r /var/lib/juju /var/log/juju tar -C / -xvp -f juju-backup/root.tar mkdir -p /var/lib/juju/db export LC_ALL=C mongorestore --drop --dbpath /var/lib/juju/db juju-backup/dump initctl start juju-db mongoEval() { mongo --ssl -u {{.Creds.Tag}} -p {{.Creds.Password | shquote}} localhost:37017/juju --eval "$1" } # wait for mongo to come up after starting the juju-db upstart service. for i in $(seq 1 60) do mongoEval ' ' && break sleep 2 done mongoEval ' db = db.getSiblingDB("juju") db.machines.update({_id: "0"}, {$set: {instanceid: '{{.NewInstanceId | printf "%q" | shquote}}' } }) db.instanceData.update({_id: "0"}, {$set: {instanceid: '{{.NewInstanceId | printf "%q"| shquote}}' } }) ' initctl start jujud-machine-0 `) func updateBootstrapMachineScript(instanceId instance.Id, creds credentials) string { return execTemplate(updateBootstrapMachineTemplate, struct { NewInstanceId instance.Id Creds credentials }{instanceId, creds}) } func (c *restoreCommand) Run(ctx *cmd.Context) error { if c.showDescription { fmt.Fprintf(ctx.Stdout, "%s\n", c.Info().Purpose) return nil } if err := c.Log.Start(ctx); err != nil { return err } creds, err := extractCreds(c.backupFile) if err != nil { return fmt.Errorf("cannot extract credentials from backup file: %v", err) } progress("extracted credentials from backup file") store, err := configstore.Default() if err != nil { return err } cfg, _, err := environs.ConfigForName(c.EnvName, store) if err != nil { return err } env, err := rebootstrap(cfg, ctx, c.Constraints) if err != nil { return fmt.Errorf("cannot re-bootstrap environment: %v", err) } progress("connecting to newly bootstrapped instance") conn, err := juju.NewAPIConn(env, api.DefaultDialOpts()) if err != nil { return fmt.Errorf("cannot connect to bootstrap instance: %v", err) } progress("restoring bootstrap machine") newInstId, machine0Addr, err := restoreBootstrapMachine(conn, c.backupFile, creds) if err != nil { return fmt.Errorf("cannot restore bootstrap machine: %v", err) } progress("restored bootstrap machine") // Update the environ state to point to the new instance. if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{ StateInstances: []instance.Id{newInstId}, }); err != nil { return fmt.Errorf("cannot update environ bootstrap state storage: %v", err) } // Construct our own state info rather than using juju.NewConn so // that we can avoid storage eventual-consistency issues // (and it's faster too). caCert, ok := cfg.CACert() if !ok { return fmt.Errorf("configuration has no CA certificate") } progress("opening state") st, err := state.Open(&state.Info{ Addrs: []string{fmt.Sprintf("%s:%d", machine0Addr, cfg.StatePort())}, CACert: caCert, Tag: creds.Tag, Password: creds.Password, }, state.DefaultDialOpts(), environs.NewStatePolicy()) if err != nil { return fmt.Errorf("cannot open state: %v", err) } progress("updating all machines") if err := updateAllMachines(st, machine0Addr); err != nil { return fmt.Errorf("cannot update machines: %v", err) } return nil } func progress(f string, a ...interface{}) { fmt.Printf("%s\n", fmt.Sprintf(f, a...)) } func rebootstrap(cfg *config.Config, ctx *cmd.Context, cons constraints.Value) (environs.Environ, error) { progress("re-bootstrapping environment") // Turn on safe mode so that the newly bootstrapped instance // will not destroy all the instances it does not know about. cfg, err := cfg.Apply(map[string]interface{}{ "provisioner-safe-mode": true, }) if err != nil { return nil, fmt.Errorf("cannot enable provisioner-safe-mode: %v", err) } env, err := environs.New(cfg) if err != nil { return nil, err } state, err := bootstrap.LoadState(env.Storage()) if err != nil { return nil, fmt.Errorf("cannot retrieve environment storage; perhaps the environment was not bootstrapped: %v", err) } if len(state.StateInstances) == 0 { return nil, fmt.Errorf("no instances found on bootstrap state; perhaps the environment was not bootstrapped") } if len(state.StateInstances) > 1 { return nil, fmt.Errorf("restore does not support HA juju configurations yet") } inst, err := env.Instances(state.StateInstances) if err == nil { return nil, fmt.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst) } if err != environs.ErrNoInstances { return nil, fmt.Errorf("cannot detect whether old instance is still running: %v", err) } // Remove the storage so that we can bootstrap without the provider complaining. if err := env.Storage().Remove(bootstrap.StateFile); err != nil { return nil, fmt.Errorf("cannot remove %q from storage: %v", bootstrap.StateFile, err) } // TODO If we fail beyond here, then we won't have a state file and // we won't be able to re-run this script because it fails without it. // We could either try to recreate the file if we fail (which is itself // error-prone) or we could provide a --no-check flag to make // it go ahead anyway without the check. if err := bootstrap.Bootstrap(ctx, env, cons); err != nil { return nil, fmt.Errorf("cannot bootstrap new instance: %v", err) } return env, nil } func restoreBootstrapMachine(conn *juju.APIConn, backupFile string, creds credentials) (newInstId instance.Id, addr string, err error) { addr, err = conn.State.Client().PublicAddress("0") if err != nil { return "", "", fmt.Errorf("cannot get public address of bootstrap machine: %v", err) } status, err := conn.State.Client().Status(nil) if err != nil { return "", "", fmt.Errorf("cannot get environment status: %v", err) } info, ok := status.Machines["0"] if !ok { return "", "", fmt.Errorf("cannot find bootstrap machine in status") } newInstId = instance.Id(info.InstanceId) progress("copying backup file to bootstrap host") if err := sendViaScp(backupFile, addr, "~/juju-backup.tgz"); err != nil { return "", "", fmt.Errorf("cannot copy backup file to bootstrap instance: %v", err) } progress("updating bootstrap machine") if err := runViaSsh(addr, updateBootstrapMachineScript(newInstId, creds)); err != nil { return "", "", fmt.Errorf("update script failed: %v", err) } return newInstId, addr, nil } type credentials struct { Tag string Password string } func extractCreds(backupFile string) (credentials, error) { f, err := os.Open(backupFile) if err != nil { return credentials{}, err } defer f.Close() gzr, err := gzip.NewReader(f) if err != nil { return credentials{}, fmt.Errorf("cannot unzip %q: %v", backupFile, err) } defer gzr.Close() outerTar, err := findFileInTar(gzr, "juju-backup/root.tar") if err != nil { return credentials{}, err } agentConf, err := findFileInTar(outerTar, "var/lib/juju/agents/machine-0/agent.conf") if err != nil { return credentials{}, err } data, err := ioutil.ReadAll(agentConf) if err != nil { return credentials{}, fmt.Errorf("failed to read agent config file: %v", err) } var conf interface{} if err := goyaml.Unmarshal(data, &conf); err != nil { return credentials{}, fmt.Errorf("cannot unmarshal agent config file: %v", err) } m, ok := conf.(map[interface{}]interface{}) if !ok { return credentials{}, fmt.Errorf("config file unmarshalled to %T not %T", conf, m) } password, ok := m["statepassword"].(string) if !ok || password == "" { return credentials{}, fmt.Errorf("agent password not found in configuration") } return credentials{ Tag: "machine-0", Password: password, }, nil } func findFileInTar(r io.Reader, name string) (io.Reader, error) { tarr := tar.NewReader(r) for { hdr, err := tarr.Next() if err != nil { return nil, fmt.Errorf("%q not found: %v", name, err) } if path.Clean(hdr.Name) == name { return tarr, nil } } } var agentAddressTemplate = mustParseTemplate(` set -exu cd /var/lib/juju/agents for agent in * do initctl stop jujud-$agent sed -i.old -r "/^(stateaddresses|apiaddresses):/{ n s/- .*(:[0-9]+)/- {{.Address}}\1/ }" $agent/agent.conf # If we're processing a unit agent's directly # and it has some relations, reset # the stored version of all of them to # ensure that any relation hooks will # fire. if [[ $agent = unit-* ]] then find $agent/state/relations -type f -exec sed -i -r 's/change-version: [0-9]+$/change-version: 0/' {} \; fi initctl start jujud-$agent done `) // setAgentAddressScript generates an ssh script argument to update state addresses func setAgentAddressScript(stateAddr string) string { return execTemplate(agentAddressTemplate, struct { Address string }{stateAddr}) } // updateAllMachines finds all machines and resets the stored state address // in each of them. The address does not include the port. func updateAllMachines(st *state.State, stateAddr string) error { machines, err := st.AllMachines() if err != nil { return err } pendingMachineCount := 0 done := make(chan error) for _, machine := range machines { // A newly resumed state server requires no updating, and more // than one state server is not yet support by this plugin. if machine.IsManager() || machine.Life() == state.Dead { continue } pendingMachineCount++ machine := machine go func() { err := runMachineUpdate(machine, setAgentAddressScript(stateAddr)) if err != nil { logger.Errorf("failed to update machine %s: %v", machine, err) } else { progress("updated machine %s", machine) } done <- err }() } err = nil for ; pendingMachineCount > 0; pendingMachineCount-- { if updateErr := <-done; updateErr != nil && err == nil { err = fmt.Errorf("machine update failed") } } return err } // runMachineUpdate connects via ssh to the machine and runs the update script func runMachineUpdate(m *state.Machine, sshArg string) error { progress("updating machine: %v\n", m) addr := instance.SelectPublicAddress(m.Addresses()) if addr == "" { return fmt.Errorf("no appropriate public address found") } return runViaSsh(addr, sshArg) } func runViaSsh(addr string, script string) error { // This is taken from cmd/juju/ssh.go there is no other clear way to set user userAddr := "ubuntu@" + addr cmd := ssh.Command(userAddr, []string{"sudo", "-n", "bash", "-c " + utils.ShQuote(script)}, nil) var stderrBuf bytes.Buffer var stdoutBuf bytes.Buffer cmd.Stderr = &stderrBuf cmd.Stdout = &stdoutBuf err := cmd.Run() if err != nil { return fmt.Errorf("ssh command failed: %v (%q)", err, stderrBuf.String()) } progress("ssh command succedded: %q", stdoutBuf.String()) return nil } func sendViaScp(file, host, destFile string) error { err := ssh.Copy([]string{file, "ubuntu@" + host + ":" + destFile}, nil) if err != nil { return fmt.Errorf("scp command failed: %v", err) } return nil } func mustParseTemplate(templ string) *template.Template { t := template.New("").Funcs(template.FuncMap{ "shquote": utils.ShQuote, }) return template.Must(t.Parse(templ)) } func execTemplate(tmpl *template.Template, data interface{}) string { var buf bytes.Buffer err := tmpl.Execute(&buf, data) if err != nil { panic(fmt.Errorf("template error: %v", err)) } return buf.String() } �������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/�����������������������������0000755�0000153�0000161�00000000000�12321736000�025631� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/metadataplugin_test.go�������0000644�0000153�0000161�00000004743�12321735642�032241� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "flag" "fmt" "os" "os/exec" "strings" stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/testing" ) func Test(t *stdtesting.T) { gc.TestingT(t) } type MetadataSuite struct { jujuHome *testing.FakeHome } var _ = gc.Suite(&MetadataSuite{}) var metadataCommandNames = []string{ "generate-image", "generate-tools", "help", "sign", "validate-images", "validate-tools", } func (s *MetadataSuite) SetUpTest(c *gc.C) { s.jujuHome = testing.MakeEmptyFakeHome(c) } func (s *MetadataSuite) TearDownTest(c *gc.C) { s.jujuHome.Restore() } var ( flagRunMain = flag.Bool("run-main", false, "Run the application's main function for recursive testing") ) // Reentrancy point for testing (something as close as possible to) the juju // tool itself. func TestRunMain(t *stdtesting.T) { if *flagRunMain { Main(flag.Args()) } } func badrun(c *gc.C, exit int, args ...string) string { localArgs := append([]string{"-test.run", "TestRunMain", "-run-main", "--", "juju-metadata"}, args...) ps := exec.Command(os.Args[0], localArgs...) ps.Env = append(os.Environ(), osenv.JujuHomeEnvKey+"="+osenv.JujuHome()) output, err := ps.CombinedOutput() if exit != 0 { c.Assert(err, gc.ErrorMatches, fmt.Sprintf("exit status %d", exit)) } return string(output) } func (s *MetadataSuite) TestHelpCommands(c *gc.C) { // Check that we have correctly registered all the sub commands // by checking the help output. out := badrun(c, 0, "--help") lines := strings.Split(out, "\n") var names []string for _, line := range lines { f := strings.Fields(line) if len(f) == 0 || !strings.HasPrefix(line, " ") { continue } names = append(names, f[0]) } // The names should be output in alphabetical order, so don't sort. c.Assert(names, gc.DeepEquals, metadataCommandNames) } func (s *MetadataSuite) assertHelpOutput(c *gc.C, cmd string) { expected := fmt.Sprintf("usage: juju metadata %s [options]", cmd) out := badrun(c, 0, cmd, "--help") lines := strings.Split(out, "\n") c.Assert(lines[0], gc.Equals, expected) } func (s *MetadataSuite) TestHelpValidateImages(c *gc.C) { s.assertHelpOutput(c, "validate-images") } func (s *MetadataSuite) TestHelpValidateTools(c *gc.C) { s.assertHelpOutput(c, "validate-tools") } func (s *MetadataSuite) TestHelpGenerateImage(c *gc.C) { s.assertHelpOutput(c, "generate-image") } �����������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/metadata.go������������������0000644�0000153�0000161�00000002746�12321735642�027764� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "github.com/juju/loggo" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju" _ "launchpad.net/juju-core/provider/all" ) var logger = loggo.GetLogger("juju.plugins.metadata") var metadataDoc = ` Juju metadata is used to find the correct image and tools when bootstrapping a Juju environment. ` // Main registers subcommands for the juju-metadata executable, and hands over control // to the cmd package. This function is not redundant with main, because it // provides an entry point for testing with arbitrary command line arguments. func Main(args []string) { ctx, err := cmd.DefaultContext() if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(2) } if err := juju.InitJujuHome(); err != nil { fmt.Fprintf(os.Stderr, "error: %s\n", err) os.Exit(2) } metadatacmd := cmd.NewSuperCommand(cmd.SuperCommandParams{ Name: "metadata", UsagePrefix: "juju", Doc: metadataDoc, Purpose: "tools for generating and validating image and tools metadata", Log: &cmd.Log{}}) metadatacmd.Register(&ValidateImageMetadataCommand{}) metadatacmd.Register(&ImageMetadataCommand{}) metadatacmd.Register(&ToolsMetadataCommand{}) metadatacmd.Register(&ValidateToolsMetadataCommand{}) metadatacmd.Register(&SignMetadataCommand{}) os.Exit(cmd.Main(metadatacmd, ctx, args[1:])) } func main() { Main(os.Args) } ��������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/imagemetadata_test.go��������0000644�0000153�0000161�00000016110�12321735642�032014� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type ImageMetadataSuite struct { testbase.LoggingSuite environ []string home *testing.FakeHome dir string } var _ = gc.Suite(&ImageMetadataSuite{}) func (s *ImageMetadataSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.environ = os.Environ() } func (s *ImageMetadataSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) os.Clearenv() s.dir = c.MkDir() // Create a fake certificate so azure test environment can be opened. certfile, err := ioutil.TempFile(s.dir, "") c.Assert(err, gc.IsNil) filename := certfile.Name() err = ioutil.WriteFile(filename, []byte("test certificate"), 0644) c.Assert(err, gc.IsNil) envConfig := strings.Replace(metadataTestEnvConfig, "/home/me/azure.pem", filename, -1) s.home = testing.MakeFakeHome(c, envConfig) s.PatchEnvironment("AWS_ACCESS_KEY_ID", "access") s.PatchEnvironment("AWS_SECRET_ACCESS_KEY", "secret") } func (s *ImageMetadataSuite) TearDownTest(c *gc.C) { for _, envstring := range s.environ { kv := strings.SplitN(envstring, "=", 2) os.Setenv(kv[0], kv[1]) } s.home.Restore() s.LoggingSuite.TearDownTest(c) } var seriesVersions map[string]string = map[string]string{ "precise": "12.04", "raring": "13.04", "trusty": "14.04", } type expectedMetadata struct { series string arch string region string endpoint string } func (s *ImageMetadataSuite) assertCommandOutput(c *gc.C, expected expectedMetadata, errOut, indexFileName, imageFileName string) { if expected.region == "" { expected.region = "region" } if expected.endpoint == "" { expected.endpoint = "endpoint" } strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `image metadata files have been written to.*`) indexpath := filepath.Join(s.dir, "images", "streams", "v1", indexFileName) data, err := ioutil.ReadFile(indexpath) c.Assert(err, gc.IsNil) content := string(data) var indices interface{} err = json.Unmarshal(data, &indices) c.Assert(err, gc.IsNil) c.Assert(indices.(map[string]interface{})["format"], gc.Equals, "index:1.0") prodId := fmt.Sprintf("com.ubuntu.cloud:server:%s:%s", seriesVersions[expected.series], expected.arch) c.Assert(content, jc.Contains, prodId) c.Assert(content, jc.Contains, fmt.Sprintf(`"region": %q`, expected.region)) c.Assert(content, jc.Contains, fmt.Sprintf(`"endpoint": %q`, expected.endpoint)) c.Assert(content, jc.Contains, fmt.Sprintf(`"path": "streams/v1/%s"`, imageFileName)) imagepath := filepath.Join(s.dir, "images", "streams", "v1", imageFileName) data, err = ioutil.ReadFile(imagepath) c.Assert(err, gc.IsNil) content = string(data) var images interface{} err = json.Unmarshal(data, &images) c.Assert(err, gc.IsNil) c.Assert(images.(map[string]interface{})["format"], gc.Equals, "products:1.0") c.Assert(content, jc.Contains, prodId) c.Assert(content, jc.Contains, `"id": "1234"`) } const ( defaultIndexFileName = "index.json" defaultImageFileName = "com.ubuntu.cloud:released:imagemetadata.json" ) func (s *ImageMetadataSuite) TestImageMetadataFilesNoEnv(c *gc.C) { ctx := testing.Context(c) code := cmd.Main( &ImageMetadataCommand{}, ctx, []string{ "-d", s.dir, "-i", "1234", "-r", "region", "-a", "arch", "-u", "endpoint", "-s", "raring"}) c.Assert(code, gc.Equals, 0) out := testing.Stdout(ctx) expected := expectedMetadata{ series: "raring", arch: "arch", } s.assertCommandOutput(c, expected, out, defaultIndexFileName, defaultImageFileName) } func (s *ImageMetadataSuite) TestImageMetadataFilesDefaultArch(c *gc.C) { ctx := testing.Context(c) code := cmd.Main( &ImageMetadataCommand{}, ctx, []string{ "-d", s.dir, "-i", "1234", "-r", "region", "-u", "endpoint", "-s", "raring"}) c.Assert(code, gc.Equals, 0) out := testing.Stdout(ctx) expected := expectedMetadata{ series: "raring", arch: "amd64", } s.assertCommandOutput(c, expected, out, defaultIndexFileName, defaultImageFileName) } func (s *ImageMetadataSuite) TestImageMetadataFilesLatestLts(c *gc.C) { ctx := testing.Context(c) code := cmd.Main( &ImageMetadataCommand{}, ctx, []string{ "-d", s.dir, "-i", "1234", "-r", "region", "-a", "arch", "-u", "endpoint"}) c.Assert(code, gc.Equals, 0) out := testing.Stdout(ctx) expected := expectedMetadata{ series: config.LatestLtsSeries(), arch: "arch", } s.assertCommandOutput(c, expected, out, defaultIndexFileName, defaultImageFileName) } func (s *ImageMetadataSuite) TestImageMetadataFilesUsingEnv(c *gc.C) { ctx := testing.Context(c) code := cmd.Main( &ImageMetadataCommand{}, ctx, []string{"-d", s.dir, "-e", "ec2", "-i", "1234"}) c.Assert(code, gc.Equals, 0) out := testing.Stdout(ctx) expected := expectedMetadata{ series: "precise", arch: "amd64", region: "us-east-1", endpoint: "https://ec2.us-east-1.amazonaws.com", } s.assertCommandOutput(c, expected, out, defaultIndexFileName, defaultImageFileName) } func (s *ImageMetadataSuite) TestImageMetadataFilesUsingEnvWithRegionOverride(c *gc.C) { ctx := testing.Context(c) code := cmd.Main( &ImageMetadataCommand{}, ctx, []string{ "-d", s.dir, "-e", "ec2", "-r", "us-west-1", "-u", "https://ec2.us-west-1.amazonaws.com", "-i", "1234"}) c.Assert(code, gc.Equals, 0) out := testing.Stdout(ctx) expected := expectedMetadata{ series: "precise", arch: "amd64", region: "us-west-1", endpoint: "https://ec2.us-west-1.amazonaws.com", } s.assertCommandOutput(c, expected, out, defaultIndexFileName, defaultImageFileName) } func (s *ImageMetadataSuite) TestImageMetadataFilesUsingEnvWithNoHasRegion(c *gc.C) { ctx := testing.Context(c) code := cmd.Main( &ImageMetadataCommand{}, ctx, []string{ "-d", s.dir, "-e", "azure", "-r", "region", "-u", "endpoint", "-i", "1234"}) c.Assert(code, gc.Equals, 0) out := testing.Stdout(ctx) expected := expectedMetadata{ series: "raring", arch: "amd64", region: "region", endpoint: "endpoint", } s.assertCommandOutput(c, expected, out, defaultIndexFileName, defaultImageFileName) } type errTestParams struct { args []string } var errTests = []errTestParams{ { // Missing image id args: []string{"-r", "region", "-a", "arch", "-u", "endpoint", "-s", "precise"}, }, { // Missing region args: []string{"-i", "1234", "-a", "arch", "-u", "endpoint", "-s", "precise"}, }, { // Missing endpoint args: []string{"-i", "1234", "-u", "endpoint", "-a", "arch", "-s", "precise"}, }, { // Missing endpoint/region for environment with no HasRegion interface args: []string{"-i", "1234", "-e", "azure"}, }, } func (s *ImageMetadataSuite) TestImageMetadataBadArgs(c *gc.C) { defer testing.MakeEmptyFakeHome(c).Restore() for i, t := range errTests { c.Logf("test: %d", i) ctx := testing.Context(c) code := cmd.Main(&ImageMetadataCommand{}, ctx, t.args) c.Check(code, gc.Equals, 1) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/signmetadata.go��������������0000644�0000153�0000161�00000006054�12321735642�030641� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "io/ioutil" "os" "path/filepath" "strings" "github.com/juju/loggo" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs/simplestreams" ) var signMetadataDoc = ` sign searches for json files in the specified directory tree and inline signs them using the private key in the specified keyring file. For each .json file, a corresponding .sjson file is procduced. The specified keyring file is expected to contain an amored private key. If the key is encrypted, then the specified passphrase is used to decrypt the key. ` // SignMetadataCommand is used to sign simplestreams metadata json files. type SignMetadataCommand struct { cmd.CommandBase dir string keyFile string passphrase string } func (c *SignMetadataCommand) Info() *cmd.Info { return &cmd.Info{ Name: "sign", Purpose: "sign simplestreams metadata", Doc: signMetadataDoc, } } func (c *SignMetadataCommand) SetFlags(f *gnuflag.FlagSet) { c.CommandBase.SetFlags(f) f.StringVar(&c.dir, "d", "", "directory in which to look for metadata") f.StringVar(&c.keyFile, "k", "", "file containing the amored private signing key") f.StringVar(&c.passphrase, "p", "", "passphrase used to decrypt the private key") } func (c *SignMetadataCommand) Init(args []string) error { if c.dir == "" { return fmt.Errorf("directory must be specified") } if c.keyFile == "" { return fmt.Errorf("keyfile must be specified") } return cmd.CheckEmpty(args) } func (c *SignMetadataCommand) Run(context *cmd.Context) error { loggo.RegisterWriter("signmetadata", cmd.NewCommandLogWriter("juju.plugins.metadata", context.Stdout, context.Stderr), loggo.INFO) defer loggo.RemoveWriter("signmetadata") keyData, err := ioutil.ReadFile(c.keyFile) if err != nil { return err } dir := context.AbsPath(c.dir) return process(dir, string(keyData), c.passphrase) } func process(dir, key, passphrase string) error { logger.Debugf("processing directory %q", dir) // Do any json files in dir filenames, err := filepath.Glob(filepath.Join(dir, "*.json")) if len(filenames) > 0 { logger.Infof("signing %d file(s) in %q", len(filenames), dir) } for _, filename := range filenames { logger.Infof("signing file %q", filename) f, err := os.Open(filename) if err != nil { return fmt.Errorf("opening file %q: %v", filename, err) } encoded, err := simplestreams.Encode(f, key, passphrase) if err != nil { return fmt.Errorf("encoding file %q: %v", filename, err) } signedFilename := strings.Replace(filename, ".json", ".sjson", -1) if err = ioutil.WriteFile(signedFilename, encoded, 0644); err != nil { return fmt.Errorf("writing signed file %q: %v", signedFilename, err) } } // Now process any directories in dir. files, err := ioutil.ReadDir(dir) if err != nil { return err } for _, f := range files { if f.IsDir() { if err = process(filepath.Join(dir, f.Name()), key, passphrase); err != nil { return err } } } return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/toolsmetadata_test.go��������0000644�0000153�0000161�00000020163�12321735642�032075� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "fmt" "path/filepath" "regexp" "strings" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/configstore" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/environs/tools" ttesting "launchpad.net/juju-core/environs/tools/testing" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/provider/dummy" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) type ToolsMetadataSuite struct { testbase.LoggingSuite home *coretesting.FakeHome env environs.Environ publicStorageDir string } var _ = gc.Suite(&ToolsMetadataSuite{}) func (s *ToolsMetadataSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.home = coretesting.MakeSampleHome(c) s.AddCleanup(func(*gc.C) { s.home.Restore() dummy.Reset() loggo.ResetLoggers() }) env, err := environs.PrepareFromName("erewhemos", coretesting.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) s.env = env envtesting.RemoveAllTools(c, s.env) loggo.GetLogger("").SetLogLevel(loggo.INFO) // Switch the default tools location. s.publicStorageDir = c.MkDir() s.PatchValue(&tools.DefaultBaseURL, s.publicStorageDir) } var currentVersionStrings = []string{ // only these ones will make it into the JSON files. version.Current.Number.String() + "-quantal-amd64", version.Current.Number.String() + "-quantal-armhf", version.Current.Number.String() + "-quantal-i386", } var versionStrings = append([]string{ "1.12.0-precise-amd64", "1.12.0-precise-i386", "1.12.0-raring-amd64", "1.12.0-raring-i386", "1.13.0-precise-amd64", }, currentVersionStrings...) var expectedOutputCommon = makeExpectedOutputCommon() func makeExpectedOutputCommon() string { expected := `Finding tools in .* .*Fetching tools to generate hash: 1\.12\.0-precise-amd64 .*Fetching tools to generate hash: 1\.12\.0-precise-i386 .*Fetching tools to generate hash: 1\.12\.0-raring-amd64 .*Fetching tools to generate hash: 1\.12\.0-raring-i386 .*Fetching tools to generate hash: 1\.13\.0-precise-amd64 ` f := ".*Fetching tools to generate hash: %s\n" for _, v := range currentVersionStrings { expected += fmt.Sprintf(f, regexp.QuoteMeta(v)) } return strings.TrimSpace(expected) } var expectedOutputDirectory = expectedOutputCommon + ` .*Writing tools/streams/v1/index\.json .*Writing tools/streams/v1/com\.ubuntu\.juju:released:tools\.json ` var expectedOutputMirrors = expectedOutputCommon + ` .*Writing tools/streams/v1/index\.json .*Writing tools/streams/v1/com\.ubuntu\.juju:released:tools\.json .*Writing tools/streams/v1/mirrors\.json ` func (s *ToolsMetadataSuite) TestGenerateDefaultDirectory(c *gc.C) { metadataDir := osenv.JujuHome() // default metadata dir ttesting.MakeTools(c, metadataDir, "releases", versionStrings) ctx := coretesting.Context(c) code := cmd.Main(&ToolsMetadataCommand{}, ctx, nil) c.Assert(code, gc.Equals, 0) output := ctx.Stdout.(*bytes.Buffer).String() c.Assert(output, gc.Matches, expectedOutputDirectory) metadata := ttesting.ParseMetadataFromDir(c, metadataDir, false) c.Assert(metadata, gc.HasLen, len(versionStrings)) obtainedVersionStrings := make([]string, len(versionStrings)) for i, metadata := range metadata { s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch) obtainedVersionStrings[i] = s } c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings) } func (s *ToolsMetadataSuite) TestGenerateDirectory(c *gc.C) { metadataDir := c.MkDir() ttesting.MakeTools(c, metadataDir, "releases", versionStrings) ctx := coretesting.Context(c) code := cmd.Main(&ToolsMetadataCommand{}, ctx, []string{"-d", metadataDir}) c.Assert(code, gc.Equals, 0) output := ctx.Stdout.(*bytes.Buffer).String() c.Assert(output, gc.Matches, expectedOutputDirectory) metadata := ttesting.ParseMetadataFromDir(c, metadataDir, false) c.Assert(metadata, gc.HasLen, len(versionStrings)) obtainedVersionStrings := make([]string, len(versionStrings)) for i, metadata := range metadata { s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch) obtainedVersionStrings[i] = s } c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings) } func (s *ToolsMetadataSuite) TestGenerateWithPublicFallback(c *gc.C) { // Write tools and metadata to the public tools location. ttesting.MakeToolsWithCheckSum(c, s.publicStorageDir, "releases", versionStrings) // Run the command with no local metadata. ctx := coretesting.Context(c) metadataDir := c.MkDir() code := cmd.Main(&ToolsMetadataCommand{}, ctx, []string{"-d", metadataDir}) c.Assert(code, gc.Equals, 0) metadata := ttesting.ParseMetadataFromDir(c, metadataDir, false) c.Assert(metadata, gc.HasLen, len(versionStrings)) obtainedVersionStrings := make([]string, len(versionStrings)) for i, metadata := range metadata { s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch) obtainedVersionStrings[i] = s } c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings) } func (s *ToolsMetadataSuite) TestGenerateWithMirrors(c *gc.C) { metadataDir := c.MkDir() ttesting.MakeTools(c, metadataDir, "releases", versionStrings) ctx := coretesting.Context(c) code := cmd.Main(&ToolsMetadataCommand{}, ctx, []string{"--public", "-d", metadataDir}) c.Assert(code, gc.Equals, 0) output := ctx.Stdout.(*bytes.Buffer).String() c.Assert(output, gc.Matches, expectedOutputMirrors) metadata := ttesting.ParseMetadataFromDir(c, metadataDir, true) c.Assert(metadata, gc.HasLen, len(versionStrings)) obtainedVersionStrings := make([]string, len(versionStrings)) for i, metadata := range metadata { s := fmt.Sprintf("%s-%s-%s", metadata.Version, metadata.Release, metadata.Arch) obtainedVersionStrings[i] = s } c.Assert(obtainedVersionStrings, gc.DeepEquals, versionStrings) } func (s *ToolsMetadataSuite) TestNoTools(c *gc.C) { ctx := coretesting.Context(c) code := cmd.Main(&ToolsMetadataCommand{}, ctx, nil) c.Assert(code, gc.Equals, 1) stdout := ctx.Stdout.(*bytes.Buffer).String() c.Assert(stdout, gc.Matches, "Finding tools in .*\n") stderr := ctx.Stderr.(*bytes.Buffer).String() c.Assert(stderr, gc.Matches, "error: no tools available\n") } func (s *ToolsMetadataSuite) TestPatchLevels(c *gc.C) { currentVersion := version.Current.Number currentVersion.Build = 0 versionStrings := []string{ currentVersion.String() + "-precise-amd64", currentVersion.String() + ".1-precise-amd64", } metadataDir := osenv.JujuHome() // default metadata dir ttesting.MakeTools(c, metadataDir, "releases", versionStrings) ctx := coretesting.Context(c) code := cmd.Main(&ToolsMetadataCommand{}, ctx, nil) c.Assert(code, gc.Equals, 0) output := ctx.Stdout.(*bytes.Buffer).String() expectedOutput := fmt.Sprintf(` Finding tools in .* .*Fetching tools to generate hash: %s .*Fetching tools to generate hash: %s .*Writing tools/streams/v1/index\.json .*Writing tools/streams/v1/com\.ubuntu\.juju:released:tools\.json `[1:], regexp.QuoteMeta(versionStrings[0]), regexp.QuoteMeta(versionStrings[1])) c.Assert(output, gc.Matches, expectedOutput) metadata := ttesting.ParseMetadataFromDir(c, metadataDir, false) c.Assert(metadata, gc.HasLen, 2) filename := fmt.Sprintf("juju-%s-precise-amd64.tgz", currentVersion) size, sha256 := ttesting.SHA256sum(c, filepath.Join(metadataDir, "tools", "releases", filename)) c.Assert(metadata[0], gc.DeepEquals, &tools.ToolsMetadata{ Release: "precise", Version: currentVersion.String(), Arch: "amd64", Size: size, Path: "releases/" + filename, FileType: "tar.gz", SHA256: sha256, }) filename = fmt.Sprintf("juju-%s.1-precise-amd64.tgz", currentVersion) size, sha256 = ttesting.SHA256sum(c, filepath.Join(metadataDir, "tools", "releases", filename)) c.Assert(metadata[1], gc.DeepEquals, &tools.ToolsMetadata{ Release: "precise", Version: currentVersion.String() + ".1", Arch: "amd64", Size: size, Path: "releases/" + filename, FileType: "tar.gz", SHA256: sha256, }) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/validatetoolsmetadata.go�����0000644�0000153�0000161�00000016422�12321735776�032563� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // ValidateToolsMetadataCommand type ValidateToolsMetadataCommand struct { cmd.EnvCommandBase out cmd.Output providerType string metadataDir string series string region string endpoint string exactVersion string partVersion string major int minor int } var validateToolsMetadataDoc = ` validate-tools loads simplestreams metadata and validates the contents by looking for tools belonging to the specified series, architecture, for the specified cloud. If version is specified, tools matching the exact specified version are found. It is also possible to just specify the major (and optionally minor) version numbers to search for. The cloud specification comes from the current Juju environment, as specified in the usual way from either ~/.juju/environments.yaml, the -e option, or JUJU_ENV. Series, Region, and Endpoint are the key attributes. It is possible to specify a local directory containing tools metadata, in which case cloud attributes like provider type, region etc are optional. The key environment attributes may be overridden using command arguments, so that the validation may be peformed on arbitary metadata. Examples: - validate using the current environment settings but with series raring juju metadata validate-tools -s raring - validate using the current environment settings but with Juju version 1.11.4 juju metadata validate-tools -j 1.11.4 - validate using the current environment settings but with Juju major version 2 juju metadata validate-tools -m 2 - validate using the current environment settings but with Juju major.minor version 2.1 juju metadata validate-tools -m 2.1 - validate using the current environment settings and list all tools found for any series juju metadata validate-tools --series= - validate with series raring and using metadata from local directory juju metadata validate-images -s raring -d <some directory> A key use case is to validate newly generated metadata prior to deployment to production. In this case, the metadata is placed in a local directory, a cloud provider type is specified (ec2, openstack etc), and the validation is performed for each supported series, version, and arcgitecture. Example bash snippet: #!/bin/bash juju metadata validate-tools -p ec2 -r us-east-1 -s precise --juju-version 1.12.0 -d <some directory> RETVAL=$? [ $RETVAL -eq 0 ] && echo Success [ $RETVAL -ne 0 ] && echo Failure ` func (c *ValidateToolsMetadataCommand) Info() *cmd.Info { return &cmd.Info{ Name: "validate-tools", Purpose: "validate tools metadata and ensure tools tarball(s) exist for Juju version(s)", Doc: validateToolsMetadataDoc, } } func (c *ValidateToolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) c.out.AddFlags(f, "smart", cmd.DefaultFormatters) f.StringVar(&c.providerType, "p", "", "the provider type eg ec2, openstack") f.StringVar(&c.metadataDir, "d", "", "directory where metadata files are found") f.StringVar(&c.series, "s", "", "the series for which to validate (overrides env config series)") f.StringVar(&c.series, "series", "", "") f.StringVar(&c.region, "r", "", "the region for which to validate (overrides env config region)") f.StringVar(&c.endpoint, "u", "", "the cloud endpoint URL for which to validate (overrides env config endpoint)") f.StringVar(&c.exactVersion, "j", "current", "the Juju version (use 'current' for current version)") f.StringVar(&c.exactVersion, "juju-version", "", "") f.StringVar(&c.partVersion, "m", "", "the Juju major[.minor] version") f.StringVar(&c.partVersion, "majorminor-version", "", "") } func (c *ValidateToolsMetadataCommand) Init(args []string) error { if c.providerType != "" { if c.region == "" { return fmt.Errorf("region required if provider type is specified") } if c.metadataDir == "" { return fmt.Errorf("metadata directory required if provider type is specified") } } if c.exactVersion == "current" { c.exactVersion = version.Current.Number.String() } if c.partVersion != "" { var err error if c.major, c.minor, err = version.ParseMajorMinor(c.partVersion); err != nil { return err } } return c.EnvCommandBase.Init(args) } func (c *ValidateToolsMetadataCommand) Run(context *cmd.Context) error { var params *simplestreams.MetadataLookupParams if c.providerType == "" { store, err := configstore.Default() if err != nil { return err } environ, err := environs.PrepareFromName(c.EnvName, context, store) if err == nil { mdLookup, ok := environ.(simplestreams.MetadataValidator) if !ok { return fmt.Errorf("%s provider does not support tools metadata validation", environ.Config().Type()) } params, err = mdLookup.MetadataLookupParams(c.region) if err != nil { return err } params.Sources, err = tools.GetMetadataSources(environ) if err != nil { return err } } else { if c.metadataDir == "" { return err } params = &simplestreams.MetadataLookupParams{ Architectures: arch.AllSupportedArches, } } } else { prov, err := environs.Provider(c.providerType) if err != nil { return err } mdLookup, ok := prov.(simplestreams.MetadataValidator) if !ok { return fmt.Errorf("%s provider does not support tools metadata validation", c.providerType) } params, err = mdLookup.MetadataLookupParams(c.region) if err != nil { return err } } if c.series != "" { params.Series = c.series } if c.region != "" { params.Region = c.region } if c.endpoint != "" { params.Endpoint = c.endpoint } if c.metadataDir != "" { if _, err := os.Stat(c.metadataDir); err != nil { return err } toolsURL, err := tools.ToolsURL(c.metadataDir) if err != nil { return err } params.Sources = []simplestreams.DataSource{simplestreams.NewURLDataSource( "local metadata directory", toolsURL, utils.VerifySSLHostnames), } } versions, resolveInfo, err := tools.ValidateToolsMetadata(&tools.ToolsMetadataLookupParams{ MetadataLookupParams: *params, Version: c.exactVersion, Major: c.major, Minor: c.minor, }) if err != nil { if resolveInfo != nil { metadata := map[string]interface{}{ "Resolve Metadata": *resolveInfo, } if metadataYaml, yamlErr := cmd.FormatYaml(metadata); yamlErr == nil { err = fmt.Errorf("%v\n%v", err, string(metadataYaml)) } } return err } if len(versions) > 0 { metadata := map[string]interface{}{ "Matching Tools Versions": versions, "Resolve Metadata": *resolveInfo, } c.out.Write(context, metadata) } else { var sources []string for _, s := range params.Sources { url, err := s.URL("") if err == nil { sources = append(sources, fmt.Sprintf("- %s (%s)", s.Description(), url)) } } return fmt.Errorf("no matching tools using sources:\n%s", strings.Join(sources, "\n")) } return nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/validateimagemetadata.go�����0000644�0000153�0000161�00000015137�12321735776�032507� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "path/filepath" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/utils" ) // ValidateImageMetadataCommand type ValidateImageMetadataCommand struct { cmd.EnvCommandBase out cmd.Output providerType string metadataDir string series string region string endpoint string stream string } var validateImagesMetadataDoc = ` validate-images loads simplestreams metadata and validates the contents by looking for images belonging to the specified cloud. The cloud specification comes from the current Juju environment, as specified in the usual way from either ~/.juju/environments.yaml, the -e option, or JUJU_ENV. Series, Region, and Endpoint are the key attributes. The key environment attributes may be overridden using command arguments, so that the validation may be peformed on arbitary metadata. Examples: - validate using the current environment settings but with series raring juju metadata validate-images -s raring - validate using the current environment settings but with series raring and using metadata from local directory (the directory is expected to have an "images" subdirectory containing the metadata, and corresponds to the parameter passed to the image metadata generatation command). juju metadata validate-images -s raring -d <some directory> A key use case is to validate newly generated metadata prior to deployment to production. In this case, the metadata is placed in a local directory, a cloud provider type is specified (ec2, openstack etc), and the validation is performed for each supported region and series. Example bash snippet: #!/bin/bash juju metadata validate-images -p ec2 -r us-east-1 -s precise -d <some directory> RETVAL=$? [ $RETVAL -eq 0 ] && echo Success [ $RETVAL -ne 0 ] && echo Failure ` func (c *ValidateImageMetadataCommand) Info() *cmd.Info { return &cmd.Info{ Name: "validate-images", Purpose: "validate image metadata and ensure image(s) exist for an environment", Doc: validateImagesMetadataDoc, } } func (c *ValidateImageMetadataCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) c.out.AddFlags(f, "smart", cmd.DefaultFormatters) f.StringVar(&c.providerType, "p", "", "the provider type eg ec2, openstack") f.StringVar(&c.metadataDir, "d", "", "directory where metadata files are found") f.StringVar(&c.series, "s", "", "the series for which to validate (overrides env config series)") f.StringVar(&c.region, "r", "", "the region for which to validate (overrides env config region)") f.StringVar(&c.endpoint, "u", "", "the cloud endpoint URL for which to validate (overrides env config endpoint)") f.StringVar(&c.stream, "m", "", "the images stream (defaults to released)") } func (c *ValidateImageMetadataCommand) Init(args []string) error { if c.providerType != "" { if c.series == "" { return fmt.Errorf("series required if provider type is specified") } if c.region == "" { return fmt.Errorf("region required if provider type is specified") } if c.metadataDir == "" { return fmt.Errorf("metadata directory required if provider type is specified") } } return c.EnvCommandBase.Init(args) } var _ environs.ConfigGetter = (*overrideEnvStream)(nil) // overrideEnvStream implements environs.ConfigGetter and // ensures that the environs.Config returned by Config() // has the specified stream. type overrideEnvStream struct { env environs.Environ stream string } func (oes *overrideEnvStream) Config() *config.Config { cfg := oes.env.Config() // If no stream specified, just use default from environ. if oes.stream == "" { return cfg } newCfg, err := cfg.Apply(map[string]interface{}{"image-stream": oes.stream}) if err != nil { // This should never happen. panic(fmt.Errorf("unexpected error making override config: %v", err)) } return newCfg } func (c *ValidateImageMetadataCommand) Run(context *cmd.Context) error { var params *simplestreams.MetadataLookupParams if c.providerType == "" { store, err := configstore.Default() if err != nil { return err } environ, err := environs.PrepareFromName(c.EnvName, context, store) if err != nil { return err } mdLookup, ok := environ.(simplestreams.MetadataValidator) if !ok { return fmt.Errorf("%s provider does not support image metadata validation", environ.Config().Type()) } params, err = mdLookup.MetadataLookupParams(c.region) if err != nil { return err } oes := &overrideEnvStream{environ, c.stream} params.Sources, err = imagemetadata.GetMetadataSources(oes) if err != nil { return err } } else { prov, err := environs.Provider(c.providerType) if err != nil { return err } mdLookup, ok := prov.(simplestreams.MetadataValidator) if !ok { return fmt.Errorf("%s provider does not support image metadata validation", c.providerType) } params, err = mdLookup.MetadataLookupParams(c.region) if err != nil { return err } } if c.series != "" { params.Series = c.series } if c.region != "" { params.Region = c.region } if c.endpoint != "" { params.Endpoint = c.endpoint } if c.metadataDir != "" { dir := filepath.Join(c.metadataDir, "images") if _, err := os.Stat(dir); err != nil { return err } params.Sources = []simplestreams.DataSource{ simplestreams.NewURLDataSource( "local metadata directory", "file://"+dir, utils.VerifySSLHostnames), } } params.Stream = c.stream image_ids, resolveInfo, err := imagemetadata.ValidateImageMetadata(params) if err != nil { if resolveInfo != nil { metadata := map[string]interface{}{ "Resolve Metadata": *resolveInfo, } if metadataYaml, yamlErr := cmd.FormatYaml(metadata); yamlErr == nil { err = fmt.Errorf("%v\n%v", err, string(metadataYaml)) } } return err } if len(image_ids) > 0 { metadata := map[string]interface{}{ "ImageIds": image_ids, "Region": params.Region, "Resolve Metadata": *resolveInfo, } c.out.Write(context, metadata) } else { var sources []string for _, s := range params.Sources { url, err := s.URL("") if err == nil { sources = append(sources, fmt.Sprintf("- %s (%s)", s.Description(), url)) } } return fmt.Errorf( "no matching image ids for region %s using sources:\n%s", params.Region, strings.Join(sources, "\n")) } return nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/imagemetadata.go�������������0000644�0000153�0000161�00000012670�12321735776�030774� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "path/filepath" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/juju/arch" ) // ImageMetadataCommand is used to write out simplestreams image metadata information. type ImageMetadataCommand struct { cmd.EnvCommandBase Dir string Series string Arch string ImageId string Region string Endpoint string privateStorage string } var imageMetadataDoc = ` generate-image creates simplestreams image metadata for the specified cloud. The cloud specification comes from the current Juju environment, as specified in the usual way from either ~/.juju/environments.yaml, the -e option, or JUJU_ENV. Using command arguments, it is possible to override cloud attributes region, endpoint, and series. By default, "amd64" is used for the architecture but this may also be changed. ` func (c *ImageMetadataCommand) Info() *cmd.Info { return &cmd.Info{ Name: "generate-image", Purpose: "generate simplestreams image metadata", Doc: imageMetadataDoc, } } func (c *ImageMetadataCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.StringVar(&c.Series, "s", "", "the charm series") f.StringVar(&c.Arch, "a", arch.AMD64, "the image achitecture") f.StringVar(&c.Dir, "d", "", "the destination directory in which to place the metadata files") f.StringVar(&c.ImageId, "i", "", "the image id") f.StringVar(&c.Region, "r", "", "the region") f.StringVar(&c.Endpoint, "u", "", "the cloud endpoint (for Openstack, this is the Identity Service endpoint)") } func (c *ImageMetadataCommand) Init(args []string) error { return cmd.CheckEmpty(args) } // setParams sets parameters based on the environment configuration // for those which have not been explicitly specified. func (c *ImageMetadataCommand) setParams(context *cmd.Context) error { c.privateStorage = "<private storage name>" var environ environs.Environ if store, err := configstore.Default(); err == nil { if environ, err = environs.PrepareFromName(c.EnvName, context, store); err == nil { logger.Infof("creating image metadata for environment %q", environ.Name()) // If the user has not specified region and endpoint, try and get it from the environment. if c.Region == "" || c.Endpoint == "" { var cloudSpec simplestreams.CloudSpec if inst, ok := environ.(simplestreams.HasRegion); ok { if cloudSpec, err = inst.Region(); err != nil { return err } } else { return fmt.Errorf("environment %q cannot provide region and endpoint", environ.Name()) } // If only one of region or endpoint is provided, that is a problem. if cloudSpec.Region != cloudSpec.Endpoint && (cloudSpec.Region == "" || cloudSpec.Endpoint == "") { return fmt.Errorf("cannot generate metadata without a complete cloud configuration") } if c.Region == "" { c.Region = cloudSpec.Region } if c.Endpoint == "" { c.Endpoint = cloudSpec.Endpoint } } cfg := environ.Config() if c.Series == "" { c.Series = config.PreferredSeries(cfg) } if v, ok := cfg.AllAttrs()["control-bucket"]; ok { c.privateStorage = v.(string) } } else { logger.Warningf("environment %q could not be opened: %v", c.EnvName, err) } } if environ == nil { logger.Infof("no environment found, creating image metadata using user supplied data") } if c.Series == "" { c.Series = config.LatestLtsSeries() } if c.ImageId == "" { return fmt.Errorf("image id must be specified") } if c.Region == "" { return fmt.Errorf("image region must be specified") } if c.Endpoint == "" { return fmt.Errorf("cloud endpoint URL must be specified") } if c.Dir == "" { logger.Infof("no destination directory specified, using current directory") var err error if c.Dir, err = os.Getwd(); err != nil { return err } } return nil } var helpDoc = ` image metadata files have been written to: %s. For Juju to use this metadata, the files need to be put into the image metadata search path. There are 2 options: 1. Use the --metadata-source parameter when bootstrapping: juju bootstrap --metadata-source %s 2. Use image-metadata-url in $JUJU_HOME/environments.yaml Configure a http server to serve the contents of %s and set the value of image-metadata-url accordingly. " ` func (c *ImageMetadataCommand) Run(context *cmd.Context) error { if err := c.setParams(context); err != nil { return err } out := context.Stdout im := &imagemetadata.ImageMetadata{ Id: c.ImageId, Arch: c.Arch, } cloudSpec := simplestreams.CloudSpec{ Region: c.Region, Endpoint: c.Endpoint, } targetStorage, err := filestorage.NewFileStorageWriter(c.Dir) if err != nil { return err } err = imagemetadata.MergeAndWriteMetadata(c.Series, []*imagemetadata.ImageMetadata{im}, &cloudSpec, targetStorage) if err != nil { return fmt.Errorf("image metadata files could not be created: %v", err) } dir := context.AbsPath(c.Dir) dest := filepath.Join(dir, storage.BaseImagesPath, "streams", "v1") fmt.Fprintf(out, fmt.Sprintf(helpDoc, dest, dir, dir)) return nil } ������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/validatetoolsmetadata_test.go0000644�0000153�0000161�00000022211�12321735642�033603� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "strings" "launchpad.net/goamz/aws" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/tools" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) type ValidateToolsMetadataSuite struct { testbase.LoggingSuite home *coretesting.FakeHome metadataDir string } var _ = gc.Suite(&ValidateToolsMetadataSuite{}) func runValidateToolsMetadata(c *gc.C, args ...string) error { _, err := coretesting.RunCommand(c, &ValidateToolsMetadataCommand{}, args) return err } var validateInitToolsErrorTests = []struct { args []string err string }{ { args: []string{"-p", "ec2", "-s", "series", "-d", "dir"}, err: `region required if provider type is specified`, }, { args: []string{"-p", "ec2", "-s", "series", "-r", "region"}, err: `metadata directory required if provider type is specified`, }, { args: []string{"-s", "series", "-r", "region", "-m", "x"}, err: `invalid major version number x: .*`, }, { args: []string{"-s", "series", "-r", "region", "-m", "2.x"}, err: `invalid minor version number x: .*`, }, { args: []string{"-s", "series", "-r", "region", "-m", "2.2.1"}, err: `invalid major.minor version number 2.2.1`, }, } func (s *ValidateToolsMetadataSuite) TestInitErrors(c *gc.C) { for i, t := range validateInitToolsErrorTests { c.Logf("test %d", i) err := coretesting.InitCommand(&ValidateToolsMetadataCommand{}, t.args) c.Check(err, gc.ErrorMatches, t.err) } } func (s *ValidateToolsMetadataSuite) TestInvalidProviderError(c *gc.C) { err := runValidateToolsMetadata(c, "-p", "foo", "-s", "series", "-r", "region", "-d", "dir") c.Check(err, gc.ErrorMatches, `no registered provider for "foo"`) } func (s *ValidateToolsMetadataSuite) TestUnsupportedProviderError(c *gc.C) { err := runValidateToolsMetadata(c, "-p", "local", "-s", "series", "-r", "region", "-d", "dir") c.Check(err, gc.ErrorMatches, `local provider does not support tools metadata validation`) } func (s *ValidateToolsMetadataSuite) makeLocalMetadata(c *gc.C, version, region, series, endpoint string) error { tm := []*tools.ToolsMetadata{{ Version: version, Arch: "amd64", Release: series, }} targetStorage, err := filestorage.NewFileStorageWriter(s.metadataDir) c.Assert(err, gc.IsNil) err = tools.WriteMetadata(targetStorage, tm, false) if err != nil { return err } return nil } func (s *ValidateToolsMetadataSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.home = coretesting.MakeFakeHome(c, metadataTestEnvConfig) s.metadataDir = c.MkDir() s.PatchEnvironment("AWS_ACCESS_KEY_ID", "access") s.PatchEnvironment("AWS_SECRET_ACCESS_KEY", "secret") } func (s *ValidateToolsMetadataSuite) TearDownTest(c *gc.C) { s.home.Restore() s.LoggingSuite.TearDownTest(c) } func (s *ValidateToolsMetadataSuite) setupEc2LocalMetadata(c *gc.C, region string) { ec2Region, ok := aws.Regions[region] if !ok { c.Fatalf("unknown ec2 region %q", region) } endpoint := ec2Region.EC2Endpoint s.makeLocalMetadata(c, "1.11.4", region, "precise", endpoint) } func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataUsingEnvironment(c *gc.C) { s.setupEc2LocalMetadata(c, "us-east-1") ctx := coretesting.Context(c) code := cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{"-e", "ec2", "-j", "1.11.4", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 0) errOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`) } func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataUsingIncompleteEnvironment(c *gc.C) { s.PatchEnvironment("AWS_ACCESS_KEY_ID", "") s.PatchEnvironment("AWS_SECRET_ACCESS_KEY", "") s.setupEc2LocalMetadata(c, "us-east-1") ctx := coretesting.Context(c) code := cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{"-e", "ec2", "-j", "1.11.4"}, ) c.Assert(code, gc.Equals, 1) errOut := ctx.Stderr.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `error: environment has no access-key or secret-key`) } func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataWithManualParams(c *gc.C) { s.setupEc2LocalMetadata(c, "us-west-1") ctx := coretesting.Context(c) code := cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{ "-p", "ec2", "-s", "precise", "-r", "us-west-1", "-j", "1.11.4", "-u", "https://ec2.us-west-1.amazonaws.com", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 0) errOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`) } func (s *ValidateToolsMetadataSuite) TestEc2LocalMetadataNoMatch(c *gc.C) { s.setupEc2LocalMetadata(c, "us-east-1") ctx := coretesting.Context(c) code := cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{ "-p", "ec2", "-s", "raring", "-r", "us-west-1", "-u", "https://ec2.us-west-1.amazonaws.com", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 1) code = cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{ "-p", "ec2", "-s", "precise", "-r", "region", "-u", "https://ec2.region.amazonaws.com", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 1) errOut := ctx.Stderr.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `.*Resolve Metadata:.*`) } func (s *ValidateToolsMetadataSuite) TestOpenstackLocalMetadataWithManualParams(c *gc.C) { s.makeLocalMetadata(c, "1.11.4", "region-2", "raring", "some-auth-url") ctx := coretesting.Context(c) code := cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{ "-p", "openstack", "-s", "raring", "-r", "region-2", "-j", "1.11.4", "-u", "some-auth-url", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 0) errOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`) } func (s *ValidateToolsMetadataSuite) TestOpenstackLocalMetadataNoMatch(c *gc.C) { s.makeLocalMetadata(c, "1.11.4", "region-2", "raring", "some-auth-url") ctx := coretesting.Context(c) code := cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{ "-p", "openstack", "-s", "precise", "-r", "region-2", "-u", "some-auth-url", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 1) code = cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{ "-p", "openstack", "-s", "raring", "-r", "region-3", "-u", "some-auth-url", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 1) errOut := ctx.Stderr.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `.*Resolve Metadata:.*`) } func (s *ValidateToolsMetadataSuite) TestDefaultVersion(c *gc.C) { s.makeLocalMetadata(c, version.Current.Number.String(), "region-2", "raring", "some-auth-url") ctx := coretesting.Context(c) code := cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{ "-p", "openstack", "-s", "raring", "-r", "region-2", "-u", "some-auth-url", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 0) errOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`) } func (s *ValidateToolsMetadataSuite) TestMajorVersionMatch(c *gc.C) { s.makeLocalMetadata(c, "1.11.4", "region-2", "raring", "some-auth-url") ctx := coretesting.Context(c) code := cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{ "-p", "openstack", "-s", "raring", "-r", "region-2", "-u", "some-auth-url", "-d", s.metadataDir, "-m", "1"}, ) c.Assert(code, gc.Equals, 0) errOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`) } func (s *ValidateToolsMetadataSuite) TestMajorMinorVersionMatch(c *gc.C) { s.makeLocalMetadata(c, "1.12.1", "region-2", "raring", "some-auth-url") ctx := coretesting.Context(c) code := cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{ "-p", "openstack", "-s", "raring", "-r", "region-2", "-u", "some-auth-url", "-d", s.metadataDir, "-m", "1.12"}, ) c.Assert(code, gc.Equals, 0) errOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`) } func (s *ValidateToolsMetadataSuite) TestJustDirectory(c *gc.C) { s.makeLocalMetadata(c, version.Current.Number.String(), "region-2", "raring", "some-auth-url") ctx := coretesting.Context(c) code := cmd.Main( &ValidateToolsMetadataCommand{}, ctx, []string{"-s", "raring", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 0) errOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `Matching Tools Versions:.*Resolve Metadata.*`) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/validateimagemetadata_test.go0000644�0000153�0000161�00000017617�12321735642�033543� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "strings" "launchpad.net/goamz/aws" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type ValidateImageMetadataSuite struct { testbase.LoggingSuite home *coretesting.FakeHome metadataDir string } var _ = gc.Suite(&ValidateImageMetadataSuite{}) func runValidateImageMetadata(c *gc.C, args ...string) error { _, err := coretesting.RunCommand(c, &ValidateImageMetadataCommand{}, args) return err } var validateInitImageErrorTests = []struct { args []string err string }{ { args: []string{"-p", "ec2", "-r", "region", "-d", "dir"}, err: `series required if provider type is specified`, }, { args: []string{"-p", "ec2", "-s", "series", "-d", "dir"}, err: `region required if provider type is specified`, }, { args: []string{"-p", "ec2", "-s", "series", "-r", "region"}, err: `metadata directory required if provider type is specified`, }, } func (s *ValidateImageMetadataSuite) TestInitErrors(c *gc.C) { for i, t := range validateInitImageErrorTests { c.Logf("test %d", i) err := coretesting.InitCommand(&ValidateImageMetadataCommand{}, t.args) c.Check(err, gc.ErrorMatches, t.err) } } func (s *ValidateImageMetadataSuite) TestInvalidProviderError(c *gc.C) { err := runValidateImageMetadata(c, "-p", "foo", "-s", "series", "-r", "region", "-d", "dir") c.Check(err, gc.ErrorMatches, `no registered provider for "foo"`) } func (s *ValidateImageMetadataSuite) TestUnsupportedProviderError(c *gc.C) { err := runValidateImageMetadata(c, "-p", "local", "-s", "series", "-r", "region", "-d", "dir") c.Check(err, gc.ErrorMatches, `local provider does not support image metadata validation`) } func (s *ValidateImageMetadataSuite) makeLocalMetadata(c *gc.C, id, region, series, endpoint, stream string) error { im := &imagemetadata.ImageMetadata{ Id: id, Arch: "amd64", Stream: stream, } cloudSpec := simplestreams.CloudSpec{ Region: region, Endpoint: endpoint, } targetStorage, err := filestorage.NewFileStorageWriter(s.metadataDir) if err != nil { return err } err = imagemetadata.MergeAndWriteMetadata(series, []*imagemetadata.ImageMetadata{im}, &cloudSpec, targetStorage) if err != nil { return err } return nil } const metadataTestEnvConfig = ` environments: ec2: type: ec2 default-series: precise region: us-east-1 azure: type: azure default-series: raring location: US West management-subscription-id: foo storage-account-name: bar management-certificate-path: /home/me/azure.pem ` func (s *ValidateImageMetadataSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.metadataDir = c.MkDir() s.home = coretesting.MakeFakeHome(c, metadataTestEnvConfig) s.PatchEnvironment("AWS_ACCESS_KEY_ID", "access") s.PatchEnvironment("AWS_SECRET_ACCESS_KEY", "secret") } func (s *ValidateImageMetadataSuite) TearDownTest(c *gc.C) { s.home.Restore() s.LoggingSuite.TearDownTest(c) } func (s *ValidateImageMetadataSuite) setupEc2LocalMetadata(c *gc.C, region, stream string) { ec2Region, ok := aws.Regions[region] if !ok { c.Fatalf("unknown ec2 region %q", region) } endpoint := ec2Region.EC2Endpoint s.makeLocalMetadata(c, "1234", region, "precise", endpoint, stream) } func (s *ValidateImageMetadataSuite) assertEc2LocalMetadataUsingEnvironment(c *gc.C, stream string) { s.setupEc2LocalMetadata(c, "us-east-1", stream) ctx := coretesting.Context(c) code := cmd.Main( &ValidateImageMetadataCommand{}, ctx, []string{"-e", "ec2", "-d", s.metadataDir, "-m", stream}, ) c.Assert(code, gc.Equals, 0) errOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check( strippedOut, gc.Matches, `ImageIds:.*"1234".*Region:.*us-east-1.*Resolve Metadata:.*source: local metadata directory.*`) } func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataUsingEnvironment(c *gc.C) { s.assertEc2LocalMetadataUsingEnvironment(c, "") s.assertEc2LocalMetadataUsingEnvironment(c, imagemetadata.ReleasedStream) s.assertEc2LocalMetadataUsingEnvironment(c, "daily") } func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataUsingIncompleteEnvironment(c *gc.C) { s.PatchEnvironment("AWS_ACCESS_KEY_ID", "") s.PatchEnvironment("AWS_SECRET_ACCESS_KEY", "") s.PatchEnvironment("EC2_ACCESS_KEY", "") s.PatchEnvironment("EC2_SECRET_KEY", "") s.setupEc2LocalMetadata(c, "us-east-1", "") ctx := coretesting.Context(c) code := cmd.Main( &ValidateImageMetadataCommand{}, ctx, []string{"-e", "ec2", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 1) errOut := ctx.Stderr.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `error: environment has no access-key or secret-key`) } func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataWithManualParams(c *gc.C) { s.setupEc2LocalMetadata(c, "us-west-1", "") ctx := coretesting.Context(c) code := cmd.Main( &ValidateImageMetadataCommand{}, ctx, []string{ "-p", "ec2", "-s", "precise", "-r", "us-west-1", "-u", "https://ec2.us-west-1.amazonaws.com", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 0) errOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check( strippedOut, gc.Matches, `ImageIds:.*"1234".*Region:.*us-west-1.*Resolve Metadata:.*source: local metadata directory.*`) } func (s *ValidateImageMetadataSuite) TestEc2LocalMetadataNoMatch(c *gc.C) { s.setupEc2LocalMetadata(c, "us-east-1", "") ctx := coretesting.Context(c) code := cmd.Main( &ValidateImageMetadataCommand{}, ctx, []string{ "-p", "ec2", "-s", "raring", "-r", "us-west-1", "-u", "https://ec2.us-west-1.amazonaws.com", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 1) code = cmd.Main( &ValidateImageMetadataCommand{}, ctx, []string{ "-p", "ec2", "-s", "precise", "-r", "region", "-u", "https://ec2.region.amazonaws.com", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 1) errOut := ctx.Stderr.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `.*Resolve Metadata:.*`) } func (s *ValidateImageMetadataSuite) TestOpenstackLocalMetadataWithManualParams(c *gc.C) { s.makeLocalMetadata(c, "1234", "region-2", "raring", "some-auth-url", "") ctx := coretesting.Context(c) code := cmd.Main( &ValidateImageMetadataCommand{}, ctx, []string{ "-p", "openstack", "-s", "raring", "-r", "region-2", "-u", "some-auth-url", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 0) errOut := ctx.Stdout.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check( strippedOut, gc.Matches, `ImageIds:.*"1234".*Region:.*region-2.*Resolve Metadata:.*source: local metadata directory.*`) } func (s *ValidateImageMetadataSuite) TestOpenstackLocalMetadataNoMatch(c *gc.C) { s.makeLocalMetadata(c, "1234", "region-2", "raring", "some-auth-url", "") ctx := coretesting.Context(c) code := cmd.Main( &ValidateImageMetadataCommand{}, ctx, []string{ "-p", "openstack", "-s", "precise", "-r", "region-2", "-u", "some-auth-url", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 1) errOut := ctx.Stderr.(*bytes.Buffer).String() strippedOut := strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `.*Resolve Metadata:.*`) code = cmd.Main( &ValidateImageMetadataCommand{}, ctx, []string{ "-p", "openstack", "-s", "raring", "-r", "region-3", "-u", "some-auth-url", "-d", s.metadataDir}, ) c.Assert(code, gc.Equals, 1) errOut = ctx.Stderr.(*bytes.Buffer).String() strippedOut = strings.Replace(errOut, "\n", "", -1) c.Check(strippedOut, gc.Matches, `.*Resolve Metadata:.*`) } �����������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/toolsmetadata.go�������������0000644�0000153�0000161�00000006257�12321735776�031056� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "github.com/juju/loggo" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/juju/osenv" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // ToolsMetadataCommand is used to generate simplestreams metadata for juju tools. type ToolsMetadataCommand struct { cmd.EnvCommandBase fetch bool metadataDir string public bool } func (c *ToolsMetadataCommand) Info() *cmd.Info { return &cmd.Info{ Name: "generate-tools", Purpose: "generate simplestreams tools metadata", } } func (c *ToolsMetadataCommand) SetFlags(f *gnuflag.FlagSet) { c.EnvCommandBase.SetFlags(f) f.StringVar(&c.metadataDir, "d", "", "local directory in which to store metadata") f.BoolVar(&c.public, "public", false, "tools are for a public cloud, so generate mirrors information") } func (c *ToolsMetadataCommand) Run(context *cmd.Context) error { loggo.RegisterWriter("toolsmetadata", cmd.NewCommandLogWriter("juju.environs.tools", context.Stdout, context.Stderr), loggo.INFO) defer loggo.RemoveWriter("toolsmetadata") if c.metadataDir == "" { c.metadataDir = osenv.JujuHome() } else { c.metadataDir = context.AbsPath(c.metadataDir) } sourceStorage, err := filestorage.NewFileStorageReader(c.metadataDir) if err != nil { return err } fmt.Fprintf(context.Stdout, "Finding tools in %s\n", c.metadataDir) const minorVersion = -1 toolsList, err := envtools.ReadList(sourceStorage, version.Current.Major, minorVersion) if err == envtools.ErrNoTools { var source string source, err = envtools.ToolsURL(envtools.DefaultBaseURL) if err != nil { return err } sourceDataSource := simplestreams.NewURLDataSource("local source", source, utils.VerifySSLHostnames) toolsList, err = envtools.FindToolsForCloud( []simplestreams.DataSource{sourceDataSource}, simplestreams.CloudSpec{}, version.Current.Major, minorVersion, coretools.Filter{}) } if err != nil { return err } targetStorage, err := filestorage.NewFileStorageWriter(c.metadataDir) if err != nil { return err } writeMirrors := envtools.DoNotWriteMirrors if c.public { writeMirrors = envtools.WriteMirrors } return mergeAndWriteMetadata(targetStorage, toolsList, writeMirrors) } // This is essentially the same as tools.MergeAndWriteMetadata, but also // resolves metadata for existing tools by fetching them and computing // size/sha256 locally. func mergeAndWriteMetadata(stor storage.Storage, toolsList coretools.List, writeMirrors envtools.ShouldWriteMirrors) error { existing, err := envtools.ReadMetadata(stor) if err != nil { return err } metadata := envtools.MetadataFromTools(toolsList) if metadata, err = envtools.MergeMetadata(metadata, existing); err != nil { return err } if err = envtools.ResolveMetadata(stor, metadata); err != nil { return err } return envtools.WriteMetadata(stor, metadata, writeMirrors) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-metadata/signmetadata_test.go���������0000644�0000153�0000161�00000005601�12321735642�031675� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "bytes" "io/ioutil" "os" "path/filepath" "strings" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs/simplestreams" sstesting "launchpad.net/juju-core/environs/simplestreams/testing" coretesting "launchpad.net/juju-core/testing" ) type SignMetadataSuite struct{} var _ = gc.Suite(&SignMetadataSuite{}) func (s *SignMetadataSuite) SetUpTest(c *gc.C) { loggo.GetLogger("").SetLogLevel(loggo.INFO) } func (s *SignMetadataSuite) TearDownTest(c *gc.C) { loggo.ResetLoggers() } var expectedLoggingOutput = `signing 2 file\(s\) in .*subdir1.* signing file .*file1\.json.* signing file .*file2\.json.* signing 1 file\(s\) in .*subdir2.* signing file .*file3\.json.* ` func makeFileNames(topLevel string) []string { return []string{ filepath.Join(topLevel, "subdir1", "file1.json"), filepath.Join(topLevel, "subdir1", "file2.json"), filepath.Join(topLevel, "subdir1", "subdir2", "file3.json"), } } func setupJsonFiles(c *gc.C, topLevel string) { err := os.MkdirAll(filepath.Join(topLevel, "subdir1", "subdir2"), 0700) c.Assert(err, gc.IsNil) content := []byte("hello world") filenames := makeFileNames(topLevel) for _, filename := range filenames { err = ioutil.WriteFile(filename, content, 0644) c.Assert(err, gc.IsNil) } } func assertSignedFile(c *gc.C, filename string) { r, err := os.Open(filename) c.Assert(err, gc.IsNil) defer r.Close() data, err := simplestreams.DecodeCheckSignature(r, sstesting.SignedMetadataPublicKey) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "hello world\n") } func assertSignedFiles(c *gc.C, topLevel string) { filenames := makeFileNames(topLevel) for _, filename := range filenames { filename = strings.Replace(filename, ".json", ".sjson", -1) assertSignedFile(c, filename) } } func (s *SignMetadataSuite) TestSignMetadata(c *gc.C) { topLevel := c.MkDir() keyfile := filepath.Join(topLevel, "privatekey.asc") err := ioutil.WriteFile(keyfile, []byte(sstesting.SignedMetadataPrivateKey), 0644) c.Assert(err, gc.IsNil) setupJsonFiles(c, topLevel) ctx := coretesting.Context(c) code := cmd.Main( &SignMetadataCommand{}, ctx, []string{"-d", topLevel, "-k", keyfile, "-p", sstesting.PrivateKeyPassphrase}) c.Assert(code, gc.Equals, 0) output := ctx.Stdout.(*bytes.Buffer).String() c.Assert(output, gc.Matches, expectedLoggingOutput) assertSignedFiles(c, topLevel) } func runSignMetadata(c *gc.C, args ...string) error { _, err := coretesting.RunCommand(c, &SignMetadataCommand{}, args) return err } func (s *SignMetadataSuite) TestSignMetadataErrors(c *gc.C) { err := runSignMetadata(c, "") c.Assert(err, gc.ErrorMatches, `directory must be specified`) err = runSignMetadata(c, "-d", "foo") c.Assert(err, gc.ErrorMatches, `keyfile must be specified`) } �������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-backup/�������������������������������0000755�0000153�0000161�00000000000�12321735642�025331� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/plugins/juju-backup/juju-backup��������������������0000755�0000153�0000161�00000006465�12321735642�027512� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash remote_cmd() { LIBJUJU=/var/lib/juju # usage: execute message cmd [arg...] # Execute the given command with the given arguments and exit with an # error on failure. The first argument (message) describes the command. execute() { MSG=$1 shift echo -n $MSG..... ERR=$( "$@" 2>&1 ) || { echo FAILED echo '------------------------------------------------------------' echo "Command failed: $*" echo "Error: $ERR" echo '------------------------------------------------------------' exit 1 } echo SUCCESS } next_step() { echo echo '**************************************************************' echo $1 echo '**************************************************************' } cd ~ # Make sure we've started in $HOME next_step 'Preparing to perform backup' if [ -e juju-backup.tgz ]; then echo Older juju backup exists, moving to juju-backup-previous execute 'Removing existing backup archive' rm -rf juju-backup-previous.tgz execute 'Archiving backup' mv juju-backup.tgz juju-backup-previous.tgz fi execute 'Making backup directory' mkdir juju-backup cd juju-backup # Mongo requires that a locale is set export LC_ALL=C #--------------------------------------------------------------------- next_step 'Backing up mongo database' execute 'Stopping mongo' stop juju-db trap "start juju-db" 0 # ensure it starts again on failure execute 'Backing up mongo' mongodump --dbpath $LIBJUJU/db execute 'Backing up environ config' mongoexport \ --dbpath $LIBJUJU/db \ --db juju \ --collection settings \ --out environconfig.json execute 'Starting mongo' start juju-db trap - 0 next_step 'Copying Juju configuration' copy_files() { # Make an archive within the main archive so that we # can easily preserve file ownership and other metadata. tar -cf root.tar "$@" 2>&1 | (grep -v 'Removing leading'; true) } # Make copies of: # - Upstart configuration files for juju-db, machine agent, but not any unit agents. # - Agent configuration directories in $LIBJUJU. # (includes the config, server.pem, tools, but not any unit agents) # - SSH authorized keys. # - /etc/rsyslog.d/*juju* config files for the agents (ignore any unit agents) # - Juju logs for machine 0 and all machines. execute 'Archiving selected files' copy_files \ /etc/init/juju-db.conf \ /etc/init/jujud-machine-*.conf \ $LIBJUJU/agents/machine-* \ $LIBJUJU/tools \ $LIBJUJU/server.pem \ ~/.ssh/authorized_keys \ /etc/rsyslog.d/*juju.conf \ /var/log/juju/all-machines.log \ /var/log/juju/machine-0.log \ #--------------------------------------------------------------------- next_step 'Creating tarball' cd .. execute 'Performing tar' tar -czf juju-backup.tgz juju-backup rm -r juju-backup execute 'Changing ownership of backup archive to ubuntu' chown -R ubuntu.ubuntu juju-backup* echo echo Juju backup finished. echo } # Run the backup script on the remote machine. REMOTE_SCRIPT=" $(declare -f remote_cmd) remote_cmd " QUOTED_SCRIPT="'$(echo "$REMOTE_SCRIPT" | sed "s/'/'\"'\"'/g")'" echo Connecting to machine 0 juju ssh 0 "sudo -n bash -c $QUOTED_SCRIPT" && { # The backup has succeeded; copy backup tarball locally. NOW=$(date '+%Y%m%d-%H%M') FILENAME=juju-backup-$NOW.tgz echo "Copying tarball to `pwd`/$FILENAME ..." juju scp 0:~/juju-backup.tgz ./$FILENAME } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/environmentcommand_test.go�������������������������0000644�0000153�0000161�00000004452�12321735776�026736� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd_test import ( "io/ioutil" "os" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/testing" ) type EnvironmentCommandSuite struct { home *testing.FakeHome } var _ = gc.Suite(&EnvironmentCommandSuite{}) func (s *EnvironmentCommandSuite) SetUpTest(c *gc.C) { s.home = testing.MakeEmptyFakeHome(c) } func (s *EnvironmentCommandSuite) TearDownTest(c *gc.C) { s.home.Restore() } func (s *EnvironmentCommandSuite) TestReadCurrentEnvironmentUnset(c *gc.C) { env := cmd.ReadCurrentEnvironment() c.Assert(env, gc.Equals, "") } func (s *EnvironmentCommandSuite) TestReadCurrentEnvironmentSet(c *gc.C) { err := cmd.WriteCurrentEnvironment("fubar") c.Assert(err, gc.IsNil) env := cmd.ReadCurrentEnvironment() c.Assert(env, gc.Equals, "fubar") } func (s *EnvironmentCommandSuite) TestGetDefaultEnvironmentNothingSet(c *gc.C) { env := cmd.GetDefaultEnvironment() c.Assert(env, gc.Equals, "") } func (s *EnvironmentCommandSuite) TestGetDefaultEnvironmentCurrentEnvironmentSet(c *gc.C) { err := cmd.WriteCurrentEnvironment("fubar") c.Assert(err, gc.IsNil) env := cmd.GetDefaultEnvironment() c.Assert(env, gc.Equals, "fubar") } func (s *EnvironmentCommandSuite) TestGetDefaultEnvironmentJujuEnvSet(c *gc.C) { os.Setenv(osenv.JujuEnvEnvKey, "magic") env := cmd.GetDefaultEnvironment() c.Assert(env, gc.Equals, "magic") } func (s *EnvironmentCommandSuite) TestGetDefaultEnvironmentBothSet(c *gc.C) { os.Setenv(osenv.JujuEnvEnvKey, "magic") err := cmd.WriteCurrentEnvironment("fubar") c.Assert(err, gc.IsNil) env := cmd.GetDefaultEnvironment() c.Assert(env, gc.Equals, "magic") } func (s *EnvironmentCommandSuite) TestWriteAddsNewline(c *gc.C) { err := cmd.WriteCurrentEnvironment("fubar") c.Assert(err, gc.IsNil) current, err := ioutil.ReadFile(cmd.GetCurrentEnvironmentFilePath()) c.Assert(err, gc.IsNil) c.Assert(string(current), gc.Equals, "fubar\n") } func (*EnvironmentCommandSuite) TestErrorWritingFile(c *gc.C) { // Can't write a file over a directory. os.MkdirAll(cmd.GetCurrentEnvironmentFilePath(), 0777) err := cmd.WriteCurrentEnvironment("fubar") c.Assert(err, gc.ErrorMatches, "unable to write to the environment file: .*") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/filevar_test.go������������������������������������0000644�0000153�0000161�00000002753�12321735642�024455� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd_test import ( "os" "launchpad.net/gnuflag" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" ) type FileVarSuite struct { ctx *cmd.Context ValidPath string InvalidPath string // invalid path refers to a file which is not readable } var _ = gc.Suite(&FileVarSuite{}) func (s *FileVarSuite) SetUpTest(c *gc.C) { s.ctx = testing.Context(c) s.ValidPath = s.ctx.AbsPath("valid.yaml") s.InvalidPath = s.ctx.AbsPath("invalid.yaml") f, err := os.Create(s.ValidPath) c.Assert(err, gc.IsNil) f.Close() f, err = os.Create(s.InvalidPath) c.Assert(err, gc.IsNil) f.Close() err = os.Chmod(s.InvalidPath, 0) // make unreadable c.Assert(err, gc.IsNil) } func (s *FileVarSuite) TestValidFileVar(c *gc.C) { fs, config := fs() err := fs.Parse(false, []string{"--config", s.ValidPath}) c.Assert(err, gc.IsNil) c.Assert(config.Path, gc.Equals, s.ValidPath) _, err = config.Read(s.ctx) c.Assert(err, gc.IsNil) } func (s *FileVarSuite) TestInvalidFileVar(c *gc.C) { fs, config := fs() err := fs.Parse(false, []string{"--config", s.InvalidPath}) c.Assert(config.Path, gc.Equals, s.InvalidPath) _, err = config.Read(s.ctx) c.Assert(err, gc.ErrorMatches, "*permission denied") } func fs() (*gnuflag.FlagSet, *cmd.FileVar) { var config cmd.FileVar fs := testing.NewFlagSet() fs.Var(&config, "config", "the config") return fs, &config } ���������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/package_test.go������������������������������������0000644�0000153�0000161�00000001321�12321735642�024406� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd_test import ( "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) func Test(t *testing.T) { gc.TestingT(t) } type Dependencies struct{} var _ = gc.Suite(&Dependencies{}) func (*Dependencies) TestPackageDependencies(c *gc.C) { // This test is to ensure we don't bring in dependencies without thinking. // Looking at the "environs/config", it is just for JujuHome. This should // really be moved into "juju/osenv". c.Assert(testbase.FindJujuCoreImports(c, "launchpad.net/juju-core/cmd"), gc.DeepEquals, []string{"juju/arch", "juju/osenv", "names", "version"}) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/supercommand_test.go�������������������������������0000644�0000153�0000161�00000021304�12321735642�025513� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd_test import ( "bytes" "fmt" "strings" "launchpad.net/gnuflag" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) func initDefenestrate(args []string) (*cmd.SuperCommand, *TestCommand, error) { jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest"}) tc := &TestCommand{Name: "defenestrate"} jc.Register(tc) return jc, tc, testing.InitCommand(jc, args) } type SuperCommandSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&SuperCommandSuite{}) const helpText = "\n help\\s+- show help on a command or other topic" const helpCommandsText = "commands:" + helpText func (s *SuperCommandSuite) TestDispatch(c *gc.C) { jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest"}) info := jc.Info() c.Assert(info.Name, gc.Equals, "jujutest") c.Assert(info.Args, gc.Equals, "<command> ...") c.Assert(info.Doc, gc.Matches, helpCommandsText) jc, _, err := initDefenestrate([]string{"discombobulate"}) c.Assert(err, gc.ErrorMatches, "unrecognized command: jujutest discombobulate") info = jc.Info() c.Assert(info.Name, gc.Equals, "jujutest") c.Assert(info.Args, gc.Equals, "<command> ...") c.Assert(info.Doc, gc.Matches, "commands:\n defenestrate - defenestrate the juju"+helpText) jc, tc, err := initDefenestrate([]string{"defenestrate"}) c.Assert(err, gc.IsNil) c.Assert(tc.Option, gc.Equals, "") info = jc.Info() c.Assert(info.Name, gc.Equals, "jujutest defenestrate") c.Assert(info.Args, gc.Equals, "<something>") c.Assert(info.Doc, gc.Equals, "defenestrate-doc") _, tc, err = initDefenestrate([]string{"defenestrate", "--option", "firmly"}) c.Assert(err, gc.IsNil) c.Assert(tc.Option, gc.Equals, "firmly") _, tc, err = initDefenestrate([]string{"defenestrate", "gibberish"}) c.Assert(err, gc.ErrorMatches, `unrecognized args: \["gibberish"\]`) // --description must be used on it's own. _, _, err = initDefenestrate([]string{"--description", "defenestrate"}) c.Assert(err, gc.ErrorMatches, `unrecognized args: \["defenestrate"\]`) } func (s *SuperCommandSuite) TestRegister(c *gc.C) { jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest"}) jc.Register(&TestCommand{Name: "flip"}) jc.Register(&TestCommand{Name: "flap"}) badCall := func() { jc.Register(&TestCommand{Name: "flap"}) } c.Assert(badCall, gc.PanicMatches, "command already registered: flap") } func (s *SuperCommandSuite) TestRegisterAlias(c *gc.C) { jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest"}) jc.Register(&TestCommand{Name: "flip", Aliases: []string{"flap", "flop"}}) info := jc.Info() c.Assert(info.Doc, gc.Equals, `commands: flap - alias for flip flip - flip the juju flop - alias for flip help - show help on a command or other topic`) } var commandsDoc = `commands: flapbabble - flapbabble the juju flip - flip the juju` func (s *SuperCommandSuite) TestInfo(c *gc.C) { jc := cmd.NewSuperCommand(cmd.SuperCommandParams{ Name: "jujutest", Purpose: "to be purposeful", Doc: "doc\nblah\ndoc", }) info := jc.Info() c.Assert(info.Name, gc.Equals, "jujutest") c.Assert(info.Purpose, gc.Equals, "to be purposeful") // info doc starts with the jc.Doc and ends with the help command c.Assert(info.Doc, gc.Matches, jc.Doc+"(.|\n)*") c.Assert(info.Doc, gc.Matches, "(.|\n)*"+helpCommandsText) jc.Register(&TestCommand{Name: "flip"}) jc.Register(&TestCommand{Name: "flapbabble"}) info = jc.Info() c.Assert(info.Doc, gc.Matches, jc.Doc+"\n\n"+commandsDoc+helpText) jc.Doc = "" info = jc.Info() c.Assert(info.Doc, gc.Matches, commandsDoc+helpText) } type testVersionFlagCommand struct { cmd.CommandBase version string } func (c *testVersionFlagCommand) Info() *cmd.Info { return &cmd.Info{Name: "test"} } func (c *testVersionFlagCommand) SetFlags(f *gnuflag.FlagSet) { f.StringVar(&c.version, "version", "", "") } func (c *testVersionFlagCommand) Run(_ *cmd.Context) error { return nil } func (s *SuperCommandSuite) TestVersionFlag(c *gc.C) { jc := cmd.NewSuperCommand(cmd.SuperCommandParams{ Name: "jujutest", Purpose: "to be purposeful", Doc: "doc\nblah\ndoc", }) testVersionFlagCommand := &testVersionFlagCommand{} jc.Register(&cmd.VersionCommand{}) jc.Register(testVersionFlagCommand) var stdout, stderr bytes.Buffer ctx := &cmd.Context{ Stdout: &stdout, Stderr: &stderr, } // baseline: juju version code := cmd.Main(jc, ctx, []string{"version"}) c.Check(code, gc.Equals, 0) baselineStderr := stderr.String() baselineStdout := stdout.String() stderr.Reset() stdout.Reset() // juju --version output should match that of juju version. code = cmd.Main(jc, ctx, []string{"--version"}) c.Check(code, gc.Equals, 0) c.Assert(stderr.String(), gc.Equals, baselineStderr) c.Assert(stdout.String(), gc.Equals, baselineStdout) stderr.Reset() stdout.Reset() // juju test --version should update testVersionFlagCommand.version, // and there should be no output. The --version flag on the 'test' // subcommand has a different type to the "juju --version" flag. code = cmd.Main(jc, ctx, []string{"test", "--version=abc.123"}) c.Check(code, gc.Equals, 0) c.Assert(stderr.String(), gc.Equals, "") c.Assert(stdout.String(), gc.Equals, "") c.Assert(testVersionFlagCommand.version, gc.Equals, "abc.123") } func (s *SuperCommandSuite) TestLogging(c *gc.C) { jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest", Log: &cmd.Log{}}) jc.Register(&TestCommand{Name: "blah"}) ctx := testing.Context(c) code := cmd.Main(jc, ctx, []string{"blah", "--option", "error", "--debug"}) c.Assert(code, gc.Equals, 1) c.Assert(bufferString(ctx.Stderr), gc.Matches, `^.* running juju-.* .* ERROR .* BAM! `) } func (s *SuperCommandSuite) TestDescription(c *gc.C) { jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest", Purpose: "blow up the death star"}) jc.Register(&TestCommand{Name: "blah"}) ctx := testing.Context(c) code := cmd.Main(jc, ctx, []string{"blah", "--description"}) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stdout), gc.Equals, "blow up the death star\n") } func (s *SuperCommandSuite) TestHelp(c *gc.C) { jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest"}) jc.Register(&TestCommand{Name: "blah"}) ctx := testing.Context(c) code := cmd.Main(jc, ctx, []string{"blah", "--help"}) c.Assert(code, gc.Equals, 0) stripped := strings.Replace(bufferString(ctx.Stdout), "\n", "", -1) c.Assert(stripped, gc.Matches, ".*usage: jujutest blah.*blah-doc.*") } func (s *SuperCommandSuite) TestHelpWithPrefix(c *gc.C) { jc := cmd.NewSuperCommand(cmd.SuperCommandParams{Name: "jujutest", UsagePrefix: "juju"}) jc.Register(&TestCommand{Name: "blah"}) ctx := testing.Context(c) code := cmd.Main(jc, ctx, []string{"blah", "--help"}) c.Assert(code, gc.Equals, 0) stripped := strings.Replace(bufferString(ctx.Stdout), "\n", "", -1) c.Assert(stripped, gc.Matches, ".*usage: juju jujutest blah.*blah-doc.*") } func NewSuperWithCallback(callback func(*cmd.Context, string, []string) error) cmd.Command { return cmd.NewSuperCommand(cmd.SuperCommandParams{ Name: "jujutest", Log: &cmd.Log{}, MissingCallback: callback, }) } func (s *SuperCommandSuite) TestMissingCallback(c *gc.C) { var calledName string var calledArgs []string callback := func(ctx *cmd.Context, subcommand string, args []string) error { calledName = subcommand calledArgs = args return nil } code := cmd.Main( NewSuperWithCallback(callback), testing.Context(c), []string{"foo", "bar", "baz", "--debug"}) c.Assert(code, gc.Equals, 0) c.Assert(calledName, gc.Equals, "foo") c.Assert(calledArgs, gc.DeepEquals, []string{"bar", "baz", "--debug"}) } func (s *SuperCommandSuite) TestMissingCallbackErrors(c *gc.C) { callback := func(ctx *cmd.Context, subcommand string, args []string) error { return fmt.Errorf("command not found %q", subcommand) } ctx := testing.Context(c) code := cmd.Main(NewSuperWithCallback(callback), ctx, []string{"foo"}) c.Assert(code, gc.Equals, 1) c.Assert(testing.Stdout(ctx), gc.Equals, "") c.Assert(testing.Stderr(ctx), gc.Equals, "ERROR command not found \"foo\"\n") } func (s *SuperCommandSuite) TestMissingCallbackContextWiredIn(c *gc.C) { callback := func(ctx *cmd.Context, subcommand string, args []string) error { fmt.Fprintf(ctx.Stdout, "this is std out") fmt.Fprintf(ctx.Stderr, "this is std err") return nil } ctx := testing.Context(c) code := cmd.Main(NewSuperWithCallback(callback), ctx, []string{"foo", "bar", "baz", "--debug"}) c.Assert(code, gc.Equals, 0) c.Assert(testing.Stdout(ctx), gc.Equals, "this is std out") c.Assert(testing.Stderr(ctx), gc.Equals, "this is std err") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charmd/��������������������������������������������0000755�0000153�0000161�00000000000�12321735642�022666� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charmd/config.yaml���������������������������������0000644�0000153�0000161�00000000064�12321735642�025017� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mongo-url: localhost:27017 api-addr: localhost:8080 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/charmd/main.go�������������������������������������0000644�0000153�0000161�00000001667�12321735642�024153� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "net/http" "os" "path/filepath" "launchpad.net/juju-core/store" ) func main() { err := serve() if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } } func serve() error { var confPath string if len(os.Args) == 2 { if _, err := os.Stat(os.Args[1]); err == nil { confPath = os.Args[1] } } if confPath == "" { return fmt.Errorf("usage: %s <config path>", filepath.Base(os.Args[0])) } conf, err := store.ReadConfig(confPath) if err != nil { return err } if conf.MongoURL == "" || conf.APIAddr == "" { return fmt.Errorf("missing mongo-url or api-addr in config file") } s, err := store.Open(conf.MongoURL) if err != nil { return err } defer s.Close() server, err := store.NewServer(s) if err != nil { return err } return http.ListenAndServe(conf.APIAddr, server) } �������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/���������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022536� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/unit_test.go���������������������������������0000644�0000153�0000161�00000016421�12321735776�025132� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/cmd" envtesting "launchpad.net/juju-core/environs/testing" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" apirsyslog "launchpad.net/juju-core/state/api/rsyslog" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/rsyslog" "launchpad.net/juju-core/worker/upgrader" ) type UnitSuite struct { coretesting.GitSuite agentSuite } var _ = gc.Suite(&UnitSuite{}) func (s *UnitSuite) SetUpTest(c *gc.C) { s.GitSuite.SetUpTest(c) s.agentSuite.SetUpTest(c) } func (s *UnitSuite) TearDownTest(c *gc.C) { s.agentSuite.TearDownTest(c) s.GitSuite.TearDownTest(c) } const initialUnitPassword = "unit-password-1234567890" // primeAgent creates a unit, and sets up the unit agent's directory. // It returns the assigned machine, new unit and the agent's configuration. func (s *UnitSuite) primeAgent(c *gc.C) (*state.Machine, *state.Unit, agent.Config, *tools.Tools) { jujutesting.AddStateServerMachine(c, s.State) svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) err = unit.SetPassword(initialUnitPassword) c.Assert(err, gc.IsNil) // Assign the unit to a machine. err = unit.AssignToNewMachine() c.Assert(err, gc.IsNil) id, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) machine, err := s.State.Machine(id) c.Assert(err, gc.IsNil) conf, tools := s.agentSuite.primeAgent(c, unit.Tag(), initialUnitPassword, version.Current) return machine, unit, conf, tools } func (s *UnitSuite) newAgent(c *gc.C, unit *state.Unit) *UnitAgent { a := &UnitAgent{} s.initAgent(c, a, "--unit-name", unit.Name()) return a } func (s *UnitSuite) TestParseSuccess(c *gc.C) { create := func() (cmd.Command, *AgentConf) { a := &UnitAgent{} return a, &a.Conf } uc := CheckAgentCommand(c, create, []string{"--unit-name", "w0rd-pre55/1"}) c.Assert(uc.(*UnitAgent).UnitName, gc.Equals, "w0rd-pre55/1") } func (s *UnitSuite) TestParseMissing(c *gc.C) { uc := &UnitAgent{} err := ParseAgentCommand(uc, []string{}) c.Assert(err, gc.ErrorMatches, "--unit-name option must be set") } func (s *UnitSuite) TestParseNonsense(c *gc.C) { for _, args := range [][]string{ {"--unit-name", "wordpress"}, {"--unit-name", "wordpress/seventeen"}, {"--unit-name", "wordpress/-32"}, {"--unit-name", "wordpress/wild/9"}, {"--unit-name", "20/20"}, } { err := ParseAgentCommand(&UnitAgent{}, args) c.Assert(err, gc.ErrorMatches, `--unit-name option expects "<service>/<n>" argument`) } } func (s *UnitSuite) TestParseUnknown(c *gc.C) { uc := &UnitAgent{} err := ParseAgentCommand(uc, []string{"--unit-name", "wordpress/1", "thundering typhoons"}) c.Assert(err, gc.ErrorMatches, `unrecognized args: \["thundering typhoons"\]`) } func waitForUnitStarted(stateConn *state.State, unit *state.Unit, c *gc.C) { timeout := time.After(5 * time.Second) for { select { case <-timeout: c.Fatalf("no activity detected") case <-time.After(coretesting.ShortWait): err := unit.Refresh() c.Assert(err, gc.IsNil) st, info, data, err := unit.Status() c.Assert(err, gc.IsNil) switch st { case params.StatusPending, params.StatusInstalled: c.Logf("waiting...") continue case params.StatusStarted: c.Logf("started!") return case params.StatusDown: stateConn.StartSync() c.Logf("unit is still down") default: c.Fatalf("unexpected status %s %s %v", st, info, data) } } } } func (s *UnitSuite) TestRunStop(c *gc.C) { _, unit, _, _ := s.primeAgent(c) a := s.newAgent(c, unit) go func() { c.Check(a.Run(nil), gc.IsNil) }() defer func() { c.Check(a.Stop(), gc.IsNil) }() waitForUnitStarted(s.State, unit, c) } func (s *UnitSuite) TestUpgrade(c *gc.C) { machine, unit, _, currentTools := s.primeAgent(c) agent := s.newAgent(c, unit) newVers := version.Current newVers.Patch++ envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), newVers) // Set the machine agent version to trigger an upgrade. err := machine.SetAgentVersion(newVers) c.Assert(err, gc.IsNil) err = runWithTimeout(agent) envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ AgentName: unit.Tag(), OldTools: currentTools.Version, NewTools: newVers, DataDir: s.DataDir(), }) } func (s *UnitSuite) TestUpgradeFailsWithoutTools(c *gc.C) { machine, unit, _, _ := s.primeAgent(c) agent := s.newAgent(c, unit) newVers := version.Current newVers.Patch++ err := machine.SetAgentVersion(newVers) c.Assert(err, gc.IsNil) err = runWithTimeout(agent) c.Assert(err, gc.ErrorMatches, "timed out waiting for agent to finish.*") } func (s *UnitSuite) TestWithDeadUnit(c *gc.C) { _, unit, _, _ := s.primeAgent(c) err := unit.EnsureDead() c.Assert(err, gc.IsNil) a := s.newAgent(c, unit) err = runWithTimeout(a) c.Assert(err, gc.IsNil) // try again when the unit has been removed. err = unit.Remove() c.Assert(err, gc.IsNil) a = s.newAgent(c, unit) err = runWithTimeout(a) c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestOpenAPIState(c *gc.C) { _, unit, _, _ := s.primeAgent(c) s.testOpenAPIState(c, unit, s.newAgent(c, unit), initialUnitPassword) } func (s *UnitSuite) TestOpenAPIStateWithBadCredsTerminates(c *gc.C) { conf, _ := s.agentSuite.primeAgent(c, "unit-missing-0", "no-password", version.Current) _, _, err := openAPIState(conf, nil) c.Assert(err, gc.Equals, worker.ErrTerminateAgent) } type fakeUnitAgent struct { unitName string } func (f *fakeUnitAgent) Entity(st *state.State) (AgentState, error) { return st.Unit(f.unitName) } func (f *fakeUnitAgent) Tag() string { return names.UnitTag(f.unitName) } func (s *UnitSuite) TestOpenAPIStateWithDeadEntityTerminates(c *gc.C) { _, unit, conf, _ := s.primeAgent(c) err := unit.EnsureDead() c.Assert(err, gc.IsNil) _, _, err = openAPIState(conf, &fakeUnitAgent{"wordpress/0"}) c.Assert(err, gc.Equals, worker.ErrTerminateAgent) } func (s *UnitSuite) TestOpenStateFails(c *gc.C) { // Start a unit agent and make sure it doesn't set a mongo password // we can use to connect to state with. _, unit, conf, _ := s.primeAgent(c) a := s.newAgent(c, unit) go func() { c.Check(a.Run(nil), gc.IsNil) }() defer func() { c.Check(a.Stop(), gc.IsNil) }() waitForUnitStarted(s.State, unit, c) s.assertCannotOpenState(c, conf.Tag(), conf.DataDir()) } func (s *UnitSuite) TestRsyslogConfigWorker(c *gc.C) { created := make(chan rsyslog.RsyslogMode, 1) s.PatchValue(&newRsyslogConfigWorker, func(_ *apirsyslog.State, _ agent.Config, mode rsyslog.RsyslogMode) (worker.Worker, error) { created <- mode return worker.NewRunner(isFatal, moreImportant), nil }) _, unit, _, _ := s.primeAgent(c) a := s.newAgent(c, unit) go func() { c.Check(a.Run(nil), gc.IsNil) }() defer func() { c.Check(a.Stop(), gc.IsNil) }() select { case <-time.After(coretesting.LongWait): c.Fatalf("timeout while waiting for rsyslog worker to be created") case mode := <-created: c.Assert(mode, gc.Equals, rsyslog.RsyslogModeForwarding) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/machine_test.go������������������������������0000644�0000153�0000161�00000066203�12321735776�025562� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "os" "path/filepath" "reflect" "strings" "time" "github.com/juju/testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" lxctesting "launchpad.net/juju-core/container/lxc/testing" "launchpad.net/juju-core/environs/config" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/juju/osenv" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/names" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" apideployer "launchpad.net/juju-core/state/api/deployer" "launchpad.net/juju-core/state/api/params" apirsyslog "launchpad.net/juju-core/state/api/rsyslog" charmtesting "launchpad.net/juju-core/state/apiserver/charmrevisionupdater/testing" "launchpad.net/juju-core/state/watcher" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" sshtesting "launchpad.net/juju-core/utils/ssh/testing" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/authenticationworker" "launchpad.net/juju-core/worker/deployer" "launchpad.net/juju-core/worker/instancepoller" "launchpad.net/juju-core/worker/machineenvironmentworker" "launchpad.net/juju-core/worker/rsyslog" "launchpad.net/juju-core/worker/upgrader" ) type commonMachineSuite struct { agentSuite lxctesting.TestSuite } func (s *commonMachineSuite) SetUpSuite(c *gc.C) { s.agentSuite.SetUpSuite(c) s.TestSuite.SetUpSuite(c) restore := testing.PatchValue(&charm.CacheDir, c.MkDir()) s.AddSuiteCleanup(func(*gc.C) { restore() }) } func (s *commonMachineSuite) TearDownSuite(c *gc.C) { s.TestSuite.TearDownSuite(c) s.agentSuite.TearDownSuite(c) } func (s *commonMachineSuite) SetUpTest(c *gc.C) { s.agentSuite.SetUpTest(c) s.TestSuite.SetUpTest(c) os.Remove(jujuRun) // ignore error; may not exist // Fake $HOME, and ssh user to avoid touching ~ubuntu/.ssh/authorized_keys. fakeHome := coretesting.MakeEmptyFakeHomeWithoutJuju(c) s.AddCleanup(func(*gc.C) { fakeHome.Restore() }) s.PatchValue(&authenticationworker.SSHUser, "") } func (s *commonMachineSuite) TearDownTest(c *gc.C) { s.TestSuite.TearDownTest(c) s.agentSuite.TearDownTest(c) } // primeAgent adds a new Machine to run the given jobs, and sets up the // machine agent's directory. It returns the new machine, the // agent's configuration and the tools currently running. func (s *commonMachineSuite) primeAgent( c *gc.C, vers version.Binary, jobs ...state.MachineJob) (m *state.Machine, config agent.Config, tools *tools.Tools) { // Add a machine and ensure it is provisioned. m, err := s.State.AddMachine("quantal", jobs...) c.Assert(err, gc.IsNil) inst, md := jujutesting.AssertStartInstance(c, s.Conn.Environ, m.Id()) c.Assert(m.SetProvisioned(inst.Id(), state.BootstrapNonce, md), gc.IsNil) // Set up the new machine. err = m.SetAgentVersion(vers) c.Assert(err, gc.IsNil) err = m.SetPassword(initialMachinePassword) c.Assert(err, gc.IsNil) tag := names.MachineTag(m.Id()) if m.IsManager() { err = m.SetMongoPassword(initialMachinePassword) c.Assert(err, gc.IsNil) config, tools = s.agentSuite.primeStateAgent(c, tag, initialMachinePassword, vers) } else { config, tools = s.agentSuite.primeAgent(c, tag, initialMachinePassword, vers) } err = config.Write() c.Assert(err, gc.IsNil) return m, config, tools } // newAgent returns a new MachineAgent instance func (s *commonMachineSuite) newAgent(c *gc.C, m *state.Machine) *MachineAgent { a := &MachineAgent{} s.initAgent(c, a, "--machine-id", m.Id()) return a } func (s *MachineSuite) TestParseSuccess(c *gc.C) { create := func() (cmd.Command, *AgentConf) { a := &MachineAgent{} return a, &a.Conf } a := CheckAgentCommand(c, create, []string{"--machine-id", "42"}) c.Assert(a.(*MachineAgent).MachineId, gc.Equals, "42") } type MachineSuite struct { commonMachineSuite } var _ = gc.Suite(&MachineSuite{}) const initialMachinePassword = "machine-password-1234567890" func (s *MachineSuite) TestParseNonsense(c *gc.C) { for _, args := range [][]string{ {}, {"--machine-id", "-4004"}, } { err := ParseAgentCommand(&MachineAgent{}, args) c.Assert(err, gc.ErrorMatches, "--machine-id option must be set, and expects a non-negative integer") } } func (s *MachineSuite) TestParseUnknown(c *gc.C) { a := &MachineAgent{} err := ParseAgentCommand(a, []string{"--machine-id", "42", "blistering barnacles"}) c.Assert(err, gc.ErrorMatches, `unrecognized args: \["blistering barnacles"\]`) } func (s *MachineSuite) TestRunInvalidMachineId(c *gc.C) { c.Skip("agents don't yet distinguish between temporary and permanent errors") m, _, _ := s.primeAgent(c, version.Current, state.JobHostUnits) err := s.newAgent(c, m).Run(nil) c.Assert(err, gc.ErrorMatches, "some error") } func (s *MachineSuite) TestRunStop(c *gc.C) { m, ac, _ := s.primeAgent(c, version.Current, state.JobHostUnits) a := s.newAgent(c, m) done := make(chan error) go func() { done <- a.Run(nil) }() err := a.Stop() c.Assert(err, gc.IsNil) c.Assert(<-done, gc.IsNil) c.Assert(charm.CacheDir, gc.Equals, filepath.Join(ac.DataDir(), "charmcache")) } func (s *MachineSuite) TestWithDeadMachine(c *gc.C) { m, _, _ := s.primeAgent(c, version.Current, state.JobHostUnits) err := m.EnsureDead() c.Assert(err, gc.IsNil) a := s.newAgent(c, m) err = runWithTimeout(a) c.Assert(err, gc.IsNil) } func (s *MachineSuite) TestWithRemovedMachine(c *gc.C) { m, _, _ := s.primeAgent(c, version.Current, state.JobHostUnits) err := m.EnsureDead() c.Assert(err, gc.IsNil) err = m.Remove() c.Assert(err, gc.IsNil) a := s.newAgent(c, m) err = runWithTimeout(a) c.Assert(err, gc.IsNil) } func (s *MachineSuite) TestDyingMachine(c *gc.C) { m, _, _ := s.primeAgent(c, version.Current, state.JobHostUnits) a := s.newAgent(c, m) done := make(chan error) go func() { done <- a.Run(nil) }() defer func() { c.Check(a.Stop(), gc.IsNil) }() err := m.Destroy() c.Assert(err, gc.IsNil) select { case err := <-done: c.Assert(err, gc.IsNil) case <-time.After(watcher.Period * 5 / 4): // TODO(rog) Fix this so it doesn't wait for so long. // https://bugs.launchpad.net/juju-core/+bug/1163983 c.Fatalf("timed out waiting for agent to terminate") } err = m.Refresh() c.Assert(err, gc.IsNil) c.Assert(m.Life(), gc.Equals, state.Dead) } func (s *MachineSuite) TestHostUnits(c *gc.C) { m, _, _ := s.primeAgent(c, version.Current, state.JobHostUnits) a := s.newAgent(c, m) ctx, reset := patchDeployContext(c, s.BackingState) defer reset() go func() { c.Check(a.Run(nil), gc.IsNil) }() defer func() { c.Check(a.Stop(), gc.IsNil) }() // check that unassigned units don't trigger any deployments. svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) u0, err := svc.AddUnit() c.Assert(err, gc.IsNil) u1, err := svc.AddUnit() c.Assert(err, gc.IsNil) ctx.waitDeployed(c) // assign u0, check it's deployed. err = u0.AssignToMachine(m) c.Assert(err, gc.IsNil) ctx.waitDeployed(c, u0.Name()) // "start the agent" for u0 to prevent short-circuited remove-on-destroy; // check that it's kept deployed despite being Dying. err = u0.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) err = u0.Destroy() c.Assert(err, gc.IsNil) ctx.waitDeployed(c, u0.Name()) // add u1 to the machine, check it's deployed. err = u1.AssignToMachine(m) c.Assert(err, gc.IsNil) ctx.waitDeployed(c, u0.Name(), u1.Name()) // make u0 dead; check the deployer recalls the unit and removes it from // state. err = u0.EnsureDead() c.Assert(err, gc.IsNil) ctx.waitDeployed(c, u1.Name()) // The deployer actually removes the unit just after // removing its deployment, so we need to poll here // until it actually happens. for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { err := u0.Refresh() if err == nil && attempt.HasNext() { continue } c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } // short-circuit-remove u1 after it's been deployed; check it's recalled // and removed from state. err = u1.Destroy() c.Assert(err, gc.IsNil) err = u1.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) ctx.waitDeployed(c) } func patchDeployContext(c *gc.C, st *state.State) (*fakeContext, func()) { ctx := &fakeContext{ inited: make(chan struct{}), } orig := newDeployContext newDeployContext = func(dst *apideployer.State, agentConfig agent.Config) deployer.Context { ctx.st = st ctx.agentConfig = agentConfig close(ctx.inited) return ctx } return ctx, func() { newDeployContext = orig } } func (s *MachineSuite) setFakeMachineAddresses(c *gc.C, machine *state.Machine) { addrs := []instance.Address{ instance.NewAddress("0.1.2.3"), } err := machine.SetAddresses(addrs) c.Assert(err, gc.IsNil) // Set the addresses in the environ instance as well so that if the instance poller // runs it won't overwrite them. instId, err := machine.InstanceId() c.Assert(err, gc.IsNil) insts, err := s.Conn.Environ.Instances([]instance.Id{instId}) c.Assert(err, gc.IsNil) dummy.SetInstanceAddresses(insts[0], addrs) } func (s *MachineSuite) TestManageEnviron(c *gc.C) { usefulVersion := version.Current usefulVersion.Series = "quantal" // to match the charm created below envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), usefulVersion) m, _, _ := s.primeAgent(c, version.Current, state.JobManageEnviron) s.setFakeMachineAddresses(c, m) op := make(chan dummy.Operation, 200) dummy.Listen(op) a := s.newAgent(c, m) // Make sure the agent is stopped even if the test fails. defer a.Stop() done := make(chan error) go func() { done <- a.Run(nil) }() // Check that the provisioner and firewaller are alive by doing // a rudimentary check that it responds to state changes. // Add one unit to a service; it should get allocated a machine // and then its ports should be opened. charm := s.AddTestingCharm(c, "dummy") svc := s.AddTestingService(c, "test-service", charm) err := svc.SetExposed() c.Assert(err, gc.IsNil) units, err := juju.AddUnits(s.State, svc, 1, "") c.Assert(err, gc.IsNil) c.Check(opRecvTimeout(c, s.State, op, dummy.OpStartInstance{}), gc.NotNil) // Wait for the instance id to show up in the state. s.waitProvisioned(c, units[0]) err = units[0].OpenPort("tcp", 999) c.Assert(err, gc.IsNil) c.Check(opRecvTimeout(c, s.State, op, dummy.OpOpenPorts{}), gc.NotNil) err = a.Stop() c.Assert(err, gc.IsNil) select { case err := <-done: c.Assert(err, gc.IsNil) case <-time.After(5 * time.Second): c.Fatalf("timed out waiting for agent to terminate") } } func (s *MachineSuite) TestManageEnvironRunsInstancePoller(c *gc.C) { s.PatchValue(&instancepoller.ShortPoll, 500*time.Millisecond) usefulVersion := version.Current usefulVersion.Series = "quantal" // to match the charm created below envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), usefulVersion) m, _, _ := s.primeAgent(c, version.Current, state.JobManageEnviron) s.setFakeMachineAddresses(c, m) a := s.newAgent(c, m) defer a.Stop() go func() { c.Check(a.Run(nil), gc.IsNil) }() // Add one unit to a service; charm := s.AddTestingCharm(c, "dummy") svc := s.AddTestingService(c, "test-service", charm) units, err := juju.AddUnits(s.State, svc, 1, "") c.Assert(err, gc.IsNil) m, instId := s.waitProvisioned(c, units[0]) insts, err := s.Conn.Environ.Instances([]instance.Id{instId}) c.Assert(err, gc.IsNil) addrs := []instance.Address{instance.NewAddress("1.2.3.4")} dummy.SetInstanceAddresses(insts[0], addrs) dummy.SetInstanceStatus(insts[0], "running") for a := coretesting.LongAttempt.Start(); a.Next(); { if !a.HasNext() { c.Logf("final machine addresses: %#v", m.Addresses()) c.Fatalf("timed out waiting for machine to get address") } err := m.Refresh() c.Assert(err, gc.IsNil) instStatus, err := m.InstanceStatus() c.Assert(err, gc.IsNil) if reflect.DeepEqual(m.Addresses(), addrs) && instStatus == "running" { break } } } func (s *MachineSuite) TestManageEnvironCallsUseMultipleCPUs(c *gc.C) { // If it has been enabled, the JobManageEnviron agent should call utils.UseMultipleCPUs usefulVersion := version.Current usefulVersion.Series = "quantal" envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), usefulVersion) m, _, _ := s.primeAgent(c, version.Current, state.JobManageEnviron) s.setFakeMachineAddresses(c, m) calledChan := make(chan struct{}, 1) s.PatchValue(&useMultipleCPUs, func() { calledChan <- struct{}{} }) // Now, start the agent, and observe that a JobManageEnviron agent // calls UseMultipleCPUs a := s.newAgent(c, m) defer a.Stop() go func() { c.Check(a.Run(nil), gc.IsNil) }() // Wait for configuration to be finished <-a.WorkersStarted() select { case <-calledChan: case <-time.After(coretesting.LongWait): c.Errorf("we failed to call UseMultipleCPUs()") } c.Check(a.Stop(), gc.IsNil) // However, an agent that just JobHostUnits doesn't call UseMultipleCPUs m2, _, _ := s.primeAgent(c, version.Current, state.JobHostUnits) s.setFakeMachineAddresses(c, m2) a2 := s.newAgent(c, m2) defer a2.Stop() go func() { c.Check(a2.Run(nil), gc.IsNil) }() // Wait until all the workers have been started, and then kill everything <-a2.workersStarted c.Check(a2.Stop(), gc.IsNil) select { case <-calledChan: c.Errorf("we should not have called UseMultipleCPUs()") case <-time.After(coretesting.ShortWait): } } func (s *MachineSuite) waitProvisioned(c *gc.C, unit *state.Unit) (*state.Machine, instance.Id) { c.Logf("waiting for unit %q to be provisioned", unit) machineId, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) m, err := s.State.Machine(machineId) c.Assert(err, gc.IsNil) w := m.Watch() defer w.Stop() timeout := time.After(coretesting.LongWait) for { select { case <-timeout: c.Fatalf("timed out waiting for provisioning") case _, ok := <-w.Changes(): c.Assert(ok, jc.IsTrue) err := m.Refresh() c.Assert(err, gc.IsNil) if instId, err := m.InstanceId(); err == nil { c.Logf("unit provisioned with instance %s", instId) return m, instId } else { c.Check(err, jc.Satisfies, state.IsNotProvisionedError) } } } panic("watcher died") } func (s *MachineSuite) testUpgradeRequest(c *gc.C, agent runner, tag string, currentTools *tools.Tools) { newVers := version.Current newVers.Patch++ newTools := envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), newVers)[0] err := s.State.SetEnvironAgentVersion(newVers.Number) c.Assert(err, gc.IsNil) err = runWithTimeout(agent) envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ AgentName: tag, OldTools: currentTools.Version, NewTools: newTools.Version, DataDir: s.DataDir(), }) } func (s *MachineSuite) TestUpgradeRequest(c *gc.C) { m, _, currentTools := s.primeAgent(c, version.Current, state.JobManageEnviron, state.JobHostUnits) a := s.newAgent(c, m) s.testUpgradeRequest(c, a, m.Tag(), currentTools) } var fastDialOpts = api.DialOpts{ Timeout: coretesting.LongWait, RetryDelay: coretesting.ShortWait, } func (s *MachineSuite) waitStopped(c *gc.C, job state.MachineJob, a *MachineAgent, done chan error) { err := a.Stop() if job == state.JobManageEnviron { // When shutting down, the API server can be shut down before // the other workers that connect to it, so they get an error so // they then die, causing Stop to return an error. It's not // easy to control the actual error that's received in this // circumstance so we just log it rather than asserting that it // is not nil. if err != nil { c.Logf("error shutting down state manager: %v", err) } } else { c.Assert(err, gc.IsNil) } select { case err := <-done: c.Assert(err, gc.IsNil) case <-time.After(5 * time.Second): c.Fatalf("timed out waiting for agent to terminate") } } func (s *MachineSuite) assertJobWithAPI( c *gc.C, job state.MachineJob, test func(agent.Config, *api.State), ) { stm, conf, _ := s.primeAgent(c, version.Current, job) a := s.newAgent(c, stm) defer a.Stop() // All state jobs currently also run an APIWorker, so no // need to check for that here, like in assertJobWithState. agentAPIs := make(chan *api.State, 1000) undo := sendOpenedAPIs(agentAPIs) defer undo() done := make(chan error) go func() { done <- a.Run(nil) }() select { case agentAPI := <-agentAPIs: c.Assert(agentAPI, gc.NotNil) test(conf, agentAPI) case <-time.After(coretesting.LongWait): c.Fatalf("API not opened") } s.waitStopped(c, job, a, done) } func (s *MachineSuite) assertJobWithState( c *gc.C, job state.MachineJob, test func(agent.Config, *state.State), ) { paramsJob := job.ToParams() if !paramsJob.NeedsState() { c.Fatalf("%v does not use state", paramsJob) } stm, conf, _ := s.primeAgent(c, version.Current, job) a := s.newAgent(c, stm) defer a.Stop() agentStates := make(chan *state.State, 1000) undo := sendOpenedStates(agentStates) defer undo() done := make(chan error) go func() { done <- a.Run(nil) }() select { case agentState := <-agentStates: c.Assert(agentState, gc.NotNil) test(conf, agentState) case <-time.After(coretesting.LongWait): c.Fatalf("state not opened") } s.waitStopped(c, job, a, done) } // TODO(jam): 2013-09-02 http://pad.lv/1219661 // This test has been failing regularly on the Bot. Until someone fixes it so // it doesn't crash, it isn't worth having as we can't tell when someone // actually breaks something. func (s *MachineSuite) TestManageEnvironServesAPI(c *gc.C) { c.Skip("does not pass reliably on the bot (http://pad.lv/1219661") s.assertJobWithState(c, state.JobManageEnviron, func(conf agent.Config, agentState *state.State) { st, _, err := conf.OpenAPI(fastDialOpts) c.Assert(err, gc.IsNil) defer st.Close() m, err := st.Machiner().Machine(conf.Tag()) c.Assert(err, gc.IsNil) c.Assert(m.Life(), gc.Equals, params.Alive) }) } func (s *MachineSuite) TestManageEnvironRunsCleaner(c *gc.C) { s.assertJobWithState(c, state.JobManageEnviron, func(conf agent.Config, agentState *state.State) { // Create a service and unit, and destroy the service. service := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) unit, err := service.AddUnit() c.Assert(err, gc.IsNil) err = service.Destroy() c.Assert(err, gc.IsNil) // Check the unit was not yet removed. err = unit.Refresh() c.Assert(err, gc.IsNil) w := unit.Watch() defer w.Stop() // Trigger a sync on the state used by the agent, and wait // for the unit to be removed. agentState.StartSync() timeout := time.After(coretesting.LongWait) for done := false; !done; { select { case <-timeout: c.Fatalf("unit not cleaned up") case <-time.After(coretesting.ShortWait): s.State.StartSync() case <-w.Changes(): err := unit.Refresh() if errors.IsNotFoundError(err) { done = true } else { c.Assert(err, gc.IsNil) } } } }) } func (s *MachineSuite) TestJobManageEnvironRunsMinUnitsWorker(c *gc.C) { s.assertJobWithState(c, state.JobManageEnviron, func(conf agent.Config, agentState *state.State) { // Ensure that the MinUnits worker is alive by doing a simple check // that it responds to state changes: add a service, set its minimum // number of units to one, wait for the worker to add the missing unit. service := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) err := service.SetMinUnits(1) c.Assert(err, gc.IsNil) w := service.Watch() defer w.Stop() // Trigger a sync on the state used by the agent, and wait for the unit // to be created. agentState.StartSync() timeout := time.After(coretesting.LongWait) for { select { case <-timeout: c.Fatalf("unit not created") case <-time.After(coretesting.ShortWait): s.State.StartSync() case <-w.Changes(): units, err := service.AllUnits() c.Assert(err, gc.IsNil) if len(units) == 1 { return } } } }) } func (s *MachineSuite) TestMachineAgentRunsAuthorisedKeysWorker(c *gc.C) { // Start the machine agent. m, _, _ := s.primeAgent(c, version.Current, state.JobHostUnits) a := s.newAgent(c, m) go func() { c.Check(a.Run(nil), gc.IsNil) }() defer func() { c.Check(a.Stop(), gc.IsNil) }() // Update the keys in the environment. sshKey := sshtesting.ValidKeyOne.Key + " user@host" err := s.BackingState.UpdateEnvironConfig(map[string]interface{}{"authorized-keys": sshKey}, nil, nil) c.Assert(err, gc.IsNil) // Wait for ssh keys file to be updated. s.State.StartSync() timeout := time.After(coretesting.LongWait) sshKeyWithCommentPrefix := sshtesting.ValidKeyOne.Key + " Juju:user@host" for { select { case <-timeout: c.Fatalf("timeout while waiting for authorised ssh keys to change") case <-time.After(coretesting.ShortWait): keys, err := ssh.ListKeys(authenticationworker.SSHUser, ssh.FullKeys) c.Assert(err, gc.IsNil) keysStr := strings.Join(keys, "\n") if sshKeyWithCommentPrefix != keysStr { continue } return } } } // opRecvTimeout waits for any of the given kinds of operation to // be received from ops, and times out if not. func opRecvTimeout(c *gc.C, st *state.State, opc <-chan dummy.Operation, kinds ...dummy.Operation) dummy.Operation { st.StartSync() for { select { case op := <-opc: for _, k := range kinds { if reflect.TypeOf(op) == reflect.TypeOf(k) { return op } } c.Logf("discarding unknown event %#v", op) case <-time.After(15 * time.Second): c.Fatalf("time out wating for operation") } } } func (s *MachineSuite) TestOpenStateFailsForJobHostUnitsButOpenAPIWorks(c *gc.C) { m, _, _ := s.primeAgent(c, version.Current, state.JobHostUnits) s.testOpenAPIState(c, m, s.newAgent(c, m), initialMachinePassword) s.assertJobWithAPI(c, state.JobHostUnits, func(conf agent.Config, st *api.State) { s.assertCannotOpenState(c, conf.Tag(), conf.DataDir()) }) } func (s *MachineSuite) TestOpenStateWorksForJobManageEnviron(c *gc.C) { s.assertJobWithAPI(c, state.JobManageEnviron, func(conf agent.Config, st *api.State) { s.assertCanOpenState(c, conf.Tag(), conf.DataDir()) }) } func (s *MachineSuite) TestMachineAgentSymlinkJujuRun(c *gc.C) { _, err := os.Stat(jujuRun) c.Assert(err, jc.Satisfies, os.IsNotExist) s.assertJobWithAPI(c, state.JobManageEnviron, func(conf agent.Config, st *api.State) { // juju-run should have been created _, err := os.Stat(jujuRun) c.Assert(err, gc.IsNil) }) } func (s *MachineSuite) TestMachineAgentSymlinkJujuRunExists(c *gc.C) { err := os.Symlink("/nowhere/special", jujuRun) c.Assert(err, gc.IsNil) _, err = os.Stat(jujuRun) c.Assert(err, jc.Satisfies, os.IsNotExist) s.assertJobWithAPI(c, state.JobManageEnviron, func(conf agent.Config, st *api.State) { // juju-run should have been recreated _, err := os.Stat(jujuRun) c.Assert(err, gc.IsNil) link, err := os.Readlink(jujuRun) c.Assert(err, gc.IsNil) c.Assert(link, gc.Not(gc.Equals), "/nowhere/special") }) } func (s *MachineSuite) TestMachineEnvirnWorker(c *gc.C) { proxyDir := c.MkDir() s.PatchValue(&machineenvironmentworker.ProxyDirectory, proxyDir) s.PatchValue(&utils.AptConfFile, filepath.Join(proxyDir, "juju-apt-proxy")) s.primeAgent(c, version.Current, state.JobHostUnits) // Make sure there are some proxy settings to write. proxySettings := osenv.ProxySettings{ Http: "http proxy", Https: "https proxy", Ftp: "ftp proxy", } updateAttrs := config.ProxyConfigMap(proxySettings) err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil) c.Assert(err, gc.IsNil) s.assertJobWithAPI(c, state.JobHostUnits, func(conf agent.Config, st *api.State) { for { select { case <-time.After(coretesting.LongWait): c.Fatalf("timeout while waiting for proxy settings to change") case <-time.After(10 * time.Millisecond): _, err := os.Stat(utils.AptConfFile) if os.IsNotExist(err) { continue } c.Assert(err, gc.IsNil) return } } }) } func (s *MachineSuite) TestMachineAgentUninstall(c *gc.C) { m, ac, _ := s.primeAgent(c, version.Current, state.JobHostUnits) err := m.EnsureDead() c.Assert(err, gc.IsNil) a := s.newAgent(c, m) err = runWithTimeout(a) c.Assert(err, gc.IsNil) // juju-run should have been removed on termination _, err = os.Stat(jujuRun) c.Assert(err, jc.Satisfies, os.IsNotExist) // data-dir should have been removed on termination _, err = os.Stat(ac.DataDir()) c.Assert(err, jc.Satisfies, os.IsNotExist) } func (s *MachineSuite) TestMachineAgentRsyslogManageEnviron(c *gc.C) { s.testMachineAgentRsyslogConfigWorker(c, state.JobManageEnviron, rsyslog.RsyslogModeAccumulate) } func (s *MachineSuite) TestMachineAgentRsyslogHostUnits(c *gc.C) { s.testMachineAgentRsyslogConfigWorker(c, state.JobHostUnits, rsyslog.RsyslogModeForwarding) } func (s *MachineSuite) testMachineAgentRsyslogConfigWorker(c *gc.C, job state.MachineJob, expectedMode rsyslog.RsyslogMode) { created := make(chan rsyslog.RsyslogMode, 1) s.PatchValue(&newRsyslogConfigWorker, func(_ *apirsyslog.State, _ agent.Config, mode rsyslog.RsyslogMode) (worker.Worker, error) { created <- mode return worker.NewRunner(isFatal, moreImportant), nil }) s.assertJobWithAPI(c, job, func(conf agent.Config, st *api.State) { select { case <-time.After(coretesting.LongWait): c.Fatalf("timeout while waiting for rsyslog worker to be created") case mode := <-created: c.Assert(mode, gc.Equals, expectedMode) } }) } // MachineWithCharmsSuite provides infrastructure for tests which need to // work with charms. type MachineWithCharmsSuite struct { charmtesting.CharmSuite machine *state.Machine } var _ = gc.Suite(&MachineWithCharmsSuite{}) func (s *MachineWithCharmsSuite) SetUpTest(c *gc.C) { s.CharmSuite.SetUpTest(c) // Create a state server machine. var err error s.machine, err = s.State.AddOneMachine(state.MachineTemplate{ Series: "quantal", InstanceId: "ardbeg-0", Nonce: state.BootstrapNonce, Jobs: []state.MachineJob{state.JobManageEnviron}, }) c.Assert(err, gc.IsNil) err = s.machine.SetPassword(initialMachinePassword) c.Assert(err, gc.IsNil) tag := names.MachineTag(s.machine.Id()) err = s.machine.SetMongoPassword(initialMachinePassword) c.Assert(err, gc.IsNil) // Set up the agent configuration. stateInfo := s.StateInfo(c) writeStateAgentConfig(c, stateInfo, s.DataDir(), tag, initialMachinePassword, version.Current) } func (s *MachineWithCharmsSuite) TestManageEnvironRunsCharmRevisionUpdater(c *gc.C) { s.SetupScenario(c) // Start the machine agent. a := &MachineAgent{} args := []string{"--data-dir", s.DataDir(), "--machine-id", s.machine.Id()} err := coretesting.InitCommand(a, args) c.Assert(err, gc.IsNil) go func() { c.Check(a.Run(nil), gc.IsNil) }() defer func() { c.Check(a.Stop(), gc.IsNil) }() checkRevision := func() bool { curl := charm.MustParseURL("cs:quantal/mysql") placeholder, err := s.State.LatestPlaceholderCharm(curl) return err == nil && placeholder.String() == curl.WithRevision(23).String() } success := false for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { if success = checkRevision(); success { break } } c.Assert(success, gc.Equals, true) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/bootstrap_test.go����������������������������0000644�0000153�0000161�00000024550�12321735776�026172� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "encoding/base64" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // We don't want to use JujuConnSuite because it gives us // an already-bootstrapped environment. type BootstrapSuite struct { testbase.LoggingSuite testing.MgoSuite dataDir string logDir string } var _ = gc.Suite(&BootstrapSuite{}) func (s *BootstrapSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *BootstrapSuite) TearDownSuite(c *gc.C) { s.MgoSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *BootstrapSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) s.dataDir = c.MkDir() s.logDir = c.MkDir() } func (s *BootstrapSuite) TearDownTest(c *gc.C) { s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } var testPassword = "my-admin-secret" func testPasswordHash() string { return utils.UserPasswordHash(testPassword, utils.CompatSalt) } func (s *BootstrapSuite) initBootstrapCommand(c *gc.C, jobs []params.MachineJob, args ...string) (machineConf agent.Config, cmd *BootstrapCommand, err error) { if len(jobs) == 0 { // Add default jobs. jobs = []params.MachineJob{ params.JobManageEnviron, params.JobHostUnits, } } // NOTE: the old test used an equivalent of the NewAgentConfig, but it // really should be using NewStateMachineConfig. params := agent.AgentConfigParams{ LogDir: s.logDir, DataDir: s.dataDir, Jobs: jobs, Tag: "bootstrap", UpgradedToVersion: version.Current.Number, Password: testPasswordHash(), Nonce: state.BootstrapNonce, StateAddresses: []string{testing.MgoServer.Addr()}, APIAddresses: []string{"0.1.2.3:1234"}, CACert: []byte(testing.CACert), } bootConf, err := agent.NewAgentConfig(params) c.Assert(err, gc.IsNil) err = bootConf.Write() c.Assert(err, gc.IsNil) params.Tag = "machine-0" machineConf, err = agent.NewAgentConfig(params) c.Assert(err, gc.IsNil) err = machineConf.Write() c.Assert(err, gc.IsNil) cmd = &BootstrapCommand{} err = testing.InitCommand(cmd, append([]string{"--data-dir", s.dataDir}, args...)) return machineConf, cmd, err } func (s *BootstrapSuite) TestInitializeEnvironment(c *gc.C) { hw := instance.MustParseHardware("arch=amd64 mem=8G") _, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig, "--instance-id", "anything", "--hardware", hw.String()) c.Assert(err, gc.IsNil) err = cmd.Run(nil) c.Assert(err, gc.IsNil) st, err := state.Open(&state.Info{ Addrs: []string{testing.MgoServer.Addr()}, CACert: []byte(testing.CACert), Password: testPasswordHash(), }, state.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, gc.IsNil) defer st.Close() machines, err := st.AllMachines() c.Assert(err, gc.IsNil) c.Assert(machines, gc.HasLen, 1) instid, err := machines[0].InstanceId() c.Assert(err, gc.IsNil) c.Assert(instid, gc.Equals, instance.Id("anything")) stateHw, err := machines[0].HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Assert(stateHw, gc.NotNil) c.Assert(*stateHw, gc.DeepEquals, hw) cons, err := st.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(&cons, jc.Satisfies, constraints.IsEmpty) } func (s *BootstrapSuite) TestSetConstraints(c *gc.C) { tcons := constraints.Value{Mem: uint64p(2048), CpuCores: uint64p(2)} _, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig, "--instance-id", "anything", "--constraints", tcons.String(), ) c.Assert(err, gc.IsNil) err = cmd.Run(nil) c.Assert(err, gc.IsNil) st, err := state.Open(&state.Info{ Addrs: []string{testing.MgoServer.Addr()}, CACert: []byte(testing.CACert), Password: testPasswordHash(), }, state.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, gc.IsNil) defer st.Close() cons, err := st.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, tcons) machines, err := st.AllMachines() c.Assert(err, gc.IsNil) c.Assert(machines, gc.HasLen, 1) cons, err = machines[0].Constraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, tcons) } func uint64p(v uint64) *uint64 { return &v } func (s *BootstrapSuite) TestDefaultMachineJobs(c *gc.C) { expectedJobs := []state.MachineJob{ state.JobManageEnviron, state.JobHostUnits, } _, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig, "--instance-id", "anything") c.Assert(err, gc.IsNil) err = cmd.Run(nil) c.Assert(err, gc.IsNil) st, err := state.Open(&state.Info{ Addrs: []string{testing.MgoServer.Addr()}, CACert: []byte(testing.CACert), Password: testPasswordHash(), }, state.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, gc.IsNil) defer st.Close() m, err := st.Machine("0") c.Assert(err, gc.IsNil) c.Assert(m.Jobs(), gc.DeepEquals, expectedJobs) } func (s *BootstrapSuite) TestConfiguredMachineJobs(c *gc.C) { jobs := []params.MachineJob{params.JobManageEnviron} _, cmd, err := s.initBootstrapCommand(c, jobs, "--env-config", testConfig, "--instance-id", "anything") c.Assert(err, gc.IsNil) err = cmd.Run(nil) c.Assert(err, gc.IsNil) st, err := state.Open(&state.Info{ Addrs: []string{testing.MgoServer.Addr()}, CACert: []byte(testing.CACert), Password: testPasswordHash(), }, state.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, gc.IsNil) defer st.Close() m, err := st.Machine("0") c.Assert(err, gc.IsNil) c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageEnviron}) } func testOpenState(c *gc.C, info *state.Info, expectErrType error) { st, err := state.Open(info, state.DefaultDialOpts(), environs.NewStatePolicy()) if st != nil { st.Close() } if expectErrType != nil { c.Assert(err, gc.FitsTypeOf, expectErrType) } else { c.Assert(err, gc.IsNil) } } func (s *BootstrapSuite) TestInitialPassword(c *gc.C) { machineConf, cmd, err := s.initBootstrapCommand(c, nil, "--env-config", testConfig, "--instance-id", "anything") c.Assert(err, gc.IsNil) err = cmd.Run(nil) c.Assert(err, gc.IsNil) // Check that we cannot now connect to the state without a // password. info := &state.Info{ Addrs: []string{testing.MgoServer.Addr()}, CACert: []byte(testing.CACert), } testOpenState(c, info, errors.Unauthorizedf("")) // Check we can log in to mongo as admin. info.Tag, info.Password = "", testPasswordHash() st, err := state.Open(info, state.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, gc.IsNil) // Reset password so the tests can continue to use the same server. defer st.Close() defer st.SetAdminMongoPassword("") // Check that the admin user has been given an appropriate // password u, err := st.User("admin") c.Assert(err, gc.IsNil) c.Assert(u.PasswordValid(testPassword), gc.Equals, true) // Check that the machine configuration has been given a new // password and that we can connect to mongo as that machine // and that the in-mongo password also verifies correctly. machineConf1, err := agent.ReadConf(agent.ConfigPath(machineConf.DataDir(), "machine-0")) c.Assert(err, gc.IsNil) st, err = machineConf1.OpenState(environs.NewStatePolicy()) c.Assert(err, gc.IsNil) defer st.Close() } var bootstrapArgTests = []struct { input []string err string expectedInstanceId string expectedHardware instance.HardwareCharacteristics expectedConfig map[string]interface{} }{ { // no value supplied for env-config err: "--env-config option must be set", }, { // empty env-config input: []string{"--env-config", ""}, err: "--env-config option must be set", }, { // wrong, should be base64 input: []string{"--env-config", "name: banana\n"}, err: ".*illegal base64 data at input byte.*", }, { // no value supplied for instance-id input: []string{ "--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), }, err: "--instance-id option must be set", }, { // empty instance-id input: []string{ "--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), "--instance-id", "", }, err: "--instance-id option must be set", }, { input: []string{ "--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), "--instance-id", "anything", }, expectedInstanceId: "anything", expectedConfig: map[string]interface{}{"name": "banana"}, }, { input: []string{ "--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), "--instance-id", "anything", "--hardware", "nonsense", }, err: `invalid value "nonsense" for flag --hardware: malformed characteristic "nonsense"`, }, { input: []string{ "--env-config", base64.StdEncoding.EncodeToString([]byte("name: banana\n")), "--instance-id", "anything", "--hardware", "arch=amd64 cpu-cores=4 root-disk=2T", }, expectedInstanceId: "anything", expectedHardware: instance.MustParseHardware("arch=amd64 cpu-cores=4 root-disk=2T"), expectedConfig: map[string]interface{}{"name": "banana"}, }, } func (s *BootstrapSuite) TestBootstrapArgs(c *gc.C) { for i, t := range bootstrapArgTests { c.Logf("test %d", i) var args []string args = append(args, t.input...) _, cmd, err := s.initBootstrapCommand(c, nil, args...) if t.err == "" { c.Assert(cmd, gc.NotNil) c.Assert(err, gc.IsNil) c.Assert(cmd.EnvConfig, gc.DeepEquals, t.expectedConfig) c.Assert(cmd.InstanceId, gc.Equals, t.expectedInstanceId) c.Assert(cmd.Hardware, gc.DeepEquals, t.expectedHardware) } else { c.Assert(err, gc.ErrorMatches, t.err) } } } type b64yaml map[string]interface{} func (m b64yaml) encode() string { data, err := goyaml.Marshal(m) if err != nil { panic(err) } return base64.StdEncoding.EncodeToString(data) } var testConfig = b64yaml( dummy.SampleConfig().Merge( testing.Attrs{ "state-server": false, "agent-version": "3.4.5", }, ).Delete("admin-secret", "ca-private-key")).encode() ��������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/run_test.go����������������������������������0000644�0000153�0000161�00000013231�12321735776�024753� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "path/filepath" "time" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/exec" "launchpad.net/juju-core/utils/fslock" "launchpad.net/juju-core/worker/uniter" ) type RunTestSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&RunTestSuite{}) func (*RunTestSuite) TestWrongArgs(c *gc.C) { for i, test := range []struct { title string args []string errMatch string unit string commands string avoidContext bool }{{ title: "no args", errMatch: "missing unit-name", }, { title: "one arg", args: []string{"foo"}, errMatch: "missing commands", }, { title: "more than two arg", args: []string{"foo", "bar", "baz"}, errMatch: `unrecognized args: \["baz"\]`, }, { title: "unit and command assignment", args: []string{"unit-name", "command"}, unit: "unit-name", commands: "command", }, { title: "unit id converted to tag", args: []string{"foo/1", "command"}, unit: "unit-foo-1", commands: "command", }, { title: "execute not in a context", args: []string{"--no-context", "command"}, commands: "command", avoidContext: true, }, } { c.Logf("\n%d: %s", i, test.title) runCommand := &RunCommand{} err := testing.InitCommand(runCommand, test.args) if test.errMatch == "" { c.Assert(err, gc.IsNil) c.Assert(runCommand.unit, gc.Equals, test.unit) c.Assert(runCommand.commands, gc.Equals, test.commands) c.Assert(runCommand.noContext, gc.Equals, test.avoidContext) } else { c.Assert(err, gc.ErrorMatches, test.errMatch) } } } func (s *RunTestSuite) TestInsideContext(c *gc.C) { s.PatchEnvironment("JUJU_CONTEXT_ID", "fake-id") runCommand := &RunCommand{} err := runCommand.Init([]string{"foo", "bar"}) c.Assert(err, gc.ErrorMatches, "juju-run cannot be called from within a hook.*") } func (s *RunTestSuite) TestMissingAgent(c *gc.C) { s.PatchValue(&AgentDir, c.MkDir()) _, err := testing.RunCommand(c, &RunCommand{}, []string{"foo", "bar"}) c.Assert(err, gc.ErrorMatches, `unit "foo" not found on this machine`) } func waitForResult(running <-chan *cmd.Context) (*cmd.Context, error) { select { case result := <-running: return result, nil case <-time.After(testing.ShortWait): return nil, fmt.Errorf("timeout") } } func startRunAsync(c *gc.C, params []string) <-chan *cmd.Context { resultChannel := make(chan *cmd.Context) go func() { ctx, err := testing.RunCommand(c, &RunCommand{}, params) c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError) c.Assert(err, gc.ErrorMatches, "rc: 0") resultChannel <- ctx close(resultChannel) }() return resultChannel } func (s *RunTestSuite) TestNoContext(c *gc.C) { s.PatchValue(&LockDir, c.MkDir()) s.PatchValue(&AgentDir, c.MkDir()) ctx, err := testing.RunCommand(c, &RunCommand{}, []string{"--no-context", "echo done"}) c.Assert(err, jc.Satisfies, cmd.IsRcPassthroughError) c.Assert(err, gc.ErrorMatches, "rc: 0") c.Assert(testing.Stdout(ctx), gc.Equals, "done\n") } func (s *RunTestSuite) TestNoContextAsync(c *gc.C) { s.PatchValue(&LockDir, c.MkDir()) s.PatchValue(&AgentDir, c.MkDir()) channel := startRunAsync(c, []string{"--no-context", "echo done"}) ctx, err := waitForResult(channel) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(ctx), gc.Equals, "done\n") } func (s *RunTestSuite) TestNoContextWithLock(c *gc.C) { s.PatchValue(&LockDir, c.MkDir()) s.PatchValue(&AgentDir, c.MkDir()) s.PatchValue(&fslock.LockWaitDelay, 10*time.Millisecond) lock, err := getLock() c.Assert(err, gc.IsNil) lock.Lock("juju-run test") channel := startRunAsync(c, []string{"--no-context", "echo done"}) ctx, err := waitForResult(channel) c.Assert(err, gc.ErrorMatches, "timeout") lock.Unlock() ctx, err = waitForResult(channel) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(ctx), gc.Equals, "done\n") } func (s *RunTestSuite) TestMissingSocket(c *gc.C) { s.PatchValue(&AgentDir, c.MkDir()) testAgentDir := filepath.Join(AgentDir, "foo") err := os.Mkdir(testAgentDir, 0755) c.Assert(err, gc.IsNil) _, err = testing.RunCommand(c, &RunCommand{}, []string{"foo", "bar"}) c.Assert(err, gc.ErrorMatches, `dial unix .*/run.socket: no such file or directory`) } func (s *RunTestSuite) TestRunning(c *gc.C) { loggo.GetLogger("worker.uniter").SetLogLevel(loggo.TRACE) s.runListenerForAgent(c, "foo") ctx, err := testing.RunCommand(c, &RunCommand{}, []string{"foo", "bar"}) c.Check(cmd.IsRcPassthroughError(err), jc.IsTrue) c.Assert(err, gc.ErrorMatches, "rc: 42") c.Assert(testing.Stdout(ctx), gc.Equals, "bar stdout") c.Assert(testing.Stderr(ctx), gc.Equals, "bar stderr") } func (s *RunTestSuite) runListenerForAgent(c *gc.C, agent string) { s.PatchValue(&AgentDir, c.MkDir()) testAgentDir := filepath.Join(AgentDir, agent) err := os.Mkdir(testAgentDir, 0755) c.Assert(err, gc.IsNil) socketPath := filepath.Join(testAgentDir, uniter.RunListenerFile) listener, err := uniter.NewRunListener(&mockRunner{c}, socketPath) c.Assert(err, gc.IsNil) c.Assert(listener, gc.NotNil) s.AddCleanup(func(*gc.C) { listener.Close() }) } type mockRunner struct { c *gc.C } var _ uniter.CommandRunner = (*mockRunner)(nil) func (r *mockRunner) RunCommands(commands string) (results *exec.ExecResponse, err error) { r.c.Log("mock runner: " + commands) return &exec.ExecResponse{ Code: 42, Stdout: []byte(commands + " stdout"), Stderr: []byte(commands + " stderr"), }, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/bootstrap.go���������������������������������0000644�0000153�0000161�00000006236�12321735776�025134� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "encoding/base64" "fmt" "launchpad.net/gnuflag" "launchpad.net/goyaml" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" ) type BootstrapCommand struct { cmd.CommandBase Conf AgentConf EnvConfig map[string]interface{} Constraints constraints.Value Hardware instance.HardwareCharacteristics InstanceId string } // Info returns a decription of the command. func (c *BootstrapCommand) Info() *cmd.Info { return &cmd.Info{ Name: "bootstrap-state", Purpose: "initialize juju state", } } func (c *BootstrapCommand) SetFlags(f *gnuflag.FlagSet) { c.Conf.addFlags(f) yamlBase64Var(f, &c.EnvConfig, "env-config", "", "initial environment configuration (yaml, base64 encoded)") f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "initial environment constraints (space-separated strings)") f.Var(&c.Hardware, "hardware", "hardware characteristics (space-separated strings)") f.StringVar(&c.InstanceId, "instance-id", "", "unique instance-id for bootstrap machine") } // Init initializes the command for running. func (c *BootstrapCommand) Init(args []string) error { if len(c.EnvConfig) == 0 { return requiredError("env-config") } if c.InstanceId == "" { return requiredError("instance-id") } return c.Conf.checkArgs(args) } // Run initializes state for an environment. func (c *BootstrapCommand) Run(_ *cmd.Context) error { envCfg, err := config.New(config.NoDefaults, c.EnvConfig) if err != nil { return err } if err := c.Conf.read("machine-0"); err != nil { return err } // agent.Jobs is an optional field in the agent config, and was // introduced after 1.17.2. We default to allowing units on // machine-0 if missing. jobs := c.Conf.config.Jobs() if len(jobs) == 0 { jobs = []params.MachineJob{ params.JobManageEnviron, params.JobHostUnits, } } st, _, err := c.Conf.config.InitializeState(envCfg, agent.BootstrapMachineConfig{ Constraints: c.Constraints, Jobs: jobs, InstanceId: instance.Id(c.InstanceId), Characteristics: c.Hardware, }, state.DefaultDialOpts(), environs.NewStatePolicy()) if err != nil { return err } st.Close() return nil } // yamlBase64Value implements gnuflag.Value on a map[string]interface{}. type yamlBase64Value map[string]interface{} // Set decodes the base64 value into yaml then expands that into a map. func (v *yamlBase64Value) Set(value string) error { decoded, err := base64.StdEncoding.DecodeString(value) if err != nil { return err } return goyaml.Unmarshal(decoded, v) } func (v *yamlBase64Value) String() string { return fmt.Sprintf("%v", *v) } // yamlBase64Var sets up a gnuflag flag analogous to the FlagSet.*Var methods. func yamlBase64Var(fs *gnuflag.FlagSet, target *map[string]interface{}, name string, value string, usage string) { fs.Var((*yamlBase64Value)(target), name, usage) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/deploy_test.go�������������������������������0000644�0000153�0000161�00000003742�12321735642�025441� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "reflect" "sort" "sync" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/state" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils/set" ) // fakeManager allows us to test deployments without actually deploying units // to the local system. It's slightly uncomfortably complex because it needs // to use the *state.State opened within the agent's runOnce -- not the one // created in the test -- to StartSync and cause the task to actually start // a sync and observe changes to the set of desired units (and thereby run // deployment tests in a reasonable amount of time). type fakeContext struct { mu sync.Mutex deployed set.Strings st *state.State agentConfig agent.Config inited chan struct{} } func (ctx *fakeContext) DeployUnit(unitName, _ string) error { ctx.mu.Lock() ctx.deployed.Add(unitName) ctx.mu.Unlock() return nil } func (ctx *fakeContext) RecallUnit(unitName string) error { ctx.mu.Lock() ctx.deployed.Remove(unitName) ctx.mu.Unlock() return nil } func (ctx *fakeContext) DeployedUnits() ([]string, error) { ctx.mu.Lock() defer ctx.mu.Unlock() if ctx.deployed.IsEmpty() { return nil, nil } return ctx.deployed.SortedValues(), nil } func (ctx *fakeContext) waitDeployed(c *gc.C, want ...string) { sort.Strings(want) timeout := time.After(testing.LongWait) select { case <-timeout: c.Fatalf("manager never initialized") case <-ctx.inited: for { ctx.st.StartSync() select { case <-timeout: got, err := ctx.DeployedUnits() c.Assert(err, gc.IsNil) c.Fatalf("unexpected units: %#v", got) case <-time.After(testing.ShortWait): got, err := ctx.DeployedUnits() c.Assert(err, gc.IsNil) if reflect.DeepEqual(got, want) { return } } } } } func (ctx *fakeContext) AgentConfig() agent.Config { return ctx.agentConfig } ������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/run.go���������������������������������������0000644�0000153�0000161�00000007415�12321735642�023713� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "net/rpc" "os" "path/filepath" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/names" "launchpad.net/juju-core/utils/exec" "launchpad.net/juju-core/utils/fslock" "launchpad.net/juju-core/worker/uniter" ) var ( AgentDir = "/var/lib/juju/agents" LockDir = "/var/lib/juju/locks" ) type RunCommand struct { cmd.CommandBase unit string commands string showHelp bool noContext bool } const runCommandDoc = ` Run the specified commands in the hook context for the unit. unit-name can be either the unit tag: i.e. unit-ubuntu-0 or the unit id: i.e. ubuntu/0 If --no-context is specified, the <unit-name> positional argument is not needed. The commands are executed with '/bin/bash -s', and the output returned. ` // Info returns usage information for the command. func (c *RunCommand) Info() *cmd.Info { return &cmd.Info{ Name: "juju-run", Args: "<unit-name> <commands>", Purpose: "run commands in a unit's hook context", Doc: runCommandDoc, } } func (c *RunCommand) SetFlags(f *gnuflag.FlagSet) { f.BoolVar(&c.showHelp, "h", false, "show help on juju-run") f.BoolVar(&c.showHelp, "help", false, "") f.BoolVar(&c.noContext, "no-context", false, "do not run the command in a unit context") } func (c *RunCommand) Init(args []string) error { // make sure we aren't in an existing hook context if contextId, err := getenv("JUJU_CONTEXT_ID"); err == nil && contextId != "" { return fmt.Errorf("juju-run cannot be called from within a hook, have context %q", contextId) } if !c.noContext { if len(args) < 1 { return fmt.Errorf("missing unit-name") } c.unit, args = args[0], args[1:] // If the command line param is a unit id (like service/2) we need to // change it to the unit tag as that is the format of the agent directory // on disk (unit-service-2). if names.IsUnit(c.unit) { c.unit = names.UnitTag(c.unit) } } if len(args) < 1 { return fmt.Errorf("missing commands") } c.commands, args = args[0], args[1:] return cmd.CheckEmpty(args) } func (c *RunCommand) Run(ctx *cmd.Context) error { if c.showHelp { return gnuflag.ErrHelp } var result *exec.ExecResponse var err error if c.noContext { result, err = c.executeNoContext() } else { result, err = c.executeInUnitContext() } if err != nil { return err } ctx.Stdout.Write(result.Stdout) ctx.Stderr.Write(result.Stderr) return cmd.NewRcPassthroughError(result.Code) } func (c *RunCommand) executeInUnitContext() (*exec.ExecResponse, error) { unitDir := filepath.Join(AgentDir, c.unit) logger.Debugf("looking for unit dir %s", unitDir) // make sure the unit exists _, err := os.Stat(unitDir) if os.IsNotExist(err) { return nil, fmt.Errorf("unit %q not found on this machine", c.unit) } else if err != nil { return nil, err } socketPath := filepath.Join(unitDir, uniter.RunListenerFile) // make sure the socket exists client, err := rpc.Dial("unix", socketPath) if err != nil { return nil, err } defer client.Close() var result exec.ExecResponse err = client.Call(uniter.JujuRunEndpoint, c.commands, &result) return &result, err } func getLock() (*fslock.Lock, error) { return fslock.NewLock(LockDir, "uniter-hook-execution") } func (c *RunCommand) executeNoContext() (*exec.ExecResponse, error) { // Acquire the uniter hook execution lock to make sure we don't // stomp on each other. lock, err := getLock() if err != nil { return nil, err } err = lock.Lock("juju-run") if err != nil { return nil, err } defer lock.Unlock() runCmd := `[ -f "/home/ubuntu/.juju-proxy" ] && . "/home/ubuntu/.juju-proxy"` + "\n" + c.commands return exec.RunCommands( exec.RunParams{ Commands: runCmd, }) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/upgrade_test.go������������������������������0000644�0000153�0000161�00000007771�12321735776�025612� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "os" "os/exec" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) type UpgradeSuite struct { commonMachineSuite aptCmds []*exec.Cmd machine *state.Machine upgradeToVersion version.Binary } var _ = gc.Suite(&UpgradeSuite{}) func fakeRestart() error { return nil } func (s *UpgradeSuite) SetUpTest(c *gc.C) { s.commonMachineSuite.SetUpTest(c) // Capture all apt commands. s.aptCmds = nil aptCmds := s.HookCommandOutput(&utils.AptCommandOutput, nil, nil) go func() { for cmd := range aptCmds { s.aptCmds = append(s.aptCmds, cmd) } }() // As Juju versions increase, update the version to which we are upgrading. s.upgradeToVersion = version.Current s.upgradeToVersion.Number.Minor++ } func (s *UpgradeSuite) TestUpgradeStepsStateServer(c *gc.C) { s.assertUpgradeSteps(c, state.JobManageEnviron) s.assertStateServerUpgrades(c) } func (s *UpgradeSuite) TestUpgradeStepsHostMachine(c *gc.C) { // We need to first start up a state server that thinks it has already been upgraded. ss, _, _ := s.primeAgent(c, s.upgradeToVersion, state.JobManageEnviron) a := s.newAgent(c, ss) go func() { c.Check(a.Run(nil), gc.IsNil) }() defer func() { c.Check(a.Stop(), gc.IsNil) }() // Now run the test. s.assertUpgradeSteps(c, state.JobHostUnits) s.assertHostUpgrades(c) } func (s *UpgradeSuite) assertUpgradeSteps(c *gc.C, job state.MachineJob) { s.PatchValue(&version.Current, s.upgradeToVersion) err := s.State.SetEnvironAgentVersion(s.upgradeToVersion.Number) c.Assert(err, gc.IsNil) oldVersion := s.upgradeToVersion oldVersion.Major = 1 oldVersion.Minor = 16 var oldConfig agent.Config s.machine, oldConfig, _ = s.primeAgent(c, oldVersion, job) a := s.newAgent(c, s.machine) go func() { c.Check(a.Run(nil), gc.IsNil) }() defer func() { c.Check(a.Stop(), gc.IsNil) }() // Wait for upgrade steps to run. success := false for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { conf, err := agent.ReadConf(agent.ConfigPath(oldConfig.DataDir(), s.machine.Tag())) c.Assert(err, gc.IsNil) success = conf.UpgradedToVersion() == s.upgradeToVersion.Number if success { break } } // Upgrade worker has completed ok. c.Assert(success, jc.IsTrue) } func (s *UpgradeSuite) keyFile() string { return filepath.Join(s.DataDir(), "system-identity") } func (s *UpgradeSuite) assertCommonUpgrades(c *gc.C) { // rsyslog-gnutls should have been installed. c.Assert(s.aptCmds, gc.HasLen, 1) args := s.aptCmds[0].Args c.Assert(len(args), jc.GreaterThan, 1) c.Assert(args[0], gc.Equals, "apt-get") c.Assert(args[len(args)-1], gc.Equals, "rsyslog-gnutls") } func (s *UpgradeSuite) assertStateServerUpgrades(c *gc.C) { s.assertCommonUpgrades(c) // System SSH key c.Assert(s.keyFile(), jc.IsNonEmptyFile) // Syslog port should have been updated cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.SyslogPort(), gc.Equals, config.DefaultSyslogPort) // Deprecated attributes should have been deleted - just test a couple. allAttrs := cfg.AllAttrs() _, ok := allAttrs["public-bucket"] c.Assert(ok, jc.IsFalse) _, ok = allAttrs["public-bucket-region"] c.Assert(ok, jc.IsFalse) } func (s *UpgradeSuite) assertHostUpgrades(c *gc.C) { s.assertCommonUpgrades(c) // Lock directory lockdir := filepath.Join(s.DataDir(), "locks") c.Assert(lockdir, jc.IsDirectory) // SSH key file should not be generated for hosts. _, err := os.Stat(s.keyFile()) c.Assert(err, jc.Satisfies, os.IsNotExist) // Syslog port should not have been updated cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.SyslogPort(), gc.Not(gc.Equals), config.DefaultSyslogPort) // Add other checks as needed... } �������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/agent.go�������������������������������������0000644�0000153�0000161�00000016115�12321735776�024212� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "io" "time" "launchpad.net/gnuflag" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/log" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" apiagent "launchpad.net/juju-core/state/api/agent" apideployer "launchpad.net/juju-core/state/api/deployer" "launchpad.net/juju-core/state/api/params" apirsyslog "launchpad.net/juju-core/state/api/rsyslog" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/deployer" "launchpad.net/juju-core/worker/rsyslog" "launchpad.net/juju-core/worker/upgrader" ) // requiredError is useful when complaining about missing command-line options. func requiredError(name string) error { return fmt.Errorf("--%s option must be set", name) } // AgentConf handles command-line flags shared by all agents. type AgentConf struct { dataDir string config agent.Config } // addFlags injects common agent flags into f. func (c *AgentConf) addFlags(f *gnuflag.FlagSet) { // TODO(dimitern) 2014-02-19 bug 1282025 // We need to pass a config location here instead and // use it to locate the conf and the infer the data-dir // from there instead of passing it like that. f.StringVar(&c.dataDir, "data-dir", "/var/lib/juju", "directory for juju data") } func (c *AgentConf) checkArgs(args []string) error { if c.dataDir == "" { return requiredError("data-dir") } return cmd.CheckEmpty(args) } func (c *AgentConf) read(tag string) (err error) { c.config, err = agent.ReadConf(agent.ConfigPath(c.dataDir, tag)) return } func importance(err error) int { switch { case err == nil: return 0 default: return 1 case isUpgraded(err): return 2 case err == worker.ErrTerminateAgent: return 3 } } // moreImportant returns whether err0 is // more important than err1 - that is, whether // we should act on err0 in preference to err1. func moreImportant(err0, err1 error) bool { return importance(err0) > importance(err1) } func isUpgraded(err error) bool { _, ok := err.(*upgrader.UpgradeReadyError) return ok } type Agent interface { Entity(st *state.State) (AgentState, error) Tag() string } // The AgentState interface is implemented by state types // that represent running agents. type AgentState interface { // SetAgentVersion sets the tools version that the agent is // currently running. SetAgentVersion(v version.Binary) error Tag() string SetMongoPassword(password string) error Life() state.Life } type fatalError struct { Err string } func (e *fatalError) Error() string { return e.Err } func isFatal(err error) bool { if err == worker.ErrTerminateAgent { return true } if isUpgraded(err) { return true } _, ok := err.(*fatalError) return ok } type pinger interface { Ping() error } // connectionIsFatal returns a function suitable for passing // as the isFatal argument to worker.NewRunner, // that diagnoses an error as fatal if the connection // has failed or if the error is otherwise fatal. func connectionIsFatal(conn pinger) func(err error) bool { return func(err error) bool { if isFatal(err) { return true } if err := conn.Ping(); err != nil { logger.Infof("error pinging %T: %v", conn, err) return true } return false } } // isleep waits for the given duration or until it receives a value on // stop. It returns whether the full duration was slept without being // stopped. func isleep(d time.Duration, stop <-chan struct{}) bool { select { case <-stop: return false case <-time.After(d): } return true } func openState(agentConfig agent.Config, a Agent) (*state.State, AgentState, error) { st, err := agentConfig.OpenState(environs.NewStatePolicy()) if err != nil { return nil, nil, err } entity, err := a.Entity(st) if errors.IsNotFoundError(err) || err == nil && entity.Life() == state.Dead { err = worker.ErrTerminateAgent } if err != nil { st.Close() return nil, nil, err } return st, entity, nil } type apiOpener interface { OpenAPI(api.DialOpts) (*api.State, string, error) } func openAPIState(agentConfig apiOpener, a Agent) (*api.State, *apiagent.Entity, error) { // We let the API dial fail immediately because the // runner's loop outside the caller of openAPIState will // keep on retrying. If we block for ages here, // then the worker that's calling this cannot // be interrupted. st, newPassword, err := agentConfig.OpenAPI(api.DialOpts{}) if err != nil { if params.IsCodeNotProvisioned(err) { err = worker.ErrTerminateAgent } if params.IsCodeUnauthorized(err) { err = worker.ErrTerminateAgent } return nil, nil, err } entity, err := st.Agent().Entity(a.Tag()) unauthorized := params.IsCodeUnauthorized(err) dead := err == nil && entity.Life() == params.Dead if unauthorized || dead { err = worker.ErrTerminateAgent } if err != nil { st.Close() return nil, nil, err } if newPassword != "" { if err := entity.SetPassword(newPassword); err != nil { return nil, nil, err } } return st, entity, nil } // agentDone processes the error returned by // an exiting agent. func agentDone(err error) error { if err == worker.ErrTerminateAgent { err = nil } if ug, ok := err.(*upgrader.UpgradeReadyError); ok { if err := ug.ChangeAgentTools(); err != nil { // Return and let upstart deal with the restart. return log.LoggedErrorf(logger, "cannot change agent tools: %v", err) } } return err } type closeWorker struct { worker worker.Worker closer io.Closer } // newCloseWorker returns a task that wraps the given task, // closing the given closer when it finishes. func newCloseWorker(worker worker.Worker, closer io.Closer) worker.Worker { return &closeWorker{ worker: worker, closer: closer, } } func (c *closeWorker) Kill() { c.worker.Kill() } func (c *closeWorker) Wait() error { err := c.worker.Wait() if err := c.closer.Close(); err != nil { logger.Errorf("closeWorker: close error: %v", err) } return err } // newDeployContext gives the tests the opportunity to create a deployer.Context // that can be used for testing so as to avoid (1) deploying units to the system // running the tests and (2) get access to the *State used internally, so that // tests can be run without waiting for the 5s watcher refresh time to which we would // otherwise be restricted. var newDeployContext = func(st *apideployer.State, agentConfig agent.Config) deployer.Context { return deployer.NewSimpleContext(agentConfig, st) } // newRsyslogConfigWorker creates and returns a new RsyslogConfigWorker // based on the specified configuration parameters. var newRsyslogConfigWorker = func(st *apirsyslog.State, agentConfig agent.Config, mode rsyslog.RsyslogMode) (worker.Worker, error) { tag := agentConfig.Tag() namespace := agentConfig.Value(agent.Namespace) var addrs []string if mode == rsyslog.RsyslogModeForwarding { var err error addrs, err = agentConfig.APIAddresses() if err != nil { return nil, err } } return rsyslog.NewRsyslogConfigWorker(st, mode, tag, namespace, addrs) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/main.go��������������������������������������0000644�0000153�0000161�00000010567�12321735642�024035� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "io" "net/rpc" "os" "path/filepath" "strings" "time" "github.com/juju/loggo" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/utils/exec" "launchpad.net/juju-core/worker/uniter/jujuc" // Import the providers. _ "launchpad.net/juju-core/provider/all" ) var jujudDoc = ` juju provides easy, intelligent service orchestration on top of environments such as OpenStack, Amazon AWS, or bare metal. jujud is a component of juju. https://juju.ubuntu.com/ The jujud command can also forward invocations over RPC for execution by the juju unit agent. When used in this way, it expects to be called via a symlink named for the desired remote command, and expects JUJU_AGENT_SOCKET and JUJU_CONTEXT_ID be set in its environment. ` func getenv(name string) (string, error) { value := os.Getenv(name) if value == "" { return "", fmt.Errorf("%s not set", name) } return value, nil } func getwd() (string, error) { dir, err := os.Getwd() if err != nil { return "", err } abs, err := filepath.Abs(dir) if err != nil { return "", err } return abs, nil } // jujuCMain uses JUJU_CONTEXT_ID and JUJU_AGENT_SOCKET to ask a running unit agent // to execute a Command on our behalf. Individual commands should be exposed // by symlinking the command name to this executable. func jujuCMain(commandName string, args []string) (code int, err error) { code = 1 contextId, err := getenv("JUJU_CONTEXT_ID") if err != nil { return } dir, err := getwd() if err != nil { return } req := jujuc.Request{ ContextId: contextId, Dir: dir, CommandName: commandName, Args: args[1:], } socketPath, err := getenv("JUJU_AGENT_SOCKET") if err != nil { return } client, err := rpc.Dial("unix", socketPath) if err != nil { return } defer client.Close() var resp exec.ExecResponse err = client.Call("Jujuc.Main", req, &resp) if err != nil { return } os.Stdout.Write(resp.Stdout) os.Stderr.Write(resp.Stderr) return resp.Code, nil } // Main registers subcommands for the jujud executable, and hands over control // to the cmd package. func jujuDMain(args []string, ctx *cmd.Context) (code int, err error) { jujud := cmd.NewSuperCommand(cmd.SuperCommandParams{ Name: "jujud", Doc: jujudDoc, Log: &cmd.Log{Factory: &writerFactory{}}, }) jujud.Register(&BootstrapCommand{}) jujud.Register(&MachineAgent{}) jujud.Register(&UnitAgent{}) jujud.Register(&cmd.VersionCommand{}) code = cmd.Main(jujud, ctx, args[1:]) return code, nil } // Main is not redundant with main(), because it provides an entry point // for testing with arbitrary command line arguments. func Main(args []string) { var code int = 1 ctx, err := cmd.DefaultContext() if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(2) } commandName := filepath.Base(args[0]) if commandName == "jujud" { code, err = jujuDMain(args, ctx) } else if commandName == "jujuc" { fmt.Fprint(os.Stderr, jujudDoc) code = 2 err = fmt.Errorf("jujuc should not be called directly") } else if commandName == "juju-run" { code = cmd.Main(&RunCommand{}, ctx, args[1:]) } else { code, err = jujuCMain(commandName, args) } if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) } os.Exit(code) } func main() { Main(os.Args) } type writerFactory struct{} func (*writerFactory) NewWriter(target io.Writer) loggo.Writer { return &jujudWriter{target: target} } type jujudWriter struct { target io.Writer unitFormatter simpleFormatter defaultFormatter loggo.DefaultFormatter } var _ loggo.Writer = (*jujudWriter)(nil) func (w *jujudWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) { if strings.HasPrefix(module, "unit.") { fmt.Fprintln(w.target, w.unitFormatter.Format(level, module, timestamp, message)) } else { fmt.Fprintln(w.target, w.defaultFormatter.Format(level, module, filename, line, timestamp, message)) } } type simpleFormatter struct{} func (*simpleFormatter) Format(level loggo.Level, module string, timestamp time.Time, message string) string { ts := timestamp.In(time.UTC).Format("2006-01-02 15:04:05") // Just show the last element of the module. lastDot := strings.LastIndex(module, ".") module = module[lastDot+1:] return fmt.Sprintf("%s %s %s %s", ts, level, module, message) } �����������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/unit.go��������������������������������������0000644�0000153�0000161�00000005713�12321735776�024075� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "runtime" "github.com/juju/loggo" "launchpad.net/gnuflag" "launchpad.net/tomb" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker" workerlogger "launchpad.net/juju-core/worker/logger" "launchpad.net/juju-core/worker/rsyslog" "launchpad.net/juju-core/worker/uniter" "launchpad.net/juju-core/worker/upgrader" ) var agentLogger = loggo.GetLogger("juju.jujud") // UnitAgent is a cmd.Command responsible for running a unit agent. type UnitAgent struct { cmd.CommandBase tomb tomb.Tomb Conf AgentConf UnitName string runner worker.Runner } // Info returns usage information for the command. func (a *UnitAgent) Info() *cmd.Info { return &cmd.Info{ Name: "unit", Purpose: "run a juju unit agent", } } func (a *UnitAgent) SetFlags(f *gnuflag.FlagSet) { a.Conf.addFlags(f) f.StringVar(&a.UnitName, "unit-name", "", "name of the unit to run") } // Init initializes the command for running. func (a *UnitAgent) Init(args []string) error { if a.UnitName == "" { return requiredError("unit-name") } if !names.IsUnit(a.UnitName) { return fmt.Errorf(`--unit-name option expects "<service>/<n>" argument`) } if err := a.Conf.checkArgs(args); err != nil { return err } a.runner = worker.NewRunner(isFatal, moreImportant) return nil } // Stop stops the unit agent. func (a *UnitAgent) Stop() error { a.runner.Kill() return a.tomb.Wait() } // Run runs a unit agent. func (a *UnitAgent) Run(ctx *cmd.Context) error { defer a.tomb.Done() if err := a.Conf.read(a.Tag()); err != nil { return err } agentLogger.Infof("unit agent %v start (%s [%s])", a.Tag(), version.Current, runtime.Compiler) a.runner.StartWorker("api", a.APIWorkers) err := agentDone(a.runner.Wait()) a.tomb.Kill(err) return err } func (a *UnitAgent) APIWorkers() (worker.Worker, error) { agentConfig := a.Conf.config st, entity, err := openAPIState(agentConfig, a) if err != nil { return nil, err } dataDir := a.Conf.dataDir runner := worker.NewRunner(connectionIsFatal(st), moreImportant) runner.StartWorker("upgrader", func() (worker.Worker, error) { return upgrader.NewUpgrader(st.Upgrader(), agentConfig), nil }) runner.StartWorker("logger", func() (worker.Worker, error) { return workerlogger.NewLogger(st.Logger(), agentConfig), nil }) runner.StartWorker("uniter", func() (worker.Worker, error) { return uniter.NewUniter(st.Uniter(), entity.Tag(), dataDir), nil }) runner.StartWorker("rsyslog", func() (worker.Worker, error) { return newRsyslogConfigWorker(st.Rsyslog(), agentConfig, rsyslog.RsyslogModeForwarding) }) return newCloseWorker(runner, st), nil } func (a *UnitAgent) Entity(st *state.State) (AgentState, error) { return st.Unit(a.UnitName) } func (a *UnitAgent) Tag() string { return names.UnitTag(a.UnitName) } �����������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/agent_test.go��������������������������������0000644�0000153�0000161�00000024053�12321735776�025251� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( stderrors "errors" "fmt" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" agenttools "launchpad.net/juju-core/agent/tools" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" envtesting "launchpad.net/juju-core/environs/testing" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/upgrader" ) var _ = gc.Suite(&toolSuite{}) type toolSuite struct { testbase.LoggingSuite } var errorImportanceTests = []error{ nil, stderrors.New("foo"), &upgrader.UpgradeReadyError{}, worker.ErrTerminateAgent, } func (*toolSuite) TestErrorImportance(c *gc.C) { for i, err0 := range errorImportanceTests { for j, err1 := range errorImportanceTests { c.Assert(moreImportant(err0, err1), gc.Equals, i > j) } } } var isFatalTests = []struct { err error isFatal bool }{{ err: worker.ErrTerminateAgent, isFatal: true, }, { err: &upgrader.UpgradeReadyError{}, isFatal: true, }, { err: &params.Error{ Message: "blah", Code: params.CodeNotProvisioned, }, isFatal: false, }, { err: &fatalError{"some fatal error"}, isFatal: true, }, { err: stderrors.New("foo"), isFatal: false, }, { err: &params.Error{ Message: "blah", Code: params.CodeNotFound, }, isFatal: false, }} func (*toolSuite) TestIsFatal(c *gc.C) { for i, test := range isFatalTests { c.Logf("test %d: %s", i, test.err) c.Assert(isFatal(test.err), gc.Equals, test.isFatal) } } type testPinger func() error func (f testPinger) Ping() error { return f() } func (s *toolSuite) TestConnectionIsFatal(c *gc.C) { var ( errPinger testPinger = func() error { return stderrors.New("ping error") } okPinger testPinger = func() error { return nil } ) for i, pinger := range []testPinger{errPinger, okPinger} { for j, test := range isFatalTests { c.Logf("test %d.%d: %s", i, j, test.err) fatal := connectionIsFatal(pinger)(test.err) if test.isFatal { c.Check(fatal, jc.IsTrue) } else { c.Check(fatal, gc.Equals, i == 0) } } } } func mkTools(s string) *coretools.Tools { return &coretools.Tools{ Version: version.MustParseBinary(s + "-foo-bar"), } } type acCreator func() (cmd.Command, *AgentConf) // CheckAgentCommand is a utility function for verifying that common agent // options are handled by a Command; it returns an instance of that // command pre-parsed, with any mandatory flags added. func CheckAgentCommand(c *gc.C, create acCreator, args []string) cmd.Command { com, conf := create() err := coretesting.InitCommand(com, args) c.Assert(conf.dataDir, gc.Equals, "/var/lib/juju") badArgs := append(args, "--data-dir", "") com, conf = create() err = coretesting.InitCommand(com, badArgs) c.Assert(err, gc.ErrorMatches, "--data-dir option must be set") args = append(args, "--data-dir", "jd") com, conf = create() c.Assert(coretesting.InitCommand(com, args), gc.IsNil) c.Assert(conf.dataDir, gc.Equals, "jd") return com } // ParseAgentCommand is a utility function that inserts the always-required args // before parsing an agent command and returning the result. func ParseAgentCommand(ac cmd.Command, args []string) error { common := []string{ "--data-dir", "jd", } return coretesting.InitCommand(ac, append(common, args...)) } type runner interface { Run(*cmd.Context) error Stop() error } // runWithTimeout runs an agent and waits // for it to complete within a reasonable time. func runWithTimeout(r runner) error { done := make(chan error) go func() { done <- r.Run(nil) }() select { case err := <-done: return err case <-time.After(5 * time.Second): } err := r.Stop() return fmt.Errorf("timed out waiting for agent to finish; stop error: %v", err) } // agentSuite is a fixture to be used by agent test suites. type agentSuite struct { oldRestartDelay time.Duration testing.JujuConnSuite } func (s *agentSuite) SetUpSuite(c *gc.C) { s.oldRestartDelay = worker.RestartDelay // We could use testing.ShortWait, but this thrashes quite // a bit when some tests are restarting every 50ms for 10 seconds, // so use a slightly more friendly delay. worker.RestartDelay = 250 * time.Millisecond s.JujuConnSuite.SetUpSuite(c) } func (s *agentSuite) TearDownSuite(c *gc.C) { s.JujuConnSuite.TearDownSuite(c) worker.RestartDelay = s.oldRestartDelay } // primeAgent writes the configuration file and tools with version vers // for an agent with the given entity name. It returns the agent's // configuration and the current tools. func (s *agentSuite) primeAgent(c *gc.C, tag, password string, vers version.Binary) (agent.Config, *coretools.Tools) { stor := s.Conn.Environ.Storage() agentTools := envtesting.PrimeTools(c, stor, s.DataDir(), vers) err := envtools.MergeAndWriteMetadata(stor, coretools.List{agentTools}, envtools.DoNotWriteMirrors) c.Assert(err, gc.IsNil) tools1, err := agenttools.ChangeAgentTools(s.DataDir(), tag, vers) c.Assert(err, gc.IsNil) c.Assert(tools1, gc.DeepEquals, agentTools) stateInfo := s.StateInfo(c) apiInfo := s.APIInfo(c) conf, err := agent.NewAgentConfig( agent.AgentConfigParams{ DataDir: s.DataDir(), Tag: tag, UpgradedToVersion: vers.Number, Password: password, Nonce: state.BootstrapNonce, StateAddresses: stateInfo.Addrs, APIAddresses: apiInfo.Addrs, CACert: stateInfo.CACert, }) c.Assert(conf.Write(), gc.IsNil) return conf, agentTools } // makeStateAgentConfig creates and writes a state agent config. func writeStateAgentConfig(c *gc.C, stateInfo *state.Info, dataDir, tag, password string, vers version.Binary) agent.Config { port := coretesting.FindTCPPort() apiAddr := []string{fmt.Sprintf("localhost:%d", port)} conf, err := agent.NewStateMachineConfig( agent.StateMachineConfigParams{ AgentConfigParams: agent.AgentConfigParams{ DataDir: dataDir, Tag: tag, UpgradedToVersion: vers.Number, Password: password, Nonce: state.BootstrapNonce, StateAddresses: stateInfo.Addrs, APIAddresses: apiAddr, CACert: stateInfo.CACert, }, StateServerCert: []byte(coretesting.ServerCert), StateServerKey: []byte(coretesting.ServerKey), StatePort: coretesting.MgoServer.Port(), APIPort: port, }) c.Assert(err, gc.IsNil) c.Assert(conf.Write(), gc.IsNil) return conf } // primeStateAgent writes the configuration file and tools with version vers // for an agent with the given entity name. It returns the agent's configuration // and the current tools. func (s *agentSuite) primeStateAgent( c *gc.C, tag, password string, vers version.Binary) (agent.Config, *coretools.Tools) { agentTools := envtesting.PrimeTools(c, s.Conn.Environ.Storage(), s.DataDir(), vers) tools1, err := agenttools.ChangeAgentTools(s.DataDir(), tag, vers) c.Assert(err, gc.IsNil) c.Assert(tools1, gc.DeepEquals, agentTools) stateInfo := s.StateInfo(c) conf := writeStateAgentConfig(c, stateInfo, s.DataDir(), tag, password, vers) return conf, agentTools } // initAgent initialises the given agent command with additional // arguments as provided. func (s *agentSuite) initAgent(c *gc.C, a cmd.Command, args ...string) { args = append([]string{"--data-dir", s.DataDir()}, args...) err := coretesting.InitCommand(a, args) c.Assert(err, gc.IsNil) } func (s *agentSuite) testOpenAPIState(c *gc.C, ent state.AgentEntity, agentCmd Agent, initialPassword string) { conf, err := agent.ReadConf(agent.ConfigPath(s.DataDir(), ent.Tag())) c.Assert(err, gc.IsNil) // Check that it starts initially and changes the password assertOpen := func(conf agent.Config) { st, gotEnt, err := openAPIState(conf, agentCmd) c.Assert(err, gc.IsNil) c.Assert(st, gc.NotNil) st.Close() c.Assert(gotEnt.Tag(), gc.Equals, ent.Tag()) } assertOpen(conf) // Check that the initial password is no longer valid. err = ent.Refresh() c.Assert(err, gc.IsNil) c.Assert(ent.PasswordValid(initialPassword), gc.Equals, false) // Read the configuration and check that we can connect with it. conf = refreshConfig(c, conf) // Check we can open the API with the new configuration. assertOpen(conf) } type errorAPIOpener struct { err error } func (e *errorAPIOpener) OpenAPI(_ api.DialOpts) (*api.State, string, error) { return nil, "", e.err } func (s *agentSuite) testOpenAPIStateReplaceErrors(c *gc.C) { for i, test := range []struct { openErr error replaceErr error }{{ fmt.Errorf("blah"), nil, }, { &params.Error{Code: params.CodeNotProvisioned}, worker.ErrTerminateAgent, }, { &params.Error{Code: params.CodeUnauthorized}, worker.ErrTerminateAgent, }} { c.Logf("test %d", i) opener := &errorAPIOpener{test.openErr} _, _, err := openAPIState(opener, nil) if test.replaceErr == nil { c.Check(err, gc.Equals, test.openErr) } else { c.Check(err, gc.Equals, test.replaceErr) } } } func (s *agentSuite) assertCanOpenState(c *gc.C, tag, dataDir string) { config, err := agent.ReadConf(agent.ConfigPath(dataDir, tag)) c.Assert(err, gc.IsNil) st, err := config.OpenState(environs.NewStatePolicy()) c.Assert(err, gc.IsNil) st.Close() } func (s *agentSuite) assertCannotOpenState(c *gc.C, tag, dataDir string) { config, err := agent.ReadConf(agent.ConfigPath(dataDir, tag)) c.Assert(err, gc.IsNil) _, err = config.OpenState(environs.NewStatePolicy()) expectErr := fmt.Sprintf("cannot log in to juju database as %q: unauthorized mongo access: auth fails", tag) c.Assert(err, gc.ErrorMatches, expectErr) } func refreshConfig(c *gc.C, config agent.Config) agent.Config { config, err := agent.ReadConf(agent.ConfigPath(config.DataDir(), config.Tag())) c.Assert(err, gc.IsNil) return config } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/machine.go�����������������������������������0000644�0000153�0000161�00000050070�12321735776�024516� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "fmt" "os" "path/filepath" "runtime" "time" "github.com/juju/loggo" "launchpad.net/gnuflag" "launchpad.net/tomb" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/container/kvm" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/provider" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" apiagent "launchpad.net/juju-core/state/api/agent" "launchpad.net/juju-core/state/api/params" apiprovisioner "launchpad.net/juju-core/state/api/provisioner" "launchpad.net/juju-core/state/apiserver" "launchpad.net/juju-core/upgrades" "launchpad.net/juju-core/upstart" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/authenticationworker" "launchpad.net/juju-core/worker/charmrevisionworker" "launchpad.net/juju-core/worker/cleaner" "launchpad.net/juju-core/worker/deployer" "launchpad.net/juju-core/worker/firewaller" "launchpad.net/juju-core/worker/instancepoller" "launchpad.net/juju-core/worker/localstorage" workerlogger "launchpad.net/juju-core/worker/logger" "launchpad.net/juju-core/worker/machineenvironmentworker" "launchpad.net/juju-core/worker/machiner" "launchpad.net/juju-core/worker/minunitsworker" "launchpad.net/juju-core/worker/provisioner" "launchpad.net/juju-core/worker/resumer" "launchpad.net/juju-core/worker/rsyslog" "launchpad.net/juju-core/worker/terminationworker" "launchpad.net/juju-core/worker/upgrader" ) var logger = loggo.GetLogger("juju.cmd.jujud") var newRunner = func(isFatal func(error) bool, moreImportant func(e0, e1 error) bool) worker.Runner { return worker.NewRunner(isFatal, moreImportant) } const bootstrapMachineId = "0" var retryDelay = 3 * time.Second var jujuRun = "/usr/local/bin/juju-run" var useMultipleCPUs = utils.UseMultipleCPUs // MachineAgent is a cmd.Command responsible for running a machine agent. type MachineAgent struct { cmd.CommandBase tomb tomb.Tomb Conf AgentConf MachineId string runner worker.Runner upgradeComplete chan struct{} stateOpened chan struct{} workersStarted chan struct{} st *state.State } // Info returns usage information for the command. func (a *MachineAgent) Info() *cmd.Info { return &cmd.Info{ Name: "machine", Purpose: "run a juju machine agent", } } func (a *MachineAgent) SetFlags(f *gnuflag.FlagSet) { a.Conf.addFlags(f) f.StringVar(&a.MachineId, "machine-id", "", "id of the machine to run") } // Init initializes the command for running. func (a *MachineAgent) Init(args []string) error { if !names.IsMachine(a.MachineId) { return fmt.Errorf("--machine-id option must be set, and expects a non-negative integer") } if err := a.Conf.checkArgs(args); err != nil { return err } a.runner = newRunner(isFatal, moreImportant) a.upgradeComplete = make(chan struct{}) a.stateOpened = make(chan struct{}) a.workersStarted = make(chan struct{}) return nil } // Wait waits for the machine agent to finish. func (a *MachineAgent) Wait() error { return a.tomb.Wait() } // Stop stops the machine agent. func (a *MachineAgent) Stop() error { a.runner.Kill() return a.tomb.Wait() } // Run runs a machine agent. func (a *MachineAgent) Run(_ *cmd.Context) error { // Due to changes in the logging, and needing to care about old // environments that have been upgraded, we need to explicitly remove the // file writer if one has been added, otherwise we will get duplicate // lines of all logging in the log file. loggo.RemoveWriter("logfile") defer a.tomb.Done() logger.Infof("machine agent %v start (%s [%s])", a.Tag(), version.Current, runtime.Compiler) if err := a.Conf.read(a.Tag()); err != nil { return err } charm.CacheDir = filepath.Join(a.Conf.dataDir, "charmcache") if err := a.initAgent(); err != nil { return err } // ensureStateWorker ensures that there is a worker that // connects to the state that runs within itself all the workers // that need a state connection. Unless we're bootstrapping, we // need to connect to the API server to find out if we need to // call this, so we make the APIWorker call it when necessary if // the machine requires it. Note that ensureStateWorker can be // called many times - StartWorker does nothing if there is // already a worker started with the given name. ensureStateWorker := func() { a.runner.StartWorker("state", a.StateWorker) } // We might be bootstrapping, and the API server is not // running yet. If so, make sure we run a state worker instead. if a.MachineId == bootstrapMachineId { // TODO(rog) When we have HA, we only want to do this // when we really are bootstrapping - once other // instances of the API server have been started, we // should follow the normal course of things and ignore // the fact that this was once the bootstrap machine. logger.Infof("Starting StateWorker for machine-0") ensureStateWorker() } a.runner.StartWorker("api", func() (worker.Worker, error) { return a.APIWorker(ensureStateWorker) }) a.runner.StartWorker("termination", func() (worker.Worker, error) { return terminationworker.NewWorker(), nil }) // At this point, all workers will have been configured to start close(a.workersStarted) err := a.runner.Wait() if err == worker.ErrTerminateAgent { err = a.uninstallAgent() } err = agentDone(err) a.tomb.Kill(err) return err } // APIWorker returns a Worker that connects to the API and starts any // workers that need an API connection. // // If a state worker is necessary, APIWorker calls ensureStateWorker. func (a *MachineAgent) APIWorker(ensureStateWorker func()) (worker.Worker, error) { agentConfig := a.Conf.config st, entity, err := openAPIState(agentConfig, a) if err != nil { return nil, err } reportOpenedAPI(st) for _, job := range entity.Jobs() { if job.NeedsState() { ensureStateWorker() break } } rsyslogMode := rsyslog.RsyslogModeForwarding for _, job := range entity.Jobs() { if job == params.JobManageEnviron { rsyslogMode = rsyslog.RsyslogModeAccumulate break } } runner := newRunner(connectionIsFatal(st), moreImportant) // Run the upgrader and the upgrade-steps worker without waiting for the upgrade steps to complete. runner.StartWorker("upgrader", func() (worker.Worker, error) { return upgrader.NewUpgrader(st.Upgrader(), agentConfig), nil }) runner.StartWorker("upgrade-steps", func() (worker.Worker, error) { return a.upgradeWorker(st, entity.Jobs()), nil }) // All other workers must wait for the upgrade steps to complete before starting. a.startWorkerAfterUpgrade(runner, "machiner", func() (worker.Worker, error) { return machiner.NewMachiner(st.Machiner(), agentConfig), nil }) a.startWorkerAfterUpgrade(runner, "logger", func() (worker.Worker, error) { return workerlogger.NewLogger(st.Logger(), agentConfig), nil }) a.startWorkerAfterUpgrade(runner, "machineenvironmentworker", func() (worker.Worker, error) { return machineenvironmentworker.NewMachineEnvironmentWorker(st.Environment(), agentConfig), nil }) a.startWorkerAfterUpgrade(runner, "rsyslog", func() (worker.Worker, error) { return newRsyslogConfigWorker(st.Rsyslog(), agentConfig, rsyslogMode) }) // If not a local provider bootstrap machine, start the worker to manage SSH keys. providerType := agentConfig.Value(agent.ProviderType) if providerType != provider.Local || a.MachineId != bootstrapMachineId { a.startWorkerAfterUpgrade(runner, "authenticationworker", func() (worker.Worker, error) { return authenticationworker.NewWorker(st.KeyUpdater(), agentConfig), nil }) } // Perform the operations needed to set up hosting for containers. if err := a.setupContainerSupport(runner, st, entity); err != nil { return nil, fmt.Errorf("setting up container support: %v", err) } for _, job := range entity.Jobs() { switch job { case params.JobHostUnits: a.startWorkerAfterUpgrade(runner, "deployer", func() (worker.Worker, error) { apiDeployer := st.Deployer() context := newDeployContext(apiDeployer, agentConfig) return deployer.NewDeployer(apiDeployer, context), nil }) case params.JobManageEnviron: a.startWorkerAfterUpgrade(runner, "environ-provisioner", func() (worker.Worker, error) { return provisioner.NewEnvironProvisioner(st.Provisioner(), agentConfig), nil }) // TODO(axw) 2013-09-24 bug #1229506 // Make another job to enable the firewaller. Not all environments // are capable of managing ports centrally. a.startWorkerAfterUpgrade(runner, "firewaller", func() (worker.Worker, error) { return firewaller.NewFirewaller(st.Firewaller()) }) a.startWorkerAfterUpgrade(runner, "charm-revision-updater", func() (worker.Worker, error) { return charmrevisionworker.NewRevisionUpdateWorker(st.CharmRevisionUpdater()), nil }) case params.JobManageStateDeprecated: // Legacy environments may set this, but we ignore it. default: // TODO(dimitern): Once all workers moved over to using // the API, report "unknown job type" here. } } return newCloseWorker(runner, st), nil // Note: a worker.Runner is itself a worker.Worker. } // setupContainerSupport determines what containers can be run on this machine and // initialises suitable infrastructure to support such containers. func (a *MachineAgent) setupContainerSupport(runner worker.Runner, st *api.State, entity *apiagent.Entity) error { var supportedContainers []instance.ContainerType // We don't yet support nested lxc containers but anything else can run an LXC container. if entity.ContainerType() != instance.LXC { supportedContainers = append(supportedContainers, instance.LXC) } supportsKvm, err := kvm.IsKVMSupported() if err != nil { logger.Warningf("determining kvm support: %v\nno kvm containers possible", err) } if err == nil && supportsKvm { supportedContainers = append(supportedContainers, instance.KVM) } return a.updateSupportedContainers(runner, st, entity.Tag(), supportedContainers) } // updateSupportedContainers records in state that a machine can run the specified containers. // It starts a watcher and when a container of a given type is first added to the machine, // the watcher is killed, the machine is set up to be able to start containers of the given type, // and a suitable provisioner is started. func (a *MachineAgent) updateSupportedContainers(runner worker.Runner, st *api.State, tag string, containers []instance.ContainerType) error { var machine *apiprovisioner.Machine var err error pr := st.Provisioner() if machine, err = pr.Machine(tag); err != nil { return fmt.Errorf("%s is not in state: %v", tag, err) } if len(containers) == 0 { if err := machine.SupportsNoContainers(); err != nil { return fmt.Errorf("clearing supported containers for %s: %v", tag, err) } return nil } if err := machine.SetSupportedContainers(containers...); err != nil { return fmt.Errorf("setting supported containers for %s: %v", tag, err) } // Start the watcher to fire when a container is first requested on the machine. watcherName := fmt.Sprintf("%s-container-watcher", machine.Id()) handler := provisioner.NewContainerSetupHandler(runner, watcherName, containers, machine, pr, a.Conf.config) a.startWorkerAfterUpgrade(runner, watcherName, func() (worker.Worker, error) { return worker.NewStringsWorker(handler), nil }) return nil } // StateJobs returns a worker running all the workers that require // a *state.State connection. func (a *MachineAgent) StateWorker() (worker.Worker, error) { agentConfig := a.Conf.config st, entity, err := openState(agentConfig, a) if err != nil { return nil, err } a.st = st close(a.stateOpened) reportOpenedState(st) m := entity.(*state.Machine) runner := newRunner(connectionIsFatal(st), moreImportant) // Take advantage of special knowledge here in that we will only ever want // the storage provider on one machine, and that is the "bootstrap" node. providerType := agentConfig.Value(agent.ProviderType) if (providerType == provider.Local || provider.IsManual(providerType)) && m.Id() == bootstrapMachineId { a.startWorkerAfterUpgrade(runner, "local-storage", func() (worker.Worker, error) { // TODO(axw) 2013-09-24 bug #1229507 // Make another job to enable storage. // There's nothing special about this. return localstorage.NewWorker(agentConfig), nil }) } for _, job := range m.Jobs() { switch job { case state.JobHostUnits: // Implemented in APIWorker. case state.JobManageEnviron: useMultipleCPUs() a.startWorkerAfterUpgrade(runner, "instancepoller", func() (worker.Worker, error) { return instancepoller.NewWorker(st), nil }) runner.StartWorker("apiserver", func() (worker.Worker, error) { // If the configuration does not have the required information, // it is currently not a recoverable error, so we kill the whole // agent, potentially enabling human intervention to fix // the agent's configuration file. In the future, we may retrieve // the state server certificate and key from the state, and // this should then change. port, cert, key := a.Conf.config.APIServerDetails() if len(cert) == 0 || len(key) == 0 { return nil, &fatalError{"configuration does not have state server cert/key"} } dataDir := a.Conf.config.DataDir() return apiserver.NewServer(st, fmt.Sprintf(":%d", port), cert, key, dataDir) }) a.startWorkerAfterUpgrade(runner, "cleaner", func() (worker.Worker, error) { return cleaner.NewCleaner(st), nil }) a.startWorkerAfterUpgrade(runner, "resumer", func() (worker.Worker, error) { // The action of resumer is so subtle that it is not tested, // because we can't figure out how to do so without brutalising // the transaction log. return resumer.NewResumer(st), nil }) a.startWorkerAfterUpgrade(runner, "minunitsworker", func() (worker.Worker, error) { return minunitsworker.NewMinUnitsWorker(st), nil }) case state.JobManageStateDeprecated: // Legacy environments may set this, but we ignore it. default: logger.Warningf("ignoring unknown job %q", job) } } return newCloseWorker(runner, st), nil } // startWorker starts a worker to run the specified child worker but only after waiting for upgrades to complete. func (a *MachineAgent) startWorkerAfterUpgrade(runner worker.Runner, name string, start func() (worker.Worker, error)) { runner.StartWorker(name, func() (worker.Worker, error) { return a.upgradeWaiterWorker(start), nil }) } // upgradeWaiterWorker runs the specified worker after upgrades have completed. func (a *MachineAgent) upgradeWaiterWorker(start func() (worker.Worker, error)) worker.Worker { return worker.NewSimpleWorker(func(stop <-chan struct{}) error { // wait for the upgrade to complete (or for us to be stopped) select { case <-stop: return nil case <-a.upgradeComplete: } w, err := start() if err != nil { return err } waitCh := make(chan error) go func() { waitCh <- w.Wait() }() select { case err := <-waitCh: return err case <-stop: w.Kill() } return <-waitCh }) } // upgradeWorker runs the required upgrade operations to upgrade to the current Juju version. func (a *MachineAgent) upgradeWorker(apiState *api.State, jobs []params.MachineJob) worker.Worker { return worker.NewSimpleWorker(func(stop <-chan struct{}) error { select { case <-a.upgradeComplete: // Our work is already done (we're probably being restarted // because the API connection has gone down), so do nothing. <-stop return nil default: } // If the machine agent is a state server, wait until state is opened. var st *state.State for _, job := range jobs { if job == params.JobManageEnviron { select { case <-a.stateOpened: } st = a.st break } } err := a.runUpgrades(st, apiState, jobs) if err != nil { return err } logger.Infof("upgrade to %v completed.", version.Current) close(a.upgradeComplete) <-stop return nil }) } // runUpgrades runs the upgrade operations for each job type and updates the updatedToVersion on success. func (a *MachineAgent) runUpgrades(st *state.State, apiState *api.State, jobs []params.MachineJob) error { agentConfig := a.Conf.config from := version.Current from.Number = agentConfig.UpgradedToVersion() if from == version.Current { logger.Infof("upgrade to %v already completed.", version.Current) return nil } context := upgrades.NewContext(agentConfig, apiState, st) for _, job := range jobs { var target upgrades.Target switch job { case params.JobManageEnviron: target = upgrades.StateServer case params.JobHostUnits: target = upgrades.HostMachine default: continue } logger.Infof("starting upgrade from %v to %v for %v %q", from, version.Current, target, a.Tag()) if err := upgrades.PerformUpgrade(from.Number, target, context); err != nil { return fmt.Errorf("cannot perform upgrade from %v to %v for %v %q: %v", from, version.Current, target, a.Tag(), err) } } return a.Conf.config.WriteUpgradedToVersion(version.Current.Number) } // WorkersStarted returns a channel that's closed once all top level workers // have been started. This is provided for testing purposes. func (a *MachineAgent) WorkersStarted() <-chan struct{} { return a.workersStarted } func (a *MachineAgent) Entity(st *state.State) (AgentState, error) { m, err := st.Machine(a.MachineId) if err != nil { return nil, err } // Check the machine nonce as provisioned matches the agent.Conf value. if !m.CheckProvisioned(a.Conf.config.Nonce()) { // The agent is running on a different machine to the one it // should be according to state. It must stop immediately. logger.Errorf("running machine %v agent on inappropriate instance", m) return nil, worker.ErrTerminateAgent } return m, nil } func (a *MachineAgent) Tag() string { return names.MachineTag(a.MachineId) } func (a *MachineAgent) initAgent() error { if err := os.Remove(jujuRun); err != nil && !os.IsNotExist(err) { return err } jujud := filepath.Join(a.Conf.dataDir, "tools", a.Tag(), "jujud") return os.Symlink(jujud, jujuRun) } func (a *MachineAgent) uninstallAgent() error { var errors []error agentServiceName := a.Conf.config.Value(agent.AgentServiceName) if agentServiceName == "" { // For backwards compatibility, handle lack of AgentServiceName. agentServiceName = os.Getenv("UPSTART_JOB") } if agentServiceName != "" { if err := upstart.NewService(agentServiceName).Remove(); err != nil { errors = append(errors, fmt.Errorf("cannot remove service %q: %v", agentServiceName, err)) } } // Remove the juju-run symlink. if err := os.Remove(jujuRun); err != nil && !os.IsNotExist(err) { errors = append(errors, err) } // The machine agent may terminate without knowing its jobs, // for example if the machine's entry in state was removed. // Thus, we do not rely on jobs here, and instead just check // if the upstart config exists. mongoServiceName := a.Conf.config.Value(agent.MongoServiceName) if mongoServiceName != "" { if err := upstart.NewService(mongoServiceName).StopAndRemove(); err != nil { errors = append(errors, fmt.Errorf("cannot stop/remove service %q: %v", mongoServiceName, err)) } } if err := os.RemoveAll(a.Conf.dataDir); err != nil { errors = append(errors, err) } if len(errors) == 0 { return nil } return fmt.Errorf("uninstall failed: %v", errors) } // Below pieces are used for testing,to give us access to the *State opened // by the agent, and allow us to trigger syncs without waiting 5s for them // to happen automatically. var stateReporter chan<- *state.State func reportOpenedState(st *state.State) { select { case stateReporter <- st: default: } } func sendOpenedStates(dst chan<- *state.State) (undo func()) { var original chan<- *state.State original, stateReporter = stateReporter, dst return func() { stateReporter = original } } var apiReporter chan<- *api.State func reportOpenedAPI(st *api.State) { select { case apiReporter <- st: default: } } func sendOpenedAPIs(dst chan<- *api.State) (undo func()) { var original chan<- *api.State original, apiReporter = apiReporter, dst return func() { apiReporter = original } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/jujud/main_test.go���������������������������������0000644�0000153�0000161�00000015732�12321735642�025073� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package main import ( "errors" "flag" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strings" stdtesting "testing" "github.com/juju/testing" "launchpad.net/gnuflag" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/deployer" "launchpad.net/juju-core/worker/uniter/jujuc" ) var caCertFile string func mkdtemp(prefix string) string { d, err := ioutil.TempDir("", prefix) if err != nil { panic(err) } return d } func mktemp(prefix string, content string) string { f, err := ioutil.TempFile("", prefix) if err != nil { panic(err) } _, err = f.WriteString(content) if err != nil { panic(err) } f.Close() return f.Name() } func TestPackage(t *stdtesting.T) { // Change the default init dir in worker/deployer, // so the deployer doesn't try to remove upstart // jobs from tests. restore := testing.PatchValue(&deployer.InitDir, mkdtemp("juju-worker-deployer")) defer restore() // TODO(waigani) 2014-03-19 bug 1294458 // Refactor to use base suites // Change the path to "juju-run", so that the // tests don't try to write to /usr/local/bin. jujuRun = mktemp("juju-run", "") defer os.Remove(jujuRun) // Create a CA certificate available for all tests. caCertFile = mktemp("juju-test-cert", coretesting.CACert) defer os.Remove(caCertFile) coretesting.MgoTestPackage(t) } type MainSuite struct{} var _ = gc.Suite(&MainSuite{}) var flagRunMain = flag.Bool("run-main", false, "Run the application's main function for recursive testing") // Reentrancy point for testing (something as close as possible to) the jujud // tool itself. func TestRunMain(t *stdtesting.T) { if *flagRunMain { Main(flag.Args()) } } func checkMessage(c *gc.C, msg string, cmd ...string) { args := append([]string{"-test.run", "TestRunMain", "-run-main", "--", "jujud"}, cmd...) c.Logf("check %#v", args) ps := exec.Command(os.Args[0], args...) output, err := ps.CombinedOutput() c.Logf(string(output)) c.Assert(err, gc.ErrorMatches, "exit status 2") lines := strings.Split(string(output), "\n") c.Assert(lines[len(lines)-2], gc.Equals, "error: "+msg) } func (s *MainSuite) TestParseErrors(c *gc.C) { // Check all the obvious parse errors checkMessage(c, "unrecognized command: jujud cavitate", "cavitate") msgf := "flag provided but not defined: --cheese" checkMessage(c, msgf, "--cheese", "cavitate") cmds := []string{"bootstrap-state", "unit", "machine"} for _, cmd := range cmds { checkMessage(c, msgf, cmd, "--cheese") } msga := `unrecognized args: ["toastie"]` checkMessage(c, msga, "bootstrap-state", "--env-config", b64yaml{"blah": "blah"}.encode(), "--instance-id", "inst", "toastie") checkMessage(c, msga, "unit", "--unit-name", "un/0", "toastie") checkMessage(c, msga, "machine", "--machine-id", "42", "toastie") } var expectedProviders = []string{ "ec2", "maas", "openstack", } func (s *MainSuite) TestProvidersAreRegistered(c *gc.C) { // check that all the expected providers are registered for _, name := range expectedProviders { _, err := environs.Provider(name) c.Assert(err, gc.IsNil) } } type RemoteCommand struct { cmd.CommandBase msg string } var expectUsage = `usage: remote [options] purpose: test jujuc options: --error (= "") if set, fail here is some documentation ` func (c *RemoteCommand) Info() *cmd.Info { return &cmd.Info{ Name: "remote", Purpose: "test jujuc", Doc: "here is some documentation", } } func (c *RemoteCommand) SetFlags(f *gnuflag.FlagSet) { f.StringVar(&c.msg, "error", "", "if set, fail") } func (c *RemoteCommand) Init(args []string) error { return cmd.CheckEmpty(args) } func (c *RemoteCommand) Run(ctx *cmd.Context) error { if c.msg != "" { return errors.New(c.msg) } fmt.Fprintf(ctx.Stdout, "success!\n") return nil } func run(c *gc.C, sockPath string, contextId string, exit int, cmd ...string) string { args := append([]string{"-test.run", "TestRunMain", "-run-main", "--"}, cmd...) c.Logf("check %v %#v", os.Args[0], args) ps := exec.Command(os.Args[0], args...) ps.Dir = c.MkDir() ps.Env = []string{ fmt.Sprintf("JUJU_AGENT_SOCKET=%s", sockPath), fmt.Sprintf("JUJU_CONTEXT_ID=%s", contextId), // Code that imports launchpad.net/juju-core/testing needs to // be able to find that module at runtime (via build.Import), // so we have to preserve that env variable. os.ExpandEnv("GOPATH=${GOPATH}"), } output, err := ps.CombinedOutput() if exit == 0 { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, fmt.Sprintf("exit status %d", exit)) } return string(output) } type JujuCMainSuite struct { sockPath string server *jujuc.Server } var _ = gc.Suite(&JujuCMainSuite{}) func (s *JujuCMainSuite) SetUpSuite(c *gc.C) { factory := func(contextId, cmdName string) (cmd.Command, error) { if contextId != "bill" { return nil, fmt.Errorf("bad context: %s", contextId) } if cmdName != "remote" { return nil, fmt.Errorf("bad command: %s", cmdName) } return &RemoteCommand{}, nil } s.sockPath = filepath.Join(c.MkDir(), "test.sock") srv, err := jujuc.NewServer(factory, s.sockPath) c.Assert(err, gc.IsNil) s.server = srv go func() { if err := s.server.Run(); err != nil { c.Fatalf("server died: %s", err) } }() } func (s *JujuCMainSuite) TearDownSuite(c *gc.C) { s.server.Close() } var argsTests = []struct { args []string code int output string }{ {[]string{"jujuc", "whatever"}, 2, jujudDoc + "error: jujuc should not be called directly\n"}, {[]string{"remote"}, 0, "success!\n"}, {[]string{"/path/to/remote"}, 0, "success!\n"}, {[]string{"remote", "--help"}, 0, expectUsage}, {[]string{"unknown"}, 1, "error: bad request: bad command: unknown\n"}, {[]string{"remote", "--error", "borken"}, 1, "error: borken\n"}, {[]string{"remote", "--unknown"}, 2, "error: flag provided but not defined: --unknown\n"}, {[]string{"remote", "unwanted"}, 2, `error: unrecognized args: ["unwanted"]` + "\n"}, } func (s *JujuCMainSuite) TestArgs(c *gc.C) { for _, t := range argsTests { fmt.Println(t.args) output := run(c, s.sockPath, "bill", t.code, t.args...) c.Assert(output, gc.Equals, t.output) } } func (s *JujuCMainSuite) TestNoClientId(c *gc.C) { output := run(c, s.sockPath, "", 1, "remote") c.Assert(output, gc.Equals, "error: JUJU_CONTEXT_ID not set\n") } func (s *JujuCMainSuite) TestBadClientId(c *gc.C) { output := run(c, s.sockPath, "ben", 1, "remote") c.Assert(output, gc.Equals, "error: bad request: bad context: ben\n") } func (s *JujuCMainSuite) TestNoSockPath(c *gc.C) { output := run(c, "", "bill", 1, "remote") c.Assert(output, gc.Equals, "error: JUJU_AGENT_SOCKET not set\n") } func (s *JujuCMainSuite) TestBadSockPath(c *gc.C) { badSock := filepath.Join(c.MkDir(), "bad.sock") output := run(c, badSock, "bill", 1, "remote") err := fmt.Sprintf("error: dial unix %s: .*\n", badSock) c.Assert(output, gc.Matches, err) } ��������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/logging_test.go������������������������������������0000644�0000153�0000161�00000016164�12321735642�024454� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd_test import ( "io/ioutil" "path/filepath" "github.com/juju/loggo" "github.com/juju/testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/juju/osenv" coretesting "launchpad.net/juju-core/testing" ) var logger = loggo.GetLogger("juju.test") type LogSuite struct { testing.CleanupSuite } var _ = gc.Suite(&LogSuite{}) func (s *LogSuite) SetUpTest(c *gc.C) { s.CleanupSuite.SetUpTest(c) s.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, "") s.AddCleanup(func(_ *gc.C) { loggo.ResetLoggers() loggo.ResetWriters() }) } func newLogWithFlags(c *gc.C, flags []string) *cmd.Log { log := &cmd.Log{} flagSet := coretesting.NewFlagSet() log.AddFlags(flagSet) err := flagSet.Parse(false, flags) c.Assert(err, gc.IsNil) return log } func (s *LogSuite) TestNoFlags(c *gc.C) { log := newLogWithFlags(c, []string{}) c.Assert(log.Path, gc.Equals, "") c.Assert(log.Quiet, gc.Equals, false) c.Assert(log.Verbose, gc.Equals, false) c.Assert(log.Debug, gc.Equals, false) c.Assert(log.Config, gc.Equals, "") } func (s *LogSuite) TestFlags(c *gc.C) { log := newLogWithFlags(c, []string{"--log-file", "foo", "--verbose", "--debug", "--logging-config=juju.cmd=INFO;juju.worker.deployer=DEBUG"}) c.Assert(log.Path, gc.Equals, "foo") c.Assert(log.Verbose, gc.Equals, true) c.Assert(log.Debug, gc.Equals, true) c.Assert(log.Config, gc.Equals, "juju.cmd=INFO;juju.worker.deployer=DEBUG") } func (s *LogSuite) TestLogConfigFromEnvironment(c *gc.C) { config := "juju.cmd=INFO;juju.worker.deployer=DEBUG" s.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, config) log := newLogWithFlags(c, []string{}) c.Assert(log.Path, gc.Equals, "") c.Assert(log.Verbose, gc.Equals, false) c.Assert(log.Debug, gc.Equals, false) c.Assert(log.Config, gc.Equals, config) } func (s *LogSuite) TestDebugSetsLogLevel(c *gc.C) { l := &cmd.Log{Debug: true} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) c.Assert(loggo.GetLogger("").LogLevel(), gc.Equals, loggo.DEBUG) c.Assert(coretesting.Stderr(ctx), gc.Equals, "") c.Assert(coretesting.Stdout(ctx), gc.Equals, "") } func (s *LogSuite) TestShowLogSetsLogLevel(c *gc.C) { l := &cmd.Log{ShowLog: true} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) c.Assert(loggo.GetLogger("").LogLevel(), gc.Equals, loggo.INFO) c.Assert(coretesting.Stderr(ctx), gc.Equals, "") c.Assert(coretesting.Stdout(ctx), gc.Equals, "") } func (s *LogSuite) TestStderr(c *gc.C) { l := &cmd.Log{ShowLog: true, Config: "<root>=INFO"} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) logger.Infof("hello") c.Assert(coretesting.Stderr(ctx), gc.Matches, `^.* INFO .* hello\n`) } func (s *LogSuite) TestRelPathLog(c *gc.C) { l := &cmd.Log{Path: "foo.log", Config: "<root>=INFO"} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) logger.Infof("hello") content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "foo.log")) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Matches, `^.* INFO .* hello\n`) c.Assert(coretesting.Stderr(ctx), gc.Equals, "") c.Assert(coretesting.Stdout(ctx), gc.Equals, "") } func (s *LogSuite) TestAbsPathLog(c *gc.C) { path := filepath.Join(c.MkDir(), "foo.log") l := &cmd.Log{Path: path, Config: "<root>=INFO"} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) logger.Infof("hello") c.Assert(coretesting.Stderr(ctx), gc.Equals, "") content, err := ioutil.ReadFile(path) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Matches, `^.* INFO .* hello\n`) } func (s *LogSuite) TestLoggingToFileAndStderr(c *gc.C) { l := &cmd.Log{Path: "foo.log", Config: "<root>=INFO", ShowLog: true} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) logger.Infof("hello") content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "foo.log")) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Matches, `^.* INFO .* hello\n`) c.Assert(coretesting.Stderr(ctx), gc.Matches, `^.* INFO .* hello\n`) c.Assert(coretesting.Stdout(ctx), gc.Equals, "") } func (s *LogSuite) TestErrorAndWarningLoggingToStderr(c *gc.C) { // Error and warning go to stderr even with ShowLog=false l := &cmd.Log{Config: "<root>=INFO", ShowLog: false} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) logger.Warningf("a warning") logger.Errorf("an error") logger.Infof("an info") c.Assert(coretesting.Stderr(ctx), gc.Matches, `^.*WARNING a warning\n.*ERROR an error\n.*`) c.Assert(coretesting.Stdout(ctx), gc.Equals, "") } func (s *LogSuite) TestQuietAndVerbose(c *gc.C) { l := &cmd.Log{Verbose: true, Quiet: true} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.ErrorMatches, `"verbose" and "quiet" flags clash, please use one or the other, not both`) } func (s *LogSuite) TestOutputDefault(c *gc.C) { l := &cmd.Log{} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) ctx.Infof("Writing info output") ctx.Verbosef("Writing verbose output") c.Assert(coretesting.Stderr(ctx), gc.Equals, "Writing info output\n") } func (s *LogSuite) TestOutputVerbose(c *gc.C) { l := &cmd.Log{Verbose: true} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) ctx.Infof("Writing info output") ctx.Verbosef("Writing verbose output") c.Assert(coretesting.Stderr(ctx), gc.Equals, "Writing info output\nWriting verbose output\n") } func (s *LogSuite) TestOutputQuiet(c *gc.C) { l := &cmd.Log{Quiet: true} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) ctx.Infof("Writing info output") ctx.Verbosef("Writing verbose output") c.Assert(coretesting.Stderr(ctx), gc.Equals, "") } func (s *LogSuite) TestOutputQuietLogs(c *gc.C) { l := &cmd.Log{Quiet: true, Path: "foo.log", Config: "<root>=INFO"} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) ctx.Infof("Writing info output") ctx.Verbosef("Writing verbose output") content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "foo.log")) c.Assert(err, gc.IsNil) c.Assert(coretesting.Stderr(ctx), gc.Equals, "") c.Assert(string(content), gc.Matches, `^.*INFO .* Writing info output\n.*INFO .*Writing verbose output\n.*`) } func (s *LogSuite) TestOutputDefaultLogsVerbose(c *gc.C) { l := &cmd.Log{Path: "foo.log", Config: "<root>=INFO"} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) ctx.Infof("Writing info output") ctx.Verbosef("Writing verbose output") content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "foo.log")) c.Assert(err, gc.IsNil) c.Assert(coretesting.Stderr(ctx), gc.Equals, "Writing info output\n") c.Assert(string(content), gc.Matches, `^.*INFO .*Writing verbose output\n.*`) } func (s *LogSuite) TestOutputDebugForcesQuiet(c *gc.C) { l := &cmd.Log{Verbose: true, Debug: true} ctx := coretesting.Context(c) err := l.Start(ctx) c.Assert(err, gc.IsNil) ctx.Infof("Writing info output") ctx.Verbosef("Writing verbose output") c.Assert(coretesting.Stderr(ctx), gc.Matches, `^.*INFO .* Writing info output\n.*INFO .*Writing verbose output\n.*`) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/args.go��������������������������������������������0000644�0000153�0000161�00000001714�12321735776�022726� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd import ( "strings" "launchpad.net/gnuflag" ) // StringsValue implements gnuflag.Value for a comma separated list of // strings. This allows flags to be created where the target is []string, and // the caller is after comma separated values. type StringsValue []string var _ gnuflag.Value = (*StringsValue)(nil) // NewStringsValue is used to create the type passed into the gnuflag.FlagSet Var function. // f.Var(cmd.NewStringsValue(defaultValue, &someMember), "name", "help") func NewStringsValue(defaultValue []string, target *[]string) *StringsValue { value := (*StringsValue)(target) *value = defaultValue return value } // Implements gnuflag.Value Set. func (v *StringsValue) Set(s string) error { *v = strings.Split(s, ",") return nil } // Implements gnuflag.Value String. func (v *StringsValue) String() string { return strings.Join(*v, ",") } ����������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/logging.go�����������������������������������������0000644�0000153�0000161�00000010252�12321735642�023405� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd import ( "fmt" "io" "os" "time" "github.com/juju/loggo" "launchpad.net/gnuflag" "launchpad.net/juju-core/juju/osenv" ) // WriterFactory defines the single method to create a new // logging writer for a specified output target. type WriterFactory interface { NewWriter(target io.Writer) loggo.Writer } // Log supplies the necessary functionality for Commands that wish to set up // logging. type Log struct { Path string Verbose bool Quiet bool Debug bool ShowLog bool Config string Factory WriterFactory } // GetLogWriter returns a logging writer for the specified target. func (l *Log) GetLogWriter(target io.Writer) loggo.Writer { if l.Factory != nil { return l.Factory.NewWriter(target) } return loggo.NewSimpleWriter(target, &loggo.DefaultFormatter{}) } // AddFlags adds appropriate flags to f. func (l *Log) AddFlags(f *gnuflag.FlagSet) { f.StringVar(&l.Path, "log-file", "", "path to write log to") f.BoolVar(&l.Verbose, "v", false, "show more verbose output") f.BoolVar(&l.Verbose, "verbose", false, "show more verbose output") f.BoolVar(&l.Quiet, "q", false, "show no informational output") f.BoolVar(&l.Quiet, "quiet", false, "show no informational output") f.BoolVar(&l.Debug, "debug", false, "equivalent to --show-log --log-config=<root>=DEBUG") defaultLogConfig := os.Getenv(osenv.JujuLoggingConfigEnvKey) f.StringVar(&l.Config, "logging-config", defaultLogConfig, "specify log levels for modules") f.BoolVar(&l.ShowLog, "show-log", false, "if set, write the log file to stderr") } // Start starts logging using the given Context. func (log *Log) Start(ctx *Context) error { if log.Verbose && log.Quiet { return fmt.Errorf(`"verbose" and "quiet" flags clash, please use one or the other, not both`) } ctx.quiet = log.Quiet ctx.verbose = log.Verbose if log.Path != "" { path := ctx.AbsPath(log.Path) target, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { return err } writer := log.GetLogWriter(target) err = loggo.RegisterWriter("logfile", writer, loggo.TRACE) if err != nil { return err } } level := loggo.WARNING if log.ShowLog { level = loggo.INFO } if log.Debug { log.ShowLog = true level = loggo.DEBUG // override quiet or verbose if set, this way all the information goes // to the log file. ctx.quiet = true ctx.verbose = false } if log.ShowLog { // We replace the default writer to use ctx.Stderr rather than os.Stderr. writer := log.GetLogWriter(ctx.Stderr) _, err := loggo.ReplaceDefaultWriter(writer) if err != nil { return err } } else { loggo.RemoveWriter("default") // Create a simple writer that doesn't show filenames, or timestamps, // and only shows warning or above. writer := loggo.NewSimpleWriter(ctx.Stderr, &warningFormatter{}) err := loggo.RegisterWriter("warning", writer, loggo.WARNING) if err != nil { return err } } // Set the level on the root logger. loggo.GetLogger("").SetLogLevel(level) // Override the logging config with specified logging config. loggo.ConfigureLoggers(log.Config) return nil } // warningFormatter is a simple loggo formatter that produces something like: // WARNING The message... type warningFormatter struct{} func (*warningFormatter) Format(level loggo.Level, _, _ string, _ int, _ time.Time, message string) string { return fmt.Sprintf("%s %s", level, message) } // NewCommandLogWriter creates a loggo writer for registration // by the callers of a command. This way the logged output can also // be displayed otherwise, e.g. on the screen. func NewCommandLogWriter(name string, out, err io.Writer) loggo.Writer { return &commandLogWriter{name, out, err} } // commandLogWriter filters the log messages for name. type commandLogWriter struct { name string out io.Writer err io.Writer } // Write implements loggo's Writer interface. func (s *commandLogWriter) Write(level loggo.Level, name, filename string, line int, timestamp time.Time, message string) { if name == s.name { if level <= loggo.INFO { fmt.Fprintf(s.out, "%s\n", message) } else { fmt.Fprintf(s.err, "%s\n", message) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/cmd_test.go����������������������������������������0000644�0000153�0000161�00000011151�12321735642�023560� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd_test import ( "bytes" "net/http" "os" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) type CmdSuite struct{} var _ = gc.Suite(&CmdSuite{}) func (s *CmdSuite) TestHttpTransport(c *gc.C) { transport := http.DefaultTransport.(*http.Transport) c.Assert(transport.DisableKeepAlives, jc.IsTrue) client := utils.GetNonValidatingHTTPClient() c.Assert(client.Transport.(*http.Transport).DisableKeepAlives, jc.IsTrue) } func (s *CmdSuite) TestContext(c *gc.C) { ctx := testing.Context(c) c.Assert(ctx.AbsPath("/foo/bar"), gc.Equals, "/foo/bar") c.Assert(ctx.AbsPath("foo/bar"), gc.Equals, filepath.Join(ctx.Dir, "foo/bar")) } func (s *CmdSuite) TestInfo(c *gc.C) { minimal := &TestCommand{Name: "verb", Minimal: true} help := minimal.Info().Help(testing.NewFlagSet()) c.Assert(string(help), gc.Equals, minimalHelp) full := &TestCommand{Name: "verb"} f := testing.NewFlagSet() var ignored string f.StringVar(&ignored, "option", "", "option-doc") help = full.Info().Help(f) c.Assert(string(help), gc.Equals, fullHelp) optionInfo := full.Info() optionInfo.Doc = "" help = optionInfo.Help(f) c.Assert(string(help), gc.Equals, optionHelp) } var initErrorTests = []struct { c *TestCommand help string }{ {&TestCommand{Name: "verb"}, fullHelp}, {&TestCommand{Name: "verb", Minimal: true}, minimalHelp}, } func (s *CmdSuite) TestMainInitError(c *gc.C) { for _, t := range initErrorTests { ctx := testing.Context(c) result := cmd.Main(t.c, ctx, []string{"--unknown"}) c.Assert(result, gc.Equals, 2) c.Assert(bufferString(ctx.Stdout), gc.Equals, "") expected := "error: flag provided but not defined: --unknown\n" c.Assert(bufferString(ctx.Stderr), gc.Equals, expected) } } func (s *CmdSuite) TestMainRunError(c *gc.C) { ctx := testing.Context(c) result := cmd.Main(&TestCommand{Name: "verb"}, ctx, []string{"--option", "error"}) c.Assert(result, gc.Equals, 1) c.Assert(bufferString(ctx.Stdout), gc.Equals, "") c.Assert(bufferString(ctx.Stderr), gc.Equals, "error: BAM!\n") } func (s *CmdSuite) TestMainRunSilentError(c *gc.C) { ctx := testing.Context(c) result := cmd.Main(&TestCommand{Name: "verb"}, ctx, []string{"--option", "silent-error"}) c.Assert(result, gc.Equals, 1) c.Assert(bufferString(ctx.Stdout), gc.Equals, "") c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } func (s *CmdSuite) TestMainSuccess(c *gc.C) { ctx := testing.Context(c) result := cmd.Main(&TestCommand{Name: "verb"}, ctx, []string{"--option", "success!"}) c.Assert(result, gc.Equals, 0) c.Assert(bufferString(ctx.Stdout), gc.Equals, "success!\n") c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } func (s *CmdSuite) TestStdin(c *gc.C) { const phrase = "Do you, Juju?" ctx := testing.Context(c) ctx.Stdin = bytes.NewBuffer([]byte(phrase)) result := cmd.Main(&TestCommand{Name: "verb"}, ctx, []string{"--option", "echo"}) c.Assert(result, gc.Equals, 0) c.Assert(bufferString(ctx.Stdout), gc.Equals, phrase) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } func (s *CmdSuite) TestMainHelp(c *gc.C) { for _, arg := range []string{"-h", "--help"} { ctx := testing.Context(c) result := cmd.Main(&TestCommand{Name: "verb"}, ctx, []string{arg}) c.Assert(result, gc.Equals, 0) c.Assert(bufferString(ctx.Stdout), gc.Equals, fullHelp) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } } func (s *CmdSuite) TestDefaultContextReturnsErrorInDeletedDirectory(c *gc.C) { ctx := testing.Context(c) wd, err := os.Getwd() c.Assert(err, gc.IsNil) missing := ctx.Dir + "/missing" err = os.Mkdir(missing, 0700) c.Assert(err, gc.IsNil) err = os.Chdir(missing) c.Assert(err, gc.IsNil) defer os.Chdir(wd) err = os.Remove(missing) c.Assert(err, gc.IsNil) ctx, err = cmd.DefaultContext() c.Assert(err, gc.ErrorMatches, `getwd: no such file or directory`) c.Assert(ctx, gc.IsNil) } func (s *CmdSuite) TestCheckEmpty(c *gc.C) { c.Assert(cmd.CheckEmpty(nil), gc.IsNil) c.Assert(cmd.CheckEmpty([]string{"boo!"}), gc.ErrorMatches, `unrecognized args: \["boo!"\]`) } func (s *CmdSuite) TestZeroOrOneArgs(c *gc.C) { expectValue := func(args []string, expected string) { arg, err := cmd.ZeroOrOneArgs(args) c.Assert(arg, gc.Equals, expected) c.Assert(err, gc.IsNil) } expectValue(nil, "") expectValue([]string{}, "") expectValue([]string{"foo"}, "foo") arg, err := cmd.ZeroOrOneArgs([]string{"foo", "bar"}) c.Assert(arg, gc.Equals, "") c.Assert(err, gc.ErrorMatches, `unrecognized args: \["bar"\]`) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/names.go�������������������������������������������0000644�0000153�0000161�00000001132�12321735642�023057� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd import ( "regexp" "launchpad.net/juju-core/names" ) const ( ContainerSnippet = "(/[a-z]+/" + names.NumberSnippet + ")" ContainerSpecSnippet = "([a-z]+:)?" ) var ( validMachineOrNewContainer = regexp.MustCompile("^" + ContainerSpecSnippet + names.MachineSnippet + "$") ) // IsMachineOrNewContainer returns whether spec is a valid machine id // or new container definition. func IsMachineOrNewContainer(spec string) bool { return validMachineOrNewContainer.MatchString(spec) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cmd/args_test.go���������������������������������������0000644�0000153�0000161�00000007167�12321735776�023775� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cmd_test import ( "fmt" "io/ioutil" "launchpad.net/gnuflag" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing/testbase" ) type ArgsSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&ArgsSuite{}) func (*ArgsSuite) TestFlagsUsage(c *gc.C) { for i, test := range []struct { message string defaultValue []string args []string expectedValue []string }{{ message: "nil default and no arg", }, { message: "default value and not set by args", defaultValue: []string{"foo", "bar"}, expectedValue: []string{"foo", "bar"}, }, { message: "no value set by args", args: []string{"--value", "foo,bar"}, expectedValue: []string{"foo", "bar"}, }, { message: "default value and set by args", defaultValue: []string{"omg"}, args: []string{"--value", "foo,bar"}, expectedValue: []string{"foo", "bar"}, }} { c.Log(fmt.Sprintf("%v: %s", i, test.message)) f := gnuflag.NewFlagSet("test", gnuflag.ContinueOnError) f.SetOutput(ioutil.Discard) var value []string f.Var(cmd.NewStringsValue(test.defaultValue, &value), "value", "help") err := f.Parse(false, test.args) c.Check(err, gc.IsNil) c.Check(value, gc.DeepEquals, test.expectedValue) } } func (*ArgsSuite) TestNewStringsValue(c *gc.C) { for i, test := range []struct { message string defaultValue []string }{{ message: "null default", }, { message: "empty default", defaultValue: []string{}, }, { message: "single value", defaultValue: []string{"foo"}, }, { message: "multiple values", defaultValue: []string{"foo", "bar", "baz"}, }} { c.Log(fmt.Sprintf("%v: %s", i, test.message)) var underlyingValue []string _ = cmd.NewStringsValue(test.defaultValue, &underlyingValue) c.Assert(underlyingValue, gc.DeepEquals, test.defaultValue) } } func (*ArgsSuite) TestSet(c *gc.C) { for i, test := range []struct { message string arg string expected []string }{{ message: "empty", expected: []string{""}, }, { message: "just whitespace", arg: " ", expected: []string{" "}, }, { message: "whitespace and comma", arg: " , ", expected: []string{" ", " "}, }, { message: "single value", arg: "foo", expected: []string{"foo"}, }, { message: "single value with comma", arg: "foo,", expected: []string{"foo", ""}, }, { message: "single value with whitespace", arg: " foo ", expected: []string{" foo "}, }, { message: "multiple values", arg: "foo,bar,baz", expected: []string{"foo", "bar", "baz"}, }, { message: "multiple values with spaces", arg: "foo, bar, baz", expected: []string{"foo", " bar", " baz"}, }} { c.Log(fmt.Sprintf("%v: %s", i, test.message)) var result []string value := cmd.NewStringsValue(nil, &result) error := value.Set(test.arg) c.Check(error, gc.IsNil) c.Check(result, gc.DeepEquals, test.expected) } } func (*ArgsSuite) TestString(c *gc.C) { for i, test := range []struct { message string target []string expected string }{{ message: "null", expected: "", }, { message: "empty", target: []string{}, expected: "", }, { message: "single value", target: []string{"foo"}, expected: "foo", }, { message: "multiple values", target: []string{"foo", "bar", "baz"}, expected: "foo,bar,baz", }} { c.Log(fmt.Sprintf("%v: %s", i, test.message)) var temp []string value := cmd.NewStringsValue(test.target, &temp) c.Assert(value.String(), gc.Equals, test.expected) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/�������������������������������������������������0000755�0000153�0000161�00000000000�12321735643�021771� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/service_test.go����������������������������������0000644�0000153�0000161�00000002732�12321735642�025022� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/names" ) type serviceSuite struct{} var _ = gc.Suite(&serviceSuite{}) var serviceNameTests = []struct { pattern string valid bool }{ {pattern: "", valid: false}, {pattern: "wordpress", valid: true}, {pattern: "foo42", valid: true}, {pattern: "doing55in54", valid: true}, {pattern: "%not", valid: false}, {pattern: "42also-not", valid: false}, {pattern: "but-this-works", valid: true}, {pattern: "so-42-far-not-good", valid: false}, {pattern: "foo/42", valid: false}, {pattern: "is-it-", valid: false}, {pattern: "broken2-", valid: false}, {pattern: "foo2", valid: true}, {pattern: "foo-2", valid: false}, } func (s *serviceSuite) TestServiceNameFormats(c *gc.C) { assertService := func(s string, expect bool) { c.Assert(names.IsService(s), gc.Equals, expect) // Check that anything that is considered a valid service name // is also (in)valid if a(n) (in)valid unit designator is added // to it. c.Assert(names.IsUnit(s+"/0"), gc.Equals, expect) c.Assert(names.IsUnit(s+"/99"), gc.Equals, expect) c.Assert(names.IsUnit(s+"/-1"), gc.Equals, false) c.Assert(names.IsUnit(s+"/blah"), gc.Equals, false) c.Assert(names.IsUnit(s+"/"), gc.Equals, false) } for i, test := range serviceNameTests { c.Logf("test %d: %q", i, test.pattern) assertService(test.pattern, test.valid) } } ��������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/tag_test.go��������������������������������������0000644�0000153�0000161�00000011500�12321735642�024126� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/names" ) type tagSuite struct{} var _ = gc.Suite(&tagSuite{}) var tagKindTests = []struct { tag string kind string err string }{ {tag: "unit-wordpress-42", kind: names.UnitTagKind}, {tag: "machine-42", kind: names.MachineTagKind}, {tag: "service-foo", kind: names.ServiceTagKind}, {tag: "environment-42", kind: names.EnvironTagKind}, {tag: "user-admin", kind: names.UserTagKind}, {tag: "relation-service1.rel1#other-svc.other-rel2", kind: names.RelationTagKind}, {tag: "relation-service.peerRelation", kind: names.RelationTagKind}, {tag: "foo", err: `"foo" is not a valid tag`}, {tag: "unit", err: `"unit" is not a valid tag`}, } func (*tagSuite) TestTagKind(c *gc.C) { for i, test := range tagKindTests { c.Logf("test %d: %q -> %q", i, test.tag, test.kind) kind, err := names.TagKind(test.tag) if test.err == "" { c.Assert(test.kind, gc.Equals, kind) c.Assert(err, gc.IsNil) } else { c.Assert(kind, gc.Equals, "") c.Assert(err, gc.ErrorMatches, test.err) } } } var parseTagTests = []struct { tag string expectKind string resultId string resultErr string }{{ tag: "machine-10", expectKind: names.MachineTagKind, resultId: "10", }, { tag: "machine-10-lxc-1", expectKind: names.MachineTagKind, resultId: "10/lxc/1", }, { tag: "foo", expectKind: names.MachineTagKind, resultErr: `"foo" is not a valid machine tag`, }, { tag: "machine-#", expectKind: names.MachineTagKind, resultErr: `"machine-#" is not a valid machine tag`, }, { tag: "unit-wordpress-0", expectKind: names.UnitTagKind, resultId: "wordpress/0", }, { tag: "unit-rabbitmq-server-0", expectKind: names.UnitTagKind, resultId: "rabbitmq-server/0", }, { tag: "foo", expectKind: names.UnitTagKind, resultErr: `"foo" is not a valid unit tag`, }, { tag: "unit-#", expectKind: names.UnitTagKind, resultErr: `"unit-#" is not a valid unit tag`, }, { tag: "service-wordpress", expectKind: names.ServiceTagKind, resultId: "wordpress", }, { tag: "service-#", expectKind: names.ServiceTagKind, resultErr: `"service-#" is not a valid service tag`, }, { tag: "unit-wordpress-0", expectKind: "machine", resultErr: `"unit-wordpress-0" is not a valid machine tag`, }, { tag: "environment-foo", expectKind: names.EnvironTagKind, resultId: "foo", }, { tag: "relation-my-svc1.myrel1#other-svc.other-rel2", expectKind: names.RelationTagKind, resultId: "my-svc1:myrel1 other-svc:other-rel2", }, { tag: "relation-riak.ring", expectKind: names.RelationTagKind, resultId: "riak:ring", }, { tag: "environment-/", expectKind: names.EnvironTagKind, resultErr: `"environment-/" is not a valid environment tag`, }, { tag: "user-foo", expectKind: names.UserTagKind, resultId: "foo", }, { tag: "user-/", expectKind: names.UserTagKind, resultErr: `"user-/" is not a valid user tag`, }, { tag: "foo", expectKind: "", resultErr: `"foo" is not a valid tag`, }} var makeTag = map[string]func(id string) string{ names.MachineTagKind: names.MachineTag, names.UnitTagKind: names.UnitTag, names.ServiceTagKind: names.ServiceTag, names.RelationTagKind: names.RelationTag, // TODO(rog) environment and user, when they have Tag functions. } func (*tagSuite) TestParseTag(c *gc.C) { for i, test := range parseTagTests { c.Logf("test %d: %q expectKind %q", i, test.tag, test.expectKind) kind, id, err := names.ParseTag(test.tag, test.expectKind) if test.resultErr != "" { c.Assert(err, gc.ErrorMatches, test.resultErr) c.Assert(kind, gc.Equals, "") c.Assert(id, gc.Equals, "") // If the tag has a valid kind which matches the // expected kind, test that using an empty // expectKind does not change the error message. if tagKind, err := names.TagKind(test.tag); err == nil && tagKind == test.expectKind { kind, id, err := names.ParseTag(test.tag, "") c.Assert(err, gc.ErrorMatches, test.resultErr) c.Assert(kind, gc.Equals, "") c.Assert(id, gc.Equals, "") } } else { c.Assert(err, gc.IsNil) c.Assert(id, gc.Equals, test.resultId) if test.expectKind != "" { c.Assert(kind, gc.Equals, test.expectKind) } else { expectKind, err := names.TagKind(test.tag) c.Assert(err, gc.IsNil) c.Assert(kind, gc.Equals, expectKind) } // Check that it's reversible. if f := makeTag[kind]; f != nil { reversed := f(id) c.Assert(reversed, gc.Equals, test.tag) } // Check that it parses ok without an expectKind. kind1, id1, err1 := names.ParseTag(test.tag, "") c.Assert(err1, gc.IsNil) c.Assert(kind1, gc.Equals, test.expectKind) c.Assert(id1, gc.Equals, id) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/unit_test.go�������������������������������������0000644�0000153�0000161�00000003514�12321735642�024340� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/names" ) type unitSuite struct{} var _ = gc.Suite(&unitSuite{}) func (s *unitSuite) TestUnitTag(c *gc.C) { c.Assert(names.UnitTag("wordpress/2"), gc.Equals, "unit-wordpress-2") } var unitNameTests = []struct { pattern string valid bool service string }{ {pattern: "wordpress/42", valid: true, service: "wordpress"}, {pattern: "rabbitmq-server/123", valid: true, service: "rabbitmq-server"}, {pattern: "foo", valid: false}, {pattern: "foo/", valid: false}, {pattern: "bar/foo", valid: false}, {pattern: "20/20", valid: false}, {pattern: "foo-55", valid: false}, {pattern: "foo-bar/123", valid: true, service: "foo-bar"}, {pattern: "foo-bar/123/", valid: false}, {pattern: "foo-bar/123-not", valid: false}, } func (s *unitSuite) TestUnitNameFormats(c *gc.C) { for i, test := range unitNameTests { c.Logf("test %d: %q", i, test.pattern) c.Assert(names.IsUnit(test.pattern), gc.Equals, test.valid) } } func (s *unitSuite) TestInvalidUnitTagFormats(c *gc.C) { for i, test := range unitNameTests { if !test.valid { c.Logf("test %d: %q", i, test.pattern) expect := fmt.Sprintf("%q is not a valid unit name", test.pattern) testUnitTag := func() { names.UnitTag(test.pattern) } c.Assert(testUnitTag, gc.PanicMatches, expect) } } } func (s *serviceSuite) TestUnitService(c *gc.C) { for i, test := range unitNameTests { c.Logf("test %d: %q", i, test.pattern) if !test.valid { expect := fmt.Sprintf("%q is not a valid unit name", test.pattern) testFunc := func() { names.UnitService(test.pattern) } c.Assert(testFunc, gc.PanicMatches, expect) } else { c.Assert(names.UnitService(test.pattern), gc.Equals, test.service) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/relation.go��������������������������������������0000644�0000153�0000161�00000002742�12321735642�024141� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names import ( "fmt" "regexp" "strings" ) const RelationSnippet = "[a-z][a-z0-9]*([_-][a-z0-9]+)*" // Relation keys have the format "service1:relName1 service2:relName2". // Except the peer relations, which have the format "service:relName" // Relation tags have the format "relation-service1.rel1#service2.rel2". // For peer relations, the format is "relation-service.rel" var ( validRelation = regexp.MustCompile("^" + ServiceSnippet + ":" + RelationSnippet + " " + ServiceSnippet + ":" + RelationSnippet + "$") validPeerRelation = regexp.MustCompile("^" + ServiceSnippet + ":" + RelationSnippet + "$") ) // IsRelation returns whether key is a valid relation key. func IsRelation(key string) bool { return validRelation.MatchString(key) || validPeerRelation.MatchString(key) } // RelationTag returns the tag for the relation with the given key. func RelationTag(relationKey string) string { if !IsRelation(relationKey) { panic(fmt.Sprintf("%q is not a valid relation key", relationKey)) } // Replace both ":" with "." and the " " with "#". relationKey = strings.Replace(relationKey, ":", ".", 2) relationKey = strings.Replace(relationKey, " ", "#", 1) return makeTag(RelationTagKind, relationKey) } func relationTagSuffixToKey(s string) string { // Replace both "." with ":" and the "#" with " ". s = strings.Replace(s, ".", ":", 2) return strings.Replace(s, "#", " ", 1) } ������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/machine_test.go����������������������������������0000644�0000153�0000161�00000003107�12321735642�024763� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names_test import ( stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/names" ) type machineSuite struct{} var _ = gc.Suite(&machineSuite{}) func Test(t *stdtesting.T) { gc.TestingT(t) } func (s *machineSuite) TestMachineTag(c *gc.C) { c.Assert(names.MachineTag("10"), gc.Equals, "machine-10") // Check a container id. c.Assert(names.MachineTag("10/lxc/1"), gc.Equals, "machine-10-lxc-1") } var machineIdTests = []struct { pattern string valid bool }{ {pattern: "42", valid: true}, {pattern: "042", valid: false}, {pattern: "0", valid: true}, {pattern: "foo", valid: false}, {pattern: "/", valid: false}, {pattern: "55/", valid: false}, {pattern: "1/foo", valid: false}, {pattern: "2/foo/", valid: false}, {pattern: "3/lxc/42", valid: true}, {pattern: "3/lxc-nodash/42", valid: false}, {pattern: "0/lxc/00", valid: false}, {pattern: "0/lxc/0/", valid: false}, {pattern: "03/lxc/42", valid: false}, {pattern: "3/lxc/042", valid: false}, {pattern: "4/foo/bar", valid: false}, {pattern: "5/lxc/42/foo", valid: false}, {pattern: "6/lxc/42/kvm/0", valid: true}, {pattern: "06/lxc/42/kvm/0", valid: false}, {pattern: "6/lxc/042/kvm/0", valid: false}, {pattern: "6/lxc/42/kvm/00", valid: false}, {pattern: "06/lxc/042/kvm/00", valid: false}, } func (s *machineSuite) TestMachineIdFormats(c *gc.C) { for i, test := range machineIdTests { c.Logf("test %d: %q", i, test.pattern) c.Assert(names.IsMachine(test.pattern), gc.Equals, test.valid) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/tag.go�������������������������������������������0000644�0000153�0000161�00000004315�12321735642�023075� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names import ( "fmt" "strings" ) const ( UnitTagKind = "unit" MachineTagKind = "machine" ServiceTagKind = "service" EnvironTagKind = "environment" UserTagKind = "user" RelationTagKind = "relation" ) var validKinds = map[string]bool{ UnitTagKind: true, MachineTagKind: true, ServiceTagKind: true, EnvironTagKind: true, UserTagKind: true, RelationTagKind: true, } var tagSuffixToId = map[string]func(string) string{ UnitTagKind: unitTagSuffixToId, MachineTagKind: machineTagSuffixToId, RelationTagKind: relationTagSuffixToKey, } var verifyId = map[string]func(string) bool{ UnitTagKind: IsUnit, MachineTagKind: IsMachine, ServiceTagKind: IsService, UserTagKind: IsUser, EnvironTagKind: IsEnvironment, RelationTagKind: IsRelation, } // TagKind returns one of the *TagKind constants for the given tag, or // an error if none matches. func TagKind(tag string) (string, error) { i := strings.Index(tag, "-") if i <= 0 || !validKinds[tag[:i]] { return "", fmt.Errorf("%q is not a valid tag", tag) } return tag[:i], nil } func splitTag(tag string) (kind, rest string, err error) { kind, err = TagKind(tag) if err != nil { return "", "", err } return kind, tag[len(kind)+1:], nil } func makeTag(kind, rest string) string { return kind + "-" + rest } // ParseTag parses a tag into its kind and identifier // components. It returns an error if the tag is malformed, // or if expectKind is not empty and the kind is // not as expected. func ParseTag(tag, expectKind string) (kind, id string, err error) { kind, id, err = splitTag(tag) if err != nil { return "", "", invalidTagError(tag, expectKind) } if expectKind != "" && kind != expectKind { return "", "", invalidTagError(tag, expectKind) } if toId := tagSuffixToId[kind]; toId != nil { id = toId(id) } if verify := verifyId[kind]; verify != nil && !verify(id) { return "", "", invalidTagError(tag, kind) } return kind, id, nil } func invalidTagError(tag, kind string) error { if kind != "" { return fmt.Errorf("%q is not a valid %s tag", tag, kind) } return fmt.Errorf("%q is not a valid tag", tag) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/user.go������������������������������������������0000644�0000153�0000161�00000000672�12321735642�023302� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names import ( "strings" ) // IsUser returns whether id is a valid user id. // TODO(rog) stricter constraints func IsUser(name string) bool { return !strings.Contains(name, "/") && name != "" } // UserTag returns the tag for the user with the given name. func UserTag(userName string) string { return makeTag(UserTagKind, userName) } ����������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/user_test.go�������������������������������������0000644�0000153�0000161�00000001142�12321735642�024332� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/names" ) type userSuite struct{} var _ = gc.Suite(&userSuite{}) func (s *userSuite) TestUserTag(c *gc.C) { c.Assert(names.UserTag("admin"), gc.Equals, "user-admin") } func (s *userSuite) TestIsUser(c *gc.C) { c.Assert(names.IsUser("admin"), jc.IsTrue) c.Assert(names.IsUser("foo42"), jc.IsTrue) c.Assert(names.IsUser("not/valid"), jc.IsFalse) c.Assert(names.IsUser(""), jc.IsFalse) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/service.go���������������������������������������0000644�0000153�0000161�00000001134�12321735642�023756� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names import ( "regexp" ) const ( ServiceSnippet = "([a-z][a-z0-9]*(-[a-z0-9]*[a-z][a-z0-9]*)*)" NumberSnippet = "(0|[1-9][0-9]*)" ) var validService = regexp.MustCompile("^" + ServiceSnippet + "$") // IsService returns whether name is a valid service name. func IsService(name string) bool { return validService.MatchString(name) } // ServiceTag returns the tag for the service with the given name. func ServiceTag(serviceName string) string { return makeTag(ServiceTagKind, serviceName) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/environ.go���������������������������������������0000644�0000153�0000161�00000001034�12321735642�023775� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names import ( "strings" ) // EnvironTag returns the tag of an environment with the given environment UUID. func EnvironTag(uuid string) string { return makeTag(EnvironTagKind, uuid) } // IsEnvironment returns whether id is a valid environment UUID. func IsEnvironment(id string) bool { // TODO(axw) 2013-12-04 #1257587 // We should not accept environment tags that // do not look like UUIDs. return !strings.Contains(id, "/") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/relation_test.go���������������������������������0000644�0000153�0000161�00000003445�12321735642�025201� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/names" ) type relationSuite struct{} var _ = gc.Suite(&relationSuite{}) var relationNameTests = []struct { pattern string valid bool }{ {pattern: "", valid: false}, {pattern: "0foo", valid: false}, {pattern: "foo", valid: true}, {pattern: "f1-boo", valid: true}, {pattern: "f-o-o", valid: true}, {pattern: "-foo", valid: false}, {pattern: "fo#o", valid: false}, {pattern: "foo-42", valid: true}, {pattern: "FooBar", valid: false}, {pattern: "foo42-bar1", valid: true}, {pattern: "42", valid: false}, {pattern: "0", valid: false}, {pattern: "%not", valid: false}, {pattern: "42also-not", valid: false}, {pattern: "042", valid: false}, {pattern: "0x42", valid: false}, {pattern: "foo_42", valid: true}, {pattern: "_foo", valid: false}, {pattern: "!foo", valid: false}, {pattern: "foo_bar-baz_boo", valid: true}, {pattern: "foo bar", valid: false}, {pattern: "foo-_", valid: false}, {pattern: "foo-", valid: false}, {pattern: "foo_-a", valid: false}, {pattern: "foo_", valid: false}, } func (s *relationSuite) TestRelationKeyFormats(c *gc.C) { // In order to test all possible combinations, we need // to use the relationNameTests and serviceNameTests // twice to construct all possible keys. for i, testRel := range relationNameTests { for j, testSvc := range serviceNameTests { peerKey := testSvc.pattern + ":" + testRel.pattern key := peerKey + " " + peerKey isValid := testSvc.valid && testRel.valid c.Logf("test %d: %q -> valid: %v", i*len(serviceNameTests)+j, key, isValid) c.Assert(names.IsRelation(key), gc.Equals, isValid) c.Assert(names.IsRelation(peerKey), gc.Equals, isValid) } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/unit.go������������������������������������������0000644�0000153�0000161�00000002453�12321735642�023302� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names import ( "fmt" "regexp" "strings" ) var validUnit = regexp.MustCompile("^" + ServiceSnippet + "/" + NumberSnippet + "$") // UnitTag returns the tag for the unit with the given name. // It will panic if the given unit name is not valid. func UnitTag(unitName string) string { // Replace only the last "/" with "-". i := strings.LastIndex(unitName, "/") if i <= 0 || !IsUnit(unitName) { panic(fmt.Sprintf("%q is not a valid unit name", unitName)) } unitName = unitName[:i] + "-" + unitName[i+1:] return makeTag(UnitTagKind, unitName) } // IsUnit returns whether name is a valid unit name. func IsUnit(name string) bool { return validUnit.MatchString(name) } // UnitService returns the name of the service that the unit is // associated with. It panics if unitName is not a valid unit name. func UnitService(unitName string) string { s := validUnit.FindStringSubmatch(unitName) if s == nil { panic(fmt.Sprintf("%q is not a valid unit name", unitName)) } return s[1] } func unitTagSuffixToId(s string) string { // Replace only the last "-" with "/", as it is valid for service // names to contain hyphens. if i := strings.LastIndex(s, "-"); i > 0 { s = s[:i] + "/" + s[i+1:] } return s } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/names/machine.go���������������������������������������0000644�0000153�0000161�00000001530�12321735642�023722� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package names import ( "regexp" "strings" ) const ( ContainerTypeSnippet = "[a-z]+" ContainerSnippet = "(/" + ContainerTypeSnippet + "/" + NumberSnippet + ")" MachineSnippet = NumberSnippet + ContainerSnippet + "*" ) var validMachine = regexp.MustCompile("^" + MachineSnippet + "$") // IsMachine returns whether id is a valid machine id. func IsMachine(id string) bool { return validMachine.MatchString(id) } // MachineTag returns the tag for the machine with the given id. func MachineTag(id string) string { tag := makeTag(MachineTagKind, id) // Containers require "/" to be replaced by "-". tag = strings.Replace(tag, "/", "-", -1) return tag } func machineTagSuffixToId(s string) string { return strings.Replace(s, "-", "/", -1) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/version/�����������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022337� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/version/export_test.go���������������������������������0000644�0000153�0000161�00000000302�12321735642�025254� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package version var ( ReadSeries = readSeries LSBReleaseFileVar = &lsbReleaseFile ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/version/version_test.go��������������������������������0000644�0000153�0000161�00000016360�12321735642�025433� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package version_test import ( "encoding/json" "io/ioutil" "path/filepath" "strings" "testing" "labix.org/v2/mgo/bson" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) type suite struct { testbase.LoggingSuite } var _ = gc.Suite(&suite{}) func Test(t *testing.T) { gc.TestingT(t) } // N.B. The FORCE-VERSION logic is tested in the environs package. var cmpTests = []struct { v1, v2 string compare int }{ {"1.0.0", "1.0.0", 0}, {"01.0.0", "1.0.0", 0}, {"10.0.0", "9.0.0", 1}, {"1.0.0", "1.0.1", -1}, {"1.0.1", "1.0.0", 1}, {"1.0.0", "1.1.0", -1}, {"1.1.0", "1.0.0", 1}, {"1.0.0", "2.0.0", -1}, {"2.0.0", "1.0.0", 1}, {"2.0.0.0", "2.0.0", 0}, {"2.0.0.0", "2.0.0.0", 0}, {"2.0.0.1", "2.0.0.0", 1}, {"2.0.1.10", "2.0.0.0", 1}, } func (*suite) TestCompare(c *gc.C) { for i, test := range cmpTests { c.Logf("test %d", i) v1, err := version.Parse(test.v1) c.Assert(err, gc.IsNil) v2, err := version.Parse(test.v2) c.Assert(err, gc.IsNil) compare := v1.Compare(v2) c.Check(compare, gc.Equals, test.compare) // Check that reversing the operands has // the expected result. compare = v2.Compare(v1) c.Check(compare, gc.Equals, -test.compare) } } var parseTests = []struct { v string err string expect version.Number dev bool }{{ v: "0.0.0", }, { v: "0.0.1", expect: version.Number{0, 0, 1, 0}, }, { v: "0.0.2", expect: version.Number{0, 0, 2, 0}, }, { v: "0.1.0", expect: version.Number{0, 1, 0, 0}, dev: true, }, { v: "0.2.3", expect: version.Number{0, 2, 3, 0}, }, { v: "1.0.0", expect: version.Number{1, 0, 0, 0}, }, { v: "10.234.3456", expect: version.Number{10, 234, 3456, 0}, }, { v: "10.234.3456.1", expect: version.Number{10, 234, 3456, 1}, dev: true, }, { v: "10.234.3456.64", expect: version.Number{10, 234, 3456, 64}, dev: true, }, { v: "10.235.3456", expect: version.Number{10, 235, 3456, 0}, dev: true, }, { v: "1234567890.2.1", err: "invalid version.*", }, { v: "0.2..1", err: "invalid version.*", }} func (*suite) TestParse(c *gc.C) { for i, test := range parseTests { c.Logf("test %d", i) got, err := version.Parse(test.v) if test.err != "" { c.Assert(err, gc.ErrorMatches, test.err) } else { c.Assert(err, gc.IsNil) c.Assert(got, gc.Equals, test.expect) c.Check(got.IsDev(), gc.Equals, test.dev) c.Check(got.String(), gc.Equals, test.v) } } } func binaryVersion(major, minor, patch, build int, series, arch string) version.Binary { return version.Binary{ Number: version.Number{ Major: major, Minor: minor, Patch: patch, Build: build, }, Series: series, Arch: arch, } } var parseBinaryTests = []struct { v string err string expect version.Binary }{{ v: "1.2.3-a-b", expect: binaryVersion(1, 2, 3, 0, "a", "b"), }, { v: "1.2.3.4-a-b", expect: binaryVersion(1, 2, 3, 4, "a", "b"), }, { v: "1.2.3--b", err: "invalid binary version.*", }, { v: "1.2.3-a-", err: "invalid binary version.*", }} func (*suite) TestParseBinary(c *gc.C) { for i, test := range parseBinaryTests { c.Logf("test 1: %d", i) got, err := version.ParseBinary(test.v) if test.err != "" { c.Assert(err, gc.ErrorMatches, test.err) } else { c.Assert(err, gc.IsNil) c.Assert(got, gc.Equals, test.expect) } } for i, test := range parseTests { c.Logf("test 2: %d", i) v := test.v + "-a-b" got, err := version.ParseBinary(v) expect := version.Binary{ Number: test.expect, Series: "a", Arch: "b", } if test.err != "" { c.Assert(err, gc.ErrorMatches, strings.Replace(test.err, "version", "binary version", 1)) } else { c.Assert(err, gc.IsNil) c.Assert(got, gc.Equals, expect) c.Check(got.IsDev(), gc.Equals, test.dev) } } } var marshallers = []struct { name string marshal func(interface{}) ([]byte, error) unmarshal func([]byte, interface{}) error }{{ "json", json.Marshal, json.Unmarshal, }, { "bson", bson.Marshal, bson.Unmarshal, }, { "yaml", goyaml.Marshal, goyaml.Unmarshal, }} func (*suite) TestBinaryMarshalUnmarshal(c *gc.C) { for _, m := range marshallers { c.Logf("encoding %v", m.name) type doc struct { Version *version.Binary } // Work around goyaml bug #1096149 // SetYAML is not called for non-pointer fields. bp := version.MustParseBinary("1.2.3-foo-bar") v := doc{&bp} data, err := m.marshal(&v) c.Assert(err, gc.IsNil) var bv doc err = m.unmarshal(data, &bv) c.Assert(err, gc.IsNil) c.Assert(bv, gc.DeepEquals, v) } } func (*suite) TestNumberMarshalUnmarshal(c *gc.C) { for _, m := range marshallers { c.Logf("encoding %v", m.name) type doc struct { Version *version.Number } // Work around goyaml bug #1096149 // SetYAML is not called for non-pointer fields. np := version.MustParse("1.2.3") v := doc{&np} data, err := m.marshal(&v) c.Assert(err, gc.IsNil) var nv doc err = m.unmarshal(data, &nv) c.Assert(err, gc.IsNil) c.Assert(nv, gc.DeepEquals, v) } } var parseMajorMinorTests = []struct { v string err string expectMajor int expectMinor int }{{ v: "1.2", expectMajor: 1, expectMinor: 2, }, { v: "1", expectMajor: 1, expectMinor: -1, }, { v: "1.2.3", err: "invalid major.minor version number 1.2.3", }, { v: "blah", err: `invalid major version number blah: strconv.ParseInt: parsing "blah": invalid syntax`, }} func (*suite) TestParseMajorMinor(c *gc.C) { for i, test := range parseMajorMinorTests { c.Logf("test %d", i) major, minor, err := version.ParseMajorMinor(test.v) if test.err != "" { c.Check(err, gc.ErrorMatches, test.err) } else { c.Check(err, gc.IsNil) c.Check(major, gc.Equals, test.expectMajor) c.Check(minor, gc.Equals, test.expectMinor) } } } func (s *suite) TestUseFastLXC(c *gc.C) { for i, test := range []struct { message string releaseContent string expected string }{{ message: "missing release file", }, { message: "missing prefix in file", releaseContent: "some junk\nand more junk", }, { message: "precise release", releaseContent: ` DISTRIB_ID=Ubuntu DISTRIB_RELEASE=12.04 DISTRIB_CODENAME=precise DISTRIB_DESCRIPTION="Ubuntu 12.04.3 LTS" `, expected: "12.04", }, { message: "trusty release", releaseContent: ` DISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.04 DISTRIB_CODENAME=trusty DISTRIB_DESCRIPTION="Ubuntu Trusty Tahr (development branch)" `, expected: "14.04", }, { message: "minimal trusty release", releaseContent: `DISTRIB_RELEASE=14.04`, expected: "14.04", }, { message: "minimal unstable unicorn", releaseContent: `DISTRIB_RELEASE=14.10`, expected: "14.10", }, { message: "minimal jaunty", releaseContent: `DISTRIB_RELEASE=9.10`, expected: "9.10", }} { c.Logf("%v: %v", i, test.message) filename := filepath.Join(c.MkDir(), "lsbRelease") s.PatchValue(version.LSBReleaseFileVar, filename) if test.releaseContent != "" { err := ioutil.WriteFile(filename, []byte(test.releaseContent+"\n"), 0644) c.Assert(err, gc.IsNil) } value := version.ReleaseVersion() c.Assert(value, gc.Equals, test.expected) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/version/version.go�������������������������������������0000644�0000153�0000161�00000020570�12321735776�024402� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The version package implements version parsing. // It also acts as guardian of the current client Juju version number. package version import ( "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" "regexp" "strconv" "strings" "labix.org/v2/mgo/bson" "launchpad.net/juju-core/juju/arch" ) // 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.1" // lsbReleaseFile is the name of the file that is read in order to determine // the release version of ubuntu. var lsbReleaseFile = "/etc/lsb-release" // Current gives the current version of the system. If the file // "FORCE-VERSION" is present in the same directory as the running // binary, it will override this. var Current = Binary{ Number: MustParse(version), Series: readSeries(lsbReleaseFile), Arch: arch.HostArch(), } func init() { toolsDir := filepath.Dir(os.Args[0]) v, err := ioutil.ReadFile(filepath.Join(toolsDir, "FORCE-VERSION")) if err != nil { if !os.IsNotExist(err) { fmt.Fprintf(os.Stderr, "WARNING: cannot read forced version: %v\n", err) } return } Current.Number = MustParse(strings.TrimSpace(string(v))) } // Number represents a juju version. When bugs are fixed the patch // number is incremented; when new features are added the minor number // is incremented and patch is reset; and when compatibility is broken // the major version is incremented and minor and patch are reset. The // build number is automatically assigned and has no well defined // sequence. If the build number is greater than zero or the minor // version is odd, it indicates that the release is still in // development. type Number struct { Major int Minor int Patch int Build int } // Zero is occasionally convenient and readable. // Please don't change its value. var Zero = Number{} // Binary specifies a binary version of juju. type Binary struct { Number Series string Arch string } func (v Binary) String() string { return fmt.Sprintf("%v-%s-%s", v.Number, v.Series, v.Arch) } // GetBSON turns v into a bson.Getter so it can be saved directly // on a MongoDB database with mgo. func (v Binary) GetBSON() (interface{}, error) { return v.String(), nil } // SetBSON turns v into a bson.Setter so it can be loaded directly // from a MongoDB database with mgo. func (vp *Binary) SetBSON(raw bson.Raw) error { var s string err := raw.Unmarshal(&s) if err != nil { return err } v, err := ParseBinary(s) if err != nil { return err } *vp = v return nil } func (v Binary) MarshalJSON() ([]byte, error) { return json.Marshal(v.String()) } func (vp *Binary) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } v, err := ParseBinary(s) if err != nil { return err } *vp = v return nil } // GetYAML implements goyaml.Getter func (v Binary) GetYAML() (tag string, value interface{}) { return "", v.String() } // SetYAML implements goyaml.Setter func (vp *Binary) SetYAML(tag string, value interface{}) bool { vstr := fmt.Sprintf("%v", value) if vstr == "" { return false } v, err := ParseBinary(vstr) if err != nil { return false } *vp = v return true } var ( binaryPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?-([^-]+)-([^-]+)$`) numberPat = regexp.MustCompile(`^(\d{1,9})\.(\d{1,9})\.(\d{1,9})(\.\d{1,9})?$`) ) // MustParse parses a version and panics if it does // not parse correctly. func MustParse(s string) Number { v, err := Parse(s) if err != nil { panic(err) } return v } // MustParseBinary parses a binary version and panics if it does // not parse correctly. func MustParseBinary(s string) Binary { v, err := ParseBinary(s) if err != nil { panic(err) } return v } // ParseBinary parses a binary version of the form "1.2.3-series-arch". func ParseBinary(s string) (Binary, error) { m := binaryPat.FindStringSubmatch(s) if m == nil { return Binary{}, fmt.Errorf("invalid binary version %q", s) } var v Binary v.Major = atoi(m[1]) v.Minor = atoi(m[2]) v.Patch = atoi(m[3]) if m[4] != "" { v.Build = atoi(m[4][1:]) } v.Series = m[5] v.Arch = m[6] return v, nil } // Parse parses the version, which is of the form 1.2.3 // giving the major, minor and release versions // respectively. func Parse(s string) (Number, error) { m := numberPat.FindStringSubmatch(s) if m == nil { return Number{}, fmt.Errorf("invalid version %q", s) } var v Number v.Major = atoi(m[1]) v.Minor = atoi(m[2]) v.Patch = atoi(m[3]) if m[4] != "" { v.Build = atoi(m[4][1:]) } return v, nil } // atoi is the same as strconv.Atoi but assumes that // the string has been verified to be a valid integer. func atoi(s string) int { n, err := strconv.Atoi(s) if err != nil { panic(err) } return n } func (v Number) String() string { s := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) if v.Build > 0 { s += fmt.Sprintf(".%d", v.Build) } return s } // Compare returns -1, 0 or 1 depending on whether // v is less than, equal to or greater than w. func (v Number) Compare(w Number) int { if v == w { return 0 } less := false switch { case v.Major != w.Major: less = v.Major < w.Major case v.Minor != w.Minor: less = v.Minor < w.Minor case v.Patch != w.Patch: less = v.Patch < w.Patch case v.Build != w.Build: less = v.Build < w.Build } if less { return -1 } return 1 } // GetBSON turns v into a bson.Getter so it can be saved directly // on a MongoDB database with mgo. func (v Number) GetBSON() (interface{}, error) { return v.String(), nil } // SetBSON turns v into a bson.Setter so it can be loaded directly // from a MongoDB database with mgo. func (vp *Number) SetBSON(raw bson.Raw) error { var s string err := raw.Unmarshal(&s) if err != nil { return err } v, err := Parse(s) if err != nil { return err } *vp = v return nil } func (v Number) MarshalJSON() ([]byte, error) { return json.Marshal(v.String()) } func (vp *Number) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } v, err := Parse(s) if err != nil { return err } *vp = v return nil } // GetYAML implements goyaml.Getter func (v Number) GetYAML() (tag string, value interface{}) { return "", v.String() } // SetYAML implements goyaml.Setter func (vp *Number) SetYAML(tag string, value interface{}) bool { vstr := fmt.Sprintf("%v", value) if vstr == "" { return false } v, err := Parse(vstr) if err != nil { return false } *vp = v return true } func isOdd(x int) bool { return x%2 != 0 } // IsDev returns whether the version represents a development // version. A version with an odd-numbered minor component or // a nonzero build component is considered to be a development // version. func (v Number) IsDev() bool { return isOdd(v.Minor) || v.Build > 0 } func readSeries(releaseFile string) string { data, err := ioutil.ReadFile(releaseFile) if err != nil { return "unknown" } for _, line := range strings.Split(string(data), "\n") { const prefix = "DISTRIB_CODENAME=" if strings.HasPrefix(line, prefix) { return strings.Trim(line[len(prefix):], "\t '\"") } } return "unknown" } // ReleaseVersion looks for the value of DISTRIB_RELEASE in the content of // the lsbReleaseFile. If the value is not found, the file is not found, or // an error occurs reading the file, an empty string is returned. func ReleaseVersion() string { content, err := ioutil.ReadFile(lsbReleaseFile) if err != nil { return "" } const prefix = "DISTRIB_RELEASE=" for _, line := range strings.Split(string(content), "\n") { if strings.HasPrefix(line, prefix) { return strings.Trim(line[len(prefix):], "\t '\"") } } return "" } // ParseMajorMinor takes an argument of the form "major.minor" and returns ints major and minor. func ParseMajorMinor(vers string) (int, int, error) { parts := strings.Split(vers, ".") major, err := strconv.Atoi(parts[0]) minor := -1 if err != nil { return -1, -1, fmt.Errorf("invalid major version number %s: %v", parts[0], err) } if len(parts) == 2 { minor, err = strconv.Atoi(parts[1]) if err != nil { return -1, -1, fmt.Errorf("invalid minor version number %s: %v", parts[1], err) } } else if len(parts) > 2 { return -1, -1, fmt.Errorf("invalid major.minor version number %s", vers) } return major, minor, nil } ����������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/version/current_test.go��������������������������������0000644�0000153�0000161�00000002745�12321735642�025432� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package version_test import ( "io/ioutil" "os/exec" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/version" ) type CurrentSuite struct{} var _ = gc.Suite(&CurrentSuite{}) var readSeriesTests = []struct { contents string series string }{{ `DISTRIB_ID=Ubuntu DISTRIB_RELEASE=12.04 DISTRIB_CODENAME=precise DISTRIB_DESCRIPTION="Ubuntu 12.04 LTS"`, "precise", }, { "DISTRIB_CODENAME= \tprecise\t", "precise", }, { `DISTRIB_CODENAME="precise"`, "precise", }, { "DISTRIB_CODENAME='precise'", "precise", }, { `DISTRIB_ID=Ubuntu DISTRIB_RELEASE=12.10 DISTRIB_CODENAME=quantal DISTRIB_DESCRIPTION="Ubuntu 12.10"`, "quantal", }, { "", "unknown", }, } func (*CurrentSuite) TestReadSeries(c *gc.C) { d := c.MkDir() f := filepath.Join(d, "foo") for i, t := range readSeriesTests { c.Logf("test %d", i) err := ioutil.WriteFile(f, []byte(t.contents), 0666) c.Assert(err, gc.IsNil) c.Assert(version.ReadSeries(f), gc.Equals, t.series) } } func (*CurrentSuite) TestCurrentSeries(c *gc.C) { s := version.Current.Series if s == "unknown" { s = "n/a" } out, err := exec.Command("lsb_release", "-c").CombinedOutput() if err != nil { // If the command fails (for instance if we're running on some other // platform) then CurrentSeries should be unknown. c.Assert(s, gc.Equals, "n/a") } else { c.Assert(string(out), gc.Equals, "Codename:\t"+s+"\n") } } ���������������������������juju-core_1.18.1/src/launchpad.net/juju-core/thirdparty/��������������������������������������������0000755�0000153�0000161�00000000000�12321735641�023056� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/thirdparty/pbkdf2/�������������������������������������0000755�0000153�0000161�00000000000�12321735642�024227� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/thirdparty/pbkdf2/pbkdf2_test.go�����������������������0000644�0000153�0000161�00000006275�12321735642�026777� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 pbkdf2 import ( "bytes" "crypto/sha1" "crypto/sha256" "hash" "testing" ) type testVector struct { password string salt string iter int output []byte } // Test vectors from RFC 6070, http://tools.ietf.org/html/rfc6070 var sha1TestVectors = []testVector{ { "password", "salt", 1, []byte{ 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, 0x2f, 0xe0, 0x37, 0xa6, }, }, { "password", "salt", 2, []byte{ 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, 0xd8, 0xde, 0x89, 0x57, }, }, { "password", "salt", 4096, []byte{ 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, 0x65, 0xa4, 0x29, 0xc1, }, }, // // This one takes too long // { // "password", // "salt", // 16777216, // []byte{ // 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4, // 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c, // 0x26, 0x34, 0xe9, 0x84, // }, // }, { "passwordPASSWORDpassword", "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, []byte{ 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a, 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, 0x38, }, }, { "pass\000word", "sa\000lt", 4096, []byte{ 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d, 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3, }, }, } // Test vectors from // http://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors var sha256TestVectors = []testVector{ { "password", "salt", 1, []byte{ 0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, }, }, { "password", "salt", 2, []byte{ 0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3, 0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, 0x6d, 0xd0, 0x2a, 0x30, 0x3f, 0x8e, }, }, { "password", "salt", 4096, []byte{ 0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, }, }, { "passwordPASSWORDpassword", "saltSALTsaltSALTsaltSALTsaltSALTsalt", 4096, []byte{ 0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f, 0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf, 0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18, 0x1c, }, }, { "pass\000word", "sa\000lt", 4096, []byte{ 0x89, 0xb6, 0x9d, 0x05, 0x16, 0xf8, 0x29, 0x89, 0x3c, 0x69, 0x62, 0x26, 0x65, 0x0a, 0x86, 0x87, }, }, } func testHash(t *testing.T, h func() hash.Hash, hashName string, vectors []testVector) { for i, v := range vectors { o := Key([]byte(v.password), []byte(v.salt), v.iter, len(v.output), h) if !bytes.Equal(o, v.output) { t.Errorf("%s %d: expected %x, got %x", hashName, i, v.output, o) } } } func TestWithHMACSHA1(t *testing.T) { testHash(t, sha1.New, "SHA1", sha1TestVectors) } func TestWithHMACSHA256(t *testing.T) { testHash(t, sha256.New, "SHA256", sha256TestVectors) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/thirdparty/pbkdf2/pbkdf2.go����������������������������0000644�0000153�0000161�00000004710�12321735642�025730� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Original package at code.google.com/p/go.crypto/pbkdf2 // 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 pbkdf2 implements the key derivation function PBKDF2 as defined in RFC 2898 / PKCS #5 v2.0. A key derivation function is useful when encrypting data based on a password or any other not-fully-random data. It uses a pseudorandom function to derive a secure encryption key based on the password. While v2.0 of the standard defines only one pseudorandom function to use, HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To choose, you can pass the `New` functions from the different SHA packages to pbkdf2.Key. */ package pbkdf2 import ( "crypto/hmac" "hash" ) // Key derives a key from the password, salt and iteration count, returning a // []byte of length keylen that can be used as cryptographic key. The key is // derived based on the method described as PBKDF2 with the HMAC variant using // the supplied hash function. // // For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you // can get a derived key for e.g. AES-256 (which needs a 32-byte key) by // doing: // // dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) // // Remember to get a good random salt. At least 8 bytes is recommended by the // RFC. // // Using a higher iteration count will increase the cost of an exhaustive // search but will also make derivation proportionally slower. func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { prf := hmac.New(h, password) hashLen := prf.Size() numBlocks := (keyLen + hashLen - 1) / hashLen var buf [4]byte dk := make([]byte, 0, numBlocks*hashLen) U := make([]byte, hashLen) for block := 1; block <= numBlocks; block++ { // N.B.: || means concatenation, ^ means XOR // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter // U_1 = PRF(password, salt || uint(i)) prf.Reset() prf.Write(salt) buf[0] = byte(block >> 24) buf[1] = byte(block >> 16) buf[2] = byte(block >> 8) buf[3] = byte(block) prf.Write(buf[:4]) dk = prf.Sum(dk) T := dk[len(dk)-hashLen:] copy(U, T) // U_n = PRF(password, U_(n-1)) for n := 2; n <= iter; n++ { prf.Reset() prf.Write(U) U = U[:0] U = prf.Sum(U) for x := range U { T[x] ^= U[x] } } } return dk[:keyLen] } ��������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/��������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�021627� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/export_test.go������������������������������������0000644�0000153�0000161�00000000257�12321735776�024565� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package juju var ( APIOpen = &apiOpen APIClose = &apiClose ProviderConnectDelay = &providerConnectDelay NewAPIFromStore = newAPIFromStore ) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/deploy.go�����������������������������������������0000644�0000153�0000161�00000011423�12321735776�023476� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package juju import ( "errors" "fmt" "strings" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" ) // DeployServiceParams contains the arguments required to deploy the referenced // charm. type DeployServiceParams struct { ServiceName string Charm *state.Charm ConfigSettings charm.Settings Constraints constraints.Value NumUnits int // ToMachineSpec is either: // - an existing machine/container id eg "1" or "1/lxc/2" // - a new container on an existing machine eg "lxc:1" // Use string to avoid ambiguity around machine 0. ToMachineSpec string // IncludeNetworks holds a list of networks to start on boot. IncludeNetworks []string // ExcludeNetworks holds a list of networks to disable on boot. ExcludeNetworks []string } // DeployService takes a charm and various parameters and deploys it. func DeployService(st *state.State, args DeployServiceParams) (*state.Service, error) { if args.NumUnits > 1 && args.ToMachineSpec != "" { return nil, errors.New("cannot use --num-units with --to") } settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) if err != nil { return nil, err } if args.Charm.Meta().Subordinate { if args.NumUnits != 0 || args.ToMachineSpec != "" { return nil, fmt.Errorf("subordinate service must be deployed without units") } if !constraints.IsEmpty(&args.Constraints) { return nil, fmt.Errorf("subordinate service must be deployed without constraints") } } // TODO(fwereade): transactional State.AddService including settings, constraints // (minimumUnitCount, initialMachineIds?). service, err := st.AddService( args.ServiceName, "user-admin", args.Charm, args.IncludeNetworks, args.ExcludeNetworks, ) if err != nil { return nil, err } if len(settings) > 0 { if err := service.UpdateConfigSettings(settings); err != nil { return nil, err } } if args.Charm.Meta().Subordinate { return service, nil } if !constraints.IsEmpty(&args.Constraints) { if err := service.SetConstraints(args.Constraints); err != nil { return nil, err } } if args.NumUnits > 0 { if _, err := AddUnits(st, service, args.NumUnits, args.ToMachineSpec); err != nil { return nil, err } } return service, nil } // AddUnits starts n units of the given service and allocates machines // to them as necessary. func AddUnits(st *state.State, svc *state.Service, n int, machineIdSpec string) ([]*state.Unit, error) { units := make([]*state.Unit, n) // Hard code for now till we implement a different approach. policy := state.AssignCleanEmpty // All units should have the same networks as the service. includeNetworks, excludeNetworks, err := svc.Networks() if err != nil { return nil, fmt.Errorf("cannot get service %q networks: %v", svc.Name(), err) } // TODO what do we do if we fail half-way through this process? for i := 0; i < n; i++ { unit, err := svc.AddUnit() if err != nil { return nil, fmt.Errorf("cannot add unit %d/%d to service %q: %v", i+1, n, svc.Name(), err) } if machineIdSpec != "" { if n != 1 { return nil, fmt.Errorf("cannot add multiple units of service %q to a single machine", svc.Name()) } // machineIdSpec may be an existing machine or container, eg 3/lxc/2 // or a new container on a machine, eg lxc:3 mid := machineIdSpec var containerType instance.ContainerType specParts := strings.SplitN(machineIdSpec, ":", 2) if len(specParts) > 1 { firstPart := specParts[0] var err error if containerType, err = instance.ParseContainerType(firstPart); err == nil { mid = specParts[1] } else { mid = machineIdSpec } } if !names.IsMachine(mid) { return nil, fmt.Errorf("invalid force machine id %q", mid) } var err error var m *state.Machine // If a container is to be used, create it. if containerType != "" { // Create the new machine marked as dirty so that // nothing else will grab it before we assign the unit to it. template := state.MachineTemplate{ Series: unit.Series(), Jobs: []state.MachineJob{state.JobHostUnits}, Dirty: true, IncludeNetworks: includeNetworks, ExcludeNetworks: excludeNetworks, } m, err = st.AddMachineInsideMachine(template, mid, containerType) } else { m, err = st.Machine(mid) } if err != nil { return nil, fmt.Errorf("cannot assign unit %q to machine: %v", unit.Name(), err) } err = unit.AssignToMachine(m) if err != nil { return nil, err } } else if err := st.AssignUnit(unit, policy); err != nil { return nil, err } units[i] = unit } return units, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/api.go��������������������������������������������0000644�0000153�0000161�00000023437�12321735776�022763� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package juju import ( "fmt" "io" "time" "github.com/juju/loggo" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/keymanager" "launchpad.net/juju-core/utils/parallel" ) var logger = loggo.GetLogger("juju") // The following are variables so that they can be // changed by tests. var ( apiOpen = api.Open apiClose = (*api.State).Close providerConnectDelay = 2 * time.Second ) // apiState wraps an api.State, redefining its Close method // so we can abuse it for testing purposes. type apiState struct { st *api.State // If cachedInfo is non-nil, it indicates that the info has been // newly retrieved, and should be cached in the config store. cachedInfo *api.Info } func (st apiState) Close() error { return apiClose(st.st) } // APIConn holds a connection to a juju environment and its // associated state through its API interface. type APIConn struct { Environ environs.Environ State *api.State } var errAborted = fmt.Errorf("aborted") // NewAPIConn returns a new Conn that uses the // given environment. The environment must have already // been bootstrapped. func NewAPIConn(environ environs.Environ, dialOpts api.DialOpts) (*APIConn, error) { info, err := environAPIInfo(environ) if err != nil { return nil, err } st, err := apiOpen(info, dialOpts) // TODO(rog): handle errUnauthorized when the API handles passwords. if err != nil { return nil, err } return &APIConn{ Environ: environ, State: st, }, nil } // Close terminates the connection to the environment and releases // any associated resources. func (c *APIConn) Close() error { return apiClose(c.State) } // NewAPIClientFromName returns an api.Client connected to the API Server for // the named environment. If envName is "", the default environment // will be used. func NewAPIClientFromName(envName string) (*api.Client, error) { st, err := newAPIClient(envName) if err != nil { return nil, err } return st.Client(), nil } // NewKeyManagerClient returns an api.keymanager.Client connected to the API Server for // the named environment. If envName is "", the default environment will be used. func NewKeyManagerClient(envName string) (*keymanager.Client, error) { st, err := newAPIClient(envName) if err != nil { return nil, err } return keymanager.NewClient(st), nil } // NewAPIFromName returns an api.State connected to the API Server for // the named environment. If envName is "", the default environment will // be used. func NewAPIFromName(envName string) (*api.State, error) { return newAPIClient(envName) } func newAPIClient(envName string) (*api.State, error) { store, err := configstore.NewDisk(osenv.JujuHome()) if err != nil { return nil, err } return newAPIFromStore(envName, store) } // newAPIFromStore implements the bulk of NewAPIClientFromName // but is separate for testing purposes. func newAPIFromStore(envName string, store configstore.Storage) (*api.State, error) { // Try to read the default environment configuration file. // If it doesn't exist, we carry on in case // there's some environment info for that environment. // This enables people to copy environment files // into their .juju/environments directory and have // them be directly useful with no further configuration changes. envs, err := environs.ReadEnvirons("") if err == nil { if envName == "" { envName = envs.Default } if envName == "" { return nil, fmt.Errorf("no default environment found") } } else if !environs.IsNoEnv(err) { return nil, err } // Try to connect to the API concurrently using two different // possible sources of truth for the API endpoint. Our // preference is for the API endpoint cached in the API info, // because we know that without needing to access any remote // provider. However, the addresses stored there may no longer // be current (and the network connection may take a very long // time to time out) so we also try to connect using information // found from the provider. We only start to make that // connection after some suitable delay, so that in the // hopefully usual case, we will make the connection to the API // and never hit the provider. By preference we use provider // attributes from the config store, but for backward // compatibility reasons, we fall back to information from // ReadEnvirons if that does not exist. chooseError := func(err0, err1 error) error { if err0 == nil { return err1 } if errorImportance(err0) < errorImportance(err1) { err0, err1 = err1, err0 } logger.Warningf("discarding API open error: %v", err1) return err0 } try := parallel.NewTry(0, chooseError) info, err := store.ReadInfo(envName) if err != nil && !errors.IsNotFoundError(err) { return nil, err } var delay time.Duration if info != nil && len(info.APIEndpoint().Addresses) > 0 { logger.Debugf("trying cached API connection settings") try.Start(func(stop <-chan struct{}) (io.Closer, error) { return apiInfoConnect(store, info, stop) }) // Delay the config connection until we've spent // some time trying to connect to the cached info. delay = providerConnectDelay } else { logger.Debugf("no cached API connection settings found") } try.Start(func(stop <-chan struct{}) (io.Closer, error) { return apiConfigConnect(info, envs, envName, stop, delay) }) try.Close() val0, err := try.Result() if err != nil { if ierr, ok := err.(*infoConnectError); ok { // lose error encapsulation: err = ierr.error } return nil, err } val := val0.(apiState) if val.cachedInfo != nil && info != nil { // Cache the connection settings only if we used the // environment config, but any errors are just logged // as warnings, because they're not fatal. err = cacheAPIInfo(info, val.cachedInfo) if err != nil { logger.Warningf(err.Error()) } else { logger.Debugf("updated API connection settings cache") } } return val.st, nil } func errorImportance(err error) int { if err == nil { return 0 } if errors.IsNotFoundError(err) { // An error from an actual connection attempt // is more interesting than the fact that there's // no environment info available. return 1 } if _, ok := err.(*infoConnectError); ok { // A connection to a potentially stale cached address // is less important than a connection from fresh info. return 2 } return 3 } type infoConnectError struct { error } // apiInfoConnect looks for endpoint on the given environment and // tries to connect to it, sending the result on the returned channel. func apiInfoConnect(store configstore.Storage, info configstore.EnvironInfo, stop <-chan struct{}) (apiState, error) { endpoint := info.APIEndpoint() if info == nil || len(endpoint.Addresses) == 0 { return apiState{}, &infoConnectError{fmt.Errorf("no cached addresses")} } logger.Infof("connecting to API addresses: %v", endpoint.Addresses) apiInfo := &api.Info{ Addrs: endpoint.Addresses, CACert: []byte(endpoint.CACert), Tag: names.UserTag(info.APICredentials().User), Password: info.APICredentials().Password, } st, err := apiOpen(apiInfo, api.DefaultDialOpts()) if err != nil { return apiState{}, &infoConnectError{err} } return apiState{st, nil}, err } // apiConfigConnect looks for configuration info on the given environment, // and tries to use an Environ constructed from that to connect to // its endpoint. It only starts the attempt after the given delay, // to allow the faster apiInfoConnect to hopefully succeed first. // It returns nil if there was no configuration information found. func apiConfigConnect(info configstore.EnvironInfo, envs *environs.Environs, envName string, stop <-chan struct{}, delay time.Duration) (apiState, error) { var cfg *config.Config var err error if info != nil && len(info.BootstrapConfig()) > 0 { cfg, err = config.New(config.NoDefaults, info.BootstrapConfig()) } else if envs != nil { cfg, err = envs.Config(envName) if errors.IsNotFoundError(err) { return apiState{}, err } } else { return apiState{}, errors.NotFoundf("environment %q", envName) } select { case <-time.After(delay): case <-stop: return apiState{}, errAborted } environ, err := environs.New(cfg) if err != nil { return apiState{}, err } apiInfo, err := environAPIInfo(environ) if err != nil { return apiState{}, err } st, err := apiOpen(apiInfo, api.DefaultDialOpts()) // TODO(rog): handle errUnauthorized when the API handles passwords. if err != nil { return apiState{}, err } return apiState{st, apiInfo}, nil } func environAPIInfo(environ environs.Environ) (*api.Info, error) { _, info, err := environ.StateInfo() if err != nil { return nil, err } info.Tag = "user-admin" password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } info.Password = password return info, nil } // cacheAPIInfo updates the local environment settings (.jenv file) // with the provided apiInfo, assuming we've just successfully // connected to the API server. func cacheAPIInfo(info configstore.EnvironInfo, apiInfo *api.Info) error { info.SetAPIEndpoint(configstore.APIEndpoint{ Addresses: apiInfo.Addrs, CACert: string(apiInfo.CACert), }) _, username, err := names.ParseTag(apiInfo.Tag, names.UserTagKind) if err != nil { return fmt.Errorf("not caching API connection settings: invalid API user tag: %v", err) } info.SetAPICredentials(configstore.APICredentials{ User: username, Password: apiInfo.Password, }) if err := info.Write(); err != nil { return fmt.Errorf("cannot cache API connection settings: %v", err) } return nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/conn.go�������������������������������������������0000644�0000153�0000161�00000016574�12321735642�023143� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package juju import ( stderrors "errors" "fmt" "io/ioutil" "net/url" "os" "time" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/log" "launchpad.net/juju-core/state" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" ) // Conn holds a connection to a juju environment and its // associated state. type Conn struct { Environ environs.Environ State *state.State } var redialStrategy = utils.AttemptStrategy{ Total: 60 * time.Second, Delay: 250 * time.Millisecond, } // NewConn returns a new Conn that uses the // given environment. The environment must have already // been bootstrapped. func NewConn(environ environs.Environ) (*Conn, error) { info, _, err := environ.StateInfo() if err != nil { return nil, err } password := environ.Config().AdminSecret() if password == "" { return nil, fmt.Errorf("cannot connect without admin-secret") } err = environs.CheckEnvironment(environ) if err != nil { return nil, err } info.Password = password opts := state.DefaultDialOpts() st, err := state.Open(info, opts, environs.NewStatePolicy()) if errors.IsUnauthorizedError(err) { log.Noticef("juju: authorization error while connecting to state server; retrying") // We can't connect with the administrator password,; // perhaps this was the first connection and the // password has not been changed yet. info.Password = utils.UserPasswordHash(password, utils.CompatSalt) // We try for a while because we might succeed in // connecting to mongo before the state has been // initialized and the initial password set. for a := redialStrategy.Start(); a.Next(); { st, err = state.Open(info, opts, environs.NewStatePolicy()) if !errors.IsUnauthorizedError(err) { break } } if err != nil { return nil, err } if err := st.SetAdminMongoPassword(password); err != nil { return nil, err } } else if err != nil { return nil, err } conn := &Conn{ Environ: environ, State: st, } if err := conn.updateSecrets(); err != nil { conn.Close() return nil, fmt.Errorf("unable to push secrets: %v", err) } return conn, nil } // NewConnFromState returns a Conn that uses an Environ // made by reading the environment configuration. // The resulting Conn uses the given State - closing // it will close that State. func NewConnFromState(st *state.State) (*Conn, error) { cfg, err := st.EnvironConfig() if err != nil { return nil, err } environ, err := environs.New(cfg) if err != nil { return nil, err } return &Conn{ Environ: environ, State: st, }, nil } // NewConnFromName returns a Conn pointing at the environName environment, or the // default environment if not specified. func NewConnFromName(environName string) (*Conn, error) { store, err := configstore.Default() if err != nil { return nil, err } environ, err := environs.NewFromName(environName, store) if err != nil { return nil, err } return NewConn(environ) } // Close terminates the connection to the environment and releases // any associated resources. func (c *Conn) Close() error { return c.State.Close() } // updateSecrets writes secrets into the environment when there are none. // This is done because environments such as ec2 offer no way to securely // deliver the secrets onto the machine, so the bootstrap is done with the // whole environment configuration but without secrets, and then secrets // are delivered on the first communication with the running environment. func (c *Conn) updateSecrets() error { secrets, err := c.Environ.Provider().SecretAttrs(c.Environ.Config()) if err != nil { return err } cfg, err := c.State.EnvironConfig() if err != nil { return err } secretAttrs := make(map[string]interface{}) attrs := cfg.AllAttrs() for k, v := range secrets { if _, exists := attrs[k]; exists { // Environment already has secrets. Won't send again. return nil } else { secretAttrs[k] = v } } return c.State.UpdateEnvironConfig(secretAttrs, nil, nil) } // PutCharm uploads the given charm to provider storage, and adds a // state.Charm to the state. The charm is not uploaded if a charm with // the same URL already exists in the state. // If bumpRevision is true, the charm must be a local directory, // and the revision number will be incremented before pushing. func (conn *Conn) PutCharm(curl *charm.URL, repo charm.Repository, bumpRevision bool) (*state.Charm, error) { if curl.Revision == -1 { rev, err := charm.Latest(repo, curl) if err != nil { return nil, fmt.Errorf("cannot get latest charm revision: %v", err) } curl = curl.WithRevision(rev) } ch, err := repo.Get(curl) if err != nil { return nil, fmt.Errorf("cannot get charm: %v", err) } if bumpRevision { chd, ok := ch.(*charm.Dir) if !ok { return nil, fmt.Errorf("cannot increment revision of charm %q: not a directory", curl) } if err = chd.SetDiskRevision(chd.Revision() + 1); err != nil { return nil, fmt.Errorf("cannot increment revision of charm %q: %v", curl, err) } curl = curl.WithRevision(chd.Revision()) } if sch, err := conn.State.Charm(curl); err == nil { return sch, nil } return conn.addCharm(curl, ch) } func (conn *Conn) addCharm(curl *charm.URL, ch charm.Charm) (*state.Charm, error) { var f *os.File name := charm.Quote(curl.String()) switch ch := ch.(type) { case *charm.Dir: var err error if f, err = ioutil.TempFile("", name); err != nil { return nil, err } defer os.Remove(f.Name()) defer f.Close() err = ch.BundleTo(f) if err != nil { return nil, fmt.Errorf("cannot bundle charm: %v", err) } if _, err := f.Seek(0, 0); err != nil { return nil, err } case *charm.Bundle: var err error if f, err = os.Open(ch.Path); err != nil { return nil, fmt.Errorf("cannot read charm bundle: %v", err) } defer f.Close() default: return nil, fmt.Errorf("unknown charm type %T", ch) } digest, size, err := utils.ReadSHA256(f) if err != nil { return nil, err } if _, err := f.Seek(0, 0); err != nil { return nil, err } stor := conn.Environ.Storage() log.Infof("writing charm to storage [%d bytes]", size) if err := stor.Put(name, f, size); err != nil { return nil, fmt.Errorf("cannot put charm: %v", err) } ustr, err := stor.URL(name) if err != nil { return nil, fmt.Errorf("cannot get storage URL for charm: %v", err) } u, err := url.Parse(ustr) if err != nil { return nil, fmt.Errorf("cannot parse storage URL: %v", err) } log.Infof("adding charm to state") sch, err := conn.State.AddCharm(ch, curl, u, digest) if err != nil { return nil, fmt.Errorf("cannot add charm: %v", err) } return sch, nil } // InitJujuHome initializes the charm, environs/config and utils/ssh packages // to use default paths based on the $JUJU_HOME or $HOME environment variables. // This function should be called before calling NewConn or Conn.Deploy. func InitJujuHome() error { jujuHome := osenv.JujuHomeDir() if jujuHome == "" { return stderrors.New( "cannot determine juju home, required environment variables are not set") } osenv.SetJujuHome(jujuHome) charm.CacheDir = osenv.JujuHomePath("charmcache") if err := ssh.LoadClientKeys(osenv.JujuHomePath("ssh")); err != nil { return fmt.Errorf("cannot load ssh client keys: %v", err) } return nil } ������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/conn_test.go��������������������������������������0000644�0000153�0000161�00000054235�12321735776�024206� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package juju_test import ( "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/set" ) func Test(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type NewConnSuite struct { testbase.LoggingSuite envtesting.ToolsFixture } var _ = gc.Suite(&NewConnSuite{}) func (cs *NewConnSuite) SetUpTest(c *gc.C) { cs.LoggingSuite.SetUpTest(c) cs.ToolsFixture.SetUpTest(c) } func (cs *NewConnSuite) TearDownTest(c *gc.C) { dummy.Reset() cs.ToolsFixture.TearDownTest(c) cs.LoggingSuite.TearDownTest(c) } func assertClose(c *gc.C, closer io.Closer) { err := closer.Close() c.Assert(err, gc.IsNil) } func (*NewConnSuite) TestNewConnWithoutAdminSecret(c *gc.C) { cfg, err := config.New(config.NoDefaults, dummy.SampleConfig()) c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) c.Assert(err, gc.IsNil) attrs := env.Config().AllAttrs() delete(attrs, "admin-secret") env1, err := environs.NewFromAttrs(attrs) c.Assert(err, gc.IsNil) conn, err := juju.NewConn(env1) c.Check(conn, gc.IsNil) c.Assert(err, gc.ErrorMatches, "cannot connect without admin-secret") } func bootstrapEnv(c *gc.C, envName string, store configstore.Storage) { if store == nil { store = configstore.NewMem() } ctx := coretesting.Context(c) env, err := environs.PrepareFromName(envName, ctx, store) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) c.Assert(err, gc.IsNil) } func (*NewConnSuite) TestConnMultipleCloseOk(c *gc.C) { defer coretesting.MakeSampleHome(c).Restore() bootstrapEnv(c, "", defaultConfigStore(c)) // Error return from here is tested in TestNewConnFromNameNotSetGetsDefault. conn, err := juju.NewConnFromName("") c.Assert(err, gc.IsNil) assertClose(c, conn) assertClose(c, conn) assertClose(c, conn) } func (*NewConnSuite) TestNewConnFromNameNotSetGetsDefault(c *gc.C) { defer coretesting.MakeSampleHome(c).Restore() bootstrapEnv(c, "", defaultConfigStore(c)) conn, err := juju.NewConnFromName("") c.Assert(err, gc.IsNil) defer assertClose(c, conn) c.Assert(conn.Environ.Name(), gc.Equals, coretesting.SampleEnvName) } func (*NewConnSuite) TestNewConnFromNameNotDefault(c *gc.C) { defer coretesting.MakeMultipleEnvHome(c).Restore() // The default environment is "erewhemos", so make sure we get what we ask for. const envName = "erewhemos-2" bootstrapEnv(c, envName, defaultConfigStore(c)) conn, err := juju.NewConnFromName(envName) c.Assert(err, gc.IsNil) defer assertClose(c, conn) c.Assert(conn.Environ.Name(), gc.Equals, envName) } func (*NewConnSuite) TestConnStateSecretsSideEffect(c *gc.C) { attrs := dummy.SampleConfig().Merge(coretesting.Attrs{ "admin-secret": "side-effect secret", "secret": "pork", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) c.Assert(err, gc.IsNil) info, _, err := env.StateInfo() c.Assert(err, gc.IsNil) info.Password = utils.UserPasswordHash("side-effect secret", utils.CompatSalt) // Use a state without a nil policy, which will allow us to set an invalid config. st, err := state.Open(info, state.DefaultDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer assertClose(c, st) // Verify we have secrets in the environ config already. statecfg, err := st.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(statecfg.UnknownAttrs()["secret"], gc.Equals, "pork") // Remove the secret from state, and then make sure it gets // pushed back again. err = st.UpdateEnvironConfig(map[string]interface{}{}, []string{"secret"}, nil) c.Assert(err, gc.IsNil) // Make a new Conn, which will push the secrets. conn, err := juju.NewConn(env) c.Assert(err, gc.IsNil) defer assertClose(c, conn) statecfg, err = conn.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(statecfg.UnknownAttrs()["secret"], gc.Equals, "pork") // Reset the admin password so the state db can be reused. err = conn.State.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) } func (*NewConnSuite) TestConnStateDoesNotUpdateExistingSecrets(c *gc.C) { attrs := dummy.SampleConfig().Merge(coretesting.Attrs{ "secret": "pork", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) c.Assert(err, gc.IsNil) // Make a new Conn, which will push the secrets. conn, err := juju.NewConn(env) c.Assert(err, gc.IsNil) defer assertClose(c, conn) // Make another env with a different secret. attrs = env.Config().AllAttrs() attrs["secret"] = "squirrel" env1, err := environs.NewFromAttrs(attrs) c.Assert(err, gc.IsNil) // Connect with the new env and check that the secret has not changed conn, err = juju.NewConn(env1) c.Assert(err, gc.IsNil) defer assertClose(c, conn) cfg, err = conn.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.UnknownAttrs()["secret"], gc.Equals, "pork") // Reset the admin password so the state db can be reused. err = conn.State.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) } func (*NewConnSuite) TestConnWithPassword(c *gc.C) { attrs := dummy.SampleConfig().Merge(coretesting.Attrs{ "admin-secret": "nutkin", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) c.Assert(err, gc.IsNil) // Check that Bootstrap has correctly used a hash // of the admin password. info, _, err := env.StateInfo() c.Assert(err, gc.IsNil) info.Password = utils.UserPasswordHash("nutkin", utils.CompatSalt) st, err := state.Open(info, state.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, gc.IsNil) assertClose(c, st) // Check that we can connect with the original environment. conn, err := juju.NewConn(env) c.Assert(err, gc.IsNil) assertClose(c, conn) // Check that the password has now been changed to the original // admin password. info.Password = "nutkin" st1, err := state.Open(info, state.DefaultDialOpts(), environs.NewStatePolicy()) c.Assert(err, gc.IsNil) assertClose(c, st1) // Check that we can still connect with the original // environment. conn, err = juju.NewConn(env) c.Assert(err, gc.IsNil) defer assertClose(c, conn) // Reset the admin password so the state db can be reused. err = conn.State.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) } type ConnSuite struct { testbase.LoggingSuite coretesting.MgoSuite envtesting.ToolsFixture conn *juju.Conn repo *charm.LocalRepository } var _ = gc.Suite(&ConnSuite{}) func (s *ConnSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) cfg, err := config.New(config.NoDefaults, dummy.SampleConfig()) c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) environ, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, environ.Storage()) err = bootstrap.Bootstrap(ctx, environ, constraints.Value{}) c.Assert(err, gc.IsNil) s.conn, err = juju.NewConn(environ) c.Assert(err, gc.IsNil) s.repo = &charm.LocalRepository{Path: c.MkDir()} } func (s *ConnSuite) TearDownTest(c *gc.C) { if s.conn == nil { return } err := s.conn.State.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) err = s.conn.Environ.Destroy() c.Check(err, gc.IsNil) assertClose(c, s.conn) s.conn = nil dummy.Reset() s.ToolsFixture.TearDownTest(c) s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *ConnSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *ConnSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) s.MgoSuite.TearDownSuite(c) } func (s *ConnSuite) TestNewConnFromState(c *gc.C) { conn, err := juju.NewConnFromState(s.conn.State) c.Assert(err, gc.IsNil) c.Assert(conn.Environ.Name(), gc.Equals, dummy.SampleConfig()["name"]) } func (s *ConnSuite) TestPutCharmBasic(c *gc.C) { curl := coretesting.Charms.ClonedURL(s.repo.Path, "quantal", "riak") curl.Revision = -1 // make sure we trigger the repo.Latest logic. sch, err := s.conn.PutCharm(curl, s.repo, false) c.Assert(err, gc.IsNil) c.Assert(sch.Meta().Summary, gc.Equals, "K/V storage engine") sch, err = s.conn.State.Charm(sch.URL()) c.Assert(err, gc.IsNil) c.Assert(sch.Meta().Summary, gc.Equals, "K/V storage engine") } func (s *ConnSuite) TestPutBundledCharm(c *gc.C) { // Bundle the riak charm into a charm repo directory. dir := filepath.Join(s.repo.Path, "quantal") err := os.Mkdir(dir, 0777) c.Assert(err, gc.IsNil) w, err := os.Create(filepath.Join(dir, "riak.charm")) c.Assert(err, gc.IsNil) defer assertClose(c, w) charmDir := coretesting.Charms.Dir("riak") err = charmDir.BundleTo(w) c.Assert(err, gc.IsNil) // Invent a URL that points to the bundled charm, and // test putting that. curl := &charm.URL{ Reference: charm.Reference{ Schema: "local", Name: "riak", Revision: -1, }, Series: "quantal", } _, err = s.conn.PutCharm(curl, s.repo, true) c.Assert(err, gc.ErrorMatches, `cannot increment revision of charm "local:quantal/riak-7": not a directory`) sch, err := s.conn.PutCharm(curl, s.repo, false) c.Assert(err, gc.IsNil) c.Assert(sch.Meta().Summary, gc.Equals, "K/V storage engine") // Check that we can get the charm from the state. sch, err = s.conn.State.Charm(sch.URL()) c.Assert(err, gc.IsNil) c.Assert(sch.Meta().Summary, gc.Equals, "K/V storage engine") } func (s *ConnSuite) TestPutCharmUpload(c *gc.C) { repo := &charm.LocalRepository{Path: c.MkDir()} curl := coretesting.Charms.ClonedURL(repo.Path, "quantal", "riak") // Put charm for the first time. sch, err := s.conn.PutCharm(curl, repo, false) c.Assert(err, gc.IsNil) c.Assert(sch.Meta().Summary, gc.Equals, "K/V storage engine") sch, err = s.conn.State.Charm(sch.URL()) c.Assert(err, gc.IsNil) sha256 := sch.BundleSha256() rev := sch.Revision() // Change the charm on disk. ch, err := repo.Get(curl) c.Assert(err, gc.IsNil) chd := ch.(*charm.Dir) err = ioutil.WriteFile(filepath.Join(chd.Path, "extra"), []byte("arble"), 0666) c.Assert(err, gc.IsNil) // Put charm again and check that it has not changed in the state. sch, err = s.conn.PutCharm(curl, repo, false) c.Assert(err, gc.IsNil) sch, err = s.conn.State.Charm(sch.URL()) c.Assert(err, gc.IsNil) c.Assert(sch.BundleSha256(), gc.Equals, sha256) c.Assert(sch.Revision(), gc.Equals, rev) // Put charm again, with bumpRevision this time, and check that // it has changed. sch, err = s.conn.PutCharm(curl, repo, true) c.Assert(err, gc.IsNil) sch, err = s.conn.State.Charm(sch.URL()) c.Assert(err, gc.IsNil) c.Assert(sch.BundleSha256(), gc.Not(gc.Equals), sha256) c.Assert(sch.Revision(), gc.Equals, rev+1) } func (s *ConnSuite) assertAssignedMachineNetworks(c *gc.C, unit *state.Unit, expectInclude, expectExclude []string) { machineId, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) machine, err := s.conn.State.Machine(machineId) c.Assert(err, gc.IsNil) include, exclude, err := machine.Networks() c.Assert(err, gc.IsNil) c.Assert(include, jc.DeepEquals, expectInclude) c.Assert(exclude, jc.DeepEquals, expectExclude) } func (s *ConnSuite) TestAddUnits(c *gc.C) { withNets := []string{"net1", "net2"} withoutNets := []string{"net3", "net4"} curl := coretesting.Charms.ClonedURL(s.repo.Path, "quantal", "riak") sch, err := s.conn.PutCharm(curl, s.repo, false) c.Assert(err, gc.IsNil) svc, err := s.conn.State.AddService("testriak", "user-admin", sch, withNets, withoutNets) c.Assert(err, gc.IsNil) units, err := juju.AddUnits(s.conn.State, svc, 2, "") c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 2) s.assertAssignedMachineNetworks(c, units[0], withNets, withoutNets) s.assertAssignedMachineNetworks(c, units[1], withNets, withoutNets) id0, err := units[0].AssignedMachineId() c.Assert(err, gc.IsNil) id1, err := units[1].AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(id0, gc.Not(gc.Equals), id1) units, err = juju.AddUnits(s.conn.State, svc, 2, "0") c.Assert(err, gc.ErrorMatches, `cannot add multiple units of service "testriak" to a single machine`) units, err = juju.AddUnits(s.conn.State, svc, 1, "0") c.Assert(err, gc.IsNil) s.assertAssignedMachineNetworks(c, units[0], withNets, withoutNets) id2, err := units[0].AssignedMachineId() c.Assert(id2, gc.Equals, id0) units, err = juju.AddUnits(s.conn.State, svc, 1, "lxc:0") c.Assert(err, gc.IsNil) s.assertAssignedMachineNetworks(c, units[0], withNets, withoutNets) id3, err := units[0].AssignedMachineId() c.Assert(id3, gc.Equals, id0+"/lxc/0") units, err = juju.AddUnits(s.conn.State, svc, 1, "lxc:"+id3) c.Assert(err, gc.IsNil) s.assertAssignedMachineNetworks(c, units[0], withNets, withoutNets) id4, err := units[0].AssignedMachineId() c.Assert(id4, gc.Equals, id0+"/lxc/0/lxc/0") // Check that all but the first colon is left alone. _, err = juju.AddUnits(s.conn.State, svc, 1, "lxc:"+strings.Replace(id3, "/", ":", -1)) c.Assert(err, gc.ErrorMatches, `invalid force machine id ".*"`) } // DeployLocalSuite uses a fresh copy of the same local dummy charm for each // test, because DeployService demands that a charm already exists in state, // and that's is the simplest way to get one in there. type DeployLocalSuite struct { testing.JujuConnSuite repo charm.Repository charm *state.Charm oldCacheDir string } var _ = gc.Suite(&DeployLocalSuite{}) func (s *DeployLocalSuite) SetUpSuite(c *gc.C) { s.JujuConnSuite.SetUpSuite(c) s.repo = &charm.LocalRepository{Path: coretesting.Charms.Path()} s.oldCacheDir, charm.CacheDir = charm.CacheDir, c.MkDir() } func (s *DeployLocalSuite) TearDownSuite(c *gc.C) { charm.CacheDir = s.oldCacheDir s.JujuConnSuite.TearDownSuite(c) } func (s *DeployLocalSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) curl := charm.MustParseURL("local:quantal/dummy") charm, err := s.Conn.PutCharm(curl, s.repo, false) c.Assert(err, gc.IsNil) s.charm = charm } func (s *DeployLocalSuite) TestDeployMinimal(c *gc.C) { service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, }) c.Assert(err, gc.IsNil) s.assertCharm(c, service, s.charm.URL()) s.assertSettings(c, service, charm.Settings{}) s.assertConstraints(c, service, constraints.Value{}) s.assertMachines(c, service, constraints.Value{}) } func (s *DeployLocalSuite) TestDeploySettings(c *gc.C) { service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, ConfigSettings: charm.Settings{ "title": "banana cupcakes", "skill-level": 9901, }, }) c.Assert(err, gc.IsNil) s.assertSettings(c, service, charm.Settings{ "title": "banana cupcakes", "skill-level": int64(9901), }) } func (s *DeployLocalSuite) TestDeploySettingsError(c *gc.C) { _, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, ConfigSettings: charm.Settings{ "skill-level": 99.01, }, }) c.Assert(err, gc.ErrorMatches, `option "skill-level" expected int, got 99.01`) _, err = s.State.Service("bob") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *DeployLocalSuite) TestDeployConstraints(c *gc.C) { err := s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) c.Assert(err, gc.IsNil) serviceCons := constraints.MustParse("cpu-cores=2") service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, }) c.Assert(err, gc.IsNil) s.assertConstraints(c, service, serviceCons) } func (s *DeployLocalSuite) TestDeployNumUnits(c *gc.C) { err := s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) c.Assert(err, gc.IsNil) serviceCons := constraints.MustParse("cpu-cores=2") service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 2, }) c.Assert(err, gc.IsNil) s.assertConstraints(c, service, serviceCons) s.assertMachines(c, service, constraints.MustParse("mem=2G cpu-cores=2"), "0", "1") } func (s *DeployLocalSuite) TestDeployWithForceMachineRejectsTooManyUnits(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(machine.Id(), gc.Equals, "0") _, err = juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, NumUnits: 2, ToMachineSpec: "0", }) c.Assert(err, gc.ErrorMatches, "cannot use --num-units with --to") } func (s *DeployLocalSuite) TestDeployForceMachineId(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(machine.Id(), gc.Equals, "0") err = s.State.SetEnvironConstraints(constraints.MustParse("mem=2G")) c.Assert(err, gc.IsNil) serviceCons := constraints.MustParse("cpu-cores=2") service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 1, ToMachineSpec: "0", }) c.Assert(err, gc.IsNil) s.assertConstraints(c, service, serviceCons) s.assertMachines(c, service, constraints.Value{}, "0") } func (s *DeployLocalSuite) TestDeployForceMachineIdWithContainer(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(machine.Id(), gc.Equals, "0") cons := constraints.MustParse("mem=2G") err = s.State.SetEnvironConstraints(cons) c.Assert(err, gc.IsNil) serviceCons := constraints.MustParse("cpu-cores=2") service, err := juju.DeployService(s.State, juju.DeployServiceParams{ ServiceName: "bob", Charm: s.charm, Constraints: serviceCons, NumUnits: 1, ToMachineSpec: fmt.Sprintf("%s:0", instance.LXC), }) c.Assert(err, gc.IsNil) s.assertConstraints(c, service, serviceCons) units, err := service.AllUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 1) // The newly created container will use the constraints. id, err := units[0].AssignedMachineId() c.Assert(err, gc.IsNil) machine, err = s.State.Machine(id) c.Assert(err, gc.IsNil) expectedCons, err := machine.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, expectedCons) } func (s *DeployLocalSuite) assertCharm(c *gc.C, service *state.Service, expect *charm.URL) { curl, force := service.CharmURL() c.Assert(curl, gc.DeepEquals, expect) c.Assert(force, gc.Equals, false) } func (s *DeployLocalSuite) assertSettings(c *gc.C, service *state.Service, expect charm.Settings) { settings, err := service.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, expect) } func (s *DeployLocalSuite) assertConstraints(c *gc.C, service *state.Service, expect constraints.Value) { cons, err := service.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, expect) } func (s *DeployLocalSuite) assertMachines(c *gc.C, service *state.Service, expectCons constraints.Value, expectIds ...string) { units, err := service.AllUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, len(expectIds)) unseenIds := set.NewStrings(expectIds...) for _, unit := range units { id, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) unseenIds.Remove(id) machine, err := s.State.Machine(id) c.Assert(err, gc.IsNil) cons, err := machine.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, expectCons) } c.Assert(unseenIds, gc.DeepEquals, set.NewStrings()) } type InitJujuHomeSuite struct { testbase.LoggingSuite originalHome string originalJujuHome string } var _ = gc.Suite(&InitJujuHomeSuite{}) func (s *InitJujuHomeSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.originalHome = osenv.Home() s.originalJujuHome = os.Getenv("JUJU_HOME") } func (s *InitJujuHomeSuite) TearDownTest(c *gc.C) { osenv.SetHome(s.originalHome) os.Setenv("JUJU_HOME", s.originalJujuHome) s.LoggingSuite.TearDownTest(c) } func (s *InitJujuHomeSuite) TestJujuHome(c *gc.C) { jujuHome := c.MkDir() os.Setenv("JUJU_HOME", jujuHome) err := juju.InitJujuHome() c.Assert(err, gc.IsNil) c.Assert(osenv.JujuHome(), gc.Equals, jujuHome) } func (s *InitJujuHomeSuite) TestHome(c *gc.C) { osHome := c.MkDir() os.Setenv("JUJU_HOME", "") osenv.SetHome(osHome) err := juju.InitJujuHome() c.Assert(err, gc.IsNil) c.Assert(osenv.JujuHome(), gc.Equals, filepath.Join(osHome, ".juju")) } func (s *InitJujuHomeSuite) TestError(c *gc.C) { os.Setenv("JUJU_HOME", "") osenv.SetHome("") err := juju.InitJujuHome() c.Assert(err, gc.ErrorMatches, "cannot determine juju home.*") } func (s *InitJujuHomeSuite) TestCacheDir(c *gc.C) { jujuHome := c.MkDir() os.Setenv("JUJU_HOME", jujuHome) c.Assert(charm.CacheDir, gc.Equals, "") err := juju.InitJujuHome() c.Assert(err, gc.IsNil) c.Assert(charm.CacheDir, gc.Equals, filepath.Join(jujuHome, "charmcache")) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/��������������������������������������������0000755�0000153�0000161�00000000000�12321735643�022775� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/vars_test.go��������������������������������0000644�0000153�0000161�00000002362�12321735642�025340� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package osenv import ( "path/filepath" "runtime" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) type importSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&importSuite{}) func (s *importSuite) TestJujuHomeWin(c *gc.C) { path := `P:\FooBar\AppData` s.PatchEnvironment("APPDATA", path) c.Assert(jujuHomeWin(), gc.Equals, filepath.Join(path, "Juju")) } func (s *importSuite) TestJujuHomeLinux(c *gc.C) { path := `/foo/bar/baz/` s.PatchEnvironment("HOME", path) c.Assert(jujuHomeLinux(), gc.Equals, filepath.Join(path, ".juju")) } func (s *importSuite) TestJujuHomeEnvVar(c *gc.C) { path := "/foo/bar/baz" s.PatchEnvironment(JujuHomeEnvKey, path) c.Assert(JujuHomeDir(), gc.Equals, path) } func (s *importSuite) TestBlankJujuHomeEnvVar(c *gc.C) { s.PatchEnvironment(JujuHomeEnvKey, "") if runtime.GOOS == "windows" { s.PatchEnvironment("APPDATA", `P:\foobar`) } else { s.PatchEnvironment("HOME", "/foobar") } c.Assert(JujuHomeDir(), gc.Not(gc.Equals), "") if runtime.GOOS == "windows" { c.Assert(JujuHomeDir(), gc.Equals, jujuHomeWin()) } else { c.Assert(JujuHomeDir(), gc.Equals, jujuHomeLinux()) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/proxy.go������������������������������������0000644�0000153�0000161�00000005340�12321735642�024506� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package osenv import ( "fmt" "os" "strings" ) const ( // Remove the likelihood of errors by mistyping string values. http_proxy = "http_proxy" https_proxy = "https_proxy" ftp_proxy = "ftp_proxy" no_proxy = "no_proxy" ) // ProxySettings holds the values for the http, https and ftp proxies as well // as the no_proxy value found by Detect Proxies. type ProxySettings struct { Http string Https string Ftp string NoProxy string } func getProxySetting(key string) string { value := os.Getenv(key) if value == "" { value = os.Getenv(strings.ToUpper(key)) } return value } // DetectProxies returns the proxy settings found the environment. func DetectProxies() ProxySettings { return ProxySettings{ Http: getProxySetting(http_proxy), Https: getProxySetting(https_proxy), Ftp: getProxySetting(ftp_proxy), NoProxy: getProxySetting(no_proxy), } } // AsScriptEnvironment returns a potentially multi-line string in a format // that specifies exported key=value lines. There are two lines for each non- // empty proxy value, one lower-case and one upper-case. func (s *ProxySettings) AsScriptEnvironment() string { lines := []string{} addLine := func(proxy, value string) { if value != "" { lines = append( lines, fmt.Sprintf("export %s=%s", proxy, value), fmt.Sprintf("export %s=%s", strings.ToUpper(proxy), value)) } } addLine(http_proxy, s.Http) addLine(https_proxy, s.Https) addLine(ftp_proxy, s.Ftp) addLine(no_proxy, s.NoProxy) return strings.Join(lines, "\n") } // AsEnvironmentValues returns a slice of strings of the format "key=value" // suitable to be used in a command environment. There are two values for each // non-empty proxy value, one lower-case and one upper-case. func (s *ProxySettings) AsEnvironmentValues() []string { lines := []string{} addLine := func(proxy, value string) { if value != "" { lines = append( lines, fmt.Sprintf("%s=%s", proxy, value), fmt.Sprintf("%s=%s", strings.ToUpper(proxy), value)) } } addLine(http_proxy, s.Http) addLine(https_proxy, s.Https) addLine(ftp_proxy, s.Ftp) addLine(no_proxy, s.NoProxy) return lines } // SetEnvironmentValues updates the process environment with the // proxy values stored in the settings object. Both the lower-case // and upper-case variants are set. // // http_proxy, HTTP_PROXY // https_proxy, HTTPS_PROXY // ftp_proxy, FTP_PROXY func (s *ProxySettings) SetEnvironmentValues() { setenv := func(proxy, value string) { os.Setenv(proxy, value) os.Setenv(strings.ToUpper(proxy), value) } setenv(http_proxy, s.Http) setenv(https_proxy, s.Https) setenv(ftp_proxy, s.Ftp) setenv(no_proxy, s.NoProxy) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/proxy_test.go�������������������������������0000644�0000153�0000161�00000014236�12321735642�025551� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package osenv_test import ( "os" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/testing/testbase" ) type proxySuite struct { testbase.LoggingSuite } var _ = gc.Suite(&proxySuite{}) func (s *proxySuite) TestDetectNoSettings(c *gc.C) { // Patch all of the environment variables we check out just in case the // user has one set. s.PatchEnvironment("http_proxy", "") s.PatchEnvironment("HTTP_PROXY", "") s.PatchEnvironment("https_proxy", "") s.PatchEnvironment("HTTPS_PROXY", "") s.PatchEnvironment("ftp_proxy", "") s.PatchEnvironment("FTP_PROXY", "") s.PatchEnvironment("no_proxy", "") s.PatchEnvironment("NO_PROXY", "") proxies := osenv.DetectProxies() c.Assert(proxies, gc.DeepEquals, osenv.ProxySettings{}) } func (s *proxySuite) TestDetectPrimary(c *gc.C) { // Patch all of the environment variables we check out just in case the // user has one set. s.PatchEnvironment("http_proxy", "http://user@10.0.0.1") s.PatchEnvironment("HTTP_PROXY", "") s.PatchEnvironment("https_proxy", "https://user@10.0.0.1") s.PatchEnvironment("HTTPS_PROXY", "") s.PatchEnvironment("ftp_proxy", "ftp://user@10.0.0.1") s.PatchEnvironment("FTP_PROXY", "") s.PatchEnvironment("no_proxy", "10.0.3.1,localhost") s.PatchEnvironment("NO_PROXY", "") proxies := osenv.DetectProxies() c.Assert(proxies, gc.DeepEquals, osenv.ProxySettings{ Http: "http://user@10.0.0.1", Https: "https://user@10.0.0.1", Ftp: "ftp://user@10.0.0.1", NoProxy: "10.0.3.1,localhost", }) } func (s *proxySuite) TestDetectFallback(c *gc.C) { // Patch all of the environment variables we check out just in case the // user has one set. s.PatchEnvironment("http_proxy", "") s.PatchEnvironment("HTTP_PROXY", "http://user@10.0.0.2") s.PatchEnvironment("https_proxy", "") s.PatchEnvironment("HTTPS_PROXY", "https://user@10.0.0.2") s.PatchEnvironment("ftp_proxy", "") s.PatchEnvironment("FTP_PROXY", "ftp://user@10.0.0.2") s.PatchEnvironment("no_proxy", "") s.PatchEnvironment("NO_PROXY", "10.0.3.1,localhost") proxies := osenv.DetectProxies() c.Assert(proxies, gc.DeepEquals, osenv.ProxySettings{ Http: "http://user@10.0.0.2", Https: "https://user@10.0.0.2", Ftp: "ftp://user@10.0.0.2", NoProxy: "10.0.3.1,localhost", }) } func (s *proxySuite) TestDetectPrimaryPreference(c *gc.C) { // Patch all of the environment variables we check out just in case the // user has one set. s.PatchEnvironment("http_proxy", "http://user@10.0.0.1") s.PatchEnvironment("https_proxy", "https://user@10.0.0.1") s.PatchEnvironment("ftp_proxy", "ftp://user@10.0.0.1") s.PatchEnvironment("no_proxy", "10.0.3.1,localhost") s.PatchEnvironment("HTTP_PROXY", "http://user@10.0.0.2") s.PatchEnvironment("HTTPS_PROXY", "https://user@10.0.0.2") s.PatchEnvironment("FTP_PROXY", "ftp://user@10.0.0.2") s.PatchEnvironment("NO_PROXY", "localhost") proxies := osenv.DetectProxies() c.Assert(proxies, gc.DeepEquals, osenv.ProxySettings{ Http: "http://user@10.0.0.1", Https: "https://user@10.0.0.1", Ftp: "ftp://user@10.0.0.1", NoProxy: "10.0.3.1,localhost", }) } func (s *proxySuite) TestAsScriptEnvironmentEmpty(c *gc.C) { proxies := osenv.ProxySettings{} c.Assert(proxies.AsScriptEnvironment(), gc.Equals, "") } func (s *proxySuite) TestAsScriptEnvironmentOneValue(c *gc.C) { proxies := osenv.ProxySettings{ Http: "some-value", } expected := ` export http_proxy=some-value export HTTP_PROXY=some-value`[1:] c.Assert(proxies.AsScriptEnvironment(), gc.Equals, expected) } func (s *proxySuite) TestAsScriptEnvironmentAllValue(c *gc.C) { proxies := osenv.ProxySettings{ Http: "some-value", Https: "special", Ftp: "who uses this?", NoProxy: "10.0.3.1,localhost", } expected := ` export http_proxy=some-value export HTTP_PROXY=some-value export https_proxy=special export HTTPS_PROXY=special export ftp_proxy=who uses this? export FTP_PROXY=who uses this? export no_proxy=10.0.3.1,localhost export NO_PROXY=10.0.3.1,localhost`[1:] c.Assert(proxies.AsScriptEnvironment(), gc.Equals, expected) } func (s *proxySuite) TestAsEnvironmentValuesEmpty(c *gc.C) { proxies := osenv.ProxySettings{} c.Assert(proxies.AsEnvironmentValues(), gc.HasLen, 0) } func (s *proxySuite) TestAsEnvironmentValuesOneValue(c *gc.C) { proxies := osenv.ProxySettings{ Http: "some-value", } expected := []string{ "http_proxy=some-value", "HTTP_PROXY=some-value", } c.Assert(proxies.AsEnvironmentValues(), gc.DeepEquals, expected) } func (s *proxySuite) TestAsEnvironmentValuesAllValue(c *gc.C) { proxies := osenv.ProxySettings{ Http: "some-value", Https: "special", Ftp: "who uses this?", NoProxy: "10.0.3.1,localhost", } expected := []string{ "http_proxy=some-value", "HTTP_PROXY=some-value", "https_proxy=special", "HTTPS_PROXY=special", "ftp_proxy=who uses this?", "FTP_PROXY=who uses this?", "no_proxy=10.0.3.1,localhost", "NO_PROXY=10.0.3.1,localhost", } c.Assert(proxies.AsEnvironmentValues(), gc.DeepEquals, expected) } func (s *proxySuite) TestSetEnvironmentValues(c *gc.C) { s.PatchEnvironment("http_proxy", "initial") s.PatchEnvironment("HTTP_PROXY", "initial") s.PatchEnvironment("https_proxy", "initial") s.PatchEnvironment("HTTPS_PROXY", "initial") s.PatchEnvironment("ftp_proxy", "initial") s.PatchEnvironment("FTP_PROXY", "initial") s.PatchEnvironment("no_proxy", "initial") s.PatchEnvironment("NO_PROXY", "initial") proxy := osenv.ProxySettings{ Http: "http proxy", Https: "https proxy", // Ftp left blank to show clearing env. NoProxy: "10.0.3.1,localhost", } proxy.SetEnvironmentValues() obtained := osenv.DetectProxies() c.Assert(obtained, gc.DeepEquals, proxy) c.Assert(os.Getenv("http_proxy"), gc.Equals, "http proxy") c.Assert(os.Getenv("HTTP_PROXY"), gc.Equals, "http proxy") c.Assert(os.Getenv("https_proxy"), gc.Equals, "https proxy") c.Assert(os.Getenv("HTTPS_PROXY"), gc.Equals, "https proxy") c.Assert(os.Getenv("ftp_proxy"), gc.Equals, "") c.Assert(os.Getenv("FTP_PROXY"), gc.Equals, "") c.Assert(os.Getenv("no_proxy"), gc.Equals, "10.0.3.1,localhost") c.Assert(os.Getenv("NO_PROXY"), gc.Equals, "10.0.3.1,localhost") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/home_test.go��������������������������������0000644�0000153�0000161�00000002105�12321735642�025310� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package osenv_test import ( "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/osenv" ) type JujuHomeSuite struct { jujuHome string } var _ = gc.Suite(&JujuHomeSuite{}) func (s *JujuHomeSuite) TestStandardHome(c *gc.C) { testJujuHome := c.MkDir() defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome)) c.Assert(osenv.JujuHome(), gc.Equals, testJujuHome) } func (s *JujuHomeSuite) TestErrorHome(c *gc.C) { // Invalid juju home leads to panic when retrieving. f := func() { _ = osenv.JujuHome() } c.Assert(f, gc.PanicMatches, "juju home hasn't been initialized") f = func() { _ = osenv.JujuHomePath("environments.yaml") } c.Assert(f, gc.PanicMatches, "juju home hasn't been initialized") } func (s *JujuHomeSuite) TestHomePath(c *gc.C) { testJujuHome := c.MkDir() defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome)) envPath := osenv.JujuHomePath("environments.yaml") c.Assert(envPath, gc.Equals, filepath.Join(testJujuHome, "environments.yaml")) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/package_test.go�����������������������������0000644�0000153�0000161�00000001123�12321735642�025752� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package osenv_test import ( stdtesting "testing" "github.com/juju/testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) func Test(t *stdtesting.T) { gc.TestingT(t) } type importSuite struct { testing.CleanupSuite } var _ = gc.Suite(&importSuite{}) func (*importSuite) TestDependencies(c *gc.C) { // This test is to ensure we don't bring in dependencies at all. c.Assert(testbase.FindJujuCoreImports(c, "launchpad.net/juju-core/juju/osenv"), gc.HasLen, 0) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/vars.go�������������������������������������0000644�0000153�0000161�00000001034�12321735642�024274� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package osenv const ( JujuEnvEnvKey = "JUJU_ENV" JujuHomeEnvKey = "JUJU_HOME" JujuRepositoryEnvKey = "JUJU_REPOSITORY" JujuLoggingConfigEnvKey = "JUJU_LOGGING_CONFIG" // TODO(thumper): 2013-09-02 bug 1219630 // As much as I'd like to remove JujuContainerType now, it is still // needed as MAAS still needs it at this stage, and we can't fix // everything at once. JujuContainerTypeEnvKey = "JUJU_CONTAINER_TYPE" ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/vars_windows_test.go������������������������0000644�0000153�0000161�00000001317�12321735642�027111� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package osenv_test import ( "os" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/osenv" ) func (s *importSuite) TestHome(c *gc.C) { s.PatchEnvironment("HOMEPATH", "") s.PatchEnvironment("HOMEDRIVE", "") drive := "P:" path := `\home\foo\bar` h := drive + path osenv.SetHome(h) c.Check(os.Getenv("HOMEPATH"), gc.Equals, path) c.Check(os.Getenv("HOMEDRIVE"), gc.Equals, drive) c.Check(osenv.Home(), gc.Equals, h) // now test that if we only set the path, we don't mess with the drive path2 := `\home\someotherfoo\bar` osenv.SetHome(path2) c.Check(os.Getenv("HOMEPATH"), gc.Equals, path2) c.Check(os.Getenv("HOMEDRIVE"), gc.Equals, drive) c.Check(osenv.Home(), gc.Equals, drive+path2) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/vars_linux_test.go��������������������������0000644�0000153�0000161�00000000356�12321735642�026560� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package osenv_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/osenv" ) func (s *importSuite) TestHomeLinux(c *gc.C) { h := "/home/foo/bar" s.PatchEnvironment("HOME", h) c.Check(osenv.Home(), gc.Equals, h) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/home.go�������������������������������������0000644�0000153�0000161�00000003433�12321735642�024256� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package osenv import ( "os" "path/filepath" "runtime" "sync" ) // jujuHome stores the path to the juju configuration // folder, which is only meaningful when running the juju // CLI tool, and is typically defined by $JUJU_HOME or // $HOME/.juju as default. var ( jujuHomeMu sync.Mutex jujuHome string ) // SetJujuHome sets the value of juju home and // returns the current one. func SetJujuHome(newJujuHome string) string { jujuHomeMu.Lock() defer jujuHomeMu.Unlock() oldJujuHome := jujuHome jujuHome = newJujuHome return oldJujuHome } // JujuHome returns the current juju home. func JujuHome() string { jujuHomeMu.Lock() defer jujuHomeMu.Unlock() if jujuHome == "" { panic("juju home hasn't been initialized") } return jujuHome } // JujuHomePath returns the path to a file in the // current juju home. func JujuHomePath(names ...string) string { all := append([]string{JujuHome()}, names...) return filepath.Join(all...) } // JujuHome returns the directory where juju should store application-specific files func JujuHomeDir() string { JujuHomeDir := os.Getenv(JujuHomeEnvKey) if JujuHomeDir == "" { if runtime.GOOS == "windows" { JujuHomeDir = jujuHomeWin() } else { JujuHomeDir = jujuHomeLinux() } } return JujuHomeDir } // jujuHomeLinux returns the directory where juju should store application-specific files on Linux. func jujuHomeLinux() string { home := Home() if home == "" { return "" } return filepath.Join(home, ".juju") } // jujuHomeWin returns the directory where juju should store application-specific files on Windows. func jujuHomeWin() string { appdata := os.Getenv("APPDATA") if appdata == "" { return "" } return filepath.Join(appdata, "Juju") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/vars_nix.go���������������������������������0000644�0000153�0000161�00000000611�12321735642�025152� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // +build !windows package osenv import ( "os" ) // Home returns the os-specific home path as specified in the environment func Home() string { return os.Getenv("HOME") } // SetHome sets the os-specific home path in the environment func SetHome(s string) error { return os.Setenv("HOME", s) } �����������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/osenv/vars_windows.go�����������������������������0000644�0000153�0000161�00000001067�12321735642�026054� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package osenv import ( "os" "path" "path/filepath" ) // Home returns the os-specific home path as specified in the environment func Home() string { return path.Join(os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH")) } // SetHome sets the os-specific home path in the environment func SetHome(s string) error { v := filepath.VolumeName(s) if v != "" { if err := os.Setenv("HOMEDRIVE", v); err != nil { return err } } return os.Setenv("HOMEPATH", s[len(v):]) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/arch/���������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022544� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/arch/arch_test.go���������������������������������0000644�0000153�0000161�00000002165�12321735776�025076� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package arch_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/testing/testbase" ) type archSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&archSuite{}) func (s *archSuite) TestHostArch(c *gc.C) { a := arch.HostArch() c.Assert(arch.IsSupportedArch(a), jc.IsTrue) } func (s *archSuite) TestNormaliseArch(c *gc.C) { for _, test := range []struct { raw string arch string }{ {"windows", "windows"}, {"amd64", "amd64"}, {"x86_64", "amd64"}, {"386", "i386"}, {"i386", "i386"}, {"i486", "i386"}, {"arm", "armhf"}, {"armv", "armhf"}, {"armv7", "armhf"}, {"aarch64", "arm64"}, {"ppc64el", "ppc64"}, {"ppc64le", "ppc64"}, } { arch := arch.NormaliseArch(test.raw) c.Check(arch, gc.Equals, test.arch) } } func (s *archSuite) TestIsSupportedArch(c *gc.C) { for _, a := range arch.AllSupportedArches { c.Assert(arch.IsSupportedArch(a), jc.IsTrue) } c.Assert(arch.IsSupportedArch("invalid"), jc.IsFalse) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/arch/arch.go��������������������������������������0000644�0000153�0000161�00000003146�12321735776�024037� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package arch import ( "regexp" "runtime" "strings" ) // The following constants define the machine architectures supported by Juju. const ( AMD64 = "amd64" I386 = "i386" ARM = "armhf" ARM64 = "arm64" PPC64 = "ppc64" ) // AllSupportedArches records the machine architectures recognised by Juju. var AllSupportedArches = []string{ AMD64, I386, ARM, ARM64, PPC64, } // archREs maps regular expressions for matching // `uname -m` to architectures recognised by Juju. var archREs = []struct { *regexp.Regexp arch string }{ {regexp.MustCompile("amd64|x86_64"), AMD64}, {regexp.MustCompile("i?[3-9]86"), I386}, {regexp.MustCompile("arm|armv.*"), ARM}, {regexp.MustCompile("aarch64"), ARM64}, {regexp.MustCompile("ppc64el|ppc64le"), PPC64}, } // Override for testing. var HostArch = hostArch // HostArch returns the Juju architecture of the machine on which it is run. func hostArch() string { return NormaliseArch(runtime.GOARCH) } // NormaliseArch returns the Juju architecture corresponding to a machine's // reported architecture. The Juju architecture is used to filter simple // streams lookup of tools and images. func NormaliseArch(rawArch string) string { rawArch = strings.TrimSpace(rawArch) for _, re := range archREs { if re.Match([]byte(rawArch)) { return re.arch break } } return rawArch } // IsSupportedArch returns true if arch is one supported by Juju. func IsSupportedArch(arch string) bool { for _, a := range AllSupportedArches { if a == arch { return true } } return false } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/arch/package_test.go������������������������������0000644�0000153�0000161�00000000344�12321735642�025541� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details. package arch_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/testing/������������������������������������������0000755�0000153�0000161�00000000000�12321736000�023304� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/testing/instance.go�������������������������������0000644�0000153�0000161�00000011032�12321735776�025457� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/testing" ) // FakeStateInfo holds information about no state - it will always // give an error when connected to. The machine id gives the machine id // of the machine to be started. func FakeStateInfo(machineId string) *state.Info { return &state.Info{ Addrs: []string{"0.1.2.3:1234"}, Tag: names.MachineTag(machineId), Password: "unimportant", CACert: []byte(testing.CACert), } } // FakeAPIInfo holds information about no state - it will always // give an error when connected to. The machine id gives the machine id // of the machine to be started. func FakeAPIInfo(machineId string) *api.Info { return &api.Info{ Addrs: []string{"0.1.2.3:1234"}, Tag: names.MachineTag(machineId), Password: "unimportant", CACert: []byte(testing.CACert), } } // AssertStartInstance is a test helper function that starts an instance with a // plausible but invalid configuration, and checks that it succeeds. func AssertStartInstance( c *gc.C, env environs.Environ, machineId string, ) ( instance.Instance, *instance.HardwareCharacteristics, ) { inst, hc, err := StartInstance(env, machineId) c.Assert(err, gc.IsNil) return inst, hc } // StartInstance is a test helper function that starts an instance with a plausible // but invalid configuration, and returns the result of Environ.StartInstance. func StartInstance( env environs.Environ, machineId string, ) ( instance.Instance, *instance.HardwareCharacteristics, error, ) { return StartInstanceWithConstraints(env, machineId, constraints.Value{}) } // AssertStartInstanceWithConstraints is a test helper function that starts an instance // with the given constraints, and a plausible but invalid configuration, and returns // the result of Environ.StartInstance. func AssertStartInstanceWithConstraints( c *gc.C, env environs.Environ, machineId string, cons constraints.Value, ) ( instance.Instance, *instance.HardwareCharacteristics, ) { inst, hc, err := StartInstanceWithConstraints(env, machineId, cons) c.Assert(err, gc.IsNil) return inst, hc } // StartInstanceWithConstraints is a test helper function that starts an instance // with the given constraints, and a plausible but invalid configuration, and returns // the result of Environ.StartInstance. func StartInstanceWithConstraints( env environs.Environ, machineId string, cons constraints.Value, ) ( instance.Instance, *instance.HardwareCharacteristics, error, ) { return StartInstanceWithConstraintsAndNetworks(env, machineId, cons, environs.Networks{}) } // AssertStartInstanceWithNetworks is a test helper function that starts an // instance with the given networks, and a plausible but invalid // configuration, and returns the result of Environ.StartInstance. func AssertStartInstanceWithNetworks( c *gc.C, env environs.Environ, machineId string, cons constraints.Value, nets environs.Networks, ) ( instance.Instance, *instance.HardwareCharacteristics, ) { inst, hc, err := StartInstanceWithConstraintsAndNetworks(env, machineId, cons, nets) c.Assert(err, gc.IsNil) return inst, hc } // StartInstanceWithNetworks is a test helper function that starts an instance // with the given networks, and a plausible but invalid configuration, and // returns the result of Environ.StartInstance. func StartInstanceWithConstraintsAndNetworks( env environs.Environ, machineId string, cons constraints.Value, nets environs.Networks, ) ( instance.Instance, *instance.HardwareCharacteristics, error, ) { series := config.PreferredSeries(env.Config()) agentVersion, ok := env.Config().AgentVersion() if !ok { return nil, nil, fmt.Errorf("missing agent version in environment config") } possibleTools, err := tools.FindInstanceTools(env, agentVersion, series, cons.Arch) if err != nil { return nil, nil, err } machineNonce := "fake_nonce" stateInfo := FakeStateInfo(machineId) apiInfo := FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, machineNonce, stateInfo, apiInfo) return env.StartInstance(environs.StartInstanceParams{ Constraints: cons, Networks: nets, Tools: possibleTools, MachineConfig: machineConfig, }) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/testing/utils.go����������������������������������0000644�0000153�0000161�00000001750�12321735776�025021� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" ) // AddStateServerMachine adds a "state server" machine to the state so // that State.Addresses and State.APIAddresses will work. It returns the // added machine. The addresses that those methods will return bear no // relation to the addresses actually used by the state and API servers. // It returns the addresses that will be returned by the State.Addresses // and State.APIAddresses methods, which will not bear any relation to // the be the addresses used by the state servers. func AddStateServerMachine(c *gc.C, st *state.State) *state.Machine { machine, err := st.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) err = machine.SetAddresses([]instance.Address{ instance.NewAddress("0.1.2.3"), }) c.Assert(err, gc.IsNil) return machine } ������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/testing/conn.go�����������������������������������0000644�0000153�0000161�00000025452�12321735776�024623� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "fmt" "io/ioutil" "os" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // JujuConnSuite provides a freshly bootstrapped juju.Conn // for each test. It also includes testbase.LoggingSuite. // // It also sets up RootDir to point to a directory hierarchy // mirroring the intended juju directory structure, including // the following: // RootDir/home/ubuntu/.juju/environments.yaml // The dummy environments.yaml file, holding // a default environment named "dummyenv" // which uses the "dummy" environment type. // RootDir/var/lib/juju // An empty directory returned as DataDir - the // root of the juju data storage space. // $HOME is set to point to RootDir/home/ubuntu. type JujuConnSuite struct { // TODO: JujuConnSuite should not be concerned both with JUJU_HOME and with // /var/lib/juju: the use cases are completely non-overlapping, and any tests that // really do need both to exist ought to be embedding distinct fixtures for the // distinct environments. testbase.LoggingSuite testing.MgoSuite envtesting.ToolsFixture Conn *juju.Conn State *state.State APIConn *juju.APIConn APIState *api.State ConfigStore configstore.Storage BackingState *state.State // The State being used by the API server RootDir string // The faked-up root directory. oldHome string oldJujuHome string environ environs.Environ DummyConfig testing.Attrs } const AdminSecret = "dummy-secret" func (s *JujuConnSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *JujuConnSuite) TearDownSuite(c *gc.C) { s.MgoSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *JujuConnSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) s.setUpConn(c) } func (s *JujuConnSuite) TearDownTest(c *gc.C) { s.tearDownConn(c) s.ToolsFixture.TearDownTest(c) s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } // Reset returns environment state to that which existed at the start of // the test. func (s *JujuConnSuite) Reset(c *gc.C) { s.tearDownConn(c) s.setUpConn(c) } func (s *JujuConnSuite) StateInfo(c *gc.C) *state.Info { info, _, err := s.Conn.Environ.StateInfo() c.Assert(err, gc.IsNil) info.Password = "dummy-secret" return info } func (s *JujuConnSuite) APIInfo(c *gc.C) *api.Info { _, apiInfo, err := s.APIConn.Environ.StateInfo() c.Assert(err, gc.IsNil) apiInfo.Tag = "user-admin" apiInfo.Password = "dummy-secret" return apiInfo } // openAPIAs opens the API and ensures that the *api.State returned will be // closed during the test teardown by using a cleanup function. func (s *JujuConnSuite) openAPIAs(c *gc.C, tag, password, nonce string) *api.State { _, info, err := s.APIConn.Environ.StateInfo() c.Assert(err, gc.IsNil) info.Tag = tag info.Password = password info.Nonce = nonce apiState, err := api.Open(info, api.DialOpts{}) c.Assert(err, gc.IsNil) c.Assert(apiState, gc.NotNil) s.AddCleanup(func(c *gc.C) { err := apiState.Close() c.Check(err, gc.IsNil) }) return apiState } // OpenAPIAs opens the API using the given identity tag and password for // authentication. The returned *api.State should not be closed by the caller // as a cleanup function has been registered to do that. func (s *JujuConnSuite) OpenAPIAs(c *gc.C, tag, password string) *api.State { return s.openAPIAs(c, tag, password, "") } // OpenAPIAsMachine opens the API using the given machine tag, password and // nonce for authentication. The returned *api.State should not be closed by // the caller as a cleanup function has been registered to do that. func (s *JujuConnSuite) OpenAPIAsMachine(c *gc.C, tag, password, nonce string) *api.State { return s.openAPIAs(c, tag, password, nonce) } // OpenAPIAsNewMachine creates a new machine entry that lives in system state, // and then uses that to open the API. The returned *api.State should not be // closed by the caller as a cleanup function has been registered to do that. // The machine will run the supplied jobs; if none are given, JobHostUnits is assumed. func (s *JujuConnSuite) OpenAPIAsNewMachine(c *gc.C, jobs ...state.MachineJob) (*api.State, *state.Machine) { if len(jobs) == 0 { jobs = []state.MachineJob{state.JobHostUnits} } machine, err := s.State.AddMachine("quantal", jobs...) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = machine.SetPassword(password) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("foo", "fake_nonce", nil) c.Assert(err, gc.IsNil) return s.openAPIAs(c, machine.Tag(), password, "fake_nonce"), machine } func PreferredDefaultVersions(conf *config.Config, template version.Binary) []version.Binary { prefVersion := template prefVersion.Series = config.PreferredSeries(conf) defaultVersion := template defaultVersion.Series = testing.FakeDefaultSeries return []version.Binary{prefVersion, defaultVersion} } func (s *JujuConnSuite) setUpConn(c *gc.C) { if s.RootDir != "" { panic("JujuConnSuite.setUpConn without teardown") } s.RootDir = c.MkDir() s.oldHome = osenv.Home() home := filepath.Join(s.RootDir, "/home/ubuntu") err := os.MkdirAll(home, 0777) c.Assert(err, gc.IsNil) osenv.SetHome(home) s.oldJujuHome = osenv.SetJujuHome(filepath.Join(home, ".juju")) err = os.Mkdir(osenv.JujuHome(), 0777) c.Assert(err, gc.IsNil) err = os.MkdirAll(s.DataDir(), 0777) c.Assert(err, gc.IsNil) s.PatchEnvironment(osenv.JujuEnvEnvKey, "") // TODO(rog) remove these files and add them only when // the tests specifically need them (in cmd/juju for example) s.writeSampleConfig(c, osenv.JujuHomePath("environments.yaml")) err = ioutil.WriteFile(osenv.JujuHomePath("dummyenv-cert.pem"), []byte(testing.CACert), 0666) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(osenv.JujuHomePath("dummyenv-private-key.pem"), []byte(testing.CAKey), 0600) c.Assert(err, gc.IsNil) store, err := configstore.Default() c.Assert(err, gc.IsNil) s.ConfigStore = store ctx := testing.Context(c) environ, err := environs.PrepareFromName("dummyenv", ctx, s.ConfigStore) c.Assert(err, gc.IsNil) // sanity check we've got the correct environment. c.Assert(environ.Name(), gc.Equals, "dummyenv") s.PatchValue(&dummy.DataDir, s.DataDir()) versions := PreferredDefaultVersions(environ.Config(), version.Current) versions = append(versions, version.Current) // Upload tools for both preferred and fake default series envtesting.MustUploadFakeToolsVersions(environ.Storage(), versions...) c.Assert(bootstrap.Bootstrap(ctx, environ, constraints.Value{}), gc.IsNil) s.BackingState = environ.(GetStater).GetStateInAPIServer() conn, err := juju.NewConn(environ) c.Assert(err, gc.IsNil) s.Conn = conn s.State = conn.State apiConn, err := juju.NewAPIConn(environ, api.DialOpts{}) c.Assert(err, gc.IsNil) s.APIConn = apiConn s.APIState = apiConn.State s.environ = environ } func (s *JujuConnSuite) writeSampleConfig(c *gc.C, path string) { if s.DummyConfig == nil { s.DummyConfig = dummy.SampleConfig() } attrs := s.DummyConfig.Merge(testing.Attrs{ "admin-secret": AdminSecret, "agent-version": version.Current.Number.String(), }).Delete("name") whole := map[string]interface{}{ "environments": map[string]interface{}{ "dummyenv": attrs, }, } data, err := goyaml.Marshal(whole) c.Assert(err, gc.IsNil) s.WriteConfig(string(data)) } type GetStater interface { GetStateInAPIServer() *state.State } func (s *JujuConnSuite) tearDownConn(c *gc.C) { // Bootstrap will set the admin password, and render non-authorized use // impossible. s.State may still hold the right password, so try to reset // the password so that the MgoSuite soft-resetting works. If that fails, // it will still work, but it will take a while since it has to kill the // whole database and start over. if err := s.State.SetAdminMongoPassword(""); err != nil { c.Logf("cannot reset admin password: %v", err) } c.Assert(s.Conn.Close(), gc.IsNil) c.Assert(s.APIConn.Close(), gc.IsNil) dummy.Reset() s.Conn = nil s.State = nil osenv.SetHome(s.oldHome) osenv.SetJujuHome(s.oldJujuHome) s.oldHome = "" s.RootDir = "" } func (s *JujuConnSuite) DataDir() string { if s.RootDir == "" { panic("DataDir called out of test context") } return filepath.Join(s.RootDir, "/var/lib/juju") } // WriteConfig writes a juju config file to the "home" directory. func (s *JujuConnSuite) WriteConfig(configData string) { if s.RootDir == "" { panic("SetUpTest has not been called; will not overwrite $JUJU_HOME/environments.yaml") } path := osenv.JujuHomePath("environments.yaml") err := ioutil.WriteFile(path, []byte(configData), 0600) if err != nil { panic(err) } } func (s *JujuConnSuite) AddTestingCharm(c *gc.C, name string) *state.Charm { ch := testing.Charms.Dir(name) ident := fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision()) curl := charm.MustParseURL("local:quantal/" + ident) repo, err := charm.InferRepository(curl.Reference, testing.Charms.Path()) c.Assert(err, gc.IsNil) sch, err := s.Conn.PutCharm(curl, repo, false) c.Assert(err, gc.IsNil) return sch } func (s *JujuConnSuite) AddTestingService(c *gc.C, name string, ch *state.Charm) *state.Service { return s.AddTestingServiceWithNetworks(c, name, ch, nil, nil) } func (s *JujuConnSuite) AddTestingServiceWithNetworks(c *gc.C, name string, ch *state.Charm, includeNetworks, excludeNetworks []string) *state.Service { c.Assert(s.State, gc.NotNil) service, err := s.State.AddService(name, "user-admin", ch, includeNetworks, excludeNetworks) c.Assert(err, gc.IsNil) return service } func (s *JujuConnSuite) AgentConfigForTag(c *gc.C, tag string) agent.Config { password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) config, err := agent.NewAgentConfig( agent.AgentConfigParams{ DataDir: s.DataDir(), Tag: tag, UpgradedToVersion: version.Current.Number, Password: password, Nonce: "nonce", StateAddresses: s.StateInfo(c).Addrs, APIAddresses: s.APIInfo(c).Addrs, CACert: []byte(testing.CACert), }) c.Assert(err, gc.IsNil) return config } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/testing/repo.go�����������������������������������0000644�0000153�0000161�00000005644�12321735642�024624� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package testing import ( "net/http" "os" "path/filepath" "sort" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/state" "launchpad.net/juju-core/utils" ) // RepoSuite acts as a JujuConnSuite but also sets up // $JUJU_REPOSITORY to point to a local charm repository. type RepoSuite struct { JujuConnSuite SeriesPath string RepoPath string } func (s *RepoSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) // Change the environ's config to ensure we're using the one in state, // not the one in the local environments.yaml updateAttrs := map[string]interface{}{"default-series": "precise"} err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil) c.Assert(err, gc.IsNil) s.RepoPath = os.Getenv("JUJU_REPOSITORY") repoPath := c.MkDir() os.Setenv("JUJU_REPOSITORY", repoPath) s.SeriesPath = filepath.Join(repoPath, "precise") err = os.Mkdir(s.SeriesPath, 0777) c.Assert(err, gc.IsNil) // Create a symlink "quantal" -> "precise", because most charms // and machines are written with hard-coded "quantal" series, // hence they interact badly with a local repository that assumes // only "precise" charms are available. err = os.Symlink(s.SeriesPath, filepath.Join(repoPath, "quantal")) c.Assert(err, gc.IsNil) } func (s *RepoSuite) TearDownTest(c *gc.C) { os.Setenv("JUJU_REPOSITORY", s.RepoPath) s.JujuConnSuite.TearDownTest(c) } func (s *RepoSuite) AssertService(c *gc.C, name string, expectCurl *charm.URL, unitCount, relCount int) (*state.Service, []*state.Relation) { svc, err := s.State.Service(name) c.Assert(err, gc.IsNil) ch, _, err := svc.Charm() c.Assert(err, gc.IsNil) c.Assert(ch.URL(), gc.DeepEquals, expectCurl) s.AssertCharmUploaded(c, expectCurl) units, err := svc.AllUnits() c.Logf("Service units: %+v", units) c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, unitCount) s.AssertUnitMachines(c, units) rels, err := svc.Relations() c.Assert(err, gc.IsNil) c.Assert(rels, gc.HasLen, relCount) return svc, rels } func (s *RepoSuite) AssertCharmUploaded(c *gc.C, curl *charm.URL) { ch, err := s.State.Charm(curl) c.Assert(err, gc.IsNil) url := ch.BundleURL() resp, err := http.Get(url.String()) c.Assert(err, gc.IsNil) defer resp.Body.Close() digest, _, err := utils.ReadSHA256(resp.Body) c.Assert(err, gc.IsNil) c.Assert(ch.BundleSha256(), gc.Equals, digest) } func (s *RepoSuite) AssertUnitMachines(c *gc.C, units []*state.Unit) { expectUnitNames := []string{} for _, u := range units { expectUnitNames = append(expectUnitNames, u.Name()) } sort.Strings(expectUnitNames) machines, err := s.State.AllMachines() c.Assert(err, gc.IsNil) c.Assert(machines, gc.HasLen, len(units)) unitNames := []string{} for _, m := range machines { mUnits, err := m.Units() c.Assert(err, gc.IsNil) c.Assert(mUnits, gc.HasLen, 1) unitNames = append(unitNames, mUnits[0].Name()) } sort.Strings(unitNames) c.Assert(unitNames, gc.DeepEquals, expectUnitNames) } ��������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/juju/apiconn_test.go�����������������������������������0000644�0000153�0000161�00000041734�12321735776�024700� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package juju_test import ( "fmt" "os" "time" "github.com/juju/testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state/api" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type NewAPIConnSuite struct { testbase.LoggingSuite envtesting.ToolsFixture } var _ = gc.Suite(&NewAPIConnSuite{}) func (cs *NewAPIConnSuite) SetUpTest(c *gc.C) { cs.LoggingSuite.SetUpTest(c) cs.ToolsFixture.SetUpTest(c) } func (cs *NewAPIConnSuite) TearDownTest(c *gc.C) { dummy.Reset() cs.ToolsFixture.TearDownTest(c) cs.LoggingSuite.TearDownTest(c) } func (*NewAPIConnSuite) TestNewConn(c *gc.C) { cfg, err := config.New(config.NoDefaults, dummy.SampleConfig()) c.Assert(err, gc.IsNil) ctx := coretesting.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) c.Assert(err, gc.IsNil) cfg = env.Config() cfg, err = cfg.Apply(map[string]interface{}{ "secret": "fnord", }) c.Assert(err, gc.IsNil) err = env.SetConfig(cfg) c.Assert(err, gc.IsNil) conn, err := juju.NewAPIConn(env, api.DefaultDialOpts()) c.Assert(err, gc.IsNil) c.Assert(conn.Environ, gc.Equals, env) c.Assert(conn.State, gc.NotNil) // the secrets will not be updated, as they already exist attrs, err := conn.State.Client().EnvironmentGet() c.Assert(attrs["secret"], gc.Equals, "pork") c.Assert(conn.Close(), gc.IsNil) } type NewAPIClientSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&NewAPIClientSuite{}) func (cs *NewAPIClientSuite) TearDownTest(c *gc.C) { dummy.Reset() cs.LoggingSuite.TearDownTest(c) } func (s *NewAPIClientSuite) TestNameDefault(c *gc.C) { defer coretesting.MakeMultipleEnvHome(c).Restore() // The connection logic should not delay the config connection // at all when there is no environment info available. // Make sure of that by providing a suitably long delay // and checking that the connection happens within that // time. s.PatchValue(juju.ProviderConnectDelay, coretesting.LongWait) bootstrapEnv(c, coretesting.SampleEnvName, defaultConfigStore(c)) startTime := time.Now() apiclient, err := juju.NewAPIClientFromName("") c.Assert(err, gc.IsNil) defer apiclient.Close() c.Assert(time.Since(startTime), jc.LessThan, coretesting.LongWait) // We should get the default sample environment if we ask for "" assertEnvironmentName(c, apiclient, coretesting.SampleEnvName) } func (*NewAPIClientSuite) TestNameNotDefault(c *gc.C) { defer coretesting.MakeMultipleEnvHome(c).Restore() envName := coretesting.SampleCertName + "-2" bootstrapEnv(c, envName, defaultConfigStore(c)) apiclient, err := juju.NewAPIClientFromName(envName) c.Assert(err, gc.IsNil) defer apiclient.Close() assertEnvironmentName(c, apiclient, envName) } func (s *NewAPIClientSuite) TestWithInfoOnly(c *gc.C) { defer coretesting.MakeEmptyFakeHome(c).Restore() storeConfig := &environInfo{ creds: configstore.APICredentials{ User: "foo", Password: "foopass", }, endpoint: configstore.APIEndpoint{ Addresses: []string{"foo.invalid"}, CACert: "certificated", }, } store := newConfigStore("noconfig", storeConfig) called := 0 expectState := new(api.State) apiOpen := func(apiInfo *api.Info, opts api.DialOpts) (*api.State, error) { c.Check(apiInfo.Tag, gc.Equals, "user-foo") c.Check(string(apiInfo.CACert), gc.Equals, "certificated") c.Check(apiInfo.Password, gc.Equals, "foopass") c.Check(opts, gc.DeepEquals, api.DefaultDialOpts()) called++ return expectState, nil } // Give NewAPIFromStore a store interface that can report when the // config was written to, to ensure the cache isn't updated. s.PatchValue(juju.APIOpen, apiOpen) mockStore := &storageWithWriteNotify{store: store} st, err := juju.NewAPIFromStore("noconfig", mockStore) c.Assert(err, gc.IsNil) c.Assert(st, gc.Equals, expectState) c.Assert(called, gc.Equals, 1) c.Assert(mockStore.written, jc.IsFalse) } func (s *NewAPIClientSuite) TestWithConfigAndNoInfo(c *gc.C) { defer coretesting.MakeSampleHome(c).Restore() store := newConfigStore(coretesting.SampleEnvName, &environInfo{ bootstrapConfig: map[string]interface{}{ "type": "dummy", "name": "myenv", "state-server": true, "authorized-keys": "i-am-a-key", "default-series": config.LatestLtsSeries(), "firewall-mode": config.FwInstance, "development": false, "ssl-hostname-verification": true, "admin-secret": "adminpass", }, }) bootstrapEnv(c, coretesting.SampleEnvName, store) // Verify the cache is empty. info, err := store.ReadInfo("myenv") c.Assert(err, gc.IsNil) c.Assert(info, gc.NotNil) c.Assert(info.APIEndpoint(), jc.DeepEquals, configstore.APIEndpoint{}) c.Assert(info.APICredentials(), jc.DeepEquals, configstore.APICredentials{}) called := 0 expectState := new(api.State) apiOpen := func(apiInfo *api.Info, opts api.DialOpts) (*api.State, error) { c.Check(apiInfo.Tag, gc.Equals, "user-admin") c.Check(string(apiInfo.CACert), gc.Not(gc.Equals), "") c.Check(apiInfo.Password, gc.Equals, "adminpass") c.Check(opts, gc.DeepEquals, api.DefaultDialOpts()) called++ return expectState, nil } s.PatchValue(juju.APIOpen, apiOpen) st, err := juju.NewAPIFromStore("myenv", store) c.Assert(err, gc.IsNil) c.Assert(st, gc.Equals, expectState) c.Assert(called, gc.Equals, 1) // Make sure the cache is updated. info, err = store.ReadInfo("myenv") c.Assert(err, gc.IsNil) c.Assert(info, gc.NotNil) ep := info.APIEndpoint() c.Check(ep.Addresses, gc.HasLen, 1) c.Check(ep.Addresses[0], gc.Matches, `127\.0\.0\.1:\d+`) c.Check(ep.CACert, gc.Not(gc.Equals), "") creds := info.APICredentials() c.Check(creds.User, gc.Equals, "admin") c.Check(creds.Password, gc.Equals, "adminpass") } func (s *NewAPIClientSuite) TestWithInfoError(c *gc.C) { defer coretesting.MakeEmptyFakeHome(c).Restore() expectErr := fmt.Errorf("an error") store := newConfigStoreWithError(expectErr) s.PatchValue(juju.APIOpen, panicAPIOpen) client, err := juju.NewAPIFromStore("noconfig", store) c.Assert(err, gc.Equals, expectErr) c.Assert(client, gc.IsNil) } func panicAPIOpen(apiInfo *api.Info, opts api.DialOpts) (*api.State, error) { panic("api.Open called unexpectedly") } func (s *NewAPIClientSuite) TestWithInfoNoAddresses(c *gc.C) { defer coretesting.MakeEmptyFakeHome(c).Restore() store := newConfigStore("noconfig", &environInfo{ endpoint: configstore.APIEndpoint{ Addresses: []string{}, CACert: "certificated", }, }) s.PatchValue(juju.APIOpen, panicAPIOpen) st, err := juju.NewAPIFromStore("noconfig", store) c.Assert(err, gc.ErrorMatches, `environment "noconfig" not found`) c.Assert(st, gc.IsNil) } func (s *NewAPIClientSuite) TestWithInfoAPIOpenError(c *gc.C) { defer coretesting.MakeEmptyFakeHome(c).Restore() store := newConfigStore("noconfig", &environInfo{ endpoint: configstore.APIEndpoint{ Addresses: []string{"foo.invalid"}, }, }) expectErr := fmt.Errorf("an error") apiOpen := func(apiInfo *api.Info, opts api.DialOpts) (*api.State, error) { return nil, expectErr } s.PatchValue(juju.APIOpen, apiOpen) st, err := juju.NewAPIFromStore("noconfig", store) c.Assert(err, gc.Equals, expectErr) c.Assert(st, gc.IsNil) } func (s *NewAPIClientSuite) TestWithSlowInfoConnect(c *gc.C) { defer coretesting.MakeSampleHome(c).Restore() store := configstore.NewMem() bootstrapEnv(c, coretesting.SampleEnvName, store) setEndpointAddress(c, store, coretesting.SampleEnvName, "infoapi.invalid") infoOpenedState := new(api.State) infoEndpointOpened := make(chan struct{}) cfgOpenedState := new(api.State) // On a sample run with no delay, the logic took 45ms to run, so // we make the delay slightly more than that, so that if the // logic doesn't delay at all, the test will fail reasonably consistently. s.PatchValue(juju.ProviderConnectDelay, 50*time.Millisecond) apiOpen := func(info *api.Info, opts api.DialOpts) (*api.State, error) { if info.Addrs[0] == "infoapi.invalid" { infoEndpointOpened <- struct{}{} return infoOpenedState, nil } return cfgOpenedState, nil } s.PatchValue(juju.APIOpen, apiOpen) stateClosed, restoreAPIClose := setAPIClosed() defer restoreAPIClose.Restore() startTime := time.Now() st, err := juju.NewAPIFromStore(coretesting.SampleEnvName, store) c.Assert(err, gc.IsNil) // The connection logic should wait for some time before opening // the API from the configuration. c.Assert(time.Since(startTime), jc.GreaterThan, *juju.ProviderConnectDelay) c.Assert(st, gc.Equals, cfgOpenedState) select { case <-infoEndpointOpened: case <-time.After(coretesting.LongWait): c.Errorf("api never opened via info") } // Check that the ignored state was closed. select { case st := <-stateClosed: c.Assert(st, gc.Equals, infoOpenedState) case <-time.After(coretesting.LongWait): c.Errorf("timed out waiting for state to be closed") } } func setEndpointAddress(c *gc.C, store configstore.Storage, envName string, addr string) { // Populate the environment's info with an endpoint // with a known address. info, err := store.ReadInfo(coretesting.SampleEnvName) c.Assert(err, gc.IsNil) info.SetAPIEndpoint(configstore.APIEndpoint{ Addresses: []string{addr}, CACert: "certificated", }) err = info.Write() c.Assert(err, gc.IsNil) } func (s *NewAPIClientSuite) TestWithSlowConfigConnect(c *gc.C) { defer coretesting.MakeSampleHome(c).Restore() store := configstore.NewMem() bootstrapEnv(c, coretesting.SampleEnvName, store) setEndpointAddress(c, store, coretesting.SampleEnvName, "infoapi.invalid") infoOpenedState := new(api.State) infoEndpointOpened := make(chan struct{}) cfgOpenedState := new(api.State) cfgEndpointOpened := make(chan struct{}) s.PatchValue(juju.ProviderConnectDelay, 0*time.Second) apiOpen := func(info *api.Info, opts api.DialOpts) (*api.State, error) { if info.Addrs[0] == "infoapi.invalid" { infoEndpointOpened <- struct{}{} <-infoEndpointOpened return infoOpenedState, nil } cfgEndpointOpened <- struct{}{} <-cfgEndpointOpened return cfgOpenedState, nil } s.PatchValue(juju.APIOpen, apiOpen) stateClosed, restoreAPIClose := setAPIClosed() defer restoreAPIClose.Restore() done := make(chan struct{}) go func() { st, err := juju.NewAPIFromStore(coretesting.SampleEnvName, store) c.Check(err, gc.IsNil) c.Check(st, gc.Equals, infoOpenedState) close(done) }() // Check that we're trying to connect to both endpoints: select { case <-infoEndpointOpened: case <-time.After(coretesting.LongWait): c.Fatalf("api never opened via info") } select { case <-cfgEndpointOpened: case <-time.After(coretesting.LongWait): c.Fatalf("api never opened via config") } // Let the info endpoint open go ahead and // check that the NewAPIFromStore call returns. infoEndpointOpened <- struct{}{} select { case <-done: case <-time.After(coretesting.LongWait): c.Errorf("timed out opening API") } // Let the config endpoint open go ahead and // check that its state is closed. cfgEndpointOpened <- struct{}{} select { case st := <-stateClosed: c.Assert(st, gc.Equals, cfgOpenedState) case <-time.After(coretesting.LongWait): c.Errorf("timed out waiting for state to be closed") } } func (s *NewAPIClientSuite) TestBothError(c *gc.C) { defer coretesting.MakeSampleHome(c).Restore() store := configstore.NewMem() bootstrapEnv(c, coretesting.SampleEnvName, store) setEndpointAddress(c, store, coretesting.SampleEnvName, "infoapi.invalid") s.PatchValue(juju.ProviderConnectDelay, 0*time.Second) apiOpen := func(info *api.Info, opts api.DialOpts) (*api.State, error) { if info.Addrs[0] == "infoapi.invalid" { return nil, fmt.Errorf("info connect failed") } return nil, fmt.Errorf("config connect failed") } s.PatchValue(juju.APIOpen, apiOpen) st, err := juju.NewAPIFromStore(coretesting.SampleEnvName, store) c.Check(err, gc.ErrorMatches, "config connect failed") c.Check(st, gc.IsNil) } func defaultConfigStore(c *gc.C) configstore.Storage { store, err := configstore.Default() c.Assert(err, gc.IsNil) return store } // TODO(jam): 2013-08-27 This should move somewhere in api.* func (*NewAPIClientSuite) TestMultipleCloseOk(c *gc.C) { defer coretesting.MakeSampleHome(c).Restore() bootstrapEnv(c, "", defaultConfigStore(c)) client, _ := juju.NewAPIClientFromName("") c.Assert(client.Close(), gc.IsNil) c.Assert(client.Close(), gc.IsNil) c.Assert(client.Close(), gc.IsNil) } func (*NewAPIClientSuite) TestWithBootstrapConfigAndNoEnvironmentsFile(c *gc.C) { defer coretesting.MakeSampleHome(c).Restore() store := configstore.NewMem() bootstrapEnv(c, coretesting.SampleEnvName, store) info, err := store.ReadInfo(coretesting.SampleEnvName) c.Assert(err, gc.IsNil) c.Assert(info.BootstrapConfig(), gc.NotNil) c.Assert(info.APIEndpoint().Addresses, gc.HasLen, 0) err = os.Remove(osenv.JujuHomePath("environments.yaml")) c.Assert(err, gc.IsNil) st, err := juju.NewAPIFromStore(coretesting.SampleEnvName, store) c.Check(err, gc.IsNil) st.Close() } func (*NewAPIClientSuite) TestWithBootstrapConfigTakesPrecedence(c *gc.C) { // We want to make sure that the code is using the bootstrap // config rather than information from environments.yaml, // even when there is an entry in environments.yaml // We can do that by changing the info bootstrap config // so it has a different environment name. defer coretesting.MakeMultipleEnvHome(c).Restore() store := configstore.NewMem() bootstrapEnv(c, coretesting.SampleEnvName, store) info, err := store.ReadInfo(coretesting.SampleEnvName) c.Assert(err, gc.IsNil) envName2 := coretesting.SampleCertName + "-2" info2, err := store.CreateInfo(envName2) c.Assert(err, gc.IsNil) info2.SetBootstrapConfig(info.BootstrapConfig()) err = info2.Write() c.Assert(err, gc.IsNil) // Now we have info for envName2 which will actually // cause a connection to the originally bootstrapped // state. st, err := juju.NewAPIFromStore(envName2, store) c.Check(err, gc.IsNil) st.Close() // Sanity check that connecting to the envName2 // but with no info fails. // Currently this panics with an "environment not prepared" error. // Disable for now until an upcoming branch fixes it. // err = info2.Destroy() // c.Assert(err, gc.IsNil) // st, err = juju.NewAPIFromStore(envName2, store) // if err == nil { // st.Close() // } // c.Assert(err, gc.ErrorMatches, "fooobie") } func assertEnvironmentName(c *gc.C, client *api.Client, expectName string) { envInfo, err := client.EnvironmentInfo() c.Assert(err, gc.IsNil) c.Assert(envInfo.Name, gc.Equals, expectName) } func setAPIClosed() (<-chan *api.State, testing.Restorer) { stateClosed := make(chan *api.State) apiClose := func(st *api.State) error { stateClosed <- st return nil } return stateClosed, testing.PatchValue(juju.APIClose, apiClose) } func updateSecretsNoop(_ environs.Environ, _ *api.State) error { return nil } // newConfigStoreWithError that will return the given // error from ReadInfo. func newConfigStoreWithError(err error) configstore.Storage { return &errorConfigStorage{ Storage: configstore.NewMem(), err: err, } } type errorConfigStorage struct { configstore.Storage err error } func (store *errorConfigStorage) ReadInfo(envName string) (configstore.EnvironInfo, error) { return nil, store.err } type environInfo struct { creds configstore.APICredentials endpoint configstore.APIEndpoint bootstrapConfig map[string]interface{} } // newConfigStore returns a storage that contains information // for the environment name. func newConfigStore(envName string, info *environInfo) configstore.Storage { store := configstore.NewMem() newInfo, err := store.CreateInfo(envName) if err != nil { panic(err) } newInfo.SetAPICredentials(info.creds) newInfo.SetAPIEndpoint(info.endpoint) newInfo.SetBootstrapConfig(info.bootstrapConfig) err = newInfo.Write() if err != nil { panic(err) } return store } type storageWithWriteNotify struct { written bool store configstore.Storage } func (*storageWithWriteNotify) CreateInfo(envName string) (configstore.EnvironInfo, error) { panic("CreateInfo not implemented") } func (s *storageWithWriteNotify) ReadInfo(envName string) (configstore.EnvironInfo, error) { info, err := s.store.ReadInfo(envName) if err != nil { return nil, err } return &infoWithWriteNotify{ written: &s.written, EnvironInfo: info, }, nil } type infoWithWriteNotify struct { configstore.EnvironInfo written *bool } func (info *infoWithWriteNotify) Write() error { *info.written = true return info.EnvironInfo.Write() } ������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/�������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�021744� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/url_test.go��������������������������������������0000644�0000153�0000161�00000024677�12321735642�024167� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "encoding/json" "fmt" "strings" "labix.org/v2/mgo/bson" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" ) type URLSuite struct{} var _ = gc.Suite(&URLSuite{}) var urlTests = []struct { s, err string url *charm.URL }{ {"cs:~user/series/name", "", &charm.URL{charm.Reference{"cs", "user", "name", -1}, "series"}}, {"cs:~user/series/name-0", "", &charm.URL{charm.Reference{"cs", "user", "name", 0}, "series"}}, {"cs:series/name", "", &charm.URL{charm.Reference{"cs", "", "name", -1}, "series"}}, {"cs:series/name-42", "", &charm.URL{charm.Reference{"cs", "", "name", 42}, "series"}}, {"local:series/name-1", "", &charm.URL{charm.Reference{"local", "", "name", 1}, "series"}}, {"local:series/name", "", &charm.URL{charm.Reference{"local", "", "name", -1}, "series"}}, {"local:series/n0-0n-n0", "", &charm.URL{charm.Reference{"local", "", "n0-0n-n0", -1}, "series"}}, {"cs:~user/name", "", &charm.URL{charm.Reference{"cs", "user", "name", -1}, ""}}, {"cs:name", "", &charm.URL{charm.Reference{"cs", "", "name", -1}, ""}}, {"local:name", "", &charm.URL{charm.Reference{"local", "", "name", -1}, ""}}, {"bs:~user/series/name-1", "charm URL has invalid schema: .*", nil}, {"cs:~1/series/name-1", "charm URL has invalid user name: .*", nil}, {"cs:~user", "charm URL without charm name: .*", nil}, {"cs:~user/1/name-1", "charm URL has invalid series: .*", nil}, {"cs:~user/series/name-1-2", "charm URL has invalid charm name: .*", nil}, {"cs:~user/series/name-1-name-2", "charm URL has invalid charm name: .*", nil}, {"cs:~user/series/name--name-2", "charm URL has invalid charm name: .*", nil}, {"cs:~user/series/huh/name-1", "charm URL has invalid form: .*", nil}, {"cs:/name", "charm URL has invalid series: .*", nil}, {"local:~user/series/name", "local charm URL with user name: .*", nil}, {"local:~user/name", "local charm URL with user name: .*", nil}, } func (s *URLSuite) TestParseURL(c *gc.C) { for i, t := range urlTests { c.Logf("test %d", i) url, uerr := charm.ParseURL(t.s) ref, series, rerr := charm.ParseReference(t.s) comment := gc.Commentf("ParseURL(%q)", t.s) if t.url != nil && t.url.Series == "" { if t.err != "" { // Expected error should match c.Assert(rerr, gc.NotNil, comment) c.Check(rerr.Error(), gc.Matches, t.err, comment) } else { // Expected charm reference should match c.Check(ref, gc.DeepEquals, t.url.Reference, comment) c.Check(t.url.Reference.String(), gc.Equals, t.s) } if rerr != nil { // If ParseReference has an error, ParseURL should share it c.Check(uerr.Error(), gc.Equals, rerr.Error(), comment) } else { // Otherwise, ParseURL with an empty series should error unresolved. c.Check(uerr.Error(), gc.Equals, charm.ErrUnresolvedUrl.Error(), comment) } } else { if t.err != "" { c.Assert(uerr, gc.NotNil, comment) c.Check(uerr.Error(), gc.Matches, t.err, comment) c.Check(uerr.Error(), gc.Equals, rerr.Error(), comment) } else { c.Check(url.Series, gc.Equals, series, comment) c.Check(url, gc.DeepEquals, t.url, comment) c.Check(t.url.String(), gc.Equals, t.s) } } } } var inferTests = []struct { vague, exact string }{ {"foo", "cs:defseries/foo"}, {"foo-1", "cs:defseries/foo-1"}, {"n0-n0-n0", "cs:defseries/n0-n0-n0"}, {"cs:foo", "cs:defseries/foo"}, {"local:foo", "local:defseries/foo"}, {"series/foo", "cs:series/foo"}, {"cs:series/foo", "cs:series/foo"}, {"local:series/foo", "local:series/foo"}, {"cs:~user/foo", "cs:~user/defseries/foo"}, {"cs:~user/series/foo", "cs:~user/series/foo"}, {"local:~user/series/foo", "local:~user/series/foo"}, {"bs:foo", "bs:defseries/foo"}, {"cs:~1/foo", "cs:~1/defseries/foo"}, {"cs:foo-1-2", "cs:defseries/foo-1-2"}, } func (s *URLSuite) TestInferURL(c *gc.C) { for i, t := range inferTests { c.Logf("test %d", i) comment := gc.Commentf("InferURL(%q, %q)", t.vague, "defseries") inferred, ierr := charm.InferURL(t.vague, "defseries") parsed, perr := charm.ParseURL(t.exact) if perr == nil { c.Check(inferred, gc.DeepEquals, parsed, comment) c.Check(ierr, gc.IsNil) } else { expect := perr.Error() if t.vague != t.exact { if colIdx := strings.Index(expect, ":"); colIdx > 0 { expect = expect[:colIdx] } } c.Check(ierr.Error(), gc.Matches, expect+".*", comment) } } u, err := charm.InferURL("~blah", "defseries") c.Assert(u, gc.IsNil) c.Assert(err, gc.ErrorMatches, "charm URL without charm name: .*") } var inferNoDefaultSeriesTests = []struct { vague, exact string resolved bool }{ {"foo", "", false}, {"foo-1", "", false}, {"cs:foo", "", false}, {"cs:~user/foo", "", false}, {"series/foo", "cs:series/foo", true}, {"cs:series/foo", "cs:series/foo", true}, {"cs:~user/series/foo", "cs:~user/series/foo", true}, } func (s *URLSuite) TestInferURLNoDefaultSeries(c *gc.C) { for _, t := range inferNoDefaultSeriesTests { inferred, err := charm.InferURL(t.vague, "") if t.exact == "" { c.Assert(err, gc.ErrorMatches, fmt.Sprintf("cannot infer charm URL for %q: no series provided", t.vague)) } else { parsed, err := charm.ParseURL(t.exact) c.Assert(err, gc.IsNil) c.Assert(inferred, gc.DeepEquals, parsed, gc.Commentf(`InferURL(%q, "")`, t.vague)) } } } func (s *URLSuite) TestParseUnresolved(c *gc.C) { for _, t := range inferNoDefaultSeriesTests { if t.resolved { url, err := charm.ParseURL(t.vague) c.Assert(err, gc.IsNil) c.Assert(url.Series, gc.Not(gc.Equals), "") } else { _, series, err := charm.ParseReference(t.vague) c.Assert(err, gc.IsNil) c.Assert(series, gc.Equals, "") _, err = charm.ParseURL(t.vague) c.Assert(err, gc.NotNil) c.Assert(err, gc.Equals, charm.ErrUnresolvedUrl) } } } var validTests = []struct { valid func(string) bool string string expect bool }{ {charm.IsValidUser, "", false}, {charm.IsValidUser, "bob", true}, {charm.IsValidUser, "Bob", false}, {charm.IsValidUser, "bOB", true}, {charm.IsValidUser, "b^b", false}, {charm.IsValidUser, "bob1", true}, {charm.IsValidUser, "bob-1", true}, {charm.IsValidUser, "bob+1", true}, {charm.IsValidUser, "bob.1", true}, {charm.IsValidUser, "1bob", true}, {charm.IsValidUser, "1-bob", true}, {charm.IsValidUser, "1+bob", true}, {charm.IsValidUser, "1.bob", true}, {charm.IsValidUser, "jim.bob+99-1.", true}, {charm.IsValidName, "", false}, {charm.IsValidName, "wordpress", true}, {charm.IsValidName, "Wordpress", false}, {charm.IsValidName, "word-press", true}, {charm.IsValidName, "word press", false}, {charm.IsValidName, "word^press", false}, {charm.IsValidName, "-wordpress", false}, {charm.IsValidName, "wordpress-", false}, {charm.IsValidName, "wordpress2", true}, {charm.IsValidName, "wordpress-2", false}, {charm.IsValidName, "word2-press2", true}, {charm.IsValidSeries, "", false}, {charm.IsValidSeries, "precise", true}, {charm.IsValidSeries, "Precise", false}, {charm.IsValidSeries, "pre cise", false}, {charm.IsValidSeries, "pre-cise", false}, {charm.IsValidSeries, "pre^cise", false}, {charm.IsValidSeries, "prec1se", true}, {charm.IsValidSeries, "-precise", false}, {charm.IsValidSeries, "precise-", false}, {charm.IsValidSeries, "precise-1", false}, {charm.IsValidSeries, "precise1", true}, {charm.IsValidSeries, "pre-c1se", false}, } func (s *URLSuite) TestValidCheckers(c *gc.C) { for i, t := range validTests { c.Logf("test %d: %s", i, t.string) c.Assert(t.valid(t.string), gc.Equals, t.expect, gc.Commentf("%s", t.string)) } } func (s *URLSuite) TestMustParseURL(c *gc.C) { url := charm.MustParseURL("cs:series/name") c.Assert(url, gc.DeepEquals, &charm.URL{Reference: charm.Reference{"cs", "", "name", -1}, Series: "series"}) f := func() { charm.MustParseURL("local:@@/name") } c.Assert(f, gc.PanicMatches, "charm URL has invalid series: .*") f = func() { charm.MustParseURL("cs:~user") } c.Assert(f, gc.PanicMatches, "charm URL without charm name: .*") f = func() { charm.MustParseURL("cs:~user") } c.Assert(f, gc.PanicMatches, "charm URL without charm name: .*") f = func() { charm.MustParseURL("cs:name") } c.Assert(f, gc.PanicMatches, "charm url series is not resolved") } func (s *URLSuite) TestWithRevision(c *gc.C) { url := charm.MustParseURL("cs:series/name") other := url.WithRevision(1) c.Assert(url, gc.DeepEquals, &charm.URL{charm.Reference{"cs", "", "name", -1}, "series"}) c.Assert(other, gc.DeepEquals, &charm.URL{charm.Reference{"cs", "", "name", 1}, "series"}) // Should always copy. The opposite behavior is error prone. c.Assert(other.WithRevision(1), gc.Not(gc.Equals), other) c.Assert(other.WithRevision(1), gc.DeepEquals, other) } var codecs = []struct { Marshal func(interface{}) ([]byte, error) Unmarshal func([]byte, interface{}) error }{{ Marshal: bson.Marshal, Unmarshal: bson.Unmarshal, }, { Marshal: json.Marshal, Unmarshal: json.Unmarshal, }} func (s *URLSuite) TestURLCodecs(c *gc.C) { for i, codec := range codecs { c.Logf("codec %d", i) type doc struct { URL *charm.URL } url := charm.MustParseURL("cs:series/name") data, err := codec.Marshal(doc{url}) c.Assert(err, gc.IsNil) var v doc err = codec.Unmarshal(data, &v) c.Assert(v.URL, gc.DeepEquals, url) data, err = codec.Marshal(doc{}) c.Assert(err, gc.IsNil) err = codec.Unmarshal(data, &v) c.Assert(err, gc.IsNil) c.Assert(v.URL, gc.IsNil) } } func (s *URLSuite) TestReferenceJSON(c *gc.C) { ref, _, err := charm.ParseReference("cs:series/name") c.Assert(err, gc.IsNil) data, err := json.Marshal(&ref) c.Assert(err, gc.IsNil) c.Check(string(data), gc.Equals, `"cs:name"`) var parsed charm.Reference err = json.Unmarshal(data, &parsed) c.Assert(err, gc.IsNil) c.Check(parsed, gc.DeepEquals, ref) // unmarshalling json gibberish and invalid charm reference strings for _, value := range []string{":{", `"cs:{}+<"`, `"cs:~_~/f00^^&^/baaaar$%-?"`} { err = json.Unmarshal([]byte(value), &parsed) c.Check(err, gc.NotNil) } } type QuoteSuite struct{} var _ = gc.Suite(&QuoteSuite{}) func (s *QuoteSuite) TestUnmodified(c *gc.C) { // Check that a string containing only valid // chars stays unmodified. in := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-" out := charm.Quote(in) c.Assert(out, gc.Equals, in) } func (s *QuoteSuite) TestQuote(c *gc.C) { // Check that invalid chars are translated correctly. in := "hello_there/how'are~you-today.sir" out := charm.Quote(in) c.Assert(out, gc.Equals, "hello_5f_there_2f_how_27_are_7e_you-today.sir") } �����������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/meta.go������������������������������������������0000644�0000153�0000161�00000024023�12321735642�023235� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "errors" "fmt" "io" "io/ioutil" "strings" "launchpad.net/goyaml" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/schema" ) // RelationScope describes the scope of a relation. type RelationScope string // Note that schema doesn't support custom string types, // so when we use these values in a schema.Checker, // we must store them as strings, not RelationScopes. const ( ScopeGlobal RelationScope = "global" ScopeContainer RelationScope = "container" ) // RelationRole defines the role of a relation. type RelationRole string const ( RoleProvider RelationRole = "provider" RoleRequirer RelationRole = "requirer" RolePeer RelationRole = "peer" ) // Relation represents a single relation defined in the charm // metadata.yaml file. type Relation struct { Name string Role RelationRole Interface string Optional bool Limit int Scope RelationScope } // ImplementedBy returns whether the relation is implemented by the supplied charm. func (r Relation) ImplementedBy(ch Charm) bool { if r.IsImplicit() { return true } var m map[string]Relation switch r.Role { case RoleProvider: m = ch.Meta().Provides case RoleRequirer: m = ch.Meta().Requires case RolePeer: m = ch.Meta().Peers default: panic(fmt.Errorf("unknown relation role %q", r.Role)) } rel, found := m[r.Name] if !found { return false } if rel.Interface == r.Interface { switch r.Scope { case ScopeGlobal: return rel.Scope != ScopeContainer case ScopeContainer: return true default: panic(fmt.Errorf("unknown relation scope %q", r.Scope)) } } return false } // IsImplicit returns whether the relation is supplied by juju itself, // rather than by a charm. func (r Relation) IsImplicit() bool { return (r.Name == "juju-info" && r.Interface == "juju-info" && r.Role == RoleProvider) } // Meta represents all the known content that may be defined // within a charm's metadata.yaml file. type Meta struct { Name string Summary string Description string Subordinate bool Provides map[string]Relation `bson:",omitempty"` Requires map[string]Relation `bson:",omitempty"` Peers map[string]Relation `bson:",omitempty"` Format int `bson:",omitempty"` OldRevision int `bson:",omitempty"` // Obsolete Categories []string `bson:",omitempty"` Series string `bson:",omitempty"` } func generateRelationHooks(relName string, allHooks map[string]bool) { for _, hookName := range hooks.RelationHooks() { allHooks[fmt.Sprintf("%s-%s", relName, hookName)] = true } } // Hooks returns a map of all possible valid hooks, taking relations // into account. It's a map to enable fast lookups, and the value is // always true. func (m Meta) Hooks() map[string]bool { allHooks := make(map[string]bool) // Unit hooks for _, hookName := range hooks.UnitHooks() { allHooks[string(hookName)] = true } // Relation hooks for hookName := range m.Provides { generateRelationHooks(hookName, allHooks) } for hookName := range m.Requires { generateRelationHooks(hookName, allHooks) } for hookName := range m.Peers { generateRelationHooks(hookName, allHooks) } return allHooks } func parseCategories(categories interface{}) []string { if categories == nil { return nil } slice := categories.([]interface{}) result := make([]string, 0, len(slice)) for _, cat := range slice { result = append(result, cat.(string)) } return result } // ReadMeta reads the content of a metadata.yaml file and returns // its representation. func ReadMeta(r io.Reader) (meta *Meta, err error) { data, err := ioutil.ReadAll(r) if err != nil { return } raw := make(map[interface{}]interface{}) err = goyaml.Unmarshal(data, raw) if err != nil { return } v, err := charmSchema.Coerce(raw, nil) if err != nil { return nil, errors.New("metadata: " + err.Error()) } m := v.(map[string]interface{}) meta = &Meta{} meta.Name = m["name"].(string) // Schema decodes as int64, but the int range should be good // enough for revisions. meta.Summary = m["summary"].(string) meta.Description = m["description"].(string) meta.Provides = parseRelations(m["provides"], RoleProvider) meta.Requires = parseRelations(m["requires"], RoleRequirer) meta.Peers = parseRelations(m["peers"], RolePeer) meta.Format = int(m["format"].(int64)) meta.Categories = parseCategories(m["categories"]) if subordinate := m["subordinate"]; subordinate != nil { meta.Subordinate = subordinate.(bool) } if rev := m["revision"]; rev != nil { // Obsolete meta.OldRevision = int(m["revision"].(int64)) } if series, ok := m["series"]; ok && series != nil { meta.Series = series.(string) } if err := meta.Check(); err != nil { return nil, err } return meta, nil } // Check checks that the metadata is well-formed. func (meta Meta) Check() error { // Check for duplicate or forbidden relation names or interfaces. names := map[string]bool{} checkRelations := func(src map[string]Relation, role RelationRole) error { for name, rel := range src { if rel.Name != name { return fmt.Errorf("charm %q has mismatched relation name %q; expected %q", meta.Name, rel.Name, name) } if rel.Role != role { return fmt.Errorf("charm %q has mismatched role %q; expected %q", meta.Name, rel.Role, role) } // Container-scoped require relations on subordinates are allowed // to use the otherwise-reserved juju-* namespace. if !meta.Subordinate || role != RoleRequirer || rel.Scope != ScopeContainer { if reservedName(name) { return fmt.Errorf("charm %q using a reserved relation name: %q", meta.Name, name) } } if role != RoleRequirer { if reservedName(rel.Interface) { return fmt.Errorf("charm %q relation %q using a reserved interface: %q", meta.Name, name, rel.Interface) } } if names[name] { return fmt.Errorf("charm %q using a duplicated relation name: %q", meta.Name, name) } names[name] = true } return nil } if err := checkRelations(meta.Provides, RoleProvider); err != nil { return err } if err := checkRelations(meta.Requires, RoleRequirer); err != nil { return err } if err := checkRelations(meta.Peers, RolePeer); err != nil { return err } // Subordinate charms must have at least one relation that // has container scope, otherwise they can't relate to the // principal. if meta.Subordinate { valid := false if meta.Requires != nil { for _, relationData := range meta.Requires { if relationData.Scope == ScopeContainer { valid = true break } } } if !valid { return fmt.Errorf("subordinate charm %q lacks \"requires\" relation with container scope", meta.Name) } } if meta.Series != "" { if !IsValidSeries(meta.Series) { return fmt.Errorf("charm %q declares invalid series: %q", meta.Name, meta.Series) } } return nil } func reservedName(name string) bool { return name == "juju" || strings.HasPrefix(name, "juju-") } func parseRelations(relations interface{}, role RelationRole) map[string]Relation { if relations == nil { return nil } result := make(map[string]Relation) for name, rel := range relations.(map[string]interface{}) { relMap := rel.(map[string]interface{}) relation := Relation{ Name: name, Role: role, Interface: relMap["interface"].(string), Optional: relMap["optional"].(bool), } if scope := relMap["scope"]; scope != nil { relation.Scope = RelationScope(scope.(string)) } if relMap["limit"] != nil { // Schema defaults to int64, but we know // the int range should be more than enough. relation.Limit = int(relMap["limit"].(int64)) } result[name] = relation } return result } // Schema coercer that expands the interface shorthand notation. // A consistent format is easier to work with than considering the // potential difference everywhere. // // Supports the following variants:: // // provides: // server: riak // admin: http // foobar: // interface: blah // // provides: // server: // interface: mysql // limit: // optional: false // // In all input cases, the output is the fully specified interface // representation as seen in the mysql interface description above. func ifaceExpander(limit interface{}) schema.Checker { return ifaceExpC{limit} } type ifaceExpC struct { limit interface{} } var ( stringC = schema.String() mapC = schema.StringMap(schema.Any()) ) func (c ifaceExpC) Coerce(v interface{}, path []string) (newv interface{}, err error) { s, err := stringC.Coerce(v, path) if err == nil { newv = map[string]interface{}{ "interface": s, "limit": c.limit, "optional": false, "scope": string(ScopeGlobal), } return } v, err = mapC.Coerce(v, path) if err != nil { return } m := v.(map[string]interface{}) if _, ok := m["limit"]; !ok { m["limit"] = c.limit } return ifaceSchema.Coerce(m, path) } var ifaceSchema = schema.FieldMap( schema.Fields{ "interface": schema.String(), "limit": schema.OneOf(schema.Const(nil), schema.Int()), "scope": schema.OneOf(schema.Const(string(ScopeGlobal)), schema.Const(string(ScopeContainer))), "optional": schema.Bool(), }, schema.Defaults{ "scope": string(ScopeGlobal), "optional": false, }, ) var charmSchema = schema.FieldMap( schema.Fields{ "name": schema.String(), "summary": schema.String(), "description": schema.String(), "peers": schema.StringMap(ifaceExpander(int64(1))), "provides": schema.StringMap(ifaceExpander(nil)), "requires": schema.StringMap(ifaceExpander(int64(1))), "revision": schema.Int(), // Obsolete "format": schema.Int(), "subordinate": schema.Bool(), "categories": schema.List(schema.String()), "series": schema.String(), }, schema.Defaults{ "provides": schema.Omit, "requires": schema.Omit, "peers": schema.Omit, "revision": schema.Omit, "format": 1, "subordinate": schema.Omit, "categories": schema.Omit, "series": schema.Omit, }, ) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/export_test.go�����������������������������������0000644�0000153�0000161�00000000423�12321735642�024665� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm // Export meaningful bits for tests only. var IfaceExpander = ifaceExpander func NewStore(url string) *CharmStore { return &CharmStore{BaseURL: url} } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/config_test.go�����������������������������������0000644�0000153�0000161�00000027653�12321735642�024627� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "bytes" "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" ) type ConfigSuite struct { config *charm.Config } var _ = gc.Suite(&ConfigSuite{}) func (s *ConfigSuite) SetUpSuite(c *gc.C) { // Just use a single shared config for the whole suite. There's no use case // for mutating a config, we we assume that nobody will do so here. var err error s.config, err = charm.ReadConfig(bytes.NewBuffer([]byte(` options: title: default: My Title description: A descriptive title used for the service. type: string subtitle: default: "" description: An optional subtitle used for the service. outlook: description: No default outlook. # type defaults to string in python username: default: admin001 description: The name of the initial account (given admin permissions). type: string skill-level: description: A number indicating skill. type: int agility-ratio: description: A number from 0 to 1 indicating agility. type: float reticulate-splines: description: Whether to reticulate splines on launch, or not. type: boolean `))) c.Assert(err, gc.IsNil) } func (s *ConfigSuite) TestReadSample(c *gc.C) { c.Assert(s.config.Options, gc.DeepEquals, map[string]charm.Option{ "title": { Default: "My Title", Description: "A descriptive title used for the service.", Type: "string", }, "subtitle": { Default: "", Description: "An optional subtitle used for the service.", Type: "string", }, "username": { Default: "admin001", Description: "The name of the initial account (given admin permissions).", Type: "string", }, "outlook": { Description: "No default outlook.", Type: "string", }, "skill-level": { Description: "A number indicating skill.", Type: "int", }, "agility-ratio": { Description: "A number from 0 to 1 indicating agility.", Type: "float", }, "reticulate-splines": { Description: "Whether to reticulate splines on launch, or not.", Type: "boolean", }, }) } func (s *ConfigSuite) TestDefaultSettings(c *gc.C) { c.Assert(s.config.DefaultSettings(), gc.DeepEquals, charm.Settings{ "title": "My Title", "subtitle": "", "username": "admin001", "outlook": nil, "skill-level": nil, "agility-ratio": nil, "reticulate-splines": nil, }) } func (s *ConfigSuite) TestFilterSettings(c *gc.C) { settings := s.config.FilterSettings(charm.Settings{ "title": "something valid", "username": nil, "unknown": "whatever", "outlook": "", "skill-level": 5.5, "agility-ratio": true, "reticulate-splines": "hullo", }) c.Assert(settings, gc.DeepEquals, charm.Settings{ "title": "something valid", "username": nil, "outlook": "", }) } func (s *ConfigSuite) TestValidateSettings(c *gc.C) { for i, test := range []struct { info string input charm.Settings expect charm.Settings err string }{{ info: "nil settings are valid", expect: charm.Settings{}, }, { info: "empty settings are valid", input: charm.Settings{}, }, { info: "unknown keys are not valid", input: charm.Settings{"foo": nil}, err: `unknown option "foo"`, }, { info: "nil is valid for every value type", input: charm.Settings{ "outlook": nil, "skill-level": nil, "agility-ratio": nil, "reticulate-splines": nil, }, }, { info: "correctly-typed values are valid", input: charm.Settings{ "outlook": "stormy", "skill-level": int64(123), "agility-ratio": 0.5, "reticulate-splines": true, }, }, { info: "empty string-typed values stay empty", input: charm.Settings{"outlook": ""}, expect: charm.Settings{"outlook": ""}, }, { info: "almost-correctly-typed values are valid", input: charm.Settings{ "skill-level": 123, "agility-ratio": float32(0.5), }, expect: charm.Settings{ "skill-level": int64(123), "agility-ratio": 0.5, }, }, { info: "bad string", input: charm.Settings{"outlook": false}, err: `option "outlook" expected string, got false`, }, { info: "bad int", input: charm.Settings{"skill-level": 123.4}, err: `option "skill-level" expected int, got 123.4`, }, { info: "bad float", input: charm.Settings{"agility-ratio": "cheese"}, err: `option "agility-ratio" expected float, got "cheese"`, }, { info: "bad boolean", input: charm.Settings{"reticulate-splines": 101}, err: `option "reticulate-splines" expected boolean, got 101`, }} { c.Logf("test %d: %s", i, test.info) result, err := s.config.ValidateSettings(test.input) if test.err != "" { c.Check(err, gc.ErrorMatches, test.err) } else { c.Check(err, gc.IsNil) if test.expect == nil { c.Check(result, gc.DeepEquals, test.input) } else { c.Check(result, gc.DeepEquals, test.expect) } } } } var settingsWithNils = charm.Settings{ "outlook": nil, "skill-level": nil, "agility-ratio": nil, "reticulate-splines": nil, } var settingsWithValues = charm.Settings{ "outlook": "whatever", "skill-level": int64(123), "agility-ratio": 2.22, "reticulate-splines": true, } func (s *ConfigSuite) TestParseSettingsYAML(c *gc.C) { for i, test := range []struct { info string yaml string key string expect charm.Settings err string }{{ info: "bad structure", yaml: "`", err: `cannot parse settings data: .*`, }, { info: "bad key", yaml: "{}", key: "blah", err: `no settings found for "blah"`, }, { info: "bad settings key", yaml: "blah:\n ping: pong", key: "blah", err: `unknown option "ping"`, }, { info: "bad type for string", yaml: "blah:\n outlook: 123", key: "blah", err: `option "outlook" expected string, got 123`, }, { info: "bad type for int", yaml: "blah:\n skill-level: 12.345", key: "blah", err: `option "skill-level" expected int, got 12.345`, }, { info: "bad type for float", yaml: "blah:\n agility-ratio: blob", key: "blah", err: `option "agility-ratio" expected float, got "blob"`, }, { info: "bad type for boolean", yaml: "blah:\n reticulate-splines: 123", key: "blah", err: `option "reticulate-splines" expected boolean, got 123`, }, { info: "bad string for int", yaml: "blah:\n skill-level: cheese", key: "blah", err: `option "skill-level" expected int, got "cheese"`, }, { info: "bad string for float", yaml: "blah:\n agility-ratio: blob", key: "blah", err: `option "agility-ratio" expected float, got "blob"`, }, { info: "bad string for boolean", yaml: "blah:\n reticulate-splines: cannonball", key: "blah", err: `option "reticulate-splines" expected boolean, got "cannonball"`, }, { info: "empty dict is valid", yaml: "blah: {}", key: "blah", expect: charm.Settings{}, }, { info: "nil values are valid", yaml: `blah: outlook: null skill-level: null agility-ratio: null reticulate-splines: null`, key: "blah", expect: settingsWithNils, }, { info: "empty strings for bool options are not accepted", yaml: `blah: outlook: "" skill-level: 123 agility-ratio: 12.0 reticulate-splines: ""`, key: "blah", err: `option "reticulate-splines" expected boolean, got ""`, }, { info: "empty strings for int options are not accepted", yaml: `blah: outlook: "" skill-level: "" agility-ratio: 12.0 reticulate-splines: false`, key: "blah", err: `option "skill-level" expected int, got ""`, }, { info: "empty strings for float options are not accepted", yaml: `blah: outlook: "" skill-level: 123 agility-ratio: "" reticulate-splines: false`, key: "blah", err: `option "agility-ratio" expected float, got ""`, }, { info: "appropriate strings are valid", yaml: `blah: outlook: whatever skill-level: "123" agility-ratio: "2.22" reticulate-splines: "true"`, key: "blah", expect: settingsWithValues, }, { info: "appropriate types are valid", yaml: `blah: outlook: whatever skill-level: 123 agility-ratio: 2.22 reticulate-splines: y`, key: "blah", expect: settingsWithValues, }} { c.Logf("test %d: %s", i, test.info) result, err := s.config.ParseSettingsYAML([]byte(test.yaml), test.key) if test.err != "" { c.Check(err, gc.ErrorMatches, test.err) } else { c.Check(err, gc.IsNil) c.Check(result, gc.DeepEquals, test.expect) } } } func (s *ConfigSuite) TestParseSettingsStrings(c *gc.C) { for i, test := range []struct { info string input map[string]string expect charm.Settings err string }{{ info: "nil map is valid", expect: charm.Settings{}, }, { info: "empty map is valid", input: map[string]string{}, expect: charm.Settings{}, }, { info: "empty strings for string options are valid", input: map[string]string{"outlook": ""}, expect: charm.Settings{"outlook": ""}, }, { info: "empty strings for non-string options are invalid", input: map[string]string{"skill-level": ""}, err: `option "skill-level" expected int, got ""`, }, { info: "strings are converted", input: map[string]string{ "outlook": "whatever", "skill-level": "123", "agility-ratio": "2.22", "reticulate-splines": "true", }, expect: settingsWithValues, }, { info: "bad string for int", input: map[string]string{"skill-level": "cheese"}, err: `option "skill-level" expected int, got "cheese"`, }, { info: "bad string for float", input: map[string]string{"agility-ratio": "blob"}, err: `option "agility-ratio" expected float, got "blob"`, }, { info: "bad string for boolean", input: map[string]string{"reticulate-splines": "cannonball"}, err: `option "reticulate-splines" expected boolean, got "cannonball"`, }} { c.Logf("test %d: %s", i, test.info) result, err := s.config.ParseSettingsStrings(test.input) if test.err != "" { c.Check(err, gc.ErrorMatches, test.err) } else { c.Check(err, gc.IsNil) c.Check(result, gc.DeepEquals, test.expect) } } } func (s *ConfigSuite) TestConfigError(c *gc.C) { _, err := charm.ReadConfig(bytes.NewBuffer([]byte(`options: {t: {type: foo}}`))) c.Assert(err, gc.ErrorMatches, `invalid config: option "t" has unknown type "foo"`) } func (s *ConfigSuite) TestDefaultType(c *gc.C) { assertDefault := func(type_ string, value string, expected interface{}) { config := fmt.Sprintf(`options: {t: {type: %s, default: %s}}`, type_, value) result, err := charm.ReadConfig(bytes.NewBuffer([]byte(config))) c.Assert(err, gc.IsNil) c.Assert(result.Options["t"].Default, gc.Equals, expected) } assertDefault("boolean", "true", true) assertDefault("string", "golden grahams", "golden grahams") assertDefault("string", `""`, "") assertDefault("float", "2.2e11", 2.2e11) assertDefault("int", "99", int64(99)) assertTypeError := func(type_, str, value string) { config := fmt.Sprintf(`options: {t: {type: %s, default: %s}}`, type_, str) _, err := charm.ReadConfig(bytes.NewBuffer([]byte(config))) expected := fmt.Sprintf(`invalid config default: option "t" expected %s, got %s`, type_, value) c.Assert(err, gc.ErrorMatches, expected) } assertTypeError("boolean", "henry", `"henry"`) assertTypeError("string", "2.5", "2.5") assertTypeError("float", "123", "123") assertTypeError("int", "true", "true") } // When an empty config is supplied an error should be returned func (s *ConfigSuite) TestEmptyConfigReturnsError(c *gc.C) { config := "" result, err := charm.ReadConfig(bytes.NewBuffer([]byte(config))) c.Assert(result, gc.IsNil) c.Assert(err, gc.ErrorMatches, "invalid config: empty configuration") } �������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/config.go����������������������������������������0000644�0000153�0000161�00000015013�12321735642�023553� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "fmt" "io" "io/ioutil" "strconv" "launchpad.net/goyaml" "launchpad.net/juju-core/schema" ) // Settings is a group of charm config option names and values. A Settings // S is considered valid by the Config C if every key in S is an option in // C, and every value either has the correct type or is nil. type Settings map[string]interface{} // Option represents a single charm config option. type Option struct { Type string Description string Default interface{} } // error replaces any supplied non-nil error with a new error describing a // validation failure for the supplied value. func (option Option) error(err *error, name string, value interface{}) { if *err != nil { *err = fmt.Errorf("option %q expected %s, got %#v", name, option.Type, value) } } // validate returns an appropriately-typed value for the supplied value, or // returns an error if it cannot be converted to the correct type. Nil values // are always considered valid. func (option Option) validate(name string, value interface{}) (_ interface{}, err error) { if value == nil { return nil, nil } defer option.error(&err, name, value) if checker := optionTypeCheckers[option.Type]; checker != nil { if value, err = checker.Coerce(value, nil); err != nil { return nil, err } return value, nil } panic(fmt.Errorf("option %q has unknown type %q", name, option.Type)) } var optionTypeCheckers = map[string]schema.Checker{ "string": schema.String(), "int": schema.Int(), "float": schema.Float(), "boolean": schema.Bool(), } // parse returns an appropriately-typed value for the supplied string, or // returns an error if it cannot be parsed to the correct type. func (option Option) parse(name, str string) (_ interface{}, err error) { defer option.error(&err, name, str) switch option.Type { case "string": return str, nil case "int": return strconv.ParseInt(str, 10, 64) case "float": return strconv.ParseFloat(str, 64) case "boolean": return strconv.ParseBool(str) } panic(fmt.Errorf("option %q has unknown type %q", name, option.Type)) } // Config represents the supported configuration options for a charm, // as declared in its config.yaml file. type Config struct { Options map[string]Option } // NewConfig returns a new Config without any options. func NewConfig() *Config { return &Config{map[string]Option{}} } // ReadConfig reads a Config in YAML format. func ReadConfig(r io.Reader) (*Config, error) { data, err := ioutil.ReadAll(r) if err != nil { return nil, err } var config *Config if err := goyaml.Unmarshal(data, &config); err != nil { return nil, err } if config == nil { return nil, fmt.Errorf("invalid config: empty configuration") } for name, option := range config.Options { switch option.Type { case "string", "int", "float", "boolean": case "": // Missing type is valid in python. option.Type = "string" default: return nil, fmt.Errorf("invalid config: option %q has unknown type %q", name, option.Type) } def := option.Default if def == "" && option.Type == "string" { // Skip normal validation for compatibility with pyjuju. } else if option.Default, err = option.validate(name, def); err != nil { option.error(&err, name, def) return nil, fmt.Errorf("invalid config default: %v", err) } config.Options[name] = option } return config, nil } // option returns the named option from the config, or an error if none // such exists. func (c *Config) option(name string) (Option, error) { if option, ok := c.Options[name]; ok { return option, nil } return Option{}, fmt.Errorf("unknown option %q", name) } // DefaultSettings returns settings containing the default value of every // option in the config. Default values may be nil. func (c *Config) DefaultSettings() Settings { out := make(Settings) for name, option := range c.Options { out[name] = option.Default } return out } // ValidateSettings returns a copy of the supplied settings with a consistent type // for each value. It returns an error if the settings contain unknown keys // or invalid values. func (c *Config) ValidateSettings(settings Settings) (Settings, error) { out := make(Settings) for name, value := range settings { if option, err := c.option(name); err != nil { return nil, err } else if value, err = option.validate(name, value); err != nil { return nil, err } out[name] = value } return out, nil } // FilterSettings returns the subset of the supplied settings that are valid. func (c *Config) FilterSettings(settings Settings) Settings { out := make(Settings) for name, value := range settings { if option, err := c.option(name); err == nil { if value, err := option.validate(name, value); err == nil { out[name] = value } } } return out } // ParseSettingsStrings returns settings derived from the supplied map. Every // value in the map must be parseable to the correct type for the option // identified by its key. Empty values are interpreted as nil. func (c *Config) ParseSettingsStrings(values map[string]string) (Settings, error) { out := make(Settings) for name, str := range values { option, err := c.option(name) if err != nil { return nil, err } value, err := option.parse(name, str) if err != nil { return nil, err } out[name] = value } return out, nil } // ParseSettingsYAML returns settings derived from the supplied YAML data. The // YAML must unmarshal to a map of strings to settings data; the supplied key // must be present in the map, and must point to a map in which every value // must have, or be a string parseable to, the correct type for the associated // config option. Empty strings and nil values are both interpreted as nil. func (c *Config) ParseSettingsYAML(yamlData []byte, key string) (Settings, error) { var allSettings map[string]Settings if err := goyaml.Unmarshal(yamlData, &allSettings); err != nil { return nil, fmt.Errorf("cannot parse settings data: %v", err) } settings, ok := allSettings[key] if !ok { return nil, fmt.Errorf("no settings found for %q", key) } out := make(Settings) for name, value := range settings { option, err := c.option(name) if err != nil { return nil, err } // Accept string values for compatibility with python. if str, ok := value.(string); ok { if value, err = option.parse(name, str); err != nil { return nil, err } } else if value, err = option.validate(name, value); err != nil { return nil, err } out[name] = value } return out, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/dir.go�������������������������������������������0000644�0000153�0000161�00000013172�12321735642�023070� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "archive/zip" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "strconv" "strings" "syscall" "launchpad.net/juju-core/log" ) // The Dir type encapsulates access to data and operations // on a charm directory. type Dir struct { Path string meta *Meta config *Config revision int } // Trick to ensure *Dir implements the Charm interface. var _ Charm = (*Dir)(nil) // ReadDir returns a Dir representing an expanded charm directory. func ReadDir(path string) (dir *Dir, err error) { dir = &Dir{Path: path} file, err := os.Open(dir.join("metadata.yaml")) if err != nil { return nil, err } dir.meta, err = ReadMeta(file) file.Close() if err != nil { return nil, err } file, err = os.Open(dir.join("config.yaml")) if _, ok := err.(*os.PathError); ok { dir.config = NewConfig() } else if err != nil { return nil, err } else { dir.config, err = ReadConfig(file) file.Close() if err != nil { return nil, err } } if file, err = os.Open(dir.join("revision")); err == nil { _, err = fmt.Fscan(file, &dir.revision) file.Close() if err != nil { return nil, errors.New("invalid revision file") } } else { dir.revision = dir.meta.OldRevision } return dir, nil } // join builds a path rooted at the charm's expanded directory // path and the extra path components provided. func (dir *Dir) join(parts ...string) string { parts = append([]string{dir.Path}, parts...) return filepath.Join(parts...) } // Revision returns the revision number for the charm // expanded in dir. func (dir *Dir) Revision() int { return dir.revision } // Meta returns the Meta representing the metadata.yaml file // for the charm expanded in dir. func (dir *Dir) Meta() *Meta { return dir.meta } // Config returns the Config representing the config.yaml file // for the charm expanded in dir. func (dir *Dir) Config() *Config { return dir.config } // SetRevision changes the charm revision number. This affects // the revision reported by Revision and the revision of the // charm bundled by BundleTo. // The revision file in the charm directory is not modified. func (dir *Dir) SetRevision(revision int) { dir.revision = revision } // SetDiskRevision does the same as SetRevision but also changes // the revision file in the charm directory. func (dir *Dir) SetDiskRevision(revision int) error { dir.SetRevision(revision) file, err := os.OpenFile(dir.join("revision"), os.O_WRONLY|os.O_CREATE, 0644) if err != nil { return err } _, err = file.Write([]byte(strconv.Itoa(revision))) file.Close() return err } // BundleTo creates a charm file from the charm expanded in dir. // By convention a charm bundle should have a ".charm" suffix. func (dir *Dir) BundleTo(w io.Writer) (err error) { zipw := zip.NewWriter(w) defer zipw.Close() zp := zipPacker{zipw, dir.Path, dir.Meta().Hooks()} zp.AddRevision(dir.revision) return filepath.Walk(dir.Path, zp.WalkFunc()) } type zipPacker struct { *zip.Writer root string hooks map[string]bool } func (zp *zipPacker) WalkFunc() filepath.WalkFunc { return func(path string, fi os.FileInfo, err error) error { return zp.visit(path, fi, err) } } func (zp *zipPacker) AddRevision(revision int) error { h := &zip.FileHeader{Name: "revision"} h.SetMode(syscall.S_IFREG | 0644) w, err := zp.CreateHeader(h) if err == nil { _, err = w.Write([]byte(strconv.Itoa(revision))) } return err } func (zp *zipPacker) visit(path string, fi os.FileInfo, err error) error { if err != nil { return err } relpath, err := filepath.Rel(zp.root, path) if err != nil { return err } method := zip.Deflate hidden := len(relpath) > 1 && relpath[0] == '.' if fi.IsDir() { if relpath == "build" { return filepath.SkipDir } if hidden { return filepath.SkipDir } relpath += "/" method = zip.Store } mode := fi.Mode() if err := checkFileType(relpath, mode); err != nil { return err } if mode&os.ModeSymlink != 0 { method = zip.Store } if hidden || relpath == "revision" { return nil } h := &zip.FileHeader{ Name: relpath, Method: method, } perm := os.FileMode(0644) if mode&os.ModeSymlink != 0 { perm = 0777 } else if mode&0100 != 0 { perm = 0755 } if filepath.Dir(relpath) == "hooks" { hookName := filepath.Base(relpath) if _, ok := zp.hooks[hookName]; !fi.IsDir() && ok && mode&0100 == 0 { log.Warningf("charm: making %q executable in charm", path) perm = perm | 0100 } } h.SetMode(mode&^0777 | perm) w, err := zp.CreateHeader(h) if err != nil || fi.IsDir() { return err } var data []byte if mode&os.ModeSymlink != 0 { target, err := os.Readlink(path) if err != nil { return err } if err := checkSymlinkTarget(zp.root, relpath, target); err != nil { return err } data = []byte(target) } else { data, err = ioutil.ReadFile(path) if err != nil { return err } } _, err = w.Write(data) return err } func checkSymlinkTarget(basedir, symlink, target string) error { if filepath.IsAbs(target) { return fmt.Errorf("symlink %q is absolute: %q", symlink, target) } p := filepath.Join(filepath.Dir(symlink), target) if p == ".." || strings.HasPrefix(p, "../") { return fmt.Errorf("symlink %q links out of charm: %q", symlink, target) } return nil } func checkFileType(path string, mode os.FileMode) error { e := "file has an unknown type: %q" switch mode & os.ModeType { case os.ModeDir, os.ModeSymlink, 0: return nil case os.ModeNamedPipe: e = "file is a named pipe: %q" case os.ModeSocket: e = "file is a socket: %q" case os.ModeDevice: e = "file is a device: %q" } return fmt.Errorf(e, path) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/url.go�������������������������������������������0000644�0000153�0000161�00000020176�12321735642�023116� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "encoding/json" "fmt" "regexp" "strconv" "strings" "labix.org/v2/mgo/bson" ) // Location represents a charm location, which must declare a path component // and a string representaion. type Location interface { Path() string String() string } // Reference represents a charm location with an unresolved, untargeted series, // such as: // // cs:~joe/wordpress // cs:wordpress-42 type Reference struct { Schema string // "cs" or "local" User string // "joe" Name string // "wordpress" Revision int // -1 if unset, N otherwise } // URL represents a fully resolved charm location with a specific series, such // as: // // cs:~joe/oneiric/wordpress // cs:oneiric/wordpress-42 // local:oneiric/wordpress // type URL struct { Reference Series string // "oneiric" } var ErrUnresolvedUrl error = fmt.Errorf("charm url series is not resolved") var ( validUser = regexp.MustCompile("^[a-z0-9][a-zA-Z0-9+.-]+$") validSeries = regexp.MustCompile("^[a-z]+([a-z0-9]+)?$") validName = regexp.MustCompile("^[a-z][a-z0-9]*(-[a-z0-9]*[a-z][a-z0-9]*)*$") ) // IsValidUser returns whether user is a valid username in charm URLs. func IsValidUser(user string) bool { return validUser.MatchString(user) } // IsValidSeries returns whether series is a valid series in charm URLs. func IsValidSeries(series string) bool { return validSeries.MatchString(series) } // IsValidName returns whether name is a valid charm name. func IsValidName(name string) bool { return validName.MatchString(name) } // WithRevision returns a URL equivalent to url but with Revision set // to revision. func (url *URL) WithRevision(revision int) *URL { urlCopy := *url urlCopy.Revision = revision return &urlCopy } // MustParseURL works like ParseURL, but panics in case of errors. func MustParseURL(url string) *URL { u, err := ParseURL(url) if err != nil { panic(err) } return u } // ParseURL parses the provided charm URL string into its respective // structure. func ParseURL(url string) (*URL, error) { r, series, err := ParseReference(url) if err != nil { return nil, err } if series == "" { return nil, ErrUnresolvedUrl } return &URL{Reference: r, Series: series}, nil } // ParseReference parses the provided charm Reference string into its // respective structure and the targeted series, if present. func ParseReference(url string) (Reference, string, error) { r := Reference{Schema: "cs"} series := "" i := strings.Index(url, ":") if i >= 0 { r.Schema = url[:i] i++ } else { i = 0 } // cs: or local: if r.Schema != "cs" && r.Schema != "local" { return Reference{}, "", fmt.Errorf("charm URL has invalid schema: %q", url) } parts := strings.Split(url[i:], "/") if len(parts) < 1 || len(parts) > 3 { return Reference{}, "", fmt.Errorf("charm URL has invalid form: %q", url) } // ~<username> if strings.HasPrefix(parts[0], "~") { if r.Schema == "local" { return Reference{}, "", fmt.Errorf("local charm URL with user name: %q", url) } r.User = parts[0][1:] if !IsValidUser(r.User) { return Reference{}, "", fmt.Errorf("charm URL has invalid user name: %q", url) } parts = parts[1:] } // <series> if len(parts) == 2 { series = parts[0] if !IsValidSeries(series) { return Reference{}, "", fmt.Errorf("charm URL has invalid series: %q", url) } parts = parts[1:] } if len(parts) < 1 { return Reference{}, "", fmt.Errorf("charm URL without charm name: %q", url) } // <name>[-<revision>] r.Name = parts[0] r.Revision = -1 for i := len(r.Name) - 1; i > 0; i-- { c := r.Name[i] if c >= '0' && c <= '9' { continue } if c == '-' && i != len(r.Name)-1 { var err error r.Revision, err = strconv.Atoi(r.Name[i+1:]) if err != nil { panic(err) // We just checked it was right. } r.Name = r.Name[:i] } break } if !IsValidName(r.Name) { return Reference{}, "", fmt.Errorf("charm URL has invalid charm name: %q", url) } return r, series, nil } // InferURL returns a charm URL inferred from src. The provided // src may be a valid URL, in which case it is returned as-is, // or it may be an alias in one of the following formats: // // name // name-revision // series/name // series/name-revision // schema:name // schema:name-revision // cs:~user/name // cs:~user/name-revision // // The defaultSeries paramater is used to define the resulting URL // when src does not include that information; similarly, a missing // schema is assumed to be 'cs'. func InferURL(src, defaultSeries string) (*URL, error) { r, series, err := ParseReference(src) if err != nil { return nil, err } if series != "" { return &URL{Reference: r, Series: series}, nil } if strings.HasPrefix(src, "~") { return nil, fmt.Errorf("cannot infer charm URL with user but no schema: %q", src) } orig := src schema := "cs" if i := strings.Index(src, ":"); i != -1 { schema, src = src[:i], src[i+1:] } var full string switch parts := strings.Split(src, "/"); len(parts) { case 1: if defaultSeries == "" { return nil, fmt.Errorf("cannot infer charm URL for %q: no series provided", orig) } full = fmt.Sprintf("%s:%s/%s", schema, defaultSeries, src) case 2: if strings.HasPrefix(parts[0], "~") { if defaultSeries == "" { return nil, fmt.Errorf("cannot infer charm URL for %q: no series provided", orig) } full = fmt.Sprintf("%s:%s/%s/%s", schema, parts[0], defaultSeries, parts[1]) } else { full = fmt.Sprintf("%s:%s", schema, src) } default: full = fmt.Sprintf("%s:%s", schema, src) } u, err := ParseURL(full) if err != nil && orig != full { err = fmt.Errorf("%s (URL inferred from %q)", err, orig) } return u, err } func (u *URL) Path() string { return u.path(u.Series) } func (r Reference) path(series string) string { var parts []string if r.User != "" { parts = append(parts, fmt.Sprintf("~%s", r.User)) } if series != "" { parts = append(parts, series) } if r.Revision >= 0 { parts = append(parts, fmt.Sprintf("%s-%d", r.Name, r.Revision)) } else { parts = append(parts, r.Name) } return strings.Join(parts, "/") } func (r Reference) Path() string { return r.path("") } func (u *URL) String() string { return fmt.Sprintf("%s:%s", u.Schema, u.Path()) } func (r Reference) String() string { return fmt.Sprintf("%s:%s", r.Schema, r.Path()) } // GetBSON turns u into a bson.Getter so it can be saved directly // on a MongoDB database with mgo. func (u *URL) GetBSON() (interface{}, error) { if u == nil { return nil, nil } return u.String(), nil } // SetBSON turns u into a bson.Setter so it can be loaded directly // from a MongoDB database with mgo. func (u *URL) SetBSON(raw bson.Raw) error { if raw.Kind == 10 { return bson.SetZero } var s string err := raw.Unmarshal(&s) if err != nil { return err } url, err := ParseURL(s) if err != nil { return err } *u = *url return nil } func (u *URL) MarshalJSON() ([]byte, error) { if u == nil { panic("cannot marshal nil *charm.URL") } return json.Marshal(u.String()) } func (u *URL) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { return err } url, err := ParseURL(s) if err != nil { return err } *u = *url return nil } func (r *Reference) MarshalJSON() ([]byte, error) { return json.Marshal(r.String()) } func (r *Reference) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { return err } ref, _, err := ParseReference(s) if err != nil { return err } *r = ref return nil } // Quote translates a charm url string into one which can be safely used // in a file path. ASCII letters, ASCII digits, dot and dash stay the // same; other characters are translated to their hex representation // surrounded by underscores. func Quote(unsafe string) string { safe := make([]byte, 0, len(unsafe)*4) for i := 0; i < len(unsafe); i++ { b := unsafe[i] switch { case b >= 'a' && b <= 'z', b >= 'A' && b <= 'Z', b >= '0' && b <= '9', b == '.', b == '-': safe = append(safe, b) default: safe = append(safe, fmt.Sprintf("_%02x_", b)...) } } return string(safe) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/hooks/�������������������������������������������0000755�0000153�0000161�00000000000�12321735642�023102� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/hooks/hooks.go�����������������������������������0000644�0000153�0000161�00000003645�12321735642�024564� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // hooks provides types and constants that define the hooks known to Juju. package hooks // Kind enumerates the different kinds of hooks that exist. type Kind string const ( // None of these hooks are ever associated with a relation; each of them // represents a change to the state of the unit as a whole. The values // themselves are all valid hook names. Install Kind = "install" Start Kind = "start" ConfigChanged Kind = "config-changed" UpgradeCharm Kind = "upgrade-charm" Stop Kind = "stop" // These hooks require an associated relation, and the name of the relation // unit whose change triggered the hook. The hook file names that these // kinds represent will be prefixed by the relation name; for example, // "db-relation-joined". RelationJoined Kind = "relation-joined" RelationChanged Kind = "relation-changed" RelationDeparted Kind = "relation-departed" // This hook requires an associated relation. The represented hook file name // will be prefixed by the relation name, just like the other Relation* Kind // values. RelationBroken Kind = "relation-broken" ) var unitHooks = []Kind{ Install, Start, ConfigChanged, UpgradeCharm, Stop, } // UnitHooks returns all known unit hook kinds. func UnitHooks() []Kind { hooks := make([]Kind, len(unitHooks)) copy(hooks, unitHooks) return hooks } var relationHooks = []Kind{ RelationJoined, RelationChanged, RelationDeparted, RelationBroken, } // RelationHooks returns all known relation hook kinds. func RelationHooks() []Kind { hooks := make([]Kind, len(relationHooks)) copy(hooks, relationHooks) return hooks } // IsRelation returns whether the Kind represents a relation hook. func (kind Kind) IsRelation() bool { switch kind { case RelationJoined, RelationChanged, RelationDeparted, RelationBroken: return true } return false } �������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/meta_test.go�������������������������������������0000644�0000153�0000161�00000041203�12321735642�024273� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "bytes" "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/testing" ) func repoMeta(name string) io.Reader { charmDir := testing.Charms.DirPath(name) file, err := os.Open(filepath.Join(charmDir, "metadata.yaml")) if err != nil { panic(err) } defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { panic(err) } return bytes.NewBuffer(data) } type MetaSuite struct{} var _ = gc.Suite(&MetaSuite{}) func (s *MetaSuite) TestReadMetaVersion1(c *gc.C) { meta, err := charm.ReadMeta(repoMeta("dummy")) c.Assert(err, gc.IsNil) c.Assert(meta.Name, gc.Equals, "dummy") c.Assert(meta.Summary, gc.Equals, "That's a dummy charm.") c.Assert(meta.Description, gc.Equals, "This is a longer description which\npotentially contains multiple lines.\n") c.Assert(meta.Format, gc.Equals, 1) c.Assert(meta.OldRevision, gc.Equals, 0) c.Assert(meta.Subordinate, gc.Equals, false) } func (s *MetaSuite) TestReadMetaVersion2(c *gc.C) { meta, err := charm.ReadMeta(repoMeta("format2")) c.Assert(err, gc.IsNil) c.Assert(meta.Name, gc.Equals, "format2") c.Assert(meta.Format, gc.Equals, 2) c.Assert(meta.Categories, gc.HasLen, 0) } func (s *MetaSuite) TestReadCategory(c *gc.C) { meta, err := charm.ReadMeta(repoMeta("category")) c.Assert(err, gc.IsNil) c.Assert(meta.Categories, gc.DeepEquals, []string{"database"}) } func (s *MetaSuite) TestSubordinate(c *gc.C) { meta, err := charm.ReadMeta(repoMeta("logging")) c.Assert(err, gc.IsNil) c.Assert(meta.Subordinate, gc.Equals, true) } func (s *MetaSuite) TestSubordinateWithoutContainerRelation(c *gc.C) { r := repoMeta("dummy") hackYaml := ReadYaml(r) hackYaml["subordinate"] = true _, err := charm.ReadMeta(hackYaml.Reader()) c.Assert(err, gc.ErrorMatches, "subordinate charm \"dummy\" lacks \"requires\" relation with container scope") } func (s *MetaSuite) TestScopeConstraint(c *gc.C) { meta, err := charm.ReadMeta(repoMeta("logging")) c.Assert(err, gc.IsNil) c.Assert(meta.Provides["logging-client"].Scope, gc.Equals, charm.ScopeGlobal) c.Assert(meta.Requires["logging-directory"].Scope, gc.Equals, charm.ScopeContainer) c.Assert(meta.Subordinate, gc.Equals, true) } func (s *MetaSuite) TestParseMetaRelations(c *gc.C) { meta, err := charm.ReadMeta(repoMeta("mysql")) c.Assert(err, gc.IsNil) c.Assert(meta.Provides["server"], gc.Equals, charm.Relation{ Name: "server", Role: charm.RoleProvider, Interface: "mysql", Scope: charm.ScopeGlobal, }) c.Assert(meta.Requires, gc.IsNil) c.Assert(meta.Peers, gc.IsNil) meta, err = charm.ReadMeta(repoMeta("riak")) c.Assert(err, gc.IsNil) c.Assert(meta.Provides["endpoint"], gc.Equals, charm.Relation{ Name: "endpoint", Role: charm.RoleProvider, Interface: "http", Scope: charm.ScopeGlobal, }) c.Assert(meta.Provides["admin"], gc.Equals, charm.Relation{ Name: "admin", Role: charm.RoleProvider, Interface: "http", Scope: charm.ScopeGlobal, }) c.Assert(meta.Peers["ring"], gc.Equals, charm.Relation{ Name: "ring", Role: charm.RolePeer, Interface: "riak", Limit: 1, Scope: charm.ScopeGlobal, }) c.Assert(meta.Requires, gc.IsNil) meta, err = charm.ReadMeta(repoMeta("terracotta")) c.Assert(err, gc.IsNil) c.Assert(meta.Provides["dso"], gc.Equals, charm.Relation{ Name: "dso", Role: charm.RoleProvider, Interface: "terracotta", Optional: true, Scope: charm.ScopeGlobal, }) c.Assert(meta.Peers["server-array"], gc.Equals, charm.Relation{ Name: "server-array", Role: charm.RolePeer, Interface: "terracotta-server", Limit: 1, Scope: charm.ScopeGlobal, }) c.Assert(meta.Requires, gc.IsNil) meta, err = charm.ReadMeta(repoMeta("wordpress")) c.Assert(err, gc.IsNil) c.Assert(meta.Provides["url"], gc.Equals, charm.Relation{ Name: "url", Role: charm.RoleProvider, Interface: "http", Scope: charm.ScopeGlobal, }) c.Assert(meta.Requires["db"], gc.Equals, charm.Relation{ Name: "db", Role: charm.RoleRequirer, Interface: "mysql", Limit: 1, Scope: charm.ScopeGlobal, }) c.Assert(meta.Requires["cache"], gc.Equals, charm.Relation{ Name: "cache", Role: charm.RoleRequirer, Interface: "varnish", Limit: 2, Optional: true, Scope: charm.ScopeGlobal, }) c.Assert(meta.Peers, gc.IsNil) } var relationsConstraintsTests = []struct { rels string err string }{ { "provides:\n foo: ping\nrequires:\n foo: pong", `charm "a" using a duplicated relation name: "foo"`, }, { "requires:\n foo: ping\npeers:\n foo: pong", `charm "a" using a duplicated relation name: "foo"`, }, { "peers:\n foo: ping\nprovides:\n foo: pong", `charm "a" using a duplicated relation name: "foo"`, }, { "provides:\n juju: blob", `charm "a" using a reserved relation name: "juju"`, }, { "requires:\n juju: blob", `charm "a" using a reserved relation name: "juju"`, }, { "peers:\n juju: blob", `charm "a" using a reserved relation name: "juju"`, }, { "provides:\n juju-snap: blub", `charm "a" using a reserved relation name: "juju-snap"`, }, { "requires:\n juju-crackle: blub", `charm "a" using a reserved relation name: "juju-crackle"`, }, { "peers:\n juju-pop: blub", `charm "a" using a reserved relation name: "juju-pop"`, }, { "provides:\n innocuous: juju", `charm "a" relation "innocuous" using a reserved interface: "juju"`, }, { "peers:\n innocuous: juju", `charm "a" relation "innocuous" using a reserved interface: "juju"`, }, { "provides:\n innocuous: juju-snap", `charm "a" relation "innocuous" using a reserved interface: "juju-snap"`, }, { "peers:\n innocuous: juju-snap", `charm "a" relation "innocuous" using a reserved interface: "juju-snap"`, }, } func (s *MetaSuite) TestRelationsConstraints(c *gc.C) { check := func(s, e string) { meta, err := charm.ReadMeta(strings.NewReader(s)) if e != "" { c.Assert(err, gc.ErrorMatches, e) c.Assert(meta, gc.IsNil) } else { c.Assert(err, gc.IsNil) c.Assert(meta, gc.NotNil) } } prefix := "name: a\nsummary: b\ndescription: c\n" for i, t := range relationsConstraintsTests { c.Logf("test %d", i) check(prefix+t.rels, t.err) check(prefix+"subordinate: true\n"+t.rels, t.err) } // The juju-* namespace is accessible to container-scoped require // relations on subordinate charms. check(prefix+` subordinate: true requires: juju-info: interface: juju-info scope: container`, "") // The juju-* interfaces are allowed on any require relation. check(prefix+` requires: innocuous: juju-info`, "") } // dummyMetadata contains a minimally valid charm metadata.yaml // for testing valid and invalid series. const dummyMetadata = "name: a\nsummary: b\ndescription: c" // TestSeries ensures that valid series values are parsed correctly when specified // in the charm metadata. func (s *MetaSuite) TestSeries(c *gc.C) { // series not specified meta, err := charm.ReadMeta(strings.NewReader(dummyMetadata)) c.Assert(err, gc.IsNil) c.Check(meta.Series, gc.Equals, "") for _, seriesName := range []string{"precise", "trusty", "plan9"} { meta, err := charm.ReadMeta(strings.NewReader( fmt.Sprintf("%s\nseries: %s\n", dummyMetadata, seriesName))) c.Assert(err, gc.IsNil) c.Check(meta.Series, gc.Equals, seriesName) } } // TestInvalidSeries ensures that invalid series values cause a parse error // when specified in the charm metadata. func (s *MetaSuite) TestInvalidSeries(c *gc.C) { for _, seriesName := range []string{"pre-c1se", "pre^cise", "cp/m", "OpenVMS"} { _, err := charm.ReadMeta(strings.NewReader( fmt.Sprintf("%s\nseries: %s\n", dummyMetadata, seriesName))) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, `charm "a" declares invalid series: .*`) } } func (s *MetaSuite) TestCheckMismatchedRelationName(c *gc.C) { // This Check case cannot be covered by the above // TestRelationsConstraints tests. meta := charm.Meta{ Name: "foo", Provides: map[string]charm.Relation{ "foo": { Name: "foo", Role: charm.RolePeer, Interface: "x", Limit: 1, Scope: charm.ScopeGlobal, }, }, } err := meta.Check() c.Assert(err, gc.ErrorMatches, `charm "foo" has mismatched role "peer"; expected "provider"`) } func (s *MetaSuite) TestCheckMismatchedRole(c *gc.C) { // This Check case cannot be covered by the above // TestRelationsConstraints tests. meta := charm.Meta{ Name: "foo", Provides: map[string]charm.Relation{ "foo": { Role: charm.RolePeer, Interface: "foo", Limit: 1, Scope: charm.ScopeGlobal, }, }, } err := meta.Check() c.Assert(err, gc.ErrorMatches, `charm "foo" has mismatched relation name ""; expected "foo"`) } // Test rewriting of a given interface specification into long form. // // InterfaceExpander uses `coerce` to do one of two things: // // - Rewrite shorthand to the long form used for actual storage // - Fills in defaults, including a configurable `limit` // // This test ensures test coverage on each of these branches, along // with ensuring the conversion object properly raises SchemaError // exceptions on invalid data. func (s *MetaSuite) TestIfaceExpander(c *gc.C) { e := charm.IfaceExpander(nil) path := []string{"<pa", "th>"} // Shorthand is properly rewritten v, err := e.Coerce("http", path) c.Assert(err, gc.IsNil) c.Assert(v, gc.DeepEquals, map[string]interface{}{"interface": "http", "limit": nil, "optional": false, "scope": string(charm.ScopeGlobal)}) // Defaults are properly applied v, err = e.Coerce(map[string]interface{}{"interface": "http"}, path) c.Assert(err, gc.IsNil) c.Assert(v, gc.DeepEquals, map[string]interface{}{"interface": "http", "limit": nil, "optional": false, "scope": string(charm.ScopeGlobal)}) v, err = e.Coerce(map[string]interface{}{"interface": "http", "limit": 2}, path) c.Assert(err, gc.IsNil) c.Assert(v, gc.DeepEquals, map[string]interface{}{"interface": "http", "limit": int64(2), "optional": false, "scope": string(charm.ScopeGlobal)}) v, err = e.Coerce(map[string]interface{}{"interface": "http", "optional": true}, path) c.Assert(err, gc.IsNil) c.Assert(v, gc.DeepEquals, map[string]interface{}{"interface": "http", "limit": nil, "optional": true, "scope": string(charm.ScopeGlobal)}) // Invalid data raises an error. v, err = e.Coerce(42, path) c.Assert(err, gc.ErrorMatches, `<path>: expected map, got int\(42\)`) v, err = e.Coerce(map[string]interface{}{"interface": "http", "optional": nil}, path) c.Assert(err, gc.ErrorMatches, "<path>.optional: expected bool, got nothing") v, err = e.Coerce(map[string]interface{}{"interface": "http", "limit": "none, really"}, path) c.Assert(err, gc.ErrorMatches, "<path>.limit: unexpected value.*") // Can change default limit e = charm.IfaceExpander(1) v, err = e.Coerce(map[string]interface{}{"interface": "http"}, path) c.Assert(err, gc.IsNil) c.Assert(v, gc.DeepEquals, map[string]interface{}{"interface": "http", "limit": int64(1), "optional": false, "scope": string(charm.ScopeGlobal)}) } func (s *MetaSuite) TestMetaHooks(c *gc.C) { meta, err := charm.ReadMeta(repoMeta("wordpress")) c.Assert(err, gc.IsNil) hooks := meta.Hooks() expectedHooks := map[string]bool{ "install": true, "start": true, "config-changed": true, "upgrade-charm": true, "stop": true, "cache-relation-joined": true, "cache-relation-changed": true, "cache-relation-departed": true, "cache-relation-broken": true, "db-relation-joined": true, "db-relation-changed": true, "db-relation-departed": true, "db-relation-broken": true, "logging-dir-relation-joined": true, "logging-dir-relation-changed": true, "logging-dir-relation-departed": true, "logging-dir-relation-broken": true, "monitoring-port-relation-joined": true, "monitoring-port-relation-changed": true, "monitoring-port-relation-departed": true, "monitoring-port-relation-broken": true, "url-relation-joined": true, "url-relation-changed": true, "url-relation-departed": true, "url-relation-broken": true, } c.Assert(hooks, gc.DeepEquals, expectedHooks) } func (s *MetaSuite) TestCodecRoundTripEmpty(c *gc.C) { for i, codec := range codecs { c.Logf("codec %d", i) empty_input := charm.Meta{} data, err := codec.Marshal(empty_input) c.Assert(err, gc.IsNil) var empty_output charm.Meta err = codec.Unmarshal(data, &empty_output) c.Assert(err, gc.IsNil) c.Assert(empty_input, gc.DeepEquals, empty_output) } } func (s *MetaSuite) TestCodecRoundTrip(c *gc.C) { var input = charm.Meta{ Name: "Foo", Summary: "Bar", Description: "Baz", Subordinate: true, Provides: map[string]charm.Relation{ "qux": { Interface: "quxx", Optional: true, Limit: 42, Scope: "quxxx", }, }, Requires: map[string]charm.Relation{ "qux": { Interface: "quxx", Optional: true, Limit: 42, Scope: "quxxx", }, }, Peers: map[string]charm.Relation{ "qux": { Interface: "quxx", Optional: true, Limit: 42, Scope: "quxxx", }, }, Categories: []string{"quxxxx", "quxxxxx"}, Format: 10, OldRevision: 11, } for i, codec := range codecs { c.Logf("codec %d", i) data, err := codec.Marshal(input) c.Assert(err, gc.IsNil) var output charm.Meta err = codec.Unmarshal(data, &output) c.Assert(err, gc.IsNil) c.Assert(input, gc.DeepEquals, output) } } var implementedByTests = []struct { ifce string name string role charm.RelationRole scope charm.RelationScope match bool implicit bool }{ {"ifce-pro", "pro", charm.RoleProvider, charm.ScopeGlobal, true, false}, {"blah", "pro", charm.RoleProvider, charm.ScopeGlobal, false, false}, {"ifce-pro", "blah", charm.RoleProvider, charm.ScopeGlobal, false, false}, {"ifce-pro", "pro", charm.RoleRequirer, charm.ScopeGlobal, false, false}, {"ifce-pro", "pro", charm.RoleProvider, charm.ScopeContainer, true, false}, {"juju-info", "juju-info", charm.RoleProvider, charm.ScopeGlobal, true, true}, {"blah", "juju-info", charm.RoleProvider, charm.ScopeGlobal, false, false}, {"juju-info", "blah", charm.RoleProvider, charm.ScopeGlobal, false, false}, {"juju-info", "juju-info", charm.RoleRequirer, charm.ScopeGlobal, false, false}, {"juju-info", "juju-info", charm.RoleProvider, charm.ScopeContainer, true, true}, {"ifce-req", "req", charm.RoleRequirer, charm.ScopeGlobal, true, false}, {"blah", "req", charm.RoleRequirer, charm.ScopeGlobal, false, false}, {"ifce-req", "blah", charm.RoleRequirer, charm.ScopeGlobal, false, false}, {"ifce-req", "req", charm.RolePeer, charm.ScopeGlobal, false, false}, {"ifce-req", "req", charm.RoleRequirer, charm.ScopeContainer, true, false}, {"juju-info", "info", charm.RoleRequirer, charm.ScopeContainer, true, false}, {"blah", "info", charm.RoleRequirer, charm.ScopeContainer, false, false}, {"juju-info", "blah", charm.RoleRequirer, charm.ScopeContainer, false, false}, {"juju-info", "info", charm.RolePeer, charm.ScopeContainer, false, false}, {"juju-info", "info", charm.RoleRequirer, charm.ScopeGlobal, false, false}, {"ifce-peer", "peer", charm.RolePeer, charm.ScopeGlobal, true, false}, {"blah", "peer", charm.RolePeer, charm.ScopeGlobal, false, false}, {"ifce-peer", "blah", charm.RolePeer, charm.ScopeGlobal, false, false}, {"ifce-peer", "peer", charm.RoleProvider, charm.ScopeGlobal, false, false}, {"ifce-peer", "peer", charm.RolePeer, charm.ScopeContainer, true, false}, } func (s *MetaSuite) TestImplementedBy(c *gc.C) { for i, t := range implementedByTests { c.Logf("test %d", i) r := charm.Relation{ Interface: t.ifce, Name: t.name, Role: t.role, Scope: t.scope, } c.Assert(r.ImplementedBy(&dummyCharm{}), gc.Equals, t.match) c.Assert(r.IsImplicit(), gc.Equals, t.implicit) } } type dummyCharm struct{} func (c *dummyCharm) Config() *charm.Config { panic("unused") } func (c *dummyCharm) Revision() int { panic("unused") } func (c *dummyCharm) Meta() *charm.Meta { return &charm.Meta{ Provides: map[string]charm.Relation{ "pro": {Interface: "ifce-pro", Scope: charm.ScopeGlobal}, }, Requires: map[string]charm.Relation{ "req": {Interface: "ifce-req", Scope: charm.ScopeGlobal}, "info": {Interface: "juju-info", Scope: charm.ScopeContainer}, }, Peers: map[string]charm.Relation{ "peer": {Interface: "ifce-peer", Scope: charm.ScopeGlobal}, }, } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/bundle_test.go�����������������������������������0000644�0000153�0000161�00000022256�12321735642�024625� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "archive/zip" "bytes" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strconv" "syscall" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils/set" ) type BundleSuite struct { repo *testing.Repo bundlePath string } var _ = gc.Suite(&BundleSuite{}) func (s *BundleSuite) SetUpSuite(c *gc.C) { s.bundlePath = testing.Charms.BundlePath(c.MkDir(), "dummy") } var dummyManifest = []string{ "config.yaml", "empty", "hooks", "hooks/install", "metadata.yaml", "revision", "src", "src/hello.c", } func (s *BundleSuite) TestReadBundle(c *gc.C) { bundle, err := charm.ReadBundle(s.bundlePath) c.Assert(err, gc.IsNil) checkDummy(c, bundle, s.bundlePath) } func (s *BundleSuite) TestReadBundleWithoutConfig(c *gc.C) { path := testing.Charms.BundlePath(c.MkDir(), "varnish") bundle, err := charm.ReadBundle(path) c.Assert(err, gc.IsNil) // A lacking config.yaml file still causes a proper // Config value to be returned. c.Assert(bundle.Config().Options, gc.HasLen, 0) } func (s *BundleSuite) TestReadBundleBytes(c *gc.C) { data, err := ioutil.ReadFile(s.bundlePath) c.Assert(err, gc.IsNil) bundle, err := charm.ReadBundleBytes(data) c.Assert(err, gc.IsNil) checkDummy(c, bundle, "") } func (s *BundleSuite) TestManifest(c *gc.C) { bundle, err := charm.ReadBundle(s.bundlePath) c.Assert(err, gc.IsNil) manifest, err := bundle.Manifest() c.Assert(err, gc.IsNil) c.Assert(manifest, jc.DeepEquals, set.NewStrings(dummyManifest...)) } func (s *BundleSuite) TestManifestNoRevision(c *gc.C) { bundle, err := charm.ReadBundle(s.bundlePath) c.Assert(err, gc.IsNil) dirPath := c.MkDir() err = bundle.ExpandTo(dirPath) c.Assert(err, gc.IsNil) err = os.Remove(filepath.Join(dirPath, "revision")) c.Assert(err, gc.IsNil) bundle = extBundleDir(c, dirPath) manifest, err := bundle.Manifest() c.Assert(err, gc.IsNil) c.Assert(manifest, gc.DeepEquals, set.NewStrings(dummyManifest...)) } func (s *BundleSuite) TestManifestSymlink(c *gc.C) { srcPath := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") if err := os.Symlink("../target", filepath.Join(srcPath, "hooks/symlink")); err != nil { c.Skip("cannot symlink") } expected := append([]string{"hooks/symlink"}, dummyManifest...) bundle := bundleDir(c, srcPath) manifest, err := bundle.Manifest() c.Assert(err, gc.IsNil) c.Assert(manifest, gc.DeepEquals, set.NewStrings(expected...)) } func (s *BundleSuite) TestExpandTo(c *gc.C) { bundle, err := charm.ReadBundle(s.bundlePath) c.Assert(err, gc.IsNil) path := filepath.Join(c.MkDir(), "charm") err = bundle.ExpandTo(path) c.Assert(err, gc.IsNil) dir, err := charm.ReadDir(path) c.Assert(err, gc.IsNil) checkDummy(c, dir, path) } func (s *BundleSuite) prepareBundle(c *gc.C, charmDir *charm.Dir, bundlePath string) { file, err := os.Create(bundlePath) c.Assert(err, gc.IsNil) defer file.Close() zipw := zip.NewWriter(file) defer zipw.Close() h := &zip.FileHeader{Name: "revision"} h.SetMode(syscall.S_IFREG | 0644) w, err := zipw.CreateHeader(h) c.Assert(err, gc.IsNil) _, err = w.Write([]byte(strconv.Itoa(charmDir.Revision()))) h = &zip.FileHeader{Name: "metadata.yaml", Method: zip.Deflate} h.SetMode(0644) w, err = zipw.CreateHeader(h) c.Assert(err, gc.IsNil) data, err := goyaml.Marshal(charmDir.Meta()) c.Assert(err, gc.IsNil) _, err = w.Write(data) c.Assert(err, gc.IsNil) for name := range charmDir.Meta().Hooks() { hookName := filepath.Join("hooks", name) h = &zip.FileHeader{ Name: hookName, Method: zip.Deflate, } // Force it non-executable h.SetMode(0644) w, err := zipw.CreateHeader(h) c.Assert(err, gc.IsNil) _, err = w.Write([]byte("not important")) c.Assert(err, gc.IsNil) } } func (s *BundleSuite) TestExpandToSetsHooksExecutable(c *gc.C) { charmDir := testing.Charms.ClonedDir(c.MkDir(), "all-hooks") // Bundle manually, so we can check ExpandTo(), unaffected // by BundleTo()'s behavior bundlePath := filepath.Join(c.MkDir(), "bundle.charm") s.prepareBundle(c, charmDir, bundlePath) bundle, err := charm.ReadBundle(bundlePath) c.Assert(err, gc.IsNil) path := filepath.Join(c.MkDir(), "charm") err = bundle.ExpandTo(path) c.Assert(err, gc.IsNil) _, err = charm.ReadDir(path) c.Assert(err, gc.IsNil) for name := range bundle.Meta().Hooks() { hookName := string(name) info, err := os.Stat(filepath.Join(path, "hooks", hookName)) c.Assert(err, gc.IsNil) perm := info.Mode() & 0777 c.Assert(perm&0100 != 0, gc.Equals, true, gc.Commentf("hook %q is not executable", hookName)) } } func (s *BundleSuite) TestBundleFileModes(c *gc.C) { // Apply subtler mode differences than can be expressed in Bazaar. srcPath := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") modes := []struct { path string mode os.FileMode }{ {"hooks/install", 0751}, {"empty", 0750}, {"src/hello.c", 0614}, } for _, m := range modes { err := os.Chmod(filepath.Join(srcPath, m.path), m.mode) c.Assert(err, gc.IsNil) } var haveSymlinks = true if err := os.Symlink("../target", filepath.Join(srcPath, "hooks/symlink")); err != nil { haveSymlinks = false } // Bundle and extract the charm to a new directory. bundle := bundleDir(c, srcPath) path := c.MkDir() err := bundle.ExpandTo(path) c.Assert(err, gc.IsNil) // Check sensible file modes once round-tripped. info, err := os.Stat(filepath.Join(path, "src", "hello.c")) c.Assert(err, gc.IsNil) c.Assert(info.Mode()&0777, gc.Equals, os.FileMode(0644)) c.Assert(info.Mode()&os.ModeType, gc.Equals, os.FileMode(0)) info, err = os.Stat(filepath.Join(path, "hooks", "install")) c.Assert(err, gc.IsNil) c.Assert(info.Mode()&0777, gc.Equals, os.FileMode(0755)) c.Assert(info.Mode()&os.ModeType, gc.Equals, os.FileMode(0)) info, err = os.Stat(filepath.Join(path, "empty")) c.Assert(err, gc.IsNil) c.Assert(info.Mode()&0777, gc.Equals, os.FileMode(0755)) if haveSymlinks { target, err := os.Readlink(filepath.Join(path, "hooks", "symlink")) c.Assert(err, gc.IsNil) c.Assert(target, gc.Equals, "../target") } } func (s *BundleSuite) TestBundleRevisionFile(c *gc.C) { charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") revPath := filepath.Join(charmDir, "revision") // Missing revision file err := os.Remove(revPath) c.Assert(err, gc.IsNil) bundle := extBundleDir(c, charmDir) c.Assert(bundle.Revision(), gc.Equals, 0) // Missing revision file with old revision in metadata file, err := os.OpenFile(filepath.Join(charmDir, "metadata.yaml"), os.O_WRONLY|os.O_APPEND, 0) c.Assert(err, gc.IsNil) _, err = file.Write([]byte("\nrevision: 1234\n")) c.Assert(err, gc.IsNil) bundle = extBundleDir(c, charmDir) c.Assert(bundle.Revision(), gc.Equals, 1234) // Revision file with bad content err = ioutil.WriteFile(revPath, []byte("garbage"), 0666) c.Assert(err, gc.IsNil) path := extBundleDirPath(c, charmDir) bundle, err = charm.ReadBundle(path) c.Assert(err, gc.ErrorMatches, "invalid revision file") c.Assert(bundle, gc.IsNil) } func (s *BundleSuite) TestBundleSetRevision(c *gc.C) { bundle, err := charm.ReadBundle(s.bundlePath) c.Assert(err, gc.IsNil) c.Assert(bundle.Revision(), gc.Equals, 1) bundle.SetRevision(42) c.Assert(bundle.Revision(), gc.Equals, 42) path := filepath.Join(c.MkDir(), "charm") err = bundle.ExpandTo(path) c.Assert(err, gc.IsNil) dir, err := charm.ReadDir(path) c.Assert(err, gc.IsNil) c.Assert(dir.Revision(), gc.Equals, 42) } func (s *BundleSuite) TestExpandToWithBadLink(c *gc.C) { charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") badLink := filepath.Join(charmDir, "hooks", "badlink") // Symlink targeting a path outside of the charm. err := os.Symlink("../../target", badLink) c.Assert(err, gc.IsNil) bundle := extBundleDir(c, charmDir) c.Assert(err, gc.IsNil) path := filepath.Join(c.MkDir(), "charm") err = bundle.ExpandTo(path) c.Assert(err, gc.ErrorMatches, `cannot extract "hooks/badlink": symlink "../../target" leads out of scope`) // Symlink targeting an absolute path. os.Remove(badLink) err = os.Symlink("/target", badLink) c.Assert(err, gc.IsNil) bundle = extBundleDir(c, charmDir) c.Assert(err, gc.IsNil) path = filepath.Join(c.MkDir(), "charm") err = bundle.ExpandTo(path) c.Assert(err, gc.ErrorMatches, `cannot extract "hooks/badlink": symlink "/target" is absolute`) } func extBundleDirPath(c *gc.C, dirpath string) string { path := filepath.Join(c.MkDir(), "bundle.charm") cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("cd %s; zip --fifo --symlinks -r %s .", dirpath, path)) output, err := cmd.CombinedOutput() c.Assert(err, gc.IsNil, gc.Commentf("Command output: %s", output)) return path } func extBundleDir(c *gc.C, dirpath string) *charm.Bundle { path := extBundleDirPath(c, dirpath) bundle, err := charm.ReadBundle(path) c.Assert(err, gc.IsNil) return bundle } func bundleDir(c *gc.C, dirpath string) *charm.Bundle { dir, err := charm.ReadDir(dirpath) c.Assert(err, gc.IsNil) buf := new(bytes.Buffer) err = dir.BundleTo(buf) c.Assert(err, gc.IsNil) bundle, err := charm.ReadBundleBytes(buf.Bytes()) c.Assert(err, gc.IsNil) return bundle } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/bundle.go����������������������������������������0000644�0000153�0000161�00000013101�12321735642�023553� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "archive/zip" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "strconv" "launchpad.net/juju-core/utils/set" ziputil "launchpad.net/juju-core/utils/zip" ) // The Bundle type encapsulates access to data and operations // on a charm bundle. type Bundle struct { Path string // May be empty if Bundle wasn't read from a file meta *Meta config *Config revision int r io.ReaderAt size int64 } // Trick to ensure *Bundle implements the Charm interface. var _ Charm = (*Bundle)(nil) // ReadBundle returns a Bundle for the charm in path. func ReadBundle(path string) (bundle *Bundle, err error) { f, err := os.Open(path) if err != nil { return } defer f.Close() fi, err := f.Stat() if err != nil { return } b, err := readBundle(f, fi.Size()) if err != nil { return } b.Path = path return b, nil } // ReadBundleBytes returns a Bundle read from the given data. // Make sure the bundle fits in memory before using this. func ReadBundleBytes(data []byte) (bundle *Bundle, err error) { return readBundle(readAtBytes(data), int64(len(data))) } func readBundle(r io.ReaderAt, size int64) (bundle *Bundle, err error) { b := &Bundle{r: r, size: size} zipr, err := zip.NewReader(r, size) if err != nil { return } reader, err := zipOpen(zipr, "metadata.yaml") if err != nil { return } b.meta, err = ReadMeta(reader) reader.Close() if err != nil { return } reader, err = zipOpen(zipr, "config.yaml") if _, ok := err.(*noBundleFile); ok { b.config = NewConfig() } else if err != nil { return nil, err } else { b.config, err = ReadConfig(reader) reader.Close() if err != nil { return nil, err } } reader, err = zipOpen(zipr, "revision") if err != nil { if _, ok := err.(*noBundleFile); !ok { return } b.revision = b.meta.OldRevision } else { _, err = fmt.Fscan(reader, &b.revision) if err != nil { return nil, errors.New("invalid revision file") } } return b, nil } func zipOpen(zipr *zip.Reader, path string) (rc io.ReadCloser, err error) { for _, fh := range zipr.File { if fh.Name == path { return fh.Open() } } return nil, &noBundleFile{path} } type noBundleFile struct { path string } func (err noBundleFile) Error() string { return fmt.Sprintf("bundle file not found: %s", err.path) } // Revision returns the revision number for the charm // expanded in dir. func (b *Bundle) Revision() int { return b.revision } // SetRevision changes the charm revision number. This affects the // revision reported by Revision and the revision of the charm // directory created by ExpandTo. func (b *Bundle) SetRevision(revision int) { b.revision = revision } // Meta returns the Meta representing the metadata.yaml file from bundle. func (b *Bundle) Meta() *Meta { return b.meta } // Config returns the Config representing the config.yaml file // for the charm bundle. func (b *Bundle) Config() *Config { return b.config } type zipReadCloser struct { io.Closer *zip.Reader } // zipOpen returns a zipReadCloser. func (b *Bundle) zipOpen() (*zipReadCloser, error) { // If we don't have a Path, try to use the original ReaderAt. if b.Path == "" { r, err := zip.NewReader(b.r, b.size) if err != nil { return nil, err } return &zipReadCloser{Closer: ioutil.NopCloser(nil), Reader: r}, nil } f, err := os.Open(b.Path) if err != nil { return nil, err } fi, err := f.Stat() if err != nil { f.Close() return nil, err } r, err := zip.NewReader(f, fi.Size()) if err != nil { f.Close() return nil, err } return &zipReadCloser{Closer: f, Reader: r}, nil } // Manifest returns a set of the charm's contents. func (b *Bundle) Manifest() (set.Strings, error) { zipr, err := b.zipOpen() if err != nil { return set.NewStrings(), err } defer zipr.Close() paths, err := ziputil.Find(zipr.Reader, "*") if err != nil { return set.NewStrings(), err } manifest := set.NewStrings(paths...) // We always write out a revision file, even if there isn't one in the // bundle; and we always strip ".", because that's sometimes not present. manifest.Add("revision") manifest.Remove(".") return manifest, nil } // ExpandTo expands the charm bundle into dir, creating it if necessary. // If any errors occur during the expansion procedure, the process will // abort. func (b *Bundle) ExpandTo(dir string) (err error) { zipr, err := b.zipOpen() if err != nil { return err } defer zipr.Close() if err := ziputil.ExtractAll(zipr.Reader, dir); err != nil { return err } hooksDir := filepath.Join(dir, "hooks") fixHook := fixHookFunc(hooksDir, b.meta.Hooks()) if err := filepath.Walk(hooksDir, fixHook); err != nil { if !os.IsNotExist(err) { return err } } revFile, err := os.Create(filepath.Join(dir, "revision")) if err != nil { return err } _, err = revFile.Write([]byte(strconv.Itoa(b.revision))) revFile.Close() return err } // fixHookFunc returns a WalkFunc that makes sure hooks are owner-executable. func fixHookFunc(hooksDir string, hookNames map[string]bool) filepath.WalkFunc { return func(path string, info os.FileInfo, err error) error { if err != nil { return err } mode := info.Mode() if path != hooksDir && mode.IsDir() { return filepath.SkipDir } if name := filepath.Base(path); hookNames[name] { if mode&0100 == 0 { return os.Chmod(path, mode|0100) } } return nil } } // FWIW, being able to do this is awesome. type readAtBytes []byte func (b readAtBytes) ReadAt(out []byte, off int64) (n int, err error) { return copy(out, b[off:]), nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/repo_test.go�������������������������������������0000644�0000153�0000161�00000043742�12321735776�024334� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "fmt" "io/ioutil" "os" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" charmtesting "launchpad.net/juju-core/charm/testing" env_config "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type StoreSuite struct { testbase.LoggingSuite server *charmtesting.MockStore store *charm.CharmStore } var _ = gc.Suite(&StoreSuite{}) func (s *StoreSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.server = charmtesting.NewMockStore(c, map[string]int{ "cs:series/good": 23, "cs:series/unwise": 23, "cs:series/better": 24, "cs:series/best": 25, }) } func (s *StoreSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.PatchValue(&charm.CacheDir, c.MkDir()) s.store = charm.NewStore(s.server.Address()) s.server.Downloads = nil s.server.Authorizations = nil s.server.Metadata = nil s.server.DownloadsNoStats = nil s.server.InfoRequestCount = 0 s.server.InfoRequestCountNoStats = 0 } // Uses the TearDownTest from testbase.LoggingSuite func (s *StoreSuite) TearDownSuite(c *gc.C) { s.server.Close() s.LoggingSuite.TearDownSuite(c) } func (s *StoreSuite) TestMissing(c *gc.C) { charmURL := charm.MustParseURL("cs:series/missing") expect := `charm not found: cs:series/missing` _, err := charm.Latest(s.store, charmURL) c.Assert(err, gc.ErrorMatches, expect) _, err = s.store.Get(charmURL) c.Assert(err, gc.ErrorMatches, expect) } func (s *StoreSuite) TestError(c *gc.C) { charmURL := charm.MustParseURL("cs:series/borken") expect := `charm info errors for "cs:series/borken": badness` _, err := charm.Latest(s.store, charmURL) c.Assert(err, gc.ErrorMatches, expect) _, err = s.store.Get(charmURL) c.Assert(err, gc.ErrorMatches, expect) } func (s *StoreSuite) TestWarning(c *gc.C) { charmURL := charm.MustParseURL("cs:series/unwise") expect := `.* WARNING juju charm store reports for "cs:series/unwise": foolishness` + "\n" r, err := charm.Latest(s.store, charmURL) c.Assert(r, gc.Equals, 23) c.Assert(err, gc.IsNil) c.Assert(c.GetTestLog(), gc.Matches, expect) ch, err := s.store.Get(charmURL) c.Assert(ch, gc.NotNil) c.Assert(err, gc.IsNil) c.Assert(c.GetTestLog(), gc.Matches, expect+expect) } func (s *StoreSuite) TestLatest(c *gc.C) { urls := []*charm.URL{ charm.MustParseURL("cs:series/good"), charm.MustParseURL("cs:series/good-2"), charm.MustParseURL("cs:series/good-99"), } revInfo, err := s.store.Latest(urls...) c.Assert(err, gc.IsNil) c.Assert(revInfo, gc.DeepEquals, []charm.CharmRevision{ {23, "2c9f01a53a73c221d5360207e7bb2f887ff83c32b04e58aca76c4d99fd071ec7", nil}, {23, "2c9f01a53a73c221d5360207e7bb2f887ff83c32b04e58aca76c4d99fd071ec7", nil}, {23, "2c9f01a53a73c221d5360207e7bb2f887ff83c32b04e58aca76c4d99fd071ec7", nil}, }) } func (s *StoreSuite) assertCached(c *gc.C, charmURL *charm.URL) { s.server.Downloads = nil ch, err := s.store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch, gc.NotNil) c.Assert(s.server.Downloads, gc.IsNil) } func (s *StoreSuite) TestGetCacheImplicitRevision(c *gc.C) { base := "cs:series/good" charmURL := charm.MustParseURL(base) revCharmURL := charm.MustParseURL(base + "-23") ch, err := s.store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch, gc.NotNil) c.Assert(s.server.Downloads, gc.DeepEquals, []*charm.URL{revCharmURL}) s.assertCached(c, charmURL) s.assertCached(c, revCharmURL) } func (s *StoreSuite) TestGetCacheExplicitRevision(c *gc.C) { base := "cs:series/good-12" charmURL := charm.MustParseURL(base) ch, err := s.store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch, gc.NotNil) c.Assert(s.server.Downloads, gc.DeepEquals, []*charm.URL{charmURL}) s.assertCached(c, charmURL) } func (s *StoreSuite) TestGetBadCache(c *gc.C) { c.Assert(os.Mkdir(filepath.Join(charm.CacheDir, "cache"), 0777), gc.IsNil) base := "cs:series/good" charmURL := charm.MustParseURL(base) revCharmURL := charm.MustParseURL(base + "-23") name := charm.Quote(revCharmURL.String()) + ".charm" err := ioutil.WriteFile(filepath.Join(charm.CacheDir, "cache", name), nil, 0666) c.Assert(err, gc.IsNil) ch, err := s.store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch, gc.NotNil) c.Assert(s.server.Downloads, gc.DeepEquals, []*charm.URL{revCharmURL}) s.assertCached(c, charmURL) s.assertCached(c, revCharmURL) } func (s *StoreSuite) TestGetTestModeFlag(c *gc.C) { base := "cs:series/good-12" charmURL := charm.MustParseURL(base) ch, err := s.store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch, gc.NotNil) c.Assert(s.server.Downloads, gc.DeepEquals, []*charm.URL{charmURL}) c.Assert(s.server.DownloadsNoStats, gc.IsNil) c.Assert(s.server.InfoRequestCount, gc.Equals, 1) c.Assert(s.server.InfoRequestCountNoStats, gc.Equals, 0) storeInTestMode := s.store.WithTestMode(true) other := "cs:series/good-23" otherURL := charm.MustParseURL(other) ch, err = storeInTestMode.Get(otherURL) c.Assert(err, gc.IsNil) c.Assert(ch, gc.NotNil) c.Assert(s.server.Downloads, gc.DeepEquals, []*charm.URL{charmURL}) c.Assert(s.server.DownloadsNoStats, gc.DeepEquals, []*charm.URL{otherURL}) c.Assert(s.server.InfoRequestCount, gc.Equals, 1) c.Assert(s.server.InfoRequestCountNoStats, gc.Equals, 1) } // The following tests cover the low-level CharmStore-specific API. func (s *StoreSuite) TestInfo(c *gc.C) { charmURLs := []charm.Location{ charm.MustParseURL("cs:series/good"), charm.MustParseURL("cs:series/better"), charm.MustParseURL("cs:series/best"), } infos, err := s.store.Info(charmURLs...) c.Assert(err, gc.IsNil) c.Assert(infos, gc.HasLen, 3) expected := []int{23, 24, 25} for i, info := range infos { c.Assert(info.Errors, gc.IsNil) c.Assert(info.Revision, gc.Equals, expected[i]) } } func (s *StoreSuite) TestInfoNotFound(c *gc.C) { charmURL := charm.MustParseURL("cs:series/missing") info, err := s.store.Info(charmURL) c.Assert(err, gc.IsNil) c.Assert(info, gc.HasLen, 1) c.Assert(info[0].Errors, gc.HasLen, 1) c.Assert(info[0].Errors[0], gc.Matches, `charm not found: cs:series/missing`) } func (s *StoreSuite) TestInfoError(c *gc.C) { charmURL := charm.MustParseURL("cs:series/borken") info, err := s.store.Info(charmURL) c.Assert(err, gc.IsNil) c.Assert(info, gc.HasLen, 1) c.Assert(info[0].Errors, gc.DeepEquals, []string{"badness"}) } func (s *StoreSuite) TestInfoWarning(c *gc.C) { charmURL := charm.MustParseURL("cs:series/unwise") info, err := s.store.Info(charmURL) c.Assert(err, gc.IsNil) c.Assert(info, gc.HasLen, 1) c.Assert(info[0].Warnings, gc.DeepEquals, []string{"foolishness"}) } func (s *StoreSuite) TestInfoTestModeFlag(c *gc.C) { charmURL := charm.MustParseURL("cs:series/good") _, err := s.store.Info(charmURL) c.Assert(err, gc.IsNil) c.Assert(s.server.InfoRequestCount, gc.Equals, 1) c.Assert(s.server.InfoRequestCountNoStats, gc.Equals, 0) storeInTestMode, ok := s.store.WithTestMode(true).(*charm.CharmStore) c.Assert(ok, gc.Equals, true) _, err = storeInTestMode.Info(charmURL) c.Assert(err, gc.IsNil) c.Assert(s.server.InfoRequestCount, gc.Equals, 1) c.Assert(s.server.InfoRequestCountNoStats, gc.Equals, 1) } func (s *StoreSuite) TestInfoDNSError(c *gc.C) { store := charm.NewStore("http://0.1.2.3") charmURL := charm.MustParseURL("cs:series/good") resp, err := store.Info(charmURL) c.Assert(resp, gc.IsNil) expect := `Cannot access the charm store. Are you connected to the internet. Error details:.*` c.Assert(err, gc.ErrorMatches, expect) } func (s *StoreSuite) TestEvent(c *gc.C) { charmURL := charm.MustParseURL("cs:series/good") event, err := s.store.Event(charmURL, "") c.Assert(err, gc.IsNil) c.Assert(event.Errors, gc.IsNil) c.Assert(event.Revision, gc.Equals, 23) c.Assert(event.Digest, gc.Equals, "the-digest") } func (s *StoreSuite) TestEventWithDigest(c *gc.C) { charmURL := charm.MustParseURL("cs:series/good") event, err := s.store.Event(charmURL, "the-digest") c.Assert(err, gc.IsNil) c.Assert(event.Errors, gc.IsNil) c.Assert(event.Revision, gc.Equals, 23) c.Assert(event.Digest, gc.Equals, "the-digest") } func (s *StoreSuite) TestEventNotFound(c *gc.C) { charmURL := charm.MustParseURL("cs:series/missing") event, err := s.store.Event(charmURL, "") c.Assert(err, gc.ErrorMatches, `charm event not found for "cs:series/missing"`) c.Assert(event, gc.IsNil) } func (s *StoreSuite) TestEventNotFoundDigest(c *gc.C) { charmURL := charm.MustParseURL("cs:series/good") event, err := s.store.Event(charmURL, "missing-digest") c.Assert(err, gc.ErrorMatches, `charm event not found for "cs:series/good" with digest "missing-digest"`) c.Assert(event, gc.IsNil) } func (s *StoreSuite) TestEventError(c *gc.C) { charmURL := charm.MustParseURL("cs:series/borken") event, err := s.store.Event(charmURL, "") c.Assert(err, gc.IsNil) c.Assert(event.Errors, gc.DeepEquals, []string{"badness"}) } func (s *StoreSuite) TestAuthorization(c *gc.C) { config := testing.CustomEnvironConfig(c, testing.Attrs{"charm-store-auth": "token=value"}) store := env_config.SpecializeCharmRepo(s.store, config) base := "cs:series/good" charmURL := charm.MustParseURL(base) _, err := store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(s.server.Authorizations, gc.HasLen, 1) c.Assert(s.server.Authorizations[0], gc.Equals, "charmstore token=value") } func (s *StoreSuite) TestNilAuthorization(c *gc.C) { config := testing.EnvironConfig(c) store := env_config.SpecializeCharmRepo(s.store, config) base := "cs:series/good" charmURL := charm.MustParseURL(base) _, err := store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(s.server.Authorizations, gc.HasLen, 0) } func (s *StoreSuite) TestMetadata(c *gc.C) { store := s.store.WithJujuAttrs("juju-metadata") base := "cs:series/good" charmURL := charm.MustParseURL(base) _, err := store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(s.server.Metadata, gc.HasLen, 1) c.Assert(s.server.Metadata[0], gc.Equals, "juju-metadata") } func (s *StoreSuite) TestNilMetadata(c *gc.C) { base := "cs:series/good" charmURL := charm.MustParseURL(base) _, err := s.store.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(s.server.Metadata, gc.HasLen, 0) } func (s *StoreSuite) TestEventWarning(c *gc.C) { charmURL := charm.MustParseURL("cs:series/unwise") event, err := s.store.Event(charmURL, "") c.Assert(err, gc.IsNil) c.Assert(event.Warnings, gc.DeepEquals, []string{"foolishness"}) } func (s *StoreSuite) TestBranchLocation(c *gc.C) { charmURL := charm.MustParseURL("cs:series/name") location := s.store.BranchLocation(charmURL) c.Assert(location, gc.Equals, "lp:charms/series/name") charmURL = charm.MustParseURL("cs:~user/series/name") location = s.store.BranchLocation(charmURL) c.Assert(location, gc.Equals, "lp:~user/charms/series/name/trunk") } func (s *StoreSuite) TestCharmURL(c *gc.C) { tests := []struct{ url, loc string }{ {"cs:precise/wordpress", "lp:charms/precise/wordpress"}, {"cs:precise/wordpress", "http://launchpad.net/+branch/charms/precise/wordpress"}, {"cs:precise/wordpress", "https://launchpad.net/+branch/charms/precise/wordpress"}, {"cs:precise/wordpress", "http://code.launchpad.net/+branch/charms/precise/wordpress"}, {"cs:precise/wordpress", "https://code.launchpad.net/+branch/charms/precise/wordpress"}, {"cs:precise/wordpress", "bzr+ssh://bazaar.launchpad.net/+branch/charms/precise/wordpress"}, {"cs:~charmers/precise/wordpress", "lp:~charmers/charms/precise/wordpress/trunk"}, {"cs:~charmers/precise/wordpress", "http://launchpad.net/~charmers/charms/precise/wordpress/trunk"}, {"cs:~charmers/precise/wordpress", "https://launchpad.net/~charmers/charms/precise/wordpress/trunk"}, {"cs:~charmers/precise/wordpress", "http://code.launchpad.net/~charmers/charms/precise/wordpress/trunk"}, {"cs:~charmers/precise/wordpress", "https://code.launchpad.net/~charmers/charms/precise/wordpress/trunk"}, {"cs:~charmers/precise/wordpress", "http://launchpad.net/+branch/~charmers/charms/precise/wordpress/trunk"}, {"cs:~charmers/precise/wordpress", "https://launchpad.net/+branch/~charmers/charms/precise/wordpress/trunk"}, {"cs:~charmers/precise/wordpress", "http://code.launchpad.net/+branch/~charmers/charms/precise/wordpress/trunk"}, {"cs:~charmers/precise/wordpress", "https://code.launchpad.net/+branch/~charmers/charms/precise/wordpress/trunk"}, {"cs:~charmers/precise/wordpress", "bzr+ssh://bazaar.launchpad.net/~charmers/charms/precise/wordpress/trunk"}, {"cs:~charmers/precise/wordpress", "bzr+ssh://bazaar.launchpad.net/~charmers/charms/precise/wordpress/trunk/"}, {"cs:~charmers/precise/wordpress", "~charmers/charms/precise/wordpress/trunk"}, {"", "lp:~charmers/charms/precise/wordpress/whatever"}, {"", "lp:~charmers/whatever/precise/wordpress/trunk"}, {"", "lp:whatever/precise/wordpress"}, } for _, t := range tests { charmURL, err := s.store.CharmURL(t.loc) if t.url == "" { c.Assert(err, gc.ErrorMatches, fmt.Sprintf("unknown branch location: %q", t.loc)) } else { c.Assert(err, gc.IsNil) c.Assert(charmURL.String(), gc.Equals, t.url) } } } type LocalRepoSuite struct { testbase.LoggingSuite repo *charm.LocalRepository seriesPath string } var _ = gc.Suite(&LocalRepoSuite{}) func (s *LocalRepoSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) root := c.MkDir() s.repo = &charm.LocalRepository{Path: root} s.seriesPath = filepath.Join(root, "quantal") c.Assert(os.Mkdir(s.seriesPath, 0777), gc.IsNil) } func (s *LocalRepoSuite) addBundle(name string) string { return testing.Charms.BundlePath(s.seriesPath, name) } func (s *LocalRepoSuite) addDir(name string) string { return testing.Charms.ClonedDirPath(s.seriesPath, name) } func (s *LocalRepoSuite) checkNotFoundErr(c *gc.C, err error, charmURL *charm.URL) { expect := `charm not found in "` + s.repo.Path + `": ` + charmURL.String() c.Check(err, gc.ErrorMatches, expect) } func (s *LocalRepoSuite) TestMissingCharm(c *gc.C) { for i, str := range []string{ "local:quantal/zebra", "local:badseries/zebra", } { c.Logf("test %d: %s", i, str) charmURL := charm.MustParseURL(str) _, err := charm.Latest(s.repo, charmURL) s.checkNotFoundErr(c, err, charmURL) _, err = s.repo.Get(charmURL) s.checkNotFoundErr(c, err, charmURL) } } func (s *LocalRepoSuite) TestMissingRepo(c *gc.C) { c.Assert(os.RemoveAll(s.repo.Path), gc.IsNil) _, err := charm.Latest(s.repo, charm.MustParseURL("local:quantal/zebra")) c.Assert(err, gc.ErrorMatches, `no repository found at ".*"`) _, err = s.repo.Get(charm.MustParseURL("local:quantal/zebra")) c.Assert(err, gc.ErrorMatches, `no repository found at ".*"`) c.Assert(ioutil.WriteFile(s.repo.Path, nil, 0666), gc.IsNil) _, err = charm.Latest(s.repo, charm.MustParseURL("local:quantal/zebra")) c.Assert(err, gc.ErrorMatches, `no repository found at ".*"`) _, err = s.repo.Get(charm.MustParseURL("local:quantal/zebra")) c.Assert(err, gc.ErrorMatches, `no repository found at ".*"`) } func (s *LocalRepoSuite) TestMultipleVersions(c *gc.C) { charmURL := charm.MustParseURL("local:quantal/upgrade") s.addDir("upgrade1") rev, err := charm.Latest(s.repo, charmURL) c.Assert(err, gc.IsNil) c.Assert(rev, gc.Equals, 1) ch, err := s.repo.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, 1) s.addDir("upgrade2") rev, err = charm.Latest(s.repo, charmURL) c.Assert(err, gc.IsNil) c.Assert(rev, gc.Equals, 2) ch, err = s.repo.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, 2) revCharmURL := charmURL.WithRevision(1) rev, err = charm.Latest(s.repo, revCharmURL) c.Assert(err, gc.IsNil) c.Assert(rev, gc.Equals, 2) ch, err = s.repo.Get(revCharmURL) c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, 1) badRevCharmURL := charmURL.WithRevision(33) rev, err = charm.Latest(s.repo, badRevCharmURL) c.Assert(err, gc.IsNil) c.Assert(rev, gc.Equals, 2) _, err = s.repo.Get(badRevCharmURL) s.checkNotFoundErr(c, err, badRevCharmURL) } func (s *LocalRepoSuite) TestBundle(c *gc.C) { charmURL := charm.MustParseURL("local:quantal/dummy") s.addBundle("dummy") rev, err := charm.Latest(s.repo, charmURL) c.Assert(err, gc.IsNil) c.Assert(rev, gc.Equals, 1) ch, err := s.repo.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, 1) } func (s *LocalRepoSuite) TestLogsErrors(c *gc.C) { err := ioutil.WriteFile(filepath.Join(s.seriesPath, "blah.charm"), nil, 0666) c.Assert(err, gc.IsNil) err = os.Mkdir(filepath.Join(s.seriesPath, "blah"), 0666) c.Assert(err, gc.IsNil) samplePath := s.addDir("upgrade2") gibberish := []byte("don't parse me by") err = ioutil.WriteFile(filepath.Join(samplePath, "metadata.yaml"), gibberish, 0666) c.Assert(err, gc.IsNil) charmURL := charm.MustParseURL("local:quantal/dummy") s.addDir("dummy") ch, err := s.repo.Get(charmURL) c.Assert(err, gc.IsNil) c.Assert(ch.Revision(), gc.Equals, 1) c.Assert(c.GetTestLog(), gc.Matches, ` .* WARNING juju failed to load charm at ".*/quantal/blah": .* .* WARNING juju failed to load charm at ".*/quantal/blah.charm": .* .* WARNING juju failed to load charm at ".*/quantal/upgrade2": .* `[1:]) } func renameSibling(c *gc.C, path, name string) { c.Assert(os.Rename(path, filepath.Join(filepath.Dir(path), name)), gc.IsNil) } func (s *LocalRepoSuite) TestIgnoresUnpromisingNames(c *gc.C) { err := ioutil.WriteFile(filepath.Join(s.seriesPath, "blah.notacharm"), nil, 0666) c.Assert(err, gc.IsNil) err = os.Mkdir(filepath.Join(s.seriesPath, ".blah"), 0666) c.Assert(err, gc.IsNil) renameSibling(c, s.addDir("dummy"), ".dummy") renameSibling(c, s.addBundle("dummy"), "dummy.notacharm") charmURL := charm.MustParseURL("local:quantal/dummy") _, err = s.repo.Get(charmURL) s.checkNotFoundErr(c, err, charmURL) _, err = charm.Latest(s.repo, charmURL) s.checkNotFoundErr(c, err, charmURL) c.Assert(c.GetTestLog(), gc.Equals, "") } func (s *LocalRepoSuite) TestFindsSymlinks(c *gc.C) { realPath := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") linkPath := filepath.Join(s.seriesPath, "dummy") err := os.Symlink(realPath, linkPath) c.Assert(err, gc.IsNil) ch, err := s.repo.Get(charm.MustParseURL("local:quantal/dummy")) c.Assert(err, gc.IsNil) checkDummy(c, ch, linkPath) } ������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/charm.go�����������������������������������������0000644�0000153�0000161�00000002143�12321735642�023400� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "errors" "fmt" "os" ) // The Charm interface is implemented by any type that // may be handled as a charm. type Charm interface { Meta() *Meta Config() *Config Revision() int } // Read reads a Charm from path, which can point to either a charm bundle or a // charm directory. func Read(path string) (Charm, error) { info, err := os.Stat(path) if err != nil { return nil, err } if info.IsDir() { return ReadDir(path) } return ReadBundle(path) } // InferRepository returns a charm repository inferred from the provided charm // reference. Local references will use the provided path. func InferRepository(ref Reference, localRepoPath string) (repo Repository, err error) { switch ref.Schema { case "cs": repo = Store case "local": if localRepoPath == "" { return nil, errors.New("path to local repository not specified") } repo = &LocalRepository{Path: localRepoPath} default: return nil, fmt.Errorf("unknown schema for charm reference %q", ref) } return } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/dir_test.go��������������������������������������0000644�0000153�0000161�00000016417�12321735642�024134� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "archive/zip" "bytes" "fmt" "io/ioutil" "os" "path/filepath" "strings" "syscall" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type DirSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&DirSuite{}) func (s *DirSuite) TestReadDir(c *gc.C) { path := testing.Charms.DirPath("dummy") dir, err := charm.ReadDir(path) c.Assert(err, gc.IsNil) checkDummy(c, dir, path) } func (s *DirSuite) TestReadDirWithoutConfig(c *gc.C) { path := testing.Charms.DirPath("varnish") dir, err := charm.ReadDir(path) c.Assert(err, gc.IsNil) // A lacking config.yaml file still causes a proper // Config value to be returned. c.Assert(dir.Config().Options, gc.HasLen, 0) } func (s *DirSuite) TestBundleTo(c *gc.C) { baseDir := c.MkDir() charmDir := testing.Charms.ClonedDirPath(baseDir, "dummy") var haveSymlinks = true if err := os.Symlink("../target", filepath.Join(charmDir, "hooks/symlink")); err != nil { haveSymlinks = false } dir, err := charm.ReadDir(charmDir) c.Assert(err, gc.IsNil) path := filepath.Join(baseDir, "bundle.charm") file, err := os.Create(path) c.Assert(err, gc.IsNil) err = dir.BundleTo(file) file.Close() c.Assert(err, gc.IsNil) zipr, err := zip.OpenReader(path) c.Assert(err, gc.IsNil) defer zipr.Close() var metaf, instf, emptyf, revf, symf *zip.File for _, f := range zipr.File { c.Logf("Bundled file: %s", f.Name) switch f.Name { case "revision": revf = f case "metadata.yaml": metaf = f case "hooks/install": instf = f case "hooks/symlink": symf = f case "empty/": emptyf = f case "build/ignored": c.Errorf("bundle includes build/*: %s", f.Name) case ".ignored", ".dir/ignored": c.Errorf("bundle includes .* entries: %s", f.Name) } } c.Assert(revf, gc.NotNil) reader, err := revf.Open() c.Assert(err, gc.IsNil) data, err := ioutil.ReadAll(reader) reader.Close() c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "1") c.Assert(metaf, gc.NotNil) reader, err = metaf.Open() c.Assert(err, gc.IsNil) meta, err := charm.ReadMeta(reader) reader.Close() c.Assert(err, gc.IsNil) c.Assert(meta.Name, gc.Equals, "dummy") c.Assert(instf, gc.NotNil) // Despite it being 0751, we pack and unpack it as 0755. c.Assert(instf.Mode()&0777, gc.Equals, os.FileMode(0755)) if haveSymlinks { c.Assert(symf, gc.NotNil) c.Assert(symf.Mode()&0777, gc.Equals, os.FileMode(0777)) reader, err = symf.Open() c.Assert(err, gc.IsNil) data, err = ioutil.ReadAll(reader) reader.Close() c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "../target") } else { c.Assert(symf, gc.IsNil) } c.Assert(emptyf, gc.NotNil) c.Assert(emptyf.Mode()&os.ModeType, gc.Equals, os.ModeDir) // Despite it being 0750, we pack and unpack it as 0755. c.Assert(emptyf.Mode()&0777, gc.Equals, os.FileMode(0755)) } // Bug #864164: Must complain if charm hooks aren't executable func (s *DirSuite) TestBundleToWithNonExecutableHooks(c *gc.C) { hooks := []string{"install", "start", "config-changed", "upgrade-charm", "stop"} for _, relName := range []string{"foo", "bar", "self"} { for _, kind := range []string{"joined", "changed", "departed", "broken"} { hooks = append(hooks, relName+"-relation-"+kind) } } dir := testing.Charms.Dir("all-hooks") path := filepath.Join(c.MkDir(), "bundle.charm") file, err := os.Create(path) c.Assert(err, gc.IsNil) err = dir.BundleTo(file) file.Close() c.Assert(err, gc.IsNil) tlog := c.GetTestLog() for _, hook := range hooks { fullpath := filepath.Join(dir.Path, "hooks", hook) exp := fmt.Sprintf(`^(.|\n)*WARNING juju charm: making "%s" executable in charm(.|\n)*$`, fullpath) c.Assert(tlog, gc.Matches, exp, gc.Commentf("hook %q was not made executable", fullpath)) } // Expand it and check the hooks' permissions // (But do not use ExpandTo(), just use the raw zip) f, err := os.Open(path) c.Assert(err, gc.IsNil) defer f.Close() fi, err := f.Stat() c.Assert(err, gc.IsNil) size := fi.Size() zipr, err := zip.NewReader(f, size) c.Assert(err, gc.IsNil) allhooks := dir.Meta().Hooks() for _, zfile := range zipr.File { cleanName := filepath.Clean(zfile.Name) if strings.HasPrefix(cleanName, "hooks") { hookName := filepath.Base(cleanName) if _, ok := allhooks[hookName]; ok { perms := zfile.Mode() c.Assert(perms&0100 != 0, gc.Equals, true, gc.Commentf("hook %q is not executable", hookName)) } } } } func (s *DirSuite) TestBundleToWithBadType(c *gc.C) { charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") badFile := filepath.Join(charmDir, "hooks", "badfile") // Symlink targeting a path outside of the charm. err := os.Symlink("../../target", badFile) c.Assert(err, gc.IsNil) dir, err := charm.ReadDir(charmDir) c.Assert(err, gc.IsNil) err = dir.BundleTo(&bytes.Buffer{}) c.Assert(err, gc.ErrorMatches, `symlink "hooks/badfile" links out of charm: "../../target"`) // Symlink targeting an absolute path. os.Remove(badFile) err = os.Symlink("/target", badFile) c.Assert(err, gc.IsNil) dir, err = charm.ReadDir(charmDir) c.Assert(err, gc.IsNil) err = dir.BundleTo(&bytes.Buffer{}) c.Assert(err, gc.ErrorMatches, `symlink "hooks/badfile" is absolute: "/target"`) // Can't bundle special files either. os.Remove(badFile) err = syscall.Mkfifo(badFile, 0644) c.Assert(err, gc.IsNil) dir, err = charm.ReadDir(charmDir) c.Assert(err, gc.IsNil) err = dir.BundleTo(&bytes.Buffer{}) c.Assert(err, gc.ErrorMatches, `file is a named pipe: "hooks/badfile"`) } func (s *DirSuite) TestDirRevisionFile(c *gc.C) { charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") revPath := filepath.Join(charmDir, "revision") // Missing revision file err := os.Remove(revPath) c.Assert(err, gc.IsNil) dir, err := charm.ReadDir(charmDir) c.Assert(err, gc.IsNil) c.Assert(dir.Revision(), gc.Equals, 0) // Missing revision file with old revision in metadata file, err := os.OpenFile(filepath.Join(charmDir, "metadata.yaml"), os.O_WRONLY|os.O_APPEND, 0) c.Assert(err, gc.IsNil) _, err = file.Write([]byte("\nrevision: 1234\n")) c.Assert(err, gc.IsNil) dir, err = charm.ReadDir(charmDir) c.Assert(err, gc.IsNil) c.Assert(dir.Revision(), gc.Equals, 1234) // Revision file with bad content err = ioutil.WriteFile(revPath, []byte("garbage"), 0666) c.Assert(err, gc.IsNil) dir, err = charm.ReadDir(charmDir) c.Assert(err, gc.ErrorMatches, "invalid revision file") c.Assert(dir, gc.IsNil) } func (s *DirSuite) TestDirSetRevision(c *gc.C) { dir := testing.Charms.ClonedDir(c.MkDir(), "dummy") c.Assert(dir.Revision(), gc.Equals, 1) dir.SetRevision(42) c.Assert(dir.Revision(), gc.Equals, 42) var b bytes.Buffer err := dir.BundleTo(&b) c.Assert(err, gc.IsNil) bundle, err := charm.ReadBundleBytes(b.Bytes()) c.Assert(bundle.Revision(), gc.Equals, 42) } func (s *DirSuite) TestDirSetDiskRevision(c *gc.C) { charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy") dir, err := charm.ReadDir(charmDir) c.Assert(err, gc.IsNil) c.Assert(dir.Revision(), gc.Equals, 1) dir.SetDiskRevision(42) c.Assert(dir.Revision(), gc.Equals, 42) dir, err = charm.ReadDir(charmDir) c.Assert(err, gc.IsNil) c.Assert(dir.Revision(), gc.Equals, 42) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/charm_test.go������������������������������������0000644�0000153�0000161�00000004644�12321735642�024447� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "bytes" "io" "io/ioutil" stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/testing" ) func Test(t *stdtesting.T) { gc.TestingT(t) } type CharmSuite struct{} var _ = gc.Suite(&CharmSuite{}) func (s *CharmSuite) TestRead(c *gc.C) { bPath := testing.Charms.BundlePath(c.MkDir(), "dummy") ch, err := charm.Read(bPath) c.Assert(err, gc.IsNil) c.Assert(ch.Meta().Name, gc.Equals, "dummy") dPath := testing.Charms.DirPath("dummy") ch, err = charm.Read(dPath) c.Assert(err, gc.IsNil) c.Assert(ch.Meta().Name, gc.Equals, "dummy") } var inferRepoTests = []struct { url string path string }{ {"cs:precise/wordpress", ""}, {"local:oneiric/wordpress", "/some/path"}, } func (s *CharmSuite) TestInferRepository(c *gc.C) { for i, t := range inferRepoTests { c.Logf("test %d", i) curl, err := charm.InferURL(t.url, "precise") c.Assert(err, gc.IsNil) repo, err := charm.InferRepository(curl.Reference, "/some/path") c.Assert(err, gc.IsNil) switch repo := repo.(type) { case *charm.LocalRepository: c.Assert(repo.Path, gc.Equals, t.path) default: c.Assert(repo, gc.Equals, charm.Store) } } curl, err := charm.InferURL("local:whatever", "precise") c.Assert(err, gc.IsNil) _, err = charm.InferRepository(curl.Reference, "") c.Assert(err, gc.ErrorMatches, "path to local repository not specified") curl.Schema = "foo" _, err = charm.InferRepository(curl.Reference, "") c.Assert(err, gc.ErrorMatches, "unknown schema for charm reference.*") } func checkDummy(c *gc.C, f charm.Charm, path string) { c.Assert(f.Revision(), gc.Equals, 1) c.Assert(f.Meta().Name, gc.Equals, "dummy") c.Assert(f.Config().Options["title"].Default, gc.Equals, "My Title") switch f := f.(type) { case *charm.Bundle: c.Assert(f.Path, gc.Equals, path) case *charm.Dir: c.Assert(f.Path, gc.Equals, path) } } type YamlHacker map[interface{}]interface{} func ReadYaml(r io.Reader) YamlHacker { data, err := ioutil.ReadAll(r) if err != nil { panic(err) } m := make(map[interface{}]interface{}) err = goyaml.Unmarshal(data, m) if err != nil { panic(err) } return YamlHacker(m) } func (yh YamlHacker) Reader() io.Reader { data, err := goyaml.Marshal(yh) if err != nil { panic(err) } return bytes.NewBuffer(data) } ��������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/testing/�����������������������������������������0000755�0000153�0000161�00000000000�12321735642�023434� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/testing/mockstore.go�����������������������������0000644�0000153�0000161�00000013024�12321735642�025771� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "bytes" "encoding/json" "fmt" "io" "net" "net/http" "os" "strconv" "strings" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) var logger = loggo.GetLogger("juju.charm.testing.mockstore") // MockStore provides a mock charm store implementation useful when testing. type MockStore struct { mux *http.ServeMux listener net.Listener bundleBytes []byte bundleSha256 string Downloads []*charm.URL DownloadsNoStats []*charm.URL Authorizations []string Metadata []string InfoRequestCount int InfoRequestCountNoStats int DefaultSeries string charms map[string]int } // NewMockStore creates a mock charm store containing the specified charms. func NewMockStore(c *gc.C, charms map[string]int) *MockStore { s := &MockStore{charms: charms, DefaultSeries: "precise"} f, err := os.Open(testing.Charms.BundlePath(c.MkDir(), "dummy")) c.Assert(err, gc.IsNil) defer f.Close() buf := &bytes.Buffer{} s.bundleSha256, _, err = utils.ReadSHA256(io.TeeReader(f, buf)) c.Assert(err, gc.IsNil) s.bundleBytes = buf.Bytes() c.Assert(err, gc.IsNil) s.mux = http.NewServeMux() s.mux.HandleFunc("/charm-info", s.serveInfo) s.mux.HandleFunc("/charm-event", s.serveEvent) s.mux.HandleFunc("/charm/", s.serveCharm) lis, err := net.Listen("tcp", "127.0.0.1:0") c.Assert(err, gc.IsNil) s.listener = lis go http.Serve(s.listener, s) return s } // Close closes the mock store's socket. func (s *MockStore) Close() { s.listener.Close() } // Address returns the URL used to make requests to the mock store. func (s *MockStore) Address() string { return "http://" + s.listener.Addr().String() } // UpdateStoreRevision sets the revision of the specified charm to rev. func (s *MockStore) UpdateStoreRevision(ch string, rev int) { s.charms[ch] = rev } // ServeHTTP implements http.ServeHTTP func (s *MockStore) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.mux.ServeHTTP(w, r) } func (s *MockStore) serveInfo(w http.ResponseWriter, r *http.Request) { if metadata := r.Header.Get("Juju-Metadata"); metadata != "" { s.Metadata = append(s.Metadata, metadata) logger.Infof("Juju metadata: " + metadata) } r.ParseForm() if r.Form.Get("stats") == "0" { s.InfoRequestCountNoStats += 1 } else { s.InfoRequestCount += 1 } response := map[string]*charm.InfoResponse{} for _, url := range r.Form["charms"] { cr := &charm.InfoResponse{} response[url] = cr charmURL, err := charm.ParseURL(url) if err == charm.ErrUnresolvedUrl { ref, _, err := charm.ParseReference(url) if err != nil { panic(err) } if s.DefaultSeries == "" { panic(fmt.Errorf("mock store lacks a default series cannot resolve charm URL: %q", url)) } charmURL = &charm.URL{Reference: ref, Series: s.DefaultSeries} } switch charmURL.Name { case "borken": cr.Errors = append(cr.Errors, "badness") case "terracotta": cr.Errors = append(cr.Errors, "cannot get revision") case "unwise": cr.Warnings = append(cr.Warnings, "foolishness") fallthrough default: if rev, ok := s.charms[charmURL.WithRevision(-1).String()]; ok { if charmURL.Revision == -1 { cr.Revision = rev } else { cr.Revision = charmURL.Revision } cr.Sha256 = s.bundleSha256 cr.CanonicalURL = charmURL.String() } else { cr.Errors = append(cr.Errors, "entry not found") } } } data, err := json.Marshal(response) if err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") _, err = w.Write(data) if err != nil { panic(err) } } func (s *MockStore) serveEvent(w http.ResponseWriter, r *http.Request) { r.ParseForm() response := map[string]*charm.EventResponse{} for _, url := range r.Form["charms"] { digest := "" if i := strings.Index(url, "@"); i >= 0 { digest = url[i+1:] url = url[:i] } er := &charm.EventResponse{} response[url] = er if digest != "" && digest != "the-digest" { er.Kind = "not-found" er.Errors = []string{"entry not found"} continue } charmURL := charm.MustParseURL(url) switch charmURL.Name { case "borken": er.Kind = "publish-error" er.Errors = append(er.Errors, "badness") case "unwise": er.Warnings = append(er.Warnings, "foolishness") fallthrough default: if rev, ok := s.charms[charmURL.WithRevision(-1).String()]; ok { er.Kind = "published" er.Revision = rev er.Digest = "the-digest" } else { er.Kind = "not-found" er.Errors = []string{"entry not found"} } } } data, err := json.Marshal(response) if err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") _, err = w.Write(data) if err != nil { panic(err) } } func (s *MockStore) serveCharm(w http.ResponseWriter, r *http.Request) { charmURL := charm.MustParseURL("cs:" + r.URL.Path[len("/charm/"):]) r.ParseForm() if r.Form.Get("stats") == "0" { s.DownloadsNoStats = append(s.DownloadsNoStats, charmURL) } else { s.Downloads = append(s.Downloads, charmURL) } if auth := r.Header.Get("Authorization"); auth != "" { s.Authorizations = append(s.Authorizations, auth) } w.Header().Set("Connection", "close") w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Length", strconv.Itoa(len(s.bundleBytes))) _, err := w.Write(s.bundleBytes) if err != nil { panic(err) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/charm/repo.go������������������������������������������0000644�0000153�0000161�00000035160�12321735776�023270� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "encoding/json" "fmt" "io" "io/ioutil" "net" "net/http" "net/url" "os" "path/filepath" "strings" "launchpad.net/juju-core/log" "launchpad.net/juju-core/utils" ) // CacheDir stores the charm cache directory path. var CacheDir string // InfoResponse is sent by the charm store in response to charm-info requests. type InfoResponse struct { CanonicalURL string `json:"canonical-url,omitempty"` Revision int `json:"revision"` // Zero is valid. Can't omitempty. Sha256 string `json:"sha256,omitempty"` Digest string `json:"digest,omitempty"` Errors []string `json:"errors,omitempty"` Warnings []string `json:"warnings,omitempty"` } // EventResponse is sent by the charm store in response to charm-event requests. type EventResponse struct { Kind string `json:"kind"` Revision int `json:"revision"` // Zero is valid. Can't omitempty. Digest string `json:"digest,omitempty"` Errors []string `json:"errors,omitempty"` Warnings []string `json:"warnings,omitempty"` Time string `json:"time,omitempty"` } // CharmRevision holds the revision number of a charm and any error // encountered in retrieving it. type CharmRevision struct { Revision int Sha256 string Err error } // Repository represents a collection of charms. type Repository interface { Get(curl *URL) (Charm, error) Latest(curls ...*URL) ([]CharmRevision, error) Resolve(ref Reference) (*URL, error) } // Latest returns the latest revision of the charm referenced by curl, regardless // of the revision set on each curl. // This is a helper which calls the bulk method and unpacks a single result. func Latest(repo Repository, curl *URL) (int, error) { revs, err := repo.Latest(curl) if err != nil { return 0, err } if len(revs) != 1 { return 0, fmt.Errorf("expected 1 result, got %d", len(revs)) } rev := revs[0] if rev.Err != nil { return 0, rev.Err } return rev.Revision, nil } // NotFoundError represents an error indicating that the requested data wasn't found. type NotFoundError struct { msg string } func (e *NotFoundError) Error() string { return e.msg } // CharmStore is a Repository that provides access to the public juju charm store. type CharmStore struct { BaseURL string authAttrs string // a list of attr=value pairs, comma separated jujuAttrs string // a list of attr=value pairs, comma separated testMode bool } var _ Repository = (*CharmStore)(nil) var Store = &CharmStore{BaseURL: "https://store.juju.ubuntu.com"} // WithAuthAttrs return a Repository with the authentication token list set. // authAttrs is a list of attr=value pairs. func (s *CharmStore) WithAuthAttrs(authAttrs string) Repository { authCS := *s authCS.authAttrs = authAttrs return &authCS } // WithTestMode returns a Repository where testMode is set to value passed to // this method. func (s *CharmStore) WithTestMode(testMode bool) Repository { newRepo := *s newRepo.testMode = testMode return &newRepo } // WithJujuAttrs returns a Repository with the Juju metadata attributes set. // jujuAttrs is a list of attr=value pairs. func (s *CharmStore) WithJujuAttrs(jujuAttrs string) Repository { jujuCS := *s jujuCS.jujuAttrs = jujuAttrs return &jujuCS } // Perform an http get, adding custom auth header if necessary. func (s *CharmStore) get(url string) (resp *http.Response, err error) { req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } if s.authAttrs != "" { // To comply with RFC 2617, we send the authentication data in // the Authorization header with a custom auth scheme // and the authentication attributes. req.Header.Add("Authorization", "charmstore "+s.authAttrs) } if s.jujuAttrs != "" { // The use of "X-" to prefix custom header values is deprecated. req.Header.Add("Juju-Metadata", s.jujuAttrs) } return http.DefaultClient.Do(req) } // Resolve canonicalizes charm URLs, resolving references and implied series. func (s *CharmStore) Resolve(ref Reference) (*URL, error) { infos, err := s.Info(ref) if err != nil { return nil, err } if len(infos) == 0 { return nil, fmt.Errorf("missing response when resolving charm URL: %q", ref) } if infos[0].CanonicalURL == "" { return nil, fmt.Errorf("cannot resolve charm URL: %q", ref) } curl, err := ParseURL(infos[0].CanonicalURL) if err != nil { return nil, err } return curl, nil } // Info returns details for all the specified charms in the charm store. func (s *CharmStore) Info(curls ...Location) ([]*InfoResponse, error) { baseURL := s.BaseURL + "/charm-info?" queryParams := make([]string, len(curls), len(curls)+1) for i, curl := range curls { queryParams[i] = "charms=" + url.QueryEscape(curl.String()) } if s.testMode { queryParams = append(queryParams, "stats=0") } resp, err := s.get(baseURL + strings.Join(queryParams, "&")) if err != nil { if url_error, ok := err.(*url.Error); ok { switch url_error.Err.(type) { case *net.DNSError, *net.OpError: return nil, fmt.Errorf("Cannot access the charm store. Are you connected to the internet? Error details: %v", err) } } return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } infos := make(map[string]*InfoResponse) if err = json.Unmarshal(body, &infos); err != nil { return nil, err } result := make([]*InfoResponse, len(curls)) for i, curl := range curls { key := curl.String() info, found := infos[key] if !found { return nil, fmt.Errorf("charm store returned response without charm %q", key) } if len(info.Errors) == 1 && info.Errors[0] == "entry not found" { info.Errors[0] = fmt.Sprintf("charm not found: %s", curl) } result[i] = info } return result, nil } // Event returns details for a charm event in the charm store. // // If digest is empty, the latest event is returned. func (s *CharmStore) Event(curl *URL, digest string) (*EventResponse, error) { key := curl.String() query := key if digest != "" { query += "@" + digest } resp, err := s.get(s.BaseURL + "/charm-event?charms=" + url.QueryEscape(query)) if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } events := make(map[string]*EventResponse) if err = json.Unmarshal(body, &events); err != nil { return nil, err } event, found := events[key] if !found { return nil, fmt.Errorf("charm store returned response without charm %q", key) } if len(event.Errors) == 1 && event.Errors[0] == "entry not found" { if digest == "" { return nil, &NotFoundError{fmt.Sprintf("charm event not found for %q", curl)} } else { return nil, &NotFoundError{fmt.Sprintf("charm event not found for %q with digest %q", curl, digest)} } } return event, nil } // revisions returns the revisions of the charms referenced by curls. func (s *CharmStore) revisions(curls ...Location) (revisions []CharmRevision, err error) { infos, err := s.Info(curls...) if err != nil { return nil, err } revisions = make([]CharmRevision, len(infos)) for i, info := range infos { for _, w := range info.Warnings { log.Warningf("charm store reports for %q: %s", curls[i], w) } if info.Errors == nil { revisions[i].Revision = info.Revision revisions[i].Sha256 = info.Sha256 } else { // If a charm is not found, we are more concise with the error message. if len(info.Errors) == 1 && strings.HasPrefix(info.Errors[0], "charm not found") { revisions[i].Err = fmt.Errorf(info.Errors[0]) } else { revisions[i].Err = fmt.Errorf("charm info errors for %q: %s", curls[i], strings.Join(info.Errors, "; ")) } } } return revisions, nil } // Latest returns the latest revision of the charms referenced by curls, regardless // of the revision set on each curl. func (s *CharmStore) Latest(curls ...*URL) ([]CharmRevision, error) { baseCurls := make([]Location, len(curls)) for i, curl := range curls { baseCurls[i] = curl.WithRevision(-1) } return s.revisions(baseCurls...) } // BranchLocation returns the location for the branch holding the charm at curl. func (s *CharmStore) BranchLocation(curl *URL) string { if curl.User != "" { return fmt.Sprintf("lp:~%s/charms/%s/%s/trunk", curl.User, curl.Series, curl.Name) } return fmt.Sprintf("lp:charms/%s/%s", curl.Series, curl.Name) } var branchPrefixes = []string{ "lp:", "bzr+ssh://bazaar.launchpad.net/+branch/", "bzr+ssh://bazaar.launchpad.net/", "http://launchpad.net/+branch/", "http://launchpad.net/", "https://launchpad.net/+branch/", "https://launchpad.net/", "http://code.launchpad.net/+branch/", "http://code.launchpad.net/", "https://code.launchpad.net/+branch/", "https://code.launchpad.net/", } // CharmURL returns the charm URL for the branch at location. func (s *CharmStore) CharmURL(location string) (*URL, error) { var l string if len(location) > 0 && location[0] == '~' { l = location } else { for _, prefix := range branchPrefixes { if strings.HasPrefix(location, prefix) { l = location[len(prefix):] break } } } if l != "" { for len(l) > 0 && l[len(l)-1] == '/' { l = l[:len(l)-1] } u := strings.Split(l, "/") if len(u) == 3 && u[0] == "charms" { return ParseURL(fmt.Sprintf("cs:%s/%s", u[1], u[2])) } if len(u) == 4 && u[0] == "charms" && u[3] == "trunk" { return ParseURL(fmt.Sprintf("cs:%s/%s", u[1], u[2])) } if len(u) == 5 && u[1] == "charms" && u[4] == "trunk" && len(u[0]) > 0 && u[0][0] == '~' { return ParseURL(fmt.Sprintf("cs:%s/%s/%s", u[0], u[2], u[3])) } } return nil, fmt.Errorf("unknown branch location: %q", location) } // verify returns an error unless a file exists at path with a hex-encoded // SHA256 matching digest. func verify(path, digest string) error { hash, _, err := utils.ReadFileSHA256(path) if err != nil { return err } if hash != digest { return fmt.Errorf("bad SHA256 of %q", path) } return nil } // Get returns the charm referenced by curl. // CacheDir must have been set, otherwise Get will panic. func (s *CharmStore) Get(curl *URL) (Charm, error) { // The cache location must have been previously set. if CacheDir == "" { panic("charm cache directory path is empty") } if err := os.MkdirAll(CacheDir, os.FileMode(0755)); err != nil { return nil, err } revInfo, err := s.revisions(curl) if err != nil { return nil, err } if len(revInfo) != 1 { return nil, fmt.Errorf("expected 1 result, got %d", len(revInfo)) } if revInfo[0].Err != nil { return nil, revInfo[0].Err } rev, digest := revInfo[0].Revision, revInfo[0].Sha256 if curl.Revision == -1 { curl = curl.WithRevision(rev) } else if curl.Revision != rev { return nil, fmt.Errorf("store returned charm with wrong revision %d for %q", rev, curl.String()) } path := filepath.Join(CacheDir, Quote(curl.String())+".charm") if verify(path, digest) != nil { store_url := s.BaseURL + "/charm/" + url.QueryEscape(curl.Path()) if s.testMode { store_url = store_url + "?stats=0" } resp, err := s.get(store_url) if err != nil { return nil, err } defer resp.Body.Close() f, err := ioutil.TempFile(CacheDir, "charm-download") if err != nil { return nil, err } dlPath := f.Name() _, err = io.Copy(f, resp.Body) if cerr := f.Close(); err == nil { err = cerr } if err != nil { os.Remove(dlPath) return nil, err } if err := utils.ReplaceFile(dlPath, path); err != nil { return nil, err } } if err := verify(path, digest); err != nil { return nil, err } return ReadBundle(path) } // LocalRepository represents a local directory containing subdirectories // named after an Ubuntu series, each of which contains charms targeted for // that series. For example: // // /path/to/repository/oneiric/mongodb/ // /path/to/repository/precise/mongodb.charm // /path/to/repository/precise/wordpress/ type LocalRepository struct { Path string defaultSeries string } var _ Repository = (*LocalRepository)(nil) // WithDefaultSeries returns a Repository with the default series set. func (r *LocalRepository) WithDefaultSeries(defaultSeries string) Repository { localRepo := *r localRepo.defaultSeries = defaultSeries return &localRepo } // Resolve canonicalizes charm URLs, resolving references and implied series. func (r *LocalRepository) Resolve(ref Reference) (*URL, error) { if r.defaultSeries == "" { return nil, fmt.Errorf("cannot resolve, repository has no default series: %q", ref) } return &URL{Reference: ref, Series: r.defaultSeries}, nil } // Latest returns the latest revision of the charm referenced by curl, regardless // of the revision set on curl itself. func (r *LocalRepository) Latest(curls ...*URL) ([]CharmRevision, error) { result := make([]CharmRevision, len(curls)) for i, curl := range curls { ch, err := r.Get(curl.WithRevision(-1)) if err == nil { result[i].Revision = ch.Revision() } else { result[i].Err = err } } return result, nil } func repoNotFound(path string) error { return &NotFoundError{fmt.Sprintf("no repository found at %q", path)} } func charmNotFound(curl *URL, repoPath string) error { return &NotFoundError{fmt.Sprintf("charm not found in %q: %s", repoPath, curl)} } func mightBeCharm(info os.FileInfo) bool { if info.IsDir() { return !strings.HasPrefix(info.Name(), ".") } return strings.HasSuffix(info.Name(), ".charm") } // Get returns a charm matching curl, if one exists. If curl has a revision of // -1, it returns the latest charm that matches curl. If multiple candidates // satisfy the foregoing, the first one encountered will be returned. func (r *LocalRepository) Get(curl *URL) (Charm, error) { if curl.Schema != "local" { return nil, fmt.Errorf("local repository got URL with non-local schema: %q", curl) } info, err := os.Stat(r.Path) if err != nil { if os.IsNotExist(err) { err = repoNotFound(r.Path) } return nil, err } if !info.IsDir() { return nil, repoNotFound(r.Path) } path := filepath.Join(r.Path, curl.Series) infos, err := ioutil.ReadDir(path) if err != nil { return nil, charmNotFound(curl, r.Path) } var latest Charm for _, info := range infos { chPath := filepath.Join(path, info.Name()) if info.Mode()&os.ModeSymlink != 0 { var err error if info, err = os.Stat(chPath); err != nil { return nil, err } } if !mightBeCharm(info) { continue } if ch, err := Read(chPath); err != nil { log.Warningf("failed to load charm at %q: %s", chPath, err) } else if ch.Meta().Name == curl.Name { if ch.Revision() == curl.Revision { return ch, nil } if latest == nil || ch.Revision() > latest.Revision() { latest = ch } } } if curl.Revision == -1 && latest != nil { return latest, nil } return nil, charmNotFound(curl, r.Path) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/constraints/�������������������������������������������0000755�0000153�0000161�00000000000�12321735642�023234� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/constraints/constraints.go�����������������������������0000644�0000153�0000161�00000022637�12321735642�026144� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package constraints import ( "fmt" "math" "strconv" "strings" "github.com/errgo/errgo" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/arch" ) // Value describes a user's requirements of the hardware on which units // of a service will run. Constraints are used to choose an existing machine // onto which a unit will be deployed, or to provision a new machine if no // existing one satisfies the requirements. type Value struct { // Arch, if not nil or empty, indicates that a machine must run the named // architecture. Arch *string `json:"arch,omitempty" yaml:"arch,omitempty"` // Container, if not nil, indicates that a machine must be the specified container type. Container *instance.ContainerType `json:"container,omitempty" yaml:"container,omitempty"` // CpuCores, if not nil, indicates that a machine must have at least that // number of effective cores available. CpuCores *uint64 `json:"cpu-cores,omitempty" yaml:"cpu-cores,omitempty"` // CpuPower, if not nil, indicates that a machine must have at least that // amount of CPU power available, where 100 CpuPower is considered to be // equivalent to 1 Amazon ECU (or, roughly, a single 2007-era Xeon). CpuPower *uint64 `json:"cpu-power,omitempty" yaml:"cpu-power,omitempty"` // Mem, if not nil, indicates that a machine must have at least that many // megabytes of RAM. Mem *uint64 `json:"mem,omitempty" yaml:"mem,omitempty"` // RootDisk, if not nil, indicates that a machine must have at least // that many megabytes of disk space available in the root disk. In // providers where the root disk is configurable at instance startup // time, an instance with the specified amount of disk space in the OS // disk might be requested. RootDisk *uint64 `json:"root-disk,omitempty" yaml:"root-disk,omitempty"` // Tags, if not nil, indicates tags that the machine must have applied to it. // An empty list is treated the same as a nil (unspecified) list, except an // empty list will override any default tags, where a nil list will not. Tags *[]string `json:"tags,omitempty" yaml:"tags,omitempty"` } // IsEmpty returns if the given constraints value has no constraints set func IsEmpty(v *Value) bool { return v == nil || v.Arch == nil && v.Container == nil && v.CpuCores == nil && v.CpuPower == nil && v.Mem == nil && v.RootDisk == nil && v.Tags == nil } // String expresses a constraints.Value in the language in which it was specified. func (v Value) String() string { var strs []string if v.Arch != nil { strs = append(strs, "arch="+*v.Arch) } if v.Container != nil { strs = append(strs, "container="+string(*v.Container)) } if v.CpuCores != nil { strs = append(strs, "cpu-cores="+uintStr(*v.CpuCores)) } if v.CpuPower != nil { strs = append(strs, "cpu-power="+uintStr(*v.CpuPower)) } if v.Mem != nil { s := uintStr(*v.Mem) if s != "" { s += "M" } strs = append(strs, "mem="+s) } if v.RootDisk != nil { s := uintStr(*v.RootDisk) if s != "" { s += "M" } strs = append(strs, "root-disk="+s) } if v.Tags != nil { s := strings.Join(*v.Tags, ",") strs = append(strs, "tags="+s) } return strings.Join(strs, " ") } // WithFallbacks returns a copy of v with nil values taken from v0. func (v Value) WithFallbacks(v0 Value) Value { v1 := v0 if v.Arch != nil { v1.Arch = v.Arch } if v.Container != nil { v1.Container = v.Container } if v.CpuCores != nil { v1.CpuCores = v.CpuCores } if v.CpuPower != nil { v1.CpuPower = v.CpuPower } if v.Mem != nil { v1.Mem = v.Mem } if v.RootDisk != nil { v1.RootDisk = v.RootDisk } if v.Tags != nil { v1.Tags = v.Tags } return v1 } func uintStr(i uint64) string { if i == 0 { return "" } return fmt.Sprintf("%d", i) } // Parse constructs a constraints.Value from the supplied arguments, // each of which must contain only spaces and name=value pairs. If any // name is specified more than once, an error is returned. func Parse(args ...string) (Value, error) { cons := Value{} for _, arg := range args { raws := strings.Split(strings.TrimSpace(arg), " ") for _, raw := range raws { if raw == "" { continue } if err := cons.setRaw(raw); err != nil { return Value{}, err } } } return cons, nil } // MustParse constructs a constraints.Value from the supplied arguments, // as Parse, but panics on failure. func MustParse(args ...string) Value { v, err := Parse(args...) if err != nil { panic(err) } return v } // Constraints implements gnuflag.Value for a Constraints. type ConstraintsValue struct { Target *Value } func (v ConstraintsValue) Set(s string) error { cons, err := Parse(s) if err != nil { return err } *v.Target = cons return nil } func (v ConstraintsValue) String() string { return v.Target.String() } // setRaw interprets a name=value string and sets the supplied value. func (v *Value) setRaw(raw string) error { eq := strings.Index(raw, "=") if eq <= 0 { return fmt.Errorf("malformed constraint %q", raw) } name, str := raw[:eq], raw[eq+1:] var err error switch name { case "arch": err = v.setArch(str) case "container": err = v.setContainer(str) case "cpu-cores": err = v.setCpuCores(str) case "cpu-power": err = v.setCpuPower(str) case "mem": err = v.setMem(str) case "root-disk": err = v.setRootDisk(str) case "tags": err = v.setTags(str) default: return fmt.Errorf("unknown constraint %q", name) } if err != nil { return errgo.Annotatef(err, "bad %q constraint", name) } return nil } // SetYAML is required to unmarshall a constraints.Value object // to ensure the container attribute is correctly handled when it is empty. // Because ContainerType is an alias for string, Go's reflect logic used in the // YAML decode determines that *string and *ContainerType are not assignable so // the container value of "" in the YAML is ignored. func (v *Value) SetYAML(tag string, value interface{}) bool { values, ok := value.(map[interface{}]interface{}) if !ok { return false } for k, val := range values { vstr := fmt.Sprintf("%v", val) var err error switch k { case "arch": v.Arch = &vstr case "container": ctype := instance.ContainerType(vstr) v.Container = &ctype case "cpu-cores": v.CpuCores, err = parseUint64(vstr) case "cpu-power": v.CpuPower, err = parseUint64(vstr) case "mem": v.Mem, err = parseUint64(vstr) case "root-disk": v.RootDisk, err = parseUint64(vstr) case "tags": v.Tags, err = parseYamlTags(val) default: return false } if err != nil { return false } } return true } func (v *Value) setContainer(str string) error { if v.Container != nil { return fmt.Errorf("already set") } if str == "" { ctype := instance.ContainerType("") v.Container = &ctype } else { ctype, err := instance.ParseContainerTypeOrNone(str) if err != nil { return err } v.Container = &ctype } return nil } // HasContainer returns true if the constraints.Value specifies a container. func (v *Value) HasContainer() bool { return v.Container != nil && *v.Container != "" && *v.Container != instance.NONE } func (v *Value) setArch(str string) error { if v.Arch != nil { return fmt.Errorf("already set") } if str != "" && !arch.IsSupportedArch(str) { return fmt.Errorf("%q not recognized", str) } v.Arch = &str return nil } func (v *Value) setCpuCores(str string) (err error) { if v.CpuCores != nil { return fmt.Errorf("already set") } v.CpuCores, err = parseUint64(str) return } func (v *Value) setCpuPower(str string) (err error) { if v.CpuPower != nil { return fmt.Errorf("already set") } v.CpuPower, err = parseUint64(str) return } func (v *Value) setMem(str string) (err error) { if v.Mem != nil { return fmt.Errorf("already set") } v.Mem, err = parseSize(str) return } func (v *Value) setRootDisk(str string) (err error) { if v.RootDisk != nil { return fmt.Errorf("already set") } v.RootDisk, err = parseSize(str) return } func (v *Value) setTags(str string) error { if v.Tags != nil { return fmt.Errorf("already set") } v.Tags = parseTags(str) return nil } func parseUint64(str string) (*uint64, error) { var value uint64 if str != "" { if val, err := strconv.ParseUint(str, 10, 64); err != nil { return nil, fmt.Errorf("must be a non-negative integer") } else { value = uint64(val) } } return &value, nil } func parseSize(str string) (*uint64, error) { var value uint64 if str != "" { mult := 1.0 if m, ok := mbSuffixes[str[len(str)-1:]]; ok { str = str[:len(str)-1] mult = m } val, err := strconv.ParseFloat(str, 64) if err != nil || val < 0 { return nil, fmt.Errorf("must be a non-negative float with optional M/G/T/P suffix") } val *= mult value = uint64(math.Ceil(val)) } return &value, nil } // parseTags returns the tags in the value s. We expect the tags to be comma delimited strings. func parseTags(s string) *[]string { if s == "" { return &[]string{} } t := strings.Split(s, ",") return &t } func parseYamlTags(val interface{}) (*[]string, error) { ifcs, ok := val.([]interface{}) if !ok { return nil, fmt.Errorf("unexpected type passed to tags: %T", val) } tags := make([]string, len(ifcs)) for n, ifc := range ifcs { s, ok := ifc.(string) if !ok { return nil, fmt.Errorf("unexpected type passed as a tag: %T", ifc) } tags[n] = s } return &tags, nil } var mbSuffixes = map[string]float64{ "M": 1, "G": 1024, "T": 1024 * 1024, "P": 1024 * 1024 * 1024, } �������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/constraints/constraints_test.go������������������������0000644�0000153�0000161�00000037516�12321735642�027205� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package constraints_test import ( "encoding/json" "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" ) func TestPackage(t *testing.T) { gc.TestingT(t) } type ConstraintsSuite struct{} var _ = gc.Suite(&ConstraintsSuite{}) var parseConstraintsTests = []struct { summary string args []string err string }{ // Simple errors. { summary: "nothing at all", }, { summary: "empty", args: []string{" "}, }, { summary: "complete nonsense", args: []string{"cheese"}, err: `malformed constraint "cheese"`, }, { summary: "missing name", args: []string{"=cheese"}, err: `malformed constraint "=cheese"`, }, { summary: "unknown constraint", args: []string{"cheese=edam"}, err: `unknown constraint "cheese"`, }, // "container" in detail. { summary: "set container empty", args: []string{"container="}, }, { summary: "set container to none", args: []string{"container=none"}, }, { summary: "set container lxc", args: []string{"container=lxc"}, }, { summary: "set nonsense container", args: []string{"container=foo"}, err: `bad "container" constraint: invalid container type "foo"`, }, { summary: "double set container together", args: []string{"container=lxc container=lxc"}, err: `bad "container" constraint: already set`, }, { summary: "double set container separately", args: []string{"container=lxc", "container="}, err: `bad "container" constraint: already set`, }, // "arch" in detail. { summary: "set arch empty", args: []string{"arch="}, }, { summary: "set arch amd64", args: []string{"arch=amd64"}, }, { summary: "set arch i386", args: []string{"arch=i386"}, }, { summary: "set arch armhf", args: []string{"arch=armhf"}, }, { summary: "set nonsense arch 1", args: []string{"arch=cheese"}, err: `bad "arch" constraint: "cheese" not recognized`, }, { summary: "set nonsense arch 2", args: []string{"arch=123.45"}, err: `bad "arch" constraint: "123.45" not recognized`, }, { summary: "double set arch together", args: []string{"arch=amd64 arch=amd64"}, err: `bad "arch" constraint: already set`, }, { summary: "double set arch separately", args: []string{"arch=armhf", "arch="}, err: `bad "arch" constraint: already set`, }, // "cpu-cores" in detail. { summary: "set cpu-cores empty", args: []string{"cpu-cores="}, }, { summary: "set cpu-cores zero", args: []string{"cpu-cores=0"}, }, { summary: "set cpu-cores", args: []string{"cpu-cores=4"}, }, { summary: "set nonsense cpu-cores 1", args: []string{"cpu-cores=cheese"}, err: `bad "cpu-cores" constraint: must be a non-negative integer`, }, { summary: "set nonsense cpu-cores 2", args: []string{"cpu-cores=-1"}, err: `bad "cpu-cores" constraint: must be a non-negative integer`, }, { summary: "set nonsense cpu-cores 3", args: []string{"cpu-cores=123.45"}, err: `bad "cpu-cores" constraint: must be a non-negative integer`, }, { summary: "double set cpu-cores together", args: []string{"cpu-cores=128 cpu-cores=1"}, err: `bad "cpu-cores" constraint: already set`, }, { summary: "double set cpu-cores separately", args: []string{"cpu-cores=128", "cpu-cores=1"}, err: `bad "cpu-cores" constraint: already set`, }, // "cpu-power" in detail. { summary: "set cpu-power empty", args: []string{"cpu-power="}, }, { summary: "set cpu-power zero", args: []string{"cpu-power=0"}, }, { summary: "set cpu-power", args: []string{"cpu-power=44"}, }, { summary: "set nonsense cpu-power 1", args: []string{"cpu-power=cheese"}, err: `bad "cpu-power" constraint: must be a non-negative integer`, }, { summary: "set nonsense cpu-power 2", args: []string{"cpu-power=-1"}, err: `bad "cpu-power" constraint: must be a non-negative integer`, }, { summary: "double set cpu-power together", args: []string{" cpu-power=300 cpu-power=1700 "}, err: `bad "cpu-power" constraint: already set`, }, { summary: "double set cpu-power separately", args: []string{"cpu-power=300 ", " cpu-power=1700"}, err: `bad "cpu-power" constraint: already set`, }, // "mem" in detail. { summary: "set mem empty", args: []string{"mem="}, }, { summary: "set mem zero", args: []string{"mem=0"}, }, { summary: "set mem without suffix", args: []string{"mem=512"}, }, { summary: "set mem with M suffix", args: []string{"mem=512M"}, }, { summary: "set mem with G suffix", args: []string{"mem=1.5G"}, }, { summary: "set mem with T suffix", args: []string{"mem=36.2T"}, }, { summary: "set mem with P suffix", args: []string{"mem=18.9P"}, }, { summary: "set nonsense mem 1", args: []string{"mem=cheese"}, err: `bad "mem" constraint: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "set nonsense mem 2", args: []string{"mem=-1"}, err: `bad "mem" constraint: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "set nonsense mem 3", args: []string{"mem=32Y"}, err: `bad "mem" constraint: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "double set mem together", args: []string{"mem=1G mem=2G"}, err: `bad "mem" constraint: already set`, }, { summary: "double set mem separately", args: []string{"mem=1G", "mem=2G"}, err: `bad "mem" constraint: already set`, }, // "root-disk" in detail. { summary: "set root-disk empty", args: []string{"root-disk="}, }, { summary: "set root-disk zero", args: []string{"root-disk=0"}, }, { summary: "set root-disk without suffix", args: []string{"root-disk=512"}, }, { summary: "set root-disk with M suffix", args: []string{"root-disk=512M"}, }, { summary: "set root-disk with G suffix", args: []string{"root-disk=1.5G"}, }, { summary: "set root-disk with T suffix", args: []string{"root-disk=36.2T"}, }, { summary: "set root-disk with P suffix", args: []string{"root-disk=18.9P"}, }, { summary: "set nonsense root-disk 1", args: []string{"root-disk=cheese"}, err: `bad "root-disk" constraint: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "set nonsense root-disk 2", args: []string{"root-disk=-1"}, err: `bad "root-disk" constraint: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "set nonsense root-disk 3", args: []string{"root-disk=32Y"}, err: `bad "root-disk" constraint: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "double set root-disk together", args: []string{"root-disk=1G root-disk=2G"}, err: `bad "root-disk" constraint: already set`, }, { summary: "double set root-disk separately", args: []string{"root-disk=1G", "root-disk=2G"}, err: `bad "root-disk" constraint: already set`, }, // tags { summary: "single tag", args: []string{"tags=foo"}, }, { summary: "multiple tags", args: []string{"tags=foo,bar"}, }, { summary: "no tags", args: []string{"tags="}, }, // Everything at once. { summary: "kitchen sink together", args: []string{" root-disk=8G mem=2T arch=i386 cpu-cores=4096 cpu-power=9001 container=lxc tags=foo,bar"}, }, { summary: "kitchen sink separately", args: []string{"root-disk=8G", "mem=2T", "cpu-cores=4096", "cpu-power=9001", "arch=armhf", "container=lxc", "tags=foo,bar"}, }, } func (s *ConstraintsSuite) TestParseConstraints(c *gc.C) { for i, t := range parseConstraintsTests { c.Logf("test %d: %s", i, t.summary) cons0, err := constraints.Parse(t.args...) if t.err == "" { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, t.err) continue } cons1, err := constraints.Parse(cons0.String()) c.Check(err, gc.IsNil) c.Check(cons1, gc.DeepEquals, cons0) } } func (s *ConstraintsSuite) TestParseMissingTags(c *gc.C) { con := constraints.MustParse("arch=amd64 mem=4G cpu-cores=1 root-disk=8G") c.Check(con.Tags, gc.IsNil) } func (s *ConstraintsSuite) TestParseNoTags(c *gc.C) { con := constraints.MustParse("arch=amd64 mem=4G cpu-cores=1 root-disk=8G tags=") c.Assert(con.Tags, gc.Not(gc.IsNil)) c.Check(*con.Tags, gc.HasLen, 0) } func (s *ConstraintsSuite) TestIsEmpty(c *gc.C) { con := constraints.Value{} c.Check(&con, jc.Satisfies, constraints.IsEmpty) con = constraints.MustParse("arch=amd64") c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) con = constraints.MustParse("") c.Check(&con, jc.Satisfies, constraints.IsEmpty) con = constraints.MustParse("tags=") c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) con = constraints.MustParse("mem=") c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) con = constraints.MustParse("arch=") c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) con = constraints.MustParse("root-disk=") c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) con = constraints.MustParse("cpu-power=") c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) con = constraints.MustParse("cpu-cores=") c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) con = constraints.MustParse("container=") c.Check(&con, gc.Not(jc.Satisfies), constraints.IsEmpty) } func uint64p(i uint64) *uint64 { return &i } func strp(s string) *string { return &s } func ctypep(ctype string) *instance.ContainerType { res := instance.ContainerType(ctype) return &res } type roundTrip struct { Name string Value constraints.Value } var constraintsRoundtripTests = []roundTrip{ {"empty", constraints.Value{}}, {"Arch1", constraints.Value{Arch: strp("")}}, {"Arch2", constraints.Value{Arch: strp("amd64")}}, {"Container1", constraints.Value{Container: ctypep("")}}, {"Container2", constraints.Value{Container: ctypep("lxc")}}, {"Container3", constraints.Value{Container: nil}}, {"CpuCores1", constraints.Value{CpuCores: nil}}, {"CpuCores2", constraints.Value{CpuCores: uint64p(0)}}, {"CpuCores3", constraints.Value{CpuCores: uint64p(128)}}, {"CpuPower1", constraints.Value{CpuPower: nil}}, {"CpuPower2", constraints.Value{CpuPower: uint64p(0)}}, {"CpuPower3", constraints.Value{CpuPower: uint64p(250)}}, {"Mem1", constraints.Value{Mem: nil}}, {"Mem2", constraints.Value{Mem: uint64p(0)}}, {"Mem3", constraints.Value{Mem: uint64p(98765)}}, {"RootDisk1", constraints.Value{RootDisk: nil}}, {"RootDisk2", constraints.Value{RootDisk: uint64p(0)}}, {"RootDisk2", constraints.Value{RootDisk: uint64p(109876)}}, {"Tags1", constraints.Value{Tags: nil}}, {"Tags2", constraints.Value{Tags: &[]string{}}}, {"Tags3", constraints.Value{Tags: &[]string{"foo", "bar"}}}, {"All", constraints.Value{ Arch: strp("i386"), Container: ctypep("lxc"), CpuCores: uint64p(4096), CpuPower: uint64p(9001), Mem: uint64p(18000000000), RootDisk: uint64p(24000000000), Tags: &[]string{"foo", "bar"}, }}, } func (s *ConstraintsSuite) TestRoundtripGnuflagValue(c *gc.C) { for _, t := range constraintsRoundtripTests { c.Logf("test %s", t.Name) var cons constraints.Value val := constraints.ConstraintsValue{&cons} err := val.Set(t.Value.String()) c.Check(err, gc.IsNil) c.Check(cons, gc.DeepEquals, t.Value) } } func (s *ConstraintsSuite) TestRoundtripString(c *gc.C) { for _, t := range constraintsRoundtripTests { c.Logf("test %s", t.Name) cons, err := constraints.Parse(t.Value.String()) c.Check(err, gc.IsNil) c.Check(cons, gc.DeepEquals, t.Value) } } func (s *ConstraintsSuite) TestRoundtripJson(c *gc.C) { for _, t := range constraintsRoundtripTests { c.Logf("test %s", t.Name) data, err := json.Marshal(t.Value) c.Assert(err, gc.IsNil) var cons constraints.Value err = json.Unmarshal(data, &cons) c.Check(err, gc.IsNil) c.Check(cons, gc.DeepEquals, t.Value) } } func (s *ConstraintsSuite) TestRoundtripYaml(c *gc.C) { for _, t := range constraintsRoundtripTests { c.Logf("test %s", t.Name) data, err := goyaml.Marshal(t.Value) c.Assert(err, gc.IsNil) var cons constraints.Value err = goyaml.Unmarshal(data, &cons) c.Check(err, gc.IsNil) c.Check(cons, gc.DeepEquals, t.Value) } } var withFallbacksTests = []struct { desc string initial string fallbacks string final string }{ { desc: "empty all round", }, { desc: "container with empty fallback", initial: "container=lxc", final: "container=lxc", }, { desc: "container from fallback", fallbacks: "container=lxc", final: "container=lxc", }, { desc: "arch with empty fallback", initial: "arch=amd64", final: "arch=amd64", }, { desc: "arch with ignored fallback", initial: "arch=amd64", fallbacks: "arch=i386", final: "arch=amd64", }, { desc: "arch from fallback", fallbacks: "arch=i386", final: "arch=i386", }, { desc: "cpu-cores with empty fallback", initial: "cpu-cores=2", final: "cpu-cores=2", }, { desc: "cpu-cores with ignored fallback", initial: "cpu-cores=4", fallbacks: "cpu-cores=8", final: "cpu-cores=4", }, { desc: "cpu-cores from fallback", fallbacks: "cpu-cores=8", final: "cpu-cores=8", }, { desc: "cpu-power with empty fallback", initial: "cpu-power=100", final: "cpu-power=100", }, { desc: "cpu-power with ignored fallback", initial: "cpu-power=100", fallbacks: "cpu-power=200", final: "cpu-power=100", }, { desc: "cpu-power from fallback", fallbacks: "cpu-power=200", final: "cpu-power=200", }, { desc: "tags with empty fallback", initial: "tags=foo,bar", final: "tags=foo,bar", }, { desc: "tags with ignored fallback", initial: "tags=foo,bar", fallbacks: "tags=baz", final: "tags=foo,bar", }, { desc: "tags from fallback", fallbacks: "tags=foo,bar", final: "tags=foo,bar", }, { desc: "tags inital empty", initial: "tags=", fallbacks: "tags=foo,bar", final: "tags=", }, { desc: "mem with empty fallback", initial: "mem=4G", final: "mem=4G", }, { desc: "mem with ignored fallback", initial: "mem=4G", fallbacks: "mem=8G", final: "mem=4G", }, { desc: "mem from fallback", fallbacks: "mem=8G", final: "mem=8G", }, { desc: "root-disk with empty fallback", initial: "root-disk=4G", final: "root-disk=4G", }, { desc: "root-disk with ignored fallback", initial: "root-disk=4G", fallbacks: "root-disk=8G", final: "root-disk=4G", }, { desc: "root-disk from fallback", fallbacks: "root-disk=8G", final: "root-disk=8G", }, { desc: "non-overlapping mix", initial: "root-disk=8G mem=4G arch=amd64", fallbacks: "cpu-power=1000 cpu-cores=4", final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4", }, { desc: "overlapping mix", initial: "root-disk=8G mem=4G arch=amd64", fallbacks: "cpu-power=1000 cpu-cores=4 mem=8G", final: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4", }, } func (s *ConstraintsSuite) TestWithFallbacks(c *gc.C) { for i, t := range withFallbacksTests { c.Logf("test %d: %s", i, t.desc) initial := constraints.MustParse(t.initial) fallbacks := constraints.MustParse(t.fallbacks) final := constraints.MustParse(t.final) c.Check(initial.WithFallbacks(fallbacks), gc.DeepEquals, final) } } var hasContainerTests = []struct { constraints string hasContainer bool }{ { hasContainer: false, }, { constraints: "container=lxc", hasContainer: true, }, { constraints: "container=none", hasContainer: false, }, } func (s *ConstraintsSuite) TestHasContainer(c *gc.C) { for i, t := range hasContainerTests { c.Logf("test %d", i) cons := constraints.MustParse(t.constraints) c.Check(cons.HasContainer(), gc.Equals, t.hasContainer) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/����������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022515� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/interface.go����������������������������������0000644�0000153�0000161�00000016263�12321735776�025037� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs import ( "io" "os" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" ) // EnvironCapability implements access to metadata about the capabilities // of an environment. type EnvironCapability interface { // SupportedArchitectures returns the image architectures which can // be hosted by this environment. SupportedArchitectures() ([]string, error) } // A EnvironProvider represents a computing and storage provider. type EnvironProvider interface { // Prepare prepares an environment for use. Any additional // configuration attributes in the returned environment should // be saved to be used later. If the environment is already // prepared, this call is equivalent to Open. Prepare(ctx BootstrapContext, cfg *config.Config) (Environ, error) // Open opens the environment and returns it. // The configuration must have come from a previously // prepared environment. Open(cfg *config.Config) (Environ, error) // Validate ensures that config is a valid configuration for this // provider, applying changes to it if necessary, and returns the // validated configuration. // If old is not nil, it holds the previous environment configuration // for consideration when validating changes. Validate(cfg, old *config.Config) (valid *config.Config, err error) // Boilerplate returns a default configuration for the environment in yaml format. // The text should be a key followed by some number of attributes: // `environName: // type: environTypeName // attr1: val1 // ` // The text is used as a template (see the template package) with one extra template // function available, rand, which expands to a random hexadecimal string when invoked. BoilerplateConfig() string // SecretAttrs filters the supplied configuration returning only values // which are considered sensitive. All of the values of these secret // attributes need to be strings. SecretAttrs(cfg *config.Config) (map[string]string, error) } // EnvironStorage implements storage access for an environment type EnvironStorage interface { // Storage returns storage specific to the environment. Storage() storage.Storage } // ConfigGetter implements access to an environment's configuration. type ConfigGetter interface { // Config returns the configuration data with which the Environ was created. // Note that this is not necessarily current; the canonical location // for the configuration data is stored in the state. Config() *config.Config } // An Environ represents a juju environment as specified // in the environments.yaml file. // // Due to the limitations of some providers (for example ec2), the // results of the Environ methods may not be fully sequentially // consistent. In particular, while a provider may retry when it // gets an error for an operation, it will not retry when // an operation succeeds, even if that success is not // consistent with a previous operation. // // Even though Juju takes care not to share an Environ between concurrent // workers, it does allow concurrent method calls into the provider // implementation. The typical provider implementation needs locking to // avoid undefined behaviour when the configuration changes. type Environ interface { // Name returns the Environ's name. Name() string // Bootstrap initializes the state for the environment, possibly // starting one or more instances. If the configuration's // AdminSecret is non-empty, the administrator password on the // newly bootstrapped state will be set to a hash of it (see // utils.PasswordHash), When first connecting to the // environment via the juju package, the password hash will be // automatically replaced by the real password. // // The supplied constraints are used to choose the initial instance // specification, and will be stored in the new environment's state. // // Bootstrap is responsible for selecting the appropriate tools, // and setting the agent-version configuration attribute prior to // bootstrapping the environment. Bootstrap(ctx BootstrapContext, cons constraints.Value) error // StateInfo returns information on the state initialized // by Bootstrap. StateInfo() (*state.Info, *api.Info, error) // InstanceBroker defines methods for starting and stopping // instances. InstanceBroker // ConfigGetter allows the retrieval of the configuration data. ConfigGetter // EnvironCapability allows access to this environment's capabilities. EnvironCapability // SetConfig updates the Environ's configuration. // // Calls to SetConfig do not affect the configuration of // values previously obtained from Storage. SetConfig(cfg *config.Config) error // Instances returns a slice of instances corresponding to the // given instance ids. If no instances were found, but there // was no other error, it will return ErrNoInstances. If // some but not all the instances were found, the returned slice // will have some nil slots, and an ErrPartialInstances error // will be returned. Instances(ids []instance.Id) ([]instance.Instance, error) EnvironStorage // Destroy shuts down all known machines and destroys the // rest of the environment. Note that on some providers, // very recently started instances may not be destroyed // because they are not yet visible. // // When Destroy has been called, any Environ referring to the // same remote environment may become invalid Destroy() error // OpenPorts opens the given ports for the whole environment. // Must only be used if the environment was setup with the // FwGlobal firewall mode. OpenPorts(ports []instance.Port) error // ClosePorts closes the given ports for the whole environment. // Must only be used if the environment was setup with the // FwGlobal firewall mode. ClosePorts(ports []instance.Port) error // Ports returns the ports opened for the whole environment. // Must only be used if the environment was setup with the // FwGlobal firewall mode. Ports() ([]instance.Port, error) // Provider returns the EnvironProvider that created this Environ. Provider() EnvironProvider // TODO(axw) 2014-02-11 #pending-review // Embed state.Prechecker, and introduce an EnvironBase // that embeds a no-op prechecker implementation. } // BootstrapContext is an interface that is passed to // Environ.Bootstrap, providing a means of obtaining // information about and manipulating the context in which // it is being invoked. type BootstrapContext interface { GetStdin() io.Reader GetStdout() io.Writer GetStderr() io.Writer Infof(format string, params ...interface{}) Verbosef(format string, params ...interface{}) // InterruptNotify starts watching for interrupt signals // on behalf of the caller, sending them to the supplied // channel. InterruptNotify(sig chan<- os.Signal) // StopInterruptNotify undoes the effects of a previous // call to InterruptNotify with the same channel. After // StopInterruptNotify returns, no more signals will be // delivered to the channel. StopInterruptNotify(chan<- os.Signal) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/configstore/����������������������������������0000755�0000153�0000161�00000000000�12321736000�025037� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/configstore/interface.go����������������������0000644�0000153�0000161�00000005403�12321735642�027343� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package configstore import ( "errors" ) var ErrEnvironInfoAlreadyExists = errors.New("environment info already exists") // APIEndpoint holds information about an API endpoint. type APIEndpoint struct { // APIAddress holds a list of API addresses. It may not be // current, and it will be empty if the environment has not been // bootstrapped. Addresses []string // CACert holds the CA certificate that // signed the API server's key. CACert string } // APICredentials hold credentials for connecting to an API endpoint. type APICredentials struct { // User holds the name of the user to connect as. User string Password string } // Storage stores environment configuration data. type Storage interface { // ReadInfo reads information associated with // the environment with the given name. // If there is no such information, it will // return an errors.NotFound error. ReadInfo(envName string) (EnvironInfo, error) // CreateInfo creates some uninitialized information associated // with the environment with the given name. // It return ErrAlreadyExists if the // information has already been created. CreateInfo(envName string) (EnvironInfo, error) } // EnvironInfo holds information associated with an environment. type EnvironInfo interface { // Initialized returns whether the environment information has // been initialized. It will return true for EnvironInfo instances // that have been created but not written. Initialized() bool // BootstrapConfig returns the configuration attributes // that an environment will be bootstrapped with. BootstrapConfig() map[string]interface{} // APIEndpoint returns the current API endpoint information. APIEndpoint() APIEndpoint // APICredentials returns the current API credentials. APICredentials() APICredentials // SetBootstrapConfig sets the configuration attributes // to be used for bootstrapping. // This method may only be called on an EnvironInfo // obtained using ConfigStorage.CreateInfo. SetBootstrapConfig(map[string]interface{}) // SetAPIEndpoint sets the API endpoint information // currently associated with the environment. SetAPIEndpoint(APIEndpoint) // SetAPICreds sets the API credentials currently // associated with the environment. SetAPICredentials(APICredentials) // Location returns the location of the source of the environment // information in a human readable format. Location() string // Write writes the current information to persistent storage. // A subsequent call to ConfigStorage.ReadInfo // can retrieve it. After this call succeeds, Initialized will return true. Write() error // Destroy destroys the information associated with // the environment. Destroy() error } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/configstore/disk.go���������������������������0000644�0000153�0000161�00000012470�12321735776�026347� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package configstore import ( "fmt" "io/ioutil" "os" "path/filepath" "github.com/errgo/errgo" "github.com/juju/loggo" "launchpad.net/goyaml" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/utils" ) var logger = loggo.GetLogger("juju.environs.configstore") // Default returns disk-based environment config storage // rooted at JujuHome. func Default() (Storage, error) { return NewDisk(osenv.JujuHome()) } type diskStore struct { dir string } type environInfo struct { path string // initialized signifies whether the info has been written. initialized bool // created signifies whether the info was returned from // a CreateInfo call. created bool User string Password string StateServers []string `yaml:"state-servers"` CACert string `yaml:"ca-cert"` Config map[string]interface{} `yaml:"bootstrap-config,omitempty"` } // NewDisk returns a ConfigStorage implementation that stores // configuration in the given directory. The parent of the directory // must already exist; the directory itself is created on demand. func NewDisk(dir string) (Storage, error) { if _, err := os.Stat(dir); err != nil { return nil, err } return &diskStore{dir}, nil } func (d *diskStore) envPath(envName string) string { return filepath.Join(d.dir, "environments", envName+".jenv") } func (d *diskStore) mkEnvironmentsDir() error { path := filepath.Join(d.dir, "environments") logger.Debugf("Making %v", path) err := os.Mkdir(path, 0700) if os.IsExist(err) { return nil } return err } // CreateInfo implements Storage.CreateInfo. func (d *diskStore) CreateInfo(envName string) (EnvironInfo, error) { if err := d.mkEnvironmentsDir(); err != nil { return nil, err } // We create an empty file so that any subsequent CreateInfos // will fail. path := d.envPath(envName) file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) if os.IsExist(err) { return nil, ErrEnvironInfoAlreadyExists } if err != nil { return nil, err } file.Close() return &environInfo{ created: true, path: path, }, nil } // ReadInfo implements Storage.ReadInfo. func (d *diskStore) ReadInfo(envName string) (EnvironInfo, error) { path := d.envPath(envName) data, err := ioutil.ReadFile(path) if err != nil { if os.IsNotExist(err) { return nil, errors.NotFoundf("environment %q", envName) } return nil, err } var info environInfo info.path = path if len(data) == 0 { return &info, nil } if err := goyaml.Unmarshal(data, &info); err != nil { return nil, fmt.Errorf("error unmarshalling %q: %v", path, err) } info.initialized = true return &info, nil } // Initialized implements EnvironInfo.Initialized. func (info *environInfo) Initialized() bool { return info.initialized } // BootstrapConfig implements EnvironInfo.BootstrapConfig. func (info *environInfo) BootstrapConfig() map[string]interface{} { return info.Config } // APICredentials implements EnvironInfo.APICredentials. func (info *environInfo) APICredentials() APICredentials { return APICredentials{ User: info.User, Password: info.Password, } } // APIEndpoint implements EnvironInfo.APIEndpoint. func (info *environInfo) APIEndpoint() APIEndpoint { return APIEndpoint{ Addresses: info.StateServers, CACert: info.CACert, } } // SetExtraConfig implements EnvironInfo.SetBootstrapConfig. func (info *environInfo) SetBootstrapConfig(attrs map[string]interface{}) { if !info.created { panic("bootstrap config set on environment info that has not just been created") } info.Config = attrs } // SetAPIEndpoint implements EnvironInfo.SetAPIEndpoint. func (info *environInfo) SetAPIEndpoint(endpoint APIEndpoint) { info.StateServers = endpoint.Addresses info.CACert = endpoint.CACert } // SetAPICredentials implements EnvironInfo.SetAPICredentials. func (info *environInfo) SetAPICredentials(creds APICredentials) { info.User = creds.User info.Password = creds.Password } // Location returns the location of the environInfo in human readable format. func (info *environInfo) Location() string { return fmt.Sprintf("file %q", info.path) } // Write implements EnvironInfo.Write. func (info *environInfo) Write() error { data, err := goyaml.Marshal(info) if err != nil { return errgo.Annotate(err, "cannot marshal environment info") } // Create a temporary file and rename it, so that the data // changes atomically. parent, _ := filepath.Split(info.path) tmpFile, err := ioutil.TempFile(parent, "") if err != nil { return errgo.Annotate(err, "cannot create temporary file") } _, err = tmpFile.Write(data) // N.B. We need to close the file before renaming it // otherwise it will fail under Windows with a file-in-use // error. tmpFile.Close() if err != nil { return errgo.Annotate(err, "cannot write temporary file") } if err := utils.ReplaceFile(tmpFile.Name(), info.path); err != nil { os.Remove(tmpFile.Name()) return errgo.Annotate(err, "cannot rename new environment info file") } info.initialized = true return nil } // Destroy implements EnvironInfo.Destroy. func (info *environInfo) Destroy() error { err := os.Remove(info.path) if os.IsNotExist(err) { return fmt.Errorf("environment info has already been removed") } return err } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/configstore/interface_test.go�����������������0000644�0000153�0000161�00000011214�12321735642�030377� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package configstore_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/testing/testbase" ) // interfaceSuite defines a set of tests on a ConfigStorage // implementation, independent of the implementation itself. // The NewStore field must be set up to return a ConfigStorage // instance of the type to be tested. type interfaceSuite struct { testbase.LoggingSuite NewStore func(c *gc.C) configstore.Storage } func (s *interfaceSuite) TestCreate(c *gc.C) { store := s.NewStore(c) info, err := store.CreateInfo("someenv") c.Assert(err, gc.IsNil) c.Assert(info.APIEndpoint(), gc.DeepEquals, configstore.APIEndpoint{}) c.Assert(info.APICredentials(), gc.DeepEquals, configstore.APICredentials{}) c.Assert(info.Initialized(), jc.IsFalse) // Check that we can't create it twice. info, err = store.CreateInfo("someenv") c.Assert(err, gc.Equals, configstore.ErrEnvironInfoAlreadyExists) c.Assert(info, gc.IsNil) // Check that we can read it again. info, err = store.ReadInfo("someenv") c.Assert(err, gc.IsNil) c.Assert(info.Initialized(), jc.IsFalse) } func (s *interfaceSuite) TestSetAPIEndpointAndCredentials(c *gc.C) { store := s.NewStore(c) info, err := store.CreateInfo("someenv") c.Assert(err, gc.IsNil) expectEndpoint := configstore.APIEndpoint{ Addresses: []string{"example.com"}, CACert: "a cert", } info.SetAPIEndpoint(expectEndpoint) c.Assert(info.APIEndpoint(), gc.DeepEquals, expectEndpoint) expectCreds := configstore.APICredentials{ User: "foobie", Password: "bletch", } info.SetAPICredentials(expectCreds) c.Assert(info.APICredentials(), gc.DeepEquals, expectCreds) } func (s *interfaceSuite) TestWrite(c *gc.C) { store := s.NewStore(c) // Create the info. info, err := store.CreateInfo("someenv") c.Assert(err, gc.IsNil) // Set it up with some actual data and write it out. expectCreds := configstore.APICredentials{ User: "foobie", Password: "bletch", } info.SetAPICredentials(expectCreds) expectEndpoint := configstore.APIEndpoint{ Addresses: []string{"example.com"}, CACert: "a cert", } info.SetAPIEndpoint(expectEndpoint) err = info.Write() c.Assert(err, gc.IsNil) c.Assert(info.Initialized(), jc.IsTrue) // Check we can read the information back info, err = store.ReadInfo("someenv") c.Assert(err, gc.IsNil) c.Assert(info.APICredentials(), gc.DeepEquals, expectCreds) c.Assert(info.APIEndpoint(), gc.DeepEquals, expectEndpoint) // Change the information and write it again. expectCreds.User = "arble" info.SetAPICredentials(expectCreds) err = info.Write() c.Assert(err, gc.IsNil) // Check we can read the information back info, err = store.ReadInfo("someenv") c.Assert(err, gc.IsNil) c.Assert(info.APICredentials(), gc.DeepEquals, expectCreds) } func (s *interfaceSuite) TestDestroy(c *gc.C) { store := s.NewStore(c) info, err := store.CreateInfo("someenv") c.Assert(err, gc.IsNil) err = info.Destroy() c.Assert(err, gc.IsNil) err = info.Destroy() c.Assert(err, gc.ErrorMatches, "environment info has already been removed") info, err = store.CreateInfo("someenv") c.Assert(err, gc.IsNil) } func (s *interfaceSuite) TestNoBleedThrough(c *gc.C) { store := s.NewStore(c) info, err := store.CreateInfo("someenv") c.Assert(err, gc.IsNil) info.SetAPICredentials(configstore.APICredentials{User: "foo"}) info.SetAPIEndpoint(configstore.APIEndpoint{CACert: "blah"}) attrs := map[string]interface{}{"foo": "bar"} info.SetBootstrapConfig(attrs) info1, err := store.ReadInfo("someenv") c.Assert(err, gc.IsNil) c.Assert(info1.Initialized(), jc.IsFalse) c.Assert(info1.APICredentials(), gc.DeepEquals, configstore.APICredentials{}) c.Assert(info1.APIEndpoint(), gc.DeepEquals, configstore.APIEndpoint{}) c.Assert(info1.BootstrapConfig(), gc.HasLen, 0) err = info.Write() c.Assert(err, gc.IsNil) attrs["foo"] = "different" info1, err = store.ReadInfo("someenv") c.Assert(err, gc.IsNil) c.Assert(info1.Initialized(), jc.IsTrue) c.Assert(info1.BootstrapConfig(), gc.DeepEquals, map[string]interface{}{"foo": "bar"}) } func (s *interfaceSuite) TestSetBootstrapConfigPanicsWhenNotCreated(c *gc.C) { store := s.NewStore(c) info, err := store.CreateInfo("someenv") c.Assert(err, gc.IsNil) info.SetBootstrapConfig(map[string]interface{}{"foo": "bar"}) err = info.Write() c.Assert(err, gc.IsNil) info, err = store.ReadInfo("someenv") c.Assert(err, gc.IsNil) c.Assert(func() { info.SetBootstrapConfig(nil) }, gc.PanicMatches, "bootstrap config set on environment info that has not just been created") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/configstore/package_test.go�������������������0000644�0000153�0000161�00000000337�12321735642�030036� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package configstore_test import ( "testing" gc "launchpad.net/gocheck" ) func TestPackage(t *testing.T) { gc.TestingT(t) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/configstore/mem.go����������������������������0000644�0000153�0000161�00000004104�12321735776�026166� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package configstore import ( "fmt" "sync" "launchpad.net/juju-core/errors" ) type memStore struct { mu sync.Mutex envs map[string]*memInfo } type memInfo struct { store *memStore name string environInfo } // clone returns a copy of the given environment info, isolated // from the store itself. func (info *memInfo) clone() *memInfo { // Note that none of the Set* methods ever set fields inside // references, which makes this OK to do. info1 := *info newAttrs := make(map[string]interface{}) for name, attr := range info.Config { newAttrs[name] = attr } info1.Config = newAttrs info1.created = false return &info1 } // NewMem returns a ConfigStorage implementation that // stores configuration in memory. func NewMem() Storage { return &memStore{ envs: make(map[string]*memInfo), } } // CreateInfo implements Storage.CreateInfo. func (m *memStore) CreateInfo(envName string) (EnvironInfo, error) { m.mu.Lock() defer m.mu.Unlock() if m.envs[envName] != nil { return nil, ErrEnvironInfoAlreadyExists } info := &memInfo{ store: m, name: envName, } info.created = true m.envs[envName] = info.clone() return info, nil } // ReadInfo implements Storage.ReadInfo. func (m *memStore) ReadInfo(envName string) (EnvironInfo, error) { m.mu.Lock() defer m.mu.Unlock() info := m.envs[envName] if info != nil { return info.clone(), nil } return nil, errors.NotFoundf("environment %q", envName) } // Location implements EnvironInfo.Location. func (info *memInfo) Location() string { return "memory" } // Write implements EnvironInfo.Write. func (info *memInfo) Write() error { m := info.store m.mu.Lock() defer m.mu.Unlock() info.initialized = true m.envs[info.name] = info.clone() return nil } // Destroy implements EnvironInfo.Destroy. func (info *memInfo) Destroy() error { m := info.store m.mu.Lock() defer m.mu.Unlock() if m.envs[info.name] == nil { return fmt.Errorf("environment info has already been removed") } delete(m.envs, info.name) return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/configstore/mem_test.go�����������������������0000644�0000153�0000161�00000001160�12321735642�027214� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package configstore_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/configstore" ) var _ = gc.Suite(&memInterfaceSuite{}) type memInterfaceSuite struct { interfaceSuite } func (s *memInterfaceSuite) SetUpSuite(c *gc.C) { s.NewStore = func(c *gc.C) configstore.Storage { return configstore.NewMem() } } func (s *memInterfaceSuite) TestMemInfoLocation(c *gc.C) { memStore := configstore.NewMem() memInfo, _ := memStore.CreateInfo("foo") c.Assert(memInfo.Location(), gc.Equals, "memory") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/configstore/disk_test.go����������������������0000644�0000153�0000161�00000014441�12321735642�027376� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package configstore_test import ( "fmt" "io/ioutil" "os" "os/user" "path/filepath" "strings" "syscall" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/testing/testbase" ) var _ = gc.Suite(&diskInterfaceSuite{}) type diskInterfaceSuite struct { interfaceSuite dir string } func (s *diskInterfaceSuite) SetUpTest(c *gc.C) { s.dir = c.MkDir() s.NewStore = func(c *gc.C) configstore.Storage { store, err := configstore.NewDisk(s.dir) c.Assert(err, gc.IsNil) return store } } // storePath returns the path to the environment info // for the named environment in the given directory. // If envName is empty, it returns the path // to the info files' containing directory. func storePath(dir string, envName string) string { path := filepath.Join(dir, "environments") if envName != "" { path = filepath.Join(path, envName+".jenv") } return path } func (s *diskInterfaceSuite) TearDownTest(c *gc.C) { s.NewStore = nil // Check that no stray temp files have been left behind entries, err := ioutil.ReadDir(storePath(s.dir, "")) c.Assert(err, gc.IsNil) for _, entry := range entries { if !strings.HasSuffix(entry.Name(), ".jenv") { c.Errorf("found possible stray temp file %q", entry.Name()) } } } var _ = gc.Suite(&diskStoreSuite{}) type diskStoreSuite struct { testbase.LoggingSuite } func (*diskStoreSuite) TestNewDisk(c *gc.C) { dir := c.MkDir() store, err := configstore.NewDisk(filepath.Join(dir, "foo")) c.Assert(err, jc.Satisfies, os.IsNotExist) c.Assert(store, gc.IsNil) store, err = configstore.NewDisk(filepath.Join(dir)) c.Assert(err, gc.IsNil) c.Assert(store, gc.NotNil) } var sampleInfo = ` user: rog password: guessit state-servers: - example.com - kremvax.ru ca-cert: 'first line second line' bootstrap-config: secret: blah arble: bletch `[1:] func (*diskStoreSuite) TestRead(c *gc.C) { dir := c.MkDir() err := os.Mkdir(storePath(dir, ""), 0700) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(storePath(dir, "someenv"), []byte(sampleInfo), 0666) c.Assert(err, gc.IsNil) store, err := configstore.NewDisk(dir) c.Assert(err, gc.IsNil) info, err := store.ReadInfo("someenv") c.Assert(err, gc.IsNil) c.Assert(info.Initialized(), jc.IsTrue) c.Assert(info.APICredentials(), gc.DeepEquals, configstore.APICredentials{ User: "rog", Password: "guessit", }) c.Assert(info.APIEndpoint(), gc.DeepEquals, configstore.APIEndpoint{ Addresses: []string{"example.com", "kremvax.ru"}, CACert: "first line\nsecond line", }) c.Assert(info.Location(), gc.Equals, fmt.Sprintf("file %q", dir+"/environments/someenv.jenv")) c.Assert(info.BootstrapConfig(), gc.DeepEquals, map[string]interface{}{ "secret": "blah", "arble": "bletch", }) } func (*diskStoreSuite) TestReadNotFound(c *gc.C) { dir := c.MkDir() store, err := configstore.NewDisk(dir) c.Assert(err, gc.IsNil) info, err := store.ReadInfo("someenv") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) c.Assert(info, gc.IsNil) } func (*diskStoreSuite) TestCreate(c *gc.C) { dir := c.MkDir() store, err := configstore.NewDisk(dir) c.Assert(err, gc.IsNil) // Create some new environment info. info, err := store.CreateInfo("someenv") c.Assert(err, gc.IsNil) c.Assert(info.APIEndpoint(), gc.DeepEquals, configstore.APIEndpoint{}) c.Assert(info.APICredentials(), gc.DeepEquals, configstore.APICredentials{}) c.Assert(info.Initialized(), jc.IsFalse) data, err := ioutil.ReadFile(storePath(dir, "someenv")) c.Assert(err, gc.IsNil) c.Assert(data, gc.HasLen, 0) // Check that we can't create it twice. info, err = store.CreateInfo("someenv") c.Assert(err, gc.Equals, configstore.ErrEnvironInfoAlreadyExists) c.Assert(info, gc.IsNil) // Check that we can read it again. info, err = store.ReadInfo("someenv") c.Assert(err, gc.IsNil) c.Assert(info.Initialized(), jc.IsFalse) } func (s *diskStoreSuite) TestCreatePermissions(c *gc.C) { // Even though it doesn't test the actual chown, it does test the code path. user, err := user.Current() c.Assert(err, gc.IsNil) s.PatchEnvironment("SUDO_UID", user.Uid) s.PatchEnvironment("SUDO_GID", user.Gid) dir := c.MkDir() store, err := configstore.NewDisk(dir) c.Assert(err, gc.IsNil) // Create some new environment info. _, err = store.CreateInfo("someenv") c.Assert(err, gc.IsNil) checkPath := func(path string) { stat, err := os.Stat(path) c.Assert(err, gc.IsNil) c.Assert(fmt.Sprint(stat.Sys().(*syscall.Stat_t).Uid), gc.Equals, user.Uid) c.Assert(fmt.Sprint(stat.Sys().(*syscall.Stat_t).Gid), gc.Equals, user.Gid) } checkPath(storePath(dir, "")) checkPath(storePath(dir, "someenv")) } func (*diskStoreSuite) TestWriteTempFileFails(c *gc.C) { dir := c.MkDir() store, err := configstore.NewDisk(dir) c.Assert(err, gc.IsNil) info, err := store.CreateInfo("someenv") c.Assert(err, gc.IsNil) // Make the directory non-writable err = os.Chmod(storePath(dir, ""), 0555) c.Assert(err, gc.IsNil) err = info.Write() c.Assert(err, gc.ErrorMatches, "cannot create temporary file: .*") // Make the directory writable again so that gocheck can clean it up. err = os.Chmod(storePath(dir, ""), 0777) c.Assert(err, gc.IsNil) } func (*diskStoreSuite) TestRenameFails(c *gc.C) { dir := c.MkDir() store, err := configstore.NewDisk(dir) c.Assert(err, gc.IsNil) info, err := store.CreateInfo("someenv") c.Assert(err, gc.IsNil) // Replace the file by an directory which can't be renamed over. path := storePath(dir, "someenv") err = os.Remove(path) c.Assert(err, gc.IsNil) err = os.Mkdir(path, 0777) c.Assert(err, gc.IsNil) err = info.Write() c.Assert(err, gc.ErrorMatches, "cannot rename new environment info file: .*") } func (*diskStoreSuite) TestDestroyRemovesFiles(c *gc.C) { dir := c.MkDir() store, err := configstore.NewDisk(dir) c.Assert(err, gc.IsNil) info, err := store.CreateInfo("someenv") c.Assert(err, gc.IsNil) _, err = os.Stat(storePath(dir, "someenv")) c.Assert(err, gc.IsNil) err = info.Destroy() c.Assert(err, gc.IsNil) _, err = os.Stat(storePath(dir, "someenv")) c.Assert(err, jc.Satisfies, os.IsNotExist) err = info.Destroy() c.Assert(err, gc.ErrorMatches, "environment info has already been removed") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/emptystorage.go�������������������������������0000644�0000153�0000161�00000002024�12321735642�025600� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs import ( "fmt" "io" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" ) // EmptyStorage holds a StorageReader object that contains no files and // offers no URLs. var EmptyStorage storage.StorageReader = emptyStorage{} type emptyStorage struct{} func (s emptyStorage) Get(name string) (io.ReadCloser, error) { return nil, errors.NotFoundf("file %q", name) } func (s emptyStorage) URL(name string) (string, error) { return "", fmt.Errorf("file %q not found", name) } func (s emptyStorage) List(prefix string) ([]string, error) { return nil, nil } // DefaultConsistencyStrategy is specified in the StorageReader interface. func (s emptyStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { return utils.AttemptStrategy{} } // ShouldRetry is specified in the StorageReader interface. func (s emptyStorage) ShouldRetry(err error) bool { return false } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/broker.go�������������������������������������0000644�0000153�0000161�00000003430�12321735776�024353� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs import ( "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/tools" ) // Networks holds network include/exclude when starting an instance. type Networks struct { IncludeNetworks []string ExcludeNetworks []string } // StartInstanceParams holds parameters for the // InstanceBroker.StartInstace method. type StartInstanceParams struct { // Constraints is a set of constraints on // the kind of instance to create. Constraints constraints.Value // Networks holds networks to include/exclude for the instance. Networks Networks // Tools is a list of tools that may be used // to start a Juju agent on the machine. Tools tools.List // MachineConfig describes the machine's configuration. MachineConfig *cloudinit.MachineConfig } // TODO(wallyworld) - we want this in the environs/instance package but import loops // stop that from being possible right now. type InstanceBroker interface { // StartInstance asks for a new instance to be created, associated with // the provided config in machineConfig. The given config describes the juju // state for the new instance to connect to. The config MachineNonce, which must be // unique within an environment, is used by juju to protect against the // consequences of multiple instances being started with the same machine // id. StartInstance(args StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) // StopInstances shuts down the given instances. StopInstances([]instance.Instance) error // AllInstances returns all instances currently known to the broker. AllInstances() ([]instance.Instance, error) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/export_test.go��������������������������������0000644�0000153�0000161�00000000534�12321735642�025441� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs var ( Providers = &providers ProviderAliases = &providerAliases ) func UpdateEnvironAttrs(envs *Environs, name string, newAttrs map[string]interface{}) { for k, v := range newAttrs { envs.rawEnvirons[name][k] = v } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/filestorage/����������������������������������0000755�0000153�0000161�00000000000�12321735642�025034� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/filestorage/filestorage_test.go���������������0000644�0000153�0000161�00000016206�12321735642�030733� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package filestorage_test // The filestorage structs are used as stubs in tests. // The tests defined herein are simple smoke tests for the // required reader and writer functionality. import ( "bytes" "io/ioutil" "os" "path/filepath" "strings" "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/storage" coreerrors "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju/osenv" ) func TestPackage(t *testing.T) { gc.TestingT(t) } type filestorageSuite struct { dir string reader storage.StorageReader writer storage.StorageWriter } var _ = gc.Suite(&filestorageSuite{}) func (s *filestorageSuite) SetUpTest(c *gc.C) { s.dir = c.MkDir() var err error s.reader, err = filestorage.NewFileStorageReader(s.dir) c.Assert(err, gc.IsNil) s.writer, err = filestorage.NewFileStorageWriter(s.dir) c.Assert(err, gc.IsNil) } func (s *filestorageSuite) createFile(c *gc.C, name string) (fullpath string, data []byte) { fullpath = filepath.Join(s.dir, name) dir := filepath.Dir(fullpath) c.Assert(os.MkdirAll(dir, 0755), gc.IsNil) data = []byte{1, 2, 3, 4, 5} err := ioutil.WriteFile(fullpath, data, 0644) c.Assert(err, gc.IsNil) return fullpath, data } func (s *filestorageSuite) TestList(c *gc.C) { names := []string{ "a/b/c", "a/bb", "a/c", "aa", "b/c/d", } for _, name := range names { s.createFile(c, name) } type test struct { prefix string expected []string } for i, test := range []test{ {"a", []string{"a/b/c", "a/bb", "a/c", "aa"}}, {"a/b", []string{"a/b/c", "a/bb"}}, {"a/b/c", []string{"a/b/c"}}, {"", names}, } { c.Logf("test %d: prefix=%q", i, test.prefix) files, err := storage.List(s.reader, test.prefix) c.Assert(err, gc.IsNil) c.Assert(files, gc.DeepEquals, test.expected) } } func (s *filestorageSuite) TestListHidesTempDir(c *gc.C) { err := s.writer.Put("test-write", bytes.NewReader(nil), 0) c.Assert(err, gc.IsNil) files, err := storage.List(s.reader, "") c.Assert(err, gc.IsNil) c.Check(files, gc.DeepEquals, []string{"test-write"}) files, err = storage.List(s.reader, "no-such-directory") c.Assert(err, gc.IsNil) c.Check(files, gc.DeepEquals, []string(nil)) // We also pretend the .tmp directory doesn't exist. If you call a // directory that doesn't exist, we just return an empty list of // strings, so we force the same behavior for '.tmp' // we poke in a file so it would have something to return s.createFile(c, ".tmp/test-file") files, err = storage.List(s.reader, ".tmp") c.Assert(err, gc.IsNil) c.Check(files, gc.DeepEquals, []string(nil)) // For consistency, we refuse all other possibilities as well s.createFile(c, ".tmp/foo/bar") files, err = storage.List(s.reader, ".tmp/foo") c.Assert(err, gc.IsNil) c.Check(files, gc.DeepEquals, []string(nil)) s.createFile(c, ".tmpother/foo") files, err = storage.List(s.reader, ".tmpother") c.Assert(err, gc.IsNil) c.Check(files, gc.DeepEquals, []string(nil)) } func (s *filestorageSuite) TestURL(c *gc.C) { expectedpath, _ := s.createFile(c, "test-file") _, file := filepath.Split(expectedpath) url, err := s.reader.URL(file) c.Assert(err, gc.IsNil) c.Assert(url, gc.Equals, "file://"+expectedpath) } func (s *filestorageSuite) TestGet(c *gc.C) { expectedpath, data := s.createFile(c, "test-file") _, file := filepath.Split(expectedpath) rc, err := storage.Get(s.reader, file) c.Assert(err, gc.IsNil) defer rc.Close() c.Assert(err, gc.IsNil) b, err := ioutil.ReadAll(rc) c.Assert(err, gc.IsNil) c.Assert(b, gc.DeepEquals, data) // Get on a non-existant path returns NotFoundError _, err = s.reader.Get("nowhere") c.Assert(err, jc.Satisfies, coreerrors.IsNotFoundError) // Get on a directory returns NotFoundError s.createFile(c, "dir/file") _, err = s.reader.Get("dir") c.Assert(err, jc.Satisfies, coreerrors.IsNotFoundError) } func (s *filestorageSuite) TestGetRefusesTemp(c *gc.C) { s.createFile(c, ".tmp/test-file") _, err := storage.Get(s.reader, ".tmp/test-file") c.Check(err, gc.NotNil) c.Check(err, jc.Satisfies, os.IsNotExist) s.createFile(c, ".tmp/foo/test-file") _, err = storage.Get(s.reader, ".tmp/foo/test-file") c.Check(err, gc.NotNil) c.Check(err, jc.Satisfies, os.IsNotExist) } func (s *filestorageSuite) TestPut(c *gc.C) { data := []byte{1, 2, 3, 4, 5} err := s.writer.Put("test-write", bytes.NewReader(data), int64(len(data))) c.Assert(err, gc.IsNil) b, err := ioutil.ReadFile(filepath.Join(s.dir, "test-write")) c.Assert(err, gc.IsNil) c.Assert(b, gc.DeepEquals, data) } func (s *filestorageSuite) TestPutRefusesTmp(c *gc.C) { data := []byte{1, 2, 3, 4, 5} err := s.writer.Put(".tmp/test-write", bytes.NewReader(data), int64(len(data))) c.Assert(err, gc.NotNil) c.Check(err, jc.Satisfies, os.IsPermission) c.Check(*err.(*os.PathError), gc.Equals, os.PathError{ Op: "Put", Path: ".tmp/test-write", Err: os.ErrPermission, }) _, err = ioutil.ReadFile(filepath.Join(s.dir, ".tmp", "test-write")) c.Assert(err, jc.Satisfies, os.IsNotExist) } func (s *filestorageSuite) TestRemove(c *gc.C) { expectedpath, _ := s.createFile(c, "test-file") _, file := filepath.Split(expectedpath) err := s.writer.Remove(file) c.Assert(err, gc.IsNil) _, err = ioutil.ReadFile(expectedpath) c.Assert(err, gc.Not(gc.IsNil)) } func (s *filestorageSuite) TestRemoveAll(c *gc.C) { expectedpath, _ := s.createFile(c, "test-file") err := s.writer.RemoveAll() c.Assert(err, gc.IsNil) _, err = ioutil.ReadFile(expectedpath) c.Assert(err, gc.Not(gc.IsNil)) } func (s *filestorageSuite) TestPutTmpDir(c *gc.C) { // Put should create and clean up the temporary directory err := s.writer.Put("test-write", bytes.NewReader(nil), 0) c.Assert(err, gc.IsNil) _, err = os.Stat(s.dir + "/.tmp") c.Assert(err, jc.Satisfies, os.IsNotExist) // To deal with recovering from hard failure, we // don't care if the temporary directory already exists. It // still removes it, though. err = os.Mkdir(s.dir+"/.tmp", 0755) c.Assert(err, gc.IsNil) err = s.writer.Put("test-write", bytes.NewReader(nil), 0) c.Assert(err, gc.IsNil) _, err = os.Stat(s.dir + "/.tmp") c.Assert(err, jc.Satisfies, os.IsNotExist) } func (s *filestorageSuite) TestPathRelativeToHome(c *gc.C) { homeDir := osenv.Home() tempDir, err := ioutil.TempDir(homeDir, "") c.Assert(err, gc.IsNil) defer os.RemoveAll(tempDir) dirName := strings.Replace(tempDir, homeDir, "", -1) reader, err := filestorage.NewFileStorageReader(filepath.Join("~", dirName)) c.Assert(err, gc.IsNil) url, err := reader.URL("") c.Assert(err, gc.IsNil) c.Assert(url, gc.Equals, "file://"+filepath.Join(homeDir, dirName)) } func (s *filestorageSuite) TestRelativePath(c *gc.C) { dir := c.MkDir() err := os.MkdirAll(filepath.Join(dir, "a", "b", "c"), os.ModePerm) c.Assert(err, gc.IsNil) cwd, err := os.Getwd() c.Assert(err, gc.IsNil) err = os.Chdir(filepath.Join(dir, "a", "b", "c")) c.Assert(err, gc.IsNil) defer os.Chdir(cwd) reader, err := filestorage.NewFileStorageReader("../..") c.Assert(err, gc.IsNil) url, err := reader.URL("") c.Assert(err, gc.IsNil) c.Assert(url, gc.Equals, "file://"+dir+"/a") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/filestorage/filestorage.go��������������������0000644�0000153�0000161�00000010700�12321735642�027665� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package filestorage import ( "fmt" "io" "io/ioutil" "os" "path/filepath" "sort" "strings" "launchpad.net/juju-core/environs/storage" coreerrors "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" ) // fileStorageReader implements StorageReader backed // by the local filesystem. type fileStorageReader struct { path string } // NewFileStorageReader returns a new storage reader for // a directory inside the local file system. func NewFileStorageReader(path string) (reader storage.StorageReader, err error) { var p string if p, err = utils.NormalizePath(path); err != nil { return nil, err } if p, err = filepath.Abs(p); err != nil { return nil, err } fi, err := os.Stat(p) if err != nil { return nil, err } if !fi.Mode().IsDir() { return nil, fmt.Errorf("specified source path is not a directory: %s", path) } return &fileStorageReader{p}, nil } func (f *fileStorageReader) fullPath(name string) string { return filepath.Join(f.path, name) } // Get implements storage.StorageReader.Get. func (f *fileStorageReader) Get(name string) (io.ReadCloser, error) { if isInternalPath(name) { return nil, &os.PathError{ Op: "Get", Path: name, Err: os.ErrNotExist, } } filename := f.fullPath(name) fi, err := os.Stat(filename) if err != nil { if os.IsNotExist(err) { err = coreerrors.NewNotFoundError(err, "") } return nil, err } else if fi.IsDir() { return nil, coreerrors.NotFoundf("no such file with name %q", name) } file, err := os.Open(filename) if err != nil { return nil, err } return file, nil } // isInternalPath returns true if a path should be hidden from user visibility // filestorage uses ".tmp/" as a staging directory for uploads, so we don't // want it to be visible func isInternalPath(path string) bool { // This blocks both ".tmp", ".tmp/foo" but also ".tmpdir", better to be // overly restrictive to start with return strings.HasPrefix(path, ".tmp") } // List implements storage.StorageReader.List. func (f *fileStorageReader) List(prefix string) ([]string, error) { var names []string if isInternalPath(prefix) { return names, nil } prefix = filepath.Join(f.path, prefix) dir := filepath.Dir(prefix) err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && strings.HasPrefix(path, prefix) { names = append(names, path[len(f.path)+1:]) } return nil }) if err != nil && !os.IsNotExist(err) { return nil, err } sort.Strings(names) return names, nil } // URL implements storage.StorageReader.URL. func (f *fileStorageReader) URL(name string) (string, error) { return "file://" + filepath.Join(f.path, name), nil } // ConsistencyStrategy implements storage.StorageReader.ConsistencyStrategy. func (f *fileStorageReader) DefaultConsistencyStrategy() utils.AttemptStrategy { return utils.AttemptStrategy{} } // ShouldRetry is specified in the StorageReader interface. func (f *fileStorageReader) ShouldRetry(err error) bool { return false } type fileStorageWriter struct { fileStorageReader } // NewFileStorageWriter returns a new read/write storag for // a directory inside the local file system. func NewFileStorageWriter(path string) (storage.Storage, error) { reader, err := NewFileStorageReader(path) if err != nil { return nil, err } return &fileStorageWriter{*reader.(*fileStorageReader)}, nil } func (f *fileStorageWriter) Put(name string, r io.Reader, length int64) error { if isInternalPath(name) { return &os.PathError{ Op: "Put", Path: name, Err: os.ErrPermission, } } fullpath := f.fullPath(name) dir := filepath.Dir(fullpath) if err := os.MkdirAll(dir, 0755); err != nil { return err } tmpdir := filepath.Join(f.path, ".tmp") if err := os.MkdirAll(tmpdir, 0755); err != nil { return err } defer os.Remove(tmpdir) // Write to a temporary file first, and then move (atomically). file, err := ioutil.TempFile(tmpdir, "juju-filestorage-") if err != nil { return err } _, err = io.CopyN(file, r, length) file.Close() if err != nil { os.Remove(file.Name()) return err } return utils.ReplaceFile(file.Name(), fullpath) } func (f *fileStorageWriter) Remove(name string) error { fullpath := f.fullPath(name) err := os.Remove(fullpath) if os.IsNotExist(err) { err = nil } return err } func (f *fileStorageWriter) RemoveAll() error { return storage.RemoveAll(f) } ����������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/config_test.go��������������������������������0000644�0000153�0000161�00000027407�12321735642�025375� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs_test import ( "fmt" "os" "path/filepath" "sort" "strings" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/provider/dummy" _ "launchpad.net/juju-core/provider/manual" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type suite struct { testbase.LoggingSuite } var _ = gc.Suite(&suite{}) func (s *suite) TearDownTest(c *gc.C) { dummy.Reset() s.LoggingSuite.TearDownTest(c) } var invalidConfigTests = []struct { env string err string }{ {"'", "YAML error:.*"}, {` default: unknown environments: only: type: unknown `, `default environment .* does not exist`, }, } func (*suite) TestInvalidConfig(c *gc.C) { for i, t := range invalidConfigTests { c.Logf("running test %v", i) _, err := environs.ReadEnvironsBytes([]byte(t.env)) c.Check(err, gc.ErrorMatches, t.err) } } var invalidEnvTests = []struct { env string name string err string }{ {` environments: only: foo: bar `, "", `environment "only" has no type`, }, {` environments: only: foo: bar `, "only", `environment "only" has no type`, }, {` environments: only: foo: bar type: crazy `, "only", `environment "only" has an unknown provider type "crazy"`, }, } func (*suite) TestInvalidEnv(c *gc.C) { defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore() for i, t := range invalidEnvTests { c.Logf("running test %v", i) es, err := environs.ReadEnvironsBytes([]byte(t.env)) c.Check(err, gc.IsNil) cfg, err := es.Config(t.name) c.Check(err, gc.ErrorMatches, t.err) c.Check(cfg, gc.IsNil) } } func (*suite) TestNoWarningForDeprecatedButUnusedEnv(c *gc.C) { // This tests that a config that has a deprecated field doesn't // generate a Warning if we don't actually ask for that environment. // However, we can only really trigger that when we have a deprecated // field. If support for the field is removed entirely, another // mechanism will need to be used defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore() content := ` environments: valid: type: dummy state-server: false deprecated: type: dummy state-server: false tools-url: aknowndeprecatedfield ` tw := &loggo.TestWriter{} // we only capture Warning or above c.Assert(loggo.RegisterWriter("invalid-env-tester", tw, loggo.WARNING), gc.IsNil) defer loggo.RemoveWriter("invalid-env-tester") envs, err := environs.ReadEnvironsBytes([]byte(content)) c.Check(err, gc.IsNil) names := envs.Names() sort.Strings(names) c.Check(names, gc.DeepEquals, []string{"deprecated", "valid"}) // There should be no warning in the log c.Check(tw.Log, gc.HasLen, 0) // Now we actually grab the 'valid' entry _, err = envs.Config("valid") c.Check(err, gc.IsNil) // And still we have no warnings c.Check(tw.Log, gc.HasLen, 0) // Only once we grab the deprecated one do we see any warnings _, err = envs.Config("deprecated") c.Check(err, gc.IsNil) c.Check(tw.Log, gc.HasLen, 1) } func (*suite) TestNoHomeBeforeConfig(c *gc.C) { // Test that we don't actually need HOME set until we call envs.Config() // Because of this, we intentionally do *not* call testing.MakeFakeHomeNoEnvironments() content := ` environments: valid: type: dummy amazon: type: ec2 ` _, err := environs.ReadEnvironsBytes([]byte(content)) c.Check(err, gc.IsNil) } func (*suite) TestNoEnv(c *gc.C) { defer testing.MakeFakeHomeNoEnvironments(c).Restore() es, err := environs.ReadEnvirons("") c.Assert(es, gc.IsNil) c.Assert(err, jc.Satisfies, environs.IsNoEnv) } var configTests = []struct { env string check func(c *gc.C, envs *environs.Environs) }{ {` environments: only: type: dummy state-server: false `, func(c *gc.C, envs *environs.Environs) { cfg, err := envs.Config("") c.Assert(err, gc.IsNil) c.Assert(cfg.Name(), gc.Equals, "only") }}, {` default: invalid environments: valid: type: dummy state-server: false invalid: type: crazy `, func(c *gc.C, envs *environs.Environs) { cfg, err := envs.Config("") c.Assert(err, gc.ErrorMatches, `environment "invalid" has an unknown provider type "crazy"`) c.Assert(cfg, gc.IsNil) cfg, err = envs.Config("valid") c.Assert(err, gc.IsNil) c.Assert(cfg.Name(), gc.Equals, "valid") }}, {` environments: one: type: dummy state-server: false two: type: dummy state-server: false `, func(c *gc.C, envs *environs.Environs) { cfg, err := envs.Config("") c.Assert(err, gc.ErrorMatches, `no default environment found`) c.Assert(cfg, gc.IsNil) }}, } func (*suite) TestConfig(c *gc.C) { defer testing.MakeFakeHomeNoEnvironments(c, "only", "valid", "one", "two").Restore() for i, t := range configTests { c.Logf("running test %v", i) envs, err := environs.ReadEnvironsBytes([]byte(t.env)) c.Assert(err, gc.IsNil) t.check(c, envs) } } func (*suite) TestDefaultConfigFile(c *gc.C) { defer testing.MakeEmptyFakeHome(c).Restore() env := ` environments: only: type: dummy state-server: false authorized-keys: i-am-a-key ` outfile, err := environs.WriteEnvirons("", env) c.Assert(err, gc.IsNil) path := testing.HomePath(".juju", "environments.yaml") c.Assert(path, gc.Equals, outfile) envs, err := environs.ReadEnvirons("") c.Assert(err, gc.IsNil) cfg, err := envs.Config("") c.Assert(err, gc.IsNil) c.Assert(cfg.Name(), gc.Equals, "only") } func (*suite) TestConfigPerm(c *gc.C) { defer testing.MakeSampleHome(c).Restore() path := testing.HomePath(".juju") info, err := os.Lstat(path) c.Assert(err, gc.IsNil) oldPerm := info.Mode().Perm() env := ` environments: only: type: dummy state-server: false authorized-keys: i-am-a-key ` outfile, err := environs.WriteEnvirons("", env) c.Assert(err, gc.IsNil) info, err = os.Lstat(outfile) c.Assert(err, gc.IsNil) c.Assert(info.Mode().Perm(), gc.Equals, os.FileMode(0600)) info, err = os.Lstat(filepath.Dir(outfile)) c.Assert(err, gc.IsNil) c.Assert(info.Mode().Perm(), gc.Equals, oldPerm) } func (*suite) TestNamedConfigFile(c *gc.C) { defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore() env := ` environments: only: type: dummy state-server: false authorized-keys: i-am-a-key ` path := filepath.Join(c.MkDir(), "a-file") outfile, err := environs.WriteEnvirons(path, env) c.Assert(err, gc.IsNil) c.Assert(path, gc.Equals, outfile) envs, err := environs.ReadEnvirons(path) c.Assert(err, gc.IsNil) cfg, err := envs.Config("") c.Assert(err, gc.IsNil) c.Assert(cfg.Name(), gc.Equals, "only") } func inMap(attrs testing.Attrs, attr string) bool { _, ok := attrs[attr] return ok } func (*suite) TestBootstrapConfig(c *gc.C) { defer testing.MakeFakeHomeNoEnvironments(c, "bladaam").Restore() attrs := dummySampleConfig().Merge(testing.Attrs{ "agent-version": "1.2.3", }) c.Assert(inMap(attrs, "secret"), jc.IsTrue) c.Assert(inMap(attrs, "ca-private-key"), jc.IsTrue) c.Assert(inMap(attrs, "admin-secret"), jc.IsTrue) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) c.Assert(err, gc.IsNil) cfg1, err := environs.BootstrapConfig(cfg) c.Assert(err, gc.IsNil) expect := cfg.AllAttrs() expect["admin-secret"] = "" expect["ca-private-key"] = "" c.Assert(cfg1.AllAttrs(), gc.DeepEquals, expect) } type dummyProvider struct { environs.EnvironProvider } func (s *suite) TestRegisterProvider(c *gc.C) { s.PatchValue(environs.Providers, make(map[string]environs.EnvironProvider)) s.PatchValue(environs.ProviderAliases, make(map[string]string)) type step struct { name string aliases []string err string } type test []step tests := []test{ []step{{ name: "providerName", }}, []step{{ name: "providerName", aliases: []string{"providerName"}, err: "juju: duplicate provider alias \"providerName\"", }}, []step{{ name: "providerName", aliases: []string{"providerAlias", "providerAlias"}, err: "juju: duplicate provider alias \"providerAlias\"", }}, []step{{ name: "providerName", aliases: []string{"providerAlias1", "providerAlias2"}, }}, []step{{ name: "providerName", }, { name: "providerName", err: "juju: duplicate provider name \"providerName\"", }}, []step{{ name: "providerName1", }, { name: "providerName2", aliases: []string{"providerName"}, }}, []step{{ name: "providerName1", }, { name: "providerName2", aliases: []string{"providerName1"}, err: "juju: duplicate provider alias \"providerName1\"", }}, } registerProvider := func(name string, aliases []string) (err error) { defer func() { err, _ = recover().(error) }() registered := &dummyProvider{} environs.RegisterProvider(name, registered, aliases...) p, err := environs.Provider(name) c.Assert(err, gc.IsNil) c.Assert(p, gc.Equals, registered) for _, alias := range aliases { p, err := environs.Provider(alias) c.Assert(err, gc.IsNil) c.Assert(p, gc.Equals, registered) c.Assert(p, gc.Equals, registered) } return nil } for i, test := range tests { c.Logf("test %d: %v", i, test) for k := range *environs.Providers { delete(*environs.Providers, k) } for k := range *environs.ProviderAliases { delete(*environs.ProviderAliases, k) } for _, step := range test { err := registerProvider(step.name, step.aliases) if step.err == "" { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, step.err) } } } } type ConfigDeprecationSuite struct { suite writer *loggo.TestWriter } var _ = gc.Suite(&ConfigDeprecationSuite{}) func (s *ConfigDeprecationSuite) setupLogger(c *gc.C) func() { var err error s.writer = &loggo.TestWriter{} err = loggo.RegisterWriter("test", s.writer, loggo.WARNING) c.Assert(err, gc.IsNil) return func() { _, _, err := loggo.RemoveWriter("test") c.Assert(err, gc.IsNil) } } func (s *ConfigDeprecationSuite) checkDeprecationWarning(c *gc.C, attrs testing.Attrs, expectedMsg string) { defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore() content := ` environments: deprecated: type: dummy state-server: false ` restore := s.setupLogger(c) defer restore() envs, err := environs.ReadEnvironsBytes([]byte(content)) c.Check(err, gc.IsNil) environs.UpdateEnvironAttrs(envs, "deprecated", attrs) _, err = envs.Config("deprecated") c.Check(err, gc.IsNil) c.Assert(s.writer.Log, gc.HasLen, 1) stripped := strings.Replace(s.writer.Log[0].Message, "\n", "", -1) c.Assert(stripped, gc.Matches, expectedMsg) } func (s *ConfigDeprecationSuite) TestDeprecatedToolsURLWarning(c *gc.C) { attrs := testing.Attrs{ "tools-url": "aknowndeprecatedfield", } expected := fmt.Sprintf(`.*Config attribute "tools-url" \(aknowndeprecatedfield\) is deprecated\.` + `The location to find tools is now specified using the "tools-metadata-url" attribute.*`) s.checkDeprecationWarning(c, attrs, expected) } func (s *ConfigDeprecationSuite) TestDeprecatedToolsURLWithNewURLWarning(c *gc.C) { attrs := testing.Attrs{ "tools-url": "aknowndeprecatedfield", "tools-metadata-url": "newvalue", } expected := fmt.Sprintf( `.*Config attribute "tools-url" \(aknowndeprecatedfield\) is deprecated and will be ignored since` + `the new tools URL attribute "tools-metadata-url".*`) s.checkDeprecationWarning(c, attrs, expected) } func (s *ConfigDeprecationSuite) TestDeprecatedTypeNullWarning(c *gc.C) { attrs := testing.Attrs{"type": "null"} expected := `Provider type \"null\" has been renamed to \"manual\"\.Please update your environment configuration\.` s.checkDeprecationWarning(c, attrs, expected) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/config.go�������������������������������������0000644�0000153�0000161�00000017762�12321735642�024341� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs import ( "fmt" "io/ioutil" "os" "path/filepath" "github.com/juju/loggo" "launchpad.net/goyaml" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju/osenv" ) var logger = loggo.GetLogger("juju.environs") // environ holds information about one environment. type environ struct { config *config.Config err error // an error if the config data could not be parsed. } // Environs holds information about each named environment // in an environments.yaml file. type Environs struct { Default string // The name of the default environment. rawEnvirons map[string]map[string]interface{} } // Names returns the list of environment names. func (e *Environs) Names() (names []string) { for name := range e.rawEnvirons { names = append(names, name) } return } func validateEnvironmentKind(rawEnviron map[string]interface{}) error { kind, _ := rawEnviron["type"].(string) if kind == "" { return fmt.Errorf("environment %q has no type", rawEnviron["name"]) } p, _ := Provider(kind) if p == nil { return fmt.Errorf("environment %q has an unknown provider type %q", rawEnviron["name"], kind) } return nil } // Config returns the environment configuration for the environment // with the given name. If the configuration is not // found, an errors.NotFoundError is returned. func (envs *Environs) Config(name string) (*config.Config, error) { if name == "" { name = envs.Default if name == "" { return nil, fmt.Errorf("no default environment found") } } attrs, ok := envs.rawEnvirons[name] if !ok { return nil, errors.NotFoundf("environment %q", name) } if err := validateEnvironmentKind(attrs); err != nil { return nil, err } // If deprecated config attributes are used, log warnings so the user can know // that they need to be fixed. if oldToolsURL := attrs["tools-url"]; oldToolsURL != nil && oldToolsURL.(string) != "" { _, newToolsSpecified := attrs["tools-metadata-url"] var msg string if newToolsSpecified { msg = fmt.Sprintf( "Config attribute %q (%v) is deprecated and will be ignored since\n"+ "the new tools URL attribute %q has also been used.\n"+ "The attribute %q should be removed from your configuration.", "tools-url", oldToolsURL, "tools-metadata-url", "tools-url") } else { msg = fmt.Sprintf( "Config attribute %q (%v) is deprecated.\n"+ "The location to find tools is now specified using the %q attribute.\n"+ "Your configuration should be updated to set %q as follows\n%v: %v.", "tools-url", oldToolsURL, "tools-metadata-url", "tools-metadata-url", "tools-metadata-url", oldToolsURL) } logger.Warningf(msg) } // null has been renamed to manual (with an alias for existing config). if oldType, _ := attrs["type"].(string); oldType == "null" { logger.Warningf( "Provider type \"null\" has been renamed to \"manual\".\n" + "Please update your environment configuration.", ) } cfg, err := config.New(config.UseDefaults, attrs) if err != nil { return nil, err } return cfg, nil } // providers maps from provider type to EnvironProvider for // each registered provider type. // // providers should not typically be used directly; the // Provider function will handle provider type aliases, // and should be used instead. var providers = make(map[string]EnvironProvider) // providerAliases is a map of provider type aliases. var providerAliases = make(map[string]string) // RegisterProvider registers a new environment provider. Name gives the name // of the provider, and p the interface to that provider. // // RegisterProvider will panic if the provider name or any of the aliases // are registered more than once. func RegisterProvider(name string, p EnvironProvider, alias ...string) { if providers[name] != nil || providerAliases[name] != "" { panic(fmt.Errorf("juju: duplicate provider name %q", name)) } providers[name] = p for _, alias := range alias { if providers[alias] != nil || providerAliases[alias] != "" { panic(fmt.Errorf("juju: duplicate provider alias %q", alias)) } providerAliases[alias] = name } } // Provider returns the previously registered provider with the given type. func Provider(providerType string) (EnvironProvider, error) { if alias, ok := providerAliases[providerType]; ok { providerType = alias } p, ok := providers[providerType] if !ok { return nil, fmt.Errorf("no registered provider for %q", providerType) } return p, nil } // ReadEnvironsBytes parses the contents of an environments.yaml file // and returns its representation. An environment with an unknown type // will only generate an error when New is called for that environment. // Attributes for environments with known types are checked. func ReadEnvironsBytes(data []byte) (*Environs, error) { var raw struct { Default string Environments map[string]map[string]interface{} } err := goyaml.Unmarshal(data, &raw) if err != nil { return nil, err } if raw.Default != "" && raw.Environments[raw.Default] == nil { return nil, fmt.Errorf("default environment %q does not exist", raw.Default) } if raw.Default == "" { // If there's a single environment, then we get the default // automatically. if len(raw.Environments) == 1 { for name := range raw.Environments { raw.Default = name break } } } for name, attrs := range raw.Environments { // store the name of the this environment in the config itself // so that providers can see it. attrs["name"] = name } return &Environs{raw.Default, raw.Environments}, nil } func environsPath(path string) string { if path == "" { path = osenv.JujuHomePath("environments.yaml") } return path } // NoEnvError indicates the default environment config file is missing. type NoEnvError struct { error } // IsNoEnv reports whether err is a NoEnvError. func IsNoEnv(err error) bool { _, ok := err.(NoEnvError) return ok } // ReadEnvirons reads the juju environments.yaml file // and returns the result of running ParseEnvironments // on the file's contents. // If path is empty, $HOME/.juju/environments.yaml is used. func ReadEnvirons(path string) (*Environs, error) { environsFilepath := environsPath(path) data, err := ioutil.ReadFile(environsFilepath) if err != nil { if os.IsNotExist(err) { return nil, NoEnvError{err} } return nil, err } e, err := ReadEnvironsBytes(data) if err != nil { return nil, fmt.Errorf("cannot parse %q: %v", environsFilepath, err) } return e, nil } // WriteEnvirons creates a new juju environments.yaml file with the specified contents. func WriteEnvirons(path string, fileContents string) (string, error) { environsFilepath := environsPath(path) environsDir := filepath.Dir(environsFilepath) var info os.FileInfo var err error if info, err = os.Lstat(environsDir); os.IsNotExist(err) { if err = os.MkdirAll(environsDir, 0700); err != nil { return "", err } } else if err != nil { return "", err } else if info.Mode().Perm() != 0700 { logger.Warningf("permission of %q is %q", environsDir, info.Mode().Perm()) } if err := ioutil.WriteFile(environsFilepath, []byte(fileContents), 0600); err != nil { return "", err } // WriteFile does not change permissions of existing files. if err := os.Chmod(environsFilepath, 0600); err != nil { return "", err } return environsFilepath, nil } // BootstrapConfig returns a copy of the supplied configuration with the // admin-secret and ca-private-key attributes removed. If the resulting // config is not suitable for bootstrapping an environment, an error is // returned. func BootstrapConfig(cfg *config.Config) (*config.Config, error) { m := cfg.AllAttrs() // We never want to push admin-secret or the root CA private key to the cloud. delete(m, "admin-secret") delete(m, "ca-private-key") cfg, err := config.New(config.NoDefaults, m) if err != nil { return nil, err } if _, ok := cfg.AgentVersion(); !ok { return nil, fmt.Errorf("environment configuration has no agent-version") } return cfg, nil } ��������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/config/���������������������������������������0000755�0000153�0000161�00000000000�12321736000�023762� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/config/config_test.go�������������������������0000644�0000153�0000161�00000130314�12321735776�026642� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package config_test import ( "fmt" "regexp" stdtesting "testing" "time" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cert" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/schema" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) func Test(t *stdtesting.T) { gc.TestingT(t) } type ConfigSuite struct { testbase.LoggingSuite home string } var _ = gc.Suite(&ConfigSuite{}) func (s *ConfigSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) // Make sure that the defaults are used, which // is <root>=WARNING loggo.ResetLoggers() } // sampleConfig holds a configuration with all required // attributes set. var sampleConfig = testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "firewall-mode": config.FwInstance, "admin-secret": "foo", "unknown": "my-unknown", "ca-cert": caCert, "ssl-hostname-verification": true, "development": false, "state-port": 1234, "api-port": 4321, "syslog-port": 2345, "default-series": "precise", } type configTest struct { about string useDefaults config.Defaulting attrs map[string]interface{} err string } var configTests = []configTest{ { about: "The minimum good configuration", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", }, }, { about: "Metadata URLs", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "image-metadata-url": "image-url", "tools-metadata-url": "tools-metadata-url-value", }, }, { about: "Deprecated tools metadata URL used", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "tools-url": "tools-metadata-url-value", }, }, { about: "Deprecated tools metadata URL ignored", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "tools-metadata-url": "tools-metadata-url-value", "tools-url": "ignore-me", }, }, { about: "Explicit series", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "default-series": "my-series", }, }, { about: "Implicit series with empty value", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "default-series": "", }, }, { about: "Explicit logging", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "logging-config": "juju=INFO", }, }, { about: "Explicit authorized-keys", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, }, }, { about: "Load authorized-keys from path", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys-path": "~/.ssh/authorized_keys2", }, }, { about: "CA cert & key from path", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert-path": "cacert2.pem", "ca-private-key-path": "cakey2.pem", }, }, { about: "CA cert & key from path; cert attribute set too", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert-path": "cacert2.pem", "ca-cert": "ignored", "ca-private-key-path": "cakey2.pem", }, }, { about: "CA cert & key from ~ path", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert-path": "~/othercert.pem", "ca-private-key-path": "~/otherkey.pem", }, }, /* { about: "CA cert only from ~ path", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert-path": "~/othercert.pem", "ca-private-key": "", }, }, { about: "CA cert only as attribute", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert": caCert, "ca-private-key": "", }, }, */{ about: "CA cert and key as attributes", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert": caCert, "ca-private-key": caKey, }, }, { about: "Mismatched CA cert and key", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert": caCert, "ca-private-key": caKey2, }, err: "bad CA certificate/key in configuration: crypto/tls: private key does not match public key", }, { about: "Invalid CA cert", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert": invalidCACert, }, err: `bad CA certificate/key in configuration: (asn1:|ASN\.1) syntax error:.*`, }, { about: "Invalid CA key", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert": caCert, "ca-private-key": invalidCAKey, }, err: "bad CA certificate/key in configuration: crypto/tls:.*", }, /* { about: "No CA cert or key", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert": "", "ca-private-key": "", }, }, { about: "CA key but no cert", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert": "", "ca-private-key": caKey, }, err: "bad CA certificate/key in configuration: crypto/tls:.*", }, { about: "No CA key", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert": "foo", "ca-private-key": "", }, err: "bad CA certificate/key in configuration: no certificates found", }, */{ about: "CA cert specified as non-existent file", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-cert-path": "no-such-file", }, err: `open .*\.juju/no-such-file: .*`, }, { about: "CA key specified as non-existent file", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ca-private-key-path": "no-such-file", }, err: `open .*\.juju/no-such-file: .*`, }, { about: "Specified agent version", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "agent-version": "1.2.3", }, }, { about: "Specified development flag", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "development": true, }, }, { about: "Specified admin secret", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "development": false, "admin-secret": "pork", }, }, { about: "Invalid development flag", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "development": "invalid", }, err: `development: expected bool, got string\("invalid"\)`, }, { about: "Invalid agent version", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "agent-version": "2", }, err: `invalid agent version in environment configuration: "2"`, }, { about: "Missing type", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "name": "my-name", }, err: "type: expected string, got nothing", }, { about: "Empty type", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "name": "my-name", "type": "", }, err: "empty type in environment configuration", }, { about: "Missing name", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", }, err: "name: expected string, got nothing", }, { about: "Bad name, no slash", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "name": "foo/bar", "type": "my-type", }, err: "environment name contains unsafe characters", }, { about: "Bad name, no backslash", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "name": "foo\\bar", "type": "my-type", }, err: "environment name contains unsafe characters", }, { about: "Empty name", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "", }, err: "empty name in environment configuration", }, { about: "Default firewall mode", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", }, }, { about: "Empty firewall mode", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "firewall-mode": "", }, }, { about: "Instance firewall mode", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "firewall-mode": config.FwInstance, }, }, { about: "Global firewall mode", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "firewall-mode": config.FwGlobal, }, }, { about: "Illegal firewall mode", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "firewall-mode": "illegal", }, err: "invalid firewall mode in environment configuration: .*", }, { about: "ssl-hostname-verification off", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ssl-hostname-verification": false, }, }, { about: "ssl-hostname-verification incorrect", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "ssl-hostname-verification": "yes please", }, err: `ssl-hostname-verification: expected bool, got string\("yes please"\)`, }, { about: "provisioner-safe-mode off", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "provisioner-safe-mode": false, }, }, { about: "provisioner-safe-mode on", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "provisioner-safe-mode": true, }, }, { about: "provisioner-safe-mode incorrect", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "provisioner-safe-mode": "yes please", }, err: `provisioner-safe-mode: expected bool, got string\("yes please"\)`, }, { about: "default image stream", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", }, }, { about: "explicit image stream", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "image-stream": "daily", }, }, { about: "Explicit state port", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "state-port": 37042, }, }, { about: "Invalid state port", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "state-port": "illegal", }, err: `state-port: expected number, got string\("illegal"\)`, }, { about: "Explicit API port", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "api-port": 77042, }, }, { about: "Invalid API port", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "api-port": "illegal", }, err: `api-port: expected number, got string\("illegal"\)`, }, { about: "Explicit syslog port", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "syslog-port": 3456, }, }, { about: "Invalid syslog port", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "syslog-port": "illegal", }, err: `syslog-port: expected number, got string\("illegal"\)`, }, { about: "Explicit bootstrap timeout", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "bootstrap-timeout": 300, }, }, { about: "Invalid bootstrap timeout", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "bootstrap-timeout": "illegal", }, err: `bootstrap-timeout: expected number, got string\("illegal"\)`, }, { about: "Explicit bootstrap retry delay", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "bootstrap-retry-delay": 5, }, }, { about: "Invalid bootstrap retry delay", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "bootstrap-retry-delay": "illegal", }, err: `bootstrap-retry-delay: expected number, got string\("illegal"\)`, }, { about: "Explicit bootstrap addresses delay", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "bootstrap-addresses-delay": 15, }, }, { about: "Invalid bootstrap addresses delay", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "bootstrap-addresses-delay": "illegal", }, err: `bootstrap-addresses-delay: expected number, got string\("illegal"\)`, }, { about: "Invalid logging configuration", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "logging-config": "foo=bar", }, err: `unknown severity level "bar"`, }, { about: "Sample configuration", useDefaults: config.UseDefaults, attrs: sampleConfig, }, { about: "No defaults: sample configuration", useDefaults: config.NoDefaults, attrs: sampleConfig, }, { about: "No defaults: with ca-cert-path", useDefaults: config.NoDefaults, attrs: sampleConfig.Merge(testing.Attrs{"ca-cert-path": "arble"}), err: `attribute "ca-cert-path" is not allowed in configuration`, }, { about: "No defaults: with ca-private-key-path", useDefaults: config.NoDefaults, attrs: sampleConfig.Merge(testing.Attrs{"ca-private-key-path": "arble"}), err: `attribute "ca-private-key-path" is not allowed in configuration`, }, { about: "No defaults: with authorized-keys-path", useDefaults: config.NoDefaults, attrs: sampleConfig.Merge(testing.Attrs{"authorized-keys-path": "arble"}), err: `attribute "authorized-keys-path" is not allowed in configuration`, }, { about: "No defaults: missing authorized-keys", useDefaults: config.NoDefaults, attrs: sampleConfig.Delete("authorized-keys"), err: `authorized-keys missing from environment configuration`, }, { about: "Config settings from juju 1.13.3 actual installation", useDefaults: config.NoDefaults, attrs: map[string]interface{}{ "name": "sample", "development": false, "admin-secret": "", "ssl-hostname-verification": true, "authorized-keys": "ssh-rsa mykeys rog@rog-x220\n", "control-bucket": "rog-some-control-bucket", "region": "us-east-1", "image-metadata-url": "", "ca-private-key": "", "default-series": "precise", "tools-metadata-url": "", "secret-key": "a-secret-key", "access-key": "an-access-key", "agent-version": "1.13.2", "ca-cert": caCert, "firewall-mode": "instance", "type": "ec2", }, }, { about: "Provider type null is replaced with manual", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "null", "name": "my-name", }, }, { about: "TestMode flag specified", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "test-mode": true, }, }, authTokenConfigTest("token=value, tokensecret=value", true), authTokenConfigTest("token=value, ", true), authTokenConfigTest("token=value, \ttokensecret=value", true), authTokenConfigTest("", true), authTokenConfigTest("token=value, tokensecret=value, \t", true), authTokenConfigTest("=", false), authTokenConfigTest("tokenvalue", false), authTokenConfigTest("token=value, sometoken=", false), authTokenConfigTest("token==value", false), authTokenConfigTest(" token=value", false), authTokenConfigTest("=value", false), authTokenConfigTest("token=value, =z", false), authTokenConfigTest("token=value =z", false), authTokenConfigTest("\t", false), missingAttributeNoDefault("firewall-mode"), missingAttributeNoDefault("development"), missingAttributeNoDefault("ssl-hostname-verification"), // TODO(rog) reinstate these tests when we can lose // backward compatibility with pre-1.13 config. // missingAttributeNoDefault("state-port"), // missingAttributeNoDefault("api-port"), } // authTokenConfigTest returns a config test that checks // that a configuration with the given auth token // will pass or fail, depending on the value of ok. func authTokenConfigTest(token string, ok bool) configTest { var testName string var err string if ok { testName = fmt.Sprintf("Valid auth token test: %q", token) } else { testName = fmt.Sprintf("Invalid auth token test: %q", token) err = fmt.Sprintf("charm store auth token needs to be a set of key-value pairs, not %q", token) } return configTest{ about: testName, useDefaults: config.UseDefaults, attrs: sampleConfig.Merge(testing.Attrs{"charm-store-auth": token}), err: regexp.QuoteMeta(err), } } func missingAttributeNoDefault(attrName string) configTest { return configTest{ about: fmt.Sprintf("No default: missing %s", attrName), useDefaults: config.NoDefaults, attrs: sampleConfig.Delete(attrName), err: fmt.Sprintf("%s: expected [a-z]+, got nothing", attrName), } } type testFile struct { name, data string } func (*ConfigSuite) TestConfig(c *gc.C) { files := []testing.TestFile{ {".ssh/id_dsa.pub", "dsa"}, {".ssh/id_rsa.pub", "rsa\n"}, {".ssh/identity.pub", "identity"}, {".ssh/authorized_keys", "auth0\n# first\nauth1\n\n"}, {".ssh/authorized_keys2", "auth2\nauth3\n"}, {".juju/my-name-cert.pem", caCert}, {".juju/my-name-private-key.pem", caKey}, {".juju/cacert2.pem", caCert2}, {".juju/cakey2.pem", caKey2}, {"othercert.pem", caCert3}, {"otherkey.pem", caKey3}, } h := testing.MakeFakeHomeWithFiles(c, files) defer h.Restore() for i, test := range configTests { c.Logf("test %d. %s", i, test.about) test.check(c, h) } } var noCertFilesTests = []configTest{ { about: "Unspecified certificate and key", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, }, }, { about: "Unspecified certificate, specified key", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "ca-private-key": caKey, }, err: "bad CA certificate/key in configuration: crypto/tls:.*", }, } func (*ConfigSuite) TestConfigNoCertFiles(c *gc.C) { h := testing.MakeEmptyFakeHome(c) defer h.Restore() for i, test := range noCertFilesTests { c.Logf("test %d. %s", i, test.about) test.check(c, h) } } var emptyCertFilesTests = []configTest{ { about: "Cert unspecified; key specified", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "ca-private-key": caKey, }, err: `file ".*/my-name-cert.pem" is empty`, }, { about: "Cert and key unspecified", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, }, err: `file ".*/my-name-cert.pem" is empty`, }, { about: "Cert specified, key unspecified", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "ca-cert": caCert, }, err: `file ".*/my-name-private-key.pem" is empty`, }, /* { about: "Cert and key specified as absent", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "ca-cert": "", "ca-private-key": "", }, }, { about: "Cert specified as absent", useDefaults: config.UseDefaults, attrs: testing.Attrs{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "ca-cert": "", }, err: "bad CA certificate/key in configuration: crypto/tls: .*", }, */ } func (*ConfigSuite) TestConfigEmptyCertFiles(c *gc.C) { files := []testing.TestFile{ {".juju/my-name-cert.pem", ""}, {".juju/my-name-private-key.pem", ""}, } h := testing.MakeFakeHomeWithFiles(c, files) defer h.Restore() for i, test := range emptyCertFilesTests { c.Logf("test %d. %s", i, test.about) test.check(c, h) } } func (test configTest) check(c *gc.C, home *testing.FakeHome) { cfg, err := config.New(test.useDefaults, test.attrs) if test.err != "" { c.Check(cfg, gc.IsNil) c.Assert(err, gc.ErrorMatches, test.err) return } c.Assert(err, gc.IsNil) typ, _ := test.attrs["type"].(string) // "null" has been deprecated in favour of "manual", // and is automatically switched. if typ == "null" { typ = "manual" } name, _ := test.attrs["name"].(string) c.Assert(cfg.Type(), gc.Equals, typ) c.Assert(cfg.Name(), gc.Equals, name) agentVersion, ok := cfg.AgentVersion() if s := test.attrs["agent-version"]; s != nil { c.Assert(ok, jc.IsTrue) c.Assert(agentVersion, gc.Equals, version.MustParse(s.(string))) } else { c.Assert(ok, jc.IsFalse) c.Assert(agentVersion, gc.Equals, version.Zero) } if statePort, ok := test.attrs["state-port"]; ok { c.Assert(cfg.StatePort(), gc.Equals, statePort) } if apiPort, ok := test.attrs["api-port"]; ok { c.Assert(cfg.APIPort(), gc.Equals, apiPort) } if syslogPort, ok := test.attrs["syslog-port"]; ok { c.Assert(cfg.SyslogPort(), gc.Equals, syslogPort) } dev, _ := test.attrs["development"].(bool) c.Assert(cfg.Development(), gc.Equals, dev) testmode, _ := test.attrs["test-mode"].(bool) c.Assert(cfg.TestMode(), gc.Equals, testmode) series, _ := test.attrs["default-series"].(string) if defaultSeries, ok := cfg.DefaultSeries(); ok { c.Assert(defaultSeries, gc.Equals, series) } else { c.Assert(series, gc.Equals, "") c.Assert(defaultSeries, gc.Equals, "") } if m, _ := test.attrs["firewall-mode"].(string); m != "" { c.Assert(cfg.FirewallMode(), gc.Equals, m) } if secret, _ := test.attrs["admin-secret"].(string); secret != "" { c.Assert(cfg.AdminSecret(), gc.Equals, secret) } if path, _ := test.attrs["authorized-keys-path"].(string); path != "" { c.Assert(cfg.AuthorizedKeys(), gc.Equals, home.FileContents(c, path)) c.Assert(cfg.AllAttrs()["authorized-keys-path"], gc.IsNil) } else if keys, _ := test.attrs["authorized-keys"].(string); keys != "" { c.Assert(cfg.AuthorizedKeys(), gc.Equals, keys) } else { // Content of all the files that are read by default. c.Assert(cfg.AuthorizedKeys(), gc.Equals, "dsa\nrsa\nidentity\n") } cert, certPresent := cfg.CACert() if path, _ := test.attrs["ca-cert-path"].(string); path != "" { c.Assert(certPresent, jc.IsTrue) c.Assert(string(cert), gc.Equals, home.FileContents(c, path)) } else if v, ok := test.attrs["ca-cert"].(string); v != "" { c.Assert(certPresent, jc.IsTrue) c.Assert(string(cert), gc.Equals, v) } else if ok { c.Check(cert, gc.HasLen, 0) c.Assert(certPresent, jc.IsFalse) } else if bool(test.useDefaults) && home.FileExists(".juju/my-name-cert.pem") { c.Assert(certPresent, jc.IsTrue) c.Assert(string(cert), gc.Equals, home.FileContents(c, "my-name-cert.pem")) } else { c.Check(cert, gc.HasLen, 0) c.Assert(certPresent, jc.IsFalse) } key, keyPresent := cfg.CAPrivateKey() if path, _ := test.attrs["ca-private-key-path"].(string); path != "" { c.Assert(keyPresent, jc.IsTrue) c.Assert(string(key), gc.Equals, home.FileContents(c, path)) } else if v, ok := test.attrs["ca-private-key"].(string); v != "" { c.Assert(keyPresent, jc.IsTrue) c.Assert(string(key), gc.Equals, v) } else if ok { c.Check(key, gc.HasLen, 0) c.Assert(keyPresent, jc.IsFalse) } else if bool(test.useDefaults) && home.FileExists(".juju/my-name-private-key.pem") { c.Assert(keyPresent, jc.IsTrue) c.Assert(string(key), gc.Equals, home.FileContents(c, "my-name-private-key.pem")) } else { c.Check(key, gc.HasLen, 0) c.Assert(keyPresent, jc.IsFalse) } if v, ok := test.attrs["ssl-hostname-verification"]; ok { c.Assert(cfg.SSLHostnameVerification(), gc.Equals, v) } if v, ok := test.attrs["provisioner-safe-mode"]; ok { c.Assert(cfg.ProvisionerSafeMode(), gc.Equals, v) } else { c.Assert(cfg.ProvisionerSafeMode(), gc.Equals, false) } sshOpts := cfg.BootstrapSSHOpts() test.assertDuration( c, "bootstrap-timeout", sshOpts.Timeout, config.DefaultBootstrapSSHTimeout, ) test.assertDuration( c, "bootstrap-retry-delay", sshOpts.RetryDelay, config.DefaultBootstrapSSHRetryDelay, ) test.assertDuration( c, "bootstrap-addresses-delay", sshOpts.AddressesDelay, config.DefaultBootstrapSSHAddressesDelay, ) if v, ok := test.attrs["image-stream"]; ok { c.Assert(cfg.ImageStream(), gc.Equals, v) } else { c.Assert(cfg.ImageStream(), gc.Equals, "released") } url, urlPresent := cfg.ImageMetadataURL() if v, _ := test.attrs["image-metadata-url"].(string); v != "" { c.Assert(url, gc.Equals, v) c.Assert(urlPresent, jc.IsTrue) } else { c.Assert(urlPresent, jc.IsFalse) } toolsURL, urlPresent := cfg.ToolsURL() oldToolsURL, oldURLPresent := cfg.AllAttrs()["tools-url"] oldToolsURLAttrValue, oldURLAttrPresent := test.attrs["tools-url"] expectedToolsURLValue := test.attrs["tools-metadata-url"] if expectedToolsURLValue == nil { expectedToolsURLValue = oldToolsURLAttrValue } if expectedToolsURLValue != nil && expectedToolsURLValue != "" { c.Assert(expectedToolsURLValue, gc.Equals, "tools-metadata-url-value") c.Assert(toolsURL, gc.Equals, expectedToolsURLValue) c.Assert(urlPresent, jc.IsTrue) c.Assert(oldToolsURL, gc.Equals, expectedToolsURLValue) c.Assert(oldURLPresent, jc.IsTrue) } else { c.Assert(urlPresent, jc.IsFalse) c.Assert(oldURLAttrPresent, jc.IsFalse) c.Assert(oldToolsURL, gc.Equals, "") } } func (test configTest) assertDuration(c *gc.C, name string, actual time.Duration, defaultInSeconds int) { value, ok := test.attrs[name].(int) if !ok || value == 0 { c.Assert(actual, gc.Equals, time.Duration(defaultInSeconds)*time.Second) } else { c.Assert(actual, gc.Equals, time.Duration(value)*time.Second) } } func (s *ConfigSuite) TestConfigAttrs(c *gc.C) { // Normally this is handled by testing.FakeHome s.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, "") attrs := map[string]interface{}{ "type": "my-type", "name": "my-name", "authorized-keys": testing.FakeAuthKeys, "firewall-mode": config.FwInstance, "admin-secret": "foo", "unknown": "my-unknown", "ca-cert": caCert, "ssl-hostname-verification": true, "development": false, "provisioner-safe-mode": false, "state-port": 1234, "api-port": 4321, "syslog-port": 2345, "bootstrap-timeout": 3600, "bootstrap-retry-delay": 30, "bootstrap-addresses-delay": 10, "default-series": testing.FakeDefaultSeries, "charm-store-auth": "token=auth", "test-mode": false, } cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) // These attributes are added if not set. attrs["development"] = false attrs["logging-config"] = "<root>=WARNING;unit=DEBUG" attrs["ca-private-key"] = "" attrs["image-metadata-url"] = "" attrs["tools-metadata-url"] = "" attrs["tools-url"] = "" attrs["image-stream"] = "" // Default firewall mode is instance attrs["firewall-mode"] = string(config.FwInstance) c.Assert(cfg.AllAttrs(), jc.DeepEquals, attrs) c.Assert(cfg.UnknownAttrs(), jc.DeepEquals, map[string]interface{}{"unknown": "my-unknown"}) newcfg, err := cfg.Apply(map[string]interface{}{ "name": "new-name", "new-unknown": "my-new-unknown", }) c.Assert(err, gc.IsNil) attrs["name"] = "new-name" attrs["new-unknown"] = "my-new-unknown" c.Assert(newcfg.AllAttrs(), jc.DeepEquals, attrs) } type validationTest struct { about string new testing.Attrs old testing.Attrs err string } var validationTests = []validationTest{{ about: "Can't change the type", new: testing.Attrs{"type": "new-type"}, err: `cannot change type from "my-type" to "new-type"`, }, { about: "Can't change the name", new: testing.Attrs{"name": "new-name"}, err: `cannot change name from "my-name" to "new-name"`, }, { about: "Can set agent version", new: testing.Attrs{"agent-version": "1.9.13"}, }, { about: "Can change agent version", old: testing.Attrs{"agent-version": "1.9.13"}, new: testing.Attrs{"agent-version": "1.9.27"}, }, { about: "Can't clear agent version", old: testing.Attrs{"agent-version": "1.9.27"}, err: `cannot clear agent-version`, }, { about: "Can't change the firewall-mode", old: testing.Attrs{"firewall-mode": config.FwGlobal}, new: testing.Attrs{"firewall-mode": config.FwInstance}, err: `cannot change firewall-mode from "global" to "instance"`, }, { about: "Cannot change the state-port", old: testing.Attrs{"state-port": config.DefaultStatePort}, new: testing.Attrs{"state-port": 42}, err: `cannot change state-port from 37017 to 42`, }, { about: "Cannot change the api-port", old: testing.Attrs{"api-port": config.DefaultAPIPort}, new: testing.Attrs{"api-port": 42}, err: `cannot change api-port from 17070 to 42`, }, { about: "Can change the state-port from explicit-default to implicit-default", old: testing.Attrs{"state-port": config.DefaultStatePort}, }, { about: "Can change the api-port from explicit-default to implicit-default", old: testing.Attrs{"api-port": config.DefaultAPIPort}, }, { about: "Can change the state-port from implicit-default to explicit-default", new: testing.Attrs{"state-port": config.DefaultStatePort}, }, { about: "Can change the api-port from implicit-default to explicit-default", new: testing.Attrs{"api-port": config.DefaultAPIPort}, }, { about: "Cannot change the state-port from implicit-default to different value", new: testing.Attrs{"state-port": 42}, err: `cannot change state-port from 37017 to 42`, }, { about: "Cannot change the api-port from implicit-default to different value", new: testing.Attrs{"api-port": 42}, err: `cannot change api-port from 17070 to 42`, }, { about: "Cannot change the bootstrap-timeout from implicit-default to different value", new: testing.Attrs{"bootstrap-timeout": 5}, err: `cannot change bootstrap-timeout from 600 to 5`, }} func (*ConfigSuite) TestValidateChange(c *gc.C) { files := []testing.TestFile{ {".ssh/identity.pub", "identity"}, } h := testing.MakeFakeHomeWithFiles(c, files) defer h.Restore() for i, test := range validationTests { c.Logf("test %d: %s", i, test.about) newConfig := newTestConfig(c, test.new) oldConfig := newTestConfig(c, test.old) err := config.Validate(newConfig, oldConfig) if test.err == "" { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, test.err) } } } func makeFakeHome(c *gc.C) *testing.FakeHome { return testing.MakeFakeHomeWithFiles(c, []testing.TestFile{ {".ssh/id_rsa.pub", "rsa\n"}, {".juju/myenv-cert.pem", caCert}, {".juju/myenv-private-key.pem", caKey}, }) } func (*ConfigSuite) TestValidateUnknownAttrs(c *gc.C) { defer makeFakeHome(c).Restore() cfg, err := config.New(config.UseDefaults, map[string]interface{}{ "name": "myenv", "type": "other", "known": "this", "unknown": "that", }) // No fields: all attrs passed through. attrs, err := cfg.ValidateUnknownAttrs(nil, nil) c.Assert(err, gc.IsNil) c.Assert(attrs, gc.DeepEquals, map[string]interface{}{ "known": "this", "unknown": "that", }) // Valid field: that and other attrs passed through. fields := schema.Fields{"known": schema.String()} attrs, err = cfg.ValidateUnknownAttrs(fields, nil) c.Assert(err, gc.IsNil) c.Assert(attrs, gc.DeepEquals, map[string]interface{}{ "known": "this", "unknown": "that", }) // Default field: inserted. fields["default"] = schema.String() defaults := schema.Defaults{"default": "the other"} attrs, err = cfg.ValidateUnknownAttrs(fields, defaults) c.Assert(err, gc.IsNil) c.Assert(attrs, gc.DeepEquals, map[string]interface{}{ "known": "this", "unknown": "that", "default": "the other", }) // Invalid field: failure. fields["known"] = schema.Int() _, err = cfg.ValidateUnknownAttrs(fields, defaults) c.Assert(err, gc.ErrorMatches, `known: expected int, got string\("this"\)`) } func newTestConfig(c *gc.C, explicit testing.Attrs) *config.Config { final := testing.Attrs{"type": "my-type", "name": "my-name"} for key, value := range explicit { final[key] = value } result, err := config.New(config.UseDefaults, final) c.Assert(err, gc.IsNil) return result } func (*ConfigSuite) TestLoggingConfig(c *gc.C) { defer makeFakeHome(c).Restore() config := newTestConfig(c, testing.Attrs{ "logging-config": "<root>=WARNING;juju=DEBUG"}) c.Assert(config.LoggingConfig(), gc.Equals, "<root>=WARNING;juju=DEBUG;unit=DEBUG") } func (*ConfigSuite) TestLoggingConfigWithUnit(c *gc.C) { defer makeFakeHome(c).Restore() config := newTestConfig(c, testing.Attrs{ "logging-config": "<root>=WARNING;unit=INFO"}) c.Assert(config.LoggingConfig(), gc.Equals, "<root>=WARNING;unit=INFO") } func (s *ConfigSuite) TestLoggingConfigFromEnvironment(c *gc.C) { defer makeFakeHome(c).Restore() s.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, "<root>=INFO") config := newTestConfig(c, nil) c.Assert(config.LoggingConfig(), gc.Equals, "<root>=INFO;unit=DEBUG") } func (*ConfigSuite) TestProxyValuesWithFallback(c *gc.C) { defer makeFakeHome(c).Restore() config := newTestConfig(c, testing.Attrs{ "http-proxy": "http://user@10.0.0.1", "https-proxy": "https://user@10.0.0.1", "ftp-proxy": "ftp://user@10.0.0.1", "no-proxy": "localhost,10.0.3.1", }) c.Assert(config.HttpProxy(), gc.Equals, "http://user@10.0.0.1") c.Assert(config.AptHttpProxy(), gc.Equals, "http://user@10.0.0.1") c.Assert(config.HttpsProxy(), gc.Equals, "https://user@10.0.0.1") c.Assert(config.AptHttpsProxy(), gc.Equals, "https://user@10.0.0.1") c.Assert(config.FtpProxy(), gc.Equals, "ftp://user@10.0.0.1") c.Assert(config.AptFtpProxy(), gc.Equals, "ftp://user@10.0.0.1") c.Assert(config.NoProxy(), gc.Equals, "localhost,10.0.3.1") } func (*ConfigSuite) TestProxyValues(c *gc.C) { defer makeFakeHome(c).Restore() config := newTestConfig(c, testing.Attrs{ "http-proxy": "http://user@10.0.0.1", "https-proxy": "https://user@10.0.0.1", "ftp-proxy": "ftp://user@10.0.0.1", "apt-http-proxy": "http://user@10.0.0.2", "apt-https-proxy": "https://user@10.0.0.2", "apt-ftp-proxy": "ftp://user@10.0.0.2", }) c.Assert(config.HttpProxy(), gc.Equals, "http://user@10.0.0.1") c.Assert(config.AptHttpProxy(), gc.Equals, "http://user@10.0.0.2") c.Assert(config.HttpsProxy(), gc.Equals, "https://user@10.0.0.1") c.Assert(config.AptHttpsProxy(), gc.Equals, "https://user@10.0.0.2") c.Assert(config.FtpProxy(), gc.Equals, "ftp://user@10.0.0.1") c.Assert(config.AptFtpProxy(), gc.Equals, "ftp://user@10.0.0.2") } func (*ConfigSuite) TestProxyValuesNotSet(c *gc.C) { defer makeFakeHome(c).Restore() config := newTestConfig(c, testing.Attrs{}) c.Assert(config.HttpProxy(), gc.Equals, "") c.Assert(config.AptHttpProxy(), gc.Equals, "") c.Assert(config.HttpsProxy(), gc.Equals, "") c.Assert(config.AptHttpsProxy(), gc.Equals, "") c.Assert(config.FtpProxy(), gc.Equals, "") c.Assert(config.AptFtpProxy(), gc.Equals, "") c.Assert(config.NoProxy(), gc.Equals, "") } func (*ConfigSuite) TestProxyConfigMap(c *gc.C) { defer makeFakeHome(c).Restore() cfg := newTestConfig(c, testing.Attrs{}) proxy := osenv.ProxySettings{ Http: "http proxy", Https: "https proxy", Ftp: "ftp proxy", NoProxy: "no proxy", } cfg, err := cfg.Apply(config.ProxyConfigMap(proxy)) c.Assert(err, gc.IsNil) c.Assert(cfg.ProxySettings(), gc.DeepEquals, proxy) // Apt proxy and proxy differ by the content of the no-proxy values. proxy.NoProxy = "" c.Assert(cfg.AptProxySettings(), gc.DeepEquals, proxy) } func (*ConfigSuite) TestAptProxyConfigMap(c *gc.C) { defer makeFakeHome(c).Restore() cfg := newTestConfig(c, testing.Attrs{}) proxy := osenv.ProxySettings{ Http: "http proxy", Https: "https proxy", Ftp: "ftp proxy", } cfg, err := cfg.Apply(config.AptProxyConfigMap(proxy)) c.Assert(err, gc.IsNil) // The default proxy settings should still be empty. c.Assert(cfg.ProxySettings(), gc.DeepEquals, osenv.ProxySettings{}) c.Assert(cfg.AptProxySettings(), gc.DeepEquals, proxy) } func (*ConfigSuite) TestGenerateStateServerCertAndKey(c *gc.C) { // In order to test missing certs, it checks the JUJU_HOME dir, so we need // a fake home. defer testing.MakeFakeHomeWithFiles(c, []testing.TestFile{ {".ssh/id_rsa.pub", "rsa\n"}, }).Restore() for _, test := range []struct { configValues map[string]interface{} errMatch string }{{ configValues: map[string]interface{}{ "name": "test-no-certs", "type": "dummy", }, errMatch: "environment configuration has no ca-cert", }, { configValues: map[string]interface{}{ "name": "test-no-certs", "type": "dummy", "ca-cert": testing.CACert, }, errMatch: "environment configuration has no ca-private-key", }, { configValues: map[string]interface{}{ "name": "test-no-certs", "type": "dummy", "ca-cert": testing.CACert, "ca-private-key": testing.CAKey, }, }} { cfg, err := config.New(config.UseDefaults, test.configValues) c.Assert(err, gc.IsNil) certPEM, keyPEM, err := cfg.GenerateStateServerCertAndKey() if test.errMatch == "" { c.Assert(err, gc.IsNil) _, _, err = cert.ParseCertAndKey(certPEM, keyPEM) c.Check(err, gc.IsNil) err = cert.Verify(certPEM, []byte(testing.CACert), time.Now()) c.Assert(err, gc.IsNil) err = cert.Verify(certPEM, []byte(testing.CACert), time.Now().AddDate(9, 0, 0)) c.Assert(err, gc.IsNil) err = cert.Verify(certPEM, []byte(testing.CACert), time.Now().AddDate(10, 0, 1)) c.Assert(err, gc.NotNil) } else { c.Assert(err, gc.ErrorMatches, test.errMatch) c.Assert(certPEM, gc.IsNil) c.Assert(keyPEM, gc.IsNil) } } } var caCert = ` -----BEGIN CERTIFICATE----- MIIBjDCCATigAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN MAsGA1UEAxMEcm9vdDAeFw0xMjExMDkxNjQwMjhaFw0yMjExMDkxNjQ1MjhaMB4x DTALBgNVBAoTBGp1anUxDTALBgNVBAMTBHJvb3QwWTALBgkqhkiG9w0BAQEDSgAw RwJAduA1Gnb2VJLxNGfG4St0Qy48Y3q5Z5HheGtTGmti/FjlvQvScCFGCnJG7fKA Knd7ia3vWg7lxYkIvMPVP88LAQIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAKQwEgYD VR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUlvKX8vwp0o+VdhdhoA9O6KlOm00w HwYDVR0jBBgwFoAUlvKX8vwp0o+VdhdhoA9O6KlOm00wCwYJKoZIhvcNAQEFA0EA LlNpevtFr8gngjAFFAO/FXc7KiZcCrA5rBfb/rEy297lIqmKt5++aVbLEPyxCIFC r71Sj63TUTFWtRZAxvn9qQ== -----END CERTIFICATE----- `[1:] var caKey = ` -----BEGIN RSA PRIVATE KEY----- MIIBOQIBAAJAduA1Gnb2VJLxNGfG4St0Qy48Y3q5Z5HheGtTGmti/FjlvQvScCFG CnJG7fKAKnd7ia3vWg7lxYkIvMPVP88LAQIDAQABAkEAsFOdMSYn+AcF1M/iBfjo uQWJ+Zz+CgwuvumjGNsUtmwxjA+hh0fCn0Ah2nAt4Ma81vKOKOdQ8W6bapvsVDH0 6QIhAJOkLmEKm4H5POQV7qunRbRsLbft/n/SHlOBz165WFvPAiEAzh9fMf70std1 sVCHJRQWKK+vw3oaEvPKvkPiV5ui0C8CIGNsvybuo8ald5IKCw5huRlFeIxSo36k m3OVCXc6zfwVAiBnTUe7WcivPNZqOC6TAZ8dYvdWo4Ifz3jjpEfymjid1wIgBIJv ERPyv2NQqIFQZIyzUP7LVRIWfpFFOo9/Ww/7s5Y= -----END RSA PRIVATE KEY----- `[1:] var caCert2 = ` -----BEGIN CERTIFICATE----- MIIBjTCCATmgAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN MAsGA1UEAxMEcm9vdDAeFw0xMjExMDkxNjQxMDhaFw0yMjExMDkxNjQ2MDhaMB4x DTALBgNVBAoTBGp1anUxDTALBgNVBAMTBHJvb3QwWjALBgkqhkiG9w0BAQEDSwAw SAJBAJkSWRrr81y8pY4dbNgt+8miSKg4z6glp2KO2NnxxAhyyNtQHKvC+fJALJj+ C2NhuvOv9xImxOl3Hg8fFPCXCtcCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgCkMBIG A1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFOsX/ZCqKzWCAaTTVcWsWKT5Msow MB8GA1UdIwQYMBaAFOsX/ZCqKzWCAaTTVcWsWKT5MsowMAsGCSqGSIb3DQEBBQNB AAVV57jetEzJQnjgBzhvx/UwauFn78jGhXfV5BrQmxIb4SF4DgSCFstPwUQOAr8h XXzJqBQH92KYmp+y3YXDoMQ= -----END CERTIFICATE----- `[1:] var caKey2 = ` -----BEGIN RSA PRIVATE KEY----- MIIBOQIBAAJBAJkSWRrr81y8pY4dbNgt+8miSKg4z6glp2KO2NnxxAhyyNtQHKvC +fJALJj+C2NhuvOv9xImxOl3Hg8fFPCXCtcCAwEAAQJATQNzO11NQvJS5U6eraFt FgSFQ8XZjILtVWQDbJv8AjdbEgKMHEy33icsAKIUAx8jL9kjq6K9kTdAKXZi9grF UQIhAPD7jccIDUVm785E5eR9eisq0+xpgUIa24Jkn8cAlst5AiEAopxVFl1auer3 GP2In3pjdL4ydzU/gcRcYisoJqwHpM8CIHtqmaXBPeq5WT9ukb5/dL3+5SJCtmxA jQMuvZWRe6khAiBvMztYtPSDKXRbCZ4xeQ+kWSDHtok8Y5zNoTeu4nvDrwIgb3Al fikzPveC5g6S6OvEQmyDz59tYBubm2XHgvxqww0= -----END RSA PRIVATE KEY----- `[1:] var caCert3 = ` -----BEGIN CERTIFICATE----- MIIBjTCCATmgAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN MAsGA1UEAxMEcm9vdDAeFw0xMjExMDkxNjQxMjlaFw0yMjExMDkxNjQ2MjlaMB4x DTALBgNVBAoTBGp1anUxDTALBgNVBAMTBHJvb3QwWjALBgkqhkiG9w0BAQEDSwAw SAJBAIW7CbHFJivvV9V6mO8AGzJS9lqjUf6MdEPsdF6wx2Cpzr/lSFIggCwRA138 9MuFxflxb/3U8Nq+rd8rVtTgFMECAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgCkMBIG A1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFJafrxqByMN9BwGfcmuF0Lw/1QII MB8GA1UdIwQYMBaAFJafrxqByMN9BwGfcmuF0Lw/1QIIMAsGCSqGSIb3DQEBBQNB AHq3vqNhxya3s33DlQfSj9whsnqM0Nm+u8mBX/T76TF5rV7+B33XmYzSyfA3yBi/ zHaUR/dbHuiNTO+KXs3/+Y4= -----END CERTIFICATE----- `[1:] var caKey3 = ` -----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAIW7CbHFJivvV9V6mO8AGzJS9lqjUf6MdEPsdF6wx2Cpzr/lSFIg gCwRA1389MuFxflxb/3U8Nq+rd8rVtTgFMECAwEAAQJAaivPi4qJPrJb2onl50H/ VZnWKqmljGF4YQDWduMEt7GTPk+76x9SpO7W4gfY490Ivd9DEXfbr/KZqhwWikNw LQIhALlLfRXLF2ZfToMfB1v1v+jith5onAu24O68mkdRc5PLAiEAuMJ/6U07hggr Ckf9OT93wh84DK66h780HJ/FUHKcoCMCIDsPZaJBpoa50BOZG0ZjcTTwti3BGCPf uZg+w0oCGz27AiEAsUCYKqEXy/ymHhT2kSecozYENdajyXvcaOG3EPkD3nUCICOP zatzs7c/4mx4a0JBG6Za0oEPUcm2I34is50KSohz -----END RSA PRIVATE KEY----- `[1:] var invalidCAKey = ` -----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJAZabKgKInuOxj5vDWLwHHQtK3/45KB+32D15w94Nt83BmuGxo90lw -----END RSA PRIVATE KEY----- `[1:] var invalidCACert = ` -----BEGIN CERTIFICATE----- MIIBOgIBAAJAZabKgKInuOxj5vDWLwHHQtK3/45KB+32D15w94Nt83BmuGxo90lw -----END CERTIFICATE----- `[1:] ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/config/config.go������������������������������0000644�0000153�0000161�00000075744�12321735776�025622� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package config import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "regexp" "strings" "time" "github.com/errgo/errgo" "github.com/juju/loggo" "launchpad.net/juju-core/cert" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/schema" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.environs.config") const ( // FwInstance requests the use of an individual firewall per instance. FwInstance = "instance" // FwGlobal requests the use of a single firewall group for all machines. // When ports are opened for one machine, all machines will have the same // port opened. FwGlobal = "global" // DefaultStatePort is the default port the state server is listening on. DefaultStatePort int = 37017 // DefaultApiPort is the default port the API server is listening on. DefaultAPIPort int = 17070 // DefaultSyslogPort is the default port that the syslog UDP/TCP listener is // listening on. DefaultSyslogPort int = 6514 // DefaultBootstrapSSHTimeout is the amount of time to wait // contacting a state server, in seconds. DefaultBootstrapSSHTimeout int = 600 // DefaultBootstrapSSHRetryDelay is the amount of time between // attempts to connect to an address, in seconds. DefaultBootstrapSSHRetryDelay int = 5 // DefaultBootstrapSSHAddressesDelay is the amount of time between // refreshing the addresses, in seconds. Not too frequent, as we // refresh addresses from the provider each time. DefaultBootstrapSSHAddressesDelay int = 10 // fallbackLtsSeries is the latest LTS series we'll use, if we fail to // obtain this information from the system. fallbackLtsSeries string = "precise" ) var latestLtsSeries string type HasDefaultSeries interface { DefaultSeries() (string, bool) } // PreferredSeries returns the preferred series to use when a charm does not // explicitly specify a series. func PreferredSeries(cfg HasDefaultSeries) string { if series, ok := cfg.DefaultSeries(); ok { return series } return LatestLtsSeries() } func LatestLtsSeries() string { if latestLtsSeries == "" { series, err := distroLtsSeries() if err != nil { latestLtsSeries = fallbackLtsSeries } else { latestLtsSeries = series } } return latestLtsSeries } // distroLtsSeries returns the latest LTS series, if this information is // available on this system. func distroLtsSeries() (string, error) { out, err := exec.Command("distro-info", "--lts").Output() if err != nil { return "", err } series := strings.TrimSpace(string(out)) if !charm.IsValidSeries(series) { return "", fmt.Errorf("not a valid LTS series: %q", series) } return series, nil } // Config holds an immutable environment configuration. type Config struct { // defined holds the attributes that are defined for Config. // unknown holds the other attributes that are passed in (aka UnknownAttrs). // the union of these two are AllAttrs defined, unknown map[string]interface{} } // Defaulting is a value that specifies whether a configuration // creator should use defaults from the environment. type Defaulting bool const ( UseDefaults Defaulting = true NoDefaults Defaulting = false ) // TODO(rog) update the doc comment below - it's getting messy // and it assumes too much prior knowledge. // New returns a new configuration. Fields that are common to all // environment providers are verified. If useDefaults is UseDefaults, // default values will be taken from the environment. // // Specifically, the "authorized-keys-path" key // is translated into "authorized-keys" by loading the content from // respective file. Similarly, "ca-cert-path" and "ca-private-key-path" // are translated into the "ca-cert" and "ca-private-key" values. If // not specified, authorized SSH keys and CA details will be read from: // // ~/.ssh/id_dsa.pub // ~/.ssh/id_rsa.pub // ~/.ssh/identity.pub // ~/.juju/<name>-cert.pem // ~/.juju/<name>-private-key.pem // // The required keys (after any files have been read) are "name", // "type" and "authorized-keys", all of type string. Additional keys // recognised are "agent-version" (string) and "development" (bool) as // well as charm-store-auth (string containing comma-separated key=value pairs). func New(withDefaults Defaulting, attrs map[string]interface{}) (*Config, error) { checker := noDefaultsChecker if withDefaults { checker = withDefaultsChecker } defined, err := checker.Coerce(attrs, nil) if err != nil { return nil, err } c := &Config{ defined: defined.(map[string]interface{}), unknown: make(map[string]interface{}), } if withDefaults { if err := c.fillInDefaults(); err != nil { return nil, err } } if err := c.ensureUnitLogging(); err != nil { return nil, err } // no old config to compare against if err := Validate(c, nil); err != nil { return nil, err } // Copy unknown attributes onto the type-specific map. for k, v := range attrs { if _, ok := fields[k]; !ok { c.unknown[k] = v } } return c, nil } func (c *Config) ensureUnitLogging() error { loggingConfig := c.asString("logging-config") // If the logging config hasn't been set, then look for the os environment // variable, and failing that, get the config from loggo itself. if loggingConfig == "" { if environmentValue := os.Getenv(osenv.JujuLoggingConfigEnvKey); environmentValue != "" { loggingConfig = environmentValue } else { loggingConfig = loggo.LoggerInfo() } } levels, err := loggo.ParseConfigurationString(loggingConfig) if err != nil { return err } // If there is is no specified level for "unit", then set one. if _, ok := levels["unit"]; !ok { loggingConfig = loggingConfig + ";unit=DEBUG" } c.defined["logging-config"] = loggingConfig return nil } func (c *Config) fillInDefaults() error { // For backward compatibility purposes, we treat as unset string // valued attributes that are set to the empty string, and fill // out their defaults accordingly. c.fillInStringDefault("firewall-mode") // Load authorized-keys-path into authorized-keys if necessary. path := c.asString("authorized-keys-path") keys := c.asString("authorized-keys") if path != "" || keys == "" { var err error c.defined["authorized-keys"], err = ReadAuthorizedKeys(path) if err != nil { return err } } delete(c.defined, "authorized-keys-path") // Don't use c.Name() because the name hasn't // been verified yet. name := c.asString("name") if name == "" { return fmt.Errorf("empty name in environment configuration") } err := maybeReadAttrFromFile(c.defined, "ca-cert", name+"-cert.pem") if err != nil { return err } err = maybeReadAttrFromFile(c.defined, "ca-private-key", name+"-private-key.pem") if err != nil { return err } return nil } func (c *Config) fillInStringDefault(attr string) { if c.asString(attr) == "" { c.defined[attr] = defaults[attr] } } // processDeprecatedAttributes ensures that the config is set up so that it works // correctly when used with older versions of Juju which require that deprecated // attribute values still be used. func (cfg *Config) processDeprecatedAttributes() { // The tools url has changed so ensure that both old and new values are in the config so that // upgrades work. "tools-url" is the old attribute name. if oldToolsURL := cfg.defined["tools-url"]; oldToolsURL != nil && oldToolsURL.(string) != "" { _, newToolsSpecified := cfg.ToolsURL() // Ensure the new attribute name "tools-metadata-url" is set. if !newToolsSpecified { cfg.defined["tools-metadata-url"] = oldToolsURL } } // Even if the user has edited their environment yaml to remove the deprecated tools-url value, // we still want it in the config for upgrades. cfg.defined["tools-url"], _ = cfg.ToolsURL() // Update the provider type from null to manual. if cfg.Type() == "null" { cfg.defined["type"] = "manual" } } // Validate ensures that config is a valid configuration. If old is not nil, // it holds the previous environment configuration for consideration when // validating changes. func Validate(cfg, old *Config) error { // Check that we don't have any disallowed fields. for _, attr := range allowedWithDefaultsOnly { if _, ok := cfg.defined[attr]; ok { return fmt.Errorf("attribute %q is not allowed in configuration", attr) } } // Check that mandatory fields are specified. for _, attr := range mandatoryWithoutDefaults { if _, ok := cfg.defined[attr]; !ok { return fmt.Errorf("%s missing from environment configuration", attr) } } // Check that all other fields that have been specified are non-empty, // unless they're allowed to be empty for backward compatibility, for attr, val := range cfg.defined { if !isEmpty(val) { continue } if !allowEmpty(attr) { return fmt.Errorf("empty %s in environment configuration", attr) } } if strings.ContainsAny(cfg.mustString("name"), "/\\") { return fmt.Errorf("environment name contains unsafe characters") } // Check that the agent version parses ok if set explicitly; otherwise leave // it alone. if v, ok := cfg.defined["agent-version"].(string); ok { if _, err := version.Parse(v); err != nil { return fmt.Errorf("invalid agent version in environment configuration: %q", v) } } // If the logging config is set, make sure it is valid. if v, ok := cfg.defined["logging-config"].(string); ok { if _, err := loggo.ParseConfigurationString(v); err != nil { return err } } // Check firewall mode. if mode := cfg.FirewallMode(); mode != FwInstance && mode != FwGlobal { return fmt.Errorf("invalid firewall mode in environment configuration: %q", mode) } caCert, caCertOK := cfg.CACert() caKey, caKeyOK := cfg.CAPrivateKey() if caCertOK || caKeyOK { if err := verifyKeyPair(caCert, caKey); err != nil { return errgo.Annotate(err, "bad CA certificate/key in configuration") } } // Ensure that the auth token is a set of key=value pairs. authToken, _ := cfg.CharmStoreAuth() validAuthToken := regexp.MustCompile(`^([^\s=]+=[^\s=]+(,\s*)?)*$`) if !validAuthToken.MatchString(authToken) { return fmt.Errorf("charm store auth token needs to be a set"+ " of key-value pairs, not %q", authToken) } // Check the immutable config values. These can't change if old != nil { for _, attr := range immutableAttributes { if newv, oldv := cfg.defined[attr], old.defined[attr]; newv != oldv { return fmt.Errorf("cannot change %s from %#v to %#v", attr, oldv, newv) } } if _, oldFound := old.AgentVersion(); oldFound { if _, newFound := cfg.AgentVersion(); !newFound { return fmt.Errorf("cannot clear agent-version") } } } cfg.processDeprecatedAttributes() return nil } func isEmpty(val interface{}) bool { switch val := val.(type) { case nil: return true case bool: return false case int: // TODO(rog) fix this to return false when // we can lose backward compatibility. // https://bugs.launchpad.net/juju-core/+bug/1224492 return val == 0 case string: return val == "" } panic(fmt.Errorf("unexpected type %T in configuration", val)) } // maybeReadAttrFromFile sets defined[attr] to: // // 1) The content of the file defined[attr+"-path"], if that's set // 2) The value of defined[attr] if it is already set. // 3) The content of defaultPath if it exists and defined[attr] is unset // 4) Preserves the content of defined[attr], otherwise // // The defined[attr+"-path"] key is always deleted. func maybeReadAttrFromFile(defined map[string]interface{}, attr, defaultPath string) error { pathAttr := attr + "-path" path, _ := defined[pathAttr].(string) delete(defined, pathAttr) hasPath := path != "" if !hasPath { // No path and attribute is already set; leave it be. if s, _ := defined[attr].(string); s != "" { return nil } path = defaultPath } path, err := utils.NormalizePath(path) if err != nil { return err } if !filepath.IsAbs(path) { path = osenv.JujuHomePath(path) } data, err := ioutil.ReadFile(path) if err != nil { if os.IsNotExist(err) && !hasPath { // If the default path isn't found, it's // not an error. return nil } return err } if len(data) == 0 { return fmt.Errorf("file %q is empty", path) } defined[attr] = string(data) return nil } // asString is a private helper method to keep the ugly string casting // in once place. It returns the given named attribute as a string, // returning "" if it isn't found. func (c *Config) asString(name string) string { value, _ := c.defined[name].(string) return value } // mustString returns the named attribute as an string, panicking if // it is not found or is empty. func (c *Config) mustString(name string) string { value, _ := c.defined[name].(string) if value == "" { panic(fmt.Errorf("empty value for %q found in configuration (type %T, val %v)", name, c.defined[name], c.defined[name])) } return value } // mustInt returns the named attribute as an integer, panicking if // it is not found or is zero. Zero values should have been // diagnosed at Validate time. func (c *Config) mustInt(name string) int { value, _ := c.defined[name].(int) if value == 0 { panic(fmt.Errorf("empty value for %q found in configuration", name)) } return value } // Type returns the environment type. func (c *Config) Type() string { return c.mustString("type") } // Name returns the environment name. func (c *Config) Name() string { return c.mustString("name") } // DefaultSeries returns the configured default Ubuntu series for the environment, // and whether the default series was explicitly configured on the environment. func (c *Config) DefaultSeries() (string, bool) { if s, ok := c.defined["default-series"]; ok { if series, ok := s.(string); ok && series != "" { return series, true } else if !ok { logger.Warningf("invalid default-series: %q", s) } } return "", false } // StatePort returns the state server port for the environment. func (c *Config) StatePort() int { return c.mustInt("state-port") } // APIPort returns the API server port for the environment. func (c *Config) APIPort() int { return c.mustInt("api-port") } // SyslogPort returns the syslog port for the environment. func (c *Config) SyslogPort() int { return c.mustInt("syslog-port") } // RsyslogCACert returns the certificate of the CA that signed the // rsyslog certificate, in PEM format, or nil if one hasn't been // generated yet. func (c *Config) RsyslogCACert() []byte { if s, ok := c.defined["rsyslog-ca-cert"]; ok { return []byte(s.(string)) } return nil } // AuthorizedKeys returns the content for ssh's authorized_keys file. func (c *Config) AuthorizedKeys() string { return c.mustString("authorized-keys") } // ProxySettings returns all four proxy settings; http, https, ftp, and no // proxy. func (c *Config) ProxySettings() osenv.ProxySettings { return osenv.ProxySettings{ Http: c.HttpProxy(), Https: c.HttpsProxy(), Ftp: c.FtpProxy(), NoProxy: c.NoProxy(), } } // HttpProxy returns the http proxy for the environment. func (c *Config) HttpProxy() string { return c.asString("http-proxy") } // HttpsProxy returns the https proxy for the environment. func (c *Config) HttpsProxy() string { return c.asString("https-proxy") } // FtpProxy returns the ftp proxy for the environment. func (c *Config) FtpProxy() string { return c.asString("ftp-proxy") } // NoProxy returns the 'no proxy' for the environment. func (c *Config) NoProxy() string { return c.asString("no-proxy") } func (c *Config) getWithFallback(key, fallback string) string { value := c.asString(key) if value == "" { value = c.asString(fallback) } return value } // AptProxySettings returns all three proxy settings; http, https and ftp. func (c *Config) AptProxySettings() osenv.ProxySettings { return osenv.ProxySettings{ Http: c.AptHttpProxy(), Https: c.AptHttpsProxy(), Ftp: c.AptFtpProxy(), } } // AptHttpProxy returns the apt http proxy for the environment. // Falls back to the default http-proxy if not specified. func (c *Config) AptHttpProxy() string { return c.getWithFallback("apt-http-proxy", "http-proxy") } // AptHttpsProxy returns the apt https proxy for the environment. // Falls back to the default https-proxy if not specified. func (c *Config) AptHttpsProxy() string { return c.getWithFallback("apt-https-proxy", "https-proxy") } // AptFtpProxy returns the apt ftp proxy for the environment. // Falls back to the default ftp-proxy if not specified. func (c *Config) AptFtpProxy() string { return c.getWithFallback("apt-ftp-proxy", "ftp-proxy") } // BootstrapSSHOpts returns the SSH timeout and retry delays used // during bootstrap. func (c *Config) BootstrapSSHOpts() SSHTimeoutOpts { opts := SSHTimeoutOpts{ Timeout: time.Duration(DefaultBootstrapSSHTimeout) * time.Second, RetryDelay: time.Duration(DefaultBootstrapSSHRetryDelay) * time.Second, AddressesDelay: time.Duration(DefaultBootstrapSSHAddressesDelay) * time.Second, } if v, ok := c.defined["bootstrap-timeout"].(int); ok && v != 0 { opts.Timeout = time.Duration(v) * time.Second } if v, ok := c.defined["bootstrap-retry-delay"].(int); ok && v != 0 { opts.RetryDelay = time.Duration(v) * time.Second } if v, ok := c.defined["bootstrap-addresses-delay"].(int); ok && v != 0 { opts.AddressesDelay = time.Duration(v) * time.Second } return opts } // CACert returns the certificate of the CA that signed the state server // certificate, in PEM format, and whether the setting is available. func (c *Config) CACert() ([]byte, bool) { if s, ok := c.defined["ca-cert"]; ok { return []byte(s.(string)), true } return nil, false } // CAPrivateKey returns the private key of the CA that signed the state // server certificate, in PEM format, and whether the setting is available. func (c *Config) CAPrivateKey() (key []byte, ok bool) { if s, ok := c.defined["ca-private-key"]; ok && s != "" { return []byte(s.(string)), true } return nil, false } // AdminSecret returns the administrator password. // It's empty if the password has not been set. func (c *Config) AdminSecret() string { if s, ok := c.defined["admin-secret"]; ok && s != "" { return s.(string) } return "" } // FirewallMode returns whether the firewall should // manage ports per machine or global // (FwInstance or FwGlobal) func (c *Config) FirewallMode() string { return c.mustString("firewall-mode") } // AgentVersion returns the proposed version number for the agent tools, // and whether it has been set. Once an environment is bootstrapped, this // must always be valid. func (c *Config) AgentVersion() (version.Number, bool) { if v, ok := c.defined["agent-version"].(string); ok { n, err := version.Parse(v) if err != nil { panic(err) // We should have checked it earlier. } return n, true } return version.Zero, false } // ToolsURL returns the URL that locates the tools tarballs and metadata, // and whether it has been set. func (c *Config) ToolsURL() (string, bool) { if url, ok := c.defined["tools-metadata-url"]; ok && url != "" { return url.(string), true } return "", false } // ImageMetadataURL returns the URL at which the metadata used to locate image ids is located, // and wether it has been set. func (c *Config) ImageMetadataURL() (string, bool) { if url, ok := c.defined["image-metadata-url"]; ok && url != "" { return url.(string), true } return "", false } // Development returns whether the environment is in development mode. func (c *Config) Development() bool { return c.defined["development"].(bool) } // SSLHostnameVerification returns weather the environment has requested // SSL hostname verification to be enabled. func (c *Config) SSLHostnameVerification() bool { return c.defined["ssl-hostname-verification"].(bool) } // LoggingConfig returns the configuration string for the loggers. func (c *Config) LoggingConfig() string { return c.asString("logging-config") } // Auth token sent to charm store func (c *Config) CharmStoreAuth() (string, bool) { auth := c.asString("charm-store-auth") return auth, auth != "" } // ProvisionerSafeMode reports whether the provisioner should not // destroy machines it does not know about. func (c *Config) ProvisionerSafeMode() bool { v, _ := c.defined["provisioner-safe-mode"].(bool) return v } // ImageStream returns the simplestreams stream // used to identify which image ids to search // when starting an instance. func (c *Config) ImageStream() string { v, _ := c.defined["image-stream"].(string) if v != "" { return v } return "released" } // TestMode indicates if the environment is intended for testing. // In this case, accessing the charm store does not affect statistical // data of the store. func (c *Config) TestMode() bool { return c.defined["test-mode"].(bool) } // UnknownAttrs returns a copy of the raw configuration attributes // that are supposedly specific to the environment type. They could // also be wrong attributes, though. Only the specific environment // implementation can tell. func (c *Config) UnknownAttrs() map[string]interface{} { newAttrs := make(map[string]interface{}) for k, v := range c.unknown { newAttrs[k] = v } return newAttrs } // AllAttrs returns a copy of the raw configuration attributes. func (c *Config) AllAttrs() map[string]interface{} { allAttrs := c.UnknownAttrs() for k, v := range c.defined { allAttrs[k] = v } return allAttrs } // Remove returns a new configuration that has the attributes of c minus attrs. func (c *Config) Remove(attrs []string) (*Config, error) { defined := c.AllAttrs() for _, k := range attrs { delete(defined, k) } return New(NoDefaults, defined) } // Apply returns a new configuration that has the attributes of c plus attrs. func (c *Config) Apply(attrs map[string]interface{}) (*Config, error) { defined := c.AllAttrs() for k, v := range attrs { defined[k] = v } return New(NoDefaults, defined) } var fields = schema.Fields{ "type": schema.String(), "name": schema.String(), "default-series": schema.String(), "tools-metadata-url": schema.String(), "image-metadata-url": schema.String(), "image-stream": schema.String(), "authorized-keys": schema.String(), "authorized-keys-path": schema.String(), "firewall-mode": schema.String(), "agent-version": schema.String(), "development": schema.Bool(), "admin-secret": schema.String(), "ca-cert": schema.String(), "ca-cert-path": schema.String(), "ca-private-key": schema.String(), "ca-private-key-path": schema.String(), "ssl-hostname-verification": schema.Bool(), "state-port": schema.ForceInt(), "api-port": schema.ForceInt(), "syslog-port": schema.ForceInt(), "rsyslog-ca-cert": schema.String(), "logging-config": schema.String(), "charm-store-auth": schema.String(), "provisioner-safe-mode": schema.Bool(), "http-proxy": schema.String(), "https-proxy": schema.String(), "ftp-proxy": schema.String(), "no-proxy": schema.String(), "apt-http-proxy": schema.String(), "apt-https-proxy": schema.String(), "apt-ftp-proxy": schema.String(), "bootstrap-timeout": schema.ForceInt(), "bootstrap-retry-delay": schema.ForceInt(), "bootstrap-addresses-delay": schema.ForceInt(), "test-mode": schema.Bool(), // Deprecated fields, retain for backwards compatibility. "tools-url": schema.String(), } // alwaysOptional holds configuration defaults for attributes that may // be unspecified even after a configuration has been created with all // defaults filled out. // // This table is not definitive: it specifies those attributes which are // optional when the config goes through its initial schema coercion, // but some fields listed as optional here are actually mandatory // with NoDefaults and are checked at the later Validate stage. var alwaysOptional = schema.Defaults{ "agent-version": schema.Omit, "ca-cert": schema.Omit, "authorized-keys": schema.Omit, "authorized-keys-path": schema.Omit, "ca-cert-path": schema.Omit, "ca-private-key-path": schema.Omit, "logging-config": schema.Omit, "provisioner-safe-mode": schema.Omit, "bootstrap-timeout": schema.Omit, "bootstrap-retry-delay": schema.Omit, "bootstrap-addresses-delay": schema.Omit, "rsyslog-ca-cert": schema.Omit, "http-proxy": schema.Omit, "https-proxy": schema.Omit, "ftp-proxy": schema.Omit, "no-proxy": schema.Omit, "apt-http-proxy": schema.Omit, "apt-https-proxy": schema.Omit, "apt-ftp-proxy": schema.Omit, // Deprecated fields, retain for backwards compatibility. "tools-url": "", // For backward compatibility reasons, the following // attributes default to empty strings rather than being // omitted. // TODO(rog) remove this support when we can // remove upgrade compatibility with versions prior to 1.14. "admin-secret": "", // TODO(rog) omit "ca-private-key": "", // TODO(rog) omit "image-metadata-url": "", // TODO(rog) omit "tools-metadata-url": "", // TODO(rog) omit "default-series": "", // For backward compatibility only - default ports were // not filled out in previous versions of the configuration. "state-port": DefaultStatePort, "api-port": DefaultAPIPort, "syslog-port": DefaultSyslogPort, // Authentication string sent with requests to the charm store "charm-store-auth": "", // Previously image-stream could be set to an empty value "image-stream": "", "test-mode": false, } func allowEmpty(attr string) bool { return alwaysOptional[attr] == "" } var defaults = allDefaults() // allDefaults returns a schema.Defaults that contains // defaults to be used when creating a new config with // UseDefaults. func allDefaults() schema.Defaults { d := schema.Defaults{ "firewall-mode": FwInstance, "development": false, "ssl-hostname-verification": true, "state-port": DefaultStatePort, "api-port": DefaultAPIPort, "syslog-port": DefaultSyslogPort, "bootstrap-timeout": DefaultBootstrapSSHTimeout, "bootstrap-retry-delay": DefaultBootstrapSSHRetryDelay, "bootstrap-addresses-delay": DefaultBootstrapSSHAddressesDelay, } for attr, val := range alwaysOptional { if _, ok := d[attr]; !ok { d[attr] = val } } return d } // allowedWithDefaultsOnly holds those attributes // that are only allowed in a configuration that is // being created with UseDefaults. var allowedWithDefaultsOnly = []string{ "ca-cert-path", "ca-private-key-path", "authorized-keys-path", } // mandatoryWithoutDefaults holds those attributes // that are mandatory if the configuration is created // with no defaults but optional otherwise. var mandatoryWithoutDefaults = []string{ "authorized-keys", } // immutableAttributes holds those attributes // which are not allowed to change in the lifetime // of an environment. var immutableAttributes = []string{ "name", "type", "firewall-mode", "state-port", "api-port", "bootstrap-timeout", "bootstrap-retry-delay", "bootstrap-addresses-delay", } var ( withDefaultsChecker = schema.FieldMap(fields, defaults) noDefaultsChecker = schema.FieldMap(fields, alwaysOptional) ) // ValidateUnknownAttrs checks the unknown attributes of the config against // the supplied fields and defaults, and returns an error if any fails to // validate. Unknown fields are warned about, but preserved, on the basis // that they are reasonably likely to have been written by or for a version // of juju that does recognise the fields, but that their presence is still // anomalous to some degree and should be flagged (and that there is thereby // a mechanism for observing fields that really are typos etc). func (cfg *Config) ValidateUnknownAttrs(fields schema.Fields, defaults schema.Defaults) (map[string]interface{}, error) { attrs := cfg.UnknownAttrs() checker := schema.FieldMap(fields, defaults) coerced, err := checker.Coerce(attrs, nil) if err != nil { logger.Debugf("coercion failed attributes: %#v, checker: %#v, %v", attrs, checker, err) return nil, err } result := coerced.(map[string]interface{}) for name, value := range attrs { if fields[name] == nil { logger.Warningf("unknown config field %q", name) result[name] = value } } return result, nil } // GenerateStateServerCertAndKey makes sure that the config has a CACert and // CAPrivateKey, generates and retruns new certificate and key. func (cfg *Config) GenerateStateServerCertAndKey() ([]byte, []byte, error) { caCert, hasCACert := cfg.CACert() if !hasCACert { return nil, nil, fmt.Errorf("environment configuration has no ca-cert") } caKey, hasCAKey := cfg.CAPrivateKey() if !hasCAKey { return nil, nil, fmt.Errorf("environment configuration has no ca-private-key") } var noHostnames []string return cert.NewServer(caCert, caKey, time.Now().UTC().AddDate(10, 0, 0), noHostnames) } type Specializer interface { WithAuthAttrs(string) charm.Repository WithTestMode(testMode bool) charm.Repository } // SpecializeCharmRepo returns a repository customized for given configuration. // It adds authentication if necessary and sets a charm store's testMode flag. func SpecializeCharmRepo(repo charm.Repository, cfg *Config) charm.Repository { // If a charm store auth token is set, pass it on to the charm store if auth, authSet := cfg.CharmStoreAuth(); authSet { if CS, isCS := repo.(Specializer); isCS { repo = CS.WithAuthAttrs(auth) } } if CS, isCS := repo.(Specializer); isCS { repo = CS.WithTestMode(cfg.TestMode()) } return repo } // SSHTimeoutOpts lists the amount of time we will wait for various // parts of the SSH connection to complete. This is similar to // DialOpts, see http://pad.lv/1258889 about possibly deduplicating // them. type SSHTimeoutOpts struct { // Timeout is the amount of time to wait contacting a state // server. Timeout time.Duration // RetryDelay is the amount of time between attempts to connect to // an address. RetryDelay time.Duration // AddressesDelay is the amount of time between refreshing the // addresses. AddressesDelay time.Duration } func addIfNotEmpty(settings map[string]interface{}, key, value string) { if value != "" { settings[key] = value } } // ProxyConfigMap returns a map suitable to be applied to a Config to update // proxy settings. func ProxyConfigMap(proxy osenv.ProxySettings) map[string]interface{} { settings := make(map[string]interface{}) addIfNotEmpty(settings, "http-proxy", proxy.Http) addIfNotEmpty(settings, "https-proxy", proxy.Https) addIfNotEmpty(settings, "ftp-proxy", proxy.Ftp) addIfNotEmpty(settings, "no-proxy", proxy.NoProxy) return settings } // AptProxyConfigMap returns a map suitable to be applied to a Config to update // proxy settings. func AptProxyConfigMap(proxy osenv.ProxySettings) map[string]interface{} { settings := make(map[string]interface{}) addIfNotEmpty(settings, "apt-http-proxy", proxy.Http) addIfNotEmpty(settings, "apt-https-proxy", proxy.Https) addIfNotEmpty(settings, "apt-ftp-proxy", proxy.Ftp) return settings } ����������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/config/authkeys.go����������������������������0000644�0000153�0000161�00000005253�12321735642�026166� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package config import ( "bytes" "crypto/tls" "fmt" "io/ioutil" "os" "path/filepath" "launchpad.net/juju-core/cert" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" ) const ( // AuthKeysConfig is the configuration key for authorised keys. AuthKeysConfig = "authorized-keys" // JujuSystemKey is the SSH key comment for Juju system keys. JujuSystemKey = "juju-system-key" ) // ReadAuthorizedKeys implements the standard juju behaviour for finding // authorized_keys. It returns a set of keys in in authorized_keys format // (see sshd(8) for a description). If path is non-empty, it names the // file to use; otherwise the user's .ssh directory will be searched. // Home directory expansion will be performed on the path if it starts with // a ~; if the expanded path is relative, it will be interpreted relative // to $HOME/.ssh. // // The result of utils/ssh.PublicKeyFiles will always be prepended to the // result. In practice, this means ReadAuthorizedKeys never returns an // error when the call originates in the CLI. func ReadAuthorizedKeys(path string) (string, error) { files := ssh.PublicKeyFiles() if path == "" { files = append(files, "id_dsa.pub", "id_rsa.pub", "identity.pub") } else { files = append(files, path) } var firstError error var keyData []byte for _, f := range files { f, err := utils.NormalizePath(f) if err != nil { if firstError == nil { firstError = err } continue } if !filepath.IsAbs(f) { f = filepath.Join(osenv.Home(), ".ssh", f) } data, err := ioutil.ReadFile(f) if err != nil { if firstError == nil && !os.IsNotExist(err) { firstError = err } continue } keyData = append(keyData, bytes.Trim(data, "\n")...) keyData = append(keyData, '\n') } if len(keyData) == 0 { if firstError == nil { firstError = fmt.Errorf("no public ssh keys found") } return "", firstError } return string(keyData), nil } // verifyKeyPair verifies that the certificate and key parse correctly. // The key is optional - if it is provided, we also check that the key // matches the certificate. func verifyKeyPair(certb, key []byte) error { if key != nil { _, err := tls.X509KeyPair(certb, key) return err } _, err := cert.ParseCert(certb) return err } // ConcatAuthKeys concatenates the two sets of authorised keys, interposing // a newline if necessary, because authorised keys are newline-separated. func ConcatAuthKeys(a, b string) string { if a == "" { return b } if b == "" { return a } if a[len(a)-1] != '\n' { return a + "\n" + b } return a + b } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/config/authkeys_test.go�����������������������0000644�0000153�0000161�00000005647�12321735642�027234� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package config_test import ( "io/ioutil" "os" "path/filepath" "strings" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/ssh" ) type AuthKeysSuite struct { testbase.LoggingSuite dotssh string // ~/.ssh } var _ = gc.Suite(&AuthKeysSuite{}) func (s *AuthKeysSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) old := osenv.Home() newhome := c.MkDir() osenv.SetHome(newhome) s.AddCleanup(func(*gc.C) { osenv.SetHome(old) }) s.dotssh = filepath.Join(newhome, ".ssh") err := os.Mkdir(s.dotssh, 0755) c.Assert(err, gc.IsNil) } func (s *AuthKeysSuite) TearDownTest(c *gc.C) { ssh.ClearClientKeys() s.LoggingSuite.TearDownTest(c) } func (s *AuthKeysSuite) TestReadAuthorizedKeysErrors(c *gc.C) { _, err := config.ReadAuthorizedKeys("") c.Assert(err, gc.ErrorMatches, "no public ssh keys found") _, err = config.ReadAuthorizedKeys(filepath.Join(s.dotssh, "notthere.pub")) c.Assert(err, gc.ErrorMatches, "no public ssh keys found") } func writeFile(c *gc.C, filename string, contents string) { err := ioutil.WriteFile(filename, []byte(contents), 0644) c.Assert(err, gc.IsNil) } func (s *AuthKeysSuite) TestReadAuthorizedKeys(c *gc.C) { writeFile(c, filepath.Join(s.dotssh, "id_rsa.pub"), "id_rsa") writeFile(c, filepath.Join(s.dotssh, "identity.pub"), "identity") writeFile(c, filepath.Join(s.dotssh, "test.pub"), "test") keys, err := config.ReadAuthorizedKeys("") c.Assert(err, gc.IsNil) c.Assert(keys, gc.Equals, "id_rsa\nidentity\n") keys, err = config.ReadAuthorizedKeys("test.pub") // relative to ~/.ssh c.Assert(err, gc.IsNil) c.Assert(keys, gc.Equals, "test\n") } func (s *AuthKeysSuite) TestReadAuthorizedKeysClientKeys(c *gc.C) { keydir := filepath.Join(s.dotssh, "juju") err := ssh.LoadClientKeys(keydir) // auto-generates a key pair c.Assert(err, gc.IsNil) pubkeyFiles := ssh.PublicKeyFiles() c.Assert(pubkeyFiles, gc.HasLen, 1) data, err := ioutil.ReadFile(pubkeyFiles[0]) c.Assert(err, gc.IsNil) prefix := strings.Trim(string(data), "\n") + "\n" writeFile(c, filepath.Join(s.dotssh, "id_rsa.pub"), "id_rsa") writeFile(c, filepath.Join(s.dotssh, "test.pub"), "test") keys, err := config.ReadAuthorizedKeys("") c.Assert(err, gc.IsNil) c.Assert(keys, gc.Equals, prefix+"id_rsa\n") keys, err = config.ReadAuthorizedKeys("test.pub") c.Assert(err, gc.IsNil) c.Assert(keys, gc.Equals, prefix+"test\n") keys, err = config.ReadAuthorizedKeys("notthere.pub") c.Assert(err, gc.IsNil) c.Assert(keys, gc.Equals, prefix) } func (s *AuthKeysSuite) TestConcatAuthKeys(c *gc.C) { for _, test := range []struct{ a, b, result string }{ {"a", "", "a"}, {"", "b", "b"}, {"a", "b", "a\nb"}, {"a\n", "b", "a\nb"}, } { c.Check(config.ConcatAuthKeys(test.a, test.b), gc.Equals, test.result) } } �����������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/open_test.go����������������������������������0000644�0000153�0000161�00000033141�12321735642�025061� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs_test import ( "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cert" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type OpenSuite struct { testbase.LoggingSuite envtesting.ToolsFixture } var _ = gc.Suite(&OpenSuite{}) func (*OpenSuite) TearDownTest(c *gc.C) { dummy.Reset() } func (*OpenSuite) TestNewDummyEnviron(c *gc.C) { // matches *Settings.Map() cfg, err := config.New(config.NoDefaults, dummySampleConfig()) c.Assert(err, gc.IsNil) ctx := testing.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, env.Storage()) err = bootstrap.Bootstrap(ctx, env, constraints.Value{}) c.Assert(err, gc.IsNil) } func (*OpenSuite) TestNewUnknownEnviron(c *gc.C) { attrs := dummySampleConfig().Merge(testing.Attrs{ "type": "wondercloud", }) env, err := environs.NewFromAttrs(attrs) c.Assert(err, gc.ErrorMatches, "no registered provider for.*") c.Assert(env, gc.IsNil) } func (*OpenSuite) TestNewFromName(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore() store := configstore.NewMem() ctx := testing.Context(c) e, err := environs.PrepareFromName("erewhemos", ctx, store) c.Assert(err, gc.IsNil) e, err = environs.NewFromName("erewhemos", store) c.Assert(err, gc.IsNil) c.Assert(e.Name(), gc.Equals, "erewhemos") } func (*OpenSuite) TestNewFromNameWithInvalidInfo(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore() store := configstore.NewMem() cfg, _, err := environs.ConfigForName("erewhemos", store) c.Assert(err, gc.IsNil) info, err := store.CreateInfo("erewhemos") c.Assert(err, gc.IsNil) // The configuration from environments.yaml is invalid // because it doesn't contain the state-id attribute which // the dummy environment adds at Prepare time. info.SetBootstrapConfig(cfg.AllAttrs()) err = info.Write() c.Assert(err, gc.IsNil) e, err := environs.NewFromName("erewhemos", store) c.Assert(err, gc.ErrorMatches, "environment is not prepared") c.Assert(e, gc.IsNil) } func (*OpenSuite) TestNewFromNameWithInvalidEnvironConfig(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore() store := configstore.NewMem() e, err := environs.NewFromName("erewhemos", store) c.Assert(err, gc.Equals, environs.ErrNotBootstrapped) c.Assert(e, gc.IsNil) } func (*OpenSuite) TestPrepareFromName(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore() ctx := testing.Context(c) e, err := environs.PrepareFromName("erewhemos", ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) c.Assert(e.Name(), gc.Equals, "erewhemos") // Check we can access storage ok, which implies the environment has been prepared. c.Assert(e.Storage(), gc.NotNil) } func (*OpenSuite) TestConfigForName(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore() cfg, source, err := environs.ConfigForName("erewhemos", configstore.NewMem()) c.Assert(err, gc.IsNil) c.Assert(source, gc.Equals, environs.ConfigFromEnvirons) c.Assert(cfg.Name(), gc.Equals, "erewhemos") } func (*OpenSuite) TestConfigForNameNoDefault(c *gc.C) { defer testing.MakeFakeHome(c, testing.MultipleEnvConfigNoDefault, testing.SampleCertName).Restore() cfg, source, err := environs.ConfigForName("", configstore.NewMem()) c.Assert(err, gc.ErrorMatches, "no default environment found") c.Assert(cfg, gc.IsNil) c.Assert(source, gc.Equals, environs.ConfigFromEnvirons) } func (*OpenSuite) TestConfigForNameDefault(c *gc.C) { defer testing.MakeFakeHome(c, testing.SingleEnvConfig, testing.SampleCertName).Restore() cfg, source, err := environs.ConfigForName("", configstore.NewMem()) c.Assert(err, gc.IsNil) c.Assert(cfg.Name(), gc.Equals, "erewhemos") c.Assert(source, gc.Equals, environs.ConfigFromEnvirons) } func (*OpenSuite) TestConfigForNameFromInfo(c *gc.C) { defer testing.MakeFakeHome(c, testing.SingleEnvConfig, testing.SampleCertName).Restore() store := configstore.NewMem() cfg, source, err := environs.ConfigForName("", store) c.Assert(err, gc.IsNil) c.Assert(source, gc.Equals, environs.ConfigFromEnvirons) info, err := store.CreateInfo("test-config") c.Assert(err, gc.IsNil) var attrs testing.Attrs = cfg.AllAttrs() attrs = attrs.Merge(testing.Attrs{ "name": "test-config", }) info.SetBootstrapConfig(attrs) err = info.Write() c.Assert(err, gc.IsNil) cfg, source, err = environs.ConfigForName("test-config", store) c.Assert(err, gc.IsNil) c.Assert(source, gc.Equals, environs.ConfigFromInfo) c.Assert(testing.Attrs(cfg.AllAttrs()), gc.DeepEquals, attrs) } func (*OpenSuite) TestNew(c *gc.C) { cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge( testing.Attrs{ "state-server": false, "name": "erewhemos", }, )) c.Assert(err, gc.IsNil) e, err := environs.New(cfg) c.Assert(err, gc.ErrorMatches, "environment is not prepared") c.Assert(e, gc.IsNil) } func (*OpenSuite) TestPrepare(c *gc.C) { baselineAttrs := dummy.SampleConfig().Merge(testing.Attrs{ "state-server": false, "name": "erewhemos", }).Delete( "ca-cert", "ca-private-key", "admin-secret", ) cfg, err := config.New(config.NoDefaults, baselineAttrs) c.Assert(err, gc.IsNil) store := configstore.NewMem() ctx := testing.Context(c) env, err := environs.Prepare(cfg, ctx, store) c.Assert(err, gc.IsNil) // Check we can access storage ok, which implies the environment has been prepared. c.Assert(env.Storage(), gc.NotNil) // Check that the environment info file was correctly created. info, err := store.ReadInfo("erewhemos") c.Assert(err, gc.IsNil) c.Assert(info.Initialized(), jc.IsTrue) c.Assert(info.BootstrapConfig(), gc.DeepEquals, env.Config().AllAttrs()) c.Logf("bootstrap config: %#v", info.BootstrapConfig()) // Check that an admin-secret was chosen. adminSecret := env.Config().AdminSecret() c.Assert(adminSecret, gc.HasLen, 32) c.Assert(adminSecret, gc.Matches, "^[0-9a-f]*$") // Check that the CA cert was generated. cfgCertPEM, cfgCertOK := env.Config().CACert() cfgKeyPEM, cfgKeyOK := env.Config().CAPrivateKey() c.Assert(cfgCertOK, gc.Equals, true) c.Assert(cfgKeyOK, gc.Equals, true) // Check the common name of the generated cert caCert, _, err := cert.ParseCertAndKey(cfgCertPEM, cfgKeyPEM) c.Assert(err, gc.IsNil) c.Assert(caCert.Subject.CommonName, gc.Equals, `juju-generated CA for environment "`+testing.SampleEnvName+`"`) // Check we can call Prepare again. env, err = environs.Prepare(cfg, ctx, store) c.Assert(err, gc.IsNil) c.Assert(env.Name(), gc.Equals, "erewhemos") c.Assert(env.Storage(), gc.NotNil) c.Assert(env.Config().AllAttrs(), gc.DeepEquals, info.BootstrapConfig()) } func (*OpenSuite) TestPrepareGeneratesDifferentAdminSecrets(c *gc.C) { baselineAttrs := dummy.SampleConfig().Merge(testing.Attrs{ "state-server": false, "name": "erewhemos", }).Delete( "admin-secret", ) cfg, err := config.New(config.NoDefaults, baselineAttrs) c.Assert(err, gc.IsNil) ctx := testing.Context(c) env0, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) adminSecret0 := env0.Config().AdminSecret() c.Assert(adminSecret0, gc.HasLen, 32) c.Assert(adminSecret0, gc.Matches, "^[0-9a-f]*$") env1, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) adminSecret1 := env1.Config().AdminSecret() c.Assert(adminSecret1, gc.HasLen, 32) c.Assert(adminSecret1, gc.Matches, "^[0-9a-f]*$") c.Assert(adminSecret1, gc.Not(gc.Equals), adminSecret0) } func (*OpenSuite) TestPrepareWithMissingKey(c *gc.C) { cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Delete("ca-cert", "ca-private-key").Merge( testing.Attrs{ "state-server": false, "name": "erewhemos", "ca-cert": string(testing.CACert), }, )) c.Assert(err, gc.IsNil) store := configstore.NewMem() env, err := environs.Prepare(cfg, testing.Context(c), store) c.Assert(err, gc.ErrorMatches, "cannot ensure CA certificate: environment configuration with a certificate but no CA private key") c.Assert(env, gc.IsNil) // Ensure that the config storage info is cleaned up. _, err = store.ReadInfo(cfg.Name()) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (*OpenSuite) TestPrepareWithExistingKeyPair(c *gc.C) { cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge( testing.Attrs{ "state-server": false, "name": "erewhemos", "ca-cert": string(testing.CACert), "ca-private-key": string(testing.CAKey), }, )) c.Assert(err, gc.IsNil) ctx := testing.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) cfgCertPEM, cfgCertOK := env.Config().CACert() cfgKeyPEM, cfgKeyOK := env.Config().CAPrivateKey() c.Assert(cfgCertOK, gc.Equals, true) c.Assert(cfgKeyOK, gc.Equals, true) c.Assert(string(cfgCertPEM), gc.DeepEquals, testing.CACert) c.Assert(string(cfgKeyPEM), gc.DeepEquals, testing.CAKey) } func (*OpenSuite) TestDestroy(c *gc.C) { cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge( testing.Attrs{ "state-server": false, "name": "erewhemos", }, )) c.Assert(err, gc.IsNil) store := configstore.NewMem() // Prepare the environment and sanity-check that // the config storage info has been made. ctx := testing.Context(c) e, err := environs.Prepare(cfg, ctx, store) c.Assert(err, gc.IsNil) _, err = store.ReadInfo(e.Name()) c.Assert(err, gc.IsNil) err = environs.Destroy(e, store) c.Assert(err, gc.IsNil) // Check that the environment has actually been destroyed // and that the config info has been destroyed too. _, _, err = e.StateInfo() c.Assert(err, gc.ErrorMatches, "environment has been destroyed") _, err = store.ReadInfo(e.Name()) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (*OpenSuite) TestNewFromAttrs(c *gc.C) { e, err := environs.NewFromAttrs(dummy.SampleConfig().Merge( testing.Attrs{ "state-server": false, "name": "erewhemos", }, )) c.Assert(err, gc.ErrorMatches, "environment is not prepared") c.Assert(e, gc.IsNil) } const checkEnv = ` environments: test: type: dummy state-server: false authorized-keys: i-am-a-key ` type checkEnvironmentSuite struct{} var _ = gc.Suite(&checkEnvironmentSuite{}) func (s *checkEnvironmentSuite) TearDownTest(c *gc.C) { dummy.Reset() } func (s *checkEnvironmentSuite) TestCheckEnvironment(c *gc.C) { defer testing.MakeFakeHome(c, checkEnv, "existing").Restore() ctx := testing.Context(c) environ, err := environs.PrepareFromName("test", ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) // VerifyStorage is sufficient for our tests and much simpler // than Bootstrap which calls it. stor := environ.Storage() err = environs.VerifyStorage(stor) c.Assert(err, gc.IsNil) err = environs.CheckEnvironment(environ) c.Assert(err, gc.IsNil) } func (s *checkEnvironmentSuite) TestCheckEnvironmentFileNotFound(c *gc.C) { defer testing.MakeFakeHome(c, checkEnv, "existing").Restore() ctx := testing.Context(c) environ, err := environs.PrepareFromName("test", ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) // VerifyStorage is sufficient for our tests and much simpler // than Bootstrap which calls it. stor := environ.Storage() err = environs.VerifyStorage(stor) c.Assert(err, gc.IsNil) // When the bootstrap-verify file does not exist, it still believes // the environment is a juju-core one because earlier versions // did not create that file. err = stor.Remove(environs.VerificationFilename) c.Assert(err, gc.IsNil) err = environs.CheckEnvironment(environ) c.Assert(err, gc.IsNil) } func (s *checkEnvironmentSuite) TestCheckEnvironmentGetFails(c *gc.C) { defer testing.MakeFakeHome(c, checkEnv, "existing").Restore() ctx := testing.Context(c) environ, err := environs.PrepareFromName("test", ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) // VerifyStorage is sufficient for our tests and much simpler // than Bootstrap which calls it. stor := environ.Storage() err = environs.VerifyStorage(stor) c.Assert(err, gc.IsNil) // When fetching the verification file from storage fails, // we get an InvalidEnvironmentError. someError := errors.Unauthorizedf("you shall not pass") dummy.Poison(stor, environs.VerificationFilename, someError) err = environs.CheckEnvironment(environ) c.Assert(err, gc.Equals, someError) } func (s *checkEnvironmentSuite) TestCheckEnvironmentBadContent(c *gc.C) { defer testing.MakeFakeHome(c, checkEnv, "existing").Restore() ctx := testing.Context(c) environ, err := environs.PrepareFromName("test", ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) // We mock a bad (eg. from a Python-juju environment) bootstrap-verify. stor := environ.Storage() content := "bad verification content" reader := strings.NewReader(content) err = stor.Put(environs.VerificationFilename, reader, int64(len(content))) c.Assert(err, gc.IsNil) // When the bootstrap-verify file contains unexpected content, // we get an InvalidEnvironmentError. err = environs.CheckEnvironment(environ) c.Assert(err, gc.Equals, environs.InvalidEnvironmentError) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sshstorage/�����������������������������������0000755�0000153�0000161�00000000000�12321735643�024713� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sshstorage/export_test.go���������������������0000644�0000153�0000161�00000000247�12321735642�027624� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshstorage var ( NewLineWrapWriter = newLineWrapWriter ) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sshstorage/storage.go�������������������������0000644�0000153�0000161�00000022535�12321735642�026714� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshstorage import ( "bufio" "bytes" "encoding/base64" "errors" "fmt" "io" "io/ioutil" "path" "sort" "strconv" "strings" "github.com/juju/loggo" coreerrors "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" ) var logger = loggo.GetLogger("juju.environs.sshstorage") // base64LineLength is the default line length for wrapping // output generated by the base64 command line utility. const base64LineLength = 76 // SSHStorage implements storage.Storage. // // The storage is created under sudo, and ownership given over to the // login uid/gid. This is done so that we don't require sudo, and by // consequence, don't require a pty, so we can interact with a script // via stdin. type SSHStorage struct { host string remotepath string tmpdir string cmd *ssh.Cmd stdin io.WriteCloser stdout io.ReadCloser scanner *bufio.Scanner } var sshCommand = func(host string, command ...string) *ssh.Cmd { return ssh.Command(host, command, nil) } type flockmode string const ( flockShared flockmode = "-s" flockExclusive flockmode = "-x" ) type NewSSHStorageParams struct { // Host is the host to connect to, in the format [user@]hostname. Host string // StorageDir is the root of the remote storage directory. StorageDir string // TmpDir is the remote temporary directory for storage. // A temporary directory must be specified, and should be located on the // same filesystem as the storage directory to ensure atomic writes. // The temporary directory will be created when NewSSHStorage is invoked // if it doesn't already exist; it will never be removed. NewSSHStorage // will attempt to reassign ownership to the login user, and will return // an error if it cannot do so. TmpDir string } // NewSSHStorage creates a new SSHStorage, connected to the // specified host, managing state under the specified remote path. func NewSSHStorage(params NewSSHStorageParams) (*SSHStorage, error) { if params.StorageDir == "" { return nil, errors.New("storagedir must be specified and non-empty") } if params.TmpDir == "" { return nil, errors.New("tmpdir must be specified and non-empty") } script := fmt.Sprintf( "install -d -g $SUDO_GID -o $SUDO_UID %s %s", utils.ShQuote(params.StorageDir), utils.ShQuote(params.TmpDir), ) cmd := sshCommand(params.Host, "sudo", "-n", "/bin/bash") var stderr bytes.Buffer cmd.Stderr = &stderr cmd.Stdin = strings.NewReader(script) if err := cmd.Run(); err != nil { err = fmt.Errorf("failed to create storage dir: %v (%v)", err, strings.TrimSpace(stderr.String())) return nil, err } // We could use sftp, but then we'd be at the mercy of // sftp's output messages for checking errors. Instead, // we execute an interactive bash shell. cmd = sshCommand(params.Host, "bash") stdin, err := cmd.StdinPipe() if err != nil { return nil, err } stdout, err := cmd.StdoutPipe() if err != nil { stdin.Close() return nil, err } // Combine stdout and stderr, so we can easily // get at catastrophic failure messages. cmd.Stderr = cmd.Stdout stor := &SSHStorage{ host: params.Host, remotepath: params.StorageDir, tmpdir: params.TmpDir, cmd: cmd, stdin: stdin, stdout: stdout, scanner: bufio.NewScanner(stdout), } cmd.Start() // Verify we have write permissions. _, err = stor.runf(flockExclusive, "touch %s", utils.ShQuote(params.StorageDir)) if err != nil { stdin.Close() stdout.Close() cmd.Wait() return nil, err } return stor, nil } // Close cleanly terminates the underlying SSH connection. func (s *SSHStorage) Close() error { s.stdin.Close() s.stdout.Close() return s.cmd.Wait() } func (s *SSHStorage) runf(flockmode flockmode, command string, args ...interface{}) (string, error) { command = fmt.Sprintf(command, args...) return s.run(flockmode, command, nil, 0) } // terminate closes the stdin, and appends any output to the input error. func (s *SSHStorage) terminate(err error) error { s.stdin.Close() var output string for s.scanner.Scan() { if len(output) > 0 { output += "\n" } output += s.scanner.Text() } if len(output) > 0 { err = fmt.Errorf("%v (output: %q)", err, output) } return err } func (s *SSHStorage) run(flockmode flockmode, command string, input io.Reader, inputlen int64) (string, error) { const rcPrefix = "JUJU-RC: " command = fmt.Sprintf( "SHELL=/bin/bash flock %s %s -c %s", flockmode, utils.ShQuote(s.remotepath), utils.ShQuote(command), ) stdin := bufio.NewWriter(s.stdin) if input != nil { command = fmt.Sprintf("base64 -d << '@EOF' | (%s)", command) } command = fmt.Sprintf("(%s) 2>&1; echo %s$?", command, rcPrefix) if _, err := stdin.WriteString(command + "\n"); err != nil { return "", fmt.Errorf("failed to write command: %v", err) } if input != nil { if err := copyAsBase64(stdin, input); err != nil { return "", s.terminate(fmt.Errorf("failed to write input: %v", err)) } } if err := stdin.Flush(); err != nil { return "", s.terminate(fmt.Errorf("failed to write input: %v", err)) } var output []string for s.scanner.Scan() { line := s.scanner.Text() if strings.HasPrefix(line, rcPrefix) { line := line[len(rcPrefix):] rc, err := strconv.Atoi(line) if err != nil { return "", fmt.Errorf("failed to parse exit code %q: %v", line, err) } outputJoined := strings.Join(output, "\n") if rc == 0 { return outputJoined, nil } return "", SSHStorageError{outputJoined, rc} } else { output = append(output, line) } } err := fmt.Errorf("failed to locate %q", rcPrefix) if len(output) > 0 { err = fmt.Errorf("%v (output: %q)", err, strings.Join(output, "\n")) } if scannerErr := s.scanner.Err(); scannerErr != nil { err = fmt.Errorf("%v (scanner error: %v)", err, scannerErr) } return "", err } func copyAsBase64(w *bufio.Writer, r io.Reader) error { wrapper := newLineWrapWriter(w, base64LineLength) encoder := base64.NewEncoder(base64.StdEncoding, wrapper) if _, err := io.Copy(encoder, r); err != nil { return err } if err := encoder.Close(); err != nil { return err } if _, err := w.WriteString("\n@EOF\n"); err != nil { return err } return nil } // path returns a remote absolute path for a storage object name. func (s *SSHStorage) path(name string) (string, error) { remotepath := path.Clean(path.Join(s.remotepath, name)) if !strings.HasPrefix(remotepath, s.remotepath) { return "", fmt.Errorf("%q escapes storage directory", name) } return remotepath, nil } // Get implements storage.StorageReader.Get. func (s *SSHStorage) Get(name string) (io.ReadCloser, error) { logger.Debugf("getting %q from storage", name) path, err := s.path(name) if err != nil { return nil, err } out, err := s.runf(flockShared, "base64 < %s", utils.ShQuote(path)) if err != nil { err := err.(SSHStorageError) if strings.Contains(err.Output, "No such file") { return nil, coreerrors.NewNotFoundError(err, "") } return nil, err } decoded, err := base64.StdEncoding.DecodeString(out) if err != nil { return nil, err } return ioutil.NopCloser(bytes.NewBuffer(decoded)), nil } // List implements storage.StorageReader.List. func (s *SSHStorage) List(prefix string) ([]string, error) { remotepath, err := s.path(prefix) if err != nil { return nil, err } dir, prefix := path.Split(remotepath) quotedDir := utils.ShQuote(dir) out, err := s.runf(flockShared, "(test -d %s && find %s -type f) || true", quotedDir, quotedDir) if err != nil { return nil, err } if out == "" { return nil, nil } var names []string for _, name := range strings.Split(out, "\n") { if strings.HasPrefix(name[len(dir):], prefix) { names = append(names, name[len(s.remotepath)+1:]) } } sort.Strings(names) return names, nil } // URL implements storage.StorageReader.URL. func (s *SSHStorage) URL(name string) (string, error) { path, err := s.path(name) if err != nil { return "", err } return fmt.Sprintf("sftp://%s/%s", s.host, path), nil } // DefaultConsistencyStrategy implements storage.StorageReader.ConsistencyStrategy. func (s *SSHStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { return utils.AttemptStrategy{} } // ShouldRetry is specified in the StorageReader interface. func (s *SSHStorage) ShouldRetry(err error) bool { return false } // Put implements storage.StorageWriter.Put func (s *SSHStorage) Put(name string, r io.Reader, length int64) error { logger.Debugf("putting %q (len %d) to storage", name, length) path, err := s.path(name) if err != nil { return err } path = utils.ShQuote(path) tmpdir := utils.ShQuote(s.tmpdir) // Write to a temporary file ($TMPFILE), then mv atomically. command := fmt.Sprintf("mkdir -p `dirname %s` && cat > $TMPFILE", path) command = fmt.Sprintf( "TMPFILE=`mktemp --tmpdir=%s` && ((%s && mv $TMPFILE %s) || rm -f $TMPFILE)", tmpdir, command, path, ) _, err = s.run(flockExclusive, command+"\n", r, length) return err } // Remove implements storage.StorageWriter.Remove func (s *SSHStorage) Remove(name string) error { path, err := s.path(name) if err != nil { return err } path = utils.ShQuote(path) _, err = s.runf(flockExclusive, "rm -f %s", path) return err } // RemoveAll implements storage.StorageWriter.RemoveAll func (s *SSHStorage) RemoveAll() error { _, err := s.runf(flockExclusive, "rm -fr %s/*", utils.ShQuote(s.remotepath)) return err } �������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sshstorage/suite_test.go����������������������0000644�0000153�0000161�00000000327�12321735642�027433� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshstorage_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sshstorage/error.go���������������������������0000644�0000153�0000161�00000000513�12321735642�026371� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshstorage import ( "fmt" ) type SSHStorageError struct { Output string ExitCode int } func (e SSHStorageError) Error() string { if e.Output == "" { return fmt.Sprintf("exit code %d", e.ExitCode) } return e.Output } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sshstorage/linewrapwriter.go������������������0000644�0000153�0000161�00000002544�12321735642�030324� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshstorage import ( "io" ) type lineWrapWriter struct { out io.Writer remain int max int } // NewLineWrapWriter returns an io.Writer that encloses the given // io.Writer, wrapping lines at the the specified line length. // It will panic if the line length is not positive. // // Note: there is no special consideration for input that // already contains newlines; this will simply add newlines // after every "lineLength" bytes. Moreover it gives no // consideration to multibyte utf-8 characters, which it can split // arbitrarily. // // This is currently only appropriate for wrapping base64-encoded // data, which is why it lives here. func newLineWrapWriter(out io.Writer, lineLength int) io.Writer { if lineLength <= 0 { panic("lineWrapWriter with line length <= 0") } return &lineWrapWriter{ out: out, remain: lineLength, max: lineLength, } } func (w *lineWrapWriter) Write(buf []byte) (int, error) { total := 0 for len(buf) >= w.remain { n, err := w.out.Write(buf[0:w.remain]) w.remain -= n total += n if err != nil { return total, err } if _, err := w.out.Write([]byte("\n")); err != nil { return total, err } w.remain = w.max buf = buf[n:] } n, err := w.out.Write(buf) w.remain -= n return total + n, err } ������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sshstorage/storage_test.go��������������������0000644�0000153�0000161�00000032251�12321735642�027747� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshstorage import ( "bytes" "fmt" "io" "io/ioutil" "os" "os/exec" "path" "path/filepath" "regexp" "strings" "time" "github.com/juju/testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/storage" coreerrors "launchpad.net/juju-core/errors" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" ) type storageSuite struct { testbase.LoggingSuite bin string } var _ = gc.Suite(&storageSuite{}) func (s *storageSuite) sshCommand(c *gc.C, host string, command ...string) *ssh.Cmd { script := []byte("#!/bin/bash\n" + strings.Join(command, " ")) err := ioutil.WriteFile(filepath.Join(s.bin, "ssh"), script, 0755) c.Assert(err, gc.IsNil) client, err := ssh.NewOpenSSHClient() c.Assert(err, gc.IsNil) return client.Command(host, command, nil) } func newSSHStorage(host, storageDir, tmpDir string) (*SSHStorage, error) { params := NewSSHStorageParams{ Host: host, StorageDir: storageDir, TmpDir: tmpDir, } return NewSSHStorage(params) } // flockBin is the path to the original "flock" binary. var flockBin string func (s *storageSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) var err error flockBin, err = exec.LookPath("flock") c.Assert(err, gc.IsNil) s.bin = c.MkDir() s.PatchEnvPathPrepend(s.bin) // Create a "sudo" command which shifts away the "-n", sets // SUDO_UID/SUDO_GID, and executes the remaining args. err = ioutil.WriteFile(filepath.Join(s.bin, "sudo"), []byte( "#!/bin/sh\nshift; export SUDO_UID=`id -u` SUDO_GID=`id -g`; exec \"$@\"", ), 0755) c.Assert(err, gc.IsNil) restoreSshCommand := testing.PatchValue(&sshCommand, func(host string, command ...string) *ssh.Cmd { return s.sshCommand(c, host, command...) }) s.AddSuiteCleanup(func(*gc.C) { restoreSshCommand() }) // Create a new "flock" which calls the original, but in non-blocking mode. data := []byte(fmt.Sprintf("#!/bin/sh\nexec %s --nonblock \"$@\"", flockBin)) err = ioutil.WriteFile(filepath.Join(s.bin, "flock"), data, 0755) c.Assert(err, gc.IsNil) } func (s *storageSuite) makeStorage(c *gc.C) (storage *SSHStorage, storageDir string) { storageDir = c.MkDir() storage, err := newSSHStorage("example.com", storageDir, storageDir+"-tmp") c.Assert(err, gc.IsNil) c.Assert(storage, gc.NotNil) s.AddCleanup(func(*gc.C) { storage.Close() }) return storage, storageDir } // createFiles creates empty files in the storage directory // with the given storage names. func createFiles(c *gc.C, storageDir string, names ...string) { for _, name := range names { path := filepath.Join(storageDir, filepath.FromSlash(name)) dir := filepath.Dir(path) if err := os.MkdirAll(dir, 0755); err != nil { c.Assert(err, jc.Satisfies, os.IsExist) } err := ioutil.WriteFile(path, nil, 0644) c.Assert(err, gc.IsNil) } } func (s *storageSuite) TestnewSSHStorage(c *gc.C) { storageDir := c.MkDir() // Run this block twice to ensure newSSHStorage can reuse // an existing storage location. for i := 0; i < 2; i++ { stor, err := newSSHStorage("example.com", storageDir, storageDir+"-tmp") c.Assert(err, gc.IsNil) c.Assert(stor, gc.NotNil) c.Assert(stor.Close(), gc.IsNil) } err := os.RemoveAll(storageDir) c.Assert(err, gc.IsNil) // You must have permissions to create the directory. storageDir = c.MkDir() err = os.Chmod(storageDir, 0555) c.Assert(err, gc.IsNil) _, err = newSSHStorage("example.com", filepath.Join(storageDir, "subdir"), storageDir+"-tmp") c.Assert(err, gc.ErrorMatches, "(.|\n)*cannot change owner and permissions of(.|\n)*") } func (s *storageSuite) TestPathValidity(c *gc.C) { stor, storageDir := s.makeStorage(c) err := os.Mkdir(filepath.Join(storageDir, "a"), 0755) c.Assert(err, gc.IsNil) createFiles(c, storageDir, "a/b") for _, prefix := range []string{"..", "a/../.."} { c.Logf("prefix: %q", prefix) _, err := storage.List(stor, prefix) c.Check(err, gc.ErrorMatches, regexp.QuoteMeta(fmt.Sprintf("%q escapes storage directory", prefix))) } // Paths are always relative, so a leading "/" may as well not be there. names, err := storage.List(stor, "/") c.Assert(err, gc.IsNil) c.Assert(names, gc.DeepEquals, []string{"a/b"}) // Paths will be canonicalised. names, err = storage.List(stor, "a/..") c.Assert(err, gc.IsNil) c.Assert(names, gc.DeepEquals, []string{"a/b"}) } func (s *storageSuite) TestGet(c *gc.C) { stor, storageDir := s.makeStorage(c) data := []byte("abc\000def") err := os.Mkdir(filepath.Join(storageDir, "a"), 0755) c.Assert(err, gc.IsNil) for _, name := range []string{"b", filepath.Join("a", "b")} { err = ioutil.WriteFile(filepath.Join(storageDir, name), data, 0644) c.Assert(err, gc.IsNil) r, err := storage.Get(stor, name) c.Assert(err, gc.IsNil) out, err := ioutil.ReadAll(r) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, data) } _, err = storage.Get(stor, "notthere") c.Assert(err, jc.Satisfies, coreerrors.IsNotFoundError) } func (s *storageSuite) TestWriteFailure(c *gc.C) { // Invocations: // 1: first "install" // 2: touch, Put // 3: second "install" // 4: touch var invocations int badSshCommand := func(host string, command ...string) *ssh.Cmd { invocations++ switch invocations { case 1, 3: return s.sshCommand(c, host, "true") case 2: // Note: must close stdin before responding the first time, or // the second command will race with closing stdin, and may // flush first. return s.sshCommand(c, host, "head -n 1 > /dev/null; exec 0<&-; echo JUJU-RC: 0; echo blah blah; echo more") case 4: return s.sshCommand(c, host, `head -n 1 > /dev/null; echo "Hey it's JUJU-RC: , but not at the beginning of the line"; echo more`) default: c.Errorf("unexpected invocation: #%d, %s", invocations, command) return nil } } s.PatchValue(&sshCommand, badSshCommand) stor, err := newSSHStorage("example.com", c.MkDir(), c.MkDir()) c.Assert(err, gc.IsNil) defer stor.Close() err = stor.Put("whatever", bytes.NewBuffer(nil), 0) c.Assert(err, gc.ErrorMatches, `failed to write input: write \|1: broken pipe \(output: "blah blah\\nmore"\)`) _, err = newSSHStorage("example.com", c.MkDir(), c.MkDir()) c.Assert(err, gc.ErrorMatches, `failed to locate "JUJU-RC: " \(output: "Hey it's JUJU-RC: , but not at the beginning of the line\\nmore"\)`) } func (s *storageSuite) TestPut(c *gc.C) { stor, storageDir := s.makeStorage(c) data := []byte("abc\000def") for _, name := range []string{"b", filepath.Join("a", "b")} { err := stor.Put(name, bytes.NewBuffer(data), int64(len(data))) c.Assert(err, gc.IsNil) out, err := ioutil.ReadFile(filepath.Join(storageDir, name)) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, data) } } func (s *storageSuite) assertList(c *gc.C, stor storage.StorageReader, prefix string, expected []string) { c.Logf("List: %v", prefix) names, err := storage.List(stor, prefix) c.Assert(err, gc.IsNil) c.Assert(names, gc.DeepEquals, expected) } func (s *storageSuite) TestList(c *gc.C) { stor, storageDir := s.makeStorage(c) s.assertList(c, stor, "", nil) // Directories don't show up in List. err := os.Mkdir(filepath.Join(storageDir, "a"), 0755) c.Assert(err, gc.IsNil) s.assertList(c, stor, "", nil) s.assertList(c, stor, "a", nil) createFiles(c, storageDir, "a/b1", "a/b2", "b") s.assertList(c, stor, "", []string{"a/b1", "a/b2", "b"}) s.assertList(c, stor, "a", []string{"a/b1", "a/b2"}) s.assertList(c, stor, "a/b", []string{"a/b1", "a/b2"}) s.assertList(c, stor, "a/b1", []string{"a/b1"}) s.assertList(c, stor, "a/b3", nil) s.assertList(c, stor, "a/b/c", nil) s.assertList(c, stor, "b", []string{"b"}) } func (s *storageSuite) TestRemove(c *gc.C) { stor, storageDir := s.makeStorage(c) err := os.Mkdir(filepath.Join(storageDir, "a"), 0755) c.Assert(err, gc.IsNil) createFiles(c, storageDir, "a/b1", "a/b2") c.Assert(stor.Remove("a"), gc.ErrorMatches, "rm: cannot remove.*Is a directory") s.assertList(c, stor, "", []string{"a/b1", "a/b2"}) c.Assert(stor.Remove("a/b"), gc.IsNil) // doesn't exist; not an error s.assertList(c, stor, "", []string{"a/b1", "a/b2"}) c.Assert(stor.Remove("a/b2"), gc.IsNil) s.assertList(c, stor, "", []string{"a/b1"}) c.Assert(stor.Remove("a/b1"), gc.IsNil) s.assertList(c, stor, "", nil) } func (s *storageSuite) TestRemoveAll(c *gc.C) { stor, storageDir := s.makeStorage(c) err := os.Mkdir(filepath.Join(storageDir, "a"), 0755) c.Assert(err, gc.IsNil) createFiles(c, storageDir, "a/b1", "a/b2") s.assertList(c, stor, "", []string{"a/b1", "a/b2"}) c.Assert(stor.RemoveAll(), gc.IsNil) s.assertList(c, stor, "", nil) // RemoveAll does not remove the base storage directory. _, err = os.Stat(storageDir) c.Assert(err, gc.IsNil) } func (s *storageSuite) TestURL(c *gc.C) { stor, storageDir := s.makeStorage(c) url, err := stor.URL("a/b") c.Assert(err, gc.IsNil) c.Assert(url, gc.Equals, "sftp://example.com/"+path.Join(storageDir, "a/b")) } func (s *storageSuite) TestDefaultConsistencyStrategy(c *gc.C) { stor, _ := s.makeStorage(c) c.Assert(stor.DefaultConsistencyStrategy(), gc.Equals, utils.AttemptStrategy{}) } const defaultFlockTimeout = 5 * time.Second // flock is a test helper that flocks a file, executes "sleep" with the // specified duration, the command is terminated in the test tear down. func (s *storageSuite) flock(c *gc.C, mode flockmode, lockfile string) { sleepcmd := fmt.Sprintf("echo started && sleep %vs", defaultFlockTimeout.Seconds()) cmd := exec.Command(flockBin, "--nonblock", "--close", string(mode), lockfile, "-c", sleepcmd) stdout, err := cmd.StdoutPipe() c.Assert(err, gc.IsNil) c.Assert(cmd.Start(), gc.IsNil) // Make sure the flock has been taken before returning by reading stdout waiting for "started" _, err = io.ReadFull(stdout, make([]byte, len("started"))) c.Assert(err, gc.IsNil) s.AddCleanup(func(*gc.C) { cmd.Process.Kill() cmd.Process.Wait() }) } func (s *storageSuite) TestCreateFailsIfFlockNotAvailable(c *gc.C) { storageDir := c.MkDir() s.flock(c, flockShared, storageDir) // Creating storage requires an exclusive lock initially. // // flock exits with exit code 1 if it can't acquire the // lock immediately in non-blocking mode (which the tests force). _, err := newSSHStorage("example.com", storageDir, storageDir+"-tmp") c.Assert(err, gc.ErrorMatches, "exit code 1") } func (s *storageSuite) TestWithSharedLocks(c *gc.C) { stor, storageDir := s.makeStorage(c) // Get and List should be able to proceed with a shared lock. // All other methods should fail. createFiles(c, storageDir, "a") s.flock(c, flockShared, storageDir) _, err := storage.Get(stor, "a") c.Assert(err, gc.IsNil) _, err = storage.List(stor, "") c.Assert(err, gc.IsNil) c.Assert(stor.Put("a", bytes.NewBuffer(nil), 0), gc.NotNil) c.Assert(stor.Remove("a"), gc.NotNil) c.Assert(stor.RemoveAll(), gc.NotNil) } func (s *storageSuite) TestWithExclusiveLocks(c *gc.C) { stor, storageDir := s.makeStorage(c) // None of the methods (apart from URL) should be able to do anything // while an exclusive lock is held. s.flock(c, flockExclusive, storageDir) _, err := stor.URL("a") c.Assert(err, gc.IsNil) c.Assert(stor.Put("a", bytes.NewBuffer(nil), 0), gc.NotNil) c.Assert(stor.Remove("a"), gc.NotNil) c.Assert(stor.RemoveAll(), gc.NotNil) _, err = storage.Get(stor, "a") c.Assert(err, gc.NotNil) _, err = storage.List(stor, "") c.Assert(err, gc.NotNil) } func (s *storageSuite) TestPutLarge(c *gc.C) { stor, _ := s.makeStorage(c) buf := make([]byte, 1048576) err := stor.Put("ohmy", bytes.NewBuffer(buf), int64(len(buf))) c.Assert(err, gc.IsNil) } func (s *storageSuite) TestStorageDirBlank(c *gc.C) { tmpdir := c.MkDir() _, err := newSSHStorage("example.com", "", tmpdir) c.Assert(err, gc.ErrorMatches, "storagedir must be specified and non-empty") } func (s *storageSuite) TestTmpDirBlank(c *gc.C) { storageDir := c.MkDir() _, err := newSSHStorage("example.com", storageDir, "") c.Assert(err, gc.ErrorMatches, "tmpdir must be specified and non-empty") } func (s *storageSuite) TestTmpDirExists(c *gc.C) { // If we explicitly set the temporary directory, // it may already exist, but doesn't have to. storageDir := c.MkDir() tmpdirs := []string{storageDir, filepath.Join(storageDir, "subdir")} for _, tmpdir := range tmpdirs { stor, err := newSSHStorage("example.com", storageDir, tmpdir) defer stor.Close() c.Assert(err, gc.IsNil) err = stor.Put("test-write", bytes.NewReader(nil), 0) c.Assert(err, gc.IsNil) } } func (s *storageSuite) TestTmpDirPermissions(c *gc.C) { // newSSHStorage will fail if it can't create or change the // permissions of the temporary directory. storageDir := c.MkDir() tmpdir := c.MkDir() os.Chmod(tmpdir, 0400) defer os.Chmod(tmpdir, 0755) _, err := newSSHStorage("example.com", storageDir, filepath.Join(tmpdir, "subdir2")) c.Assert(err, gc.ErrorMatches, ".*install: cannot create directory.*Permission denied.*") } func (s *storageSuite) TestPathCharacters(c *gc.C) { storageDirBase := c.MkDir() storageDir := filepath.Join(storageDirBase, "'") tmpdir := filepath.Join(storageDirBase, `"`) c.Assert(os.Mkdir(storageDir, 0755), gc.IsNil) c.Assert(os.Mkdir(tmpdir, 0755), gc.IsNil) _, err := newSSHStorage("example.com", storageDir, tmpdir) c.Assert(err, gc.IsNil) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sshstorage/linewrapwriter_test.go�������������0000644�0000153�0000161�00000006474�12321735642�031371� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshstorage_test import ( "bytes" "errors" "io" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/sshstorage" ) type wrapWriterSuite struct{} var _ = gc.Suite(&wrapWriterSuite{}) func (*wrapWriterSuite) TestLineWrapWriterBadLength(c *gc.C) { var buf bytes.Buffer c.Assert(func() { sshstorage.NewLineWrapWriter(&buf, 0) }, gc.PanicMatches, "lineWrapWriter with line length <= 0") c.Assert(func() { sshstorage.NewLineWrapWriter(&buf, -1) }, gc.PanicMatches, "lineWrapWriter with line length <= 0") } func (*wrapWriterSuite) TestLineWrapWriter(c *gc.C) { type test struct { input []string lineLength int expected string } tests := []test{{ input: []string{""}, lineLength: 1, expected: "", }, { input: []string{"hi!"}, lineLength: 1, expected: "h\ni\n!\n", }, { input: []string{"hi!"}, lineLength: 2, // Note: no trailing newline. expected: "hi\n!", }, { input: []string{"", "h", "i!"}, lineLength: 2, expected: "hi\n!", }, { input: []string{"", "h", "i!"}, lineLength: 2, expected: "hi\n!", }, { input: []string{"hi", "!!"}, lineLength: 2, expected: "hi\n!!\n", }, { input: []string{"hi", "!", "!"}, lineLength: 2, expected: "hi\n!!\n", }, { input: []string{"h", "i", "!!"}, lineLength: 2, expected: "hi\n!!\n", }} for i, t := range tests { c.Logf("test %d: %q, line length %d", i, t.input, t.lineLength) var buf bytes.Buffer w := sshstorage.NewLineWrapWriter(&buf, t.lineLength) c.Assert(w, gc.NotNil) for _, input := range t.input { n, err := w.Write([]byte(input)) c.Assert(err, gc.IsNil) c.Assert(n, gc.Equals, len(input)) } c.Assert(buf.String(), gc.Equals, t.expected) } } type limitedWriter struct { io.Writer remaining int } var writeLimited = errors.New("write limited") func (w *limitedWriter) Write(buf []byte) (int, error) { inputlen := len(buf) if len(buf) > w.remaining { buf = buf[:w.remaining] } n, err := w.Writer.Write(buf) w.remaining -= n if n < inputlen && err == nil { err = writeLimited } return n, err } func (*wrapWriterSuite) TestLineWrapWriterErrors(c *gc.C) { // Note: after an error is returned, all bets are off. // In the only place we use this code, we bail out immediately. const lineLength = 3 type test struct { input string output string limit int written int err error } tests := []test{{ input: "abc", output: "abc", limit: 3, // "\n" will be limited written: 3, err: writeLimited, }, { input: "abc", output: "abc\n", limit: 4, written: 3, // 3/3 bytes of input }, { input: "abc", output: "ab", limit: 2, written: 2, // 2/3 bytes of input err: writeLimited, }, { input: "abc!", output: "abc\n", limit: 4, written: 3, // 3/4 bytes of input err: writeLimited, }} for i, t := range tests { c.Logf("test %d: %q, limit %d", i, t.input, t.limit) var buf bytes.Buffer wrapWriter := &limitedWriter{&buf, t.limit} w := sshstorage.NewLineWrapWriter(wrapWriter, lineLength) c.Assert(w, gc.NotNil) n, err := w.Write([]byte(t.input)) c.Assert(n, gc.Equals, t.written) c.Assert(buf.String(), gc.Equals, t.output) c.Assert(err, gc.Equals, t.err) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/cloudinit.go����������������������������������0000644�0000153�0000161�00000015447�12321735776�025074� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs import ( "fmt" "github.com/errgo/errgo" "launchpad.net/juju-core/agent" coreCloudinit "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/utils" ) // DataDir is the default data directory. // Tests can override this where needed, so they don't need to mess with global // system state. var DataDir = agent.DefaultDataDir // CloudInitOutputLog is the default cloud-init-output.log file path. const CloudInitOutputLog = "/var/log/cloud-init-output.log" // MongoServiceName is the default Upstart service name for Mongo. const MongoServiceName = "juju-db" // NewMachineConfig sets up a basic machine configuration, for a non-bootstrap // node. You'll still need to supply more information, but this takes care of // the fixed entries and the ones that are always needed. func NewMachineConfig(machineID, machineNonce string, stateInfo *state.Info, apiInfo *api.Info) *cloudinit.MachineConfig { return &cloudinit.MachineConfig{ // Fixed entries. DataDir: DataDir, LogDir: agent.DefaultLogDir, Jobs: []params.MachineJob{params.JobHostUnits}, CloudInitOutputLog: CloudInitOutputLog, MachineAgentServiceName: "jujud-" + names.MachineTag(machineID), MongoServiceName: MongoServiceName, // Parameter entries. MachineId: machineID, MachineNonce: machineNonce, StateInfo: stateInfo, APIInfo: apiInfo, } } // NewBootstrapMachineConfig sets up a basic machine configuration for a // bootstrap node. You'll still need to supply more information, but this // takes care of the fixed entries and the ones that are always needed. func NewBootstrapMachineConfig(privateSystemSSHKey string) *cloudinit.MachineConfig { // For a bootstrap instance, FinishMachineConfig will provide the // state.Info and the api.Info. The machine id must *always* be "0". mcfg := NewMachineConfig("0", state.BootstrapNonce, nil, nil) mcfg.StateServer = true mcfg.SystemPrivateSSHKey = privateSystemSSHKey mcfg.Jobs = []params.MachineJob{params.JobManageEnviron, params.JobHostUnits} return mcfg } // PopulateMachineConfig is called both from the FinishMachineConfig below, // which does have access to the environment config, and from the container // provisioners, which don't have access to the environment config. Everything // that is needed to provision a container needs to be returned to the // provisioner in the ContainerConfig structure. Those values are then used to // call this function. func PopulateMachineConfig(mcfg *cloudinit.MachineConfig, providerType, authorizedKeys string, sslHostnameVerification bool, proxy, aptProxy osenv.ProxySettings, ) error { if authorizedKeys == "" { return fmt.Errorf("environment configuration has no authorized-keys") } mcfg.AuthorizedKeys = authorizedKeys if mcfg.AgentEnvironment == nil { mcfg.AgentEnvironment = make(map[string]string) } mcfg.AgentEnvironment[agent.ProviderType] = providerType mcfg.AgentEnvironment[agent.ContainerType] = string(mcfg.MachineContainerType) mcfg.DisableSSLHostnameVerification = !sslHostnameVerification mcfg.ProxySettings = proxy mcfg.AptProxySettings = aptProxy return nil } // FinishMachineConfig sets fields on a MachineConfig that can be determined by // inspecting a plain config.Config and the machine constraints at the last // moment before bootstrapping. It assumes that the supplied Config comes from // an environment that has passed through all the validation checks in the // Bootstrap func, and that has set an agent-version (via finding the tools to, // use for bootstrap, or otherwise). // TODO(fwereade) This function is not meant to be "good" in any serious way: // it is better that this functionality be collected in one place here than // that it be spread out across 3 or 4 providers, but this is its only // redeeming feature. func FinishMachineConfig(mcfg *cloudinit.MachineConfig, cfg *config.Config, cons constraints.Value) (err error) { defer utils.ErrorContextf(&err, "cannot complete machine configuration") if err := PopulateMachineConfig( mcfg, cfg.Type(), cfg.AuthorizedKeys(), cfg.SSLHostnameVerification(), cfg.ProxySettings(), cfg.AptProxySettings(), ); err != nil { return err } // The following settings are only appropriate at bootstrap time. At the // moment, the only state server is the bootstrap node, but this // will probably change. if !mcfg.StateServer { return nil } if mcfg.APIInfo != nil || mcfg.StateInfo != nil { return fmt.Errorf("machine configuration already has api/state info") } caCert, hasCACert := cfg.CACert() if !hasCACert { return fmt.Errorf("environment configuration has no ca-cert") } password := cfg.AdminSecret() if password == "" { return fmt.Errorf("environment configuration has no admin-secret") } passwordHash := utils.UserPasswordHash(password, utils.CompatSalt) mcfg.APIInfo = &api.Info{Password: passwordHash, CACert: caCert} mcfg.StateInfo = &state.Info{Password: passwordHash, CACert: caCert} mcfg.StatePort = cfg.StatePort() mcfg.APIPort = cfg.APIPort() mcfg.Constraints = cons if mcfg.Config, err = BootstrapConfig(cfg); err != nil { return err } // These really are directly relevant to running a state server. cert, key, err := cfg.GenerateStateServerCertAndKey() if err != nil { return errgo.Annotate(err, "cannot generate state server certificate") } mcfg.StateServerCert = cert mcfg.StateServerKey = key return nil } func configureCloudinit(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config) error { // When bootstrapping, we only want to apt-get update/upgrade // and setup the SSH keys. The rest we leave to cloudinit/sshinit. if mcfg.StateServer { return cloudinit.ConfigureBasic(mcfg, cloudcfg) } return cloudinit.Configure(mcfg, cloudcfg) } // ComposeUserData fills out the provided cloudinit configuration structure // so it is suitable for initialising a machine with the given configuration, // and then renders it and returns it as a binary (gzipped) blob of user data. // // If the provided cloudcfg is nil, a new one will be created internally. func ComposeUserData(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config) ([]byte, error) { if cloudcfg == nil { cloudcfg = coreCloudinit.New() } if err := configureCloudinit(mcfg, cloudcfg); err != nil { return nil, err } data, err := cloudcfg.Render() logger.Tracef("Generated cloud init:\n%s", string(data)) if err != nil { return nil, err } return utils.Gzip(data), nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/��������������������������������0000755�0000153�0000161�00000000000�12321735642�025420� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/json.go�������������������������0000644�0000153�0000161�00000003203�12321735642�026716� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package simplestreams import ( "encoding/json" "reflect" ) // itemCollection is a clone of ItemCollection, but // does not implement json.Unmarshaler. type itemCollection struct { Items map[string]*json.RawMessage `json:"items"` Arch string `json:"arch,omitempty"` Version string `json:"version,omitempty"` Series string `json:"release,omitempty"` RegionName string `json:"region,omitempty"` Endpoint string `json:"endpoint,omitempty"` } // ItemsCollection.UnmarshalJSON unmarshals the ItemCollection, // storing the raw bytes for each item. These can later be // unmarshalled again into product-specific types. func (c *ItemCollection) UnmarshalJSON(b []byte) error { var raw itemCollection if err := json.Unmarshal(b, &raw); err != nil { return err } c.rawItems = raw.Items c.Items = make(map[string]interface{}, len(raw.Items)) c.Arch = raw.Arch c.Version = raw.Version c.Series = raw.Series c.RegionName = raw.RegionName c.Endpoint = raw.Endpoint for key, rawv := range raw.Items { var v interface{} if err := json.Unmarshal([]byte(*rawv), &v); err != nil { return err } c.Items[key] = v } return nil } func (c *ItemCollection) construct(itemType reflect.Type) error { for key, rawv := range c.rawItems { itemValuePtr := reflect.New(itemType) err := json.Unmarshal([]byte(*rawv), itemValuePtr.Interface()) if err != nil { return err } c.Items[key] = itemValuePtr.Interface() } return nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/validation.go�������������������0000644�0000153�0000161�00000001227�12321735642�030103� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package simplestreams // MetadataValidator instances can provide parameters used to query simplestreams // metadata to find information for the specified parameters. If region is "", // then the implementation may use its own default region if it has one, // or else returns an error. type MetadataValidator interface { MetadataLookupParams(region string) (*MetadataLookupParams, error) } type MetadataLookupParams struct { Region string Series string Architectures []string Endpoint string Sources []DataSource Stream string } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/export_test.go������������������0000644�0000153�0000161�00000001703�12321735642�030330� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package simplestreams func ExtractCatalogsForProducts(metadata CloudMetadata, productIds []string) []MetadataCatalog { return metadata.extractCatalogsForProducts(productIds) } func ExtractIndexes(ind Indices) IndexMetadataSlice { return ind.extractIndexes() } func HasCloud(metadata IndexMetadata, cloud CloudSpec) bool { return metadata.hasCloud(cloud) } func HasProduct(metadata IndexMetadata, prodIds []string) bool { return metadata.hasProduct(prodIds) } func Filter(entries IndexMetadataSlice, match func(*IndexMetadata) bool) IndexMetadataSlice { return entries.filter(match) } func SetSeriesVersions(value map[string]string) func() { origVersions := seriesVersions origUpdated := updatedseriesVersions seriesVersions = value updatedseriesVersions = false return func() { seriesVersions = origVersions updatedseriesVersions = origUpdated } } �������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/simplestreams.go����������������0000644�0000153�0000161�00000103303�12321735642�030637� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The simplestreams package supports locating, parsing, and filtering metadata in simplestreams format. // See http://launchpad.net/simplestreams and in particular the doc/README file in that project for more information // about the file formats. // // Users of this package provide an empty struct and a matching function to be able to query and return a list // of typed values for a given criteria. package simplestreams import ( "bufio" "encoding/json" "fmt" "io" "io/ioutil" "os" "path" "reflect" "sort" "strings" "sync" "github.com/juju/loggo" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" ) var logger = loggo.GetLogger("juju.environs.simplestreams") type ResolveInfo struct { Source string `yaml:"source" json:"source"` Signed bool `yaml:"signed" json:"signed"` IndexURL string `yaml:"indexURL" json:"indexURL"` MirrorURL string `yaml:"mirrorURL,omitempty" json:"mirrorURL,omitempty"` } // CloudSpec uniquely defines a specific cloud deployment. type CloudSpec struct { Region string `json:"region"` Endpoint string `json:"endpoint"` } // equals returns true if spec == other, allowing for endpoints // with or without a trailing "/". func (spec *CloudSpec) equals(other *CloudSpec) bool { if spec.Region != other.Region { return false } specEndpoint := spec.Endpoint if !strings.HasSuffix(specEndpoint, "/") { specEndpoint += "/" } otherEndpoint := other.Endpoint if !strings.HasSuffix(otherEndpoint, "/") { otherEndpoint += "/" } return specEndpoint == otherEndpoint } // EmptyCloudSpec is used when we want all records regardless of cloud to be loaded. var EmptyCloudSpec = CloudSpec{} // HasRegion is implemented by instances which can provide a region to which they belong. // A region is defined by region name and endpoint. type HasRegion interface { // Region returns the necessary attributes to uniquely identify this cloud instance. // Currently these attributes are "region" and "endpoint" values. Region() (CloudSpec, error) } type LookupConstraint interface { // Generates a string array representing product ids formed similarly to an ISCSI qualified name (IQN). Ids() ([]string, error) // Returns the constraint parameters. Params() LookupParams } // LookupParams defines criteria used to find a metadata record. // Derived structs implement the Ids() method. type LookupParams struct { CloudSpec Series []string Arches []string // Stream can be "" or "released" for the default "released" stream, // or "daily" for daily images, or any other stream that the available // simplestreams metadata supports. Stream string } func (p LookupParams) Params() LookupParams { return p } // seriesVersions provides a mapping between Ubuntu series names and version numbers. // The values here are current as of the time of writing. On Ubuntu systems, we update // these values from /usr/share/distro-info/ubuntu.csv to ensure we have the latest values. // On non-Ubuntu systems, these values provide a nice fallback option. // Exported so tests can change the values to ensure the distro-info lookup works. var seriesVersions = map[string]string{ "precise": "12.04", "quantal": "12.10", "raring": "13.04", "saucy": "13.10", "trusty": "14.04", } var ( seriesVersionsMutex sync.Mutex updatedseriesVersions bool ) // SeriesVersion returns the version number for the specified Ubuntu series. func SeriesVersion(series string) (string, error) { if series == "" { panic("cannot pass empty series to SeriesVersion()") } seriesVersionsMutex.Lock() defer seriesVersionsMutex.Unlock() if vers, ok := seriesVersions[series]; ok { return vers, nil } updateSeriesVersions() if vers, ok := seriesVersions[series]; ok { return vers, nil } return "", fmt.Errorf("invalid series %q", series) } // Supported series returns the Ubuntu series for which we expect to find metadata. func SupportedSeries() []string { seriesVersionsMutex.Lock() defer seriesVersionsMutex.Unlock() updateSeriesVersions() var series []string for s := range seriesVersions { series = append(series, s) } return series } func updateSeriesVersions() { if !updatedseriesVersions { err := updateDistroInfo() if err != nil { logger.Warningf("failed to update distro info: %v", err) } updatedseriesVersions = true } } // updateDistroInfo updates seriesVersions from /usr/share/distro-info/ubuntu.csv if possible.. func updateDistroInfo() error { // We need to find the series version eg 12.04 from the series eg precise. Use the information found in // /usr/share/distro-info/ubuntu.csv provided by distro-info-data package. f, err := os.Open("/usr/share/distro-info/ubuntu.csv") if err != nil { // On non-Ubuntu systems this file won't exist but that's expected. return nil } defer f.Close() bufRdr := bufio.NewReader(f) // Only find info for precise or later. preciseOrLaterFound := false for { line, err := bufRdr.ReadString('\n') if err == io.EOF { break } if err != nil { return fmt.Errorf("reading distro info file file: %v", err) } // lines are of the form: "12.04 LTS,Precise Pangolin,precise,2011-10-13,2012-04-26,2017-04-26" parts := strings.Split(line, ",") // Ignore any malformed lines. if len(parts) < 3 { continue } series := parts[2] if series == "precise" { preciseOrLaterFound = true } if series != "precise" && !preciseOrLaterFound { continue } // the numeric version may contain a LTS moniker so strip that out. seriesInfo := strings.Split(parts[0], " ") seriesVersions[series] = seriesInfo[0] } return nil } // The following structs define the data model used in the JSON metadata files. // Not every model attribute is defined here, only the ones we care about. // See the doc/README file in lp:simplestreams for more information. // Metadata attribute values may point to a map of attribute values (aka aliases) and these attributes // are used to override/augment the existing attributes. type attributeValues map[string]string type aliasesByAttribute map[string]attributeValues type CloudMetadata struct { Products map[string]MetadataCatalog `json:"products"` Aliases map[string]aliasesByAttribute `json:"_aliases,omitempty"` Updated string `json:"updated"` Format string `json:"format"` ContentId string `json:"content_id"` } type MetadataCatalog struct { Series string `json:"release,omitempty"` Version string `json:"version,omitempty"` Arch string `json:"arch,omitempty"` RegionName string `json:"region,omitempty"` Endpoint string `json:"endpoint,omitempty"` // Items is a mapping from version to an ItemCollection, // where the version is the date the items were produced, // in the format YYYYMMDD. Items map[string]*ItemCollection `json:"versions"` } type ItemCollection struct { rawItems map[string]*json.RawMessage Items map[string]interface{} `json:"items"` Arch string `json:"arch,omitempty"` Series string `json:"release,omitempty"` Version string `json:"version,omitempty"` RegionName string `json:"region,omitempty"` Endpoint string `json:"endpoint,omitempty"` } // These structs define the model used for metadata indices. type Indices struct { Indexes map[string]*IndexMetadata `json:"index"` Updated string `json:"updated"` Format string `json:"format"` } // Exported for testing. type IndexReference struct { Indices MirroredProductsPath string Source DataSource valueParams ValueParams } type IndexMetadata struct { Updated string `json:"updated"` Format string `json:"format"` DataType string `json:"datatype"` CloudName string `json:"cloudname,omitempty"` Clouds []CloudSpec `json:"clouds,omitempty"` ProductsFilePath string `json:"path"` ProductIds []string `json:"products"` } // These structs define the model used to describe download mirrors. type MirrorRefs struct { Mirrors map[string][]MirrorReference `json:"mirrors"` } type MirrorReference struct { Updated string `json:"updated"` Format string `json:"format"` DataType string `json:"datatype"` Path string `json:"path"` Clouds []CloudSpec `json:"clouds"` } type MirrorMetadata struct { Updated string `json:"updated"` Format string `json:"format"` Mirrors map[string][]MirrorInfo `json:"mirrors"` } type MirrorInfo struct { Clouds []CloudSpec `json:"clouds"` MirrorURL string `json:"mirror"` Path string `json:"path"` } type MirrorInfoSlice []MirrorInfo type MirrorRefSlice []MirrorReference // filter returns those entries from an MirrorInfo array for which the given // match function returns true. It preserves order. func (entries MirrorInfoSlice) filter(match func(*MirrorInfo) bool) MirrorInfoSlice { result := MirrorInfoSlice{} for _, mirrorInfo := range entries { if match(&mirrorInfo) { result = append(result, mirrorInfo) } } return result } // filter returns those entries from an MirrorInfo array for which the given // match function returns true. It preserves order. func (entries MirrorRefSlice) filter(match func(*MirrorReference) bool) MirrorRefSlice { result := MirrorRefSlice{} for _, mirrorRef := range entries { if match(&mirrorRef) { result = append(result, mirrorRef) } } return result } // extractCatalogsForProducts gives you just those catalogs from a // cloudImageMetadata that are for the given product IDs. They are kept in // the order of the parameter. func (metadata *CloudMetadata) extractCatalogsForProducts(productIds []string) []MetadataCatalog { result := []MetadataCatalog{} for _, id := range productIds { if catalog, ok := metadata.Products[id]; ok { result = append(result, catalog) } } return result } // extractIndexes returns just the array of indexes, in arbitrary order. func (ind *Indices) extractIndexes() IndexMetadataSlice { result := make(IndexMetadataSlice, 0, len(ind.Indexes)) for _, metadata := range ind.Indexes { result = append(result, metadata) } return result } func (metadata *IndexMetadata) String() string { return fmt.Sprintf("%v", *metadata) } // hasCloud tells you whether an IndexMetadata has the given cloud in its // Clouds list. If IndexMetadata has no clouds defined, then hasCloud // returns true regardless so that the corresponding product records // are searched. func (metadata *IndexMetadata) hasCloud(cloud CloudSpec) bool { for _, metadataCloud := range metadata.Clouds { if metadataCloud.equals(&cloud) { return true } } return len(metadata.Clouds) == 0 } // hasProduct tells you whether an IndexMetadata provides any of the given // product IDs. func (metadata *IndexMetadata) hasProduct(prodIds []string) bool { for _, pid := range metadata.ProductIds { if containsString(prodIds, pid) { return true } } return false } type IndexMetadataSlice []*IndexMetadata // filter returns those entries from an IndexMetadata array for which the given // match function returns true. It preserves order. func (entries IndexMetadataSlice) filter(match func(*IndexMetadata) bool) IndexMetadataSlice { result := IndexMetadataSlice{} for _, metadata := range entries { if match(metadata) { result = append(result, metadata) } } return result } // noMatchingProductsError is used to indicate that metadata files have been located, // but there is no metadata satisfying a product criteria. // It is used to distinguish from the situation where the metadata files could not be found. type noMatchingProductsError struct { msg string } func (e *noMatchingProductsError) Error() string { return e.msg } func newNoMatchingProductsError(message string, args ...interface{}) error { return &noMatchingProductsError{fmt.Sprintf(message, args...)} } const ( StreamsDir = "streams/v1" UnsignedIndex = "streams/v1/index.json" DefaultIndexPath = "streams/v1/index" UnsignedMirror = "streams/v1/mirrors.json" mirrorsPath = "streams/v1/mirrors" signedSuffix = ".sjson" UnsignedSuffix = ".json" ) type appendMatchingFunc func(DataSource, []interface{}, map[string]interface{}, LookupConstraint) []interface{} // ValueParams contains the information required to pull out from the metadata structs of a particular type. type ValueParams struct { // The simplestreams data type key. DataType string // The key to use when looking for content mirrors. MirrorContentId string // A function used to filter and return records of a given type. FilterFunc appendMatchingFunc // An struct representing the type of records to return. ValueTemplate interface{} // For signed metadata, the public key used to validate the signature. PublicKey string } // GetMetadata returns metadata records matching the specified constraint,looking in each source for signed metadata. // If onlySigned is false and no signed metadata is found in a source, the source is used to look for unsigned metadata. // Each source is tried in turn until at least one signed (or unsigned) match is found. func GetMetadata( sources []DataSource, baseIndexPath string, cons LookupConstraint, onlySigned bool, params ValueParams) (items []interface{}, resolveInfo *ResolveInfo, err error) { for _, source := range sources { items, resolveInfo, err = getMaybeSignedMetadata(source, baseIndexPath, cons, true, params) // If no items are found using signed metadata, check unsigned. if err != nil && len(items) == 0 && !onlySigned { items, resolveInfo, err = getMaybeSignedMetadata(source, baseIndexPath, cons, false, params) } if err == nil { break } } if _, ok := err.(*noMatchingProductsError); ok { // no matching products is an internal error only err = nil } return items, resolveInfo, err } // getMaybeSignedMetadata returns metadata records matching the specified constraint. func getMaybeSignedMetadata(source DataSource, baseIndexPath string, cons LookupConstraint, signed bool, params ValueParams) ([]interface{}, *ResolveInfo, error) { resolveInfo := &ResolveInfo{} indexPath := baseIndexPath + UnsignedSuffix if signed { indexPath = baseIndexPath + signedSuffix } var items []interface{} indexURL, err := source.URL(indexPath) if err != nil { // Some providers return an error if asked for the URL of a non-existent file. // So the best we can do is use the relative path for the URL when logging messages. indexURL = indexPath } resolveInfo.Source = source.Description() resolveInfo.Signed = signed resolveInfo.IndexURL = indexURL indexRef, err := GetIndexWithFormat(source, indexPath, "index:1.0", signed, cons.Params().CloudSpec, params) if err != nil { if errors.IsNotFoundError(err) || errors.IsUnauthorizedError(err) { logger.Debugf("cannot load index %q: %v", indexURL, err) } return nil, resolveInfo, err } logger.Debugf("read metadata index at %q", indexURL) items, err = indexRef.getLatestMetadataWithFormat(cons, "products:1.0", signed) if err != nil { if errors.IsNotFoundError(err) { logger.Debugf("skipping index because of error getting latest metadata %q: %v", indexURL, err) return nil, resolveInfo, err } if _, ok := err.(*noMatchingProductsError); ok { logger.Debugf("%v", err) } } if indexRef.Source.Description() == "mirror" { resolveInfo.MirrorURL = indexRef.Source.(*urlDataSource).baseURL } return items, resolveInfo, err } // fetchData gets all the data from the given source located at the specified path. // It returns the data found and the full URL used. func fetchData(source DataSource, path string, requireSigned bool, publicKey string) (data []byte, dataURL string, err error) { rc, dataURL, err := source.Fetch(path) if err != nil { logger.Debugf("fetchData failed for %q: %v", dataURL, err) return nil, dataURL, errors.NotFoundf("invalid URL %q", dataURL) } defer rc.Close() if requireSigned { data, err = DecodeCheckSignature(rc, publicKey) } else { data, err = ioutil.ReadAll(rc) } if err != nil { return nil, dataURL, fmt.Errorf("cannot read URL data, %v", err) } return data, dataURL, nil } // GetIndexWithFormat returns a simplestreams index of the specified format. // Exported for testing. func GetIndexWithFormat(source DataSource, indexPath, indexFormat string, requireSigned bool, cloudSpec CloudSpec, params ValueParams) (*IndexReference, error) { data, url, err := fetchData(source, indexPath, requireSigned, params.PublicKey) if err != nil { if errors.IsNotFoundError(err) || errors.IsUnauthorizedError(err) { return nil, err } return nil, fmt.Errorf("cannot read index data, %v", err) } var indices Indices err = json.Unmarshal(data, &indices) if err != nil { logger.Errorf("bad JSON index data at URL %q: %v", url, string(data)) return nil, fmt.Errorf("cannot unmarshal JSON index metadata at URL %q: %v", url, err) } if indices.Format != indexFormat { return nil, fmt.Errorf( "unexpected index file format %q, expected %q at URL %q", indices.Format, indexFormat, url) } mirrors, url, err := getMirrorRefs(source, mirrorsPath, requireSigned, params) if err != nil && !errors.IsNotFoundError(err) && !errors.IsUnauthorizedError(err) { return nil, fmt.Errorf("cannot load mirror metadata at URL %q: %v", url, err) } indexRef := &IndexReference{ Source: source, Indices: indices, valueParams: params, } // Apply any mirror information to the source. if params.MirrorContentId != "" { mirrorInfo, err := getMirror( source, mirrors, params.DataType, params.MirrorContentId, cloudSpec, requireSigned, params.PublicKey) if err == nil { logger.Debugf("using mirrored products path: %s", path.Join(mirrorInfo.MirrorURL, mirrorInfo.Path)) indexRef.Source = NewURLDataSource("mirror", mirrorInfo.MirrorURL, utils.VerifySSLHostnames) indexRef.MirroredProductsPath = mirrorInfo.Path } else { logger.Debugf("no mirror information available for %s: %v", cloudSpec, err) } } return indexRef, nil } // getMirrorRefs parses and returns a simplestreams mirror reference. func getMirrorRefs(source DataSource, baseMirrorsPath string, requireSigned bool, params ValueParams) (MirrorRefs, string, error) { mirrorsPath := baseMirrorsPath + UnsignedSuffix if requireSigned { mirrorsPath = baseMirrorsPath + signedSuffix } var mirrors MirrorRefs data, url, err := fetchData(source, mirrorsPath, requireSigned, params.PublicKey) if err != nil { if errors.IsNotFoundError(err) || errors.IsUnauthorizedError(err) { logger.Debugf("no mirror index file found") return mirrors, url, err } return mirrors, url, fmt.Errorf("cannot read mirrors data, %v", err) } err = json.Unmarshal(data, &mirrors) if err != nil { return mirrors, url, fmt.Errorf("cannot unmarshal JSON mirror metadata at URL %q: %v", url, err) } return mirrors, url, err } // getMirror returns a mirror info struct matching the specified content and cloud. func getMirror(source DataSource, mirrors MirrorRefs, datatype, contentId string, cloudSpec CloudSpec, requireSigned bool, publicKey string) (*MirrorInfo, error) { mirrorRef, err := mirrors.getMirrorReference(datatype, contentId, cloudSpec) if err != nil { return nil, err } mirrorInfo, err := mirrorRef.getMirrorInfo(source, contentId, cloudSpec, "mirrors:1.0", requireSigned, publicKey) if err != nil { return nil, err } if mirrorInfo == nil { return nil, errors.NotFoundf("mirror metadata for %q and cloud %v", contentId, cloudSpec) } return mirrorInfo, nil } // GetProductsPath returns the path to the metadata file containing products for the specified constraint. // Exported for testing. func (indexRef *IndexReference) GetProductsPath(cons LookupConstraint) (string, error) { if indexRef.MirroredProductsPath != "" { return indexRef.MirroredProductsPath, nil } prodIds, err := cons.Ids() if err != nil { return "", err } candidates := indexRef.extractIndexes() // Restrict to image-ids entries. dataTypeMatches := func(metadata *IndexMetadata) bool { return metadata.DataType == indexRef.valueParams.DataType } candidates = candidates.filter(dataTypeMatches) if len(candidates) == 0 { return "", errors.NotFoundf("index file missing %q data", indexRef.valueParams.DataType) } // Restrict by cloud spec, if required. if cons.Params().CloudSpec != EmptyCloudSpec { hasRightCloud := func(metadata *IndexMetadata) bool { return metadata.hasCloud(cons.Params().CloudSpec) } candidates = candidates.filter(hasRightCloud) if len(candidates) == 0 { return "", errors.NotFoundf("index file has no data for cloud %v", cons.Params().CloudSpec) } } // Restrict by product IDs. hasProduct := func(metadata *IndexMetadata) bool { return metadata.hasProduct(prodIds) } candidates = candidates.filter(hasProduct) if len(candidates) == 0 { return "", newNoMatchingProductsError("index file has no data for product name(s) %q", prodIds) } logger.Debugf("candidate matches for products %q are %v", prodIds, candidates) // Pick arbitrary match. return candidates[0].ProductsFilePath, nil } // extractMirrorRefs returns just the array of MirrorRef structs for the contentId, in arbitrary order. func (mirrorRefs *MirrorRefs) extractMirrorRefs(contentId string) MirrorRefSlice { for id, refs := range mirrorRefs.Mirrors { if id == contentId { return refs } } return nil } // hasCloud tells you whether a MirrorReference has the given cloud in its // Clouds list. func (mirrorRef *MirrorReference) hasCloud(cloud CloudSpec) bool { for _, refCloud := range mirrorRef.Clouds { if refCloud.equals(&cloud) { return true } } return false } // GetMirrorReference returns the reference to the metadata file containing mirrors for the specified content and cloud. func (mirrorRefs *MirrorRefs) getMirrorReference(datatype, contentId string, cloud CloudSpec) (*MirrorReference, error) { candidates := mirrorRefs.extractMirrorRefs(contentId) if len(candidates) == 0 { return nil, errors.NotFoundf("mirror data for %q", contentId) } // Restrict by cloud spec and datatype. hasRightCloud := func(mirrorRef *MirrorReference) bool { return mirrorRef.hasCloud(cloud) && mirrorRef.DataType == datatype } matchingCandidates := candidates.filter(hasRightCloud) if len(matchingCandidates) == 0 { // No cloud specific mirrors found so look for a non cloud specific mirror. for _, candidate := range candidates { if len(candidate.Clouds) == 0 { logger.Debugf("using default candidate for content id %q are %v", contentId, candidate) return &candidate, nil } } return nil, errors.NotFoundf("index file with cloud %v", cloud) } logger.Debugf("candidate matches for content id %q are %v", contentId, candidates) // Pick arbitrary match. return &matchingCandidates[0], nil } // getMirrorInfo returns mirror information from the mirror file at the given path for the specified content and cloud. func (mirrorRef *MirrorReference) getMirrorInfo(source DataSource, contentId string, cloud CloudSpec, format string, requireSigned bool, publicKey string) (*MirrorInfo, error) { metadata, err := GetMirrorMetadataWithFormat(source, mirrorRef.Path, format, requireSigned, publicKey) if err != nil { return nil, err } mirrorInfo, err := metadata.getMirrorInfo(contentId, cloud) if err != nil { return nil, err } return mirrorInfo, nil } // GetMirrorMetadataWithFormat returns simplestreams mirror data of the specified format. // Exported for testing. func GetMirrorMetadataWithFormat(source DataSource, mirrorPath, format string, requireSigned bool, publicKey string) (*MirrorMetadata, error) { data, url, err := fetchData(source, mirrorPath, requireSigned, publicKey) if err != nil { if errors.IsNotFoundError(err) || errors.IsUnauthorizedError(err) { return nil, err } return nil, fmt.Errorf("cannot read mirror data, %v", err) } var mirrors MirrorMetadata err = json.Unmarshal(data, &mirrors) if err != nil { return nil, fmt.Errorf("cannot unmarshal JSON mirror metadata at URL %q: %v", url, err) } if mirrors.Format != format { return nil, fmt.Errorf("unexpected mirror file format %q, expected %q at URL %q", mirrors.Format, format, url) } return &mirrors, nil } // hasCloud tells you whether an MirrorInfo has the given cloud in its // Clouds list. func (mirrorInfo *MirrorInfo) hasCloud(cloud CloudSpec) bool { for _, metadataCloud := range mirrorInfo.Clouds { if metadataCloud.equals(&cloud) { return true } } return false } // getMirrorInfo returns the mirror metadata for the specified content and cloud. func (mirrorMetadata *MirrorMetadata) getMirrorInfo(contentId string, cloud CloudSpec) (*MirrorInfo, error) { var candidates MirrorInfoSlice for id, m := range mirrorMetadata.Mirrors { if id == contentId { candidates = m break } } if len(candidates) == 0 { return nil, errors.NotFoundf("mirror info for %q", contentId) } // Restrict by cloud spec. hasRightCloud := func(mirrorInfo *MirrorInfo) bool { return mirrorInfo.hasCloud(cloud) } candidates = candidates.filter(hasRightCloud) if len(candidates) == 0 { return nil, errors.NotFoundf("mirror info with cloud %v", cloud) } // Pick arbitrary match. return &candidates[0], nil } // utility function to see if element exists in values slice. func containsString(values []string, element string) bool { for _, value := range values { if value == element { return true } } return false } // To keep the metadata concise, attributes on the metadata struct which have the same value for each // item may be moved up to a higher level in the tree. denormaliseMetadata descends the tree // and fills in any missing attributes with values from a higher level. func (metadata *CloudMetadata) denormaliseMetadata() { for _, metadataCatalog := range metadata.Products { for _, ItemCollection := range metadataCatalog.Items { for _, item := range ItemCollection.Items { coll := *ItemCollection inherit(&coll, metadataCatalog) inherit(item, &coll) } } } } // inherit sets any blank fields in dst to their equivalent values in fields in src that have matching json tags. // The dst parameter must be a pointer to a struct. func inherit(dst, src interface{}) { for tag := range tags(dst) { setFieldByTag(dst, tag, fieldByTag(src, tag), false) } } // processAliases looks through the struct fields to see if // any aliases apply, and sets attributes appropriately if so. func (metadata *CloudMetadata) processAliases(item interface{}) { for tag := range tags(item) { aliases, ok := metadata.Aliases[tag] if !ok { continue } // We have found a set of aliases for one of the fields in the metadata struct. // Now check to see if the field matches one of the defined aliases. fields, ok := aliases[fieldByTag(item, tag)] if !ok { continue } // The alias matches - set all the aliased fields in the struct. for attr, val := range fields { setFieldByTag(item, attr, val, true) } } } // Apply any attribute aliases to the metadata records. func (metadata *CloudMetadata) applyAliases() { for _, metadataCatalog := range metadata.Products { for _, ItemCollection := range metadataCatalog.Items { for _, item := range ItemCollection.Items { metadata.processAliases(item) } } } } // construct iterates over the metadata records and replaces the generic maps of values // with structs of the required type. func (metadata *CloudMetadata) construct(valueType reflect.Type) error { for _, metadataCatalog := range metadata.Products { for _, ItemCollection := range metadataCatalog.Items { if err := ItemCollection.construct(valueType); err != nil { return err } } } return nil } type structTags map[reflect.Type]map[string]int var tagsForType structTags = make(structTags) // RegisterStructTags ensures the json tags for the given structs are able to be used // when parsing the simplestreams metadata. func RegisterStructTags(vals ...interface{}) { tags := mkTags(vals...) for k, v := range tags { tagsForType[k] = v } } func init() { RegisterStructTags(MetadataCatalog{}, ItemCollection{}) } func mkTags(vals ...interface{}) map[reflect.Type]map[string]int { typeMap := make(map[reflect.Type]map[string]int) for _, v := range vals { t := reflect.TypeOf(v) typeMap[t] = jsonTags(t) } return typeMap } // jsonTags returns a map from json tag to the field index for the string fields in the given type. func jsonTags(t reflect.Type) map[string]int { if t.Kind() != reflect.Struct { panic(fmt.Errorf("cannot get json tags on type %s", t)) } tags := make(map[string]int) for i := 0; i < t.NumField(); i++ { f := t.Field(i) if f.Type != reflect.TypeOf("") { continue } if tag := f.Tag.Get("json"); tag != "" { if i := strings.Index(tag, ","); i >= 0 { tag = tag[0:i] } if tag == "-" { continue } if tag != "" { f.Name = tag } } tags[f.Name] = i } return tags } // tags returns the field offsets for the JSON tags defined by the given value, which must be // a struct or a pointer to a struct. func tags(x interface{}) map[string]int { t := reflect.TypeOf(x) if t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() != reflect.Struct { panic(fmt.Errorf("expected struct, not %s", t)) } if tagm := tagsForType[t]; tagm != nil { return tagm } panic(fmt.Errorf("%s not found in type table", t)) } // fieldByTag returns the value for the field in x with the given JSON tag, or "" if there is no such field. func fieldByTag(x interface{}, tag string) string { tagm := tags(x) v := reflect.ValueOf(x) if v.Kind() == reflect.Ptr { v = v.Elem() } if i, ok := tagm[tag]; ok { return v.Field(i).Interface().(string) } return "" } // setFieldByTag sets the value for the field in x with the given JSON tag to val. // The override parameter specifies whether the value will be set even if the original value is non-empty. func setFieldByTag(x interface{}, tag, val string, override bool) { i, ok := tags(x)[tag] if !ok { return } v := reflect.ValueOf(x).Elem() f := v.Field(i) if override || f.Interface().(string) == "" { f.Set(reflect.ValueOf(val)) } } // GetCloudMetadataWithFormat loads the entire cloud metadata encoded using the specified format. // Exported for testing. func (indexRef *IndexReference) GetCloudMetadataWithFormat(cons LookupConstraint, format string, requireSigned bool) (*CloudMetadata, error) { productFilesPath, err := indexRef.GetProductsPath(cons) if err != nil { return nil, err } logger.Debugf("finding products at path %q", productFilesPath) data, url, err := fetchData(indexRef.Source, productFilesPath, requireSigned, indexRef.valueParams.PublicKey) if err != nil { return nil, fmt.Errorf("cannot read product data, %v", err) } return ParseCloudMetadata(data, format, url, indexRef.valueParams.ValueTemplate) } // ParseCloudMetadata parses the given bytes into simplestreams metadata. func ParseCloudMetadata(data []byte, format, url string, valueTemplate interface{}) (*CloudMetadata, error) { var metadata CloudMetadata err := json.Unmarshal(data, &metadata) if err != nil { return nil, fmt.Errorf("cannot unmarshal JSON metadata at URL %q: %v", url, err) } if metadata.Format != format { return nil, fmt.Errorf("unexpected index file format %q, expected %q at URL %q", metadata.Format, format, url) } if valueTemplate != nil { err = metadata.construct(reflect.TypeOf(valueTemplate)) } if err != nil { logger.Errorf("bad JSON product data at URL %q: %v", url, string(data)) return nil, fmt.Errorf("cannot unmarshal JSON metadata at URL %q: %v", url, err) } metadata.applyAliases() metadata.denormaliseMetadata() return &metadata, nil } // getLatestMetadataWithFormat loads the metadata for the given cloud and orders the resulting structs // starting with the most recent, and returns items which match the product criteria, choosing from the // latest versions first. func (indexRef *IndexReference) getLatestMetadataWithFormat(cons LookupConstraint, format string, requireSigned bool) ([]interface{}, error) { metadata, err := indexRef.GetCloudMetadataWithFormat(cons, format, requireSigned) if err != nil { return nil, err } logger.Debugf("metadata: %v", metadata) matches, err := GetLatestMetadata(metadata, cons, indexRef.Source, indexRef.valueParams.FilterFunc) if err != nil { return nil, err } if len(matches) == 0 { return nil, newNoMatchingProductsError("index has no matching records") } return matches, nil } // GetLatestMetadata extracts and returns the metadata records matching the given criteria. func GetLatestMetadata(metadata *CloudMetadata, cons LookupConstraint, source DataSource, filterFunc appendMatchingFunc) ([]interface{}, error) { prodIds, err := cons.Ids() if err != nil { return nil, err } catalogs := metadata.extractCatalogsForProducts(prodIds) if len(catalogs) == 0 { availableProducts := make([]string, 0, len(metadata.Products)) for product := range metadata.Products { availableProducts = append(availableProducts, product) } return nil, newNoMatchingProductsError( "index has no records for product ids %v; it does have product ids %v", prodIds, availableProducts) } var matchingItems []interface{} for _, catalog := range catalogs { var bv byVersionDesc = make(byVersionDesc, len(catalog.Items)) i := 0 for vers, itemColl := range catalog.Items { bv[i] = collectionVersion{vers, itemColl} i++ } sort.Sort(bv) for _, itemCollVersion := range bv { matchingItems = filterFunc(source, matchingItems, itemCollVersion.ItemCollection.Items, cons) } } return matchingItems, nil } type collectionVersion struct { version string ItemCollection *ItemCollection } // byVersionDesc is used to sort a slice of collections in descending order of their // version in YYYYMMDD. type byVersionDesc []collectionVersion func (bv byVersionDesc) Len() int { return len(bv) } func (bv byVersionDesc) Swap(i, j int) { bv[i], bv[j] = bv[j], bv[i] } func (bv byVersionDesc) Less(i, j int) bool { return bv[i].version > bv[j].version } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/simplestreams_test.go�����������0000644�0000153�0000161�00000044642�12321735642�031710� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package simplestreams_test import ( "bytes" "sort" "strings" "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/simplestreams" sstesting "launchpad.net/juju-core/environs/simplestreams/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) func Test(t *testing.T) { registerSimpleStreamsTests() gc.Suite(&signingSuite{}) gc.Suite(&jsonSuite{}) gc.TestingT(t) } func registerSimpleStreamsTests() { gc.Suite(&simplestreamsSuite{ LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{ Source: simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames), RequireSigned: false, DataType: "image-ids", ValidConstraint: sstesting.NewTestConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ Region: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", }, Series: []string{"precise"}, Arches: []string{"amd64", "arm"}, }), }, }) } type simplestreamsSuite struct { sstesting.TestDataSuite sstesting.LocalLiveSimplestreamsSuite } func (s *simplestreamsSuite) SetUpSuite(c *gc.C) { s.LocalLiveSimplestreamsSuite.SetUpSuite(c) s.TestDataSuite.SetUpSuite(c) } func (s *simplestreamsSuite) TearDownSuite(c *gc.C) { s.TestDataSuite.TearDownSuite(c) s.LocalLiveSimplestreamsSuite.TearDownSuite(c) } func (s *simplestreamsSuite) TestGetProductsPath(c *gc.C) { indexRef, err := s.GetIndexRef(sstesting.Index_v1) c.Assert(err, gc.IsNil) path, err := indexRef.GetProductsPath(s.ValidConstraint) c.Assert(err, gc.IsNil) c.Assert(path, gc.Equals, "streams/v1/image_metadata.json") } func (*simplestreamsSuite) TestExtractCatalogsForProductsAcceptsNil(c *gc.C) { empty := simplestreams.CloudMetadata{} c.Check(simplestreams.ExtractCatalogsForProducts(empty, nil), gc.HasLen, 0) } func (*simplestreamsSuite) TestExtractCatalogsForProductsReturnsMatch(c *gc.C) { metadata := simplestreams.CloudMetadata{ Products: map[string]simplestreams.MetadataCatalog{ "foo": {}, }, } c.Check( simplestreams.ExtractCatalogsForProducts(metadata, []string{"foo"}), gc.DeepEquals, []simplestreams.MetadataCatalog{metadata.Products["foo"]}) } func (*simplestreamsSuite) TestExtractCatalogsForProductsIgnoresNonMatches(c *gc.C) { metadata := simplestreams.CloudMetadata{ Products: map[string]simplestreams.MetadataCatalog{ "one-product": {}, }, } absentProducts := []string{"another-product"} c.Check(simplestreams.ExtractCatalogsForProducts(metadata, absentProducts), gc.HasLen, 0) } func (*simplestreamsSuite) TestExtractCatalogsForProductsPreservesOrder(c *gc.C) { products := map[string]simplestreams.MetadataCatalog{ "1": {}, "2": {}, "3": {}, "4": {}, } metadata := simplestreams.CloudMetadata{Products: products} c.Check( simplestreams.ExtractCatalogsForProducts(metadata, []string{"1", "3", "4", "2"}), gc.DeepEquals, []simplestreams.MetadataCatalog{ products["1"], products["3"], products["4"], products["2"], }) } func (*simplestreamsSuite) TestExtractIndexesAcceptsNil(c *gc.C) { ind := simplestreams.Indices{} c.Check(simplestreams.ExtractIndexes(ind), gc.HasLen, 0) } func (*simplestreamsSuite) TestExtractIndexesReturnsIndex(c *gc.C) { metadata := simplestreams.IndexMetadata{} ind := simplestreams.Indices{Indexes: map[string]*simplestreams.IndexMetadata{"foo": &metadata}} c.Check(simplestreams.ExtractIndexes(ind), gc.DeepEquals, simplestreams.IndexMetadataSlice{&metadata}) } func (*simplestreamsSuite) TestExtractIndexesReturnsAllIndexes(c *gc.C) { ind := simplestreams.Indices{ Indexes: map[string]*simplestreams.IndexMetadata{ "foo": {}, "bar": {}, }, } array := simplestreams.ExtractIndexes(ind) c.Assert(array, gc.HasLen, len(ind.Indexes)) c.Check(array[0], gc.NotNil) c.Check(array[1], gc.NotNil) c.Check(array[0], gc.Not(gc.Equals), array[1]) c.Check( (array[0] == ind.Indexes["foo"]), gc.Not(gc.Equals), (array[1] == ind.Indexes["foo"])) c.Check( (array[0] == ind.Indexes["bar"]), gc.Not(gc.Equals), (array[1] == ind.Indexes["bar"])) } func (*simplestreamsSuite) TestHasCloudAcceptsNil(c *gc.C) { metadata := simplestreams.IndexMetadata{Clouds: nil} c.Check(simplestreams.HasCloud(metadata, simplestreams.CloudSpec{}), jc.IsTrue) } func (*simplestreamsSuite) TestHasCloudFindsMatch(c *gc.C) { metadata := simplestreams.IndexMetadata{ Clouds: []simplestreams.CloudSpec{ {Region: "r1", Endpoint: "http://e1"}, {Region: "r2", Endpoint: "http://e2"}, }, } c.Check(simplestreams.HasCloud(metadata, metadata.Clouds[1]), jc.IsTrue) } func (*simplestreamsSuite) TestHasCloudFindsMatchWithTrailingSlash(c *gc.C) { metadata := simplestreams.IndexMetadata{ Clouds: []simplestreams.CloudSpec{ {Region: "r1", Endpoint: "http://e1/"}, {Region: "r2", Endpoint: "http://e2"}, }, } spec := simplestreams.CloudSpec{Region: "r1", Endpoint: "http://e1"} c.Check(simplestreams.HasCloud(metadata, spec), jc.IsTrue) spec = simplestreams.CloudSpec{Region: "r1", Endpoint: "http://e1/"} c.Check(simplestreams.HasCloud(metadata, spec), jc.IsTrue) spec = simplestreams.CloudSpec{Region: "r2", Endpoint: "http://e2/"} c.Check(simplestreams.HasCloud(metadata, spec), jc.IsTrue) } func (*simplestreamsSuite) TestHasCloudReturnsFalseIfCloudsDoNotMatch(c *gc.C) { metadata := simplestreams.IndexMetadata{ Clouds: []simplestreams.CloudSpec{ {Region: "r1", Endpoint: "http://e1"}, {Region: "r2", Endpoint: "http://e2"}, }, } otherCloud := simplestreams.CloudSpec{Region: "r9", Endpoint: "http://e9"} c.Check(simplestreams.HasCloud(metadata, otherCloud), jc.IsFalse) } func (*simplestreamsSuite) TestHasCloudRequiresIdenticalRegion(c *gc.C) { metadata := simplestreams.IndexMetadata{ Clouds: []simplestreams.CloudSpec{ {Region: "around", Endpoint: "http://nearby"}, }, } similarCloud := metadata.Clouds[0] similarCloud.Region = "elsewhere" c.Assert(similarCloud, gc.Not(gc.Equals), metadata.Clouds[0]) c.Check(simplestreams.HasCloud(metadata, similarCloud), jc.IsFalse) } func (*simplestreamsSuite) TestHasCloudRequiresIdenticalEndpoint(c *gc.C) { metadata := simplestreams.IndexMetadata{ Clouds: []simplestreams.CloudSpec{ {Region: "around", Endpoint: "http://nearby"}, }, } similarCloud := metadata.Clouds[0] similarCloud.Endpoint = "http://far" c.Assert(similarCloud, gc.Not(gc.Equals), metadata.Clouds[0]) c.Check(simplestreams.HasCloud(metadata, similarCloud), jc.IsFalse) } func (*simplestreamsSuite) TestHasProductAcceptsNils(c *gc.C) { metadata := simplestreams.IndexMetadata{} c.Check(simplestreams.HasProduct(metadata, nil), jc.IsFalse) } func (*simplestreamsSuite) TestHasProductFindsMatchingProduct(c *gc.C) { metadata := simplestreams.IndexMetadata{ProductIds: []string{"x", "y", "z"}} c.Check( simplestreams.HasProduct(metadata, []string{"a", "b", metadata.ProductIds[1]}), gc.Equals, true) } func (*simplestreamsSuite) TestHasProductReturnsFalseIfProductsDoNotMatch(c *gc.C) { metadata := simplestreams.IndexMetadata{ProductIds: []string{"x", "y", "z"}} c.Check(simplestreams.HasProduct(metadata, []string{"a", "b", "c"}), jc.IsFalse) } func (*simplestreamsSuite) TestFilterReturnsNothingForEmptyArray(c *gc.C) { empty := simplestreams.IndexMetadataSlice{} c.Check( simplestreams.Filter(empty, func(*simplestreams.IndexMetadata) bool { return true }), gc.HasLen, 0) } func (*simplestreamsSuite) TestFilterRemovesNonMatches(c *gc.C) { array := simplestreams.IndexMetadataSlice{&simplestreams.IndexMetadata{}} c.Check( simplestreams.Filter(array, func(*simplestreams.IndexMetadata) bool { return false }), gc.HasLen, 0) } func (*simplestreamsSuite) TestFilterIncludesMatches(c *gc.C) { metadata := simplestreams.IndexMetadata{} array := simplestreams.IndexMetadataSlice{&metadata} c.Check( simplestreams.Filter(array, func(*simplestreams.IndexMetadata) bool { return true }), gc.DeepEquals, simplestreams.IndexMetadataSlice{&metadata}) } func (*simplestreamsSuite) TestFilterLeavesOriginalUnchanged(c *gc.C) { item1 := simplestreams.IndexMetadata{CloudName: "aws"} item2 := simplestreams.IndexMetadata{CloudName: "openstack"} array := simplestreams.IndexMetadataSlice{&item1, &item2} result := simplestreams.Filter(array, func(metadata *simplestreams.IndexMetadata) bool { return metadata.CloudName == "aws" }) // This exercises both the "leave out" and the "include" code paths. c.Assert(result, gc.HasLen, 1) // The original, however, has not changed. c.Assert(array, gc.HasLen, 2) c.Check(array, gc.DeepEquals, simplestreams.IndexMetadataSlice{&item1, &item2}) } func (*simplestreamsSuite) TestFilterPreservesOrder(c *gc.C) { array := simplestreams.IndexMetadataSlice{ &simplestreams.IndexMetadata{CloudName: "aws"}, &simplestreams.IndexMetadata{CloudName: "maas"}, &simplestreams.IndexMetadata{CloudName: "openstack"}, } c.Check( simplestreams.Filter(array, func(metadata *simplestreams.IndexMetadata) bool { return true }), gc.DeepEquals, array) } func (*simplestreamsSuite) TestFilterCombinesMatchesAndNonMatches(c *gc.C) { array := simplestreams.IndexMetadataSlice{ &simplestreams.IndexMetadata{Format: "1.0"}, &simplestreams.IndexMetadata{Format: "1.1"}, &simplestreams.IndexMetadata{Format: "2.0"}, &simplestreams.IndexMetadata{Format: "2.1"}, } dotOFormats := simplestreams.Filter(array, func(metadata *simplestreams.IndexMetadata) bool { return strings.HasSuffix(metadata.Format, ".0") }) c.Check(dotOFormats, gc.DeepEquals, simplestreams.IndexMetadataSlice{array[0], array[2]}) } // countingSource is used to check that a DataSource has been queried. type countingSource struct { simplestreams.DataSource count int } func (s *countingSource) URL(path string) (string, error) { s.count++ return s.DataSource.URL(path) } func (s *simplestreamsSuite) TestGetMetadataNoMatching(c *gc.C) { source := &countingSource{ DataSource: simplestreams.NewURLDataSource( "test", "test:/daily", utils.VerifySSLHostnames, ), } sources := []simplestreams.DataSource{source, source, source} params := simplestreams.ValueParams{DataType: "image-ids"} constraint := sstesting.NewTestConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ Region: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", }, Series: []string{"precise"}, Arches: []string{"not-a-real-arch"}, // never matches }) items, resolveInfo, err := simplestreams.GetMetadata( sources, simplestreams.DefaultIndexPath, constraint, false, params, ) c.Assert(err, gc.IsNil) c.Assert(items, gc.HasLen, 0) c.Assert(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: false, IndexURL: "test:/daily/streams/v1/index.json", MirrorURL: "", }) // There should be 2 calls to each data-source: // one for .sjson, one for .json. c.Assert(source.count, gc.Equals, 2*len(sources)) } func (s *simplestreamsSuite) TestMetadataCatalog(c *gc.C) { metadata := s.AssertGetMetadata(c) c.Check(len(metadata.Products), gc.Equals, 2) c.Check(len(metadata.Aliases), gc.Equals, 1) metadataCatalog := metadata.Products["com.ubuntu.cloud:server:12.04:amd64"] c.Check(len(metadataCatalog.Items), gc.Equals, 2) c.Check(metadataCatalog.Series, gc.Equals, "precise") c.Check(metadataCatalog.Version, gc.Equals, "12.04") c.Check(metadataCatalog.Arch, gc.Equals, "amd64") c.Check(metadataCatalog.RegionName, gc.Equals, "au-east-1") c.Check(metadataCatalog.Endpoint, gc.Equals, "https://somewhere") } func (s *simplestreamsSuite) TestItemCollection(c *gc.C) { ic := s.AssertGetItemCollections(c, "20121218") c.Check(ic.RegionName, gc.Equals, "au-east-2") c.Check(ic.Endpoint, gc.Equals, "https://somewhere-else") c.Assert(len(ic.Items) > 0, jc.IsTrue) ti := ic.Items["usww2he"].(*sstesting.TestItem) c.Check(ti.Id, gc.Equals, "ami-442ea674") c.Check(ti.Storage, gc.Equals, "ebs") c.Check(ti.VirtType, gc.Equals, "hvm") c.Check(ti.RegionName, gc.Equals, "us-east-1") c.Check(ti.Endpoint, gc.Equals, "https://ec2.us-east-1.amazonaws.com") } func (s *simplestreamsSuite) TestDenormalisationFromCollection(c *gc.C) { ic := s.AssertGetItemCollections(c, "20121218") ti := ic.Items["usww1pe"].(*sstesting.TestItem) c.Check(ti.RegionName, gc.Equals, ic.RegionName) c.Check(ti.Endpoint, gc.Equals, ic.Endpoint) } func (s *simplestreamsSuite) TestDenormalisationFromCatalog(c *gc.C) { metadata := s.AssertGetMetadata(c) metadataCatalog := metadata.Products["com.ubuntu.cloud:server:12.04:amd64"] ic := metadataCatalog.Items["20111111"] ti := ic.Items["usww3pe"].(*sstesting.TestItem) c.Check(ti.RegionName, gc.Equals, metadataCatalog.RegionName) c.Check(ti.Endpoint, gc.Equals, metadataCatalog.Endpoint) } func (s *simplestreamsSuite) TestDealiasing(c *gc.C) { metadata := s.AssertGetMetadata(c) metadataCatalog := metadata.Products["com.ubuntu.cloud:server:12.04:amd64"] ic := metadataCatalog.Items["20121218"] ti := ic.Items["usww3he"].(*sstesting.TestItem) c.Check(ti.RegionName, gc.Equals, "us-west-3") c.Check(ti.Endpoint, gc.Equals, "https://ec2.us-west-3.amazonaws.com") } func (s *simplestreamsSuite) TestSeriesVersion(c *gc.C) { cleanup := simplestreams.SetSeriesVersions(make(map[string]string)) defer cleanup() vers, err := simplestreams.SeriesVersion("precise") if err != nil && err.Error() == `invalid series "precise"` { c.Fatalf(`Unable to lookup series "precise", you may need to: apt-get install distro-info`) } c.Assert(err, gc.IsNil) c.Assert(vers, gc.Equals, "12.04") } func (s *simplestreamsSuite) TestSupportedSeries(c *gc.C) { cleanup := simplestreams.SetSeriesVersions(make(map[string]string)) defer cleanup() series := simplestreams.SupportedSeries() sort.Strings(series) c.Assert(series, gc.DeepEquals, coretesting.SupportedSeries) } var getMirrorTests = []struct { region string endpoint string err string mirrorURL string path string }{{ // defaults mirrorURL: "http://some-mirror/", path: "com.ubuntu.juju:download.json", }, { // default mirror index entry region: "some-region", endpoint: "https://some-endpoint.com", mirrorURL: "http://big-mirror/", path: "big:download.json", }, { // endpoint with trailing "/" region: "some-region", endpoint: "https://some-endpoint.com/", mirrorURL: "http://big-mirror/", path: "big:download.json", }} func (s *simplestreamsSuite) TestGetMirrorMetadata(c *gc.C) { for i, t := range getMirrorTests { c.Logf("test %d", i) if t.region == "" { t.region = "us-east-2" } if t.endpoint == "" { t.endpoint = "https://ec2.us-east-2.amazonaws.com" } cloud := simplestreams.CloudSpec{t.region, t.endpoint} params := simplestreams.ValueParams{ DataType: "content-download", MirrorContentId: "com.ubuntu.juju:released:tools", } indexRef, err := simplestreams.GetIndexWithFormat( s.Source, s.IndexPath(), sstesting.Index_v1, s.RequireSigned, cloud, params) if !c.Check(err, gc.IsNil) { continue } if t.err != "" { c.Check(err, gc.ErrorMatches, t.err) continue } if !c.Check(err, gc.IsNil) { continue } mirrorURL, err := indexRef.Source.URL("") if !c.Check(err, gc.IsNil) { continue } c.Check(mirrorURL, gc.Equals, t.mirrorURL) c.Check(indexRef.MirroredProductsPath, gc.Equals, t.path) } } var testSigningKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux) lQHYBE2rFNoBBADFwqWQIW/DSqcB4yCQqnAFTJ27qS5AnB46ccAdw3u4Greeu3Bp idpoHdjULy7zSKlwR1EA873dO/k/e11Ml3dlAFUinWeejWaK2ugFP6JjiieSsrKn vWNicdCS4HTWn0X4sjl0ZiAygw6GNhqEQ3cpLeL0g8E9hnYzJKQ0LWJa0QARAQAB AAP/TB81EIo2VYNmTq0pK1ZXwUpxCrvAAIG3hwKjEzHcbQznsjNvPUihZ+NZQ6+X 0HCfPAdPkGDCLCb6NavcSW+iNnLTrdDnSI6+3BbIONqWWdRDYJhqZCkqmG6zqSfL IdkJgCw94taUg5BWP/AAeQrhzjChvpMQTVKQL5mnuZbUCeMCAN5qrYMP2S9iKdnk VANIFj7656ARKt/nf4CBzxcpHTyB8+d2CtPDKCmlJP6vL8t58Jmih+kHJMvC0dzn gr5f5+sCAOOe5gt9e0am7AvQWhdbHVfJU0TQJx+m2OiCJAqGTB1nvtBLHdJnfdC9 TnXXQ6ZXibqLyBies/xeY2sCKL5qtTMCAKnX9+9d/5yQxRyrQUHt1NYhaXZnJbHx q4ytu0eWz+5i68IYUSK69jJ1NWPM0T6SkqpB3KCAIv68VFm9PxqG1KmhSrQIVGVz dCBLZXmIuAQTAQIAIgUCTasU2gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA CgkQO9o98PRieSoLhgQAkLEZex02Qt7vGhZzMwuN0R22w3VwyYyjBx+fM3JFETy1 ut4xcLJoJfIaF5ZS38UplgakHG0FQ+b49i8dMij0aZmDqGxrew1m4kBfjXw9B/v+ eIqpODryb6cOSwyQFH0lQkXC040pjq9YqDsO5w0WYNXYKDnzRV0p4H1pweo2VDid AdgETasU2gEEAN46UPeWRqKHvA99arOxee38fBt2CI08iiWyI8T3J6ivtFGixSqV bRcPxYO/qLpVe5l84Nb3X71GfVXlc9hyv7CD6tcowL59hg1E/DC5ydI8K8iEpUmK /UnHdIY5h8/kqgGxkY/T/hgp5fRQgW1ZoZxLajVlMRZ8W4tFtT0DeA+JABEBAAEA A/0bE1jaaZKj6ndqcw86jd+QtD1SF+Cf21CWRNeLKnUds4FRRvclzTyUMuWPkUeX TaNNsUOFqBsf6QQ2oHUBBK4VCHffHCW4ZEX2cd6umz7mpHW6XzN4DECEzOVksXtc lUC1j4UB91DC/RNQqwX1IV2QLSwssVotPMPqhOi0ZLNY7wIA3n7DWKInxYZZ4K+6 rQ+POsz6brEoRHwr8x6XlHenq1Oki855pSa1yXIARoTrSJkBtn5oI+f8AzrnN0BN oyeQAwIA/7E++3HDi5aweWrViiul9cd3rcsS0dEnksPhvS0ozCJiHsq/6GFmy7J8 QSHZPteedBnZyNp5jR+H7cIfVN3KgwH/Skq4PsuPhDq5TKK6i8Pc1WW8MA6DXTdU nLkX7RGmMwjC0DBf7KWAlPjFaONAX3a8ndnz//fy1q7u2l9AZwrj1qa1iJ8EGAEC AAkFAk2rFNoCGwwACgkQO9o98PRieSo2/QP/WTzr4ioINVsvN1akKuekmEMI3LAp BfHwatufxxP1U+3Si/6YIk7kuPB9Hs+pRqCXzbvPRrI8NHZBmc8qIGthishdCYad AHcVnXjtxrULkQFGbGvhKURLvS9WnzD/m1K2zzwxzkPTzT9/Yf06O6Mal5AdugPL VrM0m72/jnpKo04= =zNCn -----END PGP PRIVATE KEY BLOCK----- ` var validClearsignInput = ` -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hello world line 2 ` var invalidClearsignInput = ` -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Invalid ` var testSig = `-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) iJwEAQECAAYFAk8kMuEACgkQO9o98PRieSpMsAQAhmY/vwmNpflrPgmfWsYhk5O8 pjnBUzZwqTDoDeINjZEoPDSpQAHGhjFjgaDx/Gj4fAl0dM4D0wuUEBb6QOrwflog 2A2k9kfSOMOtk0IH/H5VuFN1Mie9L/erYXjTQIptv9t9J7NoRBMU0QOOaFU0JaO9 MyTpno24AjIAGb+mH1U= =hIJ6 -----END PGP SIGNATURE----- ` type signingSuite struct{} func (s *signingSuite) TestDecodeCheckValidSignature(c *gc.C) { r := bytes.NewReader([]byte(validClearsignInput + testSig)) txt, err := simplestreams.DecodeCheckSignature(r, testSigningKey) c.Assert(err, gc.IsNil) c.Assert(txt, gc.DeepEquals, []byte("Hello world\nline 2\n")) } func (s *signingSuite) TestDecodeCheckInvalidSignature(c *gc.C) { r := bytes.NewReader([]byte(invalidClearsignInput + testSig)) _, err := simplestreams.DecodeCheckSignature(r, testSigningKey) c.Assert(err, gc.Not(gc.IsNil)) _, ok := err.(*simplestreams.NotPGPSignedError) c.Assert(ok, jc.IsFalse) } func (s *signingSuite) TestDecodeCheckMissingSignature(c *gc.C) { r := bytes.NewReader([]byte("foo")) _, err := simplestreams.DecodeCheckSignature(r, testSigningKey) _, ok := err.(*simplestreams.NotPGPSignedError) c.Assert(ok, jc.IsTrue) } ����������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/datasource.go�������������������0000644�0000153�0000161�00000006564�12321735642�030114� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package simplestreams import ( "fmt" "io" "net/http" "strings" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" ) // A DataSource retrieves simplestreams metadata. type DataSource interface { // Description describes the origin of this datasource. // eg tools-metadata-url, cloud storage, keystone catalog etc. Description() string // Fetch loads the data at the specified relative path. It returns a reader from which // the data can be retrieved as well as the full URL of the file. The full URL is typically // used in log messages to help diagnose issues accessing the data. Fetch(path string) (io.ReadCloser, string, error) // URL returns the full URL of the path, as applicable to this datasource. // This method is used primarily for logging purposes. URL(path string) (string, error) // SetAllowRetry sets the flag which determines if the datasource will retry fetching the metadata // if it is not immediately available. SetAllowRetry(allow bool) } // A urlDataSource retrieves data from an HTTP URL. type urlDataSource struct { description string baseURL string hostnameVerification utils.SSLHostnameVerification } // NewURLDataSource returns a new datasource reading from the specified baseURL. func NewURLDataSource(description, baseURL string, hostnameVerification utils.SSLHostnameVerification) DataSource { return &urlDataSource{ description: description, baseURL: baseURL, hostnameVerification: hostnameVerification, } } // Description is defined in simplestreams.DataSource. func (u *urlDataSource) Description() string { return u.description } func (u *urlDataSource) GoString() string { return fmt.Sprintf("%v: urlDataSource(%q)", u.description, u.baseURL) } // urlJoin returns baseURL + relpath making sure to have a '/' inbetween them // This doesn't try to do anything fancy with URL query or parameter bits // It also doesn't use path.Join because that normalizes slashes, and you need // to keep both slashes in 'http://'. func urlJoin(baseURL, relpath string) string { if strings.HasSuffix(baseURL, "/") { return baseURL + relpath } return baseURL + "/" + relpath } // Fetch is defined in simplestreams.DataSource. func (h *urlDataSource) Fetch(path string) (io.ReadCloser, string, error) { dataURL := urlJoin(h.baseURL, path) client := utils.GetHTTPClient(h.hostnameVerification) // dataURL can be http:// or file:// resp, err := client.Get(dataURL) if err != nil { logger.Debugf("Got error requesting %q: %v", dataURL, err) return nil, dataURL, errors.NotFoundf("invalid URL %q", dataURL) } if resp.StatusCode == http.StatusNotFound { return nil, dataURL, errors.NotFoundf("cannot find URL %q", dataURL) } if resp.StatusCode == http.StatusUnauthorized { return nil, dataURL, errors.Unauthorizedf("unauthorised access to URL %q", dataURL) } if resp.StatusCode != http.StatusOK { return nil, dataURL, fmt.Errorf("cannot access URL %q, %q", dataURL, resp.Status) } return resp.Body, dataURL, nil } // URL is defined in simplestreams.DataSource. func (h *urlDataSource) URL(path string) (string, error) { return urlJoin(h.baseURL, path), nil } // SetAllowRetry is defined in simplestreams.DataSource. func (h *urlDataSource) SetAllowRetry(allow bool) { // This is a NOOP for url datasources. } ��������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/encode.go�����������������������0000644�0000153�0000161�00000002167�12321735642�027212� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package simplestreams import ( "bytes" "io" "io/ioutil" "code.google.com/p/go.crypto/openpgp" "code.google.com/p/go.crypto/openpgp/clearsign" ) // Encode signs the data returned by the reader and returns an inline signed copy. func Encode(r io.Reader, armoredPrivateKey, passphrase string) ([]byte, error) { keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(armoredPrivateKey)) if err != nil { return nil, err } privateKey := keyring[0].PrivateKey if privateKey.Encrypted { err = privateKey.Decrypt([]byte(passphrase)) if err != nil { return nil, err } } var buf bytes.Buffer plaintext, err := clearsign.Encode(&buf, privateKey, nil) if err != nil { return nil, err } metadata, err := ioutil.ReadAll(r) if err != nil { return nil, err } dataToSign := metadata if dataToSign[0] == '\n' { dataToSign = dataToSign[1:] } _, err = plaintext.Write([]byte(dataToSign)) if err != nil { return nil, err } err = plaintext.Close() if err != nil { return nil, err } return buf.Bytes(), nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/json_test.go��������������������0000644�0000153�0000161�00000001513�12321735642�027757� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package simplestreams_test import ( "encoding/json" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/simplestreams" ) type jsonSuite struct{} func (s *jsonSuite) TestItemCollectionMarshalling(c *gc.C) { // Ensure that unmarshalling a simplestreams.ItemCollection // directly (not through ParseCloudMetadata) doesn't // cause any surprises. var m simplestreams.ItemCollection m.Items = make(map[string]interface{}) err := json.Unmarshal([]byte(`{ "items": { "a": "b", "c": 123 } }`), &m) c.Assert(err, gc.IsNil) c.Assert(m.Items, gc.DeepEquals, map[string]interface{}{ "a": "b", "c": float64(123), }) // Ensure marshalling works as expected, too. b, err := json.Marshal(&m) c.Assert(err, gc.IsNil) c.Assert(string(b), gc.Equals, `{"items":{"a":"b","c":123}}`) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/decode.go�����������������������0000644�0000153�0000161�00000002226�12321735642�027174� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package simplestreams import ( "bytes" "fmt" "io" "io/ioutil" "code.google.com/p/go.crypto/openpgp" "code.google.com/p/go.crypto/openpgp/clearsign" ) // DecodeCheckSignature parses the inline signed PGP text, checks the signature, // and returns plain text if the signature matches. func DecodeCheckSignature(r io.Reader, armoredPublicKey string) ([]byte, error) { data, err := ioutil.ReadAll(r) if err != nil { return nil, err } b, _ := clearsign.Decode(data) if b == nil { return nil, &NotPGPSignedError{} } keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(armoredPublicKey)) if err != nil { return nil, fmt.Errorf("failed to parse public key: %v", err) } _, err = openpgp.CheckDetachedSignature(keyring, bytes.NewBuffer(b.Bytes), b.ArmoredSignature.Body) if err != nil { return nil, err } return b.Plaintext, nil } // NotPGPSignedError is used when PGP text does not contain an inline signature. type NotPGPSignedError struct{} func (*NotPGPSignedError) Error() string { return "no PGP signature embedded in plain text data" } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/testing/������������������������0000755�0000153�0000161�00000000000�12321735642�027075� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/testing/testing.go��������������0000644�0000153�0000161�00000042630�12321735642�031106� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing // Provides a TestDataSuite which creates and provides http access to sample simplestreams metadata. import ( "fmt" "net/http" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/testing/testbase" ) var PrivateKeyPassphrase = "12345" var SignedMetadataPrivateKey = ` -----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v1.4.12 (GNU/Linux) lQH+BFJCk2EBBAC4wo3+aJ0PSeE54sv+GYNYckqysjazcZfJSdPK1GCN+Teat7ey 9dwlLhUIS34H29V+0/RcXmmRV+dObSkXzCx5ltKPSnuDsxvqiDEP0CgWdyFxhDf0 TbQuKK5OXcZ9rOTSFmnMxGaAzaV7T1IyuqA9HqntTIfC2tL4Y+QN41gS+QARAQAB /gMDAjYGIOoxe8CYYGwpat1V7NGuphvvZRpqeP0RrJ6h4vHV3hXu5NK3tn6LZF0n Qp31LfTH4BHF091UTiebexuuF1/ixLVihtv45mEVejFG1U3G298+WkWUP6AYA/5c QRzXGiuTXlsBUuFVTGn1mvxRmG3yVoLkDj0l5rN9Tq3Ir4BACIWyxjBv1n8fqw+x ti4b7YoE35FpIXQqLOdfdcKTOqUJt+5c+bed4Yx82BsLiY2/huqG2dLnbwln80Dz iYudtG8xLJ1AeHBBFB0nVdyO+mPzXgLNEbP3zle2W+rUfz+s6te7y+rlV0gad2VG tBAvUy08T9rDk0DNQl7jMq/3cGfDI1Zi/nzf2BuuBu2ddgIRmsXgKYly+Fq6eIpa nM+P1hd1Fa3rQwUSJc/zrl48tukf8sdPLDk/+nMhLHy86jp+NeXyXPLvsMAlF5kR eFjxEjHOnJlo4uIUxvlUuePyEOEl0XkQfZs+VWAPo+l2tB5UZXN0IFVzZXIgPHRl c3RAc29tZXdoZXJlLmNvbT6IuAQTAQIAIgUCUkKTYQIbAwYLCQgHAwIGFQgCCQoL BBYCAwECHgECF4AACgkQuK3uqWB66vCVugP/eJFir6Qdcvl+y9/HuP4q2iECi8ny z9tC3YC9DcJePyoBnt1LJO3HvaquZh1AIr6hgMFaujjx6cCb7YEgE0pJ4m74dvtS Y03MUPQ+Ok4cYV66zaDZLk6zpYJXZhxP7ZhlBvwQRES/rudBEQMfBcU9PrduFU39 iI+2ojHI4lsnMQE= =UUIf -----END PGP PRIVATE KEY BLOCK----- ` var SignedMetadataPublicKey = ` -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.12 (GNU/Linux) mI0EUkKTYQEEALjCjf5onQ9J4Tniy/4Zg1hySrKyNrNxl8lJ08rUYI35N5q3t7L1 3CUuFQhLfgfb1X7T9FxeaZFX505tKRfMLHmW0o9Ke4OzG+qIMQ/QKBZ3IXGEN/RN tC4ork5dxn2s5NIWaczEZoDNpXtPUjK6oD0eqe1Mh8La0vhj5A3jWBL5ABEBAAG0 HlRlc3QgVXNlciA8dGVzdEBzb21ld2hlcmUuY29tPoi4BBMBAgAiBQJSQpNhAhsD BgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRC4re6pYHrq8JW6A/94kWKvpB1y +X7L38e4/iraIQKLyfLP20LdgL0Nwl4/KgGe3Usk7ce9qq5mHUAivqGAwVq6OPHp wJvtgSATSknibvh2+1JjTcxQ9D46ThxhXrrNoNkuTrOlgldmHE/tmGUG/BBERL+u 50ERAx8FxT0+t24VTf2Ij7aiMcjiWycxAQ== =zBYH -----END PGP PUBLIC KEY BLOCK-----` var imageData = map[string]string{ "/daily/streams/v1/index.json": ` { "index": { "com.ubuntu.cloud:released:raring": { "updated": "Wed, 01 May 2013 13:31:26 +0000", "clouds": [ { "region": "us-east-1", "endpoint": "https://ec2.us-east-1.amazonaws.com" } ], "cloudname": "aws", "datatype": "image-ids", "format": "products:1.0", "products": [ "com.ubuntu.cloud:server:13.04:amd64" ], "path": "streams/v1/raring_metadata.json" } }, "updated": "Wed, 01 May 2013 13:31:26 +0000", "format": "index:1.0" } `, "/streams/v1/index.json": ` { "index": { "com.ubuntu.cloud:released:precise": { "updated": "Wed, 01 May 2013 13:31:26 +0000", "clouds": [ { "region": "us-east-1", "endpoint": "https://ec2.us-east-1.amazonaws.com" } ], "cloudname": "aws", "datatype": "image-ids", "format": "products:1.0", "products": [ "com.ubuntu.cloud:server:12.04:amd64", "com.ubuntu.cloud:server:12.04:arm" ], "path": "streams/v1/image_metadata.json" }, "com.ubuntu.cloud:released:raring": { "updated": "Wed, 01 May 2013 13:31:26 +0000", "clouds": [ { "region": "us-east-1", "endpoint": "https://ec2.us-east-1.amazonaws.com" } ], "cloudname": "aws", "datatype": "image-ids", "format": "products:1.0", "products": [ "com.ubuntu.cloud:server:13.04:amd64" ], "path": "streams/v1/raring_metadata.json" }, "com.ubuntu.cloud:released:download": { "datatype": "content-download", "path": "streams/v1/com.ubuntu.cloud:released:download.json", "updated": "Wed, 01 May 2013 13:30:37 +0000", "products": [ "com.ubuntu.cloud:server:12.10:amd64", "com.ubuntu.cloud:server:13.04:amd64" ], "format": "products:1.0" }, "com.ubuntu.juju:released:tools": { "updated": "Mon, 05 Aug 2013 11:07:04 +0000", "datatype": "content-download", "format": "products:1.0", "products": [ "com.ubuntu.juju:12.04:amd64", "com.ubuntu.juju:12.04:arm", "com.ubuntu.juju:13.04:amd64", "com.ubuntu.juju:13.04:arm" ], "path": "streams/v1/tools_metadata.json" } }, "updated": "Wed, 01 May 2013 13:31:26 +0000", "format": "index:1.0" } `, "/streams/v1/mirrors.json": ` { "mirrors": { "com.ubuntu.juju:released:tools": [ { "datatype": "content-download", "path": "streams/v1/tools_metadata:public-mirrors.json", "clouds": [ { "region": "us-east-2", "endpoint": "https://ec2.us-east-2.amazonaws.com" }, { "region": "us-west-2", "endpoint": "https://ec2.us-west-2.amazonaws.com" } ], "updated": "Wed, 14 Aug 2013 13:46:17 +0000", "format": "mirrors:1.0" }, { "datatype": "content-download", "path": "streams/v1/tools_metadata:more-mirrors.json", "updated": "Wed, 14 Aug 2013 13:46:17 +0000", "format": "mirrors:1.0" } ] }, "updated": "Wed, 01 May 2013 13:31:26 +0000", "format": "index:1.0" } `, "/streams/v1/tools_metadata.json": ` { "content_id": "com.ubuntu.juju:tools", "datatype": "content-download", "updated": "Tue, 04 Jun 2013 13:50:31 +0000", "format": "products:1.0", "products": { "com.ubuntu.juju:12.04:amd64": { "arch": "amd64", "release": "precise", "versions": { "20130806": { "items": { "1130preciseamd64": { "version": "1.13.0", "size": 2973595, "path": "tools/releases/20130806/juju-1.13.0-precise-amd64.tgz", "ftype": "tar.gz", "sha256": "447aeb6a934a5eaec4f703eda4ef2dde" } } } } }, "com.ubuntu.juju:13.04:amd64": { "arch": "amd64", "release": "raring", "versions": { "20130806": { "items": { "1130raringamd64": { "version": "1.13.0", "size": 2973173, "path": "tools/releases/20130806/juju-1.13.0-raring-amd64.tgz", "ftype": "tar.gz", "sha256": "df07ac5e1fb4232d4e9aa2effa57918a" }, "1140raringamd64": { "version": "1.14.0", "size": 2973173, "path": "tools/releases/20130806/juju-1.14.0-raring-amd64.tgz", "ftype": "tar.gz", "sha256": "df07ac5e1fb4232d4e9aa2effa57918a" } } } } }, "com.ubuntu.juju:12.04:arm": { "arch": "arm", "release": "precise", "versions": { "20130806": { "items": { "201precisearm": { "version": "2.0.1", "size": 1951096, "path": "tools/releases/20130806/juju-2.0.1-precise-arm.tgz", "ftype": "tar.gz", "sha256": "f65a92b3b41311bdf398663ee1c5cd0c" }, "1114precisearm": { "version": "1.11.4", "size": 1951096, "path": "tools/releases/20130806/juju-1.11.4-precise-arm.tgz", "ftype": "tar.gz", "sha256": "f65a92b3b41311bdf398663ee1c5cd0c" } } }, "20130803": { "items": { "1114precisearm": { "version": "1.11.4", "size": 2851541, "path": "tools/releases/20130803/juju-1.11.4-precise-arm.tgz", "ftype": "tar.gz", "sha256": "df07ac5e1fb4232d4e9aa2effa57918a" }, "1115precisearm": { "version": "1.11.5", "size": 2031281, "path": "tools/releases/20130803/juju-1.11.5-precise-arm.tgz", "ftype": "tar.gz", "sha256": "df07ac5e1fb4232d4e9aa2effa57918a" } } } } }, "com.ubuntu.juju:13.04:arm": { "arch": "arm", "release": "raring", "versions": { "20130806": { "items": { "1114raringarm": { "version": "1.11.4", "size": 1950327, "path": "tools/releases/20130806/juju-1.11.4-raring-arm.tgz", "ftype": "tar.gz", "sha256": "6472014e3255e3fe7fbd3550ef3f0a11" }, "201raringarm": { "version": "2.0.1", "size": 1950327, "path": "tools/releases/20130806/juju-2.0.1-raring-arm.tgz", "ftype": "tar.gz", "sha256": "6472014e3255e3fe7fbd3550ef3f0a11" } } } } } } } `, "/streams/v1/mirrored-tools-metadata.json": ` { "content_id": "com.ubuntu.juju:tools", "datatype": "content-download", "updated": "Tue, 04 Jun 2013 13:50:31 +0000", "format": "products:1.0", "products": { "com.ubuntu.juju:12.04:amd64": { "arch": "amd64", "release": "precise", "versions": { "20130806": { "items": { "1130preciseamd64": { "version": "1.13.0", "size": 2973595, "path": "mirrored-path/juju-1.13.0-precise-amd64.tgz", "ftype": "tar.gz", "sha256": "447aeb6a934a5eaec4f703eda4ef2dde" } } } } } } } `, "/streams/v1/tools_metadata:public-mirrors.json": ` { "mirrors": { "com.ubuntu.juju:released:tools": [ { "mirror": "http://some-mirror/", "path": "com.ubuntu.juju:download.json", "format": "products:1.0", "clouds": [ { "endpoint": "https://ec2.us-east-2.amazonaws.com", "region": "us-east-2" } ] }, { "mirror": "test:/", "path": "streams/v1/mirrored-tools-metadata.json", "format": "products:1.0", "clouds": [ { "endpoint": "https://ec2.us-west-2.amazonaws.com", "region": "us-west-2" } ] }, { "mirror": "http://another-mirror/", "path": "com.ubuntu.juju:download.json", "format": "products:1.0", "clouds": [ { "endpoint": "https://ec2.us-west-1.amazonaws.com", "region": "us-west-1" } ] } ] }, "format": "mirrors:1.0", "updated": "Mon, 05 Aug 2013 11:07:04 +0000" } `, "/streams/v1/tools_metadata:more-mirrors.json": ` { "mirrors": { "com.ubuntu.juju:released:tools": [ { "mirror": "http://big-mirror/", "path": "big:download.json", "clouds": [ { "endpoint": "https://some-endpoint.com", "region": "some-region" } ] } ] }, "format": "mirrors:1.0", "updated": "Mon, 05 Aug 2013 11:07:04 +0000" } `, "/streams/v1/image_metadata.json": ` { "updated": "Wed, 01 May 2013 13:31:26 +0000", "content_id": "com.ubuntu.cloud:released:aws", "products": { "com.ubuntu.cloud:server:12.04:amd64": { "release": "precise", "version": "12.04", "arch": "amd64", "region": "au-east-1", "endpoint": "https://somewhere", "versions": { "20121218": { "region": "au-east-2", "endpoint": "https://somewhere-else", "items": { "usww1pe": { "root_store": "ebs", "virt": "pv", "id": "ami-26745463" }, "usww2he": { "root_store": "ebs", "virt": "hvm", "id": "ami-442ea674", "region": "us-east-1", "endpoint": "https://ec2.us-east-1.amazonaws.com" }, "usww3he": { "root_store": "ebs", "virt": "hvm", "crsn": "uswest3", "id": "ami-442ea675" } }, "pubname": "ubuntu-precise-12.04-amd64-server-20121218", "label": "release" }, "20111111": { "items": { "usww3pe": { "root_store": "ebs", "virt": "pv", "id": "ami-26745464" }, "usww2pe": { "root_store": "instance", "virt": "pv", "id": "ami-442ea684", "region": "us-east-1", "endpoint": "https://ec2.us-east-1.amazonaws.com" } }, "pubname": "ubuntu-precise-12.04-amd64-server-20111111", "label": "release" } } }, "com.ubuntu.cloud:server:12.04:arm": { "release": "precise", "version": "12.04", "arch": "arm", "region": "us-east-1", "endpoint": "https://ec2.us-east-1.amazonaws.com", "versions": { "20121219": { "items": { "usww2he": { "root_store": "ebs", "virt": "pv", "id": "ami-442ea699" } }, "pubname": "ubuntu-precise-12.04-arm-server-20121219", "label": "release" } } } }, "_aliases": { "crsn": { "uswest3": { "region": "us-west-3", "endpoint": "https://ec2.us-west-3.amazonaws.com" } } }, "format": "products:1.0" } `, } var testRoundTripper *jujutest.ProxyRoundTripper func init() { testRoundTripper = &jujutest.ProxyRoundTripper{} testRoundTripper.RegisterForScheme("test") } type TestDataSuite struct{} func (s *TestDataSuite) SetUpSuite(c *gc.C) { testRoundTripper.Sub = jujutest.NewCannedRoundTripper( imageData, map[string]int{"test://unauth": http.StatusUnauthorized}) } func (s *TestDataSuite) TearDownSuite(c *gc.C) { testRoundTripper.Sub = nil } func AssertExpectedSources(c *gc.C, obtained []simplestreams.DataSource, baseURLs []string) { var obtainedURLs = make([]string, len(baseURLs)) for i, source := range obtained { url, err := source.URL("") c.Assert(err, gc.IsNil) obtainedURLs[i] = url } c.Assert(obtainedURLs, gc.DeepEquals, baseURLs) } type LocalLiveSimplestreamsSuite struct { testbase.LoggingSuite Source simplestreams.DataSource RequireSigned bool DataType string ValidConstraint simplestreams.LookupConstraint } func (s *LocalLiveSimplestreamsSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) } func (s *LocalLiveSimplestreamsSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) } const ( Index_v1 = "index:1.0" Product_v1 = "products:1.0" Mirror_v1 = "mirrors:1.0" ) type testConstraint struct { simplestreams.LookupParams } func NewTestConstraint(params simplestreams.LookupParams) *testConstraint { return &testConstraint{LookupParams: params} } func (tc *testConstraint) Ids() ([]string, error) { version, err := simplestreams.SeriesVersion(tc.Series[0]) if err != nil { return nil, err } ids := make([]string, len(tc.Arches)) for i, arch := range tc.Arches { ids[i] = fmt.Sprintf("com.ubuntu.cloud:server:%s:%s", version, arch) } return ids, nil } func init() { // Ensure out test struct can have its tags extracted. simplestreams.RegisterStructTags(TestItem{}) } type TestItem struct { Id string `json:"id"` Storage string `json:"root_store"` VirtType string `json:"virt"` Arch string `json:"arch"` RegionAlias string `json:"crsn"` RegionName string `json:"region"` Endpoint string `json:"endpoint"` } func (s *LocalLiveSimplestreamsSuite) IndexPath() string { if s.RequireSigned { return simplestreams.DefaultIndexPath + ".sjson" } return simplestreams.UnsignedIndex } func (s *LocalLiveSimplestreamsSuite) TestGetIndex(c *gc.C) { indexRef, err := s.GetIndexRef(Index_v1) c.Assert(err, gc.IsNil) c.Assert(indexRef.Format, gc.Equals, Index_v1) c.Assert(indexRef.Source, gc.Equals, s.Source) c.Assert(len(indexRef.Indexes) > 0, gc.Equals, true) } func (s *LocalLiveSimplestreamsSuite) GetIndexRef(format string) (*simplestreams.IndexReference, error) { params := simplestreams.ValueParams{ DataType: s.DataType, ValueTemplate: TestItem{}, } return simplestreams.GetIndexWithFormat( s.Source, s.IndexPath(), format, s.RequireSigned, s.ValidConstraint.Params().CloudSpec, params) } func (s *LocalLiveSimplestreamsSuite) TestGetIndexWrongFormat(c *gc.C) { _, err := s.GetIndexRef("bad") c.Assert(err, gc.NotNil) } func (s *LocalLiveSimplestreamsSuite) TestGetProductsPathExists(c *gc.C) { indexRef, err := s.GetIndexRef(Index_v1) c.Assert(err, gc.IsNil) path, err := indexRef.GetProductsPath(s.ValidConstraint) c.Assert(err, gc.IsNil) c.Assert(path, gc.Not(gc.Equals), "") } func (s *LocalLiveSimplestreamsSuite) TestGetProductsPathInvalidCloudSpec(c *gc.C) { indexRef, err := s.GetIndexRef(Index_v1) c.Assert(err, gc.IsNil) ic := NewTestConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"bad", "spec"}, Series: []string{"precise"}, }) _, err = indexRef.GetProductsPath(ic) c.Assert(err, gc.NotNil) } func (s *LocalLiveSimplestreamsSuite) TestGetProductsPathInvalidProductSpec(c *gc.C) { indexRef, err := s.GetIndexRef(Index_v1) c.Assert(err, gc.IsNil) ic := NewTestConstraint(simplestreams.LookupParams{ CloudSpec: s.ValidConstraint.Params().CloudSpec, Series: []string{"precise"}, Arches: []string{"bad"}, Stream: "spec", }) _, err = indexRef.GetProductsPath(ic) c.Assert(err, gc.NotNil) } func (s *LocalLiveSimplestreamsSuite) AssertGetMetadata(c *gc.C) *simplestreams.CloudMetadata { indexRef, err := s.GetIndexRef(Index_v1) c.Assert(err, gc.IsNil) metadata, err := indexRef.GetCloudMetadataWithFormat(s.ValidConstraint, Product_v1, s.RequireSigned) c.Assert(err, gc.IsNil) c.Assert(metadata.Format, gc.Equals, Product_v1) c.Assert(len(metadata.Products) > 0, gc.Equals, true) return metadata } func (s *LocalLiveSimplestreamsSuite) TestGetCloudMetadataWithFormat(c *gc.C) { s.AssertGetMetadata(c) } func (s *LocalLiveSimplestreamsSuite) AssertGetItemCollections(c *gc.C, version string) *simplestreams.ItemCollection { metadata := s.AssertGetMetadata(c) metadataCatalog := metadata.Products["com.ubuntu.cloud:server:12.04:amd64"] ic := metadataCatalog.Items[version] return ic } ��������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/simplestreams/datasource_test.go��������������0000644�0000153�0000161�00000005572�12321735642�031151� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package simplestreams_test import ( "io/ioutil" "net/http" "net/http/httptest" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/simplestreams/testing" "launchpad.net/juju-core/utils" ) var _ = gc.Suite(&datasourceSuite{}) var _ = gc.Suite(&datasourceHTTPSSuite{}) type datasourceSuite struct { testing.TestDataSuite } func (s *datasourceSuite) TestFetch(c *gc.C) { ds := simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames) rc, url, err := ds.Fetch("streams/v1/tools_metadata.json") c.Assert(err, gc.IsNil) defer rc.Close() c.Assert(url, gc.Equals, "test:/streams/v1/tools_metadata.json") data, err := ioutil.ReadAll(rc) cloudMetadata, err := simplestreams.ParseCloudMetadata(data, "products:1.0", url, imagemetadata.ImageMetadata{}) c.Assert(err, gc.IsNil) c.Assert(len(cloudMetadata.Products), jc.GreaterThan, 0) } func (s *datasourceSuite) TestURL(c *gc.C) { ds := simplestreams.NewURLDataSource("test", "foo", utils.VerifySSLHostnames) url, err := ds.URL("bar") c.Assert(err, gc.IsNil) c.Assert(url, gc.Equals, "foo/bar") } type datasourceHTTPSSuite struct { Server *httptest.Server } func (s *datasourceHTTPSSuite) SetUpTest(c *gc.C) { mux := http.NewServeMux() mux.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) { req.Body.Close() resp.WriteHeader(200) resp.Write([]byte("Greetings!\n")) }) s.Server = httptest.NewTLSServer(mux) } func (s *datasourceHTTPSSuite) TearDownTest(c *gc.C) { if s.Server != nil { s.Server.Close() s.Server = nil } } func (s *datasourceHTTPSSuite) TestNormalClientFails(c *gc.C) { ds := simplestreams.NewURLDataSource("test", s.Server.URL, utils.VerifySSLHostnames) url, err := ds.URL("bar") c.Assert(err, gc.IsNil) c.Check(url, gc.Equals, s.Server.URL+"/bar") reader, _, err := ds.Fetch("bar") // The underlying failure is a x509: certificate signed by unknown authority // However, the urlDataSource abstraction hides that as a simple NotFound c.Assert(err, gc.ErrorMatches, "invalid URL \".*/bar\" not found") c.Check(reader, gc.IsNil) } func (s *datasourceHTTPSSuite) TestNonVerifyingClientSucceeds(c *gc.C) { ds := simplestreams.NewURLDataSource("test", s.Server.URL, utils.NoVerifySSLHostnames) url, err := ds.URL("bar") c.Assert(err, gc.IsNil) c.Check(url, gc.Equals, s.Server.URL+"/bar") reader, _, err := ds.Fetch("bar") // The underlying failure is a x509: certificate signed by unknown authority // However, the urlDataSource abstraction hides that as a simple NotFound c.Assert(err, gc.IsNil) defer reader.Close() byteContent, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(string(byteContent), gc.Equals, "Greetings!\n") } ��������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/utils.go��������������������������������������0000644�0000153�0000161�00000001030�12321735642�024211� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package environs import ( "fmt" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/state" ) // GetStorage creates an Environ from the config in state and returns // its storage interface. func GetStorage(st *state.State) (storage.Storage, error) { envConfig, err := st.EnvironConfig() if err != nil { return nil, fmt.Errorf("cannot get environment config: %v", err) } env, err := New(envConfig) if err != nil { return nil, fmt.Errorf("cannot access environment: %v", err) } return env.Storage(), nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sync/�����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023471� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sync/sync.go����������������������������������0000644�0000153�0000161�00000025725�12321735776�025032� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sync import ( "bytes" "fmt" "io" "io/ioutil" "os" "path/filepath" "github.com/juju/loggo" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtools "launchpad.net/juju-core/environs/tools" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.environs.sync") // SyncContext describes the context for tool synchronization. type SyncContext struct { // Target holds the destination for the tool synchronization Target storage.Storage // AllVersions controls the copy of all versions, not only the latest. AllVersions bool // Copy tools with major version, if MajorVersion > 0. MajorVersion int // Copy tools with minor version, if MinorVersion > 0. MinorVersion int // DryRun controls that nothing is copied. Instead it's logged // what would be coppied. DryRun bool // Dev controls the copy of development versions as well as released ones. Dev bool // Tools are being synced for a public cloud so include mirrors information. Public bool // Source, if non-empty, specifies a directory in the local file system // to use as a source. Source string } // SyncTools copies the Juju tools tarball from the official bucket // or a specified source directory into the user's environment. func SyncTools(syncContext *SyncContext) error { sourceDataSource, err := selectSourceDatasource(syncContext) if err != nil { return err } logger.Infof("listing available tools") if syncContext.MajorVersion == 0 && syncContext.MinorVersion == 0 { syncContext.MajorVersion = version.Current.Major syncContext.MinorVersion = -1 if !syncContext.AllVersions { syncContext.MinorVersion = version.Current.Minor } } else if !syncContext.Dev && syncContext.MinorVersion != -1 { // If a major.minor version is specified, we allow dev versions. // If Dev is already true, leave it alone. syncContext.Dev = true } released := !syncContext.Dev && !version.Current.IsDev() sourceTools, err := envtools.FindToolsForCloud( []simplestreams.DataSource{sourceDataSource}, simplestreams.CloudSpec{}, syncContext.MajorVersion, syncContext.MinorVersion, coretools.Filter{Released: released}) if err != nil { return err } logger.Infof("found %d tools", len(sourceTools)) if !syncContext.AllVersions { var latest version.Number latest, sourceTools = sourceTools.Newest() logger.Infof("found %d recent tools (version %s)", len(sourceTools), latest) } for _, tool := range sourceTools { logger.Debugf("found source tool: %v", tool) } logger.Infof("listing target tools storage") targetStorage := syncContext.Target targetTools, err := envtools.ReadList(targetStorage, syncContext.MajorVersion, -1) switch err { case nil, coretools.ErrNoMatches, envtools.ErrNoTools: default: return err } for _, tool := range targetTools { logger.Debugf("found target tool: %v", tool) } missing := sourceTools.Exclude(targetTools) logger.Infof("found %d tools in target; %d tools to be copied", len(targetTools), len(missing)) err = copyTools(missing, syncContext, targetStorage) if err != nil { return err } logger.Infof("copied %d tools", len(missing)) logger.Infof("generating tools metadata") if !syncContext.DryRun { targetTools = append(targetTools, missing...) writeMirrors := envtools.DoNotWriteMirrors if syncContext.Public { writeMirrors = envtools.WriteMirrors } err = envtools.MergeAndWriteMetadata(targetStorage, targetTools, writeMirrors) if err != nil { return err } } logger.Infof("tools metadata written") return nil } // selectSourceStorage returns a storage reader based on the source setting. func selectSourceDatasource(syncContext *SyncContext) (simplestreams.DataSource, error) { source := syncContext.Source if source == "" { source = envtools.DefaultBaseURL } sourceURL, err := envtools.ToolsURL(source) if err != nil { return nil, err } logger.Infof("using sync tools source: %v", sourceURL) return simplestreams.NewURLDataSource("sync tools source", sourceURL, utils.VerifySSLHostnames), nil } // copyTools copies a set of tools from the source to the target. func copyTools(tools []*coretools.Tools, syncContext *SyncContext, dest storage.Storage) error { for _, tool := range tools { logger.Infof("copying %s from %s", tool.Version, tool.URL) if syncContext.DryRun { continue } if err := copyOneToolsPackage(tool, dest); err != nil { return err } } return nil } // copyOneToolsPackage copies one tool from the source to the target. func copyOneToolsPackage(tool *coretools.Tools, dest storage.Storage) error { toolsName := envtools.StorageName(tool.Version) logger.Infof("copying %v", toolsName) resp, err := utils.GetValidatingHTTPClient().Get(tool.URL) if err != nil { return err } buf := &bytes.Buffer{} srcFile := resp.Body defer srcFile.Close() tool.SHA256, tool.Size, err = utils.ReadSHA256(io.TeeReader(srcFile, buf)) if err != nil { return err } sizeInKB := (tool.Size + 512) / 1024 logger.Infof("downloaded %v (%dkB), uploading", toolsName, sizeInKB) logger.Infof("download %dkB, uploading", sizeInKB) return dest.Put(toolsName, buf, tool.Size) } // UploadFunc is the type of Upload, which may be // reassigned to control the behaviour of tools // uploading. type UploadFunc func(stor storage.Storage, forceVersion *version.Number, series ...string) (*coretools.Tools, error) // Upload builds whatever version of launchpad.net/juju-core is in $GOPATH, // uploads it to the given storage, and returns a Tools instance describing // them. If forceVersion is not nil, the uploaded tools bundle will report // the given version number; if any fakeSeries are supplied, additional copies // of the built tools will be uploaded for use by machines of those series. // Juju tools built for one series do not necessarily run on another, but this // func exists only for development use cases. var Upload UploadFunc = upload func upload(stor storage.Storage, forceVersion *version.Number, fakeSeries ...string) (*coretools.Tools, error) { builtTools, err := BuildToolsTarball(forceVersion) if err != nil { return nil, err } defer os.RemoveAll(builtTools.Dir) logger.Debugf("Uploading tools for %v", fakeSeries) return SyncBuiltTools(stor, builtTools, fakeSeries...) } // cloneToolsForSeries copies the built tools tarball into a tarball for the specified // series and generates corresponding metadata. func cloneToolsForSeries(toolsInfo *BuiltTools, series ...string) error { // Copy the tools to the target storage, recording a Tools struct for each one. var targetTools coretools.List targetTools = append(targetTools, &coretools.Tools{ Version: toolsInfo.Version, Size: toolsInfo.Size, SHA256: toolsInfo.Sha256Hash, }) putTools := func(vers version.Binary) (string, error) { name := envtools.StorageName(vers) src := filepath.Join(toolsInfo.Dir, toolsInfo.StorageName) dest := filepath.Join(toolsInfo.Dir, name) err := utils.CopyFile(dest, src) if err != nil { return "", err } // Append to targetTools the attributes required to write out tools metadata. targetTools = append(targetTools, &coretools.Tools{ Version: vers, Size: toolsInfo.Size, SHA256: toolsInfo.Sha256Hash, }) return name, nil } logger.Debugf("generating tarballs for %v", series) for _, series := range series { _, err := simplestreams.SeriesVersion(series) if err != nil { return err } if series != toolsInfo.Version.Series { fakeVersion := toolsInfo.Version fakeVersion.Series = series if _, err := putTools(fakeVersion); err != nil { return err } } } // The tools have been copied to a temp location from which they will be uploaded, // now write out the matching simplestreams metadata so that SyncTools can find them. metadataStore, err := filestorage.NewFileStorageWriter(toolsInfo.Dir) if err != nil { return err } logger.Debugf("generating tools metadata") return envtools.MergeAndWriteMetadata(metadataStore, targetTools, false) } // BuiltTools contains metadata for a tools tarball resulting from // a call to BundleTools. type BuiltTools struct { Version version.Binary Dir string StorageName string Sha256Hash string Size int64 } // BuildToolsTarballFunc is a function which can build a tools tarball. type BuildToolsTarballFunc func(forceVersion *version.Number) (*BuiltTools, error) // Override for testing. var BuildToolsTarball BuildToolsTarballFunc = buildToolsTarball // buildToolsTarball bundles a tools tarball and places it in a temp directory in // the expected tools path. func buildToolsTarball(forceVersion *version.Number) (builtTools *BuiltTools, err error) { // TODO(rog) find binaries from $PATH when not using a development // version of juju within a $GOPATH. logger.Debugf("Building tools") // We create the entire archive before asking the environment to // start uploading so that we can be sure we have archived // correctly. f, err := ioutil.TempFile("", "juju-tgz") if err != nil { return nil, err } defer f.Close() defer os.Remove(f.Name()) toolsVersion, sha256Hash, err := envtools.BundleTools(f, forceVersion) if err != nil { return nil, err } fileInfo, err := f.Stat() if err != nil { return nil, fmt.Errorf("cannot stat newly made tools archive: %v", err) } size := fileInfo.Size() logger.Infof("built tools %v (%dkB)", toolsVersion, (size+512)/1024) baseToolsDir, err := ioutil.TempDir("", "") if err != nil { return nil, err } // If we exit with an error, clean up the built tools directory. defer func() { if err != nil { os.RemoveAll(baseToolsDir) } }() err = os.MkdirAll(filepath.Join(baseToolsDir, storage.BaseToolsPath, "releases"), 0755) if err != nil { return nil, err } storageName := envtools.StorageName(toolsVersion) err = utils.CopyFile(filepath.Join(baseToolsDir, storageName), f.Name()) if err != nil { return nil, err } return &BuiltTools{ Version: toolsVersion, Dir: baseToolsDir, StorageName: storageName, Size: size, Sha256Hash: sha256Hash, }, nil } // SyncBuiltTools copies to storage a tools tarball and cloned copies for each series. func SyncBuiltTools(stor storage.Storage, builtTools *BuiltTools, fakeSeries ...string) (*coretools.Tools, error) { if err := cloneToolsForSeries(builtTools, fakeSeries...); err != nil { return nil, err } syncContext := &SyncContext{ Source: builtTools.Dir, Target: stor, AllVersions: true, Dev: builtTools.Version.IsDev(), MajorVersion: builtTools.Version.Major, MinorVersion: -1, } logger.Debugf("uploading tools to cloud storage") err := SyncTools(syncContext) if err != nil { return nil, err } url, err := stor.URL(builtTools.StorageName) if err != nil { return nil, err } return &coretools.Tools{ Version: builtTools.Version, URL: url, Size: builtTools.Size, SHA256: builtTools.Sha256Hash, }, nil } �������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/sync/sync_test.go�����������������������������0000644�0000153�0000161�00000030712�12321735642�026051� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sync_test import ( "bytes" "io" "io/ioutil" "net/http" "os" "os/exec" "path/filepath" "sort" "strings" "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/sync" envtesting "launchpad.net/juju-core/environs/testing" envtools "launchpad.net/juju-core/environs/tools" ttesting "launchpad.net/juju-core/environs/tools/testing" "launchpad.net/juju-core/provider/dummy" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) func TestPackage(t *testing.T) { gc.TestingT(t) } type syncSuite struct { testbase.LoggingSuite envtesting.ToolsFixture targetEnv environs.Environ origVersion version.Binary storage storage.Storage localStorage string } var _ = gc.Suite(&syncSuite{}) var _ = gc.Suite(&uploadSuite{}) func (s *syncSuite) setUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) s.origVersion = version.Current // It's important that this be v1.8.x to match the test data. version.Current.Number = version.MustParse("1.8.3") // Create a target environments.yaml. fakeHome := coretesting.MakeFakeHome(c, ` environments: test-target: type: dummy state-server: false authorized-keys: "not-really-one" `) s.AddCleanup(func(*gc.C) { fakeHome.Restore() }) var err error s.targetEnv, err = environs.PrepareFromName("test-target", coretesting.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.RemoveAllTools(c, s.targetEnv) // Create a source storage. baseDir := c.MkDir() stor, err := filestorage.NewFileStorageWriter(baseDir) c.Assert(err, gc.IsNil) s.storage = stor // Create a local tools directory. s.localStorage = c.MkDir() // Populate both local and default tools locations with the public tools. versionStrings := make([]string, len(vAll)) for i, vers := range vAll { versionStrings[i] = vers.String() } ttesting.MakeTools(c, baseDir, "releases", versionStrings) ttesting.MakeTools(c, s.localStorage, "releases", versionStrings) // Switch the default tools location. baseURL, err := s.storage.URL(storage.BaseToolsPath) c.Assert(err, gc.IsNil) s.PatchValue(&envtools.DefaultBaseURL, baseURL) } func (s *syncSuite) tearDownTest(c *gc.C) { dummy.Reset() version.Current = s.origVersion s.ToolsFixture.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } var tests = []struct { description string ctx *sync.SyncContext source bool tools []version.Binary version version.Number major int minor int expectMirrors bool }{ { description: "copy newest from the filesystem", ctx: &sync.SyncContext{}, source: true, tools: v180all, }, { description: "copy newest from the dummy environment", ctx: &sync.SyncContext{}, tools: v180all, }, { description: "copy matching dev from the dummy environment", ctx: &sync.SyncContext{}, version: version.MustParse("1.9.3"), tools: v190all, }, { description: "copy matching major, minor from the dummy environment", ctx: &sync.SyncContext{}, major: 3, minor: 2, tools: []version.Binary{v320p64}, }, { description: "copy matching major, minor dev from the dummy environment", ctx: &sync.SyncContext{}, major: 3, minor: 1, tools: []version.Binary{v310p64}, }, { description: "copy all from the dummy environment", ctx: &sync.SyncContext{ AllVersions: true, }, tools: v1noDev, }, { description: "copy all and dev from the dummy environment", ctx: &sync.SyncContext{ AllVersions: true, Dev: true, }, tools: v1all, }, { description: "write the mirrors files", ctx: &sync.SyncContext{ Public: true, }, tools: v180all, expectMirrors: true, }, } func (s *syncSuite) TestSyncing(c *gc.C) { for i, test := range tests { // Perform all tests in a "clean" environment. func() { s.setUpTest(c) defer s.tearDownTest(c) c.Logf("test %d: %s", i, test.description) if test.source { test.ctx.Source = s.localStorage } if test.version != version.Zero { version.Current.Number = test.version } if test.major > 0 { test.ctx.MajorVersion = test.major test.ctx.MinorVersion = test.minor } test.ctx.Target = s.targetEnv.Storage() err := sync.SyncTools(test.ctx) c.Assert(err, gc.IsNil) targetTools, err := envtools.FindTools( s.targetEnv, test.ctx.MajorVersion, test.ctx.MinorVersion, coretools.Filter{}, envtools.DoNotAllowRetry) c.Assert(err, gc.IsNil) assertToolsList(c, targetTools, test.tools) assertNoUnexpectedTools(c, s.targetEnv.Storage()) assertMirrors(c, s.targetEnv.Storage(), test.expectMirrors) }() } } var ( v100p64 = version.MustParseBinary("1.0.0-precise-amd64") v100q64 = version.MustParseBinary("1.0.0-quantal-amd64") v100q32 = version.MustParseBinary("1.0.0-quantal-i386") v100all = []version.Binary{v100p64, v100q64, v100q32} v180q64 = version.MustParseBinary("1.8.0-quantal-amd64") v180p32 = version.MustParseBinary("1.8.0-precise-i386") v180all = []version.Binary{v180q64, v180p32} v190q64 = version.MustParseBinary("1.9.0-quantal-amd64") v190p32 = version.MustParseBinary("1.9.0-precise-i386") v190all = []version.Binary{v190q64, v190p32} v1noDev = append(v100all, v180all...) v1all = append(v1noDev, v190all...) v200p64 = version.MustParseBinary("2.0.0-precise-amd64") v310p64 = version.MustParseBinary("3.1.0-precise-amd64") v320p64 = version.MustParseBinary("3.2.0-precise-amd64") vAll = append(append(v1all, v200p64), v310p64, v320p64) ) func assertNoUnexpectedTools(c *gc.C, stor storage.StorageReader) { // We only expect v1.x tools, no v2.x tools. list, err := envtools.ReadList(stor, 2, 0) if len(list) > 0 { c.Logf("got unexpected tools: %s", list) } c.Assert(err, gc.Equals, coretools.ErrNoMatches) } func assertToolsList(c *gc.C, list coretools.List, expected []version.Binary) { urls := list.URLs() c.Check(urls, gc.HasLen, len(expected)) for _, vers := range expected { c.Assert(urls[vers], gc.Not(gc.Equals), "") } } func assertMirrors(c *gc.C, stor storage.StorageReader, expectMirrors bool) { r, err := storage.Get(stor, "tools/"+simplestreams.UnsignedMirror) if err == nil { defer r.Close() } if expectMirrors { data, err := ioutil.ReadAll(r) c.Assert(err, gc.IsNil) c.Assert(string(data), jc.Contains, `"mirrors":`) } else { c.Assert(err, gc.NotNil) } } type uploadSuite struct { env environs.Environ testbase.LoggingSuite envtesting.ToolsFixture } func (s *uploadSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) // We only want to use simplestreams to find any synced tools. cfg, err := config.New(config.NoDefaults, dummy.SampleConfig()) c.Assert(err, gc.IsNil) s.env, err = environs.Prepare(cfg, coretesting.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) } func (s *uploadSuite) TearDownTest(c *gc.C) { dummy.Reset() s.ToolsFixture.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *uploadSuite) TestUpload(c *gc.C) { t, err := sync.Upload(s.env.Storage(), nil) c.Assert(err, gc.IsNil) c.Assert(t.Version, gc.Equals, version.Current) c.Assert(t.URL, gc.Not(gc.Equals), "") dir := downloadTools(c, t) out, err := exec.Command(filepath.Join(dir, "jujud"), "version").CombinedOutput() c.Assert(err, gc.IsNil) c.Assert(string(out), gc.Equals, version.Current.String()+"\n") } func (s *uploadSuite) TestUploadFakeSeries(c *gc.C) { seriesToUpload := "precise" if seriesToUpload == version.Current.Series { seriesToUpload = "raring" } t, err := sync.Upload(s.env.Storage(), nil, "quantal", seriesToUpload) c.Assert(err, gc.IsNil) s.assertUploadedTools(c, t, seriesToUpload) } func (s *uploadSuite) TestUploadAndForceVersion(c *gc.C) { // This test actually tests three things: // the writing of the FORCE-VERSION file; // the reading of the FORCE-VERSION file by the version package; // and the reading of the version from jujud. vers := version.Current vers.Patch++ t, err := sync.Upload(s.env.Storage(), &vers.Number) c.Assert(err, gc.IsNil) c.Assert(t.Version, gc.Equals, vers) } // Test that the upload procedure fails correctly // when the build process fails (because of a bad Go source // file in this case). func (s *uploadSuite) TestUploadBadBuild(c *gc.C) { gopath := c.MkDir() join := append([]string{gopath, "src"}, strings.Split("launchpad.net/juju-core/cmd/broken", "/")...) pkgdir := filepath.Join(join...) err := os.MkdirAll(pkgdir, 0777) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(pkgdir, "broken.go"), []byte("nope"), 0666) c.Assert(err, gc.IsNil) defer os.Setenv("GOPATH", os.Getenv("GOPATH")) os.Setenv("GOPATH", gopath) t, err := sync.Upload(s.env.Storage(), nil) c.Assert(t, gc.IsNil) c.Assert(err, gc.ErrorMatches, `build command "go" failed: exit status 1; can't load package:(.|\n)*`) } func (s *uploadSuite) TestSyncTools(c *gc.C) { builtTools, err := sync.BuildToolsTarball(nil) c.Assert(err, gc.IsNil) t, err := sync.SyncBuiltTools(s.env.Storage(), builtTools) c.Assert(err, gc.IsNil) c.Assert(t.Version, gc.Equals, version.Current) c.Assert(t.URL, gc.Not(gc.Equals), "") dir := downloadTools(c, t) out, err := exec.Command(filepath.Join(dir, "jujud"), "version").CombinedOutput() c.Assert(err, gc.IsNil) c.Assert(string(out), gc.Equals, version.Current.String()+"\n") } func (s *uploadSuite) TestSyncToolsFakeSeries(c *gc.C) { seriesToUpload := "precise" if seriesToUpload == version.Current.Series { seriesToUpload = "raring" } builtTools, err := sync.BuildToolsTarball(nil) c.Assert(err, gc.IsNil) t, err := sync.SyncBuiltTools(s.env.Storage(), builtTools, "quantal", seriesToUpload) c.Assert(err, gc.IsNil) s.assertUploadedTools(c, t, seriesToUpload) } func (s *uploadSuite) TestSyncAndForceVersion(c *gc.C) { // This test actually tests three things: // the writing of the FORCE-VERSION file; // the reading of the FORCE-VERSION file by the version package; // and the reading of the version from jujud. vers := version.Current vers.Patch++ builtTools, err := sync.BuildToolsTarball(&vers.Number) c.Assert(err, gc.IsNil) t, err := sync.SyncBuiltTools(s.env.Storage(), builtTools) c.Assert(err, gc.IsNil) c.Assert(t.Version, gc.Equals, vers) } func (s *uploadSuite) assertUploadedTools(c *gc.C, t *coretools.Tools, uploadedSeries string) { c.Assert(t.Version, gc.Equals, version.Current) expectRaw := downloadToolsRaw(c, t) list, err := envtools.ReadList(s.env.Storage(), version.Current.Major, version.Current.Minor) c.Assert(err, gc.IsNil) c.Assert(list, gc.HasLen, 3) expectSeries := []string{"quantal", uploadedSeries, version.Current.Series} sort.Strings(expectSeries) c.Assert(list.AllSeries(), gc.DeepEquals, expectSeries) for _, t := range list { c.Logf("checking %s", t.URL) c.Assert(t.Version.Number, gc.Equals, version.Current.Number) actualRaw := downloadToolsRaw(c, t) c.Assert(string(actualRaw), gc.Equals, string(expectRaw)) } metadata := ttesting.ParseMetadataFromStorage(c, s.env.Storage(), false) c.Assert(metadata, gc.HasLen, 3) for i, tm := range metadata { c.Assert(tm.Release, gc.Equals, expectSeries[i]) c.Assert(tm.Version, gc.Equals, version.Current.Number.String()) } } // downloadTools downloads the supplied tools and extracts them into a // new directory. func downloadTools(c *gc.C, t *coretools.Tools) string { resp, err := utils.GetValidatingHTTPClient().Get(t.URL) c.Assert(err, gc.IsNil) defer resp.Body.Close() cmd := exec.Command("tar", "xz") cmd.Dir = c.MkDir() cmd.Stdin = resp.Body out, err := cmd.CombinedOutput() c.Assert(err, gc.IsNil, gc.Commentf(string(out))) return cmd.Dir } // downloadToolsRaw downloads the supplied tools and returns the raw bytes. func downloadToolsRaw(c *gc.C, t *coretools.Tools) []byte { resp, err := utils.GetValidatingHTTPClient().Get(t.URL) c.Assert(err, gc.IsNil) defer resp.Body.Close() c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) var buf bytes.Buffer _, err = io.Copy(&buf, resp.Body) c.Assert(err, gc.IsNil) return buf.Bytes() } ������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/suite_test.go���������������������������������0000644�0000153�0000161�00000000341�12321735642�025245� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/authenticationprovider.go���������������������0000644�0000153�0000161�00000004407�12321735642�027656� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs import ( "fmt" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" apiprovisioner "launchpad.net/juju-core/state/api/provisioner" "launchpad.net/juju-core/utils" ) // TaggedPasswordChanger defines an interface for a entity with a // Tag() and SetPassword() methods. type TaggedPasswordChanger interface { SetPassword(string) error Tag() string } // AuthenticationProvider defines the single method that the provisioner // task needs to set up authentication for a machine. type AuthenticationProvider interface { SetupAuthentication(machine TaggedPasswordChanger) (*state.Info, *api.Info, error) } // NewEnvironAuthenticator gets the state and api info once from the environ. func NewEnvironAuthenticator(environ Environ) (AuthenticationProvider, error) { stateInfo, apiInfo, err := environ.StateInfo() if err != nil { return nil, err } return &simpleAuth{stateInfo, apiInfo}, nil } // NewAPIAuthenticator gets the state and api info once from the // provisioner API. func NewAPIAuthenticator(st *apiprovisioner.State) (AuthenticationProvider, error) { stateAddresses, err := st.StateAddresses() if err != nil { return nil, err } apiAddresses, err := st.APIAddresses() if err != nil { return nil, err } caCert, err := st.CACert() if err != nil { return nil, err } stateInfo := &state.Info{ Addrs: stateAddresses, CACert: caCert, } apiInfo := &api.Info{ Addrs: apiAddresses, CACert: caCert, } return &simpleAuth{stateInfo, apiInfo}, nil } type simpleAuth struct { stateInfo *state.Info apiInfo *api.Info } func (auth *simpleAuth) SetupAuthentication(machine TaggedPasswordChanger) (*state.Info, *api.Info, error) { password, err := utils.RandomPassword() if err != nil { return nil, nil, fmt.Errorf("cannot make password for machine %v: %v", machine, err) } if err := machine.SetPassword(password); err != nil { return nil, nil, fmt.Errorf("cannot set API password for machine %v: %v", machine, err) } stateInfo := *auth.stateInfo stateInfo.Tag = machine.Tag() stateInfo.Password = password apiInfo := *auth.apiInfo apiInfo.Tag = machine.Tag() apiInfo.Password = password return &stateInfo, &apiInfo, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/boilerplate_config_test.go��������������������0000644�0000153�0000161�00000002255�12321735642�027751� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs_test import ( "strings" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/juju/osenv" _ "launchpad.net/juju-core/provider/ec2" _ "launchpad.net/juju-core/provider/manual" _ "launchpad.net/juju-core/provider/openstack" ) type BoilerplateConfigSuite struct { } var _ = gc.Suite(&BoilerplateConfigSuite{}) func (*BoilerplateConfigSuite) TestBoilerPlateGeneration(c *gc.C) { defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir())) boilerplate_text := environs.BoilerplateConfig() _, err := environs.ReadEnvironsBytes([]byte(boilerplate_text)) c.Assert(err, gc.IsNil) } func (*BoilerplateConfigSuite) TestBoilerPlateAliases(c *gc.C) { defer osenv.SetJujuHome(osenv.SetJujuHome(c.MkDir())) boilerplate_text := environs.BoilerplateConfig() // There should be only one occurrence of "manual", despite // there being an alias ("null"). There should be nothing for // aliases. n := strings.Count(boilerplate_text, "type: manual") c.Assert(n, gc.Equals, 1) n = strings.Count(boilerplate_text, "type: null") c.Assert(n, gc.Equals, 0) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/emptystorage_test.go��������������������������0000644�0000153�0000161�00000004760�12321735642�026650� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs_test import ( "io/ioutil" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type EmptyStorageSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&EmptyStorageSuite{}) func (s *EmptyStorageSuite) TestGet(c *gc.C) { f, err := storage.Get(environs.EmptyStorage, "anything") c.Assert(f, gc.IsNil) c.Assert(err, gc.ErrorMatches, `file "anything" not found`) } func (s *EmptyStorageSuite) TestURL(c *gc.C) { url, err := environs.EmptyStorage.URL("anything") c.Assert(url, gc.Equals, "") c.Assert(err, gc.ErrorMatches, `file "anything" not found`) } func (s *EmptyStorageSuite) TestList(c *gc.C) { names, err := storage.List(environs.EmptyStorage, "anything") c.Assert(names, gc.IsNil) c.Assert(err, gc.IsNil) } type verifyStorageSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&verifyStorageSuite{}) const existingEnv = ` environments: test: type: dummy state-server: false authorized-keys: i-am-a-key ` func (s *verifyStorageSuite) TearDownTest(c *gc.C) { dummy.Reset() s.LoggingSuite.TearDownTest(c) } func (s *verifyStorageSuite) TestVerifyStorage(c *gc.C) { defer testing.MakeFakeHome(c, existingEnv, "existing").Restore() ctx := testing.Context(c) environ, err := environs.PrepareFromName("test", ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) stor := environ.Storage() err = environs.VerifyStorage(stor) c.Assert(err, gc.IsNil) reader, err := storage.Get(stor, environs.VerificationFilename) c.Assert(err, gc.IsNil) defer reader.Close() contents, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(string(contents), gc.Equals, "juju-core storage writing verified: ok\n") } func (s *verifyStorageSuite) TestVerifyStorageFails(c *gc.C) { defer testing.MakeFakeHome(c, existingEnv, "existing").Restore() ctx := testing.Context(c) environ, err := environs.PrepareFromName("test", ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) stor := environ.Storage() someError := errors.Unauthorizedf("you shall not pass") dummy.Poison(stor, environs.VerificationFilename, someError) err = environs.VerifyStorage(stor) c.Assert(err, gc.Equals, environs.VerifyStorageError) } ����������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/httpstorage/����������������������������������0000755�0000153�0000161�00000000000�12321735643�025075� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/httpstorage/backend.go������������������������0000644�0000153�0000161�00000015725�12321735642�027024� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package httpstorage import ( "crypto/tls" "crypto/x509" "errors" "fmt" "io/ioutil" "net" "net/http" "strings" "time" "launchpad.net/juju-core/cert" "launchpad.net/juju-core/environs/storage" ) // storageBackend provides HTTP access to a storage object. type storageBackend struct { backend storage.Storage // httpsPort is the port to send to clients // if they perform a HEAD request. httpsPort int // authkey is non-empty if modifying requests // require an auth key. authkey string } // ServeHTTP handles the HTTP requests to the container. func (s *storageBackend) ServeHTTP(w http.ResponseWriter, req *http.Request) { switch req.Method { case "PUT", "DELETE": // Don't allow modifying operations if there's an HTTPS backend // to handle that, and ensure the user is authorized/authenticated. if s.httpsPort != 0 || !s.authorized(req) { http.Error(w, "unauthorized access", http.StatusUnauthorized) return } } switch req.Method { case "GET": if strings.HasSuffix(req.URL.Path, "*") { s.handleList(w, req) } else { s.handleGet(w, req) } case "HEAD": s.handleHead(w, req) case "PUT": s.handlePut(w, req) case "DELETE": s.handleDelete(w, req) default: http.Error(w, "method "+req.Method+" is not supported", http.StatusMethodNotAllowed) } } // authorized checks that either the storage does not require // authorization, or the user has specified the correct auth key. func (s *storageBackend) authorized(req *http.Request) bool { if s.authkey == "" { return true } return req.URL.Query().Get("authkey") == s.authkey } // hostOnly splits a host of the form host, or host:port, // into its host and port parts, and returns the host part. func hostOnly(host string) (string, error) { hostonly, _, err := net.SplitHostPort(host) if err != nil { // err may be because of missing :port. Checking // the error message is brittle, so let's try // again with ":0" tacked on the end. var err2 error hostonly, _, err = net.SplitHostPort(host + ":0") if err2 != nil { // something heinous, return the original error return "", err } } return hostonly, nil } // handleHead returns the HTTPS URL for the specified // path in the Location header. func (s *storageBackend) handleHead(w http.ResponseWriter, req *http.Request) { if s.httpsPort != 0 { host, err := hostOnly(req.Host) if err != nil { http.Error(w, fmt.Sprintf("failed to split host: %v", err), http.StatusBadRequest) return } url := fmt.Sprintf("https://%s:%d%s", host, s.httpsPort, req.URL.Path) w.Header().Set("Location", url) } else { http.Error(w, "method HEAD is not supported", http.StatusMethodNotAllowed) return } w.WriteHeader(http.StatusOK) } // handleGet returns a storage file to the client. func (s *storageBackend) handleGet(w http.ResponseWriter, req *http.Request) { readcloser, err := s.backend.Get(req.URL.Path[1:]) if err != nil { http.Error(w, fmt.Sprint(err), http.StatusNotFound) return } defer readcloser.Close() data, err := ioutil.ReadAll(readcloser) if err != nil { http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/octet-stream") w.Write(data) } // handleList returns the file names in the storage to the client. func (s *storageBackend) handleList(w http.ResponseWriter, req *http.Request) { prefix := req.URL.Path prefix = prefix[1 : len(prefix)-1] // drop the leading '/' and trailing '*' names, err := s.backend.List(prefix) if err != nil { http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) return } data := []byte(strings.Join(names, "\n")) w.Header().Set("Content-Type", "application/octet-stream") w.Write(data) } // handlePut stores data from the client in the storage. func (s *storageBackend) handlePut(w http.ResponseWriter, req *http.Request) { if req.ContentLength < 0 { http.Error(w, "missing or invalid Content-Length header", http.StatusInternalServerError) return } err := s.backend.Put(req.URL.Path[1:], req.Body, req.ContentLength) if err != nil { http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) } // handleDelete removes a file from the storage. func (s *storageBackend) handleDelete(w http.ResponseWriter, req *http.Request) { if !s.authorized(req) { http.Error(w, "unauthorized access", http.StatusUnauthorized) return } err := s.backend.Remove(req.URL.Path[1:]) if err != nil { http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) } // Serve runs a storage server on the given network address, relaying // requests to the given storage implementation. It returns the network // listener. This can then be attached to with Client. func Serve(addr string, stor storage.Storage) (net.Listener, error) { return serve(addr, stor, nil, "") } // ServeTLS runs a storage server on the given network address, relaying // requests to the given storage implementation. The server runs a TLS // listener, and verifies client certificates (if given) against the // specified CA certificate. A client certificate is only required for // PUT and DELETE methods. // // This method returns the network listener, which can then be attached // to with ClientTLS. func ServeTLS(addr string, stor storage.Storage, caCertPEM, caKeyPEM []byte, hostnames []string, authkey string) (net.Listener, error) { expiry := time.Now().UTC().AddDate(10, 0, 0) certPEM, keyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, hostnames) if err != nil { return nil, err } serverCert, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { return nil, err } caCerts := x509.NewCertPool() if !caCerts.AppendCertsFromPEM(caCertPEM) { return nil, errors.New("error adding CA certificate to pool") } config := &tls.Config{ NextProtos: []string{"http/1.1"}, Certificates: []tls.Certificate{serverCert}, ClientAuth: tls.VerifyClientCertIfGiven, ClientCAs: caCerts, } return serve(addr, stor, config, authkey) } func serve(addr string, stor storage.Storage, tlsConfig *tls.Config, authkey string) (net.Listener, error) { listener, err := net.Listen("tcp", addr) if err != nil { return nil, fmt.Errorf("cannot start listener: %v", err) } backend := &storageBackend{backend: stor} if tlsConfig != nil { tlsBackend := &storageBackend{backend: stor, authkey: authkey} tcpAddr := listener.Addr().(*net.TCPAddr) tlsListener, err := tls.Listen("tcp", fmt.Sprintf("[%s]:0", tcpAddr.IP), tlsConfig) if err != nil { listener.Close() return nil, fmt.Errorf("cannot start TLS listener: %v", err) } backend.httpsPort = tlsListener.Addr().(*net.TCPAddr).Port goServe(tlsListener, tlsBackend) } goServe(listener, backend) return listener, nil } func goServe(listener net.Listener, backend *storageBackend) { // Construct a NewServeMux to sanitise request paths. mux := http.NewServeMux() mux.Handle("/", backend) go http.Serve(listener, mux) } �������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/httpstorage/storage.go������������������������0000644�0000153�0000161�00000014103�12321735642�027066� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package httpstorage import ( "crypto/tls" "crypto/x509" "errors" "fmt" "io" "io/ioutil" "net/http" "net/url" "sort" "strings" "sync" "github.com/juju/loggo" "launchpad.net/juju-core/environs/storage" coreerrors "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" ) var logger = loggo.GetLogger("juju.environs.httpstorage") // storage implements the storage.Storage interface. type localStorage struct { addr string client *http.Client authkey string httpsBaseURL string httpsBaseURLError error httpsBaseURLOnce sync.Once } // Client returns a storage object that will talk to the // storage server at the given network address (see Serve) func Client(addr string) storage.Storage { return &localStorage{ addr: addr, client: utils.GetValidatingHTTPClient(), } } // ClientTLS returns a storage object that will talk to the // storage server at the given network address (see Serve), // using TLS. The client is given an authentication key, // which the server will verify for Put and Remove* operations. func ClientTLS(addr string, caCertPEM []byte, authkey string) (storage.Storage, error) { logger.Debugf("using https storage at %q", addr) caCerts := x509.NewCertPool() if caCertPEM != nil { if !caCerts.AppendCertsFromPEM(caCertPEM) { return nil, errors.New("error adding CA certificate to pool") } } return &localStorage{ addr: addr, authkey: authkey, client: &http.Client{ Transport: utils.NewHttpTLSTransport(&tls.Config{RootCAs: caCerts}), }, }, nil } func (s *localStorage) getHTTPSBaseURL() (string, error) { url, _ := s.URL("") // never fails resp, err := s.client.Head(url) if err != nil { return "", err } resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("Could not access file storage: %v %s", url, resp.Status) } httpsURL, err := resp.Location() if err != nil { return "", err } return httpsURL.String(), nil } // Get opens the given storage file and returns a ReadCloser // that can be used to read its contents. It is the caller's // responsibility to close it after use. If the name does not // exist, it should return a *NotFoundError. func (s *localStorage) Get(name string) (io.ReadCloser, error) { logger.Debugf("getting %q from storage", name) url, err := s.URL(name) if err != nil { return nil, err } resp, err := s.client.Get(url) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, coreerrors.NotFoundf("file %q", name) } return resp.Body, nil } // List lists all names in the storage with the given prefix, in // alphabetical order. The names in the storage are considered // to be in a flat namespace, so the prefix may include slashes // and the names returned are the full names for the matching // entries. func (s *localStorage) List(prefix string) ([]string, error) { url, err := s.URL(prefix) if err != nil { return nil, err } resp, err := s.client.Get(url + "*") if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { // If the path is not found, it's not an error // because it's only created when the first // file is put. if resp.StatusCode == http.StatusNotFound { return []string{}, nil } return nil, fmt.Errorf("%s", resp.Status) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } if len(body) == 0 { return nil, nil } names := strings.Split(string(body), "\n") sort.Strings(names) return names, nil } // URL returns a URL that can be used to access the given storage file. func (s *localStorage) URL(name string) (string, error) { return fmt.Sprintf("http://%s/%s", s.addr, name), nil } // modURL returns a URL that can be used to modify the given storage file. func (s *localStorage) modURL(name string) (string, error) { if s.authkey == "" { return s.URL(name) } s.httpsBaseURLOnce.Do(func() { s.httpsBaseURL, s.httpsBaseURLError = s.getHTTPSBaseURL() }) if s.httpsBaseURLError != nil { return "", s.httpsBaseURLError } v := url.Values{} v.Set("authkey", s.authkey) return fmt.Sprintf("%s%s?%s", s.httpsBaseURL, name, v.Encode()), nil } // ConsistencyStrategy is specified in the StorageReader interface. func (s *localStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { return utils.AttemptStrategy{} } // ShouldRetry is specified in the StorageReader interface. func (s *localStorage) ShouldRetry(err error) bool { return false } // Put reads from r and writes to the given storage file. // The length must be set to the total length of the file. func (s *localStorage) Put(name string, r io.Reader, length int64) error { logger.Debugf("putting %q (len %d) to storage", name, length) url, err := s.modURL(name) if err != nil { return err } // Here we wrap up the reader. For some freaky unexplainable reason, the // http library will call Close on the reader if it has a Close method // available. Since we sometimes reuse the reader, especially when // putting tools, we don't want Close called. So we wrap the reader in a // struct so the Close method is not exposed. justReader := struct{ io.Reader }{r} req, err := http.NewRequest("PUT", url, justReader) if err != nil { return err } req.Header.Set("Content-Type", "application/octet-stream") req.ContentLength = length resp, err := s.client.Do(req) if err != nil { return err } if resp.StatusCode != 201 { return fmt.Errorf("%d %s", resp.StatusCode, resp.Status) } return nil } // Remove removes the given file from the environment's // storage. It should not return an error if the file does // not exist. func (s *localStorage) Remove(name string) error { url, err := s.modURL(name) if err != nil { return err } req, err := http.NewRequest("DELETE", url, nil) if err != nil { return err } resp, err := s.client.Do(req) if err != nil { return err } if resp.StatusCode != http.StatusOK { return fmt.Errorf("%d %s", resp.StatusCode, resp.Status) } return nil } func (s *localStorage) RemoveAll() error { return storage.RemoveAll(s) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/httpstorage/storage_test.go�������������������0000644�0000153�0000161�00000014030�12321735642�030124� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package httpstorage_test import ( "bytes" "fmt" "io" "io/ioutil" "net/http" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/httpstorage" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" coretesting "launchpad.net/juju-core/testing" ) type storageSuite struct{} var _ = gc.Suite(&storageSuite{}) func (s *storageSuite) TestClientTLS(c *gc.C) { listener, _, storageDir := startServerTLS(c) defer listener.Close() stor, err := httpstorage.ClientTLS(listener.Addr().String(), []byte(coretesting.CACert), testAuthkey) c.Assert(err, gc.IsNil) data := []byte("hello") err = ioutil.WriteFile(filepath.Join(storageDir, "filename"), data, 0644) c.Assert(err, gc.IsNil) names, err := storage.List(stor, "filename") c.Assert(err, gc.IsNil) c.Assert(names, gc.DeepEquals, []string{"filename"}) checkFileHasContents(c, stor, "filename", data) // Put, Remove and RemoveAll should all succeed. checkPutFile(c, stor, "filenamethesecond", data) checkFileHasContents(c, stor, "filenamethesecond", data) c.Assert(stor.Remove("filenamethesecond"), gc.IsNil) c.Assert(stor.RemoveAll(), gc.IsNil) } func (s *storageSuite) TestClientTLSInvalidAuth(c *gc.C) { listener, _, storageDir := startServerTLS(c) defer listener.Close() const invalidAuthkey = testAuthkey + "!" stor, err := httpstorage.ClientTLS(listener.Addr().String(), []byte(coretesting.CACert), invalidAuthkey) c.Assert(err, gc.IsNil) // Get and List should succeed. data := []byte("hello") err = ioutil.WriteFile(filepath.Join(storageDir, "filename"), data, 0644) c.Assert(err, gc.IsNil) names, err := storage.List(stor, "filename") c.Assert(err, gc.IsNil) c.Assert(names, gc.DeepEquals, []string{"filename"}) checkFileHasContents(c, stor, "filename", data) // Put, Remove and RemoveAll should all fail. const authErrorPattern = ".*401 Unauthorized" err = putFile(c, stor, "filenamethesecond", data) c.Assert(err, gc.ErrorMatches, authErrorPattern) c.Assert(stor.Remove("filenamethesecond"), gc.ErrorMatches, authErrorPattern) c.Assert(stor.RemoveAll(), gc.ErrorMatches, authErrorPattern) } func (s *storageSuite) TestList(c *gc.C) { listener, _, _ := startServer(c) defer listener.Close() stor := httpstorage.Client(listener.Addr().String()) names, err := storage.List(stor, "a/b/c") c.Assert(err, gc.IsNil) c.Assert(names, gc.HasLen, 0) } // TestPersistence tests the adding, reading, listing and removing // of files from the local storage. func (s *storageSuite) TestPersistence(c *gc.C) { listener, _, _ := startServer(c) defer listener.Close() stor := httpstorage.Client(listener.Addr().String()) names := []string{ "aa", "zzz/aa", "zzz/bb", } for _, name := range names { checkFileDoesNotExist(c, stor, name) checkPutFile(c, stor, name, []byte(name)) } checkList(c, stor, "", names) checkList(c, stor, "a", []string{"aa"}) checkList(c, stor, "zzz/", []string{"zzz/aa", "zzz/bb"}) storage2 := httpstorage.Client(listener.Addr().String()) for _, name := range names { checkFileHasContents(c, storage2, name, []byte(name)) } // remove the first file and check that the others remain. err := storage2.Remove(names[0]) c.Check(err, gc.IsNil) // check that it's ok to remove a file twice. err = storage2.Remove(names[0]) c.Check(err, gc.IsNil) // ... and check it's been removed in the other environment checkFileDoesNotExist(c, stor, names[0]) // ... and that the rest of the files are still around checkList(c, storage2, "", names[1:]) for _, name := range names[1:] { err := storage2.Remove(name) c.Assert(err, gc.IsNil) } // check they've all gone checkList(c, storage2, "", nil) // Check that RemoveAll works. checkRemoveAll(c, storage2) } func checkList(c *gc.C, stor storage.StorageReader, prefix string, names []string) { lnames, err := storage.List(stor, prefix) c.Assert(err, gc.IsNil) c.Assert(lnames, gc.DeepEquals, names) } type readerWithClose struct { *bytes.Buffer closeCalled bool } var _ io.Reader = (*readerWithClose)(nil) var _ io.Closer = (*readerWithClose)(nil) func (r *readerWithClose) Close() error { r.closeCalled = true return nil } func putFile(c *gc.C, stor storage.StorageWriter, name string, contents []byte) error { c.Logf("check putting file %s ...", name) reader := &readerWithClose{bytes.NewBuffer(contents), false} err := stor.Put(name, reader, int64(len(contents))) c.Assert(reader.closeCalled, jc.IsFalse) return err } func checkPutFile(c *gc.C, stor storage.StorageWriter, name string, contents []byte) { err := putFile(c, stor, name, contents) c.Assert(err, gc.IsNil) } func checkFileDoesNotExist(c *gc.C, stor storage.StorageReader, name string) { r, err := storage.Get(stor, name) c.Assert(r, gc.IsNil) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func checkFileHasContents(c *gc.C, stor storage.StorageReader, name string, contents []byte) { r, err := storage.Get(stor, name) c.Assert(err, gc.IsNil) c.Check(r, gc.NotNil) defer r.Close() data, err := ioutil.ReadAll(r) c.Check(err, gc.IsNil) c.Check(data, gc.DeepEquals, contents) url, err := stor.URL(name) c.Assert(err, gc.IsNil) resp, err := http.Get(url) c.Assert(err, gc.IsNil) data, err = ioutil.ReadAll(resp.Body) c.Assert(err, gc.IsNil) defer resp.Body.Close() c.Assert(resp.StatusCode, gc.Equals, http.StatusOK, gc.Commentf("error response: %s", data)) c.Check(data, gc.DeepEquals, contents) } func checkRemoveAll(c *gc.C, stor storage.Storage) { contents := []byte("File contents.") aFile := "a-file.txt" err := stor.Put(aFile, bytes.NewBuffer(contents), int64(len(contents))) c.Assert(err, gc.IsNil) err = stor.Put("empty-file", bytes.NewBuffer(nil), 0) c.Assert(err, gc.IsNil) err = stor.RemoveAll() c.Assert(err, gc.IsNil) files, err := storage.List(stor, "") c.Assert(err, gc.IsNil) c.Check(files, gc.HasLen, 0) _, err = storage.Get(stor, aFile) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, fmt.Sprintf("file %q not found", aFile)) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/httpstorage/backend_test.go�������������������0000644�0000153�0000161�00000027606�12321735642�030064� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package httpstorage_test import ( "bytes" "crypto/tls" "crypto/x509" "fmt" "io/ioutil" "net" "net/http" "os" "path/filepath" "strings" stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/httpstorage" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) const testAuthkey = "jabberwocky" func TestLocal(t *stdtesting.T) { gc.TestingT(t) } type backendSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&backendSuite{}) // startServer starts a new local storage server // using a temporary directory and returns the listener, // a base URL for the server and the directory path. func startServer(c *gc.C) (listener net.Listener, url, dataDir string) { dataDir = c.MkDir() embedded, err := filestorage.NewFileStorageWriter(dataDir) c.Assert(err, gc.IsNil) listener, err = httpstorage.Serve("localhost:0", embedded) c.Assert(err, gc.IsNil) return listener, fmt.Sprintf("http://%s/", listener.Addr()), dataDir } // startServerTLS starts a new TLS-based local storage server // using a temporary directory and returns the listener, // a base URL for the server and the directory path. func startServerTLS(c *gc.C) (listener net.Listener, url, dataDir string) { dataDir = c.MkDir() embedded, err := filestorage.NewFileStorageWriter(dataDir) c.Assert(err, gc.IsNil) hostnames := []string{"127.0.0.1"} caCertPEM := []byte(coretesting.CACert) caKeyPEM := []byte(coretesting.CAKey) listener, err = httpstorage.ServeTLS("127.0.0.1:0", embedded, caCertPEM, caKeyPEM, hostnames, testAuthkey) c.Assert(err, gc.IsNil) return listener, fmt.Sprintf("http://localhost:%d/", listener.Addr().(*net.TCPAddr).Port), dataDir } type testCase struct { name string content string found []string status int } var getTests = []testCase{ { // Get existing file. name: "foo", content: "this is file 'foo'", }, { // Get existing file. name: "bar", content: "this is file 'bar'", }, { // Get existing file. name: "baz", content: "this is file 'baz'", }, { // Get existing file. name: "yadda", content: "this is file 'yadda'", }, { // Get existing file from nested directory. name: "inner/fooin", content: "this is inner file 'fooin'", }, { // Get existing file from nested directory. name: "inner/barin", content: "this is inner file 'barin'", }, { // Get non-existing file. name: "dummy", status: 404, }, { // Get non-existing file from nested directory. name: "inner/dummy", status: 404, }, { // Get with a relative path ".." based on the // root is passed without invoking the handler // function. name: "../dummy", status: 404, }, { // Get with a relative path ".." based on the // root is passed without invoking the handler // function. name: "../foo", content: "this is file 'foo'", }, { // Get on a directory returns a 404 as it is // not a file. name: "inner", status: 404, }, } func (s *backendSuite) TestHeadNonAuth(c *gc.C) { // HEAD is unsupported for non-authenticating servers. listener, url, _ := startServer(c) defer listener.Close() resp, err := http.Head(url) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusMethodNotAllowed) } func (s *backendSuite) TestHeadAuth(c *gc.C) { // HEAD on an authenticating server will return the HTTPS counterpart URL. client, url, datadir := s.tlsServerAndClient(c) createTestData(c, datadir) resp, err := client.Head(url) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) location, err := resp.Location() c.Assert(err, gc.IsNil) c.Assert(location.String(), gc.Matches, "https://localhost:[0-9]{5}/") testGet(c, client, location.String()) } func (s *backendSuite) TestHeadCustomHost(c *gc.C) { // HEAD with a custom "Host:" header; the server should respond // with a Location with the specified Host header. client, url, _ := s.tlsServerAndClient(c) req, err := http.NewRequest("HEAD", url+"arbitrary", nil) c.Assert(err, gc.IsNil) req.Host = "notarealhost" resp, err := client.Do(req) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) location, err := resp.Location() c.Assert(err, gc.IsNil) c.Assert(location.String(), gc.Matches, "https://notarealhost:[0-9]{5}/arbitrary") } func (s *backendSuite) TestGet(c *gc.C) { // Test retrieving a file from a storage. listener, url, dataDir := startServer(c) defer listener.Close() createTestData(c, dataDir) testGet(c, http.DefaultClient, url) } func testGet(c *gc.C, client *http.Client, url string) { check := func(tc testCase) { resp, err := client.Get(url + tc.name) c.Assert(err, gc.IsNil) if tc.status != 0 { c.Assert(resp.StatusCode, gc.Equals, tc.status) return } else { c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) } defer resp.Body.Close() var buf bytes.Buffer _, err = buf.ReadFrom(resp.Body) c.Assert(err, gc.IsNil) c.Assert(buf.String(), gc.Equals, tc.content) } for _, tc := range getTests { check(tc) } } var listTests = []testCase{ { // List with a full filename. name: "foo", found: []string{"foo"}, }, { // List with a name matching two files. name: "ba", found: []string{"bar", "baz"}, }, { // List the contents of a directory. name: "inner/", found: []string{"inner/barin", "inner/bazin", "inner/fooin"}, }, { // List with a name matching two files in // a directory. name: "inner/ba", found: []string{"inner/barin", "inner/bazin"}, }, { // List with no name also lists the contents of all // directories. name: "", found: []string{"bar", "baz", "foo", "inner/barin", "inner/bazin", "inner/fooin", "yadda"}, }, { // List with a non-matching name returns an empty // body which is evaluated to a slice with an empty // string in the test (simplification). name: "zzz", found: []string{""}, }, { // List with a relative path ".." based on the // root is passed without invoking the handler // function. So returns the contents of all // directories. name: "../", found: []string{"bar", "baz", "foo", "inner/barin", "inner/bazin", "inner/fooin", "yadda"}, }, } func (s *backendSuite) TestList(c *gc.C) { // Test listing file of a storage. listener, url, dataDir := startServer(c) defer listener.Close() createTestData(c, dataDir) testList(c, http.DefaultClient, url) } func testList(c *gc.C, client *http.Client, url string) { check := func(tc testCase) { resp, err := client.Get(url + tc.name + "*") c.Assert(err, gc.IsNil) if tc.status != 0 { c.Assert(resp.StatusCode, gc.Equals, tc.status) return } defer resp.Body.Close() var buf bytes.Buffer _, err = buf.ReadFrom(resp.Body) c.Assert(err, gc.IsNil) names := strings.Split(buf.String(), "\n") c.Assert(names, gc.DeepEquals, tc.found) } for i, tc := range listTests { c.Logf("test %d", i) check(tc) } } var putTests = []testCase{ { // Put a file in the root directory. name: "porterhouse", content: "this is the sent file 'porterhouse'", }, { // Put a file with a relative path ".." is resolved // a redirect 301 by the Go HTTP daemon. The handler // isn't aware of it. name: "../no-way", status: 301, }, { // Put a file in a nested directory. name: "deep/cambridge", content: "this is the sent file 'deep/cambridge'", }, } func (s *backendSuite) TestPut(c *gc.C) { // Test sending a file to the storage. listener, url, dataDir := startServer(c) defer listener.Close() createTestData(c, dataDir) testPut(c, http.DefaultClient, url, dataDir, true) } func testPut(c *gc.C, client *http.Client, url, dataDir string, authorized bool) { check := func(tc testCase) { req, err := http.NewRequest("PUT", url+tc.name, bytes.NewBufferString(tc.content)) c.Assert(err, gc.IsNil) req.Header.Set("Content-Type", "application/octet-stream") resp, err := client.Do(req) c.Assert(err, gc.IsNil) if tc.status != 0 { c.Assert(resp.StatusCode, gc.Equals, tc.status) return } else if !authorized { c.Assert(resp.StatusCode, gc.Equals, http.StatusUnauthorized) return } c.Assert(resp.StatusCode, gc.Equals, http.StatusCreated) fp := filepath.Join(dataDir, tc.name) b, err := ioutil.ReadFile(fp) c.Assert(err, gc.IsNil) c.Assert(string(b), gc.Equals, tc.content) } for _, tc := range putTests { check(tc) } } var removeTests = []testCase{ { // Delete a file in the root directory. name: "fox", content: "the quick brown fox jumps over the lazy dog", }, { // Delete a file in a nested directory. name: "quick/brown/fox", content: "the quick brown fox jumps over the lazy dog", }, { // Delete a non-existing file leads to no error. name: "dog", }, { // Delete a file with a relative path ".." is resolved // a redirect 301 by the Go HTTP daemon. The handler // doesn't get aware of it. name: "../something", status: 301, }, } func (s *backendSuite) TestRemove(c *gc.C) { // Test removing a file in the storage. listener, url, dataDir := startServer(c) defer listener.Close() createTestData(c, dataDir) testRemove(c, http.DefaultClient, url, dataDir, true) } func testRemove(c *gc.C, client *http.Client, url, dataDir string, authorized bool) { check := func(tc testCase) { fp := filepath.Join(dataDir, tc.name) dir, _ := filepath.Split(fp) err := os.MkdirAll(dir, 0777) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(fp, []byte(tc.content), 0644) c.Assert(err, gc.IsNil) req, err := http.NewRequest("DELETE", url+tc.name, nil) c.Assert(err, gc.IsNil) resp, err := client.Do(req) c.Assert(err, gc.IsNil) if tc.status != 0 { c.Assert(resp.StatusCode, gc.Equals, tc.status) return } else if !authorized { c.Assert(resp.StatusCode, gc.Equals, http.StatusUnauthorized) return } c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) _, err = os.Stat(fp) c.Assert(os.IsNotExist(err), gc.Equals, true) } for i, tc := range removeTests { c.Logf("test %d", i) check(tc) } } func createTestData(c *gc.C, dataDir string) { writeData := func(dir, name, data string) { fn := filepath.Join(dir, name) c.Logf("writing data to %q", fn) err := ioutil.WriteFile(fn, []byte(data), 0644) c.Assert(err, gc.IsNil) } writeData(dataDir, "foo", "this is file 'foo'") writeData(dataDir, "bar", "this is file 'bar'") writeData(dataDir, "baz", "this is file 'baz'") writeData(dataDir, "yadda", "this is file 'yadda'") innerDir := filepath.Join(dataDir, "inner") err := os.MkdirAll(innerDir, 0777) c.Assert(err, gc.IsNil) writeData(innerDir, "fooin", "this is inner file 'fooin'") writeData(innerDir, "barin", "this is inner file 'barin'") writeData(innerDir, "bazin", "this is inner file 'bazin'") } func (b *backendSuite) tlsServerAndClient(c *gc.C) (client *http.Client, url, dataDir string) { listener, url, dataDir := startServerTLS(c) b.AddCleanup(func(*gc.C) { listener.Close() }) caCerts := x509.NewCertPool() c.Assert(caCerts.AppendCertsFromPEM([]byte(coretesting.CACert)), jc.IsTrue) client = &http.Client{ Transport: utils.NewHttpTLSTransport(&tls.Config{RootCAs: caCerts}), } return client, url, dataDir } func (b *backendSuite) TestTLSUnauthenticatedGet(c *gc.C) { client, url, dataDir := b.tlsServerAndClient(c) createTestData(c, dataDir) testGet(c, client, url) } func (b *backendSuite) TestTLSUnauthenticatedList(c *gc.C) { client, url, dataDir := b.tlsServerAndClient(c) createTestData(c, dataDir) testList(c, client, url) } func (b *backendSuite) TestTLSUnauthenticatedPut(c *gc.C) { client, url, dataDir := b.tlsServerAndClient(c) createTestData(c, dataDir) testPut(c, client, url, dataDir, false) } func (b *backendSuite) TestTLSUnauthenticatedRemove(c *gc.C) { client, url, dataDir := b.tlsServerAndClient(c) createTestData(c, dataDir) testRemove(c, client, url, dataDir, false) } ��������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/open.go���������������������������������������0000644�0000153�0000161�00000024234�12321735642�024025� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs import ( "fmt" "io/ioutil" "strings" "time" "github.com/errgo/errgo" "launchpad.net/juju-core/cert" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" ) // File named `VerificationFilename` in the storage will contain // `verificationContent`. This is also used to differentiate between // Python Juju and juju-core environments, so change the content with // care (and update CheckEnvironment below when you do that). const ( VerificationFilename = "bootstrap-verify" verificationContent = "juju-core storage writing verified: ok\n" ) var ( VerifyStorageError error = fmt.Errorf( "provider storage is not writable") InvalidEnvironmentError = fmt.Errorf( "environment is not a juju-core environment") ) // ConfigSource represents where some configuration data // has come from. // TODO(rog) remove this when we don't have to support // old environments with no configstore info. See lp#1235217 type ConfigSource int const ( ConfigFromNowhere ConfigSource = iota ConfigFromInfo ConfigFromEnvirons ) // EmptyConfig indicates the .jenv file is empty. type EmptyConfig struct { error } // IsEmptyConfig reports whether err is a EmptyConfig. func IsEmptyConfig(err error) bool { _, ok := err.(EmptyConfig) return ok } // ConfigForName returns the configuration for the environment with // the given name from the default environments file. If the name is // blank, the default environment will be used. If the configuration // is not found, an errors.NotFoundError is returned. If the given // store contains an entry for the environment and it has associated // bootstrap config, that configuration will be returned. // ConfigForName also returns where the configuration was sourced from // (this is also valid even when there is an error. func ConfigForName(name string, store configstore.Storage) (*config.Config, ConfigSource, error) { envs, err := ReadEnvirons("") if err != nil { return nil, ConfigFromNowhere, err } if name == "" { name = envs.Default } // TODO(rog) 2013-10-04 https://bugs.launchpad.net/juju-core/+bug/1235217 // Don't fall back to reading from environments.yaml // when we can be sure that everyone has a // .jenv file for their currently bootstrapped environments. if name != "" { info, err := store.ReadInfo(name) if err == nil { if len(info.BootstrapConfig()) == 0 { return nil, ConfigFromNowhere, EmptyConfig{fmt.Errorf("environment has no bootstrap configuration data")} } logger.Debugf("ConfigForName found bootstrap config %#v", info.BootstrapConfig()) cfg, err := config.New(config.NoDefaults, info.BootstrapConfig()) return cfg, ConfigFromInfo, err } if err != nil && !errors.IsNotFoundError(err) { return nil, ConfigFromInfo, fmt.Errorf("cannot read environment info for %q: %v", name, err) } } cfg, err := envs.Config(name) return cfg, ConfigFromEnvirons, err } // maybeNotBootstrapped takes an error and source, returned by // ConfigForName and returns ErrNotBootstrapped if it looks like the // environment is not bootstrapped, or err as-is otherwise. func maybeNotBootstrapped(err error, source ConfigSource) error { if err != nil && source == ConfigFromEnvirons { return ErrNotBootstrapped } return err } // NewFromName opens the environment with the given // name from the default environments file. If the // name is blank, the default environment will be used. // If the given store contains an entry for the environment // and it has associated bootstrap config, that configuration // will be returned. func NewFromName(name string, store configstore.Storage) (Environ, error) { // If we get an error when reading from a legacy // environments.yaml entry, we pretend it didn't exist // because the error is likely to be because // configuration attributes don't exist which // will be filled in by Prepare. cfg, source, err := ConfigForName(name, store) if err := maybeNotBootstrapped(err, source); err != nil { return nil, err } if err != nil { return nil, err } env, err := New(cfg) if err := maybeNotBootstrapped(err, source); err != nil { return nil, err } return env, err } // PrepareFromName is the same as NewFromName except // that the environment is is prepared as well as opened, // and environment information is created using the // given store. If the environment is already prepared, // it behaves like NewFromName. func PrepareFromName(name string, ctx BootstrapContext, store configstore.Storage) (Environ, error) { cfg, _, err := ConfigForName(name, store) if err != nil { return nil, err } return Prepare(cfg, ctx, store) } // NewFromAttrs returns a new environment based on the provided configuration // attributes. // TODO(rog) remove this function - it's almost always wrong to use it. func NewFromAttrs(attrs map[string]interface{}) (Environ, error) { cfg, err := config.New(config.NoDefaults, attrs) if err != nil { return nil, err } return New(cfg) } // New returns a new environment based on the provided configuration. func New(config *config.Config) (Environ, error) { p, err := Provider(config.Type()) if err != nil { return nil, err } return p.Open(config) } // Prepare prepares a new environment based on the provided configuration. // If the environment is already prepared, it behaves like New. func Prepare(cfg *config.Config, ctx BootstrapContext, store configstore.Storage) (Environ, error) { p, err := Provider(cfg.Type()) if err != nil { return nil, err } info, err := store.CreateInfo(cfg.Name()) if err == configstore.ErrEnvironInfoAlreadyExists { logger.Infof("environment info already exists; using New not Prepare") info, err := store.ReadInfo(cfg.Name()) if err != nil { return nil, fmt.Errorf("error reading environment info %q: %v", cfg.Name(), err) } if !info.Initialized() { return nil, fmt.Errorf("found uninitialized environment info for %q; environment preparation probably in progress or interrupted", cfg.Name()) } if len(info.BootstrapConfig()) == 0 { return nil, fmt.Errorf("found environment info but no bootstrap config") } cfg, err = config.New(config.NoDefaults, info.BootstrapConfig()) if err != nil { return nil, fmt.Errorf("cannot parse bootstrap config: %v", err) } return New(cfg) } if err != nil { return nil, fmt.Errorf("cannot create new info for environment %q: %v", cfg.Name(), err) } env, err := prepare(ctx, cfg, info, p) if err != nil { if err := info.Destroy(); err != nil { logger.Warningf("cannot destroy newly created environment info: %v", err) } return nil, err } info.SetBootstrapConfig(env.Config().AllAttrs()) if err := info.Write(); err != nil { return nil, fmt.Errorf("cannot create environment info %q: %v", env.Config().Name(), err) } return env, nil } func prepare(ctx BootstrapContext, cfg *config.Config, info configstore.EnvironInfo, p EnvironProvider) (Environ, error) { cfg, err := ensureAdminSecret(cfg) if err != nil { return nil, fmt.Errorf("cannot generate admin-secret: %v", err) } cfg, err = ensureCertificate(cfg) if err != nil { return nil, fmt.Errorf("cannot ensure CA certificate: %v", err) } return p.Prepare(ctx, cfg) } // ensureAdminSecret returns a config with a non-empty admin-secret. func ensureAdminSecret(cfg *config.Config) (*config.Config, error) { if cfg.AdminSecret() != "" { return cfg, nil } return cfg.Apply(map[string]interface{}{ "admin-secret": randomKey(), }) } // ensureCertificate generates a new CA certificate and // attaches it to the given environment configuration, // unless the configuration already has one. func ensureCertificate(cfg *config.Config) (*config.Config, error) { _, hasCACert := cfg.CACert() _, hasCAKey := cfg.CAPrivateKey() if hasCACert && hasCAKey { return cfg, nil } if hasCACert && !hasCAKey { return nil, fmt.Errorf("environment configuration with a certificate but no CA private key") } caCert, caKey, err := cert.NewCA(cfg.Name(), time.Now().UTC().AddDate(10, 0, 0)) if err != nil { return nil, err } return cfg.Apply(map[string]interface{}{ "ca-cert": string(caCert), "ca-private-key": string(caKey), }) } // Destroy destroys the environment and, if successful, // its associated configuration data from the given store. func Destroy(env Environ, store configstore.Storage) error { name := env.Name() if err := env.Destroy(); err != nil { return err } return DestroyInfo(name, store) } // DestroyInfo destroys the configuration data for the named // environment from the given store. func DestroyInfo(envName string, store configstore.Storage) error { info, err := store.ReadInfo(envName) if err != nil { if errors.IsNotFoundError(err) { return nil } return err } if err := info.Destroy(); err != nil { return errgo.Annotate(err, "cannot destroy environment configuration information") } return nil } // VerifyStorage writes the bootstrap init file to the storage to indicate // that the storage is writable. func VerifyStorage(stor storage.Storage) error { reader := strings.NewReader(verificationContent) err := stor.Put(VerificationFilename, reader, int64(len(verificationContent))) if err != nil { logger.Warningf("failed to write bootstrap-verify file: %v", err) return VerifyStorageError } return nil } // CheckEnvironment checks if an environment has a bootstrap-verify // that is written by juju-core commands (as compared to one being // written by Python juju). // // If there is no bootstrap-verify file in the storage, it is still // considered to be a Juju-core environment since early versions have // not written it out. // // Returns InvalidEnvironmentError on failure, nil otherwise. func CheckEnvironment(environ Environ) error { stor := environ.Storage() reader, err := storage.Get(stor, VerificationFilename) if errors.IsNotFoundError(err) { // When verification file does not exist, this is a juju-core // environment. return nil } else if err != nil { return err } else if content, err := ioutil.ReadAll(reader); err != nil { return err } else if string(content) != verificationContent { return InvalidEnvironmentError } return nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/cloudinit_test.go�����������������������������0000644�0000153�0000161�00000017414�12321735776�026127� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs_test import ( "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/cert" coreCloudinit "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // dummySampleConfig returns the dummy sample config without // the state server configured. // will not run a state server. func dummySampleConfig() testing.Attrs { return dummy.SampleConfig().Merge(testing.Attrs{ "state-server": false, }) } type CloudInitSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&CloudInitSuite{}) func (s *CloudInitSuite) TestFinishInstanceConfig(c *gc.C) { attrs := dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) mcfg := &cloudinit.MachineConfig{ StateInfo: &state.Info{Tag: "not touched"}, APIInfo: &api.Info{Tag: "not touched"}, } err = environs.FinishMachineConfig(mcfg, cfg, constraints.Value{}) c.Assert(err, gc.IsNil) c.Assert(mcfg, gc.DeepEquals, &cloudinit.MachineConfig{ AuthorizedKeys: "we-are-the-keys", AgentEnvironment: map[string]string{ agent.ProviderType: "dummy", agent.ContainerType: "", }, StateInfo: &state.Info{Tag: "not touched"}, APIInfo: &api.Info{Tag: "not touched"}, DisableSSLHostnameVerification: false, }) } func (s *CloudInitSuite) TestFinishMachineConfigNonDefault(c *gc.C) { attrs := dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", "ssl-hostname-verification": false, }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) mcfg := &cloudinit.MachineConfig{ StateInfo: &state.Info{Tag: "not touched"}, APIInfo: &api.Info{Tag: "not touched"}, } err = environs.FinishMachineConfig(mcfg, cfg, constraints.Value{}) c.Assert(err, gc.IsNil) c.Assert(mcfg, gc.DeepEquals, &cloudinit.MachineConfig{ AuthorizedKeys: "we-are-the-keys", AgentEnvironment: map[string]string{ agent.ProviderType: "dummy", agent.ContainerType: "", }, StateInfo: &state.Info{Tag: "not touched"}, APIInfo: &api.Info{Tag: "not touched"}, DisableSSLHostnameVerification: true, }) } func (s *CloudInitSuite) TestFinishBootstrapConfig(c *gc.C) { attrs := dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", "admin-secret": "lisboan-pork", "agent-version": "1.2.3", "state-server": false, }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) oldAttrs := cfg.AllAttrs() mcfg := &cloudinit.MachineConfig{ StateServer: true, } cons := constraints.MustParse("mem=1T cpu-power=999999999") err = environs.FinishMachineConfig(mcfg, cfg, cons) c.Assert(err, gc.IsNil) c.Check(mcfg.AuthorizedKeys, gc.Equals, "we-are-the-keys") c.Check(mcfg.DisableSSLHostnameVerification, jc.IsFalse) password := utils.UserPasswordHash("lisboan-pork", utils.CompatSalt) c.Check(mcfg.APIInfo, gc.DeepEquals, &api.Info{ Password: password, CACert: []byte(testing.CACert), }) c.Check(mcfg.StateInfo, gc.DeepEquals, &state.Info{ Password: password, CACert: []byte(testing.CACert), }) c.Check(mcfg.StatePort, gc.Equals, cfg.StatePort()) c.Check(mcfg.APIPort, gc.Equals, cfg.APIPort()) c.Check(mcfg.Constraints, gc.DeepEquals, cons) oldAttrs["ca-private-key"] = "" oldAttrs["admin-secret"] = "" c.Check(mcfg.Config.AllAttrs(), gc.DeepEquals, oldAttrs) srvCertPEM := mcfg.StateServerCert srvKeyPEM := mcfg.StateServerKey _, _, err = cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) c.Check(err, gc.IsNil) err = cert.Verify(srvCertPEM, []byte(testing.CACert), time.Now()) c.Assert(err, gc.IsNil) err = cert.Verify(srvCertPEM, []byte(testing.CACert), time.Now().AddDate(9, 0, 0)) c.Assert(err, gc.IsNil) err = cert.Verify(srvCertPEM, []byte(testing.CACert), time.Now().AddDate(10, 0, 1)) c.Assert(err, gc.NotNil) } func (s *CloudInitSuite) TestUserData(c *gc.C) { s.testUserData(c, false) } func (s *CloudInitSuite) TestStateServerUserData(c *gc.C) { s.testUserData(c, true) } func (*CloudInitSuite) testUserData(c *gc.C, stateServer bool) { testJujuHome := c.MkDir() defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome)) tools := &tools.Tools{ URL: "http://foo.com/tools/releases/juju1.2.3-linux-amd64.tgz", Version: version.MustParseBinary("1.2.3-linux-amd64"), } envConfig, err := config.New(config.NoDefaults, dummySampleConfig()) c.Assert(err, gc.IsNil) allJobs := []params.MachineJob{ params.JobManageEnviron, params.JobHostUnits, } cfg := &cloudinit.MachineConfig{ MachineId: "10", MachineNonce: "5432", Tools: tools, StateServerCert: []byte(testing.ServerCert), StateServerKey: []byte(testing.ServerKey), StateInfo: &state.Info{ Addrs: []string{"127.0.0.1:1234"}, Password: "pw1", CACert: []byte("CA CERT\n" + testing.CACert), Tag: "machine-10", }, APIInfo: &api.Info{ Addrs: []string{"127.0.0.1:1234"}, Password: "pw2", CACert: []byte("CA CERT\n" + testing.CACert), Tag: "machine-10", }, DataDir: environs.DataDir, LogDir: agent.DefaultLogDir, Jobs: allJobs, CloudInitOutputLog: environs.CloudInitOutputLog, Config: envConfig, StatePort: envConfig.StatePort(), APIPort: envConfig.APIPort(), StateServer: stateServer, AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, AuthorizedKeys: "wheredidileavemykeys", MachineAgentServiceName: "jujud-machine-10", } script1 := "script1" script2 := "script2" cloudcfg := coreCloudinit.New() cloudcfg.AddRunCmd(script1) cloudcfg.AddRunCmd(script2) result, err := environs.ComposeUserData(cfg, cloudcfg) c.Assert(err, gc.IsNil) unzipped, err := utils.Gunzip(result) c.Assert(err, gc.IsNil) config := make(map[interface{}]interface{}) err = goyaml.Unmarshal(unzipped, &config) c.Assert(err, gc.IsNil) // The scripts given to userData where added as the first // commands to be run. runCmd := config["runcmd"].([]interface{}) c.Check(runCmd[0], gc.Equals, script1) c.Check(runCmd[1], gc.Equals, script2) if stateServer { // The cloudinit config should have nothing but the basics: // SSH authorized keys, the additional runcmds, and log output. // // Note: the additional runcmds *do* belong here, at least // for MAAS. MAAS needs to configure and then bounce the // network interfaces, which would sever the SSH connection // in the synchronous bootstrap phase. c.Check(config, gc.DeepEquals, map[interface{}]interface{}{ "output": map[interface{}]interface{}{ "all": "| tee -a /var/log/cloud-init-output.log", }, "runcmd": []interface{}{ "script1", "script2", "set -xe", "install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'", "printf '%s\\n' '5432' > '/var/lib/juju/nonce.txt'", }, "ssh_authorized_keys": []interface{}{"wheredidileavemykeys"}, }) } else { // Just check that the cloudinit config looks good, // and that there are more runcmds than the additional // ones we passed into ComposeUserData. c.Check(config["apt_upgrade"], gc.Equals, true) c.Check(len(runCmd) > 2, jc.IsTrue) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023655� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/build.go��������������������������������0000644�0000153�0000161�00000014253�12321735776�025333� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "archive/tar" "compress/gzip" "crypto/sha256" "fmt" "io" "io/ioutil" "os" "os/exec" "path/filepath" "strings" "launchpad.net/juju-core/version" ) // archive writes the executable files found in the given directory in // gzipped tar format to w, returning the SHA256 hash of the resulting file. // An error is returned if an entry inside dir is not a regular executable file. func archive(w io.Writer, dir string) (string, error) { entries, err := ioutil.ReadDir(dir) if err != nil { return "", err } gzw := gzip.NewWriter(w) defer closeErrorCheck(&err, gzw) tarw := tar.NewWriter(gzw) defer closeErrorCheck(&err, tarw) sha256hash := sha256.New() for _, ent := range entries { h := tarHeader(ent) logger.Debugf("adding entry: %#v", h) // ignore local umask if isExecutable(ent) { h.Mode = 0755 } else { h.Mode = 0644 } err := tarw.WriteHeader(h) if err != nil { return "", err } fileName := filepath.Join(dir, ent.Name()) if err := copyFile(tarw, fileName); err != nil { return "", err } if err := copyFile(sha256hash, fileName); err != nil { return "", err } } return fmt.Sprintf("%x", sha256hash.Sum(nil)), nil } // copyFile writes the contents of the given file to w. func copyFile(w io.Writer, file string) error { f, err := os.Open(file) if err != nil { return err } defer f.Close() _, err = io.Copy(w, f) return err } // tarHeader returns a tar file header given the file's stat // information. func tarHeader(i os.FileInfo) *tar.Header { return &tar.Header{ Typeflag: tar.TypeReg, Name: i.Name(), Size: i.Size(), Mode: int64(i.Mode() & 0777), ModTime: i.ModTime(), AccessTime: i.ModTime(), ChangeTime: i.ModTime(), Uname: "ubuntu", Gname: "ubuntu", } } // isExecutable returns whether the given info // represents a regular file executable by (at least) the user. func isExecutable(i os.FileInfo) bool { return i.Mode()&(0100|os.ModeType) == 0100 } // closeErrorCheck means that we can ensure that // Close errors do not get lost even when we defer them, func closeErrorCheck(errp *error, c io.Closer) { err := c.Close() if *errp == nil { *errp = err } } func setenv(env []string, val string) []string { prefix := val[0 : strings.Index(val, "=")+1] for i, eval := range env { if strings.HasPrefix(eval, prefix) { env[i] = val return env } } return append(env, val) } func findExecutable(execFile string) (string, error) { logger.Debugf("looking for: %s", execFile) if filepath.IsAbs(execFile) { return execFile, nil } dir, file := filepath.Split(execFile) // Now we have two possibilities: // file == path indicating that the PATH was searched // dir != "" indicating that it is a relative path if dir == "" { path := os.Getenv("PATH") for _, name := range filepath.SplitList(path) { result := filepath.Join(name, file) info, err := os.Stat(result) if err == nil { // Sanity check to see if executable. if info.Mode()&0111 != 0 { return result, nil } } } return "", fmt.Errorf("could not find %q in the path", file) } cwd, err := os.Getwd() if err != nil { return "", err } return filepath.Clean(filepath.Join(cwd, execFile)), nil } func copyExistingJujud(dir string) error { // Assume that the user is running juju. jujuLocation, err := findExecutable(os.Args[0]) if err != nil { logger.Infof("%v", err) return err } jujudLocation := filepath.Join(filepath.Dir(jujuLocation), "jujud") logger.Debugf("checking: %s", jujudLocation) info, err := os.Stat(jujudLocation) if err != nil { logger.Infof("couldn't find existing jujud") return err } logger.Infof("found existing jujud") // TODO(thumper): break this out into a util function. // copy the file into the dir. source, err := os.Open(jujudLocation) if err != nil { logger.Infof("open source failed: %v", err) return err } defer source.Close() target := filepath.Join(dir, "jujud") logger.Infof("target: %v", target) destination, err := os.OpenFile(target, os.O_RDWR|os.O_TRUNC|os.O_CREATE, info.Mode()) if err != nil { logger.Infof("open destination failed: %v", err) return err } defer destination.Close() _, err = io.Copy(destination, source) if err != nil { return err } return nil } func buildJujud(dir string) error { logger.Infof("building jujud") cmds := [][]string{ {"go", "install", "launchpad.net/juju-core/cmd/jujud"}, {"strip", dir + "/jujud"}, } env := setenv(os.Environ(), "GOBIN="+dir) for _, args := range cmds { cmd := exec.Command(args[0], args[1:]...) cmd.Env = env out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("build command %q failed: %v; %s", args[0], err, out) } } return nil } // BundleTools bundles all the current juju tools in gzipped tar // format to the given writer. // If forceVersion is not nil, a FORCE-VERSION file is included in // the tools bundle so it will lie about its current version number. func BundleTools(w io.Writer, forceVersion *version.Number) (tvers version.Binary, sha256Hash string, err error) { dir, err := ioutil.TempDir("", "juju-tools") if err != nil { return version.Binary{}, "", err } defer os.RemoveAll(dir) if err := copyExistingJujud(dir); err != nil { logger.Debugf("copy existing failed: %v", err) if err := buildJujud(dir); err != nil { return version.Binary{}, "", err } } if forceVersion != nil { logger.Debugf("forcing version to %s", forceVersion) if err := ioutil.WriteFile(filepath.Join(dir, "FORCE-VERSION"), []byte(forceVersion.String()), 0666); err != nil { return version.Binary{}, "", err } } cmd := exec.Command(filepath.Join(dir, "jujud"), "version") out, err := cmd.CombinedOutput() if err != nil { return version.Binary{}, "", fmt.Errorf("cannot get version from %q: %v; %s", cmd.Args[0], err, out) } tvs := strings.TrimSpace(string(out)) tvers, err = version.ParseBinary(tvs) if err != nil { return version.Binary{}, "", fmt.Errorf("invalid version %q printed by jujud", tvs) } sha256Hash, err = archive(w, dir) if err != nil { return version.Binary{}, "", err } return tvers, sha256Hash, err } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/validation.go���������������������������0000644�0000153�0000161�00000004257�12321735776�026371� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "fmt" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/version" ) // ToolsMetadataLookupParams is used to query metadata for matching tools. type ToolsMetadataLookupParams struct { simplestreams.MetadataLookupParams Version string Major int Minor int } // ValidateToolsMetadata attempts to load tools metadata for the specified cloud attributes and returns // any tools versions found, or an error if the metadata could not be loaded. func ValidateToolsMetadata(params *ToolsMetadataLookupParams) ([]string, *simplestreams.ResolveInfo, error) { if len(params.Architectures) == 0 { return nil, nil, fmt.Errorf("required parameter arches not specified") } if len(params.Sources) == 0 { return nil, nil, fmt.Errorf("required parameter sources not specified") } if params.Version == "" && params.Major == 0 { params.Version = version.Current.Number.String() } var toolsConstraint *ToolsConstraint if params.Version == "" { toolsConstraint = NewGeneralToolsConstraint(params.Major, params.Minor, false, simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{params.Region, params.Endpoint}, Series: []string{params.Series}, Arches: params.Architectures, }) } else { versNum, err := version.Parse(params.Version) if err != nil { return nil, nil, err } toolsConstraint = NewVersionedToolsConstraint(versNum, simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{params.Region, params.Endpoint}, Series: []string{params.Series}, Arches: params.Architectures, }) } matchingTools, resolveInfo, err := Fetch(params.Sources, simplestreams.DefaultIndexPath, toolsConstraint, false) if err != nil { return nil, resolveInfo, err } if len(matchingTools) == 0 { return nil, resolveInfo, fmt.Errorf("no matching tools found for constraint %+v", toolsConstraint) } versions := make([]string, len(matchingTools)) for i, tm := range matchingTools { vers := version.Binary{version.MustParse(tm.Version), tm.Release, tm.Arch} versions[i] = vers.String() } return versions, resolveInfo, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/export_test.go��������������������������0000644�0000153�0000161�00000000660�12321735642�026601� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools var Setenv = setenv var FindExecutable = findExecutable var CheckToolsSeries = checkToolsSeries // SetSigningPublicKey sets a new public key for testing and returns the original key. func SetSigningPublicKey(key string) string { oldKey := simplestreamsToolsPublicKey simplestreamsToolsPublicKey = key return oldKey } ��������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/simplestreams.go������������������������0000644�0000153�0000161�00000035116�12321735642�027115� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The tools package supports locating, parsing, and filtering Ubuntu tools metadata in simplestreams format. // See http://launchpad.net/simplestreams and in particular the doc/README file in that project for more information // about the file formats. package tools import ( "bytes" "crypto/sha256" "fmt" "hash" "io" "path" "sort" "strings" "time" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/version" ) func init() { simplestreams.RegisterStructTags(ToolsMetadata{}) } const ( ContentDownload = "content-download" ) // simplestreamsToolsPublicKey is the public key required to // authenticate the simple streams data on http://streams.canonical.com. // Declared as a var so it can be overidden for testing. var simplestreamsToolsPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.11 (GNU/Linux) mQINBFJN1n8BEAC1vt2w08Y4ztJrv3maOycMezBb7iUs6DLH8hOZoqRO9EW9558W 8CN6G4sVbC/nIhivvn/paw0gSicfYXGs5teCJL3ShrcsGkhTs+5q7UO2TVGAUPwb CFWCqPkCB/+CiQ/fnEAWV5c11KzMTBtQ2nfJFS8rEQfc2PJMKqd/Y+LDItOc5E5Y SseGT/60coyTZO0iE3mKv1osFjSJlUv/6f/ziHGgV+IowOtEeeaEz8H/oU4vHhyA THL/k9DSNb0I/+aI8R84OB7EqrQ/ck6B6+CTbwGwkQUBK6z/Isl3uq9MhGjsiPjy EfOJNTfa+knlQcedc3/2S/jTUBDxU+myga9gQ2jF4oEzb74LarpV4y1KXpsqyLwd 8/vpNG5rTLtjZ3ZTJu7EkAra6pNK/Uxj9guIkCIGIVS1SWtsR0mCY+6TOdfJu7bt qOcSWkp3gaYcnCid8ecZuD8KDcxJscdYBetxCV4TLVV5CwO4MMVkxcI3zL1ORzHS j0W+aYzdtycHu2w8ZQwQRuFB2y5zsxE69MOoS857FzwhRctPSiwIPWH+Qo2BkNAM K5fVc19z9kzgtRP1+rHgBox2w+hOSZiYf0vluaG7NPUsMfVOGBFTxn1W+rb3NL/m hUoDPl2e2zoViEsaT2p+ATwFDN0DlQLLQxsVIbxdL6cfMQASHmADOHA6dwARAQAB tEtKdWp1IFRvb2xzIChDYW5vbmljYWwgSnVqdSBUb29sIEJ1aWxkZXIpIDxqdWp1 LXRvb2xzLW5vcmVwbHlAY2Fub25pY2FsLmNvbT6JAjkEEwEKACMFAlJN1n8CGwMH CwkNCAwHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRA3j2KvahV9szBED/wOlDTMpevL bYyh+mFaeNBw/mwCdWqpwQkpIRLwxt0al1eV9KIVhu6CK1g1UMZ24H3gy5Btj5N5 ga02xgqfQRrP4Mqv2dYZOL5p8WFuZjbow9a+e89mqqFuW6/os57cFwZ7Z3imbBDa aWzuzdeWLEK7PfT6rpik6ZMIpI1LGywI93abaZX8v6ouwFeQovXcS0HKt906+ElI oWgSh8dL2hqZ71SR/74sehkEZSYfQRLa7RJCDvA/iInXeGRuyaheQ1iTrY606aBh +NyOgr4cG+7Sy3FIbqgBx0hxkY8LZv4L7l2IDDjgbTEGILpQ2tkykDnFY7QgEdE4 5TzPONg9zyk91NRHqjLIm9CFt8P3rcs+MBjaxv+S45RIHQEu+ewkr6BihnPPldkN eSIi4Z0OTTQfAI0oDkREVFnnOHfzZ8uafHXOnhUYsovZ3YrowoiNXOWRxeOvt5cL XE0Gyq7n8ESe9JOCg3AZcrDX12xWX+gaSgDaD66fI5xr+A3128BLpYQTMXOpe1n9 rfsiA8XBEFsB6+xMJBtSSPUsaWjes/aziI87fBv7FpEMagnWLqJ7xk2E2RR06B9t F+SoiLF3aQ0ZJFqKpDDYBO5kZkHIql0jVkuPEz5fxTOZjZE4irTZiSMdJ6xsm9AU axxW8e4pax116l4D2toMJPvXkA9lCZ3RIrkCDQRSTdZ/ARAA7SonLFZQrrLD93Jp GpgJnYha6rr3pdIm9wH5PnV9Ysgyt/aM9RVrMXzSjMRpxdV6qxK7Lbzh/V9QxpoI YvFIi4Yu5k0wDPSm/sowBtVI/X2WMSSvd3DUaigTFBQ1giIY3R46wqcY99RfUPJ1 VsHFZ0mZq5GuAPSv/Ky7r9SByMDtQk+Pt8jiOIiJ8eGgKy/W0Wau8ImNqSUyj+67 QeOCpEKTjS2gQypi6vgCtUCDfy4yHPxppARary/GDjVIAvwjdu/+0rshWcWUOwq8 ex2ddPYQf9dGmF9CesaFknpVnkXb9pbw+qBF/CSdk6Z/ApgtXFGwWszP5/Wqq2Pd ilM1C80WcZVhuwk+acYztk5P5hGw0XL2nDeNg08hcDy2NEL/hA9PM2DSFpoWy1aA Gjt/8ICPY3SNJlfJUhMIBOK0nmHIoHGU/tX7AiuwEKyP8Qh5kp8fYoO4c59WfeKq e6rbttt7IEywAlY6HiLMymqC/d0nPk0Cy5bujacH2y3ahAgCwNVvo+E77J7m7Ui2 vqzvpcW6Fla2EzbXus4nIgqEV/qX6fQXqItptKZFvZeznj0epRswkmFm7KLXD5p1 SzkmfAujy5xQJktZKvtTKRROnX5JdBB8RT83MIJr+U4FOT3UPQYc2V1O2k4PYF9G g5YZtNPTvdx8dvN7qwiO7R7xenkAEQEAAYkCHwQYAQoACQUCUk3WfwIbDAAKCRA3 j2KvahV9s4+SD/sEKOBs6YE2dhax0y/wx1AKJbkneVhxTjgCggY/rbnLm6w85xQl EgGycmdRq4JkBDhmzsevx+THNJicBwN9qP12Z14kM1pr7WWw9fOmshPQx5kJXYs+ FiK6f5vHXcNiTyvC8oOGquGrDoB7SACgTr+Lkm/dNfpRn0XsApUy6vQSqChAzqkJ qYZCIIbHTea1DIoNhVI+VTaJ1Z5IqMM9mi43RVYeq7yyBNLwhdjEIOX9qBK4Secn mFz94SCz+b5titGyFiBAJzPBP/NSwM6DP2OfRhsBC6K4xDELn8Dpucb9FHqaLG75 K3oDhTEUfTBiG3PRfc57974+V3KrkK71rMzWpQJ2IyMtxzl8qO4JYhLRSL0kMq8/ hYlXGcNwyUUtiDPOwvG44KDVgXbrnFTVqLU6nc9k/yPD1pfommaTAWrb2tTitkGf zOxHnpWTP48l+6qzfEM1PUKvx3U04BZe8JCaU+JVdy6O/rLjEVjYq/vBY6EGOxa2 C4Vs43YdFOXSa38ze0J4nFRGO8gOBP/EJyE8Nwqg7i+6VvkD+H2KbZVUXiWld+v/ vwtaXhWd7JS+v38YZ4CijEBe69VYHpSNIz87uhVKgdkFBhoOGtf9/NEO7NYwk7/N qsH+JQgcphKkC+JH0Dw7Q/0e16LClkPPa21NseVGUWzS0WmS+0egtDDutg== =hQAI -----END PGP PUBLIC KEY BLOCK----- ` // This needs to be a var so we can override it for testing. var DefaultBaseURL = "https://streams.canonical.com/juju/tools" // ToolsConstraint defines criteria used to find a tools metadata record. type ToolsConstraint struct { simplestreams.LookupParams Version version.Number MajorVersion int MinorVersion int Released bool } // NewVersionedToolsConstraint returns a ToolsConstraint for a tools with a specific version. func NewVersionedToolsConstraint(vers version.Number, params simplestreams.LookupParams) *ToolsConstraint { return &ToolsConstraint{LookupParams: params, Version: vers} } // NewGeneralToolsConstraint returns a ToolsConstraint for tools with matching major/minor version numbers. func NewGeneralToolsConstraint(majorVersion, minorVersion int, released bool, params simplestreams.LookupParams) *ToolsConstraint { return &ToolsConstraint{LookupParams: params, Version: version.Zero, MajorVersion: majorVersion, MinorVersion: minorVersion, Released: released} } // Ids generates a string array representing product ids formed similarly to an ISCSI qualified name (IQN). func (tc *ToolsConstraint) Ids() ([]string, error) { var allIds []string for _, series := range tc.Series { version, err := simplestreams.SeriesVersion(series) if err != nil { return nil, err } ids := make([]string, len(tc.Arches)) for i, arch := range tc.Arches { ids[i] = fmt.Sprintf("com.ubuntu.juju:%s:%s", version, arch) } allIds = append(allIds, ids...) } return allIds, nil } // ToolsMetadata holds information about a particular tools tarball. type ToolsMetadata struct { Release string `json:"release"` Version string `json:"version"` Arch string `json:"arch"` Size int64 `json:"size"` Path string `json:"path"` FullPath string `json:"-"` FileType string `json:"ftype"` SHA256 string `json:"sha256"` } func (t *ToolsMetadata) String() string { return fmt.Sprintf("%+v", *t) } // binary returns the tools metadata's binary version, // which may be used for map lookup. func (t *ToolsMetadata) binary() version.Binary { return version.Binary{ Number: version.MustParse(t.Version), Series: t.Release, Arch: t.Arch, } } func (t *ToolsMetadata) productId() (string, error) { seriesVersion, err := simplestreams.SeriesVersion(t.Release) if err != nil { return "", err } return fmt.Sprintf("com.ubuntu.juju:%s:%s", seriesVersion, t.Arch), nil } // Fetch returns a list of tools for the specified cloud matching the constraint. // The base URL locations are as specified - the first location which has a file is the one used. // Signed data is preferred, but if there is no signed data available and onlySigned is false, // then unsigned data is used. func Fetch( sources []simplestreams.DataSource, indexPath string, cons *ToolsConstraint, onlySigned bool) ([]*ToolsMetadata, *simplestreams.ResolveInfo, error) { params := simplestreams.ValueParams{ DataType: ContentDownload, FilterFunc: appendMatchingTools, MirrorContentId: ToolsContentId, ValueTemplate: ToolsMetadata{}, PublicKey: simplestreamsToolsPublicKey, } items, resolveInfo, err := simplestreams.GetMetadata(sources, indexPath, cons, onlySigned, params) if err != nil { return nil, nil, err } metadata := make([]*ToolsMetadata, len(items)) for i, md := range items { metadata[i] = md.(*ToolsMetadata) } // Sorting the metadata is not strictly necessary, but it ensures consistent ordering for // all compilers, and it just makes it easier to look at the data. Sort(metadata) return metadata, resolveInfo, nil } // Sort sorts a slice of ToolsMetadata in ascending order of their version // in order to ensure the results of Fetch are ordered deterministically. func Sort(metadata []*ToolsMetadata) { sort.Sort(byVersion(metadata)) } type byVersion []*ToolsMetadata func (b byVersion) Len() int { return len(b) } func (b byVersion) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b byVersion) Less(i, j int) bool { return b[i].binary().String() < b[j].binary().String() } // appendMatchingTools updates matchingTools with tools metadata records from tools which belong to the // specified series. If a tools record already exists in matchingTools, it is not overwritten. func appendMatchingTools(source simplestreams.DataSource, matchingTools []interface{}, tools map[string]interface{}, cons simplestreams.LookupConstraint) []interface{} { toolsMap := make(map[version.Binary]*ToolsMetadata, len(matchingTools)) for _, val := range matchingTools { tm := val.(*ToolsMetadata) toolsMap[tm.binary()] = tm } for _, val := range tools { tm := val.(*ToolsMetadata) if !set.NewStrings(cons.Params().Series...).Contains(tm.Release) { continue } if toolsConstraint, ok := cons.(*ToolsConstraint); ok { tmNumber := version.MustParse(tm.Version) if toolsConstraint.Version == version.Zero { if toolsConstraint.Released && tmNumber.IsDev() { continue } if toolsConstraint.MajorVersion >= 0 && toolsConstraint.MajorVersion != tmNumber.Major { continue } if toolsConstraint.MinorVersion >= 0 && toolsConstraint.MinorVersion != tmNumber.Minor { continue } } else { if toolsConstraint.Version != tmNumber { continue } } } if _, ok := toolsMap[tm.binary()]; !ok { tm.FullPath, _ = source.URL(tm.Path) matchingTools = append(matchingTools, tm) } } return matchingTools } type MetadataFile struct { Path string Data []byte } // MetadataFromTools returns a tools metadata list derived from the // given tools list. The size and sha256 will not be computed if // missing. func MetadataFromTools(toolsList coretools.List) []*ToolsMetadata { metadata := make([]*ToolsMetadata, len(toolsList)) for i, t := range toolsList { path := fmt.Sprintf("releases/juju-%s-%s-%s.tgz", t.Version.Number, t.Version.Series, t.Version.Arch) metadata[i] = &ToolsMetadata{ Release: t.Version.Series, Version: t.Version.Number.String(), Arch: t.Version.Arch, Path: path, FileType: "tar.gz", Size: t.Size, SHA256: t.SHA256, } } return metadata } // ResolveMetadata resolves incomplete metadata // by fetching the tools from storage and computing // the size and hash locally. func ResolveMetadata(stor storage.StorageReader, metadata []*ToolsMetadata) error { for _, md := range metadata { if md.Size != 0 { continue } binary := md.binary() logger.Infof("Fetching tools to generate hash: %v", binary) size, sha256hash, err := fetchToolsHash(stor, binary) if err != nil { return err } md.Size = size md.SHA256 = fmt.Sprintf("%x", sha256hash.Sum(nil)) } return nil } // MergeMetadata merges the given tools metadata. // If metadata for the same tools version exists in both lists, // an entry with non-empty size/SHA256 takes precedence; if // the two entries have different sizes/hashes, then an error is // returned. func MergeMetadata(tmlist1, tmlist2 []*ToolsMetadata) ([]*ToolsMetadata, error) { merged := make(map[version.Binary]*ToolsMetadata) for _, tm := range tmlist1 { merged[tm.binary()] = tm } for _, tm := range tmlist2 { binary := tm.binary() if existing, ok := merged[binary]; ok { if tm.Size != 0 { if existing.Size == 0 { merged[binary] = tm } else if existing.Size != tm.Size || existing.SHA256 != tm.SHA256 { return nil, fmt.Errorf( "metadata mismatch for %s: sizes=(%v,%v) sha256=(%v,%v)", binary.String(), existing.Size, tm.Size, existing.SHA256, tm.SHA256, ) } } } else { merged[binary] = tm } } list := make([]*ToolsMetadata, 0, len(merged)) for _, metadata := range merged { list = append(list, metadata) } Sort(list) return list, nil } // ReadMetadata returns the tools metadata from the given storage. func ReadMetadata(store storage.StorageReader) ([]*ToolsMetadata, error) { dataSource := storage.NewStorageSimpleStreamsDataSource("existing metadata", store, storage.BaseToolsPath) toolsConstraint, err := makeToolsConstraint(simplestreams.CloudSpec{}, -1, -1, coretools.Filter{}) if err != nil { return nil, err } metadata, _, err := Fetch( []simplestreams.DataSource{dataSource}, simplestreams.DefaultIndexPath, toolsConstraint, false) if err != nil && !errors.IsNotFoundError(err) { return nil, err } return metadata, nil } var PublicMirrorsInfo = `{ "mirrors": { "com.ubuntu.juju:released:tools": [ { "datatype": "content-download", "path": "streams/v1/cpc-mirrors.json", "updated": "{{updated}}", "format": "mirrors:1.0" } ] } } ` // WriteMetadata writes the given tools metadata to the given storage. func WriteMetadata(stor storage.Storage, metadata []*ToolsMetadata, writeMirrors ShouldWriteMirrors) error { updated := time.Now() index, products, err := MarshalToolsMetadataJSON(metadata, updated) if err != nil { return err } metadataInfo := []MetadataFile{ {simplestreams.UnsignedIndex, index}, {ProductMetadataPath, products}, } if writeMirrors { mirrorsUpdated := updated.Format("20060102") // YYYYMMDD mirrorsInfo := strings.Replace(PublicMirrorsInfo, "{{updated}}", mirrorsUpdated, -1) metadataInfo = append(metadataInfo, MetadataFile{simplestreams.UnsignedMirror, []byte(mirrorsInfo)}) } for _, md := range metadataInfo { logger.Infof("Writing %s", "tools/"+md.Path) err = stor.Put(path.Join(storage.BaseToolsPath, md.Path), bytes.NewReader(md.Data), int64(len(md.Data))) if err != nil { return err } } return nil } type ShouldWriteMirrors bool const ( WriteMirrors = ShouldWriteMirrors(true) DoNotWriteMirrors = ShouldWriteMirrors(false) ) // MergeAndWriteMetadata reads the existing metadata from storage (if any), // and merges it with metadata generated from the given tools list. The // resulting metadata is written to storage. func MergeAndWriteMetadata(stor storage.Storage, tools coretools.List, writeMirrors ShouldWriteMirrors) error { existing, err := ReadMetadata(stor) if err != nil { return err } metadata := MetadataFromTools(tools) if metadata, err = MergeMetadata(metadata, existing); err != nil { return err } return WriteMetadata(stor, metadata, writeMirrors) } // fetchToolsHash fetches the tools from storage and calculates // its size in bytes and computes a SHA256 hash of its contents. func fetchToolsHash(stor storage.StorageReader, ver version.Binary) (size int64, sha256hash hash.Hash, err error) { r, err := storage.Get(stor, StorageName(ver)) if err != nil { return 0, nil, err } defer r.Close() sha256hash = sha256.New() size, err = io.Copy(sha256hash, r) return size, sha256hash, err } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/simplestreams_test.go�������������������0000644�0000153�0000161�00000062172�12321735642�030156� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "bytes" "flag" "fmt" "io" "net/http" "reflect" "strings" "testing" "launchpad.net/goamz/aws" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/simplestreams" sstesting "launchpad.net/juju-core/environs/simplestreams/testing" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/tools" ttesting "launchpad.net/juju-core/environs/tools/testing" "launchpad.net/juju-core/testing/testbase" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) var live = flag.Bool("live", false, "Include live simplestreams tests") var vendor = flag.String("vendor", "", "The vendor representing the source of the simplestream data") type liveTestData struct { baseURL string requireSigned bool validCloudSpec simplestreams.CloudSpec } var liveUrls = map[string]liveTestData{ "ec2": { baseURL: tools.DefaultBaseURL, requireSigned: true, validCloudSpec: simplestreams.CloudSpec{"us-east-1", aws.Regions["us-east-1"].EC2Endpoint}, }, "canonistack": { baseURL: "https://swift.canonistack.canonical.com/v1/AUTH_526ad877f3e3464589dc1145dfeaac60/juju-tools", requireSigned: false, validCloudSpec: simplestreams.CloudSpec{"lcy01", "https://keystone.canonistack.canonical.com:443/v2.0/"}, }, } func setupSimpleStreamsTests(t *testing.T) { if *live { if *vendor == "" { t.Fatal("missing vendor") } var ok bool var testData liveTestData if testData, ok = liveUrls[*vendor]; !ok { keys := reflect.ValueOf(liveUrls).MapKeys() t.Fatalf("Unknown vendor %s. Must be one of %s", *vendor, keys) } registerLiveSimpleStreamsTests(testData.baseURL, tools.NewVersionedToolsConstraint(version.MustParse("1.13.0"), simplestreams.LookupParams{ CloudSpec: testData.validCloudSpec, Series: []string{version.Current.Series}, Arches: []string{"amd64"}, }), testData.requireSigned) } registerSimpleStreamsTests() } func registerSimpleStreamsTests() { gc.Suite(&simplestreamsSuite{ LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{ Source: simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames), RequireSigned: false, DataType: tools.ContentDownload, ValidConstraint: tools.NewVersionedToolsConstraint(version.MustParse("1.13.0"), simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ Region: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", }, Series: []string{"precise"}, Arches: []string{"amd64", "arm"}, }), }, }) gc.Suite(&signedSuite{}) } func registerLiveSimpleStreamsTests(baseURL string, validToolsConstraint simplestreams.LookupConstraint, requireSigned bool) { gc.Suite(&sstesting.LocalLiveSimplestreamsSuite{ Source: simplestreams.NewURLDataSource("test", baseURL, utils.VerifySSLHostnames), RequireSigned: requireSigned, DataType: tools.ContentDownload, ValidConstraint: validToolsConstraint, }) } type simplestreamsSuite struct { sstesting.LocalLiveSimplestreamsSuite sstesting.TestDataSuite } func (s *simplestreamsSuite) SetUpSuite(c *gc.C) { s.LocalLiveSimplestreamsSuite.SetUpSuite(c) s.TestDataSuite.SetUpSuite(c) } func (s *simplestreamsSuite) TearDownSuite(c *gc.C) { s.TestDataSuite.TearDownSuite(c) s.LocalLiveSimplestreamsSuite.TearDownSuite(c) } var fetchTests = []struct { region string series string version string major int minor int released bool arches []string tools []*tools.ToolsMetadata }{{ series: "precise", arches: []string{"amd64", "arm"}, version: "1.13.0", tools: []*tools.ToolsMetadata{ { Release: "precise", Version: "1.13.0", Arch: "amd64", Size: 2973595, Path: "tools/releases/20130806/juju-1.13.0-precise-amd64.tgz", FileType: "tar.gz", SHA256: "447aeb6a934a5eaec4f703eda4ef2dde", }, }, }, { series: "raring", arches: []string{"amd64", "arm"}, version: "1.13.0", tools: []*tools.ToolsMetadata{ { Release: "raring", Version: "1.13.0", Arch: "amd64", Size: 2973173, Path: "tools/releases/20130806/juju-1.13.0-raring-amd64.tgz", FileType: "tar.gz", SHA256: "df07ac5e1fb4232d4e9aa2effa57918a", }, }, }, { series: "raring", arches: []string{"amd64", "arm"}, version: "1.11.4", tools: []*tools.ToolsMetadata{ { Release: "raring", Version: "1.11.4", Arch: "arm", Size: 1950327, Path: "tools/releases/20130806/juju-1.11.4-raring-arm.tgz", FileType: "tar.gz", SHA256: "6472014e3255e3fe7fbd3550ef3f0a11", }, }, }, { series: "precise", arches: []string{"amd64", "arm"}, major: 2, tools: []*tools.ToolsMetadata{ { Release: "precise", Version: "2.0.1", Arch: "arm", Size: 1951096, Path: "tools/releases/20130806/juju-2.0.1-precise-arm.tgz", FileType: "tar.gz", SHA256: "f65a92b3b41311bdf398663ee1c5cd0c", }, }, }, { series: "precise", arches: []string{"amd64", "arm"}, major: 1, minor: 11, tools: []*tools.ToolsMetadata{ { Release: "precise", Version: "1.11.4", Arch: "arm", Size: 1951096, Path: "tools/releases/20130806/juju-1.11.4-precise-arm.tgz", FileType: "tar.gz", SHA256: "f65a92b3b41311bdf398663ee1c5cd0c", }, { Release: "precise", Version: "1.11.5", Arch: "arm", Size: 2031281, Path: "tools/releases/20130803/juju-1.11.5-precise-arm.tgz", FileType: "tar.gz", SHA256: "df07ac5e1fb4232d4e9aa2effa57918a", }, }, }, { series: "raring", arches: []string{"amd64", "arm"}, major: 1, minor: -1, released: true, tools: []*tools.ToolsMetadata{ { Release: "raring", Version: "1.14.0", Arch: "amd64", Size: 2973173, Path: "tools/releases/20130806/juju-1.14.0-raring-amd64.tgz", FileType: "tar.gz", SHA256: "df07ac5e1fb4232d4e9aa2effa57918a", }, }, }} func (s *simplestreamsSuite) TestFetch(c *gc.C) { for i, t := range fetchTests { c.Logf("test %d", i) var toolsConstraint *tools.ToolsConstraint if t.version == "" { toolsConstraint = tools.NewGeneralToolsConstraint(t.major, t.minor, t.released, simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"}, Series: []string{t.series}, Arches: t.arches, }) } else { toolsConstraint = tools.NewVersionedToolsConstraint(version.MustParse(t.version), simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"}, Series: []string{t.series}, Arches: t.arches, }) } // Add invalid datasource and check later that resolveInfo is correct. invalidSource := simplestreams.NewURLDataSource("invalid", "file://invalid", utils.VerifySSLHostnames) tools, resolveInfo, err := tools.Fetch( []simplestreams.DataSource{invalidSource, s.Source}, simplestreams.DefaultIndexPath, toolsConstraint, s.RequireSigned) if !c.Check(err, gc.IsNil) { continue } for _, tm := range t.tools { tm.FullPath, err = s.Source.URL(tm.Path) c.Assert(err, gc.IsNil) } c.Check(tools, gc.DeepEquals, t.tools) c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: s.RequireSigned, IndexURL: "test:/streams/v1/index.json", MirrorURL: "", }) } } func (s *simplestreamsSuite) TestFetchWithMirror(c *gc.C) { toolsConstraint := tools.NewGeneralToolsConstraint(1, 13, false, simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-west-2", "https://ec2.us-west-2.amazonaws.com"}, Series: []string{"precise"}, Arches: []string{"amd64"}, }) toolsMetadata, resolveInfo, err := tools.Fetch( []simplestreams.DataSource{s.Source}, simplestreams.DefaultIndexPath, toolsConstraint, s.RequireSigned) c.Assert(err, gc.IsNil) c.Assert(len(toolsMetadata), gc.Equals, 1) expectedMetadata := &tools.ToolsMetadata{ Release: "precise", Version: "1.13.0", Arch: "amd64", Size: 2973595, Path: "mirrored-path/juju-1.13.0-precise-amd64.tgz", FullPath: "test:/mirrored-path/juju-1.13.0-precise-amd64.tgz", FileType: "tar.gz", SHA256: "447aeb6a934a5eaec4f703eda4ef2dde", } c.Assert(err, gc.IsNil) c.Assert(toolsMetadata[0], gc.DeepEquals, expectedMetadata) c.Assert(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: s.RequireSigned, IndexURL: "test:/streams/v1/index.json", MirrorURL: "test:/", }) } func assertMetadataMatches(c *gc.C, storageDir string, toolList coretools.List, metadata []*tools.ToolsMetadata) { var expectedMetadata []*tools.ToolsMetadata = make([]*tools.ToolsMetadata, len(toolList)) for i, tool := range toolList { expectedMetadata[i] = &tools.ToolsMetadata{ Release: tool.Version.Series, Version: tool.Version.Number.String(), Arch: tool.Version.Arch, Size: tool.Size, Path: fmt.Sprintf("releases/juju-%s.tgz", tool.Version.String()), FileType: "tar.gz", SHA256: tool.SHA256, } } c.Assert(metadata, gc.DeepEquals, expectedMetadata) } func (s *simplestreamsSuite) TestWriteMetadataNoFetch(c *gc.C) { toolsList := coretools.List{ { Version: version.MustParseBinary("1.2.3-precise-amd64"), Size: 123, SHA256: "abcd", }, { Version: version.MustParseBinary("2.0.1-raring-amd64"), Size: 456, SHA256: "xyz", }, } dir := c.MkDir() writer, err := filestorage.NewFileStorageWriter(dir) c.Assert(err, gc.IsNil) err = tools.MergeAndWriteMetadata(writer, toolsList, tools.DoNotWriteMirrors) c.Assert(err, gc.IsNil) metadata := ttesting.ParseMetadataFromDir(c, dir, false) assertMetadataMatches(c, dir, toolsList, metadata) } func (s *simplestreamsSuite) assertWriteMetadata(c *gc.C, withMirrors bool) { var versionStrings = []string{ "1.2.3-precise-amd64", "2.0.1-raring-amd64", } dir := c.MkDir() ttesting.MakeTools(c, dir, "releases", versionStrings) toolsList := coretools.List{ { // If sha256/size is already known, do not recalculate Version: version.MustParseBinary("1.2.3-precise-amd64"), Size: 123, SHA256: "abcd", }, { Version: version.MustParseBinary("2.0.1-raring-amd64"), // The URL is not used for generating metadata. URL: "bogus://", }, } writer, err := filestorage.NewFileStorageWriter(dir) c.Assert(err, gc.IsNil) writeMirrors := tools.DoNotWriteMirrors if withMirrors { writeMirrors = tools.WriteMirrors } err = tools.MergeAndWriteMetadata(writer, toolsList, writeMirrors) c.Assert(err, gc.IsNil) metadata := ttesting.ParseMetadataFromDir(c, dir, withMirrors) assertMetadataMatches(c, dir, toolsList, metadata) } func (s *simplestreamsSuite) TestWriteMetadata(c *gc.C) { s.assertWriteMetadata(c, false) } func (s *simplestreamsSuite) TestWriteMetadataWithMirrors(c *gc.C) { s.assertWriteMetadata(c, true) } func (s *simplestreamsSuite) TestWriteMetadataMergeWithExisting(c *gc.C) { dir := c.MkDir() existingToolsList := coretools.List{ { Version: version.MustParseBinary("1.2.3-precise-amd64"), Size: 123, SHA256: "abc", }, { Version: version.MustParseBinary("2.0.1-raring-amd64"), Size: 456, SHA256: "xyz", }, } writer, err := filestorage.NewFileStorageWriter(dir) c.Assert(err, gc.IsNil) err = tools.MergeAndWriteMetadata(writer, existingToolsList, tools.DoNotWriteMirrors) c.Assert(err, gc.IsNil) newToolsList := coretools.List{ existingToolsList[0], { Version: version.MustParseBinary("2.1.0-raring-amd64"), Size: 789, SHA256: "def", }, } err = tools.MergeAndWriteMetadata(writer, newToolsList, tools.DoNotWriteMirrors) c.Assert(err, gc.IsNil) requiredToolsList := append(existingToolsList, newToolsList[1]) metadata := ttesting.ParseMetadataFromDir(c, dir, false) assertMetadataMatches(c, dir, requiredToolsList, metadata) } type productSpecSuite struct{} var _ = gc.Suite(&productSpecSuite{}) func (s *productSpecSuite) TestId(c *gc.C) { toolsConstraint := tools.NewVersionedToolsConstraint(version.MustParse("1.13.0"), simplestreams.LookupParams{ Series: []string{"precise"}, Arches: []string{"amd64"}, }) ids, err := toolsConstraint.Ids() c.Assert(err, gc.IsNil) c.Assert(ids, gc.DeepEquals, []string{"com.ubuntu.juju:12.04:amd64"}) } func (s *productSpecSuite) TestIdMultiArch(c *gc.C) { toolsConstraint := tools.NewVersionedToolsConstraint(version.MustParse("1.11.3"), simplestreams.LookupParams{ Series: []string{"precise"}, Arches: []string{"amd64", "arm"}, }) ids, err := toolsConstraint.Ids() c.Assert(err, gc.IsNil) c.Assert(ids, gc.DeepEquals, []string{ "com.ubuntu.juju:12.04:amd64", "com.ubuntu.juju:12.04:arm"}) } func (s *productSpecSuite) TestIdMultiSeries(c *gc.C) { toolsConstraint := tools.NewVersionedToolsConstraint(version.MustParse("1.11.3"), simplestreams.LookupParams{ Series: []string{"precise", "raring"}, Arches: []string{"amd64"}, }) ids, err := toolsConstraint.Ids() c.Assert(err, gc.IsNil) c.Assert(ids, gc.DeepEquals, []string{ "com.ubuntu.juju:12.04:amd64", "com.ubuntu.juju:13.04:amd64"}) } func (s *productSpecSuite) TestIdWithMajorVersionOnly(c *gc.C) { toolsConstraint := tools.NewGeneralToolsConstraint(1, -1, false, simplestreams.LookupParams{ Series: []string{"precise"}, Arches: []string{"amd64"}, }) ids, err := toolsConstraint.Ids() c.Assert(err, gc.IsNil) c.Assert(ids, gc.DeepEquals, []string{`com.ubuntu.juju:12.04:amd64`}) } func (s *productSpecSuite) TestIdWithMajorMinorVersion(c *gc.C) { toolsConstraint := tools.NewGeneralToolsConstraint(1, 2, false, simplestreams.LookupParams{ Series: []string{"precise"}, Arches: []string{"amd64"}, }) ids, err := toolsConstraint.Ids() c.Assert(err, gc.IsNil) c.Assert(ids, gc.DeepEquals, []string{`com.ubuntu.juju:12.04:amd64`}) } func (s *productSpecSuite) TestLargeNumber(c *gc.C) { json := `{ "updated": "Fri, 30 Aug 2013 16:12:58 +0800", "format": "products:1.0", "products": { "com.ubuntu.juju:1.10.0:amd64": { "version": "1.10.0", "arch": "amd64", "versions": { "20133008": { "items": { "1.10.0-precise-amd64": { "release": "precise", "version": "1.10.0", "arch": "amd64", "size": 9223372036854775807, "path": "releases/juju-1.10.0-precise-amd64.tgz", "ftype": "tar.gz", "sha256": "" } } } } } } }` cloudMetadata, err := simplestreams.ParseCloudMetadata([]byte(json), "products:1.0", "", tools.ToolsMetadata{}) c.Assert(err, gc.IsNil) c.Assert(cloudMetadata.Products, gc.HasLen, 1) product := cloudMetadata.Products["com.ubuntu.juju:1.10.0:amd64"] c.Assert(product, gc.NotNil) c.Assert(product.Items, gc.HasLen, 1) version := product.Items["20133008"] c.Assert(version, gc.NotNil) c.Assert(version.Items, gc.HasLen, 1) item := version.Items["1.10.0-precise-amd64"] c.Assert(item, gc.NotNil) c.Assert(item, gc.FitsTypeOf, &tools.ToolsMetadata{}) c.Assert(item.(*tools.ToolsMetadata).Size, gc.Equals, int64(9223372036854775807)) } type metadataHelperSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&metadataHelperSuite{}) func (*metadataHelperSuite) TestMetadataFromTools(c *gc.C) { metadata := tools.MetadataFromTools(nil) c.Assert(metadata, gc.HasLen, 0) toolsList := coretools.List{{ Version: version.MustParseBinary("1.2.3-precise-amd64"), Size: 123, SHA256: "abc", }, { Version: version.MustParseBinary("2.0.1-raring-amd64"), URL: "file:///tmp/releases/juju-2.0.1-raring-amd64.tgz", Size: 456, SHA256: "xyz", }} metadata = tools.MetadataFromTools(toolsList) c.Assert(metadata, gc.HasLen, len(toolsList)) for i, t := range toolsList { md := metadata[i] c.Assert(md.Release, gc.Equals, t.Version.Series) c.Assert(md.Version, gc.Equals, t.Version.Number.String()) c.Assert(md.Arch, gc.Equals, t.Version.Arch) // FullPath is only filled out when reading tools using simplestreams. // It's not needed elsewhere and requires a URL() call. c.Assert(md.FullPath, gc.Equals, "") c.Assert(md.Path, gc.Equals, tools.StorageName(t.Version)[len("tools/"):]) c.Assert(md.FileType, gc.Equals, "tar.gz") c.Assert(md.Size, gc.Equals, t.Size) c.Assert(md.SHA256, gc.Equals, t.SHA256) } } type countingStorage struct { storage.StorageReader counter int } func (c *countingStorage) Get(name string) (io.ReadCloser, error) { c.counter++ return c.StorageReader.Get(name) } func (*metadataHelperSuite) TestResolveMetadata(c *gc.C) { var versionStrings = []string{"1.2.3-precise-amd64"} dir := c.MkDir() ttesting.MakeTools(c, dir, "releases", versionStrings) toolsList := coretools.List{{ Version: version.MustParseBinary(versionStrings[0]), Size: 123, SHA256: "abc", }} stor, err := filestorage.NewFileStorageReader(dir) c.Assert(err, gc.IsNil) err = tools.ResolveMetadata(stor, nil) c.Assert(err, gc.IsNil) // We already have size/sha256, so ensure that storage isn't consulted. countingStorage := &countingStorage{StorageReader: stor} metadata := tools.MetadataFromTools(toolsList) err = tools.ResolveMetadata(countingStorage, metadata) c.Assert(err, gc.IsNil) c.Assert(countingStorage.counter, gc.Equals, 0) // Now clear size/sha256, and check that it is called, and // the size/sha256 sum are updated. metadata[0].Size = 0 metadata[0].SHA256 = "" err = tools.ResolveMetadata(countingStorage, metadata) c.Assert(err, gc.IsNil) c.Assert(countingStorage.counter, gc.Equals, 1) c.Assert(metadata[0].Size, gc.Not(gc.Equals), 0) c.Assert(metadata[0].SHA256, gc.Not(gc.Equals), "") } func (*metadataHelperSuite) TestMergeMetadata(c *gc.C) { md1 := &tools.ToolsMetadata{ Release: "precise", Version: "1.2.3", Arch: "amd64", Path: "path1", } md2 := &tools.ToolsMetadata{ Release: "precise", Version: "1.2.3", Arch: "amd64", Path: "path2", } md3 := &tools.ToolsMetadata{ Release: "raring", Version: "1.2.3", Arch: "amd64", Path: "path3", } withSize := func(md *tools.ToolsMetadata, size int64) *tools.ToolsMetadata { clone := *md clone.Size = size return &clone } withSHA256 := func(md *tools.ToolsMetadata, sha256 string) *tools.ToolsMetadata { clone := *md clone.SHA256 = sha256 return &clone } type mdlist []*tools.ToolsMetadata type test struct { name string lhs, rhs, merged []*tools.ToolsMetadata err string } tests := []test{{ name: "non-empty lhs, empty rhs", lhs: mdlist{md1}, rhs: nil, merged: mdlist{md1}, }, { name: "empty lhs, non-empty rhs", lhs: nil, rhs: mdlist{md2}, merged: mdlist{md2}, }, { name: "identical lhs, rhs", lhs: mdlist{md1}, rhs: mdlist{md1}, merged: mdlist{md1}, }, { name: "same tools in lhs and rhs, neither have size: prefer lhs", lhs: mdlist{md1}, rhs: mdlist{md2}, merged: mdlist{md1}, }, { name: "same tools in lhs and rhs, only lhs has a size: prefer lhs", lhs: mdlist{withSize(md1, 123)}, rhs: mdlist{md2}, merged: mdlist{withSize(md1, 123)}, }, { name: "same tools in lhs and rhs, only rhs has a size: prefer rhs", lhs: mdlist{md1}, rhs: mdlist{withSize(md2, 123)}, merged: mdlist{withSize(md2, 123)}, }, { name: "same tools in lhs and rhs, both have the same size: prefer lhs", lhs: mdlist{withSize(md1, 123)}, rhs: mdlist{withSize(md2, 123)}, merged: mdlist{withSize(md1, 123)}, }, { name: "same tools in lhs and rhs, both have different sizes: error", lhs: mdlist{withSize(md1, 123)}, rhs: mdlist{withSize(md2, 456)}, err: "metadata mismatch for 1\\.2\\.3-precise-amd64: sizes=\\(123,456\\) sha256=\\(,\\)", }, { name: "same tools in lhs and rhs, both have same size but different sha256: error", lhs: mdlist{withSHA256(withSize(md1, 123), "a")}, rhs: mdlist{withSHA256(withSize(md2, 123), "b")}, err: "metadata mismatch for 1\\.2\\.3-precise-amd64: sizes=\\(123,123\\) sha256=\\(a,b\\)", }, { name: "lhs is a proper superset of rhs: union of lhs and rhs", lhs: mdlist{md1, md3}, rhs: mdlist{md1}, merged: mdlist{md1, md3}, }, { name: "rhs is a proper superset of lhs: union of lhs and rhs", lhs: mdlist{md1}, rhs: mdlist{md1, md3}, merged: mdlist{md1, md3}, }} for i, test := range tests { c.Logf("test %d: %s", i, test.name) merged, err := tools.MergeMetadata(test.lhs, test.rhs) if test.err == "" { c.Assert(err, gc.IsNil) c.Assert(merged, gc.DeepEquals, test.merged) } else { c.Assert(err, gc.ErrorMatches, test.err) c.Assert(merged, gc.IsNil) } } } func (*metadataHelperSuite) TestReadWriteMetadata(c *gc.C) { metadata := []*tools.ToolsMetadata{{ Release: "precise", Version: "1.2.3", Arch: "amd64", Path: "path1", }, { Release: "raring", Version: "1.2.3", Arch: "amd64", Path: "path2", }} stor, err := filestorage.NewFileStorageWriter(c.MkDir()) c.Assert(err, gc.IsNil) out, err := tools.ReadMetadata(stor) c.Assert(out, gc.HasLen, 0) c.Assert(err, gc.IsNil) // non-existence is not an error err = tools.WriteMetadata(stor, metadata, tools.DoNotWriteMirrors) c.Assert(err, gc.IsNil) out, err = tools.ReadMetadata(stor) for _, md := range out { // FullPath is set by ReadMetadata. c.Assert(md.FullPath, gc.Not(gc.Equals), "") md.FullPath = "" } c.Assert(out, gc.DeepEquals, metadata) } type signedSuite struct { origKey string } var testRoundTripper *jujutest.ProxyRoundTripper func init() { testRoundTripper = &jujutest.ProxyRoundTripper{} testRoundTripper.RegisterForScheme("signedtest") } func (s *signedSuite) SetUpSuite(c *gc.C) { var imageData = map[string]string{ "/unsigned/streams/v1/index.json": unsignedIndex, "/unsigned/streams/v1/tools_metadata.json": unsignedProduct, } // Set up some signed data from the unsigned data. // Overwrite the product path to use the sjson suffix. rawUnsignedIndex := strings.Replace( unsignedIndex, "streams/v1/tools_metadata.json", "streams/v1/tools_metadata.sjson", -1) r := bytes.NewReader([]byte(rawUnsignedIndex)) signedData, err := simplestreams.Encode( r, sstesting.SignedMetadataPrivateKey, sstesting.PrivateKeyPassphrase) c.Assert(err, gc.IsNil) imageData["/signed/streams/v1/index.sjson"] = string(signedData) // Replace the tools path in the unsigned data with a different one so we can test that the right // tools path is used. rawUnsignedProduct := strings.Replace( unsignedProduct, "juju-1.13.0", "juju-1.13.1", -1) r = bytes.NewReader([]byte(rawUnsignedProduct)) signedData, err = simplestreams.Encode( r, sstesting.SignedMetadataPrivateKey, sstesting.PrivateKeyPassphrase) c.Assert(err, gc.IsNil) imageData["/signed/streams/v1/tools_metadata.sjson"] = string(signedData) testRoundTripper.Sub = jujutest.NewCannedRoundTripper( imageData, map[string]int{"signedtest://unauth": http.StatusUnauthorized}) s.origKey = tools.SetSigningPublicKey(sstesting.SignedMetadataPublicKey) } func (s *signedSuite) TearDownSuite(c *gc.C) { testRoundTripper.Sub = nil tools.SetSigningPublicKey(s.origKey) } func (s *signedSuite) TestSignedToolsMetadata(c *gc.C) { signedSource := simplestreams.NewURLDataSource("test", "signedtest://host/signed", utils.VerifySSLHostnames) toolsConstraint := tools.NewVersionedToolsConstraint(version.MustParse("1.13.0"), simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"}, Series: []string{"precise"}, Arches: []string{"amd64"}, }) toolsMetadata, resolveInfo, err := tools.Fetch( []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, toolsConstraint, true) c.Assert(err, gc.IsNil) c.Assert(len(toolsMetadata), gc.Equals, 1) c.Assert(toolsMetadata[0].Path, gc.Equals, "tools/releases/20130806/juju-1.13.1-precise-amd64.tgz") c.Assert(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: true, IndexURL: "signedtest://host/signed/streams/v1/index.sjson", MirrorURL: "", }) } var unsignedIndex = ` { "index": { "com.ubuntu.juju:released:tools": { "updated": "Mon, 05 Aug 2013 11:07:04 +0000", "datatype": "content-download", "format": "products:1.0", "products": [ "com.ubuntu.juju:12.04:amd64" ], "path": "streams/v1/tools_metadata.json" } }, "updated": "Wed, 01 May 2013 13:31:26 +0000", "format": "index:1.0" } ` var unsignedProduct = ` { "updated": "Wed, 01 May 2013 13:31:26 +0000", "content_id": "com.ubuntu.cloud:released:aws", "datatype": "content-download", "products": { "com.ubuntu.juju:12.04:amd64": { "arch": "amd64", "release": "precise", "versions": { "20130806": { "items": { "1130preciseamd64": { "version": "1.13.0", "size": 2973595, "path": "tools/releases/20130806/juju-1.13.0-precise-amd64.tgz", "ftype": "tar.gz", "sha256": "447aeb6a934a5eaec4f703eda4ef2dde" } } } } } }, "format": "products:1.0" } ` ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/tools_test.go���������������������������0000644�0000153�0000161�00000032674�12321735642�026432� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "io/ioutil" "os" "path/filepath" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" envtesting "launchpad.net/juju-core/environs/testing" envtools "launchpad.net/juju-core/environs/tools" ttesting "launchpad.net/juju-core/environs/tools/testing" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) type SimpleStreamsToolsSuite struct { env environs.Environ testbase.LoggingSuite envtesting.ToolsFixture origCurrentVersion version.Binary customToolsDir string publicToolsDir string } func setupToolsTests() { gc.Suite(&SimpleStreamsToolsSuite{}) } func (s *SimpleStreamsToolsSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.customToolsDir = c.MkDir() s.publicToolsDir = c.MkDir() } func (s *SimpleStreamsToolsSuite) SetUpTest(c *gc.C) { s.ToolsFixture.DefaultBaseURL = "file://" + s.publicToolsDir s.LoggingSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) s.origCurrentVersion = version.Current s.reset(c, nil) } func (s *SimpleStreamsToolsSuite) TearDownTest(c *gc.C) { dummy.Reset() version.Current = s.origCurrentVersion s.ToolsFixture.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *SimpleStreamsToolsSuite) reset(c *gc.C, attrs map[string]interface{}) { final := map[string]interface{}{ "tools-metadata-url": "file://" + s.customToolsDir, } for k, v := range attrs { final[k] = v } s.resetEnv(c, final) } func (s *SimpleStreamsToolsSuite) removeTools(c *gc.C) { for _, dir := range []string{s.customToolsDir, s.publicToolsDir} { files, err := ioutil.ReadDir(dir) c.Assert(err, gc.IsNil) for _, f := range files { err := os.RemoveAll(filepath.Join(dir, f.Name())) c.Assert(err, gc.IsNil) } } } func (s *SimpleStreamsToolsSuite) uploadCustom(c *gc.C, verses ...version.Binary) map[version.Binary]string { return ttesting.UploadToDirectory(c, s.customToolsDir, verses...) } func (s *SimpleStreamsToolsSuite) uploadPublic(c *gc.C, verses ...version.Binary) map[version.Binary]string { return ttesting.UploadToDirectory(c, s.publicToolsDir, verses...) } func (s *SimpleStreamsToolsSuite) resetEnv(c *gc.C, attrs map[string]interface{}) { version.Current = s.origCurrentVersion dummy.Reset() cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge(attrs)) c.Assert(err, gc.IsNil) env, err := environs.Prepare(cfg, testing.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) s.env = env s.removeTools(c) } var findToolsTests = []struct { info string major int minor int custom []version.Binary public []version.Binary expect []version.Binary err error }{{ info: "none available anywhere", major: 1, err: envtools.ErrNoTools, }, { info: "custom/private tools only, none matching", major: 1, minor: 2, custom: envtesting.V220all, err: coretools.ErrNoMatches, }, { info: "custom tools found", major: 1, minor: 2, custom: envtesting.VAll, expect: envtesting.V120all, }, { info: "public tools found", major: 1, minor: 1, public: envtesting.VAll, expect: envtesting.V110all, }, { info: "public and custom tools found, only taken from custom", major: 1, minor: 1, custom: envtesting.V110p, public: envtesting.VAll, expect: envtesting.V110p, }, { info: "custom tools completely block public ones", major: 1, minor: -1, custom: envtesting.V220all, public: envtesting.VAll, expect: envtesting.V1all, }, { info: "tools matching major version only", major: 1, minor: -1, public: envtesting.VAll, expect: envtesting.V1all, }} func (s *SimpleStreamsToolsSuite) TestFindTools(c *gc.C) { for i, test := range findToolsTests { c.Logf("\ntest %d: %s", i, test.info) s.reset(c, nil) custom := s.uploadCustom(c, test.custom...) public := s.uploadPublic(c, test.public...) actual, err := envtools.FindTools(s.env, test.major, test.minor, coretools.Filter{}, envtools.DoNotAllowRetry) if test.err != nil { if len(actual) > 0 { c.Logf(actual.String()) } c.Check(err, jc.Satisfies, errors.IsNotFoundError) continue } expect := map[version.Binary]string{} for _, expected := range test.expect { // If the tools exist in custom, that's preferred. var ok bool if expect[expected], ok = custom[expected]; !ok { expect[expected] = public[expected] } } c.Check(actual.URLs(), gc.DeepEquals, expect) } } func (s *SimpleStreamsToolsSuite) TestFindToolsInControlBucket(c *gc.C) { s.reset(c, nil) custom := ttesting.UploadToStorage(c, s.env.Storage(), envtesting.V110p...) s.uploadPublic(c, envtesting.VAll...) actual, err := envtools.FindTools(s.env, 1, 1, coretools.Filter{}, envtools.DoNotAllowRetry) c.Assert(err, gc.IsNil) expect := map[version.Binary]string{} for _, expected := range envtesting.V110p { expect[expected] = custom[expected] } c.Assert(actual.URLs(), gc.DeepEquals, expect) } func (s *SimpleStreamsToolsSuite) TestFindToolsFiltering(c *gc.C) { tw := &loggo.TestWriter{} c.Assert(loggo.RegisterWriter("filter-tester", tw, loggo.DEBUG), gc.IsNil) defer loggo.RemoveWriter("filter-tester") _, err := envtools.FindTools( s.env, 1, -1, coretools.Filter{Number: version.Number{Major: 1, Minor: 2, Patch: 3}}, envtools.DoNotAllowRetry) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // This is slightly overly prescriptive, but feel free to change or add // messages. This still helps to ensure that all log messages are // properly formed. messages := []jc.SimpleMessage{ {loggo.INFO, "reading tools with major version 1"}, {loggo.INFO, "filtering tools by version: \\d+\\.\\d+\\.\\d+"}, {loggo.DEBUG, "no architecture specified when finding tools, looking for any"}, {loggo.DEBUG, "no series specified when finding tools, looking for any"}, } sources, err := envtools.GetMetadataSources(s.env) c.Assert(err, gc.IsNil) for i := 0; i < 2*len(sources); i++ { messages = append(messages, jc.SimpleMessage{loggo.DEBUG, `fetchData failed for .*`}, jc.SimpleMessage{loggo.DEBUG, `cannot load index .*`}) } c.Check(tw.Log, jc.LogMatches, messages) } func (s *SimpleStreamsToolsSuite) TestFindBootstrapTools(c *gc.C) { // Remove the default tools URL from the search path, just look in cloud storage. s.PatchValue(&envtools.DefaultBaseURL, "") for i, test := range envtesting.BootstrapToolsTests { c.Logf("\ntest %d: %s", i, test.Info) attrs := map[string]interface{}{ "development": test.Development, } var agentVersion *version.Number if test.AgentVersion != version.Zero { attrs["agent-version"] = test.AgentVersion.String() agentVersion = &test.AgentVersion } s.reset(c, attrs) version.Current = test.CliVersion available := s.uploadCustom(c, test.Available...) params := envtools.BootstrapToolsParams{ Version: agentVersion, Series: test.DefaultSeries, Arch: &test.Arch, } actual, err := envtools.FindBootstrapTools(s.env, params) if test.Err != "" { if len(actual) > 0 { c.Logf(actual.String()) } c.Check(err, jc.Satisfies, errors.IsNotFoundError) continue } expect := map[version.Binary]string{} for _, expected := range test.Expect { expect[expected] = available[expected] } c.Check(actual.URLs(), gc.DeepEquals, expect) } } var findInstanceToolsTests = []struct { info string available []version.Binary agentVersion version.Number series string arch string expect []version.Binary err error }{{ info: "nothing at all", agentVersion: envtesting.V120, series: "precise", err: envtools.ErrNoTools, }, { info: "nothing matching 1", available: envtesting.V100Xall, agentVersion: envtesting.V120, series: "precise", err: coretools.ErrNoMatches, }, { info: "nothing matching 2", available: envtesting.V120all, agentVersion: envtesting.V110, series: "precise", err: coretools.ErrNoMatches, }, { info: "nothing matching 3", available: envtesting.V120q, agentVersion: envtesting.V120, series: "precise", err: coretools.ErrNoMatches, }, { info: "nothing matching 4", available: envtesting.V120q, agentVersion: envtesting.V120, series: "quantal", arch: "arm", err: coretools.ErrNoMatches, }, { info: "actual match 1", available: envtesting.VAll, agentVersion: envtesting.V1001, series: "precise", expect: []version.Binary{envtesting.V1001p64}, }, { info: "actual match 2", available: envtesting.VAll, agentVersion: envtesting.V120, series: "quantal", expect: []version.Binary{envtesting.V120q64, envtesting.V120q32}, }, { info: "actual match 3", available: envtesting.VAll, agentVersion: envtesting.V110, series: "quantal", arch: "i386", expect: []version.Binary{envtesting.V110q32}, }} func (s *SimpleStreamsToolsSuite) TestFindInstanceTools(c *gc.C) { for i, test := range findInstanceToolsTests { c.Logf("\ntest %d: %s", i, test.info) s.reset(c, map[string]interface{}{ "agent-version": test.agentVersion.String(), }) available := s.uploadCustom(c, test.available...) agentVersion, _ := s.env.Config().AgentVersion() actual, err := envtools.FindInstanceTools(s.env, agentVersion, test.series, &test.arch) if test.err != nil { if len(actual) > 0 { c.Logf(actual.String()) } c.Check(err, jc.Satisfies, errors.IsNotFoundError) continue } expect := map[version.Binary]string{} for _, expected := range test.expect { expect[expected] = available[expected] } c.Check(actual.URLs(), gc.DeepEquals, expect) } } var findExactToolsTests = []struct { info string custom []version.Binary public []version.Binary seek version.Binary err error }{{ info: "nothing available", seek: envtesting.V100p64, err: envtools.ErrNoTools, }, { info: "only non-matches available in custom", custom: append(envtesting.V110all, envtesting.V100p32, envtesting.V100q64, envtesting.V1001p64), seek: envtesting.V100p64, err: coretools.ErrNoMatches, }, { info: "exact match available in custom", custom: []version.Binary{envtesting.V100p64}, seek: envtesting.V100p64, }, { info: "only non-matches available in public", custom: append(envtesting.V110all, envtesting.V100p32, envtesting.V100q64, envtesting.V1001p64), seek: envtesting.V100p64, err: coretools.ErrNoMatches, }, { info: "exact match available in public", public: []version.Binary{envtesting.V100p64}, seek: envtesting.V100p64, }, { info: "exact match in public not blocked by custom", custom: envtesting.V110all, public: []version.Binary{envtesting.V100p64}, seek: envtesting.V100p64, }} func (s *SimpleStreamsToolsSuite) TestFindExactTools(c *gc.C) { for i, test := range findExactToolsTests { c.Logf("\ntest %d: %s", i, test.info) s.reset(c, nil) custom := s.uploadCustom(c, test.custom...) public := s.uploadPublic(c, test.public...) actual, err := envtools.FindExactTools(s.env, test.seek.Number, test.seek.Series, test.seek.Arch) if test.err == nil { if !c.Check(err, gc.IsNil) { continue } c.Check(actual.Version, gc.Equals, test.seek) if _, ok := custom[actual.Version]; ok { c.Check(actual.URL, gc.DeepEquals, custom[actual.Version]) } else { c.Check(actual.URL, gc.DeepEquals, public[actual.Version]) } } else { c.Check(err, jc.Satisfies, errors.IsNotFoundError) } } } // fakeToolsForSeries fakes a Tools object with just enough information for // testing the handling its OS series. func fakeToolsForSeries(series string) *coretools.Tools { return &coretools.Tools{Version: version.Binary{Series: series}} } // fakeToolsList fakes a envtools.List containing Tools objects for the given // respective series, in the same number and order. func fakeToolsList(series ...string) coretools.List { list := coretools.List{} for _, name := range series { list = append(list, fakeToolsForSeries(name)) } return list } type ToolsListSuite struct{} func (s *ToolsListSuite) TestCheckToolsSeriesRequiresTools(c *gc.C) { err := envtools.CheckToolsSeries(fakeToolsList(), "precise") c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, "expected single series, got \\[\\]") } func (s *ToolsListSuite) TestCheckToolsSeriesAcceptsOneSetOfTools(c *gc.C) { names := []string{"precise", "raring"} for _, series := range names { list := fakeToolsList(series) err := envtools.CheckToolsSeries(list, series) c.Check(err, gc.IsNil) } } func (s *ToolsListSuite) TestCheckToolsSeriesAcceptsMultipleForSameSeries(c *gc.C) { series := "quantal" list := fakeToolsList(series, series, series) err := envtools.CheckToolsSeries(list, series) c.Check(err, gc.IsNil) } func (s *ToolsListSuite) TestCheckToolsSeriesRejectsToolsForOtherSeries(c *gc.C) { list := fakeToolsList("hoary") err := envtools.CheckToolsSeries(list, "warty") c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, "tools mismatch: expected series warty, got hoary") } func (s *ToolsListSuite) TestCheckToolsSeriesRejectsToolsForMixedSeries(c *gc.C) { list := fakeToolsList("precise", "raring") err := envtools.CheckToolsSeries(list, "precise") c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, "expected single series, got .*") } ��������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/storage.go������������������������������0000644�0000153�0000161�00000004044�12321735642�025665� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "errors" "strings" "launchpad.net/juju-core/environs/storage" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) var ErrNoTools = errors.New("no tools available") const ( toolPrefix = "tools/releases/juju-" toolSuffix = ".tgz" ) // StorageName returns the name that is used to store and retrieve the // given version of the juju tools. func StorageName(vers version.Binary) string { return toolPrefix + vers.String() + toolSuffix } // ReadList returns a List of the tools in store with the given major.minor version. // If minorVersion = -1, then only majorVersion is considered. // If store contains no such tools, it returns ErrNoMatches. func ReadList(stor storage.StorageReader, majorVersion, minorVersion int) (coretools.List, error) { if minorVersion >= 0 { logger.Debugf("reading v%d.%d tools", majorVersion, minorVersion) } else { logger.Debugf("reading v%d.* tools", majorVersion) } names, err := storage.List(stor, toolPrefix) if err != nil { return nil, err } var list coretools.List var foundAnyTools bool for _, name := range names { if !strings.HasPrefix(name, toolPrefix) || !strings.HasSuffix(name, toolSuffix) { continue } var t coretools.Tools vers := name[len(toolPrefix) : len(name)-len(toolSuffix)] if t.Version, err = version.ParseBinary(vers); err != nil { logger.Debugf("failed to parse version %q: %v", vers, err) continue } foundAnyTools = true // Major version must match specified value. if t.Version.Major != majorVersion { continue } // If specified minor version value supplied, minor version must match. if minorVersion >= 0 && t.Version.Minor != minorVersion { continue } logger.Debugf("found %s", vers) if t.URL, err = stor.URL(name); err != nil { return nil, err } list = append(list, &t) } if len(list) == 0 { if foundAnyTools { return nil, coretools.ErrNoMatches } return nil, ErrNoTools } return list, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/urls.go���������������������������������0000644�0000153�0000161�00000005511�12321735642�025206� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "fmt" "net/url" "strings" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/utils" ) // SupportsCustomSources represents an environment that // can host tools metadata at provider specific sources. type SupportsCustomSources interface { GetToolsSources() ([]simplestreams.DataSource, error) } // GetMetadataSources returns the sources to use when looking for // simplestreams tools metadata. If env implements SupportsCustomSurces, // the sources returned from that method will also be considered. // The sources are configured to not use retries. func GetMetadataSources(env environs.ConfigGetter) ([]simplestreams.DataSource, error) { return GetMetadataSourcesWithRetries(env, false) } // GetMetadataSourcesWithRetries returns the sources to use when looking for // simplestreams tools metadata. If env implements SupportsCustomSurces, // the sources returned from that method will also be considered. // The sources are configured to use retries according to the value of allowRetry. func GetMetadataSourcesWithRetries(env environs.ConfigGetter, allowRetry bool) ([]simplestreams.DataSource, error) { var sources []simplestreams.DataSource config := env.Config() if userURL, ok := config.ToolsURL(); ok { verify := utils.VerifySSLHostnames if !config.SSLHostnameVerification() { verify = utils.NoVerifySSLHostnames } sources = append(sources, simplestreams.NewURLDataSource("tools-metadata-url", userURL, verify)) } if custom, ok := env.(SupportsCustomSources); ok { customSources, err := custom.GetToolsSources() if err != nil { return nil, err } sources = append(sources, customSources...) } defaultURL, err := ToolsURL(DefaultBaseURL) if err != nil { return nil, err } if defaultURL != "" { sources = append(sources, simplestreams.NewURLDataSource("default simplestreams", defaultURL, utils.VerifySSLHostnames)) } for _, source := range sources { source.SetAllowRetry(allowRetry) } return sources, nil } // ToolsURL returns a valid tools URL constructed from source. // source may be a directory, or a URL like file://foo or http://foo. func ToolsURL(source string) (string, error) { if source == "" { return "", nil } // If source is a raw directory, we need to append the file:// prefix // so it can be used as a URL. defaultURL := source u, err := url.Parse(source) if err != nil { return "", fmt.Errorf("invalid default tools URL %s: %v", defaultURL, err) } if u.Scheme == "" { defaultURL = "file://" + defaultURL if !strings.HasSuffix(defaultURL, "/"+storage.BaseToolsPath) { defaultURL = fmt.Sprintf("%s/%s", defaultURL, storage.BaseToolsPath) } } return defaultURL, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/marshal_test.go�������������������������0000644�0000153�0000161�00000011523�12321735642�026707� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/tools" ) var _ = gc.Suite(&marshalSuite{}) type marshalSuite struct{} func (s *marshalSuite) TestLargeNumber(c *gc.C) { metadata := []*tools.ToolsMetadata{ &tools.ToolsMetadata{ Release: "saucy", Version: "1.2.3.4", Arch: "arm", Size: 9223372036854775807, Path: "/somewhere/over/the/rainbow.tar.gz", FileType: "tar.gz", }, } _, products, err := tools.MarshalToolsMetadataJSON(metadata, time.Now()) c.Assert(err, gc.IsNil) c.Assert(string(products), jc.Contains, `"size": 9223372036854775807`) } var expectedIndex = `{ "index": { "com.ubuntu.juju:released:tools": { "updated": "Thu, 01 Jan 1970 00:00:00 +0000", "format": "products:1.0", "datatype": "content-download", "path": "streams/v1/com.ubuntu.juju:released:tools.json", "products": [ "com.ubuntu.juju:12.04:amd64", "com.ubuntu.juju:12.04:arm", "com.ubuntu.juju:13.10:arm" ] } }, "updated": "Thu, 01 Jan 1970 00:00:00 +0000", "format": "index:1.0" }` var expectedProducts = `{ "products": { "com.ubuntu.juju:12.04:amd64": { "version": "4.3.2.1", "arch": "amd64", "versions": { "19700101": { "items": { "4.3.2.1-precise-amd64": { "release": "precise", "version": "4.3.2.1", "arch": "amd64", "size": 0, "path": "whatever.tar.gz", "ftype": "tar.gz", "sha256": "afb14e65c794464e378def12cbad6a96f9186d69" } } } } }, "com.ubuntu.juju:12.04:arm": { "version": "1.2.3.4", "arch": "arm", "versions": { "19700101": { "items": { "1.2.3.4-precise-arm": { "release": "precise", "version": "1.2.3.4", "arch": "arm", "size": 42, "path": "toenlightenment.tar.gz", "ftype": "tar.gz", "sha256": "" } } } } }, "com.ubuntu.juju:13.10:arm": { "version": "1.2.3.4", "arch": "arm", "versions": { "19700101": { "items": { "1.2.3.4-saucy-arm": { "release": "saucy", "version": "1.2.3.4", "arch": "arm", "size": 9223372036854775807, "path": "/somewhere/over/the/rainbow.tar.gz", "ftype": "tar.gz", "sha256": "" } } } } } }, "updated": "Thu, 01 Jan 1970 00:00:00 +0000", "format": "products:1.0", "content_id": "com.ubuntu.juju:released:tools" }` var toolMetadataForTesting = []*tools.ToolsMetadata{ &tools.ToolsMetadata{ Release: "saucy", Version: "1.2.3.4", Arch: "arm", Size: 9223372036854775807, Path: "/somewhere/over/the/rainbow.tar.gz", FileType: "tar.gz", }, &tools.ToolsMetadata{ Release: "precise", Version: "1.2.3.4", Arch: "arm", Size: 42, Path: "toenlightenment.tar.gz", FileType: "tar.gz", }, &tools.ToolsMetadata{ Release: "precise", Version: "4.3.2.1", Arch: "amd64", Path: "whatever.tar.gz", FileType: "tar.gz", SHA256: "afb14e65c794464e378def12cbad6a96f9186d69", }, } func (s *marshalSuite) TestMarshalIndex(c *gc.C) { index, err := tools.MarshalToolsMetadataIndexJSON(toolMetadataForTesting, time.Unix(0, 0).UTC()) c.Assert(err, gc.IsNil) c.Assert(string(index), gc.Equals, expectedIndex) } func (s *marshalSuite) TestMarshalProducts(c *gc.C) { products, err := tools.MarshalToolsMetadataProductsJSON(toolMetadataForTesting, time.Unix(0, 0).UTC()) c.Assert(err, gc.IsNil) c.Assert(string(products), gc.Equals, expectedProducts) } func (s *marshalSuite) TestMarshal(c *gc.C) { index, products, err := tools.MarshalToolsMetadataJSON(toolMetadataForTesting, time.Unix(0, 0).UTC()) c.Assert(err, gc.IsNil) c.Assert(string(index), gc.Equals, expectedIndex) c.Assert(string(products), gc.Equals, expectedProducts) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/suite_test.go���������������������������0000644�0000153�0000161�00000000401�12321735642�026402� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { setupToolsTests() setupSimpleStreamsTests(t) gc.TestingT(t) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/tools.go��������������������������������0000644�0000153�0000161�00000022033�12321735642�025357� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "fmt" "github.com/juju/loggo" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju/arch" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.environs.tools") func makeToolsConstraint(cloudSpec simplestreams.CloudSpec, majorVersion, minorVersion int, filter coretools.Filter) (*ToolsConstraint, error) { var toolsConstraint *ToolsConstraint if filter.Number != version.Zero { // A specific tools version is required, however, a general match based on major/minor // version may also have been requested. This is used to ensure any agent version currently // recorded in the environment matches the Juju cli version. // We can short circuit any lookup here by checking the major/minor numbers against // the filter version and exiting early if there is a mismatch. majorMismatch := majorVersion > 0 && majorVersion != filter.Number.Major minorMismacth := minorVersion != -1 && minorVersion != filter.Number.Minor if majorMismatch || minorMismacth { return nil, coretools.ErrNoMatches } toolsConstraint = NewVersionedToolsConstraint(filter.Number, simplestreams.LookupParams{CloudSpec: cloudSpec}) } else { toolsConstraint = NewGeneralToolsConstraint(majorVersion, minorVersion, filter.Released, simplestreams.LookupParams{CloudSpec: cloudSpec}) } if filter.Arch != "" { toolsConstraint.Arches = []string{filter.Arch} } else { logger.Debugf("no architecture specified when finding tools, looking for any") toolsConstraint.Arches = arch.AllSupportedArches } // The old tools search allowed finding tools without needing to specify a series. // The simplestreams metadata is keyed off series, so series must be specified in // the search constraint. If no series is specified, we gather all the series from // lucid onwards and add those to the constraint. var seriesToSearch []string if filter.Series != "" { seriesToSearch = []string{filter.Series} } else { logger.Debugf("no series specified when finding tools, looking for any") seriesToSearch = simplestreams.SupportedSeries() } toolsConstraint.Series = seriesToSearch return toolsConstraint, nil } // Define some boolean parameter values. const DoNotAllowRetry = false // FindTools returns a List containing all tools with a given // major.minor version number available in the cloud instance, filtered by filter. // If minorVersion = -1, then only majorVersion is considered. // If no *available* tools have the supplied major.minor version number, or match the // supplied filter, the function returns a *NotFoundError. func FindTools(cloudInst environs.ConfigGetter, majorVersion, minorVersion int, filter coretools.Filter, allowRetry bool) (list coretools.List, err error) { var cloudSpec simplestreams.CloudSpec if inst, ok := cloudInst.(simplestreams.HasRegion); ok { if cloudSpec, err = inst.Region(); err != nil { return nil, err } } // If only one of region or endpoint is provided, that is a problem. if cloudSpec.Region != cloudSpec.Endpoint && (cloudSpec.Region == "" || cloudSpec.Endpoint == "") { return nil, fmt.Errorf("cannot find tools without a complete cloud configuration") } if minorVersion >= 0 { logger.Infof("reading tools with major.minor version %d.%d", majorVersion, minorVersion) } else { logger.Infof("reading tools with major version %d", majorVersion) } defer convertToolsError(&err) // Construct a tools filter. // Discard all that are known to be irrelevant. if filter.Number != version.Zero { logger.Infof("filtering tools by version: %s", filter.Number) } if filter.Series != "" { logger.Infof("filtering tools by series: %s", filter.Series) } if filter.Arch != "" { logger.Infof("filtering tools by architecture: %s", filter.Arch) } sources, err := GetMetadataSourcesWithRetries(cloudInst, allowRetry) if err != nil { return nil, err } return FindToolsForCloud(sources, cloudSpec, majorVersion, minorVersion, filter) } // FindToolsForCloud returns a List containing all tools with a given // major.minor version number and cloudSpec, filtered by filter. // If minorVersion = -1, then only majorVersion is considered. // If no *available* tools have the supplied major.minor version number, or match the // supplied filter, the function returns a *NotFoundError. func FindToolsForCloud(sources []simplestreams.DataSource, cloudSpec simplestreams.CloudSpec, majorVersion, minorVersion int, filter coretools.Filter) (list coretools.List, err error) { toolsConstraint, err := makeToolsConstraint(cloudSpec, majorVersion, minorVersion, filter) if err != nil { return nil, err } toolsMetadata, _, err := Fetch(sources, simplestreams.DefaultIndexPath, toolsConstraint, false) if err != nil { if errors.IsNotFoundError(err) { err = ErrNoTools } return nil, err } if len(toolsMetadata) == 0 { return nil, coretools.ErrNoMatches } list = make(coretools.List, len(toolsMetadata)) for i, metadata := range toolsMetadata { list[i] = &coretools.Tools{ Version: metadata.binary(), URL: metadata.FullPath, Size: metadata.Size, SHA256: metadata.SHA256, } } if filter.Series != "" { if err := checkToolsSeries(list, filter.Series); err != nil { return nil, err } } return list, err } // The following allows FindTools, as called by FindBootstrapTools, to be patched for testing. var bootstrapFindTools = FindTools type findtoolsfunc func(environs.ConfigGetter, int, int, coretools.Filter, bool) (coretools.List, error) func TestingPatchBootstrapFindTools(stub findtoolsfunc) func() { origFunc := bootstrapFindTools bootstrapFindTools = stub return func() { bootstrapFindTools = origFunc } } // BootstrapToolsParams contains parameters for FindBootstrapTools type BootstrapToolsParams struct { Version *version.Number Arch *string Series string AllowRetry bool } // FindBootstrapTools returns a ToolsList containing only those tools with // which it would be reasonable to launch an environment's first machine, given the supplied constraints. // If a specific agent version is not requested, all tools matching the current major.minor version are chosen. func FindBootstrapTools(cloudInst environs.ConfigGetter, params BootstrapToolsParams) (list coretools.List, err error) { // Construct a tools filter. cfg := cloudInst.Config() cliVersion := version.Current.Number filter := coretools.Filter{ Series: params.Series, Arch: stringOrEmpty(params.Arch), } if params.Version != nil { // If we already have an explicit agent version set, we're done. filter.Number = *params.Version return bootstrapFindTools(cloudInst, cliVersion.Major, cliVersion.Minor, filter, params.AllowRetry) } if dev := cliVersion.IsDev() || cfg.Development(); !dev { logger.Infof("filtering tools by released version") filter.Released = true } return bootstrapFindTools(cloudInst, cliVersion.Major, cliVersion.Minor, filter, params.AllowRetry) } func stringOrEmpty(pstr *string) string { if pstr == nil { return "" } return *pstr } // FindInstanceTools returns a ToolsList containing only those tools with which // it would be reasonable to start a new instance, given the supplied series and arch. func FindInstanceTools(cloudInst environs.ConfigGetter, vers version.Number, series string, arch *string) (list coretools.List, err error) { // Construct a tools filter. // Discard all that are known to be irrelevant. filter := coretools.Filter{ Number: vers, Series: series, Arch: stringOrEmpty(arch), } return FindTools(cloudInst, vers.Major, vers.Minor, filter, DoNotAllowRetry) } // FindExactTools returns only the tools that match the supplied version. func FindExactTools(cloudInst environs.ConfigGetter, vers version.Number, series string, arch string) (t *coretools.Tools, err error) { logger.Infof("finding exact version %s", vers) // Construct a tools filter. // Discard all that are known to be irrelevant. filter := coretools.Filter{ Number: vers, Series: series, Arch: arch, } availableTools, err := FindTools(cloudInst, vers.Major, vers.Minor, filter, DoNotAllowRetry) if err != nil { return nil, err } if len(availableTools) != 1 { return nil, fmt.Errorf("expected one tools, got %d tools", len(availableTools)) } return availableTools[0], nil } // CheckToolsSeries verifies that all the given possible tools are for the // given OS series. func checkToolsSeries(toolsList coretools.List, series string) error { toolsSeries := toolsList.AllSeries() if len(toolsSeries) != 1 { return fmt.Errorf("expected single series, got %v", toolsSeries) } if toolsSeries[0] != series { return fmt.Errorf("tools mismatch: expected series %v, got %v", series, toolsSeries[0]) } return nil } func isToolsError(err error) bool { switch err { case ErrNoTools, coretools.ErrNoMatches: return true } return false } func convertToolsError(err *error) { if isToolsError(*err) { *err = errors.NewNotFoundError(*err, "") } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/build_test.go���������������������������0000644�0000153�0000161�00000003530�12321735642�026356� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "fmt" "io/ioutil" "os" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/testing/testbase" ) type buildSuite struct { testbase.LoggingSuite restore func() cwd string filePath string } var _ = gc.Suite(&buildSuite{}) func (b *buildSuite) SetUpTest(c *gc.C) { b.LoggingSuite.SetUpTest(c) dir1 := c.MkDir() dir2 := c.MkDir() c.Log(dir1) c.Log(dir2) path := os.Getenv("PATH") os.Setenv("PATH", fmt.Sprintf("%s:%s:%s", dir1, dir2, path)) // Make an executable file called "juju-test" in dir2. b.filePath = filepath.Join(dir2, "juju-test") err := ioutil.WriteFile( b.filePath, []byte("doesn't matter, we don't execute it"), 0755) c.Assert(err, gc.IsNil) cwd, err := os.Getwd() c.Assert(err, gc.IsNil) b.cwd = c.MkDir() err = os.Chdir(b.cwd) c.Assert(err, gc.IsNil) b.restore = func() { os.Setenv("PATH", path) os.Chdir(cwd) } } func (b *buildSuite) TearDownTest(c *gc.C) { b.restore() b.LoggingSuite.TearDownTest(c) } func (b *buildSuite) TestFindExecutable(c *gc.C) { for _, test := range []struct { execFile string expected string errorMatch string }{{ execFile: "/some/absolute/path", expected: "/some/absolute/path", }, { execFile: "./foo", expected: filepath.Join(b.cwd, "foo"), }, { execFile: "juju-test", expected: b.filePath, }, { execFile: "non-existent-exec-file", errorMatch: `could not find "non-existent-exec-file" in the path`, }} { result, err := tools.FindExecutable(test.execFile) if test.errorMatch == "" { c.Assert(err, gc.IsNil) c.Assert(result, gc.Equals, test.expected) } else { c.Assert(err, gc.ErrorMatches, test.errorMatch) c.Assert(result, gc.Equals, "") } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/urls_test.go����������������������������0000644�0000153�0000161�00000006775�12321735642�026262� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" sstesting "launchpad.net/juju-core/environs/simplestreams/testing" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/testing" ) type URLsSuite struct { home *testing.FakeHome } var _ = gc.Suite(&URLsSuite{}) func (s *URLsSuite) SetUpTest(c *gc.C) { s.home = testing.MakeEmptyFakeHome(c) } func (s *URLsSuite) TearDownTest(c *gc.C) { s.home.Restore() dummy.Reset() } func (s *URLsSuite) env(c *gc.C, toolsMetadataURL string) environs.Environ { attrs := dummy.SampleConfig() if toolsMetadataURL != "" { attrs = attrs.Merge(testing.Attrs{ "tools-metadata-url": toolsMetadataURL, }) } cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := environs.Prepare(cfg, testing.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) return env } func (s *URLsSuite) TestToolsURLsNoConfigURL(c *gc.C) { env := s.env(c, "") sources, err := tools.GetMetadataSources(env) c.Assert(err, gc.IsNil) // Put a file in tools since the dummy storage provider requires a // file to exist before the URL can be found. This is to ensure it behaves // the same way as MAAS. err = env.Storage().Put("tools/dummy", strings.NewReader("dummy"), 5) c.Assert(err, gc.IsNil) privateStorageURL, err := env.Storage().URL("tools") c.Assert(err, gc.IsNil) sstesting.AssertExpectedSources(c, sources, []string{ privateStorageURL, "https://streams.canonical.com/juju/tools/"}) } func (s *URLsSuite) TestToolsSources(c *gc.C) { env := s.env(c, "config-tools-metadata-url") sources, err := tools.GetMetadataSources(env) c.Assert(err, gc.IsNil) // Put a file in tools since the dummy storage provider requires a // file to exist before the URL can be found. This is to ensure it behaves // the same way as MAAS. err = env.Storage().Put("tools/dummy", strings.NewReader("dummy"), 5) c.Assert(err, gc.IsNil) privateStorageURL, err := env.Storage().URL("tools") c.Assert(err, gc.IsNil) sstesting.AssertExpectedSources(c, sources, []string{ "config-tools-metadata-url/", privateStorageURL, "https://streams.canonical.com/juju/tools/"}) haveExpectedSources := false for _, source := range sources { if allowRetry, ok := storage.TestingGetAllowRetry(source); ok { haveExpectedSources = true c.Assert(allowRetry, jc.IsFalse) } } c.Assert(haveExpectedSources, jc.IsTrue) } func (s *URLsSuite) TestToolsSourcesWithRetry(c *gc.C) { env := s.env(c, "") sources, err := tools.GetMetadataSourcesWithRetries(env, true) c.Assert(err, gc.IsNil) haveExpectedSources := false for _, source := range sources { if allowRetry, ok := storage.TestingGetAllowRetry(source); ok { haveExpectedSources = true c.Assert(allowRetry, jc.IsTrue) } } c.Assert(haveExpectedSources, jc.IsTrue) c.Assert(haveExpectedSources, jc.IsTrue) } func (s *URLsSuite) TestToolsURL(c *gc.C) { for source, expected := range map[string]string{ "": "", "foo": "file://foo/tools", "/home/foo": "file:///home/foo/tools", "file://foo": "file://foo", "http://foo": "http://foo", } { URL, err := tools.ToolsURL(source) c.Assert(err, gc.IsNil) c.Assert(URL, gc.Equals, expected) } } ���juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/validation_test.go����������������������0000644�0000153�0000161�00000010214�12321735642�027406� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "path" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type ValidateSuite struct { testbase.LoggingSuite metadataDir string } var _ = gc.Suite(&ValidateSuite{}) func (s *ValidateSuite) makeLocalMetadata(c *gc.C, version, series string) error { tm := []*ToolsMetadata{{ Version: version, Release: series, Arch: "amd64", Path: "/tools/tools.tar.gz", Size: 1234, FileType: "tar.gz", SHA256: "f65a92b3b41311bdf398663ee1c5cd0c", }} stor, err := filestorage.NewFileStorageWriter(s.metadataDir) c.Assert(err, gc.IsNil) err = WriteMetadata(stor, tm, false) c.Assert(err, gc.IsNil) return nil } func (s *ValidateSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.metadataDir = c.MkDir() } func (s *ValidateSuite) toolsURL() string { return "file://" + path.Join(s.metadataDir, "tools") } func (s *ValidateSuite) TestExactVersionMatch(c *gc.C) { s.makeLocalMetadata(c, "1.11.2", "raring") params := &ToolsMetadataLookupParams{ Version: "1.11.2", MetadataLookupParams: simplestreams.MetadataLookupParams{ Region: "region-2", Series: "raring", Architectures: []string{"amd64"}, Endpoint: "some-auth-url", Sources: []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", s.toolsURL(), utils.VerifySSLHostnames)}, }, } versions, resolveInfo, err := ValidateToolsMetadata(params) c.Assert(err, gc.IsNil) c.Assert(versions, gc.DeepEquals, []string{"1.11.2-raring-amd64"}) c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: false, IndexURL: "file://" + path.Join(s.metadataDir, "tools/streams/v1/index.json"), MirrorURL: "", }) } func (s *ValidateSuite) TestMajorVersionMatch(c *gc.C) { s.makeLocalMetadata(c, "1.11.2", "raring") params := &ToolsMetadataLookupParams{ Major: 1, Minor: -1, MetadataLookupParams: simplestreams.MetadataLookupParams{ Region: "region-2", Series: "raring", Architectures: []string{"amd64"}, Endpoint: "some-auth-url", Sources: []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", s.toolsURL(), utils.VerifySSLHostnames)}, }, } versions, resolveInfo, err := ValidateToolsMetadata(params) c.Assert(err, gc.IsNil) c.Assert(versions, gc.DeepEquals, []string{"1.11.2-raring-amd64"}) c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: false, IndexURL: "file://" + path.Join(s.metadataDir, "tools/streams/v1/index.json"), MirrorURL: "", }) } func (s *ValidateSuite) TestMajorMinorVersionMatch(c *gc.C) { s.makeLocalMetadata(c, "1.11.2", "raring") params := &ToolsMetadataLookupParams{ Major: 1, Minor: 11, MetadataLookupParams: simplestreams.MetadataLookupParams{ Region: "region-2", Series: "raring", Architectures: []string{"amd64"}, Endpoint: "some-auth-url", Sources: []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", s.toolsURL(), utils.VerifySSLHostnames)}, }, } versions, resolveInfo, err := ValidateToolsMetadata(params) c.Assert(err, gc.IsNil) c.Assert(versions, gc.DeepEquals, []string{"1.11.2-raring-amd64"}) c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: false, IndexURL: "file://" + path.Join(s.metadataDir, "tools/streams/v1/index.json"), MirrorURL: "", }) } func (s *ValidateSuite) TestNoMatch(c *gc.C) { s.makeLocalMetadata(c, "1.11.2", "raring") params := &ToolsMetadataLookupParams{ Version: "1.11.2", MetadataLookupParams: simplestreams.MetadataLookupParams{ Region: "region-2", Series: "precise", Architectures: []string{"amd64"}, Endpoint: "some-auth-url", Sources: []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", s.toolsURL(), utils.VerifySSLHostnames)}, }, } _, _, err := ValidateToolsMetadata(params) c.Assert(err, gc.Not(gc.IsNil)) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/storage_test.go�������������������������0000644�0000153�0000161�00000006203�12321735642�026723� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" envtesting "launchpad.net/juju-core/environs/testing" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) type StorageSuite struct { env environs.Environ testbase.LoggingSuite dataDir string } var _ = gc.Suite(&StorageSuite{}) func (s *StorageSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) cfg, err := config.New(config.NoDefaults, dummy.SampleConfig()) c.Assert(err, gc.IsNil) s.env, err = environs.Prepare(cfg, testing.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) s.dataDir = c.MkDir() } func (s *StorageSuite) TearDownTest(c *gc.C) { dummy.Reset() s.LoggingSuite.TearDownTest(c) } func (s *StorageSuite) TestStorageName(c *gc.C) { vers := version.MustParseBinary("1.2.3-precise-amd64") path := envtools.StorageName(vers) c.Assert(path, gc.Equals, "tools/releases/juju-1.2.3-precise-amd64.tgz") } func (s *StorageSuite) TestReadListEmpty(c *gc.C) { store := s.env.Storage() _, err := envtools.ReadList(store, 2, 0) c.Assert(err, gc.Equals, envtools.ErrNoTools) } func (s *StorageSuite) TestReadList(c *gc.C) { store := s.env.Storage() v001 := version.MustParseBinary("0.0.1-precise-amd64") v100 := version.MustParseBinary("1.0.0-precise-amd64") v101 := version.MustParseBinary("1.0.1-precise-amd64") v111 := version.MustParseBinary("1.1.1-precise-amd64") agentTools := envtesting.AssertUploadFakeToolsVersions(c, store, v001, v100, v101, v111) t001 := agentTools[0] t100 := agentTools[1] t101 := agentTools[2] t111 := agentTools[3] for i, t := range []struct { majorVersion, minorVersion int list coretools.List }{{ 0, 0, coretools.List{t001}, }, { 1, 0, coretools.List{t100, t101}, }, { 1, 1, coretools.List{t111}, }, { 1, -1, coretools.List{t100, t101, t111}, }, { 1, 2, nil, }, { 2, 0, nil, }} { c.Logf("test %d", i) list, err := envtools.ReadList(store, t.majorVersion, t.minorVersion) if t.list != nil { c.Assert(err, gc.IsNil) // ReadList doesn't set the Size of SHA256, so blank out those attributes. for _, tool := range t.list { tool.Size = 0 tool.SHA256 = "" } c.Assert(list, gc.DeepEquals, t.list) } else { c.Assert(err, gc.Equals, coretools.ErrNoMatches) } } } var setenvTests = []struct { set string expect []string }{ {"foo=1", []string{"foo=1", "arble="}}, {"foo=", []string{"foo=", "arble="}}, {"arble=23", []string{"foo=bar", "arble=23"}}, {"zaphod=42", []string{"foo=bar", "arble=", "zaphod=42"}}, } func (*StorageSuite) TestSetenv(c *gc.C) { env0 := []string{"foo=bar", "arble="} for i, t := range setenvTests { c.Logf("test %d", i) env := make([]string, len(env0)) copy(env, env0) env = envtools.Setenv(env, t.set) c.Check(env, gc.DeepEquals, t.expect) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/marshal.go������������������������������0000644�0000153�0000161�00000006157�12321735642�025657� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The tools package supports locating, parsing, and filtering Ubuntu tools metadata in simplestreams format. // See http://launchpad.net/simplestreams and in particular the doc/README file in that project for more information // about the file formats. package tools import ( "encoding/json" "fmt" "time" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/utils/set" ) const ( ProductMetadataPath = "streams/v1/com.ubuntu.juju:released:tools.json" ToolsContentId = "com.ubuntu.juju:released:tools" ) // MarshalToolsMetadataJSON marshals tools metadata to index and products JSON. // // updated is the time at which the JSON file was updated. func MarshalToolsMetadataJSON(metadata []*ToolsMetadata, updated time.Time) (index, products []byte, err error) { if index, err = MarshalToolsMetadataIndexJSON(metadata, updated); err != nil { return nil, nil, err } if products, err = MarshalToolsMetadataProductsJSON(metadata, updated); err != nil { return nil, nil, err } return index, products, err } // MarshalToolsMetadataIndexJSON marshals tools metadata to index JSON. // // updated is the time at which the JSON file was updated. func MarshalToolsMetadataIndexJSON(metadata []*ToolsMetadata, updated time.Time) (out []byte, err error) { productIds := make([]string, len(metadata)) for i, t := range metadata { productIds[i], err = t.productId() if err != nil { return nil, err } } var indices simplestreams.Indices indices.Updated = updated.Format(time.RFC1123Z) indices.Format = "index:1.0" indices.Indexes = map[string]*simplestreams.IndexMetadata{ ToolsContentId: &simplestreams.IndexMetadata{ Updated: indices.Updated, Format: "products:1.0", DataType: "content-download", ProductsFilePath: ProductMetadataPath, ProductIds: set.NewStrings(productIds...).SortedValues(), }, } return json.MarshalIndent(&indices, "", " ") } // MarshalToolsMetadataProductsJSON marshals tools metadata to products JSON. // // updated is the time at which the JSON file was updated. func MarshalToolsMetadataProductsJSON(metadata []*ToolsMetadata, updated time.Time) (out []byte, err error) { var cloud simplestreams.CloudMetadata cloud.Updated = updated.Format(time.RFC1123Z) cloud.Format = "products:1.0" cloud.ContentId = ToolsContentId cloud.Products = make(map[string]simplestreams.MetadataCatalog) itemsversion := updated.Format("20060102") // YYYYMMDD for _, t := range metadata { id, err := t.productId() if err != nil { return nil, err } itemid := fmt.Sprintf("%s-%s-%s", t.Version, t.Release, t.Arch) if catalog, ok := cloud.Products[id]; ok { catalog.Items[itemsversion].Items[itemid] = t } else { catalog = simplestreams.MetadataCatalog{ Arch: t.Arch, Version: t.Version, Items: map[string]*simplestreams.ItemCollection{ itemsversion: &simplestreams.ItemCollection{ Items: map[string]interface{}{itemid: t}, }, }, } cloud.Products[id] = catalog } } return json.MarshalIndent(&cloud, "", " ") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/testing/��������������������������������0000755�0000153�0000161�00000000000�12321735643�025346� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/tools/testing/testing.go����������������������0000644�0000153�0000161�00000017006�12321735642�027355� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "bytes" "fmt" "io/ioutil" "os" "path" "path/filepath" "sort" "strings" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/tools" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/version" ) // MakeTools creates some fake tools with the given version strings. func MakeTools(c *gc.C, metadataDir, subdir string, versionStrings []string) coretools.List { return makeTools(c, metadataDir, subdir, versionStrings, false) } // MakeTools creates some fake tools (including checksums) with the given version strings. func MakeToolsWithCheckSum(c *gc.C, metadataDir, subdir string, versionStrings []string) coretools.List { return makeTools(c, metadataDir, subdir, versionStrings, true) } func makeTools(c *gc.C, metadataDir, subdir string, versionStrings []string, withCheckSum bool) coretools.List { toolsDir := filepath.Join(metadataDir, storage.BaseToolsPath) if subdir != "" { toolsDir = filepath.Join(toolsDir, subdir) } c.Assert(os.MkdirAll(toolsDir, 0755), gc.IsNil) var toolsList coretools.List for _, versionString := range versionStrings { binary := version.MustParseBinary(versionString) path := filepath.Join(toolsDir, fmt.Sprintf("juju-%s.tgz", binary)) data := binary.String() err := ioutil.WriteFile(path, []byte(data), 0644) c.Assert(err, gc.IsNil) tool := &coretools.Tools{ Version: binary, URL: path, } if withCheckSum { tool.Size, tool.SHA256 = SHA256sum(c, path) } toolsList = append(toolsList, tool) } // Write the tools metadata. stor, err := filestorage.NewFileStorageWriter(metadataDir) c.Assert(err, gc.IsNil) err = tools.MergeAndWriteMetadata(stor, toolsList, false) c.Assert(err, gc.IsNil) return toolsList } // SHA256sum creates the sha256 checksum for the specified file. func SHA256sum(c *gc.C, path string) (int64, string) { if strings.HasPrefix(path, "file://") { path = path[len("file://"):] } hash, size, err := utils.ReadFileSHA256(path) c.Assert(err, gc.IsNil) return size, hash } // ParseMetadataFromDir loads ToolsMetadata from the specified directory. func ParseMetadataFromDir(c *gc.C, metadataDir string, expectMirrors bool) []*tools.ToolsMetadata { stor, err := filestorage.NewFileStorageReader(metadataDir) c.Assert(err, gc.IsNil) return ParseMetadataFromStorage(c, stor, expectMirrors) } // ParseMetadataFromStorage loads ToolsMetadata from the specified storage reader. func ParseMetadataFromStorage(c *gc.C, stor storage.StorageReader, expectMirrors bool) []*tools.ToolsMetadata { source := storage.NewStorageSimpleStreamsDataSource("test storage reader", stor, "tools") params := simplestreams.ValueParams{ DataType: tools.ContentDownload, ValueTemplate: tools.ToolsMetadata{}, } const requireSigned = false indexPath := simplestreams.UnsignedIndex indexRef, err := simplestreams.GetIndexWithFormat( source, indexPath, "index:1.0", requireSigned, simplestreams.CloudSpec{}, params) c.Assert(err, gc.IsNil) c.Assert(indexRef.Indexes, gc.HasLen, 1) toolsIndexMetadata := indexRef.Indexes["com.ubuntu.juju:released:tools"] c.Assert(toolsIndexMetadata, gc.NotNil) // Read the products file contents. r, err := stor.Get(path.Join("tools", toolsIndexMetadata.ProductsFilePath)) defer r.Close() c.Assert(err, gc.IsNil) data, err := ioutil.ReadAll(r) c.Assert(err, gc.IsNil) url, err := source.URL(toolsIndexMetadata.ProductsFilePath) c.Assert(err, gc.IsNil) cloudMetadata, err := simplestreams.ParseCloudMetadata(data, "products:1.0", url, tools.ToolsMetadata{}) c.Assert(err, gc.IsNil) toolsMetadataMap := make(map[string]*tools.ToolsMetadata) var expectedProductIds set.Strings var toolsVersions set.Strings for _, mc := range cloudMetadata.Products { for _, items := range mc.Items { for key, item := range items.Items { toolsMetadata := item.(*tools.ToolsMetadata) toolsMetadataMap[key] = toolsMetadata toolsVersions.Add(key) seriesVersion, err := simplestreams.SeriesVersion(toolsMetadata.Release) c.Assert(err, gc.IsNil) productId := fmt.Sprintf("com.ubuntu.juju:%s:%s", seriesVersion, toolsMetadata.Arch) expectedProductIds.Add(productId) } } } // Make sure index's product IDs are all represented in the products metadata. sort.Strings(toolsIndexMetadata.ProductIds) c.Assert(toolsIndexMetadata.ProductIds, gc.DeepEquals, expectedProductIds.SortedValues()) toolsMetadata := make([]*tools.ToolsMetadata, len(toolsMetadataMap)) for i, key := range toolsVersions.SortedValues() { toolsMetadata[i] = toolsMetadataMap[key] } if expectMirrors { r, err = stor.Get(path.Join("tools", simplestreams.UnsignedMirror)) defer r.Close() c.Assert(err, gc.IsNil) data, err = ioutil.ReadAll(r) c.Assert(err, gc.IsNil) c.Assert(string(data), jc.Contains, `"mirrors":`) c.Assert(err, gc.IsNil) } return toolsMetadata } type metadataFile struct { path string data []byte } func generateMetadata(c *gc.C, versions ...version.Binary) []metadataFile { var metadata = make([]*tools.ToolsMetadata, len(versions)) for i, vers := range versions { basePath := fmt.Sprintf("releases/tools-%s.tar.gz", vers.String()) metadata[i] = &tools.ToolsMetadata{ Release: vers.Series, Version: vers.Number.String(), Arch: vers.Arch, Path: basePath, } } index, products, err := tools.MarshalToolsMetadataJSON(metadata, time.Now()) c.Assert(err, gc.IsNil) objects := []metadataFile{ {simplestreams.UnsignedIndex, index}, {tools.ProductMetadataPath, products}, } return objects } // UploadToStorage uploads tools and metadata for the specified versions to storage. func UploadToStorage(c *gc.C, stor storage.Storage, versions ...version.Binary) map[version.Binary]string { uploaded := map[version.Binary]string{} if len(versions) == 0 { return uploaded } var err error for _, vers := range versions { filename := fmt.Sprintf("tools/releases/tools-%s.tar.gz", vers.String()) // Put a file in images since the dummy storage provider requires a // file to exist before the URL can be found. This is to ensure it behaves // the same way as MAAS. err = stor.Put(filename, strings.NewReader("dummy"), 5) c.Assert(err, gc.IsNil) uploaded[vers], err = stor.URL(filename) c.Assert(err, gc.IsNil) } objects := generateMetadata(c, versions...) for _, object := range objects { toolspath := path.Join("tools", object.path) err = stor.Put(toolspath, bytes.NewReader(object.data), int64(len(object.data))) c.Assert(err, gc.IsNil) } return uploaded } // UploadToStorage uploads tools and metadata for the specified versions to dir. func UploadToDirectory(c *gc.C, dir string, versions ...version.Binary) map[version.Binary]string { uploaded := map[version.Binary]string{} if len(versions) == 0 { return uploaded } for _, vers := range versions { basePath := fmt.Sprintf("releases/tools-%s.tar.gz", vers.String()) uploaded[vers] = fmt.Sprintf("file://%s/%s", dir, basePath) } objects := generateMetadata(c, versions...) for _, object := range objects { path := filepath.Join(dir, object.path) dir := filepath.Dir(path) if err := os.MkdirAll(dir, 0755); err != nil && !os.IsExist(err) { c.Assert(err, gc.IsNil) } err := ioutil.WriteFile(path, object.data, 0644) c.Assert(err, gc.IsNil) } return uploaded } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/boilerplate_config.go�������������������������0000644�0000153�0000161�00000005561�12321735642�026715� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs import ( "bytes" "crypto/rand" "fmt" "io" "text/template" ) var configHeader = ` # This is the Juju config file, which you can use to specify multiple # environments in which to deploy. By default Juju ships with AWS # (default), HP Cloud, OpenStack, Azure, MaaS, Local and Manual # providers. See https://juju.ubuntu.com/docs for more information # An environment configuration must always specify at least the # following information: # - name (to identify the environment) # - type (to specify the provider) # In the following example the name is "myenv" and type is "ec2". # myenv: # type: ec2 # Values in <brackets> below need to be filled in by the user. # Optional attributes are shown commented out, with # a sample value or a value in <brackets>. # There are several settings supported by all environments, all of which # are optional and have specified default values. For more info, see the # Juju documentation. # The default environment is chosen when an environment is not # specified using any of the following, in descending order of precedence: # 1. -e or --environment command line parameter, passed after the command, e.g. # $ juju add-unit -e myenv myservice # 2. By setting JUJU_ENV environment variable. # 3. Using the juju switch command like this: # $ juju switch myenv # default: amazon environments: `[1:] func randomKey() string { buf := make([]byte, 16) _, err := io.ReadFull(rand.Reader, buf) if err != nil { panic(fmt.Errorf("error from crypto rand: %v", err)) } return fmt.Sprintf("%x", buf) } // BoilerplateConfig returns a sample juju configuration. func BoilerplateConfig() string { var config bytes.Buffer config.WriteString(configHeader) for name, p := range providers { t, err := parseTemplate(p.BoilerplateConfig()) if err != nil { panic(fmt.Errorf("cannot parse boilerplate from %s: %v", name, err)) } var ecfg bytes.Buffer if err := t.Execute(&ecfg, nil); err != nil { panic(fmt.Errorf("cannot generate boilerplate from %s: %v", name, err)) } indent(&config, ecfg.Bytes(), " ") } // Sanity check to ensure the boilerplate parses. _, err := ReadEnvironsBytes(config.Bytes()) if err != nil { panic(fmt.Errorf("cannot parse %s:\n%v", config.String(), err)) } return config.String() } func parseTemplate(s string) (*template.Template, error) { t := template.New("") t.Funcs(template.FuncMap{"rand": randomKey}) return t.Parse(s) } // indent appends the given text to the given buffer indented by the given indent string. func indent(b *bytes.Buffer, text []byte, indentStr string) { for { if len(text) == 0 { return } b.WriteString(indentStr) i := bytes.IndexByte(text, '\n') if i == -1 { b.Write(text) b.WriteRune('\n') return } i++ b.Write(text[0:i]) text = text[i:] } } �����������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/bootstrap/������������������������������������0000755�0000153�0000161�00000000000�12321736000�024532� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/bootstrap/interruptiblestorage.go�������������0000644�0000153�0000161�00000002654�12321735642�031360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package bootstrap import ( "errors" "io" "launchpad.net/juju-core/environs/storage" ) var interruptedError = errors.New("interrupted") // interruptibleStorage is a storage.Storage that sits // between the user and another Storage, allowing the // Put method to be interrupted. type interruptibleStorage struct { storage.Storage interrupt <-chan struct{} } // newInterruptibleStorage wraps the provided Storage so that Put // will immediately return an error if the provided channel is // closed. func newInterruptibleStorage(s storage.Storage, interrupt <-chan struct{}) storage.Storage { return &interruptibleStorage{s, interrupt} } type interruptibleReader struct { io.Reader interrupt <-chan struct{} } func (r *interruptibleReader) Read(p []byte) (int, error) { // if the interrupt channel is already // closed, just drop out immediately. select { case <-r.interrupt: return 0, interruptedError default: } // read and wait for interruption concurrently var n int var err error done := make(chan struct{}) go func() { defer close(done) n, err = r.Reader.Read(p) }() select { case <-done: return n, err case <-r.interrupt: return 0, interruptedError } } func (s *interruptibleStorage) Put(name string, r io.Reader, length int64) error { return s.Storage.Put(name, &interruptibleReader{r, s.interrupt}, length) } ������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/bootstrap/export_test.go����������������������0000644�0000153�0000161�00000000254�12321735642�027455� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package bootstrap var ( NewInterruptibleStorage = newInterruptibleStorage ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/bootstrap/synctools.go������������������������0000644�0000153�0000161�00000015456�12321735642�027144� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package bootstrap import ( "fmt" "os" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/sync" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju/arch" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/version" ) const noToolsMessage = `Juju cannot bootstrap because no tools are available for your environment. An attempt was made to build and upload appropriate tools but this was unsuccessful. ` const noToolsNoUploadMessage = `Juju cannot bootstrap because no tools are available for your environment. You may want to use the 'tools-metadata-url' configuration setting to specify the tools location. ` // UploadTools uploads tools for the specified series and any other relevant series to // the environment storage, after which it sets the agent-version. If forceVersion is true, // we allow uploading even when the agent-version is already set in the environment. func UploadTools(ctx environs.BootstrapContext, env environs.Environ, toolsArch *string, forceVersion bool, bootstrapSeries ...string) error { logger.Infof("checking that upload is possible") // Check the series are valid. for _, series := range bootstrapSeries { if _, err := simplestreams.SeriesVersion(series); err != nil { return err } } // See that we are allowed to upload the tools. if err := validateUploadAllowed(env, toolsArch, forceVersion); err != nil { return err } // Make storage interruptible. interrupted := make(chan os.Signal, 1) interruptStorage := make(chan struct{}) ctx.InterruptNotify(interrupted) defer ctx.StopInterruptNotify(interrupted) defer close(interrupted) go func() { defer close(interruptStorage) // closing interrupts all uploads if _, ok := <-interrupted; ok { ctx.Infof("cancelling tools upload") } }() stor := newInterruptibleStorage(env.Storage(), interruptStorage) cfg := env.Config() explicitVersion := uploadVersion(version.Current.Number, nil) uploadSeries := SeriesToUpload(cfg, bootstrapSeries) ctx.Infof("uploading tools for series %s", uploadSeries) tools, err := sync.Upload(stor, &explicitVersion, uploadSeries...) if err != nil { return err } cfg, err = cfg.Apply(map[string]interface{}{ "agent-version": tools.Version.Number.String(), }) if err == nil { err = env.SetConfig(cfg) } if err != nil { return fmt.Errorf("failed to update environment configuration: %v", err) } return nil } // uploadVersion returns a copy of the supplied version with a build number // higher than any of the supplied tools that share its major, minor and patch. func uploadVersion(vers version.Number, existing coretools.List) version.Number { vers.Build++ for _, t := range existing { if t.Version.Major != vers.Major || t.Version.Minor != vers.Minor || t.Version.Patch != vers.Patch { continue } if t.Version.Build >= vers.Build { vers.Build = t.Version.Build + 1 } } return vers } // SeriesToUpload returns the supplied series with duplicates removed if // non-empty; otherwise it returns a default list of series we should // probably upload, based on cfg. func SeriesToUpload(cfg *config.Config, series []string) []string { unique := set.NewStrings(series...) if unique.IsEmpty() { unique.Add(version.Current.Series) unique.Add(config.LatestLtsSeries()) if series, ok := cfg.DefaultSeries(); ok { unique.Add(series) } } return unique.Values() } // validateUploadAllowed returns an error if an attempt to upload tools should // not be allowed. func validateUploadAllowed(env environs.Environ, toolsArch *string, forceVersion bool) error { if !forceVersion { // First, check that there isn't already an agent version specified. if _, hasAgentVersion := env.Config().AgentVersion(); hasAgentVersion { return fmt.Errorf(noToolsNoUploadMessage) } } // Now check that the architecture for which we are setting up an // environment matches that from which we are bootstrapping. hostArch := arch.HostArch() // We can't build tools for a different architecture if one is specified. if toolsArch != nil && *toolsArch != hostArch { return fmt.Errorf("cannot build tools for %q using a machine running on %q", *toolsArch, hostArch) } // If no architecture is specified, ensure the target provider supports instances matching our architecture. supportedArchitectures, err := env.SupportedArchitectures() if err != nil { return fmt.Errorf( "no packaged tools available and cannot determine environment's supported architectures: %v", err) } archSupported := false for _, arch := range supportedArchitectures { if hostArch == arch { archSupported = true break } } if !archSupported { envType := env.Config().Type() return fmt.Errorf( "environment %q of type %s does not support instances running on %q", env.Name(), envType, hostArch) } return nil } // EnsureToolsAvailability verifies the tools are available. If no tools are // found, it will automatically synchronize them. func EnsureToolsAvailability(ctx environs.BootstrapContext, env environs.Environ, series string, toolsArch *string) (coretools.List, error) { cfg := env.Config() var vers *version.Number if agentVersion, ok := cfg.AgentVersion(); ok { vers = &agentVersion } logger.Debugf( "looking for bootstrap tools: series=%q, arch=%v, version=%v", series, toolsArch, vers, ) params := envtools.BootstrapToolsParams{ Version: vers, Arch: toolsArch, Series: series, // If vers.Build>0, the tools may have been uploaded in this session. // Allow retries, so we wait until the storage has caught up. AllowRetry: vers != nil && vers.Build > 0, } toolsList, err := envtools.FindBootstrapTools(env, params) if err == nil { return toolsList, nil } else if !errors.IsNotFoundError(err) { return nil, err } // Only automatically upload tools for dev versions. if !version.Current.IsDev() { return nil, fmt.Errorf("cannot upload bootstrap tools: %v", noToolsNoUploadMessage) } // No tools available so our only hope is to build locally and upload. logger.Warningf("no prepackaged tools available") uploadSeries := SeriesToUpload(cfg, nil) if series != "" { uploadSeries = append(uploadSeries, series) } if err := UploadTools(ctx, env, toolsArch, false, uploadSeries...); err != nil { logger.Errorf("%s", noToolsMessage) return nil, fmt.Errorf("cannot upload bootstrap tools: %v", err) } // TODO(axw) have uploadTools return the list of tools in the target, and use that. params.AllowRetry = true if toolsList, err = envtools.FindBootstrapTools(env, params); err != nil { return nil, fmt.Errorf("cannot find bootstrap tools: %v", err) } return toolsList, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/bootstrap/interruptiblestorage_test.go��������0000644�0000153�0000161�00000003614�12321735642�032414� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package bootstrap_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/bootstrap" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/testing/testbase" ) type interruptibleStorageSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&interruptibleStorageSuite{}) type errorReader struct { close chan struct{} wait chan struct{} called int err error } func (r *errorReader) Read(buf []byte) (int, error) { if r.close != nil { close(r.close) } if r.wait != nil { <-r.wait } r.called++ return 0, r.err } func (s *interruptibleStorageSuite) TestInterruptStorage(c *gc.C) { closer, stor, _ := envtesting.CreateLocalTestStorage(c) s.AddCleanup(func(c *gc.C) { closer.Close() }) reader := &errorReader{ err: fmt.Errorf("read failed"), } interrupted := make(chan struct{}) istor := bootstrap.NewInterruptibleStorage(stor, interrupted) err := istor.Put("name", reader, 3) c.Assert(err, gc.ErrorMatches, ".*: read failed") c.Assert(reader.called, gc.Equals, 1) // If the channel is already closed, then the // underlying reader is never deferred to. close(interrupted) err = istor.Put("name", reader, 3) c.Assert(err, gc.ErrorMatches, ".*: interrupted") c.Assert(reader.called, gc.Equals, 1) } func (s *interruptibleStorageSuite) TestInterruptStorageConcurrently(c *gc.C) { closer, stor, _ := envtesting.CreateLocalTestStorage(c) s.AddCleanup(func(c *gc.C) { closer.Close() }) reader := &errorReader{ close: make(chan struct{}), wait: make(chan struct{}), err: fmt.Errorf("read failed"), } istor := bootstrap.NewInterruptibleStorage(stor, reader.close) err := istor.Put("name", reader, 3) c.Assert(err, gc.ErrorMatches, ".*: interrupted") c.Assert(reader.called, gc.Equals, 0) // reader is blocked close(reader.wait) } ��������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/bootstrap/bootstrap_test.go�������������������0000644�0000153�0000161�00000042010�12321735776�030155� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package bootstrap_test import ( "fmt" "strings" stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/sync" envtesting "launchpad.net/juju-core/environs/testing" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/provider/dummy" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) func TestPackage(t *stdtesting.T) { gc.TestingT(t) } const ( useDefaultKeys = true noKeysDefined = false ) type bootstrapSuite struct { home *coretesting.FakeHome testbase.LoggingSuite envtesting.ToolsFixture } var _ = gc.Suite(&bootstrapSuite{}) func (s *bootstrapSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) s.home = coretesting.MakeFakeHomeNoEnvironments(c, "foo") } func (s *bootstrapSuite) TearDownTest(c *gc.C) { s.home.Restore() s.ToolsFixture.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *bootstrapSuite) TestBootstrapNeedsSettings(c *gc.C) { env := newEnviron("bar", noKeysDefined, nil) s.setDummyStorage(c, env) fixEnv := func(key string, value interface{}) { cfg, err := env.Config().Apply(map[string]interface{}{ key: value, }) c.Assert(err, gc.IsNil) env.cfg = cfg } err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.ErrorMatches, "environment configuration has no admin-secret") fixEnv("admin-secret", "whatever") err = bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.ErrorMatches, "environment configuration has no ca-cert") fixEnv("ca-cert", coretesting.CACert) err = bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.ErrorMatches, "environment configuration has no ca-private-key") fixEnv("ca-private-key", coretesting.CAKey) uploadTools(c, env) err = bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) } func uploadTools(c *gc.C, env environs.Environ) { usefulVersion := version.Current usefulVersion.Series = config.PreferredSeries(env.Config()) envtesting.AssertUploadFakeToolsVersions(c, env.Storage(), usefulVersion) } func (s *bootstrapSuite) TestBootstrapEmptyConstraints(c *gc.C) { env := newEnviron("foo", useDefaultKeys, nil) s.setDummyStorage(c, env) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) c.Assert(env.bootstrapCount, gc.Equals, 1) c.Assert(env.constraints, gc.DeepEquals, constraints.Value{}) } func (s *bootstrapSuite) TestBootstrapSpecifiedConstraints(c *gc.C) { env := newEnviron("foo", useDefaultKeys, nil) s.setDummyStorage(c, env) cons := constraints.MustParse("cpu-cores=2 mem=4G") err := bootstrap.Bootstrap(coretesting.Context(c), env, cons) c.Assert(err, gc.IsNil) c.Assert(env.bootstrapCount, gc.Equals, 1) c.Assert(env.constraints, gc.DeepEquals, cons) } var bootstrapSetAgentVersionTests = []envtesting.BootstrapToolsTest{ { Info: "released cli with dev setting picks newest matching 1", Available: envtesting.V100Xall, CliVersion: envtesting.V100q32, DefaultSeries: "precise", Development: true, Expect: []version.Binary{envtesting.V1001p64}, }, { Info: "released cli with dev setting picks newest matching 2", Available: envtesting.V1all, CliVersion: envtesting.V120q64, DefaultSeries: "precise", Development: true, Arch: "i386", Expect: []version.Binary{envtesting.V120p32}, }, { Info: "dev cli picks newest matching 1", Available: envtesting.V110Xall, CliVersion: envtesting.V110q32, DefaultSeries: "precise", Expect: []version.Binary{envtesting.V1101p64}, }, { Info: "dev cli picks newest matching 2", Available: envtesting.V1all, CliVersion: envtesting.V120q64, DefaultSeries: "precise", Arch: "i386", Expect: []version.Binary{envtesting.V120p32}, }, { Info: "dev cli has different arch to available", Available: envtesting.V1all, CliVersion: envtesting.V310qppc64, DefaultSeries: "precise", Expect: []version.Binary{envtesting.V3101qppc64}, }} func (s *bootstrapSuite) TestBootstrapTools(c *gc.C) { allTests := append(envtesting.BootstrapToolsTests, bootstrapSetAgentVersionTests...) // version.Current is set in the loop so ensure it is restored later. s.PatchValue(&version.Current, version.Current) for i, test := range allTests { c.Logf("\ntest %d: %s", i, test.Info) dummy.Reset() attrs := dummy.SampleConfig().Merge(coretesting.Attrs{ "state-server": false, "development": test.Development, "default-series": test.DefaultSeries, }) if test.AgentVersion != version.Zero { attrs["agent-version"] = test.AgentVersion.String() } cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := environs.Prepare(cfg, coretesting.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) envtesting.RemoveAllTools(c, env) version.Current = test.CliVersion envtesting.AssertUploadFakeToolsVersions(c, env.Storage(), test.Available...) // Remove the default tools URL from the search path, just look in cloud storage. s.PatchValue(&envtools.DefaultBaseURL, "") cons := constraints.Value{} if test.Arch != "" { cons = constraints.MustParse("arch=" + test.Arch) } err = bootstrap.Bootstrap(coretesting.Context(c), env, cons) if test.Err != "" { c.Check(err, gc.NotNil) if err != nil { stripped := strings.Replace(err.Error(), "\n", "", -1) c.Check(stripped, gc.Matches, ".*"+stripped) } continue } else { c.Check(err, gc.IsNil) } unique := map[version.Number]bool{} for _, expected := range test.Expect { unique[expected.Number] = true } for expectAgentVersion := range unique { agentVersion, ok := env.Config().AgentVersion() c.Check(ok, gc.Equals, true) c.Check(agentVersion, gc.Equals, expectAgentVersion) } } } func (s *bootstrapSuite) TestBootstrapNoTools(c *gc.C) { env := newEnviron("foo", useDefaultKeys, nil) s.setDummyStorage(c, env) envtesting.RemoveFakeTools(c, env.Storage()) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) // bootstrap.Bootstrap leaves it to the provider to // locate bootstrap tools. c.Assert(err, gc.IsNil) } func (s *bootstrapSuite) TestEnsureToolsAvailabilityIncompatibleHostArch(c *gc.C) { // Host runs amd64, want ppc64 tools. s.PatchValue(&arch.HostArch, func() string { return "amd64" }) // Force a dev version by having an odd minor version number. // This is because we have not uploaded any tools and auto // upload is only enabled for dev versions. devVersion := version.Current devVersion.Minor = 11 s.PatchValue(&version.Current, devVersion) env := newEnviron("foo", useDefaultKeys, nil) s.setDummyStorage(c, env) envtesting.RemoveFakeTools(c, env.Storage()) arch := "ppc64" _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), &arch) c.Assert(err, gc.NotNil) stripped := strings.Replace(err.Error(), "\n", "", -1) c.Assert(stripped, gc.Matches, `cannot upload bootstrap tools: cannot build tools for "ppc64" using a machine running on "amd64"`) } func (s *bootstrapSuite) TestEnsureToolsAvailabilityIncompatibleTargetArch(c *gc.C) { // Host runs ppc64, environment only supports amd64, arm64. s.PatchValue(&arch.HostArch, func() string { return "ppc64" }) // Force a dev version by having an odd minor version number. // This is because we have not uploaded any tools and auto // upload is only enabled for dev versions. devVersion := version.Current devVersion.Minor = 11 s.PatchValue(&version.Current, devVersion) env := newEnviron("foo", useDefaultKeys, nil) s.setDummyStorage(c, env) envtesting.RemoveFakeTools(c, env.Storage()) _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), nil) c.Assert(err, gc.NotNil) stripped := strings.Replace(err.Error(), "\n", "", -1) c.Assert(stripped, gc.Matches, `cannot upload bootstrap tools: environment "foo" of type dummy does not support instances running on "ppc64"`) } func (s *bootstrapSuite) TestEnsureToolsAvailabilityAgentVersionAlreadySet(c *gc.C) { // Can't upload tools if agent version already set. env := newEnviron("foo", useDefaultKeys, map[string]interface{}{"agent-version": "1.16.0"}) s.setDummyStorage(c, env) envtesting.RemoveFakeTools(c, env.Storage()) _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), nil) c.Assert(err, gc.NotNil) stripped := strings.Replace(err.Error(), "\n", "", -1) c.Assert(stripped, gc.Matches, "cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment.*") } func (s *bootstrapSuite) TestEnsureToolsAvailabilityNonDevVersion(c *gc.C) { // Can't automatically upload tools for released versions. s.PatchValue(&version.Current, version.MustParseBinary("1.18.0-trusty-arm64")) env := newEnviron("foo", useDefaultKeys, nil) s.setDummyStorage(c, env) envtesting.RemoveFakeTools(c, env.Storage()) _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), nil) c.Assert(err, gc.NotNil) stripped := strings.Replace(err.Error(), "\n", "", -1) c.Assert(stripped, gc.Matches, "cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment.*") } // getMockBuildTools returns a sync.BuildToolsTarballFunc implementation which generates // a fake tools tarball. func (s *bootstrapSuite) getMockBuildTools(c *gc.C) sync.BuildToolsTarballFunc { toolsDir := c.MkDir() return func(forceVersion *version.Number) (*sync.BuiltTools, error) { // UploadFakeToolsVersions requires a storage to write to. stor, err := filestorage.NewFileStorageWriter(toolsDir) c.Assert(err, gc.IsNil) vers := version.Current if forceVersion != nil { vers.Number = *forceVersion } versions := []version.Binary{vers} uploadedTools, err := envtesting.UploadFakeToolsVersions(stor, versions...) c.Assert(err, gc.IsNil) agentTools := uploadedTools[0] return &sync.BuiltTools{ Dir: toolsDir, StorageName: envtools.StorageName(vers), Version: vers, Size: agentTools.Size, Sha256Hash: agentTools.SHA256, }, nil } } func (s *bootstrapSuite) TestEnsureToolsAvailability(c *gc.C) { existingToolsVersion := version.MustParseBinary("1.19.0-trusty-amd64") s.PatchValue(&version.Current, existingToolsVersion) env := newEnviron("foo", useDefaultKeys, nil) s.setDummyStorage(c, env) // At this point, as a result of setDummyStorage, env has tools for amd64 uploaded. // Set version.Current to be arm64 to simulate a different CLI version. cliVersion := version.Current cliVersion.Arch = "arm64" version.Current = cliVersion s.PatchValue(&sync.BuildToolsTarball, s.getMockBuildTools(c)) // Host runs arm64, environment supports arm64. s.PatchValue(&arch.HostArch, func() string { return "arm64" }) arch := "arm64" agentTools, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), &arch) c.Assert(err, gc.IsNil) c.Assert(agentTools, gc.HasLen, 1) expectedVers := version.Current expectedVers.Number.Build++ expectedVers.Series = config.PreferredSeries(env.Config()) c.Assert(agentTools[0].Version, gc.DeepEquals, expectedVers) } func (s *bootstrapSuite) TestSeriesToUpload(c *gc.C) { vers := version.Current vers.Series = "quantal" s.PatchValue(&version.Current, vers) env := newEnviron("foo", useDefaultKeys, nil) cfg := env.Config() prefSeries := config.PreferredSeries(cfg) expect := []string{"quantal", prefSeries} if prefSeries != config.LatestLtsSeries() { expect = append(expect, config.LatestLtsSeries()) } c.Assert(bootstrap.SeriesToUpload(cfg, nil), jc.SameContents, expect) c.Assert(bootstrap.SeriesToUpload(cfg, []string{"quantal"}), jc.SameContents, []string{"quantal"}) env = newEnviron("foo", useDefaultKeys, map[string]interface{}{"default-series": "lucid"}) cfg = env.Config() c.Assert(bootstrap.SeriesToUpload(cfg, nil), jc.SameContents, []string{"quantal", config.LatestLtsSeries(), "lucid"}) } func (s *bootstrapSuite) assertUploadTools(c *gc.C, vers version.Binary, forceVersion bool, extraConfig map[string]interface{}, errMessage string) { s.PatchValue(&version.Current, vers) // If we allow released tools to be uploaded, the build number is incremented so in that case // we need to ensure the environment is set up to allow dev tools to be used. env := newEnviron("foo", useDefaultKeys, extraConfig) s.setDummyStorage(c, env) envtesting.RemoveFakeTools(c, env.Storage()) // At this point, as a result of setDummyStorage, env has tools for amd64 uploaded. // Set version.Current to be arm64 to simulate a different CLI version. cliVersion := version.Current cliVersion.Arch = "arm64" version.Current = cliVersion s.PatchValue(&sync.BuildToolsTarball, s.getMockBuildTools(c)) // Host runs arm64, environment supports arm64. s.PatchValue(&arch.HostArch, func() string { return "arm64" }) arch := "arm64" err := bootstrap.UploadTools(coretesting.Context(c), env, &arch, forceVersion, "precise") if errMessage != "" { c.Assert(err, gc.NotNil) stripped := strings.Replace(err.Error(), "\n", "", -1) c.Assert(stripped, gc.Matches, errMessage) return } c.Assert(err, gc.IsNil) params := envtools.BootstrapToolsParams{ Arch: &arch, Series: version.Current.Series, } agentTools, err := envtools.FindBootstrapTools(env, params) c.Assert(err, gc.IsNil) c.Assert(agentTools, gc.HasLen, 1) expectedVers := vers expectedVers.Number.Build++ expectedVers.Series = version.Current.Series c.Assert(agentTools[0].Version, gc.DeepEquals, expectedVers) } func (s *bootstrapSuite) TestUploadTools(c *gc.C) { vers := version.MustParseBinary("1.19.0-trusty-arm64") s.assertUploadTools(c, vers, false, nil, "") } func (s *bootstrapSuite) TestUploadToolsReleaseToolsWithDevConfig(c *gc.C) { vers := version.MustParseBinary("1.18.0-trusty-arm64") extraCfg := map[string]interface{}{"development": true} s.assertUploadTools(c, vers, false, extraCfg, "") } func (s *bootstrapSuite) TestUploadToolsForceVersionAllowsAgentVersionSet(c *gc.C) { vers := version.MustParseBinary("1.18.0-trusty-arm64") extraCfg := map[string]interface{}{"agent-version": "1.18.0", "development": true} s.assertUploadTools(c, vers, true, extraCfg, "") } type bootstrapEnviron struct { name string cfg *config.Config environs.Environ // stub out all methods we don't care about. // The following fields are filled in when Bootstrap is called. bootstrapCount int constraints constraints.Value storage storage.Storage } var _ envtools.SupportsCustomSources = (*bootstrapEnviron)(nil) // GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata. func (e *bootstrapEnviron) GetToolsSources() ([]simplestreams.DataSource, error) { // Add the simplestreams source off the control bucket. return []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}, nil } func newEnviron(name string, defaultKeys bool, extraAttrs map[string]interface{}) *bootstrapEnviron { m := dummy.SampleConfig().Merge(extraAttrs) if !defaultKeys { m = m.Delete( "ca-cert", "ca-private-key", "admin-secret", ) } cfg, err := config.New(config.NoDefaults, m) if err != nil { panic(fmt.Errorf("cannot create config from %#v: %v", m, err)) } return &bootstrapEnviron{ name: name, cfg: cfg, } } // setDummyStorage injects the local provider's fake storage implementation // into the given environment, so that tests can manipulate storage as if it // were real. func (s *bootstrapSuite) setDummyStorage(c *gc.C, env *bootstrapEnviron) { closer, stor, _ := envtesting.CreateLocalTestStorage(c) env.storage = stor envtesting.UploadFakeTools(c, env.storage) s.AddCleanup(func(c *gc.C) { closer.Close() }) } func (e *bootstrapEnviron) Name() string { return e.name } func (e *bootstrapEnviron) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { e.bootstrapCount++ e.constraints = cons return nil } func (e *bootstrapEnviron) Config() *config.Config { return e.cfg } func (e *bootstrapEnviron) SetConfig(cfg *config.Config) error { e.cfg = cfg return nil } func (e *bootstrapEnviron) Storage() storage.Storage { return e.storage } func (e *bootstrapEnviron) SupportedArchitectures() ([]string, error) { return []string{"amd64", "arm64"}, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/bootstrap/state.go����������������������������0000644�0000153�0000161�00000005573�12321735776�026236� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package bootstrap import ( "bytes" "fmt" "io" "io/ioutil" "launchpad.net/goyaml" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/storage" coreerrors "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" ) // StateFile is the name of the file where the provider's state is stored. const StateFile = "provider-state" // BootstrapState is the state information that is stored in StateFile. // // Individual providers may define their own state structures instead of // this one, and use their own code for loading and saving those, but this is // the definition that most practically useful providers share unchanged. type BootstrapState struct { // StateInstances are the state servers. StateInstances []instance.Id `yaml:"state-instances"` // Characteristics reflect the hardware each state server is running on. // This is used at bootstrap time so the state server knows what hardware it has. // The state *may* be updated later without this information, but by then it's // served its purpose. Characteristics []instance.HardwareCharacteristics `yaml:"characteristics,omitempty"` } // putState writes the given data to the state file on the given storage. // The file's name is as defined in StateFile. func putState(stor storage.StorageWriter, data []byte) error { logger.Debugf("putting %q to bootstrap storage %T", StateFile, stor) return stor.Put(StateFile, bytes.NewBuffer(data), int64(len(data))) } // CreateStateFile creates an empty state file on the given storage, and // returns its URL. func CreateStateFile(stor storage.Storage) (string, error) { err := putState(stor, []byte{}) if err != nil { return "", fmt.Errorf("cannot create initial state file: %v", err) } return stor.URL(StateFile) } // DeleteStateFile deletes the state file on the given storage. func DeleteStateFile(stor storage.Storage) error { return stor.Remove(StateFile) } // SaveState writes the given state to the given storage. func SaveState(storage storage.StorageWriter, state *BootstrapState) error { data, err := goyaml.Marshal(state) if err != nil { return err } return putState(storage, data) } // LoadState reads state from the given storage. func LoadState(stor storage.StorageReader) (*BootstrapState, error) { r, err := storage.Get(stor, StateFile) if err != nil { if coreerrors.IsNotFoundError(err) { return nil, environs.ErrNotBootstrapped } return nil, err } return loadState(r) } func loadState(r io.ReadCloser) (*BootstrapState, error) { defer r.Close() data, err := ioutil.ReadAll(r) if err != nil { return nil, fmt.Errorf("error reading %q: %v", StateFile, err) } var state BootstrapState err = goyaml.Unmarshal(data, &state) if err != nil { return nil, fmt.Errorf("error unmarshalling %q: %v", StateFile, err) } return &state, nil } �������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/bootstrap/bootstrap.go������������������������0000644�0000153�0000161�00000006072�12321735776�027126� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package bootstrap import ( "fmt" "github.com/juju/loggo" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils/ssh" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.environs.bootstrap") // Bootstrap bootstraps the given environment. The supplied constraints are // used to provision the instance, and are also set within the bootstrapped // environment. func Bootstrap(ctx environs.BootstrapContext, environ environs.Environ, cons constraints.Value) error { cfg := environ.Config() if secret := cfg.AdminSecret(); secret == "" { return fmt.Errorf("environment configuration has no admin-secret") } if authKeys := ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys()); len(authKeys) == 0 { // Apparently this can never happen, so it's not tested. But, one day, // Config will act differently (it's pretty crazy that, AFAICT, the // authorized-keys are optional config settings... but it's impossible // to actually *create* a config without them)... and when it does, // we'll be here to catch this problem early. return fmt.Errorf("environment configuration has no authorized-keys") } if _, hasCACert := cfg.CACert(); !hasCACert { return fmt.Errorf("environment configuration has no ca-cert") } if _, hasCAKey := cfg.CAPrivateKey(); !hasCAKey { return fmt.Errorf("environment configuration has no ca-private-key") } // Write out the bootstrap-init file, and confirm storage is writeable. if err := environs.VerifyStorage(environ.Storage()); err != nil { return err } logger.Infof("bootstrapping environment %q", environ.Name()) return environ.Bootstrap(ctx, cons) } // SetBootstrapTools returns the newest tools from the given tools list, // and updates the agent-version configuration attribute. func SetBootstrapTools(environ environs.Environ, possibleTools coretools.List) (coretools.List, error) { if len(possibleTools) == 0 { return nil, fmt.Errorf("no bootstrap tools available") } var newVersion version.Number newVersion, toolsList := possibleTools.Newest() logger.Infof("picked newest version: %s", newVersion) cfg := environ.Config() if agentVersion, _ := cfg.AgentVersion(); agentVersion != newVersion { cfg, err := cfg.Apply(map[string]interface{}{ "agent-version": newVersion.String(), }) if err == nil { err = environ.SetConfig(cfg) } if err != nil { return nil, fmt.Errorf("failed to update environment configuration: %v", err) } } return toolsList, nil } // EnsureNotBootstrapped returns nil if the environment is not // bootstrapped, and an error if it is or if the function was not able // to tell. func EnsureNotBootstrapped(env environs.Environ) error { _, err := LoadState(env.Storage()) // If there is no error loading the bootstrap state, then we are // bootstrapped. if err == nil { return fmt.Errorf("environment is already bootstrapped") } if err == environs.ErrNotBootstrapped { return nil } return err } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/bootstrap/state_test.go�����������������������0000644�0000153�0000161�00000010735�12321735776�027271� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package bootstrap_test import ( "io/ioutil" "net/http" "net/http/httptest" "os" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/storage" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/testing/testbase" ) type StateSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&StateSuite{}) func (suite *StateSuite) newStorageWithDataDir(c *gc.C) (storage.Storage, string) { closer, stor, dataDir := envtesting.CreateLocalTestStorage(c) suite.AddCleanup(func(*gc.C) { closer.Close() }) envtesting.UploadFakeTools(c, stor) return stor, dataDir } func (suite *StateSuite) newStorage(c *gc.C) storage.Storage { stor, _ := suite.newStorageWithDataDir(c) return stor } // testingHTTPServer creates a tempdir backed https server with internal // self-signed certs that will not be accepted as valid. func (suite *StateSuite) testingHTTPSServer(c *gc.C) (string, string) { dataDir := c.MkDir() mux := http.NewServeMux() mux.Handle("/", http.FileServer(http.Dir(dataDir))) server := httptest.NewTLSServer(mux) suite.AddCleanup(func(*gc.C) { server.Close() }) return server.URL, dataDir } func (suite *StateSuite) TestCreateStateFileWritesEmptyStateFile(c *gc.C) { stor := suite.newStorage(c) url, err := bootstrap.CreateStateFile(stor) c.Assert(err, gc.IsNil) reader, err := storage.Get(stor, bootstrap.StateFile) c.Assert(err, gc.IsNil) data, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(string(data), gc.Equals, "") c.Assert(url, gc.NotNil) expectedURL, err := stor.URL(bootstrap.StateFile) c.Assert(err, gc.IsNil) c.Check(url, gc.Equals, expectedURL) } func (suite *StateSuite) TestDeleteStateFile(c *gc.C) { closer, stor, dataDir := envtesting.CreateLocalTestStorage(c) defer closer.Close() err := bootstrap.DeleteStateFile(stor) c.Assert(err, gc.IsNil) // doesn't exist, juju don't care _, err = bootstrap.CreateStateFile(stor) c.Assert(err, gc.IsNil) _, err = os.Stat(filepath.Join(dataDir, bootstrap.StateFile)) c.Assert(err, gc.IsNil) err = bootstrap.DeleteStateFile(stor) c.Assert(err, gc.IsNil) _, err = os.Stat(filepath.Join(dataDir, bootstrap.StateFile)) c.Assert(err, jc.Satisfies, os.IsNotExist) } func (suite *StateSuite) TestSaveStateWritesStateFile(c *gc.C) { stor := suite.newStorage(c) arch := "amd64" state := bootstrap.BootstrapState{ StateInstances: []instance.Id{instance.Id("an-instance-id")}, Characteristics: []instance.HardwareCharacteristics{{Arch: &arch}}} marshaledState, err := goyaml.Marshal(state) c.Assert(err, gc.IsNil) err = bootstrap.SaveState(stor, &state) c.Assert(err, gc.IsNil) loadedState, err := storage.Get(stor, bootstrap.StateFile) c.Assert(err, gc.IsNil) content, err := ioutil.ReadAll(loadedState) c.Assert(err, gc.IsNil) c.Check(content, gc.DeepEquals, marshaledState) } func (suite *StateSuite) setUpSavedState(c *gc.C, dataDir string) bootstrap.BootstrapState { arch := "amd64" state := bootstrap.BootstrapState{ StateInstances: []instance.Id{instance.Id("an-instance-id")}, Characteristics: []instance.HardwareCharacteristics{{Arch: &arch}}} content, err := goyaml.Marshal(state) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(dataDir, bootstrap.StateFile), []byte(content), 0644) c.Assert(err, gc.IsNil) return state } func (suite *StateSuite) TestLoadStateReadsStateFile(c *gc.C) { storage, dataDir := suite.newStorageWithDataDir(c) state := suite.setUpSavedState(c, dataDir) storedState, err := bootstrap.LoadState(storage) c.Assert(err, gc.IsNil) c.Check(*storedState, gc.DeepEquals, state) } func (suite *StateSuite) TestLoadStateMissingFile(c *gc.C) { stor := suite.newStorage(c) _, err := bootstrap.LoadState(stor) c.Check(err, gc.Equals, environs.ErrNotBootstrapped) } func (suite *StateSuite) TestLoadStateIntegratesWithSaveState(c *gc.C) { storage := suite.newStorage(c) arch := "amd64" state := bootstrap.BootstrapState{ StateInstances: []instance.Id{instance.Id("an-instance-id")}, Characteristics: []instance.HardwareCharacteristics{{Arch: &arch}}} err := bootstrap.SaveState(storage, &state) c.Assert(err, gc.IsNil) storedState, err := bootstrap.LoadState(storage) c.Assert(err, gc.IsNil) c.Check(*storedState, gc.DeepEquals, state) } �����������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/errors.go�������������������������������������0000644�0000153�0000161�00000000533�12321735642�024374� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs import ( "errors" ) var ( ErrNotBootstrapped = errors.New("environment is not bootstrapped") ErrNoInstances = errors.New("no instances found") ErrPartialInstances = errors.New("only some instances were found") ) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/testing/��������������������������������������0000755�0000153�0000161�00000000000�12321736000�024172� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/testing/cloudinit.go��������������������������0000644�0000153�0000161�00000000746�12321735642�026535� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "launchpad.net/juju-core/environs" ) // PatchDataDir temporarily overrides environs.DataDir for testing purposes. // It returns a cleanup function that you must call later to restore the // original value. func PatchDataDir(path string) func() { originalDataDir := environs.DataDir environs.DataDir = path return func() { environs.DataDir = originalDataDir } } ��������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/testing/storage.go����������������������������0000644�0000153�0000161�00000001515�12321735642�026202� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "io" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/httpstorage" "launchpad.net/juju-core/environs/storage" ) // CreateLocalTestStorage returns the listener, which needs to be closed, and // the storage that is backed by a directory created in the running test's temp // directory. func CreateLocalTestStorage(c *gc.C) (closer io.Closer, stor storage.Storage, dataDir string) { dataDir = c.MkDir() underlying, err := filestorage.NewFileStorageWriter(dataDir) c.Assert(err, gc.IsNil) listener, err := httpstorage.Serve("localhost:0", underlying) c.Assert(err, gc.IsNil) stor = httpstorage.Client(listener.Addr().String()) closer = listener return } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/testing/polling.go����������������������������0000644�0000153�0000161�00000005120�12321735642�026176� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/utils" ) // impatientAttempt is an extremely short polling time suitable for tests. // It polls at least once, never delays, and times out very quickly. var impatientAttempt = utils.AttemptStrategy{} // savedAttemptStrategy holds the state needed to restore an AttemptStrategy's // original setting. type savedAttemptStrategy struct { address *utils.AttemptStrategy original utils.AttemptStrategy } // saveAttemptStrategies captures the information required to restore the // given AttemptStrategy objects. func saveAttemptStrategies(strategies []*utils.AttemptStrategy) []savedAttemptStrategy { savedStrategies := make([]savedAttemptStrategy, len(strategies)) for index, strategy := range strategies { savedStrategies[index] = savedAttemptStrategy{ address: strategy, original: *strategy, } } return savedStrategies } // restore returns a saved strategy to its original configuration. func (saved savedAttemptStrategy) restore() { *saved.address = saved.original } // restoreAttemptStrategies restores multiple saved AttemptStrategies. func restoreAttemptStrategies(strategies []savedAttemptStrategy) { for _, saved := range strategies { saved.restore() } } // internalPatchAttemptStrategies sets the given AttemptStrategy objects // to the impatientAttempt configuration, and returns a function that restores // the original configurations. func internalPatchAttemptStrategies(strategies []*utils.AttemptStrategy) func() { snapshot := saveAttemptStrategies(strategies) for _, strategy := range strategies { *strategy = impatientAttempt } return func() { restoreAttemptStrategies(snapshot) } } // TODO: Everything up to this point is generic. Move it to utils? // PatchAttemptStrategies patches environs' global polling strategy, plus any // otther AttemptStrategy objects whose addresses you pass, to very short // polling and timeout times so that tests can run fast. // It returns a cleanup function that restores the original settings. You must // call this afterwards. func PatchAttemptStrategies(strategies ...*utils.AttemptStrategy) func() { // The one irregularity here is that LongAttempt goes on the list of // strategies that need patching. To keep testing simple, we treat // the given attempts and LongAttempt as a single slice from here on. combinedStrategies := append(strategies, &common.LongAttempt, &common.ShortAttempt) return internalPatchAttemptStrategies(combinedStrategies) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/testing/bootstrap.go��������������������������0000644�0000153�0000161�00000001573�12321735642�026557� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "github.com/juju/loggo" "github.com/juju/testing" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/utils/ssh" ) var logger = loggo.GetLogger("juju.environs.testing") // DisableFinishBootstrap disables common.FinishBootstrap so that tests // do not attempt to SSH to non-existent machines. The result is a function // that restores finishBootstrap. func DisableFinishBootstrap() func() { f := func(environs.BootstrapContext, ssh.Client, instance.Instance, *cloudinit.MachineConfig) error { logger.Warningf("provider/common.FinishBootstrap is disabled") return nil } return testing.PatchValue(&common.FinishBootstrap, f) } �������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/testing/tools.go������������������������������0000644�0000153�0000161�00000034751�12321735776�025716� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "bytes" "os" "path" "path/filepath" gc "launchpad.net/gocheck" agenttools "launchpad.net/juju-core/agent/tools" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/log" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker/upgrader" ) // ToolsFixture is used as a fixture to stub out the default tools URL so we // don't hit the real internet during tests. type ToolsFixture struct { origDefaultURL string DefaultBaseURL string } func (s *ToolsFixture) SetUpTest(c *gc.C) { s.origDefaultURL = envtools.DefaultBaseURL envtools.DefaultBaseURL = s.DefaultBaseURL } func (s *ToolsFixture) TearDownTest(c *gc.C) { envtools.DefaultBaseURL = s.origDefaultURL } // RemoveFakeMetadata deletes the fake simplestreams tools metadata from the supplied storage. func RemoveFakeToolsMetadata(c *gc.C, stor storage.Storage) { files := []string{simplestreams.UnsignedIndex, envtools.ProductMetadataPath} for _, file := range files { toolspath := path.Join("tools", file) err := stor.Remove(toolspath) c.Check(err, gc.IsNil) } } // CheckTools ensures the obtained and expected tools are equal, allowing for the fact that // the obtained tools may not have size and checksum set. func CheckTools(c *gc.C, obtained, expected *coretools.Tools) { c.Assert(obtained.Version, gc.Equals, expected.Version) // TODO(dimitern) 2013-10-02 bug #1234217 // Are these used at at all? If not we should drop them. if obtained.URL != "" { c.Assert(obtained.URL, gc.Equals, expected.URL) } if obtained.Size > 0 { c.Assert(obtained.Size, gc.Equals, expected.Size) c.Assert(obtained.SHA256, gc.Equals, expected.SHA256) } } // CheckUpgraderReadyError ensures the obtained and expected errors are equal. func CheckUpgraderReadyError(c *gc.C, obtained error, expected *upgrader.UpgradeReadyError) { c.Assert(obtained, gc.FitsTypeOf, &upgrader.UpgradeReadyError{}) err := obtained.(*upgrader.UpgradeReadyError) c.Assert(err.AgentName, gc.Equals, expected.AgentName) c.Assert(err.DataDir, gc.Equals, expected.DataDir) c.Assert(err.OldTools, gc.Equals, expected.OldTools) c.Assert(err.NewTools, gc.Equals, expected.NewTools) } // PrimeTools sets up the current version of the tools to vers and // makes sure that they're available in the dataDir. func PrimeTools(c *gc.C, stor storage.Storage, dataDir string, vers version.Binary) *coretools.Tools { err := os.RemoveAll(filepath.Join(dataDir, "tools")) c.Assert(err, gc.IsNil) agentTools, err := uploadFakeToolsVersion(stor, vers) c.Assert(err, gc.IsNil) resp, err := utils.GetValidatingHTTPClient().Get(agentTools.URL) c.Assert(err, gc.IsNil) defer resp.Body.Close() err = agenttools.UnpackTools(dataDir, agentTools, resp.Body) c.Assert(err, gc.IsNil) return agentTools } func uploadFakeToolsVersion(stor storage.Storage, vers version.Binary) (*coretools.Tools, error) { log.Noticef("environs/testing: uploading FAKE tools %s", vers) tgz, checksum := coretesting.TarGz( coretesting.NewTarFile("jujud", 0777, "jujud contents "+vers.String())) size := int64(len(tgz)) name := envtools.StorageName(vers) if err := stor.Put(name, bytes.NewReader(tgz), size); err != nil { return nil, err } url, err := stor.URL(name) if err != nil { return nil, err } return &coretools.Tools{URL: url, Version: vers, Size: size, SHA256: checksum}, nil } // UploadFakeToolsVersions puts fake tools in the supplied storage for the supplied versions. func UploadFakeToolsVersions(stor storage.Storage, versions ...version.Binary) ([]*coretools.Tools, error) { // Leave existing tools alone. existingTools := make(map[version.Binary]*coretools.Tools) existing, _ := envtools.ReadList(stor, 1, -1) for _, tools := range existing { existingTools[tools.Version] = tools } var agentTools coretools.List = make(coretools.List, len(versions)) for i, version := range versions { if tools, ok := existingTools[version]; ok { agentTools[i] = tools } else { t, err := uploadFakeToolsVersion(stor, version) if err != nil { return nil, err } agentTools[i] = t } } if err := envtools.MergeAndWriteMetadata(stor, agentTools, envtools.DoNotWriteMirrors); err != nil { return nil, err } return agentTools, nil } // AssertUploadFakeToolsVersions puts fake tools in the supplied storage for the supplied versions. func AssertUploadFakeToolsVersions(c *gc.C, stor storage.Storage, versions ...version.Binary) []*coretools.Tools { agentTools, err := UploadFakeToolsVersions(stor, versions...) c.Assert(err, gc.IsNil) err = envtools.MergeAndWriteMetadata(stor, agentTools, envtools.DoNotWriteMirrors) c.Assert(err, gc.IsNil) return agentTools } // MustUploadFakeToolsVersions acts as UploadFakeToolsVersions, but panics on failure. func MustUploadFakeToolsVersions(stor storage.Storage, versions ...version.Binary) []*coretools.Tools { var agentTools coretools.List = make(coretools.List, len(versions)) for i, version := range versions { t, err := uploadFakeToolsVersion(stor, version) if err != nil { panic(err) } agentTools[i] = t } err := envtools.MergeAndWriteMetadata(stor, agentTools, envtools.DoNotWriteMirrors) if err != nil { panic(err) } return agentTools } func uploadFakeTools(stor storage.Storage) error { versions := []version.Binary{version.Current} toolsVersion := version.Current latestLts := coretesting.FakeDefaultSeries if toolsVersion.Series != latestLts { toolsVersion.Series = latestLts versions = append(versions, toolsVersion) } if _, err := UploadFakeToolsVersions(stor, versions...); err != nil { return err } return nil } // UploadFakeTools puts fake tools into the supplied storage with a binary // version matching version.Current; if version.Current's series is different // to coretesting.FakeDefaultSeries, matching fake tools will be uploaded for that // series. This is useful for tests that are kinda casual about specifying // their environment. func UploadFakeTools(c *gc.C, stor storage.Storage) { c.Assert(uploadFakeTools(stor), gc.IsNil) } // MustUploadFakeTools acts as UploadFakeTools, but panics on failure. func MustUploadFakeTools(stor storage.Storage) { if err := uploadFakeTools(stor); err != nil { panic(err) } } // RemoveFakeTools deletes the fake tools from the supplied storage. func RemoveFakeTools(c *gc.C, stor storage.Storage) { c.Logf("removing fake tools") toolsVersion := version.Current name := envtools.StorageName(toolsVersion) err := stor.Remove(name) c.Check(err, gc.IsNil) defaultSeries := coretesting.FakeDefaultSeries if version.Current.Series != defaultSeries { toolsVersion.Series = defaultSeries name := envtools.StorageName(toolsVersion) err := stor.Remove(name) c.Check(err, gc.IsNil) } RemoveFakeToolsMetadata(c, stor) } // RemoveTools deletes all tools from the supplied storage. func RemoveTools(c *gc.C, stor storage.Storage) { names, err := storage.List(stor, "tools/releases/juju-") c.Assert(err, gc.IsNil) c.Logf("removing files: %v", names) for _, name := range names { err = stor.Remove(name) c.Check(err, gc.IsNil) } RemoveFakeToolsMetadata(c, stor) } // RemoveAllTools deletes all tools from the supplied environment. func RemoveAllTools(c *gc.C, env environs.Environ) { c.Logf("clearing private storage") RemoveTools(c, env.Storage()) } var ( V100 = version.MustParse("1.0.0") V100p64 = version.MustParseBinary("1.0.0-precise-amd64") V100p32 = version.MustParseBinary("1.0.0-precise-i386") V100p = []version.Binary{V100p64, V100p32} V100q64 = version.MustParseBinary("1.0.0-quantal-amd64") V100q32 = version.MustParseBinary("1.0.0-quantal-i386") V100q = []version.Binary{V100q64, V100q32} V100all = append(V100p, V100q...) V1001 = version.MustParse("1.0.0.1") V1001p64 = version.MustParseBinary("1.0.0.1-precise-amd64") V100Xall = append(V100all, V1001p64) V110 = version.MustParse("1.1.0") V110p64 = version.MustParseBinary("1.1.0-precise-amd64") V110p32 = version.MustParseBinary("1.1.0-precise-i386") V110p = []version.Binary{V110p64, V110p32} V110q64 = version.MustParseBinary("1.1.0-quantal-amd64") V110q32 = version.MustParseBinary("1.1.0-quantal-i386") V110q = []version.Binary{V110q64, V110q32} V110all = append(V110p, V110q...) V1101p64 = version.MustParseBinary("1.1.0.1-precise-amd64") V110Xall = append(V110all, V1101p64) V120 = version.MustParse("1.2.0") V120p64 = version.MustParseBinary("1.2.0-precise-amd64") V120p32 = version.MustParseBinary("1.2.0-precise-i386") V120p = []version.Binary{V120p64, V120p32} V120q64 = version.MustParseBinary("1.2.0-quantal-amd64") V120q32 = version.MustParseBinary("1.2.0-quantal-i386") V120q = []version.Binary{V120q64, V120q32} V120all = append(V120p, V120q...) V1all = append(V100Xall, append(V110all, V120all...)...) V220 = version.MustParse("2.2.0") V220p32 = version.MustParseBinary("2.2.0-precise-i386") V220p64 = version.MustParseBinary("2.2.0-precise-amd64") V220q32 = version.MustParseBinary("2.2.0-quantal-i386") V220q64 = version.MustParseBinary("2.2.0-quantal-amd64") V220all = []version.Binary{V220p64, V220p32, V220q64, V220q32} VAll = append(V1all, V220all...) V310qppc64 = version.MustParseBinary("3.1.0-quantal-ppc64") V3101qppc64 = version.MustParseBinary("3.1.0.1-quantal-ppc64") ) type BootstrapToolsTest struct { Info string Available []version.Binary CliVersion version.Binary DefaultSeries string AgentVersion version.Number Development bool Arch string Expect []version.Binary Err string } var noToolsMessage = "Juju cannot bootstrap because no tools are available for your environment.*" var BootstrapToolsTests = []BootstrapToolsTest{ { Info: "no tools at all", CliVersion: V100p64, DefaultSeries: "precise", Err: noToolsMessage, }, { Info: "released cli: use newest compatible release version", Available: VAll, CliVersion: V100p64, DefaultSeries: "precise", Expect: V100p, }, { Info: "released cli: cli Arch ignored", Available: VAll, CliVersion: V100p32, DefaultSeries: "precise", Expect: V100p, }, { Info: "released cli: cli series ignored", Available: VAll, CliVersion: V100q64, DefaultSeries: "precise", Expect: V100p, }, { Info: "released cli: series taken from default-series", Available: V120all, CliVersion: V120p64, DefaultSeries: "quantal", Expect: V120q, }, { Info: "released cli: ignore close dev match", Available: V100Xall, CliVersion: V100p64, DefaultSeries: "precise", Expect: V100p, }, { Info: "released cli: filter by arch constraints", Available: V120all, CliVersion: V120p64, DefaultSeries: "precise", Arch: "i386", Expect: []version.Binary{V120p32}, }, { Info: "released cli: specific released version", Available: VAll, CliVersion: V100p64, AgentVersion: V100, DefaultSeries: "precise", Expect: V100p, }, { Info: "released cli: specific dev version", Available: VAll, CliVersion: V110p64, AgentVersion: V110, DefaultSeries: "precise", Expect: V110p, }, { Info: "released cli: major upgrades bad", Available: V220all, CliVersion: V100p64, DefaultSeries: "precise", Err: noToolsMessage, }, { Info: "released cli: minor upgrades bad", Available: V120all, CliVersion: V100p64, DefaultSeries: "precise", Err: noToolsMessage, }, { Info: "released cli: major downgrades bad", Available: V100Xall, CliVersion: V220p64, DefaultSeries: "precise", Err: noToolsMessage, }, { Info: "released cli: minor downgrades bad", Available: V100Xall, CliVersion: V120p64, DefaultSeries: "quantal", Err: noToolsMessage, }, { Info: "released cli: no matching series", Available: VAll, CliVersion: V100p64, DefaultSeries: "raring", Err: noToolsMessage, }, { Info: "released cli: no matching arches", Available: VAll, CliVersion: V100p64, DefaultSeries: "precise", Arch: "armhf", Err: noToolsMessage, }, { Info: "released cli: specific bad major 1", Available: VAll, CliVersion: V220p64, AgentVersion: V120, DefaultSeries: "precise", Err: noToolsMessage, }, { Info: "released cli: specific bad major 2", Available: VAll, CliVersion: V120p64, AgentVersion: V220, DefaultSeries: "precise", Err: noToolsMessage, }, { Info: "released cli: ignore dev tools 1", Available: V110all, CliVersion: V100p64, DefaultSeries: "precise", Err: noToolsMessage, }, { Info: "released cli: ignore dev tools 2", Available: V110all, CliVersion: V120p64, DefaultSeries: "precise", Err: noToolsMessage, }, { Info: "released cli: ignore dev tools 3", Available: []version.Binary{V1001p64}, CliVersion: V100p64, DefaultSeries: "precise", Err: noToolsMessage, }, { Info: "released cli with dev setting respects agent-version", Available: VAll, CliVersion: V100q32, AgentVersion: V1001, DefaultSeries: "precise", Development: true, Expect: []version.Binary{V1001p64}, }, { Info: "dev cli respects agent-version", Available: VAll, CliVersion: V100q32, AgentVersion: V1001, DefaultSeries: "precise", Expect: []version.Binary{V1001p64}, }, { Info: "released cli with dev setting respects agent-version", Available: V1all, CliVersion: V100q32, AgentVersion: V1001, DefaultSeries: "precise", Development: true, Expect: []version.Binary{V1001p64}, }, { Info: "dev cli respects agent-version", Available: V1all, CliVersion: V100q32, AgentVersion: V1001, DefaultSeries: "precise", Expect: []version.Binary{V1001p64}, }} func SetSSLHostnameVerification(c *gc.C, st *state.State, SSLHostnameVerification bool) { err := st.UpdateEnvironConfig(map[string]interface{}{"ssl-hostname-verification": SSLHostnameVerification}, nil, nil) c.Assert(err, gc.IsNil) } �����������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/testing/polling_test.go�����������������������0000644�0000153�0000161�00000005624�12321735642�027246� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/utils" ) func TestPackage(t *stdtesting.T) { gc.TestingT(t) } type testingSuite struct{} var _ = gc.Suite(&testingSuite{}) func (*testingSuite) TestSaveAttemptStrategiesSaves(c *gc.C) { attempt := utils.AttemptStrategy{ Total: time.Second, Delay: time.Millisecond, } snapshot := saveAttemptStrategies([]*utils.AttemptStrategy{&attempt}) c.Assert(snapshot, gc.HasLen, 1) c.Check(snapshot[0].address, gc.Equals, &attempt) c.Check(snapshot[0].original, gc.DeepEquals, attempt) } func (*testingSuite) TestSaveAttemptStrategiesLeavesOriginalsIntact(c *gc.C) { original := utils.AttemptStrategy{ Total: time.Second, Delay: time.Millisecond, } attempt := original saveAttemptStrategies([]*utils.AttemptStrategy{&attempt}) c.Check(attempt, gc.DeepEquals, original) } func (*testingSuite) TestInternalPatchAttemptStrategiesPatches(c *gc.C) { attempt := utils.AttemptStrategy{ Total: 33 * time.Millisecond, Delay: 99 * time.Microsecond, } c.Assert(attempt, gc.Not(gc.DeepEquals), impatientAttempt) internalPatchAttemptStrategies([]*utils.AttemptStrategy{&attempt}) c.Check(attempt, gc.DeepEquals, impatientAttempt) } // internalPatchAttemptStrategies returns a cleanup function that restores // the given strategies to their original configurations. For simplicity, // these tests take this as sufficient proof that any strategy that gets // patched, also gets restored by the cleanup function. func (*testingSuite) TestInternalPatchAttemptStrategiesReturnsCleanup(c *gc.C) { original := utils.AttemptStrategy{ Total: 22 * time.Millisecond, Delay: 77 * time.Microsecond, } c.Assert(original, gc.Not(gc.DeepEquals), impatientAttempt) attempt := original cleanup := internalPatchAttemptStrategies([]*utils.AttemptStrategy{&attempt}) cleanup() c.Check(attempt, gc.DeepEquals, original) } func (*testingSuite) TestPatchAttemptStrategiesPatchesEnvironsStrategies(c *gc.C) { c.Assert(common.LongAttempt, gc.Not(gc.DeepEquals), impatientAttempt) c.Assert(common.ShortAttempt, gc.Not(gc.DeepEquals), impatientAttempt) cleanup := PatchAttemptStrategies() defer cleanup() c.Check(common.LongAttempt, gc.DeepEquals, impatientAttempt) c.Check(common.ShortAttempt, gc.DeepEquals, impatientAttempt) } func (*testingSuite) TestPatchAttemptStrategiesPatchesGivenAttempts(c *gc.C) { attempt1 := utils.AttemptStrategy{ Total: 33 * time.Millisecond, Delay: 99 * time.Microsecond, } attempt2 := utils.AttemptStrategy{ Total: 82 * time.Microsecond, Delay: 62 * time.Nanosecond, } cleanup := PatchAttemptStrategies(&attempt1, &attempt2) defer cleanup() c.Check(attempt1, gc.DeepEquals, impatientAttempt) c.Check(attempt2, gc.DeepEquals, impatientAttempt) } ������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/cloudinit/������������������������������������0000755�0000153�0000161�00000000000�12321736000�024507� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/cloudinit/cloudinit.go������������������������0000644�0000153�0000161�00000065245�12321735776�027067� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cloudinit import ( "encoding/base64" "encoding/json" "fmt" "path" "strings" "github.com/errgo/errgo" "launchpad.net/goyaml" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/agent/mongo" agenttools "launchpad.net/juju-core/agent/tools" "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/upstart" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // SystemIdentity is the name of the file where the environment SSH key is kept. const SystemIdentity = "system-identity" // fileSchemePrefix is the prefix for file:// URLs. const fileSchemePrefix = "file://" // MachineConfig represents initialization information for a new juju machine. type MachineConfig struct { // StateServer specifies whether the new machine will run the // mongo and API servers. StateServer bool // StateServerCert and StateServerKey hold the state server // certificate and private key in PEM format; they are required when // StateServer is set, and ignored otherwise. StateServerCert []byte StateServerKey []byte // StatePort specifies the TCP port that will be used // by the MongoDB server. It must be non-zero // if StateServer is true. StatePort int // APIPort specifies the TCP port that will be used // by the API server. It must be non-zero // if StateServer is true. APIPort int // StateInfo holds the means for the new instance to communicate with the // juju state. Unless the new machine is running a state server (StateServer is // set), there must be at least one state server address supplied. // The entity name must match that of the machine being started, // or be empty when starting a state server. StateInfo *state.Info // APIInfo holds the means for the new instance to communicate with the // juju state API. Unless the new machine is running a state server (StateServer is // set), there must be at least one state server address supplied. // The entity name must match that of the machine being started, // or be empty when starting a state server. APIInfo *api.Info // InstanceId is the instance ID of the machine being initialised. // This is required when bootstrapping, and ignored otherwise. InstanceId instance.Id // HardwareCharacteristics contains the harrdware characteristics of // the machine being initialised. This optional, and is only used by // the bootstrap agent during state initialisation. HardwareCharacteristics *instance.HardwareCharacteristics // MachineNonce is set at provisioning/bootstrap time and used to // ensure the agent is running on the correct instance. MachineNonce string // Tools is juju tools to be used on the new machine. Tools *coretools.Tools // DataDir holds the directory that juju state will be put in the new // machine. DataDir string // LogDir holds the directory that juju logs will be written to. LogDir string // Jobs holds what machine jobs to run. Jobs []params.MachineJob // CloudInitOutputLog specifies the path to the output log for cloud-init. // The directory containing the log file must already exist. CloudInitOutputLog string // MachineId identifies the new machine. MachineId string // MachineContainerType specifies the type of container that the machine // is. If the machine is not a container, then the type is "". MachineContainerType instance.ContainerType // AuthorizedKeys specifies the keys that are allowed to // connect to the machine (see cloudinit.SSHAddAuthorizedKeys) // If no keys are supplied, there can be no ssh access to the node. // On a bootstrap machine, that is fatal. On other // machines it will mean that the ssh, scp and debug-hooks // commands cannot work. AuthorizedKeys string // AgentEnvironment defines additional configuration variables to set in // the machine agent config. AgentEnvironment map[string]string // WARNING: this is only set if the machine being configured is // a state server node. // // Config holds the initial environment configuration. Config *config.Config // Constraints holds the initial environment constraints. Constraints constraints.Value // DisableSSLHostnameVerification can be set to true to tell cloud-init // that it shouldn't verify SSL certificates DisableSSLHostnameVerification bool // SystemPrivateSSHKey is created at bootstrap time and recorded on every // node that has an API server. At this stage, that is any machine where // StateServer (member above) is set to true. SystemPrivateSSHKey string // DisablePackageCommands is a flag that specifies whether to suppress // the addition of package management commands. DisablePackageCommands bool // MachineAgentServiceName is the Upstart service name for the Juju machine agent. MachineAgentServiceName string // MongoServiceName is the Upstart service name for the Mongo database. MongoServiceName string // ProxySettings define normal http, https and ftp proxies. ProxySettings osenv.ProxySettings // AptProxySettings define the http, https and ftp proxy settings to use // for apt, which may or may not be the same as the normal ProxySettings. AptProxySettings osenv.ProxySettings } func base64yaml(m *config.Config) string { data, err := goyaml.Marshal(m.AllAttrs()) if err != nil { // can't happen, these values have been validated a number of times panic(err) } return base64.StdEncoding.EncodeToString(data) } // Configure updates the provided cloudinit.Config with // configuration to initialize a Juju machine agent. func Configure(cfg *MachineConfig, c *cloudinit.Config) error { if err := ConfigureBasic(cfg, c); err != nil { return err } return ConfigureJuju(cfg, c) } // NonceFile is written by cloud-init as the last thing it does. // The file will contain the machine's nonce. The filename is // relative to the Juju data-dir. const NonceFile = "nonce.txt" // ConfigureBasic updates the provided cloudinit.Config with // basic configuration to initialise an OS image, such that it can // be connected to via SSH, and log to a standard location. // // Any potentially failing operation should not be added to the // configuration, but should instead be done in ConfigureJuju. // // Note: we don't do apt update/upgrade here so as not to have to wait on // apt to finish when performing the second half of image initialisation. // Doing it later brings the benefit of feedback in the face of errors, // but adds to the running time of initialisation due to lack of activity // between image bringup and start of agent installation. func ConfigureBasic(cfg *MachineConfig, c *cloudinit.Config) error { c.AddScripts( "set -xe", // ensure we run all the scripts or abort. ) c.AddSSHAuthorizedKeys(cfg.AuthorizedKeys) c.SetOutput(cloudinit.OutAll, "| tee -a "+cfg.CloudInitOutputLog, "") // Create a file in a well-defined location containing the machine's // nonce. The presence and contents of this file will be verified // during bootstrap. // // Note: this must be the last runcmd we do in ConfigureBasic, as // the presence of the nonce file is used to gate the remainder // of synchronous bootstrap. noncefile := path.Join(cfg.DataDir, NonceFile) c.AddFile(noncefile, cfg.MachineNonce, 0644) return nil } // AddAptCommands update the cloudinit.Config instance with the necessary // packages, the request to do the apt-get update/upgrade on boot, and adds // the apt proxy settings if there are any. func AddAptCommands(proxy osenv.ProxySettings, c *cloudinit.Config) { // Bring packages up-to-date. c.SetAptUpdate(true) c.SetAptUpgrade(true) // juju requires git for managing charm directories. c.AddPackage("git") c.AddPackage("curl") c.AddPackage("cpu-checker") c.AddPackage("bridge-utils") c.AddPackage("rsyslog-gnutls") // Write out the apt proxy settings if (proxy != osenv.ProxySettings{}) { filename := utils.AptConfFile c.AddBootCmd(fmt.Sprintf( `[ -f %s ] || (printf '%%s\n' %s > %s)`, filename, shquote(utils.AptProxyContent(proxy)), filename)) } } // ConfigureJuju updates the provided cloudinit.Config with configuration // to initialise a Juju machine agent. func ConfigureJuju(cfg *MachineConfig, c *cloudinit.Config) error { if err := verifyConfig(cfg); err != nil { return err } // Initialise progress reporting. We need to do separately for runcmd // and (possibly, below) for bootcmd, as they may be run in different // shell sessions. initProgressCmd := cloudinit.InitProgressCmd() c.AddRunCmd(initProgressCmd) // If we're doing synchronous bootstrap or manual provisioning, then // ConfigureBasic won't have been invoked; thus, the output log won't // have been set. We don't want to show the log to the user, so simply // append to the log file rather than teeing. if stdout, _ := c.Output(cloudinit.OutAll); stdout == "" { c.SetOutput(cloudinit.OutAll, ">> "+cfg.CloudInitOutputLog, "") c.AddBootCmd(initProgressCmd) c.AddBootCmd(cloudinit.LogProgressCmd("Logging to %s on remote host", cfg.CloudInitOutputLog)) } if !cfg.DisablePackageCommands { AddAptCommands(cfg.AptProxySettings, c) } // Write out the normal proxy settings so that the settings are // sourced by bash, and ssh through that. c.AddScripts( // We look to see if the proxy line is there already as // the manual provider may have had it aleady. The ubuntu // user may not exist (local provider only). `([ ! -e /home/ubuntu/.profile ] || grep -q '.juju-proxy' /home/ubuntu/.profile) || ` + `printf '\n# Added by juju\n[ -f "$HOME/.juju-proxy" ] && . "$HOME/.juju-proxy"\n' >> /home/ubuntu/.profile`) if (cfg.ProxySettings != osenv.ProxySettings{}) { exportedProxyEnv := cfg.ProxySettings.AsScriptEnvironment() c.AddScripts(strings.Split(exportedProxyEnv, "\n")...) c.AddScripts( fmt.Sprintf( `[ -e /home/ubuntu ] && (printf '%%s\n' %s > /home/ubuntu/.juju-proxy && chown ubuntu:ubuntu /home/ubuntu/.juju-proxy)`, shquote(cfg.ProxySettings.AsScriptEnvironment()))) } // Make the lock dir and change the ownership of the lock dir itself to // ubuntu:ubuntu from root:root so the juju-run command run as the ubuntu // user is able to get access to the hook execution lock (like the uniter // itself does.) lockDir := path.Join(cfg.DataDir, "locks") c.AddScripts( fmt.Sprintf("mkdir -p %s", lockDir), // We only try to change ownership if there is an ubuntu user // defined, and we determine this by the existance of the home dir. fmt.Sprintf("[ -e /home/ubuntu ] && chown ubuntu:ubuntu %s", lockDir), fmt.Sprintf("mkdir -p %s", cfg.LogDir), fmt.Sprintf("chown syslog:adm %s", cfg.LogDir), ) // Make a directory for the tools to live in, then fetch the // tools and unarchive them into it. var copyCmd string if strings.HasPrefix(cfg.Tools.URL, fileSchemePrefix) { copyCmd = fmt.Sprintf("cp %s $bin/tools.tar.gz", shquote(cfg.Tools.URL[len(fileSchemePrefix):])) } else { curlCommand := "curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s '" if cfg.DisableSSLHostnameVerification { curlCommand += " --insecure" } copyCmd = fmt.Sprintf("%s -o $bin/tools.tar.gz %s", curlCommand, shquote(cfg.Tools.URL)) c.AddRunCmd(cloudinit.LogProgressCmd("Fetching tools: %s", copyCmd)) } toolsJson, err := json.Marshal(cfg.Tools) if err != nil { return err } c.AddScripts( "bin="+shquote(cfg.jujuTools()), "mkdir -p $bin", copyCmd, fmt.Sprintf("sha256sum $bin/tools.tar.gz > $bin/juju%s.sha256", cfg.Tools.Version), fmt.Sprintf(`grep '%s' $bin/juju%s.sha256 || (echo "Tools checksum mismatch"; exit 1)`, cfg.Tools.SHA256, cfg.Tools.Version), fmt.Sprintf("tar zxf $bin/tools.tar.gz -C $bin"), fmt.Sprintf("rm $bin/tools.tar.gz && rm $bin/juju%s.sha256", cfg.Tools.Version), fmt.Sprintf("printf %%s %s > $bin/downloaded-tools.txt", shquote(string(toolsJson))), ) // We add the machine agent's configuration info // before running bootstrap-state so that bootstrap-state // has a chance to rerwrite it to change the password. // It would be cleaner to change bootstrap-state to // be responsible for starting the machine agent itself, // but this would not be backwardly compatible. machineTag := names.MachineTag(cfg.MachineId) _, err = cfg.addAgentInfo(c, machineTag) if err != nil { return err } // Add the cloud archive cloud-tools pocket to apt sources // for series that need it. This gives us up-to-date LXC, // MongoDB, and other infrastructure. if !cfg.DisablePackageCommands { series := cfg.Tools.Version.Series MaybeAddCloudArchiveCloudTools(c, series) } if cfg.StateServer { identityFile := cfg.dataFile(SystemIdentity) c.AddFile(identityFile, cfg.SystemPrivateSSHKey, 0600) if !cfg.DisablePackageCommands { series := cfg.Tools.Version.Series mongoPackage := mongo.MongoPackageForSeries(series) if mongoPackage == "mongodb-server" { // Disable the default mongodb installed by the mongodb-server package. // Only do this if the file doesn't exist already, so users can run // their own mongodb server if they wish to. c.AddBootCmd( `[ -f /etc/default/mongodb ] || (echo ENABLE_MONGODB="no" > /etc/default/mongodb)`) if cfg.NeedMongoPPA() { const key = "" // key is loaded from PPA c.AddAptSource("ppa:juju/stable", key, nil) } if series == "precise" { // In precise we add the cloud-tools pocket and // pin it with a lower priority, so we need to // explicitly specify the target release when // installing mongodb-server from there. c.AddPackageFromTargetRelease("mongodb-server", "precise-updates/cloud-tools") } else { c.AddPackage("mongodb-server") } } else { c.AddPackage(mongoPackage) } } certKey := string(cfg.StateServerCert) + string(cfg.StateServerKey) c.AddFile(cfg.dataFile("server.pem"), certKey, 0600) if err := cfg.addMongoToBoot(c); err != nil { return err } // We temporarily give bootstrap-state a directory // of its own so that it can get the state info via the // same mechanism as other jujud commands. // TODO(rog) 2013-10-04 // This is redundant now as jujud bootstrap // uses the machine agent's configuration. // We leave it for the time being for backward compatibility. acfg, err := cfg.addAgentInfo(c, "bootstrap") if err != nil { return err } cons := cfg.Constraints.String() if cons != "" { cons = " --constraints " + shquote(cons) } var hardware string if cfg.HardwareCharacteristics != nil { if hardware = cfg.HardwareCharacteristics.String(); hardware != "" { hardware = " --hardware " + shquote(hardware) } } c.AddRunCmd(cloudinit.LogProgressCmd("Bootstrapping Juju machine agent")) c.AddScripts( // The bootstrapping is always run with debug on. cfg.jujuTools()+"/jujud bootstrap-state"+ " --data-dir "+shquote(cfg.DataDir)+ " --env-config "+shquote(base64yaml(cfg.Config))+ " --instance-id "+shquote(string(cfg.InstanceId))+ hardware+ cons+ " --debug", "rm -rf "+shquote(acfg.Dir()), ) } return cfg.addMachineAgentToBoot(c, machineTag, cfg.MachineId) } func (cfg *MachineConfig) dataFile(name string) string { return path.Join(cfg.DataDir, name) } func (cfg *MachineConfig) agentConfig(tag string) (agent.Config, error) { // TODO for HAState: the stateHostAddrs and apiHostAddrs here assume that // if the machine is a stateServer then to use localhost. This may be // sufficient, but needs thought in the new world order. var password string if cfg.StateInfo == nil { password = cfg.APIInfo.Password } else { password = cfg.StateInfo.Password } configParams := agent.AgentConfigParams{ DataDir: cfg.DataDir, LogDir: cfg.LogDir, Jobs: cfg.Jobs, Tag: tag, UpgradedToVersion: version.Current.Number, Password: password, Nonce: cfg.MachineNonce, StateAddresses: cfg.stateHostAddrs(), APIAddresses: cfg.apiHostAddrs(), CACert: cfg.StateInfo.CACert, Values: cfg.AgentEnvironment, } if !cfg.StateServer { return agent.NewAgentConfig(configParams) } return agent.NewStateMachineConfig(agent.StateMachineConfigParams{ AgentConfigParams: configParams, StateServerCert: cfg.StateServerCert, StateServerKey: cfg.StateServerKey, StatePort: cfg.StatePort, APIPort: cfg.APIPort, }) } // addAgentInfo adds agent-required information to the agent's directory // and returns the agent directory name. func (cfg *MachineConfig) addAgentInfo(c *cloudinit.Config, tag string) (agent.Config, error) { acfg, err := cfg.agentConfig(tag) if err != nil { return nil, err } acfg.SetValue(agent.AgentServiceName, cfg.MachineAgentServiceName) if cfg.StateServer { acfg.SetValue(agent.MongoServiceName, cfg.MongoServiceName) } cmds, err := acfg.WriteCommands() if err != nil { return nil, errgo.Annotate(err, "failed to write commands") } c.AddScripts(cmds...) return acfg, nil } func (cfg *MachineConfig) addMachineAgentToBoot(c *cloudinit.Config, tag, machineId string) error { // Make the agent run via a symbolic link to the actual tools // directory, so it can upgrade itself without needing to change // the upstart script. toolsDir := agenttools.ToolsDir(cfg.DataDir, tag) // TODO(dfc) ln -nfs, so it doesn't fail if for some reason that the target already exists c.AddScripts(fmt.Sprintf("ln -s %v %s", cfg.Tools.Version, shquote(toolsDir))) name := cfg.MachineAgentServiceName conf := upstart.MachineAgentUpstartService(name, toolsDir, cfg.DataDir, cfg.LogDir, tag, machineId, nil) cmds, err := conf.InstallCommands() if err != nil { return errgo.Annotatef(err, "cannot make cloud-init upstart script for the %s agent", tag) } c.AddRunCmd(cloudinit.LogProgressCmd("Starting Juju machine agent (%s)", name)) c.AddScripts(cmds...) return nil } func (cfg *MachineConfig) addMongoToBoot(c *cloudinit.Config) error { dbDir := path.Join(cfg.DataDir, "db") c.AddScripts( "mkdir -p "+dbDir+"/journal", "chmod 0700 "+dbDir, // Otherwise we get three files with 100M+ each, which takes time. "dd bs=1M count=1 if=/dev/zero of="+dbDir+"/journal/prealloc.0", "dd bs=1M count=1 if=/dev/zero of="+dbDir+"/journal/prealloc.1", "dd bs=1M count=1 if=/dev/zero of="+dbDir+"/journal/prealloc.2", ) name := cfg.MongoServiceName mongodExec := mongo.MongodPathForSeries(cfg.Tools.Version.Series) conf, err := mongo.MongoUpstartService(name, mongodExec, cfg.DataDir, cfg.StatePort) if err != nil { return err } cmds, err := conf.InstallCommands() if err != nil { return errgo.Annotate(err, "cannot make cloud-init upstart script for the state database") } c.AddRunCmd(cloudinit.LogProgressCmd("Starting MongoDB server (%s)", name)) c.AddScripts(cmds...) return nil } // versionDir converts a tools URL into a name // to use as a directory for storing the tools executables in // by using the last element stripped of its extension. func versionDir(toolsURL string) string { name := path.Base(toolsURL) ext := path.Ext(name) return name[:len(name)-len(ext)] } func (cfg *MachineConfig) jujuTools() string { return agenttools.SharedToolsDir(cfg.DataDir, cfg.Tools.Version) } func (cfg *MachineConfig) stateHostAddrs() []string { var hosts []string if cfg.StateServer { hosts = append(hosts, fmt.Sprintf("localhost:%d", cfg.StatePort)) } if cfg.StateInfo != nil { hosts = append(hosts, cfg.StateInfo.Addrs...) } return hosts } func (cfg *MachineConfig) apiHostAddrs() []string { var hosts []string if cfg.StateServer { hosts = append(hosts, fmt.Sprintf("localhost:%d", cfg.APIPort)) } if cfg.APIInfo != nil { hosts = append(hosts, cfg.APIInfo.Addrs...) } return hosts } const CanonicalCloudArchiveSigningKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- Version: SKS 1.1.4 Comment: Hostname: keyserver.ubuntu.com mQINBFAqSlgBEADPKwXUwqbgoDYgR20zFypxSZlSbrttOKVPEMb0HSUx9Wj8VvNCr+mT4E9w Ayq7NTIs5ad2cUhXoyenrjcfGqK6k9R6yRHDbvAxCSWTnJjw7mzsajDNocXC6THKVW8BSjrh 0aOBLpht6d5QCO2vyWxw65FKM65GOsbX03ZngUPMuOuiOEHQZo97VSH2pSB+L+B3d9B0nw3Q nU8qZMne+nVWYLYRXhCIxSv1/h39SXzHRgJoRUFHvL2aiiVrn88NjqfDW15HFhVJcGOFuACZ nRA0/EqTq0qNo3GziQO4mxuZi3bTVL5sGABiYW9uIlokPqcS7Fa0FRVIU9R+bBdHZompcYnK AeGag+uRvuTqC3MMRcLUS9Oi/P9I8fPARXUPwzYN3fagCGB8ffYVqMunnFs0L6td08BgvWwe r+Buu4fPGsQ5OzMclgZ0TJmXyOlIW49lc1UXnORp4sm7HS6okA7P6URbqyGbaplSsNUVTgVb i+vc8/jYdfExt/3HxVqgrPlq9htqYgwhYvGIbBAxmeFQD8Ak/ShSiWb1FdQ+f7Lty+4mZLfN 8x4zPZ//7fD5d/PETPh9P0msF+lLFlP564+1j75wx+skFO4v1gGlBcDaeipkFzeozndAgpeg ydKSNTF4QK9iTYobTIwsYfGuS8rV21zE2saLM0CE3T90aHYB/wARAQABtD1DYW5vbmljYWwg Q2xvdWQgQXJjaGl2ZSBTaWduaW5nIEtleSA8ZnRwbWFzdGVyQGNhbm9uaWNhbC5jb20+iQI3 BBMBCAAhBQJQKkpYAhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEF7bG2LsSSbqKxkQ AIKtgImrk02YCDldg6tLt3b69ZK0kIVI3Xso/zCBZbrYFmgGQEFHAa58mIgpv5GcgHHxWjpX 3n4tu2RM9EneKvFjFBstTTgoyuCgFr7iblvs/aMW4jFJAiIbmjjXWVc0CVB/JlLqzBJ/MlHd R9OWmojN9ZzoIA+i+tWlypgUot8iIxkR6JENxit5v9dN8i6anmnWybQ6PXFMuNi6GzQ0JgZI Vs37n0ks2wh0N8hBjAKuUgqu4MPMwvNtz8FxEzyKwLNSMnjLAhzml/oje/Nj1GBB8roj5dmw 7PSul5pAqQ5KTaXzl6gJN5vMEZzO4tEoGtRpA0/GTSXIlcx/SGkUK5+lqdQIMdySn8bImU6V 6rDSoOaI9YWHZtpv5WeUsNTdf68jZsFCRD+2+NEmIqBVm11yhmUoasC6dYw5l9P/PBdwmFm6 NBUSEwxb+ROfpL1ICaZk9Jy++6akxhY//+cYEPLin02r43Z3o5Piqujrs1R2Hs7kX84gL5Sl BzTM4Ed+ob7KVtQHTefpbO35bQllkPNqfBsC8AIC8xvTP2S8FicYOPATEuiRWs7Kn31TWC2i wswRKEKVRmN0fdpu/UPdMikyoNu9szBZRxvkRAezh3WheJ6MW6Fmg9d+uTFJohZt5qHdpxYa 4beuN4me8LF0TYzgfEbFT6b9D6IyTFoT0LequQINBFAqSlgBEADmL3TEq5ejBYrA+64zo8FY vCF4gziPa5rCIJGZ/gZXQ7pm5zek/lOe9C80mhxNWeLmrWMkMOWKCeaDMFpMBOQhZZmRdakO nH/xxO5x+fRdOOhy+5GTRJiwkuGOV6rB9eYJ3UN9caP2hfipCMpJjlg3j/GwktjhuqcBHXhA HMhzxEOIDE5hmpDqZ051f8LGXld9aSL8RctoYFM8sgafPVmICTCq0Wh03dr5c2JAgEXy3ush Ym/8i2WFmyldo7vbtTfx3DpmJc/EMpGKV+GxcI3/ERqSkde0kWlmfPZbo/5+hRqSryqfQtRK nFEQgAqAhPIwXwOkjCpPnDNfrkvzVEtl2/BWP/1/SOqzXjk9TIb1Q7MHANeFMrTCprzPLX6I dC4zLp+LpV91W2zygQJzPgWqH/Z/WFH4gXcBBqmI8bFpMPONYc9/67AWUABo2VOCojgtQmjx uFn+uGNw9PvxJAF3yjl781PVLUw3n66dwHRmYj4hqxNDLywhhnL/CC7KUDtBnUU/CKn/0Xgm 9oz3thuxG6i3F3pQgpp7MeMntKhLFWRXo9Bie8z/c0NV4K5HcpbGa8QPqoDseB5WaO4yGIBO t+nizM4DLrI+v07yXe3Jm7zBSpYSrGarZGK68qamS3XPzMshPdoXXz33bkQrTPpivGYQVRZu zd/R6b+6IurV+QARAQABiQIfBBgBCAAJBQJQKkpYAhsMAAoJEF7bG2LsSSbq59EP/1U3815/ yHV3cf/JeHgh6WS/Oy2kRHp/kJt3ev/l/qIxfMIpyM3u/D6siORPTUXHPm3AaZrbw0EDWByA 3jHQEzlLIbsDGZgrnl+mxFuHwC1yEuW3xrzgjtGZCJureZ/BD6xfRuRcmvnetAZv/z98VN/o j3rvYhUi71NApqSvMExpNBGrdO6gQlI5azhOu8xGNy4OSke8J6pAsMUXIcEwjVEIvewJuqBW /3rj3Hh14tmWjQ7shNnYBuSJwbLeUW2e8bURnfXETxrCmXzDmQldD5GQWCcD5WDosk/HVHBm Hlqrqy0VO2nE3c73dQlNcI4jVWeC4b4QSpYVsFz/6Iqy5ZQkCOpQ57MCf0B6P5nF92c5f3TY PMxHf0x3DrjDbUVZytxDiZZaXsbZzsejbbc1bSNp4hb+IWhmWoFnq/hNHXzKPHBTapObnQju +9zUlQngV0BlPT62hOHOw3Pv7suOuzzfuOO7qpz0uAy8cFKe7kBtLSFVjBwaG5JX89mgttYW +lw9Rmsbp9Iw4KKFHIBLOwk7s+u0LUhP3d8neBI6NfkOYKZZCm3CuvkiOeQP9/2okFjtj+29 jEL+9KQwrGNFEVNe85Un5MJfYIjgyqX3nJcwypYxidntnhMhr2VD3HL2R/4CiswBOa4g9309 p/+af/HU1smBrOfIeRoxb8jQoHu3 =xg4S -----END PGP PUBLIC KEY BLOCK-----` // MaybeAddCloudArchiveCloudTools adds the cloud-archive cloud-tools // pocket to apt sources, if the series requires it. func MaybeAddCloudArchiveCloudTools(c *cloudinit.Config, series string) { if series != "precise" { // Currently only precise; presumably we'll // need to add each LTS in here as they're // added to the cloud archive. return } const url = "http://ubuntu-cloud.archive.canonical.com/ubuntu" name := fmt.Sprintf("deb %s %s-updates/cloud-tools main", url, series) prefs := &cloudinit.AptPreferences{ Path: cloudinit.CloudToolsPrefsPath, Explanation: "Pin with lower priority, not to interfere with charms", Package: "*", Pin: fmt.Sprintf("release n=%s-updates/cloud-tools", series), PinPriority: 400, } c.AddAptSource(name, CanonicalCloudArchiveSigningKey, prefs) } func (cfg *MachineConfig) NeedMongoPPA() bool { series := cfg.Tools.Version.Series // 11.10 and earlier are not supported. // 12.04 can get a compatible version from the cloud-archive. // 13.04 and later ship a compatible version in the archive. return series == "quantal" } func shquote(p string) string { return utils.ShQuote(p) } type requiresError string func (e requiresError) Error() string { return "invalid machine configuration: missing " + string(e) } func verifyConfig(cfg *MachineConfig) (err error) { defer utils.ErrorContextf(&err, "invalid machine configuration") if !names.IsMachine(cfg.MachineId) { return fmt.Errorf("invalid machine id") } if cfg.DataDir == "" { return fmt.Errorf("missing var directory") } if cfg.LogDir == "" { return fmt.Errorf("missing log directory") } if len(cfg.Jobs) == 0 { return fmt.Errorf("missing machine jobs") } if cfg.CloudInitOutputLog == "" { return fmt.Errorf("missing cloud-init output log path") } if cfg.Tools == nil { return fmt.Errorf("missing tools") } if cfg.Tools.URL == "" { return fmt.Errorf("missing tools URL") } if cfg.StateInfo == nil { return fmt.Errorf("missing state info") } if len(cfg.StateInfo.CACert) == 0 { return fmt.Errorf("missing CA certificate") } if cfg.APIInfo == nil { return fmt.Errorf("missing API info") } if len(cfg.APIInfo.CACert) == 0 { return fmt.Errorf("missing API CA certificate") } if cfg.MachineAgentServiceName == "" { return fmt.Errorf("missing machine agent service name") } if cfg.StateServer { if cfg.MongoServiceName == "" { return fmt.Errorf("missing mongo service name") } if cfg.Config == nil { return fmt.Errorf("missing environment configuration") } if cfg.StateInfo.Tag != "" { return fmt.Errorf("entity tag must be blank when starting a state server") } if cfg.APIInfo.Tag != "" { return fmt.Errorf("entity tag must be blank when starting a state server") } if len(cfg.StateServerCert) == 0 { return fmt.Errorf("missing state server certificate") } if len(cfg.StateServerKey) == 0 { return fmt.Errorf("missing state server private key") } if cfg.StatePort == 0 { return fmt.Errorf("missing state port") } if cfg.APIPort == 0 { return fmt.Errorf("missing API port") } if cfg.SystemPrivateSSHKey == "" { return fmt.Errorf("missing system ssh identity") } if cfg.InstanceId == "" { return fmt.Errorf("missing instance-id") } } else { if len(cfg.StateInfo.Addrs) == 0 { return fmt.Errorf("missing state hosts") } if cfg.StateInfo.Tag != names.MachineTag(cfg.MachineId) { return fmt.Errorf("entity tag must match started machine") } if len(cfg.APIInfo.Addrs) == 0 { return fmt.Errorf("missing API hosts") } if cfg.APIInfo.Tag != names.MachineTag(cfg.MachineId) { return fmt.Errorf("entity tag must match started machine") } } if cfg.MachineNonce == "" { return fmt.Errorf("missing machine nonce") } return nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/cloudinit/suite_test.go�����������������������0000644�0000153�0000161�00000000414�12321735642�027240� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cloudinit_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } type suite struct{} var _ = gc.Suite(suite{}) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/cloudinit/cloudinit_test.go�������������������0000644�0000153�0000161�00000104231�12321735776�030113� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cloudinit_test import ( "encoding/base64" "regexp" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/agent" coreCloudinit "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) // Use local suite since this file lives in the ec2 package // for testing internals. type cloudinitSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&cloudinitSuite{}) var envConstraints = constraints.MustParse("mem=2G") var allMachineJobs = []params.MachineJob{ params.JobManageEnviron, params.JobHostUnits, } var normalMachineJobs = []params.MachineJob{ params.JobHostUnits, } type cloudinitTest struct { cfg cloudinit.MachineConfig setEnvConfig bool expectScripts string // inexactMatch signifies whether we allow extra lines // in the actual scripts found. If it's true, the lines // mentioned in expectScripts must appear in that // order, but they can be arbitrarily interleaved with other // script lines. inexactMatch bool } func minimalConfig(c *gc.C) *config.Config { cfg, err := config.New(config.NoDefaults, testing.FakeConfig()) c.Assert(err, gc.IsNil) return cfg } func must(s string, err error) string { if err != nil { panic(err) } return s } // Each test gives a cloudinit config - we check the // output to see if it looks correct. var cloudinitTests = []cloudinitTest{ { // precise state server cfg: cloudinit.MachineConfig{ MachineId: "0", AuthorizedKeys: "sshkey1", AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, // precise currently needs mongo from PPA Tools: newSimpleTools("1.2.3-precise-amd64"), StateServer: true, StateServerCert: serverCert, StateServerKey: serverKey, StatePort: 37017, APIPort: 17070, MachineNonce: "FAKE_NONCE", StateInfo: &state.Info{ Password: "arble", CACert: []byte("CA CERT\n" + testing.CACert), }, APIInfo: &api.Info{ Password: "bletch", CACert: []byte("CA CERT\n" + testing.CACert), }, Constraints: envConstraints, DataDir: environs.DataDir, LogDir: agent.DefaultLogDir, Jobs: allMachineJobs, CloudInitOutputLog: environs.CloudInitOutputLog, InstanceId: "i-bootstrap", SystemPrivateSSHKey: "private rsa key", MachineAgentServiceName: "jujud-machine-0", MongoServiceName: "juju-db", }, setEnvConfig: true, expectScripts: ` echo ENABLE_MONGODB="no" > /etc/default/mongodb set -xe install -D -m 644 /dev/null '/var/lib/juju/nonce.txt' printf '%s\\n' 'FAKE_NONCE' > '/var/lib/juju/nonce.txt' test -e /proc/self/fd/9 \|\| exec 9>&2 \(\[ ! -e /home/ubuntu/.profile \] \|\| grep -q '.juju-proxy' /home/ubuntu/.profile\) \|\| printf .* >> /home/ubuntu/.profile mkdir -p /var/lib/juju/locks \[ -e /home/ubuntu \] && chown ubuntu:ubuntu /var/lib/juju/locks mkdir -p /var/log/juju chown syslog:adm /var/log/juju echo 'Fetching tools.* bin='/var/lib/juju/tools/1\.2\.3-precise-amd64' mkdir -p \$bin curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s ' -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-precise-amd64\.tgz' sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-precise-amd64\.sha256 grep '1234' \$bin/juju1\.2\.3-precise-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\) tar zxf \$bin/tools.tar.gz -C \$bin rm \$bin/tools\.tar\.gz && rm \$bin/juju1\.2\.3-precise-amd64\.sha256 printf %s '{"version":"1\.2\.3-precise-amd64","url":"http://foo\.com/tools/releases/juju1\.2\.3-precise-amd64\.tgz","sha256":"1234","size":10}' > \$bin/downloaded-tools\.txt mkdir -p '/var/lib/juju/agents/machine-0' install -m 600 /dev/null '/var/lib/juju/agents/machine-0/agent\.conf' printf '%s\\n' '.*' > '/var/lib/juju/agents/machine-0/agent\.conf' install -D -m 644 /dev/null '/etc/apt/preferences\.d/50-cloud-tools' printf '%s\\n' '.*' > '/etc/apt/preferences\.d/50-cloud-tools' install -D -m 600 /dev/null '/var/lib/juju/system-identity' printf '%s\\n' '.*' > '/var/lib/juju/system-identity' install -D -m 600 /dev/null '/var/lib/juju/server\.pem' printf '%s\\n' 'SERVER CERT\\n[^']*SERVER KEY\\n[^']*' > '/var/lib/juju/server\.pem' mkdir -p /var/lib/juju/db/journal chmod 0700 /var/lib/juju/db dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc\.0 dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc\.1 dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc\.2 echo 'Starting MongoDB server \(juju-db\)'.* cat >> /etc/init/juju-db\.conf << 'EOF'\\ndescription "juju state database"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\n\\nlimit nofile 65000 65000\\nlimit nproc 20000 20000\\n\\nexec /usr/bin/mongod --auth --dbpath=/var/lib/juju/db --sslOnNormalPorts --sslPEMKeyFile '/var/lib/juju/server\.pem' --sslPEMKeyPassword ignored --bind_ip 0\.0\.0\.0 --port 37017 --noprealloc --syslog --smallfiles\\nEOF\\n start juju-db mkdir -p '/var/lib/juju/agents/bootstrap' install -m 600 /dev/null '/var/lib/juju/agents/bootstrap/agent\.conf' printf '%s\\n' '.*' > '/var/lib/juju/agents/bootstrap/agent\.conf' echo 'Bootstrapping Juju machine agent'.* /var/lib/juju/tools/1\.2\.3-precise-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --instance-id 'i-bootstrap' --constraints 'mem=2048M' --debug rm -rf '/var/lib/juju/agents/bootstrap' ln -s 1\.2\.3-precise-amd64 '/var/lib/juju/tools/machine-0' echo 'Starting Juju machine agent \(jujud-machine-0\)'.* cat >> /etc/init/jujud-machine-0\.conf << 'EOF'\\ndescription "juju machine-0 agent"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\n\\nlimit nofile 20000 20000\\n\\nexec /var/lib/juju/tools/machine-0/jujud machine --data-dir '/var/lib/juju' --machine-id 0 --debug >> /var/log/juju/machine-0\.log 2>&1\\nEOF\\n start jujud-machine-0 `, }, { // raring state server - we just test the raring-specific parts of the output. cfg: cloudinit.MachineConfig{ MachineId: "0", AuthorizedKeys: "sshkey1", AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, // raring provides mongo in the archive Tools: newSimpleTools("1.2.3-raring-amd64"), StateServer: true, StateServerCert: serverCert, StateServerKey: serverKey, StatePort: 37017, APIPort: 17070, MachineNonce: "FAKE_NONCE", StateInfo: &state.Info{ Password: "arble", CACert: []byte("CA CERT\n" + testing.CACert), }, APIInfo: &api.Info{ Password: "bletch", CACert: []byte("CA CERT\n" + testing.CACert), }, Constraints: envConstraints, DataDir: environs.DataDir, LogDir: agent.DefaultLogDir, Jobs: allMachineJobs, CloudInitOutputLog: environs.CloudInitOutputLog, InstanceId: "i-bootstrap", SystemPrivateSSHKey: "private rsa key", MachineAgentServiceName: "jujud-machine-0", MongoServiceName: "juju-db", }, setEnvConfig: true, inexactMatch: true, expectScripts: ` bin='/var/lib/juju/tools/1\.2\.3-raring-amd64' curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s ' -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-raring-amd64\.tgz' sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-raring-amd64\.sha256 grep '1234' \$bin/juju1\.2\.3-raring-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\) rm \$bin/tools\.tar\.gz && rm \$bin/juju1\.2\.3-raring-amd64\.sha256 printf %s '{"version":"1\.2\.3-raring-amd64","url":"http://foo\.com/tools/releases/juju1\.2\.3-raring-amd64\.tgz","sha256":"1234","size":10}' > \$bin/downloaded-tools\.txt /var/lib/juju/tools/1\.2\.3-raring-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --instance-id 'i-bootstrap' --constraints 'mem=2048M' --debug rm -rf '/var/lib/juju/agents/bootstrap' ln -s 1\.2\.3-raring-amd64 '/var/lib/juju/tools/machine-0' `, }, { // trusty state server - use the new mongo from juju-mongodb cfg: cloudinit.MachineConfig{ MachineId: "0", AuthorizedKeys: "sshkey1", AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, // raring provides mongo in the archive Tools: newSimpleTools("1.2.3-trusty-amd64"), StateServer: true, StateServerCert: serverCert, StateServerKey: serverKey, StatePort: 37017, APIPort: 17070, MachineNonce: "FAKE_NONCE", StateInfo: &state.Info{ Password: "arble", CACert: []byte("CA CERT\n" + testing.CACert), }, APIInfo: &api.Info{ Password: "bletch", CACert: []byte("CA CERT\n" + testing.CACert), }, Constraints: envConstraints, DataDir: environs.DataDir, LogDir: agent.DefaultLogDir, Jobs: allMachineJobs, CloudInitOutputLog: environs.CloudInitOutputLog, InstanceId: "i-bootstrap", SystemPrivateSSHKey: "private rsa key", MachineAgentServiceName: "jujud-machine-0", MongoServiceName: "juju-db", }, setEnvConfig: true, inexactMatch: true, expectScripts: ` echo 'Starting MongoDB server \(juju-db\)'.* cat >> /etc/init/juju-db\.conf << 'EOF'\\ndescription "juju state database"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\n\\nlimit nofile 65000 65000\\nlimit nproc 20000 20000\\n\\nexec /usr/lib/juju/bin/mongod --auth --dbpath=/var/lib/juju/db --sslOnNormalPorts --sslPEMKeyFile '/var/lib/juju/server\.pem' --sslPEMKeyPassword ignored --bind_ip 0\.0\.0\.0 --port 37017 --noprealloc --syslog --smallfiles\\nEOF\\n `, }, { // non state server. cfg: cloudinit.MachineConfig{ MachineId: "99", AuthorizedKeys: "sshkey1", AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, DataDir: environs.DataDir, LogDir: agent.DefaultLogDir, Jobs: normalMachineJobs, CloudInitOutputLog: environs.CloudInitOutputLog, StateServer: false, Tools: newSimpleTools("1.2.3-linux-amd64"), MachineNonce: "FAKE_NONCE", StateInfo: &state.Info{ Addrs: []string{"state-addr.testing.invalid:12345"}, Tag: "machine-99", Password: "arble", CACert: []byte("CA CERT\n" + testing.CACert), }, APIInfo: &api.Info{ Addrs: []string{"state-addr.testing.invalid:54321"}, Tag: "machine-99", Password: "bletch", CACert: []byte("CA CERT\n" + testing.CACert), }, MachineAgentServiceName: "jujud-machine-99", }, expectScripts: ` set -xe install -D -m 644 /dev/null '/var/lib/juju/nonce.txt' printf '%s\\n' 'FAKE_NONCE' > '/var/lib/juju/nonce.txt' test -e /proc/self/fd/9 \|\| exec 9>&2 \(\[ ! -e /home/ubuntu/\.profile \] \|\| grep -q '.juju-proxy' /home/ubuntu/.profile\) \|\| printf .* >> /home/ubuntu/.profile mkdir -p /var/lib/juju/locks \[ -e /home/ubuntu \] && chown ubuntu:ubuntu /var/lib/juju/locks mkdir -p /var/log/juju chown syslog:adm /var/log/juju echo 'Fetching tools.* bin='/var/lib/juju/tools/1\.2\.3-linux-amd64' mkdir -p \$bin curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s ' -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-linux-amd64\.tgz' sha256sum \$bin/tools\.tar\.gz > \$bin/juju1\.2\.3-linux-amd64\.sha256 grep '1234' \$bin/juju1\.2\.3-linux-amd64.sha256 \|\| \(echo "Tools checksum mismatch"; exit 1\) tar zxf \$bin/tools.tar.gz -C \$bin rm \$bin/tools\.tar\.gz && rm \$bin/juju1\.2\.3-linux-amd64\.sha256 printf %s '{"version":"1\.2\.3-linux-amd64","url":"http://foo\.com/tools/releases/juju1\.2\.3-linux-amd64\.tgz","sha256":"1234","size":10}' > \$bin/downloaded-tools\.txt mkdir -p '/var/lib/juju/agents/machine-99' install -m 600 /dev/null '/var/lib/juju/agents/machine-99/agent\.conf' printf '%s\\n' '.*' > '/var/lib/juju/agents/machine-99/agent\.conf' ln -s 1\.2\.3-linux-amd64 '/var/lib/juju/tools/machine-99' echo 'Starting Juju machine agent \(jujud-machine-99\)'.* cat >> /etc/init/jujud-machine-99\.conf << 'EOF'\\ndescription "juju machine-99 agent"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\n\\nlimit nofile 20000 20000\\n\\nexec /var/lib/juju/tools/machine-99/jujud machine --data-dir '/var/lib/juju' --machine-id 99 --debug >> /var/log/juju/machine-99\.log 2>&1\\nEOF\\n start jujud-machine-99 `, }, { // check that it works ok with compound machine ids. cfg: cloudinit.MachineConfig{ MachineId: "2/lxc/1", MachineContainerType: "lxc", AuthorizedKeys: "sshkey1", AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, DataDir: environs.DataDir, LogDir: agent.DefaultLogDir, Jobs: normalMachineJobs, CloudInitOutputLog: environs.CloudInitOutputLog, StateServer: false, Tools: newSimpleTools("1.2.3-linux-amd64"), MachineNonce: "FAKE_NONCE", StateInfo: &state.Info{ Addrs: []string{"state-addr.testing.invalid:12345"}, Tag: "machine-2-lxc-1", Password: "arble", CACert: []byte("CA CERT\n" + testing.CACert), }, APIInfo: &api.Info{ Addrs: []string{"state-addr.testing.invalid:54321"}, Tag: "machine-2-lxc-1", Password: "bletch", CACert: []byte("CA CERT\n" + testing.CACert), }, MachineAgentServiceName: "jujud-machine-2-lxc-1", }, inexactMatch: true, expectScripts: ` mkdir -p '/var/lib/juju/agents/machine-2-lxc-1' install -m 600 /dev/null '/var/lib/juju/agents/machine-2-lxc-1/agent\.conf' printf '%s\\n' '.*' > '/var/lib/juju/agents/machine-2-lxc-1/agent\.conf' ln -s 1\.2\.3-linux-amd64 '/var/lib/juju/tools/machine-2-lxc-1' cat >> /etc/init/jujud-machine-2-lxc-1\.conf << 'EOF'\\ndescription "juju machine-2-lxc-1 agent"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\n\\nlimit nofile 20000 20000\\n\\nexec /var/lib/juju/tools/machine-2-lxc-1/jujud machine --data-dir '/var/lib/juju' --machine-id 2/lxc/1 --debug >> /var/log/juju/machine-2-lxc-1\.log 2>&1\\nEOF\\n start jujud-machine-2-lxc-1 `, }, { // hostname verification disabled. cfg: cloudinit.MachineConfig{ MachineId: "99", AuthorizedKeys: "sshkey1", AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, DataDir: environs.DataDir, LogDir: agent.DefaultLogDir, Jobs: normalMachineJobs, CloudInitOutputLog: environs.CloudInitOutputLog, StateServer: false, Tools: newSimpleTools("1.2.3-linux-amd64"), MachineNonce: "FAKE_NONCE", StateInfo: &state.Info{ Addrs: []string{"state-addr.testing.invalid:12345"}, Tag: "machine-99", Password: "arble", CACert: []byte("CA CERT\n" + testing.CACert), }, APIInfo: &api.Info{ Addrs: []string{"state-addr.testing.invalid:54321"}, Tag: "machine-99", Password: "bletch", CACert: []byte("CA CERT\n" + testing.CACert), }, DisableSSLHostnameVerification: true, MachineAgentServiceName: "jujud-machine-99", }, inexactMatch: true, expectScripts: ` curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s ' --insecure -o \$bin/tools\.tar\.gz 'http://foo\.com/tools/releases/juju1\.2\.3-linux-amd64\.tgz' `, }, { // empty contraints. cfg: cloudinit.MachineConfig{ MachineId: "0", AuthorizedKeys: "sshkey1", AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, // precise currently needs mongo from PPA Tools: newSimpleTools("1.2.3-precise-amd64"), StateServer: true, StateServerCert: serverCert, StateServerKey: serverKey, StatePort: 37017, APIPort: 17070, MachineNonce: "FAKE_NONCE", StateInfo: &state.Info{ Password: "arble", CACert: []byte("CA CERT\n" + testing.CACert), }, APIInfo: &api.Info{ Password: "bletch", CACert: []byte("CA CERT\n" + testing.CACert), }, DataDir: environs.DataDir, LogDir: agent.DefaultLogDir, Jobs: allMachineJobs, CloudInitOutputLog: environs.CloudInitOutputLog, InstanceId: "i-bootstrap", SystemPrivateSSHKey: "private rsa key", MachineAgentServiceName: "jujud-machine-0", MongoServiceName: "juju-db", }, setEnvConfig: true, inexactMatch: true, expectScripts: ` /var/lib/juju/tools/1\.2\.3-precise-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --instance-id 'i-bootstrap' --debug `, }, } func newSimpleTools(vers string) *tools.Tools { return &tools.Tools{ URL: "http://foo.com/tools/releases/juju" + vers + ".tgz", Version: version.MustParseBinary(vers), Size: 10, SHA256: "1234", } } func newFileTools(vers, path string) *tools.Tools { tools := newSimpleTools(vers) tools.URL = "file://" + path return tools } func getAgentConfig(c *gc.C, tag string, scripts []string) (cfg string) { re := regexp.MustCompile(`printf '%s\\n' '((\n|.)+)' > .*agents/` + regexp.QuoteMeta(tag) + `/agent\.conf`) found := false for _, s := range scripts { m := re.FindStringSubmatch(s) if m == nil { continue } cfg = m[1] found = true } c.Assert(found, gc.Equals, true) return cfg } // check that any --env-config $base64 is valid and matches t.cfg.Config func checkEnvConfig(c *gc.C, cfg *config.Config, x map[interface{}]interface{}, scripts []string) { re := regexp.MustCompile(`--env-config '([^']+)'`) found := false for _, s := range scripts { m := re.FindStringSubmatch(s) if m == nil { continue } found = true buf, err := base64.StdEncoding.DecodeString(m[1]) c.Assert(err, gc.IsNil) var actual map[string]interface{} err = goyaml.Unmarshal(buf, &actual) c.Assert(err, gc.IsNil) c.Assert(cfg.AllAttrs(), gc.DeepEquals, actual) } c.Assert(found, gc.Equals, true) } // TestCloudInit checks that the output from the various tests // in cloudinitTests is well formed. func (*cloudinitSuite) TestCloudInit(c *gc.C) { expectedMongoPackage := map[string]string{ "precise": "mongodb-server", "raring": "mongodb-server", "trusty": "juju-mongodb", } for i, test := range cloudinitTests { c.Logf("test %d", i) if test.setEnvConfig { test.cfg.Config = minimalConfig(c) } ci := coreCloudinit.New() err := cloudinit.Configure(&test.cfg, ci) c.Assert(err, gc.IsNil) c.Check(ci, gc.NotNil) // render the cloudinit config to bytes, and then // back to a map so we can introspect it without // worrying about internal details of the cloudinit // package. data, err := ci.Render() c.Assert(err, gc.IsNil) x := make(map[interface{}]interface{}) err = goyaml.Unmarshal(data, &x) c.Assert(err, gc.IsNil) c.Check(x["apt_upgrade"], gc.Equals, true) c.Check(x["apt_update"], gc.Equals, true) scripts := getScripts(x) assertScriptMatch(c, scripts, test.expectScripts, !test.inexactMatch) if test.cfg.Config != nil { checkEnvConfig(c, test.cfg.Config, x, scripts) } checkPackage(c, x, "git", true) tag := names.MachineTag(test.cfg.MachineId) acfg := getAgentConfig(c, tag, scripts) c.Assert(acfg, jc.Contains, "AGENT_SERVICE_NAME: jujud-"+tag) if test.cfg.StateServer { series := test.cfg.Tools.Version.Series mongoPackage := expectedMongoPackage[series] checkPackage(c, x, mongoPackage, true) source := "ppa:juju/stable" checkAptSource(c, x, source, "", test.cfg.NeedMongoPPA()) c.Assert(acfg, jc.Contains, "MONGO_SERVICE_NAME: juju-db") } else { c.Assert(acfg, gc.Not(jc.Contains), "MONGO_SERVICE_NAME") } source := "deb http://ubuntu-cloud.archive.canonical.com/ubuntu precise-updates/cloud-tools main" needCloudArchive := test.cfg.Tools.Version.Series == "precise" checkAptSource(c, x, source, cloudinit.CanonicalCloudArchiveSigningKey, needCloudArchive) } } func (*cloudinitSuite) TestCloudInitConfigure(c *gc.C) { for i, test := range cloudinitTests { test.cfg.Config = minimalConfig(c) c.Logf("test %d (Configure)", i) cloudcfg := coreCloudinit.New() err := cloudinit.Configure(&test.cfg, cloudcfg) c.Assert(err, gc.IsNil) } } func (*cloudinitSuite) TestCloudInitConfigureUsesGivenConfig(c *gc.C) { // Create a simple cloudinit config with a 'runcmd' statement. cloudcfg := coreCloudinit.New() script := "test script" cloudcfg.AddRunCmd(script) cloudinitTests[0].cfg.Config = minimalConfig(c) err := cloudinit.Configure(&cloudinitTests[0].cfg, cloudcfg) c.Assert(err, gc.IsNil) data, err := cloudcfg.Render() c.Assert(err, gc.IsNil) ciContent := make(map[interface{}]interface{}) err = goyaml.Unmarshal(data, &ciContent) c.Assert(err, gc.IsNil) // The 'runcmd' statement is at the beginning of the list // of 'runcmd' statements. runCmd := ciContent["runcmd"].([]interface{}) c.Check(runCmd[0], gc.Equals, script) } func getScripts(x map[interface{}]interface{}) []string { var scripts []string if bootcmds, ok := x["bootcmd"]; ok { for _, s := range bootcmds.([]interface{}) { scripts = append(scripts, s.(string)) } } for _, s := range x["runcmd"].([]interface{}) { scripts = append(scripts, s.(string)) } return scripts } type line struct { index int line string } func assertScriptMatch(c *gc.C, got []string, expect string, exact bool) { for _, s := range got { c.Logf("script: %s", regexp.QuoteMeta(strings.Replace(s, "\n", "\\n", -1))) } var pats []line for i, pat := range strings.Split(strings.Trim(expect, "\n"), "\n") { pats = append(pats, line{ index: i, line: pat, }) } var scripts []line for i := range got { scripts = append(scripts, line{ index: i, line: strings.Replace(got[i], "\n", "\\n", -1), // make .* work }) } for { switch { case len(pats) == 0 && len(scripts) == 0: return case len(pats) == 0: if exact { c.Fatalf("too many scripts found (got %q at line %d)", scripts[0].line, scripts[0].index) } return case len(scripts) == 0: if exact { c.Fatalf("too few scripts found (expected %q at line %d)", pats[0].line, pats[0].index) } c.Fatalf("could not find match for %q", pats[0].line) default: ok, err := regexp.MatchString(pats[0].line, scripts[0].line) c.Assert(err, gc.IsNil) if ok { pats = pats[1:] scripts = scripts[1:] } else if exact { c.Assert(scripts[0].line, gc.Matches, pats[0].line, gc.Commentf("line %d", scripts[0].index)) panic("unreachable") } else { scripts = scripts[1:] } } } } // checkPackage checks that the cloudinit will or won't install the given // package, depending on the value of match. func checkPackage(c *gc.C, x map[interface{}]interface{}, pkg string, match bool) { pkgs0 := x["packages"] if pkgs0 == nil { if match { c.Errorf("cloudinit has no entry for packages") } return } pkgs := pkgs0.([]interface{}) found := false for _, p0 := range pkgs { p := p0.(string) hasTargetRelease := strings.Contains(p, "--target-release") hasQuotedPkg := strings.Contains(p, "'"+pkg+"'") if p == pkg || (hasTargetRelease && hasQuotedPkg) { found = true } } switch { case match && !found: c.Errorf("package %q not found in %v", pkg, pkgs) case !match && found: c.Errorf("%q found but not expected in %v", pkg, pkgs) } } // checkAptSources checks that the cloudinit will or won't install the given // source, depending on the value of match. func checkAptSource(c *gc.C, x map[interface{}]interface{}, source, key string, match bool) { sources0 := x["apt_sources"] if sources0 == nil { if match { c.Errorf("cloudinit has no entry for apt_sources") } return } sources := sources0.([]interface{}) found := false for _, s0 := range sources { s := s0.(map[interface{}]interface{}) if s["source"] == source && s["key"] == key { found = true } } switch { case match && !found: c.Errorf("source %q not found in %v", source, sources) case !match && found: c.Errorf("%q found but not expected in %v", source, sources) } } // When mutate is called on a known-good MachineConfig, // there should be an error complaining about the missing // field named by the adjacent err. var verifyTests = []struct { err string mutate func(*cloudinit.MachineConfig) }{ {"invalid machine id", func(cfg *cloudinit.MachineConfig) { cfg.MachineId = "-1" }}, {"missing environment configuration", func(cfg *cloudinit.MachineConfig) { cfg.Config = nil }}, {"missing state info", func(cfg *cloudinit.MachineConfig) { cfg.StateInfo = nil }}, {"missing API info", func(cfg *cloudinit.MachineConfig) { cfg.APIInfo = nil }}, {"missing state hosts", func(cfg *cloudinit.MachineConfig) { cfg.StateServer = false cfg.StateInfo = &state.Info{ Tag: "machine-99", CACert: []byte(testing.CACert), } cfg.APIInfo = &api.Info{ Addrs: []string{"foo:35"}, Tag: "machine-99", CACert: []byte(testing.CACert), } }}, {"missing API hosts", func(cfg *cloudinit.MachineConfig) { cfg.StateServer = false cfg.StateInfo = &state.Info{ Addrs: []string{"foo:35"}, Tag: "machine-99", CACert: []byte(testing.CACert), } cfg.APIInfo = &api.Info{ Tag: "machine-99", CACert: []byte(testing.CACert), } }}, {"missing CA certificate", func(cfg *cloudinit.MachineConfig) { cfg.StateInfo = &state.Info{Addrs: []string{"host:98765"}} }}, {"missing CA certificate", func(cfg *cloudinit.MachineConfig) { cfg.StateServer = false cfg.StateInfo = &state.Info{ Tag: "machine-99", Addrs: []string{"host:98765"}, } }}, {"missing state server certificate", func(cfg *cloudinit.MachineConfig) { cfg.StateServerCert = []byte{} }}, {"missing state server private key", func(cfg *cloudinit.MachineConfig) { cfg.StateServerKey = []byte{} }}, {"missing var directory", func(cfg *cloudinit.MachineConfig) { cfg.DataDir = "" }}, {"missing log directory", func(cfg *cloudinit.MachineConfig) { cfg.LogDir = "" }}, {"missing cloud-init output log path", func(cfg *cloudinit.MachineConfig) { cfg.CloudInitOutputLog = "" }}, {"missing tools", func(cfg *cloudinit.MachineConfig) { cfg.Tools = nil }}, {"missing tools URL", func(cfg *cloudinit.MachineConfig) { cfg.Tools = &tools.Tools{} }}, {"entity tag must match started machine", func(cfg *cloudinit.MachineConfig) { cfg.StateServer = false info := *cfg.StateInfo info.Tag = "machine-0" cfg.StateInfo = &info }}, {"entity tag must match started machine", func(cfg *cloudinit.MachineConfig) { cfg.StateServer = false info := *cfg.StateInfo info.Tag = "" cfg.StateInfo = &info }}, {"entity tag must match started machine", func(cfg *cloudinit.MachineConfig) { cfg.StateServer = false info := *cfg.APIInfo info.Tag = "machine-0" cfg.APIInfo = &info }}, {"entity tag must match started machine", func(cfg *cloudinit.MachineConfig) { cfg.StateServer = false info := *cfg.APIInfo info.Tag = "" cfg.APIInfo = &info }}, {"entity tag must be blank when starting a state server", func(cfg *cloudinit.MachineConfig) { info := *cfg.StateInfo info.Tag = "machine-0" cfg.StateInfo = &info }}, {"entity tag must be blank when starting a state server", func(cfg *cloudinit.MachineConfig) { info := *cfg.APIInfo info.Tag = "machine-0" cfg.APIInfo = &info }}, {"missing state port", func(cfg *cloudinit.MachineConfig) { cfg.StatePort = 0 }}, {"missing API port", func(cfg *cloudinit.MachineConfig) { cfg.APIPort = 0 }}, {"missing machine nonce", func(cfg *cloudinit.MachineConfig) { cfg.MachineNonce = "" }}, {"missing machine agent service name", func(cfg *cloudinit.MachineConfig) { cfg.MachineAgentServiceName = "" }}, {"missing mongo service name", func(cfg *cloudinit.MachineConfig) { cfg.MongoServiceName = "" }}, {"missing instance-id", func(cfg *cloudinit.MachineConfig) { cfg.InstanceId = "" }}, } // TestCloudInitVerify checks that required fields are appropriately // checked for by NewCloudInit. func (*cloudinitSuite) TestCloudInitVerify(c *gc.C) { cfg := &cloudinit.MachineConfig{ StateServer: true, StateServerCert: serverCert, StateServerKey: serverKey, StatePort: 1234, APIPort: 1235, MachineId: "99", Tools: newSimpleTools("9.9.9-linux-arble"), AuthorizedKeys: "sshkey1", AgentEnvironment: map[string]string{agent.ProviderType: "dummy"}, StateInfo: &state.Info{ Addrs: []string{"host:98765"}, CACert: []byte(testing.CACert), Password: "password", }, APIInfo: &api.Info{ Addrs: []string{"host:9999"}, CACert: []byte(testing.CACert), }, Config: minimalConfig(c), DataDir: environs.DataDir, LogDir: agent.DefaultLogDir, Jobs: normalMachineJobs, CloudInitOutputLog: environs.CloudInitOutputLog, InstanceId: "i-bootstrap", MachineNonce: "FAKE_NONCE", SystemPrivateSSHKey: "private rsa key", MachineAgentServiceName: "jujud-machine-99", MongoServiceName: "juju-db", } // check that the base configuration does not give an error ci := coreCloudinit.New() err := cloudinit.Configure(cfg, ci) c.Assert(err, gc.IsNil) for i, test := range verifyTests { c.Logf("test %d. %s", i, test.err) cfg1 := *cfg test.mutate(&cfg1) err = cloudinit.Configure(&cfg1, ci) c.Assert(err, gc.ErrorMatches, "invalid machine configuration: "+test.err) } } func (*cloudinitSuite) createMachineConfig(c *gc.C, environConfig *config.Config) *cloudinit.MachineConfig { machineId := "42" machineNonce := "fake-nonce" stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, machineNonce, stateInfo, apiInfo) machineConfig.Tools = &tools.Tools{ Version: version.MustParseBinary("2.3.4-foo-bar"), URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", } err := environs.FinishMachineConfig(machineConfig, environConfig, constraints.Value{}) c.Assert(err, gc.IsNil) return machineConfig } func (s *cloudinitSuite) TestAptProxyNotWrittenIfNotSet(c *gc.C) { environConfig := minimalConfig(c) machineCfg := s.createMachineConfig(c, environConfig) cloudcfg := coreCloudinit.New() err := cloudinit.Configure(machineCfg, cloudcfg) c.Assert(err, gc.IsNil) cmds := cloudcfg.BootCmds() c.Assert(cmds, gc.DeepEquals, []interface{}{}) } func (s *cloudinitSuite) TestAptProxyWritten(c *gc.C) { environConfig := minimalConfig(c) environConfig, err := environConfig.Apply(map[string]interface{}{ "apt-http-proxy": "http://user@10.0.0.1", }) c.Assert(err, gc.IsNil) machineCfg := s.createMachineConfig(c, environConfig) cloudcfg := coreCloudinit.New() err = cloudinit.Configure(machineCfg, cloudcfg) c.Assert(err, gc.IsNil) cmds := cloudcfg.BootCmds() expected := "[ -f /etc/apt/apt.conf.d/42-juju-proxy-settings ] || (printf '%s\\n' 'Acquire::http::Proxy \"http://user@10.0.0.1\";' > /etc/apt/apt.conf.d/42-juju-proxy-settings)" c.Assert(cmds, gc.DeepEquals, []interface{}{expected}) } func (s *cloudinitSuite) TestProxyWritten(c *gc.C) { environConfig := minimalConfig(c) environConfig, err := environConfig.Apply(map[string]interface{}{ "http-proxy": "http://user@10.0.0.1", "no-proxy": "localhost,10.0.3.1", }) c.Assert(err, gc.IsNil) machineCfg := s.createMachineConfig(c, environConfig) cloudcfg := coreCloudinit.New() err = cloudinit.Configure(machineCfg, cloudcfg) c.Assert(err, gc.IsNil) cmds := cloudcfg.RunCmds() first := `([ ! -e /home/ubuntu/.profile ] || grep -q '.juju-proxy' /home/ubuntu/.profile) || printf '\n# Added by juju\n[ -f "$HOME/.juju-proxy" ] && . "$HOME/.juju-proxy"\n' >> /home/ubuntu/.profile` expected := []interface{}{ `export http_proxy=http://user@10.0.0.1`, `export HTTP_PROXY=http://user@10.0.0.1`, `export no_proxy=localhost,10.0.3.1`, `export NO_PROXY=localhost,10.0.3.1`, `[ -e /home/ubuntu ] && (printf '%s\n' 'export http_proxy=http://user@10.0.0.1 export HTTP_PROXY=http://user@10.0.0.1 export no_proxy=localhost,10.0.3.1 export NO_PROXY=localhost,10.0.3.1' > /home/ubuntu/.juju-proxy && chown ubuntu:ubuntu /home/ubuntu/.juju-proxy)`, } found := false for i, cmd := range cmds { if cmd == first { c.Assert(cmds[i+1:i+6], jc.DeepEquals, expected) found = true break } } c.Assert(found, jc.IsTrue) } var serverCert = []byte(` SERVER CERT -----BEGIN CERTIFICATE----- MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN MAsGA1UEAxMEcm9vdDAeFw0xMjExMDgxNjIyMzRaFw0xMzExMDgxNjI3MzRaMBwx DDAKBgNVBAoTA2htbTEMMAoGA1UEAxMDYW55MFowCwYJKoZIhvcNAQEBA0sAMEgC QQCACqz6JPwM7nbxAWub+APpnNB7myckWJ6nnsPKi9SipP1hyhfzkp8RGMJ5Uv7y 8CSTtJ8kg/ibka1VV8LvP9tnAgMBAAGjUjBQMA4GA1UdDwEB/wQEAwIAsDAdBgNV HQ4EFgQU6G1ERaHCgfAv+yoDMFVpDbLOmIQwHwYDVR0jBBgwFoAUP/mfUdwOlHfk fR+gLQjslxf64w0wCwYJKoZIhvcNAQEFA0EAbn0MaxWVgGYBomeLYfDdb8vCq/5/ G/2iCUQCXsVrBparMLFnor/iKOkJB5n3z3rtu70rFt+DpX6L8uBR3LB3+A== -----END CERTIFICATE----- `[1:]) var serverKey = []byte(` SERVER KEY -----BEGIN RSA PRIVATE KEY----- MIIBPAIBAAJBAIAKrPok/AzudvEBa5v4A+mc0HubJyRYnqeew8qL1KKk/WHKF/OS nxEYwnlS/vLwJJO0nySD+JuRrVVXwu8/22cCAwEAAQJBAJsk1F0wTRuaIhJ5xxqw FIWPFep/n5jhrDOsIs6cSaRbfIBy3rAl956pf/MHKvf/IXh7KlG9p36IW49hjQHK 7HkCIQD2CqyV1ppNPFSoCI8mSwO8IZppU3i2V4MhpwnqHz3H0wIhAIU5XIlhLJW8 TNOaFMEia/TuYofdwJnYvi9t0v4UKBWdAiEA76AtvjEoTpi3in/ri0v78zp2/KXD JzPMDvZ0fYS30ukCIA1stlJxpFiCXQuFn0nG+jH4Q52FTv8xxBhrbLOFvHRRAiEA 2Vc9NN09ty+HZgxpwqIA1fHVuYJY9GMPG1LnTnZ9INg= -----END RSA PRIVATE KEY----- `[1:]) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/statepolicy.go��������������������������������0000644�0000153�0000161�00000001723�12321735776�025432� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environs import ( "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" ) // environStatePolicy implements state.Policy in // terms of environs.Environ and related types. type environStatePolicy struct{} var _ state.Policy = environStatePolicy{} // NewStatePolicy returns a state.Policy that is // implemented in terms of Environ and related // types. func NewStatePolicy() state.Policy { return environStatePolicy{} } func (environStatePolicy) Prechecker(cfg *config.Config) (state.Prechecker, error) { env, err := New(cfg) if err != nil { return nil, err } if p, ok := env.(state.Prechecker); ok { return p, nil } return nil, errors.NewNotImplementedError("Prechecker") } func (environStatePolicy) ConfigValidator(providerType string) (state.ConfigValidator, error) { return Provider(providerType) } ���������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/instances/������������������������������������0000755�0000153�0000161�00000000000�12321735642�024517� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/instances/instancetype.go���������������������0000644�0000153�0000161�00000012743�12321735642�027563� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instances import ( "fmt" "sort" "launchpad.net/juju-core/constraints" ) // InstanceType holds all relevant attributes of the various instance types. type InstanceType struct { Id string Name string Arches []string CpuCores uint64 Mem uint64 Cost uint64 RootDisk uint64 // These attributes are not supported by all clouds. VirtType *string // The type of virtualisation used by the hypervisor, must match the image. CpuPower *uint64 Tags []string } func CpuPower(power uint64) *uint64 { return &power } // match returns true if itype can satisfy the supplied constraints. If so, // it also returns a copy of itype with any arches that do not match the // constraints filtered out. func (itype InstanceType) match(cons constraints.Value) (InstanceType, bool) { nothing := InstanceType{} if cons.Arch != nil { itype.Arches = filterArches(itype.Arches, []string{*cons.Arch}) } if len(itype.Arches) == 0 { return nothing, false } if cons.CpuCores != nil && itype.CpuCores < *cons.CpuCores { return nothing, false } if cons.CpuPower != nil && itype.CpuPower != nil && *itype.CpuPower < *cons.CpuPower { return nothing, false } if cons.Mem != nil && itype.Mem < *cons.Mem { return nothing, false } if cons.RootDisk != nil && itype.RootDisk < *cons.RootDisk { return nothing, false } if cons.Tags != nil && len(*cons.Tags) > 0 && !tagsMatch(*cons.Tags, itype.Tags) { return nothing, false } return itype, true } // filterArches returns every element of src that also exists in filter. func filterArches(src, filter []string) (dst []string) { for _, arch := range src { for _, match := range filter { if arch == match { dst = append(dst, arch) break } } } return dst } // minMemoryHeuristic is the assumed minimum amount of memory (in MB) we prefer in order to run a server (1GB) const minMemoryHeuristic = 1024 // matchingTypesForConstraint returns instance types from allTypes which match cons. func matchingTypesForConstraint(allTypes []InstanceType, cons constraints.Value) []InstanceType { var matchingTypes []InstanceType for _, itype := range allTypes { itype, ok := itype.match(cons) if !ok { continue } matchingTypes = append(matchingTypes, itype) } return matchingTypes } // getMatchingInstanceTypes returns all instance types matching ic.Constraints and available // in ic.Region, sorted by increasing region-specific cost (if known). func getMatchingInstanceTypes(ic *InstanceConstraint, allInstanceTypes []InstanceType) ([]InstanceType, error) { region := ic.Region var itypes []InstanceType // Rules used to select instance types: // - non memory constraints like cpu-cores etc are always honoured // - if no mem constraint specified, try opinionated default with enough mem to run a server. // - if no matches and no mem constraint specified, try again and return any matching instance // with the largest memory cons := ic.Constraints if ic.Constraints.Mem == nil { minMem := uint64(minMemoryHeuristic) cons.Mem = &minMem } itypes = matchingTypesForConstraint(allInstanceTypes, cons) // No matches using opinionated default, so if no mem constraint specified, // look for matching instance with largest memory. if len(itypes) == 0 && ic.Constraints.Mem == nil { itypes = matchingTypesForConstraint(allInstanceTypes, ic.Constraints) if len(itypes) > 0 { sort.Sort(byMemory(itypes)) itypes = []InstanceType{itypes[len(itypes)-1]} } } // If we have matching instance types, we can return those, sorted by cost. if len(itypes) > 0 { sort.Sort(byCost(itypes)) return itypes, nil } // No luck, so report the error. return nil, fmt.Errorf("no instance types in %s matching constraints %q", region, ic.Constraints) } // tagsMatch returns if the tags in wanted all exist in have. // Note that duplicates of tags are disregarded in both lists func tagsMatch(wanted, have []string) bool { machineTags := map[string]struct{}{} for _, tag := range have { machineTags[tag] = struct{}{} } for _, tag := range wanted { if _, ok := machineTags[tag]; !ok { return false } } return true } // byCost is used to sort a slice of instance types by Cost. type byCost []InstanceType func (bc byCost) Len() int { return len(bc) } func (bc byCost) Less(i, j int) bool { inst0, inst1 := &bc[i], &bc[j] if inst0.Cost != inst1.Cost { return inst0.Cost < inst1.Cost } if inst0.Mem != inst1.Mem { return inst0.Mem < inst1.Mem } if inst0.CpuPower != nil && inst1.CpuPower != nil && *inst0.CpuPower != *inst1.CpuPower { return *inst0.CpuPower < *inst1.CpuPower } if inst0.CpuCores != inst1.CpuCores { return inst0.CpuCores < inst1.CpuCores } if inst0.RootDisk != inst1.RootDisk { return inst0.RootDisk < inst1.RootDisk } // we intentionally don't compare tags, since we can't know how tags compare against each other return false } func (bc byCost) Swap(i, j int) { bc[i], bc[j] = bc[j], bc[i] } //byMemory is used to sort a slice of instance types by the amount of RAM they have. type byMemory []InstanceType func (s byMemory) Len() int { return len(s) } func (s byMemory) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s byMemory) Less(i, j int) bool { inst0, inst1 := &s[i], &s[j] if inst0.Mem != inst1.Mem { return s[i].Mem < s[j].Mem } // Memory is equal, so use cost as a tie breaker. // Result is in descending order of cost so instance with lowest cost is used. return inst0.Cost > inst1.Cost } �����������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/instances/instancetype_test.go����������������0000644�0000153�0000161�00000025434�12321735642�030623� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instances import ( "sort" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/testing/testbase" ) type instanceTypeSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&instanceTypeSuite{}) var hvm = "hvm" // The instance types below do not necessarily reflect reality and are just // defined here for ease of testing special cases. var instanceTypes = []InstanceType{ { Name: "m1.small", Arches: []string{"amd64", "armhf"}, CpuCores: 1, CpuPower: CpuPower(100), Mem: 1740, Cost: 60, RootDisk: 8192, }, { Name: "m1.medium", Arches: []string{"amd64", "armhf"}, CpuCores: 1, CpuPower: CpuPower(200), Mem: 3840, Cost: 120, RootDisk: 16384, }, { Name: "m1.large", Arches: []string{"amd64"}, CpuCores: 2, CpuPower: CpuPower(400), Mem: 7680, Cost: 240, RootDisk: 32768, }, { Name: "m1.xlarge", Arches: []string{"amd64"}, CpuCores: 4, CpuPower: CpuPower(800), Mem: 15360, Cost: 480, RootDisk: 65536, }, { Name: "t1.micro", Arches: []string{"amd64", "armhf"}, CpuCores: 1, CpuPower: CpuPower(20), Mem: 613, Cost: 20, RootDisk: 4096, }, { Name: "c1.medium", Arches: []string{"amd64", "armhf"}, CpuCores: 2, CpuPower: CpuPower(500), Mem: 1740, Cost: 145, RootDisk: 8192, }, { Name: "c1.xlarge", Arches: []string{"amd64"}, CpuCores: 8, CpuPower: CpuPower(2000), Mem: 7168, Cost: 580, RootDisk: 32768, }, { Name: "cc1.4xlarge", Arches: []string{"amd64"}, CpuCores: 8, CpuPower: CpuPower(3350), Mem: 23552, Cost: 1300, RootDisk: 32768, VirtType: &hvm, }, { Name: "cc2.8xlarge", Arches: []string{"amd64"}, CpuCores: 16, CpuPower: CpuPower(8800), Mem: 61952, Cost: 2400, RootDisk: 131072, VirtType: &hvm, }, } var getInstanceTypesTest = []struct { about string cons string itypesToUse []InstanceType expectedItypes []string arches []string }{ { about: "cpu-cores", cons: "cpu-cores=2", expectedItypes: []string{ "c1.medium", "m1.large", "m1.xlarge", "c1.xlarge", "cc1.4xlarge", "cc2.8xlarge", }, }, { about: "cpu-power", cons: "cpu-power=2000", expectedItypes: []string{"c1.xlarge", "cc1.4xlarge", "cc2.8xlarge"}, }, { about: "mem", cons: "mem=4G", expectedItypes: []string{ "m1.large", "m1.xlarge", "c1.xlarge", "cc1.4xlarge", "cc2.8xlarge", }, }, { about: "root-disk", cons: "root-disk=16G", expectedItypes: []string{ "m1.medium", "m1.large", "m1.xlarge", "c1.xlarge", "cc1.4xlarge", "cc2.8xlarge", }, }, { about: "arches filtered by constraint", cons: "cpu-power=100 arch=armhf", expectedItypes: []string{"m1.small", "m1.medium", "c1.medium"}, arches: []string{"armhf"}, }, { about: "enough memory for mongodb if mem not specified", cons: "cpu-cores=4", itypesToUse: []InstanceType{ {Id: "5", Name: "it-5", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 2}, {Id: "4", Name: "it-4", Arches: []string{"amd64"}, Mem: 2048, CpuCores: 4}, {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 4}, {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 256, CpuCores: 4}, {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4}, }, expectedItypes: []string{"it-3", "it-4"}, }, { about: "small mem specified, use that even though less than needed for mongodb", cons: "mem=300M", itypesToUse: []InstanceType{ {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 2048}, {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 256}, {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512}, }, expectedItypes: []string{"it-1", "it-3"}, }, { about: "mem specified and match found", cons: "mem=4G arch=amd64", itypesToUse: []InstanceType{ {Id: "4", Name: "it-4", Arches: []string{"armhf"}, Mem: 8096}, {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 4096}, {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 2048}, {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512}, }, expectedItypes: []string{"it-3"}, }, { about: "largest mem available matching other constraints if mem not specified", cons: "cpu-cores=4", itypesToUse: []InstanceType{ {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 2}, {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 256, CpuCores: 4}, {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4}, }, expectedItypes: []string{"it-1"}, }, { about: "largest mem available matching other constraints if mem not specified, cost is tie breaker", cons: "cpu-cores=4", itypesToUse: []InstanceType{ {Id: "4", Name: "it-4", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 2}, {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 256, CpuCores: 4}, {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4, Cost: 50}, {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4, Cost: 100}, }, expectedItypes: []string{"it-2"}, }, } func constraint(region, cons string) *InstanceConstraint { return &InstanceConstraint{ Region: region, Constraints: constraints.MustParse(cons), } } func (s *instanceTypeSuite) TestGetMatchingInstanceTypes(c *gc.C) { for i, t := range getInstanceTypesTest { c.Logf("test %d: %s", i, t.about) itypesToUse := t.itypesToUse if itypesToUse == nil { itypesToUse = instanceTypes } itypes, err := getMatchingInstanceTypes(constraint("test", t.cons), itypesToUse) c.Assert(err, gc.IsNil) names := make([]string, len(itypes)) for i, itype := range itypes { if len(t.arches) > 0 { c.Check(itype.Arches, gc.DeepEquals, filterArches(itype.Arches, t.arches)) } else { c.Check(len(itype.Arches) > 0, gc.Equals, true) } names[i] = itype.Name } c.Check(names, gc.DeepEquals, t.expectedItypes) } } func (s *instanceTypeSuite) TestGetMatchingInstanceTypesErrors(c *gc.C) { _, err := getMatchingInstanceTypes(constraint("test", "cpu-power=9001"), nil) c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "cpu-power=9001"`) _, err = getMatchingInstanceTypes(constraint("test", "arch=i386 mem=8G"), instanceTypes) c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "arch=i386 mem=8192M"`) _, err = getMatchingInstanceTypes(constraint("test", "cpu-cores=9000"), instanceTypes) c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "cpu-cores=9000"`) _, err = getMatchingInstanceTypes(constraint("test", "mem=90000M"), instanceTypes) c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "mem=90000M"`) } var instanceTypeMatchTests = []struct { cons string itype string arches []string }{ {"", "m1.small", []string{"amd64", "armhf"}}, {"", "m1.large", []string{"amd64"}}, {"cpu-power=100", "m1.small", []string{"amd64", "armhf"}}, {"arch=amd64", "m1.small", []string{"amd64"}}, {"cpu-cores=3", "m1.xlarge", []string{"amd64"}}, {"cpu-power=", "t1.micro", []string{"amd64", "armhf"}}, {"cpu-power=500", "c1.medium", []string{"amd64", "armhf"}}, {"cpu-power=2000", "c1.xlarge", []string{"amd64"}}, {"cpu-power=2001", "cc1.4xlarge", []string{"amd64"}}, {"mem=2G", "m1.medium", []string{"amd64", "armhf"}}, {"arch=i386", "m1.small", nil}, {"cpu-power=100", "t1.micro", nil}, {"cpu-power=9001", "cc2.8xlarge", nil}, {"mem=1G", "t1.micro", nil}, {"arch=armhf", "c1.xlarge", nil}, } func (s *instanceTypeSuite) TestMatch(c *gc.C) { for i, t := range instanceTypeMatchTests { c.Logf("test %d", i) cons := constraints.MustParse(t.cons) var itype InstanceType for _, itype = range instanceTypes { if itype.Name == t.itype { break } } c.Assert(itype.Name, gc.Not(gc.Equals), "") itype, match := itype.match(cons) if len(t.arches) > 0 { c.Check(match, gc.Equals, true) expect := itype expect.Arches = t.arches c.Check(itype, gc.DeepEquals, expect) } else { c.Check(match, gc.Equals, false) c.Check(itype, gc.DeepEquals, InstanceType{}) } } } var byCostTests = []struct { about string itypesToUse []InstanceType expectedItypes []string }{ { about: "default to lowest cost", itypesToUse: []InstanceType{ {Id: "2", Name: "it-2", CpuCores: 2, Mem: 4096, Cost: 240}, {Id: "1", Name: "it-1", CpuCores: 1, Mem: 2048, Cost: 241}, }, expectedItypes: []string{ "it-2", "it-1", }, }, { about: "when no cost associated, pick lowest ram", itypesToUse: []InstanceType{ {Id: "2", Name: "it-2", CpuCores: 2, Mem: 4096}, {Id: "1", Name: "it-1", CpuCores: 1, Mem: 2048}, }, expectedItypes: []string{ "it-1", "it-2", }, }, { about: "when cost is the same, pick lowest ram", itypesToUse: []InstanceType{ {Id: "2", Name: "it-2", CpuCores: 2, Mem: 4096, Cost: 240}, {Id: "1", Name: "it-1", CpuCores: 1, Mem: 2048, Cost: 240}, }, expectedItypes: []string{ "it-1", "it-2", }, }, { about: "when cost and ram is the same, pick lowest cpu power", itypesToUse: []InstanceType{ {Id: "2", Name: "it-2", CpuCores: 2, CpuPower: CpuPower(200)}, {Id: "1", Name: "it-1", CpuCores: 1, CpuPower: CpuPower(100)}, }, expectedItypes: []string{ "it-1", "it-2", }, }, { about: "when cpu power is the same, pick the lowest cores", itypesToUse: []InstanceType{ {Id: "2", Name: "it-2", CpuCores: 2, CpuPower: CpuPower(200)}, {Id: "1", Name: "it-1", CpuCores: 1, CpuPower: CpuPower(200)}, }, expectedItypes: []string{ "it-1", "it-2", }, }, { about: "when cpu power is missing in side a, pick the lowest cores", itypesToUse: []InstanceType{ {Id: "2", Name: "it-2", CpuCores: 2, CpuPower: CpuPower(200)}, {Id: "1", Name: "it-1", CpuCores: 1}, }, expectedItypes: []string{ "it-1", "it-2", }, }, { about: "when cpu power is missing in side b, pick the lowest cores", itypesToUse: []InstanceType{ {Id: "2", Name: "it-2", CpuCores: 2}, {Id: "1", Name: "it-1", CpuCores: 1, CpuPower: CpuPower(200)}, }, expectedItypes: []string{ "it-1", "it-2", }, }, { about: "when cpu cores is the same, pick the lowest root disk size", itypesToUse: []InstanceType{ {Id: "2", Name: "it-2", CpuCores: 1, RootDisk: 8192}, {Id: "1", Name: "it-1", CpuCores: 1, RootDisk: 4096}, }, expectedItypes: []string{ "it-1", "it-2", }, }, } func (s *instanceTypeSuite) TestSortByCost(c *gc.C) { for i, t := range byCostTests { c.Logf("test %d: %s", i, t.about) sort.Sort(byCost(t.itypesToUse)) names := make([]string, len(t.itypesToUse)) for i, itype := range t.itypesToUse { names[i] = itype.Name } c.Check(names, gc.DeepEquals, t.expectedItypes) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/instances/image_test.go�����������������������0000644�0000153�0000161�00000021725�12321735642�027176� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instances import ( "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type imageSuite struct { testbase.LoggingSuite } func Test(t *testing.T) { gc.TestingT(t) } var _ = gc.Suite(&imageSuite{}) var jsonImagesContent = ` { "content_id": "com.ubuntu.cloud:released:aws", "products": { "com.ubuntu.cloud:server:12.04:amd64": { "release": "precise", "version": "12.04", "arch": "amd64", "versions": { "20121218": { "items": { "usee1pi": { "root_store": "instance", "virt": "pv", "region": "us-east-1", "id": "ami-00000011" }, "usww1pe": { "root_store": "ebs", "virt": "pv", "region": "us-west-1", "id": "ami-00000016" }, "apne1pe": { "root_store": "ebs", "virt": "pv", "region": "ap-northeast-1", "id": "ami-00000026" }, "test1pe": { "root_store": "ebs", "virt": "pv", "region": "test", "id": "ami-00000033" }, "test1he": { "root_store": "ebs", "virt": "hvm", "region": "test", "id": "ami-00000035" } }, "pubname": "ubuntu-precise-12.04-amd64-server-20121218", "label": "release" }, "20121118": { "items": { "apne1pe": { "root_store": "ebs", "virt": "pv", "region": "ap-northeast-1", "id": "ami-00000008" }, "test2he": { "root_store": "ebs", "virt": "hvm", "region": "test", "id": "ami-00000036" } }, "pubname": "ubuntu-precise-12.04-amd64-server-20121118", "label": "release" } } }, "com.ubuntu.cloud:server:12.04:arm": { "release": "precise", "version": "12.04", "arch": "arm", "versions": { "20121218": { "items": { "apne1pe": { "root_store": "ebs", "virt": "pv", "region": "ap-northeast-1", "id": "ami-00000023" }, "test1pe": { "root_store": "ebs", "virt": "pv", "region": "test", "id": "ami-00000034" }, "armo1pe": { "root_store": "ebs", "virt": "pv", "region": "arm-only", "id": "ami-00000036" } }, "pubname": "ubuntu-precise-12.04-arm-server-20121218", "label": "release" } } }, "com.ubuntu.cloud.daily:server:12.04:amd64": { "release": "precise", "version": "12.04", "arch": "amd64", "versions": { "20121218": { "items": { "apne1pe": { "root_store": "ebs", "virt": "pv", "region": "ap-northeast-1", "id": "ami-10000026" }, "test1pe": { "root_store": "ebs", "virt": "hvm", "region": "test", "id": "ami-10000035" } }, "pubname": "ubuntu-precise-12.04-amd64-daily-20121218", "label": "release" } } } }, "format": "products:1.0" } ` type instanceSpecTestParams struct { desc string region string arches []string stream string constraints string instanceTypes []InstanceType imageId string instanceTypeId string instanceTypeName string err string } func (p *instanceSpecTestParams) init() { if p.arches == nil { p.arches = []string{"amd64", "arm"} } if p.instanceTypes == nil { p.instanceTypes = []InstanceType{{Id: "1", Name: "it-1", Arches: []string{"amd64", "arm"}}} p.instanceTypeId = "1" p.instanceTypeName = "it-1" } } var pv = "pv" var findInstanceSpecTests = []instanceSpecTestParams{ { desc: "image exists in metadata", region: "test", imageId: "ami-00000033", instanceTypes: []InstanceType{ {Id: "1", Name: "it-1", Arches: []string{"amd64"}, VirtType: &pv, Mem: 512}, }, }, { desc: "explicit release stream", region: "test", stream: "released", imageId: "ami-00000035", instanceTypes: []InstanceType{ {Id: "1", Name: "it-1", Arches: []string{"amd64"}, VirtType: &hvm, Mem: 512, CpuCores: 2}, }, }, { desc: "non-release stream", region: "test", stream: "daily", imageId: "ami-10000035", instanceTypes: []InstanceType{ {Id: "1", Name: "it-1", Arches: []string{"amd64"}, VirtType: &hvm, Mem: 512, CpuCores: 2}, }, }, { desc: "multiple images exists in metadata, use most recent", region: "test", imageId: "ami-00000035", instanceTypes: []InstanceType{ {Id: "1", Name: "it-1", Arches: []string{"amd64"}, VirtType: &hvm, Mem: 512, CpuCores: 2}, }, }, { desc: "no image exists in metadata", region: "invalid-region", err: `no "precise" images in invalid-region with arches \[amd64 arm\]`, }, { desc: "no valid instance types", region: "test", instanceTypes: []InstanceType{}, err: `no instance types in test matching constraints ""`, }, { desc: "no compatible instance types", region: "arm-only", instanceTypes: []InstanceType{{Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 2048}}, err: `no "precise" images in arm-only matching instance types \[it-1\]`, }, } func (s *imageSuite) TestFindInstanceSpec(c *gc.C) { for _, t := range findInstanceSpecTests { c.Logf("test: %v", t.desc) t.init() cons := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{t.region, "ep"}, Series: []string{"precise"}, Arches: t.arches, Stream: t.stream, }) imageMeta, err := imagemetadata.GetLatestImageIdMetadata( []byte(jsonImagesContent), simplestreams.NewURLDataSource("test", "some-url", utils.VerifySSLHostnames), cons) c.Assert(err, gc.IsNil) var images []Image for _, imageMetadata := range imageMeta { im := *imageMetadata images = append(images, Image{ Id: im.Id, VirtType: im.VirtType, Arch: im.Arch, }) } spec, err := FindInstanceSpec(images, &InstanceConstraint{ Series: "precise", Region: t.region, Arches: t.arches, Constraints: constraints.MustParse(t.constraints), }, t.instanceTypes) if t.err != "" { c.Check(err, gc.ErrorMatches, t.err) continue } else { if !c.Check(err, gc.IsNil) { continue } c.Check(spec.Image.Id, gc.Equals, t.imageId) if len(t.instanceTypes) == 1 { c.Check(spec.InstanceType, gc.DeepEquals, t.instanceTypes[0]) } } } } var imageMatchtests = []struct { image Image itype InstanceType match bool }{ { image: Image{Arch: "amd64"}, itype: InstanceType{Arches: []string{"amd64"}}, match: true, }, { image: Image{Arch: "amd64"}, itype: InstanceType{Arches: []string{"amd64", "arm"}}, match: true, }, { image: Image{Arch: "amd64", VirtType: hvm}, itype: InstanceType{Arches: []string{"amd64"}, VirtType: &hvm}, match: true, }, { image: Image{Arch: "arm"}, itype: InstanceType{Arches: []string{"amd64"}}, }, { image: Image{Arch: "amd64", VirtType: hvm}, itype: InstanceType{Arches: []string{"amd64"}}, match: true, }, { image: Image{Arch: "amd64", VirtType: "pv"}, itype: InstanceType{Arches: []string{"amd64"}, VirtType: &hvm}, }, } func (s *imageSuite) TestImageMatch(c *gc.C) { for i, t := range imageMatchtests { c.Logf("test %d", i) c.Check(t.image.match(t.itype), gc.Equals, t.match) } } func (*imageSuite) TestImageMetadataToImagesAcceptsNil(c *gc.C) { c.Check(ImageMetadataToImages(nil), gc.HasLen, 0) } func (*imageSuite) TestImageMetadataToImagesConvertsSelectMetadata(c *gc.C) { input := []*imagemetadata.ImageMetadata{ { Id: "id", Storage: "storage-is-ignored", VirtType: "vtype", Arch: "arch", RegionAlias: "region-alias-is-ignored", RegionName: "region-name-is-ignored", Endpoint: "endpoint-is-ignored", }, } expectation := []Image{ { Id: "id", VirtType: "vtype", Arch: "arch", }, } c.Check(ImageMetadataToImages(input), gc.DeepEquals, expectation) } func (*imageSuite) TestImageMetadataToImagesMaintainsOrdering(c *gc.C) { input := []*imagemetadata.ImageMetadata{ {Id: "one", Arch: "Z80"}, {Id: "two", Arch: "i386"}, {Id: "three", Arch: "amd64"}, } expectation := []Image{ {Id: "one", Arch: "Z80"}, {Id: "two", Arch: "i386"}, {Id: "three", Arch: "amd64"}, } c.Check(ImageMetadataToImages(input), gc.DeepEquals, expectation) } �������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/instances/image.go����������������������������0000644�0000153�0000161�00000006625�12321735642�026141� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instances import ( "fmt" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/imagemetadata" ) // InstanceConstraint constrains the possible instances that may be // chosen by the environment provider. type InstanceConstraint struct { Region string Series string Arches []string Constraints constraints.Value // Optional filtering criteria not supported by all providers. These attributes are not specified // by the user as a constraint but rather passed in by the provider implementation to restrict the // choice of available images. Storage *string } // String returns a human readable form of this InstanceConstaint. func (ic *InstanceConstraint) String() string { storage := "none" if ic.Storage != nil { storage = *ic.Storage } return fmt.Sprintf( "{region: %s, series: %s, arches: %s, constraints: %s, storage: %s}", ic.Region, ic.Series, ic.Arches, ic.Constraints, storage, ) } // InstanceSpec holds an instance type name and the chosen image info. type InstanceSpec struct { InstanceType InstanceType Image Image } // FindInstanceSpec returns an InstanceSpec satisfying the supplied InstanceConstraint. // possibleImages contains a list of images matching the InstanceConstraint. // allInstanceTypes provides information on every known available instance type (name, memory, cpu cores etc) on // which instances can be run. The InstanceConstraint is used to filter allInstanceTypes and then a suitable image // compatible with the matching instance types is returned. func FindInstanceSpec(possibleImages []Image, ic *InstanceConstraint, allInstanceTypes []InstanceType) (*InstanceSpec, error) { matchingTypes, err := getMatchingInstanceTypes(ic, allInstanceTypes) if err != nil { return nil, err } for _, itype := range matchingTypes { for _, image := range possibleImages { if image.match(itype) { return &InstanceSpec{itype, image}, nil } } } if len(possibleImages) == 0 || len(matchingTypes) == 0 { return nil, fmt.Errorf("no %q images in %s with arches %s", ic.Series, ic.Region, ic.Arches) } names := make([]string, len(matchingTypes)) for i, itype := range matchingTypes { names[i] = itype.Name } return nil, fmt.Errorf("no %q images in %s matching instance types %v", ic.Series, ic.Region, names) } // Image holds the attributes that vary amongst relevant images for // a given series in a given region. type Image struct { Id string Arch string // The type of virtualisation supported by this image. VirtType string } // match returns true if the image can run on the supplied instance type. func (image Image) match(itype InstanceType) bool { // The virtualisation type is optional. if itype.VirtType != nil && image.VirtType != *itype.VirtType { return false } for _, arch := range itype.Arches { if arch == image.Arch { return true } } return false } // ImageMetadataToImages converts an array of ImageMetadata pointers (as // returned by imagemetadata.Fetch) to an array of Image objects (as required // by instances.FindInstanceSpec). func ImageMetadataToImages(inputs []*imagemetadata.ImageMetadata) []Image { result := make([]Image, len(inputs)) for index, input := range inputs { result[index] = Image{ Id: input.Id, VirtType: input.VirtType, Arch: input.Arch, } } return result } �����������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/��������������������������������0000755�0000153�0000161�00000000000�12321736000�025300� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/validation.go�������������������0000644�0000153�0000161�00000003270�12321735776�030006� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata import ( "fmt" "launchpad.net/juju-core/environs/simplestreams" ) // ValidateImageMetadata attempts to load image metadata for the specified cloud attributes and stream // and returns any image ids found, or an error if the metadata could not be loaded. func ValidateImageMetadata(params *simplestreams.MetadataLookupParams) ([]string, *simplestreams.ResolveInfo, error) { if params.Series == "" { return nil, nil, fmt.Errorf("required parameter series not specified") } if params.Region == "" { return nil, nil, fmt.Errorf("required parameter region not specified") } if params.Endpoint == "" { return nil, nil, fmt.Errorf("required parameter endpoint not specified") } if len(params.Architectures) == 0 { return nil, nil, fmt.Errorf("required parameter arches not specified") } if len(params.Sources) == 0 { return nil, nil, fmt.Errorf("required parameter sources not specified") } imageConstraint := NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{params.Region, params.Endpoint}, Series: []string{params.Series}, Arches: params.Architectures, Stream: params.Stream, }) matchingImages, resolveInfo, err := Fetch(params.Sources, simplestreams.DefaultIndexPath, imageConstraint, false) if err != nil { return nil, resolveInfo, err } if len(matchingImages) == 0 { return nil, resolveInfo, fmt.Errorf("no matching images found for constraint %+v", imageConstraint) } image_ids := make([]string, len(matchingImages)) for i, im := range matchingImages { image_ids[i] = im.Id } return image_ids, resolveInfo, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/export_test.go������������������0000644�0000153�0000161�00000000527�12321735642�030226� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata // SetSigningPublicKey sets a new public key for testing and returns the original key. func SetSigningPublicKey(key string) string { oldKey := simplestreamsImagesPublicKey simplestreamsImagesPublicKey = key return oldKey } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/simplestreams.go����������������0000644�0000153�0000161�00000023123�12321735642�030533� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The imagemetadata package supports locating, parsing, and filtering Ubuntu image metadata in simplestreams format. // See http://launchpad.net/simplestreams and in particular the doc/README file in that project for more information // about the file formats. package imagemetadata import ( "fmt" "sort" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/juju/arch" ) func init() { simplestreams.RegisterStructTags(ImageMetadata{}) } const ( ImageIds = "image-ids" ) // simplestreamsImagesPublicKey is the public key required to // authenticate the simple streams data on http://cloud-images.ubuntu.com. // Declared as a var so it can be overidden for testing. // See http://bazaar.launchpad.net/~smoser/simplestreams/trunk/view/head:/examples/keys/cloud-images.pub var simplestreamsImagesPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.12 (GNU/Linux) mQINBFCMc9EBEADDKn9mOi9VZhW+0cxmu3aFZWMg0p7NEKuIokkEdd6P+BRITccO ddDLaBuuamMbt/V1vrxWC5J+UXe33TwgO6KGfH+ECnXD5gYdEOyjVKkUyIzYV5RV U5BMrxTukHuh+PkcMVUy5vossCk9MivtCRIqM6eRqfeXv6IBV9MFkAbG3x96ZNI/ TqaWTlaHGszz2Axf9JccHCNfb3muLI2uVnUaojtDiZPm9SHTn6O0p7Tz7M7+P8qy vc6bdn5FYAk+Wbo+zejYVBG/HLLE4+fNZPESGVCWZtbZODBPxppTnNVm3E84CTFt pmWFBvBE/q2G9e8s5/mP2ATrzLdUKMxr3vcbNX+NY1Uyvn0Z02PjbxThiz1G+4qh 6Ct7gprtwXPOB/bCITZL9YLrchwXiNgLLKcGF0XjlpD1hfELGi0aPZaHFLAa6qq8 Ro9WSJljY/Z0g3woj6sXpM9TdWe/zaWhxBGmteJl33WBV7a1GucN0zF1dHIvev4F krp13Uej3bMWLKUWCmZ01OHStLASshTqVxIBj2rgsxIcqH66DKTSdZWyBQtgm/kC qBvuoQLFfUgIlGZihTQ96YZXqn+VfBiFbpnh1vLt24CfnVdKmzibp48KkhfqduDE Xxx/f/uZENH7t8xCuNd3p+u1zemGNnxuO8jxS6Ico3bvnJaG4DAl48vaBQARAQAB tG9VYnVudHUgQ2xvdWQgSW1hZ2UgQnVpbGRlciAoQ2Fub25pY2FsIEludGVybmFs IENsb3VkIEltYWdlIEJ1aWxkZXIpIDx1YnVudHUtY2xvdWRidWlsZGVyLW5vcmVw bHlAY2Fub25pY2FsLmNvbT6JAjgEEwECACIFAlCMc9ECGwMGCwkIBwMCBhUIAgkK CwQWAgMBAh4BAheAAAoJEH/z9AhHbPEAvRIQAMLE4ZMYiLvwSoWPAicM+3FInaqP 2rf1ZEf1k6175/G2n8cG3vK0nIFQE9Cus+ty2LrTggm79onV2KBGGScKe3ga+meO txj601Wd7zde10IWUa1wlTxPXBxLo6tpF4s4aw6xWOf4OFqYfPU4esKblFYn1eMK Dd53s3/123u8BZqzFC8WSMokY6WgBa+hvr5J3qaNT95UXo1tkMf65ZXievcQJ+Hr bp1m5pslHgd5PqzlultNWePwzqmHXXf14zI1QKtbc4UjXPQ+a59ulZLVdcpvmbjx HdZfK0NJpQX+j5PU6bMuQ3QTMscuvrH4W41/zcZPFaPkdJE5+VcYDL17DBFVzknJ eC1uzNHxRqSMRQy9fzOuZ72ARojvL3+cyPR1qrqSCceX1/Kp838P2/CbeNvJxadt liwI6rzUgK7mq1Bw5LTyBo3mLwzRJ0+eJHevNpxl6VoFyuoA3rCeoyE4on3oah1G iAJt576xXMDoa1Gdj3YtnZItEaX3jb9ZB3iz9WkzZWlZsssdyZMNmpYV30Ayj3CE KyurYF9lzIQWyYsNPBoXORNh73jkHJmL6g1sdMaxAZeQqKqznXbuhBbt8lkbEHMJ Stxc2IGZaNpQ+/3LCwbwCphVnSMq+xl3iLg6c0s4uRn6FGX+8aknmc/fepvRe+ba ntqvgz+SMPKrjeevuQINBFCMc9EBEADKGFPKBL7/pMSTKf5YH1zhFH2lr7tf5hbz ztsx6j3y+nODiaQumdG+TPMbrFlgRlJ6Ah1FTuJZqdPYObGSQ7qd/VvvYZGnDYJv Z1kPkNDmCJrWJs+6PwNARvyLw2bMtjCIOAq/k8wByKkMzegobJgWsbr2Jb5fT4cv FxYpm3l0QxQSw49rriO5HmwyiyG1ncvaFUcpxXJY8A2s7qX1jmjsqDY1fWsv5PaN ue0Fr3VXfOi9p+0CfaPY0Pl4GHzat/D+wLwnOhnjl3hFtfbhY5bPl5+cD51SbOnh 2nFv+bUK5HxiZlz0bw8hTUBN3oSbAC+2zViRD/9GaBYY1QjimOuAfpO1GZmqohVI msZKxHNIIsk5H98mN2+LB3vH+B6zrSMDm3d2Hi7ZA8wH26mLIKLbVkh7hr8RGQjf UZRxeQEf+f8F3KVoSqmfXGJfBMUtGQMTkaIeEFpMobVeHZZ3wk+Wj3dCMZ6bbt2i QBaoa7SU5ZmRShJkPJzCG3SkqN+g9ZcbFMQsybl+wLN7UnZ2MbSk7JEy6SLsyuVi 7EjLmqHmG2gkybisnTu3wjJezpG12oz//cuylOzjuPWUWowVQQiLs3oANzYdZ0Hp SuNjjtEILSRnN5FAeogs0AKH6sy3kKjxtlj764CIgn1hNidSr2Hyb4xbJ/1GE3Rk sjJi6uYIJwARAQABiQIfBBgBAgAJBQJQjHPRAhsMAAoJEH/z9AhHbPEA6IsP/3jJ DaowJcKOBhU2TXZglHM+ZRMauHRZavo+xAKmqgQc/izgtyMxsLwJQ+wcTEQT5uqE 4DoWH2T7DGiHZd/89Qe6HuRExR4p7lQwUop7kdoabqm1wQfcqr+77Znp1+KkRDyS lWfbsh9ARU6krQGryODEOpXJdqdzTgYhdbVRxq6dUopz1Gf+XDreFgnqJ+okGve2 fJGERKYynUmHxkFZJPWZg5ifeGVt+YY6vuOCg489dzx/CmULpjZeiOQmWyqUzqy2 QJ70/sC8BJYCjsESId9yPmgdDoMFd+gf3jhjpuZ0JHTeUUw+ncf+1kRf7LAALPJp 2PTSo7VXUwoEXDyUTM+dI02dIMcjTcY4yxvnpxRFFOtklvXt8Pwa9x/aCmJb9f0E 5FO0nj7l9pRd2g7UCJWETFRfSW52iktvdtDrBCft9OytmTl492wAmgbbGeoRq3ze QtzkRx9cPiyNQokjXXF+SQcq586oEd8K/JUSFPdvth3IoKlfnXSQnt/hRKv71kbZ IXmR3B/q5x2Msr+NfUxyXfUnYOZ5KertdprUfbZjudjmQ78LOvqPF8TdtHg3gD2H +G2z+IoH7qsOsc7FaJsIIa4+dljwV3QZTE7JFmsas90bRcMuM4D37p3snOpHAHY3 p7vH1ewg+vd9ySST0+OkWXYpbMOIARfBKyrGM3nu =+MFT -----END PGP PUBLIC KEY BLOCK----- ` const ( // The location where Ubuntu cloud image metadata is published for // public consumption. UbuntuCloudImagesURL = "http://cloud-images.ubuntu.com" // The path where released image metadata is found. ReleasedImagesPath = "releases" ) // This needs to be a var so we can override it for testing and in bootstrap. var DefaultBaseURL = UbuntuCloudImagesURL // ImageConstraint defines criteria used to find an image metadata record. type ImageConstraint struct { simplestreams.LookupParams } func NewImageConstraint(params simplestreams.LookupParams) *ImageConstraint { if len(params.Series) == 0 { params.Series = simplestreams.SupportedSeries() } if len(params.Arches) == 0 { params.Arches = arch.AllSupportedArches } return &ImageConstraint{LookupParams: params} } const ( // Used to specify the released image metadata. ReleasedStream = "released" ) // idStream returns the string to use in making a product id // for the given product stream. func idStream(stream string) string { idstream := "" if stream != "" && stream != ReleasedStream { idstream = "." + stream } return idstream } // Generates a string array representing product ids formed similarly to an ISCSI qualified name (IQN). func (ic *ImageConstraint) Ids() ([]string, error) { stream := idStream(ic.Stream) nrArches := len(ic.Arches) nrSeries := len(ic.Series) ids := make([]string, nrArches*nrSeries) for i, arch := range ic.Arches { for j, series := range ic.Series { version, err := simplestreams.SeriesVersion(series) if err != nil { return nil, err } ids[j*nrArches+i] = fmt.Sprintf("com.ubuntu.cloud%s:server:%s:%s", stream, version, arch) } } return ids, nil } // ImageMetadata holds information about a particular cloud image. type ImageMetadata struct { Id string `json:"id"` Storage string `json:"root_store,omitempty"` VirtType string `json:"virt,omitempty"` Arch string `json:"arch,omitempty"` Version string `json:"version,omitempty"` RegionAlias string `json:"crsn,omitempty"` RegionName string `json:"region,omitempty"` Endpoint string `json:"endpoint,omitempty"` Stream string `json:"-"` } func (im *ImageMetadata) String() string { return fmt.Sprintf("%#v", im) } func (im *ImageMetadata) productId() string { stream := idStream(im.Stream) return fmt.Sprintf("com.ubuntu.cloud%s:server:%s:%s", stream, im.Version, im.Arch) } // Fetch returns a list of images for the specified cloud matching the constraint. // The base URL locations are as specified - the first location which has a file is the one used. // Signed data is preferred, but if there is no signed data available and onlySigned is false, // then unsigned data is used. func Fetch( sources []simplestreams.DataSource, indexPath string, cons *ImageConstraint, onlySigned bool) ([]*ImageMetadata, *simplestreams.ResolveInfo, error) { params := simplestreams.ValueParams{ DataType: ImageIds, FilterFunc: appendMatchingImages, ValueTemplate: ImageMetadata{}, PublicKey: simplestreamsImagesPublicKey, } items, resolveInfo, err := simplestreams.GetMetadata(sources, indexPath, cons, onlySigned, params) if err != nil { return nil, resolveInfo, err } metadata := make([]*ImageMetadata, len(items)) for i, md := range items { metadata[i] = md.(*ImageMetadata) } // Sorting the metadata is not strictly necessary, but it ensures consistent ordering for // all compilers, and it just makes it easier to look at the data. Sort(metadata) return metadata, resolveInfo, nil } // Sort sorts a slice of ImageMetadata in ascending order of their id // in order to ensure the results of Fetch are ordered deterministically. func Sort(metadata []*ImageMetadata) { sort.Sort(byId(metadata)) } type byId []*ImageMetadata func (b byId) Len() int { return len(b) } func (b byId) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b byId) Less(i, j int) bool { return b[i].Id < b[j].Id } type imageKey struct { vtype string arch string version string region string storage string } // appendMatchingImages updates matchingImages with image metadata records from images which belong to the // specified region. If an image already exists in matchingImages, it is not overwritten. func appendMatchingImages(source simplestreams.DataSource, matchingImages []interface{}, images map[string]interface{}, cons simplestreams.LookupConstraint) []interface{} { imagesMap := make(map[imageKey]*ImageMetadata, len(matchingImages)) for _, val := range matchingImages { im := val.(*ImageMetadata) imagesMap[imageKey{im.VirtType, im.Arch, im.Version, im.RegionName, im.Storage}] = im } for _, val := range images { im := val.(*ImageMetadata) if cons != nil && cons.Params().Region != "" && cons.Params().Region != im.RegionName { continue } if _, ok := imagesMap[imageKey{im.VirtType, im.Arch, im.Version, im.RegionName, im.Storage}]; !ok { matchingImages = append(matchingImages, im) } } return matchingImages } // GetLatestImageIdMetadata is provided so it can be call by tests outside the imagemetadata package. func GetLatestImageIdMetadata(data []byte, source simplestreams.DataSource, cons *ImageConstraint) ([]*ImageMetadata, error) { metadata, err := simplestreams.ParseCloudMetadata(data, "products:1.0", "<unknown>", ImageMetadata{}) if err != nil { return nil, err } items, err := simplestreams.GetLatestMetadata(metadata, cons, source, appendMatchingImages) if err != nil { return nil, err } result := make([]*ImageMetadata, len(items)) for i, md := range items { result[i] = md.(*ImageMetadata) } return result, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/generate.go���������������������0000644�0000153�0000161�00000006757�12321735776�027463� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata import ( "bytes" "fmt" "path/filepath" "time" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" ) // MergeAndWriteMetadata reads the existing metadata from storage (if any), // and merges it with supplied metadata, writing the resulting metadata is written to storage. func MergeAndWriteMetadata(series string, metadata []*ImageMetadata, cloudSpec *simplestreams.CloudSpec, metadataStore storage.Storage) error { existingMetadata, err := readMetadata(metadataStore) if err != nil { return err } seriesVersion, err := simplestreams.SeriesVersion(series) if err != nil { return err } toWrite, allCloudSpec := mergeMetadata(seriesVersion, cloudSpec, metadata, existingMetadata) return writeMetadata(toWrite, allCloudSpec, metadataStore) } // readMetadata reads the image metadata from metadataStore. func readMetadata(metadataStore storage.Storage) ([]*ImageMetadata, error) { // Read any existing metadata so we can merge the new tools metadata with what's there. dataSource := storage.NewStorageSimpleStreamsDataSource("existing metadata", metadataStore, storage.BaseImagesPath) imageConstraint := NewImageConstraint(simplestreams.LookupParams{}) existingMetadata, _, err := Fetch( []simplestreams.DataSource{dataSource}, simplestreams.DefaultIndexPath, imageConstraint, false) if err != nil && !errors.IsNotFoundError(err) { return nil, err } return existingMetadata, nil } func mapKey(im *ImageMetadata) string { return fmt.Sprintf("%s-%s", im.productId(), im.RegionName) } // mergeMetadata merges the newMetadata into existingMetadata, overwriting existing matching image records. func mergeMetadata(seriesVersion string, cloudSpec *simplestreams.CloudSpec, newMetadata, existingMetadata []*ImageMetadata) ([]*ImageMetadata, []simplestreams.CloudSpec) { var toWrite = make([]*ImageMetadata, len(newMetadata)) imageIds := make(map[string]bool) for i, im := range newMetadata { newRecord := *im newRecord.Version = seriesVersion newRecord.RegionName = cloudSpec.Region newRecord.Endpoint = cloudSpec.Endpoint toWrite[i] = &newRecord imageIds[mapKey(&newRecord)] = true } regions := make(map[string]bool) var allCloudSpecs = []simplestreams.CloudSpec{*cloudSpec} for _, im := range existingMetadata { if _, ok := imageIds[mapKey(im)]; ok { continue } toWrite = append(toWrite, im) if _, ok := regions[im.RegionName]; ok { continue } regions[im.RegionName] = true existingCloudSpec := simplestreams.CloudSpec{im.RegionName, im.Endpoint} allCloudSpecs = append(allCloudSpecs, existingCloudSpec) } return toWrite, allCloudSpecs } type MetadataFile struct { Path string Data []byte } // writeMetadata generates some basic simplestreams metadata using the specified cloud and image details and writes // it to the supplied store. func writeMetadata(metadata []*ImageMetadata, cloudSpec []simplestreams.CloudSpec, metadataStore storage.Storage) error { index, products, err := MarshalImageMetadataJSON(metadata, cloudSpec, time.Now()) if err != nil { return err } metadataInfo := []MetadataFile{ {simplestreams.UnsignedIndex, index}, {ProductMetadataPath, products}, } for _, md := range metadataInfo { err = metadataStore.Put( filepath.Join(storage.BaseImagesPath, md.Path), bytes.NewReader(md.Data), int64(len(md.Data))) if err != nil { return err } } return nil } �����������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/simplestreams_test.go�����������0000644�0000153�0000161�00000032271�12321735642�031576� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata_test import ( "bytes" "flag" "net/http" "reflect" "strings" "testing" "launchpad.net/goamz/aws" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/simplestreams" sstesting "launchpad.net/juju-core/environs/simplestreams/testing" "launchpad.net/juju-core/utils" ) var live = flag.Bool("live", false, "Include live simplestreams tests") var vendor = flag.String("vendor", "", "The vendor representing the source of the simplestream data") type liveTestData struct { baseURL string requireSigned bool validCloudSpec simplestreams.CloudSpec } var liveUrls = map[string]liveTestData{ "ec2": { baseURL: imagemetadata.DefaultBaseURL, requireSigned: true, validCloudSpec: simplestreams.CloudSpec{"us-east-1", aws.Regions["us-east-1"].EC2Endpoint}, }, "canonistack": { baseURL: "https://swift.canonistack.canonical.com/v1/AUTH_a48765cc0e864be980ee21ae26aaaed4/simplestreams/data", requireSigned: false, validCloudSpec: simplestreams.CloudSpec{"lcy01", "https://keystone.canonistack.canonical.com:443/v2.0/"}, }, } func Test(t *testing.T) { if *live { if *vendor == "" { t.Fatal("missing vendor") } var ok bool var testData liveTestData if testData, ok = liveUrls[*vendor]; !ok { keys := reflect.ValueOf(liveUrls).MapKeys() t.Fatalf("Unknown vendor %s. Must be one of %s", *vendor, keys) } registerLiveSimpleStreamsTests(testData.baseURL, imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: testData.validCloudSpec, Series: []string{"quantal"}, Arches: []string{"amd64"}, }), testData.requireSigned) } registerSimpleStreamsTests() gc.TestingT(t) } func registerSimpleStreamsTests() { gc.Suite(&simplestreamsSuite{ LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{ Source: simplestreams.NewURLDataSource( "test roundtripper", "test:", utils.VerifySSLHostnames), RequireSigned: false, DataType: imagemetadata.ImageIds, ValidConstraint: imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ Region: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", }, Series: []string{"precise"}, Arches: []string{"amd64", "arm"}, }), }, }) gc.Suite(&signedSuite{}) } func registerLiveSimpleStreamsTests(baseURL string, validImageConstraint simplestreams.LookupConstraint, requireSigned bool) { gc.Suite(&sstesting.LocalLiveSimplestreamsSuite{ Source: simplestreams.NewURLDataSource("test", baseURL, utils.VerifySSLHostnames), RequireSigned: requireSigned, DataType: imagemetadata.ImageIds, ValidConstraint: validImageConstraint, }) } type simplestreamsSuite struct { sstesting.LocalLiveSimplestreamsSuite sstesting.TestDataSuite } func (s *simplestreamsSuite) SetUpSuite(c *gc.C) { s.LocalLiveSimplestreamsSuite.SetUpSuite(c) s.TestDataSuite.SetUpSuite(c) } func (s *simplestreamsSuite) TearDownSuite(c *gc.C) { s.TestDataSuite.TearDownSuite(c) s.LocalLiveSimplestreamsSuite.TearDownSuite(c) } var fetchTests = []struct { region string version string arches []string images []*imagemetadata.ImageMetadata }{ { region: "us-east-1", version: "12.04", arches: []string{"amd64", "arm"}, images: []*imagemetadata.ImageMetadata{ { Id: "ami-442ea674", VirtType: "hvm", Arch: "amd64", RegionName: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", Storage: "ebs", }, { Id: "ami-442ea684", VirtType: "pv", Arch: "amd64", RegionName: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", Storage: "instance", }, { Id: "ami-442ea699", VirtType: "pv", Arch: "arm", RegionName: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", Storage: "ebs", }, }, }, { region: "us-east-1", version: "12.04", arches: []string{"amd64"}, images: []*imagemetadata.ImageMetadata{ { Id: "ami-442ea674", VirtType: "hvm", Arch: "amd64", RegionName: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", Storage: "ebs", }, { Id: "ami-442ea684", VirtType: "pv", Arch: "amd64", RegionName: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", Storage: "instance", }, }, }, { region: "us-east-1", version: "12.04", arches: []string{"arm"}, images: []*imagemetadata.ImageMetadata{ { Id: "ami-442ea699", VirtType: "pv", Arch: "arm", RegionName: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", Storage: "ebs", }, }, }, { region: "us-east-1", version: "12.04", arches: []string{"amd64"}, images: []*imagemetadata.ImageMetadata{ { Id: "ami-442ea674", VirtType: "hvm", Arch: "amd64", RegionName: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", Storage: "ebs", }, { Id: "ami-442ea684", VirtType: "pv", Arch: "amd64", RegionName: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", Storage: "instance", }, }, }, { version: "12.04", arches: []string{"amd64"}, images: []*imagemetadata.ImageMetadata{ { Id: "ami-26745463", VirtType: "pv", Arch: "amd64", RegionName: "au-east-2", Endpoint: "https://somewhere-else", Storage: "ebs", }, { Id: "ami-26745464", VirtType: "pv", Arch: "amd64", RegionName: "au-east-1", Endpoint: "https://somewhere", Storage: "ebs", }, { Id: "ami-442ea674", VirtType: "hvm", Arch: "amd64", RegionName: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", Storage: "ebs", }, { Id: "ami-442ea675", VirtType: "hvm", Arch: "amd64", RegionAlias: "uswest3", RegionName: "us-west-3", Endpoint: "https://ec2.us-west-3.amazonaws.com", Storage: "ebs", }, { Id: "ami-442ea684", VirtType: "pv", Arch: "amd64", RegionName: "us-east-1", Endpoint: "https://ec2.us-east-1.amazonaws.com", Storage: "instance", }, }, }, } func (s *simplestreamsSuite) TestFetch(c *gc.C) { for i, t := range fetchTests { c.Logf("test %d", i) cloudSpec := simplestreams.CloudSpec{t.region, "https://ec2.us-east-1.amazonaws.com"} if t.region == "" { cloudSpec = simplestreams.EmptyCloudSpec } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: cloudSpec, Series: []string{"precise"}, Arches: t.arches, }) // Add invalid datasource and check later that resolveInfo is correct. invalidSource := simplestreams.NewURLDataSource("invalid", "file://invalid", utils.VerifySSLHostnames) images, resolveInfo, err := imagemetadata.Fetch( []simplestreams.DataSource{invalidSource, s.Source}, simplestreams.DefaultIndexPath, imageConstraint, s.RequireSigned) if !c.Check(err, gc.IsNil) { continue } for _, testImage := range t.images { testImage.Version = t.version } c.Check(images, gc.DeepEquals, t.images) c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test roundtripper", Signed: s.RequireSigned, IndexURL: "test:/streams/v1/index.json", MirrorURL: "", }) } } type productSpecSuite struct{} var _ = gc.Suite(&productSpecSuite{}) func (s *productSpecSuite) TestIdWithDefaultStream(c *gc.C) { imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ Series: []string{"precise"}, Arches: []string{"amd64"}, }) for _, stream := range []string{"", "released"} { imageConstraint.Stream = stream ids, err := imageConstraint.Ids() c.Assert(err, gc.IsNil) c.Assert(ids, gc.DeepEquals, []string{"com.ubuntu.cloud:server:12.04:amd64"}) } } func (s *productSpecSuite) TestId(c *gc.C) { imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ Series: []string{"precise"}, Arches: []string{"amd64"}, Stream: "daily", }) ids, err := imageConstraint.Ids() c.Assert(err, gc.IsNil) c.Assert(ids, gc.DeepEquals, []string{"com.ubuntu.cloud.daily:server:12.04:amd64"}) } func (s *productSpecSuite) TestIdMultiArch(c *gc.C) { imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ Series: []string{"precise"}, Arches: []string{"amd64", "i386"}, Stream: "daily", }) ids, err := imageConstraint.Ids() c.Assert(err, gc.IsNil) c.Assert(ids, gc.DeepEquals, []string{ "com.ubuntu.cloud.daily:server:12.04:amd64", "com.ubuntu.cloud.daily:server:12.04:i386"}) } type signedSuite struct { origKey string } var testRoundTripper *jujutest.ProxyRoundTripper func init() { testRoundTripper = &jujutest.ProxyRoundTripper{} testRoundTripper.RegisterForScheme("signedtest") } func (s *signedSuite) SetUpSuite(c *gc.C) { var imageData = map[string]string{ "/unsigned/streams/v1/index.json": unsignedIndex, "/unsigned/streams/v1/image_metadata.json": unsignedProduct, } // Set up some signed data from the unsigned data. // Overwrite the product path to use the sjson suffix. rawUnsignedIndex := strings.Replace( unsignedIndex, "streams/v1/image_metadata.json", "streams/v1/image_metadata.sjson", -1) r := bytes.NewReader([]byte(rawUnsignedIndex)) signedData, err := simplestreams.Encode( r, sstesting.SignedMetadataPrivateKey, sstesting.PrivateKeyPassphrase) c.Assert(err, gc.IsNil) imageData["/signed/streams/v1/index.sjson"] = string(signedData) // Replace the image id in the unsigned data with a different one so we can test that the right // image id is used. rawUnsignedProduct := strings.Replace( unsignedProduct, "ami-26745463", "ami-123456", -1) r = bytes.NewReader([]byte(rawUnsignedProduct)) signedData, err = simplestreams.Encode( r, sstesting.SignedMetadataPrivateKey, sstesting.PrivateKeyPassphrase) c.Assert(err, gc.IsNil) imageData["/signed/streams/v1/image_metadata.sjson"] = string(signedData) testRoundTripper.Sub = jujutest.NewCannedRoundTripper( imageData, map[string]int{"signedtest://unauth": http.StatusUnauthorized}) s.origKey = imagemetadata.SetSigningPublicKey(sstesting.SignedMetadataPublicKey) } func (s *signedSuite) TearDownSuite(c *gc.C) { testRoundTripper.Sub = nil imagemetadata.SetSigningPublicKey(s.origKey) } func (s *signedSuite) TestSignedImageMetadata(c *gc.C) { signedSource := simplestreams.NewURLDataSource("test", "signedtest://host/signed", utils.VerifySSLHostnames) imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"}, Series: []string{"precise"}, Arches: []string{"amd64"}, }) images, resolveInfo, err := imagemetadata.Fetch( []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, imageConstraint, true) c.Assert(err, gc.IsNil) c.Assert(len(images), gc.Equals, 1) c.Assert(images[0].Id, gc.Equals, "ami-123456") c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: true, IndexURL: "signedtest://host/signed/streams/v1/index.sjson", MirrorURL: "", }) } func (s *signedSuite) TestSignedImageMetadataInvalidSignature(c *gc.C) { signedSource := simplestreams.NewURLDataSource("test", "signedtest://host/signed", utils.VerifySSLHostnames) imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{"us-east-1", "https://ec2.us-east-1.amazonaws.com"}, Series: []string{"precise"}, Arches: []string{"amd64"}, }) imagemetadata.SetSigningPublicKey(s.origKey) _, _, err := imagemetadata.Fetch( []simplestreams.DataSource{signedSource}, simplestreams.DefaultIndexPath, imageConstraint, true) c.Assert(err, gc.ErrorMatches, "cannot read index data.*") } var unsignedIndex = ` { "index": { "com.ubuntu.cloud:released:precise": { "updated": "Wed, 01 May 2013 13:31:26 +0000", "clouds": [ { "region": "us-east-1", "endpoint": "https://ec2.us-east-1.amazonaws.com" } ], "cloudname": "aws", "datatype": "image-ids", "format": "products:1.0", "products": [ "com.ubuntu.cloud:server:12.04:amd64" ], "path": "streams/v1/image_metadata.json" } }, "updated": "Wed, 01 May 2013 13:31:26 +0000", "format": "index:1.0" } ` var unsignedProduct = ` { "updated": "Wed, 01 May 2013 13:31:26 +0000", "content_id": "com.ubuntu.cloud:released:aws", "products": { "com.ubuntu.cloud:server:12.04:amd64": { "release": "precise", "version": "12.04", "arch": "amd64", "region": "us-east-1", "endpoint": "https://somewhere", "versions": { "20121218": { "region": "us-east-1", "endpoint": "https://somewhere-else", "items": { "usww1pe": { "root_store": "ebs", "virt": "pv", "id": "ami-26745463" } }, "pubname": "ubuntu-precise-12.04-amd64-server-20121218", "label": "release" } } } }, "format": "products:1.0" } ` ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/urls.go�������������������������0000644�0000153�0000161�00000005327�12321735642�026636� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata import ( "fmt" "net/url" "strings" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/utils" ) // SupportsCustomSources represents an environment that // can host image metadata at provider specific sources. type SupportsCustomSources interface { GetImageSources() ([]simplestreams.DataSource, error) } // GetMetadataSources returns the sources to use when looking for // simplestreams image id metadata for the given stream. If env implements // SupportsCustomSources, the sources returned from that method will also // be considered. func GetMetadataSources(env environs.ConfigGetter) ([]simplestreams.DataSource, error) { var sources []simplestreams.DataSource config := env.Config() if userURL, ok := config.ImageMetadataURL(); ok { verify := utils.VerifySSLHostnames if !config.SSLHostnameVerification() { verify = utils.NoVerifySSLHostnames } sources = append(sources, simplestreams.NewURLDataSource("image-metadata-url", userURL, verify)) } if custom, ok := env.(SupportsCustomSources); ok { customSources, err := custom.GetImageSources() if err != nil { return nil, err } sources = append(sources, customSources...) } defaultURL, err := ImageMetadataURL(DefaultBaseURL, config.ImageStream()) if err != nil { return nil, err } if defaultURL != "" { sources = append(sources, simplestreams.NewURLDataSource("default cloud images", defaultURL, utils.VerifySSLHostnames)) } return sources, nil } // ImageMetadataURL returns a valid image metadata URL constructed from source. // source may be a directory, or a URL like file://foo or http://foo. func ImageMetadataURL(source, stream string) (string, error) { if source == "" { return "", nil } // If the image metadata is coming from the official cloud images site, // set up the correct path according to the images stream requested. if source == UbuntuCloudImagesURL { cloudImagesPath := ReleasedImagesPath if stream != "" && stream != ReleasedStream { cloudImagesPath = stream } source = fmt.Sprintf("%s/%s", source, cloudImagesPath) } // If source is a raw directory, we need to append the file:// prefix // so it can be used as a URL. defaultURL := source u, err := url.Parse(source) if err != nil { return "", fmt.Errorf("invalid default image metadata URL %s: %v", defaultURL, err) } if u.Scheme == "" { defaultURL = "file://" + defaultURL if !strings.HasSuffix(defaultURL, "/"+storage.BaseImagesPath) { defaultURL = fmt.Sprintf("%s/%s", defaultURL, storage.BaseImagesPath) } } return defaultURL, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/marshal_test.go�����������������0000644�0000153�0000161�00000007352�12321735642�030337� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata_test import ( "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/testing/testbase" ) var _ = gc.Suite(&marshalSuite{}) type marshalSuite struct { testbase.LoggingSuite } var expectedIndex = `{ "index": { "com.ubuntu.cloud:custom": { "updated": "Thu, 01 Jan 1970 00:00:00 +0000", "format": "products:1.0", "datatype": "image-ids", "cloudname": "custom", "clouds": [ { "region": "region", "endpoint": "endpoint" } ], "path": "streams/v1/com.ubuntu.cloud:released:imagemetadata.json", "products": [ "com.ubuntu.cloud:server:12.04:amd64", "com.ubuntu.cloud:server:12.04:arm", "com.ubuntu.cloud:server:13.10:arm" ] } }, "updated": "Thu, 01 Jan 1970 00:00:00 +0000", "format": "index:1.0" }` var expectedProducts = `{ "products": { "com.ubuntu.cloud:server:12.04:amd64": { "version": "12.04", "arch": "amd64", "versions": { "19700101": { "items": { "abcd": { "id": "abcd", "virt": "virt" } } } } }, "com.ubuntu.cloud:server:12.04:arm": { "version": "12.04", "arch": "arm", "versions": { "19700101": { "items": { "5678": { "id": "5678" } } } } }, "com.ubuntu.cloud:server:13.10:arm": { "version": "13.10", "arch": "arm", "versions": { "19700101": { "items": { "1234": { "id": "1234" } } } } } }, "updated": "Thu, 01 Jan 1970 00:00:00 +0000", "format": "products:1.0", "content_id": "com.ubuntu.cloud:custom" }` var imageMetadataForTesting = []*imagemetadata.ImageMetadata{ &imagemetadata.ImageMetadata{ Id: "1234", Version: "13.10", Arch: "arm", }, &imagemetadata.ImageMetadata{ Id: "5678", Version: "12.04", Arch: "arm", }, &imagemetadata.ImageMetadata{ Id: "abcd", Version: "12.04", Arch: "amd64", VirtType: "virt", }, } func (s *marshalSuite) TestMarshalIndex(c *gc.C) { cloudSpec := []simplestreams.CloudSpec{{Region: "region", Endpoint: "endpoint"}} index, err := imagemetadata.MarshalImageMetadataIndexJSON(imageMetadataForTesting, cloudSpec, time.Unix(0, 0).UTC()) c.Assert(err, gc.IsNil) c.Assert(string(index), gc.Equals, expectedIndex) } func (s *marshalSuite) TestMarshalProducts(c *gc.C) { products, err := imagemetadata.MarshalImageMetadataProductsJSON(imageMetadataForTesting, time.Unix(0, 0).UTC()) c.Assert(err, gc.IsNil) c.Assert(string(products), gc.Equals, expectedProducts) } func (s *marshalSuite) TestMarshal(c *gc.C) { cloudSpec := []simplestreams.CloudSpec{{Region: "region", Endpoint: "endpoint"}} index, products, err := imagemetadata.MarshalImageMetadataJSON(imageMetadataForTesting, cloudSpec, time.Unix(0, 0).UTC()) c.Assert(err, gc.IsNil) c.Assert(string(index), gc.Equals, expectedIndex) c.Assert(string(products), gc.Equals, expectedProducts) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/upload_test.go������������������0000644�0000153�0000161�00000005700�12321735642�030167� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata_test import ( "io/ioutil" "os" "path/filepath" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/imagemetadata/testing" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/testing/testbase" ) var _ = gc.Suite(&uploadSuite{}) type uploadSuite struct { testbase.LoggingSuite } func createImageMetadata(c *gc.C) (sourceDir string, destDir string, destStor storage.Storage, metadata *imagemetadata.ImageMetadata) { destDir = c.MkDir() var err error destStor, err = filestorage.NewFileStorageWriter(destDir) c.Assert(err, gc.IsNil) // Generate some metadata. sourceDir = c.MkDir() im := []*imagemetadata.ImageMetadata{ { Id: "1234", Arch: "amd64", Version: "13.04", }, } cloudSpec := &simplestreams.CloudSpec{ Region: "region", Endpoint: "endpoint", } im[0].RegionName = cloudSpec.Region im[0].Endpoint = cloudSpec.Endpoint var sourceStor storage.Storage sourceStor, err = filestorage.NewFileStorageWriter(sourceDir) c.Assert(err, gc.IsNil) err = imagemetadata.MergeAndWriteMetadata("raring", im, cloudSpec, sourceStor) c.Assert(err, gc.IsNil) return sourceDir, destDir, destStor, im[0] } func (s *uploadSuite) TestUpload(c *gc.C) { // Create some metadata. sourceDir, destDir, destStor, im := createImageMetadata(c) // Ensure it can be uploaded. err := imagemetadata.UploadImageMetadata(destStor, sourceDir) c.Assert(err, gc.IsNil) metadata := testing.ParseMetadataFromDir(c, destDir) c.Assert(metadata, gc.HasLen, 1) c.Assert(im, gc.DeepEquals, metadata[0]) } func (s *uploadSuite) TestUploadErrors(c *gc.C) { destDir := c.MkDir() destStor, err := filestorage.NewFileStorageWriter(destDir) c.Assert(err, gc.IsNil) err = imagemetadata.UploadImageMetadata(destStor, "foo") c.Assert(err, jc.Satisfies, os.IsNotExist) } func (s *uploadSuite) TestUploadIgnoresNonJsonFiles(c *gc.C) { // Create some metadata. sourceDir, destDir, destStor, _ := createImageMetadata(c) // Add an extra file. sourceMetadataPath := filepath.Join(sourceDir, storage.BaseImagesPath, simplestreams.StreamsDir) err := ioutil.WriteFile(filepath.Join(sourceMetadataPath, "foo.txt"), []byte("hello"), 0644) c.Assert(err, gc.IsNil) // Upload the metadata. err = imagemetadata.UploadImageMetadata(destStor, sourceDir) c.Assert(err, gc.IsNil) // Check only json files are uploaded. destMetadataPath := filepath.Join(destDir, storage.BaseImagesPath, simplestreams.StreamsDir) files, err := ioutil.ReadDir(destMetadataPath) c.Assert(err, gc.IsNil) c.Assert(files, gc.HasLen, 2) for _, f := range files { fileName := f.Name() c.Assert(strings.HasSuffix(fileName, simplestreams.UnsignedSuffix), jc.IsTrue) } } ����������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/generate_test.go����������������0000644�0000153�0000161�00000013655�12321735642�030505� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/imagemetadata/testing" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/testing/testbase" ) var _ = gc.Suite(&generateSuite{}) type generateSuite struct { testbase.LoggingSuite } func assertFetch(c *gc.C, stor storage.Storage, series, arch, region, endpoint, id string) { cons := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{region, endpoint}, Series: []string{series}, Arches: []string{arch}, }) dataSource := storage.NewStorageSimpleStreamsDataSource("test datasource", stor, "images") metadata, _, err := imagemetadata.Fetch( []simplestreams.DataSource{dataSource}, simplestreams.DefaultIndexPath, cons, false) c.Assert(err, gc.IsNil) c.Assert(metadata, gc.HasLen, 1) c.Assert(metadata[0].Id, gc.Equals, id) } func (s *generateSuite) TestWriteMetadata(c *gc.C) { im := []*imagemetadata.ImageMetadata{ { Id: "1234", Arch: "amd64", Version: "13.04", }, } cloudSpec := &simplestreams.CloudSpec{ Region: "region", Endpoint: "endpoint", } dir := c.MkDir() targetStorage, err := filestorage.NewFileStorageWriter(dir) c.Assert(err, gc.IsNil) err = imagemetadata.MergeAndWriteMetadata("raring", im, cloudSpec, targetStorage) c.Assert(err, gc.IsNil) metadata := testing.ParseMetadataFromDir(c, dir) c.Assert(metadata, gc.HasLen, 1) im[0].RegionName = cloudSpec.Region im[0].Endpoint = cloudSpec.Endpoint c.Assert(im[0], gc.DeepEquals, metadata[0]) assertFetch(c, targetStorage, "raring", "amd64", "region", "endpoint", "1234") } func (s *generateSuite) TestWriteMetadataMergeOverwriteSameArch(c *gc.C) { existingImageMetadata := []*imagemetadata.ImageMetadata{ { Id: "1234", Arch: "amd64", Version: "13.04", }, } cloudSpec := &simplestreams.CloudSpec{ Region: "region", Endpoint: "endpoint", } dir := c.MkDir() targetStorage, err := filestorage.NewFileStorageWriter(dir) c.Assert(err, gc.IsNil) err = imagemetadata.MergeAndWriteMetadata("raring", existingImageMetadata, cloudSpec, targetStorage) c.Assert(err, gc.IsNil) newImageMetadata := []*imagemetadata.ImageMetadata{ { Id: "abcd", Arch: "amd64", Version: "13.04", }, { Id: "xyz", Arch: "arm", Version: "13.04", }, } err = imagemetadata.MergeAndWriteMetadata("raring", newImageMetadata, cloudSpec, targetStorage) c.Assert(err, gc.IsNil) metadata := testing.ParseMetadataFromDir(c, dir) c.Assert(metadata, gc.HasLen, 2) for _, im := range newImageMetadata { im.RegionName = cloudSpec.Region im.Endpoint = cloudSpec.Endpoint } c.Assert(metadata, gc.DeepEquals, newImageMetadata) assertFetch(c, targetStorage, "raring", "amd64", "region", "endpoint", "abcd") assertFetch(c, targetStorage, "raring", "arm", "region", "endpoint", "xyz") } func (s *generateSuite) TestWriteMetadataMergeDifferentSeries(c *gc.C) { existingImageMetadata := []*imagemetadata.ImageMetadata{ { Id: "1234", Arch: "amd64", Version: "13.04", }, } cloudSpec := &simplestreams.CloudSpec{ Region: "region", Endpoint: "endpoint", } dir := c.MkDir() targetStorage, err := filestorage.NewFileStorageWriter(dir) c.Assert(err, gc.IsNil) err = imagemetadata.MergeAndWriteMetadata("raring", existingImageMetadata, cloudSpec, targetStorage) c.Assert(err, gc.IsNil) newImageMetadata := []*imagemetadata.ImageMetadata{ { Id: "abcd", Arch: "amd64", Version: "12.04", }, { Id: "xyz", Arch: "arm", Version: "12.04", }, } err = imagemetadata.MergeAndWriteMetadata("precise", newImageMetadata, cloudSpec, targetStorage) c.Assert(err, gc.IsNil) metadata := testing.ParseMetadataFromDir(c, dir) c.Assert(metadata, gc.HasLen, 3) newImageMetadata = append(newImageMetadata, existingImageMetadata[0]) for _, im := range newImageMetadata { im.RegionName = cloudSpec.Region im.Endpoint = cloudSpec.Endpoint } imagemetadata.Sort(newImageMetadata) c.Assert(metadata, gc.DeepEquals, newImageMetadata) assertFetch(c, targetStorage, "raring", "amd64", "region", "endpoint", "1234") assertFetch(c, targetStorage, "precise", "amd64", "region", "endpoint", "abcd") } func (s *generateSuite) TestWriteMetadataMergeDifferentRegion(c *gc.C) { existingImageMetadata := []*imagemetadata.ImageMetadata{ { Id: "1234", Arch: "amd64", Version: "13.04", }, } cloudSpec := &simplestreams.CloudSpec{ Region: "region", Endpoint: "endpoint", } dir := c.MkDir() targetStorage, err := filestorage.NewFileStorageWriter(dir) c.Assert(err, gc.IsNil) err = imagemetadata.MergeAndWriteMetadata("raring", existingImageMetadata, cloudSpec, targetStorage) c.Assert(err, gc.IsNil) newImageMetadata := []*imagemetadata.ImageMetadata{ { Id: "abcd", Arch: "amd64", Version: "13.04", }, { Id: "xyz", Arch: "arm", Version: "13.04", }, } cloudSpec = &simplestreams.CloudSpec{ Region: "region2", Endpoint: "endpoint2", } err = imagemetadata.MergeAndWriteMetadata("raring", newImageMetadata, cloudSpec, targetStorage) c.Assert(err, gc.IsNil) metadata := testing.ParseMetadataFromDir(c, dir) c.Assert(metadata, gc.HasLen, 3) for _, im := range newImageMetadata { im.RegionName = "region2" im.Endpoint = "endpoint2" } existingImageMetadata[0].RegionName = "region" existingImageMetadata[0].Endpoint = "endpoint" newImageMetadata = append(newImageMetadata, existingImageMetadata[0]) imagemetadata.Sort(newImageMetadata) c.Assert(metadata, gc.DeepEquals, newImageMetadata) assertFetch(c, targetStorage, "raring", "amd64", "region", "endpoint", "1234") assertFetch(c, targetStorage, "raring", "amd64", "region2", "endpoint2", "abcd") } �����������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/urls_test.go��������������������0000644�0000153�0000161�00000007274�12321735642�027700� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata_test import ( "strings" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/imagemetadata" sstesting "launchpad.net/juju-core/environs/simplestreams/testing" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/testing" ) type URLsSuite struct { home *testing.FakeHome } var _ = gc.Suite(&URLsSuite{}) func (s *URLsSuite) SetUpTest(c *gc.C) { s.home = testing.MakeEmptyFakeHome(c) } func (s *URLsSuite) TearDownTest(c *gc.C) { dummy.Reset() s.home.Restore() } func (s *URLsSuite) env(c *gc.C, imageMetadataURL, stream string) environs.Environ { attrs := dummy.SampleConfig() if stream != "" { attrs = attrs.Merge(testing.Attrs{ "image-stream": stream, }) } if imageMetadataURL != "" { attrs = attrs.Merge(testing.Attrs{ "image-metadata-url": imageMetadataURL, }) } cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := environs.Prepare(cfg, testing.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) // Put a file in images since the dummy storage provider requires a // file to exist before the URL can be found. This is to ensure it behaves // the same way as MAAS. err = env.Storage().Put("images/dummy", strings.NewReader("dummy"), 5) c.Assert(err, gc.IsNil) return env } func (s *URLsSuite) TestImageMetadataURLsNoConfigURL(c *gc.C) { env := s.env(c, "", "") sources, err := imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) privateStorageURL, err := env.Storage().URL("images") c.Assert(err, gc.IsNil) sstesting.AssertExpectedSources(c, sources, []string{ privateStorageURL, "http://cloud-images.ubuntu.com/releases/"}) } func (s *URLsSuite) TestImageMetadataURLs(c *gc.C) { env := s.env(c, "config-image-metadata-url", "") sources, err := imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) privateStorageURL, err := env.Storage().URL("images") c.Assert(err, gc.IsNil) sstesting.AssertExpectedSources(c, sources, []string{ "config-image-metadata-url/", privateStorageURL, "http://cloud-images.ubuntu.com/releases/"}) } func (s *URLsSuite) TestImageMetadataURLsNonReleaseStream(c *gc.C) { env := s.env(c, "", "daily") sources, err := imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) privateStorageURL, err := env.Storage().URL("images") c.Assert(err, gc.IsNil) sstesting.AssertExpectedSources(c, sources, []string{ privateStorageURL, "http://cloud-images.ubuntu.com/daily/"}) } func (s *URLsSuite) TestImageMetadataURL(c *gc.C) { for source, expected := range map[string]string{ "": "", "foo": "file://foo/images", "/home/foo": "file:///home/foo/images", "file://foo": "file://foo", "http://foo": "http://foo", } { URL, err := imagemetadata.ImageMetadataURL(source, "") c.Assert(err, gc.IsNil) c.Assert(URL, gc.Equals, expected) } } func (s *URLsSuite) TestImageMetadataURLOfficialSource(c *gc.C) { // Released streams. URL, err := imagemetadata.ImageMetadataURL(imagemetadata.UbuntuCloudImagesURL, "") c.Assert(err, gc.IsNil) c.Assert(URL, gc.Equals, "http://cloud-images.ubuntu.com/releases") URL, err = imagemetadata.ImageMetadataURL(imagemetadata.UbuntuCloudImagesURL, imagemetadata.ReleasedStream) c.Assert(err, gc.IsNil) c.Assert(URL, gc.Equals, "http://cloud-images.ubuntu.com/releases") // Non-released streams. URL, err = imagemetadata.ImageMetadataURL(imagemetadata.UbuntuCloudImagesURL, "daily") c.Assert(err, gc.IsNil) c.Assert(URL, gc.Equals, "http://cloud-images.ubuntu.com/daily") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/validation_test.go��������������0000644�0000153�0000161�00000005556�12321735642�031046� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata_test import ( "path" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type ValidateSuite struct { testbase.LoggingSuite metadataDir string } var _ = gc.Suite(&ValidateSuite{}) func (s *ValidateSuite) makeLocalMetadata(c *gc.C, id, region, series, endpoint, stream string) error { metadata := []*imagemetadata.ImageMetadata{ { Id: id, Arch: "amd64", Stream: stream, }, } cloudSpec := simplestreams.CloudSpec{ Region: region, Endpoint: endpoint, } targetStorage, err := filestorage.NewFileStorageWriter(s.metadataDir) c.Assert(err, gc.IsNil) err = imagemetadata.MergeAndWriteMetadata(series, metadata, &cloudSpec, targetStorage) if err != nil { return err } return nil } func (s *ValidateSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.metadataDir = c.MkDir() } func (s *ValidateSuite) assertMatch(c *gc.C, stream string) { s.makeLocalMetadata(c, "1234", "region-2", "raring", "some-auth-url", stream) metadataPath := filepath.Join(s.metadataDir, "images") params := &simplestreams.MetadataLookupParams{ Region: "region-2", Series: "raring", Architectures: []string{"amd64"}, Endpoint: "some-auth-url", Stream: stream, Sources: []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", "file://"+metadataPath, utils.VerifySSLHostnames)}, } imageIds, resolveInfo, err := imagemetadata.ValidateImageMetadata(params) c.Assert(err, gc.IsNil) c.Assert(imageIds, gc.DeepEquals, []string{"1234"}) c.Check(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ Source: "test", Signed: false, IndexURL: "file://" + path.Join(metadataPath, "streams/v1/index.json"), MirrorURL: "", }) } func (s *ValidateSuite) TestMatch(c *gc.C) { s.assertMatch(c, "") s.assertMatch(c, imagemetadata.ReleasedStream) s.assertMatch(c, "daily") } func (s *ValidateSuite) assertNoMatch(c *gc.C, stream string) { s.makeLocalMetadata(c, "1234", "region-2", "raring", "some-auth-url", stream) params := &simplestreams.MetadataLookupParams{ Region: "region-2", Series: "precise", Architectures: []string{"amd64"}, Endpoint: "some-auth-url", Stream: stream, Sources: []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", "file://"+s.metadataDir, utils.VerifySSLHostnames)}, } _, _, err := imagemetadata.ValidateImageMetadata(params) c.Assert(err, gc.Not(gc.IsNil)) } func (s *ValidateSuite) TestNoMatch(c *gc.C) { s.assertNoMatch(c, "") s.assertNoMatch(c, imagemetadata.ReleasedStream) s.assertNoMatch(c, "daily") } ��������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/marshal.go����������������������0000644�0000153�0000161�00000005770�12321735642�027302� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata import ( "encoding/json" "time" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/utils/set" ) const ( ProductMetadataPath = "streams/v1/com.ubuntu.cloud:released:imagemetadata.json" ImageContentId = "com.ubuntu.cloud:custom" ) // MarshalImageMetadataJSON marshals image metadata to index and products JSON. // // updated is the time at which the JSON file was updated. func MarshalImageMetadataJSON(metadata []*ImageMetadata, cloudSpec []simplestreams.CloudSpec, updated time.Time) (index, products []byte, err error) { if index, err = MarshalImageMetadataIndexJSON(metadata, cloudSpec, updated); err != nil { return nil, nil, err } if products, err = MarshalImageMetadataProductsJSON(metadata, updated); err != nil { return nil, nil, err } return index, products, err } // MarshalImageMetadataIndexJSON marshals image metadata to index JSON. // // updated is the time at which the JSON file was updated. func MarshalImageMetadataIndexJSON(metadata []*ImageMetadata, cloudSpec []simplestreams.CloudSpec, updated time.Time) (out []byte, err error) { productIds := make([]string, len(metadata)) for i, t := range metadata { productIds[i] = t.productId() } var indices simplestreams.Indices indices.Updated = updated.Format(time.RFC1123Z) indices.Format = "index:1.0" indices.Indexes = map[string]*simplestreams.IndexMetadata{ ImageContentId: &simplestreams.IndexMetadata{ CloudName: "custom", Updated: indices.Updated, Format: "products:1.0", DataType: "image-ids", ProductsFilePath: ProductMetadataPath, ProductIds: set.NewStrings(productIds...).SortedValues(), Clouds: cloudSpec, }, } return json.MarshalIndent(&indices, "", " ") } // MarshalImageMetadataProductsJSON marshals image metadata to products JSON. // // updated is the time at which the JSON file was updated. func MarshalImageMetadataProductsJSON(metadata []*ImageMetadata, updated time.Time) (out []byte, err error) { var cloud simplestreams.CloudMetadata cloud.Updated = updated.Format(time.RFC1123Z) cloud.Format = "products:1.0" cloud.ContentId = ImageContentId cloud.Products = make(map[string]simplestreams.MetadataCatalog) itemsversion := updated.Format("20060201") // YYYYMMDD for _, t := range metadata { toWrite := &ImageMetadata{ Id: t.Id, RegionName: t.RegionName, Endpoint: t.Endpoint, VirtType: t.VirtType, } if catalog, ok := cloud.Products[t.productId()]; ok { catalog.Items[itemsversion].Items[t.Id] = toWrite } else { catalog = simplestreams.MetadataCatalog{ Arch: t.Arch, Version: t.Version, Items: map[string]*simplestreams.ItemCollection{ itemsversion: &simplestreams.ItemCollection{ Items: map[string]interface{}{t.Id: toWrite}, }, }, } cloud.Products[t.productId()] = catalog } } return json.MarshalIndent(&cloud, "", " ") } ��������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/upload.go�����������������������0000644�0000153�0000161�00000003136�12321735642�027131� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package imagemetadata import ( "fmt" "io/ioutil" "os" "path" "path/filepath" "strings" "github.com/juju/loggo" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" ) var logger = loggo.GetLogger("juju.environs.imagemetadata") // UploadImageMetadata uploads image metadata files from sourceDir to stor. func UploadImageMetadata(stor storage.Storage, sourceDir string) error { if sourceDir == "" { return nil } metadataDir := path.Join(sourceDir, storage.BaseImagesPath, simplestreams.StreamsDir) info, err := os.Stat(metadataDir) if err != nil { return err } if !info.IsDir() { return fmt.Errorf("%s is not a directory", sourceDir) } logger.Debugf("reading image metadata from %s", metadataDir) files, err := ioutil.ReadDir(metadataDir) if err != nil { return err } for _, f := range files { fileName := f.Name() if !strings.HasSuffix(fileName, simplestreams.UnsignedSuffix) { continue } if err := uploadMetadataFile(stor, metadataDir, fileName, f.Size()); err != nil { return err } } return nil } func uploadMetadataFile(stor storage.Storage, metadataDir, fileName string, size int64) error { fullSourceFilename := filepath.Join(metadataDir, fileName) logger.Debugf("uploading metadata file %s", fileName) f, err := os.Open(fullSourceFilename) if err != nil { return err } defer f.Close() destMetadataDir := path.Join(storage.BaseImagesPath, simplestreams.StreamsDir) return stor.Put(path.Join(destMetadataDir, fileName), f, size) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/testing/������������������������0000755�0000153�0000161�00000000000�12321735643�026771� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/imagemetadata/testing/testing.go��������������0000644�0000153�0000161�00000005637�12321735642�031007� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "fmt" "io/ioutil" "path" "sort" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/utils/set" ) // ParseMetadataFromDir loads ImageMetadata from the specified directory. func ParseMetadataFromDir(c *gc.C, metadataDir string) []*imagemetadata.ImageMetadata { stor, err := filestorage.NewFileStorageReader(metadataDir) c.Assert(err, gc.IsNil) return ParseMetadataFromStorage(c, stor) } // ParseMetadataFromStorage loads ImageMetadata from the specified storage reader. func ParseMetadataFromStorage(c *gc.C, stor storage.StorageReader) []*imagemetadata.ImageMetadata { source := storage.NewStorageSimpleStreamsDataSource("test storage reader", stor, "images") // Find the simplestreams index file. params := simplestreams.ValueParams{ DataType: "image-ids", ValueTemplate: imagemetadata.ImageMetadata{}, } const requireSigned = false indexPath := simplestreams.UnsignedIndex indexRef, err := simplestreams.GetIndexWithFormat( source, indexPath, "index:1.0", requireSigned, simplestreams.CloudSpec{}, params) c.Assert(err, gc.IsNil) c.Assert(indexRef.Indexes, gc.HasLen, 1) imageIndexMetadata := indexRef.Indexes["com.ubuntu.cloud:custom"] c.Assert(imageIndexMetadata, gc.NotNil) // Read the products file contents. r, err := stor.Get(path.Join("images", imageIndexMetadata.ProductsFilePath)) defer r.Close() c.Assert(err, gc.IsNil) data, err := ioutil.ReadAll(r) c.Assert(err, gc.IsNil) // Parse the products file metadata. url, err := source.URL(imageIndexMetadata.ProductsFilePath) c.Assert(err, gc.IsNil) cloudMetadata, err := simplestreams.ParseCloudMetadata(data, "products:1.0", url, imagemetadata.ImageMetadata{}) c.Assert(err, gc.IsNil) // Collate the metadata. imageMetadataMap := make(map[string]*imagemetadata.ImageMetadata) var expectedProductIds set.Strings var imageVersions set.Strings for _, mc := range cloudMetadata.Products { for _, items := range mc.Items { for key, item := range items.Items { imageMetadata := item.(*imagemetadata.ImageMetadata) imageMetadataMap[key] = imageMetadata imageVersions.Add(key) productId := fmt.Sprintf("com.ubuntu.cloud:server:%s:%s", mc.Version, imageMetadata.Arch) expectedProductIds.Add(productId) } } } // Make sure index's product IDs are all represented in the products metadata. sort.Strings(imageIndexMetadata.ProductIds) c.Assert(imageIndexMetadata.ProductIds, gc.DeepEquals, expectedProductIds.SortedValues()) imageMetadata := make([]*imagemetadata.ImageMetadata, len(imageMetadataMap)) for i, key := range imageVersions.SortedValues() { imageMetadata[i] = imageMetadataMap[key] } return imageMetadata } �������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/storage/��������������������������������������0000755�0000153�0000161�00000000000�12321735643�024175� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/storage/interfaces.go�������������������������0000644�0000153�0000161�00000004733�12321735642�026655� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package storage import ( "io" "launchpad.net/juju-core/utils" ) // A StorageReader can retrieve and list files from a storage provider. type StorageReader interface { // Get opens the given storage file and returns a ReadCloser // that can be used to read its contents. It is the caller's // responsibility to close it after use. If the name does not // exist, it should return a *NotFoundError. Get(name string) (io.ReadCloser, error) // List lists all names in the storage with the given prefix, in // alphabetical order. The names in the storage are considered // to be in a flat namespace, so the prefix may include slashes // and the names returned are the full names for the matching // entries. List(prefix string) ([]string, error) // URL returns a URL that can be used to access the given storage file. URL(name string) (string, error) // DefaultConsistencyStrategy returns the appropriate polling for waiting // for this storage to become consistent. // If the storage implementation has immediate consistency, the // strategy won't need to wait at all. But for eventually-consistent // storage backends a few seconds of polling may be needed. DefaultConsistencyStrategy() utils.AttemptStrategy // ShouldRetry returns true is the specified error is such that an // operation can be performed again with a chance of success. This is // typically the case where the storage implementation does not have // immediate consistency and needs to be given a chance to "catch up". ShouldRetry(error) bool } // A StorageWriter adds and removes files in a storage provider. type StorageWriter interface { // Put reads from r and writes to the given storage file. // The length must give the total length of the file. Put(name string, r io.Reader, length int64) error // Remove removes the given file from the environment's // storage. It should not return an error if the file does // not exist. Remove(name string) error // RemoveAll deletes all files that have been stored here. // If the underlying storage implementation may be shared // with other actors, it must be sure not to delete their // file as well. // Nevertheless, use with care! This method is only mean // for cleaning up an environment that's being destroyed. RemoveAll() error } // Storage represents storage that can be both // read and written. type Storage interface { StorageReader StorageWriter } �������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/storage/storage.go����������������������������0000644�0000153�0000161�00000010173�12321735642�026171� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package storage import ( "fmt" "io" "path" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/utils" ) // RemoveAll is a default implementation for StorageWriter.RemoveAll. // Providers may have more efficient implementations, or better error handling, // or safeguards against races with other users of the same storage medium. // But a simple way to implement RemoveAll would be to delegate to here. func RemoveAll(stor Storage) error { files, err := List(stor, "") if err != nil { return fmt.Errorf("unable to list files for deletion: %v", err) } // Some limited parallellism might be useful in this loop. for _, file := range files { err = stor.Remove(file) if err != nil { break } } return err } // Get gets the named file from stor using the stor's default consistency strategy. func Get(stor StorageReader, name string) (io.ReadCloser, error) { return GetWithRetry(stor, name, stor.DefaultConsistencyStrategy()) } // GetWithRetry gets the named file from stor using the specified attempt strategy. func GetWithRetry(stor StorageReader, name string, attempt utils.AttemptStrategy) (r io.ReadCloser, err error) { for a := attempt.Start(); a.Next(); { r, err = stor.Get(name) if err == nil || !stor.ShouldRetry(err) { break } } return r, err } // List lists the files matching prefix from stor using the stor's default consistency strategy. func List(stor StorageReader, prefix string) ([]string, error) { return ListWithRetry(stor, prefix, stor.DefaultConsistencyStrategy()) } // ListWithRetry lists the files matching prefix from stor using the specified attempt strategy. func ListWithRetry(stor StorageReader, prefix string, attempt utils.AttemptStrategy) (list []string, err error) { for a := attempt.Start(); a.Next(); { list, err = stor.List(prefix) if err == nil || !stor.ShouldRetry(err) { break } } return list, err } // BaseToolsPath is the container where tools tarballs and metadata are found. var BaseToolsPath = "tools" // BaseImagesPath is the container where images metadata is found. var BaseImagesPath = "images" // A storageSimpleStreamsDataSource retrieves data from a StorageReader. type storageSimpleStreamsDataSource struct { description string basePath string storage StorageReader allowRetry bool } // TestingGetAllowRetry is used in tests which need to see if allowRetry has been // set on a storageSimpleStreamsDataSource. func TestingGetAllowRetry(s simplestreams.DataSource) (bool, ok bool) { if storageDataSource, ok := s.(*storageSimpleStreamsDataSource); ok { return storageDataSource.allowRetry, ok } return false, false } // NewStorageSimpleStreamsDataSource returns a new datasource reading from the specified storage. func NewStorageSimpleStreamsDataSource(description string, storage StorageReader, basePath string) simplestreams.DataSource { return &storageSimpleStreamsDataSource{description, basePath, storage, false} } func (s *storageSimpleStreamsDataSource) relpath(storagePath string) string { relpath := storagePath if s.basePath != "" { relpath = path.Join(s.basePath, relpath) } return relpath } // Description is defined in simplestreams.DataSource. func (s *storageSimpleStreamsDataSource) Description() string { return s.description } // Fetch is defined in simplestreams.DataSource. func (s *storageSimpleStreamsDataSource) Fetch(path string) (io.ReadCloser, string, error) { relpath := s.relpath(path) dataURL := relpath fullURL, err := s.storage.URL(relpath) if err == nil { dataURL = fullURL } var attempt utils.AttemptStrategy if s.allowRetry { attempt = s.storage.DefaultConsistencyStrategy() } rc, err := GetWithRetry(s.storage, relpath, attempt) if err != nil { return nil, dataURL, err } return rc, dataURL, nil } // URL is defined in simplestreams.DataSource. func (s *storageSimpleStreamsDataSource) URL(path string) (string, error) { return s.storage.URL(s.relpath(path)) } // SetAllowRetry is defined in simplestreams.DataSource. func (s *storageSimpleStreamsDataSource) SetAllowRetry(allow bool) { s.allowRetry = allow } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/storage/storage_test.go�����������������������0000644�0000153�0000161�00000013342�12321735642�027231� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package storage_test import ( "bytes" "fmt" "io" "io/ioutil" stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) func TestPackage(t *stdtesting.T) { gc.TestingT(t) } var _ = gc.Suite(&datasourceSuite{}) type datasourceSuite struct { home *testing.FakeHome stor storage.Storage baseURL string } const existingEnv = ` environments: test: type: dummy state-server: false authorized-keys: i-am-a-key ` func (s *datasourceSuite) SetUpTest(c *gc.C) { s.home = testing.MakeFakeHome(c, existingEnv, "existing") environ, err := environs.PrepareFromName("test", testing.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) s.stor = environ.Storage() s.baseURL, err = s.stor.URL("") c.Assert(err, gc.IsNil) } func (s *datasourceSuite) TearDownTest(c *gc.C) { dummy.Reset() s.home.Restore() } func (s *datasourceSuite) TestFetch(c *gc.C) { sampleData := "hello world" s.stor.Put("foo/bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData))) ds := storage.NewStorageSimpleStreamsDataSource("test datasource", s.stor, "") rc, url, err := ds.Fetch("foo/bar/data.txt") c.Assert(err, gc.IsNil) defer rc.Close() c.Assert(url, gc.Equals, s.baseURL+"foo/bar/data.txt") data, err := ioutil.ReadAll(rc) c.Assert(data, gc.DeepEquals, []byte(sampleData)) } func (s *datasourceSuite) TestFetchWithBasePath(c *gc.C) { sampleData := "hello world" s.stor.Put("base/foo/bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData))) ds := storage.NewStorageSimpleStreamsDataSource("test datasource", s.stor, "base") rc, url, err := ds.Fetch("foo/bar/data.txt") c.Assert(err, gc.IsNil) defer rc.Close() c.Assert(url, gc.Equals, s.baseURL+"base/foo/bar/data.txt") data, err := ioutil.ReadAll(rc) c.Assert(data, gc.DeepEquals, []byte(sampleData)) } func (s *datasourceSuite) TestFetchWithRetry(c *gc.C) { stor := &fakeStorage{shouldRetry: true} ds := storage.NewStorageSimpleStreamsDataSource("test datasource", stor, "base") ds.SetAllowRetry(true) _, _, err := ds.Fetch("foo/bar/data.txt") c.Assert(err, gc.ErrorMatches, "an error") c.Assert(stor.getName, gc.Equals, "base/foo/bar/data.txt") c.Assert(stor.invokeCount, gc.Equals, 10) } func (s *datasourceSuite) TestFetchWithNoRetry(c *gc.C) { // NB shouldRetry below is true indicating the fake storage is capable of // retrying, not that it will retry. stor := &fakeStorage{shouldRetry: true} ds := storage.NewStorageSimpleStreamsDataSource("test datasource", stor, "base") _, _, err := ds.Fetch("foo/bar/data.txt") c.Assert(err, gc.ErrorMatches, "an error") c.Assert(stor.getName, gc.Equals, "base/foo/bar/data.txt") c.Assert(stor.invokeCount, gc.Equals, 1) } func (s *datasourceSuite) TestURL(c *gc.C) { sampleData := "hello world" s.stor.Put("bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData))) ds := storage.NewStorageSimpleStreamsDataSource("test datasource", s.stor, "") url, err := ds.URL("bar") c.Assert(err, gc.IsNil) expectedURL, _ := s.stor.URL("bar") c.Assert(url, gc.Equals, expectedURL) } func (s *datasourceSuite) TestURLWithBasePath(c *gc.C) { sampleData := "hello world" s.stor.Put("base/bar/data.txt", bytes.NewReader([]byte(sampleData)), int64(len(sampleData))) ds := storage.NewStorageSimpleStreamsDataSource("test datasource", s.stor, "base") url, err := ds.URL("bar") c.Assert(err, gc.IsNil) expectedURL, _ := s.stor.URL("base/bar") c.Assert(url, gc.Equals, expectedURL) } var _ = gc.Suite(&storageSuite{}) type storageSuite struct{} type fakeStorage struct { getName string listPrefix string invokeCount int shouldRetry bool } func (s *fakeStorage) Get(name string) (io.ReadCloser, error) { s.getName = name s.invokeCount++ return nil, fmt.Errorf("an error") } func (s *fakeStorage) List(prefix string) ([]string, error) { s.listPrefix = prefix s.invokeCount++ return nil, fmt.Errorf("an error") } func (s *fakeStorage) URL(name string) (string, error) { return "", nil } func (s *fakeStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { return utils.AttemptStrategy{Min: 10} } func (s *fakeStorage) ShouldRetry(error) bool { return s.shouldRetry } func (s *storageSuite) TestGetWithRetry(c *gc.C) { stor := &fakeStorage{shouldRetry: true} attempt := utils.AttemptStrategy{Min: 5} storage.GetWithRetry(stor, "foo", attempt) c.Assert(stor.getName, gc.Equals, "foo") c.Assert(stor.invokeCount, gc.Equals, 5) } func (s *storageSuite) TestGet(c *gc.C) { stor := &fakeStorage{shouldRetry: true} storage.Get(stor, "foo") c.Assert(stor.getName, gc.Equals, "foo") c.Assert(stor.invokeCount, gc.Equals, 10) } func (s *storageSuite) TestGetNoRetryAllowed(c *gc.C) { stor := &fakeStorage{} storage.Get(stor, "foo") c.Assert(stor.getName, gc.Equals, "foo") c.Assert(stor.invokeCount, gc.Equals, 1) } func (s *storageSuite) TestListWithRetry(c *gc.C) { stor := &fakeStorage{shouldRetry: true} attempt := utils.AttemptStrategy{Min: 5} storage.ListWithRetry(stor, "foo", attempt) c.Assert(stor.listPrefix, gc.Equals, "foo") c.Assert(stor.invokeCount, gc.Equals, 5) } func (s *storageSuite) TestList(c *gc.C) { stor := &fakeStorage{shouldRetry: true} storage.List(stor, "foo") c.Assert(stor.listPrefix, gc.Equals, "foo") c.Assert(stor.invokeCount, gc.Equals, 10) } func (s *storageSuite) TestListNoRetryAllowed(c *gc.C) { stor := &fakeStorage{} storage.List(stor, "foo") c.Assert(stor.listPrefix, gc.Equals, "foo") c.Assert(stor.invokeCount, gc.Equals, 1) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/���������������������������������������0000755�0000153�0000161�00000000000�12321736000�023772� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/export_test.go�������������������������0000644�0000153�0000161�00000000546�12321735776�026731� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual var ( InstanceHostAddresses = &instanceHostAddresses ProvisionMachineAgent = &provisionMachineAgent CheckProvisioned = checkProvisioned ) const ( DetectionScript = detectionScript CheckProvisionedScript = checkProvisionedScript ) ����������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/fakessh_test.go������������������������0000644�0000153�0000161�00000010457�12321735642�027026� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual_test import ( "fmt" "io/ioutil" "path/filepath" "strings" "github.com/juju/testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/manual" ) // sshscript should only print the result on the first execution, // to handle the case where it's called multiple times. On // subsequent executions, it should find the next 'ssh' in $PATH // and exec that. var sshscript = `#!/bin/bash --norc if [ ! -e "$0.run" ]; then touch "$0.run" if [ -e "$0.expected-input" ]; then diff "$0.expected-input" - exitcode=$? if [ $exitcode -ne 0 ]; then echo "ERROR: did not match expected input" >&2 exit $exitcode fi else head >/dev/null fi # stdout %s # stderr %s exit %d else export PATH=${PATH#*:} exec ssh $* fi` // installFakeSSH creates a fake "ssh" command in a new $PATH, // updates $PATH, and returns a function to reset $PATH to its // original value when called. // // input may be: // - nil (ignore input) // - a string (match input exactly) // output may be: // - nil (no output) // - a string (stdout) // - a slice of strings, of length two (stdout, stderr) func installFakeSSH(c *gc.C, input, output interface{}, rc int) testing.Restorer { fakebin := c.MkDir() ssh := filepath.Join(fakebin, "ssh") switch input := input.(type) { case nil: case string: sshexpectedinput := ssh + ".expected-input" err := ioutil.WriteFile(sshexpectedinput, []byte(input), 0644) c.Assert(err, gc.IsNil) default: c.Errorf("input has invalid type: %T", input) } var stdout, stderr string switch output := output.(type) { case nil: case string: stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output) case []string: c.Assert(output, gc.HasLen, 2) stdout = fmt.Sprintf("cat<<EOF\n%s\nEOF", output[0]) stderr = fmt.Sprintf("cat>&2<<EOF\n%s\nEOF", output[1]) } script := fmt.Sprintf(sshscript, stdout, stderr, rc) err := ioutil.WriteFile(ssh, []byte(script), 0777) c.Assert(err, gc.IsNil) return testing.PatchEnvPathPrepend(fakebin) } // installDetectionFakeSSH installs a fake SSH command, which will respond // to the series/hardware detection script with the specified // series/arch. func installDetectionFakeSSH(c *gc.C, series, arch string) testing.Restorer { if series == "" { series = "precise" } if arch == "" { arch = "amd64" } detectionoutput := strings.Join([]string{ series, arch, "MemTotal: 4096 kB", "processor: 0", }, "\n") return installFakeSSH(c, manual.DetectionScript, detectionoutput, 0) } // fakeSSH wraps the invocation of InstallFakeSSH based on the parameters. type fakeSSH struct { Series string Arch string // Provisioned should be set to true if the fakeSSH script // should respond to checkProvisioned with a non-empty result. Provisioned bool // exit code for the checkProvisioned script. CheckProvisionedExitCode int // exit code for the machine agent provisioning script. ProvisionAgentExitCode int // InitUbuntuUser should be set to true if the fakeSSH script // should respond to an attempt to initialise the ubuntu user. InitUbuntuUser bool // there are conditions other than error in the above // that might cause provisioning to not go ahead, such // as tools being missing. SkipProvisionAgent bool // detection will be skipped if the series/hardware were // detected ahead of time. This should always be set to // true when testing Bootstrap. SkipDetection bool } // install installs fake SSH commands, which will respond to // manual provisioning/bootstrapping commands with the specified // output and exit codes. func (r fakeSSH) install(c *gc.C) testing.Restorer { var restore testing.Restorer add := func(input, output interface{}, rc int) { restore = restore.Add(installFakeSSH(c, input, output, rc)) } if !r.SkipProvisionAgent { add(nil, nil, r.ProvisionAgentExitCode) } if !r.SkipDetection { restore.Add(installDetectionFakeSSH(c, r.Series, r.Arch)) } var checkProvisionedOutput interface{} if r.Provisioned { checkProvisionedOutput = "/etc/init/jujud-machine-0.conf" } add(manual.CheckProvisionedScript, checkProvisionedOutput, r.CheckProvisionedExitCode) if r.InitUbuntuUser { add("", nil, 0) } return restore } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/bootstrap_test.go����������������������0000644�0000153�0000161�00000012465�12321735642�027420� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual_test import ( "fmt" "io" "os" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/manual" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/version" ) type bootstrapSuite struct { testing.JujuConnSuite env *localStorageEnviron } var _ = gc.Suite(&bootstrapSuite{}) type localStorageEnviron struct { environs.Environ storage storage.Storage storageAddr string storageDir string } func (e *localStorageEnviron) Storage() storage.Storage { return e.storage } func (e *localStorageEnviron) StorageAddr() string { return e.storageAddr } func (e *localStorageEnviron) StorageDir() string { return e.storageDir } func (s *bootstrapSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.env = &localStorageEnviron{ Environ: s.Conn.Environ, storageDir: c.MkDir(), } storage, err := filestorage.NewFileStorageWriter(s.env.storageDir) c.Assert(err, gc.IsNil) s.env.storage = storage } func (s *bootstrapSuite) getArgs(c *gc.C) manual.BootstrapArgs { hostname, err := os.Hostname() c.Assert(err, gc.IsNil) toolsList, err := tools.FindBootstrapTools(s.Conn.Environ, tools.BootstrapToolsParams{}) c.Assert(err, gc.IsNil) arch := "amd64" return manual.BootstrapArgs{ Host: hostname, DataDir: "/var/lib/juju", Environ: s.env, PossibleTools: toolsList, Series: "precise", HardwareCharacteristics: &instance.HardwareCharacteristics{ Arch: &arch, }, Context: coretesting.Context(c), } } func (s *bootstrapSuite) TestBootstrap(c *gc.C) { args := s.getArgs(c) args.Host = "ubuntu@" + args.Host defer fakeSSH{SkipDetection: true}.install(c).Restore() err := manual.Bootstrap(args) c.Assert(err, gc.IsNil) bootstrapState, err := bootstrap.LoadState(s.env.Storage()) c.Assert(err, gc.IsNil) c.Assert( bootstrapState.StateInstances, gc.DeepEquals, []instance.Id{manual.BootstrapInstanceId}, ) // Do it all again; this should work, despite the fact that // there's a bootstrap state file. Existence for that is // checked in general bootstrap code (environs/bootstrap). defer fakeSSH{SkipDetection: true}.install(c).Restore() err = manual.Bootstrap(args) c.Assert(err, gc.IsNil) // We *do* check that the machine has no juju* upstart jobs, though. defer fakeSSH{ Provisioned: true, SkipDetection: true, SkipProvisionAgent: true, }.install(c).Restore() err = manual.Bootstrap(args) c.Assert(err, gc.Equals, manual.ErrProvisioned) } func (s *bootstrapSuite) TestBootstrapScriptFailure(c *gc.C) { args := s.getArgs(c) args.Host = "ubuntu@" + args.Host defer fakeSSH{SkipDetection: true, ProvisionAgentExitCode: 1}.install(c).Restore() err := manual.Bootstrap(args) c.Assert(err, gc.NotNil) // Since the script failed, the state file should have been // removed from storage. _, err = bootstrap.LoadState(s.env.Storage()) c.Check(err, gc.Equals, environs.ErrNotBootstrapped) } func (s *bootstrapSuite) TestBootstrapEmptyDataDir(c *gc.C) { args := s.getArgs(c) args.DataDir = "" c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "data-dir argument is empty") } func (s *bootstrapSuite) TestBootstrapEmptyHost(c *gc.C) { args := s.getArgs(c) args.Host = "" c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "host argument is empty") } func (s *bootstrapSuite) TestBootstrapNilEnviron(c *gc.C) { args := s.getArgs(c) args.Environ = nil c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "environ argument is nil") } func (s *bootstrapSuite) TestBootstrapNoMatchingTools(c *gc.C) { // Empty tools list. args := s.getArgs(c) args.PossibleTools = nil defer fakeSSH{SkipDetection: true, SkipProvisionAgent: true}.install(c).Restore() c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "possible tools is empty") // Non-empty list, but none that match the series/arch. args = s.getArgs(c) args.Series = "edgy" defer fakeSSH{SkipDetection: true, SkipProvisionAgent: true}.install(c).Restore() c.Assert(manual.Bootstrap(args), gc.ErrorMatches, "no matching tools available") } func (s *bootstrapSuite) TestBootstrapToolsFileURL(c *gc.C) { storageName := tools.StorageName(version.Current) sftpURL, err := s.env.Storage().URL(storageName) c.Assert(err, gc.IsNil) fileURL := fmt.Sprintf("file://%s/%s", s.env.storageDir, storageName) s.testBootstrapToolsURL(c, sftpURL, fileURL) } func (s *bootstrapSuite) TestBootstrapToolsExternalURL(c *gc.C) { const externalURL = "http://test.invalid/tools.tgz" s.testBootstrapToolsURL(c, externalURL, externalURL) } func (s *bootstrapSuite) testBootstrapToolsURL(c *gc.C, toolsURL, expectedURL string) { s.PatchValue(manual.ProvisionMachineAgent, func(host string, mcfg *cloudinit.MachineConfig, w io.Writer) error { c.Assert(mcfg.Tools.URL, gc.Equals, expectedURL) return nil }) args := s.getArgs(c) args.PossibleTools[0].URL = toolsURL defer fakeSSH{SkipDetection: true}.install(c).Restore() err := manual.Bootstrap(args) c.Assert(err, gc.IsNil) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/addresses_test.go����������������������0000644�0000153�0000161�00000002124�12321735776�027357� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/manual" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/testing/testbase" ) type addressesSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&addressesSuite{}) func (s *addressesSuite) TestHostAddresses(c *gc.C) { const hostname = "boxen0" s.PatchValue(manual.InstanceHostAddresses, func(host string) ([]instance.Address, error) { c.Check(host, gc.Equals, hostname) return []instance.Address{ instance.NewAddress("192.168.0.1"), instance.NewAddress("nickname"), instance.NewAddress(hostname), }, nil }) addrs, err := manual.HostAddresses(hostname) 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.NetworkCloudLocal) c.Assert(addrs[1].NetworkScope, gc.Equals, instance.NetworkUnknown) c.Assert(addrs[2].NetworkScope, gc.Equals, instance.NetworkPublic) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/bootstrap.go���������������������������0000644�0000153�0000161�00000010132�12321735776�026356� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual import ( "errors" "fmt" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/worker/localstorage" ) const BootstrapInstanceId = instance.Id(manualInstancePrefix) // LocalStorageEnviron is an Environ where the bootstrap node // manages its own local storage. type LocalStorageEnviron interface { environs.Environ localstorage.LocalStorageConfig } type BootstrapArgs struct { Host string DataDir string Environ LocalStorageEnviron PossibleTools tools.List Context environs.BootstrapContext Series string HardwareCharacteristics *instance.HardwareCharacteristics } func errMachineIdInvalid(machineId string) error { return fmt.Errorf("%q is not a valid machine ID", machineId) } // NewManualBootstrapEnviron wraps a LocalStorageEnviron with another which // overrides the Bootstrap method; when Bootstrap is invoked, the specified // host will be manually bootstrapped. // // InitUbuntuUser is expected to have been executed successfully against // the host being bootstrapped. func Bootstrap(args BootstrapArgs) (err error) { if args.Host == "" { return errors.New("host argument is empty") } if args.Environ == nil { return errors.New("environ argument is nil") } if args.DataDir == "" { return errors.New("data-dir argument is empty") } if args.Series == "" { return errors.New("series argument is empty") } if args.HardwareCharacteristics == nil { return errors.New("hardware characteristics argument is empty") } if len(args.PossibleTools) == 0 { return errors.New("possible tools is empty") } provisioned, err := checkProvisioned(args.Host) if err != nil { return fmt.Errorf("failed to check provisioned status: %v", err) } if provisioned { return ErrProvisioned } // Filter tools based on detected series/arch. logger.Infof("Filtering possible tools: %v", args.PossibleTools) possibleTools, err := args.PossibleTools.Match(tools.Filter{ Arch: *args.HardwareCharacteristics.Arch, Series: args.Series, }) if err != nil { return err } // Store the state file. If provisioning fails, we'll remove the file. logger.Infof("Saving bootstrap state file to bootstrap storage") bootstrapStorage := args.Environ.Storage() err = bootstrap.SaveState( bootstrapStorage, &bootstrap.BootstrapState{ StateInstances: []instance.Id{BootstrapInstanceId}, Characteristics: []instance.HardwareCharacteristics{*args.HardwareCharacteristics}, }, ) if err != nil { return err } defer func() { if err != nil { logger.Errorf("bootstrapping failed, removing state file: %v", err) bootstrapStorage.Remove(bootstrap.StateFile) } }() // If the tools are on the machine already, get a file:// scheme tools URL. tools := *possibleTools[0] storageDir := args.Environ.StorageDir() toolsStorageName := envtools.StorageName(tools.Version) if url, _ := bootstrapStorage.URL(toolsStorageName); url == tools.URL { tools.URL = fmt.Sprintf("file://%s/%s", storageDir, toolsStorageName) } // Add the local storage configuration. agentEnv, err := localstorage.StoreConfig(args.Environ) if err != nil { return err } privateKey, err := common.GenerateSystemSSHKey(args.Environ) if err != nil { return err } // Finally, provision the machine agent. mcfg := environs.NewBootstrapMachineConfig(privateKey) mcfg.InstanceId = BootstrapInstanceId mcfg.HardwareCharacteristics = args.HardwareCharacteristics if args.DataDir != "" { mcfg.DataDir = args.DataDir } mcfg.Tools = &tools err = environs.FinishMachineConfig(mcfg, args.Environ.Config(), constraints.Value{}) if err != nil { return err } for k, v := range agentEnv { mcfg.AgentEnvironment[k] = v } return provisionMachineAgent(args.Host, mcfg, args.Context.GetStderr()) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/suite_test.go��������������������������0000644�0000153�0000161�00000000363�12321735642�026526� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func Test(t *stdtesting.T) { testing.MgoTestPackage(t) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/provisioner.go�������������������������0000644�0000153�0000161�00000025220�12321735776�026724� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual import ( "bytes" "errors" "fmt" "io" "net" "strings" "github.com/juju/loggo" coreCloudinit "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/cloudinit/sshinit" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/statecmd" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/shell" ) const manualInstancePrefix = "manual:" var logger = loggo.GetLogger("juju.environs.manual") type ProvisionMachineArgs struct { // Host is the SSH host: [user@]host Host string // DataDir is the root directory for juju data. // If left blank, the default location "/var/lib/juju" will be used. DataDir string // EnvName is the name of the environment for which the machine will be provisioned. EnvName string // Tools to install on the machine. If nil, tools will be automatically // chosen using environs/tools FindInstanceTools. Tools *tools.Tools // Stdin is required to respond to sudo prompts, // and must be a terminal (except in tests) Stdin io.Reader // Stdout is required to present sudo prompts to the user. Stdout io.Writer // Stderr is required to present machine provisioning progress to the user. Stderr io.Writer } // ErrProvisioned is returned by ProvisionMachine if the target // machine has an existing machine agent. var ErrProvisioned = errors.New("machine is already provisioned") // ProvisionMachine provisions a machine agent to an existing host, via // an SSH connection to the specified host. The host may optionally be preceded // with a login username, as in [user@]host. // // On successful completion, this function will return the id of the state.Machine // that was entered into state. func ProvisionMachine(args ProvisionMachineArgs) (machineId string, err error) { client, err := juju.NewAPIClientFromName(args.EnvName) if err != nil { return "", err } // Used for fallback to 1.16 code var stateConn *juju.Conn defer func() { if machineId != "" && err != nil { logger.Errorf("provisioning failed, removing machine %v: %v", machineId, err) // If we have stateConn, then we are in 1.16 // compatibility mode and we should issue // DestroyMachines directly on the state, rather than // via API (because DestroyMachine *also* didn't exist // in 1.16, though it will be in 1.16.5). // TODO: When this compatibility code is removed, we // should remove the method in state as well (as long // as destroy-machine also no longer needs it.) var cleanupErr error if stateConn != nil { cleanupErr = statecmd.DestroyMachines1dot16(stateConn.State, machineId) } else { cleanupErr = client.DestroyMachines(machineId) } if cleanupErr != nil { logger.Warningf("error cleaning up machine: %s", cleanupErr) } machineId = "" } if stateConn != nil { stateConn.Close() stateConn = nil } client.Close() }() // Create the "ubuntu" user and initialise passwordless sudo. We populate // the ubuntu user's authorized_keys file with the public keys in the current // user's ~/.ssh directory. The authenticationworker will later update the // ubuntu user's authorized_keys. user, hostname := splitUserHost(args.Host) authorizedKeys, err := config.ReadAuthorizedKeys("") if err := InitUbuntuUser(hostname, user, authorizedKeys, args.Stdin, args.Stdout); err != nil { return "", err } machineParams, err := gatherMachineParams(hostname) if err != nil { return "", err } // Inform Juju that the machine exists. machineId, err = recordMachineInState(client, *machineParams) if params.IsCodeNotImplemented(err) { logger.Infof("AddMachines not supported by the API server, " + "falling back to 1.16 compatibility mode (direct DB access)") stateConn, err = juju.NewConnFromName(args.EnvName) if err == nil { machineId, err = recordMachineInState1dot16(stateConn, *machineParams) } } if err != nil { return "", err } var provisioningScript string if stateConn == nil { provisioningScript, err = client.ProvisioningScript(params.ProvisioningScriptParams{ MachineId: machineId, Nonce: machineParams.Nonce, }) if err != nil { return "", err } } else { mcfg, err := statecmd.MachineConfig(stateConn.State, machineId, machineParams.Nonce, args.DataDir) if err == nil { provisioningScript, err = ProvisioningScript(mcfg) } if err != nil { return "", err } } // Finally, provision the machine agent. err = runProvisionScript(provisioningScript, hostname, args.Stderr) if err != nil { return machineId, err } logger.Infof("Provisioned machine %v", machineId) return machineId, nil } func splitUserHost(host string) (string, string) { if at := strings.Index(host, "@"); at != -1 { return host[:at], host[at+1:] } return "", host } func recordMachineInState( client *api.Client, machineParams params.AddMachineParams) (machineId string, err error) { results, err := client.AddMachines([]params.AddMachineParams{machineParams}) if err != nil { return "", err } // Currently, only one machine is added, but in future there may be several added in one call. machineInfo := results[0] if machineInfo.Error != nil { return "", machineInfo.Error } return machineInfo.Machine, nil } // convertToStateJobs takes a slice of params.MachineJob and makes them a slice of state.MachineJob func convertToStateJobs(jobs []params.MachineJob) ([]state.MachineJob, error) { outJobs := make([]state.MachineJob, len(jobs)) var err error for j, job := range jobs { if outJobs[j], err = state.MachineJobFromParams(job); err != nil { return nil, err } } return outJobs, nil } func recordMachineInState1dot16( stateConn *juju.Conn, machineParams params.AddMachineParams) (machineId string, err error) { stateJobs, err := convertToStateJobs(machineParams.Jobs) if err != nil { return "", err } template := state.MachineTemplate{ Series: machineParams.Series, Constraints: machineParams.Constraints, InstanceId: machineParams.InstanceId, Jobs: stateJobs, Nonce: machineParams.Nonce, HardwareCharacteristics: machineParams.HardwareCharacteristics, Addresses: machineParams.Addrs, } machine, err := stateConn.State.AddOneMachine(template) if err != nil { return "", err } return machine.Id(), nil } // gatherMachineParams collects all the information we know about the machine // we are about to provision. It will SSH into that machine as the ubuntu user. // The hostname supplied should not include a username. // If we can, we will reverse lookup the hostname by its IP address, and use // the DNS resolved name, rather than the name that was supplied func gatherMachineParams(hostname string) (*params.AddMachineParams, error) { // Generate a unique nonce for the machine. uuid, err := utils.NewUUID() if err != nil { return nil, err } // First, gather the parameters needed to inject the existing host into state. if ip := net.ParseIP(hostname); ip != nil { // Do a reverse-lookup on the IP. The IP may not have // a DNS entry, so just log a warning if this fails. names, err := net.LookupAddr(ip.String()) if err != nil { logger.Infof("failed to resolve %v: %v", ip, err) } else { logger.Infof("resolved %v to %v", ip, names) hostname = names[0] // TODO: jam 2014-01-09 https://bugs.launchpad.net/bugs/1267387 // We change what 'hostname' we are using here (rather // than an IP address we use the DNS name). I'm not // sure why that is better, but if we are changing the // host, we should probably be returning the hostname // to the parent function. // Also, we don't seem to try and compare if 'ip' is in // the list of addrs returned from // instance.HostAddresses in case you might get // multiple and one of them is what you are supposed to // be using. } } addrs, err := HostAddresses(hostname) if err != nil { return nil, err } logger.Infof("addresses for %v: %v", hostname, addrs) provisioned, err := checkProvisioned(hostname) if err != nil { err = fmt.Errorf("error checking if provisioned: %v", err) return nil, err } if provisioned { return nil, ErrProvisioned } hc, series, err := DetectSeriesAndHardwareCharacteristics(hostname) if err != nil { err = fmt.Errorf("error detecting hardware characteristics: %v", err) return nil, err } // There will never be a corresponding "instance" that any provider // knows about. This is fine, and works well with the provisioner // task. The provisioner task will happily remove any and all dead // machines from state, but will ignore the associated instance ID // if it isn't one that the environment provider knows about. instanceId := instance.Id(manualInstancePrefix + hostname) nonce := fmt.Sprintf("%s:%s", instanceId, uuid.String()) machineParams := &params.AddMachineParams{ Series: series, HardwareCharacteristics: hc, InstanceId: instanceId, Nonce: nonce, Addrs: addrs, Jobs: []params.MachineJob{params.JobHostUnits}, } return machineParams, nil } var provisionMachineAgent = func(host string, mcfg *cloudinit.MachineConfig, progressWriter io.Writer) error { script, err := ProvisioningScript(mcfg) if err != nil { return err } return runProvisionScript(script, host, progressWriter) } // ProvisioningScript generates a bash script that can be // executed on a remote host to carry out the cloud-init // configuration. func ProvisioningScript(mcfg *cloudinit.MachineConfig) (string, error) { cloudcfg := coreCloudinit.New() if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil { return "", err } // Explicitly disabling apt_upgrade so as not to trample // the target machine's existing configuration. cloudcfg.SetAptUpgrade(false) configScript, err := sshinit.ConfigureScript(cloudcfg) if err != nil { return "", err } var buf bytes.Buffer // Always remove the cloud-init-output.log file first, if it exists. fmt.Fprintf(&buf, "rm -f %s\n", utils.ShQuote(mcfg.CloudInitOutputLog)) // If something goes wrong, dump cloud-init-output.log to stderr. buf.WriteString(shell.DumpFileOnErrorScript(mcfg.CloudInitOutputLog)) buf.WriteString(configScript) return buf.String(), nil } func runProvisionScript(script, host string, progressWriter io.Writer) error { params := sshinit.ConfigureParams{ Host: "ubuntu@" + host, ProgressWriter: progressWriter, } return sshinit.RunConfigureScript(script, params) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/provisioner_test.go��������������������0000644�0000153�0000161�00000012114�12321735776�027761� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual_test import ( "fmt" "os" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" coreCloudinit "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/cloudinit/sshinit" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/manual" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/statecmd" "launchpad.net/juju-core/utils/shell" "launchpad.net/juju-core/version" ) type provisionerSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&provisionerSuite{}) func (s *provisionerSuite) getArgs(c *gc.C) manual.ProvisionMachineArgs { hostname, err := os.Hostname() c.Assert(err, gc.IsNil) return manual.ProvisionMachineArgs{ Host: hostname, EnvName: "dummyenv", } } func (s *provisionerSuite) TestProvisionMachine(c *gc.C) { const series = "precise" const arch = "amd64" args := s.getArgs(c) hostname := args.Host args.Host = "ubuntu@" + args.Host envtesting.RemoveTools(c, s.Conn.Environ.Storage()) defer fakeSSH{ Series: series, Arch: arch, InitUbuntuUser: true, SkipProvisionAgent: true, }.install(c).Restore() // Attempt to provision a machine with no tools available, expect it to fail. machineId, err := manual.ProvisionMachine(args) c.Assert(err, jc.Satisfies, params.IsCodeNotFound) c.Assert(machineId, gc.Equals, "") cfg := s.Conn.Environ.Config() number, ok := cfg.AgentVersion() c.Assert(ok, jc.IsTrue) binVersion := version.Binary{number, series, arch} envtesting.AssertUploadFakeToolsVersions(c, s.Conn.Environ.Storage(), binVersion) for i, errorCode := range []int{255, 0} { c.Logf("test %d: code %d", i, errorCode) defer fakeSSH{ Series: series, Arch: arch, InitUbuntuUser: true, ProvisionAgentExitCode: errorCode, }.install(c).Restore() machineId, err = manual.ProvisionMachine(args) if errorCode != 0 { c.Assert(err, gc.ErrorMatches, fmt.Sprintf("rc: %d", errorCode)) c.Assert(machineId, gc.Equals, "") } else { c.Assert(err, gc.IsNil) c.Assert(machineId, gc.Not(gc.Equals), "") // machine ID will be incremented. Even though we failed and the // machine is removed, the ID is not reused. c.Assert(machineId, gc.Equals, fmt.Sprint(i+1)) m, err := s.State.Machine(machineId) c.Assert(err, gc.IsNil) instanceId, err := m.InstanceId() c.Assert(err, gc.IsNil) c.Assert(instanceId, gc.Equals, instance.Id("manual:"+hostname)) } } // Attempting to provision a machine twice should fail. We effect // this by checking for existing juju upstart configurations. defer fakeSSH{ Provisioned: true, InitUbuntuUser: true, SkipDetection: true, SkipProvisionAgent: true, }.install(c).Restore() _, err = manual.ProvisionMachine(args) c.Assert(err, gc.Equals, manual.ErrProvisioned) defer fakeSSH{ Provisioned: true, CheckProvisionedExitCode: 255, InitUbuntuUser: true, SkipDetection: true, SkipProvisionAgent: true, }.install(c).Restore() _, err = manual.ProvisionMachine(args) c.Assert(err, gc.ErrorMatches, "error checking if provisioned: rc: 255") } func (s *provisionerSuite) TestFinishMachineConfig(c *gc.C) { const series = "precise" const arch = "amd64" defer fakeSSH{ Series: series, Arch: arch, InitUbuntuUser: true, }.install(c).Restore() machineId, err := manual.ProvisionMachine(s.getArgs(c)) c.Assert(err, gc.IsNil) // Now check what we would've configured it with. mcfg, err := statecmd.MachineConfig(s.State, machineId, state.BootstrapNonce, "/var/lib/juju") c.Assert(err, gc.IsNil) c.Check(mcfg, gc.NotNil) c.Check(mcfg.APIInfo, gc.NotNil) c.Check(mcfg.StateInfo, gc.NotNil) stateInfo, apiInfo, err := s.APIConn.Environ.StateInfo() c.Assert(err, gc.IsNil) c.Check(mcfg.APIInfo.Addrs, gc.DeepEquals, apiInfo.Addrs) c.Check(mcfg.StateInfo.Addrs, gc.DeepEquals, stateInfo.Addrs) } func (s *provisionerSuite) TestProvisioningScript(c *gc.C) { const series = "precise" const arch = "amd64" defer fakeSSH{ Series: series, Arch: arch, InitUbuntuUser: true, }.install(c).Restore() machineId, err := manual.ProvisionMachine(s.getArgs(c)) c.Assert(err, gc.IsNil) mcfg, err := statecmd.MachineConfig(s.State, machineId, state.BootstrapNonce, "/var/lib/juju") c.Assert(err, gc.IsNil) script, err := manual.ProvisioningScript(mcfg) c.Assert(err, gc.IsNil) cloudcfg := coreCloudinit.New() err = cloudinit.ConfigureJuju(mcfg, cloudcfg) c.Assert(err, gc.IsNil) cloudcfg.SetAptUpgrade(false) sshinitScript, err := sshinit.ConfigureScript(cloudcfg) c.Assert(err, gc.IsNil) removeLogFile := "rm -f '/var/log/cloud-init-output.log'\n" expectedScript := removeLogFile + shell.DumpFileOnErrorScript("/var/log/cloud-init-output.log") + sshinitScript c.Assert(script, gc.Equals, expectedScript) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/init.go��������������������������������0000644�0000153�0000161�00000014077�12321735642�025310� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual import ( "bytes" "fmt" "io" "strconv" "strings" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" ) // detectionScript is the script to run on the remote machine to // detect the OS series and hardware characteristics. const detectionScript = `#!/bin/bash set -e lsb_release -cs uname -m grep MemTotal /proc/meminfo cat /proc/cpuinfo` // checkProvisionedScript is the script to run on the remote machine // to check if a machine has already been provisioned. // // This is a little convoluted to avoid returning an error in the // common case of no matching files. const checkProvisionedScript = "ls /etc/init/ | grep juju.*\\.conf || exit 0" // checkProvisioned checks if any juju upstart jobs already // exist on the host machine. func checkProvisioned(host string) (bool, error) { logger.Infof("Checking if %s is already provisioned", host) cmd := ssh.Command("ubuntu@"+host, []string{"/bin/bash"}, nil) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Stdin = strings.NewReader(checkProvisionedScript) if err := cmd.Run(); err != nil { if stderr.Len() != 0 { err = fmt.Errorf("%v (%v)", err, strings.TrimSpace(stderr.String())) } return false, err } output := strings.TrimSpace(stdout.String()) provisioned := len(output) > 0 if provisioned { logger.Infof("%s is already provisioned [%q]", host, output) } else { logger.Infof("%s is not provisioned", host) } return provisioned, nil } // Patch for testing. var DetectSeriesAndHardwareCharacteristics = detectSeriesAndHardwareCharacteristics // detectSeriesAndHardwareCharacteristics detects the OS // series and hardware characteristics of the remote machine // by connecting to the machine and executing a bash script. func detectSeriesAndHardwareCharacteristics(host string) (hc instance.HardwareCharacteristics, series string, err error) { logger.Infof("Detecting series and characteristics on %s", host) cmd := ssh.Command("ubuntu@"+host, []string{"/bin/bash"}, nil) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Stdin = bytes.NewBufferString(detectionScript) if err := cmd.Run(); err != nil { if stderr.Len() != 0 { err = fmt.Errorf("%v (%v)", err, strings.TrimSpace(stderr.String())) } return hc, "", err } lines := strings.Split(stdout.String(), "\n") series = strings.TrimSpace(lines[0]) arch := arch.NormaliseArch(lines[1]) hc.Arch = &arch // HardwareCharacteristics wants memory in megabytes, // meminfo reports it in kilobytes. memkB := strings.Fields(lines[2])[1] // "MemTotal: NNN kB" hc.Mem = new(uint64) *hc.Mem, err = strconv.ParseUint(memkB, 10, 0) *hc.Mem /= 1024 // For each "physical id", count the number of cores. // This way we only count physical cores, not additional // logical cores due to hyperthreading. recorded := make(map[string]bool) var physicalId string hc.CpuCores = new(uint64) for _, line := range lines[3:] { if strings.HasPrefix(line, "physical id") { physicalId = strings.TrimSpace(strings.SplitN(line, ":", 2)[1]) } else if strings.HasPrefix(line, "cpu cores") { var cores uint64 value := strings.TrimSpace(strings.SplitN(line, ":", 2)[1]) if cores, err = strconv.ParseUint(value, 10, 0); err != nil { return hc, "", err } if !recorded[physicalId] { *hc.CpuCores += cores recorded[physicalId] = true } } } if *hc.CpuCores == 0 { // In the case of a single-core, non-HT CPU, we'll see no // "physical id" or "cpu cores" lines. *hc.CpuCores = 1 } // TODO(axw) calculate CpuPower. What algorithm do we use? logger.Infof("series: %s, characteristics: %s", series, hc) return hc, series, nil } // InitUbuntuUser adds the ubuntu user if it doesn't // already exist, updates its ~/.ssh/authorized_keys, // and enables passwordless sudo for it. // // InitUbuntuUser will initially attempt to login as // the ubuntu user, and verify that passwordless sudo // is enabled; only if this is false will there be an // attempt with the specified login. // // authorizedKeys may be empty, in which case the file // will be created and left empty. // // stdin and stdout will be used for remote sudo prompts, // if the ubuntu user must be created/updated. func InitUbuntuUser(host, login, authorizedKeys string, stdin io.Reader, stdout io.Writer) error { logger.Infof("initialising %q, user %q", host, login) // To avoid unnecessary prompting for the specified login, // initUbuntuUser will first attempt to ssh to the machine // as "ubuntu" with password authentication disabled, and // ensure that it can use sudo without a password. // // Note that we explicitly do not allocate a PTY, so we // get a failure if sudo prompts. cmd := ssh.Command("ubuntu@"+host, []string{"sudo", "-n", "true"}, nil) if cmd.Run() == nil { logger.Infof("ubuntu user is already initialised") return nil } // Failed to login as ubuntu (or passwordless sudo is not enabled). // Use specified login, and execute the initUbuntuScript below. if login != "" { host = login + "@" + host } script := fmt.Sprintf(initUbuntuScript, utils.ShQuote(authorizedKeys)) var options ssh.Options options.AllowPasswordAuthentication() options.EnablePTY() cmd = ssh.Command(host, []string{"sudo", "/bin/bash -c " + utils.ShQuote(script)}, &options) var stderr bytes.Buffer cmd.Stdin = stdin cmd.Stdout = stdout // for sudo prompt cmd.Stderr = &stderr if err := cmd.Run(); err != nil { if stderr.Len() != 0 { err = fmt.Errorf("%v (%v)", err, strings.TrimSpace(stderr.String())) } return err } return nil } const initUbuntuScript = ` set -e (id ubuntu &> /dev/null) || useradd -m ubuntu umask 0077 temp=$(mktemp) echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > $temp install -m 0440 $temp /etc/sudoers.d/90-juju-ubuntu rm $temp su ubuntu -c 'install -D -m 0600 /dev/null ~/.ssh/authorized_keys' export authorized_keys=%s if [ ! -z "$authorized_keys" ]; then su ubuntu -c 'printf "%%s\n" "$authorized_keys" >> ~/.ssh/authorized_keys' fi` �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/init_test.go���������������������������0000644�0000153�0000161�00000012071�12321735776�026347� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual_test import ( "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/manual" "launchpad.net/juju-core/testing/testbase" ) type initialisationSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&initialisationSuite{}) func (s *initialisationSuite) TestDetectSeries(c *gc.C) { response := strings.Join([]string{ "edgy", "armv4", "MemTotal: 4096 kB", "processor: 0", }, "\n") defer installFakeSSH(c, manual.DetectionScript, response, 0)() _, series, err := manual.DetectSeriesAndHardwareCharacteristics("whatever") c.Assert(err, gc.IsNil) c.Assert(series, gc.Equals, "edgy") } func (s *initialisationSuite) TestDetectionError(c *gc.C) { scriptResponse := strings.Join([]string{ "edgy", "armv4", "MemTotal: 4096 kB", "processor: 0", }, "\n") // if the script fails for whatever reason, then checkProvisioned // will return an error. stderr will be included in the error message. defer installFakeSSH(c, manual.DetectionScript, []string{scriptResponse, "oh noes"}, 33)() hc, _, err := manual.DetectSeriesAndHardwareCharacteristics("hostname") c.Assert(err, gc.ErrorMatches, "rc: 33 \\(oh noes\\)") // if the script doesn't fail, stderr is simply ignored. defer installFakeSSH(c, manual.DetectionScript, []string{scriptResponse, "non-empty-stderr"}, 0)() hc, _, err = manual.DetectSeriesAndHardwareCharacteristics("hostname") c.Assert(err, gc.IsNil) c.Assert(hc.String(), gc.Equals, "arch=armhf cpu-cores=1 mem=4M") } func (s *initialisationSuite) TestDetectHardwareCharacteristics(c *gc.C) { tests := []struct { summary string scriptResponse []string expectedHc string }{{ "Single CPU socket, single core, no hyper-threading", []string{"edgy", "armv4", "MemTotal: 4096 kB", "processor: 0"}, "arch=armhf cpu-cores=1 mem=4M", }, { "Single CPU socket, single core, hyper-threading", []string{ "edgy", "armv4", "MemTotal: 4096 kB", "processor: 0", "physical id: 0", "cpu cores: 1", "processor: 1", "physical id: 0", "cpu cores: 1", }, "arch=armhf cpu-cores=1 mem=4M", }, { "Single CPU socket, dual-core, no hyper-threading", []string{ "edgy", "armv4", "MemTotal: 4096 kB", "processor: 0", "physical id: 0", "cpu cores: 2", "processor: 1", "physical id: 0", "cpu cores: 2", }, "arch=armhf cpu-cores=2 mem=4M", }, { "Dual CPU socket, each single-core, hyper-threading", []string{ "edgy", "armv4", "MemTotal: 4096 kB", "processor: 0", "physical id: 0", "cpu cores: 1", "processor: 1", "physical id: 0", "cpu cores: 1", "processor: 2", "physical id: 1", "cpu cores: 1", "processor: 3", "physical id: 1", "cpu cores: 1", }, "arch=armhf cpu-cores=2 mem=4M", }} for i, test := range tests { c.Logf("test %d: %s", i, test.summary) scriptResponse := strings.Join(test.scriptResponse, "\n") defer installFakeSSH(c, manual.DetectionScript, scriptResponse, 0)() hc, _, err := manual.DetectSeriesAndHardwareCharacteristics("hostname") c.Assert(err, gc.IsNil) c.Assert(hc.String(), gc.Equals, test.expectedHc) } } func (s *initialisationSuite) TestCheckProvisioned(c *gc.C) { defer installFakeSSH(c, manual.CheckProvisionedScript, "", 0)() provisioned, err := manual.CheckProvisioned("example.com") c.Assert(err, gc.IsNil) c.Assert(provisioned, jc.IsFalse) defer installFakeSSH(c, manual.CheckProvisionedScript, "non-empty", 0)() provisioned, err = manual.CheckProvisioned("example.com") c.Assert(err, gc.IsNil) c.Assert(provisioned, jc.IsTrue) // stderr should not affect result. defer installFakeSSH(c, manual.CheckProvisionedScript, []string{"", "non-empty-stderr"}, 0)() provisioned, err = manual.CheckProvisioned("example.com") c.Assert(err, gc.IsNil) c.Assert(provisioned, jc.IsFalse) // if the script fails for whatever reason, then checkProvisioned // will return an error. stderr will be included in the error message. defer installFakeSSH(c, manual.CheckProvisionedScript, []string{"non-empty-stdout", "non-empty-stderr"}, 255)() _, err = manual.CheckProvisioned("example.com") c.Assert(err, gc.ErrorMatches, "rc: 255 \\(non-empty-stderr\\)") } func (s *initialisationSuite) TestInitUbuntuUserNonExisting(c *gc.C) { defer installFakeSSH(c, "", "", 0)() // successful creation of ubuntu user defer installFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login err := manual.InitUbuntuUser("testhost", "testuser", "", nil, nil) c.Assert(err, gc.IsNil) } func (s *initialisationSuite) TestInitUbuntuUserExisting(c *gc.C) { defer installFakeSSH(c, "", nil, 0)() manual.InitUbuntuUser("testhost", "testuser", "", nil, nil) } func (s *initialisationSuite) TestInitUbuntuUserError(c *gc.C) { defer installFakeSSH(c, "", []string{"", "failed to create ubuntu user"}, 123)() defer installFakeSSH(c, "", "", 1)() // simulate failure of ubuntu@ login err := manual.InitUbuntuUser("testhost", "testuser", "", nil, nil) c.Assert(err, gc.ErrorMatches, "rc: 123 \\(failed to create ubuntu user\\)") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/manual/addresses.go���������������������������0000644�0000153�0000161�00000001230�12321735776�026315� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual import ( "launchpad.net/juju-core/instance" ) var instanceHostAddresses = instance.HostAddresses // HostAddresses returns the addresses for the specified // hostname, and marks the input address as being public; // all other addresses have "unknown" scope. func HostAddresses(hostname string) ([]instance.Address, error) { addrs, err := instanceHostAddresses(hostname) if err != nil { return nil, err } // The final address is the one we fed in: mark it as public. addrs[len(addrs)-1].NetworkScope = instance.NetworkPublic return addrs, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/jujutest/�������������������������������������0000755�0000153�0000161�00000000000�12321736000�024372� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/jujutest/metadata.go��������������������������0000644�0000153�0000161�00000010656�12321735642�026524� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujutest import ( "fmt" "io/ioutil" "net/http" "strings" ) // CannedRoundTripper can be used to provide canned "http" responses without // actually starting an HTTP server. // // Use this in conjunction with ProxyRoundTripper. A ProxyRoundTripper is // what gets registered as the default handler for a given protocol (such as // "test") and then tests can direct the ProxyRoundTripper to delegate to a // CannedRoundTripper. The reason for this is that we can register a // roundtripper to handle a scheme, but there is no way to unregister it: you // may need to re-use the same ProxyRoundTripper but use different // CannedRoundTrippers to return different results. type CannedRoundTripper struct { // files maps file names to their contents. If the roundtripper // receives a request for any of these files, and none of the entries // in errorURLs below matches, it will return the contents associated // with that filename here. // TODO(jtv): Do something more sensible here: either make files take // precedence over errors, or return the given error *with* the given // contents, or just disallow overlap. files map[string]string // errorURLs are prefixes that should return specific HTTP status // codes. If a request's URL matches any of these prefixes, the // associated error status is returned. // There is no clever longest-prefix selection here. If more than // one prefix matches, any one of them may be used. // TODO(jtv): Decide what to do about multiple matching prefixes. errorURLS map[string]int } var _ http.RoundTripper = (*CannedRoundTripper)(nil) // ProxyRoundTripper is an http.RoundTripper implementation that does nothing // but delegate to another RoundTripper. This lets tests change how they handle // requests for a given scheme, despite the fact that the standard library does // not support un-registration, or registration of a new roundtripper with a // URL scheme that's already handled. // // Use the RegisterForScheme method to install this as the standard handler // for a particular protocol. For example, if you call // prt.RegisterForScheme("test") then afterwards, any request to "test:///foo" // will be routed to prt. type ProxyRoundTripper struct { // Sub is the roundtripper that this roundtripper delegates to, if any. // If you leave this nil, this roundtripper is effectively disabled. Sub http.RoundTripper } var _ http.RoundTripper = (*ProxyRoundTripper)(nil) // RegisterForScheme registers a ProxyRoundTripper as the default roundtripper // for the given URL scheme. // // This cannot be undone, nor overwritten with a different roundtripper. If // you change your mind later about what the roundtripper should do, set its // "Sub" field to delegate to a different roundtripper (or to nil if you don't // want to handle its requests at all any more). func (prt *ProxyRoundTripper) RegisterForScheme(scheme string) { http.DefaultTransport.(*http.Transport).RegisterProtocol(scheme, prt) } func (prt *ProxyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if prt.Sub == nil { panic("An attempt was made to request file content without having" + " the virtual filesystem initialized.") } return prt.Sub.RoundTrip(req) } func newHTTPResponse(status string, statusCode int, body string) *http.Response { return &http.Response{ Proto: "HTTP/1.0", ProtoMajor: 1, Header: make(http.Header), Close: true, // Parameter fields: Status: status, StatusCode: statusCode, Body: ioutil.NopCloser(strings.NewReader(body)), ContentLength: int64(len(body)), } } // RoundTrip returns a canned error or body for the given request. func (v *CannedRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { full := req.URL.String() for urlPrefix, statusCode := range v.errorURLS { if strings.HasPrefix(full, urlPrefix) { status := fmt.Sprintf("%d Error", statusCode) return newHTTPResponse(status, statusCode, ""), nil } } if contents, found := v.files[req.URL.Path]; found { return newHTTPResponse("200 OK", http.StatusOK, contents), nil } return newHTTPResponse("404 Not Found", http.StatusNotFound, ""), nil } // NewCannedRoundTripper returns a CannedRoundTripper with the given canned // responses. func NewCannedRoundTripper(files map[string]string, errorURLs map[string]int) *CannedRoundTripper { return &CannedRoundTripper{files, errorURLs} } ����������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/jujutest/metadata_test.go���������������������0000644�0000153�0000161�00000002570�12321735642�027557� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujutest import ( "io/ioutil" "net/http" "net/url" gc "launchpad.net/gocheck" ) type metadataSuite struct{} var _ = gc.Suite(&metadataSuite{}) func (s *metadataSuite) TestCannedRoundTripper(c *gc.C) { aContent := "a-content" vrt := NewCannedRoundTripper(map[string]string{ "a": aContent, "b": "b-content", }, nil) c.Assert(vrt, gc.NotNil) req := &http.Request{URL: &url.URL{Path: "a"}} resp, err := vrt.RoundTrip(req) c.Assert(err, gc.IsNil) c.Assert(resp, gc.NotNil) content, err := ioutil.ReadAll(resp.Body) c.Assert(string(content), gc.Equals, aContent) c.Assert(resp.ContentLength, gc.Equals, int64(len(aContent))) c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) c.Assert(resp.Status, gc.Equals, "200 OK") } func (s *metadataSuite) TestCannedRoundTripperMissing(c *gc.C) { vrt := NewCannedRoundTripper(map[string]string{"a": "a-content"}, nil) c.Assert(vrt, gc.NotNil) req := &http.Request{URL: &url.URL{Path: "no-such-file"}} resp, err := vrt.RoundTrip(req) c.Assert(err, gc.IsNil) c.Assert(resp, gc.NotNil) content, err := ioutil.ReadAll(resp.Body) c.Assert(string(content), gc.Equals, "") c.Assert(resp.ContentLength, gc.Equals, int64(0)) c.Assert(resp.StatusCode, gc.Equals, http.StatusNotFound) c.Assert(resp.Status, gc.Equals, "404 Not Found") } ����������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/jujutest/jujutest_test.go���������������������0000644�0000153�0000161�00000000326�12321735642�027651� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujutest import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/jujutest/tests.go�����������������������������0000644�0000153�0000161�00000017733�12321735642�026111� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujutest import ( "bytes" "io/ioutil" "net/http" "sort" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/storage" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // Tests is a gocheck suite containing tests verifying juju functionality // against the environment with the given configuration. The // tests are not designed to be run against a live server - the Environ // is opened once for each test, and some potentially expensive operations // may be executed. type Tests struct { testbase.LoggingSuite TestConfig coretesting.Attrs envtesting.ToolsFixture // ConfigStore holds the configuration storage // used when preparing the environment. // This is initialized by SetUpTest. ConfigStore configstore.Storage } // Open opens an instance of the testing environment. func (t *Tests) Open(c *gc.C) environs.Environ { info, err := t.ConfigStore.ReadInfo(t.TestConfig["name"].(string)) c.Assert(err, gc.IsNil) cfg, err := config.New(config.NoDefaults, info.BootstrapConfig()) c.Assert(err, gc.IsNil) e, err := environs.New(cfg) c.Assert(err, gc.IsNil, gc.Commentf("opening environ %#v", cfg.AllAttrs())) c.Assert(e, gc.NotNil) return e } // Prepare prepares an instance of the testing environment. func (t *Tests) Prepare(c *gc.C) environs.Environ { cfg, err := config.New(config.NoDefaults, t.TestConfig) c.Assert(err, gc.IsNil) e, err := environs.Prepare(cfg, coretesting.Context(c), t.ConfigStore) c.Assert(err, gc.IsNil, gc.Commentf("preparing environ %#v", t.TestConfig)) c.Assert(e, gc.NotNil) return e } func (t *Tests) SetUpTest(c *gc.C) { t.LoggingSuite.SetUpTest(c) t.ToolsFixture.SetUpTest(c) t.ConfigStore = configstore.NewMem() } func (t *Tests) TearDownTest(c *gc.C) { t.ToolsFixture.TearDownTest(c) t.LoggingSuite.TearDownTest(c) } func (t *Tests) TestStartStop(c *gc.C) { e := t.Prepare(c) envtesting.UploadFakeTools(c, e.Storage()) cfg, err := e.Config().Apply(map[string]interface{}{ "agent-version": version.Current.Number.String(), }) c.Assert(err, gc.IsNil) err = e.SetConfig(cfg) c.Assert(err, gc.IsNil) insts, err := e.Instances(nil) c.Assert(err, gc.IsNil) c.Assert(insts, gc.HasLen, 0) inst0, hc := testing.AssertStartInstance(c, e, "0") c.Assert(inst0, gc.NotNil) id0 := inst0.Id() // Sanity check for hardware characteristics. c.Assert(hc.Arch, gc.NotNil) c.Assert(hc.Mem, gc.NotNil) c.Assert(hc.CpuCores, gc.NotNil) inst1, _ := testing.AssertStartInstance(c, e, "1") c.Assert(inst1, gc.NotNil) id1 := inst1.Id() insts, err = e.Instances([]instance.Id{id0, id1}) c.Assert(err, gc.IsNil) c.Assert(insts, gc.HasLen, 2) c.Assert(insts[0].Id(), gc.Equals, id0) c.Assert(insts[1].Id(), gc.Equals, id1) // order of results is not specified insts, err = e.AllInstances() c.Assert(err, gc.IsNil) c.Assert(insts, gc.HasLen, 2) c.Assert(insts[0].Id(), gc.Not(gc.Equals), insts[1].Id()) err = e.StopInstances([]instance.Instance{inst0}) c.Assert(err, gc.IsNil) insts, err = e.Instances([]instance.Id{id0, id1}) c.Assert(err, gc.Equals, environs.ErrPartialInstances) c.Assert(insts[0], gc.IsNil) c.Assert(insts[1].Id(), gc.Equals, id1) insts, err = e.AllInstances() c.Assert(err, gc.IsNil) c.Assert(insts[0].Id(), gc.Equals, id1) } func (t *Tests) TestBootstrap(c *gc.C) { e := t.Prepare(c) envtesting.UploadFakeTools(c, e.Storage()) err := bootstrap.EnsureNotBootstrapped(e) c.Assert(err, gc.IsNil) err = bootstrap.Bootstrap(coretesting.Context(c), e, constraints.Value{}) c.Assert(err, gc.IsNil) info, apiInfo, err := e.StateInfo() c.Check(info.Addrs, gc.Not(gc.HasLen), 0) c.Check(apiInfo.Addrs, gc.Not(gc.HasLen), 0) err = bootstrap.EnsureNotBootstrapped(e) c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped") e2 := t.Open(c) envtesting.UploadFakeTools(c, e2.Storage()) err = bootstrap.EnsureNotBootstrapped(e2) c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped") info2, apiInfo2, err := e2.StateInfo() c.Check(info2, gc.DeepEquals, info) c.Check(apiInfo2, gc.DeepEquals, apiInfo) err = environs.Destroy(e2, t.ConfigStore) c.Assert(err, gc.IsNil) // Prepare again because Destroy invalidates old environments. e3 := t.Prepare(c) envtesting.UploadFakeTools(c, e3.Storage()) err = bootstrap.EnsureNotBootstrapped(e3) c.Assert(err, gc.IsNil) err = bootstrap.Bootstrap(coretesting.Context(c), e3, constraints.Value{}) c.Assert(err, gc.IsNil) err = bootstrap.EnsureNotBootstrapped(e3) c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped") } var noRetry = utils.AttemptStrategy{} func (t *Tests) TestPersistence(c *gc.C) { stor := t.Prepare(c).Storage() names := []string{ "aa", "zzz/aa", "zzz/bb", } for _, name := range names { checkFileDoesNotExist(c, stor, name, noRetry) checkPutFile(c, stor, name, []byte(name)) } checkList(c, stor, "", names) checkList(c, stor, "a", []string{"aa"}) checkList(c, stor, "zzz/", []string{"zzz/aa", "zzz/bb"}) storage2 := t.Open(c).Storage() for _, name := range names { checkFileHasContents(c, storage2, name, []byte(name), noRetry) } // remove the first file and check that the others remain. err := storage2.Remove(names[0]) c.Check(err, gc.IsNil) // check that it's ok to remove a file twice. err = storage2.Remove(names[0]) c.Check(err, gc.IsNil) // ... and check it's been removed in the other environment checkFileDoesNotExist(c, stor, names[0], noRetry) // ... and that the rest of the files are still around checkList(c, storage2, "", names[1:]) for _, name := range names[1:] { err := storage2.Remove(name) c.Assert(err, gc.IsNil) } // check they've all gone checkList(c, storage2, "", nil) } func checkList(c *gc.C, stor storage.StorageReader, prefix string, names []string) { lnames, err := storage.List(stor, prefix) c.Assert(err, gc.IsNil) // TODO(dfc) gocheck should grow an SliceEquals checker. expected := copyslice(lnames) sort.Strings(expected) actual := copyslice(names) sort.Strings(actual) c.Assert(expected, gc.DeepEquals, actual) } // copyslice returns a copy of the slice func copyslice(s []string) []string { r := make([]string, len(s)) copy(r, s) return r } func checkPutFile(c *gc.C, stor storage.StorageWriter, name string, contents []byte) { err := stor.Put(name, bytes.NewBuffer(contents), int64(len(contents))) c.Assert(err, gc.IsNil) } func checkFileDoesNotExist(c *gc.C, stor storage.StorageReader, name string, attempt utils.AttemptStrategy) { r, err := storage.GetWithRetry(stor, name, attempt) c.Assert(r, gc.IsNil) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func checkFileHasContents(c *gc.C, stor storage.StorageReader, name string, contents []byte, attempt utils.AttemptStrategy) { r, err := storage.GetWithRetry(stor, name, attempt) c.Assert(err, gc.IsNil) c.Check(r, gc.NotNil) defer r.Close() data, err := ioutil.ReadAll(r) c.Check(err, gc.IsNil) c.Check(data, gc.DeepEquals, contents) url, err := stor.URL(name) c.Assert(err, gc.IsNil) var resp *http.Response for a := attempt.Start(); a.Next(); { resp, err = utils.GetValidatingHTTPClient().Get(url) c.Assert(err, gc.IsNil) if resp.StatusCode != 404 { break } c.Logf("get retrying after earlier get succeeded. *sigh*.") } c.Assert(err, gc.IsNil) data, err = ioutil.ReadAll(resp.Body) c.Assert(err, gc.IsNil) defer resp.Body.Close() c.Assert(resp.StatusCode, gc.Equals, 200, gc.Commentf("error response: %s", data)) c.Check(data, gc.DeepEquals, contents) } �������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/environs/jujutest/livetests.go�������������������������0000644�0000153�0000161�00000070156�12321735776�026777� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujutest import ( "bytes" "fmt" "io" "io/ioutil" "strings" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/sync" envtesting "launchpad.net/juju-core/environs/testing" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // LiveTests contains tests that are designed to run against a live server // (e.g. Amazon EC2). The Environ is opened once only for all the tests // in the suite, stored in Env, and Destroyed after the suite has completed. type LiveTests struct { testbase.LoggingSuite envtesting.ToolsFixture // TestConfig contains the configuration attributes for opening an environment. TestConfig coretesting.Attrs // Attempt holds a strategy for waiting until the environment // becomes logically consistent. Attempt utils.AttemptStrategy // CanOpenState should be true if the testing environment allows // the state to be opened after bootstrapping. CanOpenState bool // HasProvisioner should be true if the environment has // a provisioning agent. HasProvisioner bool // Env holds the currently opened environment. // This is set by PrepareOnce and BootstrapOnce. Env environs.Environ // ConfigStore holds the configuration storage // used when preparing the environment. // This is initialized by SetUpSuite. ConfigStore configstore.Storage prepared bool bootstrapped bool } func (t *LiveTests) SetUpSuite(c *gc.C) { t.LoggingSuite.SetUpSuite(c) t.ConfigStore = configstore.NewMem() } func (t *LiveTests) SetUpTest(c *gc.C) { t.LoggingSuite.SetUpTest(c) t.ToolsFixture.SetUpTest(c) } func publicAttrs(e environs.Environ) map[string]interface{} { cfg := e.Config() secrets, err := e.Provider().SecretAttrs(cfg) if err != nil { panic(err) } attrs := cfg.AllAttrs() for attr := range secrets { delete(attrs, attr) } return attrs } func (t *LiveTests) TearDownSuite(c *gc.C) { if t.Env != nil { t.Destroy(c) } t.LoggingSuite.TearDownSuite(c) } func (t *LiveTests) TearDownTest(c *gc.C) { t.ToolsFixture.TearDownTest(c) t.LoggingSuite.TearDownTest(c) } // PrepareOnce ensures that the environment is // available and prepared. It sets t.Env appropriately. func (t *LiveTests) PrepareOnce(c *gc.C) { if t.prepared { return } cfg, err := config.New(config.NoDefaults, t.TestConfig) c.Assert(err, gc.IsNil) e, err := environs.Prepare(cfg, coretesting.Context(c), t.ConfigStore) c.Assert(err, gc.IsNil, gc.Commentf("preparing environ %#v", t.TestConfig)) c.Assert(e, gc.NotNil) t.Env = e t.prepared = true } func (t *LiveTests) BootstrapOnce(c *gc.C) { if t.bootstrapped { return } t.PrepareOnce(c) // We only build and upload tools if there will be a state agent that // we could connect to (actual live tests, rather than local-only) cons := constraints.MustParse("mem=2G") if t.CanOpenState { _, err := sync.Upload(t.Env.Storage(), nil, coretesting.FakeDefaultSeries) c.Assert(err, gc.IsNil) } envtesting.UploadFakeTools(c, t.Env.Storage()) err := bootstrap.EnsureNotBootstrapped(t.Env) c.Assert(err, gc.IsNil) err = bootstrap.Bootstrap(coretesting.Context(c), t.Env, cons) c.Assert(err, gc.IsNil) t.bootstrapped = true } func (t *LiveTests) Destroy(c *gc.C) { if t.Env == nil { return } err := environs.Destroy(t.Env, t.ConfigStore) c.Assert(err, gc.IsNil) t.bootstrapped = false t.prepared = false t.Env = nil } func (t *LiveTests) TestPrechecker(c *gc.C) { // Providers may implement Prechecker. If they do, then they should // return nil for empty constraints (excluding the null provider). prechecker, ok := t.Env.(state.Prechecker) if !ok { return } err := prechecker.PrecheckInstance("precise", constraints.Value{}) c.Assert(err, gc.IsNil) } // TestStartStop is similar to Tests.TestStartStop except // that it does not assume a pristine environment. func (t *LiveTests) TestStartStop(c *gc.C) { t.PrepareOnce(c) envtesting.UploadFakeTools(c, t.Env.Storage()) inst, _ := testing.AssertStartInstance(c, t.Env, "0") c.Assert(inst, gc.NotNil) id0 := inst.Id() insts, err := t.Env.Instances([]instance.Id{id0, id0}) c.Assert(err, gc.IsNil) c.Assert(insts, gc.HasLen, 2) c.Assert(insts[0].Id(), gc.Equals, id0) c.Assert(insts[1].Id(), gc.Equals, id0) // Asserting on the return of AllInstances makes the test fragile, // as even comparing the before and after start values can be thrown // off if other instances have been created or destroyed in the same // time frame. Instead, just check the instance we created exists. insts, err = t.Env.AllInstances() c.Assert(err, gc.IsNil) found := false for _, inst := range insts { if inst.Id() == id0 { c.Assert(found, gc.Equals, false, gc.Commentf("%v", insts)) found = true } } c.Assert(found, gc.Equals, true, gc.Commentf("expected %v in %v", inst, insts)) dns, err := inst.WaitDNSName() c.Assert(err, gc.IsNil) c.Assert(dns, gc.Not(gc.Equals), "") insts, err = t.Env.Instances([]instance.Id{id0, ""}) c.Assert(err, gc.Equals, environs.ErrPartialInstances) c.Assert(insts, gc.HasLen, 2) c.Check(insts[0].Id(), gc.Equals, id0) c.Check(insts[1], gc.IsNil) err = t.Env.StopInstances([]instance.Instance{inst}) c.Assert(err, gc.IsNil) // The machine may not be marked as shutting down // immediately. Repeat a few times to ensure we get the error. for a := t.Attempt.Start(); a.Next(); { insts, err = t.Env.Instances([]instance.Id{id0}) if err != nil { break } } c.Assert(err, gc.Equals, environs.ErrNoInstances) c.Assert(insts, gc.HasLen, 0) } func (t *LiveTests) TestPorts(c *gc.C) { t.PrepareOnce(c) envtesting.UploadFakeTools(c, t.Env.Storage()) inst1, _ := testing.AssertStartInstance(c, t.Env, "1") c.Assert(inst1, gc.NotNil) defer t.Env.StopInstances([]instance.Instance{inst1}) ports, err := inst1.Ports("1") c.Assert(err, gc.IsNil) c.Assert(ports, gc.HasLen, 0) inst2, _ := testing.AssertStartInstance(c, t.Env, "2") c.Assert(inst2, gc.NotNil) ports, err = inst2.Ports("2") c.Assert(err, gc.IsNil) c.Assert(ports, gc.HasLen, 0) defer t.Env.StopInstances([]instance.Instance{inst2}) // Open some ports and check they're there. err = inst1.OpenPorts("1", []instance.Port{{"udp", 67}, {"tcp", 45}}) c.Assert(err, gc.IsNil) ports, err = inst1.Ports("1") c.Assert(err, gc.IsNil) c.Assert(ports, gc.DeepEquals, []instance.Port{{"tcp", 45}, {"udp", 67}}) ports, err = inst2.Ports("2") c.Assert(err, gc.IsNil) c.Assert(ports, gc.HasLen, 0) err = inst2.OpenPorts("2", []instance.Port{{"tcp", 89}, {"tcp", 45}}) c.Assert(err, gc.IsNil) // Check there's no crosstalk to another machine ports, err = inst2.Ports("2") c.Assert(err, gc.IsNil) c.Assert(ports, gc.DeepEquals, []instance.Port{{"tcp", 45}, {"tcp", 89}}) ports, err = inst1.Ports("1") c.Assert(err, gc.IsNil) c.Assert(ports, gc.DeepEquals, []instance.Port{{"tcp", 45}, {"udp", 67}}) // Check that opening the same port again is ok. oldPorts, err := inst2.Ports("2") c.Assert(err, gc.IsNil) err = inst2.OpenPorts("2", []instance.Port{{"tcp", 45}}) c.Assert(err, gc.IsNil) ports, err = inst2.Ports("2") c.Assert(err, gc.IsNil) c.Assert(ports, gc.DeepEquals, oldPorts) // Check that opening the same port again and another port is ok. err = inst2.OpenPorts("2", []instance.Port{{"tcp", 45}, {"tcp", 99}}) c.Assert(err, gc.IsNil) ports, err = inst2.Ports("2") c.Assert(err, gc.IsNil) c.Assert(ports, gc.DeepEquals, []instance.Port{{"tcp", 45}, {"tcp", 89}, {"tcp", 99}}) err = inst2.ClosePorts("2", []instance.Port{{"tcp", 45}, {"tcp", 99}}) c.Assert(err, gc.IsNil) // Check that we can close ports and that there's no crosstalk. ports, err = inst2.Ports("2") c.Assert(err, gc.IsNil) c.Assert(ports, gc.DeepEquals, []instance.Port{{"tcp", 89}}) ports, err = inst1.Ports("1") c.Assert(err, gc.IsNil) c.Assert(ports, gc.DeepEquals, []instance.Port{{"tcp", 45}, {"udp", 67}}) // Check that we can close multiple ports. err = inst1.ClosePorts("1", []instance.Port{{"tcp", 45}, {"udp", 67}}) c.Assert(err, gc.IsNil) ports, err = inst1.Ports("1") c.Assert(ports, gc.HasLen, 0) // Check that we can close ports that aren't there. err = inst2.ClosePorts("2", []instance.Port{{"tcp", 111}, {"udp", 222}}) c.Assert(err, gc.IsNil) ports, err = inst2.Ports("2") c.Assert(ports, gc.DeepEquals, []instance.Port{{"tcp", 89}}) // Check errors when acting on environment. err = t.Env.OpenPorts([]instance.Port{{"tcp", 80}}) c.Assert(err, gc.ErrorMatches, `invalid firewall mode "instance" for opening ports on environment`) err = t.Env.ClosePorts([]instance.Port{{"tcp", 80}}) c.Assert(err, gc.ErrorMatches, `invalid firewall mode "instance" for closing ports on environment`) _, err = t.Env.Ports() c.Assert(err, gc.ErrorMatches, `invalid firewall mode "instance" for retrieving ports from environment`) } func (t *LiveTests) TestGlobalPorts(c *gc.C) { t.PrepareOnce(c) envtesting.UploadFakeTools(c, t.Env.Storage()) // Change configuration. oldConfig := t.Env.Config() defer func() { err := t.Env.SetConfig(oldConfig) c.Assert(err, gc.IsNil) }() attrs := t.Env.Config().AllAttrs() attrs["firewall-mode"] = "global" newConfig, err := t.Env.Config().Apply(attrs) c.Assert(err, gc.IsNil) err = t.Env.SetConfig(newConfig) c.Assert(err, gc.IsNil) // Create instances and check open ports on both instances. inst1, _ := testing.AssertStartInstance(c, t.Env, "1") defer t.Env.StopInstances([]instance.Instance{inst1}) ports, err := t.Env.Ports() c.Assert(err, gc.IsNil) c.Assert(ports, gc.HasLen, 0) inst2, _ := testing.AssertStartInstance(c, t.Env, "2") ports, err = t.Env.Ports() c.Assert(err, gc.IsNil) c.Assert(ports, gc.HasLen, 0) defer t.Env.StopInstances([]instance.Instance{inst2}) err = t.Env.OpenPorts([]instance.Port{{"udp", 67}, {"tcp", 45}, {"tcp", 89}, {"tcp", 99}}) c.Assert(err, gc.IsNil) ports, err = t.Env.Ports() c.Assert(err, gc.IsNil) c.Assert(ports, gc.DeepEquals, []instance.Port{{"tcp", 45}, {"tcp", 89}, {"tcp", 99}, {"udp", 67}}) // Check closing some ports. err = t.Env.ClosePorts([]instance.Port{{"tcp", 99}, {"udp", 67}}) c.Assert(err, gc.IsNil) ports, err = t.Env.Ports() c.Assert(err, gc.IsNil) c.Assert(ports, gc.DeepEquals, []instance.Port{{"tcp", 45}, {"tcp", 89}}) // Check that we can close ports that aren't there. err = t.Env.ClosePorts([]instance.Port{{"tcp", 111}, {"udp", 222}}) c.Assert(err, gc.IsNil) ports, err = t.Env.Ports() c.Assert(err, gc.IsNil) c.Assert(ports, gc.DeepEquals, []instance.Port{{"tcp", 45}, {"tcp", 89}}) // Check errors when acting on instances. err = inst1.OpenPorts("1", []instance.Port{{"tcp", 80}}) c.Assert(err, gc.ErrorMatches, `invalid firewall mode "global" for opening ports on instance`) err = inst1.ClosePorts("1", []instance.Port{{"tcp", 80}}) c.Assert(err, gc.ErrorMatches, `invalid firewall mode "global" for closing ports on instance`) _, err = inst1.Ports("1") c.Assert(err, gc.ErrorMatches, `invalid firewall mode "global" for retrieving ports from instance`) } func (t *LiveTests) TestBootstrapMultiple(c *gc.C) { // bootstrap.Bootstrap no longer raises errors if the environment is // already up, this has been moved into the bootstrap command. t.BootstrapOnce(c) err := bootstrap.EnsureNotBootstrapped(t.Env) c.Assert(err, gc.ErrorMatches, "environment is already bootstrapped") c.Logf("destroy env") env := t.Env t.Destroy(c) env.Destroy() // Again, should work fine and do nothing. // check that we can bootstrap after destroy t.BootstrapOnce(c) } func (t *LiveTests) TestBootstrapAndDeploy(c *gc.C) { if !t.CanOpenState || !t.HasProvisioner { c.Skip(fmt.Sprintf("skipping provisioner test, CanOpenState: %v, HasProvisioner: %v", t.CanOpenState, t.HasProvisioner)) } t.BootstrapOnce(c) // TODO(niemeyer): Stop growing this kitchen sink test and split it into proper parts. c.Logf("opening connection") conn, err := juju.NewConn(t.Env) c.Assert(err, gc.IsNil) defer conn.Close() c.Logf("opening API connection") apiConn, err := juju.NewAPIConn(t.Env, api.DefaultDialOpts()) c.Assert(err, gc.IsNil) defer conn.Close() // Check that the agent version has made it through the // bootstrap process (it's optional in the config.Config) cfg, err := conn.State.EnvironConfig() c.Assert(err, gc.IsNil) agentVersion, ok := cfg.AgentVersion() c.Check(ok, gc.Equals, true) c.Check(agentVersion, gc.Equals, version.Current.Number) // Check that the constraints have been set in the environment. cons, err := conn.State.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(cons.String(), gc.Equals, "mem=2048M") // Wait for machine agent to come up on the bootstrap // machine and find the deployed series from that. m0, err := conn.State.Machine("0") c.Assert(err, gc.IsNil) instId0, err := m0.InstanceId() c.Assert(err, gc.IsNil) // Check that the API connection is working. status, err := apiConn.State.Client().Status(nil) c.Assert(err, gc.IsNil) c.Assert(status.Machines["0"].InstanceId, gc.Equals, string(instId0)) mw0 := newMachineToolWaiter(m0) defer mw0.Stop() // If the series has not been specified, we expect the most recent Ubuntu LTS release to be used. expectedVersion := version.Current expectedVersion.Series = config.LatestLtsSeries() mtools0 := waitAgentTools(c, mw0, expectedVersion) // Create a new service and deploy a unit of it. c.Logf("deploying service") repoDir := c.MkDir() url := coretesting.Charms.ClonedURL(repoDir, mtools0.Version.Series, "dummy") sch, err := conn.PutCharm(url, &charm.LocalRepository{Path: repoDir}, false) c.Assert(err, gc.IsNil) svc, err := conn.State.AddService("dummy", "user-admin", sch, nil, nil) c.Assert(err, gc.IsNil) units, err := juju.AddUnits(conn.State, svc, 1, "") c.Assert(err, gc.IsNil) unit := units[0] // Wait for the unit's machine and associated agent to come up // and announce itself. mid1, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) m1, err := conn.State.Machine(mid1) c.Assert(err, gc.IsNil) mw1 := newMachineToolWaiter(m1) defer mw1.Stop() waitAgentTools(c, mw1, mtools0.Version) err = m1.Refresh() c.Assert(err, gc.IsNil) instId1, err := m1.InstanceId() c.Assert(err, gc.IsNil) uw := newUnitToolWaiter(unit) defer uw.Stop() utools := waitAgentTools(c, uw, expectedVersion) // Check that we can upgrade the environment. newVersion := utools.Version newVersion.Patch++ t.checkUpgrade(c, conn, newVersion, mw0, mw1, uw) // BUG(niemeyer): Logic below is very much wrong. Must be: // // 1. EnsureDying on the unit and EnsureDying on the machine // 2. Unit dies by itself // 3. Machine removes dead unit // 4. Machine dies by itself // 5. Provisioner removes dead machine // // Now remove the unit and its assigned machine and // check that the PA removes it. c.Logf("removing unit") err = unit.Destroy() c.Assert(err, gc.IsNil) // Wait until unit is dead uwatch := unit.Watch() defer uwatch.Stop() for unit.Life() != state.Dead { c.Logf("waiting for unit change") <-uwatch.Changes() err := unit.Refresh() c.Logf("refreshed; err %v", err) if errors.IsNotFoundError(err) { c.Logf("unit has been removed") break } c.Assert(err, gc.IsNil) } for { c.Logf("destroying machine") err := m1.Destroy() if err == nil { break } c.Assert(err, gc.FitsTypeOf, &state.HasAssignedUnitsError{}) time.Sleep(5 * time.Second) err = m1.Refresh() if errors.IsNotFoundError(err) { break } c.Assert(err, gc.IsNil) } c.Logf("waiting for instance to be removed") t.assertStopInstance(c, conn.Environ, instId1) } func (t *LiveTests) TestBootstrapVerifyStorage(c *gc.C) { // Bootstrap automatically verifies that storage is writable. t.BootstrapOnce(c) environ := t.Env stor := environ.Storage() reader, err := storage.Get(stor, "bootstrap-verify") c.Assert(err, gc.IsNil) defer reader.Close() contents, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(string(contents), gc.Equals, "juju-core storage writing verified: ok\n") } func restoreBootstrapVerificationFile(c *gc.C, stor storage.Storage) { content := "juju-core storage writing verified: ok\n" contentReader := strings.NewReader(content) err := stor.Put("bootstrap-verify", contentReader, int64(len(content))) c.Assert(err, gc.IsNil) } func (t *LiveTests) TestCheckEnvironmentOnConnect(c *gc.C) { // When new connection is established to a bootstraped environment, // it is checked that we are running against a juju-core environment. if !t.CanOpenState { c.Skip("CanOpenState is false; cannot open state connection") } t.BootstrapOnce(c) conn, err := juju.NewConn(t.Env) c.Assert(err, gc.IsNil) conn.Close() apiConn, err := juju.NewAPIConn(t.Env, api.DefaultDialOpts()) c.Assert(err, gc.IsNil) apiConn.Close() } func (t *LiveTests) TestCheckEnvironmentOnConnectNoVerificationFile(c *gc.C) { // When new connection is established to a bootstraped environment, // it is checked that we are running against a juju-core environment. // // Absence of a verification file means it is a juju-core environment // with an older version, which is fine. if !t.CanOpenState { c.Skip("CanOpenState is false; cannot open state connection") } t.BootstrapOnce(c) environ := t.Env stor := environ.Storage() err := stor.Remove("bootstrap-verify") c.Assert(err, gc.IsNil) defer restoreBootstrapVerificationFile(c, stor) conn, err := juju.NewConn(t.Env) c.Assert(err, gc.IsNil) conn.Close() } func (t *LiveTests) TestCheckEnvironmentOnConnectBadVerificationFile(c *gc.C) { // When new connection is established to a bootstraped environment, // it is checked that we are running against a juju-core environment. // // If the verification file has unexpected content, it is not // a juju-core environment (likely to a Python juju environment). if !t.CanOpenState { c.Skip("CanOpenState is false; cannot open state connection") } t.BootstrapOnce(c) environ := t.Env stor := environ.Storage() // Finally, replace the content with an arbitrary string. badVerificationContent := "bootstrap storage verification" reader := strings.NewReader(badVerificationContent) err := stor.Put( "bootstrap-verify", reader, int64(len(badVerificationContent))) c.Assert(err, gc.IsNil) defer restoreBootstrapVerificationFile(c, stor) // Running NewConn() should fail. _, err = juju.NewConn(t.Env) c.Assert(err, gc.Equals, environs.InvalidEnvironmentError) } type tooler interface { Life() state.Life AgentTools() (*coretools.Tools, error) Refresh() error String() string } type watcher interface { Stop() error Err() error } type toolsWaiter struct { lastTools *coretools.Tools // changes is a chan of struct{} so that it can // be used with different kinds of entity watcher. changes chan struct{} watcher watcher tooler tooler } func newMachineToolWaiter(m *state.Machine) *toolsWaiter { w := m.Watch() waiter := &toolsWaiter{ changes: make(chan struct{}, 1), watcher: w, tooler: m, } go func() { for _ = range w.Changes() { waiter.changes <- struct{}{} } close(waiter.changes) }() return waiter } func newUnitToolWaiter(u *state.Unit) *toolsWaiter { w := u.Watch() waiter := &toolsWaiter{ changes: make(chan struct{}, 1), watcher: w, tooler: u, } go func() { for _ = range w.Changes() { waiter.changes <- struct{}{} } close(waiter.changes) }() return waiter } func (w *toolsWaiter) Stop() error { return w.watcher.Stop() } // NextTools returns the next changed tools, waiting // until the tools are actually set. func (w *toolsWaiter) NextTools(c *gc.C) (*coretools.Tools, error) { for _ = range w.changes { err := w.tooler.Refresh() if err != nil { return nil, fmt.Errorf("cannot refresh: %v", err) } if w.tooler.Life() == state.Dead { return nil, fmt.Errorf("object is dead") } tools, err := w.tooler.AgentTools() if errors.IsNotFoundError(err) { c.Logf("tools not yet set") continue } if err != nil { return nil, err } changed := w.lastTools == nil || *tools != *w.lastTools w.lastTools = tools if changed { return tools, nil } c.Logf("found same tools") } return nil, fmt.Errorf("watcher closed prematurely: %v", w.watcher.Err()) } // waitAgentTools waits for the given agent // to start and returns the tools that it is running. func waitAgentTools(c *gc.C, w *toolsWaiter, expect version.Binary) *coretools.Tools { c.Logf("waiting for %v to signal agent version", w.tooler.String()) tools, err := w.NextTools(c) c.Assert(err, gc.IsNil) c.Check(tools.Version, gc.Equals, expect) return tools } // checkUpgrade sets the environment agent version and checks that // all the provided watchers upgrade to the requested version. func (t *LiveTests) checkUpgrade(c *gc.C, conn *juju.Conn, newVersion version.Binary, waiters ...*toolsWaiter) { c.Logf("putting testing version of juju tools") upgradeTools, err := sync.Upload(t.Env.Storage(), &newVersion.Number, newVersion.Series) c.Assert(err, gc.IsNil) // sync.Upload always returns tools for the series on which the tests are running. // We are only interested in checking the version.Number below so need to fake the // upgraded tools series to match that of newVersion. upgradeTools.Version.Series = newVersion.Series // Check that the put version really is the version we expect. c.Assert(upgradeTools.Version, gc.Equals, newVersion) err = statetesting.SetAgentVersion(conn.State, newVersion.Number) c.Assert(err, gc.IsNil) for i, w := range waiters { c.Logf("waiting for upgrade of %d: %v", i, w.tooler.String()) waitAgentTools(c, w, newVersion) c.Logf("upgrade %d successful", i) } } var waitAgent = utils.AttemptStrategy{ Total: 30 * time.Second, Delay: 1 * time.Second, } func (t *LiveTests) assertStartInstance(c *gc.C, m *state.Machine) { // Wait for machine to get an instance id. for a := waitAgent.Start(); a.Next(); { err := m.Refresh() c.Assert(err, gc.IsNil) instId, err := m.InstanceId() if err != nil { c.Assert(err, jc.Satisfies, state.IsNotProvisionedError) continue } _, err = t.Env.Instances([]instance.Id{instId}) c.Assert(err, gc.IsNil) return } c.Fatalf("provisioner failed to start machine after %v", waitAgent.Total) } func (t *LiveTests) assertStopInstance(c *gc.C, env environs.Environ, instId instance.Id) { var err error for a := waitAgent.Start(); a.Next(); { _, err = t.Env.Instances([]instance.Id{instId}) if err == nil { continue } if err == environs.ErrNoInstances { return } c.Logf("error from Instances: %v", err) } c.Fatalf("provisioner failed to stop machine after %v", waitAgent.Total) } // assertInstanceId asserts that the machine has an instance id // that matches that of the given instance. If the instance is nil, // It asserts that the instance id is unset. func assertInstanceId(c *gc.C, m *state.Machine, inst instance.Instance) { var wantId, gotId instance.Id var err error if inst != nil { wantId = inst.Id() } for a := waitAgent.Start(); a.Next(); { err := m.Refresh() c.Assert(err, gc.IsNil) gotId, err = m.InstanceId() if err != nil { c.Assert(err, jc.Satisfies, state.IsNotProvisionedError) if inst == nil { return } continue } break } c.Assert(err, gc.IsNil) c.Assert(gotId, gc.Equals, wantId) } // TODO check that binary data works ok? var contents = []byte("hello\n") var contents2 = []byte("goodbye\n\n") func (t *LiveTests) TestFile(c *gc.C) { t.PrepareOnce(c) name := fmt.Sprint("testfile", time.Now().UnixNano()) stor := t.Env.Storage() checkFileDoesNotExist(c, stor, name, t.Attempt) checkPutFile(c, stor, name, contents) checkFileHasContents(c, stor, name, contents, t.Attempt) checkPutFile(c, stor, name, contents2) // check that we can overwrite the file checkFileHasContents(c, stor, name, contents2, t.Attempt) // check that the listed contents include the // expected name. found := false var names []string attempt: for a := t.Attempt.Start(); a.Next(); { var err error names, err = stor.List("") c.Assert(err, gc.IsNil) for _, lname := range names { if lname == name { found = true break attempt } } } if !found { c.Errorf("file name %q not found in file list %q", name, names) } err := stor.Remove(name) c.Check(err, gc.IsNil) checkFileDoesNotExist(c, stor, name, t.Attempt) // removing a file that does not exist should not be an error. err = stor.Remove(name) c.Check(err, gc.IsNil) // RemoveAll deletes all files from storage. checkPutFile(c, stor, "file-1.txt", contents) checkPutFile(c, stor, "file-2.txt", contents) err = stor.RemoveAll() c.Check(err, gc.IsNil) checkFileDoesNotExist(c, stor, "file-1.txt", t.Attempt) checkFileDoesNotExist(c, stor, "file-2.txt", t.Attempt) } // Check that we get a consistent error when asking for an instance without // a valid machine config. func (t *LiveTests) TestStartInstanceWithEmptyNonceFails(c *gc.C) { machineId := "4" stateInfo := testing.FakeStateInfo(machineId) apiInfo := testing.FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, "", stateInfo, apiInfo) t.PrepareOnce(c) possibleTools := envtesting.AssertUploadFakeToolsVersions(c, t.Env.Storage(), version.MustParseBinary("5.4.5-precise-amd64")) inst, _, err := t.Env.StartInstance(environs.StartInstanceParams{ Tools: possibleTools, MachineConfig: machineConfig, }) if inst != nil { err := t.Env.StopInstances([]instance.Instance{inst}) c.Check(err, gc.IsNil) } c.Assert(inst, gc.IsNil) c.Assert(err, gc.ErrorMatches, ".*missing machine nonce") } func (t *LiveTests) TestBootstrapWithDefaultSeries(c *gc.C) { if !t.HasProvisioner { c.Skip("HasProvisioner is false; cannot test deployment") } current := version.Current other := current other.Series = "quantal" if current == other { other.Series = "precise" } dummyCfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge(coretesting.Attrs{ "state-server": false, "name": "dummy storage", })) dummyenv, err := environs.Prepare(dummyCfg, coretesting.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) defer dummyenv.Destroy() t.Destroy(c) attrs := t.TestConfig.Merge(coretesting.Attrs{"default-series": other.Series}) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := environs.Prepare(cfg, coretesting.Context(c), t.ConfigStore) c.Assert(err, gc.IsNil) defer environs.Destroy(env, t.ConfigStore) currentName := envtools.StorageName(current) otherName := envtools.StorageName(other) envStorage := env.Storage() dummyStorage := dummyenv.Storage() defer envStorage.Remove(otherName) _, err = sync.Upload(dummyStorage, &current.Number) c.Assert(err, gc.IsNil) // This will only work while cross-compiling across releases is safe, // which depends on external elements. Tends to be safe for the last // few releases, but we may have to refactor some day. err = storageCopy(dummyStorage, currentName, envStorage, otherName) c.Assert(err, gc.IsNil) err = bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) conn, err := juju.NewConn(env) c.Assert(err, gc.IsNil) defer conn.Close() // Wait for machine agent to come up on the bootstrap // machine and ensure it deployed the proper series. m0, err := conn.State.Machine("0") c.Assert(err, gc.IsNil) mw0 := newMachineToolWaiter(m0) defer mw0.Stop() waitAgentTools(c, mw0, other) } func storageCopy(source storage.Storage, sourcePath string, target storage.Storage, targetPath string) error { rc, err := storage.Get(source, sourcePath) if err != nil { return err } var buf bytes.Buffer _, err = io.Copy(&buf, rc) rc.Close() if err != nil { return err } return target.Put(targetPath, &buf, int64(buf.Len())) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/etc/���������������������������������������������������0000755�0000153�0000161�00000000000�12321735641�021437� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/etc/bash_completion.d/���������������������������������0000755�0000153�0000161�00000000000�12321735642�025030� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/etc/bash_completion.d/juju-core������������������������0000644�0000153�0000161�00000012026�12321735642�026657� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # juju-core.bash_completion.sh: dynamic bash completion for juju cmdline, # from parsed (and cached) juju status output. # # Author: JuanJo Ciarlante <jjo@canonical.com> # Copyright 2013+, Canonical Ltd. # License: GPLv3 # # Print (return) all machines _juju_machines_from_file() { python -c ' import json, sys; j=json.load(sys.stdin) print "\n".join(j["machines"].keys());' < ${1?} } # Print (return) all units, each optionally postfixed by $2 (eg. 'myservice/0:') _juju_units_from_file() { python -c ' trail="'${2}'" import json, sys; j=json.load(sys.stdin) all_units=[] for k,v in j["services"].items(): if v.get("units"): all_units.extend(v.get("units",{}).keys()) print "\n".join([unit + trail for unit in all_units]) ' < ${1?} } # Print (return) all services _juju_services_from_file() { python -c ' import json, sys; j=json.load(sys.stdin) print "\n".join(j["services"].keys());' < ${1?} } # Print (return) both services and units, currently used for juju status completion _juju_services_and_units_from_file() { _juju_services_from_file "$@" _juju_units_from_file "$@" } # Print (return) all juju commands _juju_list_commands() { juju help commands 2>/dev/null | awk '{print $1}' } # Print (return) flags for juju action, shamelessly excluding # -e/--environment for cleaner completion for common usage cases # (e.g. juju ssh <TAB>, etc) _juju_flags_for() { test -z "${1}" && return 0 juju help ${1} 2>/dev/null |egrep -o -- '(^|-)-[a-z-]+'|egrep -v -- '^(-e|--environment)'|sort -u } # Print (return) guessed completion function for cmd. # Guessing is done by parsing 1st line of juju help <cmd>, # see case switch below. _juju_completion_func_for_cmd() { local action=${1} cword=${2} # if cword==1 or action==help, use _juju_list_commands if [ "${cword}" -eq 1 -o "${action}" = help ]; then echo _juju_list_commands return 0 fi # parse 1st line of juju help <cmd>, to guess the completion function case $(juju help ${action} 2>/dev/null| head -1) in # special case for ssh, scp which have 'service' in 1st line of help: *\<unit*|*juju?ssh*|*juju?scp*) echo _juju_units_from_file;; *\<service*) echo _juju_services_from_file;; *\<machine*) echo _juju_machines_from_file;; *pattern*) echo _juju_services_and_units_from_file;; # e.g. status ?*) echo true ;; # help ok, existing command, no more expansion *) echo false;; # failed, not a command esac } # Print (return) filename from juju status cached output (if not expired), # create cache dirs if needed # - setups caching dir if non-existent # - caches juju status output, $cache_mins minutes max _juju_get_status_filename() { local cache_mins=60 # ttl=60 mins local cache_dir=$HOME/.cache/juju local juju_status_file=${cache_dir}/juju-status-${JUJU_ENV:-default} # setup caching dir under ~/.cache/juju test -d ${cache_dir} || install -d ${cache_dir} -m 700 # if can't find a fresh (age < $cache_mins) saved file, with a ~reasonable size ... if [[ -z $(find "${juju_status_file}" -mmin -${cache_mins} -a -size +32c 2> /dev/null) ]]; then # ... create it juju status --format=json > "${juju_status_file}".tmp && \ mv "${juju_status_file}".tmp "${juju_status_file}" rm -f "${juju_status_file}".tmp fi if [ -r "${juju_status_file}" ]; then echo "${juju_status_file}" else return 1 fi } # Main completion function wrap: # calls passed completion function, also adding flags for cmd _juju_complete_with_func() { local action="${1}" func=${2?} local cur # scp is special, as we want ':' appended to unit names, # and filename completion also. local postfix_str= compgen_xtra= if [ "${action}" = "scp" ]; then local orig_comp_wordbreaks="${COMP_WORDBREAKS}" COMP_WORDBREAKS="${COMP_WORDBREAKS/:/}" postfix_str=':' compgen_xtra='-A file' compopt -o nospace fi juju_status_file= # if func name ends with 'from_file', set juju_status_file [[ ${func} =~ .*from_file ]] && juju_status_file=$(_juju_get_status_filename) # build COMPREPLY from passed function stdout, and _juju_flags_for $action cur="${COMP_WORDS[COMP_CWORD]}" COMPREPLY=( $( compgen ${compgen_xtra} -W "$(${func} ${juju_status_file} ${postfix_str}) $(_juju_flags_for "${action}")" -- ${cur} )) if [ "${action}" = "scp" ]; then COMP_WORDBREAKS="${orig_comp_wordbreaks}" compopt +o nospace fi return 0 } # Not used here, available to the user for quick cache removal _juju_completion_cache_rm() { rm -fv $HOME/.cache/juju/juju-status-${JUJU_ENV:-default} } # main completion function entry point _juju() { local action parsing_func action="${COMP_WORDS[1]}" COMPREPLY=() parsing_func=$(_juju_completion_func_for_cmd "${action}" ${COMP_CWORD}) test -z "${parsing_func}" && return 0 _juju_complete_with_func "${action}" "${parsing_func}" return $? } complete -F _juju juju # vim: ai et sw=2 ts=2 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022163� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/�����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023471� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/context_test.go��������������������������0000644�0000153�0000161�00000056300�12321735776�026572� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( "fmt" "io/ioutil" "os" "path/filepath" "strings" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" apiuniter "launchpad.net/juju-core/state/api/uniter" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker/uniter" "launchpad.net/juju-core/worker/uniter/jujuc" ) var noProxies = osenv.ProxySettings{} type RunHookSuite struct { HookContextSuite } var _ = gc.Suite(&RunHookSuite{}) type hookSpec struct { // name is the name of the hook. name string // perm is the file permissions of the hook. perm os.FileMode // code is the exit status of the hook. code int // stdout holds a string to print to stdout stdout string // stderr holds a string to print to stderr stderr string // background holds a string to print in the background after 0.2s. background string } // makeCharm constructs a fake charm dir containing a single named hook // with permissions perm and exit code code. If output is non-empty, // the charm will write it to stdout and stderr, with each one prefixed // by name of the stream. It returns the charm directory and the path // to which the hook script will write environment variables. func makeCharm(c *gc.C, spec hookSpec) (charmDir, outPath string) { charmDir = c.MkDir() hooksDir := filepath.Join(charmDir, "hooks") err := os.Mkdir(hooksDir, 0755) c.Assert(err, gc.IsNil) c.Logf("openfile perm %v", spec.perm) hook, err := os.OpenFile(filepath.Join(hooksDir, spec.name), os.O_CREATE|os.O_WRONLY, spec.perm) c.Assert(err, gc.IsNil) defer hook.Close() printf := func(f string, a ...interface{}) { if _, err := fmt.Fprintf(hook, f+"\n", a...); err != nil { panic(err) } } outPath = filepath.Join(c.MkDir(), "hook.out") printf("#!/bin/bash") printf("env > " + outPath) if spec.stdout != "" { printf("echo %s", spec.stdout) } if spec.stderr != "" { printf("echo %s >&2", spec.stderr) } if spec.background != "" { // Print something fairly quickly, then sleep for // quite a long time - if the hook execution is // blocking because of the background process, // the hook execution will take much longer than // expected. printf("(sleep 0.2; echo %s; sleep 10) &", spec.background) } printf("exit %d", spec.code) return charmDir, outPath } func AssertEnvContains(c *gc.C, lines []string, env map[string]string) { for k, v := range env { sought := k + "=" + v found := false for _, line := range lines { if line == sought { found = true continue } } comment := gc.Commentf("expected to find %v among %v", sought, lines) c.Assert(found, gc.Equals, true, comment) } } func AssertEnv(c *gc.C, outPath string, charmDir string, env map[string]string, uuid string) { out, err := ioutil.ReadFile(outPath) c.Assert(err, gc.IsNil) lines := strings.Split(string(out), "\n") AssertEnvContains(c, lines, env) AssertEnvContains(c, lines, map[string]string{ "DEBIAN_FRONTEND": "noninteractive", "APT_LISTCHANGES_FRONTEND": "none", "CHARM_DIR": charmDir, "JUJU_AGENT_SOCKET": "/path/to/socket", "JUJU_ENV_UUID": uuid, }) } // LineBufferSize matches the constant used when creating // the bufio line reader. const lineBufferSize = 4096 var apiAddrs = []string{"a1:123", "a2:123"} var expectedApiAddrs = strings.Join(apiAddrs, " ") var runHookTests = []struct { summary string relid int remote string spec hookSpec err string env map[string]string proxySettings osenv.ProxySettings }{ { summary: "missing hook is not an error", relid: -1, }, { summary: "report failure to execute hook", relid: -1, spec: hookSpec{perm: 0600}, err: `exec: .*something-happened": permission denied`, }, { summary: "report error indicated by hook's exit status", relid: -1, spec: hookSpec{ perm: 0700, code: 99, }, err: "exit status 99", }, { summary: "output logging", relid: -1, spec: hookSpec{ perm: 0700, stdout: "stdout", stderr: "stderr", }, }, { summary: "output logging with background process", relid: -1, spec: hookSpec{ perm: 0700, stdout: "stdout", background: "not printed", }, }, { summary: "long line split", relid: -1, spec: hookSpec{ perm: 0700, stdout: strings.Repeat("a", lineBufferSize+10), }, }, { summary: "check shell environment for non-relation hook context", relid: -1, spec: hookSpec{perm: 0700}, proxySettings: osenv.ProxySettings{ Http: "http", Https: "https", Ftp: "ftp", NoProxy: "no proxy"}, env: map[string]string{ "JUJU_UNIT_NAME": "u/0", "JUJU_API_ADDRESSES": expectedApiAddrs, "JUJU_ENV_NAME": "test-env-name", "http_proxy": "http", "HTTP_PROXY": "http", "https_proxy": "https", "HTTPS_PROXY": "https", "ftp_proxy": "ftp", "FTP_PROXY": "ftp", "no_proxy": "no proxy", "NO_PROXY": "no proxy", }, }, { summary: "check shell environment for relation-broken hook context", relid: 1, spec: hookSpec{perm: 0700}, env: map[string]string{ "JUJU_UNIT_NAME": "u/0", "JUJU_API_ADDRESSES": expectedApiAddrs, "JUJU_ENV_NAME": "test-env-name", "JUJU_RELATION": "db", "JUJU_RELATION_ID": "db:1", "JUJU_REMOTE_UNIT": "", }, }, { summary: "check shell environment for relation hook context", relid: 1, remote: "r/1", spec: hookSpec{perm: 0700}, env: map[string]string{ "JUJU_UNIT_NAME": "u/0", "JUJU_API_ADDRESSES": expectedApiAddrs, "JUJU_ENV_NAME": "test-env-name", "JUJU_RELATION": "db", "JUJU_RELATION_ID": "db:1", "JUJU_REMOTE_UNIT": "r/1", }, }, } func (s *RunHookSuite) TestRunHook(c *gc.C) { uuid, err := utils.NewUUID() c.Assert(err, gc.IsNil) for i, t := range runHookTests { c.Logf("\ntest %d: %s; perm %v", i, t.summary, t.spec.perm) ctx := s.getHookContext(c, uuid.String(), t.relid, t.remote, t.proxySettings) var charmDir, outPath string var hookExists bool if t.spec.perm == 0 { charmDir = c.MkDir() } else { spec := t.spec spec.name = "something-happened" c.Logf("makeCharm %#v", spec) charmDir, outPath = makeCharm(c, spec) hookExists = true } toolsDir := c.MkDir() t0 := time.Now() err := ctx.RunHook("something-happened", charmDir, toolsDir, "/path/to/socket") if t.err == "" && hookExists { c.Assert(err, gc.IsNil) } else if !hookExists { c.Assert(uniter.IsMissingHookError(err), jc.IsTrue) } else { c.Assert(err, gc.ErrorMatches, t.err) } if t.env != nil { env := map[string]string{"PATH": toolsDir + ":" + os.Getenv("PATH")} for k, v := range t.env { env[k] = v } AssertEnv(c, outPath, charmDir, env, uuid.String()) } if t.spec.background != "" && time.Now().Sub(t0) > 5*time.Second { c.Errorf("background process holding up hook execution") } } } // split the line into buffer-sized lengths. func splitLine(s string) []string { var ss []string for len(s) > lineBufferSize { ss = append(ss, s[0:lineBufferSize]) s = s[lineBufferSize:] } if len(s) > 0 { ss = append(ss, s) } return ss } func (s *RunHookSuite) TestRunHookRelationFlushing(c *gc.C) { // Create a charm with a breaking hook. uuid, err := utils.NewUUID() c.Assert(err, gc.IsNil) ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies) charmDir, _ := makeCharm(c, hookSpec{ name: "something-happened", perm: 0700, code: 123, }) // Mess with multiple relation settings. node0, err := s.relctxs[0].Settings() node0.Set("foo", "1") node1, err := s.relctxs[1].Settings() node1.Set("bar", "2") // Run the failing hook. err = ctx.RunHook("something-happened", charmDir, c.MkDir(), "/path/to/socket") c.Assert(err, gc.ErrorMatches, "exit status 123") // Check that the changes to the local settings nodes have been discarded. node0, err = s.relctxs[0].Settings() c.Assert(err, gc.IsNil) c.Assert(node0.Map(), gc.DeepEquals, params.RelationSettings{"relation-name": "db0"}) node1, err = s.relctxs[1].Settings() c.Assert(err, gc.IsNil) c.Assert(node1.Map(), gc.DeepEquals, params.RelationSettings{"relation-name": "db1"}) // Check that the changes have been written to state. settings0, err := s.relunits[0].ReadSettings("u/0") c.Assert(err, gc.IsNil) c.Assert(settings0, gc.DeepEquals, map[string]interface{}{"relation-name": "db0"}) settings1, err := s.relunits[1].ReadSettings("u/0") c.Assert(err, gc.IsNil) c.Assert(settings1, gc.DeepEquals, map[string]interface{}{"relation-name": "db1"}) // Create a charm with a working hook, and mess with settings again. charmDir, _ = makeCharm(c, hookSpec{ name: "something-happened", perm: 0700, }) node0.Set("baz", "3") node1.Set("qux", "4") // Run the hook. err = ctx.RunHook("something-happened", charmDir, c.MkDir(), "/path/to/socket") c.Assert(err, gc.IsNil) // Check that the changes to the local settings nodes are still there. node0, err = s.relctxs[0].Settings() c.Assert(err, gc.IsNil) c.Assert(node0.Map(), gc.DeepEquals, params.RelationSettings{ "relation-name": "db0", "baz": "3", }) node1, err = s.relctxs[1].Settings() c.Assert(err, gc.IsNil) c.Assert(node1.Map(), gc.DeepEquals, params.RelationSettings{ "relation-name": "db1", "qux": "4", }) // Check that the changes have been written to state. settings0, err = s.relunits[0].ReadSettings("u/0") c.Assert(err, gc.IsNil) c.Assert(settings0, gc.DeepEquals, map[string]interface{}{ "relation-name": "db0", "baz": "3", }) settings1, err = s.relunits[1].ReadSettings("u/0") c.Assert(err, gc.IsNil) c.Assert(settings1, gc.DeepEquals, map[string]interface{}{ "relation-name": "db1", "qux": "4", }) } type ContextRelationSuite struct { testing.JujuConnSuite svc *state.Service rel *state.Relation ru *state.RelationUnit st *api.State uniter *apiuniter.State apiRelUnit *apiuniter.RelationUnit } var _ = gc.Suite(&ContextRelationSuite{}) func (s *ContextRelationSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) ch := s.AddTestingCharm(c, "riak") var err error s.svc = s.AddTestingService(c, "u", ch) rels, err := s.svc.Relations() c.Assert(err, gc.IsNil) c.Assert(rels, gc.HasLen, 1) s.rel = rels[0] unit, err := s.svc.AddUnit() c.Assert(err, gc.IsNil) s.ru, err = s.rel.Unit(unit) c.Assert(err, gc.IsNil) err = s.ru.EnterScope(nil) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = unit.SetPassword(password) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAs(c, unit.Tag(), password) s.uniter = s.st.Uniter() c.Assert(s.uniter, gc.NotNil) apiRel, err := s.uniter.Relation(s.rel.Tag()) c.Assert(err, gc.IsNil) apiUnit, err := s.uniter.Unit(unit.Tag()) c.Assert(err, gc.IsNil) s.apiRelUnit, err = apiRel.Unit(apiUnit) c.Assert(err, gc.IsNil) } func (s *ContextRelationSuite) TestChangeMembers(c *gc.C) { ctx := uniter.NewContextRelation(s.apiRelUnit, nil) c.Assert(ctx.UnitNames(), gc.HasLen, 0) // Check the units and settings after a simple update. ctx.UpdateMembers(uniter.SettingsMap{ "u/2": {"baz": "2"}, "u/4": {"qux": "4"}, }) c.Assert(ctx.UnitNames(), gc.DeepEquals, []string{"u/2", "u/4"}) assertSettings := func(unit string, expect params.RelationSettings) { actual, err := ctx.ReadSettings(unit) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, expect) } assertSettings("u/2", params.RelationSettings{"baz": "2"}) assertSettings("u/4", params.RelationSettings{"qux": "4"}) // Send a second update; check that members are only added, not removed. ctx.UpdateMembers(uniter.SettingsMap{ "u/1": {"foo": "1"}, "u/2": {"abc": "2"}, "u/3": {"bar": "3"}, }) c.Assert(ctx.UnitNames(), gc.DeepEquals, []string{"u/1", "u/2", "u/3", "u/4"}) // Check that all settings remain cached. assertSettings("u/1", params.RelationSettings{"foo": "1"}) assertSettings("u/2", params.RelationSettings{"abc": "2"}) assertSettings("u/3", params.RelationSettings{"bar": "3"}) assertSettings("u/4", params.RelationSettings{"qux": "4"}) // Delete a member, and check that it is no longer a member... ctx.DeleteMember("u/2") c.Assert(ctx.UnitNames(), gc.DeepEquals, []string{"u/1", "u/3", "u/4"}) // ...and that its settings are no longer cached. _, err := ctx.ReadSettings("u/2") c.Assert(err, gc.ErrorMatches, "cannot read settings for unit \"u/2\" in relation \"u:ring\": settings not found") } func (s *ContextRelationSuite) TestMemberCaching(c *gc.C) { unit, err := s.svc.AddUnit() c.Assert(err, gc.IsNil) ru, err := s.rel.Unit(unit) c.Assert(err, gc.IsNil) err = ru.EnterScope(map[string]interface{}{"blib": "blob"}) c.Assert(err, gc.IsNil) settings, err := ru.Settings() c.Assert(err, gc.IsNil) settings.Set("ping", "pong") _, err = settings.Write() c.Assert(err, gc.IsNil) ctx := uniter.NewContextRelation(s.apiRelUnit, map[string]int64{"u/1": 0}) // Check that uncached settings are read from state. m, err := ctx.ReadSettings("u/1") c.Assert(err, gc.IsNil) expectMap := settings.Map() expectSettings := convertMap(expectMap) c.Assert(m, gc.DeepEquals, expectSettings) // Check that changes to state do not affect the cached settings. settings.Set("ping", "pow") _, err = settings.Write() c.Assert(err, gc.IsNil) m, err = ctx.ReadSettings("u/1") c.Assert(err, gc.IsNil) c.Assert(m, gc.DeepEquals, expectSettings) // Check that ClearCache spares the members cache. ctx.ClearCache() m, err = ctx.ReadSettings("u/1") c.Assert(err, gc.IsNil) c.Assert(m, gc.DeepEquals, expectSettings) // Check that updating the context overwrites the cached settings, and // that the contents of state are ignored. ctx.UpdateMembers(uniter.SettingsMap{"u/1": {"entirely": "different"}}) m, err = ctx.ReadSettings("u/1") c.Assert(err, gc.IsNil) c.Assert(m, gc.DeepEquals, params.RelationSettings{"entirely": "different"}) } func (s *ContextRelationSuite) TestNonMemberCaching(c *gc.C) { unit, err := s.svc.AddUnit() c.Assert(err, gc.IsNil) ru, err := s.rel.Unit(unit) c.Assert(err, gc.IsNil) err = ru.EnterScope(map[string]interface{}{"blib": "blob"}) c.Assert(err, gc.IsNil) settings, err := ru.Settings() c.Assert(err, gc.IsNil) settings.Set("ping", "pong") _, err = settings.Write() c.Assert(err, gc.IsNil) ctx := uniter.NewContextRelation(s.apiRelUnit, nil) // Check that settings are read from state. m, err := ctx.ReadSettings("u/1") c.Assert(err, gc.IsNil) expectMap := settings.Map() expectSettings := convertMap(expectMap) c.Assert(m, gc.DeepEquals, expectSettings) // Check that changes to state do not affect the obtained settings... settings.Set("ping", "pow") _, err = settings.Write() c.Assert(err, gc.IsNil) m, err = ctx.ReadSettings("u/1") c.Assert(err, gc.IsNil) c.Assert(m, gc.DeepEquals, expectSettings) // ...until the caches are cleared. ctx.ClearCache() c.Assert(err, gc.IsNil) m, err = ctx.ReadSettings("u/1") c.Assert(err, gc.IsNil) c.Assert(m["ping"], gc.Equals, "pow") } func (s *ContextRelationSuite) TestSettings(c *gc.C) { ctx := uniter.NewContextRelation(s.apiRelUnit, nil) // Change Settings, then clear cache without writing. node, err := ctx.Settings() c.Assert(err, gc.IsNil) expectSettings := node.Map() expectMap := convertSettings(expectSettings) node.Set("change", "exciting") ctx.ClearCache() // Check that the change is not cached... node, err = ctx.Settings() c.Assert(err, gc.IsNil) c.Assert(node.Map(), gc.DeepEquals, expectSettings) // ...and not written to state. settings, err := s.ru.ReadSettings("u/0") c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, expectMap) // Change again, write settings, and clear caches. node.Set("change", "exciting") err = ctx.WriteSettings() c.Assert(err, gc.IsNil) ctx.ClearCache() // Check that the change is reflected in Settings... expectSettings["change"] = "exciting" expectMap["change"] = expectSettings["change"] node, err = ctx.Settings() c.Assert(err, gc.IsNil) c.Assert(node.Map(), gc.DeepEquals, expectSettings) // ...and was written to state. settings, err = s.ru.ReadSettings("u/0") c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, expectMap) } type InterfaceSuite struct { HookContextSuite } var _ = gc.Suite(&InterfaceSuite{}) func (s *InterfaceSuite) GetContext(c *gc.C, relId int, remoteName string) jujuc.Context { uuid, err := utils.NewUUID() c.Assert(err, gc.IsNil) return s.HookContextSuite.getHookContext(c, uuid.String(), relId, remoteName, noProxies) } func (s *InterfaceSuite) TestUtils(c *gc.C) { ctx := s.GetContext(c, -1, "") c.Assert(ctx.UnitName(), gc.Equals, "u/0") r, found := ctx.HookRelation() c.Assert(found, gc.Equals, false) c.Assert(r, gc.IsNil) name, found := ctx.RemoteUnitName() c.Assert(found, gc.Equals, false) c.Assert(name, gc.Equals, "") c.Assert(ctx.RelationIds(), gc.HasLen, 2) r, found = ctx.Relation(0) c.Assert(found, gc.Equals, true) c.Assert(r.Name(), gc.Equals, "db") c.Assert(r.FakeId(), gc.Equals, "db:0") r, found = ctx.Relation(123) c.Assert(found, gc.Equals, false) c.Assert(r, gc.IsNil) ctx = s.GetContext(c, 1, "") r, found = ctx.HookRelation() c.Assert(found, gc.Equals, true) c.Assert(r.Name(), gc.Equals, "db") c.Assert(r.FakeId(), gc.Equals, "db:1") ctx = s.GetContext(c, 1, "u/123") name, found = ctx.RemoteUnitName() c.Assert(found, gc.Equals, true) c.Assert(name, gc.Equals, "u/123") } func (s *InterfaceSuite) TestUnitCaching(c *gc.C) { ctx := s.GetContext(c, -1, "") pr, ok := ctx.PrivateAddress() c.Assert(ok, gc.Equals, true) c.Assert(pr, gc.Equals, "u-0.testing.invalid") _, ok = ctx.PublicAddress() c.Assert(ok, gc.Equals, false) // Change remote state. u, err := s.State.Unit("u/0") c.Assert(err, gc.IsNil) err = u.SetPrivateAddress("") c.Assert(err, gc.IsNil) err = u.SetPublicAddress("blah.testing.invalid") c.Assert(err, gc.IsNil) // Local view is unchanged. pr, ok = ctx.PrivateAddress() c.Assert(ok, gc.Equals, true) c.Assert(pr, gc.Equals, "u-0.testing.invalid") _, ok = ctx.PublicAddress() c.Assert(ok, gc.Equals, false) } func (s *InterfaceSuite) TestConfigCaching(c *gc.C) { ctx := s.GetContext(c, -1, "") settings, err := ctx.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) // Change remote config. err = s.service.UpdateConfigSettings(charm.Settings{ "blog-title": "Something Else", }) c.Assert(err, gc.IsNil) // Local view is not changed. settings, err = ctx.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) } type HookContextSuite struct { testing.JujuConnSuite service *state.Service unit *state.Unit relch *state.Charm relunits map[int]*state.RelationUnit relctxs map[int]*uniter.ContextRelation st *api.State uniter *apiuniter.State apiUnit *apiuniter.Unit } func (s *HookContextSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) var err error sch := s.AddTestingCharm(c, "wordpress") s.service = s.AddTestingService(c, "u", sch) s.unit = s.AddUnit(c, s.service) password, err := utils.RandomPassword() err = s.unit.SetPassword(password) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAs(c, s.unit.Tag(), password) s.uniter = s.st.Uniter() c.Assert(s.uniter, gc.NotNil) // Note: The unit must always have a charm URL set, because this // happens as part of the installation process (that happens // before the initial install hook). err = s.unit.SetCharmURL(sch.URL()) c.Assert(err, gc.IsNil) s.relch = s.AddTestingCharm(c, "mysql") s.relunits = map[int]*state.RelationUnit{} s.relctxs = map[int]*uniter.ContextRelation{} s.AddContextRelation(c, "db0") s.AddContextRelation(c, "db1") } func (s *HookContextSuite) AddUnit(c *gc.C, svc *state.Service) *state.Unit { unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) name := strings.Replace(unit.Name(), "/", "-", 1) err = unit.SetPrivateAddress(name + ".testing.invalid") c.Assert(err, gc.IsNil) return unit } func (s *HookContextSuite) AddContextRelation(c *gc.C, name string) { s.AddTestingService(c, name, s.relch) eps, err := s.State.InferEndpoints([]string{"u", name}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(s.unit) c.Assert(err, gc.IsNil) s.relunits[rel.Id()] = ru err = ru.EnterScope(map[string]interface{}{"relation-name": name}) c.Assert(err, gc.IsNil) s.apiUnit, err = s.uniter.Unit(s.unit.Tag()) c.Assert(err, gc.IsNil) apiRel, err := s.uniter.Relation(rel.Tag()) c.Assert(err, gc.IsNil) apiRelUnit, err := apiRel.Unit(s.apiUnit) c.Assert(err, gc.IsNil) s.relctxs[rel.Id()] = uniter.NewContextRelation(apiRelUnit, nil) } func (s *HookContextSuite) getHookContext(c *gc.C, uuid string, relid int, remote string, proxies osenv.ProxySettings) *uniter.HookContext { if relid != -1 { _, found := s.relctxs[relid] c.Assert(found, gc.Equals, true) } context, err := uniter.NewHookContext(s.apiUnit, "TestCtx", uuid, "test-env-name", relid, remote, s.relctxs, apiAddrs, "test-owner", proxies) c.Assert(err, gc.IsNil) return context } func convertSettings(settings params.RelationSettings) map[string]interface{} { result := make(map[string]interface{}) for k, v := range settings { result[k] = v } return result } func convertMap(settingsMap map[string]interface{}) params.RelationSettings { result := make(params.RelationSettings) for k, v := range settingsMap { result[k] = v.(string) } return result } type RunCommandSuite struct { HookContextSuite } var _ = gc.Suite(&RunCommandSuite{}) func (s *RunCommandSuite) getHookContext(c *gc.C) *uniter.HookContext { uuid, err := utils.NewUUID() c.Assert(err, gc.IsNil) return s.HookContextSuite.getHookContext(c, uuid.String(), -1, "", noProxies) } func (s *RunCommandSuite) TestRunCommandsHasEnvironSet(c *gc.C) { context := s.getHookContext(c) charmDir := c.MkDir() result, err := context.RunCommands("env | sort", charmDir, "/path/to/tools", "/path/to/socket") c.Assert(err, gc.IsNil) executionEnvironment := map[string]string{} for _, value := range strings.Split(string(result.Stdout), "\n") { bits := strings.SplitN(value, "=", 2) if len(bits) == 2 { executionEnvironment[bits[0]] = bits[1] } } expected := map[string]string{ "APT_LISTCHANGES_FRONTEND": "none", "DEBIAN_FRONTEND": "noninteractive", "CHARM_DIR": charmDir, "JUJU_CONTEXT_ID": "TestCtx", "JUJU_AGENT_SOCKET": "/path/to/socket", "JUJU_UNIT_NAME": "u/0", "JUJU_ENV_NAME": "test-env-name", } for key, value := range expected { c.Check(executionEnvironment[key], gc.Equals, value) } } func (s *RunCommandSuite) TestRunCommandsStdOutAndErrAndRC(c *gc.C) { context := s.getHookContext(c) charmDir := c.MkDir() commands := ` echo this is standard out echo this is standard err >&2 exit 42 ` result, err := context.RunCommands(commands, charmDir, "/path/to/tools", "/path/to/socket") c.Assert(err, gc.IsNil) c.Assert(result.Code, gc.Equals, 42) c.Assert(string(result.Stdout), gc.Equals, "this is standard out\n") c.Assert(string(result.Stderr), gc.Equals, "this is standard err\n") } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/context.go�������������������������������0000644�0000153�0000161�00000027745�12321735642�025536� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "bufio" "fmt" "io" "os" "os/exec" "path/filepath" "sort" "strings" "sync" "time" "github.com/juju/loggo" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/uniter" utilexec "launchpad.net/juju-core/utils/exec" unitdebug "launchpad.net/juju-core/worker/uniter/debug" "launchpad.net/juju-core/worker/uniter/jujuc" ) type missingHookError struct { hookName string } func (e *missingHookError) Error() string { return e.hookName + " does not exist" } func IsMissingHookError(err error) bool { _, ok := err.(*missingHookError) return ok } // HookContext is the implementation of jujuc.Context. type HookContext struct { unit *uniter.Unit // privateAddress is the cached value of the unit's private // address. privateAddress string // publicAddress is the cached value of the unit's public // address. publicAddress string // configSettings holds the service configuration. configSettings charm.Settings // id identifies the context. id string // uuid is the universally unique identifier of the environment. uuid string // envName is the human friendly name of the environment. envName string // relationId identifies the relation for which a relation hook is // executing. If it is -1, the context is not running a relation hook; // otherwise, its value must be a valid key into the relations map. relationId int // remoteUnitName identifies the changing unit of the executing relation // hook. It will be empty if the context is not running a relation hook, // or if it is running a relation-broken hook. remoteUnitName string // relations contains the context for every relation the unit is a member // of, keyed on relation id. relations map[int]*ContextRelation // apiAddrs contains the API server addresses. apiAddrs []string // serviceOwner contains the owner of the service serviceOwner string // proxySettings are the current proxy settings that the uniter knows about proxySettings osenv.ProxySettings } func NewHookContext(unit *uniter.Unit, id, uuid, envName string, relationId int, remoteUnitName string, relations map[int]*ContextRelation, apiAddrs []string, serviceOwner string, proxySettings osenv.ProxySettings) (*HookContext, error) { ctx := &HookContext{ unit: unit, id: id, uuid: uuid, envName: envName, relationId: relationId, remoteUnitName: remoteUnitName, relations: relations, apiAddrs: apiAddrs, serviceOwner: serviceOwner, proxySettings: proxySettings, } // Get and cache the addresses. var err error ctx.publicAddress, err = unit.PublicAddress() if err != nil && !params.IsCodeNoAddressSet(err) { return nil, err } ctx.privateAddress, err = unit.PrivateAddress() if err != nil && !params.IsCodeNoAddressSet(err) { return nil, err } return ctx, nil } func (ctx *HookContext) UnitName() string { return ctx.unit.Name() } func (ctx *HookContext) PublicAddress() (string, bool) { return ctx.publicAddress, ctx.publicAddress != "" } func (ctx *HookContext) PrivateAddress() (string, bool) { return ctx.privateAddress, ctx.privateAddress != "" } func (ctx *HookContext) OpenPort(protocol string, port int) error { return ctx.unit.OpenPort(protocol, port) } func (ctx *HookContext) ClosePort(protocol string, port int) error { return ctx.unit.ClosePort(protocol, port) } func (ctx *HookContext) OwnerTag() string { return ctx.serviceOwner } func (ctx *HookContext) ConfigSettings() (charm.Settings, error) { if ctx.configSettings == nil { var err error ctx.configSettings, err = ctx.unit.ConfigSettings() if err != nil { return nil, err } } result := charm.Settings{} for name, value := range ctx.configSettings { result[name] = value } return result, nil } func (ctx *HookContext) HookRelation() (jujuc.ContextRelation, bool) { return ctx.Relation(ctx.relationId) } func (ctx *HookContext) RemoteUnitName() (string, bool) { return ctx.remoteUnitName, ctx.remoteUnitName != "" } func (ctx *HookContext) Relation(id int) (jujuc.ContextRelation, bool) { r, found := ctx.relations[id] return r, found } func (ctx *HookContext) RelationIds() []int { ids := []int{} for id := range ctx.relations { ids = append(ids, id) } return ids } // hookVars returns an os.Environ-style list of strings necessary to run a hook // such that it can know what environment it's operating in, and can call back // into ctx. func (ctx *HookContext) hookVars(charmDir, toolsDir, socketPath string) []string { vars := []string{ "APT_LISTCHANGES_FRONTEND=none", "DEBIAN_FRONTEND=noninteractive", "PATH=" + toolsDir + ":" + os.Getenv("PATH"), "CHARM_DIR=" + charmDir, "JUJU_CONTEXT_ID=" + ctx.id, "JUJU_AGENT_SOCKET=" + socketPath, "JUJU_UNIT_NAME=" + ctx.unit.Name(), "JUJU_ENV_UUID=" + ctx.uuid, "JUJU_ENV_NAME=" + ctx.envName, "JUJU_API_ADDRESSES=" + strings.Join(ctx.apiAddrs, " "), } if r, found := ctx.HookRelation(); found { vars = append(vars, "JUJU_RELATION="+r.Name()) vars = append(vars, "JUJU_RELATION_ID="+r.FakeId()) name, _ := ctx.RemoteUnitName() vars = append(vars, "JUJU_REMOTE_UNIT="+name) } vars = append(vars, ctx.proxySettings.AsEnvironmentValues()...) return vars } func (ctx *HookContext) finalizeContext(process string, err error) error { writeChanges := err == nil for id, rctx := range ctx.relations { if writeChanges { if e := rctx.WriteSettings(); e != nil { e = fmt.Errorf( "could not write settings from %q to relation %d: %v", process, id, e, ) logger.Errorf("%v", e) if err == nil { err = e } } } rctx.ClearCache() } return err } // RunCommands executes the commands in an environment which allows it to to // call back into the hook context to execute jujuc tools. func (ctx *HookContext) RunCommands(commands, charmDir, toolsDir, socketPath string) (*utilexec.ExecResponse, error) { env := ctx.hookVars(charmDir, toolsDir, socketPath) result, err := utilexec.RunCommands( utilexec.RunParams{ Commands: commands, WorkingDir: charmDir, Environment: env}) return result, ctx.finalizeContext("run commands", err) } func (ctx *HookContext) GetLogger(hookName string) loggo.Logger { return loggo.GetLogger(fmt.Sprintf("unit.%s.%s", ctx.UnitName(), hookName)) } // RunHook executes a hook in an environment which allows it to to call back // into the hook context to execute jujuc tools. func (ctx *HookContext) RunHook(hookName, charmDir, toolsDir, socketPath string) error { var err error env := ctx.hookVars(charmDir, toolsDir, socketPath) debugctx := unitdebug.NewHooksContext(ctx.unit.Name()) if session, _ := debugctx.FindSession(); session != nil && session.MatchHook(hookName) { logger.Infof("executing %s via debug-hooks", hookName) err = session.RunHook(hookName, charmDir, env) } else { err = ctx.runCharmHook(hookName, charmDir, env) } return ctx.finalizeContext(hookName, err) } func (ctx *HookContext) runCharmHook(hookName, charmDir string, env []string) error { hook, err := exec.LookPath(filepath.Join(charmDir, "hooks", hookName)) if err != nil { if ee, ok := err.(*exec.Error); ok && os.IsNotExist(ee.Err) { // Missing hook is perfectly valid, but worth mentioning. logger.Infof("skipped %q hook (not implemented)", hookName) return &missingHookError{hookName} } return err } ps := exec.Command(hook) ps.Env = env ps.Dir = charmDir outReader, outWriter, err := os.Pipe() if err != nil { return fmt.Errorf("cannot make logging pipe: %v", err) } ps.Stdout = outWriter ps.Stderr = outWriter hookLogger := &hookLogger{ r: outReader, done: make(chan struct{}), logger: ctx.GetLogger(hookName), } go hookLogger.run() err = ps.Start() outWriter.Close() if err == nil { err = ps.Wait() } hookLogger.stop() return err } type hookLogger struct { r io.ReadCloser done chan struct{} mu sync.Mutex stopped bool logger loggo.Logger } func (l *hookLogger) run() { defer close(l.done) defer l.r.Close() br := bufio.NewReaderSize(l.r, 4096) for { line, _, err := br.ReadLine() if err != nil { if err != io.EOF { logger.Errorf("cannot read hook output: %v", err) } break } l.mu.Lock() if l.stopped { l.mu.Unlock() return } l.logger.Infof("%s", line) l.mu.Unlock() } } func (l *hookLogger) stop() { // We can see the process exit before the logger has processed // all its output, so allow a moment for the data buffered // in the pipe to be processed. We don't wait indefinitely though, // because the hook may have started a background process // that keeps the pipe open. select { case <-l.done: case <-time.After(100 * time.Millisecond): } // We can't close the pipe asynchronously, so just // stifle output instead. l.mu.Lock() l.stopped = true l.mu.Unlock() } // SettingsMap is a map from unit name to relation settings. type SettingsMap map[string]params.RelationSettings // ContextRelation is the implementation of jujuc.ContextRelation. type ContextRelation struct { ru *uniter.RelationUnit // members contains settings for known relation members. Nil values // indicate members whose settings have not yet been cached. members SettingsMap // settings allows read and write access to the relation unit settings. settings *uniter.Settings // cache is a short-term cache that enables consistent access to settings // for units that are not currently participating in the relation. Its // contents should be cleared whenever a new hook is executed. cache SettingsMap } // NewContextRelation creates a new context for the given relation unit. // The unit-name keys of members supplies the initial membership. func NewContextRelation(ru *uniter.RelationUnit, members map[string]int64) *ContextRelation { ctx := &ContextRelation{ru: ru, members: SettingsMap{}} for unit := range members { ctx.members[unit] = nil } ctx.ClearCache() return ctx } // WriteSettings persists all changes made to the unit's relation settings. func (ctx *ContextRelation) WriteSettings() (err error) { if ctx.settings != nil { err = ctx.settings.Write() } return } // ClearCache discards all cached settings for units that are not members // of the relation, and all unwritten changes to the unit's relation settings. // including any changes to Settings that have not been written. func (ctx *ContextRelation) ClearCache() { ctx.settings = nil ctx.cache = make(SettingsMap) } // UpdateMembers ensures that the context is aware of every supplied // member unit. For each supplied member, the cached settings will be // overwritten. func (ctx *ContextRelation) UpdateMembers(members SettingsMap) { for m, s := range members { ctx.members[m] = s } } // DeleteMember drops the membership and cache of a single remote unit, without // perturbing settings for the remaining members. func (ctx *ContextRelation) DeleteMember(unitName string) { delete(ctx.members, unitName) } func (ctx *ContextRelation) Id() int { return ctx.ru.Relation().Id() } func (ctx *ContextRelation) Name() string { return ctx.ru.Endpoint().Name } func (ctx *ContextRelation) FakeId() string { return fmt.Sprintf("%s:%d", ctx.Name(), ctx.ru.Relation().Id()) } func (ctx *ContextRelation) UnitNames() (units []string) { for unit := range ctx.members { units = append(units, unit) } sort.Strings(units) return units } func (ctx *ContextRelation) Settings() (jujuc.Settings, error) { if ctx.settings == nil { node, err := ctx.ru.Settings() if err != nil { return nil, err } ctx.settings = node } return ctx.settings, nil } func (ctx *ContextRelation) ReadSettings(unit string) (settings params.RelationSettings, err error) { settings, member := ctx.members[unit] if settings == nil { if settings = ctx.cache[unit]; settings == nil { settings, err = ctx.ru.ReadSettings(unit) if err != nil { return nil, err } } } if member { ctx.members[unit] = settings } else { ctx.cache[unit] = settings } return settings, nil } ���������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/export_test.go���������������������������0000644�0000153�0000161�00000000574�12321735642�026421� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "launchpad.net/juju-core/juju/osenv" ) func SetUniterObserver(u *Uniter, observer UniterExecutionObserver) { u.observer = observer } func (u *Uniter) GetProxyValues() osenv.ProxySettings { u.proxyMutex.Lock() defer u.proxyMutex.Unlock() return u.proxy } ������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/charm/�����������������������������������0000755�0000153�0000161�00000000000�12321736000�024563� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/charm/bundles_test.go��������������������0000644�0000153�0000161�00000010626�12321735642�027625� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "crypto/sha256" "encoding/hex" "fmt" "io/ioutil" "net/url" "os" "path/filepath" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" corecharm "launchpad.net/juju-core/charm" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/uniter" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker/uniter/charm" ) type BundlesDirSuite struct { coretesting.HTTPSuite testing.JujuConnSuite st *api.State uniter *uniter.State } var _ = gc.Suite(&BundlesDirSuite{}) func (s *BundlesDirSuite) SetUpSuite(c *gc.C) { s.HTTPSuite.SetUpSuite(c) s.JujuConnSuite.SetUpSuite(c) } func (s *BundlesDirSuite) TearDownSuite(c *gc.C) { s.JujuConnSuite.TearDownSuite(c) s.HTTPSuite.TearDownSuite(c) } func (s *BundlesDirSuite) SetUpTest(c *gc.C) { s.HTTPSuite.SetUpTest(c) s.JujuConnSuite.SetUpTest(c) // Add a charm, service and unit to login to the API with. charm := s.AddTestingCharm(c, "wordpress") service := s.AddTestingService(c, "wordpress", charm) unit, err := service.AddUnit() c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = unit.SetPassword(password) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAs(c, unit.Tag(), password) c.Assert(s.st, gc.NotNil) s.uniter = s.st.Uniter() c.Assert(s.uniter, gc.NotNil) } func (s *BundlesDirSuite) TearDownTest(c *gc.C) { err := s.st.Close() c.Assert(err, gc.IsNil) s.JujuConnSuite.TearDownTest(c) s.HTTPSuite.TearDownTest(c) } func (s *BundlesDirSuite) AddCharm(c *gc.C) (*uniter.Charm, *state.Charm, []byte) { curl := corecharm.MustParseURL("cs:quantal/dummy-1") surl, err := url.Parse(s.URL("/some/charm.bundle")) c.Assert(err, gc.IsNil) bunpath := coretesting.Charms.BundlePath(c.MkDir(), "dummy") bun, err := corecharm.ReadBundle(bunpath) c.Assert(err, gc.IsNil) bundata, hash := readHash(c, bunpath) sch, err := s.State.AddCharm(bun, curl, surl, hash) c.Assert(err, gc.IsNil) apiCharm, err := s.uniter.Charm(sch.URL()) c.Assert(err, gc.IsNil) return apiCharm, sch, bundata } func (s *BundlesDirSuite) TestGet(c *gc.C) { basedir := c.MkDir() bunsdir := filepath.Join(basedir, "random", "bundles") d := charm.NewBundlesDir(bunsdir) // Check it doesn't get created until it's needed. _, err := os.Stat(bunsdir) c.Assert(err, jc.Satisfies, os.IsNotExist) // Add a charm to state that we can try to get. apiCharm, sch, bundata := s.AddCharm(c) // Try to get the charm when the content doesn't match. coretesting.Server.Response(200, nil, []byte("roflcopter")) _, err = d.Read(apiCharm, nil) prefix := fmt.Sprintf(`failed to download charm "cs:quantal/dummy-1" from %q: `, sch.BundleURL()) c.Assert(err, gc.ErrorMatches, prefix+fmt.Sprintf(`expected sha256 %q, got ".*"`, sch.BundleSha256())) // Try to get a charm whose bundle doesn't exist. coretesting.Server.Response(404, nil, nil) _, err = d.Read(apiCharm, nil) c.Assert(err, gc.ErrorMatches, prefix+`.* 404 Not Found`) // Get a charm whose bundle exists and whose content matches. coretesting.Server.Response(200, nil, bundata) ch, err := d.Read(apiCharm, nil) c.Assert(err, gc.IsNil) assertCharm(c, ch, sch) // Get the same charm again, without preparing a response from the server. ch, err = d.Read(apiCharm, nil) c.Assert(err, gc.IsNil) assertCharm(c, ch, sch) // Abort a download. err = os.RemoveAll(bunsdir) c.Assert(err, gc.IsNil) abort := make(chan struct{}) done := make(chan bool) go func() { ch, err := d.Read(apiCharm, abort) c.Assert(ch, gc.IsNil) c.Assert(err, gc.ErrorMatches, prefix+"aborted") close(done) }() close(abort) coretesting.Server.Response(500, nil, nil) select { case <-done: case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for abort") } } func readHash(c *gc.C, path string) ([]byte, string) { data, err := ioutil.ReadFile(path) c.Assert(err, gc.IsNil) hash := sha256.New() hash.Write(data) return data, hex.EncodeToString(hash.Sum(nil)) } func assertCharm(c *gc.C, bun charm.Bundle, sch *state.Charm) { actual := bun.(*corecharm.Bundle) c.Assert(actual.Revision(), gc.Equals, sch.Revision()) c.Assert(actual.Meta(), gc.DeepEquals, sch.Meta()) c.Assert(actual.Config(), gc.DeepEquals, sch.Config()) } ����������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/charm/export_test.go���������������������0000644�0000153�0000161�00000000622�12321735642�027505� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm // exported so we can get the deployer path from tests. func GitDeployerDataPath(d Deployer) string { return d.(*gitDeployer).dataPath } // exported so we can get the deployer current git repo from tests. func GitDeployerCurrent(d Deployer) *GitDir { return d.(*gitDeployer).current } ��������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/charm/git_test.go������������������������0000644�0000153�0000161�00000015402�12321735642�026751� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "io/ioutil" "os" "os/exec" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" corecharm "launchpad.net/juju-core/charm" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/worker/uniter/charm" ) var curl = corecharm.MustParseURL("cs:series/blah-blah-123") type GitDirSuite struct { testing.GitSuite LoggingSuite testbase.LoggingSuite } var _ = gc.Suite(&GitDirSuite{}) func (s *GitDirSuite) SetUpTest(c *gc.C) { s.GitSuite.SetUpTest(c) s.LoggingSuite.SetUpTest(c) s.LoggingSuite.PatchEnvironment("LC_ALL", "en_US") } func (s *GitDirSuite) TearDownTest(c *gc.C) { s.LoggingSuite.TearDownTest(c) s.GitSuite.TearDownTest(c) } func (s *GitDirSuite) TestInitConfig(c *gc.C) { base := c.MkDir() repo := charm.NewGitDir(filepath.Join(base, "repo")) err := repo.Init() c.Assert(err, gc.IsNil) cmd := exec.Command("git", "config", "--list", "--local") cmd.Dir = repo.Path() out, err := cmd.Output() c.Assert(err, gc.IsNil) outstr := string(out) c.Assert(outstr, jc.Contains, "user.email=juju@localhost") c.Assert(outstr, jc.Contains, "user.name=juju") } func (s *GitDirSuite) TestCreate(c *gc.C) { base := c.MkDir() repo := charm.NewGitDir(filepath.Join(base, "repo")) exists, err := repo.Exists() c.Assert(err, gc.IsNil) c.Assert(exists, jc.IsFalse) err = ioutil.WriteFile(repo.Path(), nil, 0644) c.Assert(err, gc.IsNil) _, err = repo.Exists() c.Assert(err, gc.ErrorMatches, `".*/repo" is not a directory`) err = os.Remove(repo.Path()) c.Assert(err, gc.IsNil) err = os.Chmod(base, 0555) c.Assert(err, gc.IsNil) defer os.Chmod(base, 0755) err = repo.Init() c.Assert(err, gc.ErrorMatches, ".* permission denied") exists, err = repo.Exists() c.Assert(err, gc.IsNil) c.Assert(exists, jc.IsFalse) err = os.Chmod(base, 0755) c.Assert(err, gc.IsNil) err = repo.Init() c.Assert(err, gc.IsNil) exists, err = repo.Exists() c.Assert(err, gc.IsNil) c.Assert(exists, jc.IsTrue) _, err = repo.ReadCharmURL() c.Assert(err, jc.Satisfies, os.IsNotExist) err = repo.Init() c.Assert(err, gc.IsNil) } func (s *GitDirSuite) TestAddCommitPullRevert(c *gc.C) { target := charm.NewGitDir(c.MkDir()) err := target.Init() c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(target.Path(), "initial"), []byte("initial"), 0644) c.Assert(err, gc.IsNil) err = target.WriteCharmURL(curl) c.Assert(err, gc.IsNil) err = target.AddAll() c.Assert(err, gc.IsNil) dirty, err := target.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsTrue) err = target.Commitf("initial") c.Assert(err, gc.IsNil) dirty, err = target.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsFalse) source := newRepo(c) err = target.Pull(source) c.Assert(err, gc.IsNil) url, err := target.ReadCharmURL() c.Assert(err, gc.IsNil) c.Assert(url, gc.DeepEquals, curl) fi, err := os.Stat(filepath.Join(target.Path(), "some-dir")) c.Assert(err, gc.IsNil) c.Assert(fi, jc.Satisfies, os.FileInfo.IsDir) data, err := ioutil.ReadFile(filepath.Join(target.Path(), "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "hello") dirty, err = target.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsFalse) err = ioutil.WriteFile(filepath.Join(target.Path(), "another-file"), []byte("blah"), 0644) c.Assert(err, gc.IsNil) dirty, err = target.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsTrue) err = source.AddAll() c.Assert(err, gc.IsNil) dirty, err = target.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsTrue) err = target.Revert() c.Assert(err, gc.IsNil) _, err = os.Stat(filepath.Join(target.Path(), "some-file")) c.Assert(err, jc.Satisfies, os.IsNotExist) _, err = os.Stat(filepath.Join(target.Path(), "some-dir")) c.Assert(err, jc.Satisfies, os.IsNotExist) data, err = ioutil.ReadFile(filepath.Join(target.Path(), "initial")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "initial") dirty, err = target.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsFalse) } func (s *GitDirSuite) TestClone(c *gc.C) { repo, err := newRepo(c).Clone(c.MkDir()) c.Assert(err, gc.IsNil) _, err = os.Stat(filepath.Join(repo.Path(), "some-file")) c.Assert(err, jc.Satisfies, os.IsNotExist) _, err = os.Stat(filepath.Join(repo.Path(), "some-dir")) c.Assert(err, jc.Satisfies, os.IsNotExist) dirty, err := repo.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsTrue) err = repo.AddAll() c.Assert(err, gc.IsNil) dirty, err = repo.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsTrue) err = repo.Commitf("blank overwrite") c.Assert(err, gc.IsNil) dirty, err = repo.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsFalse) lines, err := repo.Log() c.Assert(err, gc.IsNil) c.Assert(lines, gc.HasLen, 2) c.Assert(lines[0], gc.Matches, "[a-f0-9]{7} blank overwrite") c.Assert(lines[1], gc.Matches, "[a-f0-9]{7} im in ur repo committin ur files") } func (s *GitDirSuite) TestConflictRevert(c *gc.C) { source := newRepo(c) updated, err := source.Clone(c.MkDir()) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(updated.Path(), "some-dir"), []byte("hello"), 0644) c.Assert(err, gc.IsNil) err = updated.Snapshotf("potential conflict src") c.Assert(err, gc.IsNil) conflicted, err := updated.Conflicted() c.Assert(err, gc.IsNil) c.Assert(conflicted, jc.IsFalse) target := charm.NewGitDir(c.MkDir()) err = target.Init() c.Assert(err, gc.IsNil) err = target.Pull(source) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(target.Path(), "some-dir", "conflicting-file"), []byte("hello"), 0644) c.Assert(err, gc.IsNil) err = target.Snapshotf("potential conflict dst") c.Assert(err, gc.IsNil) conflicted, err = target.Conflicted() c.Assert(err, gc.IsNil) c.Assert(conflicted, jc.IsFalse) err = target.Pull(updated) c.Assert(err, gc.Equals, charm.ErrConflict) conflicted, err = target.Conflicted() c.Assert(err, gc.IsNil) c.Assert(conflicted, jc.IsTrue) dirty, err := target.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsTrue) err = target.Revert() c.Assert(err, gc.IsNil) conflicted, err = target.Conflicted() c.Assert(err, gc.IsNil) c.Assert(conflicted, jc.IsFalse) dirty, err = target.Dirty() c.Assert(err, gc.IsNil) c.Assert(dirty, jc.IsFalse) } func newRepo(c *gc.C) *charm.GitDir { repo := charm.NewGitDir(c.MkDir()) err := repo.Init() c.Assert(err, gc.IsNil) err = os.Mkdir(filepath.Join(repo.Path(), "some-dir"), 0755) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(repo.Path(), "some-file"), []byte("hello"), 0644) c.Assert(err, gc.IsNil) err = repo.AddAll() c.Assert(err, gc.IsNil) err = repo.Commitf("im in ur repo committin ur %s", "files") c.Assert(err, gc.IsNil) return repo } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/charm/git.go�����������������������������0000644�0000153�0000161�00000014410�12321735642�025710� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "fmt" "io" "io/ioutil" "os" "os/exec" "path/filepath" "strings" "launchpad.net/juju-core/charm" ) // GitDir exposes a specialized subset of git operations on a directory. type GitDir struct { path string } // NewGitDir creates a new GitDir at path. It does not touch the filesystem. func NewGitDir(path string) *GitDir { return &GitDir{path} } // Path returns the directory path. func (d *GitDir) Path() string { return d.path } // Exists returns true if the directory exists. func (d *GitDir) Exists() (bool, error) { fi, err := os.Stat(d.path) if err != nil { if os.IsNotExist(err) { return false, nil } return false, err } if fi.IsDir() { return true, nil } return false, fmt.Errorf("%q is not a directory", d.path) } // Init ensures that a git repository exists in the directory. func (d *GitDir) Init() error { if err := os.MkdirAll(d.path, 0755); err != nil { return err } commands := [][]string{ {"init"}, {"config", "user.email", "juju@localhost"}, {"config", "user.name", "juju"}, } for _, args := range commands { if err := d.cmd(args...); err != nil { return err } } return nil } // AddAll ensures that the next commit will reflect the current contents of // the directory. Empty directories will be preserved by inserting and tracking // empty files named .empty. func (d *GitDir) AddAll() error { walker := func(path string, fi os.FileInfo, err error) error { if err != nil { return err } if !fi.IsDir() { return nil } f, err := os.Open(path) if err != nil { return err } defer f.Close() if _, err := f.Readdir(1); err != nil { if err == io.EOF { empty := filepath.Join(path, ".empty") return ioutil.WriteFile(empty, nil, 0644) } return err } return nil } if err := filepath.Walk(d.path, walker); err != nil { return err } // special handling for addall, since there is an error condition that // we need to suppress return d.addAll() } // addAll runs "git add -A ."" and swallows errors about no matching files. This // is to replicate the behavior of older versions of git that returned no error // in that situation. func (d *GitDir) addAll() error { args := []string{"add", "-A", "."} cmd := exec.Command("git", args...) cmd.Dir = d.path if out, err := cmd.CombinedOutput(); err != nil { output := string(out) // Swallow this specific error. It's a change in behavior from older // versions of git, and we want AddAll to be able to be used on empty // directories. if !strings.Contains(output, "pathspec '.' did not match any files") { return d.logError(err, string(out), args...) } } return nil } // Commitf commits a new revision to the repository with the supplied message. func (d *GitDir) Commitf(format string, args ...interface{}) error { return d.cmd("commit", "--allow-empty", "-m", fmt.Sprintf(format, args...)) } // Snapshotf adds all changes made since the last commit, including deletions // and empty directories, and commits them using the supplied message. func (d *GitDir) Snapshotf(format string, args ...interface{}) error { if err := d.AddAll(); err != nil { return err } return d.Commitf(format, args...) } // Clone creates a new GitDir at the specified path, with history cloned // from the existing GitDir. It does not check out any files. func (d *GitDir) Clone(path string) (*GitDir, error) { if err := d.cmd("clone", "--no-checkout", ".", path); err != nil { return nil, err } return &GitDir{path}, nil } // Pull pulls from the supplied GitDir. func (d *GitDir) Pull(src *GitDir) error { err := d.cmd("pull", src.path) if err != nil { if conflicted, e := d.Conflicted(); e == nil && conflicted { return ErrConflict } } return err } // Dirty returns true if the directory contains any uncommitted local changes. func (d *GitDir) Dirty() (bool, error) { statuses, err := d.statuses() if err != nil { return false, err } return len(statuses) != 0, nil } // Conflicted returns true if the directory contains any conflicts. func (d *GitDir) Conflicted() (bool, error) { statuses, err := d.statuses() if err != nil { return false, err } for _, st := range statuses { switch st { case "AA", "DD", "UU", "AU", "UA", "DU", "UD": return true, nil } } return false, nil } // Revert removes unversioned files and reverts everything else to its state // as of the most recent commit. func (d *GitDir) Revert() error { if err := d.cmd("reset", "--hard", "ORIG_HEAD"); err != nil { return err } return d.cmd("clean", "-f", "-f", "-d") } // Log returns a highly compacted history of the directory. func (d *GitDir) Log() ([]string, error) { cmd := exec.Command("git", "--no-pager", "log", "--oneline") cmd.Dir = d.path out, err := cmd.Output() if err != nil { return nil, err } trim := strings.TrimRight(string(out), "\n") return strings.Split(trim, "\n"), nil } // cmd runs the specified command inside the directory. Errors will be logged // in detail. func (d *GitDir) cmd(args ...string) error { cmd := exec.Command("git", args...) cmd.Dir = d.path if out, err := cmd.CombinedOutput(); err != nil { return d.logError(err, string(out), args...) } return nil } func (d *GitDir) logError(err error, output string, args ...string) error { logger.Errorf("git command failed: %s\npath: %s\nargs: %#v\n%s", err, d.path, args, output) return fmt.Errorf("git %s failed: %s", args[0], err) } // statuses returns a list of XY-coded git statuses for the files in the directory. func (d *GitDir) statuses() ([]string, error) { cmd := exec.Command("git", "status", "--porcelain") cmd.Dir = d.path out, err := cmd.Output() if err != nil { return nil, fmt.Errorf("git status failed: %v", err) } statuses := []string{} for _, line := range strings.Split(string(out), "\n") { if line != "" { statuses = append(statuses, line[:2]) } } return statuses, nil } // ReadCharmURL reads the charm identity file from the GitDir. func (d *GitDir) ReadCharmURL() (*charm.URL, error) { path := filepath.Join(d.path, charmURLPath) return ReadCharmURL(path) } // WriteCharmURL writes the charm identity file into the GitDir. func (d *GitDir) WriteCharmURL(url *charm.URL) error { return WriteCharmURL(filepath.Join(d.path, charmURLPath), url) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/charm/charm.go���������������������������0000644�0000153�0000161�00000006543�12321735642�026227� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "errors" "net/url" "github.com/juju/loggo" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/set" ) var logger = loggo.GetLogger("juju.worker.uniter.charm") // charmURLPath is the path within a charm directory to which Deployers // commonly write the charm URL of the latest deployed charm. const charmURLPath = ".juju-charm" // Bundle allows access to a charm's files. type Bundle interface { // Manifest returns a set of slash-separated strings representing files, // directories, and symlinks stored in the bundle. Manifest() (set.Strings, error) // ExpandTo unpacks the entities referenced in the manifest into the // supplied directory. If it returns without error, every file referenced // in the charm must be present in the directory; implementations may vary // in the details of what they do with other files present. ExpandTo(dir string) error } // BundleInfo describes a Bundle. type BundleInfo interface { // URL returns the charm URL identifying the bundle. URL() *charm.URL // Archive URL returns the location of the bundle data. ArchiveURL() (*url.URL, utils.SSLHostnameVerification, error) // ArchiveSha256 returns the hex-encoded SHA-256 digest of the bundle data. ArchiveSha256() (string, error) } // BundleReader provides a mechanism for getting a Bundle from a BundleInfo. type BundleReader interface { // Read returns the bundle identified by the supplied info. The abort chan // can be used to notify an implementation that it need not complete the // operation, and can immediately error out if it is convenient to do so. Read(bi BundleInfo, abort <-chan struct{}) (Bundle, error) } // Deployer is responsible for installing and upgrading charms. type Deployer interface { // Stage must be called to prime the Deployer to install or upgrade the // bundle identified by the supplied info. The abort chan can be used to // notify an implementation that it need not complete the operation, and // can immediately error out if it convenient to do so. It must always // be safe to restage the same bundle, or to stage a new bundle. Stage(info BundleInfo, abort <-chan struct{}) error // Deploy will install or upgrade the most recently staged bundle. // Behaviour is undefined if Stage has not been called. Failures that // can be resolved by user intervention will be signalled by returning // ErrConflict. Deploy() error // NotifyRevert must be called when a conflicted deploy is abandoned, in // preparation for a new upgrade. NotifyRevert() error // NotifyResolved must be called when the cause of a deploy conflict has // been resolved, and a new deploy attempt will be made. NotifyResolved() error } // ErrConflict indicates that an upgrade failed and cannot be resolved // without human intervention. var ErrConflict = errors.New("charm upgrade has conflicts") // ReadCharmURL reads a charm identity file from the supplied path. func ReadCharmURL(path string) (*charm.URL, error) { surl := "" if err := utils.ReadYaml(path, &surl); err != nil { return nil, err } return charm.ParseURL(surl) } // WriteCharmURL writes a charm identity file into the supplied path. func WriteCharmURL(path string, url *charm.URL) error { return utils.WriteYaml(path, url.String()) } �������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/charm/git_deployer_test.go���������������0000644�0000153�0000161�00000015727�12321735642�030666� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "io/ioutil" "os" "path/filepath" gc "launchpad.net/gocheck" corecharm "launchpad.net/juju-core/charm" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/charm" ) type GitDeployerSuite struct { testing.GitSuite bundles *bundleReader targetPath string deployer charm.Deployer } var _ = gc.Suite(&GitDeployerSuite{}) func (s *GitDeployerSuite) SetUpTest(c *gc.C) { s.GitSuite.SetUpTest(c) s.bundles = &bundleReader{} s.targetPath = filepath.Join(c.MkDir(), "target") deployerPath := filepath.Join(c.MkDir(), "deployer") s.deployer = charm.NewGitDeployer(s.targetPath, deployerPath, s.bundles) } func (s *GitDeployerSuite) TestUnsetCharm(c *gc.C) { err := s.deployer.Deploy() c.Assert(err, gc.ErrorMatches, "charm deployment failed: no charm set") } func (s *GitDeployerSuite) TestInstall(c *gc.C) { // Prepare. info := s.bundles.AddCustomBundle(c, corecharm.MustParseURL("cs:s/c-1"), func(path string) { err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("hello"), 0644) c.Assert(err, gc.IsNil) }) err := s.deployer.Stage(info, nil) c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) // Install. err = s.deployer.Deploy() c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) // Check content. data, err := ioutil.ReadFile(filepath.Join(s.targetPath, "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "hello") target := charm.NewGitDir(s.targetPath) url, err := target.ReadCharmURL() c.Assert(err, gc.IsNil) c.Assert(url, gc.DeepEquals, corecharm.MustParseURL("cs:s/c-1")) lines, err := target.Log() c.Assert(err, gc.IsNil) c.Assert(lines, gc.HasLen, 2) c.Assert(lines[0], gc.Matches, `[0-9a-f]{7} Deployed charm "cs:s/c-1"\.`) c.Assert(lines[1], gc.Matches, `[0-9a-f]{7} Imported charm "cs:s/c-1"\.`) } func (s *GitDeployerSuite) TestUpgrade(c *gc.C) { // Install. info1 := s.bundles.AddCustomBundle(c, corecharm.MustParseURL("cs:s/c-1"), func(path string) { err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("hello"), 0644) c.Assert(err, gc.IsNil) err = os.Symlink("./some-file", filepath.Join(path, "a-symlink")) c.Assert(err, gc.IsNil) }) err := s.deployer.Stage(info1, nil) c.Assert(err, gc.IsNil) err = s.deployer.Deploy() c.Assert(err, gc.IsNil) // Upgrade. info2 := s.bundles.AddCustomBundle(c, corecharm.MustParseURL("cs:s/c-2"), func(path string) { err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("goodbye"), 0644) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(path, "a-symlink"), []byte("not any more!"), 0644) c.Assert(err, gc.IsNil) }) err = s.deployer.Stage(info2, nil) c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) err = s.deployer.Deploy() c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) // Check content. data, err := ioutil.ReadFile(filepath.Join(s.targetPath, "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "goodbye") data, err = ioutil.ReadFile(filepath.Join(s.targetPath, "a-symlink")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "not any more!") target := charm.NewGitDir(s.targetPath) url, err := target.ReadCharmURL() c.Assert(err, gc.IsNil) c.Assert(url, gc.DeepEquals, corecharm.MustParseURL("cs:s/c-2")) lines, err := target.Log() c.Assert(err, gc.IsNil) c.Assert(lines, gc.HasLen, 5) c.Assert(lines[0], gc.Matches, `[0-9a-f]{7} Upgraded charm to "cs:s/c-2".`) } func (s *GitDeployerSuite) TestConflictRevertResolve(c *gc.C) { // Install. info1 := s.bundles.AddCustomBundle(c, corecharm.MustParseURL("cs:s/c-1"), func(path string) { err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("hello"), 0644) c.Assert(err, gc.IsNil) }) err := s.deployer.Stage(info1, nil) c.Assert(err, gc.IsNil) err = s.deployer.Deploy() c.Assert(err, gc.IsNil) // Mess up target. err = ioutil.WriteFile(filepath.Join(s.targetPath, "some-file"), []byte("mu!"), 0644) c.Assert(err, gc.IsNil) // Upgrade. info2 := s.bundles.AddCustomBundle(c, corecharm.MustParseURL("cs:s/c-2"), func(path string) { err := ioutil.WriteFile(filepath.Join(path, "some-file"), []byte("goodbye"), 0644) c.Assert(err, gc.IsNil) }) err = s.deployer.Stage(info2, nil) c.Assert(err, gc.IsNil) err = s.deployer.Deploy() c.Assert(err, gc.Equals, charm.ErrConflict) checkCleanup(c, s.deployer) // Check state. target := charm.NewGitDir(s.targetPath) conflicted, err := target.Conflicted() c.Assert(err, gc.IsNil) c.Assert(conflicted, gc.Equals, true) // Revert and check initial content. err = s.deployer.NotifyRevert() c.Assert(err, gc.IsNil) data, err := ioutil.ReadFile(filepath.Join(s.targetPath, "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "mu!") conflicted, err = target.Conflicted() c.Assert(err, gc.IsNil) c.Assert(conflicted, gc.Equals, false) // Try to upgrade again. err = s.deployer.Deploy() c.Assert(err, gc.Equals, charm.ErrConflict) conflicted, err = target.Conflicted() c.Assert(err, gc.IsNil) c.Assert(conflicted, gc.Equals, true) checkCleanup(c, s.deployer) // And again. err = s.deployer.Deploy() c.Assert(err, gc.Equals, charm.ErrConflict) conflicted, err = target.Conflicted() c.Assert(err, gc.IsNil) c.Assert(conflicted, gc.Equals, true) checkCleanup(c, s.deployer) // Manually resolve, and commit. err = ioutil.WriteFile(filepath.Join(target.Path(), "some-file"), []byte("nu!"), 0644) c.Assert(err, gc.IsNil) err = s.deployer.NotifyResolved() c.Assert(err, gc.IsNil) conflicted, err = target.Conflicted() c.Assert(err, gc.IsNil) c.Assert(conflicted, gc.Equals, false) // Try a final upgrade to the same charm and check it doesn't write anything // except the upgrade log line. err = s.deployer.Deploy() c.Assert(err, gc.IsNil) checkCleanup(c, s.deployer) data, err = ioutil.ReadFile(filepath.Join(target.Path(), "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "nu!") conflicted, err = target.Conflicted() c.Assert(err, gc.IsNil) c.Assert(conflicted, gc.Equals, false) lines, err := target.Log() c.Assert(err, gc.IsNil) c.Assert(lines[0], gc.Matches, `[0-9a-f]{7} Upgraded charm to "cs:s/c-2".`) } func checkCleanup(c *gc.C, d charm.Deployer) { // Only one update dir should exist and be pointed to by the 'current' // symlink since extra ones should have been cleaned up by // cleanupOrphans. deployerPath := charm.GitDeployerDataPath(d) updateDirs, err := filepath.Glob(filepath.Join(deployerPath, "update-*")) c.Assert(err, gc.IsNil) c.Assert(updateDirs, gc.HasLen, 1) deployerCurrent := charm.GitDeployerCurrent(d) current, err := os.Readlink(deployerCurrent.Path()) c.Assert(err, gc.IsNil) c.Assert(updateDirs[0], gc.Equals, current) // No install dirs should be left behind since the one created is // renamed to the target path. installDirs, err := filepath.Glob(filepath.Join(deployerPath, "install-*")) c.Assert(err, gc.IsNil) c.Assert(installDirs, gc.HasLen, 0) } �����������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/charm/charm_test.go����������������������0000644�0000153�0000161�00000004424�12321735776�027272� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm_test import ( "fmt" "os" "path/filepath" stdtesting "testing" gc "launchpad.net/gocheck" corecharm "launchpad.net/juju-core/charm" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/worker/uniter/charm" ) func TestPackage(t *stdtesting.T) { // TODO(fwereade) 2014-03-21 not-worth-a-bug-number // rewrite BundlesDir tests to use the mocks below and not require an API // server and associated gubbins. coretesting.MgoTestPackage(t) } // bundleReader is a charm.BundleReader that lets us mock out the bundles we // deploy to test the Deployers. type bundleReader struct { bundles map[string]charm.Bundle } // Read implements the BundleReader interface. func (br *bundleReader) Read(info charm.BundleInfo, abort <-chan struct{}) (charm.Bundle, error) { bundle, ok := br.bundles[info.URL().String()] if !ok { return nil, fmt.Errorf("no such charm!") } return bundle, nil } func (br *bundleReader) AddCustomBundle(c *gc.C, url *corecharm.URL, customize func(path string)) charm.BundleInfo { base := c.MkDir() dirpath := coretesting.Charms.ClonedDirPath(base, "dummy") customize(dirpath) dir, err := corecharm.ReadDir(dirpath) c.Assert(err, gc.IsNil) err = dir.SetDiskRevision(url.Revision) c.Assert(err, gc.IsNil) bunpath := filepath.Join(base, "bundle") file, err := os.Create(bunpath) c.Assert(err, gc.IsNil) defer file.Close() err = dir.BundleTo(file) c.Assert(err, gc.IsNil) bundle, err := corecharm.ReadBundle(bunpath) c.Assert(err, gc.IsNil) return br.AddBundle(c, url, bundle) } func (br *bundleReader) AddBundle(c *gc.C, url *corecharm.URL, bundle charm.Bundle) charm.BundleInfo { if br.bundles == nil { br.bundles = map[string]charm.Bundle{} } br.bundles[url.String()] = bundle return &bundleInfo{nil, url} } type bundleInfo struct { charm.BundleInfo url *corecharm.URL } func (info *bundleInfo) URL() *corecharm.URL { return info.url } type mockBundle struct { paths set.Strings expand func(dir string) error } func (b mockBundle) Manifest() (set.Strings, error) { return set.NewStrings(b.paths.Values()...), nil } func (b mockBundle) ExpandTo(dir string) error { return b.expand(dir) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/charm/git_deployer.go��������������������0000644�0000153�0000161�00000014002�12321735642�027610� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "fmt" "io/ioutil" "os" "path/filepath" "time" ) const ( gitUpdatePrefix = "update-" gitInstallPrefix = "install-" gitCurrentPath = "current" ) // gitDeployer maintains a git repository tracking a series of charm versions, // and can install and upgrade charm deployments to the current version. type gitDeployer struct { target *GitDir dataPath string bundles BundleReader current *GitDir } // NewGitDeployer creates a new Deployer which stores its state in dataPath, // and installs or upgrades the charm at charmPath. func NewGitDeployer(charmPath, dataPath string, bundles BundleReader) Deployer { return &gitDeployer{ target: NewGitDir(charmPath), dataPath: dataPath, bundles: bundles, current: NewGitDir(filepath.Join(dataPath, gitCurrentPath)), } } func (d *gitDeployer) Stage(info BundleInfo, abort <-chan struct{}) error { // Make sure we've got an actual bundle available. bundle, err := d.bundles.Read(info, abort) if err != nil { return err } // Read present state of current. if err := os.MkdirAll(d.dataPath, 0755); err != nil { return err } defer collectGitOrphans(d.dataPath) srcExists, err := d.current.Exists() if err != nil { return err } url := info.URL() if srcExists { prevURL, err := d.current.ReadCharmURL() if err != nil { return err } if *url == *prevURL { return nil } } // Prepare a fresh repository for the update, using current's history // if it exists. updatePath, err := d.newDir(gitUpdatePrefix) if err != nil { return err } var repo *GitDir if srcExists { repo, err = d.current.Clone(updatePath) } else { repo = NewGitDir(updatePath) err = repo.Init() } if err != nil { return err } // Write the desired new state and commit. if err = bundle.ExpandTo(updatePath); err != nil { return err } if err = repo.WriteCharmURL(url); err != nil { return err } if err = repo.Snapshotf("Imported charm %q.", url); err != nil { return err } // Atomically rename fresh repository to current. tmplink := filepath.Join(updatePath, "tmplink") if err = os.Symlink(updatePath, tmplink); err != nil { return err } return os.Rename(tmplink, d.current.Path()) } func (d *gitDeployer) Deploy() (err error) { defer func() { if err == ErrConflict { logger.Warningf("charm deployment completed with conflicts") } else if err != nil { err = fmt.Errorf("charm deployment failed: %s", err) logger.Errorf("%v", err) } else { logger.Infof("charm deployment succeeded") } }() if exists, err := d.current.Exists(); err != nil { return err } else if !exists { return fmt.Errorf("no charm set") } if exists, err := d.target.Exists(); err != nil { return err } else if !exists { return d.install() } return d.upgrade() } func (d *gitDeployer) NotifyRevert() error { return d.target.Revert() } func (d *gitDeployer) NotifyResolved() error { return d.target.Snapshotf("Upgrade conflict resolved.") } // install creates a new deployment of current, and atomically moves it to // target. func (d *gitDeployer) install() error { defer collectGitOrphans(d.dataPath) logger.Infof("preparing new charm deployment") url, err := d.current.ReadCharmURL() if err != nil { return err } installPath, err := d.newDir(gitInstallPrefix) if err != nil { return err } repo := NewGitDir(installPath) if err = repo.Init(); err != nil { return err } if err = repo.Pull(d.current); err != nil { return err } if err = repo.Snapshotf("Deployed charm %q.", url); err != nil { return err } logger.Infof("deploying charm") return os.Rename(installPath, d.target.Path()) } // upgrade pulls from current into target. If target has local changes, but // no conflicts, it will be snapshotted before any changes are made. func (d *gitDeployer) upgrade() error { logger.Infof("preparing charm upgrade") url, err := d.current.ReadCharmURL() if err != nil { return err } if err := d.target.Init(); err != nil { return err } if dirty, err := d.target.Dirty(); err != nil { return err } else if dirty { if conflicted, err := d.target.Conflicted(); err != nil { return err } else if !conflicted { logger.Infof("snapshotting dirty charm before upgrade") if err = d.target.Snapshotf("Pre-upgrade snapshot."); err != nil { return err } } } logger.Infof("deploying charm") if err := d.target.Pull(d.current); err != nil { return err } return d.target.Snapshotf("Upgraded charm to %q.", url) } // collectGitOrphans deletes all repos in dataPath except the one pointed to by // a git deployer's "current" symlink. // Errors are generally ignored; some are logged. If current does not exist, *all* // repos are orphans, and all will be deleted; this should only be the case when // converting a gitDeployer to a manifestDeployer. func collectGitOrphans(dataPath string) { current, err := os.Readlink(filepath.Join(dataPath, gitCurrentPath)) if os.IsNotExist(err) { logger.Warningf("no current staging repo") } else if err != nil { logger.Warningf("cannot read current staging repo: %v", err) return } else if !filepath.IsAbs(current) { current = filepath.Join(dataPath, current) } orphans, err := filepath.Glob(filepath.Join(dataPath, fmt.Sprintf("%s*", gitUpdatePrefix))) if err != nil { return } installOrphans, err := filepath.Glob(filepath.Join(dataPath, fmt.Sprintf("%s*", gitInstallPrefix))) if err != nil { return } orphans = append(orphans, installOrphans...) for _, repoPath := range orphans { if repoPath != dataPath && repoPath != current { if err = os.RemoveAll(repoPath); err != nil { logger.Warningf("failed to remove orphan repo at %s: %s", repoPath, err) } } } } // newDir creates a new timestamped directory with the given prefix. It // assumes that the deployer will not need to create more than 10 // directories in any given second. func (d *gitDeployer) newDir(prefix string) (string, error) { return ioutil.TempDir(d.dataPath, prefix+time.Now().Format("20060102-150405")) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/charm/bundles.go�������������������������0000644�0000153�0000161�00000006327�12321735642�026571� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charm import ( "fmt" "os" "path" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/downloader" "launchpad.net/juju-core/utils" ) // BundlesDir is responsible for storing and retrieving charm bundles // identified by state charms. type BundlesDir struct { path string } // NewBundlesDir returns a new BundlesDir which uses path for storage. func NewBundlesDir(path string) *BundlesDir { return &BundlesDir{path} } // Read returns a charm bundle from the directory. If no bundle exists yet, // one will be downloaded and validated and copied into the directory before // being returned. Downloads will be aborted if a value is received on abort. func (d *BundlesDir) Read(info BundleInfo, abort <-chan struct{}) (Bundle, error) { path := d.bundlePath(info) if _, err := os.Stat(path); err != nil { if !os.IsNotExist(err) { return nil, err } else if err = d.download(info, abort); err != nil { return nil, err } } return charm.ReadBundle(path) } // download fetches the supplied charm and checks that it has the correct sha256 // hash, then copies it into the directory. If a value is received on abort, the // download will be stopped. func (d *BundlesDir) download(info BundleInfo, abort <-chan struct{}) (err error) { archiveURL, disableSSLHostnameVerification, err := info.ArchiveURL() if err != nil { return err } defer utils.ErrorContextf(&err, "failed to download charm %q from %q", info.URL(), archiveURL) dir := d.downloadsPath() if err := os.MkdirAll(dir, 0755); err != nil { return err } aurl := archiveURL.String() logger.Infof("downloading %s from %s", info.URL(), aurl) if disableSSLHostnameVerification { logger.Infof("SSL hostname verification disabled") } dl := downloader.New(aurl, dir, disableSSLHostnameVerification) defer dl.Stop() for { select { case <-abort: logger.Infof("download aborted") return fmt.Errorf("aborted") case st := <-dl.Done(): if st.Err != nil { return st.Err } logger.Infof("download complete") defer st.File.Close() actualSha256, _, err := utils.ReadSHA256(st.File) if err != nil { return err } archiveSha256, err := info.ArchiveSha256() if err != nil { return err } if actualSha256 != archiveSha256 { return fmt.Errorf( "expected sha256 %q, got %q", archiveSha256, actualSha256, ) } logger.Infof("download verified") if err := os.MkdirAll(d.path, 0755); err != nil { return err } return os.Rename(st.File.Name(), d.bundlePath(info)) } } } // bundlePath returns the path to the location where the verified charm // bundle identified by info will be, or has been, saved. func (d *BundlesDir) bundlePath(info BundleInfo) string { return d.bundleURLPath(info.URL()) } // bundleURLPath returns the path to the location where the verified charm // bundle identified by url will be, or has been, saved. func (d *BundlesDir) bundleURLPath(url *charm.URL) string { return path.Join(d.path, charm.Quote(url.String())) } // downloadsPath returns the path to the directory into which charms are // downloaded. func (d *BundlesDir) downloadsPath() string { return path.Join(d.path, "downloads") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/relationer_test.go�����������������������0000644�0000153�0000161�00000033577�12321735776�027265� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( "strconv" "strings" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/errors" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "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" "launchpad.net/juju-core/worker/uniter/relation" ) type RelationerSuite struct { jujutesting.JujuConnSuite hooks chan hook.Info svc *state.Service rel *state.Relation dir *relation.StateDir dirPath string st *api.State uniter *apiuniter.State apiRelUnit *apiuniter.RelationUnit } var _ = gc.Suite(&RelationerSuite{}) func (s *RelationerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) var err error s.svc = s.AddTestingService(c, "u", s.AddTestingCharm(c, "riak")) c.Assert(err, gc.IsNil) rels, err := s.svc.Relations() c.Assert(err, gc.IsNil) c.Assert(rels, gc.HasLen, 1) s.rel = rels[0] _, unit := s.AddRelationUnit(c, "u/0") s.dirPath = c.MkDir() s.dir, err = relation.ReadStateDir(s.dirPath, s.rel.Id()) c.Assert(err, gc.IsNil) s.hooks = make(chan hook.Info) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = unit.SetPassword(password) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAs(c, unit.Tag(), password) s.uniter = s.st.Uniter() c.Assert(s.uniter, gc.NotNil) apiUnit, err := s.uniter.Unit(unit.Tag()) c.Assert(err, gc.IsNil) apiRel, err := s.uniter.Relation(s.rel.Tag()) c.Assert(err, gc.IsNil) s.apiRelUnit, err = apiRel.Unit(apiUnit) c.Assert(err, gc.IsNil) } func (s *RelationerSuite) AddRelationUnit(c *gc.C, name string) (*state.RelationUnit, *state.Unit) { u, err := s.svc.AddUnit() c.Assert(err, gc.IsNil) c.Assert(u.Name(), gc.Equals, name) err = u.SetPrivateAddress(strings.Replace(name, "/", "-", 1) + ".testing.invalid") c.Assert(err, gc.IsNil) ru, err := s.rel.Unit(u) c.Assert(err, gc.IsNil) 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) // u/1 does not consider u/0 to be alive. w := ru1.Watch() defer stop(c, w) s.State.StartSync() ch, ok := <-w.Changes() c.Assert(ok, gc.Equals, true) c.Assert(ch.Changed, gc.HasLen, 0) c.Assert(ch.Departed, gc.HasLen, 0) // u/0 enters scope; u/1 observes it. err := r.Join() c.Assert(err, gc.IsNil) s.State.StartSync() select { case ch, ok := <-w.Changes(): c.Assert(ok, gc.Equals, true) c.Assert(ch.Changed, gc.HasLen, 1) _, found := ch.Changed["u/0"] c.Assert(found, gc.Equals, true) c.Assert(ch.Departed, gc.HasLen, 0) case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for presence detection") } // re-Join is no-op. err = r.Join() c.Assert(err, gc.IsNil) // TODO(jam): This would be a great to replace with statetesting.NotifyWatcherC s.State.StartSync() select { case ch, ok := <-w.Changes(): c.Fatalf("got unexpected change: %#v, %#v", ch, ok) case <-time.After(coretesting.ShortWait): } // u/0 leaves scope; u/1 observes it. hi := hook.Info{Kind: hooks.RelationBroken} _, err = r.PrepareHook(hi) c.Assert(err, gc.IsNil) err = r.CommitHook(hi) c.Assert(err, gc.IsNil) s.State.StartSync() select { case ch, ok := <-w.Changes(): c.Assert(ok, gc.Equals, true) c.Assert(ch.Changed, gc.HasLen, 0) c.Assert(ch.Departed, gc.DeepEquals, []string{"u/0"}) case <-time.After(worstCase): c.Fatalf("timed out waiting for absence detection") } } func (s *RelationerSuite) TestStartStopHooks(c *gc.C) { ru1, _ := s.AddRelationUnit(c, "u/1") ru2, _ := s.AddRelationUnit(c, "u/2") r := uniter.NewRelationer(s.apiRelUnit, s.dir, s.hooks) c.Assert(r.IsImplicit(), gc.Equals, false) err := r.Join() c.Assert(err, gc.IsNil) // Check no hooks are being sent. s.assertNoHook(c) // Start hooks, and check that still no changes are sent. r.StartHooks() defer stopHooks(c, r) s.assertNoHook(c) // Check we can't start hooks again. f := func() { r.StartHooks() } c.Assert(f, gc.PanicMatches, "hooks already started!") // Join u/1 to the relation, and check that we receive the expected hooks. settings := map[string]interface{}{"unit": "settings"} err = ru1.EnterScope(settings) c.Assert(err, gc.IsNil) s.assertHook(c, hook.Info{ Kind: hooks.RelationJoined, RemoteUnit: "u/1", }) s.assertHook(c, hook.Info{ Kind: hooks.RelationChanged, RemoteUnit: "u/1", }) s.assertNoHook(c) // Stop hooks, make more changes, check no events. err = r.StopHooks() c.Assert(err, gc.IsNil) err = ru1.LeaveScope() c.Assert(err, gc.IsNil) err = ru2.EnterScope(nil) c.Assert(err, gc.IsNil) node, err := ru2.Settings() c.Assert(err, gc.IsNil) node.Set("private-address", "roehampton") _, err = node.Write() c.Assert(err, gc.IsNil) s.assertNoHook(c) // Stop hooks again to verify safety. err = r.StopHooks() c.Assert(err, gc.IsNil) s.assertNoHook(c) // Start them again, and check we get the expected events sent. r.StartHooks() defer stopHooks(c, r) s.assertHook(c, hook.Info{ Kind: hooks.RelationDeparted, RemoteUnit: "u/1", }) s.assertHook(c, hook.Info{ Kind: hooks.RelationJoined, ChangeVersion: 1, RemoteUnit: "u/2", }) s.assertHook(c, hook.Info{ Kind: hooks.RelationChanged, ChangeVersion: 1, RemoteUnit: "u/2", }) s.assertNoHook(c) // Stop them again, just to be sure. err = r.StopHooks() c.Assert(err, gc.IsNil) s.assertNoHook(c) } func (s *RelationerSuite) TestPrepareCommitHooks(c *gc.C) { r := uniter.NewRelationer(s.apiRelUnit, s.dir, s.hooks) err := r.Join() c.Assert(err, gc.IsNil) ctx := r.Context() c.Assert(ctx.UnitNames(), gc.HasLen, 0) // Check preparing an invalid hook changes nothing. changed := hook.Info{ Kind: hooks.RelationChanged, RemoteUnit: "u/1", ChangeVersion: 7, } _, err = r.PrepareHook(changed) c.Assert(err, gc.ErrorMatches, `inappropriate "relation-changed" for "u/1": unit has not joined`) c.Assert(ctx.UnitNames(), gc.HasLen, 0) c.Assert(s.dir.State().Members, gc.HasLen, 0) // Check preparing a valid hook updates the context, but not persistent // relation state. joined := hook.Info{ Kind: hooks.RelationJoined, RemoteUnit: "u/1", } name, err := r.PrepareHook(joined) c.Assert(err, gc.IsNil) c.Assert(s.dir.State().Members, gc.HasLen, 0) c.Assert(name, gc.Equals, "ring-relation-joined") c.Assert(ctx.UnitNames(), gc.DeepEquals, []string{"u/1"}) // Check that preparing the following hook fails as before... _, err = r.PrepareHook(changed) c.Assert(err, gc.ErrorMatches, `inappropriate "relation-changed" for "u/1": unit has not joined`) c.Assert(s.dir.State().Members, gc.HasLen, 0) c.Assert(ctx.UnitNames(), gc.DeepEquals, []string{"u/1"}) // ...but that committing the previous hook updates the persistent // relation state... err = r.CommitHook(joined) c.Assert(err, gc.IsNil) c.Assert(s.dir.State().Members, gc.DeepEquals, map[string]int64{"u/1": 0}) c.Assert(ctx.UnitNames(), gc.DeepEquals, []string{"u/1"}) // ...and allows us to prepare the next hook... name, err = r.PrepareHook(changed) c.Assert(err, gc.IsNil) c.Assert(name, gc.Equals, "ring-relation-changed") c.Assert(s.dir.State().Members, gc.DeepEquals, map[string]int64{"u/1": 0}) c.Assert(ctx.UnitNames(), gc.DeepEquals, []string{"u/1"}) // ...and commit it. err = r.CommitHook(changed) c.Assert(err, gc.IsNil) c.Assert(s.dir.State().Members, gc.DeepEquals, map[string]int64{"u/1": 7}) c.Assert(ctx.UnitNames(), gc.DeepEquals, []string{"u/1"}) // To verify implied behaviour above, prepare a new joined hook with // missing membership information, and check relation context // membership is updated appropriately... joined.RemoteUnit = "u/2" joined.ChangeVersion = 3 name, err = r.PrepareHook(joined) c.Assert(err, gc.IsNil) c.Assert(s.dir.State().Members, gc.HasLen, 1) c.Assert(name, gc.Equals, "ring-relation-joined") c.Assert(ctx.UnitNames(), gc.DeepEquals, []string{"u/1", "u/2"}) // ...and so is relation state on commit. err = r.CommitHook(joined) c.Assert(err, gc.IsNil) c.Assert(s.dir.State().Members, gc.DeepEquals, map[string]int64{"u/1": 7, "u/2": 3}) c.Assert(ctx.UnitNames(), gc.DeepEquals, []string{"u/1", "u/2"}) } func (s *RelationerSuite) TestSetDying(c *gc.C) { ru1, _ := s.AddRelationUnit(c, "u/1") settings := map[string]interface{}{"unit": "settings"} err := ru1.EnterScope(settings) c.Assert(err, gc.IsNil) r := uniter.NewRelationer(s.apiRelUnit, s.dir, s.hooks) err = r.Join() c.Assert(err, gc.IsNil) r.StartHooks() defer stopHooks(c, r) s.assertHook(c, hook.Info{ Kind: hooks.RelationJoined, RemoteUnit: "u/1", }) // While a changed hook is still pending, the relation (or possibly the unit, // pending lifecycle work), changes Life to Dying, and the relationer is // informed. err = r.SetDying() c.Assert(err, gc.IsNil) // Check that we cannot rejoin the relation. f := func() { r.Join() } c.Assert(f, gc.PanicMatches, "dying relationer must not join!") // ...but the hook stream continues, sending the required changed hook for // u/1 before moving on to a departed, despite the fact that its pinger is // still running, and closing with a broken. s.assertHook(c, hook.Info{Kind: hooks.RelationChanged, RemoteUnit: "u/1"}) s.assertHook(c, hook.Info{Kind: hooks.RelationDeparted, RemoteUnit: "u/1"}) s.assertHook(c, hook.Info{Kind: hooks.RelationBroken}) // Check that the relation state has been broken. err = s.dir.State().Validate(hook.Info{Kind: hooks.RelationBroken}) c.Assert(err, gc.ErrorMatches, ".*: relation is broken and cannot be changed further") } func (s *RelationerSuite) assertNoHook(c *gc.C) { s.BackingState.StartSync() select { case hi, ok := <-s.hooks: c.Fatalf("got unexpected hook info %#v (%t)", hi, ok) case <-time.After(coretesting.ShortWait): } } func (s *RelationerSuite) assertHook(c *gc.C, expect hook.Info) { s.BackingState.StartSync() // We must ensure the local state dir exists first. c.Assert(s.dir.Ensure(), gc.IsNil) select { case hi, ok := <-s.hooks: c.Assert(ok, gc.Equals, true) expect.ChangeVersion = hi.ChangeVersion c.Assert(hi, gc.DeepEquals, expect) c.Assert(s.dir.Write(hi), gc.Equals, nil) case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for %#v", expect) } } type stopper interface { Stop() error } func stop(c *gc.C, s stopper) { c.Assert(s.Stop(), gc.IsNil) } func stopHooks(c *gc.C, r *uniter.Relationer) { c.Assert(r.StopHooks(), gc.IsNil) } type RelationerImplicitSuite struct { jujutesting.JujuConnSuite } var _ = gc.Suite(&RelationerImplicitSuite{}) func (s *RelationerImplicitSuite) TestImplicitRelationer(c *gc.C) { // Create a relationer for an implicit endpoint (mysql:juju-info). mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) u, err := mysql.AddUnit() c.Assert(err, gc.IsNil) err = u.SetPrivateAddress("blah") c.Assert(err, gc.IsNil) logging := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"logging", "mysql"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) relsDir := c.MkDir() dir, err := relation.ReadStateDir(relsDir, rel.Id()) c.Assert(err, gc.IsNil) hooks := make(chan hook.Info) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = u.SetPassword(password) c.Assert(err, gc.IsNil) st := s.OpenAPIAs(c, u.Tag(), password) uniterState := st.Uniter() c.Assert(uniterState, gc.NotNil) apiUnit, err := uniterState.Unit(u.Tag()) c.Assert(err, gc.IsNil) apiRel, err := uniterState.Relation(rel.Tag()) c.Assert(err, gc.IsNil) apiRelUnit, err := apiRel.Unit(apiUnit) c.Assert(err, gc.IsNil) r := uniter.NewRelationer(apiRelUnit, dir, hooks) c.Assert(r, jc.Satisfies, (*uniter.Relationer).IsImplicit) // Join the relation. err = r.Join() c.Assert(err, gc.IsNil) sub, err := logging.Unit("logging/0") c.Assert(err, gc.IsNil) err = sub.SetPrivateAddress("blah") c.Assert(err, gc.IsNil) // Join the other side; check no hooks are sent. r.StartHooks() defer func() { c.Assert(r.StopHooks(), gc.IsNil) }() subru, err := rel.Unit(sub) c.Assert(err, gc.IsNil) err = subru.EnterScope(map[string]interface{}{"some": "data"}) c.Assert(err, gc.IsNil) s.State.StartSync() select { case <-time.After(coretesting.ShortWait): case <-hooks: c.Fatalf("unexpected hook generated") } // Set it to Dying; check that the dir is removed immediately. err = r.SetDying() c.Assert(err, gc.IsNil) 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. err = subru.LeaveScope() c.Assert(err, gc.IsNil) err = rel.Destroy() c.Assert(err, gc.IsNil) err = rel.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // Verify that no other hooks were sent at any stage. select { case <-hooks: c.Fatalf("unexpected hook generated") default: } } ���������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/relationer.go����������������������������0000644�0000153�0000161�00000010546�12321735642�026205� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "fmt" "launchpad.net/juju-core/charm/hooks" apiuniter "launchpad.net/juju-core/state/api/uniter" "launchpad.net/juju-core/worker/uniter/hook" "launchpad.net/juju-core/worker/uniter/relation" ) // Relationer manages a unit's presence in a relation. type Relationer struct { ctx *ContextRelation ru *apiuniter.RelationUnit dir *relation.StateDir queue relation.HookQueue hooks chan<- hook.Info dying bool } // NewRelationer creates a new Relationer. The unit will not join the // relation until explicitly requested. func NewRelationer(ru *apiuniter.RelationUnit, dir *relation.StateDir, hooks chan<- hook.Info) *Relationer { return &Relationer{ ctx: NewContextRelation(ru, dir.State().Members), ru: ru, dir: dir, hooks: hooks, } } // Context returns the ContextRelation associated with r. func (r *Relationer) Context() *ContextRelation { return r.ctx } // IsImplicit returns whether the local relation endpoint is implicit. Implicit // relations do not run hooks. func (r *Relationer) IsImplicit() bool { return r.ru.Endpoint().IsImplicit() } // Join initializes local state and causes the unit to enter its relation // scope, allowing its counterpart units to detect its presence and settings // changes. Local state directory is not created until needed. func (r *Relationer) Join() error { 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() } // SetDying informs the relationer that the unit is departing the relation, // and that the only hooks it should send henceforth are -departed hooks, // until the relation is empty, followed by a -broken hook. func (r *Relationer) SetDying() error { if r.IsImplicit() { r.dying = true return r.die() } if r.queue != nil { if err := r.StopHooks(); err != nil { return err } defer r.StartHooks() } r.dying = true return nil } // die is run when the relationer has no further responsibilities; it leaves // relation scope, and removes the local relation state directory. func (r *Relationer) die() error { if err := r.ru.LeaveScope(); err != nil { return err } return r.dir.Remove() } // StartHooks starts watching the relation, and sending hook.Info events on the // hooks channel. It will panic if called when already responding to relation // changes. func (r *Relationer) StartHooks() error { if r.IsImplicit() { return nil } if r.queue != nil { panic("hooks already started!") } if r.dying { r.queue = relation.NewDyingHookQueue(r.dir.State(), r.hooks) } else { w, err := r.ru.Watch() if err != nil { return err } r.queue = relation.NewAliveHookQueue(r.dir.State(), r.hooks, w) } return nil } // StopHooks ensures that the relationer is not watching the relation, or sending // hook.Info events on the hooks channel. func (r *Relationer) StopHooks() error { if r.queue == nil { return nil } queue := r.queue r.queue = nil return queue.Stop() } // PrepareHook checks that the relation is in a state such that it makes // sense to execute the supplied hook, and ensures that the relation context // contains the latest relation state as communicated in the hook.Info. It // returns the name of the hook that must be run. func (r *Relationer) PrepareHook(hi hook.Info) (hookName string, err error) { if r.IsImplicit() { panic("implicit relations must not run hooks") } if err = r.dir.State().Validate(hi); err != nil { return } if hi.Kind == hooks.RelationDeparted { r.ctx.DeleteMember(hi.RemoteUnit) } else if hi.RemoteUnit != "" { r.ctx.UpdateMembers(SettingsMap{hi.RemoteUnit: nil}) } name := r.ru.Endpoint().Name return fmt.Sprintf("%s-%s", name, hi.Kind), nil } // CommitHook persists the fact of the supplied hook's completion. func (r *Relationer) CommitHook(hi hook.Info) error { if r.IsImplicit() { panic("implicit relations must not run hooks") } if hi.Kind == hooks.RelationBroken { return r.die() } return r.dir.Write(hi) } ����������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/tools_test.go����������������������������0000644�0000153�0000161�00000003625�12321735642�026240� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( "io/ioutil" "os" "path/filepath" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent/tools" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker/uniter" "launchpad.net/juju-core/worker/uniter/jujuc" ) type ToolsSuite struct { dataDir, toolsDir string } var _ = gc.Suite(&ToolsSuite{}) func (s *ToolsSuite) SetUpTest(c *gc.C) { s.dataDir = c.MkDir() s.toolsDir = tools.SharedToolsDir(s.dataDir, version.Current) err := os.MkdirAll(s.toolsDir, 0755) c.Assert(err, gc.IsNil) err = os.Symlink(s.toolsDir, tools.ToolsDir(s.dataDir, "unit-u-123")) c.Assert(err, gc.IsNil) } func (s *ToolsSuite) TestEnsureJujucSymlinks(c *gc.C) { jujudPath := filepath.Join(s.toolsDir, "jujud") err := ioutil.WriteFile(jujudPath, []byte("assume sane"), 0755) c.Assert(err, gc.IsNil) assertLink := func(path string) time.Time { target, err := os.Readlink(path) c.Assert(err, gc.IsNil) c.Assert(target, gc.Equals, "./jujud") fi, err := os.Lstat(path) c.Assert(err, gc.IsNil) return fi.ModTime() } // Check that EnsureJujucSymlinks writes appropriate symlinks. err = uniter.EnsureJujucSymlinks(s.toolsDir) c.Assert(err, gc.IsNil) mtimes := map[string]time.Time{} for _, name := range jujuc.CommandNames() { tool := filepath.Join(s.toolsDir, name) mtimes[tool] = assertLink(tool) } // Check that EnsureJujucSymlinks doesn't overwrite things that don't need to be. err = uniter.EnsureJujucSymlinks(s.toolsDir) c.Assert(err, gc.IsNil) for tool, mtime := range mtimes { c.Assert(assertLink(tool), gc.Equals, mtime) } } func (s *ToolsSuite) TestEnsureJujucSymlinksBadDir(c *gc.C) { err := uniter.EnsureJujucSymlinks(filepath.Join(c.MkDir(), "noexist")) c.Assert(err, gc.ErrorMatches, "cannot initialize hook commands in .*: no such file or directory") } �����������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/state.go���������������������������������0000644�0000153�0000161�00000007534�12321735642�025164� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "errors" "fmt" "os" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/utils" uhook "launchpad.net/juju-core/worker/uniter/hook" ) // Op enumerates the operations the uniter can perform. type Op string const ( // Install indicates that the uniter is installing the charm. Install Op = "install" // RunHook indicates that the uniter is running a hook. RunHook Op = "run-hook" // Upgrade indicates that the uniter is upgrading the charm. Upgrade Op = "upgrade" // Continue indicates that the uniter should run ModeContinue // to determine the next operation. Continue Op = "continue" ) // OpStep describes the recorded progression of an operation. type OpStep string const ( // Queued indicates that the uniter should undertake the operation // as soon as possible. Queued OpStep = "queued" // Pending indicates that the uniter has started, but not completed, // the operation. Pending OpStep = "pending" // Done indicates that the uniter has completed the operation, // but may not yet have synchronized all necessary secondary state. Done OpStep = "done" ) // State defines the local persistent state of the uniter, excluding relation // state. type State struct { // Started indicates whether the start hook has run. Started bool // Op indicates the current operation. Op Op // OpStep indicates the current operation's progression. OpStep OpStep // Hook holds hook information relevant to the current operation. If Op // is Continue, it holds the last hook that was executed; if Op is RunHook, // it holds the running hook; if Op is Upgrade, a non-nil hook indicates // that the uniter should return to that hook's Pending state after the // upgrade is complete (instead of running an upgrade-charm hook). Hook *uhook.Info `yaml:"hook,omitempty"` // Charm describes the charm being deployed by an Install or Upgrade // operation, and is otherwise blank. CharmURL *charm.URL `yaml:"charm,omitempty"` } // validate returns an error if the state violates expectations. func (st State) validate() (err error) { defer utils.ErrorContextf(&err, "invalid uniter state") hasHook := st.Hook != nil hasCharm := st.CharmURL != nil switch st.Op { case Install: if hasHook { return fmt.Errorf("unexpected hook info") } fallthrough case Upgrade: if !hasCharm { return fmt.Errorf("missing charm URL") } case Continue, RunHook: if !hasHook { return fmt.Errorf("missing hook info") } else if hasCharm { return fmt.Errorf("unexpected charm URL") } default: return fmt.Errorf("unknown operation %q", st.Op) } switch st.OpStep { case Queued, Pending, Done: default: return fmt.Errorf("unknown operation step %q", st.OpStep) } if hasHook { return st.Hook.Validate() } return nil } // StateFile holds the disk state for a uniter. type StateFile struct { path string } // NewStateFile returns a new StateFile using path. func NewStateFile(path string) *StateFile { return &StateFile{path} } var ErrNoStateFile = errors.New("uniter state file does not exist") // Read reads a State from the file. If the file does not exist it returns // ErrNoStateFile. func (f *StateFile) Read() (*State, error) { var st State if err := utils.ReadYaml(f.path, &st); err != nil { if os.IsNotExist(err) { return nil, ErrNoStateFile } } if err := st.validate(); err != nil { return nil, fmt.Errorf("cannot read charm state at %q: %v", f.path, err) } return &st, nil } // Write stores the supplied state to the file. func (f *StateFile) Write(started bool, op Op, step OpStep, hi *uhook.Info, url *charm.URL) error { st := &State{ Started: started, Op: op, OpStep: step, Hook: hi, CharmURL: url, } if err := st.validate(); err != nil { panic(err) } return utils.WriteYaml(f.path, st) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/tools.go���������������������������������0000644�0000153�0000161�00000001651�12321735642�025176� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "fmt" "os" "path/filepath" "launchpad.net/juju-core/worker/uniter/jujuc" ) // EnsureJujucSymlinks creates a symbolic link to jujuc within dir for each // hook command. If the commands already exist, this operation does nothing. func EnsureJujucSymlinks(dir string) (err error) { for _, name := range jujuc.CommandNames() { // The link operation fails when the target already exists, // so this is a no-op when the command names already // exist. err := os.Symlink("./jujud", filepath.Join(dir, name)) if err == nil { continue } // TODO(rog) drop LinkError check when fix is released (see http://codereview.appspot.com/6442080/) if e, ok := err.(*os.LinkError); !ok || !os.IsExist(e.Err) { return fmt.Errorf("cannot initialize hook commands in %q: %v", dir, err) } } return nil } ���������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/runlistener_test.go����������������������0000644�0000153�0000161�00000003547�12321735642�027455� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( "net/rpc" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/exec" "launchpad.net/juju-core/worker/uniter" ) type ListenerSuite struct { testbase.LoggingSuite socketPath string } var _ = gc.Suite(&ListenerSuite{}) // Mirror the params to uniter.NewRunListener, but add cleanup to close it. func (s *ListenerSuite) NewRunListener(c *gc.C) *uniter.RunListener { s.socketPath = filepath.Join(c.MkDir(), "test.listener") listener, err := uniter.NewRunListener(&mockRunner{c}, s.socketPath) c.Assert(err, gc.IsNil) c.Assert(listener, gc.NotNil) s.AddCleanup(func(*gc.C) { listener.Close() }) return listener } func (s *ListenerSuite) TestNewRunListenerOnExistingSocketRemovesItAndSucceeds(c *gc.C) { s.NewRunListener(c) listener, err := uniter.NewRunListener(&mockRunner{}, s.socketPath) c.Assert(err, gc.IsNil) c.Assert(listener, gc.NotNil) listener.Close() } func (s *ListenerSuite) TestClientCall(c *gc.C) { s.NewRunListener(c) client, err := rpc.Dial("unix", s.socketPath) c.Assert(err, gc.IsNil) defer client.Close() var result exec.ExecResponse err = client.Call(uniter.JujuRunEndpoint, "some-command", &result) c.Assert(err, gc.IsNil) c.Assert(string(result.Stdout), gc.Equals, "some-command stdout") c.Assert(string(result.Stderr), gc.Equals, "some-command stderr") c.Assert(result.Code, gc.Equals, 42) } type mockRunner struct { c *gc.C } var _ uniter.CommandRunner = (*mockRunner)(nil) func (r *mockRunner) RunCommands(commands string) (results *exec.ExecResponse, err error) { r.c.Log("mock runner: " + commands) return &exec.ExecResponse{ Code: 42, Stdout: []byte(commands + " stdout"), Stderr: []byte(commands + " stderr"), }, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/uniter_test.go���������������������������0000644�0000153�0000161�00000156071�12321735776�026422� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( "bytes" "fmt" "io/ioutil" "net/rpc" "net/url" "os" "os/exec" "path/filepath" "strconv" "strings" stdtesting "testing" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/agent/tools" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "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" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/uniter" ) // worstCase is used for timeouts when timing out // will fail the test. Raising this value should // not affect the overall running time of the tests // unless they fail. const worstCase = coretesting.LongWait func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type UniterSuite struct { coretesting.GitSuite testing.JujuConnSuite coretesting.HTTPSuite dataDir string oldLcAll string unitDir string st *api.State uniter *apiuniter.State } var _ = gc.Suite(&UniterSuite{}) func (s *UniterSuite) SetUpSuite(c *gc.C) { s.JujuConnSuite.SetUpSuite(c) s.HTTPSuite.SetUpSuite(c) s.dataDir = c.MkDir() toolsDir := tools.ToolsDir(s.dataDir, "unit-u-0") err := os.MkdirAll(toolsDir, 0755) c.Assert(err, gc.IsNil) cmd := exec.Command("go", "build", "launchpad.net/juju-core/cmd/jujud") cmd.Dir = toolsDir out, err := cmd.CombinedOutput() c.Logf(string(out)) c.Assert(err, gc.IsNil) s.oldLcAll = os.Getenv("LC_ALL") os.Setenv("LC_ALL", "en_US") s.unitDir = filepath.Join(s.dataDir, "agents", "unit-u-0") } func (s *UniterSuite) TearDownSuite(c *gc.C) { os.Setenv("LC_ALL", s.oldLcAll) s.HTTPSuite.TearDownSuite(c) s.JujuConnSuite.TearDownSuite(c) } func (s *UniterSuite) SetUpTest(c *gc.C) { s.GitSuite.SetUpTest(c) s.JujuConnSuite.SetUpTest(c) s.HTTPSuite.SetUpTest(c) } func (s *UniterSuite) TearDownTest(c *gc.C) { s.ResetContext(c) s.HTTPSuite.TearDownTest(c) s.JujuConnSuite.TearDownTest(c) s.GitSuite.TearDownTest(c) } func (s *UniterSuite) Reset(c *gc.C) { s.JujuConnSuite.Reset(c) s.ResetContext(c) } func (s *UniterSuite) ResetContext(c *gc.C) { coretesting.Server.Flush() err := os.RemoveAll(s.unitDir) c.Assert(err, gc.IsNil) } func (s *UniterSuite) APILogin(c *gc.C, unit *state.Unit) { password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = unit.SetPassword(password) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAs(c, unit.Tag(), password) c.Assert(s.st, gc.NotNil) c.Logf("API: login as %q successful", unit.Tag()) s.uniter = s.st.Uniter() c.Assert(s.uniter, gc.NotNil) } var _ worker.Worker = (*uniter.Uniter)(nil) type uniterTest struct { summary string steps []stepper } func ut(summary string, steps ...stepper) uniterTest { return uniterTest{summary, steps} } type stepper interface { step(c *gc.C, ctx *context) } type context struct { uuid string path string dataDir string s *UniterSuite st *state.State charms coretesting.ResponseMap hooks []string sch *state.Charm svc *state.Service unit *state.Unit uniter *uniter.Uniter relatedSvc *state.Service relation *state.Relation relationUnits map[string]*state.RelationUnit subordinate *state.Unit hooksCompleted []string } var _ uniter.UniterExecutionObserver = (*context)(nil) func (ctx *context) HookCompleted(hookName string) { ctx.hooksCompleted = append(ctx.hooksCompleted, hookName) } func (ctx *context) HookFailed(hookName string) { ctx.hooksCompleted = append(ctx.hooksCompleted, "fail-"+hookName) } func (ctx *context) run(c *gc.C, steps []stepper) { defer func() { if ctx.uniter != nil { err := ctx.uniter.Stop() c.Assert(err, gc.IsNil) } }() for i, s := range steps { c.Logf("step %d:\n", i) step(c, ctx, s) } } var goodHook = ` #!/bin/bash --norc juju-log $JUJU_ENV_UUID %s $JUJU_REMOTE_UNIT `[1:] var badHook = ` #!/bin/bash --norc juju-log $JUJU_ENV_UUID fail-%s $JUJU_REMOTE_UNIT exit 1 `[1:] func (ctx *context) writeHook(c *gc.C, path string, good bool) { hook := badHook if good { hook = goodHook } content := fmt.Sprintf(hook, filepath.Base(path)) err := ioutil.WriteFile(path, []byte(content), 0755) c.Assert(err, gc.IsNil) } func (ctx *context) matchHooks(c *gc.C) (match bool, overshoot bool) { c.Logf("ctx.hooksCompleted: %#v", ctx.hooksCompleted) if len(ctx.hooksCompleted) < len(ctx.hooks) { return false, false } for i, e := range ctx.hooks { if ctx.hooksCompleted[i] != e { return false, false } } return true, len(ctx.hooksCompleted) > len(ctx.hooks) } var startupTests = []uniterTest{ // Check conditions that can cause the uniter to fail to start. ut( "unable to create state dir", writeFile{"state", 0644}, createCharm{}, createServiceAndUnit{}, startUniter{}, waitUniterDead{`failed to initialize uniter for "unit-u-0": .*not a directory`}, ), ut( "unknown unit", // We still need to create a unit, because that's when we also // connect to the API, but here we use a different service // (and hence unit) name. createCharm{}, createServiceAndUnit{serviceName: "w"}, startUniter{"unit-u-0"}, waitUniterDead{`failed to initialize uniter for "unit-u-0": permission denied`}, ), } func (s *UniterSuite) TestUniterStartup(c *gc.C) { s.runUniterTests(c, startupTests) } var bootstrapTests = []uniterTest{ // Check error conditions during unit bootstrap phase. ut( "insane deployment", createCharm{}, serveCharm{}, writeFile{"charm", 0644}, createUniter{}, waitUniterDead{`ModeInstalling cs:quantal/wordpress-0: charm deployment failed: ".*charm" is not a directory`}, ), ut( "charm cannot be downloaded", createCharm{}, custom{func(c *gc.C, ctx *context) { coretesting.Server.Response(404, nil, nil) }}, createUniter{}, waitUniterDead{`ModeInstalling cs:quantal/wordpress-0: failed to download charm .* 404 Not Found`}, ), } func (s *UniterSuite) TestUniterBootstrap(c *gc.C) { s.runUniterTests(c, bootstrapTests) } var installHookTests = []uniterTest{ ut( "install hook fail and resolve", startupError{"install"}, verifyWaiting{}, resolveError{state.ResolvedNoHooks}, waitUnit{ status: params.StatusStarted, }, waitHooks{"config-changed", "start"}, ), ut( "install hook fail and retry", startupError{"install"}, verifyWaiting{}, resolveError{state.ResolvedRetryHooks}, waitUnit{ status: params.StatusError, info: `hook failed: "install"`, data: params.StatusData{ "hook": "install", }, }, waitHooks{"fail-install"}, fixHook{"install"}, verifyWaiting{}, resolveError{state.ResolvedRetryHooks}, waitUnit{ status: params.StatusStarted, }, waitHooks{"install", "config-changed", "start"}, ), } func (s *UniterSuite) TestUniterInstallHook(c *gc.C) { s.runUniterTests(c, installHookTests) } var startHookTests = []uniterTest{ ut( "start hook fail and resolve", startupError{"start"}, verifyWaiting{}, resolveError{state.ResolvedNoHooks}, waitUnit{ status: params.StatusStarted, }, waitHooks{"config-changed"}, verifyRunning{}, ), ut( "start hook fail and retry", startupError{"start"}, verifyWaiting{}, resolveError{state.ResolvedRetryHooks}, waitUnit{ status: params.StatusError, info: `hook failed: "start"`, data: params.StatusData{ "hook": "start", }, }, waitHooks{"fail-start"}, verifyWaiting{}, fixHook{"start"}, resolveError{state.ResolvedRetryHooks}, waitUnit{ status: params.StatusStarted, }, waitHooks{"start", "config-changed"}, verifyRunning{}, ), } func (s *UniterSuite) TestUniterStartHook(c *gc.C) { s.runUniterTests(c, startHookTests) } var multipleErrorsTests = []uniterTest{ ut( "resolved is cleared before moving on to next hook", createCharm{badHooks: []string{"install", "config-changed", "start"}}, serveCharm{}, createUniter{}, waitUnit{ status: params.StatusError, info: `hook failed: "install"`, data: params.StatusData{ "hook": "install", }, }, resolveError{state.ResolvedNoHooks}, waitUnit{ status: params.StatusError, info: `hook failed: "config-changed"`, data: params.StatusData{ "hook": "config-changed", }, }, resolveError{state.ResolvedNoHooks}, waitUnit{ status: params.StatusError, info: `hook failed: "start"`, data: params.StatusData{ "hook": "start", }, }, ), } func (s *UniterSuite) TestUniterMultipleErrors(c *gc.C) { s.runUniterTests(c, multipleErrorsTests) } var configChangedHookTests = []uniterTest{ ut( "config-changed hook fail and resolve", startupError{"config-changed"}, verifyWaiting{}, // Note: we'll run another config-changed as soon as we hit the // started state, so the broken hook would actually prevent us // from advancing at all if we didn't fix it. fixHook{"config-changed"}, resolveError{state.ResolvedNoHooks}, waitUnit{ status: params.StatusStarted, }, waitHooks{"start", "config-changed"}, // If we'd accidentally retried that hook, somehow, we would get // an extra config-changed as we entered started; see that we don't. waitHooks{}, verifyRunning{}, ), ut( "config-changed hook fail and retry", startupError{"config-changed"}, verifyWaiting{}, resolveError{state.ResolvedRetryHooks}, waitUnit{ status: params.StatusError, info: `hook failed: "config-changed"`, data: params.StatusData{ "hook": "config-changed", }, }, waitHooks{"fail-config-changed"}, verifyWaiting{}, fixHook{"config-changed"}, resolveError{state.ResolvedRetryHooks}, waitUnit{ status: params.StatusStarted, }, waitHooks{"config-changed", "start"}, verifyRunning{}, ), ut( "steady state config change with config-get verification", createCharm{ customize: func(c *gc.C, ctx *context, path string) { appendHook(c, path, "config-changed", "config-get --format yaml --output config.out") }, }, serveCharm{}, createUniter{}, waitUnit{ status: params.StatusStarted, }, waitHooks{"install", "config-changed", "start"}, assertYaml{"charm/config.out", map[string]interface{}{ "blog-title": "My Title", }}, changeConfig{"blog-title": "Goodness Gracious Me"}, waitHooks{"config-changed"}, verifyRunning{}, assertYaml{"charm/config.out", map[string]interface{}{ "blog-title": "Goodness Gracious Me", }}, )} func (s *UniterSuite) TestUniterConfigChangedHook(c *gc.C) { s.runUniterTests(c, configChangedHookTests) } var hookSynchronizationTests = []uniterTest{ ut( "verify config change hook not run while lock held", quickStart{}, acquireHookSyncLock{}, changeConfig{"blog-title": "Goodness Gracious Me"}, waitHooks{}, releaseHookSyncLock, waitHooks{"config-changed"}, ), ut( "verify held lock by this unit is broken", acquireHookSyncLock{"u/0:fake"}, quickStart{}, verifyHookSyncLockUnlocked, ), ut( "verify held lock by another unit is not broken", acquireHookSyncLock{"u/1:fake"}, // Can't use quickstart as it has a built in waitHooks. createCharm{}, serveCharm{}, ensureStateWorker{}, createServiceAndUnit{}, startUniter{}, waitAddresses{}, waitHooks{}, verifyHookSyncLockLocked, releaseHookSyncLock, waitUnit{status: params.StatusStarted}, waitHooks{"install", "config-changed", "start"}, ), } func (s *UniterSuite) TestUniterHookSynchronisation(c *gc.C) { s.runUniterTests(c, hookSynchronizationTests) } var dyingReactionTests = []uniterTest{ // Reaction to entity deaths. ut( "steady state service dying", quickStart{}, serviceDying, waitHooks{"stop"}, waitUniterDead{}, ), ut( "steady state unit dying", quickStart{}, unitDying, waitHooks{"stop"}, waitUniterDead{}, ), ut( "steady state unit dead", quickStart{}, unitDead, waitUniterDead{}, waitHooks{}, ), ut( "hook error service dying", startupError{"start"}, serviceDying, verifyWaiting{}, fixHook{"start"}, resolveError{state.ResolvedRetryHooks}, waitHooks{"start", "config-changed", "stop"}, waitUniterDead{}, ), ut( "hook error unit dying", startupError{"start"}, unitDying, verifyWaiting{}, fixHook{"start"}, resolveError{state.ResolvedRetryHooks}, waitHooks{"start", "config-changed", "stop"}, waitUniterDead{}, ), ut( "hook error unit dead", startupError{"start"}, unitDead, waitUniterDead{}, waitHooks{}, ), } func (s *UniterSuite) TestUniterDyingReaction(c *gc.C) { s.runUniterTests(c, dyingReactionTests) } var steadyUpgradeTests = []uniterTest{ // Upgrade scenarios from steady state. ut( "steady state upgrade", quickStart{}, createCharm{revision: 1}, upgradeCharm{revision: 1}, waitUnit{ status: params.StatusStarted, charm: 1, }, waitHooks{"upgrade-charm", "config-changed"}, verifyCharm{revision: 1}, verifyRunning{}, ), ut( "steady state forced upgrade (identical behaviour)", quickStart{}, createCharm{revision: 1}, upgradeCharm{revision: 1, forced: true}, waitUnit{ status: params.StatusStarted, charm: 1, }, waitHooks{"upgrade-charm", "config-changed"}, verifyCharm{revision: 1}, verifyRunning{}, ), ut( "steady state upgrade hook fail and resolve", quickStart{}, createCharm{revision: 1, badHooks: []string{"upgrade-charm"}}, upgradeCharm{revision: 1}, waitUnit{ status: params.StatusError, info: `hook failed: "upgrade-charm"`, data: params.StatusData{ "hook": "upgrade-charm", }, charm: 1, }, waitHooks{"fail-upgrade-charm"}, verifyCharm{revision: 1}, verifyWaiting{}, resolveError{state.ResolvedNoHooks}, waitUnit{ status: params.StatusStarted, charm: 1, }, waitHooks{"config-changed"}, verifyRunning{}, ), ut( "steady state upgrade hook fail and retry", quickStart{}, createCharm{revision: 1, badHooks: []string{"upgrade-charm"}}, upgradeCharm{revision: 1}, waitUnit{ status: params.StatusError, info: `hook failed: "upgrade-charm"`, data: params.StatusData{ "hook": "upgrade-charm", }, charm: 1, }, waitHooks{"fail-upgrade-charm"}, verifyCharm{revision: 1}, verifyWaiting{}, resolveError{state.ResolvedRetryHooks}, waitUnit{ status: params.StatusError, info: `hook failed: "upgrade-charm"`, data: params.StatusData{ "hook": "upgrade-charm", }, charm: 1, }, waitHooks{"fail-upgrade-charm"}, verifyWaiting{}, fixHook{"upgrade-charm"}, resolveError{state.ResolvedRetryHooks}, waitUnit{ status: params.StatusStarted, charm: 1, }, waitHooks{"upgrade-charm", "config-changed"}, verifyRunning{}, ), ut( // This test does an add-relation as quickly as possible // after an upgrade-charm, in the hope that the scheduler will // deliver the events in the wrong order. The observed // behaviour should be the same in either case. "ignore unknown relations until upgrade is done", quickStart{}, createCharm{ revision: 2, customize: func(c *gc.C, ctx *context, path string) { renameRelation(c, path, "db", "db2") hpath := filepath.Join(path, "hooks", "db2-relation-joined") ctx.writeHook(c, hpath, true) }, }, serveCharm{}, upgradeCharm{revision: 2}, addRelation{}, addRelationUnit{}, waitHooks{"upgrade-charm", "config-changed", "db2-relation-joined mysql/0 db2:0"}, verifyCharm{revision: 2}, ), } func (s *UniterSuite) TestUniterSteadyStateUpgrade(c *gc.C) { s.runUniterTests(c, steadyUpgradeTests) } var errorUpgradeTests = []uniterTest{ // Upgrade scenarios from error state. ut( "error state unforced upgrade (ignored until started state)", startupError{"start"}, createCharm{revision: 1}, upgradeCharm{revision: 1}, waitUnit{ status: params.StatusError, info: `hook failed: "start"`, data: params.StatusData{ "hook": "start", }, }, waitHooks{}, verifyCharm{}, verifyWaiting{}, resolveError{state.ResolvedNoHooks}, waitUnit{ status: params.StatusStarted, charm: 1, }, waitHooks{"config-changed", "upgrade-charm", "config-changed"}, verifyCharm{revision: 1}, verifyRunning{}, ), ut( "error state forced upgrade", startupError{"start"}, createCharm{revision: 1}, upgradeCharm{revision: 1, forced: true}, // It's not possible to tell directly from state when the upgrade is // complete, because the new unit charm URL is set at the upgrade // process's point of no return (before actually deploying, but after // the charm has been downloaded and verified). However, it's still // useful to wait until that point... waitUnit{ status: params.StatusError, info: `hook failed: "start"`, data: params.StatusData{ "hook": "start", }, charm: 1, }, // ...because the uniter *will* complete a started deployment even if // we stop it from outside. So, by stopping and starting, we can be // sure that the operation has completed and can safely verify that // the charm state on disk is as we expect. verifyWaiting{}, verifyCharm{revision: 1}, resolveError{state.ResolvedNoHooks}, waitUnit{ status: params.StatusStarted, charm: 1, }, waitHooks{"config-changed"}, verifyRunning{}, ), } func (s *UniterSuite) TestUniterErrorStateUpgrade(c *gc.C) { s.runUniterTests(c, errorUpgradeTests) } var upgradeConflictsTests = []uniterTest{ // Upgrade scenarios - handling conflicts. ut( "upgrade: conflicting files", startUpgradeError{}, // NOTE: this is just dumbly committing the conflicts, but AFAICT this // is the only reasonable solution; if the user tells us it's resolved // we have to take their word for it. resolveError{state.ResolvedNoHooks}, waitHooks{"upgrade-charm", "config-changed"}, waitUnit{ status: params.StatusStarted, charm: 1, }, verifyCharm{revision: 1}, ), ut( `upgrade: conflicting directories`, createCharm{ customize: func(c *gc.C, ctx *context, path string) { err := os.Mkdir(filepath.Join(path, "data"), 0755) c.Assert(err, gc.IsNil) appendHook(c, path, "start", "echo DATA > data/newfile") }, }, serveCharm{}, createUniter{}, waitUnit{ status: params.StatusStarted, }, waitHooks{"install", "config-changed", "start"}, verifyCharm{dirty: true}, createCharm{ revision: 1, customize: func(c *gc.C, ctx *context, path string) { data := filepath.Join(path, "data") err := ioutil.WriteFile(data, []byte("<nelson>ha ha</nelson>"), 0644) c.Assert(err, gc.IsNil) }, }, serveCharm{}, upgradeCharm{revision: 1}, waitUnit{ status: params.StatusError, info: "upgrade failed", charm: 1, }, verifyWaiting{}, verifyCharm{dirty: true}, resolveError{state.ResolvedNoHooks}, waitHooks{"upgrade-charm", "config-changed"}, waitUnit{ status: params.StatusStarted, charm: 1, }, verifyCharm{revision: 1}, ), ut( "upgrade conflict resolved with forced upgrade", startUpgradeError{}, createCharm{ revision: 2, customize: func(c *gc.C, ctx *context, path string) { otherdata := filepath.Join(path, "otherdata") err := ioutil.WriteFile(otherdata, []byte("blah"), 0644) c.Assert(err, gc.IsNil) }, }, serveCharm{}, upgradeCharm{revision: 2, forced: true}, waitUnit{ status: params.StatusStarted, charm: 2, }, waitHooks{"upgrade-charm", "config-changed"}, verifyCharm{revision: 2}, custom{func(c *gc.C, ctx *context) { // otherdata should exist (in v2) otherdata, err := ioutil.ReadFile(filepath.Join(ctx.path, "charm", "otherdata")) c.Assert(err, gc.IsNil) c.Assert(string(otherdata), gc.Equals, "blah") // ignore should not (only in v1) _, err = os.Stat(filepath.Join(ctx.path, "charm", "ignore")) c.Assert(err, jc.Satisfies, os.IsNotExist) // data should contain what was written in the start hook data, err := ioutil.ReadFile(filepath.Join(ctx.path, "charm", "data")) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "STARTDATA\n") }}, ), ut( "upgrade conflict service dying", startUpgradeError{}, serviceDying, verifyWaiting{}, resolveError{state.ResolvedNoHooks}, waitHooks{"upgrade-charm", "config-changed", "stop"}, waitUniterDead{}, ), ut( "upgrade conflict unit dying", startUpgradeError{}, unitDying, verifyWaiting{}, resolveError{state.ResolvedNoHooks}, waitHooks{"upgrade-charm", "config-changed", "stop"}, waitUniterDead{}, ), ut( "upgrade conflict unit dead", startUpgradeError{}, unitDead, waitUniterDead{}, waitHooks{}, ), } func (s *UniterSuite) TestUniterUpgradeConflicts(c *gc.C) { s.runUniterTests(c, upgradeConflictsTests) } func (s *UniterSuite) TestRunCommand(c *gc.C) { testDir := c.MkDir() testFile := func(name string) string { return filepath.Join(testDir, name) } echoUnitNameToFile := func(name string) string { path := filepath.Join(testDir, name) template := "echo juju run ${JUJU_UNIT_NAME} > %s.tmp; mv %s.tmp %s" return fmt.Sprintf(template, path, path, path) } tests := []uniterTest{ ut( "run commands: environment", quickStart{}, runCommands{echoUnitNameToFile("run.output")}, verifyFile{filepath.Join(testDir, "run.output"), "juju run u/0\n"}, ), ut( "run commands: jujuc commands", quickStartRelation{}, runCommands{ fmt.Sprintf("owner-get tag > %s", testFile("jujuc.output")), fmt.Sprintf("unit-get private-address >> %s", testFile("jujuc.output")), fmt.Sprintf("unit-get public-address >> %s", testFile("jujuc.output")), }, verifyFile{ testFile("jujuc.output"), "user-admin\nprivate.address.example.com\npublic.address.example.com\n", }, ), ut( "run commands: proxy settings set", quickStartRelation{}, setProxySettings{Http: "http", Https: "https", Ftp: "ftp", NoProxy: "localhost"}, runCommands{ fmt.Sprintf("echo $http_proxy > %s", testFile("proxy.output")), fmt.Sprintf("echo $HTTP_PROXY >> %s", testFile("proxy.output")), fmt.Sprintf("echo $https_proxy >> %s", testFile("proxy.output")), fmt.Sprintf("echo $HTTPS_PROXY >> %s", testFile("proxy.output")), fmt.Sprintf("echo $ftp_proxy >> %s", testFile("proxy.output")), fmt.Sprintf("echo $FTP_PROXY >> %s", testFile("proxy.output")), fmt.Sprintf("echo $no_proxy >> %s", testFile("proxy.output")), fmt.Sprintf("echo $NO_PROXY >> %s", testFile("proxy.output")), }, verifyFile{ testFile("proxy.output"), "http\nhttp\nhttps\nhttps\nftp\nftp\nlocalhost\nlocalhost\n", }, ), ut( "run commands: async using rpc client", quickStart{}, asyncRunCommands{echoUnitNameToFile("run.output")}, verifyFile{testFile("run.output"), "juju run u/0\n"}, ), ut( "run commands: waits for lock", quickStart{}, acquireHookSyncLock{}, asyncRunCommands{echoUnitNameToFile("wait.output")}, verifyNoFile{testFile("wait.output")}, releaseHookSyncLock, verifyFile{testFile("wait.output"), "juju run u/0\n"}, ), } s.runUniterTests(c, tests) } var relationsTests = []uniterTest{ // Relations. ut( "simple joined/changed/departed", quickStartRelation{}, addRelationUnit{}, waitHooks{"db-relation-joined mysql/1 db:0", "db-relation-changed mysql/1 db:0"}, changeRelationUnit{"mysql/0"}, waitHooks{"db-relation-changed mysql/0 db:0"}, removeRelationUnit{"mysql/1"}, waitHooks{"db-relation-departed mysql/1 db:0"}, verifyRunning{}, ), ut( "relation becomes dying; unit is not last remaining member", quickStartRelation{}, relationDying, waitHooks{"db-relation-departed mysql/0 db:0", "db-relation-broken db:0"}, verifyRunning{}, relationState{life: state.Dying}, removeRelationUnit{"mysql/0"}, verifyRunning{}, relationState{removed: true}, verifyRunning{}, ), ut( "relation becomes dying; unit is last remaining member", quickStartRelation{}, removeRelationUnit{"mysql/0"}, waitHooks{"db-relation-departed mysql/0 db:0"}, relationDying, waitHooks{"db-relation-broken db:0"}, verifyRunning{}, relationState{removed: true}, verifyRunning{}, ), ut( "service becomes dying while in a relation", quickStartRelation{}, serviceDying, waitHooks{"db-relation-departed mysql/0 db:0", "db-relation-broken db:0", "stop"}, waitUniterDead{}, relationState{life: state.Dying}, removeRelationUnit{"mysql/0"}, relationState{removed: true}, ), ut( "unit becomes dying while in a relation", quickStartRelation{}, unitDying, waitHooks{"db-relation-departed mysql/0 db:0", "db-relation-broken db:0", "stop"}, waitUniterDead{}, relationState{life: state.Alive}, removeRelationUnit{"mysql/0"}, relationState{life: state.Alive}, ), ut( "unit becomes dead while in a relation", quickStartRelation{}, unitDead, waitUniterDead{}, 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 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) }}, ), } func (s *UniterSuite) TestUniterRelations(c *gc.C) { s.runUniterTests(c, relationsTests) } var relationsErrorTests = []uniterTest{ ut( "hook error during join of a relation", startupRelationError{"db-relation-joined"}, waitUnit{ status: params.StatusError, info: `hook failed: "db-relation-joined"`, data: params.StatusData{ "hook": "db-relation-joined", "relation-id": 0, "remote-unit": "mysql/0", }, }, ), ut( "hook error during change of a relation", startupRelationError{"db-relation-changed"}, waitUnit{ status: params.StatusError, info: `hook failed: "db-relation-changed"`, data: params.StatusData{ "hook": "db-relation-changed", "relation-id": 0, "remote-unit": "mysql/0", }, }, ), ut( "hook error after a unit departed", startupRelationError{"db-relation-departed"}, waitHooks{"db-relation-joined mysql/0 db:0", "db-relation-changed mysql/0 db:0"}, removeRelationUnit{"mysql/0"}, waitUnit{ status: params.StatusError, info: `hook failed: "db-relation-departed"`, data: params.StatusData{ "hook": "db-relation-departed", "relation-id": 0, "remote-unit": "mysql/0", }, }, ), ut( "hook error after a relation died", startupRelationError{"db-relation-broken"}, waitHooks{"db-relation-joined mysql/0 db:0", "db-relation-changed mysql/0 db:0"}, relationDying, waitUnit{ status: params.StatusError, info: `hook failed: "db-relation-broken"`, data: params.StatusData{ "hook": "db-relation-broken", "relation-id": 0, }, }, ), } func (s *UniterSuite) TestUniterRelationErrors(c *gc.C) { s.runUniterTests(c, relationsErrorTests) } var subordinatesTests = []uniterTest{ // Subordinates. ut( "unit becomes dying while subordinates exist", quickStart{}, addSubordinateRelation{"juju-info"}, waitSubordinateExists{"logging/0"}, unitDying, waitSubordinateDying{}, waitHooks{"stop"}, verifyRunning{true}, removeSubordinate{}, waitUniterDead{}, ), ut( "new subordinate becomes necessary while old one is dying", quickStart{}, addSubordinateRelation{"juju-info"}, waitSubordinateExists{"logging/0"}, removeSubordinateRelation{"juju-info"}, // The subordinate Uniter would usually set Dying in this situation. subordinateDying, addSubordinateRelation{"logging-dir"}, verifyRunning{}, removeSubordinate{}, waitSubordinateExists{"logging/1"}, ), } func (s *UniterSuite) TestUniterSubordinates(c *gc.C) { s.runUniterTests(c, subordinatesTests) } func (s *UniterSuite) runUniterTests(c *gc.C, uniterTests []uniterTest) { for i, t := range uniterTests { c.Logf("\ntest %d: %s\n", i, t.summary) func() { defer s.Reset(c) env, err := s.State.Environment() c.Assert(err, gc.IsNil) ctx := &context{ s: s, st: s.State, uuid: env.UUID(), path: s.unitDir, dataDir: s.dataDir, charms: coretesting.ResponseMap{}, } ctx.run(c, t.steps) }() } } // Assign the unit to a provisioned machine with dummy addresses set. func assertAssignUnit(c *gc.C, st *state.State, u *state.Unit) { err := u.AssignToNewMachine() c.Assert(err, gc.IsNil) mid, err := u.AssignedMachineId() c.Assert(err, gc.IsNil) machine, err := st.Machine(mid) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("i-exist", "fake_nonce", nil) c.Assert(err, gc.IsNil) err = machine.SetAddresses([]instance.Address{{ Type: instance.Ipv4Address, NetworkScope: instance.NetworkCloudLocal, Value: "private.address.example.com", }, { Type: instance.Ipv4Address, NetworkScope: instance.NetworkPublic, Value: "public.address.example.com", }}) c.Assert(err, gc.IsNil) } func (s *UniterSuite) TestSubordinateDying(c *gc.C) { // Create a test context for later use. ctx := &context{ s: s, st: s.State, path: filepath.Join(s.dataDir, "agents", "unit-u-0"), dataDir: s.dataDir, charms: coretesting.ResponseMap{}, } testing.AddStateServerMachine(c, ctx.st) // Create the subordinate service. dir := coretesting.Charms.ClonedDir(c.MkDir(), "logging") curl, err := charm.ParseURL("cs:quantal/logging") c.Assert(err, gc.IsNil) curl = curl.WithRevision(dir.Revision()) step(c, ctx, addCharm{dir, curl}) ctx.svc = s.AddTestingService(c, "u", ctx.sch) // Create the principal service and add a relation. wps := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) wpu, err := wps.AddUnit() c.Assert(err, gc.IsNil) eps, err := s.State.InferEndpoints([]string{"wordpress", "u"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) assertAssignUnit(c, s.State, wpu) // Create the subordinate unit by entering scope as the principal. wpru, err := rel.Unit(wpu) c.Assert(err, gc.IsNil) err = wpru.EnterScope(nil) c.Assert(err, gc.IsNil) ctx.unit, err = s.State.Unit("u/0") c.Assert(err, gc.IsNil) s.APILogin(c, ctx.unit) // Run the actual test. ctx.run(c, []stepper{ serveCharm{}, startUniter{}, waitAddresses{}, custom{func(c *gc.C, ctx *context) { c.Assert(rel.Destroy(), gc.IsNil) }}, waitUniterDead{}, }) } func step(c *gc.C, ctx *context, s stepper) { c.Logf("%#v", s) s.step(c, ctx) } type ensureStateWorker struct { } func (s ensureStateWorker) step(c *gc.C, ctx *context) { addresses, err := ctx.st.Addresses() if err != nil || len(addresses) == 0 { testing.AddStateServerMachine(c, ctx.st) } addresses, err = ctx.st.APIAddressesFromMachines() c.Assert(err, gc.IsNil) c.Assert(addresses, gc.HasLen, 1) } type createCharm struct { revision int badHooks []string customize func(*gc.C, *context, string) } var charmHooks = []string{ "install", "start", "config-changed", "upgrade-charm", "stop", "db-relation-joined", "db-relation-changed", "db-relation-departed", "db-relation-broken", } func (s createCharm) step(c *gc.C, ctx *context) { base := coretesting.Charms.ClonedDirPath(c.MkDir(), "wordpress") for _, name := range charmHooks { path := filepath.Join(base, "hooks", name) good := true for _, bad := range s.badHooks { if name == bad { good = false } } ctx.writeHook(c, path, good) } if s.customize != nil { s.customize(c, ctx, base) } dir, err := charm.ReadDir(base) c.Assert(err, gc.IsNil) err = dir.SetDiskRevision(s.revision) c.Assert(err, gc.IsNil) step(c, ctx, addCharm{dir, curl(s.revision)}) } type addCharm struct { dir *charm.Dir curl *charm.URL } func (s addCharm) step(c *gc.C, ctx *context) { var buf bytes.Buffer err := s.dir.BundleTo(&buf) c.Assert(err, gc.IsNil) body := buf.Bytes() hash, _, err := utils.ReadSHA256(&buf) c.Assert(err, gc.IsNil) key := fmt.Sprintf("/charms/%s/%d", s.dir.Meta().Name, s.dir.Revision()) hurl, err := url.Parse(coretesting.Server.URL + key) c.Assert(err, gc.IsNil) ctx.charms[key] = coretesting.Response{200, nil, body} ctx.sch, err = ctx.st.AddCharm(s.dir, s.curl, hurl, hash) c.Assert(err, gc.IsNil) } type serveCharm struct{} func (serveCharm) step(c *gc.C, ctx *context) { coretesting.Server.ResponseMap(1, ctx.charms) } type createServiceAndUnit struct { serviceName string } func (csau createServiceAndUnit) step(c *gc.C, ctx *context) { if csau.serviceName == "" { csau.serviceName = "u" } sch, err := ctx.st.Charm(curl(0)) c.Assert(err, gc.IsNil) svc := ctx.s.AddTestingService(c, csau.serviceName, sch) unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) // Assign the unit to a provisioned machine to match expected state. assertAssignUnit(c, ctx.st, unit) ctx.svc = svc ctx.unit = unit ctx.s.APILogin(c, unit) } type createUniter struct{} func (createUniter) step(c *gc.C, ctx *context) { step(c, ctx, ensureStateWorker{}) step(c, ctx, createServiceAndUnit{}) step(c, ctx, startUniter{}) step(c, ctx, waitAddresses{}) } type waitAddresses struct{} func (waitAddresses) step(c *gc.C, ctx *context) { timeout := time.After(worstCase) for { select { case <-timeout: c.Fatalf("timed out waiting for unit addresses") case <-time.After(coretesting.ShortWait): err := ctx.unit.Refresh() if err != nil { c.Fatalf("unit refresh failed: %v", err) } // GZ 2013-07-10: Hardcoded values from dummy environ // special cased here, questionable. private, _ := ctx.unit.PrivateAddress() if private != "private.address.example.com" { continue } public, _ := ctx.unit.PublicAddress() if public != "public.address.example.com" { continue } return } } } type startUniter struct { unitTag string } func (s startUniter) step(c *gc.C, ctx *context) { if s.unitTag == "" { s.unitTag = "unit-u-0" } if ctx.uniter != nil { panic("don't start two uniters!") } if ctx.s.uniter == nil { panic("API connection not established") } ctx.uniter = uniter.NewUniter(ctx.s.uniter, s.unitTag, ctx.dataDir) uniter.SetUniterObserver(ctx.uniter, ctx) } type waitUniterDead struct { err string } func (s waitUniterDead) step(c *gc.C, ctx *context) { if s.err != "" { err := s.waitDead(c, ctx) c.Assert(err, gc.ErrorMatches, s.err) return } // In the default case, we're waiting for worker.ErrTerminateAgent, but // the path to that error can be tricky. If the unit becomes Dead at an // inconvenient time, unrelated calls can fail -- as they should -- but // not be detected as worker.ErrTerminateAgent. In this case, we restart // the uniter and check that it fails as expected when starting up; this // mimics the behaviour of the unit agent and verifies that the UA will, // eventually, see the correct error and respond appropriately. err := s.waitDead(c, ctx) if err != worker.ErrTerminateAgent { step(c, ctx, startUniter{}) err = s.waitDead(c, ctx) } c.Assert(err, gc.Equals, worker.ErrTerminateAgent) err = ctx.unit.Refresh() c.Assert(err, gc.IsNil) c.Assert(ctx.unit.Life(), gc.Equals, state.Dead) } func (s waitUniterDead) waitDead(c *gc.C, ctx *context) error { u := ctx.uniter ctx.uniter = nil timeout := time.After(worstCase) for { // The repeated StartSync is to ensure timely completion of this method // in the case(s) where a state change causes a uniter action which // causes a state change which causes a uniter action, in which case we // need more than one sync. At the moment there's only one situation // that causes this -- setting the unit's service to Dying -- but it's // not an intrinsically insane pattern of action (and helps to simplify // the filter code) so this test seems like a small price to pay. ctx.s.BackingState.StartSync() select { case <-u.Dead(): return u.Wait() case <-time.After(coretesting.ShortWait): continue case <-timeout: c.Fatalf("uniter still alive") } } } type stopUniter struct { err string } func (s stopUniter) step(c *gc.C, ctx *context) { u := ctx.uniter ctx.uniter = nil err := u.Stop() if s.err == "" { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, s.err) } } type verifyWaiting struct{} func (s verifyWaiting) step(c *gc.C, ctx *context) { step(c, ctx, stopUniter{}) step(c, ctx, startUniter{}) step(c, ctx, waitHooks{}) } type verifyRunning struct { noHooks bool } func (s verifyRunning) step(c *gc.C, ctx *context) { step(c, ctx, stopUniter{}) step(c, ctx, startUniter{}) if s.noHooks { step(c, ctx, waitHooks{}) } else { step(c, ctx, waitHooks{"config-changed"}) } } type startupError struct { badHook string } func (s startupError) step(c *gc.C, ctx *context) { step(c, ctx, createCharm{badHooks: []string{s.badHook}}) step(c, ctx, serveCharm{}) step(c, ctx, createUniter{}) step(c, ctx, waitUnit{ status: params.StatusError, info: fmt.Sprintf(`hook failed: %q`, s.badHook), }) for _, hook := range []string{"install", "config-changed", "start"} { if hook == s.badHook { step(c, ctx, waitHooks{"fail-" + hook}) break } step(c, ctx, waitHooks{hook}) } step(c, ctx, verifyCharm{}) } type quickStart struct{} func (s quickStart) step(c *gc.C, ctx *context) { step(c, ctx, createCharm{}) step(c, ctx, serveCharm{}) step(c, ctx, createUniter{}) step(c, ctx, waitUnit{status: params.StatusStarted}) step(c, ctx, waitHooks{"install", "config-changed", "start"}) step(c, ctx, verifyCharm{}) } type quickStartRelation struct{} func (s quickStartRelation) step(c *gc.C, ctx *context) { step(c, ctx, quickStart{}) step(c, ctx, addRelation{}) step(c, ctx, addRelationUnit{}) step(c, ctx, waitHooks{"db-relation-joined mysql/0 db:0", "db-relation-changed mysql/0 db:0"}) step(c, ctx, verifyRunning{}) } type startupRelationError struct { badHook string } func (s startupRelationError) step(c *gc.C, ctx *context) { step(c, ctx, createCharm{badHooks: []string{s.badHook}}) step(c, ctx, serveCharm{}) step(c, ctx, createUniter{}) step(c, ctx, waitUnit{status: params.StatusStarted}) step(c, ctx, waitHooks{"install", "config-changed", "start"}) step(c, ctx, verifyCharm{}) step(c, ctx, addRelation{}) step(c, ctx, addRelationUnit{}) } type resolveError struct { resolved state.ResolvedMode } func (s resolveError) step(c *gc.C, ctx *context) { err := ctx.unit.SetResolved(s.resolved) c.Assert(err, gc.IsNil) } type waitUnit struct { status params.Status info string data params.StatusData charm int resolved state.ResolvedMode } func (s waitUnit) step(c *gc.C, ctx *context) { timeout := time.After(worstCase) for { ctx.s.BackingState.StartSync() select { case <-time.After(coretesting.ShortWait): err := ctx.unit.Refresh() if err != nil { c.Fatalf("cannot refresh unit: %v", err) } resolved := ctx.unit.Resolved() if resolved != s.resolved { c.Logf("want resolved mode %q, got %q; still waiting", s.resolved, resolved) continue } url, ok := ctx.unit.CharmURL() if !ok || *url != *curl(s.charm) { var got string if ok { got = url.String() } c.Logf("want unit charm %q, got %q; still waiting", curl(s.charm), got) continue } status, info, data, err := ctx.unit.Status() c.Assert(err, gc.IsNil) if status != s.status { c.Logf("want unit status %q, got %q; still waiting", s.status, status) continue } if info != s.info { c.Logf("want unit status info %q, got %q; still waiting", s.info, info) continue } if s.data != nil { if len(data) != len(s.data) { c.Logf("want %d unit status data value(s), got %d; still waiting", len(s.data), len(data)) continue } for key, value := range s.data { if data[key] != value { c.Logf("want unit status data value %q for key %q, got %q; still waiting", value, key, data[key]) continue } } } return case <-timeout: c.Fatalf("never reached desired status") } } } type waitHooks []string func (s waitHooks) step(c *gc.C, ctx *context) { if len(s) == 0 { // Give unwanted hooks a moment to run... ctx.s.BackingState.StartSync() time.Sleep(coretesting.ShortWait) } ctx.hooks = append(ctx.hooks, s...) c.Logf("waiting for hooks: %#v", ctx.hooks) match, overshoot := ctx.matchHooks(c) if overshoot && len(s) == 0 { c.Fatalf("ran more hooks than expected") } if match { return } timeout := time.After(worstCase) for { ctx.s.BackingState.StartSync() select { case <-time.After(coretesting.ShortWait): if match, _ = ctx.matchHooks(c); match { return } case <-timeout: c.Fatalf("never got expected hooks") } } } type fixHook struct { name string } func (s fixHook) step(c *gc.C, ctx *context) { path := filepath.Join(ctx.path, "charm", "hooks", s.name) ctx.writeHook(c, path, true) } type changeConfig map[string]interface{} func (s changeConfig) step(c *gc.C, ctx *context) { err := ctx.svc.UpdateConfigSettings(charm.Settings(s)) c.Assert(err, gc.IsNil) } type upgradeCharm struct { revision int forced bool } func (s upgradeCharm) step(c *gc.C, ctx *context) { sch, err := ctx.st.Charm(curl(s.revision)) c.Assert(err, gc.IsNil) err = ctx.svc.SetCharm(sch, s.forced) c.Assert(err, gc.IsNil) serveCharm{}.step(c, ctx) } type verifyCharm struct { revision int dirty bool } func (s verifyCharm) step(c *gc.C, ctx *context) { if !s.dirty { path := filepath.Join(ctx.path, "charm", "revision") content, err := ioutil.ReadFile(path) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, strconv.Itoa(s.revision)) err = ctx.unit.Refresh() c.Assert(err, gc.IsNil) url, ok := ctx.unit.CharmURL() c.Assert(ok, gc.Equals, true) c.Assert(url, gc.DeepEquals, curl(s.revision)) } // Before we try to check the git status, make sure expected hooks are all // complete, to prevent the test and the uniter interfering with each other. step(c, ctx, waitHooks{}) cmd := exec.Command("git", "status") cmd.Dir = filepath.Join(ctx.path, "charm") out, err := cmd.CombinedOutput() c.Assert(err, gc.IsNil) cmp := gc.Matches if s.dirty { cmp = gc.Not(gc.Matches) } c.Assert(string(out), cmp, "(# )?On branch master\nnothing to commit.*\n") } type startUpgradeError struct{} func (s startUpgradeError) step(c *gc.C, ctx *context) { steps := []stepper{ createCharm{ customize: func(c *gc.C, ctx *context, path string) { appendHook(c, path, "start", "echo STARTDATA > data") }, }, serveCharm{}, createUniter{}, waitUnit{ status: params.StatusStarted, }, waitHooks{"install", "config-changed", "start"}, verifyCharm{dirty: true}, createCharm{ revision: 1, customize: func(c *gc.C, ctx *context, path string) { data := filepath.Join(path, "data") err := ioutil.WriteFile(data, []byte("<nelson>ha ha</nelson>"), 0644) c.Assert(err, gc.IsNil) ignore := filepath.Join(path, "ignore") err = ioutil.WriteFile(ignore, []byte("anything"), 0644) c.Assert(err, gc.IsNil) }, }, serveCharm{}, upgradeCharm{revision: 1}, waitUnit{ status: params.StatusError, info: "upgrade failed", charm: 1, }, verifyWaiting{}, verifyCharm{dirty: true}, } for _, s_ := range steps { step(c, ctx, s_) } } type addRelation struct { waitJoin bool } func (s addRelation) step(c *gc.C, ctx *context) { if ctx.relation != nil { panic("don't add two relations!") } if ctx.relatedSvc == nil { ctx.relatedSvc = ctx.s.AddTestingService(c, "mysql", ctx.s.AddTestingCharm(c, "mysql")) } eps, err := ctx.st.InferEndpoints([]string{"u", "mysql"}) c.Assert(err, gc.IsNil) 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{} func (s addRelationUnit) step(c *gc.C, ctx *context) { u, err := ctx.relatedSvc.AddUnit() c.Assert(err, gc.IsNil) ru, err := ctx.relation.Unit(u) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) ctx.relationUnits[u.Name()] = ru } type changeRelationUnit struct { name string } func (s changeRelationUnit) step(c *gc.C, ctx *context) { settings, err := ctx.relationUnits[s.name].Settings() c.Assert(err, gc.IsNil) key := "madness?" raw, _ := settings.Get(key) val, _ := raw.(string) if val == "" { val = "this is juju" } else { val += "u" } settings.Set(key, val) _, err = settings.Write() c.Assert(err, gc.IsNil) } type removeRelationUnit struct { name string } func (s removeRelationUnit) step(c *gc.C, ctx *context) { err := ctx.relationUnits[s.name].LeaveScope() c.Assert(err, gc.IsNil) ctx.relationUnits[s.name] = nil } type relationState struct { removed bool life state.Life } func (s relationState) step(c *gc.C, ctx *context) { err := ctx.relation.Refresh() if s.removed { c.Assert(err, jc.Satisfies, errors.IsNotFoundError) return } c.Assert(err, gc.IsNil) c.Assert(ctx.relation.Life(), gc.Equals, s.life) } type addSubordinateRelation struct { ifce string } func (s addSubordinateRelation) step(c *gc.C, ctx *context) { if _, err := ctx.st.Service("logging"); errors.IsNotFoundError(err) { ctx.s.AddTestingService(c, "logging", ctx.s.AddTestingCharm(c, "logging")) } eps, err := ctx.st.InferEndpoints([]string{"logging", "u:" + s.ifce}) c.Assert(err, gc.IsNil) _, err = ctx.st.AddRelation(eps...) c.Assert(err, gc.IsNil) } type removeSubordinateRelation struct { ifce string } func (s removeSubordinateRelation) step(c *gc.C, ctx *context) { eps, err := ctx.st.InferEndpoints([]string{"logging", "u:" + s.ifce}) c.Assert(err, gc.IsNil) rel, err := ctx.st.EndpointsRelation(eps...) c.Assert(err, gc.IsNil) err = rel.Destroy() c.Assert(err, gc.IsNil) } type waitSubordinateExists struct { name string } func (s waitSubordinateExists) step(c *gc.C, ctx *context) { timeout := time.After(worstCase) for { ctx.s.BackingState.StartSync() select { case <-timeout: c.Fatalf("subordinate was not created") case <-time.After(coretesting.ShortWait): var err error ctx.subordinate, err = ctx.st.Unit(s.name) if errors.IsNotFoundError(err) { continue } c.Assert(err, gc.IsNil) return } } } type waitSubordinateDying struct{} func (waitSubordinateDying) step(c *gc.C, ctx *context) { timeout := time.After(worstCase) for { ctx.s.BackingState.StartSync() select { case <-timeout: c.Fatalf("subordinate was not made Dying") case <-time.After(coretesting.ShortWait): err := ctx.subordinate.Refresh() c.Assert(err, gc.IsNil) if ctx.subordinate.Life() != state.Dying { continue } } break } } type removeSubordinate struct{} func (removeSubordinate) step(c *gc.C, ctx *context) { err := ctx.subordinate.EnsureDead() c.Assert(err, gc.IsNil) err = ctx.subordinate.Remove() c.Assert(err, gc.IsNil) ctx.subordinate = nil } type assertYaml struct { path string expect map[string]interface{} } func (s assertYaml) step(c *gc.C, ctx *context) { data, err := ioutil.ReadFile(filepath.Join(ctx.path, s.path)) c.Assert(err, gc.IsNil) actual := make(map[string]interface{}) err = goyaml.Unmarshal(data, &actual) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, s.expect) } type writeFile struct { path string mode os.FileMode } func (s writeFile) step(c *gc.C, ctx *context) { path := filepath.Join(ctx.path, s.path) dir := filepath.Dir(path) err := os.MkdirAll(dir, 0755) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(path, nil, s.mode) c.Assert(err, gc.IsNil) } type chmod struct { path string mode os.FileMode } func (s chmod) step(c *gc.C, ctx *context) { path := filepath.Join(ctx.path, s.path) err := os.Chmod(path, s.mode) c.Assert(err, gc.IsNil) } type custom struct { f func(*gc.C, *context) } func (s custom) step(c *gc.C, ctx *context) { s.f(c, ctx) } var serviceDying = custom{func(c *gc.C, ctx *context) { c.Assert(ctx.svc.Destroy(), gc.IsNil) }} var relationDying = custom{func(c *gc.C, ctx *context) { c.Assert(ctx.relation.Destroy(), gc.IsNil) }} var unitDying = custom{func(c *gc.C, ctx *context) { c.Assert(ctx.unit.Destroy(), gc.IsNil) }} var unitDead = custom{func(c *gc.C, ctx *context) { c.Assert(ctx.unit.EnsureDead(), gc.IsNil) }} var subordinateDying = custom{func(c *gc.C, ctx *context) { c.Assert(ctx.subordinate.Destroy(), gc.IsNil) }} func curl(revision int) *charm.URL { return charm.MustParseURL("cs:quantal/wordpress").WithRevision(revision) } func appendHook(c *gc.C, charm, name, data string) { path := filepath.Join(charm, "hooks", name) f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0755) c.Assert(err, gc.IsNil) defer f.Close() _, err = f.Write([]byte(data)) c.Assert(err, gc.IsNil) } func renameRelation(c *gc.C, charmPath, oldName, newName string) { path := filepath.Join(charmPath, "metadata.yaml") f, err := os.Open(path) c.Assert(err, gc.IsNil) defer f.Close() meta, err := charm.ReadMeta(f) c.Assert(err, gc.IsNil) replace := func(what map[string]charm.Relation) bool { for relName, relation := range what { if relName == oldName { what[newName] = relation delete(what, oldName) return true } } return false } replaced := replace(meta.Provides) || replace(meta.Requires) || replace(meta.Peers) c.Assert(replaced, gc.Equals, true, gc.Commentf("charm %q does not implement relation %q", charmPath, oldName)) newmeta, err := goyaml.Marshal(meta) c.Assert(err, gc.IsNil) ioutil.WriteFile(path, newmeta, 0644) f, err = os.Open(path) c.Assert(err, gc.IsNil) defer f.Close() meta, err = charm.ReadMeta(f) c.Assert(err, gc.IsNil) } func createHookLock(c *gc.C, dataDir string) *fslock.Lock { lockDir := filepath.Join(dataDir, "locks") lock, err := fslock.NewLock(lockDir, "uniter-hook-execution") c.Assert(err, gc.IsNil) return lock } type acquireHookSyncLock struct { message string } func (s acquireHookSyncLock) step(c *gc.C, ctx *context) { lock := createHookLock(c, ctx.dataDir) c.Assert(lock.IsLocked(), gc.Equals, false) err := lock.Lock(s.message) c.Assert(err, gc.IsNil) } var releaseHookSyncLock = custom{func(c *gc.C, ctx *context) { lock := createHookLock(c, ctx.dataDir) // Force the release. err := lock.BreakLock() c.Assert(err, gc.IsNil) }} var verifyHookSyncLockUnlocked = custom{func(c *gc.C, ctx *context) { lock := createHookLock(c, ctx.dataDir) c.Assert(lock.IsLocked(), jc.IsFalse) }} var verifyHookSyncLockLocked = custom{func(c *gc.C, ctx *context) { lock := createHookLock(c, ctx.dataDir) c.Assert(lock.IsLocked(), jc.IsTrue) }} type setProxySettings osenv.ProxySettings func (s setProxySettings) step(c *gc.C, ctx *context) { attrs := map[string]interface{}{ "http-proxy": s.Http, "https-proxy": s.Https, "ftp-proxy": s.Ftp, "no-proxy": s.NoProxy, } err := ctx.st.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) // wait for the new values... expected := (osenv.ProxySettings)(s) for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { if ctx.uniter.GetProxyValues() == expected { // Also confirm that the values were specified for the environment. c.Assert(os.Getenv("http_proxy"), gc.Equals, expected.Http) c.Assert(os.Getenv("HTTP_PROXY"), gc.Equals, expected.Http) c.Assert(os.Getenv("https_proxy"), gc.Equals, expected.Https) c.Assert(os.Getenv("HTTPS_PROXY"), gc.Equals, expected.Https) c.Assert(os.Getenv("ftp_proxy"), gc.Equals, expected.Ftp) c.Assert(os.Getenv("FTP_PROXY"), gc.Equals, expected.Ftp) c.Assert(os.Getenv("no_proxy"), gc.Equals, expected.NoProxy) c.Assert(os.Getenv("NO_PROXY"), gc.Equals, expected.NoProxy) return } } c.Fatal("settings didn't get noticed by the uniter") } type runCommands []string func (cmds runCommands) step(c *gc.C, ctx *context) { commands := strings.Join(cmds, "\n") result, err := ctx.uniter.RunCommands(commands) c.Assert(err, gc.IsNil) c.Check(result.Code, gc.Equals, 0) c.Check(string(result.Stdout), gc.Equals, "") c.Check(string(result.Stderr), gc.Equals, "") } type asyncRunCommands []string func (cmds asyncRunCommands) step(c *gc.C, ctx *context) { commands := strings.Join(cmds, "\n") socketPath := filepath.Join(ctx.path, uniter.RunListenerFile) go func() { // make sure the socket exists client, err := rpc.Dial("unix", socketPath) c.Assert(err, gc.IsNil) defer client.Close() var result utilexec.ExecResponse err = client.Call(uniter.JujuRunEndpoint, commands, &result) c.Assert(err, gc.IsNil) c.Check(result.Code, gc.Equals, 0) c.Check(string(result.Stdout), gc.Equals, "") c.Check(string(result.Stderr), gc.Equals, "") }() } type verifyFile struct { filename string content string } func (verify verifyFile) fileExists() bool { _, err := os.Stat(verify.filename) return err == nil } func (verify verifyFile) checkContent(c *gc.C) { content, err := ioutil.ReadFile(verify.filename) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, verify.content) } func (verify verifyFile) step(c *gc.C, ctx *context) { if verify.fileExists() { verify.checkContent(c) return } c.Logf("waiting for file: %s", verify.filename) timeout := time.After(worstCase) for { select { case <-time.After(coretesting.ShortWait): if verify.fileExists() { verify.checkContent(c) return } case <-timeout: c.Fatalf("file does not exist") } } } // verify that the file does not exist type verifyNoFile struct { filename string } func (verify verifyNoFile) step(c *gc.C, ctx *context) { c.Assert(verify.filename, jc.DoesNotExist) // Wait a short time and check again. time.Sleep(coretesting.ShortWait) c.Assert(verify.filename, jc.DoesNotExist) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/debug/�����������������������������������0000755�0000153�0000161�00000000000�12321735643�024573� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/debug/client_test.go���������������������0000644�0000153�0000161�00000003371�12321735642�027442� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package debug_test import ( "fmt" "regexp" gc "launchpad.net/gocheck" "launchpad.net/juju-core/worker/uniter/debug" ) type DebugHooksClientSuite struct{} var _ = gc.Suite(&DebugHooksClientSuite{}) func (*DebugHooksClientSuite) TestClientScript(c *gc.C) { ctx := debug.NewHooksContext("foo/8") // Test the variable substitutions. result := debug.ClientScript(ctx, nil) // No variables left behind. c.Assert(result, gc.Matches, "[^{}]*") // tmux new-session -d -s {unit_name} c.Assert(result, gc.Matches, fmt.Sprintf("(.|\n)*tmux new-session -s %s(.|\n)*", regexp.QuoteMeta(ctx.Unit))) //) 9>{exit_flock} c.Assert(result, gc.Matches, fmt.Sprintf("(.|\n)*\\) 9>%s(.|\n)*", regexp.QuoteMeta(ctx.ClientExitFileLock()))) //) 8>{entry_flock} c.Assert(result, gc.Matches, fmt.Sprintf("(.|\n)*\\) 8>%s(.|\n)*", regexp.QuoteMeta(ctx.ClientFileLock()))) // nil is the same as empty slice is the same as "*". // Also, if "*" is present as well as a named hook, // it is equivalent to "*". c.Assert(debug.ClientScript(ctx, nil), gc.Equals, debug.ClientScript(ctx, []string{})) c.Assert(debug.ClientScript(ctx, []string{"*"}), gc.Equals, debug.ClientScript(ctx, nil)) c.Assert(debug.ClientScript(ctx, []string{"*", "something"}), gc.Equals, debug.ClientScript(ctx, []string{"*"})) // debug.ClientScript does not validate hook names, as it doesn't have // a full state API connection to determine valid relation hooks. expected := fmt.Sprintf( `(.|\n)*echo "aG9va3M6Ci0gc29tZXRoaW5nIHNvbWV0aGluZ2Vsc2UK" | base64 -d > %s(.|\n)*`, regexp.QuoteMeta(ctx.ClientFileLock()), ) c.Assert(debug.ClientScript(ctx, []string{"something somethingelse"}), gc.Matches, expected) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/debug/common_test.go���������������������0000644�0000153�0000161�00000001443�12321735642�027452� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package debug_test import ( "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/worker/uniter/debug" ) type DebugHooksCommonSuite struct{} var _ = gc.Suite(&DebugHooksCommonSuite{}) func TestPackage(t *testing.T) { gc.TestingT(t) } // TestCommonScript tests the behaviour of HooksContext. func (*DebugHooksCommonSuite) TestHooksContext(c *gc.C) { ctx := debug.NewHooksContext("foo/8") c.Assert(ctx.Unit, gc.Equals, "foo/8") c.Assert(ctx.FlockDir, gc.Equals, "/tmp") ctx.FlockDir = "/var/lib/juju" c.Assert(ctx.ClientFileLock(), gc.Equals, "/var/lib/juju/juju-unit-foo-8-debug-hooks") c.Assert(ctx.ClientExitFileLock(), gc.Equals, "/var/lib/juju/juju-unit-foo-8-debug-hooks-exit") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/debug/server.go��������������������������0000644�0000153�0000161�00000006336�12321735642�026437� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package debug import ( "bytes" "errors" "io/ioutil" "os" "os/exec" "launchpad.net/goyaml" "launchpad.net/juju-core/utils/set" ) // ServerSession represents a "juju debug-hooks" session. type ServerSession struct { *HooksContext hooks set.Strings } // MatchHook returns true if the specified hook name matches // the hook specified by the debug-hooks client. func (s *ServerSession) MatchHook(hookName string) bool { return s.hooks.IsEmpty() || s.hooks.Contains(hookName) } // waitClientExit executes flock, waiting for the SSH client to exit. // This is a var so it can be replaced for testing. var waitClientExit = func(s *ServerSession) { path := s.ClientExitFileLock() exec.Command("flock", path, "-c", "true").Run() } // RunHook "runs" the hook with the specified name via debug-hooks. func (s *ServerSession) RunHook(hookName, charmDir string, env []string) error { env = append(env, "JUJU_HOOK_NAME="+hookName) cmd := exec.Command("/bin/bash", "-s") cmd.Env = env cmd.Dir = charmDir cmd.Stdin = bytes.NewBufferString(debugHooksServerScript) if err := cmd.Start(); err != nil { return err } go func(proc *os.Process) { // Wait for the SSH client to exit (i.e. release the flock), // then kill the server hook process in case the client // exited uncleanly. waitClientExit(s) proc.Kill() }(cmd.Process) return cmd.Wait() } // FindSession attempts to find a debug hooks session for the unit specified // in the context, and returns a new ServerSession structure for it. func (c *HooksContext) FindSession() (*ServerSession, error) { cmd := exec.Command("tmux", "has-session", "-t", c.tmuxSessionName()) out, err := cmd.CombinedOutput() if err != nil { if len(out) != 0 { return nil, errors.New(string(out)) } else { return nil, err } } // Parse the debug-hooks file for an optional hook name. data, err := ioutil.ReadFile(c.ClientFileLock()) if err != nil { return nil, err } var args hookArgs err = goyaml.Unmarshal(data, &args) if err != nil { return nil, err } hooks := set.NewStrings(args.Hooks...) session := &ServerSession{c, hooks} return session, nil } const debugHooksServerScript = `set -e export JUJU_DEBUG=$(mktemp -d) exec > $JUJU_DEBUG/debug.log >&1 # Set a useful prompt. export PS1="$JUJU_UNIT_NAME:$JUJU_HOOK_NAME % " # Save environment variables and export them for sourcing. FILTER='^\(LS_COLORS\|LESSOPEN\|LESSCLOSE\|PWD\)=' export | grep -v $FILTER > $JUJU_DEBUG/env.sh # Create an internal script which will load the hook environment. cat > $JUJU_DEBUG/hook.sh <<END #!/bin/bash . $JUJU_DEBUG/env.sh echo \$\$ > $JUJU_DEBUG/hook.pid exec /bin/bash --noprofile --norc END chmod +x $JUJU_DEBUG/hook.sh tmux new-window -t $JUJU_UNIT_NAME -n $JUJU_HOOK_NAME "$JUJU_DEBUG/hook.sh" # If we exit for whatever reason, kill the hook shell. exit_handler() { if [ -f $JUJU_DEBUG/hook.pid ]; then kill -9 $(cat $JUJU_DEBUG/hook.pid) || true fi } trap exit_handler EXIT # Wait for the hook shell to start, and then wait for it to exit. while [ ! -f $JUJU_DEBUG/hook.pid ]; do sleep 1 done HOOK_PID=$(cat $JUJU_DEBUG/hook.pid) while kill -0 "$HOOK_PID" 2> /dev/null; do sleep 1 done ` ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/debug/client.go��������������������������0000644�0000153�0000161�00000005753�12321735642�026411� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package debug import ( "encoding/base64" "strings" "launchpad.net/goyaml" ) type hookArgs struct { Hooks []string `yaml:"hooks,omitempty"` } // ClientScript returns a bash script suitable for executing // on the unit system to intercept hooks via tmux shell. func ClientScript(c *HooksContext, hooks []string) string { // If any hook is "*", then the client is interested in all. for _, hook := range hooks { if hook == "*" { hooks = nil break } } s := strings.Replace(debugHooksClientScript, "{unit_name}", c.Unit, -1) s = strings.Replace(s, "{tmux_conf}", tmuxConf, 1) s = strings.Replace(s, "{entry_flock}", c.ClientFileLock(), -1) s = strings.Replace(s, "{exit_flock}", c.ClientExitFileLock(), -1) yamlArgs := encodeArgs(hooks) base64Args := base64.StdEncoding.EncodeToString(yamlArgs) s = strings.Replace(s, "{hook_args}", base64Args, 1) return s } func encodeArgs(hooks []string) []byte { // Marshal to YAML, then encode in base64 to avoid shell escapes. yamlArgs, err := goyaml.Marshal(hookArgs{Hooks: hooks}) if err != nil { // This should not happen: we're in full control. panic(err) } return yamlArgs } const debugHooksClientScript = `#!/bin/bash ( # Lock the juju-<unit>-debug lockfile. flock -n 8 || (echo "Failed to acquire {entry_flock}: unit is already being debugged" 2>&1; exit 1) ( # Close the inherited lock FD, or tmux will keep it open. exec 8>&- # Write out the debug-hooks args. echo "{hook_args}" | base64 -d > {entry_flock} # Lock the juju-<unit>-debug-exit lockfile. flock -n 9 || exit 1 # Wait for tmux to be installed. while [ ! -f /usr/bin/tmux ]; do sleep 1 done if [ ! -f ~/.tmux.conf ]; then if [ -f /usr/share/byobu/profiles/tmux ]; then # Use byobu/tmux profile for familiar keybindings and branding echo "source-file /usr/share/byobu/profiles/tmux" > ~/.tmux.conf else # Otherwise, use the legacy juju/tmux configuration cat > ~/.tmux.conf <<END {tmux_conf} END fi fi ( # Close the inherited lock FD, or tmux will keep it open. exec 9>&- exec tmux new-session -s {unit_name} ) ) 9>{exit_flock} ) 8>{entry_flock} exit $? ` const tmuxConf = ` # Status bar set-option -g status-bg black set-option -g status-fg white set-window-option -g window-status-current-bg red set-window-option -g window-status-current-attr bright set-option -g status-right '' # Panes set-option -g pane-border-fg white set-option -g pane-active-border-fg white # Monitor activity on windows set-window-option -g monitor-activity on # Screen bindings, since people are more familiar with that. set-option -g prefix C-a bind C-a last-window bind a send-key C-a bind | split-window -h bind - split-window -v # Fix CTRL-PGUP/PGDOWN for vim set-window-option -g xterm-keys on # Prevent ESC key from adding delay and breaking Vim's ESC > arrow key set-option -s escape-time 0 ` ���������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/debug/common.go��������������������������0000644�0000153�0000161�00000001324�12321735642�026411� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package debug import ( "fmt" "path/filepath" "launchpad.net/juju-core/names" ) const defaultFlockDir = "/tmp" type HooksContext struct { Unit string FlockDir string } func NewHooksContext(unitName string) *HooksContext { return &HooksContext{Unit: unitName, FlockDir: defaultFlockDir} } func (c *HooksContext) ClientFileLock() string { basename := fmt.Sprintf("juju-%s-debug-hooks", names.UnitTag(c.Unit)) return filepath.Join(c.FlockDir, basename) } func (c *HooksContext) ClientExitFileLock() string { return c.ClientFileLock() + "-exit" } func (c *HooksContext) tmuxSessionName() string { return c.Unit } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/debug/server_test.go���������������������0000644�0000153�0000161�00000014013�12321735642�027465� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package debug import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "regexp" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) type DebugHooksServerSuite struct { testbase.LoggingSuite ctx *HooksContext fakebin string tmpdir string } var _ = gc.Suite(&DebugHooksServerSuite{}) // echocommand outputs its name and arguments to stdout for verification, // and exits with the value of $EXIT_CODE var echocommand = `#!/bin/bash --norc echo $(basename $0) $@ exit $EXIT_CODE ` var fakecommands = []string{"tmux"} func (s *DebugHooksServerSuite) SetUpTest(c *gc.C) { s.fakebin = c.MkDir() s.tmpdir = c.MkDir() s.PatchEnvPathPrepend(s.fakebin) s.PatchEnvironment("TMPDIR", s.tmpdir) s.PatchEnvironment("TEST_RESULT", "") for _, name := range fakecommands { err := ioutil.WriteFile(filepath.Join(s.fakebin, name), []byte(echocommand), 0777) c.Assert(err, gc.IsNil) } s.ctx = NewHooksContext("foo/8") s.ctx.FlockDir = s.tmpdir s.PatchEnvironment("JUJU_UNIT_NAME", s.ctx.Unit) } func (s *DebugHooksServerSuite) TestFindSession(c *gc.C) { // Test "tmux has-session" failure. The error // message is the output of tmux has-session. os.Setenv("EXIT_CODE", "1") session, err := s.ctx.FindSession() c.Assert(session, gc.IsNil) c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta("tmux has-session -t "+s.ctx.Unit+"\n")) os.Setenv("EXIT_CODE", "") // tmux session exists, but missing debug-hooks file: error. session, err = s.ctx.FindSession() c.Assert(session, gc.IsNil) c.Assert(err, gc.NotNil) c.Assert(err, jc.Satisfies, os.IsNotExist) // Hooks file is present, empty. err = ioutil.WriteFile(s.ctx.ClientFileLock(), []byte{}, 0777) c.Assert(err, gc.IsNil) session, err = s.ctx.FindSession() c.Assert(session, gc.NotNil) c.Assert(err, gc.IsNil) // If session.hooks is empty, it'll match anything. c.Assert(session.MatchHook(""), jc.IsTrue) c.Assert(session.MatchHook("something"), jc.IsTrue) // Hooks file is present, non-empty err = ioutil.WriteFile(s.ctx.ClientFileLock(), []byte(`hooks: [foo, bar, baz]`), 0777) c.Assert(err, gc.IsNil) session, err = s.ctx.FindSession() c.Assert(session, gc.NotNil) c.Assert(err, gc.IsNil) // session should only match "foo", "bar" or "baz". c.Assert(session.MatchHook(""), jc.IsFalse) c.Assert(session.MatchHook("something"), jc.IsFalse) c.Assert(session.MatchHook("foo"), jc.IsTrue) c.Assert(session.MatchHook("bar"), jc.IsTrue) c.Assert(session.MatchHook("baz"), jc.IsTrue) c.Assert(session.MatchHook("foo bar baz"), jc.IsFalse) } func (s *DebugHooksServerSuite) TestRunHookExceptional(c *gc.C) { err := ioutil.WriteFile(s.ctx.ClientFileLock(), []byte{}, 0777) c.Assert(err, gc.IsNil) session, err := s.ctx.FindSession() c.Assert(session, gc.NotNil) c.Assert(err, gc.IsNil) // Run the hook in debug mode with no exit flock held. // The exit flock will be acquired immediately, and the // debug-hooks server process killed. err = session.RunHook("myhook", s.tmpdir, os.Environ()) c.Assert(err, gc.ErrorMatches, "signal: [kK]illed") // Run the hook in debug mode, simulating the holding // of the exit flock. This simulates the client process // starting but not cleanly exiting (normally the .pid // file is updated, and the server waits on the client // process' death). ch := make(chan bool) var clientExited bool s.PatchValue(&waitClientExit, func(*ServerSession) { clientExited = <-ch }) go func() { ch <- true }() err = session.RunHook("myhook", s.tmpdir, os.Environ()) c.Assert(clientExited, jc.IsTrue) c.Assert(err, gc.ErrorMatches, "signal: [kK]illed") } func (s *DebugHooksServerSuite) TestRunHook(c *gc.C) { err := ioutil.WriteFile(s.ctx.ClientFileLock(), []byte{}, 0777) c.Assert(err, gc.IsNil) session, err := s.ctx.FindSession() c.Assert(session, gc.NotNil) c.Assert(err, gc.IsNil) const hookName = "myhook" // Run the hook in debug mode with the exit flock held, // and also create the .pid file. We'll populate it with // an invalid PID; this will cause the server process to // exit cleanly (as if the PID were real and no longer running). cmd := exec.Command("flock", s.ctx.ClientExitFileLock(), "-c", "sleep 5s") c.Assert(cmd.Start(), gc.IsNil) ch := make(chan error) go func() { ch <- session.RunHook(hookName, s.tmpdir, os.Environ()) }() // Wait until either we find the debug dir, or the flock is released. ticker := time.Tick(10 * time.Millisecond) var debugdir os.FileInfo for debugdir == nil { select { case err = <-ch: // flock was released before we found the debug dir. c.Error("could not find hook.sh") case <-ticker: tmpdir, err := os.Open(s.tmpdir) if err != nil { c.Fatalf("Failed to open $TMPDIR: %s", err) } fi, err := tmpdir.Readdir(-1) if err != nil { c.Fatalf("Failed to read $TMPDIR: %s", err) } tmpdir.Close() for _, fi := range fi { if fi.IsDir() { hooksh := filepath.Join(s.tmpdir, fi.Name(), "hook.sh") if _, err = os.Stat(hooksh); err == nil { debugdir = fi break } } } if debugdir != nil { break } time.Sleep(10 * time.Millisecond) } } envsh := filepath.Join(s.tmpdir, debugdir.Name(), "env.sh") s.verifyEnvshFile(c, envsh, hookName) hookpid := filepath.Join(s.tmpdir, debugdir.Name(), "hook.pid") err = ioutil.WriteFile(hookpid, []byte("not a pid"), 0777) c.Assert(err, gc.IsNil) // RunHook should complete without waiting to be // killed, and despite the exit lock being held. err = <-ch c.Assert(err, gc.IsNil) cmd.Process.Kill() // kill flock } func (s *DebugHooksServerSuite) verifyEnvshFile(c *gc.C, envshPath string, hookName string) { data, err := ioutil.ReadFile(envshPath) c.Assert(err, gc.IsNil) contents := string(data) c.Assert(contents, jc.Contains, fmt.Sprintf("JUJU_UNIT_NAME=%q", s.ctx.Unit)) c.Assert(contents, jc.Contains, fmt.Sprintf("JUJU_HOOK_NAME=%q", hookName)) c.Assert(contents, jc.Contains, fmt.Sprintf(`PS1="%s:%s %% "`, s.ctx.Unit, hookName)) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/modes.go���������������������������������0000644�0000153�0000161�00000024405�12321735642�025147� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( stderrors "errors" "fmt" "launchpad.net/tomb" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/worker" ucharm "launchpad.net/juju-core/worker/uniter/charm" "launchpad.net/juju-core/worker/uniter/hook" ) // Mode defines the signature of the functions that implement the possible // states of a running Uniter. type Mode func(u *Uniter) (Mode, error) // ModeContinue determines what action to take based on persistent uniter state. func ModeContinue(u *Uniter) (next Mode, err error) { defer modeContext("ModeContinue", &err)() // If we haven't yet loaded state, do so. if u.s == nil { logger.Infof("loading uniter state") if u.s, err = u.sf.Read(); err == ErrNoStateFile { // When no state exists, start from scratch. logger.Infof("charm is not deployed") curl, _, err := u.service.CharmURL() if err != nil { return nil, err } return ModeInstalling(curl), nil } else if err != nil { return nil, err } } // Filter out states not related to charm deployment. switch u.s.Op { case Continue: logger.Infof("continuing after %q hook", u.s.Hook.Kind) switch u.s.Hook.Kind { case hooks.Stop: return ModeTerminating, nil case hooks.UpgradeCharm: return ModeConfigChanged, nil case hooks.ConfigChanged: if !u.s.Started { return ModeStarting, nil } } if !u.ranConfigChanged { return ModeConfigChanged, nil } return ModeAbide, nil case RunHook: if u.s.OpStep == Queued { logger.Infof("found queued %q hook", u.s.Hook.Kind) if err = u.runHook(*u.s.Hook); err != nil && err != errHookFailed { return nil, err } return ModeContinue, nil } if u.s.OpStep == Done { logger.Infof("found uncommitted %q hook", u.s.Hook.Kind) if err = u.commitHook(*u.s.Hook); err != nil { return nil, err } return ModeContinue, nil } logger.Infof("awaiting error resolution for %q hook", u.s.Hook.Kind) return ModeHookError, nil } // Resume interrupted deployment operations. curl := u.s.CharmURL if u.s.Op == Install { logger.Infof("resuming charm install") return ModeInstalling(curl), nil } else if u.s.Op == Upgrade { logger.Infof("resuming charm upgrade") return ModeUpgrading(curl), nil } panic(fmt.Errorf("unhandled uniter operation %q", u.s.Op)) } // ModeInstalling is responsible for the initial charm deployment. func ModeInstalling(curl *charm.URL) Mode { name := fmt.Sprintf("ModeInstalling %s", curl) return func(u *Uniter) (next Mode, err error) { defer modeContext(name, &err)() if err = u.deploy(curl, Install); err != nil { return nil, err } return ModeContinue, nil } } // ModeUpgrading is responsible for upgrading the charm. func ModeUpgrading(curl *charm.URL) Mode { name := fmt.Sprintf("ModeUpgrading %s", curl) return func(u *Uniter) (next Mode, err error) { defer modeContext(name, &err)() if err = u.deploy(curl, Upgrade); err == ucharm.ErrConflict { return ModeConflicted(curl), nil } else if err != nil { return nil, err } return ModeContinue, nil } } // ModeConfigChanged runs the "config-changed" hook. func ModeConfigChanged(u *Uniter) (next Mode, err error) { defer modeContext("ModeConfigChanged", &err)() if !u.s.Started { if err = u.unit.SetStatus(params.StatusInstalled, "", nil); err != nil { return nil, err } } u.f.DiscardConfigEvent() if err := u.runHook(hook.Info{Kind: hooks.ConfigChanged}); err == errHookFailed { return ModeHookError, nil } else if err != nil { return nil, err } return ModeContinue, nil } // ModeStarting runs the "start" hook. func ModeStarting(u *Uniter) (next Mode, err error) { defer modeContext("ModeStarting", &err)() if err := u.runHook(hook.Info{Kind: hooks.Start}); err == errHookFailed { return ModeHookError, nil } else if err != nil { return nil, err } return ModeContinue, nil } // ModeStopping runs the "stop" hook. func ModeStopping(u *Uniter) (next Mode, err error) { defer modeContext("ModeStopping", &err)() if err := u.runHook(hook.Info{Kind: hooks.Stop}); err == errHookFailed { return ModeHookError, nil } else if err != nil { return nil, err } return ModeContinue, nil } // ModeTerminating marks the unit dead and returns ErrTerminateAgent. func ModeTerminating(u *Uniter) (next Mode, err error) { defer modeContext("ModeTerminating", &err)() if err = u.unit.SetStatus(params.StatusStopped, "", nil); err != nil { return nil, err } w, err := u.unit.Watch() if err != nil { return nil, err } defer watcher.Stop(w, &u.tomb) for { select { case <-u.tomb.Dying(): return nil, tomb.ErrDying case _, ok := <-w.Changes(): if !ok { return nil, watcher.MustErr(w) } if err := u.unit.Refresh(); err != nil { return nil, err } if hasSubs, err := u.unit.HasSubordinates(); err != nil { return nil, err } else if hasSubs { continue } // The unit is known to be Dying; so if it didn't have subordinates // just above, it can't acquire new ones before this call. if err := u.unit.EnsureDead(); err != nil { return nil, err } return nil, worker.ErrTerminateAgent } } } // ModeAbide is the Uniter's usual steady state. It watches for and responds to: // * service configuration changes // * charm upgrade requests // * relation changes // * unit death func ModeAbide(u *Uniter) (next Mode, err error) { defer modeContext("ModeAbide", &err)() if u.s.Op != Continue { return nil, fmt.Errorf("insane uniter state: %#v", u.s) } if err = u.unit.SetStatus(params.StatusStarted, "", nil); err != nil { return nil, err } u.f.WantUpgradeEvent(false) for _, r := range u.relationers { r.StartHooks() } defer func() { for _, r := range u.relationers { if e := r.StopHooks(); e != nil && err == nil { err = e } } }() select { case <-u.f.UnitDying(): return modeAbideDyingLoop(u) default: } return modeAbideAliveLoop(u) } // modeAbideAliveLoop handles all state changes for ModeAbide when the unit // is in an Alive state. func modeAbideAliveLoop(u *Uniter) (Mode, error) { for { hi := hook.Info{} select { case <-u.tomb.Dying(): return nil, tomb.ErrDying case <-u.f.UnitDying(): return modeAbideDyingLoop(u) case <-u.f.ConfigEvents(): hi = hook.Info{Kind: hooks.ConfigChanged} case hi = <-u.relationHooks: case ids := <-u.f.RelationsEvents(): added, err := u.updateRelations(ids) if err != nil { return nil, err } for _, r := range added { r.StartHooks() } continue case curl := <-u.f.UpgradeEvents(): return ModeUpgrading(curl), nil } if err := u.runHook(hi); err == errHookFailed { return ModeHookError, nil } else if err != nil { return nil, err } } } // modeAbideDyingLoop handles the proper termination of all relations in // response to a Dying unit. func modeAbideDyingLoop(u *Uniter) (next Mode, err error) { if err := u.unit.Refresh(); err != nil { return nil, err } if err = u.unit.DestroyAllSubordinates(); err != nil { return nil, err } for id, r := range u.relationers { if err := r.SetDying(); err != nil { return nil, err } else if r.IsImplicit() { delete(u.relationers, id) } } for { if len(u.relationers) == 0 { return ModeStopping, nil } hi := hook.Info{} select { case <-u.tomb.Dying(): return nil, tomb.ErrDying case <-u.f.ConfigEvents(): hi = hook.Info{Kind: hooks.ConfigChanged} case hi = <-u.relationHooks: } if err = u.runHook(hi); err == errHookFailed { return ModeHookError, nil } else if err != nil { return nil, err } } } // ModeHookError is responsible for watching and responding to: // * user resolution of hook errors // * charm upgrade requests func ModeHookError(u *Uniter) (next Mode, err error) { defer modeContext("ModeHookError", &err)() if u.s.Op != RunHook || u.s.OpStep != Pending { return nil, fmt.Errorf("insane uniter state: %#v", u.s) } msg := fmt.Sprintf("hook failed: %q", u.currentHookName()) // Create error information for status. data := params.StatusData{"hook": u.currentHookName()} if u.s.Hook.Kind.IsRelation() { data["relation-id"] = u.s.Hook.RelationId if u.s.Hook.RemoteUnit != "" { data["remote-unit"] = u.s.Hook.RemoteUnit } } if err = u.unit.SetStatus(params.StatusError, msg, data); err != nil { return nil, err } u.f.WantResolvedEvent() u.f.WantUpgradeEvent(true) for { select { case <-u.tomb.Dying(): return nil, tomb.ErrDying case rm := <-u.f.ResolvedEvents(): switch rm { case params.ResolvedRetryHooks: err = u.runHook(*u.s.Hook) case params.ResolvedNoHooks: err = u.commitHook(*u.s.Hook) default: return nil, fmt.Errorf("unknown resolved mode %q", rm) } if e := u.f.ClearResolved(); e != nil { return nil, e } if err == errHookFailed { continue } else if err != nil { return nil, err } return ModeContinue, nil case curl := <-u.f.UpgradeEvents(): return ModeUpgrading(curl), nil } } } // ModeConflicted is responsible for watching and responding to: // * user resolution of charm upgrade conflicts // * forced charm upgrade requests func ModeConflicted(curl *charm.URL) Mode { return func(u *Uniter) (next Mode, err error) { defer modeContext("ModeConflicted", &err)() // TODO(mue) Add helpful data here too in later CL. if err = u.unit.SetStatus(params.StatusError, "upgrade failed", nil); err != nil { return nil, err } u.f.WantResolvedEvent() u.f.WantUpgradeEvent(true) select { case <-u.tomb.Dying(): return nil, tomb.ErrDying case <-u.f.ResolvedEvents(): err = u.deployer.NotifyResolved() if e := u.f.ClearResolved(); e != nil { return nil, e } if err != nil { return nil, err } case curl = <-u.f.UpgradeEvents(): if err := u.deployer.NotifyRevert(); err != nil { return nil, err } } return ModeUpgrading(curl), nil } } // modeContext returns a function that implements logging and common error // manipulation for Mode funcs. func modeContext(name string, err *error) func() { logger.Infof("%s starting", name) return func() { logger.Debugf("%s exiting", name) switch *err { case nil, tomb.ErrDying, worker.ErrTerminateAgent: default: *err = stderrors.New(name + ": " + (*err).Error()) } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/filter_test.go���������������������������0000644�0000153�0000161�00000031163�12321735642�026363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "fmt" "time" gc "launchpad.net/gocheck" "launchpad.net/tomb" "launchpad.net/juju-core/charm" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" apiuniter "launchpad.net/juju-core/state/api/uniter" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker" ) type FilterSuite struct { jujutesting.JujuConnSuite wordpress *state.Service unit *state.Unit mysqlcharm *state.Charm wpcharm *state.Charm st *api.State uniter *apiuniter.State } var _ = gc.Suite(&FilterSuite{}) func (s *FilterSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.wpcharm = s.AddTestingCharm(c, "wordpress") s.wordpress = s.AddTestingService(c, "wordpress", s.wpcharm) var err error s.unit, err = s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = s.unit.AssignToNewMachine() c.Assert(err, gc.IsNil) mid, err := s.unit.AssignedMachineId() c.Assert(err, gc.IsNil) machine, err := s.State.Machine(mid) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("i-exist", "fake_nonce", nil) c.Assert(err, gc.IsNil) s.APILogin(c, s.unit) } func (s *FilterSuite) APILogin(c *gc.C, unit *state.Unit) { password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = unit.SetPassword(password) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAs(c, unit.Tag(), password) s.uniter = s.st.Uniter() c.Assert(s.uniter, gc.NotNil) } func (s *FilterSuite) TestUnitDeath(c *gc.C) { f, err := newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer f.Stop() // no AssertStop, we test for an error below asserter := coretesting.NotifyAsserterC{ Precond: func() { s.BackingState.StartSync() }, C: c, Chan: f.UnitDying(), } asserter.AssertNoReceive() // Irrelevant change. err = s.unit.SetResolved(state.ResolvedRetryHooks) c.Assert(err, gc.IsNil) asserter.AssertNoReceive() // Set dying. err = s.unit.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) err = s.unit.Destroy() c.Assert(err, gc.IsNil) asserter.AssertClosed() // Another irrelevant change. err = s.unit.ClearResolved() c.Assert(err, gc.IsNil) asserter.AssertClosed() // Set dead. err = s.unit.EnsureDead() c.Assert(err, gc.IsNil) s.assertAgentTerminates(c, f) } func (s *FilterSuite) TestUnitRemoval(c *gc.C) { f, err := newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer f.Stop() // no AssertStop, we test for an error below // short-circuit to remove because no status set. err = s.unit.Destroy() c.Assert(err, gc.IsNil) s.assertAgentTerminates(c, f) } // Ensure we get a signal on f.Dead() func (s *FilterSuite) assertFilterDies(c *gc.C, f *filter) { asserter := coretesting.NotifyAsserterC{ Precond: func() { s.BackingState.StartSync() }, C: c, Chan: f.Dead(), } asserter.AssertClosed() } func (s *FilterSuite) assertAgentTerminates(c *gc.C, f *filter) { s.assertFilterDies(c, f) c.Assert(f.Wait(), gc.Equals, worker.ErrTerminateAgent) } func (s *FilterSuite) TestServiceDeath(c *gc.C) { f, err := newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, f) dyingAsserter := coretesting.NotifyAsserterC{ C: c, Precond: func() { s.BackingState.StartSync() }, Chan: f.UnitDying(), } dyingAsserter.AssertNoReceive() err = s.unit.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) err = s.wordpress.Destroy() c.Assert(err, gc.IsNil) timeout := time.After(coretesting.LongWait) loop: for { select { case <-f.UnitDying(): break loop case <-time.After(coretesting.ShortWait): s.BackingState.StartSync() case <-timeout: c.Fatalf("dead not detected") } } err = s.unit.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.unit.Life(), gc.Equals, state.Dying) // Can't set s.wordpress to Dead while it still has units. } func (s *FilterSuite) TestResolvedEvents(c *gc.C) { f, err := newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, f) resolvedAsserter := coretesting.ContentAsserterC{ C: c, Precond: func() { s.BackingState.StartSync() }, Chan: f.ResolvedEvents(), } resolvedAsserter.AssertNoReceive() // Request an event; no interesting event is available. f.WantResolvedEvent() resolvedAsserter.AssertNoReceive() // Change the unit in an irrelevant way; no events. err = s.unit.SetStatus(params.StatusError, "blarg", nil) c.Assert(err, gc.IsNil) resolvedAsserter.AssertNoReceive() // Change the unit's resolved to an interesting value; new event received. err = s.unit.SetResolved(state.ResolvedRetryHooks) c.Assert(err, gc.IsNil) assertChange := func(expect params.ResolvedMode) { rm := resolvedAsserter.AssertOneReceive().(params.ResolvedMode) c.Assert(rm, gc.Equals, expect) } assertChange(params.ResolvedRetryHooks) // Ask for the event again, and check it's resent. f.WantResolvedEvent() assertChange(params.ResolvedRetryHooks) // Clear the resolved status *via the filter*; check not resent... err = f.ClearResolved() c.Assert(err, gc.IsNil) resolvedAsserter.AssertNoReceive() // ...even when requested. f.WantResolvedEvent() resolvedAsserter.AssertNoReceive() // Induce several events; only latest state is reported. err = s.unit.SetResolved(state.ResolvedRetryHooks) c.Assert(err, gc.IsNil) err = f.ClearResolved() c.Assert(err, gc.IsNil) err = s.unit.SetResolved(state.ResolvedNoHooks) c.Assert(err, gc.IsNil) assertChange(params.ResolvedNoHooks) } func (s *FilterSuite) TestCharmUpgradeEvents(c *gc.C) { oldCharm := s.AddTestingCharm(c, "upgrade1") svc := s.AddTestingService(c, "upgradetest", oldCharm) unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) s.APILogin(c, unit) f, err := newFilter(s.uniter, unit.Tag()) c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, f) // No initial event is sent. assertNoChange := func() { s.BackingState.StartSync() select { case sch := <-f.UpgradeEvents(): c.Fatalf("unexpected %#v", sch) case <-time.After(coretesting.ShortWait): } } assertNoChange() // Setting a charm generates no new events if it already matches. err = f.SetCharm(oldCharm.URL()) c.Assert(err, gc.IsNil) assertNoChange() // Explicitly request an event relative to the existing state; nothing. f.WantUpgradeEvent(false) assertNoChange() // Change the service in an irrelevant way; no events. err = svc.SetExposed() c.Assert(err, gc.IsNil) assertNoChange() // Change the service's charm; new event received. newCharm := s.AddTestingCharm(c, "upgrade2") err = svc.SetCharm(newCharm, false) c.Assert(err, gc.IsNil) assertChange := func(url *charm.URL) { s.BackingState.StartSync() select { case upgradeCharm := <-f.UpgradeEvents(): c.Assert(upgradeCharm, gc.DeepEquals, url) case <-time.After(coretesting.LongWait): c.Fatalf("timed out") } } assertChange(newCharm.URL()) assertNoChange() // Request a new upgrade *unforced* upgrade event, we should see one. f.WantUpgradeEvent(false) assertChange(newCharm.URL()) assertNoChange() // Request only *forced* upgrade events; nothing. f.WantUpgradeEvent(true) assertNoChange() // But when we have a forced upgrade to the same URL, no new event. err = svc.SetCharm(oldCharm, true) c.Assert(err, gc.IsNil) assertNoChange() // ...but a *forced* change to a different URL should generate an event. err = svc.SetCharm(newCharm, true) assertChange(newCharm.URL()) assertNoChange() } func (s *FilterSuite) TestConfigEvents(c *gc.C) { f, err := newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, f) // Test no changes before the charm URL is set. assertNoChange := func() { s.BackingState.StartSync() select { case <-f.ConfigEvents(): c.Fatalf("unexpected config event") case <-time.After(coretesting.ShortWait): } } assertNoChange() // Set the charm URL to trigger config events. err = f.SetCharm(s.wpcharm.URL()) c.Assert(err, gc.IsNil) assertChange := func() { s.BackingState.StartSync() select { case _, ok := <-f.ConfigEvents(): c.Assert(ok, gc.Equals, true) case <-time.After(coretesting.LongWait): c.Fatalf("timed out") } assertNoChange() } assertChange() // Change the config; new event received. changeConfig := func(title interface{}) { err := s.wordpress.UpdateConfigSettings(charm.Settings{ "blog-title": title, }) c.Assert(err, gc.IsNil) } changeConfig("20,000 leagues in the cloud") assertChange() // Change the config a few more times, then reset the events. We sync to // make sure the events have arrived in the watcher -- and then wait a // little longer, to allow for the delay while the events are coalesced // -- before we tell it to discard all received events. This would be // much better tested by controlling a mocked-out watcher directly, but // that's a bit inconvenient for this change. changeConfig(nil) changeConfig("the curious incident of the dog in the cloud") s.BackingState.StartSync() time.Sleep(250 * time.Millisecond) f.DiscardConfigEvent() assertNoChange() // Check that a filter's initial event works with DiscardConfigEvent // as expected. f, err = newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, f) s.BackingState.StartSync() f.DiscardConfigEvent() assertNoChange() // Further changes are still collapsed as appropriate. changeConfig("forsooth") changeConfig("imagination failure") assertChange() } func (s *FilterSuite) TestCharmErrorEvents(c *gc.C) { f, err := newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer f.Stop() // no AssertStop, we test for an error below assertNoChange := func() { s.BackingState.StartSync() select { case <-f.ConfigEvents(): c.Fatalf("unexpected config event") case <-time.After(coretesting.ShortWait): } } // Check setting an invalid charm URL does not send events. err = f.SetCharm(charm.MustParseURL("cs:missing/one-1")) c.Assert(err, gc.Equals, tomb.ErrDying) assertNoChange() s.assertFilterDies(c, f) // Filter died after the error, so restart it. f, err = newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer f.Stop() // no AssertStop, we test for an error below // Check with a nil charm URL, again no changes. err = f.SetCharm(nil) c.Assert(err, gc.Equals, tomb.ErrDying) assertNoChange() s.assertFilterDies(c, f) } func (s *FilterSuite) TestRelationsEvents(c *gc.C) { f, err := newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, f) assertNoChange := func() { s.BackingState.StartSync() select { case ids := <-f.RelationsEvents(): c.Fatalf("unexpected relations event %#v", ids) case <-time.After(coretesting.ShortWait): } } assertNoChange() // Add a couple of relations; check the event. rel0 := s.addRelation(c) rel1 := s.addRelation(c) assertChange := func(expect []int) { s.BackingState.StartSync() select { case got := <-f.RelationsEvents(): c.Assert(got, gc.DeepEquals, expect) case <-time.After(coretesting.LongWait): c.Fatalf("timed out") } assertNoChange() } assertChange([]int{0, 1}) // Add another relation, and change another's Life (by entering scope before // Destroy, thereby setting the relation to Dying); check event. s.addRelation(c) ru0, err := rel0.Unit(s.unit) c.Assert(err, gc.IsNil) err = ru0.EnterScope(nil) c.Assert(err, gc.IsNil) err = rel0.Destroy() c.Assert(err, gc.IsNil) assertChange([]int{0, 2}) // Remove a relation completely; check no event, because the relation // could not have been removed if the unit was in scope, and therefore // the uniter never needs to hear about it. err = rel1.Destroy() c.Assert(err, gc.IsNil) assertNoChange() err = f.Stop() c.Assert(err, gc.IsNil) // Start a new filter, check initial event. f, err = newFilter(s.uniter, s.unit.Tag()) c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, f) assertChange([]int{0, 2}) // Check setting the charm URL generates all new relation events. err = f.SetCharm(s.wpcharm.URL()) c.Assert(err, gc.IsNil) assertChange([]int{0, 2}) } func (s *FilterSuite) addRelation(c *gc.C) *state.Relation { if s.mysqlcharm == nil { s.mysqlcharm = s.AddTestingCharm(c, "mysql") } rels, err := s.wordpress.Relations() c.Assert(err, gc.IsNil) svcName := fmt.Sprintf("mysql%d", len(rels)) s.AddTestingService(c, svcName, s.mysqlcharm) eps, err := s.State.InferEndpoints([]string{svcName, "wordpress"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) return rel } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/hook/������������������������������������0000755�0000153�0000161�00000000000�12321735642�024444� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/hook/hook.go�����������������������������0000644�0000153�0000161�00000002577�12321735642�025746� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // hook provides types that define the hooks known to the Uniter package hook import ( "fmt" "launchpad.net/juju-core/charm/hooks" ) // Info holds details required to execute a hook. Not all fields are // relevant to all Kind values. type Info struct { Kind hooks.Kind // RelationId identifies the relation associated with the hook. It is // only set when Kind indicates a relation hook. RelationId int `yaml:"relation-id,omitempty"` // RemoteUnit is the name of the unit that triggered the hook. It is only // set when Kind inicates a relation hook other than relation-broken. RemoteUnit string `yaml:"remote-unit,omitempty"` // ChangeVersion identifies the most recent unit settings change // associated with RemoteUnit. It is only set when RemoteUnit is set. ChangeVersion int64 `yaml:"change-version,omitempty"` } // Validate returns an error if the info is not valid. func (hi Info) Validate() error { switch hi.Kind { case hooks.RelationJoined, hooks.RelationChanged, hooks.RelationDeparted: if hi.RemoteUnit == "" { return fmt.Errorf("%q hook requires a remote unit", hi.Kind) } fallthrough case hooks.Install, hooks.Start, hooks.ConfigChanged, hooks.UpgradeCharm, hooks.Stop, hooks.RelationBroken: return nil } return fmt.Errorf("unknown hook kind %q", hi.Kind) } ���������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/hook/hook_test.go������������������������0000644�0000153�0000161�00000002704�12321735642�026775� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package hook_test import ( "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/worker/uniter/hook" ) func Test(t *testing.T) { gc.TestingT(t) } type InfoSuite struct{} var _ = gc.Suite(&InfoSuite{}) var validateTests = []struct { info hook.Info err string }{ { hook.Info{Kind: hooks.RelationJoined}, `"relation-joined" hook requires a remote unit`, }, { hook.Info{Kind: hooks.RelationChanged}, `"relation-changed" hook requires a remote unit`, }, { hook.Info{Kind: hooks.RelationDeparted}, `"relation-departed" hook requires a remote unit`, }, { hook.Info{Kind: hooks.Kind("grok")}, `unknown hook kind "grok"`, }, {hook.Info{Kind: hooks.Install}, ""}, {hook.Info{Kind: hooks.Start}, ""}, {hook.Info{Kind: hooks.ConfigChanged}, ""}, {hook.Info{Kind: hooks.UpgradeCharm}, ""}, {hook.Info{Kind: hooks.Stop}, ""}, {hook.Info{Kind: hooks.RelationJoined, RemoteUnit: "x"}, ""}, {hook.Info{Kind: hooks.RelationChanged, RemoteUnit: "x"}, ""}, {hook.Info{Kind: hooks.RelationDeparted, RemoteUnit: "x"}, ""}, {hook.Info{Kind: hooks.RelationBroken}, ""}, } func (s *InfoSuite) TestValidate(c *gc.C) { for i, t := range validateTests { c.Logf("test %d", i) err := t.info.Validate() if t.err == "" { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, t.err) } } } ������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/filter.go��������������������������������0000644�0000153�0000161�00000036141�12321735642�025325� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "sort" "github.com/juju/loggo" "launchpad.net/tomb" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/uniter" apiwatcher "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/worker" ) var filterLogger = loggo.GetLogger("juju.worker.uniter.filter") // filter collects unit, service, and service config information from separate // state watchers, and presents it as events on channels designed specifically // for the convenience of the uniter. type filter struct { st *uniter.State tomb tomb.Tomb // outUnitDying is closed when the unit's life becomes Dying. outUnitDying chan struct{} // The out*On chans are used to deliver events to clients. // The out* chans, when set to the corresponding out*On chan (rather than // nil) indicate that an event of the appropriate type is ready to send // to the client. outConfig chan struct{} outConfigOn chan struct{} outUpgrade chan *charm.URL outUpgradeOn chan *charm.URL outResolved chan params.ResolvedMode outResolvedOn chan params.ResolvedMode outRelations chan []int outRelationsOn chan []int // The want* chans are used to indicate that the filter should send // events if it has them available. wantForcedUpgrade chan bool wantResolved chan struct{} // discardConfig is used to indicate that any pending config event // should be discarded. discardConfig chan struct{} // setCharm is used to request that the unit's charm URL be set to // a new value. This must be done in the filter's goroutine, so // that config watches can be stopped and restarted pointing to // the new charm URL. If we don't stop the watch before the // (potentially) last reference to that settings document is // removed, we'll see spurious errors (and even in the best case, // we risk getting notifications for the wrong settings version). setCharm chan *charm.URL // didSetCharm is used to report back after setting a charm URL. didSetCharm chan struct{} // clearResolved is used to request that the unit's resolved flag // be cleared. This must be done on the filter's goroutine so that // it can immediately trigger the unit change handler, and thus // ensure that subsquent requests for resolved events -- that land // before the next watcher update for the unit -- do not erroneously // send out stale values. clearResolved chan struct{} // didClearResolved is used to report back after clearing the resolved // flag. didClearResolved chan struct{} // The following fields hold state that is collected while running, // and used to detect interesting changes to express as events. unit *uniter.Unit life params.Life resolved params.ResolvedMode service *uniter.Service upgradeFrom serviceCharm upgradeAvailable serviceCharm upgrade *charm.URL relations []int } // newFilter returns a filter that handles state changes pertaining to the // supplied unit. func newFilter(st *uniter.State, unitTag string) (*filter, error) { f := &filter{ st: st, outUnitDying: make(chan struct{}), outConfig: make(chan struct{}), outConfigOn: make(chan struct{}), outUpgrade: make(chan *charm.URL), outUpgradeOn: make(chan *charm.URL), outResolved: make(chan params.ResolvedMode), outResolvedOn: make(chan params.ResolvedMode), outRelations: make(chan []int), outRelationsOn: make(chan []int), wantForcedUpgrade: make(chan bool), wantResolved: make(chan struct{}), discardConfig: make(chan struct{}), setCharm: make(chan *charm.URL), didSetCharm: make(chan struct{}), clearResolved: make(chan struct{}), didClearResolved: make(chan struct{}), } go func() { defer f.tomb.Done() err := f.loop(unitTag) filterLogger.Errorf("%v", err) f.tomb.Kill(err) }() return f, nil } func (f *filter) Stop() error { f.tomb.Kill(nil) return f.tomb.Wait() } func (f *filter) Dead() <-chan struct{} { return f.tomb.Dead() } func (f *filter) Wait() error { return f.tomb.Wait() } // UnitDying returns a channel which is closed when the Unit enters a Dying state. func (f *filter) UnitDying() <-chan struct{} { return f.outUnitDying } // UpgradeEvents returns a channel that will receive a new charm URL whenever an // upgrade is indicated. Events should not be read until the baseline state // has been specified by calling WantUpgradeEvent. func (f *filter) UpgradeEvents() <-chan *charm.URL { return f.outUpgradeOn } // ResolvedEvents returns a channel that may receive a ResolvedMode when the // unit's Resolved value changes, or when an event is explicitly requested. // A ResolvedNone state will never generate events, but ResolvedRetryHooks and // ResolvedNoHooks will always be delivered as described. func (f *filter) ResolvedEvents() <-chan params.ResolvedMode { return f.outResolvedOn } // ConfigEvents returns a channel that will receive a signal whenever the service's // configuration changes, or when an event is explicitly requested. func (f *filter) ConfigEvents() <-chan struct{} { return f.outConfigOn } // RelationsEvents returns a channel that will receive the ids of all the service's // relations whose Life status has changed. func (f *filter) RelationsEvents() <-chan []int { return f.outRelationsOn } // WantUpgradeEvent controls whether the filter will generate upgrade // events for unforced service charm changes. func (f *filter) WantUpgradeEvent(mustForce bool) { select { case <-f.tomb.Dying(): case f.wantForcedUpgrade <- mustForce: } } // SetCharm notifies the filter that the unit is running a new // charm. It causes the unit's charm URL to be set in state, and the // following changes to the filter's behaviour: // // * Upgrade events will only be generated for charms different to // that supplied; // * A fresh relations event will be generated containing every relation // the service is participating in; // * A fresh configuration event will be generated, and subsequent // events will only be sent in response to changes in the version // of the service's settings that is specific to that charm. // // SetCharm blocks until the charm URL is set in state, returning any // error that occurred. func (f *filter) SetCharm(curl *charm.URL) error { select { case <-f.tomb.Dying(): return tomb.ErrDying case f.setCharm <- curl: } select { case <-f.tomb.Dying(): return tomb.ErrDying case <-f.didSetCharm: return nil } } // WantResolvedEvent indicates that the filter should send a resolved event // if one is available. func (f *filter) WantResolvedEvent() { select { case <-f.tomb.Dying(): case f.wantResolved <- nothing: } } // ClearResolved notifies the filter that a resolved event has been handled // and should not be reported again. func (f *filter) ClearResolved() error { select { case <-f.tomb.Dying(): return tomb.ErrDying case f.clearResolved <- nothing: } select { case <-f.tomb.Dying(): return tomb.ErrDying case <-f.didClearResolved: filterLogger.Debugf("resolved clear completed") return nil } } // DiscardConfigEvent indicates that the filter should discard any pending // config event. func (f *filter) DiscardConfigEvent() { select { case <-f.tomb.Dying(): case f.discardConfig <- nothing: } } func (f *filter) maybeStopWatcher(w watcher.Stopper) { if w != nil { watcher.Stop(w, &f.tomb) } } func (f *filter) loop(unitTag string) (err error) { defer func() { if params.IsCodeNotFoundOrCodeUnauthorized(err) { err = worker.ErrTerminateAgent } }() if f.unit, err = f.st.Unit(unitTag); err != nil { return err } if err = f.unitChanged(); err != nil { return err } f.service, err = f.unit.Service() if err != nil { return err } if err = f.serviceChanged(); err != nil { return err } unitw, err := f.unit.Watch() if err != nil { return err } defer f.maybeStopWatcher(unitw) servicew, err := f.service.Watch() if err != nil { return err } defer f.maybeStopWatcher(servicew) // configw and relationsw can get restarted, so we need to use // their eventual values in the defer calls. var configw apiwatcher.NotifyWatcher var configChanges <-chan struct{} curl, err := f.unit.CharmURL() if err == nil { configw, err = f.unit.WatchConfigSettings() if err != nil { return err } configChanges = configw.Changes() f.upgradeFrom.url = curl } else if err != uniter.ErrNoCharmURLSet { filterLogger.Errorf("unit charm: %v", err) return err } defer func() { if configw != nil { watcher.Stop(configw, &f.tomb) } }() relationsw, err := f.service.WatchRelations() if err != nil { return err } defer func() { if relationsw != nil { watcher.Stop(relationsw, &f.tomb) } }() // Config events cannot be meaningfully discarded until one is available; // once we receive the initial change, we unblock discard requests by // setting this channel to its namesake on f. var discardConfig chan struct{} for { var ok bool select { case <-f.tomb.Dying(): return tomb.ErrDying // Handle watcher changes. case _, ok = <-unitw.Changes(): filterLogger.Debugf("got unit change") if !ok { return watcher.MustErr(unitw) } if err = f.unitChanged(); err != nil { return err } case _, ok = <-servicew.Changes(): filterLogger.Debugf("got service change") if !ok { return watcher.MustErr(servicew) } if err = f.serviceChanged(); err != nil { return err } case _, ok = <-configChanges: filterLogger.Debugf("got config change") if !ok { return watcher.MustErr(configw) } filterLogger.Debugf("preparing new config event") f.outConfig = f.outConfigOn discardConfig = f.discardConfig case keys, ok := <-relationsw.Changes(): filterLogger.Debugf("got relations change") if !ok { return watcher.MustErr(relationsw) } var ids []int for _, key := range keys { relationTag := names.RelationTag(key) rel, err := f.st.Relation(relationTag) if params.IsCodeNotFoundOrCodeUnauthorized(err) { // If it's actually gone, this unit cannot have entered // scope, and therefore never needs to know about it. } else if err != nil { return err } else { ids = append(ids, rel.Id()) } } f.relationsChanged(ids) // Send events on active out chans. case f.outUpgrade <- f.upgrade: filterLogger.Debugf("sent upgrade event") f.outUpgrade = nil case f.outResolved <- f.resolved: filterLogger.Debugf("sent resolved event") f.outResolved = nil case f.outConfig <- nothing: filterLogger.Debugf("sent config event") f.outConfig = nil case f.outRelations <- f.relations: filterLogger.Debugf("sent relations event") f.outRelations = nil f.relations = nil // Handle explicit requests. case curl := <-f.setCharm: filterLogger.Debugf("changing charm to %q", curl) // We need to restart the config watcher after setting the // charm, because service config settings are distinct for // different service charms. if configw != nil { if err := configw.Stop(); err != nil { return err } } if err := f.unit.SetCharmURL(curl); err != nil { filterLogger.Debugf("failed setting charm url %q: %v", curl, err) return err } select { case <-f.tomb.Dying(): return tomb.ErrDying case f.didSetCharm <- nothing: } configw, err = f.unit.WatchConfigSettings() if err != nil { return err } configChanges = configw.Changes() // Restart the relations watcher. if err := relationsw.Stop(); err != nil { return err } relationsw, err = f.service.WatchRelations() if err != nil { return err } f.upgradeFrom.url = curl if err = f.upgradeChanged(); err != nil { return err } case force := <-f.wantForcedUpgrade: filterLogger.Debugf("want forced upgrade %v", force) f.upgradeFrom.force = force if err = f.upgradeChanged(); err != nil { return err } case <-f.wantResolved: filterLogger.Debugf("want resolved event") if f.resolved != params.ResolvedNone { f.outResolved = f.outResolvedOn } case <-f.clearResolved: filterLogger.Debugf("resolved event handled") f.outResolved = nil if err := f.unit.ClearResolved(); err != nil { return err } if err := f.unitChanged(); err != nil { return err } select { case <-f.tomb.Dying(): return tomb.ErrDying case f.didClearResolved <- nothing: } case <-discardConfig: filterLogger.Debugf("discarded config event") f.outConfig = nil } } } // unitChanged responds to changes in the unit. func (f *filter) unitChanged() error { if err := f.unit.Refresh(); err != nil { return err } if f.life != f.unit.Life() { switch f.life = f.unit.Life(); f.life { case params.Dying: filterLogger.Infof("unit is dying") close(f.outUnitDying) f.outUpgrade = nil case params.Dead: filterLogger.Infof("unit is dead") return worker.ErrTerminateAgent } } resolved, err := f.unit.Resolved() if err != nil { return err } if resolved != f.resolved { f.resolved = resolved if f.resolved != params.ResolvedNone { f.outResolved = f.outResolvedOn } } return nil } // serviceChanged responds to changes in the service. func (f *filter) serviceChanged() error { if err := f.service.Refresh(); err != nil { return err } url, force, err := f.service.CharmURL() if err != nil { return err } f.upgradeAvailable = serviceCharm{url, force} switch f.service.Life() { case params.Dying: if err := f.unit.Destroy(); err != nil { return err } case params.Dead: filterLogger.Infof("service is dead") return worker.ErrTerminateAgent } return f.upgradeChanged() } // upgradeChanged responds to changes in the service or in the // upgrade requests that defines which charm changes should be // delivered as upgrades. func (f *filter) upgradeChanged() (err error) { if f.life != params.Alive { filterLogger.Debugf("charm check skipped, unit is dying") f.outUpgrade = nil return nil } if f.upgradeFrom.url == nil { filterLogger.Debugf("charm check skipped, not yet installed.") f.outUpgrade = nil return nil } if *f.upgradeAvailable.url != *f.upgradeFrom.url { if f.upgradeAvailable.force || !f.upgradeFrom.force { filterLogger.Debugf("preparing new upgrade event") if f.upgrade == nil || *f.upgrade != *f.upgradeAvailable.url { f.upgrade = f.upgradeAvailable.url } f.outUpgrade = f.outUpgradeOn return nil } } filterLogger.Debugf("no new charm event") f.outUpgrade = nil return nil } // relationsChanged responds to service relation changes. func (f *filter) relationsChanged(ids []int) { outer: for _, id := range ids { for _, existing := range f.relations { if id == existing { continue outer } } f.relations = append(f.relations, id) } if len(f.relations) != 0 { sort.Ints(f.relations) f.outRelations = f.outRelationsOn } } // serviceCharm holds information about a charm. type serviceCharm struct { url *charm.URL force bool } // nothing is marginally more pleasant to read than "struct{}{}". var nothing = struct{}{} �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/uniter.go��������������������������������0000644�0000153�0000161�00000052001�12321735642�025337� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( stderrors "errors" "fmt" "math/rand" "os" "path/filepath" "strings" "sync" "time" "github.com/juju/loggo" "launchpad.net/tomb" "launchpad.net/juju-core/agent/tools" corecharm "launchpad.net/juju-core/charm" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/uniter" apiwatcher "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/state/watcher" "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" "launchpad.net/juju-core/worker/uniter/relation" ) var logger = loggo.GetLogger("juju.worker.uniter") const ( // These work fine for linux, but should we need to work with windows // workloads in the future, we'll need to move these into a file that is // compiled conditionally for different targets and use tcp (most likely). RunListenerFile = "run.socket" ) // A UniterExecutionObserver gets the appropriate methods called when a hook // is executed and either succeeds or fails. Missing hooks don't get reported // in this way. type UniterExecutionObserver interface { HookCompleted(hookName string) HookFailed(hookName string) } // Uniter implements the capabilities of the unit agent. It is not intended to // implement the actual *behaviour* of the unit agent; that responsibility is // delegated to Mode values, which are expected to react to events and direct // the uniter's responses to them. type Uniter struct { tomb tomb.Tomb st *uniter.State f *filter unit *uniter.Unit service *uniter.Service relationers map[int]*Relationer relationHooks chan hook.Info uuid string envName string dataDir string baseDir string toolsDir string relationsDir string charm *charm.GitDir deployer charm.Deployer s *State sf *StateFile rand *rand.Rand hookLock *fslock.Lock runListener *RunListener proxy osenv.ProxySettings proxyMutex sync.Mutex ranConfigChanged bool // The execution observer is only used in tests at this stage. Should this // need to be extended, perhaps a list of observers would be needed. observer UniterExecutionObserver } // NewUniter creates a new Uniter which will install, run, and upgrade // a charm on behalf of the unit with the given unitTag, by executing // hooks and operations provoked by changes in st. func NewUniter(st *uniter.State, unitTag string, dataDir string) *Uniter { u := &Uniter{ st: st, dataDir: dataDir, } go func() { defer u.tomb.Done() u.tomb.Kill(u.loop(unitTag)) }() return u } func (u *Uniter) loop(unitTag string) (err error) { if err = u.init(unitTag); err != nil { return err } defer u.runListener.Close() logger.Infof("unit %q started", u.unit) environWatcher, err := u.st.WatchForEnvironConfigChanges() if err != nil { return err } defer watcher.Stop(environWatcher, &u.tomb) u.watchForProxyChanges(environWatcher) // Start filtering state change events for consumption by modes. u.f, err = newFilter(u.st, unitTag) if err != nil { return err } defer watcher.Stop(u.f, &u.tomb) go func() { u.tomb.Kill(u.f.Wait()) }() // Run modes until we encounter an error. mode := ModeContinue for err == nil { select { case <-u.tomb.Dying(): err = tomb.ErrDying default: mode, err = mode(u) } } logger.Infof("unit %q shutting down: %s", u.unit, err) return err } func (u *Uniter) setupLocks() (err error) { lockDir := filepath.Join(u.dataDir, "locks") u.hookLock, err = fslock.NewLock(lockDir, "uniter-hook-execution") if err != nil { return err } if message := u.hookLock.Message(); u.hookLock.IsLocked() && message != "" { // Look to see if it was us that held the lock before. If it was, we // should be safe enough to break it, as it is likely that we died // before unlocking, and have been restarted by upstart. parts := strings.SplitN(message, ":", 2) if len(parts) > 1 && parts[0] == u.unit.Name() { if err := u.hookLock.BreakLock(); err != nil { return err } } } return nil } func (u *Uniter) init(unitTag string) (err error) { defer utils.ErrorContextf(&err, "failed to initialize uniter for %q", unitTag) u.unit, err = u.st.Unit(unitTag) 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 } u.toolsDir = tools.ToolsDir(u.dataDir, unitTag) if err := EnsureJujucSymlinks(u.toolsDir); err != nil { return err } u.baseDir = filepath.Join(u.dataDir, "agents", unitTag) u.relationsDir = filepath.Join(u.baseDir, "state", "relations") if err := os.MkdirAll(u.relationsDir, 0755); err != nil { return err } u.service, err = u.st.Service(u.unit.ServiceTag()) if err != nil { return err } var env *uniter.Environment env, err = u.st.Environment() if err != nil { return err } u.uuid = env.UUID() u.envName = env.Name() u.relationers = map[int]*Relationer{} u.relationHooks = make(chan hook.Info) u.charm = charm.NewGitDir(filepath.Join(u.baseDir, "charm")) deployerPath := filepath.Join(u.baseDir, "state", "deployer") bundles := charm.NewBundlesDir(filepath.Join(u.baseDir, "state", "bundles")) 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())) // 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() { u.tomb.Kill(nil) } func (u *Uniter) Wait() error { return u.tomb.Wait() } func (u *Uniter) Stop() error { u.tomb.Kill(nil) return u.Wait() } func (u *Uniter) Dead() <-chan struct{} { return u.tomb.Dead() } // writeState saves uniter state with the supplied values, and infers the appropriate // value of Started. func (u *Uniter) writeState(op Op, step OpStep, hi *hook.Info, url *corecharm.URL) error { s := State{ Started: op == RunHook && hi.Kind == hooks.Start || u.s != nil && u.s.Started, Op: op, OpStep: step, Hook: hi, CharmURL: url, } if err := u.sf.Write(s.Started, s.Op, s.OpStep, s.Hook, s.CharmURL); err != nil { return err } u.s = &s return nil } // deploy deploys the supplied charm URL, and sets follow-up hook operation state // as indicated by reason. func (u *Uniter) deploy(curl *corecharm.URL, reason Op) error { if reason != Install && reason != Upgrade { panic(fmt.Errorf("%q is not a deploy operation", reason)) } var hi *hook.Info if u.s != nil && (u.s.Op == RunHook || u.s.Op == Upgrade) { // If this upgrade interrupts a RunHook, we need to preserve the hook // info so that we can return to the appropriate error state. However, // if we're resuming (or have force-interrupted) an Upgrade, we also // need to preserve whatever hook info was preserved when we initially // started upgrading, to ensure we still return to the correct state. hi = u.s.Hook } if u.s == nil || u.s.OpStep != Done { // Get the new charm bundle before announcing intention to use it. logger.Infof("fetching charm %q", curl) sch, err := u.st.Charm(curl) if err != nil { return err } if err = u.deployer.Stage(sch, u.tomb.Dying()); err != nil { return err } // Set the new charm URL - this returns when the operation is complete, // at which point we can refresh the local copy of the unit to get a // version with the correct charm URL, and can go ahead and deploy // the charm proper. if err := u.f.SetCharm(curl); err != nil { return err } if err := u.unit.Refresh(); err != nil { return err } logger.Infof("deploying charm %q", curl) if err = u.writeState(reason, Pending, hi, curl); err != nil { return err } if err = u.deployer.Deploy(); err != nil { return err } if err = u.writeState(reason, Done, hi, curl); err != nil { return err } } logger.Infof("charm %q is deployed", curl) status := Queued if hi != nil { // If a hook operation was interrupted, restore it. status = Pending } else { // Otherwise, queue the relevant post-deploy hook. hi = &hook.Info{} switch reason { case Install: hi.Kind = hooks.Install case Upgrade: hi.Kind = hooks.UpgradeCharm } } return u.writeState(RunHook, status, hi, nil) } // errHookFailed indicates that a hook failed to execute, but that the Uniter's // operation is not affected by the error. var errHookFailed = stderrors.New("hook execution failed") func (u *Uniter) getHookContext(hctxId string, relationId int, remoteUnitName string) (context *HookContext, err error) { apiAddrs, err := u.st.APIAddresses() if err != nil { return nil, err } ownerTag, err := u.service.GetOwnerTag() if err != nil { return nil, err } ctxRelations := map[int]*ContextRelation{} for id, r := range u.relationers { ctxRelations[id] = r.Context() } u.proxyMutex.Lock() defer u.proxyMutex.Unlock() // Make a copy of the proxy settings. proxySettings := u.proxy return NewHookContext(u.unit, hctxId, u.uuid, u.envName, relationId, remoteUnitName, ctxRelations, apiAddrs, ownerTag, proxySettings) } func (u *Uniter) acquireHookLock(message string) (err error) { // We want to make sure we don't block forever when locking, but take the // tomb into account. checkTomb := func() error { select { case <-u.tomb.Dying(): return tomb.ErrDying default: // no-op to fall through to return. } return nil } if err = u.hookLock.LockWithFunc(message, checkTomb); err != nil { return err } return nil } func (u *Uniter) startJujucServer(context *HookContext) (*jujuc.Server, string, error) { // Prepare server. getCmd := func(ctxId, cmdName string) (cmd.Command, error) { // TODO: switch to long-running server with single context; // use nonce in place of context id. if ctxId != context.id { return nil, fmt.Errorf("expected context id %q, got %q", context.id, ctxId) } return jujuc.NewCommand(context, cmdName) } socketPath := filepath.Join(u.baseDir, "agent.socket") // Use abstract namespace so we don't get stale socket files. socketPath = "@" + socketPath srv, err := jujuc.NewServer(getCmd, socketPath) if err != nil { return nil, "", err } go srv.Run() return srv, socketPath, nil } // RunCommands executes the supplied commands in a hook context. func (u *Uniter) RunCommands(commands string) (results *exec.ExecResponse, err error) { logger.Tracef("run commands: %s", commands) hctxId := fmt.Sprintf("%s:run-commands:%d", u.unit.Name(), u.rand.Int63()) lockMessage := fmt.Sprintf("%s: running commands", u.unit.Name()) if err = u.acquireHookLock(lockMessage); err != nil { return nil, err } defer u.hookLock.Unlock() hctx, err := u.getHookContext(hctxId, -1, "") if err != nil { return nil, err } srv, socketPath, err := u.startJujucServer(hctx) if err != nil { return nil, err } defer srv.Close() result, err := hctx.RunCommands(commands, u.charm.Path(), u.toolsDir, socketPath) if result != nil { logger.Tracef("run commands: rc=%v\nstdout:\n%sstderr:\n%s", result.Code, result.Stdout, result.Stderr) } return result, err } func (u *Uniter) notifyHookInternal(hook string, hctx *HookContext, method func(string)) { if r, ok := hctx.HookRelation(); ok { remote, _ := hctx.RemoteUnitName() if remote != "" { remote = " " + remote } hook = hook + remote + " " + r.FakeId() } method(hook) } func (u *Uniter) notifyHookCompleted(hook string, hctx *HookContext) { if u.observer != nil { u.notifyHookInternal(hook, hctx, u.observer.HookCompleted) } } func (u *Uniter) notifyHookFailed(hook string, hctx *HookContext) { if u.observer != nil { u.notifyHookInternal(hook, hctx, u.observer.HookFailed) } } // runHook executes the supplied hook.Info in an appropriate hook context. If // the hook itself fails to execute, it returns errHookFailed. func (u *Uniter) runHook(hi hook.Info) (err error) { // Prepare context. if err = hi.Validate(); err != nil { return err } hookName := string(hi.Kind) relationId := -1 if hi.Kind.IsRelation() { relationId = hi.RelationId if hookName, err = u.relationers[relationId].PrepareHook(hi); err != nil { return err } } hctxId := fmt.Sprintf("%s:%s:%d", u.unit.Name(), hookName, u.rand.Int63()) lockMessage := fmt.Sprintf("%s: running hook %q", u.unit.Name(), hookName) if err = u.acquireHookLock(lockMessage); err != nil { return err } defer u.hookLock.Unlock() hctx, err := u.getHookContext(hctxId, relationId, hi.RemoteUnit) if err != nil { return err } srv, socketPath, err := u.startJujucServer(hctx) if err != nil { return err } defer srv.Close() // Run the hook. if err := u.writeState(RunHook, Pending, &hi, nil); err != nil { return err } logger.Infof("running %q hook", hookName) ranHook := true err = hctx.RunHook(hookName, u.charm.Path(), u.toolsDir, socketPath) if IsMissingHookError(err) { ranHook = false } else if err != nil { logger.Errorf("hook failed: %s", err) u.notifyHookFailed(hookName, hctx) return errHookFailed } if err := u.writeState(RunHook, Done, &hi, nil); err != nil { return err } if ranHook { logger.Infof("ran %q hook", hookName) u.notifyHookCompleted(hookName, hctx) } else { logger.Infof("skipped %q hook (missing)", hookName) } return u.commitHook(hi) } // commitHook ensures that state is consistent with the supplied hook, and // that the fact of the hook's completion is persisted. func (u *Uniter) commitHook(hi hook.Info) error { logger.Infof("committing %q hook", hi.Kind) if hi.Kind.IsRelation() { if err := u.relationers[hi.RelationId].CommitHook(hi); err != nil { return err } if hi.Kind == hooks.RelationBroken { delete(u.relationers, hi.RelationId) } } if hi.Kind == hooks.ConfigChanged { u.ranConfigChanged = true } if err := u.writeState(Continue, Pending, &hi, nil); err != nil { return err } logger.Infof("committed %q hook", hi.Kind) return nil } // currentHookName returns the current full hook name. func (u *Uniter) currentHookName() string { hookInfo := u.s.Hook hookName := string(hookInfo.Kind) if hookInfo.Kind.IsRelation() { relationer := u.relationers[hookInfo.RelationId] name := relationer.ru.Endpoint().Name hookName = fmt.Sprintf("%s-%s", name, hookInfo.Kind) } return hookName } // 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 { joinedRelations, err := u.getJoinedRelations() if err != nil { return err } 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 } } for id, rel := range joinedRelations { if _, ok := knownDirs[id]; ok { continue } dir, err := relation.ReadStateDir(u.relationsDir, id) if err != nil { return err } if err := u.addRelation(rel, dir); err != nil { return err } } return nil } // updateRelations responds to changes in the life states of the relations // with the supplied ids. If any id corresponds to an alive relation not // known to the unit, the uniter will join that relation and return its // relationer in the added list. func (u *Uniter) updateRelations(ids []int) (added []*Relationer, err error) { for _, id := range ids { if r, found := u.relationers[id]; found { rel := r.ru.Relation() if err := rel.Refresh(); err != nil { return nil, fmt.Errorf("cannot update relation %q: %v", rel, err) } if rel.Life() == params.Dying { if err := r.SetDying(); err != nil { return nil, err } else if r.IsImplicit() { delete(u.relationers, id) } } continue } // Relations that are not alive are simply skipped, because they // were not previously known anyway. rel, err := u.st.RelationById(id) if err != nil { if params.IsCodeNotFoundOrCodeUnauthorized(err) { continue } return nil, err } if rel.Life() != params.Alive { continue } // Make sure we ignore relations not implemented by the unit's charm ch, err := corecharm.ReadDir(u.charm.Path()) if err != nil { return nil, err } if ep, err := rel.Endpoint(); err != nil { return nil, err } else if !ep.ImplementedBy(ch) { logger.Warningf("skipping relation with unknown endpoint %q", ep.Name) continue } dir, err := relation.ReadStateDir(u.relationsDir, id) if err != nil { return nil, err } err = u.addRelation(rel, dir) if err == nil { added = append(added, u.relationers[id]) continue } e := dir.Remove() if !params.IsCodeCannotEnterScope(err) { return nil, err } if e != nil { return nil, e } } if ok, err := u.unit.IsPrincipal(); err != nil { return nil, err } else if ok { return added, nil } // If no Alive relations remain between a subordinate unit's service // and its principal's service, the subordinate must become Dying. keepAlive := false for _, r := range u.relationers { scope := r.ru.Endpoint().Scope if scope == corecharm.ScopeContainer && !r.dying { keepAlive = true break } } if !keepAlive { if err := u.unit.Destroy(); err != nil { return nil, err } } return added, nil } // addRelation causes the unit agent to join the supplied relation, and to // store persistent state in the supplied dir. func (u *Uniter) addRelation(rel *uniter.Relation, dir *relation.StateDir) error { logger.Infof("joining relation %q", rel) ru, err := rel.Unit(u.unit) if err != nil { return err } r := NewRelationer(ru, dir, u.relationHooks) w, err := u.unit.Watch() if err != nil { return err } defer watcher.Stop(w, &u.tomb) for { select { case <-u.tomb.Dying(): return tomb.ErrDying case _, ok := <-w.Changes(): if !ok { return watcher.MustErr(w) } err := r.Join() if params.IsCodeCannotEnterScopeYet(err) { logger.Infof("cannot enter scope for relation %q; waiting for subordinate to be removed", rel) continue } else if err != nil { return err } logger.Infof("joined relation %q", rel) u.relationers[rel.Id()] = r return nil } } } // updatePackageProxy updates the package proxy settings from the // environment. func (u *Uniter) updatePackageProxy(cfg *config.Config) { u.proxyMutex.Lock() defer u.proxyMutex.Unlock() newSettings := cfg.ProxySettings() if u.proxy != newSettings { u.proxy = newSettings logger.Debugf("Updated proxy settings: %#v", u.proxy) // Update the environment values used by the process. u.proxy.SetEnvironmentValues() } } // watchForProxyChanges kicks off a go routine to listen to the watcher and // update the proxy settings. func (u *Uniter) watchForProxyChanges(environWatcher apiwatcher.NotifyWatcher) { go func() { for { select { case <-u.tomb.Dying(): return case _, ok := <-environWatcher.Changes(): logger.Debugf("new environment change") if !ok { return } environConfig, err := u.st.EnvironConfig() if err != nil { logger.Errorf("cannot load environment configuration: %v", err) } else { u.updatePackageProxy(environConfig) } } } }() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/�����������������������������������0000755�0000153�0000161�00000000000�12321735643�024625� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/util_test.go�����������������������0000644�0000153�0000161�00000010030�12321735642�027161� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( "bytes" "fmt" "io" "sort" "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/worker/uniter/jujuc" ) func TestPackage(t *testing.T) { gc.TestingT(t) } func bufferBytes(stream io.Writer) []byte { return stream.(*bytes.Buffer).Bytes() } func bufferString(w io.Writer) string { return w.(*bytes.Buffer).String() } type ContextSuite struct { testbase.LoggingSuite rels map[int]*ContextRelation } func (s *ContextSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.rels = map[int]*ContextRelation{ 0: { id: 0, name: "peer0", units: map[string]Settings{ "u/0": {"private-address": "u-0.testing.invalid"}, }, }, 1: { id: 1, name: "peer1", units: map[string]Settings{ "u/0": {"private-address": "u-0.testing.invalid"}, }, }, } } func (s *ContextSuite) GetHookContext(c *gc.C, relid int, remote string) *Context { if relid != -1 { _, found := s.rels[relid] c.Assert(found, gc.Equals, true) } return &Context{ relid: relid, remote: remote, rels: s.rels, } } func setSettings(c *gc.C, ru *state.RelationUnit, settings map[string]interface{}) { node, err := ru.Settings() c.Assert(err, gc.IsNil) for _, k := range node.Keys() { node.Delete(k) } node.Update(settings) _, err = node.Write() c.Assert(err, gc.IsNil) } type Context struct { ports set.Strings relid int remote string rels map[int]*ContextRelation } func (c *Context) UnitName() string { return "u/0" } func (c *Context) PublicAddress() (string, bool) { return "gimli.minecraft.testing.invalid", true } func (c *Context) PrivateAddress() (string, bool) { return "192.168.0.99", true } func (c *Context) OpenPort(protocol string, port int) error { c.ports.Add(fmt.Sprintf("%d/%s", port, protocol)) return nil } func (c *Context) ClosePort(protocol string, port int) error { c.ports.Remove(fmt.Sprintf("%d/%s", port, protocol)) return nil } func (c *Context) ConfigSettings() (charm.Settings, error) { return charm.Settings{ "empty": nil, "monsters": false, "spline-reticulation": 45.0, "title": "My Title", "username": "admin001", }, nil } func (c *Context) HookRelation() (jujuc.ContextRelation, bool) { return c.Relation(c.relid) } func (c *Context) RemoteUnitName() (string, bool) { return c.remote, c.remote != "" } func (c *Context) Relation(id int) (jujuc.ContextRelation, bool) { r, found := c.rels[id] return r, found } func (c *Context) RelationIds() []int { ids := []int{} for id := range c.rels { ids = append(ids, id) } return ids } func (c *Context) OwnerTag() string { return "test-owner" } type ContextRelation struct { id int name string units map[string]Settings } func (r *ContextRelation) Id() int { return r.id } func (r *ContextRelation) Name() string { return r.name } func (r *ContextRelation) FakeId() string { return fmt.Sprintf("%s:%d", r.name, r.id) } func (r *ContextRelation) Settings() (jujuc.Settings, error) { return r.units["u/0"], nil } func (r *ContextRelation) UnitNames() []string { var s []string // initially nil to match the true context. for name := range r.units { s = append(s, name) } sort.Strings(s) return s } func (r *ContextRelation) ReadSettings(name string) (params.RelationSettings, error) { s, found := r.units[name] if !found { return nil, fmt.Errorf("unknown unit %s", name) } return s.Map(), nil } type Settings params.RelationSettings func (s Settings) Get(k string) (interface{}, bool) { v, f := s[k] return v, f } func (s Settings) Set(k, v string) { s[k] = v } func (s Settings) Delete(k string) { delete(s, k) } func (s Settings) Map() params.RelationSettings { r := params.RelationSettings{} for k, v := range s { r[k] = v } return r } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/unit-get_test.go�������������������0000644�0000153�0000161�00000005350�12321735642�027751� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( "io/ioutil" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/jujuc" ) type UnitGetSuite struct { ContextSuite } var _ = gc.Suite(&UnitGetSuite{}) var unitGetTests = []struct { args []string out string }{ {[]string{"private-address"}, "192.168.0.99\n"}, {[]string{"private-address", "--format", "yaml"}, "192.168.0.99\n"}, {[]string{"private-address", "--format", "json"}, `"192.168.0.99"` + "\n"}, {[]string{"public-address"}, "gimli.minecraft.testing.invalid\n"}, {[]string{"public-address", "--format", "yaml"}, "gimli.minecraft.testing.invalid\n"}, {[]string{"public-address", "--format", "json"}, `"gimli.minecraft.testing.invalid"` + "\n"}, } func (s *UnitGetSuite) createCommand(c *gc.C) cmd.Command { hctx := s.GetHookContext(c, -1, "") com, err := jujuc.NewCommand(hctx, "unit-get") c.Assert(err, gc.IsNil) return com } func (s *UnitGetSuite) TestOutputFormat(c *gc.C) { for _, t := range unitGetTests { com := s.createCommand(c) ctx := testing.Context(c) code := cmd.Main(com, ctx, t.args) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") c.Assert(bufferString(ctx.Stdout), gc.Matches, t.out) } } func (s *UnitGetSuite) TestHelp(c *gc.C) { com := s.createCommand(c) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--help"}) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stdout), gc.Equals, `usage: unit-get [options] <setting> purpose: print public-address or private-address options: --format (= smart) specify output format (json|smart|yaml) -o, --output (= "") specify an output file `) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } func (s *UnitGetSuite) TestOutputPath(c *gc.C) { com := s.createCommand(c) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--output", "some-file", "private-address"}) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") c.Assert(bufferString(ctx.Stdout), gc.Equals, "") content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, "192.168.0.99\n") } func (s *UnitGetSuite) TestUnknownSetting(c *gc.C) { com := s.createCommand(c) err := testing.InitCommand(com, []string{"protected-address"}) c.Assert(err, gc.ErrorMatches, `unknown setting "protected-address"`) } func (s *UnitGetSuite) TestUnknownArg(c *gc.C) { com := s.createCommand(c) err := testing.InitCommand(com, []string{"private-address", "blah"}) c.Assert(err, gc.ErrorMatches, `unrecognized args: \["blah"\]`) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/context.go�������������������������0000644�0000153�0000161�00000007674�12321735642�026655� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc import ( "fmt" "strconv" "strings" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/state/api/params" ) // Context is the interface that all hook helper commands // depend on to interact with the rest of the system. type Context interface { // Unit returns the executing unit's name. UnitName() string // PublicAddress returns the executing unit's public address. PublicAddress() (string, bool) // PrivateAddress returns the executing unit's private address. PrivateAddress() (string, bool) // OpenPort marks the supplied port for opening when the executing unit's // service is exposed. OpenPort(protocol string, port int) error // ClosePort ensures the supplied port is closed even when the executing // unit's service is exposed (unless it is opened separately by a co- // located unit). ClosePort(protocol string, port int) error // Config returns the current service configuration of the executing unit. ConfigSettings() (charm.Settings, error) // HookRelation returns the ContextRelation associated with the executing // hook if it was found, and whether it was found. HookRelation() (ContextRelation, bool) // RemoteUnitName returns the name of the remote unit the hook execution // is associated with if it was found, and whether it was found. RemoteUnitName() (string, bool) // Relation returns the relation with the supplied id if it was found, and // whether it was found. Relation(id int) (ContextRelation, bool) // RelationIds returns the ids of all relations the executing unit is // currently participating in. RelationIds() []int // OwnerTag returns the owner of the service the executing units belongs to OwnerTag() string } // ContextRelation expresses the capabilities of a hook with respect to a relation. type ContextRelation interface { // Id returns an integer which uniquely identifies the relation. Id() int // Name returns the name the locally executing charm assigned to this relation. Name() string // FakeId returns a string of the form "relation-name:123", which uniquely // identifies the relation to the hook. In reality, the identification // of the relation is the integer following the colon, but the composed // name is useful to humans observing it. FakeId() string // Settings allows read/write access to the local unit's settings in // this relation. Settings() (Settings, error) // UnitNames returns a list of the remote units in the relation. UnitNames() []string // ReadSettings returns the settings of any remote unit in the relation. ReadSettings(unit string) (params.RelationSettings, error) } // Settings is implemented by types that manipulate unit settings. type Settings interface { Map() params.RelationSettings Set(string, string) Delete(string) } // newRelationIdValue returns a gnuflag.Value for convenient parsing of relation // ids in ctx. func newRelationIdValue(ctx Context, result *int) *relationIdValue { v := &relationIdValue{result: result, ctx: ctx} id := -1 if r, found := ctx.HookRelation(); found { id = r.Id() v.value = r.FakeId() } *result = id return v } // relationIdValue implements gnuflag.Value for use in relation commands. type relationIdValue struct { result *int ctx Context value string } // String returns the current value. func (v *relationIdValue) String() string { return v.value } // Set interprets value as a relation id, if possible, and returns an error // if it is not known to the system. The parsed relation id will be written // to v.result. func (v *relationIdValue) Set(value string) error { trim := value if idx := strings.LastIndex(trim, ":"); idx != -1 { trim = trim[idx+1:] } id, err := strconv.Atoi(trim) if err != nil { return fmt.Errorf("invalid relation id") } if _, found := v.ctx.Relation(id); !found { return fmt.Errorf("unknown relation id") } *v.result = id v.value = value return nil } ��������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/juju-log.go������������������������0000644�0000153�0000161�00000003456�12321735642�026717� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc import ( "errors" "fmt" "strings" "github.com/juju/loggo" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) // JujuLogCommand implements the juju-log command. type JujuLogCommand struct { cmd.CommandBase ctx Context Message string Debug bool Level string formatFlag string // deprecated } func NewJujuLogCommand(ctx Context) cmd.Command { return &JujuLogCommand{ctx: ctx} } func (c *JujuLogCommand) Info() *cmd.Info { return &cmd.Info{ Name: "juju-log", Args: "<message>", Purpose: "write a message to the juju log", } } func (c *JujuLogCommand) SetFlags(f *gnuflag.FlagSet) { f.BoolVar(&c.Debug, "debug", false, "log at debug level") f.StringVar(&c.Level, "l", "INFO", "Send log message at the given level") f.StringVar(&c.Level, "log-level", "INFO", "") f.StringVar(&c.formatFlag, "format", "", "deprecated format flag") } func (c *JujuLogCommand) Init(args []string) error { if args == nil { return errors.New("no message specified") } c.Message = strings.Join(args, " ") return nil } func (c *JujuLogCommand) Run(ctx *cmd.Context) error { if c.formatFlag != "" { fmt.Fprintf(ctx.Stderr, "--format flag deprecated for command %q", c.Info().Name) } logger := loggo.GetLogger(fmt.Sprintf("unit.%s.juju-log", c.ctx.UnitName())) logLevel := loggo.INFO if c.Debug { logLevel = loggo.DEBUG } else if c.Level != "" { var ok bool logLevel, ok = loggo.ParseLevel(c.Level) if !ok { logger.Warningf("Specified log level of %q is not valid", c.Level) logLevel = loggo.INFO } } prefix := "" if r, found := c.ctx.HookRelation(); found { prefix = r.FakeId() + ": " } logger.Logf(logLevel, "%s%s", prefix, c.Message) return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/relation-set_test.go���������������0000644�0000153�0000161�00000013155�12321735642�030625� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/jujuc" ) type RelationSetSuite struct { ContextSuite } var _ = gc.Suite(&RelationSetSuite{}) var helpTests = []struct { relid int expect string }{{-1, ""}, {0, "peer0:0"}} func (s *RelationSetSuite) TestHelp(c *gc.C) { for i, t := range helpTests { c.Logf("test %d", i) hctx := s.GetHookContext(c, t.relid, "") com, err := jujuc.NewCommand(hctx, "relation-set") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--help"}) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stdout), gc.Equals, fmt.Sprintf(` usage: relation-set [options] key=value [key=value ...] purpose: set relation settings options: --format (= "") deprecated format flag -r (= %s) specify a relation by id `[1:], t.expect)) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } } var relationSetInitTests = []struct { ctxrelid int args []string err string relid int settings map[string]string }{ { // compatibility: 0 args is valid. }, { ctxrelid: -1, err: `no relation id specified`, }, { ctxrelid: -1, args: []string{"-r", "one"}, err: `invalid value "one" for flag -r: invalid relation id`, }, { ctxrelid: 1, args: []string{"-r", "one"}, err: `invalid value "one" for flag -r: invalid relation id`, }, { ctxrelid: -1, args: []string{"-r", "ignored:one"}, err: `invalid value "ignored:one" for flag -r: invalid relation id`, }, { ctxrelid: 1, args: []string{"-r", "ignored:one"}, err: `invalid value "ignored:one" for flag -r: invalid relation id`, }, { ctxrelid: -1, args: []string{"-r", "2"}, err: `invalid value "2" for flag -r: unknown relation id`, }, { ctxrelid: 1, args: []string{"-r", "ignored:2"}, err: `invalid value "ignored:2" for flag -r: unknown relation id`, }, { ctxrelid: -1, err: `no relation id specified`, }, { ctxrelid: 1, args: []string{"-r", "ignored:0"}, relid: 0, }, { ctxrelid: 1, args: []string{"-r", "0"}, relid: 0, }, { ctxrelid: -1, args: []string{"-r", "1"}, relid: 1, }, { ctxrelid: 0, args: []string{"-r", "1"}, relid: 1, }, { ctxrelid: 1, args: []string{"haha"}, err: `expected "key=value", got "haha"`, }, { ctxrelid: 1, args: []string{"=haha"}, err: `expected "key=value", got "=haha"`, }, { ctxrelid: 1, args: []string{"foo="}, relid: 1, settings: map[string]string{"foo": ""}, }, { ctxrelid: 1, args: []string{"foo='"}, relid: 1, settings: map[string]string{"foo": "'"}, }, { ctxrelid: 1, args: []string{"foo=bar"}, relid: 1, settings: map[string]string{"foo": "bar"}, }, { ctxrelid: 1, args: []string{"foo=bar=baz=qux"}, relid: 1, settings: map[string]string{"foo": "bar=baz=qux"}, }, { ctxrelid: 1, args: []string{"foo=foo: bar"}, relid: 1, settings: map[string]string{"foo": "foo: bar"}, }, { ctxrelid: 0, args: []string{"-r", "1", "foo=bar"}, relid: 1, settings: map[string]string{"foo": "bar"}, }, { ctxrelid: 1, args: []string{"foo=123", "bar=true", "baz=4.5", "qux="}, relid: 1, settings: map[string]string{"foo": "123", "bar": "true", "baz": "4.5", "qux": ""}, }, } func (s *RelationSetSuite) TestInit(c *gc.C) { for i, t := range relationSetInitTests { c.Logf("test %d", i) hctx := s.GetHookContext(c, t.ctxrelid, "") com, err := jujuc.NewCommand(hctx, "relation-set") c.Assert(err, gc.IsNil) err = testing.InitCommand(com, t.args) if t.err == "" { c.Assert(err, gc.IsNil) rset := com.(*jujuc.RelationSetCommand) c.Assert(rset.RelationId, gc.Equals, t.relid) settings := t.settings if settings == nil { settings = map[string]string{} } c.Assert(rset.Settings, gc.DeepEquals, settings) } else { c.Assert(err, gc.ErrorMatches, t.err) } } } // Tests start with a relation with the settings {"base": "value"} var relationSetRunTests = []struct { change map[string]string expect Settings }{ { map[string]string{"base": ""}, Settings{}, }, { map[string]string{"foo": "bar"}, Settings{"base": "value", "foo": "bar"}, }, { map[string]string{"base": "changed"}, Settings{"base": "changed"}, }, } func (s *RelationSetSuite) TestRun(c *gc.C) { hctx := s.GetHookContext(c, 0, "") for i, t := range relationSetRunTests { c.Logf("test %d", i) pristine := Settings{"pristine": "untouched"} hctx.rels[0].units["u/0"] = pristine basic := Settings{"base": "value"} hctx.rels[1].units["u/0"] = basic // Run the command. com, err := jujuc.NewCommand(hctx, "relation-set") c.Assert(err, gc.IsNil) rset := com.(*jujuc.RelationSetCommand) rset.RelationId = 1 rset.Settings = t.change ctx := testing.Context(c) err = com.Run(ctx) c.Assert(err, gc.IsNil) // Check changes. c.Assert(hctx.rels[0].units["u/0"], gc.DeepEquals, pristine) c.Assert(hctx.rels[1].units["u/0"], gc.DeepEquals, t.expect) } } func (s *RelationSetSuite) TestRunDeprecationWarning(c *gc.C) { hctx := s.GetHookContext(c, 0, "") com, _ := jujuc.NewCommand(hctx, "relation-set") // The rel= is needed to make this a valid command. ctx, err := testing.RunCommand(c, com, []string{"--format", "foo", "rel="}) c.Assert(err, gc.IsNil) c.Assert(testing.Stdout(ctx), gc.Equals, "") c.Assert(testing.Stderr(ctx), gc.Equals, "--format flag deprecated for command \"relation-set\"") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/ports.go���������������������������0000644�0000153�0000161�00000004444�12321735642�026330� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc import ( "errors" "fmt" "strconv" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) const portFormat = "<port>[/<protocol>]" // portCommand implements the open-port and close-port commands. type portCommand struct { cmd.CommandBase info *cmd.Info action func(*portCommand) error Protocol string Port int formatFlag string // deprecated } func (c *portCommand) Info() *cmd.Info { return c.info } func badPort(value interface{}) error { return fmt.Errorf(`port must be in the range [1, 65535]; got "%v"`, value) } func (c *portCommand) SetFlags(f *gnuflag.FlagSet) { f.StringVar(&c.formatFlag, "format", "", "deprecated format flag") } func (c *portCommand) Init(args []string) error { if args == nil { return errors.New("no port specified") } parts := strings.Split(args[0], "/") if len(parts) > 2 { return fmt.Errorf("expected %s; got %q", portFormat, args[0]) } port, err := strconv.Atoi(parts[0]) if err != nil { return badPort(parts[0]) } if port < 1 || port > 65535 { return badPort(port) } protocol := "tcp" if len(parts) == 2 { protocol = strings.ToLower(parts[1]) if protocol != "tcp" && protocol != "udp" { return fmt.Errorf(`protocol must be "tcp" or "udp"; got %q`, protocol) } } c.Port = port c.Protocol = protocol return cmd.CheckEmpty(args[1:]) } func (c *portCommand) Run(ctx *cmd.Context) error { if c.formatFlag != "" { fmt.Fprintf(ctx.Stderr, "--format flag deprecated for command %q", c.Info().Name) } return c.action(c) } var openPortInfo = &cmd.Info{ Name: "open-port", Args: portFormat, Purpose: "register a port to open", Doc: "The port will only be open while the service is exposed.", } func NewOpenPortCommand(ctx Context) cmd.Command { return &portCommand{ info: openPortInfo, action: func(c *portCommand) error { return ctx.OpenPort(c.Protocol, c.Port) }, } } var closePortInfo = &cmd.Info{ Name: "close-port", Args: portFormat, Purpose: "ensure a port is always closed", } func NewClosePortCommand(ctx Context) cmd.Command { return &portCommand{ info: closePortInfo, action: func(c *portCommand) error { return ctx.ClosePort(c.Protocol, c.Port) }, } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/relation-list_test.go��������������0000644�0000153�0000161�00000010727�12321735642�031007� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/jujuc" ) type RelationListSuite struct { ContextSuite } var _ = gc.Suite(&RelationListSuite{}) var relationListTests = []struct { summary string relid int members0, members1 []string args []string code int out string }{ { summary: "no default relation, no arg", relid: -1, code: 2, out: "no relation id specified", }, { summary: "no default relation, bad arg", relid: -1, args: []string{"-r", "bad"}, code: 2, out: `invalid value "bad" for flag -r: invalid relation id`, }, { summary: "no default relation, unknown arg", relid: -1, args: []string{"-r", "unknown:123"}, code: 2, out: `invalid value "unknown:123" for flag -r: unknown relation id`, }, { summary: "default relation, bad arg", relid: 1, args: []string{"-r", "bad"}, code: 2, out: `invalid value "bad" for flag -r: invalid relation id`, }, { summary: "default relation, unknown arg", relid: 1, args: []string{"-r", "unknown:123"}, code: 2, out: `invalid value "unknown:123" for flag -r: unknown relation id`, }, { summary: "default relation, no members", relid: 1, }, { summary: "default relation, members", members1: []string{"foo", "bar", "baz"}, relid: 1, out: "bar\nbaz\nfoo", }, { summary: "alternative relation, members", members0: []string{"pew", "pow", "paw"}, relid: 1, args: []string{"-r", "ignored:0"}, out: "paw\npew\npow", }, { summary: "explicit smart formatting 1", relid: 1, args: []string{"--format", "smart"}, }, { summary: "explicit smart formatting 2", members1: []string{"foo", "bar", "baz"}, relid: 1, args: []string{"--format", "smart"}, out: "bar\nbaz\nfoo", }, { summary: "json formatting 1", relid: 1, args: []string{"--format", "json"}, out: "[]", }, { summary: "json formatting 2", members1: []string{"foo", "bar", "baz"}, relid: 1, args: []string{"--format", "json"}, out: `["bar","baz","foo"]`, }, { summary: "yaml formatting 1", relid: 1, args: []string{"--format", "yaml"}, out: "[]", }, { summary: "yaml formatting 2", members1: []string{"foo", "bar", "baz"}, relid: 1, args: []string{"--format", "yaml"}, out: "- bar\n- baz\n- foo", }, } func (s *RelationListSuite) TestRelationList(c *gc.C) { for i, t := range relationListTests { c.Logf("test %d: %s", i, t.summary) hctx := s.GetHookContext(c, t.relid, "") setMembers(hctx.rels[0], t.members0) setMembers(hctx.rels[1], t.members1) com, err := jujuc.NewCommand(hctx, "relation-list") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, t.args) c.Logf(bufferString(ctx.Stderr)) c.Assert(code, gc.Equals, t.code) if code == 0 { c.Assert(bufferString(ctx.Stderr), gc.Equals, "") expect := t.out if expect != "" { expect = expect + "\n" } c.Assert(bufferString(ctx.Stdout), gc.Equals, expect) } else { c.Assert(bufferString(ctx.Stdout), gc.Equals, "") expect := fmt.Sprintf(`(.|\n)*error: %s\n`, t.out) c.Assert(bufferString(ctx.Stderr), gc.Matches, expect) } } } func (s *RelationListSuite) TestRelationListHelp(c *gc.C) { template := ` usage: relation-list [options] purpose: list relation units options: --format (= smart) specify output format (json|smart|yaml) -o, --output (= "") specify an output file -r (= %s) specify a relation by id %s`[1:] for relid, t := range map[int]struct { usage, doc string }{ -1: {"", "\n-r must be specified when not in a relation hook\n"}, 0: {"peer0:0", ""}, } { c.Logf("test relid %d", relid) hctx := s.GetHookContext(c, relid, "") com, err := jujuc.NewCommand(hctx, "relation-list") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--help"}) c.Assert(code, gc.Equals, 0) expect := fmt.Sprintf(template, t.usage, t.doc) c.Assert(bufferString(ctx.Stdout), gc.Equals, expect) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } } func setMembers(rctx *ContextRelation, members []string) { rctx.units = map[string]Settings{} for _, name := range members { rctx.units[name] = nil } } �����������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/server.go��������������������������0000644�0000153�0000161�00000011236�12321735642�026464� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The worker/uniter/jujuc package implements the server side of the jujuc proxy // tool, which forwards command invocations to the unit agent process so that // they can be executed against specific state. package jujuc import ( "bytes" "fmt" "net" "net/rpc" "path/filepath" "sort" "sync" "github.com/juju/loggo" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/utils/exec" ) var logger = loggo.GetLogger("worker.uniter.jujuc") // newCommands maps Command names to initializers. var newCommands = map[string]func(Context) cmd.Command{ "close-port": NewClosePortCommand, "config-get": NewConfigGetCommand, "juju-log": NewJujuLogCommand, "open-port": NewOpenPortCommand, "relation-get": NewRelationGetCommand, "relation-ids": NewRelationIdsCommand, "relation-list": NewRelationListCommand, "relation-set": NewRelationSetCommand, "unit-get": NewUnitGetCommand, "owner-get": NewOwnerGetCommand, } // CommandNames returns the names of all jujuc commands. func CommandNames() (names []string) { for name := range newCommands { names = append(names, name) } sort.Strings(names) return } // NewCommand returns an instance of the named Command, initialized to execute // against the supplied Context. func NewCommand(ctx Context, name string) (cmd.Command, error) { f := newCommands[name] if f == nil { return nil, fmt.Errorf("unknown command: %s", name) } return f(ctx), nil } // Request contains the information necessary to run a Command remotely. type Request struct { ContextId string Dir string CommandName string Args []string } // CmdGetter looks up a Command implementation connected to a particular Context. type CmdGetter func(contextId, cmdName string) (cmd.Command, error) // Jujuc implements the jujuc command in the form required by net/rpc. type Jujuc struct { mu sync.Mutex getCmd CmdGetter } // badReqErrorf returns an error indicating a bad Request. func badReqErrorf(format string, v ...interface{}) error { return fmt.Errorf("bad request: "+format, v...) } // Main runs the Command specified by req, and fills in resp. A single command // is run at a time. func (j *Jujuc) Main(req Request, resp *exec.ExecResponse) error { if req.CommandName == "" { return badReqErrorf("command not specified") } if !filepath.IsAbs(req.Dir) { return badReqErrorf("Dir is not absolute") } c, err := j.getCmd(req.ContextId, req.CommandName) if err != nil { return badReqErrorf("%s", err) } var stdin, stdout, stderr bytes.Buffer ctx := &cmd.Context{ Dir: req.Dir, Stdin: &stdin, Stdout: &stdout, Stderr: &stderr, } j.mu.Lock() defer j.mu.Unlock() logger.Infof("running hook tool %q %q", req.CommandName, req.Args) logger.Debugf("hook context id %q; dir %q", req.ContextId, req.Dir) resp.Code = cmd.Main(c, ctx, req.Args) resp.Stdout = stdout.Bytes() resp.Stderr = stderr.Bytes() return nil } // Server implements a server that serves command invocations via // a unix domain socket. type Server struct { socketPath string listener net.Listener server *rpc.Server closed chan bool closing chan bool wg sync.WaitGroup } // NewServer creates an RPC server bound to socketPath, which can execute // remote command invocations against an appropriate Context. It will not // actually do so until Run is called. func NewServer(getCmd CmdGetter, socketPath string) (*Server, error) { server := rpc.NewServer() if err := server.Register(&Jujuc{getCmd: getCmd}); err != nil { return nil, err } listener, err := net.Listen("unix", socketPath) if err != nil { return nil, err } s := &Server{ socketPath: socketPath, listener: listener, server: server, closed: make(chan bool), closing: make(chan bool), } return s, nil } // Run accepts new connections until it encounters an error, or until Close is // called, and then blocks until all existing connections have been closed. func (s *Server) Run() (err error) { var conn net.Conn for { conn, err = s.listener.Accept() if err != nil { break } s.wg.Add(1) go func(conn net.Conn) { s.server.ServeConn(conn) s.wg.Done() }(conn) } select { case <-s.closing: // Someone has called Close(), so it is overwhelmingly likely that // the error from Accept is a direct result of the Listener being // closed, and can therefore be safely ignored. err = nil default: } s.wg.Wait() close(s.closed) return } // Close immediately stops accepting connections, and blocks until all existing // connections have been closed. func (s *Server) Close() { close(s.closing) s.listener.Close() <-s.closed } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/owner-get.go�����������������������0000644�0000153�0000161�00000002254�12321735642�027065� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc import ( "errors" "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) // OwnerGetCommand implements the owner-get command. type OwnerGetCommand struct { cmd.CommandBase ctx Context Key string out cmd.Output } func NewOwnerGetCommand(ctx Context) cmd.Command { return &OwnerGetCommand{ctx: ctx} } func (c *OwnerGetCommand) Info() *cmd.Info { return &cmd.Info{ Name: "owner-get", Args: "<setting>", Purpose: `print information about the owner of the service. The only valid value for <setting> is currently tag`, } } func (c *OwnerGetCommand) SetFlags(f *gnuflag.FlagSet) { c.out.AddFlags(f, "smart", cmd.DefaultFormatters) } func (c *OwnerGetCommand) Init(args []string) error { if args == nil { return errors.New("no setting specified") } if args[0] != "tag" { return fmt.Errorf("unknown setting %q", args[0]) } c.Key = args[0] return cmd.CheckEmpty(args[1:]) } func (c *OwnerGetCommand) Run(ctx *cmd.Context) error { if c.Key != "tag" { return fmt.Errorf("%s not set", c.Key) } return c.out.Write(ctx, c.ctx.OwnerTag()) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/relation-ids_test.go���������������0000644�0000153�0000161�00000007605�12321735642�030614� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/jujuc" ) type RelationIdsSuite struct { ContextSuite } var _ = gc.Suite(&RelationIdsSuite{}) func (s *RelationIdsSuite) SetUpTest(c *gc.C) { s.ContextSuite.SetUpTest(c) s.rels = map[int]*ContextRelation{} s.AddRelatedServices(c, "x", 3) s.AddRelatedServices(c, "y", 1) } func (s *RelationIdsSuite) AddRelatedServices(c *gc.C, relname string, count int) { for i := 0; i < count; i++ { id := len(s.rels) s.rels[id] = &ContextRelation{id, relname, nil} } } var relationIdsTests = []struct { summary string relid int args []string code int out string }{ { summary: "no default, no name", relid: -1, code: 2, out: "(.|\n)*error: no relation name specified\n", }, { summary: "default name", relid: 1, out: "x:0\nx:1\nx:2", }, { summary: "explicit name", relid: -1, args: []string{"x"}, out: "x:0\nx:1\nx:2", }, { summary: "explicit different name", relid: -1, args: []string{"y"}, out: "y:3", }, { summary: "nonexistent name", relid: -1, args: []string{"z"}, }, { summary: "explicit smart formatting 1", args: []string{"--format", "smart", "x"}, out: "x:0\nx:1\nx:2", }, { summary: "explicit smart formatting 2", args: []string{"--format", "smart", "y"}, out: "y:3", }, { summary: "explicit smart formatting 3", args: []string{"--format", "smart", "z"}, }, { summary: "json formatting 1", args: []string{"--format", "json", "x"}, out: `["x:0","x:1","x:2"]`, }, { summary: "json formatting 2", args: []string{"--format", "json", "y"}, out: `["y:3"]`, }, { summary: "json formatting 3", args: []string{"--format", "json", "z"}, out: `[]`, }, { summary: "yaml formatting 1", args: []string{"--format", "yaml", "x"}, out: "- x:0\n- x:1\n- x:2", }, { summary: "yaml formatting 2", args: []string{"--format", "yaml", "y"}, out: "- y:3", }, { summary: "yaml formatting 3", args: []string{"--format", "yaml", "z"}, out: "[]", }, } func (s *RelationIdsSuite) TestRelationIds(c *gc.C) { for i, t := range relationIdsTests { c.Logf("test %d: %s", i, t.summary) hctx := s.GetHookContext(c, t.relid, "") com, err := jujuc.NewCommand(hctx, "relation-ids") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, t.args) c.Assert(code, gc.Equals, t.code) if code == 0 { c.Assert(bufferString(ctx.Stderr), gc.Equals, "") expect := t.out if expect != "" { expect += "\n" } c.Assert(bufferString(ctx.Stdout), gc.Equals, expect) } else { c.Assert(bufferString(ctx.Stdout), gc.Equals, "") c.Assert(bufferString(ctx.Stderr), gc.Matches, t.out) } } } func (s *RelationIdsSuite) TestHelp(c *gc.C) { template := ` usage: %s purpose: list all relation ids with the given relation name options: --format (= smart) specify output format (json|smart|yaml) -o, --output (= "") specify an output file %s`[1:] for relid, t := range map[int]struct { usage, doc string }{ -1: {"relation-ids [options] <name>", ""}, 0: {"relation-ids [options] [<name>]", "\nCurrent default relation name is \"x\".\n"}, 3: {"relation-ids [options] [<name>]", "\nCurrent default relation name is \"y\".\n"}, } { c.Logf("relid %d", relid) hctx := s.GetHookContext(c, relid, "") com, err := jujuc.NewCommand(hctx, "relation-ids") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--help"}) c.Assert(code, gc.Equals, 0) expect := fmt.Sprintf(template, t.usage, t.doc) c.Assert(bufferString(ctx.Stdout), gc.Equals, expect) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } } ���������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/relation-get.go��������������������0000644�0000153�0000161�00000004425�12321735642�027552� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/state/api/params" ) // RelationGetCommand implements the relation-get command. type RelationGetCommand struct { cmd.CommandBase ctx Context RelationId int Key string UnitName string out cmd.Output } func NewRelationGetCommand(ctx Context) cmd.Command { return &RelationGetCommand{ctx: ctx} } func (c *RelationGetCommand) Info() *cmd.Info { args := "<key> <unit id>" doc := ` relation-get prints the value of a unit's relation setting, specified by key. If no key is given, or if the key is "-", all keys and values will be printed. ` if name, found := c.ctx.RemoteUnitName(); found { args = "[<key> [<unit id>]]" doc += fmt.Sprintf("Current default unit id is %q.", name) } return &cmd.Info{ Name: "relation-get", Args: args, Purpose: "get relation settings", Doc: doc, } } func (c *RelationGetCommand) SetFlags(f *gnuflag.FlagSet) { c.out.AddFlags(f, "smart", cmd.DefaultFormatters) f.Var(newRelationIdValue(c.ctx, &c.RelationId), "r", "specify a relation by id") } func (c *RelationGetCommand) Init(args []string) error { if c.RelationId == -1 { return fmt.Errorf("no relation id specified") } c.Key = "" if len(args) > 0 { if c.Key = args[0]; c.Key == "-" { c.Key = "" } args = args[1:] } if name, found := c.ctx.RemoteUnitName(); found { c.UnitName = name } if len(args) > 0 { c.UnitName = args[0] args = args[1:] } if c.UnitName == "" { return fmt.Errorf("no unit id specified") } return cmd.CheckEmpty(args) } func (c *RelationGetCommand) Run(ctx *cmd.Context) error { r, found := c.ctx.Relation(c.RelationId) if !found { return fmt.Errorf("unknown relation id") } var settings params.RelationSettings if c.UnitName == c.ctx.UnitName() { node, err := r.Settings() if err != nil { return err } settings = node.Map() } else { var err error settings, err = r.ReadSettings(c.UnitName) if err != nil { return err } } if c.Key == "" { return c.out.Write(ctx, settings) } if value, ok := settings[c.Key]; ok { return c.out.Write(ctx, value) } return c.out.Write(ctx, nil) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/ports_test.go����������������������0000644�0000153�0000161�00000006616�12321735642�027372� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/worker/uniter/jujuc" ) type PortsSuite struct { ContextSuite } var _ = gc.Suite(&PortsSuite{}) var portsTests = []struct { cmd []string expect set.Strings }{ {[]string{"open-port", "80"}, set.NewStrings("80/tcp")}, {[]string{"open-port", "99/tcp"}, set.NewStrings("80/tcp", "99/tcp")}, {[]string{"close-port", "80/TCP"}, set.NewStrings("99/tcp")}, {[]string{"open-port", "123/udp"}, set.NewStrings("99/tcp", "123/udp")}, {[]string{"close-port", "9999/UDP"}, set.NewStrings("99/tcp", "123/udp")}, } func (s *PortsSuite) TestOpenClose(c *gc.C) { hctx := s.GetHookContext(c, -1, "") for _, t := range portsTests { com, err := jujuc.NewCommand(hctx, t.cmd[0]) c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, t.cmd[1:]) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stdout), gc.Equals, "") c.Assert(bufferString(ctx.Stderr), gc.Equals, "") c.Assert(hctx.ports, gc.DeepEquals, t.expect) } } var badPortsTests = []struct { args []string err string }{ {nil, "no port specified"}, {[]string{"0"}, `port must be in the range \[1, 65535\]; got "0"`}, {[]string{"65536"}, `port must be in the range \[1, 65535\]; got "65536"`}, {[]string{"two"}, `port must be in the range \[1, 65535\]; got "two"`}, {[]string{"80/http"}, `protocol must be "tcp" or "udp"; got "http"`}, {[]string{"blah/blah/blah"}, `expected <port>\[/<protocol>\]; got "blah/blah/blah"`}, {[]string{"123", "haha"}, `unrecognized args: \["haha"\]`}, } func (s *PortsSuite) TestBadArgs(c *gc.C) { for _, name := range []string{"open-port", "close-port"} { for _, t := range badPortsTests { hctx := s.GetHookContext(c, -1, "") com, err := jujuc.NewCommand(hctx, name) c.Assert(err, gc.IsNil) err = testing.InitCommand(com, t.args) c.Assert(err, gc.ErrorMatches, t.err) } } } func (s *PortsSuite) TestHelp(c *gc.C) { hctx := s.GetHookContext(c, -1, "") open, err := jujuc.NewCommand(hctx, "open-port") c.Assert(err, gc.IsNil) flags := testing.NewFlagSet() c.Assert(string(open.Info().Help(flags)), gc.Equals, ` usage: open-port <port>[/<protocol>] purpose: register a port to open The port will only be open while the service is exposed. `[1:]) close, err := jujuc.NewCommand(hctx, "close-port") c.Assert(err, gc.IsNil) c.Assert(string(close.Info().Help(flags)), gc.Equals, ` usage: close-port <port>[/<protocol>] purpose: ensure a port is always closed `[1:]) } // Since the deprecation warning gets output during Run, we really need // some valid commands to run var portsFormatDeprectaionTests = []struct { cmd []string }{ {[]string{"open-port", "--format", "foo", "80"}}, {[]string{"close-port", "--format", "foo", "80/TCP"}}, } func (s *PortsSuite) TestOpenCloseDeprecation(c *gc.C) { hctx := s.GetHookContext(c, -1, "") for _, t := range portsFormatDeprectaionTests { name := t.cmd[0] com, err := jujuc.NewCommand(hctx, name) c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, t.cmd[1:]) c.Assert(code, gc.Equals, 0) c.Assert(testing.Stdout(ctx), gc.Equals, "") c.Assert(testing.Stderr(ctx), gc.Equals, "--format flag deprecated for command \""+name+"\"") } } ������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/relation-set.go��������������������0000644�0000153�0000161�00000003265�12321735642�027567� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc import ( "fmt" "strings" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) // RelationSetCommand implements the relation-set command. type RelationSetCommand struct { cmd.CommandBase ctx Context RelationId int Settings map[string]string formatFlag string // deprecated } func NewRelationSetCommand(ctx Context) cmd.Command { return &RelationSetCommand{ctx: ctx, Settings: map[string]string{}} } func (c *RelationSetCommand) Info() *cmd.Info { return &cmd.Info{ Name: "relation-set", Args: "key=value [key=value ...]", Purpose: "set relation settings", } } func (c *RelationSetCommand) SetFlags(f *gnuflag.FlagSet) { f.Var(newRelationIdValue(c.ctx, &c.RelationId), "r", "specify a relation by id") f.StringVar(&c.formatFlag, "format", "", "deprecated format flag") } func (c *RelationSetCommand) Init(args []string) error { if c.RelationId == -1 { return fmt.Errorf("no relation id specified") } for _, kv := range args { parts := strings.SplitN(kv, "=", 2) if len(parts) != 2 || len(parts[0]) == 0 { return fmt.Errorf(`expected "key=value", got %q`, kv) } c.Settings[parts[0]] = parts[1] } return nil } func (c *RelationSetCommand) Run(ctx *cmd.Context) (err error) { if c.formatFlag != "" { fmt.Fprintf(ctx.Stderr, "--format flag deprecated for command %q", c.Info().Name) } r, found := c.ctx.Relation(c.RelationId) if !found { return fmt.Errorf("unknown relation id") } settings, err := r.Settings() for k, v := range c.Settings { if v != "" { settings.Set(k, v) } else { settings.Delete(k) } } return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/relation-list.go�������������������0000644�0000153�0000161�00000002545�12321735642�027747� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) // RelationListCommand implements the relation-list command. type RelationListCommand struct { cmd.CommandBase ctx Context RelationId int out cmd.Output } func NewRelationListCommand(ctx Context) cmd.Command { return &RelationListCommand{ctx: ctx} } func (c *RelationListCommand) Info() *cmd.Info { doc := "-r must be specified when not in a relation hook" if _, found := c.ctx.HookRelation(); found { doc = "" } return &cmd.Info{ Name: "relation-list", Purpose: "list relation units", Doc: doc, } } func (c *RelationListCommand) SetFlags(f *gnuflag.FlagSet) { c.out.AddFlags(f, "smart", cmd.DefaultFormatters) f.Var(newRelationIdValue(c.ctx, &c.RelationId), "r", "specify a relation by id") } func (c *RelationListCommand) Init(args []string) (err error) { if c.RelationId == -1 { return fmt.Errorf("no relation id specified") } return cmd.CheckEmpty(args) } func (c *RelationListCommand) Run(ctx *cmd.Context) error { r, found := c.ctx.Relation(c.RelationId) if !found { return fmt.Errorf("unknown relation id") } unitNames := r.UnitNames() if unitNames == nil { unitNames = []string{} } return c.out.Write(ctx, unitNames) } �����������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/relation-get_test.go���������������0000644�0000153�0000161�00000016015�12321735642�030607� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( "fmt" "io/ioutil" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/jujuc" ) type RelationGetSuite struct { ContextSuite } var _ = gc.Suite(&RelationGetSuite{}) func (s *RelationGetSuite) SetUpTest(c *gc.C) { s.ContextSuite.SetUpTest(c) s.rels[0].units["u/0"]["private-address"] = "foo: bar\n" s.rels[1].units["m/0"] = Settings{"pew": "pew\npew\n"} s.rels[1].units["u/1"] = Settings{"value": "12345"} } var relationGetTests = []struct { summary string relid int unit string args []string code int out string checkctx func(*gc.C, *cmd.Context) }{ { summary: "no default relation", relid: -1, code: 2, out: `no relation id specified`, }, { summary: "explicit relation, not known", relid: -1, code: 2, args: []string{"-r", "burble:123"}, out: `invalid value "burble:123" for flag -r: unknown relation id`, }, { summary: "default relation, no unit chosen", relid: 1, code: 2, out: `no unit id specified`, }, { summary: "explicit relation, no unit chosen", relid: -1, code: 2, args: []string{"-r", "burble:1"}, out: `no unit id specified`, }, { summary: "missing key", relid: 1, unit: "m/0", args: []string{"ker-plunk"}, }, { summary: "missing unit", relid: 1, unit: "bad/0", code: 1, out: `unknown unit bad/0`, }, { summary: "all keys with implicit member", relid: 1, unit: "m/0", out: "pew: 'pew\n\n pew\n\n'", }, { summary: "all keys with explicit member", relid: 1, args: []string{"-", "m/0"}, out: "pew: 'pew\n\n pew\n\n'", }, { summary: "all keys with explicit non-member", relid: 1, args: []string{"-", "u/1"}, out: `value: "12345"`, }, { summary: "all keys with explicit local", relid: 0, args: []string{"-", "u/0"}, out: "private-address: 'foo: bar\n\n'", }, { summary: "specific key with implicit member", relid: 1, unit: "m/0", args: []string{"pew"}, out: "pew\npew\n", }, { summary: "specific key with explicit member", relid: 1, args: []string{"pew", "m/0"}, out: "pew\npew\n", }, { summary: "specific key with explicit non-member", relid: 1, args: []string{"value", "u/1"}, out: "12345", }, { summary: "specific key with explicit local", relid: 0, args: []string{"private-address", "u/0"}, out: "foo: bar\n", }, { summary: "explicit smart formatting 1", relid: 1, unit: "m/0", args: []string{"--format", "smart"}, out: "pew: 'pew\n\n pew\n\n'", }, { summary: "explicit smart formatting 2", relid: 1, unit: "m/0", args: []string{"pew", "--format", "smart"}, out: "pew\npew\n", }, { summary: "explicit smart formatting 3", relid: 1, args: []string{"value", "u/1", "--format", "smart"}, out: "12345", }, { summary: "explicit smart formatting 4", relid: 1, args: []string{"missing", "u/1", "--format", "smart"}, out: "", }, { summary: "json formatting 1", relid: 1, unit: "m/0", args: []string{"--format", "json"}, out: `{"pew":"pew\npew\n"}`, }, { summary: "json formatting 2", relid: 1, unit: "m/0", args: []string{"pew", "--format", "json"}, out: `"pew\npew\n"`, }, { summary: "json formatting 3", relid: 1, args: []string{"value", "u/1", "--format", "json"}, out: `"12345"`, }, { summary: "json formatting 4", relid: 1, args: []string{"missing", "u/1", "--format", "json"}, out: `null`, }, { summary: "yaml formatting 1", relid: 1, unit: "m/0", args: []string{"--format", "yaml"}, out: "pew: 'pew\n\n pew\n\n'", }, { summary: "yaml formatting 2", relid: 1, unit: "m/0", args: []string{"pew", "--format", "yaml"}, out: "'pew\n\n pew\n\n'", }, { summary: "yaml formatting 3", relid: 1, args: []string{"value", "u/1", "--format", "yaml"}, out: `"12345"`, }, { summary: "yaml formatting 4", relid: 1, args: []string{"missing", "u/1", "--format", "yaml"}, out: ``, }, } func (s *RelationGetSuite) TestRelationGet(c *gc.C) { for i, t := range relationGetTests { c.Logf("test %d: %s", i, t.summary) hctx := s.GetHookContext(c, t.relid, t.unit) com, err := jujuc.NewCommand(hctx, "relation-get") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, t.args) c.Check(code, gc.Equals, t.code) if code == 0 { c.Check(bufferString(ctx.Stderr), gc.Equals, "") expect := t.out if expect != "" { expect = expect + "\n" } c.Check(bufferString(ctx.Stdout), gc.Equals, expect) } else { c.Check(bufferString(ctx.Stdout), gc.Equals, "") expect := fmt.Sprintf(`(.|\n)*error: %s\n`, t.out) c.Check(bufferString(ctx.Stderr), gc.Matches, expect) } } } var helpTemplate = ` usage: %s purpose: get relation settings options: --format (= smart) specify output format (json|smart|yaml) -o, --output (= "") specify an output file -r (= %s) specify a relation by id relation-get prints the value of a unit's relation setting, specified by key. If no key is given, or if the key is "-", all keys and values will be printed. %s`[1:] var relationGetHelpTests = []struct { summary string relid int unit string usage string rel string }{ { summary: "no default relation", relid: -1, usage: "relation-get [options] <key> <unit id>", }, { summary: "no default unit", relid: 1, usage: "relation-get [options] <key> <unit id>", rel: "peer1:1", }, { summary: "default unit", relid: 1, unit: "any/1", usage: `relation-get [options] [<key> [<unit id>]]`, rel: "peer1:1", }, } func (s *RelationGetSuite) TestHelp(c *gc.C) { for i, t := range relationGetHelpTests { c.Logf("test %d", i) hctx := s.GetHookContext(c, t.relid, t.unit) com, err := jujuc.NewCommand(hctx, "relation-get") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--help"}) c.Assert(code, gc.Equals, 0) unitHelp := "" if t.unit != "" { unitHelp = fmt.Sprintf("Current default unit id is %q.\n", t.unit) } expect := fmt.Sprintf(helpTemplate, t.usage, t.rel, unitHelp) c.Assert(bufferString(ctx.Stdout), gc.Equals, expect) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } } func (s *RelationGetSuite) TestOutputPath(c *gc.C) { hctx := s.GetHookContext(c, 1, "m/0") com, err := jujuc.NewCommand(hctx, "relation-get") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--output", "some-file", "pew"}) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") c.Assert(bufferString(ctx.Stdout), gc.Equals, "") content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, "pew\npew\n\n") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/relation-ids.go��������������������0000644�0000153�0000161�00000002756�12321735642�027557� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc import ( "fmt" "sort" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) // RelationIdsCommand implements the relation-ids command. type RelationIdsCommand struct { cmd.CommandBase ctx Context Name string out cmd.Output } func NewRelationIdsCommand(ctx Context) cmd.Command { return &RelationIdsCommand{ctx: ctx} } func (c *RelationIdsCommand) Info() *cmd.Info { args := "<name>" doc := "" if r, found := c.ctx.HookRelation(); found { args = "[<name>]" doc = fmt.Sprintf("Current default relation name is %q.", r.Name()) } return &cmd.Info{ Name: "relation-ids", Args: args, Purpose: "list all relation ids with the given relation name", Doc: doc, } } func (c *RelationIdsCommand) SetFlags(f *gnuflag.FlagSet) { c.out.AddFlags(f, "smart", cmd.DefaultFormatters) } func (c *RelationIdsCommand) Init(args []string) error { if r, found := c.ctx.HookRelation(); found { c.Name = r.Name() } if len(args) > 0 { c.Name = args[0] args = args[1:] } else if c.Name == "" { return fmt.Errorf("no relation name specified") } return cmd.CheckEmpty(args) } func (c *RelationIdsCommand) Run(ctx *cmd.Context) error { result := []string{} for _, id := range c.ctx.RelationIds() { if r, found := c.ctx.Relation(id); found && r.Name() == c.Name { result = append(result, r.FakeId()) } } sort.Strings(result) return c.out.Write(ctx, result) } ������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/juju-log_test.go�������������������0000644�0000153�0000161�00000005631�12321735642�027753� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( "fmt" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/jujuc" ) type JujuLogSuite struct { ContextSuite } var _ = gc.Suite(&JujuLogSuite{}) func assertLogs(c *gc.C, ctx jujuc.Context, writer *loggo.TestWriter, unitname, badge string) { msg1 := "the chickens" msg2 := "are 110% AWESOME" com, err := jujuc.NewCommand(ctx, "juju-log") c.Assert(err, gc.IsNil) for _, t := range []struct { args []string level loggo.Level }{ { level: loggo.INFO, }, { args: []string{"--debug"}, level: loggo.DEBUG, }, { args: []string{"--log-level", "TRACE"}, level: loggo.TRACE, }, { args: []string{"--log-level", "info"}, level: loggo.INFO, }, { args: []string{"--log-level", "WaRnInG"}, level: loggo.WARNING, }, { args: []string{"--log-level", "error"}, level: loggo.ERROR, }, } { writer.Clear() c.Assert(err, gc.IsNil) args := append(t.args, msg1, msg2) code := cmd.Main(com, &cmd.Context{}, args) c.Assert(code, gc.Equals, 0) c.Assert(writer.Log, gc.HasLen, 1) c.Assert(writer.Log[0].Level, gc.Equals, t.level) c.Assert(writer.Log[0].Module, gc.Equals, fmt.Sprintf("unit.%s.juju-log", unitname)) c.Assert(writer.Log[0].Message, gc.Equals, fmt.Sprintf("%s%s %s", badge, msg1, msg2)) } } func (s *JujuLogSuite) TestBadges(c *gc.C) { tw := &loggo.TestWriter{} _, err := loggo.ReplaceDefaultWriter(tw) loggo.GetLogger("unit").SetLogLevel(loggo.TRACE) c.Assert(err, gc.IsNil) hctx := s.GetHookContext(c, -1, "") assertLogs(c, hctx, tw, "u/0", "") hctx = s.GetHookContext(c, 1, "u/1") assertLogs(c, hctx, tw, "u/0", "peer1:1: ") } func newJujuLogCommand(c *gc.C) cmd.Command { ctx := &Context{} com, err := jujuc.NewCommand(ctx, "juju-log") c.Assert(err, gc.IsNil) return com } func (s *JujuLogSuite) TestRequiresMessage(c *gc.C) { com := newJujuLogCommand(c) testing.TestInit(c, com, nil, "no message specified") } func (s *JujuLogSuite) TestLogInitMissingLevel(c *gc.C) { com := newJujuLogCommand(c) testing.TestInit(c, com, []string{"-l"}, "flag needs an argument.*") com = newJujuLogCommand(c) testing.TestInit(c, com, []string{"--log-level"}, "flag needs an argument.*") } func (s *JujuLogSuite) TestLogInitMissingMessage(c *gc.C) { com := newJujuLogCommand(c) testing.TestInit(c, com, []string{"-l", "FATAL"}, "no message specified") com = newJujuLogCommand(c) testing.TestInit(c, com, []string{"--log-level", "FATAL"}, "no message specified") } func (s *JujuLogSuite) TestLogDeprecation(c *gc.C) { com := newJujuLogCommand(c) ctx, err := testing.RunCommand(c, com, []string{"--format", "foo", "msg"}) c.Assert(err, gc.IsNil) c.Assert(testing.Stderr(ctx), gc.Equals, "--format flag deprecated for command \"juju-log\"") } �������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/config-get.go����������������������0000644�0000153�0000161�00000003244�12321735642�027200� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc import ( "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) // ConfigGetCommand implements the config-get command. type ConfigGetCommand struct { cmd.CommandBase ctx Context Key string // The key to show. If empty, show all. All bool out cmd.Output } func NewConfigGetCommand(ctx Context) cmd.Command { return &ConfigGetCommand{ctx: ctx} } func (c *ConfigGetCommand) Info() *cmd.Info { doc := ` When no <key> is supplied, all keys with values or defaults are printed. If --all is set, all known keys are printed; those without defaults or values are reported as null. <key> and --all are mutually exclusive. ` return &cmd.Info{ Name: "config-get", Args: "[<key>]", Purpose: "print service configuration", Doc: doc, } } func (c *ConfigGetCommand) SetFlags(f *gnuflag.FlagSet) { c.out.AddFlags(f, "smart", cmd.DefaultFormatters) f.BoolVar(&c.All, "a", false, "print all keys") f.BoolVar(&c.All, "all", false, "") } func (c *ConfigGetCommand) Init(args []string) error { if args == nil { return nil } c.Key = args[0] if c.Key != "" && c.All { return fmt.Errorf("cannot use argument --all together with key %q", c.Key) } return cmd.CheckEmpty(args[1:]) } func (c *ConfigGetCommand) Run(ctx *cmd.Context) error { settings, err := c.ctx.ConfigSettings() if err != nil { return err } var value interface{} if c.Key == "" { if !c.All { for k, v := range settings { if v == nil { delete(settings, k) } } } value = settings } else { value, _ = settings[c.Key] } return c.out.Write(ctx, value) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/owner-get_test.go������������������0000644�0000153�0000161�00000004757�12321735642�030136� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( "io/ioutil" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/jujuc" ) type OwnerGetSuite struct { ContextSuite } var _ = gc.Suite(&OwnerGetSuite{}) var ownerGetTests = []struct { args []string out string }{ {[]string{"tag"}, "test-owner\n"}, {[]string{"tag", "--format", "yaml"}, "test-owner\n"}, {[]string{"tag", "--format", "json"}, `"test-owner"` + "\n"}, } func (s *OwnerGetSuite) createCommand(c *gc.C) cmd.Command { hctx := s.GetHookContext(c, -1, "") com, err := jujuc.NewCommand(hctx, "owner-get") c.Assert(err, gc.IsNil) return com } func (s *OwnerGetSuite) TestOutputFormat(c *gc.C) { for _, t := range ownerGetTests { com := s.createCommand(c) ctx := testing.Context(c) code := cmd.Main(com, ctx, t.args) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") c.Assert(bufferString(ctx.Stdout), gc.Matches, t.out) } } func (s *OwnerGetSuite) TestHelp(c *gc.C) { com := s.createCommand(c) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--help"}) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stdout), gc.Equals, `usage: owner-get [options] <setting> purpose: print information about the owner of the service. The only valid value for <setting> is currently tag options: --format (= smart) specify output format (json|smart|yaml) -o, --output (= "") specify an output file `) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } func (s *OwnerGetSuite) TestOutputPath(c *gc.C) { com := s.createCommand(c) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--output", "some-file", "tag"}) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") c.Assert(bufferString(ctx.Stdout), gc.Equals, "") content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, "test-owner\n") } func (s *OwnerGetSuite) TestUnknownSetting(c *gc.C) { com := s.createCommand(c) err := testing.InitCommand(com, []string{"unknown-setting"}) c.Assert(err, gc.ErrorMatches, `unknown setting "unknown-setting"`) } func (s *OwnerGetSuite) TestUnknownArg(c *gc.C) { com := s.createCommand(c) err := testing.InitCommand(com, []string{"tag", "blah"}) c.Assert(err, gc.ErrorMatches, `unrecognized args: \["blah"\]`) } �����������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/unit-get.go������������������������0000644�0000153�0000161�00000002407�12321735642�026712� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc import ( "errors" "fmt" "launchpad.net/gnuflag" "launchpad.net/juju-core/cmd" ) // UnitGetCommand implements the unit-get command. type UnitGetCommand struct { cmd.CommandBase ctx Context Key string out cmd.Output } func NewUnitGetCommand(ctx Context) cmd.Command { return &UnitGetCommand{ctx: ctx} } func (c *UnitGetCommand) Info() *cmd.Info { return &cmd.Info{ Name: "unit-get", Args: "<setting>", Purpose: "print public-address or private-address", } } func (c *UnitGetCommand) SetFlags(f *gnuflag.FlagSet) { c.out.AddFlags(f, "smart", cmd.DefaultFormatters) } func (c *UnitGetCommand) Init(args []string) error { if args == nil { return errors.New("no setting specified") } if args[0] != "private-address" && args[0] != "public-address" { return fmt.Errorf("unknown setting %q", args[0]) } c.Key = args[0] return cmd.CheckEmpty(args[1:]) } func (c *UnitGetCommand) Run(ctx *cmd.Context) error { value, ok := "", false if c.Key == "private-address" { value, ok = c.ctx.PrivateAddress() } else { value, ok = c.ctx.PublicAddress() } if !ok { return fmt.Errorf("%s not set", c.Key) } return c.out.Write(ctx, value) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/server_test.go���������������������0000644�0000153�0000161�00000013213�12321735642�027520� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( "errors" "fmt" "io/ioutil" "net/rpc" "os" "path/filepath" "sync" "time" jc "github.com/juju/testing/checkers" "launchpad.net/gnuflag" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/exec" "launchpad.net/juju-core/worker/uniter/jujuc" ) type RpcCommand struct { cmd.CommandBase Value string Slow bool } func (c *RpcCommand) Info() *cmd.Info { return &cmd.Info{ Name: "remote", Purpose: "act at a distance", Doc: "blah doc", } } func (c *RpcCommand) SetFlags(f *gnuflag.FlagSet) { f.StringVar(&c.Value, "value", "", "doc") f.BoolVar(&c.Slow, "slow", false, "doc") } func (c *RpcCommand) Init(args []string) error { return cmd.CheckEmpty(args) } func (c *RpcCommand) Run(ctx *cmd.Context) error { if c.Value == "error" { return errors.New("blam") } if c.Slow { time.Sleep(testing.ShortWait) return nil } ctx.Stdout.Write([]byte("eye of newt\n")) ctx.Stderr.Write([]byte("toe of frog\n")) return ioutil.WriteFile(ctx.AbsPath("local"), []byte(c.Value), 0644) } func factory(contextId, cmdName string) (cmd.Command, error) { if contextId != "validCtx" { return nil, fmt.Errorf("unknown context %q", contextId) } if cmdName != "remote" { return nil, fmt.Errorf("unknown command %q", cmdName) } return &RpcCommand{}, nil } type ServerSuite struct { testbase.LoggingSuite server *jujuc.Server sockPath string err chan error } var _ = gc.Suite(&ServerSuite{}) func (s *ServerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.sockPath = filepath.Join(c.MkDir(), "test.sock") srv, err := jujuc.NewServer(factory, s.sockPath) c.Assert(err, gc.IsNil) c.Assert(srv, gc.NotNil) s.server = srv s.err = make(chan error) go func() { s.err <- s.server.Run() }() } func (s *ServerSuite) TearDownTest(c *gc.C) { s.server.Close() c.Assert(<-s.err, gc.IsNil) _, err := os.Open(s.sockPath) c.Assert(err, jc.Satisfies, os.IsNotExist) s.LoggingSuite.TearDownTest(c) } func (s *ServerSuite) Call(c *gc.C, req jujuc.Request) (resp exec.ExecResponse, err error) { client, err := rpc.Dial("unix", s.sockPath) c.Assert(err, gc.IsNil) defer client.Close() err = client.Call("Jujuc.Main", req, &resp) return resp, err } func (s *ServerSuite) TestHappyPath(c *gc.C) { dir := c.MkDir() resp, err := s.Call(c, jujuc.Request{ "validCtx", dir, "remote", []string{"--value", "something"}, }) c.Assert(err, gc.IsNil) c.Assert(resp.Code, gc.Equals, 0) c.Assert(string(resp.Stdout), gc.Equals, "eye of newt\n") c.Assert(string(resp.Stderr), gc.Equals, "toe of frog\n") content, err := ioutil.ReadFile(filepath.Join(dir, "local")) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, "something") } func (s *ServerSuite) TestLocks(c *gc.C) { var wg sync.WaitGroup t0 := time.Now() for i := 0; i < 4; i++ { wg.Add(1) go func() { dir := c.MkDir() resp, err := s.Call(c, jujuc.Request{ "validCtx", dir, "remote", []string{"--slow"}, }) c.Assert(err, gc.IsNil) c.Assert(resp.Code, gc.Equals, 0) wg.Done() }() } wg.Wait() t1 := time.Now() c.Assert(t0.Add(4*testing.ShortWait).Before(t1), gc.Equals, true) } func (s *ServerSuite) TestBadCommandName(c *gc.C) { dir := c.MkDir() _, err := s.Call(c, jujuc.Request{"validCtx", dir, "", nil}) c.Assert(err, gc.ErrorMatches, "bad request: command not specified") _, err = s.Call(c, jujuc.Request{"validCtx", dir, "witchcraft", nil}) c.Assert(err, gc.ErrorMatches, `bad request: unknown command "witchcraft"`) } func (s *ServerSuite) TestBadDir(c *gc.C) { for _, req := range []jujuc.Request{ {"validCtx", "", "anything", nil}, {"validCtx", "foo/bar", "anything", nil}, } { _, err := s.Call(c, req) c.Assert(err, gc.ErrorMatches, "bad request: Dir is not absolute") } } func (s *ServerSuite) TestBadContextId(c *gc.C) { _, err := s.Call(c, jujuc.Request{"whatever", c.MkDir(), "remote", nil}) c.Assert(err, gc.ErrorMatches, `bad request: unknown context "whatever"`) } func (s *ServerSuite) AssertBadCommand(c *gc.C, args []string, code int) exec.ExecResponse { resp, err := s.Call(c, jujuc.Request{"validCtx", c.MkDir(), args[0], args[1:]}) c.Assert(err, gc.IsNil) c.Assert(resp.Code, gc.Equals, code) return resp } func (s *ServerSuite) TestParseError(c *gc.C) { resp := s.AssertBadCommand(c, []string{"remote", "--cheese"}, 2) c.Assert(string(resp.Stdout), gc.Equals, "") c.Assert(string(resp.Stderr), gc.Equals, "error: flag provided but not defined: --cheese\n") } func (s *ServerSuite) TestBrokenCommand(c *gc.C) { resp := s.AssertBadCommand(c, []string{"remote", "--value", "error"}, 1) c.Assert(string(resp.Stdout), gc.Equals, "") c.Assert(string(resp.Stderr), gc.Equals, "error: blam\n") } type NewCommandSuite struct { ContextSuite } var _ = gc.Suite(&NewCommandSuite{}) var newCommandTests = []struct { name string err string }{ {"close-port", ""}, {"config-get", ""}, {"juju-log", ""}, {"open-port", ""}, {"relation-get", ""}, {"relation-ids", ""}, {"relation-list", ""}, {"relation-set", ""}, {"unit-get", ""}, {"random", "unknown command: random"}, } func (s *NewCommandSuite) TestNewCommand(c *gc.C) { ctx := s.GetHookContext(c, 0, "") for _, t := range newCommandTests { com, err := jujuc.NewCommand(ctx, t.name) if t.err == "" { // At this level, just check basic sanity; commands are tested in // more detail elsewhere. c.Assert(err, gc.IsNil) c.Assert(com.Info().Name, gc.Equals, t.name) } else { c.Assert(com, gc.IsNil) c.Assert(err, gc.ErrorMatches, t.err) } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/jujuc/config-get_test.go�����������������0000644�0000153�0000161�00000012616�12321735642�030242� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package jujuc_test import ( "encoding/json" "io/ioutil" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/jujuc" ) type ConfigGetSuite struct { ContextSuite } var _ = gc.Suite(&ConfigGetSuite{}) var configGetKeyTests = []struct { args []string out string }{ {[]string{"monsters"}, "False\n"}, {[]string{"--format", "yaml", "monsters"}, "false\n"}, {[]string{"--format", "json", "monsters"}, "false\n"}, {[]string{"spline-reticulation"}, "45\n"}, {[]string{"--format", "yaml", "spline-reticulation"}, "45\n"}, {[]string{"--format", "json", "spline-reticulation"}, "45\n"}, {[]string{"missing"}, ""}, {[]string{"--format", "yaml", "missing"}, ""}, {[]string{"--format", "json", "missing"}, "null\n"}, } func (s *ConfigGetSuite) TestOutputFormatKey(c *gc.C) { for i, t := range configGetKeyTests { c.Logf("test %d: %#v", i, t.args) hctx := s.GetHookContext(c, -1, "") com, err := jujuc.NewCommand(hctx, "config-get") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, t.args) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") c.Assert(bufferString(ctx.Stdout), gc.Matches, t.out) } } var ( configGetYamlMap = map[string]interface{}{ "monsters": false, "spline-reticulation": 45, "title": "My Title", "username": "admin001", } configGetJsonMap = map[string]interface{}{ "monsters": false, "spline-reticulation": 45.0, "title": "My Title", "username": "admin001", } configGetYamlMapAll = map[string]interface{}{ "empty": nil, "monsters": false, "spline-reticulation": 45, "title": "My Title", "username": "admin001", } configGetJsonMapAll = map[string]interface{}{ "empty": nil, "monsters": false, "spline-reticulation": 45.0, "title": "My Title", "username": "admin001", } ) const ( formatYaml = iota formatJson ) var configGetAllTests = []struct { args []string format int out map[string]interface{} }{ {nil, formatYaml, configGetYamlMap}, {[]string{"--format", "yaml"}, formatYaml, configGetYamlMap}, {[]string{"--format", "json"}, formatJson, configGetJsonMap}, {[]string{"--all", "--format", "yaml"}, formatYaml, configGetYamlMapAll}, {[]string{"--all", "--format", "json"}, formatJson, configGetJsonMapAll}, {[]string{"-a", "--format", "yaml"}, formatYaml, configGetYamlMapAll}, {[]string{"-a", "--format", "json"}, formatJson, configGetJsonMapAll}, } func (s *ConfigGetSuite) TestOutputFormatAll(c *gc.C) { for i, t := range configGetAllTests { c.Logf("test %d: %#v", i, t.args) hctx := s.GetHookContext(c, -1, "") com, err := jujuc.NewCommand(hctx, "config-get") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, t.args) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") out := map[string]interface{}{} switch t.format { case formatYaml: c.Assert(goyaml.Unmarshal(bufferBytes(ctx.Stdout), &out), gc.IsNil) case formatJson: c.Assert(json.Unmarshal(bufferBytes(ctx.Stdout), &out), gc.IsNil) } c.Assert(out, gc.DeepEquals, t.out) } } func (s *ConfigGetSuite) TestHelp(c *gc.C) { hctx := s.GetHookContext(c, -1, "") com, err := jujuc.NewCommand(hctx, "config-get") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--help"}) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stdout), gc.Equals, `usage: config-get [options] [<key>] purpose: print service configuration options: -a, --all (= false) print all keys --format (= smart) specify output format (json|smart|yaml) -o, --output (= "") specify an output file When no <key> is supplied, all keys with values or defaults are printed. If --all is set, all known keys are printed; those without defaults or values are reported as null. <key> and --all are mutually exclusive. `) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") } func (s *ConfigGetSuite) TestOutputPath(c *gc.C) { hctx := s.GetHookContext(c, -1, "") com, err := jujuc.NewCommand(hctx, "config-get") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--output", "some-file", "monsters"}) c.Assert(code, gc.Equals, 0) c.Assert(bufferString(ctx.Stderr), gc.Equals, "") c.Assert(bufferString(ctx.Stdout), gc.Equals, "") content, err := ioutil.ReadFile(filepath.Join(ctx.Dir, "some-file")) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, "False\n") } func (s *ConfigGetSuite) TestUnknownArg(c *gc.C) { hctx := s.GetHookContext(c, -1, "") com, err := jujuc.NewCommand(hctx, "config-get") c.Assert(err, gc.IsNil) testing.TestInit(c, com, []string{"multiple", "keys"}, `unrecognized args: \["keys"\]`) } func (s *ConfigGetSuite) TestAllPlusKey(c *gc.C) { hctx := s.GetHookContext(c, -1, "") com, err := jujuc.NewCommand(hctx, "config-get") c.Assert(err, gc.IsNil) ctx := testing.Context(c) code := cmd.Main(com, ctx, []string{"--all", "--format", "json", "monsters"}) c.Assert(code, gc.Equals, 2) c.Assert(bufferString(ctx.Stderr), gc.Equals, "error: cannot use argument --all together with key \"monsters\"\n") } ������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/state_test.go����������������������������0000644�0000153�0000161�00000007322�12321735642�026216� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker/uniter" "launchpad.net/juju-core/worker/uniter/hook" ) type StateFileSuite struct{} var _ = gc.Suite(&StateFileSuite{}) var stcurl = charm.MustParseURL("cs:quantal/service-name-123") var relhook = &hook.Info{ Kind: hooks.RelationJoined, RemoteUnit: "some-thing/123", } var stateTests = []struct { st uniter.State err string }{ // Invalid op/step. { st: uniter.State{Op: uniter.Op("bloviate")}, err: `unknown operation "bloviate"`, }, { st: uniter.State{ Op: uniter.Continue, OpStep: uniter.OpStep("dudelike"), Hook: &hook.Info{Kind: hooks.ConfigChanged}, }, err: `unknown operation step "dudelike"`, }, // Install operation. { st: uniter.State{ Op: uniter.Install, OpStep: uniter.Pending, Hook: &hook.Info{Kind: hooks.ConfigChanged}, }, err: `unexpected hook info`, }, { st: uniter.State{ Op: uniter.Install, OpStep: uniter.Pending, }, err: `missing charm URL`, }, { st: uniter.State{ Op: uniter.Install, OpStep: uniter.Pending, CharmURL: stcurl, }, }, // RunHook operation. { st: uniter.State{ Op: uniter.RunHook, OpStep: uniter.Pending, Hook: &hook.Info{Kind: hooks.Kind("machine-exploded")}, }, err: `unknown hook kind "machine-exploded"`, }, { st: uniter.State{ Op: uniter.RunHook, OpStep: uniter.Pending, Hook: &hook.Info{Kind: hooks.RelationJoined}, }, err: `"relation-joined" hook requires a remote unit`, }, { st: uniter.State{ Op: uniter.RunHook, OpStep: uniter.Pending, Hook: &hook.Info{Kind: hooks.ConfigChanged}, CharmURL: stcurl, }, err: `unexpected charm URL`, }, { st: uniter.State{ Op: uniter.RunHook, OpStep: uniter.Pending, Hook: &hook.Info{Kind: hooks.ConfigChanged}, }, }, { st: uniter.State{ Op: uniter.RunHook, OpStep: uniter.Pending, Hook: relhook, }, }, // Upgrade operation. { st: uniter.State{ Op: uniter.Upgrade, OpStep: uniter.Pending, }, err: `missing charm URL`, }, { st: uniter.State{ Op: uniter.Upgrade, OpStep: uniter.Pending, CharmURL: stcurl, }, }, { st: uniter.State{ Op: uniter.Upgrade, OpStep: uniter.Pending, Hook: relhook, CharmURL: stcurl, }, }, // Continue operation. { st: uniter.State{ Op: uniter.Continue, OpStep: uniter.Pending, }, err: `missing hook info`, }, { st: uniter.State{ Op: uniter.Continue, OpStep: uniter.Pending, Hook: relhook, CharmURL: stcurl, }, err: `unexpected charm URL`, }, { st: uniter.State{ Op: uniter.Continue, OpStep: uniter.Pending, Hook: relhook, }, }, } func (s *StateFileSuite) TestStates(c *gc.C) { for i, t := range stateTests { c.Logf("test %d", i) path := filepath.Join(c.MkDir(), "uniter") file := uniter.NewStateFile(path) _, err := file.Read() c.Assert(err, gc.Equals, uniter.ErrNoStateFile) write := func() { err := file.Write(t.st.Started, t.st.Op, t.st.OpStep, t.st.Hook, t.st.CharmURL) c.Assert(err, gc.IsNil) } if t.err != "" { c.Assert(write, gc.PanicMatches, "invalid uniter state: "+t.err) err := utils.WriteYaml(path, &t.st) c.Assert(err, gc.IsNil) _, err = file.Read() c.Assert(err, gc.ErrorMatches, "cannot read charm state at .*: invalid uniter state: "+t.err) continue } write() st, err := file.Read() c.Assert(err, gc.IsNil) c.Assert(*st, gc.DeepEquals, t.st) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/relation/��������������������������������0000755�0000153�0000161�00000000000�12321735643�025322� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/relation/relation.go���������������������0000644�0000153�0000161�00000016227�12321735642�027475� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // relation implements persistent local storage of a unit's relation state, and // translation of relation changes into hooks that need to be run. package relation import ( "fmt" "io/ioutil" "os" "path/filepath" "strconv" "strings" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker/uniter/hook" ) // State describes the state of a relation. type State struct { // RelationId identifies the relation. RelationId int // Members is a map from unit name to the last change version // for which a hook.Info was delivered on the output channel. Members map[string]int64 // ChangedPending indicates that a "relation-changed" hook for the given // unit name must be the first hook.Info to be sent to the output channel. ChangedPending string } // copy returns an independent copy of the state. func (s *State) copy() *State { copy := &State{ RelationId: s.RelationId, ChangedPending: s.ChangedPending, } if s.Members != nil { copy.Members = map[string]int64{} for m, v := range s.Members { copy.Members[m] = v } } return copy } // Validate returns an error if the supplied hook.Info does not represent // a valid change to the relation state. Hooks must always be validated // against the current state before they are run, to ensure that the system // meets its guarantees about hook execution order. func (s *State) Validate(hi hook.Info) (err error) { defer utils.ErrorContextf(&err, "inappropriate %q for %q", hi.Kind, hi.RemoteUnit) if hi.RelationId != s.RelationId { return fmt.Errorf("expected relation %d, got relation %d", s.RelationId, hi.RelationId) } if s.Members == nil { return fmt.Errorf(`relation is broken and cannot be changed further`) } unit, kind := hi.RemoteUnit, hi.Kind if kind == hooks.RelationBroken { if len(s.Members) == 0 { return nil } return fmt.Errorf(`cannot run "relation-broken" while units still present`) } if s.ChangedPending != "" { if unit != s.ChangedPending || kind != hooks.RelationChanged { return fmt.Errorf(`expected "relation-changed" for %q`, s.ChangedPending) } } else if _, joined := s.Members[unit]; joined && kind == hooks.RelationJoined { return fmt.Errorf("unit already joined") } else if !joined && kind != hooks.RelationJoined { return fmt.Errorf("unit has not joined") } return nil } // StateDir is a filesystem-backed representation of the state of a // relation. Concurrent modifications to the underlying state directory // will have undefined consequences. type StateDir struct { // path identifies the directory holding persistent state. path string // state is the cached state of the directory, which is guaranteed // to be synchronized with the true state so long as no concurrent // changes are made to the directory. state State } // State returns the current state of the relation. func (d *StateDir) State() *State { return d.state.copy() } // ReadStateDir loads a StateDir from the subdirectory of dirPath named // for the supplied RelationId. If the directory does not exist, no error // is returned, func ReadStateDir(dirPath string, relationId int) (d *StateDir, err error) { d = &StateDir{ filepath.Join(dirPath, strconv.Itoa(relationId)), State{relationId, map[string]int64{}, ""}, } defer utils.ErrorContextf(&err, "cannot load relation state from %q", d.path) if _, err := os.Stat(d.path); os.IsNotExist(err) { return d, nil } else if err != nil { return nil, err } fis, err := ioutil.ReadDir(d.path) if err != nil { return nil, err } for _, fi := range fis { // Entries with names ending in "-" followed by an integer must be // files containing valid unit data; all other names are ignored. name := fi.Name() i := strings.LastIndex(name, "-") if i == -1 { continue } svcName := name[:i] unitId := name[i+1:] if _, err := strconv.Atoi(unitId); err != nil { continue } unitName := svcName + "/" + unitId var info diskInfo if err = utils.ReadYaml(filepath.Join(d.path, name), &info); err != nil { return nil, fmt.Errorf("invalid unit file %q: %v", name, err) } if info.ChangeVersion == nil { return nil, fmt.Errorf(`invalid unit file %q: "changed-version" not set`, name) } d.state.Members[unitName] = *info.ChangeVersion if info.ChangedPending { if d.state.ChangedPending != "" { return nil, fmt.Errorf("%q and %q both have pending changed hooks", d.state.ChangedPending, unitName) } d.state.ChangedPending = unitName } } return d, nil } // ReadAllStateDirs loads and returns every StateDir persisted directly inside // the supplied dirPath. If dirPath does not exist, no error is returned. func ReadAllStateDirs(dirPath string) (dirs map[int]*StateDir, err error) { defer utils.ErrorContextf(&err, "cannot load relations state from %q", dirPath) if _, err := os.Stat(dirPath); os.IsNotExist(err) { return nil, nil } else if err != nil { return nil, err } fis, err := ioutil.ReadDir(dirPath) if err != nil { return nil, err } dirs = map[int]*StateDir{} for _, fi := range fis { // Entries with integer names must be directories containing StateDir // data; all other names will be ignored. relationId, err := strconv.Atoi(fi.Name()) if err != nil { // This doesn't look like a relation. continue } dir, err := ReadStateDir(dirPath, relationId) if err != nil { return nil, err } dirs[relationId] = dir } return dirs, nil } // Ensure creates the directory if it does not already exist. func (d *StateDir) Ensure() error { return os.MkdirAll(d.path, 0755) } // Write atomically writes to disk the relation state change in hi. // It must be called after the respective hook was executed successfully. // Write doesn't validate hi but guarantees that successive writes of // the same hi are idempotent. func (d *StateDir) Write(hi hook.Info) (err error) { defer utils.ErrorContextf(&err, "failed to write %q hook info for %q on state directory", hi.Kind, hi.RemoteUnit) if hi.Kind == hooks.RelationBroken { return d.Remove() } name := strings.Replace(hi.RemoteUnit, "/", "-", 1) path := filepath.Join(d.path, name) if hi.Kind == hooks.RelationDeparted { if err = os.Remove(path); err != nil && !os.IsNotExist(err) { return err } // If atomic delete succeeded, update own state. delete(d.state.Members, hi.RemoteUnit) return nil } di := diskInfo{&hi.ChangeVersion, hi.Kind == hooks.RelationJoined} if err := utils.WriteYaml(path, &di); err != nil { return err } // If write was successful, update own state. d.state.Members[hi.RemoteUnit] = hi.ChangeVersion if hi.Kind == hooks.RelationJoined { d.state.ChangedPending = hi.RemoteUnit } else { d.state.ChangedPending = "" } return nil } // Remove removes the directory if it exists and is empty. func (d *StateDir) Remove() error { if err := os.Remove(d.path); err != nil && !os.IsNotExist(err) { return err } // If atomic delete succeeded, update own state. d.state.Members = nil return nil } // diskInfo defines the relation unit data serialization. type diskInfo struct { ChangeVersion *int64 `yaml:"change-version"` ChangedPending bool `yaml:"changed-pending,omitempty"` } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/relation/hookqueue.go��������������������0000644�0000153�0000161�00000024236�12321735642�027664� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package relation import ( "sort" "launchpad.net/tomb" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/worker/uniter/hook" ) // HookQueue is the minimal interface implemented by both AliveHookQueue and // DyingHookQueue. type HookQueue interface { hookQueue() Stop() error } // RelationUnitsWatcher is used to enable deterministic testing of // AliveHookQueue, by supplying a reliable stream of RelationUnitsChange // events; usually, it will be a *state.RelationUnitsWatcher. type RelationUnitsWatcher interface { Err() error Stop() error Changes() <-chan params.RelationUnitsChange } // AliveHookQueue aggregates values obtained from a relation units watcher // and sends out details about hooks that must be executed in the unit. type AliveHookQueue struct { tomb tomb.Tomb w RelationUnitsWatcher out chan<- hook.Info relationId int // info holds information about all units that were added to the // queue and haven't had a "relation-departed" event popped. This // means the unit may be in info and not currently in the queue // itself. info map[string]*unitInfo // head and tail are the ends of the queue. head, tail *unitInfo // changedPending, if not empty, indicates that the most recently // popped event was a "relation-joined" for the named unit, and // therefore that the next event must be a "relation-changed" // for that same unit. // If changedPending is not empty, the queue is considered non- // empty, even if head is nil. changedPending string } // unitInfo holds unit information for management by AliveHookQueue. type unitInfo struct { // unit holds the name of the unit. unit string // version and settings hold the most recent settings known // to the AliveHookQueue. version int64 // joined is set to true when a "relation-joined" is popped for this unit. joined bool // hookKind holds the current idea of the next hook that should // be run for the unit, and is empty if and only if the unit // is not queued. hookKind hooks.Kind // prev and next define the position in the queue of the // unit's next hook. prev, next *unitInfo } // NewAliveHookQueue returns a new AliveHookQueue that aggregates the values // obtained from the w watcher and sends into out the details about hooks that // must be executed in the unit. It guarantees that the stream of hooks will // respect the guarantees Juju makes about hook execution order. If any values // have previously been received from w's Changes channel, the AliveHookQueue's // behaviour is undefined. func NewAliveHookQueue(initial *State, out chan<- hook.Info, w RelationUnitsWatcher) *AliveHookQueue { q := &AliveHookQueue{ w: w, out: out, relationId: initial.RelationId, info: map[string]*unitInfo{}, } go q.loop(initial) return q } func (q *AliveHookQueue) loop(initial *State) { defer q.tomb.Done() defer watcher.Stop(q.w, &q.tomb) // Consume initial event, and reconcile with initial state, by inserting // a new RelationUnitsChange before the initial event, which schedules // every missing unit for immediate departure before anything else happens // (apart from a single potential required post-joined changed event). ch1, ok := <-q.w.Changes() if !ok { q.tomb.Kill(watcher.MustErr(q.w)) return } if len(ch1.Departed) != 0 { panic("AliveHookQueue must be started with a fresh RelationUnitsWatcher") } q.changedPending = initial.ChangedPending ch0 := params.RelationUnitsChange{} for unit, version := range initial.Members { q.info[unit] = &unitInfo{ unit: unit, version: version, joined: true, } if _, found := ch1.Changed[unit]; !found { ch0.Departed = append(ch0.Departed, unit) } } q.update(ch0) q.update(ch1) var next hook.Info var out chan<- hook.Info for { if q.empty() { out = nil } else { out = q.out next = q.next() } select { case <-q.tomb.Dying(): return case ch, ok := <-q.w.Changes(): if !ok { q.tomb.Kill(watcher.MustErr(q.w)) return } q.update(ch) case out <- next: q.pop() } } } func (q *AliveHookQueue) hookQueue() { panic("interface sentinel method, do not call") } // Stop stops the AliveHookQueue and returns any errors encountered during // operation or while shutting down. func (q *AliveHookQueue) Stop() error { q.tomb.Kill(nil) return q.tomb.Wait() } // empty returns true if the queue is empty. func (q *AliveHookQueue) empty() bool { return q.head == nil && q.changedPending == "" } // update modifies the queue such that the hook.Info values it sends will // reflect the supplied change. func (q *AliveHookQueue) update(ruc params.RelationUnitsChange) { // Enforce consistent addition order, mainly for testing purposes. changedUnits := []string{} for unit := range ruc.Changed { changedUnits = append(changedUnits, unit) } sort.Strings(changedUnits) for _, unit := range changedUnits { settings := ruc.Changed[unit] info, found := q.info[unit] if !found { info = &unitInfo{unit: unit} q.info[unit] = info q.queue(unit, hooks.RelationJoined) } else if info.hookKind != hooks.RelationJoined { if settings.Version != info.version { q.queue(unit, hooks.RelationChanged) } else { q.unqueue(unit) } } info.version = settings.Version } for _, unit := range ruc.Departed { if q.info[unit].hookKind == hooks.RelationJoined { q.unqueue(unit) } else { q.queue(unit, hooks.RelationDeparted) } } } // pop advances the queue. It will panic if the queue is already empty. func (q *AliveHookQueue) pop() { if q.empty() { panic("queue is empty") } if q.changedPending != "" { if q.info[q.changedPending].hookKind == hooks.RelationChanged { // We just ran this very hook; no sense keeping it queued. q.unqueue(q.changedPending) } q.changedPending = "" } else { old := *q.head q.unqueue(q.head.unit) if old.hookKind == hooks.RelationJoined { q.changedPending = old.unit q.info[old.unit].joined = true } else if old.hookKind == hooks.RelationDeparted { delete(q.info, old.unit) } } } // next returns the next hook.Info value to send. func (q *AliveHookQueue) next() hook.Info { if q.empty() { panic("queue is empty") } var unit string var kind hooks.Kind if q.changedPending != "" { unit = q.changedPending kind = hooks.RelationChanged } else { unit = q.head.unit kind = q.head.hookKind } version := q.info[unit].version return hook.Info{ Kind: kind, RelationId: q.relationId, RemoteUnit: unit, ChangeVersion: version, } } // queue sets the next hook to be run for the named unit, and places it // at the tail of the queue if it is not already queued. It will panic // if the unit is not in q.info. func (q *AliveHookQueue) queue(unit string, kind hooks.Kind) { // If the unit is not in the queue, place it at the tail. info := q.info[unit] if info.hookKind == "" { info.prev = q.tail if q.tail != nil { q.tail.next = info } q.tail = info // If the queue is empty, the tail is also the head. if q.head == nil { q.head = info } } info.hookKind = kind } // unqueue removes the named unit from the queue. It is fine to // unqueue a unit that is not in the queue, but it will panic if // the unit is not in q.info. func (q *AliveHookQueue) unqueue(unit string) { if q.head == nil { // The queue is empty, nothing to do. return } // Get the unit info and clear its next action. info := q.info[unit] if info.hookKind == "" { // The unit is not in the queue, nothing to do. return } info.hookKind = "" // Update queue pointers. if info.prev == nil { q.head = info.next } else { info.prev.next = info.next } if info.next == nil { q.tail = info.prev } else { info.next.prev = info.prev } info.prev = nil info.next = nil } // DyingHookQueue is a hook queue that deals with a relation that is being // shut down. It honours the obligations of an AliveHookQueue with respect to // relation hook execution order; as soon as those obligations are fulfilled, // it sends a "relation-departed" hook for every relation member, and finally a // "relation-broken" hook for the relation itself. type DyingHookQueue struct { tomb tomb.Tomb out chan<- hook.Info relationId int members map[string]int64 changedPending string } // NewDyingHookQueue returns a new DyingHookQueue that shuts down the state in // initial. func NewDyingHookQueue(initial *State, out chan<- hook.Info) *DyingHookQueue { q := &DyingHookQueue{ out: out, relationId: initial.RelationId, members: map[string]int64{}, changedPending: initial.ChangedPending, } for m, v := range initial.Members { q.members[m] = v } go q.loop() return q } func (q *DyingHookQueue) loop() { defer q.tomb.Done() // Honour any expected relation-changed hook. if q.changedPending != "" { select { case <-q.tomb.Dying(): return case q.out <- q.hookInfo(hooks.RelationChanged, q.changedPending): } } // Depart in consistent order, mainly for testing purposes. departs := []string{} for m := range q.members { departs = append(departs, m) } sort.Strings(departs) for _, unit := range departs { select { case <-q.tomb.Dying(): return case q.out <- q.hookInfo(hooks.RelationDeparted, unit): } } // Finally break the relation. select { case <-q.tomb.Dying(): return case q.out <- hook.Info{Kind: hooks.RelationBroken, RelationId: q.relationId}: } q.tomb.Kill(nil) return } // hookInfo updates the queue's internal membership state according to the // supplied information, and returns a hook.Info reflecting that change. func (q *DyingHookQueue) hookInfo(kind hooks.Kind, unit string) hook.Info { hi := hook.Info{ Kind: kind, RelationId: q.relationId, RemoteUnit: unit, ChangeVersion: q.members[unit], } return hi } func (q *DyingHookQueue) hookQueue() { panic("interface sentinel method, do not call") } // Stop stops the DyingHookQueue and returns any errors encountered // during operation or while shutting down. func (q *DyingHookQueue) Stop() error { q.tomb.Kill(nil) return q.tomb.Wait() } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/relation/relation_test.go����������������0000644�0000153�0000161�00000024565�12321735642�030540� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package relation_test import ( "fmt" "io/ioutil" "os" "path/filepath" "strconv" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/worker/uniter/hook" "launchpad.net/juju-core/worker/uniter/relation" ) type StateDirSuite struct{} var _ = gc.Suite(&StateDirSuite{}) func (s *StateDirSuite) TestReadStateDirEmpty(c *gc.C) { basedir := c.MkDir() reldir := filepath.Join(basedir, "123") dir, err := relation.ReadStateDir(basedir, 123) c.Assert(err, gc.IsNil) state := dir.State() c.Assert(state.RelationId, gc.Equals, 123) c.Assert(msi(state.Members), gc.DeepEquals, msi{}) c.Assert(state.ChangedPending, gc.Equals, "") _, err = os.Stat(reldir) c.Assert(err, jc.Satisfies, os.IsNotExist) err = dir.Ensure() c.Assert(err, gc.IsNil) fi, err := os.Stat(reldir) c.Assert(err, gc.IsNil) c.Assert(fi, jc.Satisfies, os.FileInfo.IsDir) } func (s *StateDirSuite) TestReadStateDirValid(c *gc.C) { basedir := c.MkDir() reldir := setUpDir(c, basedir, "123", map[string]string{ "foo-bar-1": "change-version: 99\n", "foo-bar-1.preparing": "change-version: 100\n", "baz-qux-7": "change-version: 101\nchanged-pending: true\n", "nonsensical": "blah", "27": "blah", }) setUpDir(c, reldir, "ignored", nil) dir, err := relation.ReadStateDir(basedir, 123) c.Assert(err, gc.IsNil) state := dir.State() c.Assert(state.RelationId, gc.Equals, 123) c.Assert(msi(state.Members), gc.DeepEquals, msi{"foo-bar/1": 99, "baz-qux/7": 101}) c.Assert(state.ChangedPending, gc.Equals, "baz-qux/7") } var badRelationsTests = []struct { contents map[string]string subdirs []string err string }{ { nil, []string{"foo-bar-1"}, `.* is a directory`, }, { map[string]string{"foo-1": "'"}, nil, `invalid unit file "foo-1": YAML error: .*`, }, { map[string]string{"foo-1": "blah: blah\n"}, nil, `invalid unit file "foo-1": "changed-version" not set`, }, { map[string]string{ "foo-1": "change-version: 123\nchanged-pending: true\n", "foo-2": "change-version: 456\nchanged-pending: true\n", }, nil, `"foo/1" and "foo/2" both have pending changed hooks`, }, } func (s *StateDirSuite) TestBadRelations(c *gc.C) { for i, t := range badRelationsTests { c.Logf("test %d", i) basedir := c.MkDir() reldir := setUpDir(c, basedir, "123", t.contents) for _, subdir := range t.subdirs { setUpDir(c, reldir, subdir, nil) } _, err := relation.ReadStateDir(basedir, 123) expect := `cannot load relation state from ".*": ` + t.err c.Assert(err, gc.ErrorMatches, expect) } } var defaultMembers = msi{"foo/1": 0, "foo/2": 0} // writeTests verify the behaviour of sequences of HookInfos on a relation // state that starts off containing defaultMembers. var writeTests = []struct { hooks []hook.Info members msi pending string err string deleted bool }{ // Verify that valid changes work. { hooks: []hook.Info{ {Kind: hooks.RelationChanged, RelationId: 123, RemoteUnit: "foo/1", ChangeVersion: 1}, }, members: msi{"foo/1": 1, "foo/2": 0}, }, { hooks: []hook.Info{ {Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/3"}, }, members: msi{"foo/1": 0, "foo/2": 0, "foo/3": 0}, pending: "foo/3", }, { hooks: []hook.Info{ {Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/3"}, {Kind: hooks.RelationChanged, RelationId: 123, RemoteUnit: "foo/3"}, }, members: msi{"foo/1": 0, "foo/2": 0, "foo/3": 0}, }, { hooks: []hook.Info{ {Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/1"}, }, members: msi{"foo/2": 0}, }, { hooks: []hook.Info{ {Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/1"}, {Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/1"}, }, members: msi{"foo/1": 0, "foo/2": 0}, pending: "foo/1", }, { hooks: []hook.Info{ {Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/1"}, {Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/1"}, {Kind: hooks.RelationChanged, RelationId: 123, RemoteUnit: "foo/1"}, }, members: msi{"foo/1": 0, "foo/2": 0}, }, { hooks: []hook.Info{ {Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/1"}, {Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/2"}, {Kind: hooks.RelationBroken, RelationId: 123}, }, deleted: true, }, // Verify detection of various error conditions. { hooks: []hook.Info{ {Kind: hooks.RelationJoined, RelationId: 456, RemoteUnit: "foo/1"}, }, err: "expected relation 123, got relation 456", }, { hooks: []hook.Info{ {Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/3"}, {Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/4"}, }, members: msi{"foo/1": 0, "foo/2": 0, "foo/3": 0}, pending: "foo/3", err: `expected "relation-changed" for "foo/3"`, }, { hooks: []hook.Info{ {Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/3"}, {Kind: hooks.RelationChanged, RelationId: 123, RemoteUnit: "foo/1"}, }, members: msi{"foo/1": 0, "foo/2": 0, "foo/3": 0}, pending: "foo/3", err: `expected "relation-changed" for "foo/3"`, }, { hooks: []hook.Info{ {Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/1"}, }, err: "unit already joined", }, { hooks: []hook.Info{ {Kind: hooks.RelationChanged, RelationId: 123, RemoteUnit: "foo/3"}, }, err: "unit has not joined", }, { hooks: []hook.Info{ {Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/3"}, }, err: "unit has not joined", }, { hooks: []hook.Info{ {Kind: hooks.RelationBroken, RelationId: 123}, }, err: `cannot run "relation-broken" while units still present`, }, { hooks: []hook.Info{ {Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/1"}, {Kind: hooks.RelationDeparted, RelationId: 123, RemoteUnit: "foo/2"}, {Kind: hooks.RelationBroken, RelationId: 123}, {Kind: hooks.RelationJoined, RelationId: 123, RemoteUnit: "foo/1"}, }, err: `relation is broken and cannot be changed further`, deleted: true, }, } func (s *StateDirSuite) TestWrite(c *gc.C) { for i, t := range writeTests { c.Logf("test %d", i) basedir := c.MkDir() setUpDir(c, basedir, "123", map[string]string{ "foo-1": "change-version: 0\n", "foo-2": "change-version: 0\n", }) dir, err := relation.ReadStateDir(basedir, 123) c.Assert(err, gc.IsNil) for i, hi := range t.hooks { c.Logf(" hook %d", i) if i == len(t.hooks)-1 && t.err != "" { err = dir.State().Validate(hi) expect := fmt.Sprintf(`inappropriate %q for %q: %s`, hi.Kind, hi.RemoteUnit, t.err) c.Assert(err, gc.ErrorMatches, expect) } else { err = dir.State().Validate(hi) c.Assert(err, gc.IsNil) err = dir.Write(hi) c.Assert(err, gc.IsNil) // Check that writing the same change again is OK. err = dir.Write(hi) c.Assert(err, gc.IsNil) } } members := t.members if members == nil && !t.deleted { members = defaultMembers } assertState(c, dir, basedir, 123, members, t.pending, t.deleted) } } func (s *StateDirSuite) TestRemove(c *gc.C) { basedir := c.MkDir() dir, err := relation.ReadStateDir(basedir, 1) c.Assert(err, gc.IsNil) err = dir.Ensure() c.Assert(err, gc.IsNil) err = dir.Remove() c.Assert(err, gc.IsNil) err = dir.Remove() c.Assert(err, gc.IsNil) setUpDir(c, basedir, "99", map[string]string{ "foo-1": "change-version: 0\n", }) dir, err = relation.ReadStateDir(basedir, 99) c.Assert(err, gc.IsNil) err = dir.Remove() c.Assert(err, gc.ErrorMatches, ".*: directory not empty") } type ReadAllStateDirsSuite struct{} var _ = gc.Suite(&ReadAllStateDirsSuite{}) func (s *ReadAllStateDirsSuite) TestNoDir(c *gc.C) { basedir := c.MkDir() relsdir := filepath.Join(basedir, "relations") dirs, err := relation.ReadAllStateDirs(relsdir) c.Assert(err, gc.IsNil) c.Assert(dirs, gc.HasLen, 0) _, err = os.Stat(relsdir) c.Assert(err, jc.Satisfies, os.IsNotExist) } func (s *ReadAllStateDirsSuite) TestBadStateDir(c *gc.C) { basedir := c.MkDir() relsdir := setUpDir(c, basedir, "relations", nil) setUpDir(c, relsdir, "123", map[string]string{ "bad-0": "blah: blah\n", }) _, err := relation.ReadAllStateDirs(relsdir) c.Assert(err, gc.ErrorMatches, `cannot load relations state from .*: cannot load relation state from .*: invalid unit file "bad-0": "changed-version" not set`) } func (s *ReadAllStateDirsSuite) TestReadAllStateDirs(c *gc.C) { basedir := c.MkDir() relsdir := setUpDir(c, basedir, "relations", map[string]string{ "ignored": "blah", "foo-bar-123": "gibberish", }) setUpDir(c, relsdir, "123", map[string]string{ "foo-0": "change-version: 1\n", "foo-1": "change-version: 2\nchanged-pending: true\n", "gibberish": "gibberish", }) setUpDir(c, relsdir, "456", map[string]string{ "bar-0": "change-version: 3\n", "bar-1": "change-version: 4\n", }) setUpDir(c, relsdir, "789", nil) setUpDir(c, relsdir, "onethousand", map[string]string{ "baz-0": "change-version: 3\n", "baz-1": "change-version: 4\n", }) dirs, err := relation.ReadAllStateDirs(relsdir) c.Assert(err, gc.IsNil) for id, dir := range dirs { c.Logf("%d: %#v", id, dir) } assertState(c, dirs[123], relsdir, 123, msi{"foo/0": 1, "foo/1": 2}, "foo/1", false) assertState(c, dirs[456], relsdir, 456, msi{"bar/0": 3, "bar/1": 4}, "", false) assertState(c, dirs[789], relsdir, 789, msi{}, "", false) c.Assert(dirs, gc.HasLen, 3) } func setUpDir(c *gc.C, basedir, name string, contents map[string]string) string { reldir := filepath.Join(basedir, name) err := os.Mkdir(reldir, 0777) c.Assert(err, gc.IsNil) for name, content := range contents { path := filepath.Join(reldir, name) err := ioutil.WriteFile(path, []byte(content), 0777) c.Assert(err, gc.IsNil) } return reldir } func assertState(c *gc.C, dir *relation.StateDir, relsdir string, relationId int, members msi, pending string, deleted bool) { expect := &relation.State{ RelationId: relationId, Members: map[string]int64(members), ChangedPending: pending, } c.Assert(dir.State(), gc.DeepEquals, expect) if deleted { _, err := os.Stat(filepath.Join(relsdir, strconv.Itoa(relationId))) c.Assert(err, jc.Satisfies, os.IsNotExist) } else { fresh, err := relation.ReadStateDir(relsdir, relationId) c.Assert(err, gc.IsNil) c.Assert(fresh.State(), gc.DeepEquals, expect) } } �������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/relation/hookqueue_test.go���������������0000644�0000153�0000161�00000021156�12321735642�030721� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package relation_test import ( stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm/hooks" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/uniter/hook" "launchpad.net/juju-core/worker/uniter/relation" ) func Test(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type HookQueueSuite struct{} var _ = gc.Suite(&HookQueueSuite{}) type msi map[string]int64 type hookQueueTest struct { summary string initial *relation.State steps []checker } func fullTest(summary string, steps ...checker) hookQueueTest { return hookQueueTest{summary, &relation.State{21345, nil, ""}, steps} } func reconcileTest(summary string, members msi, joined string, steps ...checker) hookQueueTest { return hookQueueTest{summary, &relation.State{21345, members, joined}, steps} } var aliveHookQueueTests = []hookQueueTest{ fullTest( "Empty initial change causes no hooks.", send{nil, nil}, ), fullTest( "Joined and changed are both run when unit is first detected.", send{msi{"u/0": 0}, nil}, expect{hooks.RelationJoined, "u/0", 0}, expect{hooks.RelationChanged, "u/0", 0}, ), fullTest( "Automatic changed is run with latest settings.", send{msi{"u/0": 0}, nil}, expect{hooks.RelationJoined, "u/0", 0}, send{msi{"u/0": 7}, nil}, expect{hooks.RelationChanged, "u/0", 7}, ), fullTest( "Joined is also run with latest settings.", send{msi{"u/0": 0}, nil}, send{msi{"u/0": 7}, nil}, expect{hooks.RelationJoined, "u/0", 7}, expect{hooks.RelationChanged, "u/0", 7}, ), fullTest( "Nothing happens if a unit departs before its joined is run.", send{msi{"u/0": 0}, nil}, send{msi{"u/0": 7}, nil}, send{nil, []string{"u/0"}}, ), fullTest( "A changed is run after a joined, even if a departed is known.", send{msi{"u/0": 0}, nil}, expect{hooks.RelationJoined, "u/0", 0}, send{nil, []string{"u/0"}}, expect{hooks.RelationChanged, "u/0", 0}, expect{hooks.RelationDeparted, "u/0", 0}, ), fullTest( "A departed replaces a changed.", send{msi{"u/0": 0}, nil}, advance{2}, send{msi{"u/0": 7}, nil}, send{nil, []string{"u/0"}}, expect{hooks.RelationDeparted, "u/0", 7}, ), fullTest( "Changed events are ignored if the version has not changed.", send{msi{"u/0": 0}, nil}, advance{2}, send{msi{"u/0": 0}, nil}, ), fullTest( "Redundant changed events are elided.", send{msi{"u/0": 0}, nil}, advance{2}, send{msi{"u/0": 3}, nil}, send{msi{"u/0": 7}, nil}, send{msi{"u/0": 79}, nil}, expect{hooks.RelationChanged, "u/0", 79}, ), fullTest( "Latest hooks are run in the original unit order.", send{msi{"u/0": 0, "u/1": 1}, nil}, advance{4}, send{msi{"u/0": 3}, nil}, send{msi{"u/1": 7}, nil}, send{nil, []string{"u/0"}}, expect{hooks.RelationDeparted, "u/0", 3}, expect{hooks.RelationChanged, "u/1", 7}, ), fullTest( "Test everything we can think of at the same time.", send{msi{"u/0": 0, "u/1": 0, "u/2": 0, "u/3": 0, "u/4": 0}, nil}, advance{6}, // u/0, u/1, u/2 are now up to date; u/3, u/4 are untouched. send{msi{"u/0": 1}, nil}, send{msi{"u/1": 1, "u/2": 1, "u/3": 1, "u/5": 0}, []string{"u/0", "u/4"}}, send{msi{"u/3": 2}, nil}, // - Finish off the rest of the initial state, ignoring u/4, but using // the latest known settings. expect{hooks.RelationJoined, "u/3", 2}, expect{hooks.RelationChanged, "u/3", 2}, // - u/0 was queued for change by the first RUC, but this change is // no longer relevant; it's departed in the second RUC, so we run // that hook instead. expect{hooks.RelationDeparted, "u/0", 1}, // - Handle the remaining changes in the second RUC, still ignoring u/4. // We do run new changed hooks for u/1 and u/2, because the latest settings // are newer than those used in their original changed events. expect{hooks.RelationChanged, "u/1", 1}, expect{hooks.RelationChanged, "u/2", 1}, expect{hooks.RelationJoined, "u/5", 0}, expect{hooks.RelationChanged, "u/5", 0}, // - Ignore the third RUC, because the original joined/changed on u/3 // was executed after we got the latest settings version. ), reconcileTest( "Check that matching settings versions cause no changes.", msi{"u/0": 0}, "", send{msi{"u/0": 0}, nil}, ), reconcileTest( "Check that new settings versions cause appropriate changes.", msi{"u/0": 0}, "", send{msi{"u/0": 1}, nil}, expect{hooks.RelationChanged, "u/0", 1}, ), reconcileTest( "Check that a just-joined unit gets its changed hook run first.", msi{"u/0": 0}, "u/0", send{msi{"u/0": 0}, nil}, expect{hooks.RelationChanged, "u/0", 0}, ), reconcileTest( "Check that missing units are queued for depart as early as possible.", msi{"u/0": 0}, "", send{msi{"u/1": 0}, nil}, expect{hooks.RelationDeparted, "u/0", 0}, expect{hooks.RelationJoined, "u/1", 0}, expect{hooks.RelationChanged, "u/1", 0}, ), reconcileTest( "Double-check that a pending changed happens before an injected departed.", msi{"u/0": 0}, "u/0", send{nil, nil}, expect{hooks.RelationChanged, "u/0", 0}, expect{hooks.RelationDeparted, "u/0", 0}, ), reconcileTest( "Check that missing units don't slip in front of required changed hooks.", msi{"u/0": 0}, "u/0", send{msi{"u/1": 0}, nil}, expect{hooks.RelationChanged, "u/0", 0}, expect{hooks.RelationDeparted, "u/0", 0}, expect{hooks.RelationJoined, "u/1", 0}, expect{hooks.RelationChanged, "u/1", 0}, ), } func (s *HookQueueSuite) TestAliveHookQueue(c *gc.C) { for i, t := range aliveHookQueueTests { c.Logf("test %d: %s", i, t.summary) out := make(chan hook.Info) in := make(chan params.RelationUnitsChange) ruw := &RUW{in, false} q := relation.NewAliveHookQueue(t.initial, out, ruw) for i, step := range t.steps { c.Logf(" step %d", i) step.check(c, in, out) } expect{}.check(c, in, out) q.Stop() c.Assert(ruw.stopped, gc.Equals, true) } } var dyingHookQueueTests = []hookQueueTest{ fullTest( "Empty state just gets a broken hook.", expect{hook: hooks.RelationBroken}, ), reconcileTest( "Each current member is departed before broken is sent.", msi{"u/1": 7, "u/4": 33}, "", expect{hooks.RelationDeparted, "u/1", 7}, expect{hooks.RelationDeparted, "u/4", 33}, expect{hook: hooks.RelationBroken}, ), reconcileTest( "If there's a pending changed, that must still be respected.", msi{"u/0": 3}, "u/0", expect{hooks.RelationChanged, "u/0", 3}, expect{hooks.RelationDeparted, "u/0", 3}, expect{hook: hooks.RelationBroken}, ), } func (s *HookQueueSuite) TestDyingHookQueue(c *gc.C) { for i, t := range dyingHookQueueTests { c.Logf("test %d: %s", i, t.summary) out := make(chan hook.Info) q := relation.NewDyingHookQueue(t.initial, out) for i, step := range t.steps { c.Logf(" step %d", i) step.check(c, nil, out) } expect{}.check(c, nil, out) q.Stop() } } // RUW exists entirely to send RelationUnitsChanged events to a tested // HookQueue in a synchronous and predictable fashion. type RUW struct { in chan params.RelationUnitsChange stopped bool } func (w *RUW) Changes() <-chan params.RelationUnitsChange { return w.in } func (w *RUW) Stop() error { close(w.in) w.stopped = true return nil } func (w *RUW) Err() error { return nil } type checker interface { check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info) } type send struct { changed msi departed []string } func (d send) check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info) { ruc := params.RelationUnitsChange{Changed: map[string]params.UnitSettings{}} for name, version := range d.changed { ruc.Changed[name] = params.UnitSettings{Version: version} } for _, name := range d.departed { ruc.Departed = append(ruc.Departed, name) } in <- ruc } type advance struct { count int } func (d advance) check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info) { for i := 0; i < d.count; i++ { select { case <-out: case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for event %d", i) } } } type expect struct { hook hooks.Kind unit string version int64 } func (d expect) check(c *gc.C, in chan params.RelationUnitsChange, out chan hook.Info) { if d.hook == "" { select { case unexpected := <-out: c.Fatalf("got %#v", unexpected) case <-time.After(coretesting.ShortWait): } return } expect := hook.Info{ Kind: d.hook, RelationId: 21345, RemoteUnit: d.unit, ChangeVersion: d.version, } select { case actual := <-out: c.Assert(actual, gc.DeepEquals, expect) case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for %#v", expect) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/uniter/runlistener.go���������������������������0000644�0000153�0000161�00000006551�12321735642�026414� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The run listener is a worker go-routine that listens on either a unix // socket or a tcp connection for juju-run commands. package uniter import ( "net" "net/rpc" "os" "sync" "launchpad.net/juju-core/utils/exec" ) const JujuRunEndpoint = "JujuRunServer.RunCommands" // A CommandRunner is something that will actually execute the commands and // return the results of that execution in the exec.ExecResponse (which // contains stdout, stderr, and return code). type CommandRunner interface { RunCommands(commands string) (results *exec.ExecResponse, err error) } // RunListener is responsible for listening on the network connection and // seting up the rpc server on that net connection. Also starts the go routine // that listens and hands off the work. type RunListener struct { listener net.Listener server *rpc.Server closed chan struct{} closing chan struct{} wg sync.WaitGroup } // The JujuRunServer is the entity that has the methods that are called over // the rpc connection. type JujuRunServer struct { runner CommandRunner } // RunCommands delegates the actual running to the runner and populates the // response structure. func (r *JujuRunServer) RunCommands(commands string, result *exec.ExecResponse) error { logger.Debugf("RunCommands: %q", commands) runResult, err := r.runner.RunCommands(commands) *result = *runResult return err } // NewRunListener returns a new RunListener that is listening on given // unix socket path passed in. If a valid RunListener is returned, is // has the go routine running, and should be closed by the creator // when they are done with it. func NewRunListener(runner CommandRunner, socketPath string) (*RunListener, error) { server := rpc.NewServer() if err := server.Register(&JujuRunServer{runner}); err != nil { return nil, err } // In case the unix socket is present, delete it. if err := os.Remove(socketPath); err != nil { logger.Tracef("ignoring error on removing %q: %v", socketPath, err) } listener, err := net.Listen("unix", socketPath) if err != nil { logger.Errorf("failed to listen on unix:%s: %v", socketPath, err) return nil, err } runListener := &RunListener{ listener: listener, server: server, closed: make(chan struct{}), closing: make(chan struct{}), } go runListener.Run() return runListener, nil } // Run accepts new connections until it encounters an error, or until Close is // called, and then blocks until all existing connections have been closed. func (s *RunListener) Run() (err error) { logger.Debugf("juju-run listener running") var conn net.Conn for { conn, err = s.listener.Accept() if err != nil { break } s.wg.Add(1) go func(conn net.Conn) { s.server.ServeConn(conn) s.wg.Done() }(conn) } logger.Debugf("juju-run listener stopping") select { case <-s.closing: // Someone has called Close(), so it is overwhelmingly likely that // the error from Accept is a direct result of the Listener being // closed, and can therefore be safely ignored. err = nil default: } s.wg.Wait() close(s.closed) return } // Close immediately stops accepting connections, and blocks until all existing // connections have been closed. func (s *RunListener) Close() { close(s.closing) s.listener.Close() <-s.closed logger.Debugf("juju-run listener stopped") } �������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/machiner/���������������������������������������0000755�0000153�0000161�00000000000�12321736000�023751� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/machiner/machiner_test.go�����������������������0000644�0000153�0000161�00000010533�12321735776�027152� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package machiner_test import ( "net" stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" apimachiner "launchpad.net/juju-core/state/api/machiner" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/machiner" ) // worstCase is used for timeouts when timing out // will fail the test. Raising this value should // not affect the overall running time of the tests // unless they fail. const worstCase = 5 * time.Second func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type MachinerSuite struct { testing.JujuConnSuite st *api.State machinerState *apimachiner.State machine *state.Machine apiMachine *apimachiner.Machine } var _ = gc.Suite(&MachinerSuite{}) func (s *MachinerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.st, s.machine = s.OpenAPIAsNewMachine(c) // Create the machiner API facade. s.machinerState = s.st.Machiner() c.Assert(s.machinerState, gc.NotNil) // Get the machine through the facade. var err error s.apiMachine, err = s.machinerState.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) c.Assert(s.apiMachine.Tag(), gc.Equals, s.machine.Tag()) } func (s *MachinerSuite) waitMachineStatus(c *gc.C, m *state.Machine, expectStatus params.Status) { timeout := time.After(worstCase) for { select { case <-timeout: c.Fatalf("timeout while waiting for machine status to change") case <-time.After(10 * time.Millisecond): status, _, _, err := m.Status() c.Assert(err, gc.IsNil) if status != expectStatus { c.Logf("machine %q status is %s, still waiting", m, status) continue } return } } } var _ worker.NotifyWatchHandler = (*machiner.Machiner)(nil) type mockConfig struct { agent.Config tag string } func (mock *mockConfig) Tag() string { return mock.tag } func agentConfig(tag string) agent.Config { return &mockConfig{tag: tag} } func (s *MachinerSuite) TestNotFoundOrUnauthorized(c *gc.C) { mr := machiner.NewMachiner(s.machinerState, agentConfig("eleventy-one")) c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent) } func (s *MachinerSuite) makeMachiner() worker.Worker { return machiner.NewMachiner(s.machinerState, agentConfig(s.apiMachine.Tag())) } func (s *MachinerSuite) TestRunStop(c *gc.C) { mr := s.makeMachiner() c.Assert(worker.Stop(mr), gc.IsNil) c.Assert(s.apiMachine.Refresh(), gc.IsNil) c.Assert(s.apiMachine.Life(), gc.Equals, params.Alive) } func (s *MachinerSuite) TestStartSetsStatus(c *gc.C) { status, info, _, err := s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusPending) c.Assert(info, gc.Equals, "") mr := s.makeMachiner() defer worker.Stop(mr) s.waitMachineStatus(c, s.machine, params.StatusStarted) } func (s *MachinerSuite) TestSetsStatusWhenDying(c *gc.C) { mr := s.makeMachiner() defer worker.Stop(mr) c.Assert(s.machine.Destroy(), gc.IsNil) s.waitMachineStatus(c, s.machine, params.StatusStopped) } func (s *MachinerSuite) TestSetDead(c *gc.C) { mr := s.makeMachiner() defer worker.Stop(mr) c.Assert(s.machine.Destroy(), gc.IsNil) s.State.StartSync() c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent) c.Assert(s.machine.Refresh(), gc.IsNil) c.Assert(s.machine.Life(), gc.Equals, state.Dead) } func (s *MachinerSuite) TestMachineAddresses(c *gc.C) { 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)}, &net.IPAddr{IP: net.IPv6loopback}, &net.UnixAddr{}, // not IP, ignored &net.IPNet{IP: net.ParseIP("2001:db8::1")}, } return addrs, nil }) mr := s.makeMachiner() defer worker.Stop(mr) c.Assert(s.machine.Destroy(), gc.IsNil) s.State.StartSync() c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent) 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"), }) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/machiner/export_test.go�������������������������0000644�0000153�0000161�00000000225�12321735642�026672� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package machiner var InterfaceAddrs = &interfaceAddrs ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/machiner/machiner.go����������������������������0000644�0000153�0000161�00000006067�12321735776�026122� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package machiner import ( "fmt" "net" "github.com/juju/loggo" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state/api/machiner" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/worker" ) var logger = loggo.GetLogger("juju.worker.machiner") // Machiner is responsible for a machine agent's lifecycle. type Machiner struct { st *machiner.State tag string machine *machiner.Machine } // NewMachiner returns a Worker that will wait for the identified machine // to become Dying and make it Dead; or until the machine becomes Dead by // other means. func NewMachiner(st *machiner.State, agentConfig agent.Config) worker.Worker { mr := &Machiner{st: st, tag: agentConfig.Tag()} return worker.NewNotifyWorker(mr) } func (mr *Machiner) SetUp() (watcher.NotifyWatcher, error) { // Find which machine we're responsible for. m, err := mr.st.Machine(mr.tag) if params.IsCodeNotFoundOrCodeUnauthorized(err) { return nil, worker.ErrTerminateAgent } else if err != nil { return nil, err } mr.machine = m // Set the addresses in state to the host's addresses. if err := setMachineAddresses(m); err != nil { return nil, err } // Mark the machine as started and log it. if err := m.SetStatus(params.StatusStarted, "", nil); err != nil { return nil, fmt.Errorf("%s failed to set status started: %v", mr.tag, err) } logger.Infof("%q started", mr.tag) return m.Watch() } var interfaceAddrs = net.InterfaceAddrs // setMachineAddresses sets the addresses for this machine to all of the // host's non-loopback interface IP addresses. func setMachineAddresses(m *machiner.Machine) error { addrs, err := interfaceAddrs() if err != nil { return err } var hostAddresses []instance.Address for _, addr := range addrs { var ip net.IP switch addr := addr.(type) { case *net.IPAddr: ip = addr.IP case *net.IPNet: ip = addr.IP default: continue } hostAddresses = append(hostAddresses, instance.NewAddress(ip.String())) } if len(hostAddresses) == 0 { return nil } logger.Infof("setting addresses for %v to %q", m.Tag(), hostAddresses) return m.SetMachineAddresses(hostAddresses) } func (mr *Machiner) Handle() error { if err := mr.machine.Refresh(); params.IsCodeNotFoundOrCodeUnauthorized(err) { return worker.ErrTerminateAgent } else if err != nil { return err } if mr.machine.Life() == params.Alive { return nil } logger.Debugf("%q is now %s", mr.tag, mr.machine.Life()) if err := mr.machine.SetStatus(params.StatusStopped, "", nil); err != nil { return fmt.Errorf("%s failed to set status stopped: %v", mr.tag, err) } // If the machine is Dying, it has no units, // and can be safely set to Dead. if err := mr.machine.EnsureDead(); err != nil { return fmt.Errorf("%s failed to set machine to dead: %v", mr.tag, err) } return worker.ErrTerminateAgent } func (mr *Machiner) TearDown() error { // Nothing to do here. return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/notifyworker.go���������������������������������0000644�0000153�0000161�00000005031�12321735642�025266� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker import ( "launchpad.net/tomb" apiWatcher "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/state/watcher" ) // mustErr is defined as a variable to allow the test suite // to override it. var mustErr = watcher.MustErr // notifyWorker is the internal implementation of the Worker // interface, using a NotifyWatcher for handling changes. type notifyWorker struct { tomb tomb.Tomb // handler is what will handle when events are triggered handler NotifyWatchHandler } // NotifyWatchHandler implements the business logic that is triggered // as part of watching a NotifyWatcher. type NotifyWatchHandler interface { // SetUp starts the handler, this should create the watcher we // will be waiting on for more events. SetUp can return a Watcher // even if there is an error, and the notify Worker will make sure // to stop the watcher. SetUp() (apiWatcher.NotifyWatcher, error) // TearDown should cleanup any resources that are left around TearDown() error // Handle is called when the Watcher has indicated there are // changes, do whatever work is necessary to process it Handle() error } // NewNotifyWorker starts a new worker running the business logic from // the handler. The worker loop is started in another goroutine as a // side effect of calling this. func NewNotifyWorker(handler NotifyWatchHandler) Worker { nw := &notifyWorker{ handler: handler, } go func() { defer nw.tomb.Done() nw.tomb.Kill(nw.loop()) }() return nw } // Kill the loop with no-error func (nw *notifyWorker) Kill() { nw.tomb.Kill(nil) } // Wait for the looping to finish func (nw *notifyWorker) Wait() error { return nw.tomb.Wait() } type tearDowner interface { TearDown() error } // propagateTearDown tears down the handler, but ensures any error is // propagated through the tomb's Kill method. func propagateTearDown(handler tearDowner, t *tomb.Tomb) { if err := handler.TearDown(); err != nil { t.Kill(err) } } func (nw *notifyWorker) loop() error { w, err := nw.handler.SetUp() if err != nil { if w != nil { // We don't bother to propagate an error, because we // already have an error w.Stop() } return err } defer propagateTearDown(nw.handler, &nw.tomb) defer watcher.Stop(w, &nw.tomb) for { select { case <-nw.tomb.Dying(): return tomb.ErrDying case _, ok := <-w.Changes(): if !ok { return mustErr(w) } if err := nw.handler.Handle(); err != nil { return err } } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/simpleworker_test.go����������������������������0000644�0000153�0000161�00000001727�12321735642�026316� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker import ( "errors" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) type simpleWorkerSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&simpleWorkerSuite{}) var testError = errors.New("test error") func (s *simpleWorkerSuite) TestWait(c *gc.C) { doWork := func(_ <-chan struct{}) error { return testError } w := NewSimpleWorker(doWork) c.Assert(w.Wait(), gc.Equals, testError) } func (s *simpleWorkerSuite) TestWaitNil(c *gc.C) { doWork := func(_ <-chan struct{}) error { return nil } w := NewSimpleWorker(doWork) c.Assert(w.Wait(), gc.Equals, nil) } func (s *simpleWorkerSuite) TestKill(c *gc.C) { doWork := func(stopCh <-chan struct{}) error { <-stopCh return testError } w := NewSimpleWorker(doWork) w.Kill() c.Assert(w.Wait(), gc.Equals, testError) // test we can kill again without a panic w.Kill() } �����������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/minunitsworker/���������������������������������0000755�0000153�0000161�00000000000�12321735642�025276� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/minunitsworker/minunitsworker_test.go�����������0000644�0000153�0000161�00000003702�12321735642�031766� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package minunitsworker_test import ( stdtesting "testing" "time" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/minunitsworker" ) var logger = loggo.GetLogger("juju.worker.minunitsworker_test") func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type minUnitsWorkerSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&minUnitsWorkerSuite{}) var _ worker.StringsWatchHandler = (*minunitsworker.MinUnitsWorker)(nil) func (s *minUnitsWorkerSuite) TestMinUnitsWorker(c *gc.C) { mu := minunitsworker.NewMinUnitsWorker(s.State) defer func() { c.Assert(worker.Stop(mu), gc.IsNil) }() // Set up services and units for later use. wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) unit, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) _, err = wordpress.AddUnit() c.Assert(err, gc.IsNil) // Set up minimum units for services. err = wordpress.SetMinUnits(3) c.Assert(err, gc.IsNil) err = mysql.SetMinUnits(2) c.Assert(err, gc.IsNil) // Remove a unit for a service. err = unit.Destroy() c.Assert(err, gc.IsNil) timeout := time.After(coretesting.LongWait) for { s.State.StartSync() select { case <-time.After(coretesting.ShortWait): wordpressUnits, err := wordpress.AllUnits() c.Assert(err, gc.IsNil) mysqlUnits, err := mysql.AllUnits() c.Assert(err, gc.IsNil) wordpressCount := len(wordpressUnits) mysqlCount := len(mysqlUnits) if wordpressCount == 3 && mysqlCount == 2 { return } logger.Infof("wordpress units: %d; mysql units: %d", wordpressCount, mysqlCount) case <-timeout: c.Fatalf("timed out waiting for minunits events") } } } ��������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/minunitsworker/minunitsworker.go����������������0000644�0000153�0000161�00000002703�12321735642�030727� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package minunitsworker import ( "github.com/juju/loggo" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/worker" ) var logger = loggo.GetLogger("juju.worker.minunitsworker") // MinUnitsWorker ensures the minimum number of units for services is respected. type MinUnitsWorker struct { st *state.State } // NewMinUnitsWorker returns a Worker that runs service.EnsureMinUnits() // if the number of alive units belonging to a service decreases, or if the // minimum required number of units for a service is increased. func NewMinUnitsWorker(st *state.State) worker.Worker { mu := &MinUnitsWorker{st: st} return worker.NewStringsWorker(mu) } func (mu *MinUnitsWorker) SetUp() (watcher.StringsWatcher, error) { return mu.st.WatchMinUnits(), nil } func (mu *MinUnitsWorker) handleOneService(serviceName string) error { service, err := mu.st.Service(serviceName) if err != nil { return err } return service.EnsureMinUnits() } func (mu *MinUnitsWorker) Handle(serviceNames []string) error { for _, name := range serviceNames { logger.Infof("processing service %q", name) if err := mu.handleOneService(name); err != nil { logger.Errorf("failed to process service %q: %v", name, err) return err } } return nil } func (mu *MinUnitsWorker) TearDown() error { // Nothing to do here. return nil } �������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/export_test.go����������������������������������0000644�0000153�0000161�00000000723�12321735642�025107� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker import ( "launchpad.net/juju-core/state/watcher" ) var LoadedInvalid = make(chan struct{}) func init() { loadedInvalid = func() { LoadedInvalid <- struct{}{} } } func SetMustErr(f func(watcher.Errer) error) { if f == nil { mustErr = watcher.MustErr } else { mustErr = f } } func MustErr() func(watcher.Errer) error { return mustErr } ���������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/runner_test.go����������������������������������0000644�0000153�0000161�00000026741�12321735642�025107� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker_test import ( "errors" "fmt" "sync/atomic" "time" gc "launchpad.net/gocheck" "launchpad.net/tomb" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/worker" ) type runnerSuite struct { testbase.LoggingSuite restartDelay time.Duration } var _ = gc.Suite(&runnerSuite{}) func noneFatal(error) bool { return false } func allFatal(error) bool { return true } func noImportance(err0, err1 error) bool { return false } func (s *runnerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.restartDelay = worker.RestartDelay worker.RestartDelay = 0 } func (s *runnerSuite) TearDownTest(c *gc.C) { worker.RestartDelay = s.restartDelay s.LoggingSuite.TearDownTest(c) } func (*runnerSuite) TestOneWorkerStart(c *gc.C) { runner := worker.NewRunner(noneFatal, noImportance) starter := newTestWorkerStarter() err := runner.StartWorker("id", testWorkerStart(starter)) c.Assert(err, gc.IsNil) starter.assertStarted(c, true) c.Assert(worker.Stop(runner), gc.IsNil) starter.assertStarted(c, false) } func (*runnerSuite) TestOneWorkerRestart(c *gc.C) { runner := worker.NewRunner(noneFatal, noImportance) starter := newTestWorkerStarter() err := runner.StartWorker("id", testWorkerStart(starter)) c.Assert(err, gc.IsNil) starter.assertStarted(c, true) // Check it restarts a few times time. for i := 0; i < 3; i++ { starter.die <- fmt.Errorf("an error") starter.assertStarted(c, false) starter.assertStarted(c, true) } c.Assert(worker.Stop(runner), gc.IsNil) starter.assertStarted(c, false) } func (*runnerSuite) TestOneWorkerStartFatalError(c *gc.C) { runner := worker.NewRunner(allFatal, noImportance) starter := newTestWorkerStarter() starter.startErr = errors.New("cannot start test task") err := runner.StartWorker("id", testWorkerStart(starter)) c.Assert(err, gc.IsNil) err = runner.Wait() c.Assert(err, gc.Equals, starter.startErr) } func (*runnerSuite) TestOneWorkerDieFatalError(c *gc.C) { runner := worker.NewRunner(allFatal, noImportance) starter := newTestWorkerStarter() err := runner.StartWorker("id", testWorkerStart(starter)) c.Assert(err, gc.IsNil) starter.assertStarted(c, true) dieErr := errors.New("error when running") starter.die <- dieErr err = runner.Wait() c.Assert(err, gc.Equals, dieErr) starter.assertStarted(c, false) } func (*runnerSuite) TestOneWorkerStartStop(c *gc.C) { runner := worker.NewRunner(allFatal, noImportance) starter := newTestWorkerStarter() err := runner.StartWorker("id", testWorkerStart(starter)) c.Assert(err, gc.IsNil) starter.assertStarted(c, true) err = runner.StopWorker("id") c.Assert(err, gc.IsNil) starter.assertStarted(c, false) c.Assert(worker.Stop(runner), gc.IsNil) } func (*runnerSuite) TestOneWorkerStopFatalError(c *gc.C) { runner := worker.NewRunner(allFatal, noImportance) starter := newTestWorkerStarter() starter.stopErr = errors.New("stop error") err := runner.StartWorker("id", testWorkerStart(starter)) c.Assert(err, gc.IsNil) starter.assertStarted(c, true) err = runner.StopWorker("id") c.Assert(err, gc.IsNil) err = runner.Wait() c.Assert(err, gc.Equals, starter.stopErr) } func (*runnerSuite) TestOneWorkerStartWhenStopping(c *gc.C) { worker.RestartDelay = 3 * time.Second runner := worker.NewRunner(allFatal, noImportance) starter := newTestWorkerStarter() starter.stopWait = make(chan struct{}) err := runner.StartWorker("id", testWorkerStart(starter)) c.Assert(err, gc.IsNil) starter.assertStarted(c, true) err = runner.StopWorker("id") c.Assert(err, gc.IsNil) err = runner.StartWorker("id", testWorkerStart(starter)) c.Assert(err, gc.IsNil) close(starter.stopWait) starter.assertStarted(c, false) // Check that the task is restarted immediately without // the usual restart timeout delay. t0 := time.Now() starter.assertStarted(c, true) restartDuration := time.Since(t0) if restartDuration > 1*time.Second { c.Fatalf("task did not restart immediately") } c.Assert(worker.Stop(runner), gc.IsNil) } func (*runnerSuite) TestOneWorkerRestartDelay(c *gc.C) { worker.RestartDelay = 100 * time.Millisecond runner := worker.NewRunner(noneFatal, noImportance) starter := newTestWorkerStarter() err := runner.StartWorker("id", testWorkerStart(starter)) c.Assert(err, gc.IsNil) starter.assertStarted(c, true) starter.die <- fmt.Errorf("non-fatal error") starter.assertStarted(c, false) t0 := time.Now() starter.assertStarted(c, true) restartDuration := time.Since(t0) if restartDuration < worker.RestartDelay { c.Fatalf("restart delay was not respected; got %v want %v", restartDuration, worker.RestartDelay) } } type errorLevel int func (e errorLevel) Error() string { return fmt.Sprintf("error with importance %d", e) } func (*runnerSuite) TestErrorImportance(c *gc.C) { moreImportant := func(err0, err1 error) bool { return err0.(errorLevel) > err1.(errorLevel) } id := func(i int) string { return fmt.Sprint(i) } runner := worker.NewRunner(allFatal, moreImportant) for i := 0; i < 10; i++ { starter := newTestWorkerStarter() starter.stopErr = errorLevel(i) err := runner.StartWorker(id(i), testWorkerStart(starter)) c.Assert(err, gc.IsNil) } err := runner.StopWorker(id(4)) c.Assert(err, gc.IsNil) err = runner.Wait() c.Assert(err, gc.Equals, errorLevel(9)) } func (*runnerSuite) TestStartWorkerWhenDead(c *gc.C) { runner := worker.NewRunner(allFatal, noImportance) c.Assert(worker.Stop(runner), gc.IsNil) c.Assert(runner.StartWorker("foo", nil), gc.Equals, worker.ErrDead) } func (*runnerSuite) TestStopWorkerWhenDead(c *gc.C) { runner := worker.NewRunner(allFatal, noImportance) c.Assert(worker.Stop(runner), gc.IsNil) c.Assert(runner.StopWorker("foo"), gc.Equals, worker.ErrDead) } func (*runnerSuite) TestAllWorkersStoppedWhenOneDiesWithFatalError(c *gc.C) { runner := worker.NewRunner(allFatal, noImportance) var starters []*testWorkerStarter for i := 0; i < 10; i++ { starter := newTestWorkerStarter() err := runner.StartWorker(fmt.Sprint(i), testWorkerStart(starter)) c.Assert(err, gc.IsNil) starters = append(starters, starter) } for _, starter := range starters { starter.assertStarted(c, true) } dieErr := errors.New("fatal error") starters[4].die <- dieErr err := runner.Wait() c.Assert(err, gc.Equals, dieErr) for _, starter := range starters { starter.assertStarted(c, false) } } func (*runnerSuite) TestFatalErrorWhileStarting(c *gc.C) { // Original deadlock problem that this tests for: // A worker dies with fatal error while another worker // is inside start(). runWorker can't send startInfo on startedc. runner := worker.NewRunner(allFatal, noImportance) slowStarter := newTestWorkerStarter() // make the startNotify channel synchronous so // we can delay the start indefinitely. slowStarter.startNotify = make(chan bool) err := runner.StartWorker("slow starter", testWorkerStart(slowStarter)) c.Assert(err, gc.IsNil) fatalStarter := newTestWorkerStarter() fatalStarter.startErr = fmt.Errorf("a fatal error") err = runner.StartWorker("fatal worker", testWorkerStart(fatalStarter)) c.Assert(err, gc.IsNil) // Wait for the runner loop to react to the fatal // error and go into final shutdown mode. time.Sleep(10 * time.Millisecond) // At this point, the loop is in shutdown mode, but the // slowStarter's worker is still in its start function. // When the start function continues (the first assertStarted // allows that to happen) and returns the new Worker, // runWorker will try to send it on runner.startedc. // This test makes sure that succeeds ok. slowStarter.assertStarted(c, true) slowStarter.assertStarted(c, false) err = runner.Wait() c.Assert(err, gc.Equals, fatalStarter.startErr) } func (*runnerSuite) TestFatalErrorWhileSelfStartWorker(c *gc.C) { // Original deadlock problem that this tests for: // A worker tries to call StartWorker in its start function // at the same time another worker dies with a fatal error. // It might not be able to send on startc. runner := worker.NewRunner(allFatal, noImportance) selfStarter := newTestWorkerStarter() // make the startNotify channel synchronous so // we can delay the start indefinitely. selfStarter.startNotify = make(chan bool) selfStarter.hook = func() { runner.StartWorker("another", func() (worker.Worker, error) { return nil, fmt.Errorf("no worker started") }) } err := runner.StartWorker("self starter", testWorkerStart(selfStarter)) c.Assert(err, gc.IsNil) fatalStarter := newTestWorkerStarter() fatalStarter.startErr = fmt.Errorf("a fatal error") err = runner.StartWorker("fatal worker", testWorkerStart(fatalStarter)) c.Assert(err, gc.IsNil) // Wait for the runner loop to react to the fatal // error and go into final shutdown mode. time.Sleep(10 * time.Millisecond) // At this point, the loop is in shutdown mode, but the // selfStarter's worker is still in its start function. // When the start function continues (the first assertStarted // allows that to happen) it will try to create a new // worker. This failed in an earlier version of the code because the // loop was not ready to receive start requests. selfStarter.assertStarted(c, true) selfStarter.assertStarted(c, false) err = runner.Wait() c.Assert(err, gc.Equals, fatalStarter.startErr) } type testWorkerStarter struct { startCount int32 // startNotify receives true when the worker starts // and false when it exits. If startErr is non-nil, // it sends false only. startNotify chan bool // If stopWait is non-nil, the worker will // wait for a value to be sent on it before // exiting. stopWait chan struct{} // Sending a value on die causes the worker // to die with the given error. die chan error // If startErr is non-nil, the worker will die immediately // with this error after starting. startErr error // If stopErr is non-nil, the worker will die with this // error when asked to stop. stopErr error // The hook function is called after starting the worker. hook func() } func newTestWorkerStarter() *testWorkerStarter { return &testWorkerStarter{ die: make(chan error, 1), startNotify: make(chan bool, 100), hook: func() {}, } } func (starter *testWorkerStarter) assertStarted(c *gc.C, started bool) { select { case isStarted := <-starter.startNotify: c.Assert(isStarted, gc.Equals, started) case <-time.After(1 * time.Second): c.Fatalf("timed out waiting for start notification") } } func testWorkerStart(starter *testWorkerStarter) func() (worker.Worker, error) { return func() (worker.Worker, error) { return starter.start() } } func (starter *testWorkerStarter) start() (worker.Worker, error) { if count := atomic.AddInt32(&starter.startCount, 1); count != 1 { panic(fmt.Errorf("unexpected start count %d; expected 1", count)) } if starter.startErr != nil { starter.startNotify <- false return nil, starter.startErr } task := &testWorker{ starter: starter, } starter.startNotify <- true go task.run() return task, nil } type testWorker struct { starter *testWorkerStarter tomb tomb.Tomb } func (t *testWorker) Kill() { t.tomb.Kill(nil) } func (t *testWorker) Wait() error { return t.tomb.Wait() } func (t *testWorker) run() { defer t.tomb.Done() t.starter.hook() select { case <-t.tomb.Dying(): t.tomb.Kill(t.starter.stopErr) case err := <-t.starter.die: t.tomb.Kill(err) } if t.starter.stopWait != nil { <-t.starter.stopWait } t.starter.startNotify <- false if count := atomic.AddInt32(&t.starter.startCount, -1); count != 0 { panic(fmt.Errorf("unexpected start count %d; expected 0", count)) } } �������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/logger/�����������������������������������������0000755�0000153�0000161�00000000000�12321735642�023455� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/logger/logger.go��������������������������������0000644�0000153�0000161�00000004151�12321735642�025264� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package logger import ( "github.com/juju/loggo" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/state/api/logger" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/worker" ) var log = loggo.GetLogger("juju.worker.logger") // Logger is responsible for updating the loggo configuration when the // environment watcher tells the agent that the value has changed. type Logger struct { api *logger.State agentConfig agent.Config lastConfig string } var _ worker.NotifyWatchHandler = (*Logger)(nil) // NewLogger returns a worker.Worker that uses the notify watcher returned // from the setup. func NewLogger(api *logger.State, agentConfig agent.Config) worker.Worker { logger := &Logger{ api: api, agentConfig: agentConfig, lastConfig: loggo.LoggerInfo(), } log.Debugf("initial log config: %q", logger.lastConfig) return worker.NewNotifyWorker(logger) } func (logger *Logger) setLogging() { loggingConfig, err := logger.api.LoggingConfig(logger.agentConfig.Tag()) if err != nil { log.Errorf("%v", err) } else { if loggingConfig != logger.lastConfig { log.Debugf("reconfiguring logging from %q to %q", logger.lastConfig, loggingConfig) loggo.ResetLoggers() if err := loggo.ConfigureLoggers(loggingConfig); err != nil { // This shouldn't occur as the loggingConfig should be // validated by the original Config before it gets here. log.Warningf("configure loggers failed: %v", err) // Try to reset to what we had before loggo.ConfigureLoggers(logger.lastConfig) } logger.lastConfig = loggingConfig } } } func (logger *Logger) SetUp() (watcher.NotifyWatcher, error) { log.Debugf("logger setup") // We need to set this up initially as the NotifyWorker sucks up the first // event. logger.setLogging() return logger.api.WatchLoggingConfig(logger.agentConfig.Tag()) } func (logger *Logger) Handle() error { logger.setLogging() return nil } func (logger *Logger) TearDown() error { // Nothing to cleanup, only state is the watcher return nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/logger/package_test.go��������������������������0000644�0000153�0000161�00000000366�12321735642�026443� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package logger_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/logger/logger_test.go���������������������������0000644�0000153�0000161�00000004564�12321735642�026333� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package logger_test import ( "time" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" apilogger "launchpad.net/juju-core/state/api/logger" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/logger" ) // worstCase is used for timeouts when timing out // will fail the test. Raising this value should // not affect the overall running time of the tests // unless they fail. const worstCase = 5 * time.Second type LoggerSuite struct { testing.JujuConnSuite apiRoot *api.State loggerApi *apilogger.State machine *state.Machine } var _ = gc.Suite(&LoggerSuite{}) func (s *LoggerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.apiRoot, s.machine = s.OpenAPIAsNewMachine(c) // Create the machiner API facade. s.loggerApi = s.apiRoot.Logger() c.Assert(s.loggerApi, gc.NotNil) } func (s *LoggerSuite) waitLoggingInfo(c *gc.C, expected string) { timeout := time.After(worstCase) for { select { case <-timeout: c.Fatalf("timeout while waiting for logging info to change") case <-time.After(10 * time.Millisecond): loggerInfo := loggo.LoggerInfo() if loggerInfo != expected { c.Logf("logging is %q, still waiting", loggerInfo) continue } return } } } type mockConfig struct { agent.Config c *gc.C tag string } func (mock *mockConfig) Tag() string { return mock.tag } func agentConfig(c *gc.C, tag string) *mockConfig { return &mockConfig{c: c, tag: tag} } func (s *LoggerSuite) makeLogger(c *gc.C) (worker.Worker, *mockConfig) { config := agentConfig(c, s.machine.Tag()) return logger.NewLogger(s.loggerApi, config), config } func (s *LoggerSuite) TestRunStop(c *gc.C) { loggingWorker, _ := s.makeLogger(c) c.Assert(worker.Stop(loggingWorker), gc.IsNil) } func (s *LoggerSuite) TestInitialState(c *gc.C) { config, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) expected := config.LoggingConfig() initial := "<root>=DEBUG;wibble=ERROR" c.Assert(expected, gc.Not(gc.Equals), initial) loggo.ResetLoggers() err = loggo.ConfigureLoggers(initial) c.Assert(err, gc.IsNil) loggingWorker, _ := s.makeLogger(c) defer worker.Stop(loggingWorker) s.waitLoggingInfo(c, expected) } ��������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/terminationworker/������������������������������0000755�0000153�0000161�00000000000�12321735642�025761� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/terminationworker/worker.go���������������������0000644�0000153�0000161�00000002157�12321735642�027626� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package terminationworker import ( "os" "os/signal" "syscall" "launchpad.net/tomb" "launchpad.net/juju-core/worker" ) // TerminationSignal is the signal that // indicates the agent should terminate // and uninstall itself. // // We do not use SIGTERM as SIGTERM is the // default signal used to initiate a graceful // shutdown. const TerminationSignal = syscall.SIGABRT // NewWorker returns a worker that wais for a type terminationWorker struct { tomb tomb.Tomb } // TerminationSignal signal and exits with // ErrTerminateAgent. func NewWorker() worker.Worker { u := &terminationWorker{} go func() { defer u.tomb.Done() u.tomb.Kill(u.loop()) }() return u } func (u *terminationWorker) Kill() { u.tomb.Kill(nil) } func (u *terminationWorker) Wait() error { return u.tomb.Wait() } func (u *terminationWorker) loop() (err error) { c := make(chan os.Signal, 1) signal.Notify(c, TerminationSignal) defer signal.Stop(c) select { case <-c: return worker.ErrTerminateAgent case <-u.tomb.Dying(): return nil } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/terminationworker/worker_test.go����������������0000644�0000153�0000161�00000002576�12321735642�030672� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package terminationworker_test import ( "os" "os/signal" stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/terminationworker" ) func TestPackage(t *stdtesting.T) { gc.TestingT(t) } var _ = gc.Suite(&TerminationWorkerSuite{}) type TerminationWorkerSuite struct { testbase.LoggingSuite // c is a channel that will wait for the termination // signal, to prevent signals terminating the process. c chan os.Signal } func (s *TerminationWorkerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.c = make(chan os.Signal, 1) signal.Notify(s.c, terminationworker.TerminationSignal) } func (s *TerminationWorkerSuite) TearDownTest(c *gc.C) { close(s.c) signal.Stop(s.c) s.LoggingSuite.TearDownTest(c) } func (s *TerminationWorkerSuite) TestStartStop(c *gc.C) { w := terminationworker.NewWorker() w.Kill() err := w.Wait() c.Assert(err, gc.IsNil) } func (s *TerminationWorkerSuite) TestSignal(c *gc.C) { w := terminationworker.NewWorker() proc, err := os.FindProcess(os.Getpid()) c.Assert(err, gc.IsNil) defer proc.Release() err = proc.Signal(terminationworker.TerminationSignal) c.Assert(err, gc.IsNil) err = w.Wait() c.Assert(err, gc.Equals, worker.ErrTerminateAgent) } ����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/authenticationworker/���������������������������0000755�0000153�0000161�00000000000�12321735643�026450� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/authenticationworker/worker.go������������������0000644�0000153�0000161�00000010001�12321735642�030277� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package authenticationworker import ( "strings" "github.com/juju/loggo" "launchpad.net/tomb" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/log" "launchpad.net/juju-core/state/api/keyupdater" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/utils/ssh" "launchpad.net/juju-core/worker" ) // The user name used to ssh into Juju nodes. // Override for testing. var SSHUser = "ubuntu" var logger = loggo.GetLogger("juju.worker.authenticationworker") type keyupdaterWorker struct { st *keyupdater.State tomb tomb.Tomb tag string // jujuKeys are the most recently retrieved keys from state. jujuKeys set.Strings // nonJujuKeys are those added externally to auth keys file // such keys do not have comments with the Juju: prefix. nonJujuKeys []string } var _ worker.NotifyWatchHandler = (*keyupdaterWorker)(nil) // NewWorker returns a worker that keeps track of // the machine's authorised ssh keys and ensures the // ~/.ssh/authorized_keys file is up to date. func NewWorker(st *keyupdater.State, agentConfig agent.Config) worker.Worker { kw := &keyupdaterWorker{st: st, tag: agentConfig.Tag()} return worker.NewNotifyWorker(kw) } // SetUp is defined on the worker.NotifyWatchHandler interface. func (kw *keyupdaterWorker) SetUp() (watcher.NotifyWatcher, error) { // Record the keys Juju knows about. jujuKeys, err := kw.st.AuthorisedKeys(kw.tag) if err != nil { return nil, log.LoggedErrorf(logger, "reading Juju ssh keys for %q: %v", kw.tag, err) } kw.jujuKeys = set.NewStrings(jujuKeys...) // Read the keys currently in ~/.ssh/authorised_keys. sshKeys, err := ssh.ListKeys(SSHUser, ssh.FullKeys) if err != nil { return nil, log.LoggedErrorf(logger, "reading ssh authorized keys for %q: %v", kw.tag, err) } // Record any keys not added by Juju. for _, key := range sshKeys { _, comment, err := ssh.KeyFingerprint(key) // Also record keys which we cannot parse. if err != nil || !strings.HasPrefix(comment, ssh.JujuCommentPrefix) { kw.nonJujuKeys = append(kw.nonJujuKeys, key) } } // Write out the ssh authorised keys file to match the current state of the world. if err := kw.writeSSHKeys(jujuKeys); err != nil { return nil, log.LoggedErrorf(logger, "adding current Juju keys to ssh authorised keys: %v", err) } w, err := kw.st.WatchAuthorisedKeys(kw.tag) if err != nil { return nil, log.LoggedErrorf(logger, "starting key updater worker: %v", err) } logger.Infof("%q key updater worker started", kw.tag) return w, nil } // writeSSHKeys writes out a new ~/.ssh/authorised_keys file, retaining any non Juju keys // and adding the specified set of Juju keys. func (kw *keyupdaterWorker) writeSSHKeys(jujuKeys []string) error { allKeys := kw.nonJujuKeys // Ensure any Juju keys have the required prefix in their comment. for i, key := range jujuKeys { jujuKeys[i] = ssh.EnsureJujuComment(key) } allKeys = append(allKeys, jujuKeys...) return ssh.ReplaceKeys(SSHUser, allKeys...) } // Handle is defined on the worker.NotifyWatchHandler interface. func (kw *keyupdaterWorker) Handle() error { // Read the keys that Juju has. newKeys, err := kw.st.AuthorisedKeys(kw.tag) if err != nil { return log.LoggedErrorf(logger, "reading Juju ssh keys for %q: %v", kw.tag, err) } // Figure out if any keys have been added or deleted. newJujuKeys := set.NewStrings(newKeys...) deleted := kw.jujuKeys.Difference(newJujuKeys) added := newJujuKeys.Difference(kw.jujuKeys) if added.Size() > 0 || deleted.Size() > 0 { logger.Debugf("adding ssh keys to authorised keys: %v", added) logger.Debugf("deleting ssh keys from authorised keys: %v", deleted) if err = kw.writeSSHKeys(newKeys); err != nil { return log.LoggedErrorf(logger, "updating ssh keys: %v", err) } } kw.jujuKeys = newJujuKeys return nil } // TearDown is defined on the worker.NotifyWatchHandler interface. func (kw *keyupdaterWorker) TearDown() error { // Nothing to do here. return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/authenticationworker/worker_test.go�������������0000644�0000153�0000161�00000014146�12321735642�031354� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package authenticationworker_test import ( "strings" stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/keyupdater" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils/ssh" sshtesting "launchpad.net/juju-core/utils/ssh/testing" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/authenticationworker" ) // worstCase is used for timeouts when timing out // will fail the test. Raising this value should // not affect the overall running time of the tests // unless they fail. const worstCase = 5 * time.Second func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } var _ = gc.Suite(&workerSuite{}) type workerSuite struct { jujutesting.JujuConnSuite stateMachine *state.Machine machine *state.Machine keyupdaterApi *keyupdater.State existingEnvKey string existingKeys []string } func (s *workerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) // Default ssh user is currently "ubuntu". c.Assert(authenticationworker.SSHUser, gc.Equals, "ubuntu") // Set the ssh user to empty (the current user) as required by the test infrastructure. s.PatchValue(&authenticationworker.SSHUser, "") // Replace the default dummy key in the test environment with a valid one. // This will be added to the ssh authorised keys when the agent starts. s.setAuthorisedKeys(c, sshtesting.ValidKeyOne.Key+" firstuser@host") // Record the existing key with its prefix for testing later. s.existingEnvKey = sshtesting.ValidKeyOne.Key + " Juju:firstuser@host" // Set up an existing key (which is not in the environment) in the ssh authorised_keys file. s.existingKeys = []string{sshtesting.ValidKeyTwo.Key + " existinguser@host"} err := ssh.AddKeys(authenticationworker.SSHUser, s.existingKeys...) c.Assert(err, gc.IsNil) var apiRoot *api.State apiRoot, s.machine = s.OpenAPIAsNewMachine(c) c.Assert(apiRoot, gc.NotNil) s.keyupdaterApi = apiRoot.KeyUpdater() c.Assert(s.keyupdaterApi, gc.NotNil) } func stop(c *gc.C, w worker.Worker) { c.Assert(worker.Stop(w), gc.IsNil) } type mockConfig struct { agent.Config c *gc.C tag string } func (mock *mockConfig) Tag() string { return mock.tag } func agentConfig(c *gc.C, tag string) *mockConfig { return &mockConfig{c: c, tag: tag} } func (s *workerSuite) setAuthorisedKeys(c *gc.C, keys ...string) { keyStr := strings.Join(keys, "\n") err := s.BackingState.UpdateEnvironConfig(map[string]interface{}{"authorized-keys": keyStr}, nil, nil) c.Assert(err, gc.IsNil) s.BackingState.StartSync() } func (s *workerSuite) waitSSHKeys(c *gc.C, expected []string) { timeout := time.After(worstCase) for { select { case <-timeout: c.Fatalf("timeout while waiting for authoirsed ssh keys to change") case <-time.After(coretesting.ShortWait): keys, err := ssh.ListKeys(authenticationworker.SSHUser, ssh.FullKeys) c.Assert(err, gc.IsNil) keysStr := strings.Join(keys, "\n") expectedStr := strings.Join(expected, "\n") if expectedStr != keysStr { continue } return } } } func (s *workerSuite) TestKeyUpdateRetainsExisting(c *gc.C) { authWorker := authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag())) defer stop(c, authWorker) newKey := sshtesting.ValidKeyThree.Key + " user@host" s.setAuthorisedKeys(c, newKey) newKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:user@host" s.waitSSHKeys(c, append(s.existingKeys, newKeyWithCommentPrefix)) } func (s *workerSuite) TestNewKeysInJujuAreSavedOnStartup(c *gc.C) { newKey := sshtesting.ValidKeyThree.Key + " user@host" s.setAuthorisedKeys(c, newKey) authWorker := authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag())) defer stop(c, authWorker) newKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:user@host" s.waitSSHKeys(c, append(s.existingKeys, newKeyWithCommentPrefix)) } func (s *workerSuite) TestDeleteKey(c *gc.C) { authWorker := authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag())) defer stop(c, authWorker) // Add another key anotherKey := sshtesting.ValidKeyThree.Key + " another@host" s.setAuthorisedKeys(c, s.existingEnvKey, anotherKey) anotherKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:another@host" s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey, anotherKeyWithCommentPrefix)) // Delete the original key and check anotherKey plus the existing keys remain. s.setAuthorisedKeys(c, anotherKey) s.waitSSHKeys(c, append(s.existingKeys, anotherKeyWithCommentPrefix)) } func (s *workerSuite) TestMultipleChanges(c *gc.C) { authWorker := authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag())) defer stop(c, authWorker) s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey)) // Perform a set to add a key and delete a key. // added: key 3 // deleted: key 1 (existing env key) s.setAuthorisedKeys(c, sshtesting.ValidKeyThree.Key+" yetanother@host") yetAnotherKeyWithComment := sshtesting.ValidKeyThree.Key + " Juju:yetanother@host" s.waitSSHKeys(c, append(s.existingKeys, yetAnotherKeyWithComment)) } func (s *workerSuite) TestWorkerRestart(c *gc.C) { authWorker := authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag())) defer stop(c, authWorker) s.waitSSHKeys(c, append(s.existingKeys, s.existingEnvKey)) // Stop the worker and delete and add keys from the environment while it is down. // added: key 3 // deleted: key 1 (existing env key) stop(c, authWorker) s.setAuthorisedKeys(c, sshtesting.ValidKeyThree.Key+" yetanother@host") // Restart the worker and check that the ssh auth keys are as expected. authWorker = authenticationworker.NewWorker(s.keyupdaterApi, agentConfig(c, s.machine.Tag())) defer stop(c, authWorker) yetAnotherKeyWithCommentPrefix := sshtesting.ValidKeyThree.Key + " Juju:yetanother@host" s.waitSSHKeys(c, append(s.existingKeys, yetAnotherKeyWithCommentPrefix)) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/localstorage/�����������������������������������0000755�0000153�0000161�00000000000�12321735643�024656� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/localstorage/config_test.go���������������������0000644�0000153�0000161�00000005144�12321735642�027514� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package localstorage_test import ( stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/worker/localstorage" ) type configSuite struct{} var _ = gc.Suite(&configSuite{}) func TestPackage(t *stdtesting.T) { gc.TestingT(t) } type localStorageConfig struct { storageDir string storageAddr string } func (c *localStorageConfig) StorageDir() string { return c.storageDir } func (c *localStorageConfig) StorageAddr() string { return c.storageAddr } type localTLSStorageConfig struct { localStorageConfig caCertPEM []byte caKeyPEM []byte hostnames []string authkey string } func (c *localTLSStorageConfig) StorageCACert() []byte { return c.caCertPEM } func (c *localTLSStorageConfig) StorageCAKey() []byte { return c.caKeyPEM } func (c *localTLSStorageConfig) StorageHostnames() []string { return c.hostnames } func (c *localTLSStorageConfig) StorageAuthKey() string { return c.authkey } func (*configSuite) TestStoreConfig(c *gc.C) { var config localStorageConfig m, err := localstorage.StoreConfig(&config) c.Assert(err, gc.IsNil) c.Assert(m, gc.DeepEquals, map[string]string{ localstorage.StorageDir: "", localstorage.StorageAddr: "", }) config.storageDir = "a" config.storageAddr = "b" m, err = localstorage.StoreConfig(&config) c.Assert(err, gc.IsNil) c.Assert(m, gc.DeepEquals, map[string]string{ localstorage.StorageDir: config.storageDir, localstorage.StorageAddr: config.storageAddr, }) } func (*configSuite) TestStoreConfigTLS(c *gc.C) { var config localTLSStorageConfig m, err := localstorage.StoreConfig(&config) c.Assert(err, gc.IsNil) c.Assert(m, gc.DeepEquals, map[string]string{ localstorage.StorageDir: "", localstorage.StorageAddr: "", }) config.storageDir = "a" config.storageAddr = "b" config.caCertPEM = []byte("heyhey") config.caKeyPEM = []byte("hoho") config.hostnames = []string{"easy", "as", "1.2.3"} config.authkey = "password" m, err = localstorage.StoreConfig(&config) c.Assert(err, gc.IsNil) c.Assert(m, gc.DeepEquals, map[string]string{ localstorage.StorageDir: config.storageDir, localstorage.StorageAddr: config.storageAddr, localstorage.StorageCACert: string(config.caCertPEM), localstorage.StorageCAKey: string(config.caKeyPEM), localstorage.StorageHostnames: mustMarshalYAML(c, config.hostnames), localstorage.StorageAuthKey: config.authkey, }) } func mustMarshalYAML(c *gc.C, v interface{}) string { data, err := goyaml.Marshal(v) c.Assert(err, gc.IsNil) return string(data) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/localstorage/config.go��������������������������0000644�0000153�0000161�00000006176�12321735642�026463� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package localstorage import ( "launchpad.net/goyaml" "launchpad.net/juju-core/agent" ) const ( // TODO(axw) 2013-09-25 bug #1230131 // Move these variables out of agent when we can do upgrades in // the right place. In this case, the local provider should do // the envvar-to-agent.conf migration. StorageDir = agent.StorageDir StorageAddr = agent.StorageAddr StorageCACert = "StorageCACert" StorageCAKey = "StorageCAKey" StorageHostnames = "StorageHostnames" StorageAuthKey = "StorageAuthKey" ) // LocalStorageConfig is an interface that, if implemented, may be used // to configure a machine agent for use with the localstorage worker in // this package. type LocalStorageConfig interface { StorageDir() string StorageAddr() string } // LocalTLSStorageConfig is an interface that extends LocalStorageConfig // to support serving storage over TLS. type LocalTLSStorageConfig interface { LocalStorageConfig // StorageCACert is the CA certificate in PEM format. StorageCACert() []byte // StorageCAKey is the CA private key in PEM format. StorageCAKey() []byte // StorageHostnames is the set of hostnames that will // be assigned to the storage server's certificate. StorageHostnames() []string // StorageAuthKey is the key that clients must present // to perform modifying operations. StorageAuthKey() string } type config struct { storageDir string storageAddr string caCertPEM []byte caKeyPEM []byte hostnames []string authkey string } // StoreConfig takes a LocalStorageConfig (or derivative interface), // and stores it in a map[string]string suitable for updating an // agent.Config's key/value map. func StoreConfig(storageConfig LocalStorageConfig) (map[string]string, error) { kv := make(map[string]string) kv[StorageDir] = storageConfig.StorageDir() kv[StorageAddr] = storageConfig.StorageAddr() if tlsConfig, ok := storageConfig.(LocalTLSStorageConfig); ok { if authkey := tlsConfig.StorageAuthKey(); authkey != "" { kv[StorageAuthKey] = authkey } if cert := tlsConfig.StorageCACert(); cert != nil { kv[StorageCACert] = string(cert) } if key := tlsConfig.StorageCAKey(); key != nil { kv[StorageCAKey] = string(key) } if hostnames := tlsConfig.StorageHostnames(); len(hostnames) > 0 { data, err := goyaml.Marshal(hostnames) if err != nil { return nil, err } kv[StorageHostnames] = string(data) } } return kv, nil } func loadConfig(agentConfig agent.Config) (*config, error) { config := &config{ storageDir: agentConfig.Value(StorageDir), storageAddr: agentConfig.Value(StorageAddr), authkey: agentConfig.Value(StorageAuthKey), } caCertPEM := agentConfig.Value(StorageCACert) if len(caCertPEM) > 0 { config.caCertPEM = []byte(caCertPEM) } caKeyPEM := agentConfig.Value(StorageCAKey) if len(caKeyPEM) > 0 { config.caKeyPEM = []byte(caKeyPEM) } hostnames := agentConfig.Value(StorageHostnames) if len(hostnames) > 0 { err := goyaml.Unmarshal([]byte(hostnames), &config.hostnames) if err != nil { return nil, err } } return config, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/localstorage/worker.go��������������������������0000644�0000153�0000161�00000003740�12321735642�026521� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package localstorage import ( "net" "github.com/juju/loggo" "launchpad.net/tomb" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/httpstorage" "launchpad.net/juju-core/worker" ) var logger = loggo.GetLogger("juju.worker.localstorage") type storageWorker struct { config agent.Config tomb tomb.Tomb } func NewWorker(config agent.Config) worker.Worker { w := &storageWorker{config: config} go func() { defer w.tomb.Done() w.tomb.Kill(w.waitForDeath()) }() return w } // Kill implements worker.Worker.Kill. func (s *storageWorker) Kill() { s.tomb.Kill(nil) } // Wait implements worker.Worker.Wait. func (s *storageWorker) Wait() error { return s.tomb.Wait() } func (s *storageWorker) serveStorage(storageAddr, storageDir string, config *config) (net.Listener, error) { authenticated := len(config.caCertPEM) > 0 && len(config.caKeyPEM) > 0 scheme := "http://" if authenticated { scheme = "https://" } logger.Infof("serving storage from %s to %s%s", storageDir, scheme, storageAddr) storage, err := filestorage.NewFileStorageWriter(storageDir) if err != nil { return nil, err } if authenticated { return httpstorage.ServeTLS( storageAddr, storage, config.caCertPEM, config.caKeyPEM, config.hostnames, config.authkey, ) } return httpstorage.Serve(storageAddr, storage) } func (s *storageWorker) waitForDeath() error { config, err := loadConfig(s.config) if err != nil { logger.Errorf("error loading config: %v", err) return err } storageListener, err := s.serveStorage(config.storageAddr, config.storageDir, config) if err != nil { logger.Errorf("error with local storage: %v", err) return err } defer storageListener.Close() logger.Infof("storage routines started, awaiting death") <-s.tomb.Dying() logger.Infof("dying, closing storage listeners") return tomb.ErrDying } ��������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/stringsworker.go��������������������������������0000644�0000153�0000161�00000004252�12321735642�025453� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker import ( "launchpad.net/tomb" apiWatcher "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/state/watcher" ) // stringsWorker is the internal implementation of the Worker // interface, using a StringsWatcher for handling changes. type stringsWorker struct { tomb tomb.Tomb // handler is what will be called when events are triggered. handler StringsWatchHandler } // StringsWatchHandler implements the business logic triggered as part // of watching a StringsWatcher. type StringsWatchHandler interface { // SetUp starts the handler, this should create the watcher we // will be waiting on for more events. SetUp can return a Watcher // even if there is an error, and strings Worker will make sure to // stop the watcher. SetUp() (apiWatcher.StringsWatcher, error) // TearDown should cleanup any resources that are left around TearDown() error // Handle is called when the Watcher has indicated there are // changes, do whatever work is necessary to process it Handle(changes []string) error } // NewStringsWorker starts a new worker running the business logic // from the handler. The worker loop is started in another goroutine // as a side effect of calling this. func NewStringsWorker(handler StringsWatchHandler) Worker { sw := &stringsWorker{ handler: handler, } go func() { defer sw.tomb.Done() sw.tomb.Kill(sw.loop()) }() return sw } // Kill the loop with no-error func (sw *stringsWorker) Kill() { sw.tomb.Kill(nil) } // Wait for the looping to finish func (sw *stringsWorker) Wait() error { return sw.tomb.Wait() } func (sw *stringsWorker) loop() error { w, err := sw.handler.SetUp() if err != nil { if w != nil { // We don't bother to propagate an error, because we // already have an error w.Stop() } return err } defer propagateTearDown(sw.handler, &sw.tomb) defer watcher.Stop(w, &sw.tomb) for { select { case <-sw.tomb.Dying(): return tomb.ErrDying case changes, ok := <-w.Changes(): if !ok { return mustErr(w) } if err := sw.handler.Handle(changes); err != nil { return err } } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/rsyslog/����������������������������������������0000755�0000153�0000161�00000000000�12321735642�023700� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/rsyslog/export_test.go��������������������������0000644�0000153�0000161�00000000512�12321735642�026605� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rsyslog var ( RestartRsyslog = &restartRsyslog LogDir = &logDir RsyslogConfDir = &rsyslogConfDir LookupUser = &lookupUser NewRsyslogConfigHandler = newRsyslogConfigHandler ) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/rsyslog/rsyslog_test.go�������������������������0000644�0000153�0000161�00000021441�12321735642�026772� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rsyslog_test import ( "io/ioutil" "os" "path/filepath" stdtesting "testing" "time" "github.com/juju/testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cert" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/log/syslog" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/rsyslog" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type RsyslogSuite struct { jujutesting.JujuConnSuite } var _ = gc.Suite(&RsyslogSuite{}) func (s *RsyslogSuite) SetUpSuite(c *gc.C) { s.JujuConnSuite.SetUpSuite(c) // TODO(waigani) 2014-03-19 bug 1294462 // Add patch for suite functions restore := testing.PatchValue(rsyslog.LookupUser, func(username string) (uid, gid int, err error) { // worker will not attempt to chown files if uid/gid is 0 return 0, 0, nil }) s.AddSuiteCleanup(func(*gc.C) { restore() }) } func (s *RsyslogSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.PatchValue(rsyslog.RestartRsyslog, func() error { return nil }) s.PatchValue(rsyslog.LogDir, c.MkDir()) s.PatchValue(rsyslog.RsyslogConfDir, c.MkDir()) } func waitForFile(c *gc.C, file string) { timeout := time.After(coretesting.LongWait) for { select { case <-timeout: c.Fatalf("timed out waiting for %s to be written", file) case <-time.After(coretesting.ShortWait): if _, err := os.Stat(file); err == nil { return } } } } func waitForRestart(c *gc.C, restarted chan struct{}) { timeout := time.After(coretesting.LongWait) for { select { case <-timeout: c.Fatalf("timed out waiting for rsyslog to be restarted") case <-restarted: return } } } func (s *RsyslogSuite) TestStartStop(c *gc.C) { st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits) worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", []string{"0.1.2.3"}) c.Assert(err, gc.IsNil) worker.Kill() c.Assert(worker.Wait(), gc.IsNil) } func (s *RsyslogSuite) TestTearDown(c *gc.C) { st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", []string{"0.1.2.3"}) c.Assert(err, gc.IsNil) confFile := filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf") // On worker teardown, the rsyslog config file should be removed. defer func() { _, err := os.Stat(confFile) c.Assert(err, jc.Satisfies, os.IsNotExist) }() defer func() { c.Assert(worker.Wait(), gc.IsNil) }() defer worker.Kill() waitForFile(c, confFile) } func (s *RsyslogSuite) TestModeForwarding(c *gc.C) { err := s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert}) c.Assert(err, gc.IsNil) st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits) addrs := []string{"0.1.2.3", "0.2.4.6"} worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", addrs) c.Assert(err, gc.IsNil) defer func() { c.Assert(worker.Wait(), gc.IsNil) }() defer worker.Kill() // We should get a ca-cert.pem with the contents introduced into state config. waitForFile(c, filepath.Join(*rsyslog.LogDir, "ca-cert.pem")) caCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "ca-cert.pem")) c.Assert(err, gc.IsNil) c.Assert(string(caCertPEM), gc.DeepEquals, coretesting.CACert) // Verify rsyslog configuration. waitForFile(c, filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")) rsyslogConf, err := ioutil.ReadFile(filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")) c.Assert(err, gc.IsNil) syslogPort := s.Conn.Environ.Config().SyslogPort() syslogConfig := syslog.NewForwardConfig(m.Tag(), *rsyslog.LogDir, syslogPort, "", addrs) syslogConfig.ConfigDir = *rsyslog.RsyslogConfDir rendered, err := syslogConfig.Render() c.Assert(err, gc.IsNil) c.Assert(string(rsyslogConf), gc.DeepEquals, string(rendered)) } func (s *RsyslogSuite) TestModeAccumulate(c *gc.C) { st, m := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeAccumulate, m.Tag(), "", nil) c.Assert(err, gc.IsNil) defer func() { c.Assert(worker.Wait(), gc.IsNil) }() defer worker.Kill() waitForFile(c, filepath.Join(*rsyslog.LogDir, "ca-cert.pem")) // We should have ca-cert.pem, rsyslog-cert.pem, and rsyslog-key.pem. caCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "ca-cert.pem")) c.Assert(err, gc.IsNil) rsyslogCertPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "rsyslog-cert.pem")) c.Assert(err, gc.IsNil) rsyslogKeyPEM, err := ioutil.ReadFile(filepath.Join(*rsyslog.LogDir, "rsyslog-key.pem")) c.Assert(err, gc.IsNil) _, _, err = cert.ParseCertAndKey(rsyslogCertPEM, rsyslogKeyPEM) c.Assert(err, gc.IsNil) err = cert.Verify(rsyslogCertPEM, caCertPEM, time.Now().UTC()) c.Assert(err, gc.IsNil) // Verify rsyslog configuration. waitForFile(c, filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")) rsyslogConf, err := ioutil.ReadFile(filepath.Join(*rsyslog.RsyslogConfDir, "25-juju.conf")) c.Assert(err, gc.IsNil) syslogPort := s.Conn.Environ.Config().SyslogPort() syslogConfig := syslog.NewAccumulateConfig(m.Tag(), *rsyslog.LogDir, syslogPort, "") syslogConfig.ConfigDir = *rsyslog.RsyslogConfDir rendered, err := syslogConfig.Render() c.Assert(err, gc.IsNil) c.Assert(string(rsyslogConf), gc.DeepEquals, string(rendered)) } func (s *RsyslogSuite) TestNamespace(c *gc.C) { st, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) // namespace only takes effect in filenames // for machine-0; all others assume isolation. s.testNamespace(c, st, "machine-0", "", "25-juju.conf", *rsyslog.LogDir) s.testNamespace(c, st, "machine-0", "mynamespace", "25-juju-mynamespace.conf", *rsyslog.LogDir+"-mynamespace") s.testNamespace(c, st, "machine-1", "", "25-juju.conf", *rsyslog.LogDir) s.testNamespace(c, st, "machine-1", "mynamespace", "25-juju.conf", *rsyslog.LogDir) s.testNamespace(c, st, "unit-myservice-0", "", "26-juju-unit-myservice-0.conf", *rsyslog.LogDir) s.testNamespace(c, st, "unit-myservice-0", "mynamespace", "26-juju-unit-myservice-0.conf", *rsyslog.LogDir) } // testNamespace starts a worker and ensures that // the rsyslog config file has the expected filename, // and the appropriate log dir is used. func (s *RsyslogSuite) testNamespace(c *gc.C, st *api.State, tag, namespace, expectedFilename, expectedLogDir string) { restarted := make(chan struct{}, 2) // once for create, once for teardown s.PatchValue(rsyslog.RestartRsyslog, func() error { restarted <- struct{}{} return nil }) err := os.MkdirAll(expectedLogDir, 0755) c.Assert(err, gc.IsNil) err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert}) c.Assert(err, gc.IsNil) worker, err := rsyslog.NewRsyslogConfigWorker(st.Rsyslog(), rsyslog.RsyslogModeForwarding, tag, namespace, []string{"0.1.2.3"}) c.Assert(err, gc.IsNil) defer func() { c.Assert(worker.Wait(), gc.IsNil) }() defer worker.Kill() // Ensure that ca-cert.pem gets written to the expected log dir. waitForFile(c, filepath.Join(expectedLogDir, "ca-cert.pem")) // Wait for rsyslog to be restarted, so we can check to see // what the name of the config file is. waitForRestart(c, restarted) dir, err := os.Open(*rsyslog.RsyslogConfDir) c.Assert(err, gc.IsNil) names, err := dir.Readdirnames(-1) dir.Close() c.Assert(err, gc.IsNil) c.Assert(names, gc.HasLen, 1) c.Assert(names[0], gc.Equals, expectedFilename) } func (s *RsyslogSuite) TestConfigChange(c *gc.C) { var restarted bool s.PatchValue(rsyslog.RestartRsyslog, func() error { restarted = true return nil }) st, m := s.OpenAPIAsNewMachine(c, state.JobHostUnits) handler, err := rsyslog.NewRsyslogConfigHandler(st.Rsyslog(), rsyslog.RsyslogModeForwarding, m.Tag(), "", []string{"0.1.2.3"}) c.Assert(err, gc.IsNil) assertRestart := func(v bool) { err := handler.Handle() c.Assert(err, gc.IsNil) c.Assert(restarted, gc.Equals, v) restarted = false // Handling again should not restart, as no changes have been made. err = handler.Handle() c.Assert(err, gc.IsNil) c.Assert(restarted, jc.IsFalse) } err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"rsyslog-ca-cert": coretesting.CACert}) c.Assert(err, gc.IsNil) assertRestart(true) err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"syslog-port": 1}) c.Assert(err, gc.IsNil) assertRestart(true) err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"unrelated": "anything"}) c.Assert(err, gc.IsNil) assertRestart(false) err = s.APIState.Client().EnvironmentSet(map[string]interface{}{"syslog-port": 2}) c.Assert(err, gc.IsNil) assertRestart(true) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/rsyslog/worker.go�������������������������������0000644�0000153�0000161�00000017057�12321735642�025552� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rsyslog import ( "bytes" "fmt" "os" "os/user" "strconv" "time" "github.com/errgo/errgo" "github.com/juju/loggo" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/cert" "launchpad.net/juju-core/log/syslog" "launchpad.net/juju-core/names" apirsyslog "launchpad.net/juju-core/state/api/rsyslog" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker" ) var logger = loggo.GetLogger("juju.worker.rsyslog") var ( rsyslogConfDir = "/etc/rsyslog.d" logDir = agent.DefaultLogDir ) // RsyslogMode describes how to configure rsyslog. type RsyslogMode int const ( RsyslogModeInvalid RsyslogMode = iota // RsyslogModeForwarding is the mode in which // rsyslog will be configured to forward logging // to state servers. RsyslogModeForwarding // RsyslogModeAccumulate is the mode in which // rsyslog will be configured to accumulate logging // from other machines into an "all-machines.log". RsyslogModeAccumulate ) // RsyslogConfigHandler implements worker.NotifyWatchHandler, watching // environment configuration changes and generating new rsyslog // configuration. type RsyslogConfigHandler struct { st *apirsyslog.State mode RsyslogMode syslogConfig *syslog.SyslogConfig rsyslogConfPath string // We store the syslog-port and rsyslog-ca-cert // values after writing the rsyslog configuration, // so we can decide whether a change has occurred. syslogPort int rsyslogCACert []byte } var _ worker.NotifyWatchHandler = (*RsyslogConfigHandler)(nil) // NewRsyslogConfigWorker returns a worker.Worker that watches // for config changes and updates rsyslog configuration based // on changes. The worker will remove the configuration file // on teardown. func NewRsyslogConfigWorker(st *apirsyslog.State, mode RsyslogMode, tag, namespace string, stateServerAddrs []string) (worker.Worker, error) { handler, err := newRsyslogConfigHandler(st, mode, tag, namespace, stateServerAddrs) if err != nil { return nil, err } logger.Debugf("starting rsyslog worker mode %v for %q %q", mode, tag, namespace) return worker.NewNotifyWorker(handler), nil } func newRsyslogConfigHandler(st *apirsyslog.State, mode RsyslogMode, tag, namespace string, stateServerAddrs []string) (*RsyslogConfigHandler, error) { var syslogConfig *syslog.SyslogConfig if mode == RsyslogModeAccumulate { syslogConfig = syslog.NewAccumulateConfig( tag, logDir, 0, namespace, ) } else { syslogConfig = syslog.NewForwardConfig( tag, logDir, 0, namespace, stateServerAddrs, ) } // Historically only machine-0 includes the namespace in the log // dir/file; for backwards compatibility we continue the tradition. if tag != "machine-0" { namespace = "" } kind, err := names.TagKind(tag) if err != nil { return nil, err } if kind == names.MachineTagKind { if namespace == "" { syslogConfig.ConfigFileName = "25-juju.conf" } else { syslogConfig.ConfigFileName = fmt.Sprintf("25-juju-%s.conf", namespace) } } else { syslogConfig.ConfigFileName = fmt.Sprintf("26-juju-%s.conf", tag) } syslogConfig.ConfigDir = rsyslogConfDir syslogConfig.LogDir = logDir if namespace != "" { syslogConfig.LogDir += "-" + namespace } return &RsyslogConfigHandler{ st: st, mode: mode, syslogConfig: syslogConfig, }, nil } func (h *RsyslogConfigHandler) SetUp() (watcher.NotifyWatcher, error) { if h.mode == RsyslogModeAccumulate { if err := h.ensureCertificates(); err != nil { return nil, errgo.Annotate(err, "failed to write rsyslog certificates") } } return h.st.WatchForEnvironConfigChanges() } var restartRsyslog = syslog.Restart func (h *RsyslogConfigHandler) TearDown() error { if err := os.Remove(h.syslogConfig.ConfigFilePath()); err == nil { restartRsyslog() } return nil } func (h *RsyslogConfigHandler) Handle() error { cfg, err := h.st.EnvironConfig() if err != nil { return errgo.Annotate(err, "cannot get environ config") } rsyslogCACert := cfg.RsyslogCACert() if rsyslogCACert == nil { return nil } // If neither syslog-port nor rsyslog-ca-cert // have changed, we can drop out now. if cfg.SyslogPort() == h.syslogPort && bytes.Equal(rsyslogCACert, h.rsyslogCACert) { return nil } h.syslogConfig.Port = cfg.SyslogPort() if h.mode == RsyslogModeForwarding { if err := writeFileAtomic(h.syslogConfig.CACertPath(), rsyslogCACert, 0644, 0, 0); err != nil { return errgo.Annotate(err, "cannot write CA certificate") } } data, err := h.syslogConfig.Render() if err != nil { return errgo.Annotate(err, "failed to render rsyslog configuration file") } if err := writeFileAtomic(h.syslogConfig.ConfigFilePath(), []byte(data), 0644, 0, 0); err != nil { return errgo.Annotate(err, "failed to write rsyslog configuration file") } logger.Debugf("Reloading rsyslog configuration") if err := restartRsyslog(); err != nil { logger.Errorf("failed to reload rsyslog configuration") return errgo.Annotate(err, "cannot restart rsyslog") } // Record config values so we don't try again. // Do this last so we recover from intermittent // failures. h.syslogPort = cfg.SyslogPort() h.rsyslogCACert = rsyslogCACert return nil } var lookupUser = func(username string) (uid, gid int, err error) { u, err := user.Lookup(username) if err != nil { return -1, -1, err } uid, err = strconv.Atoi(u.Uid) if err != nil { return -1, -1, err } gid, err = strconv.Atoi(u.Gid) if err != nil { return -1, -1, err } return uid, gid, nil } // ensureCertificates ensures that a CA certificate, // server certificate, and private key exist in the log // directory, and writes them if not. The CA certificate // is entered into the environment configuration to be // picked up by other agents. func (h *RsyslogConfigHandler) ensureCertificates() error { // We write ca-cert.pem last, after propagating into state. // If it's there, then there's nothing to do. Otherwise, // start over. caCertPEM := h.syslogConfig.CACertPath() if _, err := os.Stat(caCertPEM); err == nil { return nil } // Files must be chowned to syslog:adm. syslogUid, syslogGid, err := lookupUser("syslog") if err != nil { return err } // Generate a new CA and server cert/key pairs. // The CA key will be discarded after the server // cert has been generated. expiry := time.Now().UTC().AddDate(10, 0, 0) caCertPEMBytes, caKeyPEMBytes, err := cert.NewCA("rsyslog", expiry) if err != nil { return err } rsyslogCertPEMBytes, rsyslogKeyPEMBytes, err := cert.NewServer(caCertPEMBytes, caKeyPEMBytes, expiry, nil) if err != nil { return err } // Update the environment config with the CA cert, // so clients can configure rsyslog. if err := h.st.SetRsyslogCert(caCertPEMBytes); err != nil { return err } // Write the certificates and key. The CA certificate must be written last for idempotency. for _, pair := range []struct { path string data []byte }{ {h.syslogConfig.ServerCertPath(), rsyslogCertPEMBytes}, {h.syslogConfig.ServerKeyPath(), rsyslogKeyPEMBytes}, {h.syslogConfig.CACertPath(), caCertPEMBytes}, } { if err := writeFileAtomic(pair.path, pair.data, 0600, syslogUid, syslogGid); err != nil { return err } } return nil } func writeFileAtomic(path string, data []byte, mode os.FileMode, uid, gid int) error { chmodAndChown := func(f *os.File) error { if err := f.Chmod(mode); err != nil { return err } if uid != 0 { if err := f.Chown(uid, gid); err != nil { return err } } return nil } return utils.AtomicWriteFileAndChange(path, data, chmodAndChown) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/instancepoller/���������������������������������0000755�0000153�0000161�00000000000�12321736000�025205� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/instancepoller/aggregate.go���������������������0000644�0000153�0000161�00000004746�12321735642�027510� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instancepoller import ( "time" "github.com/juju/ratelimit" "launchpad.net/tomb" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" ) type instanceGetter interface { Instances(ids []instance.Id) ([]instance.Instance, error) } type aggregator struct { environ instanceGetter reqc chan instanceInfoReq tomb tomb.Tomb } func newAggregator(env instanceGetter) *aggregator { a := &aggregator{ environ: env, reqc: make(chan instanceInfoReq), } go func() { defer a.tomb.Done() a.tomb.Kill(a.loop()) }() return a } type instanceInfoReq struct { instId instance.Id reply chan<- instanceInfoReply } type instanceInfoReply struct { info instanceInfo err error } func (a *aggregator) instanceInfo(id instance.Id) (instanceInfo, error) { reply := make(chan instanceInfoReply) a.reqc <- instanceInfoReq{ instId: id, reply: reply, } r := <-reply return r.info, r.err } var gatherTime = 3 * time.Second func (a *aggregator) loop() error { timer := time.NewTimer(0) timer.Stop() var reqs []instanceInfoReq // We use a capacity of 1 so that sporadic requests will // be serviced immediately without having to wait. bucket := ratelimit.New(gatherTime, 1) for { select { case <-a.tomb.Dying(): return tomb.ErrDying case req := <-a.reqc: if len(reqs) == 0 { waitTime := bucket.Take(1) timer.Reset(waitTime) } reqs = append(reqs, req) case <-timer.C: ids := make([]instance.Id, len(reqs)) for i, req := range reqs { ids[i] = req.instId } insts, err := a.environ.Instances(ids) for i, req := range reqs { var reply instanceInfoReply if err != nil && err != environs.ErrPartialInstances { reply.err = err } else { reply.info, reply.err = a.instInfo(req.instId, insts[i]) } req.reply <- reply } reqs = nil } } } // instInfo returns the instance info for the given id // and instance. If inst is nil, it returns a not-found error. func (*aggregator) instInfo(id instance.Id, inst instance.Instance) (instanceInfo, error) { if inst == nil { return instanceInfo{}, errors.NotFoundf("instance %v", id) } addr, err := inst.Addresses() if err != nil { return instanceInfo{}, err } return instanceInfo{ addr, inst.Status(), }, nil } func (a *aggregator) Kill() { a.tomb.Kill(nil) } func (a *aggregator) Wait() error { return a.tomb.Wait() } ��������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/instancepoller/machine_test.go������������������0000644�0000153�0000161�00000025413�12321735776�030227� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // TODO(wallyworld) - move to instancepoller_test package instancepoller import ( stderrors "errors" "fmt" "math" "sync" "sync/atomic" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) var _ = gc.Suite(&machineSuite{}) type machineSuite struct { testbase.LoggingSuite } var testAddrs = []instance.Address{instance.NewAddress("127.0.0.1")} func (s *machineSuite) TestSetsInstanceInfoInitially(c *gc.C) { context := &testMachineContext{ getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil), dyingc: make(chan struct{}), } m := &testMachine{ id: "99", instanceId: "i1234", refresh: func() error { return nil }, life: state.Alive, } died := make(chan machine) // Change the poll intervals to be short, so that we know // that we've polled (probably) at least a few times. s.PatchValue(&ShortPoll, coretesting.ShortWait/10) s.PatchValue(&LongPoll, coretesting.ShortWait/10) go runMachine(context, m, nil, died) time.Sleep(coretesting.ShortWait) killMachineLoop(c, m, context.dyingc, died) c.Assert(context.killAllErr, gc.Equals, nil) c.Assert(m.addresses, gc.DeepEquals, testAddrs) c.Assert(m.setAddressCount, gc.Equals, 1) c.Assert(m.instStatus, gc.Equals, "running") } func (s *machineSuite) TestShortPollIntervalWhenNoAddress(c *gc.C) { s.PatchValue(&ShortPoll, 1*time.Millisecond) s.PatchValue(&LongPoll, coretesting.LongWait) count := countPolls(c, nil, "running", params.StatusStarted) c.Assert(count, jc.GreaterThan, 2) } func (s *machineSuite) TestShortPollIntervalWhenNoStatus(c *gc.C) { s.PatchValue(&ShortPoll, 1*time.Millisecond) s.PatchValue(&LongPoll, coretesting.LongWait) count := countPolls(c, testAddrs, "", params.StatusStarted) c.Assert(count, jc.GreaterThan, 2) } func (s *machineSuite) TestShortPollIntervalWhenNotStarted(c *gc.C) { s.PatchValue(&ShortPoll, 1*time.Millisecond) s.PatchValue(&LongPoll, coretesting.LongWait) count := countPolls(c, testAddrs, "pending", params.StatusPending) c.Assert(count, jc.GreaterThan, 2) } func (s *machineSuite) TestShortPollIntervalExponent(c *gc.C) { s.PatchValue(&ShortPoll, 1*time.Microsecond) s.PatchValue(&LongPoll, coretesting.LongWait) s.PatchValue(&ShortPollBackoff, 2.0) // With an exponent of 2, the maximum number of polls that can // occur within the given interval ShortWait is log to the base // ShortPollBackoff of ShortWait/ShortPoll, given that sleep will // sleep for at least the requested interval. maxCount := int(math.Log(float64(coretesting.ShortWait)/float64(ShortPoll))/math.Log(ShortPollBackoff) + 1) count := countPolls(c, nil, "", params.StatusStarted) c.Assert(count, jc.GreaterThan, 2) c.Assert(count, jc.LessThan, maxCount) c.Logf("actual count: %v; max %v", count, maxCount) } func (s *machineSuite) TestLongPollIntervalWhenHasAllInstanceInfo(c *gc.C) { s.PatchValue(&ShortPoll, coretesting.LongWait) s.PatchValue(&LongPoll, 1*time.Millisecond) count := countPolls(c, testAddrs, "running", params.StatusStarted) c.Assert(count, jc.GreaterThan, 2) } // countPolls sets up a machine loop with the given // addresses and status to be returned from getInstanceInfo, // waits for coretesting.ShortWait, and returns the // number of times the instance is polled. func countPolls(c *gc.C, addrs []instance.Address, instStatus string, machineStatus params.Status) int { count := int32(0) getInstanceInfo := func(id instance.Id) (instanceInfo, error) { c.Check(id, gc.Equals, instance.Id("i1234")) atomic.AddInt32(&count, 1) if addrs == nil { return instanceInfo{}, fmt.Errorf("no instance addresses available") } return instanceInfo{addrs, instStatus}, nil } context := &testMachineContext{ getInstanceInfo: getInstanceInfo, dyingc: make(chan struct{}), } m := &testMachine{ id: "99", instanceId: "i1234", refresh: func() error { return nil }, addresses: addrs, life: state.Alive, status: machineStatus, } died := make(chan machine) go runMachine(context, m, nil, died) time.Sleep(coretesting.ShortWait) killMachineLoop(c, m, context.dyingc, died) c.Assert(context.killAllErr, gc.Equals, nil) return int(count) } func (s *machineSuite) TestSinglePollWhenInstancInfoUnimplemented(c *gc.C) { s.PatchValue(&ShortPoll, 1*time.Millisecond) s.PatchValue(&LongPoll, 1*time.Millisecond) count := int32(0) getInstanceInfo := func(id instance.Id) (instanceInfo, error) { c.Check(id, gc.Equals, instance.Id("i1234")) atomic.AddInt32(&count, 1) return instanceInfo{}, errors.NewNotImplementedError("instance address") } context := &testMachineContext{ getInstanceInfo: getInstanceInfo, dyingc: make(chan struct{}), } m := &testMachine{ id: "99", instanceId: "i1234", refresh: func() error { return nil }, life: state.Alive, } died := make(chan machine) go runMachine(context, m, nil, died) time.Sleep(coretesting.ShortWait) killMachineLoop(c, m, context.dyingc, died) c.Assert(context.killAllErr, gc.Equals, nil) c.Assert(count, gc.Equals, int32(1)) } func (*machineSuite) TestChangedRefreshes(c *gc.C) { context := &testMachineContext{ getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil), dyingc: make(chan struct{}), } refreshc := make(chan struct{}) m := &testMachine{ id: "99", instanceId: "i1234", refresh: func() error { refreshc <- struct{}{} return nil }, addresses: testAddrs, life: state.Dead, } died := make(chan machine) changed := make(chan struct{}) go runMachine(context, m, changed, died) select { case <-died: c.Fatalf("machine died prematurely") case <-time.After(coretesting.ShortWait): } // Notify the machine that it has changed; it should // refresh, and publish the fact that it no longer has // an address. changed <- struct{}{} select { case <-refreshc: case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for refresh") } select { case <-died: case <-time.After(coretesting.LongWait): c.Fatalf("expected death after life set to dying") } // The machine addresses should remain the same even // after death. c.Assert(m.addresses, gc.DeepEquals, testAddrs) } var terminatingErrorsTests = []struct { about string mutate func(m *testMachine, err error) }{{ about: "set addresses", mutate: func(m *testMachine, err error) { m.setAddressesErr = err }, }, { about: "refresh", mutate: func(m *testMachine, err error) { m.refresh = func() error { return err } }, }, { about: "instance id", mutate: func(m *testMachine, err error) { m.instanceIdErr = err }, }} func (*machineSuite) TestTerminatingErrors(c *gc.C) { for i, test := range terminatingErrorsTests { c.Logf("test %d: %s", i, test.about) testTerminatingErrors(c, test.mutate) } } // // testTerminatingErrors checks that when a testMachine is // changed with the given mutate function, the machine goroutine // will die having called its context's killAll function with the // given error. The test is cunningly structured so that it in the normal course // of things it will go through all possible places that can return an error. func testTerminatingErrors(c *gc.C, mutate func(m *testMachine, err error)) { context := &testMachineContext{ getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil), dyingc: make(chan struct{}), } expectErr := stderrors.New("a very unusual error") m := &testMachine{ id: "99", instanceId: "i1234", refresh: func() error { return nil }, life: state.Alive, } mutate(m, expectErr) died := make(chan machine) changed := make(chan struct{}, 1) go runMachine(context, m, changed, died) changed <- struct{}{} select { case <-died: case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for machine to die") } c.Assert(context.killAllErr, gc.ErrorMatches, ".*"+expectErr.Error()) } func killMachineLoop(c *gc.C, m machine, dying chan struct{}, died <-chan machine) { close(dying) select { case diedm := <-died: c.Assert(diedm, gc.Equals, m) case <-time.After(coretesting.LongWait): c.Fatalf("updater did not die after dying channel was closed") } } func instanceInfoGetter( c *gc.C, expectId instance.Id, addrs []instance.Address, status string, err error) func(id instance.Id) (instanceInfo, error) { return func(id instance.Id) (instanceInfo, error) { c.Check(id, gc.Equals, expectId) return instanceInfo{addrs, status}, err } } type testMachineContext struct { killAllErr error getInstanceInfo func(instance.Id) (instanceInfo, error) dyingc chan struct{} } func (context *testMachineContext) killAll(err error) { if err == nil { panic("killAll with nil error") } context.killAllErr = err } func (context *testMachineContext) instanceInfo(id instance.Id) (instanceInfo, error) { return context.getInstanceInfo(id) } func (context *testMachineContext) dying() <-chan struct{} { return context.dyingc } type testMachine struct { instanceId instance.Id instanceIdErr error id string instStatus string status params.Status refresh func() error setAddressesErr error // mu protects the following fields. mu sync.Mutex life state.Life addresses []instance.Address setAddressCount int } func (m *testMachine) Id() string { if m.id == "" { panic("Id called but not set") } return m.id } func (m *testMachine) Addresses() []instance.Address { m.mu.Lock() defer m.mu.Unlock() return m.addresses } func (m *testMachine) InstanceId() (instance.Id, error) { return m.instanceId, m.instanceIdErr } func (m *testMachine) Status() (status params.Status, info string, data params.StatusData, err error) { return m.status, "", nil, nil } func (m *testMachine) InstanceStatus() (string, error) { m.mu.Lock() defer m.mu.Unlock() return m.instStatus, nil } func (m *testMachine) SetInstanceStatus(status string) error { m.mu.Lock() defer m.mu.Unlock() m.instStatus = status return nil } func (m *testMachine) SetAddresses(addrs []instance.Address) error { if m.setAddressesErr != nil { return m.setAddressesErr } m.mu.Lock() defer m.mu.Unlock() m.addresses = append(m.addresses[:0], addrs...) m.setAddressCount++ return nil } func (m *testMachine) String() string { return m.id } func (m *testMachine) Refresh() error { return m.refresh() } func (m *testMachine) Life() state.Life { m.mu.Lock() defer m.mu.Unlock() return m.life } func (m *testMachine) setLife(life state.Life) { m.mu.Lock() defer m.mu.Unlock() m.life = life } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/instancepoller/updater.go�����������������������0000644�0000153�0000161�00000017047�12321735776�027234� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instancepoller import ( "fmt" "time" "github.com/juju/loggo" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" ) var logger = loggo.GetLogger("juju.worker.instanceupdater") // ShortPoll and LongPoll hold the polling intervals for the instance // updater. When a machine has no address or is not started, it will be // polled at ShortPoll intervals until it does, exponentially backing off // with an exponent of ShortPollBackoff until a maximum(ish) of LongPoll. // // When a machine has an address and is started LongPoll will be used to // check that the instance address or status has not changed. var ( ShortPoll = 1 * time.Second ShortPollBackoff = 2.0 LongPoll = 15 * time.Minute ) type machine interface { Id() string InstanceId() (instance.Id, error) Addresses() []instance.Address SetAddresses([]instance.Address) error InstanceStatus() (string, error) SetInstanceStatus(status string) error String() string Refresh() error Life() state.Life Status() (status params.Status, info string, data params.StatusData, err error) } type instanceInfo struct { addresses []instance.Address status string } type machineContext interface { killAll(err error) instanceInfo(id instance.Id) (instanceInfo, error) dying() <-chan struct{} } type machineAddress struct { machine machine addresses []instance.Address } var _ machine = (*state.Machine)(nil) type machinesWatcher interface { Changes() <-chan []string Err() error Stop() error } type updaterContext interface { newMachineContext() machineContext getMachine(id string) (machine, error) dying() <-chan struct{} } type updater struct { context updaterContext machines map[string]chan struct{} machineDead chan machine } // watchMachinesLoop watches for changes provided by the given // machinesWatcher and starts machine goroutines to deal // with them, using the provided newMachineContext // function to create the appropriate context for each new machine id. func watchMachinesLoop(context updaterContext, w machinesWatcher) (err error) { p := &updater{ context: context, machines: make(map[string]chan struct{}), machineDead: make(chan machine), } defer func() { if stopErr := w.Stop(); stopErr != nil { if err == nil { err = fmt.Errorf("error stopping watcher: %v", stopErr) } else { logger.Warningf("ignoring error when stopping watcher: %v", stopErr) } } for len(p.machines) > 0 { delete(p.machines, (<-p.machineDead).Id()) } }() for { select { case ids, ok := <-w.Changes(): if !ok { return watcher.MustErr(w) } if err := p.startMachines(ids); err != nil { return err } case m := <-p.machineDead: delete(p.machines, m.Id()) case <-p.context.dying(): return nil } } } func (p *updater) startMachines(ids []string) error { for _, id := range ids { if c := p.machines[id]; c == nil { // We don't know about the machine - start // a goroutine to deal with it. m, err := p.context.getMachine(id) if errors.IsNotFoundError(err) { logger.Warningf("watcher gave notification of non-existent machine %q", id) continue } if err != nil { return err } c = make(chan struct{}) p.machines[id] = c go runMachine(p.context.newMachineContext(), m, c, p.machineDead) } else { c <- struct{}{} } } return nil } // runMachine processes the address and status publishing for a given machine. // We assume that the machine is alive when this is first called. func runMachine(context machineContext, m machine, changed <-chan struct{}, died chan<- machine) { defer func() { // We can't just send on the died channel because the // central loop might be trying to write to us on the // changed channel. for { select { case died <- m: return case <-changed: } } }() if err := machineLoop(context, m, changed); err != nil { context.killAll(err) } } func machineLoop(context machineContext, m machine, changed <-chan struct{}) error { // Use a short poll interval when initially waiting for // a machine's address and machine agent to start, and a long one when it already // has an address and the machine agent is started. pollInterval := ShortPoll pollInstance := true for { if pollInstance { instInfo, err := pollInstanceInfo(context, m) if err != nil { // If the provider doesn't implement Addresses/Status now, // it never will until we're upgraded, so don't bother // asking any more. We could use less resources // by taking down the entire worker, but this is easier for now // (and hopefully the local provider will implement // Addresses/Status in the not-too-distant future), // so we won't need to worry about this case at all. if errors.IsNotImplementedError(err) { pollInterval = 365 * 24 * time.Hour } else { return err } } machineStatus, _, _, err := m.Status() if err != nil { logger.Warningf("cannot get current machine status for machine %v: %v", m.Id(), err) } if len(instInfo.addresses) > 0 && instInfo.status != "" && machineStatus == params.StatusStarted { // We've got at least one address and a status and instance is started, so poll infrequently. pollInterval = LongPoll } else if pollInterval < LongPoll { // We have no addresses or not started - poll increasingly rarely // until we do. pollInterval = time.Duration(float64(pollInterval) * ShortPollBackoff) } pollInstance = false } select { case <-time.After(pollInterval): pollInstance = true case <-context.dying(): return nil case <-changed: if err := m.Refresh(); err != nil { return err } if m.Life() == state.Dead { return nil } } } } // pollInstanceInfo checks the current provider addresses and status // for the given machine's instance, and sets them on the machine if they've changed. func pollInstanceInfo(context machineContext, m machine) (instInfo instanceInfo, err error) { instInfo = instanceInfo{} instId, err := m.InstanceId() if err != nil && !state.IsNotProvisionedError(err) { return instInfo, fmt.Errorf("cannot get machine's instance id: %v", err) } instInfo, err = context.instanceInfo(instId) if err != nil { if errors.IsNotImplementedError(err) { return instInfo, err } logger.Warningf("cannot get instance info for instance %q: %v", instId, err) return instInfo, nil } currentInstStatus, err := m.InstanceStatus() if err != nil { // This should never occur since the machine is provisioned. // But just in case, we reset polled status so we try again next time. logger.Warningf("cannot get current instance status for machine %v: %v", m.Id(), err) instInfo.status = "" } else { if instInfo.status != currentInstStatus { logger.Infof("machine %q has new instance status: %v", m.Id(), instInfo.status) if err = m.SetInstanceStatus(instInfo.status); err != nil { logger.Errorf("cannot set instance status on %q: %v", m, err) } } } if !addressesEqual(m.Addresses(), instInfo.addresses) { logger.Infof("machine %q has new addresses: %v", m.Id(), instInfo.addresses) if err = m.SetAddresses(instInfo.addresses); err != nil { logger.Errorf("cannot set addresses on %q: %v", m, err) } } return instInfo, err } func addressesEqual(a0, a1 []instance.Address) bool { if len(a0) != len(a1) { return false } for i := range a0 { if a0[i] != a1[i] { return false } } return true } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/instancepoller/updater_test.go������������������0000644�0000153�0000161�00000007612�12321735776�030270� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // TODO(wallyworld) - move to instancepoller_test package instancepoller import ( "errors" stdtesting "testing" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } var _ = gc.Suite(&updaterSuite{}) type updaterSuite struct { testbase.LoggingSuite } func (*updaterSuite) TestStopsWatcher(c *gc.C) { context := &testUpdaterContext{ dyingc: make(chan struct{}), } expectErr := errors.New("some error") watcher := &testMachinesWatcher{ changes: make(chan []string), err: expectErr, } done := make(chan error) go func() { done <- watchMachinesLoop(context, watcher) }() close(context.dyingc) select { case err := <-done: c.Assert(err, gc.ErrorMatches, ".*"+expectErr.Error()) case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for watchMachinesLoop to terminate") } c.Assert(watcher.stopped, jc.IsTrue) } func (*updaterSuite) TestWatchMachinesWaitsForMachinePollers(c *gc.C) { // We can't see that the machine pollers are still alive directly, // but we can make the machine's Refresh method block, // and test that watchMachinesLoop only terminates // when it unblocks. waitRefresh := make(chan struct{}) m := &testMachine{ id: "99", instanceId: "i1234", life: state.Alive, refresh: func() error { // Signal that we're in Refresh. waitRefresh <- struct{}{} // Wait to be unblocked. <-waitRefresh return nil }, } dyingc := make(chan struct{}) context := &testUpdaterContext{ dyingc: dyingc, newMachineContextFunc: func() machineContext { return &testMachineContext{ getInstanceInfo: instanceInfoGetter(c, "i1234", testAddrs, "running", nil), dyingc: dyingc, } }, getMachineFunc: func(id string) (machine, error) { c.Check(id, gc.Equals, m.id) return m, nil }, } watcher := &testMachinesWatcher{ changes: make(chan []string), } done := make(chan error) go func() { done <- watchMachinesLoop(context, watcher) }() // Send two changes; the first one should start the machineLoop; // the second should call Refresh. watcher.changes <- []string{"99"} watcher.changes <- []string{"99"} // Wait for the machineLoop to call Refresh select { case <-waitRefresh: c.Logf("poller called Refresh") case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for machine to be refreshed") } close(context.dyingc) // Wait a little while to be sure that watchMachinesLoop is // actually waiting for its machine poller to finish. select { case err := <-done: c.Fatalf("watchMachinesLoop terminated prematurely: %v", err) case <-time.After(coretesting.ShortWait): } waitRefresh <- struct{}{} select { case err := <-done: c.Assert(err, gc.IsNil) case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for watchMachinesLoop to terminate") } c.Assert(watcher.stopped, jc.IsTrue) } type testUpdaterContext struct { newMachineContextFunc func() machineContext getMachineFunc func(id string) (machine, error) dyingc chan struct{} } func (context *testUpdaterContext) newMachineContext() machineContext { return context.newMachineContextFunc() } func (context *testUpdaterContext) getMachine(id string) (machine, error) { return context.getMachineFunc(id) } func (context *testUpdaterContext) dying() <-chan struct{} { return context.dyingc } type testMachinesWatcher struct { stopped bool changes chan []string err error } func (w *testMachinesWatcher) Changes() <-chan []string { return w.changes } func (w *testMachinesWatcher) Stop() error { w.stopped = true return w.err } func (w *testMachinesWatcher) Err() error { return w.err } ����������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/instancepoller/worker.go������������������������0000644�0000153�0000161�00000002763�12321735642�027070� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instancepoller import ( "launchpad.net/tomb" "launchpad.net/juju-core/state" "launchpad.net/juju-core/worker" ) type updaterWorker struct { st *state.State tomb tomb.Tomb *aggregator observer *worker.EnvironObserver } // NewWorker returns a worker that keeps track of // the machines in the state and polls their instance // addresses and status periodically to keep them up to date. func NewWorker(st *state.State) worker.Worker { u := &updaterWorker{ st: st, } // wait for environment go func() { defer u.tomb.Done() u.tomb.Kill(u.loop()) }() return u } func (u *updaterWorker) Kill() { u.tomb.Kill(nil) } func (u *updaterWorker) Wait() error { return u.tomb.Wait() } func (u *updaterWorker) loop() (err error) { u.observer, err = worker.NewEnvironObserver(u.st) if err != nil { return err } u.aggregator = newAggregator(u.observer.Environ()) logger.Infof("instance poller received inital environment configuration") defer func() { obsErr := worker.Stop(u.observer) if err == nil { err = obsErr } }() return watchMachinesLoop(u, u.st.WatchEnvironMachines()) } func (u *updaterWorker) newMachineContext() machineContext { return u } func (u *updaterWorker) getMachine(id string) (machine, error) { return u.st.Machine(id) } func (u *updaterWorker) dying() <-chan struct{} { return u.tomb.Dying() } func (u *updaterWorker) killAll(err error) { u.tomb.Kill(err) } �������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/instancepoller/worker_test.go�������������������0000644�0000153�0000161�00000012117�12321735776�030131� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // TODO(wallyworld) - move to instancepoller_test package instancepoller import ( "fmt" "reflect" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker" ) var _ = gc.Suite(&workerSuite{}) type workerSuite struct { testing.JujuConnSuite } func (*workerSuite) instId(i int) instance.Id { return instance.Id(fmt.Sprint(i)) } func (*workerSuite) addressesForIndex(i int) []instance.Address { return []instance.Address{ instance.NewAddress(fmt.Sprintf("127.0.0.%d", i)), } } func (s *workerSuite) TestWorker(c *gc.C) { // Most functionality is already tested in detail - we // just need to test that things are wired together // correctly. s.PatchValue(&ShortPoll, 10*time.Millisecond) s.PatchValue(&LongPoll, 10*time.Millisecond) s.PatchValue(&gatherTime, 10*time.Millisecond) machines, insts := s.setupScenario(c) s.State.StartSync() w := NewWorker(s.State) defer func() { c.Assert(worker.Stop(w), gc.IsNil) }() checkInstanceInfo := func(index int, m machine, expectedStatus string) bool { isProvisioned := true status, err := m.InstanceStatus() if state.IsNotProvisionedError(err) { isProvisioned = false } else { c.Assert(err, gc.IsNil) } return reflect.DeepEqual(m.Addresses(), s.addressesForIndex(index)) && (!isProvisioned || status == expectedStatus) } // Wait for the odd numbered machines in the // first half of the machine slice to be given their // addresses and status. for a := coretesting.LongAttempt.Start(); a.Next(); { if !a.HasNext() { c.Fatalf("timed out waiting for instance info") } if machinesSatisfy(c, machines, func(i int, m *state.Machine) bool { if i < len(machines)/2 && i%2 == 1 { return checkInstanceInfo(i, m, "running") } status, err := m.InstanceStatus() if i%2 == 0 { // Even machines not provisioned yet. c.Assert(err, jc.Satisfies, state.IsNotProvisionedError) } else { c.Assert(status, gc.Equals, "") } return len(m.Addresses()) == 0 }) { break } } // Now provision the even machines in the first half and watch them get addresses. for i := 0; i < len(insts)/2; i += 2 { m := machines[i] err := m.SetProvisioned(insts[i].Id(), "nonce", nil) c.Assert(err, gc.IsNil) dummy.SetInstanceAddresses(insts[i], s.addressesForIndex(i)) dummy.SetInstanceStatus(insts[i], "running") } for a := coretesting.LongAttempt.Start(); a.Next(); { if !a.HasNext() { c.Fatalf("timed out waiting for machine instance info") } if machinesSatisfy(c, machines, func(i int, m *state.Machine) bool { if i < len(machines)/2 { return checkInstanceInfo(i, m, "running") } // Machines in second half still have no addresses, nor status. status, err := m.InstanceStatus() if i%2 == 0 { // Even machines not provisioned yet. c.Assert(err, jc.Satisfies, state.IsNotProvisionedError) } else { c.Assert(status, gc.Equals, "") } return len(m.Addresses()) == 0 }) { break } } // Provision the remaining machines and check the address and status. for i := len(insts) / 2; i < len(insts); i++ { if i%2 == 0 { m := machines[i] err := m.SetProvisioned(insts[i].Id(), "nonce", nil) c.Assert(err, gc.IsNil) } dummy.SetInstanceAddresses(insts[i], s.addressesForIndex(i)) dummy.SetInstanceStatus(insts[i], "running") } for a := coretesting.LongAttempt.Start(); a.Next(); { if !a.HasNext() { c.Fatalf("timed out waiting for machine instance info") } if machinesSatisfy(c, machines, func(i int, m *state.Machine) bool { return checkInstanceInfo(i, m, "running") }) { break } } } // TODO(rog) // - check that the environment observer is actually hooked up. // - check that the environment observer is stopped. // - check that the errors propagate correctly. func machinesSatisfy(c *gc.C, machines []*state.Machine, f func(i int, m *state.Machine) bool) bool { for i, m := range machines { err := m.Refresh() c.Assert(err, gc.IsNil) if !f(i, m) { return false } } return true } func (s *workerSuite) setupScenario(c *gc.C) ([]*state.Machine, []instance.Instance) { var machines []*state.Machine var insts []instance.Instance for i := 0; i < 10; i++ { m, err := s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, gc.IsNil) machines = append(machines, m) inst, _ := testing.AssertStartInstance(c, s.Conn.Environ, m.Id()) insts = append(insts, inst) } // Associate the odd-numbered machines with an instance. for i := 1; i < len(machines); i += 2 { m := machines[i] err := m.SetProvisioned(insts[i].Id(), "nonce", nil) c.Assert(err, gc.IsNil) } // Associate the first half of the instances with an address and status. for i := 0; i < len(machines)/2; i++ { dummy.SetInstanceAddresses(insts[i], s.addressesForIndex(i)) dummy.SetInstanceStatus(insts[i], "running") } return machines, insts } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/instancepoller/aggregate_test.go����������������0000644�0000153�0000161�00000011706�12321735776�030551� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instancepoller import ( "fmt" "sync" "sync/atomic" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/testing/testbase" ) type aggregateSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&aggregateSuite{}) type testInstance struct { instance.Instance addresses []instance.Address status string err error } var _ instance.Instance = (*testInstance)(nil) func (t *testInstance) Addresses() ([]instance.Address, error) { if t.err != nil { return nil, t.err } return t.addresses, nil } func (t *testInstance) Status() string { return t.status } type testInstanceGetter struct { // ids is set when the Instances method is called. ids []instance.Id results []instance.Instance err error counter int32 } func (i *testInstanceGetter) Instances(ids []instance.Id) (result []instance.Instance, err error) { i.ids = ids atomic.AddInt32(&i.counter, 1) return i.results, i.err } func newTestInstance(status string, addresses []string) *testInstance { thisInstance := testInstance{status: status} thisInstance.addresses = instance.NewAddresses(addresses) return &thisInstance } func (s *aggregateSuite) TestSingleRequest(c *gc.C) { testGetter := new(testInstanceGetter) instance1 := newTestInstance("foobar", []string{"127.0.0.1", "192.168.1.1"}) testGetter.results = []instance.Instance{instance1} aggregator := newAggregator(testGetter) info, err := aggregator.instanceInfo("foo") c.Assert(err, gc.IsNil) c.Assert(info, gc.DeepEquals, instanceInfo{ status: "foobar", addresses: instance1.addresses, }) c.Assert(testGetter.ids, gc.DeepEquals, []instance.Id{"foo"}) } func (s *aggregateSuite) TestMultipleResponseHandling(c *gc.C) { s.PatchValue(&gatherTime, 30*time.Millisecond) testGetter := new(testInstanceGetter) instance1 := newTestInstance("foobar", []string{"127.0.0.1", "192.168.1.1"}) testGetter.results = []instance.Instance{instance1} aggregator := newAggregator(testGetter) replyChan := make(chan instanceInfoReply) req := instanceInfoReq{ reply: replyChan, instId: instance.Id("foo"), } aggregator.reqc <- req reply := <-replyChan c.Assert(reply.err, gc.IsNil) instance2 := newTestInstance("not foobar", []string{"192.168.1.2"}) instance3 := newTestInstance("ok-ish", []string{"192.168.1.3"}) testGetter.results = []instance.Instance{instance2, instance3} var wg sync.WaitGroup checkInfo := func(id instance.Id, expectStatus string) { info, err := aggregator.instanceInfo(id) c.Check(err, gc.IsNil) c.Check(info.status, gc.Equals, expectStatus) wg.Done() } wg.Add(2) go checkInfo("foo2", "not foobar") go checkInfo("foo3", "ok-ish") wg.Wait() c.Assert(len(testGetter.ids), gc.DeepEquals, 2) } func (s *aggregateSuite) TestBatching(c *gc.C) { s.PatchValue(&gatherTime, 10*time.Millisecond) testGetter := new(testInstanceGetter) aggregator := newAggregator(testGetter) for i := 0; i < 100; i++ { testGetter.results = append(testGetter.results, newTestInstance("foobar", []string{"127.0.0.1", "192.168.1.1"})) } var wg sync.WaitGroup makeRequest := func() { _, err := aggregator.instanceInfo("foo") c.Check(err, gc.IsNil) wg.Done() } startTime := time.Now() wg.Add(100) for i := 0; i < 100; i++ { go makeRequest() time.Sleep(time.Millisecond) } wg.Wait() totalTime := time.Now().Sub(startTime) // +1 because we expect one extra call for the first request expectedMax := int((totalTime / (10 * time.Millisecond)) + 1) c.Assert(testGetter.counter, jc.LessThan, expectedMax+1) c.Assert(testGetter.counter, jc.GreaterThan, 10) } func (s *aggregateSuite) TestError(c *gc.C) { testGetter := new(testInstanceGetter) ourError := fmt.Errorf("Some error") testGetter.err = ourError aggregator := newAggregator(testGetter) _, err := aggregator.instanceInfo("foo") c.Assert(err, gc.Equals, ourError) } func (s *aggregateSuite) TestPartialErrResponse(c *gc.C) { testGetter := new(testInstanceGetter) testGetter.err = environs.ErrPartialInstances testGetter.results = []instance.Instance{nil} aggregator := newAggregator(testGetter) _, err := aggregator.instanceInfo("foo") c.Assert(err, gc.DeepEquals, errors.NotFoundf("instance foo")) } func (s *aggregateSuite) TestAddressesError(c *gc.C) { testGetter := new(testInstanceGetter) instance1 := newTestInstance("foobar", []string{"127.0.0.1", "192.168.1.1"}) ourError := fmt.Errorf("gotcha") instance1.err = ourError testGetter.results = []instance.Instance{instance1} aggregator := newAggregator(testGetter) _, err := aggregator.instanceInfo("foo") c.Assert(err, gc.Equals, ourError) } func (s *aggregateSuite) TestKillAndWait(c *gc.C) { testGetter := new(testInstanceGetter) aggregator := newAggregator(testGetter) aggregator.Kill() err := aggregator.Wait() c.Assert(err, gc.IsNil) } ����������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/stringsworker_test.go���������������������������0000644�0000153�0000161�00000021544�12321735642�026515� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker_test import ( "fmt" "sync" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/tomb" apiWatcher "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/state/watcher" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/worker" ) type stringsWorkerSuite struct { testbase.LoggingSuite worker worker.Worker actor *stringsHandler } var _ = gc.Suite(&stringsWorkerSuite{}) func (s *stringsWorkerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.actor = &stringsHandler{ actions: nil, handled: make(chan []string, 1), watcher: &testStringsWatcher{ changes: make(chan []string), }, } s.worker = worker.NewStringsWorker(s.actor) } func (s *stringsWorkerSuite) TearDownTest(c *gc.C) { s.stopWorker(c) s.LoggingSuite.TearDownTest(c) } type stringsHandler struct { actions []string mu sync.Mutex // Signal handled when we get a handle() call handled chan []string setupError error teardownError error handlerError error watcher *testStringsWatcher } var _ worker.StringsWatchHandler = (*stringsHandler)(nil) func (sh *stringsHandler) SetUp() (apiWatcher.StringsWatcher, error) { sh.mu.Lock() defer sh.mu.Unlock() sh.actions = append(sh.actions, "setup") if sh.watcher == nil { return nil, sh.setupError } return sh.watcher, sh.setupError } func (sh *stringsHandler) TearDown() error { sh.mu.Lock() defer sh.mu.Unlock() sh.actions = append(sh.actions, "teardown") if sh.handled != nil { close(sh.handled) } return sh.teardownError } func (sh *stringsHandler) Handle(changes []string) error { sh.mu.Lock() defer sh.mu.Unlock() sh.actions = append(sh.actions, "handler") if sh.handled != nil { // Unlock while we are waiting for the send sh.mu.Unlock() sh.handled <- changes sh.mu.Lock() } return sh.handlerError } func (sh *stringsHandler) CheckActions(c *gc.C, actions ...string) { sh.mu.Lock() defer sh.mu.Unlock() c.Check(sh.actions, gc.DeepEquals, actions) } // During teardown we try to stop the worker, but don't hang the test suite if // Stop never returns func (s *stringsWorkerSuite) stopWorker(c *gc.C) { if s.worker == nil { return } done := make(chan error) go func() { done <- worker.Stop(s.worker) }() err := waitForTimeout(c, done, coretesting.LongWait) c.Check(err, gc.IsNil) s.actor = nil s.worker = nil } type testStringsWatcher struct { mu sync.Mutex changes chan []string stopped bool stopError error } var _ apiWatcher.StringsWatcher = (*testStringsWatcher)(nil) func (tsw *testStringsWatcher) Changes() <-chan []string { return tsw.changes } func (tsw *testStringsWatcher) Err() error { return tsw.stopError } func (tsw *testStringsWatcher) Stop() error { tsw.mu.Lock() defer tsw.mu.Unlock() if !tsw.stopped { close(tsw.changes) } tsw.stopped = true return tsw.stopError } func (tsw *testStringsWatcher) SetStopError(err error) { tsw.mu.Lock() tsw.stopError = err tsw.mu.Unlock() } func (tsw *testStringsWatcher) TriggerChange(c *gc.C, changes []string) { select { case tsw.changes <- changes: case <-time.After(coretesting.LongWait): c.Errorf("timed out trying to trigger a change") } } func waitForHandledStrings(c *gc.C, handled chan []string, expect []string) { select { case changes := <-handled: c.Assert(changes, gc.DeepEquals, expect) case <-time.After(coretesting.LongWait): c.Errorf("handled failed to signal after %s", coretesting.LongWait) } } func (s *stringsWorkerSuite) TestKill(c *gc.C) { s.worker.Kill() err := waitShort(c, s.worker) c.Assert(err, gc.IsNil) } func (s *stringsWorkerSuite) TestStop(c *gc.C) { err := worker.Stop(s.worker) c.Assert(err, gc.IsNil) // After stop, Wait should return right away err = waitShort(c, s.worker) c.Assert(err, gc.IsNil) } func (s *stringsWorkerSuite) TestWait(c *gc.C) { done := make(chan error) go func() { done <- s.worker.Wait() }() // Wait should not return until we've killed the worker select { case err := <-done: c.Errorf("Wait() didn't wait until we stopped it: %v", err) case <-time.After(coretesting.ShortWait): } s.worker.Kill() err := waitForTimeout(c, done, coretesting.LongWait) c.Assert(err, gc.IsNil) } func (s *stringsWorkerSuite) TestCallSetUpAndTearDown(c *gc.C) { // After calling NewStringsWorker, we should have called setup s.actor.CheckActions(c, "setup") // If we kill the worker, it should notice, and call teardown s.worker.Kill() err := waitShort(c, s.worker) c.Check(err, gc.IsNil) s.actor.CheckActions(c, "setup", "teardown") c.Check(s.actor.watcher.stopped, jc.IsTrue) } func (s *stringsWorkerSuite) TestChangesTriggerHandler(c *gc.C) { s.actor.CheckActions(c, "setup") s.actor.watcher.TriggerChange(c, []string{"aa", "bb"}) waitForHandledStrings(c, s.actor.handled, []string{"aa", "bb"}) s.actor.CheckActions(c, "setup", "handler") s.actor.watcher.TriggerChange(c, []string{"cc", "dd"}) waitForHandledStrings(c, s.actor.handled, []string{"cc", "dd"}) s.actor.watcher.TriggerChange(c, []string{"ee", "ff"}) waitForHandledStrings(c, s.actor.handled, []string{"ee", "ff"}) s.actor.CheckActions(c, "setup", "handler", "handler", "handler") c.Assert(worker.Stop(s.worker), gc.IsNil) s.actor.CheckActions(c, "setup", "handler", "handler", "handler", "teardown") } func (s *stringsWorkerSuite) TestSetUpFailureStopsWithTearDown(c *gc.C) { // Stop the worker and SetUp again, this time with an error s.stopWorker(c) actor := &stringsHandler{ actions: nil, handled: make(chan []string, 1), setupError: fmt.Errorf("my special error"), watcher: &testStringsWatcher{ changes: make(chan []string), }, } w := worker.NewStringsWorker(actor) err := waitShort(c, w) c.Check(err, gc.ErrorMatches, "my special error") // TearDown is not called on SetUp error. actor.CheckActions(c, "setup") c.Check(actor.watcher.stopped, jc.IsTrue) } func (s *stringsWorkerSuite) TestWatcherStopFailurePropagates(c *gc.C) { s.actor.watcher.SetStopError(fmt.Errorf("error while stopping watcher")) s.worker.Kill() c.Assert(s.worker.Wait(), gc.ErrorMatches, "error while stopping watcher") // We've already stopped the worker, don't let teardown notice the // worker is in an error state s.worker = nil } func (s *stringsWorkerSuite) TestCleanRunNoticesTearDownError(c *gc.C) { s.actor.teardownError = fmt.Errorf("failed to tear down watcher") s.worker.Kill() c.Assert(s.worker.Wait(), gc.ErrorMatches, "failed to tear down watcher") s.worker = nil } func (s *stringsWorkerSuite) TestHandleErrorStopsWorkerAndWatcher(c *gc.C) { s.stopWorker(c) actor := &stringsHandler{ actions: nil, handled: make(chan []string, 1), handlerError: fmt.Errorf("my handling error"), watcher: &testStringsWatcher{ changes: make(chan []string), }, } w := worker.NewStringsWorker(actor) actor.watcher.TriggerChange(c, []string{"aa", "bb"}) waitForHandledStrings(c, actor.handled, []string{"aa", "bb"}) err := waitShort(c, w) c.Check(err, gc.ErrorMatches, "my handling error") actor.CheckActions(c, "setup", "handler", "teardown") c.Check(actor.watcher.stopped, jc.IsTrue) } func (s *stringsWorkerSuite) TestNoticesStoppedWatcher(c *gc.C) { // The default closedHandler doesn't panic if you have a genuine error // (because it assumes you want to propagate a real error and then // restart s.actor.watcher.SetStopError(fmt.Errorf("Stopped Watcher")) s.actor.watcher.Stop() err := waitShort(c, s.worker) c.Check(err, gc.ErrorMatches, "Stopped Watcher") s.actor.CheckActions(c, "setup", "teardown") // Worker is stopped, don't fail TearDownTest s.worker = nil } func (s *stringsWorkerSuite) TestErrorsOnStillAliveButClosedChannel(c *gc.C) { foundErr := fmt.Errorf("did not get an error") triggeredHandler := func(errer watcher.Errer) error { foundErr = errer.Err() return foundErr } worker.SetMustErr(triggeredHandler) s.actor.watcher.SetStopError(tomb.ErrStillAlive) s.actor.watcher.Stop() err := waitShort(c, s.worker) c.Check(foundErr, gc.Equals, tomb.ErrStillAlive) // ErrStillAlive is trapped by the Stop logic and gets turned into a // 'nil' when stopping. However TestDefaultClosedHandler can assert // that it would have triggered a panic. c.Check(err, gc.IsNil) s.actor.CheckActions(c, "setup", "teardown") // Worker is stopped, don't fail TearDownTest s.worker = nil } func (s *stringsWorkerSuite) TestErrorsOnClosedChannel(c *gc.C) { foundErr := fmt.Errorf("did not get an error") triggeredHandler := func(errer watcher.Errer) error { foundErr = errer.Err() return foundErr } worker.SetMustErr(triggeredHandler) s.actor.watcher.Stop() err := waitShort(c, s.worker) // If the foundErr is nil, we would have panic-ed (see TestDefaultClosedHandler) c.Check(foundErr, gc.IsNil) c.Check(err, gc.IsNil) s.actor.CheckActions(c, "setup", "teardown") } ������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/notifyworker_test.go����������������������������0000644�0000153�0000161�00000022525�12321735642�026334� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker_test import ( "fmt" "sync" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/tomb" apiWatcher "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/state/watcher" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/worker" ) type notifyWorkerSuite struct { testbase.LoggingSuite worker worker.Worker actor *notifyHandler } var _ = gc.Suite(&notifyWorkerSuite{}) func (s *notifyWorkerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.actor = &notifyHandler{ actions: nil, handled: make(chan struct{}, 1), watcher: &testNotifyWatcher{ changes: make(chan struct{}), }, } s.worker = worker.NewNotifyWorker(s.actor) } func (s *notifyWorkerSuite) TearDownTest(c *gc.C) { worker.SetMustErr(nil) s.stopWorker(c) s.LoggingSuite.TearDownTest(c) } type notifyHandler struct { actions []string mu sync.Mutex // Signal handled when we get a handle() call handled chan struct{} setupError error teardownError error handlerError error watcher *testNotifyWatcher } var _ worker.NotifyWatchHandler = (*notifyHandler)(nil) func (nh *notifyHandler) SetUp() (apiWatcher.NotifyWatcher, error) { nh.mu.Lock() defer nh.mu.Unlock() nh.actions = append(nh.actions, "setup") if nh.watcher == nil { return nil, nh.setupError } return nh.watcher, nh.setupError } func (nh *notifyHandler) TearDown() error { nh.mu.Lock() defer nh.mu.Unlock() nh.actions = append(nh.actions, "teardown") if nh.handled != nil { close(nh.handled) } return nh.teardownError } func (nh *notifyHandler) Handle() error { nh.mu.Lock() defer nh.mu.Unlock() nh.actions = append(nh.actions, "handler") if nh.handled != nil { // Unlock while we are waiting for the send nh.mu.Unlock() nh.handled <- struct{}{} nh.mu.Lock() } return nh.handlerError } func (nh *notifyHandler) CheckActions(c *gc.C, actions ...string) { nh.mu.Lock() defer nh.mu.Unlock() c.Check(nh.actions, gc.DeepEquals, actions) } // During teardown we try to stop the worker, but don't hang the test suite if // Stop never returns func (s *notifyWorkerSuite) stopWorker(c *gc.C) { if s.worker == nil { return } done := make(chan error) go func() { done <- worker.Stop(s.worker) }() err := waitForTimeout(c, done, coretesting.LongWait) c.Check(err, gc.IsNil) s.actor = nil s.worker = nil } type testNotifyWatcher struct { mu sync.Mutex changes chan struct{} stopped bool stopError error } var _ apiWatcher.NotifyWatcher = (*testNotifyWatcher)(nil) func (tnw *testNotifyWatcher) Changes() <-chan struct{} { return tnw.changes } func (tnw *testNotifyWatcher) Err() error { return tnw.stopError } func (tnw *testNotifyWatcher) Stop() error { tnw.mu.Lock() defer tnw.mu.Unlock() if !tnw.stopped { close(tnw.changes) } tnw.stopped = true return tnw.stopError } func (tnw *testNotifyWatcher) SetStopError(err error) { tnw.mu.Lock() tnw.stopError = err tnw.mu.Unlock() } func (tnw *testNotifyWatcher) TriggerChange(c *gc.C) { select { case tnw.changes <- struct{}{}: case <-time.After(coretesting.LongWait): c.Errorf("timed out trying to trigger a change") } } func waitForTimeout(c *gc.C, ch <-chan error, timeout time.Duration) error { select { case err := <-ch: return err case <-time.After(timeout): c.Errorf("timed out waiting to receive a change after %s", timeout) } return nil } func waitShort(c *gc.C, w worker.Worker) error { done := make(chan error) go func() { done <- w.Wait() }() return waitForTimeout(c, done, coretesting.ShortWait) } func waitForHandledNotify(c *gc.C, handled chan struct{}) { select { case <-handled: case <-time.After(coretesting.LongWait): c.Errorf("handled failed to signal after %s", coretesting.LongWait) } } func (s *notifyWorkerSuite) TestKill(c *gc.C) { s.worker.Kill() err := waitShort(c, s.worker) c.Assert(err, gc.IsNil) } func (s *notifyWorkerSuite) TestStop(c *gc.C) { err := worker.Stop(s.worker) c.Assert(err, gc.IsNil) // After stop, Wait should return right away err = waitShort(c, s.worker) c.Assert(err, gc.IsNil) } func (s *notifyWorkerSuite) TestWait(c *gc.C) { done := make(chan error) go func() { done <- s.worker.Wait() }() // Wait should not return until we've killed the worker select { case err := <-done: c.Errorf("Wait() didn't wait until we stopped it: %v", err) case <-time.After(coretesting.ShortWait): } s.worker.Kill() err := waitForTimeout(c, done, coretesting.LongWait) c.Assert(err, gc.IsNil) } func (s *notifyWorkerSuite) TestCallSetUpAndTearDown(c *gc.C) { // After calling NewNotifyWorker, we should have called setup s.actor.CheckActions(c, "setup") // If we kill the worker, it should notice, and call teardown s.worker.Kill() err := waitShort(c, s.worker) c.Check(err, gc.IsNil) s.actor.CheckActions(c, "setup", "teardown") c.Check(s.actor.watcher.stopped, jc.IsTrue) } func (s *notifyWorkerSuite) TestChangesTriggerHandler(c *gc.C) { s.actor.CheckActions(c, "setup") s.actor.watcher.TriggerChange(c) waitForHandledNotify(c, s.actor.handled) s.actor.CheckActions(c, "setup", "handler") s.actor.watcher.TriggerChange(c) waitForHandledNotify(c, s.actor.handled) s.actor.watcher.TriggerChange(c) waitForHandledNotify(c, s.actor.handled) s.actor.CheckActions(c, "setup", "handler", "handler", "handler") c.Assert(worker.Stop(s.worker), gc.IsNil) s.actor.CheckActions(c, "setup", "handler", "handler", "handler", "teardown") } func (s *notifyWorkerSuite) TestSetUpFailureStopsWithTearDown(c *gc.C) { // Stop the worker and SetUp again, this time with an error s.stopWorker(c) actor := &notifyHandler{ actions: nil, handled: make(chan struct{}, 1), setupError: fmt.Errorf("my special error"), watcher: &testNotifyWatcher{ changes: make(chan struct{}), }, } w := worker.NewNotifyWorker(actor) err := waitShort(c, w) c.Check(err, gc.ErrorMatches, "my special error") // TearDown is not called on SetUp error. actor.CheckActions(c, "setup") c.Check(actor.watcher.stopped, jc.IsTrue) } func (s *notifyWorkerSuite) TestWatcherStopFailurePropagates(c *gc.C) { s.actor.watcher.SetStopError(fmt.Errorf("error while stopping watcher")) s.worker.Kill() c.Assert(s.worker.Wait(), gc.ErrorMatches, "error while stopping watcher") // We've already stopped the worker, don't let teardown notice the // worker is in an error state s.worker = nil } func (s *notifyWorkerSuite) TestCleanRunNoticesTearDownError(c *gc.C) { s.actor.teardownError = fmt.Errorf("failed to tear down watcher") s.worker.Kill() c.Assert(s.worker.Wait(), gc.ErrorMatches, "failed to tear down watcher") s.worker = nil } func (s *notifyWorkerSuite) TestHandleErrorStopsWorkerAndWatcher(c *gc.C) { s.stopWorker(c) actor := &notifyHandler{ actions: nil, handled: make(chan struct{}, 1), handlerError: fmt.Errorf("my handling error"), watcher: &testNotifyWatcher{ changes: make(chan struct{}), }, } w := worker.NewNotifyWorker(actor) actor.watcher.TriggerChange(c) waitForHandledNotify(c, actor.handled) err := waitShort(c, w) c.Check(err, gc.ErrorMatches, "my handling error") actor.CheckActions(c, "setup", "handler", "teardown") c.Check(actor.watcher.stopped, jc.IsTrue) } func (s *notifyWorkerSuite) TestNoticesStoppedWatcher(c *gc.C) { // The default closedHandler doesn't panic if you have a genuine error // (because it assumes you want to propagate a real error and then // restart s.actor.watcher.SetStopError(fmt.Errorf("Stopped Watcher")) s.actor.watcher.Stop() err := waitShort(c, s.worker) c.Check(err, gc.ErrorMatches, "Stopped Watcher") s.actor.CheckActions(c, "setup", "teardown") // Worker is stopped, don't fail TearDownTest s.worker = nil } func noopHandler(watcher.Errer) error { return nil } type CannedErrer struct { err error } func (c CannedErrer) Err() error { return c.err } func (s *notifyWorkerSuite) TestDefaultClosedHandler(c *gc.C) { // Roundabout check for function equality. // Is this test really worth it? c.Assert(fmt.Sprintf("%p", worker.MustErr()), gc.Equals, fmt.Sprintf("%p", watcher.MustErr)) } func (s *notifyWorkerSuite) TestErrorsOnStillAliveButClosedChannel(c *gc.C) { foundErr := fmt.Errorf("did not get an error") triggeredHandler := func(errer watcher.Errer) error { foundErr = errer.Err() return foundErr } worker.SetMustErr(triggeredHandler) s.actor.watcher.SetStopError(tomb.ErrStillAlive) s.actor.watcher.Stop() err := waitShort(c, s.worker) c.Check(foundErr, gc.Equals, tomb.ErrStillAlive) // ErrStillAlive is trapped by the Stop logic and gets turned into a // 'nil' when stopping. However TestDefaultClosedHandler can assert // that it would have triggered a panic. c.Check(err, gc.IsNil) s.actor.CheckActions(c, "setup", "teardown") // Worker is stopped, don't fail TearDownTest s.worker = nil } func (s *notifyWorkerSuite) TestErrorsOnClosedChannel(c *gc.C) { foundErr := fmt.Errorf("did not get an error") triggeredHandler := func(errer watcher.Errer) error { foundErr = errer.Err() return foundErr } worker.SetMustErr(triggeredHandler) s.actor.watcher.Stop() err := waitShort(c, s.worker) // If the foundErr is nil, we would have panic-ed (see TestDefaultClosedHandler) c.Check(foundErr, gc.IsNil) c.Check(err, gc.IsNil) s.actor.CheckActions(c, "setup", "teardown") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/peergrouper/������������������������������������0000755�0000153�0000161�00000000000�12321736000�024522� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/peergrouper/mock_test.go������������������������0000644�0000153�0000161�00000027467�12321735776�027104� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package peergrouper import ( "encoding/json" "fmt" "net" "path" "reflect" "strconv" "sync" "launchpad.net/tomb" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/replicaset" "launchpad.net/juju-core/state" "launchpad.net/juju-core/utils/voyeur" "launchpad.net/juju-core/worker" ) // This file holds helper functions for mocking pieces of State and replicaset // that we don't want to directly depend on in unit tests. type fakeState struct { mu sync.Mutex machines map[string]*fakeMachine stateServers voyeur.Value // of *state.StateServerInfo session *fakeMongoSession check func(st *fakeState) error } var ( _ stateInterface = (*fakeState)(nil) _ stateMachine = (*fakeMachine)(nil) _ mongoSession = (*fakeMongoSession)(nil) ) type errorPattern struct { pattern string errFunc func() error } var ( errorsMutex sync.Mutex errorPatterns []errorPattern ) // setErrorFor causes the given error to be returned // from any mock call that matches the given // string, which may contain wildcards as // in path.Match. // // The standard form for errors is: // Type.Function <arg>... // See individual functions for details. func setErrorFor(what string, err error) { setErrorFuncFor(what, func() error { return err }) } // setErrorFuncFor causes the given function // to be invoked to return the error for the // given pattern. func setErrorFuncFor(what string, errFunc func() error) { errorsMutex.Lock() defer errorsMutex.Unlock() errorPatterns = append(errorPatterns, errorPattern{ pattern: what, errFunc: errFunc, }) } // errorFor concatenates the call name // with all the args, space separated, // and returns any error registered with // setErrorFor that matches the resulting string. func errorFor(name string, args ...interface{}) error { errorsMutex.Lock() s := name for _, arg := range args { s += " " + fmt.Sprint(arg) } f := func() error { return nil } for _, pattern := range errorPatterns { if ok, _ := path.Match(pattern.pattern, s); ok { f = pattern.errFunc break } } errorsMutex.Unlock() err := f() logger.Errorf("errorFor %q -> %v", s, err) return err } func resetErrors() { errorsMutex.Lock() defer errorsMutex.Unlock() errorPatterns = errorPatterns[:0] } func newFakeState() *fakeState { st := &fakeState{ machines: make(map[string]*fakeMachine), } st.session = newFakeMongoSession(st) st.stateServers.Set(&state.StateServerInfo{}) return st } func (st *fakeState) MongoSession() mongoSession { return st.session } func (st *fakeState) checkInvariants() { if st.check == nil { return } if err := st.check(st); err != nil { // Force a panic, otherwise we can deadlock // when called from within the worker. go panic(err) select {} } } // checkInvariants checks that all the expected invariants // in the state hold true. Currently we check that: // - total number of votes is odd. // - member voting status implies that machine has vote. func checkInvariants(st *fakeState) error { members := st.session.members.Get().([]replicaset.Member) voteCount := 0 for _, m := range members { votes := 1 if m.Votes != nil { votes = *m.Votes } voteCount += votes if id, ok := m.Tags["juju-machine-id"]; ok { if votes > 0 { m := st.machine(id) if m == nil { return fmt.Errorf("voting member with machine id %q has no associated Machine", id) } if !m.HasVote() { return fmt.Errorf("machine %q should be marked as having the vote, but does not", id) } } } } if voteCount%2 != 1 { return fmt.Errorf("total vote count is not odd (got %d)", voteCount) } return nil } type invariantChecker interface { checkInvariants() } // machine is similar to Machine except that // it bypasses the error mocking machinery. // It returns nil if there is no machine with the // given id. func (st *fakeState) machine(id string) *fakeMachine { st.mu.Lock() defer st.mu.Unlock() return st.machines[id] } func (st *fakeState) Machine(id string) (stateMachine, error) { if err := errorFor("State.Machine", id); err != nil { return nil, err } if m := st.machine(id); m != nil { return m, nil } return nil, errors.NotFoundf("machine %s", id) } func (st *fakeState) addMachine(id string, wantsVote bool) *fakeMachine { st.mu.Lock() defer st.mu.Unlock() logger.Infof("fakeState.addMachine %q", id) if st.machines[id] != nil { panic(fmt.Errorf("id %q already used", id)) } m := &fakeMachine{ checker: st, doc: machineDoc{ id: id, wantsVote: wantsVote, }, } st.machines[id] = m m.val.Set(m.doc) return m } func (st *fakeState) removeMachine(id string) { st.mu.Lock() defer st.mu.Unlock() if st.machines[id] == nil { panic(fmt.Errorf("removing non-existent machine %q", id)) } delete(st.machines, id) } func (st *fakeState) setStateServers(ids ...string) { st.stateServers.Set(&state.StateServerInfo{ MachineIds: ids, }) } func (st *fakeState) StateServerInfo() (*state.StateServerInfo, error) { if err := errorFor("State.StateServerInfo"); err != nil { return nil, err } return deepCopy(st.stateServers.Get()).(*state.StateServerInfo), nil } func (st *fakeState) WatchStateServerInfo() state.NotifyWatcher { return WatchValue(&st.stateServers) } type fakeMachine struct { mu sync.Mutex val voyeur.Value // of machineDoc doc machineDoc checker invariantChecker } type machineDoc struct { id string wantsVote bool hasVote bool instanceId instance.Id mongoHostPorts []instance.HostPort apiHostPorts []instance.HostPort } func (m *fakeMachine) Refresh() error { if err := errorFor("Machine.Refresh", m.doc.id); err != nil { return err } m.doc = m.val.Get().(machineDoc) return nil } func (m *fakeMachine) GoString() string { return fmt.Sprintf("&fakeMachine{%#v}", m.doc) } func (m *fakeMachine) Id() string { return m.doc.id } func (m *fakeMachine) InstanceId() (instance.Id, error) { if err := errorFor("Machine.InstanceId", m.doc.id); err != nil { return "", err } return m.doc.instanceId, nil } func (m *fakeMachine) Watch() state.NotifyWatcher { return WatchValue(&m.val) } func (m *fakeMachine) WantsVote() bool { return m.doc.wantsVote } func (m *fakeMachine) HasVote() bool { return m.doc.hasVote } func (m *fakeMachine) MongoHostPorts() []instance.HostPort { return m.doc.mongoHostPorts } func (m *fakeMachine) APIHostPorts() []instance.HostPort { return m.doc.apiHostPorts } // mutate atomically changes the machineDoc of // the receiver by mutating it with the provided function. func (m *fakeMachine) mutate(f func(*machineDoc)) { m.mu.Lock() doc := m.val.Get().(machineDoc) f(&doc) m.val.Set(doc) f(&m.doc) m.mu.Unlock() m.checker.checkInvariants() } func (m *fakeMachine) setStateHostPort(hostPort string) { var mongoHostPorts []instance.HostPort if hostPort != "" { host, portStr, err := net.SplitHostPort(hostPort) if err != nil { panic(err) } port, err := strconv.Atoi(portStr) if err != nil { panic(err) } mongoHostPorts = instance.AddressesWithPort(instance.NewAddresses([]string{host}), port) mongoHostPorts[0].NetworkScope = instance.NetworkCloudLocal } m.mutate(func(doc *machineDoc) { doc.mongoHostPorts = mongoHostPorts }) } func (m *fakeMachine) setMongoHostPorts(hostPorts []instance.HostPort) { m.mutate(func(doc *machineDoc) { doc.mongoHostPorts = hostPorts }) } func (m *fakeMachine) setAPIHostPorts(hostPorts []instance.HostPort) { m.mutate(func(doc *machineDoc) { doc.apiHostPorts = hostPorts }) } func (m *fakeMachine) setInstanceId(instanceId instance.Id) { m.mutate(func(doc *machineDoc) { doc.instanceId = instanceId }) } // SetHasVote implements stateMachine.SetHasVote. func (m *fakeMachine) SetHasVote(hasVote bool) error { if err := errorFor("Machine.SetHasVote", m.doc.id, hasVote); err != nil { return err } m.mutate(func(doc *machineDoc) { doc.hasVote = hasVote }) return nil } func (m *fakeMachine) setWantsVote(wantsVote bool) { m.mutate(func(doc *machineDoc) { doc.wantsVote = wantsVote }) } type fakeMongoSession struct { // If InstantlyReady is true, replica status of // all members will be instantly reported as ready. InstantlyReady bool checker invariantChecker members voyeur.Value // of []replicaset.Member status voyeur.Value // of *replicaset.Status } // newFakeMongoSession returns a mock implementation of mongoSession. func newFakeMongoSession(checker invariantChecker) *fakeMongoSession { s := new(fakeMongoSession) s.checker = checker s.members.Set([]replicaset.Member(nil)) s.status.Set(&replicaset.Status{}) return s } // CurrentMembers implements mongoSession.CurrentMembers. func (session *fakeMongoSession) CurrentMembers() ([]replicaset.Member, error) { if err := errorFor("Session.CurrentMembers"); err != nil { return nil, err } return deepCopy(session.members.Get()).([]replicaset.Member), nil } // CurrentStatus implements mongoSession.CurrentStatus. func (session *fakeMongoSession) CurrentStatus() (*replicaset.Status, error) { if err := errorFor("Session.CurrentStatus"); err != nil { return nil, err } return deepCopy(session.status.Get()).(*replicaset.Status), nil } // setStatus sets the status of the current members of the session. func (session *fakeMongoSession) setStatus(members []replicaset.MemberStatus) { session.status.Set(deepCopy(&replicaset.Status{ Members: members, })) } // Set implements mongoSession.Set func (session *fakeMongoSession) Set(members []replicaset.Member) error { if err := errorFor("Session.Set"); err != nil { logger.Infof("not setting replicaset members to %#v", members) return err } logger.Infof("setting replicaset members to %#v", members) session.members.Set(deepCopy(members)) if session.InstantlyReady { statuses := make([]replicaset.MemberStatus, len(members)) for i, m := range members { statuses[i] = replicaset.MemberStatus{ Id: m.Id, Address: m.Address, Healthy: true, State: replicaset.SecondaryState, } if i == 0 { statuses[i].State = replicaset.PrimaryState } } session.setStatus(statuses) } session.checker.checkInvariants() return nil } // deepCopy makes a deep copy of any type by marshalling // it as JSON, then unmarshalling it. func deepCopy(x interface{}) interface{} { v := reflect.ValueOf(x) data, err := json.Marshal(x) if err != nil { panic(fmt.Errorf("cannot marshal %#v: %v", x, err)) } newv := reflect.New(v.Type()) if err := json.Unmarshal(data, newv.Interface()); err != nil { panic(fmt.Errorf("cannot unmarshal %q into %s", data, newv.Type())) } // sanity check newx := newv.Elem().Interface() if !reflect.DeepEqual(newx, x) { panic(fmt.Errorf("value not deep-copied correctly")) } return newx } type notifier struct { tomb tomb.Tomb w *voyeur.Watcher changes chan struct{} } // WatchValue returns a NotifyWatcher that triggers // when the given value changes. Its Wait and Err methods // never return a non-nil error. func WatchValue(val *voyeur.Value) state.NotifyWatcher { n := &notifier{ w: val.Watch(), changes: make(chan struct{}), } go n.loop() return n } func (n *notifier) loop() { defer n.tomb.Done() for n.w.Next() { select { case n.changes <- struct{}{}: case <-n.tomb.Dying(): } } } // Changes returns a channel that sends a value when the value changes. // The value itself can be retrieved by calling the value's Get method. func (n *notifier) Changes() <-chan struct{} { return n.changes } // Kill stops the notifier but does not wait for it to finish. func (n *notifier) Kill() { n.tomb.Kill(nil) n.w.Close() } func (n *notifier) Err() error { return n.tomb.Err() } // Wait waits for the notifier to finish. It always returns nil. func (n *notifier) Wait() error { return n.tomb.Wait() } func (n *notifier) Stop() error { return worker.Stop(n) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/peergrouper/publish.go��������������������������0000644�0000153�0000161�00000001030�12321735642�026524� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package peergrouper import ( "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" ) type publisher struct { st *state.State } func newPublisher(st *state.State) *publisher { return &publisher{ st: st, } } func (pub *publisher) publishAPIServers(apiServers [][]instance.HostPort, instanceIds []instance.Id) error { // TODO(rog) publish instanceIds in environment storage. return pub.st.SetAPIHostPorts(apiServers) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/peergrouper/shim.go�����������������������������0000644�0000153�0000161�00000002737�12321735642�026035� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package peergrouper import ( "labix.org/v2/mgo" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/replicaset" "launchpad.net/juju-core/state" ) // This file holds code that translates from State // to the interface expected internally by the // worker. type stateShim struct { *state.State mongoPort int apiPort int } func (s *stateShim) Machine(id string) (stateMachine, error) { m, err := s.State.Machine(id) if err != nil { return nil, err } return &machineShim{ Machine: m, mongoPort: s.mongoPort, apiPort: s.apiPort, }, nil } func (s *stateShim) MongoSession() mongoSession { return mongoSessionShim{s.State.MongoSession()} } func (m *machineShim) APIHostPorts() []instance.HostPort { return instance.AddressesWithPort(m.Addresses(), m.apiPort) } func (m *machineShim) MongoHostPorts() []instance.HostPort { return instance.AddressesWithPort(m.Addresses(), m.mongoPort) } type machineShim struct { *state.Machine mongoPort int apiPort int } type mongoSessionShim struct { session *mgo.Session } func (s mongoSessionShim) CurrentStatus() (*replicaset.Status, error) { return replicaset.CurrentStatus(s.session) } func (s mongoSessionShim) CurrentMembers() ([]replicaset.Member, error) { return replicaset.CurrentMembers(s.session) } func (s mongoSessionShim) Set(members []replicaset.Member) error { return replicaset.Set(s.session, members) } ���������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/peergrouper/desired.go��������������������������0000644�0000153�0000161�00000022217�12321735642�026507� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package peergrouper import ( "fmt" "sort" "github.com/juju/loggo" "launchpad.net/juju-core/replicaset" ) var logger = loggo.GetLogger("juju.worker.peergrouper") // peerGroupInfo holds information that may contribute to // a peer group. type peerGroupInfo struct { machines map[string]*machine // id -> machine statuses []replicaset.MemberStatus members []replicaset.Member } // desiredPeerGroup returns the mongo peer group according to the given // servers and a map with an element for each machine in info.machines // specifying whether that machine has been configured as voting. It may // return (nil, nil, nil) if the current group is already correct. func desiredPeerGroup(info *peerGroupInfo) ([]replicaset.Member, map[*machine]bool, error) { if len(info.members) == 0 { return nil, nil, fmt.Errorf("current member set is empty") } changed := false members, extra, maxId := info.membersMap() logger.Debugf("calculating desired peer group") logger.Debugf("members: %#v", members) logger.Debugf("extra: %#v", extra) logger.Debugf("maxId: %v", maxId) // We may find extra peer group members if the machines // have been removed or their state server status removed. // This should only happen if they had been set to non-voting // before removal, in which case we want to remove it // from the members list. If we find a member that's still configured // to vote, it's an error. // TODO There are some other possibilities // for what to do in that case. // 1) leave them untouched, but deal // with others as usual "i didn't see that bit" // 2) leave them untouched, deal with others, // but make sure the extras aren't eligible to // be primary. // 3) remove them "get rid of bad rubbish" // 4) do nothing "nothing to see here" for _, member := range extra { if member.Votes == nil || *member.Votes > 0 { return nil, nil, fmt.Errorf("voting non-machine member %#v found in peer group", member) } changed = true } toRemoveVote, toAddVote, toKeep := possiblePeerGroupChanges(info, members) // Set up initial record of machine votes. Any changes after // this will trigger a peer group election. machineVoting := make(map[*machine]bool) for _, m := range info.machines { if member := members[m]; member != nil && isVotingMember(member) { machineVoting[m] = true } } setVoting := func(m *machine, voting bool) { setMemberVoting(members[m], voting) machineVoting[m] = voting changed = true } adjustVotes(toRemoveVote, toAddVote, setVoting) addNewMembers(members, toKeep, maxId, setVoting) if updateAddresses(members, info.machines) { changed = true } if !changed { return nil, nil, nil } var memberSet []replicaset.Member for _, member := range members { memberSet = append(memberSet, *member) } return memberSet, machineVoting, nil } func isVotingMember(member *replicaset.Member) bool { return member.Votes == nil || *member.Votes > 0 } // possiblePeerGroupChanges returns a set of slices // classifying all the existing machines according to // how their vote might move. // toRemoveVote holds machines whose vote should // be removed; toAddVote holds machines which are // ready to vote; toKeep holds machines with no desired // change to their voting status (this includes machines // that are not yet represented in the peer group). func possiblePeerGroupChanges( info *peerGroupInfo, members map[*machine]*replicaset.Member, ) (toRemoveVote, toAddVote, toKeep []*machine) { statuses := info.statusesMap(members) logger.Debugf("assessing possible peer group changes:") for _, m := range info.machines { member := members[m] isVoting := member != nil && isVotingMember(member) switch { case m.wantsVote && isVoting: logger.Debugf("machine %q is already voting", m.id) toKeep = append(toKeep, m) case m.wantsVote && !isVoting: if status, ok := statuses[m]; ok && isReady(status) { logger.Debugf("machine %q is a potential voter", m.id) toAddVote = append(toAddVote, m) } else { logger.Debugf("machine %q is not ready (has status: %v)", m.id, ok) toKeep = append(toKeep, m) } case !m.wantsVote && isVoting: logger.Debugf("machine %q is a potential non-voter", m.id) toRemoveVote = append(toRemoveVote, m) case !m.wantsVote && !isVoting: logger.Debugf("machine %q does not want the vote", m.id) toKeep = append(toKeep, m) } } logger.Debugf("assessed") // sort machines to be added and removed so that we // get deterministic behaviour when testing. Earlier // entries will be dealt with preferentially, so we could // potentially sort by some other metric in each case. sort.Sort(byId(toRemoveVote)) sort.Sort(byId(toAddVote)) sort.Sort(byId(toKeep)) return toRemoveVote, toAddVote, toKeep } // updateAddresses updates the members' addresses from the machines' addresses. // It reports whether any changes have been made. func updateAddresses(members map[*machine]*replicaset.Member, machines map[string]*machine) bool { changed := false // Make sure all members' machine addresses are up to date. for _, m := range machines { hp := m.mongoHostPort() if hp == "" { continue } // TODO ensure that replicaset works correctly with IPv6 [host]:port addresses. if hp != members[m].Address { members[m].Address = hp changed = true } } return changed } // adjustVotes adjusts the votes of the given machines, taking // care not to let the total number of votes become even at // any time. It calls setVoting to change the voting status // of a machine. func adjustVotes(toRemoveVote, toAddVote []*machine, setVoting func(*machine, bool)) { // Remove voting members if they can be replaced by // candidates that are ready. This does not affect // the total number of votes. nreplace := min(len(toRemoveVote), len(toAddVote)) for i := 0; i < nreplace; i++ { from := toRemoveVote[i] to := toAddVote[i] setVoting(from, false) setVoting(to, true) } toAddVote = toAddVote[nreplace:] toRemoveVote = toRemoveVote[nreplace:] // At this point, one or both of toAdd or toRemove is empty, so // we can adjust the voting-member count by an even delta, // maintaining the invariant that the total vote count is odd. if len(toAddVote) > 0 { toAddVote = toAddVote[0 : len(toAddVote)-len(toAddVote)%2] for _, m := range toAddVote { setVoting(m, true) } } else { toRemoveVote = toRemoveVote[0 : len(toRemoveVote)-len(toRemoveVote)%2] for _, m := range toRemoveVote { setVoting(m, false) } } } // addNewMembers adds new members from toKeep // to the given set of members, allocating ids from // maxId upwards. It calls setVoting to set the voting // status of each new member. func addNewMembers( members map[*machine]*replicaset.Member, toKeep []*machine, maxId int, setVoting func(*machine, bool), ) { for _, m := range toKeep { hasAddress := m.mongoHostPort() != "" if members[m] == nil && hasAddress { // This machine was not previously in the members list, // so add it (as non-voting). We maintain the // id manually to make it easier for tests. maxId++ member := &replicaset.Member{ Tags: map[string]string{ "juju-machine-id": m.id, }, Id: maxId, } members[m] = member setVoting(m, false) } else if !hasAddress { logger.Debugf("ignoring machine %q with no address", m.id) } } } func isReady(status replicaset.MemberStatus) bool { return status.Healthy && (status.State == replicaset.PrimaryState || status.State == replicaset.SecondaryState) } func setMemberVoting(member *replicaset.Member, voting bool) { if voting { member.Votes = nil member.Priority = nil } else { votes := 0 member.Votes = &votes priority := 0.0 member.Priority = &priority } } type byId []*machine func (l byId) Len() int { return len(l) } func (l byId) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l byId) Less(i, j int) bool { return l[i].id < l[j].id } // membersMap returns the replica-set members inside info keyed // by machine. Any members that do not have a corresponding // machine are returned in extra. // The maximum replica-set id is returned in maxId. func (info *peerGroupInfo) membersMap() (members map[*machine]*replicaset.Member, extra []replicaset.Member, maxId int) { maxId = -1 members = make(map[*machine]*replicaset.Member) for _, member := range info.members { member := member mid, ok := member.Tags["juju-machine-id"] var found *machine if ok { found = info.machines[mid] } if found != nil { members[found] = &member } else { extra = append(extra, member) } if member.Id > maxId { maxId = member.Id } } return members, extra, maxId } // statusesMap returns the statuses inside info keyed by machine. // The provided members map holds the members keyed by machine, // as returned by membersMap. func (info *peerGroupInfo) statusesMap(members map[*machine]*replicaset.Member) map[*machine]replicaset.MemberStatus { statuses := make(map[*machine]replicaset.MemberStatus) for _, status := range info.statuses { for m, member := range members { if member.Id == status.Id { statuses[m] = status break } } } return statuses } func min(i, j int) int { if i < j { return i } return j } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/peergrouper/worker.go���������������������������0000644�0000153�0000161�00000033753�12321735776�026420� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package peergrouper import ( "fmt" "sync" "time" "launchpad.net/tomb" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/replicaset" "launchpad.net/juju-core/state" "launchpad.net/juju-core/worker" ) type stateInterface interface { Machine(id string) (stateMachine, error) WatchStateServerInfo() state.NotifyWatcher StateServerInfo() (*state.StateServerInfo, error) MongoSession() mongoSession } type stateMachine interface { Id() string InstanceId() (instance.Id, error) Refresh() error Watch() state.NotifyWatcher WantsVote() bool HasVote() bool SetHasVote(hasVote bool) error APIHostPorts() []instance.HostPort MongoHostPorts() []instance.HostPort } type mongoSession interface { CurrentStatus() (*replicaset.Status, error) CurrentMembers() ([]replicaset.Member, error) Set([]replicaset.Member) error } type publisherInterface interface { // publish publishes information about the given state servers // to whomsoever it may concern. When it is called there // is no guarantee that any of the information has actually changed. publishAPIServers(apiServers [][]instance.HostPort, instanceIds []instance.Id) error } // notifyFunc holds a function that is sent // to the main worker loop to fetch new information // when something changes. It reports whether // the information has actually changed (and by implication // whether the replica set may need to be changed). type notifyFunc func() (changed bool, err error) var ( // If we fail to set the mongo replica set members, // we retry at the following interval until we succeed. retryInterval = 2 * time.Second // pollInterval holds the interval at which the replica set // members will be updated even in the absence of changes // to State. This enables us to make changes to members // that are triggered by changes to member status. // // 10 seconds is the default time interval used by // mongo to keep its replicas up to date. pollInterval = 10 * time.Second ) // pgWorker holds all the mutable state that we are watching. // The only goroutine that is allowed to modify this // is worker.loop - other watchers modify the // current state by calling worker.notify instead of // modifying it directly. type pgWorker struct { tomb tomb.Tomb // wg represents all the currently running goroutines. // The worker main loop waits for all of these to exit // before finishing. wg sync.WaitGroup // st represents the State. It is an interface so we can swap // out the implementation during testing. st stateInterface // When something changes that might affect // the peer group membership, it sends a function // on notifyCh that is run inside the main worker // goroutine to mutate the state. It reports whether // the state has actually changed. notifyCh chan notifyFunc // machines holds the set of machines we are currently // watching (all the state server machines). Each one has an // associated goroutine that // watches attributes of that machine. machines map[string]*machine // publisher holds the implementation of the API // address publisher. publisher publisherInterface } // New returns a new worker that maintains the mongo replica set // with respect to the given state. func New(st *state.State) (worker.Worker, error) { cfg, err := st.EnvironConfig() if err != nil { return nil, err } return newWorker(&stateShim{ State: st, mongoPort: cfg.StatePort(), apiPort: cfg.APIPort(), }, newPublisher(st)), nil } func newWorker(st stateInterface, pub publisherInterface) worker.Worker { w := &pgWorker{ st: st, notifyCh: make(chan notifyFunc), machines: make(map[string]*machine), publisher: pub, } logger.Infof("worker starting") go func() { defer w.tomb.Done() if err := w.loop(); err != nil { logger.Errorf("peergrouper loop terminated: %v", err) w.tomb.Kill(err) } // Wait for the various goroutines to be killed. // N.B. we don't defer this call because // if we do and a bug causes a panic, Wait will deadlock // waiting for the unkilled goroutines to exit. w.wg.Wait() }() return w } func (w *pgWorker) Kill() { w.tomb.Kill(nil) } func (w *pgWorker) Wait() error { return w.tomb.Wait() } func (w *pgWorker) loop() error { infow := w.watchStateServerInfo() defer infow.stop() retry := time.NewTimer(0) retry.Stop() for { select { case f := <-w.notifyCh: // Update our current view of the state of affairs. changed, err := f() if err != nil { return err } if !changed { break } // Try to update the replica set immediately. retry.Reset(0) case <-retry.C: ok := true servers, instanceIds, err := w.apiPublishInfo() if err != nil { return fmt.Errorf("cannot get API server info: %v", err) } if err := w.publisher.publishAPIServers(servers, instanceIds); err != nil { logger.Errorf("cannot publish API server addresses: %v", err) ok = false } if err := w.updateReplicaset(); err != nil { if _, isReplicaSetError := err.(*replicaSetError); !isReplicaSetError { return err } logger.Errorf("cannot set replicaset: %v", err) ok = false } if ok { // Update the replica set members occasionally // to keep them up to date with the current // replica set member statuses. retry.Reset(pollInterval) } else { retry.Reset(retryInterval) } case <-w.tomb.Dying(): return tomb.ErrDying } } } func (w *pgWorker) apiPublishInfo() ([][]instance.HostPort, []instance.Id, error) { servers := make([][]instance.HostPort, 0, len(w.machines)) instanceIds := make([]instance.Id, 0, len(w.machines)) for _, m := range w.machines { if len(m.apiHostPorts) == 0 { continue } instanceId, err := m.stm.InstanceId() if err != nil { return nil, nil, err } instanceIds = append(instanceIds, instanceId) servers = append(servers, m.apiHostPorts) } return servers, instanceIds, nil } // notify sends the given notification function to // the worker main loop to be executed. func (w *pgWorker) notify(f notifyFunc) bool { select { case w.notifyCh <- f: return true case <-w.tomb.Dying(): return false } } // peerGroupInfo collates current session information about the // mongo peer group with information from state machines. func (w *pgWorker) peerGroupInfo() (*peerGroupInfo, error) { session := w.st.MongoSession() info := &peerGroupInfo{} var err error status, err := session.CurrentStatus() if err != nil { return nil, fmt.Errorf("cannot get replica set status: %v", err) } info.statuses = status.Members info.members, err = session.CurrentMembers() if err != nil { return nil, fmt.Errorf("cannot get replica set members: %v", err) } info.machines = w.machines return info, nil } // replicaSetError holds an error returned as a result // of calling replicaset.Set. As this is expected to fail // in the normal course of things, it needs special treatment. type replicaSetError struct { error } // updateReplicaset sets the current replica set members, and applies the // given voting status to machines in the state. func (w *pgWorker) updateReplicaset() error { info, err := w.peerGroupInfo() if err != nil { return err } members, voting, err := desiredPeerGroup(info) if err != nil { return fmt.Errorf("cannot compute desired peer group: %v", err) } if members == nil { logger.Debugf("no change in desired peer group") return nil } logger.Debugf("desired peer group members: %#v", members) // We cannot change the HasVote flag of a machine in state at exactly // the same moment as changing its voting status in the replica set. // // Thus we need to be careful that a machine which is actually a voting // member is not seen to not have a vote, because otherwise // there is nothing to prevent the machine being removed. // // To avoid this happening, we make sure when we call SetReplicaSet, // that the voting status of machines is the union of both old // and new voting machines - that is the set of HasVote machines // is a superset of all the actual voting machines. // // Only after the call has taken place do we reset the voting status // of the machines that have lost their vote. // // If there's a crash, the voting status may not reflect the // actual voting status for a while, but when things come // back on line, it will be sorted out, as desiredReplicaSet // will return the actual voting status. var added, removed []*machine for m, hasVote := range voting { switch { case hasVote && !m.stm.HasVote(): added = append(added, m) case !hasVote && m.stm.HasVote(): removed = append(removed, m) } } if err := setHasVote(added, true); err != nil { return err } if err := w.st.MongoSession().Set(members); err != nil { // We've failed to set the replica set, so revert back // to the previous settings. if err1 := setHasVote(added, false); err1 != nil { logger.Errorf("cannot revert machine voting after failure to change replica set: %v", err1) } return &replicaSetError{err} } logger.Infof("successfully changed replica set to %#v", members) if err := setHasVote(removed, false); err != nil { return err } return nil } // start runs the given loop function until it returns. // When it returns, the receiving pgWorker is killed with // the returned error. func (w *pgWorker) start(loop func() error) { w.wg.Add(1) go func() { defer w.wg.Done() if err := loop(); err != nil { w.tomb.Kill(err) } }() } // setHasVote sets the HasVote status of all the given // machines to hasVote. func setHasVote(ms []*machine, hasVote bool) error { for _, m := range ms { if err := m.stm.SetHasVote(hasVote); err != nil { return fmt.Errorf("cannot set voting status of %q to %v: %v", m.id, hasVote, err) } } return nil } // serverInfoWatcher watches the state server info and // notifies the worker when it changes. type serverInfoWatcher struct { worker *pgWorker watcher state.NotifyWatcher } func (w *pgWorker) watchStateServerInfo() *serverInfoWatcher { infow := &serverInfoWatcher{ worker: w, watcher: w.st.WatchStateServerInfo(), } w.start(infow.loop) return infow } func (infow *serverInfoWatcher) loop() error { for { select { case _, ok := <-infow.watcher.Changes(): if !ok { return infow.watcher.Err() } infow.worker.notify(infow.updateMachines) case <-infow.worker.tomb.Dying(): return tomb.ErrDying } } } func (infow *serverInfoWatcher) stop() { infow.watcher.Stop() } // updateMachines is a notifyFunc that updates the current // machines when the state server info has changed. func (infow *serverInfoWatcher) updateMachines() (bool, error) { info, err := infow.worker.st.StateServerInfo() if err != nil { return false, fmt.Errorf("cannot get state server info: %v", err) } changed := false // Stop machine goroutines that no longer correspond to state server // machines. for _, m := range infow.worker.machines { if !inStrings(m.id, info.MachineIds) { m.stop() delete(infow.worker.machines, m.id) changed = true } } // Start machines with no watcher for _, id := range info.MachineIds { if _, ok := infow.worker.machines[id]; ok { continue } logger.Debugf("found new machine %q", id) stm, err := infow.worker.st.Machine(id) if err != nil { if errors.IsNotFoundError(err) { // If the machine isn't found, it must have been // removed and will soon enough be removed // from the state server list. This will probably // never happen, but we'll code defensively anyway. logger.Warningf("machine %q from state server list not found", id) continue } return false, fmt.Errorf("cannot get machine %q: %v", id, err) } infow.worker.machines[id] = infow.worker.newMachine(stm) changed = true } return changed, nil } // machine represents a machine in State. type machine struct { id string wantsVote bool apiHostPorts []instance.HostPort mongoHostPorts []instance.HostPort worker *pgWorker stm stateMachine machineWatcher state.NotifyWatcher } func (m *machine) mongoHostPort() string { return instance.SelectInternalHostPort(m.mongoHostPorts, false) } func (m *machine) String() string { return m.id } func (m *machine) GoString() string { return fmt.Sprintf("&peergrouper.machine{id: %q, wantsVote: %v, hostPort: %q}", m.id, m.wantsVote, m.mongoHostPort()) } func (w *pgWorker) newMachine(stm stateMachine) *machine { m := &machine{ worker: w, id: stm.Id(), stm: stm, apiHostPorts: stm.APIHostPorts(), mongoHostPorts: stm.MongoHostPorts(), wantsVote: stm.WantsVote(), machineWatcher: stm.Watch(), } w.start(m.loop) return m } func (m *machine) loop() error { for { select { case _, ok := <-m.machineWatcher.Changes(): if !ok { return m.machineWatcher.Err() } m.worker.notify(m.refresh) case <-m.worker.tomb.Dying(): return nil } } } func (m *machine) stop() { m.machineWatcher.Stop() } func (m *machine) refresh() (bool, error) { if err := m.stm.Refresh(); err != nil { if errors.IsNotFoundError(err) { // We want to be robust when the machine // state is out of date with respect to the // state server info, so if the machine // has been removed, just assume that // no change has happened - the machine // loop will be stopped very soon anyway. return false, nil } return false, err } changed := false if wantsVote := m.stm.WantsVote(); wantsVote != m.wantsVote { m.wantsVote = wantsVote changed = true } if hps := m.stm.MongoHostPorts(); !hostPortsEqual(hps, m.mongoHostPorts) { m.mongoHostPorts = hps changed = true } if hps := m.stm.APIHostPorts(); !hostPortsEqual(hps, m.apiHostPorts) { m.apiHostPorts = hps changed = true } return changed, nil } func hostPortsEqual(hps1, hps2 []instance.HostPort) bool { if len(hps1) != len(hps2) { return false } for i := range hps1 { if hps1[i] != hps2[i] { return false } } return true } func inStrings(t string, ss []string) bool { for _, s := range ss { if s == t { return true } } return false } ���������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/peergrouper/worker_test.go����������������������0000644�0000153�0000161�00000026051�12321735776�027450� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package peergrouper import ( "errors" "fmt" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/voyeur" "launchpad.net/juju-core/worker" ) type workerJujuConnSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&workerJujuConnSuite{}) func (s *workerJujuConnSuite) TestStartStop(c *gc.C) { w, err := New(s.State) c.Assert(err, gc.IsNil) err = worker.Stop(w) c.Assert(err, gc.IsNil) } func (s *workerJujuConnSuite) TestPublisherSetsAPIHostPorts(c *gc.C) { st := newFakeState() initState(c, st, 3) watcher := s.State.WatchAPIHostPorts() cwatch := statetesting.NewNotifyWatcherC(c, s.State, watcher) cwatch.AssertOneChange() statePublish := newPublisher(s.State) // Wrap the publisher so that we can call StartSync immediately // after the publishAPIServers method is called. publish := func(apiServers [][]instance.HostPort, instanceIds []instance.Id) error { err := statePublish.publishAPIServers(apiServers, instanceIds) s.State.StartSync() return err } w := newWorker(st, publisherFunc(publish)) defer func() { c.Check(worker.Stop(w), gc.IsNil) }() cwatch.AssertOneChange() hps, err := s.State.APIHostPorts() c.Assert(err, gc.IsNil) c.Assert(hps, jc.DeepEquals, expectedAPIHostPorts(3)) } type workerSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&workerSuite{}) func (s *workerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) resetErrors() } // initState initializes the fake state with a single // replicaset member and numMachines machines // primed to vote. func initState(c *gc.C, st *fakeState, numMachines int) { var ids []string for i := 10; i < 10+numMachines; i++ { id := fmt.Sprint(i) m := st.addMachine(id, true) m.setInstanceId(instance.Id("id-" + id)) m.setStateHostPort(fmt.Sprintf("0.1.2.%d:%d", i, mongoPort)) ids = append(ids, id) c.Assert(m.MongoHostPorts(), gc.HasLen, 1) m.setAPIHostPorts(addressesWithPort(apiPort, fmt.Sprintf("0.1.2.%d", i))) } st.machine("10").SetHasVote(true) st.setStateServers(ids...) st.session.Set(mkMembers("0v")) st.session.setStatus(mkStatuses("0p")) st.check = checkInvariants } // expectedAPIHostPorts returns the expected addresses // of the machines as created by initState. func expectedAPIHostPorts(n int) [][]instance.HostPort { servers := make([][]instance.HostPort, n) for i := range servers { servers[i] = []instance.HostPort{{ Address: instance.NewAddress(fmt.Sprintf("0.1.2.%d", i+10)), Port: apiPort, }} } return servers } func addressesWithPort(port int, addrs ...string) []instance.HostPort { return instance.AddressesWithPort(instance.NewAddresses(addrs), port) } func (s *workerSuite) TestSetsAndUpdatesMembers(c *gc.C) { s.PatchValue(&pollInterval, 5*time.Millisecond) st := newFakeState() initState(c, st, 3) memberWatcher := st.session.members.Watch() mustNext(c, memberWatcher) assertMembers(c, memberWatcher.Value(), mkMembers("0v")) logger.Infof("starting worker") w := newWorker(st, noPublisher{}) defer func() { c.Check(worker.Stop(w), gc.IsNil) }() // Wait for the worker to set the initial members. mustNext(c, memberWatcher) assertMembers(c, memberWatcher.Value(), mkMembers("0v 1 2")) // Update the status of the new members // and check that they become voting. c.Logf("updating new member status") st.session.setStatus(mkStatuses("0p 1s 2s")) mustNext(c, memberWatcher) assertMembers(c, memberWatcher.Value(), mkMembers("0v 1v 2v")) c.Logf("adding another machine") // Add another machine. m13 := st.addMachine("13", false) m13.setStateHostPort(fmt.Sprintf("0.1.2.%d:%d", 13, mongoPort)) st.setStateServers("10", "11", "12", "13") c.Logf("waiting for new member to be added") mustNext(c, memberWatcher) assertMembers(c, memberWatcher.Value(), mkMembers("0v 1v 2v 3")) // Remove vote from an existing member; // and give it to the new machine. // Also set the status of the new machine to // healthy. c.Logf("removing vote from machine 10 and adding it to machine 13") st.machine("10").setWantsVote(false) st.machine("13").setWantsVote(true) st.session.setStatus(mkStatuses("0p 1s 2s 3s")) // Check that the new machine gets the vote and the // old machine loses it. c.Logf("waiting for vote switch") mustNext(c, memberWatcher) assertMembers(c, memberWatcher.Value(), mkMembers("0 1v 2v 3v")) c.Logf("removing old machine") // Remove the old machine. st.removeMachine("10") st.setStateServers("11", "12", "13") // Check that it's removed from the members. c.Logf("waiting for removal") mustNext(c, memberWatcher) assertMembers(c, memberWatcher.Value(), mkMembers("1v 2v 3v")) } func (s *workerSuite) TestAddressChange(c *gc.C) { st := newFakeState() initState(c, st, 3) memberWatcher := st.session.members.Watch() mustNext(c, memberWatcher) assertMembers(c, memberWatcher.Value(), mkMembers("0v")) logger.Infof("starting worker") w := newWorker(st, noPublisher{}) defer func() { c.Check(worker.Stop(w), gc.IsNil) }() // Wait for the worker to set the initial members. mustNext(c, memberWatcher) assertMembers(c, memberWatcher.Value(), mkMembers("0v 1 2")) // Change an address and wait for it to be changed in the // members. st.machine("11").setStateHostPort("0.1.99.99:9876") mustNext(c, memberWatcher) expectMembers := mkMembers("0v 1 2") expectMembers[1].Address = "0.1.99.99:9876" assertMembers(c, memberWatcher.Value(), expectMembers) } var fatalErrorsTests = []struct { errPattern string err error expectErr string }{{ errPattern: "State.StateServerInfo", expectErr: "cannot get state server info: sample", }, { errPattern: "Machine.SetHasVote 11 true", expectErr: `cannot set voting status of "11" to true: sample`, }, { errPattern: "Session.CurrentStatus", expectErr: "cannot get replica set status: sample", }, { errPattern: "Session.CurrentMembers", expectErr: "cannot get replica set members: sample", }, { errPattern: "State.Machine *", expectErr: `cannot get machine "10": sample`, }, { errPattern: "Machine.InstanceId *", expectErr: `cannot get API server info: sample`, }} func (s *workerSuite) TestFatalErrors(c *gc.C) { s.PatchValue(&pollInterval, 5*time.Millisecond) for i, test := range fatalErrorsTests { c.Logf("test %d: %s -> %s", i, test.errPattern, test.expectErr) resetErrors() st := newFakeState() st.session.InstantlyReady = true initState(c, st, 3) setErrorFor(test.errPattern, errors.New("sample")) w := newWorker(st, noPublisher{}) done := make(chan error) go func() { done <- w.Wait() }() select { case err := <-done: c.Assert(err, gc.ErrorMatches, test.expectErr) case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for error") } } } func (s *workerSuite) TestSetMembersErrorIsNotFatal(c *gc.C) { st := newFakeState() initState(c, st, 3) st.session.setStatus(mkStatuses("0p 1s 2s")) var isSet voyeur.Value count := 0 setErrorFuncFor("Session.Set", func() error { isSet.Set(count) count++ return errors.New("sample") }) s.PatchValue(&retryInterval, 5*time.Millisecond) w := newWorker(st, noPublisher{}) defer func() { c.Check(worker.Stop(w), gc.IsNil) }() isSetWatcher := isSet.Watch() n0, _ := mustNext(c, isSetWatcher) // The worker should not retry more than every // retryInterval. time.Sleep(retryInterval * 10) n1, _ := mustNext(c, isSetWatcher) c.Assert(n0.(int)-n0.(int), jc.LessThan, 11) c.Assert(n1, jc.GreaterThan, n0) } type publisherFunc func(apiServers [][]instance.HostPort, instanceIds []instance.Id) error func (f publisherFunc) publishAPIServers(apiServers [][]instance.HostPort, instanceIds []instance.Id) error { return f(apiServers, instanceIds) } func (s *workerSuite) TestStateServersArePublished(c *gc.C) { publishCh := make(chan [][]instance.HostPort) publish := func(apiServers [][]instance.HostPort, instanceIds []instance.Id) error { publishCh <- apiServers return nil } st := newFakeState() initState(c, st, 3) w := newWorker(st, publisherFunc(publish)) defer func() { c.Check(worker.Stop(w), gc.IsNil) }() select { case servers := <-publishCh: c.Assert(servers, gc.DeepEquals, expectedAPIHostPorts(3)) case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for publish") } // Change one of the servers' API addresses and check that it's published. newMachine10APIHostPorts := addressesWithPort(apiPort, "0.2.8.124") st.machine("10").setAPIHostPorts(newMachine10APIHostPorts) select { case servers := <-publishCh: expected := expectedAPIHostPorts(3) expected[0] = newMachine10APIHostPorts c.Assert(servers, jc.DeepEquals, expected) case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for publish") } } func (s *workerSuite) TestWorkerRetriesOnPublishError(c *gc.C) { s.PatchValue(&pollInterval, coretesting.LongWait+time.Second) s.PatchValue(&retryInterval, 5*time.Millisecond) publishCh := make(chan [][]instance.HostPort, 100) count := 0 publish := func(apiServers [][]instance.HostPort, instanceIds []instance.Id) error { publishCh <- apiServers count++ if count <= 3 { return fmt.Errorf("publish error") } return nil } st := newFakeState() initState(c, st, 3) w := newWorker(st, publisherFunc(publish)) defer func() { c.Check(worker.Stop(w), gc.IsNil) }() for i := 0; i < 4; i++ { select { case servers := <-publishCh: c.Assert(servers, jc.DeepEquals, expectedAPIHostPorts(3)) case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for publish #%d", i) } } select { case <-publishCh: c.Errorf("unexpected publish event") case <-time.After(coretesting.ShortWait): } } func (s *workerSuite) TestWorkerPublishesInstanceIds(c *gc.C) { s.PatchValue(&pollInterval, coretesting.LongWait+time.Second) s.PatchValue(&retryInterval, 5*time.Millisecond) publishCh := make(chan []instance.Id, 100) publish := func(apiServers [][]instance.HostPort, instanceIds []instance.Id) error { publishCh <- instanceIds return nil } st := newFakeState() initState(c, st, 3) w := newWorker(st, publisherFunc(publish)) defer func() { c.Check(worker.Stop(w), gc.IsNil) }() select { case instanceIds := <-publishCh: c.Assert(instanceIds, jc.DeepEquals, []instance.Id{"id-10", "id-11", "id-12"}) case <-time.After(coretesting.LongWait): c.Errorf("timed out waiting for publish") } } func mustNext(c *gc.C, w *voyeur.Watcher) (val interface{}, ok bool) { done := make(chan struct{}) go func() { c.Logf("mustNext %p", w) ok = w.Next() val = w.Value() c.Logf("mustNext done %p, ok %v", w, ok) done <- struct{}{} }() select { case <-done: return case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for value to be set") } panic("unreachable") } type noPublisher struct{} func (noPublisher) publishAPIServers(apiServers [][]instance.HostPort, instanceIds []instance.Id) error { return nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/peergrouper/desired_test.go���������������������0000644�0000153�0000161�00000027123�12321735776�027557� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package peergrouper import ( "fmt" "sort" "strconv" "strings" stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/replicaset" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type desiredPeerGroupSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&desiredPeerGroupSuite{}) const ( mongoPort = 1234 apiPort = 5678 ) var desiredPeerGroupTests = []struct { about string machines []*machine statuses []replicaset.MemberStatus members []replicaset.Member expectMembers []replicaset.Member expectVoting []bool expectErr string }{{ // Note that this should never happen - mongo // should always be bootstrapped with at least a single // member in its member-set. about: "no members - error", expectErr: "current member set is empty", }, { about: "one machine, two more proposed members", machines: mkMachines("10v 11v 12v"), statuses: mkStatuses("0p"), members: mkMembers("0v"), expectMembers: mkMembers("0v 1 2"), expectVoting: []bool{true, false, false}, }, { about: "single machine, no change", machines: mkMachines("11v"), members: mkMembers("1v"), statuses: mkStatuses("1p"), expectVoting: []bool{true}, expectMembers: nil, }, { about: "extra member with nil Vote", machines: mkMachines("11v"), members: mkMembers("1v 2vT"), statuses: mkStatuses("1p 2s"), expectVoting: []bool{true}, expectErr: "voting non-machine member.* found in peer group", }, { about: "extra member with >1 votes", machines: mkMachines("11v"), members: append(mkMembers("1v"), replicaset.Member{ Id: 2, Votes: newInt(2), Address: "0.1.2.12:1234", }), statuses: mkStatuses("1p 2s"), expectVoting: []bool{true}, expectErr: "voting non-machine member.* found in peer group", }, { about: "new machine with no associated member", machines: mkMachines("11v 12v"), members: mkMembers("1v"), statuses: mkStatuses("1p"), expectVoting: []bool{true, false}, expectMembers: mkMembers("1v 2"), }, { about: "one machine has become ready to vote (-> no change)", machines: mkMachines("11v 12v"), members: mkMembers("1v 2"), statuses: mkStatuses("1p 2s"), expectVoting: []bool{true, false}, expectMembers: nil, }, { about: "two machines have become ready to vote (-> added)", machines: mkMachines("11v 12v 13v"), members: mkMembers("1v 2 3"), statuses: mkStatuses("1p 2s 3s"), expectVoting: []bool{true, true, true}, expectMembers: mkMembers("1v 2v 3v"), }, { about: "two machines have become ready to vote but one is not healthy (-> no change)", machines: mkMachines("11v 12v 13v"), members: mkMembers("1v 2 3"), statuses: mkStatuses("1p 2s 3sH"), expectVoting: []bool{true, false, false}, expectMembers: nil, }, { about: "three machines have become ready to vote (-> 2 added)", machines: mkMachines("11v 12v 13v 14v"), members: mkMembers("1v 2 3 4"), statuses: mkStatuses("1p 2s 3s 4s"), expectVoting: []bool{true, true, true, false}, expectMembers: mkMembers("1v 2v 3v 4"), }, { about: "one machine ready to lose vote with no others -> no change", machines: mkMachines("11"), members: mkMembers("1v"), statuses: mkStatuses("1p"), expectVoting: []bool{true}, expectMembers: nil, }, { about: "two machines ready to lose vote -> votes removed", machines: mkMachines("11 12v 13"), members: mkMembers("1v 2v 3v"), statuses: mkStatuses("1p 2p 3p"), expectVoting: []bool{false, true, false}, expectMembers: mkMembers("1 2v 3"), }, { about: "machines removed as state server -> removed from members", machines: mkMachines("11v"), members: mkMembers("1v 2 3"), statuses: mkStatuses("1p 2s 3s"), expectVoting: []bool{true}, expectMembers: mkMembers("1v"), }, { about: "a candidate can take the vote of a non-candidate when they're ready", machines: mkMachines("11v 12v 13 14v"), members: mkMembers("1v 2v 3v 4"), statuses: mkStatuses("1p 2s 3s 4s"), expectVoting: []bool{true, true, false, true}, expectMembers: mkMembers("1v 2v 3 4v"), }, { about: "several candidates can take non-candidates' votes", machines: mkMachines("11v 12v 13 14 15 16v 17v 18v"), members: mkMembers("1v 2v 3v 4v 5v 6 7 8"), statuses: mkStatuses("1p 2s 3s 4s 5s 6s 7s 8s"), expectVoting: []bool{true, true, false, false, false, true, true, true}, expectMembers: mkMembers("1v 2v 3 4 5 6v 7v 8v"), }, { about: "a changed machine address should propagate to the members", machines: append(mkMachines("11v 12v"), &machine{ id: "13", wantsVote: true, mongoHostPorts: []instance.HostPort{{ Address: instance.Address{ Value: "0.1.99.13", Type: instance.Ipv4Address, NetworkScope: instance.NetworkCloudLocal, }, Port: 1234, }}, }), statuses: mkStatuses("1s 2p 3p"), members: mkMembers("1v 2v 3v"), expectVoting: []bool{true, true, true}, expectMembers: append(mkMembers("1v 2v"), replicaset.Member{ Id: 3, Address: "0.1.99.13:1234", Tags: memberTag("13"), }), }, { about: "a machine's address is ignored if it changes to empty", machines: append(mkMachines("11v 12v"), &machine{ id: "13", wantsVote: true, mongoHostPorts: nil, }), statuses: mkStatuses("1s 2p 3p"), members: mkMembers("1v 2v 3v"), expectVoting: []bool{true, true, true}, expectMembers: nil, }} func (*desiredPeerGroupSuite) TestDesiredPeerGroup(c *gc.C) { for i, test := range desiredPeerGroupTests { c.Logf("\ntest %d: %s", i, test.about) machineMap := make(map[string]*machine) for _, m := range test.machines { c.Assert(machineMap[m.id], gc.IsNil) machineMap[m.id] = m } info := &peerGroupInfo{ machines: machineMap, statuses: test.statuses, members: test.members, } members, voting, err := desiredPeerGroup(info) if test.expectErr != "" { c.Assert(err, gc.ErrorMatches, test.expectErr) c.Assert(members, gc.IsNil) continue } sort.Sort(membersById(members)) c.Assert(members, jc.DeepEquals, test.expectMembers) if len(members) == 0 { continue } for i, m := range test.machines { c.Assert(voting[m], gc.Equals, test.expectVoting[i], gc.Commentf("machine %s", m.id)) } // Assure ourselves that the total number of desired votes is odd in // all circumstances. c.Assert(countVotes(members)%2, gc.Equals, 1) // Make sure that when the members are set as // required, that there's no further change // if desiredPeerGroup is called again. info.members = members members, voting, err = desiredPeerGroup(info) c.Assert(members, gc.IsNil) c.Assert(voting, gc.IsNil) c.Assert(err, gc.IsNil) } } func countVotes(members []replicaset.Member) int { tot := 0 for _, m := range members { v := 1 if m.Votes != nil { v = *m.Votes } tot += v } return tot } func newInt(i int) *int { return &i } func newFloat64(f float64) *float64 { return &f } // mkMachines returns a slice of *machine based on // the given description. // Each machine in the description is white-space separated // and holds the decimal machine id followed by an optional // "v" if the machine wants a vote. func mkMachines(description string) []*machine { descrs := parseDescr(description) ms := make([]*machine, len(descrs)) for i, d := range descrs { ms[i] = &machine{ id: fmt.Sprint(d.id), mongoHostPorts: []instance.HostPort{{ Address: instance.Address{ Value: fmt.Sprintf("0.1.2.%d", d.id), Type: instance.Ipv4Address, NetworkScope: instance.NetworkCloudLocal, }, Port: mongoPort, }}, wantsVote: strings.Contains(d.flags, "v"), } } return ms } func memberTag(id string) map[string]string { return map[string]string{"juju-machine-id": id} } // mkMembers returns a slice of *replicaset.Member // based on the given description. // Each member in the description is white-space separated // and holds the decimal replica-set id optionally followed by the characters: // - 'v' if the member is voting. // - 'T' if the member has no associated machine tags. // Unless the T flag is specified, the machine tag // will be the replica-set id + 10. func mkMembers(description string) []replicaset.Member { descrs := parseDescr(description) ms := make([]replicaset.Member, len(descrs)) for i, d := range descrs { machineId := d.id + 10 m := replicaset.Member{ Id: d.id, Address: fmt.Sprintf("0.1.2.%d:%d", machineId, mongoPort), Tags: memberTag(fmt.Sprint(machineId)), } if !strings.Contains(d.flags, "v") { m.Priority = newFloat64(0) m.Votes = newInt(0) } if strings.Contains(d.flags, "T") { m.Tags = nil } ms[i] = m } return ms } var stateFlags = map[rune]replicaset.MemberState{ 'p': replicaset.PrimaryState, 's': replicaset.SecondaryState, } // mkStatuses returns a slice of *replicaset.Member // based on the given description. // Each member in the description is white-space separated // and holds the decimal replica-set id optionally followed by the // characters: // - 'H' if the instance is not healthy. // - 'p' if the instance is in PrimaryState // - 's' if the instance is in SecondaryState func mkStatuses(description string) []replicaset.MemberStatus { descrs := parseDescr(description) ss := make([]replicaset.MemberStatus, len(descrs)) for i, d := range descrs { machineId := d.id + 10 s := replicaset.MemberStatus{ Id: d.id, Address: fmt.Sprintf("0.1.2.%d:%d", machineId, mongoPort), Healthy: !strings.Contains(d.flags, "H"), State: replicaset.UnknownState, } for _, r := range d.flags { if state, ok := stateFlags[r]; ok { s.State = state } } ss[i] = s } return ss } type descr struct { id int flags string } func isNotDigit(r rune) bool { return r < '0' || r > '9' } var parseDescrTests = []struct { descr string expect []descr }{{ descr: "", expect: []descr{}, }, { descr: "0", expect: []descr{{id: 0}}, }, { descr: "1foo", expect: []descr{{id: 1, flags: "foo"}}, }, { descr: "10c 5 6443arble ", expect: []descr{{ id: 10, flags: "c", }, { id: 5, }, { id: 6443, flags: "arble", }}, }} func (*desiredPeerGroupSuite) TestParseDescr(c *gc.C) { for i, test := range parseDescrTests { c.Logf("test %d. %q", i, test.descr) c.Assert(parseDescr(test.descr), jc.DeepEquals, test.expect) } } // parseDescr parses white-space separated fields of the form // <id><flags> into descr structures. func parseDescr(s string) []descr { fields := strings.Fields(s) descrs := make([]descr, len(fields)) for i, field := range fields { d := &descrs[i] i := strings.IndexFunc(field, isNotDigit) if i == -1 { i = len(field) } id, err := strconv.Atoi(field[0:i]) if err != nil { panic(fmt.Errorf("bad field %q", field)) } d.id = id d.flags = field[i:] } return descrs } func assertMembers(c *gc.C, obtained interface{}, expected []replicaset.Member) { c.Assert(obtained, gc.FitsTypeOf, []replicaset.Member{}) sort.Sort(membersById(obtained.([]replicaset.Member))) sort.Sort(membersById(expected)) c.Assert(obtained, jc.DeepEquals, expected) } type membersById []replicaset.Member func (l membersById) Len() int { return len(l) } func (l membersById) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l membersById) Less(i, j int) bool { return l[i].Id < l[j].Id } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/machineenvironmentworker/�����������������������0000755�0000153�0000161�00000000000�12321735642�027321� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000151�00000000000�011562� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/machineenvironmentworker/machineenvironmentworker.go������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/machineenvironmentworker/machineenvironmentworke0000644�0000153�0000161�00000013661�12321735642�034214� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package machineenvironmentworker import ( "fmt" "io/ioutil" "path" "github.com/juju/loggo" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/names" "launchpad.net/juju-core/provider" "launchpad.net/juju-core/state/api/environment" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/exec" "launchpad.net/juju-core/worker" ) var ( logger = loggo.GetLogger("juju.worker.machineenvironment") // ProxyDirectory is the directory containing the proxy file that contains // the environment settings for the proxies based on the environment // config values. ProxyDirectory = "/home/ubuntu" // ProxyFile is the name of the file to be stored in the ProxyDirectory. ProxyFile = ".juju-proxy" // Started is a function that is called when the worker has started. Started = func() {} ) // MachineEnvironmentWorker is responsible for monitoring the juju environment // configuration and making changes on the physical (or virtual) machine as // necessary to match the environment changes. Examples of these types of // changes are apt proxy configuration and the juju proxies stored in the juju // proxy file. type MachineEnvironmentWorker struct { api *environment.Facade aptProxy osenv.ProxySettings proxy osenv.ProxySettings writeSystemFiles bool // The whole point of the first value is to make sure that the the files // are written out the first time through, even if they are the same as // "last" time, as the initial value for last time is the zeroed struct. // There is the possibility that the files exist on disk with old // settings, and the environment has been updated to now not have them. We // need to make sure that the disk reflects the environment, so the first // time through, even if the proxies are empty, we write the files to // disk. first bool } var _ worker.NotifyWatchHandler = (*MachineEnvironmentWorker)(nil) // NewMachineEnvironmentWorker returns a worker.Worker that uses the notify // watcher returned from the setup. func NewMachineEnvironmentWorker(api *environment.Facade, agentConfig agent.Config) worker.Worker { // We don't write out system files for the local provider on machine zero // as that is the host machine. writeSystemFiles := (agentConfig.Tag() != names.MachineTag("0") || agentConfig.Value(agent.ProviderType) != provider.Local) logger.Debugf("write system files: %v", writeSystemFiles) envWorker := &MachineEnvironmentWorker{ api: api, writeSystemFiles: writeSystemFiles, first: true, } return worker.NewNotifyWorker(envWorker) } func (w *MachineEnvironmentWorker) writeEnvironmentFile() error { // Writing the environment file is handled by executing the script for two // primary reasons: // // 1: In order to have the local provider specify the environment settings // for the machine agent running on the host, this worker needs to run, // but it shouldn't be touching any files on the disk. If however there is // an ubuntu user, it will. This shouldn't be a problem. // // 2: On cloud-instance ubuntu images, the ubuntu user is uid 1000, but in // the situation where the ubuntu user has been created as a part of the // manual provisioning process, the user will exist, and will not have the // same uid/gid as the default cloud image. // // It is easier to shell out to check both these things, and is also the // same way that the file is written in the cloud-init process, so // consistency FTW. filePath := path.Join(ProxyDirectory, ProxyFile) result, err := exec.RunCommands(exec.RunParams{ Commands: fmt.Sprintf( `[ -e %s ] && (printf '%%s\n' %s > %s && chown ubuntu:ubuntu %s)`, ProxyDirectory, utils.ShQuote(w.proxy.AsScriptEnvironment()), filePath, filePath), WorkingDir: ProxyDirectory, }) if err != nil { return err } if result.Code != 0 { logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr) } return nil } func (w *MachineEnvironmentWorker) handleProxyValues(proxySettings osenv.ProxySettings) { if proxySettings != w.proxy || w.first { logger.Debugf("new proxy settings %#v", proxySettings) w.proxy = proxySettings w.proxy.SetEnvironmentValues() if w.writeSystemFiles { if err := w.writeEnvironmentFile(); err != nil { // It isn't really fatal, but we should record it. logger.Errorf("error writing proxy environment file: %v", err) } } } } func (w *MachineEnvironmentWorker) handleAptProxyValues(aptSettings osenv.ProxySettings) { if w.writeSystemFiles && (aptSettings != w.aptProxy || w.first) { logger.Debugf("new apt proxy settings %#v", aptSettings) w.aptProxy = aptSettings // Always finish with a new line. content := utils.AptProxyContent(w.aptProxy) + "\n" err := ioutil.WriteFile(utils.AptConfFile, []byte(content), 0644) if err != nil { // It isn't really fatal, but we should record it. logger.Errorf("error writing apt proxy config file: %v", err) } } } func (w *MachineEnvironmentWorker) onChange() error { env, err := w.api.EnvironConfig() if err != nil { return err } w.handleProxyValues(env.ProxySettings()) w.handleAptProxyValues(env.AptProxySettings()) return nil } // SetUp is defined on the worker.NotifyWatchHandler interface. func (w *MachineEnvironmentWorker) SetUp() (watcher.NotifyWatcher, error) { // We need to set this up initially as the NotifyWorker sucks up the first // event. err := w.onChange() if err != nil { return nil, err } w.first = false Started() return w.api.WatchForEnvironConfigChanges() } // Handle is defined on the worker.NotifyWatchHandler interface. func (w *MachineEnvironmentWorker) Handle() error { return w.onChange() } // TearDown is defined on the worker.NotifyWatchHandler interface. func (w *MachineEnvironmentWorker) TearDown() error { // Nothing to cleanup, only state is the watcher return nil } �������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/machineenvironmentworker/package_test.go��������0000644�0000153�0000161�00000000410�12321735642�032275� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package machineenvironmentworker_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000156�00000000000�011567� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/machineenvironmentworker/machineenvironmentworker_test.go�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/machineenvironmentworker/machineenvironmentworke0000644�0000153�0000161�00000014317�12321735642�034213� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package machineenvironmentworker_test import ( "io/ioutil" "os" "path" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju/osenv" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/names" "launchpad.net/juju-core/provider" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/environment" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/machineenvironmentworker" ) type MachineEnvironmentWatcherSuite struct { jujutesting.JujuConnSuite apiRoot *api.State environmentAPI *environment.Facade machine *state.Machine proxyFile string started bool } var _ = gc.Suite(&MachineEnvironmentWatcherSuite{}) func (s *MachineEnvironmentWatcherSuite) setStarted() { s.started = true } func (s *MachineEnvironmentWatcherSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.apiRoot, s.machine = s.OpenAPIAsNewMachine(c) // Create the machiner API facade. s.environmentAPI = s.apiRoot.Environment() c.Assert(s.environmentAPI, gc.NotNil) proxyDir := c.MkDir() s.PatchValue(&machineenvironmentworker.ProxyDirectory, proxyDir) s.started = false s.PatchValue(&machineenvironmentworker.Started, s.setStarted) s.PatchValue(&utils.AptConfFile, path.Join(proxyDir, "juju-apt-proxy")) s.proxyFile = path.Join(proxyDir, machineenvironmentworker.ProxyFile) } func (s *MachineEnvironmentWatcherSuite) waitForPostSetup(c *gc.C) { for { select { case <-time.After(testing.LongWait): c.Fatalf("timeout while waiting for setup") case <-time.After(10 * time.Millisecond): if s.started { return } } } } func (s *MachineEnvironmentWatcherSuite) waitProxySettings(c *gc.C, expected osenv.ProxySettings) { for { select { case <-time.After(testing.LongWait): c.Fatalf("timeout while waiting for proxy settings to change") case <-time.After(10 * time.Millisecond): obtained := osenv.DetectProxies() if obtained != expected { c.Logf("proxy settings are %#v, still waiting", obtained) continue } return } } } func (s *MachineEnvironmentWatcherSuite) waitForFile(c *gc.C, filename, expected string) { for { select { case <-time.After(testing.LongWait): c.Fatalf("timeout while waiting for proxy settings to change") case <-time.After(10 * time.Millisecond): fileContent, err := ioutil.ReadFile(filename) if os.IsNotExist(err) { continue } c.Assert(err, gc.IsNil) if string(fileContent) != expected { c.Logf("file content not matching, still waiting") continue } return } } } func (s *MachineEnvironmentWatcherSuite) makeWorker(c *gc.C, agentConfig agent.Config) worker.Worker { return machineenvironmentworker.NewMachineEnvironmentWorker(s.environmentAPI, agentConfig) } func (s *MachineEnvironmentWatcherSuite) TestRunStop(c *gc.C) { agentConfig := agentConfig("0", "ec2") envWorker := s.makeWorker(c, agentConfig) c.Assert(worker.Stop(envWorker), gc.IsNil) } func (s *MachineEnvironmentWatcherSuite) updateConfig(c *gc.C) (osenv.ProxySettings, osenv.ProxySettings) { proxySettings := osenv.ProxySettings{ Http: "http proxy", Https: "https proxy", Ftp: "ftp proxy", NoProxy: "no proxy", } attrs := map[string]interface{}{} for k, v := range config.ProxyConfigMap(proxySettings) { attrs[k] = v } // We explicitly set apt proxy settings as well to show that it is the apt // settings that are used for the apt config, and not just the normal // proxy settings which is what we would get if we don't explicitly set // apt values. aptProxySettings := osenv.ProxySettings{ Http: "apt http proxy", Https: "apt https proxy", Ftp: "apt ftp proxy", } for k, v := range config.AptProxyConfigMap(aptProxySettings) { attrs[k] = v } err := s.State.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) return proxySettings, aptProxySettings } func (s *MachineEnvironmentWatcherSuite) TestInitialState(c *gc.C) { proxySettings, aptProxySettings := s.updateConfig(c) agentConfig := agentConfig("0", "ec2") envWorker := s.makeWorker(c, agentConfig) defer worker.Stop(envWorker) s.waitProxySettings(c, proxySettings) s.waitForFile(c, s.proxyFile, proxySettings.AsScriptEnvironment()+"\n") s.waitForFile(c, utils.AptConfFile, utils.AptProxyContent(aptProxySettings)+"\n") } func (s *MachineEnvironmentWatcherSuite) TestRespondsToEvents(c *gc.C) { agentConfig := agentConfig("0", "ec2") envWorker := s.makeWorker(c, agentConfig) defer worker.Stop(envWorker) s.waitForPostSetup(c) proxySettings, aptProxySettings := s.updateConfig(c) s.waitProxySettings(c, proxySettings) s.waitForFile(c, s.proxyFile, proxySettings.AsScriptEnvironment()+"\n") s.waitForFile(c, utils.AptConfFile, utils.AptProxyContent(aptProxySettings)+"\n") } func (s *MachineEnvironmentWatcherSuite) TestInitialStateLocalMachine1(c *gc.C) { proxySettings, aptProxySettings := s.updateConfig(c) agentConfig := agentConfig("1", provider.Local) envWorker := s.makeWorker(c, agentConfig) defer worker.Stop(envWorker) s.waitProxySettings(c, proxySettings) s.waitForFile(c, s.proxyFile, proxySettings.AsScriptEnvironment()+"\n") s.waitForFile(c, utils.AptConfFile, utils.AptProxyContent(aptProxySettings)+"\n") } func (s *MachineEnvironmentWatcherSuite) TestInitialStateLocalMachine0(c *gc.C) { proxySettings, _ := s.updateConfig(c) agentConfig := agentConfig("0", provider.Local) envWorker := s.makeWorker(c, agentConfig) defer worker.Stop(envWorker) s.waitForPostSetup(c) s.waitProxySettings(c, proxySettings) c.Assert(utils.AptConfFile, jc.DoesNotExist) c.Assert(s.proxyFile, jc.DoesNotExist) } type mockConfig struct { agent.Config tag string provider string } func (mock *mockConfig) Tag() string { return mock.tag } func (mock *mockConfig) Value(key string) string { if key == agent.ProviderType { return mock.provider } return "" } func agentConfig(machineId, provider string) *mockConfig { return &mockConfig{tag: names.MachineTag(machineId), provider: provider} } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/upgrader/���������������������������������������0000755�0000153�0000161�00000000000�12321736000�023774� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/upgrader/export_test.go�������������������������0000644�0000153�0000161�00000000713�12321735642�026717� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader import ( "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" ) var ( RetryAfter = &retryAfter AllowedTargetVersion = allowedTargetVersion ) func EnsureTools(u *Upgrader, agentTools *tools.Tools, hostnameVerification utils.SSLHostnameVerification) error { return u.ensureTools(agentTools, hostnameVerification) } �����������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/upgrader/upgrader.go����������������������������0000644�0000153�0000161�00000012763�12321735642�026160� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader import ( "fmt" "net/http" "time" "github.com/juju/loggo" "launchpad.net/tomb" "launchpad.net/juju-core/agent" agenttools "launchpad.net/juju-core/agent/tools" "launchpad.net/juju-core/state/api/upgrader" "launchpad.net/juju-core/state/watcher" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // retryAfter returns a channel that receives a value // when a failed download should be retried. var retryAfter = func() <-chan time.Time { return time.After(5 * time.Second) } var logger = loggo.GetLogger("juju.worker.upgrader") // Upgrader represents a worker that watches the state for upgrade // requests. type Upgrader struct { tomb tomb.Tomb st *upgrader.State dataDir string tag string } // NewUpgrader returns a new upgrader worker. It watches changes to the // current version of the current agent (with the given tag) and tries to // download the tools for any new version into the given data directory. If // an upgrade is needed, the worker will exit with an UpgradeReadyError // holding details of the requested upgrade. The tools will have been // downloaded and unpacked. func NewUpgrader(st *upgrader.State, agentConfig agent.Config) *Upgrader { u := &Upgrader{ st: st, dataDir: agentConfig.DataDir(), tag: agentConfig.Tag(), } go func() { defer u.tomb.Done() u.tomb.Kill(u.loop()) }() return u } // Kill implements worker.Worker.Kill. func (u *Upgrader) Kill() { u.tomb.Kill(nil) } // Wait implements worker.Worker.Wait. func (u *Upgrader) Wait() error { return u.tomb.Wait() } // Stop stops the upgrader and returns any // error it encountered when running. func (u *Upgrader) Stop() error { u.Kill() 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) if err != nil { return err } versionWatcher, err := u.st.WatchAPIVersion(u.tag) if err != nil { return err } changes := versionWatcher.Changes() defer watcher.Stop(versionWatcher, &u.tomb) var retry <-chan time.Time // We don't read on the dying channel until we have received the // initial event from the API version watcher, thus ensuring // that we attempt an upgrade even if other workers are dying // all around us. var ( dying <-chan struct{} wantTools *coretools.Tools wantVersion version.Number hostnameVerification utils.SSLHostnameVerification ) for { select { case _, ok := <-changes: if !ok { return watcher.MustErr(versionWatcher) } wantVersion, err = u.st.DesiredVersion(u.tag) if err != nil { return err } logger.Infof("desired tool version: %v", wantVersion) dying = u.tomb.Dying() case <-retry: case <-dying: return nil } 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() } } func (u *Upgrader) ensureTools(agentTools *coretools.Tools, hostnameVerification utils.SSLHostnameVerification) error { if _, err := agenttools.ReadTools(u.dataDir, agentTools.Version); err == nil { // Tools have already been downloaded return nil } logger.Infof("fetching tools from %q", agentTools.URL) client := utils.GetHTTPClient(hostnameVerification) resp, err := client.Get(agentTools.URL) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("bad HTTP response: %v", resp.Status) } err = agenttools.UnpackTools(u.dataDir, agentTools, resp.Body) if err != nil { return fmt.Errorf("cannot unpack tools: %v", err) } logger.Infof("unpacked tools %s to %s", agentTools.Version, u.dataDir) return nil } �������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/upgrader/error.go�������������������������������0000644�0000153�0000161�00000001715�12321735642�025473� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader import ( "launchpad.net/juju-core/agent/tools" "launchpad.net/juju-core/version" ) // UpgradeReadyError is returned by an Upgrader to report that // an upgrade is ready to be performed and a restart is due. type UpgradeReadyError struct { AgentName string OldTools version.Binary NewTools version.Binary DataDir string } func (e *UpgradeReadyError) Error() string { return "must restart: an agent upgrade is available" } // ChangeAgentTools does the actual agent upgrade. // It should be called just before an agent exits, so that // it will restart running the new tools. func (e *UpgradeReadyError) ChangeAgentTools() error { agentTools, err := tools.ChangeAgentTools(e.DataDir, e.AgentName, e.NewTools) if err != nil { return err } logger.Infof("upgraded from %v to %v (%q)", e.OldTools, agentTools.Version, agentTools.URL) return nil } ���������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/upgrader/upgrader_test.go�����������������������0000644�0000153�0000161�00000024340�12321735776�027221� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader_test import ( "fmt" "os" "path/filepath" stdtesting "testing" "time" gc "launchpad.net/gocheck" coretesting "launchpad.net/juju-core/testing" jc "github.com/juju/testing/checkers" "launchpad.net/juju-core/agent" agenttools "launchpad.net/juju-core/agent/tools" envtesting "launchpad.net/juju-core/environs/testing" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/errors" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" statetesting "launchpad.net/juju-core/state/testing" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker/upgrader" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type UpgraderSuite struct { jujutesting.JujuConnSuite machine *state.Machine state *api.State 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) s.state, s.machine = s.OpenAPIAsNewMachine(c) // Capture the value of RetryAfter, and use that captured // value in the cleanup lambda. oldRetryAfter := *upgrader.RetryAfter s.AddCleanup(func(*gc.C) { *upgrader.RetryAfter = oldRetryAfter }) } type mockConfig struct { agent.Config tag string datadir string } func (mock *mockConfig) Tag() string { return mock.tag } func (mock *mockConfig) DataDir() string { return mock.datadir } func agentConfig(tag, datadir string) agent.Config { return &mockConfig{tag: tag, datadir: datadir} } func (s *UpgraderSuite) makeUpgrader() *upgrader.Upgrader { config := agentConfig(s.machine.Tag(), s.DataDir()) return upgrader.NewUpgrader(s.state.Upgrader(), config) } func (s *UpgraderSuite) TestUpgraderSetsTools(c *gc.C) { vers := version.MustParseBinary("5.4.3-precise-amd64") err := statetesting.SetAgentVersion(s.State, vers.Number) c.Assert(err, gc.IsNil) stor := s.Conn.Environ.Storage() agentTools := envtesting.PrimeTools(c, stor, s.DataDir(), vers) s.PatchValue(&version.Current, agentTools.Version) err = envtools.MergeAndWriteMetadata(stor, coretools.List{agentTools}, envtools.DoNotWriteMirrors) _, err = s.machine.AgentTools() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) u := s.makeUpgrader() statetesting.AssertStop(c, u) s.machine.Refresh() gotTools, err := s.machine.AgentTools() c.Assert(err, gc.IsNil) envtesting.CheckTools(c, gotTools, agentTools) } func (s *UpgraderSuite) TestUpgraderSetVersion(c *gc.C) { vers := version.MustParseBinary("5.4.3-precise-amd64") agentTools := envtesting.PrimeTools(c, s.Conn.Environ.Storage(), s.DataDir(), vers) s.PatchValue(&version.Current, agentTools.Version) err := os.RemoveAll(filepath.Join(s.DataDir(), "tools")) c.Assert(err, gc.IsNil) _, err = s.machine.AgentTools() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = statetesting.SetAgentVersion(s.State, vers.Number) c.Assert(err, gc.IsNil) u := s.makeUpgrader() statetesting.AssertStop(c, u) s.machine.Refresh() gotTools, err := s.machine.AgentTools() c.Assert(err, gc.IsNil) c.Assert(gotTools, gc.DeepEquals, &coretools.Tools{Version: version.Current}) } func (s *UpgraderSuite) TestUpgraderUpgradesImmediately(c *gc.C) { stor := s.Conn.Environ.Storage() oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64")) s.PatchValue(&version.Current, oldTools.Version) newTools := envtesting.AssertUploadFakeToolsVersions( c, stor, version.MustParseBinary("5.4.5-precise-amd64"))[0] err := statetesting.SetAgentVersion(s.State, newTools.Version.Number) c.Assert(err, gc.IsNil) // Make the download take a while so that we verify that // the download happens before the upgrader checks if // it's been stopped. dummy.SetStorageDelay(coretesting.ShortWait) u := s.makeUpgrader() err = u.Stop() envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ AgentName: s.machine.Tag(), OldTools: oldTools.Version, NewTools: newTools.Version, DataDir: s.DataDir(), }) foundTools, err := agenttools.ReadTools(s.DataDir(), newTools.Version) c.Assert(err, gc.IsNil) envtesting.CheckTools(c, foundTools, newTools) } func (s *UpgraderSuite) TestUpgraderRetryAndChanged(c *gc.C) { stor := s.Conn.Environ.Storage() oldTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64")) s.PatchValue(&version.Current, oldTools.Version) newTools := envtesting.AssertUploadFakeToolsVersions( c, stor, version.MustParseBinary("5.4.5-precise-amd64"))[0] err := statetesting.SetAgentVersion(s.State, newTools.Version.Number) c.Assert(err, gc.IsNil) retryc := make(chan time.Time) *upgrader.RetryAfter = func() <-chan time.Time { c.Logf("replacement retry after") return retryc } dummy.Poison(s.Conn.Environ.Storage(), envtools.StorageName(newTools.Version), fmt.Errorf("a non-fatal dose")) u := s.makeUpgrader() defer u.Stop() for i := 0; i < 3; i++ { select { case retryc <- time.Now(): case <-time.After(coretesting.LongWait): c.Fatalf("upgrader did not retry (attempt %d)", i) } } // Make it upgrade to some newer tools that can be // downloaded ok; it should stop retrying, download // the newer tools and exit. newerTools := envtesting.AssertUploadFakeToolsVersions( c, s.Conn.Environ.Storage(), version.MustParseBinary("5.4.6-precise-amd64"))[0] err = statetesting.SetAgentVersion(s.State, newerTools.Version.Number) c.Assert(err, gc.IsNil) s.BackingState.StartSync() done := make(chan error) go func() { done <- u.Wait() }() select { case err := <-done: envtesting.CheckUpgraderReadyError(c, err, &upgrader.UpgradeReadyError{ AgentName: s.machine.Tag(), OldTools: oldTools.Version, NewTools: newerTools.Version, DataDir: s.DataDir(), }) case <-time.After(coretesting.LongWait): c.Fatalf("upgrader did not quit after upgrading") } } func (s *UpgraderSuite) TestChangeAgentTools(c *gc.C) { oldTools := &coretools.Tools{ Version: version.MustParseBinary("1.2.3-quantal-amd64"), } stor := s.Conn.Environ.Storage() newTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64")) s.PatchValue(&version.Current, newTools.Version) err := envtools.MergeAndWriteMetadata(stor, coretools.List{newTools}, envtools.DoNotWriteMirrors) c.Assert(err, gc.IsNil) ugErr := &upgrader.UpgradeReadyError{ AgentName: "anAgent", OldTools: oldTools.Version, NewTools: newTools.Version, DataDir: s.DataDir(), } err = ugErr.ChangeAgentTools() c.Assert(err, gc.IsNil) link, err := os.Readlink(agenttools.ToolsDir(s.DataDir(), "anAgent")) c.Assert(err, gc.IsNil) c.Assert(link, gc.Equals, newTools.Version.String()) } func (s *UpgraderSuite) TestEnsureToolsChecksBeforeDownloading(c *gc.C) { stor := s.Conn.Environ.Storage() newTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64")) s.PatchValue(&version.Current, newTools.Version) // We've already downloaded the tools, so change the URL to be // 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://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) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/resumer/����������������������������������������0000755�0000153�0000161�00000000000�12321735643�023661� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/resumer/export_test.go��������������������������0000644�0000153�0000161�00000000365�12321735642�026573� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package resumer import ( "time" ) func SetInterval(i time.Duration) { interval = i } func RestoreInterval() { interval = defaultInterval } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/resumer/resumer_test.go�������������������������0000644�0000153�0000161�00000003454�12321735642�026736� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package resumer_test import ( stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker/resumer" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type ResumerSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&ResumerSuite{}) func (s *ResumerSuite) TestRunStopWithState(c *gc.C) { // Test with state ensures that state fulfills the // TransactionResumer interface. rr := resumer.NewResumer(s.State) c.Assert(rr.Stop(), gc.IsNil) } func (s *ResumerSuite) TestResumerCalls(c *gc.C) { // Shorter interval and mock help to count // the resumer calls in a given timespan. testInterval := 10 * time.Millisecond resumer.SetInterval(testInterval) defer resumer.RestoreInterval() tr := &transactionResumerMock{[]time.Time{}} rr := resumer.NewResumer(tr) defer func() { c.Assert(rr.Stop(), gc.IsNil) }() time.Sleep(10 * testInterval) // Check that a numner of calls has happened with a time // difference somewhere between the interval and twice the // interval. A more precise time behavior cannot be // specified due to the load during the test. c.Assert(len(tr.timestamps) > 0, gc.Equals, true) for i := 1; i < len(tr.timestamps); i++ { diff := tr.timestamps[i].Sub(tr.timestamps[i-1]) c.Assert(diff >= testInterval, gc.Equals, true) c.Assert(diff <= 4*testInterval, gc.Equals, true) } } // transactionResumerMock is used to check the // calls of ResumeTransactions(). type transactionResumerMock struct { timestamps []time.Time } func (tr *transactionResumerMock) ResumeTransactions() error { tr.timestamps = append(tr.timestamps, time.Now()) return nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/resumer/resumer.go������������������������������0000644�0000153�0000161�00000002715�12321735642�025676� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package resumer import ( "fmt" "time" "launchpad.net/tomb" "launchpad.net/juju-core/log" ) // defaultInterval is the standard value for the interval setting. const defaultInterval = time.Minute // interval sets how often the resuming is called. var interval = defaultInterval // TransactionResumer defines the interface for types capable to // resume transactions. type TransactionResumer interface { // ResumeTransactions resumes all pending transactions. ResumeTransactions() error } // Resumer is responsible for a periodical resuming of pending transactions. type Resumer struct { tomb tomb.Tomb tr TransactionResumer } // NewResumer periodically resumes pending transactions. func NewResumer(tr TransactionResumer) *Resumer { rr := &Resumer{tr: tr} go func() { defer rr.tomb.Done() rr.tomb.Kill(rr.loop()) }() return rr } func (rr *Resumer) String() string { return fmt.Sprintf("resumer") } func (rr *Resumer) Kill() { rr.tomb.Kill(nil) } func (rr *Resumer) Stop() error { rr.tomb.Kill(nil) return rr.tomb.Wait() } func (rr *Resumer) Wait() error { return rr.tomb.Wait() } func (rr *Resumer) loop() error { for { select { case <-rr.tomb.Dying(): return tomb.ErrDying case <-time.After(interval): if err := rr.tr.ResumeTransactions(); err != nil { log.Errorf("worker/resumer: cannot resume transactions: %v", err) } } } } ���������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/environ.go��������������������������������������0000644�0000153�0000161�00000006654�12321735642�024220� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker import ( "errors" "fmt" "sync" "github.com/juju/loggo" "launchpad.net/tomb" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/state" apiwatcher "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/state/watcher" ) var ErrTerminateAgent = errors.New("agent should be terminated") var loadedInvalid = func() {} var logger = loggo.GetLogger("juju.worker") // EnvironConfigGetter interface defines a way to read the environment // configuration. type EnvironConfigGetter interface { EnvironConfig() (*config.Config, error) } // TODO(rog) remove WaitForEnviron, as we now should always // start with a valid environ config. // WaitForEnviron waits for an valid environment to arrive from // the given watcher. It terminates with tomb.ErrDying if // it receives a value on dying. func WaitForEnviron(w apiwatcher.NotifyWatcher, st EnvironConfigGetter, dying <-chan struct{}) (environs.Environ, error) { for { select { case <-dying: return nil, tomb.ErrDying case _, ok := <-w.Changes(): if !ok { return nil, watcher.MustErr(w) } config, err := st.EnvironConfig() if err != nil { return nil, err } environ, err := environs.New(config) if err == nil { return environ, nil } logger.Errorf("loaded invalid environment configuration: %v", err) loadedInvalid() } } } // EnvironObserver watches the current environment configuration // and makes it available. It discards invalid environment // configurations. type EnvironObserver struct { tomb tomb.Tomb environWatcher state.NotifyWatcher st *state.State mu sync.Mutex environ environs.Environ } // NewEnvironObserver waits for the state to have a valid environment // configuration and returns a new environment observer. While waiting // for the first environment configuration, it will return with // tomb.ErrDying if it receives a value on dying. func NewEnvironObserver(st *state.State) (*EnvironObserver, error) { config, err := st.EnvironConfig() if err != nil { return nil, err } environ, err := environs.New(config) if err != nil { return nil, fmt.Errorf("cannot make Environ: %v", err) } environWatcher := st.WatchForEnvironConfigChanges() obs := &EnvironObserver{ st: st, environ: environ, environWatcher: environWatcher, } go func() { defer obs.tomb.Done() defer watcher.Stop(environWatcher, &obs.tomb) obs.tomb.Kill(obs.loop()) }() return obs, nil } func (obs *EnvironObserver) loop() error { for { select { case <-obs.tomb.Dying(): return nil case _, ok := <-obs.environWatcher.Changes(): if !ok { return watcher.MustErr(obs.environWatcher) } } config, err := obs.st.EnvironConfig() if err != nil { logger.Warningf("error reading environment config: %v", err) continue } environ, err := environs.New(config) if err != nil { logger.Warningf("error creating Environ: %v", err) continue } obs.mu.Lock() obs.environ = environ obs.mu.Unlock() } } // Environ returns the most recent valid Environ. func (obs *EnvironObserver) Environ() environs.Environ { obs.mu.Lock() defer obs.mu.Unlock() return obs.environ } func (obs *EnvironObserver) Kill() { obs.tomb.Kill(nil) } func (obs *EnvironObserver) Wait() error { return obs.tomb.Wait() } ������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/charmrevisionworker/����������������������������0000755�0000153�0000161�00000000000�12321735643�026302� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/charmrevisionworker/export_test.go��������������0000644�0000153�0000161�00000000232�12321735642�031205� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charmrevisionworker var Interval = &interval ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/charmrevisionworker/revisionupdater.go����������0000644�0000153�0000161�00000003466�12321735642�032064� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charmrevisionworker import ( "fmt" "time" "launchpad.net/tomb" "launchpad.net/juju-core/log" "launchpad.net/juju-core/state/api/charmrevisionupdater" "launchpad.net/juju-core/worker" ) // interval sets how often the resuming is called. var interval = 24 * time.Hour var _ worker.Worker = (*RevisionUpdateWorker)(nil) // RevisionUpdateWorker is responsible for a periodical retrieval of charm versions // from the charm store, and recording the revision status for deployed charms. type RevisionUpdateWorker struct { st *charmrevisionupdater.State tomb tomb.Tomb } // NewRevisionUpdateWorker periodically retrieves charm versions from the charm store. func NewRevisionUpdateWorker(st *charmrevisionupdater.State) *RevisionUpdateWorker { ruw := &RevisionUpdateWorker{st: st} go func() { defer ruw.tomb.Done() ruw.tomb.Kill(ruw.loop()) }() return ruw } func (ruw *RevisionUpdateWorker) String() string { return fmt.Sprintf("charm version lookup worker") } // Stop stops the worker. func (ruw *RevisionUpdateWorker) Stop() error { ruw.tomb.Kill(nil) return ruw.tomb.Wait() } // Kill is defined on the worker.Worker interface. func (ruw *RevisionUpdateWorker) Kill() { ruw.tomb.Kill(nil) } // Wait is defined on the worker.Worker interface. func (ruw *RevisionUpdateWorker) Wait() error { return ruw.tomb.Wait() } func (ruw *RevisionUpdateWorker) loop() error { ruw.updateVersions() for { select { case <-ruw.tomb.Dying(): return tomb.ErrDying case <-time.After(interval): ruw.updateVersions() } } } func (ruw *RevisionUpdateWorker) updateVersions() { if err := ruw.st.UpdateLatestRevisions(); err != nil { log.Errorf("worker/charm revision lookup: cannot process charms: %v", err) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/charmrevisionworker/revisionupdater_test.go�����0000644�0000153�0000161�00000005646�12321735642�033125� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charmrevisionworker_test import ( stdtesting "testing" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/apiserver/charmrevisionupdater/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker/charmrevisionworker" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type RevisionUpdateSuite struct { testing.CharmSuite st *api.State versionUpdater *charmrevisionworker.RevisionUpdateWorker } var _ = gc.Suite(&RevisionUpdateSuite{}) func (s *RevisionUpdateSuite) SetUpSuite(c *gc.C) { c.Assert(*charmrevisionworker.Interval, gc.Equals, 24*time.Hour) s.CharmSuite.SetUpSuite(c) } func (s *RevisionUpdateSuite) SetUpTest(c *gc.C) { s.CharmSuite.SetUpTest(c) machine, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = machine.SetPassword(password) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("i-manager", "fake_nonce", nil) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAsMachine(c, machine.Tag(), password, "fake_nonce") c.Assert(s.st, gc.NotNil) } func (s *RevisionUpdateSuite) runUpdater(c *gc.C, updateInterval time.Duration) { s.PatchValue(charmrevisionworker.Interval, updateInterval) revisionUpdaterState := s.st.CharmRevisionUpdater() c.Assert(revisionUpdaterState, gc.NotNil) s.versionUpdater = charmrevisionworker.NewRevisionUpdateWorker(revisionUpdaterState) s.AddCleanup(func(c *gc.C) { s.versionUpdater.Stop() }) } func (s *RevisionUpdateSuite) checkCharmRevision(c *gc.C, expectedRev int) bool { checkRevision := func() bool { curl := charm.MustParseURL("cs:quantal/mysql") placeholder, err := s.State.LatestPlaceholderCharm(curl) return err == nil && placeholder.String() == curl.WithRevision(expectedRev).String() } success := false for attempt := coretesting.LongAttempt.Start(); attempt.Next(); { if success = checkRevision(); success { break } } return success } func (s *RevisionUpdateSuite) TestVersionUpdateRunsInitially(c *gc.C) { s.SetupScenario(c) // Run the updater with a long update interval to ensure only the initial // update on startup is run. s.runUpdater(c, time.Hour) c.Assert(s.checkCharmRevision(c, 23), jc.IsTrue) } func (s *RevisionUpdateSuite) TestVersionUpdateRunsPeriodically(c *gc.C) { s.SetupScenario(c) // Start the updater and check the initial status. s.runUpdater(c, 5*time.Millisecond) c.Assert(s.checkCharmRevision(c, 23), jc.IsTrue) // Make some changes s.UpdateStoreRevision("cs:quantal/mysql", 24) // Check the results of the latest changes. c.Assert(s.checkCharmRevision(c, 24), jc.IsTrue) } ������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/simpleworker.go���������������������������������0000644�0000153�0000161�00000002477�12321735642�025262� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker // simpleWorker implements the worker returned by NewSimpleWorker. // The stopc and done channels are used for closing notifications // only. No values are sent over them. The err value is set once only, // just before the done channel is closed. type simpleWorker struct { stopc chan struct{} done chan struct{} err error } // NewSimpleWorker returns a worker that runs the given function. The // stopCh argument will be closed when the worker is killed. The error returned // by the doWork function will be returned by the worker's Wait function. func NewSimpleWorker(doWork func(stopCh <-chan struct{}) error) Worker { w := &simpleWorker{ stopc: make(chan struct{}), done: make(chan struct{}), } go func() { w.err = doWork(w.stopc) close(w.done) }() return w } // Kill implements Worker.Kill() and will close the channel given to the doWork // function. func (w *simpleWorker) Kill() { defer func() { // Allow any number of calls to Kill - the second and subsequent calls // will panic, but we don't care. recover() }() close(w.stopc) } // Wait implements Worker.Wait(), and will return the error returned by // the doWork function. func (w *simpleWorker) Wait() error { <-w.done return w.err } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/cleaner/����������������������������������������0000755�0000153�0000161�00000000000�12321735642�023607� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/cleaner/cleaner_test.go�������������������������0000644�0000153�0000161�00000003305�12321735642�026607� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cleaner_test import ( stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/cleaner" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type CleanerSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&CleanerSuite{}) var _ worker.NotifyWatchHandler = (*cleaner.Cleaner)(nil) func (s *CleanerSuite) TestCleaner(c *gc.C) { cr := cleaner.NewCleaner(s.State) defer func() { c.Assert(worker.Stop(cr), gc.IsNil) }() needed, err := s.State.NeedsCleanup() c.Assert(err, gc.IsNil) c.Assert(needed, gc.Equals, false) s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) c.Assert(err, gc.IsNil) relM, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) needed, err = s.State.NeedsCleanup() c.Assert(err, gc.IsNil) c.Assert(needed, gc.Equals, false) // Observe destroying of the relation with a watcher. cw := s.State.WatchCleanups() defer func() { c.Assert(cw.Stop(), gc.IsNil) }() err = relM.Destroy() c.Assert(err, gc.IsNil) timeout := time.After(coretesting.LongWait) for { s.State.StartSync() select { case <-time.After(coretesting.ShortWait): continue case <-timeout: c.Fatalf("timed out waiting for cleanup") case <-cw.Changes(): needed, err = s.State.NeedsCleanup() c.Assert(err, gc.IsNil) if needed { continue } } break } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/cleaner/cleaner.go������������������������������0000644�0000153�0000161�00000002027�12321735642�025550� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cleaner import ( "launchpad.net/juju-core/log" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/worker" ) // Cleaner is responsible for cleaning up the state. type Cleaner struct { st *state.State } // NewCleaner returns a worker.Worker that runs state.Cleanup() // if the CleanupWatcher signals documents marked for deletion. func NewCleaner(st *state.State) worker.Worker { return worker.NewNotifyWorker(&Cleaner{st: st}) } func (c *Cleaner) SetUp() (watcher.NotifyWatcher, error) { return c.st.WatchCleanups(), nil } func (c *Cleaner) Handle() error { if err := c.st.Cleanup(); err != nil { log.Errorf("worker/cleaner: cannot cleanup state: %v", err) } // We do not return the err from Cleanup, because we don't want to stop // the loop as a failure return nil } func (c *Cleaner) TearDown() error { // Nothing to cleanup, only state is the watcher return nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/deployer/���������������������������������������0000755�0000153�0000161�00000000000�12321736000�024006� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/deployer/export_test.go�������������������������0000644�0000153�0000161�00000001211�12321735642�026723� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer import ( "launchpad.net/juju-core/agent" "launchpad.net/juju-core/state/api/params" ) type fakeAPI struct{} func (*fakeAPI) ConnectionInfo() (params.DeployerConnectionValues, error) { return params.DeployerConnectionValues{ StateAddresses: []string{"s1:123", "s2:123"}, APIAddresses: []string{"a1:123", "a2:123"}, }, nil } func NewTestSimpleContext(agentConfig agent.Config, initDir, logDir string) *SimpleContext { return &SimpleContext{ api: &fakeAPI{}, agentConfig: agentConfig, initDir: initDir, } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/deployer/deployer.go����������������������������0000644�0000153�0000161�00000012714�12321735642�026200� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer import ( "fmt" "github.com/juju/loggo" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/names" apideployer "launchpad.net/juju-core/state/api/deployer" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/worker" ) var logger = loggo.GetLogger("juju.worker.deployer") // Deployer is responsible for deploying and recalling unit agents, according // to changes in a set of state units; and for the final removal of its agents' // units from state when they are no longer needed. type Deployer struct { st *apideployer.State ctx Context deployed set.Strings } // Context abstracts away the differences between different unit deployment // strategies; where a Deployer is responsible for what to deploy, a Context // is responsible for how to deploy. type Context interface { // DeployUnit causes the agent for the specified unit to be started and run // continuously until further notice without further intervention. It will // return an error if the agent is already deployed. DeployUnit(unitName, initialPassword string) error // RecallUnit causes the agent for the specified unit to be stopped, and // the agent's data to be destroyed. It will return an error if the agent // was not deployed by the manager. RecallUnit(unitName string) error // DeployedUnits returns the names of all units deployed by the manager. DeployedUnits() ([]string, error) // AgentConfig returns the agent config for the machine agent that is // running the deployer. AgentConfig() agent.Config } // NewDeployer returns a Worker that deploys and recalls unit agents // via ctx, taking a machine id to operate on. func NewDeployer(st *apideployer.State, ctx Context) worker.Worker { d := &Deployer{ st: st, ctx: ctx, } return worker.NewStringsWorker(d) } func (d *Deployer) SetUp() (watcher.StringsWatcher, error) { machineTag := d.ctx.AgentConfig().Tag() machine, err := d.st.Machine(machineTag) if err != nil { return nil, err } machineUnitsWatcher, err := machine.WatchUnits() if err != nil { return nil, err } deployed, err := d.ctx.DeployedUnits() if err != nil { return nil, err } for _, unitName := range deployed { d.deployed.Add(unitName) if err := d.changed(unitName); err != nil { return nil, err } } return machineUnitsWatcher, nil } func (d *Deployer) Handle(unitNames []string) error { for _, unitName := range unitNames { if err := d.changed(unitName); err != nil { return err } } return nil } // changed ensures that the named unit is deployed, recalled, or removed, as // indicated by its state. func (d *Deployer) changed(unitName string) error { unitTag := names.UnitTag(unitName) // Determine unit life state, and whether we're responsible for it. logger.Infof("checking unit %q", unitName) var life params.Life unit, err := d.st.Unit(unitTag) if params.IsCodeNotFoundOrCodeUnauthorized(err) { life = params.Dead } else if err != nil { return err } else { life = unit.Life() } // Deployed units must be removed if they're Dead, or if the deployer // is no longer responsible for them. if d.deployed.Contains(unitName) { if life == params.Dead { if err := d.recall(unitName); err != nil { return err } } } // The only units that should be deployed are those that (1) we are responsible // for and (2) are Alive -- if we're responsible for a Dying unit that is not // yet deployed, we should remove it immediately rather than undergo the hassle // of deploying a unit agent purely so it can set itself to Dead. if !d.deployed.Contains(unitName) { if life == params.Alive { return d.deploy(unit) } else if unit != nil { return d.remove(unit) } } return nil } // deploy will deploy the supplied unit with the deployer's manager. It will // panic if it observes inconsistent internal state. func (d *Deployer) deploy(unit *apideployer.Unit) error { unitName := unit.Name() if d.deployed.Contains(unit.Name()) { panic("must not re-deploy a deployed unit") } logger.Infof("deploying unit %q", unitName) initialPassword, err := utils.RandomPassword() if err != nil { return err } if err := unit.SetPassword(initialPassword); err != nil { return fmt.Errorf("cannot set password for unit %q: %v", unitName, err) } if err := d.ctx.DeployUnit(unitName, initialPassword); err != nil { return err } d.deployed.Add(unitName) return nil } // recall will recall the named unit with the deployer's manager. It will // panic if it observes inconsistent internal state. func (d *Deployer) recall(unitName string) error { if !d.deployed.Contains(unitName) { panic("must not recall a unit that is not deployed") } logger.Infof("recalling unit %q", unitName) if err := d.ctx.RecallUnit(unitName); err != nil { return err } d.deployed.Remove(unitName) return nil } // remove will remove the supplied unit from state. It will panic if it // observes inconsistent internal state. func (d *Deployer) remove(unit *apideployer.Unit) error { unitName := unit.Name() if d.deployed.Contains(unitName) { panic("must not remove a deployed unit") } else if unit.Life() == params.Alive { panic("must not remove an Alive unit") } logger.Infof("removing unit %q", unitName) return unit.Remove() } func (d *Deployer) TearDown() error { // Nothing to do here. return nil } ����������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/deployer/simple_test.go�������������������������0000644�0000153�0000161�00000022430�12321735776�026711� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer_test import ( "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" "regexp" "sort" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/agent/tools" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker/deployer" ) type SimpleContextSuite struct { SimpleToolsFixture } var _ = gc.Suite(&SimpleContextSuite{}) func (s *SimpleContextSuite) SetUpTest(c *gc.C) { s.SimpleToolsFixture.SetUp(c, c.MkDir()) } func (s *SimpleContextSuite) TearDownTest(c *gc.C) { s.SimpleToolsFixture.TearDown(c) } func (s *SimpleContextSuite) TestDeployRecall(c *gc.C) { mgr0 := s.getContext(c) units, err := mgr0.DeployedUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 0) s.assertUpstartCount(c, 0) err = mgr0.DeployUnit("foo/123", "some-password") c.Assert(err, gc.IsNil) units, err = mgr0.DeployedUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.DeepEquals, []string{"foo/123"}) s.assertUpstartCount(c, 1) s.checkUnitInstalled(c, "foo/123", "some-password") err = mgr0.RecallUnit("foo/123") c.Assert(err, gc.IsNil) units, err = mgr0.DeployedUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 0) s.assertUpstartCount(c, 0) s.checkUnitRemoved(c, "foo/123") } func (s *SimpleContextSuite) TestOldDeployedUnitsCanBeRecalled(c *gc.C) { // After r1347 deployer tag is no longer part of the upstart conf filenames, // now only the units' tags are used. This change is with the assumption only // one deployer will be running on a machine (in the machine agent as a task, // unlike before where there was one in the unit agent as well). // This test ensures units deployed previously (or their upstart confs more // specifically) can be detected and recalled by the deployer. manager := s.getContext(c) // No deployed units at first. units, err := manager.DeployedUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 0) s.assertUpstartCount(c, 0) // Trying to recall any units will fail. err = manager.RecallUnit("principal/1") c.Assert(err, gc.ErrorMatches, `unit "principal/1" is not deployed`) // Simulate some previously deployed units with the old // upstart conf filename format (+deployer tags). s.injectUnit(c, "jujud-machine-0:unit-mysql-0.conf", "unit-mysql-0") s.assertUpstartCount(c, 1) s.injectUnit(c, "jujud-unit-wordpress-0:unit-nrpe-0.conf", "unit-nrpe-0") s.assertUpstartCount(c, 2) // Make sure we can discover them. units, err = manager.DeployedUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 2) sort.Strings(units) c.Assert(units, gc.DeepEquals, []string{"mysql/0", "nrpe/0"}) // Deploy some units. err = manager.DeployUnit("principal/1", "some-password") c.Assert(err, gc.IsNil) s.checkUnitInstalled(c, "principal/1", "some-password") s.assertUpstartCount(c, 3) err = manager.DeployUnit("subordinate/2", "fake-password") c.Assert(err, gc.IsNil) s.checkUnitInstalled(c, "subordinate/2", "fake-password") s.assertUpstartCount(c, 4) // Verify the newly deployed units are also discoverable. units, err = manager.DeployedUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 4) sort.Strings(units) c.Assert(units, gc.DeepEquals, []string{"mysql/0", "nrpe/0", "principal/1", "subordinate/2"}) // Recall all of them - should work ok. unitCount := 4 for _, unitName := range units { err = manager.RecallUnit(unitName) c.Assert(err, gc.IsNil) unitCount-- s.checkUnitRemoved(c, unitName) s.assertUpstartCount(c, unitCount) } // Verify they're no longer discoverable. units, err = manager.DeployedUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 0) } type SimpleToolsFixture struct { testbase.LoggingSuite dataDir string logDir string initDir string origPath string binDir string } var fakeJujud = "#!/bin/bash --norc\n# fake-jujud\nexit 0\n" func (fix *SimpleToolsFixture) SetUp(c *gc.C, dataDir string) { fix.LoggingSuite.SetUpTest(c) fix.dataDir = dataDir fix.initDir = c.MkDir() fix.logDir = c.MkDir() toolsDir := tools.SharedToolsDir(fix.dataDir, version.Current) err := os.MkdirAll(toolsDir, 0755) c.Assert(err, gc.IsNil) jujudPath := filepath.Join(toolsDir, "jujud") err = ioutil.WriteFile(jujudPath, []byte(fakeJujud), 0755) c.Assert(err, gc.IsNil) toolsPath := filepath.Join(toolsDir, "downloaded-tools.txt") testTools := coretools.Tools{Version: version.Current, URL: "http://testing.invalid/tools"} data, err := json.Marshal(testTools) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(toolsPath, data, 0644) c.Assert(err, gc.IsNil) fix.binDir = c.MkDir() fix.origPath = os.Getenv("PATH") os.Setenv("PATH", fix.binDir+":"+fix.origPath) fix.makeBin(c, "status", `echo "blah stop/waiting"`) fix.makeBin(c, "stopped-status", `echo "blah stop/waiting"`) fix.makeBin(c, "started-status", `echo "blah start/running, process 666"`) fix.makeBin(c, "start", "cp $(which started-status) $(which status)") fix.makeBin(c, "stop", "cp $(which stopped-status) $(which status)") } func (fix *SimpleToolsFixture) TearDown(c *gc.C) { os.Setenv("PATH", fix.origPath) fix.LoggingSuite.TearDownTest(c) } func (fix *SimpleToolsFixture) makeBin(c *gc.C, name, script string) { path := filepath.Join(fix.binDir, name) err := ioutil.WriteFile(path, []byte("#!/bin/bash --norc\n"+script), 0755) c.Assert(err, gc.IsNil) } func (fix *SimpleToolsFixture) assertUpstartCount(c *gc.C, count int) { fis, err := ioutil.ReadDir(fix.initDir) c.Assert(err, gc.IsNil) c.Assert(fis, gc.HasLen, count) } func (fix *SimpleToolsFixture) getContext(c *gc.C) *deployer.SimpleContext { config := agentConfig("machine-tag", fix.dataDir, fix.logDir) return deployer.NewTestSimpleContext(config, fix.initDir, fix.logDir) } func (fix *SimpleToolsFixture) getContextForMachine(c *gc.C, machineTag string) *deployer.SimpleContext { config := agentConfig(machineTag, fix.dataDir, fix.logDir) return deployer.NewTestSimpleContext(config, fix.initDir, fix.logDir) } func (fix *SimpleToolsFixture) paths(tag string) (confPath, agentDir, toolsDir string) { confName := fmt.Sprintf("jujud-%s.conf", tag) confPath = filepath.Join(fix.initDir, confName) agentDir = agent.Dir(fix.dataDir, tag) toolsDir = tools.ToolsDir(fix.dataDir, tag) return } func (fix *SimpleToolsFixture) checkUnitInstalled(c *gc.C, name, password string) { tag := names.UnitTag(name) uconfPath, _, toolsDir := fix.paths(tag) uconfData, err := ioutil.ReadFile(uconfPath) c.Assert(err, gc.IsNil) uconf := string(uconfData) var execLine string for _, line := range strings.Split(uconf, "\n") { if strings.HasPrefix(line, "exec ") { execLine = line break } } if execLine == "" { c.Fatalf("no command found in %s:\n%s", uconfPath, uconf) } logPath := filepath.Join(fix.logDir, tag+".log") jujudPath := filepath.Join(toolsDir, "jujud") for _, pat := range []string{ "^exec " + jujudPath + " unit ", " --unit-name " + name + " ", " >> " + logPath + " 2>&1$", } { match, err := regexp.MatchString(pat, execLine) c.Assert(err, gc.IsNil) if !match { c.Fatalf("failed to match:\n%s\nin:\n%s", pat, execLine) } } conf, err := agent.ReadConf(agent.ConfigPath(fix.dataDir, tag)) c.Assert(err, gc.IsNil) c.Assert(conf.Tag(), gc.Equals, tag) c.Assert(conf.DataDir(), gc.Equals, fix.dataDir) jujudData, err := ioutil.ReadFile(jujudPath) c.Assert(err, gc.IsNil) c.Assert(string(jujudData), gc.Equals, fakeJujud) } func (fix *SimpleToolsFixture) checkUnitRemoved(c *gc.C, name string) { tag := names.UnitTag(name) confPath, agentDir, toolsDir := fix.paths(tag) for _, path := range []string{confPath, agentDir, toolsDir} { _, err := ioutil.ReadFile(path) if err == nil { c.Log("Warning: %q not removed as expected", path) } else { c.Assert(err, jc.Satisfies, os.IsNotExist) } } } func (fix *SimpleToolsFixture) injectUnit(c *gc.C, upstartConf, unitTag string) { confPath := filepath.Join(fix.initDir, upstartConf) err := ioutil.WriteFile(confPath, []byte("#!/bin/bash --norc\necho $0"), 0644) c.Assert(err, gc.IsNil) toolsDir := filepath.Join(fix.dataDir, "tools", unitTag) err = os.MkdirAll(toolsDir, 0755) c.Assert(err, gc.IsNil) } type mockConfig struct { agent.Config tag string datadir string logdir string upgradedToVersion version.Number jobs []params.MachineJob } func (mock *mockConfig) Tag() string { return mock.tag } func (mock *mockConfig) DataDir() string { return mock.datadir } func (mock *mockConfig) LogDir() string { return mock.logdir } func (mock *mockConfig) Jobs() []params.MachineJob { return mock.jobs } func (mock *mockConfig) UpgradedToVersion() version.Number { return mock.upgradedToVersion } func (mock *mockConfig) WriteUpgradedToVersion(newVersion version.Number) error { mock.upgradedToVersion = newVersion return nil } func (mock *mockConfig) CACert() []byte { return []byte(testing.CACert) } func (mock *mockConfig) Value(_ string) string { return "" } func agentConfig(tag, datadir, logdir string) agent.Config { return &mockConfig{tag: tag, datadir: datadir, logdir: logdir} } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/deployer/deployer_test.go�����������������������0000644�0000153�0000161�00000017272�12321735642�027243� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer_test import ( "sort" "strings" stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" apideployer "launchpad.net/juju-core/state/api/deployer" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/deployer" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type deployerSuite struct { jujutesting.JujuConnSuite SimpleToolsFixture machine *state.Machine stateAPI *api.State deployerState *apideployer.State } var _ = gc.Suite(&deployerSuite{}) var _ worker.StringsWatchHandler = (*deployer.Deployer)(nil) func (s *deployerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.SimpleToolsFixture.SetUp(c, s.DataDir()) s.stateAPI, s.machine = s.OpenAPIAsNewMachine(c) // Create the deployer facade. s.deployerState = s.stateAPI.Deployer() c.Assert(s.deployerState, gc.NotNil) } func (s *deployerSuite) TearDownTest(c *gc.C) { s.SimpleToolsFixture.TearDown(c) s.JujuConnSuite.TearDownTest(c) } func (s *deployerSuite) makeDeployerAndContext(c *gc.C) (worker.Worker, deployer.Context) { // Create a deployer acting on behalf of the machine. ctx := s.getContextForMachine(c, s.machine.Tag()) return deployer.NewDeployer(s.deployerState, ctx), ctx } func (s *deployerSuite) TestDeployRecallRemovePrincipals(c *gc.C) { // Create a machine, and a couple of units. svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) u0, err := svc.AddUnit() c.Assert(err, gc.IsNil) u1, err := svc.AddUnit() c.Assert(err, gc.IsNil) dep, ctx := s.makeDeployerAndContext(c) defer stop(c, dep) // Assign one unit, and wait for it to be deployed. err = u0.AssignToMachine(s.machine) c.Assert(err, gc.IsNil) s.waitFor(c, isDeployed(ctx, u0.Name())) // Assign another unit, and wait for that to be deployed. err = u1.AssignToMachine(s.machine) c.Assert(err, gc.IsNil) s.waitFor(c, isDeployed(ctx, u0.Name(), u1.Name())) // Cause a unit to become Dying, and check no change. err = u1.SetStatus(params.StatusInstalled, "", nil) c.Assert(err, gc.IsNil) err = u1.Destroy() c.Assert(err, gc.IsNil) s.waitFor(c, isDeployed(ctx, u0.Name(), u1.Name())) // Cause a unit to become Dead, and check that it is both recalled and // removed from state. err = u0.EnsureDead() c.Assert(err, gc.IsNil) s.waitFor(c, isRemoved(s.State, u0.Name())) s.waitFor(c, isDeployed(ctx, u1.Name())) // Remove the Dying unit from the machine, and check that it is recalled... err = u1.UnassignFromMachine() c.Assert(err, gc.IsNil) s.waitFor(c, isDeployed(ctx)) // ...and that the deployer, no longer bearing any responsibility for the // Dying unit, does nothing further to it. err = u1.Refresh() c.Assert(err, gc.IsNil) c.Assert(u1.Life(), gc.Equals, state.Dying) } func (s *deployerSuite) TestRemoveNonAlivePrincipals(c *gc.C) { // Create a service, and a couple of units. svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) u0, err := svc.AddUnit() c.Assert(err, gc.IsNil) u1, err := svc.AddUnit() c.Assert(err, gc.IsNil) // Assign the units to the machine, and set them to Dying/Dead. err = u0.AssignToMachine(s.machine) c.Assert(err, gc.IsNil) err = u0.EnsureDead() c.Assert(err, gc.IsNil) err = u1.AssignToMachine(s.machine) c.Assert(err, gc.IsNil) // note: this is not a sane state; for the unit to have a status it must // have been deployed. But it's instructive to check that the right thing // would happen if it were possible to have a dying unit in this situation. err = u1.SetStatus(params.StatusInstalled, "", nil) c.Assert(err, gc.IsNil) err = u1.Destroy() c.Assert(err, gc.IsNil) // When the deployer is started, in each case (1) no unit agent is deployed // and (2) the non-Alive unit is been removed from state. dep, ctx := s.makeDeployerAndContext(c) defer stop(c, dep) s.waitFor(c, isRemoved(s.State, u0.Name())) s.waitFor(c, isRemoved(s.State, u1.Name())) s.waitFor(c, isDeployed(ctx)) } func (s *deployerSuite) prepareSubordinates(c *gc.C) (*state.Unit, []*state.RelationUnit) { svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) u, err := svc.AddUnit() c.Assert(err, gc.IsNil) err = u.AssignToMachine(s.machine) c.Assert(err, gc.IsNil) rus := []*state.RelationUnit{} logging := s.AddTestingCharm(c, "logging") for _, name := range []string{"subsvc0", "subsvc1"} { s.AddTestingService(c, name, logging) eps, err := s.State.InferEndpoints([]string{"wordpress", name}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(u) c.Assert(err, gc.IsNil) rus = append(rus, ru) } return u, rus } func (s *deployerSuite) TestDeployRecallRemoveSubordinates(c *gc.C) { // Create a deployer acting on behalf of the principal. u, rus := s.prepareSubordinates(c) dep, ctx := s.makeDeployerAndContext(c) defer stop(c, dep) // Add a subordinate, and wait for it to be deployed. err := rus[0].EnterScope(nil) c.Assert(err, gc.IsNil) sub0, err := s.State.Unit("subsvc0/0") c.Assert(err, gc.IsNil) // Make sure the principal is deployed first, then the subordinate s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name())) // And another. err = rus[1].EnterScope(nil) c.Assert(err, gc.IsNil) sub1, err := s.State.Unit("subsvc1/0") c.Assert(err, gc.IsNil) s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name())) // Set one to Dying; check nothing happens. err = sub1.Destroy() c.Assert(err, gc.IsNil) s.State.StartSync() c.Assert(isRemoved(s.State, sub1.Name())(c), gc.Equals, false) s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name())) // Set the other to Dead; check it's recalled and removed. err = sub0.EnsureDead() c.Assert(err, gc.IsNil) s.waitFor(c, isDeployed(ctx, u.Name(), sub1.Name())) s.waitFor(c, isRemoved(s.State, sub0.Name())) } func (s *deployerSuite) TestNonAliveSubordinates(c *gc.C) { // Add two subordinate units and set them to Dead/Dying respectively. _, rus := s.prepareSubordinates(c) err := rus[0].EnterScope(nil) c.Assert(err, gc.IsNil) sub0, err := s.State.Unit("subsvc0/0") c.Assert(err, gc.IsNil) err = sub0.EnsureDead() c.Assert(err, gc.IsNil) err = rus[1].EnterScope(nil) c.Assert(err, gc.IsNil) sub1, err := s.State.Unit("subsvc1/0") c.Assert(err, gc.IsNil) err = sub1.Destroy() c.Assert(err, gc.IsNil) // When we start a new deployer, neither unit will be deployed and // both will be removed. dep, _ := s.makeDeployerAndContext(c) defer stop(c, dep) s.waitFor(c, isRemoved(s.State, sub0.Name())) s.waitFor(c, isRemoved(s.State, sub1.Name())) } func (s *deployerSuite) waitFor(c *gc.C, t func(c *gc.C) bool) { s.BackingState.StartSync() if t(c) { return } timeout := time.After(coretesting.LongWait) for { select { case <-timeout: c.Fatalf("timeout") case <-time.After(coretesting.ShortWait): if t(c) { return } } } } func isDeployed(ctx deployer.Context, expected ...string) func(*gc.C) bool { return func(c *gc.C) bool { sort.Strings(expected) current, err := ctx.DeployedUnits() c.Assert(err, gc.IsNil) sort.Strings(current) return strings.Join(expected, ":") == strings.Join(current, ":") } } func isRemoved(st *state.State, name string) func(*gc.C) bool { return func(c *gc.C) bool { _, err := st.Unit(name) if errors.IsNotFoundError(err) { return true } c.Assert(err, gc.IsNil) return false } } func stop(c *gc.C, w worker.Worker) { c.Assert(worker.Stop(w), gc.IsNil) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/deployer/simple.go������������������������������0000644�0000153�0000161�00000014302�12321735642�025641� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer import ( "fmt" "io/ioutil" "os" "path" "regexp" "strings" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/agent/tools" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/upstart" "launchpad.net/juju-core/version" ) // InitDir is the default upstart init directory. // This is a var so it can be overridden by tests. var InitDir = "/etc/init" // APICalls defines the interface to the API that the simple context needs. type APICalls interface { ConnectionInfo() (params.DeployerConnectionValues, error) } // SimpleContext is a Context that manages unit deployments via upstart // jobs on the local system. type SimpleContext struct { // api is used to get the current state server addresses at the time the // given unit is deployed. api APICalls // agentConfig returns the agent config for the machine agent that is // running the deployer. agentConfig agent.Config // initDir specifies the directory used by upstart on the local system. // It is typically set to "/etc/init". initDir string } var _ Context = (*SimpleContext)(nil) // NewSimpleContext returns a new SimpleContext, acting on behalf of // the specified deployer, that deploys unit agents as upstart jobs in // "/etc/init". Paths to which agents and tools are installed are // relative to dataDir. func NewSimpleContext(agentConfig agent.Config, api APICalls) *SimpleContext { return &SimpleContext{ api: api, agentConfig: agentConfig, initDir: InitDir, } } func (ctx *SimpleContext) AgentConfig() agent.Config { return ctx.agentConfig } func (ctx *SimpleContext) DeployUnit(unitName, initialPassword string) (err error) { // Check sanity. svc := ctx.upstartService(unitName) if svc.Installed() { return fmt.Errorf("unit %q is already deployed", unitName) } // Link the current tools for use by the new agent. tag := names.UnitTag(unitName) dataDir := ctx.agentConfig.DataDir() logDir := ctx.agentConfig.LogDir() _, err = tools.ChangeAgentTools(dataDir, tag, version.Current) toolsDir := tools.ToolsDir(dataDir, tag) defer removeOnErr(&err, toolsDir) result, err := ctx.api.ConnectionInfo() if err != nil { return err } logger.Debugf("state addresses: %q", result.StateAddresses) logger.Debugf("API addresses: %q", result.APIAddresses) containerType := ctx.agentConfig.Value(agent.ContainerType) namespace := ctx.agentConfig.Value(agent.Namespace) conf, err := agent.NewAgentConfig( agent.AgentConfigParams{ DataDir: dataDir, LogDir: logDir, UpgradedToVersion: version.Current.Number, Tag: tag, Password: initialPassword, Nonce: "unused", // TODO: remove the state addresses here and test when api only. StateAddresses: result.StateAddresses, APIAddresses: result.APIAddresses, CACert: ctx.agentConfig.CACert(), Values: map[string]string{ agent.ContainerType: containerType, agent.Namespace: namespace, }, }) if err != nil { return err } if err := conf.Write(); err != nil { return err } defer removeOnErr(&err, conf.Dir()) // Install an upstart job that runs the unit agent. logPath := path.Join(logDir, tag+".log") cmd := strings.Join([]string{ path.Join(toolsDir, "jujud"), "unit", "--data-dir", dataDir, "--unit-name", unitName, "--debug", // TODO: propagate debug state sensibly }, " ") // TODO(thumper): 2013-09-02 bug 1219630 // As much as I'd like to remove JujuContainerType now, it is still // needed as MAAS still needs it at this stage, and we can't fix // everything at once. uconf := &upstart.Conf{ Service: *svc, Desc: "juju unit agent for " + unitName, Cmd: cmd, Out: logPath, Env: map[string]string{ osenv.JujuContainerTypeEnvKey: containerType, }, } return uconf.Install() } // findUpstartJob tries to find an upstart job matching the // given unit name in one of these formats: // jujud-<deployer-tag>:<unit-tag>.conf (for compatibility) // jujud-<unit-tag>.conf (default) func (ctx *SimpleContext) findUpstartJob(unitName string) *upstart.Service { unitsAndJobs, err := ctx.deployedUnitsUpstartJobs() if err != nil { return nil } if job, ok := unitsAndJobs[unitName]; ok { svc := upstart.NewService(job) svc.InitDir = ctx.initDir return svc } return nil } func (ctx *SimpleContext) RecallUnit(unitName string) error { svc := ctx.findUpstartJob(unitName) if svc == nil || !svc.Installed() { return fmt.Errorf("unit %q is not deployed", unitName) } if err := svc.StopAndRemove(); err != nil { return err } tag := names.UnitTag(unitName) dataDir := ctx.agentConfig.DataDir() agentDir := agent.Dir(dataDir, tag) if err := os.RemoveAll(agentDir); err != nil { return err } toolsDir := tools.ToolsDir(dataDir, tag) return os.Remove(toolsDir) } var deployedRe = regexp.MustCompile("^(jujud-.*unit-([a-z0-9-]+)-([0-9]+))\\.conf$") func (ctx *SimpleContext) deployedUnitsUpstartJobs() (map[string]string, error) { fis, err := ioutil.ReadDir(ctx.initDir) if err != nil { return nil, err } installed := make(map[string]string) for _, fi := range fis { if groups := deployedRe.FindStringSubmatch(fi.Name()); len(groups) == 4 { unitName := groups[2] + "/" + groups[3] if !names.IsUnit(unitName) { continue } installed[unitName] = groups[1] } } return installed, nil } func (ctx *SimpleContext) DeployedUnits() ([]string, error) { unitsAndJobs, err := ctx.deployedUnitsUpstartJobs() if err != nil { return nil, err } var installed []string for unitName := range unitsAndJobs { installed = append(installed, unitName) } return installed, nil } // upstartService returns an upstart.Service corresponding to the specified // unit. func (ctx *SimpleContext) upstartService(unitName string) *upstart.Service { tag := names.UnitTag(unitName) svcName := "jujud-" + tag svc := upstart.NewService(svcName) svc.InitDir = ctx.initDir return svc } func removeOnErr(err *error, path string) { if *err != nil { if err := os.Remove(path); err != nil { logger.Warningf("installer: cannot remove %q: %v", path, err) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/runner.go���������������������������������������0000644�0000153�0000161�00000015754�12321735642�024052� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker import ( "errors" "time" "launchpad.net/tomb" "launchpad.net/juju-core/log" ) // RestartDelay holds the length of time that a worker // will wait between exiting and restarting. var RestartDelay = 3 * time.Second // Worker is implemented by a running worker. type Worker interface { // Kill asks the worker to stop without necessarily // waiting for it to do so. Kill() // Wait waits for the worker to exit and returns any // error encountered when it was running. Wait() error } // Runner is implemented by instances capable of starting and stopping workers. type Runner interface { Worker StartWorker(id string, startFunc func() (Worker, error)) error StopWorker(id string) error } // runner runs a set of workers, restarting them as necessary // when they fail. type runner struct { tomb tomb.Tomb startc chan startReq stopc chan string donec chan doneInfo startedc chan startInfo isFatal func(error) bool moreImportant func(err0, err1 error) bool } var _ Runner = (*runner)(nil) type startReq struct { id string start func() (Worker, error) } type startInfo struct { id string worker Worker } type doneInfo struct { id string err error } // NewRunner creates a new Runner. When a worker finishes, if its error // is deemed fatal (determined by calling isFatal), all the other workers // will be stopped and the runner itself will finish. Of all the fatal errors // returned by the stopped workers, only the most important one, // determined by calling moreImportant, will be returned from // Runner.Wait. Non-fatal errors will not be returned. // // The function isFatal(err) returns whether err is a fatal error. The // function moreImportant(err0, err1) returns whether err0 is considered // more important than err1. func NewRunner(isFatal func(error) bool, moreImportant func(err0, err1 error) bool) Runner { runner := &runner{ startc: make(chan startReq), stopc: make(chan string), donec: make(chan doneInfo), startedc: make(chan startInfo), isFatal: isFatal, moreImportant: moreImportant, } go func() { defer runner.tomb.Done() runner.tomb.Kill(runner.run()) }() return runner } var ErrDead = errors.New("worker runner is not running") // StartWorker starts a worker running associated with the given id. // The startFunc function will be called to create the worker; // when the worker exits, it will be restarted as long as it // does not return a fatal error. // // If there is already a worker with the given id, nothing will be done. // // StartWorker returns ErrDead if the runner is not running. func (runner *runner) StartWorker(id string, startFunc func() (Worker, error)) error { select { case runner.startc <- startReq{id, startFunc}: return nil case <-runner.tomb.Dead(): } return ErrDead } // StopWorker stops the worker associated with the given id. // It does nothing if there is no such worker. // // StopWorker returns ErrDead if the runner is not running. func (runner *runner) StopWorker(id string) error { select { case runner.stopc <- id: return nil case <-runner.tomb.Dead(): } return ErrDead } func (runner *runner) Wait() error { return runner.tomb.Wait() } func (runner *runner) Kill() { log.Debugf("worker: killing runner %p", runner) runner.tomb.Kill(nil) } // Stop kills the given worker and waits for it to exit. func Stop(worker Worker) error { worker.Kill() return worker.Wait() } type workerInfo struct { start func() (Worker, error) worker Worker restartDelay time.Duration stopping bool } func (runner *runner) run() error { // workers holds the current set of workers. All workers with a // running goroutine have an entry here. workers := make(map[string]*workerInfo) var finalError error // isDying holds whether the runner is currently dying. When it // is dying (whether as a result of being killed or due to a // fatal error), all existing workers are killed, no new workers // will be started, and the loop will exit when all existing // workers have stopped. isDying := false tombDying := runner.tomb.Dying() for { if isDying && len(workers) == 0 { return finalError } select { case <-tombDying: log.Infof("worker: runner is dying") isDying = true killAll(workers) tombDying = nil case req := <-runner.startc: if isDying { log.Infof("worker: ignoring start request for %q when dying", req.id) break } info := workers[req.id] if info == nil { workers[req.id] = &workerInfo{ start: req.start, restartDelay: RestartDelay, } go runner.runWorker(0, req.id, req.start) break } if !info.stopping { // The worker is already running, so leave it alone break } // The worker previously existed and is // currently being stopped. When it eventually // does stop, we'll restart it immediately with // the new start function. info.start = req.start info.restartDelay = 0 case id := <-runner.stopc: if info := workers[id]; info != nil { killWorker(id, info) } case info := <-runner.startedc: workerInfo := workers[info.id] workerInfo.worker = info.worker if isDying { killWorker(info.id, workerInfo) } case info := <-runner.donec: workerInfo := workers[info.id] if !workerInfo.stopping && info.err == nil { info.err = errors.New("unexpected quit") } if info.err != nil { if runner.isFatal(info.err) { log.Errorf("worker: fatal %q: %v", info.id, info.err) if finalError == nil || runner.moreImportant(info.err, finalError) { finalError = info.err } delete(workers, info.id) if !isDying { isDying = true killAll(workers) } break } else { log.Errorf("worker: exited %q: %v", info.id, info.err) } } if workerInfo.start == nil { // The worker has been deliberately stopped; // we can now remove it from the list of workers. delete(workers, info.id) break } go runner.runWorker(workerInfo.restartDelay, info.id, workerInfo.start) workerInfo.restartDelay = RestartDelay } } } func killAll(workers map[string]*workerInfo) { for id, info := range workers { killWorker(id, info) } } func killWorker(id string, info *workerInfo) { if info.worker != nil { log.Debugf("worker: killing %q", id) info.worker.Kill() info.worker = nil } info.stopping = true info.start = nil } // runWorker starts the given worker after waiting for the given delay. func (runner *runner) runWorker(delay time.Duration, id string, start func() (Worker, error)) { if delay > 0 { log.Infof("worker: restarting %q in %v", id, delay) select { case <-runner.tomb.Dying(): runner.donec <- doneInfo{id, nil} return case <-time.After(delay): } } log.Infof("worker: start %q", id) worker, err := start() if err == nil { runner.startedc <- startInfo{id, worker} err = worker.Wait() } runner.donec <- doneInfo{id, err} } ��������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/firewaller/�������������������������������������0000755�0000153�0000161�00000000000�12321735642�024332� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/firewaller/firewaller.go������������������������0000644�0000153�0000161�00000051262�12321735642�027023� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller import ( "github.com/errgo/errgo" "github.com/juju/loggo" "launchpad.net/tomb" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" apifirewaller "launchpad.net/juju-core/state/api/firewaller" "launchpad.net/juju-core/state/api/params" apiwatcher "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/worker" ) var logger = loggo.GetLogger("juju.worker.firewaller") // Firewaller watches the state for ports opened or closed // and reflects those changes onto the backing environment. type Firewaller struct { tomb tomb.Tomb st *apifirewaller.State environ environs.Environ environWatcher apiwatcher.NotifyWatcher machinesWatcher apiwatcher.StringsWatcher machineds map[string]*machineData unitsChange chan *unitsChange unitds map[string]*unitData portsChange chan *portsChange serviceds map[string]*serviceData exposedChange chan *exposedChange globalMode bool globalPortRef map[instance.Port]int } // NewFirewaller returns a new Firewaller. func NewFirewaller(st *apifirewaller.State) (*Firewaller, error) { environWatcher, err := st.WatchForEnvironConfigChanges() if err != nil { return nil, err } machinesWatcher, err := st.WatchEnvironMachines() if err != nil { return nil, err } fw := &Firewaller{ st: st, environWatcher: environWatcher, machinesWatcher: machinesWatcher, machineds: make(map[string]*machineData), unitsChange: make(chan *unitsChange), unitds: make(map[string]*unitData), portsChange: make(chan *portsChange), serviceds: make(map[string]*serviceData), exposedChange: make(chan *exposedChange), } go func() { defer fw.tomb.Done() fw.tomb.Kill(fw.loop()) }() return fw, nil } func (fw *Firewaller) loop() error { defer fw.stopWatchers() var err error var reconciled bool fw.environ, err = worker.WaitForEnviron(fw.environWatcher, fw.st, fw.tomb.Dying()) if err != nil { return err } if fw.environ.Config().FirewallMode() == config.FwGlobal { fw.globalMode = true fw.globalPortRef = make(map[instance.Port]int) } for { select { case <-fw.tomb.Dying(): return tomb.ErrDying case _, ok := <-fw.environWatcher.Changes(): if !ok { return watcher.MustErr(fw.environWatcher) } config, err := fw.st.EnvironConfig() if err != nil { return err } if err := fw.environ.SetConfig(config); err != nil { logger.Errorf("loaded invalid environment configuration: %v", err) } case change, ok := <-fw.machinesWatcher.Changes(): if !ok { return watcher.MustErr(fw.machinesWatcher) } for _, machineId := range change { fw.machineLifeChanged(names.MachineTag(machineId)) } if !reconciled { reconciled = true var err error if fw.globalMode { err = fw.reconcileGlobal() } else { err = fw.reconcileInstances() } if err != nil { return err } } case change := <-fw.unitsChange: if err := fw.unitsChanged(change); err != nil { return err } case change := <-fw.portsChange: change.unitd.ports = change.ports if err := fw.flushUnits([]*unitData{change.unitd}); err != nil { return errgo.Annotate(err, "cannot change firewall ports") } case change := <-fw.exposedChange: change.serviced.exposed = change.exposed unitds := []*unitData{} for _, unitd := range change.serviced.unitds { unitds = append(unitds, unitd) } if err := fw.flushUnits(unitds); err != nil { return errgo.Annotate(err, "cannot change firewall ports") } } } } // stop a watcher with logging of a possible error. func stop(what string, stopper watcher.Stopper) { if err := stopper.Stop(); err != nil { logger.Errorf("error stopping %s: %v", what, err) } } // startMachine creates a new data value for tracking details of the // machine and starts watching the machine for units added or removed. func (fw *Firewaller) startMachine(tag string) error { machined := &machineData{ fw: fw, tag: tag, unitds: make(map[string]*unitData), ports: make([]instance.Port, 0), } m, err := machined.machine() if params.IsCodeNotFound(err) { return nil } else if err != nil { return errgo.Annotate(err, "cannot watch machine units") } unitw, err := m.WatchUnits() if err != nil { return err } select { case <-fw.tomb.Dying(): stop("units watcher", unitw) return tomb.ErrDying case change, ok := <-unitw.Changes(): if !ok { stop("units watcher", unitw) return watcher.MustErr(unitw) } fw.machineds[tag] = machined err = fw.unitsChanged(&unitsChange{machined, change}) if err != nil { stop("units watcher", unitw) delete(fw.machineds, tag) return errgo.Annotatef(err, "cannot respond to units changes for %q", tag) } } go machined.watchLoop(unitw) return nil } // startUnit creates a new data value for tracking details of the unit // and starts watching the unit for port changes. The provided // machineTag must be the tag for the machine the unit was last // observed to be assigned to. func (fw *Firewaller) startUnit(unit *apifirewaller.Unit, machineTag string) error { service, err := unit.Service() if err != nil { return err } serviceName := service.Name() unitName := unit.Name() openedPorts, err := unit.OpenedPorts() if err != nil { return err } unitd := &unitData{ fw: fw, unit: unit, ports: openedPorts, } fw.unitds[unitName] = unitd unitd.machined = fw.machineds[machineTag] unitd.machined.unitds[unitName] = unitd if fw.serviceds[serviceName] == nil { err := fw.startService(service) if err != nil { delete(fw.unitds, unitName) return err } } unitd.serviced = fw.serviceds[serviceName] unitd.serviced.unitds[unitName] = unitd ports := make([]instance.Port, len(unitd.ports)) copy(ports, unitd.ports) go unitd.watchLoop(ports) return nil } // startService creates a new data value for tracking details of the // service and starts watching the service for exposure changes. func (fw *Firewaller) startService(service *apifirewaller.Service) error { exposed, err := service.IsExposed() if err != nil { return err } serviced := &serviceData{ fw: fw, service: service, exposed: exposed, unitds: make(map[string]*unitData), } fw.serviceds[service.Name()] = serviced go serviced.watchLoop(serviced.exposed) return nil } // reconcileGlobal compares the initially started watcher for machines, // units and services with the opened and closed ports globally and // opens and closes the appropriate ports for the whole environment. func (fw *Firewaller) reconcileGlobal() error { initialPorts, err := fw.environ.Ports() if err != nil { return err } collector := make(map[instance.Port]bool) for _, unitd := range fw.unitds { if unitd.serviced.exposed { for _, port := range unitd.ports { collector[port] = true } } } wantedPorts := []instance.Port{} for port := range collector { wantedPorts = append(wantedPorts, port) } // Check which ports to open or to close. toOpen := Diff(wantedPorts, initialPorts) toClose := Diff(initialPorts, wantedPorts) if len(toOpen) > 0 { logger.Infof("opening global ports %v", toOpen) if err := fw.environ.OpenPorts(toOpen); err != nil { return err } instance.SortPorts(toOpen) } if len(toClose) > 0 { logger.Infof("closing global ports %v", toClose) if err := fw.environ.ClosePorts(toClose); err != nil { return err } instance.SortPorts(toClose) } return nil } // reconcileInstances compares the initially started watcher for machines, // units and services with the opened and closed ports of the instances and // opens and closes the appropriate ports for each instance. func (fw *Firewaller) reconcileInstances() error { for _, machined := range fw.machineds { m, err := machined.machine() if params.IsCodeNotFound(err) { if err := fw.forgetMachine(machined); err != nil { return err } continue } else if err != nil { return err } instanceId, err := m.InstanceId() if err != nil { return err } instances, err := fw.environ.Instances([]instance.Id{instanceId}) if err == environs.ErrNoInstances { return nil } else if err != nil { return err } _, machineId, err := names.ParseTag(machined.tag, names.MachineTagKind) if err != nil { return err } initialPorts, err := instances[0].Ports(machineId) if err != nil { return err } // Check which ports to open or to close. toOpen := Diff(machined.ports, initialPorts) toClose := Diff(initialPorts, machined.ports) if len(toOpen) > 0 { logger.Infof("opening instance ports %v for %q", toOpen, machined.tag) if err := instances[0].OpenPorts(machineId, toOpen); err != nil { // TODO(mue) Add local retry logic. return err } instance.SortPorts(toOpen) } if len(toClose) > 0 { logger.Infof("closing instance ports %v for %q", toClose, machined.tag) if err := instances[0].ClosePorts(machineId, toClose); err != nil { // TODO(mue) Add local retry logic. return err } instance.SortPorts(toClose) } } return nil } // unitsChanged responds to changes to the assigned units. func (fw *Firewaller) unitsChanged(change *unitsChange) error { changed := []*unitData{} for _, name := range change.units { unit, err := fw.st.Unit(names.UnitTag(name)) if err != nil && !params.IsCodeNotFound(err) { return err } var machineTag string if unit != nil { machineTag, err = unit.AssignedMachine() if params.IsCodeNotFound(err) { continue } else if err != nil && !params.IsCodeNotAssigned(err) { return err } } if unitd, known := fw.unitds[name]; known { knownMachineTag := fw.unitds[name].machined.tag if unit == nil || unit.Life() == params.Dead || machineTag != knownMachineTag { fw.forgetUnit(unitd) changed = append(changed, unitd) logger.Debugf("stopped watching unit %s", name) } } else if unit != nil && unit.Life() != params.Dead && fw.machineds[machineTag] != nil { err = fw.startUnit(unit, machineTag) if err != nil { return err } changed = append(changed, fw.unitds[name]) logger.Debugf("started watching unit %s", name) } } if err := fw.flushUnits(changed); err != nil { return errgo.Annotate(err, "cannot change firewall ports") } return nil } // flushUnits opens and closes ports for the passed unit data. func (fw *Firewaller) flushUnits(unitds []*unitData) error { machineds := map[string]*machineData{} for _, unitd := range unitds { machineds[unitd.machined.tag] = unitd.machined } for _, machined := range machineds { if err := fw.flushMachine(machined); err != nil { return err } } return nil } // flushMachine opens and closes ports for the passed machine. func (fw *Firewaller) flushMachine(machined *machineData) error { // Gather ports to open and close. ports := map[instance.Port]bool{} for _, unitd := range machined.unitds { if unitd.serviced.exposed { for _, port := range unitd.ports { ports[port] = true } } } want := []instance.Port{} for port := range ports { want = append(want, port) } toOpen := Diff(want, machined.ports) toClose := Diff(machined.ports, want) machined.ports = want if fw.globalMode { return fw.flushGlobalPorts(toOpen, toClose) } return fw.flushInstancePorts(machined, toOpen, toClose) } // flushGlobalPorts opens and closes global ports in the environment. // It keeps a reference count for ports so that only 0-to-1 and 1-to-0 events // modify the environment. func (fw *Firewaller) flushGlobalPorts(rawOpen, rawClose []instance.Port) error { // Filter which ports are really to open or close. var toOpen, toClose []instance.Port for _, port := range rawOpen { if fw.globalPortRef[port] == 0 { toOpen = append(toOpen, port) } fw.globalPortRef[port]++ } for _, port := range rawClose { fw.globalPortRef[port]-- if fw.globalPortRef[port] == 0 { toClose = append(toClose, port) delete(fw.globalPortRef, port) } } // Open and close the ports. if len(toOpen) > 0 { if err := fw.environ.OpenPorts(toOpen); err != nil { // TODO(mue) Add local retry logic. return err } instance.SortPorts(toOpen) logger.Infof("opened ports %v in environment", toOpen) } if len(toClose) > 0 { if err := fw.environ.ClosePorts(toClose); err != nil { // TODO(mue) Add local retry logic. return err } instance.SortPorts(toClose) logger.Infof("closed ports %v in environment", toClose) } return nil } // flushGlobalPorts opens and closes ports global on the machine. func (fw *Firewaller) flushInstancePorts(machined *machineData, toOpen, toClose []instance.Port) error { // If there's nothing to do, do nothing. // This is important because when a machine is first created, // it will have no instance id but also no open ports - // InstanceId will fail but we don't care. if len(toOpen) == 0 && len(toClose) == 0 { return nil } m, err := machined.machine() if params.IsCodeNotFound(err) { return nil } if err != nil { return err } _, machineId, err := names.ParseTag(machined.tag, names.MachineTagKind) if err != nil { return err } instanceId, err := m.InstanceId() if err != nil { return err } instances, err := fw.environ.Instances([]instance.Id{instanceId}) if err != nil { return err } // Open and close the ports. if len(toOpen) > 0 { if err := instances[0].OpenPorts(machineId, toOpen); err != nil { // TODO(mue) Add local retry logic. return err } instance.SortPorts(toOpen) logger.Infof("opened ports %v on %q", toOpen, machined.tag) } if len(toClose) > 0 { if err := instances[0].ClosePorts(machineId, toClose); err != nil { // TODO(mue) Add local retry logic. return err } instance.SortPorts(toClose) logger.Infof("closed ports %v on %q", toClose, machined.tag) } return nil } // machineLifeChanged starts watching new machines when the firewaller // is starting, or when new machines come to life, and stops watching // machines that are dying. func (fw *Firewaller) machineLifeChanged(tag string) error { m, err := fw.st.Machine(tag) found := !params.IsCodeNotFound(err) if found && err != nil { return err } dead := !found || m.Life() == params.Dead machined, known := fw.machineds[tag] if known && dead { return fw.forgetMachine(machined) } if !known && !dead { err = fw.startMachine(tag) if err != nil { return err } logger.Debugf("started watching %q", tag) } return nil } // forgetMachine cleans the machine data after the machine is removed. func (fw *Firewaller) forgetMachine(machined *machineData) error { for _, unitd := range machined.unitds { fw.forgetUnit(unitd) } if err := fw.flushMachine(machined); err != nil { return err } delete(fw.machineds, machined.tag) if err := machined.Stop(); err != nil { return err } logger.Debugf("stopped watching %q", machined.tag) return nil } // forgetUnit cleans the unit data after the unit is removed. func (fw *Firewaller) forgetUnit(unitd *unitData) { name := unitd.unit.Name() serviced := unitd.serviced machined := unitd.machined if err := unitd.Stop(); err != nil { logger.Errorf("unit watcher %q returned error when stopping: %v", name, err) } // Clean up after stopping. delete(fw.unitds, name) delete(machined.unitds, name) delete(serviced.unitds, name) if len(serviced.unitds) == 0 { // Stop service data after all units are removed. if err := serviced.Stop(); err != nil { logger.Errorf("service watcher %q returned error when stopping: %v", serviced.service.Name(), err) } delete(fw.serviceds, serviced.service.Name()) } } // stopWatchers stops all the firewaller's watchers. func (fw *Firewaller) stopWatchers() { watcher.Stop(fw.environWatcher, &fw.tomb) watcher.Stop(fw.machinesWatcher, &fw.tomb) for _, unitd := range fw.unitds { watcher.Stop(unitd, &fw.tomb) } for _, serviced := range fw.serviceds { watcher.Stop(serviced, &fw.tomb) } for _, machined := range fw.machineds { watcher.Stop(machined, &fw.tomb) } } // Err returns the reason why the firewaller has stopped or tomb.ErrStillAlive // when it is still alive. func (fw *Firewaller) Err() (reason error) { return fw.tomb.Err() } // Kill implements worker.Worker.Kill. func (fw *Firewaller) Kill() { fw.tomb.Kill(nil) } // Wait implements worker.Worker.Wait. func (fw *Firewaller) Wait() error { return fw.tomb.Wait() } // Stop stops the Firewaller and returns any error encountered while stopping. func (fw *Firewaller) Stop() error { fw.tomb.Kill(nil) return fw.tomb.Wait() } // unitsChange contains the changed units for one specific machine. type unitsChange struct { machined *machineData units []string } // machineData holds machine details and watches units added or removed. type machineData struct { tomb tomb.Tomb fw *Firewaller tag string unitds map[string]*unitData ports []instance.Port } func (md *machineData) machine() (*apifirewaller.Machine, error) { return md.fw.st.Machine(md.tag) } // watchLoop watches the machine for units added or removed. func (md *machineData) watchLoop(unitw apiwatcher.StringsWatcher) { defer md.tomb.Done() defer watcher.Stop(unitw, &md.tomb) for { select { case <-md.tomb.Dying(): return case change, ok := <-unitw.Changes(): if !ok { _, err := md.machine() if !params.IsCodeNotFound(err) { md.fw.tomb.Kill(watcher.MustErr(unitw)) } return } select { case md.fw.unitsChange <- &unitsChange{md, change}: case <-md.tomb.Dying(): return } } } } // stopWatch stops the machine watching. func (md *machineData) Stop() error { md.tomb.Kill(nil) return md.tomb.Wait() } // portsChange contains the changed ports for one specific unit. type portsChange struct { unitd *unitData ports []instance.Port } // unitData holds unit details and watches port changes. type unitData struct { tomb tomb.Tomb fw *Firewaller unit *apifirewaller.Unit serviced *serviceData machined *machineData ports []instance.Port } // watchLoop watches the unit for port changes. func (ud *unitData) watchLoop(latestPorts []instance.Port) { defer ud.tomb.Done() w, err := ud.unit.Watch() if err != nil { ud.fw.tomb.Kill(err) return } defer watcher.Stop(w, &ud.tomb) for { select { case <-ud.tomb.Dying(): return case _, ok := <-w.Changes(): if !ok { ud.fw.tomb.Kill(watcher.MustErr(w)) return } if err := ud.unit.Refresh(); err != nil { if !params.IsCodeNotFound(err) { ud.fw.tomb.Kill(err) } return } change, err := ud.unit.OpenedPorts() if err != nil { ud.fw.tomb.Kill(err) return } if samePorts(change, latestPorts) { continue } latestPorts = append(latestPorts[:0], change...) select { case ud.fw.portsChange <- &portsChange{ud, change}: case <-ud.tomb.Dying(): return } } } } // samePorts returns whether old and new contain the same set of ports. // Both old and new must be sorted. func samePorts(old, new []instance.Port) bool { if len(old) != len(new) { return false } for i, p := range old { if new[i] != p { return false } } return true } // Stop stops the unit watching. func (ud *unitData) Stop() error { ud.tomb.Kill(nil) return ud.tomb.Wait() } // exposedChange contains the changed exposed flag for one specific service. type exposedChange struct { serviced *serviceData exposed bool } // serviceData holds service details and watches exposure changes. type serviceData struct { tomb tomb.Tomb fw *Firewaller service *apifirewaller.Service exposed bool unitds map[string]*unitData } // watchLoop watches the service's exposed flag for changes. func (sd *serviceData) watchLoop(exposed bool) { defer sd.tomb.Done() w, err := sd.service.Watch() if err != nil { sd.fw.tomb.Kill(err) return } defer watcher.Stop(w, &sd.tomb) for { select { case <-sd.tomb.Dying(): return case _, ok := <-w.Changes(): if !ok { sd.fw.tomb.Kill(watcher.MustErr(w)) return } if err := sd.service.Refresh(); err != nil { if !params.IsCodeNotFound(err) { sd.fw.tomb.Kill(err) } return } change, err := sd.service.IsExposed() if err != nil { sd.fw.tomb.Kill(err) return } if change == exposed { continue } exposed = change select { case sd.fw.exposedChange <- &exposedChange{sd, change}: case <-sd.tomb.Dying(): return } } } } // Stop stops the service watching. func (sd *serviceData) Stop() error { sd.tomb.Kill(nil) return sd.tomb.Wait() } // Diff returns all the ports that exist in A but not B. func Diff(A, B []instance.Port) (missing []instance.Port) { next: for _, a := range A { for _, b := range B { if a == b { continue next } } missing = append(missing, a) } return } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/firewaller/firewaller_test.go�������������������0000644�0000153�0000161�00000050574�12321735642�030067� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller_test import ( "launchpad.net/juju-core/environs/config" "reflect" stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" apifirewaller "launchpad.net/juju-core/state/api/firewaller" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/firewaller" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type FirewallerSuite struct { testing.JujuConnSuite op <-chan dummy.Operation charm *state.Charm st *api.State firewaller *apifirewaller.State } type FirewallerGlobalModeSuite struct { FirewallerSuite } var _ worker.Worker = (*firewaller.Firewaller)(nil) // assertPorts retrieves the open ports of the instance and compares them // to the expected. func (s *FirewallerSuite) assertPorts(c *gc.C, inst instance.Instance, machineId string, expected []instance.Port) { s.BackingState.StartSync() start := time.Now() for { got, err := inst.Ports(machineId) if err != nil { c.Fatal(err) return } instance.SortPorts(got) instance.SortPorts(expected) if reflect.DeepEqual(got, expected) { c.Succeed() return } if time.Since(start) > coretesting.LongWait { c.Fatalf("timed out: expected %q; got %q", expected, got) return } time.Sleep(coretesting.ShortWait) } } // assertEnvironPorts retrieves the open ports of environment and compares them // to the expected. func (s *FirewallerSuite) assertEnvironPorts(c *gc.C, expected []instance.Port) { s.BackingState.StartSync() start := time.Now() for { got, err := s.Conn.Environ.Ports() if err != nil { c.Fatal(err) return } instance.SortPorts(got) instance.SortPorts(expected) if reflect.DeepEqual(got, expected) { c.Succeed() return } if time.Since(start) > coretesting.LongWait { c.Fatalf("timed out: expected %q; got %q", expected, got) return } time.Sleep(coretesting.ShortWait) } } var _ = gc.Suite(&FirewallerSuite{}) func (s FirewallerGlobalModeSuite) SetUpTest(c *gc.C) { add := map[string]interface{}{"firewall-mode": config.FwGlobal} s.DummyConfig = dummy.SampleConfig().Merge(add).Delete("admin-secret", "ca-private-key") s.FirewallerSuite.SetUpTest(c) } func (s *FirewallerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.charm = s.AddTestingCharm(c, "dummy") // Create a manager machine and login to the API. machine, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = machine.SetPassword(password) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("i-manager", "fake_nonce", nil) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAsMachine(c, machine.Tag(), password, "fake_nonce") c.Assert(s.st, gc.NotNil) // Create the firewaller API facade. s.firewaller = s.st.Firewaller() c.Assert(s.firewaller, gc.NotNil) } func (s *FirewallerSuite) TestStartStop(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) c.Assert(fw.Stop(), gc.IsNil) } func (s *FirewallerSuite) addUnit(c *gc.C, svc *state.Service) (*state.Unit, *state.Machine) { units, err := juju.AddUnits(s.State, svc, 1, "") c.Assert(err, gc.IsNil) u := units[0] id, err := u.AssignedMachineId() c.Assert(err, gc.IsNil) m, err := s.State.Machine(id) c.Assert(err, gc.IsNil) return u, m } // startInstance starts a new instance for the given machine. func (s *FirewallerSuite) startInstance(c *gc.C, m *state.Machine) instance.Instance { inst, hc := testing.AssertStartInstance(c, s.Conn.Environ, m.Id()) err := m.SetProvisioned(inst.Id(), "fake_nonce", hc) c.Assert(err, gc.IsNil) return inst } func (s *FirewallerSuite) TestNotExposedService(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc := s.AddTestingService(c, "wordpress", s.charm) u, m := s.addUnit(c, svc) inst := s.startInstance(c, m) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 8080) c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), nil) err = u.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), nil) } func (s *FirewallerSuite) TestExposedService(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc := s.AddTestingService(c, "wordpress", s.charm) err = svc.SetExposed() c.Assert(err, gc.IsNil) u, m := s.addUnit(c, svc) inst := s.startInstance(c, m) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 8080) c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), []instance.Port{{"tcp", 80}, {"tcp", 8080}}) err = u.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), []instance.Port{{"tcp", 8080}}) } func (s *FirewallerSuite) TestMultipleExposedServices(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc1 := s.AddTestingService(c, "wordpress", s.charm) err = svc1.SetExposed() c.Assert(err, gc.IsNil) u1, m1 := s.addUnit(c, svc1) inst1 := s.startInstance(c, m1) err = u1.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) err = u1.OpenPort("tcp", 8080) c.Assert(err, gc.IsNil) svc2 := s.AddTestingService(c, "mysql", s.charm) c.Assert(err, gc.IsNil) err = svc2.SetExposed() c.Assert(err, gc.IsNil) u2, m2 := s.addUnit(c, svc2) inst2 := s.startInstance(c, m2) err = u2.OpenPort("tcp", 3306) c.Assert(err, gc.IsNil) s.assertPorts(c, inst1, m1.Id(), []instance.Port{{"tcp", 80}, {"tcp", 8080}}) s.assertPorts(c, inst2, m2.Id(), []instance.Port{{"tcp", 3306}}) err = u1.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) err = u2.ClosePort("tcp", 3306) c.Assert(err, gc.IsNil) s.assertPorts(c, inst1, m1.Id(), []instance.Port{{"tcp", 8080}}) s.assertPorts(c, inst2, m2.Id(), nil) } func (s *FirewallerSuite) TestMachineWithoutInstanceId(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc := s.AddTestingService(c, "wordpress", s.charm) err = svc.SetExposed() c.Assert(err, gc.IsNil) // add a unit but don't start its instance yet. u1, m1 := s.addUnit(c, svc) // add another unit and start its instance, so that // we're sure the firewaller has seen the first instance. u2, m2 := s.addUnit(c, svc) inst2 := s.startInstance(c, m2) err = u2.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) s.assertPorts(c, inst2, m2.Id(), []instance.Port{{"tcp", 80}}) inst1 := s.startInstance(c, m1) err = u1.OpenPort("tcp", 8080) c.Assert(err, gc.IsNil) s.assertPorts(c, inst1, m1.Id(), []instance.Port{{"tcp", 8080}}) } func (s *FirewallerSuite) TestMultipleUnits(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc := s.AddTestingService(c, "wordpress", s.charm) err = svc.SetExposed() c.Assert(err, gc.IsNil) u1, m1 := s.addUnit(c, svc) inst1 := s.startInstance(c, m1) err = u1.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) u2, m2 := s.addUnit(c, svc) inst2 := s.startInstance(c, m2) err = u2.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) s.assertPorts(c, inst1, m1.Id(), []instance.Port{{"tcp", 80}}) s.assertPorts(c, inst2, m2.Id(), []instance.Port{{"tcp", 80}}) err = u1.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) err = u2.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) s.assertPorts(c, inst1, m1.Id(), nil) s.assertPorts(c, inst2, m2.Id(), nil) } func (s *FirewallerSuite) TestStartWithState(c *gc.C) { svc := s.AddTestingService(c, "wordpress", s.charm) err := svc.SetExposed() c.Assert(err, gc.IsNil) u, m := s.addUnit(c, svc) inst := s.startInstance(c, m) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 8080) c.Assert(err, gc.IsNil) // Nothing open without firewaller. s.assertPorts(c, inst, m.Id(), nil) // Starting the firewaller opens the ports. fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() s.assertPorts(c, inst, m.Id(), []instance.Port{{"tcp", 80}, {"tcp", 8080}}) err = svc.SetExposed() c.Assert(err, gc.IsNil) } func (s *FirewallerSuite) TestStartWithPartialState(c *gc.C) { m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) inst := s.startInstance(c, m) svc := s.AddTestingService(c, "wordpress", s.charm) err = svc.SetExposed() c.Assert(err, gc.IsNil) // Starting the firewaller, no open ports. fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() s.assertPorts(c, inst, m.Id(), nil) // Complete steps to open port. u, err := svc.AddUnit() c.Assert(err, gc.IsNil) err = u.AssignToMachine(m) c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), []instance.Port{{"tcp", 80}}) } func (s *FirewallerSuite) TestStartWithUnexposedService(c *gc.C) { m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) inst := s.startInstance(c, m) svc := s.AddTestingService(c, "wordpress", s.charm) u, err := svc.AddUnit() c.Assert(err, gc.IsNil) err = u.AssignToMachine(m) c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) // Starting the firewaller, no open ports. fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() s.assertPorts(c, inst, m.Id(), nil) // Expose service. err = svc.SetExposed() c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), []instance.Port{{"tcp", 80}}) } func (s *FirewallerSuite) TestSetClearExposedService(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc := s.AddTestingService(c, "wordpress", s.charm) u, m := s.addUnit(c, svc) inst := s.startInstance(c, m) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 8080) c.Assert(err, gc.IsNil) // Not exposed service, so no open port. s.assertPorts(c, inst, m.Id(), nil) // SeExposed opens the ports. err = svc.SetExposed() c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), []instance.Port{{"tcp", 80}, {"tcp", 8080}}) // ClearExposed closes the ports again. err = svc.ClearExposed() c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), nil) } func (s *FirewallerSuite) TestRemoveUnit(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc := s.AddTestingService(c, "wordpress", s.charm) err = svc.SetExposed() c.Assert(err, gc.IsNil) u1, m1 := s.addUnit(c, svc) inst1 := s.startInstance(c, m1) err = u1.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) u2, m2 := s.addUnit(c, svc) inst2 := s.startInstance(c, m2) err = u2.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) s.assertPorts(c, inst1, m1.Id(), []instance.Port{{"tcp", 80}}) s.assertPorts(c, inst2, m2.Id(), []instance.Port{{"tcp", 80}}) // Remove unit. err = u1.EnsureDead() c.Assert(err, gc.IsNil) err = u1.Remove() c.Assert(err, gc.IsNil) s.assertPorts(c, inst1, m1.Id(), nil) s.assertPorts(c, inst2, m2.Id(), []instance.Port{{"tcp", 80}}) } func (s *FirewallerSuite) TestRemoveService(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc := s.AddTestingService(c, "wordpress", s.charm) err = svc.SetExposed() c.Assert(err, gc.IsNil) u, m := s.addUnit(c, svc) inst := s.startInstance(c, m) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), []instance.Port{{"tcp", 80}}) // Remove service. err = u.EnsureDead() c.Assert(err, gc.IsNil) err = u.Remove() c.Assert(err, gc.IsNil) err = svc.Destroy() c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), nil) } func (s *FirewallerSuite) TestRemoveMultipleServices(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc1 := s.AddTestingService(c, "wordpress", s.charm) err = svc1.SetExposed() c.Assert(err, gc.IsNil) u1, m1 := s.addUnit(c, svc1) inst1 := s.startInstance(c, m1) err = u1.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) svc2 := s.AddTestingService(c, "mysql", s.charm) err = svc2.SetExposed() c.Assert(err, gc.IsNil) u2, m2 := s.addUnit(c, svc2) inst2 := s.startInstance(c, m2) err = u2.OpenPort("tcp", 3306) c.Assert(err, gc.IsNil) s.assertPorts(c, inst1, m1.Id(), []instance.Port{{"tcp", 80}}) s.assertPorts(c, inst2, m2.Id(), []instance.Port{{"tcp", 3306}}) // Remove services. err = u2.EnsureDead() c.Assert(err, gc.IsNil) err = u2.Remove() c.Assert(err, gc.IsNil) err = svc2.Destroy() c.Assert(err, gc.IsNil) err = u1.EnsureDead() c.Assert(err, gc.IsNil) err = u1.Remove() c.Assert(err, gc.IsNil) err = svc1.Destroy() c.Assert(err, gc.IsNil) s.assertPorts(c, inst1, m1.Id(), nil) s.assertPorts(c, inst2, m2.Id(), nil) } func (s *FirewallerSuite) TestDeadMachine(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc := s.AddTestingService(c, "wordpress", s.charm) err = svc.SetExposed() c.Assert(err, gc.IsNil) u, m := s.addUnit(c, svc) inst := s.startInstance(c, m) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), []instance.Port{{"tcp", 80}}) // Remove unit and service, also tested without. Has no effect. err = u.EnsureDead() c.Assert(err, gc.IsNil) err = u.Remove() c.Assert(err, gc.IsNil) err = svc.Destroy() c.Assert(err, gc.IsNil) // Kill machine. err = m.Refresh() c.Assert(err, gc.IsNil) err = m.EnsureDead() c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), nil) } func (s *FirewallerSuite) TestRemoveMachine(c *gc.C) { fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc := s.AddTestingService(c, "wordpress", s.charm) err = svc.SetExposed() c.Assert(err, gc.IsNil) u, m := s.addUnit(c, svc) inst := s.startInstance(c, m) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) s.assertPorts(c, inst, m.Id(), []instance.Port{{"tcp", 80}}) // Remove unit. err = u.EnsureDead() c.Assert(err, gc.IsNil) err = u.Remove() c.Assert(err, gc.IsNil) // Remove machine. Nothing bad should happen, but can't // assert port state since the machine must have been // destroyed and we lost its reference. err = m.Refresh() c.Assert(err, gc.IsNil) err = m.EnsureDead() c.Assert(err, gc.IsNil) err = m.Remove() c.Assert(err, gc.IsNil) } func (s *FirewallerGlobalModeSuite) TestGlobalMode(c *gc.C) { // Start firewaller and open ports. fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() svc1 := s.AddTestingService(c, "wordpress", s.charm) err = svc1.SetExposed() c.Assert(err, gc.IsNil) u1, m1 := s.addUnit(c, svc1) s.startInstance(c, m1) err = u1.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) err = u1.OpenPort("tcp", 8080) c.Assert(err, gc.IsNil) svc2 := s.AddTestingService(c, "moinmoin", s.charm) c.Assert(err, gc.IsNil) err = svc2.SetExposed() c.Assert(err, gc.IsNil) u2, m2 := s.addUnit(c, svc2) s.startInstance(c, m2) err = u2.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}, {"tcp", 8080}}) // Closing a port opened by a different unit won't touch the environment. err = u1.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}, {"tcp", 8080}}) // Closing a port used just once changes the environment. err = u1.ClosePort("tcp", 8080) c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}}) // Closing the last port also modifies the environment. err = u2.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, nil) } func (s *FirewallerGlobalModeSuite) TestGlobalModeStartWithUnexposedService(c *gc.C) { m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) s.startInstance(c, m) svc := s.AddTestingService(c, "wordpress", s.charm) u, err := svc.AddUnit() c.Assert(err, gc.IsNil) err = u.AssignToMachine(m) c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) // Starting the firewaller, no open ports. fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() s.assertEnvironPorts(c, nil) // Expose service. err = svc.SetExposed() c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}}) } func (s *FirewallerGlobalModeSuite) TestGlobalModeRestart(c *gc.C) { // Start firewaller and open ports. fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) svc := s.AddTestingService(c, "wordpress", s.charm) err = svc.SetExposed() c.Assert(err, gc.IsNil) u, m := s.addUnit(c, svc) s.startInstance(c, m) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 8080) c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}, {"tcp", 8080}}) // Stop firewaller and close one and open a different port. err = fw.Stop() c.Assert(err, gc.IsNil) err = u.ClosePort("tcp", 8080) c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 8888) c.Assert(err, gc.IsNil) // Start firewaller and check port. fw, err = firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}, {"tcp", 8888}}) } func (s *FirewallerGlobalModeSuite) TestGlobalModeRestartUnexposedService(c *gc.C) { // Start firewaller and open ports. fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) svc := s.AddTestingService(c, "wordpress", s.charm) err = svc.SetExposed() c.Assert(err, gc.IsNil) u, m := s.addUnit(c, svc) s.startInstance(c, m) err = u.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 8080) c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}, {"tcp", 8080}}) // Stop firewaller and clear exposed flag on service. err = fw.Stop() c.Assert(err, gc.IsNil) err = svc.ClearExposed() c.Assert(err, gc.IsNil) // Start firewaller and check port. fw, err = firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() s.assertEnvironPorts(c, nil) } func (s *FirewallerGlobalModeSuite) TestGlobalModeRestartPortCount(c *gc.C) { // Start firewaller and open ports. fw, err := firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) svc1 := s.AddTestingService(c, "wordpress", s.charm) err = svc1.SetExposed() c.Assert(err, gc.IsNil) u1, m1 := s.addUnit(c, svc1) s.startInstance(c, m1) err = u1.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) err = u1.OpenPort("tcp", 8080) c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}, {"tcp", 8080}}) // Stop firewaller and add another service using the port. err = fw.Stop() c.Assert(err, gc.IsNil) svc2 := s.AddTestingService(c, "moinmoin", s.charm) err = svc2.SetExposed() c.Assert(err, gc.IsNil) u2, m2 := s.addUnit(c, svc2) s.startInstance(c, m2) err = u2.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) // Start firewaller and check port. fw, err = firewaller.NewFirewaller(s.firewaller) c.Assert(err, gc.IsNil) defer func() { c.Assert(fw.Stop(), gc.IsNil) }() s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}, {"tcp", 8080}}) // Closing a port opened by a different unit won't touch the environment. err = u1.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}, {"tcp", 8080}}) // Closing a port used just once changes the environment. err = u1.ClosePort("tcp", 8080) c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, []instance.Port{{"tcp", 80}}) // Closing the last port also modifies the environment. err = u2.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) s.assertEnvironPorts(c, nil) } ������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/������������������������������������0000755�0000153�0000161�00000000000�12321736000�024542� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/export_test.go����������������������0000644�0000153�0000161�00000000724�12321735642�027467� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner import ( "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/state/api/watcher" ) func SetObserver(p Provisioner, observer chan<- *config.Config) { ep := p.(*environProvisioner) ep.Lock() ep.observer = observer ep.Unlock() } func GetRetryWatcher(p Provisioner) (watcher.NotifyWatcher, error) { return p.getRetryWatcher() } ��������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/kvm-broker_test.go������������������0000644�0000153�0000161�00000017252�12321735776�030241� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner_test import ( "fmt" "path/filepath" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/container/kvm/mock" kvmtesting "launchpad.net/juju-core/container/kvm/testing" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" instancetest "launchpad.net/juju-core/instance/testing" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker/provisioner" ) type kvmSuite struct { kvmtesting.TestSuite events chan mock.Event } type kvmBrokerSuite struct { kvmSuite broker environs.InstanceBroker agentConfig agent.Config } var _ = gc.Suite(&kvmBrokerSuite{}) func (s *kvmSuite) SetUpTest(c *gc.C) { s.TestSuite.SetUpTest(c) s.events = make(chan mock.Event) go func() { for event := range s.events { c.Output(3, fmt.Sprintf("kvm event: <%s, %s>", event.Action, event.InstanceId)) } }() s.TestSuite.Factory.AddListener(s.events) } func (s *kvmSuite) TearDownTest(c *gc.C) { close(s.events) s.TestSuite.TearDownTest(c) } func (s *kvmBrokerSuite) SetUpTest(c *gc.C) { s.kvmSuite.SetUpTest(c) tools := &coretools.Tools{ Version: version.MustParseBinary("2.3.4-foo-bar"), URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", } var err error s.agentConfig, err = agent.NewAgentConfig( agent.AgentConfigParams{ DataDir: "/not/used/here", Tag: "tag", UpgradedToVersion: version.Current.Number, Password: "dummy-secret", Nonce: "nonce", APIAddresses: []string{"10.0.0.1:1234"}, CACert: []byte(coretesting.CACert), }) c.Assert(err, gc.IsNil) s.broker, err = provisioner.NewKvmBroker(&fakeAPI{}, tools, s.agentConfig) c.Assert(err, gc.IsNil) } func (s *kvmBrokerSuite) startInstance(c *gc.C, machineId string) instance.Instance { machineNonce := "fake-nonce" stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, machineNonce, stateInfo, apiInfo) cons := constraints.Value{} possibleTools := s.broker.(coretools.HasTools).Tools() kvm, _, err := s.broker.StartInstance(environs.StartInstanceParams{ Constraints: cons, Tools: possibleTools, MachineConfig: machineConfig, }) c.Assert(err, gc.IsNil) return kvm } func (s *kvmBrokerSuite) TestStopInstance(c *gc.C) { kvm0 := s.startInstance(c, "1/kvm/0") kvm1 := s.startInstance(c, "1/kvm/1") kvm2 := s.startInstance(c, "1/kvm/2") err := s.broker.StopInstances([]instance.Instance{kvm0}) c.Assert(err, gc.IsNil) s.assertInstances(c, kvm1, kvm2) c.Assert(s.kvmContainerDir(kvm0), jc.DoesNotExist) c.Assert(s.kvmRemovedContainerDir(kvm0), jc.IsDirectory) err = s.broker.StopInstances([]instance.Instance{kvm1, kvm2}) c.Assert(err, gc.IsNil) s.assertInstances(c) } func (s *kvmBrokerSuite) TestAllInstances(c *gc.C) { kvm0 := s.startInstance(c, "1/kvm/0") kvm1 := s.startInstance(c, "1/kvm/1") s.assertInstances(c, kvm0, kvm1) err := s.broker.StopInstances([]instance.Instance{kvm1}) c.Assert(err, gc.IsNil) kvm2 := s.startInstance(c, "1/kvm/2") s.assertInstances(c, kvm0, kvm2) } func (s *kvmBrokerSuite) assertInstances(c *gc.C, inst ...instance.Instance) { results, err := s.broker.AllInstances() c.Assert(err, gc.IsNil) instancetest.MatchInstances(c, results, inst...) } func (s *kvmBrokerSuite) kvmContainerDir(inst instance.Instance) string { return filepath.Join(s.ContainerDir, string(inst.Id())) } func (s *kvmBrokerSuite) kvmRemovedContainerDir(inst instance.Instance) string { return filepath.Join(s.RemovedDir, string(inst.Id())) } type kvmProvisionerSuite struct { CommonProvisionerSuite kvmSuite machineId string events chan mock.Event } var _ = gc.Suite(&kvmProvisionerSuite{}) func (s *kvmProvisionerSuite) SetUpSuite(c *gc.C) { s.CommonProvisionerSuite.SetUpSuite(c) s.kvmSuite.SetUpSuite(c) } func (s *kvmProvisionerSuite) TearDownSuite(c *gc.C) { s.kvmSuite.TearDownSuite(c) s.CommonProvisionerSuite.TearDownSuite(c) } func (s *kvmProvisionerSuite) SetUpTest(c *gc.C) { s.CommonProvisionerSuite.SetUpTest(c) s.kvmSuite.SetUpTest(c) // The kvm provisioner actually needs the machine it is being created on // to be in state, in order to get the watcher. m, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits, state.JobManageEnviron) c.Assert(err, gc.IsNil) err = m.SetAddresses([]instance.Address{ instance.NewAddress("0.1.2.3"), }) c.Assert(err, gc.IsNil) s.machineId = m.Id() s.APILogin(c, m) err = m.SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) s.events = make(chan mock.Event, 25) s.Factory.AddListener(s.events) } func (s *kvmProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string { s.State.StartSync() event := <-s.events c.Assert(event.Action, gc.Equals, mock.Started) err := machine.Refresh() c.Assert(err, gc.IsNil) s.waitInstanceId(c, machine, instance.Id(event.InstanceId)) return event.InstanceId } func (s *kvmProvisionerSuite) expectStopped(c *gc.C, instId string) { s.State.StartSync() event := <-s.events c.Assert(event.Action, gc.Equals, mock.Stopped) c.Assert(event.InstanceId, gc.Equals, instId) } func (s *kvmProvisionerSuite) expectNoEvents(c *gc.C) { select { case event := <-s.events: c.Fatalf("unexpected event %#v", event) case <-time.After(coretesting.ShortWait): return } } func (s *kvmProvisionerSuite) TearDownTest(c *gc.C) { close(s.events) s.kvmSuite.TearDownTest(c) s.CommonProvisionerSuite.TearDownTest(c) } func (s *kvmProvisionerSuite) newKvmProvisioner(c *gc.C) provisioner.Provisioner { machineTag := names.MachineTag(s.machineId) agentConfig := s.AgentConfigForTag(c, machineTag) tools, err := s.provisioner.Tools(agentConfig.Tag()) c.Assert(err, gc.IsNil) broker, err := provisioner.NewKvmBroker(s.provisioner, tools, agentConfig) c.Assert(err, gc.IsNil) return provisioner.NewContainerProvisioner(instance.KVM, s.provisioner, agentConfig, broker) } func (s *kvmProvisionerSuite) TestProvisionerStartStop(c *gc.C) { p := s.newKvmProvisioner(c) c.Assert(p.Stop(), gc.IsNil) } func (s *kvmProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) { p := s.newKvmProvisioner(c) defer stop(c, p) // Check that an instance is not provisioned when the machine is created. _, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) c.Assert(err, gc.IsNil) s.expectNoEvents(c) } func (s *kvmProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) { p := s.newKvmProvisioner(c) defer stop(c, p) w, err := provisioner.GetRetryWatcher(p) c.Assert(w, gc.IsNil) c.Assert(errors.IsNotImplementedError(err), jc.IsTrue) } func (s *kvmProvisionerSuite) addContainer(c *gc.C) *state.Machine { template := state.MachineTemplate{ Series: coretesting.FakeDefaultSeries, Jobs: []state.MachineJob{state.JobHostUnits}, } container, err := s.State.AddMachineInsideMachine(template, s.machineId, instance.KVM) c.Assert(err, gc.IsNil) return container } func (s *kvmProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) { p := s.newKvmProvisioner(c) defer stop(c, p) container := s.addContainer(c) instId := s.expectStarted(c, container) // ...and removed, along with the machine, when the machine is Dead. c.Assert(container.EnsureDead(), gc.IsNil) s.expectStopped(c, instId) s.waitRemoved(c, container) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/provisioner_task.go�����������������0000644�0000153�0000161�00000036327�12321735776�030530� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner import ( "fmt" "launchpad.net/tomb" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" apiprovisioner "launchpad.net/juju-core/state/api/provisioner" apiwatcher "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/state/watcher" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/worker" ) type ProvisionerTask interface { worker.Worker Stop() error Dying() <-chan struct{} Err() error // SetSafeMode sets a flag to indicate whether the provisioner task // runs in safe mode or not. In safe mode, any running instances // which do no exist in state are allowed to keep running rather than // being shut down. SetSafeMode(safeMode bool) } type MachineGetter interface { Machine(tag string) (*apiprovisioner.Machine, error) MachinesWithTransientErrors() ([]*apiprovisioner.Machine, []params.StatusResult, error) } func NewProvisionerTask( machineTag string, safeMode bool, machineGetter MachineGetter, machineWatcher apiwatcher.StringsWatcher, retryWatcher apiwatcher.NotifyWatcher, broker environs.InstanceBroker, auth environs.AuthenticationProvider, ) ProvisionerTask { task := &provisionerTask{ machineTag: machineTag, machineGetter: machineGetter, machineWatcher: machineWatcher, retryWatcher: retryWatcher, broker: broker, auth: auth, safeMode: safeMode, safeModeChan: make(chan bool, 1), machines: make(map[string]*apiprovisioner.Machine), } go func() { defer task.tomb.Done() task.tomb.Kill(task.loop()) }() return task } type provisionerTask struct { machineTag string machineGetter MachineGetter machineWatcher apiwatcher.StringsWatcher retryWatcher apiwatcher.NotifyWatcher broker environs.InstanceBroker tomb tomb.Tomb auth environs.AuthenticationProvider safeMode bool safeModeChan chan bool // instance id -> instance instances map[instance.Id]instance.Instance // machine id -> machine machines map[string]*apiprovisioner.Machine } // Kill implements worker.Worker.Kill. func (task *provisionerTask) Kill() { task.tomb.Kill(nil) } // Wait implements worker.Worker.Wait. func (task *provisionerTask) Wait() error { return task.tomb.Wait() } func (task *provisionerTask) Stop() error { task.Kill() return task.Wait() } func (task *provisionerTask) Dying() <-chan struct{} { return task.tomb.Dying() } func (task *provisionerTask) Err() error { return task.tomb.Err() } func (task *provisionerTask) loop() error { logger.Infof("Starting up provisioner task %s", task.machineTag) defer watcher.Stop(task.machineWatcher, &task.tomb) // Don't allow the safe mode to change until we have // read at least one set of changes, which will populate // the task.machines map. Otherwise we will potentially // see all legitimate instances as unknown. var safeModeChan chan bool // Not all provisioners have a retry channel. var retryChan <-chan struct{} if task.retryWatcher != nil { retryChan = task.retryWatcher.Changes() } // When the watcher is started, it will have the initial changes be all // the machines that are relevant. Also, since this is available straight // away, we know there will be some changes right off the bat. for { select { case <-task.tomb.Dying(): logger.Infof("Shutting down provisioner task %s", task.machineTag) return tomb.ErrDying case ids, ok := <-task.machineWatcher.Changes(): if !ok { return watcher.MustErr(task.machineWatcher) } if err := task.processMachines(ids); err != nil { return fmt.Errorf("failed to process updated machines: %v", err) } // We've seen a set of changes. Enable safe mode change. safeModeChan = task.safeModeChan case safeMode := <-safeModeChan: if safeMode == task.safeMode { break } logger.Infof("safe mode changed to %v", safeMode) task.safeMode = safeMode if !safeMode { // Safe mode has been disabled, so process current machines // so that unknown machines will be immediately dealt with. if err := task.processMachines(nil); err != nil { return fmt.Errorf("failed to process machines after safe mode disabled: %v", err) } } case <-retryChan: if err := task.processMachinesWithTransientErrors(); err != nil { return fmt.Errorf("failed to process machines with transient errors: %v", err) } } } } // SetSafeMode implements ProvisionerTask.SetSafeMode(). func (task *provisionerTask) SetSafeMode(safeMode bool) { select { case task.safeModeChan <- safeMode: case <-task.Dying(): } } func (task *provisionerTask) processMachinesWithTransientErrors() error { machines, statusResults, err := task.machineGetter.MachinesWithTransientErrors() if err != nil { return nil } logger.Tracef("processMachinesWithTransientErrors(%v)", statusResults) var pending []*apiprovisioner.Machine for i, status := range statusResults { if status.Error != nil { logger.Errorf("cannot retry provisioning of machine %q: %v", status.Id, status.Error) continue } machine := machines[i] if err := machine.SetStatus(params.StatusPending, "", nil); err != nil { logger.Errorf("cannot reset status of machine %q: %v", status.Id, err) continue } task.machines[machine.Tag()] = machine pending = append(pending, machine) } return task.startMachines(pending) } func (task *provisionerTask) processMachines(ids []string) error { logger.Tracef("processMachines(%v)", ids) // Populate the tasks maps of current instances and machines. err := task.populateMachineMaps(ids) if err != nil { return err } // Find machines without an instance id or that are dead pending, dead, err := task.pendingOrDead(ids) if err != nil { return err } // Stop all machines that are dead stopping := task.instancesForMachines(dead) // Find running instances that have no machines associated unknown, err := task.findUnknownInstances(stopping) if err != nil { return err } if task.safeMode { logger.Infof("running in safe mode, unknown instances not stopped %v", instanceIds(unknown)) unknown = nil } if len(stopping) > 0 { logger.Infof("stopping known instances %v", stopping) } if len(unknown) > 0 { logger.Infof("stopping unknown instances %v", instanceIds(unknown)) } // It's important that we stop unknown instances before starting // pending ones, because if we start an instance and then fail to // set its InstanceId on the machine we don't want to start a new // instance for the same machine ID. if err := task.stopInstances(append(stopping, unknown...)); err != nil { return err } // Remove any dead machines from state. for _, machine := range dead { logger.Infof("removing dead machine %q", machine) if err := machine.Remove(); err != nil { logger.Errorf("failed to remove dead machine %q", machine) } delete(task.machines, machine.Id()) } // Start an instance for the pending ones return task.startMachines(pending) } func instanceIds(instances []instance.Instance) []string { ids := make([]string, 0, len(instances)) for _, inst := range instances { ids = append(ids, string(inst.Id())) } return ids } func (task *provisionerTask) populateMachineMaps(ids []string) error { task.instances = make(map[instance.Id]instance.Instance) instances, err := task.broker.AllInstances() if err != nil { logger.Errorf("failed to get all instances from broker: %v", err) return err } for _, i := range instances { task.instances[i.Id()] = i } // Update the machines map with new data for each of the machines in the // change list. // TODO(thumper): update for API server later to get all machines in one go. for _, id := range ids { machineTag := names.MachineTag(id) machine, err := task.machineGetter.Machine(machineTag) switch { case params.IsCodeNotFoundOrCodeUnauthorized(err): logger.Debugf("machine %q not found in state", id) delete(task.machines, id) case err == nil: task.machines[id] = machine default: logger.Errorf("failed to get machine: %v", err) } } return nil } // pendingOrDead looks up machines with ids and returns those that do not // have an instance id assigned yet, and also those that are dead. func (task *provisionerTask) pendingOrDead(ids []string) (pending, dead []*apiprovisioner.Machine, err error) { for _, id := range ids { machine, found := task.machines[id] if !found { logger.Infof("machine %q not found", id) continue } switch machine.Life() { case params.Dying: if _, err := machine.InstanceId(); err == nil { continue } else if !params.IsCodeNotProvisioned(err) { logger.Errorf("failed to load machine %q instance id: %v", machine, err) return nil, nil, err } logger.Infof("killing dying, unprovisioned machine %q", machine) if err := machine.EnsureDead(); err != nil { logger.Errorf("failed to ensure machine dead %q: %v", machine, err) return nil, nil, err } fallthrough case params.Dead: dead = append(dead, machine) continue } if instId, err := machine.InstanceId(); err != nil { if !params.IsCodeNotProvisioned(err) { logger.Errorf("failed to load machine %q instance id: %v", machine, err) continue } status, _, err := machine.Status() if err != nil { logger.Infof("cannot get machine %q status: %v", machine, err) continue } if status == params.StatusPending { pending = append(pending, machine) logger.Infof("found machine %q pending provisioning", machine) continue } } else { logger.Infof("machine %v already started as instance %q", machine, instId) } } logger.Tracef("pending machines: %v", pending) logger.Tracef("dead machines: %v", dead) return } // findUnknownInstances finds instances which are not associated with a machine. func (task *provisionerTask) findUnknownInstances(stopping []instance.Instance) ([]instance.Instance, error) { // Make a copy of the instances we know about. instances := make(map[instance.Id]instance.Instance) for k, v := range task.instances { instances[k] = v } for _, m := range task.machines { instId, err := m.InstanceId() switch { case err == nil: delete(instances, instId) case params.IsCodeNotProvisioned(err): case params.IsCodeNotFoundOrCodeUnauthorized(err): default: return nil, err } } // Now remove all those instances that we are stopping already as we // know about those and don't want to include them in the unknown list. for _, inst := range stopping { delete(instances, inst.Id()) } var unknown []instance.Instance for _, inst := range instances { unknown = append(unknown, inst) } return unknown, nil } // instancesForMachines returns a list of instance.Instance that represent // the list of machines running in the provider. Missing machines are // omitted from the list. func (task *provisionerTask) instancesForMachines(machines []*apiprovisioner.Machine) []instance.Instance { var instances []instance.Instance for _, machine := range machines { instId, err := machine.InstanceId() if err == nil { instance, found := task.instances[instId] // If the instance is not found we can't stop it. if found { instances = append(instances, instance) } } } return instances } func (task *provisionerTask) stopInstances(instances []instance.Instance) error { // Although calling StopInstance with an empty slice should produce no change in the // provider, environs like dummy do not consider this a noop. if len(instances) == 0 { return nil } if err := task.broker.StopInstances(instances); err != nil { logger.Errorf("broker failed to stop instances: %v", err) return err } return nil } func (task *provisionerTask) startMachines(machines []*apiprovisioner.Machine) error { for _, m := range machines { if err := task.startMachine(m); err != nil { return fmt.Errorf("cannot start machine %v: %v", m, err) } } return nil } func (task *provisionerTask) startMachine(machine *apiprovisioner.Machine) error { cons, err := machine.Constraints() if err != nil { return err } series, err := machine.Series() if err != nil { return err } possibleTools, err := task.possibleTools(series, cons) if err != nil { return err } machineConfig, err := task.machineConfig(machine) if err != nil { return err } inst, metadata, err := task.broker.StartInstance(environs.StartInstanceParams{ Constraints: cons, Tools: possibleTools, MachineConfig: machineConfig, }) if err != nil { // Set the state to error, so the machine will be skipped next // time until the error is resolved, but don't return an // error; just keep going with the other machines. logger.Errorf("cannot start instance for machine %q: %v", machine, err) if err1 := machine.SetStatus(params.StatusError, err.Error(), nil); err1 != nil { // Something is wrong with this machine, better report it back. logger.Errorf("cannot set error status for machine %q: %v", machine, err1) return err1 } return nil } nonce := machineConfig.MachineNonce if err := machine.SetProvisioned(inst.Id(), nonce, metadata); err != nil { logger.Errorf("cannot register instance for machine %v: %v", machine, err) // The machine is started, but we can't record the mapping in // state. It'll keep running while we fail out and restart, // but will then be detected by findUnknownInstances and // killed again. // // TODO(dimitern) Stop the instance right away here. // // Multiple instantiations of a given machine (with the same // machine ID) cannot coexist, because findUnknownInstances is // called before startMachines. However, if the first machine // had started to do work before being replaced, we may // encounter surprising problems. return err } logger.Infof("started machine %s as instance %s with hardware %q", machine, inst.Id(), metadata) return nil } func (task *provisionerTask) possibleTools(series string, cons constraints.Value) (coretools.List, error) { if env, ok := task.broker.(environs.Environ); ok { agentVersion, ok := env.Config().AgentVersion() if !ok { return nil, fmt.Errorf("no agent version set in environment configuration") } return tools.FindInstanceTools(env, agentVersion, series, cons.Arch) } if hasTools, ok := task.broker.(coretools.HasTools); ok { return hasTools.Tools(), nil } panic(fmt.Errorf("broker of type %T does not provide any tools", task.broker)) } func (task *provisionerTask) machineConfig(machine *apiprovisioner.Machine) (*cloudinit.MachineConfig, error) { stateInfo, apiInfo, err := task.auth.SetupAuthentication(machine) if err != nil { logger.Errorf("failed to setup authentication: %v", err) return nil, err } // Generated a nonce for the new instance, with the format: "machine-#:UUID". // The first part is a badge, specifying the tag of the machine the provisioner // is running on, while the second part is a random UUID. uuid, err := utils.NewUUID() if err != nil { return nil, err } nonce := fmt.Sprintf("%s:%s", task.machineTag, uuid.String()) machineConfig := environs.NewMachineConfig(machine.Id(), nonce, stateInfo, apiInfo) return machineConfig, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/lxc-broker_test.go������������������0000644�0000153�0000161�00000022520�12321735776�030224� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner_test import ( "fmt" "io/ioutil" "path/filepath" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/container/lxc/mock" lxctesting "launchpad.net/juju-core/container/lxc/testing" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" instancetest "launchpad.net/juju-core/instance/testing" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker/provisioner" ) type lxcSuite struct { lxctesting.TestSuite events chan mock.Event } type lxcBrokerSuite struct { lxcSuite broker environs.InstanceBroker agentConfig agent.Config } var _ = gc.Suite(&lxcBrokerSuite{}) func (s *lxcSuite) SetUpTest(c *gc.C) { s.TestSuite.SetUpTest(c) s.events = make(chan mock.Event) go func() { for event := range s.events { c.Output(3, fmt.Sprintf("lxc event: <%s, %s>", event.Action, event.InstanceId)) } }() s.TestSuite.Factory.AddListener(s.events) } func (s *lxcSuite) TearDownTest(c *gc.C) { close(s.events) s.TestSuite.TearDownTest(c) } func (s *lxcBrokerSuite) SetUpTest(c *gc.C) { s.lxcSuite.SetUpTest(c) tools := &coretools.Tools{ Version: version.MustParseBinary("2.3.4-foo-bar"), URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", } var err error s.agentConfig, err = agent.NewAgentConfig( agent.AgentConfigParams{ DataDir: "/not/used/here", Tag: "tag", UpgradedToVersion: version.Current.Number, Password: "dummy-secret", Nonce: "nonce", APIAddresses: []string{"10.0.0.1:1234"}, CACert: []byte(coretesting.CACert), }) c.Assert(err, gc.IsNil) s.broker, err = provisioner.NewLxcBroker(&fakeAPI{}, tools, s.agentConfig) c.Assert(err, gc.IsNil) } func (s *lxcBrokerSuite) startInstance(c *gc.C, machineId string) instance.Instance { machineNonce := "fake-nonce" stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, machineNonce, stateInfo, apiInfo) cons := constraints.Value{} possibleTools := s.broker.(coretools.HasTools).Tools() lxc, _, err := s.broker.StartInstance(environs.StartInstanceParams{ Constraints: cons, Tools: possibleTools, MachineConfig: machineConfig, }) c.Assert(err, gc.IsNil) return lxc } func (s *lxcBrokerSuite) TestStartInstance(c *gc.C) { machineId := "1/lxc/0" lxc := s.startInstance(c, machineId) c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0")) c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory) s.assertInstances(c, lxc) // Uses default network config lxcConfContents, err := ioutil.ReadFile(filepath.Join(s.ContainerDir, string(lxc.Id()), "lxc.conf")) c.Assert(err, gc.IsNil) c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.type = veth") c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.link = lxcbr0") } func (s *lxcBrokerSuite) TestStartInstanceWithBridgeEnviron(c *gc.C) { s.agentConfig.SetValue(agent.LxcBridge, "br0") machineId := "1/lxc/0" lxc := s.startInstance(c, machineId) c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0")) c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory) s.assertInstances(c, lxc) // Uses default network config lxcConfContents, err := ioutil.ReadFile(filepath.Join(s.ContainerDir, string(lxc.Id()), "lxc.conf")) c.Assert(err, gc.IsNil) c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.type = veth") c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.link = br0") } func (s *lxcBrokerSuite) TestStopInstance(c *gc.C) { lxc0 := s.startInstance(c, "1/lxc/0") lxc1 := s.startInstance(c, "1/lxc/1") lxc2 := s.startInstance(c, "1/lxc/2") err := s.broker.StopInstances([]instance.Instance{lxc0}) c.Assert(err, gc.IsNil) s.assertInstances(c, lxc1, lxc2) c.Assert(s.lxcContainerDir(lxc0), jc.DoesNotExist) c.Assert(s.lxcRemovedContainerDir(lxc0), jc.IsDirectory) err = s.broker.StopInstances([]instance.Instance{lxc1, lxc2}) c.Assert(err, gc.IsNil) s.assertInstances(c) } func (s *lxcBrokerSuite) TestAllInstances(c *gc.C) { lxc0 := s.startInstance(c, "1/lxc/0") lxc1 := s.startInstance(c, "1/lxc/1") s.assertInstances(c, lxc0, lxc1) err := s.broker.StopInstances([]instance.Instance{lxc1}) c.Assert(err, gc.IsNil) lxc2 := s.startInstance(c, "1/lxc/2") s.assertInstances(c, lxc0, lxc2) } func (s *lxcBrokerSuite) assertInstances(c *gc.C, inst ...instance.Instance) { results, err := s.broker.AllInstances() c.Assert(err, gc.IsNil) instancetest.MatchInstances(c, results, inst...) } func (s *lxcBrokerSuite) lxcContainerDir(inst instance.Instance) string { return filepath.Join(s.ContainerDir, string(inst.Id())) } func (s *lxcBrokerSuite) lxcRemovedContainerDir(inst instance.Instance) string { return filepath.Join(s.RemovedDir, string(inst.Id())) } type lxcProvisionerSuite struct { CommonProvisionerSuite lxcSuite parentMachineId string events chan mock.Event } var _ = gc.Suite(&lxcProvisionerSuite{}) func (s *lxcProvisionerSuite) SetUpSuite(c *gc.C) { s.CommonProvisionerSuite.SetUpSuite(c) s.lxcSuite.SetUpSuite(c) } func (s *lxcProvisionerSuite) TearDownSuite(c *gc.C) { s.lxcSuite.TearDownSuite(c) s.CommonProvisionerSuite.TearDownSuite(c) } func (s *lxcProvisionerSuite) SetUpTest(c *gc.C) { s.CommonProvisionerSuite.SetUpTest(c) s.lxcSuite.SetUpTest(c) // The lxc provisioner actually needs the machine it is being created on // to be in state, in order to get the watcher. m, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits, state.JobManageEnviron) c.Assert(err, gc.IsNil) err = m.SetAddresses([]instance.Address{ instance.NewAddress("0.1.2.3"), }) c.Assert(err, gc.IsNil) s.parentMachineId = m.Id() s.APILogin(c, m) err = m.SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) s.events = make(chan mock.Event, 25) s.Factory.AddListener(s.events) } func (s *lxcProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string { s.State.StartSync() event := <-s.events c.Assert(event.Action, gc.Equals, mock.Created) event = <-s.events c.Assert(event.Action, gc.Equals, mock.Started) err := machine.Refresh() c.Assert(err, gc.IsNil) s.waitInstanceId(c, machine, instance.Id(event.InstanceId)) return event.InstanceId } func (s *lxcProvisionerSuite) expectStopped(c *gc.C, instId string) { s.State.StartSync() event := <-s.events c.Assert(event.Action, gc.Equals, mock.Stopped) event = <-s.events c.Assert(event.Action, gc.Equals, mock.Destroyed) c.Assert(event.InstanceId, gc.Equals, instId) } func (s *lxcProvisionerSuite) expectNoEvents(c *gc.C) { select { case event := <-s.events: c.Fatalf("unexpected event %#v", event) case <-time.After(coretesting.ShortWait): return } } func (s *lxcProvisionerSuite) TearDownTest(c *gc.C) { close(s.events) s.lxcSuite.TearDownTest(c) s.CommonProvisionerSuite.TearDownTest(c) } func (s *lxcProvisionerSuite) newLxcProvisioner(c *gc.C) provisioner.Provisioner { parentMachineTag := names.MachineTag(s.parentMachineId) agentConfig := s.AgentConfigForTag(c, parentMachineTag) tools, err := s.provisioner.Tools(agentConfig.Tag()) c.Assert(err, gc.IsNil) broker, err := provisioner.NewLxcBroker(s.provisioner, tools, agentConfig) c.Assert(err, gc.IsNil) return provisioner.NewContainerProvisioner(instance.LXC, s.provisioner, agentConfig, broker) } func (s *lxcProvisionerSuite) TestProvisionerStartStop(c *gc.C) { p := s.newLxcProvisioner(c) c.Assert(p.Stop(), gc.IsNil) } func (s *lxcProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) { p := s.newLxcProvisioner(c) defer stop(c, p) // Check that an instance is not provisioned when the machine is created. _, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) c.Assert(err, gc.IsNil) s.expectNoEvents(c) } func (s *lxcProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) { p := s.newLxcProvisioner(c) defer stop(c, p) w, err := provisioner.GetRetryWatcher(p) c.Assert(w, gc.IsNil) c.Assert(errors.IsNotImplementedError(err), jc.IsTrue) } func (s *lxcProvisionerSuite) addContainer(c *gc.C) *state.Machine { template := state.MachineTemplate{ Series: coretesting.FakeDefaultSeries, Jobs: []state.MachineJob{state.JobHostUnits}, } container, err := s.State.AddMachineInsideMachine(template, s.parentMachineId, instance.LXC) c.Assert(err, gc.IsNil) return container } func (s *lxcProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) { p := s.newLxcProvisioner(c) defer stop(c, p) container := s.addContainer(c) instId := s.expectStarted(c, container) // ...and removed, along with the machine, when the machine is Dead. c.Assert(container.EnsureDead(), gc.IsNil) s.expectStopped(c, instId) s.waitRemoved(c, container) } type fakeAPI struct{} func (*fakeAPI) ContainerConfig() (params.ContainerConfig, error) { return params.ContainerConfig{ ProviderType: "fake", AuthorizedKeys: coretesting.FakeAuthKeys, SSLHostnameVerification: true}, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/lxc-broker.go�����������������������0000644�0000153�0000161�00000006434�12321735776�027173� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner import ( "github.com/juju/loggo" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/lxc" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/tools" ) var lxcLogger = loggo.GetLogger("juju.provisioner.lxc") var _ environs.InstanceBroker = (*lxcBroker)(nil) var _ tools.HasTools = (*lxcBroker)(nil) type APICalls interface { ContainerConfig() (params.ContainerConfig, error) } func NewLxcBroker(api APICalls, tools *tools.Tools, agentConfig agent.Config) (environs.InstanceBroker, error) { manager, err := lxc.NewContainerManager(container.ManagerConfig{container.ConfigName: "juju"}) if err != nil { return nil, err } return &lxcBroker{ manager: manager, api: api, tools: tools, agentConfig: agentConfig, }, nil } type lxcBroker struct { manager container.Manager api APICalls tools *tools.Tools agentConfig agent.Config } func (broker *lxcBroker) Tools() tools.List { return tools.List{broker.tools} } // StartInstance is specified in the Broker interface. func (broker *lxcBroker) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { // TODO: refactor common code out of the container brokers. machineId := args.MachineConfig.MachineId lxcLogger.Infof("starting lxc container for machineId: %s", machineId) // Default to using the host network until we can configure. bridgeDevice := broker.agentConfig.Value(agent.LxcBridge) if bridgeDevice == "" { bridgeDevice = lxc.DefaultLxcBridge } network := container.BridgeNetworkConfig(bridgeDevice) series := args.Tools.OneSeries() args.MachineConfig.MachineContainerType = instance.LXC args.MachineConfig.Tools = args.Tools[0] config, err := broker.api.ContainerConfig() if err != nil { lxcLogger.Errorf("failed to get container config: %v", err) return nil, nil, err } if err := environs.PopulateMachineConfig( args.MachineConfig, config.ProviderType, config.AuthorizedKeys, config.SSLHostnameVerification, config.Proxy, config.AptProxy, ); err != nil { lxcLogger.Errorf("failed to populate machine config: %v", err) return nil, nil, err } inst, hardware, err := broker.manager.CreateContainer(args.MachineConfig, series, network) if err != nil { lxcLogger.Errorf("failed to start container: %v", err) return nil, nil, err } lxcLogger.Infof("started lxc container for machineId: %s, %s, %s", machineId, inst.Id(), hardware.String()) return inst, hardware, nil } // StopInstances shuts down the given instances. func (broker *lxcBroker) StopInstances(instances []instance.Instance) error { // TODO: potentially parallelise. for _, instance := range instances { lxcLogger.Infof("stopping lxc container for instance: %s", instance.Id()) if err := broker.manager.DestroyContainer(instance); err != nil { lxcLogger.Errorf("container did not stop: %v", err) return err } } return nil } // AllInstances only returns running containers. func (broker *lxcBroker) AllInstances() (result []instance.Instance, err error) { return broker.manager.ListContainers() } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/provisioner.go����������������������0000644�0000153�0000161�00000016200�12321735776�027472� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner import ( "sync" "github.com/juju/loggo" "launchpad.net/tomb" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" apiprovisioner "launchpad.net/juju-core/state/api/provisioner" apiwatcher "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/worker" ) var logger = loggo.GetLogger("juju.provisioner") // Ensure our structs implement the required Provisioner interface. var _ Provisioner = (*environProvisioner)(nil) var _ Provisioner = (*containerProvisioner)(nil) // Provisioner represents a running provisioner worker. type Provisioner interface { worker.Worker Stop() error getMachineWatcher() (apiwatcher.StringsWatcher, error) getRetryWatcher() (apiwatcher.NotifyWatcher, error) } // environProvisioner represents a running provisioning worker for machine nodes // belonging to an environment. type environProvisioner struct { provisioner environ environs.Environ configObserver } // containerProvisioner represents a running provisioning worker for containers // hosted on a machine. type containerProvisioner struct { provisioner containerType instance.ContainerType machine *apiprovisioner.Machine } // provisioner providers common behaviour for a running provisioning worker. type provisioner struct { Provisioner st *apiprovisioner.State agentConfig agent.Config broker environs.InstanceBroker tomb tomb.Tomb } // configObserver is implemented so that tests can see // when the environment configuration changes. type configObserver struct { sync.Mutex observer chan<- *config.Config } // notify notifies the observer of a configuration change. func (o *configObserver) notify(cfg *config.Config) { o.Lock() if o.observer != nil { o.observer <- cfg } o.Unlock() } // Err returns the reason why the provisioner has stopped or tomb.ErrStillAlive // when it is still alive. func (p *provisioner) Err() (reason error) { return p.tomb.Err() } // Kill implements worker.Worker.Kill. func (p *provisioner) Kill() { p.tomb.Kill(nil) } // Wait implements worker.Worker.Wait. func (p *provisioner) Wait() error { return p.tomb.Wait() } // Stop stops the provisioner and returns any error encountered while // provisioning. func (p *provisioner) Stop() error { p.tomb.Kill(nil) return p.tomb.Wait() } // getStartTask creates a new worker for the provisioner, func (p *provisioner) getStartTask(safeMode bool) (ProvisionerTask, error) { auth, err := environs.NewAPIAuthenticator(p.st) if err != nil { return nil, err } // Start responding to changes in machines, and to any further updates // to the environment config. machineWatcher, err := p.getMachineWatcher() if err != nil { return nil, err } retryWatcher, err := p.getRetryWatcher() if err != nil && !errors.IsNotImplementedError(err) { return nil, err } task := NewProvisionerTask( p.agentConfig.Tag(), safeMode, p.st, machineWatcher, retryWatcher, p.broker, auth) return task, nil } // NewEnvironProvisioner returns a new Provisioner for an environment. // When new machines are added to the state, it allocates instances // from the environment and allocates them to the new machines. func NewEnvironProvisioner(st *apiprovisioner.State, agentConfig agent.Config) Provisioner { p := &environProvisioner{ provisioner: provisioner{ st: st, agentConfig: agentConfig, }, } p.Provisioner = p logger.Tracef("Starting environ provisioner for %q", p.agentConfig.Tag()) go func() { defer p.tomb.Done() p.tomb.Kill(p.loop()) }() return p } func (p *environProvisioner) loop() error { var environConfigChanges <-chan struct{} environWatcher, err := p.st.WatchForEnvironConfigChanges() if err != nil { return err } environConfigChanges = environWatcher.Changes() defer watcher.Stop(environWatcher, &p.tomb) p.environ, err = worker.WaitForEnviron(environWatcher, p.st, p.tomb.Dying()) if err != nil { return err } p.broker = p.environ safeMode := p.environ.Config().ProvisionerSafeMode() task, err := p.getStartTask(safeMode) if err != nil { return err } defer watcher.Stop(task, &p.tomb) for { select { case <-p.tomb.Dying(): return tomb.ErrDying case <-task.Dying(): err := task.Err() logger.Errorf("environ provisioner died: %v", err) return err case _, ok := <-environConfigChanges: if !ok { return watcher.MustErr(environWatcher) } environConfig, err := p.st.EnvironConfig() if err != nil { logger.Errorf("cannot load environment configuration: %v", err) return err } if err := p.setConfig(environConfig); err != nil { logger.Errorf("loaded invalid environment configuration: %v", err) } task.SetSafeMode(environConfig.ProvisionerSafeMode()) } } } func (p *environProvisioner) getMachineWatcher() (apiwatcher.StringsWatcher, error) { return p.st.WatchEnvironMachines() } func (p *environProvisioner) getRetryWatcher() (apiwatcher.NotifyWatcher, error) { return p.st.WatchMachineErrorRetry() } // setConfig updates the environment configuration and notifies // the config observer. func (p *environProvisioner) setConfig(environConfig *config.Config) error { if err := p.environ.SetConfig(environConfig); err != nil { return err } p.configObserver.notify(environConfig) return nil } // NewContainerProvisioner returns a new Provisioner. When new machines // are added to the state, it allocates instances from the environment // and allocates them to the new machines. func NewContainerProvisioner(containerType instance.ContainerType, st *apiprovisioner.State, agentConfig agent.Config, broker environs.InstanceBroker) Provisioner { p := &containerProvisioner{ provisioner: provisioner{ st: st, agentConfig: agentConfig, broker: broker, }, containerType: containerType, } p.Provisioner = p logger.Tracef("Starting %s provisioner for %q", p.containerType, p.agentConfig.Tag()) go func() { defer p.tomb.Done() p.tomb.Kill(p.loop()) }() return p } func (p *containerProvisioner) loop() error { task, err := p.getStartTask(false) if err != nil { return err } defer watcher.Stop(task, &p.tomb) for { select { case <-p.tomb.Dying(): return tomb.ErrDying case <-task.Dying(): err := task.Err() logger.Errorf("%s provisioner died: %v", p.containerType, err) return err } } } func (p *containerProvisioner) getMachine() (*apiprovisioner.Machine, error) { if p.machine == nil { var err error if p.machine, err = p.st.Machine(p.agentConfig.Tag()); err != nil { logger.Errorf("%s is not in state", p.agentConfig.Tag()) return nil, err } } return p.machine, nil } func (p *containerProvisioner) getMachineWatcher() (apiwatcher.StringsWatcher, error) { machine, err := p.getMachine() if err != nil { return nil, err } return machine.WatchContainers(p.containerType) } func (p *containerProvisioner) getRetryWatcher() (apiwatcher.NotifyWatcher, error) { return nil, errors.NewNotImplementedError("getRetryWatcher") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/provisioner_test.go�����������������0000644�0000153�0000161�00000057274�12321735776�030551� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner_test import ( "fmt" "strings" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/names" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" apiprovisioner "launchpad.net/juju-core/state/api/provisioner" apiserverprovisioner "launchpad.net/juju-core/state/apiserver/provisioner" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/worker/provisioner" ) type CommonProvisionerSuite struct { testing.JujuConnSuite op <-chan dummy.Operation cfg *config.Config // // defaultConstraints are used when adding a machine and then later in test assertions. defaultConstraints constraints.Value st *api.State provisioner *apiprovisioner.State } type ProvisionerSuite struct { CommonProvisionerSuite } var _ = gc.Suite(&ProvisionerSuite{}) var veryShortAttempt = utils.AttemptStrategy{ Total: 1 * time.Second, Delay: 80 * time.Millisecond, } func (s *CommonProvisionerSuite) SetUpSuite(c *gc.C) { s.JujuConnSuite.SetUpSuite(c) s.defaultConstraints = constraints.MustParse("arch=amd64 mem=4G cpu-cores=1 root-disk=8G") } func (s *CommonProvisionerSuite) SetUpTest(c *gc.C) { // Disable the default state policy, because the // provisioner needs to be able to test pathological // scenarios where a machine exists in state with // invalid environment config. dummy.SetStatePolicy(nil) s.JujuConnSuite.SetUpTest(c) // Create the operations channel with more than enough space // for those tests that don't listen on it. op := make(chan dummy.Operation, 500) dummy.Listen(op) s.op = op cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) s.cfg = cfg } func (s *CommonProvisionerSuite) APILogin(c *gc.C, machine *state.Machine) { if s.st != nil { c.Assert(s.st.Close(), gc.IsNil) } password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = machine.SetPassword(password) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("i-fake", "fake_nonce", nil) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAsMachine(c, machine.Tag(), password, "fake_nonce") c.Assert(s.st, gc.NotNil) c.Logf("API: login as %q successful", machine.Tag()) s.provisioner = s.st.Provisioner() c.Assert(s.provisioner, gc.NotNil) } // breakDummyProvider changes the environment config in state in a way // that causes the given environMethod of the dummy provider to return // an error, which is also returned as a message to be checked. func breakDummyProvider(c *gc.C, st *state.State, environMethod string) string { attrs := map[string]interface{}{"broken": environMethod} err := st.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) return fmt.Sprintf("dummy.%s is broken", environMethod) } // setupEnvironment adds an environment manager machine and login to the API. func (s *CommonProvisionerSuite) setupEnvironmentManager(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) c.Assert(machine.Id(), gc.Equals, "0") err = machine.SetAddresses([]instance.Address{ instance.NewAddress("0.1.2.3"), }) c.Assert(err, gc.IsNil) s.APILogin(c, machine) } // invalidateEnvironment alters the environment configuration // so the Settings returned from the watcher will not pass // validation. func (s *CommonProvisionerSuite) invalidateEnvironment(c *gc.C) { st, err := state.Open(s.StateInfo(c), state.DefaultDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st.Close() attrs := map[string]interface{}{"type": "unknown"} err = st.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) } // fixEnvironment undoes the work of invalidateEnvironment. func (s *CommonProvisionerSuite) fixEnvironment(c *gc.C) error { st, err := state.Open(s.StateInfo(c), state.DefaultDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st.Close() attrs := map[string]interface{}{"type": s.cfg.AllAttrs()["type"]} return st.UpdateEnvironConfig(attrs, nil, nil) } // stopper is stoppable. type stopper interface { Stop() error } // stop stops a stopper. func stop(c *gc.C, s stopper) { c.Assert(s.Stop(), gc.IsNil) } func (s *CommonProvisionerSuite) startUnknownInstance(c *gc.C, id string) instance.Instance { instance, _ := testing.AssertStartInstance(c, s.Conn.Environ, id) select { case o := <-s.op: switch o := o.(type) { case dummy.OpStartInstance: default: c.Fatalf("unexpected operation %#v", o) } case <-time.After(coretesting.LongWait): c.Fatalf("timed out waiting for startinstance operation") } return instance } func (s *CommonProvisionerSuite) checkStartInstance(c *gc.C, m *state.Machine) instance.Instance { return s.checkStartInstanceCustom(c, m, "pork", s.defaultConstraints) } func (s *CommonProvisionerSuite) checkStartInstanceCustom(c *gc.C, m *state.Machine, secret string, cons constraints.Value) (inst instance.Instance) { s.BackingState.StartSync() for { select { case o := <-s.op: switch o := o.(type) { case dummy.OpStartInstance: inst = o.Instance s.waitInstanceId(c, m, inst.Id()) // Check the instance was started with the expected params. c.Assert(o.MachineId, gc.Equals, m.Id()) nonceParts := strings.SplitN(o.MachineNonce, ":", 2) c.Assert(nonceParts, gc.HasLen, 2) c.Assert(nonceParts[0], gc.Equals, names.MachineTag("0")) c.Assert(nonceParts[1], jc.Satisfies, utils.IsValidUUIDString) c.Assert(o.Secret, gc.Equals, secret) c.Assert(o.Constraints, gc.DeepEquals, cons) // All provisioned machines in this test suite have their hardware characteristics // attributes set to the same values as the constraints due to the dummy environment being used. hc, err := m.HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Assert(*hc, gc.DeepEquals, instance.HardwareCharacteristics{ Arch: cons.Arch, Mem: cons.Mem, RootDisk: cons.RootDisk, CpuCores: cons.CpuCores, CpuPower: cons.CpuPower, Tags: cons.Tags, }) return default: c.Logf("ignoring unexpected operation %#v", o) } case <-time.After(2 * time.Second): c.Fatalf("provisioner did not start an instance") return } } return } // checkNoOperations checks that the environ was not operated upon. func (s *CommonProvisionerSuite) checkNoOperations(c *gc.C) { s.BackingState.StartSync() select { case o := <-s.op: c.Fatalf("unexpected operation %#v", o) case <-time.After(coretesting.ShortWait): return } } // checkStopInstances checks that an instance has been stopped. func (s *CommonProvisionerSuite) checkStopInstances(c *gc.C, instances ...instance.Instance) { s.checkStopSomeInstances(c, instances, nil) } // checkStopSomeInstances checks that instancesToStop are stopped while instancesToKeep are not. func (s *CommonProvisionerSuite) checkStopSomeInstances(c *gc.C, instancesToStop []instance.Instance, instancesToKeep []instance.Instance) { s.BackingState.StartSync() instanceIdsToStop := set.NewStrings() for _, instance := range instancesToStop { instanceIdsToStop.Add(string(instance.Id())) } instanceIdsToKeep := set.NewStrings() for _, instance := range instancesToKeep { instanceIdsToKeep.Add(string(instance.Id())) } // Continue checking for stop instance calls until all the instances we // are waiting on to finish, actually finish, or we time out. for !instanceIdsToStop.IsEmpty() { select { case o := <-s.op: switch o := o.(type) { case dummy.OpStopInstances: for _, stoppedInstance := range o.Instances { instId := string(stoppedInstance.Id()) instanceIdsToStop.Remove(instId) if instanceIdsToKeep.Contains(instId) { c.Errorf("provisioner unexpectedly stopped instance %s", instId) } } default: c.Fatalf("unexpected operation %#v", o) return } case <-time.After(2 * time.Second): c.Fatalf("provisioner did not stop an instance") return } } } func (s *CommonProvisionerSuite) waitMachine(c *gc.C, m *state.Machine, check func() bool) { // TODO(jam): We need to grow a new method on NotifyWatcherC // that calls StartSync while waiting for changes, then // waitMachine and waitHardwareCharacteristics can use that // instead w := m.Watch() defer stop(c, w) timeout := time.After(coretesting.LongWait) resync := time.After(0) for { select { case <-w.Changes(): if check() { return } case <-resync: resync = time.After(coretesting.ShortWait) s.BackingState.StartSync() case <-timeout: c.Fatalf("machine %v wait timed out", m) } } } func (s *CommonProvisionerSuite) waitHardwareCharacteristics(c *gc.C, m *state.Machine, check func() bool) { w := m.WatchHardwareCharacteristics() defer stop(c, w) timeout := time.After(coretesting.LongWait) resync := time.After(0) for { select { case <-w.Changes(): if check() { return } case <-resync: resync = time.After(coretesting.ShortWait) s.BackingState.StartSync() case <-timeout: c.Fatalf("hardware characteristics for machine %v wait timed out", m) } } } // waitRemoved waits for the supplied machine to be removed from state. func (s *CommonProvisionerSuite) waitRemoved(c *gc.C, m *state.Machine) { s.waitMachine(c, m, func() bool { err := m.Refresh() if errors.IsNotFoundError(err) { return true } c.Assert(err, gc.IsNil) c.Logf("machine %v is still %s", m, m.Life()) return false }) } // waitInstanceId waits until the supplied machine has an instance id, then // asserts it is as expected. func (s *CommonProvisionerSuite) waitInstanceId(c *gc.C, m *state.Machine, expect instance.Id) { s.waitHardwareCharacteristics(c, m, func() bool { if actual, err := m.InstanceId(); err == nil { c.Assert(actual, gc.Equals, expect) return true } else if !state.IsNotProvisionedError(err) { // We don't expect any errors. panic(err) } c.Logf("machine %v is still unprovisioned", m) return false }) } func (s *ProvisionerSuite) SetUpTest(c *gc.C) { s.CommonProvisionerSuite.SetUpTest(c) s.CommonProvisionerSuite.setupEnvironmentManager(c) } func (s *ProvisionerSuite) newEnvironProvisioner(c *gc.C) provisioner.Provisioner { machineTag := "machine-0" agentConfig := s.AgentConfigForTag(c, machineTag) return provisioner.NewEnvironProvisioner(s.provisioner, agentConfig) } func (s *ProvisionerSuite) TestProvisionerStartStop(c *gc.C) { p := s.newEnvironProvisioner(c) c.Assert(p.Stop(), gc.IsNil) } func (s *ProvisionerSuite) addMachine() (*state.Machine, error) { return s.BackingState.AddOneMachine(state.MachineTemplate{ Series: coretesting.FakeDefaultSeries, Jobs: []state.MachineJob{state.JobHostUnits}, Constraints: s.defaultConstraints, }) } func (s *ProvisionerSuite) TestSimple(c *gc.C) { p := s.newEnvironProvisioner(c) defer stop(c, p) // Check that an instance is provisioned when the machine is created... m, err := s.addMachine() c.Assert(err, gc.IsNil) instance := s.checkStartInstance(c, m) // ...and removed, along with the machine, when the machine is Dead. c.Assert(m.EnsureDead(), gc.IsNil) s.checkStopInstances(c, instance) s.waitRemoved(c, m) } func (s *ProvisionerSuite) TestConstraints(c *gc.C) { // Create a machine with non-standard constraints. m, err := s.addMachine() c.Assert(err, gc.IsNil) cons := constraints.MustParse("mem=8G arch=amd64 cpu-cores=2 root-disk=10G") err = m.SetConstraints(cons) c.Assert(err, gc.IsNil) // Start a provisioner and check those constraints are used. p := s.newEnvironProvisioner(c) defer stop(c, p) s.checkStartInstanceCustom(c, m, "pork", cons) } func (s *ProvisionerSuite) TestProvisionerSetsErrorStatusWhenStartInstanceFailed(c *gc.C) { brokenMsg := breakDummyProvider(c, s.State, "StartInstance") p := s.newEnvironProvisioner(c) defer stop(c, p) // Check that an instance is not provisioned when the machine is created... m, err := s.addMachine() c.Assert(err, gc.IsNil) s.checkNoOperations(c) t0 := time.Now() for time.Since(t0) < coretesting.LongWait { // And check the machine status is set to error. status, info, _, err := m.Status() c.Assert(err, gc.IsNil) if status == params.StatusPending { time.Sleep(coretesting.ShortWait) continue } c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, brokenMsg) break } // Unbreak the environ config. err = s.fixEnvironment(c) c.Assert(err, gc.IsNil) // Restart the PA to make sure the machine is skipped again. stop(c, p) p = s.newEnvironProvisioner(c) defer stop(c, p) s.checkNoOperations(c) } func (s *ProvisionerSuite) TestProvisioningDoesNotOccurForContainers(c *gc.C) { p := s.newEnvironProvisioner(c) defer stop(c, p) // create a machine to host the container. m, err := s.addMachine() c.Assert(err, gc.IsNil) inst := s.checkStartInstance(c, m) // make a container on the machine we just created template := state.MachineTemplate{ Series: coretesting.FakeDefaultSeries, Jobs: []state.MachineJob{state.JobHostUnits}, } container, err := s.State.AddMachineInsideMachine(template, m.Id(), instance.LXC) c.Assert(err, gc.IsNil) // the PA should not attempt to create it s.checkNoOperations(c) // cleanup c.Assert(container.EnsureDead(), gc.IsNil) c.Assert(container.Remove(), gc.IsNil) c.Assert(m.EnsureDead(), gc.IsNil) s.checkStopInstances(c, inst) s.waitRemoved(c, m) } func (s *ProvisionerSuite) TestProvisioningDoesNotOccurWithAnInvalidEnvironment(c *gc.C) { s.invalidateEnvironment(c) p := s.newEnvironProvisioner(c) defer stop(c, p) // try to create a machine _, err := s.addMachine() c.Assert(err, gc.IsNil) // the PA should not create it s.checkNoOperations(c) } func (s *ProvisionerSuite) TestProvisioningOccursWithFixedEnvironment(c *gc.C) { s.invalidateEnvironment(c) p := s.newEnvironProvisioner(c) defer stop(c, p) // try to create a machine m, err := s.addMachine() c.Assert(err, gc.IsNil) // the PA should not create it s.checkNoOperations(c) err = s.fixEnvironment(c) c.Assert(err, gc.IsNil) s.checkStartInstance(c, m) } func (s *ProvisionerSuite) TestProvisioningDoesOccurAfterInvalidEnvironmentPublished(c *gc.C) { p := s.newEnvironProvisioner(c) defer stop(c, p) // place a new machine into the state m, err := s.addMachine() c.Assert(err, gc.IsNil) s.checkStartInstance(c, m) s.invalidateEnvironment(c) // create a second machine m, err = s.addMachine() c.Assert(err, gc.IsNil) // the PA should create it using the old environment s.checkStartInstance(c, m) } func (s *ProvisionerSuite) TestProvisioningDoesNotProvisionTheSameMachineAfterRestart(c *gc.C) { p := s.newEnvironProvisioner(c) defer stop(c, p) // create a machine m, err := s.addMachine() c.Assert(err, gc.IsNil) s.checkStartInstance(c, m) // restart the PA stop(c, p) p = s.newEnvironProvisioner(c) defer stop(c, p) // check that there is only one machine provisioned. machines, err := s.State.AllMachines() c.Assert(err, gc.IsNil) c.Check(len(machines), gc.Equals, 2) c.Check(machines[0].Id(), gc.Equals, "0") c.Check(machines[1].CheckProvisioned("fake_nonce"), jc.IsFalse) // the PA should not create it a second time s.checkNoOperations(c) } func (s *ProvisionerSuite) TestProvisioningStopsInstances(c *gc.C) { p := s.newEnvironProvisioner(c) defer stop(c, p) // create a machine m0, err := s.addMachine() c.Assert(err, gc.IsNil) i0 := s.checkStartInstance(c, m0) // create a second machine m1, err := s.addMachine() c.Assert(err, gc.IsNil) i1 := s.checkStartInstance(c, m1) stop(c, p) // mark the first machine as dead c.Assert(m0.EnsureDead(), gc.IsNil) // remove the second machine entirely c.Assert(m1.EnsureDead(), gc.IsNil) c.Assert(m1.Remove(), gc.IsNil) // start a new provisioner to shut them both down p = s.newEnvironProvisioner(c) defer stop(c, p) s.checkStopInstances(c, i0, i1) s.waitRemoved(c, m0) } func (s *ProvisionerSuite) TestDyingMachines(c *gc.C) { p := s.newEnvironProvisioner(c) defer stop(c, p) // provision a machine m0, err := s.addMachine() c.Assert(err, gc.IsNil) s.checkStartInstance(c, m0) // stop the provisioner and make the machine dying stop(c, p) err = m0.Destroy() c.Assert(err, gc.IsNil) // add a new, dying, unprovisioned machine m1, err := s.addMachine() c.Assert(err, gc.IsNil) err = m1.Destroy() c.Assert(err, gc.IsNil) // start the provisioner and wait for it to reap the useless machine p = s.newEnvironProvisioner(c) defer stop(c, p) s.checkNoOperations(c) s.waitRemoved(c, m1) // verify the other one's still fine err = m0.Refresh() c.Assert(err, gc.IsNil) c.Assert(m0.Life(), gc.Equals, state.Dying) } func (s *ProvisionerSuite) TestProvisioningRecoversAfterInvalidEnvironmentPublished(c *gc.C) { p := s.newEnvironProvisioner(c) defer stop(c, p) // place a new machine into the state m, err := s.addMachine() c.Assert(err, gc.IsNil) s.checkStartInstance(c, m) s.invalidateEnvironment(c) s.BackingState.StartSync() // create a second machine m, err = s.addMachine() c.Assert(err, gc.IsNil) // the PA should create it using the old environment s.checkStartInstance(c, m) err = s.fixEnvironment(c) c.Assert(err, gc.IsNil) // insert our observer cfgObserver := make(chan *config.Config, 1) provisioner.SetObserver(p, cfgObserver) err = s.State.UpdateEnvironConfig(map[string]interface{}{"secret": "beef"}, nil, nil) c.Assert(err, gc.IsNil) s.BackingState.StartSync() // wait for the PA to load the new configuration select { case <-cfgObserver: case <-time.After(coretesting.LongWait): c.Fatalf("PA did not action config change") } // create a third machine m, err = s.addMachine() c.Assert(err, gc.IsNil) // the PA should create it using the new environment s.checkStartInstanceCustom(c, m, "beef", s.defaultConstraints) } func (s *ProvisionerSuite) TestProvisioningSafeMode(c *gc.C) { p := s.newEnvironProvisioner(c) defer stop(c, p) // create a machine m0, err := s.addMachine() c.Assert(err, gc.IsNil) i0 := s.checkStartInstance(c, m0) // create a second machine m1, err := s.addMachine() c.Assert(err, gc.IsNil) i1 := s.checkStartInstance(c, m1) stop(c, p) // mark the first machine as dead c.Assert(m0.EnsureDead(), gc.IsNil) // remove the second machine entirely from state c.Assert(m1.EnsureDead(), gc.IsNil) c.Assert(m1.Remove(), gc.IsNil) // turn on safe mode attrs := map[string]interface{}{"provisioner-safe-mode": true} err = s.State.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) // start a new provisioner to shut down only the machine still in state. p = s.newEnvironProvisioner(c) defer stop(c, p) s.checkStopSomeInstances(c, []instance.Instance{i0}, []instance.Instance{i1}) s.waitRemoved(c, m0) } func (s *ProvisionerSuite) TestProvisioningSafeModeChange(c *gc.C) { p := s.newEnvironProvisioner(c) defer stop(c, p) // First check that safe mode is initially off. // create a machine m0, err := s.addMachine() c.Assert(err, gc.IsNil) i0 := s.checkStartInstance(c, m0) // create a second machine m1, err := s.addMachine() c.Assert(err, gc.IsNil) i1 := s.checkStartInstance(c, m1) // mark the first machine as dead c.Assert(m0.EnsureDead(), gc.IsNil) // remove the second machine entirely from state c.Assert(m1.EnsureDead(), gc.IsNil) c.Assert(m1.Remove(), gc.IsNil) s.checkStopInstances(c, i0, i1) s.waitRemoved(c, m0) // insert our observer cfgObserver := make(chan *config.Config, 1) provisioner.SetObserver(p, cfgObserver) // turn on safe mode attrs := map[string]interface{}{"provisioner-safe-mode": true} err = s.State.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) s.BackingState.StartSync() // wait for the PA to load the new configuration select { case <-cfgObserver: case <-time.After(coretesting.LongWait): c.Fatalf("PA did not action config change") } // Now check that the provisioner has noticed safe mode is on. // create a machine m3, err := s.addMachine() c.Assert(err, gc.IsNil) i3 := s.checkStartInstance(c, m3) // create an instance out of band i4 := s.startUnknownInstance(c, "999") // mark the machine as dead c.Assert(m3.EnsureDead(), gc.IsNil) // check the machine's instance is stopped, and the other isn't s.checkStopSomeInstances(c, []instance.Instance{i3}, []instance.Instance{i4}) s.waitRemoved(c, m3) } func (s *ProvisionerSuite) newProvisionerTask(c *gc.C, safeMode bool, broker environs.InstanceBroker) provisioner.ProvisionerTask { machineWatcher, err := s.provisioner.WatchEnvironMachines() c.Assert(err, gc.IsNil) retryWatcher, err := s.provisioner.WatchMachineErrorRetry() c.Assert(err, gc.IsNil) auth, err := environs.NewAPIAuthenticator(s.provisioner) c.Assert(err, gc.IsNil) return provisioner.NewProvisionerTask( "machine-0", safeMode, s.provisioner, machineWatcher, retryWatcher, broker, auth) } func (s *ProvisionerSuite) TestTurningOffSafeModeReapsUnknownInstances(c *gc.C) { task := s.newProvisionerTask(c, true, s.APIConn.Environ) defer stop(c, task) // Initially create a machine, and an unknown instance, with safe mode on. m0, err := s.addMachine() c.Assert(err, gc.IsNil) i0 := s.checkStartInstance(c, m0) i1 := s.startUnknownInstance(c, "999") // mark the first machine as dead c.Assert(m0.EnsureDead(), gc.IsNil) // with safe mode on, only one of the machines is stopped. s.checkStopSomeInstances(c, []instance.Instance{i0}, []instance.Instance{i1}) s.waitRemoved(c, m0) // turn off safe mode and check that the other machine is now stopped also. task.SetSafeMode(false) s.checkStopInstances(c, i1) } func (s *ProvisionerSuite) TestProvisionerRetriesTransientErrors(c *gc.C) { s.PatchValue(&apiserverprovisioner.ErrorRetryWaitDelay, 5*time.Millisecond) var e environs.Environ = &mockBroker{Environ: s.APIConn.Environ, retryCount: make(map[string]int)} task := s.newProvisionerTask(c, false, e) defer stop(c, task) // Provision some machines, some will be started first time, // another will require retries. m1, err := s.addMachine() c.Assert(err, gc.IsNil) m2, err := s.addMachine() c.Assert(err, gc.IsNil) m3, err := s.addMachine() c.Assert(err, gc.IsNil) m4, err := s.addMachine() c.Assert(err, gc.IsNil) s.checkStartInstance(c, m1) s.checkStartInstance(c, m2) thatsAllFolks := make(chan struct{}) go func() { for { select { case <-thatsAllFolks: return case <-time.After(coretesting.ShortWait): err := m3.SetStatus(params.StatusError, "info", params.StatusData{"transient": true}) c.Assert(err, gc.IsNil) } } }() s.checkStartInstance(c, m3) close(thatsAllFolks) // Machine 4 is never provisioned. status, _, _, err := m4.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) _, err = m4.InstanceId() c.Assert(err, jc.Satisfies, state.IsNotProvisionedError) } type mockBroker struct { environs.Environ retryCount map[string]int } func (b *mockBroker) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { // All machines except machines 3, 4 are provisioned successfully the first time. // Machines 3 is provisioned after some attempts have been made. // Machine 4 is never provisioned. id := args.MachineConfig.MachineId retries := b.retryCount[id] if (id != "3" && id != "4") || retries > 2 { return b.Environ.StartInstance(args) } else { b.retryCount[id] = retries + 1 } return nil, nil, fmt.Errorf("error: some error") } func (b *mockBroker) GetToolsSources() ([]simplestreams.DataSource, error) { return b.Environ.(tools.SupportsCustomSources).GetToolsSources() } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/container_initialisation_test.go����0000644�0000153�0000161�00000014042�12321735776�033236� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner_test import ( "fmt" "os/exec" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" apiprovisioner "launchpad.net/juju-core/state/api/provisioner" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker" "launchpad.net/juju-core/worker/provisioner" ) type ContainerSetupSuite struct { CommonProvisionerSuite p provisioner.Provisioner // Record the apt commands issued as part of container initialisation aptCmdChan <-chan *exec.Cmd } var _ = gc.Suite(&ContainerSetupSuite{}) func (s *ContainerSetupSuite) SetUpSuite(c *gc.C) { s.CommonProvisionerSuite.SetUpSuite(c) } func (s *ContainerSetupSuite) TearDownSuite(c *gc.C) { s.CommonProvisionerSuite.TearDownSuite(c) } func allFatal(error) bool { return true } func noImportance(err0, err1 error) bool { return false } func (s *ContainerSetupSuite) SetUpTest(c *gc.C) { s.CommonProvisionerSuite.SetUpTest(c) s.CommonProvisionerSuite.setupEnvironmentManager(c) aptCmdChan := s.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil) s.aptCmdChan = aptCmdChan // Set up provisioner for the state machine. agentConfig := s.AgentConfigForTag(c, "machine-0") s.p = provisioner.NewEnvironProvisioner(s.provisioner, agentConfig) } func (s *ContainerSetupSuite) TearDownTest(c *gc.C) { stop(c, s.p) s.CommonProvisionerSuite.TearDownTest(c) } func (s *ContainerSetupSuite) setupContainerWorker(c *gc.C, tag string) { runner := worker.NewRunner(allFatal, noImportance) pr := s.st.Provisioner() machine, err := pr.Machine(tag) c.Assert(err, gc.IsNil) err = machine.SetSupportedContainers(instance.ContainerTypes...) c.Assert(err, gc.IsNil) cfg := s.AgentConfigForTag(c, tag) watcherName := fmt.Sprintf("%s-container-watcher", machine.Id()) handler := provisioner.NewContainerSetupHandler(runner, watcherName, instance.ContainerTypes, machine, pr, cfg) runner.StartWorker(watcherName, func() (worker.Worker, error) { return worker.NewStringsWorker(handler), nil }) go func() { runner.Wait() }() } func (s *ContainerSetupSuite) createContainer(c *gc.C, host *state.Machine, ctype instance.ContainerType) { inst := s.checkStartInstance(c, host) s.setupContainerWorker(c, host.Tag()) // make a container on the host machine template := state.MachineTemplate{ Series: coretesting.FakeDefaultSeries, Jobs: []state.MachineJob{state.JobHostUnits}, } container, err := s.State.AddMachineInsideMachine(template, host.Id(), ctype) c.Assert(err, gc.IsNil) // the host machine agent should not attempt to create the container s.checkNoOperations(c) // cleanup c.Assert(container.EnsureDead(), gc.IsNil) c.Assert(container.Remove(), gc.IsNil) c.Assert(host.EnsureDead(), gc.IsNil) s.checkStopInstances(c, inst) s.waitRemoved(c, host) } func (s *ContainerSetupSuite) assertContainerProvisionerStarted( c *gc.C, host *state.Machine, ctype instance.ContainerType) { // A stub worker callback to record what happens. provisionerStarted := false startProvisionerWorker := func(runner worker.Runner, containerType instance.ContainerType, pr *apiprovisioner.State, cfg agent.Config, broker environs.InstanceBroker) error { c.Assert(containerType, gc.Equals, ctype) c.Assert(cfg.Tag(), gc.Equals, host.Tag()) provisionerStarted = true return nil } s.PatchValue(&provisioner.StartProvisioner, startProvisionerWorker) s.createContainer(c, host, ctype) // Consume the apt command used to initialise the container. <-s.aptCmdChan // the container worker should have created the provisioner c.Assert(provisionerStarted, jc.IsTrue) } func (s *ContainerSetupSuite) TestContainerProvisionerStarted(c *gc.C) { for _, ctype := range instance.ContainerTypes { // create a machine to host the container. m, err := s.BackingState.AddOneMachine(state.MachineTemplate{ Series: coretesting.FakeDefaultSeries, Jobs: []state.MachineJob{state.JobHostUnits}, Constraints: s.defaultConstraints, }) c.Assert(err, gc.IsNil) err = m.SetSupportedContainers([]instance.ContainerType{instance.LXC, instance.KVM}) c.Assert(err, gc.IsNil) err = m.SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) s.assertContainerProvisionerStarted(c, m, ctype) } } func (s *ContainerSetupSuite) assertContainerInitialised(c *gc.C, ctype instance.ContainerType, packages []string) { // A noop worker callback. startProvisionerWorker := func(runner worker.Runner, containerType instance.ContainerType, pr *apiprovisioner.State, cfg agent.Config, broker environs.InstanceBroker) error { return nil } s.PatchValue(&provisioner.StartProvisioner, startProvisionerWorker) // create a machine to host the container. m, err := s.BackingState.AddOneMachine(state.MachineTemplate{ Series: coretesting.FakeDefaultSeries, Jobs: []state.MachineJob{state.JobHostUnits}, Constraints: s.defaultConstraints, }) c.Assert(err, gc.IsNil) err = m.SetSupportedContainers([]instance.ContainerType{instance.LXC, instance.KVM}) c.Assert(err, gc.IsNil) err = m.SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) s.createContainer(c, m, ctype) cmd := <-s.aptCmdChan c.Assert(cmd.Env[len(cmd.Env)-1], gc.Equals, "DEBIAN_FRONTEND=noninteractive") expected := []string{ "apt-get", "--option=Dpkg::Options::=--force-confold", "--option=Dpkg::options::=--force-unsafe-io", "--assume-yes", "--quiet", "install"} expected = append(expected, packages...) c.Assert(cmd.Args, gc.DeepEquals, expected) } func (s *ContainerSetupSuite) TestContainerInitialised(c *gc.C) { for _, test := range []struct { ctype instance.ContainerType packages []string }{ {instance.LXC, []string{"--target-release", "precise-updates/cloud-tools", "lxc"}}, {instance.KVM, []string{"uvtool-libvirt", "uvtool"}}, } { s.assertContainerInitialised(c, test.ctype, test.packages) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/package_test.go���������������������0000644�0000153�0000161�00000000370�12321735642�027536� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func Test(t *stdtesting.T) { testing.MgoTestPackage(t) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/container_initialisation.go���������0000644�0000153�0000161�00000015542�12321735642�032175� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner import ( "fmt" "sync/atomic" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/kvm" "launchpad.net/juju-core/container/lxc" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" apiprovisioner "launchpad.net/juju-core/state/api/provisioner" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/worker" ) // ContainerSetup is a StringsWatchHandler that is notified when containers // are created on the given machine. It will set up the machine to be able // to create containers and start a suitable provisioner. type ContainerSetup struct { runner worker.Runner supportedContainers []instance.ContainerType provisioner *apiprovisioner.State machine *apiprovisioner.Machine config agent.Config // Save the workerName so the worker thread can be stopped. workerName string // setupDone[containerType] is non zero if the container setup has been invoked // for that container type. setupDone map[instance.ContainerType]*int32 // The number of provisioners started. Once all necessary provisioners have // been started, the container watcher can be stopped. numberProvisioners int32 } // NewContainerSetupHandler returns a StringsWatchHandler which is notified when // containers are created on the given machine. func NewContainerSetupHandler(runner worker.Runner, workerName string, supportedContainers []instance.ContainerType, machine *apiprovisioner.Machine, provisioner *apiprovisioner.State, config agent.Config) worker.StringsWatchHandler { return &ContainerSetup{ runner: runner, machine: machine, supportedContainers: supportedContainers, provisioner: provisioner, config: config, workerName: workerName, } } // SetUp is defined on the StringsWatchHandler interface. func (cs *ContainerSetup) SetUp() (watcher watcher.StringsWatcher, err error) { // Set up the semaphores for each container type. cs.setupDone = make(map[instance.ContainerType]*int32, len(instance.ContainerTypes)) for _, containerType := range instance.ContainerTypes { zero := int32(0) cs.setupDone[containerType] = &zero } // Listen to all container lifecycle events on our machine. if watcher, err = cs.machine.WatchAllContainers(); err != nil { return nil, err } return watcher, nil } // Handle is called whenever containers change on the machine being watched. // All machines start out with so containers so the first time Handle is called, // it will be because a container has been added. func (cs *ContainerSetup) Handle(containerIds []string) (resultError error) { // Consume the initial watcher event. if len(containerIds) == 0 { return nil } logger.Tracef("initial container setup with ids: %v", containerIds) for _, id := range containerIds { containerType := state.ContainerTypeFromId(id) // If this container type has been dealt with, do nothing. if atomic.LoadInt32(cs.setupDone[containerType]) != 0 { continue } if err := cs.initialiseAndStartProvisioner(containerType); err != nil { logger.Errorf("starting container provisioner for %v: %v", containerType, err) // Just because dealing with one type of container fails, we won't exit the entire // function because we still want to try and start other container types. So we // take note of and return the first such error. if resultError == nil { resultError = err } } } return resultError } func (cs *ContainerSetup) initialiseAndStartProvisioner(containerType instance.ContainerType) error { // Flag that this container type has been handled. atomic.StoreInt32(cs.setupDone[containerType], 1) if atomic.AddInt32(&cs.numberProvisioners, 1) == int32(len(cs.supportedContainers)) { // We only care about the initial container creation. // This worker has done its job so stop it. // We do not expect there will be an error, and there's not much we can do anyway. if err := cs.runner.StopWorker(cs.workerName); err != nil { logger.Warningf("stopping machine agent container watcher: %v", err) } } // We only care about the initial container creation. // This worker has done its job so stop it. // We do not expect there will be an error, and there's not much we can do anyway. if err := cs.runner.StopWorker(cs.workerName); err != nil { logger.Warningf("stopping machine agent container watcher: %v", err) } if initialiser, broker, err := cs.getContainerArtifacts(containerType); err != nil { return fmt.Errorf("initialising container infrastructure on host machine: %v", err) } else { if err := initialiser.Initialise(); err != nil { return fmt.Errorf("setting up container dependnecies on host machine: %v", err) } return StartProvisioner(cs.runner, containerType, cs.provisioner, cs.config, broker) } } // TearDown is defined on the StringsWatchHandler interface. func (cs *ContainerSetup) TearDown() error { // Nothing to do here. return nil } func (cs *ContainerSetup) getContainerArtifacts(containerType instance.ContainerType) (container.Initialiser, environs.InstanceBroker, error) { tools, err := cs.provisioner.Tools(cs.config.Tag()) if err != nil { logger.Errorf("cannot get tools from machine for %s container", containerType) return nil, nil, err } var initialiser container.Initialiser var broker environs.InstanceBroker switch containerType { case instance.LXC: series, err := cs.machine.Series() if err != nil { return nil, nil, err } initialiser = lxc.NewContainerInitialiser(series) broker, err = NewLxcBroker(cs.provisioner, tools, cs.config) if err != nil { return nil, nil, err } case instance.KVM: initialiser = kvm.NewContainerInitialiser() broker, err = NewKvmBroker(cs.provisioner, tools, cs.config) if err != nil { logger.Errorf("failed to create new kvm broker") return nil, nil, err } default: return nil, nil, fmt.Errorf("unknown container type: %v", containerType) } return initialiser, broker, nil } // Override for testing. var StartProvisioner = startProvisionerWorker // startProvisionerWorker kicks off a provisioner task responsible for creating containers // of the specified type on the machine. func startProvisionerWorker(runner worker.Runner, containerType instance.ContainerType, provisioner *apiprovisioner.State, config agent.Config, broker environs.InstanceBroker) error { workerName := fmt.Sprintf("%s-provisioner", containerType) // The provisioner task is created after a container record has already been added to the machine. // It will see that the container does not have an instance yet and create one. return runner.StartWorker(workerName, func() (worker.Worker, error) { return NewContainerProvisioner(containerType, provisioner, config, broker), nil }) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/provisioner/kvm-broker.go�����������������������0000644�0000153�0000161�00000006533�12321735776�027202� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner import ( "github.com/juju/loggo" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/kvm" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/tools" ) var kvmLogger = loggo.GetLogger("juju.provisioner.kvm") var _ environs.InstanceBroker = (*kvmBroker)(nil) var _ tools.HasTools = (*kvmBroker)(nil) func NewKvmBroker( api APICalls, tools *tools.Tools, agentConfig agent.Config, ) (environs.InstanceBroker, error) { manager, err := kvm.NewContainerManager(container.ManagerConfig{container.ConfigName: "juju"}) if err != nil { return nil, err } return &kvmBroker{ manager: manager, api: api, tools: tools, agentConfig: agentConfig, }, nil } type kvmBroker struct { manager container.Manager api APICalls tools *tools.Tools agentConfig agent.Config } func (broker *kvmBroker) Tools() tools.List { return tools.List{broker.tools} } // StartInstance is specified in the Broker interface. func (broker *kvmBroker) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { // TODO: refactor common code out of the container brokers. machineId := args.MachineConfig.MachineId kvmLogger.Infof("starting kvm container for machineId: %s", machineId) // TODO: Default to using the host network until we can configure. Yes, // this is using the LxcBridge value, we should put it in the api call for // container config. bridgeDevice := broker.agentConfig.Value(agent.LxcBridge) if bridgeDevice == "" { bridgeDevice = kvm.DefaultKvmBridge } network := container.BridgeNetworkConfig(bridgeDevice) // TODO: series doesn't necessarily need to be the same as the host. series := args.Tools.OneSeries() args.MachineConfig.MachineContainerType = instance.KVM args.MachineConfig.Tools = args.Tools[0] config, err := broker.api.ContainerConfig() if err != nil { kvmLogger.Errorf("failed to get container config: %v", err) return nil, nil, err } if err := environs.PopulateMachineConfig( args.MachineConfig, config.ProviderType, config.AuthorizedKeys, config.SSLHostnameVerification, config.Proxy, config.AptProxy, ); err != nil { kvmLogger.Errorf("failed to populate machine config: %v", err) return nil, nil, err } inst, hardware, err := broker.manager.CreateContainer(args.MachineConfig, series, network) if err != nil { kvmLogger.Errorf("failed to start container: %v", err) return nil, nil, err } kvmLogger.Infof("started kvm container for machineId: %s, %s, %s", machineId, inst.Id(), hardware.String()) return inst, hardware, nil } // StopInstances shuts down the given instances. func (broker *kvmBroker) StopInstances(instances []instance.Instance) error { // TODO: potentially parallelise. for _, instance := range instances { kvmLogger.Infof("stopping kvm container for instance: %s", instance.Id()) if err := broker.manager.DestroyContainer(instance); err != nil { kvmLogger.Errorf("container did not stop: %v", err) return err } } return nil } // AllInstances only returns running containers. func (broker *kvmBroker) AllInstances() (result []instance.Instance, err error) { return broker.manager.ListContainers() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/worker/environ_test.go���������������������������������0000644�0000153�0000161�00000010636�12321735642�025252� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package worker_test import ( "strings" stdtesting "testing" "time" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/tomb" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/worker" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type environSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&environSuite{}) func (s *environSuite) TestStop(c *gc.C) { w := s.State.WatchForEnvironConfigChanges() defer stopWatcher(c, w) stop := make(chan struct{}) done := make(chan error) go func() { env, err := worker.WaitForEnviron(w, s.State, stop) c.Check(env, gc.IsNil) done <- err }() close(stop) c.Assert(<-done, gc.Equals, tomb.ErrDying) } func stopWatcher(c *gc.C, w state.NotifyWatcher) { err := w.Stop() c.Check(err, gc.IsNil) } func (s *environSuite) TestInvalidConfig(c *gc.C) { var oldType string oldType = s.Conn.Environ.Config().AllAttrs()["type"].(string) // Create an invalid config by taking the current config and // tweaking the provider type. info := s.StateInfo(c) opts := state.DefaultDialOpts() st2, err := state.Open(info, opts, state.Policy(nil)) c.Assert(err, gc.IsNil) defer st2.Close() err = st2.UpdateEnvironConfig(map[string]interface{}{"type": "unknown"}, nil, nil) c.Assert(err, gc.IsNil) w := st2.WatchForEnvironConfigChanges() defer stopWatcher(c, w) done := make(chan environs.Environ) go func() { env, err := worker.WaitForEnviron(w, st2, nil) c.Check(err, gc.IsNil) done <- env }() // Wait for the loop to process the invalid configuratrion <-worker.LoadedInvalid st2.UpdateEnvironConfig(map[string]interface{}{ "type": oldType, "secret": "environ_test", }, nil, nil) env := <-done c.Assert(env, gc.NotNil) c.Assert(env.Config().AllAttrs()["secret"], gc.Equals, "environ_test") } func (s *environSuite) TestErrorWhenEnvironIsInvalid(c *gc.C) { // reopen the state so that we can wangle a dodgy environ config in there. st, err := state.Open(s.StateInfo(c), state.DefaultDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st.Close() err = st.UpdateEnvironConfig(map[string]interface{}{"secret": 999}, nil, nil) c.Assert(err, gc.IsNil) obs, err := worker.NewEnvironObserver(s.State) c.Assert(err, gc.ErrorMatches, `cannot make Environ: secret: expected string, got int\(999\)`) c.Assert(obs, gc.IsNil) } func (s *environSuite) TestEnvironmentChanges(c *gc.C) { originalConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) logc := make(logChan, 1009) c.Assert(loggo.RegisterWriter("testing", logc, loggo.WARNING), gc.IsNil) defer loggo.RemoveWriter("testing") obs, err := worker.NewEnvironObserver(s.State) c.Assert(err, gc.IsNil) env := obs.Environ() c.Assert(env.Config().AllAttrs(), gc.DeepEquals, originalConfig.AllAttrs()) var oldType string oldType = env.Config().AllAttrs()["type"].(string) info := s.StateInfo(c) opts := state.DefaultDialOpts() st2, err := state.Open(info, opts, state.Policy(nil)) defer st2.Close() // Change to an invalid configuration and check // that the observer's environment remains the same. st2.UpdateEnvironConfig(map[string]interface{}{"type": "invalid"}, nil, nil) st2.StartSync() // Wait for the observer to register the invalid environment timeout := time.After(coretesting.LongWait) loop: for { select { case msg := <-logc: if strings.Contains(msg, "error creating Environ") { break loop } case <-timeout: c.Fatalf("timed out waiting to see broken environment") } } // Check that the returned environ is still the same. env = obs.Environ() c.Assert(env.Config().AllAttrs(), gc.DeepEquals, originalConfig.AllAttrs()) // Change the environment back to a valid configuration // with a different name and check that we see it. st2.UpdateEnvironConfig(map[string]interface{}{"type": oldType, "name": "a-new-name"}, nil, nil) st2.StartSync() for a := coretesting.LongAttempt.Start(); a.Next(); { env := obs.Environ() if !a.HasNext() { c.Fatalf("timed out waiting for new environ") } if env.Config().Name() == "a-new-name" { break } } } type logChan chan string func (logc logChan) Write(level loggo.Level, name, filename string, line int, timestamp time.Time, message string) { logc <- message } ��������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/.lbox.check��������������������������������������������0000755�0000153�0000161�00000001074�12321735642�022713� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash set -e BADFMT=`find * -name '*.go' -not -name '.#*' | xargs gofmt -l` if [ -n "$BADFMT" ]; then BADFMT=`echo "$BADFMT" | sed "s/^/ /"` echo -e "gofmt is sad:\n\n$BADFMT" exit 1 fi VERSION=`go version | awk '{print $3}'` go tool vet \ -methods \ -printf \ -rangeloops \ -printfuncs 'ErrorContextf:1,notFoundf:0,badReqErrorf:0,Commitf:0,Snapshotf:0,Debugf:0,Infof:0,Warningf:0,Errorf:0,Criticalf:0,Tracef:0' \ . # check this branch builds cleanly go build launchpad.net/juju-core/... # check that all tests are wired up ./scripts/checktesting.bash ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/.bzrignore���������������������������������������������0000644�0000153�0000161�00000000224�12321735642�022665� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������cmd/juju/juju cmd/jujud/jujud cmd/builddb/builddb cmd/charmd/charmd cmd/charmload/charmload ./tags ./TAGS .emacs.desktop .emacs.desktop.lock *.test ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/CONTRIBUTING�������������������������������������������0000644�0000153�0000161�00000021204�12321735642�022516� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Getting started =============== Before contributing to `juju-core` please read the following sections describing the tools and conventions of this project. This file is a companion to README and it is assumed that file has been read prior. cobzr ----- juju-core uses bzr for source control. To make the bzr branching strategy more palatable for the go toolset `juju-core` recommends using the `cobzr` helper. `cobzr` allows the user to juggle multiple branches in place in a way that is compatible with GOPATH. While it is possible to manage bzr branches manually, the remainder of this document will assume cobzr is in use as practiced by the juju-core team. To install `cobzr`, go get launchpad.net/cobzr TODO(dfc) document PPA version It is recommended to use alias `cobzr` to `bzr`. alias bzr=cobzr As `cobzr` passes any options it does not handle to `bzr`, it is safe, and recommended to add this alias command to your login shell. lbox ---- `juju-core` uses `lbox` for code review and submission. In addition `lbox` enables the use of prerequisite branches. To install lbox go get launchpad.net/lbox TODO(dfc) document PPA version Branching ========= All changes to `juju-core` must be performed on a branch, that branch reviewed, then submitted. An overview of the commands used to do so follows. These examples use the `bzr` command, which is assumed to be aliased to `cobzr`, as described above. It is also assumed that your working directory is $GOPATH/src/launchpad.net/juju-core. First, create a branch for your change using the following command bzr branch lp:juju-core/ add-CONTRIBUTING This will branch `juju-core` and create a new branch called `add-CONTRIBUTING` in your local workspace. Importantly this will not switch to this branch immediately, so to switch to this branch use the following bzr switch add-CONTRIBUTING At this point your previous branch will be stashed and the working copy updated to match the state of the `add-CONTRIBUTING` branch. You must ensure any outstanding changes to the previous branch are committed or reverted to avoid local merge issues. You can also list any branches you are currently working on by bzr branch Imports ------- Import statements are grouped into 3 sections: standard library, 3rd party libraries, juju-core imports. The tool "go fmt" can be used to ensure each group is alphabetically sorted. eg: import ( "fmt" "time" "labix.org/v2/mgo" gc "launchpad.net/gocheck" "github.com/juju/loggo" "launchpad.net/juju-core/state" "launchpad.net/juju-core/worker" ) Because "launchpad.net/gocheck" will be referenced frequently in test suites, its name gets a default short name of just "gc". Testing ======= `juju-core` uses the `gocheck` testing framework. `gocheck` is automatically installed as a dependency of `juju-core`. You can read more about `gocheck` at http://go.pkgdoc.org/pkg/launchpad.net/gocheck. `gocheck` is integrated into the source of each package so the standard `go test` command is used to run `gocheck` tests. For example go test launchpad.net/juju-core/... will run all the tests in the `juju-core` project. By default `gocheck` prints only minimal output, and as `gocheck` is hooked into the testing framework via a single `go test` test per package, the usual `go test -v` flags are less useful. As a replacement the following commands produce more output from `gocheck`. go test -gocheck.v is similar to `go test -v` and outputs the name of each test as it is run as well as any logging statements. It is important to note that these statements are buffered until the test completes. go test -gocheck.vv extends the previous example by outputting any logging data immediately, rather than waiting for the test to complete. By default `gocheck` will run all tests in a package, selected tests can by run by passing `-gocheck.f` go test -gocheck.f '$REGEX' to match a subset of test names. Finally, because by default `go test` runs the tests in the current package, and is not recursive, the following commands are equal, and will produce no output. cd $GOPATH/src/launchpad.net/juju-core go test 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 ========= All code review is done on rietveld (http://code.google.com/p/rietveld/), not on launchpad. Note: If this is your first time using `lbox` you will also be prompted to visit lauchpad to authorise an oauth token for `lbox` Once your change is ready, and you have successfully run all the tests you can propose your branch for merging lbox propose `lbox` will prompt you for a branch description and will create a rietveld code review for this change, linking it to the launchpad branch, and mailing reviewers. The `lbox` tool manages the state of the launchpad review, with the exception of marking reviews as rejected or work in progress by the reviewer. If you abandon a proposal, you should mark it as rejected in launchpad to avoid wasting the time of others. If you decide to start again, you should add a comment to the abandoned rietveld proposal linking it to your replacement. If your proposal requires additional work, then you can use the `lbox propose` command again to re-propose, this will also instruct rietveld to mail off any comments to your reviewers and upload fresh diff. If your branch requires another branch as a prerequisite use the `-req` flag to indicate so lbox propose -req lp:dave-cheney/add-README This will produce a diff in rietveld which masks the changes from your prereq. It is sometimes useful to be able to review your branch in rietveld without proposing, do to this the `-wip` flag is used lbox propose -wip You can also edit the description of your change with `-edit`. This is useful when combined with the `-wip` flag to avoid sending too many mails to reviewers while preparing your proposal. lbox propose -edit By default, lbox creates a new bug in launchpad for each proposed branch, and assigns the branch to it. If you wish to assign it to an existing bug instead, use the `-bug` flag to link the branch inside launchpad. lbox propose -bug 1234567 There is currently a bug with `lbox` when linking a branch to a bug which sets the milestone field to the last milestone defined on the project. Generally this is not what you want, so you should visit the bug's page and correct the milestone, if set. Code review =========== `juju-core` operates on a two positive, no negative review process. You may not submit your proposal until it has received two LGTM comments. If any NOT LGTM comments are received, those comments should be resolved to the satisfaction of the objecting reviewer before submitting. Once your have received at least two positive reviews, you can submit your branch by going to the launchpad merge proposal page and: - copy and paste the merge proposal description into the commit message. - mark the proposal as Approved. The merge proposal will then be tested and merged into trunk assuming all tests pass cleanly. lbox hooks ---------- Before proposing, `lbox` runs a number of hooks to improve code quality and ensure that code is properly formatted. These checks are in `$GOPATH/src/launchpad.net/juju-core/.lbox.check`. They are run automatically by `lbox` before proposing or submitting. If these hooks fail you will have to resolve those errors before trying again. For example % lbox propose gofmt is sad: version/version.go Dependency management ===================== In the top-level directory, there is a file, dependencies.tsv, that holds the revision ids of all the external projects that juju-core depends on. The tab-separated columns in the file are the project name, the type version control system used by that project, and the revision id and number respectively. This file is generated by running the godeps command (which you can get with `go get launchpad.net/godeps') on a juju-core installation with all freshly downloaded directories. The bash commands used to generate it from scratch are as follows: % export GOPATH=/tmp/juju-build % go get launchpad.net/juju-core/... % go test launchpad.net/juju-core/... % godeps -t $(go list launchpad.net/juju-core/...) > dependencies.tsv ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/���������������������������������������������0000755�0000153�0000161�00000000000�12321735643�022650� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/userdata.go����������������������������������0000644�0000153�0000161�00000003201�12321735642�025002� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package container import ( "io/ioutil" "path/filepath" "github.com/juju/loggo" coreCloudinit "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/environs/cloudinit" ) var ( logger = loggo.GetLogger("juju.container") ) // WriteUserData generates the cloud init for the specified machine config, // and writes the serialized form out to a cloud-init file in the directory // specified. func WriteUserData(machineConfig *cloudinit.MachineConfig, directory string) (string, error) { userData, err := cloudInitUserData(machineConfig) if err != nil { logger.Errorf("failed to create user data: %v", err) return "", err } return WriteCloudInitFile(directory, userData) } // WriteCloudInitFile writes the data out to a cloud-init file in the // directory specified, and returns the filename. func WriteCloudInitFile(directory string, userData []byte) (string, error) { userDataFilename := filepath.Join(directory, "cloud-init") if err := ioutil.WriteFile(userDataFilename, userData, 0644); err != nil { logger.Errorf("failed to write user data: %v", err) return "", err } return userDataFilename, nil } func cloudInitUserData(machineConfig *cloudinit.MachineConfig) ([]byte, error) { cloudConfig := coreCloudinit.New() err := cloudinit.Configure(machineConfig, cloudConfig) if err != nil { return nil, err } // Run ifconfig to get the addresses of the internal container at least // logged in the host. cloudConfig.AddRunCmd("ifconfig") data, err := cloudConfig.Render() if err != nil { return nil, err } return data, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/interface.go���������������������������������0000644�0000153�0000161�00000003610�12321735642�025136� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package container import ( "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/instance" ) const ( ConfigName = "name" ConfigLogDir = "log-dir" ) // ManagerConfig contains the initialization parameters for the ContainerManager. // The name of the manager is used to namespace the containers on the machine. type ManagerConfig map[string]string // Manager is responsible for starting containers, and stopping and listing // containers that it has started. type Manager interface { // CreateContainer creates and starts a new container for the specified machine. CreateContainer( machineConfig *cloudinit.MachineConfig, series string, network *NetworkConfig) (instance.Instance, *instance.HardwareCharacteristics, error) // DestroyContainer stops and destroyes the container identified by Instance. DestroyContainer(instance.Instance) error // ListContainers return a list of containers that have been started by // this manager. ListContainers() ([]instance.Instance, error) } // Initialiser is responsible for performing the steps required to initialise // a host machine so it can run containers. type Initialiser interface { // Initialise installs all required packages, sync any images etc so // that the host machine can run containers. Initialise() error } // PopValue returns the requested key from the config map. If the value // doesn't exist, the function returns the empty string. If the value does // exist, the value is returned, and the element removed from the map. func (m ManagerConfig) PopValue(key string) string { value := m[key] delete(m, key) return value } // WarnAboutUnused emits a warning about each value in the map. func (m ManagerConfig) WarnAboutUnused() { for key, value := range m { logger.Warningf("unused config option: %q -> %q", key, value) } } ������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/directory.go���������������������������������0000644�0000153�0000161�00000003133�12321735642�025202� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package container import ( "os" "path/filepath" "launchpad.net/juju-core/utils" ) var ( ContainerDir = "/var/lib/juju/containers" RemovedContainerDir = "/var/lib/juju/removed-containers" ) // NewDirectory creates a new directory for the container name in the // directory identified by `ContainerDir`. func NewDirectory(containerName string) (directory string, err error) { directory = dirForName(containerName) logger.Tracef("create directory: %s", directory) if err = os.MkdirAll(directory, 0755); err != nil { logger.Errorf("failed to create container directory: %v", err) return "", err } return directory, nil } // RemoveDirectory moves the container directory from `ContainerDir` // to `RemovedContainerDir` and makes sure that the names don't clash. func RemoveDirectory(containerName string) error { // Move the directory. logger.Tracef("create old container dir: %s", RemovedContainerDir) if err := os.MkdirAll(RemovedContainerDir, 0755); err != nil { logger.Errorf("failed to create removed container directory: %v", err) return err } removedDir, err := utils.UniqueDirectory(RemovedContainerDir, containerName) if err != nil { logger.Errorf("was not able to generate a unique directory: %v", err) return err } if err := os.Rename(dirForName(containerName), removedDir); err != nil { logger.Errorf("failed to rename container directory: %v", err) return err } return nil } func dirForName(containerName string) string { return filepath.Join(ContainerDir, containerName) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/directory_test.go����������������������������0000644�0000153�0000161�00000003034�12321735642�026241� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package container_test import ( "os" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/container" "launchpad.net/juju-core/testing/testbase" ) type DirectorySuite struct { testbase.LoggingSuite containerDir string removedDir string } var _ = gc.Suite(&DirectorySuite{}) func (s *DirectorySuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.containerDir = c.MkDir() s.PatchValue(&container.ContainerDir, s.containerDir) s.removedDir = c.MkDir() s.PatchValue(&container.RemovedContainerDir, s.removedDir) } func (*DirectorySuite) TestNewContainerDir(c *gc.C) { dir, err := container.NewDirectory("testing") c.Assert(err, gc.IsNil) c.Assert(dir, jc.IsDirectory) } func (s *DirectorySuite) TestRemoveContainerDir(c *gc.C) { dir, err := container.NewDirectory("testing") c.Assert(err, gc.IsNil) err = container.RemoveDirectory("testing") c.Assert(err, gc.IsNil) c.Assert(dir, jc.DoesNotExist) c.Assert(filepath.Join(s.removedDir, "testing"), jc.IsDirectory) } func (s *DirectorySuite) TestRemoveContainerDirWithClash(c *gc.C) { dir, err := container.NewDirectory("testing") c.Assert(err, gc.IsNil) clash := filepath.Join(s.removedDir, "testing") err = os.MkdirAll(clash, 0755) c.Assert(err, gc.IsNil) err = container.RemoveDirectory("testing") c.Assert(err, gc.IsNil) c.Assert(dir, jc.DoesNotExist) c.Assert(filepath.Join(s.removedDir, "testing.1"), jc.IsDirectory) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/�����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023422� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/export_test.go���������������������������0000644�0000153�0000161�00000000544�12321735642�026347� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package lxc var ( ContainerConfigFilename = containerConfigFilename ContainerDirFilesystem = containerDirFilesystem GenerateNetworkConfig = generateNetworkConfig NetworkConfigTemplate = networkConfigTemplate RestartSymlink = restartSymlink ) ������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/instance.go������������������������������0000644�0000153�0000161�00000003323�12321735642�025571� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package lxc import ( "fmt" "launchpad.net/golxc" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" ) type lxcInstance struct { golxc.Container id string } var _ instance.Instance = (*lxcInstance)(nil) // Id implements instance.Instance.Id. func (lxc *lxcInstance) Id() instance.Id { return instance.Id(lxc.id) } // Status implements instance.Instance.Status. func (lxc *lxcInstance) Status() string { // On error, the state will be "unknown". state, _, _ := lxc.Info() return string(state) } func (*lxcInstance) Refresh() error { return nil } func (lxc *lxcInstance) Addresses() ([]instance.Address, error) { return nil, errors.NewNotImplementedError("lxcInstance.Addresses") } // DNSName implements instance.Instance.DNSName. func (lxc *lxcInstance) DNSName() (string, error) { return "", instance.ErrNoDNSName } // WaitDNSName implements instance.Instance.WaitDNSName. func (lxc *lxcInstance) WaitDNSName() (string, error) { return "", instance.ErrNoDNSName } // OpenPorts implements instance.Instance.OpenPorts. func (lxc *lxcInstance) OpenPorts(machineId string, ports []instance.Port) error { return fmt.Errorf("not implemented") } // ClosePorts implements instance.Instance.ClosePorts. func (lxc *lxcInstance) ClosePorts(machineId string, ports []instance.Port) error { return fmt.Errorf("not implemented") } // Ports implements instance.Instance.Ports. func (lxc *lxcInstance) Ports(machineId string) ([]instance.Port, error) { return nil, fmt.Errorf("not implemented") } // Add a string representation of the id. func (lxc *lxcInstance) String() string { return fmt.Sprintf("lxc:%s", lxc.id) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/lxc.go�����������������������������������0000644�0000153�0000161�00000027242�12321735642�024561� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package lxc import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strconv" "strings" "time" "github.com/juju/loggo" "launchpad.net/golxc" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/container" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.container.lxc") var ( defaultTemplate = "ubuntu-cloud" LxcContainerDir = "/var/lib/lxc" LxcRestartDir = "/etc/lxc/auto" LxcObjectFactory = golxc.Factory() ) const ( // DefaultLxcBridge is the package created container bridge DefaultLxcBridge = "lxcbr0" // Btrfs is special as we treat it differently for create and clone. Btrfs = "btrfs" ) // DefaultNetworkConfig returns a valid NetworkConfig to use the // defaultLxcBridge that is created by the lxc package. func DefaultNetworkConfig() *container.NetworkConfig { return container.BridgeNetworkConfig(DefaultLxcBridge) } // FsCommandOutput calls cmd.Output, this is used as an overloading point so // we can test what *would* be run without actually executing another program var FsCommandOutput = (*exec.Cmd).CombinedOutput func containerDirFilesystem() (string, error) { cmd := exec.Command("df", "--output=fstype", LxcContainerDir) out, err := FsCommandOutput(cmd) if err != nil { return "", err } // The filesystem is the second line. lines := strings.Split(string(out), "\n") if len(lines) < 2 { logger.Errorf("unexpected output: %q", out) return "", fmt.Errorf("could not determine filesystem type") } return lines[1], nil } type containerManager struct { name string logdir string createWithClone bool useAUFS bool backingFilesystem string } // containerManager implements container.Manager. var _ container.Manager = (*containerManager)(nil) // NewContainerManager returns a manager object that can start and stop lxc // containers. The containers that are created are namespaced by the name // parameter. func NewContainerManager(conf container.ManagerConfig) (container.Manager, error) { name := conf.PopValue(container.ConfigName) if name == "" { return nil, fmt.Errorf("name is required") } logDir := conf.PopValue(container.ConfigLogDir) if logDir == "" { logDir = agent.DefaultLogDir } // Explicitly ignore the error result from ParseBool. // If it fails to parse, the value is false, and this suits // us fine. useClone, _ := strconv.ParseBool(conf.PopValue("use-clone")) useAUFS, _ := strconv.ParseBool(conf.PopValue("use-aufs")) backingFS, err := containerDirFilesystem() if err != nil { // Especially in tests, or a bot, the lxc dir may not exist // causing the test to fail. Since we only really care if the // backingFS is 'btrfs' and we treat the rest the same, just // call it 'unknown'. backingFS = "unknown" } logger.Tracef("backing filesystem: %q", backingFS) conf.WarnAboutUnused() return &containerManager{ name: name, logdir: logDir, createWithClone: useClone, useAUFS: useAUFS, backingFilesystem: backingFS, }, nil } func (manager *containerManager) CreateContainer( machineConfig *cloudinit.MachineConfig, series string, network *container.NetworkConfig, ) (instance.Instance, *instance.HardwareCharacteristics, error) { start := time.Now() name := names.MachineTag(machineConfig.MachineId) if manager.name != "" { name = fmt.Sprintf("%s-%s", manager.name, name) } // Create the cloud-init. directory, err := container.NewDirectory(name) if err != nil { return nil, nil, err } logger.Tracef("write cloud-init") if manager.createWithClone { // If we are using clone, disable the apt-get steps machineConfig.DisablePackageCommands = true } userDataFilename, err := container.WriteUserData(machineConfig, directory) if err != nil { logger.Errorf("failed to write user data: %v", err) return nil, nil, err } logger.Tracef("write the lxc.conf file") configFile, err := writeLxcConfig(network, directory) if err != nil { logger.Errorf("failed to write config file: %v", err) return nil, nil, err } var lxcContainer golxc.Container if manager.createWithClone { templateContainer, err := EnsureCloneTemplate( manager.backingFilesystem, series, network, machineConfig.AuthorizedKeys, machineConfig.AptProxySettings, ) if err != nil { return nil, nil, err } templateParams := []string{ "--debug", // Debug errors in the cloud image "--userdata", userDataFilename, // Our groovey cloud-init "--hostid", name, // Use the container name as the hostid } var extraCloneArgs []string if manager.backingFilesystem == Btrfs || manager.useAUFS { extraCloneArgs = append(extraCloneArgs, "--snapshot") } if manager.backingFilesystem != Btrfs && manager.useAUFS { extraCloneArgs = append(extraCloneArgs, "--backingstore", "aufs") } lock, err := AcquireTemplateLock(templateContainer.Name(), "clone") if err != nil { return nil, nil, fmt.Errorf("failed to acquire lock on template: %v", err) } defer lock.Unlock() lxcContainer, err = templateContainer.Clone(name, extraCloneArgs, templateParams) if err != nil { logger.Errorf("lxc container cloning failed: %v", err) return nil, nil, err } } else { // Note here that the lxcObjectFacotry only returns a valid container // object, and doesn't actually construct the underlying lxc container on // disk. lxcContainer = LxcObjectFactory.New(name) templateParams := []string{ "--debug", // Debug errors in the cloud image "--userdata", userDataFilename, // Our groovey cloud-init "--hostid", name, // Use the container name as the hostid "-r", series, } // Create the container. logger.Tracef("create the container") if err := lxcContainer.Create(configFile, defaultTemplate, nil, templateParams); err != nil { logger.Errorf("lxc container creation failed: %v", err) return nil, nil, err } logger.Tracef("lxc container created") } if err := autostartContainer(name); err != nil { return nil, nil, err } if err := mountHostLogDir(name, manager.logdir); err != nil { return nil, nil, err } // Start the lxc container with the appropriate settings for grabbing the // console output and a log file. consoleFile := filepath.Join(directory, "console.log") lxcContainer.SetLogFile(filepath.Join(directory, "container.log"), golxc.LogDebug) logger.Tracef("start the container") // We explicitly don't pass through the config file to the container.Start // method as we have passed it through at container creation time. This // is necessary to get the appropriate rootfs reference without explicitly // setting it ourselves. if err = lxcContainer.Start("", consoleFile); err != nil { logger.Errorf("container failed to start: %v", err) return nil, nil, err } arch := version.Current.Arch hardware := &instance.HardwareCharacteristics{ Arch: &arch, } logger.Tracef("container %q started: %v", name, time.Now().Sub(start)) return &lxcInstance{lxcContainer, name}, hardware, nil } func appendToContainerConfig(name, line string) error { file, err := os.OpenFile( containerConfigFilename(name), os.O_RDWR|os.O_APPEND, 0644) if err != nil { return err } defer file.Close() _, err = file.WriteString(line) return err } func autostartContainer(name string) error { // Now symlink the config file into the restart directory, if it exists. // This is for backwards compatiblity. From Trusty onwards, the auto start // option should be set in the LXC config file, this is done in the networkConfigTemplate // function below. if useRestartDir() { if err := os.Symlink( containerConfigFilename(name), restartSymlink(name), ); err != nil { return err } logger.Tracef("auto-restart link created") } else { logger.Tracef("Setting auto start to true in lxc config.") return appendToContainerConfig(name, "lxc.start.auto = 1\n") } return nil } func mountHostLogDir(name, logDir string) error { // Make sure that the mount dir has been created. logger.Tracef("make the mount dir for the shared logs") if err := os.MkdirAll(internalLogDir(name), 0755); err != nil { logger.Errorf("failed to create internal /var/log/juju mount dir: %v", err) return err } line := fmt.Sprintf( "lxc.mount.entry=%s var/log/juju none defaults,bind 0 0\n", logDir) return appendToContainerConfig(name, line) } func (manager *containerManager) DestroyContainer(instance instance.Instance) error { start := time.Now() name := string(instance.Id()) lxcContainer := LxcObjectFactory.New(name) if useRestartDir() { // Remove the autostart link. if err := os.Remove(restartSymlink(name)); err != nil { logger.Errorf("failed to remove restart symlink: %v", err) return err } } if err := lxcContainer.Destroy(); err != nil { logger.Errorf("failed to destroy lxc container: %v", err) return err } err := container.RemoveDirectory(name) logger.Tracef("container %q stopped: %v", name, time.Now().Sub(start)) return err } func (manager *containerManager) ListContainers() (result []instance.Instance, err error) { containers, err := LxcObjectFactory.List() if err != nil { logger.Errorf("failed getting all instances: %v", err) return } managerPrefix := "" if manager.name != "" { managerPrefix = fmt.Sprintf("%s-", manager.name) } for _, container := range containers { // Filter out those not starting with our name. name := container.Name() if !strings.HasPrefix(name, managerPrefix) { continue } if container.IsRunning() { result = append(result, &lxcInstance{container, name}) } } return } const internalLogDirTemplate = "%s/%s/rootfs/var/log/juju" func internalLogDir(containerName string) string { return fmt.Sprintf(internalLogDirTemplate, LxcContainerDir, containerName) } func restartSymlink(name string) string { return filepath.Join(LxcRestartDir, name+".conf") } func containerConfigFilename(name string) string { return filepath.Join(LxcContainerDir, name, "config") } const networkTemplate = ` lxc.network.type = %s lxc.network.link = %s lxc.network.flags = up ` func networkConfigTemplate(networkType, networkLink string) string { return fmt.Sprintf(networkTemplate, networkType, networkLink) } func generateNetworkConfig(network *container.NetworkConfig) string { var lxcConfig string if network == nil { logger.Warningf("network unspecified, using default networking config") network = DefaultNetworkConfig() } switch network.NetworkType { case container.PhysicalNetwork: lxcConfig = networkConfigTemplate("phys", network.Device) default: logger.Warningf("Unknown network config type %q: using bridge", network.NetworkType) fallthrough case container.BridgeNetwork: lxcConfig = networkConfigTemplate("veth", network.Device) } return lxcConfig } func writeLxcConfig(network *container.NetworkConfig, directory string) (string, error) { networkConfig := generateNetworkConfig(network) configFilename := filepath.Join(directory, "lxc.conf") if err := ioutil.WriteFile(configFilename, []byte(networkConfig), 0644); err != nil { return "", err } return configFilename, nil } // useRestartDir is used to determine whether or not to use a symlink to the // container config as the restart mechanism. Older versions of LXC had the // /etc/lxc/auto directory that would indicate that a container shoud auto- // restart when the machine boots by having a symlink to the lxc.conf file. // Newer versions don't do this, but instead have a config value inside the // lxc.conf file. func useRestartDir() bool { if _, err := os.Stat(LxcRestartDir); err != nil { if os.IsNotExist(err) { return false } } return true } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/lxc_test.go������������������������������0000644�0000153�0000161�00000033176�12321735642�025623� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package lxc_test import ( "fmt" "io/ioutil" "os" "path/filepath" "strings" stdtesting "testing" "time" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/golxc" "launchpad.net/goyaml" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/lxc" "launchpad.net/juju-core/container/lxc/mock" lxctesting "launchpad.net/juju-core/container/lxc/testing" containertesting "launchpad.net/juju-core/container/testing" instancetest "launchpad.net/juju-core/instance/testing" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/testing/testbase" ) func Test(t *stdtesting.T) { gc.TestingT(t) } type LxcSuite struct { lxctesting.TestSuite events chan mock.Event useClone bool useAUFS bool } var _ = gc.Suite(&LxcSuite{}) func (s *LxcSuite) SetUpTest(c *gc.C) { s.TestSuite.SetUpTest(c) loggo.GetLogger("juju.container.lxc").SetLogLevel(loggo.TRACE) s.events = make(chan mock.Event, 25) s.TestSuite.Factory.AddListener(s.events) s.PatchValue(&lxc.TemplateLockDir, c.MkDir()) s.PatchValue(&lxc.TemplateStopTimeout, 500*time.Millisecond) } func (s *LxcSuite) TearDownTest(c *gc.C) { s.TestSuite.Factory.RemoveListener(s.events) close(s.events) s.TestSuite.TearDownTest(c) } func (s *LxcSuite) TestContainerDirFilesystem(c *gc.C) { for i, test := range []struct { message string output string expected string errorMatch string }{{ message: "btrfs", output: "Type\nbtrfs\n", expected: lxc.Btrfs, }, { message: "ext4", output: "Type\next4\n", expected: "ext4", }, { message: "not enough output", output: "foo", errorMatch: "could not determine filesystem type", }} { c.Logf("%v: %s", i, test.message) s.HookCommandOutput(&lxc.FsCommandOutput, []byte(test.output), nil) value, err := lxc.ContainerDirFilesystem() if test.errorMatch == "" { c.Check(err, gc.IsNil) c.Check(value, gc.Equals, test.expected) } else { c.Check(err, gc.ErrorMatches, test.errorMatch) } } } func (s *LxcSuite) makeManager(c *gc.C, name string) container.Manager { params := container.ManagerConfig{ container.ConfigName: name, } if s.useClone { params["use-clone"] = "true" } if s.useAUFS { params["use-aufs"] = "true" } manager, err := lxc.NewContainerManager(params) c.Assert(err, gc.IsNil) return manager } func (*LxcSuite) TestManagerWarnsAboutUnknownOption(c *gc.C) { _, err := lxc.NewContainerManager(container.ManagerConfig{ container.ConfigName: "BillyBatson", "shazam": "Captain Marvel", }) c.Assert(err, gc.IsNil) c.Assert(c.GetTestLog(), jc.Contains, `WARNING juju.container unused config option: "shazam" -> "Captain Marvel"`) } func (s *LxcSuite) TestCreateContainer(c *gc.C) { manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1/lxc/0") name := string(instance.Id()) // Check our container config files. lxcConfContents, err := ioutil.ReadFile(filepath.Join(s.ContainerDir, name, "lxc.conf")) c.Assert(err, gc.IsNil) c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.link = nic42") cloudInitFilename := filepath.Join(s.ContainerDir, name, "cloud-init") data := containertesting.AssertCloudInit(c, cloudInitFilename) x := make(map[interface{}]interface{}) err = goyaml.Unmarshal(data, &x) c.Assert(err, gc.IsNil) var scripts []string for _, s := range x["runcmd"].([]interface{}) { scripts = append(scripts, s.(string)) } c.Assert(scripts[len(scripts)-2:], gc.DeepEquals, []string{ "start jujud-machine-1-lxc-0", "ifconfig", }) // Check the mount point has been created inside the container. c.Assert(filepath.Join(s.LxcDir, name, "rootfs", agent.DefaultLogDir), jc.IsDirectory) // Check that the config file is linked in the restart dir. expectedLinkLocation := filepath.Join(s.RestartDir, name+".conf") expectedTarget := filepath.Join(s.LxcDir, name, "config") linkInfo, err := os.Lstat(expectedLinkLocation) c.Assert(err, gc.IsNil) c.Assert(linkInfo.Mode()&os.ModeSymlink, gc.Equals, os.ModeSymlink) location, err := os.Readlink(expectedLinkLocation) c.Assert(err, gc.IsNil) c.Assert(location, gc.Equals, expectedTarget) } func (s *LxcSuite) ensureTemplateStopped(name string) { go func() { for { template := s.Factory.New(name) if template.IsRunning() { template.Stop() } time.Sleep(50 * time.Millisecond) } }() } func (s *LxcSuite) AssertEvent(c *gc.C, event mock.Event, expected mock.Action, id string) { c.Assert(event.Action, gc.Equals, expected) c.Assert(event.InstanceId, gc.Equals, id) } func (s *LxcSuite) TestCreateContainerEvents(c *gc.C) { manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1") id := string(instance.Id()) s.AssertEvent(c, <-s.events, mock.Created, id) s.AssertEvent(c, <-s.events, mock.Started, id) } func (s *LxcSuite) TestCreateContainerEventsWithClone(c *gc.C) { s.PatchValue(&s.useClone, true) // The template containers are created with an upstart job that // stops them once cloud init has finished. We emulate that here. template := "juju-series-template" s.ensureTemplateStopped(template) manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1") id := string(instance.Id()) s.AssertEvent(c, <-s.events, mock.Created, template) s.AssertEvent(c, <-s.events, mock.Started, template) s.AssertEvent(c, <-s.events, mock.Stopped, template) s.AssertEvent(c, <-s.events, mock.Cloned, template) s.AssertEvent(c, <-s.events, mock.Started, id) } func (s *LxcSuite) createTemplate(c *gc.C) golxc.Container { name := "juju-series-template" s.ensureTemplateStopped(name) network := container.BridgeNetworkConfig("nic42") authorizedKeys := "authorized keys list" aptProxy := osenv.ProxySettings{} template, err := lxc.EnsureCloneTemplate( "ext4", "series", network, authorizedKeys, aptProxy) c.Assert(err, gc.IsNil) c.Assert(template.Name(), gc.Equals, name) s.AssertEvent(c, <-s.events, mock.Created, name) s.AssertEvent(c, <-s.events, mock.Started, name) s.AssertEvent(c, <-s.events, mock.Stopped, name) autostartLink := lxc.RestartSymlink(name) config, err := ioutil.ReadFile(lxc.ContainerConfigFilename(name)) c.Assert(err, gc.IsNil) expected := ` lxc.network.type = veth lxc.network.link = nic42 lxc.network.flags = up ` // NOTE: no autostart, no mounting the log dir c.Assert(string(config), gc.Equals, expected) c.Assert(autostartLink, jc.DoesNotExist) return template } func (s *LxcSuite) TestCreateContainerEventsWithCloneExistingTemplate(c *gc.C) { s.createTemplate(c) s.PatchValue(&s.useClone, true) manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1") name := string(instance.Id()) cloned := <-s.events s.AssertEvent(c, cloned, mock.Cloned, "juju-series-template") c.Assert(cloned.Args, gc.IsNil) s.AssertEvent(c, <-s.events, mock.Started, name) } func (s *LxcSuite) TestCreateContainerEventsWithCloneExistingTemplateAUFS(c *gc.C) { s.createTemplate(c) s.PatchValue(&s.useClone, true) s.PatchValue(&s.useAUFS, true) manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1") name := string(instance.Id()) cloned := <-s.events s.AssertEvent(c, cloned, mock.Cloned, "juju-series-template") c.Assert(cloned.Args, gc.DeepEquals, []string{"--snapshot", "--backingstore", "aufs"}) s.AssertEvent(c, <-s.events, mock.Started, name) } func (s *LxcSuite) TestCreateContainerWithCloneMountsAndAutostarts(c *gc.C) { s.createTemplate(c) s.PatchValue(&s.useClone, true) manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1") name := string(instance.Id()) autostartLink := lxc.RestartSymlink(name) config, err := ioutil.ReadFile(lxc.ContainerConfigFilename(name)) c.Assert(err, gc.IsNil) mountLine := "lxc.mount.entry=/var/log/juju var/log/juju none defaults,bind 0 0" c.Assert(string(config), jc.Contains, mountLine) c.Assert(autostartLink, jc.IsSymlink) } func (s *LxcSuite) TestContainerState(c *gc.C) { manager := s.makeManager(c, "test") c.Logf("%#v", manager) instance := containertesting.CreateContainer(c, manager, "1/lxc/0") // The mock container will be immediately "running". c.Assert(instance.Status(), gc.Equals, string(golxc.StateRunning)) // DestroyContainer stops and then destroys the container, putting it // into "unknown" state. err := manager.DestroyContainer(instance) c.Assert(err, gc.IsNil) c.Assert(instance.Status(), gc.Equals, string(golxc.StateUnknown)) } func (s *LxcSuite) TestDestroyContainer(c *gc.C) { manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1/lxc/0") err := manager.DestroyContainer(instance) c.Assert(err, gc.IsNil) name := string(instance.Id()) // Check that the container dir is no longer in the container dir c.Assert(filepath.Join(s.ContainerDir, name), jc.DoesNotExist) // but instead, in the removed container dir c.Assert(filepath.Join(s.RemovedDir, name), jc.IsDirectory) } func (s *LxcSuite) TestDestroyContainerNameClash(c *gc.C) { manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1/lxc/0") name := string(instance.Id()) targetDir := filepath.Join(s.RemovedDir, name) err := os.MkdirAll(targetDir, 0755) c.Assert(err, gc.IsNil) err = manager.DestroyContainer(instance) c.Assert(err, gc.IsNil) // Check that the container dir is no longer in the container dir c.Assert(filepath.Join(s.ContainerDir, name), jc.DoesNotExist) // but instead, in the removed container dir with a ".1" suffix as there was already a directory there. c.Assert(filepath.Join(s.RemovedDir, fmt.Sprintf("%s.1", name)), jc.IsDirectory) } func (s *LxcSuite) TestNamedManagerPrefix(c *gc.C) { manager := s.makeManager(c, "eric") instance := containertesting.CreateContainer(c, manager, "1/lxc/0") c.Assert(string(instance.Id()), gc.Equals, "eric-machine-1-lxc-0") } func (s *LxcSuite) TestListContainers(c *gc.C) { foo := s.makeManager(c, "foo") bar := s.makeManager(c, "bar") foo1 := containertesting.CreateContainer(c, foo, "1/lxc/0") foo2 := containertesting.CreateContainer(c, foo, "1/lxc/1") foo3 := containertesting.CreateContainer(c, foo, "1/lxc/2") bar1 := containertesting.CreateContainer(c, bar, "1/lxc/0") bar2 := containertesting.CreateContainer(c, bar, "1/lxc/1") result, err := foo.ListContainers() c.Assert(err, gc.IsNil) instancetest.MatchInstances(c, result, foo1, foo2, foo3) result, err = bar.ListContainers() c.Assert(err, gc.IsNil) instancetest.MatchInstances(c, result, bar1, bar2) } func (s *LxcSuite) TestCreateContainerAutostarts(c *gc.C) { manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1/lxc/0") autostartLink := lxc.RestartSymlink(string(instance.Id())) c.Assert(autostartLink, jc.IsSymlink) } func (s *LxcSuite) TestCreateContainerNoRestartDir(c *gc.C) { err := os.Remove(s.RestartDir) c.Assert(err, gc.IsNil) manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1/lxc/0") name := string(instance.Id()) autostartLink := lxc.RestartSymlink(name) config, err := ioutil.ReadFile(lxc.ContainerConfigFilename(name)) c.Assert(err, gc.IsNil) expected := ` lxc.network.type = veth lxc.network.link = nic42 lxc.network.flags = up lxc.start.auto = 1 lxc.mount.entry=/var/log/juju var/log/juju none defaults,bind 0 0 ` c.Assert(string(config), gc.Equals, expected) c.Assert(autostartLink, jc.DoesNotExist) } func (s *LxcSuite) TestDestroyContainerRemovesAutostartLink(c *gc.C) { manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1/lxc/0") err := manager.DestroyContainer(instance) c.Assert(err, gc.IsNil) autostartLink := lxc.RestartSymlink(string(instance.Id())) c.Assert(autostartLink, jc.SymlinkDoesNotExist) } func (s *LxcSuite) TestDestroyContainerNoRestartDir(c *gc.C) { err := os.Remove(s.RestartDir) c.Assert(err, gc.IsNil) manager := s.makeManager(c, "test") instance := containertesting.CreateContainer(c, manager, "1/lxc/0") err = manager.DestroyContainer(instance) c.Assert(err, gc.IsNil) } type NetworkSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&NetworkSuite{}) func (*NetworkSuite) TestGenerateNetworkConfig(c *gc.C) { for _, test := range []struct { config *container.NetworkConfig net string link string }{{ config: nil, net: "veth", link: "lxcbr0", }, { config: lxc.DefaultNetworkConfig(), net: "veth", link: "lxcbr0", }, { config: container.BridgeNetworkConfig("foo"), net: "veth", link: "foo", }, { config: container.PhysicalNetworkConfig("foo"), net: "phys", link: "foo", }} { config := lxc.GenerateNetworkConfig(test.config) c.Assert(config, jc.Contains, fmt.Sprintf("lxc.network.type = %s\n", test.net)) c.Assert(config, jc.Contains, fmt.Sprintf("lxc.network.link = %s\n", test.link)) } } func (*NetworkSuite) TestNetworkConfigTemplate(c *gc.C) { config := lxc.NetworkConfigTemplate("foo", "bar") //In the past, the entire lxc.conf file was just networking. With the addition //of the auto start, we now have to have better isolate this test. As such, we //parse the conf template results and just get the results that start with //'lxc.network' as that is what the test cares about. obtained := []string{} for _, value := range strings.Split(config, "\n") { if strings.HasPrefix(value, "lxc.network") { obtained = append(obtained, value) } } expected := []string{ "lxc.network.type = foo", "lxc.network.link = bar", "lxc.network.flags = up", } c.Assert(obtained, gc.DeepEquals, expected) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/initialisation_test.go�������������������0000644�0000153�0000161�00000002403�12321735776�030052� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package lxc import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type InitialiserSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&InitialiserSuite{}) func (s *InitialiserSuite) TestLTSSeriesPackages(c *gc.C) { cmdChan := s.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil) container := NewContainerInitialiser("precise") err := container.Initialise() c.Assert(err, gc.IsNil) cmd := <-cmdChan c.Assert(cmd.Args, gc.DeepEquals, []string{ "apt-get", "--option=Dpkg::Options::=--force-confold", "--option=Dpkg::options::=--force-unsafe-io", "--assume-yes", "--quiet", "install", "--target-release", "precise-updates/cloud-tools", "lxc", }) } func (s *InitialiserSuite) TestNoSeriesPackages(c *gc.C) { cmdChan := s.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil) container := NewContainerInitialiser("") err := container.Initialise() c.Assert(err, gc.IsNil) cmd := <-cmdChan c.Assert(cmd.Args, gc.DeepEquals, []string{ "apt-get", "--option=Dpkg::Options::=--force-confold", "--option=Dpkg::options::=--force-unsafe-io", "--assume-yes", "--quiet", "install", "lxc", }) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/clonetemplate.go�������������������������0000644�0000153�0000161�00000014242�12321735776�026633� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package lxc import ( "fmt" "io" "os" "path/filepath" "sync" "time" "launchpad.net/golxc" coreCloudinit "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/container" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/fslock" "launchpad.net/juju-core/utils/tailer" ) const ( templateShutdownUpstartFilename = "/etc/init/juju-template-restart.conf" templateShutdownUpstartScript = ` description "Juju lxc template shutdown job" author "Juju Team <juju@lists.ubuntu.com>" start on stopped cloud-final script shutdown -h now end script post-stop script rm ` + templateShutdownUpstartFilename + ` end script ` ) var ( TemplateLockDir = "/var/lib/juju/locks" TemplateStopTimeout = 5 * time.Minute ) // templateUserData returns a minimal user data necessary for the template. // This should have the authorized keys, base packages, the cloud archive if // necessary, initial apt proxy config, and it should do the apt-get // update/upgrade initially. func templateUserData( series string, authorizedKeys string, aptProxy osenv.ProxySettings, ) ([]byte, error) { config := coreCloudinit.New() config.AddScripts( "set -xe", // ensure we run all the scripts or abort. ) config.AddSSHAuthorizedKeys(authorizedKeys) cloudinit.MaybeAddCloudArchiveCloudTools(config, series) cloudinit.AddAptCommands(aptProxy, config) config.AddScripts( fmt.Sprintf( "printf '%%s\n' %s > %s", utils.ShQuote(templateShutdownUpstartScript), templateShutdownUpstartFilename, )) data, err := config.Render() if err != nil { return nil, err } return data, nil } func AcquireTemplateLock(name, message string) (*fslock.Lock, error) { logger.Infof("wait for fslock on %v", name) lock, err := fslock.NewLock(TemplateLockDir, name) if err != nil { logger.Tracef("failed to create fslock for template: %v", err) return nil, err } err = lock.Lock(message) if err != nil { logger.Tracef("failed to acquire lock for template: %v", err) return nil, err } return lock, nil } // Make sure a template exists that we can clone from. func EnsureCloneTemplate( backingFilesystem string, series string, network *container.NetworkConfig, authorizedKeys string, aptProxy osenv.ProxySettings, ) (golxc.Container, error) { name := fmt.Sprintf("juju-%s-template", series) containerDirectory, err := container.NewDirectory(name) if err != nil { return nil, err } lock, err := AcquireTemplateLock(name, "ensure clone exists") if err != nil { return nil, err } defer lock.Unlock() lxcContainer := LxcObjectFactory.New(name) // Early exit if the container has been constructed before. if lxcContainer.IsConstructed() { logger.Infof("template exists, continuing") return lxcContainer, nil } logger.Infof("template does not exist, creating") userData, err := templateUserData(series, authorizedKeys, aptProxy) if err != nil { logger.Tracef("filed to create tempalte user data for template: %v", err) return nil, err } userDataFilename, err := container.WriteCloudInitFile(containerDirectory, userData) if err != nil { return nil, err } configFile, err := writeLxcConfig(network, containerDirectory) if err != nil { logger.Errorf("failed to write config file: %v", err) return nil, err } templateParams := []string{ "--debug", // Debug errors in the cloud image "--userdata", userDataFilename, // Our groovey cloud-init "--hostid", name, // Use the container name as the hostid "-r", series, } var extraCreateArgs []string if backingFilesystem == Btrfs { extraCreateArgs = append(extraCreateArgs, "-B", Btrfs) } // Create the container. logger.Tracef("create the container") if err := lxcContainer.Create(configFile, defaultTemplate, extraCreateArgs, templateParams); err != nil { logger.Errorf("lxc container creation failed: %v", err) return nil, err } // Make sure that the mount dir has been created. logger.Tracef("make the mount dir for the shared logs") if err := os.MkdirAll(internalLogDir(name), 0755); err != nil { logger.Tracef("failed to create internal /var/log/juju mount dir: %v", err) return nil, err } // Start the lxc container with the appropriate settings for grabbing the // console output and a log file. consoleFile := filepath.Join(containerDirectory, "console.log") lxcContainer.SetLogFile(filepath.Join(containerDirectory, "container.log"), golxc.LogDebug) logger.Tracef("start the container") // We explicitly don't pass through the config file to the container.Start // method as we have passed it through at container creation time. This // is necessary to get the appropriate rootfs reference without explicitly // setting it ourselves. if err = lxcContainer.Start("", consoleFile); err != nil { logger.Errorf("container failed to start: %v", err) return nil, err } logger.Infof("template container started, now wait for it to stop") // Perhaps we should wait for it to finish, and the question becomes "how // long do we wait for it to complete?" console, err := os.Open(consoleFile) if err != nil { // can't listen return nil, err } tailWriter := &logTail{tick: time.Now()} consoleTailer := tailer.NewTailer(console, tailWriter, 0, nil) defer consoleTailer.Stop() // We should wait maybe 1 minute between output? // if no output check to see if stopped // If we have no output and still running, something has probably gone wrong for lxcContainer.IsRunning() { if tailWriter.lastTick().Before(time.Now().Add(-TemplateStopTimeout)) { logger.Infof("not heard anything from the template log for five minutes") return nil, fmt.Errorf("template container %q did not stop", name) } time.Sleep(time.Second) } return lxcContainer, nil } type logTail struct { tick time.Time mutex sync.Mutex } var _ io.Writer = (*logTail)(nil) func (t *logTail) Write(data []byte) (int, error) { logger.Tracef(string(data)) t.mutex.Lock() defer t.mutex.Unlock() t.tick = time.Now() return len(data), nil } func (t *logTail) lastTick() time.Time { t.mutex.Lock() defer t.mutex.Unlock() tick := t.tick return tick } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/mock/������������������������������������0000755�0000153�0000161�00000000000�12321735642�024366� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/mock/mock-lxc.go�������������������������0000644�0000153�0000161�00000020146�12321735642�026435� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package mock import ( "fmt" "io/ioutil" "os" "path/filepath" "sync" "github.com/juju/loggo" "launchpad.net/golxc" "launchpad.net/juju-core/container" "launchpad.net/juju-core/utils" ) // This file provides a mock implementation of the golxc interfaces // ContainerFactory and Container. var logger = loggo.GetLogger("juju.container.lxc.mock") type Action int const ( // A container has been started. Started Action = iota // A container has been stopped. Stopped // A container has been created. Created // A container has been destroyed. Destroyed // A container has been cloned. Cloned ) func (action Action) String() string { switch action { case Started: return "Started" case Stopped: return "Stopped" case Created: return "Created" case Destroyed: return "Destroyed" case Cloned: return "Cloned" } return "unknown" } type Event struct { Action Action InstanceId string Args []string TemplateArgs []string } type ContainerFactory interface { golxc.ContainerFactory AddListener(chan<- Event) RemoveListener(chan<- Event) } type mockFactory struct { containerDir string instances map[string]golxc.Container listeners []chan<- Event mutex sync.Mutex } func MockFactory(containerDir string) ContainerFactory { return &mockFactory{ containerDir: containerDir, instances: make(map[string]golxc.Container), } } type mockContainer struct { factory *mockFactory name string state golxc.State logFile string logLevel golxc.LogLevel } func (mock *mockContainer) getState() golxc.State { mock.factory.mutex.Lock() defer mock.factory.mutex.Unlock() return mock.state } func (mock *mockContainer) setState(newState golxc.State) { mock.factory.mutex.Lock() defer mock.factory.mutex.Unlock() mock.state = newState logger.Debugf("container %q state change to %s", mock.name, string(newState)) } // Name returns the name of the container. func (mock *mockContainer) Name() string { return mock.name } func (mock *mockContainer) configFilename() string { return filepath.Join(mock.factory.containerDir, mock.name, "config") } // Create creates a new container based on the given template. func (mock *mockContainer) Create(configFile, template string, extraArgs []string, templateArgs []string) error { if mock.getState() != golxc.StateUnknown { return fmt.Errorf("container is already created") } mock.factory.instances[mock.name] = mock // Create the container directory. containerDir := filepath.Join(mock.factory.containerDir, mock.name) if err := os.MkdirAll(containerDir, 0755); err != nil { return err } if err := utils.CopyFile(mock.configFilename(), configFile); err != nil { return err } mock.setState(golxc.StateStopped) mock.factory.notify(eventArgs(Created, mock.name, extraArgs, templateArgs)) return nil } // Start runs the container as a daemon. func (mock *mockContainer) Start(configFile, consoleFile string) error { state := mock.getState() if state == golxc.StateUnknown { return fmt.Errorf("container has not been created") } else if state == golxc.StateRunning { return fmt.Errorf("container is already running") } ioutil.WriteFile( filepath.Join(container.ContainerDir, mock.name, "console.log"), []byte("fake console.log"), 0644) mock.setState(golxc.StateRunning) mock.factory.notify(event(Started, mock.name)) return nil } // Stop terminates the running container. func (mock *mockContainer) Stop() error { state := mock.getState() if state == golxc.StateUnknown { return fmt.Errorf("container has not been created") } else if state == golxc.StateStopped { return fmt.Errorf("container is already stopped") } mock.setState(golxc.StateStopped) mock.factory.notify(event(Stopped, mock.name)) return nil } // Clone creates a copy of the container, giving the copy the specified name. func (mock *mockContainer) Clone(name string, extraArgs []string, templateArgs []string) (golxc.Container, error) { state := mock.getState() if state == golxc.StateUnknown { return nil, fmt.Errorf("container has not been created") } else if state == golxc.StateRunning { return nil, fmt.Errorf("container is running, clone not possible") } container := &mockContainer{ factory: mock.factory, name: name, state: golxc.StateStopped, logLevel: golxc.LogWarning, } mock.factory.instances[name] = container // Create the container directory. containerDir := filepath.Join(mock.factory.containerDir, name) if err := os.MkdirAll(containerDir, 0755); err != nil { return nil, err } if err := utils.CopyFile(container.configFilename(), mock.configFilename()); err != nil { return nil, err } mock.factory.notify(eventArgs(Cloned, mock.name, extraArgs, templateArgs)) return container, nil } // Freeze freezes all the container's processes. func (mock *mockContainer) Freeze() error { return nil } // Unfreeze thaws all frozen container's processes. func (mock *mockContainer) Unfreeze() error { return nil } // Destroy stops and removes the container. func (mock *mockContainer) Destroy() error { state := mock.getState() // golxc destroy will stop the machine if it is running. if state == golxc.StateRunning { mock.Stop() } if state == golxc.StateUnknown { return fmt.Errorf("container has not been created") } delete(mock.factory.instances, mock.name) mock.setState(golxc.StateUnknown) mock.factory.notify(event(Destroyed, mock.name)) return nil } // Wait waits for one of the specified container states. func (mock *mockContainer) Wait(states ...golxc.State) error { return nil } // Info returns the status and the process id of the container. func (mock *mockContainer) Info() (golxc.State, int, error) { pid := -1 state := mock.getState() if state == golxc.StateRunning { pid = 42 } return state, pid, nil } // IsConstructed checks if the container image exists. func (mock *mockContainer) IsConstructed() bool { return mock.getState() != golxc.StateUnknown } // IsRunning checks if the state of the container is 'RUNNING'. func (mock *mockContainer) IsRunning() bool { return mock.getState() == golxc.StateRunning } // String returns information about the container, like the name, state, // and process id. func (mock *mockContainer) String() string { state, pid, _ := mock.Info() return fmt.Sprintf("<MockContainer %q, state: %s, pid %d>", mock.name, string(state), pid) } // LogFile returns the current filename used for the LogFile. func (mock *mockContainer) LogFile() string { return mock.logFile } // LogLevel returns the current logging level (only used if the // LogFile is not ""). func (mock *mockContainer) LogLevel() golxc.LogLevel { return mock.logLevel } // SetLogFile sets both the LogFile and LogLevel. func (mock *mockContainer) SetLogFile(filename string, level golxc.LogLevel) { mock.logFile = filename mock.logLevel = level } func (mock *mockFactory) String() string { return fmt.Sprintf("mock lxc factory") } func (mock *mockFactory) New(name string) golxc.Container { mock.mutex.Lock() defer mock.mutex.Unlock() container, ok := mock.instances[name] if ok { return container } container = &mockContainer{ factory: mock, name: name, state: golxc.StateUnknown, logLevel: golxc.LogWarning, } mock.instances[name] = container return container } func (mock *mockFactory) List() (result []golxc.Container, err error) { for _, container := range mock.instances { result = append(result, container) } return } func event(action Action, instanceId string) Event { return Event{action, instanceId, nil, nil} } func eventArgs(action Action, instanceId string, args []string, template []string) Event { return Event{action, instanceId, args, template} } func (mock *mockFactory) notify(event Event) { for _, c := range mock.listeners { c <- event } } func (mock *mockFactory) AddListener(listener chan<- Event) { mock.listeners = append(mock.listeners, listener) } func (mock *mockFactory) RemoveListener(listener chan<- Event) { pos := 0 for i, c := range mock.listeners { if c == listener { pos = i } } mock.listeners = append(mock.listeners[:pos], mock.listeners[pos+1:]...) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/testing/���������������������������������0000755�0000153�0000161�00000000000�12321735643�025113� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/testing/test.go��������������������������0000644�0000153�0000161�00000002033�12321735642�026416� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/lxc" "launchpad.net/juju-core/container/lxc/mock" "launchpad.net/juju-core/testing/testbase" ) // TestSuite replaces the lxc factory that the broker uses with a mock // implementation. type TestSuite struct { testbase.LoggingSuite Factory mock.ContainerFactory ContainerDir string RemovedDir string LxcDir string RestartDir string } func (s *TestSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.ContainerDir = c.MkDir() s.PatchValue(&container.ContainerDir, s.ContainerDir) s.RemovedDir = c.MkDir() s.PatchValue(&container.RemovedContainerDir, s.RemovedDir) s.LxcDir = c.MkDir() s.PatchValue(&lxc.LxcContainerDir, s.LxcDir) s.RestartDir = c.MkDir() s.PatchValue(&lxc.LxcRestartDir, s.RestartDir) s.Factory = mock.MockFactory(s.LxcDir) s.PatchValue(&lxc.LxcObjectFactory, s.Factory) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/lxc/initialisation.go������������������������0000644�0000153�0000161�00000002326�12321735776�027017� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package lxc import ( "launchpad.net/juju-core/container" "launchpad.net/juju-core/utils" ) var requiredPackages = []string{ "lxc", } type containerInitialiser struct { series string } // containerInitialiser implements container.Initialiser. var _ container.Initialiser = (*containerInitialiser)(nil) // NewContainerInitialiser returns an instance used to perform the steps // required to allow a host machine to run a LXC container. func NewContainerInitialiser(series string) container.Initialiser { return &containerInitialiser{series} } // Initialise is specified on the container.Initialiser interface. func (ci *containerInitialiser) Initialise() error { return ensureDependencies(ci.series) } // ensureDependencies creates a set of install packages using AptGetPreparePackages // and runs each set of packages through AptGetInstall func ensureDependencies(series string) error { var err error aptGetInstallCommandList := utils.AptGetPreparePackages(requiredPackages, series) for _, commands := range aptGetInstallCommandList { err = utils.AptGetInstall(commands...) if err != nil { return err } } return err } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/�����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023431� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/interface.go�����������������������������0000644�0000153�0000161�00000002547�12321735642�025743� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package kvm import ( "launchpad.net/juju-core/container" ) // StartParams is a simple parameter struct for Container.Start. type StartParams struct { Series string Arch string UserDataFile string Network *container.NetworkConfig Memory uint64 // MB CpuCores uint64 RootDisk uint64 // GB } // Container represents a virtualized container instance and provides // operations to create, maintain and destroy the container. type Container interface { // Name returns the name of the container. Name() string // Start runs the container as a daemon. Start(params StartParams) error // Stop terminates the running container. Stop() error // IsRunning returns wheter or not the container is running and active. IsRunning() bool // String returns information about the container, like the name, state, // and process id. String() string } // ContainerFactory represents the methods used to create Containers. This // wraps the low level OS functions for dealing with the containers. type ContainerFactory interface { // New returns a container instance which can then be used for operations // like Start() and Stop() New(string) Container // List returns all the existing containers on the system. List() ([]Container, error) } ���������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/container.go�����������������������������0000644�0000153�0000161�00000003774�12321735642�025770� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package kvm import ( "fmt" "launchpad.net/juju-core/container" "launchpad.net/juju-core/log" ) type kvmContainer struct { factory *containerFactory name string // started is a three state boolean, true, false, or unknown // this allows for checking when we don't know, but using a // value if we already know it (like in the list situation). started *bool } var _ Container = (*kvmContainer)(nil) func (c *kvmContainer) Name() string { return c.name } func (c *kvmContainer) Start(params StartParams) error { logger.Debugf("Synchronise images for %s %s", params.Series, params.Arch) if err := SyncImages(params.Series, params.Arch); err != nil { return err } var bridge string if params.Network != nil { if params.Network.NetworkType == container.BridgeNetwork { bridge = params.Network.Device } else { return log.LoggedErrorf(logger, "Non-bridge network devices not yet supported") } } logger.Debugf("Create the machine %s", c.name) if err := CreateMachine(CreateMachineParams{ Hostname: c.name, Series: params.Series, Arch: params.Arch, UserDataFile: params.UserDataFile, NetworkBridge: bridge, Memory: params.Memory, CpuCores: params.CpuCores, RootDisk: params.RootDisk, }); err != nil { return err } logger.Debugf("Set machine %s to autostart", c.name) return AutostartMachine(c.name) } func (c *kvmContainer) Stop() error { if !c.IsRunning() { logger.Debugf("%s is already stopped", c.name) return nil } // Make started state unknown again. c.started = nil logger.Debugf("Stop %s", c.name) return DestroyMachine(c.name) } func (c *kvmContainer) IsRunning() bool { if c.started != nil { return *c.started } machines, err := ListMachines() if err != nil { return false } c.started = isRunning(machines[c.name]) return *c.started } func (c *kvmContainer) String() string { return fmt.Sprintf("<KVM container %v>", *c) } ����juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/kvm_test.go������������������������������0000644�0000153�0000161�00000015007�12321735642�025632� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package kvm_test import ( "path/filepath" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/kvm" kvmtesting "launchpad.net/juju-core/container/kvm/testing" containertesting "launchpad.net/juju-core/container/testing" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) type KVMSuite struct { kvmtesting.TestSuite manager container.Manager } var _ = gc.Suite(&KVMSuite{}) func (s *KVMSuite) SetUpTest(c *gc.C) { s.TestSuite.SetUpTest(c) var err error s.manager, err = kvm.NewContainerManager(container.ManagerConfig{container.ConfigName: "test"}) c.Assert(err, gc.IsNil) } func (*KVMSuite) TestManagerNameNeeded(c *gc.C) { manager, err := kvm.NewContainerManager(container.ManagerConfig{container.ConfigName: ""}) c.Assert(err, gc.ErrorMatches, "name is required") c.Assert(manager, gc.IsNil) } func (*KVMSuite) TestManagerWarnsAboutUnknownOption(c *gc.C) { _, err := kvm.NewContainerManager(container.ManagerConfig{ container.ConfigName: "BillyBatson", "shazam": "Captain Marvel", }) c.Assert(err, gc.IsNil) c.Assert(c.GetTestLog(), jc.Contains, `WARNING juju.container unused config option: "shazam" -> "Captain Marvel"`) } func (s *KVMSuite) TestListInitiallyEmpty(c *gc.C) { containers, err := s.manager.ListContainers() c.Assert(err, gc.IsNil) c.Assert(containers, gc.HasLen, 0) } func (s *KVMSuite) createRunningContainer(c *gc.C, name string) kvm.Container { kvmContainer := s.Factory.New(name) network := container.BridgeNetworkConfig("testbr0") c.Assert(kvmContainer.Start(kvm.StartParams{ Series: "quantal", Arch: version.Current.Arch, UserDataFile: "userdata.txt", Network: network}), gc.IsNil) return kvmContainer } func (s *KVMSuite) TestListMatchesManagerName(c *gc.C) { s.createRunningContainer(c, "test-match1") s.createRunningContainer(c, "test-match2") s.createRunningContainer(c, "testNoMatch") s.createRunningContainer(c, "other") containers, err := s.manager.ListContainers() c.Assert(err, gc.IsNil) c.Assert(containers, gc.HasLen, 2) expectedIds := []instance.Id{"test-match1", "test-match2"} ids := []instance.Id{containers[0].Id(), containers[1].Id()} c.Assert(ids, jc.SameContents, expectedIds) } func (s *KVMSuite) TestListMatchesRunningContainers(c *gc.C) { running := s.createRunningContainer(c, "test-running") s.Factory.New("test-stopped") containers, err := s.manager.ListContainers() c.Assert(err, gc.IsNil) c.Assert(containers, gc.HasLen, 1) c.Assert(string(containers[0].Id()), gc.Equals, running.Name()) } func (s *KVMSuite) TestCreateContainer(c *gc.C) { instance := containertesting.CreateContainer(c, s.manager, "1/kvm/0") name := string(instance.Id()) cloudInitFilename := filepath.Join(s.ContainerDir, name, "cloud-init") containertesting.AssertCloudInit(c, cloudInitFilename) } func (s *KVMSuite) TestDestroyContainer(c *gc.C) { instance := containertesting.CreateContainer(c, s.manager, "1/lxc/0") err := s.manager.DestroyContainer(instance) c.Assert(err, gc.IsNil) name := string(instance.Id()) // Check that the container dir is no longer in the container dir c.Assert(filepath.Join(s.ContainerDir, name), jc.DoesNotExist) // but instead, in the removed container dir c.Assert(filepath.Join(s.RemovedDir, name), jc.IsDirectory) } type ConstraintsSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&ConstraintsSuite{}) func (s *ConstraintsSuite) TestDefaults(c *gc.C) { for _, test := range []struct { cons string expected kvm.StartParams infoLog []string }{{ expected: kvm.StartParams{ Memory: kvm.DefaultMemory, CpuCores: kvm.DefaultCpu, RootDisk: kvm.DefaultDisk, }, }, { cons: "mem=256M", expected: kvm.StartParams{ Memory: kvm.MinMemory, CpuCores: kvm.DefaultCpu, RootDisk: kvm.DefaultDisk, }, }, { cons: "mem=4G", expected: kvm.StartParams{ Memory: 4 * 1024, CpuCores: kvm.DefaultCpu, RootDisk: kvm.DefaultDisk, }, }, { cons: "cpu-cores=4", expected: kvm.StartParams{ Memory: kvm.DefaultMemory, CpuCores: 4, RootDisk: kvm.DefaultDisk, }, }, { cons: "cpu-cores=0", expected: kvm.StartParams{ Memory: kvm.DefaultMemory, CpuCores: kvm.MinCpu, RootDisk: kvm.DefaultDisk, }, }, { cons: "root-disk=512M", expected: kvm.StartParams{ Memory: kvm.DefaultMemory, CpuCores: kvm.DefaultCpu, RootDisk: kvm.MinDisk, }, }, { cons: "root-disk=4G", expected: kvm.StartParams{ Memory: kvm.DefaultMemory, CpuCores: kvm.DefaultCpu, RootDisk: 4, }, }, { cons: "arch=armhf", expected: kvm.StartParams{ Memory: kvm.DefaultMemory, CpuCores: kvm.DefaultCpu, RootDisk: kvm.DefaultDisk, }, infoLog: []string{ `arch constraint of "armhf" being ignored as not supported`, }, }, { cons: "container=lxc", expected: kvm.StartParams{ Memory: kvm.DefaultMemory, CpuCores: kvm.DefaultCpu, RootDisk: kvm.DefaultDisk, }, infoLog: []string{ `container constraint of "lxc" being ignored as not supported`, }, }, { cons: "cpu-power=100", expected: kvm.StartParams{ Memory: kvm.DefaultMemory, CpuCores: kvm.DefaultCpu, RootDisk: kvm.DefaultDisk, }, infoLog: []string{ `cpu-power constraint of 100 being ignored as not supported`, }, }, { cons: "tags=foo,bar", expected: kvm.StartParams{ Memory: kvm.DefaultMemory, CpuCores: kvm.DefaultCpu, RootDisk: kvm.DefaultDisk, }, infoLog: []string{ `tags constraint of "foo,bar" being ignored as not supported`, }, }, { cons: "mem=4G cpu-cores=4 root-disk=20G arch=armhf cpu-power=100 container=lxc tags=foo,bar", expected: kvm.StartParams{ Memory: 4 * 1024, CpuCores: 4, RootDisk: 20, }, infoLog: []string{ `arch constraint of "armhf" being ignored as not supported`, `container constraint of "lxc" being ignored as not supported`, `cpu-power constraint of 100 being ignored as not supported`, `tags constraint of "foo,bar" being ignored as not supported`, }, }} { tw := &loggo.TestWriter{} c.Assert(loggo.RegisterWriter("constraint-tester", tw, loggo.DEBUG), gc.IsNil) cons := constraints.MustParse(test.cons) params := kvm.ParseConstraintsToStartParams(cons) c.Check(params, gc.DeepEquals, test.expected) c.Check(tw.Log, jc.LogMatches, test.infoLog) loggo.RemoveWriter("constraint-tester") } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/instance.go������������������������������0000644�0000153�0000161�00000003225�12321735642�025601� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package kvm import ( "fmt" "launchpad.net/juju-core/instance" ) type kvmInstance struct { container Container id string } var _ instance.Instance = (*kvmInstance)(nil) // Id implements instance.Instance.Id. func (kvm *kvmInstance) Id() instance.Id { return instance.Id(kvm.id) } // Status implements instance.Instance.Status. func (kvm *kvmInstance) Status() string { if kvm.container.IsRunning() { return "running" } return "stopped" } func (*kvmInstance) Refresh() error { return nil } func (kvm *kvmInstance) Addresses() ([]instance.Address, error) { logger.Errorf("kvmInstance.Addresses not implemented") return nil, nil } // DNSName implements instance.Instance.DNSName. func (kvm *kvmInstance) DNSName() (string, error) { return "", instance.ErrNoDNSName } // WaitDNSName implements instance.Instance.WaitDNSName. func (kvm *kvmInstance) WaitDNSName() (string, error) { return "", instance.ErrNoDNSName } // OpenPorts implements instance.Instance.OpenPorts. func (kvm *kvmInstance) OpenPorts(machineId string, ports []instance.Port) error { return fmt.Errorf("not implemented") } // ClosePorts implements instance.Instance.ClosePorts. func (kvm *kvmInstance) ClosePorts(machineId string, ports []instance.Port) error { return fmt.Errorf("not implemented") } // Ports implements instance.Instance.Ports. func (kvm *kvmInstance) Ports(machineId string) ([]instance.Port, error) { return nil, fmt.Errorf("not implemented") } // Add a string representation of the id. func (kvm *kvmInstance) String() string { return fmt.Sprintf("kvm:%s", kvm.id) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/live_test.go�����������������������������0000644�0000153�0000161�00000010141�12321735776�025776� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package kvm_test import ( "fmt" "os" "runtime" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/kvm" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" jujutesting "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) type LiveSuite struct { testbase.LoggingSuite ContainerDir string RemovedDir string } var _ = gc.Suite(&LiveSuite{}) func (s *LiveSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) // Skip if not linux if runtime.GOOS != "linux" { c.Skip("not running linux") } // Skip if not running as root. if os.Getuid() != 0 { c.Skip("not running as root") } s.ContainerDir = c.MkDir() s.PatchValue(&container.ContainerDir, s.ContainerDir) s.RemovedDir = c.MkDir() s.PatchValue(&container.RemovedContainerDir, s.RemovedDir) loggo.GetLogger("juju.container").SetLogLevel(loggo.TRACE) } func (s *LiveSuite) newManager(c *gc.C, name string) container.Manager { manager, err := kvm.NewContainerManager( container.ManagerConfig{ container.ConfigName: name, container.ConfigLogDir: c.MkDir(), }) c.Assert(err, gc.IsNil) return manager } func assertNumberOfContainers(c *gc.C, manager container.Manager, count int) { containers, err := manager.ListContainers() c.Assert(err, gc.IsNil) c.Assert(containers, gc.HasLen, count) } func (s *LiveSuite) TestNoInitialContainers(c *gc.C) { manager := s.newManager(c, "test") assertNumberOfContainers(c, manager, 0) } func shutdownMachines(manager container.Manager) func(*gc.C) { return func(c *gc.C) { instances, err := manager.ListContainers() c.Assert(err, gc.IsNil) for _, instance := range instances { err := manager.DestroyContainer(instance) c.Check(err, gc.IsNil) } } } func createContainer(c *gc.C, manager container.Manager, machineId string) instance.Instance { machineNonce := "fake-nonce" stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, machineNonce, stateInfo, apiInfo) network := container.BridgeNetworkConfig("virbr0") machineConfig.Tools = &tools.Tools{ Version: version.MustParseBinary("2.3.4-foo-bar"), URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", } environConfig := dummyConfig(c) err := environs.FinishMachineConfig(machineConfig, environConfig, constraints.Value{}) c.Assert(err, gc.IsNil) inst, hardware, err := manager.CreateContainer(machineConfig, "precise", network) c.Assert(err, gc.IsNil) c.Assert(hardware, gc.NotNil) expected := fmt.Sprintf("arch=%s cpu-cores=1 mem=512M root-disk=8192M", version.Current.Arch) c.Assert(hardware.String(), gc.Equals, expected) return inst } func (s *LiveSuite) TestShutdownMachines(c *gc.C) { manager := s.newManager(c, "test") createContainer(c, manager, "1/kvm/0") createContainer(c, manager, "1/kvm/1") assertNumberOfContainers(c, manager, 2) shutdownMachines(manager)(c) assertNumberOfContainers(c, manager, 0) } func (s *LiveSuite) TestManagerIsolation(c *gc.C) { firstManager := s.newManager(c, "first") s.AddCleanup(shutdownMachines(firstManager)) createContainer(c, firstManager, "1/kvm/0") createContainer(c, firstManager, "1/kvm/1") secondManager := s.newManager(c, "second") s.AddCleanup(shutdownMachines(secondManager)) createContainer(c, secondManager, "1/kvm/0") assertNumberOfContainers(c, firstManager, 2) assertNumberOfContainers(c, secondManager, 1) } func dummyConfig(c *gc.C) *config.Config { testConfig, err := config.New(config.UseDefaults, coretesting.FakeConfig()) c.Assert(err, gc.IsNil) testConfig, err = testConfig.Apply(map[string]interface{}{ "type": "dummy", "state-server": false, "agent-version": version.Current.Number.String(), }) c.Assert(err, gc.IsNil) return testConfig } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/libvirt.go�������������������������������0000644�0000153�0000161�00000007727�12321735642�025463� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package kvm // This file contains wrappers around the following executables: // uvt-simplestreams-libvirt // uvt-kvm // virsh // Those executables are found in the following packages: // uvtool-libvirt // libvirt-bin // // These executables provide Juju's interface to dealing with kvm containers. // The define how we start, stop and list running containers on the host import ( "fmt" "regexp" "strings" "launchpad.net/juju-core/utils" ) var ( // The regular expression for breaking up the results of 'virsh list' // (?m) - specify that this is a multiline regex // first part is the opaque identifier we don't care about // then the hostname, and lastly the status. machineListPattern = regexp.MustCompile(`(?m)^\s+\d+\s+(?P<hostname>[-\w]+)\s+(?P<status>.+)\s*$`) ) // run the command and return the combined output. func run(command string, args ...string) (output string, err error) { logger.Tracef("%s %v", command, args) output, err = utils.RunCommand(command, args...) logger.Tracef("output: %v", output) return output, err } // SyncImages updates the local cached images by reading the simplestreams // data and downloading the cloud images to the uvtool pool (used by libvirt). func SyncImages(series string, arch string) error { args := []string{ "sync", fmt.Sprintf("arch=%s", arch), fmt.Sprintf("release=%s", series), } _, err := run("uvt-simplestreams-libvirt", args...) return err } type CreateMachineParams struct { Hostname string Series string Arch string UserDataFile string NetworkBridge string Memory uint64 CpuCores uint64 RootDisk uint64 } // CreateMachine creates a virtual machine and starts it. func CreateMachine(params CreateMachineParams) error { if params.Hostname == "" { return fmt.Errorf("Hostname is required") } args := []string{ "create", "--log-console-output", // do wonder where this goes... } if params.UserDataFile != "" { args = append(args, "--user-data", params.UserDataFile) } if params.NetworkBridge != "" { args = append(args, "--bridge", params.NetworkBridge) } if params.Memory != 0 { args = append(args, "--memory", fmt.Sprint(params.Memory)) } if params.CpuCores != 0 { args = append(args, "--cpu", fmt.Sprint(params.CpuCores)) } if params.RootDisk != 0 { args = append(args, "--disk", fmt.Sprint(params.RootDisk)) } // TODO add memory, cpu and disk prior to hostname args = append(args, params.Hostname) if params.Series != "" { args = append(args, fmt.Sprintf("release=%s", params.Series)) } if params.Arch != "" { args = append(args, fmt.Sprintf("arch=%s", params.Arch)) } output, err := run("uvt-kvm", args...) logger.Debugf("is this the logged output?:\n%s", output) return err } // DestroyMachine destroys the virtual machine identified by hostname. func DestroyMachine(hostname string) error { _, err := run("uvt-kvm", "destroy", hostname) return err } // AutostartMachine indicates that the virtual machines should automatically // restart when the host restarts. func AutostartMachine(hostname string) error { _, err := run("virsh", "autostart", hostname) return err } // ListMachines returns a map of machine name to state, where state is one of: // running, idle, paused, shutdown, shut off, crashed, dying, pmsuspended. func ListMachines() (map[string]string, error) { output, err := run("virsh", "-q", "list", "--all") if err != nil { return nil, err } // Split the output into lines. // Regex matching is the easiest way to match the lines. // id hostname status // separated by whitespace, with whitespace at the start too. result := make(map[string]string) for _, s := range machineListPattern.FindAllStringSubmatchIndex(output, -1) { hostnameAndStatus := machineListPattern.ExpandString(nil, "$hostname $status", output, s) parts := strings.SplitN(string(hostnameAndStatus), " ", 2) result[parts[0]] = parts[1] } return result, nil } �����������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/package_test.go��������������������������0000644�0000153�0000161�00000000320�12321735642�026420� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package kvm_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/containerfactory.go����������������������0000644�0000153�0000161�00000001417�12321735642�027350� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package kvm type containerFactory struct { } var _ ContainerFactory = (*containerFactory)(nil) func (factory *containerFactory) New(name string) Container { return &kvmContainer{ factory: factory, name: name, } } func isRunning(value string) *bool { var result *bool = new(bool) if value == "running" { *result = true } return result } func (factory *containerFactory) List() (result []Container, err error) { machines, err := ListMachines() if err != nil { return nil, err } for hostname, status := range machines { result = append(result, &kvmContainer{ factory: factory, name: hostname, started: isRunning(status), }) } return result, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/mock/������������������������������������0000755�0000153�0000161�00000000000�12321735642�024375� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/mock/mock-kvm_test.go��������������������0000644�0000153�0000161�00000011410�12321735642�027504� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package mock_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/container/kvm" "launchpad.net/juju-core/container/kvm/mock" "launchpad.net/juju-core/testing/testbase" ) type MockSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&MockSuite{}) func (*MockSuite) TestListInitiallyEmpty(c *gc.C) { factory := mock.MockFactory() containers, err := factory.List() c.Assert(err, gc.IsNil) c.Assert(containers, gc.HasLen, 0) } func (*MockSuite) TestNewContainersInList(c *gc.C) { factory := mock.MockFactory() added := []kvm.Container{} added = append(added, factory.New("first")) added = append(added, factory.New("second")) containers, err := factory.List() c.Assert(err, gc.IsNil) c.Assert(containers, jc.SameContents, added) } func (*MockSuite) TestContainers(c *gc.C) { factory := mock.MockFactory() container := factory.New("first") c.Assert(container.Name(), gc.Equals, "first") c.Assert(container.IsRunning(), jc.IsFalse) } func (*MockSuite) TestContainerStoppingStoppedErrors(c *gc.C) { factory := mock.MockFactory() container := factory.New("first") err := container.Stop() c.Assert(err, gc.ErrorMatches, "container is not running") } func (*MockSuite) TestContainerStartStarts(c *gc.C) { factory := mock.MockFactory() container := factory.New("first") err := container.Start(kvm.StartParams{}) c.Assert(err, gc.IsNil) c.Assert(container.IsRunning(), jc.IsTrue) } func (*MockSuite) TestContainerStartingRunningErrors(c *gc.C) { factory := mock.MockFactory() container := factory.New("first") err := container.Start(kvm.StartParams{}) c.Assert(err, gc.IsNil) err = container.Start(kvm.StartParams{}) c.Assert(err, gc.ErrorMatches, "container is already running") } func (*MockSuite) TestContainerStoppingRunningStops(c *gc.C) { factory := mock.MockFactory() container := factory.New("first") err := container.Start(kvm.StartParams{}) c.Assert(err, gc.IsNil) err = container.Stop() c.Assert(err, gc.IsNil) c.Assert(container.IsRunning(), jc.IsFalse) } func (*MockSuite) TestAddListener(c *gc.C) { listener := make(chan mock.Event) factory := mock.MockFactory() factory.AddListener(listener) c.Assert(factory.HasListener(listener), jc.IsTrue) } func (*MockSuite) TestRemoveFirstListener(c *gc.C) { factory := mock.MockFactory() first := make(chan mock.Event) factory.AddListener(first) second := make(chan mock.Event) factory.AddListener(second) third := make(chan mock.Event) factory.AddListener(third) factory.RemoveListener(first) c.Assert(factory.HasListener(first), jc.IsFalse) c.Assert(factory.HasListener(second), jc.IsTrue) c.Assert(factory.HasListener(third), jc.IsTrue) } func (*MockSuite) TestRemoveMiddleListener(c *gc.C) { factory := mock.MockFactory() first := make(chan mock.Event) factory.AddListener(first) second := make(chan mock.Event) factory.AddListener(second) third := make(chan mock.Event) factory.AddListener(third) factory.RemoveListener(second) c.Assert(factory.HasListener(first), jc.IsTrue) c.Assert(factory.HasListener(second), jc.IsFalse) c.Assert(factory.HasListener(third), jc.IsTrue) } func (*MockSuite) TestRemoveLastListener(c *gc.C) { factory := mock.MockFactory() first := make(chan mock.Event) factory.AddListener(first) second := make(chan mock.Event) factory.AddListener(second) third := make(chan mock.Event) factory.AddListener(third) factory.RemoveListener(third) c.Assert(factory.HasListener(first), jc.IsTrue) c.Assert(factory.HasListener(second), jc.IsTrue) c.Assert(factory.HasListener(third), jc.IsFalse) } func (*MockSuite) TestEvents(c *gc.C) { factory := mock.MockFactory() listener := make(chan mock.Event, 5) factory.AddListener(listener) first := factory.New("first") second := factory.New("second") first.Start(kvm.StartParams{}) second.Start(kvm.StartParams{}) second.Stop() first.Stop() c.Assert(<-listener, gc.Equals, mock.Event{mock.Started, "first"}) c.Assert(<-listener, gc.Equals, mock.Event{mock.Started, "second"}) c.Assert(<-listener, gc.Equals, mock.Event{mock.Stopped, "second"}) c.Assert(<-listener, gc.Equals, mock.Event{mock.Stopped, "first"}) } func (*MockSuite) TestEventsGoToAllListeners(c *gc.C) { factory := mock.MockFactory() first := make(chan mock.Event, 5) factory.AddListener(first) second := make(chan mock.Event, 5) factory.AddListener(second) container := factory.New("container") container.Start(kvm.StartParams{}) container.Stop() c.Assert(<-first, gc.Equals, mock.Event{mock.Started, "container"}) c.Assert(<-second, gc.Equals, mock.Event{mock.Started, "container"}) c.Assert(<-first, gc.Equals, mock.Event{mock.Stopped, "container"}) c.Assert(<-second, gc.Equals, mock.Event{mock.Stopped, "container"}) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/mock/package_test.go���������������������0000644�0000153�0000161�00000000321�12321735642�027352� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package mock_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/mock/mock-kvm.go�������������������������0000644�0000153�0000161�00000005652�12321735642�026460� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package mock import ( "fmt" "launchpad.net/juju-core/container/kvm" ) // This file provides a mock implementation of the kvm interfaces // ContainerFactory and Container. type Action int const ( // A container has been started. Started Action = iota // A container has been stopped. Stopped ) func (action Action) String() string { switch action { case Started: return "Started" case Stopped: return "Stopped" } return "unknown" } type Event struct { Action Action InstanceId string } type ContainerFactory interface { kvm.ContainerFactory AddListener(chan<- Event) RemoveListener(chan<- Event) HasListener(chan<- Event) bool } type mockFactory struct { instances map[string]kvm.Container listeners []chan<- Event } func MockFactory() ContainerFactory { return &mockFactory{ instances: make(map[string]kvm.Container), } } type mockContainer struct { factory *mockFactory name string started bool } // Name returns the name of the container. func (mock *mockContainer) Name() string { return mock.name } func (mock *mockContainer) Start(params kvm.StartParams) error { if mock.started { return fmt.Errorf("container is already running") } mock.started = true mock.factory.notify(Started, mock.name) return nil } // Stop terminates the running container. func (mock *mockContainer) Stop() error { if !mock.started { return fmt.Errorf("container is not running") } mock.started = false mock.factory.notify(Stopped, mock.name) return nil } func (mock *mockContainer) IsRunning() bool { return mock.started } // String returns information about the container. func (mock *mockContainer) String() string { return fmt.Sprintf("<MockContainer %q>", mock.name) } func (mock *mockFactory) String() string { return fmt.Sprintf("<Mock KVM Factory>") } func (mock *mockFactory) New(name string) kvm.Container { container, ok := mock.instances[name] if ok { return container } container = &mockContainer{ factory: mock, name: name, } mock.instances[name] = container return container } func (mock *mockFactory) List() (result []kvm.Container, err error) { for _, container := range mock.instances { result = append(result, container) } return } func (mock *mockFactory) notify(action Action, instanceId string) { event := Event{action, instanceId} for _, c := range mock.listeners { c <- event } } func (mock *mockFactory) AddListener(listener chan<- Event) { mock.listeners = append(mock.listeners, listener) } func (mock *mockFactory) RemoveListener(listener chan<- Event) { pos := 0 for i, c := range mock.listeners { if c == listener { pos = i } } mock.listeners = append(mock.listeners[:pos], mock.listeners[pos+1:]...) } func (mock *mockFactory) HasListener(listener chan<- Event) bool { for _, c := range mock.listeners { if c == listener { return true } } return false } ��������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/kvm.go�����������������������������������0000644�0000153�0000161�00000014526�12321735642�024600� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package kvm import ( "fmt" "os/exec" "strings" "github.com/juju/loggo" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/container" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/log" "launchpad.net/juju-core/names" "launchpad.net/juju-core/version" ) var ( logger = loggo.GetLogger("juju.container.kvm") KvmObjectFactory ContainerFactory = &containerFactory{} DefaultKvmBridge = "virbr0" // In order for Juju to be able to create the hardware characteristics of // the kvm machines it creates, we need to be explicit in our definition // of memory, cpu-cores and root-disk. The defaults here have been // extracted from the uvt-kvm executable. DefaultMemory uint64 = 512 // MB DefaultCpu uint64 = 1 DefaultDisk uint64 = 8 // GB // There are some values where it doesn't make sense to go below. MinMemory uint64 = 512 // MB MinCpu uint64 = 1 MinDisk uint64 = 2 // GB ) // IsKVMSupported calls into the kvm-ok executable from the cpu-checkers package. // It is a variable to allow us to overrid behaviour in the tests. var IsKVMSupported = func() (bool, error) { command := exec.Command("kvm-ok") output, err := command.CombinedOutput() if err != nil { return false, err } logger.Debugf("kvm-ok output:\n%s", output) return command.ProcessState.Success(), nil } // NewContainerManager returns a manager object that can start and stop kvm // containers. The containers that are created are namespaced by the name // parameter. func NewContainerManager(conf container.ManagerConfig) (container.Manager, error) { name := conf.PopValue(container.ConfigName) if name == "" { return nil, fmt.Errorf("name is required") } logDir := conf.PopValue(container.ConfigLogDir) if logDir == "" { logDir = agent.DefaultLogDir } conf.WarnAboutUnused() return &containerManager{name: name, logdir: logDir}, nil } // containerManager handles all of the business logic at the juju specific // level. It makes sure that the necessary directories are in place, that the // user-data is written out in the right place. type containerManager struct { name string logdir string } var _ container.Manager = (*containerManager)(nil) func (manager *containerManager) CreateContainer( machineConfig *cloudinit.MachineConfig, series string, network *container.NetworkConfig) (instance.Instance, *instance.HardwareCharacteristics, error) { name := names.MachineTag(machineConfig.MachineId) if manager.name != "" { name = fmt.Sprintf("%s-%s", manager.name, name) } // Note here that the kvmObjectFacotry only returns a valid container // object, and doesn't actually construct the underlying kvm container on // disk. kvmContainer := KvmObjectFactory.New(name) // Create the cloud-init. directory, err := container.NewDirectory(name) if err != nil { return nil, nil, fmt.Errorf("failed to create container directory: %v", err) } logger.Tracef("write cloud-init") userDataFilename, err := container.WriteUserData(machineConfig, directory) if err != nil { return nil, nil, log.LoggedErrorf(logger, "failed to write user data: %v", err) } // Create the container. startParams := ParseConstraintsToStartParams(machineConfig.Constraints) startParams.Arch = version.Current.Arch startParams.Series = series startParams.Network = network startParams.UserDataFile = userDataFilename var hardware instance.HardwareCharacteristics hardware, err = instance.ParseHardware( fmt.Sprintf("arch=%s mem=%vM root-disk=%vG cpu-cores=%v", startParams.Arch, startParams.Memory, startParams.RootDisk, startParams.CpuCores)) if err != nil { logger.Warningf("failed to parse hardware: %v", err) } logger.Tracef("create the container, constraints: %v", machineConfig.Constraints) if err := kvmContainer.Start(startParams); err != nil { return nil, nil, log.LoggedErrorf(logger, "kvm container creation failed: %v", err) } logger.Tracef("kvm container created") return &kvmInstance{kvmContainer, name}, &hardware, nil } func (manager *containerManager) DestroyContainer(instance instance.Instance) error { name := string(instance.Id()) kvmContainer := KvmObjectFactory.New(name) if err := kvmContainer.Stop(); err != nil { logger.Errorf("failed to stop kvm container: %v", err) return err } return container.RemoveDirectory(name) } func (manager *containerManager) ListContainers() (result []instance.Instance, err error) { containers, err := KvmObjectFactory.List() if err != nil { logger.Errorf("failed getting all instances: %v", err) return } managerPrefix := fmt.Sprintf("%s-", manager.name) for _, container := range containers { // Filter out those not starting with our name. name := container.Name() if !strings.HasPrefix(name, managerPrefix) { continue } if container.IsRunning() { result = append(result, &kvmInstance{container, name}) } } return } // ParseConstraintsToStartParams takes a constrants object and returns a bare // StartParams object that has Memory, Cpu, and Disk populated. If there are // no defined values in the constraints for those fields, default values are // used. Other constrains cause a warning to be emitted. func ParseConstraintsToStartParams(cons constraints.Value) StartParams { params := StartParams{ Memory: DefaultMemory, CpuCores: DefaultCpu, RootDisk: DefaultDisk, } if cons.Mem != nil { mem := *cons.Mem if mem < MinMemory { params.Memory = MinMemory } else { params.Memory = mem } } if cons.CpuCores != nil { cores := *cons.CpuCores if cores < MinCpu { params.CpuCores = MinCpu } else { params.CpuCores = cores } } if cons.RootDisk != nil { size := *cons.RootDisk / 1024 if size < MinDisk { params.RootDisk = MinDisk } else { params.RootDisk = size } } if cons.Arch != nil { logger.Infof("arch constraint of %q being ignored as not supported", *cons.Arch) } if cons.Container != nil { logger.Infof("container constraint of %q being ignored as not supported", *cons.Container) } if cons.CpuPower != nil { logger.Infof("cpu-power constraint of %v being ignored as not supported", *cons.CpuPower) } if cons.Tags != nil { logger.Infof("tags constraint of %q being ignored as not supported", strings.Join(*cons.Tags, ",")) } return params } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/testing/���������������������������������0000755�0000153�0000161�00000000000�12321735642�025121� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/testing/test.go��������������������������0000644�0000153�0000161�00000002036�12321735642�026430� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // Functions defined in this file should *ONLY* be used for testing. These // functions are exported for testing purposes only, and shouldn't be called // from code that isn't in a test file. package testing import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/kvm" "launchpad.net/juju-core/container/kvm/mock" "launchpad.net/juju-core/testing/testbase" ) // TestSuite replaces the kvm factory that the manager uses with a mock // implementation. type TestSuite struct { testbase.LoggingSuite Factory mock.ContainerFactory ContainerDir string RemovedDir string } func (s *TestSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.ContainerDir = c.MkDir() s.PatchValue(&container.ContainerDir, s.ContainerDir) s.RemovedDir = c.MkDir() s.PatchValue(&container.RemovedContainerDir, s.RemovedDir) s.Factory = mock.MockFactory() s.PatchValue(&kvm.KvmObjectFactory, s.Factory) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/kvm/initialisation.go������������������������0000644�0000153�0000161�00000004252�12321735642�027016� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package kvm import ( "fmt" "strings" "launchpad.net/juju-core/container" "launchpad.net/juju-core/utils" ) var requiredPackages = []string{ "uvtool-libvirt", "uvtool", } type containerInitialiser struct{} // containerInitialiser implements container.Initialiser. var _ container.Initialiser = (*containerInitialiser)(nil) // NewContainerInitialiser returns an instance used to perform the steps // required to allow a host machine to run a KVM container. func NewContainerInitialiser() container.Initialiser { return &containerInitialiser{} } // Initialise is specified on the container.Initialiser interface. func (ci *containerInitialiser) Initialise() error { return ensureDependencies() } func ensureDependencies() error { return utils.AptGetInstall(requiredPackages...) } const kvmNeedsUbuntu = `Sorry, KVM support with the local provider is only supported on the Ubuntu OS.` const kvmNotSupported = `KVM is not currently supported with the current settings. You could try running 'kvm-ok' yourself as root to get the full rationale as to why it isn't supported, or potentially some BIOS settings to change to enable KVM support.` const neetToInstallKVMOk = `kvm-ok is not installed. Please install the cpu-checker package. sudo apt-get install cpu-checker` const missingKVMDeps = `Some required packages are missing for KVM to work: sudo apt-get install %s ` // VerifyKVMEnabled makes sure that the host OS is Ubuntu, and that the required // packages are installed, and that the host CPU is able to support KVM. func VerifyKVMEnabled() error { if !utils.IsUbuntu() { return fmt.Errorf(kvmNeedsUbuntu) } supported, err := IsKVMSupported() if err != nil { // Missing the kvm-ok package. return fmt.Errorf(neetToInstallKVMOk) } if !supported { return fmt.Errorf(kvmNotSupported) } // Check for other packages needed. toInstall := []string{} for _, pkg := range requiredPackages { if !utils.IsPackageInstalled(pkg) { toInstall = append(toInstall, pkg) } } if len(toInstall) > 0 { return fmt.Errorf(missingKVMDeps, strings.Join(toInstall, " ")) } return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/network.go�����������������������������������0000644�0000153�0000161�00000001641�12321735642�024671� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package container const ( // BridgeNetwork will have the container use the network bridge. BridgeNetwork = "bridge" // PhyscialNetwork will have the container use a specified network device. PhysicalNetwork = "physical" ) // NetworkConfig defines how the container network will be configured. type NetworkConfig struct { NetworkType string Device string } // BridgeNetworkConfig returns a valid NetworkConfig to use the specified // device as a network bridge for the container. func BridgeNetworkConfig(device string) *NetworkConfig { return &NetworkConfig{BridgeNetwork, device} } // PhysicalNetworkConfig returns a valid NetworkConfig to use the specified // device as the network device for the container. func PhysicalNetworkConfig(device string) *NetworkConfig { return &NetworkConfig{PhysicalNetwork, device} } �����������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/package_test.go������������������������������0000644�0000153�0000161�00000000326�12321735642�025631� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package container_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/factory/�������������������������������������0000755�0000153�0000161�00000000000�12321735642�024316� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/factory/factory.go���������������������������0000644�0000153�0000161�00000001376�12321735642�026323� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // This package exists solely to avoid circular imports. package factory import ( "fmt" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/kvm" "launchpad.net/juju-core/container/lxc" "launchpad.net/juju-core/instance" ) // NewContainerManager creates the appropriate container.Manager for the // specified container type. func NewContainerManager(forType instance.ContainerType, conf container.ManagerConfig) (container.Manager, error) { switch forType { case instance.LXC: return lxc.NewContainerManager(conf) case instance.KVM: return kvm.NewContainerManager(conf) } return nil, fmt.Errorf("unknown container type: %q", forType) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/factory/factory_test.go����������������������0000644�0000153�0000161�00000002201�12321735642�027346� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package factory_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/factory" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/testing/testbase" ) type factorySuite struct { testbase.LoggingSuite } var _ = gc.Suite(&factorySuite{}) func (*factorySuite) TestNewContainerManager(c *gc.C) { for _, test := range []struct { containerType instance.ContainerType valid bool }{{ containerType: instance.LXC, valid: true, }, { containerType: instance.KVM, valid: true, }, { containerType: instance.NONE, valid: false, }, { containerType: instance.ContainerType("other"), valid: false, }} { conf := container.ManagerConfig{container.ConfigName: "test"} manager, err := factory.NewContainerManager(test.containerType, conf) if test.valid { c.Assert(err, gc.IsNil) c.Assert(manager, gc.NotNil) } else { c.Assert(err, gc.ErrorMatches, `unknown container type: ".*"`) c.Assert(manager, gc.IsNil) } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/factory/package_test.go����������������������0000644�0000153�0000161�00000000324�12321735642�027276� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package factory_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/testing/�������������������������������������0000755�0000153�0000161�00000000000�12321736000�024311� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/container/testing/common.go����������������������������0000644�0000153�0000161�00000002542�12321735776�026156� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "io/ioutil" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/container" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/instance" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) func CreateContainer(c *gc.C, manager container.Manager, machineId string) instance.Instance { stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, "fake-nonce", stateInfo, apiInfo) machineConfig.Tools = &tools.Tools{ Version: version.MustParseBinary("2.3.4-foo-bar"), URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", } series := "series" network := container.BridgeNetworkConfig("nic42") inst, hardware, err := manager.CreateContainer(machineConfig, series, network) c.Assert(err, gc.IsNil) c.Assert(hardware, gc.NotNil) c.Assert(hardware.String(), gc.Not(gc.Equals), "") return inst } func AssertCloudInit(c *gc.C, filename string) []byte { c.Assert(filename, jc.IsNonEmptyFile) data, err := ioutil.ReadFile(filename) c.Assert(err, gc.IsNil) c.Assert(string(data), jc.HasPrefix, "#cloud-config\n") return data } ��������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cert/��������������������������������������������������0000755�0000153�0000161�00000000000�12321735642�021622� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cert/cert_test.go��������������������������������������0000644�0000153�0000161�00000024777�12321735642�024166� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cert_test import ( "bytes" "crypto/rsa" "crypto/tls" "crypto/x509" "fmt" "io" "io/ioutil" "net" "strings" "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cert" ) func TestAll(t *testing.T) { gc.TestingT(t) } type certSuite struct{} var _ = gc.Suite(certSuite{}) func (certSuite) TestParseCertificate(c *gc.C) { xcert, err := cert.ParseCert(caCertPEM) c.Assert(err, gc.IsNil) c.Assert(xcert.Subject.CommonName, gc.Equals, "juju testing") xcert, err = cert.ParseCert(caKeyPEM) c.Check(xcert, gc.IsNil) c.Assert(err, gc.ErrorMatches, "no certificates found") xcert, err = cert.ParseCert([]byte("hello")) c.Check(xcert, gc.IsNil) c.Assert(err, gc.ErrorMatches, "no certificates found") } func (certSuite) TestParseCertAndKey(c *gc.C) { xcert, key, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) c.Assert(err, gc.IsNil) c.Assert(xcert.Subject.CommonName, gc.Equals, "juju testing") c.Assert(key, gc.NotNil) c.Assert(xcert.PublicKey.(*rsa.PublicKey), gc.DeepEquals, &key.PublicKey) } func (certSuite) TestNewCA(c *gc.C) { expiry := roundTime(time.Now().AddDate(0, 0, 1)) caCertPEM, caKeyPEM, err := cert.NewCA("foo", expiry) c.Assert(err, gc.IsNil) caCert, caKey, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) c.Assert(err, gc.IsNil) c.Assert(caKey, gc.FitsTypeOf, (*rsa.PrivateKey)(nil)) c.Assert(caCert.Subject.CommonName, gc.Equals, `juju-generated CA for environment "foo"`) c.Assert(caCert.NotAfter.Equal(expiry), gc.Equals, true) c.Assert(caCert.BasicConstraintsValid, gc.Equals, true) c.Assert(caCert.IsCA, gc.Equals, true) //c.Assert(caCert.MaxPathLen, Equals, 0) TODO it ends up as -1 - check that this is ok. } func (certSuite) TestNewServer(c *gc.C) { expiry := roundTime(time.Now().AddDate(1, 0, 0)) caCertPEM, caKeyPEM, err := cert.NewCA("foo", expiry) c.Assert(err, gc.IsNil) caCert, _, err := cert.ParseCertAndKey(caCertPEM, caKeyPEM) c.Assert(err, gc.IsNil) var noHostnames []string srvCertPEM, srvKeyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, noHostnames) c.Assert(err, gc.IsNil) srvCert, srvKey, err := cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) c.Assert(err, gc.IsNil) c.Assert(srvCert.Subject.CommonName, gc.Equals, "*") c.Assert(srvCert.NotAfter.Equal(expiry), gc.Equals, true) c.Assert(srvCert.BasicConstraintsValid, gc.Equals, false) c.Assert(srvCert.IsCA, gc.Equals, false) c.Assert(srvCert.ExtKeyUsage, gc.DeepEquals, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}) checkTLSConnection(c, caCert, srvCert, srvKey) } func (certSuite) TestNewServerHostnames(c *gc.C) { type test struct { hostnames []string expectedDNSNames []string expectedIPAddresses []net.IP } tests := []test{{ []string{}, nil, nil, }, { []string{"example.com"}, []string{"example.com"}, nil, }, { []string{"example.com", "127.0.0.1"}, []string{"example.com"}, []net.IP{net.IPv4(127, 0, 0, 1).To4()}, }, { []string{"::1"}, nil, []net.IP{net.IPv6loopback}, }} for i, t := range tests { c.Logf("test %d: %v", i, t.hostnames) expiry := roundTime(time.Now().AddDate(1, 0, 0)) srvCertPEM, srvKeyPEM, err := cert.NewServer(caCertPEM, caKeyPEM, expiry, t.hostnames) c.Assert(err, gc.IsNil) srvCert, _, err := cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) c.Assert(err, gc.IsNil) c.Assert(srvCert.DNSNames, gc.DeepEquals, t.expectedDNSNames) c.Assert(srvCert.IPAddresses, gc.DeepEquals, t.expectedIPAddresses) } } func (certSuite) TestWithNonUTCExpiry(c *gc.C) { expiry, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", "2012-11-28 15:53:57 +0100 CET") c.Assert(err, gc.IsNil) certPEM, keyPEM, err := cert.NewCA("foo", expiry) xcert, err := cert.ParseCert(certPEM) c.Assert(err, gc.IsNil) c.Assert(xcert.NotAfter.Equal(expiry), gc.Equals, true) var noHostnames []string certPEM, _, err = cert.NewServer(certPEM, keyPEM, expiry, noHostnames) xcert, err = cert.ParseCert(certPEM) c.Assert(err, gc.IsNil) c.Assert(xcert.NotAfter.Equal(expiry), gc.Equals, true) } func (certSuite) TestNewServerWithInvalidCert(c *gc.C) { var noHostnames []string srvCert, srvKey, err := cert.NewServer(nonCACert, nonCAKey, time.Now(), noHostnames) c.Check(srvCert, gc.IsNil) c.Check(srvKey, gc.IsNil) c.Assert(err, gc.ErrorMatches, "CA certificate is not a valid CA") } func (certSuite) TestVerify(c *gc.C) { now := time.Now() caCert, caKey, err := cert.NewCA("foo", now.Add(1*time.Minute)) c.Assert(err, gc.IsNil) var noHostnames []string srvCert, _, err := cert.NewServer(caCert, caKey, now.Add(3*time.Minute), noHostnames) c.Assert(err, gc.IsNil) err = cert.Verify(srvCert, caCert, now) c.Assert(err, gc.IsNil) err = cert.Verify(srvCert, caCert, now.Add(55*time.Second)) c.Assert(err, gc.IsNil) // TODO(rog) why does this succeed? // err = cert.Verify(srvCert, caCert, now.Add(-1 * time.Minute)) //c.Check(err, gc.ErrorMatches, "x509: certificate has expired or is not yet valid") err = cert.Verify(srvCert, caCert, now.Add(2*time.Minute)) c.Check(err, gc.ErrorMatches, "x509: certificate has expired or is not yet valid") caCert2, caKey2, err := cert.NewCA("bar", now.Add(1*time.Minute)) c.Assert(err, gc.IsNil) // Check original server certificate against wrong CA. err = cert.Verify(srvCert, caCert2, now) c.Check(err, gc.ErrorMatches, "x509: certificate signed by unknown authority") srvCert2, _, err := cert.NewServer(caCert2, caKey2, now.Add(1*time.Minute), noHostnames) c.Assert(err, gc.IsNil) // Check new server certificate against original CA. err = cert.Verify(srvCert2, caCert, now) c.Check(err, gc.ErrorMatches, "x509: certificate signed by unknown authority") } // checkTLSConnection checks that we can correctly perform a TLS // handshake using the given credentials. func checkTLSConnection(c *gc.C, caCert, srvCert *x509.Certificate, srvKey *rsa.PrivateKey) (caName string) { clientCertPool := x509.NewCertPool() clientCertPool.AddCert(caCert) var inBytes, outBytes bytes.Buffer const msg = "hello to the server" p0, p1 := net.Pipe() p0 = bufferedConn(p0, 3) p0 = recordingConn(p0, &inBytes, &outBytes) var clientState tls.ConnectionState done := make(chan error) go func() { clientConn := tls.Client(p0, &tls.Config{ ServerName: "anyServer", RootCAs: clientCertPool, }) defer clientConn.Close() _, err := clientConn.Write([]byte(msg)) if err != nil { done <- fmt.Errorf("client: %v", err) } clientState = clientConn.ConnectionState() done <- nil }() go func() { serverConn := tls.Server(p1, &tls.Config{ Certificates: []tls.Certificate{ newTLSCert(c, srvCert, srvKey), }, }) defer serverConn.Close() data, err := ioutil.ReadAll(serverConn) if err != nil { done <- fmt.Errorf("server: %v", err) return } if string(data) != msg { done <- fmt.Errorf("server: got %q; expected %q", data, msg) return } done <- nil }() for i := 0; i < 2; i++ { err := <-done c.Check(err, gc.IsNil) } outData := string(outBytes.Bytes()) c.Assert(outData, gc.Not(gc.HasLen), 0) if strings.Index(outData, msg) != -1 { c.Fatalf("TLS connection not encrypted") } c.Assert(clientState.VerifiedChains, gc.HasLen, 1) c.Assert(clientState.VerifiedChains[0], gc.HasLen, 2) return clientState.VerifiedChains[0][1].Subject.CommonName } func newTLSCert(c *gc.C, cert *x509.Certificate, key *rsa.PrivateKey) tls.Certificate { return tls.Certificate{ Certificate: [][]byte{cert.Raw}, PrivateKey: key, } } // bufferedConn adds buffering for at least // n writes to the given connection. func bufferedConn(c net.Conn, n int) net.Conn { for i := 0; i < n; i++ { p0, p1 := net.Pipe() go copyClose(p1, c) go copyClose(c, p1) c = p0 } return c } // recordingConn returns a connection which // records traffic in or out of the given connection. func recordingConn(c net.Conn, in, out io.Writer) net.Conn { p0, p1 := net.Pipe() go func() { io.Copy(io.MultiWriter(c, out), p1) c.Close() }() go func() { io.Copy(io.MultiWriter(p1, in), c) p1.Close() }() return p0 } func copyClose(w io.WriteCloser, r io.Reader) { io.Copy(w, r) w.Close() } // roundTime returns t rounded to the previous whole second. func roundTime(t time.Time) time.Time { return t.Add(time.Duration(-t.Nanosecond())) } var ( caCertPEM = []byte(` -----BEGIN CERTIFICATE----- MIIBnTCCAUmgAwIBAgIBADALBgkqhkiG9w0BAQUwJjENMAsGA1UEChMEanVqdTEV MBMGA1UEAxMManVqdSB0ZXN0aW5nMB4XDTEyMTExNDE0Mzg1NFoXDTIyMTExNDE0 NDM1NFowJjENMAsGA1UEChMEanVqdTEVMBMGA1UEAxMManVqdSB0ZXN0aW5nMFow CwYJKoZIhvcNAQEBA0sAMEgCQQCCOOpn9aWKcKr2GQGtygwD7PdfNe1I9BYiPAqa 2I33F5+6PqFdfujUKvoyTJI6XG4Qo/CECaaN9smhyq9DxzMhAgMBAAGjZjBkMA4G A1UdDwEB/wQEAwIABDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBQQDswP FQGeGMeTzPbHW62EZbbTJzAfBgNVHSMEGDAWgBQQDswPFQGeGMeTzPbHW62EZbbT JzALBgkqhkiG9w0BAQUDQQAqZzN0DqUyEfR8zIanozyD2pp10m9le+ODaKZDDNfH 8cB2x26F1iZ8ccq5IC2LtQf1IKJnpTcYlLuDvW6yB96g -----END CERTIFICATE----- `) caKeyPEM = []byte(` -----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBAII46mf1pYpwqvYZAa3KDAPs91817Uj0FiI8CprYjfcXn7o+oV1+ 6NQq+jJMkjpcbhCj8IQJpo32yaHKr0PHMyECAwEAAQJAYctedh4raLE+Ir0a3qnK pjQSfiUggtYTvTf7+tfAnZu946PX88ysr7XHPkXEGP4tWDTbl8BfGndrTKswVOx6 RQIhAOT5OzafJneDQ5cuGLN/hxIPBLWxKT1/25O6dhtBlRyPAiEAkZfFvCtBZyKB JFwDdp+7gE98mXtaFrjctLWeFx797U8CIAnnqiMTwWM8H2ljyhfBtYMXeTmu3zzU 0hfS4hcNwDiLAiEAkNXXU7YEPkFJD46ps1x7/s0UOutHV8tXZD44ou+l1GkCIQDO HOzuvYngJpoClGw0ipzJPoNZ2Z/GkdOWGByPeKu/8g== -----END RSA PRIVATE KEY----- `) nonCACert = []byte(`-----BEGIN CERTIFICATE----- MIIBmjCCAUagAwIBAgIBADALBgkqhkiG9w0BAQUwJjENMAsGA1UEChMEanVqdTEV MBMGA1UEAxMManVqdSB0ZXN0aW5nMB4XDTEyMTExNDE3MTU1NloXDTIyMTExNDE3 MjA1NlowJjENMAsGA1UEChMEanVqdTEVMBMGA1UEAxMManVqdSB0ZXN0aW5nMFow CwYJKoZIhvcNAQEBA0sAMEgCQQC96/CsTTY1Va8et6QYNXwrssAi36asFlV/fksG hqRucidiz/+xHvhs9EiqEu7NGxeVAkcfIhXu6/BDlobtj2v5AgMBAAGjYzBhMA4G A1UdDwEB/wQEAwIABDAPBgNVHRMBAf8EBTADAgEBMB0GA1UdDgQWBBRqbxkIW4R0 vmmkUoYuWg9sDob4jzAfBgNVHSMEGDAWgBRqbxkIW4R0vmmkUoYuWg9sDob4jzAL BgkqhkiG9w0BAQUDQQC3+KN8RppKdvlbP6fDwRC22PaCxd0PVyIHsn7I4jgpBPf8 Z3codMYYA5/f0AmUsD7wi7nnJVPPLZK7JWu4VI/w -----END CERTIFICATE----- `) nonCAKey = []byte(`-----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAL3r8KxNNjVVrx63pBg1fCuywCLfpqwWVX9+SwaGpG5yJ2LP/7Ee +Gz0SKoS7s0bF5UCRx8iFe7r8EOWhu2Pa/kCAwEAAQJAdzuAxStUNPeuEWLJKkmp wuVdqocuZCtBUeE/yMEOyibZ9NLKSuDJuDorkoeoiBz2vyUITHkLp4jgNmCI8NGg AQIhAPZG9+3OghlzcqWR4nTho8KO/CuO9bu5G4jNEdIrSJ6BAiEAxWtoLZNMwI4Q kj2moFk9GdBXZV9I0t1VTwcDvVyeAXkCIDrfvldQPdO9wJOKK3vLkS1qpyf2lhIZ b1alx3PZuxOBAiAthPltYMRWtar+fTaZTFo5RH+SQSkibaRI534mQF+ySQIhAIml yiWVLC2XrtwijDu1fwh/wtFCb/bPvqvgG5wgAO+2 -----END RSA PRIVATE KEY----- `) ) �juju-core_1.18.1/src/launchpad.net/juju-core/cert/cert.go�������������������������������������������0000644�0000153�0000161�00000013522�12321735642�023111� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cert import ( "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "errors" "fmt" "math/big" "net" "time" "github.com/errgo/errgo" ) var KeyBits = 1024 // ParseCert parses the given PEM-formatted X509 certificate. func ParseCert(certPEM []byte) (*x509.Certificate, error) { for len(certPEM) > 0 { var certBlock *pem.Block certBlock, certPEM = pem.Decode(certPEM) if certBlock == nil { break } if certBlock.Type == "CERTIFICATE" { cert, err := x509.ParseCertificate(certBlock.Bytes) return cert, err } } return nil, errors.New("no certificates found") } // ParseCert parses the given PEM-formatted X509 certificate // and RSA private key. func ParseCertAndKey(certPEM, keyPEM []byte) (*x509.Certificate, *rsa.PrivateKey, error) { tlsCert, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { return nil, nil, err } cert, err := x509.ParseCertificate(tlsCert.Certificate[0]) if err != nil { return nil, nil, err } key, ok := tlsCert.PrivateKey.(*rsa.PrivateKey) if !ok { return nil, nil, fmt.Errorf("private key with unexpected type %T", key) } return cert, key, nil } // Verify verifies that the given server certificate is valid with // respect to the given CA certificate at the given time. func Verify(srvCertPEM, caCertPEM []byte, when time.Time) error { caCert, err := ParseCert(caCertPEM) if err != nil { return errgo.Annotate(err, "cannot parse CA certificate") } srvCert, err := ParseCert(srvCertPEM) if err != nil { return errgo.Annotate(err, "cannot parse server certificate") } pool := x509.NewCertPool() pool.AddCert(caCert) opts := x509.VerifyOptions{ DNSName: "anyServer", Roots: pool, CurrentTime: when, } _, err = srvCert.Verify(opts) return err } // NewCA generates a CA certificate/key pair suitable for signing server // keys for an environment with the given name. func NewCA(envName string, expiry time.Time) (certPEM, keyPEM []byte, err error) { key, err := rsa.GenerateKey(rand.Reader, KeyBits) if err != nil { return nil, nil, err } now := time.Now() template := &x509.Certificate{ SerialNumber: new(big.Int), Subject: pkix.Name{ CommonName: fmt.Sprintf("juju-generated CA for environment %q", envName), Organization: []string{"juju"}, }, NotBefore: now.UTC().Add(-5 * time.Minute), NotAfter: expiry.UTC(), SubjectKeyId: bigIntHash(key.N), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, IsCA: true, MaxPathLen: 0, // Disallow delegation for now. BasicConstraintsValid: true, } certDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) if err != nil { return nil, nil, fmt.Errorf("canot create certificate: %v", err) } certPEM = pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: certDER, }) keyPEM = pem.EncodeToMemory(&pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), }) return certPEM, keyPEM, nil } // NewServer generates a certificate/key pair suitable for use by a server. func NewServer(caCertPEM, caKeyPEM []byte, expiry time.Time, hostnames []string) (certPEM, keyPEM []byte, err error) { return newLeaf(caCertPEM, caKeyPEM, expiry, hostnames, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}) } // NewClient generates a certificate/key pair suitable for client authentication. func NewClient(caCertPEM, caKeyPEM []byte, expiry time.Time) (certPEM, keyPEM []byte, err error) { return newLeaf(caCertPEM, caKeyPEM, expiry, nil, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}) } // newLeaf generates a certificate/key pair suitable for use by a leaf node. func newLeaf(caCertPEM, caKeyPEM []byte, expiry time.Time, hostnames []string, extKeyUsage []x509.ExtKeyUsage) (certPEM, keyPEM []byte, err error) { tlsCert, err := tls.X509KeyPair(caCertPEM, caKeyPEM) if err != nil { return nil, nil, err } if len(tlsCert.Certificate) != 1 { return nil, nil, fmt.Errorf("more than one certificate for CA") } caCert, err := x509.ParseCertificate(tlsCert.Certificate[0]) if err != nil { return nil, nil, err } if !caCert.BasicConstraintsValid || !caCert.IsCA { return nil, nil, fmt.Errorf("CA certificate is not a valid CA") } caKey, ok := tlsCert.PrivateKey.(*rsa.PrivateKey) if !ok { return nil, nil, fmt.Errorf("CA private key has unexpected type %T", tlsCert.PrivateKey) } key, err := rsa.GenerateKey(rand.Reader, KeyBits) if err != nil { return nil, nil, fmt.Errorf("cannot generate key: %v", err) } now := time.Now() template := &x509.Certificate{ SerialNumber: new(big.Int), Subject: pkix.Name{ // This won't match host names with dots. The hostname // is hardcoded when connecting to avoid the issue. CommonName: "*", Organization: []string{"juju"}, }, NotBefore: now.UTC().Add(-5 * time.Minute), NotAfter: expiry.UTC(), SubjectKeyId: bigIntHash(key.N), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement, ExtKeyUsage: extKeyUsage, } for _, hostname := range hostnames { if ip := net.ParseIP(hostname); ip != nil { template.IPAddresses = append(template.IPAddresses, ip) } else { template.DNSNames = append(template.DNSNames, hostname) } } certDER, err := x509.CreateCertificate(rand.Reader, template, caCert, &key.PublicKey, caKey) if err != nil { return nil, nil, err } certPEM = pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: certDER, }) keyPEM = pem.EncodeToMemory(&pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), }) return certPEM, keyPEM, nil } func bigIntHash(n *big.Int) []byte { h := sha1.New() h.Write(n.Bytes()) return h.Sum(nil) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/.lbox��������������������������������������������������0000644�0000153�0000161�00000000036�12321735642�021631� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������propose -cr -for lp:juju-core ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/LICENCE������������������������������������������������0000644�0000153�0000161�00000103330�12321735642�021652� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see <http://www.gnu.org/licenses/>. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/README�������������������������������������������������0000644�0000153�0000161�00000011443�12321735642�021550� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core ========= juju is devops distilled. Getting started =============== `juju-core` is written in Go (http://golang.org), a modern, compiled, statically typed, concurrent language. This document describes how to build `juju-core` from source. If you are looking for binary releases of `juju-core`, they are available from the Juju stable PPA, `https://launchpad.net/~juju/+archive/stable`, and can be installed with: sudo apt-add-repository ppa:juju/stable sudo apt-get update sudo apt-get install juju Installing prerequisites ------------------------ You can use `make install-dependencies` or, if you prefer to install them manually, check the Makefile target. This will add some PPAs to ensure that you can install the required golang and mongodb-server versions for precise onwards, in addition to the other dependencies. Setting GOPATH -------------- When working with the source of Go programs, you should define a path within your home directory (or other workspace) which will be your `GOPATH`. `GOPATH` is similar to Java's `CLASSPATH` or Python's `~/.local`. `GOPATH` is documented online at `http://golang.org/pkg/go/build/` and inside the `go` tool itself go help gopath Various conventions exist for naming the location of your `GOPATH`, but it should exist, and be writable by you. For example export GOPATH=${HOME}/work mkdir $GOPATH will define and create `$HOME/work` as your local `GOPATH`. The `go` tool itself will create three subdirectories inside your `GOPATH` when required; `src`, `pkg` and `bin`, which hold the source of Go programs, compiled packages and compiled binaries, respectively. Setting `GOPATH` correctly is critical when developing Go programs. Set and export it as part of your login script. Add `$GOPATH/bin` to your `PATH`, so you can run the go programs you install: PATH="$PATH:$GOPATH/bin" Getting juju-core ================= The easiest way to get the source for `juju-core` is to use the `go get` command. go get -v launchpad.net/juju-core/... This command will checkout the source of `juju-core` and inspect it for any unmet Go package dependencies, downloading those as well. `go get` will also build and install `juju-core` and its dependencies. To checkout without installing, use the `-d` flag. More details on the `go get` flags are available using go help get At this point you will have the bzr working copy of the `juju-core` source at `$GOPATH/launchpad.net/juju-core`. The source for any dependent packages will also be available inside `$GOPATH`. You can use `bzr pull`, or the less convenient `go get -u launchpad.net/juju-core/...` to update the source from time to time. If you want to know more about contributing to `juju-core`, please read the `CONTRIBUTING` companion to this file. Building juju-core ================== go install -v launchpad.net/juju-core/... Will build juju and install the binary commands into `$GOPATH/bin`. It is likely if you have just completed the previous step to get the `juju-core` source, the install process will produce no output, as the final executables are up-to-date. Using juju-core =============== After following the steps above you will have the `juju` client installed in `GOPATH/bin/juju`. You should ensure that this version of `juju` appears earlier in your path than any packaged versions of `juju-core`, or older Python juju commands. You can verify this using which juju You should be able to bootstrap a local environment now with the following (Note: the use of sudo for bootstrap here is only required for the local provider because it uses LXC, which requires root privileges) juju init juju switch local sudo juju bootstrap --upload-tools -------------- The `juju` client program, and the juju 'tools' are deployed in lockstep. When a release of `juju-core` is made, the compiled tools matching that version of juju are extracted and uploaded to a known location. This consumes a release version number, and implies that no tools are available for the next, development, version of juju. Therefore, when using the development version of juju you will need to pass an additional flag, `--upload-tools` to instruct the `juju` client to build a set of tools from source and upload them to the environment as part of the bootstrap process. juju bootstrap -e your-environment --upload-tools {--debug} Installing bash completion for juju =================================== make install-etc Will install Bash completion for `juju` cli to `/etc/bash_completion.d/juju-core` It does dynamic completion for commands requiring service, unit or machine names (like e.g. juju status <service>, juju ssh <instance>, juju terminate-machine <machine#>, etc), by parsing cached `juju status` output for speedup. It also does command flags completion by parsing `juju help ...` output. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/dependencies_test.go�����������������������������������0000644�0000153�0000161�00000001523�12321735642�024702� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package juju_test import ( "go/build" "io/ioutil" "path/filepath" "strings" "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } type dependenciesTest struct{} var _ = gc.Suite(&dependenciesTest{}) func projectRoot(c *gc.C) string { p, err := build.Import("launchpad.net/juju-core", "", build.FindOnly) c.Assert(err, gc.IsNil) return p.Dir } func (*dependenciesTest) TestDependenciesTsvFormat(c *gc.C) { filename := filepath.Join(projectRoot(c), "dependencies.tsv") content, err := ioutil.ReadFile(filename) c.Assert(err, gc.IsNil) for _, line := range strings.Split(string(content), "\n") { if line == "" { continue } segments := strings.Split(line, "\t") c.Assert(segments, gc.HasLen, 4) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/log/���������������������������������������������������0000755�0000153�0000161�00000000000�12321735642�021446� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/log/log.go���������������������������������������������0000644�0000153�0000161�00000002406�12321735642�022560� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package log import ( "fmt" "github.com/juju/loggo" ) var ( logger = loggo.GetLogger("juju") ) // Errorf logs a message using the ERROR priority. func Errorf(format string, a ...interface{}) error { logger.Logf(loggo.ERROR, format, a...) return nil } // Warningf logs a message using the WARNING priority. func Warningf(format string, a ...interface{}) error { logger.Logf(loggo.WARNING, format, a...) return nil } // Noticef logs a message using the NOTICE priority. // Notice doesn't really convert to the loggo priorities... func Noticef(format string, a ...interface{}) error { logger.Logf(loggo.INFO, format, a...) return nil } // Infof logs a message using the INFO priority. func Infof(format string, a ...interface{}) error { logger.Logf(loggo.INFO, format, a...) return nil } // Debugf logs a message using the DEBUG priority. func Debugf(format string, a ...interface{}) (err error) { logger.Logf(loggo.DEBUG, format, a...) return nil } // Log the error and return an error with the same text. func LoggedErrorf(logger loggo.Logger, format string, a ...interface{}) error { logger.Logf(loggo.ERROR, format, a...) return fmt.Errorf(format, a...) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/log/log_test.go����������������������������������������0000644�0000153�0000161�00000003612�12321735642�023617� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package log_test import ( "bytes" "fmt" "testing" "time" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/log" ) func Test(t *testing.T) { gc.TestingT(t) } type testWriter struct { bytes.Buffer } type suite struct { writer *testWriter oldWriter loggo.Writer oldLevel loggo.Level } var _ = gc.Suite(&suite{}) func (t *testWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) { t.Buffer.WriteString(fmt.Sprintf("%s %s %s", level, module, message)) } func (s *suite) SetUpTest(c *gc.C) { var err error s.writer = &testWriter{} s.oldWriter, s.oldLevel, err = loggo.RemoveWriter("default") c.Assert(err, gc.IsNil) err = loggo.RegisterWriter("test", s.writer, loggo.TRACE) c.Assert(err, gc.IsNil) logger := loggo.GetLogger("juju") logger.SetLogLevel(loggo.TRACE) } func (s *suite) TearDownTest(c *gc.C) { _, _, err := loggo.RemoveWriter("test") c.Assert(err, gc.IsNil) err = loggo.RegisterWriter("default", s.oldWriter, s.oldLevel) c.Assert(err, gc.IsNil) } func (s *suite) TestLoggerDebug(c *gc.C) { input := "Hello World" log.Debugf(input) c.Assert(s.writer.String(), gc.Equals, "DEBUG juju "+input) } func (s *suite) TestInfoLogger(c *gc.C) { input := "Hello World" log.Infof(input) c.Assert(s.writer.String(), gc.Equals, "INFO juju "+input) } func (s *suite) TestErrorLogger(c *gc.C) { input := "Hello World" log.Errorf(input) c.Assert(s.writer.String(), gc.Equals, "ERROR juju "+input) } func (s *suite) TestWarningLogger(c *gc.C) { input := "Hello World" log.Warningf(input) c.Assert(s.writer.String(), gc.Equals, "WARNING juju "+input) } func (s *suite) TestNoticeLogger(c *gc.C) { input := "Hello World" log.Noticef(input) c.Assert(s.writer.String(), gc.Equals, "INFO juju "+input) } ����������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/log/syslog/��������������������������������������������0000755�0000153�0000161�00000000000�12321735642�022766� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/log/syslog/config_test.go������������������������������0000644�0000153�0000161�00000007272�12321735642�025631� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package syslog_test import ( "io/ioutil" "path/filepath" stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/log/syslog" syslogtesting "launchpad.net/juju-core/log/syslog/testing" ) func Test(t *stdtesting.T) { gc.TestingT(t) } type SyslogConfigSuite struct { configDir string } var _ = gc.Suite(&SyslogConfigSuite{}) func (s *SyslogConfigSuite) SetUpTest(c *gc.C) { s.configDir = c.MkDir() } func (s *SyslogConfigSuite) assertRsyslogConfigPath(c *gc.C, slConfig *syslog.SyslogConfig) { slConfig.ConfigDir = s.configDir slConfig.ConfigFileName = "rsyslog.conf" c.Assert(slConfig.ConfigFilePath(), gc.Equals, filepath.Join(s.configDir, "rsyslog.conf")) } func (s *SyslogConfigSuite) assertRsyslogConfigContents(c *gc.C, slConfig *syslog.SyslogConfig, expectedConf string) { data, err := slConfig.Render() c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, expectedConf) } func (s *SyslogConfigSuite) TestAccumulateConfigRender(c *gc.C) { syslogConfigRenderer := syslog.NewAccumulateConfig("some-machine", agent.DefaultLogDir, 8888, "") s.assertRsyslogConfigContents( c, syslogConfigRenderer, syslogtesting.ExpectedAccumulateSyslogConf(c, "some-machine", "", 8888), ) } func (s *SyslogConfigSuite) TestAccumulateConfigWrite(c *gc.C) { syslogConfigRenderer := syslog.NewAccumulateConfig("some-machine", agent.DefaultLogDir, 8888, "") syslogConfigRenderer.ConfigDir = s.configDir syslogConfigRenderer.ConfigFileName = "rsyslog.conf" s.assertRsyslogConfigPath(c, syslogConfigRenderer) err := syslogConfigRenderer.Write() c.Assert(err, gc.IsNil) syslogConfData, err := ioutil.ReadFile(syslogConfigRenderer.ConfigFilePath()) c.Assert(err, gc.IsNil) c.Assert( string(syslogConfData), gc.Equals, syslogtesting.ExpectedAccumulateSyslogConf(c, "some-machine", "", 8888), ) } func (s *SyslogConfigSuite) TestAccumulateConfigRenderWithNamespace(c *gc.C) { syslogConfigRenderer := syslog.NewAccumulateConfig("some-machine", agent.DefaultLogDir, 8888, "namespace") syslogConfigRenderer.LogDir += "-namespace" s.assertRsyslogConfigContents( c, syslogConfigRenderer, syslogtesting.ExpectedAccumulateSyslogConf( c, "some-machine", "namespace", 8888, ), ) } func (s *SyslogConfigSuite) TestForwardConfigRender(c *gc.C) { syslogConfigRenderer := syslog.NewForwardConfig( "some-machine", agent.DefaultLogDir, 999, "", []string{"server"}, ) s.assertRsyslogConfigContents( c, syslogConfigRenderer, syslogtesting.ExpectedForwardSyslogConf( c, "some-machine", agent.DefaultLogDir, "", "server", 999, ), ) } func (s *SyslogConfigSuite) TestForwardConfigRenderWithNamespace(c *gc.C) { syslogConfigRenderer := syslog.NewForwardConfig( "some-machine", agent.DefaultLogDir, 999, "namespace", []string{"server"}, ) s.assertRsyslogConfigContents( c, syslogConfigRenderer, syslogtesting.ExpectedForwardSyslogConf( c, "some-machine", agent.DefaultLogDir, "namespace", "server", 999, ), ) } func (s *SyslogConfigSuite) TestForwardConfigWrite(c *gc.C) { syslogConfigRenderer := syslog.NewForwardConfig( "some-machine", agent.DefaultLogDir, 999, "", []string{"server"}, ) syslogConfigRenderer.ConfigDir = s.configDir syslogConfigRenderer.ConfigFileName = "rsyslog.conf" s.assertRsyslogConfigPath(c, syslogConfigRenderer) err := syslogConfigRenderer.Write() c.Assert(err, gc.IsNil) syslogConfData, err := ioutil.ReadFile(syslogConfigRenderer.ConfigFilePath()) c.Assert(err, gc.IsNil) c.Assert( string(syslogConfData), gc.Equals, syslogtesting.ExpectedForwardSyslogConf( c, "some-machine", agent.DefaultLogDir, "", "server", 999, ), ) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/log/syslog/config.go�����������������������������������0000644�0000153�0000161�00000020164�12321735642�024565� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package syslog import ( "bytes" "fmt" "io/ioutil" "path/filepath" "strings" "text/template" ) // tagOffset represents the substring start value for the tag to return // the logfileName value from the syslogtag. Substrings in syslog are // indexed from 1, hence the + 1. const tagOffset = len("juju-") + 1 // The rsyslog conf for state server nodes. // Messages are gathered from other nodes and accumulated in an all-machines.log file. // // The apparmor profile is quite strict about where rsyslog can write files. // Instead of poking with the profile, the local provider now logs to // {{logDir}}-{{user}}-{{env name}}/all-machines.log, and a symlink is made // in the local provider log dir to point to that file. The file is also // created with 0644 so the user can read it without poking permissions. By // default rsyslog creates files with 0644, but in the ubuntu package, the // setting is changed to 0640, which means normal users can't read the log // file. Using a new action directive (new as in not-legacy), we can specify // the file create mode so it doesn't use the default. // // I would dearly love to write the filtering action as follows to avoid setting // and resetting the global $FileCreateMode, but alas, precise doesn't support it // // if $syslogtag startswith "juju{{namespace}}-" then // action(type="omfile" // File="{{logDir}}{{namespace}}/all-machines.log" // Template="JujuLogFormat{{namespace}}" // FileCreateMode="0644") // & stop // // Instead we need to mess with the global FileCreateMode. We set it back // to the ubuntu default after defining our rule. const stateServerRsyslogTemplate = ` $ModLoad imfile $InputFilePersistStateInterval 50 $InputFilePollInterval 5 $InputFileName {{logfilePath}} $InputFileTag juju{{namespace}}-{{logfileName}}: $InputFileStateFile {{logfileName}}{{namespace}} $InputRunFileMonitor $ModLoad imtcp $DefaultNetstreamDriver gtls $DefaultNetstreamDriverCAFile {{tlsCACertPath}} $DefaultNetstreamDriverCertFile {{tlsCertPath}} $DefaultNetstreamDriverKeyFile {{tlsKeyPath}} $InputTCPServerStreamDriverAuthMode anon $InputTCPServerStreamDriverMode 1 # run driver in TLS-only mode $InputTCPServerRun {{portNumber}} # Messages received from remote rsyslog machines have messages prefixed with a space, # so add one in for local messages too if needed. $template JujuLogFormat{{namespace}},"%syslogtag:{{tagStart}}:$%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n" $FileCreateMode 0644 :syslogtag, startswith, "juju{{namespace}}-" {{logDir}}/all-machines.log;JujuLogFormat{{namespace}} & ~ $FileCreateMode 0640 ` // The rsyslog conf for non-state server nodes. // Messages are forwarded to the state server node. const nodeRsyslogTemplate = ` $ModLoad imfile # Enable reliable forwarding. $ActionQueueType LinkedList $ActionQueueFileName {{logfileName}}{{namespace}} $ActionResumeRetryCount -1 $ActionQueueSaveOnShutdown on $InputFilePersistStateInterval 50 $InputFilePollInterval 5 $InputFileName {{logfilePath}} $InputFileTag juju{{namespace}}-{{logfileName}}: $InputFileStateFile {{logfileName}}{{namespace}} $InputRunFileMonitor $DefaultNetstreamDriver gtls $DefaultNetstreamDriverCAFile {{tlsCACertPath}} $ActionSendStreamDriverAuthMode anon $ActionSendStreamDriverMode 1 # run driver in TLS-only mode $template LongTagForwardFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%" :syslogtag, startswith, "juju{{namespace}}-" @@{{bootstrapIP}}:{{portNumber}};LongTagForwardFormat & ~ ` // nodeRsyslogTemplateTLSHeader is prepended to // nodeRsyslogTemplate if TLS is to be used. const nodeRsyslogTemplateTLSHeader = ` ` const ( defaultConfigDir = "/etc/rsyslog.d" defaultCACertFileName = "ca-cert.pem" defaultServerCertFileName = "rsyslog-cert.pem" defaultServerKeyFileName = "rsyslog-key.pem" ) // SyslogConfigRenderer instances are used to generate a rsyslog conf file. type SyslogConfigRenderer interface { Render() ([]byte, error) } // SyslogConfig provides a means to configure and generate rsyslog conf files for // the state server nodes and unit nodes. // rsyslog is configured to tail the specified log file. type SyslogConfig struct { // the template representing the config file contents. configTemplate string // the directory where the config file is written. ConfigDir string // the config file name. ConfigFileName string // the name of the log file to tail. LogFileName string // the addresses of the state server to which messages should be forwarded. StateServerAddresses []string // CA certificate file name. CACertFileName string // Server certificate file name. ServerCertFileName string // Server private key file name. ServerKeyFileName string // the port number for the listener Port int // the directory for the logfiles LogDir string // namespace is used when there are multiple environments on one machine Namespace string } // NewForwardConfig creates a SyslogConfig instance used on unit nodes to forward log entries // to the state server nodes. func NewForwardConfig(logFile, logDir string, port int, namespace string, stateServerAddresses []string) *SyslogConfig { conf := &SyslogConfig{ configTemplate: nodeRsyslogTemplate, StateServerAddresses: stateServerAddresses, LogFileName: logFile, Port: port, LogDir: logDir, } if namespace != "" { conf.Namespace = "-" + namespace } return conf } // NewAccumulateConfig creates a SyslogConfig instance used to accumulate log entries from the // various unit nodes. func NewAccumulateConfig(logFile, logDir string, port int, namespace string) *SyslogConfig { conf := &SyslogConfig{ configTemplate: stateServerRsyslogTemplate, LogFileName: logFile, Port: port, LogDir: logDir, } if namespace != "" { conf.Namespace = "-" + namespace } return conf } func either(a, b string) string { if a != "" { return a } return b } func (slConfig *SyslogConfig) ConfigFilePath() string { dir := either(slConfig.ConfigDir, defaultConfigDir) return filepath.Join(dir, slConfig.ConfigFileName) } func (slConfig *SyslogConfig) CACertPath() string { filename := either(slConfig.CACertFileName, defaultCACertFileName) return filepath.Join(slConfig.LogDir, filename) } func (slConfig *SyslogConfig) ServerCertPath() string { filename := either(slConfig.ServerCertFileName, defaultServerCertFileName) return filepath.Join(slConfig.LogDir, filename) } func (slConfig *SyslogConfig) ServerKeyPath() string { filename := either(slConfig.ServerCertFileName, defaultServerKeyFileName) return filepath.Join(slConfig.LogDir, filename) } // Render generates the rsyslog config. func (slConfig *SyslogConfig) Render() ([]byte, error) { // TODO: for HA, we will want to send to all state server addresses (maybe). var bootstrapIP = func() string { addr := slConfig.StateServerAddresses[0] parts := strings.Split(addr, ":") return parts[0] } var logFilePath = func() string { return fmt.Sprintf("%s/%s.log", slConfig.LogDir, slConfig.LogFileName) } t := template.New("") t.Funcs(template.FuncMap{ "logfileName": func() string { return slConfig.LogFileName }, "bootstrapIP": bootstrapIP, "logfilePath": logFilePath, "portNumber": func() int { return slConfig.Port }, "logDir": func() string { return slConfig.LogDir }, "namespace": func() string { return slConfig.Namespace }, "tagStart": func() int { return tagOffset + len(slConfig.Namespace) }, "tlsCACertPath": slConfig.CACertPath, "tlsCertPath": slConfig.ServerCertPath, "tlsKeyPath": slConfig.ServerKeyPath, }) // Process the rsyslog config template and echo to the conf file. p, err := t.Parse(slConfig.configTemplate) if err != nil { return nil, err } var confBuf bytes.Buffer if err := p.Execute(&confBuf, nil); err != nil { return nil, err } return confBuf.Bytes(), nil } // Write generates and writes the rsyslog config. func (slConfig *SyslogConfig) Write() error { data, err := slConfig.Render() if err != nil { return err } err = ioutil.WriteFile(slConfig.ConfigFilePath(), data, 0644) return err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/log/syslog/cmd.go��������������������������������������0000644�0000153�0000161�00000001100�12321735642�024050� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package syslog import ( "bytes" "fmt" "os" "os/exec" ) func Restart() error { if os.Geteuid() == 0 { return runCommand("restart", "rsyslog") } return fmt.Errorf("must be root") } func runCommand(args ...string) error { out, err := exec.Command(args[0], args[1:]...).CombinedOutput() if err == nil { return nil } out = bytes.TrimSpace(out) if len(out) > 0 { return fmt.Errorf("exec %q: %v (%s)", args, err, out) } return fmt.Errorf("exec %q: %v", args, err) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/log/syslog/testing/������������������������������������0000755�0000153�0000161�00000000000�12321735643�024444� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/log/syslog/testing/syslogconf.go�����������������������0000644�0000153�0000161�00000006566�12321735642�027175� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "bytes" "text/template" gc "launchpad.net/gocheck" ) var expectedAccumulateSyslogConfTemplate = ` $ModLoad imfile $InputFilePersistStateInterval 50 $InputFilePollInterval 5 $InputFileName /var/log/juju{{.Namespace}}/{{.MachineTag}}.log $InputFileTag juju{{.Namespace}}-{{.MachineTag}}: $InputFileStateFile {{.MachineTag}}{{.Namespace}} $InputRunFileMonitor $ModLoad imtcp $DefaultNetstreamDriver gtls $DefaultNetstreamDriverCAFile /var/log/juju{{.Namespace}}/ca-cert.pem $DefaultNetstreamDriverCertFile /var/log/juju{{.Namespace}}/rsyslog-cert.pem $DefaultNetstreamDriverKeyFile /var/log/juju{{.Namespace}}/rsyslog-key.pem $InputTCPServerStreamDriverAuthMode anon $InputTCPServerStreamDriverMode 1 # run driver in TLS-only mode $InputTCPServerRun {{.Port}} # Messages received from remote rsyslog machines have messages prefixed with a space, # so add one in for local messages too if needed. $template JujuLogFormat{{.Namespace}},"%syslogtag:{{.Offset}}:$%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n" $FileCreateMode 0644 :syslogtag, startswith, "juju{{.Namespace}}-" /var/log/juju{{.Namespace}}/all-machines.log;JujuLogFormat{{.Namespace}} & ~ $FileCreateMode 0640 ` type templateArgs struct { MachineTag string LogDir string Namespace string BootstrapIP string Port int Offset int } // ExpectedAccumulateSyslogConf returns the expected content for a rsyslog file on a state server. func ExpectedAccumulateSyslogConf(c *gc.C, machineTag, namespace string, port int) string { if namespace != "" { namespace = "-" + namespace } t := template.Must(template.New("").Parse(expectedAccumulateSyslogConfTemplate)) var conf bytes.Buffer err := t.Execute(&conf, templateArgs{ MachineTag: machineTag, Namespace: namespace, Offset: len("juju-") + len(namespace) + 1, Port: port, }) c.Assert(err, gc.IsNil) return conf.String() } var expectedForwardSyslogConfTemplate = ` $ModLoad imfile # Enable reliable forwarding. $ActionQueueType LinkedList $ActionQueueFileName {{.MachineTag}}{{.Namespace}} $ActionResumeRetryCount -1 $ActionQueueSaveOnShutdown on $InputFilePersistStateInterval 50 $InputFilePollInterval 5 $InputFileName {{.LogDir}}/{{.MachineTag}}.log $InputFileTag juju{{.Namespace}}-{{.MachineTag}}: $InputFileStateFile {{.MachineTag}}{{.Namespace}} $InputRunFileMonitor $DefaultNetstreamDriver gtls $DefaultNetstreamDriverCAFile {{.LogDir}}/ca-cert.pem $ActionSendStreamDriverAuthMode anon $ActionSendStreamDriverMode 1 # run driver in TLS-only mode $template LongTagForwardFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg%" :syslogtag, startswith, "juju{{.Namespace}}-" @@{{.BootstrapIP}}:{{.Port}};LongTagForwardFormat & ~ ` // ExpectedForwardSyslogConf returns the expected content for a rsyslog file on a host machine. func ExpectedForwardSyslogConf(c *gc.C, machineTag, logDir, namespace, bootstrapIP string, port int) string { if namespace != "" { namespace = "-" + namespace } t := template.Must(template.New("").Parse(expectedForwardSyslogConfTemplate)) var conf bytes.Buffer err := t.Execute(&conf, templateArgs{ MachineTag: machineTag, LogDir: logDir, Namespace: namespace, BootstrapIP: bootstrapIP, Port: port, }) c.Assert(err, gc.IsNil) return conf.String() } ������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/Makefile�����������������������������������������������0000644�0000153�0000161�00000004162�12321735776�022340� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # Makefile for juju-core. # ifndef GOPATH $(warning You need to set up a GOPATH. See the README file.) endif PROJECT := launchpad.net/juju-core PROJECT_DIR := $(shell go list -e -f '{{.Dir}}' $(PROJECT)) ifeq ($(shell uname -p | sed -r 's/.*(x86|armel|armhf).*/golang/'), golang) GO_C := golang INSTALL_FLAGS := else GO_C := gccgo-4.9 gccgo-go INSTALL_FLAGS := -gccgoflags=-static-libgo endif define DEPENDENCIES build-essential bzr distro-info-data git-core mercurial rsyslog-gnutls zip $(GO_C) endef default: build # Start of GOPATH-dependent targets. Some targets only make sense - # and will only work - when this tree is found on the GOPATH. ifeq ($(CURDIR),$(PROJECT_DIR)) build: go build $(PROJECT)/... check: go test $(PROJECT)/... install: go install $(INSTALL_FLAGS) -v $(PROJECT)/... clean: go clean $(PROJECT)/... else # -------------------------------- build: $(error Cannot $@; $(CURDIR) is not on GOPATH) check: $(error Cannot $@; $(CURDIR) is not on GOPATH) install: $(error Cannot $@; $(CURDIR) is not on GOPATH) clean: $(error Cannot $@; $(CURDIR) is not on GOPATH) endif # End of GOPATH-dependent targets. # Reformat source files. format: gofmt -w -l . # Reformat and simplify source files. simplify: gofmt -w -l -s . # Install packages required to develop Juju and run tests. The stable # PPA includes the required mongodb-server binaries. However, neither # PPA works on Saucy just yet. install-dependencies: ifeq ($(shell lsb_release -cs|sed -r 's/precise|quantal|raring/old/'),old) @echo Adding juju PPAs for golang and mongodb-server @sudo apt-add-repository --yes ppa:juju/golang @sudo apt-add-repository --yes ppa:juju/stable @sudo apt-get update endif @echo Installing dependencies @sudo apt-get --yes install $(strip $(DEPENDENCIES)) \ $(shell apt-cache madison juju-mongodb mongodb-server | head -1 | cut -d '|' -f1) # Install bash_completion install-etc: @echo Installing bash completion @sudo install -o root -g root -m 644 etc/bash_completion.d/juju-core /etc/bash_completion.d .PHONY: build check install .PHONY: clean format simplify .PHONY: install-dependencies ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/����������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022464� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/deprecatedenvsettings.go����������������������0000644�0000153�0000161�00000000753�12321735642�027425� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades func processDeprecatedEnvSettings(context Context) error { st := context.State() removeAttrs := []string{ "public-bucket", "public-bucket-region", "public-bucket-url", "default-image-id", "default-instance-type", "shared-storage-port", } // TODO (wallyworld) - delete tools-url in 1.20 return st.UpdateEnvironConfig(map[string]interface{}{}, removeAttrs, nil) } ���������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/rsyslogport.go��������������������������������0000644�0000153�0000161�00000000560�12321735642�025436� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades import ( "launchpad.net/juju-core/environs/config" ) func updateRsyslogPort(context Context) error { st := context.State() attrs := map[string]interface{}{ "syslog-port": config.DefaultSyslogPort, } return st.UpdateEnvironConfig(attrs, nil, nil) } ������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/export_test.go��������������������������������0000644�0000153�0000161�00000001500�12321735642�025402� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades var ( UpgradeOperations = &upgradeOperations UbuntuHome = &ubuntuHome RootLogDir = &rootLogDir RootSpoolDir = &rootSpoolDir ChownPath = &chownPath IsLocalEnviron = &isLocalEnviron // 118 upgrade functions StepsFor118 = stepsFor118 EnsureLockDirExistsAndUbuntuWritable = ensureLockDirExistsAndUbuntuWritable EnsureSystemSSHKey = ensureSystemSSHKey EnsureUbuntuDotProfileSourcesProxyFile = ensureUbuntuDotProfileSourcesProxyFile UpdateRsyslogPort = updateRsyslogPort ProcessDeprecatedEnvSettings = processDeprecatedEnvSettings MigrateLocalProviderAgentConfig = migrateLocalProviderAgentConfig ) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/rsysloggnutls.go������������������������������0000644�0000153�0000161�00000000575�12321735642�025774� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades import ( "launchpad.net/juju-core/utils" ) // installRsyslogGnutls installs the rsyslog-gnutls package, // which is required for our rsyslog configuration from 1.18.0. func installRsyslogGnutls(context Context) error { return utils.AptGetInstall("rsyslog-gnutls") } �����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/dotprofile_test.go����������������������������0000644�0000153�0000161�00000004261�12321735642�026237� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades_test import ( "io/ioutil" "path" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/upgrades" ) type ensureDotProfileSuite struct { testing.FakeHomeSuite home string ctx upgrades.Context } var _ = gc.Suite(&ensureDotProfileSuite{}) func (s *ensureDotProfileSuite) SetUpTest(c *gc.C) { s.FakeHomeSuite.SetUpTest(c) loggo.GetLogger("juju.upgrade").SetLogLevel(loggo.TRACE) s.home = c.MkDir() s.PatchValue(upgrades.UbuntuHome, s.home) s.ctx = &mockContext{} } const expectedLine = ` # Added by juju [ -f "$HOME/.juju-proxy" ] && . "$HOME/.juju-proxy" ` func (s *ensureDotProfileSuite) writeDotProfile(c *gc.C, content string) { dotProfile := path.Join(s.home, ".profile") err := ioutil.WriteFile(dotProfile, []byte(content), 0644) c.Assert(err, gc.IsNil) } func (s *ensureDotProfileSuite) assertProfile(c *gc.C, content string) { dotProfile := path.Join(s.home, ".profile") data, err := ioutil.ReadFile(dotProfile) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, content) } func (s *ensureDotProfileSuite) TestSourceAdded(c *gc.C) { s.writeDotProfile(c, "") err := upgrades.EnsureUbuntuDotProfileSourcesProxyFile(s.ctx) c.Assert(err, gc.IsNil) s.assertProfile(c, expectedLine) } func (s *ensureDotProfileSuite) TestIdempotent(c *gc.C) { s.writeDotProfile(c, "") err := upgrades.EnsureUbuntuDotProfileSourcesProxyFile(s.ctx) c.Assert(err, gc.IsNil) err = upgrades.EnsureUbuntuDotProfileSourcesProxyFile(s.ctx) c.Assert(err, gc.IsNil) s.assertProfile(c, expectedLine) } func (s *ensureDotProfileSuite) TestProfileUntouchedIfJujuProxyInSource(c *gc.C) { content := "source .juju-proxy\n" s.writeDotProfile(c, content) err := upgrades.EnsureUbuntuDotProfileSourcesProxyFile(s.ctx) c.Assert(err, gc.IsNil) s.assertProfile(c, content) } func (s *ensureDotProfileSuite) TestSkippedIfDotProfileDoesntExist(c *gc.C) { err := upgrades.EnsureUbuntuDotProfileSourcesProxyFile(s.ctx) c.Assert(err, gc.IsNil) c.Assert(path.Join(s.home, ".profile"), jc.DoesNotExist) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/operations.go���������������������������������0000644�0000153�0000161�00000001070�12321735642�025207� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades import "launchpad.net/juju-core/version" // upgradeOperations returns an ordered slice of sets of operations needed // to upgrade Juju to particular version. The slice is ordered by target // version, so that the sets of operations are executed in order from oldest // version to most recent. var upgradeOperations = func() []Operation { steps := []Operation{ upgradeToVersion{ version.MustParse("1.18.0"), stepsFor118(), }, } return steps } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/doc.go����������������������������������������0000644�0000153�0000161�00000001171�12321735642�023573� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The upgrades package provides infrastructure to upgrade previous Juju // deployments to the current Juju version. The upgrade is performed on // a per node basis, across all of the running Juju machines. // // Important exported APIs include: // PerformUpgrade, which is invoked on each node by the machine agent with: // fromVersion - the Juju version from which the upgrade is occurring // target - the type of Juju node being upgraded // context - provides API access to Juju state servers // package upgrades �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/steps118.go�����������������������������������0000644�0000153�0000161�00000002502�12321735642�024415� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades // stepsFor118 returns upgrade steps to upgrade to a Juju 1.18 deployment. func stepsFor118() []Step { return []Step{ &upgradeStep{ description: "make $DATADIR/locks owned by ubuntu:ubuntu", targets: []Target{AllMachines}, run: ensureLockDirExistsAndUbuntuWritable, }, &upgradeStep{ description: "generate system ssh key", targets: []Target{StateServer}, run: ensureSystemSSHKey, }, &upgradeStep{ description: "update rsyslog port", targets: []Target{StateServer}, run: updateRsyslogPort, }, &upgradeStep{ description: "install rsyslog-gnutls", targets: []Target{AllMachines}, run: installRsyslogGnutls, }, &upgradeStep{ description: "remove deprecated environment config settings", targets: []Target{StateServer}, run: processDeprecatedEnvSettings, }, &upgradeStep{ description: "migrate local provider agent config", targets: []Target{StateServer}, run: migrateLocalProviderAgentConfig, }, &upgradeStep{ description: "make /home/ubuntu/.profile source .juju-proxy file", targets: []Target{AllMachines}, run: ensureUbuntuDotProfileSourcesProxyFile, }, } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/deprecatedenvsettings_test.go�����������������0000644�0000153�0000161�00000004754�12321735642�030471� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/upgrades" ) type processDeprecatedEnvSettingsSuite struct { jujutesting.JujuConnSuite ctx upgrades.Context } var _ = gc.Suite(&processDeprecatedEnvSettingsSuite{}) func (s *processDeprecatedEnvSettingsSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) apiState, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) s.ctx = &mockContext{ agentConfig: &mockAgentConfig{dataDir: s.DataDir()}, apiState: apiState, state: s.State, } // Add in old environment settings. newCfg := map[string]interface{}{ "public-bucket": "foo", "public-bucket-region": "bar", "public-bucket-url": "shazbot", "default-instance-type": "vulch", "default-image-id": "1234", "shared-storage-port": 1234, } err := s.State.UpdateEnvironConfig(newCfg, nil, nil) c.Assert(err, gc.IsNil) } func (s *processDeprecatedEnvSettingsSuite) TestEnvSettingsSet(c *gc.C) { cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) allAttrs := cfg.AllAttrs() c.Assert(allAttrs["public-bucket"], gc.Equals, "foo") c.Assert(allAttrs["public-bucket-region"], gc.Equals, "bar") c.Assert(allAttrs["public-bucket-url"], gc.Equals, "shazbot") c.Assert(allAttrs["default-instance-type"], gc.Equals, "vulch") c.Assert(allAttrs["default-image-id"], gc.Equals, "1234") c.Assert(allAttrs["shared-storage-port"], gc.Equals, 1234) } func (s *processDeprecatedEnvSettingsSuite) assertConfigProcessed(c *gc.C) { cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) allAttrs := cfg.AllAttrs() for _, deprecated := range []string{ "public-bucket", "public-bucket-region", "public-bucket-url", "default-image-id", "default-instance-type", "shared-storage-port", } { _, ok := allAttrs[deprecated] c.Assert(ok, jc.IsFalse) } } func (s *processDeprecatedEnvSettingsSuite) TestOldConfigRemoved(c *gc.C) { err := upgrades.ProcessDeprecatedEnvSettings(s.ctx) c.Assert(err, gc.IsNil) s.assertConfigProcessed(c) } func (s *processDeprecatedEnvSettingsSuite) TestIdempotent(c *gc.C) { err := upgrades.ProcessDeprecatedEnvSettings(s.ctx) c.Assert(err, gc.IsNil) s.assertConfigProcessed(c) err = upgrades.ProcessDeprecatedEnvSettings(s.ctx) c.Assert(err, gc.IsNil) s.assertConfigProcessed(c) } ��������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/upgrade.go������������������������������������0000644�0000153�0000161�00000013551�12321735776�024472� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades import ( "fmt" "github.com/juju/loggo" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.upgrade") // Step defines an idempotent operation that is run to perform // a specific upgrade step. type Step interface { // Description is a human readable description of what the upgrade step does. Description() string // Targets returns the target machine types for which the upgrade step is applicable. Targets() []Target // Run executes the upgrade business logic. Run(context Context) error } // Operation defines what steps to perform to upgrade to a target version. type Operation interface { // The Juju version for which this operation is applicable. // Upgrade operations designed for versions of Juju earlier // than we are upgrading from are not run since such steps would // already have been used to get to the version we are running now. TargetVersion() version.Number // Steps to perform during an upgrade. Steps() []Step } // Target defines the type of machine for which a particular upgrade // step can be run. type Target string const ( // AllMachines applies to any machine. AllMachines = Target("allMachines") // HostMachine is a machine on which units are deployed. HostMachine = Target("hostMachine") // StateServer is a machine participating in a Juju state server cluster. StateServer = Target("stateServer") ) // upgradeToVersion encapsulates the steps which need to be run to // upgrade any prior version of Juju to targetVersion. type upgradeToVersion struct { targetVersion version.Number steps []Step } // Steps is defined on the Operation interface. func (u upgradeToVersion) Steps() []Step { return u.steps } // TargetVersion is defined on the Operation interface. func (u upgradeToVersion) TargetVersion() version.Number { return u.targetVersion } // Context is used give the upgrade steps attributes needed // to do their job. type Context interface { // APIState returns an API connection to state. APIState() *api.State // State returns a connection to state. This will be non-nil // only in the context of a state server. State() *state.State // AgentConfig returns the agent config for the machine that is being // upgraded. AgentConfig() agent.Config } // upgradeContext is a default Context implementation. type upgradeContext struct { // Work in progress........ // Exactly what a context needs is to be determined as the // implementation evolves. api *api.State st *state.State agentConfig agent.Config } // APIState is defined on the Context interface. func (c *upgradeContext) APIState() *api.State { return c.api } // State is defined on the Context interface. func (c *upgradeContext) State() *state.State { return c.st } // AgentConfig is defined on the Context interface. func (c *upgradeContext) AgentConfig() agent.Config { return c.agentConfig } // NewContext returns a new upgrade context. func NewContext(agentConfig agent.Config, api *api.State, st *state.State) Context { return &upgradeContext{ api: api, st: st, agentConfig: agentConfig, } } // upgradeError records a description of the step being performed and the error. type upgradeError struct { description string err error } func (e *upgradeError) Error() string { return fmt.Sprintf("%s: %v", e.description, e.err) } // PerformUpgrade runs the business logic needed to upgrade the current "from" version to this // version of Juju on the "target" type of machine. func PerformUpgrade(from version.Number, target Target, context Context) error { // If from is not known, it is 1.16. if from == version.Zero { from = version.MustParse("1.16.0") } for _, upgradeOps := range upgradeOperations() { targetVersion := upgradeOps.TargetVersion() // Do not run steps for versions of Juju earlier or same as we are upgrading from. if targetVersion.Compare(from) <= 0 { continue } // Do not run steps for versions of Juju later than we are upgrading to. if targetVersion.Compare(version.Current.Number) > 0 { continue } if err := runUpgradeSteps(context, target, upgradeOps); err != nil { return err } } return nil } // validTarget returns true if target is in step.Targets(). func validTarget(target Target, step Step) bool { for _, opTarget := range step.Targets() { if opTarget == AllMachines || target == opTarget { return true } } return len(step.Targets()) == 0 } // runUpgradeSteps runs all the upgrade steps relevant to target. // As soon as any error is encountered, the operation is aborted since // subsequent steps may required successful completion of earlier ones. // The steps must be idempotent so that the entire upgrade operation can // be retried. func runUpgradeSteps(context Context, target Target, upgradeOp Operation) *upgradeError { for _, step := range upgradeOp.Steps() { if !validTarget(target, step) { continue } logger.Infof("running upgrade step on target %q: %v", target, step.Description()) if err := step.Run(context); err != nil { logger.Errorf("upgrade step %q failed: %v", step.Description(), err) return &upgradeError{ description: step.Description(), err: err, } } } logger.Infof("All upgrade steps completed successfully") return nil } type upgradeStep struct { description string targets []Target run func(Context) error } // Description is defined on the Step interface. func (step *upgradeStep) Description() string { return step.description } // Targets is defined on the Step interface. func (step *upgradeStep) Targets() []Target { return step.targets } // Run is defined on the Step interface. func (step *upgradeStep) Run(context Context) error { return step.run(context) } �������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/systemsshkey.go�������������������������������0000644�0000153�0000161�00000002664�12321735642�025611� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades import ( "fmt" "io/ioutil" "os" "path" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/state/api/keymanager" "launchpad.net/juju-core/utils/ssh" ) func ensureSystemSSHKey(context Context) error { identityFile := path.Join(context.AgentConfig().DataDir(), cloudinit.SystemIdentity) // Don't generate a key unless we have to. keyExists, err := systemKeyExists(identityFile) if err != nil { return fmt.Errorf("failed to check system key exists: %v", err) } if keyExists { return nil } privateKey, publicKey, err := ssh.GenerateKey(config.JujuSystemKey) if err != nil { return fmt.Errorf("failed to create system key: %v", err) } // Write new authorised key. keyManager := keymanager.NewClient(context.APIState()) errResults, err := keyManager.AddKeys(config.JujuSystemKey, publicKey) apiErr := err if apiErr == nil { apiErr = errResults[0].Error } if err != nil || errResults[0].Error != nil { return fmt.Errorf("failed to update authoised keys with new system key: %v", apiErr) } return ioutil.WriteFile(identityFile, []byte(privateKey), 0600) } func systemKeyExists(identityFile string) (bool, error) { _, err := os.Stat(identityFile) if err == nil { return true, nil } if !os.IsNotExist(err) { return false, err } return false, nil } ����������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/lockdirectory.go������������������������������0000644�0000153�0000161�00000002612�12321735642�025704� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades import ( "fmt" "path" "launchpad.net/juju-core/utils/exec" ) var ubuntuHome = "/home/ubuntu" // Previously the lock directory was created when the uniter started. This // allows serialization of all of the hook execution across units running on a // single machine. This lock directory is now also used but the juju-run // command on the host machine. juju-run also gets a lock on the hook // execution fslock prior to execution. However, the lock directory was owned // by root, and the juju-run process was being executed by the ubuntu user, so // we need to change the ownership of the lock directory to ubuntu:ubuntu. // Also we need to make sure that this directory exists on machines with no // units. func ensureLockDirExistsAndUbuntuWritable(context Context) error { lockDir := path.Join(context.AgentConfig().DataDir(), "locks") // We only try to change ownership if there is an ubuntu user // defined, and we determine this by the existance of the home dir. command := fmt.Sprintf(""+ "mkdir -p %s\n"+ "[ -e %s ] && chown ubuntu:ubuntu %s\n", lockDir, ubuntuHome, lockDir) logger.Tracef("command: %s", command) result, err := exec.RunCommands(exec.RunParams{ Commands: command, }) if err != nil { return err } logger.Tracef("stdout: %s", result.Stdout) return nil } ����������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/lockdirectory_test.go�������������������������0000644�0000153�0000161�00000004376�12321735642�026754� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades_test import ( "fmt" "io/ioutil" "path/filepath" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/upgrades" ) type ensureLockDirSuite struct { testing.FakeHomeSuite bin string home string datadir string lockdir string ctx upgrades.Context } var _ = gc.Suite(&ensureLockDirSuite{}) // fakecommand outputs its arguments to stdout for verification var fakecommand = `#!/bin/bash echo $@ | tee $0.args ` func (s *ensureLockDirSuite) SetUpTest(c *gc.C) { s.FakeHomeSuite.SetUpTest(c) s.bin = c.MkDir() s.PatchEnvPathPrepend(s.bin) err := ioutil.WriteFile( filepath.Join(s.bin, "chown"), []byte(fakecommand), 0777) c.Assert(err, gc.IsNil) loggo.GetLogger("juju.upgrade").SetLogLevel(loggo.TRACE) s.home = c.MkDir() s.PatchValue(upgrades.UbuntuHome, s.home) s.datadir = c.MkDir() s.lockdir = filepath.Join(s.datadir, "locks") s.ctx = &mockContext{agentConfig: &mockAgentConfig{dataDir: s.datadir}} } func (s *ensureLockDirSuite) assertChownCalled(c *gc.C) { bytes, err := ioutil.ReadFile(filepath.Join(s.bin, "chown.args")) c.Assert(err, gc.IsNil) c.Assert(string(bytes), gc.Equals, fmt.Sprintf("ubuntu:ubuntu %s\n", s.lockdir)) } func (s *ensureLockDirSuite) assertNoChownCalled(c *gc.C) { c.Assert(filepath.Join(s.bin, "chown.args"), jc.DoesNotExist) } func (s *ensureLockDirSuite) TestLockDirCreated(c *gc.C) { err := upgrades.EnsureLockDirExistsAndUbuntuWritable(s.ctx) c.Assert(err, gc.IsNil) c.Assert(s.lockdir, jc.IsDirectory) s.assertChownCalled(c) } func (s *ensureLockDirSuite) TestIdempotent(c *gc.C) { err := upgrades.EnsureLockDirExistsAndUbuntuWritable(s.ctx) c.Assert(err, gc.IsNil) err = upgrades.EnsureLockDirExistsAndUbuntuWritable(s.ctx) c.Assert(err, gc.IsNil) c.Assert(s.lockdir, jc.IsDirectory) s.assertChownCalled(c) } func (s *ensureLockDirSuite) TestNoChownIfNoHome(c *gc.C) { s.PatchValue(upgrades.UbuntuHome, filepath.Join(s.home, "not-exist")) err := upgrades.EnsureLockDirExistsAndUbuntuWritable(s.ctx) c.Assert(err, gc.IsNil) c.Assert(s.lockdir, jc.IsDirectory) s.assertNoChownCalled(c) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/steps118_test.go������������������������������0000644�0000153�0000161�00000001516�12321735642�025460� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/upgrades" ) type steps118Suite struct { testbase.LoggingSuite } var _ = gc.Suite(&steps118Suite{}) var expectedSteps = []string{ "make $DATADIR/locks owned by ubuntu:ubuntu", "generate system ssh key", "update rsyslog port", "install rsyslog-gnutls", "remove deprecated environment config settings", "migrate local provider agent config", "make /home/ubuntu/.profile source .juju-proxy file", } func (s *steps118Suite) TestUpgradeOperationsContent(c *gc.C) { upgradeSteps := upgrades.StepsFor118() c.Assert(upgradeSteps, gc.HasLen, len(expectedSteps)) assertExpectedSteps(c, upgradeSteps, expectedSteps) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/upgrade_test.go�������������������������������0000644�0000153�0000161�00000017526�12321735776�025537� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades_test import ( "errors" "strings" stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/upgrades" "launchpad.net/juju-core/version" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } // assertExpectedSteps is a helper function used to check that the upgrade steps match // what is expected for a version. func assertExpectedSteps(c *gc.C, steps []upgrades.Step, expectedSteps []string) { var stepNames = make([]string, len(steps)) for i, step := range steps { stepNames[i] = step.Description() } c.Assert(stepNames, gc.DeepEquals, expectedSteps) } type upgradeSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&upgradeSuite{}) type mockUpgradeOperation struct { targetVersion version.Number steps []upgrades.Step } func (m *mockUpgradeOperation) TargetVersion() version.Number { return m.targetVersion } func (m *mockUpgradeOperation) Steps() []upgrades.Step { return m.steps } type mockUpgradeStep struct { msg string targets []upgrades.Target } func (u *mockUpgradeStep) Description() string { return u.msg } func (u *mockUpgradeStep) Targets() []upgrades.Target { return u.targets } func (u *mockUpgradeStep) Run(context upgrades.Context) error { if strings.HasSuffix(u.msg, "error") { return errors.New("upgrade error occurred") } ctx := context.(*mockContext) ctx.messages = append(ctx.messages, u.msg) return nil } type mockContext struct { messages []string agentConfig *mockAgentConfig realAgentConfig agent.Config apiState *api.State state *state.State } func (c *mockContext) APIState() *api.State { return c.apiState } func (c *mockContext) State() *state.State { return c.state } func (c *mockContext) AgentConfig() agent.Config { if c.realAgentConfig != nil { return c.realAgentConfig } return c.agentConfig } type mockAgentConfig struct { agent.Config dataDir string logDir string tag string jobs []params.MachineJob apiAddresses []string values map[string]string } func (mock *mockAgentConfig) Tag() string { return mock.tag } func (mock *mockAgentConfig) DataDir() string { return mock.dataDir } func (mock *mockAgentConfig) LogDir() string { return mock.logDir } func (mock *mockAgentConfig) Jobs() []params.MachineJob { return mock.jobs } func (mock *mockAgentConfig) APIAddresses() ([]string, error) { return mock.apiAddresses, nil } func (mock *mockAgentConfig) Value(name string) string { return mock.values[name] } func targets(targets ...upgrades.Target) (upgradeTargets []upgrades.Target) { for _, t := range targets { upgradeTargets = append(upgradeTargets, t) } return upgradeTargets } func upgradeOperations() []upgrades.Operation { steps := []upgrades.Operation{ &mockUpgradeOperation{ targetVersion: version.MustParse("1.12.0"), steps: []upgrades.Step{ &mockUpgradeStep{"step 1 - 1.12.0", nil}, &mockUpgradeStep{"step 2 error", targets(upgrades.HostMachine)}, &mockUpgradeStep{"step 3", targets(upgrades.HostMachine)}, }, }, &mockUpgradeOperation{ targetVersion: version.MustParse("1.16.0"), steps: []upgrades.Step{ &mockUpgradeStep{"step 1 - 1.16.0", targets(upgrades.HostMachine)}, &mockUpgradeStep{"step 2 - 1.16.0", targets(upgrades.HostMachine)}, &mockUpgradeStep{"step 3 - 1.16.0", targets(upgrades.StateServer)}, }, }, &mockUpgradeOperation{ targetVersion: version.MustParse("1.17.0"), steps: []upgrades.Step{ &mockUpgradeStep{"step 1 - 1.17.0", targets(upgrades.HostMachine)}, }, }, &mockUpgradeOperation{ targetVersion: version.MustParse("1.17.1"), steps: []upgrades.Step{ &mockUpgradeStep{"step 1 - 1.17.1", targets(upgrades.HostMachine)}, &mockUpgradeStep{"step 2 - 1.17.1", targets(upgrades.StateServer)}, }, }, &mockUpgradeOperation{ targetVersion: version.MustParse("1.18.0"), steps: []upgrades.Step{ &mockUpgradeStep{"step 1 - 1.18.0", targets(upgrades.HostMachine)}, &mockUpgradeStep{"step 2 - 1.18.0", targets(upgrades.StateServer)}, }, }, &mockUpgradeOperation{ targetVersion: version.MustParse("1.20.0"), steps: []upgrades.Step{ &mockUpgradeStep{"step 1 - 1.20.0", targets(upgrades.AllMachines)}, &mockUpgradeStep{"step 2 - 1.20.0", targets(upgrades.HostMachine)}, &mockUpgradeStep{"step 3 - 1.20.0", targets(upgrades.StateServer)}, }, }, } return steps } type upgradeTest struct { about string fromVersion string toVersion string target upgrades.Target expectedSteps []string err string } var upgradeTests = []upgradeTest{ { about: "from version excludes steps for same version", fromVersion: "1.18.0", target: upgrades.HostMachine, expectedSteps: []string{}, }, { about: "target version excludes steps for newer version", toVersion: "1.17.1", target: upgrades.HostMachine, expectedSteps: []string{"step 1 - 1.17.0", "step 1 - 1.17.1"}, }, { about: "from version excludes older steps", fromVersion: "1.17.0", target: upgrades.HostMachine, expectedSteps: []string{"step 1 - 1.17.1", "step 1 - 1.18.0"}, }, { about: "incompatible targets excluded", fromVersion: "1.17.1", target: upgrades.StateServer, expectedSteps: []string{"step 2 - 1.18.0"}, }, { about: "allMachines matches everything", fromVersion: "1.18.1", toVersion: "1.20.0", target: upgrades.HostMachine, expectedSteps: []string{"step 1 - 1.20.0", "step 2 - 1.20.0"}, }, { about: "allMachines matches everything", fromVersion: "1.18.1", toVersion: "1.20.0", target: upgrades.StateServer, expectedSteps: []string{"step 1 - 1.20.0", "step 3 - 1.20.0"}, }, { about: "error aborts, subsequent steps not run", fromVersion: "1.10.0", target: upgrades.HostMachine, expectedSteps: []string{"step 1 - 1.12.0"}, err: "step 2 error: upgrade error occurred", }, { about: "default from version is 1.16", fromVersion: "", target: upgrades.StateServer, expectedSteps: []string{"step 2 - 1.17.1", "step 2 - 1.18.0"}, }, } func (s *upgradeSuite) TestPerformUpgrade(c *gc.C) { s.PatchValue(upgrades.UpgradeOperations, upgradeOperations) for i, test := range upgradeTests { c.Logf("%d: %s", i, test.about) var messages []string ctx := &mockContext{ messages: messages, } fromVersion := version.Zero if test.fromVersion != "" { fromVersion = version.MustParse(test.fromVersion) } toVersion := version.MustParse("1.18.0") if test.toVersion != "" { toVersion = version.MustParse(test.toVersion) } vers := version.Current vers.Number = toVersion s.PatchValue(&version.Current, vers) err := upgrades.PerformUpgrade(fromVersion, test.target, ctx) if test.err == "" { c.Check(err, gc.IsNil) } else { c.Check(err, gc.ErrorMatches, test.err) } c.Check(ctx.messages, jc.DeepEquals, test.expectedSteps) } } func (s *upgradeSuite) TestUpgradeOperationsOrdered(c *gc.C) { var previous version.Number for i, utv := range (*upgrades.UpgradeOperations)() { vers := utv.TargetVersion() if i > 0 { c.Check(previous.Compare(vers), gc.Equals, -1) } previous = vers } } var expectedVersions = []string{"1.18.0"} func (s *upgradeSuite) TestUpgradeOperationsVersions(c *gc.C) { var versions []string for _, utv := range (*upgrades.UpgradeOperations)() { versions = append(versions, utv.TargetVersion().String()) } c.Assert(versions, gc.DeepEquals, expectedVersions) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/dotprofile.go���������������������������������0000644�0000153�0000161�00000002426�12321735642�025201� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades import ( "fmt" "launchpad.net/juju-core/utils/exec" ) // As of the middle of the 1.17 cycle, the proxy settings are written out to // /home/ubuntu/.juju-proxy both by cloud-init and the machine environ worker. // An older version of juju that has been upgraded will get the proxy settings // written out to the .juju-proxy file, but the .profile for the ubuntu user // wouldn't have been updated to source this file. // // This upgrade step is to add the line to source the file if it is missing // from the file. func ensureUbuntuDotProfileSourcesProxyFile(context Context) error { // We look to see if the proxy line is there already as the manual // provider may have had it aleady. The ubuntu user may not exist // (local provider only). command := fmt.Sprintf(""+ `([ ! -e %s/.profile ] || grep -q '.juju-proxy' %s/.profile) || `+ `printf '\n# Added by juju\n[ -f "$HOME/.juju-proxy" ] && . "$HOME/.juju-proxy"\n' >> %s/.profile`, ubuntuHome, ubuntuHome, ubuntuHome) logger.Tracef("command: %s", command) result, err := exec.RunCommands(exec.RunParams{ Commands: command, }) if err != nil { return err } logger.Tracef("stdout: %s", result.Stdout) return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/systemsshkey_test.go��������������������������0000644�0000153�0000161�00000005017�12321735642�026643� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades_test import ( "io/ioutil" "os" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/upgrades" "launchpad.net/juju-core/utils/ssh" ) type systemSSHKeySuite struct { jujutesting.JujuConnSuite ctx upgrades.Context } var _ = gc.Suite(&systemSSHKeySuite{}) func (s *systemSSHKeySuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) apiState, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) s.ctx = &mockContext{ agentConfig: &mockAgentConfig{dataDir: s.DataDir()}, apiState: apiState, } _, err := os.Stat(s.keyFile()) c.Assert(err, jc.Satisfies, os.IsNotExist) // There's initially one authorised key for the test user. cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) authKeys := ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys()) c.Assert(authKeys, gc.HasLen, 1) } func (s *systemSSHKeySuite) keyFile() string { return filepath.Join(s.DataDir(), "system-identity") } func (s *systemSSHKeySuite) assertKeyCreation(c *gc.C) { c.Assert(s.keyFile(), jc.IsNonEmptyFile) // Check the private key from the system identify file. privateKey, err := ioutil.ReadFile(s.keyFile()) c.Assert(err, gc.IsNil) c.Check(string(privateKey), jc.HasPrefix, "-----BEGIN RSA PRIVATE KEY-----\n") c.Check(string(privateKey), jc.HasSuffix, "-----END RSA PRIVATE KEY-----\n") // Check the public key from the auth keys config. cfg, err := s.JujuConnSuite.State.EnvironConfig() c.Assert(err, gc.IsNil) authKeys := ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys()) // The dummy env is created with 1 fake key. We check that another has been added. c.Assert(authKeys, gc.HasLen, 2) c.Check(authKeys[1], jc.HasPrefix, "ssh-rsa ") c.Check(authKeys[1], jc.HasSuffix, " juju-system-key") } func (s *systemSSHKeySuite) TestSystemKeyCreated(c *gc.C) { err := upgrades.EnsureSystemSSHKey(s.ctx) c.Assert(err, gc.IsNil) s.assertKeyCreation(c) } func (s *systemSSHKeySuite) TestIdempotent(c *gc.C) { err := upgrades.EnsureSystemSSHKey(s.ctx) c.Assert(err, gc.IsNil) privateKey, err := ioutil.ReadFile(s.keyFile()) c.Assert(err, gc.IsNil) err = upgrades.EnsureSystemSSHKey(s.ctx) c.Assert(err, gc.IsNil) // Ensure we haven't generated the key again a second time. privateKey2, err := ioutil.ReadFile(s.keyFile()) c.Assert(err, gc.IsNil) c.Assert(privateKey, gc.DeepEquals, privateKey2) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/agentconfig.go��������������������������������0000644�0000153�0000161�00000011031�12321735776�025316� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades import ( "fmt" "os" "os/user" "path/filepath" "strconv" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/state/api/params" ) var ( rootLogDir = "/var/log" rootSpoolDir = "/var/spool/rsyslog" ) var chownPath = func(path, username string) error { user, err := user.Lookup(username) if err != nil { return fmt.Errorf("cannot lookup %q user id: %v", username, err) } uid, err := strconv.Atoi(user.Uid) if err != nil { return fmt.Errorf("invalid user id %q: %v", user.Uid, err) } gid, err := strconv.Atoi(user.Gid) if err != nil { return fmt.Errorf("invalid group id %q: %v", user.Gid, err) } return os.Chown(path, uid, gid) } var isLocalEnviron = func(envConfig *config.Config) bool { return envConfig.Type() == "local" } func migrateLocalProviderAgentConfig(context Context) error { st := context.State() if st == nil { logger.Debugf("no state connection, no migration required") // We're running on a different node than the state server. return nil } envConfig, err := st.EnvironConfig() if err != nil { return fmt.Errorf("failed to read current config: %v", err) } if !isLocalEnviron(envConfig) { logger.Debugf("not a local environment, no migration required") return nil } attrs := envConfig.AllAttrs() rootDir, _ := attrs["root-dir"].(string) sharedStorageDir := filepath.Join(rootDir, "shared-storage") // In case these two are empty we need to set them and update the // environment config. namespace, _ := attrs["namespace"].(string) container, _ := attrs["container"].(string) if namespace == "" { username := os.Getenv("USER") if username == "root" { // sudo was probably called, get the original user. username = os.Getenv("SUDO_USER") } if username == "" { return fmt.Errorf("cannot get current user from the environment: %v", os.Environ()) } namespace = username + "-" + envConfig.Name() } if container == "" { container = "lxc" } dataDir := rootDir localLogDir := filepath.Join(rootDir, "log") // rsyslogd is restricted to write to /var/log logDir := fmt.Sprintf("%s/juju-%s", rootLogDir, namespace) jobs := []params.MachineJob{params.JobManageEnviron} values := map[string]string{ agent.Namespace: namespace, // ContainerType is empty on the bootstrap node. agent.ContainerType: "", agent.AgentServiceName: "juju-agent-" + namespace, agent.MongoServiceName: "juju-db-" + namespace, } deprecatedValues := []string{ "SHARED_STORAGE_ADDR", "SHARED_STORAGE_DIR", } // Remove shared-storage dir if there. if err := os.RemoveAll(sharedStorageDir); err != nil { return fmt.Errorf("cannot remove deprecated %q: %v", sharedStorageDir, err) } // We need to create the dirs if they don't exist. if err := os.MkdirAll(dataDir, 0755); err != nil { return fmt.Errorf("cannot create dataDir %q: %v", dataDir, err) } // We always recreate the logDir to make sure it's empty. if err := os.RemoveAll(logDir); err != nil { return fmt.Errorf("cannot remove logDir %q: %v", logDir, err) } if err := os.MkdirAll(logDir, 0755); err != nil { return fmt.Errorf("cannot create logDir %q: %v", logDir, err) } // Reconfigure rsyslog as needed: // 1. logDir must be owned by syslog:adm // 2. Remove old rsyslog spool config // 3. Relink logs to the new logDir if err := chownPath(logDir, "syslog"); err != nil { return err } spoolConfig := fmt.Sprintf("%s/machine-0-%s", rootSpoolDir, namespace) if err := os.RemoveAll(spoolConfig); err != nil { return fmt.Errorf("cannot remove %q: %v", spoolConfig, err) } allMachinesLog := filepath.Join(logDir, "all-machines.log") if err := os.Symlink(allMachinesLog, localLogDir+"/"); err != nil && !os.IsExist(err) { return fmt.Errorf("cannot symlink %q to %q: %v", allMachinesLog, localLogDir, err) } machine0Log := filepath.Join(localLogDir, "machine-0.log") if err := os.Symlink(machine0Log, logDir+"/"); err != nil && !os.IsExist(err) { return fmt.Errorf("cannot symlink %q to %q: %v", machine0Log, logDir, err) } newCfg := map[string]interface{}{ "namespace": namespace, "container": container, } if err := st.UpdateEnvironConfig(newCfg, nil, nil); err != nil { return fmt.Errorf("cannot update environment config: %v", err) } migrateParams := agent.MigrateConfigParams{ DataDir: dataDir, LogDir: logDir, Jobs: jobs, Values: values, DeleteValues: deprecatedValues, } return agent.MigrateConfig(context.AgentConfig(), migrateParams) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/rsyslogport_test.go���������������������������0000644�0000153�0000161�00000002623�12321735642�026477� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/upgrades" ) type rsyslogPortSuite struct { jujutesting.JujuConnSuite ctx upgrades.Context } var _ = gc.Suite(&rsyslogPortSuite{}) func (s *rsyslogPortSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) apiState, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) s.ctx = &mockContext{ agentConfig: &mockAgentConfig{dataDir: s.DataDir()}, apiState: apiState, state: s.State, } cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.SyslogPort(), gc.Not(gc.Equals), config.DefaultSyslogPort) } func (s *rsyslogPortSuite) TestSyslogPortChanged(c *gc.C) { err := upgrades.UpdateRsyslogPort(s.ctx) c.Assert(err, gc.IsNil) cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.SyslogPort(), gc.Equals, config.DefaultSyslogPort) } func (s *rsyslogPortSuite) TestIdempotent(c *gc.C) { err := upgrades.UpdateRsyslogPort(s.ctx) c.Assert(err, gc.IsNil) err = upgrades.UpdateRsyslogPort(s.ctx) c.Assert(err, gc.IsNil) cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.SyslogPort(), gc.Equals, config.DefaultSyslogPort) } �������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upgrades/agentconfig_test.go���������������������������0000644�0000153�0000161�00000015055�12321735776�026367� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrades_test import ( "os" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/environs/config" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/upgrades" "launchpad.net/juju-core/version" ) type migrateLocalProviderAgentConfigSuite struct { jujutesting.JujuConnSuite ctx upgrades.Context } var _ = gc.Suite(&migrateLocalProviderAgentConfigSuite{}) func (s *migrateLocalProviderAgentConfigSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) // Make sure we fallback to SUDO_USER if USER is root. s.PatchEnvironment("USER", "root") s.PatchEnvironment("SUDO_USER", "user") s.PatchValue(upgrades.RootLogDir, c.MkDir()) s.PatchValue(upgrades.RootSpoolDir, c.MkDir()) s.PatchValue(&agent.DefaultDataDir, c.MkDir()) s.PatchValue(upgrades.ChownPath, func(_, _ string) error { return nil }) s.PatchValue(upgrades.IsLocalEnviron, func(_ *config.Config) bool { return true }) } func (s *migrateLocalProviderAgentConfigSuite) primeConfig(c *gc.C, st *state.State, job state.MachineJob, tag string) { rootDir := c.MkDir() sharedStorageDir := filepath.Join(rootDir, "shared-storage") c.Assert(os.MkdirAll(sharedStorageDir, 0755), gc.IsNil) localLogDir := filepath.Join(rootDir, "log") c.Assert(os.MkdirAll(localLogDir, 0755), gc.IsNil) initialConfig, err := agent.NewAgentConfig(agent.AgentConfigParams{ Tag: tag, Password: "blah", CACert: []byte(testing.CACert), StateAddresses: []string{"localhost:1111"}, DataDir: agent.DefaultDataDir, LogDir: agent.DefaultLogDir, UpgradedToVersion: version.MustParse("1.16.0"), Values: map[string]string{ "SHARED_STORAGE_ADDR": "blah", "SHARED_STORAGE_DIR": sharedStorageDir, }, }) c.Assert(err, gc.IsNil) c.Assert(initialConfig.Write(), gc.IsNil) apiState, _ := s.OpenAPIAsNewMachine(c, job) s.ctx = &mockContext{ realAgentConfig: initialConfig, apiState: apiState, state: st, } newCfg := (map[string]interface{}{ "root-dir": rootDir, }) err = s.State.UpdateEnvironConfig(newCfg, nil, nil) c.Assert(err, gc.IsNil) } func (s *migrateLocalProviderAgentConfigSuite) assertConfigProcessed(c *gc.C) { envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) allAttrs := envConfig.AllAttrs() namespace, _ := allAttrs["namespace"].(string) c.Assert(namespace, gc.Equals, "user-dummyenv") container, _ := allAttrs["container"].(string) c.Assert(container, gc.Equals, "lxc") expectedDataDir, _ := allAttrs["root-dir"].(string) expectedSharedStorageDir := filepath.Join(expectedDataDir, "shared-storage") _, err = os.Lstat(expectedSharedStorageDir) c.Assert(err, gc.NotNil) c.Assert(err, jc.Satisfies, os.IsNotExist) expectedLogDir := filepath.Join(*upgrades.RootLogDir, "juju-"+namespace) expectedJobs := []params.MachineJob{params.JobManageEnviron} tag := s.ctx.AgentConfig().Tag() // We need to read the actual migrated agent config. configFilePath := agent.ConfigPath(expectedDataDir, tag) agentConfig, err := agent.ReadConf(configFilePath) c.Assert(err, gc.IsNil) c.Assert(agentConfig.DataDir(), gc.Equals, expectedDataDir) c.Assert(agentConfig.LogDir(), gc.Equals, expectedLogDir) c.Assert(agentConfig.Jobs(), gc.DeepEquals, expectedJobs) c.Assert(agentConfig.Value("SHARED_STORAGE_ADDR"), gc.Equals, "") c.Assert(agentConfig.Value("SHARED_STORAGE_DIR"), gc.Equals, "") c.Assert(agentConfig.Value(agent.Namespace), gc.Equals, namespace) agentService := "juju-agent-user-dummyenv" mongoService := "juju-db-user-dummyenv" c.Assert(agentConfig.Value(agent.AgentServiceName), gc.Equals, agentService) c.Assert(agentConfig.Value(agent.MongoServiceName), gc.Equals, mongoService) c.Assert(agentConfig.Value(agent.ContainerType), gc.Equals, "") } func (s *migrateLocalProviderAgentConfigSuite) assertConfigNotProcessed(c *gc.C) { envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) allAttrs := envConfig.AllAttrs() namespace, _ := allAttrs["namespace"].(string) c.Assert(namespace, gc.Equals, "") container, _ := allAttrs["container"].(string) c.Assert(container, gc.Equals, "") rootDir, _ := allAttrs["root-dir"].(string) expectedSharedStorageDir := filepath.Join(rootDir, "shared-storage") _, err = os.Lstat(expectedSharedStorageDir) c.Assert(err, gc.IsNil) tag := s.ctx.AgentConfig().Tag() // We need to read the actual migrated agent config. configFilePath := agent.ConfigPath(agent.DefaultDataDir, tag) agentConfig, err := agent.ReadConf(configFilePath) c.Assert(err, gc.IsNil) c.Assert(agentConfig.DataDir(), gc.Equals, agent.DefaultDataDir) c.Assert(agentConfig.LogDir(), gc.Equals, agent.DefaultLogDir) c.Assert(agentConfig.Jobs(), gc.HasLen, 0) c.Assert(agentConfig.Value("SHARED_STORAGE_ADDR"), gc.Equals, "blah") c.Assert(agentConfig.Value("SHARED_STORAGE_DIR"), gc.Equals, expectedSharedStorageDir) c.Assert(agentConfig.Value(agent.Namespace), gc.Equals, "") c.Assert(agentConfig.Value(agent.AgentServiceName), gc.Equals, "") c.Assert(agentConfig.Value(agent.MongoServiceName), gc.Equals, "") c.Assert(agentConfig.Value(agent.ContainerType), gc.Equals, "") } func (s *migrateLocalProviderAgentConfigSuite) TestMigrateStateServer(c *gc.C) { s.primeConfig(c, s.State, state.JobManageEnviron, "machine-0") err := upgrades.MigrateLocalProviderAgentConfig(s.ctx) c.Assert(err, gc.IsNil) s.assertConfigProcessed(c) } func (s *migrateLocalProviderAgentConfigSuite) TestMigrateNonLocalEnvNotDone(c *gc.C) { s.PatchValue(upgrades.IsLocalEnviron, func(_ *config.Config) bool { return false }) s.primeConfig(c, s.State, state.JobManageEnviron, "machine-0") err := upgrades.MigrateLocalProviderAgentConfig(s.ctx) c.Assert(err, gc.IsNil) s.assertConfigNotProcessed(c) } func (s *migrateLocalProviderAgentConfigSuite) TestMigrateWithoutStateConnectionNotDone(c *gc.C) { s.primeConfig(c, nil, state.JobManageEnviron, "machine-0") err := upgrades.MigrateLocalProviderAgentConfig(s.ctx) c.Assert(err, gc.IsNil) s.assertConfigNotProcessed(c) } func (s *migrateLocalProviderAgentConfigSuite) TestIdempotent(c *gc.C) { s.primeConfig(c, s.State, state.JobManageEnviron, "machine-0") err := upgrades.MigrateLocalProviderAgentConfig(s.ctx) c.Assert(err, gc.IsNil) s.assertConfigProcessed(c) err = upgrades.MigrateLocalProviderAgentConfig(s.ctx) c.Assert(err, gc.IsNil) s.assertConfigProcessed(c) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upstart/�����������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022354� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upstart/upstart.go�������������������������������������0000644�0000153�0000161�00000012145�12321735776�024433� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upstart import ( "bytes" "errors" "fmt" "io/ioutil" "os" "os/exec" "path" "regexp" "text/template" "time" "launchpad.net/juju-core/utils" ) var startedRE = regexp.MustCompile(`^.* start/running, process (\d+)\n$`) // InitDir holds the default init directory name. var InitDir = "/etc/init" var InstallStartRetryAttempts = utils.AttemptStrategy{ Total: 1 * time.Second, Delay: 250 * time.Millisecond, } // Service provides visibility into and control over an upstart service. type Service struct { Name string InitDir string // defaults to "/etc/init" } func NewService(name string) *Service { return &Service{Name: name, InitDir: InitDir} } // confPath returns the path to the service's configuration file. func (s *Service) confPath() string { return path.Join(s.InitDir, s.Name+".conf") } // Installed returns whether the service configuration exists in the // init directory. func (s *Service) Installed() bool { _, err := os.Stat(s.confPath()) return err == nil } // Running returns true if the Service appears to be running. func (s *Service) Running() bool { cmd := exec.Command("status", "--system", s.Name) out, err := cmd.CombinedOutput() if err != nil { return false } return startedRE.Match(out) } // Start starts the service. func (s *Service) Start() error { if s.Running() { return nil } err := runCommand("start", "--system", s.Name) if err != nil { // Double check to see if we were started before our command ran. if s.Running() { return nil } } return err } func runCommand(args ...string) error { out, err := exec.Command(args[0], args[1:]...).CombinedOutput() if err == nil { return nil } out = bytes.TrimSpace(out) if len(out) > 0 { return fmt.Errorf("exec %q: %v (%s)", args, err, out) } return fmt.Errorf("exec %q: %v", args, err) } // Stop stops the service. func (s *Service) Stop() error { if !s.Running() { return nil } return runCommand("stop", "--system", s.Name) } // StopAndRemove stops the service and then deletes the service // configuration from the init directory. func (s *Service) StopAndRemove() error { if !s.Installed() { return nil } if err := s.Stop(); err != nil { return err } return os.Remove(s.confPath()) } // Remove deletes the service configuration from the init directory. func (s *Service) Remove() error { if !s.Installed() { return nil } return os.Remove(s.confPath()) } // BUG: %q quoting does not necessarily match libnih quoting rules // (as used by upstart); this may become an issue in the future. var confT = template.Must(template.New("").Parse(` description "{{.Desc}}" author "Juju Team <juju@lists.ubuntu.com>" start on runlevel [2345] stop on runlevel [!2345] respawn normal exit 0 {{range $k, $v := .Env}}env {{$k}}={{$v|printf "%q"}} {{end}} {{range $k, $v := .Limit}}limit {{$k}} {{$v}} {{end}} exec {{.Cmd}}{{if .Out}} >> {{.Out}} 2>&1{{end}} `[1:])) // Conf is responsible for defining and installing upstart services. Its fields // represent elements of an upstart service configuration file. type Conf struct { Service // Desc is the upstart service's description. Desc string // Env holds the environment variables that will be set when the command runs. Env map[string]string // Limit holds the ulimit values that will be set when the command runs. Limit map[string]string // Cmd is the command (with arguments) that will be run. // The command will be restarted if it exits with a non-zero exit code. Cmd string // Out, if set, will redirect output to that path. Out string } // validate returns an error if the service is not adequately defined. func (c *Conf) validate() error { if c.Name == "" { return errors.New("missing Name") } if c.InitDir == "" { return errors.New("missing InitDir") } if c.Desc == "" { return errors.New("missing Desc") } if c.Cmd == "" { return errors.New("missing Cmd") } return nil } // render returns the upstart configuration for the service as a string. func (c *Conf) render() ([]byte, error) { if err := c.validate(); err != nil { return nil, err } var buf bytes.Buffer if err := confT.Execute(&buf, c); err != nil { return nil, err } return buf.Bytes(), nil } // Install installs and starts the service. func (c *Conf) Install() error { conf, err := c.render() if err != nil { return err } if c.Installed() { if err := c.StopAndRemove(); err != nil { return fmt.Errorf("upstart: could not remove installed service: %s", err) } } if err := ioutil.WriteFile(c.confPath(), conf, 0644); err != nil { return err } // On slower disks, upstart may take a short time to realise // that there is a service there. for attempt := InstallStartRetryAttempts.Start(); attempt.Next(); { if err = c.Start(); err == nil { break } } return err } // InstallCommands returns shell commands to install and start the service. func (c *Conf) InstallCommands() ([]string, error) { conf, err := c.render() if err != nil { return nil, err } return []string{ fmt.Sprintf("cat >> %s << 'EOF'\n%sEOF\n", c.confPath(), conf), "start " + c.Name, }, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upstart/upstart_test.go��������������������������������0000644�0000153�0000161�00000016235�12321735642�025466� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upstart_test import ( "fmt" "io/ioutil" "os" "path/filepath" "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/upstart" "launchpad.net/juju-core/utils" ) func Test(t *testing.T) { gc.TestingT(t) } type UpstartSuite struct { testbase.LoggingSuite testPath string service *upstart.Service } var _ = gc.Suite(&UpstartSuite{}) func (s *UpstartSuite) SetUpTest(c *gc.C) { s.testPath = c.MkDir() s.PatchEnvPathPrepend(s.testPath) s.PatchValue(&upstart.InstallStartRetryAttempts, utils.AttemptStrategy{}) s.service = &upstart.Service{Name: "some-service", InitDir: c.MkDir()} _, err := os.Create(filepath.Join(s.service.InitDir, "some-service.conf")) c.Assert(err, gc.IsNil) } var checkargs = ` #!/bin/bash --norc if [ "$1" != "--system" ]; then exit 255 fi if [ "$2" != "some-service" ]; then exit 255 fi if [ "$3" != "" ]; then exit 255 fi `[1:] func (s *UpstartSuite) MakeTool(c *gc.C, name, script string) { path := filepath.Join(s.testPath, name) err := ioutil.WriteFile(path, []byte(checkargs+script), 0755) c.Assert(err, gc.IsNil) } func (s *UpstartSuite) StoppedStatus(c *gc.C) { s.MakeTool(c, "status", `echo "some-service stop/waiting"`) } func (s *UpstartSuite) RunningStatus(c *gc.C) { s.MakeTool(c, "status", `echo "some-service start/running, process 123"`) } func (s *UpstartSuite) TestInitDir(c *gc.C) { svc := upstart.NewService("blah") c.Assert(svc.InitDir, gc.Equals, "/etc/init") } func (s *UpstartSuite) TestInstalled(c *gc.C) { c.Assert(s.service.Installed(), gc.Equals, true) err := os.Remove(filepath.Join(s.service.InitDir, "some-service.conf")) c.Assert(err, gc.IsNil) c.Assert(s.service.Installed(), gc.Equals, false) } func (s *UpstartSuite) TestRunning(c *gc.C) { s.MakeTool(c, "status", "exit 1") c.Assert(s.service.Running(), gc.Equals, false) s.MakeTool(c, "status", `echo "GIBBERISH NONSENSE"`) c.Assert(s.service.Running(), gc.Equals, false) s.RunningStatus(c) c.Assert(s.service.Running(), gc.Equals, true) } func (s *UpstartSuite) TestStart(c *gc.C) { s.RunningStatus(c) s.MakeTool(c, "start", "exit 99") c.Assert(s.service.Start(), gc.IsNil) s.StoppedStatus(c) c.Assert(s.service.Start(), gc.ErrorMatches, ".*exit status 99.*") s.MakeTool(c, "start", "exit 0") c.Assert(s.service.Start(), gc.IsNil) } func (s *UpstartSuite) TestStop(c *gc.C) { s.StoppedStatus(c) s.MakeTool(c, "stop", "exit 99") c.Assert(s.service.Stop(), gc.IsNil) s.RunningStatus(c) c.Assert(s.service.Stop(), gc.ErrorMatches, ".*exit status 99.*") s.MakeTool(c, "stop", "exit 0") c.Assert(s.service.Stop(), gc.IsNil) } func (s *UpstartSuite) TestRemoveMissing(c *gc.C) { err := os.Remove(filepath.Join(s.service.InitDir, "some-service.conf")) c.Assert(err, gc.IsNil) c.Assert(s.service.StopAndRemove(), gc.IsNil) } func (s *UpstartSuite) TestRemoveStopped(c *gc.C) { s.StoppedStatus(c) c.Assert(s.service.StopAndRemove(), gc.IsNil) _, err := os.Stat(filepath.Join(s.service.InitDir, "some-service.conf")) c.Assert(err, jc.Satisfies, os.IsNotExist) } func (s *UpstartSuite) TestRemoveRunning(c *gc.C) { s.RunningStatus(c) s.MakeTool(c, "stop", "exit 99") c.Assert(s.service.StopAndRemove(), gc.ErrorMatches, ".*exit status 99.*") _, err := os.Stat(filepath.Join(s.service.InitDir, "some-service.conf")) c.Assert(err, gc.IsNil) s.MakeTool(c, "stop", "exit 0") c.Assert(s.service.StopAndRemove(), gc.IsNil) _, err = os.Stat(filepath.Join(s.service.InitDir, "some-service.conf")) c.Assert(err, jc.Satisfies, os.IsNotExist) } func (s *UpstartSuite) TestStopAndRemove(c *gc.C) { s.RunningStatus(c) s.MakeTool(c, "stop", "exit 99") // StopAndRemove will fail, as it calls stop. c.Assert(s.service.StopAndRemove(), gc.ErrorMatches, ".*exit status 99.*") _, err := os.Stat(filepath.Join(s.service.InitDir, "some-service.conf")) c.Assert(err, gc.IsNil) // Plain old Remove will succeed. c.Assert(s.service.Remove(), gc.IsNil) _, err = os.Stat(filepath.Join(s.service.InitDir, "some-service.conf")) c.Assert(err, jc.Satisfies, os.IsNotExist) } func (s *UpstartSuite) TestInstallErrors(c *gc.C) { conf := &upstart.Conf{} check := func(msg string) { c.Assert(conf.Install(), gc.ErrorMatches, msg) _, err := conf.InstallCommands() c.Assert(err, gc.ErrorMatches, msg) } check("missing Name") conf.Name = "some-service" check("missing InitDir") conf.InitDir = c.MkDir() check("missing Desc") conf.Desc = "this is an upstart service" check("missing Cmd") } const expectStart = `description "this is an upstart service" author "Juju Team <juju@lists.ubuntu.com>" start on runlevel [2345] stop on runlevel [!2345] respawn normal exit 0 ` func (s *UpstartSuite) dummyConf(c *gc.C) *upstart.Conf { return &upstart.Conf{ Service: *s.service, Desc: "this is an upstart service", Cmd: "do something", } } func (s *UpstartSuite) assertInstall(c *gc.C, conf *upstart.Conf, expectEnd string) { expectContent := expectStart + expectEnd expectPath := filepath.Join(conf.InitDir, "some-service.conf") cmds, err := conf.InstallCommands() c.Assert(err, gc.IsNil) c.Assert(cmds, gc.DeepEquals, []string{ "cat >> " + expectPath + " << 'EOF'\n" + expectContent + "EOF\n", "start some-service", }) s.MakeTool(c, "start", "exit 99") err = conf.Install() c.Assert(err, gc.ErrorMatches, ".*exit status 99.*") s.MakeTool(c, "start", "exit 0") err = conf.Install() c.Assert(err, gc.IsNil) content, err := ioutil.ReadFile(expectPath) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, expectContent) } func (s *UpstartSuite) TestInstallSimple(c *gc.C) { conf := s.dummyConf(c) s.assertInstall(c, conf, "\n\nexec do something\n") } func (s *UpstartSuite) TestInstallOutput(c *gc.C) { conf := s.dummyConf(c) conf.Out = "/some/output/path" s.assertInstall(c, conf, "\n\nexec do something >> /some/output/path 2>&1\n") } func (s *UpstartSuite) TestInstallEnv(c *gc.C) { conf := s.dummyConf(c) conf.Env = map[string]string{"FOO": "bar baz", "QUX": "ping pong"} s.assertInstall(c, conf, `env FOO="bar baz" env QUX="ping pong" exec do something `) } func (s *UpstartSuite) TestInstallLimit(c *gc.C) { conf := s.dummyConf(c) conf.Limit = map[string]string{"nofile": "65000 65000", "nproc": "20000 20000"} s.assertInstall(c, conf, ` limit nofile 65000 65000 limit nproc 20000 20000 exec do something `) } func (s *UpstartSuite) TestInstallAlreadyRunning(c *gc.C) { pathTo := func(name string) string { return filepath.Join(s.testPath, name) } s.MakeTool(c, "status-stopped", `echo "some-service stop/waiting"`) s.MakeTool(c, "status-started", `echo "some-service start/running, process 123"`) s.MakeTool(c, "stop", fmt.Sprintf( "rm %s; ln -s %s %s", pathTo("status"), pathTo("status-stopped"), pathTo("status"), )) s.MakeTool(c, "start", fmt.Sprintf( "rm %s; ln -s %s %s", pathTo("status"), pathTo("status-started"), pathTo("status"), )) err := os.Symlink(pathTo("status-started"), pathTo("status")) c.Assert(err, gc.IsNil) conf := s.dummyConf(c) err = conf.Install() c.Assert(err, gc.IsNil) c.Assert(&conf.Service, jc.Satisfies, (*upstart.Service).Running) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/upstart/service.go�������������������������������������0000644�0000153�0000161�00000002001�12321735642�024347� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upstart import ( "fmt" "path" "launchpad.net/juju-core/utils" ) const ( maxAgentFiles = 20000 ) // MachineAgentUpstartService returns the upstart config for a machine agent // based on the tag and machineId passed in. func MachineAgentUpstartService(name, toolsDir, dataDir, logDir, tag, machineId string, env map[string]string) *Conf { svc := NewService(name) logFile := path.Join(logDir, tag+".log") // The machine agent always starts with debug turned on. The logger worker // will update this to the system logging environment as soon as it starts. return &Conf{ Service: *svc, Desc: fmt.Sprintf("juju %s agent", tag), Limit: map[string]string{ "nofile": fmt.Sprintf("%d %d", maxAgentFiles, maxAgentFiles), }, Cmd: path.Join(toolsDir, "jujud") + " machine" + " --data-dir " + utils.ShQuote(dataDir) + " --machine-id " + machineId + " --debug", Out: logFile, Env: env, } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/downloader/��������������������������������������������0000755�0000153�0000161�00000000000�12321735642�023023� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/downloader/downloader.go�������������������������������0000644�0000153�0000161�00000005573�12321735642�025522� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package downloader import ( "fmt" "io" "io/ioutil" "net/http" "os" "launchpad.net/tomb" "launchpad.net/juju-core/log" "launchpad.net/juju-core/utils" ) // Status represents the status of a completed download. type Status struct { // File holds the downloaded data on success. File *os.File // Err describes any error encountered while downloading. Err error } // Download can download a file from the network. type Download struct { tomb tomb.Tomb done chan Status hostnameVerification utils.SSLHostnameVerification } // New returns a new Download instance downloading from the given URL // to the given directory. If dir is empty, it defaults to // os.TempDir(). If disableSSLHostnameVerification is true then a non- // validating http client will be used. func New(url, dir string, hostnameVerification utils.SSLHostnameVerification) *Download { d := &Download{ done: make(chan Status), hostnameVerification: hostnameVerification, } go d.run(url, dir) return d } // Stop stops any download that's in progress. func (d *Download) Stop() { d.tomb.Kill(nil) d.tomb.Wait() } // Done returns a channel that receives a status when the download has // completed. It is the receiver's responsibility to close and remove // the received file. func (d *Download) Done() <-chan Status { return d.done } func (d *Download) run(url, dir string) { defer d.tomb.Done() // TODO(dimitern) 2013-10-03 bug #1234715 // Add a testing HTTPS storage to verify the // disableSSLHostnameVerification behavior here. file, err := download(url, dir, d.hostnameVerification) if err != nil { err = fmt.Errorf("cannot download %q: %v", url, err) } status := Status{ File: file, Err: err, } select { case d.done <- status: case <-d.tomb.Dying(): cleanTempFile(status.File) } } func download(url, dir string, hostnameVerification utils.SSLHostnameVerification) (file *os.File, err error) { if dir == "" { dir = os.TempDir() } tempFile, err := ioutil.TempFile(dir, "inprogress-") if err != nil { return nil, err } defer func() { if err != nil { cleanTempFile(tempFile) } }() // TODO(rog) make the download operation interruptible. client := utils.GetHTTPClient(hostnameVerification) resp, err := client.Get(url) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("bad http response: %v", resp.Status) } _, err = io.Copy(tempFile, resp.Body) if err != nil { return nil, err } if _, err := tempFile.Seek(0, 0); err != nil { return nil, err } return tempFile, nil } func cleanTempFile(f *os.File) { if f != nil { f.Close() if err := os.Remove(f.Name()); err != nil { log.Warningf("downloader: cannot remove temp file %q: %v", f.Name(), err) } } } �������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/downloader/downloader_test.go��������������������������0000644�0000153�0000161�00000005130�12321735642�026546� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package downloader_test import ( "io/ioutil" "os" "path/filepath" stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/downloader" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type suite struct { testbase.LoggingSuite testing.HTTPSuite } func (s *suite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.HTTPSuite.SetUpSuite(c) } func (s *suite) TearDownSuite(c *gc.C) { s.HTTPSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *suite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.HTTPSuite.SetUpTest(c) } func (s *suite) TearDownTest(c *gc.C) { s.HTTPSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } var _ = gc.Suite(&suite{}) func Test(t *stdtesting.T) { gc.TestingT(t) } func (s *suite) testDownload(c *gc.C, hostnameVerification utils.SSLHostnameVerification) { tmp := c.MkDir() testing.Server.Response(200, nil, []byte("archive")) d := downloader.New(s.URL("/archive.tgz"), tmp, hostnameVerification) status := <-d.Done() c.Assert(status.Err, gc.IsNil) c.Assert(status.File, gc.NotNil) defer os.Remove(status.File.Name()) defer status.File.Close() dir, _ := filepath.Split(status.File.Name()) c.Assert(filepath.Clean(dir), gc.Equals, tmp) assertFileContents(c, status.File, "archive") } func (s *suite) TestDownloadWithoutDisablingSSLHostnameVerification(c *gc.C) { s.testDownload(c, utils.VerifySSLHostnames) } func (s *suite) TestDownloadWithDisablingSSLHostnameVerification(c *gc.C) { s.testDownload(c, utils.NoVerifySSLHostnames) } func (s *suite) TestDownloadError(c *gc.C) { testing.Server.Response(404, nil, nil) d := downloader.New(s.URL("/archive.tgz"), c.MkDir(), utils.VerifySSLHostnames) status := <-d.Done() c.Assert(status.File, gc.IsNil) c.Assert(status.Err, gc.ErrorMatches, `cannot download ".*": bad http response: 404 Not Found`) } func (s *suite) TestStopDownload(c *gc.C) { tmp := c.MkDir() d := downloader.New(s.URL("/x.tgz"), tmp, utils.VerifySSLHostnames) d.Stop() select { case status := <-d.Done(): c.Fatalf("received status %#v after stop", status) case <-time.After(testing.ShortWait): } infos, err := ioutil.ReadDir(tmp) c.Assert(err, gc.IsNil) c.Assert(infos, gc.HasLen, 0) } func assertFileContents(c *gc.C, f *os.File, expect string) { got, err := ioutil.ReadAll(f) c.Assert(err, gc.IsNil) if !c.Check(string(got), gc.Equals, expect) { info, err := f.Stat() c.Assert(err, gc.IsNil) c.Logf("info %#v", info) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/instance/����������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022456� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/instance/container.go����������������������������������0000644�0000153�0000161�00000002221�12321735642�024777� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instance import ( "fmt" ) type ContainerType string const ( NONE = ContainerType("none") LXC = ContainerType("lxc") KVM = ContainerType("kvm") ) // ContainerTypes is used to validate add-machine arguments. var ContainerTypes []ContainerType = []ContainerType{ LXC, KVM, } // ParseContainerTypeOrNone converts the specified string into a supported // ContainerType instance or returns an error if the container type is invalid. // For this version of the function, 'none' is a valid value. func ParseContainerTypeOrNone(ctype string) (ContainerType, error) { if ContainerType(ctype) == NONE { return NONE, nil } return ParseContainerType(ctype) } // ParseContainerType converts the specified string into a supported // ContainerType instance or returns an error if the container type is invalid. func ParseContainerType(ctype string) (ContainerType, error) { for _, supportedType := range ContainerTypes { if ContainerType(ctype) == supportedType { return supportedType, nil } } return "", fmt.Errorf("invalid container type %q", ctype) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/instance/address.go������������������������������������0000644�0000153�0000161�00000021231�12321735776�024454� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instance import ( "bytes" "net" "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 const ( HostName AddressType = "hostname" Ipv4Address AddressType = "ipv4" Ipv6Address AddressType = "ipv6" ) // NetworkScope denotes the context a location may apply to. If a name or // address can be reached from the wider internet, it is considered public. A // private network address is either specific to the cloud or cloud subnet a // machine belongs to, or to the machine itself for containers. type NetworkScope string const ( NetworkUnknown NetworkScope = "" NetworkPublic NetworkScope = "public" NetworkCloudLocal NetworkScope = "local-cloud" NetworkMachineLocal NetworkScope = "local-machine" ) // Address represents the location of a machine, including metadata about what // kind of location the address describes. type Address struct { Value string Type AddressType NetworkName string NetworkScope } // HostPort associates an address with a port. type HostPort struct { Address Port int } // AddressesWithPort returns the given addresses all // associated with the given port. func AddressesWithPort(addrs []Address, port int) []HostPort { hps := make([]HostPort, len(addrs)) for i, addr := range addrs { hps[i] = HostPort{ Address: addr, Port: port, } } return hps } // NetAddr returns the host-port as an address // suitable for calling net.Dial. func (hp HostPort) NetAddr() string { return net.JoinHostPort(hp.Value, strconv.Itoa(hp.Port)) } // String returns a string representation of the address, // in the form: scope:address(network name); // for example: // // public:c2-54-226-162-124.compute-1.amazonaws.com(ec2network) // // If the scope is NetworkUnknown, the initial scope: prefix will // be omitted. If the NetworkName is blank, the (network name) suffix // will be omitted. func (a Address) String() string { var buf bytes.Buffer if a.NetworkScope != NetworkUnknown { buf.WriteString(string(a.NetworkScope)) buf.WriteByte(':') } buf.WriteString(a.Value) if a.NetworkName != "" { buf.WriteByte('(') buf.WriteString(a.NetworkName) buf.WriteByte(')') } return buf.String() } // NewAddresses is a convenience function to create addresses from a string slice func NewAddresses(inAddresses []string) (outAddresses []Address) { for _, address := range inAddresses { outAddresses = append(outAddresses, NewAddress(address)) } return outAddresses } func DeriveAddressType(value string) AddressType { ip := net.ParseIP(value) 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") } } // 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 { 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. // // The argument passed in is always added ast the final // address in the resulting slice. func HostAddresses(host string) (addrs []Address, err error) { hostAddr := NewAddress(host) if hostAddr.Type != HostName { // IPs shouldn't be fed into LookupIP. return []Address{hostAddr}, nil } ipaddrs, err := netLookupIP(host) if err != nil { return nil, err } addrs = make([]Address, len(ipaddrs)+1) for i, ipaddr := range ipaddrs { switch len(ipaddr) { case net.IPv4len: addrs[i].Type = Ipv4Address addrs[i].Value = ipaddr.String() case net.IPv6len: if ipaddr.To4() != nil { // ipaddr is an IPv4 address represented in 16 bytes. addrs[i].Type = Ipv4Address } else { addrs[i].Type = Ipv6Address } addrs[i].Value = ipaddr.String() } addrs[i].NetworkScope = deriveNetworkScope(addrs[i]) } addrs[len(addrs)-1] = hostAddr return addrs, err } // SelectPublicAddress picks one address from a slice that would // 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 := bestAddressIndex(len(addresses), func(i int) Address { return addresses[i] }, publicMatch) if index < 0 { return "" } return addresses[index].Value } func SelectPublicHostPort(hps []HostPort) string { index := bestAddressIndex(len(hps), func(i int) Address { return hps[i].Address }, publicMatch) if index < 0 { return "" } return hps[index].NetAddr() } // 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 := bestAddressIndex(len(addresses), func(i int) Address { return addresses[i] }, internalAddressMatcher(machineLocal)) if index < 0 { return "" } return addresses[index].Value } // SelectInternalAddress picks one HostPort from a slice that can be // used as an endpoint for juju internal communication // 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 := bestAddressIndex(len(hps), func(i int) Address { return hps[i].Address }, internalAddressMatcher(machineLocal)) if index < 0 { return "" } return hps[index].NetAddr() } 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 match(addr) { case exactScope: return i case fallbackScope: // Use the first fallback address if there are no exact matches. if fallbackAddressIndex == -1 { fallbackAddressIndex = i } } } } return fallbackAddressIndex } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/instance/instance.go�����������������������������������0000644�0000153�0000161�00000017115�12321735642�024631� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instance import ( "errors" "fmt" "math" "sort" "strconv" "strings" "launchpad.net/juju-core/juju/arch" ) var ErrNoDNSName = errors.New("DNS name not allocated") // An instance Id is a provider-specific identifier associated with an // instance (physical or virtual machine allocated in the provider). type Id string // Port identifies a network port number for a particular protocol. type Port struct { Protocol string Number int } func (p Port) String() string { return fmt.Sprintf("%d/%s", p.Number, p.Protocol) } // Instance represents the the realization of a machine in state. type Instance interface { // Id returns a provider-generated identifier for the Instance. Id() Id // Status returns the provider-specific status for the instance. Status() string // Refresh refreshes local knowledge of the instance from the provider. Refresh() error // Addresses returns a list of hostnames or ip addresses // associated with the instance. This will supercede DNSName // which can be implemented by selecting a preferred address. Addresses() ([]Address, error) // DNSName returns the DNS name for the instance. // If the name is not yet allocated, it will return // an ErrNoDNSName error. DNSName() (string, error) // WaitDNSName returns the DNS name for the instance, // waiting until it is allocated if necessary. // TODO: We may not need this in the interface any more. All // implementations now delegate to environs.WaitDNSName. WaitDNSName() (string, error) // OpenPorts opens the given ports on the instance, which // should have been started with the given machine id. OpenPorts(machineId string, ports []Port) error // ClosePorts closes the given ports on the instance, which // should have been started with the given machine id. ClosePorts(machineId string, ports []Port) error // Ports returns the set of ports open on the instance, which // should have been started with the given machine id. // The ports are returned as sorted by SortPorts. Ports(machineId string) ([]Port, error) } // HardwareCharacteristics represents the characteristics of the instance (if known). // Attributes that are nil are unknown or not supported. type HardwareCharacteristics struct { Arch *string `json:",omitempty" yaml:"arch,omitempty"` Mem *uint64 `json:",omitempty" yaml:"mem,omitempty"` RootDisk *uint64 `json:",omitempty" yaml:"rootdisk,omitempty"` CpuCores *uint64 `json:",omitempty" yaml:"cpucores,omitempty"` CpuPower *uint64 `json:",omitempty" yaml:"cpupower,omitempty"` Tags *[]string `json:",omitempty" yaml:"tags,omitempty"` } func uintStr(i uint64) string { if i == 0 { return "" } return fmt.Sprintf("%d", i) } func (hc HardwareCharacteristics) String() string { var strs []string if hc.Arch != nil { strs = append(strs, fmt.Sprintf("arch=%s", *hc.Arch)) } if hc.CpuCores != nil { strs = append(strs, fmt.Sprintf("cpu-cores=%d", *hc.CpuCores)) } if hc.CpuPower != nil { strs = append(strs, fmt.Sprintf("cpu-power=%d", *hc.CpuPower)) } if hc.Mem != nil { strs = append(strs, fmt.Sprintf("mem=%dM", *hc.Mem)) } if hc.RootDisk != nil { strs = append(strs, fmt.Sprintf("root-disk=%dM", *hc.RootDisk)) } if hc.Tags != nil && len(*hc.Tags) > 0 { strs = append(strs, fmt.Sprintf("tags=%s", strings.Join(*hc.Tags, ","))) } return strings.Join(strs, " ") } // Implement gnuflag.Value func (hc *HardwareCharacteristics) Set(s string) error { parsed, err := ParseHardware(s) if err != nil { return err } *hc = parsed return nil } // MustParseHardware constructs a HardwareCharacteristics from the supplied arguments, // as Parse, but panics on failure. func MustParseHardware(args ...string) HardwareCharacteristics { hc, err := ParseHardware(args...) if err != nil { panic(err) } return hc } // ParseHardware constructs a HardwareCharacteristics from the supplied arguments, // each of which must contain only spaces and name=value pairs. If any // name is specified more than once, an error is returned. func ParseHardware(args ...string) (HardwareCharacteristics, error) { hc := HardwareCharacteristics{} for _, arg := range args { raws := strings.Split(strings.TrimSpace(arg), " ") for _, raw := range raws { if raw == "" { continue } if err := hc.setRaw(raw); err != nil { return HardwareCharacteristics{}, err } } } return hc, nil } // setRaw interprets a name=value string and sets the supplied value. func (hc *HardwareCharacteristics) setRaw(raw string) error { eq := strings.Index(raw, "=") if eq <= 0 { return fmt.Errorf("malformed characteristic %q", raw) } name, str := raw[:eq], raw[eq+1:] var err error switch name { case "arch": err = hc.setArch(str) case "cpu-cores": err = hc.setCpuCores(str) case "cpu-power": err = hc.setCpuPower(str) case "mem": err = hc.setMem(str) case "root-disk": err = hc.setRootDisk(str) case "tags": err = hc.setTags(str) default: return fmt.Errorf("unknown characteristic %q", name) } if err != nil { return fmt.Errorf("bad %q characteristic: %v", name, err) } return nil } func (hc *HardwareCharacteristics) setArch(str string) error { if hc.Arch != nil { return fmt.Errorf("already set") } if str != "" && !arch.IsSupportedArch(str) { return fmt.Errorf("%q not recognized", str) } hc.Arch = &str return nil } func (hc *HardwareCharacteristics) setCpuCores(str string) (err error) { if hc.CpuCores != nil { return fmt.Errorf("already set") } hc.CpuCores, err = parseUint64(str) return } func (hc *HardwareCharacteristics) setCpuPower(str string) (err error) { if hc.CpuPower != nil { return fmt.Errorf("already set") } hc.CpuPower, err = parseUint64(str) return } func (hc *HardwareCharacteristics) setMem(str string) (err error) { if hc.Mem != nil { return fmt.Errorf("already set") } hc.Mem, err = parseSize(str) return } func (hc *HardwareCharacteristics) setRootDisk(str string) (err error) { if hc.RootDisk != nil { return fmt.Errorf("already set") } hc.RootDisk, err = parseSize(str) return } func (hc *HardwareCharacteristics) setTags(str string) (err error) { if hc.Tags != nil { return fmt.Errorf("already set") } hc.Tags = parseTags(str) return } // parseTags returns the tags in the value s func parseTags(s string) *[]string { if s == "" { return &[]string{} } tags := strings.Split(s, ",") return &tags } func parseUint64(str string) (*uint64, error) { var value uint64 if str != "" { if val, err := strconv.ParseUint(str, 10, 64); err != nil { return nil, fmt.Errorf("must be a non-negative integer") } else { value = uint64(val) } } return &value, nil } func parseSize(str string) (*uint64, error) { var value uint64 if str != "" { mult := 1.0 if m, ok := mbSuffixes[str[len(str)-1:]]; ok { str = str[:len(str)-1] mult = m } val, err := strconv.ParseFloat(str, 64) if err != nil || val < 0 { return nil, fmt.Errorf("must be a non-negative float with optional M/G/T/P suffix") } val *= mult value = uint64(math.Ceil(val)) } return &value, nil } var mbSuffixes = map[string]float64{ "M": 1, "G": 1024, "T": 1024 * 1024, "P": 1024 * 1024 * 1024, } type portSlice []Port func (p portSlice) Len() int { return len(p) } func (p portSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p portSlice) Less(i, j int) bool { p1 := p[i] p2 := p[j] if p1.Protocol != p2.Protocol { return p1.Protocol < p2.Protocol } return p1.Number < p2.Number } // SortPorts sorts the given ports, first by protocol, // then by number. func SortPorts(ports []Port) { sort.Sort(portSlice(ports)) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/instance/instance_test.go������������������������������0000644�0000153�0000161�00000017004�12321735642�025665� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instance_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" ) type HardwareSuite struct{} var _ = gc.Suite(&HardwareSuite{}) var parseHardwareTests = []struct { summary string args []string err string }{ // Simple errors. { summary: "nothing at all", }, { summary: "empty", args: []string{" "}, }, { summary: "complete nonsense", args: []string{"cheese"}, err: `malformed characteristic "cheese"`, }, { summary: "missing name", args: []string{"=cheese"}, err: `malformed characteristic "=cheese"`, }, { summary: "unknown characteristic", args: []string{"cheese=edam"}, err: `unknown characteristic "cheese"`, }, // "arch" in detail. { summary: "set arch empty", args: []string{"arch="}, }, { summary: "set arch amd64", args: []string{"arch=amd64"}, }, { summary: "set arch i386", args: []string{"arch=i386"}, }, { summary: "set arch armhf", args: []string{"arch=armhf"}, }, { summary: "set nonsense arch 1", args: []string{"arch=cheese"}, err: `bad "arch" characteristic: "cheese" not recognized`, }, { summary: "set nonsense arch 2", args: []string{"arch=123.45"}, err: `bad "arch" characteristic: "123.45" not recognized`, }, { summary: "double set arch together", args: []string{"arch=amd64 arch=amd64"}, err: `bad "arch" characteristic: already set`, }, { summary: "double set arch separately", args: []string{"arch=armhf", "arch="}, err: `bad "arch" characteristic: already set`, }, // "cpu-cores" in detail. { summary: "set cpu-cores empty", args: []string{"cpu-cores="}, }, { summary: "set cpu-cores zero", args: []string{"cpu-cores=0"}, }, { summary: "set cpu-cores", args: []string{"cpu-cores=4"}, }, { summary: "set nonsense cpu-cores 1", args: []string{"cpu-cores=cheese"}, err: `bad "cpu-cores" characteristic: must be a non-negative integer`, }, { summary: "set nonsense cpu-cores 2", args: []string{"cpu-cores=-1"}, err: `bad "cpu-cores" characteristic: must be a non-negative integer`, }, { summary: "set nonsense cpu-cores 3", args: []string{"cpu-cores=123.45"}, err: `bad "cpu-cores" characteristic: must be a non-negative integer`, }, { summary: "double set cpu-cores together", args: []string{"cpu-cores=128 cpu-cores=1"}, err: `bad "cpu-cores" characteristic: already set`, }, { summary: "double set cpu-cores separately", args: []string{"cpu-cores=128", "cpu-cores=1"}, err: `bad "cpu-cores" characteristic: already set`, }, // "cpu-power" in detail. { summary: "set cpu-power empty", args: []string{"cpu-power="}, }, { summary: "set cpu-power zero", args: []string{"cpu-power=0"}, }, { summary: "set cpu-power", args: []string{"cpu-power=44"}, }, { summary: "set nonsense cpu-power 1", args: []string{"cpu-power=cheese"}, err: `bad "cpu-power" characteristic: must be a non-negative integer`, }, { summary: "set nonsense cpu-power 2", args: []string{"cpu-power=-1"}, err: `bad "cpu-power" characteristic: must be a non-negative integer`, }, { summary: "double set cpu-power together", args: []string{" cpu-power=300 cpu-power=1700 "}, err: `bad "cpu-power" characteristic: already set`, }, { summary: "double set cpu-power separately", args: []string{"cpu-power=300 ", " cpu-power=1700"}, err: `bad "cpu-power" characteristic: already set`, }, // "mem" in detail. { summary: "set mem empty", args: []string{"mem="}, }, { summary: "set mem zero", args: []string{"mem=0"}, }, { summary: "set mem without suffix", args: []string{"mem=512"}, }, { summary: "set mem with M suffix", args: []string{"mem=512M"}, }, { summary: "set mem with G suffix", args: []string{"mem=1.5G"}, }, { summary: "set mem with T suffix", args: []string{"mem=36.2T"}, }, { summary: "set mem with P suffix", args: []string{"mem=18.9P"}, }, { summary: "set nonsense mem 1", args: []string{"mem=cheese"}, err: `bad "mem" characteristic: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "set nonsense mem 2", args: []string{"mem=-1"}, err: `bad "mem" characteristic: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "set nonsense mem 3", args: []string{"mem=32Y"}, err: `bad "mem" characteristic: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "double set mem together", args: []string{"mem=1G mem=2G"}, err: `bad "mem" characteristic: already set`, }, { summary: "double set mem separately", args: []string{"mem=1G", "mem=2G"}, err: `bad "mem" characteristic: already set`, }, // "root-disk" in detail. { summary: "set root-disk empty", args: []string{"root-disk="}, }, { summary: "set root-disk zero", args: []string{"root-disk=0"}, }, { summary: "set root-disk without suffix", args: []string{"root-disk=512"}, }, { summary: "set root-disk with M suffix", args: []string{"root-disk=512M"}, }, { summary: "set root-disk with G suffix", args: []string{"root-disk=1.5G"}, }, { summary: "set root-disk with T suffix", args: []string{"root-disk=36.2T"}, }, { summary: "set root-disk with P suffix", args: []string{"root-disk=18.9P"}, }, { summary: "set nonsense root-disk 1", args: []string{"root-disk=cheese"}, err: `bad "root-disk" characteristic: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "set nonsense root-disk 2", args: []string{"root-disk=-1"}, err: `bad "root-disk" characteristic: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "set nonsense root-disk 3", args: []string{"root-disk=32Y"}, err: `bad "root-disk" characteristic: must be a non-negative float with optional M/G/T/P suffix`, }, { summary: "double set root-disk together", args: []string{"root-disk=1G root-disk=2G"}, err: `bad "root-disk" characteristic: already set`, }, { summary: "double set root-disk separately", args: []string{"root-disk=1G", "root-disk=2G"}, err: `bad "root-disk" characteristic: already set`, }, // Everything at once. { summary: "kitchen sink together", args: []string{" root-disk=4G mem=2T arch=i386 cpu-cores=4096 cpu-power=9001"}, }, { summary: "kitchen sink separately", args: []string{"root-disk=4G", "mem=2T", "cpu-cores=4096", "cpu-power=9001", "arch=armhf"}, }, } func (s *HardwareSuite) TestParseHardware(c *gc.C) { for i, t := range parseHardwareTests { c.Logf("test %d: %s", i, t.summary) hwc, err := instance.ParseHardware(t.args...) if t.err == "" { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, t.err) continue } cons1, err := instance.ParseHardware(hwc.String()) c.Assert(err, gc.IsNil) c.Assert(cons1, gc.DeepEquals, hwc) } } type PortsSuite struct{} var _ = gc.Suite(&PortsSuite{}) var sortPortsTests = []struct { have, want []instance.Port }{ {nil, []instance.Port{}}, {[]instance.Port{{"b", 1}, {"a", 99}, {"a", 1}}, []instance.Port{{"a", 1}, {"a", 99}, {"b", 1}}}, } func (*PortsSuite) TestSortPorts(c *gc.C) { for _, t := range sortPortsTests { p := make([]instance.Port, len(t.have)) copy(p, t.have) instance.SortPorts(p) c.Check(p, gc.DeepEquals, t.want) instance.SortPorts(p) c.Check(p, gc.DeepEquals, t.want) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/instance/address_test.go�������������������������������0000644�0000153�0000161�00000022353�12321735776�025521� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instance import ( "errors" "net" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) type AddressSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&AddressSuite{}) func (s *AddressSuite) TestNewAddressIpv4(c *gc.C) { 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) { addresses := NewAddresses( []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) { addr := NewAddress("localhost") c.Check(addr.Value, gc.Equals, "localhost") c.Check(addr.Type, gc.Equals, HostName) } type selectTest struct { about string addresses []Address expectedIndex int } // expected returns the expected address for the test. func (t selectTest) expected() string { if t.expectedIndex == -1 { return "" } return t.addresses[t.expectedIndex].Value } type hostPortTest struct { about string hostPorts []HostPort expectedIndex int } // hostPortTest returns the HostPort equivalent test to the // receiving selectTest. func (t selectTest) hostPortTest() hostPortTest { hps := AddressesWithPort(t.addresses, 9999) for i := range hps { hps[i].Port = i + 1 } return hostPortTest{ about: t.about, hostPorts: hps, expectedIndex: t.expectedIndex, } } // expected returns the expected host:port result // of the test. func (t hostPortTest) expected() string { if t.expectedIndex == -1 { return "" } return t.hostPorts[t.expectedIndex].NetAddr() } var selectPublicTests = []selectTest{{ "no addresses gives empty string result", []Address{}, -1, }, { "a public address is selected", []Address{ {"8.8.8.8", Ipv4Address, "public", NetworkPublic}, }, 0, }, { "a machine local address is not selected", []Address{ {"127.0.0.1", Ipv4Address, "machine", NetworkMachineLocal}, }, -1, }, { "an ipv6 address is not selected", []Address{ {"2001:DB8::1", Ipv6Address, "", NetworkPublic}, }, -1, }, { "a public name is preferred to an unknown or cloud local address", []Address{ {"127.0.0.1", Ipv4Address, "local", NetworkUnknown}, {"10.0.0.1", Ipv4Address, "cloud", NetworkCloudLocal}, {"public.invalid.testing", HostName, "public", NetworkPublic}, }, 2, }, { "first unknown address selected", []Address{ {"10.0.0.1", Ipv4Address, "cloud", NetworkUnknown}, {"8.8.8.8", Ipv4Address, "floating", NetworkUnknown}, }, 0, }} func (s *AddressSuite) TestSelectPublicAddress(c *gc.C) { for i, t := range selectPublicTests { c.Logf("test %d. %s", i, t.about) c.Check(SelectPublicAddress(t.addresses), gc.Equals, t.expected()) } } func (s *AddressSuite) TestSelectPublicHostPort(c *gc.C) { for i, t0 := range selectPublicTests { t := t0.hostPortTest() c.Logf("test %d. %s", i, t.about) c.Assert(SelectPublicHostPort(t.hostPorts), gc.DeepEquals, t.expected()) } } var selectInternalTests = []selectTest{{ "no addresses gives empty string result", []Address{}, -1, }, { "a public address is selected", []Address{ {"8.8.8.8", Ipv4Address, "public", NetworkPublic}, }, 0, }, { "a cloud local address is selected", []Address{ {"10.0.0.1", Ipv4Address, "private", NetworkCloudLocal}, }, 0, }, { "a machine local address is not selected", []Address{ {"127.0.0.1", Ipv4Address, "machine", NetworkMachineLocal}, }, -1, }, { "ipv6 addresses are not selected", []Address{ {"::1", Ipv6Address, "", NetworkCloudLocal}, }, -1, }, { "a cloud local address is preferred to a public address", []Address{ {"10.0.0.1", Ipv4Address, "cloud", NetworkCloudLocal}, {"8.8.8.8", Ipv4Address, "public", NetworkPublic}, }, 0, }} func (s *AddressSuite) TestSelectInternalAddress(c *gc.C) { for i, t := range selectInternalTests { c.Logf("test %d. %s", i, t.about) c.Check(SelectInternalAddress(t.addresses, false), gc.Equals, t.expected()) } } func (s *AddressSuite) TestSelectInternalHostPort(c *gc.C) { for i, t0 := range selectInternalTests { t := t0.hostPortTest() c.Logf("test %d. %s", i, t.about) c.Assert(SelectInternalHostPort(t.hostPorts, false), gc.DeepEquals, t.expected()) } } var selectInternalMachineTests = []selectTest{{ "a cloud local address is selected", []Address{ {"10.0.0.1", Ipv4Address, "cloud", NetworkCloudLocal}, }, 0, }, { "a machine local address is selected", []Address{ {"127.0.0.1", Ipv4Address, "container", NetworkMachineLocal}, }, 0, }} func (s *AddressSuite) TestSelectInternalMachineAddress(c *gc.C) { for i, t := range selectInternalMachineTests { c.Logf("test %d. %s", i, t.about) c.Check(SelectInternalAddress(t.addresses, true), gc.Equals, t.expected()) } } func (s *AddressSuite) TestSelectInternalMachineHostPort(c *gc.C) { for i, t0 := range selectInternalMachineTests { t := t0.hostPortTest() c.Logf("test %d. %s", i, t.about) c.Assert(SelectInternalHostPort(t.hostPorts, true), gc.DeepEquals, t.expected()) } } func (s *AddressSuite) TestHostAddresses(c *gc.C) { // Mock the call to net.LookupIP made from HostAddresses. var lookupIPs []net.IP var lookupErr error lookupIP := func(addr string) ([]net.IP, error) { return append([]net.IP{}, lookupIPs...), lookupErr } s.PatchValue(&netLookupIP, lookupIP) // err is only non-nil if net.LookupIP fails. addrs, err := HostAddresses("") c.Assert(err, gc.IsNil) // addrs always contains the input address. c.Assert(addrs, gc.HasLen, 1) c.Assert(addrs[0], gc.Equals, NewAddress("")) loopback := net.ParseIP("127.0.0.1").To4() lookupIPs = []net.IP{net.IPv6loopback, net.IPv4zero, loopback} addrs, err = HostAddresses("localhost") c.Assert(err, gc.IsNil) c.Assert(addrs, gc.HasLen, 4) c.Assert(addrs[0], gc.Equals, NewAddress(net.IPv6loopback.String())) c.Assert(addrs[1], gc.Equals, NewAddress(net.IPv4zero.String())) c.Assert(addrs[2], gc.Equals, NewAddress(loopback.String())) c.Assert(addrs[3], gc.Equals, NewAddress("localhost")) lookupErr = errors.New("what happened?") addrs, err = HostAddresses("localhost") c.Assert(err, gc.Equals, lookupErr) // If the input address is an IP, the call to net.LookupIP is elided. addrs, err = HostAddresses("127.0.0.1") c.Assert(err, gc.IsNil) c.Assert(addrs, gc.HasLen, 1) c.Assert(addrs[0], gc.Equals, NewAddress("127.0.0.1")) } var stringTests = []struct { addr Address str string }{{ addr: Address{ Type: Ipv4Address, Value: "127.0.0.1", }, str: "127.0.0.1", }, { addr: Address{ Type: HostName, Value: "foo.com", }, str: "foo.com", }, { addr: Address{ Type: HostName, Value: "foo.com", NetworkScope: NetworkUnknown, }, str: "foo.com", }, { addr: Address{ Type: HostName, Value: "foo.com", NetworkScope: NetworkPublic, }, str: "public:foo.com", }, { addr: Address{ Type: HostName, Value: "foo.com", NetworkScope: NetworkPublic, NetworkName: "netname", }, str: "public:foo.com(netname)", }} func (s *AddressSuite) TestString(c *gc.C) { for _, test := range stringTests { c.Check(test.addr.String(), gc.Equals, test.str) } } func (*AddressSuite) TestAddressesWithPort(c *gc.C) { addrs := NewAddresses([]string{"0.1.2.3", "0.2.4.6"}) hps := AddressesWithPort(addrs, 999) c.Assert(hps, jc.DeepEquals, []HostPort{{ Address: NewAddress("0.1.2.3"), Port: 999, }, { Address: NewAddress("0.2.4.6"), Port: 999, }}) } var netAddrTests = []struct { addr Address port int expect string }{{ addr: NewAddress("0.1.2.3"), port: 99, expect: "0.1.2.3:99", }, { addr: NewAddress("2001:DB8::1"), port: 100, expect: "[2001:DB8::1]:100", }} func (*AddressSuite) TestNetAddr(c *gc.C) { for i, test := range netAddrTests { c.Logf("test %d: %q", i, test.addr) hp := HostPort{ Address: test.addr, Port: test.port, } c.Assert(hp.NetAddr(), gc.Equals, test.expect) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/instance/container_test.go�����������������������������0000644�0000153�0000161�00000002575�12321735642�026052� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package instance_test import ( "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" ) func TestPackage(t *testing.T) { gc.TestingT(t) } type InstanceSuite struct{} var _ = gc.Suite(&InstanceSuite{}) func (s *InstanceSuite) TestParseContainerType(c *gc.C) { ctype, err := instance.ParseContainerType("lxc") c.Assert(err, gc.IsNil) c.Assert(ctype, gc.Equals, instance.LXC) ctype, err = instance.ParseContainerType("kvm") c.Assert(err, gc.IsNil) c.Assert(ctype, gc.Equals, instance.KVM) ctype, err = instance.ParseContainerType("none") c.Assert(err, gc.ErrorMatches, `invalid container type "none"`) ctype, err = instance.ParseContainerType("omg") c.Assert(err, gc.ErrorMatches, `invalid container type "omg"`) } func (s *InstanceSuite) TestParseContainerTypeOrNone(c *gc.C) { ctype, err := instance.ParseContainerTypeOrNone("lxc") c.Assert(err, gc.IsNil) c.Assert(ctype, gc.Equals, instance.LXC) ctype, err = instance.ParseContainerTypeOrNone("kvm") c.Assert(err, gc.IsNil) c.Assert(ctype, gc.Equals, instance.KVM) ctype, err = instance.ParseContainerTypeOrNone("none") c.Assert(err, gc.IsNil) c.Assert(ctype, gc.Equals, instance.NONE) ctype, err = instance.ParseContainerTypeOrNone("omg") c.Assert(err, gc.ErrorMatches, `invalid container type "omg"`) } �����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/instance/testing/��������������������������������������0000755�0000153�0000161�00000000000�12321735642�024146� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/instance/testing/instance.go���������������������������0000644�0000153�0000161�00000001365�12321735642�026306� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" ) // MatchInstances uses DeepEquals to check the instances returned. The lists // are first put into a map, so the ordering of the result and expected values // is not tested, and duplicates are ignored. func MatchInstances(c *gc.C, result []instance.Instance, expected ...instance.Instance) { resultMap := make(map[instance.Id]instance.Instance) for _, i := range result { resultMap[i.Id()] = i } expectedMap := make(map[instance.Id]instance.Instance) for _, i := range expected { expectedMap[i.Id()] = i } c.Assert(resultMap, gc.DeepEquals, expectedMap) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/bzr/���������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�021447� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/bzr/bzr.go���������������������������������������������0000644�0000153�0000161�00000010473�12321735642�022613� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // Package bzr offers an interface to manage branches of the Bazaar VCS. package bzr import ( "bytes" "fmt" "os" "os/exec" "path" "strings" ) // Branch represents a Bazaar branch. type Branch struct { location string env []string } // New returns a new Branch for the Bazaar branch at location. func New(location string) *Branch { b := &Branch{location, cenv()} if _, err := os.Stat(location); err == nil { stdout, _, err := b.bzr("root") if err == nil { b.location = strings.TrimRight(string(stdout), "\n") } } return b } // cenv returns a copy of the current process environment with LC_ALL=C. func cenv() []string { env := os.Environ() for i, pair := range env { if strings.HasPrefix(pair, "LC_ALL=") { env[i] = "LC_ALL=C" return env } } return append(env, "LC_ALL=C") } // Location returns the location of branch b. func (b *Branch) Location() string { return b.location } // Join returns b's location with parts appended as path components. // In other words, if b's location is "lp:foo", and parts is {"bar, baz"}, // Join returns "lp:foo/bar/baz". func (b *Branch) Join(parts ...string) string { return path.Join(append([]string{b.location}, parts...)...) } func (b *Branch) bzr(subcommand string, args ...string) (stdout, stderr []byte, err error) { cmd := exec.Command("bzr", append([]string{subcommand}, args...)...) if _, err := os.Stat(b.location); err == nil { cmd.Dir = b.location } errbuf := &bytes.Buffer{} cmd.Stderr = errbuf cmd.Env = b.env stdout, err = cmd.Output() // Some commands fail with exit status 0 (e.g. bzr root). :-( if err != nil || bytes.Contains(errbuf.Bytes(), []byte("ERROR")) { var errmsg string if err != nil { errmsg = err.Error() } return nil, nil, fmt.Errorf(`error running "bzr %s": %s%s%s`, subcommand, stdout, errbuf.Bytes(), errmsg) } return stdout, errbuf.Bytes(), err } // Init intializes a new branch at b's location. func (b *Branch) Init() error { _, _, err := b.bzr("init", b.location) return err } // Add adds to b the path resultant from calling b.Join(parts...). func (b *Branch) Add(parts ...string) error { _, _, err := b.bzr("add", b.Join(parts...)) return err } // Commit commits pending changes into b. func (b *Branch) Commit(message string) error { _, _, err := b.bzr("commit", "-q", "-m", message) return err } // RevisionId returns the Bazaar revision id for the tip of b. func (b *Branch) RevisionId() (string, error) { stdout, stderr, err := b.bzr("revision-info", "-d", b.location) if err != nil { return "", err } pair := bytes.Fields(stdout) if len(pair) != 2 { return "", fmt.Errorf(`invalid output from "bzr revision-info": %s%s`, stdout, stderr) } id := string(pair[1]) if id == "null:" { return "", fmt.Errorf("branch has no content") } return id, nil } // PushLocation returns the default push location for b. func (b *Branch) PushLocation() (string, error) { stdout, _, err := b.bzr("info", b.location) if err != nil { return "", err } if i := bytes.Index(stdout, []byte("push branch:")); i >= 0 { return string(stdout[i+13 : i+bytes.IndexAny(stdout[i:], "\r\n")]), nil } return "", fmt.Errorf("no push branch location defined") } // PushAttr holds options for the Branch.Push method. type PushAttr struct { Location string // Location to push to. Use the default push location if empty. Remember bool // Whether to remember the location being pushed to as the default. } // Push pushes any new revisions in b to attr.Location if that's // provided, or to the default push location otherwise. // See PushAttr for other options. func (b *Branch) Push(attr *PushAttr) error { var args []string if attr != nil { if attr.Remember { args = append(args, "--remember") } if attr.Location != "" { args = append(args, attr.Location) } } _, _, err := b.bzr("push", args...) return err } // CheckClean returns an error if 'bzr status' is not clean. func (b *Branch) CheckClean() error { stdout, _, err := b.bzr("status", b.location) if err != nil { return err } if bytes.Count(stdout, []byte{'\n'}) == 1 && bytes.Contains(stdout, []byte(`See "bzr shelve --list" for details.`)) { return nil // Shelves are fine. } if len(stdout) > 0 { return fmt.Errorf("branch is not clean (bzr status)") } return nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/bzr/bzr_test.go����������������������������������������0000644�0000153�0000161�00000006636�12321735776�023670� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package bzr_test import ( "os" "os/exec" "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/bzr" ) func Test(t *testing.T) { gc.TestingT(t) } var _ = gc.Suite(&BzrSuite{}) type BzrSuite struct { b *bzr.Branch } func (s *BzrSuite) SetUpTest(c *gc.C) { s.b = bzr.New(c.MkDir()) c.Assert(s.b.Init(), gc.IsNil) } func (s *BzrSuite) TestNewFindsRoot(c *gc.C) { err := os.Mkdir(s.b.Join("dir"), 0755) c.Assert(err, gc.IsNil) b := bzr.New(s.b.Join("dir")) c.Assert(b.Location(), gc.Equals, s.b.Location()) } func (s *BzrSuite) TestJoin(c *gc.C) { path := bzr.New("lp:foo").Join("baz", "bar") c.Assert(path, gc.Equals, "lp:foo/baz/bar") } func (s *BzrSuite) TestErrorHandling(c *gc.C) { err := bzr.New("/non/existent/path").Init() c.Assert(err, gc.ErrorMatches, `(?s)error running "bzr init":.*does not exist.*`) } func (s *BzrSuite) TestInit(c *gc.C) { _, err := os.Stat(s.b.Join(".bzr")) c.Assert(err, gc.IsNil) } func (s *BzrSuite) TestRevisionIdOnEmpty(c *gc.C) { revid, err := s.b.RevisionId() c.Assert(err, gc.ErrorMatches, "branch has no content") c.Assert(revid, gc.Equals, "") } func (s *BzrSuite) TestCommit(c *gc.C) { f, err := os.Create(s.b.Join("myfile")) c.Assert(err, gc.IsNil) f.Close() err = s.b.Add("myfile") c.Assert(err, gc.IsNil) err = s.b.Commit("my log message") c.Assert(err, gc.IsNil) revid, err := s.b.RevisionId() c.Assert(err, gc.IsNil) cmd := exec.Command("bzr", "log", "--long", "--show-ids", "-v", s.b.Location()) output, err := cmd.CombinedOutput() c.Assert(err, gc.IsNil) c.Assert(string(output), gc.Matches, "(?s).*revision-id: "+revid+"\n.*message:\n.*my log message\n.*added:\n.*myfile .*") } func (s *BzrSuite) TestPush(c *gc.C) { b1 := bzr.New(c.MkDir()) b2 := bzr.New(c.MkDir()) b3 := bzr.New(c.MkDir()) c.Assert(b1.Init(), gc.IsNil) c.Assert(b2.Init(), gc.IsNil) c.Assert(b3.Init(), gc.IsNil) // Create and add b1/file to the branch. f, err := os.Create(b1.Join("file")) c.Assert(err, gc.IsNil) f.Close() err = b1.Add("file") c.Assert(err, gc.IsNil) err = b1.Commit("added file") c.Assert(err, gc.IsNil) // Push file to b2. err = b1.Push(&bzr.PushAttr{Location: b2.Location()}) c.Assert(err, gc.IsNil) // Push location should be set to b2. location, err := b1.PushLocation() c.Assert(err, gc.IsNil) c.Assert(location, gc.Equals, b2.Location()) // Now push it to b3. err = b1.Push(&bzr.PushAttr{Location: b3.Location()}) c.Assert(err, gc.IsNil) // Push location is still set to b2. location, err = b1.PushLocation() c.Assert(err, gc.IsNil) c.Assert(location, gc.Equals, b2.Location()) // Push it again, this time with the remember flag set. err = b1.Push(&bzr.PushAttr{Location: b3.Location(), Remember: true}) c.Assert(err, gc.IsNil) // Now the push location has shifted to b3. location, err = b1.PushLocation() c.Assert(err, gc.IsNil) c.Assert(location, gc.Equals, b3.Location()) // Both b2 and b3 should have the file. _, err = os.Stat(b2.Join("file")) c.Assert(err, gc.IsNil) _, err = os.Stat(b3.Join("file")) c.Assert(err, gc.IsNil) } func (s *BzrSuite) TestCheckClean(c *gc.C) { err := s.b.CheckClean() c.Assert(err, gc.IsNil) // Create and add b1/file to the branch. f, err := os.Create(s.b.Join("file")) c.Assert(err, gc.IsNil) f.Close() err = s.b.CheckClean() c.Assert(err, gc.ErrorMatches, `branch is not clean \(bzr status\)`) } ��������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/errors/������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022166� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/errors/errors_test.go����������������������������������0000644�0000153�0000161�00000004520�12321735776�025114� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package errors_test import ( stderrors "errors" "reflect" "runtime" "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" ) type errorsSuite struct{} var _ = gc.Suite(&errorsSuite{}) func Test(t *testing.T) { gc.TestingT(t) } type errorSatisfier struct { f func(error) bool } func (s *errorSatisfier) String() string { value := reflect.ValueOf(s.f) f := runtime.FuncForPC(value.Pointer()) return f.Name() } func (*errorsSuite) TestNotFoundError(c *gc.C) { isNotFoundError := &errorSatisfier{errors.IsNotFoundError} isUnauthorizedError := &errorSatisfier{errors.IsUnauthorizedError} isNotImplementedError := &errorSatisfier{errors.IsNotImplementedError} satisfiers := []*errorSatisfier{ isNotFoundError, isUnauthorizedError, isNotImplementedError, } // make some errors, and record the errorSatsifier // that should satisfy the error. type errorTest struct { err error message string satisfier *errorSatisfier } errorTests := []errorTest{{ errors.NotFoundf("woop %d", 123), "woop 123 not found", isNotFoundError, }, { errors.NewNotFoundError(stderrors.New("woo"), "msg"), "msg: woo", isNotFoundError, }, { errors.NewNotFoundError(stderrors.New("woo"), ""), "woo", isNotFoundError, }, { errors.NewNotFoundError(nil, "msg"), "msg", isNotFoundError, }, { errors.NewNotFoundError(nil, ""), "", isNotFoundError, }, { errors.Unauthorizedf("woo %s", "hoo"), "woo hoo", isUnauthorizedError, }, { errors.NewUnauthorizedError(stderrors.New("hoo"), "woo"), "woo: hoo", isUnauthorizedError, }, { errors.NewNotImplementedError("something"), "something not implemented", isNotImplementedError, }} for i, t := range errorTests { c.Logf("test #%d: %v", i, t.err) c.Assert(t.err, gc.ErrorMatches, t.message) c.Assert(t.err, jc.Satisfies, t.satisfier.f) for _, satisfier := range satisfiers { // Not using jc.Satisfier here, because it doesn't give // a nice string representation of the function. Also, // you can't take the address of a func, but you can // store it and take the address of the struct. if satisfier != t.satisfier && satisfier.f(t.err) { c.Errorf("%#v satisfies %v", t.err, satisfier) } } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/errors/errors.go���������������������������������������0000644�0000153�0000161�00000005370�12321735776�024061� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package errors import ( "fmt" ) // errorWrapper defines a way to encapsulate an error inside another error. type errorWrapper struct { // Err is the underlying error. Err error Msg string } // Error implements the error interface. func (e *errorWrapper) Error() string { if e.Msg != "" || e.Err == nil { if e.Err != nil { return fmt.Sprintf("%s: %v", e.Msg, e.Err.Error()) } return e.Msg } return e.Err.Error() } // notFoundError records an error when something has not been found. type notFoundError struct { *errorWrapper } // IsNotFoundError is satisfied by errors created by this package representing // resources that can't be found. func IsNotFoundError(err error) bool { if _, ok := err.(notFoundError); ok { return true } return false } // NotFoundf returns a error which satisfies IsNotFoundError(). // The message for the error is made up from the given // arguments formatted as with fmt.Sprintf, with the // string " not found" appended. func NotFoundf(format string, args ...interface{}) error { return notFoundError{ &errorWrapper{ Msg: fmt.Sprintf(format+" not found", args...), }, } } // NewNotFoundError returns a new error wrapping err that satisfies // IsNotFoundError(). func NewNotFoundError(err error, msg string) error { return notFoundError{&errorWrapper{Err: err, Msg: msg}} } // unauthorizedError represents the error that an operation is unauthorized. // Use IsUnauthorized() to determine if the error was related to authorization // failure. type unauthorizedError struct { *errorWrapper } // IsUnauthorizedError is satisfied by errors created by this package // representing authorization failures. func IsUnauthorizedError(err error) bool { _, ok := err.(unauthorizedError) return ok } // Unauthorizedf returns an error which satisfies IsUnauthorizedError(). func Unauthorizedf(format string, args ...interface{}) error { return unauthorizedError{ &errorWrapper{ Msg: fmt.Sprintf(format, args...), }, } } // NewUnauthorizedError returns an error which wraps err and satisfies // IsUnauthorized(). func NewUnauthorizedError(err error, msg string) error { return unauthorizedError{&errorWrapper{Err: err, Msg: msg}} } type notImplementedError struct { what string } // NewNotImplementedError returns an error signifying that // something is not implemented. func NewNotImplementedError(what string) error { return &notImplementedError{what: what} } func (e *notImplementedError) Error() string { return e.what + " not implemented" } // IsNotImplementedError reports whether the error // was created with NewNotImplementedError. func IsNotImplementedError(err error) bool { _, ok := err.(*notImplementedError) return ok } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/�������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022006� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/export_test.go�����������������������������������0000644�0000153�0000161�00000000213�12321735642�024724� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store var TimeToStamp = timeToStamp �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/config_test.go�����������������������������������0000644�0000153�0000161�00000001632�12321735642�024656� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store_test import ( "fmt" "os" "path" gc "launchpad.net/gocheck" "launchpad.net/juju-core/store" "launchpad.net/juju-core/testing/testbase" ) type ConfigSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&ConfigSuite{}) const testConfig = ` mongo-url: localhost:23456 foo: 1 bar: false ` func (s *ConfigSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) } func (s *ConfigSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) } func (s *ConfigSuite) TestReadConfig(c *gc.C) { confDir := c.MkDir() f, err := os.Create(path.Join(confDir, "charmd.conf")) c.Assert(err, gc.IsNil) cfgPath := f.Name() { defer f.Close() fmt.Fprint(f, testConfig) } dstr, err := store.ReadConfig(cfgPath) c.Assert(err, gc.IsNil) c.Assert(dstr.MongoURL, gc.Equals, "localhost:23456") } ������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/config.go����������������������������������������0000644�0000153�0000161�00000001302�12321735642�023611� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store import ( "fmt" "io/ioutil" "os" "launchpad.net/goyaml" ) type Config struct { MongoURL string `yaml:"mongo-url"` APIAddr string `yaml:"api-addr"` } func ReadConfig(path string) (*Config, error) { f, err := os.Open(path) if err != nil { return nil, fmt.Errorf("opening config file: %v", err) } defer f.Close() data, err := ioutil.ReadAll(f) if err != nil { return nil, fmt.Errorf("reading config file: %v", err) } conf := new(Config) err = goyaml.Unmarshal(data, conf) if err != nil { return nil, fmt.Errorf("processing config file: %v", err) } return conf, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/branch_test.go�����������������������������������0000644�0000153�0000161�00000016021�12321735642�024644� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store_test import ( "bytes" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strings" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/store" "launchpad.net/juju-core/testing" ) func (s *StoreSuite) dummyBranch(c *gc.C, suffix string) bzrDir { tmpDir := c.MkDir() if suffix != "" { tmpDir = filepath.Join(tmpDir, suffix) err := os.MkdirAll(tmpDir, 0755) c.Assert(err, gc.IsNil) } branch := bzrDir(tmpDir) branch.init() copyCharmDir(branch.path(), testing.Charms.Dir("dummy")) branch.add() branch.commit("Imported charm.") return branch } var urls = []*charm.URL{ charm.MustParseURL("cs:~joe/oneiric/dummy"), charm.MustParseURL("cs:oneiric/dummy"), } type fakePlugin struct { oldEnv string } func (p *fakePlugin) install(dir string, content string) { p.oldEnv = os.Getenv("BZR_PLUGINS_AT") err := ioutil.WriteFile(filepath.Join(dir, "__init__.py"), []byte(content), 0644) if err != nil { panic(err) } os.Setenv("BZR_PLUGINS_AT", "fakePlugin@"+dir) } func (p *fakePlugin) uninstall() { os.Setenv("BZR_PLUGINS_AT", p.oldEnv) } func (s *StoreSuite) TestPublish(c *gc.C) { branch := s.dummyBranch(c, "") // Ensure that the streams are parsed separately by inserting // garbage on stderr. The wanted information is still there. plugin := fakePlugin{} plugin.install(c.MkDir(), `import sys; sys.stderr.write("STDERR STUFF FROM TEST\n")`) defer plugin.uninstall() err := store.PublishBazaarBranch(s.store, urls, branch.path(), "wrong-rev") c.Assert(err, gc.IsNil) for _, url := range urls { info, rc, err := s.store.OpenCharm(url) c.Assert(err, gc.IsNil) defer rc.Close() c.Assert(info.Revision(), gc.Equals, 0) c.Assert(info.Meta().Name, gc.Equals, "dummy") data, err := ioutil.ReadAll(rc) c.Assert(err, gc.IsNil) bundle, err := charm.ReadBundleBytes(data) c.Assert(err, gc.IsNil) c.Assert(bundle.Revision(), gc.Equals, 0) c.Assert(bundle.Meta().Name, gc.Equals, "dummy") } // Attempt to publish the same content again while providing the wrong // tip revision. It must pick the real revision from the branch and // note this was previously published. err = store.PublishBazaarBranch(s.store, urls, branch.path(), "wrong-rev") c.Assert(err, gc.Equals, store.ErrRedundantUpdate) // Bump the content revision and lie again about the known tip revision. // This time, though, pretend it's the same as the real branch revision // previously published. It must error and not publish the new revision // because it will use the revision provided as a parameter to check if // publishing was attempted before. This is the mechanism that enables // stopping fast without having to download every single branch. Real // revision is picked in the next scan. digest1 := branch.digest() branch.change() err = store.PublishBazaarBranch(s.store, urls, branch.path(), digest1) c.Assert(err, gc.Equals, store.ErrRedundantUpdate) // Now allow it to publish the new content by providing an unseen revision. err = store.PublishBazaarBranch(s.store, urls, branch.path(), "wrong-rev") c.Assert(err, gc.IsNil) digest2 := branch.digest() info, err := s.store.CharmInfo(urls[0]) c.Assert(err, gc.IsNil) c.Assert(info.Revision(), gc.Equals, 1) c.Assert(info.Meta().Name, gc.Equals, "dummy") // There are two events published, for each of the successful attempts. // The failures are ignored given that they are artifacts of the // publishing mechanism rather than actual problems. _, err = s.store.CharmEvent(urls[0], "wrong-rev") c.Assert(err, gc.Equals, store.ErrNotFound) for i, digest := range []string{digest1, digest2} { event, err := s.store.CharmEvent(urls[0], digest) c.Assert(err, gc.IsNil) c.Assert(event.Kind, gc.Equals, store.EventPublished) c.Assert(event.Revision, gc.Equals, i) c.Assert(event.Errors, gc.IsNil) c.Assert(event.Warnings, gc.IsNil) } } func (s *StoreSuite) TestPublishErrorFromBzr(c *gc.C) { branch := s.dummyBranch(c, "") // In TestPublish we ensure that the streams are parsed // separately by inserting garbage on stderr. Now make // sure that stderr isn't simply trashed, as we want to // know about what a real error tells us. plugin := fakePlugin{} plugin.install(c.MkDir(), `import sys; sys.stderr.write("STDERR STUFF FROM TEST\n"); sys.exit(1)`) defer plugin.uninstall() err := store.PublishBazaarBranch(s.store, urls, branch.path(), "wrong-rev") c.Assert(err, gc.ErrorMatches, "(?s).*STDERR STUFF.*") } func (s *StoreSuite) TestPublishErrorInCharm(c *gc.C) { branch := s.dummyBranch(c, "") // Corrupt the charm. branch.remove("metadata.yaml") branch.commit("Removed metadata.yaml.") // Attempt to publish the erroneous content. err := store.PublishBazaarBranch(s.store, urls, branch.path(), "wrong-rev") c.Assert(err, gc.ErrorMatches, ".*/metadata.yaml: no such file or directory") // The event should be logged as well, since this was an error in the charm // that won't go away and must be communicated to the author. event, err := s.store.CharmEvent(urls[0], branch.digest()) c.Assert(err, gc.IsNil) c.Assert(event.Kind, gc.Equals, store.EventPublishError) c.Assert(event.Revision, gc.Equals, 0) c.Assert(event.Errors, gc.NotNil) c.Assert(event.Errors[0], gc.Matches, ".*/metadata.yaml: no such file or directory") c.Assert(event.Warnings, gc.IsNil) } type bzrDir string func (dir bzrDir) path(args ...string) string { return filepath.Join(append([]string{string(dir)}, args...)...) } func (dir bzrDir) run(args ...string) []byte { cmd := exec.Command("bzr", args...) oldemail := os.Getenv("EMAIL") defer os.Setenv("EMAIL", oldemail) // bzr will complain if bzr whoami has not been run previously, // avoid this by passing $EMAIL into the environment. os.Setenv("EMAIL", "nobody@testing.invalid") cmd.Dir = string(dir) output, err := cmd.Output() if err != nil { panic(fmt.Sprintf("command failed: bzr %s\n%s", strings.Join(args, " "), output)) } return output } func (dir bzrDir) init() { dir.run("init") } func (dir bzrDir) add(paths ...string) { dir.run(append([]string{"add"}, paths...)...) } func (dir bzrDir) remove(paths ...string) { dir.run(append([]string{"rm"}, paths...)...) } func (dir bzrDir) commit(msg string) { dir.run("commit", "-m", msg) } func (dir bzrDir) write(path string, data string) { err := ioutil.WriteFile(dir.path(path), []byte(data), 0644) if err != nil { panic(err) } } func (dir bzrDir) change() { t := time.Now().String() dir.write("timestamp", t) dir.add("timestamp") dir.commit("Revision bumped at " + t) } func (dir bzrDir) digest() string { output := dir.run("revision-info") f := bytes.Fields(output) if len(f) != 2 { panic("revision-info returned bad output: " + string(output)) } return string(f[1]) } func copyCharmDir(dst string, dir *charm.Dir) { var b bytes.Buffer err := dir.BundleTo(&b) if err != nil { panic(err) } bundle, err := charm.ReadBundleBytes(b.Bytes()) if err != nil { panic(err) } err = bundle.ExpandTo(dst) if err != nil { panic(err) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/mgo_test.go��������������������������������������0000644�0000153�0000161�00000004103�12321735642�024167� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store_test import ( "bytes" "os/exec" "time" "labix.org/v2/mgo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" ) // ---------------------------------------------------------------------------- // The mgo test suite type MgoSuite struct { Addr string Session *mgo.Session output bytes.Buffer server *exec.Cmd } func (s *MgoSuite) SetUpSuite(c *gc.C) { mgo.SetDebug(true) mgo.SetStats(true) dbdir := c.MkDir() args := []string{ "--dbpath", dbdir, "--bind_ip", "127.0.0.1", "--port", "50017", "--nssize", "1", "--noprealloc", "--smallfiles", "--nojournal", } s.server = exec.Command("mongod", args...) s.server.Stdout = &s.output s.server.Stderr = &s.output err := s.server.Start() c.Assert(err, gc.IsNil) } func (s *MgoSuite) TearDownSuite(c *gc.C) { s.server.Process.Kill() s.server.Process.Wait() } func (s *MgoSuite) SetUpTest(c *gc.C) { err := DropAll("localhost:50017") c.Assert(err, gc.IsNil) mgo.SetLogger(c) mgo.ResetStats() s.Addr = "127.0.0.1:50017" s.Session, err = mgo.Dial(s.Addr) c.Assert(err, gc.IsNil) } func (s *MgoSuite) TearDownTest(c *gc.C) { if s.Session != nil { s.Session.Close() } t0 := time.Now() for i := 0; ; i++ { stats := mgo.GetStats() if stats.SocketsInUse == 0 && stats.SocketsAlive == 0 { break } if time.Since(t0) > testing.LongWait { // We wait up to 10s for all workers to finish up c.Fatal("Test left sockets in a dirty state") } c.Logf("Waiting for sockets to die: %d in use, %d alive", stats.SocketsInUse, stats.SocketsAlive) time.Sleep(testing.ShortWait) } } func DropAll(mongourl string) (err error) { session, err := mgo.Dial(mongourl) if err != nil { return err } defer session.Close() names, err := session.DatabaseNames() if err != nil { return err } for _, name := range names { switch name { case "admin", "local", "config": default: err = session.DB(name).DropDatabase() if err != nil { return err } } } return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/server.go����������������������������������������0000644�0000153�0000161�00000025040�12321735642�023657� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store import ( "encoding/json" "fmt" "io" "net/http" "strconv" "strings" "time" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/log" ) const DefaultSeries = "precise" // Server is an http.Handler that serves the HTTP API of juju // so that juju clients can retrieve published charms. type Server struct { store *Store mux *http.ServeMux } // New returns a new *Server using store. func NewServer(store *Store) (*Server, error) { s := &Server{ store: store, mux: http.NewServeMux(), } s.mux.HandleFunc("/charm-info", func(w http.ResponseWriter, r *http.Request) { s.serveInfo(w, r) }) s.mux.HandleFunc("/charm-event", func(w http.ResponseWriter, r *http.Request) { s.serveEvent(w, r) }) s.mux.HandleFunc("/charm/", func(w http.ResponseWriter, r *http.Request) { s.serveCharm(w, r) }) s.mux.HandleFunc("/stats/counter/", func(w http.ResponseWriter, r *http.Request) { s.serveStats(w, r) }) // This is just a validation key to allow blitz.io to run // performance tests against the site. s.mux.HandleFunc("/mu-35700a31-6bf320ca-a800b670-05f845ee", func(w http.ResponseWriter, r *http.Request) { s.serveBlitzKey(w, r) }) return s, nil } // ServeHTTP serves an http request. // This method turns *Server into an http.Handler. func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { http.Redirect(w, r, "https://juju.ubuntu.com", http.StatusSeeOther) return } s.mux.ServeHTTP(w, r) } func statsEnabled(req *http.Request) bool { // It's fine to parse the form more than once, and it avoids // bugs from not parsing it. req.ParseForm() return req.Form.Get("stats") != "0" } func charmStatsKey(curl *charm.URL, kind string) []string { if curl.User == "" { return []string{kind, curl.Series, curl.Name} } return []string{kind, curl.Series, curl.Name, curl.User} } func (s *Server) resolveURL(url string) (*charm.URL, error) { ref, series, err := charm.ParseReference(url) if err != nil { return nil, err } if series == "" { prefSeries, err := s.store.Series(ref) if err != nil { return nil, err } if len(prefSeries) == 0 { return nil, ErrNotFound } return &charm.URL{Reference: ref, Series: prefSeries[0]}, nil } return &charm.URL{Reference: ref, Series: series}, nil } func (s *Server) serveInfo(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/charm-info" { w.WriteHeader(http.StatusNotFound) return } r.ParseForm() response := map[string]*charm.InfoResponse{} for _, url := range r.Form["charms"] { c := &charm.InfoResponse{} response[url] = c curl, err := s.resolveURL(url) var info *CharmInfo if err == nil { info, err = s.store.CharmInfo(curl) } var skey []string if err == nil { skey = charmStatsKey(curl, "charm-info") c.CanonicalURL = curl.String() c.Sha256 = info.BundleSha256() c.Revision = info.Revision() c.Digest = info.Digest() } else { if err == ErrNotFound && curl != nil { skey = charmStatsKey(curl, "charm-missing") } c.Errors = append(c.Errors, err.Error()) } if skey != nil && statsEnabled(r) { go s.store.IncCounter(skey) } } data, err := json.Marshal(response) if err == nil { w.Header().Set("Content-Type", "application/json") _, err = w.Write(data) } if err != nil { log.Errorf("store: cannot write content: %v", err) w.WriteHeader(http.StatusInternalServerError) return } } func (s *Server) serveEvent(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/charm-event" { w.WriteHeader(http.StatusNotFound) return } r.ParseForm() response := map[string]*charm.EventResponse{} for _, url := range r.Form["charms"] { digest := "" if i := strings.Index(url, "@"); i >= 0 && i+1 < len(url) { digest = url[i+1:] url = url[:i] } c := &charm.EventResponse{} response[url] = c curl, err := s.resolveURL(url) var event *CharmEvent if err == nil { event, err = s.store.CharmEvent(curl, digest) } var skey []string if err == nil { skey = charmStatsKey(curl, "charm-event") c.Kind = event.Kind.String() c.Revision = event.Revision c.Digest = event.Digest c.Errors = event.Errors c.Warnings = event.Warnings c.Time = event.Time.UTC().Format(time.RFC3339) } else { c.Errors = append(c.Errors, err.Error()) } if skey != nil && statsEnabled(r) { go s.store.IncCounter(skey) } } data, err := json.Marshal(response) if err == nil { w.Header().Set("Content-Type", "application/json") _, err = w.Write(data) } if err != nil { log.Errorf("store: cannot write content: %v", err) w.WriteHeader(http.StatusInternalServerError) return } } func (s *Server) serveCharm(w http.ResponseWriter, r *http.Request) { if !strings.HasPrefix(r.URL.Path, "/charm/") { panic("serveCharm: bad url") } curl, err := s.resolveURL("cs:" + r.URL.Path[len("/charm/"):]) if err != nil { w.WriteHeader(http.StatusNotFound) return } info, rc, err := s.store.OpenCharm(curl) if err == ErrNotFound { w.WriteHeader(http.StatusNotFound) return } if err != nil { w.WriteHeader(http.StatusInternalServerError) log.Errorf("store: cannot open charm %q: %v", curl, err) return } if statsEnabled(r) { go s.store.IncCounter(charmStatsKey(curl, "charm-bundle")) } defer rc.Close() w.Header().Set("Connection", "close") // No keep-alive for now. w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Length", strconv.FormatInt(info.BundleSize(), 10)) _, err = io.Copy(w, rc) if err != nil { log.Errorf("store: failed to stream charm %q: %v", curl, err) } } func (s *Server) serveStats(w http.ResponseWriter, r *http.Request) { // TODO: Adopt a smarter mux that simplifies this logic. const dir = "/stats/counter/" if !strings.HasPrefix(r.URL.Path, dir) { panic("bad url") } base := r.URL.Path[len(dir):] if strings.Index(base, "/") > 0 { w.WriteHeader(http.StatusNotFound) return } if base == "" { w.WriteHeader(http.StatusForbidden) return } r.ParseForm() var by CounterRequestBy switch v := r.Form.Get("by"); v { case "": by = ByAll case "day": by = ByDay case "week": by = ByWeek default: w.WriteHeader(http.StatusBadRequest) w.Write([]byte(fmt.Sprintf("Invalid 'by' value: %q", v))) return } req := CounterRequest{ Key: strings.Split(base, ":"), List: r.Form.Get("list") == "1", By: by, } if v := r.Form.Get("start"); v != "" { var err error req.Start, err = time.Parse("2006-01-02", v) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(fmt.Sprintf("Invalid 'start' value: %q", v))) return } } if v := r.Form.Get("stop"); v != "" { var err error req.Stop, err = time.Parse("2006-01-02", v) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(fmt.Sprintf("Invalid 'stop' value: %q", v))) return } // Cover all timestamps within the stop day. req.Stop = req.Stop.Add(24*time.Hour - 1*time.Second) } if req.Key[len(req.Key)-1] == "*" { req.Prefix = true req.Key = req.Key[:len(req.Key)-1] if len(req.Key) == 0 { // No point in counting something unknown. w.WriteHeader(http.StatusForbidden) return } } var format func([]formatItem) []byte switch v := r.Form.Get("format"); v { case "": if !req.List && req.By == ByAll { format = formatCount } else { format = formatText } case "text": format = formatText case "csv": format = formatCSV case "json": format = formatJSON default: w.WriteHeader(http.StatusBadRequest) w.Write([]byte(fmt.Sprintf("Invalid 'format' value: %q", v))) return } entries, err := s.store.Counters(&req) if err != nil { log.Errorf("store: cannot query counters: %v", err) w.WriteHeader(http.StatusInternalServerError) return } var buf []byte var items []formatItem for i := range entries { entry := &entries[i] if req.List { for j := range entry.Key { buf = append(buf, entry.Key[j]...) buf = append(buf, ':') } if entry.Prefix { buf = append(buf, '*') } else { buf = buf[:len(buf)-1] } } items = append(items, formatItem{string(buf), entry.Count, entry.Time}) buf = buf[:0] } buf = format(items) w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Length", strconv.Itoa(len(buf))) _, err = w.Write(buf) if err != nil { log.Errorf("store: cannot write content: %v", err) w.WriteHeader(http.StatusInternalServerError) } } func (s *Server) serveBlitzKey(w http.ResponseWriter, r *http.Request) { w.Header().Set("Connection", "close") w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Length", "2") w.Write([]byte("42")) } type formatItem struct { key string count int64 time time.Time } func (fi *formatItem) hasKey() bool { return fi.key != "" } func (fi *formatItem) hasTime() bool { return !fi.time.IsZero() } func (fi *formatItem) formatTime() string { return fi.time.Format("2006-01-02") } func formatCount(items []formatItem) []byte { return strconv.AppendInt(nil, items[0].count, 10) } func formatText(items []formatItem) []byte { var maxKeyLength int for i := range items { if l := len(items[i].key); maxKeyLength < l { maxKeyLength = l } } spaces := make([]byte, maxKeyLength+2) for i := range spaces { spaces[i] = ' ' } var buf []byte for i := range items { item := &items[i] if item.hasKey() { buf = append(buf, item.key...) buf = append(buf, spaces[len(item.key):]...) } if item.hasTime() { buf = append(buf, item.formatTime()...) buf = append(buf, ' ', ' ') } buf = strconv.AppendInt(buf, item.count, 10) buf = append(buf, '\n') } return buf } func formatCSV(items []formatItem) []byte { var buf []byte for i := range items { item := &items[i] if item.hasKey() { buf = append(buf, item.key...) buf = append(buf, ',') } if item.hasTime() { buf = append(buf, item.formatTime()...) buf = append(buf, ',') } buf = strconv.AppendInt(buf, item.count, 10) buf = append(buf, '\n') } return buf } func formatJSON(items []formatItem) []byte { if len(items) == 0 { return []byte("[]") } var buf []byte buf = append(buf, '[') for i := range items { item := &items[i] if i == 0 { buf = append(buf, '[') } else { buf = append(buf, ',', '[') } if item.hasKey() { buf = append(buf, '"') buf = append(buf, item.key...) buf = append(buf, '"', ',') } if item.hasTime() { buf = append(buf, '"') buf = append(buf, item.formatTime()...) buf = append(buf, '"', ',') } buf = strconv.AppendInt(buf, item.count, 10) buf = append(buf, ']') } buf = append(buf, ']') return buf } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/branch.go����������������������������������������0000644�0000153�0000161�00000011346�12321735642�023612� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store import ( "bytes" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strings" "launchpad.net/juju-core/charm" ) // PublishBazaarBranch checks out the Bazaar branch from burl and // publishes its latest revision at urls in the given store. // The digest parameter must be the most recent known Bazaar // revision id for the branch tip. If publishing this specific digest // for these URLs has been attempted already, the publishing // procedure may abort early. The published digest is the Bazaar // revision id of the checked out branch's tip, though, which may // differ from the digest parameter. func PublishBazaarBranch(store *Store, urls []*charm.URL, burl string, digest string) error { // Prevent other publishers from updating these specific URLs // concurrently. lock, err := store.LockUpdates(urls) if err != nil { return err } defer lock.Unlock() var branchDir string NewTip: // Prepare the charm publisher. This will compute the revision // to be assigned to the charm, and it will also fail if the // operation is unnecessary because charms are up-to-date. pub, err := store.CharmPublisher(urls, digest) if err != nil { return err } // Figure if publishing this charm was already attempted before and // failed. We won't try again endlessly if so. In the future we may // retry automatically in certain circumstances. event, err := store.CharmEvent(urls[0], digest) if err == nil && event.Kind != EventPublished { return fmt.Errorf("charm publishing previously failed: %s", strings.Join(event.Errors, "; ")) } else if err != nil && err != ErrNotFound { return err } if branchDir == "" { // Retrieve the branch with a lightweight checkout, so that it // builds a working tree as cheaply as possible. History // doesn't matter here. tempDir, err := ioutil.TempDir("", "publish-branch-") if err != nil { return err } defer os.RemoveAll(tempDir) branchDir = filepath.Join(tempDir, "branch") output, err := exec.Command("bzr", "checkout", "--lightweight", burl, branchDir).CombinedOutput() if err != nil { return outputErr(output, err) } // Pick actual digest from tip. Publishing the real tip // revision rather than the revision for the digest provided is // strictly necessary to prevent a race condition. If the // provided digest was published instead, there's a chance // another publisher concurrently running could have found a // newer revision and published that first, and the digest // parameter provided is in fact an old version that would // overwrite the new version. tipDigest, err := bzrRevisionId(branchDir) if err != nil { return err } if tipDigest != digest { digest = tipDigest goto NewTip } } ch, err := charm.ReadDir(branchDir) if err == nil { // Hand over the charm to the store for bundling and // streaming its content into the database. err = pub.Publish(ch) if err == ErrUpdateConflict { // A conflict may happen in edge cases if the whole // locking mechanism fails due to an expiration event, // and then the expired concurrent publisher revives // for whatever reason and attempts to finish // publishing. The state of the system is still // consistent in that case, and the error isn't logged // since the revision was properly published before. return err } } // Publishing is done. Log failure or error. event = &CharmEvent{ URLs: urls, Digest: digest, } if err == nil { event.Kind = EventPublished event.Revision = pub.Revision() } else { event.Kind = EventPublishError event.Errors = []string{err.Error()} } if logerr := store.LogCharmEvent(event); logerr != nil { if err == nil { err = logerr } else { err = fmt.Errorf("%v; %v", err, logerr) } } return err } // bzrRevisionId returns the Bazaar revision id for the branch in branchDir. func bzrRevisionId(branchDir string) (string, error) { cmd := exec.Command("bzr", "revision-info") cmd.Dir = branchDir stderr := &bytes.Buffer{} cmd.Stderr = stderr output, err := cmd.Output() if err != nil { output = append(output, '\n') output = append(output, stderr.Bytes()...) return "", outputErr(output, err) } pair := bytes.Fields(output) if len(pair) != 2 { output = append(output, '\n') output = append(output, stderr.Bytes()...) return "", fmt.Errorf(`invalid output from "bzr revision-info": %s`, output) } return string(pair[1]), nil } // outputErr returns an error that assembles some command's output and its // error, if both output and err are set, and returns only err if output is nil. func outputErr(output []byte, err error) error { if len(output) > 0 { return fmt.Errorf("%v\n%s", err, output) } return err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/store_test.go������������������������������������0000644�0000153�0000161�00000071554�12321735642�024557� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store_test import ( "flag" "fmt" "io" "io/ioutil" "os" "strconv" "sync" stdtesting "testing" "time" "labix.org/v2/mgo/bson" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/store" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) func Test(t *stdtesting.T) { gc.TestingT(t) } var _ = gc.Suite(&StoreSuite{}) var _ = gc.Suite(&TrivialSuite{}) type StoreSuite struct { MgoSuite testing.HTTPSuite testbase.LoggingSuite 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) { s.LoggingSuite.TearDownSuite(c) s.HTTPSuite.TearDownSuite(c) s.MgoSuite.TearDownSuite(c) } func (s *StoreSuite) SetUpTest(c *gc.C) { s.MgoSuite.SetUpTest(c) s.LoggingSuite.SetUpTest(c) var err error s.store, err = store.Open(s.Addr) c.Assert(err, gc.IsNil) } func (s *StoreSuite) TearDownTest(c *gc.C) { s.LoggingSuite.TearDownTest(c) s.HTTPSuite.TearDownTest(c) if s.store != nil { s.store.Close() } s.MgoSuite.TearDownTest(c) } // FakeCharmDir is a charm that implements the interface that the // store publisher cares about. type FakeCharmDir struct { revision interface{} // so we can tell if it's not set. error string } func (d *FakeCharmDir) Meta() *charm.Meta { return &charm.Meta{ Name: "fakecharm", Summary: "Fake charm for testing purposes.", Description: "This is a fake charm for testing purposes.\n", Provides: make(map[string]charm.Relation), Requires: make(map[string]charm.Relation), Peers: make(map[string]charm.Relation), } } func (d *FakeCharmDir) Config() *charm.Config { return &charm.Config{make(map[string]charm.Option)} } func (d *FakeCharmDir) SetRevision(revision int) { d.revision = revision } func (d *FakeCharmDir) BundleTo(w io.Writer) error { if d.error == "beforeWrite" { return fmt.Errorf(d.error) } _, err := w.Write([]byte(fmt.Sprintf("charm-revision-%v", d.revision))) if d.error == "afterWrite" { return fmt.Errorf(d.error) } return err } func (s *StoreSuite) TestCharmPublisherWithRevisionedURL(c *gc.C) { urls := []*charm.URL{charm.MustParseURL("cs:oneiric/wordpress-0")} pub, err := s.store.CharmPublisher(urls, "some-digest") c.Assert(err, gc.ErrorMatches, "CharmPublisher: got charm URL with revision: cs:oneiric/wordpress-0") c.Assert(pub, gc.IsNil) } func (s *StoreSuite) TestCharmPublisher(c *gc.C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} pub, err := s.store.CharmPublisher(urls, "some-digest") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(testing.Charms.ClonedDir(c.MkDir(), "dummy")) c.Assert(err, gc.IsNil) for _, url := range urls { info, rc, err := s.store.OpenCharm(url) c.Assert(err, gc.IsNil) c.Assert(info.Revision(), gc.Equals, 0) c.Assert(info.Digest(), gc.Equals, "some-digest") data, err := ioutil.ReadAll(rc) c.Check(err, gc.IsNil) err = rc.Close() c.Assert(err, gc.IsNil) bundle, err := charm.ReadBundleBytes(data) c.Assert(err, gc.IsNil) // The same information must be available by reading the // full charm data... c.Assert(bundle.Meta().Name, gc.Equals, "dummy") c.Assert(bundle.Config().Options["title"].Default, gc.Equals, "My Title") // ... and the queriable details. c.Assert(info.Meta().Name, gc.Equals, "dummy") c.Assert(info.Config().Options["title"].Default, gc.Equals, "My Title") info2, err := s.store.CharmInfo(url) c.Assert(err, gc.IsNil) c.Assert(info2, gc.DeepEquals, info) } } func (s *StoreSuite) TestDeleteCharm(c *gc.C) { url := charm.MustParseURL("cs:oneiric/wordpress") for i := 0; i < 4; i++ { pub, err := s.store.CharmPublisher([]*charm.URL{url}, fmt.Sprintf("some-digest-%d", i)) c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, i) err = pub.Publish(testing.Charms.ClonedDir(c.MkDir(), "dummy")) c.Assert(err, gc.IsNil) } // Verify charms were published info, rc, err := s.store.OpenCharm(url) c.Assert(err, gc.IsNil) err = rc.Close() c.Assert(err, gc.IsNil) c.Assert(info.Revision(), gc.Equals, 3) // Delete an arbitrary middle revision url1 := url.WithRevision(1) infos, err := s.store.DeleteCharm(url1) c.Assert(err, gc.IsNil) c.Assert(len(infos), gc.Equals, 1) // Verify still published info, rc, err = s.store.OpenCharm(url) c.Assert(err, gc.IsNil) err = rc.Close() c.Assert(err, gc.IsNil) c.Assert(info.Revision(), gc.Equals, 3) // Delete all revisions expectedRevs := map[int]bool{0: true, 2: true, 3: true} infos, err = s.store.DeleteCharm(url) c.Assert(err, gc.IsNil) c.Assert(len(infos), gc.Equals, 3) for _, deleted := range infos { // We deleted the charm we expected to c.Assert(info.Meta().Name, gc.Equals, deleted.Meta().Name) _, has := expectedRevs[deleted.Revision()] c.Assert(has, gc.Equals, true) delete(expectedRevs, deleted.Revision()) } c.Assert(len(expectedRevs), gc.Equals, 0) // The charm is all gone _, _, err = s.store.OpenCharm(url) c.Assert(err, gc.Not(gc.IsNil)) } func (s *StoreSuite) TestCharmPublishError(c *gc.C) { url := charm.MustParseURL("cs:oneiric/wordpress") urls := []*charm.URL{url} // Publish one successfully to bump the revision so we can // make sure it is being correctly set below. pub, err := s.store.CharmPublisher(urls, "one-digest") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) pub, err = s.store.CharmPublisher(urls, "another-digest") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 1) err = pub.Publish(&FakeCharmDir{error: "beforeWrite"}) c.Assert(err, gc.ErrorMatches, "beforeWrite") pub, err = s.store.CharmPublisher(urls, "another-digest") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 1) err = pub.Publish(&FakeCharmDir{error: "afterWrite"}) c.Assert(err, gc.ErrorMatches, "afterWrite") // Still at the original charm revision that succeeded first. info, err := s.store.CharmInfo(url) c.Assert(err, gc.IsNil) c.Assert(info.Revision(), gc.Equals, 0) c.Assert(info.Digest(), gc.Equals, "one-digest") } func (s *StoreSuite) TestCharmInfoNotFound(c *gc.C) { info, err := s.store.CharmInfo(charm.MustParseURL("cs:oneiric/wordpress")) c.Assert(err, gc.Equals, store.ErrNotFound) c.Assert(info, gc.IsNil) } func (s *StoreSuite) TestRevisioning(c *gc.C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} tests := []struct { urls []*charm.URL data string }{ {urls[0:], "charm-revision-0"}, {urls[1:], "charm-revision-1"}, {urls[0:], "charm-revision-2"}, } for i, t := range tests { pub, err := s.store.CharmPublisher(t.urls, fmt.Sprintf("digest-%d", i)) c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, i) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) } for i, t := range tests { for _, url := range t.urls { url = url.WithRevision(i) info, rc, err := s.store.OpenCharm(url) c.Assert(err, gc.IsNil) data, err := ioutil.ReadAll(rc) cerr := rc.Close() c.Assert(info.Revision(), gc.Equals, i) c.Assert(url.Revision, gc.Equals, i) // Untouched. c.Assert(cerr, gc.IsNil) c.Assert(string(data), gc.Equals, string(t.data)) c.Assert(err, gc.IsNil) } } info, rc, err := s.store.OpenCharm(urlA.WithRevision(1)) c.Assert(err, gc.Equals, store.ErrNotFound) c.Assert(info, gc.IsNil) c.Assert(rc, gc.IsNil) } func (s *StoreSuite) TestLockUpdates(c *gc.C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} // Lock update of just B to force a partial conflict. lock1, err := s.store.LockUpdates(urls[1:]) c.Assert(err, gc.IsNil) // Partially conflicts with locked update above. lock2, err := s.store.LockUpdates(urls) c.Check(err, gc.Equals, store.ErrUpdateConflict) c.Check(lock2, gc.IsNil) lock1.Unlock() // Trying again should work since lock1 was released. lock3, err := s.store.LockUpdates(urls) c.Assert(err, gc.IsNil) lock3.Unlock() } func (s *StoreSuite) TestLockUpdatesExpires(c *gc.C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} // Initiate an update of B only to force a partial conflict. lock1, err := s.store.LockUpdates(urls[1:]) c.Assert(err, gc.IsNil) // Hack time to force an expiration. locks := s.Session.DB("juju").C("locks") selector := bson.M{"_id": urlB.String()} update := bson.M{"time": bson.Now().Add(-store.UpdateTimeout - 10e9)} err = locks.Update(selector, update) c.Check(err, gc.IsNil) // Works due to expiration of previous lock. lock2, err := s.store.LockUpdates(urls) c.Assert(err, gc.IsNil) defer lock2.Unlock() // The expired lock was forcefully killed. Unlocking it must // not interfere with lock2 which is still alive. lock1.Unlock() // The above statement was a NOOP and lock2 is still in effect, // so attempting another lock must necessarily fail. lock3, err := s.store.LockUpdates(urls) c.Check(err, gc.Equals, store.ErrUpdateConflict) c.Check(lock3, gc.IsNil) } var seriesSolverCharms = []struct { series, name string }{ {"oneiric", "wordpress"}, {"precise", "wordpress"}, {"quantal", "wordpress"}, {"trusty", "wordpress"}, {"volumetric", "wordpress"}, {"precise", "mysql"}, {"trusty", "mysqladmin"}, {"def", "zebra"}, {"zef", "zebra"}, } func (s *StoreSuite) TestSeriesSolver(c *gc.C) { for _, t := range seriesSolverCharms { url := charm.MustParseURL(fmt.Sprintf("cs:%s/%s", t.series, t.name)) urls := []*charm.URL{url} pub, err := s.store.CharmPublisher(urls, fmt.Sprintf("some-%s-%s-digest", t.series, t.name)) c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) } // LTS, then non-LTS, reverse alphabetical order ref, _, err := charm.ParseReference("cs:wordpress") c.Assert(err, gc.IsNil) series, err := s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 5) c.Check(series[0], gc.Equals, "trusty") c.Check(series[1], gc.Equals, "precise") c.Check(series[2], gc.Equals, "volumetric") c.Check(series[3], gc.Equals, "quantal") c.Check(series[4], gc.Equals, "oneiric") // Ensure that the full charm name matches, not just prefix ref, _, err = charm.ParseReference("cs:mysql") c.Assert(err, gc.IsNil) series, err = s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 1) c.Check(series[0], gc.Equals, "precise") // No LTS, reverse alphabetical order ref, _, err = charm.ParseReference("cs:zebra") c.Assert(err, gc.IsNil) series, err = s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 2) c.Check(series[0], gc.Equals, "zef") c.Check(series[1], gc.Equals, "def") } var mysqlSeriesCharms = []struct { fakeDigest string urls []string }{ {"533224069221503992aaa726", []string{"cs:~charmers/oneiric/mysql", "cs:oneiric/mysql"}}, {"533224c79221503992aaa7ea", []string{"cs:~charmers/precise/mysql", "cs:precise/mysql"}}, {"533223a69221503992aaa6be", []string{"cs:~bjornt/trusty/mysql"}}, {"533225b49221503992aaa8e5", []string{"cs:~clint-fewbar/precise/mysql"}}, {"5332261b9221503992aaa96b", []string{"cs:~gandelman-a/precise/mysql"}}, {"533226289221503992aaa97d", []string{"cs:~gandelman-a/quantal/mysql"}}, {"5332264d9221503992aaa9b0", []string{"cs:~hazmat/precise/mysql"}}, {"5332272d9221503992aaaa4d", []string{"cs:~jmit/oneiric/mysql"}}, {"53328a439221503992aaad28", []string{"cs:~landscape/trusty/mysql"}}, {"533228ae9221503992aaab96", []string{"cs:~negronjl/precise/mysql-file-permissions"}}, {"533228f39221503992aaabde", []string{"cs:~openstack-ubuntu-testing/oneiric/mysql"}}, {"533229029221503992aaabed", []string{"cs:~openstack-ubuntu-testing/precise/mysql"}}, {"5332291e9221503992aaac09", []string{"cs:~openstack-ubuntu-testing/quantal/mysql"}}, {"53327f4f9221503992aaad1e", []string{"cs:~tribaal/trusty/mysql"}}, } func (s *StoreSuite) TestMysqlSeriesSolver(c *gc.C) { for _, t := range mysqlSeriesCharms { var urls []*charm.URL for _, url := range t.urls { urls = append(urls, charm.MustParseURL(url)) } pub, err := s.store.CharmPublisher(urls, t.fakeDigest) c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) } ref, _, err := charm.ParseReference("cs:mysql") c.Assert(err, gc.IsNil) series, err := s.store.Series(ref) c.Assert(err, gc.IsNil) c.Assert(series, gc.HasLen, 2) c.Check(series[0], gc.Equals, "precise") c.Check(series[1], gc.Equals, "oneiric") } func (s *StoreSuite) TestConflictingUpdate(c *gc.C) { // This test checks that if for whatever reason the locking // safety-net fails, adding two charms in parallel still // results in a sane outcome. url := charm.MustParseURL("cs:oneiric/wordpress") urls := []*charm.URL{url} pub1, err := s.store.CharmPublisher(urls, "some-digest") c.Assert(err, gc.IsNil) c.Assert(pub1.Revision(), gc.Equals, 0) pub2, err := s.store.CharmPublisher(urls, "some-digest") c.Assert(err, gc.IsNil) c.Assert(pub2.Revision(), gc.Equals, 0) // The first publishing attempt should work. err = pub2.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) // Attempting to finish the second attempt should break, // since it lost the race and the given revision is already // in place. err = pub1.Publish(&FakeCharmDir{}) c.Assert(err, gc.Equals, store.ErrUpdateConflict) } func (s *StoreSuite) TestRedundantUpdate(c *gc.C) { urlA := charm.MustParseURL("cs:oneiric/wordpress-a") urlB := charm.MustParseURL("cs:oneiric/wordpress-b") urls := []*charm.URL{urlA, urlB} pub, err := s.store.CharmPublisher(urls, "digest-0") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) // All charms are already on digest-0. pub, err = s.store.CharmPublisher(urls, "digest-0") c.Assert(err, gc.ErrorMatches, "charm is up-to-date") c.Assert(err, gc.Equals, store.ErrRedundantUpdate) c.Assert(pub, gc.IsNil) // Now add a second revision just for wordpress-b. pub, err = s.store.CharmPublisher(urls[1:], "digest-1") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 1) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) // Same digest bumps revision because one of them was old. pub, err = s.store.CharmPublisher(urls, "digest-1") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 2) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) } const fakeRevZeroSha = "319095521ac8a62fa1e8423351973512ecca8928c9f62025e37de57c9ef07a53" func (s *StoreSuite) TestCharmBundleData(c *gc.C) { url := charm.MustParseURL("cs:oneiric/wordpress") urls := []*charm.URL{url} pub, err := s.store.CharmPublisher(urls, "key") c.Assert(err, gc.IsNil) c.Assert(pub.Revision(), gc.Equals, 0) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) info, rc, err := s.store.OpenCharm(url) c.Assert(err, gc.IsNil) c.Check(info.BundleSha256(), gc.Equals, fakeRevZeroSha) c.Check(info.BundleSize(), gc.Equals, int64(len("charm-revision-0"))) err = rc.Close() c.Check(err, gc.IsNil) } func (s *StoreSuite) TestLogCharmEventWithRevisionedURL(c *gc.C) { url := charm.MustParseURL("cs:oneiric/wordpress-0") event := &store.CharmEvent{ Kind: store.EventPublishError, Digest: "some-digest", URLs: []*charm.URL{url}, } err := s.store.LogCharmEvent(event) c.Assert(err, gc.ErrorMatches, "LogCharmEvent: got charm URL with revision: cs:oneiric/wordpress-0") // This may work in the future, but not now. event, err = s.store.CharmEvent(url, "some-digest") c.Assert(err, gc.ErrorMatches, "CharmEvent: got charm URL with revision: cs:oneiric/wordpress-0") c.Assert(event, gc.IsNil) } func (s *StoreSuite) TestLogCharmEvent(c *gc.C) { url1 := charm.MustParseURL("cs:oneiric/wordpress") url2 := charm.MustParseURL("cs:oneiric/mysql") urls := []*charm.URL{url1, url2} event1 := &store.CharmEvent{ Kind: store.EventPublished, Revision: 42, Digest: "revKey1", URLs: urls, Warnings: []string{"A warning."}, Time: time.Unix(1, 0), } event2 := &store.CharmEvent{ Kind: store.EventPublished, Revision: 42, Digest: "revKey2", URLs: urls, Time: time.Unix(1, 0), } event3 := &store.CharmEvent{ Kind: store.EventPublishError, Digest: "revKey2", Errors: []string{"An error."}, URLs: urls[:1], } for _, event := range []*store.CharmEvent{event1, event2, event3} { err := s.store.LogCharmEvent(event) c.Assert(err, gc.IsNil) } events := s.Session.DB("juju").C("events") var s1, s2 map[string]interface{} err := events.Find(bson.M{"digest": "revKey1"}).One(&s1) c.Assert(err, gc.IsNil) c.Assert(s1["kind"], gc.Equals, int(store.EventPublished)) c.Assert(s1["urls"], gc.DeepEquals, []interface{}{"cs:oneiric/wordpress", "cs:oneiric/mysql"}) c.Assert(s1["warnings"], gc.DeepEquals, []interface{}{"A warning."}) c.Assert(s1["errors"], gc.IsNil) c.Assert(s1["time"], gc.DeepEquals, time.Unix(1, 0)) err = events.Find(bson.M{"digest": "revKey2", "kind": store.EventPublishError}).One(&s2) c.Assert(err, gc.IsNil) c.Assert(s2["urls"], gc.DeepEquals, []interface{}{"cs:oneiric/wordpress"}) c.Assert(s2["warnings"], gc.IsNil) c.Assert(s2["errors"], gc.DeepEquals, []interface{}{"An error."}) c.Assert(s2["time"].(time.Time).After(bson.Now().Add(-10e9)), gc.Equals, true) // Mongo stores timestamps in milliseconds, so chop // off the extra bits for comparison. event3.Time = time.Unix(0, event3.Time.UnixNano()/1e6*1e6) event, err := s.store.CharmEvent(urls[0], "revKey2") c.Assert(err, gc.IsNil) c.Assert(event, gc.DeepEquals, event3) event, err = s.store.CharmEvent(urls[1], "revKey1") c.Assert(err, gc.IsNil) c.Assert(event, gc.DeepEquals, event1) event, err = s.store.CharmEvent(urls[1], "revKeyX") c.Assert(err, gc.Equals, store.ErrNotFound) c.Assert(event, gc.IsNil) } 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) c.Assert(cs, gc.DeepEquals, []store.Counter{{Key: req.Key, Count: 0}}) for i := 0; i < 10; i++ { err := s.store.IncCounter([]string{"a", "b", "c"}) c.Assert(err, gc.IsNil) } for i := 0; i < 7; i++ { s.store.IncCounter([]string{"a", "b"}) c.Assert(err, gc.IsNil) } for i := 0; i < 3; i++ { s.store.IncCounter([]string{"a", "z", "b"}) c.Assert(err, gc.IsNil) } tests := []struct { key []string prefix bool result int64 }{ {[]string{"a", "b", "c"}, false, 10}, {[]string{"a", "b"}, false, 7}, {[]string{"a", "z", "b"}, false, 3}, {[]string{"a", "b", "c"}, true, 0}, {[]string{"a", "b", "c", "d"}, false, 0}, {[]string{"a", "b"}, true, 10}, {[]string{"a"}, true, 20}, {[]string{"b"}, true, 0}, } for _, t := range tests { c.Logf("Test: %#v\n", t) req = store.CounterRequest{Key: t.key, Prefix: t.prefix} cs, err := s.store.Counters(&req) c.Assert(err, gc.IsNil) c.Assert(cs, gc.DeepEquals, []store.Counter{{Key: t.key, Prefix: t.prefix, Count: t.result}}) } // High-level interface works. Now check that the data is // stored correctly. counters := s.Session.DB("juju").C("stat.counters") docs1, err := counters.Count() c.Assert(err, gc.IsNil) if docs1 != 3 && docs1 != 4 { fmt.Errorf("Expected 3 or 4 docs in counters collection, got %d", docs1) } // Hack times so that the next operation adds another document. err = counters.Update(nil, bson.D{{"$set", bson.D{{"t", 1}}}}) c.Check(err, gc.IsNil) err = s.store.IncCounter([]string{"a", "b", "c"}) c.Assert(err, gc.IsNil) docs2, err := counters.Count() c.Assert(err, gc.IsNil) c.Assert(docs2, gc.Equals, docs1+1) req = store.CounterRequest{Key: []string{"a", "b", "c"}} cs, err = s.store.Counters(&req) c.Assert(err, gc.IsNil) c.Assert(cs, gc.DeepEquals, []store.Counter{{Key: req.Key, Count: 11}}) req = store.CounterRequest{Key: []string{"a"}, Prefix: true} cs, err = s.store.Counters(&req) c.Assert(err, gc.IsNil) c.Assert(cs, gc.DeepEquals, []store.Counter{{Key: req.Key, Prefix: true, Count: 21}}) } 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) c.Assert(err, gc.IsNil) tokens := s.Session.DB("juju").C("stat.tokens") n, err := tokens.Count() c.Assert(err, gc.IsNil) c.Assert(n, gc.Equals, 0) } 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) c.Assert(err, gc.IsNil) c.Assert(cs[0].Count, gc.Equals, want) } assertSum(100000, 0) const genSize = 1024 // All of these will be cached, as we have two generations // of genSize entries each. for i := 0; i < genSize*2; i++ { err := s.store.IncCounter([]string{strconv.Itoa(i)}) c.Assert(err, gc.IsNil) } // Now go behind the scenes and corrupt all the tokens. tokens := s.Session.DB("juju").C("stat.tokens") iter := tokens.Find(nil).Iter() var t struct { Id int "_id" Token string "t" } for iter.Next(&t) { err := tokens.UpdateId(t.Id, bson.M{"$set": bson.M{"t": "corrupted" + t.Token}}) c.Assert(err, gc.IsNil) } c.Assert(iter.Err(), gc.IsNil) // We can consult the counters for the cached entries still. // First, check that the newest generation is good. for i := genSize; i < genSize*2; i++ { assertSum(i, 1) } // Now, we can still access a single entry of the older generation, // but this will cause the generations to flip and thus the rest // of the old generation will go away as the top half of the // entries is turned into the old generation. assertSum(0, 1) // Now we've lost access to the rest of the old generation. for i := 1; i < genSize; i++ { assertSum(i, 0) } // But we still have all of the top half available since it was // moved into the old generation. for i := genSize; i < genSize*2; i++ { assertSum(i, 1) } } 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) for i := 0; i < 10; i++ { go func() { wg0.Done() wg0.Wait() defer wg1.Done() err := s.store.IncCounter([]string{"a"}) c.Check(err, gc.IsNil) }() } wg1.Wait() req := store.CounterRequest{Key: []string{"a"}} cs, err := s.store.Counters(&req) c.Assert(err, gc.IsNil) c.Assert(cs[0].Count, gc.Equals, int64(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"}, {"a", "c"}, {"a", "b"}, {"a", "b", "c"}, {"a", "b", "c"}, {"a", "b", "e"}, {"a", "b", "d"}, {"a", "f", "g"}, {"a", "f", "h"}, {"a", "i"}, {"a", "i", "j"}, {"k", "l"}, } for _, key := range incs { err := s.store.IncCounter(key) c.Assert(err, gc.IsNil) } tests := []struct { prefix []string result []store.Counter }{ { []string{"a"}, []store.Counter{ {Key: []string{"a", "b"}, Prefix: true, Count: 4}, {Key: []string{"a", "f"}, Prefix: true, Count: 2}, {Key: []string{"a", "b"}, Prefix: false, Count: 1}, {Key: []string{"a", "c"}, Prefix: false, Count: 1}, {Key: []string{"a", "i"}, Prefix: false, Count: 1}, {Key: []string{"a", "i"}, Prefix: true, Count: 1}, }, }, { []string{"a", "b"}, []store.Counter{ {Key: []string{"a", "b", "c"}, Prefix: false, Count: 2}, {Key: []string{"a", "b", "d"}, Prefix: false, Count: 1}, {Key: []string{"a", "b", "e"}, Prefix: false, Count: 1}, }, }, { []string{"z"}, []store.Counter(nil), }, } // Use a different store to exercise cache filling. st, err := store.Open(s.Addr) c.Assert(err, gc.IsNil) defer st.Close() for i := range tests { req := &store.CounterRequest{Key: tests[i].prefix, Prefix: true, List: true} result, err := st.Counters(req) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, tests[i].result) } } func (s *StoreSuite) TestListCountersBy(c *gc.C) { if *noTestMongoJs { c.Skip("MongoDB javascript not available") } incs := []struct { key []string day int }{ {[]string{"a"}, 1}, {[]string{"a"}, 1}, {[]string{"b"}, 1}, {[]string{"a", "b"}, 1}, {[]string{"a", "c"}, 1}, {[]string{"a"}, 3}, {[]string{"a", "b"}, 3}, {[]string{"b"}, 9}, {[]string{"b"}, 9}, {[]string{"a", "c", "d"}, 9}, {[]string{"a", "c", "e"}, 9}, {[]string{"a", "c", "f"}, 9}, } day := func(i int) time.Time { return time.Date(2012, time.May, i, 0, 0, 0, 0, time.UTC) } counters := s.Session.DB("juju").C("stat.counters") for i, inc := range incs { err := s.store.IncCounter(inc.key) c.Assert(err, gc.IsNil) // Hack time so counters are assigned to 2012-05-<day> filter := bson.M{"t": bson.M{"$gt": store.TimeToStamp(time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC))}} stamp := store.TimeToStamp(day(inc.day)) stamp += int32(i) * 60 // Make every entry unique. err = counters.Update(filter, bson.D{{"$set", bson.D{{"t", stamp}}}}) c.Check(err, gc.IsNil) } tests := []struct { request store.CounterRequest result []store.Counter }{ { store.CounterRequest{ Key: []string{"a"}, Prefix: false, List: false, By: store.ByDay, }, []store.Counter{ {Key: []string{"a"}, Prefix: false, Count: 2, Time: day(1)}, {Key: []string{"a"}, Prefix: false, Count: 1, Time: day(3)}, }, }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: false, By: store.ByDay, }, []store.Counter{ {Key: []string{"a"}, Prefix: true, Count: 2, Time: day(1)}, {Key: []string{"a"}, Prefix: true, Count: 1, Time: day(3)}, {Key: []string{"a"}, Prefix: true, Count: 3, Time: day(9)}, }, }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: false, By: store.ByDay, Start: day(2), }, []store.Counter{ {Key: []string{"a"}, Prefix: true, Count: 1, Time: day(3)}, {Key: []string{"a"}, Prefix: true, Count: 3, Time: day(9)}, }, }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: false, By: store.ByDay, Stop: day(4), }, []store.Counter{ {Key: []string{"a"}, Prefix: true, Count: 2, Time: day(1)}, {Key: []string{"a"}, Prefix: true, Count: 1, Time: day(3)}, }, }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: false, By: store.ByDay, Start: day(3), Stop: day(8), }, []store.Counter{ {Key: []string{"a"}, Prefix: true, Count: 1, Time: day(3)}, }, }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: true, By: store.ByDay, }, []store.Counter{ {Key: []string{"a", "b"}, Prefix: false, Count: 1, Time: day(1)}, {Key: []string{"a", "c"}, Prefix: false, Count: 1, Time: day(1)}, {Key: []string{"a", "b"}, Prefix: false, Count: 1, Time: day(3)}, {Key: []string{"a", "c"}, Prefix: true, Count: 3, Time: day(9)}, }, }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: false, By: store.ByWeek, }, []store.Counter{ {Key: []string{"a"}, Prefix: true, Count: 3, Time: day(6)}, {Key: []string{"a"}, Prefix: true, Count: 3, Time: day(13)}, }, }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: true, By: store.ByWeek, }, []store.Counter{ {Key: []string{"a", "b"}, Prefix: false, Count: 2, Time: day(6)}, {Key: []string{"a", "c"}, Prefix: false, Count: 1, Time: day(6)}, {Key: []string{"a", "c"}, Prefix: true, Count: 3, Time: day(13)}, }, }, } for _, test := range tests { result, err := s.store.Counters(&test.request) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, test.result) } } func (s *TrivialSuite) TestEventString(c *gc.C) { c.Assert(store.EventPublished, gc.Matches, "published") c.Assert(store.EventPublishError, gc.Matches, "publish-error") for kind := store.CharmEventKind(1); kind < store.EventKindCount; kind++ { // This guarantees the switch in String is properly // updated with new event kinds. c.Assert(kind.String(), gc.Matches, "[a-z-]+") } } ����������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/lpad_test.go�������������������������������������0000644�0000153�0000161�00000004764�12321735642�024342� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/lpad" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/store" "launchpad.net/juju-core/testing" ) var jsonType = map[string]string{ "Content-Type": "application/json", } func (s *StoreSuite) TestPublishCharmDistro(c *gc.C) { branch := s.dummyBranch(c, "~joe/charms/oneiric/dummy/trunk") // The Distro call will look for bare /charms, first. testing.Server.Response(200, jsonType, []byte("{}")) // And then it picks up the tips. data := fmt.Sprintf(`[`+ `["file://%s", "rev1", ["oneiric", "precise"]],`+ `["file://%s", "%s", []],`+ `["file:///non-existent/~jeff/charms/precise/bad/trunk", "rev2", []],`+ `["file:///non-existent/~jeff/charms/precise/bad/skip-me", "rev3", []]`+ `]`, branch.path(), branch.path(), branch.digest()) testing.Server.Response(200, jsonType, []byte(data)) apiBase := lpad.APIBase(testing.Server.URL) err := store.PublishCharmsDistro(s.store, apiBase) // Should have a single failure from the trunk branch that doesn't // exist. The redundant update with the known digest should be // ignored, and skip-me isn't a supported branch name so it's // ignored as well. c.Assert(err, gc.ErrorMatches, `1 branch\(es\) failed to be published`) berr := err.(store.PublishBranchErrors)[0] c.Assert(berr.URL, gc.Equals, "file:///non-existent/~jeff/charms/precise/bad/trunk") c.Assert(berr.Err, gc.ErrorMatches, "(?s).*bzr: ERROR: Not a branch.*") for _, url := range []string{"cs:oneiric/dummy", "cs:precise/dummy-0", "cs:~joe/oneiric/dummy-0"} { dummy, err := s.store.CharmInfo(charm.MustParseURL(url)) c.Assert(err, gc.IsNil) c.Assert(dummy.Meta().Name, gc.Equals, "dummy") } // The known digest should have been ignored, so revision is still at 0. _, err = s.store.CharmInfo(charm.MustParseURL("cs:~joe/oneiric/dummy-1")) c.Assert(err, gc.Equals, store.ErrNotFound) // bare /charms lookup req := testing.Server.WaitRequest() c.Assert(req.Method, gc.Equals, "GET") c.Assert(req.URL.Path, gc.Equals, "/charms") // tips request req = testing.Server.WaitRequest() c.Assert(req.Method, gc.Equals, "GET") c.Assert(req.URL.Path, gc.Equals, "/charms") c.Assert(req.Form["ws.op"], gc.DeepEquals, []string{"getBranchTips"}) c.Assert(req.Form["since"], gc.IsNil) // Request must be signed by juju. c.Assert(req.Header.Get("Authorization"), gc.Matches, `.*oauth_consumer_key="juju".*`) } ������������juju-core_1.18.1/src/launchpad.net/juju-core/store/lpad.go������������������������������������������0000644�0000153�0000161�00000006255�12321735642�023300� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store import ( "fmt" "strings" "time" "launchpad.net/lpad" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/log" ) type PublishBranchError struct { URL string Err error } type PublishBranchErrors []PublishBranchError func (errs PublishBranchErrors) Error() string { return fmt.Sprintf("%d branch(es) failed to be published", len(errs)) } // PublishCharmsDistro publishes all branch tips found in // the /charms distribution in Launchpad onto store under // the "cs:" scheme. // apiBase specifies the Launchpad base API URL, such // as lpad.Production or lpad.Staging. // Errors found while processing one or more branches are // all returned as a PublishBranchErrors value. func PublishCharmsDistro(store *Store, apiBase lpad.APIBase) error { oauth := &lpad.OAuth{Anonymous: true, Consumer: "juju"} root, err := lpad.Login(apiBase, oauth) if err != nil { return err } distro, err := root.Distro("charms") if err != nil { return err } tips, err := distro.BranchTips(time.Time{}) if err != nil { return err } var errs PublishBranchErrors for _, tip := range tips { if !strings.HasSuffix(tip.UniqueName, "/trunk") { continue } burl, curl, err := uniqueNameURLs(tip.UniqueName) if err != nil { errs = append(errs, PublishBranchError{tip.UniqueName, err}) log.Errorf("%v\n", err) continue } log.Infof("----- %s\n", burl) if tip.Revision == "" { errs = append(errs, PublishBranchError{burl, fmt.Errorf("branch has no revisions")}) log.Errorf("branch has no revisions\n") continue } // Charm is published in the personal URL and in any explicitly // assigned official series. urls := []*charm.URL{curl} schema, name := curl.Schema, curl.Name for _, series := range tip.OfficialSeries { curl = &charm.URL{ Reference: charm.Reference{Schema: schema, Name: name, Revision: -1}, Series: series, } curl.Series = series curl.User = "" urls = append(urls, curl) } err = PublishBazaarBranch(store, urls, burl, tip.Revision) if err == ErrRedundantUpdate { continue } if err != nil { errs = append(errs, PublishBranchError{burl, err}) log.Errorf("%v\n", err) } } if errs != nil { return errs } return nil } // uniqueNameURLs returns the branch URL and the charm URL for the // provided Launchpad branch unique name. The unique name must be // in the form: // // ~<user>/charms/<series>/<charm name>/trunk // // For testing purposes, if name has a prefix preceding a string in // this format, the prefix is stripped out for computing the charm // URL, and the unique name is returned unchanged as the branch URL. func uniqueNameURLs(name string) (burl string, curl *charm.URL, err error) { u := strings.Split(name, "/") if len(u) > 5 { u = u[len(u)-5:] burl = name } else { burl = "lp:" + name } if len(u) < 5 || u[1] != "charms" || u[4] != "trunk" || len(u[0]) == 0 || u[0][0] != '~' { return "", nil, fmt.Errorf("unwanted branch name: %s", name) } curl, err = charm.ParseURL(fmt.Sprintf("cs:%s/%s/%s", u[0], u[2], u[3])) if err != nil { return "", nil, err } return burl, curl, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/server_test.go�����������������������������������0000644�0000153�0000161�00000037675�12321735642�024737� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package store_test import ( "encoding/json" "io/ioutil" "net/http" "net/http/httptest" "net/url" "strconv" "strings" "time" "labix.org/v2/mgo/bson" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/store" ) func (s *StoreSuite) prepareServer(c *gc.C) (*store.Server, *charm.URL) { curl := charm.MustParseURL("cs:precise/wordpress") pub, err := s.store.CharmPublisher([]*charm.URL{curl}, "some-digest") c.Assert(err, gc.IsNil) err = pub.Publish(&FakeCharmDir{}) c.Assert(err, gc.IsNil) server, err := store.NewServer(s.store) c.Assert(err, gc.IsNil) return server, curl } func (s *StoreSuite) TestServerCharmInfo(c *gc.C) { server, curl := s.prepareServer(c) req, err := http.NewRequest("GET", "/charm-info", nil) c.Assert(err, gc.IsNil) var tests = []struct{ url, canonical, sha, digest, err string }{ {curl.String(), curl.String(), fakeRevZeroSha, "some-digest", ""}, {"cs:oneiric/non-existent", "", "", "", "entry not found"}, {"cs:wordpress", curl.String(), fakeRevZeroSha, "some-digest", ""}, {"cs:/bad", "", "", "", `charm URL has invalid series: "cs:/bad"`}, {"gopher:archie-server", "", "", "", `charm URL has invalid schema: "gopher:archie-server"`}, } for _, t := range tests { req.Form = url.Values{"charms": []string{t.url}} rec := httptest.NewRecorder() server.ServeHTTP(rec, req) expected := make(map[string]interface{}) if t.sha != "" { expected[t.url] = map[string]interface{}{ "canonical-url": t.canonical, "revision": float64(0), "sha256": t.sha, "digest": t.digest, } } else { expected[t.url] = map[string]interface{}{ "revision": float64(0), "errors": []interface{}{t.err}, } } obtained := map[string]interface{}{} err = json.NewDecoder(rec.Body).Decode(&obtained) c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, expected, gc.Commentf("URL: %s", t.url)) c.Assert(rec.Header().Get("Content-Type"), gc.Equals, "application/json") } // 2 charm-info events, one for resolved URL, one for the reference. s.checkCounterSum(c, []string{"charm-info", curl.Series, curl.Name}, false, 2) s.checkCounterSum(c, []string{"charm-missing", "oneiric", "non-existent"}, false, 1) } func (s *StoreSuite) TestServerCharmEvent(c *gc.C) { server, _ := s.prepareServer(c) req, err := http.NewRequest("GET", "/charm-event", nil) c.Assert(err, gc.IsNil) url1 := charm.MustParseURL("cs:oneiric/wordpress") url2 := charm.MustParseURL("cs:oneiric/mysql") urls := []*charm.URL{url1, url2} event1 := &store.CharmEvent{ Kind: store.EventPublished, Revision: 42, Digest: "revKey1", URLs: urls, Warnings: []string{"A warning."}, Time: time.Unix(1, 0), } event2 := &store.CharmEvent{ Kind: store.EventPublished, Revision: 43, Digest: "revKey2", URLs: urls, Time: time.Unix(2, 0), } event3 := &store.CharmEvent{ Kind: store.EventPublishError, Digest: "revKey3", Errors: []string{"An error."}, URLs: urls[:1], Time: time.Unix(3, 0), } for _, event := range []*store.CharmEvent{event1, event2, event3} { err := s.store.LogCharmEvent(event) c.Assert(err, gc.IsNil) } var tests = []struct { query string kind, digest string err, warn string time string revision int }{ { query: url1.String(), digest: "revKey3", kind: "publish-error", err: "An error.", time: "1970-01-01T00:00:03Z", }, { query: url2.String(), digest: "revKey2", kind: "published", revision: 43, time: "1970-01-01T00:00:02Z", }, { query: url1.String() + "@revKey1", digest: "revKey1", kind: "published", revision: 42, warn: "A warning.", time: "1970-01-01T00:00:01Z", }, { query: "cs:non/existent", revision: 0, err: "entry not found", }, } for _, t := range tests { req.Form = url.Values{"charms": []string{t.query}} rec := httptest.NewRecorder() server.ServeHTTP(rec, req) url := t.query if i := strings.Index(url, "@"); i >= 0 { url = url[:i] } info := map[string]interface{}{ "kind": "", "revision": float64(0), } if t.kind != "" { info["kind"] = t.kind info["revision"] = float64(t.revision) info["digest"] = t.digest info["time"] = t.time } if t.err != "" { info["errors"] = []interface{}{t.err} } if t.warn != "" { info["warnings"] = []interface{}{t.warn} } expected := map[string]interface{}{url: info} obtained := map[string]interface{}{} err = json.NewDecoder(rec.Body).Decode(&obtained) c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, expected) c.Assert(rec.Header().Get("Content-Type"), gc.Equals, "application/json") } s.checkCounterSum(c, []string{"charm-event", "oneiric", "wordpress"}, false, 2) s.checkCounterSum(c, []string{"charm-event", "oneiric", "mysql"}, false, 1) } func (s *StoreSuite) TestSeriesNotFound(c *gc.C) { server, err := store.NewServer(s.store) req, err := http.NewRequest("GET", "/charm-info?charms=cs:not-found", nil) c.Assert(err, gc.IsNil) rec := httptest.NewRecorder() server.ServeHTTP(rec, req) c.Assert(rec.Code, gc.Equals, http.StatusOK) expected := map[string]interface{}{"cs:not-found": map[string]interface{}{ "revision": float64(0), "errors": []interface{}{"entry not found"}}} obtained := map[string]interface{}{} err = json.NewDecoder(rec.Body).Decode(&obtained) c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, expected) } // 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) req := store.CounterRequest{Key: key, Prefix: prefix} cs, err := s.store.Counters(&req) c.Assert(err, gc.IsNil) if sum = cs[0].Count; sum == expected { if expected == 0 && retry < 2 { continue // Wait a bit to make sure. } return } } c.Errorf("counter sum for %#v is %d, want %d", key, sum, expected) } func (s *StoreSuite) TestCharmStreaming(c *gc.C) { server, curl := s.prepareServer(c) req, err := http.NewRequest("GET", "/charm/"+curl.String()[3:], nil) c.Assert(err, gc.IsNil) rec := httptest.NewRecorder() server.ServeHTTP(rec, req) data, err := ioutil.ReadAll(rec.Body) c.Assert(string(data), gc.Equals, "charm-revision-0") c.Assert(rec.Header().Get("Connection"), gc.Equals, "close") c.Assert(rec.Header().Get("Content-Type"), gc.Equals, "application/octet-stream") c.Assert(rec.Header().Get("Content-Length"), gc.Equals, "16") // Check that it was accounted for in statistics. s.checkCounterSum(c, []string{"charm-bundle", curl.Series, curl.Name}, false, 1) } func (s *StoreSuite) TestDisableStats(c *gc.C) { server, curl := s.prepareServer(c) req, err := http.NewRequest("GET", "/charm-info", nil) c.Assert(err, gc.IsNil) req.Form = url.Values{"charms": []string{curl.String()}, "stats": []string{"0"}} rec := httptest.NewRecorder() server.ServeHTTP(rec, req) c.Assert(rec.Code, gc.Equals, 200) req, err = http.NewRequest("GET", "/charm/"+curl.String()[3:], nil) c.Assert(err, gc.IsNil) req.Form = url.Values{"stats": []string{"0"}} rec = httptest.NewRecorder() server.ServeHTTP(rec, req) c.Assert(rec.Code, gc.Equals, 200) // No statistics should have been collected given the use of stats=0. for _, prefix := range []string{"charm-info", "charm-bundle", "charm-missing"} { s.checkCounterSum(c, []string{prefix}, true, 0) } } func (s *StoreSuite) TestServerStatus(c *gc.C) { server, err := store.NewServer(s.store) c.Assert(err, gc.IsNil) tests := []struct { path string code int }{ {"/charm-info/any", 404}, {"/charm/bad-url", 404}, {"/charm/bad-series/wordpress", 404}, {"/stats/counter/", 403}, {"/stats/counter/*", 403}, {"/stats/counter/any/", 404}, {"/stats/", 404}, {"/stats/any", 404}, } for _, test := range tests { req, err := http.NewRequest("GET", test.path, nil) c.Assert(err, gc.IsNil) rec := httptest.NewRecorder() server.ServeHTTP(rec, req) c.Assert(rec.Code, gc.Equals, test.code, gc.Commentf("Path: %s", test.path)) } } func (s *StoreSuite) TestRootRedirect(c *gc.C) { server, err := store.NewServer(s.store) c.Assert(err, gc.IsNil) req, err := http.NewRequest("GET", "/", nil) c.Assert(err, gc.IsNil) rec := httptest.NewRecorder() server.ServeHTTP(rec, req) c.Assert(rec.Code, gc.Equals, 303) c.Assert(rec.Header().Get("Location"), gc.Equals, "https://juju.ubuntu.com") } 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) } server, _ := s.prepareServer(c) expected := map[string]string{ "a:b": "2", "a:b:*": "0", "a:*": "3", "a": "1", "a:b:c": "0", } for counter, n := range expected { req, err := http.NewRequest("GET", "/stats/counter/"+counter, nil) c.Assert(err, gc.IsNil) rec := httptest.NewRecorder() server.ServeHTTP(rec, req) data, err := ioutil.ReadAll(rec.Body) c.Assert(string(data), gc.Equals, n) c.Assert(rec.Header().Get("Content-Type"), gc.Equals, "text/plain") c.Assert(rec.Header().Get("Content-Length"), gc.Equals, strconv.Itoa(len(n))) } } func (s *StoreSuite) TestStatsCounterList(c *gc.C) { if *noTestMongoJs { c.Skip("MongoDB javascript not available") } incs := [][]string{ {"a"}, {"a", "b"}, {"a", "b", "c"}, {"a", "b", "c"}, {"a", "b", "d"}, {"a", "b", "e"}, {"a", "f", "g"}, {"a", "f", "h"}, {"a", "i"}, {"j", "k"}, } for _, key := range incs { err := s.store.IncCounter(key) c.Assert(err, gc.IsNil) } server, _ := s.prepareServer(c) tests := []struct { key, format, result string }{ {"a", "", "a 1\n"}, {"a:*", "", "a:b:* 4\na:f:* 2\na:b 1\na:i 1\n"}, {"a:b:*", "", "a:b:c 2\na:b:d 1\na:b:e 1\n"}, {"a:*", "csv", "a:b:*,4\na:f:*,2\na:b,1\na:i,1\n"}, {"a:*", "json", `[["a:b:*",4],["a:f:*",2],["a:b",1],["a:i",1]]`}, } for _, test := range tests { req, err := http.NewRequest("GET", "/stats/counter/"+test.key, nil) c.Assert(err, gc.IsNil) req.Form = url.Values{"list": []string{"1"}} if test.format != "" { req.Form.Set("format", test.format) } rec := httptest.NewRecorder() server.ServeHTTP(rec, req) data, err := ioutil.ReadAll(rec.Body) c.Assert(string(data), gc.Equals, test.result) c.Assert(rec.Header().Get("Content-Type"), gc.Equals, "text/plain") c.Assert(rec.Header().Get("Content-Length"), gc.Equals, strconv.Itoa(len(test.result))) } } func (s *StoreSuite) TestStatsCounterBy(c *gc.C) { if *noTestMongoJs { c.Skip("MongoDB javascript not available") } incs := []struct { key []string day int }{ {[]string{"a"}, 1}, {[]string{"a"}, 1}, {[]string{"b"}, 1}, {[]string{"a", "b"}, 1}, {[]string{"a", "c"}, 1}, {[]string{"a"}, 3}, {[]string{"a", "b"}, 3}, {[]string{"b"}, 9}, {[]string{"b"}, 9}, {[]string{"a", "c", "d"}, 9}, {[]string{"a", "c", "e"}, 9}, {[]string{"a", "c", "f"}, 9}, } day := func(i int) time.Time { return time.Date(2012, time.May, i, 0, 0, 0, 0, time.UTC) } server, _ := s.prepareServer(c) counters := s.Session.DB("juju").C("stat.counters") for i, inc := range incs { err := s.store.IncCounter(inc.key) c.Assert(err, gc.IsNil) // Hack time so counters are assigned to 2012-05-<day> filter := bson.M{"t": bson.M{"$gt": store.TimeToStamp(time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC))}} stamp := store.TimeToStamp(day(inc.day)) stamp += int32(i) * 60 // Make every entry unique. err = counters.Update(filter, bson.D{{"$set", bson.D{{"t", stamp}}}}) c.Check(err, gc.IsNil) } tests := []struct { request store.CounterRequest format string result string }{ { store.CounterRequest{ Key: []string{"a"}, Prefix: false, List: false, By: store.ByDay, }, "", "2012-05-01 2\n2012-05-03 1\n", }, { store.CounterRequest{ Key: []string{"a"}, Prefix: false, List: false, By: store.ByDay, }, "csv", "2012-05-01,2\n2012-05-03,1\n", }, { store.CounterRequest{ Key: []string{"a"}, Prefix: false, List: false, By: store.ByDay, }, "json", `[["2012-05-01",2],["2012-05-03",1]]`, }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: false, By: store.ByDay, }, "", "2012-05-01 2\n2012-05-03 1\n2012-05-09 3\n", }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: false, By: store.ByDay, Start: time.Date(2012, 5, 2, 0, 0, 0, 0, time.UTC), }, "", "2012-05-03 1\n2012-05-09 3\n", }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: false, By: store.ByDay, Stop: time.Date(2012, 5, 4, 0, 0, 0, 0, time.UTC), }, "", "2012-05-01 2\n2012-05-03 1\n", }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: false, By: store.ByDay, Start: time.Date(2012, 5, 3, 0, 0, 0, 0, time.UTC), Stop: time.Date(2012, 5, 3, 0, 0, 0, 0, time.UTC), }, "", "2012-05-03 1\n", }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: true, By: store.ByDay, }, "", "a:b 2012-05-01 1\na:c 2012-05-01 1\na:b 2012-05-03 1\na:c:* 2012-05-09 3\n", }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: false, By: store.ByWeek, }, "", "2012-05-06 3\n2012-05-13 3\n", }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: true, By: store.ByWeek, }, "", "a:b 2012-05-06 2\na:c 2012-05-06 1\na:c:* 2012-05-13 3\n", }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: true, By: store.ByWeek, }, "csv", "a:b,2012-05-06,2\na:c,2012-05-06,1\na:c:*,2012-05-13,3\n", }, { store.CounterRequest{ Key: []string{"a"}, Prefix: true, List: true, By: store.ByWeek, }, "json", `[["a:b","2012-05-06",2],["a:c","2012-05-06",1],["a:c:*","2012-05-13",3]]`, }, } for _, test := range tests { path := "/stats/counter/" + strings.Join(test.request.Key, ":") if test.request.Prefix { path += ":*" } req, err := http.NewRequest("GET", path, nil) req.Form = url.Values{} c.Assert(err, gc.IsNil) if test.request.List { req.Form.Set("list", "1") } if test.format != "" { req.Form.Set("format", test.format) } if !test.request.Start.IsZero() { req.Form.Set("start", test.request.Start.Format("2006-01-02")) } if !test.request.Stop.IsZero() { req.Form.Set("stop", test.request.Stop.Format("2006-01-02")) } switch test.request.By { case store.ByDay: req.Form.Set("by", "day") case store.ByWeek: req.Form.Set("by", "week") } rec := httptest.NewRecorder() server.ServeHTTP(rec, req) data, err := ioutil.ReadAll(rec.Body) c.Assert(string(data), gc.Equals, test.result) c.Assert(rec.Header().Get("Content-Type"), gc.Equals, "text/plain") c.Assert(rec.Header().Get("Content-Length"), gc.Equals, strconv.Itoa(len(test.result))) } } func (s *StoreSuite) TestBlitzKey(c *gc.C) { server, _ := s.prepareServer(c) // This is just a validation key to allow blitz.io to run // performance tests against the site. req, err := http.NewRequest("GET", "/mu-35700a31-6bf320ca-a800b670-05f845ee", nil) c.Assert(err, gc.IsNil) rec := httptest.NewRecorder() server.ServeHTTP(rec, req) data, err := ioutil.ReadAll(rec.Body) c.Assert(string(data), gc.Equals, "42") c.Assert(rec.Header().Get("Connection"), gc.Equals, "close") c.Assert(rec.Header().Get("Content-Type"), gc.Equals, "text/plain") c.Assert(rec.Header().Get("Content-Length"), gc.Equals, "2") } �������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/store/store.go�����������������������������������������0000644�0000153�0000161�00000075626�12321735776�023534� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The store package is capable of storing and updating charms in a MongoDB // database, as well as maintaining further information about them such as // the VCS revision the charm was loaded from and the URLs for the charms. package store import ( "crypto/sha256" "encoding/hex" "errors" "fmt" "hash" "io" "sort" "strconv" "strings" "sync" "time" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/log" ) // The following MongoDB collections are currently used: // // juju.events - Log of events relating to the lifecycle of charms // juju.charms - Information about the stored charms // juju.charmfs.* - GridFS with the charm files // juju.locks - Has unique keys with url of updating charms // juju.stat.counters - Counters for statistics // juju.stat.tokens - Tokens used in statistics counter keys var ( ErrUpdateConflict = errors.New("charm update in progress") ErrRedundantUpdate = errors.New("charm is up-to-date") // Note that this error message is part of the API, since it's sent // both in charm-info and charm-event responses as errors indicating // that the given charm or charm event wasn't found. ErrNotFound = errors.New("entry not found") ) const ( UpdateTimeout = 600e9 ) // Store holds a connection to a charm store. type Store struct { session *storeSession // Cache for statistics key words (two generations). cacheMu sync.RWMutex statsIdNew map[string]int statsIdOld map[string]int statsTokenNew map[int]string statsTokenOld map[int]string } // Open creates a new session with the store. It connects to the MongoDB // server at the given address (as expected by the Mongo function in the // labix.org/v2/mgo package). func Open(mongoAddr string) (store *Store, err error) { log.Infof("store: Store opened. Connecting to: %s", mongoAddr) store = &Store{} session, err := mgo.Dial(mongoAddr) if err != nil { log.Errorf("store: Error connecting to MongoDB: %v", err) return nil, err } store = &Store{session: &storeSession{session}} // Ignore error. It'll always fail after created. // TODO Check the error once mgo hands it to us. _ = store.session.DB("juju").Run(bson.D{{"create", "stat.counters"}, {"autoIndexId", false}}, nil) if err := store.ensureIndexes(); err != nil { session.Close() return nil, err } // Put the used socket back in the pool. session.Refresh() return store, nil } func (s *Store) ensureIndexes() error { session := s.session indexes := []struct { c *mgo.Collection i mgo.Index }{{ session.StatCounters(), mgo.Index{Key: []string{"k", "t"}, Unique: true}, }, { session.StatTokens(), mgo.Index{Key: []string{"t"}, Unique: true}, }, { session.Charms(), mgo.Index{Key: []string{"urls", "revision"}, Unique: true}, }, { session.Events(), mgo.Index{Key: []string{"urls", "digest"}}, }} for _, idx := range indexes { err := idx.c.EnsureIndex(idx.i) if err != nil { log.Errorf("store: Error ensuring stat.counters index: %v", err) return err } } return nil } // Close terminates the connection with the store. func (s *Store) Close() { s.session.Close() } // statsKey returns the compound statistics identifier that represents key. // If write is true, the identifier will be created if necessary. // Identifiers have a form similar to "ab:c:def:", where each section is a // base-32 number that represents the respective word in key. This form // allows efficiently indexing and searching for prefixes, while detaching // the key content and size from the actual words used in key. func (s *Store) statsKey(session *storeSession, key []string, write bool) (string, error) { if len(key) == 0 { return "", fmt.Errorf("store: empty statistics key") } tokens := session.StatTokens() skey := make([]byte, 0, len(key)*4) // Retry limit is mainly to prevent infinite recursion in edge cases, // such as if the database is ever run in read-only mode. // The logic below should deteministically stop in normal scenarios. var err error for i, retry := 0, 30; i < len(key) && retry > 0; retry-- { err = nil id, found := s.statsTokenId(key[i]) if !found { var t tokenId err = tokens.Find(bson.D{{"t", key[i]}}).One(&t) if err == mgo.ErrNotFound { if !write { return "", ErrNotFound } t.Id, err = tokens.Count() if err != nil { continue } t.Id++ t.Token = key[i] err = tokens.Insert(&t) } if err != nil { continue } s.cacheStatsTokenId(t.Token, t.Id) id = t.Id } skey = strconv.AppendInt(skey, int64(id), 32) skey = append(skey, ':') i++ } if err != nil { return "", err } return string(skey), nil } const statsTokenCacheSize = 1024 type tokenId struct { Id int "_id" Token string "t" } // cacheStatsTokenId adds the id for token into the cache. // The cache has two generations so that the least frequently used // tokens are evicted regularly. func (s *Store) cacheStatsTokenId(token string, id int) { s.cacheMu.Lock() defer s.cacheMu.Unlock() // Can't possibly be >, but reviews want it for defensiveness. if len(s.statsIdNew) >= statsTokenCacheSize { s.statsIdOld = s.statsIdNew s.statsIdNew = nil s.statsTokenOld = s.statsTokenNew s.statsTokenNew = nil } if s.statsIdNew == nil { s.statsIdNew = make(map[string]int, statsTokenCacheSize) s.statsTokenNew = make(map[int]string, statsTokenCacheSize) } s.statsIdNew[token] = id s.statsTokenNew[id] = token } // statsTokenId returns the id for token from the cache, if found. func (s *Store) statsTokenId(token string) (id int, found bool) { s.cacheMu.RLock() id, found = s.statsIdNew[token] if found { s.cacheMu.RUnlock() return } id, found = s.statsIdOld[token] s.cacheMu.RUnlock() if found { s.cacheStatsTokenId(token, id) } return } // statsIdToken returns the token for id from the cache, if found. func (s *Store) statsIdToken(id int) (token string, found bool) { s.cacheMu.RLock() token, found = s.statsTokenNew[id] if found { s.cacheMu.RUnlock() return } token, found = s.statsTokenOld[id] s.cacheMu.RUnlock() if found { s.cacheStatsTokenId(token, id) } return } var counterEpoch = time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC).Unix() func timeToStamp(t time.Time) int32 { return int32(t.Unix() - counterEpoch) } // IncCounter increases by one the counter associated with the composed key. func (s *Store) IncCounter(key []string) error { session := s.session.Copy() defer session.Close() skey, err := s.statsKey(session, key, true) if err != nil { return err } t := time.Now().UTC() // Round to the start of the minute so we get one document per minute at most. t = t.Add(-time.Duration(t.Second()) * time.Second) counters := session.StatCounters() _, err = counters.Upsert(bson.D{{"k", skey}, {"t", timeToStamp(t)}}, bson.D{{"$inc", bson.D{{"c", 1}}}}) return err } // CounterRequest represents a request to aggregate counter values. type CounterRequest struct { // Key and Prefix determine the counter keys to match. // If Prefix is false, Key must match exactly. Otherwise, counters // must begin with Key and have at least one more key token. Key []string Prefix bool // If List is true, matching counters are aggregated under their // prefixes instead of being returned as a single overall sum. // // For example, given the following counts: // // {"a", "b"}: 1, // {"a", "c"}: 3 // {"a", "c", "d"}: 5 // {"a", "c", "e"}: 7 // // and assuming that Prefix is true, the following keys will // present the respective results if List is true: // // {"a"} => {{"a", "b"}, 1, false}, // {{"a", "c"}, 3, false}, // {{"a", "c"}, 12, true} // {"a", "c"} => {{"a", "c", "d"}, 3, false}, // {{"a", "c", "e"}, 5, false} // // If List is false, the same key prefixes will present: // // {"a"} => {{"a"}, 16, true} // {"a", "c"} => {{"a", "c"}, 12, false} // List bool // By defines the period covered by each aggregated data point. // If unspecified, it defaults to ByAll, which aggregates all // matching data points in a single entry. By CounterRequestBy // Start, if provided, changes the query so that only data points // ocurring at the given time or afterwards are considered. Start time.Time // Stop, if provided, changes the query so that only data points // ocurring at the given time or before are considered. Stop time.Time } type CounterRequestBy int const ( ByAll CounterRequestBy = iota ByDay ByWeek ) type Counter struct { Key []string Prefix bool Count int64 Time time.Time } // Counters aggregates and returns counter values according to the provided request. func (s *Store) Counters(req *CounterRequest) ([]Counter, error) { session := s.session.Copy() defer session.Close() tokensColl := session.StatTokens() countersColl := session.StatCounters() searchKey, err := s.statsKey(session, req.Key, false) if err == ErrNotFound { if !req.List { return []Counter{{Key: req.Key, Prefix: req.Prefix, Count: 0}}, nil } return nil, nil } if err != nil { return nil, err } var regex string if req.Prefix { regex = "^" + searchKey + ".+" } else { regex = "^" + searchKey + "$" } // This reduce function simply sums, for each emitted key, all the values found under it. job := mgo.MapReduce{Reduce: "function(key, values) { return Array.sum(values); }"} var emit string switch req.By { case ByDay: emit = "emit(k+'@'+NumberInt(this.t/86400), this.c);" case ByWeek: emit = "emit(k+'@'+NumberInt(this.t/604800), this.c);" default: emit = "emit(k, this.c);" } if req.List && req.Prefix { // For a search key "a:b:" matching a key "a:b:c:d:e:", this map function emits "a:b:c:*". // For a search key "a:b:" matching a key "a:b:c:", it emits "a:b:c:". // For a search key "a:b:" matching a key "a:b:", it emits "a:b:". job.Scope = bson.D{{"searchKeyLen", len(searchKey)}} job.Map = fmt.Sprintf(` function() { var k = this.k; var i = k.indexOf(':', searchKeyLen)+1; if (k.length > i) { k = k.substr(0, i)+'*'; } %s }`, emit) } else { // For a search key "a:b:" matching a key "a:b:c:d:e:", this map function emits "a:b:*". // For a search key "a:b:" matching a key "a:b:c:", it also emits "a:b:*". // For a search key "a:b:" matching a key "a:b:", it emits "a:b:". emitKey := searchKey if req.Prefix { emitKey += "*" } job.Scope = bson.D{{"emitKey", emitKey}} job.Map = fmt.Sprintf(` function() { var k = emitKey; %s }`, emit) } var result []struct { Key string `bson:"_id"` Value int64 } var query, tquery bson.D if !req.Start.IsZero() { tquery = append(tquery, bson.DocElem{"$gte", timeToStamp(req.Start)}) } if !req.Stop.IsZero() { tquery = append(tquery, bson.DocElem{"$lte", timeToStamp(req.Stop)}) } if len(tquery) == 0 { query = bson.D{{"k", bson.D{{"$regex", regex}}}} } else { query = bson.D{{"k", bson.D{{"$regex", regex}}}, {"t", tquery}} } _, err = countersColl.Find(query).MapReduce(&job, &result) if err != nil { return nil, err } var counters []Counter for i := range result { key := result[i].Key when := time.Time{} if req.By != ByAll { var stamp int64 if at := strings.Index(key, "@"); at != -1 && len(key) > at+1 { stamp, _ = strconv.ParseInt(key[at+1:], 10, 32) key = key[:at] } if stamp == 0 { return nil, fmt.Errorf("internal error: bad aggregated key: %q", result[i].Key) } switch req.By { case ByDay: stamp = stamp * 86400 case ByWeek: // The +1 puts it at the end of the period. stamp = (stamp + 1) * 604800 } when = time.Unix(counterEpoch+stamp, 0).In(time.UTC) } ids := strings.Split(key, ":") tokens := make([]string, 0, len(ids)) for i := 0; i < len(ids)-1; i++ { if ids[i] == "*" { continue } id, err := strconv.ParseInt(ids[i], 32, 32) if err != nil { return nil, fmt.Errorf("store: invalid id: %q", ids[i]) } token, found := s.statsIdToken(int(id)) if !found { var t tokenId err = tokensColl.FindId(id).One(&t) if err == mgo.ErrNotFound { return nil, fmt.Errorf("store: internal error; token id not found: %d", id) } s.cacheStatsTokenId(t.Token, t.Id) token = t.Token } tokens = append(tokens, token) } counter := Counter{ Key: tokens, Prefix: len(ids) > 0 && ids[len(ids)-1] == "*", Count: result[i].Value, Time: when, } counters = append(counters, counter) } if !req.List && len(counters) == 0 { counters = []Counter{{Key: req.Key, Prefix: req.Prefix, Count: 0}} } else if len(counters) > 1 { sort.Sort(sortableCounters(counters)) } return counters, nil } type sortableCounters []Counter func (s sortableCounters) Len() int { return len(s) } func (s sortableCounters) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s sortableCounters) Less(i, j int) bool { // Earlier times first. if !s[i].Time.Equal(s[j].Time) { return s[i].Time.Before(s[j].Time) } // Then larger counts first. if s[i].Count != s[j].Count { return s[j].Count < s[i].Count } // Then smaller/shorter keys first. ki := s[i].Key kj := s[j].Key for n := range ki { if n >= len(kj) { return false } if ki[n] != kj[n] { return ki[n] < kj[n] } } if len(ki) < len(kj) { return true } // Then full keys first. return !s[i].Prefix && s[j].Prefix } // A CharmPublisher is responsible for importing a charm dir onto the store. type CharmPublisher struct { revision int w *charmWriter } // Revision returns the revision that will be assigned to the published charm. func (p *CharmPublisher) Revision() int { return p.revision } // CharmDir matches the part of the interface of *charm.Dir that is necessary // to publish a charm. Using this interface rather than *charm.Dir directly // makes testing some aspects of the store possible. type CharmDir interface { Meta() *charm.Meta Config() *charm.Config SetRevision(revision int) BundleTo(w io.Writer) error } // Statically ensure that *charm.Dir is indeed a CharmDir. var _ CharmDir = (*charm.Dir)(nil) // Publish bundles charm and writes it to the store. The written charm // bundle will have its revision set to the result of Revision. // Publish must be called only once for a CharmPublisher. func (p *CharmPublisher) Publish(charm CharmDir) error { w := p.w if w == nil { panic("CharmPublisher already published a charm") } p.w = nil w.charm = charm // TODO: Refactor to BundleTo(w, revision) charm.SetRevision(p.revision) err := charm.BundleTo(w) if err == nil { err = w.finish() } else { w.abort() } return err } // CharmPublisher returns a new CharmPublisher for importing a charm that // will be made available in the store at all of the provided URLs. // The digest parameter must contain the unique identifier that // represents the charm data being imported (e.g. the VCS revision sha1). // ErrRedundantUpdate is returned if all of the provided urls are // already associated to that digest. func (s *Store) CharmPublisher(urls []*charm.URL, digest string) (p *CharmPublisher, err error) { log.Infof("store: Trying to add charms %v with key %q...", urls, digest) if err = mustLackRevision("CharmPublisher", urls...); err != nil { return } session := s.session.Copy() defer session.Close() maxRev := -1 newKey := false charms := session.Charms() doc := charmDoc{} for i := range urls { urlStr := urls[i].String() err = charms.Find(bson.D{{"urls", urlStr}}).Sort("-revision").One(&doc) if err == mgo.ErrNotFound { log.Infof("store: Charm %s not yet in the store.", urls[i]) newKey = true continue } if doc.Digest != digest { log.Infof("store: Charm %s is out of date with revision key %q.", urlStr, digest) newKey = true } if err != nil { log.Errorf("store: Unknown error looking for charm %s: %s", urlStr, err) return } if doc.Revision > maxRev { maxRev = doc.Revision } } if !newKey { log.Infof("store: All charms have revision key %q. Nothing to update.", digest) err = ErrRedundantUpdate return } revision := maxRev + 1 log.Infof("store: Preparing writer to add charms with revision %d.", revision) w := &charmWriter{ store: s, urls: urls, revision: revision, digest: digest, } return &CharmPublisher{revision, w}, nil } // charmWriter is an io.Writer that writes charm bundles to the charms GridFS. type charmWriter struct { store *Store session *storeSession file *mgo.GridFile sha256 hash.Hash charm CharmDir urls []*charm.URL revision int digest string } // Write creates an entry in the charms GridFS when first called, // and streams all written data into it. func (w *charmWriter) Write(data []byte) (n int, err error) { if w.file == nil { w.session = w.store.session.Copy() w.file, err = w.session.CharmFS().Create("") if err != nil { log.Errorf("store: Failed to create GridFS file: %v", err) return 0, err } w.sha256 = sha256.New() log.Infof("store: Creating GridFS file with id %q...", w.file.Id().(bson.ObjectId).Hex()) } _, err = w.sha256.Write(data) if err != nil { panic("hash.Hash should never error") } return w.file.Write(data) } // abort cancels the charm writing. func (w *charmWriter) abort() { if w.file != nil { // Ignore error. Already aborting due to a preceding bad situation // elsewhere. This error is not important right now. _ = w.file.Close() w.session.Close() } } // finish completes the charm writing process and inserts the final metadata. // After it completes the charm will be available for consumption. func (w *charmWriter) finish() error { if w.file == nil { return nil } defer w.session.Close() id := w.file.Id() size := w.file.Size() err := w.file.Close() if err != nil { log.Errorf("store: Failed to close GridFS file: %v", err) return err } charms := w.session.Charms() sha256 := hex.EncodeToString(w.sha256.Sum(nil)) charm := charmDoc{ w.urls, w.revision, w.digest, sha256, size, id.(bson.ObjectId), w.charm.Meta(), w.charm.Config(), } if err = charms.Insert(&charm); err != nil { err = maybeConflict(err) log.Errorf("store: Failed to insert new revision of charm %v: %v", w.urls, err) return err } return nil } type CharmInfo struct { revision int digest string sha256 string size int64 fileId bson.ObjectId meta *charm.Meta config *charm.Config } // Statically ensure CharmInfo is a charm.Charm. var _ charm.Charm = (*CharmInfo)(nil) // Revision returns the store charm's revision. func (ci *CharmInfo) Revision() int { return ci.revision } // BundleSha256 returns the sha256 checksum for the stored charm bundle. func (ci *CharmInfo) BundleSha256() string { return ci.sha256 } // BundleSize returns the size for the stored charm bundle. func (ci *CharmInfo) BundleSize() int64 { return ci.size } // Digest returns the unique identifier that represents the charm // data imported. This is typically set to the VCS revision digest. func (ci *CharmInfo) Digest() string { return ci.digest } // Meta returns the charm.Meta details for the stored charm. func (ci *CharmInfo) Meta() *charm.Meta { return ci.meta } // Config returns the charm.Config details for the stored charm. func (ci *CharmInfo) Config() *charm.Config { return ci.config } var ltsReleases = map[string]bool{ "lucid": true, "precise": true, "trusty": true, } type byPreferredSeries []string func (s byPreferredSeries) Len() int { return len(s) } func (s byPreferredSeries) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s byPreferredSeries) Less(i, j int) bool { _, iLts := ltsReleases[s[i]] _, jLts := ltsReleases[s[j]] if iLts == jLts { return sort.StringSlice(s).Less(j, i) } return iLts } // Series returns all the series available for a charm reference, in descending // order of preference. LTS releases preferred over non-LTS func (s *Store) Series(ref charm.Reference) ([]string, error) { session := s.session.Copy() defer session.Close() patternURL := &charm.URL{Reference: ref, Series: "[a-z][^/]+"} patternURL = patternURL.WithRevision(-1) charms := session.Charms() q := charms.Find(bson.M{ "urls": bson.RegEx{Pattern: fmt.Sprintf("^%s$", patternURL.String())}, }) var cdocs []charmDoc err := q.All(&cdocs) if err != nil { return nil, err } // Unique set of series seriesSet := make(map[string]bool) for _, cdoc := range cdocs { for _, url := range cdoc.URLs { if ref == url.Reference { seriesSet[url.Series] = true } } } // Collect into a slice var result []string for series := range seriesSet { result = append(result, series) } sort.Sort(byPreferredSeries(result)) return result, nil } // getRevisions returns at most the last n revisions for charm at url, // in descending revision order. For limit n=0, all revisions are returned. func (s *Store) getRevisions(url *charm.URL, n int) ([]*CharmInfo, error) { session := s.session.Copy() defer session.Close() log.Debugf("store: Retrieving charm info for %s", url) rev := url.Revision url = url.WithRevision(-1) charms := session.Charms() var cdocs []charmDoc var qdoc interface{} if rev == -1 { qdoc = bson.D{{"urls", url}} } else { qdoc = bson.D{{"urls", url}, {"revision", rev}} } q := charms.Find(qdoc).Sort("-revision") if n > 0 { q = q.Limit(n) } if err := q.All(&cdocs); err != nil { log.Errorf("store: Failed to find charm %s: %v", url, err) return nil, ErrNotFound } var infos []*CharmInfo for _, cdoc := range cdocs { infos = append(infos, &CharmInfo{ cdoc.Revision, cdoc.Digest, cdoc.Sha256, cdoc.Size, cdoc.FileId, cdoc.Meta, cdoc.Config, }) } return infos, nil } // CharmInfo retrieves the CharmInfo value for the charm at url. func (s *Store) CharmInfo(url *charm.URL) (*CharmInfo, error) { infos, err := s.getRevisions(url, 1) if err != nil { log.Errorf("store: Failed to find charm %s: %v", url, err) return nil, ErrNotFound } else if len(infos) < 1 { return nil, ErrNotFound } return infos[0], nil } // OpenCharm opens for reading via rc the charm currently available at url. // rc must be closed after dealing with it or resources will leak. func (s *Store) OpenCharm(url *charm.URL) (info *CharmInfo, rc io.ReadCloser, err error) { log.Debugf("store: Opening charm %s", url) info, err = s.CharmInfo(url) if err != nil { return nil, nil, err } session := s.session.Copy() file, err := session.CharmFS().OpenId(info.fileId) if err != nil { log.Errorf("store: Failed to open GridFS file for charm %s: %v", url, err) session.Close() return nil, nil, err } rc = &reader{session, file} return } // DeleteCharm deletes the charms matching url. If no revision is specified, // all revisions of the charm are deleted. func (s *Store) DeleteCharm(url *charm.URL) ([]*CharmInfo, error) { log.Debugf("store: Deleting charm %s", url) infos, err := s.getRevisions(url, 0) if err != nil { return nil, err } if len(infos) == 0 { return nil, ErrNotFound } session := s.session.Copy() defer session.Close() var deleted []*CharmInfo for _, info := range infos { err := session.Charms().Remove( bson.D{{"urls", url.WithRevision(-1)}, {"revision", info.Revision()}}) if err != nil { log.Errorf("store: Failed to delete metadata for charm %s: %v", url, err) return deleted, err } err = session.CharmFS().RemoveId(info.fileId) if err != nil { log.Errorf("store: Failed to delete GridFS file for charm %s: %v", url, err) return deleted, err } deleted = append(deleted, info) } return deleted, err } type reader struct { session *storeSession file *mgo.GridFile } // Read consumes data from the opened charm. func (r *reader) Read(buf []byte) (n int, err error) { return r.file.Read(buf) } // Close closes the opened charm and frees associated resources. func (r *reader) Close() error { err := r.file.Close() r.session.Close() return err } // charmDoc represents the document stored in MongoDB for a charm. type charmDoc struct { URLs []*charm.URL Revision int Digest string Sha256 string Size int64 FileId bson.ObjectId Meta *charm.Meta Config *charm.Config } // LockUpdates acquires a server-side lock for updating a single charm // that is supposed to be made available in all of the provided urls. // If the lock can't be acquired in any of the urls, an error will be // immediately returned. // In the usual case, any locking done is undone when an error happens, // or when l.Unlock is called. If something else goes wrong, the locks // will also expire after the period defined in UpdateTimeout. func (s *Store) LockUpdates(urls []*charm.URL) (l *UpdateLock, err error) { session := s.session.Copy() keys := make([]string, len(urls)) for i := range urls { keys[i] = urls[i].String() } sort.Strings(keys) l = &UpdateLock{keys, session.Locks(), bson.Now()} if err = l.tryLock(); err != nil { session.Close() return nil, err } return l, nil } // UpdateLock represents an acquired update lock over a set of charm URLs. type UpdateLock struct { keys []string locks *mgo.Collection time time.Time } // Unlock removes the previously acquired server-side lock that prevents // other processes from attempting to update a set of charm URLs. func (l *UpdateLock) Unlock() { log.Debugf("store: Unlocking charms for future updates: %v", l.keys) defer l.locks.Database.Session.Close() for i := len(l.keys) - 1; i >= 0; i-- { // Using time below ensures only the proper lock is removed. // Can't do much about errors here. Locks will expire anyway. l.locks.Remove(bson.D{{"_id", l.keys[i]}, {"time", l.time}}) } } // tryLock tries locking l.keys, one at a time, and succeeds only if it // can lock all of them in order. The keys should be pre-sorted so that // two-way conflicts can't happen. If any of the keys fail to be locked, // and expiring the old lock doesn't work, tryLock undoes all previous // locks and aborts with an error. func (l *UpdateLock) tryLock() error { for i, key := range l.keys { log.Debugf("store: Trying to lock charm %s for updates...", key) doc := bson.D{{"_id", key}, {"time", l.time}} err := l.locks.Insert(doc) if err == nil { log.Debugf("store: Charm %s is now locked for updates.", key) continue } if lerr, ok := err.(*mgo.LastError); ok && lerr.Code == 11000 { log.Debugf("store: Charm %s is locked. Trying to expire lock.", key) l.tryExpire(key) err = l.locks.Insert(doc) if err == nil { log.Debugf("store: Charm %s is now locked for updates.", key) continue } } // Couldn't lock everyone. Undo previous locks. for j := i - 1; j >= 0; j-- { // Using time below should be unnecessary, but it's an extra check. // Can't do anything about errors here. Lock will expire anyway. l.locks.Remove(bson.D{{"_id", l.keys[j]}, {"time", l.time}}) } err = maybeConflict(err) log.Errorf("store: Can't lock charms %v for updating: %v", l.keys, err) return err } return nil } // tryExpire attempts to remove outdated locks from the database. func (l *UpdateLock) tryExpire(key string) { // Ignore errors. If nothing happens the key will continue locked. l.locks.Remove(bson.D{{"_id", key}, {"time", bson.D{{"$lt", bson.Now().Add(-UpdateTimeout)}}}}) } // maybeConflict returns an ErrUpdateConflict if err is a mgo // insert conflict LastError, or err itself otherwise. func maybeConflict(err error) error { if lerr, ok := err.(*mgo.LastError); ok && lerr.Code == 11000 { return ErrUpdateConflict } return err } // storeSession wraps a mgo.Session ands adds a few convenience methods. type storeSession struct { *mgo.Session } // Copy copies the storeSession and its underlying mgo session. func (s *storeSession) Copy() *storeSession { return &storeSession{s.Session.Copy()} } // Charms returns the mongo collection where charms are stored. func (s *storeSession) Charms() *mgo.Collection { return s.DB("juju").C("charms") } // Charms returns a mgo.GridFS to read and write charms. func (s *storeSession) CharmFS() *mgo.GridFS { return s.DB("juju").GridFS("charmfs") } // Events returns the mongo collection where charm events are stored. func (s *storeSession) Events() *mgo.Collection { return s.DB("juju").C("events") } // Locks returns the mongo collection where charm locks are stored. func (s *storeSession) Locks() *mgo.Collection { return s.DB("juju").C("locks") } // StatTokens returns the mongo collection for storing key tokens // for statistics collection. func (s *storeSession) StatTokens() *mgo.Collection { return s.DB("juju").C("stat.tokens") } // StatCounters returns the mongo collection for counter values. func (s *storeSession) StatCounters() *mgo.Collection { return s.DB("juju").C("stat.counters") } type CharmEventKind int const ( EventPublished CharmEventKind = iota + 1 EventPublishError EventKindCount ) func (k CharmEventKind) String() string { switch k { case EventPublished: return "published" case EventPublishError: return "publish-error" } panic(fmt.Errorf("unknown charm event kind %d", k)) } // CharmEvent is a record for an event relating to one or more charm URLs. type CharmEvent struct { Kind CharmEventKind Digest string Revision int URLs []*charm.URL Errors []string `bson:",omitempty"` Warnings []string `bson:",omitempty"` Time time.Time } // LogCharmEvent records an event related to one or more charm URLs. func (s *Store) LogCharmEvent(event *CharmEvent) (err error) { log.Infof("store: Adding charm event for %v with key %q: %s", event.URLs, event.Digest, event.Kind) if err = mustLackRevision("LogCharmEvent", event.URLs...); err != nil { return } session := s.session.Copy() defer session.Close() if event.Kind == 0 || event.Digest == "" || len(event.URLs) == 0 { return fmt.Errorf("LogCharmEvent: need valid Kind, Digest and URLs") } if event.Time.IsZero() { event.Time = time.Now() } events := session.Events() return events.Insert(event) } // CharmEvent returns the most recent event associated with url // and digest. If the specified event isn't found the error // ErrUnknownChange will be returned. If digest is empty, any // digest will match. func (s *Store) CharmEvent(url *charm.URL, digest string) (*CharmEvent, error) { // TODO: It'd actually make sense to find the charm event after the // revision id, but since we don't care about that now, just make sure // we don't write bad code. if err := mustLackRevision("CharmEvent", url); err != nil { return nil, err } session := s.session.Copy() defer session.Close() events := session.Events() event := &CharmEvent{Digest: digest} var query *mgo.Query if digest == "" { query = events.Find(bson.D{{"urls", url}}) } else { query = events.Find(bson.D{{"urls", url}, {"digest", digest}}) } err := query.Sort("-time").One(&event) if err == mgo.ErrNotFound { return nil, ErrNotFound } if err != nil { return nil, err } return event, nil } // mustLackRevision returns an error if any of the urls has a revision. func mustLackRevision(context string, urls ...*charm.URL) error { for _, url := range urls { if url.Revision != -1 { err := fmt.Errorf("%s: got charm URL with revision: %s", context, url) log.Errorf("store: %v", err) return err } } return nil } ����������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/�������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�021750� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/format-1.16_whitebox_test.go���������������������0000644�0000153�0000161�00000014522�12321735776�027151� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The format tests are white box tests, meaning that the tests are in the // same package as the code, as all the format details are internal to the // package. package agent import ( "io/ioutil" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) type format_1_16Suite struct { testbase.LoggingSuite } var _ = gc.Suite(&format_1_16Suite{}) func (s *format_1_16Suite) TestMissingAttributes(c *gc.C) { dataDir := c.MkDir() formatPath := filepath.Join(dataDir, legacyFormatFilename) err := utils.AtomicWriteFile(formatPath, []byte(legacyFormatFileContents), 0600) c.Assert(err, gc.IsNil) configPath := filepath.Join(dataDir, agentConfigFilename) err = utils.AtomicWriteFile(configPath, []byte(configDataWithoutNewAttributes), 0600) c.Assert(err, gc.IsNil) readConfig, err := ReadConf(configPath) c.Assert(err, gc.IsNil) c.Assert(readConfig.UpgradedToVersion(), gc.Equals, version.MustParse("1.16.0")) c.Assert(readConfig.LogDir(), gc.Equals, "/var/log/juju") c.Assert(readConfig.DataDir(), gc.Equals, "/var/lib/juju") } func (*format_1_16Suite) TestReadConfReadsLegacyFormatAndWritesNew(c *gc.C) { dataDir := c.MkDir() formatPath := filepath.Join(dataDir, legacyFormatFilename) err := utils.AtomicWriteFile(formatPath, []byte(legacyFormatFileContents), 0600) c.Assert(err, gc.IsNil) configPath := filepath.Join(dataDir, agentConfigFilename) err = utils.AtomicWriteFile(configPath, []byte(agentConfig1_16Contents), 0600) c.Assert(err, gc.IsNil) config, err := ReadConf(configPath) c.Assert(err, gc.IsNil) c.Assert(config, gc.NotNil) // Test we wrote a currently valid config. config, err = ReadConf(configPath) c.Assert(err, gc.IsNil) c.Assert(config, gc.NotNil) c.Assert(config.UpgradedToVersion(), jc.DeepEquals, version.MustParse("1.16.0")) c.Assert(config.Jobs(), gc.HasLen, 0) // Old format was deleted. assertFileNotExist(c, formatPath) // And new contents were written. data, err := ioutil.ReadFile(configPath) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Not(gc.Equals), agentConfig1_16Contents) } const legacyFormatFileContents = "format 1.16" var agentConfig1_16Contents = ` tag: machine-0 nonce: user-admin:bootstrap cacert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNXekNDQWNhZ0F3SUJBZ0lCQURBTEJna3Foa2lHOXcwQkFRVXdRekVOTUFzR0ExVUVDaE1FYW5WcWRURXkKTURBR0ExVUVBd3dwYW5WcWRTMW5aVzVsY21GMFpXUWdRMEVnWm05eUlHVnVkbWx5YjI1dFpXNTBJQ0pzYjJOaApiQ0l3SGhjTk1UUXdNekExTVRJeE9ERTJXaGNOTWpRd016QTFNVEl5TXpFMldqQkRNUTB3Q3dZRFZRUUtFd1JxCmRXcDFNVEl3TUFZRFZRUUREQ2xxZFdwMUxXZGxibVZ5WVhSbFpDQkRRU0JtYjNJZ1pXNTJhWEp2Ym0xbGJuUWcKSW14dlkyRnNJakNCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBd3NaVUg3NUZGSW1QUWVGSgpaVnVYcmlUWmNYdlNQMnk0VDJaSU5WNlVrY2E5VFdXb01XaWlPYm4yNk03MjNGQllPczh3WHRYNEUxZ2l1amxYCmZGeHNFckloczEyVXQ1S3JOVkkyMlEydCtVOGViakZMUHJiUE5Fb3pzdnU3UzFjZklFbjBXTVg4MWRBaENOMnQKVkxGaC9hS3NqSHdDLzJ5Y3Z0VSttTngyVG5FQ0F3RUFBYU5qTUdFd0RnWURWUjBQQVFIL0JBUURBZ0NrTUE4RwpBMVVkRXdFQi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZKVUxKZVlIbERsdlJ3T0owcWdyemcwclZGZUVNQjhHCkExVWRJd1FZTUJhQUZKVUxKZVlIbERsdlJ3T0owcWdyemcwclZGZUVNQXNHQ1NxR1NJYjNEUUVCQlFPQmdRQ2UKRlRZbThsWkVYZUp1cEdPc3pwc2pjaHNSMEFxeXROZ1dxQmE1cWUyMS9xS2R3TUFSQkNFMTU3eUxGVnl6MVoycQp2YVhVNy9VKzdKbGNiWmtHRHJ5djE2S2UwK2RIY3NEdG5jR2FOVkZKMTAxYnNJNG1sVEkzQWpQNDErNG5mQ0VlCmhwalRvYm1YdlBhOFN1NGhQYTBFc1E4bXFaZGFabmdwRU0vb1JiZ0RMdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K stateaddresses: - localhost:37017 statepassword: OlUMkte5J3Ss0CH9yxedilIC apiaddresses: - localhost:17070 apipassword: OlUMkte5J3Ss0CH9yxedilIC oldpassword: oBlMbFUGvCb2PMFgYVzjS6GD values: PROVIDER_TYPE: local SHARED_STORAGE_ADDR: 10.0.3.1:8041 SHARED_STORAGE_DIR: /home/user/.juju/local/shared-storage STORAGE_ADDR: 10.0.3.1:8040 STORAGE_DIR: /home/user/.juju/local/storage stateservercert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNJakNDQVkyZ0F3SUJBZ0lCQURBTEJna3Foa2lHOXcwQkFRVXdRekVOTUFzR0ExVUVDaE1FYW5WcWRURXkKTURBR0ExVUVBd3dwYW5WcWRTMW5aVzVsY21GMFpXUWdRMEVnWm05eUlHVnVkbWx5YjI1dFpXNTBJQ0pzYjJOaApiQ0l3SGhjTk1UUXdNekExTVRJeE9ESXlXaGNOTWpRd016QTFNVEl5TXpJeVdqQWJNUTB3Q3dZRFZRUUtFd1JxCmRXcDFNUW93Q0FZRFZRUURFd0VxTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FDdVA0dTAKQjZtbGs0V0g3SHFvOXhkSFp4TWtCUVRqV2VLTkhERzFMb21SWmc2RHA4Z0VQK0ZNVm5IaUprZW1pQnJNSEk3OAo5bG4zSVRBT0NJT0xna0NkN3ZsaDJub2FheTlSeXpUaG9PZ0RMSzVpR0VidmZDeEFWZThhWDQvbThhOGNLWE9TCmJJZTZFNnVtb0wza0JNaEdiL1QrYW1xbHRjaHVNRXJhanJSVit3SURBUUFCbzFJd1VEQU9CZ05WSFE4QkFmOEUKQkFNQ0FCQXdIUVlEVlIwT0JCWUVGRTV1RFg3UlRjckF2ajFNcWpiU2w1M21pR0NITUI4R0ExVWRJd1FZTUJhQQpGSlVMSmVZSGxEbHZSd09KMHFncnpnMHJWRmVFTUFzR0NTcUdTSWIzRFFFQkJRT0JnUUJUNC8vZkpESUcxM2dxClBiamNnUTN6eHh6TG12STY5Ty8zMFFDbmIrUGZObDRET0U1SktwVE5OTjhkOEJEQWZPYStvWE5neEM3VTZXdjUKZjBYNzEyRnlNdUc3VXJEVkNDY0kxS3JSQ0F0THlPWUREL0ZPblBwSWdVQjF1bFRnOGlRUzdlTjM2d0NEL21wVApsUVVUS2FuU00yMnhnWWJKazlRY1dBSzQ0ZjA4SEE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== stateserverkey: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlDV3dJQkFBS0JnUUN1UDR1MEI2bWxrNFdIN0hxbzl4ZEhaeE1rQlFUaldlS05IREcxTG9tUlpnNkRwOGdFClArRk1WbkhpSmtlbWlCck1ISTc4OWxuM0lUQU9DSU9MZ2tDZDd2bGgybm9hYXk5Unl6VGhvT2dETEs1aUdFYnYKZkN4QVZlOGFYNC9tOGE4Y0tYT1NiSWU2RTZ1bW9MM2tCTWhHYi9UK2FtcWx0Y2h1TUVyYWpyUlYrd0lEQVFBQgpBb0dBRERJZ2FoSmJPbDZQNndxUEwwSlVHOGhJRzY1S1FFdHJRdXNsUTRRbFZzcm8yeWdrSkwvLzJlTDNCNWdjClRiaWEvNHhFS2Nwb1U1YThFVTloUGFONU9EYnlkVEsxQ1I3R2JXSGkwWm1LbGZCUlR4bUpxakdKVU1CSmI4a0QKNStpMzlvcXdQS3dnaXoyTVR5SHZKZFFJVHB0ZDVrbEQyYjU1by9YWFRCTnk2NGtDUVFEbXRFWHNTL2kxTm5pSwozZVJkeHM4UVFGN0pKVG5SR042ZUh6ZHlXb242Zjl2ZkxrSDROWUdxcFUydjVBNUl1Nno3K3NJdXVHU2ZSeEI1CktrZVFXdlVQQWtFQXdWcVdlczdmc3NLbUFCZGxER3ozYzNxMjI2eVVaUE00R3lTb1cxYXZsYzJ1VDVYRm9vVUsKNjRpUjJuU2I1OHZ2bGY1RjRRMnJuRjh2cFRLcFJwK0lWUUpBTlcwZ0dFWEx0ZU9FYk54UUMydUQva1o1N09rRApCNnBUdTVpTkZaMWtBSy9sY2p6YktDanorMW5Hc09vR2FNK1ZrdEVTY1JGZ3RBWVlDWWRDQldzYS93SkFROWJXCnlVdmdMTVlpbkJHWlFKelN6VStXN01oR1lJejllSGlLSVZIdTFTNlBKQmsyZUdrWmhiNHEvbXkvYnJxYzJ4R1YKenZxTzVaUjRFUXdQWEZvSTZRSkFkeVdDMllOTTF2a1BuWnJqZzNQQXFHRHJQMHJwNEZ0bFV4alh0ay8vcW9hNgpRcXVYcE9vNjd4THRieW1PTlJTdDFiZGE5ZE5tbGljMFVNZ0JQRHgrYnc9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo= apiport: 17070 `[1:] const configDataWithoutNewAttributes = ` tag: omg nonce: a nonce cacert: Y2EgY2VydA== stateaddresses: - localhost:1234 apiaddresses: - localhost:1235 oldpassword: sekrit values: {} ` ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/format-1.16.go�����������������������������������0000644�0000153�0000161�00000005656�12321735776�024211� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent import ( "encoding/base64" "launchpad.net/goyaml" "launchpad.net/juju-core/version" ) var format_1_16 = formatter_1_16{} // formatter_1_16 is the formatter for the 1.16 format. type formatter_1_16 struct { } // Ensure that the formatter_1_16 struct implements the formatter interface. var _ formatter = formatter_1_16{} // format_1_16Serialization holds information for a given agent. type format_1_16Serialization struct { Tag string Nonce string UpgradedToVersion *version.Number `yaml:"upgradedToVersion"` CACert string StateAddresses []string `yaml:",omitempty"` StatePassword string `yaml:",omitempty"` APIAddresses []string `yaml:",omitempty"` APIPassword string `yaml:",omitempty"` OldPassword string Values map[string]string // Only state server machines have these next three items StateServerCert string `yaml:",omitempty"` StateServerKey string `yaml:",omitempty"` APIPort int `yaml:",omitempty"` } func init() { registerFormat(format_1_16) } const legacyFormatFilename = "format" // legacyFormatPrefix is the prefix of the legacy format file. const legacyFormatPrefix = "format " // decode64 makes sure that for an empty string we have a nil slice, not an // empty slice, which is what the base64 DecodeString function returns. func decode64(value string) (result []byte, err error) { if value != "" { result, err = base64.StdEncoding.DecodeString(value) } return } func (formatter_1_16) version() string { return "1.16" } func (formatter_1_16) unmarshal(data []byte) (*configInternal, error) { var format format_1_16Serialization if err := goyaml.Unmarshal(data, &format); err != nil { return nil, err } caCert, err := decode64(format.CACert) if err != nil { return nil, err } stateServerCert, err := decode64(format.StateServerCert) if err != nil { return nil, err } stateServerKey, err := decode64(format.StateServerKey) if err != nil { return nil, err } if format.UpgradedToVersion == nil { // Assume it's 1.16.0. upgradedToVersion := version.MustParse("1.16.0") format.UpgradedToVersion = &upgradedToVersion } config := &configInternal{ tag: format.Tag, nonce: format.Nonce, dataDir: DefaultDataDir, logDir: DefaultLogDir, upgradedToVersion: *format.UpgradedToVersion, caCert: caCert, oldPassword: format.OldPassword, stateServerCert: stateServerCert, stateServerKey: stateServerKey, apiPort: format.APIPort, values: format.Values, } if len(format.StateAddresses) > 0 { config.stateDetails = &connectionDetails{ format.StateAddresses, format.StatePassword, } } if len(format.APIAddresses) > 0 { config.apiDetails = &connectionDetails{ format.APIAddresses, format.APIPassword, } } return config, nil } ����������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/export_test.go�����������������������������������0000644�0000153�0000161�00000002425�12321735776�024705� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent import ( "fmt" "os" "launchpad.net/juju-core/state/api/params" ) func Password(config Config) string { c := config.(*configInternal) if c.stateDetails == nil { return c.apiDetails.password } else { return c.stateDetails.password } return "" } func WriteNewPassword(cfg Config) (string, error) { return cfg.(*configInternal).writeNewPassword() } func PatchConfig(config Config, fieldName string, value interface{}) error { conf := config.(*configInternal) switch fieldName { case "DataDir": conf.dataDir = value.(string) case "LogDir": conf.logDir = value.(string) case "Jobs": conf.jobs = value.([]params.MachineJob)[:] case "DeleteValues": for _, key := range value.([]string) { delete(conf.values, key) } case "Values": for key, val := range value.(map[string]string) { if conf.values == nil { conf.values = make(map[string]string) } conf.values[key] = val } default: return fmt.Errorf("unknown field %q", fieldName) } conf.configFilePath = ConfigPath(conf.dataDir, conf.tag) return nil } func ConfigFileExists(config Config) bool { conf := config.(*configInternal) _, err := os.Lstat(conf.configFilePath) return err == nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/bootstrap_test.go��������������������������������0000644�0000153�0000161�00000012742�12321735776�025404� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) type bootstrapSuite struct { testbase.LoggingSuite testing.MgoSuite } var _ = gc.Suite(&bootstrapSuite{}) func (s *bootstrapSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *bootstrapSuite) TearDownSuite(c *gc.C) { s.MgoSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *bootstrapSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) } func (s *bootstrapSuite) TearDownTest(c *gc.C) { s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *bootstrapSuite) TestInitializeState(c *gc.C) { dataDir := c.MkDir() pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt) cfg, err := agent.NewAgentConfig(agent.AgentConfigParams{ DataDir: dataDir, Tag: "machine-0", UpgradedToVersion: version.Current.Number, StateAddresses: []string{testing.MgoServer.Addr()}, CACert: []byte(testing.CACert), Password: pwHash, }) c.Assert(err, gc.IsNil) expectConstraints := constraints.MustParse("mem=1024M") expectHW := instance.MustParseHardware("mem=2048M") mcfg := agent.BootstrapMachineConfig{ Constraints: expectConstraints, Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: "i-bootstrap", Characteristics: expectHW, } envAttrs := testing.FakeConfig().Delete("admin-secret").Merge(testing.Attrs{ "agent-version": version.Current.Number.String(), }) envCfg, err := config.New(config.NoDefaults, envAttrs) c.Assert(err, gc.IsNil) st, m, err := cfg.InitializeState(envCfg, mcfg, state.DialOpts{}, environs.NewStatePolicy()) c.Assert(err, gc.IsNil) defer st.Close() // Check that initial admin user has been set up correctly. s.assertCanLogInAsAdmin(c, pwHash) user, err := st.User("admin") c.Assert(err, gc.IsNil) c.Assert(user.PasswordValid(testing.DefaultMongoPassword), jc.IsTrue) // Check that environment configuration has been added. newEnvCfg, err := st.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(newEnvCfg.AllAttrs(), gc.DeepEquals, envCfg.AllAttrs()) // Check that the bootstrap machine looks correct. c.Assert(m.Id(), gc.Equals, "0") c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobHostUnits}) c.Assert(m.Series(), gc.Equals, version.Current.Series) c.Assert(m.CheckProvisioned(state.BootstrapNonce), jc.IsTrue) gotConstraints, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(gotConstraints, gc.DeepEquals, expectConstraints) c.Assert(err, gc.IsNil) gotHW, err := m.HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Assert(*gotHW, gc.DeepEquals, expectHW) // Check that the machine agent's config has been written // and that we can use it to connect to the state. newCfg, err := agent.ReadConf(agent.ConfigPath(dataDir, "machine-0")) c.Assert(err, gc.IsNil) c.Assert(newCfg.Tag(), gc.Equals, "machine-0") c.Assert(agent.Password(newCfg), gc.Not(gc.Equals), pwHash) c.Assert(agent.Password(newCfg), gc.Not(gc.Equals), testing.DefaultMongoPassword) st1, err := cfg.OpenState(environs.NewStatePolicy()) c.Assert(err, gc.IsNil) defer st1.Close() } func (s *bootstrapSuite) TestInitializeStateFailsSecondTime(c *gc.C) { dataDir := c.MkDir() pwHash := utils.UserPasswordHash(testing.DefaultMongoPassword, utils.CompatSalt) cfg, err := agent.NewAgentConfig(agent.AgentConfigParams{ DataDir: dataDir, Tag: "machine-0", UpgradedToVersion: version.Current.Number, StateAddresses: []string{testing.MgoServer.Addr()}, CACert: []byte(testing.CACert), Password: pwHash, }) c.Assert(err, gc.IsNil) expectConstraints := constraints.MustParse("mem=1024M") expectHW := instance.MustParseHardware("mem=2048M") mcfg := agent.BootstrapMachineConfig{ Constraints: expectConstraints, Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: "i-bootstrap", Characteristics: expectHW, } envAttrs := testing.FakeConfig().Delete("admin-secret").Merge(testing.Attrs{ "agent-version": version.Current.Number.String(), }) envCfg, err := config.New(config.NoDefaults, envAttrs) c.Assert(err, gc.IsNil) st, _, err := cfg.InitializeState(envCfg, mcfg, state.DialOpts{}, environs.NewStatePolicy()) c.Assert(err, gc.IsNil) err = st.SetAdminMongoPassword("") c.Check(err, gc.IsNil) st.Close() st, _, err = cfg.InitializeState(envCfg, mcfg, state.DialOpts{}, environs.NewStatePolicy()) if err == nil { st.Close() } c.Assert(err, gc.ErrorMatches, "cannot initialize bootstrap user: user already exists") } func (*bootstrapSuite) assertCanLogInAsAdmin(c *gc.C, password string) { info := &state.Info{ Addrs: []string{testing.MgoServer.Addr()}, CACert: []byte(testing.CACert), Tag: "", Password: password, } st, err := state.Open(info, state.DialOpts{}, environs.NewStatePolicy()) c.Assert(err, gc.IsNil) defer st.Close() _, err = st.Machine("0") c.Assert(err, gc.IsNil) } ������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/bootstrap.go�������������������������������������0000644�0000153�0000161�00000013357�12321735776�024350� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent import ( "fmt" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/version" ) // InitializeState should be called on the bootstrap machine's agent // configuration. It uses that information to dial the state server and // initialize it. It also generates a new password for the bootstrap // machine and calls Write to save the the configuration. // // The envCfg values will be stored in the state's EnvironConfig; the // machineCfg values will be used to configure the bootstrap Machine, // and its constraints will be also be used for the environment-level // constraints. The connection to the state server will respect the // given timeout parameter. // // InitializeState returns the newly initialized state and bootstrap // machine. If it fails, the state may well be irredeemably compromised. type StateInitializer interface { InitializeState(envCfg *config.Config, machineCfg BootstrapMachineConfig, timeout state.DialOpts, policy state.Policy) (*state.State, *state.Machine, error) } // BootstrapMachineConfig holds configuration information // to attach to the bootstrap machine. type BootstrapMachineConfig struct { // Constraints holds the bootstrap machine's constraints. // This value is also used for the environment-level constraints. Constraints constraints.Value // Jobs holds the jobs that the machine agent will run. Jobs []params.MachineJob // InstanceId holds the instance id of the bootstrap machine. InstanceId instance.Id // Characteristics holds hardware information on the // bootstrap machine. Characteristics instance.HardwareCharacteristics } const bootstrapMachineId = "0" func (c *configInternal) InitializeState(envCfg *config.Config, machineCfg BootstrapMachineConfig, timeout state.DialOpts, policy state.Policy) (*state.State, *state.Machine, error) { if c.Tag() != names.MachineTag(bootstrapMachineId) { return nil, nil, fmt.Errorf("InitializeState not called with bootstrap machine's configuration") } info := state.Info{ Addrs: c.stateDetails.addresses, CACert: c.caCert, } logger.Debugf("initializing address %v", info.Addrs) st, err := state.Initialize(&info, envCfg, timeout, policy) if err != nil { return nil, nil, fmt.Errorf("failed to initialize state: %v", err) } logger.Debugf("connected to initial state") m, err := c.initUsersAndBootstrapMachine(st, machineCfg) if err != nil { st.Close() return nil, nil, err } return st, m, nil } func (c *configInternal) initUsersAndBootstrapMachine(st *state.State, cfg BootstrapMachineConfig) (*state.Machine, error) { if err := initBootstrapUser(st, c.oldPassword); err != nil { return nil, fmt.Errorf("cannot initialize bootstrap user: %v", err) } if err := st.SetEnvironConstraints(cfg.Constraints); err != nil { return nil, fmt.Errorf("cannot set initial environ constraints: %v", err) } m, err := c.initBootstrapMachine(st, cfg) if err != nil { return nil, fmt.Errorf("cannot initialize bootstrap machine: %v", err) } return m, nil } // initBootstrapUser creates the initial admin user for the database, and sets // the initial password. func initBootstrapUser(st *state.State, passwordHash string) error { logger.Debugf("adding admin user") // Set up initial authentication. u, err := st.AddUser("admin", "") if err != nil { return err } // Note that at bootstrap time, the password is set to // the hash of its actual value. The first time a client // connects to mongo, it changes the mongo password // to the original password. logger.Debugf("setting password hash for admin user") // TODO(jam): http://pad.lv/1248839 // We could teach bootstrap how to generate a custom salt and apply // that to the hash that was generated. At which point we'd need to set // it here. For now, we pass "" so that on first login we will create a // new salt, but the fixed-salt password is still available from // cloud-init. if err := u.SetPasswordHash(passwordHash, ""); err != nil { return err } if err := st.SetAdminMongoPassword(passwordHash); err != nil { return err } return nil } // initBootstrapMachine initializes the initial bootstrap machine in state. func (c *configInternal) initBootstrapMachine(st *state.State, cfg BootstrapMachineConfig) (*state.Machine, error) { logger.Infof("initialising bootstrap machine with config: %+v", cfg) jobs := make([]state.MachineJob, len(cfg.Jobs)) for i, job := range cfg.Jobs { machineJob, err := state.MachineJobFromParams(job) if err != nil { return nil, fmt.Errorf("invalid bootstrap machine job %q: %v", job, err) } jobs[i] = machineJob } m, err := st.AddOneMachine(state.MachineTemplate{ Series: version.Current.Series, Nonce: state.BootstrapNonce, Constraints: cfg.Constraints, InstanceId: cfg.InstanceId, HardwareCharacteristics: cfg.Characteristics, Jobs: jobs, }) if err != nil { return nil, fmt.Errorf("cannot create bootstrap machine in state: %v", err) } if m.Id() != bootstrapMachineId { return nil, fmt.Errorf("bootstrap machine expected id 0, got %q", m.Id()) } // Read the machine agent's password and change it to // a new password (other agents will change their password // via the API connection). logger.Debugf("create new random password for machine %v", m.Id()) newPassword, err := c.writeNewPassword() if err != nil { return nil, err } if err := m.SetMongoPassword(newPassword); err != nil { return nil, err } if err := m.SetPassword(newPassword); err != nil { return nil, err } return m, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/format-1.18.go�����������������������������������0000644�0000153�0000161�00000007022�12321735776�024200� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent import ( "launchpad.net/goyaml" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/version" ) var format_1_18 = formatter_1_18{} // formatter_1_18 is the formatter for the 1.18 format. type formatter_1_18 struct { } // Ensure that the formatter_1_18 struct implements the formatter interface. var _ formatter = formatter_1_18{} // format_1_18Serialization holds information for a given agent. type format_1_18Serialization struct { Tag string DataDir string LogDir string Nonce string Jobs []params.MachineJob `yaml:",omitempty"` UpgradedToVersion *version.Number `yaml:"upgradedToVersion"` CACert string StateAddresses []string `yaml:",omitempty"` StatePassword string `yaml:",omitempty"` APIAddresses []string `yaml:",omitempty"` APIPassword string `yaml:",omitempty"` OldPassword string Values map[string]string // Only state server machines have these next three items StateServerCert string `yaml:",omitempty"` StateServerKey string `yaml:",omitempty"` APIPort int `yaml:",omitempty"` } func init() { registerFormat(format_1_18) } func (formatter_1_18) version() string { return "1.18" } func (formatter_1_18) unmarshal(data []byte) (*configInternal, error) { var format format_1_18Serialization if err := goyaml.Unmarshal(data, &format); err != nil { return nil, err } if format.UpgradedToVersion == nil || *format.UpgradedToVersion == version.Zero { // Assume we upgrade from 1.16. upgradedToVersion := version.MustParse("1.16.0") format.UpgradedToVersion = &upgradedToVersion } config := &configInternal{ tag: format.Tag, dataDir: format.DataDir, logDir: format.LogDir, jobs: format.Jobs, upgradedToVersion: *format.UpgradedToVersion, nonce: format.Nonce, caCert: []byte(format.CACert), oldPassword: format.OldPassword, stateServerCert: []byte(format.StateServerCert), stateServerKey: []byte(format.StateServerKey), apiPort: format.APIPort, values: format.Values, } if config.logDir == "" { config.logDir = DefaultLogDir } if config.dataDir == "" { config.dataDir = DefaultDataDir } if len(format.StateAddresses) > 0 { config.stateDetails = &connectionDetails{ format.StateAddresses, format.StatePassword, } } if len(format.APIAddresses) > 0 { config.apiDetails = &connectionDetails{ format.APIAddresses, format.APIPassword, } } return config, nil } func (formatter_1_18) marshal(config *configInternal) ([]byte, error) { format := &format_1_18Serialization{ Tag: config.tag, DataDir: config.dataDir, LogDir: config.logDir, Jobs: config.jobs, UpgradedToVersion: &config.upgradedToVersion, Nonce: config.nonce, CACert: string(config.caCert), OldPassword: config.oldPassword, StateServerCert: string(config.stateServerCert), StateServerKey: string(config.stateServerKey), APIPort: config.apiPort, Values: config.values, } if config.stateDetails != nil { format.StateAddresses = config.stateDetails.addresses format.StatePassword = config.stateDetails.password } if config.apiDetails != nil { format.APIAddresses = config.apiDetails.addresses format.APIPassword = config.apiDetails.password } return goyaml.Marshal(format) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/package_test.go����������������������������������0000644�0000153�0000161�00000000362�12321735642�024745� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func Test(t *stdtesting.T) { testing.MgoTestPackage(t) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/format_whitebox_test.go��������������������������0000644�0000153�0000161�00000006235�12321735776�026570� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent import ( "os" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) type formatSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&formatSuite{}) // The agentParams are used by the specific formatter whitebox tests, and is // located here for easy reuse. var agentParams = AgentConfigParams{ Tag: "omg", UpgradedToVersion: version.Current.Number, Jobs: []params.MachineJob{params.JobHostUnits}, Password: "sekrit", CACert: []byte("ca cert"), StateAddresses: []string{"localhost:1234"}, APIAddresses: []string{"localhost:1235"}, Nonce: "a nonce", } func newTestConfig(c *gc.C) *configInternal { params := agentParams params.DataDir = c.MkDir() params.LogDir = c.MkDir() config, err := NewAgentConfig(params) c.Assert(err, gc.IsNil) return config.(*configInternal) } func (*formatSuite) TestWriteCommands(c *gc.C) { config := newTestConfig(c) commands, err := config.WriteCommands() c.Assert(err, gc.IsNil) c.Assert(commands, gc.HasLen, 3) c.Assert(commands[0], gc.Matches, `mkdir -p '\S+/agents/omg'`) c.Assert(commands[1], gc.Matches, `install -m 600 /dev/null '\S+/agents/omg/agent.conf'`) c.Assert(commands[2], gc.Matches, `printf '%s\\n' '(.|\n)*' > '\S+/agents/omg/agent.conf'`) } func (*formatSuite) TestWriteAgentConfig(c *gc.C) { config := newTestConfig(c) err := config.Write() c.Assert(err, gc.IsNil) configPath := ConfigPath(config.DataDir(), config.Tag()) formatPath := filepath.Join(config.Dir(), legacyFormatFilename) assertFileExists(c, configPath) assertFileNotExist(c, formatPath) } func (*formatSuite) TestRead(c *gc.C) { config := newTestConfig(c) assertWriteAndRead(c, config) } func (*formatSuite) TestReadWriteStateConfig(c *gc.C) { stateParams := StateMachineConfigParams{ AgentConfigParams: agentParams, StateServerCert: []byte("some special cert"), StateServerKey: []byte("a special key"), StatePort: 12345, APIPort: 23456, } stateParams.DataDir = c.MkDir() stateParams.Values = map[string]string{"foo": "bar", "wibble": "wobble"} configInterface, err := NewStateMachineConfig(stateParams) c.Assert(err, gc.IsNil) config, ok := configInterface.(*configInternal) c.Assert(ok, jc.IsTrue) assertWriteAndRead(c, config) } func assertWriteAndRead(c *gc.C, config *configInternal) { err := config.Write() c.Assert(err, gc.IsNil) configPath := ConfigPath(config.DataDir(), config.Tag()) readConfig, err := ReadConf(configPath) c.Assert(err, gc.IsNil) c.Assert(readConfig, jc.DeepEquals, config) } func assertFileExists(c *gc.C, path string) { fileInfo, err := os.Stat(path) c.Assert(err, gc.IsNil) c.Assert(fileInfo.Mode().IsRegular(), jc.IsTrue) c.Assert(fileInfo.Mode().Perm(), gc.Equals, os.FileMode(0600)) c.Assert(fileInfo.Size(), jc.GreaterThan, 0) } func assertFileNotExist(c *gc.C, path string) { _, err := os.Stat(path) c.Assert(err, jc.Satisfies, os.IsNotExist) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/format-1.18_whitebox_test.go���������������������0000644�0000153�0000161�00000012070�12321735776�027147� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The format tests are white box tests, meaning that the tests are in the // same package as the code, as all the format details are internal to the // package. package agent import ( "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) type format_1_18Suite struct { testbase.LoggingSuite } var _ = gc.Suite(&format_1_18Suite{}) var configData1_18WithoutUpgradedToVersion = "# format 1.18\n" + configDataWithoutNewAttributes func (s *format_1_18Suite) TestMissingAttributes(c *gc.C) { dataDir := c.MkDir() configPath := filepath.Join(dataDir, agentConfigFilename) err := utils.AtomicWriteFile(configPath, []byte(configData1_18WithoutUpgradedToVersion), 0600) c.Assert(err, gc.IsNil) readConfig, err := ReadConf(configPath) c.Assert(err, gc.IsNil) c.Assert(readConfig.UpgradedToVersion(), gc.Equals, version.MustParse("1.16.0")) c.Assert(readConfig.LogDir(), gc.Equals, "/var/log/juju") c.Assert(readConfig.DataDir(), gc.Equals, "/var/lib/juju") } func (*format_1_18Suite) TestReadConfWithExisting1_18ConfigFileContents(c *gc.C) { dataDir := c.MkDir() configPath := filepath.Join(dataDir, agentConfigFilename) err := utils.AtomicWriteFile(configPath, []byte(agentConfig1_18Contents), 0600) c.Assert(err, gc.IsNil) config, err := ReadConf(configPath) c.Assert(err, gc.IsNil) c.Assert(config.UpgradedToVersion(), jc.DeepEquals, version.MustParse("1.17.5.1")) c.Assert(config.Jobs(), jc.DeepEquals, []params.MachineJob{params.JobManageEnviron}) } var agentConfig1_18Contents = ` # format 1.18 tag: machine-0 datadir: /home/user/.juju/local logdir: /var/log/juju-user-local nonce: user-admin:bootstrap jobs: - JobManageEnviron upgradedToVersion: 1.17.5.1 cacert: '-----BEGIN CERTIFICATE----- MIICWzCCAcagAwIBAgIBADALBgkqhkiG9w0BAQUwQzENMAsGA1UEChMEanVqdTEy MDAGA1UEAwwpanVqdS1nZW5lcmF0ZWQgQ0EgZm9yIGVudmlyb25tZW50ICJsb2Nh bCIwHhcNMTQwMzA1MTQxOTA3WhcNMjQwMzA1MTQyNDA3WjBDMQ0wCwYDVQQKEwRq dWp1MTIwMAYDVQQDDClqdWp1LWdlbmVyYXRlZCBDQSBmb3IgZW52aXJvbm1lbnQg ImxvY2FsIjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwHsKV7fKfmSQt2QL P4+hrqQJhDTMifgNkIY9nTlLHegV5jl5XJ8lRYjZBXJEMz0AzW/RbrDElkn5+4Do pIWPNDAT0eztXBvVwL6qQOUtiBsA7vHQJMQaLVAmZNKvrHyuhcoG+hpf8EMaLdbA iCGKifs+Y0MFt5AeriVDH5lGlzcCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgCkMA8G A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFB3Td3SP66UToZkOjVh3Wy8b6HR6MB8G A1UdIwQYMBaAFB3Td3SP66UToZkOjVh3Wy8b6HR6MAsGCSqGSIb3DQEBBQOBgQB4 izvSRSpimi40aEOnZIsSMHVBiSCclpBg5cq7lGyiUSsDROTIbsRAKPBmrflB/qbf J70rWFwh/d/5ssCAYrZviFL6WvpuLD3j3m4PYampNMmvJf2s6zVRIMotEY+bVwfU z4jGaVpODac0i0bE0/Uh9qXK1UXcYY57vNNAgkaYAQ== -----END CERTIFICATE----- ' stateaddresses: - localhost:37017 statepassword: NB5imrDaWCCRW/4akSSvUxhX apiaddresses: - localhost:17070 apipassword: NB5imrDaWCCRW/4akSSvUxhX oldpassword: oBlMbFUGvCb2PMFgYVzjS6GD values: AGENT_SERVICE_NAME: juju-agent-user-local CONTAINER_TYPE: "" MONGO_SERVICE_NAME: juju-db-user-local NAMESPACE: user-local PROVIDER_TYPE: local STORAGE_ADDR: 10.0.3.1:8040 STORAGE_DIR: /home/user/.juju/local/storage stateservercert: '-----BEGIN CERTIFICATE----- MIICNzCCAaKgAwIBAgIBADALBgkqhkiG9w0BAQUwQzENMAsGA1UEChMEanVqdTEy MDAGA1UEAwwpanVqdS1nZW5lcmF0ZWQgQ0EgZm9yIGVudmlyb25tZW50ICJsb2Nh bCIwHhcNMTQwMzA1MTQxOTE1WhcNMjQwMzA1MTQyNDE1WjAbMQ0wCwYDVQQKEwRq dWp1MQowCAYDVQQDEwEqMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJnbuN L3m/oY7Er2lEF6ye1SodepvpI0CLCdLwrYP52cRxbVzoD1jbXveclolg2xoUquga qxsAhvVzzGaoLux1BoBD+G0N637fnY4XSIC9IuSkPOAdReKJkOvTL4nTjpzgfeHR hRin6Xckvp96L4Prmki7sYQ8PG9Q7TBcOf4yowIDAQABo2cwZTAOBgNVHQ8BAf8E BAMCAKgwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYEFE1MB3d+5BW+n066 lWcVkhta1etlMB8GA1UdIwQYMBaAFB3Td3SP66UToZkOjVh3Wy8b6HR6MAsGCSqG SIb3DQEBBQOBgQBnsBvl3hfIQbHhAlqritDBCWGpaXywlHe4PvyVL3LZTLiAZ9a/ BOSBfovs81sjUe5l60j+1vgRQgvT2Pnw6WGWmYWhSyxW7upEUl1LuZxnw3AVGVFO r140iBNUtTfGUf3PmyBXHSotqgMime+rNSjl25qSoYwnuQXdFdCKJoutYg== -----END CERTIFICATE----- ' stateserverkey: '-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDJnbuNL3m/oY7Er2lEF6ye1SodepvpI0CLCdLwrYP52cRxbVzo D1jbXveclolg2xoUqugaqxsAhvVzzGaoLux1BoBD+G0N637fnY4XSIC9IuSkPOAd ReKJkOvTL4nTjpzgfeHRhRin6Xckvp96L4Prmki7sYQ8PG9Q7TBcOf4yowIDAQAB AoGASEtzETFQ6tI3q3dqu6vxjhLJw0BP381wO2sOZJcTl+fqdPHOOrgmGKN5DoE8 SarHM1oFWGq6h/nc0eUdenk4+CokpbKRgUU9hB1TKGYMbN3bUTKPOqTMHbnrhWdT P/fqa+nXhvg7igMT3Rk7l9DsSxoYB5xZmiLaXqynVE5MNoECQQDRsgDDUrUOeMH6 1+GO+afb8beRzR8mnaBvja6XLlZB6SUcGet9bMgAiGH3arH6ARfNNsWrDAmvArah SKeqRB5TAkEA9iMEQDkcybCmxu4Y3YLeQuT9r3h26QhQjc+eRINS/3ZLN+lxKnXG N019ZUlsyL97lJBDzTMPsBqfXJ2pbqXwcQJBAJNLuPN63kl7E68zA3Ld9UYvBWY6 Mp56bJ7PZAs39kk4DuQtZNhmmBqfskkMPlZBfEmfJrxeqVKw0j56faPBU5cCQFYU mP/8+VxwM2OPEZMmmaS7gR1E/BEznzh5S9iaNQSy0kuTkMhQuCnPJ/OsYiczEH08 lvnEyc/E/8bcPM09q4ECQCFwMWzw2Jx9VOBGm60yiOKIdLgdDZOY/tP0jigNCMJF 47/BJx3FCgW3io81a4KOc445LxgiPUJyyCNlY1dW70o= -----END RSA PRIVATE KEY----- ' apiport: 17070 `[1:] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/mongo/�������������������������������������������0000755�0000153�0000161�00000000000�12321736000�023067� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/mongo/mongo.go�����������������������������������0000644�0000153�0000161�00000012356�12321735776�024567� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package mongo import ( "fmt" "os" "os/exec" "path" "path/filepath" "github.com/juju/loggo" "launchpad.net/juju-core/upstart" "launchpad.net/juju-core/utils" ) const ( maxFiles = 65000 maxProcs = 20000 ) var ( logger = loggo.GetLogger("juju.agent.mongo") oldMongoServiceName = "juju-db" // JujuMongodPath holds the default path to the juju-specific mongod. JujuMongodPath = "/usr/lib/juju/bin/mongod" // MongodbServerPath holds the default path to the generic mongod. MongodbServerPath = "/usr/bin/mongod" ) // MongoPackageForSeries returns the name of the mongo package for the series // of the machine that it is going to be running on. func MongoPackageForSeries(series string) string { switch series { case "precise", "quantal", "raring", "saucy": return "mongodb-server" default: // trusty and onwards return "juju-mongodb" } } // MongodPathForSeries returns the path to the mongod executable for the // series of the machine that it is going to be running on. func MongodPathForSeries(series string) string { if series == "trusty" { return JujuMongodPath } return MongodbServerPath } // MongoPath returns the executable path to be used to run mongod on this // machine. If the juju-bundled version of mongo exists, it will return that // path, otherwise it will return the command to run mongod from the path. func MongodPath() (string, error) { if _, err := os.Stat(JujuMongodPath); err == nil { return JujuMongodPath, nil } path, err := exec.LookPath("mongod") if err != nil { return "", err } return path, nil } // EnsureMongoServer ensures that the correct mongo upstart script is installed // and running. // // This method will remove old versions of the mongo upstart script as necessary // before installing the new version. func EnsureMongoServer(dir string, port int) error { // NOTE: ensure that the right package is installed? name := makeServiceName(mongoScriptVersion) // TODO: get the series from somewhere, non trusty values return // the existing default path. mongodPath := MongodPathForSeries("some-series") service, err := MongoUpstartService(name, mongodPath, dir, port) if err != nil { return err } if service.Installed() { return nil } if err := removeOldMongoServices(mongoScriptVersion); err != nil { return err } if err := makeJournalDirs(dir); err != nil { return err } if err := service.Install(); err != nil { return fmt.Errorf("failed to install mongo service %q: %v", service.Name, err) } return service.Start() } func makeJournalDirs(dir string) error { journalDir := path.Join(dir, "journal") if err := os.MkdirAll(journalDir, 0700); err != nil { logger.Errorf("failed to make mongo journal dir %s: %v", journalDir, err) return err } // manually create the prealloc files, since otherwise they get created as 100M files. zeroes := make([]byte, 64*1024) // should be enough for anyone for x := 0; x < 3; x++ { name := fmt.Sprintf("prealloc.%d", x) filename := filepath.Join(journalDir, name) f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700) if err != nil { return fmt.Errorf("failed to open mongo prealloc file %q: %v", filename, err) } defer f.Close() for total := 0; total < 1024*1024; { n, err := f.Write(zeroes) if err != nil { return fmt.Errorf("failed to write to mongo prealloc file %q: %v", filename, err) } total += n } } return nil } // removeOldMongoServices looks for any old juju mongo upstart scripts and // removes them. func removeOldMongoServices(curVersion int) error { old := upstart.NewService(oldMongoServiceName) if err := old.StopAndRemove(); err != nil { logger.Errorf("Failed to remove old mongo upstart service %q: %v", old.Name, err) return err } // the new formatting for the script name started at version 2 for x := 2; x < curVersion; x++ { old := upstart.NewService(makeServiceName(x)) if err := old.StopAndRemove(); err != nil { logger.Errorf("Failed to remove old mongo upstart service %q: %v", old.Name, err) return err } } return nil } func makeServiceName(version int) string { return fmt.Sprintf("juju-db-v%d", version) } // mongoScriptVersion keeps track of changes to the mongo upstart script. // Update this version when you update the script that gets installed from // MongoUpstartService. const mongoScriptVersion = 2 // MongoUpstartService returns the upstart config for the mongo state service. // // This method assumes there is a server.pem keyfile in dataDir. func MongoUpstartService(name, mongodExec, dataDir string, port int) (*upstart.Conf, error) { keyFile := path.Join(dataDir, "server.pem") svc := upstart.NewService(name) dbDir := path.Join(dataDir, "db") conf := &upstart.Conf{ Service: *svc, Desc: "juju state database", Limit: map[string]string{ "nofile": fmt.Sprintf("%d %d", maxFiles, maxFiles), "nproc": fmt.Sprintf("%d %d", maxProcs, maxProcs), }, Cmd: mongodExec + " --auth" + " --dbpath=" + dbDir + " --sslOnNormalPorts" + " --sslPEMKeyFile " + utils.ShQuote(keyFile) + " --sslPEMKeyPassword ignored" + " --bind_ip 0.0.0.0" + " --port " + fmt.Sprint(port) + " --noprealloc" + " --syslog" + " --smallfiles", // TODO(Nate): uncomment when we commit HA stuff // + // " --replSet juju", } return conf, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/mongo/mongo_test.go������������������������������0000644�0000153�0000161�00000007225�12321735776�025625� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package mongo import ( "io/ioutil" "os" "path" "path/filepath" "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/upstart" ) func Test(t *testing.T) { gc.TestingT(t) } type MongoSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&MongoSuite{}) func (s *MongoSuite) SetUpSuite(c *gc.C) { testpath := c.MkDir() s.PatchEnvPathPrepend(testpath) // mock out the start method so we can fake install services without sudo start := filepath.Join(testpath, "start") err := ioutil.WriteFile(start, []byte("#!/bin/bash --norc\nexit 0"), 0755) c.Assert(err, gc.IsNil) s.PatchValue(&upstart.InitDir, c.MkDir()) } func (s *MongoSuite) TestJujuMongodPath(c *gc.C) { d := c.MkDir() defer os.RemoveAll(d) mongoPath := filepath.Join(d, "mongod") s.PatchValue(&JujuMongodPath, mongoPath) err := ioutil.WriteFile(mongoPath, nil, 0777) c.Assert(err, gc.IsNil) obtained, err := MongodPath() c.Check(err, gc.IsNil) c.Check(obtained, gc.Equals, mongoPath) } func (s *MongoSuite) TestDefaultMongodPath(c *gc.C) { s.PatchValue(&JujuMongodPath, "/not/going/to/exist/mongod") dir := c.MkDir() s.PatchEnvPathPrepend(dir) filename := filepath.Join(dir, "mongod") err := ioutil.WriteFile(filename, nil, 0777) c.Assert(err, gc.IsNil) obtained, err := MongodPath() c.Check(err, gc.IsNil) c.Check(obtained, gc.Equals, filename) } func (s *MongoSuite) TestRemoveOldMongoServices(c *gc.C) { s.PatchValue(&oldMongoServiceName, "someNameThatShouldntExist") // Make fake old services. // We defer the removes manually just in case the test fails, we don't leave // junk behind. conf := makeService(oldMongoServiceName, c) defer conf.Remove() conf2 := makeService(makeServiceName(2), c) defer conf2.Remove() conf3 := makeService(makeServiceName(3), c) defer conf3.Remove() // Remove with current version = 4, which should remove all previous // versions plus the old service name. err := removeOldMongoServices(4) c.Assert(err, gc.IsNil) c.Assert(conf.Installed(), jc.IsFalse) c.Assert(conf2.Installed(), jc.IsFalse) c.Assert(conf3.Installed(), jc.IsFalse) } func (s *MongoSuite) TestMakeJournalDirs(c *gc.C) { dir := c.MkDir() err := makeJournalDirs(dir) c.Assert(err, gc.IsNil) testJournalDirs(dir, c) } func testJournalDirs(dir string, c *gc.C) { journalDir := path.Join(dir, "journal") c.Check(journalDir, jc.IsDirectory) info, err := os.Stat(filepath.Join(journalDir, "prealloc.0")) c.Check(err, gc.IsNil) size := int64(1024 * 1024) c.Check(info.Size(), gc.Equals, size) info, err = os.Stat(filepath.Join(journalDir, "prealloc.1")) c.Check(err, gc.IsNil) c.Check(info.Size(), gc.Equals, size) info, err = os.Stat(filepath.Join(journalDir, "prealloc.2")) c.Check(err, gc.IsNil) c.Check(info.Size(), gc.Equals, size) } func (s *MongoSuite) TestEnsureMongoServer(c *gc.C) { dir := c.MkDir() port := 25252 oldsvc := makeService(oldMongoServiceName, c) defer oldsvc.Remove() err := EnsureMongoServer(dir, port) c.Assert(err, gc.IsNil) mongodPath := MongodPathForSeries("some-series") svc, err := MongoUpstartService(makeServiceName(mongoScriptVersion), mongodPath, dir, port) c.Assert(err, gc.IsNil) defer svc.Remove() testJournalDirs(dir, c) c.Check(oldsvc.Installed(), jc.IsFalse) c.Check(svc.Installed(), jc.IsTrue) // now check we can call it multiple times without error err = EnsureMongoServer(dir, port) c.Assert(err, gc.IsNil) } func makeService(name string, c *gc.C) *upstart.Conf { conf := &upstart.Conf{ Desc: "foo", Service: *upstart.NewService(name), Cmd: "echo hi", } err := conf.Install() c.Assert(err, gc.IsNil) return conf } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/agent.go�����������������������������������������0000644�0000153�0000161�00000046472�12321735776�023435� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent import ( "bytes" "fmt" "io/ioutil" "os" "path" "path/filepath" "regexp" "strings" "sync" "github.com/errgo/errgo" "github.com/juju/loggo" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.agent") // DefaultLogDir defines the default log directory for juju agents. // It's defined as a variable so it could be overridden in tests. var DefaultLogDir = "/var/log/juju" // DefaultDataDir defines the default data directory for juju agents. // It's defined as a variable so it could be overridden in tests. var DefaultDataDir = "/var/lib/juju" const ( LxcBridge = "LXC_BRIDGE" ProviderType = "PROVIDER_TYPE" ContainerType = "CONTAINER_TYPE" Namespace = "NAMESPACE" StorageDir = "STORAGE_DIR" StorageAddr = "STORAGE_ADDR" AgentServiceName = "AGENT_SERVICE_NAME" MongoServiceName = "MONGO_SERVICE_NAME" ) // The Config interface is the sole way that the agent gets access to the // configuration information for the machine and unit agents. There should // only be one instance of a config object for any given agent, and this // interface is passed between multiple go routines. The mutable methods are // protected by a mutex, and it is expected that the caller doesn't modify any // slice that may be returned. // // NOTE: should new mutating methods be added to this interface, consideration // is needed around the synchronisation as a single instance is used in // multiple go routines. type Config interface { // DataDir returns the data directory. Each agent has a subdirectory // containing the configuration files. DataDir() string // LogDir returns the log directory. All logs from all agents on // the machine are written to this directory. LogDir() string // Jobs returns a list of MachineJobs that need to run. Jobs() []params.MachineJob // Tag returns the tag of the entity on whose behalf the state connection // will be made. Tag() string // Dir returns the agent's directory. Dir() string // Nonce returns the nonce saved when the machine was provisioned // TODO: make this one of the key/value pairs. Nonce() string // CACert returns the CA certificate that is used to validate the state or // API servier's certificate. CACert() []byte // OpenAPI tries to connect to an API end-point. If a non-empty // newPassword is returned, OpenAPI will have written the configuration // with the new password; the caller should set the connecting entity's // password accordingly. OpenAPI(dialOpts api.DialOpts) (st *api.State, newPassword string, err error) // APIAddresses returns the addresses needed to connect to the api server APIAddresses() ([]string, error) // OpenState tries to open a direct connection to the state database using // the given Conf. OpenState(policy state.Policy) (*state.State, error) // Write writes the agent configuration. Write() error // WriteCommands returns shell commands to write the agent configuration. // It returns an error if the configuration does not have all the right // elements. WriteCommands() ([]string, error) // APIServerDetails returns the details needed to run an API server. APIServerDetails() (port int, cert, key []byte) // UpgradedToVersion returns the version for which all upgrade steps have been // successfully run, which is also the same as the initially deployed version. UpgradedToVersion() version.Number // WriteUpgradedToVersion updates the config's UpgradedToVersion and writes // the new agent configuration. WriteUpgradedToVersion(newVersion version.Number) error // Value returns the value associated with the key, or an empty string if // the key is not found. Value(key string) string // SetValue updates the value for the specified key. SetValue(key, value string) StateInitializer } // MigrateConfigParams holds agent config values to change in a // MigrateConfig call. Empty fields will be ignored. DeleteValues // specifies a list of keys to delete. type MigrateConfigParams struct { DataDir string LogDir string Jobs []params.MachineJob DeleteValues []string Values map[string]string } // MigrateConfig takes an existing agent config and applies the given // newParams selectively. Only non-empty fields in newParams are used // to change existing config settings. All changes are written // atomically. UpgradedToVersion cannot be changed here, because // MigrateConfig is most likely called during an upgrade, so it will be // changed at the end of the upgrade anyway, if successful. func MigrateConfig(currentConfig Config, newParams MigrateConfigParams) error { configMutex.Lock() defer configMutex.Unlock() config := currentConfig.(*configInternal) if newParams.DataDir != "" { config.dataDir = newParams.DataDir } if newParams.LogDir != "" { config.logDir = newParams.LogDir } if len(newParams.Jobs) > 0 { config.jobs = make([]params.MachineJob, len(newParams.Jobs)) copy(config.jobs, newParams.Jobs) } for _, key := range newParams.DeleteValues { delete(config.values, key) } for key, value := range newParams.Values { if config.values == nil { config.values = make(map[string]string) } config.values[key] = value } if err := config.check(); err != nil { return fmt.Errorf("migrated agent config is invalid: %v", err) } oldConfigFile := config.configFilePath config.configFilePath = ConfigPath(config.dataDir, config.tag) if err := config.write(); err != nil { return fmt.Errorf("cannot migrate agent config: %v", err) } if oldConfigFile != config.configFilePath && oldConfigFile != "" { err := os.Remove(oldConfigFile) if err != nil && !os.IsNotExist(err) { return fmt.Errorf("cannot remove old agent config %q: %v", oldConfigFile, err) } } return nil } // Ensure that the configInternal struct implements the Config interface. var _ Config = (*configInternal)(nil) // The configMutex should be locked before any writing to disk during // the write commands, and unlocked when the writing is complete. This // process wide lock should stop any unintended concurrent writes. // This may happen when multiple go-routines may be adding things to // the agent config, and wanting to persist them to disk. To ensure // that the correct data is written to disk, the mutex should be // locked prior to generating any disk state. This way calls that // might get interleaved would always write the most recent state to // disk. Since we have different agent configs for each agent, and // there is only one process for each agent, a simple mutex is enough // for concurrency. The mutex should also be locked around any access // to mutable values, either setting or getting. The only mutable // value is the values map. Retrieving and setting values here are // protected by the mutex. New mutating methods should also be // synchronized using this mutex. Config is essentially a singleton // implementation, having a non-constructable-in-a-normal-way backing // type configInternal. var configMutex sync.Mutex type connectionDetails struct { addresses []string password string } type configInternal struct { configFilePath string dataDir string logDir string tag string nonce string jobs []params.MachineJob upgradedToVersion version.Number caCert []byte stateDetails *connectionDetails apiDetails *connectionDetails oldPassword string stateServerCert []byte stateServerKey []byte apiPort int values map[string]string } type AgentConfigParams struct { DataDir string LogDir string Jobs []params.MachineJob UpgradedToVersion version.Number Tag string Password string Nonce string StateAddresses []string APIAddresses []string CACert []byte Values map[string]string } // NewAgentConfig returns a new config object suitable for use for a // machine or unit agent. func NewAgentConfig(configParams AgentConfigParams) (Config, error) { if configParams.DataDir == "" { return nil, errgo.Trace(requiredError("data directory")) } logDir := DefaultLogDir if configParams.LogDir != "" { logDir = configParams.LogDir } if configParams.Tag == "" { return nil, errgo.Trace(requiredError("entity tag")) } if configParams.UpgradedToVersion == version.Zero { return nil, errgo.Trace(requiredError("upgradedToVersion")) } if configParams.Password == "" { return nil, errgo.Trace(requiredError("password")) } if len(configParams.CACert) == 0 { return nil, errgo.Trace(requiredError("CA certificate")) } // Note that the password parts of the state and api information are // blank. This is by design. config := &configInternal{ logDir: logDir, dataDir: configParams.DataDir, jobs: configParams.Jobs, upgradedToVersion: configParams.UpgradedToVersion, tag: configParams.Tag, nonce: configParams.Nonce, caCert: configParams.CACert, oldPassword: configParams.Password, values: configParams.Values, } if len(configParams.StateAddresses) > 0 { config.stateDetails = &connectionDetails{ addresses: configParams.StateAddresses, } } if len(configParams.APIAddresses) > 0 { config.apiDetails = &connectionDetails{ addresses: configParams.APIAddresses, } } if err := config.check(); err != nil { return nil, err } if config.values == nil { config.values = make(map[string]string) } config.configFilePath = ConfigPath(config.dataDir, config.tag) return config, nil } type StateMachineConfigParams struct { AgentConfigParams StateServerCert []byte StateServerKey []byte StatePort int APIPort int } // NewStateMachineConfig returns a configuration suitable for // a machine running the state server. func NewStateMachineConfig(configParams StateMachineConfigParams) (Config, error) { if configParams.StateServerCert == nil { return nil, errgo.Trace(requiredError("state server cert")) } if configParams.StateServerKey == nil { return nil, errgo.Trace(requiredError("state server key")) } config0, err := NewAgentConfig(configParams.AgentConfigParams) if err != nil { return nil, err } config := config0.(*configInternal) config.stateServerCert = configParams.StateServerCert config.stateServerKey = configParams.StateServerKey config.apiPort = configParams.APIPort return config, nil } // Dir returns the agent-specific data directory. func Dir(dataDir, agentName string) string { // Note: must use path, not filepath, as this // function is used by the client on Windows. return path.Join(dataDir, "agents", agentName) } // ConfigPath returns the full path to the agent config file. // NOTE: Delete this once all agents accept --config instead // of --data-dir - it won't be needed anymore. func ConfigPath(dataDir, agentName string) string { return filepath.Join(Dir(dataDir, agentName), agentConfigFilename) } // ReadConf reads configuration data from the given location. func ReadConf(configFilePath string) (Config, error) { // Even though the ReadConf is done at the start of the agent loading, and // that this should not be called more than once by an agent, I feel that // not locking the mutex that is used to protect writes is wrong. configMutex.Lock() defer configMutex.Unlock() var ( format formatter config *configInternal ) configData, err := ioutil.ReadFile(configFilePath) if err != nil { return nil, fmt.Errorf("cannot read agent config %q: %v", configFilePath, err) } // Try to read the legacy format file. dir := filepath.Dir(configFilePath) legacyFormatPath := filepath.Join(dir, legacyFormatFilename) formatBytes, err := ioutil.ReadFile(legacyFormatPath) if err != nil && !os.IsNotExist(err) { return nil, fmt.Errorf("cannot read format file: %v", err) } formatData := string(formatBytes) if err == nil { // It exists, so unmarshal with a legacy formatter. // Drop the format prefix to leave the version only. if !strings.HasPrefix(formatData, legacyFormatPrefix) { return nil, fmt.Errorf("malformed agent config format %q", formatData) } format, err = getFormatter(strings.TrimPrefix(formatData, legacyFormatPrefix)) if err != nil { return nil, err } config, err = format.unmarshal(configData) } else { // Does not exist, just parse the data. format, config, err = parseConfigData(configData) } if err != nil { return nil, err } logger.Debugf("read agent config, format %q", format.version()) config.configFilePath = configFilePath if format != currentFormat { // Migrate from a legacy format to the new one. err := config.write() if err != nil { return nil, fmt.Errorf("cannot migrate %s agent config to %s: %v", format.version(), currentFormat.version(), err) } logger.Debugf("migrated agent config from %s to %s", format.version(), currentFormat.version()) err = os.Remove(legacyFormatPath) if err != nil && !os.IsNotExist(err) { return nil, fmt.Errorf("cannot remove legacy format file %q: %v", legacyFormatPath, err) } } return config, nil } func requiredError(what string) error { return fmt.Errorf("%s not found in configuration", what) } func (c *configInternal) File(name string) string { return path.Join(c.Dir(), name) } func (c *configInternal) DataDir() string { return c.dataDir } func (c *configInternal) LogDir() string { return c.logDir } func (c *configInternal) Jobs() []params.MachineJob { return c.jobs } func (c *configInternal) Nonce() string { return c.nonce } func (c *configInternal) UpgradedToVersion() version.Number { return c.upgradedToVersion } func (c *configInternal) CACert() []byte { // Give the caller their own copy of the cert to avoid any possibility of // modifying the config's copy. result := append([]byte{}, c.caCert...) return result } func (c *configInternal) Value(key string) string { configMutex.Lock() defer configMutex.Unlock() return c.values[key] } func (c *configInternal) SetValue(key, value string) { configMutex.Lock() defer configMutex.Unlock() if value == "" { delete(c.values, key) } else { c.values[key] = value } } func (c *configInternal) APIServerDetails() (port int, cert, key []byte) { return c.apiPort, c.stateServerCert, c.stateServerKey } func (c *configInternal) APIAddresses() ([]string, error) { if c.apiDetails == nil { return []string{}, errgo.New("No apidetails in config") } return append([]string{}, c.apiDetails.addresses...), nil } func (c *configInternal) Tag() string { return c.tag } func (c *configInternal) Dir() string { return Dir(c.dataDir, c.tag) } func (c *configInternal) check() error { if c.stateDetails == nil && c.apiDetails == nil { return errgo.Trace(requiredError("state or API addresses")) } if c.stateDetails != nil { if err := checkAddrs(c.stateDetails.addresses, "state server address"); err != nil { return err } } if c.apiDetails != nil { if err := checkAddrs(c.apiDetails.addresses, "API server address"); err != nil { return err } } return nil } var validAddr = regexp.MustCompile("^.+:[0-9]+$") func checkAddrs(addrs []string, what string) error { if len(addrs) == 0 { return errgo.Trace(requiredError(what)) } for _, a := range addrs { if !validAddr.MatchString(a) { return errgo.New("invalid %s %q", what, a) } } return nil } // writeNewPassword generates a new password and writes // the configuration with it in. func (c *configInternal) writeNewPassword() (string, error) { newPassword, err := utils.RandomPassword() if err != nil { return "", err } // Make a copy of the configuration so that if we fail // to write the configuration file, the configuration will // still be valid. other := *c if c.stateDetails != nil { stateDetails := *c.stateDetails stateDetails.password = newPassword other.stateDetails = &stateDetails } if c.apiDetails != nil { apiDetails := *c.apiDetails apiDetails.password = newPassword other.apiDetails = &apiDetails } logger.Debugf("writing configuration file") if err := other.write(); err != nil { return "", err } *c = other return newPassword, nil } func (c *configInternal) fileContents() ([]byte, error) { data, err := currentFormat.marshal(c) if err != nil { return nil, err } var buf bytes.Buffer fmt.Fprintf(&buf, "%s%s\n", formatPrefix, currentFormat.version()) buf.Write(data) return buf.Bytes(), nil } // write is the internal implementation of c.Write(). func (c *configInternal) write() error { data, err := c.fileContents() if err != nil { return err } // Make sure the config dir gets created. configDir := filepath.Dir(c.configFilePath) if err := os.MkdirAll(configDir, 0755); err != nil { return fmt.Errorf("cannot create agent config dir %q: %v", configDir, err) } return utils.AtomicWriteFile(c.configFilePath, data, 0600) } func (c *configInternal) Write() error { // Lock is taken prior to generating any content to write. configMutex.Lock() defer configMutex.Unlock() return c.write() } func (c *configInternal) WriteUpgradedToVersion(newVersion version.Number) error { configMutex.Lock() defer configMutex.Unlock() originalVersion := c.upgradedToVersion c.upgradedToVersion = newVersion err := c.write() if err != nil { // We don't want to retain the new version if there's been an error writing the file. c.upgradedToVersion = originalVersion } return err } func (c *configInternal) WriteCommands() ([]string, error) { data, err := c.fileContents() if err != nil { return nil, err } commands := []string{"mkdir -p " + utils.ShQuote(c.Dir())} commands = append(commands, writeFileCommands(c.File(agentConfigFilename), data, 0600)...) return commands, nil } func (c *configInternal) OpenAPI(dialOpts api.DialOpts) (st *api.State, newPassword string, err error) { configMutex.Lock() defer configMutex.Unlock() info := api.Info{ Addrs: c.apiDetails.addresses, Password: c.apiDetails.password, CACert: c.caCert, Tag: c.tag, Nonce: c.nonce, } if info.Password != "" { st, err := api.Open(&info, dialOpts) if err == nil { return st, "", nil } if !params.IsCodeUnauthorized(err) { return nil, "", err } // Access isn't authorized even though we have a password // This can happen if we crash after saving the // password but before changing it, so we'll try again // with the old password. } info.Password = c.oldPassword st, err = api.Open(&info, dialOpts) if err != nil { return nil, "", err } // We've succeeded in connecting with the old password, so // we can now change it to something more private. password, err := c.writeNewPassword() if err != nil { st.Close() return nil, "", err } return st, password, nil } func (c *configInternal) OpenState(policy state.Policy) (*state.State, error) { info := state.Info{ Addrs: c.stateDetails.addresses, Password: c.stateDetails.password, CACert: c.caCert, Tag: c.tag, } if info.Password != "" { st, err := state.Open(&info, state.DefaultDialOpts(), policy) if err == nil { return st, nil } // TODO(rog) remove this fallback behaviour when // all initial connections are via the API. if !errors.IsUnauthorizedError(err) { return nil, err } } info.Password = c.oldPassword return state.Open(&info, state.DefaultDialOpts(), policy) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/tools/�������������������������������������������0000755�0000153�0000161�00000000000�12321735643�023124� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/tools/diskmanager_test.go������������������������0000644�0000153�0000161�00000006644�12321735642�027010� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "bytes" "encoding/json" "os" "path/filepath" gc "launchpad.net/gocheck" agenttools "launchpad.net/juju-core/agent/tools" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) var _ = gc.Suite(&DiskManagerSuite{}) var _ agenttools.ToolsManager = (*agenttools.DiskManager)(nil) type DiskManagerSuite struct { testbase.LoggingSuite dataDir string manager agenttools.ToolsManager } func (s *DiskManagerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.dataDir = c.MkDir() s.manager = agenttools.NewDiskManager(s.dataDir) } func (s *DiskManagerSuite) toolsDir() string { // TODO: Somehow hide this behind the DataManager return filepath.Join(s.dataDir, "tools") } // Copied from environs/agent/tools_test.go func (s *DiskManagerSuite) TestUnpackToolsContents(c *gc.C) { files := []*coretesting.TarFile{ coretesting.NewTarFile("bar", 0755, "bar contents"), coretesting.NewTarFile("foo", 0755, "foo contents"), } gzfile, checksum := coretesting.TarGz(files...) t1 := &coretools.Tools{ URL: "http://foo/bar", Version: version.MustParseBinary("1.2.3-foo-bar"), Size: int64(len(gzfile)), SHA256: checksum, } err := s.manager.UnpackTools(t1, bytes.NewReader(gzfile)) c.Assert(err, gc.IsNil) assertDirNames(c, s.toolsDir(), []string{"1.2.3-foo-bar"}) s.assertToolsContents(c, t1, files) // Try to unpack the same version of tools again - it should succeed, // leaving the original version around. files2 := []*coretesting.TarFile{ coretesting.NewTarFile("bar", 0755, "bar2 contents"), coretesting.NewTarFile("x", 0755, "x contents"), } gzfile2, checksum2 := coretesting.TarGz(files2...) t2 := &coretools.Tools{ URL: "http://arble", Version: version.MustParseBinary("1.2.3-foo-bar"), Size: int64(len(gzfile2)), SHA256: checksum2, } err = s.manager.UnpackTools(t2, bytes.NewReader(gzfile2)) c.Assert(err, gc.IsNil) assertDirNames(c, s.toolsDir(), []string{"1.2.3-foo-bar"}) s.assertToolsContents(c, t1, files) } func (t *DiskManagerSuite) TestSharedToolsDir(c *gc.C) { manager := agenttools.NewDiskManager("/var/lib/juju") dir := manager.SharedToolsDir(version.MustParseBinary("1.2.3-precise-amd64")) c.Assert(dir, gc.Equals, "/var/lib/juju/tools/1.2.3-precise-amd64") } // assertToolsContents asserts that the directory for the tools // has the given contents. func (s *DiskManagerSuite) assertToolsContents(c *gc.C, t *coretools.Tools, files []*coretesting.TarFile) { var wantNames []string for _, f := range files { wantNames = append(wantNames, f.Header.Name) } wantNames = append(wantNames, toolsFile) dir := s.manager.(*agenttools.DiskManager).SharedToolsDir(t.Version) assertDirNames(c, dir, wantNames) expectedFileContents, err := json.Marshal(t) c.Assert(err, gc.IsNil) assertFileContents(c, dir, toolsFile, string(expectedFileContents), 0200) for _, f := range files { assertFileContents(c, dir, f.Header.Name, f.Contents, 0400) } gotTools, err := s.manager.ReadTools(t.Version) c.Assert(err, gc.IsNil) c.Assert(*gotTools, gc.Equals, *t) // Make sure that the tools directory is readable by the ubuntu user (for // juju-run) info, err := os.Stat(dir) c.Assert(err, gc.IsNil) c.Assert(info.Mode().Perm(), gc.Equals, os.FileMode(0755)) } ��������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/tools/tools_test.go������������������������������0000644�0000153�0000161�00000021603�12321735642�025653� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "bytes" "encoding/json" "io/ioutil" "os" "path/filepath" "sort" gc "launchpad.net/gocheck" agenttools "launchpad.net/juju-core/agent/tools" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" coretest "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) type ToolsSuite struct { testbase.LoggingSuite dataDir string } var _ = gc.Suite(&ToolsSuite{}) func (t *ToolsSuite) SetUpTest(c *gc.C) { t.LoggingSuite.SetUpTest(c) t.dataDir = c.MkDir() } func (t *ToolsSuite) TestPackageDependencies(c *gc.C) { // This test is to ensure we don't bring in dependencies on state, environ // or any of the other bigger packages that'll drag in yet more dependencies. // Only imports that start with "launchpad.net/juju-core" are checked, and the // resulting slice has that prefix removed to keep the output short. c.Assert(testbase.FindJujuCoreImports(c, "launchpad.net/juju-core/agent/tools"), gc.DeepEquals, []string{"juju/arch", "tools", "utils/set", "version"}) } const toolsFile = "downloaded-tools.txt" // gzyesses holds the result of running: // yes | head -17000 | gzip var gzyesses = []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x29, 0xae, 0x1a, 0x50, 0x00, 0x03, 0xed, 0xc2, 0x31, 0x0d, 0x00, 0x00, 0x00, 0x02, 0xa0, 0xdf, 0xc6, 0xb6, 0xb7, 0x87, 0x63, 0xd0, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x31, 0x53, 0xad, 0x03, 0x8d, 0xd0, 0x84, 0x00, 0x00, } type badDataTest struct { data []byte checksum string err string } func initBadDataTest(name string, mode os.FileMode, contents string, err string) badDataTest { var result badDataTest result.data, result.checksum = testing.TarGz(testing.NewTarFile(name, mode, contents)) result.err = err return result } var unpackToolsBadDataTests = []badDataTest{ initBadDataTest("bar", os.ModeDir, "", "bad file type.*"), initBadDataTest("../../etc/passwd", 0755, "", "bad name.*"), initBadDataTest(`\ini.sys`, 0755, "", "bad name.*"), badDataTest{[]byte("x"), "2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881", "unexpected EOF"}, badDataTest{gzyesses, "8d900c68a1a847aae4e95edcb29fcecd142c9b88ca4fe63209c216edbed546e1", "archive/tar: invalid tar header"}, } func (t *ToolsSuite) TestUnpackToolsBadData(c *gc.C) { for i, test := range unpackToolsBadDataTests { c.Logf("test %d", i) testTools := &coretest.Tools{ URL: "http://foo/bar", Version: version.MustParseBinary("1.2.3-foo-bar"), Size: int64(len(test.data)), SHA256: test.checksum, } err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(test.data)) c.Assert(err, gc.ErrorMatches, test.err) assertDirNames(c, t.toolsDir(), []string{}) } } func (t *ToolsSuite) TestUnpackToolsBadChecksum(c *gc.C) { data, _ := testing.TarGz(testing.NewTarFile("tools", 0755, "some data")) testTools := &coretest.Tools{ URL: "http://foo/bar", Version: version.MustParseBinary("1.2.3-foo-bar"), Size: int64(len(data)), SHA256: "1234", } err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(data)) c.Assert(err, gc.ErrorMatches, "tarball sha256 mismatch, expected 1234, got .*") _, err = os.Stat(t.toolsDir()) c.Assert(err, gc.FitsTypeOf, &os.PathError{}) } func (t *ToolsSuite) toolsDir() string { return filepath.Join(t.dataDir, "tools") } func (t *ToolsSuite) TestUnpackToolsContents(c *gc.C) { files := []*testing.TarFile{ testing.NewTarFile("bar", 0755, "bar contents"), testing.NewTarFile("foo", 0755, "foo contents"), } data, checksum := testing.TarGz(files...) testTools := &coretest.Tools{ URL: "http://foo/bar", Version: version.MustParseBinary("1.2.3-foo-bar"), Size: int64(len(data)), SHA256: checksum, } err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(data)) c.Assert(err, gc.IsNil) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar"}) t.assertToolsContents(c, testTools, files) // Try to unpack the same version of tools again - it should succeed, // leaving the original version around. files2 := []*testing.TarFile{ testing.NewTarFile("bar", 0755, "bar2 contents"), testing.NewTarFile("x", 0755, "x contents"), } data2, checksum2 := testing.TarGz(files2...) tools2 := &coretest.Tools{ URL: "http://arble", Version: version.MustParseBinary("1.2.3-foo-bar"), Size: int64(len(data2)), SHA256: checksum2, } err = agenttools.UnpackTools(t.dataDir, tools2, bytes.NewReader(data2)) c.Assert(err, gc.IsNil) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar"}) t.assertToolsContents(c, testTools, files) } func (t *ToolsSuite) TestReadToolsErrors(c *gc.C) { vers := version.MustParseBinary("1.2.3-precise-amd64") testTools, err := agenttools.ReadTools(t.dataDir, vers) c.Assert(testTools, gc.IsNil) c.Assert(err, gc.ErrorMatches, "cannot read tools metadata in tools directory: .*") dir := agenttools.SharedToolsDir(t.dataDir, vers) err = os.MkdirAll(dir, 0755) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(dir, toolsFile), []byte(" \t\n"), 0644) c.Assert(err, gc.IsNil) testTools, err = agenttools.ReadTools(t.dataDir, vers) c.Assert(testTools, gc.IsNil) c.Assert(err, gc.ErrorMatches, "invalid tools metadata in tools directory .*") } func (t *ToolsSuite) TestChangeAgentTools(c *gc.C) { files := []*testing.TarFile{ testing.NewTarFile("jujuc", 0755, "juju executable"), testing.NewTarFile("jujud", 0755, "jujuc executable"), } data, checksum := testing.TarGz(files...) testTools := &coretest.Tools{ URL: "http://foo/bar1", Version: version.MustParseBinary("1.2.3-foo-bar"), Size: int64(len(data)), SHA256: checksum, } err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(data)) c.Assert(err, gc.IsNil) gotTools, err := agenttools.ChangeAgentTools(t.dataDir, "testagent", testTools.Version) c.Assert(err, gc.IsNil) c.Assert(*gotTools, gc.Equals, *testTools) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar", "testagent"}) assertDirNames(c, agenttools.ToolsDir(t.dataDir, "testagent"), []string{"jujuc", "jujud", toolsFile}) // Upgrade again to check that the link replacement logic works ok. files2 := []*testing.TarFile{ testing.NewTarFile("foo", 0755, "foo content"), testing.NewTarFile("bar", 0755, "bar content"), } data2, checksum2 := testing.TarGz(files2...) tools2 := &coretest.Tools{ URL: "http://foo/bar2", Version: version.MustParseBinary("1.2.4-foo-bar"), Size: int64(len(data2)), SHA256: checksum2, } err = agenttools.UnpackTools(t.dataDir, tools2, bytes.NewReader(data2)) c.Assert(err, gc.IsNil) gotTools, err = agenttools.ChangeAgentTools(t.dataDir, "testagent", tools2.Version) c.Assert(err, gc.IsNil) c.Assert(*gotTools, gc.Equals, *tools2) assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar", "1.2.4-foo-bar", "testagent"}) assertDirNames(c, agenttools.ToolsDir(t.dataDir, "testagent"), []string{"foo", "bar", toolsFile}) } func (t *ToolsSuite) TestSharedToolsDir(c *gc.C) { dir := agenttools.SharedToolsDir("/var/lib/juju", version.MustParseBinary("1.2.3-precise-amd64")) c.Assert(dir, gc.Equals, "/var/lib/juju/tools/1.2.3-precise-amd64") } // assertToolsContents asserts that the directory for the tools // has the given contents. func (t *ToolsSuite) assertToolsContents(c *gc.C, testTools *coretest.Tools, files []*testing.TarFile) { var wantNames []string for _, f := range files { wantNames = append(wantNames, f.Header.Name) } wantNames = append(wantNames, toolsFile) dir := agenttools.SharedToolsDir(t.dataDir, testTools.Version) assertDirNames(c, dir, wantNames) expectedURLFileContents, err := json.Marshal(testTools) c.Assert(err, gc.IsNil) assertFileContents(c, dir, toolsFile, string(expectedURLFileContents), 0200) for _, f := range files { assertFileContents(c, dir, f.Header.Name, f.Contents, 0400) } gotTools, err := agenttools.ReadTools(t.dataDir, testTools.Version) c.Assert(err, gc.IsNil) c.Assert(*gotTools, gc.Equals, *testTools) } // assertFileContents asserts that the given file in the // given directory has the given contents. func assertFileContents(c *gc.C, dir, file, contents string, mode os.FileMode) { file = filepath.Join(dir, file) info, err := os.Stat(file) c.Assert(err, gc.IsNil) c.Assert(info.Mode()&(os.ModeType|mode), gc.Equals, mode) data, err := ioutil.ReadFile(file) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, contents) } // assertDirNames asserts that the given directory // holds the given file or directory names. func assertDirNames(c *gc.C, dir string, names []string) { f, err := os.Open(dir) c.Assert(err, gc.IsNil) defer f.Close() dnames, err := f.Readdirnames(0) c.Assert(err, gc.IsNil) sort.Strings(dnames) sort.Strings(names) c.Assert(dnames, gc.DeepEquals, names) } �����������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/tools/toolsdir.go��������������������������������0000644�0000153�0000161�00000012514�12321735642�025314� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "archive/tar" "compress/gzip" "crypto/sha256" "encoding/json" "fmt" "io" "io/ioutil" "os" "path" "strings" "github.com/errgo/errgo" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) const toolsFile = "downloaded-tools.txt" // SharedToolsDir returns the directory that is used to // store binaries for the given version of the juju tools // within the dataDir directory. func SharedToolsDir(dataDir string, vers version.Binary) string { return path.Join(dataDir, "tools", vers.String()) } // ToolsDir returns the directory that is used/ to store binaries for // the tools used by the given agent within the given dataDir directory. // Conventionally it is a symbolic link to the actual tools directory. func ToolsDir(dataDir, agentName string) string { return path.Join(dataDir, "tools", agentName) } // UnpackTools reads a set of juju tools in gzipped tar-archive // format and unpacks them into the appropriate tools directory // within dataDir. If a valid tools directory already exists, // UnpackTools returns without error. func UnpackTools(dataDir string, tools *coretools.Tools, r io.Reader) (err error) { // Unpack the gzip file and compute the checksum. sha256hash := sha256.New() zr, err := gzip.NewReader(io.TeeReader(r, sha256hash)) if err != nil { return err } defer zr.Close() f, err := ioutil.TempFile(os.TempDir(), "tools-tar") if err != nil { return err } _, err = io.Copy(f, zr) if err != nil { return err } defer os.Remove(f.Name()) // TODO(wallyworld) - 2013-09-24 bug=1229512 // When we can ensure all tools records have valid checksums recorded, // we can remove this test short circuit. gzipSHA256 := fmt.Sprintf("%x", sha256hash.Sum(nil)) if tools.SHA256 != "" && tools.SHA256 != gzipSHA256 { return fmt.Errorf("tarball sha256 mismatch, expected %s, got %s", tools.SHA256, gzipSHA256) } // Make a temporary directory in the tools directory, // first ensuring that the tools directory exists. toolsDir := path.Join(dataDir, "tools") err = os.MkdirAll(toolsDir, 0755) if err != nil { return err } dir, err := ioutil.TempDir(toolsDir, "unpacking-") if err != nil { return err } defer removeAll(dir) // Checksum matches, now reset the file and untar it. _, err = f.Seek(0, 0) if err != nil { return err } tr := tar.NewReader(f) for { hdr, err := tr.Next() if err == io.EOF { break } if err != nil { return err } if strings.ContainsAny(hdr.Name, "/\\") { return fmt.Errorf("bad name %q in tools archive", hdr.Name) } if hdr.Typeflag != tar.TypeReg { return fmt.Errorf("bad file type %c in file %q in tools archive", hdr.Typeflag, hdr.Name) } name := path.Join(dir, hdr.Name) if err := writeFile(name, os.FileMode(hdr.Mode&0777), tr); err != nil { return errgo.Annotatef(err, "tar extract %q failed", name) } } toolsMetadataData, err := json.Marshal(tools) if err != nil { return err } err = ioutil.WriteFile(path.Join(dir, toolsFile), []byte(toolsMetadataData), 0644) if err != nil { return err } // The tempdir is created with 0700, so we need to make it more // accessable for juju-run. err = os.Chmod(dir, 0755) if err != nil { return err } err = os.Rename(dir, SharedToolsDir(dataDir, tools.Version)) // If we've failed to rename the directory, it may be because // the directory already exists - if ReadTools succeeds, we // assume all's ok. if err != nil { if _, err := ReadTools(dataDir, tools.Version); err == nil { return nil } } return err } func removeAll(dir string) { err := os.RemoveAll(dir) if err == nil || os.IsNotExist(err) { return } logger.Warningf("cannot remove %q: %v", dir, err) } func writeFile(name string, mode os.FileMode, r io.Reader) error { f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) if err != nil { return err } defer f.Close() _, err = io.Copy(f, r) return err } // ReadTools checks that the tools information for the given version exists // in the dataDir directory, and returns a Tools instance. // The tools information is json encoded in a text file, "downloaded-tools.txt". func ReadTools(dataDir string, vers version.Binary) (*coretools.Tools, error) { dir := SharedToolsDir(dataDir, vers) toolsData, err := ioutil.ReadFile(path.Join(dir, toolsFile)) if err != nil { return nil, fmt.Errorf("cannot read tools metadata in tools directory: %v", err) } var tools coretools.Tools if err := json.Unmarshal(toolsData, &tools); err != nil { return nil, fmt.Errorf("invalid tools metadata in tools directory %q: %v", dir, err) } return &tools, nil } // ChangeAgentTools atomically replaces the agent-specific symlink // under dataDir so it points to the previously unpacked // version vers. It returns the new tools read. func ChangeAgentTools(dataDir string, agentName string, vers version.Binary) (*coretools.Tools, error) { tools, err := ReadTools(dataDir, vers) if err != nil { return nil, err } tmpName := ToolsDir(dataDir, "tmplink-"+agentName) err = os.Symlink(tools.Version.String(), tmpName) if err != nil { return nil, fmt.Errorf("cannot create tools symlink: %v", err) } err = os.Rename(tmpName, ToolsDir(dataDir, agentName)) if err != nil { return nil, fmt.Errorf("cannot update tools symlink: %v", err) } return tools, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/tools/tools.go�����������������������������������0000644�0000153�0000161�00000001375�12321735642�024620� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "io" "github.com/juju/loggo" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.agent.tools") // ToolsManager keeps track of a pool of tools type ToolsManager interface { // ReadTools looks in the current storage to see what tools are // available that match the given Binary version. ReadTools(version version.Binary) (*tools.Tools, error) // UnpackTools reads the compressed tarball from the io.Reader and // extracts the tools to be used. tools is used to indicate what exact // version are in the contents of the tarball UnpackTools(tools *tools.Tools, r io.Reader) error } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/tools/diskmanager.go�����������������������������0000644�0000153�0000161�00000001605�12321735642�025741� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "io" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) // DiskManager keeps track of a collections of Juju agent tools in a directory // structure on disk. type DiskManager struct { dataDir string } // NewDiskManager returns a DiskManager handling a given directory. // *DiskManager conforms to the ToolsManager interface func NewDiskManager(dataDir string) *DiskManager { return &DiskManager{dataDir: dataDir} } func (d *DiskManager) ReadTools(vers version.Binary) (*tools.Tools, error) { return ReadTools(d.dataDir, vers) } func (d *DiskManager) UnpackTools(tools *tools.Tools, r io.Reader) error { return UnpackTools(d.dataDir, tools, r) } func (d *DiskManager) SharedToolsDir(vers version.Binary) string { return SharedToolsDir(d.dataDir, vers) } ���������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/tools/package_test.go����������������������������0000644�0000153�0000161�00000000322�12321735642�026101� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/agent_test.go������������������������������������0000644�0000153�0000161�00000032400�12321735776�024456� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent_test import ( "reflect" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) type suite struct { testbase.LoggingSuite } var _ = gc.Suite(&suite{}) var agentConfigTests = []struct { about string params agent.AgentConfigParams checkErr string inspectConfig func(*gc.C, agent.Config) }{{ about: "missing data directory", checkErr: "data directory not found in configuration", }, { about: "missing tag", params: agent.AgentConfigParams{ DataDir: "/data/dir", }, checkErr: "entity tag not found in configuration", }, { about: "missing upgraded to version", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", }, checkErr: "upgradedToVersion not found in configuration", }, { about: "missing password", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", UpgradedToVersion: version.Current.Number, }, checkErr: "password not found in configuration", }, { about: "missing CA cert", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", }, checkErr: "CA certificate not found in configuration", }, { about: "need either state or api addresses", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", CACert: []byte("ca cert"), }, checkErr: "state or API addresses not found in configuration", }, { about: "invalid state address", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", CACert: []byte("ca cert"), StateAddresses: []string{"localhost:8080", "bad-address"}, }, checkErr: `invalid state server address "bad-address"`, }, { about: "invalid api address", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", CACert: []byte("ca cert"), APIAddresses: []string{"localhost:8080", "bad-address"}, }, checkErr: `invalid API server address "bad-address"`, }, { about: "good state addresses", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", CACert: []byte("ca cert"), StateAddresses: []string{"localhost:1234"}, }, }, { about: "good api addresses", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", CACert: []byte("ca cert"), APIAddresses: []string{"localhost:1234"}, }, }, { about: "both state and api addresses", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", CACert: []byte("ca cert"), StateAddresses: []string{"localhost:1234"}, APIAddresses: []string{"localhost:1235"}, }, }, { about: "everything...", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", Password: "sekrit", UpgradedToVersion: version.Current.Number, CACert: []byte("ca cert"), StateAddresses: []string{"localhost:1234"}, APIAddresses: []string{"localhost:1235"}, Nonce: "a nonce", }, }, { about: "missing logDir sets default", params: agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", Password: "sekrit", UpgradedToVersion: version.Current.Number, CACert: []byte("ca cert"), StateAddresses: []string{"localhost:1234"}, APIAddresses: []string{"localhost:1235"}, Nonce: "a nonce", }, inspectConfig: func(c *gc.C, cfg agent.Config) { c.Check(cfg.LogDir(), gc.Equals, agent.DefaultLogDir) }, }} func (*suite) TestNewAgentConfig(c *gc.C) { for i, test := range agentConfigTests { c.Logf("%v: %s", i, test.about) _, err := agent.NewAgentConfig(test.params) if test.checkErr == "" { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, test.checkErr) } } } func (*suite) TestMigrateConfig(c *gc.C) { initialParams := agent.AgentConfigParams{ DataDir: c.MkDir(), LogDir: c.MkDir(), Tag: "omg", Nonce: "nonce", Password: "secret", UpgradedToVersion: version.MustParse("1.16.5"), Jobs: []params.MachineJob{ params.JobManageEnviron, params.JobHostUnits, }, CACert: []byte("ca cert"), StateAddresses: []string{"localhost:1234"}, APIAddresses: []string{"localhost:4321"}, Values: map[string]string{ "key1": "value1", "key2": "value2", "key3": "value3", }, } migrateTests := []struct { comment string fields []string newParams agent.MigrateConfigParams expectValues map[string]string expectErr string }{{ comment: "nothing to change", fields: nil, newParams: agent.MigrateConfigParams{}, }, { fields: []string{"DataDir"}, newParams: agent.MigrateConfigParams{ DataDir: c.MkDir(), }, }, { fields: []string{"DataDir", "LogDir"}, newParams: agent.MigrateConfigParams{ DataDir: c.MkDir(), LogDir: c.MkDir(), }, }, { fields: []string{"Jobs"}, newParams: agent.MigrateConfigParams{ Jobs: []params.MachineJob{params.JobHostUnits}, }, }, { comment: "invalid/immutable field specified", fields: []string{"InvalidField"}, newParams: agent.MigrateConfigParams{}, expectErr: `unknown field "InvalidField"`, }, { comment: "Values can be added, changed or removed", fields: []string{"Values", "DeleteValues"}, newParams: agent.MigrateConfigParams{ DeleteValues: []string{"key2", "key3"}, // delete Values: map[string]string{ "key1": "new value1", // change "new key3": "value3", // add "empty": "", // add empty val }, }, expectValues: map[string]string{ "key1": "new value1", "new key3": "value3", "empty": "", }, }} for i, test := range migrateTests { summary := "migrate fields" if test.comment != "" { summary += " (" + test.comment + ") " } c.Logf("test %d: %s %v", i, summary, test.fields) initialConfig, err := agent.NewAgentConfig(initialParams) c.Check(err, gc.IsNil) newConfig, err := agent.NewAgentConfig(initialParams) c.Check(err, gc.IsNil) c.Check(initialConfig.Write(), gc.IsNil) c.Check(agent.ConfigFileExists(initialConfig), jc.IsTrue) err = agent.MigrateConfig(newConfig, test.newParams) c.Check(err, gc.IsNil) c.Check(agent.ConfigFileExists(newConfig), jc.IsTrue) if test.newParams.DataDir != "" { // If we're changing where the new config is saved, // we can verify the old config got removed. c.Check(agent.ConfigFileExists(initialConfig), jc.IsFalse) } // Make sure we can read it back successfully and it // matches what we wrote. configPath := agent.ConfigPath(newConfig.DataDir(), newConfig.Tag()) readConfig, err := agent.ReadConf(configPath) c.Check(err, gc.IsNil) c.Check(newConfig, jc.DeepEquals, readConfig) // Make sure only the specified fields were changed and // the rest matches. for _, field := range test.fields { switch field { case "Values": err = agent.PatchConfig(initialConfig, field, test.expectValues) c.Check(err, gc.IsNil) case "DeleteValues": err = agent.PatchConfig(initialConfig, field, test.newParams.DeleteValues) c.Check(err, gc.IsNil) default: value := reflect.ValueOf(test.newParams).FieldByName(field) if value.IsValid() && test.expectErr == "" { err = agent.PatchConfig(initialConfig, field, value.Interface()) c.Check(err, gc.IsNil) } else { err = agent.PatchConfig(initialConfig, field, value) c.Check(err, gc.ErrorMatches, test.expectErr) } } } c.Check(newConfig, jc.DeepEquals, initialConfig) } } func (*suite) TestNewStateMachineConfig(c *gc.C) { type testStruct struct { about string params agent.StateMachineConfigParams checkErr string inspectConfig func(*gc.C, agent.Config) } var tests = []testStruct{{ about: "missing state server cert", checkErr: "state server cert not found in configuration", }, { about: "missing state server key", params: agent.StateMachineConfigParams{ StateServerCert: []byte("server cert"), }, checkErr: "state server key not found in configuration", }} for _, test := range agentConfigTests { tests = append(tests, testStruct{ about: test.about, params: agent.StateMachineConfigParams{ StateServerCert: []byte("server cert"), StateServerKey: []byte("server key"), AgentConfigParams: test.params, }, checkErr: test.checkErr, }) } for i, test := range tests { c.Logf("%v: %s", i, test.about) cfg, err := agent.NewStateMachineConfig(test.params) if test.checkErr == "" { c.Assert(err, gc.IsNil) if test.inspectConfig != nil { test.inspectConfig(c, cfg) } } else { c.Assert(err, gc.ErrorMatches, test.checkErr) } } } var attributeParams = agent.AgentConfigParams{ DataDir: "/data/dir", Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", CACert: []byte("ca cert"), StateAddresses: []string{"localhost:1234"}, APIAddresses: []string{"localhost:1235"}, Nonce: "a nonce", } func (*suite) TestAttributes(c *gc.C) { conf, err := agent.NewAgentConfig(attributeParams) c.Assert(err, gc.IsNil) c.Assert(conf.DataDir(), gc.Equals, "/data/dir") c.Assert(conf.Tag(), gc.Equals, "omg") c.Assert(conf.Dir(), gc.Equals, "/data/dir/agents/omg") c.Assert(conf.Nonce(), gc.Equals, "a nonce") c.Assert(conf.UpgradedToVersion(), gc.DeepEquals, version.Current.Number) } func (s *suite) TestApiAddressesCantWriteBack(c *gc.C) { conf, err := agent.NewAgentConfig(attributeParams) c.Assert(err, gc.IsNil) value, err := conf.APIAddresses() c.Assert(err, gc.IsNil) c.Assert(value, gc.DeepEquals, []string{"localhost:1235"}) value[0] = "invalidAdr" //Check out change hasn't gone back into the internals newValue, err := conf.APIAddresses() c.Assert(err, gc.IsNil) c.Assert(newValue, gc.DeepEquals, []string{"localhost:1235"}) } func (*suite) TestWriteAndRead(c *gc.C) { testParams := attributeParams testParams.DataDir = c.MkDir() testParams.LogDir = c.MkDir() conf, err := agent.NewAgentConfig(testParams) c.Assert(err, gc.IsNil) c.Assert(conf.Write(), gc.IsNil) reread, err := agent.ReadConf(agent.ConfigPath(conf.DataDir(), conf.Tag())) c.Assert(err, gc.IsNil) c.Assert(reread, jc.DeepEquals, conf) } func (*suite) TestWriteNewPassword(c *gc.C) { for i, test := range []struct { about string params agent.AgentConfigParams }{{ about: "good state addresses", params: agent.AgentConfigParams{ DataDir: c.MkDir(), Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", CACert: []byte("ca cert"), StateAddresses: []string{"localhost:1234"}, }, }, { about: "good api addresses", params: agent.AgentConfigParams{ DataDir: c.MkDir(), Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", CACert: []byte("ca cert"), APIAddresses: []string{"localhost:1234"}, }, }, { about: "both state and api addresses", params: agent.AgentConfigParams{ DataDir: c.MkDir(), Tag: "omg", UpgradedToVersion: version.Current.Number, Password: "sekrit", CACert: []byte("ca cert"), StateAddresses: []string{"localhost:1234"}, APIAddresses: []string{"localhost:1235"}, }, }} { c.Logf("%v: %s", i, test.about) conf, err := agent.NewAgentConfig(test.params) c.Assert(err, gc.IsNil) newPass, err := agent.WriteNewPassword(conf) c.Assert(err, gc.IsNil) // Show that the password is saved. reread, err := agent.ReadConf(agent.ConfigPath(conf.DataDir(), conf.Tag())) c.Assert(agent.Password(conf), gc.Equals, agent.Password(reread)) c.Assert(newPass, gc.Equals, agent.Password(conf)) } } func (*suite) TestWriteUpgradedToVersion(c *gc.C) { testParams := attributeParams testParams.DataDir = c.MkDir() conf, err := agent.NewAgentConfig(testParams) c.Assert(err, gc.IsNil) c.Assert(conf.Write(), gc.IsNil) newVersion := version.Current.Number newVersion.Major++ c.Assert(conf.WriteUpgradedToVersion(newVersion), gc.IsNil) c.Assert(conf.UpgradedToVersion(), gc.DeepEquals, newVersion) // Show that the upgradedToVersion is saved. reread, err := agent.ReadConf(agent.ConfigPath(conf.DataDir(), conf.Tag())) c.Assert(reread, jc.DeepEquals, conf) } // Actual opening of state and api requires a lot more boiler plate to make // sure they are valid connections. This is done in the cmd/jujud tests for // bootstrap, machine and unit tests. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/agent/format.go����������������������������������������0000644�0000153�0000161�00000007003�12321735642�023602� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent import ( "bytes" "fmt" "strings" "launchpad.net/juju-core/utils" ) // Current agent config format is defined as follows: // # format <version>\n (very first line; <version> is 1.18 or later) // <config-encoded-as-yaml> // All of this is saved in a single agent.conf file. // // Historically the format file in the agent config directory was used // to identify the method of serialization. This was used by // individual legacy (pre 1.18) format readers and writers to be able // to translate from the file format to the in-memory structure. From // version 1.18, the format is part of the agent configuration file, // so there is only a single source of truth. // // Juju only supports upgrading from single steps, so Juju only needs // to know about the current format and the format of the previous // stable release. For convenience, the format name includes the // version number of the stable release that it will be released with. // Once this release has happened, the format should be considered // FIXED, and should no longer be modified. If changes are necessary // to the format, a new format should be created. // // We don't need to create new formats for each release, the version // number is just a convenience for us to know which stable release // introduced that format. var formats = make(map[string]formatter) // The formatter defines the two methods needed by the formatters for // translating to and from the internal, format agnostic, structure. type formatter interface { version() string unmarshal(data []byte) (*configInternal, error) } func registerFormat(format formatter) { formats[format.version()] = format } // Once a new format version is introduced: // - Create a formatter for the new version (including a marshal() method); // - Call registerFormat in the new format's init() function. // - Change this to point to the new format; // - Remove the marshal() method from the old format; // currentFormat holds the current agent config version's formatter. var currentFormat = format_1_18 // agentConfigFilename is the default file name of used for the agent // config. const agentConfigFilename = "agent.conf" // formatPrefix is prefix of the first line in an agent config file. const formatPrefix = "# format " func writeFileCommands(filename string, contents []byte, permission int) []string { quotedFilename := utils.ShQuote(filename) quotedContents := utils.ShQuote(string(contents)) return []string{ fmt.Sprintf("install -m %o /dev/null %s", permission, quotedFilename), fmt.Sprintf(`printf '%%s\n' %s > %s`, quotedContents, quotedFilename), } } func getFormatter(version string) (formatter, error) { version = strings.TrimSpace(version) format, ok := formats[version] if !ok { return nil, fmt.Errorf("unknown agent config format %q", version) } return format, nil } func parseConfigData(data []byte) (formatter, *configInternal, error) { i := bytes.IndexByte(data, '\n') if i == -1 { return nil, nil, fmt.Errorf("invalid agent config format: %s", string(data)) } version, configData := string(data[0:i]), data[i+1:] if !strings.HasPrefix(version, formatPrefix) { return nil, nil, fmt.Errorf("malformed agent config format %q", version) } version = strings.TrimPrefix(version, formatPrefix) format, err := getFormatter(version) if err != nil { return nil, nil, err } config, err := format.unmarshal(configData) if err != nil { return nil, nil, err } return format, config, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/tools/�������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022012� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/tools/list_test.go�������������������������������������0000644�0000153�0000161�00000016016�12321735642�024372� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) type ListSuite struct{} var _ = gc.Suite(&ListSuite{}) func mustParseTools(name string) *tools.Tools { return &tools.Tools{ Version: version.MustParseBinary(name), URL: "http://testing.invalid/" + name, } } func extend(lists ...tools.List) tools.List { var result tools.List for _, list := range lists { result = append(result, list...) } return result } var ( t100precise = mustParseTools("1.0.0-precise-amd64") t100precise32 = mustParseTools("1.0.0-precise-i386") t100quantal = mustParseTools("1.0.0-quantal-amd64") t100quantal32 = mustParseTools("1.0.0-quantal-i386") t100all = tools.List{ t100precise, t100precise32, t100quantal, t100quantal32, } t190precise = mustParseTools("1.9.0-precise-amd64") t190precise32 = mustParseTools("1.9.0-precise-i386") t190quantal = mustParseTools("1.9.0-quantal-amd64") t190all = tools.List{ t190precise, t190precise32, t190quantal, } t200precise = mustParseTools("2.0.0-precise-amd64") t200quantal32 = mustParseTools("2.0.0-quantal-i386") t200all = tools.List{ t200precise, t200quantal32, } t2001precise = mustParseTools("2.0.0.1-precise-amd64") tAllBefore210 = extend(t100all, t190all, append(t200all, t2001precise)) t210precise = mustParseTools("2.1.0-precise-amd64") t211precise = mustParseTools("2.1.1-precise-amd64") t215precise = mustParseTools("2.1.5-precise-amd64") t2152precise = mustParseTools("2.1.5.2-precise-amd64") t210all = tools.List{t210precise, t211precise, t215precise, t2152precise} ) type stringsTest struct { src tools.List expect []string } var seriesTests = []stringsTest{{ src: tools.List{t100precise}, expect: []string{"precise"}, }, { src: tools.List{t100precise, t100precise32, t200precise}, expect: []string{"precise"}, }, { src: tAllBefore210, expect: []string{"precise", "quantal"}, }} func (s *ListSuite) TestSeries(c *gc.C) { for i, test := range seriesTests { c.Logf("test %d", i) c.Check(test.src.AllSeries(), gc.DeepEquals, test.expect) if len(test.expect) == 1 { c.Check(test.src.OneSeries(), gc.Equals, test.expect[0]) } } } var archesTests = []stringsTest{{ src: tools.List{t100precise}, expect: []string{"amd64"}, }, { src: tools.List{t100precise, t100quantal, t200precise}, expect: []string{"amd64"}, }, { src: tAllBefore210, expect: []string{"amd64", "i386"}, }} func (s *ListSuite) TestArches(c *gc.C) { for i, test := range archesTests { c.Logf("test %d", i) c.Check(test.src.Arches(), gc.DeepEquals, test.expect) } } func (s *ListSuite) TestURLs(c *gc.C) { empty := tools.List{} c.Check(empty.URLs(), gc.DeepEquals, map[version.Binary]string{}) full := tools.List{t100precise, t190quantal, t2001precise} c.Check(full.URLs(), gc.DeepEquals, map[version.Binary]string{ t100precise.Version: t100precise.URL, t190quantal.Version: t190quantal.URL, t2001precise.Version: t2001precise.URL, }) } var newestTests = []struct { src tools.List expect tools.List number version.Number }{{ src: nil, expect: nil, number: version.Zero, }, { src: tools.List{t100precise}, expect: tools.List{t100precise}, number: version.MustParse("1.0.0"), }, { src: t100all, expect: t100all, number: version.MustParse("1.0.0"), }, { src: extend(t100all, t190all, t200all), expect: t200all, number: version.MustParse("2.0.0"), }, { src: tAllBefore210, expect: tools.List{t2001precise}, number: version.MustParse("2.0.0.1"), }} func (s *ListSuite) TestNewest(c *gc.C) { for i, test := range newestTests { c.Logf("test %d", i) number, actual := test.src.Newest() c.Check(number, gc.DeepEquals, test.number) c.Check(actual, gc.DeepEquals, test.expect) } } var newestCompatibleTests = []struct { src tools.List base version.Number expect version.Number found bool }{{ src: nil, base: version.Zero, expect: version.Zero, found: false, }, { src: tools.List{t100precise}, base: version.Zero, expect: version.Zero, found: false, }, { src: t100all, base: version.MustParse("1.0.0"), expect: version.MustParse("1.0.0"), found: true, }, { src: tAllBefore210, base: version.MustParse("2.0.0"), expect: version.MustParse("2.0.0.1"), found: true, }, { src: tAllBefore210, base: version.MustParse("1.9.0"), expect: version.MustParse("1.9.0"), found: true, }, { src: t210all, base: version.MustParse("2.1.1"), expect: version.MustParse("2.1.5.2"), found: true, }} func (s *ListSuite) TestNewestCompatible(c *gc.C) { for i, test := range newestCompatibleTests { c.Logf("test %d", i) actual, found := test.src.NewestCompatible(test.base) c.Check(actual, gc.DeepEquals, test.expect) c.Check(found, gc.Equals, test.found) } } var excludeTests = []struct { src tools.List arg tools.List expect tools.List }{{ nil, tools.List{t100precise}, nil, }, { tools.List{t100precise}, nil, tools.List{t100precise}, }, { tools.List{t100precise}, tools.List{t100precise}, nil, }, { nil, tAllBefore210, nil, }, { tAllBefore210, nil, tAllBefore210, }, { tAllBefore210, tAllBefore210, nil, }, { t100all, tools.List{t100precise}, tools.List{t100precise32, t100quantal, t100quantal32}, }, { t100all, tools.List{t100precise32, t100quantal, t100quantal32}, tools.List{t100precise}, }, { t100all, t190all, t100all, }, { t190all, t100all, t190all, }, { extend(t100all, t190all), t190all, t100all, }} func (s *ListSuite) TestExclude(c *gc.C) { for i, test := range excludeTests { c.Logf("test %d", i) c.Check(test.src.Exclude(test.arg), gc.DeepEquals, test.expect) } } var matchTests = []struct { src tools.List filter tools.Filter expect tools.List }{{ tools.List{t100precise}, tools.Filter{}, tools.List{t100precise}, }, { tAllBefore210, tools.Filter{}, tAllBefore210, }, { tAllBefore210, tools.Filter{Released: true}, extend(t100all, t200all), }, { t190all, tools.Filter{Released: true}, nil, }, { tAllBefore210, tools.Filter{Number: version.MustParse("1.9.0")}, t190all, }, { tAllBefore210, tools.Filter{Number: version.MustParse("1.9.0.1")}, nil, }, { tAllBefore210, tools.Filter{Series: "quantal"}, tools.List{t100quantal, t100quantal32, t190quantal, t200quantal32}, }, { tAllBefore210, tools.Filter{Series: "raring"}, nil, }, { tAllBefore210, tools.Filter{Arch: "i386"}, tools.List{t100precise32, t100quantal32, t190precise32, t200quantal32}, }, { tAllBefore210, tools.Filter{Arch: "arm"}, nil, }, { tAllBefore210, tools.Filter{ Released: true, Number: version.MustParse("2.0.0"), Series: "quantal", Arch: "i386", }, tools.List{t200quantal32}, }} func (s *ListSuite) TestMatch(c *gc.C) { for i, test := range matchTests { c.Logf("test %d", i) actual, err := test.src.Match(test.filter) c.Check(actual, gc.DeepEquals, test.expect) if len(test.expect) > 0 { c.Check(err, gc.IsNil) } else { c.Check(err, gc.Equals, tools.ErrNoMatches) } } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/tools/marshal_test.go����������������������������������0000644�0000153�0000161�00000002645�12321735642�025051� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "labix.org/v2/mgo/bson" gc "launchpad.net/gocheck" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) var _ = gc.Suite(&marshalSuite{}) type marshalSuite struct { } func newTools(vers, url string) *tools.Tools { return &tools.Tools{ Version: version.MustParseBinary(vers), URL: url, Size: 10, SHA256: "1234", } } func (s *marshalSuite) TestMarshalUnmarshal(c *gc.C) { testTools := newTools("7.8.9-foo-bar", "http://arble.tgz") data, err := bson.Marshal(&testTools) c.Assert(err, gc.IsNil) // Check the exact document. want := bson.M{ "version": testTools.Version.String(), "url": testTools.URL, "size": testTools.Size, "sha256": testTools.SHA256, } got := bson.M{} err = bson.Unmarshal(data, &got) c.Assert(err, gc.IsNil) c.Assert(got, gc.DeepEquals, want) // Check that it unpacks properly too. var t tools.Tools err = bson.Unmarshal(data, &t) c.Assert(err, gc.IsNil) c.Assert(t, gc.Equals, *testTools) } func (s *marshalSuite) TestUnmarshalNilRoundtrip(c *gc.C) { // We have a custom unmarshaller that should keep // the field unset when it finds a nil value. var v struct{ Tools *tools.Tools } data, err := bson.Marshal(&v) c.Assert(err, gc.IsNil) err = bson.Unmarshal(data, &v) c.Assert(err, gc.IsNil) c.Assert(v.Tools, gc.IsNil) } �������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/tools/tools.go�����������������������������������������0000644�0000153�0000161�00000001077�12321735776�023531� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "github.com/juju/loggo" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.tools") // Tools represents the location and version of a tools tarball. type Tools struct { Version version.Binary `json:"version"` URL string `json:"url"` SHA256 string `json:"sha256,omitempty"` Size int64 `json:"size"` } // HasTools instances can be asked for a tools list. type HasTools interface { Tools() List } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/tools/package_test.go����������������������������������0000644�0000153�0000161�00000000322�12321735642�025003� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/tools/marshal.go���������������������������������������0000644�0000153�0000161�00000001627�12321735642�024011� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "labix.org/v2/mgo/bson" "launchpad.net/juju-core/version" ) type toolsDoc struct { Version version.Binary URL string Size int64 SHA256 string } // GetBSON returns the structure to be serialized for the tools as a generic // interface. func (t *Tools) GetBSON() (interface{}, error) { if t == nil { return nil, nil } return &toolsDoc{t.Version, t.URL, t.Size, t.SHA256}, nil } // SetBSON updates the internal members with the data stored in the bson.Raw // parameter. func (t *Tools) SetBSON(raw bson.Raw) error { if raw.Kind == 10 { // Preserve the nil value in that case. return bson.SetZero } var doc toolsDoc if err := raw.Unmarshal(&doc); err != nil { return err } t.Version = doc.Version t.URL = doc.URL t.Size = doc.Size t.SHA256 = doc.SHA256 return nil } ���������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/tools/list.go������������������������������������������0000644�0000153�0000161�00000011040�12321735642�023323� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tools import ( "errors" "fmt" "strings" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/version" ) // List holds tools available in an environment. The order of tools within // a List is not significant. type List []*Tools var ErrNoMatches = errors.New("no matching tools available") // String returns the versions of the tools in src, separated by semicolons. func (src List) String() string { names := make([]string, len(src)) for i, tools := range src { names[i] = tools.Version.String() } return strings.Join(names, ";") } // AllSeries returns all series for which some tools in src were built. func (src List) AllSeries() []string { return src.collect(func(tools *Tools) string { return tools.Version.Series }) } // OneSeries returns the single series for which all tools in src were built. func (src List) OneSeries() string { series := src.AllSeries() if len(series) != 1 { panic(fmt.Errorf("should have gotten tools for one series, got %v", series)) } return series[0] } // Arches returns all architectures for which some tools in src were built. func (src List) Arches() []string { return src.collect(func(tools *Tools) string { return tools.Version.Arch }) } // collect calls f on all values in src and returns an alphabetically // ordered list of the returned results without duplicates. func (src List) collect(f func(*Tools) string) []string { var seen set.Strings for _, tools := range src { seen.Add(f(tools)) } return seen.SortedValues() } // URLs returns download URLs for the tools in src, keyed by binary version. func (src List) URLs() map[version.Binary]string { result := map[version.Binary]string{} for _, tools := range src { result[tools.Version] = tools.URL } return result } // Newest returns the greatest version in src, and the tools with that version. func (src List) Newest() (version.Number, List) { var result List var best version.Number for _, tools := range src { if best.Compare(tools.Version.Number) < 0 { // Found new best number; reset result list. best = tools.Version.Number result = append(result[:0], tools) } else if tools.Version.Number == best { result = append(result, tools) } } return best, result } // NewestCompatible returns the most recent version compatible with // base, i.e. with the same major and minor numbers and greater or // equal patch and build numbers. func (src List) NewestCompatible(base version.Number) (newest version.Number, found bool) { newest = base found = false for _, tool := range src { toolVersion := tool.Version.Number if newest == toolVersion { found = true } else if newest.Compare(toolVersion) < 0 && toolVersion.Major == newest.Major && toolVersion.Minor == newest.Minor { newest = toolVersion found = true } } return newest, found } // Difference returns the tools in src that are not in excluded. func (src List) Exclude(excluded List) List { ignore := make(map[version.Binary]bool, len(excluded)) for _, tool := range excluded { ignore[tool.Version] = true } var result List for _, tool := range src { if !ignore[tool.Version] { result = append(result, tool) } } return result } // Match returns a List, derived from src, containing only those tools that // match the supplied Filter. If no tools match, it returns ErrNoMatches. func (src List) Match(f Filter) (List, error) { var result List for _, tools := range src { if f.match(tools) { result = append(result, tools) } } if len(result) == 0 { logger.Errorf("cannot match %#v", f) return nil, ErrNoMatches } return result, nil } // Filter holds criteria for choosing tools. type Filter struct { // Release, if true, causes the filter to match only tools with a // non-development version number. Released bool // Number, if non-zero, causes the filter to match only tools with // that exact version number. Number version.Number // Series, if not empty, causes the filter to match only tools with // that series. Series string // Arch, if not empty, causes the filter to match only tools with // that architecture. Arch string } // match returns true if the supplied tools match f. func (f Filter) match(tools *Tools) bool { if f.Released && tools.Version.IsDev() { return false } if f.Number != version.Zero && tools.Version.Number != f.Number { return false } if f.Series != "" && tools.Version.Series != f.Series { return false } if f.Arch != "" && tools.Version.Arch != f.Arch { return false } return true } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/���������������������������������������������������0000755�0000153�0000161�00000000000�12321735643�021452� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/reflect_test.go������������������������������������0000644�0000153�0000161�00000010502�12321735642�024461� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rpc_test import ( "reflect" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/rpc/rpcreflect" "launchpad.net/juju-core/testing/testbase" ) // We test rpcreflect in this package, so that the // tests can all share the same testing Root type. type reflectSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&reflectSuite{}) func (*reflectSuite) TestTypeOf(c *gc.C) { rtype := rpcreflect.TypeOf(reflect.TypeOf(&Root{})) c.Assert(rtype.DiscardedMethods(), gc.DeepEquals, []string{ "Discard1", "Discard2", "Discard3", }) expect := map[string]reflect.Type{ "CallbackMethods": reflect.TypeOf(&CallbackMethods{}), "ChangeAPIMethods": reflect.TypeOf(&ChangeAPIMethods{}), "DelayedMethods": reflect.TypeOf(&DelayedMethods{}), "ErrorMethods": reflect.TypeOf(&ErrorMethods{}), "InterfaceMethods": reflect.TypeOf((*InterfaceMethods)(nil)).Elem(), "SimpleMethods": reflect.TypeOf(&SimpleMethods{}), } c.Assert(rtype.MethodNames(), gc.HasLen, len(expect)) for name, expectGoType := range expect { m, err := rtype.Method(name) c.Assert(err, gc.IsNil) c.Assert(m, gc.NotNil) c.Assert(m.Call, gc.NotNil) c.Assert(m.ObjType, gc.Equals, rpcreflect.ObjTypeOf(expectGoType)) c.Assert(m.ObjType.GoType(), gc.Equals, expectGoType) } m, err := rtype.Method("not found") c.Assert(err, gc.Equals, rpcreflect.ErrMethodNotFound) c.Assert(m, gc.DeepEquals, rpcreflect.RootMethod{}) } func (*reflectSuite) TestObjTypeOf(c *gc.C) { objType := rpcreflect.ObjTypeOf(reflect.TypeOf(&SimpleMethods{})) c.Check(objType.DiscardedMethods(), gc.DeepEquals, []string{ "Discard1", "Discard2", "Discard3", "Discard4", }) expect := map[string]*rpcreflect.ObjMethod{ "SliceArg": { Params: reflect.TypeOf(struct{ X []string }{}), Result: reflect.TypeOf(stringVal{}), }, } for narg := 0; narg < 2; narg++ { for nret := 0; nret < 2; nret++ { for nerr := 0; nerr < 2; nerr++ { retErr := nerr != 0 var m rpcreflect.ObjMethod if narg > 0 { m.Params = reflect.TypeOf(stringVal{}) } if nret > 0 { m.Result = reflect.TypeOf(stringVal{}) } expect[callName(narg, nret, retErr)] = &m } } } c.Assert(objType.MethodNames(), gc.HasLen, len(expect)) for name, expectMethod := range expect { m, err := objType.Method(name) c.Check(err, gc.IsNil) c.Assert(m, gc.NotNil) c.Check(m.Call, gc.NotNil) c.Check(m.Params, gc.Equals, expectMethod.Params) c.Check(m.Result, gc.Equals, expectMethod.Result) } m, err := objType.Method("not found") c.Check(err, gc.Equals, rpcreflect.ErrMethodNotFound) c.Check(m, gc.DeepEquals, rpcreflect.ObjMethod{}) } func (*reflectSuite) TestValueOf(c *gc.C) { v := rpcreflect.ValueOf(reflect.ValueOf(nil)) c.Check(v.IsValid(), jc.IsFalse) c.Check(func() { v.MethodCaller("foo", "bar") }, gc.PanicMatches, "MethodCaller called on invalid Value") root := &Root{} v = rpcreflect.ValueOf(reflect.ValueOf(root)) c.Check(v.IsValid(), jc.IsTrue) c.Check(v.GoValue().Interface(), gc.Equals, root) } func (*reflectSuite) TestMethodCaller(c *gc.C) { // MethodCaller is actually extensively tested because it's // used in the implementation of the rpc server, // so just a simple sanity check test here. root := &Root{ simple: make(map[string]*SimpleMethods), } root.simple["a99"] = &SimpleMethods{root: root, id: "a99"} v := rpcreflect.ValueOf(reflect.ValueOf(root)) m, err := v.MethodCaller("foo", "bar") c.Assert(err, gc.ErrorMatches, `unknown object type "foo"`) c.Assert(err, gc.FitsTypeOf, (*rpcreflect.CallNotImplementedError)(nil)) c.Assert(m, gc.DeepEquals, rpcreflect.MethodCaller{}) m, err = v.MethodCaller("SimpleMethods", "bar") c.Assert(err, gc.ErrorMatches, "no such request - method SimpleMethods.bar is not implemented") c.Assert(err, gc.FitsTypeOf, (*rpcreflect.CallNotImplementedError)(nil)) c.Assert(m, gc.DeepEquals, rpcreflect.MethodCaller{}) m, err = v.MethodCaller("SimpleMethods", "Call1r1e") c.Assert(err, gc.IsNil) c.Assert(m.ParamsType, gc.Equals, reflect.TypeOf(stringVal{})) c.Assert(m.ResultType, gc.Equals, reflect.TypeOf(stringVal{})) ret, err := m.Call("a99", reflect.ValueOf(stringVal{"foo"})) c.Assert(err, gc.IsNil) c.Assert(ret.Interface(), gc.Equals, stringVal{"Call1r1e ret"}) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/server.go������������������������������������������0000644�0000153�0000161�00000036616�12321735642�023322� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rpc import ( "fmt" "io" "reflect" "sync" "time" "github.com/juju/loggo" "launchpad.net/juju-core/rpc/rpcreflect" ) const CodeNotImplemented = "not implemented" var logger = loggo.GetLogger("juju.rpc") // A Codec implements reading and writing of messages in an RPC // session. The RPC code calls WriteMessage to write a message to the // connection and calls ReadHeader and ReadBody in pairs to read // messages. type Codec interface { // ReadHeader reads a message header into hdr. ReadHeader(hdr *Header) error // ReadBody reads a message body into the given body value. The // isRequest parameter specifies whether the message being read // is a request; if not, it's a response. The body value will // be a non-nil struct pointer, or nil to signify that the body // should be read and discarded. ReadBody(body interface{}, isRequest bool) error // WriteMessage writes a message with the given header and body. // The body will always be a struct. It may be called concurrently // with ReadHeader and ReadBody, but will not be called // concurrently with itself. WriteMessage(hdr *Header, body interface{}) error // Close closes the codec. It may be called concurrently // and should cause the Read methods to unblock. Close() error } // Header is a header written before every RPC call. Since RPC requests // can be initiated from either side, the header may represent a request // from the other side or a response to an outstanding request. type Header struct { // RequestId holds the sequence number of the request. // For replies, it holds the sequence number of the request // that is being replied to. RequestId uint64 // Request holds the action to invoke. Request Request // Error holds the error, if any. Error string // ErrorCode holds the code of the error, if any. ErrorCode string } // Request represents an RPC to be performed, absent its parameters. type Request struct { // Type holds the type of object to act on. Type string // Id holds the id of the object to act on. Id string // Action holds the action to perform on the object. Action string } // IsRequest returns whether the header represents an RPC request. If // it is not a request, it is a response. func (hdr *Header) IsRequest() bool { return hdr.Request.Type != "" || hdr.Request.Action != "" } // Note that we use "client request" and "server request" to name // requests initiated locally and remotely respectively. // Conn represents an RPC endpoint. It can both initiate and receive // RPC requests. There may be multiple outstanding Calls associated // with a single Client, and a Client may be used by multiple goroutines // simultaneously. type Conn struct { // codec holds the underlying RPC connection. codec Codec // notifier is informed about RPC requests. It may be nil. notifier RequestNotifier // srvPending represents the current server requests. srvPending sync.WaitGroup // sending guards the write side of the codec - it ensures // that codec.WriteMessage is not called concurrently. // It also guards shutdown. sending sync.Mutex // mutex guards the following values. mutex sync.Mutex // rootValue holds the value to use to serve RPC requests, if any. rootValue rpcreflect.Value // transformErrors is used to transform returned errors. transformErrors func(error) error // reqId holds the latest client request id. reqId uint64 // clientPending holds all pending client requests. clientPending map[uint64]*Call // closing is set when the connection is shutting down via // Close. When this is set, no more client or server requests // will be initiated. closing bool // shutdown is set when the input loop terminates. When this // is set, no more client requests will be sent to the server. shutdown bool // dead is closed when the input loop terminates. dead chan struct{} // inputLoopError holds the error that caused the input loop to // terminate prematurely. It is set before dead is closed. inputLoopError error } // RequestNotifier can be implemented to find out about requests // occurring in an RPC conn, for example to print requests for logging // purposes. The calls should not block or interact with the Conn object // as that can cause delays to the RPC server or deadlock. // Note that the methods on RequestNotifier may // be called concurrently. type RequestNotifier interface { // ServerRequest informs the RequestNotifier of a request made // to the Conn. If the request was not recognized or there was // an error reading the body, body will be nil. // // ServerRequest is called just before the server method // is invoked. ServerRequest(hdr *Header, body interface{}) // ServerReply informs the RequestNotifier of a reply sent to a // server request. The given Request gives details of the call // that was made; the given Header and body are the header and // body sent as reply. // // ServerReply is called just before the reply is written. ServerReply(req Request, hdr *Header, body interface{}, timeSpent time.Duration) // ClientRequest informs the RequestNotifier of a request // made from the Conn. It is called just before the request is // written. ClientRequest(hdr *Header, body interface{}) // ClientReply informs the RequestNotifier of a reply received // to a request. If the reply was to an unrecognised request, // the Request will be zero-valued. If the reply contained an // error, body will be nil; otherwise body will be the value that // was passed to the Conn.Call method. // // ClientReply is called just before the reply is handed to // back to the caller. ClientReply(req Request, hdr *Header, body interface{}) } // NewConn creates a new connection that uses the given codec for // transport, but it does not start it. Conn.Start must be called before // any requests are sent or received. If notifier is non-nil, the // appropriate method will be called for every RPC request. func NewConn(codec Codec, notifier RequestNotifier) *Conn { return &Conn{ codec: codec, clientPending: make(map[uint64]*Call), notifier: notifier, } } // Start starts the RPC connection running. It must be called at least // once for any RPC connection (client or server side) It has no effect // if it has already been called. By default, a connection serves no // methods. See Conn.Serve for a description of how to serve methods on // a Conn. func (conn *Conn) Start() { conn.mutex.Lock() defer conn.mutex.Unlock() if conn.dead == nil { conn.dead = make(chan struct{}) go conn.input() } } // Serve serves RPC requests on the connection by invoking methods on // root. Note that it does not start the connection running, // though it may be called once the connection is already started. // // The server executes each client request by calling a method on root // to obtain an object to act on; then it invokes an method on that // object with the request parameters, possibly returning some result. // // Methods on the root value are of the form: // // M(id string) (O, error) // // where M is an exported name, conventionally naming the object type, // id is some identifier for the object and O is the type of the // returned object. // // Methods defined on O may defined in one of the following forms, where // T and R must be struct types. // // Method() // Method() R // Method() (R, error) // Method() error // Method(T) // Method(T) R // Method(T) (R, error) // Method(T) error // // If transformErrors is non-nil, it will be called on all returned // non-nil errors, for example to transform the errors into ServerErrors // with specified codes. There will be a panic if transformErrors // returns nil. // // Serve may be called at any time on a connection to change the // set of methods being served by the connection. This will have // no effect on calls that are currently being services. // If root is nil, the connection will serve no methods. func (conn *Conn) Serve(root interface{}, transformErrors func(error) error) { rootValue := rpcreflect.ValueOf(reflect.ValueOf(root)) if rootValue.IsValid() && transformErrors == nil { transformErrors = func(err error) error { return err } } conn.mutex.Lock() defer conn.mutex.Unlock() conn.rootValue = rootValue conn.transformErrors = transformErrors } // Dead returns a channel that is closed when the connection // has been closed or the underlying transport has received // an error. There may still be outstanding requests. // Dead must be called after conn.Start has been called. func (conn *Conn) Dead() <-chan struct{} { return conn.dead } // Close closes the connection and its underlying codec; it returns when // all requests have been terminated. // // If the connection is serving requests, and the root value implements // the Killer interface, its Kill method will be called. The codec will // then be closed only when all its outstanding server calls have // completed. // // Calling Close multiple times is not an error. func (conn *Conn) Close() error { conn.mutex.Lock() if conn.closing { conn.mutex.Unlock() // Golang's net/rpc returns rpc.ErrShutdown if you ask to close // a closing or shutdown connection. Our choice is that Close // is an idempotent way to ask for resources to be released and // isn't a failure if called multiple times. return nil } conn.closing = true // Kill server requests if appropriate. Client requests will be // terminated when the input loop finishes. if conn.rootValue.IsValid() { if killer, ok := conn.rootValue.GoValue().Interface().(Killer); ok { killer.Kill() } } conn.mutex.Unlock() // Wait for any outstanding server requests to complete // and write their replies before closing the codec. conn.srvPending.Wait() // Closing the codec should cause the input loop to terminate. if err := conn.codec.Close(); err != nil { logger.Infof("error closing codec: %v", err) } <-conn.dead return conn.inputLoopError } // ErrorCoder represents an any error that has an associated // error code. An error code is a short string that represents the // kind of an error. type ErrorCoder interface { ErrorCode() string } // Killer represents a type that can be asked to abort any outstanding // requests. The Kill method should return immediately. type Killer interface { Kill() } // input reads messages from the connection and handles them // appropriately. func (conn *Conn) input() { err := conn.loop() conn.sending.Lock() defer conn.sending.Unlock() conn.mutex.Lock() defer conn.mutex.Unlock() if conn.closing || err == io.EOF { err = ErrShutdown } else { // Make the error available for Conn.Close to see. conn.inputLoopError = err } // Terminate all client requests. for _, call := range conn.clientPending { call.Error = err call.done() } conn.clientPending = nil conn.shutdown = true close(conn.dead) } // loop implements the looping part of Conn.input. func (conn *Conn) loop() error { var hdr Header for { hdr = Header{} err := conn.codec.ReadHeader(&hdr) if err != nil { return err } if hdr.IsRequest() { err = conn.handleRequest(&hdr) } else { err = conn.handleResponse(&hdr) } if err != nil { return err } } } func (conn *Conn) readBody(resp interface{}, isRequest bool) error { if resp == nil { resp = &struct{}{} } return conn.codec.ReadBody(resp, isRequest) } func (conn *Conn) handleRequest(hdr *Header) error { startTime := time.Now() req, err := conn.bindRequest(hdr) if err != nil { if conn.notifier != nil { conn.notifier.ServerRequest(hdr, nil) } if err := conn.readBody(nil, true); err != nil { return err } // We don't transform the error because there // may be no transformErrors function available. return conn.writeErrorResponse(hdr, err, startTime) } var argp interface{} var arg reflect.Value if req.ParamsType != nil { v := reflect.New(req.ParamsType) arg = v.Elem() argp = v.Interface() } if err := conn.readBody(argp, true); err != nil { if conn.notifier != nil { conn.notifier.ServerRequest(hdr, nil) } // If we get EOF, we know the connection is a // goner, so don't try to respond. if err == io.EOF || err == io.ErrUnexpectedEOF { return err } // An error reading the body often indicates bad // request parameters rather than an issue with // the connection itself, so we reply with an // error rather than tearing down the connection // unless it's obviously a connection issue. If // the error is actually a framing or syntax // problem, then the next ReadHeader should pick // up the problem and abort. return conn.writeErrorResponse(hdr, req.transformErrors(err), startTime) } if conn.notifier != nil { if req.ParamsType != nil { conn.notifier.ServerRequest(hdr, arg.Interface()) } else { conn.notifier.ServerRequest(hdr, struct{}{}) } } conn.mutex.Lock() closing := conn.closing if !closing { conn.srvPending.Add(1) go conn.runRequest(req, arg, startTime) } conn.mutex.Unlock() if closing { // We're closing down - no new requests may be initiated. return conn.writeErrorResponse(hdr, req.transformErrors(ErrShutdown), startTime) } return nil } func (conn *Conn) writeErrorResponse(reqHdr *Header, err error, startTime time.Time) error { conn.sending.Lock() defer conn.sending.Unlock() hdr := &Header{ RequestId: reqHdr.RequestId, } if err, ok := err.(ErrorCoder); ok { hdr.ErrorCode = err.ErrorCode() } else { hdr.ErrorCode = "" } hdr.Error = err.Error() if conn.notifier != nil { conn.notifier.ServerReply(reqHdr.Request, hdr, struct{}{}, time.Since(startTime)) } return conn.codec.WriteMessage(hdr, struct{}{}) } // boundRequest represents an RPC request that is // bound to an actual implementation. type boundRequest struct { rpcreflect.MethodCaller transformErrors func(error) error hdr Header } // bindRequest searches for methods implementing the // request held in the given header and returns // a boundRequest that can call those methods. func (conn *Conn) bindRequest(hdr *Header) (boundRequest, error) { conn.mutex.Lock() rootValue := conn.rootValue transformErrors := conn.transformErrors conn.mutex.Unlock() if !rootValue.IsValid() { return boundRequest{}, fmt.Errorf("no service") } caller, err := rootValue.MethodCaller(hdr.Request.Type, hdr.Request.Action) if err != nil { if _, ok := err.(*rpcreflect.CallNotImplementedError); ok { err = &serverError{ Message: err.Error(), Code: CodeNotImplemented, } } return boundRequest{}, err } return boundRequest{ MethodCaller: caller, transformErrors: transformErrors, hdr: *hdr, }, nil } // runRequest runs the given request and sends the reply. func (conn *Conn) runRequest(req boundRequest, arg reflect.Value, startTime time.Time) { defer conn.srvPending.Done() rv, err := req.Call(req.hdr.Request.Id, arg) if err != nil { err = conn.writeErrorResponse(&req.hdr, req.transformErrors(err), startTime) } else { hdr := &Header{ RequestId: req.hdr.RequestId, } var rvi interface{} if rv.IsValid() { rvi = rv.Interface() } else { rvi = struct{}{} } if conn.notifier != nil { conn.notifier.ServerReply(req.hdr.Request, hdr, rvi, time.Since(startTime)) } conn.sending.Lock() err = conn.codec.WriteMessage(hdr, rvi) conn.sending.Unlock() } if err != nil { logger.Errorf("error writing response: %v", err) } } type serverError RequestError func (e *serverError) Error() string { return e.Message } func (e *serverError) ErrorCode() string { return e.Code } ������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/rpcreflect/����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023567� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/rpcreflect/value.go��������������������������������0000644�0000153�0000161�00000005576�12321735642�025262� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rpcreflect import ( "fmt" "reflect" ) // CallNotImplementedError is an error, returned an attempt to call to // an unknown API method is made. type CallNotImplementedError struct { Method string RootMethod string } func (e *CallNotImplementedError) Error() string { if e.Method == "" { return fmt.Sprintf("unknown object type %q", e.RootMethod) } return fmt.Sprintf("no such request - method %s.%s is not implemented", e.RootMethod, e.Method) } // MethodCaller knows how to call a particular RPC method. type MethodCaller struct { // ParamsType holds the required type of the parameter to the object method. ParamsType reflect.Type // ResultType holds the result type of the result of caling the object method. ResultType reflect.Type rootValue reflect.Value rootMethod RootMethod objMethod ObjMethod } // MethodCaller holds the value of the root of an RPC server that // can call methods directly on a Go value. type Value struct { rootValue reflect.Value rootType *Type } // ValueOf returns a value that can be used to call RPC-style // methods on the given root value. It returns the zero // Value if rootValue.IsValid is false. func ValueOf(rootValue reflect.Value) Value { if !rootValue.IsValid() { return Value{} } return Value{ rootValue: rootValue, rootType: TypeOf(rootValue.Type()), } } // IsValid returns whether the Value has been initialized with ValueOf. func (v Value) IsValid() bool { return v.rootType != nil } // GoValue returns the value that was passed to ValueOf to create v. func (v Value) GoValue() reflect.Value { return v.rootValue } // MethodCaller returns an object that can be used to make calls on // the given root value to the given root method and object method. // It returns an error if either the root method or the object // method were not found. // It panics if called on the zero Value. func (v Value) MethodCaller(rootMethodName, objMethodName string) (MethodCaller, error) { if !v.IsValid() { panic("MethodCaller called on invalid Value") } caller := MethodCaller{ rootValue: v.rootValue, } var err error caller.rootMethod, err = v.rootType.Method(rootMethodName) if err != nil { return MethodCaller{}, &CallNotImplementedError{ RootMethod: rootMethodName, } } caller.objMethod, err = caller.rootMethod.ObjType.Method(objMethodName) if err != nil { return MethodCaller{}, &CallNotImplementedError{ RootMethod: rootMethodName, Method: objMethodName, } } caller.ParamsType = caller.objMethod.Params caller.ResultType = caller.objMethod.Result return caller, nil } func (caller MethodCaller) Call(objId string, arg reflect.Value) (reflect.Value, error) { obj, err := caller.rootMethod.Call(caller.rootValue, objId) if err != nil { return reflect.Value{}, err } return caller.objMethod.Call(obj, arg) } ����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/rpcreflect/type.go���������������������������������0000644�0000153�0000161�00000020467�12321735776�025133� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rpcreflect import ( "errors" "reflect" "sort" "sync" ) var ( errorType = reflect.TypeOf((*error)(nil)).Elem() stringType = reflect.TypeOf("") ) var ( typeMutex sync.RWMutex typesByGoType = make(map[reflect.Type]*Type) objTypeMutex sync.RWMutex objTypesByGoType = make(map[reflect.Type]*ObjType) ) var ErrMethodNotFound = errors.New("no such method") // Type holds information about a type that implements RPC server methods, // a root-level RPC type. type Type struct { // root holds the root type. root reflect.Type // method maps from root-object method name to // information about that method. The term "obtain" // is because these methods obtain an object to // call an action on. method map[string]*RootMethod // discarded holds names of all discarded methods. discarded []string } // MethodNames returns the names of all the root object // methods on the receiving object. func (r *Type) MethodNames() []string { names := make([]string, 0, len(r.method)) for name := range r.method { names = append(names, name) } sort.Strings(names) return names } // Method returns information on the method with the given name, // or the zero Method and ErrMethodNotFound if there is no such method // (the only possible error). func (r *Type) Method(name string) (RootMethod, error) { m, ok := r.method[name] if !ok { return RootMethod{}, ErrMethodNotFound } return *m, nil } func (r *Type) DiscardedMethods() []string { return append([]string(nil), r.discarded...) } // RootMethod holds information on a root-level method. type RootMethod struct { // Call invokes the method. The rcvr parameter must be // the same type as the root object. The given id is passed // as an argument to the method. Call func(rcvr reflect.Value, id string) (reflect.Value, error) // Methods holds RPC object-method information about // objects returned from the above call. ObjType *ObjType } // TypeOf returns information on all root-level RPC methods // implemented by an object of the given Go type. // If goType is nil, it returns nil. func TypeOf(goType reflect.Type) *Type { if goType == nil { return nil } typeMutex.RLock() t := typesByGoType[goType] typeMutex.RUnlock() if t != nil { return t } typeMutex.Lock() defer typeMutex.Unlock() t = typesByGoType[goType] if t != nil { return t } t = typeOf(goType) typesByGoType[goType] = t return t } // typeOf is like TypeOf but without the cache - it // always allocates. Called with rootTypeMutex locked. func typeOf(goType reflect.Type) *Type { rm := &Type{ method: make(map[string]*RootMethod), } for i := 0; i < goType.NumMethod(); i++ { m := goType.Method(i) if m.PkgPath != "" || isKillMethod(m) { // The Kill method gets a special exception because // it fulfils the Killer interface which we're expecting, // so it's not really discarded as such. continue } if o := newRootMethod(m); o != nil { rm.method[m.Name] = o } else { rm.discarded = append(rm.discarded, m.Name) } } return rm } func isKillMethod(m reflect.Method) bool { return m.Name == "Kill" && m.Type.NumIn() == 1 && m.Type.NumOut() == 0 } func newRootMethod(m reflect.Method) *RootMethod { if m.PkgPath != "" { return nil } t := m.Type if t.NumIn() != 2 || t.NumOut() != 2 || t.In(1) != stringType || t.Out(1) != errorType { return nil } f := func(rcvr reflect.Value, id string) (r reflect.Value, err error) { out := rcvr.Method(m.Index).Call([]reflect.Value{reflect.ValueOf(id)}) if !out[1].IsNil() { err = out[1].Interface().(error) } r = out[0] return } return &RootMethod{ Call: f, ObjType: ObjTypeOf(t.Out(0)), } } // ObjType holds information on RPC methods implemented on // an RPC object. type ObjType struct { goType reflect.Type method map[string]*ObjMethod discarded []string } func (t *ObjType) GoType() reflect.Type { return t.goType } // Method returns information on the method with the given name, // or the zero Method and ErrMethodNotFound if there is no such method // (the only possible error). func (t *ObjType) Method(name string) (ObjMethod, error) { m, ok := t.method[name] if !ok { return ObjMethod{}, ErrMethodNotFound } return *m, nil } // DiscardedMethods returns the names of all methods that cannot // implement RPC calls because their type signature is inappropriate. func (t *ObjType) DiscardedMethods() []string { return append([]string(nil), t.discarded...) } // MethodNames returns the names of all the RPC methods // defined on the type. func (t *ObjType) MethodNames() []string { names := make([]string, 0, len(t.method)) for name := range t.method { names = append(names, name) } sort.Strings(names) return names } // ObjMethod holds information about an RPC method on an // object returned by a root-level method. type ObjMethod struct { // Params holds the argument type of the method, or nil // if there is no argument. Params reflect.Type // Result holds the return type of the method, or nil // if the method returns no value. Result reflect.Type // Call calls the method with the given argument // on the given receiver value. If the method does // not return a value, the returned value will not be valid. Call func(rcvr, arg reflect.Value) (reflect.Value, error) } // ObjTypeOf returns information on all RPC methods // implemented by an object of the given Go type, // as returned from a root-level method. // If objType is nil, it returns nil. func ObjTypeOf(objType reflect.Type) *ObjType { if objType == nil { return nil } objTypeMutex.RLock() methods := objTypesByGoType[objType] objTypeMutex.RUnlock() if methods != nil { return methods } objTypeMutex.Lock() defer objTypeMutex.Unlock() methods = objTypesByGoType[objType] if methods != nil { return methods } methods = objTypeOf(objType) objTypesByGoType[objType] = methods return methods } // objTypeOf is like ObjTypeOf but without the cache. // Called with objTypeMutex locked. func objTypeOf(goType reflect.Type) *ObjType { objType := &ObjType{ method: make(map[string]*ObjMethod), goType: goType, } for i := 0; i < goType.NumMethod(); i++ { m := goType.Method(i) if m.PkgPath != "" { continue } if objm := newMethod(m, goType.Kind()); objm != nil { objType.method[m.Name] = objm } else { objType.discarded = append(objType.discarded, m.Name) } } return objType } func newMethod(m reflect.Method, receiverKind reflect.Kind) *ObjMethod { if m.PkgPath != "" { return nil } var p ObjMethod var assemble func(arg reflect.Value) []reflect.Value // N.B. The method type has the receiver as its first argument // unless the receiver is an interface. receiverArgCount := 1 if receiverKind == reflect.Interface { receiverArgCount = 0 } t := m.Type switch { case t.NumIn() == 0+receiverArgCount: // Method() ... assemble = func(arg reflect.Value) []reflect.Value { return nil } case t.NumIn() == 1+receiverArgCount: // Method(T) ... p.Params = t.In(receiverArgCount) assemble = func(arg reflect.Value) []reflect.Value { return []reflect.Value{arg} } default: return nil } switch { case t.NumOut() == 0: // Method(...) p.Call = func(rcvr, arg reflect.Value) (r reflect.Value, err error) { rcvr.Method(m.Index).Call(assemble(arg)) return } case t.NumOut() == 1 && t.Out(0) == errorType: // Method(...) error p.Call = func(rcvr, arg reflect.Value) (r reflect.Value, err error) { out := rcvr.Method(m.Index).Call(assemble(arg)) if !out[0].IsNil() { err = out[0].Interface().(error) } return } case t.NumOut() == 1: // Method(...) R p.Result = t.Out(0) p.Call = func(rcvr, arg reflect.Value) (reflect.Value, error) { out := rcvr.Method(m.Index).Call(assemble(arg)) return out[0], nil } case t.NumOut() == 2 && t.Out(1) == errorType: // Method(...) (R, error) p.Result = t.Out(0) p.Call = func(rcvr, arg reflect.Value) (r reflect.Value, err error) { out := rcvr.Method(m.Index).Call(assemble(arg)) r = out[0] if !out[1].IsNil() { err = out[1].Interface().(error) } return } default: return nil } // The parameters and return value must be of struct type. if p.Params != nil && p.Params.Kind() != reflect.Struct { return nil } if p.Result != nil && p.Result.Kind() != reflect.Struct { return nil } return &p } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/jsoncodec/�����������������������������������������0000755�0000153�0000161�00000000000�12321735642�023420� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/jsoncodec/codec_test.go����������������������������0000644�0000153�0000161�00000020353�12321735642�026066� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package jsoncodec_test import ( "encoding/json" "errors" "io" "reflect" "regexp" stdtesting "testing" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/rpc" "launchpad.net/juju-core/rpc/jsoncodec" "launchpad.net/juju-core/testing/testbase" ) type suite struct { testbase.LoggingSuite } var _ = gc.Suite(&suite{}) func TestPackage(t *stdtesting.T) { gc.TestingT(t) } type value struct { X string } var readTests = []struct { msg string expectHdr rpc.Header expectBody interface{} }{{ msg: `{"RequestId": 1, "Type": "foo", "Id": "id", "Request": "frob", "Params": {"X": "param"}}`, expectHdr: rpc.Header{ RequestId: 1, Request: rpc.Request{ Type: "foo", Id: "id", Action: "frob", }, }, expectBody: &value{X: "param"}, }, { msg: `{"RequestId": 2, "Error": "an error", "ErrorCode": "a code"}`, expectHdr: rpc.Header{ RequestId: 2, Error: "an error", ErrorCode: "a code", }, expectBody: new(map[string]interface{}), }, { msg: `{"RequestId": 3, "Response": {"X": "result"}}`, expectHdr: rpc.Header{ RequestId: 3, }, expectBody: &value{X: "result"}, }} func (*suite) TestRead(c *gc.C) { for i, test := range readTests { c.Logf("test %d", i) codec := jsoncodec.New(&testConn{ readMsgs: []string{test.msg}, }) var hdr rpc.Header err := codec.ReadHeader(&hdr) c.Assert(err, gc.IsNil) c.Assert(hdr, gc.DeepEquals, test.expectHdr) c.Assert(hdr.IsRequest(), gc.Equals, test.expectHdr.IsRequest()) body := reflect.New(reflect.ValueOf(test.expectBody).Type().Elem()).Interface() err = codec.ReadBody(body, test.expectHdr.IsRequest()) c.Assert(err, gc.IsNil) c.Assert(body, gc.DeepEquals, test.expectBody) err = codec.ReadHeader(&hdr) c.Assert(err, gc.Equals, io.EOF) } } func (*suite) TestReadHeaderLogsRequests(c *gc.C) { codecLogger := loggo.GetLogger("juju.rpc.jsoncodec") defer codecLogger.SetLogLevel(codecLogger.LogLevel()) codecLogger.SetLogLevel(loggo.TRACE) msg := `{"RequestId":1,"Type": "foo","Id": "id","Request":"frob","Params":{"X":"param"}}` codec := jsoncodec.New(&testConn{ readMsgs: []string{msg, msg, msg}, }) // Check that logging is off by default var h rpc.Header err := codec.ReadHeader(&h) c.Assert(err, gc.IsNil) c.Assert(c.GetTestLog(), gc.Matches, "") // Check that we see a log message when we switch logging on. codec.SetLogging(true) err = codec.ReadHeader(&h) c.Assert(err, gc.IsNil) c.Assert(c.GetTestLog(), gc.Matches, ".*TRACE juju.rpc.jsoncodec <- "+regexp.QuoteMeta(msg)+`\n`) // Check that we can switch it off again codec.SetLogging(false) err = codec.ReadHeader(&h) c.Assert(err, gc.IsNil) c.Assert(c.GetTestLog(), gc.Matches, ".*TRACE juju.rpc.jsoncodec <- "+regexp.QuoteMeta(msg)+`\n`) } func (*suite) TestWriteMessageLogsRequests(c *gc.C) { codecLogger := loggo.GetLogger("juju.rpc.jsoncodec") defer codecLogger.SetLogLevel(codecLogger.LogLevel()) codecLogger.SetLogLevel(loggo.TRACE) codec := jsoncodec.New(&testConn{}) h := rpc.Header{ RequestId: 1, Request: rpc.Request{ Type: "foo", Id: "id", Action: "frob", }, } // Check that logging is off by default err := codec.WriteMessage(&h, value{X: "param"}) c.Assert(err, gc.IsNil) c.Assert(c.GetTestLog(), gc.Matches, "") // Check that we see a log message when we switch logging on. codec.SetLogging(true) err = codec.WriteMessage(&h, value{X: "param"}) c.Assert(err, gc.IsNil) msg := `{"RequestId":1,"Type":"foo","Id":"id","Request":"frob","Params":{"X":"param"}}` c.Assert(c.GetTestLog(), gc.Matches, `.*TRACE juju.rpc.jsoncodec -> `+regexp.QuoteMeta(msg)+`\n`) // Check that we can switch it off again codec.SetLogging(false) err = codec.WriteMessage(&h, value{X: "param"}) c.Assert(err, gc.IsNil) c.Assert(c.GetTestLog(), gc.Matches, `.*TRACE juju.rpc.jsoncodec -> `+regexp.QuoteMeta(msg)+`\n`) } func (*suite) TestConcurrentSetLoggingAndWrite(c *gc.C) { // If log messages are not set atomically, this // test will fail when run under the race detector. codec := jsoncodec.New(&testConn{}) done := make(chan struct{}) go func() { codec.SetLogging(true) done <- struct{}{} }() h := rpc.Header{ RequestId: 1, Request: rpc.Request{ Type: "foo", Id: "id", Action: "frob", }, } err := codec.WriteMessage(&h, value{X: "param"}) c.Assert(err, gc.IsNil) <-done } func (*suite) TestConcurrentSetLoggingAndRead(c *gc.C) { // If log messages are not set atomically, this // test will fail when run under the race detector. msg := `{"RequestId":1,"Type": "foo","Id": "id","Request":"frob","Params":{"X":"param"}}` codec := jsoncodec.New(&testConn{ readMsgs: []string{msg, msg, msg}, }) done := make(chan struct{}) go func() { codec.SetLogging(true) done <- struct{}{} }() var h rpc.Header err := codec.ReadHeader(&h) c.Assert(err, gc.IsNil) <-done } func (*suite) TestErrorAfterClose(c *gc.C) { conn := &testConn{ err: errors.New("some error"), } codec := jsoncodec.New(conn) var hdr rpc.Header err := codec.ReadHeader(&hdr) c.Assert(err, gc.ErrorMatches, "error receiving message: some error") err = codec.Close() c.Assert(err, gc.IsNil) c.Assert(conn.closed, gc.Equals, true) err = codec.ReadHeader(&hdr) c.Assert(err, gc.Equals, io.EOF) } var writeTests = []struct { hdr *rpc.Header body interface{} isRequest bool expect string }{{ hdr: &rpc.Header{ RequestId: 1, Request: rpc.Request{ Type: "foo", Id: "id", Action: "frob", }, }, body: &value{X: "param"}, expect: `{"RequestId": 1, "Type": "foo","Id":"id", "Request": "frob", "Params": {"X": "param"}}`, }, { hdr: &rpc.Header{ RequestId: 2, Error: "an error", ErrorCode: "a code", }, expect: `{"RequestId": 2, "Error": "an error", "ErrorCode": "a code"}`, }, { hdr: &rpc.Header{ RequestId: 3, }, body: &value{X: "result"}, expect: `{"RequestId": 3, "Response": {"X": "result"}}`, }} func (*suite) TestWrite(c *gc.C) { for i, test := range writeTests { c.Logf("test %d", i) var conn testConn codec := jsoncodec.New(&conn) err := codec.WriteMessage(test.hdr, test.body) c.Assert(err, gc.IsNil) c.Assert(conn.writeMsgs, gc.HasLen, 1) assertJSONEqual(c, conn.writeMsgs[0], test.expect) } } var dumpRequestTests = []struct { hdr rpc.Header body interface{} expect string }{{ hdr: rpc.Header{ RequestId: 1, Request: rpc.Request{ Type: "Foo", Id: "id", Action: "Something", }, }, body: struct{ Arg string }{Arg: "an arg"}, expect: `{"RequestId":1,"Type":"Foo","Id":"id","Request":"Something","Params":{"Arg":"an arg"}}`, }, { hdr: rpc.Header{ RequestId: 2, }, body: struct{ Ret string }{Ret: "return value"}, expect: `{"RequestId":2,"Response":{"Ret":"return value"}}`, }, { hdr: rpc.Header{ RequestId: 3, }, expect: `{"RequestId":3}`, }, { hdr: rpc.Header{ RequestId: 4, Error: "an error", ErrorCode: "an error code", }, expect: `{"RequestId":4,"Error":"an error","ErrorCode":"an error code"}`, }, { hdr: rpc.Header{ RequestId: 5, }, body: make(chan int), expect: `"marshal error: json: unsupported type: chan int"`, }} func (*suite) TestDumpRequest(c *gc.C) { for i, test := range dumpRequestTests { c.Logf("test %d; %#v", i, test.hdr) data := jsoncodec.DumpRequest(&test.hdr, test.body) c.Check(string(data), gc.Equals, test.expect) } } // assertJSONEqual compares the json strings v0 // and v1 ignoring white space. func assertJSONEqual(c *gc.C, v0, v1 string) { var m0, m1 interface{} err := json.Unmarshal([]byte(v0), &m0) c.Assert(err, gc.IsNil) err = json.Unmarshal([]byte(v1), &m1) c.Assert(err, gc.IsNil) data0, err := json.Marshal(m0) c.Assert(err, gc.IsNil) data1, err := json.Marshal(m1) c.Assert(err, gc.IsNil) c.Assert(string(data0), gc.Equals, string(data1)) } type testConn struct { readMsgs []string err error writeMsgs []string closed bool } func (c *testConn) Receive(msg interface{}) error { if len(c.readMsgs) > 0 { s := c.readMsgs[0] c.readMsgs = c.readMsgs[1:] return json.Unmarshal([]byte(s), msg) } if c.err != nil { return c.err } return io.EOF } func (c *testConn) Send(msg interface{}) error { data, err := json.Marshal(msg) if err != nil { return err } c.writeMsgs = append(c.writeMsgs, string(data)) return nil } func (c *testConn) Close() error { c.closed = true return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/jsoncodec/conn.go����������������������������������0000644�0000153�0000161�00000002215�12321735642�024704� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package jsoncodec import ( "encoding/json" "net" "code.google.com/p/go.net/websocket" ) // NewWebsocket returns an rpc codec that uses the given websocket // connection to send and receive messages. func NewWebsocket(conn *websocket.Conn) *Codec { return New(wsJSONConn{conn}) } type wsJSONConn struct { conn *websocket.Conn } func (conn wsJSONConn) Send(msg interface{}) error { return websocket.JSON.Send(conn.conn, msg) } func (conn wsJSONConn) Receive(msg interface{}) error { return websocket.JSON.Receive(conn.conn, msg) } func (conn wsJSONConn) Close() error { return conn.conn.Close() } // NewNet returns an rpc codec that uses the given net // connection to send and receive messages. func NewNet(conn net.Conn) *Codec { return New(&netConn{ enc: json.NewEncoder(conn), dec: json.NewDecoder(conn), conn: conn, }) } type netConn struct { enc *json.Encoder dec *json.Decoder conn net.Conn } func (conn *netConn) Send(msg interface{}) error { return conn.enc.Encode(msg) } func (conn *netConn) Receive(msg interface{}) error { return conn.dec.Decode(msg) } func (conn *netConn) Close() error { return conn.conn.Close() } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/jsoncodec/codec.go���������������������������������0000644�0000153�0000161�00000010650�12321735642�025026� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// The jsoncodec package provides a JSON codec for the rpc package. package jsoncodec import ( "encoding/json" "fmt" "io" "sync" "sync/atomic" "github.com/juju/loggo" "launchpad.net/juju-core/rpc" ) var logger = loggo.GetLogger("juju.rpc.jsoncodec") // JSONConn sends and receives messages to an underlying connection // in JSON format. type JSONConn interface { // Send sends a message. Send(msg interface{}) error // Receive receives a message into msg. Receive(msg interface{}) error Close() error } // Codec implements rpc.Codec for a connection. type Codec struct { // msg holds the message that's just been read by ReadHeader, so // that the body can be read by ReadBody. msg inMsg conn JSONConn logMessages int32 mu sync.Mutex closing bool } // New returns an rpc codec that uses conn to send and receive // messages. func New(conn JSONConn) *Codec { return &Codec{ conn: conn, } } // SetLogging sets whether messages will be logged // by the codec. func (c *Codec) SetLogging(on bool) { val := int32(0) if on { val = 1 } atomic.StoreInt32(&c.logMessages, val) } func (c *Codec) isLogging() bool { return atomic.LoadInt32(&c.logMessages) != 0 } // inMsg holds an incoming message. We don't know the type of the // parameters or response yet, so we delay parsing by storing them // in a RawMessage. type inMsg struct { RequestId uint64 Type string Id string Request string Params json.RawMessage Error string ErrorCode string Response json.RawMessage } // outMsg holds an outgoing message. type outMsg struct { RequestId uint64 Type string `json:",omitempty"` Id string `json:",omitempty"` Request string `json:",omitempty"` Params interface{} `json:",omitempty"` Error string `json:",omitempty"` ErrorCode string `json:",omitempty"` Response interface{} `json:",omitempty"` } func (c *Codec) Close() error { c.mu.Lock() c.closing = true c.mu.Unlock() return c.conn.Close() } func (c *Codec) isClosing() bool { c.mu.Lock() defer c.mu.Unlock() return c.closing } func (c *Codec) ReadHeader(hdr *rpc.Header) error { c.msg = inMsg{} // avoid any potential cross-message contamination. var err error if c.isLogging() { var m json.RawMessage err = c.conn.Receive(&m) if err == nil { logger.Tracef("<- %s", m) err = json.Unmarshal(m, &c.msg) } else { logger.Tracef("<- error: %v (closing %v)", err, c.isClosing()) } } else { err = c.conn.Receive(&c.msg) } if err != nil { // If we've closed the connection, we may get a spurious error, // so ignore it. if c.isClosing() || err == io.EOF { return io.EOF } return fmt.Errorf("error receiving message: %v", err) } hdr.RequestId = c.msg.RequestId hdr.Request = rpc.Request{ Type: c.msg.Type, Id: c.msg.Id, Action: c.msg.Request, } hdr.Error = c.msg.Error hdr.ErrorCode = c.msg.ErrorCode return nil } func (c *Codec) ReadBody(body interface{}, isRequest bool) error { if body == nil { return nil } var rawBody json.RawMessage if isRequest { rawBody = c.msg.Params } else { rawBody = c.msg.Response } if len(rawBody) == 0 { // If the response or params are omitted, it's // equivalent to an empty object. return nil } return json.Unmarshal(rawBody, body) } // DumpRequest returns JSON-formatted data representing // the RPC message with the given header and body, // as it would be written by Codec.WriteMessage. // If the body cannot be marshalled as JSON, the data // will hold a JSON string describing the error. func DumpRequest(hdr *rpc.Header, body interface{}) []byte { var m outMsg m.init(hdr, body) data, err := json.Marshal(&m) if err != nil { return []byte(fmt.Sprintf("%q", "marshal error: "+err.Error())) } return data } func (c *Codec) WriteMessage(hdr *rpc.Header, body interface{}) error { var m outMsg m.init(hdr, body) if c.isLogging() { data, err := json.Marshal(&m) if err != nil { logger.Tracef("-> marshal error: %v", err) return err } logger.Tracef("-> %s", data) } return c.conn.Send(&m) } // init fills out the receiving outMsg with information from the given // header and body. func (m *outMsg) init(hdr *rpc.Header, body interface{}) { m.RequestId = hdr.RequestId m.Type = hdr.Request.Type m.Id = hdr.Request.Id m.Request = hdr.Request.Action m.Error = hdr.Error m.ErrorCode = hdr.ErrorCode if hdr.IsRequest() { m.Params = body } else { m.Response = body } } ����������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/client.go������������������������������������������0000644�0000153�0000161�00000011204�12321735642�023254� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rpc import ( "errors" "strings" ) var ErrShutdown = errors.New("connection is shut down") // Call represents an active RPC. type Call struct { Request Params interface{} Response interface{} Error error Done chan *Call } // RequestError represents an error returned from an RPC request. type RequestError struct { Message string Code string } func (e *RequestError) Error() string { m := "request error: " + e.Message if e.Code != "" { m += " (" + e.Code + ")" } return m } func (e *RequestError) ErrorCode() string { return e.Code } func (conn *Conn) send(call *Call) { conn.sending.Lock() defer conn.sending.Unlock() // Register this call. conn.mutex.Lock() if conn.dead == nil { panic("rpc: call made when connection not started") } if conn.closing || conn.shutdown { call.Error = ErrShutdown conn.mutex.Unlock() call.done() return } conn.reqId++ reqId := conn.reqId conn.clientPending[reqId] = call conn.mutex.Unlock() // Encode and send the request. hdr := &Header{ RequestId: reqId, Request: call.Request, } params := call.Params if params == nil { params = struct{}{} } if conn.notifier != nil { conn.notifier.ClientRequest(hdr, params) } if err := conn.codec.WriteMessage(hdr, params); err != nil { conn.mutex.Lock() call = conn.clientPending[reqId] delete(conn.clientPending, reqId) conn.mutex.Unlock() if call != nil { call.Error = err call.done() } } } func (conn *Conn) handleResponse(hdr *Header) error { reqId := hdr.RequestId conn.mutex.Lock() call := conn.clientPending[reqId] delete(conn.clientPending, reqId) conn.mutex.Unlock() var err error switch { case call == nil: // We've got no pending call. That usually means that // WriteHeader partially failed, and call was already // removed; response is a server telling us about an // error reading request body. We should still attempt // to read error body, but there's no one to give it to. if conn.notifier != nil { conn.notifier.ClientReply(Request{}, hdr, nil) } err = conn.readBody(nil, false) case hdr.Error != "": // Report rpcreflect.NoSuchMethodError with CodeNotImplemented. if strings.HasPrefix(hdr.Error, "no such request ") && hdr.ErrorCode == "" { hdr.ErrorCode = CodeNotImplemented } // We've got an error response. Give this to the request; // any subsequent requests will get the ReadResponseBody // error if there is one. call.Error = &RequestError{ Message: hdr.Error, Code: hdr.ErrorCode, } err = conn.readBody(nil, false) if conn.notifier != nil { conn.notifier.ClientReply(call.Request, hdr, nil) } call.done() default: err = conn.readBody(call.Response, false) if conn.notifier != nil { conn.notifier.ClientReply(call.Request, hdr, call.Response) } call.done() } return err } func (call *Call) done() { select { case call.Done <- call: // ok default: // We don't want to block here. It is the caller's responsibility to make // sure the channel has enough buffer space. See comment in Go(). logger.Errorf("discarding Call reply due to insufficient Done chan capacity") } } // Call invokes the named action on the object of the given type with // the given id. The returned values will be stored in response, which // should be a pointer. If the action fails remotely, the returned // error will be of type RequestError. The params value may be nil if // no parameters are provided; the response value may be nil to indicate // that any result should be discarded. func (conn *Conn) Call(req Request, params, response interface{}) error { call := <-conn.Go(req, params, response, make(chan *Call, 1)).Done return call.Error } // Go invokes the request asynchronously. It returns the Call structure representing // the invocation. The done channel will signal when the call is complete by returning // the same Call object. If done is nil, Go will allocate a new channel. // If non-nil, done must be buffered or Go will deliberately panic. func (conn *Conn) Go(req Request, args, response interface{}, done chan *Call) *Call { if done == nil { done = make(chan *Call, 1) } else { // If caller passes done != nil, it must arrange that // done has enough buffer for the number of simultaneous // RPCs that will be using that channel. If the channel // is totally unbuffered, it's best not to run at all. if cap(done) == 0 { panic("launchpad.net/juju-core/rpc: done channel is unbuffered") } } call := &Call{ Request: req, Params: args, Response: response, Done: done, } conn.send(call) return call } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/rpc/rpc_test.go����������������������������������������0000644�0000153�0000161�00000072077�12321735642�023640� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rpc_test import ( "encoding/json" "fmt" "net" "reflect" "regexp" "sync" stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/log" "launchpad.net/juju-core/rpc" "launchpad.net/juju-core/rpc/jsoncodec" "launchpad.net/juju-core/testing/testbase" ) type rpcSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&rpcSuite{}) func TestAll(t *stdtesting.T) { gc.TestingT(t) } type callInfo struct { rcvr interface{} method string arg interface{} } type callError callInfo func (e *callError) Error() string { return fmt.Sprintf("error calling %s", e.method) } type stringVal struct { Val string } type Root struct { mu sync.Mutex conn *rpc.Conn calls []*callInfo returnErr bool simple map[string]*SimpleMethods delayed map[string]*DelayedMethods errorInst *ErrorMethods } func (r *Root) callError(rcvr interface{}, name string, arg interface{}) error { if r.returnErr { return &callError{rcvr, name, arg} } return nil } func (r *Root) SimpleMethods(id string) (*SimpleMethods, error) { r.mu.Lock() defer r.mu.Unlock() if a := r.simple[id]; a != nil { return a, nil } return nil, fmt.Errorf("unknown SimpleMethods id") } func (r *Root) DelayedMethods(id string) (*DelayedMethods, error) { r.mu.Lock() defer r.mu.Unlock() if a := r.delayed[id]; a != nil { return a, nil } return nil, fmt.Errorf("unknown DelayedMethods id") } func (r *Root) ErrorMethods(id string) (*ErrorMethods, error) { if r.errorInst == nil { return nil, fmt.Errorf("no error methods") } return r.errorInst, nil } func (r *Root) Discard1() {} func (r *Root) Discard2(id string) error { return nil } func (r *Root) Discard3(id string) int { return 0 } func (r *Root) CallbackMethods(string) (*CallbackMethods, error) { return &CallbackMethods{r}, nil } func (r *Root) InterfaceMethods(id string) (InterfaceMethods, error) { log.Infof("interface methods called") m, err := r.SimpleMethods(id) if err != nil { return nil, err } return m, nil } type InterfaceMethods interface { Call1r1e(s stringVal) (stringVal, error) } type ChangeAPIMethods struct { r *Root } func (r *Root) ChangeAPIMethods(string) (*ChangeAPIMethods, error) { return &ChangeAPIMethods{r}, nil } func (t *Root) called(rcvr interface{}, method string, arg interface{}) { t.mu.Lock() t.calls = append(t.calls, &callInfo{rcvr, method, arg}) t.mu.Unlock() } type SimpleMethods struct { root *Root id string } // Each Call method is named in this standard form: // // Call<narg>r<nret><e> // // where narg is the number of arguments, nret is the number of returned // values (not including the error) and e is the letter 'e' if the // method returns an error. func (a *SimpleMethods) Call0r0() { a.root.called(a, "Call0r0", nil) } func (a *SimpleMethods) Call0r1() stringVal { a.root.called(a, "Call0r1", nil) return stringVal{"Call0r1 ret"} } func (a *SimpleMethods) Call0r1e() (stringVal, error) { a.root.called(a, "Call0r1e", nil) return stringVal{"Call0r1e ret"}, a.root.callError(a, "Call0r1e", nil) } func (a *SimpleMethods) Call0r0e() error { a.root.called(a, "Call0r0e", nil) return a.root.callError(a, "Call0r0e", nil) } func (a *SimpleMethods) Call1r0(s stringVal) { a.root.called(a, "Call1r0", s) } func (a *SimpleMethods) Call1r1(s stringVal) stringVal { a.root.called(a, "Call1r1", s) return stringVal{"Call1r1 ret"} } func (a *SimpleMethods) Call1r1e(s stringVal) (stringVal, error) { a.root.called(a, "Call1r1e", s) return stringVal{"Call1r1e ret"}, a.root.callError(a, "Call1r1e", s) } func (a *SimpleMethods) Call1r0e(s stringVal) error { a.root.called(a, "Call1r0e", s) return a.root.callError(a, "Call1r0e", s) } func (a *SimpleMethods) SliceArg(struct{ X []string }) stringVal { return stringVal{"SliceArg ret"} } func (a *SimpleMethods) Discard1(int) {} func (a *SimpleMethods) Discard2(struct{}, struct{}) {} func (a *SimpleMethods) Discard3() int { return 0 } func (a *SimpleMethods) Discard4() (_, _ struct{}) { return } type DelayedMethods struct { ready chan struct{} done chan string doneError chan error } func (a *DelayedMethods) Delay() (stringVal, error) { if a.ready != nil { a.ready <- struct{}{} } select { case s := <-a.done: return stringVal{s}, nil case err := <-a.doneError: return stringVal{}, err } } type ErrorMethods struct { err error } func (e *ErrorMethods) Call() error { return e.err } type CallbackMethods struct { root *Root } type int64val struct { I int64 } func (a *CallbackMethods) Factorial(x int64val) (int64val, error) { if x.I <= 1 { return int64val{1}, nil } var r int64val err := a.root.conn.Call(rpc.Request{"CallbackMethods", "", "Factorial"}, int64val{x.I - 1}, &r) if err != nil { return int64val{}, err } return int64val{x.I * r.I}, nil } func (a *ChangeAPIMethods) ChangeAPI() { a.r.conn.Serve(&changedAPIRoot{}, nil) } func (a *ChangeAPIMethods) RemoveAPI() { a.r.conn.Serve(nil, nil) } type changedAPIRoot struct{} func (r *changedAPIRoot) NewlyAvailable(string) (newlyAvailableMethods, error) { return newlyAvailableMethods{}, nil } type newlyAvailableMethods struct{} func (newlyAvailableMethods) NewMethod() stringVal { return stringVal{"new method result"} } func (*rpcSuite) TestRPC(c *gc.C) { root := &Root{ simple: make(map[string]*SimpleMethods), } root.simple["a99"] = &SimpleMethods{root: root, id: "a99"} client, srvDone, clientNotifier, serverNotifier := newRPCClientServer(c, root, nil, false) defer closeClient(c, client, srvDone) for narg := 0; narg < 2; narg++ { for nret := 0; nret < 2; nret++ { for nerr := 0; nerr < 2; nerr++ { retErr := nerr != 0 p := testCallParams{ client: client, clientNotifier: clientNotifier, serverNotifier: serverNotifier, entry: "SimpleMethods", narg: narg, nret: nret, retErr: retErr, testErr: false, } root.testCall(c, p) if retErr { p.testErr = true root.testCall(c, p) } } } } } func callName(narg, nret int, retErr bool) string { e := "" if retErr { e = "e" } return fmt.Sprintf("Call%dr%d%s", narg, nret, e) } type testCallParams struct { // client holds the client-side of the rpc connection that // will be used to make the call. client *rpc.Conn // clientNotifier holds the notifier for the client side. clientNotifier *notifier // serverNotifier holds the notifier for the server side. serverNotifier *notifier // entry holds the top-level type that will be invoked // (e.g. "SimpleMethods") entry string // narg holds the number of arguments accepted by the // call (0 or 1) narg int // nret holds the number of values returned by the // call (0 or 1). nret int // retErr specifies whether the call returns an error. retErr bool // testErr specifies whether the call should be made to return an error. testErr bool } // request returns the RPC request for the test call. func (p testCallParams) request() rpc.Request { return rpc.Request{ Type: p.entry, Id: "a99", Action: callName(p.narg, p.nret, p.retErr), } } // error message returns the error message that the test call // should return if it returns an error. func (p testCallParams) errorMessage() string { return fmt.Sprintf("error calling %s", p.request().Action) } func (root *Root) testCall(c *gc.C, p testCallParams) { p.clientNotifier.reset() p.serverNotifier.reset() root.calls = nil root.returnErr = p.testErr c.Logf("test call %s", p.request().Action) var r stringVal err := p.client.Call(p.request(), stringVal{"arg"}, &r) switch { case p.retErr && p.testErr: c.Assert(err, gc.DeepEquals, &rpc.RequestError{ Message: p.errorMessage(), }) c.Assert(r, gc.Equals, stringVal{}) case p.nret > 0: c.Assert(r, gc.Equals, stringVal{p.request().Action + " ret"}) } // Check that the call was actually made, the right // parameters were received and the right result returned. root.mu.Lock() defer root.mu.Unlock() root.assertCallMade(c, p) requestId := root.assertClientNotified(c, p, &r) root.assertServerNotified(c, p, requestId) } func (root *Root) assertCallMade(c *gc.C, p testCallParams) { expectCall := callInfo{ rcvr: root.simple["a99"], method: p.request().Action, } if p.narg > 0 { expectCall.arg = stringVal{"arg"} } c.Assert(root.calls, gc.HasLen, 1) c.Assert(*root.calls[0], gc.Equals, expectCall) } // assertClientNotified asserts that the right client notifications // were made for the given test call parameters. The value of r // holds the result parameter passed to the call. // It returns the request id. func (root *Root) assertClientNotified(c *gc.C, p testCallParams, r interface{}) uint64 { c.Assert(p.clientNotifier.serverRequests, gc.HasLen, 0) c.Assert(p.clientNotifier.serverReplies, gc.HasLen, 0) // Test that there was a notification for the request. c.Assert(p.clientNotifier.clientRequests, gc.HasLen, 1) clientReq := p.clientNotifier.clientRequests[0] requestId := clientReq.hdr.RequestId clientReq.hdr.RequestId = 0 // Ignore the exact value of the request id to start with. c.Assert(clientReq.hdr, gc.DeepEquals, rpc.Header{ Request: p.request(), }) c.Assert(clientReq.body, gc.Equals, stringVal{"arg"}) // Test that there was a notification for the reply. c.Assert(p.clientNotifier.clientReplies, gc.HasLen, 1) clientReply := p.clientNotifier.clientReplies[0] c.Assert(clientReply.req, gc.Equals, p.request()) if p.retErr && p.testErr { c.Assert(clientReply.body, gc.Equals, nil) } else { c.Assert(clientReply.body, gc.Equals, r) } if p.retErr && p.testErr { c.Assert(clientReply.hdr, gc.DeepEquals, rpc.Header{ RequestId: requestId, Error: p.errorMessage(), }) } else { c.Assert(clientReply.hdr, gc.DeepEquals, rpc.Header{ RequestId: requestId, }) } return requestId } // assertServerNotified asserts that the right server notifications // were made for the given test call parameters. The id of the request // is held in requestId. func (root *Root) assertServerNotified(c *gc.C, p testCallParams, requestId uint64) { // Check that the right server notifications were made. c.Assert(p.serverNotifier.clientRequests, gc.HasLen, 0) c.Assert(p.serverNotifier.clientReplies, gc.HasLen, 0) // Test that there was a notification for the request. c.Assert(p.serverNotifier.serverRequests, gc.HasLen, 1) serverReq := p.serverNotifier.serverRequests[0] c.Assert(serverReq.hdr, gc.DeepEquals, rpc.Header{ RequestId: requestId, Request: p.request(), }) if p.narg > 0 { c.Assert(serverReq.body, gc.Equals, stringVal{"arg"}) } else { c.Assert(serverReq.body, gc.Equals, struct{}{}) } // Test that there was a notification for the reply. c.Assert(p.serverNotifier.serverReplies, gc.HasLen, 1) serverReply := p.serverNotifier.serverReplies[0] c.Assert(serverReply.req, gc.Equals, p.request()) if p.retErr && p.testErr || p.nret == 0 { c.Assert(serverReply.body, gc.Equals, struct{}{}) } else { c.Assert(serverReply.body, gc.Equals, stringVal{p.request().Action + " ret"}) } if p.retErr && p.testErr { c.Assert(serverReply.hdr, gc.Equals, rpc.Header{ RequestId: requestId, Error: p.errorMessage(), }) } else { c.Assert(serverReply.hdr, gc.Equals, rpc.Header{ RequestId: requestId, }) } } func (*rpcSuite) TestInterfaceMethods(c *gc.C) { root := &Root{ simple: make(map[string]*SimpleMethods), } root.simple["a99"] = &SimpleMethods{root: root, id: "a99"} client, srvDone, clientNotifier, serverNotifier := newRPCClientServer(c, root, nil, false) defer closeClient(c, client, srvDone) p := testCallParams{ client: client, clientNotifier: clientNotifier, serverNotifier: serverNotifier, entry: "InterfaceMethods", narg: 1, nret: 1, retErr: true, testErr: false, } root.testCall(c, p) p.testErr = true root.testCall(c, p) } func (*rpcSuite) TestConcurrentCalls(c *gc.C) { start1 := make(chan string) start2 := make(chan string) ready1 := make(chan struct{}) ready2 := make(chan struct{}) root := &Root{ delayed: map[string]*DelayedMethods{ "1": {ready: ready1, done: start1}, "2": {ready: ready2, done: start2}, }, } client, srvDone, _, _ := newRPCClientServer(c, root, nil, false) defer closeClient(c, client, srvDone) call := func(id string, done chan<- struct{}) { var r stringVal err := client.Call(rpc.Request{"DelayedMethods", id, "Delay"}, nil, &r) c.Check(err, gc.IsNil) c.Check(r.Val, gc.Equals, "return "+id) done <- struct{}{} } done1 := make(chan struct{}) done2 := make(chan struct{}) go call("1", done1) go call("2", done2) // Check that both calls are running concurrently. chanRead(c, ready1, "method 1 ready") chanRead(c, ready2, "method 2 ready") // Let the requests complete. start1 <- "return 1" start2 <- "return 2" chanRead(c, done1, "method 1 done") chanRead(c, done2, "method 2 done") } type codedError struct { m string code string } func (e *codedError) Error() string { return e.m } func (e *codedError) ErrorCode() string { return e.code } func (*rpcSuite) TestErrorCode(c *gc.C) { root := &Root{ errorInst: &ErrorMethods{&codedError{"message", "code"}}, } client, srvDone, _, _ := newRPCClientServer(c, root, nil, false) defer closeClient(c, client, srvDone) err := client.Call(rpc.Request{"ErrorMethods", "", "Call"}, nil, nil) c.Assert(err, gc.ErrorMatches, `request error: message \(code\)`) c.Assert(err.(rpc.ErrorCoder).ErrorCode(), gc.Equals, "code") } func (*rpcSuite) TestTransformErrors(c *gc.C) { root := &Root{ errorInst: &ErrorMethods{&codedError{"message", "code"}}, } tfErr := func(err error) error { c.Check(err, gc.NotNil) if e, ok := err.(*codedError); ok { return &codedError{ m: "transformed: " + e.m, code: "transformed: " + e.code, } } return fmt.Errorf("transformed: %v", err) } client, srvDone, _, _ := newRPCClientServer(c, root, tfErr, false) defer closeClient(c, client, srvDone) err := client.Call(rpc.Request{"ErrorMethods", "", "Call"}, nil, nil) c.Assert(err, gc.DeepEquals, &rpc.RequestError{ Message: "transformed: message", Code: "transformed: code", }) root.errorInst.err = nil err = client.Call(rpc.Request{"ErrorMethods", "", "Call"}, nil, nil) c.Assert(err, gc.IsNil) root.errorInst = nil err = client.Call(rpc.Request{"ErrorMethods", "", "Call"}, nil, nil) c.Assert(err, gc.DeepEquals, &rpc.RequestError{ Message: "transformed: no error methods", }) } func (*rpcSuite) TestServerWaitsForOutstandingCalls(c *gc.C) { ready := make(chan struct{}) start := make(chan string) root := &Root{ delayed: map[string]*DelayedMethods{ "1": { ready: ready, done: start, }, }, } client, srvDone, _, _ := newRPCClientServer(c, root, nil, false) defer closeClient(c, client, srvDone) done := make(chan struct{}) go func() { var r stringVal err := client.Call(rpc.Request{"DelayedMethods", "1", "Delay"}, nil, &r) c.Check(err, gc.Equals, rpc.ErrShutdown) done <- struct{}{} }() chanRead(c, ready, "DelayedMethods.Delay ready") client.Close() select { case err := <-srvDone: c.Fatalf("server returned while outstanding operation in progress: %v", err) <-done case <-time.After(25 * time.Millisecond): } start <- "xxx" } func chanRead(c *gc.C, ch <-chan struct{}, what string) { select { case <-ch: return case <-time.After(3 * time.Second): c.Fatalf("timeout on channel read %s", what) } } func (*rpcSuite) TestCompatibility(c *gc.C) { root := &Root{ simple: make(map[string]*SimpleMethods), } a0 := &SimpleMethods{root: root, id: "a0"} root.simple["a0"] = a0 client, srvDone, _, _ := newRPCClientServer(c, root, nil, false) defer closeClient(c, client, srvDone) call := func(method string, arg, ret interface{}) (passedArg interface{}) { root.calls = nil err := client.Call(rpc.Request{"SimpleMethods", "a0", method}, arg, ret) c.Assert(err, gc.IsNil) c.Assert(root.calls, gc.HasLen, 1) info := root.calls[0] c.Assert(info.rcvr, gc.Equals, a0) c.Assert(info.method, gc.Equals, method) return info.arg } type extra struct { Val string Extra string } // Extra fields in request and response. var r extra arg := call("Call1r1", extra{"x", "y"}, &r) c.Assert(arg, gc.Equals, stringVal{"x"}) // Nil argument as request. r = extra{} arg = call("Call1r1", nil, &r) c.Assert(arg, gc.Equals, stringVal{}) // Nil argument as response. arg = call("Call1r1", stringVal{"x"}, nil) c.Assert(arg, gc.Equals, stringVal{"x"}) // Non-nil argument for no response. r = extra{} arg = call("Call1r0", stringVal{"x"}, &r) c.Assert(arg, gc.Equals, stringVal{"x"}) c.Assert(r, gc.Equals, extra{}) } func (*rpcSuite) TestBadCall(c *gc.C) { root := &Root{ simple: make(map[string]*SimpleMethods), } a0 := &SimpleMethods{root: root, id: "a0"} root.simple["a0"] = a0 client, srvDone, clientNotifier, serverNotifier := newRPCClientServer(c, root, nil, false) defer closeClient(c, client, srvDone) testBadCall(c, client, clientNotifier, serverNotifier, rpc.Request{"BadSomething", "a0", "No"}, `unknown object type "BadSomething"`, rpc.CodeNotImplemented, false, ) testBadCall(c, client, clientNotifier, serverNotifier, rpc.Request{"SimpleMethods", "xx", "No"}, "no such request - method SimpleMethods.No is not implemented", rpc.CodeNotImplemented, false, ) testBadCall(c, client, clientNotifier, serverNotifier, rpc.Request{"SimpleMethods", "xx", "Call0r0"}, `unknown SimpleMethods id`, "", true, ) } func testBadCall( c *gc.C, client *rpc.Conn, clientNotifier, serverNotifier *notifier, req rpc.Request, expectedErr string, expectedErrCode string, requestKnown bool, ) { clientNotifier.reset() serverNotifier.reset() err := client.Call(req, nil, nil) msg := expectedErr if expectedErrCode != "" { msg += " (" + expectedErrCode + ")" } c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta("request error: "+msg)) // Test that there was a notification for the client request. c.Assert(clientNotifier.clientRequests, gc.HasLen, 1) clientReq := clientNotifier.clientRequests[0] requestId := clientReq.hdr.RequestId c.Assert(clientReq, gc.DeepEquals, requestEvent{ hdr: rpc.Header{ RequestId: requestId, Request: req, }, body: struct{}{}, }) // Test that there was a notification for the client reply. c.Assert(clientNotifier.clientReplies, gc.HasLen, 1) clientReply := clientNotifier.clientReplies[0] c.Assert(clientReply, gc.DeepEquals, replyEvent{ req: req, hdr: rpc.Header{ RequestId: requestId, Error: expectedErr, ErrorCode: expectedErrCode, }, }) // Test that there was a notification for the server request. c.Assert(serverNotifier.serverRequests, gc.HasLen, 1) serverReq := serverNotifier.serverRequests[0] // From docs on ServerRequest: // If the request was not recognized or there was // an error reading the body, body will be nil. var expectBody interface{} if requestKnown { expectBody = struct{}{} } c.Assert(serverReq, gc.DeepEquals, requestEvent{ hdr: rpc.Header{ RequestId: requestId, Request: req, }, body: expectBody, }) // Test that there was a notification for the server reply. c.Assert(serverNotifier.serverReplies, gc.HasLen, 1) serverReply := serverNotifier.serverReplies[0] c.Assert(serverReply, gc.DeepEquals, replyEvent{ hdr: rpc.Header{ RequestId: requestId, Error: expectedErr, ErrorCode: expectedErrCode, }, req: req, body: struct{}{}, }) } func (*rpcSuite) TestContinueAfterReadBodyError(c *gc.C) { root := &Root{ simple: make(map[string]*SimpleMethods), } a0 := &SimpleMethods{root: root, id: "a0"} root.simple["a0"] = a0 client, srvDone, _, _ := newRPCClientServer(c, root, nil, false) defer closeClient(c, client, srvDone) var ret stringVal arg0 := struct { X map[string]int }{ X: map[string]int{"hello": 65}, } err := client.Call(rpc.Request{"SimpleMethods", "a0", "SliceArg"}, arg0, &ret) c.Assert(err, gc.ErrorMatches, `request error: json: cannot unmarshal object into Go value of type \[\]string`) err = client.Call(rpc.Request{"SimpleMethods", "a0", "SliceArg"}, arg0, &ret) c.Assert(err, gc.ErrorMatches, `request error: json: cannot unmarshal object into Go value of type \[\]string`) arg1 := struct { X []string }{ X: []string{"one"}, } err = client.Call(rpc.Request{"SimpleMethods", "a0", "SliceArg"}, arg1, &ret) c.Assert(err, gc.IsNil) c.Assert(ret.Val, gc.Equals, "SliceArg ret") } func (*rpcSuite) TestErrorAfterClientClose(c *gc.C) { client, srvDone, _, _ := newRPCClientServer(c, &Root{}, nil, false) err := client.Close() c.Assert(err, gc.IsNil) err = client.Call(rpc.Request{"Foo", "", "Bar"}, nil, nil) c.Assert(err, gc.Equals, rpc.ErrShutdown) err = chanReadError(c, srvDone, "server done") c.Assert(err, gc.IsNil) } func (*rpcSuite) TestClientCloseIdempotent(c *gc.C) { client, _, _, _ := newRPCClientServer(c, &Root{}, nil, false) err := client.Close() c.Assert(err, gc.IsNil) err = client.Close() c.Assert(err, gc.IsNil) err = client.Close() c.Assert(err, gc.IsNil) } type KillerRoot struct { killed bool Root } func (r *KillerRoot) Kill() { r.killed = true } func (*rpcSuite) TestRootIsKilled(c *gc.C) { root := &KillerRoot{} client, srvDone, _, _ := newRPCClientServer(c, root, nil, false) err := client.Close() c.Assert(err, gc.IsNil) err = chanReadError(c, srvDone, "server done") c.Assert(err, gc.IsNil) c.Assert(root.killed, gc.Equals, true) } func (*rpcSuite) TestBidirectional(c *gc.C) { srvRoot := &Root{} client, srvDone, _, _ := newRPCClientServer(c, srvRoot, nil, true) defer closeClient(c, client, srvDone) clientRoot := &Root{conn: client} client.Serve(clientRoot, nil) var r int64val err := client.Call(rpc.Request{"CallbackMethods", "", "Factorial"}, int64val{12}, &r) c.Assert(err, gc.IsNil) c.Assert(r.I, gc.Equals, int64(479001600)) } func (*rpcSuite) TestServerRequestWhenNotServing(c *gc.C) { srvRoot := &Root{} client, srvDone, _, _ := newRPCClientServer(c, srvRoot, nil, true) defer closeClient(c, client, srvDone) var r int64val err := client.Call(rpc.Request{"CallbackMethods", "", "Factorial"}, int64val{12}, &r) c.Assert(err, gc.ErrorMatches, "request error: request error: no service") } func (*rpcSuite) TestChangeAPI(c *gc.C) { srvRoot := &Root{} client, srvDone, _, _ := newRPCClientServer(c, srvRoot, nil, true) defer closeClient(c, client, srvDone) var s stringVal err := client.Call(rpc.Request{"NewlyAvailable", "", "NewMethod"}, nil, &s) c.Assert(err, gc.ErrorMatches, `request error: unknown object type "NewlyAvailable" \(not implemented\)`) err = client.Call(rpc.Request{"ChangeAPIMethods", "", "ChangeAPI"}, nil, nil) c.Assert(err, gc.IsNil) err = client.Call(rpc.Request{"ChangeAPIMethods", "", "ChangeAPI"}, nil, nil) c.Assert(err, gc.ErrorMatches, `request error: unknown object type "ChangeAPIMethods" \(not implemented\)`) err = client.Call(rpc.Request{"NewlyAvailable", "", "NewMethod"}, nil, &s) c.Assert(err, gc.IsNil) c.Assert(s, gc.Equals, stringVal{"new method result"}) } func (*rpcSuite) TestChangeAPIToNil(c *gc.C) { srvRoot := &Root{} client, srvDone, _, _ := newRPCClientServer(c, srvRoot, nil, true) defer closeClient(c, client, srvDone) err := client.Call(rpc.Request{"ChangeAPIMethods", "", "RemoveAPI"}, nil, nil) c.Assert(err, gc.IsNil) err = client.Call(rpc.Request{"ChangeAPIMethods", "", "RemoveAPI"}, nil, nil) c.Assert(err, gc.ErrorMatches, "request error: no service") } func (*rpcSuite) TestChangeAPIWhileServingRequest(c *gc.C) { ready := make(chan struct{}) done := make(chan error) srvRoot := &Root{ delayed: map[string]*DelayedMethods{ "1": {ready: ready, doneError: done}, }, } transform := func(err error) error { return fmt.Errorf("transformed: %v", err) } client, srvDone, _, _ := newRPCClientServer(c, srvRoot, transform, true) defer closeClient(c, client, srvDone) result := make(chan error) go func() { result <- client.Call(rpc.Request{"DelayedMethods", "1", "Delay"}, nil, nil) }() chanRead(c, ready, "method ready") err := client.Call(rpc.Request{"ChangeAPIMethods", "", "ChangeAPI"}, nil, nil) c.Assert(err, gc.IsNil) // Ensure that not only does the request in progress complete, // but that the original transformErrors function is called. done <- fmt.Errorf("an error") select { case r := <-result: c.Assert(r, gc.ErrorMatches, "request error: transformed: an error") case <-time.After(3 * time.Second): c.Fatalf("timeout on channel read") } } func chanReadError(c *gc.C, ch <-chan error, what string) error { select { case e := <-ch: return e case <-time.After(3 * time.Second): c.Fatalf("timeout on channel read %s", what) } panic("unreachable") } // newRPCClientServer starts an RPC server serving a connection from a // single client. When the server has finished serving the connection, // it sends a value on the returned channel. // If bidir is true, requests can flow in both directions. func newRPCClientServer(c *gc.C, root interface{}, tfErr func(error) error, bidir bool) (client *rpc.Conn, srvDone chan error, clientNotifier, serverNotifier *notifier) { l, err := net.Listen("tcp", "127.0.0.1:0") c.Assert(err, gc.IsNil) srvDone = make(chan error, 1) clientNotifier = new(notifier) serverNotifier = new(notifier) go func() { conn, err := l.Accept() if err != nil { srvDone <- nil return } defer l.Close() role := roleServer if bidir { role = roleBoth } rpcConn := rpc.NewConn(NewJSONCodec(conn, role), serverNotifier) rpcConn.Serve(root, tfErr) if root, ok := root.(*Root); ok { root.conn = rpcConn } rpcConn.Start() <-rpcConn.Dead() srvDone <- rpcConn.Close() }() conn, err := net.Dial("tcp", l.Addr().String()) c.Assert(err, gc.IsNil) role := roleClient if bidir { role = roleBoth } client = rpc.NewConn(NewJSONCodec(conn, role), clientNotifier) client.Start() return client, srvDone, clientNotifier, serverNotifier } func closeClient(c *gc.C, client *rpc.Conn, srvDone <-chan error) { err := client.Close() c.Assert(err, gc.IsNil) err = chanReadError(c, srvDone, "server done") c.Assert(err, gc.IsNil) } type encoder interface { Encode(e interface{}) error } type decoder interface { Decode(e interface{}) error } // testCodec wraps an rpc.Codec with extra error checking code. type testCodec struct { role connRole rpc.Codec } func (c *testCodec) WriteMessage(hdr *rpc.Header, x interface{}) error { if reflect.ValueOf(x).Kind() != reflect.Struct { panic(fmt.Errorf("WriteRequest bad param; want struct got %T (%#v)", x, x)) } if c.role != roleBoth && hdr.IsRequest() != (c.role == roleClient) { panic(fmt.Errorf("codec role %v; header wrong type %#v", c.role, hdr)) } log.Infof("send header: %#v; body: %#v", hdr, x) return c.Codec.WriteMessage(hdr, x) } func (c *testCodec) ReadHeader(hdr *rpc.Header) error { err := c.Codec.ReadHeader(hdr) if err != nil { return err } log.Infof("got header %#v", hdr) if c.role != roleBoth && hdr.IsRequest() == (c.role == roleClient) { panic(fmt.Errorf("codec role %v; read wrong type %#v", c.role, hdr)) } return nil } func (c *testCodec) ReadBody(r interface{}, isRequest bool) error { if v := reflect.ValueOf(r); v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { panic(fmt.Errorf("ReadResponseBody bad destination; want *struct got %T", r)) } if c.role != roleBoth && isRequest == (c.role == roleClient) { panic(fmt.Errorf("codec role %v; read wrong body type %#v", c.role, r)) } // Note: this will need to change if we want to test a non-JSON codec. var m json.RawMessage err := c.Codec.ReadBody(&m, isRequest) if err != nil { return err } log.Infof("got response body: %q", m) err = json.Unmarshal(m, r) log.Infof("unmarshalled into %#v", r) return err } type connRole string const ( roleBoth connRole = "both" roleClient connRole = "client" roleServer connRole = "server" ) func NewJSONCodec(c net.Conn, role connRole) rpc.Codec { return &testCodec{ role: role, Codec: jsoncodec.NewNet(c), } } type requestEvent struct { hdr rpc.Header body interface{} } type replyEvent struct { req rpc.Request hdr rpc.Header body interface{} } type notifier struct { mu sync.Mutex serverRequests []requestEvent serverReplies []replyEvent clientRequests []requestEvent clientReplies []replyEvent } func (n *notifier) reset() { n.mu.Lock() defer n.mu.Unlock() n.serverRequests = nil n.serverReplies = nil n.clientRequests = nil n.clientReplies = nil } func (n *notifier) ServerRequest(hdr *rpc.Header, body interface{}) { n.mu.Lock() defer n.mu.Unlock() n.serverRequests = append(n.serverRequests, requestEvent{ hdr: *hdr, body: body, }) } func (n *notifier) ServerReply(req rpc.Request, hdr *rpc.Header, body interface{}, timeSpent time.Duration) { n.mu.Lock() defer n.mu.Unlock() n.serverReplies = append(n.serverReplies, replyEvent{ req: req, hdr: *hdr, body: body, }) } func (n *notifier) ClientRequest(hdr *rpc.Header, body interface{}) { n.mu.Lock() defer n.mu.Unlock() n.clientRequests = append(n.clientRequests, requestEvent{ hdr: *hdr, body: body, }) } func (n *notifier) ClientReply(req rpc.Request, hdr *rpc.Header, body interface{}) { n.mu.Lock() defer n.mu.Unlock() n.clientReplies = append(n.clientReplies, replyEvent{ req: req, hdr: *hdr, body: body, }) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/�������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�021772� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/service_test.go����������������������������������0000644�0000153�0000161�00000120327�12321735642�025040� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "fmt" "sort" jc "github.com/juju/testing/checkers" "labix.org/v2/mgo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/testing" ) type ServiceSuite struct { ConnSuite charm *state.Charm mysql *state.Service } var _ = gc.Suite(&ServiceSuite{}) func (s *ServiceSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) s.charm = s.AddTestingCharm(c, "mysql") s.mysql = s.AddTestingService(c, "mysql", s.charm) } func (s *ServiceSuite) TestSetCharm(c *gc.C) { ch, force, err := s.mysql.Charm() c.Assert(err, gc.IsNil) c.Assert(ch.URL(), gc.DeepEquals, s.charm.URL()) c.Assert(force, gc.Equals, false) url, force := s.mysql.CharmURL() c.Assert(url, gc.DeepEquals, s.charm.URL()) c.Assert(force, gc.Equals, false) // Add a compatible charm and force it. sch := s.AddMetaCharm(c, "mysql", metaBase, 2) // revno 1 is used by SetUpSuite err = s.mysql.SetCharm(sch, true) c.Assert(err, gc.IsNil) ch, force, err = s.mysql.Charm() c.Assert(err, gc.IsNil) c.Assert(ch.URL(), gc.DeepEquals, sch.URL()) c.Assert(force, gc.Equals, true) url, force = s.mysql.CharmURL() c.Assert(url, gc.DeepEquals, sch.URL()) c.Assert(force, gc.Equals, true) // SetCharm fails when the service is Dying. _, err = s.mysql.AddUnit() c.Assert(err, gc.IsNil) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) err = s.mysql.SetCharm(sch, true) c.Assert(err, gc.ErrorMatches, `service "mysql" is not alive`) } func (s *ServiceSuite) TestSetCharmErrors(c *gc.C) { logging := s.AddTestingCharm(c, "logging") err := s.mysql.SetCharm(logging, false) c.Assert(err, gc.ErrorMatches, "cannot change a service's subordinacy") othermysql := s.AddSeriesCharm(c, "mysql", "otherseries") err = s.mysql.SetCharm(othermysql, false) c.Assert(err, gc.ErrorMatches, "cannot change a service's series") } var metaBase = ` name: mysql summary: "Fake MySQL Database engine" description: "Complete with nonsense relations" provides: server: mysql requires: client: mysql peers: cluster: mysql ` var metaDifferentProvider = ` name: mysql description: none summary: none provides: kludge: mysql requires: client: mysql peers: cluster: mysql ` var metaDifferentRequirer = ` name: mysql description: none summary: none provides: server: mysql requires: kludge: mysql peers: cluster: mysql ` var metaDifferentPeer = ` name: mysql description: none summary: none provides: server: mysql requires: client: mysql peers: kludge: mysql ` var metaExtraEndpoints = ` name: mysql description: none summary: none provides: server: mysql foo: bar requires: client: mysql baz: woot peers: cluster: mysql just: me ` var setCharmEndpointsTests = []struct { summary string meta string err string }{ { summary: "different provider (but no relation yet)", meta: metaDifferentProvider, }, { summary: "different requirer (but no relation yet)", meta: metaDifferentRequirer, }, { summary: "different peer", meta: metaDifferentPeer, err: `cannot upgrade service "fakemysql" to charm "local:quantal/quantal-mysql-5": would break relation "fakemysql:cluster"`, }, { summary: "same relations ok", meta: metaBase, }, { summary: "extra endpoints ok", meta: metaExtraEndpoints, }, } func (s *ServiceSuite) TestSetCharmChecksEndpointsWithoutRelations(c *gc.C) { revno := 2 // 1 is used in SetUpSuite ms := s.AddMetaCharm(c, "mysql", metaBase, revno) svc := s.AddTestingService(c, "fakemysql", ms) err := svc.SetCharm(ms, false) c.Assert(err, gc.IsNil) for i, t := range setCharmEndpointsTests { c.Logf("test %d: %s", i, t.summary) newCh := s.AddMetaCharm(c, "mysql", t.meta, revno+i+1) err = svc.SetCharm(newCh, false) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) } else { c.Assert(err, gc.IsNil) } } err = svc.Destroy() c.Assert(err, gc.IsNil) } func (s *ServiceSuite) TestSetCharmChecksEndpointsWithRelations(c *gc.C) { revno := 2 // 1 is used by SetUpSuite providerCharm := s.AddMetaCharm(c, "mysql", metaDifferentProvider, revno) providerSvc := s.AddTestingService(c, "myprovider", providerCharm) err := providerSvc.SetCharm(providerCharm, false) c.Assert(err, gc.IsNil) revno++ requirerCharm := s.AddMetaCharm(c, "mysql", metaDifferentRequirer, revno) requirerSvc := s.AddTestingService(c, "myrequirer", requirerCharm) err = requirerSvc.SetCharm(requirerCharm, false) c.Assert(err, gc.IsNil) eps, err := s.State.InferEndpoints([]string{"myprovider:kludge", "myrequirer:kludge"}) c.Assert(err, gc.IsNil) _, err = s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) revno++ baseCharm := s.AddMetaCharm(c, "mysql", metaBase, revno) err = providerSvc.SetCharm(baseCharm, false) c.Assert(err, gc.ErrorMatches, `cannot upgrade service "myprovider" to charm "local:quantal/quantal-mysql-4": would break relation "myrequirer:kludge myprovider:kludge"`) err = requirerSvc.SetCharm(baseCharm, false) c.Assert(err, gc.ErrorMatches, `cannot upgrade service "myrequirer" to charm "local:quantal/quantal-mysql-4": would break relation "myrequirer:kludge myprovider:kludge"`) } var stringConfig = ` options: key: {default: My Key, description: Desc, type: string} ` var emptyConfig = ` options: {} ` var floatConfig = ` options: key: {default: 0.42, description: Float key, type: float} ` var newStringConfig = ` options: key: {default: My Key, description: Desc, type: string} other: {default: None, description: My Other, type: string} ` var setCharmConfigTests = []struct { summary string startconfig string startvalues charm.Settings endconfig string endvalues charm.Settings err string }{ { summary: "add float key to empty config", startconfig: emptyConfig, endconfig: floatConfig, }, { summary: "add string key to empty config", startconfig: emptyConfig, endconfig: stringConfig, }, { summary: "add string key and preserve existing values", startconfig: stringConfig, startvalues: charm.Settings{"key": "foo"}, endconfig: newStringConfig, endvalues: charm.Settings{"key": "foo"}, }, { summary: "remove string key", startconfig: stringConfig, startvalues: charm.Settings{"key": "value"}, endconfig: emptyConfig, }, { summary: "remove float key", startconfig: floatConfig, startvalues: charm.Settings{"key": 123.45}, endconfig: emptyConfig, }, { summary: "change key type without values", startconfig: stringConfig, endconfig: floatConfig, }, { summary: "change key type with values", startconfig: stringConfig, startvalues: charm.Settings{"key": "value"}, endconfig: floatConfig, }, } func (s *ServiceSuite) TestSetCharmConfig(c *gc.C) { charms := map[string]*state.Charm{ stringConfig: s.AddConfigCharm(c, "wordpress", stringConfig, 1), emptyConfig: s.AddConfigCharm(c, "wordpress", emptyConfig, 2), floatConfig: s.AddConfigCharm(c, "wordpress", floatConfig, 3), newStringConfig: s.AddConfigCharm(c, "wordpress", newStringConfig, 4), } for i, t := range setCharmConfigTests { c.Logf("test %d: %s", i, t.summary) origCh := charms[t.startconfig] svc := s.AddTestingService(c, "wordpress", origCh) err := svc.UpdateConfigSettings(t.startvalues) c.Assert(err, gc.IsNil) newCh := charms[t.endconfig] err = svc.SetCharm(newCh, false) var expectVals charm.Settings var expectCh *state.Charm if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) expectCh = origCh expectVals = t.startvalues } else { c.Assert(err, gc.IsNil) expectCh = newCh expectVals = t.endvalues } sch, _, err := svc.Charm() c.Assert(err, gc.IsNil) c.Assert(sch.URL(), gc.DeepEquals, expectCh.URL()) settings, err := svc.ConfigSettings() c.Assert(err, gc.IsNil) if len(expectVals) == 0 { c.Assert(settings, gc.HasLen, 0) } else { c.Assert(settings, gc.DeepEquals, expectVals) } err = svc.Destroy() c.Assert(err, gc.IsNil) } } var serviceUpdateConfigSettingsTests = []struct { about string initial charm.Settings update charm.Settings expect charm.Settings err string }{{ about: "unknown option", update: charm.Settings{"foo": "bar"}, err: `unknown option "foo"`, }, { about: "bad type", update: charm.Settings{"skill-level": "profound"}, err: `option "skill-level" expected int, got "profound"`, }, { about: "set string", update: charm.Settings{"outlook": "positive"}, expect: charm.Settings{"outlook": "positive"}, }, { about: "unset string and set another", initial: charm.Settings{"outlook": "positive"}, update: charm.Settings{"outlook": nil, "title": "sir"}, expect: charm.Settings{"title": "sir"}, }, { about: "unset missing string", update: charm.Settings{"outlook": nil}, }, { about: `empty strings are valid`, initial: charm.Settings{"outlook": "positive"}, update: charm.Settings{"outlook": "", "title": ""}, expect: charm.Settings{"outlook": "", "title": ""}, }, { about: "preserve existing value", initial: charm.Settings{"title": "sir"}, update: charm.Settings{"username": "admin001"}, expect: charm.Settings{"username": "admin001", "title": "sir"}, }, { about: "unset a default value, set a different default", initial: charm.Settings{"username": "admin001", "title": "sir"}, update: charm.Settings{"username": nil, "title": "My Title"}, expect: charm.Settings{"title": "My Title"}, }, { about: "non-string type", update: charm.Settings{"skill-level": 303}, expect: charm.Settings{"skill-level": int64(303)}, }, { about: "unset non-string type", initial: charm.Settings{"skill-level": 303}, update: charm.Settings{"skill-level": nil}, }} func (s *ServiceSuite) TestUpdateConfigSettings(c *gc.C) { sch := s.AddTestingCharm(c, "dummy") for i, t := range serviceUpdateConfigSettingsTests { c.Logf("test %d. %s", i, t.about) svc := s.AddTestingService(c, "dummy-service", sch) if t.initial != nil { err := svc.UpdateConfigSettings(t.initial) c.Assert(err, gc.IsNil) } err := svc.UpdateConfigSettings(t.update) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) } else { c.Assert(err, gc.IsNil) settings, err := svc.ConfigSettings() c.Assert(err, gc.IsNil) expect := t.expect if expect == nil { expect = charm.Settings{} } c.Assert(settings, gc.DeepEquals, expect) } err = svc.Destroy() c.Assert(err, gc.IsNil) } } func (s *ServiceSuite) TestSettingsRefCountWorks(c *gc.C) { oldCh := s.AddConfigCharm(c, "wordpress", emptyConfig, 1) newCh := s.AddConfigCharm(c, "wordpress", emptyConfig, 2) svcName := "mywp" assertNoRef := func(sch *state.Charm) { _, err := state.ServiceSettingsRefCount(s.State, svcName, sch.URL()) c.Assert(err, gc.Equals, mgo.ErrNotFound) } assertRef := func(sch *state.Charm, refcount int) { rc, err := state.ServiceSettingsRefCount(s.State, svcName, sch.URL()) c.Assert(err, gc.IsNil) c.Assert(rc, gc.Equals, refcount) } assertNoRef(oldCh) assertNoRef(newCh) svc := s.AddTestingService(c, svcName, oldCh) assertRef(oldCh, 1) assertNoRef(newCh) err := svc.SetCharm(oldCh, false) c.Assert(err, gc.IsNil) assertRef(oldCh, 1) assertNoRef(newCh) err = svc.SetCharm(newCh, false) c.Assert(err, gc.IsNil) assertNoRef(oldCh) assertRef(newCh, 1) err = svc.SetCharm(oldCh, false) c.Assert(err, gc.IsNil) assertRef(oldCh, 1) assertNoRef(newCh) u, err := svc.AddUnit() c.Assert(err, gc.IsNil) curl, ok := u.CharmURL() c.Assert(ok, gc.Equals, false) assertRef(oldCh, 1) assertNoRef(newCh) err = u.SetCharmURL(oldCh.URL()) c.Assert(err, gc.IsNil) curl, ok = u.CharmURL() c.Assert(ok, gc.Equals, true) c.Assert(curl, gc.DeepEquals, oldCh.URL()) assertRef(oldCh, 2) assertNoRef(newCh) err = u.EnsureDead() c.Assert(err, gc.IsNil) assertRef(oldCh, 2) assertNoRef(newCh) err = u.Remove() c.Assert(err, gc.IsNil) assertRef(oldCh, 1) assertNoRef(newCh) err = svc.Destroy() c.Assert(err, gc.IsNil) assertNoRef(oldCh) assertNoRef(newCh) } const mysqlBaseMeta = ` name: mysql summary: "Database engine" description: "A pretty popular database" provides: server: mysql ` const onePeerMeta = ` peers: cluster: mysql ` const twoPeersMeta = ` peers: cluster: mysql loadbalancer: phony ` func (s *ServiceSuite) assertServiceRelations(c *gc.C, svc *state.Service, expectedKeys ...string) []*state.Relation { rels, err := svc.Relations() c.Assert(err, gc.IsNil) if len(rels) == 0 { return nil } relKeys := make([]string, len(expectedKeys)) for i, rel := range rels { relKeys[i] = rel.String() } sort.Strings(relKeys) c.Assert(relKeys, gc.DeepEquals, expectedKeys) return rels } func (s *ServiceSuite) TestNewPeerRelationsAddedOnUpgrade(c *gc.C) { // Original mysql charm has no peer relations. oldCh := s.AddMetaCharm(c, "mysql", mysqlBaseMeta+onePeerMeta, 2) newCh := s.AddMetaCharm(c, "mysql", mysqlBaseMeta+twoPeersMeta, 3) // No relations joined yet. s.assertServiceRelations(c, s.mysql) err := s.mysql.SetCharm(oldCh, false) c.Assert(err, gc.IsNil) s.assertServiceRelations(c, s.mysql, "mysql:cluster") err = s.mysql.SetCharm(newCh, false) c.Assert(err, gc.IsNil) rels := s.assertServiceRelations(c, s.mysql, "mysql:cluster", "mysql:loadbalancer") // Check state consistency by attempting to destroy the service. err = s.mysql.Destroy() c.Assert(err, gc.IsNil) // Check the peer relations got destroyed as well. for _, rel := range rels { err = rel.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } } func jujuInfoEp(serviceName string) state.Endpoint { return state.Endpoint{ ServiceName: serviceName, Relation: charm.Relation{ Interface: "juju-info", Name: "juju-info", Role: charm.RoleProvider, Scope: charm.ScopeGlobal, }, } } func (s *ServiceSuite) TestTag(c *gc.C) { c.Assert(s.mysql.Tag(), gc.Equals, "service-mysql") } func (s *ServiceSuite) TestMysqlEndpoints(c *gc.C) { _, err := s.mysql.Endpoint("mysql") c.Assert(err, gc.ErrorMatches, `service "mysql" has no "mysql" relation`) jiEP, err := s.mysql.Endpoint("juju-info") c.Assert(err, gc.IsNil) c.Assert(jiEP, gc.DeepEquals, jujuInfoEp("mysql")) serverEP, err := s.mysql.Endpoint("server") c.Assert(err, gc.IsNil) c.Assert(serverEP, gc.DeepEquals, state.Endpoint{ ServiceName: "mysql", Relation: charm.Relation{ Interface: "mysql", Name: "server", Role: charm.RoleProvider, Scope: charm.ScopeGlobal, }, }) eps, err := s.mysql.Endpoints() c.Assert(err, gc.IsNil) c.Assert(eps, gc.DeepEquals, []state.Endpoint{jiEP, serverEP}) } func (s *ServiceSuite) TestRiakEndpoints(c *gc.C) { riak := s.AddTestingService(c, "myriak", s.AddTestingCharm(c, "riak")) _, err := riak.Endpoint("garble") c.Assert(err, gc.ErrorMatches, `service "myriak" has no "garble" relation`) jiEP, err := riak.Endpoint("juju-info") c.Assert(err, gc.IsNil) c.Assert(jiEP, gc.DeepEquals, jujuInfoEp("myriak")) ringEP, err := riak.Endpoint("ring") c.Assert(err, gc.IsNil) c.Assert(ringEP, gc.DeepEquals, state.Endpoint{ ServiceName: "myriak", Relation: charm.Relation{ Interface: "riak", Name: "ring", Role: charm.RolePeer, Scope: charm.ScopeGlobal, Limit: 1, }, }) adminEP, err := riak.Endpoint(state.AdminUser) c.Assert(err, gc.IsNil) c.Assert(adminEP, gc.DeepEquals, state.Endpoint{ ServiceName: "myriak", Relation: charm.Relation{ Interface: "http", Name: state.AdminUser, Role: charm.RoleProvider, Scope: charm.ScopeGlobal, }, }) endpointEP, err := riak.Endpoint("endpoint") c.Assert(err, gc.IsNil) c.Assert(endpointEP, gc.DeepEquals, state.Endpoint{ ServiceName: "myriak", Relation: charm.Relation{ Interface: "http", Name: "endpoint", Role: charm.RoleProvider, Scope: charm.ScopeGlobal, }, }) eps, err := riak.Endpoints() c.Assert(err, gc.IsNil) c.Assert(eps, gc.DeepEquals, []state.Endpoint{adminEP, endpointEP, jiEP, ringEP}) } func (s *ServiceSuite) TestWordpressEndpoints(c *gc.C) { wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) _, err := wordpress.Endpoint("nonsense") c.Assert(err, gc.ErrorMatches, `service "wordpress" has no "nonsense" relation`) jiEP, err := wordpress.Endpoint("juju-info") c.Assert(err, gc.IsNil) c.Assert(jiEP, gc.DeepEquals, jujuInfoEp("wordpress")) urlEP, err := wordpress.Endpoint("url") c.Assert(err, gc.IsNil) c.Assert(urlEP, gc.DeepEquals, state.Endpoint{ ServiceName: "wordpress", Relation: charm.Relation{ Interface: "http", Name: "url", Role: charm.RoleProvider, Scope: charm.ScopeGlobal, }, }) ldEP, err := wordpress.Endpoint("logging-dir") c.Assert(err, gc.IsNil) c.Assert(ldEP, gc.DeepEquals, state.Endpoint{ ServiceName: "wordpress", Relation: charm.Relation{ Interface: "logging", Name: "logging-dir", Role: charm.RoleProvider, Scope: charm.ScopeContainer, }, }) mpEP, err := wordpress.Endpoint("monitoring-port") c.Assert(err, gc.IsNil) c.Assert(mpEP, gc.DeepEquals, state.Endpoint{ ServiceName: "wordpress", Relation: charm.Relation{ Interface: "monitoring", Name: "monitoring-port", Role: charm.RoleProvider, Scope: charm.ScopeContainer, }, }) dbEP, err := wordpress.Endpoint("db") c.Assert(err, gc.IsNil) c.Assert(dbEP, gc.DeepEquals, state.Endpoint{ ServiceName: "wordpress", Relation: charm.Relation{ Interface: "mysql", Name: "db", Role: charm.RoleRequirer, Scope: charm.ScopeGlobal, Limit: 1, }, }) cacheEP, err := wordpress.Endpoint("cache") c.Assert(err, gc.IsNil) c.Assert(cacheEP, gc.DeepEquals, state.Endpoint{ ServiceName: "wordpress", Relation: charm.Relation{ Interface: "varnish", Name: "cache", Role: charm.RoleRequirer, Scope: charm.ScopeGlobal, Limit: 2, Optional: true, }, }) eps, err := wordpress.Endpoints() c.Assert(err, gc.IsNil) c.Assert(eps, gc.DeepEquals, []state.Endpoint{cacheEP, dbEP, jiEP, ldEP, mpEP, urlEP}) } func (s *ServiceSuite) TestServiceRefresh(c *gc.C) { s1, err := s.State.Service(s.mysql.Name()) c.Assert(err, gc.IsNil) err = s.mysql.SetCharm(s.charm, true) c.Assert(err, gc.IsNil) testch, force, err := s1.Charm() c.Assert(err, gc.IsNil) c.Assert(force, gc.Equals, false) c.Assert(testch.URL(), gc.DeepEquals, s.charm.URL()) err = s1.Refresh() c.Assert(err, gc.IsNil) testch, force, err = s1.Charm() c.Assert(err, gc.IsNil) c.Assert(force, gc.Equals, true) c.Assert(testch.URL(), gc.DeepEquals, s.charm.URL()) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) err = s.mysql.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *ServiceSuite) TestServiceExposed(c *gc.C) { // Check that querying for the exposed flag works correctly. c.Assert(s.mysql.IsExposed(), gc.Equals, false) // Check that setting and clearing the exposed flag works correctly. err := s.mysql.SetExposed() c.Assert(err, gc.IsNil) c.Assert(s.mysql.IsExposed(), gc.Equals, true) err = s.mysql.ClearExposed() c.Assert(err, gc.IsNil) c.Assert(s.mysql.IsExposed(), gc.Equals, false) // Check that setting and clearing the exposed flag repeatedly does not fail. err = s.mysql.SetExposed() c.Assert(err, gc.IsNil) err = s.mysql.SetExposed() c.Assert(err, gc.IsNil) err = s.mysql.ClearExposed() c.Assert(err, gc.IsNil) err = s.mysql.ClearExposed() c.Assert(err, gc.IsNil) err = s.mysql.SetExposed() c.Assert(err, gc.IsNil) c.Assert(s.mysql.IsExposed(), gc.Equals, true) // Make the service Dying and check that ClearExposed and SetExposed fail. // TODO(fwereade): maybe service destruction should always unexpose? u, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) err = s.mysql.ClearExposed() c.Assert(err, gc.ErrorMatches, notAliveErr) err = s.mysql.SetExposed() c.Assert(err, gc.ErrorMatches, notAliveErr) // Remove the service and check that both fail. err = u.EnsureDead() c.Assert(err, gc.IsNil) err = u.Remove() c.Assert(err, gc.IsNil) err = s.mysql.SetExposed() c.Assert(err, gc.ErrorMatches, notAliveErr) err = s.mysql.ClearExposed() c.Assert(err, gc.ErrorMatches, notAliveErr) } func (s *ServiceSuite) TestAddUnit(c *gc.C) { // Check that principal units can be added on their own. unitZero, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) c.Assert(unitZero.Name(), gc.Equals, "mysql/0") c.Assert(unitZero.IsPrincipal(), gc.Equals, true) c.Assert(unitZero.SubordinateNames(), gc.HasLen, 0) unitOne, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) c.Assert(unitOne.Name(), gc.Equals, "mysql/1") c.Assert(unitOne.IsPrincipal(), gc.Equals, true) c.Assert(unitOne.SubordinateNames(), gc.HasLen, 0) // Assign the principal unit to a machine. m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = unitZero.AssignToMachine(m) c.Assert(err, gc.IsNil) // Add a subordinate service and check that units cannot be added directly. // to add a subordinate unit. subCharm := s.AddTestingCharm(c, "logging") logging := s.AddTestingService(c, "logging", subCharm) _, err = logging.AddUnit() c.Assert(err, gc.ErrorMatches, `cannot add unit to service "logging": service is a subordinate`) // Indirectly create a subordinate unit by adding a relation and entering // scope as a principal. eps, err := s.State.InferEndpoints([]string{"logging", "mysql"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(unitZero) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) subZero, err := s.State.Unit("logging/0") c.Assert(err, gc.IsNil) // Check that once it's refreshed unitZero has subordinates. err = unitZero.Refresh() c.Assert(err, gc.IsNil) c.Assert(unitZero.SubordinateNames(), gc.DeepEquals, []string{"logging/0"}) // Check the subordinate unit has been assigned its principal's machine. id, err := subZero.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(id, gc.Equals, m.Id()) } func (s *ServiceSuite) TestAddUnitWhenNotAlive(c *gc.C) { u, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) _, err = s.mysql.AddUnit() c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql": service is not alive`) err = u.EnsureDead() c.Assert(err, gc.IsNil) err = u.Remove() c.Assert(err, gc.IsNil) _, err = s.mysql.AddUnit() c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql": service "mysql" not found`) } func (s *ServiceSuite) TestReadUnit(c *gc.C) { _, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) _, err = s.mysql.AddUnit() c.Assert(err, gc.IsNil) // Check that retrieving a unit from the service works correctly. unit, err := s.mysql.Unit("mysql/0") c.Assert(err, gc.IsNil) c.Assert(unit.Name(), gc.Equals, "mysql/0") // Check that retrieving a unit from state works correctly. unit, err = s.State.Unit("mysql/0") c.Assert(err, gc.IsNil) c.Assert(unit.Name(), gc.Equals, "mysql/0") // Check that retrieving a non-existent or an invalidly // named unit fail nicely. unit, err = s.mysql.Unit("mysql") c.Assert(err, gc.ErrorMatches, `"mysql" is not a valid unit name`) unit, err = s.mysql.Unit("mysql/0/0") c.Assert(err, gc.ErrorMatches, `"mysql/0/0" is not a valid unit name`) unit, err = s.mysql.Unit("pressword/0") c.Assert(err, gc.ErrorMatches, `cannot get unit "pressword/0" from service "mysql": .*`) // Check direct state retrieval also fails nicely. unit, err = s.State.Unit("mysql") c.Assert(err, gc.ErrorMatches, `"mysql" is not a valid unit name`) unit, err = s.State.Unit("mysql/0/0") c.Assert(err, gc.ErrorMatches, `"mysql/0/0" is not a valid unit name`) unit, err = s.State.Unit("pressword/0") c.Assert(err, gc.ErrorMatches, `unit "pressword/0" not found`) // Add another service to check units are not misattributed. mysql := s.AddTestingService(c, "wordpress", s.charm) _, err = mysql.AddUnit() c.Assert(err, gc.IsNil) // BUG(aram): use error strings from state. unit, err = s.mysql.Unit("wordpress/0") c.Assert(err, gc.ErrorMatches, `cannot get unit "wordpress/0" from service "mysql": .*`) units, err := s.mysql.AllUnits() c.Assert(err, gc.IsNil) c.Assert(sortedUnitNames(units), gc.DeepEquals, []string{"mysql/0", "mysql/1"}) } func (s *ServiceSuite) TestReadUnitWhenDying(c *gc.C) { // Test that we can still read units when the service is Dying... unit, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) preventUnitDestroyRemove(c, unit) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) _, err = s.mysql.AllUnits() c.Assert(err, gc.IsNil) _, err = s.mysql.Unit("mysql/0") c.Assert(err, gc.IsNil) // ...and when those units are Dying or Dead... testWhenDying(c, unit, noErr, noErr, func() error { _, err := s.mysql.AllUnits() return err }, func() error { _, err := s.mysql.Unit("mysql/0") return err }) // ...and even, in a very limited way, when the service itself is removed. removeAllUnits(c, s.mysql) _, err = s.mysql.AllUnits() c.Assert(err, gc.IsNil) } func (s *ServiceSuite) TestDestroySimple(c *gc.C) { err := s.mysql.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.mysql.Life(), gc.Equals, state.Dying) err = s.mysql.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *ServiceSuite) TestDestroyStillHasUnits(c *gc.C) { unit, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.mysql.Life(), gc.Equals, state.Dying) err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = s.mysql.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.mysql.Life(), gc.Equals, state.Dying) err = unit.Remove() c.Assert(err, gc.IsNil) err = s.mysql.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *ServiceSuite) TestDestroyOnceHadUnits(c *gc.C) { unit, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.IsNil) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.mysql.Life(), gc.Equals, state.Dying) err = s.mysql.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *ServiceSuite) TestDestroyStaleNonZeroUnitCount(c *gc.C) { unit, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) err = s.mysql.Refresh() c.Assert(err, gc.IsNil) err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.IsNil) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.mysql.Life(), gc.Equals, state.Dying) err = s.mysql.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *ServiceSuite) TestDestroyStaleZeroUnitCount(c *gc.C) { unit, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.mysql.Life(), gc.Equals, state.Dying) err = s.mysql.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.mysql.Life(), gc.Equals, state.Dying) err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = s.mysql.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.mysql.Life(), gc.Equals, state.Dying) err = unit.Remove() c.Assert(err, gc.IsNil) err = s.mysql.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *ServiceSuite) TestDestroyWithRemovableRelation(c *gc.C) { wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) // Destroy a service with no units in relation scope; check service and // unit removed. err = wordpress.Destroy() c.Assert(err, gc.IsNil) err = wordpress.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = rel.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *ServiceSuite) TestDestroyWithReferencedRelation(c *gc.C) { s.assertDestroyWithReferencedRelation(c, true) } func (s *ServiceSuite) TestDestroyWithreferencedRelationStaleCount(c *gc.C) { s.assertDestroyWithReferencedRelation(c, false) } func (s *ServiceSuite) assertDestroyWithReferencedRelation(c *gc.C, refresh bool) { wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) c.Assert(err, gc.IsNil) rel0, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err = s.State.InferEndpoints([]string{"logging", "mysql"}) c.Assert(err, gc.IsNil) rel1, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) // Add a separate reference to the first relation. unit, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) ru, err := rel0.Unit(unit) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) // Optionally update the service document to get correct relation counts. if refresh { err = s.mysql.Destroy() c.Assert(err, gc.IsNil) } // Destroy, and check that the first relation becomes Dying... err = s.mysql.Destroy() c.Assert(err, gc.IsNil) err = rel0.Refresh() c.Assert(err, gc.IsNil) c.Assert(rel0.Life(), gc.Equals, state.Dying) // ...while the second is removed directly. err = rel1.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // Drop the last reference to the first relation; check the relation and // the service are are both removed. err = ru.LeaveScope() c.Assert(err, gc.IsNil) err = s.mysql.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = rel0.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *ServiceSuite) TestDestroyQueuesUnitCleanup(c *gc.C) { // Add 5 units; block quick-remove of mysql/1 and mysql/3 units := make([]*state.Unit, 5) for i := range units { unit, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) units[i] = unit if i%2 != 0 { preventUnitDestroyRemove(c, unit) } } // Check state is clean. dirty, err := s.State.NeedsCleanup() c.Assert(err, gc.IsNil) c.Assert(dirty, gc.Equals, false) // Destroy mysql, and check units are not touched. err = s.mysql.Destroy() c.Assert(err, gc.IsNil) for _, unit := range units { assertLife(c, unit, state.Alive) } // Check a cleanup doc was added. dirty, err = s.State.NeedsCleanup() c.Assert(err, gc.IsNil) c.Assert(dirty, gc.Equals, true) // Run the cleanup and check the units. err = s.State.Cleanup() c.Assert(err, gc.IsNil) for i, unit := range units { if i%2 != 0 { assertLife(c, unit, state.Dying) } else { assertRemoved(c, unit) } } // Check we're now clean. dirty, err = s.State.NeedsCleanup() c.Assert(err, gc.IsNil) c.Assert(dirty, gc.Equals, false) } func (s *ServiceSuite) TestReadUnitWithChangingState(c *gc.C) { // Check that reading a unit after removing the service // fails nicely. err := s.mysql.Destroy() c.Assert(err, gc.IsNil) err = s.mysql.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) _, err = s.State.Unit("mysql/0") c.Assert(err, gc.ErrorMatches, `unit "mysql/0" not found`) } func uint64p(val uint64) *uint64 { return &val } func (s *ServiceSuite) TestConstraints(c *gc.C) { // Constraints are initially empty (for now). cons, err := s.mysql.Constraints() c.Assert(err, gc.IsNil) c.Assert(&cons, jc.Satisfies, constraints.IsEmpty) // Constraints can be set. cons2 := constraints.Value{Mem: uint64p(4096)} err = s.mysql.SetConstraints(cons2) cons3, err := s.mysql.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons3, gc.DeepEquals, cons2) // Constraints are completely overwritten when re-set. cons4 := constraints.Value{CpuPower: uint64p(750)} err = s.mysql.SetConstraints(cons4) c.Assert(err, gc.IsNil) cons5, err := s.mysql.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons5, gc.DeepEquals, cons4) // Destroy the existing service; there's no way to directly assert // that the constraints are deleted... err = s.mysql.Destroy() c.Assert(err, gc.IsNil) err = s.mysql.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // ...but we can check that old constraints do not affect new services // with matching names. ch, _, err := s.mysql.Charm() c.Assert(err, gc.IsNil) mysql := s.AddTestingService(c, s.mysql.Name(), ch) cons6, err := mysql.Constraints() c.Assert(err, gc.IsNil) c.Assert(&cons6, jc.Satisfies, constraints.IsEmpty) } func (s *ServiceSuite) TestConstraintsLifecycle(c *gc.C) { // Dying. unit, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) cons1 := constraints.MustParse("mem=1G") err = s.mysql.SetConstraints(cons1) c.Assert(err, gc.ErrorMatches, `cannot set constraints: not found or not alive`) scons, err := s.mysql.Constraints() c.Assert(err, gc.IsNil) c.Assert(&scons, jc.Satisfies, constraints.IsEmpty) // Removed (== Dead, for a service). err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.IsNil) err = s.mysql.SetConstraints(cons1) c.Assert(err, gc.ErrorMatches, `cannot set constraints: not found or not alive`) _, err = s.mysql.Constraints() c.Assert(err, gc.ErrorMatches, `constraints not found`) } func (s *ServiceSuite) TestSubordinateConstraints(c *gc.C) { loggingCh := s.AddTestingCharm(c, "logging") logging := s.AddTestingService(c, "logging", loggingCh) _, err := logging.Constraints() c.Assert(err, gc.Equals, state.ErrSubordinateConstraints) err = logging.SetConstraints(constraints.Value{}) c.Assert(err, gc.Equals, state.ErrSubordinateConstraints) } func (s *ServiceSuite) TestWatchUnitsBulkEvents(c *gc.C) { // Alive unit... alive, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) // Dying unit... dying, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) preventUnitDestroyRemove(c, dying) err = dying.Destroy() c.Assert(err, gc.IsNil) // Dead unit... dead, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) preventUnitDestroyRemove(c, dead) err = dead.Destroy() c.Assert(err, gc.IsNil) err = dead.EnsureDead() c.Assert(err, gc.IsNil) // Gone unit. gone, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) err = gone.Destroy() c.Assert(err, gc.IsNil) // All except gone unit are reported in initial event. w := s.mysql.WatchUnits() defer testing.AssertStop(c, w) wc := testing.NewStringsWatcherC(c, s.State, w) wc.AssertChange(alive.Name(), dying.Name(), dead.Name()) wc.AssertNoChange() // Remove them all; alive/dying changes reported; dead never mentioned again. err = alive.Destroy() c.Assert(err, gc.IsNil) err = dying.EnsureDead() c.Assert(err, gc.IsNil) err = dying.Remove() c.Assert(err, gc.IsNil) err = dead.Remove() c.Assert(err, gc.IsNil) wc.AssertChange(alive.Name(), dying.Name()) wc.AssertNoChange() } func (s *ServiceSuite) TestWatchUnitsLifecycle(c *gc.C) { // Empty initial event when no units. w := s.mysql.WatchUnits() defer testing.AssertStop(c, w) wc := testing.NewStringsWatcherC(c, s.State, w) wc.AssertChange() wc.AssertNoChange() // Create one unit, check one change. quick, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) wc.AssertChange(quick.Name()) wc.AssertNoChange() // Destroy that unit (short-circuited to removal), check one change. err = quick.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange(quick.Name()) wc.AssertNoChange() // Create another, check one change. slow, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) wc.AssertChange(slow.Name()) wc.AssertNoChange() // Change unit itself, no change. preventUnitDestroyRemove(c, slow) wc.AssertNoChange() // Make unit Dying, change detected. err = slow.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange(slow.Name()) wc.AssertNoChange() // Make unit Dead, change detected. err = slow.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertChange(slow.Name()) wc.AssertNoChange() // Remove unit, final change not detected. err = slow.Remove() c.Assert(err, gc.IsNil) wc.AssertNoChange() } func (s *ServiceSuite) TestWatchRelations(c *gc.C) { // TODO(fwereade) split this test up a bit. w := s.mysql.WatchRelations() defer testing.AssertStop(c, w) wc := testing.NewStringsWatcherC(c, s.State, w) wc.AssertChange() wc.AssertNoChange() // Add a relation; check change. mysqlep, err := s.mysql.Endpoint("server") c.Assert(err, gc.IsNil) wpch := s.AddTestingCharm(c, "wordpress") wpi := 0 addRelation := func() *state.Relation { name := fmt.Sprintf("wp%d", wpi) wpi++ wp := s.AddTestingService(c, name, wpch) wpep, err := wp.Endpoint("db") c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(mysqlep, wpep) c.Assert(err, gc.IsNil) return rel } rel0 := addRelation() wc.AssertChange(rel0.String()) wc.AssertNoChange() // Add another relation; check change. rel1 := addRelation() wc.AssertChange(rel1.String()) wc.AssertNoChange() // Destroy a relation; check change. err = rel0.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange(rel0.String()) wc.AssertNoChange() // Stop watcher; check change chan is closed. testing.AssertStop(c, w) wc.AssertClosed() // Add a new relation; start a new watcher; check initial event. rel2 := addRelation() w = s.mysql.WatchRelations() defer testing.AssertStop(c, w) wc = testing.NewStringsWatcherC(c, s.State, w) wc.AssertChange(rel1.String(), rel2.String()) wc.AssertNoChange() // Add a unit to the new relation; check no change. unit, err := s.mysql.AddUnit() c.Assert(err, gc.IsNil) ru2, err := rel2.Unit(unit) c.Assert(err, gc.IsNil) err = ru2.EnterScope(nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Destroy the relation with the unit in scope, and add another; check // changes. err = rel2.Destroy() c.Assert(err, gc.IsNil) rel3 := addRelation() wc.AssertChange(rel2.String(), rel3.String()) wc.AssertNoChange() // Leave scope, destroying the relation, and check that change as well. err = ru2.LeaveScope() c.Assert(err, gc.IsNil) wc.AssertChange(rel2.String()) wc.AssertNoChange() } func removeAllUnits(c *gc.C, s *state.Service) { us, err := s.AllUnits() c.Assert(err, gc.IsNil) for _, u := range us { err = u.EnsureDead() c.Assert(err, gc.IsNil) err = u.Remove() c.Assert(err, gc.IsNil) } } func (s *ServiceSuite) TestWatchService(c *gc.C) { w := s.mysql.Watch() defer testing.AssertStop(c, w) // Initial event. wc := testing.NewNotifyWatcherC(c, s.State, w) wc.AssertOneChange() // Make one change (to a separate instance), check one event. service, err := s.State.Service(s.mysql.Name()) c.Assert(err, gc.IsNil) err = service.SetExposed() c.Assert(err, gc.IsNil) wc.AssertOneChange() // Make two changes, check one event. err = service.ClearExposed() c.Assert(err, gc.IsNil) err = service.SetCharm(s.charm, true) c.Assert(err, gc.IsNil) wc.AssertOneChange() // Stop, check closed. testing.AssertStop(c, w) wc.AssertClosed() // Remove service, start new watch, check single event. err = service.Destroy() c.Assert(err, gc.IsNil) w = s.mysql.Watch() defer testing.AssertStop(c, w) testing.NewNotifyWatcherC(c, s.State, w).AssertOneChange() } func (s *ServiceSuite) TestAnnotatorForService(c *gc.C) { testAnnotator(c, func() (state.Annotator, error) { return s.State.Service("mysql") }) } func (s *ServiceSuite) TestAnnotationRemovalForService(c *gc.C) { annotations := map[string]string{"mykey": "myvalue"} err := s.mysql.SetAnnotations(annotations) c.Assert(err, gc.IsNil) err = s.mysql.Destroy() c.Assert(err, gc.IsNil) ann, err := s.mysql.Annotations() c.Assert(err, gc.IsNil) c.Assert(ann, gc.DeepEquals, make(map[string]string)) } // SCHEMACHANGE // TODO(mattyw) remove when schema upgrades are possible // Check that GetOwnerTag returns user-admin even // when the service has no owner func (s *ServiceSuite) TestOwnerTagSchemaProtection(c *gc.C) { service := s.AddTestingService(c, "foobar", s.charm) state.SetServiceOwnerTag(service, "") c.Assert(state.GetServiceOwnerTag(service), gc.Equals, "") c.Assert(service.GetOwnerTag(), gc.Equals, "user-admin") } func (s *ServiceSuite) TestNetworks(c *gc.C) { service, err := s.State.Service(s.mysql.Name()) c.Assert(err, gc.IsNil) include, exclude, err := service.Networks() c.Assert(err, gc.IsNil) c.Check(include, gc.HasLen, 0) c.Check(exclude, gc.HasLen, 0) } func (s *ServiceSuite) TestNetworksOnService(c *gc.C) { includeNetworks := []string{"yes", "on"} excludeNetworks := []string{"no", "off"} service := s.AddTestingServiceWithNetworks(c, "withnets", s.charm, includeNetworks, excludeNetworks) haveIncludeNetworks, haveExcludeNetworks, err := service.Networks() c.Assert(err, gc.IsNil) c.Check(haveIncludeNetworks, gc.DeepEquals, includeNetworks) c.Check(haveExcludeNetworks, gc.DeepEquals, excludeNetworks) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/interface.go�������������������������������������0000644�0000153�0000161�00000010362�12321735642�024276� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) // EntityFinder is implemented by *State. See State.FindEntity // for documentation on the method. type EntityFinder interface { FindEntity(tag string) (Entity, error) } var _ EntityFinder = (*State)(nil) // Entity represents any entity that can be returned // by State.FindEntity. All entities have a tag. type Entity interface { Tag() string } var ( _ Entity = (*Machine)(nil) _ Entity = (*Unit)(nil) _ Entity = (*Service)(nil) _ Entity = (*Environment)(nil) _ Entity = (*User)(nil) ) type StatusSetter interface { SetStatus(status params.Status, info string, data params.StatusData) error } type StatusGetter interface { Status() (status params.Status, info string, data params.StatusData, err error) } var ( _ StatusSetter = (*Machine)(nil) _ StatusSetter = (*Unit)(nil) _ StatusGetter = (*Machine)(nil) _ StatusGetter = (*Unit)(nil) ) // Lifer represents an entity with a life. type Lifer interface { Life() Life } var ( _ Lifer = (*Machine)(nil) _ Lifer = (*Unit)(nil) _ Lifer = (*Service)(nil) _ Lifer = (*Relation)(nil) ) // AgentTooler is implemented by entities // that have associated agent tools. type AgentTooler interface { AgentTools() (*tools.Tools, error) SetAgentVersion(version.Binary) error } // EnsureDeader with an EnsureDead method. type EnsureDeader interface { EnsureDead() error } var ( _ EnsureDeader = (*Machine)(nil) _ EnsureDeader = (*Unit)(nil) ) // Remover represents entities with a Remove method. type Remover interface { Remove() error } var ( _ Remover = (*Machine)(nil) _ Remover = (*Unit)(nil) ) // Authenticator represents entites capable of handling password // authentication. type Authenticator interface { Refresh() error SetPassword(pass string) error PasswordValid(pass string) bool } var ( _ Authenticator = (*Machine)(nil) _ Authenticator = (*Unit)(nil) _ Authenticator = (*User)(nil) ) // MongoPassworder represents an entity that can // have a mongo password set for it. type MongoPassworder interface { SetMongoPassword(password string) error } var ( _ MongoPassworder = (*Machine)(nil) _ MongoPassworder = (*Unit)(nil) ) // Annotator represents entities capable of handling annotations. type Annotator interface { Annotation(key string) (string, error) Annotations() (map[string]string, error) SetAnnotations(pairs map[string]string) error } var ( _ Annotator = (*Machine)(nil) _ Annotator = (*Unit)(nil) _ Annotator = (*Service)(nil) _ Annotator = (*Environment)(nil) ) // NotifyWatcherFactory represents an entity that // can be watched. type NotifyWatcherFactory interface { Watch() NotifyWatcher } var ( _ NotifyWatcherFactory = (*Machine)(nil) _ NotifyWatcherFactory = (*Unit)(nil) _ NotifyWatcherFactory = (*Service)(nil) _ NotifyWatcherFactory = (*Environment)(nil) ) // AgentEntity represents an entity that can // have an agent responsible for it. type AgentEntity interface { Entity Lifer Authenticator MongoPassworder AgentTooler StatusSetter EnsureDeader Remover NotifyWatcherFactory } var ( _ AgentEntity = (*Machine)(nil) _ AgentEntity = (*Unit)(nil) ) // EnvironAccessor defines the methods needed to watch for environment // config changes, and read the environment config. type EnvironAccessor interface { WatchForEnvironConfigChanges() NotifyWatcher EnvironConfig() (*config.Config, error) } var _ EnvironAccessor = (*State)(nil) // UnitsWatcher defines the methods needed to retrieve an entity (a // machine or a service) and watch its units. type UnitsWatcher interface { Entity WatchUnits() StringsWatcher } var _ UnitsWatcher = (*Machine)(nil) var _ UnitsWatcher = (*Service)(nil) // EnvironMachinesWatcher defines a single method - // WatchEnvironMachines. type EnvironMachinesWatcher interface { WatchEnvironMachines() StringsWatcher } var _ EnvironMachinesWatcher = (*State)(nil) // InstanceIdGetter defines a single method - InstanceId. type InstanceIdGetter interface { InstanceId() (instance.Id, error) } var _ InstanceIdGetter = (*Machine)(nil) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/container.go�������������������������������������0000644�0000153�0000161�00000005422�12321735642�024321� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "strings" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/instance" ) // machineContainers holds the machine ids of all the containers belonging to a parent machine. // All machines have an associated container ref doc, regardless of whether they host any containers. type machineContainers struct { Id string `bson:"_id"` Children []string `bson:",omitempty"` } func (st *State) addChildToContainerRefOp(parentId string, childId string) txn.Op { return txn.Op{ C: st.containerRefs.Name, Id: parentId, Assert: txn.DocExists, Update: bson.D{{"$addToSet", bson.D{{"children", childId}}}}, } } func (st *State) insertNewContainerRefOp(machineId string, children ...string) txn.Op { return txn.Op{ C: st.containerRefs.Name, Id: machineId, Assert: txn.DocMissing, Insert: &machineContainers{ Id: machineId, Children: children, }, } } // removeContainerRefOps returns the txn.Op's necessary to remove a machine container record. // These include removing the record itself and updating the host machine's children property. func removeContainerRefOps(st *State, machineId string) []txn.Op { removeRefOp := txn.Op{ C: st.containerRefs.Name, Id: machineId, Assert: txn.DocExists, Remove: true, } // If the machine is a container, figure out its parent host. parentId := ParentId(machineId) if parentId == "" { return []txn.Op{removeRefOp} } removeParentRefOp := txn.Op{ C: st.containerRefs.Name, Id: parentId, Assert: txn.DocExists, Update: bson.D{{"$pull", bson.D{{"children", machineId}}}}, } return []txn.Op{removeRefOp, removeParentRefOp} } // ParentId returns the id of the host machine if machineId a container id, or "" // if machineId is not for a container. func ParentId(machineId string) string { idParts := strings.Split(machineId, "/") if len(idParts) < 3 { return "" } return strings.Join(idParts[:len(idParts)-2], "/") } // ContainerTypeFromId returns the container type if machineId is a container id, or "" // if machineId is not for a container. func ContainerTypeFromId(machineId string) instance.ContainerType { idParts := strings.Split(machineId, "/") if len(idParts) < 3 { return instance.ContainerType("") } return instance.ContainerType(idParts[len(idParts)-2]) } // NestingLevel returns how many levels of nesting exist for a machine id. func NestingLevel(machineId string) int { idParts := strings.Split(machineId, "/") return (len(idParts) - 1) / 2 } // TopParentId returns the id of the top level host machine for a container id. func TopParentId(machineId string) string { idParts := strings.Split(machineId, "/") return idParts[0] } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/unit_test.go�������������������������������������0000644�0000153�0000161�00000120045�12321735776�024364� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "strconv" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" ) type UnitSuite struct { ConnSuite charm *state.Charm service *state.Service unit *state.Unit } var _ = gc.Suite(&UnitSuite{}) func (s *UnitSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) s.charm = s.AddTestingCharm(c, "wordpress") var err error s.service = s.AddTestingService(c, "wordpress", s.charm) c.Assert(err, gc.IsNil) s.unit, err = s.service.AddUnit() c.Assert(err, gc.IsNil) c.Assert(s.unit.Series(), gc.Equals, "quantal") } func (s *UnitSuite) TestUnitNotFound(c *gc.C) { _, err := s.State.Unit("subway/0") c.Assert(err, gc.ErrorMatches, `unit "subway/0" not found`) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *UnitSuite) TestService(c *gc.C) { svc, err := s.unit.Service() c.Assert(err, gc.IsNil) c.Assert(svc.Name(), gc.Equals, s.unit.ServiceName()) } func (s *UnitSuite) TestConfigSettingsNeedCharmURLSet(c *gc.C) { _, err := s.unit.ConfigSettings() c.Assert(err, gc.ErrorMatches, "unit charm not set") } func (s *UnitSuite) TestConfigSettingsIncludeDefaults(c *gc.C) { err := s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.IsNil) settings, err := s.unit.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) } func (s *UnitSuite) TestConfigSettingsReflectService(c *gc.C) { err := s.service.UpdateConfigSettings(charm.Settings{"blog-title": "no title"}) c.Assert(err, gc.IsNil) err = s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.IsNil) settings, err := s.unit.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "no title"}) err = s.service.UpdateConfigSettings(charm.Settings{"blog-title": "ironic title"}) c.Assert(err, gc.IsNil) settings, err = s.unit.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "ironic title"}) } func (s *UnitSuite) TestConfigSettingsReflectCharm(c *gc.C) { err := s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.IsNil) newCharm := s.AddConfigCharm(c, "wordpress", "options: {}", 123) err = s.service.SetCharm(newCharm, false) c.Assert(err, gc.IsNil) // Settings still reflect charm set on unit. settings, err := s.unit.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) // When the unit has the new charm set, it'll see the new config. err = s.unit.SetCharmURL(newCharm.URL()) c.Assert(err, gc.IsNil) settings, err = s.unit.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{}) } func (s *UnitSuite) TestWatchConfigSettingsNeedsCharmURL(c *gc.C) { _, err := s.unit.WatchConfigSettings() c.Assert(err, gc.ErrorMatches, "unit charm not set") } func (s *UnitSuite) TestWatchConfigSettings(c *gc.C) { err := s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.IsNil) w, err := s.unit.WatchConfigSettings() c.Assert(err, gc.IsNil) defer testing.AssertStop(c, w) // Initial event. wc := testing.NewNotifyWatcherC(c, s.State, w) wc.AssertOneChange() // Update config a couple of times, check a single event. err = s.service.UpdateConfigSettings(charm.Settings{ "blog-title": "superhero paparazzi", }) c.Assert(err, gc.IsNil) err = s.service.UpdateConfigSettings(charm.Settings{ "blog-title": "sauceror central", }) c.Assert(err, gc.IsNil) wc.AssertOneChange() // Non-change is not reported. err = s.service.UpdateConfigSettings(charm.Settings{ "blog-title": "sauceror central", }) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Change service's charm; nothing detected. newCharm := s.AddConfigCharm(c, "wordpress", floatConfig, 123) err = s.service.SetCharm(newCharm, false) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Change service config for new charm; nothing detected. err = s.service.UpdateConfigSettings(charm.Settings{ "key": 42.0, }) c.Assert(err, gc.IsNil) wc.AssertNoChange() // NOTE: if we were to change the unit to use the new charm, we'd see // another event, because the originally-watched document will become // unreferenced and be removed. But I'm not testing that behaviour // because it's not very helpful and subject to change. } func (s *UnitSuite) TestGetSetPublicAddress(c *gc.C) { _, ok := s.unit.PublicAddress() c.Assert(ok, gc.Equals, false) err := s.unit.SetPublicAddress("example.foobar.com") c.Assert(err, gc.IsNil) address, ok := s.unit.PublicAddress() c.Assert(ok, gc.Equals, true) c.Assert(address, gc.Equals, "example.foobar.com") defer state.SetBeforeHooks(c, s.State, func() { c.Assert(s.unit.Destroy(), gc.IsNil) }).Check() err = s.unit.SetPublicAddress("example.foobar.com") c.Assert(err, gc.ErrorMatches, `cannot set public address of unit "wordpress/0": unit not found`) } func (s *UnitSuite) addSubordinateUnit(c *gc.C) *state.Unit { subCharm := s.AddTestingCharm(c, "logging") s.AddTestingService(c, "logging", subCharm) eps, err := s.State.InferEndpoints([]string{"wordpress", "logging"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(s.unit) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) subUnit, err := s.State.Unit("logging/0") c.Assert(err, gc.IsNil) return subUnit } func (s *UnitSuite) setAssignedMachineAddresses(c *gc.C, u *state.Unit) { err := u.AssignToNewMachine() c.Assert(err, gc.IsNil) mid, err := u.AssignedMachineId() c.Assert(err, gc.IsNil) machine, err := s.State.Machine(mid) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("i-exist", "fake_nonce", nil) c.Assert(err, gc.IsNil) err = machine.SetAddresses([]instance.Address{{ Type: instance.Ipv4Address, NetworkScope: instance.NetworkCloudLocal, Value: "private.address.example.com", }, { Type: instance.Ipv4Address, NetworkScope: instance.NetworkPublic, Value: "public.address.example.com", }}) c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestGetPublicAddressSubordinate(c *gc.C) { subUnit := s.addSubordinateUnit(c) _, ok := subUnit.PublicAddress() c.Assert(ok, gc.Equals, false) s.setAssignedMachineAddresses(c, s.unit) address, ok := subUnit.PublicAddress() c.Assert(ok, gc.Equals, true) c.Assert(address, gc.Equals, "public.address.example.com") } func (s *UnitSuite) TestGetPublicAddressFromMachine(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) address, ok := s.unit.PublicAddress() c.Check(address, gc.Equals, "") c.Assert(ok, gc.Equals, false) addresses := []instance.Address{ instance.NewAddress("127.0.0.1"), instance.NewAddress("8.8.8.8"), } err = machine.SetAddresses(addresses) 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) } func (s *UnitSuite) TestGetSetPrivateAddress(c *gc.C) { _, ok := s.unit.PrivateAddress() c.Assert(ok, gc.Equals, false) err := s.unit.SetPrivateAddress("example.local") c.Assert(err, gc.IsNil) address, ok := s.unit.PrivateAddress() c.Assert(ok, gc.Equals, true) c.Assert(address, gc.Equals, "example.local") defer state.SetBeforeHooks(c, s.State, func() { c.Assert(s.unit.Destroy(), gc.IsNil) }).Check() err = s.unit.SetPrivateAddress("example.local") c.Assert(err, gc.ErrorMatches, `cannot set private address of unit "wordpress/0": unit not found`) } func (s *UnitSuite) TestGetPrivateAddressSubordinate(c *gc.C) { subUnit := s.addSubordinateUnit(c) _, ok := subUnit.PrivateAddress() c.Assert(ok, gc.Equals, false) s.setAssignedMachineAddresses(c, s.unit) address, ok := subUnit.PrivateAddress() c.Assert(ok, gc.Equals, true) c.Assert(address, gc.Equals, "private.address.example.com") } func (s *UnitSuite) TestGetPrivateAddressFromMachine(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) address, ok := s.unit.PrivateAddress() c.Check(address, gc.Equals, "") c.Assert(ok, gc.Equals, false) addresses := []instance.Address{ 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, "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) } func (s *UnitSuite) TestRefresh(c *gc.C) { unit1, err := s.State.Unit(s.unit.Name()) c.Assert(err, gc.IsNil) err = s.unit.SetPrivateAddress("example.local") c.Assert(err, gc.IsNil) err = s.unit.SetPublicAddress("example.foobar.com") c.Assert(err, gc.IsNil) address, ok := unit1.PrivateAddress() c.Assert(ok, gc.Equals, false) address, ok = unit1.PublicAddress() c.Assert(ok, gc.Equals, false) err = unit1.Refresh() c.Assert(err, gc.IsNil) address, ok = unit1.PrivateAddress() c.Assert(ok, gc.Equals, true) c.Assert(address, gc.Equals, "example.local") address, ok = unit1.PublicAddress() c.Assert(ok, gc.Equals, true) c.Assert(address, gc.Equals, "example.foobar.com") err = unit1.EnsureDead() c.Assert(err, gc.IsNil) err = unit1.Remove() c.Assert(err, gc.IsNil) err = unit1.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *UnitSuite) TestGetSetStatusWhileAlive(c *gc.C) { err := s.unit.SetStatus(params.StatusError, "", nil) c.Assert(err, gc.ErrorMatches, `cannot set status "error" without info`) err = s.unit.SetStatus(params.StatusPending, "", nil) c.Assert(err, gc.ErrorMatches, `cannot set status "pending"`) err = s.unit.SetStatus(params.StatusDown, "", nil) c.Assert(err, gc.ErrorMatches, `cannot set status "down"`) err = s.unit.SetStatus(params.Status("vliegkat"), "orville", nil) c.Assert(err, gc.ErrorMatches, `cannot set invalid status "vliegkat"`) status, info, data, err := s.unit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusPending) c.Assert(info, gc.Equals, "") c.Assert(data, gc.HasLen, 0) err = s.unit.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) status, info, data, err = s.unit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStarted) c.Assert(info, gc.Equals, "") c.Assert(data, gc.HasLen, 0) err = s.unit.SetStatus(params.StatusError, "test-hook failed", params.StatusData{ "foo": "bar", }) c.Assert(err, gc.IsNil) status, info, data, err = s.unit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "test-hook failed") c.Assert(data, gc.DeepEquals, params.StatusData{ "foo": "bar", }) } func (s *UnitSuite) TestGetSetStatusWhileNotAlive(c *gc.C) { err := s.unit.Destroy() c.Assert(err, gc.IsNil) err = s.unit.SetStatus(params.StatusStarted, "not really", nil) c.Assert(err, gc.ErrorMatches, `cannot set status of unit "wordpress/0": not found or dead`) _, _, _, err = s.unit.Status() c.Assert(err, gc.ErrorMatches, "status not found") err = s.unit.EnsureDead() c.Assert(err, gc.IsNil) err = s.unit.SetStatus(params.StatusStarted, "not really", nil) c.Assert(err, gc.ErrorMatches, `cannot set status of unit "wordpress/0": not found or dead`) _, _, _, err = s.unit.Status() c.Assert(err, gc.ErrorMatches, "status not found") } func (s *UnitSuite) TestGetSetStatusDataStandard(c *gc.C) { err := s.unit.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) _, _, _, err = s.unit.Status() c.Assert(err, gc.IsNil) // Regular status setting with data. err = s.unit.SetStatus(params.StatusError, "test-hook failed", params.StatusData{ "1st-key": "one", "2nd-key": 2, "3rd-key": true, }) c.Assert(err, gc.IsNil) status, info, data, err := s.unit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "test-hook failed") c.Assert(data, gc.DeepEquals, params.StatusData{ "1st-key": "one", "2nd-key": 2, "3rd-key": true, }) } func (s *UnitSuite) TestGetSetStatusDataMongo(c *gc.C) { err := s.unit.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) _, _, _, err = s.unit.Status() c.Assert(err, gc.IsNil) // Status setting with MongoDB special values. err = s.unit.SetStatus(params.StatusError, "mongo", params.StatusData{ `{name: "Joe"}`: "$where", "eval": `eval(function(foo) { return foo; }, "bar")`, "mapReduce": "mapReduce", "group": "group", }) c.Assert(err, gc.IsNil) status, info, data, err := s.unit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "mongo") c.Assert(data, gc.DeepEquals, params.StatusData{ `{name: "Joe"}`: "$where", "eval": `eval(function(foo) { return foo; }, "bar")`, "mapReduce": "mapReduce", "group": "group", }) } func (s *UnitSuite) TestGetSetStatusDataChange(c *gc.C) { err := s.unit.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) _, _, _, err = s.unit.Status() c.Assert(err, gc.IsNil) // Status setting and changing data afterwards. data := params.StatusData{ "1st-key": "one", "2nd-key": 2, "3rd-key": true, } err = s.unit.SetStatus(params.StatusError, "test-hook failed", data) c.Assert(err, gc.IsNil) data["4th-key"] = 4.0 status, info, data, err := s.unit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "test-hook failed") c.Assert(data, gc.DeepEquals, params.StatusData{ "1st-key": "one", "2nd-key": 2, "3rd-key": true, }) // Set status data to nil, so an empty map will be returned. err = s.unit.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) status, info, data, err = s.unit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStarted) c.Assert(info, gc.Equals, "") c.Assert(data, gc.HasLen, 0) } func (s *UnitSuite) TestUnitCharm(c *gc.C) { preventUnitDestroyRemove(c, s.unit) curl, ok := s.unit.CharmURL() c.Assert(ok, gc.Equals, false) c.Assert(curl, gc.IsNil) err := s.unit.SetCharmURL(nil) c.Assert(err, gc.ErrorMatches, "cannot set nil charm url") err = s.unit.SetCharmURL(charm.MustParseURL("cs:missing/one-1")) c.Assert(err, gc.ErrorMatches, `unknown charm url "cs:missing/one-1"`) err = s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.IsNil) curl, ok = s.unit.CharmURL() c.Assert(ok, gc.Equals, true) c.Assert(curl, gc.DeepEquals, s.charm.URL()) err = s.unit.Destroy() c.Assert(err, gc.IsNil) err = s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.IsNil) curl, ok = s.unit.CharmURL() c.Assert(ok, gc.Equals, true) c.Assert(curl, gc.DeepEquals, s.charm.URL()) err = s.unit.EnsureDead() c.Assert(err, gc.IsNil) err = s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is dead`) } func (s *UnitSuite) TestDestroySetStatusRetry(c *gc.C) { defer state.SetRetryHooks(c, s.State, func() { err := s.unit.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) }, func() { assertLife(c, s.unit, state.Dying) }).Check() err := s.unit.Destroy() c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestDestroySetCharmRetry(c *gc.C) { defer state.SetRetryHooks(c, s.State, func() { err := s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.IsNil) }, func() { assertRemoved(c, s.unit) }).Check() err := s.unit.Destroy() c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestDestroyChangeCharmRetry(c *gc.C) { err := s.unit.SetCharmURL(s.charm.URL()) c.Assert(err, gc.IsNil) newCharm := s.AddConfigCharm(c, "mysql", "options: {}", 99) err = s.service.SetCharm(newCharm, false) c.Assert(err, gc.IsNil) defer state.SetRetryHooks(c, s.State, func() { err := s.unit.SetCharmURL(newCharm.URL()) c.Assert(err, gc.IsNil) }, func() { assertRemoved(c, s.unit) }).Check() err = s.unit.Destroy() c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestDestroyAssignRetry(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) defer state.SetRetryHooks(c, s.State, func() { err := s.unit.AssignToMachine(machine) c.Assert(err, gc.IsNil) }, func() { assertRemoved(c, s.unit) // Also check the unit ref was properly removed from the machine doc -- // if it weren't, we wouldn't be able to make the machine Dead. err := machine.EnsureDead() c.Assert(err, gc.IsNil) }).Check() err = s.unit.Destroy() c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestDestroyUnassignRetry(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) defer state.SetRetryHooks(c, s.State, func() { err := s.unit.UnassignFromMachine() c.Assert(err, gc.IsNil) }, func() { assertRemoved(c, s.unit) }).Check() err = s.unit.Destroy() c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestShortCircuitDestroyUnit(c *gc.C) { // A unit that has not set any status is removed directly. err := s.unit.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.unit.Life(), gc.Equals, state.Dying) assertRemoved(c, s.unit) } func (s *UnitSuite) TestCannotShortCircuitDestroyWithSubordinates(c *gc.C) { // A unit with subordinates is just set to Dying. s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"logging", "wordpress"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(s.unit) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) err = s.unit.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.unit.Life(), gc.Equals, state.Dying) assertLife(c, s.unit, state.Dying) } func (s *UnitSuite) TestCannotShortCircuitDestroyWithStatus(c *gc.C) { for i, test := range []struct { status params.Status info string }{{ params.StatusInstalled, "", }, { params.StatusStarted, "", }, { params.StatusError, "blah", }, { params.StatusStopped, "", }} { c.Logf("test %d: %s", i, test.status) unit, err := s.service.AddUnit() c.Assert(err, gc.IsNil) err = unit.SetStatus(test.status, test.info, nil) c.Assert(err, gc.IsNil) err = unit.Destroy() c.Assert(err, gc.IsNil) c.Assert(unit.Life(), gc.Equals, state.Dying) assertLife(c, unit, state.Dying) } } func (s *UnitSuite) TestShortCircuitDestroyWithProvisionedMachine(c *gc.C) { // A unit assigned to a provisioned machine is still removed directly so // long as it has not set status. err := s.unit.AssignToNewMachine() c.Assert(err, gc.IsNil) mid, err := s.unit.AssignedMachineId() c.Assert(err, gc.IsNil) machine, err := s.State.Machine(mid) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("i-malive", "fake_nonce", nil) c.Assert(err, gc.IsNil) err = s.unit.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.unit.Life(), gc.Equals, state.Dying) assertRemoved(c, s.unit) } func assertLife(c *gc.C, entity state.Living, life state.Life) { c.Assert(entity.Refresh(), gc.IsNil) c.Assert(entity.Life(), gc.Equals, life) } func assertRemoved(c *gc.C, entity state.Living) { err := entity.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = entity.Destroy() c.Assert(err, gc.IsNil) if entity, ok := entity.(state.AgentLiving); ok { err = entity.EnsureDead() c.Assert(err, gc.IsNil) err = entity.Remove() c.Assert(err, gc.IsNil) } } func (s *UnitSuite) TestTag(c *gc.C) { c.Assert(s.unit.Tag(), gc.Equals, "unit-wordpress-0") } func (s *UnitSuite) TestSetMongoPassword(c *gc.C) { testSetMongoPassword(c, func(st *state.State) (entity, error) { return st.Unit(s.unit.Name()) }) } func (s *UnitSuite) TestSetPassword(c *gc.C) { preventUnitDestroyRemove(c, s.unit) testSetPassword(c, func() (state.Authenticator, error) { return s.State.Unit(s.unit.Name()) }) } func (s *UnitSuite) TestSetAgentCompatPassword(c *gc.C) { e, err := s.State.Unit(s.unit.Name()) c.Assert(err, gc.IsNil) testSetAgentCompatPassword(c, e) } func (s *UnitSuite) TestSetMongoPasswordOnUnitAfterConnectingAsMachineEntity(c *gc.C) { // Make a second unit to use later. (Subordinate units can only be created // as a side-effect of a principal entering relation scope.) subUnit := s.addSubordinateUnit(c) info := state.TestingStateInfo() st, err := state.Open(info, state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st.Close() // Turn on fully-authenticated mode. err = st.SetAdminMongoPassword("admin-secret") c.Assert(err, gc.IsNil) // Add a new machine, assign the units to it // and set its password. m, err := st.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) unit, err := st.Unit(s.unit.Name()) c.Assert(err, gc.IsNil) subUnit, err = st.Unit(subUnit.Name()) c.Assert(err, gc.IsNil) err = unit.AssignToMachine(m) c.Assert(err, gc.IsNil) err = m.SetMongoPassword("foo") c.Assert(err, gc.IsNil) // Sanity check that we cannot connect with the wrong // password info.Tag = m.Tag() info.Password = "foo1" err = tryOpenState(info) c.Assert(err, jc.Satisfies, errors.IsUnauthorizedError) // Connect as the machine entity. info.Tag = m.Tag() info.Password = "foo" st1, err := state.Open(info, state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st1.Close() // Change the password for a unit derived from // the machine entity's state. unit, err = st1.Unit(s.unit.Name()) c.Assert(err, gc.IsNil) err = unit.SetMongoPassword("bar") c.Assert(err, gc.IsNil) // Now connect as the unit entity and, as that // that entity, change the password for a new unit. info.Tag = unit.Tag() info.Password = "bar" st2, err := state.Open(info, state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st2.Close() // Check that we can set its password. unit, err = st2.Unit(subUnit.Name()) c.Assert(err, gc.IsNil) err = unit.SetMongoPassword("bar2") c.Assert(err, gc.IsNil) // Clear the admin password, so tests can reset the db. err = st.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestUnitSetAgentAlive(c *gc.C) { alive, err := s.unit.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, false) pinger, err := s.unit.SetAgentAlive() c.Assert(err, gc.IsNil) c.Assert(pinger, gc.NotNil) defer pinger.Stop() s.State.StartSync() alive, err = s.unit.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, true) } func (s *UnitSuite) TestUnitWaitAgentAlive(c *gc.C) { alive, err := s.unit.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, false) err = s.unit.WaitAgentAlive(coretesting.ShortWait) c.Assert(err, gc.ErrorMatches, `waiting for agent of unit "wordpress/0": still not alive after timeout`) pinger, err := s.unit.SetAgentAlive() c.Assert(err, gc.IsNil) s.State.StartSync() err = s.unit.WaitAgentAlive(coretesting.LongWait) c.Assert(err, gc.IsNil) alive, err = s.unit.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, true) err = pinger.Kill() c.Assert(err, gc.IsNil) s.State.StartSync() alive, err = s.unit.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, false) } func (s *UnitSuite) TestResolve(c *gc.C) { err := s.unit.Resolve(false) c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not in an error state`) err = s.unit.Resolve(true) c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not in an error state`) err = s.unit.SetStatus(params.StatusError, "gaaah", nil) c.Assert(err, gc.IsNil) err = s.unit.Resolve(false) c.Assert(err, gc.IsNil) err = s.unit.Resolve(true) c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`) c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedNoHooks) err = s.unit.ClearResolved() c.Assert(err, gc.IsNil) err = s.unit.Resolve(true) c.Assert(err, gc.IsNil) err = s.unit.Resolve(false) c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`) c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedRetryHooks) } func (s *UnitSuite) TestGetSetClearResolved(c *gc.C) { mode := s.unit.Resolved() c.Assert(mode, gc.Equals, state.ResolvedNone) err := s.unit.SetResolved(state.ResolvedNoHooks) c.Assert(err, gc.IsNil) err = s.unit.SetResolved(state.ResolvedNoHooks) c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": already resolved`) mode = s.unit.Resolved() c.Assert(mode, gc.Equals, state.ResolvedNoHooks) err = s.unit.Refresh() c.Assert(err, gc.IsNil) mode = s.unit.Resolved() c.Assert(mode, gc.Equals, state.ResolvedNoHooks) err = s.unit.ClearResolved() c.Assert(err, gc.IsNil) mode = s.unit.Resolved() c.Assert(mode, gc.Equals, state.ResolvedNone) err = s.unit.Refresh() c.Assert(err, gc.IsNil) mode = s.unit.Resolved() c.Assert(mode, gc.Equals, state.ResolvedNone) err = s.unit.ClearResolved() c.Assert(err, gc.IsNil) err = s.unit.SetResolved(state.ResolvedNone) c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": invalid error resolution mode: ""`) err = s.unit.SetResolved(state.ResolvedMode("foo")) c.Assert(err, gc.ErrorMatches, `cannot set resolved mode for unit "wordpress/0": invalid error resolution mode: "foo"`) } func (s *UnitSuite) TestOpenedPorts(c *gc.C) { // Verify no open ports before activity. c.Assert(s.unit.OpenedPorts(), gc.HasLen, 0) // Now open and close port. err := s.unit.OpenPort("tcp", 80) c.Assert(err, gc.IsNil) open := s.unit.OpenedPorts() c.Assert(open, gc.DeepEquals, []instance.Port{ {"tcp", 80}, }) err = s.unit.OpenPort("udp", 53) c.Assert(err, gc.IsNil) open = s.unit.OpenedPorts() c.Assert(open, gc.DeepEquals, []instance.Port{ {"tcp", 80}, {"udp", 53}, }) err = s.unit.OpenPort("tcp", 53) c.Assert(err, gc.IsNil) open = s.unit.OpenedPorts() c.Assert(open, gc.DeepEquals, []instance.Port{ {"tcp", 53}, {"tcp", 80}, {"udp", 53}, }) err = s.unit.OpenPort("tcp", 443) c.Assert(err, gc.IsNil) open = s.unit.OpenedPorts() c.Assert(open, gc.DeepEquals, []instance.Port{ {"tcp", 53}, {"tcp", 80}, {"tcp", 443}, {"udp", 53}, }) err = s.unit.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) open = s.unit.OpenedPorts() c.Assert(open, gc.DeepEquals, []instance.Port{ {"tcp", 53}, {"tcp", 443}, {"udp", 53}, }) err = s.unit.ClosePort("tcp", 80) c.Assert(err, gc.IsNil) open = s.unit.OpenedPorts() c.Assert(open, gc.DeepEquals, []instance.Port{ {"tcp", 53}, {"tcp", 443}, {"udp", 53}, }) } func (s *UnitSuite) TestOpenClosePortWhenDying(c *gc.C) { preventUnitDestroyRemove(c, s.unit) testWhenDying(c, s.unit, noErr, deadErr, func() error { return s.unit.OpenPort("tcp", 20) }, func() error { return s.unit.ClosePort("tcp", 20) }) } func (s *UnitSuite) TestSetClearResolvedWhenNotAlive(c *gc.C) { preventUnitDestroyRemove(c, s.unit) err := s.unit.Destroy() c.Assert(err, gc.IsNil) err = s.unit.SetResolved(state.ResolvedNoHooks) c.Assert(err, gc.IsNil) err = s.unit.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.unit.Resolved(), gc.Equals, state.ResolvedNoHooks) err = s.unit.ClearResolved() c.Assert(err, gc.IsNil) err = s.unit.EnsureDead() c.Assert(err, gc.IsNil) err = s.unit.SetResolved(state.ResolvedRetryHooks) c.Assert(err, gc.ErrorMatches, deadErr) err = s.unit.ClearResolved() c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestSubordinateChangeInPrincipal(c *gc.C) { subCharm := s.AddTestingCharm(c, "logging") for i := 0; i < 2; i++ { // Note: subordinate units can only be created as a side effect of a // principal entering scope; and a given principal can only have a // single subordinate unit of each service. name := "logging" + strconv.Itoa(i) s.AddTestingService(c, name, subCharm) eps, err := s.State.InferEndpoints([]string{name, "wordpress"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(s.unit) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) } err := s.unit.Refresh() c.Assert(err, gc.IsNil) subordinates := s.unit.SubordinateNames() c.Assert(subordinates, gc.DeepEquals, []string{"logging0/0", "logging1/0"}) su1, err := s.State.Unit("logging1/0") c.Assert(err, gc.IsNil) err = su1.EnsureDead() c.Assert(err, gc.IsNil) err = su1.Remove() c.Assert(err, gc.IsNil) err = s.unit.Refresh() c.Assert(err, gc.IsNil) subordinates = s.unit.SubordinateNames() c.Assert(subordinates, gc.DeepEquals, []string{"logging0/0"}) } func (s *UnitSuite) TestDeathWithSubordinates(c *gc.C) { // Check that units can become dead when they've never had subordinates. u, err := s.service.AddUnit() c.Assert(err, gc.IsNil) err = u.EnsureDead() c.Assert(err, gc.IsNil) // Create a new unit and add a subordinate. u, err = s.service.AddUnit() c.Assert(err, gc.IsNil) s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) c.Assert(err, gc.IsNil) eps, err := s.State.InferEndpoints([]string{"logging", "wordpress"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(u) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) // Check the unit cannot become Dead, but can become Dying... err = u.EnsureDead() c.Assert(err, gc.Equals, state.ErrUnitHasSubordinates) err = u.Destroy() c.Assert(err, gc.IsNil) // ...and that it still can't become Dead now it's Dying. err = u.EnsureDead() c.Assert(err, gc.Equals, state.ErrUnitHasSubordinates) // Make the subordinate Dead and check the principal still cannot be removed. sub, err := s.State.Unit("logging/0") c.Assert(err, gc.IsNil) err = sub.EnsureDead() c.Assert(err, gc.IsNil) err = u.EnsureDead() c.Assert(err, gc.Equals, state.ErrUnitHasSubordinates) // remove the subordinate and check the principal can finally become Dead. err = sub.Remove() c.Assert(err, gc.IsNil) err = u.EnsureDead() c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestPrincipalName(c *gc.C) { subCharm := s.AddTestingCharm(c, "logging") s.AddTestingService(c, "logging", subCharm) eps, err := s.State.InferEndpoints([]string{"logging", "wordpress"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(s.unit) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) err = s.unit.Refresh() c.Assert(err, gc.IsNil) subordinates := s.unit.SubordinateNames() c.Assert(subordinates, gc.DeepEquals, []string{"logging/0"}) su, err := s.State.Unit("logging/0") c.Assert(err, gc.IsNil) principal, valid := su.PrincipalName() c.Assert(valid, gc.Equals, true) c.Assert(principal, gc.Equals, s.unit.Name()) // Calling PrincipalName on a principal unit yields "", false. principal, valid = s.unit.PrincipalName() c.Assert(valid, gc.Equals, false) 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`) err = s.unit.EnsureDead() c.Assert(err, gc.IsNil) err = s.unit.Remove() c.Assert(err, gc.IsNil) err = s.unit.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) units, err := s.service.AllUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 0) err = s.unit.Remove() c.Assert(err, gc.IsNil) } func (s *UnitSuite) TestRemovePathological(c *gc.C) { // Add a relation between wordpress and mysql... wordpress := s.service wordpress0 := s.unit mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) // The relation holds a reference to wordpress, but that can't keep // wordpress from being removed -- because the relation will be removed // if we destroy wordpress. // However, if a unit of the *other* service joins the relation, that // will add an additional reference and prevent the relation -- and // thus wordpress itself -- from being removed when its last unit is. mysql0, err := mysql.AddUnit() c.Assert(err, gc.IsNil) mysql0ru, err := rel.Unit(mysql0) c.Assert(err, gc.IsNil) err = mysql0ru.EnterScope(nil) c.Assert(err, gc.IsNil) // Destroy wordpress, and remove its last unit. err = wordpress.Destroy() c.Assert(err, gc.IsNil) err = wordpress0.EnsureDead() c.Assert(err, gc.IsNil) err = wordpress0.Remove() c.Assert(err, gc.IsNil) // Check this didn't kill the service or relation yet... err = wordpress.Refresh() c.Assert(err, gc.IsNil) err = rel.Refresh() c.Assert(err, gc.IsNil) // ...but when the unit on the other side departs the relation, the // relation and the other service are cleaned up. err = mysql0ru.LeaveScope() c.Assert(err, gc.IsNil) err = wordpress.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = rel.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *UnitSuite) TestRemovePathologicalWithBuggyUniter(c *gc.C) { // Add a relation between wordpress and mysql... wordpress := s.service wordpress0 := s.unit mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) // The relation holds a reference to wordpress, but that can't keep // wordpress from being removed -- because the relation will be removed // if we destroy wordpress. // However, if a unit of the *other* service joins the relation, that // will add an additional reference and prevent the relation -- and // thus wordpress itself -- from being removed when its last unit is. mysql0, err := mysql.AddUnit() c.Assert(err, gc.IsNil) mysql0ru, err := rel.Unit(mysql0) c.Assert(err, gc.IsNil) err = mysql0ru.EnterScope(nil) c.Assert(err, gc.IsNil) // Destroy wordpress, and remove its last unit. err = wordpress.Destroy() c.Assert(err, gc.IsNil) err = wordpress0.EnsureDead() c.Assert(err, gc.IsNil) err = wordpress0.Remove() c.Assert(err, gc.IsNil) // Check this didn't kill the service or relation yet... err = wordpress.Refresh() c.Assert(err, gc.IsNil) err = rel.Refresh() c.Assert(err, gc.IsNil) // ...and that when the malfunctioning unit agent on the other side // sets itself to dead *without* departing the relation, the unit's // removal causes the relation and the other service to be cleaned up. err = mysql0.EnsureDead() c.Assert(err, gc.IsNil) err = mysql0.Remove() c.Assert(err, gc.IsNil) err = wordpress.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = rel.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *UnitSuite) TestWatchSubordinates(c *gc.C) { w := s.unit.WatchSubordinateUnits() defer testing.AssertStop(c, w) wc := testing.NewStringsWatcherC(c, s.State, w) wc.AssertChange() wc.AssertNoChange() // Add a couple of subordinates, check change. subCharm := s.AddTestingCharm(c, "logging") var subUnits []*state.Unit for i := 0; i < 2; i++ { // Note: subordinate units can only be created as a side effect of a // principal entering scope; and a given principal can only have a // single subordinate unit of each service. name := "logging" + strconv.Itoa(i) subSvc := s.AddTestingService(c, name, subCharm) eps, err := s.State.InferEndpoints([]string{name, "wordpress"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(s.unit) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) units, err := subSvc.AllUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 1) subUnits = append(subUnits, units[0]) } wc.AssertChange(subUnits[0].Name(), subUnits[1].Name()) wc.AssertNoChange() // Set one to Dying, check change. err := subUnits[0].Destroy() c.Assert(err, gc.IsNil) wc.AssertChange(subUnits[0].Name()) wc.AssertNoChange() // Set both to Dead, and remove one; check change. err = subUnits[0].EnsureDead() c.Assert(err, gc.IsNil) err = subUnits[1].EnsureDead() c.Assert(err, gc.IsNil) err = subUnits[1].Remove() c.Assert(err, gc.IsNil) wc.AssertChange(subUnits[0].Name(), subUnits[1].Name()) wc.AssertNoChange() // Stop watcher, check closed. testing.AssertStop(c, w) wc.AssertClosed() // Start a new watch, check Dead unit is reported. w = s.unit.WatchSubordinateUnits() defer testing.AssertStop(c, w) wc = testing.NewStringsWatcherC(c, s.State, w) wc.AssertChange(subUnits[0].Name()) wc.AssertNoChange() // Remove the leftover, check no change. err = subUnits[0].Remove() c.Assert(err, gc.IsNil) wc.AssertNoChange() } func (s *UnitSuite) TestWatchUnit(c *gc.C) { preventUnitDestroyRemove(c, s.unit) w := s.unit.Watch() defer testing.AssertStop(c, w) // Initial event. wc := testing.NewNotifyWatcherC(c, s.State, w) wc.AssertOneChange() // Make one change (to a separate instance), check one event. unit, err := s.State.Unit(s.unit.Name()) c.Assert(err, gc.IsNil) err = unit.SetPublicAddress("example.foobar.com") c.Assert(err, gc.IsNil) wc.AssertOneChange() // Make two changes, check one event. err = unit.SetPrivateAddress("example.foobar") c.Assert(err, gc.IsNil) err = unit.Destroy() c.Assert(err, gc.IsNil) wc.AssertOneChange() // Stop, check closed. testing.AssertStop(c, w) wc.AssertClosed() // Remove unit, start new watch, check single event. err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.IsNil) w = s.unit.Watch() defer testing.AssertStop(c, w) testing.NewNotifyWatcherC(c, s.State, w).AssertOneChange() } func (s *UnitSuite) TestAnnotatorForUnit(c *gc.C) { testAnnotator(c, func() (state.Annotator, error) { return s.State.Unit("wordpress/0") }) } func (s *UnitSuite) TestAnnotationRemovalForUnit(c *gc.C) { annotations := map[string]string{"mykey": "myvalue"} err := s.unit.SetAnnotations(annotations) c.Assert(err, gc.IsNil) err = s.unit.EnsureDead() c.Assert(err, gc.IsNil) err = s.unit.Remove() c.Assert(err, gc.IsNil) ann, err := s.unit.Annotations() c.Assert(err, gc.IsNil) c.Assert(ann, gc.DeepEquals, make(map[string]string)) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/relation.go��������������������������������������0000644�0000153�0000161�00000021003�12321735642�024145� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( stderrors "errors" "fmt" "sort" "strconv" "strings" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/names" "launchpad.net/juju-core/utils" ) // relationKey returns a string describing the relation defined by // endpoints, for use in various contexts (including error messages). func relationKey(endpoints []Endpoint) string { eps := epSlice{} for _, ep := range endpoints { eps = append(eps, ep) } sort.Sort(eps) names := []string{} for _, ep := range eps { names = append(names, ep.String()) } return strings.Join(names, " ") } // relationDoc is the internal representation of a Relation in MongoDB. // Note the correspondence with RelationInfo in state/api/params. type relationDoc struct { Key string `bson:"_id"` Id int Endpoints []Endpoint Life Life UnitCount int } // Relation represents a relation between one or two service endpoints. type Relation struct { st *State doc relationDoc } func newRelation(st *State, doc *relationDoc) *Relation { return &Relation{ st: st, doc: *doc, } } func (r *Relation) String() string { return r.doc.Key } // Tag returns a name identifying the relation that is safe to use // as a file name. func (r *Relation) Tag() string { return names.RelationTag(r.doc.Key) } // Refresh refreshes the contents of the relation from the underlying // state. It returns an error that satisfies IsNotFound if the relation has been // removed. func (r *Relation) Refresh() error { doc := relationDoc{} err := r.st.relations.FindId(r.doc.Key).One(&doc) if err == mgo.ErrNotFound { return errors.NotFoundf("relation %v", r) } if err != nil { return fmt.Errorf("cannot refresh relation %v: %v", r, err) } if r.doc.Id != doc.Id { // The relation has been destroyed and recreated. This is *not* the // same relation; if we pretend it is, we run the risk of violating // the lifecycle-only-advances guarantee. return errors.NotFoundf("relation %v", r) } r.doc = doc return nil } // Life returns the relation's current life state. func (r *Relation) Life() Life { return r.doc.Life } // Destroy ensures that the relation will be removed at some point; if no units // are currently in scope, it will be removed immediately. func (r *Relation) Destroy() (err error) { defer utils.ErrorContextf(&err, "cannot destroy relation %q", r) if len(r.doc.Endpoints) == 1 && r.doc.Endpoints[0].Role == charm.RolePeer { return fmt.Errorf("is a peer relation") } defer func() { if err == nil { // This is a white lie; the document might actually be removed. r.doc.Life = Dying } }() rel := &Relation{r.st, r.doc} // In this context, aborted transactions indicate that the number of units // in scope have changed between 0 and not-0. The chances of 5 successive // attempts each hitting this change -- which is itself an unlikely one -- // are considered to be extremely small. for attempt := 0; attempt < 5; attempt++ { ops, _, err := rel.destroyOps("") if err == errAlreadyDying { return nil } else if err != nil { return err } if err := rel.st.runTransaction(ops); err != txn.ErrAborted { return err } if err := rel.Refresh(); errors.IsNotFoundError(err) { return nil } else if err != nil { return err } } return ErrExcessiveContention } var errAlreadyDying = stderrors.New("entity is already dying and cannot be destroyed") // destroyOps returns the operations necessary to destroy the relation, and // whether those operations will lead to the relation's removal. These // operations may include changes to the relation's services; however, if // ignoreService is not empty, no operations modifying that service will // be generated. func (r *Relation) destroyOps(ignoreService string) (ops []txn.Op, isRemove bool, err error) { if r.doc.Life != Alive { return nil, false, errAlreadyDying } if r.doc.UnitCount == 0 { removeOps, err := r.removeOps(ignoreService, nil) if err != nil { return nil, false, err } return removeOps, true, nil } return []txn.Op{{ C: r.st.relations.Name, Id: r.doc.Key, Assert: bson.D{{"life", Alive}, {"unitcount", bson.D{{"$gt", 0}}}}, Update: bson.D{{"$set", bson.D{{"life", Dying}}}}, }}, false, nil } // removeOps returns the operations necessary to remove the relation. If // ignoreService is not empty, no operations affecting that service will be // included; if departingUnit is not nil, this implies that the relation's // services may be Dying and otherwise unreferenced, and may thus require // removal themselves. func (r *Relation) removeOps(ignoreService string, departingUnit *Unit) ([]txn.Op, error) { relOp := txn.Op{ C: r.st.relations.Name, Id: r.doc.Key, Remove: true, } if departingUnit != nil { relOp.Assert = bson.D{{"life", Dying}, {"unitcount", 1}} } else { relOp.Assert = bson.D{{"life", Alive}, {"unitcount", 0}} } ops := []txn.Op{relOp} for _, ep := range r.doc.Endpoints { if ep.ServiceName == ignoreService { continue } var asserts bson.D hasRelation := bson.D{{"relationcount", bson.D{{"$gt", 0}}}} if departingUnit == nil { // We're constructing a destroy operation, either of the relation // or one of its services, and can therefore be assured that both // services are Alive. asserts = append(hasRelation, isAliveDoc...) } else if ep.ServiceName == departingUnit.ServiceName() { // This service must have at least one unit -- the one that's // departing the relation -- so it cannot be ready for removal. cannotDieYet := bson.D{{"unitcount", bson.D{{"$gt", 0}}}} asserts = append(hasRelation, cannotDieYet...) } else { // This service may require immediate removal. svc := &Service{st: r.st} hasLastRef := bson.D{{"life", Dying}, {"unitcount", 0}, {"relationcount", 1}} removable := append(bson.D{{"_id", ep.ServiceName}}, hasLastRef...) if err := r.st.services.Find(removable).One(&svc.doc); err == nil { ops = append(ops, svc.removeOps(hasLastRef)...) continue } else if err != mgo.ErrNotFound { return nil, err } // If not, we must check that this is still the case when the // transaction is applied. asserts = bson.D{{"$or", []bson.D{ {{"life", Alive}}, {{"unitcount", bson.D{{"$gt", 0}}}}, {{"relationcount", bson.D{{"$gt", 1}}}}, }}} } ops = append(ops, txn.Op{ C: r.st.services.Name, Id: ep.ServiceName, Assert: asserts, Update: bson.D{{"$inc", bson.D{{"relationcount", -1}}}}, }) } cleanupOp := r.st.newCleanupOp("settings", fmt.Sprintf("r#%d#", r.Id())) return append(ops, cleanupOp), nil } // Id returns the integer internal relation key. This is exposed // because the unit agent needs to expose a value derived from this // (as JUJU_RELATION_ID) to allow relation hooks to differentiate // between relations with different services. func (r *Relation) Id() int { return r.doc.Id } // Endpoint returns the endpoint of the relation for the named service. // If the service is not part of the relation, an error will be returned. func (r *Relation) Endpoint(serviceName string) (Endpoint, error) { for _, ep := range r.doc.Endpoints { if ep.ServiceName == serviceName { return ep, nil } } return Endpoint{}, fmt.Errorf("service %q is not a member of %q", serviceName, r) } // RelatedEndpoints returns the endpoints of the relation r with which // units of the named service will establish relations. If the service // is not part of the relation r, an error will be returned. func (r *Relation) RelatedEndpoints(serviceName string) ([]Endpoint, error) { local, err := r.Endpoint(serviceName) if err != nil { return nil, err } role := counterpartRole(local.Role) var eps []Endpoint for _, ep := range r.doc.Endpoints { if ep.Role == role { eps = append(eps, ep) } } if eps == nil { return nil, fmt.Errorf("no endpoints of %q relate to service %q", r, serviceName) } return eps, nil } // Unit returns a RelationUnit for the supplied unit. func (r *Relation) Unit(u *Unit) (*RelationUnit, error) { ep, err := r.Endpoint(u.doc.Service) if err != nil { return nil, err } scope := []string{"r", strconv.Itoa(r.doc.Id)} if ep.Scope == charm.ScopeContainer { container := u.doc.Principal if container == "" { container = u.doc.Name } scope = append(scope, container) } return &RelationUnit{ st: r.st, relation: r, unit: u, endpoint: ep, scope: strings.Join(scope, "#"), }, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/endpoint_test.go���������������������������������0000644�0000153�0000161�00000003622�12321735642�025216� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/state" ) type EndpointSuite struct { } var _ = gc.Suite(&EndpointSuite{}) var canRelateTests = []struct { role1, role2 charm.RelationRole success bool }{ {charm.RoleProvider, charm.RoleRequirer, true}, {charm.RoleRequirer, charm.RolePeer, false}, {charm.RolePeer, charm.RoleProvider, false}, {charm.RoleProvider, charm.RoleProvider, false}, {charm.RoleRequirer, charm.RoleRequirer, false}, {charm.RolePeer, charm.RolePeer, false}, } func (s *EndpointSuite) TestCanRelate(c *gc.C) { for i, t := range canRelateTests { c.Logf("test %d", i) ep1 := state.Endpoint{ ServiceName: "one-service", Relation: charm.Relation{ Interface: "ifce", Name: "foo", Role: t.role1, Scope: charm.ScopeGlobal, }, } ep2 := state.Endpoint{ ServiceName: "another-service", Relation: charm.Relation{ Interface: "ifce", Name: "bar", Role: t.role2, Scope: charm.ScopeGlobal, }, } if t.success { c.Assert(ep1.CanRelateTo(ep2), gc.Equals, true) c.Assert(ep2.CanRelateTo(ep1), gc.Equals, true) ep1.Interface = "different" } c.Assert(ep1.CanRelateTo(ep2), gc.Equals, false) c.Assert(ep2.CanRelateTo(ep1), gc.Equals, false) } ep1 := state.Endpoint{ ServiceName: "same-service", Relation: charm.Relation{ Interface: "ifce", Name: "foo", Role: charm.RoleProvider, Scope: charm.ScopeGlobal, }, } ep2 := state.Endpoint{ ServiceName: "same-service", Relation: charm.Relation{ Interface: "ifce", Name: "bar", Role: charm.RoleRequirer, Scope: charm.ScopeGlobal, }, } c.Assert(ep1.CanRelateTo(ep2), gc.Equals, false) c.Assert(ep2.CanRelateTo(ep1), gc.Equals, false) } ��������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/sequence.go��������������������������������������0000644�0000153�0000161�00000001176�12321735642�024151� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" ) type sequenceDoc struct { Name string `bson:"_id"` Counter int } func (s *State) sequence(name string) (int, error) { query := s.db.C("sequence").Find(bson.D{{"_id", name}}) inc := mgo.Change{ Update: bson.M{"$inc": bson.M{"counter": 1}}, Upsert: true, } result := &sequenceDoc{} _, err := query.Apply(inc, result) if err != nil { return -1, fmt.Errorf("cannot increment %q sequence number: %v", name, err) } return result.Counter, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/settings_test.go���������������������������������0000644�0000153�0000161�00000035377�12321735642�025252� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( jc "github.com/juju/testing/checkers" "labix.org/v2/mgo/txn" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type SettingsSuite struct { testbase.LoggingSuite testing.MgoSuite state *State key string } var _ = gc.Suite(&SettingsSuite{}) // TestingStateInfo returns information suitable for // connecting to the testing state server. func TestingStateInfo() *Info { return &Info{ Addrs: []string{testing.MgoServer.Addr()}, CACert: []byte(testing.CACert), } } // TestingDialOpts returns configuration parameters for // connecting to the testing state server. func TestingDialOpts() DialOpts { return DialOpts{ Timeout: testing.LongWait, } } func (s *SettingsSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *SettingsSuite) TearDownSuite(c *gc.C) { s.MgoSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *SettingsSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) // TODO(dfc) this logic is duplicated with the metawatcher_test. state, err := Open(TestingStateInfo(), TestingDialOpts(), Policy(nil)) c.Assert(err, gc.IsNil) s.state = state s.key = "config" } func (s *SettingsSuite) TearDownTest(c *gc.C) { s.state.Close() s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *SettingsSuite) TestCreateEmptySettings(c *gc.C) { node, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) c.Assert(node.Keys(), gc.DeepEquals, []string{}) } func (s *SettingsSuite) TestCannotOverwrite(c *gc.C) { _, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) _, err = createSettings(s.state, s.key, nil) c.Assert(err, gc.ErrorMatches, "cannot overwrite existing settings") } func (s *SettingsSuite) TestCannotReadMissing(c *gc.C) { _, err := readSettings(s.state, s.key) c.Assert(err, gc.ErrorMatches, "settings not found") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *SettingsSuite) TestCannotWriteMissing(c *gc.C) { node, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) err = removeSettings(s.state, s.key) c.Assert(err, gc.IsNil) node.Set("foo", "bar") _, err = node.Write() c.Assert(err, gc.ErrorMatches, "settings not found") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *SettingsSuite) TestUpdateWithWrite(c *gc.C) { node, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) options := map[string]interface{}{"alpha": "beta", "one": 1} node.Update(options) changes, err := node.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "alpha", nil, "beta"}, {ItemAdded, "one", nil, 1}, }) // Check local state. c.Assert(node.Map(), gc.DeepEquals, options) // Check MongoDB state. mgoData := make(map[string]interface{}, 0) err = s.MgoSuite.Session.DB("juju").C("settings").FindId(s.key).One(&mgoData) c.Assert(err, gc.IsNil) cleanSettingsMap(mgoData) c.Assert(mgoData, gc.DeepEquals, options) } func (s *SettingsSuite) TestConflictOnSet(c *gc.C) { // Check version conflict errors. nodeOne, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) nodeTwo, err := readSettings(s.state, s.key) c.Assert(err, gc.IsNil) optionsOld := map[string]interface{}{"alpha": "beta", "one": 1} nodeOne.Update(optionsOld) nodeOne.Write() nodeTwo.Update(optionsOld) changes, err := nodeTwo.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "alpha", nil, "beta"}, {ItemAdded, "one", nil, 1}, }) // First test node one. c.Assert(nodeOne.Map(), gc.DeepEquals, optionsOld) // Write on node one. optionsNew := map[string]interface{}{"alpha": "gamma", "one": "two"} nodeOne.Update(optionsNew) changes, err = nodeOne.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemModified, "alpha", "beta", "gamma"}, {ItemModified, "one", 1, "two"}, }) // Verify that node one reports as expected. c.Assert(nodeOne.Map(), gc.DeepEquals, optionsNew) // Verify that node two has still the old data. c.Assert(nodeTwo.Map(), gc.DeepEquals, optionsOld) // Now issue a Set/Write from node two. This will // merge the data deleting 'one' and updating // other values. optionsMerge := map[string]interface{}{"alpha": "cappa", "new": "next"} nodeTwo.Update(optionsMerge) nodeTwo.Delete("one") expected := map[string]interface{}{"alpha": "cappa", "new": "next"} changes, err = nodeTwo.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemModified, "alpha", "beta", "cappa"}, {ItemAdded, "new", nil, "next"}, {ItemDeleted, "one", 1, nil}, }) c.Assert(expected, gc.DeepEquals, nodeTwo.Map()) // But node one still reflects the former data. c.Assert(nodeOne.Map(), gc.DeepEquals, optionsNew) } func (s *SettingsSuite) TestSetItem(c *gc.C) { // Check that Set works as expected. node, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) options := map[string]interface{}{"alpha": "beta", "one": 1} node.Set("alpha", "beta") node.Set("one", 1) changes, err := node.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "alpha", nil, "beta"}, {ItemAdded, "one", nil, 1}, }) // Check local state. c.Assert(node.Map(), gc.DeepEquals, options) // Check MongoDB state. mgoData := make(map[string]interface{}, 0) err = s.MgoSuite.Session.DB("juju").C("settings").FindId(s.key).One(&mgoData) c.Assert(err, gc.IsNil) cleanSettingsMap(mgoData) c.Assert(mgoData, gc.DeepEquals, options) } func (s *SettingsSuite) TestSetItemEscape(c *gc.C) { // Check that Set works as expected. node, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) options := map[string]interface{}{"$bar": 1, "foo.alpha": "beta"} node.Set("foo.alpha", "beta") node.Set("$bar", 1) changes, err := node.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "$bar", nil, 1}, {ItemAdded, "foo.alpha", nil, "beta"}, }) // Check local state. c.Assert(node.Map(), gc.DeepEquals, options) // Check MongoDB state. mgoOptions := map[string]interface{}{"\uff04bar": 1, "foo\uff0ealpha": "beta"} mgoData := make(map[string]interface{}, 0) err = s.MgoSuite.Session.DB("juju").C("settings").FindId(s.key).One(&mgoData) c.Assert(err, gc.IsNil) cleanMgoSettings(mgoData) c.Assert(mgoData, gc.DeepEquals, mgoOptions) // Now get another state by reading from the database instance and // check read state has replaced '.' and '$' after fetching from // MongoDB. nodeTwo, err := readSettings(s.state, s.key) c.Assert(err, gc.IsNil) c.Assert(nodeTwo.disk, gc.DeepEquals, options) c.Assert(nodeTwo.core, gc.DeepEquals, options) } func (s *SettingsSuite) TestReplaceSettingsEscape(c *gc.C) { // Check that replaceSettings works as expected. node, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) node.Set("foo.alpha", "beta") node.Set("$bar", 1) _, err = node.Write() c.Assert(err, gc.IsNil) options := map[string]interface{}{"$baz": 1, "foo.bar": "beta"} rop, settingsChanged, err := replaceSettingsOp(s.state, s.key, options) c.Assert(err, gc.IsNil) ops := []txn.Op{rop} err = node.st.runTransaction(ops) c.Assert(err, gc.IsNil) changed, err := settingsChanged() c.Assert(err, gc.IsNil) c.Assert(changed, gc.Equals, true) // Check MongoDB state. mgoOptions := map[string]interface{}{"\uff04baz": 1, "foo\uff0ebar": "beta"} mgoData := make(map[string]interface{}, 0) err = s.MgoSuite.Session.DB("juju").C("settings").FindId(s.key).One(&mgoData) c.Assert(err, gc.IsNil) cleanMgoSettings(mgoData) c.Assert(mgoData, gc.DeepEquals, mgoOptions) } func (s *SettingsSuite) TestCreateSettingsEscape(c *gc.C) { // Check that createSettings works as expected. options := map[string]interface{}{"$baz": 1, "foo.bar": "beta"} node, err := createSettings(s.state, s.key, options) c.Assert(err, gc.IsNil) // Check local state. c.Assert(node.Map(), gc.DeepEquals, options) // Check MongoDB state. mgoOptions := map[string]interface{}{"\uff04baz": 1, "foo\uff0ebar": "beta"} mgoData := make(map[string]interface{}, 0) err = s.MgoSuite.Session.DB("juju").C("settings").FindId(s.key).One(&mgoData) c.Assert(err, gc.IsNil) cleanMgoSettings(mgoData) c.Assert(mgoData, gc.DeepEquals, mgoOptions) } func (s *SettingsSuite) TestMultipleReads(c *gc.C) { // Check that reads without writes always resets the data. nodeOne, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) nodeOne.Update(map[string]interface{}{"alpha": "beta", "foo": "bar"}) value, ok := nodeOne.Get("alpha") c.Assert(ok, gc.Equals, true) c.Assert(value, gc.Equals, "beta") value, ok = nodeOne.Get("foo") c.Assert(ok, gc.Equals, true) c.Assert(value, gc.Equals, "bar") value, ok = nodeOne.Get("baz") c.Assert(ok, gc.Equals, false) // A read resets the data to the empty state. err = nodeOne.Read() c.Assert(err, gc.IsNil) c.Assert(nodeOne.Map(), gc.DeepEquals, map[string]interface{}{}) nodeOne.Update(map[string]interface{}{"alpha": "beta", "foo": "bar"}) changes, err := nodeOne.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "alpha", nil, "beta"}, {ItemAdded, "foo", nil, "bar"}, }) // A write retains the newly set values. value, ok = nodeOne.Get("alpha") c.Assert(ok, gc.Equals, true) c.Assert(value, gc.Equals, "beta") value, ok = nodeOne.Get("foo") c.Assert(ok, gc.Equals, true) c.Assert(value, gc.Equals, "bar") // Now get another state instance and change underlying state. nodeTwo, err := readSettings(s.state, s.key) c.Assert(err, gc.IsNil) nodeTwo.Update(map[string]interface{}{"foo": "different"}) changes, err = nodeTwo.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemModified, "foo", "bar", "different"}, }) // This should pull in the new state into node one. err = nodeOne.Read() c.Assert(err, gc.IsNil) value, ok = nodeOne.Get("alpha") c.Assert(ok, gc.Equals, true) c.Assert(value, gc.Equals, "beta") value, ok = nodeOne.Get("foo") c.Assert(ok, gc.Equals, true) c.Assert(value, gc.Equals, "different") } func (s *SettingsSuite) TestDeleteEmptiesState(c *gc.C) { node, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) node.Set("a", "foo") changes, err := node.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "a", nil, "foo"}, }) node.Delete("a") changes, err = node.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemDeleted, "a", "foo", nil}, }) c.Assert(node.Map(), gc.DeepEquals, map[string]interface{}{}) } func (s *SettingsSuite) TestReadResync(c *gc.C) { // Check that read pulls the data into the node. nodeOne, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) nodeOne.Set("a", "foo") changes, err := nodeOne.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "a", nil, "foo"}, }) nodeTwo, err := readSettings(s.state, s.key) c.Assert(err, gc.IsNil) nodeTwo.Delete("a") changes, err = nodeTwo.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemDeleted, "a", "foo", nil}, }) nodeTwo.Set("a", "bar") changes, err = nodeTwo.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "a", nil, "bar"}, }) // Read of node one should pick up the new value. err = nodeOne.Read() c.Assert(err, gc.IsNil) value, ok := nodeOne.Get("a") c.Assert(ok, gc.Equals, true) c.Assert(value, gc.Equals, "bar") } func (s *SettingsSuite) TestMultipleWrites(c *gc.C) { // Check that multiple writes only do the right changes. node, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) node.Update(map[string]interface{}{"foo": "bar", "this": "that"}) changes, err := node.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "foo", nil, "bar"}, {ItemAdded, "this", nil, "that"}, }) node.Delete("this") node.Set("another", "value") changes, err = node.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "another", nil, "value"}, {ItemDeleted, "this", "that", nil}, }) expected := map[string]interface{}{"foo": "bar", "another": "value"} c.Assert(expected, gc.DeepEquals, node.Map()) changes, err = node.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{}) err = node.Read() c.Assert(err, gc.IsNil) c.Assert(expected, gc.DeepEquals, node.Map()) changes, err = node.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{}) } func (s *SettingsSuite) TestMultipleWritesAreStable(c *gc.C) { node, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) node.Update(map[string]interface{}{"foo": "bar", "this": "that"}) _, err = node.Write() c.Assert(err, gc.IsNil) mgoData := make(map[string]interface{}) err = s.MgoSuite.Session.DB("juju").C("settings").FindId(s.key).One(&mgoData) c.Assert(err, gc.IsNil) version := mgoData["version"] for i := 0; i < 100; i++ { node.Set("value", i) node.Set("foo", "bar") node.Delete("value") node.Set("this", "that") _, err := node.Write() c.Assert(err, gc.IsNil) } mgoData = make(map[string]interface{}) err = s.MgoSuite.Session.DB("juju").C("settings").FindId(s.key).One(&mgoData) c.Assert(err, gc.IsNil) newVersion := mgoData["version"] c.Assert(version, gc.Equals, newVersion) } func (s *SettingsSuite) TestWriteTwice(c *gc.C) { // Check the correct writing into a node by two config nodes. nodeOne, err := createSettings(s.state, s.key, nil) c.Assert(err, gc.IsNil) nodeOne.Set("a", "foo") changes, err := nodeOne.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemAdded, "a", nil, "foo"}, }) nodeTwo, err := readSettings(s.state, s.key) c.Assert(err, gc.IsNil) nodeTwo.Set("a", "bar") changes, err = nodeTwo.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{ {ItemModified, "a", "foo", "bar"}, }) // Shouldn't write again. Changes were already // flushed and acted upon by other parties. changes, err = nodeOne.Write() c.Assert(err, gc.IsNil) c.Assert(changes, gc.DeepEquals, []ItemChange{}) err = nodeOne.Read() c.Assert(err, gc.IsNil) c.Assert(nodeOne.key, gc.Equals, nodeTwo.key) c.Assert(nodeOne.disk, gc.DeepEquals, nodeTwo.disk) c.Assert(nodeOne.core, gc.DeepEquals, nodeTwo.core) } // cleanMgoSettings will remove MongoDB-specific settings but not unescape any // keys, as opposed to cleanSettingsMap which does unescape keys. func cleanMgoSettings(in map[string]interface{}) { delete(in, "_id") delete(in, "txn-revno") delete(in, "txn-queue") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/statecmd/����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023576� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/statecmd/destroymachines.go����������������������0000644�0000153�0000161�00000002101�12321735776�027343� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package statecmd import ( "fmt" "strings" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" ) // DestroyMachines1dot16 destroys the machines with the specified ids. // This is copied from the 1.16.3 code to enable compatibility. It should be // removed when we release a version that goes via the API only (whatever is // after 1.18) func DestroyMachines1dot16(st *state.State, ids ...string) (err error) { var errs []string for _, id := range ids { machine, err := st.Machine(id) switch { case errors.IsNotFoundError(err): err = fmt.Errorf("machine %s does not exist", id) case err != nil: case machine.Life() != state.Alive: continue default: err = machine.Destroy() } if err != nil { errs = append(errs, err.Error()) } } if len(errs) == 0 { return nil } msg := "some machines were not destroyed" if len(errs) == len(ids) { msg = "no machines were destroyed" } return fmt.Errorf("%s: %s", msg, strings.Join(errs, "; ")) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/statecmd/machineconfig.go������������������������0000644�0000153�0000161�00000004726�12321735776�026753� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package statecmd import ( "fmt" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/cloudinit" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/state" "launchpad.net/juju-core/tools" ) func findInstanceTools(env environs.Environ, series, arch string) (*tools.Tools, error) { agentVersion, ok := env.Config().AgentVersion() if !ok { return nil, fmt.Errorf("no agent version set in environment configuration") } possibleTools, err := envtools.FindInstanceTools(env, agentVersion, series, &arch) if err != nil { return nil, err } return possibleTools[0], nil } // MachineConfig returns information from the environment config that is // needed for machine cloud-init (for non-state servers only). // // The code is here so that it can be shared between the API server // (for ProvisioningScript) and the CLI (for manual provisioning) for maintaining // compatibiility with juju-1.16 (where the API MachineConfig did not exist). // When we drop 1.16 compatibility, this code should be moved into apiserver. func MachineConfig(st *state.State, machineId, nonce, dataDir string) (*cloudinit.MachineConfig, error) { environConfig, err := st.EnvironConfig() if err != nil { return nil, err } // Get the machine so we can get its series and arch. // If the Arch is not set in hardware-characteristics, // an error is returned. machine, err := st.Machine(machineId) if err != nil { return nil, err } hc, err := machine.HardwareCharacteristics() if err != nil { return nil, err } if hc.Arch == nil { return nil, fmt.Errorf("arch is not set for %q", machine.Tag()) } // Find the appropriate tools information. env, err := environs.New(environConfig) if err != nil { return nil, err } tools, err := findInstanceTools(env, machine.Series(), *hc.Arch) if err != nil { return nil, err } // Find the secrets and API endpoints. auth, err := environs.NewEnvironAuthenticator(env) if err != nil { return nil, err } stateInfo, apiInfo, err := auth.SetupAuthentication(machine) if err != nil { return nil, err } mcfg := environs.NewMachineConfig(machineId, nonce, stateInfo, apiInfo) if dataDir != "" { mcfg.DataDir = dataDir } mcfg.Tools = tools err = environs.FinishMachineConfig(mcfg, environConfig, constraints.Value{}) if err != nil { return nil, err } return mcfg, nil } ������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/statecmd/machineconfig_test.go�������������������0000644�0000153�0000161�00000005754�12321735776�030014� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package statecmd_test import ( "fmt" stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/statecmd" coretesting "launchpad.net/juju-core/testing" coretools "launchpad.net/juju-core/tools" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type machineConfigSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&machineConfigSuite{}) func (s *machineConfigSuite) TestMachineConfig(c *gc.C) { addrs := []instance.Address{instance.NewAddress("1.2.3.4")} hc := instance.MustParseHardware("mem=4G arch=amd64") apiParams := params.AddMachineParams{ Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: instance.Id("1234"), Nonce: "foo", HardwareCharacteristics: hc, Addrs: addrs, } machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) c.Assert(err, gc.IsNil) c.Assert(len(machines), gc.Equals, 1) machineId := machines[0].Machine machineConfig, err := statecmd.MachineConfig(s.State, machineId, apiParams.Nonce, "") c.Assert(err, gc.IsNil) envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) env, err := environs.New(envConfig) c.Assert(err, gc.IsNil) stateInfo, apiInfo, err := env.StateInfo() c.Assert(err, gc.IsNil) c.Check(machineConfig.StateInfo.Addrs, gc.DeepEquals, stateInfo.Addrs) c.Check(machineConfig.APIInfo.Addrs, gc.DeepEquals, apiInfo.Addrs) c.Assert(machineConfig.Tools.URL, gc.Not(gc.Equals), "") } func (s *machineConfigSuite) TestMachineConfigNoArch(c *gc.C) { apiParams := params.AddMachineParams{ Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: instance.Id("1234"), Nonce: "foo", } machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) c.Assert(err, gc.IsNil) c.Assert(len(machines), gc.Equals, 1) _, err = statecmd.MachineConfig(s.State, machines[0].Machine, apiParams.Nonce, "") c.Assert(err, gc.ErrorMatches, fmt.Sprintf("arch is not set for %q", "machine-"+machines[0].Machine)) } func (s *machineConfigSuite) TestMachineConfigNoTools(c *gc.C) { s.PatchValue(&envtools.DefaultBaseURL, "") addrs := []instance.Address{instance.NewAddress("1.2.3.4")} hc := instance.MustParseHardware("mem=4G arch=amd64") apiParams := params.AddMachineParams{ Series: "quantal", Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: instance.Id("1234"), Nonce: "foo", HardwareCharacteristics: hc, Addrs: addrs, } machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) c.Assert(err, gc.IsNil) _, err = statecmd.MachineConfig(s.State, machines[0].Machine, apiParams.Nonce, "") c.Assert(err, gc.ErrorMatches, coretools.ErrNoMatches.Error()) } ��������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/statecmd/status.go�������������������������������0000644�0000153�0000161�00000034140�12321735642�025465� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package statecmd import ( "fmt" "path" "regexp" "strings" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils/set" ) func Status(conn *juju.Conn, patterns []string) (*api.Status, error) { var nilStatus api.Status var context statusContext unitMatcher, err := NewUnitMatcher(patterns) if err != nil { return &nilStatus, err } if context.services, context.units, context.latestCharms, err = fetchAllServicesAndUnits(conn.State, unitMatcher); err != nil { return &nilStatus, err } // Filter machines by units in scope. var machineIds *set.Strings if !unitMatcher.matchesAny() { machineIds, err = fetchUnitMachineIds(context.units) if err != nil { return &nilStatus, err } } if context.machines, err = fetchMachines(conn.State, machineIds); err != nil { return &nilStatus, err } return &api.Status{ EnvironmentName: conn.Environ.Name(), Machines: context.processMachines(), Services: context.processServices(), }, nil } type statusContext struct { machines map[string][]*state.Machine services map[string]*state.Service units map[string]map[string]*state.Unit latestCharms map[charm.URL]string } type unitMatcher struct { patterns []string } // matchesAny returns true if the unitMatcher will // match any unit, regardless of its attributes. func (m unitMatcher) matchesAny() bool { return len(m.patterns) == 0 } // matchUnit attempts to match a state.Unit to one of // a set of patterns, taking into account subordinate // relationships. func (m unitMatcher) matchUnit(u *state.Unit) bool { if m.matchesAny() { return true } // Keep the unit if: // (a) its name matches a pattern, or // (b) it's a principal and one of its subordinates matches, or // (c) it's a subordinate and its principal matches. // // Note: do *not* include a second subordinate if the principal is // only matched on account of a first subordinate matching. if m.matchString(u.Name()) { return true } if u.IsPrincipal() { for _, s := range u.SubordinateNames() { if m.matchString(s) { return true } } return false } principal, valid := u.PrincipalName() if !valid { panic("PrincipalName failed for subordinate unit") } return m.matchString(principal) } // matchString matches a string to one of the patterns in // the unit matcher, returning an error if a pattern with // invalid syntax is encountered. func (m unitMatcher) matchString(s string) bool { for _, pattern := range m.patterns { ok, err := path.Match(pattern, s) if err != nil { // We validate patterns, so should never get here. panic(fmt.Errorf("pattern syntax error in %q", pattern)) } else if ok { return true } } return false } // validPattern must match the parts of a unit or service name // pattern either side of the '/' for it to be valid. var validPattern = regexp.MustCompile("^[a-z0-9-*]+$") // NewUnitMatcher returns a unitMatcher that matches units // with one of the specified patterns, or all units if no // patterns are specified. // // An error will be returned if any of the specified patterns // is invalid. Patterns are valid if they contain only // alpha-numeric characters, hyphens, or asterisks (and one // optional '/' to separate service/unit). func NewUnitMatcher(patterns []string) (unitMatcher, error) { for i, pattern := range patterns { fields := strings.Split(pattern, "/") if len(fields) > 2 { return unitMatcher{}, fmt.Errorf("pattern %q contains too many '/' characters", pattern) } for _, f := range fields { if !validPattern.MatchString(f) { return unitMatcher{}, fmt.Errorf("pattern %q contains invalid characters", pattern) } } if len(fields) == 1 { patterns[i] += "/*" } } return unitMatcher{patterns}, nil } // fetchMachines returns a map from top level machine id to machines, where machines[0] is the host // machine and machines[1..n] are any containers (including nested ones). // // If machineIds is non-nil, only machines whose IDs are in the set are returned. func fetchMachines(st *state.State, machineIds *set.Strings) (map[string][]*state.Machine, error) { v := make(map[string][]*state.Machine) machines, err := st.AllMachines() if err != nil { return nil, err } // AllMachines gives us machines sorted by id. for _, m := range machines { if machineIds != nil && !machineIds.Contains(m.Id()) { continue } parentId, ok := m.ParentId() if !ok { // Only top level host machines go directly into the machine map. v[m.Id()] = []*state.Machine{m} } else { topParentId := state.TopParentId(m.Id()) machines, ok := v[topParentId] if !ok { panic(fmt.Errorf("unexpected machine id %q", parentId)) } machines = append(machines, m) v[topParentId] = machines } } return v, nil } // fetchAllServicesAndUnits returns a map from service name to service, // a map from service name to unit name to unit, and a map from base charm URL to latest URL. func fetchAllServicesAndUnits( st *state.State, unitMatcher unitMatcher) ( map[string]*state.Service, map[string]map[string]*state.Unit, map[charm.URL]string, error) { svcMap := make(map[string]*state.Service) unitMap := make(map[string]map[string]*state.Unit) latestCharms := make(map[charm.URL]string) services, err := st.AllServices() if err != nil { return nil, nil, nil, err } for _, s := range services { units, err := s.AllUnits() if err != nil { return nil, nil, nil, err } svcUnitMap := make(map[string]*state.Unit) for _, u := range units { if !unitMatcher.matchUnit(u) { continue } svcUnitMap[u.Name()] = u } if unitMatcher.matchesAny() || len(svcUnitMap) > 0 { unitMap[s.Name()] = svcUnitMap svcMap[s.Name()] = s // Record the base URL for the service's charm so that // the latest store revision can be looked up. charmURL, _ := s.CharmURL() if charmURL.Schema == "cs" { latestCharms[*charmURL.WithRevision(-1)] = "" } } } for baseURL, _ := range latestCharms { ch, err := st.LatestPlaceholderCharm(&baseURL) if errors.IsNotFoundError(err) { continue } if err != nil { return nil, nil, nil, err } latestCharms[baseURL] = ch.String() } return svcMap, unitMap, latestCharms, nil } // fetchUnitMachineIds returns a set of IDs for machines that // the specified units reside on, and those machines' ancestors. func fetchUnitMachineIds(units map[string]map[string]*state.Unit) (*set.Strings, error) { machineIds := new(set.Strings) for _, svcUnitMap := range units { for _, unit := range svcUnitMap { if !unit.IsPrincipal() { continue } mid, err := unit.AssignedMachineId() if err != nil { return nil, err } for mid != "" { machineIds.Add(mid) mid = state.ParentId(mid) } } } return machineIds, nil } func (context *statusContext) processMachines() map[string]api.MachineStatus { machinesMap := make(map[string]api.MachineStatus) for id, machines := range context.machines { hostStatus := context.makeMachineStatus(machines[0]) context.processMachine(machines, &hostStatus, 0) machinesMap[id] = hostStatus } return machinesMap } func (context *statusContext) processMachine(machines []*state.Machine, host *api.MachineStatus, startIndex int) (nextIndex int) { nextIndex = startIndex + 1 currentHost := host var previousContainer *api.MachineStatus for nextIndex < len(machines) { machine := machines[nextIndex] container := context.makeMachineStatus(machine) if currentHost.Id == state.ParentId(machine.Id()) { currentHost.Containers[machine.Id()] = container previousContainer = &container nextIndex++ } else { if state.NestingLevel(machine.Id()) > state.NestingLevel(previousContainer.Id) { nextIndex = context.processMachine(machines, previousContainer, nextIndex-1) } else { break } } } return } func (context *statusContext) makeMachineStatus(machine *state.Machine) (status api.MachineStatus) { status.Id = machine.Id() status.Life, status.AgentVersion, status.AgentState, status.AgentStateInfo, status.Err = processAgent(machine) status.Series = machine.Series() instid, err := machine.InstanceId() if err == nil { status.InstanceId = instid status.InstanceState, err = machine.InstanceStatus() if err != nil { status.InstanceState = "error" } status.DNSName = instance.SelectPublicAddress(machine.Addresses()) } else { if state.IsNotProvisionedError(err) { status.InstanceId = "pending" } else { status.InstanceId = "error" } // There's no point in reporting a pending agent state // if the machine hasn't been provisioned. This // also makes unprovisioned machines visually distinct // in the output. status.AgentState = "" } hc, err := machine.HardwareCharacteristics() if err != nil { if !errors.IsNotFoundError(err) { status.Hardware = "error" } } else { status.Hardware = hc.String() } status.Containers = make(map[string]api.MachineStatus) return } func (context *statusContext) processServices() map[string]api.ServiceStatus { servicesMap := make(map[string]api.ServiceStatus) for _, s := range context.services { servicesMap[s.Name()] = context.processService(s) } return servicesMap } func (context *statusContext) processService(service *state.Service) (status api.ServiceStatus) { serviceCharmURL, _ := service.CharmURL() status.Charm = serviceCharmURL.String() status.Exposed = service.IsExposed() status.Life = processLife(service) latestCharm, ok := context.latestCharms[*serviceCharmURL.WithRevision(-1)] if ok && latestCharm != serviceCharmURL.String() { status.CanUpgradeTo = latestCharm } var err error status.Relations, status.SubordinateTo, err = context.processRelations(service) if err != nil { status.Err = err return } includeNetworks, excludeNetworks, err := service.Networks() if err == nil { status.Networks = api.NetworksSpecification{ Enabled: includeNetworks, Disabled: excludeNetworks, } } if service.IsPrincipal() { status.Units = context.processUnits(context.units[service.Name()], serviceCharmURL.String()) } return status } func (context *statusContext) processUnits(units map[string]*state.Unit, serviceCharm string) map[string]api.UnitStatus { unitsMap := make(map[string]api.UnitStatus) for _, unit := range units { unitsMap[unit.Name()] = context.processUnit(unit, serviceCharm) } return unitsMap } func (context *statusContext) processUnit(unit *state.Unit, serviceCharm string) (status api.UnitStatus) { status.PublicAddress, _ = unit.PublicAddress() for _, port := range unit.OpenedPorts() { status.OpenedPorts = append(status.OpenedPorts, port.String()) } if unit.IsPrincipal() { status.Machine, _ = unit.AssignedMachineId() } curl, _ := unit.CharmURL() if serviceCharm != "" && curl != nil && curl.String() != serviceCharm { status.Charm = curl.String() } status.Life, status.AgentVersion, status.AgentState, status.AgentStateInfo, status.Err = processAgent(unit) if subUnits := unit.SubordinateNames(); len(subUnits) > 0 { status.Subordinates = make(map[string]api.UnitStatus) for _, name := range subUnits { subUnit := context.unitByName(name) // subUnit may be nil if subordinate was filtered out. if subUnit != nil { status.Subordinates[name] = context.processUnit(subUnit, serviceCharm) } } } return } func (context *statusContext) unitByName(name string) *state.Unit { serviceName := strings.Split(name, "/")[0] return context.units[serviceName][name] } func (*statusContext) processRelations(service *state.Service) (related map[string][]string, subord []string, err error) { // TODO(mue) This way the same relation is read twice (for each service). // Maybe add Relations() to state, read them only once and pass them to each // call of this function. relations, err := service.Relations() if err != nil { return nil, nil, err } var subordSet set.Strings related = make(map[string][]string) for _, relation := range relations { ep, err := relation.Endpoint(service.Name()) if err != nil { return nil, nil, err } relationName := ep.Relation.Name eps, err := relation.RelatedEndpoints(service.Name()) if err != nil { return nil, nil, err } for _, ep := range eps { if ep.Scope == charm.ScopeContainer && !service.IsPrincipal() { subordSet.Add(ep.ServiceName) } related[relationName] = append(related[relationName], ep.ServiceName) } } for relationName, serviceNames := range related { sn := set.NewStrings(serviceNames...) related[relationName] = sn.SortedValues() } return related, subordSet.SortedValues(), nil } type lifer interface { Life() state.Life } type stateAgent interface { lifer AgentAlive() (bool, error) AgentTools() (*tools.Tools, error) Status() (params.Status, string, params.StatusData, error) } // processAgent retrieves version and status information from the given entity // and sets the destination version, status and info values accordingly. func processAgent(entity stateAgent) (life string, version string, status params.Status, info string, err error) { life = processLife(entity) if t, err := entity.AgentTools(); err == nil { version = t.Version.Number.String() } // TODO(mue) StatusData may be useful here too. status, info, _, err = entity.Status() if err != nil { return } if status == params.StatusPending { // The status is pending - there's no point // in enquiring about the agent liveness. return } agentAlive, err := entity.AgentAlive() if err != nil { return } if entity.Life() != state.Dead && !agentAlive { // The agent *should* be alive but is not. // Add the original status to the info, so it's not lost. if info != "" { info = fmt.Sprintf("(%s: %s)", status, info) } else { info = fmt.Sprintf("(%s)", status) } status = params.StatusDown } return } func processLife(entity lifer) string { if life := entity.Life(); life != state.Alive { // alive is the usual state so omit it by default. return life.String() } return "" } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/relation_internal_test.go������������������������0000644�0000153�0000161�00000001647�12321735642�027114� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" ) type RelationSuite struct{} var _ = gc.Suite(&RelationSuite{}) // TestRelatedEndpoints verifies the behaviour of RelatedEndpoints in // multi-endpoint peer relations, which are currently not constructable // by normal means. func (s *RelationSuite) TestRelatedEndpoints(c *gc.C) { rel := charm.Relation{ Interface: "ifce", Name: "group", Role: charm.RolePeer, Scope: charm.ScopeGlobal, } eps := []Endpoint{{ ServiceName: "jeff", Relation: rel, }, { ServiceName: "mike", Relation: rel, }, { ServiceName: "mike", Relation: rel, }} r := &Relation{nil, relationDoc{Endpoints: eps}} relatedEps, err := r.RelatedEndpoints("mike") c.Assert(err, gc.IsNil) c.Assert(relatedEps, gc.DeepEquals, eps) } �����������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/machine_test.go����������������������������������0000644�0000153�0000161�00000133251�12321735776�025014� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "sort" jc "github.com/juju/testing/checkers" "labix.org/v2/mgo/bson" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/version" ) type MachineSuite struct { ConnSuite machine0 *state.Machine machine *state.Machine } var _ = gc.Suite(&MachineSuite{}) func (s *MachineSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) var err error s.machine0, err = s.State.AddMachine("quantal", state.JobManageEnviron) s.machine, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) } func (s *MachineSuite) TestContainerDefaults(c *gc.C) { c.Assert(string(s.machine.ContainerType()), gc.Equals, "") containers, err := s.machine.Containers() c.Assert(err, gc.IsNil) c.Assert(containers, gc.DeepEquals, []string(nil)) } func (s *MachineSuite) TestMachineJobFromParams(c *gc.C) { for stateMachineJob, paramsMachineJob := range state.JobNames { job, err := state.MachineJobFromParams(paramsMachineJob) c.Assert(err, gc.IsNil) c.Assert(job, gc.Equals, stateMachineJob) } _, err := state.MachineJobFromParams("invalid") c.Assert(err, gc.NotNil) } func (s *MachineSuite) TestParentId(c *gc.C) { parentId, ok := s.machine.ParentId() c.Assert(parentId, gc.Equals, "") c.Assert(ok, gc.Equals, false) container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, s.machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) parentId, ok = container.ParentId() c.Assert(parentId, gc.Equals, s.machine.Id()) c.Assert(ok, gc.Equals, true) } func (s *MachineSuite) TestMachineIsManager(c *gc.C) { c.Assert(s.machine0.IsManager(), jc.IsTrue) c.Assert(s.machine.IsManager(), jc.IsFalse) } func (s *MachineSuite) TestMachineIsManualBootstrap(c *gc.C) { cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.Type(), gc.Not(gc.Equals), "null") c.Assert(s.machine.Id(), gc.Equals, "1") manual, err := s.machine0.IsManual() c.Assert(err, gc.IsNil) c.Assert(manual, jc.IsFalse) attrs := map[string]interface{}{"type": "null"} err = s.State.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) manual, err = s.machine0.IsManual() c.Assert(err, gc.IsNil) c.Assert(manual, jc.IsTrue) } func (s *MachineSuite) TestMachineIsManual(c *gc.C) { tests := []struct { instanceId instance.Id nonce string isManual bool }{ {instanceId: "x", nonce: "y", isManual: false}, {instanceId: "manual:", nonce: "y", isManual: false}, {instanceId: "x", nonce: "manual:", isManual: true}, {instanceId: "x", nonce: "manual:y", isManual: true}, {instanceId: "x", nonce: "manual", isManual: false}, } for _, test := range tests { m, err := s.State.AddOneMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, InstanceId: test.instanceId, Nonce: test.nonce, }) c.Assert(err, gc.IsNil) isManual, err := m.IsManual() c.Assert(isManual, gc.Equals, test.isManual) } } func (s *MachineSuite) TestLifeJobManageEnviron(c *gc.C) { // A JobManageEnviron machine must never advance lifecycle. m := s.machine0 err := m.Destroy() c.Assert(err, gc.ErrorMatches, "machine 0 is required by the environment") err = m.ForceDestroy() c.Assert(err, gc.ErrorMatches, "machine 0 is required by the environment") err = m.EnsureDead() c.Assert(err, gc.ErrorMatches, "machine 0 is required by the environment") } func (s *MachineSuite) TestLifeMachineWithContainer(c *gc.C) { // A machine hosting a container must not advance lifecycle. _, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, s.machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) err = s.machine.Destroy() c.Assert(err, gc.FitsTypeOf, &state.HasContainersError{}) c.Assert(err, gc.ErrorMatches, `machine 1 is hosting containers "1/lxc/0"`) err1 := s.machine.EnsureDead() c.Assert(err1, gc.DeepEquals, err) c.Assert(s.machine.Life(), gc.Equals, state.Alive) } func (s *MachineSuite) TestLifeJobHostUnits(c *gc.C) { // A machine with an assigned unit must not advance lifecycle. svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToMachine(s.machine) c.Assert(err, gc.IsNil) err = s.machine.Destroy() c.Assert(err, gc.FitsTypeOf, &state.HasAssignedUnitsError{}) c.Assert(err, gc.ErrorMatches, `machine 1 has unit "wordpress/0" assigned`) err1 := s.machine.EnsureDead() c.Assert(err1, gc.DeepEquals, err) c.Assert(s.machine.Life(), gc.Equals, state.Alive) // Once no unit is assigned, lifecycle can advance. err = unit.UnassignFromMachine() c.Assert(err, gc.IsNil) err = s.machine.Destroy() c.Assert(s.machine.Life(), gc.Equals, state.Dying) c.Assert(err, gc.IsNil) err = s.machine.EnsureDead() c.Assert(err, gc.IsNil) c.Assert(s.machine.Life(), gc.Equals, state.Dead) // A machine that has never had units assigned can advance lifecycle. m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = m.Destroy() c.Assert(err, gc.IsNil) c.Assert(m.Life(), gc.Equals, state.Dying) err = m.EnsureDead() c.Assert(err, gc.IsNil) c.Assert(m.Life(), gc.Equals, state.Dead) } func (s *MachineSuite) TestDestroyAbort(c *gc.C) { defer state.SetBeforeHooks(c, s.State, func() { c.Assert(s.machine.Destroy(), gc.IsNil) }).Check() err := s.machine.Destroy() c.Assert(err, gc.IsNil) } func (s *MachineSuite) TestDestroyCancel(c *gc.C) { svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) defer state.SetBeforeHooks(c, s.State, func() { c.Assert(unit.AssignToMachine(s.machine), gc.IsNil) }).Check() err = s.machine.Destroy() c.Assert(err, gc.FitsTypeOf, &state.HasAssignedUnitsError{}) } func (s *MachineSuite) TestDestroyContention(c *gc.C) { svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) perturb := state.TransactionHook{ Before: func() { c.Assert(unit.AssignToMachine(s.machine), gc.IsNil) }, After: func() { c.Assert(unit.UnassignFromMachine(), gc.IsNil) }, } defer state.SetTransactionHooks( c, s.State, perturb, perturb, perturb, ).Check() err = s.machine.Destroy() c.Assert(err, gc.ErrorMatches, "machine 1 cannot advance lifecycle: state changing too quickly; try again soon") } func (s *MachineSuite) TestRemove(c *gc.C) { err := s.machine.Remove() c.Assert(err, gc.ErrorMatches, "cannot remove machine 1: machine is not dead") err = s.machine.EnsureDead() c.Assert(err, gc.IsNil) err = s.machine.Remove() c.Assert(err, gc.IsNil) err = s.machine.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) _, err = s.machine.HardwareCharacteristics() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) _, err = s.machine.Containers() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) include, exclude, err := s.machine.Networks() c.Assert(err, gc.IsNil) c.Assert(include, gc.HasLen, 0) c.Assert(exclude, gc.HasLen, 0) err = s.machine.Remove() c.Assert(err, gc.IsNil) } func (s *MachineSuite) TestHasVote(c *gc.C) { c.Assert(s.machine.HasVote(), jc.IsFalse) // Make another machine value so that // it won't have the cached HasVote value. m, err := s.State.Machine(s.machine.Id()) c.Assert(err, gc.IsNil) err = s.machine.SetHasVote(true) c.Assert(err, gc.IsNil) c.Assert(s.machine.HasVote(), jc.IsTrue) c.Assert(m.HasVote(), jc.IsFalse) err = m.Refresh() c.Assert(err, gc.IsNil) c.Assert(m.HasVote(), jc.IsTrue) err = m.SetHasVote(false) c.Assert(err, gc.IsNil) c.Assert(m.HasVote(), jc.IsFalse) c.Assert(s.machine.HasVote(), jc.IsTrue) err = s.machine.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine.HasVote(), jc.IsFalse) } func (s *MachineSuite) TestCannotDestroyMachineWithVote(c *gc.C) { err := s.machine.SetHasVote(true) c.Assert(err, gc.IsNil) // Make another machine value so that // it won't have the cached HasVote value. m, err := s.State.Machine(s.machine.Id()) c.Assert(err, gc.IsNil) err = s.machine.Destroy() c.Assert(err, gc.ErrorMatches, "machine "+s.machine.Id()+" is a voting replica set member") err = m.Destroy() c.Assert(err, gc.ErrorMatches, "machine "+s.machine.Id()+" is a voting replica set member") } func (s *MachineSuite) TestRemoveAbort(c *gc.C) { err := s.machine.EnsureDead() c.Assert(err, gc.IsNil) defer state.SetBeforeHooks(c, s.State, func() { c.Assert(s.machine.Remove(), gc.IsNil) }).Check() err = s.machine.Remove() c.Assert(err, gc.IsNil) } func (s *MachineSuite) TestMachineSetAgentAlive(c *gc.C) { alive, err := s.machine.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, false) pinger, err := s.machine.SetAgentAlive() c.Assert(err, gc.IsNil) c.Assert(pinger, gc.NotNil) defer pinger.Stop() s.State.StartSync() alive, err = s.machine.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, true) } func (s *MachineSuite) TestTag(c *gc.C) { c.Assert(s.machine.Tag(), gc.Equals, "machine-1") } func (s *MachineSuite) TestSetMongoPassword(c *gc.C) { testSetMongoPassword(c, func(st *state.State) (entity, error) { return st.Machine(s.machine.Id()) }) } func (s *MachineSuite) TestSetPassword(c *gc.C) { testSetPassword(c, func() (state.Authenticator, error) { return s.State.Machine(s.machine.Id()) }) } func (s *MachineSuite) TestSetAgentCompatPassword(c *gc.C) { e, err := s.State.Machine(s.machine.Id()) c.Assert(err, gc.IsNil) testSetAgentCompatPassword(c, e) } func (s *MachineSuite) TestMachineWaitAgentAlive(c *gc.C) { alive, err := s.machine.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, false) s.State.StartSync() err = s.machine.WaitAgentAlive(coretesting.ShortWait) c.Assert(err, gc.ErrorMatches, `waiting for agent of machine 1: still not alive after timeout`) pinger, err := s.machine.SetAgentAlive() c.Assert(err, gc.IsNil) s.State.StartSync() err = s.machine.WaitAgentAlive(coretesting.LongWait) c.Assert(err, gc.IsNil) alive, err = s.machine.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, true) err = pinger.Kill() c.Assert(err, gc.IsNil) s.State.StartSync() alive, err = s.machine.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, false) } func (s *MachineSuite) TestMachineNetworks(c *gc.C) { // s.machine is created without networks, so check // they're empty when we read them. include, exclude, err := s.machine.Networks() c.Assert(err, gc.IsNil) c.Assert(include, gc.HasLen, 0) c.Assert(exclude, gc.HasLen, 0) // Now create a machine with networks and read them back. machine, err := s.State.AddOneMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, IncludeNetworks: []string{"net1", "mynet"}, ExcludeNetworks: []string{"private-net", "logging"}, }) c.Assert(err, gc.IsNil) include, exclude, err = machine.Networks() c.Assert(err, gc.IsNil) c.Assert(include, jc.DeepEquals, []string{"net1", "mynet"}) c.Assert(exclude, jc.DeepEquals, []string{"private-net", "logging"}) // Finally, networks should be removed with the machine. err = machine.EnsureDead() c.Assert(err, gc.IsNil) err = machine.Remove() c.Assert(err, gc.IsNil) include, exclude, err = machine.Networks() c.Assert(err, gc.IsNil) c.Assert(include, gc.HasLen, 0) c.Assert(exclude, gc.HasLen, 0) } func (s *MachineSuite) TestMachineInstanceId(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = s.machines.Update( bson.D{{"_id", machine.Id()}}, bson.D{{"$set", bson.D{{"instanceid", "spaceship/0"}}}}, ) c.Assert(err, gc.IsNil) err = machine.Refresh() c.Assert(err, gc.IsNil) iid, err := machine.InstanceId() c.Assert(err, gc.IsNil) c.Assert(iid, gc.Equals, instance.Id("spaceship/0")) } func (s *MachineSuite) TestMachineInstanceIdCorrupt(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = s.machines.Update( bson.D{{"_id", machine.Id()}}, bson.D{{"$set", bson.D{{"instanceid", bson.D{{"foo", "bar"}}}}}}, ) c.Assert(err, gc.IsNil) err = machine.Refresh() c.Assert(err, gc.IsNil) iid, err := machine.InstanceId() c.Assert(err, jc.Satisfies, state.IsNotProvisionedError) c.Assert(iid, gc.Equals, instance.Id("")) } func (s *MachineSuite) TestMachineInstanceIdMissing(c *gc.C) { iid, err := s.machine.InstanceId() c.Assert(err, jc.Satisfies, state.IsNotProvisionedError) c.Assert(string(iid), gc.Equals, "") } func (s *MachineSuite) TestMachineInstanceIdBlank(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = s.machines.Update( bson.D{{"_id", machine.Id()}}, bson.D{{"$set", bson.D{{"instanceid", ""}}}}, ) c.Assert(err, gc.IsNil) err = machine.Refresh() c.Assert(err, gc.IsNil) iid, err := machine.InstanceId() c.Assert(err, jc.Satisfies, state.IsNotProvisionedError) c.Assert(string(iid), gc.Equals, "") } func (s *MachineSuite) TestMachineSetProvisionedUpdatesCharacteristics(c *gc.C) { // Before provisioning, there is no hardware characteristics. _, err := s.machine.HardwareCharacteristics() c.Assert(errors.IsNotFoundError(err), gc.Equals, true) arch := "amd64" mem := uint64(4096) expected := &instance.HardwareCharacteristics{ Arch: &arch, Mem: &mem, } err = s.machine.SetProvisioned("umbrella/0", "fake_nonce", expected) c.Assert(err, gc.IsNil) md, err := s.machine.HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Assert(*md, gc.DeepEquals, *expected) // Reload machine and check again. err = s.machine.Refresh() c.Assert(err, gc.IsNil) md, err = s.machine.HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Assert(*md, gc.DeepEquals, *expected) } func (s *MachineSuite) TestMachineSetCheckProvisioned(c *gc.C) { // Check before provisioning. c.Assert(s.machine.CheckProvisioned("fake_nonce"), gc.Equals, false) // Either one should not be empty. err := s.machine.SetProvisioned("umbrella/0", "", nil) c.Assert(err, gc.ErrorMatches, `cannot set instance data for machine "1": instance id and nonce cannot be empty`) err = s.machine.SetProvisioned("", "fake_nonce", nil) c.Assert(err, gc.ErrorMatches, `cannot set instance data for machine "1": instance id and nonce cannot be empty`) err = s.machine.SetProvisioned("", "", nil) c.Assert(err, gc.ErrorMatches, `cannot set instance data for machine "1": instance id and nonce cannot be empty`) err = s.machine.SetProvisioned("umbrella/0", "fake_nonce", nil) c.Assert(err, gc.IsNil) m, err := s.State.Machine(s.machine.Id()) c.Assert(err, gc.IsNil) id, err := m.InstanceId() c.Assert(err, gc.IsNil) c.Assert(string(id), gc.Equals, "umbrella/0") c.Assert(s.machine.CheckProvisioned("fake_nonce"), gc.Equals, true) // Clear the deprecated machineDoc InstanceId attribute and ensure that CheckProvisioned() // still works as expected with the new data model. state.SetMachineInstanceId(s.machine, "") id, err = s.machine.InstanceId() c.Assert(err, gc.IsNil) c.Assert(string(id), gc.Equals, "umbrella/0") c.Assert(s.machine.CheckProvisioned("fake_nonce"), gc.Equals, true) // Try it twice, it should fail. err = s.machine.SetProvisioned("doesn't-matter", "phony", nil) c.Assert(err, gc.ErrorMatches, `cannot set instance data for machine "1": already set`) // Check it with invalid nonce. c.Assert(s.machine.CheckProvisioned("not-really"), gc.Equals, false) } func (s *MachineSuite) TestMachineSetProvisionedWhenNotAlive(c *gc.C) { testWhenDying(c, s.machine, notAliveErr, notAliveErr, func() error { return s.machine.SetProvisioned("umbrella/0", "fake_nonce", nil) }) } func (s *MachineSuite) TestMachineSetInstanceStatus(c *gc.C) { // Machine needs to be provisioned first. err := s.machine.SetProvisioned("umbrella/0", "fake_nonce", nil) c.Assert(err, gc.IsNil) err = s.machine.SetInstanceStatus("ALIVE") c.Assert(err, gc.IsNil) // Reload machine and check result. err = s.machine.Refresh() c.Assert(err, gc.IsNil) status, err := s.machine.InstanceStatus() c.Assert(err, gc.IsNil) c.Assert(status, gc.DeepEquals, "ALIVE") } func (s *MachineSuite) TestNotProvisionedMachineSetInstanceStatus(c *gc.C) { err := s.machine.SetInstanceStatus("ALIVE") c.Assert(err, gc.ErrorMatches, ".* not provisioned") } func (s *MachineSuite) TestNotProvisionedMachineInstanceStatus(c *gc.C) { _, err := s.machine.InstanceStatus() c.Assert(err, jc.Satisfies, state.IsNotProvisionedError) } // SCHEMACHANGE func (s *MachineSuite) TestInstanceStatusOldSchema(c *gc.C) { // Machine needs to be provisioned first. err := s.machine.SetProvisioned("umbrella/0", "fake_nonce", nil) c.Assert(err, gc.IsNil) // Remove the InstanceId from instanceData doc to simulate an old schema. state.ClearInstanceDocId(c, s.machine) err = s.machine.SetInstanceStatus("ALIVE") c.Assert(err, gc.IsNil) // Reload machine and check result. err = s.machine.Refresh() c.Assert(err, gc.IsNil) status, err := s.machine.InstanceStatus() c.Assert(err, gc.IsNil) c.Assert(status, gc.DeepEquals, "ALIVE") } func (s *MachineSuite) TestMachineRefresh(c *gc.C) { m0, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) oldTools, _ := m0.AgentTools() m1, err := s.State.Machine(m0.Id()) c.Assert(err, gc.IsNil) err = m0.SetAgentVersion(version.MustParseBinary("0.0.3-series-arch")) c.Assert(err, gc.IsNil) newTools, _ := m0.AgentTools() m1Tools, _ := m1.AgentTools() c.Assert(m1Tools, gc.DeepEquals, oldTools) err = m1.Refresh() c.Assert(err, gc.IsNil) m1Tools, _ = m1.AgentTools() c.Assert(*m1Tools, gc.Equals, *newTools) err = m0.EnsureDead() c.Assert(err, gc.IsNil) err = m0.Remove() c.Assert(err, gc.IsNil) err = m0.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *MachineSuite) TestRefreshWhenNotAlive(c *gc.C) { // Refresh should work regardless of liveness status. testWhenDying(c, s.machine, noErr, noErr, func() error { return s.machine.Refresh() }) } func (s *MachineSuite) TestMachinePrincipalUnits(c *gc.C) { // Check that Machine.Units works correctly. // Make three machines, three services and three units for each service; // variously assign units to machines and check that Machine.Units // tells us the right thing. m1 := s.machine m2, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) m3, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) dummy := s.AddTestingCharm(c, "dummy") logging := s.AddTestingCharm(c, "logging") s0 := s.AddTestingService(c, "s0", dummy) s1 := s.AddTestingService(c, "s1", dummy) s2 := s.AddTestingService(c, "s2", dummy) s3 := s.AddTestingService(c, "s3", logging) units := make([][]*state.Unit, 4) for i, svc := range []*state.Service{s0, s1, s2} { units[i] = make([]*state.Unit, 3) for j := range units[i] { units[i][j], err = svc.AddUnit() c.Assert(err, gc.IsNil) } } // Add the logging units subordinate to the s2 units. eps, err := s.State.InferEndpoints([]string{"s2", "s3"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) for _, u := range units[2] { ru, err := rel.Unit(u) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) } units[3], err = s3.AllUnits() c.Assert(err, gc.IsNil) assignments := []struct { machine *state.Machine units []*state.Unit subordinates []*state.Unit }{ {m1, []*state.Unit{units[0][0]}, nil}, {m2, []*state.Unit{units[0][1], units[1][0], units[1][1], units[2][0]}, []*state.Unit{units[3][0]}}, {m3, []*state.Unit{units[2][2]}, []*state.Unit{units[3][2]}}, } for _, a := range assignments { for _, u := range a.units { err := u.AssignToMachine(a.machine) c.Assert(err, gc.IsNil) } } for i, a := range assignments { c.Logf("test %d", i) got, err := a.machine.Units() c.Assert(err, gc.IsNil) expect := sortedUnitNames(append(a.units, a.subordinates...)) c.Assert(sortedUnitNames(got), gc.DeepEquals, expect) } } func sortedUnitNames(units []*state.Unit) []string { names := make([]string, len(units)) for i, u := range units { names[i] = u.Name() } sort.Strings(names) return names } func (s *MachineSuite) assertMachineDirtyAfterAddingUnit(c *gc.C) (*state.Machine, *state.Service, *state.Unit) { m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(m.Clean(), gc.Equals, true) svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToMachine(m) c.Assert(err, gc.IsNil) c.Assert(m.Clean(), gc.Equals, false) return m, svc, unit } func (s *MachineSuite) TestMachineDirtyAfterAddingUnit(c *gc.C) { s.assertMachineDirtyAfterAddingUnit(c) } func (s *MachineSuite) TestMachineDirtyAfterUnassigningUnit(c *gc.C) { m, _, unit := s.assertMachineDirtyAfterAddingUnit(c) err := unit.UnassignFromMachine() c.Assert(err, gc.IsNil) c.Assert(m.Clean(), gc.Equals, false) } func (s *MachineSuite) TestMachineDirtyAfterRemovingUnit(c *gc.C) { m, svc, unit := s.assertMachineDirtyAfterAddingUnit(c) err := unit.EnsureDead() c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.IsNil) err = svc.Destroy() c.Assert(err, gc.IsNil) c.Assert(m.Clean(), gc.Equals, false) } func (s *MachineSuite) TestWatchMachine(c *gc.C) { w := s.machine.Watch() defer testing.AssertStop(c, w) // Initial event. wc := testing.NewNotifyWatcherC(c, s.State, w) wc.AssertOneChange() // Make one change (to a separate instance), check one event. machine, err := s.State.Machine(s.machine.Id()) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("m-foo", "fake_nonce", nil) c.Assert(err, gc.IsNil) wc.AssertOneChange() // Make two changes, check one event. err = machine.SetAgentVersion(version.MustParseBinary("0.0.3-series-arch")) c.Assert(err, gc.IsNil) err = machine.Destroy() c.Assert(err, gc.IsNil) wc.AssertOneChange() // Stop, check closed. testing.AssertStop(c, w) wc.AssertClosed() // Remove machine, start new watch, check single event. err = machine.EnsureDead() c.Assert(err, gc.IsNil) err = machine.Remove() c.Assert(err, gc.IsNil) w = s.machine.Watch() defer testing.AssertStop(c, w) testing.NewNotifyWatcherC(c, s.State, w).AssertOneChange() } func (s *MachineSuite) TestWatchDiesOnStateClose(c *gc.C) { // This test is testing logic in watcher.entityWatcher, which // is also used by: // Machine.WatchHardwareCharacteristics // Service.Watch // Unit.Watch // State.WatchForEnvironConfigChanges // Unit.WatchConfigSettings testWatcherDiesWhenStateCloses(c, func(c *gc.C, st *state.State) waiter { m, err := st.Machine(s.machine.Id()) c.Assert(err, gc.IsNil) w := m.Watch() <-w.Changes() return w }) } func (s *MachineSuite) TestWatchPrincipalUnits(c *gc.C) { // Start a watch on an empty machine; check no units reported. w := s.machine.WatchPrincipalUnits() defer testing.AssertStop(c, w) wc := testing.NewStringsWatcherC(c, s.State, w) wc.AssertChange() wc.AssertNoChange() // Change machine, and create a unit independently; no change. err := s.machine.SetProvisioned("cheese", "fake_nonce", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) mysql0, err := mysql.AddUnit() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Assign that unit (to a separate machine instance); change detected. machine, err := s.State.Machine(s.machine.Id()) c.Assert(err, gc.IsNil) err = mysql0.AssignToMachine(machine) c.Assert(err, gc.IsNil) wc.AssertChange("mysql/0") wc.AssertNoChange() // Change the unit; no change. err = mysql0.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Assign another unit and make the first Dying; check both changes detected. mysql1, err := mysql.AddUnit() c.Assert(err, gc.IsNil) err = mysql1.AssignToMachine(machine) c.Assert(err, gc.IsNil) err = mysql0.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange("mysql/0", "mysql/1") wc.AssertNoChange() // Add a subordinate to the Alive unit; no change. logging := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"mysql", "logging"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) mysqlru1, err := rel.Unit(mysql1) c.Assert(err, gc.IsNil) err = mysqlru1.EnterScope(nil) c.Assert(err, gc.IsNil) logging0, err := logging.Unit("logging/0") c.Assert(err, gc.IsNil) wc.AssertNoChange() // Change the subordinate; no change. err = logging0.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Make the Dying unit Dead; change detected. err = mysql0.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertChange("mysql/0") wc.AssertNoChange() // Stop watcher; check Changes chan closed. testing.AssertStop(c, w) wc.AssertClosed() // Start a fresh watcher; check both principals reported. w = s.machine.WatchPrincipalUnits() defer testing.AssertStop(c, w) wc = testing.NewStringsWatcherC(c, s.State, w) wc.AssertChange("mysql/0", "mysql/1") wc.AssertNoChange() // Remove the Dead unit; no change. err = mysql0.Remove() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Destroy the subordinate; no change. err = logging0.Destroy() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Unassign the unit; check change. err = mysql1.UnassignFromMachine() c.Assert(err, gc.IsNil) wc.AssertChange("mysql/1") wc.AssertNoChange() } func (s *MachineSuite) TestWatchPrincipalUnitsDiesOnStateClose(c *gc.C) { // This test is testing logic in watcher.unitsWatcher, which // is also used by Unit.WatchSubordinateUnits. testWatcherDiesWhenStateCloses(c, func(c *gc.C, st *state.State) waiter { m, err := st.Machine(s.machine.Id()) c.Assert(err, gc.IsNil) w := m.WatchPrincipalUnits() <-w.Changes() return w }) } func (s *MachineSuite) TestWatchUnits(c *gc.C) { // Start a watch on an empty machine; check no units reported. w := s.machine.WatchUnits() defer testing.AssertStop(c, w) wc := testing.NewStringsWatcherC(c, s.State, w) wc.AssertChange() wc.AssertNoChange() // Change machine; no change. err := s.machine.SetProvisioned("cheese", "fake_nonce", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Assign a unit (to a separate instance); change detected. mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) mysql0, err := mysql.AddUnit() c.Assert(err, gc.IsNil) machine, err := s.State.Machine(s.machine.Id()) c.Assert(err, gc.IsNil) err = mysql0.AssignToMachine(machine) c.Assert(err, gc.IsNil) wc.AssertChange("mysql/0") wc.AssertNoChange() // Change the unit; no change. err = mysql0.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Assign another unit and make the first Dying; check both changes detected. mysql1, err := mysql.AddUnit() c.Assert(err, gc.IsNil) err = mysql1.AssignToMachine(machine) c.Assert(err, gc.IsNil) err = mysql0.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange("mysql/0", "mysql/1") wc.AssertNoChange() // Add a subordinate to the Alive unit; change detected. logging := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"mysql", "logging"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) mysqlru1, err := rel.Unit(mysql1) c.Assert(err, gc.IsNil) err = mysqlru1.EnterScope(nil) c.Assert(err, gc.IsNil) logging0, err := logging.Unit("logging/0") c.Assert(err, gc.IsNil) wc.AssertChange("logging/0") wc.AssertNoChange() // Change the subordinate; no change. err = logging0.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Make the Dying unit Dead; change detected. err = mysql0.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertChange("mysql/0") wc.AssertNoChange() // Stop watcher; check Changes chan closed. testing.AssertStop(c, w) wc.AssertClosed() // Start a fresh watcher; check all units reported. w = s.machine.WatchUnits() defer testing.AssertStop(c, w) wc = testing.NewStringsWatcherC(c, s.State, w) wc.AssertChange("mysql/0", "mysql/1", "logging/0") wc.AssertNoChange() // Remove the Dead unit; no change. err = mysql0.Remove() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Destroy the subordinate; change detected. err = logging0.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange("logging/0") wc.AssertNoChange() // Unassign the principal; check subordinate departure also reported. err = mysql1.UnassignFromMachine() c.Assert(err, gc.IsNil) wc.AssertChange("mysql/1", "logging/0") wc.AssertNoChange() } func (s *MachineSuite) TestWatchUnitsDiesOnStateClose(c *gc.C) { testWatcherDiesWhenStateCloses(c, func(c *gc.C, st *state.State) waiter { m, err := st.Machine(s.machine.Id()) c.Assert(err, gc.IsNil) w := m.WatchUnits() <-w.Changes() return w }) } func (s *MachineSuite) TestAnnotatorForMachine(c *gc.C) { testAnnotator(c, func() (state.Annotator, error) { return s.State.Machine(s.machine.Id()) }) } func (s *MachineSuite) TestAnnotationRemovalForMachine(c *gc.C) { annotations := map[string]string{"mykey": "myvalue"} err := s.machine.SetAnnotations(annotations) c.Assert(err, gc.IsNil) err = s.machine.EnsureDead() c.Assert(err, gc.IsNil) err = s.machine.Remove() c.Assert(err, gc.IsNil) ann, err := s.machine.Annotations() c.Assert(err, gc.IsNil) c.Assert(ann, gc.DeepEquals, make(map[string]string)) } func (s *MachineSuite) TestConstraintsFromEnvironment(c *gc.C) { econs1 := constraints.MustParse("mem=1G") econs2 := constraints.MustParse("mem=2G") // A newly-created machine gets a copy of the environment constraints. err := s.State.SetEnvironConstraints(econs1) c.Assert(err, gc.IsNil) machine1, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) mcons1, err := machine1.Constraints() c.Assert(err, gc.IsNil) c.Assert(mcons1, gc.DeepEquals, econs1) // Change environment constraints and add a new machine. err = s.State.SetEnvironConstraints(econs2) c.Assert(err, gc.IsNil) machine2, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) mcons2, err := machine2.Constraints() c.Assert(err, gc.IsNil) c.Assert(mcons2, gc.DeepEquals, econs2) // Check the original machine has its original constraints. mcons1, err = machine1.Constraints() c.Assert(err, gc.IsNil) c.Assert(mcons1, gc.DeepEquals, econs1) } func (s *MachineSuite) TestSetConstraints(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // Constraints can be set... cons1 := constraints.MustParse("mem=1G") err = machine.SetConstraints(cons1) c.Assert(err, gc.IsNil) mcons, err := machine.Constraints() c.Assert(err, gc.IsNil) c.Assert(mcons, gc.DeepEquals, cons1) // ...until the machine is provisioned, at which point they stick. err = machine.SetProvisioned("i-mstuck", "fake_nonce", nil) c.Assert(err, gc.IsNil) cons2 := constraints.MustParse("mem=2G") err = machine.SetConstraints(cons2) c.Assert(err, gc.ErrorMatches, "cannot set constraints: machine is already provisioned") // Check the failed set had no effect. mcons, err = machine.Constraints() c.Assert(err, gc.IsNil) c.Assert(mcons, gc.DeepEquals, cons1) } func (s *MachineSuite) TestConstraintsLifecycle(c *gc.C) { cons := constraints.MustParse("mem=1G") cannotSet := `cannot set constraints: not found or not alive` testWhenDying(c, s.machine, cannotSet, cannotSet, func() error { err := s.machine.SetConstraints(cons) mcons, err1 := s.machine.Constraints() c.Assert(err1, gc.IsNil) c.Assert(&mcons, jc.Satisfies, constraints.IsEmpty) return err }) err := s.machine.Remove() c.Assert(err, gc.IsNil) err = s.machine.SetConstraints(cons) c.Assert(err, gc.ErrorMatches, cannotSet) _, err = s.machine.Constraints() c.Assert(err, gc.ErrorMatches, `constraints not found`) } func (s *MachineSuite) TestGetSetStatusWhileAlive(c *gc.C) { err := s.machine.SetStatus(params.StatusError, "", nil) c.Assert(err, gc.ErrorMatches, `cannot set status "error" without info`) err = s.machine.SetStatus(params.StatusDown, "", nil) c.Assert(err, gc.ErrorMatches, `cannot set status "down"`) err = s.machine.SetStatus(params.Status("vliegkat"), "orville", nil) c.Assert(err, gc.ErrorMatches, `cannot set invalid status "vliegkat"`) status, info, data, err := s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusPending) c.Assert(info, gc.Equals, "") c.Assert(data, gc.HasLen, 0) err = s.machine.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) status, info, data, err = s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStarted) c.Assert(info, gc.Equals, "") c.Assert(data, gc.HasLen, 0) err = s.machine.SetStatus(params.StatusError, "provisioning failed", params.StatusData{ "foo": "bar", }) c.Assert(err, gc.IsNil) status, info, data, err = s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "provisioning failed") c.Assert(data, gc.DeepEquals, params.StatusData{ "foo": "bar", }) } func (s *MachineSuite) TestSetStatusPending(c *gc.C) { err := s.machine.SetStatus(params.StatusPending, "", nil) c.Assert(err, gc.IsNil) // Cannot set status to pending once a machine is provisioned. err = s.machine.SetProvisioned("umbrella/0", "fake_nonce", nil) c.Assert(err, gc.IsNil) err = s.machine.SetStatus(params.StatusPending, "", nil) c.Assert(err, gc.ErrorMatches, `cannot set status "pending"`) } func (s *MachineSuite) TestGetSetStatusWhileNotAlive(c *gc.C) { // When Dying set/get should work. err := s.machine.Destroy() c.Assert(err, gc.IsNil) err = s.machine.SetStatus(params.StatusStopped, "", nil) c.Assert(err, gc.IsNil) status, info, data, err := s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStopped) c.Assert(info, gc.Equals, "") c.Assert(data, gc.HasLen, 0) // When Dead set should fail, but get will work. err = s.machine.EnsureDead() c.Assert(err, gc.IsNil) err = s.machine.SetStatus(params.StatusStarted, "not really", nil) c.Assert(err, gc.ErrorMatches, `cannot set status of machine "1": not found or not alive`) status, info, data, err = s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStopped) c.Assert(info, gc.Equals, "") c.Assert(data, gc.HasLen, 0) err = s.machine.Remove() c.Assert(err, gc.IsNil) err = s.machine.SetStatus(params.StatusStarted, "not really", nil) c.Assert(err, gc.ErrorMatches, `cannot set status of machine "1": not found or not alive`) _, _, _, err = s.machine.Status() c.Assert(err, gc.ErrorMatches, "status not found") } func (s *MachineSuite) TestGetSetStatusDataStandard(c *gc.C) { err := s.machine.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) _, _, _, err = s.machine.Status() c.Assert(err, gc.IsNil) // Regular status setting with data. err = s.machine.SetStatus(params.StatusError, "provisioning failed", params.StatusData{ "1st-key": "one", "2nd-key": 2, "3rd-key": true, }) c.Assert(err, gc.IsNil) status, info, data, err := s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "provisioning failed") c.Assert(data, gc.DeepEquals, params.StatusData{ "1st-key": "one", "2nd-key": 2, "3rd-key": true, }) } func (s *MachineSuite) TestGetSetStatusDataMongo(c *gc.C) { err := s.machine.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) _, _, _, err = s.machine.Status() c.Assert(err, gc.IsNil) // Status setting with MongoDB special values. err = s.machine.SetStatus(params.StatusError, "mongo", params.StatusData{ `{name: "Joe"}`: "$where", "eval": `eval(function(foo) { return foo; }, "bar")`, "mapReduce": "mapReduce", "group": "group", }) c.Assert(err, gc.IsNil) status, info, data, err := s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "mongo") c.Assert(data, gc.DeepEquals, params.StatusData{ `{name: "Joe"}`: "$where", "eval": `eval(function(foo) { return foo; }, "bar")`, "mapReduce": "mapReduce", "group": "group", }) } func (s *MachineSuite) TestGetSetStatusDataChange(c *gc.C) { err := s.machine.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) _, _, _, err = s.machine.Status() c.Assert(err, gc.IsNil) // Status setting and changing data afterwards. data := params.StatusData{ "1st-key": "one", "2nd-key": 2, "3rd-key": true, } err = s.machine.SetStatus(params.StatusError, "provisioning failed", data) c.Assert(err, gc.IsNil) data["4th-key"] = 4.0 status, info, data, err := s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "provisioning failed") c.Assert(data, gc.DeepEquals, params.StatusData{ "1st-key": "one", "2nd-key": 2, "3rd-key": true, }) // Set status data to nil, so an empty map will be returned. err = s.machine.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) status, info, data, err = s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStarted) c.Assert(info, gc.Equals, "") c.Assert(data, gc.HasLen, 0) } func (s *MachineSuite) TestSetAddresses(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"), } err = machine.SetAddresses(addresses) c.Assert(err, gc.IsNil) err = machine.Refresh() c.Assert(err, gc.IsNil) c.Assert(machine.Addresses(), gc.DeepEquals, addresses) } func (s *MachineSuite) TestSetMachineAddresses(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"), } 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) containers := []instance.ContainerType{container} err = machine.SetSupportedContainers(containers) c.Assert(err, gc.IsNil) assertSupportedContainers(c, machine, containers) return machine } // assertSupportedContainers checks the document in memory has the specified // containers and then reloads the document from the database to assert saved // values match also. func assertSupportedContainers(c *gc.C, machine *state.Machine, containers []instance.ContainerType) { supportedContainers, known := machine.SupportedContainers() c.Assert(known, jc.IsTrue) c.Assert(supportedContainers, gc.DeepEquals, containers) // Reload so we can check the saved values. err := machine.Refresh() c.Assert(err, gc.IsNil) supportedContainers, known = machine.SupportedContainers() c.Assert(known, jc.IsTrue) c.Assert(supportedContainers, gc.DeepEquals, containers) } func assertSupportedContainersUnknown(c *gc.C, machine *state.Machine) { containers, known := machine.SupportedContainers() c.Assert(known, jc.IsFalse) c.Assert(containers, gc.HasLen, 0) } func (s *MachineSuite) TestSupportedContainersInitiallyUnknown(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) assertSupportedContainersUnknown(c, machine) } func (s *MachineSuite) TestSupportsNoContainers(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine.SupportsNoContainers() c.Assert(err, gc.IsNil) assertSupportedContainers(c, machine, []instance.ContainerType{}) } func (s *MachineSuite) TestSetSupportedContainerTypeNoneIsError(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine.SetSupportedContainers([]instance.ContainerType{instance.LXC, instance.NONE}) c.Assert(err, gc.ErrorMatches, `"none" is not a valid container type`) assertSupportedContainersUnknown(c, machine) err = machine.Refresh() c.Assert(err, gc.IsNil) assertSupportedContainersUnknown(c, machine) } func (s *MachineSuite) TestSupportsNoContainersOverwritesExisting(c *gc.C) { machine := s.addMachineWithSupportedContainer(c, instance.LXC) err := machine.SupportsNoContainers() c.Assert(err, gc.IsNil) assertSupportedContainers(c, machine, []instance.ContainerType{}) } func (s *MachineSuite) TestSetSupportedContainersSingle(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine.SetSupportedContainers([]instance.ContainerType{instance.LXC}) c.Assert(err, gc.IsNil) assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXC}) } func (s *MachineSuite) TestSetSupportedContainersSame(c *gc.C) { machine := s.addMachineWithSupportedContainer(c, instance.LXC) err := machine.SetSupportedContainers([]instance.ContainerType{instance.LXC}) c.Assert(err, gc.IsNil) assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXC}) } func (s *MachineSuite) TestSetSupportedContainersNew(c *gc.C) { machine := s.addMachineWithSupportedContainer(c, instance.LXC) err := machine.SetSupportedContainers([]instance.ContainerType{instance.LXC, instance.KVM}) c.Assert(err, gc.IsNil) assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXC, instance.KVM}) } func (s *MachineSuite) TestSetSupportedContainersMultipeNew(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine.SetSupportedContainers([]instance.ContainerType{instance.LXC, instance.KVM}) c.Assert(err, gc.IsNil) assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXC, instance.KVM}) } func (s *MachineSuite) TestSetSupportedContainersMultipleExisting(c *gc.C) { machine := s.addMachineWithSupportedContainer(c, instance.LXC) err := machine.SetSupportedContainers([]instance.ContainerType{instance.LXC, instance.KVM}) c.Assert(err, gc.IsNil) assertSupportedContainers(c, machine, []instance.ContainerType{instance.LXC, instance.KVM}) } func (s *MachineSuite) TestSetSupportedContainersSetsUnknownToError(c *gc.C) { // Create a machine and add lxc and kvm containers prior to calling SetSupportedContainers machine, err := s.State.AddMachine("quantal", state.JobHostUnits) template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } container, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) supportedContainer, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.KVM) c.Assert(err, gc.IsNil) err = machine.SetSupportedContainers([]instance.ContainerType{instance.KVM}) c.Assert(err, gc.IsNil) // A supported (kvm) container will have a pending status. err = supportedContainer.Refresh() c.Assert(err, gc.IsNil) status, info, data, err := supportedContainer.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusPending) // An unsupported (lxc) container will have an error status. err = container.Refresh() c.Assert(err, gc.IsNil) status, info, data, err = container.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "unsupported container") c.Assert(data, gc.DeepEquals, params.StatusData{"type": "lxc"}) } func (s *MachineSuite) TestSupportsNoContainersSetsAllToError(c *gc.C) { // Create a machine and add all container types prior to calling SupportsNoContainers machine, err := s.State.AddMachine("quantal", state.JobHostUnits) var containers []*state.Machine template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } for _, containerType := range instance.ContainerTypes { container, err := s.State.AddMachineInsideMachine(template, machine.Id(), containerType) c.Assert(err, gc.IsNil) containers = append(containers, container) } err = machine.SupportsNoContainers() c.Assert(err, gc.IsNil) // All containers should be in error state. for _, container := range containers { err = container.Refresh() c.Assert(err, gc.IsNil) status, info, data, err := container.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "unsupported container") containerType := state.ContainerTypeFromId(container.Id()) c.Assert(data, gc.DeepEquals, params.StatusData{"type": string(containerType)}) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/assign_test.go�����������������������������������0000644�0000153�0000161�00000114174�12321735776�024677� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "fmt" "sort" "strconv" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" ) type AssignSuite struct { ConnSuite wordpress *state.Service } var _ = gc.Suite(&AssignSuite{}) var _ = gc.Suite(&assignCleanSuite{ConnSuite{}, state.AssignCleanEmpty, nil}) var _ = gc.Suite(&assignCleanSuite{ConnSuite{}, state.AssignClean, nil}) func (s *AssignSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) wordpress := s.AddTestingServiceWithNetworks( c, "wordpress", s.AddTestingCharm(c, "wordpress"), []string{"net1", "net2"}, []string{"net3", "net4"}, ) s.wordpress = wordpress } func (s *AssignSuite) addSubordinate(c *gc.C, principal *state.Unit) *state.Unit { s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"logging", "wordpress"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(principal) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) subUnit, err := s.State.Unit("logging/0") c.Assert(err, gc.IsNil) return subUnit } func (s *AssignSuite) TestUnassignUnitFromMachineWithoutBeingAssigned(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // When unassigning a machine from a unit, it is possible that // the machine has not been previously assigned, or that it // was assigned but the state changed beneath us. In either // case, the end state is the intended state, so we simply // move forward without any errors here, to avoid having to // handle the extra complexity of dealing with the concurrency // problems. err = unit.UnassignFromMachine() c.Assert(err, gc.IsNil) // Check that the unit has no machine assigned. _, err = unit.AssignedMachineId() c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not assigned to a machine`) } func (s *AssignSuite) TestAssignUnitToMachineAgainFails(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Check that assigning an already assigned unit to // a machine fails if it isn't precisely the same // machine. machineOne, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) machineTwo, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = unit.AssignToMachine(machineOne) c.Assert(err, gc.IsNil) // Assigning the unit to the same machine should return no error. err = unit.AssignToMachine(machineOne) c.Assert(err, gc.IsNil) // Assigning the unit to a different machine should fail. err = unit.AssignToMachine(machineTwo) c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to machine 1: unit is already assigned to a machine`) machineId, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(machineId, gc.Equals, "0") } func (s *AssignSuite) TestAssignedMachineIdWhenNotAlive(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = unit.AssignToMachine(machine) c.Assert(err, gc.IsNil) testWhenDying(c, unit, noErr, noErr, func() error { _, err = unit.AssignedMachineId() return err }) } func (s *AssignSuite) TestAssignedMachineIdWhenPrincipalNotAlive(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = unit.AssignToMachine(machine) c.Assert(err, gc.IsNil) subUnit := s.addSubordinate(c, unit) err = unit.Destroy() c.Assert(err, gc.IsNil) mid, err := subUnit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, machine.Id()) } func (s *AssignSuite) TestUnassignUnitFromMachineWithChangingState(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Check that unassigning while the state changes fails nicely. // Remove the unit for the tests. err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.IsNil) err = unit.UnassignFromMachine() c.Assert(err, gc.ErrorMatches, `cannot unassign unit "wordpress/0" from machine: .*`) _, err = unit.AssignedMachineId() c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not assigned to a machine`) err = s.wordpress.Destroy() c.Assert(err, gc.IsNil) err = unit.UnassignFromMachine() c.Assert(err, gc.ErrorMatches, `cannot unassign unit "wordpress/0" from machine: .*`) _, err = unit.AssignedMachineId() c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not assigned to a machine`) } func (s *AssignSuite) TestAssignSubordinatesToMachine(c *gc.C) { // Check that assigning a principal unit assigns its subordinates too. unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) subUnit := s.addSubordinate(c, unit) // None of the direct unit assign methods work on subordinates. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = subUnit.AssignToMachine(machine) c.Assert(err, gc.ErrorMatches, `cannot assign unit "logging/0" to machine 0: unit is a subordinate`) _, err = subUnit.AssignToCleanMachine() c.Assert(err, gc.ErrorMatches, `cannot assign unit "logging/0" to clean machine: unit is a subordinate`) _, err = subUnit.AssignToCleanEmptyMachine() c.Assert(err, gc.ErrorMatches, `cannot assign unit "logging/0" to clean, empty machine: unit is a subordinate`) err = subUnit.AssignToNewMachine() c.Assert(err, gc.ErrorMatches, `cannot assign unit "logging/0" to new machine: unit is a subordinate`) // Subordinates know the machine they're indirectly assigned to. err = unit.AssignToMachine(machine) c.Assert(err, gc.IsNil) id, err := subUnit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Check(id, gc.Equals, machine.Id()) // Unassigning the principal unassigns the subordinates too. err = unit.UnassignFromMachine() c.Assert(err, gc.IsNil) _, err = subUnit.AssignedMachineId() c.Assert(err, gc.ErrorMatches, `unit "logging/0" is not assigned to a machine`) } func (s *AssignSuite) TestDeployerTag(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) principal, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) subordinate := s.addSubordinate(c, principal) assertDeployer := func(u *state.Unit, d state.Entity) { err := u.Refresh() c.Assert(err, gc.IsNil) name, ok := u.DeployerTag() if d == nil { c.Assert(ok, jc.IsFalse) } else { c.Assert(ok, jc.IsTrue) c.Assert(name, gc.Equals, d.Tag()) } } assertDeployer(subordinate, principal) assertDeployer(principal, nil) err = principal.AssignToMachine(machine) c.Assert(err, gc.IsNil) assertDeployer(subordinate, principal) assertDeployer(principal, machine) err = principal.UnassignFromMachine() c.Assert(err, gc.IsNil) assertDeployer(subordinate, principal) assertDeployer(principal, nil) } func (s *AssignSuite) TestDirectAssignIgnoresConstraints(c *gc.C) { // Set up constraints. scons := constraints.MustParse("mem=2G cpu-power=400") err := s.wordpress.SetConstraints(scons) c.Assert(err, gc.IsNil) econs := constraints.MustParse("mem=4G cpu-cores=2") err = s.State.SetEnvironConstraints(econs) c.Assert(err, gc.IsNil) // Machine will take environment constraints on creation. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // Unit will take combined service/environ constraints on creation. unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Machine keeps its original constraints on direct assignment. err = unit.AssignToMachine(machine) c.Assert(err, gc.IsNil) mcons, err := machine.Constraints() c.Assert(err, gc.IsNil) c.Assert(mcons, gc.DeepEquals, econs) } func (s *AssignSuite) TestAssignBadSeries(c *gc.C) { machine, err := s.State.AddMachine("burble", state.JobHostUnits) c.Assert(err, gc.IsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToMachine(machine) c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to machine 0: series does not match`) } func (s *AssignSuite) TestAssignMachineWhenDying(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) subUnit := s.addSubordinate(c, unit) assignTest := func() error { err := unit.AssignToMachine(machine) c.Assert(unit.UnassignFromMachine(), gc.IsNil) if subUnit != nil { err := subUnit.EnsureDead() c.Assert(err, gc.IsNil) err = subUnit.Remove() c.Assert(err, gc.IsNil) subUnit = nil } return err } expect := ".*: unit is not alive" testWhenDying(c, unit, expect, expect, assignTest) expect = ".*: machine is not alive" unit, err = s.wordpress.AddUnit() c.Assert(err, gc.IsNil) testWhenDying(c, machine, expect, expect, assignTest) } func (s *AssignSuite) TestAssignMachinePrincipalsChange(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToMachine(machine) c.Assert(err, gc.IsNil) unit, err = s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToMachine(machine) c.Assert(err, gc.IsNil) subUnit := s.addSubordinate(c, unit) doc := make(map[string][]string) s.ConnSuite.machines.FindId(machine.Id()).One(&doc) principals, ok := doc["principals"] if !ok { c.Errorf(`machine document does not have a "principals" field`) } c.Assert(principals, gc.DeepEquals, []string{"wordpress/0", "wordpress/1"}) err = subUnit.EnsureDead() c.Assert(err, gc.IsNil) err = subUnit.Remove() c.Assert(err, gc.IsNil) err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.IsNil) doc = make(map[string][]string) s.ConnSuite.machines.FindId(machine.Id()).One(&doc) principals, ok = doc["principals"] if !ok { c.Errorf(`machine document does not have a "principals" field`) } c.Assert(principals, gc.DeepEquals, []string{"wordpress/0"}) } func (s *AssignSuite) assertAssignedUnit(c *gc.C, unit *state.Unit) string { // Get service networks. service, err := unit.Service() c.Assert(err, gc.IsNil) includeNetworks, excludeNetworks, err := service.Networks() c.Assert(err, gc.IsNil) // Check the machine on the unit is set. machineId, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) // Check that the principal is set on the machine. machine, err := s.State.Machine(machineId) c.Assert(err, gc.IsNil) include, exclude, err := machine.Networks() c.Assert(err, gc.IsNil) c.Assert(include, gc.DeepEquals, includeNetworks) c.Assert(exclude, gc.DeepEquals, excludeNetworks) machineUnits, err := machine.Units() c.Assert(err, gc.IsNil) c.Assert(machineUnits, gc.HasLen, 1) // Make sure it is the right unit. c.Assert(machineUnits[0].Name(), gc.Equals, unit.Name()) return machineId } func (s *AssignSuite) TestAssignUnitToNewMachine(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToNewMachine() c.Assert(err, gc.IsNil) s.assertAssignedUnit(c, unit) } func (s *AssignSuite) assertAssignUnitToNewMachineContainerConstraint(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToNewMachine() c.Assert(err, gc.IsNil) machineId := s.assertAssignedUnit(c, unit) c.Assert(state.ParentId(machineId), gc.Not(gc.Equals), "") c.Assert(state.ContainerTypeFromId(machineId), gc.Equals, instance.LXC) } func (s *AssignSuite) TestAssignUnitToNewMachineContainerConstraint(c *gc.C) { // Set up service constraints. scons := constraints.MustParse("container=lxc") err := s.wordpress.SetConstraints(scons) c.Assert(err, gc.IsNil) s.assertAssignUnitToNewMachineContainerConstraint(c) } func (s *AssignSuite) TestAssignUnitToNewMachineDefaultContainerConstraint(c *gc.C) { // Set up env constraints. econs := constraints.MustParse("container=lxc") err := s.State.SetEnvironConstraints(econs) c.Assert(err, gc.IsNil) s.assertAssignUnitToNewMachineContainerConstraint(c) } func (s *AssignSuite) TestAssignToNewMachineMakesDirty(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToNewMachine() c.Assert(err, gc.IsNil) mid, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) machine, err := s.State.Machine(mid) c.Assert(err, gc.IsNil) c.Assert(machine.Clean(), jc.IsFalse) } func (s *AssignSuite) TestAssignUnitToNewMachineSetsConstraints(c *gc.C) { // Set up constraints. scons := constraints.MustParse("mem=2G cpu-power=400") err := s.wordpress.SetConstraints(scons) c.Assert(err, gc.IsNil) econs := constraints.MustParse("mem=4G cpu-cores=2") err = s.State.SetEnvironConstraints(econs) c.Assert(err, gc.IsNil) // Unit will take combined service/environ constraints on creation. unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Change service/env constraints before assigning, to verify this. scons = constraints.MustParse("mem=6G cpu-power=800") err = s.wordpress.SetConstraints(scons) c.Assert(err, gc.IsNil) econs = constraints.MustParse("cpu-cores=4") err = s.State.SetEnvironConstraints(econs) c.Assert(err, gc.IsNil) // The new machine takes the original combined unit constraints. err = unit.AssignToNewMachine() c.Assert(err, gc.IsNil) err = unit.Refresh() c.Assert(err, gc.IsNil) mid, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) machine, err := s.State.Machine(mid) c.Assert(err, gc.IsNil) mcons, err := machine.Constraints() c.Assert(err, gc.IsNil) expect := constraints.MustParse("mem=2G cpu-cores=2 cpu-power=400") c.Assert(mcons, gc.DeepEquals, expect) } func (s *AssignSuite) TestAssignUnitToNewMachineCleanAvailable(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Add a clean machine. clean, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = unit.AssignToNewMachine() c.Assert(err, gc.IsNil) // Check the machine on the unit is set. machineId, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) // Check that the machine isn't our clean one. machine, err := s.State.Machine(machineId) c.Assert(err, gc.IsNil) c.Assert(machine.Id(), gc.Not(gc.Equals), clean.Id()) } func (s *AssignSuite) TestAssignUnitToNewMachineAlreadyAssigned(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Make the unit assigned err = unit.AssignToNewMachine() c.Assert(err, gc.IsNil) // Try to assign it again err = unit.AssignToNewMachine() c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to new machine: unit is already assigned to a machine`) } func (s *AssignSuite) TestAssignUnitToNewMachineUnitNotAlive(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) subUnit := s.addSubordinate(c, unit) // Try to assign a dying unit... err = unit.Destroy() c.Assert(err, gc.IsNil) err = unit.AssignToNewMachine() c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to new machine: unit is not alive`) // ...and a dead one. err = subUnit.EnsureDead() c.Assert(err, gc.IsNil) err = subUnit.Remove() c.Assert(err, gc.IsNil) err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = unit.AssignToNewMachine() c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to new machine: unit is not alive`) } func (s *AssignSuite) TestAssignUnitToNewMachineUnitRemoved(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = unit.Destroy() c.Assert(err, gc.IsNil) err = unit.AssignToNewMachine() c.Assert(err, gc.ErrorMatches, `cannot assign unit "wordpress/0" to new machine: unit not found`) } func (s *AssignSuite) TestAssignUnitToNewMachineBecomesDirty(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobManageEnviron) // bootstrap machine c.Assert(err, gc.IsNil) // Set up constraints to specify we want to install into a container. econs := constraints.MustParse("container=lxc") err = s.State.SetEnvironConstraints(econs) c.Assert(err, gc.IsNil) // Create some units and a clean machine. unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) anotherUnit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) makeDirty := state.TransactionHook{ Before: func() { c.Assert(unit.AssignToMachine(machine), gc.IsNil) }, } defer state.SetTransactionHooks( c, s.State, makeDirty, ).Check() err = anotherUnit.AssignToNewMachineOrContainer() c.Assert(err, gc.IsNil) mid, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, "1") mid, err = anotherUnit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, "2/lxc/0") } func (s *AssignSuite) TestAssignUnitToNewMachineBecomesHost(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobManageEnviron) // bootstrap machine c.Assert(err, gc.IsNil) // Set up constraints to specify we want to install into a container. econs := constraints.MustParse("container=lxc") err = s.State.SetEnvironConstraints(econs) c.Assert(err, gc.IsNil) // Create a unit and a clean machine. unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) addContainer := state.TransactionHook{ Before: func() { _, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) }, } defer state.SetTransactionHooks( c, s.State, addContainer, ).Check() err = unit.AssignToNewMachineOrContainer() c.Assert(err, gc.IsNil) mid, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, "2/lxc/0") } func (s *AssignSuite) TestAssignUnitBadPolicy(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Check nonsensical policy err = s.State.AssignUnit(unit, state.AssignmentPolicy("random")) c.Assert(err, gc.ErrorMatches, `.*unknown unit assignment policy: "random"`) _, err = unit.AssignedMachineId() c.Assert(err, gc.NotNil) assertMachineCount(c, s.State, 0) } func (s *AssignSuite) TestAssignUnitLocalPolicy(c *gc.C) { m, err := s.State.AddMachine("quantal", state.JobManageEnviron, state.JobHostUnits) // bootstrap machine c.Assert(err, gc.IsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) for i := 0; i < 2; i++ { err = s.State.AssignUnit(unit, state.AssignLocal) c.Assert(err, gc.IsNil) mid, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, m.Id()) assertMachineCount(c, s.State, 1) } } func (s *AssignSuite) assertAssignUnitNewPolicyNoContainer(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobHostUnits) // available machine c.Assert(err, gc.IsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = s.State.AssignUnit(unit, state.AssignNew) c.Assert(err, gc.IsNil) assertMachineCount(c, s.State, 2) id, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(state.ParentId(id), gc.Equals, "") } func (s *AssignSuite) TestAssignUnitNewPolicy(c *gc.C) { s.assertAssignUnitNewPolicyNoContainer(c) } func (s *AssignSuite) TestAssignUnitNewPolicyWithContainerConstraintIgnoresNone(c *gc.C) { scons := constraints.MustParse("container=none") err := s.wordpress.SetConstraints(scons) c.Assert(err, gc.IsNil) s.assertAssignUnitNewPolicyNoContainer(c) } func (s *AssignSuite) assertAssignUnitNewPolicyWithContainerConstraint(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = s.State.AssignUnit(unit, state.AssignNew) c.Assert(err, gc.IsNil) assertMachineCount(c, s.State, 3) id, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(id, gc.Equals, "1/lxc/0") } func (s *AssignSuite) TestAssignUnitNewPolicyWithContainerConstraint(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // Set up service constraints. scons := constraints.MustParse("container=lxc") err = s.wordpress.SetConstraints(scons) c.Assert(err, gc.IsNil) s.assertAssignUnitNewPolicyWithContainerConstraint(c) } func (s *AssignSuite) TestAssignUnitNewPolicyWithDefaultContainerConstraint(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // Set up env constraints. econs := constraints.MustParse("container=lxc") err = s.State.SetEnvironConstraints(econs) c.Assert(err, gc.IsNil) s.assertAssignUnitNewPolicyWithContainerConstraint(c) } func (s *AssignSuite) TestAssignUnitWithSubordinate(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobManageEnviron) // bootstrap machine c.Assert(err, gc.IsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Check cannot assign subordinates to machines subUnit := s.addSubordinate(c, unit) for _, policy := range []state.AssignmentPolicy{ state.AssignLocal, state.AssignNew, state.AssignClean, state.AssignCleanEmpty, } { err = s.State.AssignUnit(subUnit, policy) c.Assert(err, gc.ErrorMatches, `subordinate unit "logging/0" cannot be assigned directly to a machine`) } } func assertMachineCount(c *gc.C, st *state.State, expect int) { ms, err := st.AllMachines() c.Assert(err, gc.IsNil) c.Assert(ms, gc.HasLen, expect, gc.Commentf("%v", ms)) } // assignCleanSuite has tests for assigning units to 1. clean, and 2. clean&empty machines. type assignCleanSuite struct { ConnSuite policy state.AssignmentPolicy wordpress *state.Service } func (s *assignCleanSuite) SetUpTest(c *gc.C) { c.Logf("assignment policy for this test: %q", s.policy) s.ConnSuite.SetUpTest(c) wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) s.wordpress = wordpress } func (s *assignCleanSuite) errorMessage(msg string) string { context := "clean" if s.policy == state.AssignCleanEmpty { context += ", empty" } return fmt.Sprintf(msg, context) } func (s *assignCleanSuite) assignUnit(unit *state.Unit) (*state.Machine, error) { if s.policy == state.AssignCleanEmpty { return unit.AssignToCleanEmptyMachine() } return unit.AssignToCleanMachine() } func (s *assignCleanSuite) assertMachineEmpty(c *gc.C, machine *state.Machine) { containers, err := machine.Containers() c.Assert(err, gc.IsNil) c.Assert(len(containers), gc.Equals, 0) } func (s *assignCleanSuite) assertMachineNotEmpty(c *gc.C, machine *state.Machine) { containers, err := machine.Containers() c.Assert(err, gc.IsNil) c.Assert(len(containers), gc.Not(gc.Equals), 0) } // setupMachines creates a combination of machines with which to test. func (s *assignCleanSuite) setupMachines(c *gc.C) (hostMachine *state.Machine, container *state.Machine, cleanEmptyMachine *state.Machine) { _, err := s.State.AddMachine("quantal", state.JobManageEnviron) // bootstrap machine c.Assert(err, gc.IsNil) // Add some units to another service and allocate them to machines service1 := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) units := make([]*state.Unit, 3) for i := range units { u, err := service1.AddUnit() c.Assert(err, gc.IsNil) m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = u.AssignToMachine(m) c.Assert(err, gc.IsNil) units[i] = u } // Create a new, clean machine but add containers so it is not empty. hostMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) container, err = s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, hostMachine.Id(), instance.LXC) c.Assert(hostMachine.Clean(), jc.IsTrue) s.assertMachineNotEmpty(c, hostMachine) // Create a new, clean, empty machine. cleanEmptyMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(cleanEmptyMachine.Clean(), jc.IsTrue) s.assertMachineEmpty(c, cleanEmptyMachine) return hostMachine, container, cleanEmptyMachine } func (s *assignCleanSuite) assertAssignUnit(c *gc.C, expectedMachine *state.Machine) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) reusedMachine, err := s.assignUnit(unit) c.Assert(err, gc.IsNil) c.Assert(reusedMachine.Id(), gc.Equals, expectedMachine.Id()) c.Assert(reusedMachine.Clean(), jc.IsFalse) } func (s *assignCleanSuite) TestAssignUnit(c *gc.C) { hostMachine, container, cleanEmptyMachine := s.setupMachines(c) // Check that AssignToClean(Empty)Machine finds a newly created, clean (maybe empty) machine. if s.policy == state.AssignCleanEmpty { // The first clean, empty machine is the container. s.assertAssignUnit(c, container) // The next deployment will use the remaining clean, empty machine. s.assertAssignUnit(c, cleanEmptyMachine) } else { s.assertAssignUnit(c, hostMachine) } } func (s *assignCleanSuite) TestAssignUnitTwiceFails(c *gc.C) { s.setupMachines(c) unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Assign the first time. _, err = s.assignUnit(unit) c.Assert(err, gc.IsNil) // Check that it fails when called again, even when there's an available machine m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) _, err = s.assignUnit(unit) c.Assert(err, gc.ErrorMatches, s.errorMessage(`cannot assign unit "wordpress/0" to %s machine: unit is already assigned to a machine`)) c.Assert(m.EnsureDead(), gc.IsNil) c.Assert(m.Remove(), gc.IsNil) } const eligibleMachinesInUse = "all eligible machines in use" func (s *assignCleanSuite) TestAssignToMachineNoneAvailable(c *gc.C) { // Try to assign a unit to a clean (maybe empty) machine and check that we can't. unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) m, err := s.assignUnit(unit) c.Assert(m, gc.IsNil) c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) // Add a state management machine which can host units and check it is not chosen. // Note that this must the first machine added, as AddMachine can only // be used to add state-manager machines for the bootstrap machine. m, err = s.State.AddMachine("quantal", state.JobManageEnviron, state.JobHostUnits) c.Assert(err, gc.IsNil) m, err = s.assignUnit(unit) c.Assert(m, gc.IsNil) c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) // Add a dying machine and check that it is not chosen. m, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = m.Destroy() c.Assert(err, gc.IsNil) m, err = s.assignUnit(unit) c.Assert(m, gc.IsNil) c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) // Add two environ manager machines and check they are not chosen. err = s.State.EnsureAvailability(3, constraints.Value{}, "quantal") c.Assert(err, gc.IsNil) m, err = s.assignUnit(unit) c.Assert(m, gc.IsNil) c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) // Add a machine with the wrong series and check it is not chosen. m, err = s.State.AddMachine("anotherseries", state.JobHostUnits) c.Assert(err, gc.IsNil) m, err = s.assignUnit(unit) c.Assert(m, gc.IsNil) c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) } var assignUsingConstraintsTests = []struct { unitConstraints string hardwareCharacteristics string assignOk bool }{ { unitConstraints: "", hardwareCharacteristics: "", assignOk: true, }, { unitConstraints: "arch=amd64", hardwareCharacteristics: "none", assignOk: false, }, { unitConstraints: "arch=amd64", hardwareCharacteristics: "cpu-cores=1", assignOk: false, }, { unitConstraints: "arch=amd64", hardwareCharacteristics: "arch=amd64", assignOk: true, }, { unitConstraints: "arch=amd64", hardwareCharacteristics: "arch=i386", assignOk: false, }, { unitConstraints: "mem=4G", hardwareCharacteristics: "none", assignOk: false, }, { unitConstraints: "mem=4G", hardwareCharacteristics: "cpu-cores=1", assignOk: false, }, { unitConstraints: "mem=4G", hardwareCharacteristics: "mem=4G", assignOk: true, }, { unitConstraints: "mem=4G", hardwareCharacteristics: "mem=2G", assignOk: false, }, { unitConstraints: "cpu-cores=2", hardwareCharacteristics: "cpu-cores=2", assignOk: true, }, { unitConstraints: "cpu-cores=2", hardwareCharacteristics: "cpu-cores=1", assignOk: false, }, { unitConstraints: "cpu-cores=2", hardwareCharacteristics: "mem=4G", assignOk: false, }, { unitConstraints: "cpu-power=50", hardwareCharacteristics: "cpu-power=50", assignOk: true, }, { unitConstraints: "cpu-power=100", hardwareCharacteristics: "cpu-power=50", assignOk: false, }, { unitConstraints: "cpu-power=50", hardwareCharacteristics: "mem=4G", assignOk: false, }, { unitConstraints: "root-disk=8192", hardwareCharacteristics: "cpu-power=50", assignOk: false, }, { unitConstraints: "root-disk=8192", hardwareCharacteristics: "root-disk=4096", assignOk: false, }, { unitConstraints: "root-disk=8192", hardwareCharacteristics: "root-disk=8192", assignOk: true, }, { unitConstraints: "arch=amd64 mem=4G cpu-cores=2 root-disk=8192", hardwareCharacteristics: "arch=amd64 mem=8G cpu-cores=2 root-disk=8192 cpu-power=50", assignOk: true, }, { unitConstraints: "arch=amd64 mem=4G cpu-cores=2 root-disk=8192", hardwareCharacteristics: "arch=amd64 mem=8G cpu-cores=1 root-disk=4096 cpu-power=50", assignOk: false, }, } func (s *assignCleanSuite) TestAssignUsingConstraintsToMachine(c *gc.C) { for i, t := range assignUsingConstraintsTests { c.Logf("test %d", i) cons := constraints.MustParse(t.unitConstraints) err := s.State.SetEnvironConstraints(cons) c.Assert(err, gc.IsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) if t.hardwareCharacteristics != "none" { hc := instance.MustParseHardware(t.hardwareCharacteristics) err = m.SetProvisioned("inst-id", "fake_nonce", &hc) c.Assert(err, gc.IsNil) } um, err := s.assignUnit(unit) if t.assignOk { c.Assert(err, gc.IsNil) c.Assert(um.Id(), gc.Equals, m.Id()) } else { c.Assert(um, gc.IsNil) c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse) // Destroy the machine so it can't be used for the next test. err = m.Destroy() c.Assert(err, gc.IsNil) } } } func (s *assignCleanSuite) TestAssignUnitWithRemovedService(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobManageEnviron) // bootstrap machine c.Assert(err, gc.IsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Fail if service is removed. removeAllUnits(c, s.wordpress) err = s.wordpress.Destroy() c.Assert(err, gc.IsNil) _, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) _, err = s.assignUnit(unit) c.Assert(err, gc.ErrorMatches, s.errorMessage(`cannot assign unit "wordpress/0" to %s machine.*: unit not found`)) } func (s *assignCleanSuite) TestAssignUnitToMachineWithRemovedUnit(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobManageEnviron) // bootstrap machine c.Assert(err, gc.IsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) // Fail if unit is removed. err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.IsNil) _, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) _, err = s.assignUnit(unit) c.Assert(err, gc.ErrorMatches, s.errorMessage(`cannot assign unit "wordpress/0" to %s machine.*: unit not found`)) } func (s *assignCleanSuite) TestAssignUnitToMachineWorksWithMachine0(c *gc.C) { m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, "0") unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) assignedTo, err := s.assignUnit(unit) c.Assert(err, gc.IsNil) c.Assert(assignedTo.Id(), gc.Equals, "0") } func (s *assignCleanSuite) TestAssignUnitPolicy(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobManageEnviron) // bootstrap machine c.Assert(err, gc.IsNil) // Check unassigned placements with no clean and/or empty machines. for i := 0; i < 10; i++ { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = s.State.AssignUnit(unit, s.policy) c.Assert(err, gc.IsNil) mid, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, strconv.Itoa(1+i)) assertMachineCount(c, s.State, i+2) // Sanity check that the machine knows about its assigned unit and was // created with the appropriate series. m, err := s.State.Machine(mid) c.Assert(err, gc.IsNil) units, err := m.Units() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 1) c.Assert(units[0].Name(), gc.Equals, unit.Name()) c.Assert(m.Series(), gc.Equals, "quantal") } // Remove units from alternate machines. These machines will still be // considered as dirty so will continue to be ignored by the policy. for i := 1; i < 11; i += 2 { mid := strconv.Itoa(i) m, err := s.State.Machine(mid) c.Assert(err, gc.IsNil) units, err := m.Units() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 1) unit := units[0] err = unit.UnassignFromMachine() c.Assert(err, gc.IsNil) err = unit.Destroy() c.Assert(err, gc.IsNil) } var expectedMachines []string // Create a new, clean machine but add containers so it is not empty. hostMachine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, hostMachine.Id(), instance.LXC) c.Assert(hostMachine.Clean(), jc.IsTrue) s.assertMachineNotEmpty(c, hostMachine) if s.policy == state.AssignClean { expectedMachines = append(expectedMachines, hostMachine.Id()) } expectedMachines = append(expectedMachines, container.Id()) // Add some more clean machines for i := 0; i < 4; i++ { m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) expectedMachines = append(expectedMachines, m.Id()) } // Assign units to all the expectedMachines machines. var got []string for _ = range expectedMachines { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = s.State.AssignUnit(unit, s.policy) c.Assert(err, gc.IsNil) mid, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) got = append(got, mid) } sort.Strings(expectedMachines) sort.Strings(got) c.Assert(got, gc.DeepEquals, expectedMachines) } func (s *assignCleanSuite) TestAssignUnitPolicyWithContainers(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobManageEnviron) // bootstrap machine c.Assert(err, gc.IsNil) // Create a machine and add a new container. hostMachine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, hostMachine.Id(), instance.LXC) err = hostMachine.Refresh() c.Assert(err, gc.IsNil) c.Assert(hostMachine.Clean(), jc.IsTrue) s.assertMachineNotEmpty(c, hostMachine) // Set up constraints to specify we want to install into a container. econs := constraints.MustParse("container=lxc") err = s.State.SetEnvironConstraints(econs) c.Assert(err, gc.IsNil) // Check the first placement goes into the newly created, clean container above. unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = s.State.AssignUnit(unit, s.policy) c.Assert(err, gc.IsNil) mid, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, container.Id()) assertContainerPlacement := func(expectedNumUnits int) { unit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = s.State.AssignUnit(unit, s.policy) c.Assert(err, gc.IsNil) mid, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, fmt.Sprintf("%d/lxc/0", expectedNumUnits+1)) assertMachineCount(c, s.State, 2*expectedNumUnits+3) // Sanity check that the machine knows about its assigned unit and was // created with the appropriate series. m, err := s.State.Machine(mid) c.Assert(err, gc.IsNil) units, err := m.Units() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 1) c.Assert(units[0].Name(), gc.Equals, unit.Name()) c.Assert(m.Series(), gc.Equals, "quantal") } // Check unassigned placements with no clean and/or empty machines cause a new container to be created. assertContainerPlacement(1) assertContainerPlacement(2) // Create a new, clean instance and check that the next container creation uses it. hostMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) unit, err = s.wordpress.AddUnit() c.Assert(err, gc.IsNil) err = s.State.AssignUnit(unit, s.policy) c.Assert(err, gc.IsNil) mid, err = unit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, hostMachine.Id()+"/lxc/0") } func (s *assignCleanSuite) TestAssignUnitPolicyConcurrently(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobManageEnviron) // bootstrap machine c.Assert(err, gc.IsNil) us := make([]*state.Unit, 50) for i := range us { us[i], err = s.wordpress.AddUnit() c.Assert(err, gc.IsNil) } type result struct { u *state.Unit err error } done := make(chan result) for i, u := range us { i, u := i, u go func() { // Start the AssignUnit at different times // to increase the likeliness of a race. time.Sleep(time.Duration(i) * time.Millisecond / 2) err := s.State.AssignUnit(u, s.policy) done <- result{u, err} }() } assignments := make(map[string][]*state.Unit) for _ = range us { r := <-done if !c.Check(r.err, gc.IsNil) { continue } id, err := r.u.AssignedMachineId() c.Assert(err, gc.IsNil) assignments[id] = append(assignments[id], r.u) } for id, us := range assignments { if len(us) != 1 { c.Errorf("machine %s expected one unit, got %q", id, us) } } c.Assert(assignments, gc.HasLen, len(us)) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/export_test.go�����������������������������������0000644�0000153�0000161�00000020164�12321735642�024717� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "io/ioutil" "net/url" "path/filepath" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/testing" ) // transactionHook holds Before and After func()s that will be called // respectively before and after a particular state transaction is executed. type TransactionHook transactionHook // TransactionChecker values are returned from the various Set*Hooks calls, // and should be run after the code under test has been executed to check // that the expected number of transactions were run. type TransactionChecker func() func (c TransactionChecker) Check() { c() } // SetTransactionHooks queues up hooks to be applied to the next transactions, // and returns a function that asserts all hooks have been run (and removes any // that have not). Each hook function can freely execute its own transactions // without causing other hooks to be triggered. // It returns a function that asserts that all hooks have been run, and removes // any that have not. It is an error to set transaction hooks when any are // already queued; and setting transaction hooks renders the *State goroutine- // unsafe. func SetTransactionHooks(c *gc.C, st *State, transactionHooks ...TransactionHook) TransactionChecker { converted := make([]transactionHook, len(transactionHooks)) for i, hook := range transactionHooks { converted[i] = transactionHook(hook) c.Logf("%d: %#v", i, converted[i]) } original := <-st.transactionHooks st.transactionHooks <- converted c.Assert(original, gc.HasLen, 0) return func() { remaining := <-st.transactionHooks st.transactionHooks <- nil c.Assert(remaining, gc.HasLen, 0) } } // SetBeforeHooks uses SetTransactionHooks to queue N functions to be run // immediately before the next N transactions. The first function is executed // before the first transaction, the second function before the second // transaction and so on. Nil values are accepted, and useful, in that they can // be used to ensure that a transaction is run at the expected time, without // having to make any changes or assert any state. func SetBeforeHooks(c *gc.C, st *State, fs ...func()) TransactionChecker { transactionHooks := make([]TransactionHook, len(fs)) for i, f := range fs { transactionHooks[i] = TransactionHook{Before: f} } return SetTransactionHooks(c, st, transactionHooks...) } // SetAfterHooks uses SetTransactionHooks to queue N functions to be run // immediately after the next N transactions. The first function is executed // after the first transaction, the second function after the second // transaction and so on. func SetAfterHooks(c *gc.C, st *State, fs ...func()) TransactionChecker { transactionHooks := make([]TransactionHook, len(fs)) for i, f := range fs { transactionHooks[i] = TransactionHook{After: f} } return SetTransactionHooks(c, st, transactionHooks...) } // SetRetryHooks uses SetTransactionHooks to inject a block function designed // to disrupt a transaction built against recent state, and a check function // designed to verify that the replacement transaction against the new state // has been applied as expected. func SetRetryHooks(c *gc.C, st *State, block, check func()) TransactionChecker { return SetTransactionHooks(c, st, TransactionHook{ Before: block, }, TransactionHook{ After: check, }) } // SetPolicy updates the State's policy field to the // given Policy, and returns the old value. func SetPolicy(st *State, p Policy) Policy { old := st.policy st.policy = p return old } // TestingInitialize initializes the state and returns it. If state was not // already initialized, and cfg is nil, the minimal default environment // configuration will be used. func TestingInitialize(c *gc.C, cfg *config.Config, policy Policy) *State { if cfg == nil { cfg = testing.EnvironConfig(c) } st, err := Initialize(TestingStateInfo(), cfg, TestingDialOpts(), policy) c.Assert(err, gc.IsNil) return st } type ( CharmDoc charmDoc MachineDoc machineDoc RelationDoc relationDoc ServiceDoc serviceDoc UnitDoc unitDoc ) func (doc *MachineDoc) String() string { m := &Machine{doc: machineDoc(*doc)} return m.String() } func ServiceSettingsRefCount(st *State, serviceName string, curl *charm.URL) (int, error) { key := serviceSettingsKey(serviceName, curl) var doc settingsRefsDoc if err := st.settingsrefs.FindId(key).One(&doc); err == nil { return doc.RefCount, nil } return 0, mgo.ErrNotFound } func AddTestingCharm(c *gc.C, st *State, name string) *Charm { return addCharm(c, st, "quantal", testing.Charms.Dir(name)) } func AddTestingService(c *gc.C, st *State, name string, ch *Charm) *Service { return AddTestingServiceWithNetworks(c, st, name, ch, nil, nil) } func AddTestingServiceWithNetworks(c *gc.C, st *State, name string, ch *Charm, includeNetworks, excludeNetworks []string) *Service { service, err := st.AddService(name, "user-admin", ch, includeNetworks, excludeNetworks) c.Assert(err, gc.IsNil) return service } func AddCustomCharm(c *gc.C, st *State, name, filename, content, series string, revision int) *Charm { path := testing.Charms.ClonedDirPath(c.MkDir(), name) if filename != "" { config := filepath.Join(path, filename) err := ioutil.WriteFile(config, []byte(content), 0644) c.Assert(err, gc.IsNil) } ch, err := charm.ReadDir(path) c.Assert(err, gc.IsNil) if revision != -1 { ch.SetRevision(revision) } return addCharm(c, st, series, ch) } func addCharm(c *gc.C, st *State, series string, ch charm.Charm) *Charm { ident := fmt.Sprintf("%s-%s-%d", series, ch.Meta().Name, ch.Revision()) curl := charm.MustParseURL("local:" + series + "/" + ident) bundleURL, err := url.Parse("http://bundles.testing.invalid/" + ident) c.Assert(err, gc.IsNil) sch, err := st.AddCharm(ch, curl, bundleURL, ident+"-sha256") c.Assert(err, gc.IsNil) return sch } var MachineIdLessThan = machineIdLessThan var JobNames = jobNames // SCHEMACHANGE // This method is used to reset a deprecated machine attribute. func SetMachineInstanceId(m *Machine, instanceId string) { m.doc.InstanceId = instance.Id(instanceId) } // SCHEMACHANGE // ClearInstanceDocId sets instanceid on instanceData for machine to "". func ClearInstanceDocId(c *gc.C, m *Machine) { ops := []txn.Op{ { C: m.st.instanceData.Name, Id: m.doc.Id, Assert: txn.DocExists, Update: bson.D{{"$set", bson.D{{"instanceid", ""}}}}, }, } err := m.st.runTransaction(ops) c.Assert(err, gc.IsNil) } // SCHEMACHANGE // This method is used to reset the ownertag attribute func SetServiceOwnerTag(s *Service, ownerTag string) { s.doc.OwnerTag = ownerTag } // SCHEMACHANGE // Get the owner directly func GetServiceOwnerTag(s *Service) string { return s.doc.OwnerTag } func SetPasswordHash(e Authenticator, passwordHash string) error { type hasSetPasswordHash interface { setPasswordHash(string) error } return e.(hasSetPasswordHash).setPasswordHash(passwordHash) } // Return the underlying PasswordHash stored in the database. Used by the test // suite to check that the PasswordHash gets properly updated to new values // when compatibility mode is detected. func GetPasswordHash(e Authenticator) string { type hasGetPasswordHash interface { getPasswordHash() string } return e.(hasGetPasswordHash).getPasswordHash() } func init() { logSize = logSizeTests } // MinUnitsRevno returns the Revno of the minUnits document // associated with the given service name. func MinUnitsRevno(st *State, serviceName string) (int, error) { var doc minUnitsDoc if err := st.minUnits.FindId(serviceName).One(&doc); err != nil { return 0, err } return doc.Revno, nil } func ParseTag(st *State, tag string) (string, string, error) { return st.parseTag(tag) } // Return the PasswordSalt that goes along with the PasswordHash func GetUserPasswordSaltAndHash(u *User) (string, string) { return u.doc.PasswordSalt, u.doc.PasswordHash } var NewAddress = newAddress func CheckUserExists(st *State, name string) (bool, error) { return st.checkUserExists(name) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/bench_test.go������������������������������������0000644�0000153�0000161�00000002224�12321735642�024452� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" ) type BenchmarkSuite struct { } var _ = gc.Suite(&BenchmarkSuite{}) func (*BenchmarkSuite) BenchmarkAddUnit(c *gc.C) { // TODO(rog) embed ConnSuite in BenchmarkSuite when // gocheck calls appropriate fixture methods for benchmark // functions. var s ConnSuite s.SetUpSuite(c) defer s.TearDownSuite(c) s.SetUpTest(c) defer s.TearDownTest(c) charm := s.AddTestingCharm(c, "wordpress") svc := s.AddTestingService(c, "wordpress", charm) c.ResetTimer() for i := 0; i < c.N; i++ { _, err := svc.AddUnit() c.Assert(err, gc.IsNil) } } func (*BenchmarkSuite) BenchmarkAddAndAssignUnit(c *gc.C) { var s ConnSuite s.SetUpSuite(c) defer s.TearDownSuite(c) s.SetUpTest(c) defer s.TearDownTest(c) charm := s.AddTestingCharm(c, "wordpress") svc := s.AddTestingService(c, "wordpress", charm) c.ResetTimer() for i := 0; i < c.N; i++ { unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) err = s.State.AssignUnit(unit, state.AssignClean) c.Assert(err, gc.IsNil) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/address.go���������������������������������������0000644�0000153�0000161�00000014577�12321735642�023777� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/instance" ) // stateServerAddresses returns the list of internal addresses of the state // server machines. func (st *State) stateServerAddresses() ([]string, error) { type addressMachine struct { Addresses []address } var allAddresses []addressMachine // TODO(rog) 2013/10/14 index machines on jobs. err := st.machines.Find(bson.D{{"jobs", JobManageEnviron}}).All(&allAddresses) if err != nil { return nil, err } if len(allAddresses) == 0 { return nil, fmt.Errorf("no state server machines found") } apiAddrs := make([]string, 0, len(allAddresses)) for _, addrs := range allAddresses { instAddrs := addressesToInstanceAddresses(addrs.Addresses) addr := instance.SelectInternalAddress(instAddrs, false) if addr != "" { apiAddrs = append(apiAddrs, addr) } } if len(apiAddrs) == 0 { return nil, fmt.Errorf("no state server machines with addresses found") } return apiAddrs, nil } func appendPort(addrs []string, port int) []string { newAddrs := make([]string, len(addrs)) for i, addr := range addrs { newAddrs[i] = fmt.Sprintf("%s:%d", addr, port) } return newAddrs } // Addresses returns the list of cloud-internal addresses that // can be used to connect to the state. func (st *State) Addresses() ([]string, error) { addrs, err := st.stateServerAddresses() if err != nil { return nil, err } config, err := st.EnvironConfig() if err != nil { return nil, err } return appendPort(addrs, config.StatePort()), nil } // APIAddressesFromMachines returns the list of cloud-internal addresses that // can be used to connect to the state API server. // This method will be deprecated when API addresses are // stored independently in their own document. func (st *State) APIAddressesFromMachines() ([]string, error) { addrs, err := st.stateServerAddresses() if err != nil { return nil, err } config, err := st.EnvironConfig() if err != nil { return nil, err } return appendPort(addrs, config.APIPort()), nil } const apiHostPortsKey = "apiHostPorts" type apiHostPortsDoc struct { APIHostPorts [][]hostPort } // SetAPIHostPorts sets the addresses of the API server // instances. Each server is represented by one element // in the top level slice. func (st *State) SetAPIHostPorts(hps [][]instance.HostPort) error { doc := apiHostPortsDoc{ APIHostPorts: instanceHostPortsToHostPorts(hps), } // We need to insert the document if it does not already // exist to make this method work even on old environments // where the document was not created by Initialize. ops := []txn.Op{{ C: st.stateServers.Name, Id: apiHostPortsKey, Update: bson.D{{"$set", bson.D{ {"apihostports", doc.APIHostPorts}, }}}, }} if err := st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set API addresses: %v", err) } return nil } // APIHostPorts returns the API addresses as set by SetAPIHostPorts. func (st *State) APIHostPorts() ([][]instance.HostPort, error) { var doc apiHostPortsDoc err := st.stateServers.Find(bson.D{{"_id", apiHostPortsKey}}).One(&doc) if err != nil { return nil, err } return hostPortsToInstanceHostPorts(doc.APIHostPorts), nil } type DeployerConnectionValues struct { StateAddresses []string APIAddresses []string } // DeployerConnectionInfo returns the address information necessary for the deployer. // The function does the expensive operations (getting stuff from mongo) just once. func (st *State) DeployerConnectionInfo() (*DeployerConnectionValues, error) { addrs, err := st.stateServerAddresses() if err != nil { return nil, err } config, err := st.EnvironConfig() if err != nil { return nil, err } return &DeployerConnectionValues{ StateAddresses: appendPort(addrs, config.StatePort()), APIAddresses: appendPort(addrs, config.APIPort()), }, nil } // address represents the location of a machine, including metadata about what // kind of location the address describes. type address struct { Value string AddressType instance.AddressType NetworkName string `bson:",omitempty"` NetworkScope instance.NetworkScope `bson:",omitempty"` } type hostPort struct { Value string AddressType instance.AddressType NetworkName string `bson:",omitempty"` NetworkScope instance.NetworkScope `bson:",omitempty"` Port int } func newAddress(addr instance.Address) address { return address{ Value: addr.Value, AddressType: addr.Type, NetworkName: addr.NetworkName, NetworkScope: addr.NetworkScope, } } func (addr *address) InstanceAddress() instance.Address { return instance.Address{ Value: addr.Value, Type: addr.AddressType, NetworkName: addr.NetworkName, NetworkScope: addr.NetworkScope, } } func newHostPort(hp instance.HostPort) hostPort { return hostPort{ Value: hp.Value, AddressType: hp.Type, NetworkName: hp.NetworkName, NetworkScope: hp.NetworkScope, Port: hp.Port, } } func (hp *hostPort) InstanceHostPort() instance.HostPort { return instance.HostPort{ Address: instance.Address{ Value: hp.Value, Type: hp.AddressType, NetworkName: hp.NetworkName, NetworkScope: hp.NetworkScope, }, Port: hp.Port, } } func addressesToInstanceAddresses(addrs []address) []instance.Address { instanceAddrs := make([]instance.Address, len(addrs)) for i, addr := range addrs { instanceAddrs[i] = addr.InstanceAddress() } return instanceAddrs } func instanceAddressesToAddresses(instanceAddrs []instance.Address) []address { addrs := make([]address, len(instanceAddrs)) for i, addr := range instanceAddrs { addrs[i] = newAddress(addr) } return addrs } func hostPortsToInstanceHostPorts(insts [][]hostPort) [][]instance.HostPort { instanceHostPorts := make([][]instance.HostPort, len(insts)) for i, hps := range insts { instanceHostPorts[i] = make([]instance.HostPort, len(hps)) for j, hp := range hps { instanceHostPorts[i][j] = hp.InstanceHostPort() } } return instanceHostPorts } func instanceHostPortsToHostPorts(instanceHostPorts [][]instance.HostPort) [][]hostPort { hps := make([][]hostPort, len(instanceHostPorts)) for i, instanceHps := range instanceHostPorts { hps[i] = make([]hostPort, len(instanceHps)) for j, hp := range instanceHps { hps[i][j] = newHostPort(hp) } } return hps } ���������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/constraints.go�����������������������������������0000644�0000153�0000161�00000004315�12321735776�024716� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" ) // constraintsDoc is the mongodb representation of a constraints.Value. type constraintsDoc struct { Arch *string CpuCores *uint64 CpuPower *uint64 Mem *uint64 RootDisk *uint64 Container *instance.ContainerType Tags *[]string ",omitempty" } func (doc constraintsDoc) value() constraints.Value { return constraints.Value{ Arch: doc.Arch, CpuCores: doc.CpuCores, CpuPower: doc.CpuPower, Mem: doc.Mem, RootDisk: doc.RootDisk, Container: doc.Container, Tags: doc.Tags, } } func newConstraintsDoc(cons constraints.Value) constraintsDoc { return constraintsDoc{ Arch: cons.Arch, CpuCores: cons.CpuCores, CpuPower: cons.CpuPower, Mem: cons.Mem, RootDisk: cons.RootDisk, Container: cons.Container, Tags: cons.Tags, } } func createConstraintsOp(st *State, id string, cons constraints.Value) txn.Op { return txn.Op{ C: st.constraints.Name, Id: id, Assert: txn.DocMissing, Insert: newConstraintsDoc(cons), } } func setConstraintsOp(st *State, id string, cons constraints.Value) txn.Op { return txn.Op{ C: st.constraints.Name, Id: id, Assert: txn.DocExists, Update: bson.D{{"$set", newConstraintsDoc(cons)}}, } } func removeConstraintsOp(st *State, id string) txn.Op { return txn.Op{ C: st.constraints.Name, Id: id, Remove: true, } } func readConstraints(st *State, id string) (constraints.Value, error) { doc := constraintsDoc{} if err := st.constraints.FindId(id).One(&doc); err == mgo.ErrNotFound { return constraints.Value{}, errors.NotFoundf("constraints") } else if err != nil { return constraints.Value{}, err } return doc.value(), nil } func writeConstraints(st *State, id string, cons constraints.Value) error { ops := []txn.Op{setConstraintsOp(st, id, cons)} if err := st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set constraints: %v", err) } return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/relationunit_test.go�����������������������������0000644�0000153�0000161�00000106056�12321735776�026130� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "fmt" "sort" "strconv" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" ) type RUs []*state.RelationUnit type RelationUnitSuite struct { ConnSuite } var _ = gc.Suite(&RelationUnitSuite{}) func assertInScope(c *gc.C, ru *state.RelationUnit) { ok, err := ru.InScope() c.Assert(err, gc.IsNil) c.Assert(ok, jc.IsTrue) } func assertNotInScope(c *gc.C, ru *state.RelationUnit) { ok, err := ru.InScope() c.Assert(err, gc.IsNil) c.Assert(ok, jc.IsFalse) } func (s *RelationUnitSuite) TestReadSettingsErrors(c *gc.C) { riak := s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak")) u0, err := riak.AddUnit() c.Assert(err, gc.IsNil) riakEP, err := riak.Endpoint("ring") c.Assert(err, gc.IsNil) rel, err := s.State.EndpointsRelation(riakEP) c.Assert(err, gc.IsNil) ru0, err := rel.Unit(u0) c.Assert(err, gc.IsNil) _, err = ru0.ReadSettings("nonsense") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "nonsense" in relation "riak:ring": "nonsense" is not a valid unit name`) _, err = ru0.ReadSettings("unknown/0") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "unknown/0" in relation "riak:ring": service "unknown" is not a member of "riak:ring"`) _, err = ru0.ReadSettings("riak/pressure") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/pressure" in relation "riak:ring": "riak/pressure" is not a valid unit name`) _, err = ru0.ReadSettings("riak/1") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/1" in relation "riak:ring": settings not found`) } func (s *RelationUnitSuite) TestPeerSettings(c *gc.C) { pr := NewPeerRelation(c, s.State) rus := RUs{pr.ru0, pr.ru1} // Check missing settings cannot be read by any RU. for _, ru := range rus { _, err := ru.ReadSettings("riak/0") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": settings not found`) } // Add settings for one RU. assertNotInScope(c, pr.ru0) err := pr.ru0.EnterScope(map[string]interface{}{"gene": "kelly"}) c.Assert(err, gc.IsNil) node, err := pr.ru0.Settings() c.Assert(err, gc.IsNil) node.Set("meme", "socially-awkward-penguin") _, err = node.Write() c.Assert(err, gc.IsNil) normal := map[string]interface{}{ "gene": "kelly", "meme": "socially-awkward-penguin", } // Check settings can be read by every RU. assertSettings := func(u *state.Unit, expect map[string]interface{}) { for _, ru := range rus { m, err := ru.ReadSettings(u.Name()) c.Assert(err, gc.IsNil) c.Assert(m, gc.DeepEquals, expect) } } assertSettings(pr.u0, normal) assertInScope(c, pr.ru0) // Check that EnterScope when scope already entered does not touch // settings at all. changed := map[string]interface{}{"foo": "bar"} err = pr.ru0.EnterScope(changed) c.Assert(err, gc.IsNil) assertSettings(pr.u0, normal) assertInScope(c, pr.ru0) // Leave scope, check settings are still as accessible as before. err = pr.ru0.LeaveScope() c.Assert(err, gc.IsNil) assertSettings(pr.u0, normal) assertNotInScope(c, pr.ru0) // Re-enter scope wih changed settings, and check they completely overwrite // the old ones. err = pr.ru0.EnterScope(changed) c.Assert(err, gc.IsNil) assertSettings(pr.u0, changed) assertInScope(c, pr.ru0) // Leave and re-enter with nil nettings, and check they overwrite to become // an empty map. err = pr.ru0.LeaveScope() c.Assert(err, gc.IsNil) assertNotInScope(c, pr.ru0) err = pr.ru0.EnterScope(nil) c.Assert(err, gc.IsNil) assertSettings(pr.u0, map[string]interface{}{}) assertInScope(c, pr.ru0) // Check that entering scope for the first time with nil settings works correctly. assertNotInScope(c, pr.ru1) err = pr.ru1.EnterScope(nil) c.Assert(err, gc.IsNil) assertSettings(pr.u1, map[string]interface{}{}) assertInScope(c, pr.ru1) } func (s *RelationUnitSuite) TestProReqSettings(c *gc.C) { prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal) rus := RUs{prr.pru0, prr.pru1, prr.rru0, prr.rru1} // Check missing settings cannot be read by any RU. for _, ru := range rus { _, err := ru.ReadSettings("mysql/0") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": settings not found`) } // Add settings for one RU. assertNotInScope(c, prr.pru0) err := prr.pru0.EnterScope(map[string]interface{}{"gene": "simmons"}) c.Assert(err, gc.IsNil) node, err := prr.pru0.Settings() c.Assert(err, gc.IsNil) node.Set("meme", "foul-bachelor-frog") _, err = node.Write() c.Assert(err, gc.IsNil) assertInScope(c, prr.pru0) // Check settings can be read by every RU. for _, ru := range rus { m, err := ru.ReadSettings("mysql/0") c.Assert(err, gc.IsNil) c.Assert(m["gene"], gc.Equals, "simmons") c.Assert(m["meme"], gc.Equals, "foul-bachelor-frog") } } func (s *RelationUnitSuite) TestContainerSettings(c *gc.C) { prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeContainer) rus := RUs{prr.pru0, prr.pru1, prr.rru0, prr.rru1} // Check missing settings cannot be read by any RU. for _, ru := range rus { _, err := ru.ReadSettings("logging/0") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "logging/0" in relation "logging:info mysql:juju-info": settings not found`) } // Add settings for one RU. assertNotInScope(c, prr.pru0) err := prr.pru0.EnterScope(map[string]interface{}{"gene": "hackman"}) c.Assert(err, gc.IsNil) node, err := prr.pru0.Settings() c.Assert(err, gc.IsNil) node.Set("meme", "foul-bachelor-frog") _, err = node.Write() c.Assert(err, gc.IsNil) assertInScope(c, prr.pru0) // Check settings can be read by RUs in the same container. rus0 := RUs{prr.pru0, prr.rru0} for _, ru := range rus0 { m, err := ru.ReadSettings("mysql/0") c.Assert(err, gc.IsNil) c.Assert(m["gene"], gc.Equals, "hackman") c.Assert(m["meme"], gc.Equals, "foul-bachelor-frog") } // Check settings are still inaccessible to RUs outside that container rus1 := RUs{prr.pru1, prr.rru1} for _, ru := range rus1 { _, err := ru.ReadSettings("mysql/0") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "mysql/0" in relation "logging:info mysql:juju-info": settings not found`) } } func (s *RelationUnitSuite) TestContainerCreateSubordinate(c *gc.C) { psvc := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) rsvc := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"mysql", "logging"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) punit, err := psvc.AddUnit() c.Assert(err, gc.IsNil) pru, err := rel.Unit(punit) c.Assert(err, gc.IsNil) // Check that no units of the subordinate service exist. assertSubCount := func(expect int) []*state.Unit { runits, err := rsvc.AllUnits() c.Assert(err, gc.IsNil) c.Assert(runits, gc.HasLen, expect) return runits } assertSubCount(0) // Enter principal's scope and check a subordinate was created. assertNotInScope(c, pru) err = pru.EnterScope(nil) c.Assert(err, gc.IsNil) assertSubCount(1) assertInScope(c, pru) // Enter principal scope again and check no more subordinates created. err = pru.EnterScope(nil) c.Assert(err, gc.IsNil) assertSubCount(1) assertInScope(c, pru) // Leave principal scope, then re-enter, and check that still no further // subordinates are created. err = pru.LeaveScope() c.Assert(err, gc.IsNil) assertNotInScope(c, pru) err = pru.EnterScope(nil) c.Assert(err, gc.IsNil) runits := assertSubCount(1) assertInScope(c, pru) // Set the subordinate to Dying, and enter scope again; because the scope // is already entered, no error is returned. runit := runits[0] err = runit.Destroy() c.Assert(err, gc.IsNil) err = pru.EnterScope(nil) c.Assert(err, gc.IsNil) assertInScope(c, pru) // Leave scope, then try to enter again with the Dying subordinate. err = pru.LeaveScope() c.Assert(err, gc.IsNil) assertNotInScope(c, pru) err = pru.EnterScope(nil) c.Assert(err, gc.Equals, state.ErrCannotEnterScopeYet) assertNotInScope(c, pru) // Remove the subordinate, and enter scope again; this should work, and // create a new subordinate. err = runit.EnsureDead() c.Assert(err, gc.IsNil) err = runit.Remove() c.Assert(err, gc.IsNil) assertSubCount(0) assertNotInScope(c, pru) err = pru.EnterScope(nil) c.Assert(err, gc.IsNil) assertSubCount(1) assertInScope(c, pru) } func (s *RelationUnitSuite) TestDestroyRelationWithUnitsInScope(c *gc.C) { pr := NewPeerRelation(c, s.State) rel := pr.ru0.Relation() // Enter two units, and check that Destroying the service sets the // relation to Dying (rather than removing it directly). assertNotInScope(c, pr.ru0) err := pr.ru0.EnterScope(map[string]interface{}{"some": "settings"}) c.Assert(err, gc.IsNil) assertInScope(c, pr.ru0) assertNotInScope(c, pr.ru1) err = pr.ru1.EnterScope(nil) c.Assert(err, gc.IsNil) assertInScope(c, pr.ru1) err = pr.svc.Destroy() c.Assert(err, gc.IsNil) err = rel.Refresh() c.Assert(err, gc.IsNil) c.Assert(rel.Life(), gc.Equals, state.Dying) // Check that we can't add a new unit now. assertNotInScope(c, pr.ru2) err = pr.ru2.EnterScope(nil) c.Assert(err, gc.Equals, state.ErrCannotEnterScope) assertNotInScope(c, pr.ru2) // Check that we created no settings for the unit we failed to add. _, err = pr.ru0.ReadSettings("riak/2") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/2" in relation "riak:ring": settings not found`) // ru0 leaves the scope; check that service Destroy is still a no-op. assertInScope(c, pr.ru0) err = pr.ru0.LeaveScope() c.Assert(err, gc.IsNil) assertNotInScope(c, pr.ru0) err = pr.svc.Destroy() c.Assert(err, gc.IsNil) // Check that unit settings for the original unit still exist, and have // not yet been marked for deletion. err = s.State.Cleanup() c.Assert(err, gc.IsNil) assertSettings := func() { settings, err := pr.ru1.ReadSettings("riak/0") c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, map[string]interface{}{"some": "settings"}) } assertSettings() // The final unit leaves the scope, and cleans up after itself. assertInScope(c, pr.ru1) err = pr.ru1.LeaveScope() c.Assert(err, gc.IsNil) assertNotInScope(c, pr.ru1) err = rel.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // The settings were not themselves actually deleted yet... assertSettings() // ...but they were scheduled for deletion. err = s.State.Cleanup() c.Assert(err, gc.IsNil) _, err = pr.ru1.ReadSettings("riak/0") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": settings not found`) } func (s *RelationUnitSuite) TestAliveRelationScope(c *gc.C) { pr := NewPeerRelation(c, s.State) rel := pr.ru0.Relation() // Two units enter... assertNotInScope(c, pr.ru0) err := pr.ru0.EnterScope(nil) c.Assert(err, gc.IsNil) assertInScope(c, pr.ru0) assertNotInScope(c, pr.ru1) err = pr.ru1.EnterScope(nil) c.Assert(err, gc.IsNil) assertInScope(c, pr.ru1) // One unit becomes Dying, then re-enters the scope; this is not an error, // because the state is already as requested. err = pr.u0.Destroy() c.Assert(err, gc.IsNil) err = pr.ru0.EnterScope(nil) c.Assert(err, gc.IsNil) assertInScope(c, pr.ru0) // Two units leave... err = pr.ru0.LeaveScope() c.Assert(err, gc.IsNil) assertNotInScope(c, pr.ru0) err = pr.ru1.LeaveScope() c.Assert(err, gc.IsNil) assertNotInScope(c, pr.ru1) // The relation scope is empty, but the relation is still alive... err = rel.Refresh() c.Assert(err, gc.IsNil) c.Assert(rel.Life(), gc.Equals, state.Alive) // ...and new units can still join it... assertNotInScope(c, pr.ru2) err = pr.ru2.EnterScope(nil) c.Assert(err, gc.IsNil) assertInScope(c, pr.ru2) // ...but Dying units cannot. err = pr.u3.Destroy() c.Assert(err, gc.IsNil) assertNotInScope(c, pr.ru3) err = pr.ru3.EnterScope(nil) c.Assert(err, gc.Equals, state.ErrCannotEnterScope) assertNotInScope(c, pr.ru3) } func (s *StateSuite) TestWatchWatchScopeDiesOnStateClose(c *gc.C) { testWatcherDiesWhenStateCloses(c, func(c *gc.C, st *state.State) waiter { pr := NewPeerRelation(c, st) w := pr.ru0.WatchScope() <-w.Changes() return w }) } func (s *RelationUnitSuite) TestPeerWatchScope(c *gc.C) { pr := NewPeerRelation(c, s.State) // Test empty initial event. w0 := pr.ru0.WatchScope() defer testing.AssertStop(c, w0) s.assertScopeChange(c, w0, nil, nil) s.assertNoScopeChange(c, w0) // ru0 enters; check no change, but settings written. assertNotInScope(c, pr.ru0) err := pr.ru0.EnterScope(map[string]interface{}{"foo": "bar"}) c.Assert(err, gc.IsNil) s.assertNoScopeChange(c, w0) node, err := pr.ru0.Settings() c.Assert(err, gc.IsNil) c.Assert(node.Map(), gc.DeepEquals, map[string]interface{}{"foo": "bar"}) assertInScope(c, pr.ru0) // ru1 enters; check change is observed. assertNotInScope(c, pr.ru1) err = pr.ru1.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertScopeChange(c, w0, []string{"riak/1"}, nil) s.assertNoScopeChange(c, w0) assertInScope(c, pr.ru1) // ru1 enters again, check no problems and no changes. err = pr.ru1.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertNoScopeChange(c, w0) assertInScope(c, pr.ru1) // Stop watching; ru2 enters. testing.AssertStop(c, w0) assertNotInScope(c, pr.ru2) err = pr.ru2.EnterScope(nil) c.Assert(err, gc.IsNil) assertInScope(c, pr.ru2) // Start watch again, check initial event. w0 = pr.ru0.WatchScope() defer testing.AssertStop(c, w0) s.assertScopeChange(c, w0, []string{"riak/1", "riak/2"}, nil) s.assertNoScopeChange(c, w0) // ru1 leaves; check event. assertInScope(c, pr.ru1) err = pr.ru1.LeaveScope() c.Assert(err, gc.IsNil) s.assertScopeChange(c, w0, nil, []string{"riak/1"}) s.assertNoScopeChange(c, w0) assertNotInScope(c, pr.ru1) // ru1 leaves again; check no problems and no changes. err = pr.ru1.LeaveScope() c.Assert(err, gc.IsNil) s.assertNoScopeChange(c, w0) assertNotInScope(c, pr.ru1) } func (s *RelationUnitSuite) TestProReqWatchScope(c *gc.C) { prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeGlobal) // Test empty initial events for all RUs. ws := prr.watches() for _, w := range ws { defer testing.AssertStop(c, w) } for _, w := range ws { s.assertScopeChange(c, w, nil, nil) } s.assertNoScopeChange(c, ws...) // pru0 enters; check detected only by req RUs. assertNotInScope(c, prr.pru0) err := prr.pru0.EnterScope(nil) c.Assert(err, gc.IsNil) rws := func() []*state.RelationScopeWatcher { return []*state.RelationScopeWatcher{ws[2], ws[3]} } for _, w := range rws() { s.assertScopeChange(c, w, []string{"mysql/0"}, nil) } s.assertNoScopeChange(c, ws...) assertInScope(c, prr.pru0) // req0 enters; check detected only by pro RUs. assertNotInScope(c, prr.rru0) err = prr.rru0.EnterScope(nil) c.Assert(err, gc.IsNil) pws := func() []*state.RelationScopeWatcher { return []*state.RelationScopeWatcher{ws[0], ws[1]} } for _, w := range pws() { s.assertScopeChange(c, w, []string{"wordpress/0"}, nil) } s.assertNoScopeChange(c, ws...) assertInScope(c, prr.rru0) // Stop watches; remaining RUs enter. for _, w := range ws { testing.AssertStop(c, w) } assertNotInScope(c, prr.pru1) err = prr.pru1.EnterScope(nil) c.Assert(err, gc.IsNil) assertInScope(c, prr.pru1) assertNotInScope(c, prr.rru1) err = prr.rru1.EnterScope(nil) c.Assert(err, gc.IsNil) assertInScope(c, prr.rru0) // Start new watches, check initial events. ws = prr.watches() for _, w := range ws { defer testing.AssertStop(c, w) } for _, w := range pws() { s.assertScopeChange(c, w, []string{"wordpress/0", "wordpress/1"}, nil) } for _, w := range rws() { s.assertScopeChange(c, w, []string{"mysql/0", "mysql/1"}, nil) } s.assertNoScopeChange(c, ws...) // pru0 leaves; check detected only by req RUs. assertInScope(c, prr.pru0) err = prr.pru0.LeaveScope() c.Assert(err, gc.IsNil) for _, w := range rws() { s.assertScopeChange(c, w, nil, []string{"mysql/0"}) } s.assertNoScopeChange(c, ws...) assertNotInScope(c, prr.pru0) // rru0 leaves; check detected only by pro RUs. assertInScope(c, prr.rru0) err = prr.rru0.LeaveScope() c.Assert(err, gc.IsNil) for _, w := range pws() { s.assertScopeChange(c, w, nil, []string{"wordpress/0"}) } s.assertNoScopeChange(c, ws...) assertNotInScope(c, prr.rru0) } func (s *RelationUnitSuite) TestContainerWatchScope(c *gc.C) { prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeContainer) // Test empty initial events for all RUs. ws := prr.watches() for _, w := range ws { defer testing.AssertStop(c, w) } for _, w := range ws { s.assertScopeChange(c, w, nil, nil) } s.assertNoScopeChange(c, ws...) // pru0 enters; check detected only by same-container req. assertNotInScope(c, prr.pru0) err := prr.pru0.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertScopeChange(c, ws[2], []string{"mysql/0"}, nil) s.assertNoScopeChange(c, ws...) assertInScope(c, prr.pru0) // req1 enters; check detected only by same-container pro. assertNotInScope(c, prr.rru1) err = prr.rru1.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertScopeChange(c, ws[1], []string{"logging/1"}, nil) s.assertNoScopeChange(c, ws...) assertInScope(c, prr.rru1) // Stop watches; remaining RUs enter scope. for _, w := range ws { testing.AssertStop(c, w) } assertNotInScope(c, prr.pru1) err = prr.pru1.EnterScope(nil) c.Assert(err, gc.IsNil) assertNotInScope(c, prr.rru0) err = prr.rru0.EnterScope(nil) c.Assert(err, gc.IsNil) // Start new watches, check initial events. ws = prr.watches() for _, w := range ws { defer testing.AssertStop(c, w) } s.assertScopeChange(c, ws[0], []string{"logging/0"}, nil) s.assertScopeChange(c, ws[1], []string{"logging/1"}, nil) s.assertScopeChange(c, ws[2], []string{"mysql/0"}, nil) s.assertScopeChange(c, ws[3], []string{"mysql/1"}, nil) s.assertNoScopeChange(c, ws...) assertInScope(c, prr.pru1) assertInScope(c, prr.rru0) // pru0 leaves; check detected only by same-container req. assertInScope(c, prr.pru0) err = prr.pru0.LeaveScope() c.Assert(err, gc.IsNil) s.assertScopeChange(c, ws[2], nil, []string{"mysql/0"}) s.assertNoScopeChange(c, ws...) assertNotInScope(c, prr.pru0) // rru0 leaves; check detected only by same-container pro. assertInScope(c, prr.rru0) err = prr.rru0.LeaveScope() c.Assert(err, gc.IsNil) s.assertScopeChange(c, ws[0], nil, []string{"logging/0"}) s.assertNoScopeChange(c, ws...) assertNotInScope(c, prr.rru0) } func (s *RelationUnitSuite) assertScopeChange(c *gc.C, w *state.RelationScopeWatcher, entered, left []string) { s.State.StartSync() select { case ch, ok := <-w.Changes(): c.Assert(ok, gc.Equals, true) sort.Strings(entered) sort.Strings(ch.Entered) c.Assert(ch.Entered, gc.DeepEquals, entered) sort.Strings(left) sort.Strings(ch.Left) c.Assert(ch.Left, gc.DeepEquals, left) case <-time.After(coretesting.LongWait): c.Fatalf("no change") } } func (s *RelationUnitSuite) assertNoScopeChange(c *gc.C, ws ...*state.RelationScopeWatcher) { s.State.StartSync() for _, w := range ws { select { case ch, ok := <-w.Changes(): c.Fatalf("got unwanted change: %#v, %t", ch, ok) case <-time.After(coretesting.ShortWait): } } } type PeerRelation struct { rel *state.Relation svc *state.Service u0, u1, u2, u3 *state.Unit ru0, ru1, ru2, ru3 *state.RelationUnit } func NewPeerRelation(c *gc.C, st *state.State) *PeerRelation { svc := state.AddTestingService(c, st, "riak", state.AddTestingCharm(c, st, "riak")) ep, err := svc.Endpoint("ring") c.Assert(err, gc.IsNil) rel, err := st.EndpointsRelation(ep) c.Assert(err, gc.IsNil) pr := &PeerRelation{rel: rel, svc: svc} pr.u0, pr.ru0 = addRU(c, svc, rel, nil) pr.u1, pr.ru1 = addRU(c, svc, rel, nil) pr.u2, pr.ru2 = addRU(c, svc, rel, nil) pr.u3, pr.ru3 = addRU(c, svc, rel, nil) return pr } type ProReqRelation struct { rel *state.Relation psvc, rsvc *state.Service pu0, pu1, ru0, ru1 *state.Unit pru0, pru1, rru0, rru1 *state.RelationUnit } func NewProReqRelation(c *gc.C, s *ConnSuite, scope charm.RelationScope) *ProReqRelation { psvc := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) var rsvc *state.Service if scope == charm.ScopeGlobal { rsvc = s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) } else { rsvc = s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) } eps, err := s.State.InferEndpoints([]string{"mysql", rsvc.Name()}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) prr := &ProReqRelation{rel: rel, psvc: psvc, rsvc: rsvc} prr.pu0, prr.pru0 = addRU(c, psvc, rel, nil) prr.pu1, prr.pru1 = addRU(c, psvc, rel, nil) if scope == charm.ScopeGlobal { prr.ru0, prr.rru0 = addRU(c, rsvc, rel, nil) prr.ru1, prr.rru1 = addRU(c, rsvc, rel, nil) } else { prr.ru0, prr.rru0 = addRU(c, rsvc, rel, prr.pu0) prr.ru1, prr.rru1 = addRU(c, rsvc, rel, prr.pu1) } return prr } func (prr *ProReqRelation) watches() []*state.RelationScopeWatcher { return []*state.RelationScopeWatcher{ prr.pru0.WatchScope(), prr.pru1.WatchScope(), prr.rru0.WatchScope(), prr.rru1.WatchScope(), } } func addRU(c *gc.C, svc *state.Service, rel *state.Relation, principal *state.Unit) (*state.Unit, *state.RelationUnit) { // Given the service svc in the relation rel, add a unit of svc and create // a RelationUnit with rel. If principal is supplied, svc is assumed to be // subordinate and the unit will be created by temporarily entering the // relation's scope as the principal. var u *state.Unit if principal == nil { unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) u = unit } else { origUnits, err := svc.AllUnits() c.Assert(err, gc.IsNil) pru, err := rel.Unit(principal) c.Assert(err, gc.IsNil) err = pru.EnterScope(nil) // to create the subordinate c.Assert(err, gc.IsNil) err = pru.LeaveScope() // to reset to initial expected state c.Assert(err, gc.IsNil) newUnits, err := svc.AllUnits() c.Assert(err, gc.IsNil) for _, unit := range newUnits { found := false for _, old := range origUnits { if unit.Name() == old.Name() { found = true break } } if !found { u = unit break } } c.Assert(u, gc.NotNil) } preventUnitDestroyRemove(c, u) ru, err := rel.Unit(u) c.Assert(err, gc.IsNil) return u, ru } type WatchScopeSuite struct { ConnSuite } var _ = gc.Suite(&WatchScopeSuite{}) func (s *WatchScopeSuite) TestPeer(c *gc.C) { // Create a service and get a peer relation. riak := s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak")) riakEP, err := riak.Endpoint("ring") c.Assert(err, gc.IsNil) rels, err := riak.Relations() c.Assert(err, gc.IsNil) c.Assert(rels, gc.HasLen, 1) rel := rels[0] // Add some units to the service and set their private addresses; get // the relevant RelationUnits. // (Private addresses should be set by their unit agents on // startup; this test does not include that, but Join expects // the information to be available, and uses it to populate the // relation settings node.) addUnit := func(i int) *state.RelationUnit { unit, err := riak.AddUnit() c.Assert(err, gc.IsNil) err = unit.SetPrivateAddress(fmt.Sprintf("riak%d.example.com", i)) c.Assert(err, gc.IsNil) ru, err := rel.Unit(unit) c.Assert(err, gc.IsNil) c.Assert(ru.Endpoint(), gc.Equals, riakEP) return ru } ru0 := addUnit(0) ru1 := addUnit(1) ru2 := addUnit(2) // ---------- Single unit ---------- // Start watching the relation from the perspective of the first unit. w0 := ru0.Watch() defer testing.AssertStop(c, w0) w0c := testing.NewRelationUnitsWatcherC(c, s.State, w0) w0c.AssertChange(nil, nil) w0c.AssertNoChange() // Join the first unit to the relation, and change the settings, and // check that nothing apparently happens. err = ru0.EnterScope(nil) c.Assert(err, gc.IsNil) changeSettings(c, ru0) w0c.AssertNoChange() // ---------- Two units ---------- // Now join another unit to the relation... err = ru1.EnterScope(nil) c.Assert(err, gc.IsNil) // ...and check that the first relation unit sees the change. expectChanged := []string{"riak/1"} w0c.AssertChange(expectChanged, nil) w0c.AssertNoChange() // Join again, check it's a no-op. err = ru1.EnterScope(nil) c.Assert(err, gc.IsNil) w0c.AssertNoChange() // Start watching the relation from the perspective of the second unit, // and check that it sees the right state. w1 := ru1.Watch() defer testing.AssertStop(c, w1) w1c := testing.NewRelationUnitsWatcherC(c, s.State, w1) expectChanged = []string{"riak/0"} w1c.AssertChange(expectChanged, nil) w1c.AssertNoChange() // ---------- Three units ---------- // Whoa, it works. Ok, check the third unit's opinion of the state. w2 := ru2.Watch() defer testing.AssertStop(c, w2) w2c := testing.NewRelationUnitsWatcherC(c, s.State, w2) expectChanged = []string{"riak/0", "riak/1"} w2c.AssertChange(expectChanged, nil) w2c.AssertNoChange() // Join the third unit, and check the first and second units see it. err = ru2.EnterScope(nil) c.Assert(err, gc.IsNil) expectChanged = []string{"riak/2"} w0c.AssertChange(expectChanged, nil) w0c.AssertNoChange() w1c.AssertChange(expectChanged, nil) w1c.AssertNoChange() // Change the second unit's settings, and check that only // the first and third see changes. changeSettings(c, ru1) w1c.AssertNoChange() expectChanged = []string{"riak/1"} w0c.AssertChange(expectChanged, nil) w0c.AssertNoChange() w2c.AssertChange(expectChanged, nil) w2c.AssertNoChange() // ---------- Two units again ---------- // Depart the second unit, and check that the first and third detect it. err = ru1.LeaveScope() c.Assert(err, gc.IsNil) expectDeparted := []string{"riak/1"} w0c.AssertChange(nil, expectDeparted) w0c.AssertNoChange() w2c.AssertChange(nil, expectDeparted) w2c.AssertNoChange() // Change its settings, and check the others don't observe anything. changeSettings(c, ru1) w0c.AssertNoChange() w2c.AssertNoChange() // Check no spurious events showed up on the second unit's watch, and check // it closes cleanly. w1c.AssertNoChange() testing.AssertStop(c, w1) // OK, we're done here. Cleanup, and error detection during same, // will be handled by the deferred kill/stop calls. Phew. } func (s *WatchScopeSuite) TestProviderRequirerGlobal(c *gc.C) { // Create a pair of services and a relation between them. mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) mysqlEP, err := mysql.Endpoint("server") c.Assert(err, gc.IsNil) wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) wordpressEP, err := wordpress.Endpoint("db") c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(mysqlEP, wordpressEP) c.Assert(err, gc.IsNil) // Add some units to the services and set their private addresses. addUnit := func(srv *state.Service, sub string, ep state.Endpoint) *state.RelationUnit { unit, err := srv.AddUnit() c.Assert(err, gc.IsNil) ru, err := rel.Unit(unit) c.Assert(err, gc.IsNil) c.Assert(ru.Endpoint(), gc.Equals, ep) return ru } msru0 := addUnit(mysql, "ms0", mysqlEP) msru1 := addUnit(mysql, "ms1", mysqlEP) wpru0 := addUnit(wordpress, "wp0", wordpressEP) wpru1 := addUnit(wordpress, "wp1", wordpressEP) // ---------- Single role active ---------- // Watch the relation from the perspective of the first provider unit and // check initial event. msw0 := msru0.Watch() defer testing.AssertStop(c, msw0) msw0c := testing.NewRelationUnitsWatcherC(c, s.State, msw0) msw0c.AssertChange(nil, nil) msw0c.AssertNoChange() // Join the unit to the relation, change its settings, and check that // nothing apparently happens. err = msru0.EnterScope(nil) c.Assert(err, gc.IsNil) changeSettings(c, msru0) msw0c.AssertNoChange() // Join the second provider unit, start its watch, and check what it thinks the // state of the relation is. err = msru1.EnterScope(nil) c.Assert(err, gc.IsNil) msw1 := msru1.Watch() defer testing.AssertStop(c, msw1) msw1c := testing.NewRelationUnitsWatcherC(c, s.State, msw1) msw1c.AssertChange(nil, nil) msw1c.AssertNoChange() // Change the unit's settings, and check that neither provider unit // observes any change. changeSettings(c, msru1) msw1c.AssertNoChange() msw0c.AssertNoChange() // ---------- Two roles active ---------- // Start watches from both requirer units' perspectives, and check that // they see the provider units. expectChanged := []string{"mysql/0", "mysql/1"} wpw0 := wpru0.Watch() defer testing.AssertStop(c, wpw0) wpw0c := testing.NewRelationUnitsWatcherC(c, s.State, wpw0) wpw0c.AssertChange(expectChanged, nil) wpw0c.AssertNoChange() wpw1 := wpru1.Watch() defer testing.AssertStop(c, wpw1) wpw1c := testing.NewRelationUnitsWatcherC(c, s.State, wpw1) wpw1c.AssertChange(expectChanged, nil) wpw1c.AssertNoChange() // Join the first requirer unit, and check the provider units see it. err = wpru0.EnterScope(nil) c.Assert(err, gc.IsNil) expectChanged = []string{"wordpress/0"} msw0c.AssertChange(expectChanged, nil) msw0c.AssertNoChange() msw1c.AssertChange(expectChanged, nil) msw1c.AssertNoChange() // Join again, check no-op. err = wpru0.EnterScope(nil) c.Assert(err, gc.IsNil) msw0c.AssertNoChange() msw1c.AssertNoChange() // Join the second requirer, and check the provider units see the change. err = wpru1.EnterScope(nil) c.Assert(err, gc.IsNil) expectChanged = []string{"wordpress/1"} msw0c.AssertChange(expectChanged, nil) msw0c.AssertNoChange() msw1c.AssertChange(expectChanged, nil) msw1c.AssertNoChange() // Verify that neither requirer has observed any change to the relation. wpw0c.AssertNoChange() wpw1c.AssertNoChange() // Change settings for the first requirer, check providers see it... changeSettings(c, wpru0) expectChanged = []string{"wordpress/0"} msw0c.AssertChange(expectChanged, nil) msw0c.AssertNoChange() msw1c.AssertChange(expectChanged, nil) msw1c.AssertNoChange() // ...and requirers don't. wpw0c.AssertNoChange() wpw1c.AssertNoChange() // Depart the second requirer and check the providers see it... err = wpru1.LeaveScope() c.Assert(err, gc.IsNil) expectDeparted := []string{"wordpress/1"} msw0c.AssertChange(nil, expectDeparted) msw0c.AssertNoChange() msw1c.AssertChange(nil, expectDeparted) msw1c.AssertNoChange() // ...and the requirers don't. wpw0c.AssertNoChange() wpw1c.AssertNoChange() // Cleanup handled by defers as before. } func (s *WatchScopeSuite) TestProviderRequirerContainer(c *gc.C) { // Create a pair of services and a relation between them. mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) mysqlEP, err := mysql.Endpoint("juju-info") c.Assert(err, gc.IsNil) logging := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) loggingEP, err := logging.Endpoint("info") c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(mysqlEP, loggingEP) c.Assert(err, gc.IsNil) // Change mysqlEP to match the endpoint that will actually be used by the relation. mysqlEP.Scope = charm.ScopeContainer // Add some units to the services and set their private addresses. addUnits := func(i int) (*state.RelationUnit, *state.RelationUnit) { msu, err := mysql.AddUnit() c.Assert(err, gc.IsNil) msru, err := rel.Unit(msu) c.Assert(err, gc.IsNil) c.Assert(msru.Endpoint(), gc.Equals, mysqlEP) err = msru.EnterScope(nil) c.Assert(err, gc.IsNil) err = msru.LeaveScope() c.Assert(err, gc.IsNil) lgu, err := s.State.Unit("logging/" + strconv.Itoa(i)) c.Assert(err, gc.IsNil) lgru, err := rel.Unit(lgu) c.Assert(err, gc.IsNil) c.Assert(lgru.Endpoint(), gc.Equals, loggingEP) return msru, lgru } msru0, lgru0 := addUnits(0) msru1, lgru1 := addUnits(1) // ---------- Single role active ---------- // Start watching the relation from the perspective of the first unit, and // check the initial event. msw0 := msru0.Watch() defer testing.AssertStop(c, msw0) msw0c := testing.NewRelationUnitsWatcherC(c, s.State, msw0) msw0c.AssertChange(nil, nil) msw0c.AssertNoChange() // Join the unit to the relation, change its settings, and check that // nothing apparently happens. err = msru0.EnterScope(nil) c.Assert(err, gc.IsNil) changeSettings(c, msru0) msw0c.AssertNoChange() // Watch the relation from the perspective of the second provider, and // check initial event. msw1 := msru1.Watch() defer testing.AssertStop(c, msw1) msw1c := testing.NewRelationUnitsWatcherC(c, s.State, msw1) msw1c.AssertChange(nil, nil) msw1c.AssertNoChange() // Join the second provider unit to the relation, and check that neither // watching unit observes any change. err = msru1.EnterScope(nil) c.Assert(err, gc.IsNil) msw1c.AssertNoChange() msw0c.AssertNoChange() // Change the unit's settings, and check that nothing apparently happens. changeSettings(c, msru1) msw1c.AssertNoChange() msw0c.AssertNoChange() // ---------- Two roles active ---------- // Start a watch from the first requirer unit's perspective, and check it // only sees the first provider (with which it shares a container). lgw0 := lgru0.Watch() defer testing.AssertStop(c, lgw0) lgw0c := testing.NewRelationUnitsWatcherC(c, s.State, lgw0) expectChanged := []string{"mysql/0"} lgw0c.AssertChange(expectChanged, nil) lgw0c.AssertNoChange() // Join the first requirer unit, and check that only the first provider // observes the change. err = lgru0.EnterScope(nil) c.Assert(err, gc.IsNil) expectChanged = []string{"logging/0"} msw0c.AssertChange(expectChanged, nil) msw0c.AssertNoChange() msw1c.AssertNoChange() lgw0c.AssertNoChange() // Watch from the second requirer's perspective, and check it only sees the // second provider. lgw1 := lgru1.Watch() defer testing.AssertStop(c, lgw1) lgw1c := testing.NewRelationUnitsWatcherC(c, s.State, lgw1) expectChanged = []string{"mysql/1"} lgw1c.AssertChange(expectChanged, nil) lgw1c.AssertNoChange() // Join the second requirer, and check that the first provider observes it... err = lgru1.EnterScope(nil) c.Assert(err, gc.IsNil) expectChanged = []string{"logging/1"} msw1c.AssertChange(expectChanged, nil) msw1c.AssertNoChange() // ...and that nothing else sees anything. msw0c.AssertNoChange() lgw0c.AssertNoChange() lgw1c.AssertNoChange() // Change the second provider's settings and check that the second // requirer notices... changeSettings(c, msru1) expectChanged = []string{"mysql/1"} lgw1c.AssertChange(expectChanged, nil) lgw1c.AssertNoChange() // ...but that nothing else does. msw0c.AssertNoChange() msw1c.AssertNoChange() msw0c.AssertNoChange() // Finally, depart the first provider, and check that only the first // requirer observes any change. err = msru0.LeaveScope() c.Assert(err, gc.IsNil) expectDeparted := []string{"mysql/0"} lgw0c.AssertChange(nil, expectDeparted) lgw0c.AssertNoChange() lgw1c.AssertNoChange() msw0c.AssertNoChange() msw1c.AssertNoChange() // Again, I think we're done, and can be comfortable that the appropriate // connections are in place. } func changeSettings(c *gc.C, ru *state.RelationUnit) { node, err := ru.Settings() c.Assert(err, gc.IsNil) value, _ := node.Get("value") v, _ := value.(int) node.Set("value", v+1) _, err = node.Write() c.Assert(err, gc.IsNil) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/watcher/�����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023427� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/watcher/helpers.go�������������������������������0000644�0000153�0000161�00000001462�12321735642�025436� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package watcher import ( "launchpad.net/tomb" ) // Stopper is implemented by all watchers. type Stopper interface { Stop() error } // Errer is implemented by all watchers. type Errer interface { Err() error } // Stop stops the watcher. If an error is returned by the // watcher, t is killed with the error. func Stop(w Stopper, t *tomb.Tomb) { if err := w.Stop(); err != nil { t.Kill(err) } } // MustErr returns the error with which w died. // Calling it will panic if w is still running or was stopped cleanly. func MustErr(w Errer) error { err := w.Err() if err == nil { panic("watcher was stopped cleanly") } else if err == tomb.ErrStillAlive { panic("watcher is still running") } return err } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/watcher/helpers_test.go��������������������������0000644�0000153�0000161�00000002037�12321735642�026474� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package watcher_test import ( "errors" gc "launchpad.net/gocheck" "launchpad.net/tomb" "launchpad.net/juju-core/state/watcher" ) type dummyWatcher struct { err error } func (w *dummyWatcher) Stop() error { return w.err } func (w *dummyWatcher) Err() error { return w.err } func (s *FastPeriodSuite) TestStop(c *gc.C) { t := &tomb.Tomb{} watcher.Stop(&dummyWatcher{nil}, t) c.Assert(t.Err(), gc.Equals, tomb.ErrStillAlive) watcher.Stop(&dummyWatcher{errors.New("BLAM")}, t) c.Assert(t.Err(), gc.ErrorMatches, "BLAM") } func (s *FastPeriodSuite) TestMustErr(c *gc.C) { err := watcher.MustErr(&dummyWatcher{errors.New("POW")}) c.Assert(err, gc.ErrorMatches, "POW") stillAlive := func() { watcher.MustErr(&dummyWatcher{tomb.ErrStillAlive}) } c.Assert(stillAlive, gc.PanicMatches, "watcher is still running") noErr := func() { watcher.MustErr(&dummyWatcher{nil}) } c.Assert(noErr, gc.PanicMatches, "watcher was stopped cleanly") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/watcher/watcher_test.go��������������������������0000644�0000153�0000161�00000043021�12321735642�026465� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package watcher_test import ( stdtesting "testing" "time" "labix.org/v2/mgo" "labix.org/v2/mgo/txn" gc "launchpad.net/gocheck" "launchpad.net/tomb" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) // Test tuning parameters. const ( // worstCase is used for timeouts when timing out // will fail the test. Raising this value should // not affect the overall running time of the tests // unless they fail. worstCase = testing.LongWait // justLongEnough is used for timeouts that // are expected to happen for a test to complete // successfully. Reducing this value will make // the tests run faster at the expense of making them // fail more often on heavily loaded or slow hardware. justLongEnough = testing.ShortWait // fastPeriod specifies the period of the watcher for // tests where the timing is not critical. fastPeriod = 10 * time.Millisecond // slowPeriod specifies the period of the watcher // for tests where the timing is important. slowPeriod = 1 * time.Second ) func TestPackage(t *stdtesting.T) { testing.MgoTestPackage(t) } type watcherSuite struct { testing.MgoSuite testbase.LoggingSuite log *mgo.Collection stash *mgo.Collection runner *txn.Runner w *watcher.Watcher ch chan watcher.Change oldPeriod time.Duration } // FastPeriodSuite implements tests that should // work regardless of the watcher refresh period. type FastPeriodSuite struct { watcherSuite } func (s *FastPeriodSuite) SetUpSuite(c *gc.C) { s.watcherSuite.SetUpSuite(c) watcher.Period = fastPeriod } var _ = gc.Suite(&FastPeriodSuite{}) func (s *watcherSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) s.oldPeriod = watcher.Period } func (s *watcherSuite) TearDownSuite(c *gc.C) { s.MgoSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) watcher.Period = s.oldPeriod } func (s *watcherSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) db := s.MgoSuite.Session.DB("juju") s.log = db.C("txnlog") s.log.Create(&mgo.CollectionInfo{ Capped: true, MaxBytes: 1000000, }) s.stash = db.C("txn.stash") s.runner = txn.NewRunner(db.C("txn")) s.runner.ChangeLog(s.log) s.w = watcher.New(s.log) s.ch = make(chan watcher.Change) } func (s *watcherSuite) TearDownTest(c *gc.C) { c.Assert(s.w.Stop(), gc.IsNil) s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } type M map[string]interface{} func assertChange(c *gc.C, watch <-chan watcher.Change, want watcher.Change) { select { case got := <-watch: if got != want { c.Fatalf("watch reported %v, want %v", got, want) } case <-time.After(worstCase): c.Fatalf("watch reported nothing, want %v", want) } } func assertNoChange(c *gc.C, watch <-chan watcher.Change) { select { case got := <-watch: c.Fatalf("watch reported %v, want nothing", got) case <-time.After(justLongEnough): } } func assertOrder(c *gc.C, revnos ...int64) { last := int64(-2) for _, revno := range revnos { if revno <= last { c.Fatalf("got bad revno sequence: %v", revnos) } last = revno } } func (s *watcherSuite) revno(c string, id interface{}) (revno int64) { var doc struct { Revno int64 "txn-revno" } err := s.log.Database.C(c).FindId(id).One(&doc) if err != nil { panic(err) } return doc.Revno } func (s *watcherSuite) insert(c *gc.C, coll string, id interface{}) (revno int64) { ops := []txn.Op{{C: coll, Id: id, Insert: M{"n": 1}}} err := s.runner.Run(ops, "", nil) if err != nil { panic(err) } revno = s.revno(coll, id) c.Logf("insert(%#v, %#v) => revno %d", coll, id, revno) return revno } func (s *watcherSuite) insertAll(c *gc.C, coll string, ids ...interface{}) (revnos []int64) { var ops []txn.Op for _, id := range ids { ops = append(ops, txn.Op{C: coll, Id: id, Insert: M{"n": 1}}) } err := s.runner.Run(ops, "", nil) if err != nil { panic(err) } for _, id := range ids { revnos = append(revnos, s.revno(coll, id)) } c.Logf("insertAll(%#v, %v) => revnos %v", coll, ids, revnos) return revnos } func (s *watcherSuite) update(c *gc.C, coll string, id interface{}) (revno int64) { ops := []txn.Op{{C: coll, Id: id, Update: M{"$inc": M{"n": 1}}}} err := s.runner.Run(ops, "", nil) if err != nil { panic(err) } revno = s.revno(coll, id) c.Logf("update(%#v, %#v) => revno %d", coll, id, revno) return revno } func (s *watcherSuite) remove(c *gc.C, coll string, id interface{}) (revno int64) { ops := []txn.Op{{C: coll, Id: id, Remove: true}} err := s.runner.Run(ops, "", nil) if err != nil { panic(err) } c.Logf("remove(%#v, %#v) => revno -1", coll, id) return -1 } func (s *FastPeriodSuite) TestErrAndDead(c *gc.C) { c.Assert(s.w.Err(), gc.Equals, tomb.ErrStillAlive) select { case <-s.w.Dead(): c.Fatalf("Dead channel fired unexpectedly") default: } c.Assert(s.w.Stop(), gc.IsNil) c.Assert(s.w.Err(), gc.IsNil) select { case <-s.w.Dead(): default: c.Fatalf("Dead channel should have fired") } } func (s *FastPeriodSuite) TestWatchBeforeKnown(c *gc.C) { s.w.Watch("test", "a", -1, s.ch) assertNoChange(c, s.ch) revno := s.insert(c, "test", "a") s.w.StartSync() assertChange(c, s.ch, watcher.Change{"test", "a", revno}) assertNoChange(c, s.ch) assertOrder(c, -1, revno) } func (s *FastPeriodSuite) TestWatchAfterKnown(c *gc.C) { revno := s.insert(c, "test", "a") s.w.StartSync() s.w.Watch("test", "a", -1, s.ch) assertChange(c, s.ch, watcher.Change{"test", "a", revno}) assertNoChange(c, s.ch) assertOrder(c, -1, revno) } func (s *FastPeriodSuite) TestWatchIgnoreUnwatched(c *gc.C) { s.w.Watch("test", "a", -1, s.ch) assertNoChange(c, s.ch) s.insert(c, "test", "b") s.w.StartSync() assertNoChange(c, s.ch) } func (s *FastPeriodSuite) TestWatchOrder(c *gc.C) { s.w.StartSync() for _, id := range []string{"a", "b", "c", "d"} { s.w.Watch("test", id, -1, s.ch) } revno1 := s.insert(c, "test", "a") revno2 := s.insert(c, "test", "b") revno3 := s.insert(c, "test", "c") s.w.StartSync() assertChange(c, s.ch, watcher.Change{"test", "a", revno1}) assertChange(c, s.ch, watcher.Change{"test", "b", revno2}) assertChange(c, s.ch, watcher.Change{"test", "c", revno3}) assertNoChange(c, s.ch) } func (s *FastPeriodSuite) TestTransactionWithMultiple(c *gc.C) { s.w.StartSync() for _, id := range []string{"a", "b", "c"} { s.w.Watch("test", id, -1, s.ch) } revnos := s.insertAll(c, "test", "a", "b", "c") s.w.StartSync() assertChange(c, s.ch, watcher.Change{"test", "a", revnos[0]}) assertChange(c, s.ch, watcher.Change{"test", "b", revnos[1]}) assertChange(c, s.ch, watcher.Change{"test", "c", revnos[2]}) assertNoChange(c, s.ch) } func (s *FastPeriodSuite) TestWatchMultipleChannels(c *gc.C) { ch1 := make(chan watcher.Change) ch2 := make(chan watcher.Change) ch3 := make(chan watcher.Change) s.w.Watch("test1", 1, -1, ch1) s.w.Watch("test2", 2, -1, ch2) s.w.Watch("test3", 3, -1, ch3) revno1 := s.insert(c, "test1", 1) revno2 := s.insert(c, "test2", 2) revno3 := s.insert(c, "test3", 3) s.w.StartSync() s.w.Unwatch("test2", 2, ch2) assertChange(c, ch1, watcher.Change{"test1", 1, revno1}) _ = revno2 assertChange(c, ch3, watcher.Change{"test3", 3, revno3}) assertNoChange(c, ch1) assertNoChange(c, ch2) assertNoChange(c, ch3) } func (s *FastPeriodSuite) TestIgnoreAncientHistory(c *gc.C) { s.insert(c, "test", "a") w := watcher.New(s.log) defer w.Stop() w.StartSync() w.Watch("test", "a", -1, s.ch) assertNoChange(c, s.ch) } func (s *FastPeriodSuite) TestUpdate(c *gc.C) { s.w.Watch("test", "a", -1, s.ch) assertNoChange(c, s.ch) revno1 := s.insert(c, "test", "a") s.w.StartSync() assertChange(c, s.ch, watcher.Change{"test", "a", revno1}) assertNoChange(c, s.ch) revno2 := s.update(c, "test", "a") s.w.StartSync() assertChange(c, s.ch, watcher.Change{"test", "a", revno2}) assertOrder(c, -1, revno1, revno2) } func (s *FastPeriodSuite) TestRemove(c *gc.C) { s.w.Watch("test", "a", -1, s.ch) assertNoChange(c, s.ch) revno1 := s.insert(c, "test", "a") s.w.StartSync() assertChange(c, s.ch, watcher.Change{"test", "a", revno1}) assertNoChange(c, s.ch) revno2 := s.remove(c, "test", "a") s.w.StartSync() assertChange(c, s.ch, watcher.Change{"test", "a", -1}) assertNoChange(c, s.ch) revno3 := s.insert(c, "test", "a") s.w.StartSync() assertChange(c, s.ch, watcher.Change{"test", "a", revno3}) assertNoChange(c, s.ch) assertOrder(c, revno2, revno1) assertOrder(c, revno2, revno3) } func (s *FastPeriodSuite) TestWatchKnownRemove(c *gc.C) { revno1 := s.insert(c, "test", "a") revno2 := s.remove(c, "test", "a") s.w.StartSync() s.w.Watch("test", "a", revno1, s.ch) assertChange(c, s.ch, watcher.Change{"test", "a", revno2}) assertOrder(c, revno2, revno1) } func (s *FastPeriodSuite) TestScale(c *gc.C) { const N = 500 const T = 10 c.Logf("Creating %d documents, %d per transaction...", N, T) ops := make([]txn.Op, T) for i := 0; i < (N / T); i++ { ops = ops[:0] for j := 0; j < T && i*T+j < N; j++ { ops = append(ops, txn.Op{C: "test", Id: i*T + j, Insert: M{"n": 1}}) } err := s.runner.Run(ops, "", nil) c.Assert(err, gc.IsNil) } c.Logf("Watching all documents...") for i := 0; i < N; i++ { s.w.Watch("test", i, -1, s.ch) } c.Logf("Forcing a refresh...") s.w.StartSync() count, err := s.Session.DB("juju").C("test").Count() c.Assert(err, gc.IsNil) c.Logf("Got %d documents in the collection...", count) c.Assert(count, gc.Equals, N) c.Logf("Reading all changes...") seen := make(map[interface{}]bool) for i := 0; i < N; i++ { select { case change := <-s.ch: seen[change.Id] = true case <-time.After(worstCase): c.Fatalf("not enough changes: got %d, want %d", len(seen), N) } } c.Assert(len(seen), gc.Equals, N) } func (s *FastPeriodSuite) TestWatchUnwatchOnQueue(c *gc.C) { const N = 10 for i := 0; i < N; i++ { s.insert(c, "test", i) } s.w.StartSync() for i := 0; i < N; i++ { s.w.Watch("test", i, -1, s.ch) } for i := 1; i < N; i += 2 { s.w.Unwatch("test", i, s.ch) } s.w.StartSync() seen := make(map[interface{}]bool) for i := 0; i < N/2; i++ { select { case change := <-s.ch: seen[change.Id] = true case <-time.After(worstCase): c.Fatalf("not enough changes: got %d, want %d", len(seen), N/2) } } c.Assert(len(seen), gc.Equals, N/2) assertNoChange(c, s.ch) } func (s *FastPeriodSuite) TestStartSync(c *gc.C) { s.w.Watch("test", "a", -1, s.ch) revno := s.insert(c, "test", "a") done := make(chan bool) go func() { s.w.StartSync() s.w.StartSync() s.w.StartSync() done <- true }() select { case <-done: case <-time.After(worstCase): c.Fatalf("StartSync failed to return") } assertChange(c, s.ch, watcher.Change{"test", "a", revno}) } func (s *FastPeriodSuite) TestWatchCollection(c *gc.C) { chA1 := make(chan watcher.Change) chB1 := make(chan watcher.Change) chA := make(chan watcher.Change) chB := make(chan watcher.Change) s.w.Watch("testA", 1, -1, chA1) s.w.Watch("testB", 1, -1, chB1) s.w.WatchCollection("testA", chA) s.w.WatchCollection("testB", chB) revno1 := s.insert(c, "testA", 1) revno2 := s.insert(c, "testA", 2) revno3 := s.insert(c, "testB", 1) revno4 := s.insert(c, "testB", 2) s.w.StartSync() seen := map[chan<- watcher.Change][]watcher.Change{} timeout := time.After(testing.LongWait) n := 0 Loop1: for n < 6 { select { case chg := <-chA1: seen[chA1] = append(seen[chA1], chg) case chg := <-chB1: seen[chB1] = append(seen[chB1], chg) case chg := <-chA: seen[chA] = append(seen[chA], chg) case chg := <-chB: seen[chB] = append(seen[chB], chg) case <-timeout: break Loop1 } n++ } c.Check(seen[chA1], gc.DeepEquals, []watcher.Change{{"testA", 1, revno1}}) c.Check(seen[chB1], gc.DeepEquals, []watcher.Change{{"testB", 1, revno3}}) c.Check(seen[chA], gc.DeepEquals, []watcher.Change{{"testA", 1, revno1}, {"testA", 2, revno2}}) c.Check(seen[chB], gc.DeepEquals, []watcher.Change{{"testB", 1, revno3}, {"testB", 2, revno4}}) if c.Failed() { return } s.w.UnwatchCollection("testB", chB) s.w.Unwatch("testB", 1, chB1) revno1 = s.update(c, "testA", 1) revno3 = s.update(c, "testB", 1) s.w.StartSync() timeout = time.After(testing.LongWait) seen = map[chan<- watcher.Change][]watcher.Change{} n = 0 Loop2: for n < 2 { select { case chg := <-chA1: seen[chA1] = append(seen[chA1], chg) case chg := <-chB1: seen[chB1] = append(seen[chB1], chg) case chg := <-chA: seen[chA] = append(seen[chA], chg) case chg := <-chB: seen[chB] = append(seen[chB], chg) case <-timeout: break Loop2 } n++ } c.Check(seen[chA1], gc.DeepEquals, []watcher.Change{{"testA", 1, revno1}}) c.Check(seen[chB1], gc.IsNil) c.Check(seen[chA], gc.DeepEquals, []watcher.Change{{"testA", 1, revno1}}) c.Check(seen[chB], gc.IsNil) // Check that no extra events arrive. seen = map[chan<- watcher.Change][]watcher.Change{} timeout = time.After(testing.ShortWait) Loop3: for { select { case chg := <-chA1: seen[chA1] = append(seen[chA1], chg) case chg := <-chB1: seen[chB1] = append(seen[chB1], chg) case chg := <-chA: seen[chA] = append(seen[chA], chg) case chg := <-chB: seen[chB] = append(seen[chB], chg) case <-timeout: break Loop3 } } c.Check(seen[chA1], gc.IsNil) c.Check(seen[chB1], gc.IsNil) c.Check(seen[chA], gc.IsNil) c.Check(seen[chB], gc.IsNil) } func (s *FastPeriodSuite) TestUnwatchCollectionWithFilter(c *gc.C) { filter := func(key interface{}) bool { id := key.(int) return id != 2 } chA := make(chan watcher.Change) s.w.WatchCollectionWithFilter("testA", chA, filter) revnoA := s.insert(c, "testA", 1) assertChange(c, chA, watcher.Change{"testA", 1, revnoA}) s.insert(c, "testA", 2) assertNoChange(c, chA) s.insert(c, "testA", 3) s.w.StartSync() assertChange(c, chA, watcher.Change{"testA", 3, revnoA}) } func (s *FastPeriodSuite) TestUnwatchCollectionWithOutstandingRequest(c *gc.C) { chA := make(chan watcher.Change) s.w.WatchCollection("testA", chA) chB := make(chan watcher.Change) s.w.Watch("testB", 1, -1, chB) revnoA := s.insert(c, "testA", 1) s.insert(c, "testA", 2) // By inserting this *after* the testA document, we ensure that // the watcher will try to send on chB after sending on chA. // The original bug that this test guards against meant that the // UnwatchCollection did not correctly cancel the outstanding // request, so the loop would never get around to sending on // chB. revnoB := s.insert(c, "testB", 1) s.w.StartSync() // When we receive the first change on chA, we know that // the watcher is trying to send changes on all the // watcher channels (2 changes on chA and 1 change on chB). assertChange(c, chA, watcher.Change{"testA", 1, revnoA}) s.w.UnwatchCollection("testA", chA) assertChange(c, chB, watcher.Change{"testB", 1, revnoB}) } func (s *FastPeriodSuite) TestNonMutatingTxn(c *gc.C) { chA1 := make(chan watcher.Change) chA := make(chan watcher.Change) revno1 := s.insert(c, "test", "a") s.w.StartSync() s.w.Watch("test", 1, revno1, chA1) s.w.WatchCollection("test", chA) revno2 := s.insert(c, "test", "a") c.Assert(revno1, gc.Equals, revno2) s.w.StartSync() assertNoChange(c, chA1) assertNoChange(c, chA) } // SlowPeriodSuite implements tests // that are flaky when the watcher refresh period // is small. type SlowPeriodSuite struct { watcherSuite } func (s *SlowPeriodSuite) SetUpSuite(c *gc.C) { s.watcherSuite.SetUpSuite(c) watcher.Period = slowPeriod } var _ = gc.Suite(&SlowPeriodSuite{}) func (s *SlowPeriodSuite) TestWatchBeforeRemoveKnown(c *gc.C) { revno1 := s.insert(c, "test", "a") s.w.StartSync() revno2 := s.remove(c, "test", "a") s.w.Watch("test", "a", -1, s.ch) assertChange(c, s.ch, watcher.Change{"test", "a", revno1}) s.w.StartSync() assertChange(c, s.ch, watcher.Change{"test", "a", revno2}) assertOrder(c, revno2, revno1) } func (s *SlowPeriodSuite) TestDoubleUpdate(c *gc.C) { assertNoChange(c, s.ch) revno1 := s.insert(c, "test", "a") s.w.StartSync() revno2 := s.update(c, "test", "a") revno3 := s.update(c, "test", "a") s.w.Watch("test", "a", revno2, s.ch) assertNoChange(c, s.ch) s.w.StartSync() assertChange(c, s.ch, watcher.Change{"test", "a", revno3}) assertNoChange(c, s.ch) assertOrder(c, -1, revno1, revno2, revno3) } func (s *SlowPeriodSuite) TestWatchPeriod(c *gc.C) { revno1 := s.insert(c, "test", "a") s.w.StartSync() t0 := time.Now() s.w.Watch("test", "a", revno1, s.ch) revno2 := s.update(c, "test", "a") leeway := watcher.Period / 4 select { case got := <-s.ch: gotPeriod := time.Since(t0) c.Assert(got, gc.Equals, watcher.Change{"test", "a", revno2}) if gotPeriod < watcher.Period-leeway { c.Fatalf("watcher not waiting long enough; got %v want %v", gotPeriod, watcher.Period) } case <-time.After(watcher.Period + leeway): gotPeriod := time.Since(t0) c.Fatalf("watcher waited too long; got %v want %v", gotPeriod, watcher.Period) } assertOrder(c, -1, revno1, revno2) } func (s *SlowPeriodSuite) TestStartSyncStartsImmediately(c *gc.C) { // Ensure we're at the start of a sync cycle. s.w.StartSync() time.Sleep(justLongEnough) // Watching after StartSync should see the current state of affairs. revno := s.insert(c, "test", "a") s.w.StartSync() s.w.Watch("test", "a", -1, s.ch) select { case got := <-s.ch: c.Assert(got.Revno, gc.Equals, revno) case <-time.After(watcher.Period / 2): c.Fatalf("watch after StartSync is still using old info") } s.remove(c, "test", "a") s.w.StartSync() ch := make(chan watcher.Change) s.w.Watch("test", "a", -1, ch) select { case got := <-ch: c.Fatalf("got event %#v when starting watcher after doc was removed", got) case <-time.After(justLongEnough): } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/watcher/watcher.go�������������������������������0000644�0000153�0000161�00000026561�12321735776�025450� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The watcher package provides an interface for observing changes // to arbitrary MongoDB documents that are maintained via the // mgo/txn transaction package. package watcher import ( "fmt" "time" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "launchpad.net/tomb" "launchpad.net/juju-core/log" ) // Debug specifies whether the package will log debug // messages. // TODO(rog) allow debug level setting in the log package. var Debug = false // A Watcher can watch any number of collections and documents for changes. type Watcher struct { tomb tomb.Tomb log *mgo.Collection // watches holds the observers managed by Watch/Unwatch. watches map[watchKey][]watchInfo // current holds the current txn-revno values for all the observed // documents known to exist. Documents not observed or deleted are // omitted from this map and are considered to have revno -1. current map[watchKey]int64 // needSync is set when a synchronization should take // place. needSync bool // syncEvents and requestEvents contain the events to be // dispatched to the watcher channels. They're queued during // processing and flushed at the end to simplify the algorithm. // The two queues are separated because events from sync are // handled in reverse order due to the way the algorithm works. syncEvents, requestEvents []event // request is used to deliver requests from the public API into // the the goroutine loop. request chan interface{} // lastId is the most recent transaction id observed by a sync. lastId interface{} } // A Change holds information about a document change. type Change struct { // C and Id hold the collection name and document _id field value. C string Id interface{} // Revno is the latest known value for the document's txn-revno // field, or -1 if the document was deleted. Revno int64 } type watchKey struct { c string id interface{} // nil when watching collection } func (k watchKey) String() string { coll := "collection " + k.c if k.id == nil { return coll } return fmt.Sprintf("document %v in %s", k.id, coll) } // match returns whether the receiving watch key, // which may refer to a particular item or // an entire collection, matches k1, which refers // to a particular item. func (k watchKey) match(k1 watchKey) bool { if k.c != k1.c { return false } if k.id == nil { // k refers to entire collection return true } return k.id == k1.id } type watchInfo struct { ch chan<- Change revno int64 filter func(interface{}) bool } type event struct { ch chan<- Change key watchKey revno int64 } // New returns a new Watcher observing the changelog collection, // which must be a capped collection maintained by mgo/txn. func New(changelog *mgo.Collection) *Watcher { w := &Watcher{ log: changelog, watches: make(map[watchKey][]watchInfo), current: make(map[watchKey]int64), request: make(chan interface{}), } go func() { w.tomb.Kill(w.loop()) w.tomb.Done() }() return w } // Stop stops all the watcher activities. func (w *Watcher) Stop() error { w.tomb.Kill(nil) return w.tomb.Wait() } // Dead returns a channel that is closed when the watcher has stopped. func (w *Watcher) Dead() <-chan struct{} { return w.tomb.Dead() } // Err returns the error with which the watcher stopped. // It returns nil if the watcher stopped cleanly, tomb.ErrStillAlive // if the watcher is still running properly, or the respective error // if the watcher is terminating or has terminated with an error. func (w *Watcher) Err() error { return w.tomb.Err() } type reqWatch struct { key watchKey info watchInfo } type reqUnwatch struct { key watchKey ch chan<- Change } type reqSync struct{} func (w *Watcher) sendReq(req interface{}) { select { case w.request <- req: case <-w.tomb.Dying(): } } // Watch starts watching the given collection and document id. // An event will be sent onto ch whenever a matching document's txn-revno // field is observed to change after a transaction is applied. The revno // parameter holds the currently known revision number for the document. // Non-existent documents are represented by a -1 revno. func (w *Watcher) Watch(collection string, id interface{}, revno int64, ch chan<- Change) { if id == nil { panic("watcher: cannot watch a document with nil id") } w.sendReq(reqWatch{watchKey{collection, id}, watchInfo{ch, revno, nil}}) } // WatchCollection starts watching the given collection. // An event will be sent onto ch whenever the txn-revno field is observed // to change after a transaction is applied for any document in the collection. func (w *Watcher) WatchCollection(collection string, ch chan<- Change) { w.WatchCollectionWithFilter(collection, ch, nil) } // WatchCollectionWithFilter starts watching the given collection. // An event will be sent onto ch whenever the txn-revno field is observed // to change after a transaction is applied for any document in the collection, so long as the // specified filter function returns true when called with the document id value. func (w *Watcher) WatchCollectionWithFilter(collection string, ch chan<- Change, filter func(interface{}) bool) { w.sendReq(reqWatch{watchKey{collection, nil}, watchInfo{ch, 0, filter}}) } // Unwatch stops watching the given collection and document id via ch. func (w *Watcher) Unwatch(collection string, id interface{}, ch chan<- Change) { if id == nil { panic("watcher: cannot unwatch a document with nil id") } w.sendReq(reqUnwatch{watchKey{collection, id}, ch}) } // UnwatchCollection stops watching the given collection via ch. func (w *Watcher) UnwatchCollection(collection string, ch chan<- Change) { w.sendReq(reqUnwatch{watchKey{collection, nil}, ch}) } // StartSync forces the watcher to load new events from the database. func (w *Watcher) StartSync() { w.sendReq(reqSync{}) } // Period is the delay between each sync. // It must not be changed when any watchers are active. var Period time.Duration = 5 * time.Second // loop implements the main watcher loop. func (w *Watcher) loop() error { next := time.After(Period) w.needSync = true if err := w.initLastId(); err != nil { return err } for { if w.needSync { if err := w.sync(); err != nil { return err } w.flush() next = time.After(Period) } select { case <-w.tomb.Dying(): return tomb.ErrDying case <-next: next = time.After(Period) w.needSync = true case req := <-w.request: w.handle(req) w.flush() } } } // flush sends all pending events to their respective channels. func (w *Watcher) flush() { // refreshEvents are stored newest first. for i := len(w.syncEvents) - 1; i >= 0; i-- { e := &w.syncEvents[i] for e.ch != nil { select { case <-w.tomb.Dying(): return case req := <-w.request: w.handle(req) continue case e.ch <- Change{e.key.c, e.key.id, e.revno}: } break } } // requestEvents are stored oldest first, and // may grow during the loop. for i := 0; i < len(w.requestEvents); i++ { e := &w.requestEvents[i] for e.ch != nil { select { case <-w.tomb.Dying(): return case req := <-w.request: w.handle(req) continue case e.ch <- Change{e.key.c, e.key.id, e.revno}: } break } } w.syncEvents = w.syncEvents[:0] w.requestEvents = w.requestEvents[:0] } // handle deals with requests delivered by the public API // onto the background watcher goroutine. func (w *Watcher) handle(req interface{}) { debugf("state/watcher: got request: %#v", req) switch r := req.(type) { case reqSync: w.needSync = true case reqWatch: for _, info := range w.watches[r.key] { if info.ch == r.info.ch { panic(fmt.Errorf("tried to re-add channel %v for %s", info.ch, r.key)) } } if revno, ok := w.current[r.key]; ok && (revno > r.info.revno || revno == -1 && r.info.revno >= 0) { r.info.revno = revno w.requestEvents = append(w.requestEvents, event{r.info.ch, r.key, revno}) } w.watches[r.key] = append(w.watches[r.key], r.info) case reqUnwatch: watches := w.watches[r.key] removed := false for i, info := range watches { if info.ch == r.ch { watches[i] = watches[len(watches)-1] w.watches[r.key] = watches[:len(watches)-1] removed = true break } } if !removed { panic(fmt.Errorf("tried to remove missing channel %v for %s", r.ch, r.key)) } for i := range w.requestEvents { e := &w.requestEvents[i] if r.key.match(e.key) && e.ch == r.ch { e.ch = nil } } for i := range w.syncEvents { e := &w.syncEvents[i] if r.key.match(e.key) && e.ch == r.ch { e.ch = nil } } default: panic(fmt.Errorf("unknown request: %T", req)) } } type logInfo struct { Docs []interface{} `bson:"d"` Revnos []int64 `bson:"r"` } // initLastId reads the most recent changelog document and initializes // lastId with it. This causes all history that precedes the creation // of the watcher to be ignored. func (w *Watcher) initLastId() error { var entry struct { Id interface{} "_id" } err := w.log.Find(nil).Sort("-$natural").One(&entry) if err != nil && err != mgo.ErrNotFound { return err } w.lastId = entry.Id return nil } // sync updates the watcher knowledge from the database, and // queues events to observing channels. func (w *Watcher) sync() error { w.needSync = false // Iterate through log events in reverse insertion order (newest first). iter := w.log.Find(nil).Batch(10).Sort("-$natural").Iter() seen := make(map[watchKey]bool) first := true lastId := w.lastId var entry bson.D for iter.Next(&entry) { if len(entry) == 0 { debugf("state/watcher: got empty changelog document") } id := entry[0] if id.Name != "_id" { panic("watcher: _id field isn't first entry") } if first { w.lastId = id.Value first = false } if id.Value == lastId { break } debugf("state/watcher: got changelog document: %#v", entry) for _, c := range entry[1:] { // See txn's Runner.ChangeLog for the structure of log entries. var d, r []interface{} dr, _ := c.Value.(bson.D) for _, item := range dr { switch item.Name { case "d": d, _ = item.Value.([]interface{}) case "r": r, _ = item.Value.([]interface{}) } } if len(d) == 0 || len(d) != len(r) { log.Warningf("state/watcher: changelog has invalid collection document: %#v", c) continue } for i := len(d) - 1; i >= 0; i-- { key := watchKey{c.Name, d[i]} if seen[key] { continue } seen[key] = true revno, ok := r[i].(int64) if !ok { log.Warningf("state/watcher: changelog has revno with type %T: %#v", r[i], r[i]) continue } if revno < 0 { revno = -1 } if w.current[key] == revno { continue } w.current[key] = revno // Queue notifications for per-collection watches. for _, info := range w.watches[watchKey{c.Name, nil}] { if info.filter != nil && !info.filter(d[i]) { continue } w.syncEvents = append(w.syncEvents, event{info.ch, key, revno}) } // Queue notifications for per-document watches. infos := w.watches[key] for i, info := range infos { if revno > info.revno || revno < 0 && info.revno >= 0 { infos[i].revno = revno w.syncEvents = append(w.syncEvents, event{info.ch, key, revno}) } } } } } if iter.Err() != nil { return fmt.Errorf("watcher iteration error: %v", iter.Err()) } return nil } func debugf(f string, a ...interface{}) { if Debug { log.Debugf(f, a...) } } �����������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/policy.go����������������������������������������0000644�0000153�0000161�00000006037�12321735776�023651� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" ) // Policy is an interface provided to State that may // be consulted by State to validate or modify the // behaviour of certain operations. // // If a Policy implementation does not implement one // of the methods, it must return an error that // satisfies errors.IsNotImplemented, and will thus // be ignored. Any other error will cause an error // in the use of the policy. type Policy interface { // Prechecker takes a *config.Config and returns // a (possibly nil) Prechecker or an error. Prechecker(*config.Config) (Prechecker, error) // ConfigValidator takes a string (environ type) and returns // a (possibly nil) ConfigValidator or an error. ConfigValidator(string) (ConfigValidator, error) } // Prechecker is a policy interface that is provided to State // to perform pre-flight checking of instance creation. type Prechecker interface { // PrecheckInstance performs a preflight check on the specified // series and constraints, ensuring that they are possibly valid for // creating an instance in this environment. // // PrecheckInstance is best effort, and not guaranteed to eliminate // all invalid parameters. If PrecheckInstance returns nil, it is not // guaranteed that the constraints are valid; if a non-nil error is // returned, then the constraints are definitely invalid. PrecheckInstance(series string, cons constraints.Value) error } // ConfigValidator is a policy interface that is provided to State // to check validity of new configuration attributes before applying them to state. type ConfigValidator interface { Validate(cfg, old *config.Config) (valid *config.Config, err error) } // precheckInstance calls the state's assigned policy, if non-nil, to obtain // a Prechecker, and calls PrecheckInstance if a non-nil Prechecker is returned. func (st *State) precheckInstance(series string, cons constraints.Value) error { if st.policy == nil { return nil } cfg, err := st.EnvironConfig() if err != nil { return err } prechecker, err := st.policy.Prechecker(cfg) if errors.IsNotImplementedError(err) { return nil } else if err != nil { return err } if prechecker == nil { return fmt.Errorf("policy returned nil prechecker without an error") } return prechecker.PrecheckInstance(series, cons) } // validate calls the state's assigned policy, if non-nil, to obtain // a Validator, and calls validate if a non-nil Validator is returned. func (st *State) validate(cfg, old *config.Config) (valid *config.Config, err error) { if st.policy == nil { return cfg, nil } configValidator, err := st.policy.ConfigValidator(cfg.Type()) if errors.IsNotImplementedError(err) { return cfg, nil } else if err != nil { return nil, err } if configValidator == nil { return nil, fmt.Errorf("policy returned nil configValidator without an error") } return configValidator.Validate(cfg, old) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/relationunit.go����������������������������������0000644�0000153�0000161�00000032144�12321735642�025055� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( stderrors "errors" "fmt" "strings" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/names" "launchpad.net/juju-core/utils" ) // RelationUnit holds information about a single unit in a relation, and // allows clients to conveniently access unit-specific functionality. type RelationUnit struct { st *State relation *Relation unit *Unit endpoint Endpoint scope string } // Relation returns the relation associated with the unit. func (ru *RelationUnit) Relation() *Relation { return ru.relation } // Endpoint returns the relation endpoint that defines the unit's // participation in the relation. func (ru *RelationUnit) Endpoint() Endpoint { return ru.endpoint } // PrivateAddress returns the private address of the unit and whether it is valid. func (ru *RelationUnit) PrivateAddress() (string, bool) { return ru.unit.PrivateAddress() } // ErrCannotEnterScope indicates that a relation unit failed to enter its scope // due to either the unit or the relation not being Alive. var ErrCannotEnterScope = stderrors.New("cannot enter scope: unit or relation is not alive") // ErrCannotEnterScopeYet indicates that a relation unit failed to enter its // scope due to a required and pre-existing subordinate unit that is not Alive. // Once that subordinate has been removed, a new one can be created. var ErrCannotEnterScopeYet = stderrors.New("cannot enter scope yet: non-alive subordinate unit has not been removed") // EnterScope ensures that the unit has entered its scope in the relation. // When the unit has already entered its relation scope, EnterScope will report // success but make no changes to state. // // Otherwise, assuming both the relation and the unit are alive, it will enter // scope and create or overwrite the unit's settings in the relation according // to the supplied map. // // If the unit is a principal and the relation has container scope, EnterScope // will also create the required subordinate unit, if it does not already exist; // this is because there's no point having a principal in scope if there is no // corresponding subordinate to join it. // // Once a unit has entered a scope, it stays in scope without further // intervention; the relation will not be able to become Dead until all units // have departed its scopes. func (ru *RelationUnit) EnterScope(settings map[string]interface{}) error { // Verify that the unit is not already in scope, and abort without error // if it is. ruKey, err := ru.key(ru.unit.Name()) if err != nil { return err } if count, err := ru.st.relationScopes.FindId(ruKey).Count(); err != nil { return err } else if count != 0 { return nil } // Collect the operations necessary to enter scope, as follows: // * Check unit and relation state, and incref the relation. // * TODO(fwereade): check unit status == params.StatusStarted (this // breaks a bunch of tests in a boring but noisy-to-fix way, and is // being saved for a followup). unitName, relationKey := ru.unit.doc.Name, ru.relation.doc.Key ops := []txn.Op{{ C: ru.st.units.Name, Id: unitName, Assert: isAliveDoc, }, { C: ru.st.relations.Name, Id: relationKey, Assert: isAliveDoc, Update: bson.D{{"$inc", bson.D{{"unitcount", 1}}}}, }} // * Create the unit settings in this relation, if they do not already // exist; or completely overwrite them if they do. This must happen // before we create the scope doc, because the existence of a scope doc // is considered to be a guarantee of the existence of a settings doc. settingsChanged := func() (bool, error) { return false, nil } if count, err := ru.st.settings.FindId(ruKey).Count(); err != nil { return err } else if count == 0 { ops = append(ops, createSettingsOp(ru.st, ruKey, settings)) } else { var rop txn.Op rop, settingsChanged, err = replaceSettingsOp(ru.st, ruKey, settings) if err != nil { return err } ops = append(ops, rop) } // * Create the scope doc. ops = append(ops, txn.Op{ C: ru.st.relationScopes.Name, Id: ruKey, Assert: txn.DocMissing, Insert: relationScopeDoc{ruKey}, }) // * If the unit should have a subordinate, and does not, create it. var existingSubName string if subOps, subName, err := ru.subordinateOps(); err != nil { return err } else { existingSubName = subName ops = append(ops, subOps...) } // Now run the complete transaction, or figure out why we can't. if err := ru.st.runTransaction(ops); err != txn.ErrAborted { return err } if count, err := ru.st.relationScopes.FindId(ruKey).Count(); err != nil { return err } else if count != 0 { // The scope document exists, so we're actually already in scope. return nil } // The relation or unit might no longer be Alive. (Note that there is no // need for additional checks if we're trying to create a subordinate // unit: this could fail due to the subordinate service's not being Alive, // but this case will always be caught by the check for the relation's // life (because a relation cannot be Alive if its services are not).) if alive, err := isAlive(ru.st.units, unitName); err != nil { return err } else if !alive { return ErrCannotEnterScope } if alive, err := isAlive(ru.st.relations, relationKey); err != nil { return err } else if !alive { return ErrCannotEnterScope } // Maybe a subordinate used to exist, but is no longer alive. If that is // case, we will be unable to enter scope until that unit is gone. if existingSubName != "" { if alive, err := isAlive(ru.st.units, existingSubName); err != nil { return err } else if !alive { return ErrCannotEnterScopeYet } } // It's possible that there was a pre-existing settings doc whose version // has changed under our feet, preventing us from clearing it properly; if // that is the case, something is seriously wrong (nobody else should be // touching that doc under our feet) and we should bail out. prefix := fmt.Sprintf("cannot enter scope for unit %q in relation %q: ", ru.unit, ru.relation) if changed, err := settingsChanged(); err != nil { return err } else if changed { return fmt.Errorf(prefix + "concurrent settings change detected") } // Apparently, all our assertions should have passed, but the txn was // aborted: something is really seriously wrong. return fmt.Errorf(prefix + "inconsistent state in EnterScope") } // subordinateOps returns any txn operations necessary to ensure sane // subordinate state when entering scope. If a required subordinate unit // exists and is Alive, its name will be returned as well; if one exists // but is not Alive, ErrCannotEnterScopeYet is returned. func (ru *RelationUnit) subordinateOps() ([]txn.Op, string, error) { if !ru.unit.IsPrincipal() || ru.endpoint.Scope != charm.ScopeContainer { return nil, "", nil } related, err := ru.relation.RelatedEndpoints(ru.endpoint.ServiceName) if err != nil { return nil, "", err } if len(related) != 1 { return nil, "", fmt.Errorf("expected single related endpoint, got %v", related) } serviceName, unitName := related[0].ServiceName, ru.unit.doc.Name selSubordinate := bson.D{{"service", serviceName}, {"principal", unitName}} var lDoc lifeDoc if err := ru.st.units.Find(selSubordinate).One(&lDoc); err == mgo.ErrNotFound { service, err := ru.st.Service(serviceName) if err != nil { return nil, "", err } _, ops, err := service.addUnitOps(unitName, nil) return ops, "", err } else if err != nil { return nil, "", err } else if lDoc.Life != Alive { return nil, "", ErrCannotEnterScopeYet } return []txn.Op{{ C: ru.st.units.Name, Id: lDoc.Id, Assert: isAliveDoc, }}, lDoc.Id, nil } // LeaveScope signals that the unit has left its scope in the relation. // After the unit has left its relation scope, it is no longer a member // of the relation; if the relation is dying when its last member unit // leaves, it is removed immediately. It is not an error to leave a scope // that the unit is not, or never was, a member of. func (ru *RelationUnit) LeaveScope() error { key, err := ru.key(ru.unit.Name()) if err != nil { return err } // The logic below is involved because we remove a dying relation // with the last unit that leaves a scope in it. It handles three // possible cases: // // 1. Relation is alive: just leave the scope. // // 2. Relation is dying, and other units remain: just leave the scope. // // 3. Relation is dying, and this is the last unit: leave the scope // and remove the relation. // // In each of those cases, proper assertions are done to guarantee // that the condition observed is still valid when the transaction is // applied. If an abort happens, it observes the new condition and // retries. In theory, a worst case will try at most all of the // conditions once, because units cannot join a scope once its relation // is dying. // // Keep in mind that in the first iteration of the loop it's possible // to have a Dying relation with a smaller-than-real unit count, because // Destroy changes the Life attribute in memory (units could join before // the database is actually changed). desc := fmt.Sprintf("unit %q in relation %q", ru.unit, ru.relation) for attempt := 0; attempt < 3; attempt++ { count, err := ru.st.relationScopes.FindId(key).Count() if err != nil { return fmt.Errorf("cannot examine scope for %s: %v", desc, err) } else if count == 0 { return nil } ops := []txn.Op{{ C: ru.st.relationScopes.Name, Id: key, Assert: txn.DocExists, Remove: true, }} if ru.relation.doc.Life == Alive { ops = append(ops, txn.Op{ C: ru.st.relations.Name, Id: ru.relation.doc.Key, Assert: bson.D{{"life", Alive}}, Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}}, }) } else if ru.relation.doc.UnitCount > 1 { ops = append(ops, txn.Op{ C: ru.st.relations.Name, Id: ru.relation.doc.Key, Assert: bson.D{{"unitcount", bson.D{{"$gt", 1}}}}, Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}}, }) } else { relOps, err := ru.relation.removeOps("", ru.unit) if err != nil { return err } ops = append(ops, relOps...) } if err = ru.st.runTransaction(ops); err != txn.ErrAborted { if err != nil { return fmt.Errorf("cannot leave scope for %s: %v", desc, err) } return err } if err := ru.relation.Refresh(); errors.IsNotFoundError(err) { return nil } else if err != nil { return err } } return fmt.Errorf("cannot leave scope for %s: inconsistent state", desc) } // InScope returns whether the relation unit has entered scope or not. func (ru *RelationUnit) InScope() (bool, error) { key, err := ru.key(ru.unit.Name()) if err != nil { return false, err } count, err := ru.st.relationScopes.FindId(key).Count() if err != nil { return false, err } return count > 0, nil } // WatchScope returns a watcher which notifies of counterpart units // entering and leaving the unit's scope. func (ru *RelationUnit) WatchScope() *RelationScopeWatcher { role := counterpartRole(ru.endpoint.Role) scope := ru.scope + "#" + string(role) return newRelationScopeWatcher(ru.st, scope, ru.unit.Name()) } // Settings returns a Settings which allows access to the unit's settings // within the relation. func (ru *RelationUnit) Settings() (*Settings, error) { key, err := ru.key(ru.unit.Name()) if err != nil { return nil, err } return readSettings(ru.st, key) } // ReadSettings returns a map holding the settings of the unit with the // supplied name within this relation. An error will be returned if the // relation no longer exists, or if the unit's service is not part of the // relation, or the settings are invalid; but mere non-existence of the // unit is not grounds for an error, because the unit settings are // guaranteed to persist for the lifetime of the relation, regardless // of the lifetime of the unit. func (ru *RelationUnit) ReadSettings(uname string) (m map[string]interface{}, err error) { defer utils.ErrorContextf(&err, "cannot read settings for unit %q in relation %q", uname, ru.relation) if !names.IsUnit(uname) { return nil, fmt.Errorf("%q is not a valid unit name", uname) } key, err := ru.key(uname) if err != nil { return nil, err } node, err := readSettings(ru.st, key) if err != nil { return nil, err } return node.Map(), nil } // key returns a string, based on the relation and the supplied unit name, // which is used as a key for that unit within this relation in the settings, // presence, and relationScopes collections. func (ru *RelationUnit) key(uname string) (string, error) { uparts := strings.Split(uname, "/") sname := uparts[0] ep, err := ru.relation.Endpoint(sname) if err != nil { return "", err } parts := []string{ru.scope, string(ep.Role), uname} return strings.Join(parts, "#"), nil } // relationScopeDoc represents a unit which is in a relation scope. // The relation, container, role, and unit are all encoded in the key. type relationScopeDoc struct { Key string `bson:"_id"` } func (d *relationScopeDoc) unitName() string { parts := strings.Split(d.Key, "#") return parts[len(parts)-1] } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/address_test.go����������������������������������0000644�0000153�0000161�00000001502�12321735642�025016� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" ) type AddressSuite struct{} var _ = gc.Suite(&AddressSuite{}) func (s *AddressSuite) TestNewAddress(c *gc.C) { instanceaddress := instance.Address{"0.0.0.0", instance.Ipv4Address, "net", instance.NetworkUnknown} stateaddress := state.NewAddress(instanceaddress) c.Assert(stateaddress, gc.NotNil) } func (s *AddressSuite) TestInstanceAddressRoundtrips(c *gc.C) { instanceaddress := instance.Address{"0.0.0.0", instance.Ipv4Address, "net", instance.NetworkUnknown} stateaddress := state.NewAddress(instanceaddress) addr := stateaddress.InstanceAddress() c.Assert(addr, gc.Equals, instanceaddress) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/configvalidator_test.go��������������������������0000644�0000153�0000161�00000006410�12321735642�026547� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" ) type ConfigValidatorSuite struct { ConnSuite configValidator mockConfigValidator } var _ = gc.Suite(&ConfigValidatorSuite{}) type mockConfigValidator struct { validateError error validateCfg *config.Config validateOld *config.Config validateValid *config.Config } // To test UpdateEnvironConfig updates state, Validate returns a config // different to both input configs func mockValidCfg() (valid *config.Config, err error) { cfg, err := config.New(config.UseDefaults, coretesting.FakeConfig()) if err != nil { return nil, err } valid, err = cfg.Apply(map[string]interface{}{ "arbitrary-key": "cptn-marvel", }) if err != nil { return nil, err } return valid, nil } func (p *mockConfigValidator) Validate(cfg, old *config.Config) (valid *config.Config, err error) { p.validateCfg = cfg p.validateOld = old p.validateValid, p.validateError = mockValidCfg() return p.validateValid, p.validateError } func (s *ConfigValidatorSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) s.configValidator = mockConfigValidator{} s.policy.getConfigValidator = func(string) (state.ConfigValidator, error) { return &s.configValidator, nil } } func (s *ConfigValidatorSuite) updateEnvironConfig(c *gc.C) error { updateAttrs := map[string]interface{}{ "authorized-keys": "different-keys", "arbitrary-key": "shazam!", } return s.State.UpdateEnvironConfig(updateAttrs, nil, nil) } func (s *ConfigValidatorSuite) TestConfigValidate(c *gc.C) { err := s.updateEnvironConfig(c) c.Assert(err, gc.IsNil) } func (s *ConfigValidatorSuite) TestUpdateEnvironConfigFailsOnConfigValidateError(c *gc.C) { var configValidatorErr error s.policy.getConfigValidator = func(string) (state.ConfigValidator, error) { configValidatorErr = errors.NotFoundf("") return &s.configValidator, configValidatorErr } err := s.updateEnvironConfig(c) c.Assert(err, gc.ErrorMatches, " not found") } func (s *ConfigValidatorSuite) TestUpdateEnvironConfigUpdatesState(c *gc.C) { s.updateEnvironConfig(c) stateCfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) newValidCfg, err := mockValidCfg() c.Assert(err, gc.IsNil) c.Assert(stateCfg.AllAttrs()["arbitrary-key"], gc.Equals, newValidCfg.AllAttrs()["arbitrary-key"]) } func (s *ConfigValidatorSuite) TestConfigValidateUnimplemented(c *gc.C) { var configValidatorErr error s.policy.getConfigValidator = func(string) (state.ConfigValidator, error) { return nil, configValidatorErr } err := s.updateEnvironConfig(c) c.Assert(err, gc.ErrorMatches, "policy returned nil configValidator without an error") configValidatorErr = errors.NewNotImplementedError("Validator") err = s.updateEnvironConfig(c) c.Assert(err, gc.IsNil) } func (s *ConfigValidatorSuite) TestConfigValidateNoPolicy(c *gc.C) { s.policy.getConfigValidator = func(providerType string) (state.ConfigValidator, error) { c.Errorf("should not have been invoked") return nil, nil } state.SetPolicy(s.State, nil) err := s.updateEnvironConfig(c) c.Assert(err, gc.IsNil) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/networks.go��������������������������������������0000644�0000153�0000161�00000003104�12321735776�024216� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "labix.org/v2/mgo" "labix.org/v2/mgo/txn" ) // networksDoc represents the network restrictions for a service or machine. // The document ID field is the globalKey of a service or a machine. type networksDoc struct { IncludeNetworks []string `bson:"include"` ExcludeNetworks []string `bson:"exclude"` } func newNetworksDoc(includeNetworks, excludeNetworks []string) *networksDoc { return &networksDoc{ IncludeNetworks: includeNetworks, ExcludeNetworks: excludeNetworks, } } func createNetworksOp(st *State, id string, includeNetworks, excludeNetworks []string) txn.Op { return txn.Op{ C: st.networks.Name, Id: id, Assert: txn.DocMissing, Insert: newNetworksDoc(includeNetworks, excludeNetworks), } } // While networks are immutable, there is no setNetworksOp function. func removeNetworksOp(st *State, id string) txn.Op { return txn.Op{ C: st.networks.Name, Id: id, Remove: true, } } func readNetworks(st *State, id string) (includeNetworks, excludeNetworks []string, err error) { doc := networksDoc{} if err = st.networks.FindId(id).One(&doc); err == mgo.ErrNotFound { // In 1.17.7+ we always create a networksDoc for each service or // machine we create, but in legacy databases this is not the // case. We ignore the error here for backwards-compatibility. err = nil } else if err == nil { includeNetworks = doc.IncludeNetworks excludeNetworks = doc.ExcludeNetworks } return includeNetworks, excludeNetworks, err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/cleanup_test.go����������������������������������0000644�0000153�0000161�00000017002�12321735642�025022� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package state_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" ) type CleanupSuite struct { ConnSuite } var _ = gc.Suite(&CleanupSuite{}) func (s *CleanupSuite) TestCleanupDyingServiceUnits(c *gc.C) { s.assertDoesNotNeedCleanup(c) // Create a service with some units. mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) units := make([]*state.Unit, 3) for i := range units { unit, err := mysql.AddUnit() c.Assert(err, gc.IsNil) units[i] = unit } preventUnitDestroyRemove(c, units[0]) s.assertDoesNotNeedCleanup(c) // Destroy the service and check the units are unaffected, but a cleanup // has been scheduled. err := mysql.Destroy() c.Assert(err, gc.IsNil) for _, unit := range units { err := unit.Refresh() c.Assert(err, gc.IsNil) } s.assertNeedsCleanup(c) // Run the cleanup, and check that units are all destroyed as appropriate. s.assertCleanupRuns(c) s.assertDoesNotNeedCleanup(c) err = units[0].Refresh() c.Assert(err, gc.IsNil) c.Assert(units[0].Life(), gc.Equals, state.Dying) err = units[1].Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = units[2].Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *CleanupSuite) TestCleanupEnvironmentServices(c *gc.C) { s.assertDoesNotNeedCleanup(c) // Create a service with some units. mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) units := make([]*state.Unit, 3) for i := range units { unit, err := mysql.AddUnit() c.Assert(err, gc.IsNil) units[i] = unit } s.assertDoesNotNeedCleanup(c) // Destroy the environment and check the service and units are // unaffected, but a cleanup for the service has been scheduled. env, err := s.State.Environment() c.Assert(err, gc.IsNil) err = env.Destroy() c.Assert(err, gc.IsNil) s.assertNeedsCleanup(c) s.assertCleanupRuns(c) err = mysql.Refresh() c.Assert(err, gc.IsNil) c.Assert(mysql.Life(), gc.Equals, state.Dying) for _, unit := range units { err = unit.Refresh() c.Assert(err, gc.IsNil) c.Assert(unit.Life(), gc.Equals, state.Alive) } // The first cleanup Destroys the service, which // schedules another cleanup to destroy the units. s.assertNeedsCleanup(c) s.assertCleanupRuns(c) for _, unit := range units { err = unit.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } s.assertDoesNotNeedCleanup(c) } func (s *CleanupSuite) TestCleanupRelationSettings(c *gc.C) { s.assertDoesNotNeedCleanup(c) // Create a relation with a unit in scope. pr := NewPeerRelation(c, s.State) rel := pr.ru0.Relation() err := pr.ru0.EnterScope(map[string]interface{}{"some": "settings"}) c.Assert(err, gc.IsNil) s.assertDoesNotNeedCleanup(c) // Destroy the service, check the relation's still around. err = pr.svc.Destroy() c.Assert(err, gc.IsNil) s.assertNeedsCleanup(c) s.assertCleanupRuns(c) err = rel.Refresh() c.Assert(err, gc.IsNil) c.Assert(rel.Life(), gc.Equals, state.Dying) s.assertDoesNotNeedCleanup(c) // The unit leaves scope, triggering relation removal. err = pr.ru0.LeaveScope() c.Assert(err, gc.IsNil) s.assertNeedsCleanup(c) // Settings are not destroyed yet... settings, err := pr.ru1.ReadSettings("riak/0") c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, map[string]interface{}{"some": "settings"}) // ...but they are on cleanup. s.assertCleanupRuns(c) s.assertDoesNotNeedCleanup(c) _, err = pr.ru1.ReadSettings("riak/0") c.Assert(err, gc.ErrorMatches, `cannot read settings for unit "riak/0" in relation "riak:ring": settings not found`) } func (s *CleanupSuite) TestForceDestroyMachineErrors(c *gc.C) { manager, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) s.assertDoesNotNeedCleanup(c) err = manager.ForceDestroy() expect := fmt.Sprintf("machine %s is required by the environment", manager.Id()) c.Assert(err, gc.ErrorMatches, expect) s.assertDoesNotNeedCleanup(c) assertLife(c, manager, state.Alive) } func (s *CleanupSuite) TestCleanupForceDestroyedMachineUnit(c *gc.C) { s.assertDoesNotNeedCleanup(c) // Create a machine. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // Create a relation with a unit in scope and assigned to the machine. pr := NewPeerRelation(c, s.State) err = pr.u0.AssignToMachine(machine) c.Assert(err, gc.IsNil) err = pr.ru0.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertDoesNotNeedCleanup(c) // Force machine destruction, check cleanup queued. err = machine.ForceDestroy() c.Assert(err, gc.IsNil) s.assertNeedsCleanup(c) // Clean up, and check that the unit has been removed... s.assertCleanupRuns(c) s.assertDoesNotNeedCleanup(c) assertRemoved(c, pr.u0) // ...and the unit has departed relation scope... assertNotInScope(c, pr.ru0) // ...but that the machine remains, and is Dead, ready for removal by the // provisioner. assertLife(c, machine, state.Dead) } func (s *CleanupSuite) TestCleanupForceDestroyedMachineWithContainer(c *gc.C) { s.assertDoesNotNeedCleanup(c) // Create a machine with a container. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) container, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) // Create active units (in relation scope, with subordinates). prr := NewProReqRelation(c, &s.ConnSuite, charm.ScopeContainer) err = prr.pru0.EnterScope(nil) c.Assert(err, gc.IsNil) err = prr.pru1.EnterScope(nil) c.Assert(err, gc.IsNil) err = prr.rru0.EnterScope(nil) c.Assert(err, gc.IsNil) err = prr.rru1.EnterScope(nil) c.Assert(err, gc.IsNil) // Assign the various units to machines. err = prr.pu0.AssignToMachine(machine) c.Assert(err, gc.IsNil) err = prr.pu1.AssignToMachine(container) c.Assert(err, gc.IsNil) s.assertDoesNotNeedCleanup(c) // Force removal of the top-level machine. err = machine.ForceDestroy() c.Assert(err, gc.IsNil) s.assertNeedsCleanup(c) // And do it again, just to check that the second cleanup doc for the same // machine doesn't cause problems down the line. err = machine.ForceDestroy() c.Assert(err, gc.IsNil) s.assertNeedsCleanup(c) // Clean up, and check that the container has been removed... s.assertCleanupRuns(c) s.assertDoesNotNeedCleanup(c) err = container.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // ...and so have all the units... assertRemoved(c, prr.pu0) assertRemoved(c, prr.pu1) assertRemoved(c, prr.ru0) assertRemoved(c, prr.ru1) // ...and none of the units have left relation scopes occupied... assertNotInScope(c, prr.pru0) assertNotInScope(c, prr.pru1) assertNotInScope(c, prr.rru0) assertNotInScope(c, prr.rru1) // ...but that the machine remains, and is Dead, ready for removal by the // provisioner. assertLife(c, machine, state.Dead) } func (s *CleanupSuite) TestNothingToCleanup(c *gc.C) { s.assertDoesNotNeedCleanup(c) s.assertCleanupRuns(c) s.assertDoesNotNeedCleanup(c) } func (s *CleanupSuite) assertCleanupRuns(c *gc.C) { err := s.State.Cleanup() c.Assert(err, gc.IsNil) } func (s *CleanupSuite) assertNeedsCleanup(c *gc.C) { actual, err := s.State.NeedsCleanup() c.Assert(err, gc.IsNil) c.Assert(actual, jc.IsTrue) } func (s *CleanupSuite) assertDoesNotNeedCleanup(c *gc.C) { actual, err := s.State.NeedsCleanup() c.Assert(err, gc.IsNil) c.Assert(actual, jc.IsFalse) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/tools_test.go������������������������������������0000644�0000153�0000161�00000003724�12321735642�024541� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) type tooler interface { AgentTools() (*tools.Tools, error) SetAgentVersion(v version.Binary) error Life() state.Life Refresh() error Destroy() error EnsureDead() error } var _ = gc.Suite(&ToolsSuite{}) type ToolsSuite struct { ConnSuite } func newTools(vers, url string) *tools.Tools { return &tools.Tools{ Version: version.MustParseBinary(vers), URL: url, Size: 10, SHA256: "1234", } } func testAgentTools(c *gc.C, obj tooler, agent string) { // object starts with zero'd tools. t, err := obj.AgentTools() c.Assert(t, gc.IsNil) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = obj.SetAgentVersion(version.Binary{}) c.Assert(err, gc.ErrorMatches, fmt.Sprintf("cannot set agent version for %s: empty series or arch", agent)) v2 := version.MustParseBinary("7.8.9-foo-bar") err = obj.SetAgentVersion(v2) c.Assert(err, gc.IsNil) t3, err := obj.AgentTools() c.Assert(err, gc.IsNil) c.Assert(t3.Version, gc.DeepEquals, v2) err = obj.Refresh() c.Assert(err, gc.IsNil) t3, err = obj.AgentTools() c.Assert(err, gc.IsNil) c.Assert(t3.Version, gc.DeepEquals, v2) testWhenDying(c, obj, noErr, deadErr, func() error { return obj.SetAgentVersion(v2) }) } func (s *ToolsSuite) TestMachineAgentTools(c *gc.C) { m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) testAgentTools(c, m, "machine 0") } func (s *ToolsSuite) TestUnitAgentTools(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") svc := s.AddTestingService(c, "wordpress", charm) unit, err := svc.AddUnit() c.Assert(err, gc.IsNil) preventUnitDestroyRemove(c, unit) testAgentTools(c, unit, `unit "wordpress/0"`) } ��������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/megawatcher_internal_test.go���������������������0000644�0000153�0000161�00000102445�12321735776�027574� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "errors" "fmt" "reflect" "sort" "time" "labix.org/v2/mgo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/multiwatcher" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) var dottedConfig = ` options: key.dotted: {default: My Key, description: Desc, type: string} ` type storeManagerStateSuite struct { testbase.LoggingSuite testing.MgoSuite State *State } func (s *storeManagerStateSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *storeManagerStateSuite) TearDownSuite(c *gc.C) { s.MgoSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *storeManagerStateSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) s.State = TestingInitialize(c, nil, Policy(nil)) s.State.AddUser(AdminUser, "pass") } func (s *storeManagerStateSuite) TearDownTest(c *gc.C) { s.State.Close() s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *storeManagerStateSuite) Reset(c *gc.C) { s.TearDownTest(c) s.SetUpTest(c) } var _ = gc.Suite(&storeManagerStateSuite{}) // setUpScenario adds some entities to the state so that // we can check that they all get pulled in by // allWatcherStateBacking.getAll. func (s *storeManagerStateSuite) setUpScenario(c *gc.C) (entities entityInfoSlice) { add := func(e params.EntityInfo) { entities = append(entities, e) } m, err := s.State.AddMachine("quantal", JobManageEnviron) c.Assert(err, gc.IsNil) c.Assert(m.Tag(), gc.Equals, "machine-0") err = m.SetProvisioned(instance.Id("i-"+m.Tag()), "fake_nonce", nil) c.Assert(err, gc.IsNil) hc, err := m.HardwareCharacteristics() c.Assert(err, gc.IsNil) addresses := instance.NewAddresses([]string{"example.com"}) err = m.SetAddresses(addresses) c.Assert(err, gc.IsNil) add(&params.MachineInfo{ Id: "0", InstanceId: "i-machine-0", Status: params.StatusPending, Life: params.Alive, Series: "quantal", Jobs: []params.MachineJob{JobManageEnviron.ToParams()}, Addresses: m.Addresses(), HardwareCharacteristics: hc, }) wordpress := AddTestingService(c, s.State, "wordpress", AddTestingCharm(c, s.State, "wordpress")) err = wordpress.SetExposed() c.Assert(err, gc.IsNil) err = wordpress.SetMinUnits(3) c.Assert(err, gc.IsNil) err = wordpress.SetConstraints(constraints.MustParse("mem=100M")) c.Assert(err, gc.IsNil) setServiceConfigAttr(c, wordpress, "blog-title", "boring") add(&params.ServiceInfo{ Name: "wordpress", Exposed: true, CharmURL: serviceCharmURL(wordpress).String(), OwnerTag: "user-admin", Life: params.Alive, MinUnits: 3, Constraints: constraints.MustParse("mem=100M"), Config: charm.Settings{"blog-title": "boring"}, }) pairs := map[string]string{"x": "12", "y": "99"} err = wordpress.SetAnnotations(pairs) c.Assert(err, gc.IsNil) add(&params.AnnotationInfo{ Tag: "service-wordpress", Annotations: pairs, }) logging := AddTestingService(c, s.State, "logging", AddTestingCharm(c, s.State, "logging")) add(&params.ServiceInfo{ Name: "logging", CharmURL: serviceCharmURL(logging).String(), OwnerTag: "user-admin", Life: params.Alive, Config: charm.Settings{}, }) eps, err := s.State.InferEndpoints([]string{"logging", "wordpress"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) add(&params.RelationInfo{ Key: "logging:logging-directory wordpress:logging-dir", Id: rel.Id(), Endpoints: []params.Endpoint{ {ServiceName: "logging", Relation: charm.Relation{Name: "logging-directory", Role: "requirer", Interface: "logging", Optional: false, Limit: 1, Scope: "container"}}, {ServiceName: "wordpress", Relation: charm.Relation{Name: "logging-dir", Role: "provider", Interface: "logging", Optional: false, Limit: 0, Scope: "container"}}}, }) for i := 0; i < 2; i++ { wu, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) c.Assert(wu.Tag(), gc.Equals, fmt.Sprintf("unit-wordpress-%d", i)) m, err := s.State.AddMachine("quantal", JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(m.Tag(), gc.Equals, fmt.Sprintf("machine-%d", i+1)) add(&params.UnitInfo{ Name: fmt.Sprintf("wordpress/%d", i), Service: wordpress.Name(), Series: m.Series(), MachineId: m.Id(), Ports: []instance.Port{}, Status: params.StatusPending, }) pairs := map[string]string{"name": fmt.Sprintf("bar %d", i)} err = wu.SetAnnotations(pairs) c.Assert(err, gc.IsNil) add(&params.AnnotationInfo{ Tag: fmt.Sprintf("unit-wordpress-%d", i), Annotations: pairs, }) err = m.SetProvisioned(instance.Id("i-"+m.Tag()), "fake_nonce", nil) c.Assert(err, gc.IsNil) err = m.SetStatus(params.StatusError, m.Tag(), nil) c.Assert(err, gc.IsNil) hc, err := m.HardwareCharacteristics() c.Assert(err, gc.IsNil) add(&params.MachineInfo{ Id: fmt.Sprint(i + 1), InstanceId: "i-" + m.Tag(), Status: params.StatusError, StatusInfo: m.Tag(), Life: params.Alive, Series: "quantal", Jobs: []params.MachineJob{JobHostUnits.ToParams()}, Addresses: []instance.Address{}, HardwareCharacteristics: hc, }) err = wu.AssignToMachine(m) c.Assert(err, gc.IsNil) deployer, ok := wu.DeployerTag() c.Assert(ok, gc.Equals, true) c.Assert(deployer, gc.Equals, fmt.Sprintf("machine-%d", i+1)) wru, err := rel.Unit(wu) c.Assert(err, gc.IsNil) // Create the subordinate unit as a side-effect of entering // scope in the principal's relation-unit. err = wru.EnterScope(nil) c.Assert(err, gc.IsNil) lu, err := s.State.Unit(fmt.Sprintf("logging/%d", i)) c.Assert(err, gc.IsNil) c.Assert(lu.IsPrincipal(), gc.Equals, false) deployer, ok = lu.DeployerTag() c.Assert(ok, gc.Equals, true) c.Assert(deployer, gc.Equals, fmt.Sprintf("unit-wordpress-%d", i)) add(&params.UnitInfo{ Name: fmt.Sprintf("logging/%d", i), Service: "logging", Series: "quantal", Ports: []instance.Port{}, Status: params.StatusPending, }) } return } func serviceCharmURL(svc *Service) *charm.URL { url, _ := svc.CharmURL() return url } func assertEntitiesEqual(c *gc.C, got, want []params.EntityInfo) { if len(got) == 0 { got = nil } if len(want) == 0 { want = nil } if reflect.DeepEqual(got, want) { return } c.Errorf("entity mismatch; got len %d; want %d", len(got), len(want)) c.Logf("got:") for _, e := range got { c.Logf("\t%T %#v", e, e) } c.Logf("expected:") for _, e := range want { c.Logf("\t%T %#v", e, e) } c.FailNow() } func (s *storeManagerStateSuite) TestStateBackingGetAll(c *gc.C) { expectEntities := s.setUpScenario(c) b := newAllWatcherStateBacking(s.State) all := multiwatcher.NewStore() err := b.GetAll(all) c.Assert(err, gc.IsNil) var gotEntities entityInfoSlice = all.All() sort.Sort(gotEntities) sort.Sort(expectEntities) assertEntitiesEqual(c, gotEntities, expectEntities) } var allWatcherChangedTests = []struct { about string add []params.EntityInfo setUp func(c *gc.C, st *State) change watcher.Change expectContents []params.EntityInfo }{ // Machine changes { about: "no machine in state, no machine in store -> do nothing", setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "machines", Id: "1", }, }, { about: "machine is removed if it's not in backing", add: []params.EntityInfo{&params.MachineInfo{Id: "1"}}, setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "machines", Id: "1", }, }, { about: "machine is added if it's in backing but not in Store", setUp: func(c *gc.C, st *State) { m, err := st.AddMachine("quantal", JobHostUnits) c.Assert(err, gc.IsNil) err = m.SetStatus(params.StatusError, "failure", nil) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "machines", Id: "0", }, expectContents: []params.EntityInfo{ &params.MachineInfo{ Id: "0", Status: params.StatusError, StatusInfo: "failure", Life: params.Alive, Series: "quantal", Jobs: []params.MachineJob{JobHostUnits.ToParams()}, Addresses: []instance.Address{}, }, }, }, // Machine status changes { about: "machine is updated if it's in backing and in Store", add: []params.EntityInfo{ &params.MachineInfo{ Id: "0", Status: params.StatusError, StatusInfo: "another failure", }, }, setUp: func(c *gc.C, st *State) { m, err := st.AddMachine("trusty", JobManageEnviron) c.Assert(err, gc.IsNil) err = m.SetProvisioned("i-0", "bootstrap_nonce", nil) c.Assert(err, gc.IsNil) err = m.SetSupportedContainers([]instance.ContainerType{instance.LXC}) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "machines", Id: "0", }, expectContents: []params.EntityInfo{ &params.MachineInfo{ Id: "0", InstanceId: "i-0", Status: params.StatusError, StatusInfo: "another failure", Life: params.Alive, Series: "trusty", Jobs: []params.MachineJob{JobManageEnviron.ToParams()}, Addresses: []instance.Address{}, HardwareCharacteristics: &instance.HardwareCharacteristics{}, SupportedContainers: []instance.ContainerType{instance.LXC}, SupportedContainersKnown: true, }, }, }, // Unit changes { about: "no unit in state, no unit in store -> do nothing", setUp: func(c *gc.C, st *State) {}, change: watcher.Change{ C: "units", Id: "1", }, }, { about: "unit is removed if it's not in backing", add: []params.EntityInfo{&params.UnitInfo{Name: "wordpress/1"}}, setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "units", Id: "wordpress/1", }, }, { about: "unit is added if it's in backing but not in Store", setUp: func(c *gc.C, st *State) { wordpress := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) u, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) err = u.SetPublicAddress("public") c.Assert(err, gc.IsNil) err = u.SetPrivateAddress("private") c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 12345) c.Assert(err, gc.IsNil) m, err := st.AddMachine("quantal", JobHostUnits) c.Assert(err, gc.IsNil) err = u.AssignToMachine(m) c.Assert(err, gc.IsNil) err = u.SetStatus(params.StatusError, "failure", nil) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "units", Id: "wordpress/0", }, expectContents: []params.EntityInfo{ &params.UnitInfo{ Name: "wordpress/0", Service: "wordpress", Series: "quantal", PublicAddress: "public", PrivateAddress: "private", MachineId: "0", Ports: []instance.Port{{"tcp", 12345}}, Status: params.StatusError, StatusInfo: "failure", }, }, }, { about: "unit is updated if it's in backing and in multiwatcher.Store", add: []params.EntityInfo{&params.UnitInfo{ Name: "wordpress/0", Status: params.StatusError, StatusInfo: "another failure", }}, setUp: func(c *gc.C, st *State) { wordpress := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) u, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) err = u.SetPublicAddress("public") c.Assert(err, gc.IsNil) err = u.OpenPort("udp", 17070) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "units", Id: "wordpress/0", }, expectContents: []params.EntityInfo{ &params.UnitInfo{ Name: "wordpress/0", Service: "wordpress", Series: "quantal", PublicAddress: "public", Ports: []instance.Port{{"udp", 17070}}, Status: params.StatusError, StatusInfo: "another failure", }, }, }, { about: "unit addresses are read from the assigned machine for recent Juju releases", setUp: func(c *gc.C, st *State) { wordpress := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) u, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) err = u.OpenPort("tcp", 12345) c.Assert(err, gc.IsNil) m, err := st.AddMachine("quantal", JobHostUnits) c.Assert(err, gc.IsNil) err = u.AssignToMachine(m) c.Assert(err, gc.IsNil) publicAddress := instance.NewAddress("public") publicAddress.NetworkScope = instance.NetworkPublic privateAddress := instance.NewAddress("private") privateAddress.NetworkScope = instance.NetworkCloudLocal err = m.SetAddresses([]instance.Address{publicAddress, privateAddress}) c.Assert(err, gc.IsNil) err = u.SetStatus(params.StatusError, "failure", nil) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "units", Id: "wordpress/0", }, expectContents: []params.EntityInfo{ &params.UnitInfo{ Name: "wordpress/0", Service: "wordpress", Series: "quantal", PublicAddress: "public", PrivateAddress: "private", MachineId: "0", Ports: []instance.Port{{"tcp", 12345}}, Status: params.StatusError, StatusInfo: "failure", }, }, }, // Service changes { about: "no service in state, no service in store -> do nothing", setUp: func(c *gc.C, st *State) {}, change: watcher.Change{ C: "services", Id: "wordpress", }, }, { about: "service is removed if it's not in backing", add: []params.EntityInfo{&params.ServiceInfo{Name: "wordpress"}}, setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "services", Id: "wordpress", }, }, { about: "service is added if it's in backing but not in Store", setUp: func(c *gc.C, st *State) { wordpress := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) err := wordpress.SetExposed() c.Assert(err, gc.IsNil) err = wordpress.SetMinUnits(42) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "services", Id: "wordpress", }, expectContents: []params.EntityInfo{ &params.ServiceInfo{ Name: "wordpress", Exposed: true, CharmURL: "local:quantal/quantal-wordpress-3", OwnerTag: "user-admin", Life: params.Alive, MinUnits: 42, Config: charm.Settings{}, }, }, }, { about: "service is updated if it's in backing and in multiwatcher.Store", add: []params.EntityInfo{&params.ServiceInfo{ Name: "wordpress", Exposed: true, CharmURL: "local:quantal/quantal-wordpress-3", MinUnits: 47, Constraints: constraints.MustParse("mem=99M"), Config: charm.Settings{"blog-title": "boring"}, }}, setUp: func(c *gc.C, st *State) { svc := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) setServiceConfigAttr(c, svc, "blog-title", "boring") }, change: watcher.Change{ C: "services", Id: "wordpress", }, expectContents: []params.EntityInfo{ &params.ServiceInfo{ Name: "wordpress", CharmURL: "local:quantal/quantal-wordpress-3", OwnerTag: "user-admin", Life: params.Alive, Constraints: constraints.MustParse("mem=99M"), Config: charm.Settings{"blog-title": "boring"}, }, }, }, { about: "service re-reads config when charm URL changes", add: []params.EntityInfo{&params.ServiceInfo{ Name: "wordpress", // Note: CharmURL has a different revision number from // the wordpress revision in the testing repo. CharmURL: "local:quantal/quantal-wordpress-2", Config: charm.Settings{"foo": "bar"}, }}, setUp: func(c *gc.C, st *State) { svc := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) setServiceConfigAttr(c, svc, "blog-title", "boring") }, change: watcher.Change{ C: "services", Id: "wordpress", }, expectContents: []params.EntityInfo{ &params.ServiceInfo{ Name: "wordpress", CharmURL: "local:quantal/quantal-wordpress-3", OwnerTag: "user-admin", Life: params.Alive, Config: charm.Settings{"blog-title": "boring"}, }, }, }, // Relation changes { about: "no relation in state, no service in store -> do nothing", setUp: func(c *gc.C, st *State) {}, change: watcher.Change{ C: "relations", Id: "logging:logging-directory wordpress:logging-dir", }, }, { about: "relation is removed if it's not in backing", add: []params.EntityInfo{&params.RelationInfo{Key: "logging:logging-directory wordpress:logging-dir"}}, setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "relations", Id: "logging:logging-directory wordpress:logging-dir", }, }, { about: "relation is added if it's in backing but not in Store", setUp: func(c *gc.C, st *State) { AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) AddTestingService(c, st, "logging", AddTestingCharm(c, st, "logging")) eps, err := st.InferEndpoints([]string{"logging", "wordpress"}) c.Assert(err, gc.IsNil) _, err = st.AddRelation(eps...) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "relations", Id: "logging:logging-directory wordpress:logging-dir", }, expectContents: []params.EntityInfo{ &params.RelationInfo{ Key: "logging:logging-directory wordpress:logging-dir", Endpoints: []params.Endpoint{ {ServiceName: "logging", Relation: charm.Relation{Name: "logging-directory", Role: "requirer", Interface: "logging", Optional: false, Limit: 1, Scope: "container"}}, {ServiceName: "wordpress", Relation: charm.Relation{Name: "logging-dir", Role: "provider", Interface: "logging", Optional: false, Limit: 0, Scope: "container"}}}, }, }, }, // Annotation changes { about: "no annotation in state, no annotation in store -> do nothing", setUp: func(c *gc.C, st *State) {}, change: watcher.Change{ C: "relations", Id: "m#0", }, }, { about: "annotation is removed if it's not in backing", add: []params.EntityInfo{&params.AnnotationInfo{Tag: "machine-0"}}, setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "annotations", Id: "m#0", }, }, { about: "annotation is added if it's in backing but not in Store", setUp: func(c *gc.C, st *State) { m, err := st.AddMachine("quantal", JobHostUnits) c.Assert(err, gc.IsNil) err = m.SetAnnotations(map[string]string{"foo": "bar", "arble": "baz"}) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "annotations", Id: "m#0", }, expectContents: []params.EntityInfo{ &params.AnnotationInfo{ Tag: "machine-0", Annotations: map[string]string{"foo": "bar", "arble": "baz"}, }, }, }, { about: "annotation is updated if it's in backing and in multiwatcher.Store", add: []params.EntityInfo{&params.AnnotationInfo{ Tag: "machine-0", Annotations: map[string]string{ "arble": "baz", "foo": "bar", "pretty": "polly", }, }}, setUp: func(c *gc.C, st *State) { m, err := st.AddMachine("quantal", JobHostUnits) c.Assert(err, gc.IsNil) err = m.SetAnnotations(map[string]string{ "arble": "khroomph", "pretty": "", "new": "attr", }) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "annotations", Id: "m#0", }, expectContents: []params.EntityInfo{ &params.AnnotationInfo{ Tag: "machine-0", Annotations: map[string]string{ "arble": "khroomph", "new": "attr", }, }, }, }, // Unit status changes { about: "no unit in state -> do nothing", setUp: func(c *gc.C, st *State) {}, change: watcher.Change{ C: "statuses", Id: "u#wordpress/0", }, }, { about: "no change if status is not in backing", add: []params.EntityInfo{&params.UnitInfo{ Name: "wordpress/0", Status: params.StatusError, StatusInfo: "failure", }}, setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "statuses", Id: "u#wordpress/0", }, expectContents: []params.EntityInfo{ &params.UnitInfo{ Name: "wordpress/0", Status: params.StatusError, StatusInfo: "failure", }, }, }, { about: "status is changed if the unit exists in the store", add: []params.EntityInfo{&params.UnitInfo{ Name: "wordpress/0", Status: params.StatusError, StatusInfo: "failure", }}, setUp: func(c *gc.C, st *State) { wordpress := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) u, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) err = u.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "statuses", Id: "u#wordpress/0", }, expectContents: []params.EntityInfo{ &params.UnitInfo{ Name: "wordpress/0", Status: params.StatusStarted, StatusData: params.StatusData{}, }, }, }, { about: "status is changed with additional status data", add: []params.EntityInfo{&params.UnitInfo{ Name: "wordpress/0", Status: params.StatusStarted, }}, setUp: func(c *gc.C, st *State) { wordpress := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) u, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) err = u.SetStatus(params.StatusError, "hook error", params.StatusData{ "1st-key": "one", "2nd-key": 2, "3rd-key": true, }) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "statuses", Id: "u#wordpress/0", }, expectContents: []params.EntityInfo{ &params.UnitInfo{ Name: "wordpress/0", Status: params.StatusError, StatusInfo: "hook error", StatusData: params.StatusData{ "1st-key": "one", "2nd-key": 2, "3rd-key": true, }, }, }, }, // Machine status changes { about: "no machine in state -> do nothing", setUp: func(c *gc.C, st *State) {}, change: watcher.Change{ C: "statuses", Id: "m#0", }, }, { about: "no change if status is not in backing", add: []params.EntityInfo{&params.MachineInfo{ Id: "0", Status: params.StatusError, StatusInfo: "failure", }}, setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "statuses", Id: "m#0", }, expectContents: []params.EntityInfo{&params.MachineInfo{ Id: "0", Status: params.StatusError, StatusInfo: "failure", }}, }, { about: "status is changed if the machine exists in the store", add: []params.EntityInfo{&params.MachineInfo{ Id: "0", Status: params.StatusError, StatusInfo: "failure", }}, setUp: func(c *gc.C, st *State) { m, err := st.AddMachine("quantal", JobHostUnits) c.Assert(err, gc.IsNil) err = m.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "statuses", Id: "m#0", }, expectContents: []params.EntityInfo{ &params.MachineInfo{ Id: "0", Status: params.StatusStarted, StatusData: params.StatusData{}, }, }, }, // Service constraints changes { about: "no service in state -> do nothing", setUp: func(c *gc.C, st *State) {}, change: watcher.Change{ C: "constraints", Id: "s#wordpress", }, }, { about: "no change if service is not in backing", add: []params.EntityInfo{&params.ServiceInfo{ Name: "wordpress", Constraints: constraints.MustParse("mem=99M"), }}, setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "constraints", Id: "s#wordpress", }, expectContents: []params.EntityInfo{&params.ServiceInfo{ Name: "wordpress", Constraints: constraints.MustParse("mem=99M"), }}, }, { about: "status is changed if the service exists in the store", add: []params.EntityInfo{&params.ServiceInfo{ Name: "wordpress", Constraints: constraints.MustParse("mem=99M cpu-cores=2 cpu-power=4"), }}, setUp: func(c *gc.C, st *State) { svc := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) err := svc.SetConstraints(constraints.MustParse("mem=4G cpu-cores= arch=amd64")) c.Assert(err, gc.IsNil) }, change: watcher.Change{ C: "constraints", Id: "s#wordpress", }, expectContents: []params.EntityInfo{ &params.ServiceInfo{ Name: "wordpress", Constraints: constraints.MustParse("mem=4G cpu-cores= arch=amd64"), }, }, }, // Service config changes. { about: "no service in state -> do nothing", setUp: func(c *gc.C, st *State) {}, change: watcher.Change{ C: "settings", Id: "s#wordpress#local:quantal/quantal-wordpress-3", }, }, { about: "no change if service is not in backing", add: []params.EntityInfo{&params.ServiceInfo{ Name: "wordpress", CharmURL: "local:quantal/quantal-wordpress-3", }}, setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "settings", Id: "s#wordpress#local:quantal/quantal-wordpress-3", }, expectContents: []params.EntityInfo{&params.ServiceInfo{ Name: "wordpress", CharmURL: "local:quantal/quantal-wordpress-3", }}, }, { about: "service config is changed if service exists in the store with the same URL", add: []params.EntityInfo{&params.ServiceInfo{ Name: "wordpress", CharmURL: "local:quantal/quantal-wordpress-3", Config: charm.Settings{"foo": "bar"}, }}, setUp: func(c *gc.C, st *State) { svc := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) setServiceConfigAttr(c, svc, "blog-title", "foo") }, change: watcher.Change{ C: "settings", Id: "s#wordpress#local:quantal/quantal-wordpress-3", }, expectContents: []params.EntityInfo{ &params.ServiceInfo{ Name: "wordpress", CharmURL: "local:quantal/quantal-wordpress-3", Config: charm.Settings{"blog-title": "foo"}, }, }, }, { about: "service config is unescaped when reading from the backing store", add: []params.EntityInfo{&params.ServiceInfo{ Name: "wordpress", CharmURL: "local:quantal/quantal-wordpress-3", Config: charm.Settings{"key.dotted": "bar"}, }}, setUp: func(c *gc.C, st *State) { testCharm := AddCustomCharm( c, st, "wordpress", "config.yaml", dottedConfig, "quantal", 3) svc := AddTestingService(c, st, "wordpress", testCharm) setServiceConfigAttr(c, svc, "key.dotted", "foo") }, change: watcher.Change{ C: "settings", Id: "s#wordpress#local:quantal/quantal-wordpress-3", }, expectContents: []params.EntityInfo{ &params.ServiceInfo{ Name: "wordpress", CharmURL: "local:quantal/quantal-wordpress-3", Config: charm.Settings{"key.dotted": "foo"}, }, }, }, { about: "service config is unchanged if service exists in the store with a different URL", add: []params.EntityInfo{&params.ServiceInfo{ Name: "wordpress", CharmURL: "local:quantal/quantal-wordpress-2", // Note different revno. Config: charm.Settings{"foo": "bar"}, }}, setUp: func(c *gc.C, st *State) { svc := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress")) setServiceConfigAttr(c, svc, "blog-title", "foo") }, change: watcher.Change{ C: "settings", Id: "s#wordpress#local:quantal/quantal-wordpress-3", }, expectContents: []params.EntityInfo{ &params.ServiceInfo{ Name: "wordpress", CharmURL: "local:quantal/quantal-wordpress-2", Config: charm.Settings{"foo": "bar"}, }, }, }, { about: "non-service config change is ignored", setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "settings", Id: "m#0", }, }, { about: "service config change with no charm url is ignored", setUp: func(*gc.C, *State) {}, change: watcher.Change{ C: "settings", Id: "s#foo", }, }, } func setServiceConfigAttr(c *gc.C, svc *Service, attr string, val interface{}) { err := svc.UpdateConfigSettings(charm.Settings{attr: val}) c.Assert(err, gc.IsNil) } func (s *storeManagerStateSuite) TestChanged(c *gc.C) { collections := map[string]*mgo.Collection{ "machines": s.State.machines, "units": s.State.units, "services": s.State.services, "relations": s.State.relations, "annotations": s.State.annotations, "statuses": s.State.statuses, "constraints": s.State.constraints, "settings": s.State.settings, } for i, test := range allWatcherChangedTests { c.Logf("test %d. %s", i, test.about) b := newAllWatcherStateBacking(s.State) all := multiwatcher.NewStore() for _, info := range test.add { all.Update(info) } test.setUp(c, s.State) c.Logf("done set up") ch := test.change ch.C = collections[ch.C].Name err := b.Changed(all, test.change) c.Assert(err, gc.IsNil) assertEntitiesEqual(c, all.All(), test.expectContents) s.Reset(c) } } // StateWatcher tests the integration of the state watcher // with the state-based backing. Most of the logic is tested elsewhere - // this just tests end-to-end. func (s *storeManagerStateSuite) TestStateWatcher(c *gc.C) { m0, err := s.State.AddMachine("quantal", JobManageEnviron) c.Assert(err, gc.IsNil) c.Assert(m0.Id(), gc.Equals, "0") m1, err := s.State.AddMachine("saucy", JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(m1.Id(), gc.Equals, "1") b := newAllWatcherStateBacking(s.State) aw := multiwatcher.NewStoreManager(b) defer aw.Stop() w := multiwatcher.NewWatcher(aw) s.State.StartSync() checkNext(c, w, b, []params.Delta{{ Entity: &params.MachineInfo{ Id: "0", Status: params.StatusPending, Life: params.Alive, Series: "quantal", Jobs: []params.MachineJob{JobManageEnviron.ToParams()}, Addresses: []instance.Address{}, }, }, { Entity: &params.MachineInfo{ Id: "1", Status: params.StatusPending, Life: params.Alive, Series: "saucy", Jobs: []params.MachineJob{JobHostUnits.ToParams()}, Addresses: []instance.Address{}, }, }}, "") // Make some changes to the state. arch := "amd64" mem := uint64(4096) hc := &instance.HardwareCharacteristics{ Arch: &arch, Mem: &mem, } err = m0.SetProvisioned("i-0", "bootstrap_nonce", hc) c.Assert(err, gc.IsNil) err = m1.Destroy() c.Assert(err, gc.IsNil) err = m1.EnsureDead() c.Assert(err, gc.IsNil) err = m1.Remove() c.Assert(err, gc.IsNil) m2, err := s.State.AddMachine("trusty", JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(m2.Id(), gc.Equals, "2") s.State.StartSync() // Check that we see the changes happen within a // reasonable time. var deltas []params.Delta for { d, err := getNext(c, w, 100*time.Millisecond) if err == errTimeout { break } c.Assert(err, gc.IsNil) deltas = append(deltas, d...) } checkDeltasEqual(c, b, deltas, []params.Delta{{ Removed: true, Entity: &params.MachineInfo{ Id: "1", Status: params.StatusPending, Life: params.Alive, Series: "saucy", Jobs: []params.MachineJob{JobHostUnits.ToParams()}, Addresses: []instance.Address{}, }, }, { Entity: &params.MachineInfo{ Id: "2", Status: params.StatusPending, Life: params.Alive, Series: "trusty", Jobs: []params.MachineJob{JobHostUnits.ToParams()}, Addresses: []instance.Address{}, }, }, { Entity: &params.MachineInfo{ Id: "0", InstanceId: "i-0", Status: params.StatusPending, Life: params.Alive, Series: "quantal", Jobs: []params.MachineJob{JobManageEnviron.ToParams()}, Addresses: []instance.Address{}, HardwareCharacteristics: hc, }, }}) err = w.Stop() c.Assert(err, gc.IsNil) _, err = w.Next() c.Assert(err, gc.Equals, multiwatcher.ErrWatcherStopped) } type entityInfoSlice []params.EntityInfo func (s entityInfoSlice) Len() int { return len(s) } func (s entityInfoSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s entityInfoSlice) Less(i, j int) bool { id0, id1 := s[i].EntityId(), s[j].EntityId() if id0.Kind != id1.Kind { return id0.Kind < id1.Kind } switch id := id0.Id.(type) { case string: return id < id1.Id.(string) default: } panic("unexpected entity id type") } var errTimeout = errors.New("no change received in sufficient time") func getNext(c *gc.C, w *multiwatcher.Watcher, timeout time.Duration) ([]params.Delta, error) { var deltas []params.Delta var err error ch := make(chan struct{}, 1) go func() { deltas, err = w.Next() ch <- struct{}{} }() select { case <-ch: return deltas, err case <-time.After(1 * time.Second): } return nil, errTimeout } func checkNext(c *gc.C, w *multiwatcher.Watcher, b multiwatcher.Backing, deltas []params.Delta, expectErr string) { d, err := getNext(c, w, 1*time.Second) if expectErr != "" { c.Check(err, gc.ErrorMatches, expectErr) return } checkDeltasEqual(c, b, d, deltas) } // deltas are returns in arbitrary order, so we compare // them as sets. func checkDeltasEqual(c *gc.C, b multiwatcher.Backing, d0, d1 []params.Delta) { c.Check(deltaMap(d0, b), gc.DeepEquals, deltaMap(d1, b)) } func deltaMap(deltas []params.Delta, b multiwatcher.Backing) map[multiwatcher.InfoId]params.EntityInfo { m := make(map[multiwatcher.InfoId]params.EntityInfo) for _, d := range deltas { id := d.Entity.EntityId() if _, ok := m[id]; ok { panic(fmt.Errorf("%v mentioned twice in delta set", id)) } if d.Removed { m[id] = nil } else { m[id] = d.Entity } } return m } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/multiwatcher/������������������������������������0000755�0000153�0000161�00000000000�12321735642�024515� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/multiwatcher/multiwatcher.go���������������������0000644�0000153�0000161�00000031062�12321735642�027556� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package multiwatcher import ( "container/list" "errors" "reflect" "launchpad.net/tomb" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" ) // Watcher watches any changes to the state. type Watcher struct { all *StoreManager // The following fields are maintained by the StoreManager // goroutine. revno int64 stopped bool } // NewWatcher creates a new watcher that can observe // changes to an underlying store manager. func NewWatcher(all *StoreManager) *Watcher { return &Watcher{ all: all, } } // Stop stops the watcher. func (w *Watcher) Stop() error { select { case w.all.request <- &request{w: w}: return nil case <-w.all.tomb.Dead(): } return w.all.tomb.Err() } var ErrWatcherStopped = errors.New("watcher was stopped") // Next retrieves all changes that have happened since the last // time it was called, blocking until there are some changes available. func (w *Watcher) Next() ([]params.Delta, error) { req := &request{ w: w, reply: make(chan bool), } select { case w.all.request <- req: case <-w.all.tomb.Dead(): err := w.all.tomb.Err() if err == nil { err = errors.New("shared state watcher was stopped") } return nil, err } if ok := <-req.reply; !ok { return nil, ErrWatcherStopped } return req.changes, nil } // StoreManager holds a shared record of current state and replies to // requests from Watchers to tell them when it changes. type StoreManager struct { tomb tomb.Tomb // backing knows how to fetch information from // the underlying state. backing Backing // request receives requests from Watcher clients. request chan *request // all holds information on everything the StoreManager cares about. all *Store // Each entry in the waiting map holds a linked list of Next requests // outstanding for the associated Watcher. waiting map[*Watcher]*request } // InfoId holds an identifier for an Info item held in a Store. type InfoId interface{} // Backing is the interface required by the StoreManager to access the // underlying state. type Backing interface { // GetAll retrieves information about all information // known to the Backing and stashes it in the Store. GetAll(all *Store) error // Changed informs the backing about a change received // from a watcher channel. The backing is responsible for // updating the Store to reflect the change. Changed(all *Store, change watcher.Change) error // Watch watches for any changes and sends them // on the given channel. Watch(in chan<- watcher.Change) // Unwatch stops watching for changes on the // given channel. Unwatch(in chan<- watcher.Change) } // request holds a message from the Watcher to the // StoreManager for some changes. The request will be // replied to when some changes are available. type request struct { // w holds the Watcher that has originated the request. w *Watcher // reply receives a message when deltas are ready. If reply is // nil, the Watcher will be stopped. If the reply is true, // the request has been processed; if false, the Watcher // has been stopped, reply chan bool // On reply, changes will hold changes that have occurred since // the last replied-to Next request. changes []params.Delta // next points to the next request in the list of outstanding // requests on a given watcher. It is used only by the central // StoreManager goroutine. next *request } // newStoreManagerNoRun creates the store manager // but does not start its run loop. func newStoreManagerNoRun(backing Backing) *StoreManager { return &StoreManager{ backing: backing, request: make(chan *request), all: NewStore(), waiting: make(map[*Watcher]*request), } } // NewStoreManager returns a new StoreManager that retrieves information // using the given backing. func NewStoreManager(backing Backing) *StoreManager { sm := newStoreManagerNoRun(backing) go func() { defer sm.tomb.Done() // TODO(rog) distinguish between temporary and permanent errors: // if we get an error in loop, this logic kill the state's StoreManager // forever. This currently fits the way we go about things, // because we reconnect to the state on any error, but // perhaps there are errors we could recover from. sm.tomb.Kill(sm.loop()) }() return sm } func (sm *StoreManager) loop() error { in := make(chan watcher.Change) sm.backing.Watch(in) defer sm.backing.Unwatch(in) // We have no idea what changes the watcher might be trying to // send us while getAll proceeds, but we don't mind, because // StoreManager.changed is idempotent with respect to both updates // and removals. // TODO(rog) Perhaps find a way to avoid blocking all other // watchers while GetAll is running. if err := sm.backing.GetAll(sm.all); err != nil { return err } for { select { case <-sm.tomb.Dying(): return tomb.ErrDying case change := <-in: if err := sm.backing.Changed(sm.all, change); err != nil { return err } case req := <-sm.request: sm.handle(req) } sm.respond() } } // Stop stops the StoreManager. func (sm *StoreManager) Stop() error { sm.tomb.Kill(nil) return sm.tomb.Wait() } // handle processes a request from a Watcher to the StoreManager. func (sm *StoreManager) handle(req *request) { if req.w.stopped { // The watcher has previously been stopped. if req.reply != nil { req.reply <- false } return } if req.reply == nil { // This is a request to stop the watcher. for req := sm.waiting[req.w]; req != nil; req = req.next { req.reply <- false } delete(sm.waiting, req.w) req.w.stopped = true sm.leave(req.w) return } // Add request to head of list. req.next = sm.waiting[req.w] sm.waiting[req.w] = req } // respond responds to all outstanding requests that are satisfiable. func (sm *StoreManager) respond() { for w, req := range sm.waiting { revno := w.revno changes := sm.all.ChangesSince(revno) if len(changes) == 0 { continue } req.changes = changes w.revno = sm.all.latestRevno req.reply <- true if req := req.next; req == nil { // Last request for this watcher. delete(sm.waiting, w) } else { sm.waiting[w] = req } sm.seen(revno) } } // seen states that a Watcher has just been given information about // all entities newer than the given revno. We assume it has already // seen all the older entities. func (sm *StoreManager) seen(revno int64) { for e := sm.all.list.Front(); e != nil; { next := e.Next() entry := e.Value.(*entityEntry) if entry.revno <= revno { break } if entry.creationRevno > revno { if !entry.removed { // This is a new entity that hasn't been seen yet, // so increment the entry's refCount. entry.refCount++ } } else if entry.removed { // This is an entity that we previously saw, but // has now been removed, so decrement its refCount, removing // the entity if nothing else is waiting to be notified that it's // gone. sm.all.decRef(entry) } e = next } } // leave is called when the given watcher leaves. It decrements the reference // counts of any entities that have been seen by the watcher. func (sm *StoreManager) leave(w *Watcher) { for e := sm.all.list.Front(); e != nil; { next := e.Next() entry := e.Value.(*entityEntry) if entry.creationRevno <= w.revno { // The watcher has seen this entry. if entry.removed && entry.revno <= w.revno { // The entity has been removed and the // watcher has already been informed of that, // so its refcount has already been decremented. e = next continue } sm.all.decRef(entry) } e = next } } // entityEntry holds an entry in the linked list of all entities known // to a Watcher. type entityEntry struct { // The revno holds the local idea of the latest change to the // given entity. It is not the same as the transaction revno - // this means we can unconditionally move a newly fetched entity // to the front of the list without worrying if the revno has // changed since the watcher reported it. revno int64 // creationRevno holds the revision number when the // entity was created. creationRevno int64 // removed marks whether the entity has been removed. removed bool // refCount holds a count of the number of watchers that // have seen this entity. When the entity is marked as removed, // the ref count is decremented whenever a Watcher that // has previously seen the entry now sees that it has been removed; // the entry will be deleted when all such Watchers have // been notified. refCount int // info holds the actual information on the entity. info params.EntityInfo } // Store holds a list of all entities known // to a Watcher. type Store struct { latestRevno int64 entities map[InfoId]*list.Element list *list.List } // NewStore returns an Store instance holding information about the // current state of all entities in the environment. // It is only exposed here for testing purposes. func NewStore() *Store { all := &Store{ entities: make(map[InfoId]*list.Element), list: list.New(), } return all } // All returns all the entities stored in the Store, // oldest first. It is only exposed for testing purposes. func (a *Store) All() []params.EntityInfo { entities := make([]params.EntityInfo, 0, a.list.Len()) for e := a.list.Front(); e != nil; e = e.Next() { entry := e.Value.(*entityEntry) if entry.removed { continue } entities = append(entities, entry.info) } return entities } // add adds a new entity with the given id and associated // information to the list. func (a *Store) add(id InfoId, info params.EntityInfo) { if a.entities[id] != nil { panic("adding new entry with duplicate id") } a.latestRevno++ entry := &entityEntry{ info: info, revno: a.latestRevno, creationRevno: a.latestRevno, } a.entities[id] = a.list.PushFront(entry) } // decRef decrements the reference count of an entry within the list, // deleting it if it becomes zero and the entry is removed. func (a *Store) decRef(entry *entityEntry) { if entry.refCount--; entry.refCount > 0 { return } if entry.refCount < 0 { panic("negative reference count") } if !entry.removed { return } id := entry.info.EntityId() elem := a.entities[id] if elem == nil { panic("delete of non-existent entry") } delete(a.entities, id) a.list.Remove(elem) } // delete deletes the entry with the given info id. func (a *Store) delete(id params.EntityId) { elem := a.entities[id] if elem == nil { return } delete(a.entities, id) a.list.Remove(elem) } // Remove marks that the entity with the given id has // been removed from the backing. If nothing has seen the // entity, then we delete it immediately. func (a *Store) Remove(id params.EntityId) { if elem := a.entities[id]; elem != nil { entry := elem.Value.(*entityEntry) if entry.removed { return } a.latestRevno++ if entry.refCount == 0 { a.delete(id) return } entry.revno = a.latestRevno entry.removed = true a.list.MoveToFront(elem) } } // Update updates the information for the given entity. func (a *Store) Update(info params.EntityInfo) { id := info.EntityId() elem := a.entities[id] if elem == nil { a.add(id, info) return } entry := elem.Value.(*entityEntry) // Nothing has changed, so change nothing. // TODO(rog) do the comparison more efficiently. if reflect.DeepEqual(info, entry.info) { return } // We already know about the entity; update its doc. a.latestRevno++ entry.revno = a.latestRevno entry.info = info a.list.MoveToFront(elem) } // Get returns the stored entity with the given // id, or nil if none was found. The contents of the returned entity // should not be changed. func (a *Store) Get(id params.EntityId) params.EntityInfo { if e := a.entities[id]; e != nil { return e.Value.(*entityEntry).info } return nil } // ChangesSince returns any changes that have occurred since // the given revno, oldest first. func (a *Store) ChangesSince(revno int64) []params.Delta { e := a.list.Front() n := 0 for ; e != nil; e = e.Next() { entry := e.Value.(*entityEntry) if entry.revno <= revno { break } n++ } if e != nil { // We've found an element that we've already seen. e = e.Prev() } else { // We haven't seen any elements, so we want all of them. e = a.list.Back() n++ } changes := make([]params.Delta, 0, n) for ; e != nil; e = e.Prev() { entry := e.Value.(*entityEntry) if entry.removed && entry.creationRevno > revno { // Don't include entries that have been created // and removed since the revno. continue } changes = append(changes, params.Delta{ Removed: entry.removed, Entity: entry.info, }) } return changes } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/multiwatcher/multiwatcher_internal_test.go�������0000644�0000153�0000161�00000054604�12321735642�032520� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package multiwatcher import ( "container/list" "errors" "fmt" "sync" stdtesting "testing" "time" "labix.org/v2/mgo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/testing/testbase" ) func Test(t *stdtesting.T) { gc.TestingT(t) } type storeSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&storeSuite{}) var StoreChangeMethodTests = []struct { about string change func(all *Store) expectRevno int64 expectContents []entityEntry }{{ about: "empty at first", change: func(*Store) {}, }, { about: "add single entry", change: func(all *Store) { all.Update(&MachineInfo{ Id: "0", InstanceId: "i-0", }) }, expectRevno: 1, expectContents: []entityEntry{{ creationRevno: 1, revno: 1, info: &MachineInfo{ Id: "0", InstanceId: "i-0", }, }}, }, { about: "add two entries", change: func(all *Store) { all.Update(&MachineInfo{ Id: "0", InstanceId: "i-0", }) all.Update(&ServiceInfo{ Name: "wordpress", Exposed: true, }) }, expectRevno: 2, expectContents: []entityEntry{{ creationRevno: 1, revno: 1, info: &MachineInfo{ Id: "0", InstanceId: "i-0", }, }, { creationRevno: 2, revno: 2, info: &ServiceInfo{ Name: "wordpress", Exposed: true, }, }}, }, { about: "update an entity that's not currently there", change: func(all *Store) { m := &MachineInfo{Id: "1"} all.Update(m) }, expectRevno: 1, expectContents: []entityEntry{{ creationRevno: 1, revno: 1, info: &MachineInfo{Id: "1"}, }}, }, { about: "mark removed on existing entry", change: func(all *Store) { all.Update(&MachineInfo{Id: "0"}) all.Update(&MachineInfo{Id: "1"}) StoreIncRef(all, params.EntityId{"machine", "0"}) all.Remove(params.EntityId{"machine", "0"}) }, expectRevno: 3, expectContents: []entityEntry{{ creationRevno: 2, revno: 2, info: &MachineInfo{Id: "1"}, }, { creationRevno: 1, revno: 3, refCount: 1, removed: true, info: &MachineInfo{Id: "0"}, }}, }, { about: "mark removed on nonexistent entry", change: func(all *Store) { all.Remove(params.EntityId{"machine", "0"}) }, }, { about: "mark removed on already marked entry", change: func(all *Store) { all.Update(&MachineInfo{Id: "0"}) all.Update(&MachineInfo{Id: "1"}) StoreIncRef(all, params.EntityId{"machine", "0"}) all.Remove(params.EntityId{"machine", "0"}) all.Update(&MachineInfo{ Id: "1", InstanceId: "i-1", }) all.Remove(params.EntityId{"machine", "0"}) }, expectRevno: 4, expectContents: []entityEntry{{ creationRevno: 1, revno: 3, refCount: 1, removed: true, info: &MachineInfo{Id: "0"}, }, { creationRevno: 2, revno: 4, info: &MachineInfo{ Id: "1", InstanceId: "i-1", }, }}, }, { about: "mark removed on entry with zero ref count", change: func(all *Store) { all.Update(&MachineInfo{Id: "0"}) all.Remove(params.EntityId{"machine", "0"}) }, expectRevno: 2, }, { about: "delete entry", change: func(all *Store) { all.Update(&MachineInfo{Id: "0"}) all.delete(params.EntityId{"machine", "0"}) }, expectRevno: 1, }, { about: "decref of non-removed entity", change: func(all *Store) { m := &MachineInfo{Id: "0"} all.Update(m) id := m.EntityId() StoreIncRef(all, id) entry := all.entities[id].Value.(*entityEntry) all.decRef(entry) }, expectRevno: 1, expectContents: []entityEntry{{ creationRevno: 1, revno: 1, refCount: 0, info: &MachineInfo{Id: "0"}, }}, }, { about: "decref of removed entity", change: func(all *Store) { m := &MachineInfo{Id: "0"} all.Update(m) id := m.EntityId() entry := all.entities[id].Value.(*entityEntry) entry.refCount++ all.Remove(id) all.decRef(entry) }, expectRevno: 2, }, } func (s *storeSuite) TestStoreChangeMethods(c *gc.C) { for i, test := range StoreChangeMethodTests { all := NewStore() c.Logf("test %d. %s", i, test.about) test.change(all) assertStoreContents(c, all, test.expectRevno, test.expectContents) } } func (s *storeSuite) TestChangesSince(c *gc.C) { a := NewStore() // Add three entries. var deltas []params.Delta for i := 0; i < 3; i++ { m := &MachineInfo{Id: fmt.Sprint(i)} a.Update(m) deltas = append(deltas, params.Delta{Entity: m}) } // Check that the deltas from each revno are as expected. for i := 0; i < 3; i++ { c.Logf("test %d", i) c.Assert(a.ChangesSince(int64(i)), gc.DeepEquals, deltas[i:]) } // Check boundary cases. c.Assert(a.ChangesSince(-1), gc.DeepEquals, deltas) c.Assert(a.ChangesSince(99), gc.HasLen, 0) // Update one machine and check we see the changes. rev := a.latestRevno m1 := &MachineInfo{ Id: "1", InstanceId: "foo", } a.Update(m1) c.Assert(a.ChangesSince(rev), gc.DeepEquals, []params.Delta{{Entity: m1}}) // Make sure the machine isn't simply removed from // the list when it's marked as removed. StoreIncRef(a, params.EntityId{"machine", "0"}) // Remove another machine and check we see it's removed. m0 := &MachineInfo{Id: "0"} a.Remove(m0.EntityId()) // Check that something that never saw m0 does not get // informed of its removal (even those the removed entity // is still in the list. c.Assert(a.ChangesSince(0), gc.DeepEquals, []params.Delta{{ Entity: &MachineInfo{Id: "2"}, }, { Entity: m1, }}) c.Assert(a.ChangesSince(rev), gc.DeepEquals, []params.Delta{{ Entity: m1, }, { Removed: true, Entity: m0, }}) c.Assert(a.ChangesSince(rev+1), gc.DeepEquals, []params.Delta{{ Removed: true, Entity: m0, }}) } func (s *storeSuite) TestGet(c *gc.C) { a := NewStore() m := &MachineInfo{Id: "0"} a.Update(m) c.Assert(a.Get(m.EntityId()), gc.Equals, m) c.Assert(a.Get(params.EntityId{"machine", "1"}), gc.IsNil) } type storeManagerSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&storeManagerSuite{}) func (*storeManagerSuite) TestHandle(c *gc.C) { sm := newStoreManagerNoRun(newTestBacking(nil)) // Add request from first watcher. w0 := &Watcher{all: sm} req0 := &request{ w: w0, reply: make(chan bool, 1), } sm.handle(req0) assertWaitingRequests(c, sm, map[*Watcher][]*request{ w0: {req0}, }) // Add second request from first watcher. req1 := &request{ w: w0, reply: make(chan bool, 1), } sm.handle(req1) assertWaitingRequests(c, sm, map[*Watcher][]*request{ w0: {req1, req0}, }) // Add request from second watcher. w1 := &Watcher{all: sm} req2 := &request{ w: w1, reply: make(chan bool, 1), } sm.handle(req2) assertWaitingRequests(c, sm, map[*Watcher][]*request{ w0: {req1, req0}, w1: {req2}, }) // Stop first watcher. sm.handle(&request{ w: w0, }) assertWaitingRequests(c, sm, map[*Watcher][]*request{ w1: {req2}, }) assertReplied(c, false, req0) assertReplied(c, false, req1) // Stop second watcher. sm.handle(&request{ w: w1, }) assertWaitingRequests(c, sm, nil) assertReplied(c, false, req2) } func (s *storeManagerSuite) TestHandleStopNoDecRefIfMoreRecentlyCreated(c *gc.C) { // If the Watcher hasn't seen the item, then we shouldn't // decrement its ref count when it is stopped. sm := NewStoreManager(newTestBacking(nil)) sm.all.Update(&MachineInfo{Id: "0"}) StoreIncRef(sm.all, params.EntityId{"machine", "0"}) w := &Watcher{all: sm} // Stop the watcher. sm.handle(&request{w: w}) assertStoreContents(c, sm.all, 1, []entityEntry{{ creationRevno: 1, revno: 1, refCount: 1, info: &MachineInfo{ Id: "0", }, }}) } func (s *storeManagerSuite) TestHandleStopNoDecRefIfAlreadySeenRemoved(c *gc.C) { // If the Watcher has already seen the item removed, then // we shouldn't decrement its ref count when it is stopped. sm := NewStoreManager(newTestBacking(nil)) sm.all.Update(&MachineInfo{Id: "0"}) StoreIncRef(sm.all, params.EntityId{"machine", "0"}) sm.all.Remove(params.EntityId{"machine", "0"}) w := &Watcher{all: sm} // Stop the watcher. sm.handle(&request{w: w}) assertStoreContents(c, sm.all, 2, []entityEntry{{ creationRevno: 1, revno: 2, refCount: 1, removed: true, info: &MachineInfo{ Id: "0", }, }}) } func (s *storeManagerSuite) TestHandleStopDecRefIfAlreadySeenAndNotRemoved(c *gc.C) { // If the Watcher has already seen the item removed, then // we should decrement its ref count when it is stopped. sm := NewStoreManager(newTestBacking(nil)) sm.all.Update(&MachineInfo{Id: "0"}) StoreIncRef(sm.all, params.EntityId{"machine", "0"}) w := &Watcher{all: sm} w.revno = sm.all.latestRevno // Stop the watcher. sm.handle(&request{w: w}) assertStoreContents(c, sm.all, 1, []entityEntry{{ creationRevno: 1, revno: 1, info: &MachineInfo{ Id: "0", }, }}) } func (s *storeManagerSuite) TestHandleStopNoDecRefIfNotSeen(c *gc.C) { // If the Watcher hasn't seen the item at all, it should // leave the ref count untouched. sm := NewStoreManager(newTestBacking(nil)) sm.all.Update(&MachineInfo{Id: "0"}) StoreIncRef(sm.all, params.EntityId{"machine", "0"}) w := &Watcher{all: sm} // Stop the watcher. sm.handle(&request{w: w}) assertStoreContents(c, sm.all, 1, []entityEntry{{ creationRevno: 1, revno: 1, refCount: 1, info: &MachineInfo{ Id: "0", }, }}) } var respondTestChanges = [...]func(all *Store){ func(all *Store) { all.Update(&MachineInfo{Id: "0"}) }, func(all *Store) { all.Update(&MachineInfo{Id: "1"}) }, func(all *Store) { all.Update(&MachineInfo{Id: "2"}) }, func(all *Store) { all.Remove(params.EntityId{"machine", "0"}) }, func(all *Store) { all.Update(&MachineInfo{ Id: "1", InstanceId: "i-1", }) }, func(all *Store) { all.Remove(params.EntityId{"machine", "1"}) }, } var ( respondTestFinalState = []entityEntry{{ creationRevno: 3, revno: 3, info: &MachineInfo{ Id: "2", }, }} respondTestFinalRevno = int64(len(respondTestChanges)) ) func (s *storeManagerSuite) TestRespondResults(c *gc.C) { // We test the response results for a pair of watchers by // interleaving notional Next requests in all possible // combinations after each change in respondTestChanges and // checking that the view of the world as seen by the watchers // matches the actual current state. // We decide whether if we make a request for a given // watcher by inspecting a number n - bit i of n determines whether // a request will be responded to after running respondTestChanges[i]. numCombinations := 1 << uint(len(respondTestChanges)) const wcount = 2 ns := make([]int, wcount) for ns[0] = 0; ns[0] < numCombinations; ns[0]++ { for ns[1] = 0; ns[1] < numCombinations; ns[1]++ { sm := newStoreManagerNoRun(&storeManagerTestBacking{}) c.Logf("test %0*b", len(respondTestChanges), ns) var ( ws []*Watcher wstates []watcherState reqs []*request ) for i := 0; i < wcount; i++ { ws = append(ws, &Watcher{}) wstates = append(wstates, make(watcherState)) reqs = append(reqs, nil) } // Make each change in turn, and make a request for each // watcher if n and respond for i, change := range respondTestChanges { c.Logf("change %d", i) change(sm.all) needRespond := false for wi, n := range ns { if n&(1<<uint(i)) != 0 { needRespond = true if reqs[wi] == nil { reqs[wi] = &request{ w: ws[wi], reply: make(chan bool, 1), } sm.handle(reqs[wi]) } } } if !needRespond { continue } // Check that the expected requests are pending. expectWaiting := make(map[*Watcher][]*request) for wi, w := range ws { if reqs[wi] != nil { expectWaiting[w] = []*request{reqs[wi]} } } assertWaitingRequests(c, sm, expectWaiting) // Actually respond; then check that each watcher with // an outstanding request now has an up to date view // of the world. sm.respond() for wi, req := range reqs { if req == nil { continue } select { case ok := <-req.reply: c.Assert(ok, gc.Equals, true) c.Assert(len(req.changes) > 0, gc.Equals, true) wstates[wi].update(req.changes) reqs[wi] = nil default: } c.Logf("check %d", wi) wstates[wi].check(c, sm.all) } } // Stop the watcher and check that all ref counts end up at zero // and removed objects are deleted. for wi, w := range ws { sm.handle(&request{w: w}) if reqs[wi] != nil { assertReplied(c, false, reqs[wi]) } } assertStoreContents(c, sm.all, respondTestFinalRevno, respondTestFinalState) } } } func (*storeManagerSuite) TestRespondMultiple(c *gc.C) { sm := NewStoreManager(newTestBacking(nil)) sm.all.Update(&MachineInfo{Id: "0"}) // Add one request and respond. // It should see the above change. w0 := &Watcher{all: sm} req0 := &request{ w: w0, reply: make(chan bool, 1), } sm.handle(req0) sm.respond() assertReplied(c, true, req0) c.Assert(req0.changes, gc.DeepEquals, []params.Delta{{Entity: &MachineInfo{Id: "0"}}}) assertWaitingRequests(c, sm, nil) // Add another request from the same watcher and respond. // It should have no reply because nothing has changed. req0 = &request{ w: w0, reply: make(chan bool, 1), } sm.handle(req0) sm.respond() assertNotReplied(c, req0) // Add two requests from another watcher and respond. // The request from the first watcher should still not // be replied to, but the later of the two requests from // the second watcher should get a reply. w1 := &Watcher{all: sm} req1 := &request{ w: w1, reply: make(chan bool, 1), } sm.handle(req1) req2 := &request{ w: w1, reply: make(chan bool, 1), } sm.handle(req2) assertWaitingRequests(c, sm, map[*Watcher][]*request{ w0: {req0}, w1: {req2, req1}, }) sm.respond() assertNotReplied(c, req0) assertNotReplied(c, req1) assertReplied(c, true, req2) c.Assert(req2.changes, gc.DeepEquals, []params.Delta{{Entity: &MachineInfo{Id: "0"}}}) assertWaitingRequests(c, sm, map[*Watcher][]*request{ w0: {req0}, w1: {req1}, }) // Check that nothing more gets responded to if we call respond again. sm.respond() assertNotReplied(c, req0) assertNotReplied(c, req1) // Now make a change and check that both waiting requests // get serviced. sm.all.Update(&MachineInfo{Id: "1"}) sm.respond() assertReplied(c, true, req0) assertReplied(c, true, req1) assertWaitingRequests(c, sm, nil) deltas := []params.Delta{{Entity: &MachineInfo{Id: "1"}}} c.Assert(req0.changes, gc.DeepEquals, deltas) c.Assert(req1.changes, gc.DeepEquals, deltas) } func (*storeManagerSuite) TestRunStop(c *gc.C) { sm := NewStoreManager(newTestBacking(nil)) w := &Watcher{all: sm} err := sm.Stop() c.Assert(err, gc.IsNil) d, err := w.Next() c.Assert(err, gc.ErrorMatches, "shared state watcher was stopped") c.Assert(d, gc.HasLen, 0) } func (*storeManagerSuite) TestRun(c *gc.C) { b := newTestBacking([]params.EntityInfo{ &MachineInfo{Id: "0"}, &ServiceInfo{Name: "logging"}, &ServiceInfo{Name: "wordpress"}, }) sm := NewStoreManager(b) defer func() { c.Check(sm.Stop(), gc.IsNil) }() w := &Watcher{all: sm} checkNext(c, w, []params.Delta{ {Entity: &MachineInfo{Id: "0"}}, {Entity: &ServiceInfo{Name: "logging"}}, {Entity: &ServiceInfo{Name: "wordpress"}}, }, "") b.updateEntity(&MachineInfo{Id: "0", InstanceId: "i-0"}) checkNext(c, w, []params.Delta{ {Entity: &MachineInfo{Id: "0", InstanceId: "i-0"}}, }, "") b.deleteEntity(params.EntityId{"machine", "0"}) checkNext(c, w, []params.Delta{ {Removed: true, Entity: &MachineInfo{Id: "0"}}, }, "") } func (*storeManagerSuite) TestWatcherStop(c *gc.C) { sm := NewStoreManager(newTestBacking(nil)) defer func() { c.Check(sm.Stop(), gc.IsNil) }() w := &Watcher{all: sm} done := make(chan struct{}) go func() { checkNext(c, w, nil, ErrWatcherStopped.Error()) done <- struct{}{} }() err := w.Stop() c.Assert(err, gc.IsNil) <-done } func (*storeManagerSuite) TestWatcherStopBecauseStoreManagerError(c *gc.C) { b := newTestBacking([]params.EntityInfo{&MachineInfo{Id: "0"}}) sm := NewStoreManager(b) defer func() { c.Check(sm.Stop(), gc.ErrorMatches, "some error") }() w := &Watcher{all: sm} // Receive one delta to make sure that the storeManager // has seen the initial state. checkNext(c, w, []params.Delta{{Entity: &MachineInfo{Id: "0"}}}, "") c.Logf("setting fetch error") b.setFetchError(errors.New("some error")) c.Logf("updating entity") b.updateEntity(&MachineInfo{Id: "1"}) checkNext(c, w, nil, "some error") } func StoreIncRef(a *Store, id InfoId) { entry := a.entities[id].Value.(*entityEntry) entry.refCount++ } func assertStoreContents(c *gc.C, a *Store, latestRevno int64, entries []entityEntry) { var gotEntries []entityEntry var gotElems []*list.Element c.Check(a.list.Len(), gc.Equals, len(entries)) for e := a.list.Back(); e != nil; e = e.Prev() { gotEntries = append(gotEntries, *e.Value.(*entityEntry)) gotElems = append(gotElems, e) } c.Assert(gotEntries, gc.DeepEquals, entries) for i, ent := range entries { c.Assert(a.entities[ent.info.EntityId()], gc.Equals, gotElems[i]) } c.Assert(a.entities, gc.HasLen, len(entries)) c.Assert(a.latestRevno, gc.Equals, latestRevno) } var errTimeout = errors.New("no change received in sufficient time") func getNext(c *gc.C, w *Watcher, timeout time.Duration) ([]params.Delta, error) { var deltas []params.Delta var err error ch := make(chan struct{}, 1) go func() { deltas, err = w.Next() ch <- struct{}{} }() select { case <-ch: return deltas, err case <-time.After(1 * time.Second): } return nil, errTimeout } func checkNext(c *gc.C, w *Watcher, deltas []params.Delta, expectErr string) { d, err := getNext(c, w, 1*time.Second) if expectErr != "" { c.Check(err, gc.ErrorMatches, expectErr) return } checkDeltasEqual(c, d, deltas) } // deltas are returns in arbitrary order, so we compare // them as sets. func checkDeltasEqual(c *gc.C, d0, d1 []params.Delta) { c.Check(deltaMap(d0), gc.DeepEquals, deltaMap(d1)) } func deltaMap(deltas []params.Delta) map[InfoId]params.EntityInfo { m := make(map[InfoId]params.EntityInfo) for _, d := range deltas { id := d.Entity.EntityId() if _, ok := m[id]; ok { panic(fmt.Errorf("%v mentioned twice in delta set", id)) } if d.Removed { m[id] = nil } else { m[id] = d.Entity } } return m } // watcherState represents a Watcher client's // current view of the state. It holds the last delta that a given // state watcher has seen for each entity. type watcherState map[InfoId]params.Delta func (s watcherState) update(changes []params.Delta) { for _, d := range changes { id := d.Entity.EntityId() if d.Removed { if _, ok := s[id]; !ok { panic(fmt.Errorf("entity id %v removed when it wasn't there", id)) } delete(s, id) } else { s[id] = d } } } // check checks that the watcher state matches that // held in current. func (s watcherState) check(c *gc.C, current *Store) { currentEntities := make(watcherState) for id, elem := range current.entities { entry := elem.Value.(*entityEntry) if !entry.removed { currentEntities[id] = params.Delta{Entity: entry.info} } } c.Assert(s, gc.DeepEquals, currentEntities) } func assertNotReplied(c *gc.C, req *request) { select { case v := <-req.reply: c.Fatalf("request was unexpectedly replied to (got %v)", v) default: } } func assertReplied(c *gc.C, val bool, req *request) { select { case v := <-req.reply: c.Assert(v, gc.Equals, val) default: c.Fatalf("request was not replied to") } } func assertWaitingRequests(c *gc.C, sm *StoreManager, waiting map[*Watcher][]*request) { c.Assert(sm.waiting, gc.HasLen, len(waiting)) for w, reqs := range waiting { i := 0 for req := sm.waiting[w]; ; req = req.next { if i >= len(reqs) { c.Assert(req, gc.IsNil) break } c.Assert(req, gc.Equals, reqs[i]) assertNotReplied(c, req) i++ } } } type storeManagerTestBacking struct { mu sync.Mutex fetchErr error entities map[InfoId]params.EntityInfo watchc chan<- watcher.Change txnRevno int64 } func newTestBacking(initial []params.EntityInfo) *storeManagerTestBacking { b := &storeManagerTestBacking{ entities: make(map[InfoId]params.EntityInfo), } for _, info := range initial { b.entities[info.EntityId()] = info } return b } func (b *storeManagerTestBacking) Changed(all *Store, change watcher.Change) error { id := params.EntityId{ Kind: change.C, Id: change.Id.(string), } info, err := b.fetch(id) if err == mgo.ErrNotFound { all.Remove(id) return nil } if err != nil { return err } all.Update(info) return nil } func (b *storeManagerTestBacking) fetch(id InfoId) (params.EntityInfo, error) { b.mu.Lock() defer b.mu.Unlock() if b.fetchErr != nil { return nil, b.fetchErr } if info, ok := b.entities[id]; ok { return info, nil } return nil, mgo.ErrNotFound } func (b *storeManagerTestBacking) Watch(c chan<- watcher.Change) { b.mu.Lock() defer b.mu.Unlock() if b.watchc != nil { panic("test backing can only watch once") } b.watchc = c } func (b *storeManagerTestBacking) Unwatch(c chan<- watcher.Change) { b.mu.Lock() defer b.mu.Unlock() if c != b.watchc { panic("unwatching wrong channel") } b.watchc = nil } func (b *storeManagerTestBacking) GetAll(all *Store) error { b.mu.Lock() defer b.mu.Unlock() for _, info := range b.entities { all.Update(info) } return nil } func (b *storeManagerTestBacking) updateEntity(info params.EntityInfo) { b.mu.Lock() defer b.mu.Unlock() id := info.EntityId() b.entities[id] = info b.txnRevno++ if b.watchc != nil { b.watchc <- watcher.Change{ C: id.Kind, Id: id.Id, Revno: b.txnRevno, // This is actually ignored, but fill it in anyway. } } } func (b *storeManagerTestBacking) setFetchError(err error) { b.mu.Lock() defer b.mu.Unlock() b.fetchErr = err } func (b *storeManagerTestBacking) deleteEntity(id0 InfoId) { id := id0.(params.EntityId) b.mu.Lock() defer b.mu.Unlock() delete(b.entities, id) b.txnRevno++ if b.watchc != nil { b.watchc <- watcher.Change{ C: id.Kind, Id: id.Id, Revno: -1, } } } type MachineInfo struct { Id string InstanceId string } func (i *MachineInfo) EntityId() params.EntityId { return params.EntityId{ Kind: "machine", Id: i.Id, } } type ServiceInfo struct { Name string Exposed bool } func (i *ServiceInfo) EntityId() params.EntityId { return params.EntityId{ Kind: "service", Id: i.Name, } } ����������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/state.go�����������������������������������������0000644�0000153�0000161�00000126350�12321735776�023473� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The state package enables reading, observing, and changing // the state stored in MongoDB of a whole environment // managed by juju. package state import ( "fmt" "net/url" "regexp" "sort" "strconv" "strings" "sync" "github.com/juju/loggo" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/multiwatcher" "launchpad.net/juju-core/state/presence" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.state") // BootstrapNonce is used as a nonce for the state server machine. const ( BootstrapNonce = "user-admin:bootstrap" AdminUser = "admin" ) // State represents the state of an environment // managed by juju. type State struct { info *Info policy Policy db *mgo.Database environments *mgo.Collection charms *mgo.Collection machines *mgo.Collection instanceData *mgo.Collection containerRefs *mgo.Collection relations *mgo.Collection relationScopes *mgo.Collection services *mgo.Collection networks *mgo.Collection minUnits *mgo.Collection settings *mgo.Collection settingsrefs *mgo.Collection constraints *mgo.Collection units *mgo.Collection users *mgo.Collection presence *mgo.Collection cleanups *mgo.Collection annotations *mgo.Collection statuses *mgo.Collection stateServers *mgo.Collection runner *txn.Runner transactionHooks chan ([]transactionHook) watcher *watcher.Watcher pwatcher *presence.Watcher // mu guards allManager. mu sync.Mutex allManager *multiwatcher.StoreManager } // transactionHook holds a pair of functions to be called before and after a // mgo/txn transaction is run. It is only used in testing. type transactionHook struct { Before func() After func() } // runTransaction runs the supplied operations as a single mgo/txn transaction, // and includes a mechanism whereby tests can use SetTransactionHooks to induce // arbitrary state mutations before and after particular transactions. func (st *State) runTransaction(ops []txn.Op) error { transactionHooks := <-st.transactionHooks st.transactionHooks <- nil if len(transactionHooks) > 0 { // Note that this code should only ever be triggered // during tests. If we see the log messages below // in a production run, something is wrong. defer func() { if transactionHooks[0].After != nil { logger.Infof("transaction 'after' hook start") transactionHooks[0].After() logger.Infof("transaction 'after' hook end") } if <-st.transactionHooks != nil { panic("concurrent use of transaction hooks") } st.transactionHooks <- transactionHooks[1:] }() if transactionHooks[0].Before != nil { logger.Infof("transaction 'before' hook start") transactionHooks[0].Before() logger.Infof("transaction 'before' hook end") } } return st.runner.Run(ops, "", nil) } // Ping probes the state's database connection to ensure // that it is still alive. func (st *State) Ping() error { return st.db.Session.Ping() } // MongoSession returns the underlying mongodb session // used by the state. It is exposed so that external code // can maintain the mongo replica set and should not // otherwise be used. func (st *State) MongoSession() *mgo.Session { return st.db.Session } func (st *State) Watch() *multiwatcher.Watcher { st.mu.Lock() if st.allManager == nil { st.allManager = multiwatcher.NewStoreManager(newAllWatcherStateBacking(st)) } st.mu.Unlock() return multiwatcher.NewWatcher(st.allManager) } func (st *State) EnvironConfig() (*config.Config, error) { settings, err := readSettings(st, environGlobalKey) if err != nil { return nil, err } attrs := settings.Map() return config.New(config.NoDefaults, attrs) } // checkEnvironConfig returns an error if the config is definitely invalid. func checkEnvironConfig(cfg *config.Config) error { if cfg.AdminSecret() != "" { return fmt.Errorf("admin-secret should never be written to the state") } if _, ok := cfg.AgentVersion(); !ok { return fmt.Errorf("agent-version must always be set in state") } return nil } // versionInconsistentError indicates one or more agents have a // different version from the current one (even empty, when not yet // set). type versionInconsistentError struct { currentVersion version.Number agents []string } func (e *versionInconsistentError) Error() string { sort.Strings(e.agents) return fmt.Sprintf("some agents have not upgraded to the current environment version %s: %s", e.currentVersion, strings.Join(e.agents, ", ")) } // newVersionInconsistentError returns a new instance of // versionInconsistentError. func newVersionInconsistentError(currentVersion version.Number, agents []string) *versionInconsistentError { return &versionInconsistentError{currentVersion, agents} } // IsVersionInconsistentError returns if the given error is // versionInconsistentError. func IsVersionInconsistentError(e interface{}) bool { _, ok := e.(*versionInconsistentError) return ok } func (st *State) checkCanUpgrade(currentVersion, newVersion string) error { matchCurrent := "^" + regexp.QuoteMeta(currentVersion) + "-" matchNew := "^" + regexp.QuoteMeta(newVersion) + "-" // Get all machines and units with a different or empty version. sel := bson.D{{"$or", []bson.D{ {{"tools", bson.D{{"$exists", false}}}}, {{"$and", []bson.D{ {{"tools.version", bson.D{{"$not", bson.RegEx{matchCurrent, ""}}}}}, {{"tools.version", bson.D{{"$not", bson.RegEx{matchNew, ""}}}}}, }}}, }}} var agentTags []string for _, collection := range []*mgo.Collection{st.machines, st.units} { var doc struct { Id string `bson:"_id"` } iter := collection.Find(sel).Select(bson.D{{"_id", 1}}).Iter() for iter.Next(&doc) { switch collection.Name { case "machines": agentTags = append(agentTags, names.MachineTag(doc.Id)) case "units": agentTags = append(agentTags, names.UnitTag(doc.Id)) } } if err := iter.Err(); err != nil { return err } } if len(agentTags) > 0 { return newVersionInconsistentError(version.MustParse(currentVersion), agentTags) } return nil } // SetEnvironAgentVersion changes the agent version for the // environment to the given version, only if the environment is in a // stable state (all agents are running the current version). func (st *State) SetEnvironAgentVersion(newVersion version.Number) error { for i := 0; i < 5; i++ { settings, err := readSettings(st, environGlobalKey) if err != nil { return err } agentVersion, ok := settings.Get("agent-version") if !ok { return fmt.Errorf("no agent version set in the environment") } currentVersion, ok := agentVersion.(string) if !ok { return fmt.Errorf("invalid agent version format: expected string, got %v", agentVersion) } if newVersion.String() == currentVersion { // Nothing to do. return nil } if err := st.checkCanUpgrade(currentVersion, newVersion.String()); err != nil { return err } ops := []txn.Op{{ C: st.settings.Name, Id: environGlobalKey, Assert: bson.D{{"txn-revno", settings.txnRevno}}, Update: bson.D{{"$set", bson.D{{"agent-version", newVersion.String()}}}}, }} if err := st.runTransaction(ops); err == nil { return nil } else if err != txn.ErrAborted { return fmt.Errorf("cannot set agent-version: %v", err) } } return ErrExcessiveContention } func (st *State) buildAndValidateEnvironConfig(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) (validCfg *config.Config, err error) { newConfig, err := oldConfig.Apply(updateAttrs) if err != nil { return nil, err } if len(removeAttrs) != 0 { newConfig, err = newConfig.Remove(removeAttrs) if err != nil { return nil, err } } if err := checkEnvironConfig(newConfig); err != nil { return nil, err } return st.validate(newConfig, oldConfig) } type ValidateConfigFunc func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error // UpateEnvironConfig adds, updates or removes attributes in the current // configuration of the environment with the provided updateAttrs and // removeAttrs. func (st *State) UpdateEnvironConfig(updateAttrs map[string]interface{}, removeAttrs []string, additionalValidation ValidateConfigFunc) error { if len(updateAttrs)+len(removeAttrs) == 0 { return nil } // TODO(axw) 2013-12-6 #1167616 // Ensure that the settings on disk have not changed // underneath us. The settings changes are actually // applied as a delta to what's on disk; if there has // been a concurrent update, the change may not be what // the user asked for. settings, err := readSettings(st, environGlobalKey) if err != nil { return err } // Get the existing environment config from state. oldConfig, err := config.New(config.NoDefaults, settings.Map()) if err != nil { return err } if additionalValidation != nil { err = additionalValidation(updateAttrs, removeAttrs, oldConfig) if err != nil { return err } } validCfg, err := st.buildAndValidateEnvironConfig(updateAttrs, removeAttrs, oldConfig) if err != nil { return err } validAttrs := validCfg.AllAttrs() for k, _ := range oldConfig.AllAttrs() { if _, ok := validAttrs[k]; !ok { settings.Delete(k) } } settings.Update(validAttrs) _, err = settings.Write() return err } // EnvironConstraints returns the current environment constraints. func (st *State) EnvironConstraints() (constraints.Value, error) { return readConstraints(st, environGlobalKey) } // SetEnvironConstraints replaces the current environment constraints. func (st *State) SetEnvironConstraints(cons constraints.Value) error { return writeConstraints(st, environGlobalKey, cons) } var errDead = fmt.Errorf("not found or dead") var errNotAlive = fmt.Errorf("not found or not alive") func onAbort(txnErr, err error) error { if txnErr == txn.ErrAborted { return err } return txnErr } // AllMachines returns all machines in the environment // ordered by id. func (st *State) AllMachines() (machines []*Machine, err error) { mdocs := machineDocSlice{} err = st.machines.Find(nil).All(&mdocs) if err != nil { return nil, fmt.Errorf("cannot get all machines: %v", err) } sort.Sort(mdocs) for _, doc := range mdocs { machines = append(machines, newMachine(st, &doc)) } return } type machineDocSlice []machineDoc func (ms machineDocSlice) Len() int { return len(ms) } func (ms machineDocSlice) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] } func (ms machineDocSlice) Less(i, j int) bool { return machineIdLessThan(ms[i].Id, ms[j].Id) } // machineIdLessThan returns true if id1 < id2, false otherwise. // Machine ids may include "/" separators if they are for a container so // the comparison is done by comparing the id component values from // left to right (most significant part to least significant). Ids for // host machines are always less than ids for their containers. func machineIdLessThan(id1, id2 string) bool { // Most times, we are dealing with host machines and not containers, so we will // try interpreting the ids as ints - this will be faster than dealing with the // container ids below. mint1, err1 := strconv.Atoi(id1) mint2, err2 := strconv.Atoi(id2) if err1 == nil && err2 == nil { return mint1 < mint2 } // We have at least one container id so it gets complicated. idParts1 := strings.Split(id1, "/") idParts2 := strings.Split(id2, "/") nrParts1 := len(idParts1) nrParts2 := len(idParts2) minLen := nrParts1 if nrParts2 < minLen { minLen = nrParts2 } for x := 0; x < minLen; x++ { m1 := idParts1[x] m2 := idParts2[x] if m1 == m2 { continue } // See if the id part is a container type, and if so compare directly. if x%2 == 1 { return m1 < m2 } // Compare the integer ids. // There's nothing we can do with errors at this point. mint1, _ := strconv.Atoi(m1) mint2, _ := strconv.Atoi(m2) return mint1 < mint2 } return nrParts1 < nrParts2 } // Machine returns the machine with the given id. func (st *State) Machine(id string) (*Machine, error) { mdoc := &machineDoc{} sel := bson.D{{"_id", id}} err := st.machines.Find(sel).One(mdoc) if err == mgo.ErrNotFound { return nil, errors.NotFoundf("machine %s", id) } if err != nil { return nil, fmt.Errorf("cannot get machine %s: %v", id, err) } return newMachine(st, mdoc), nil } // FindEntity returns the entity with the given tag. // // The returned value can be of type *Machine, *Unit, // *User, *Service or *Environment, depending // on the tag. func (st *State) FindEntity(tag string) (Entity, error) { kind, id, err := names.ParseTag(tag, "") switch kind { case names.MachineTagKind: return st.Machine(id) case names.UnitTagKind: return st.Unit(id) case names.UserTagKind: return st.User(id) case names.ServiceTagKind: return st.Service(id) case names.EnvironTagKind: env, err := st.Environment() if err != nil { return nil, err } // Return an invalid entity error if the requested environment is not // the current one. if id != env.UUID() { if utils.IsValidUUIDString(id) { return nil, errors.NotFoundf("environment %q", id) } // TODO(axw) 2013-12-04 #1257587 // We should not accept environment tags that do not match the // environment's UUID. We accept anything for now, to cater // both for past usage, and for potentially supporting aliases. logger.Warningf("environment-tag does not match current environment UUID: %q != %q", id, env.UUID()) conf, err := st.EnvironConfig() if err != nil { logger.Warningf("EnvironConfig failed: %v", err) } else if id != conf.Name() { logger.Warningf("environment-tag does not match current environment name: %q != %q", id, conf.Name()) } } return env, nil case names.RelationTagKind: return st.KeyRelation(id) } return nil, err } // parseTag, given an entity tag, returns the collection name and id // of the entity document. func (st *State) parseTag(tag string) (coll string, id string, err error) { kind, id, err := names.ParseTag(tag, "") if err != nil { return "", "", err } switch kind { case names.MachineTagKind: coll = st.machines.Name case names.ServiceTagKind: coll = st.services.Name case names.UnitTagKind: coll = st.units.Name case names.UserTagKind: coll = st.users.Name case names.RelationTagKind: coll = st.relations.Name case names.EnvironTagKind: coll = st.environments.Name default: return "", "", fmt.Errorf("%q is not a valid collection tag", tag) } return coll, id, nil } // AddCharm adds the ch charm with curl to the state. bundleURL must // be set to a URL where the bundle for ch may be downloaded from. On // success the newly added charm state is returned. func (st *State) AddCharm(ch charm.Charm, curl *charm.URL, bundleURL *url.URL, bundleSha256 string) (stch *Charm, err error) { // The charm may already exist in state as a placeholder, so we // check for that situation and update the existing charm record // if necessary, otherwise add a new record. var existing charmDoc err = st.charms.Find(bson.D{{"_id", curl.String()}, {"placeholder", true}}).One(&existing) if err == mgo.ErrNotFound { cdoc := &charmDoc{ URL: curl, Meta: ch.Meta(), Config: ch.Config(), BundleURL: bundleURL, BundleSha256: bundleSha256, } err = st.charms.Insert(cdoc) if err != nil { return nil, fmt.Errorf("cannot add charm %q: %v", curl, err) } return newCharm(st, cdoc) } else if err != nil { return nil, err } return st.updateCharmDoc(ch, curl, bundleURL, bundleSha256, stillPlaceholder) } // Charm returns the charm with the given URL. Charms pending upload // to storage and placeholders are never returned. func (st *State) Charm(curl *charm.URL) (*Charm, error) { cdoc := &charmDoc{} what := bson.D{ {"_id", curl}, {"placeholder", bson.D{{"$ne", true}}}, {"pendingupload", bson.D{{"$ne", true}}}, } err := st.charms.Find(what).One(&cdoc) if err == mgo.ErrNotFound { return nil, errors.NotFoundf("charm %q", curl) } if err != nil { return nil, fmt.Errorf("cannot get charm %q: %v", curl, err) } if err := cdoc.Meta.Check(); err != nil { return nil, fmt.Errorf("malformed charm metadata found in state: %v", err) } return newCharm(st, cdoc) } // LatestPlaceholderCharm returns the latest charm described by the // given URL but which is not yet deployed. func (st *State) LatestPlaceholderCharm(curl *charm.URL) (*Charm, error) { noRevURL := curl.WithRevision(-1) curlRegex := "^" + regexp.QuoteMeta(noRevURL.String()) var docs []charmDoc err := st.charms.Find(bson.D{{"_id", bson.D{{"$regex", curlRegex}}}, {"placeholder", true}}).All(&docs) if err != nil { return nil, fmt.Errorf("cannot get charm %q: %v", curl, err) } // Find the highest revision. var latest charmDoc for _, doc := range docs { if latest.URL == nil || doc.URL.Revision > latest.URL.Revision { latest = doc } } if latest.URL == nil { return nil, errors.NotFoundf("placeholder charm %q", noRevURL) } return newCharm(st, &latest) } // PrepareLocalCharmUpload must be called before a local charm is // uploaded to the provider storage in order to create a charm // document in state. It returns the chosen unique charm URL reserved // in state for the charm. // // The url's schema must be "local" and it must include a revision. func (st *State) PrepareLocalCharmUpload(curl *charm.URL) (chosenUrl *charm.URL, err error) { // Perform a few sanity checks first. if curl.Schema != "local" { return nil, fmt.Errorf("expected charm URL with local schema, got %q", curl) } if curl.Revision < 0 { return nil, fmt.Errorf("expected charm URL with revision, got %q", curl) } // Get a regex with the charm URL and no revision. noRevURL := curl.WithRevision(-1) curlRegex := "^" + regexp.QuoteMeta(noRevURL.String()) for attempt := 0; attempt < 3; attempt++ { // Find the highest revision of that charm in state. var docs []charmDoc err = st.charms.Find(bson.D{{"_id", bson.D{{"$regex", curlRegex}}}}).Select(bson.D{{"_id", 1}}).All(&docs) if err != nil { return nil, err } // Find the highest revision. maxRevision := -1 for _, doc := range docs { if doc.URL.Revision > maxRevision { maxRevision = doc.URL.Revision } } // Respect the local charm's revision first. chosenRevision := curl.Revision if maxRevision >= chosenRevision { // More recent revision exists in state, pick the next. chosenRevision = maxRevision + 1 } chosenUrl = curl.WithRevision(chosenRevision) uploadedCharm := &charmDoc{ URL: chosenUrl, PendingUpload: true, } ops := []txn.Op{{ C: st.charms.Name, Id: uploadedCharm.URL, Assert: txn.DocMissing, Insert: uploadedCharm, }} // Run the transaction, and retry on abort. if err = st.runTransaction(ops); err == txn.ErrAborted { continue } else if err != nil { return nil, err } break } if err != nil { return nil, ErrExcessiveContention } return chosenUrl, nil } // PrepareStoreCharmUpload must be called before a charm store charm // is uploaded to the provider storage in order to create a charm // document in state. If a charm with the same URL is already in // state, it will be returned as a *state.Charm (is can be still // pending or already uploaded). Otherwise, a new charm document is // added in state with just the given charm URL and // PendingUpload=true, which is then returned as a *state.Charm. // // The url's schema must be "cs" and it must include a revision. func (st *State) PrepareStoreCharmUpload(curl *charm.URL) (*Charm, error) { // Perform a few sanity checks first. if curl.Schema != "cs" { return nil, fmt.Errorf("expected charm URL with cs schema, got %q", curl) } if curl.Revision < 0 { return nil, fmt.Errorf("expected charm URL with revision, got %q", curl) } var ( uploadedCharm charmDoc err error ) for attempt := 0; attempt < 3; attempt++ { // Find an uploaded or pending charm with the given exact curl. err = st.charms.FindId(curl).One(&uploadedCharm) if err != nil && err != mgo.ErrNotFound { return nil, err } else if err == nil && !uploadedCharm.Placeholder { // The charm exists and it's either uploaded or still // pending, but it's not a placeholder. In any case, we // just return what we got. return newCharm(st, &uploadedCharm) } else if err == mgo.ErrNotFound { // Prepare the pending charm document for insertion. uploadedCharm = charmDoc{ URL: curl, PendingUpload: true, Placeholder: false, } } var ops []txn.Op if uploadedCharm.Placeholder { // Convert the placeholder to a pending charm, while // asserting the fields updated after an upload have not // changed yet. ops = []txn.Op{{ C: st.charms.Name, Id: curl, Assert: bson.D{ {"bundlesha256", ""}, {"pendingupload", false}, {"placeholder", true}, }, Update: bson.D{{"$set", bson.D{ {"pendingupload", true}, {"placeholder", false}, }}}, }} // Update the fields of the document we're returning. uploadedCharm.PendingUpload = true uploadedCharm.Placeholder = false } else { // No charm document with this curl yet, insert it. ops = []txn.Op{{ C: st.charms.Name, Id: curl, Assert: txn.DocMissing, Insert: uploadedCharm, }} } // Run the transaction, and retry on abort. err = st.runTransaction(ops) if err == txn.ErrAborted { continue } else if err != nil { return nil, err } else if err == nil { return newCharm(st, &uploadedCharm) } } return nil, ErrExcessiveContention } var ( stillPending = bson.D{{"pendingupload", true}} stillPlaceholder = bson.D{{"placeholder", true}} ) // AddStoreCharmPlaceholder creates a charm document in state for the given charm URL which // must reference a charm from the store. The charm document is marked as a placeholder which // means that if the charm is to be deployed, it will need to first be uploaded to env storage. func (st *State) AddStoreCharmPlaceholder(curl *charm.URL) (err error) { // Perform sanity checks first. if curl.Schema != "cs" { return fmt.Errorf("expected charm URL with cs schema, got %q", curl) } if curl.Revision < 0 { return fmt.Errorf("expected charm URL with revision, got %q", curl) } for attempt := 0; attempt < 3; attempt++ { // See if the charm already exists in state and exit early if that's the case. var doc charmDoc err = st.charms.Find(bson.D{{"_id", curl.String()}}).Select(bson.D{{"_id", 1}}).One(&doc) if err != nil && err != mgo.ErrNotFound { return err } if err == nil { return nil } // Delete all previous placeholders so we don't fill up the database with unused data. var ops []txn.Op ops, err = st.deleteOldPlaceholderCharmsOps(curl) if err != nil { return nil } // Add the new charm doc. placeholderCharm := &charmDoc{ URL: curl, Placeholder: true, } ops = append(ops, txn.Op{ C: st.charms.Name, Id: placeholderCharm.URL.String(), Assert: txn.DocMissing, Insert: placeholderCharm, }) // Run the transaction, and retry on abort. if err = st.runTransaction(ops); err == txn.ErrAborted { continue } else if err != nil { return err } break } if err != nil { return ErrExcessiveContention } return nil } // deleteOldPlaceholderCharmsOps returns the txn ops required to delete all placeholder charm // records older than the specified charm URL. func (st *State) deleteOldPlaceholderCharmsOps(curl *charm.URL) ([]txn.Op, error) { // Get a regex with the charm URL and no revision. noRevURL := curl.WithRevision(-1) curlRegex := "^" + regexp.QuoteMeta(noRevURL.String()) var docs []charmDoc err := st.charms.Find( bson.D{{"_id", bson.D{{"$regex", curlRegex}}}, {"placeholder", true}}).Select(bson.D{{"_id", 1}}).All(&docs) if err != nil { return nil, err } var ops []txn.Op for _, doc := range docs { if doc.URL.Revision >= curl.Revision { continue } ops = append(ops, txn.Op{ C: st.charms.Name, Id: doc.URL.String(), Assert: stillPlaceholder, Remove: true, }) } return ops, nil } // ErrCharmAlreadyUploaded is returned by UpdateUploadedCharm() when // the given charm is already uploaded and marked as not pending in // state. type ErrCharmAlreadyUploaded struct { curl *charm.URL } func (e *ErrCharmAlreadyUploaded) Error() string { return fmt.Sprintf("charm %q already uploaded", e.curl) } // IsCharmAlreadyUploadedError returns if the given error is // ErrCharmAlreadyUploaded. func IsCharmAlreadyUploadedError(err interface{}) bool { if err == nil { return false } _, ok := err.(*ErrCharmAlreadyUploaded) return ok } // ErrCharmRevisionAlreadyModified is returned when a pending or // placeholder charm is no longer pending or a placeholder, signaling // the charm is available in state with its full information. var ErrCharmRevisionAlreadyModified = fmt.Errorf("charm revision already modified") // UpdateUploadedCharm marks the given charm URL as uploaded and // updates the rest of its data, returning it as *state.Charm. func (st *State) UpdateUploadedCharm(ch charm.Charm, curl *charm.URL, bundleURL *url.URL, bundleSha256 string) (*Charm, error) { doc := &charmDoc{} err := st.charms.FindId(curl).One(&doc) if err == mgo.ErrNotFound { return nil, errors.NotFoundf("charm %q", curl) } if err != nil { return nil, err } if !doc.PendingUpload { return nil, &ErrCharmAlreadyUploaded{curl} } return st.updateCharmDoc(ch, curl, bundleURL, bundleSha256, stillPending) } // updateCharmDoc updates the charm with specified URL with the given // data, and resets the placeholder and pendingupdate flags. If the // charm is no longer a placeholder or pending (depending on preReq), // it returns ErrCharmRevisionAlreadyModified. func (st *State) updateCharmDoc( ch charm.Charm, curl *charm.URL, bundleURL *url.URL, bundleSha256 string, preReq interface{}) (*Charm, error) { updateFields := bson.D{{"$set", bson.D{ {"meta", ch.Meta()}, {"config", ch.Config()}, {"bundleurl", bundleURL}, {"bundlesha256", bundleSha256}, {"pendingupload", false}, {"placeholder", false}, }}} ops := []txn.Op{{ C: st.charms.Name, Id: curl, Assert: preReq, Update: updateFields, }} if err := st.runTransaction(ops); err != nil { return nil, onAbort(err, ErrCharmRevisionAlreadyModified) } return st.Charm(curl) } // addPeerRelationsOps returns the operations necessary to add the // specified service peer relations to the state. func (st *State) addPeerRelationsOps(serviceName string, peers map[string]charm.Relation) ([]txn.Op, error) { var ops []txn.Op for _, rel := range peers { relId, err := st.sequence("relation") if err != nil { return nil, err } eps := []Endpoint{{ ServiceName: serviceName, Relation: rel, }} relKey := relationKey(eps) relDoc := &relationDoc{ Key: relKey, Id: relId, Endpoints: eps, Life: Alive, } ops = append(ops, txn.Op{ C: st.relations.Name, Id: relKey, Assert: txn.DocMissing, Insert: relDoc, }) } return ops, nil } // AddService creates a new service, running the supplied charm, with the // supplied name (which must be unique). If the charm defines peer relations, // they will be created automatically. func (st *State) AddService(name, ownerTag string, ch *Charm, includeNetworks, excludeNetworks []string) (service *Service, err error) { defer utils.ErrorContextf(&err, "cannot add service %q", name) kind, ownerId, err := names.ParseTag(ownerTag, names.UserTagKind) if err != nil || kind != names.UserTagKind { return nil, fmt.Errorf("Invalid ownertag %s", ownerTag) } // Sanity checks. if !names.IsService(name) { return nil, fmt.Errorf("invalid name") } if ch == nil { return nil, fmt.Errorf("charm is nil") } if exists, err := isNotDead(st.services, name); err != nil { return nil, err } else if exists { return nil, fmt.Errorf("service already exists") } env, err := st.Environment() if err != nil { return nil, err } else if env.Life() != Alive { return nil, fmt.Errorf("environment is no longer alive") } if userExists, err := st.checkUserExists(ownerId); err != nil { return nil, err } else if !userExists { return nil, fmt.Errorf("user %v doesn't exist", ownerId) } // Create the service addition operations. peers := ch.Meta().Peers svcDoc := &serviceDoc{ Name: name, Series: ch.URL().Series, Subordinate: ch.Meta().Subordinate, CharmURL: ch.URL(), RelationCount: len(peers), Life: Alive, OwnerTag: ownerTag, } svc := newService(st, svcDoc) ops := []txn.Op{ env.assertAliveOp(), createConstraintsOp(st, svc.globalKey(), constraints.Value{}), createNetworksOp(st, svc.globalKey(), includeNetworks, excludeNetworks), createSettingsOp(st, svc.settingsKey(), nil), { C: st.users.Name, Id: ownerId, Assert: txn.DocExists, }, { C: st.settingsrefs.Name, Id: svc.settingsKey(), Assert: txn.DocMissing, Insert: settingsRefsDoc{1}, }, { C: st.services.Name, Id: name, Assert: txn.DocMissing, Insert: svcDoc, }} // Collect peer relation addition operations. peerOps, err := st.addPeerRelationsOps(name, peers) if err != nil { return nil, err } ops = append(ops, peerOps...) if err := st.runTransaction(ops); err == txn.ErrAborted { err := env.Refresh() if (err == nil && env.Life() != Alive) || errors.IsNotFoundError(err) { return nil, fmt.Errorf("environment is no longer alive") } else if err != nil { return nil, err } if userExists, ueErr := st.checkUserExists(ownerId); ueErr != nil { return nil, ueErr } else if !userExists { return nil, fmt.Errorf("unknown user %q", ownerId) } return nil, fmt.Errorf("service already exists") } else if err != nil { return nil, err } // Refresh to pick the txn-revno. if err = svc.Refresh(); err != nil { return nil, err } return svc, nil } // Service returns a service state by name. func (st *State) Service(name string) (service *Service, err error) { if !names.IsService(name) { return nil, fmt.Errorf("%q is not a valid service name", name) } sdoc := &serviceDoc{} sel := bson.D{{"_id", name}} err = st.services.Find(sel).One(sdoc) if err == mgo.ErrNotFound { return nil, errors.NotFoundf("service %q", name) } if err != nil { return nil, fmt.Errorf("cannot get service %q: %v", name, err) } return newService(st, sdoc), nil } // AllServices returns all deployed services in the environment. func (st *State) AllServices() (services []*Service, err error) { sdocs := []serviceDoc{} err = st.services.Find(bson.D{}).All(&sdocs) if err != nil { return nil, fmt.Errorf("cannot get all services") } for _, v := range sdocs { services = append(services, newService(st, &v)) } return services, nil } // InferEndpoints returns the endpoints corresponding to the supplied names. // There must be 1 or 2 supplied names, of the form <service>[:<relation>]. // If the supplied names uniquely specify a possible relation, or if they // uniquely specify a possible relation once all implicit relations have been // filtered, the endpoints corresponding to that relation will be returned. func (st *State) InferEndpoints(names []string) ([]Endpoint, error) { // Collect all possible sane endpoint lists. var candidates [][]Endpoint switch len(names) { case 1: eps, err := st.endpoints(names[0], isPeer) if err != nil { return nil, err } for _, ep := range eps { candidates = append(candidates, []Endpoint{ep}) } case 2: eps1, err := st.endpoints(names[0], notPeer) if err != nil { return nil, err } eps2, err := st.endpoints(names[1], notPeer) if err != nil { return nil, err } for _, ep1 := range eps1 { for _, ep2 := range eps2 { if ep1.CanRelateTo(ep2) { candidates = append(candidates, []Endpoint{ep1, ep2}) } } } default: return nil, fmt.Errorf("cannot relate %d endpoints", len(names)) } // If there's ambiguity, try discarding implicit relations. switch len(candidates) { case 0: return nil, fmt.Errorf("no relations found") case 1: return candidates[0], nil } var filtered [][]Endpoint outer: for _, cand := range candidates { for _, ep := range cand { if ep.IsImplicit() { continue outer } } filtered = append(filtered, cand) } if len(filtered) == 1 { return filtered[0], nil } keys := []string{} for _, cand := range candidates { keys = append(keys, fmt.Sprintf("%q", relationKey(cand))) } sort.Strings(keys) return nil, fmt.Errorf("ambiguous relation: %q could refer to %s", strings.Join(names, " "), strings.Join(keys, "; ")) } func isPeer(ep Endpoint) bool { return ep.Role == charm.RolePeer } func notPeer(ep Endpoint) bool { return ep.Role != charm.RolePeer } // endpoints returns all endpoints that could be intended by the // supplied endpoint name, and which cause the filter param to // return true. func (st *State) endpoints(name string, filter func(ep Endpoint) bool) ([]Endpoint, error) { var svcName, relName string if i := strings.Index(name, ":"); i == -1 { svcName = name } else if i != 0 && i != len(name)-1 { svcName = name[:i] relName = name[i+1:] } else { return nil, fmt.Errorf("invalid endpoint %q", name) } svc, err := st.Service(svcName) if err != nil { return nil, err } eps := []Endpoint{} if relName != "" { ep, err := svc.Endpoint(relName) if err != nil { return nil, err } eps = append(eps, ep) } else { eps, err = svc.Endpoints() if err != nil { return nil, err } } final := []Endpoint{} for _, ep := range eps { if filter(ep) { final = append(final, ep) } } return final, nil } // AddRelation creates a new relation with the given endpoints. func (st *State) AddRelation(eps ...Endpoint) (r *Relation, err error) { key := relationKey(eps) defer utils.ErrorContextf(&err, "cannot add relation %q", key) // Enforce basic endpoint sanity. The epCount restrictions may be relaxed // in the future; if so, this method is likely to need significant rework. if len(eps) != 2 { return nil, fmt.Errorf("relation must have two endpoints") } if !eps[0].CanRelateTo(eps[1]) { return nil, fmt.Errorf("endpoints do not relate") } // If either endpoint has container scope, so must the other; and the // services's series must also match, because they'll be deployed to // the same machines. matchSeries := true if eps[0].Scope == charm.ScopeContainer { eps[1].Scope = charm.ScopeContainer } else if eps[1].Scope == charm.ScopeContainer { eps[0].Scope = charm.ScopeContainer } else { matchSeries = false } // We only get a unique relation id once, to save on roundtrips. If it's // -1, we haven't got it yet (we don't get it at this stage, because we // still don't know whether it's sane to even attempt creation). id := -1 // If a service's charm is upgraded while we're trying to add a relation, // we'll need to re-validate service sanity. This is probably relatively // rare, so we only try 3 times. for attempt := 0; attempt < 3; attempt++ { // Perform initial relation sanity check. if exists, err := isNotDead(st.relations, key); err != nil { return nil, err } else if exists { return nil, fmt.Errorf("relation already exists") } // Collect per-service operations, checking sanity as we go. var ops []txn.Op series := map[string]bool{} for _, ep := range eps { svc, err := st.Service(ep.ServiceName) if errors.IsNotFoundError(err) { return nil, fmt.Errorf("service %q does not exist", ep.ServiceName) } else if err != nil { return nil, err } else if svc.doc.Life != Alive { return nil, fmt.Errorf("service %q is not alive", ep.ServiceName) } series[svc.doc.Series] = true ch, _, err := svc.Charm() if err != nil { return nil, err } if !ep.ImplementedBy(ch) { return nil, fmt.Errorf("%q does not implement %q", ep.ServiceName, ep) } ops = append(ops, txn.Op{ C: st.services.Name, Id: ep.ServiceName, Assert: bson.D{{"life", Alive}, {"charmurl", ch.URL()}}, Update: bson.D{{"$inc", bson.D{{"relationcount", 1}}}}, }) } if matchSeries && len(series) != 1 { return nil, fmt.Errorf("principal and subordinate services' series must match") } // Create a new unique id if that has not already been done, and add // an operation to create the relation document. if id == -1 { var err error if id, err = st.sequence("relation"); err != nil { return nil, err } } doc := &relationDoc{ Key: key, Id: id, Endpoints: eps, Life: Alive, } ops = append(ops, txn.Op{ C: st.relations.Name, Id: doc.Key, Assert: txn.DocMissing, Insert: doc, }) // Run the transaction, and retry on abort. if err = st.runTransaction(ops); err == txn.ErrAborted { continue } else if err != nil { return nil, err } return &Relation{st, *doc}, nil } return nil, ErrExcessiveContention } // EndpointsRelation returns the existing relation with the given endpoints. func (st *State) EndpointsRelation(endpoints ...Endpoint) (*Relation, error) { return st.KeyRelation(relationKey(endpoints)) } // KeyRelation returns the existing relation with the given key (which can // be derived unambiguously from the relation's endpoints). func (st *State) KeyRelation(key string) (*Relation, error) { doc := relationDoc{} err := st.relations.Find(bson.D{{"_id", key}}).One(&doc) if err == mgo.ErrNotFound { return nil, errors.NotFoundf("relation %q", key) } if err != nil { return nil, fmt.Errorf("cannot get relation %q: %v", key, err) } return newRelation(st, &doc), nil } // Relation returns the existing relation with the given id. func (st *State) Relation(id int) (*Relation, error) { doc := relationDoc{} err := st.relations.Find(bson.D{{"id", id}}).One(&doc) if err == mgo.ErrNotFound { return nil, errors.NotFoundf("relation %d", id) } if err != nil { return nil, fmt.Errorf("cannot get relation %d: %v", id, err) } return newRelation(st, &doc), nil } // Unit returns a unit by name. func (st *State) Unit(name string) (*Unit, error) { if !names.IsUnit(name) { return nil, fmt.Errorf("%q is not a valid unit name", name) } doc := unitDoc{} err := st.units.FindId(name).One(&doc) if err == mgo.ErrNotFound { return nil, errors.NotFoundf("unit %q", name) } if err != nil { return nil, fmt.Errorf("cannot get unit %q: %v", name, err) } return newUnit(st, &doc), nil } // AssignUnit places the unit on a machine. Depending on the policy, and the // state of the environment, this may lead to new instances being launched // within the environment. func (st *State) AssignUnit(u *Unit, policy AssignmentPolicy) (err error) { if !u.IsPrincipal() { return fmt.Errorf("subordinate unit %q cannot be assigned directly to a machine", u) } defer utils.ErrorContextf(&err, "cannot assign unit %q to machine", u) var m *Machine switch policy { case AssignLocal: m, err = st.Machine("0") if err != nil { return err } return u.AssignToMachine(m) case AssignClean: if _, err = u.AssignToCleanMachine(); err != noCleanMachines { return err } return u.AssignToNewMachineOrContainer() case AssignCleanEmpty: if _, err = u.AssignToCleanEmptyMachine(); err != noCleanMachines { return err } return u.AssignToNewMachineOrContainer() case AssignNew: return u.AssignToNewMachine() } return fmt.Errorf("unknown unit assignment policy: %q", policy) } // StartSync forces watchers to resynchronize their state with the // database immediately. This will happen periodically automatically. func (st *State) StartSync() { st.watcher.StartSync() st.pwatcher.Sync() } // SetAdminMongoPassword sets the administrative password // to access the state. If the password is non-empty, // all subsequent attempts to access the state must // be authorized; otherwise no authorization is required. func (st *State) SetAdminMongoPassword(password string) error { admin := st.db.Session.DB(AdminUser) if password != "" { // On 2.2+, we get a "need to login" error without a code when // adding the first user because we go from no-auth+no-login to // auth+no-login. Not great. Hopefully being fixed in 2.4. if err := admin.AddUser(AdminUser, password, false); err != nil && err.Error() != "need to login" { return fmt.Errorf("cannot set admin password: %v", err) } if err := admin.Login(AdminUser, password); err != nil { return fmt.Errorf("cannot login after setting password: %v", err) } } else { if err := admin.RemoveUser(AdminUser); err != nil && err != mgo.ErrNotFound { return fmt.Errorf("cannot disable admin password: %v", err) } } return nil } func (st *State) setMongoPassword(name, password string) error { if err := st.db.AddUser(name, password, false); err != nil { return fmt.Errorf("cannot set password in juju db for %q: %v", name, err) } if err := st.db.Session.DB("presence").AddUser(name, password, false); err != nil { return fmt.Errorf("cannot set password in presence db for %q: %v", name, err) } return nil } type stateServersDoc struct { Id string `bson:"_id"` MachineIds []string VotingMachineIds []string } // StateServerInfo holds information about currently // configured state server machines. type StateServerInfo struct { // MachineIds holds the ids of all machines configured // to run a state server. It includes all the machine // ids in VotingMachineIds. MachineIds []string // VotingMachineIds holds the ids of all machines // configured to run a state server and to have a vote // in peer election. VotingMachineIds []string } // StateServerInfo returns information about // the currently configured state server machines. func (st *State) StateServerInfo() (*StateServerInfo, error) { var doc stateServersDoc err := st.stateServers.Find(bson.D{{"_id", environGlobalKey}}).One(&doc) if err != nil { return nil, fmt.Errorf("cannot get state servers document: %v", err) } return &StateServerInfo{ MachineIds: doc.MachineIds, VotingMachineIds: doc.VotingMachineIds, }, nil } const stateServingInfoKey = "stateServingInfo" // StateServingInfo returns information for running a state server machine func (st *State) StateServingInfo() (params.StateServingInfo, error) { var info params.StateServingInfo err := st.stateServers.Find(bson.D{{"_id", stateServingInfoKey}}).One(&info) if err != nil { return info, err } return info, nil } // SetStateServingInfo stores information needed for running a state server func (st *State) SetStateServingInfo(info params.StateServingInfo) error { ops := []txn.Op{{ C: st.stateServers.Name, Id: stateServingInfoKey, Update: bson.D{{"$set", info}}, }} if err := st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set state serving info: %v", err) } return nil } // ResumeTransactions resumes all pending transactions. func (st *State) ResumeTransactions() error { return st.runner.ResumeAll() } var tagPrefix = map[byte]string{ 'm': names.MachineTagKind + "-", 's': names.ServiceTagKind + "-", 'u': names.UnitTagKind + "-", 'e': names.EnvironTagKind + "-", 'r': names.RelationTagKind + "-", } func tagForGlobalKey(key string) (string, bool) { if len(key) < 3 || key[1] != '#' { return "", false } p, ok := tagPrefix[key[0]] if !ok { return "", false } return p + key[2:], true } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/���������������������������������������0000755�0000153�0000161�00000000000�12321736000�023772� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/charmrevisionupdater/������������������0000755�0000153�0000161�00000000000�12321736000�030230� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/charmrevisionupdater/updater.go��������0000644�0000153�0000161�00000010273�12321735776�032251� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charmrevisionupdater import ( "github.com/juju/loggo" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/log" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" ) var logger = loggo.GetLogger("juju.state.apiserver.charmrevisionupdater") // CharmRevisionUpdater defines the methods on the charmrevisionupdater API end point. type CharmRevisionUpdater interface { UpdateLatestRevisions() (params.ErrorResult, error) } // CharmRevisionUpdaterAPI implements the CharmRevisionUpdater interface and is the concrete // implementation of the api end point. type CharmRevisionUpdaterAPI struct { state *state.State resources *common.Resources authorizer common.Authorizer } var _ CharmRevisionUpdater = (*CharmRevisionUpdaterAPI)(nil) // NewCharmRevisionUpdaterAPI creates a new server-side charmrevisionupdater API end point. func NewCharmRevisionUpdaterAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*CharmRevisionUpdaterAPI, error) { if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() { return nil, common.ErrPerm } return &CharmRevisionUpdaterAPI{ state: st, resources: resources, authorizer: authorizer}, nil } // UpdateLatestRevisions retrieves the latest revision information from the charm store for all deployed charms // and records this information in state. func (api *CharmRevisionUpdaterAPI) UpdateLatestRevisions() (params.ErrorResult, error) { // First get the uuid for the environment to use when querying the charm store. env, err := api.state.Environment() if err != nil { return params.ErrorResult{common.ServerError(err)}, nil } uuid := env.UUID() deployedCharms, err := fetchAllDeployedCharms(api.state) if err != nil { return params.ErrorResult{common.ServerError(err)}, nil } // Look up the revision information for all the deployed charms. curls, err := retrieveLatestCharmInfo(deployedCharms, uuid) if err != nil { return params.ErrorResult{common.ServerError(err)}, nil } // Add the charms and latest revision info to state as charm placeholders. for _, curl := range curls { if err = api.state.AddStoreCharmPlaceholder(curl); err != nil { return params.ErrorResult{common.ServerError(err)}, nil } } return params.ErrorResult{}, nil } // fetchAllServicesAndUnits returns a map from service name to service // and a map from service name to unit name to unit. func fetchAllDeployedCharms(st *state.State) (map[string]*charm.URL, error) { deployedCharms := make(map[string]*charm.URL) services, err := st.AllServices() if err != nil { return nil, err } for _, s := range services { url, _ := s.CharmURL() // Record the basic charm information so it can be bulk processed later to // get the available revision numbers from the repo. baseCharm := url.WithRevision(-1) deployedCharms[baseCharm.String()] = baseCharm } return deployedCharms, nil } // retrieveLatestCharmInfo looks up the charm store to return the charm URLs for the // latest revision of the deployed charms. func retrieveLatestCharmInfo(deployedCharms map[string]*charm.URL, uuid string) ([]*charm.URL, error) { var curls []*charm.URL for _, curl := range deployedCharms { if curl.Schema == "local" { // Version checking for charms from local repositories is not // currently supported, since we don't yet support passing in // a path to the local repo. This may change if the need arises. continue } curls = append(curls, curl) } // Do a bulk call to get the revision info for all charms. logger.Infof("retrieving revision information for %d charms", len(curls)) store := charm.Store.WithJujuAttrs("environment_uuid=" + uuid) revInfo, err := store.Latest(curls...) if err != nil { return nil, log.LoggedErrorf(logger, "finding charm revision info: %v", err) } var latestCurls []*charm.URL for i, info := range revInfo { curl := curls[i] if info.Err == nil { latestCurls = append(latestCurls, curl.WithRevision(info.Revision)) } else { logger.Errorf("retrieving charm info for %s: %v", curl, info.Err) } } return latestCurls, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/charmrevisionupdater/updater_test.go���0000644�0000153�0000161�00000007700�12321735642�033301� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charmrevisionupdater_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/apiserver/charmrevisionupdater" "launchpad.net/juju-core/state/apiserver/charmrevisionupdater/testing" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" ) type charmVersionSuite struct { testing.CharmSuite charmrevisionupdater *charmrevisionupdater.CharmRevisionUpdaterAPI resources *common.Resources authoriser apiservertesting.FakeAuthorizer } var _ = gc.Suite(&charmVersionSuite{}) func (s *charmVersionSuite) SetUpSuite(c *gc.C) { s.CharmSuite.SetUpSuite(c) } func (s *charmVersionSuite) SetUpTest(c *gc.C) { s.CharmSuite.SetUpTest(c) s.resources = common.NewResources() s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) s.authoriser = apiservertesting.FakeAuthorizer{ LoggedIn: true, EnvironManager: true, } var err error s.charmrevisionupdater, err = charmrevisionupdater.NewCharmRevisionUpdaterAPI(s.State, s.resources, s.authoriser) c.Assert(err, gc.IsNil) } func (s *charmVersionSuite) TestNewCharmRevisionUpdaterAPIAcceptsStateManager(c *gc.C) { endPoint, err := charmrevisionupdater.NewCharmRevisionUpdaterAPI(s.State, s.resources, s.authoriser) c.Assert(err, gc.IsNil) c.Assert(endPoint, gc.NotNil) } func (s *charmVersionSuite) TestNewCharmRevisionUpdaterAPIRefusesNonStateManager(c *gc.C) { anAuthoriser := s.authoriser anAuthoriser.EnvironManager = false endPoint, err := charmrevisionupdater.NewCharmRevisionUpdaterAPI(s.State, s.resources, anAuthoriser) c.Assert(endPoint, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *charmVersionSuite) TestUpdateRevisions(c *gc.C) { s.AddMachine(c, "0", state.JobManageEnviron) s.SetupScenario(c) curl := charm.MustParseURL("cs:quantal/mysql") _, err := s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) curl = charm.MustParseURL("cs:quantal/wordpress") _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) result, err := s.charmrevisionupdater.UpdateLatestRevisions() c.Assert(err, gc.IsNil) c.Assert(result.Error, gc.IsNil) curl = charm.MustParseURL("cs:quantal/mysql") pending, err := s.State.LatestPlaceholderCharm(curl) c.Assert(err, gc.IsNil) c.Assert(pending.String(), gc.Equals, "cs:quantal/mysql-23") // Latest wordpress is already deployed, so no pending charm. curl = charm.MustParseURL("cs:quantal/wordpress") _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // Varnish has an error when updating, so no pending charm. curl = charm.MustParseURL("cs:quantal/varnish") _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // Update mysql version and run update again. svc, err := s.State.Service("mysql") c.Assert(err, gc.IsNil) ch := s.AddCharmWithRevision(c, "mysql", 23) err = svc.SetCharm(ch, true) c.Assert(err, gc.IsNil) result, err = s.charmrevisionupdater.UpdateLatestRevisions() c.Assert(err, gc.IsNil) c.Assert(result.Error, gc.IsNil) // Latest mysql is now deployed, so no pending charm. curl = charm.MustParseURL("cs:quantal/mysql") _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *charmVersionSuite) TestEnvironmentUUIDUsed(c *gc.C) { s.AddMachine(c, "0", state.JobManageEnviron) s.SetupScenario(c) result, err := s.charmrevisionupdater.UpdateLatestRevisions() c.Assert(err, gc.IsNil) c.Assert(result.Error, gc.IsNil) env, err := s.State.Environment() c.Assert(err, gc.IsNil) c.Assert(s.Server.Metadata, gc.DeepEquals, []string{"environment_uuid=" + env.UUID()}) } ����������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/charmrevisionupdater/package_test.go���0000644�0000153�0000161�00000000404�12321735642�033222� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charmrevisionupdater_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/charmrevisionupdater/testing/����������0000755�0000153�0000161�00000000000�12321735642�031720� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/charmrevisionupdater/testing/suite.go��0000644�0000153�0000161�00000011221�12321735642�033375� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "fmt" "net/url" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" charmtesting "launchpad.net/juju-core/charm/testing" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" coretesting "launchpad.net/juju-core/testing" ) // CharmSuite provides infrastructure to set up and perform tests associated // with charm versioning. A mock charm store is created using some known charms // used for testing. type CharmSuite struct { jujutesting.JujuConnSuite Server *charmtesting.MockStore charms map[string]*state.Charm } func (s *CharmSuite) SetUpSuite(c *gc.C) { s.JujuConnSuite.SetUpSuite(c) s.Server = charmtesting.NewMockStore(c, map[string]int{ "cs:quantal/mysql": 23, "cs:quantal/dummy": 24, "cs:quantal/riak": 25, "cs:quantal/wordpress": 26, "cs:quantal/logging": 27, "cs:quantal/borken": 28, }) } func (s *CharmSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.PatchValue(&charm.CacheDir, c.MkDir()) s.PatchValue(&charm.Store, &charm.CharmStore{BaseURL: s.Server.Address()}) s.Server.Downloads = nil s.Server.Authorizations = nil s.Server.Metadata = nil s.charms = make(map[string]*state.Charm) } func (s *CharmSuite) TearDownSuite(c *gc.C) { s.Server.Close() s.JujuConnSuite.TearDownSuite(c) } // UpdateStoreRevision sets the revision of the specified charm to rev. func (s *CharmSuite) UpdateStoreRevision(ch string, rev int) { s.Server.UpdateStoreRevision(ch, rev) } // AddMachine adds a new machine to state. func (s *CharmSuite) AddMachine(c *gc.C, machineId string, job state.MachineJob) { m, err := s.State.AddOneMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{job}, }) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, machineId) cons, err := m.Constraints() c.Assert(err, gc.IsNil) inst, hc := jujutesting.AssertStartInstanceWithConstraints(c, s.Conn.Environ, m.Id(), cons) err = m.SetProvisioned(inst.Id(), "fake_nonce", hc) c.Assert(err, gc.IsNil) } // AddCharmWithRevision adds a charm with the specified revision to state. func (s *CharmSuite) AddCharmWithRevision(c *gc.C, charmName string, rev int) *state.Charm { ch := coretesting.Charms.Dir(charmName) name := ch.Meta().Name curl := charm.MustParseURL(fmt.Sprintf("cs:quantal/%s-%d", name, rev)) bundleURL, err := url.Parse(fmt.Sprintf("http://bundles.testing.invalid/%s-%d", name, rev)) c.Assert(err, gc.IsNil) dummy, err := s.State.AddCharm(ch, curl, bundleURL, fmt.Sprintf("%s-%d-sha256", name, rev)) c.Assert(err, gc.IsNil) s.charms[name] = dummy return dummy } // AddService adds a service for the specified charm to state. func (s *CharmSuite) AddService(c *gc.C, charmName, serviceName string, includeNetworks, excludeNetworks []string) { ch, ok := s.charms[charmName] c.Assert(ok, gc.Equals, true) _, err := s.State.AddService(serviceName, "user-admin", ch, includeNetworks, excludeNetworks) c.Assert(err, gc.IsNil) } // AddUnit adds a new unit for service to the specified machine. func (s *CharmSuite) AddUnit(c *gc.C, serviceName, machineId string) { svc, err := s.State.Service(serviceName) c.Assert(err, gc.IsNil) u, err := svc.AddUnit() c.Assert(err, gc.IsNil) m, err := s.State.Machine(machineId) c.Assert(err, gc.IsNil) err = u.AssignToMachine(m) c.Assert(err, gc.IsNil) } // SetUnitRevision sets the unit's charm to the specified revision. func (s *CharmSuite) SetUnitRevision(c *gc.C, unitName string, rev int) { u, err := s.State.Unit(unitName) c.Assert(err, gc.IsNil) svc, err := u.Service() c.Assert(err, gc.IsNil) curl := charm.MustParseURL(fmt.Sprintf("cs:quantal/%s-%d", svc.Name(), rev)) err = u.SetCharmURL(curl) c.Assert(err, gc.IsNil) } // SetupScenario adds some machines and services to state. // It assumes a state server machine has already been created. func (s *CharmSuite) SetupScenario(c *gc.C) { s.AddMachine(c, "1", state.JobHostUnits) s.AddMachine(c, "2", state.JobHostUnits) s.AddMachine(c, "3", state.JobHostUnits) // mysql is out of date s.AddCharmWithRevision(c, "mysql", 22) s.AddService(c, "mysql", "mysql", nil, nil) s.AddUnit(c, "mysql", "1") // wordpress is up to date s.AddCharmWithRevision(c, "wordpress", 26) s.AddService(c, "wordpress", "wordpress", nil, nil) s.AddUnit(c, "wordpress", "2") s.AddUnit(c, "wordpress", "2") // wordpress/0 has a version, wordpress/1 is unknown s.SetUnitRevision(c, "wordpress/0", 26) // varnish is a charm that does not have a version in the mock store. s.AddCharmWithRevision(c, "varnish", 5) s.AddService(c, "varnish", "varnish", nil, nil) s.AddUnit(c, "varnish", "3") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/uniter/��������������������������������0000755�0000153�0000161�00000000000�12321736000�025300� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/uniter/uniter_test.go������������������0000644�0000153�0000161�00000135577�12321735776�030241� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" commontesting "launchpad.net/juju-core/state/apiserver/common/testing" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" "launchpad.net/juju-core/state/apiserver/uniter" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" ) func Test(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type uniterSuite struct { testing.JujuConnSuite *commontesting.EnvironWatcherTest authorizer apiservertesting.FakeAuthorizer resources *common.Resources machine0 *state.Machine machine1 *state.Machine wordpress *state.Service wpCharm *state.Charm mysql *state.Service wordpressUnit *state.Unit mysqlUnit *state.Unit uniter *uniter.UniterAPI } var _ = gc.Suite(&uniterSuite{}) func (s *uniterSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.wpCharm = s.AddTestingCharm(c, "wordpress") // Create two machines, two services and add a unit to each service. var err error s.machine0, err = s.State.AddMachine("quantal", state.JobHostUnits, state.JobManageEnviron) c.Assert(err, gc.IsNil) s.machine1, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) s.wordpress = s.AddTestingService(c, "wordpress", s.wpCharm) s.mysql = s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) s.wordpressUnit, err = s.wordpress.AddUnit() c.Assert(err, gc.IsNil) s.mysqlUnit, err = s.mysql.AddUnit() c.Assert(err, gc.IsNil) // Assign each unit to each machine. err = s.wordpressUnit.AssignToMachine(s.machine0) c.Assert(err, gc.IsNil) err = s.mysqlUnit.AssignToMachine(s.machine1) c.Assert(err, gc.IsNil) // Create a FakeAuthorizer so we can check permissions, // set up assuming unit 0 has logged in. s.authorizer = apiservertesting.FakeAuthorizer{ Tag: s.wordpressUnit.Tag(), LoggedIn: true, UnitAgent: true, Entity: s.wordpressUnit, } // Create the resource registry separately to track invocations to // Register. s.resources = common.NewResources() // Create a uniter API for unit 0. s.uniter, err = uniter.NewUniterAPI( s.State, s.resources, s.authorizer, ) c.Assert(err, gc.IsNil) s.EnvironWatcherTest = commontesting.NewEnvironWatcherTest(s.uniter, s.State, s.resources, commontesting.NoSecrets) } func (s *uniterSuite) TestUniterFailsWithNonUnitAgentUser(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.UnitAgent = false anUniter, err := uniter.NewUniterAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.NotNil) c.Assert(anUniter, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *uniterSuite) TestSetStatus(c *gc.C) { err := s.wordpressUnit.SetStatus(params.StatusStarted, "blah", nil) c.Assert(err, gc.IsNil) err = s.mysqlUnit.SetStatus(params.StatusStopped, "foo", nil) c.Assert(err, gc.IsNil) args := params.SetStatus{ Entities: []params.EntityStatus{ {Tag: "unit-mysql-0", Status: params.StatusError, Info: "not really"}, {Tag: "unit-wordpress-0", Status: params.StatusStopped, Info: "foobar"}, {Tag: "unit-foo-42", Status: params.StatusStarted, Info: "blah"}, }} result, err := s.uniter.SetStatus(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) // Verify mysqlUnit - no change. status, info, _, err := s.mysqlUnit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStopped) c.Assert(info, gc.Equals, "foo") // ...wordpressUnit is fine though. status, info, _, err = s.wordpressUnit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStopped) c.Assert(info, gc.Equals, "foobar") } func (s *uniterSuite) TestLife(c *gc.C) { // Add a relation wordpress-mysql. 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) c.Assert(rel.Life(), gc.Equals, state.Alive) // Make the wordpressUnit dead. err = s.wordpressUnit.EnsureDead() c.Assert(err, gc.IsNil) err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) // Add another unit, so the service will stay dying when we // destroy it later. extraUnit, err := s.wordpress.AddUnit() c.Assert(err, gc.IsNil) c.Assert(extraUnit, gc.NotNil) // Make the wordpress service dying. err = s.wordpress.Destroy() c.Assert(err, gc.IsNil) err = s.wordpress.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.wordpress.Life(), gc.Equals, state.Dying) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, {Tag: "service-mysql"}, {Tag: "service-wordpress"}, {Tag: "service-foo"}, {Tag: "just-foo"}, {Tag: rel.Tag()}, {Tag: "relation-svc1.rel1#svc2.rel2"}, {Tag: "relation-blah"}, }} result, err := s.uniter.Life(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.LifeResults{ Results: []params.LifeResult{ {Error: apiservertesting.ErrUnauthorized}, {Life: "dead"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Life: "dying"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) TestEnsureDead(c *gc.C) { c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, }} result, err := s.uniter.EnsureDead(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) err = s.mysqlUnit.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive) // Try it again on a Dead unit; should work. args = params.Entities{ Entities: []params.Entity{{Tag: "unit-wordpress-0"}}, } result, err = s.uniter.EnsureDead(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{{nil}}, }) // Verify Life is unchanged. err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) } func (s *uniterSuite) assertOneStringsWatcher(c *gc.C, result params.StringsWatchResults, err error) { c.Assert(err, gc.IsNil) c.Assert(result.Results, gc.HasLen, 3) c.Assert(result.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) c.Assert(result.Results[1].StringsWatcherId, gc.Equals, "1") c.Assert(result.Results[1].Changes, gc.NotNil) c.Assert(result.Results[1].Error, gc.IsNil) c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) // Verify the resource was registered and stop when done c.Assert(s.resources.Count(), gc.Equals, 1) resource := s.resources.Get("1") defer statetesting.AssertStop(c, resource) // Check that the Watch has consumed the initial event ("returned" in // the Watch call) wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) wc.AssertNoChange() } func (s *uniterSuite) TestWatch(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, {Tag: "service-mysql"}, {Tag: "service-wordpress"}, {Tag: "service-foo"}, {Tag: "just-foo"}, }} result, err := s.uniter.Watch(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ Results: []params.NotifyWatchResult{ {Error: apiservertesting.ErrUnauthorized}, {NotifyWatcherId: "1"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {NotifyWatcherId: "2"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Verify the resource was registered and stop when done c.Assert(s.resources.Count(), gc.Equals, 2) resource1 := s.resources.Get("1") defer statetesting.AssertStop(c, resource1) resource2 := s.resources.Get("2") defer statetesting.AssertStop(c, resource2) // Check that the Watch has consumed the initial event ("returned" in // the Watch call) wc := statetesting.NewNotifyWatcherC(c, s.State, resource1.(state.NotifyWatcher)) wc.AssertNoChange() wc = statetesting.NewNotifyWatcherC(c, s.State, resource2.(state.NotifyWatcher)) wc.AssertNoChange() } func (s *uniterSuite) TestPublicAddress(c *gc.C) { // Try first without setting an address. args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, }} expectErr := &params.Error{ Code: params.CodeNoAddressSet, Message: `"unit-wordpress-0" has no public address set`, } result, err := s.uniter.PublicAddress(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Error: apiservertesting.ErrUnauthorized}, {Error: expectErr}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Now set it an try again. err = s.wordpressUnit.SetPublicAddress("1.2.3.4") c.Assert(err, gc.IsNil) address, ok := s.wordpressUnit.PublicAddress() c.Assert(address, gc.Equals, "1.2.3.4") c.Assert(ok, jc.IsTrue) result, err = s.uniter.PublicAddress(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Error: apiservertesting.ErrUnauthorized}, {Result: "1.2.3.4"}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) TestSetPublicAddress(c *gc.C) { err := s.wordpressUnit.SetPublicAddress("1.2.3.4") c.Assert(err, gc.IsNil) address, ok := s.wordpressUnit.PublicAddress() c.Assert(address, gc.Equals, "1.2.3.4") c.Assert(ok, jc.IsTrue) args := params.SetEntityAddresses{Entities: []params.SetEntityAddress{ {Tag: "unit-mysql-0", Address: "4.3.2.1"}, {Tag: "unit-wordpress-0", Address: "4.4.2.2"}, {Tag: "unit-foo-42", Address: "2.2.4.4"}, }} result, err := s.uniter.SetPublicAddress(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) // Verify wordpressUnit's address has changed. err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) address, ok = s.wordpressUnit.PublicAddress() c.Assert(address, gc.Equals, "4.4.2.2") c.Assert(ok, jc.IsTrue) } func (s *uniterSuite) TestPrivateAddress(c *gc.C) { args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, }} expectErr := &params.Error{ Code: params.CodeNoAddressSet, Message: `"unit-wordpress-0" has no private address set`, } result, err := s.uniter.PrivateAddress(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Error: apiservertesting.ErrUnauthorized}, {Error: expectErr}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Now set it and try again. err = s.wordpressUnit.SetPrivateAddress("1.2.3.4") c.Assert(err, gc.IsNil) address, ok := s.wordpressUnit.PrivateAddress() c.Assert(address, gc.Equals, "1.2.3.4") c.Assert(ok, jc.IsTrue) result, err = s.uniter.PrivateAddress(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Error: apiservertesting.ErrUnauthorized}, {Result: "1.2.3.4"}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) TestSetPrivateAddress(c *gc.C) { err := s.wordpressUnit.SetPrivateAddress("1.2.3.4") c.Assert(err, gc.IsNil) address, ok := s.wordpressUnit.PrivateAddress() c.Assert(address, gc.Equals, "1.2.3.4") c.Assert(ok, jc.IsTrue) args := params.SetEntityAddresses{Entities: []params.SetEntityAddress{ {Tag: "unit-mysql-0", Address: "4.3.2.1"}, {Tag: "unit-wordpress-0", Address: "4.4.2.2"}, {Tag: "unit-foo-42", Address: "2.2.4.4"}, }} result, err := s.uniter.SetPrivateAddress(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) // Verify wordpressUnit's address has changed. err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) address, ok = s.wordpressUnit.PrivateAddress() c.Assert(address, gc.Equals, "4.4.2.2") c.Assert(ok, jc.IsTrue) } func (s *uniterSuite) TestResolved(c *gc.C) { err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks) c.Assert(err, gc.IsNil) mode := s.wordpressUnit.Resolved() c.Assert(mode, gc.Equals, state.ResolvedRetryHooks) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, }} result, err := s.uniter.Resolved(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ResolvedModeResults{ Results: []params.ResolvedModeResult{ {Error: apiservertesting.ErrUnauthorized}, {Mode: params.ResolvedMode(mode)}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) TestClearResolved(c *gc.C) { err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks) c.Assert(err, gc.IsNil) mode := s.wordpressUnit.Resolved() c.Assert(mode, gc.Equals, state.ResolvedRetryHooks) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, }} result, err := s.uniter.ClearResolved(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) // Verify wordpressUnit's resolved mode has changed. err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) mode = s.wordpressUnit.Resolved() c.Assert(mode, gc.Equals, state.ResolvedNone) } func (s *uniterSuite) TestGetPrincipal(c *gc.C) { // Add a subordinate to wordpressUnit. _, _, subordinate := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) principal, ok := subordinate.PrincipalName() c.Assert(principal, gc.Equals, s.wordpressUnit.Name()) c.Assert(ok, jc.IsTrue) // First try it as wordpressUnit's agent. args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: subordinate.Tag()}, {Tag: "unit-foo-42"}, }} result, err := s.uniter.GetPrincipal(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringBoolResults{ Results: []params.StringBoolResult{ {Error: apiservertesting.ErrUnauthorized}, {Result: "", Ok: false, Error: nil}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Now try as subordinate's agent. subAuthorizer := s.authorizer subAuthorizer.Tag = subordinate.Tag() subUniter, err := uniter.NewUniterAPI(s.State, s.resources, subAuthorizer) c.Assert(err, gc.IsNil) result, err = subUniter.GetPrincipal(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringBoolResults{ Results: []params.StringBoolResult{ {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Result: "unit-wordpress-0", Ok: true, Error: nil}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) addRelatedService(c *gc.C, firstSvc, relatedSvc string, unit *state.Unit) (*state.Relation, *state.Service, *state.Unit) { relatedService := s.AddTestingService(c, relatedSvc, s.AddTestingCharm(c, relatedSvc)) rel := s.addRelation(c, firstSvc, relatedSvc) relUnit, err := rel.Unit(unit) c.Assert(err, gc.IsNil) err = relUnit.EnterScope(nil) c.Assert(err, gc.IsNil) relatedUnit, err := relatedService.Unit(relatedSvc + "/0") c.Assert(err, gc.IsNil) return rel, relatedService, relatedUnit } func (s *uniterSuite) TestHasSubordinates(c *gc.C) { // Try first without any subordinates for wordpressUnit. args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-logging-0"}, {Tag: "unit-foo-42"}, }} result, err := s.uniter.HasSubordinates(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.BoolResults{ Results: []params.BoolResult{ {Error: apiservertesting.ErrUnauthorized}, {Result: false}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Add two subordinates to wordpressUnit and try again. s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit) result, err = s.uniter.HasSubordinates(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.BoolResults{ Results: []params.BoolResult{ {Error: apiservertesting.ErrUnauthorized}, {Result: true}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) TestDestroy(c *gc.C) { c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, }} result, err := s.uniter.Destroy(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) // Verify wordpressUnit is destroyed and removed. err = s.wordpressUnit.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *uniterSuite) TestDestroyAllSubordinates(c *gc.C) { // Add two subordinates to wordpressUnit. _, _, loggingSub := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) _, _, monitoringSub := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit) c.Assert(loggingSub.Life(), gc.Equals, state.Alive) c.Assert(monitoringSub.Life(), gc.Equals, state.Alive) err := s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) subordinates := s.wordpressUnit.SubordinateNames() c.Assert(subordinates, gc.DeepEquals, []string{"logging/0", "monitoring/0"}) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, }} result, err := s.uniter.DestroyAllSubordinates(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) // Verify wordpressUnit's subordinates were destroyed. err = loggingSub.Refresh() c.Assert(err, gc.IsNil) c.Assert(loggingSub.Life(), gc.Equals, state.Dying) err = monitoringSub.Refresh() c.Assert(err, gc.IsNil) c.Assert(monitoringSub.Life(), gc.Equals, state.Dying) } func (s *uniterSuite) TestCharmURL(c *gc.C) { // Set wordpressUnit's charm URL first. err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) c.Assert(err, gc.IsNil) curl, ok := s.wordpressUnit.CharmURL() c.Assert(curl, gc.DeepEquals, s.wpCharm.URL()) c.Assert(ok, jc.IsTrue) // Make sure wordpress service's charm is what we expect. curl, force := s.wordpress.CharmURL() c.Assert(curl, gc.DeepEquals, s.wpCharm.URL()) c.Assert(force, jc.IsFalse) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, {Tag: "service-mysql"}, {Tag: "service-wordpress"}, {Tag: "service-foo"}, {Tag: "just-foo"}, }} result, err := s.uniter.CharmURL(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringBoolResults{ Results: []params.StringBoolResult{ {Error: apiservertesting.ErrUnauthorized}, {Result: s.wpCharm.String(), Ok: ok}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Result: s.wpCharm.String(), Ok: force}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) TestSetCharmURL(c *gc.C) { charmUrl, ok := s.wordpressUnit.CharmURL() c.Assert(charmUrl, gc.IsNil) c.Assert(ok, jc.IsFalse) args := params.EntitiesCharmURL{Entities: []params.EntityCharmURL{ {Tag: "unit-mysql-0", CharmURL: "cs:quantal/service-42"}, {Tag: "unit-wordpress-0", CharmURL: s.wpCharm.String()}, {Tag: "unit-foo-42", CharmURL: "cs:quantal/foo-321"}, }} result, err := s.uniter.SetCharmURL(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) // Verify the charm URL was set. err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) charmUrl, ok = s.wordpressUnit.CharmURL() c.Assert(charmUrl, gc.NotNil) c.Assert(charmUrl.String(), gc.Equals, s.wpCharm.String()) c.Assert(ok, jc.IsTrue) } func (s *uniterSuite) TestOpenPort(c *gc.C) { openedPorts := s.wordpressUnit.OpenedPorts() c.Assert(openedPorts, gc.HasLen, 0) args := params.EntitiesPorts{Entities: []params.EntityPort{ {Tag: "unit-mysql-0", Protocol: "tcp", Port: 1234}, {Tag: "unit-wordpress-0", Protocol: "udp", Port: 4321}, {Tag: "unit-foo-42", Protocol: "tcp", Port: 42}, }} result, err := s.uniter.OpenPort(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) // Verify the wordpressUnit's port is opened. err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) openedPorts = s.wordpressUnit.OpenedPorts() c.Assert(openedPorts, gc.DeepEquals, []instance.Port{ {Protocol: "udp", Number: 4321}, }) } func (s *uniterSuite) TestClosePort(c *gc.C) { // Open port udp:4321 in advance on wordpressUnit. err := s.wordpressUnit.OpenPort("udp", 4321) c.Assert(err, gc.IsNil) err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) openedPorts := s.wordpressUnit.OpenedPorts() c.Assert(openedPorts, gc.DeepEquals, []instance.Port{ {Protocol: "udp", Number: 4321}, }) args := params.EntitiesPorts{Entities: []params.EntityPort{ {Tag: "unit-mysql-0", Protocol: "tcp", Port: 1234}, {Tag: "unit-wordpress-0", Protocol: "udp", Port: 4321}, {Tag: "unit-foo-42", Protocol: "tcp", Port: 42}, }} result, err := s.uniter.ClosePort(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) // Verify the wordpressUnit's port is closed. err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) openedPorts = s.wordpressUnit.OpenedPorts() c.Assert(openedPorts, gc.HasLen, 0) } func (s *uniterSuite) TestWatchConfigSettings(c *gc.C) { err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) c.Assert(err, gc.IsNil) c.Assert(s.resources.Count(), gc.Equals, 0) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, }} result, err := s.uniter.WatchConfigSettings(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ Results: []params.NotifyWatchResult{ {Error: apiservertesting.ErrUnauthorized}, {NotifyWatcherId: "1"}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Verify the resource was registered and stop when done c.Assert(s.resources.Count(), gc.Equals, 1) resource := s.resources.Get("1") defer statetesting.AssertStop(c, resource) // Check that the Watch has consumed the initial event ("returned" in // the Watch call) wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher)) wc.AssertNoChange() } func (s *uniterSuite) TestConfigSettings(c *gc.C) { err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL()) c.Assert(err, gc.IsNil) settings, err := s.wordpressUnit.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-wordpress-0"}, {Tag: "unit-foo-42"}, }} result, err := s.uniter.ConfigSettings(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ConfigSettingsResults{ Results: []params.ConfigSettingsResult{ {Error: apiservertesting.ErrUnauthorized}, {Settings: params.ConfigSettings{"blog-title": "My Title"}}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) TestWatchServiceRelations(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) args := params.Entities{Entities: []params.Entity{ {Tag: "service-mysql"}, {Tag: "service-wordpress"}, {Tag: "service-foo"}, }} result, err := s.uniter.WatchServiceRelations(args) s.assertOneStringsWatcher(c, result, err) } func (s *uniterSuite) TestCharmArchiveURL(c *gc.C) { dummyCharm := s.AddTestingCharm(c, "dummy") args := params.CharmURLs{URLs: []params.CharmURL{ {URL: "something-invalid"}, {URL: s.wpCharm.String()}, {URL: dummyCharm.String()}, }} result, err := s.uniter.CharmArchiveURL(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.CharmArchiveURLResults{ Results: []params.CharmArchiveURLResult{ {Error: apiservertesting.ErrUnauthorized}, { Result: s.wpCharm.BundleURL().String(), DisableSSLHostnameVerification: false, }, { Result: dummyCharm.BundleURL().String(), DisableSSLHostnameVerification: false, }, }, }) envtesting.SetSSLHostnameVerification(c, s.State, false) result, err = s.uniter.CharmArchiveURL(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.CharmArchiveURLResults{ Results: []params.CharmArchiveURLResult{ {Error: apiservertesting.ErrUnauthorized}, { Result: s.wpCharm.BundleURL().String(), DisableSSLHostnameVerification: true, }, { Result: dummyCharm.BundleURL().String(), DisableSSLHostnameVerification: true, }, }, }) } func (s *uniterSuite) TestCharmArchiveSha256(c *gc.C) { dummyCharm := s.AddTestingCharm(c, "dummy") args := params.CharmURLs{URLs: []params.CharmURL{ {URL: "something-invalid"}, {URL: s.wpCharm.String()}, {URL: dummyCharm.String()}, }} result, err := s.uniter.CharmArchiveSha256(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Error: apiservertesting.ErrUnauthorized}, {Result: s.wpCharm.BundleSha256()}, {Result: dummyCharm.BundleSha256()}, }, }) } func (s *uniterSuite) TestCurrentEnvironUUID(c *gc.C) { env, err := s.State.Environment() c.Assert(err, gc.IsNil) result, err := s.uniter.CurrentEnvironUUID() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringResult{Result: env.UUID()}) } func (s *uniterSuite) TestCurrentEnvironment(c *gc.C) { env, err := s.State.Environment() c.Assert(err, gc.IsNil) result, err := s.uniter.CurrentEnvironment() c.Assert(err, gc.IsNil) expected := params.EnvironmentResult{ Name: env.Name(), UUID: env.UUID(), } c.Assert(result, gc.DeepEquals, expected) } func (s *uniterSuite) addRelation(c *gc.C, first, second string) *state.Relation { eps, err := s.State.InferEndpoints([]string{first, second}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) return rel } func (s *uniterSuite) TestRelation(c *gc.C) { rel := s.addRelation(c, "wordpress", "mysql") wpEp, err := rel.Endpoint("wordpress") c.Assert(err, gc.IsNil) args := params.RelationUnits{RelationUnits: []params.RelationUnit{ {Relation: "relation-42", Unit: "unit-foo-0"}, {Relation: rel.Tag(), Unit: "unit-wordpress-0"}, {Relation: rel.Tag(), Unit: "unit-mysql-0"}, {Relation: rel.Tag(), Unit: "unit-foo-0"}, {Relation: "relation-blah", Unit: "unit-wordpress-0"}, {Relation: "service-foo", Unit: "user-admin"}, {Relation: "foo", Unit: "bar"}, {Relation: "unit-wordpress-0", Unit: rel.Tag()}, }} result, err := s.uniter.Relation(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.RelationResults{ Results: []params.RelationResult{ {Error: apiservertesting.ErrUnauthorized}, { Id: rel.Id(), Key: rel.String(), Life: params.Life(rel.Life().String()), Endpoint: params.Endpoint{ ServiceName: wpEp.ServiceName, Relation: wpEp.Relation, }, }, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) TestRelationById(c *gc.C) { rel := s.addRelation(c, "wordpress", "mysql") c.Assert(rel.Id(), gc.Equals, 0) wpEp, err := rel.Endpoint("wordpress") c.Assert(err, gc.IsNil) // Add another relation to mysql service, so we can see we can't // get it. otherRel, _, _ := s.addRelatedService(c, "mysql", "logging", s.mysqlUnit) args := params.RelationIds{ RelationIds: []int{-1, rel.Id(), otherRel.Id(), 42, 234}, } result, err := s.uniter.RelationById(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.RelationResults{ Results: []params.RelationResult{ {Error: apiservertesting.ErrUnauthorized}, { Id: rel.Id(), Key: rel.String(), Life: params.Life(rel.Life().String()), Endpoint: params.Endpoint{ ServiceName: wpEp.ServiceName, Relation: wpEp.Relation, }, }, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) TestProviderType(c *gc.C) { cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) result, err := s.uniter.ProviderType() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringResult{Result: cfg.Type()}) } func (s *uniterSuite) assertInScope(c *gc.C, relUnit *state.RelationUnit, inScope bool) { ok, err := relUnit.InScope() c.Assert(err, gc.IsNil) c.Assert(ok, gc.Equals, inScope) } func (s *uniterSuite) TestEnterScope(c *gc.C) { // Set wordpressUnit's private address first. err := s.wordpressUnit.SetPrivateAddress("1.2.3.4") c.Assert(err, gc.IsNil) rel := s.addRelation(c, "wordpress", "mysql") relUnit, err := rel.Unit(s.wordpressUnit) c.Assert(err, gc.IsNil) s.assertInScope(c, relUnit, false) args := params.RelationUnits{RelationUnits: []params.RelationUnit{ {Relation: "relation-42", Unit: "unit-foo-0"}, {Relation: rel.Tag(), Unit: "unit-wordpress-0"}, {Relation: rel.Tag(), Unit: "unit-wordpress-0"}, {Relation: "relation-42", Unit: "unit-wordpress-0"}, {Relation: "relation-foo", Unit: "unit-wordpress-0"}, {Relation: "service-wordpress", Unit: "unit-foo-0"}, {Relation: "foo", Unit: "bar"}, {Relation: rel.Tag(), Unit: "unit-mysql-0"}, {Relation: rel.Tag(), Unit: "service-wordpress"}, {Relation: rel.Tag(), Unit: "service-mysql"}, {Relation: rel.Tag(), Unit: "user-admin"}, }} result, err := s.uniter.EnterScope(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {nil}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) // Verify the scope changes and settings. s.assertInScope(c, relUnit, true) readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name()) c.Assert(err, gc.IsNil) c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{ "private-address": "1.2.3.4", }) } func (s *uniterSuite) TestLeaveScope(c *gc.C) { rel := s.addRelation(c, "wordpress", "mysql") relUnit, err := rel.Unit(s.wordpressUnit) c.Assert(err, gc.IsNil) settings := map[string]interface{}{ "some": "settings", } err = relUnit.EnterScope(settings) c.Assert(err, gc.IsNil) s.assertInScope(c, relUnit, true) args := params.RelationUnits{RelationUnits: []params.RelationUnit{ {Relation: "relation-42", Unit: "unit-foo-0"}, {Relation: rel.Tag(), Unit: "unit-wordpress-0"}, {Relation: rel.Tag(), Unit: "unit-wordpress-0"}, {Relation: "relation-42", Unit: "unit-wordpress-0"}, {Relation: "relation-foo", Unit: "unit-wordpress-0"}, {Relation: "service-wordpress", Unit: "unit-foo-0"}, {Relation: "foo", Unit: "bar"}, {Relation: rel.Tag(), Unit: "unit-mysql-0"}, {Relation: rel.Tag(), Unit: "service-wordpress"}, {Relation: rel.Tag(), Unit: "service-mysql"}, {Relation: rel.Tag(), Unit: "user-admin"}, }} result, err := s.uniter.LeaveScope(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {nil}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) // Verify the scope changes. s.assertInScope(c, relUnit, false) readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name()) c.Assert(err, gc.IsNil) 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) c.Assert(err, gc.IsNil) settings := map[string]interface{}{ "some": "settings", } err = relUnit.EnterScope(settings) c.Assert(err, gc.IsNil) s.assertInScope(c, relUnit, true) args := params.RelationUnits{RelationUnits: []params.RelationUnit{ {Relation: "relation-42", Unit: "unit-foo-0"}, {Relation: rel.Tag(), Unit: "unit-wordpress-0"}, {Relation: rel.Tag(), Unit: "unit-mysql-0"}, {Relation: "relation-42", Unit: "unit-wordpress-0"}, {Relation: "relation-foo", Unit: ""}, {Relation: "service-wordpress", Unit: "unit-foo-0"}, {Relation: "foo", Unit: "bar"}, {Relation: rel.Tag(), Unit: "unit-mysql-0"}, {Relation: rel.Tag(), Unit: "service-wordpress"}, {Relation: rel.Tag(), Unit: "service-mysql"}, {Relation: rel.Tag(), Unit: "user-admin"}, }} result, err := s.uniter.ReadSettings(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.RelationSettingsResults{ Results: []params.RelationSettingsResult{ {Error: apiservertesting.ErrUnauthorized}, {Settings: params.RelationSettings{ "some": "settings", }}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *uniterSuite) TestReadSettingsWithNonStringValuesFails(c *gc.C) { rel := s.addRelation(c, "wordpress", "mysql") relUnit, err := rel.Unit(s.wordpressUnit) c.Assert(err, gc.IsNil) settings := map[string]interface{}{ "other": "things", "invalid-bool": false, } err = relUnit.EnterScope(settings) c.Assert(err, gc.IsNil) s.assertInScope(c, relUnit, true) args := params.RelationUnits{RelationUnits: []params.RelationUnit{ {Relation: rel.Tag(), Unit: "unit-wordpress-0"}, }} expectErr := `unexpected relation setting "invalid-bool": expected string, got bool` result, err := s.uniter.ReadSettings(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.RelationSettingsResults{ Results: []params.RelationSettingsResult{ {Error: &params.Error{Message: expectErr}}, }, }) } func (s *uniterSuite) TestReadRemoteSettings(c *gc.C) { rel := s.addRelation(c, "wordpress", "mysql") relUnit, err := rel.Unit(s.wordpressUnit) c.Assert(err, gc.IsNil) settings := map[string]interface{}{ "some": "settings", } err = relUnit.EnterScope(settings) c.Assert(err, gc.IsNil) s.assertInScope(c, relUnit, true) // First test most of the invalid args tests and try to read the // (unset) remote unit settings. args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{ {Relation: "relation-42", LocalUnit: "unit-foo-0", RemoteUnit: "foo"}, {Relation: rel.Tag(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-wordpress-0"}, {Relation: rel.Tag(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-mysql-0"}, {Relation: "relation-42", LocalUnit: "unit-wordpress-0", RemoteUnit: ""}, {Relation: "relation-foo", LocalUnit: "", RemoteUnit: ""}, {Relation: "service-wordpress", LocalUnit: "unit-foo-0", RemoteUnit: "user-admin"}, {Relation: "foo", LocalUnit: "bar", RemoteUnit: "baz"}, {Relation: rel.Tag(), LocalUnit: "unit-mysql-0", RemoteUnit: "unit-wordpress-0"}, {Relation: rel.Tag(), LocalUnit: "service-wordpress", RemoteUnit: "service-mysql"}, {Relation: rel.Tag(), LocalUnit: "service-mysql", RemoteUnit: "foo"}, {Relation: rel.Tag(), LocalUnit: "user-admin", RemoteUnit: "unit-wordpress-0"}, }} result, err := s.uniter.ReadRemoteSettings(args) // We don't set the remote unit settings on purpose to test the error. expectErr := `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": settings not found` c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.RelationSettingsResults{ Results: []params.RelationSettingsResult{ {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: &params.Error{Message: expectErr}}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Now leave the mysqlUnit and re-enter with new settings. relUnit, err = rel.Unit(s.mysqlUnit) c.Assert(err, gc.IsNil) settings = map[string]interface{}{ "other": "things", } err = relUnit.LeaveScope() c.Assert(err, gc.IsNil) s.assertInScope(c, relUnit, false) err = relUnit.EnterScope(settings) c.Assert(err, gc.IsNil) s.assertInScope(c, relUnit, true) // Test the remote unit settings can be read. args = params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{ Relation: rel.Tag(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-mysql-0", }}} expect := params.RelationSettingsResults{ Results: []params.RelationSettingsResult{ {Settings: params.RelationSettings{ "other": "things", }}, }, } result, err = s.uniter.ReadRemoteSettings(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, expect) // Now destroy the remote unit, and check its settings can still be read. err = s.mysqlUnit.Destroy() c.Assert(err, gc.IsNil) err = s.mysqlUnit.EnsureDead() c.Assert(err, gc.IsNil) err = s.mysqlUnit.Remove() c.Assert(err, gc.IsNil) result, err = s.uniter.ReadRemoteSettings(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, expect) } func (s *uniterSuite) TestReadRemoteSettingsWithNonStringValuesFails(c *gc.C) { rel := s.addRelation(c, "wordpress", "mysql") relUnit, err := rel.Unit(s.mysqlUnit) c.Assert(err, gc.IsNil) settings := map[string]interface{}{ "other": "things", "invalid-bool": false, } err = relUnit.EnterScope(settings) c.Assert(err, gc.IsNil) s.assertInScope(c, relUnit, true) args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{ Relation: rel.Tag(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-mysql-0", }}} expectErr := `unexpected relation setting "invalid-bool": expected string, got bool` result, err := s.uniter.ReadRemoteSettings(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.RelationSettingsResults{ Results: []params.RelationSettingsResult{ {Error: &params.Error{Message: expectErr}}, }, }) } func (s *uniterSuite) TestUpdateSettings(c *gc.C) { rel := s.addRelation(c, "wordpress", "mysql") relUnit, err := rel.Unit(s.wordpressUnit) c.Assert(err, gc.IsNil) settings := map[string]interface{}{ "some": "settings", "other": "stuff", } err = relUnit.EnterScope(settings) s.assertInScope(c, relUnit, true) newSettings := params.RelationSettings{ "some": "different", "other": "", } args := params.RelationUnitsSettings{RelationUnits: []params.RelationUnitSettings{ {Relation: "relation-42", Unit: "unit-foo-0", Settings: nil}, {Relation: rel.Tag(), Unit: "unit-wordpress-0", Settings: newSettings}, {Relation: "relation-42", Unit: "unit-wordpress-0", Settings: nil}, {Relation: "relation-foo", Unit: "unit-wordpress-0", Settings: nil}, {Relation: "service-wordpress", Unit: "unit-foo-0", Settings: nil}, {Relation: "foo", Unit: "bar", Settings: nil}, {Relation: rel.Tag(), Unit: "unit-mysql-0", Settings: nil}, {Relation: rel.Tag(), Unit: "service-wordpress", Settings: nil}, {Relation: rel.Tag(), Unit: "service-mysql", Settings: nil}, {Relation: rel.Tag(), Unit: "user-admin", Settings: nil}, }} result, err := s.uniter.UpdateSettings(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) // Verify the settings were saved. s.assertInScope(c, relUnit, true) readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name()) c.Assert(err, gc.IsNil) c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{ "some": "different", }) } func (s *uniterSuite) TestWatchRelationUnits(c *gc.C) { // Add a relation between wordpress and mysql and enter scope with // mysqlUnit. rel := s.addRelation(c, "wordpress", "mysql") myRelUnit, err := rel.Unit(s.mysqlUnit) c.Assert(err, gc.IsNil) err = myRelUnit.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertInScope(c, myRelUnit, true) c.Assert(s.resources.Count(), gc.Equals, 0) args := params.RelationUnits{RelationUnits: []params.RelationUnit{ {Relation: "relation-42", Unit: "unit-foo-0"}, {Relation: rel.Tag(), Unit: "unit-wordpress-0"}, {Relation: rel.Tag(), Unit: "unit-mysql-0"}, {Relation: "relation-42", Unit: "unit-wordpress-0"}, {Relation: "relation-foo", Unit: ""}, {Relation: "service-wordpress", Unit: "unit-foo-0"}, {Relation: "foo", Unit: "bar"}, {Relation: rel.Tag(), Unit: "unit-mysql-0"}, {Relation: rel.Tag(), Unit: "service-wordpress"}, {Relation: rel.Tag(), Unit: "service-mysql"}, {Relation: rel.Tag(), Unit: "user-admin"}, }} result, err := s.uniter.WatchRelationUnits(args) c.Assert(err, gc.IsNil) // UnitSettings versions are volatile, so we don't check them. // We just make sure the keys of the Changed field are as // expected. c.Assert(result.Results, gc.HasLen, len(args.RelationUnits)) mysqlChanges := result.Results[1].Changes c.Assert(mysqlChanges, gc.NotNil) changed, ok := mysqlChanges.Changed["mysql/0"] c.Assert(ok, jc.IsTrue) expectChanges := params.RelationUnitsChange{ Changed: map[string]params.UnitSettings{"mysql/0": changed}, } c.Assert(result, gc.DeepEquals, params.RelationUnitsWatchResults{ Results: []params.RelationUnitsWatchResult{ {Error: apiservertesting.ErrUnauthorized}, { RelationUnitsWatcherId: "1", Changes: expectChanges, }, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Verify the resource was registered and stop when done c.Assert(s.resources.Count(), gc.Equals, 1) resource := s.resources.Get("1") defer statetesting.AssertStop(c, resource) // Check that the Watch has consumed the initial event ("returned" in // the Watch call) wc := statetesting.NewRelationUnitsWatcherC(c, s.State, resource.(state.RelationUnitsWatcher)) wc.AssertNoChange() // Leave scope with mysqlUnit and check it's detected. err = myRelUnit.LeaveScope() c.Assert(err, gc.IsNil) s.assertInScope(c, myRelUnit, false) wc.AssertChange(nil, []string{"mysql/0"}) } func (s *uniterSuite) TestAPIAddresses(c *gc.C) { err := s.machine0.SetAddresses([]instance.Address{ instance.NewAddress("0.1.2.3"), }) c.Assert(err, gc.IsNil) apiAddresses, err := s.State.APIAddressesFromMachines() c.Assert(err, gc.IsNil) result, err := s.uniter.APIAddresses() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringsResult{ Result: apiAddresses, }) } func (s *uniterSuite) TestGetOwnerTag(c *gc.C) { tag := s.mysql.Tag() args := params.Entities{Entities: []params.Entity{ {Tag: tag}, }} result, err := s.uniter.GetOwnerTag(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringResult{ Result: "user-admin", }) } ���������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/uniter/uniter.go�����������������������0000644�0000153�0000161�00000075371�12321735776�027175� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The uniter package implements the API interface // used by the uniter worker. package uniter import ( "fmt" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/watcher" ) // UniterAPI implements the API used by the uniter worker. type UniterAPI struct { *common.LifeGetter *common.StatusSetter *common.DeadEnsurer *common.AgentEntityWatcher *common.APIAddresser *common.EnvironWatcher st *state.State auth common.Authorizer resources *common.Resources accessUnit common.GetAuthFunc accessService common.GetAuthFunc } // NewUniterAPI creates a new instance of the Uniter API. func NewUniterAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*UniterAPI, error) { if !authorizer.AuthUnitAgent() { return nil, common.ErrPerm } accessUnit := func() (common.AuthFunc, error) { return authorizer.AuthOwner, nil } accessService := func() (common.AuthFunc, error) { unit, ok := authorizer.GetAuthEntity().(*state.Unit) if !ok { panic("authenticated entity is not a unit") } return func(tag string) bool { return tag == names.ServiceTag(unit.ServiceName()) }, nil } accessUnitOrService := common.AuthEither(accessUnit, accessService) // Uniter can always watch for environ changes. getCanWatch := common.AuthAlways(true) // Uniter can not get the secrets. getCanReadSecrets := common.AuthAlways(false) return &UniterAPI{ LifeGetter: common.NewLifeGetter(st, accessUnitOrService), StatusSetter: common.NewStatusSetter(st, accessUnit), DeadEnsurer: common.NewDeadEnsurer(st, accessUnit), AgentEntityWatcher: common.NewAgentEntityWatcher(st, resources, accessUnitOrService), APIAddresser: common.NewAPIAddresser(st, resources), EnvironWatcher: common.NewEnvironWatcher(st, resources, getCanWatch, getCanReadSecrets), st: st, auth: authorizer, resources: resources, accessUnit: accessUnit, accessService: accessService, }, nil } func (u *UniterAPI) getUnit(tag string) (*state.Unit, error) { _, name, err := names.ParseTag(tag, names.UnitTagKind) if err != nil { return nil, err } return u.st.Unit(name) } func (u *UniterAPI) getService(tag string) (*state.Service, error) { _, name, err := names.ParseTag(tag, names.ServiceTagKind) if err != nil { return nil, err } return u.st.Service(name) } // PublicAddress returns the public address for each given unit, if set. func (u *UniterAPI) PublicAddress(args params.Entities) (params.StringResults, error) { result := params.StringResults{ Results: make([]params.StringResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.StringResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { address, ok := unit.PublicAddress() if ok { result.Results[i].Result = address } else { err = common.NoAddressSetError(entity.Tag, "public") } } } result.Results[i].Error = common.ServerError(err) } return result, nil } // SetPublicAddress sets the public address of each of the given units. func (u *UniterAPI) SetPublicAddress(args params.SetEntityAddresses) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { err = unit.SetPublicAddress(entity.Address) } } result.Results[i].Error = common.ServerError(err) } return result, nil } // PrivateAddress returns the private address for each given unit, if set. func (u *UniterAPI) PrivateAddress(args params.Entities) (params.StringResults, error) { result := params.StringResults{ Results: make([]params.StringResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.StringResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { address, ok := unit.PrivateAddress() if ok { result.Results[i].Result = address } else { err = common.NoAddressSetError(entity.Tag, "private") } } } result.Results[i].Error = common.ServerError(err) } return result, nil } // SetPrivateAddress sets the private address of each of the given units. func (u *UniterAPI) SetPrivateAddress(args params.SetEntityAddresses) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { err = unit.SetPrivateAddress(entity.Address) } } result.Results[i].Error = common.ServerError(err) } return result, nil } // Resolved returns the current resolved setting for each given unit. func (u *UniterAPI) Resolved(args params.Entities) (params.ResolvedModeResults, error) { result := params.ResolvedModeResults{ Results: make([]params.ResolvedModeResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ResolvedModeResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { result.Results[i].Mode = params.ResolvedMode(unit.Resolved()) } } result.Results[i].Error = common.ServerError(err) } return result, nil } // ClearResolved removes any resolved setting from each given unit. func (u *UniterAPI) ClearResolved(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { err = unit.ClearResolved() } } result.Results[i].Error = common.ServerError(err) } return result, nil } // GetPrincipal returns the result of calling PrincipalName() and // converting it to a tag, on each given unit. func (u *UniterAPI) GetPrincipal(args params.Entities) (params.StringBoolResults, error) { result := params.StringBoolResults{ Results: make([]params.StringBoolResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.StringBoolResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { principal, ok := unit.PrincipalName() if principal != "" { result.Results[i].Result = names.UnitTag(principal) } result.Results[i].Ok = ok } } result.Results[i].Error = common.ServerError(err) } return result, nil } // Destroy advances all given Alive units' lifecycles as far as // possible. See state/Unit.Destroy(). func (u *UniterAPI) Destroy(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { err = unit.Destroy() } } result.Results[i].Error = common.ServerError(err) } return result, nil } func (u *UniterAPI) destroySubordinates(principal *state.Unit) error { subordinates := principal.SubordinateNames() for _, subName := range subordinates { unit, err := u.getUnit(names.UnitTag(subName)) if err != nil { return err } if err = unit.Destroy(); err != nil { return err } } return nil } // DestroyAllSubordinates destroys all subordinates of each given unit. func (u *UniterAPI) DestroyAllSubordinates(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { err = u.destroySubordinates(unit) } } result.Results[i].Error = common.ServerError(err) } return result, nil } // HasSubordinates returns the whether each given unit has any subordinates. func (u *UniterAPI) HasSubordinates(args params.Entities) (params.BoolResults, error) { result := params.BoolResults{ Results: make([]params.BoolResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.BoolResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { subordinates := unit.SubordinateNames() result.Results[i].Result = len(subordinates) > 0 } } result.Results[i].Error = common.ServerError(err) } return result, nil } // CharmURL returns the charm URL for all given units or services. func (u *UniterAPI) CharmURL(args params.Entities) (params.StringBoolResults, error) { result := params.StringBoolResults{ Results: make([]params.StringBoolResult, len(args.Entities)), } accessUnitOrService := common.AuthEither(u.accessUnit, u.accessService) canAccess, err := accessUnitOrService() if err != nil { return params.StringBoolResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unitOrService state.Entity unitOrService, err = u.st.FindEntity(entity.Tag) if err == nil { charmURLer := unitOrService.(interface { CharmURL() (*charm.URL, bool) }) curl, ok := charmURLer.CharmURL() if curl != nil { result.Results[i].Result = curl.String() result.Results[i].Ok = ok } } } result.Results[i].Error = common.ServerError(err) } return result, nil } // SetCharmURL sets the charm URL for each given unit. An error will // be returned if a unit is dead, or the charm URL is not know. func (u *UniterAPI) SetCharmURL(args params.EntitiesCharmURL) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { var curl *charm.URL curl, err = charm.ParseURL(entity.CharmURL) if err == nil { err = unit.SetCharmURL(curl) } } } result.Results[i].Error = common.ServerError(err) } return result, nil } // OpenPort sets the policy of the port with protocol an number to be // opened, for all given units. func (u *UniterAPI) OpenPort(args params.EntitiesPorts) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { err = unit.OpenPort(entity.Protocol, entity.Port) } } result.Results[i].Error = common.ServerError(err) } return result, nil } // ClosePort sets the policy of the port with protocol and number to // be closed, for all given units. func (u *UniterAPI) ClosePort(args params.EntitiesPorts) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { err = unit.ClosePort(entity.Protocol, entity.Port) } } result.Results[i].Error = common.ServerError(err) } return result, nil } func (u *UniterAPI) watchOneUnitConfigSettings(tag string) (string, error) { unit, err := u.getUnit(tag) if err != nil { return "", err } watch, err := unit.WatchConfigSettings() if err != nil { return "", err } // Consume the initial event. Technically, API // calls to Watch 'transmit' the initial event // in the Watch response. But NotifyWatchers // have no state to transmit. if _, ok := <-watch.Changes(); ok { return u.resources.Register(watch), nil } return "", watcher.MustErr(watch) } // WatchConfigSettings returns a NotifyWatcher for observing changes // to each unit's service configuration settings. See also // state/watcher.go:Unit.WatchConfigSettings(). func (u *UniterAPI) WatchConfigSettings(args params.Entities) (params.NotifyWatchResults, error) { result := params.NotifyWatchResults{ Results: make([]params.NotifyWatchResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.NotifyWatchResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm watcherId := "" if canAccess(entity.Tag) { watcherId, err = u.watchOneUnitConfigSettings(entity.Tag) } result.Results[i].NotifyWatcherId = watcherId result.Results[i].Error = common.ServerError(err) } return result, nil } // ConfigSettings returns the complete set of service charm config // settings available to each given unit. func (u *UniterAPI) ConfigSettings(args params.Entities) (params.ConfigSettingsResults, error) { result := params.ConfigSettingsResults{ Results: make([]params.ConfigSettingsResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.ConfigSettingsResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { var settings charm.Settings settings, err = unit.ConfigSettings() if err == nil { result.Results[i].Settings = params.ConfigSettings(settings) } } } result.Results[i].Error = common.ServerError(err) } return result, nil } func (u *UniterAPI) watchOneServiceRelations(tag string) (params.StringsWatchResult, error) { nothing := params.StringsWatchResult{} service, err := u.getService(tag) if err != nil { return nothing, err } watch := service.WatchRelations() // Consume the initial event and forward it to the result. if changes, ok := <-watch.Changes(); ok { return params.StringsWatchResult{ StringsWatcherId: u.resources.Register(watch), Changes: changes, }, nil } return nothing, watcher.MustErr(watch) } // WatchServiceRelations returns a StringsWatcher, for each given // service, that notifies of changes to the lifecycles of relations // involving that service. func (u *UniterAPI) WatchServiceRelations(args params.Entities) (params.StringsWatchResults, error) { result := params.StringsWatchResults{ Results: make([]params.StringsWatchResult, len(args.Entities)), } canAccess, err := u.accessService() if err != nil { return params.StringsWatchResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { result.Results[i], err = u.watchOneServiceRelations(entity.Tag) } result.Results[i].Error = common.ServerError(err) } return result, nil } // CharmArchiveURL returns the URL, corresponding to the charm archive // (bundle) in the provider storage for each given charm URL, along // with the DisableSSLHostnameVerification flag. func (u *UniterAPI) CharmArchiveURL(args params.CharmURLs) (params.CharmArchiveURLResults, error) { result := params.CharmArchiveURLResults{ Results: make([]params.CharmArchiveURLResult, len(args.URLs)), } // Get the SSL hostname verification environment setting. envConfig, err := u.st.EnvironConfig() if err != nil { return result, err } // SSLHostnameVerification defaults to true, so we need to // invert that, for backwards-compatibility (older versions // will have DisableSSLHostnameVerification: false by default). disableSSLHostnameVerification := !envConfig.SSLHostnameVerification() for i, arg := range args.URLs { curl, err := charm.ParseURL(arg.URL) if err != nil { err = common.ErrPerm } else { var sch *state.Charm sch, err = u.st.Charm(curl) if errors.IsNotFoundError(err) { err = common.ErrPerm } if err == nil { result.Results[i].Result = sch.BundleURL().String() result.Results[i].DisableSSLHostnameVerification = disableSSLHostnameVerification } } result.Results[i].Error = common.ServerError(err) } return result, nil } // CharmArchiveSha256 returns the SHA256 digest of the charm archive // (bundle) data for each charm url in the given parameters. func (u *UniterAPI) CharmArchiveSha256(args params.CharmURLs) (params.StringResults, error) { result := params.StringResults{ Results: make([]params.StringResult, len(args.URLs)), } for i, arg := range args.URLs { curl, err := charm.ParseURL(arg.URL) if err != nil { err = common.ErrPerm } else { var sch *state.Charm sch, err = u.st.Charm(curl) if errors.IsNotFoundError(err) { err = common.ErrPerm } if err == nil { result.Results[i].Result = sch.BundleSha256() } } result.Results[i].Error = common.ServerError(err) } return result, nil } func (u *UniterAPI) getRelationAndUnit(canAccess common.AuthFunc, relTag, unitTag string) (*state.Relation, *state.Unit, error) { _, key, err := names.ParseTag(relTag, names.RelationTagKind) if err != nil { return nil, nil, common.ErrPerm } rel, err := u.st.KeyRelation(key) if errors.IsNotFoundError(err) { return nil, nil, common.ErrPerm } else if err != nil { return nil, nil, err } if !canAccess(unitTag) { return nil, nil, common.ErrPerm } unit, err := u.getUnit(unitTag) return rel, unit, err } func (u *UniterAPI) prepareRelationResult(rel *state.Relation, unit *state.Unit) (params.RelationResult, error) { nothing := params.RelationResult{} ep, err := rel.Endpoint(unit.ServiceName()) if err != nil { // An error here means the unit's service is not part of the // relation. return nothing, err } return params.RelationResult{ Id: rel.Id(), Key: rel.String(), Life: params.Life(rel.Life().String()), Endpoint: params.Endpoint{ ServiceName: ep.ServiceName, Relation: ep.Relation, }, }, nil } func (u *UniterAPI) getOneRelation(canAccess common.AuthFunc, relTag, unitTag string) (params.RelationResult, error) { nothing := params.RelationResult{} rel, unit, err := u.getRelationAndUnit(canAccess, relTag, unitTag) if err != nil { return nothing, err } return u.prepareRelationResult(rel, unit) } func (u *UniterAPI) getOneRelationById(relId int) (params.RelationResult, error) { nothing := params.RelationResult{} rel, err := u.st.Relation(relId) if errors.IsNotFoundError(err) { return nothing, common.ErrPerm } else if err != nil { return nothing, err } // Use the currently authenticated unit to get the endpoint. unit, ok := u.auth.GetAuthEntity().(*state.Unit) if !ok { panic("authenticated entity is not a unit") } result, err := u.prepareRelationResult(rel, unit) if err != nil { // An error from prepareRelationResult means the authenticated // unit's service is not part of the requested // relation. That's why it's appropriate to return ErrPerm // here. return nothing, common.ErrPerm } return result, nil } func (u *UniterAPI) getRelationUnit(canAccess common.AuthFunc, relTag, unitTag string) (*state.RelationUnit, error) { rel, unit, err := u.getRelationAndUnit(canAccess, relTag, unitTag) if err != nil { return nil, err } return rel.Unit(unit) } // Relation returns information about all given relation/unit pairs, // including their id, key and the local endpoint. func (u *UniterAPI) Relation(args params.RelationUnits) (params.RelationResults, error) { result := params.RelationResults{ Results: make([]params.RelationResult, len(args.RelationUnits)), } canAccess, err := u.accessUnit() if err != nil { return params.RelationResults{}, err } for i, rel := range args.RelationUnits { relParams, err := u.getOneRelation(canAccess, rel.Relation, rel.Unit) if err == nil { result.Results[i] = relParams } result.Results[i].Error = common.ServerError(err) } return result, nil } // RelationById returns information about all given relations, // specified by their ids, including their key and the local // endpoint. func (u *UniterAPI) RelationById(args params.RelationIds) (params.RelationResults, error) { result := params.RelationResults{ Results: make([]params.RelationResult, len(args.RelationIds)), } for i, relId := range args.RelationIds { relParams, err := u.getOneRelationById(relId) if err == nil { result.Results[i] = relParams } result.Results[i].Error = common.ServerError(err) } 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{} env, err := u.st.Environment() if err == nil { result.Result = env.UUID() } return result, err } // CurrentEnvironment returns the name and UUID for the current juju environment. func (u *UniterAPI) CurrentEnvironment() (params.EnvironmentResult, error) { result := params.EnvironmentResult{} env, err := u.st.Environment() if err == nil { result.Name = env.Name() result.UUID = env.UUID() } return result, err } // ProviderType returns the provider type used by the current juju // environment. // // TODO(dimitern): Refactor the uniter to call this instead of calling // EnvironConfig() just to get the provider type. Once we have machine // addresses, this might be completely unnecessary though. func (u *UniterAPI) ProviderType() (params.StringResult, error) { result := params.StringResult{} cfg, err := u.st.EnvironConfig() if err == nil { result.Result = cfg.Type() } return result, err } // EnterScope ensures each unit has entered its scope in the relation, // for all of the given relation/unit pairs. See also // state.RelationUnit.EnterScope(). func (u *UniterAPI) EnterScope(args params.RelationUnits) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.RelationUnits)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, arg := range args.RelationUnits { relUnit, err := u.getRelationUnit(canAccess, arg.Relation, arg.Unit) if err == nil { // Construct the settings, passing the unit's // private address (we already know it). privateAddress, _ := relUnit.PrivateAddress() settings := map[string]interface{}{ "private-address": privateAddress, } err = relUnit.EnterScope(settings) } result.Results[i].Error = common.ServerError(err) } return result, nil } // LeaveScope signals each unit has left its scope in the relation, // for all of the given relation/unit pairs. See also // state.RelationUnit.LeaveScope(). func (u *UniterAPI) LeaveScope(args params.RelationUnits) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.RelationUnits)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, arg := range args.RelationUnits { relUnit, err := u.getRelationUnit(canAccess, arg.Relation, arg.Unit) if err == nil { err = relUnit.LeaveScope() } result.Results[i].Error = common.ServerError(err) } return result, nil } func convertRelationSettings(settings map[string]interface{}) (params.RelationSettings, error) { result := make(params.RelationSettings) for k, v := range settings { // All relation settings should be strings. sval, ok := v.(string) if !ok { return nil, fmt.Errorf("unexpected relation setting %q: expected string, got %T", k, v) } result[k] = sval } return result, nil } // ReadSettings returns the local settings of each given set of // relation/unit. func (u *UniterAPI) ReadSettings(args params.RelationUnits) (params.RelationSettingsResults, error) { result := params.RelationSettingsResults{ Results: make([]params.RelationSettingsResult, len(args.RelationUnits)), } canAccess, err := u.accessUnit() if err != nil { return params.RelationSettingsResults{}, err } for i, arg := range args.RelationUnits { relUnit, err := u.getRelationUnit(canAccess, arg.Relation, arg.Unit) if err == nil { var settings *state.Settings settings, err = relUnit.Settings() if err == nil { result.Results[i].Settings, err = convertRelationSettings(settings.Map()) } } result.Results[i].Error = common.ServerError(err) } return result, nil } func (u *UniterAPI) checkRemoteUnit(relUnit *state.RelationUnit, remoteUnitTag string) (string, error) { // Make sure the unit is indeed remote. if remoteUnitTag == u.auth.GetAuthTag() { return "", common.ErrPerm } // Check remoteUnit is indeed related. Note that we don't want to actually get // the *Unit, because it might have been removed; but its relation settings will // persist until the relation itself has been removed (and must remain accessible // because the local unit's view of reality may be time-shifted). _, remoteUnitName, err := names.ParseTag(remoteUnitTag, names.UnitTagKind) if err != nil { return "", err } remoteServiceName := names.UnitService(remoteUnitName) rel := relUnit.Relation() _, err = rel.RelatedEndpoints(remoteServiceName) if err != nil { return "", common.ErrPerm } return remoteUnitName, nil } // ReadRemoteSettings returns the remote settings of each given set of // relation/local unit/remote unit. func (u *UniterAPI) ReadRemoteSettings(args params.RelationUnitPairs) (params.RelationSettingsResults, error) { result := params.RelationSettingsResults{ Results: make([]params.RelationSettingsResult, len(args.RelationUnitPairs)), } canAccess, err := u.accessUnit() if err != nil { return params.RelationSettingsResults{}, err } for i, arg := range args.RelationUnitPairs { relUnit, err := u.getRelationUnit(canAccess, arg.Relation, arg.LocalUnit) if err == nil { remoteUnit := "" remoteUnit, err = u.checkRemoteUnit(relUnit, arg.RemoteUnit) if err == nil { var settings map[string]interface{} settings, err = relUnit.ReadSettings(remoteUnit) if err == nil { result.Results[i].Settings, err = convertRelationSettings(settings) } } } result.Results[i].Error = common.ServerError(err) } return result, nil } // UpdateSettings persists all changes made to the local settings of // all given pairs of relation and unit. Keys with empty values are // considered a signal to delete these values. func (u *UniterAPI) UpdateSettings(args params.RelationUnitsSettings) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.RelationUnits)), } canAccess, err := u.accessUnit() if err != nil { return params.ErrorResults{}, err } for i, arg := range args.RelationUnits { relUnit, err := u.getRelationUnit(canAccess, arg.Relation, arg.Unit) if err == nil { var settings *state.Settings settings, err = relUnit.Settings() if err == nil { for k, v := range arg.Settings { if v == "" { settings.Delete(k) } else { settings.Set(k, v) } } _, err = settings.Write() } } result.Results[i].Error = common.ServerError(err) } return result, nil } func (u *UniterAPI) watchOneRelationUnit(relUnit *state.RelationUnit) (params.RelationUnitsWatchResult, error) { watch := relUnit.Watch() // Consume the initial event and forward it to the result. if changes, ok := <-watch.Changes(); ok { return params.RelationUnitsWatchResult{ RelationUnitsWatcherId: u.resources.Register(watch), Changes: changes, }, nil } return params.RelationUnitsWatchResult{}, watcher.MustErr(watch) } // WatchRelationUnits returns a RelationUnitsWatcher for observing // changes to every unit in the supplied relation that is visible to // the supplied unit. See also state/watcher.go:RelationUnit.Watch(). func (u *UniterAPI) WatchRelationUnits(args params.RelationUnits) (params.RelationUnitsWatchResults, error) { result := params.RelationUnitsWatchResults{ Results: make([]params.RelationUnitsWatchResult, len(args.RelationUnits)), } canAccess, err := u.accessUnit() if err != nil { return params.RelationUnitsWatchResults{}, err } for i, arg := range args.RelationUnits { relUnit, err := u.getRelationUnit(canAccess, arg.Relation, arg.Unit) if err == nil { result.Results[i], err = u.watchOneRelationUnit(relUnit) } result.Results[i].Error = common.ServerError(err) } return result, nil } // TODO(dimitern) bug #1270795 2014-01-20 // Add a doc comment here and use u.accessService() // below in the body to check for permissions. func (u *UniterAPI) GetOwnerTag(args params.Entities) (params.StringResult, error) { nothing := params.StringResult{} service, err := u.getService(args.Entities[0].Tag) if err != nil { return nothing, err } return params.StringResult{ Result: service.GetOwnerTag(), }, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/charms.go������������������������������0000644�0000153�0000161�00000034747�12321735776�025640� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver import ( "archive/zip" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io" "io/ioutil" "mime" "net/http" "net/url" "os" "path" "path/filepath" "sort" "strconv" "strings" "github.com/errgo/errgo" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/state/api/params" ziputil "launchpad.net/juju-core/utils/zip" ) // charmsHandler handles charm upload through HTTPS in the API server. type charmsHandler struct { httpHandler dataDir string } // bundleContentSenderFunc functions are responsible for sending a // response related to a charm bundle. type bundleContentSenderFunc func(w http.ResponseWriter, r *http.Request, bundle *charm.Bundle) func (h *charmsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err := h.authenticate(r); err != nil { h.authError(w) return } switch r.Method { case "POST": // Add a local charm to the store provider. // Requires a "series" query specifying the series to use for the charm. charmURL, err := h.processPost(r) if err != nil { h.sendError(w, http.StatusBadRequest, err.Error()) return } h.sendJSON(w, http.StatusOK, &params.CharmsResponse{CharmURL: charmURL.String()}) case "GET": // Retrieve or list charm files. // Requires "url" (charm URL) and an optional "file" (the path to the // charm file) to be included in the query. if charmArchivePath, filePath, err := h.processGet(r); err != nil { // An error occurred retrieving the charm bundle. h.sendError(w, http.StatusBadRequest, err.Error()) } else if filePath == "" { // The client requested the list of charm files. sendBundleContent(w, r, charmArchivePath, h.manifestSender) } else { // The client requested a specific file. sendBundleContent(w, r, charmArchivePath, h.fileSender(filePath)) } default: h.sendError(w, http.StatusMethodNotAllowed, fmt.Sprintf("unsupported method: %q", r.Method)) } } // sendJSON sends a JSON-encoded response to the client. func (h *charmsHandler) sendJSON(w http.ResponseWriter, statusCode int, response *params.CharmsResponse) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(statusCode) body, err := json.Marshal(response) if err != nil { return err } w.Write(body) return nil } // sendBundleContent uses the given bundleContentSenderFunc to send a response // related to the charm archive located in the given archivePath. func sendBundleContent(w http.ResponseWriter, r *http.Request, archivePath string, sender bundleContentSenderFunc) { bundle, err := charm.ReadBundle(archivePath) if err != nil { http.Error( w, fmt.Sprintf("unable to read archive in %q: %v", archivePath, err), http.StatusInternalServerError) return } // The bundleContentSenderFunc will set up and send an appropriate response. sender(w, r, bundle) } // manifestSender sends a JSON-encoded response to the client including the // list of files contained in the charm bundle. func (h *charmsHandler) manifestSender(w http.ResponseWriter, r *http.Request, bundle *charm.Bundle) { manifest, err := bundle.Manifest() if err != nil { http.Error( w, fmt.Sprintf("unable to read archive in %q: %v", bundle.Path, err), http.StatusInternalServerError) return } h.sendJSON(w, http.StatusOK, &params.CharmsResponse{Files: manifest.SortedValues()}) } // fileSender returns a bundleContentSenderFunc which is responsible for sending // the contents of filePath included in the given charm bundle. If filePath does // not identify a file or a symlink, a 403 forbidden error is returned. func (h *charmsHandler) fileSender(filePath string) bundleContentSenderFunc { return func(w http.ResponseWriter, r *http.Request, bundle *charm.Bundle) { // TODO(fwereade) 2014-01-27 bug #1285685 // This doesn't handle symlinks helpfully, and should be talking in // terms of bundles rather than zip readers; but this demands thought // and design and is not amenable to a quick fix. zipReader, err := zip.OpenReader(bundle.Path) if err != nil { http.Error( w, fmt.Sprintf("unable to read charm: %v", err), http.StatusInternalServerError) return } defer zipReader.Close() for _, file := range zipReader.File { if path.Clean(file.Name) != filePath { continue } fileInfo := file.FileInfo() if fileInfo.IsDir() { http.Error(w, "directory listing not allowed", http.StatusForbidden) return } contents, err := file.Open() if err != nil { http.Error( w, fmt.Sprintf("unable to read file %q: %v", filePath, err), http.StatusInternalServerError) return } defer contents.Close() ctype := mime.TypeByExtension(filepath.Ext(filePath)) if ctype != "" { w.Header().Set("Content-Type", ctype) } w.Header().Set("Content-Length", strconv.FormatInt(fileInfo.Size(), 10)) w.WriteHeader(http.StatusOK) io.Copy(w, contents) return } http.NotFound(w, r) return } } // sendError sends a JSON-encoded error response. func (h *charmsHandler) sendError(w http.ResponseWriter, statusCode int, message string) error { return h.sendJSON(w, statusCode, &params.CharmsResponse{Error: message}) } // processPost handles a charm upload POST request after authentication. func (h *charmsHandler) processPost(r *http.Request) (*charm.URL, error) { query := r.URL.Query() series := query.Get("series") if series == "" { return nil, fmt.Errorf("expected series=URL argument") } // Make sure the content type is zip. contentType := r.Header.Get("Content-Type") if contentType != "application/zip" { return nil, fmt.Errorf("expected Content-Type: application/zip, got: %v", contentType) } tempFile, err := ioutil.TempFile("", "charm") if err != nil { return nil, fmt.Errorf("cannot create temp file: %v", err) } defer tempFile.Close() defer os.Remove(tempFile.Name()) if _, err := io.Copy(tempFile, r.Body); err != nil { return nil, fmt.Errorf("error processing file upload: %v", err) } err = h.processUploadedArchive(tempFile.Name()) if err != nil { return nil, err } archive, err := charm.ReadBundle(tempFile.Name()) if err != nil { return nil, fmt.Errorf("invalid charm archive: %v", err) } // We got it, now let's reserve a charm URL for it in state. archiveURL := &charm.URL{ Reference: charm.Reference{ Schema: "local", Name: archive.Meta().Name, Revision: archive.Revision(), }, Series: series, } preparedURL, err := h.state.PrepareLocalCharmUpload(archiveURL) if err != nil { return nil, err } // Now we need to repackage it with the reserved URL, upload it to // provider storage and update the state. err = h.repackageAndUploadCharm(archive, preparedURL) if err != nil { return nil, err } // All done. return preparedURL, nil } // processUploadedArchive opens the given charm archive from path, // inspects it to see if it has all files at the root of the archive // or it has subdirs. It repackages the archive so it has all the // files at the root dir, if necessary, replacing the original archive // at path. func (h *charmsHandler) processUploadedArchive(path string) error { // Open the archive as a zip. f, err := os.OpenFile(path, os.O_RDWR, 0644) if err != nil { return err } defer f.Close() fi, err := f.Stat() if err != nil { return err } zipr, err := zip.NewReader(f, fi.Size()) if err != nil { return errgo.Annotate(err, "cannot open charm archive") } // Find out the root dir prefix from the archive. rootDir, err := h.findArchiveRootDir(zipr) if err != nil { return errgo.Annotate(err, "cannot read charm archive") } if rootDir == "." { // Normal charm, just use charm.ReadBundle(). return nil } // There is one or more subdirs, so we need extract it to a temp // dir and then read it as a charm dir. tempDir, err := ioutil.TempDir("", "charm-extract") if err != nil { return errgo.Annotate(err, "cannot create temp directory") } defer os.RemoveAll(tempDir) if err := ziputil.Extract(zipr, tempDir, rootDir); err != nil { return errgo.Annotate(err, "cannot extract charm archive") } dir, err := charm.ReadDir(tempDir) if err != nil { return errgo.Annotate(err, "cannot read extracted archive") } // Now repackage the dir as a bundle at the original path. if err := f.Truncate(0); err != nil { return err } if err := dir.BundleTo(f); err != nil { return err } return nil } // findArchiveRootDir scans a zip archive and returns the rootDir of // the archive, the one containing metadata.yaml, config.yaml and // revision files, or an error if the archive appears invalid. func (h *charmsHandler) findArchiveRootDir(zipr *zip.Reader) (string, error) { paths, err := ziputil.Find(zipr, "metadata.yaml") if err != nil { return "", err } switch len(paths) { case 0: return "", fmt.Errorf("invalid charm archive: missing metadata.yaml") case 1: default: sort.Sort(byDepth(paths)) if depth(paths[0]) == depth(paths[1]) { return "", fmt.Errorf("invalid charm archive: ambiguous root directory") } } return filepath.Dir(paths[0]), nil } func depth(path string) int { return strings.Count(path, "/") } type byDepth []string func (d byDepth) Len() int { return len(d) } func (d byDepth) Swap(i, j int) { d[i], d[j] = d[j], d[i] } func (d byDepth) Less(i, j int) bool { return depth(d[i]) < depth(d[j]) } // repackageAndUploadCharm expands the given charm archive to a // temporary directoy, repackages it with the given curl's revision, // then uploads it to providr storage, and finally updates the state. func (h *charmsHandler) repackageAndUploadCharm(archive *charm.Bundle, curl *charm.URL) error { // Create a temp dir to contain the extracted charm // dir and the repackaged archive. tempDir, err := ioutil.TempDir("", "charm-download") if err != nil { return errgo.Annotate(err, "cannot create temp directory") } defer os.RemoveAll(tempDir) extractPath := filepath.Join(tempDir, "extracted") repackagedPath := filepath.Join(tempDir, "repackaged.zip") repackagedArchive, err := os.Create(repackagedPath) if err != nil { return errgo.Annotate(err, "cannot repackage uploaded charm") } defer repackagedArchive.Close() // Expand and repack it with the revision specified by curl. archive.SetRevision(curl.Revision) if err := archive.ExpandTo(extractPath); err != nil { return errgo.Annotate(err, "cannot extract uploaded charm") } charmDir, err := charm.ReadDir(extractPath) if err != nil { return errgo.Annotate(err, "cannot read extracted charm") } // Bundle the charm and calculate its sha256 hash at the // same time. hash := sha256.New() err = charmDir.BundleTo(io.MultiWriter(hash, repackagedArchive)) if err != nil { return errgo.Annotate(err, "cannot repackage uploaded charm") } bundleSHA256 := hex.EncodeToString(hash.Sum(nil)) size, err := repackagedArchive.Seek(0, 2) if err != nil { return errgo.Annotate(err, "cannot get charm file size") } // Now upload to provider storage. if _, err := repackagedArchive.Seek(0, 0); err != nil { return errgo.Annotate(err, "cannot rewind the charm file reader") } storage, err := environs.GetStorage(h.state) if err != nil { return errgo.Annotate(err, "cannot access provider storage") } name := charm.Quote(curl.String()) if err := storage.Put(name, repackagedArchive, size); err != nil { return errgo.Annotate(err, "cannot upload charm to provider storage") } storageURL, err := storage.URL(name) if err != nil { return errgo.Annotate(err, "cannot get storage URL for charm") } bundleURL, err := url.Parse(storageURL) if err != nil { return errgo.Annotate(err, "cannot parse storage URL") } // And finally, update state. _, err = h.state.UpdateUploadedCharm(archive, curl, bundleURL, bundleSHA256) if err != nil { return errgo.Annotate(err, "cannot update uploaded charm in state") } return nil } // processGet handles a charm file GET request after authentication. // It returns the bundle path, the requested file path (if any) and an error. func (h *charmsHandler) processGet(r *http.Request) (string, string, error) { query := r.URL.Query() // Retrieve and validate query parameters. curl := query.Get("url") if curl == "" { return "", "", fmt.Errorf("expected url=CharmURL query argument") } var filePath string file := query.Get("file") if file == "" { filePath = "" } else { filePath = path.Clean(file) } // Prepare the bundle directories. name := charm.Quote(curl) charmArchivePath := filepath.Join(h.dataDir, "charm-get-cache", name+".zip") // Check if the charm archive is already in the cache. if _, err := os.Stat(charmArchivePath); os.IsNotExist(err) { // Download the charm archive and save it to the cache. if err = h.downloadCharm(name, charmArchivePath); err != nil { return "", "", fmt.Errorf("unable to retrieve and save the charm: %v", err) } } else if err != nil { return "", "", fmt.Errorf("cannot access the charms cache: %v", err) } return charmArchivePath, filePath, nil } // downloadCharm downloads the given charm name from the provider storage and // saves the corresponding zip archive to the given charmArchivePath. func (h *charmsHandler) downloadCharm(name, charmArchivePath string) error { // Get the provider storage. storage, err := environs.GetStorage(h.state) if err != nil { return errgo.Annotate(err, "cannot access provider storage") } // Use the storage to retrieve and save the charm archive. reader, err := storage.Get(name) if err != nil { return errgo.Annotate(err, "charm not found in the provider storage") } defer reader.Close() data, err := ioutil.ReadAll(reader) if err != nil { return errgo.Annotate(err, "cannot read charm data") } // In order to avoid races, the archive is saved in a temporary file which // is then atomically renamed. The temporary file is created in the // charm cache directory so that we can safely assume the rename source and // target live in the same file system. cacheDir := filepath.Dir(charmArchivePath) if err = os.MkdirAll(cacheDir, 0755); err != nil { return errgo.Annotate(err, "cannot create the charms cache") } tempCharmArchive, err := ioutil.TempFile(cacheDir, "charm") if err != nil { return errgo.Annotate(err, "cannot create charm archive temp file") } defer tempCharmArchive.Close() if err = ioutil.WriteFile(tempCharmArchive.Name(), data, 0644); err != nil { return errgo.Annotate(err, "error processing charm archive download") } if err = os.Rename(tempCharmArchive.Name(), charmArchivePath); err != nil { defer os.Remove(tempCharmArchive.Name()) return errgo.Annotate(err, "error renaming the charm archive") } return nil } �������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/admin.go�������������������������������0000644�0000153�0000161�00000010250�12321735776�025432� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver import ( stderrors "errors" "sync" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/rpc" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/presence" ) func newStateServer(srv *Server, rpcConn *rpc.Conn, reqNotifier *requestNotifier) *initialRoot { r := &initialRoot{ srv: srv, rpcConn: rpcConn, } r.admin = &srvAdmin{ root: r, reqNotifier: reqNotifier, } return r } // initialRoot implements the API that a client first sees // when connecting to the API. We start serving a different // API once the user has logged in. type initialRoot struct { srv *Server rpcConn *rpc.Conn admin *srvAdmin } // Admin returns an object that provides API access // to methods that can be called even when not // authenticated. func (r *initialRoot) Admin(id string) (*srvAdmin, error) { if id != "" { // Safeguard id for possible future use. return nil, common.ErrBadId } return r.admin, nil } // srvAdmin is the only object that unlogged-in // clients can access. It holds any methods // that are needed to log in. type srvAdmin struct { mu sync.Mutex root *initialRoot loggedIn bool reqNotifier *requestNotifier } var errAlreadyLoggedIn = stderrors.New("already logged in") // Login logs in with the provided credentials. // All subsequent requests on the connection will // act as the authenticated user. func (a *srvAdmin) Login(c params.Creds) error { a.mu.Lock() defer a.mu.Unlock() if a.loggedIn { // This can only happen if Login is called concurrently. return errAlreadyLoggedIn } entity, err := checkCreds(a.root.srv.state, c) if err != nil { return err } if a.reqNotifier != nil { a.reqNotifier.login(entity.Tag()) } // We have authenticated the user; now choose an appropriate API // to serve to them. newRoot, err := a.apiRootForEntity(entity, c) if err != nil { return err } a.root.rpcConn.Serve(newRoot, serverError) return nil } func checkCreds(st *state.State, c params.Creds) (taggedAuthenticator, error) { entity0, err := st.FindEntity(c.AuthTag) if err != nil && !errors.IsNotFoundError(err) { return nil, err } // We return the same error when an entity // does not exist as for a bad password, so that // we don't allow unauthenticated users to find information // about existing entities. entity, ok := entity0.(taggedAuthenticator) if !ok { return nil, common.ErrBadCreds } if err != nil || !entity.PasswordValid(c.Password) { return nil, common.ErrBadCreds } return entity, nil } // machinePinger wraps a presence.Pinger. type machinePinger struct { *presence.Pinger } // Stop implements Pinger.Stop() as Pinger.Kill(), needed at // connection closing time to properly stop the wrapped pinger. func (p *machinePinger) Stop() error { if err := p.Pinger.Stop(); err != nil { return err } return p.Pinger.Kill() } func (a *srvAdmin) apiRootForEntity(entity taggedAuthenticator, c params.Creds) (interface{}, error) { // TODO(rog) choose appropriate object to serve. newRoot := newSrvRoot(a.root, entity) // If this is a machine agent connecting, we need to check the // nonce matches, otherwise the wrong agent might be trying to // connect. machine, ok := entity.(*state.Machine) if ok { if !machine.CheckProvisioned(c.Nonce) { return nil, state.NotProvisionedError(machine.Id()) } } setAgentAliver, ok := entity.(interface { SetAgentAlive() (*presence.Pinger, error) }) if ok { // A machine or unit agent has connected, so start a pinger to // announce it's now alive, and set up the API pinger // so that the connection will be terminated if a sufficient // interval passes between pings. pinger, err := setAgentAliver.SetAgentAlive() if err != nil { return nil, err } newRoot.resources.Register(&machinePinger{pinger}) action := func() { if err := newRoot.rpcConn.Close(); err != nil { logger.Errorf("error closing the RPC connection: %v", err) } } newRoot.pingTimeout = newPingTimeout(action, maxPingInterval) } return newRoot, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/export_test.go�������������������������0000644�0000153�0000161�00000000405�12321735776�026723� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver import "reflect" var ( RootType = reflect.TypeOf(&srvRoot{}) NewPingTimeout = newPingTimeout MaxPingInterval = &maxPingInterval ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/logger/��������������������������������0000755�0000153�0000161�00000000000�12321736000�025251� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/logger/logger.go�����������������������0000644�0000153�0000161�00000006031�12321735776�027102� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package logger import ( "github.com/juju/loggo" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/watcher" ) var logger = loggo.GetLogger("juju.api.logger") // Logger defines the methods on the logger API end point. Unfortunately, the // api infrastructure doesn't allow interfaces to be used as an actual // endpoint because our rpc mechanism panics. However, I still feel that this // provides a useful documentation purpose. type Logger interface { WatchLoggingConfig(args params.Entities) params.NotifyWatchResults LoggingConfig(args params.Entities) params.StringResults } // LoggerAPI implements the Logger interface and is the concrete // implementation of the api end point. type LoggerAPI struct { state *state.State resources *common.Resources authorizer common.Authorizer } var _ Logger = (*LoggerAPI)(nil) // NewLoggerAPI creates a new server-side logger API end point. func NewLoggerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*LoggerAPI, error) { if !authorizer.AuthMachineAgent() && !authorizer.AuthUnitAgent() { return nil, common.ErrPerm } return &LoggerAPI{state: st, resources: resources, authorizer: authorizer}, nil } // WatchLoggingConfig starts a watcher to track changes to the logging config // for the agents specified.. Unfortunately the current infrastruture makes // watching parts of the config non-trivial, so currently any change to the // config will cause the watcher to notify the client. func (api *LoggerAPI) WatchLoggingConfig(arg params.Entities) params.NotifyWatchResults { result := make([]params.NotifyWatchResult, len(arg.Entities)) for i, entity := range arg.Entities { err := common.ErrPerm if api.authorizer.AuthOwner(entity.Tag) { watch := api.state.WatchForEnvironConfigChanges() // Consume the initial event. Technically, API calls to Watch // 'transmit' the initial event in the Watch response. But // NotifyWatchers have no state to transmit. if _, ok := <-watch.Changes(); ok { result[i].NotifyWatcherId = api.resources.Register(watch) err = nil } else { err = watcher.MustErr(watch) } } result[i].Error = common.ServerError(err) } return params.NotifyWatchResults{result} } // LoggingConfig reports the logging configuration for the agents specified. func (api *LoggerAPI) LoggingConfig(arg params.Entities) params.StringResults { if len(arg.Entities) == 0 { return params.StringResults{} } results := make([]params.StringResult, len(arg.Entities)) config, configErr := api.state.EnvironConfig() for i, entity := range arg.Entities { err := common.ErrPerm if api.authorizer.AuthOwner(entity.Tag) { if configErr == nil { results[i].Result = config.LoggingConfig() err = nil } else { err = configErr } } results[i].Error = common.ServerError(err) } return params.StringResults{results} } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/logger/package_test.go�����������������0000644�0000153�0000161�00000000366�12321735642�030252� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package logger_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/logger/logger_test.go������������������0000644�0000153�0000161�00000011463�12321735642�030136� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package logger_test import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/apiserver/logger" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" statetesting "launchpad.net/juju-core/state/testing" ) type loggerSuite struct { jujutesting.JujuConnSuite // These are raw State objects. Use them for setup and assertions, but // should never be touched by the API calls themselves rawMachine *state.Machine logger *logger.LoggerAPI resources *common.Resources authorizer apiservertesting.FakeAuthorizer } var _ = gc.Suite(&loggerSuite{}) func (s *loggerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.resources = common.NewResources() s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) // Create a machine to work with var err error s.rawMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // The default auth is as the machine agent s.authorizer = apiservertesting.FakeAuthorizer{ Tag: s.rawMachine.Tag(), LoggedIn: true, MachineAgent: true, } s.logger, err = logger.NewLoggerAPI(s.State, s.resources, s.authorizer) c.Assert(err, gc.IsNil) } func (s *loggerSuite) TestNewLoggerAPIRefusesNonAgent(c *gc.C) { // We aren't even a machine agent anAuthorizer := s.authorizer anAuthorizer.MachineAgent = false endPoint, err := logger.NewLoggerAPI(s.State, s.resources, anAuthorizer) c.Assert(endPoint, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *loggerSuite) TestNewLoggerAPIAcceptsUnitAgent(c *gc.C) { // We aren't even a machine agent anAuthorizer := s.authorizer anAuthorizer.UnitAgent = true anAuthorizer.MachineAgent = false endPoint, err := logger.NewLoggerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.IsNil) c.Assert(endPoint, gc.NotNil) } func (s *loggerSuite) TestWatchLoggingConfigNothing(c *gc.C) { // Not an error to watch nothing results := s.logger.WatchLoggingConfig(params.Entities{}) c.Assert(results.Results, gc.HasLen, 0) } func (s *loggerSuite) setLoggingConfig(c *gc.C, loggingConfig string) { err := s.State.UpdateEnvironConfig(map[string]interface{}{"logging-config": loggingConfig}, nil, nil) c.Assert(err, gc.IsNil) envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(envConfig.LoggingConfig(), gc.Equals, loggingConfig) } func (s *loggerSuite) TestWatchLoggingConfig(c *gc.C) { args := params.Entities{ Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}, } results := s.logger.WatchLoggingConfig(args) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].NotifyWatcherId, gc.Not(gc.Equals), "") c.Assert(results.Results[0].Error, gc.IsNil) resource := s.resources.Get(results.Results[0].NotifyWatcherId) c.Assert(resource, gc.NotNil) w := resource.(state.NotifyWatcher) wc := statetesting.NewNotifyWatcherC(c, s.State, w) wc.AssertNoChange() newLoggingConfig := "<root>=WARN;juju.log.test=DEBUG;unit=INFO" s.setLoggingConfig(c, newLoggingConfig) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *loggerSuite) TestWatchLoggingConfigRefusesWrongAgent(c *gc.C) { // We are a machine agent, but not the one we are trying to track args := params.Entities{ Entities: []params.Entity{{Tag: "machine-12354"}}, } results := s.logger.WatchLoggingConfig(args) // It is not an error to make the request, but the specific item is rejected c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].NotifyWatcherId, gc.Equals, "") c.Assert(results.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *loggerSuite) TestLoggingConfigForNoone(c *gc.C) { // Not an error to request nothing, dumb, but not an error. results := s.logger.LoggingConfig(params.Entities{}) c.Assert(results.Results, gc.HasLen, 0) } func (s *loggerSuite) TestLoggingConfigRefusesWrongAgent(c *gc.C) { args := params.Entities{ Entities: []params.Entity{{Tag: "machine-12354"}}, } results := s.logger.LoggingConfig(args) c.Assert(results.Results, gc.HasLen, 1) result := results.Results[0] c.Assert(result.Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *loggerSuite) TestLoggingConfigForAgent(c *gc.C) { newLoggingConfig := "<root>=WARN;juju.log.test=DEBUG;unit=INFO" s.setLoggingConfig(c, newLoggingConfig) args := params.Entities{ Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}, } results := s.logger.LoggingConfig(args) c.Assert(results.Results, gc.HasLen, 1) result := results.Results[0] c.Assert(result.Error, gc.IsNil) c.Assert(result.Result, gc.Equals, newLoggingConfig) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/machine/�������������������������������0000755�0000153�0000161�00000000000�12321736000�025376� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/machine/machiner_test.go���������������0000644�0000153�0000161�00000014044�12321735776�030600� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package machine_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/apiserver/machine" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" statetesting "launchpad.net/juju-core/state/testing" ) type machinerSuite struct { commonSuite resources *common.Resources machiner *machine.MachinerAPI } var _ = gc.Suite(&machinerSuite{}) func (s *machinerSuite) SetUpTest(c *gc.C) { s.commonSuite.SetUpTest(c) // Create the resource registry separately to track invocations to // Register. s.resources = common.NewResources() // Create a machiner API for machine 1. machiner, err := machine.NewMachinerAPI( s.State, s.resources, s.authorizer, ) c.Assert(err, gc.IsNil) s.machiner = machiner } func (s *machinerSuite) TestMachinerFailsWithNonMachineAgentUser(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.MachineAgent = false aMachiner, err := machine.NewMachinerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.NotNil) c.Assert(aMachiner, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *machinerSuite) TestSetStatus(c *gc.C) { err := s.machine0.SetStatus(params.StatusStarted, "blah", nil) c.Assert(err, gc.IsNil) err = s.machine1.SetStatus(params.StatusStopped, "foo", nil) c.Assert(err, gc.IsNil) args := params.SetStatus{ Entities: []params.EntityStatus{ {Tag: "machine-1", Status: params.StatusError, Info: "not really"}, {Tag: "machine-0", Status: params.StatusStopped, Info: "foobar"}, {Tag: "machine-42", Status: params.StatusStarted, Info: "blah"}, }} result, err := s.machiner.SetStatus(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {nil}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) // Verify machine 0 - no change. status, info, _, err := s.machine0.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStarted) c.Assert(info, gc.Equals, "blah") // ...machine 1 is fine though. status, info, _, err = s.machine1.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "not really") } func (s *machinerSuite) TestLife(c *gc.C) { err := s.machine1.EnsureDead() c.Assert(err, gc.IsNil) err = s.machine1.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine1.Life(), gc.Equals, state.Dead) args := params.Entities{Entities: []params.Entity{ {Tag: "machine-1"}, {Tag: "machine-0"}, {Tag: "machine-42"}, }} result, err := s.machiner.Life(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.LifeResults{ Results: []params.LifeResult{ {Life: "dead"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *machinerSuite) TestEnsureDead(c *gc.C) { c.Assert(s.machine0.Life(), gc.Equals, state.Alive) c.Assert(s.machine1.Life(), gc.Equals, state.Alive) args := params.Entities{Entities: []params.Entity{ {Tag: "machine-1"}, {Tag: "machine-0"}, {Tag: "machine-42"}, }} result, err := s.machiner.EnsureDead(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {nil}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) err = s.machine0.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine0.Life(), gc.Equals, state.Alive) err = s.machine1.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine1.Life(), gc.Equals, state.Dead) // Try it again on a Dead machine; should work. args = params.Entities{ Entities: []params.Entity{{Tag: "machine-1"}}, } result, err = s.machiner.EnsureDead(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{{nil}}, }) // Verify Life is unchanged. err = s.machine1.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine1.Life(), gc.Equals, state.Dead) } func (s *machinerSuite) TestSetMachineAddresses(c *gc.C) { c.Assert(s.machine0.Addresses(), gc.HasLen, 0) c.Assert(s.machine1.Addresses(), gc.HasLen, 0) addresses := []instance.Address{ instance.NewAddress("127.0.0.1"), instance.NewAddress("8.8.8.8"), } args := params.SetMachinesAddresses{MachineAddresses: []params.MachineAddresses{ {Tag: "machine-1", Addresses: addresses}, {Tag: "machine-0", Addresses: addresses}, {Tag: "machine-42", Addresses: addresses}, }} result, err := s.machiner.SetMachineAddresses(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {nil}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) err = s.machine1.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine1.MachineAddresses(), gc.DeepEquals, addresses) err = s.machine0.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine0.MachineAddresses(), gc.HasLen, 0) } func (s *machinerSuite) TestWatch(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) args := params.Entities{Entities: []params.Entity{ {Tag: "machine-1"}, {Tag: "machine-0"}, {Tag: "machine-42"}, }} result, err := s.machiner.Watch(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ Results: []params.NotifyWatchResult{ {NotifyWatcherId: "1"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Verify the resource was registered and stop when done c.Assert(s.resources.Count(), gc.Equals, 1) c.Assert(result.Results[0].NotifyWatcherId, gc.Equals, "1") resource := s.resources.Get("1") defer statetesting.AssertStop(c, resource) // Check that the Watch has consumed the initial event ("returned" in // the Watch call) wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher)) wc.AssertNoChange() } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/machine/machiner.go��������������������0000644�0000153�0000161�00000004633�12321735642�027534� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The machiner package implements the API interface // used by the machiner worker. package machine import ( "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" ) // MachinerAPI implements the API used by the machiner worker. type MachinerAPI struct { *common.LifeGetter *common.StatusSetter *common.DeadEnsurer *common.AgentEntityWatcher *common.APIAddresser st *state.State auth common.Authorizer getCanModify common.GetAuthFunc getCanRead common.GetAuthFunc } // NewMachinerAPI creates a new instance of the Machiner API. func NewMachinerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*MachinerAPI, error) { if !authorizer.AuthMachineAgent() { return nil, common.ErrPerm } getCanModify := func() (common.AuthFunc, error) { return authorizer.AuthOwner, nil } getCanRead := func() (common.AuthFunc, error) { return authorizer.AuthOwner, nil } return &MachinerAPI{ LifeGetter: common.NewLifeGetter(st, getCanRead), StatusSetter: common.NewStatusSetter(st, getCanModify), DeadEnsurer: common.NewDeadEnsurer(st, getCanModify), AgentEntityWatcher: common.NewAgentEntityWatcher(st, resources, getCanRead), APIAddresser: common.NewAPIAddresser(st, resources), st: st, auth: authorizer, getCanModify: getCanModify, }, nil } func (api *MachinerAPI) getMachine(tag string) (*state.Machine, error) { entity, err := api.st.FindEntity(tag) if err != nil { return nil, err } return entity.(*state.Machine), nil } func (api *MachinerAPI) SetMachineAddresses(args params.SetMachinesAddresses) (params.ErrorResults, error) { results := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.MachineAddresses)), } canModify, err := api.getCanModify() if err != nil { return params.ErrorResults{}, err } for i, arg := range args.MachineAddresses { err := common.ErrPerm if canModify(arg.Tag) { var m *state.Machine m, err = api.getMachine(arg.Tag) if err == nil { err = m.SetMachineAddresses(arg.Addresses...) } else if errors.IsNotFoundError(err) { err = common.ErrPerm } } results.Results[i].Error = common.ServerError(err) } return results, nil } �����������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/machine/common_test.go�����������������0000644�0000153�0000161�00000001752�12321735642�030274� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package machine_test import ( stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" coretesting "launchpad.net/juju-core/testing" ) func Test(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type commonSuite struct { testing.JujuConnSuite authorizer apiservertesting.FakeAuthorizer machine0 *state.Machine machine1 *state.Machine } func (s *commonSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) var err error s.machine0, err = s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) s.machine1, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // Create a FakeAuthorizer so we can check permissions, // set up assuming machine 1 has logged in. s.authorizer = apiservertesting.FakeAuthorizer{ Tag: s.machine1.Tag(), LoggedIn: true, MachineAgent: true, } } ����������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/tools_test.go��������������������������0000644�0000153�0000161�00000015303�12321735642�026535� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver_test import ( "encoding/json" "io/ioutil" "net/http" "path" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/tools" ttesting "launchpad.net/juju-core/environs/tools/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" "path/filepath" ) type toolsSuite struct { authHttpSuite } var _ = gc.Suite(&toolsSuite{}) func (s *toolsSuite) SetUpSuite(c *gc.C) { s.JujuConnSuite.SetUpSuite(c) s.archiveContentType = "application/x-tar-gz" } func (s *toolsSuite) TestToolsUploadedSecurely(c *gc.C) { _, info, err := s.APIConn.Environ.StateInfo() c.Assert(err, gc.IsNil) uri := "http://" + info.Addrs[0] + "/tools" _, err = s.sendRequest(c, "", "", "PUT", uri, "", nil) c.Assert(err, gc.ErrorMatches, `.*malformed HTTP response.*`) } func (s *toolsSuite) TestRequiresAuth(c *gc.C) { resp, err := s.sendRequest(c, "", "", "GET", s.toolsURI(c, ""), "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusUnauthorized, "unauthorized") } func (s *toolsSuite) TestRequiresPOST(c *gc.C) { resp, err := s.authRequest(c, "PUT", s.toolsURI(c, ""), "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusMethodNotAllowed, `unsupported method: "PUT"`) } func (s *toolsSuite) TestAuthRequiresUser(c *gc.C) { // Add a machine and try to login. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("foo", "fake_nonce", nil) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = machine.SetPassword(password) c.Assert(err, gc.IsNil) resp, err := s.sendRequest(c, machine.Tag(), password, "POST", s.toolsURI(c, ""), "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusUnauthorized, "unauthorized") // Now try a user login. resp, err = s.authRequest(c, "POST", s.toolsURI(c, ""), "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusBadRequest, "expected binaryVersion argument") } func (s *toolsSuite) TestUploadRequiresVersion(c *gc.C) { resp, err := s.authRequest(c, "POST", s.toolsURI(c, ""), "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusBadRequest, "expected binaryVersion argument") } func (s *toolsSuite) TestUploadFailsWithNoTools(c *gc.C) { // Create an empty file. tempFile, err := ioutil.TempFile(c.MkDir(), "tools") c.Assert(err, gc.IsNil) resp, err := s.uploadRequest(c, s.toolsURI(c, "?binaryVersion=1.18.0-quantal-amd64"), true, tempFile.Name()) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusBadRequest, "no tools uploaded") } func (s *toolsSuite) TestUploadFailsWithInvalidContentType(c *gc.C) { // Create an empty file. tempFile, err := ioutil.TempFile(c.MkDir(), "tools") c.Assert(err, gc.IsNil) // Now try with the default Content-Type. resp, err := s.uploadRequest(c, s.toolsURI(c, "?binaryVersion=1.18.0-quantal-amd64"), false, tempFile.Name()) c.Assert(err, gc.IsNil) s.assertErrorResponse( c, resp, http.StatusBadRequest, "expected Content-Type: application/x-tar-gz, got: application/octet-stream") } func (s *toolsSuite) TestUpload(c *gc.C) { // Make some fake tools. localStorage := c.MkDir() vers := version.MustParseBinary("1.9.0-quantal-amd64") versionStrings := []string{vers.String()} expectedTools := ttesting.MakeToolsWithCheckSum(c, localStorage, "releases", versionStrings) // Now try uploading them. toolsFile := tools.StorageName(vers) resp, err := s.uploadRequest( c, s.toolsURI(c, "?binaryVersion="+vers.String()), true, path.Join(localStorage, toolsFile)) c.Assert(err, gc.IsNil) // Check the response. stor := s.Conn.Environ.Storage() toolsURL, err := stor.URL(tools.StorageName(vers)) c.Assert(err, gc.IsNil) expectedTools[0].URL = toolsURL s.assertUploadResponse(c, resp, expectedTools[0]) // Check the contents. r, err := stor.Get(tools.StorageName(vers)) c.Assert(err, gc.IsNil) uploadedData, err := ioutil.ReadAll(r) c.Assert(err, gc.IsNil) expectedData, err := ioutil.ReadFile(filepath.Join(localStorage, tools.StorageName(vers))) c.Assert(err, gc.IsNil) c.Assert(uploadedData, gc.DeepEquals, expectedData) } func (s *toolsSuite) TestUploadFakeSeries(c *gc.C) { // Make some fake tools. localStorage := c.MkDir() vers := version.MustParseBinary("1.9.0-quantal-amd64") versionStrings := []string{vers.String()} expectedTools := ttesting.MakeToolsWithCheckSum(c, localStorage, "releases", versionStrings) // Now try uploading them. toolsFile := tools.StorageName(vers) params := "?binaryVersion=" + vers.String() + "&series=precise,trusty" resp, err := s.uploadRequest(c, s.toolsURI(c, params), true, path.Join(localStorage, toolsFile)) c.Assert(err, gc.IsNil) // Check the response. stor := s.Conn.Environ.Storage() toolsURL, err := stor.URL(tools.StorageName(vers)) c.Assert(err, gc.IsNil) expectedTools[0].URL = toolsURL s.assertUploadResponse(c, resp, expectedTools[0]) // Check the contents. for _, series := range []string{"precise", "quantal", "trusty"} { toolsVersion := vers toolsVersion.Series = series r, err := stor.Get(tools.StorageName(toolsVersion)) c.Assert(err, gc.IsNil) uploadedData, err := ioutil.ReadAll(r) c.Assert(err, gc.IsNil) expectedData, err := ioutil.ReadFile(filepath.Join(localStorage, tools.StorageName(vers))) c.Assert(err, gc.IsNil) c.Assert(uploadedData, gc.DeepEquals, expectedData) } } func (s *toolsSuite) toolsURI(c *gc.C, query string) string { _, info, err := s.APIConn.Environ.StateInfo() c.Assert(err, gc.IsNil) return "https://" + info.Addrs[0] + "/tools" + query } func (s *toolsSuite) assertUploadResponse(c *gc.C, resp *http.Response, agentTools *coretools.Tools) { body := assertResponse(c, resp, http.StatusOK, "application/json") toolsResult := jsonToolsResponse(c, body) c.Check(toolsResult.Error, gc.IsNil) c.Check(toolsResult.Tools, gc.DeepEquals, agentTools) } func (s *toolsSuite) assertGetFileResponse(c *gc.C, resp *http.Response, expBody, expContentType string) { body := assertResponse(c, resp, http.StatusOK, expContentType) c.Check(string(body), gc.Equals, expBody) } func (s *toolsSuite) assertErrorResponse(c *gc.C, resp *http.Response, expCode int, expError string) { body := assertResponse(c, resp, expCode, "application/json") err := jsonToolsResponse(c, body).Error c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, expError) } func jsonToolsResponse(c *gc.C, body []byte) (jsonResponse params.ToolsResult) { err := json.Unmarshal(body, &jsonResponse) c.Assert(err, gc.IsNil) return } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/utils.go�������������������������������0000644�0000153�0000161�00000001563�12321735642�025501� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver import ( "fmt" "launchpad.net/juju-core/state" ) // isMachineWithJob returns whether the given entity is a machine that // is configured to run the given job. func isMachineWithJob(e state.Authenticator, j state.MachineJob) bool { m, ok := e.(*state.Machine) if !ok { return false } for _, mj := range m.Jobs() { if mj == j { return true } } return false } // isAgent returns whether the given entity is an agent. func isAgent(e state.Authenticator) bool { _, isUser := e.(*state.User) return !isUser } func setPassword(e state.Authenticator, password string) error { // Catch expected common case of misspelled // or missing Password parameter. if password == "" { return fmt.Errorf("password is empty") } return e.SetPassword(password) } ���������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/rsyslog/�������������������������������0000755�0000153�0000161�00000000000�12321735643�025510� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/rsyslog/rsyslog_test.go����������������0000644�0000153�0000161�00000005304�12321735642�030601� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rsyslog_test import ( "encoding/pem" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" apirsyslog "launchpad.net/juju-core/state/api/rsyslog" "launchpad.net/juju-core/state/apiserver/common" commontesting "launchpad.net/juju-core/state/apiserver/common/testing" "launchpad.net/juju-core/state/apiserver/rsyslog" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" coretesting "launchpad.net/juju-core/testing" ) type rsyslogSuite struct { testing.JujuConnSuite *commontesting.EnvironWatcherTest authorizer apiservertesting.FakeAuthorizer resources *common.Resources } var _ = gc.Suite(&rsyslogSuite{}) func (s *rsyslogSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.authorizer = apiservertesting.FakeAuthorizer{ LoggedIn: true, EnvironManager: true, } s.resources = common.NewResources() api, err := rsyslog.NewRsyslogAPI(s.State, s.resources, s.authorizer) c.Assert(err, gc.IsNil) s.EnvironWatcherTest = commontesting.NewEnvironWatcherTest( api, s.State, s.resources, commontesting.NoSecrets) } func verifyRsyslogCACert(c *gc.C, st *apirsyslog.State, expected []byte) { cfg, err := st.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.RsyslogCACert(), gc.DeepEquals, expected) } func (s *rsyslogSuite) TestSetRsyslogCert(c *gc.C) { st, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) err := st.Rsyslog().SetRsyslogCert([]byte(coretesting.CACert)) c.Assert(err, gc.IsNil) verifyRsyslogCACert(c, st.Rsyslog(), []byte(coretesting.CACert)) } func (s *rsyslogSuite) TestSetRsyslogCertNil(c *gc.C) { st, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) err := st.Rsyslog().SetRsyslogCert(nil) c.Assert(err, gc.ErrorMatches, "no certificates found") verifyRsyslogCACert(c, st.Rsyslog(), nil) } func (s *rsyslogSuite) TestSetRsyslogCertInvalid(c *gc.C) { st, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) err := st.Rsyslog().SetRsyslogCert(pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: []byte("not a valid certificate"), })) c.Assert(err, gc.ErrorMatches, ".*structure error.*") verifyRsyslogCACert(c, st.Rsyslog(), nil) } func (s *rsyslogSuite) TestSetRsyslogCertPerms(c *gc.C) { st, _ := s.OpenAPIAsNewMachine(c, state.JobHostUnits) err := st.Rsyslog().SetRsyslogCert([]byte(coretesting.CACert)) c.Assert(err, gc.ErrorMatches, "invalid entity name or password") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) // Verify no change was effected. verifyRsyslogCACert(c, st.Rsyslog(), nil) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/rsyslog/rsyslog.go���������������������0000644�0000153�0000161�00000002773�12321735642�027551� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rsyslog import ( "launchpad.net/juju-core/cert" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" ) // RsyslogAPI implements the API used by the rsyslog worker. type RsyslogAPI struct { *common.EnvironWatcher st *state.State canModify bool } // NewRsyslogAPI creates a new instance of the Rsyslog API. func NewRsyslogAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*RsyslogAPI, error) { // Can always watch for environ changes. getCanWatch := common.AuthAlways(true) // Does not get the secrets. getCanReadSecrets := common.AuthAlways(false) return &RsyslogAPI{ EnvironWatcher: common.NewEnvironWatcher(st, resources, getCanWatch, getCanReadSecrets), st: st, canModify: authorizer.AuthEnvironManager(), }, nil } func (api *RsyslogAPI) SetRsyslogCert(args params.SetRsyslogCertParams) (params.ErrorResult, error) { var result params.ErrorResult if !api.canModify { result.Error = common.ServerError(common.ErrBadCreds) return result, nil } if _, err := cert.ParseCert(args.CACert); err != nil { result.Error = common.ServerError(err) return result, nil } attrs := map[string]interface{}{"rsyslog-ca-cert": string(args.CACert)} if err := api.st.UpdateEnvironConfig(attrs, nil, nil); err != nil { result.Error = common.ServerError(err) } return result, nil } �����juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/rsyslog/package_test.go����������������0000644�0000153�0000161�00000000367�12321735642�030476� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rsyslog_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/charms_test.go�������������������������0000644�0000153�0000161�00000037350�12321735642�026660� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver_test import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "net/http" "net/url" "os" "path/filepath" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/environs" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) type authHttpSuite struct { jujutesting.JujuConnSuite userTag string password string archiveContentType string } func (s *authHttpSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) user, err := s.State.AddUser("joe", password) c.Assert(err, gc.IsNil) s.userTag = user.Tag() s.password = password } func (s *authHttpSuite) sendRequest(c *gc.C, tag, password, method, uri, contentType string, body io.Reader) (*http.Response, error) { req, err := http.NewRequest(method, uri, body) c.Assert(err, gc.IsNil) if tag != "" && password != "" { req.SetBasicAuth(tag, password) } if contentType != "" { req.Header.Set("Content-Type", contentType) } return utils.GetNonValidatingHTTPClient().Do(req) } func (s *authHttpSuite) authRequest(c *gc.C, method, uri, contentType string, body io.Reader) (*http.Response, error) { return s.sendRequest(c, s.userTag, s.password, method, uri, contentType, body) } func (s *authHttpSuite) uploadRequest(c *gc.C, uri string, asZip bool, path string) (*http.Response, error) { contentType := "application/octet-stream" if asZip { contentType = s.archiveContentType } if path == "" { return s.authRequest(c, "POST", uri, contentType, nil) } file, err := os.Open(path) c.Assert(err, gc.IsNil) defer file.Close() return s.authRequest(c, "POST", uri, contentType, file) } type charmsSuite struct { authHttpSuite } var _ = gc.Suite(&charmsSuite{}) func (s *charmsSuite) SetUpSuite(c *gc.C) { s.JujuConnSuite.SetUpSuite(c) s.archiveContentType = "application/zip" } func (s *charmsSuite) TestCharmsServedSecurely(c *gc.C) { _, info, err := s.APIConn.Environ.StateInfo() c.Assert(err, gc.IsNil) uri := "http://" + info.Addrs[0] + "/charms" _, err = s.sendRequest(c, "", "", "GET", uri, "", nil) c.Assert(err, gc.ErrorMatches, `.*malformed HTTP response.*`) } func (s *charmsSuite) TestRequiresAuth(c *gc.C) { resp, err := s.sendRequest(c, "", "", "GET", s.charmsURI(c, ""), "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusUnauthorized, "unauthorized") } func (s *charmsSuite) TestRequiresPOSTorGET(c *gc.C) { resp, err := s.authRequest(c, "PUT", s.charmsURI(c, ""), "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusMethodNotAllowed, `unsupported method: "PUT"`) } func (s *charmsSuite) TestAuthRequiresUser(c *gc.C) { // Add a machine and try to login. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("foo", "fake_nonce", nil) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = machine.SetPassword(password) c.Assert(err, gc.IsNil) resp, err := s.sendRequest(c, machine.Tag(), password, "GET", s.charmsURI(c, ""), "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusUnauthorized, "unauthorized") // Now try a user login. resp, err = s.authRequest(c, "GET", s.charmsURI(c, ""), "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusBadRequest, "expected url=CharmURL query argument") } func (s *charmsSuite) TestUploadRequiresSeries(c *gc.C) { resp, err := s.authRequest(c, "POST", s.charmsURI(c, ""), "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusBadRequest, "expected series=URL argument") } func (s *charmsSuite) TestUploadFailsWithInvalidZip(c *gc.C) { // Create an empty file. tempFile, err := ioutil.TempFile(c.MkDir(), "charm") c.Assert(err, gc.IsNil) // Pretend we upload a zip by setting the Content-Type, so we can // check the error at extraction time later. resp, err := s.uploadRequest(c, s.charmsURI(c, "?series=quantal"), true, tempFile.Name()) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusBadRequest, "cannot open charm archive: zip: not a valid zip file") // Now try with the default Content-Type. resp, err = s.uploadRequest(c, s.charmsURI(c, "?series=quantal"), false, tempFile.Name()) c.Assert(err, gc.IsNil) s.assertErrorResponse(c, resp, http.StatusBadRequest, "expected Content-Type: application/zip, got: application/octet-stream") } func (s *charmsSuite) TestUploadBumpsRevision(c *gc.C) { // Add the dummy charm with revision 1. ch := coretesting.Charms.Bundle(c.MkDir(), "dummy") curl := charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", ch.Meta().Name, ch.Revision()), ) bundleURL, err := url.Parse("http://bundles.testing.invalid/dummy-1") c.Assert(err, gc.IsNil) _, err = s.State.AddCharm(ch, curl, bundleURL, "dummy-1-sha256") c.Assert(err, gc.IsNil) // Now try uploading the same revision and verify it gets bumped, // and the BundleURL and BundleSha256 are calculated. resp, err := s.uploadRequest(c, s.charmsURI(c, "?series=quantal"), true, ch.Path) c.Assert(err, gc.IsNil) expectedURL := charm.MustParseURL("local:quantal/dummy-2") s.assertUploadResponse(c, resp, expectedURL.String()) sch, err := s.State.Charm(expectedURL) c.Assert(err, gc.IsNil) c.Assert(sch.URL(), gc.DeepEquals, expectedURL) c.Assert(sch.Revision(), gc.Equals, 2) c.Assert(sch.IsUploaded(), jc.IsTrue) // No more checks for these two here, because they // are verified in TestUploadRespectsLocalRevision. c.Assert(sch.BundleURL(), gc.Not(gc.Equals), "") c.Assert(sch.BundleSha256(), gc.Not(gc.Equals), "") } func (s *charmsSuite) TestUploadRespectsLocalRevision(c *gc.C) { // Make a dummy charm dir with revision 123. dir := coretesting.Charms.ClonedDir(c.MkDir(), "dummy") dir.SetDiskRevision(123) // Now bundle the dir. tempFile, err := ioutil.TempFile(c.MkDir(), "charm") c.Assert(err, gc.IsNil) defer tempFile.Close() defer os.Remove(tempFile.Name()) err = dir.BundleTo(tempFile) c.Assert(err, gc.IsNil) // Now try uploading it and ensure the revision persists. resp, err := s.uploadRequest(c, s.charmsURI(c, "?series=quantal"), true, tempFile.Name()) c.Assert(err, gc.IsNil) expectedURL := charm.MustParseURL("local:quantal/dummy-123") s.assertUploadResponse(c, resp, expectedURL.String()) sch, err := s.State.Charm(expectedURL) c.Assert(err, gc.IsNil) c.Assert(sch.URL(), gc.DeepEquals, expectedURL) c.Assert(sch.Revision(), gc.Equals, 123) c.Assert(sch.IsUploaded(), jc.IsTrue) // First rewind the reader, which was reset but BundleTo() above. _, err = tempFile.Seek(0, 0) c.Assert(err, gc.IsNil) // Finally, verify the SHA256 and uploaded URL. expectedSHA256, _, err := utils.ReadSHA256(tempFile) c.Assert(err, gc.IsNil) name := charm.Quote(expectedURL.String()) storage, err := environs.GetStorage(s.State) c.Assert(err, gc.IsNil) expectedUploadURL, err := storage.URL(name) c.Assert(err, gc.IsNil) c.Assert(sch.BundleURL().String(), gc.Equals, expectedUploadURL) c.Assert(sch.BundleSha256(), gc.Equals, expectedSHA256) reader, err := storage.Get(name) c.Assert(err, gc.IsNil) defer reader.Close() downloadedSHA256, _, err := utils.ReadSHA256(reader) c.Assert(err, gc.IsNil) c.Assert(downloadedSHA256, gc.Equals, expectedSHA256) } func (s *charmsSuite) TestUploadRepackagesNestedArchives(c *gc.C) { // Make a clone of the dummy charm in a nested directory. rootDir := c.MkDir() dirPath := filepath.Join(rootDir, "subdir1", "subdir2") err := os.MkdirAll(dirPath, 0755) c.Assert(err, gc.IsNil) dir := coretesting.Charms.ClonedDir(dirPath, "dummy") // Now tweak the path the dir thinks it is in and bundle it. dir.Path = rootDir tempFile, err := ioutil.TempFile(c.MkDir(), "charm") c.Assert(err, gc.IsNil) defer tempFile.Close() defer os.Remove(tempFile.Name()) err = dir.BundleTo(tempFile) c.Assert(err, gc.IsNil) // Try reading it as a bundle - should fail due to nested dirs. _, err = charm.ReadBundle(tempFile.Name()) c.Assert(err, gc.ErrorMatches, "bundle file not found: metadata.yaml") // Now try uploading it - should succeeed and be repackaged. resp, err := s.uploadRequest(c, s.charmsURI(c, "?series=quantal"), true, tempFile.Name()) c.Assert(err, gc.IsNil) expectedURL := charm.MustParseURL("local:quantal/dummy-1") s.assertUploadResponse(c, resp, expectedURL.String()) sch, err := s.State.Charm(expectedURL) c.Assert(err, gc.IsNil) c.Assert(sch.URL(), gc.DeepEquals, expectedURL) c.Assert(sch.Revision(), gc.Equals, 1) c.Assert(sch.IsUploaded(), jc.IsTrue) // Get it from the storage and try to read it as a bundle - it // should succeed, because it was repackaged during upload to // strip nested dirs. archiveName := strings.TrimPrefix(sch.BundleURL().RequestURI(), "/dummyenv/private/") storage, err := environs.GetStorage(s.State) c.Assert(err, gc.IsNil) reader, err := storage.Get(archiveName) c.Assert(err, gc.IsNil) defer reader.Close() data, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) downloadedFile, err := ioutil.TempFile(c.MkDir(), "downloaded") c.Assert(err, gc.IsNil) defer downloadedFile.Close() defer os.Remove(downloadedFile.Name()) err = ioutil.WriteFile(downloadedFile.Name(), data, 0644) c.Assert(err, gc.IsNil) bundle, err := charm.ReadBundle(downloadedFile.Name()) c.Assert(err, gc.IsNil) c.Assert(bundle.Revision(), jc.DeepEquals, sch.Revision()) c.Assert(bundle.Meta(), jc.DeepEquals, sch.Meta()) c.Assert(bundle.Config(), jc.DeepEquals, sch.Config()) } func (s *charmsSuite) TestGetRequiresCharmURL(c *gc.C) { uri := s.charmsURI(c, "?file=hooks/install") resp, err := s.authRequest(c, "GET", uri, "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse( c, resp, http.StatusBadRequest, "expected url=CharmURL query argument", ) } func (s *charmsSuite) TestGetFailsWithInvalidCharmURL(c *gc.C) { uri := s.charmsURI(c, "?url=local:precise/no-such") resp, err := s.authRequest(c, "GET", uri, "", nil) c.Assert(err, gc.IsNil) s.assertErrorResponse( c, resp, http.StatusBadRequest, "unable to retrieve and save the charm: charm not found in the provider storage: .*", ) } func (s *charmsSuite) TestGetReturnsNotFoundWhenMissing(c *gc.C) { // Add the dummy charm. ch := coretesting.Charms.Bundle(c.MkDir(), "dummy") _, err := s.uploadRequest( c, s.charmsURI(c, "?series=quantal"), true, ch.Path) c.Assert(err, gc.IsNil) // Ensure a 404 is returned for files not included in the charm. for i, file := range []string{ "no-such-file", "..", "../../../etc/passwd", "hooks/delete", } { c.Logf("test %d: %s", i, file) uri := s.charmsURI(c, "?url=local:quantal/dummy-1&file="+file) resp, err := s.authRequest(c, "GET", uri, "", nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusNotFound) } } func (s *charmsSuite) TestGetReturnsForbiddenWithDirectory(c *gc.C) { // Add the dummy charm. ch := coretesting.Charms.Bundle(c.MkDir(), "dummy") _, err := s.uploadRequest( c, s.charmsURI(c, "?series=quantal"), true, ch.Path) c.Assert(err, gc.IsNil) // Ensure a 403 is returned if the requested file is a directory. uri := s.charmsURI(c, "?url=local:quantal/dummy-1&file=hooks") resp, err := s.authRequest(c, "GET", uri, "", nil) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusForbidden) } func (s *charmsSuite) TestGetReturnsFileContents(c *gc.C) { // Add the dummy charm. ch := coretesting.Charms.Bundle(c.MkDir(), "dummy") _, err := s.uploadRequest( c, s.charmsURI(c, "?series=quantal"), true, ch.Path) c.Assert(err, gc.IsNil) // Ensure the file contents are properly returned. for i, t := range []struct { summary string file string response string }{{ summary: "relative path", file: "revision", response: "1", }, { summary: "exotic path", file: "./hooks/../revision", response: "1", }, { summary: "sub-directory path", file: "hooks/install", response: "#!/bin/bash\necho \"Done!\"\n", }, } { c.Logf("test %d: %s", i, t.summary) uri := s.charmsURI(c, "?url=local:quantal/dummy-1&file="+t.file) resp, err := s.authRequest(c, "GET", uri, "", nil) c.Assert(err, gc.IsNil) s.assertGetFileResponse(c, resp, t.response, "text/plain; charset=utf-8") } } func (s *charmsSuite) TestGetReturnsManifest(c *gc.C) { // Add the dummy charm. ch := coretesting.Charms.Bundle(c.MkDir(), "dummy") _, err := s.uploadRequest( c, s.charmsURI(c, "?series=quantal"), true, ch.Path) c.Assert(err, gc.IsNil) // Ensure charm files are properly listed. uri := s.charmsURI(c, "?url=local:quantal/dummy-1") resp, err := s.authRequest(c, "GET", uri, "", nil) c.Assert(err, gc.IsNil) manifest, err := ch.Manifest() c.Assert(err, gc.IsNil) expectedFiles := manifest.SortedValues() s.assertGetFileListResponse(c, resp, expectedFiles) ctype := resp.Header.Get("content-type") c.Assert(ctype, gc.Equals, "application/json") } func (s *charmsSuite) TestGetUsesCache(c *gc.C) { // Add a fake charm archive in the cache directory. cacheDir := filepath.Join(s.DataDir(), "charm-get-cache") err := os.MkdirAll(cacheDir, 0755) c.Assert(err, gc.IsNil) // Create and save a bundle in it. charmDir := coretesting.Charms.ClonedDir(c.MkDir(), "dummy") testPath := filepath.Join(charmDir.Path, "utils.js") contents := "// blah blah" err = ioutil.WriteFile(testPath, []byte(contents), 0755) c.Assert(err, gc.IsNil) var buffer bytes.Buffer err = charmDir.BundleTo(&buffer) c.Assert(err, gc.IsNil) charmArchivePath := filepath.Join( cacheDir, charm.Quote("local:trusty/django-42")+".zip") err = ioutil.WriteFile(charmArchivePath, buffer.Bytes(), 0644) c.Assert(err, gc.IsNil) // Ensure the cached contents are properly retrieved. uri := s.charmsURI(c, "?url=local:trusty/django-42&file=utils.js") resp, err := s.authRequest(c, "GET", uri, "", nil) c.Assert(err, gc.IsNil) s.assertGetFileResponse(c, resp, contents, "application/javascript") } func (s *charmsSuite) charmsURI(c *gc.C, query string) string { _, info, err := s.APIConn.Environ.StateInfo() c.Assert(err, gc.IsNil) return "https://" + info.Addrs[0] + "/charms" + query } func (s *charmsSuite) assertUploadResponse(c *gc.C, resp *http.Response, expCharmURL string) { body := assertResponse(c, resp, http.StatusOK, "application/json") charmResponse := jsonResponse(c, body) c.Check(charmResponse.Error, gc.Equals, "") c.Check(charmResponse.CharmURL, gc.Equals, expCharmURL) } func (s *charmsSuite) assertGetFileResponse(c *gc.C, resp *http.Response, expBody, expContentType string) { body := assertResponse(c, resp, http.StatusOK, expContentType) c.Check(string(body), gc.Equals, expBody) } func (s *charmsSuite) assertGetFileListResponse(c *gc.C, resp *http.Response, expFiles []string) { body := assertResponse(c, resp, http.StatusOK, "application/json") charmResponse := jsonResponse(c, body) c.Check(charmResponse.Error, gc.Equals, "") c.Check(charmResponse.Files, gc.DeepEquals, expFiles) } func (s *charmsSuite) assertErrorResponse(c *gc.C, resp *http.Response, expCode int, expError string) { body := assertResponse(c, resp, expCode, "application/json") c.Check(jsonResponse(c, body).Error, gc.Matches, expError) } func assertResponse(c *gc.C, resp *http.Response, expCode int, expContentType string) []byte { c.Check(resp.StatusCode, gc.Equals, expCode) body, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() c.Assert(err, gc.IsNil) ctype := resp.Header.Get("Content-Type") c.Assert(ctype, gc.Equals, expContentType) return body } func jsonResponse(c *gc.C, body []byte) (jsonResponse params.CharmsResponse) { err := json.Unmarshal(body, &jsonResponse) c.Assert(err, gc.IsNil) return } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/usermanager/���������������������������0000755�0000153�0000161�00000000000�12321735643�026317� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/usermanager/usermanager_test.go��������0000644�0000153�0000161�00000007001�12321735642�032213� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package usermanager_test import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state/api/params" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" "launchpad.net/juju-core/state/apiserver/usermanager" ) type userManagerSuite struct { jujutesting.JujuConnSuite usermanager *usermanager.UserManagerAPI authorizer apiservertesting.FakeAuthorizer } var _ = gc.Suite(&userManagerSuite{}) func (s *userManagerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.authorizer = apiservertesting.FakeAuthorizer{ Tag: "user-admin", LoggedIn: true, Client: true, } var err error s.usermanager, err = usermanager.NewUserManagerAPI(s.State, s.authorizer) c.Assert(err, gc.IsNil) } func (s *userManagerSuite) TestNewUserManagerAPIRefusesNonClient(c *gc.C) { anAuthoriser := s.authorizer anAuthoriser.Client = false endPoint, err := usermanager.NewUserManagerAPI(s.State, anAuthoriser) c.Assert(endPoint, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *userManagerSuite) TestAddUser(c *gc.C) { arg := params.EntityPassword{ Tag: "foobar", Password: "password", } args := params.EntityPasswords{Changes: []params.EntityPassword{arg}} result, err := s.usermanager.AddUser(args) // Check that the call is succesful c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{params.ErrorResult{Error: nil}}}) // Check that the call results in a new user being created user, err := s.State.User("foobar") c.Assert(err, gc.IsNil) c.Assert(user, gc.NotNil) } func (s *userManagerSuite) TestRemoveUser(c *gc.C) { arg := params.EntityPassword{ Tag: "foobar", Password: "password", } removeArg := params.Entity{ Tag: "foobar", } args := params.EntityPasswords{Changes: []params.EntityPassword{arg}} removeArgs := params.Entities{Entities: []params.Entity{removeArg}} _, err := s.usermanager.AddUser(args) c.Assert(err, gc.IsNil) user, err := s.State.User("foobar") c.Assert(user.IsDeactivated(), gc.Equals, false) // The user should be active result, err := s.usermanager.RemoveUser(removeArgs) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{params.ErrorResult{Error: nil}}}) user, err = s.State.User("foobar") c.Assert(err, gc.IsNil) // Removal makes the user in active c.Assert(user.IsDeactivated(), gc.Equals, true) c.Assert(user.PasswordValid(arg.Password), gc.Equals, false) } // Since removing a user just deacitvates them you cannot add a user // that has been previously been removed // TODO(mattyw) 2014-03-07 bug #1288745 func (s *userManagerSuite) TestCannotAddRemoveAdd(c *gc.C) { arg := params.EntityPassword{ Tag: "addremove", Password: "password", } removeArg := params.Entity{ Tag: "foobar", } args := params.EntityPasswords{Changes: []params.EntityPassword{arg}} removeArgs := params.Entities{Entities: []params.Entity{removeArg}} _, err := s.usermanager.AddUser(args) c.Assert(err, gc.IsNil) _, err = s.usermanager.RemoveUser(removeArgs) c.Assert(err, gc.IsNil) _, err = s.State.User("addremove") result, err := s.usermanager.AddUser(args) expectedError := params.Error{Code: "", Message: "Failed to create user: user already exists"} c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ params.ErrorResult{&expectedError}}}) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/usermanager/package_test.go������������0000644�0000153�0000161�00000000373�12321735642�031302� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package usermanager_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/usermanager/usermanager.go�������������0000644�0000153�0000161�00000005261�12321735642�031162� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package usermanager import ( "fmt" "github.com/loggo/loggo" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" ) var logger = loggo.GetLogger("juju.state.apiserver.usermanager") // UserManager defines the methods on the usermanager API end point. type UserManager interface { AddUser(arg params.EntityPasswords) (params.ErrorResults, error) RemoveUser(arg params.Entities) (params.ErrorResults, error) } // UserManagerAPI implements the user manager interface and is the concrete // implementation of the api end point. type UserManagerAPI struct { state *state.State authorizer common.Authorizer getCanWrite common.GetAuthFunc } var _ UserManager = (*UserManagerAPI)(nil) func NewUserManagerAPI( st *state.State, authorizer common.Authorizer, ) (*UserManagerAPI, error) { if !authorizer.AuthClient() { return nil, common.ErrPerm } // TODO(mattyw) - replace stub with real canWrite function getCanWrite := common.AuthAlways(true) return &UserManagerAPI{ state: st, authorizer: authorizer, getCanWrite: getCanWrite}, nil } func (api *UserManagerAPI) AddUser(args params.EntityPasswords) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Changes)), } if len(args.Changes) == 0 { return result, nil } canWrite, err := api.getCanWrite() if err != nil { result.Results[0].Error = common.ServerError(err) return result, err } for i, arg := range args.Changes { if !canWrite(arg.Tag) { result.Results[0].Error = common.ServerError(common.ErrPerm) continue } _, err := api.state.AddUser(arg.Tag, arg.Password) if err != nil { result.Results[i].Error = common.ServerError(fmt.Errorf("Failed to create user: %s", err)) continue } } return result, nil } func (api *UserManagerAPI) RemoveUser(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canWrite, err := api.getCanWrite() if err != nil { return result, err } for i, arg := range args.Entities { if !canWrite(arg.Tag) { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } user, err := api.state.User(arg.Tag) if err != nil { result.Results[i].Error = common.ServerError(common.ErrPerm) continue } err = user.Deactivate() if err != nil { result.Results[i].Error = common.ServerError(fmt.Errorf("Failed to remove user: %s", err)) continue } } return result, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/apiserver.go���������������������������0000644�0000153�0000161�00000013412�12321735776�026345� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver import ( "crypto/tls" "net" "net/http" "sync" "sync/atomic" "time" "code.google.com/p/go.net/websocket" "github.com/juju/loggo" "launchpad.net/tomb" "launchpad.net/juju-core/rpc" "launchpad.net/juju-core/rpc/jsoncodec" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/apiserver/common" ) var logger = loggo.GetLogger("juju.state.apiserver") // Server holds the server side of the API. type Server struct { tomb tomb.Tomb wg sync.WaitGroup state *state.State addr net.Addr dataDir string } // Serve serves the given state by accepting requests on the given // listener, using the given certificate and key (in PEM format) for // authentication. func NewServer(s *state.State, addr string, cert, key []byte, datadir string) (*Server, error) { lis, err := net.Listen("tcp", addr) if err != nil { return nil, err } logger.Infof("listening on %q", lis.Addr()) tlsCert, err := tls.X509KeyPair(cert, key) if err != nil { return nil, err } srv := &Server{ state: s, addr: lis.Addr(), dataDir: datadir, } // TODO(rog) check that *srvRoot is a valid type for using // as an RPC server. lis = tls.NewListener(lis, &tls.Config{ Certificates: []tls.Certificate{tlsCert}, }) go srv.run(lis) return srv, nil } // Dead returns a channel that signals when the server has exited. func (srv *Server) Dead() <-chan struct{} { return srv.tomb.Dead() } // Stop stops the server and returns when all running requests // have completed. func (srv *Server) Stop() error { srv.tomb.Kill(nil) return srv.tomb.Wait() } // Kill implements worker.Worker.Kill. func (srv *Server) Kill() { srv.tomb.Kill(nil) } // Wait implements worker.Worker.Wait. func (srv *Server) Wait() error { return srv.tomb.Wait() } type requestNotifier struct { id int64 start time.Time mu sync.Mutex tag_ string } var globalCounter int64 func newRequestNotifier() *requestNotifier { return &requestNotifier{ id: atomic.AddInt64(&globalCounter, 1), tag_: "<unknown>", start: time.Now(), } } func (n *requestNotifier) login(tag string) { n.mu.Lock() n.tag_ = tag n.mu.Unlock() } func (n *requestNotifier) tag() (tag string) { n.mu.Lock() tag = n.tag_ n.mu.Unlock() return } func (n *requestNotifier) ServerRequest(hdr *rpc.Header, body interface{}) { if hdr.Request.Type == "Pinger" && hdr.Request.Action == "Ping" { return } // TODO(rog) 2013-10-11 remove secrets from some requests. logger.Debugf("<- [%X] %s %s", n.id, n.tag(), jsoncodec.DumpRequest(hdr, body)) } func (n *requestNotifier) ServerReply(req rpc.Request, hdr *rpc.Header, body interface{}, timeSpent time.Duration) { if req.Type == "Pinger" && req.Action == "Ping" { return } logger.Debugf("-> [%X] %s %s %s %s[%q].%s", n.id, n.tag(), timeSpent, jsoncodec.DumpRequest(hdr, body), req.Type, req.Id, req.Action) } func (n *requestNotifier) join(req *http.Request) { logger.Infof("[%X] API connection from %s", n.id, req.RemoteAddr) } func (n *requestNotifier) leave() { logger.Infof("[%X] %s API connection terminated after %v", n.id, n.tag(), time.Since(n.start)) } func (n requestNotifier) ClientRequest(hdr *rpc.Header, body interface{}) { } func (n requestNotifier) ClientReply(req rpc.Request, hdr *rpc.Header, body interface{}) { } func (srv *Server) run(lis net.Listener) { defer srv.tomb.Done() defer srv.wg.Wait() // wait for any outstanding requests to complete. srv.wg.Add(1) go func() { <-srv.tomb.Dying() lis.Close() srv.wg.Done() }() mux := http.NewServeMux() mux.HandleFunc("/", srv.apiHandler) charmHandler := &charmsHandler{httpHandler: httpHandler{state: srv.state}, dataDir: srv.dataDir} // charmHandler itself provides the errorSender implementation for the embedded httpHandler. charmHandler.httpHandler.errorSender = charmHandler mux.Handle("/charms", charmHandler) toolsHandler := &toolsHandler{httpHandler{state: srv.state}} // toolsHandler itself provides the errorSender implementation for the embedded httpHandler. toolsHandler.httpHandler.errorSender = toolsHandler mux.Handle("/tools", toolsHandler) // The error from http.Serve is not interesting. http.Serve(lis, mux) } func (srv *Server) apiHandler(w http.ResponseWriter, req *http.Request) { reqNotifier := newRequestNotifier() reqNotifier.join(req) defer reqNotifier.leave() wsServer := websocket.Server{ Handler: func(conn *websocket.Conn) { srv.wg.Add(1) defer srv.wg.Done() // If we've got to this stage and the tomb is still // alive, we know that any tomb.Kill must occur after we // have called wg.Add, so we avoid the possibility of a // handler goroutine running after Stop has returned. if srv.tomb.Err() != tomb.ErrStillAlive { return } if err := srv.serveConn(conn, reqNotifier); err != nil { logger.Errorf("error serving RPCs: %v", err) } }, } wsServer.ServeHTTP(w, req) } // Addr returns the address that the server is listening on. func (srv *Server) Addr() string { return srv.addr.String() } func (srv *Server) serveConn(wsConn *websocket.Conn, reqNotifier *requestNotifier) error { codec := jsoncodec.NewWebsocket(wsConn) if loggo.GetLogger("juju.rpc.jsoncodec").EffectiveLogLevel() <= loggo.TRACE { codec.SetLogging(true) } var notifier rpc.RequestNotifier if logger.EffectiveLogLevel() <= loggo.DEBUG { // Incur request monitoring overhead only if we // know we'll need it. notifier = reqNotifier } conn := rpc.NewConn(codec, notifier) conn.Serve(newStateServer(srv, conn, reqNotifier), serverError) conn.Start() select { case <-conn.Dead(): case <-srv.tomb.Dying(): } return conn.Close() } func serverError(err error) error { if err := common.ServerError(err); err != nil { return err } return nil } var logRequests = true ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/pinger_test.go�������������������������0000644�0000153�0000161�00000004147�12321735776�026675� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver_test import ( "time" "github.com/juju/loggo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/rpc" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/apiserver" coretesting "launchpad.net/juju-core/testing" ) type stateSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&stateSuite{}) var testPingPeriod = 100 * time.Millisecond func (s *stateSuite) TestConnectionBrokenDetection(c *gc.C) { s.PatchValue(&api.PingPeriod, testPingPeriod) st, _ := s.OpenAPIAsNewMachine(c) // Connection still alive select { case <-time.After(testPingPeriod): case <-st.Broken(): c.Fatalf("connection should be alive still") } // Close the connection and see if we detect this go st.Close() // Check it's detected select { case <-time.After(testPingPeriod + time.Second): c.Fatalf("connection not closed as expected") case <-st.Broken(): return } } func (s *stateSuite) TestPing(c *gc.C) { tw := &loggo.TestWriter{} c.Assert(loggo.RegisterWriter("ping-tester", tw, loggo.DEBUG), gc.IsNil) defer loggo.RemoveWriter("ping-tester") st, _ := s.OpenAPIAsNewMachine(c) err := st.Ping() c.Assert(err, gc.IsNil) err = st.Close() c.Assert(err, gc.IsNil) err = st.Ping() c.Assert(err, gc.Equals, rpc.ErrShutdown) // Make sure that ping messages have not been logged. for _, m := range tw.Log { c.Logf("checking %q", m.Message) c.Check(m.Message, gc.Not(gc.Matches), ".*Ping.*") } } func (s *stateSuite) TestClientNoNeedToPing(c *gc.C) { s.PatchValue(apiserver.MaxPingInterval, time.Duration(0)) st, err := api.Open(s.APIInfo(c), api.DefaultDialOpts()) c.Assert(err, gc.IsNil) time.Sleep(coretesting.ShortWait) err = st.Ping() c.Assert(err, gc.IsNil) } func (s *stateSuite) TestAgentConnectionShutsDownWithNoPing(c *gc.C) { s.PatchValue(apiserver.MaxPingInterval, time.Duration(0)) st, _ := s.OpenAPIAsNewMachine(c) time.Sleep(coretesting.ShortWait) err := st.Ping() c.Assert(err, gc.ErrorMatches, "connection is shut down") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/tools.go�������������������������������0000644�0000153�0000161�00000014775�12321735776�025522� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver import ( "crypto/sha256" "encoding/json" "fmt" "io" "io/ioutil" "net/http" "os" "path" "strings" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/sync" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) // toolsHandler handles tool upload through HTTPS in the API server. type toolsHandler struct { httpHandler } func (h *toolsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err := h.authenticate(r); err != nil { h.authError(w) return } switch r.Method { case "POST": // Add a local charm to the store provider. // Requires a "series" query specifying the series to use for the charm. agentTools, disableSSLHostnameVerification, err := h.processPost(r) if err != nil { h.sendError(w, http.StatusBadRequest, err.Error()) return } h.sendJSON(w, http.StatusOK, &params.ToolsResult{ Tools: agentTools, DisableSSLHostnameVerification: disableSSLHostnameVerification, }) default: h.sendError(w, http.StatusMethodNotAllowed, fmt.Sprintf("unsupported method: %q", r.Method)) } } // sendJSON sends a JSON-encoded response to the client. func (h *toolsHandler) sendJSON(w http.ResponseWriter, statusCode int, response *params.ToolsResult) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(statusCode) body, err := json.Marshal(response) if err != nil { return err } w.Write(body) return nil } // sendError sends a JSON-encoded error response. func (h *toolsHandler) sendError(w http.ResponseWriter, statusCode int, message string) error { err := common.ServerError(fmt.Errorf(message)) return h.sendJSON(w, statusCode, &params.ToolsResult{Error: err}) } // processPost handles a charm upload POST request after authentication. func (h *toolsHandler) processPost(r *http.Request) (*tools.Tools, bool, error) { query := r.URL.Query() binaryVersionParam := query.Get("binaryVersion") if binaryVersionParam == "" { return nil, false, fmt.Errorf("expected binaryVersion argument") } toolsVersion, err := version.ParseBinary(binaryVersionParam) if err != nil { return nil, false, fmt.Errorf("invalid tools version %q: %v", binaryVersionParam, err) } var fakeSeries []string seriesParam := query.Get("series") if seriesParam != "" { fakeSeries = strings.Split(seriesParam, ",") } logger.Debugf("request to upload tools %s for series %q", toolsVersion, seriesParam) // Make sure the content type is x-tar-gz. contentType := r.Header.Get("Content-Type") if contentType != "application/x-tar-gz" { return nil, false, fmt.Errorf("expected Content-Type: application/x-tar-gz, got: %v", contentType) } return h.handleUpload(r.Body, toolsVersion, fakeSeries...) } // handleUpload uploads the tools data from the reader to env storage as the specified version. func (h *toolsHandler) handleUpload(r io.Reader, toolsVersion version.Binary, fakeSeries ...string) (*tools.Tools, bool, error) { // Set up a local temp directory for the tools tarball. tmpDir, err := ioutil.TempDir("", "juju-upload-tools-") if err != nil { return nil, false, fmt.Errorf("cannot create temp dir: %v", err) } defer os.RemoveAll(tmpDir) toolsFilename := envtools.StorageName(toolsVersion) toolsDir := path.Dir(toolsFilename) fullToolsDir := path.Join(tmpDir, toolsDir) err = os.MkdirAll(fullToolsDir, 0700) if err != nil { return nil, false, fmt.Errorf("cannot create tools dir %s: %v", toolsDir, err) } // Read the tools tarball from the request, calculating the sha256 along the way. fullToolsFilename := path.Join(tmpDir, toolsFilename) toolsFile, err := os.Create(fullToolsFilename) if err != nil { return nil, false, fmt.Errorf("cannot create tools file %s: %v", fullToolsFilename, err) } logger.Debugf("saving uploaded tools to temp file: %s", fullToolsFilename) defer toolsFile.Close() sha256hash := sha256.New() var size int64 if size, err = io.Copy(toolsFile, io.TeeReader(r, sha256hash)); err != nil { return nil, false, fmt.Errorf("error processing file upload: %v", err) } if size == 0 { return nil, false, fmt.Errorf("no tools uploaded") } // TODO(wallyworld): check integrity of tools tarball. // Create a tools record and sync to storage. uploadedTools := &tools.Tools{ Version: toolsVersion, Size: size, SHA256: fmt.Sprintf("%x", sha256hash.Sum(nil)), } logger.Debugf("about to upload tools %+v to storage", uploadedTools) return h.uploadToStorage(uploadedTools, tmpDir, toolsFilename, fakeSeries...) } // uploadToStorage uploads the tools from the specified directory to environment storage. func (h *toolsHandler) uploadToStorage(uploadedTools *tools.Tools, toolsDir, toolsFilename string, fakeSeries ...string) (*tools.Tools, bool, error) { // SyncTools requires simplestreams metadata to find the tools to upload. stor, err := filestorage.NewFileStorageWriter(toolsDir) if err != nil { return nil, false, fmt.Errorf("cannot create metadata storage: %v", err) } // Generate metadata for the fake series. The URL for each fake series // record points to the same tools tarball. allToolsMetadata := []*tools.Tools{uploadedTools} for _, series := range fakeSeries { vers := uploadedTools.Version vers.Series = series allToolsMetadata = append(allToolsMetadata, &tools.Tools{ Version: vers, URL: uploadedTools.URL, Size: uploadedTools.Size, SHA256: uploadedTools.SHA256, }) } err = envtools.MergeAndWriteMetadata(stor, allToolsMetadata, false) if err != nil { return nil, false, fmt.Errorf("cannot get environment config: %v", err) } // Create the environment so we can get the storage to which we upload the tools. envConfig, err := h.state.EnvironConfig() if err != nil { return nil, false, fmt.Errorf("cannot get environment config: %v", err) } env, err := environs.New(envConfig) if err != nil { return nil, false, fmt.Errorf("cannot access environment: %v", err) } // Now perform the upload. builtTools := &sync.BuiltTools{ Version: uploadedTools.Version, Dir: toolsDir, StorageName: toolsFilename, Size: uploadedTools.Size, Sha256Hash: uploadedTools.SHA256, } uploadedTools, err = sync.SyncBuiltTools(env.Storage(), builtTools, fakeSeries...) if err != nil { return nil, false, err } return uploadedTools, !envConfig.SSLHostnameVerification(), nil } ���juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/root_test.go���������������������������0000644�0000153�0000161�00000003414�12321735642�026360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver_test import ( "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/rpc/rpcreflect" "launchpad.net/juju-core/state/apiserver" "launchpad.net/juju-core/testing" ) type rootSuite struct{} var _ = gc.Suite(&rootSuite{}) var allowedDiscardedMethods = []string{ "AuthClient", "AuthEnvironManager", "AuthMachineAgent", "AuthOwner", "AuthUnitAgent", "GetAuthEntity", "GetAuthTag", } func (*rootSuite) TestDiscardedAPIMethods(c *gc.C) { t := rpcreflect.TypeOf(apiserver.RootType) // We must have some root-level methods. c.Assert(t.MethodNames(), gc.Not(gc.HasLen), 0) c.Assert(t.DiscardedMethods(), gc.DeepEquals, allowedDiscardedMethods) for _, name := range t.MethodNames() { m, err := t.Method(name) c.Assert(err, gc.IsNil) // We must have some methods on every object returned // by a root-level method. c.Assert(m.ObjType.MethodNames(), gc.Not(gc.HasLen), 0) // We don't allow any methods that don't implement // an RPC entry point. c.Assert(m.ObjType.DiscardedMethods(), gc.HasLen, 0) } } func (r *rootSuite) TestPingTimeout(c *gc.C) { closedc := make(chan time.Time, 1) action := func() { closedc <- time.Now() } timeout := apiserver.NewPingTimeout(action, 50*time.Millisecond) for i := 0; i < 2; i++ { time.Sleep(10 * time.Millisecond) timeout.Ping() } // Expect action to be executed about 50ms after last ping. broken := time.Now() var closed time.Time time.Sleep(100 * time.Millisecond) select { case closed = <-closedc: case <-time.After(testing.LongWait): c.Fatalf("action never executed") } closeDiff := closed.Sub(broken) / time.Millisecond c.Assert(50 <= closeDiff && closeDiff <= 100, gc.Equals, true) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/��������������������������������0000755�0000153�0000161�00000000000�12321736000�025262� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/errors_test.go������������������0000644�0000153�0000161�00000006306�12321735776�030214� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( stderrors "errors" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/testing/testbase" ) type errorsSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&errorsSuite{}) var errorTransformTests = []struct { err error code string helperFunc func(error) bool }{{ err: errors.NotFoundf("hello"), code: params.CodeNotFound, helperFunc: params.IsCodeNotFound, }, { err: errors.Unauthorizedf("hello"), code: params.CodeUnauthorized, helperFunc: params.IsCodeUnauthorized, }, { err: state.ErrCannotEnterScopeYet, code: params.CodeCannotEnterScopeYet, helperFunc: params.IsCodeCannotEnterScopeYet, }, { err: state.ErrCannotEnterScope, code: params.CodeCannotEnterScope, helperFunc: params.IsCodeCannotEnterScope, }, { err: state.ErrExcessiveContention, code: params.CodeExcessiveContention, helperFunc: params.IsCodeExcessiveContention, }, { err: state.ErrUnitHasSubordinates, code: params.CodeUnitHasSubordinates, helperFunc: params.IsCodeUnitHasSubordinates, }, { err: common.ErrBadId, code: params.CodeNotFound, helperFunc: params.IsCodeNotFound, }, { err: common.NoAddressSetError("unit-mysql-0", "public"), code: params.CodeNoAddressSet, helperFunc: params.IsCodeNoAddressSet, }, { err: common.ErrBadCreds, code: params.CodeUnauthorized, helperFunc: params.IsCodeUnauthorized, }, { err: common.ErrPerm, code: params.CodeUnauthorized, helperFunc: params.IsCodeUnauthorized, }, { err: common.ErrNotLoggedIn, code: params.CodeUnauthorized, helperFunc: params.IsCodeUnauthorized, }, { err: state.NotProvisionedError("0"), code: params.CodeNotProvisioned, helperFunc: params.IsCodeNotProvisioned, }, { err: common.ErrUnknownWatcher, code: params.CodeNotFound, helperFunc: params.IsCodeNotFound, }, { err: &state.NotAssignedError{&state.Unit{}}, // too sleazy?! nah.. code: params.CodeNotAssigned, helperFunc: params.IsCodeNotAssigned, }, { err: common.ErrStoppedWatcher, code: params.CodeStopped, helperFunc: params.IsCodeStopped, }, { err: &state.HasAssignedUnitsError{"42", []string{"a"}}, code: params.CodeHasAssignedUnits, helperFunc: params.IsCodeHasAssignedUnits, }, { err: stderrors.New("an error"), code: "", }, { err: unhashableError{"foo"}, code: "", }, { err: nil, code: "", }} type unhashableError []string func (err unhashableError) Error() string { return err[0] } func (s *errorsSuite) TestErrorTransform(c *gc.C) { for _, t := range errorTransformTests { err1 := common.ServerError(t.err) if t.err == nil { c.Assert(err1, gc.IsNil) } else { c.Assert(err1.Message, gc.Equals, t.err.Error()) c.Assert(err1.Code, gc.Equals, t.code) if t.helperFunc != nil { c.Assert(err1, jc.Satisfies, t.helperFunc) } } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/ensuredead.go�������������������0000644�0000153�0000161�00000003206�12321735642�027744� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" ) // DeadEnsurer implements a common EnsureDead method for use by // various facades. type DeadEnsurer struct { st state.EntityFinder getCanModify GetAuthFunc } // NewDeadEnsurer returns a new DeadEnsurer. The GetAuthFunc will be // used on each invocation of EnsureDead to determine current // permissions. func NewDeadEnsurer(st state.EntityFinder, getCanModify GetAuthFunc) *DeadEnsurer { return &DeadEnsurer{ st: st, getCanModify: getCanModify, } } func (d *DeadEnsurer) ensureEntityDead(tag string) error { entity0, err := d.st.FindEntity(tag) if err != nil { return err } entity, ok := entity0.(state.EnsureDeader) if !ok { return NotSupportedError(tag, "ensuring death") } return entity.EnsureDead() } // EnsureDead calls EnsureDead on each given entity from state. It // will fail if the entity is not present. If it's Alive, nothing will // happen (see state/EnsureDead() for units or machines). func (d *DeadEnsurer) EnsureDead(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canModify, err := d.getCanModify() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := ErrPerm if canModify(entity.Tag) { err = d.ensureEntityDead(entity.Tag) } result.Results[i].Error = ServerError(err) } return result, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/remove.go�����������������������0000644�0000153�0000161�00000003761�12321735642�027130� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "fmt" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" ) // Remover implements a common Remove method for use by various facades. type Remover struct { st state.EntityFinder callEnsureDead bool getCanModify GetAuthFunc } // NewRemover returns a new Remover. The callEnsureDead flag specifies // whether EnsureDead should be called on an entity before // removing. The GetAuthFunc will be used on each invocation of Remove // to determine current permissions. func NewRemover(st state.EntityFinder, callEnsureDead bool, getCanModify GetAuthFunc) *Remover { return &Remover{ st: st, callEnsureDead: callEnsureDead, getCanModify: getCanModify, } } func (r *Remover) removeEntity(tag string) error { entity, err := r.st.FindEntity(tag) if err != nil { return err } remover, ok := entity.(interface { state.Lifer state.Remover state.EnsureDeader }) if !ok { return NotSupportedError(tag, "removal") } // Only remove entites that are not Alive. if life := remover.Life(); life == state.Alive { return fmt.Errorf("cannot remove entity %q: still alive", tag) } if r.callEnsureDead { if err := remover.EnsureDead(); err != nil { return err } } return remover.Remove() } // Remove removes every given entity from state, calling EnsureDead // first, then Remove. It will fail if the entity is not present. func (r *Remover) Remove(args params.Entities) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canModify, err := r.getCanModify() if err != nil { return params.ErrorResults{}, err } for i, entity := range args.Entities { err := ErrPerm if canModify(entity.Tag) { err = r.removeEntity(entity.Tag) } result.Results[i].Error = ServerError(err) } return result, nil } ���������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/instanceidgetter.go�������������0000644�0000153�0000161�00000003310�12321735642�031155� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" ) // InstanceIdGetter implements a common InstanceId method for use by // various facades. type InstanceIdGetter struct { st state.EntityFinder getCanRead GetAuthFunc } // NewInstanceIdGetter returns a new InstanceIdGetter. The GetAuthFunc // will be used on each invocation of InstanceId to determine current // permissions. func NewInstanceIdGetter(st state.EntityFinder, getCanRead GetAuthFunc) *InstanceIdGetter { return &InstanceIdGetter{ st: st, getCanRead: getCanRead, } } func (ig *InstanceIdGetter) getInstanceId(tag string) (instance.Id, error) { entity0, err := ig.st.FindEntity(tag) if err != nil { return "", err } entity, ok := entity0.(state.InstanceIdGetter) if !ok { return "", NotSupportedError(tag, "instance id") } return entity.InstanceId() } // InstanceId returns the provider specific instance id for each given // machine or an CodeNotProvisioned error, if not set. func (ig *InstanceIdGetter) InstanceId(args params.Entities) (params.StringResults, error) { result := params.StringResults{ Results: make([]params.StringResult, len(args.Entities)), } canRead, err := ig.getCanRead() if err != nil { return result, err } for i, entity := range args.Entities { err := ErrPerm if canRead(entity.Tag) { var instanceId instance.Id instanceId, err = ig.getInstanceId(entity.Tag) if err == nil { result.Results[i].Result = string(instanceId) } } result.Results[i].Error = ServerError(err) } return result, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/interfaces.go�������������������0000644�0000153�0000161�00000003743�12321735642�027756� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/state" ) // AuthFunc returns whether the given entity is available to some operation. type AuthFunc func(tag string) bool // GetAuthFunc returns an AuthFunc. type GetAuthFunc func() (AuthFunc, error) // Authorizer represents a value that can be asked for authorization // information on its associated authenticated entity. It is // implemented by an API server to allow an API implementation to ask // questions about the client that is currently connected. type Authorizer interface { // AuthMachineAgent returns whether the authenticated entity is a // machine agent. AuthMachineAgent() bool // AuthUnitAgent returns whether the authenticated entity is a // unit agent. AuthUnitAgent() bool // AuthOwner returns whether the authenticated entity is the same // as the given entity. AuthOwner(tag string) bool // AuthEnvironManager returns whether the authenticated entity is // a machine running the environment manager job. AuthEnvironManager() bool // AuthClient returns whether the authenticated entity // is a client user. AuthClient() bool // GetAuthTag returns the tag of the authenticated entity. GetAuthTag() string // GetAuthEntity returns the authenticated entity. GetAuthEntity() state.Entity } // AuthEither returns an AuthFunc generator that returns and AuthFunc // that accepts any tag authorized by either of its arguments. func AuthEither(a, b GetAuthFunc) GetAuthFunc { return func() (AuthFunc, error) { f1, err := a() if err != nil { return nil, err } f2, err := b() if err != nil { return nil, err } return func(tag string) bool { return f1(tag) || f2(tag) }, nil } } // AuthAlways returns an authentication function that always returns // the given permission. func AuthAlways(ok bool) GetAuthFunc { return func() (AuthFunc, error) { return func(tag string) bool { return ok }, nil } } �����������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/common_test.go������������������0000644�0000153�0000161�00000004031�12321735642�030151� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state/apiserver/common" coretesting "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type commonSuite struct{} var _ = gc.Suite(&commonSuite{}) func errorAuth() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } func fooAuth() (common.AuthFunc, error) { return func(tag string) bool { return tag == "foo" }, nil } func barAuth() (common.AuthFunc, error) { return func(tag string) bool { return tag == "bar" }, nil } var authEitherTests = []struct { about string a, b func() (common.AuthFunc, error) tag string expect bool err string }{{ about: "a returns an error", a: errorAuth, b: fooAuth, err: "pow", }, { about: "b returns an error", a: fooAuth, b: errorAuth, err: "pow", }, { about: "both a and b return an error", a: errorAuth, b: errorAuth, err: "pow", }, { about: "tag foo - a returns true", a: fooAuth, b: barAuth, tag: "foo", expect: true, }, { about: "tag foo - b returns true", a: barAuth, b: fooAuth, tag: "foo", expect: true, }, { about: "tag bar - b returns true", a: fooAuth, b: barAuth, tag: "bar", expect: true, }, { about: "tag foo - both return true", a: fooAuth, b: fooAuth, tag: "foo", expect: true, }, { about: "tag baz - both return false", a: fooAuth, b: barAuth, tag: "baz", expect: false, }} func (s *commonSuite) TestAuthEither(c *gc.C) { for i, test := range authEitherTests { c.Logf("test %d: %s", i, test.about) authEither := common.AuthEither(test.a, test.b) either, err := authEither() if test.err == "" { c.Assert(err, gc.IsNil) ok := either(test.tag) c.Assert(ok, gc.Equals, test.expect) } else { c.Assert(err, gc.ErrorMatches, test.err) c.Assert(either, gc.IsNil) } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/watch_test.go�������������������0000644�0000153�0000161�00000005224�12321735642�027774� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" ) type agentEntityWatcherSuite struct{} var _ = gc.Suite(&agentEntityWatcherSuite{}) type fakeAgentEntityWatcher struct { state.Entity fetchError } func (a *fakeAgentEntityWatcher) Watch() state.NotifyWatcher { changes := make(chan struct{}, 1) // Simulate initial event. changes <- struct{}{} return &fakeNotifyWatcher{changes} } type fakeNotifyWatcher struct { changes chan struct{} } func (*fakeNotifyWatcher) Stop() error { return nil } func (*fakeNotifyWatcher) Kill() {} func (*fakeNotifyWatcher) Wait() error { return nil } func (*fakeNotifyWatcher) Err() error { return nil } func (w *fakeNotifyWatcher) Changes() <-chan struct{} { return w.changes } func (*agentEntityWatcherSuite) TestWatch(c *gc.C) { st := &fakeState{ entities: map[string]entityWithError{ "x0": &fakeAgentEntityWatcher{fetchError: "x0 fails"}, "x1": &fakeAgentEntityWatcher{}, "x2": &fakeAgentEntityWatcher{}, }, } getCanWatch := func() (common.AuthFunc, error) { return func(tag string) bool { switch tag { case "x0", "x1": return true } return false }, nil } resources := common.NewResources() a := common.NewAgentEntityWatcher(st, resources, getCanWatch) entities := params.Entities{[]params.Entity{ {"x0"}, {"x1"}, {"x2"}, {"x3"}, }} result, err := a.Watch(entities) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{ Results: []params.NotifyWatchResult{ {Error: &params.Error{Message: "x0 fails"}}, {"1", nil}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (*agentEntityWatcherSuite) TestWatchError(c *gc.C) { getCanWatch := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } resources := common.NewResources() a := common.NewAgentEntityWatcher( &fakeState{}, resources, getCanWatch, ) _, err := a.Watch(params.Entities{[]params.Entity{{"x0"}}}) c.Assert(err, gc.ErrorMatches, "pow") } func (*agentEntityWatcherSuite) TestWatchNoArgsNoError(c *gc.C) { getCanWatch := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } resources := common.NewResources() a := common.NewAgentEntityWatcher( &fakeState{}, resources, getCanWatch, ) result, err := a.Watch(params.Entities{}) c.Assert(err, gc.IsNil) c.Assert(result.Results, gc.HasLen, 0) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/addresses_test.go���������������0000644�0000153�0000161�00000003734�12321735642�030647� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/apiserver/common" ) type stateAddresserSuite struct { addresser *common.StateAddresser } type apiAddresserSuite struct { addresser *common.APIAddresser } var _ = gc.Suite(&stateAddresserSuite{}) var _ = gc.Suite(&apiAddresserSuite{}) func (s *stateAddresserSuite) SetUpTest(c *gc.C) { s.addresser = common.NewStateAddresser(fakeAddresses{}) } // Verify that AddressAndCertGetter is satisfied by *state.State. var _ common.AddressAndCertGetter = (*state.State)(nil) func (s *stateAddresserSuite) TestStateAddresses(c *gc.C) { result, err := s.addresser.StateAddresses() c.Assert(err, gc.IsNil) c.Assert(result.Result, gc.DeepEquals, []string{"addresses:1", "addresses:2"}) } func (s *apiAddresserSuite) SetUpTest(c *gc.C) { s.addresser = common.NewAPIAddresser(fakeAddresses{}, common.NewResources()) } func (s *apiAddresserSuite) TestAPIAddresses(c *gc.C) { result, err := s.addresser.APIAddresses() c.Assert(err, gc.IsNil) c.Assert(result.Result, gc.DeepEquals, []string{"apiaddresses:1", "apiaddresses:2"}) } func (s *apiAddresserSuite) TestCACert(c *gc.C) { result := s.addresser.CACert() c.Assert(string(result.Result), gc.Equals, "a cert") } var _ common.AddressAndCertGetter = fakeAddresses{} type fakeAddresses struct{} func (fakeAddresses) Addresses() ([]string, error) { return []string{"addresses:1", "addresses:2"}, nil } func (fakeAddresses) APIAddressesFromMachines() ([]string, error) { return []string{"apiaddresses:1", "apiaddresses:2"}, nil } func (fakeAddresses) CACert() []byte { return []byte("a cert") } func (fakeAddresses) APIHostPorts() ([][]instance.HostPort, error) { panic("should never be called") } func (fakeAddresses) WatchAPIHostPorts() state.NotifyWatcher { panic("should never be called") } ������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/tools_test.go�������������������0000644�0000153�0000161�00000007176�12321735642�030036� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" "launchpad.net/juju-core/version" ) type toolsSuite struct { testing.JujuConnSuite machine0 *state.Machine } var _ = gc.Suite(&toolsSuite{}) func (s *toolsSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) var err error s.machine0, err = s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, gc.IsNil) } func (s *toolsSuite) TestTools(c *gc.C) { getCanRead := func() (common.AuthFunc, error) { return func(tag string) bool { return tag == "machine-0" || tag == "machine-42" }, nil } tg := common.NewToolsGetter(s.State, getCanRead) c.Assert(tg, gc.NotNil) err := s.machine0.SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) args := params.Entities{ Entities: []params.Entity{ {Tag: "machine-0"}, {Tag: "machine-1"}, {Tag: "machine-42"}, }} result, err := tg.Tools(args) c.Assert(err, gc.IsNil) c.Assert(result.Results, gc.HasLen, 3) c.Assert(result.Results[0].Tools, gc.NotNil) c.Assert(result.Results[0].Tools.Version, gc.DeepEquals, version.Current) c.Assert(result.Results[0].DisableSSLHostnameVerification, jc.IsFalse) c.Assert(result.Results[1].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.NotFoundError("machine 42")) } func (s *toolsSuite) TestToolsError(c *gc.C) { getCanRead := func() (common.AuthFunc, error) { return nil, fmt.Errorf("splat") } tg := common.NewToolsGetter(s.State, getCanRead) args := params.Entities{ Entities: []params.Entity{{Tag: "machine-42"}}, } result, err := tg.Tools(args) c.Assert(err, gc.ErrorMatches, "splat") c.Assert(result.Results, gc.HasLen, 1) } func (s *toolsSuite) TestSetTools(c *gc.C) { getCanWrite := func() (common.AuthFunc, error) { return func(tag string) bool { return tag == "machine-0" || tag == "machine-42" }, nil } ts := common.NewToolsSetter(s.State, getCanWrite) c.Assert(ts, gc.NotNil) err := s.machine0.SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) args := params.EntitiesVersion{ AgentTools: []params.EntityVersion{{ Tag: "machine-0", Tools: &params.Version{ Version: version.Current, }, }, { Tag: "machine-1", Tools: &params.Version{ Version: version.Current, }, }, { Tag: "machine-42", Tools: &params.Version{ Version: version.Current, }, }}, } result, err := ts.SetTools(args) c.Assert(err, gc.IsNil) c.Assert(result.Results, gc.HasLen, 3) c.Assert(result.Results[0].Error, gc.IsNil) agentTools, err := s.machine0.AgentTools() c.Assert(err, gc.IsNil) c.Assert(agentTools.Version, gc.DeepEquals, version.Current) c.Assert(result.Results[1].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.NotFoundError("machine 42")) } func (s *toolsSuite) TestToolsSetError(c *gc.C) { getCanWrite := func() (common.AuthFunc, error) { return nil, fmt.Errorf("splat") } ts := common.NewToolsSetter(s.State, getCanWrite) args := params.EntitiesVersion{ AgentTools: []params.EntityVersion{{ Tag: "machine-42", Tools: &params.Version{ Version: version.Current, }, }}, } result, err := ts.SetTools(args) c.Assert(err, gc.ErrorMatches, "splat") c.Assert(result.Results, gc.HasLen, 1) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/resource.go���������������������0000644�0000153�0000161�00000004572�12321735642�027463� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "strconv" "sync" "launchpad.net/juju-core/log" ) // Resource represents any resource that should be cleaned up when an // API connection terminates. The Stop method will be called when // that happens. type Resource interface { Stop() error } // Resources holds all the resources for a connection. // It allows the registration of resources that will be cleaned // up when a connection terminates. type Resources struct { mu sync.Mutex maxId uint64 resources map[string]Resource } func NewResources() *Resources { return &Resources{ resources: make(map[string]Resource), } } // Get returns the resource for the given id, or // nil if there is no such resource. func (rs *Resources) Get(id string) Resource { rs.mu.Lock() defer rs.mu.Unlock() return rs.resources[id] } // Register registers the given resource. It returns a unique // identifier for the resource which can then be used in // subsequent API requests to refer to the resource. func (rs *Resources) Register(r Resource) string { rs.mu.Lock() defer rs.mu.Unlock() rs.maxId++ id := strconv.FormatUint(rs.maxId, 10) rs.resources[id] = r return id } // Stop stops the resource with the given id and unregisters it. // It returns any error from the underlying Stop call. // It does not return an error if the resource has already // been unregistered. func (rs *Resources) Stop(id string) error { // We don't hold the mutex while calling Stop, because // that might take a while and we don't want to // stop all other resource manipulation while we do so. // If resources.Stop is called concurrently, we'll get // two concurrent calls to Stop, but that should fit // well with the way we invariably implement Stop. r := rs.Get(id) if r == nil { return nil } err := r.Stop() rs.mu.Lock() defer rs.mu.Unlock() delete(rs.resources, id) return err } // StopAll stops all the resources. func (rs *Resources) StopAll() { rs.mu.Lock() defer rs.mu.Unlock() for _, r := range rs.resources { if err := r.Stop(); err != nil { log.Errorf("state/api: error stopping %T resource: %v", r, err) } } rs.resources = make(map[string]Resource) } // Count returns the number of resources currently held. func (rs *Resources) Count() int { rs.mu.Lock() defer rs.mu.Unlock() return len(rs.resources) } ��������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/remove_test.go������������������0000644�0000153�0000161�00000005672�12321735642�030172� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" ) type removeSuite struct{} var _ = gc.Suite(&removeSuite{}) type fakeRemover struct { state.Entity life state.Life errEnsureDead error errRemove error fetchError } func (r *fakeRemover) EnsureDead() error { return r.errEnsureDead } func (r *fakeRemover) Remove() error { return r.errRemove } func (r *fakeRemover) Life() state.Life { return r.life } func (*removeSuite) TestRemove(c *gc.C) { st := &fakeState{ entities: map[string]entityWithError{ "x0": &fakeRemover{life: state.Dying, errEnsureDead: fmt.Errorf("x0 EnsureDead fails")}, "x1": &fakeRemover{life: state.Dying, errRemove: fmt.Errorf("x1 Remove fails")}, "x2": &fakeRemover{life: state.Alive}, "x3": &fakeRemover{life: state.Dying}, "x4": &fakeRemover{life: state.Dead}, "x5": &fakeRemover{fetchError: "x5 error"}, }, } getCanModify := func() (common.AuthFunc, error) { return func(tag string) bool { switch tag { case "x0", "x1", "x2", "x3", "x5": return true } return false }, nil } r := common.NewRemover(st, true, getCanModify) entities := params.Entities{[]params.Entity{ {"x0"}, {"x1"}, {"x2"}, {"x3"}, {"x4"}, {"x5"}, {"x6"}, }} result, err := r.Remove(entities) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {&params.Error{Message: "x0 EnsureDead fails"}}, {&params.Error{Message: "x1 Remove fails"}}, {&params.Error{Message: `cannot remove entity "x2": still alive`}}, {nil}, {apiservertesting.ErrUnauthorized}, {&params.Error{Message: "x5 error"}}, {apiservertesting.ErrUnauthorized}, }, }) // Make sure when callEnsureDead is false EnsureDead() doesn't // get called. r = common.NewRemover(st, false, getCanModify) entities = params.Entities{[]params.Entity{{"x0"}, {"x1"}}} result, err = r.Remove(entities) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {nil}, {&params.Error{Message: "x1 Remove fails"}}, }, }) } func (*removeSuite) TestRemoveError(c *gc.C) { getCanModify := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } r := common.NewRemover(&fakeState{}, true, getCanModify) _, err := r.Remove(params.Entities{[]params.Entity{{"x0"}}}) c.Assert(err, gc.ErrorMatches, "pow") } func (*removeSuite) TestRemoveNoArgsNoError(c *gc.C) { getCanModify := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } r := common.NewRemover(&fakeState{}, true, getCanModify) result, err := r.Remove(params.Entities{}) c.Assert(err, gc.IsNil) c.Assert(result.Results, gc.HasLen, 0) } ����������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/resource_test.go����������������0000644�0000153�0000161�00000003563�12321735642�030521� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package common_test import ( "sync" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state/apiserver/common" ) type resourceSuite struct{} var _ = gc.Suite(resourceSuite{}) type fakeResource struct { stopped bool } func (r *fakeResource) Stop() error { r.stopped = true return nil } func (resourceSuite) TestRegisterGetCount(c *gc.C) { rs := common.NewResources() r1 := &fakeResource{} id := rs.Register(r1) c.Assert(id, gc.Equals, "1") c.Assert(rs.Get("1"), gc.Equals, r1) c.Assert(rs.Count(), gc.Equals, 1) r2 := &fakeResource{} id = rs.Register(r2) c.Assert(id, gc.Equals, "2") c.Assert(rs.Get("2"), gc.Equals, r2) c.Assert(rs.Count(), gc.Equals, 2) } func (resourceSuite) TestConcurrency(c *gc.C) { // This test is designed to cause the race detector // to fail if the locking is not done correctly. var wg sync.WaitGroup rs := common.NewResources() start := func(f func()) { wg.Add(1) go func() { f() wg.Done() }() } rs.Register(&fakeResource{}) start(func() { rs.Register(&fakeResource{}) }) start(func() { rs.Stop("1") }) start(func() { rs.Count() }) start(func() { rs.StopAll() }) start(func() { rs.Get("2") }) wg.Wait() } func (resourceSuite) TestStop(c *gc.C) { rs := common.NewResources() r1 := &fakeResource{} rs.Register(r1) r2 := &fakeResource{} rs.Register(r2) rs.Stop("1") c.Assert(r1.stopped, gc.Equals, true) c.Assert(rs.Get("1"), gc.IsNil) c.Assert(r2.stopped, gc.Equals, false) c.Assert(rs.Get("2"), gc.Equals, r2) c.Assert(rs.Count(), gc.Equals, 1) } func (resourceSuite) TestStopAll(c *gc.C) { rs := common.NewResources() r1 := &fakeResource{} rs.Register(r1) r2 := &fakeResource{} rs.Register(r2) rs.StopAll() c.Assert(r1.stopped, gc.Equals, true) c.Assert(rs.Get("1"), gc.IsNil) c.Assert(r2.stopped, gc.Equals, true) c.Assert(rs.Get("2"), gc.IsNil) c.Assert(rs.Count(), gc.Equals, 0) } ���������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/tools.go������������������������0000644�0000153�0000161�00000010735�12321735642�026772� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "fmt" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) type EntityFinderEnvironConfigGetter interface { state.EntityFinder EnvironConfig() (*config.Config, error) } // ToolsGetter implements a common Tools method for use by various // facades. type ToolsGetter struct { st EntityFinderEnvironConfigGetter getCanRead GetAuthFunc } // NewToolsGetter returns a new ToolsGetter. The GetAuthFunc will be // used on each invocation of Tools to determine current permissions. func NewToolsGetter(st EntityFinderEnvironConfigGetter, getCanRead GetAuthFunc) *ToolsGetter { return &ToolsGetter{ st: st, getCanRead: getCanRead, } } // Tools finds the tools necessary for the given agents. func (t *ToolsGetter) Tools(args params.Entities) (params.ToolsResults, error) { result := params.ToolsResults{ Results: make([]params.ToolsResult, len(args.Entities)), } canRead, err := t.getCanRead() if err != nil { return result, err } agentVersion, cfg, err := t.getGlobalAgentVersion() if err != nil { return result, err } // SSLHostnameVerification defaults to true, so we need to // invert that, for backwards-compatibility (older versions // will have DisableSSLHostnameVerification: false by default). disableSSLHostnameVerification := !cfg.SSLHostnameVerification() env, err := environs.New(cfg) if err != nil { return result, err } for i, entity := range args.Entities { agentTools, err := t.oneAgentTools(canRead, entity.Tag, agentVersion, env) if err == nil { result.Results[i].Tools = agentTools result.Results[i].DisableSSLHostnameVerification = disableSSLHostnameVerification } result.Results[i].Error = ServerError(err) } return result, nil } func (t *ToolsGetter) getGlobalAgentVersion() (version.Number, *config.Config, error) { // Get the Agent Version requested in the Environment Config nothing := version.Number{} cfg, err := t.st.EnvironConfig() if err != nil { return nothing, nil, err } agentVersion, ok := cfg.AgentVersion() if !ok { return nothing, nil, fmt.Errorf("agent version not set in environment config") } return agentVersion, cfg, nil } func (t *ToolsGetter) oneAgentTools(canRead AuthFunc, tag string, agentVersion version.Number, env environs.Environ) (*coretools.Tools, error) { if !canRead(tag) { return nil, ErrPerm } entity, err := t.st.FindEntity(tag) if err != nil { return nil, err } tooler, ok := entity.(state.AgentTooler) if !ok { return nil, NotSupportedError(tag, "agent tools") } existingTools, err := tooler.AgentTools() if err != nil { return nil, err } // TODO(jam): Avoid searching the provider for every machine // that wants to upgrade. The information could just be cached // in state, or even in the API servers return envtools.FindExactTools(env, agentVersion, existingTools.Version.Series, existingTools.Version.Arch) } // ToolsSetter implements a common Tools method for use by various // facades. type ToolsSetter struct { st state.EntityFinder getCanWrite GetAuthFunc } // NewToolsGetter returns a new ToolsGetter. The GetAuthFunc will be // used on each invocation of Tools to determine current permissions. func NewToolsSetter(st state.EntityFinder, getCanWrite GetAuthFunc) *ToolsSetter { return &ToolsSetter{ st: st, getCanWrite: getCanWrite, } } // SetTools updates the recorded tools version for the agents. func (t *ToolsSetter) SetTools(args params.EntitiesVersion) (params.ErrorResults, error) { results := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.AgentTools)), } canWrite, err := t.getCanWrite() if err != nil { return results, err } for i, agentTools := range args.AgentTools { err := t.setOneAgentVersion(agentTools.Tag, agentTools.Tools.Version, canWrite) results.Results[i].Error = ServerError(err) } return results, nil } func (t *ToolsSetter) setOneAgentVersion(tag string, vers version.Binary, canWrite AuthFunc) error { if !canWrite(tag) { return ErrPerm } entity0, err := t.st.FindEntity(tag) if err != nil { return err } entity, ok := entity0.(state.AgentTooler) if !ok { return NotSupportedError(tag, "agent tools") } return entity.SetAgentVersion(vers) } �����������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/password.go���������������������0000644�0000153�0000161�00000005237�12321735642�027475� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/log" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" ) // PasswordChanger implements a common SetPasswords method for use by // various facades. type PasswordChanger struct { st state.EntityFinder getCanChange GetAuthFunc } // NewPasswordChanger returns a new PasswordChanger. The GetAuthFunc will be // used on each invocation of SetPasswords to determine current permissions. func NewPasswordChanger(st state.EntityFinder, getCanChange GetAuthFunc) *PasswordChanger { return &PasswordChanger{ st: st, getCanChange: getCanChange, } } // SetPasswords sets the given password for each supplied entity, if possible. func (pc *PasswordChanger) SetPasswords(args params.EntityPasswords) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Changes)), } if len(args.Changes) == 0 { return result, nil } canChange, err := pc.getCanChange() if err != nil { return params.ErrorResults{}, err } for i, param := range args.Changes { if !canChange(param.Tag) { result.Results[i].Error = ServerError(ErrPerm) continue } if err := pc.setPassword(param.Tag, param.Password); err != nil { result.Results[i].Error = ServerError(err) } } return result, nil } func (pc *PasswordChanger) setMongoPassword(entity state.Entity, password string) error { type mongoPassworder interface { SetMongoPassword(password string) error } // We set the mongo password first on the grounds that // if it fails, the agent in question should still be able // to authenticate to another API server and ask it to change // its password. if entity0, ok := entity.(mongoPassworder); ok { if err := entity0.SetMongoPassword(password); err != nil { return err } log.Infof("setting mongo password for %q", entity.Tag()) return nil } return NotSupportedError(entity.Tag(), "mongo access") } func (pc *PasswordChanger) setPassword(tag, password string) error { type jobsGetter interface { Jobs() []state.MachineJob } var err error entity0, err := pc.st.FindEntity(tag) if err != nil { return err } entity, ok := entity0.(state.Authenticator) if !ok { return NotSupportedError(tag, "authentication") } if entity, ok := entity0.(jobsGetter); ok { for _, job := range entity.Jobs() { paramsJob := job.ToParams() if paramsJob.NeedsState() { err = pc.setMongoPassword(entity0, password) break } } } if err == nil { err = entity.SetPassword(password) log.Infof("setting password for %q", tag) } return err } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/life_test.go��������������������0000644�0000153�0000161�00000004004�12321735642�027600� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" ) type lifeSuite struct{} var _ = gc.Suite(&lifeSuite{}) type fakeLifer struct { state.Entity life state.Life fetchError } func (l *fakeLifer) Life() state.Life { return l.life } func (*lifeSuite) TestLife(c *gc.C) { st := &fakeState{ entities: map[string]entityWithError{ "x0": &fakeLifer{life: state.Alive}, "x1": &fakeLifer{life: state.Dying}, "x2": &fakeLifer{life: state.Dead}, "x3": &fakeLifer{fetchError: "x3 error"}, }, } getCanRead := func() (common.AuthFunc, error) { return func(tag string) bool { switch tag { case "x0", "x2", "x3": return true } return false }, nil } lg := common.NewLifeGetter(st, getCanRead) entities := params.Entities{[]params.Entity{ {"x0"}, {"x1"}, {"x2"}, {"x3"}, {"x4"}, }} results, err := lg.Life(entities) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.LifeResults{ Results: []params.LifeResult{ {Life: params.Alive}, {Error: apiservertesting.ErrUnauthorized}, {Life: params.Dead}, {Error: &params.Error{Message: "x3 error"}}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (*lifeSuite) TestLifeError(c *gc.C) { getCanRead := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } lg := common.NewLifeGetter(&fakeState{}, getCanRead) _, err := lg.Life(params.Entities{[]params.Entity{{"x0"}}}) c.Assert(err, gc.ErrorMatches, "pow") } func (*lifeSuite) TestLifeNoArgsNoError(c *gc.C) { getCanRead := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } lg := common.NewLifeGetter(&fakeState{}, getCanRead) result, err := lg.Life(params.Entities{}) c.Assert(err, gc.IsNil) c.Assert(result.Results, gc.HasLen, 0) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/unitswatcher_test.go������������0000644�0000153�0000161�00000005343�12321735642�031410� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" ) type unitsWatcherSuite struct{} var _ = gc.Suite(&unitsWatcherSuite{}) type fakeUnitsWatcher struct { state.UnitsWatcher initial []string fetchError } func (f *fakeUnitsWatcher) WatchUnits() state.StringsWatcher { changes := make(chan []string, 1) // Simulate initial event. changes <- f.initial return &fakeStringsWatcher{changes} } type fakeStringsWatcher struct { changes chan []string } func (*fakeStringsWatcher) Stop() error { return nil } func (*fakeStringsWatcher) Kill() {} func (*fakeStringsWatcher) Wait() error { return nil } func (*fakeStringsWatcher) Err() error { return nil } func (w *fakeStringsWatcher) Changes() <-chan []string { return w.changes } func (*unitsWatcherSuite) TestWatchUnits(c *gc.C) { st := &fakeState{ entities: map[string]entityWithError{ "x0": &fakeUnitsWatcher{fetchError: "x0 fails"}, "x1": &fakeUnitsWatcher{initial: []string{"foo", "bar"}}, "x2": &fakeUnitsWatcher{}, }, } getCanWatch := func() (common.AuthFunc, error) { return func(tag string) bool { switch tag { case "x0", "x1": return true } return false }, nil } resources := common.NewResources() w := common.NewUnitsWatcher(st, resources, getCanWatch) entities := params.Entities{[]params.Entity{ {"x0"}, {"x1"}, {"x2"}, {"x3"}, }} result, err := w.WatchUnits(entities) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.StringsWatchResults{ Results: []params.StringsWatchResult{ {Error: &params.Error{Message: "x0 fails"}}, {"1", []string{"foo", "bar"}, nil}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (*unitsWatcherSuite) TestWatchUnitsError(c *gc.C) { getCanWatch := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } resources := common.NewResources() w := common.NewUnitsWatcher( &fakeState{}, resources, getCanWatch, ) _, err := w.WatchUnits(params.Entities{[]params.Entity{{"x0"}}}) c.Assert(err, gc.ErrorMatches, "pow") } func (*unitsWatcherSuite) TestWatchNoArgsNoError(c *gc.C) { getCanWatch := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } resources := common.NewResources() w := common.NewUnitsWatcher( &fakeState{}, resources, getCanWatch, ) result, err := w.WatchUnits(params.Entities{}) c.Assert(err, gc.IsNil) c.Assert(result.Results, gc.HasLen, 0) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/setstatus.go��������������������0000644�0000153�0000161�00000005750�12321735642�027672� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "fmt" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" ) // StatusSetter implements a common SetStatus method for use by // various facades. type StatusSetter struct { st state.EntityFinder getCanModify GetAuthFunc } // NewStatusSetter returns a new StatusSetter. The GetAuthFunc will be // used on each invocation of SetStatus to determine current // permissions. func NewStatusSetter(st state.EntityFinder, getCanModify GetAuthFunc) *StatusSetter { return &StatusSetter{ st: st, getCanModify: getCanModify, } } func (s *StatusSetter) setEntityStatus(tag string, status params.Status, info string, data params.StatusData) error { entity0, err := s.st.FindEntity(tag) if err != nil { return err } entity, ok := entity0.(state.StatusSetter) if !ok { return NotSupportedError(tag, "setting status") } return entity.SetStatus(status, info, data) } // SetStatus sets the status of each given entity. func (s *StatusSetter) SetStatus(args params.SetStatus) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canModify, err := s.getCanModify() if err != nil { return params.ErrorResults{}, err } for i, arg := range args.Entities { err := ErrPerm if canModify(arg.Tag) { err = s.setEntityStatus(arg.Tag, arg.Status, arg.Info, arg.Data) } result.Results[i].Error = ServerError(err) } return result, nil } func (s *StatusSetter) updateEntityStatusData(tag string, data params.StatusData) error { entity0, err := s.st.FindEntity(tag) if err != nil { return err } statusGetter, ok := entity0.(state.StatusGetter) if !ok { return NotSupportedError(tag, "getting status") } existingStatus, existingInfo, existingData, err := statusGetter.Status() if err != nil { return err } newData := existingData if newData == nil { newData = data } else { for k, v := range data { newData[k] = v } } entity, ok := entity0.(state.StatusSetter) if !ok { return NotSupportedError(tag, "updating status") } if len(newData) > 0 && existingStatus != params.StatusError { return fmt.Errorf("machine %q is not in an error state", tag) } return entity.SetStatus(existingStatus, existingInfo, newData) } // UpdateStatus updates the status data of each given entity. func (s *StatusSetter) UpdateStatus(args params.SetStatus) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canModify, err := s.getCanModify() if err != nil { return params.ErrorResults{}, err } for i, arg := range args.Entities { err := ErrPerm if canModify(arg.Tag) { err = s.updateEntityStatusData(arg.Tag, arg.Data) } result.Results[i].Error = ServerError(err) } return result, nil } ������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/environmachineswatcher.go�������0000644�0000153�0000161�00000003251�12321735642�032373� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "fmt" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" ) // EnvironMachinesWatcher implements a common WatchEnvironMachines // method for use by various facades. type EnvironMachinesWatcher struct { st state.EnvironMachinesWatcher resources *Resources getCanWatch GetAuthFunc } // NewEnvironMachinesWatcher returns a new EnvironMachinesWatcher. The // GetAuthFunc will be used on each invocation of WatchUnits to // determine current permissions. func NewEnvironMachinesWatcher(st state.EnvironMachinesWatcher, resources *Resources, getCanWatch GetAuthFunc) *EnvironMachinesWatcher { return &EnvironMachinesWatcher{ st: st, resources: resources, getCanWatch: getCanWatch, } } // WatchEnvironMachines returns a StringsWatcher that notifies of // changes to the life cycles of the top level machines in the current // environment. func (e *EnvironMachinesWatcher) WatchEnvironMachines() (params.StringsWatchResult, error) { result := params.StringsWatchResult{} canWatch, err := e.getCanWatch() if err != nil { return params.StringsWatchResult{}, err } if !canWatch("") { return result, ErrPerm } watch := e.st.WatchEnvironMachines() // Consume the initial event and forward it to the result. if changes, ok := <-watch.Changes(); ok { result.StringsWatcherId = e.resources.Register(watch) result.Changes = changes } else { err := watcher.MustErr(watch) return result, fmt.Errorf("cannot obtain initial environment machines: %v", err) } return result, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/environwatcher_test.go����������0000644�0000153�0000161�00000011604�12321735642�031723� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type environWatcherSuite struct { testbase.LoggingSuite testingEnvConfig *config.Config } var _ = gc.Suite(&environWatcherSuite{}) type fakeEnvironAccessor struct { envConfig *config.Config envConfigError error } func (*fakeEnvironAccessor) WatchForEnvironConfigChanges() state.NotifyWatcher { changes := make(chan struct{}, 1) // Simulate initial event. changes <- struct{}{} return &fakeNotifyWatcher{changes} } func (f *fakeEnvironAccessor) EnvironConfig() (*config.Config, error) { if f.envConfigError != nil { return nil, f.envConfigError } return f.envConfig, nil } func (s *environWatcherSuite) TearDownTest(c *gc.C) { dummy.Reset() s.LoggingSuite.TearDownTest(c) } func (*environWatcherSuite) TestWatchSuccess(c *gc.C) { getCanWatch := func() (common.AuthFunc, error) { return func(tag string) bool { return true }, nil } resources := common.NewResources() e := common.NewEnvironWatcher( &fakeEnvironAccessor{}, resources, getCanWatch, nil, ) result, err := e.WatchForEnvironConfigChanges() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.NotifyWatchResult{"1", nil}) c.Assert(resources.Count(), gc.Equals, 1) } func (*environWatcherSuite) TestWatchGetAuthError(c *gc.C) { getCanWatch := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } resources := common.NewResources() e := common.NewEnvironWatcher( &fakeEnvironAccessor{}, resources, getCanWatch, nil, ) _, err := e.WatchForEnvironConfigChanges() c.Assert(err, gc.ErrorMatches, "pow") c.Assert(resources.Count(), gc.Equals, 0) } func (*environWatcherSuite) TestWatchAuthError(c *gc.C) { getCanWatch := func() (common.AuthFunc, error) { return func(tag string) bool { return false }, nil } resources := common.NewResources() e := common.NewEnvironWatcher( &fakeEnvironAccessor{}, resources, getCanWatch, nil, ) result, err := e.WatchForEnvironConfigChanges() c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(result, gc.DeepEquals, params.NotifyWatchResult{}) c.Assert(resources.Count(), gc.Equals, 0) } func (*environWatcherSuite) TestEnvironConfigSuccess(c *gc.C) { getCanReadSecrets := func() (common.AuthFunc, error) { return func(tag string) bool { return true }, nil } testingEnvConfig := testingEnvConfig(c) e := common.NewEnvironWatcher( &fakeEnvironAccessor{envConfig: testingEnvConfig}, nil, nil, getCanReadSecrets, ) result, err := e.EnvironConfig() c.Assert(err, gc.IsNil) // Make sure we can read the secret attribute (i.e. it's not masked). c.Check(result.Config["secret"], gc.Equals, "pork") c.Check(map[string]interface{}(result.Config), jc.DeepEquals, testingEnvConfig.AllAttrs()) } func (*environWatcherSuite) TestEnvironConfigFetchError(c *gc.C) { getCanReadSecrets := func() (common.AuthFunc, error) { return func(tag string) bool { return true }, nil } e := common.NewEnvironWatcher( &fakeEnvironAccessor{ envConfigError: fmt.Errorf("pow"), }, nil, nil, getCanReadSecrets, ) _, err := e.EnvironConfig() c.Assert(err, gc.ErrorMatches, "pow") } func (*environWatcherSuite) TestEnvironConfigGetAuthError(c *gc.C) { getCanReadSecrets := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } e := common.NewEnvironWatcher( &fakeEnvironAccessor{envConfig: testingEnvConfig(c)}, nil, nil, getCanReadSecrets, ) _, err := e.EnvironConfig() c.Assert(err, gc.ErrorMatches, "pow") } func (*environWatcherSuite) TestEnvironConfigReadSecretsFalse(c *gc.C) { getCanReadSecrets := func() (common.AuthFunc, error) { return func(tag string) bool { return false }, nil } testingEnvConfig := testingEnvConfig(c) e := common.NewEnvironWatcher( &fakeEnvironAccessor{envConfig: testingEnvConfig}, nil, nil, getCanReadSecrets, ) result, err := e.EnvironConfig() c.Assert(err, gc.IsNil) // Make sure the secret attribute is masked. c.Check(result.Config["secret"], gc.Equals, "not available") // And only that is masked. result.Config["secret"] = "pork" c.Check(map[string]interface{}(result.Config), jc.DeepEquals, testingEnvConfig.AllAttrs()) } func testingEnvConfig(c *gc.C) *config.Config { cfg, err := config.New(config.NoDefaults, dummy.SampleConfig()) c.Assert(err, gc.IsNil) env, err := environs.Prepare(cfg, testing.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) return env.Config() } ����������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/environwatcher.go���������������0000644�0000153�0000161�00000006436�12321735642�030673� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/environs" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" ) // EnvironWatcher implements two common methods for use by various // facades - WatchForEnvironConfigChanges and EnvironConfig. type EnvironWatcher struct { st state.EnvironAccessor resources *Resources getCanWatch GetAuthFunc getCanReadSecrets GetAuthFunc } // NewEnvironWatcher returns a new EnvironWatcher. Active watchers // will be stored in the provided Resources. The two GetAuthFunc // callbacks will be used on each invocation of the methods to // determine current permissions. // Right now, environment tags are not used, so both created AuthFuncs // are called with "" for tag, which means "the current environment". func NewEnvironWatcher(st state.EnvironAccessor, resources *Resources, getCanWatch, getCanReadSecrets GetAuthFunc) *EnvironWatcher { return &EnvironWatcher{ st: st, resources: resources, getCanWatch: getCanWatch, getCanReadSecrets: getCanReadSecrets, } } // WatchForEnvironConfigChanges returns a NotifyWatcher that observes // changes to the environment configuration. // Note that although the NotifyWatchResult contains an Error field, // it's not used because we are only returning a single watcher, // so we use the regular error return. func (e *EnvironWatcher) WatchForEnvironConfigChanges() (params.NotifyWatchResult, error) { result := params.NotifyWatchResult{} canWatch, err := e.getCanWatch() if err != nil { return result, err } // TODO(dimitern) If we have multiple environments in state, use a // tag argument here and as a method argument. if !canWatch("") { return result, ErrPerm } watch := e.st.WatchForEnvironConfigChanges() // Consume the initial event. Technically, API // calls to Watch 'transmit' the initial event // in the Watch response. But NotifyWatchers // have no state to transmit. if _, ok := <-watch.Changes(); ok { result.NotifyWatcherId = e.resources.Register(watch) } else { return result, watcher.MustErr(watch) } return result, nil } // EnvironConfig returns the current environment's configuration. func (e *EnvironWatcher) EnvironConfig() (params.EnvironConfigResult, error) { result := params.EnvironConfigResult{} canReadSecrets, err := e.getCanReadSecrets() if err != nil { return result, err } config, err := e.st.EnvironConfig() if err != nil { return result, err } allAttrs := config.AllAttrs() // TODO(dimitern) If we have multiple environments in state, use a // tag argument here and as a method argument. if !canReadSecrets("") { // Mask out any secrets in the environment configuration // with values of the same type, so it'll pass validation. // // TODO(dimitern) 201309-26 bug #1231384 // Delete the code below and mark the bug as fixed, // once it's live tested on MAAS and 1.16 compatibility // is dropped. env, err := environs.New(config) if err != nil { return result, err } secretAttrs, err := env.Provider().SecretAttrs(config) for k := range secretAttrs { allAttrs[k] = "not available" } } result.Config = allAttrs return result, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/addresses.go��������������������0000644�0000153�0000161�00000005572�12321735642�027612� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" ) // AddressAndCertGetter can be used to find out // state server addresses and the CA public certificate. type AddressAndCertGetter interface { Addresses() ([]string, error) APIAddressesFromMachines() ([]string, error) CACert() []byte APIHostPorts() ([][]instance.HostPort, error) WatchAPIHostPorts() state.NotifyWatcher } // APIAddresser implements the APIAddresses method type APIAddresser struct { resources *Resources getter AddressAndCertGetter } // NewAPIAddresser returns a new APIAddresser that uses the given getter to // fetch its addresses. func NewAPIAddresser(getter AddressAndCertGetter, resources *Resources) *APIAddresser { return &APIAddresser{ getter: getter, resources: resources, } } // APIHostPorts returns the API server addresses. func (api *APIAddresser) APIHostPorts() (params.APIHostPortsResult, error) { servers, err := api.getter.APIHostPorts() if err != nil { return params.APIHostPortsResult{}, err } return params.APIHostPortsResult{ Servers: servers, }, nil } // WatchAPIHostPorts watches the API server addresses. func (api *APIAddresser) WatchAPIHostPorts() (params.NotifyWatchResult, error) { watch := api.getter.WatchAPIHostPorts() if _, ok := <-watch.Changes(); ok { return params.NotifyWatchResult{ NotifyWatcherId: api.resources.Register(watch), }, nil } return params.NotifyWatchResult{}, watcher.MustErr(watch) } // APIAddresses returns the list of addresses used to connect to the API. func (a *APIAddresser) APIAddresses() (params.StringsResult, error) { // TODO(rog) change this to use api.st.APIHostPorts() addrs, err := a.getter.APIAddressesFromMachines() if err != nil { return params.StringsResult{}, err } return params.StringsResult{ Result: addrs, }, nil } // CACert returns the certificate used to validate the state connection. func (a *APIAddresser) CACert() params.BytesResult { return params.BytesResult{ Result: a.getter.CACert(), } } // StateAddresser implements a common set of methods for getting state // server addresses, and the CA certificate used to authenticate them. type StateAddresser struct { getter AddressAndCertGetter } // NewAddresser returns a new StateAddresser that uses the given // st value to fetch its addresses. func NewStateAddresser(getter AddressAndCertGetter) *StateAddresser { return &StateAddresser{getter} } // StateAddresses returns the list of addresses used to connect to the state. func (a *StateAddresser) StateAddresses() (params.StringsResult, error) { addrs, err := a.getter.Addresses() if err != nil { return params.StringsResult{}, err } return params.StringsResult{ Result: addrs, }, nil } ��������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/environmachineswatcher_test.go��0000644�0000153�0000161�00000004126�12321735642�033434� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" ) type environMachinesWatcherSuite struct{} var _ = gc.Suite(&environMachinesWatcherSuite{}) type fakeEnvironMachinesWatcher struct { state.EnvironMachinesWatcher initial []string } func (f *fakeEnvironMachinesWatcher) WatchEnvironMachines() state.StringsWatcher { changes := make(chan []string, 1) // Simulate initial event. changes <- f.initial return &fakeStringsWatcher{changes} } func (*environMachinesWatcherSuite) TestWatchEnvironMachines(c *gc.C) { getCanWatch := func() (common.AuthFunc, error) { return func(tag string) bool { return true }, nil } resources := common.NewResources() e := common.NewEnvironMachinesWatcher( &fakeEnvironMachinesWatcher{initial: []string{"foo"}}, resources, getCanWatch, ) result, err := e.WatchEnvironMachines() c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.StringsWatchResult{"1", []string{"foo"}, nil}) c.Assert(resources.Count(), gc.Equals, 1) } func (*environMachinesWatcherSuite) TestWatchGetAuthError(c *gc.C) { getCanWatch := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } resources := common.NewResources() e := common.NewEnvironMachinesWatcher( &fakeEnvironMachinesWatcher{}, resources, getCanWatch, ) _, err := e.WatchEnvironMachines() c.Assert(err, gc.ErrorMatches, "pow") c.Assert(resources.Count(), gc.Equals, 0) } func (*environMachinesWatcherSuite) TestWatchAuthError(c *gc.C) { getCanWatch := func() (common.AuthFunc, error) { return func(tag string) bool { return false }, nil } resources := common.NewResources() e := common.NewEnvironMachinesWatcher( &fakeEnvironMachinesWatcher{}, resources, getCanWatch, ) _, err := e.WatchEnvironMachines() c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(resources.Count(), gc.Equals, 0) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/setstatus_test.go���������������0000644�0000153�0000161�00000012517�12321735642�030730� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" ) type statusSetterSuite struct{} var _ = gc.Suite(&statusSetterSuite{}) type fakeStatusSetter struct { state.Entity status params.Status info string data params.StatusData err error fetchError } func (s *fakeStatusSetter) SetStatus(status params.Status, info string, data params.StatusData) error { s.status = status s.info = info s.data = data return s.err } func (s *fakeStatusSetter) Status() (status params.Status, info string, data params.StatusData, err error) { return s.status, s.info, s.data, nil } func (s *fakeStatusSetter) UpdateStatus(data params.StatusData) error { for k, v := range data { s.data[k] = v } return s.err } func (*statusSetterSuite) TestSetStatus(c *gc.C) { st := &fakeState{ entities: map[string]entityWithError{ "x0": &fakeStatusSetter{status: params.StatusPending, info: "blah", err: fmt.Errorf("x0 fails")}, "x1": &fakeStatusSetter{status: params.StatusStarted, info: "foo"}, "x2": &fakeStatusSetter{status: params.StatusError, info: "some info"}, "x3": &fakeStatusSetter{fetchError: "x3 error"}, "x4": &fakeStatusSetter{status: params.StatusStopped, info: ""}, }, } getCanModify := func() (common.AuthFunc, error) { return func(tag string) bool { switch tag { case "x0", "x1", "x2", "x3": return true } return false }, nil } s := common.NewStatusSetter(st, getCanModify) args := params.SetStatus{ Entities: []params.EntityStatus{ {"x0", params.StatusStarted, "bar", nil}, {"x1", params.StatusStopped, "", nil}, {"x2", params.StatusPending, "not really", nil}, {"x3", params.StatusStopped, "", nil}, {"x4", params.StatusError, "blarg", nil}, {"x5", params.StatusStarted, "42", nil}, }, } result, err := s.SetStatus(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {&params.Error{Message: "x0 fails"}}, {nil}, {nil}, {&params.Error{Message: "x3 error"}}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) get := func(tag string) *fakeStatusSetter { return st.entities[tag].(*fakeStatusSetter) } c.Assert(get("x1").status, gc.Equals, params.StatusStopped) c.Assert(get("x1").info, gc.Equals, "") c.Assert(get("x2").status, gc.Equals, params.StatusPending) c.Assert(get("x2").info, gc.Equals, "not really") } func (*statusSetterSuite) TestSetStatusError(c *gc.C) { getCanModify := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } s := common.NewStatusSetter(&fakeState{}, getCanModify) args := params.SetStatus{ Entities: []params.EntityStatus{{"x0", "", "", nil}}, } _, err := s.SetStatus(args) c.Assert(err, gc.ErrorMatches, "pow") } func (*statusSetterSuite) TestSetStatusNoArgsNoError(c *gc.C) { getCanModify := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } s := common.NewStatusSetter(&fakeState{}, getCanModify) result, err := s.SetStatus(params.SetStatus{}) c.Assert(err, gc.IsNil) c.Assert(result.Results, gc.HasLen, 0) } func (*statusSetterSuite) TestUpdateStatus(c *gc.C) { st := &fakeState{ entities: map[string]entityWithError{ "x0": &fakeStatusSetter{status: params.StatusPending, info: "blah", err: fmt.Errorf("x0 fails")}, "x1": &fakeStatusSetter{status: params.StatusError, info: "foo", data: params.StatusData{"foo": "blah"}}, "x2": &fakeStatusSetter{status: params.StatusError, info: "some info"}, "x3": &fakeStatusSetter{fetchError: "x3 error"}, "x4": &fakeStatusSetter{status: params.StatusStarted}, "x5": &fakeStatusSetter{status: params.StatusStopped, info: ""}, }, } getCanModify := func() (common.AuthFunc, error) { return func(tag string) bool { switch tag { case "x0", "x1", "x2", "x3", "x4": return true } return false }, nil } s := common.NewStatusSetter(st, getCanModify) args := params.SetStatus{ Entities: []params.EntityStatus{ {Tag: "x0", Data: nil}, {Tag: "x1", Data: nil}, {Tag: "x2", Data: params.StatusData{"foo": "bar"}}, {Tag: "x3", Data: params.StatusData{"foo": "bar"}}, {Tag: "x4", Data: params.StatusData{"foo": "bar"}}, {Tag: "x5", Data: params.StatusData{"foo": "bar"}}, {Tag: "x6", Data: nil}, }, } result, err := s.UpdateStatus(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {&params.Error{Message: "x0 fails"}}, {nil}, {nil}, {&params.Error{Message: "x3 error"}}, {&params.Error{Message: `machine "x4" is not in an error state`}}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) get := func(tag string) *fakeStatusSetter { return st.entities[tag].(*fakeStatusSetter) } c.Assert(get("x1").status, gc.Equals, params.StatusError) c.Assert(get("x1").info, gc.Equals, "foo") c.Assert(get("x1").data, gc.DeepEquals, params.StatusData{"foo": "blah"}) c.Assert(get("x2").status, gc.Equals, params.StatusError) c.Assert(get("x2").info, gc.Equals, "some info") c.Assert(get("x2").data, gc.DeepEquals, params.StatusData{"foo": "bar"}) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/ensuredead_test.go��������������0000644�0000153�0000161�00000004417�12321735642�031010� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" ) type deadEnsurerSuite struct{} var _ = gc.Suite(&deadEnsurerSuite{}) type fakeDeadEnsurer struct { state.Entity life state.Life err error fetchError } func (e *fakeDeadEnsurer) EnsureDead() error { return e.err } func (e *fakeDeadEnsurer) Life() state.Life { return e.life } func (*deadEnsurerSuite) TestEnsureDead(c *gc.C) { st := &fakeState{ entities: map[string]entityWithError{ "x0": &fakeDeadEnsurer{life: state.Dying, err: fmt.Errorf("x0 fails")}, "x1": &fakeDeadEnsurer{life: state.Alive}, "x2": &fakeDeadEnsurer{life: state.Dying}, "x3": &fakeDeadEnsurer{life: state.Dead}, "x4": &fakeDeadEnsurer{fetchError: "x4 error"}, }, } getCanModify := func() (common.AuthFunc, error) { return func(tag string) bool { switch tag { case "x0", "x1", "x2", "x4": return true } return false }, nil } d := common.NewDeadEnsurer(st, getCanModify) entities := params.Entities{[]params.Entity{ {"x0"}, {"x1"}, {"x2"}, {"x3"}, {"x4"}, {"x5"}, }} result, err := d.EnsureDead(entities) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {&params.Error{Message: "x0 fails"}}, {nil}, {nil}, {apiservertesting.ErrUnauthorized}, {&params.Error{Message: "x4 error"}}, {apiservertesting.ErrUnauthorized}, }, }) } func (*deadEnsurerSuite) TestEnsureDeadError(c *gc.C) { getCanModify := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } d := common.NewDeadEnsurer(&fakeState{}, getCanModify) _, err := d.EnsureDead(params.Entities{[]params.Entity{{"x0"}}}) c.Assert(err, gc.ErrorMatches, "pow") } func (*removeSuite) TestEnsureDeadNoArgsNoError(c *gc.C) { getCanModify := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } d := common.NewDeadEnsurer(&fakeState{}, getCanModify) result, err := d.EnsureDead(params.Entities{}) c.Assert(err, gc.IsNil) c.Assert(result.Results, gc.HasLen, 0) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/watch.go������������������������0000644�0000153�0000161�00000004040�12321735642�026730� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" ) // AgentEntityWatcher implements a common Watch method for use by // various facades. type AgentEntityWatcher struct { st state.EntityFinder resources *Resources getCanWatch GetAuthFunc } // NewAgentEntityWatcher returns a new AgentEntityWatcher. The // GetAuthFunc will be used on each invocation of Watch to determine // current permissions. func NewAgentEntityWatcher(st state.EntityFinder, resources *Resources, getCanWatch GetAuthFunc) *AgentEntityWatcher { return &AgentEntityWatcher{ st: st, resources: resources, getCanWatch: getCanWatch, } } func (a *AgentEntityWatcher) watchEntity(tag string) (string, error) { entity0, err := a.st.FindEntity(tag) if err != nil { return "", err } entity, ok := entity0.(state.NotifyWatcherFactory) if !ok { return "", NotSupportedError(tag, "watching") } watch := entity.Watch() // Consume the initial event. Technically, API // calls to Watch 'transmit' the initial event // in the Watch response. But NotifyWatchers // have no state to transmit. if _, ok := <-watch.Changes(); ok { return a.resources.Register(watch), nil } return "", watcher.MustErr(watch) } // Watch starts an NotifyWatcher for each given entity. func (a *AgentEntityWatcher) Watch(args params.Entities) (params.NotifyWatchResults, error) { result := params.NotifyWatchResults{ Results: make([]params.NotifyWatchResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canWatch, err := a.getCanWatch() if err != nil { return params.NotifyWatchResults{}, err } for i, entity := range args.Entities { err := ErrPerm watcherId := "" if canWatch(entity.Tag) { watcherId, err = a.watchEntity(entity.Tag) } result.Results[i].NotifyWatcherId = watcherId result.Results[i].Error = ServerError(err) } return result, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/errors.go�����������������������0000644�0000153�0000161�00000006115�12321735776�027153� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( stderrors "errors" "fmt" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" ) type notSupportedError struct { entity string operation string } func (e *notSupportedError) Error() string { return fmt.Sprintf("entity %q does not support %s", e.entity, e.operation) } func NotSupportedError(entity, operation string) error { return &notSupportedError{entity, operation} } type noAddressSetError struct { unitTag string addressName string } func (e *noAddressSetError) Error() string { return fmt.Sprintf("%q has no %s address set", e.unitTag, e.addressName) } func NoAddressSetError(unitTag, addressName string) error { return &noAddressSetError{unitTag, addressName} } func IsNoAddressSetError(err error) bool { _, ok := err.(*noAddressSetError) return ok } var ( ErrBadId = stderrors.New("id not found") ErrBadCreds = stderrors.New("invalid entity name or password") ErrPerm = stderrors.New("permission denied") ErrNotLoggedIn = stderrors.New("not logged in") ErrUnknownWatcher = stderrors.New("unknown watcher id") ErrUnknownPinger = stderrors.New("unknown pinger id") ErrStoppedWatcher = stderrors.New("watcher has been stopped") ErrBadRequest = stderrors.New("invalid request") ) var singletonErrorCodes = map[error]string{ state.ErrCannotEnterScopeYet: params.CodeCannotEnterScopeYet, state.ErrCannotEnterScope: params.CodeCannotEnterScope, state.ErrExcessiveContention: params.CodeExcessiveContention, state.ErrUnitHasSubordinates: params.CodeUnitHasSubordinates, ErrBadId: params.CodeNotFound, ErrBadCreds: params.CodeUnauthorized, ErrPerm: params.CodeUnauthorized, ErrNotLoggedIn: params.CodeUnauthorized, ErrUnknownWatcher: params.CodeNotFound, ErrStoppedWatcher: params.CodeStopped, } func singletonCode(err error) (string, bool) { // All error types may not be hashable; deal with // that by catching the panic if we try to look up // a non-hashable type. defer func() { recover() }() code, ok := singletonErrorCodes[err] return code, ok } // ServerError returns an error suitable for returning to an API // client, with an error code suitable for various kinds of errors // generated in packages outside the API. func ServerError(err error) *params.Error { if err == nil { return nil } code, ok := singletonCode(err) switch { case ok: case errors.IsUnauthorizedError(err): code = params.CodeUnauthorized case errors.IsNotFoundError(err): code = params.CodeNotFound case state.IsNotAssigned(err): code = params.CodeNotAssigned case state.IsHasAssignedUnitsError(err): code = params.CodeHasAssignedUnits case IsNoAddressSetError(err): code = params.CodeNoAddressSet case state.IsNotProvisionedError(err): code = params.CodeNotProvisioned default: code = params.ErrCode(err) } return &params.Error{ Message: err.Error(), Code: code, } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/testing/������������������������0000755�0000153�0000161�00000000000�12321735642�026752� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/testing/environwatcher.go�������0000644�0000153�0000161�00000005227�12321735642�032345� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" statetesting "launchpad.net/juju-core/state/testing" ) const ( HasSecrets = true NoSecrets = false ) type EnvironmentWatcher interface { WatchForEnvironConfigChanges() (params.NotifyWatchResult, error) EnvironConfig() (params.EnvironConfigResult, error) } type EnvironWatcherTest struct { envWatcher EnvironmentWatcher st *state.State resources *common.Resources hasSecrets bool } func NewEnvironWatcherTest( envWatcher EnvironmentWatcher, st *state.State, resources *common.Resources, hasSecrets bool) *EnvironWatcherTest { return &EnvironWatcherTest{envWatcher, st, resources, hasSecrets} } // AssertEnvironConfig provides a method to test the config from the // envWatcher. This allows other tests that embed this type to have // more than just the default test. func (s *EnvironWatcherTest) AssertEnvironConfig(c *gc.C, envWatcher EnvironmentWatcher, hasSecrets bool) { envConfig, err := s.st.EnvironConfig() c.Assert(err, gc.IsNil) result, err := envWatcher.EnvironConfig() c.Assert(err, gc.IsNil) configAttributes := envConfig.AllAttrs() // If the implementor doesn't provide secrets, we need to replace the config // values in our environment to compare against with the secrets replaced. if !hasSecrets { env, err := environs.New(envConfig) c.Assert(err, gc.IsNil) secretAttrs, err := env.Provider().SecretAttrs(envConfig) c.Assert(err, gc.IsNil) for key := range secretAttrs { configAttributes[key] = "not available" } } c.Assert(result.Config, jc.DeepEquals, params.EnvironConfig(configAttributes)) } func (s *EnvironWatcherTest) TestEnvironConfig(c *gc.C) { s.AssertEnvironConfig(c, s.envWatcher, s.hasSecrets) } func (s *EnvironWatcherTest) TestWatchForEnvironConfigChanges(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) result, err := s.envWatcher.WatchForEnvironConfigChanges() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.NotifyWatchResult{ NotifyWatcherId: "1", }) // Verify the resources were registered and stop them when done. c.Assert(s.resources.Count(), gc.Equals, 1) resource := s.resources.Get("1") defer statetesting.AssertStop(c, resource) // Check that the Watch has consumed the initial event ("returned" // in the Watch call) wc := statetesting.NewNotifyWatcherC(c, s.st, resource.(state.NotifyWatcher)) wc.AssertNoChange() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/instanceidgetter_test.go��������0000644�0000153�0000161�00000004123�12321735642�032217� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" ) type instanceIdGetterSuite struct{} var _ = gc.Suite(&instanceIdGetterSuite{}) type fakeInstanceIdGetter struct { state.Entity instanceId string err string fetchError } func (f *fakeInstanceIdGetter) InstanceId() (instance.Id, error) { if f.err != "" { return "", fmt.Errorf(f.err) } return instance.Id(f.instanceId), nil } func (*instanceIdGetterSuite) TestInstanceId(c *gc.C) { st := &fakeState{ entities: map[string]entityWithError{ "x0": &fakeInstanceIdGetter{instanceId: "foo"}, "x1": &fakeInstanceIdGetter{instanceId: "bar"}, "x2": &fakeInstanceIdGetter{instanceId: "baz", err: "x2 error"}, "x3": &fakeInstanceIdGetter{fetchError: "x3 error"}, }, } getCanRead := func() (common.AuthFunc, error) { return func(tag string) bool { switch tag { case "x0", "x2", "x3": return true } return false }, nil } ig := common.NewInstanceIdGetter(st, getCanRead) entities := params.Entities{[]params.Entity{ {"x0"}, {"x1"}, {"x2"}, {"x3"}, {"x4"}, }} results, err := ig.InstanceId(entities) c.Assert(err, gc.IsNil) c.Assert(results, jc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Result: "foo"}, {Error: apiservertesting.ErrUnauthorized}, {Error: &params.Error{Message: "x2 error"}}, {Error: &params.Error{Message: "x3 error"}}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (*instanceIdGetterSuite) TestInstanceIdError(c *gc.C) { getCanRead := func() (common.AuthFunc, error) { return nil, fmt.Errorf("pow") } ig := common.NewInstanceIdGetter(&fakeState{}, getCanRead) _, err := ig.InstanceId(params.Entities{[]params.Entity{{"x0"}}}) c.Assert(err, gc.ErrorMatches, "pow") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/unitswatcher.go�����������������0000644�0000153�0000161�00000004210�12321735642�030341� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" ) // UnitsWatcher implements a common WatchUnits method for use by // various facades. type UnitsWatcher struct { st state.EntityFinder resources *Resources getCanWatch GetAuthFunc } // NewUnitsWatcher returns a new UnitsWatcher. The GetAuthFunc will be // used on each invocation of WatchUnits to determine current // permissions. func NewUnitsWatcher(st state.EntityFinder, resources *Resources, getCanWatch GetAuthFunc) *UnitsWatcher { return &UnitsWatcher{ st: st, resources: resources, getCanWatch: getCanWatch, } } func (u *UnitsWatcher) watchOneEntityUnits(canWatch AuthFunc, tag string) (params.StringsWatchResult, error) { nothing := params.StringsWatchResult{} if !canWatch(tag) { return nothing, ErrPerm } entity0, err := u.st.FindEntity(tag) if err != nil { return nothing, err } entity, ok := entity0.(state.UnitsWatcher) if !ok { return nothing, NotSupportedError(tag, "watching units") } watch := entity.WatchUnits() // Consume the initial event and forward it to the result. if changes, ok := <-watch.Changes(); ok { return params.StringsWatchResult{ StringsWatcherId: u.resources.Register(watch), Changes: changes, }, nil } return nothing, watcher.MustErr(watch) } // WatchUnits starts a StringsWatcher to watch all units belonging to // to any entity (machine or service) passed in args. func (u *UnitsWatcher) WatchUnits(args params.Entities) (params.StringsWatchResults, error) { result := params.StringsWatchResults{ Results: make([]params.StringsWatchResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canWatch, err := u.getCanWatch() if err != nil { return params.StringsWatchResults{}, err } for i, entity := range args.Entities { entityResult, err := u.watchOneEntityUnits(canWatch, entity.Tag) result.Results[i] = entityResult result.Results[i].Error = ServerError(err) } return result, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/life.go�������������������������0000644�0000153�0000161�00000003003�12321735642�026537� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" ) // LifeGetter implements a common Life method for use by various facades. type LifeGetter struct { st state.EntityFinder getCanRead GetAuthFunc } // NewLifeGetter returns a new LifeGetter. The GetAuthFunc will be used on // each invocation of Life to determine current permissions. func NewLifeGetter(st state.EntityFinder, getCanRead GetAuthFunc) *LifeGetter { return &LifeGetter{ st: st, getCanRead: getCanRead, } } func (lg *LifeGetter) oneLife(tag string) (params.Life, error) { entity0, err := lg.st.FindEntity(tag) if err != nil { return "", err } entity, ok := entity0.(state.Lifer) if !ok { return "", NotSupportedError(tag, "life cycles") } return params.Life(entity.Life().String()), nil } // Life returns the life status of every supplied entity, where available. func (lg *LifeGetter) Life(args params.Entities) (params.LifeResults, error) { result := params.LifeResults{ Results: make([]params.LifeResult, len(args.Entities)), } if len(args.Entities) == 0 { return result, nil } canRead, err := lg.getCanRead() if err != nil { return params.LifeResults{}, err } for i, entity := range args.Entities { err := ErrPerm if canRead(entity.Tag) { result.Results[i].Life, err = lg.oneLife(entity.Tag) } result.Results[i].Error = ServerError(err) } return result, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/common/password_test.go����������������0000644�0000153�0000161�00000011244�12321735642�030527� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" ) type passwordSuite struct{} var _ = gc.Suite(&passwordSuite{}) type entityWithError interface { state.Entity error() error } type fakeState struct { entities map[string]entityWithError } func (st *fakeState) FindEntity(tag string) (state.Entity, error) { entity, ok := st.entities[tag] if !ok { return nil, errors.NotFoundf("entity %q", tag) } if err := entity.error(); err != nil { return nil, err } return entity, nil } type fetchError string func (f fetchError) error() error { if f == "" { return nil } return fmt.Errorf("%s", string(f)) } type fakeAuthenticator struct { // Any Authenticator methods we don't implement on fakeAuthenticator // will fall back to this and panic because it's always nil. state.Authenticator state.Entity err error pass string fetchError } func (a *fakeAuthenticator) SetPassword(pass string) error { if a.err != nil { return a.err } a.pass = pass return nil } // fakeUnitAuthenticator simulates a unit entity. type fakeUnitAuthenticator struct { fakeAuthenticator mongoPass string } func (a *fakeUnitAuthenticator) Tag() string { return "fake" } func (a *fakeUnitAuthenticator) SetMongoPassword(pass string) error { if a.err != nil { return a.err } a.mongoPass = pass return nil } // fakeMachineAuthenticator simulates a machine entity. type fakeMachineAuthenticator struct { fakeUnitAuthenticator jobs []state.MachineJob } func (a *fakeMachineAuthenticator) Jobs() []state.MachineJob { return a.jobs } func (*passwordSuite) TestSetPasswords(c *gc.C) { st := &fakeState{ entities: map[string]entityWithError{ "x0": &fakeAuthenticator{}, "x1": &fakeAuthenticator{}, "x2": &fakeAuthenticator{ err: fmt.Errorf("x2 error"), }, "x3": &fakeAuthenticator{ fetchError: "x3 error", }, "x4": &fakeUnitAuthenticator{}, "x5": &fakeMachineAuthenticator{jobs: []state.MachineJob{state.JobHostUnits}}, "x6": &fakeMachineAuthenticator{jobs: []state.MachineJob{state.JobManageEnviron}}, }, } getCanChange := func() (common.AuthFunc, error) { return func(tag string) bool { return tag != "x0" }, nil } pc := common.NewPasswordChanger(st, getCanChange) var changes []params.EntityPassword for i := 0; i < len(st.entities); i++ { tag := fmt.Sprintf("x%d", i) changes = append(changes, params.EntityPassword{ Tag: tag, Password: fmt.Sprintf("%spass", tag), }) } results, err := pc.SetPasswords(params.EntityPasswords{ Changes: changes, }) c.Assert(err, gc.IsNil) c.Assert(results, jc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {&params.Error{Message: "x2 error"}}, {&params.Error{Message: "x3 error"}}, {nil}, {nil}, {nil}, }, }) c.Check(st.entities["x0"].(*fakeAuthenticator).pass, gc.Equals, "") c.Check(st.entities["x1"].(*fakeAuthenticator).pass, gc.Equals, "x1pass") c.Check(st.entities["x2"].(*fakeAuthenticator).pass, gc.Equals, "") c.Check(st.entities["x4"].(*fakeUnitAuthenticator).pass, gc.Equals, "x4pass") c.Check(st.entities["x4"].(*fakeUnitAuthenticator).mongoPass, gc.Equals, "") c.Check(st.entities["x5"].(*fakeMachineAuthenticator).pass, gc.Equals, "x5pass") c.Check(st.entities["x5"].(*fakeMachineAuthenticator).mongoPass, gc.Equals, "") c.Check(st.entities["x6"].(*fakeMachineAuthenticator).pass, gc.Equals, "x6pass") c.Check(st.entities["x6"].(*fakeMachineAuthenticator).mongoPass, gc.Equals, "x6pass") } func (*passwordSuite) TestSetPasswordsError(c *gc.C) { getCanChange := func() (common.AuthFunc, error) { return nil, fmt.Errorf("splat") } pc := common.NewPasswordChanger(&fakeState{}, getCanChange) var changes []params.EntityPassword for i := 0; i < 4; i++ { tag := fmt.Sprintf("x%d", i) changes = append(changes, params.EntityPassword{ Tag: tag, Password: fmt.Sprintf("%spass", tag), }) } _, err := pc.SetPasswords(params.EntityPasswords{Changes: changes}) c.Assert(err, gc.ErrorMatches, "splat") } func (*passwordSuite) TestSetPasswordsNoArgsNoError(c *gc.C) { getCanChange := func() (common.AuthFunc, error) { return nil, fmt.Errorf("splat") } pc := common.NewPasswordChanger(&fakeState{}, getCanChange) result, err := pc.SetPasswords(params.EntityPasswords{}) c.Assert(err, gc.IsNil) c.Assert(result.Results, gc.HasLen, 0) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/keyupdater/����������������������������0000755�0000153�0000161�00000000000�12321736000�026147� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/keyupdater/package_test.go�������������0000644�0000153�0000161�00000000372�12321735642�031145� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keyupdater_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/keyupdater/authorisedkeys.go�����������0000644�0000153�0000161�00000010367�12321735776�031573� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keyupdater import ( "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/utils/ssh" ) // KeyUpdater defines the methods on the keyupdater API end point. type KeyUpdater interface { AuthorisedKeys(args params.Entities) (params.StringsResults, error) WatchAuthorisedKeys(args params.Entities) (params.NotifyWatchResults, error) } // KeyUpdaterAPI implements the KeyUpdater interface and is the concrete // implementation of the api end point. type KeyUpdaterAPI struct { state *state.State resources *common.Resources authorizer common.Authorizer getCanRead common.GetAuthFunc } var _ KeyUpdater = (*KeyUpdaterAPI)(nil) // NewKeyUpdaterAPI creates a new server-side keyupdater API end point. func NewKeyUpdaterAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*KeyUpdaterAPI, error) { // Only machine agents have access to the keyupdater service. if !authorizer.AuthMachineAgent() { return nil, common.ErrPerm } // No-one else except the machine itself can only read a machine's own credentials. getCanRead := func() (common.AuthFunc, error) { return authorizer.AuthOwner, nil } return &KeyUpdaterAPI{state: st, resources: resources, authorizer: authorizer, getCanRead: getCanRead}, nil } // WatchAuthorisedKeys starts a watcher to track changes to the authorised ssh keys // for the specified machines. // The current implementation relies on global authorised keys being stored in the environment config. // This will change as new user management and authorisation functionality is added. func (api *KeyUpdaterAPI) WatchAuthorisedKeys(arg params.Entities) (params.NotifyWatchResults, error) { results := make([]params.NotifyWatchResult, len(arg.Entities)) canRead, err := api.getCanRead() if err != nil { return params.NotifyWatchResults{}, err } for i, entity := range arg.Entities { // 1. Check permissions if !canRead(entity.Tag) { results[i].Error = common.ServerError(common.ErrPerm) continue } // 2. Check entity exists if _, err := api.state.FindEntity(entity.Tag); err != nil { if errors.IsNotFoundError(err) { results[i].Error = common.ServerError(common.ErrPerm) } else { results[i].Error = common.ServerError(err) } continue } // 3. Watch fr changes var err error watch := api.state.WatchForEnvironConfigChanges() // Consume the initial event. if _, ok := <-watch.Changes(); ok { results[i].NotifyWatcherId = api.resources.Register(watch) } else { err = watcher.MustErr(watch) } results[i].Error = common.ServerError(err) } return params.NotifyWatchResults{results}, nil } // AuthorisedKeys reports the authorised ssh keys for the specified machines. // The current implementation relies on global authorised keys being stored in the environment config. // This will change as new user management and authorisation functionality is added. func (api *KeyUpdaterAPI) AuthorisedKeys(arg params.Entities) (params.StringsResults, error) { if len(arg.Entities) == 0 { return params.StringsResults{}, nil } results := make([]params.StringsResult, len(arg.Entities)) // For now, authorised keys are global, common to all machines. var keys []string config, configErr := api.state.EnvironConfig() if configErr == nil { keys = ssh.SplitAuthorisedKeys(config.AuthorizedKeys()) } canRead, err := api.getCanRead() if err != nil { return params.StringsResults{}, err } for i, entity := range arg.Entities { // 1. Check permissions if !canRead(entity.Tag) { results[i].Error = common.ServerError(common.ErrPerm) continue } // 2. Check entity exists if _, err := api.state.FindEntity(entity.Tag); err != nil { if errors.IsNotFoundError(err) { results[i].Error = common.ServerError(common.ErrPerm) } else { results[i].Error = common.ServerError(err) } continue } // 3. Get keys var err error if configErr == nil { results[i].Result = keys } else { err = configErr } results[i].Error = common.ServerError(err) } return params.StringsResults{results}, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/keyupdater/authorisedkeys_test.go������0000644�0000153�0000161�00000010677�12321735642�032626� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keyupdater_test import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/apiserver/keyupdater" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" statetesting "launchpad.net/juju-core/state/testing" ) type authorisedKeysSuite struct { jujutesting.JujuConnSuite // These are raw State objects. Use them for setup and assertions, but // should never be touched by the API calls themselves rawMachine *state.Machine unrelatedMachine *state.Machine keyupdater *keyupdater.KeyUpdaterAPI resources *common.Resources authoriser apiservertesting.FakeAuthorizer } var _ = gc.Suite(&authorisedKeysSuite{}) func (s *authorisedKeysSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.resources = common.NewResources() s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) // Create machines to work with var err error s.rawMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) s.unrelatedMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // The default auth is as a state server s.authoriser = apiservertesting.FakeAuthorizer{ Tag: s.rawMachine.Tag(), LoggedIn: true, MachineAgent: true, } s.keyupdater, err = keyupdater.NewKeyUpdaterAPI(s.State, s.resources, s.authoriser) c.Assert(err, gc.IsNil) } func (s *authorisedKeysSuite) TestNewKeyUpdaterAPIAcceptsStateServer(c *gc.C) { endPoint, err := keyupdater.NewKeyUpdaterAPI(s.State, s.resources, s.authoriser) c.Assert(err, gc.IsNil) c.Assert(endPoint, gc.NotNil) } func (s *authorisedKeysSuite) TestNewKeyUpdaterAPIRefusesNonMachineAgent(c *gc.C) { anAuthoriser := s.authoriser anAuthoriser.MachineAgent = false endPoint, err := keyupdater.NewKeyUpdaterAPI(s.State, s.resources, anAuthoriser) c.Assert(endPoint, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *authorisedKeysSuite) TestWatchAuthorisedKeysNothing(c *gc.C) { // Not an error to watch nothing results, err := s.keyupdater.WatchAuthorisedKeys(params.Entities{}) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 0) } func (s *authorisedKeysSuite) setAuthorizedKeys(c *gc.C, keys string) { err := s.State.UpdateEnvironConfig(map[string]interface{}{"authorized-keys": keys}, nil, nil) c.Assert(err, gc.IsNil) envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(envConfig.AuthorizedKeys(), gc.Equals, keys) } func (s *authorisedKeysSuite) TestWatchAuthorisedKeys(c *gc.C) { args := params.Entities{ Entities: []params.Entity{ {Tag: s.rawMachine.Tag()}, {Tag: s.unrelatedMachine.Tag()}, {Tag: "machine-42"}, }, } results, err := s.keyupdater.WatchAuthorisedKeys(args) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.NotifyWatchResults{ Results: []params.NotifyWatchResult{ {NotifyWatcherId: "1"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) c.Assert(results.Results[0].NotifyWatcherId, gc.Not(gc.Equals), "") c.Assert(results.Results[0].Error, gc.IsNil) resource := s.resources.Get(results.Results[0].NotifyWatcherId) c.Assert(resource, gc.NotNil) w := resource.(state.NotifyWatcher) wc := statetesting.NewNotifyWatcherC(c, s.State, w) wc.AssertNoChange() s.setAuthorizedKeys(c, "key1\nkey2") wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *authorisedKeysSuite) TestAuthorisedKeysForNoone(c *gc.C) { // Not an error to request nothing, dumb, but not an error. results, err := s.keyupdater.AuthorisedKeys(params.Entities{}) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 0) } func (s *authorisedKeysSuite) TestAuthorisedKeys(c *gc.C) { s.setAuthorizedKeys(c, "key1\nkey2") args := params.Entities{ Entities: []params.Entity{ {Tag: s.rawMachine.Tag()}, {Tag: s.unrelatedMachine.Tag()}, {Tag: "machine-42"}, }, } results, err := s.keyupdater.AuthorisedKeys(args) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.StringsResults{ Results: []params.StringsResult{ {Result: []string{"key1", "key2"}}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } �����������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/upgrader/������������������������������0000755�0000153�0000161�00000000000�12321736000�025603� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/upgrader/upgrader.go�������������������0000644�0000153�0000161�00000006633�12321735776�027776� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader import ( "errors" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/version" ) type Upgrader interface { WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) DesiredVersion(args params.Entities) (params.VersionResults, error) Tools(args params.Entities) (params.ToolsResults, error) SetTools(args params.EntitiesVersion) (params.ErrorResults, error) } // UpgraderAPI provides access to the Upgrader API facade. type UpgraderAPI struct { *common.ToolsGetter *common.ToolsSetter st *state.State resources *common.Resources authorizer common.Authorizer } // NewUpgraderAPI creates a new client-side UpgraderAPI facade. func NewUpgraderAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*UpgraderAPI, error) { if !authorizer.AuthMachineAgent() { return nil, common.ErrPerm } getCanReadWrite := func() (common.AuthFunc, error) { return authorizer.AuthOwner, nil } return &UpgraderAPI{ ToolsGetter: common.NewToolsGetter(st, getCanReadWrite), ToolsSetter: common.NewToolsSetter(st, getCanReadWrite), st: st, resources: resources, authorizer: authorizer, }, nil } // WatchAPIVersion starts a watcher to track if there is a new version // of the API that we want to upgrade to func (u *UpgraderAPI) WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) { result := params.NotifyWatchResults{ Results: make([]params.NotifyWatchResult, len(args.Entities)), } for i, agent := range args.Entities { err := common.ErrPerm if u.authorizer.AuthOwner(agent.Tag) { watch := u.st.WatchForEnvironConfigChanges() // Consume the initial event. Technically, API // calls to Watch 'transmit' the initial event // in the Watch response. But NotifyWatchers // have no state to transmit. if _, ok := <-watch.Changes(); ok { result.Results[i].NotifyWatcherId = u.resources.Register(watch) err = nil } else { err = watcher.MustErr(watch) } } result.Results[i].Error = common.ServerError(err) } return result, nil } func (u *UpgraderAPI) getGlobalAgentVersion() (version.Number, *config.Config, error) { // Get the Agent Version requested in the Environment Config cfg, err := u.st.EnvironConfig() if err != nil { return version.Number{}, nil, err } agentVersion, ok := cfg.AgentVersion() if !ok { return version.Number{}, nil, errors.New("agent version not set in environment config") } return agentVersion, cfg, nil } // DesiredVersion reports the Agent Version that we want that agent to be running func (u *UpgraderAPI) DesiredVersion(args params.Entities) (params.VersionResults, error) { results := make([]params.VersionResult, len(args.Entities)) if len(args.Entities) == 0 { return params.VersionResults{}, nil } agentVersion, _, err := u.getGlobalAgentVersion() if err != nil { return params.VersionResults{}, common.ServerError(err) } for i, entity := range args.Entities { err := common.ErrPerm if u.authorizer.AuthOwner(entity.Tag) { results[i].Version = &agentVersion err = nil } results[i].Error = common.ServerError(err) } return params.VersionResults{results}, nil } �����������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/upgrader/unitupgrader_test.go����������0000644�0000153�0000161�00000023103�12321735642�031714� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" "launchpad.net/juju-core/state/apiserver/upgrader" statetesting "launchpad.net/juju-core/state/testing" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) type unitUpgraderSuite struct { jujutesting.JujuConnSuite // These are raw State objects. Use them for setup and assertions, but // should never be touched by the API calls themselves rawMachine *state.Machine rawUnit *state.Unit upgrader *upgrader.UnitUpgraderAPI resources *common.Resources authorizer apiservertesting.FakeAuthorizer // Tools for the assigned machine. fakeTools *tools.Tools } var _ = gc.Suite(&unitUpgraderSuite{}) func (s *unitUpgraderSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.resources = common.NewResources() // Create a machine and unit to work with var err error _, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) s.rawUnit, err = svc.AddUnit() c.Assert(err, gc.IsNil) // Assign the unit to the machine. s.rawMachine, err = s.rawUnit.AssignToCleanMachine() c.Assert(err, gc.IsNil) // The default auth is as the unit agent s.authorizer = apiservertesting.FakeAuthorizer{ Tag: s.rawUnit.Tag(), LoggedIn: true, UnitAgent: true, } s.upgrader, err = upgrader.NewUnitUpgraderAPI(s.State, s.resources, s.authorizer, s.DataDir()) c.Assert(err, gc.IsNil) } func (s *unitUpgraderSuite) TearDownTest(c *gc.C) { if s.resources != nil { s.resources.StopAll() } s.JujuConnSuite.TearDownTest(c) } func (s *unitUpgraderSuite) TestWatchAPIVersionNothing(c *gc.C) { // Not an error to watch nothing results, err := s.upgrader.WatchAPIVersion(params.Entities{}) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 0) } func (s *unitUpgraderSuite) TestWatchAPIVersion(c *gc.C) { args := params.Entities{ Entities: []params.Entity{{Tag: s.rawUnit.Tag()}}, } results, err := s.upgrader.WatchAPIVersion(args) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) c.Check(results.Results[0].NotifyWatcherId, gc.Not(gc.Equals), "") c.Check(results.Results[0].Error, gc.IsNil) resource := s.resources.Get(results.Results[0].NotifyWatcherId) c.Check(resource, gc.NotNil) w := resource.(state.NotifyWatcher) wc := statetesting.NewNotifyWatcherC(c, s.State, w) wc.AssertNoChange() err = s.rawMachine.SetAgentVersion(version.MustParseBinary("3.4.567.8-quantal-amd64")) c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *unitUpgraderSuite) TestUpgraderAPIRefusesNonUnitAgent(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.MachineAgent = true anAuthorizer.UnitAgent = false anUpgrader, err := upgrader.NewUnitUpgraderAPI(s.State, s.resources, anAuthorizer, "") c.Check(err, gc.NotNil) c.Check(anUpgrader, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *unitUpgraderSuite) TestWatchAPIVersionRefusesWrongAgent(c *gc.C) { // We are a unit agent, but not the one we are trying to track anAuthorizer := s.authorizer anAuthorizer.Tag = "unit-wordpress-12354" anUpgrader, err := upgrader.NewUnitUpgraderAPI(s.State, s.resources, anAuthorizer, "") c.Check(err, gc.IsNil) args := params.Entities{ Entities: []params.Entity{{Tag: s.rawUnit.Tag()}}, } results, err := anUpgrader.WatchAPIVersion(args) // It is not an error to make the request, but the specific item is rejected c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) c.Check(results.Results[0].NotifyWatcherId, gc.Equals, "") c.Assert(results.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *unitUpgraderSuite) TestToolsNothing(c *gc.C) { // Not an error to watch nothing results, err := s.upgrader.Tools(params.Entities{}) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 0) } func (s *unitUpgraderSuite) TestToolsRefusesWrongAgent(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.Tag = "unit-wordpress-12354" anUpgrader, err := upgrader.NewUnitUpgraderAPI(s.State, s.resources, anAuthorizer, "") c.Check(err, gc.IsNil) args := params.Entities{ Entities: []params.Entity{{Tag: s.rawUnit.Tag()}}, } results, err := anUpgrader.Tools(args) // It is not an error to make the request, but the specific item is rejected c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) toolResult := results.Results[0] c.Assert(toolResult.Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *unitUpgraderSuite) TestToolsForAgent(c *gc.C) { agent := params.Entity{Tag: s.rawUnit.Tag()} // The machine must have its existing tools set before we query for the // next tools. This is so that we can grab Arch and Series without // having to pass it in again err := s.rawMachine.SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) args := params.Entities{Entities: []params.Entity{agent}} results, err := s.upgrader.Tools(args) c.Assert(err, gc.IsNil) assertTools := func() { c.Check(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) agentTools := results.Results[0].Tools c.Check(agentTools.Version.Number, gc.DeepEquals, version.Current.Number) c.Assert(agentTools.URL, gc.NotNil) } assertTools() } func (s *unitUpgraderSuite) TestSetToolsNothing(c *gc.C) { // Not an error to watch nothing results, err := s.upgrader.SetTools(params.EntitiesVersion{}) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 0) } func (s *unitUpgraderSuite) TestSetToolsRefusesWrongAgent(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.Tag = "unit-wordpress-12354" anUpgrader, err := upgrader.NewUnitUpgraderAPI(s.State, s.resources, anAuthorizer, "") c.Check(err, gc.IsNil) args := params.EntitiesVersion{ AgentTools: []params.EntityVersion{{ Tag: s.rawUnit.Tag(), Tools: &params.Version{ Version: version.Current, }, }}, } results, err := anUpgrader.SetTools(args) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *unitUpgraderSuite) TestSetTools(c *gc.C) { cur := version.Current _, err := s.rawUnit.AgentTools() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) args := params.EntitiesVersion{ AgentTools: []params.EntityVersion{{ Tag: s.rawUnit.Tag(), Tools: &params.Version{ Version: cur, }}, }, } results, err := s.upgrader.SetTools(args) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) // Check that the new value actually got set, we must Refresh because // it was set on a different Machine object err = s.rawUnit.Refresh() c.Assert(err, gc.IsNil) realTools, err := s.rawUnit.AgentTools() c.Assert(err, gc.IsNil) c.Check(realTools.Version.Arch, gc.Equals, cur.Arch) c.Check(realTools.Version.Series, gc.Equals, cur.Series) c.Check(realTools.Version.Major, gc.Equals, cur.Major) c.Check(realTools.Version.Minor, gc.Equals, cur.Minor) c.Check(realTools.Version.Patch, gc.Equals, cur.Patch) c.Check(realTools.Version.Build, gc.Equals, cur.Build) c.Check(realTools.URL, gc.Equals, "") } func (s *unitUpgraderSuite) TestDesiredVersionNothing(c *gc.C) { // Not an error to watch nothing results, err := s.upgrader.DesiredVersion(params.Entities{}) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 0) } func (s *unitUpgraderSuite) TestDesiredVersionRefusesWrongAgent(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.Tag = "unit-wordpress-12354" anUpgrader, err := upgrader.NewUnitUpgraderAPI(s.State, s.resources, anAuthorizer, "") c.Check(err, gc.IsNil) args := params.Entities{ Entities: []params.Entity{{Tag: s.rawUnit.Tag()}}, } results, err := anUpgrader.DesiredVersion(args) // It is not an error to make the request, but the specific item is rejected c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) toolResult := results.Results[0] c.Assert(toolResult.Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *unitUpgraderSuite) TestDesiredVersionNoticesMixedAgents(c *gc.C) { err := s.rawMachine.SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) args := params.Entities{Entities: []params.Entity{ {Tag: s.rawUnit.Tag()}, {Tag: "unit-wordpress-12345"}, }} results, err := s.upgrader.DesiredVersion(args) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 2) c.Assert(results.Results[0].Error, gc.IsNil) agentVersion := results.Results[0].Version c.Assert(agentVersion, gc.NotNil) c.Check(*agentVersion, gc.DeepEquals, version.Current.Number) c.Assert(results.Results[1].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) c.Assert(results.Results[1].Version, gc.IsNil) } func (s *unitUpgraderSuite) TestDesiredVersionForAgent(c *gc.C) { err := s.rawMachine.SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) args := params.Entities{Entities: []params.Entity{{Tag: s.rawUnit.Tag()}}} results, err := s.upgrader.DesiredVersion(args) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) agentVersion := results.Results[0].Version c.Assert(agentVersion, gc.NotNil) c.Check(*agentVersion, gc.DeepEquals, version.Current.Number) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/upgrader/suite_test.go�����������������0000644�0000153�0000161�00000000410�12321735642�030330� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader_test import ( stdtesting "testing" coretesting "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/upgrader/unitupgrader.go���������������0000644�0000153�0000161�00000013247�12321735776�030675� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader import ( "launchpad.net/juju-core/environs" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/version" ) // UnitUpgraderAPI provides access to the UnitUpgrader API facade. type UnitUpgraderAPI struct { *common.ToolsSetter st *state.State resources *common.Resources authorizer common.Authorizer dataDir string } // NewUnitUpgraderAPI creates a new server-side UnitUpgraderAPI facade. func NewUnitUpgraderAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, dataDir string, ) (*UnitUpgraderAPI, error) { if !authorizer.AuthUnitAgent() { return nil, common.ErrPerm } getCanWrite := func() (common.AuthFunc, error) { return authorizer.AuthOwner, nil } return &UnitUpgraderAPI{ ToolsSetter: common.NewToolsSetter(st, getCanWrite), st: st, resources: resources, authorizer: authorizer, dataDir: dataDir, }, nil } func (u *UnitUpgraderAPI) watchAssignedMachine(unitTag string) (string, error) { machine, err := u.getAssignedMachine(unitTag) if err != nil { return "", err } watch := machine.Watch() // Consume the initial event. Technically, API // calls to Watch 'transmit' the initial event // in the Watch response. But NotifyWatchers // have no state to transmit. if _, ok := <-watch.Changes(); ok { return u.resources.Register(watch), nil } return "", watcher.MustErr(watch) } // WatchAPIVersion starts a watcher to track if there is a new version // of the API that we want to upgrade to. The watcher tracks changes to // the unit's assigned machine since that's where the required agent version is stored. func (u *UnitUpgraderAPI) WatchAPIVersion(args params.Entities) (params.NotifyWatchResults, error) { result := params.NotifyWatchResults{ Results: make([]params.NotifyWatchResult, len(args.Entities)), } for i, agent := range args.Entities { err := common.ErrPerm if u.authorizer.AuthOwner(agent.Tag) { var watcherId string watcherId, err = u.watchAssignedMachine(agent.Tag) if err == nil { result.Results[i].NotifyWatcherId = watcherId } } result.Results[i].Error = common.ServerError(err) } return result, nil } // DesiredVersion reports the Agent Version that we want that unit to be running. // The desired version is what the unit's assigned machine is running. func (u *UnitUpgraderAPI) DesiredVersion(args params.Entities) (params.VersionResults, error) { result := make([]params.VersionResult, len(args.Entities)) if len(args.Entities) == 0 { return params.VersionResults{}, nil } for i, entity := range args.Entities { err := common.ErrPerm if u.authorizer.AuthOwner(entity.Tag) { result[i].Version, err = u.getMachineToolsVersion(entity.Tag) } result[i].Error = common.ServerError(err) } return params.VersionResults{result}, nil } // Tools finds the tools necessary for the given agents. func (u *UnitUpgraderAPI) Tools(args params.Entities) (params.ToolsResults, error) { result := params.ToolsResults{ Results: make([]params.ToolsResult, len(args.Entities)), } for i, entity := range args.Entities { result.Results[i].Error = common.ServerError(common.ErrPerm) if u.authorizer.AuthOwner(entity.Tag) { result.Results[i] = u.getMachineTools(entity.Tag) } } return result, nil } func (u *UnitUpgraderAPI) getAssignedMachine(tag string) (*state.Machine, error) { // Check that we really have a unit tag. _, unitName, err := names.ParseTag(tag, names.UnitTagKind) if err != nil { return nil, common.ErrPerm } unit, err := u.st.Unit(unitName) if err != nil { return nil, common.ErrPerm } id, err := unit.AssignedMachineId() if err != nil { return nil, err } return u.st.Machine(id) } func (u *UnitUpgraderAPI) getMachineTools(tag string) params.ToolsResult { var result params.ToolsResult machine, err := u.getAssignedMachine(tag) if err != nil { result.Error = common.ServerError(err) return result } machineTools, err := machine.AgentTools() if err != nil { result.Error = common.ServerError(err) return result } // For older 1.16 upgrader workers, we need to supply a tools URL since the worker will attempt to // download the tools even though they already have been fetched by the machine agent. Newer upgrader // workers do not have this problem. So to be compatible across all versions, we return the full // tools metadata. // TODO (wallyworld) - remove in 1.20, just return machineTools cfg, err := u.st.EnvironConfig() if err != nil { result.Error = common.ServerError(err) return result } // SSLHostnameVerification defaults to true, so we need to // invert that, for backwards-compatibility (older versions // will have DisableSSLHostnameVerification: false by default). result.DisableSSLHostnameVerification = !cfg.SSLHostnameVerification() env, err := environs.New(cfg) if err != nil { result.Error = common.ServerError(err) return result } agentTools, err := envtools.FindExactTools( env, machineTools.Version.Number, machineTools.Version.Series, machineTools.Version.Arch) if err != nil { result.Error = common.ServerError(err) return result } result.Tools = agentTools return result } func (u *UnitUpgraderAPI) getMachineToolsVersion(tag string) (*version.Number, error) { machine, err := u.getAssignedMachine(tag) if err != nil { return nil, err } machineTools, err := machine.AgentTools() if err != nil { return nil, err } return &machineTools.Version.Number, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/upgrader/upgrader_test.go��������������0000644�0000153�0000161�00000022426�12321735776�031033� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/errors" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" "launchpad.net/juju-core/state/apiserver/upgrader" statetesting "launchpad.net/juju-core/state/testing" "launchpad.net/juju-core/version" ) type upgraderSuite struct { jujutesting.JujuConnSuite // These are raw State objects. Use them for setup and assertions, but // should never be touched by the API calls themselves rawMachine *state.Machine upgrader *upgrader.UpgraderAPI resources *common.Resources authorizer apiservertesting.FakeAuthorizer } var _ = gc.Suite(&upgraderSuite{}) func (s *upgraderSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.resources = common.NewResources() // Create a machine to work with var err error s.rawMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // The default auth is as the machine agent s.authorizer = apiservertesting.FakeAuthorizer{ Tag: s.rawMachine.Tag(), LoggedIn: true, MachineAgent: true, } s.upgrader, err = upgrader.NewUpgraderAPI(s.State, s.resources, s.authorizer) c.Assert(err, gc.IsNil) } func (s *upgraderSuite) TearDownTest(c *gc.C) { if s.resources != nil { s.resources.StopAll() } s.JujuConnSuite.TearDownTest(c) } func (s *upgraderSuite) TestWatchAPIVersionNothing(c *gc.C) { // Not an error to watch nothing results, err := s.upgrader.WatchAPIVersion(params.Entities{}) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 0) } func (s *upgraderSuite) TestWatchAPIVersion(c *gc.C) { args := params.Entities{ Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}, } results, err := s.upgrader.WatchAPIVersion(args) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) c.Check(results.Results[0].NotifyWatcherId, gc.Not(gc.Equals), "") c.Check(results.Results[0].Error, gc.IsNil) resource := s.resources.Get(results.Results[0].NotifyWatcherId) c.Check(resource, gc.NotNil) w := resource.(state.NotifyWatcher) wc := statetesting.NewNotifyWatcherC(c, s.State, w) wc.AssertNoChange() err = statetesting.SetAgentVersion(s.State, version.MustParse("3.4.567.8")) c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *upgraderSuite) TestUpgraderAPIRefusesNonMachineAgent(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.UnitAgent = true anAuthorizer.MachineAgent = false anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer) c.Check(err, gc.NotNil) c.Check(anUpgrader, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *upgraderSuite) TestWatchAPIVersionRefusesWrongAgent(c *gc.C) { // We are a machine agent, but not the one we are trying to track anAuthorizer := s.authorizer anAuthorizer.Tag = "machine-12354" anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer) c.Check(err, gc.IsNil) args := params.Entities{ Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}, } results, err := anUpgrader.WatchAPIVersion(args) // It is not an error to make the request, but the specific item is rejected c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) c.Check(results.Results[0].NotifyWatcherId, gc.Equals, "") c.Assert(results.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *upgraderSuite) TestToolsNothing(c *gc.C) { // Not an error to watch nothing results, err := s.upgrader.Tools(params.Entities{}) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 0) } func (s *upgraderSuite) TestToolsRefusesWrongAgent(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.Tag = "machine-12354" anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer) c.Check(err, gc.IsNil) args := params.Entities{ Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}, } results, err := anUpgrader.Tools(args) // It is not an error to make the request, but the specific item is rejected c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) toolResult := results.Results[0] c.Assert(toolResult.Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *upgraderSuite) TestToolsForAgent(c *gc.C) { cur := version.Current agent := params.Entity{Tag: s.rawMachine.Tag()} // The machine must have its existing tools set before we query for the // next tools. This is so that we can grab Arch and Series without // having to pass it in again err := s.rawMachine.SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) args := params.Entities{Entities: []params.Entity{agent}} results, err := s.upgrader.Tools(args) c.Assert(err, gc.IsNil) assertTools := func() { c.Check(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) agentTools := results.Results[0].Tools c.Check(agentTools.URL, gc.Not(gc.Equals), "") c.Check(agentTools.Version, gc.DeepEquals, cur) } assertTools() c.Check(results.Results[0].DisableSSLHostnameVerification, jc.IsFalse) envtesting.SetSSLHostnameVerification(c, s.State, false) results, err = s.upgrader.Tools(args) c.Assert(err, gc.IsNil) assertTools() c.Check(results.Results[0].DisableSSLHostnameVerification, jc.IsTrue) } func (s *upgraderSuite) TestSetToolsNothing(c *gc.C) { // Not an error to watch nothing results, err := s.upgrader.SetTools(params.EntitiesVersion{}) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 0) } func (s *upgraderSuite) TestSetToolsRefusesWrongAgent(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.Tag = "machine-12354" anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer) c.Check(err, gc.IsNil) args := params.EntitiesVersion{ AgentTools: []params.EntityVersion{{ Tag: s.rawMachine.Tag(), Tools: &params.Version{ Version: version.Current, }, }}, } results, err := anUpgrader.SetTools(args) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *upgraderSuite) TestSetTools(c *gc.C) { cur := version.Current _, err := s.rawMachine.AgentTools() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) args := params.EntitiesVersion{ AgentTools: []params.EntityVersion{{ Tag: s.rawMachine.Tag(), Tools: &params.Version{ Version: cur, }}, }, } results, err := s.upgrader.SetTools(args) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) // Check that the new value actually got set, we must Refresh because // it was set on a different Machine object err = s.rawMachine.Refresh() c.Assert(err, gc.IsNil) realTools, err := s.rawMachine.AgentTools() c.Assert(err, gc.IsNil) c.Check(realTools.Version.Arch, gc.Equals, cur.Arch) c.Check(realTools.Version.Series, gc.Equals, cur.Series) c.Check(realTools.Version.Major, gc.Equals, cur.Major) c.Check(realTools.Version.Minor, gc.Equals, cur.Minor) c.Check(realTools.Version.Patch, gc.Equals, cur.Patch) c.Check(realTools.Version.Build, gc.Equals, cur.Build) c.Check(realTools.URL, gc.Equals, "") } func (s *upgraderSuite) TestDesiredVersionNothing(c *gc.C) { // Not an error to watch nothing results, err := s.upgrader.DesiredVersion(params.Entities{}) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 0) } func (s *upgraderSuite) TestDesiredVersionRefusesWrongAgent(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.Tag = "machine-12354" anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer) c.Check(err, gc.IsNil) args := params.Entities{ Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}, } results, err := anUpgrader.DesiredVersion(args) // It is not an error to make the request, but the specific item is rejected c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) toolResult := results.Results[0] c.Assert(toolResult.Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *upgraderSuite) TestDesiredVersionNoticesMixedAgents(c *gc.C) { args := params.Entities{Entities: []params.Entity{ {Tag: s.rawMachine.Tag()}, {Tag: "machine-12345"}, }} results, err := s.upgrader.DesiredVersion(args) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 2) c.Assert(results.Results[0].Error, gc.IsNil) agentVersion := results.Results[0].Version c.Assert(agentVersion, gc.NotNil) c.Check(*agentVersion, gc.DeepEquals, version.Current.Number) c.Assert(results.Results[1].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) c.Assert(results.Results[1].Version, gc.IsNil) } func (s *upgraderSuite) TestDesiredVersionForAgent(c *gc.C) { args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} results, err := s.upgrader.DesiredVersion(args) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) agentVersion := results.Results[0].Version c.Assert(agentVersion, gc.NotNil) c.Check(*agentVersion, gc.DeepEquals, version.Current.Number) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/��������������������������������0000755�0000153�0000161�00000000000�12321736000�025250� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/client_test.go������������������0000644�0000153�0000161�00000216466�12321735776�030156� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client_test import ( "fmt" "net/url" "strconv" "strings" "sync" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/manual" envstorage "launchpad.net/juju-core/environs/storage" ttesting "launchpad.net/juju-core/environs/tools/testing" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/client" "launchpad.net/juju-core/state/statecmd" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) type clientSuite struct { baseSuite } var _ = gc.Suite(&clientSuite{}) func (s *clientSuite) TestClientStatus(c *gc.C) { s.setUpScenario(c) status, err := s.APIState.Client().Status(nil) c.Assert(err, gc.IsNil) c.Assert(status, jc.DeepEquals, scenarioStatus) } func (s *clientSuite) TestCompatibleSettingsParsing(c *gc.C) { // Test the exported settings parsing in a compatible way. s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) service, err := s.State.Service("dummy") c.Assert(err, gc.IsNil) ch, _, err := service.Charm() c.Assert(err, gc.IsNil) c.Assert(ch.URL().String(), gc.Equals, "local:quantal/dummy-1") // Empty string will be returned as nil. options := map[string]string{ "title": "foobar", "username": "", } settings, err := client.ParseSettingsCompatible(ch, options) c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{ "title": "foobar", "username": nil, }) // Illegal settings lead to an error. options = map[string]string{ "yummy": "didgeridoo", } settings, err = client.ParseSettingsCompatible(ch, options) c.Assert(err, gc.ErrorMatches, `unknown option "yummy"`) } func (s *clientSuite) TestClientServiceSet(c *gc.C) { dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) err := s.APIState.Client().ServiceSet("dummy", map[string]string{ "title": "foobar", "username": "user name", }) c.Assert(err, gc.IsNil) settings, err := dummy.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{ "title": "foobar", "username": "user name", }) err = s.APIState.Client().ServiceSet("dummy", map[string]string{ "title": "barfoo", "username": "", }) c.Assert(err, gc.IsNil) settings, err = dummy.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{ "title": "barfoo", "username": "", }) } func (s *clientSuite) TestClientServerUnset(c *gc.C) { dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) err := s.APIState.Client().ServiceSet("dummy", map[string]string{ "title": "foobar", "username": "user name", }) c.Assert(err, gc.IsNil) settings, err := dummy.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{ "title": "foobar", "username": "user name", }) err = s.APIState.Client().ServiceUnset("dummy", []string{"username"}) c.Assert(err, gc.IsNil) settings, err = dummy.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{ "title": "foobar", }) } func (s *clientSuite) TestClientServiceSetYAML(c *gc.C) { dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) err := s.APIState.Client().ServiceSetYAML("dummy", "dummy:\n title: foobar\n username: user name\n") c.Assert(err, gc.IsNil) settings, err := dummy.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{ "title": "foobar", "username": "user name", }) err = s.APIState.Client().ServiceSetYAML("dummy", "dummy:\n title: barfoo\n username: \n") c.Assert(err, gc.IsNil) settings, err = dummy.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{ "title": "barfoo", }) } var clientAddServiceUnitsTests = []struct { about string service string // if not set, defaults to 'dummy' expected []string to string err string }{ { about: "returns unit names", expected: []string{"dummy/0", "dummy/1", "dummy/2"}, }, { about: "fails trying to add zero units", err: "must add at least one unit", }, { about: "cannot mix to when adding multiple units", err: "cannot use NumUnits with ToMachineSpec", expected: []string{"dummy/0", "dummy/1"}, to: "0", }, { // Note: chained-state, we add 1 unit here, but the 3 units // from the first condition still exist about: "force the unit onto bootstrap machine", expected: []string{"dummy/3"}, to: "0", }, { about: "unknown service name", service: "unknown-service", err: `service "unknown-service" not found`, }, } func (s *clientSuite) TestClientAddServiceUnits(c *gc.C) { s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) for i, t := range clientAddServiceUnitsTests { c.Logf("test %d. %s", i, t.about) serviceName := t.service if serviceName == "" { serviceName = "dummy" } units, err := s.APIState.Client().AddServiceUnits(serviceName, len(t.expected), t.to) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) continue } c.Assert(err, gc.IsNil) c.Assert(units, gc.DeepEquals, t.expected) } // Test that we actually assigned the unit to machine 0 forcedUnit, err := s.BackingState.Unit("dummy/3") c.Assert(err, gc.IsNil) assignedMachine, err := forcedUnit.AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(assignedMachine, gc.Equals, "0") } var clientCharmInfoTests = []struct { about string url string err string }{ { about: "retrieves charm info", url: "local:quantal/wordpress-3", }, { about: "invalid URL", url: "not-valid", err: "charm url series is not resolved", }, { about: "invalid schema", url: "not-valid:your-arguments", err: `charm URL has invalid schema: "not-valid:your-arguments"`, }, { about: "unknown charm", url: "cs:missing/one-1", err: `charm "cs:missing/one-1" not found`, }, } func (s *clientSuite) TestClientCharmInfo(c *gc.C) { // Use wordpress for tests so that we can compare Provides and Requires. charm := s.AddTestingCharm(c, "wordpress") for i, t := range clientCharmInfoTests { c.Logf("test %d. %s", i, t.about) info, err := s.APIState.Client().CharmInfo(t.url) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) continue } c.Assert(err, gc.IsNil) expected := &api.CharmInfo{ Revision: charm.Revision(), URL: charm.URL().String(), Config: charm.Config(), Meta: charm.Meta(), } c.Assert(info, gc.DeepEquals, expected) } } func (s *clientSuite) TestClientEnvironmentInfo(c *gc.C) { conf, _ := s.State.EnvironConfig() info, err := s.APIState.Client().EnvironmentInfo() c.Assert(err, gc.IsNil) env, err := s.State.Environment() c.Assert(err, gc.IsNil) c.Assert(info.DefaultSeries, gc.Equals, config.PreferredSeries(conf)) c.Assert(info.ProviderType, gc.Equals, conf.Type()) c.Assert(info.Name, gc.Equals, conf.Name()) c.Assert(info.UUID, gc.Equals, env.UUID()) } var clientAnnotationsTests = []struct { about string initial map[string]string input map[string]string expected map[string]string err string }{ { about: "test setting an annotation", input: map[string]string{"mykey": "myvalue"}, expected: map[string]string{"mykey": "myvalue"}, }, { about: "test setting multiple annotations", input: map[string]string{"key1": "value1", "key2": "value2"}, expected: map[string]string{"key1": "value1", "key2": "value2"}, }, { about: "test overriding annotations", initial: map[string]string{"mykey": "myvalue"}, input: map[string]string{"mykey": "another-value"}, expected: map[string]string{"mykey": "another-value"}, }, { about: "test setting an invalid annotation", input: map[string]string{"invalid.key": "myvalue"}, err: `cannot update annotations on .*: invalid key "invalid.key"`, }, } func (s *clientSuite) TestClientAnnotations(c *gc.C) { // Set up entities. service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) unit, err := service.AddUnit() c.Assert(err, gc.IsNil) machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) environment, err := s.State.Environment() c.Assert(err, gc.IsNil) type taggedAnnotator interface { state.Annotator state.Entity } entities := []taggedAnnotator{service, unit, machine, environment} for i, t := range clientAnnotationsTests { for _, entity := range entities { id := entity.Tag() c.Logf("test %d. %s. entity %s", i, t.about, id) // Set initial entity annotations. err := entity.SetAnnotations(t.initial) c.Assert(err, gc.IsNil) // Add annotations using the API call. err = s.APIState.Client().SetAnnotations(id, t.input) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) continue } // Check annotations are correctly set. dbann, err := entity.Annotations() c.Assert(err, gc.IsNil) c.Assert(dbann, gc.DeepEquals, t.expected) // Retrieve annotations using the API call. ann, err := s.APIState.Client().GetAnnotations(id) c.Assert(err, gc.IsNil) // Check annotations are correctly returned. c.Assert(ann, gc.DeepEquals, dbann) // Clean up annotations on the current entity. cleanup := make(map[string]string) for key := range dbann { cleanup[key] = "" } err = entity.SetAnnotations(cleanup) c.Assert(err, gc.IsNil) } } } func (s *clientSuite) TestClientAnnotationsBadEntity(c *gc.C) { bad := []string{"", "machine", "-foo", "foo-", "---", "machine-jim", "unit-123", "unit-foo", "service-", "service-foo/bar"} expected := `".*" is not a valid( [a-z]+)? tag` for _, id := range bad { err := s.APIState.Client().SetAnnotations(id, map[string]string{"mykey": "myvalue"}) c.Assert(err, gc.ErrorMatches, expected) _, err = s.APIState.Client().GetAnnotations(id) c.Assert(err, gc.ErrorMatches, expected) } } var serviceExposeTests = []struct { about string service string err string exposed bool }{ { about: "unknown service name", service: "unknown-service", err: `service "unknown-service" not found`, }, { about: "expose a service", service: "dummy-service", exposed: true, }, { about: "expose an already exposed service", service: "exposed-service", exposed: true, }, } func (s *clientSuite) TestClientServiceExpose(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") serviceNames := []string{"dummy-service", "exposed-service"} svcs := make([]*state.Service, len(serviceNames)) var err error for i, name := range serviceNames { svcs[i] = s.AddTestingService(c, name, charm) c.Assert(svcs[i].IsExposed(), gc.Equals, false) } err = svcs[1].SetExposed() c.Assert(err, gc.IsNil) c.Assert(svcs[1].IsExposed(), gc.Equals, true) for i, t := range serviceExposeTests { c.Logf("test %d. %s", i, t.about) err = s.APIState.Client().ServiceExpose(t.service) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) } else { c.Assert(err, gc.IsNil) service, err := s.State.Service(t.service) c.Assert(err, gc.IsNil) c.Assert(service.IsExposed(), gc.Equals, t.exposed) } } } var serviceUnexposeTests = []struct { about string service string err string initial bool expected bool }{ { about: "unknown service name", service: "unknown-service", err: `service "unknown-service" not found`, }, { about: "unexpose a service", service: "dummy-service", initial: true, expected: false, }, { about: "unexpose an already unexposed service", service: "dummy-service", initial: false, expected: false, }, } func (s *clientSuite) TestClientServiceUnexpose(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") for i, t := range serviceUnexposeTests { c.Logf("test %d. %s", i, t.about) svc := s.AddTestingService(c, "dummy-service", charm) if t.initial { svc.SetExposed() } c.Assert(svc.IsExposed(), gc.Equals, t.initial) err := s.APIState.Client().ServiceUnexpose(t.service) if t.err == "" { c.Assert(err, gc.IsNil) svc.Refresh() c.Assert(svc.IsExposed(), gc.Equals, t.expected) } else { c.Assert(err, gc.ErrorMatches, t.err) } err = svc.Destroy() c.Assert(err, gc.IsNil) } } var serviceDestroyTests = []struct { about string service string err string }{ { about: "unknown service name", service: "unknown-service", err: `service "unknown-service" not found`, }, { about: "destroy a service", service: "dummy-service", }, { about: "destroy an already destroyed service", service: "dummy-service", err: `service "dummy-service" not found`, }, } func (s *clientSuite) TestClientServiceDestroy(c *gc.C) { s.AddTestingService(c, "dummy-service", s.AddTestingCharm(c, "dummy")) for i, t := range serviceDestroyTests { c.Logf("test %d. %s", i, t.about) err := s.APIState.Client().ServiceDestroy(t.service) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) } else { c.Assert(err, gc.IsNil) } } // Now do ServiceDestroy on a service with units. Destroy will // cause the service to be not-Alive, but will not remove its // document. s.setUpScenario(c) serviceName := "wordpress" service, err := s.State.Service(serviceName) c.Assert(err, gc.IsNil) err = s.APIState.Client().ServiceDestroy(serviceName) c.Assert(err, gc.IsNil) err = service.Refresh() c.Assert(err, gc.IsNil) c.Assert(service.Life(), gc.Not(gc.Equals), state.Alive) } func assertLife(c *gc.C, entity state.Living, life state.Life) { err := entity.Refresh() c.Assert(err, gc.IsNil) c.Assert(entity.Life(), gc.Equals, life) } func assertRemoved(c *gc.C, entity state.Living) { err := entity.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *clientSuite) setupDestroyMachinesTest(c *gc.C) (*state.Machine, *state.Machine, *state.Machine, *state.Unit) { m0, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) m1, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) m2, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) sch := s.AddTestingCharm(c, "wordpress") wordpress := s.AddTestingService(c, "wordpress", sch) u, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) err = u.AssignToMachine(m1) c.Assert(err, gc.IsNil) return m0, m1, m2, u } func (s *clientSuite) TestDestroyMachines(c *gc.C) { m0, m1, m2, u := s.setupDestroyMachinesTest(c) err := s.APIState.Client().DestroyMachines("0", "1", "2") c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment; machine 1 has unit "wordpress/0" assigned`) assertLife(c, m0, state.Alive) assertLife(c, m1, state.Alive) assertLife(c, m2, state.Dying) err = u.UnassignFromMachine() c.Assert(err, gc.IsNil) err = s.APIState.Client().DestroyMachines("0", "1", "2") c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment`) assertLife(c, m0, state.Alive) assertLife(c, m1, state.Dying) assertLife(c, m2, state.Dying) } func (s *clientSuite) TestForceDestroyMachines(c *gc.C) { m0, m1, m2, u := s.setupDestroyMachinesTest(c) err := s.APIState.Client().ForceDestroyMachines("0", "1", "2") c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment`) assertLife(c, m0, state.Alive) assertLife(c, m1, state.Alive) assertLife(c, m2, state.Alive) assertLife(c, u, state.Alive) err = s.State.Cleanup() c.Assert(err, gc.IsNil) assertLife(c, m0, state.Alive) assertLife(c, m1, state.Dead) assertLife(c, m2, state.Dead) assertRemoved(c, u) } func (s *clientSuite) TestDestroyPrincipalUnits(c *gc.C) { wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) units := make([]*state.Unit, 5) for i := range units { unit, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) err = unit.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) units[i] = unit } // Destroy 2 of them; check they become Dying. err := s.APIState.Client().DestroyServiceUnits("wordpress/0", "wordpress/1") c.Assert(err, gc.IsNil) assertLife(c, units[0], state.Dying) assertLife(c, units[1], state.Dying) // Try to destroy an Alive one and a Dying one; check // it destroys the Alive one and ignores the Dying one. err = s.APIState.Client().DestroyServiceUnits("wordpress/2", "wordpress/0") c.Assert(err, gc.IsNil) assertLife(c, units[2], state.Dying) // Try to destroy an Alive one along with a nonexistent one; check that // the valid instruction is followed but the invalid one is warned about. err = s.APIState.Client().DestroyServiceUnits("boojum/123", "wordpress/3") c.Assert(err, gc.ErrorMatches, `some units were not destroyed: unit "boojum/123" does not exist`) assertLife(c, units[3], state.Dying) // Make one Dead, and destroy an Alive one alongside it; check no errors. wp0, err := s.State.Unit("wordpress/0") c.Assert(err, gc.IsNil) err = wp0.EnsureDead() c.Assert(err, gc.IsNil) err = s.APIState.Client().DestroyServiceUnits("wordpress/0", "wordpress/4") c.Assert(err, gc.IsNil) assertLife(c, units[0], state.Dead) assertLife(c, units[4], state.Dying) } func (s *clientSuite) TestDestroySubordinateUnits(c *gc.C) { wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) wordpress0, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"logging", "wordpress"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) ru, err := rel.Unit(wordpress0) c.Assert(err, gc.IsNil) err = ru.EnterScope(nil) c.Assert(err, gc.IsNil) logging0, err := s.State.Unit("logging/0") c.Assert(err, gc.IsNil) // Try to destroy the subordinate alone; check it fails. err = s.APIState.Client().DestroyServiceUnits("logging/0") c.Assert(err, gc.ErrorMatches, `no units were destroyed: unit "logging/0" is a subordinate`) assertLife(c, logging0, state.Alive) // Try to destroy the principal and the subordinate together; check it warns // about the subordinate, but destroys the one it can. (The principal unit // agent will be resposible for destroying the subordinate.) err = s.APIState.Client().DestroyServiceUnits("wordpress/0", "logging/0") c.Assert(err, gc.ErrorMatches, `some units were not destroyed: unit "logging/0" is a subordinate`) assertLife(c, wordpress0, state.Dying) assertLife(c, logging0, state.Alive) } func (s *clientSuite) testClientUnitResolved(c *gc.C, retry bool, expectedResolvedMode state.ResolvedMode) { // Setup: s.setUpScenario(c) u, err := s.State.Unit("wordpress/0") c.Assert(err, gc.IsNil) err = u.SetStatus(params.StatusError, "gaaah", nil) c.Assert(err, gc.IsNil) // Code under test: err = s.APIState.Client().Resolved("wordpress/0", retry) c.Assert(err, gc.IsNil) // Freshen the unit's state. err = u.Refresh() c.Assert(err, gc.IsNil) // And now the actual test assertions: we set the unit as resolved via // the API so it should have a resolved mode set. mode := u.Resolved() c.Assert(mode, gc.Equals, expectedResolvedMode) } func (s *clientSuite) TestClientUnitResolved(c *gc.C) { s.testClientUnitResolved(c, false, state.ResolvedNoHooks) } func (s *clientSuite) TestClientUnitResolvedRetry(c *gc.C) { s.testClientUnitResolved(c, true, state.ResolvedRetryHooks) } func (s *clientSuite) TestClientServiceDeployCharmErrors(c *gc.C) { _, restore := makeMockCharmStore() defer restore() for url, expect := range map[string]string{ "wordpress": "charm url series is not resolved", "cs:wordpress": "charm url series is not resolved", "cs:precise/wordpress": "charm url must include revision", "cs:precise/wordpress-999999": `cannot download charm ".*": charm not found in mock store: cs:precise/wordpress-999999`, } { c.Logf("test %s", url) err := s.APIState.Client().ServiceDeploy( url, "service", 1, "", constraints.Value{}, "", ) c.Check(err, gc.ErrorMatches, expect) _, err = s.State.Service("service") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } } func (s *clientSuite) TestClientServiceDeployWithNetworks(c *gc.C) { store, restore := makeMockCharmStore() defer restore() curl, bundle := addCharm(c, store, "dummy") mem4g := constraints.MustParse("mem=4G") err := s.APIState.Client().ServiceDeployWithNetworks( curl.String(), "service", 3, "", mem4g, "", []string{"net1", "net2"}, []string{"net3"}, ) c.Assert(err, gc.IsNil) service := s.assertPrincipalDeployed(c, "service", curl, false, bundle, mem4g) include, exclude, err := service.Networks() c.Assert(err, gc.IsNil) c.Assert(include, gc.DeepEquals, []string{"net1", "net2"}) c.Assert(exclude, gc.DeepEquals, []string{"net3"}) } func (s *clientSuite) assertPrincipalDeployed(c *gc.C, serviceName string, curl *charm.URL, forced bool, bundle charm.Charm, cons constraints.Value) *state.Service { service, err := s.State.Service(serviceName) c.Assert(err, gc.IsNil) charm, force, err := service.Charm() c.Assert(err, gc.IsNil) c.Assert(force, gc.Equals, forced) c.Assert(charm.URL(), gc.DeepEquals, curl) c.Assert(charm.Meta(), gc.DeepEquals, bundle.Meta()) c.Assert(charm.Config(), gc.DeepEquals, bundle.Config()) serviceCons, err := service.Constraints() c.Assert(err, gc.IsNil) c.Assert(serviceCons, gc.DeepEquals, cons) units, err := service.AllUnits() c.Assert(err, gc.IsNil) for _, unit := range units { mid, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) machine, err := s.State.Machine(mid) c.Assert(err, gc.IsNil) machineCons, err := machine.Constraints() c.Assert(err, gc.IsNil) c.Assert(machineCons, gc.DeepEquals, cons) } return service } func (s *clientSuite) TestClientServiceDeployPrincipal(c *gc.C) { // TODO(fwereade): test ToMachineSpec directly on srvClient, when we // manage to extract it as a package and can thus do it conveniently. store, restore := makeMockCharmStore() defer restore() curl, bundle := addCharm(c, store, "dummy") mem4g := constraints.MustParse("mem=4G") err := s.APIState.Client().ServiceDeploy( curl.String(), "service", 3, "", mem4g, "", ) c.Assert(err, gc.IsNil) s.assertPrincipalDeployed(c, "service", curl, false, bundle, mem4g) } func (s *clientSuite) TestClientServiceDeploySubordinate(c *gc.C) { store, restore := makeMockCharmStore() defer restore() curl, bundle := addCharm(c, store, "logging") err := s.APIState.Client().ServiceDeploy( curl.String(), "service-name", 0, "", constraints.Value{}, "", ) service, err := s.State.Service("service-name") c.Assert(err, gc.IsNil) charm, force, err := service.Charm() c.Assert(err, gc.IsNil) c.Assert(force, gc.Equals, false) c.Assert(charm.URL(), gc.DeepEquals, curl) c.Assert(charm.Meta(), gc.DeepEquals, bundle.Meta()) c.Assert(charm.Config(), gc.DeepEquals, bundle.Config()) units, err := service.AllUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 0) } func (s *clientSuite) TestClientServiceDeployConfig(c *gc.C) { // TODO(fwereade): test Config/ConfigYAML handling directly on srvClient. // Can't be done cleanly until it's extracted similarly to Machiner. store, restore := makeMockCharmStore() defer restore() curl, _ := addCharm(c, store, "dummy") err := s.APIState.Client().ServiceDeploy( curl.String(), "service-name", 1, "service-name:\n username: fred", constraints.Value{}, "", ) c.Assert(err, gc.IsNil) service, err := s.State.Service("service-name") c.Assert(err, gc.IsNil) settings, err := service.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{"username": "fred"}) } func (s *clientSuite) TestClientServiceDeployConfigError(c *gc.C) { // TODO(fwereade): test Config/ConfigYAML handling directly on srvClient. // Can't be done cleanly until it's extracted similarly to Machiner. store, restore := makeMockCharmStore() defer restore() curl, _ := addCharm(c, store, "dummy") err := s.APIState.Client().ServiceDeploy( curl.String(), "service-name", 1, "service-name:\n skill-level: fred", constraints.Value{}, "", ) c.Assert(err, gc.ErrorMatches, `option "skill-level" expected int, got "fred"`) _, err = s.State.Service("service-name") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *clientSuite) TestClientServiceDeployToMachine(c *gc.C) { store, restore := makeMockCharmStore() defer restore() curl, bundle := addCharm(c, store, "dummy") machine, err := s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, gc.IsNil) err = s.APIState.Client().ServiceDeploy( curl.String(), "service-name", 1, "service-name:\n username: fred", constraints.Value{}, machine.Id(), ) c.Assert(err, gc.IsNil) service, err := s.State.Service("service-name") c.Assert(err, gc.IsNil) charm, force, err := service.Charm() c.Assert(err, gc.IsNil) c.Assert(force, gc.Equals, false) c.Assert(charm.URL(), gc.DeepEquals, curl) c.Assert(charm.Meta(), gc.DeepEquals, bundle.Meta()) c.Assert(charm.Config(), gc.DeepEquals, bundle.Config()) units, err := service.AllUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, 1) mid, err := units[0].AssignedMachineId() c.Assert(err, gc.IsNil) c.Assert(mid, gc.Equals, machine.Id()) } func (s *clientSuite) deployServiceForTests(c *gc.C, store *coretesting.MockCharmStore) { curl, _ := addCharm(c, store, "dummy") err := s.APIState.Client().ServiceDeploy(curl.String(), "service", 1, "", constraints.Value{}, "", ) c.Assert(err, gc.IsNil) } func (s *clientSuite) checkClientServiceUpdateSetCharm(c *gc.C, forceCharmUrl bool) { store, restore := makeMockCharmStore() defer restore() s.deployServiceForTests(c, store) addCharm(c, store, "wordpress") // Update the charm for the service. args := params.ServiceUpdate{ ServiceName: "service", CharmUrl: "cs:precise/wordpress-3", ForceCharmUrl: forceCharmUrl, } err := s.APIState.Client().ServiceUpdate(args) c.Assert(err, gc.IsNil) // Ensure the charm has been updated and and the force flag correctly set. service, err := s.State.Service("service") c.Assert(err, gc.IsNil) ch, force, err := service.Charm() c.Assert(err, gc.IsNil) c.Assert(ch.URL().String(), gc.Equals, "cs:precise/wordpress-3") c.Assert(force, gc.Equals, forceCharmUrl) } func (s *clientSuite) TestClientServiceUpdateSetCharm(c *gc.C) { s.checkClientServiceUpdateSetCharm(c, false) } func (s *clientSuite) TestClientServiceUpdateForceSetCharm(c *gc.C) { s.checkClientServiceUpdateSetCharm(c, true) } func (s *clientSuite) TestClientServiceUpdateSetCharmErrors(c *gc.C) { _, restore := makeMockCharmStore() defer restore() s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) for charmUrl, expect := range map[string]string{ "wordpress": "charm url series is not resolved", "cs:wordpress": "charm url series is not resolved", "cs:precise/wordpress": "charm url must include revision", "cs:precise/wordpress-999999": `cannot download charm ".*": charm not found in mock store: cs:precise/wordpress-999999`, } { c.Logf("test %s", charmUrl) args := params.ServiceUpdate{ ServiceName: "wordpress", CharmUrl: charmUrl, } err := s.APIState.Client().ServiceUpdate(args) c.Check(err, gc.ErrorMatches, expect) } } func (s *clientSuite) TestClientServiceUpdateSetMinUnits(c *gc.C) { service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) // Set minimum units for the service. minUnits := 2 args := params.ServiceUpdate{ ServiceName: "dummy", MinUnits: &minUnits, } err := s.APIState.Client().ServiceUpdate(args) c.Assert(err, gc.IsNil) // Ensure the minimum number of units has been set. c.Assert(service.Refresh(), gc.IsNil) c.Assert(service.MinUnits(), gc.Equals, minUnits) } func (s *clientSuite) TestClientServiceUpdateSetMinUnitsError(c *gc.C) { service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) // Set a negative minimum number of units for the service. minUnits := -1 args := params.ServiceUpdate{ ServiceName: "dummy", MinUnits: &minUnits, } err := s.APIState.Client().ServiceUpdate(args) c.Assert(err, gc.ErrorMatches, `cannot set minimum units for service "dummy": cannot set a negative minimum number of units`) // Ensure the minimum number of units has not been set. c.Assert(service.Refresh(), gc.IsNil) c.Assert(service.MinUnits(), gc.Equals, 0) } func (s *clientSuite) TestClientServiceUpdateSetSettingsStrings(c *gc.C) { service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) // Update settings for the service. args := params.ServiceUpdate{ ServiceName: "dummy", SettingsStrings: map[string]string{"title": "s-title", "username": "s-user"}, } err := s.APIState.Client().ServiceUpdate(args) c.Assert(err, gc.IsNil) // Ensure the settings have been correctly updated. expected := charm.Settings{"title": "s-title", "username": "s-user"} obtained, err := service.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, expected) } func (s *clientSuite) TestClientServiceUpdateSetSettingsYAML(c *gc.C) { service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) // Update settings for the service. args := params.ServiceUpdate{ ServiceName: "dummy", SettingsYAML: "dummy:\n title: y-title\n username: y-user", } err := s.APIState.Client().ServiceUpdate(args) c.Assert(err, gc.IsNil) // Ensure the settings have been correctly updated. expected := charm.Settings{"title": "y-title", "username": "y-user"} obtained, err := service.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, expected) } func (s *clientSuite) TestClientServiceUpdateSetConstraints(c *gc.C) { service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) // Update constraints for the service. cons, err := constraints.Parse("mem=4096", "cpu-cores=2") c.Assert(err, gc.IsNil) args := params.ServiceUpdate{ ServiceName: "dummy", Constraints: &cons, } err = s.APIState.Client().ServiceUpdate(args) c.Assert(err, gc.IsNil) // Ensure the constraints have been correctly updated. obtained, err := service.Constraints() c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, cons) } func (s *clientSuite) TestClientServiceUpdateAllParams(c *gc.C) { store, restore := makeMockCharmStore() defer restore() s.deployServiceForTests(c, store) addCharm(c, store, "wordpress") // Update all the service attributes. minUnits := 3 cons, err := constraints.Parse("mem=4096", "cpu-cores=2") c.Assert(err, gc.IsNil) args := params.ServiceUpdate{ ServiceName: "service", CharmUrl: "cs:precise/wordpress-3", ForceCharmUrl: true, MinUnits: &minUnits, SettingsStrings: map[string]string{"blog-title": "string-title"}, SettingsYAML: "service:\n blog-title: yaml-title\n", Constraints: &cons, } err = s.APIState.Client().ServiceUpdate(args) c.Assert(err, gc.IsNil) // Ensure the service has been correctly updated. service, err := s.State.Service("service") c.Assert(err, gc.IsNil) // Check the charm. ch, force, err := service.Charm() c.Assert(err, gc.IsNil) c.Assert(ch.URL().String(), gc.Equals, "cs:precise/wordpress-3") c.Assert(force, gc.Equals, true) // Check the minimum number of units. c.Assert(service.MinUnits(), gc.Equals, minUnits) // Check the settings: also ensure the YAML settings take precedence // over strings ones. expectedSettings := charm.Settings{"blog-title": "yaml-title"} obtainedSettings, err := service.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(obtainedSettings, gc.DeepEquals, expectedSettings) // Check the constraints. obtainedConstraints, err := service.Constraints() c.Assert(err, gc.IsNil) c.Assert(obtainedConstraints, gc.DeepEquals, cons) } func (s *clientSuite) TestClientServiceUpdateNoParams(c *gc.C) { s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) // Calling ServiceUpdate with no parameters set is a no-op. args := params.ServiceUpdate{ServiceName: "wordpress"} err := s.APIState.Client().ServiceUpdate(args) c.Assert(err, gc.IsNil) } func (s *clientSuite) TestClientServiceUpdateNoService(c *gc.C) { err := s.APIState.Client().ServiceUpdate(params.ServiceUpdate{}) c.Assert(err, gc.ErrorMatches, `"" is not a valid service name`) } func (s *clientSuite) TestClientServiceUpdateInvalidService(c *gc.C) { args := params.ServiceUpdate{ServiceName: "no-such-service"} err := s.APIState.Client().ServiceUpdate(args) c.Assert(err, gc.ErrorMatches, `service "no-such-service" not found`) } func (s *clientSuite) TestClientServiceSetCharm(c *gc.C) { store, restore := makeMockCharmStore() defer restore() curl, _ := addCharm(c, store, "dummy") err := s.APIState.Client().ServiceDeploy( curl.String(), "service", 3, "", constraints.Value{}, "", ) c.Assert(err, gc.IsNil) addCharm(c, store, "wordpress") err = s.APIState.Client().ServiceSetCharm( "service", "cs:precise/wordpress-3", false, ) c.Assert(err, gc.IsNil) // Ensure that the charm is not marked as forced. service, err := s.State.Service("service") c.Assert(err, gc.IsNil) charm, force, err := service.Charm() c.Assert(err, gc.IsNil) c.Assert(charm.URL().String(), gc.Equals, "cs:precise/wordpress-3") c.Assert(force, gc.Equals, false) } func (s *clientSuite) TestClientServiceSetCharmForce(c *gc.C) { store, restore := makeMockCharmStore() defer restore() curl, _ := addCharm(c, store, "dummy") err := s.APIState.Client().ServiceDeploy( curl.String(), "service", 3, "", constraints.Value{}, "", ) c.Assert(err, gc.IsNil) addCharm(c, store, "wordpress") err = s.APIState.Client().ServiceSetCharm( "service", "cs:precise/wordpress-3", true, ) c.Assert(err, gc.IsNil) // Ensure that the charm is marked as forced. service, err := s.State.Service("service") c.Assert(err, gc.IsNil) charm, force, err := service.Charm() c.Assert(err, gc.IsNil) c.Assert(charm.URL().String(), gc.Equals, "cs:precise/wordpress-3") c.Assert(force, gc.Equals, true) } func (s *clientSuite) TestClientServiceSetCharmInvalidService(c *gc.C) { _, restore := makeMockCharmStore() defer restore() err := s.APIState.Client().ServiceSetCharm( "badservice", "cs:precise/wordpress-3", true, ) c.Assert(err, gc.ErrorMatches, `service "badservice" not found`) } func (s *clientSuite) TestClientServiceSetCharmErrors(c *gc.C) { _, restore := makeMockCharmStore() defer restore() s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) for url, expect := range map[string]string{ // TODO(fwereade,Makyo) make these errors consistent one day. "wordpress": "charm url series is not resolved", "cs:wordpress": "charm url series is not resolved", "cs:precise/wordpress": "charm url must include revision", "cs:precise/wordpress-999999": `cannot download charm ".*": charm not found in mock store: cs:precise/wordpress-999999`, } { c.Logf("test %s", url) err := s.APIState.Client().ServiceSetCharm( "wordpress", url, false, ) c.Check(err, gc.ErrorMatches, expect) } } func makeMockCharmStore() (store *coretesting.MockCharmStore, restore func()) { mockStore := coretesting.NewMockCharmStore() origStore := client.CharmStore client.CharmStore = mockStore return mockStore, func() { client.CharmStore = origStore } } func addCharm(c *gc.C, store *coretesting.MockCharmStore, name string) (*charm.URL, charm.Charm) { return addSeriesCharm(c, store, "precise", name) } func addSeriesCharm(c *gc.C, store *coretesting.MockCharmStore, series, name string) (*charm.URL, charm.Charm) { bundle := coretesting.Charms.Bundle(c.MkDir(), name) scurl := fmt.Sprintf("cs:%s/%s-%d", series, name, bundle.Revision()) curl := charm.MustParseURL(scurl) err := store.SetCharm(curl, bundle) c.Assert(err, gc.IsNil) return curl, bundle } func (s *clientSuite) checkEndpoints(c *gc.C, endpoints map[string]charm.Relation) { c.Assert(endpoints["wordpress"], gc.DeepEquals, charm.Relation{ Name: "db", Role: charm.RelationRole("requirer"), Interface: "mysql", Optional: false, Limit: 1, Scope: charm.RelationScope("global"), }) c.Assert(endpoints["mysql"], gc.DeepEquals, charm.Relation{ Name: "server", Role: charm.RelationRole("provider"), Interface: "mysql", Optional: false, Limit: 0, Scope: charm.RelationScope("global"), }) } func (s *clientSuite) assertAddRelation(c *gc.C, endpoints []string) { s.setUpScenario(c) res, err := s.APIState.Client().AddRelation(endpoints...) c.Assert(err, gc.IsNil) s.checkEndpoints(c, res.Endpoints) // Show that the relation was added. wpSvc, err := s.State.Service("wordpress") c.Assert(err, gc.IsNil) rels, err := wpSvc.Relations() // There are 2 relations - the logging-wordpress one set up in the // scenario and the one created in this test. c.Assert(len(rels), gc.Equals, 2) mySvc, err := s.State.Service("mysql") c.Assert(err, gc.IsNil) rels, err = mySvc.Relations() c.Assert(len(rels), gc.Equals, 1) } func (s *clientSuite) TestSuccessfullyAddRelation(c *gc.C) { endpoints := []string{"wordpress", "mysql"} s.assertAddRelation(c, endpoints) } func (s *clientSuite) TestSuccessfullyAddRelationSwapped(c *gc.C) { // Show that the order of the services listed in the AddRelation call // does not matter. This is a repeat of the previous test with the service // names swapped. endpoints := []string{"mysql", "wordpress"} s.assertAddRelation(c, endpoints) } func (s *clientSuite) TestCallWithOnlyOneEndpoint(c *gc.C) { s.setUpScenario(c) endpoints := []string{"wordpress"} _, err := s.APIState.Client().AddRelation(endpoints...) c.Assert(err, gc.ErrorMatches, "no relations found") } func (s *clientSuite) TestCallWithOneEndpointTooMany(c *gc.C) { s.setUpScenario(c) endpoints := []string{"wordpress", "mysql", "logging"} _, err := s.APIState.Client().AddRelation(endpoints...) c.Assert(err, gc.ErrorMatches, "cannot relate 3 endpoints") } func (s *clientSuite) TestAddAlreadyAddedRelation(c *gc.C) { s.setUpScenario(c) // Add a relation between wordpress and mysql. endpoints := []string{"wordpress", "mysql"} eps, err := s.State.InferEndpoints(endpoints) c.Assert(err, gc.IsNil) _, err = s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) // And try to add it again. _, err = s.APIState.Client().AddRelation(endpoints...) c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": relation already exists`) } func (s *clientSuite) assertDestroyRelation(c *gc.C, endpoints []string) { s.setUpScenario(c) // Add a relation between the endpoints. eps, err := s.State.InferEndpoints(endpoints) c.Assert(err, gc.IsNil) relation, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) err = s.APIState.Client().DestroyRelation(endpoints...) c.Assert(err, gc.IsNil) // Show that the relation was removed. c.Assert(relation.Refresh(), jc.Satisfies, errors.IsNotFoundError) } func (s *clientSuite) TestSuccessfulDestroyRelation(c *gc.C) { endpoints := []string{"wordpress", "mysql"} s.assertDestroyRelation(c, endpoints) } func (s *clientSuite) TestSuccessfullyDestroyRelationSwapped(c *gc.C) { // Show that the order of the services listed in the DestroyRelation call // does not matter. This is a repeat of the previous test with the service // names swapped. endpoints := []string{"mysql", "wordpress"} s.assertDestroyRelation(c, endpoints) } func (s *clientSuite) TestNoRelation(c *gc.C) { s.setUpScenario(c) endpoints := []string{"wordpress", "mysql"} err := s.APIState.Client().DestroyRelation(endpoints...) c.Assert(err, gc.ErrorMatches, `relation "wordpress:db mysql:server" not found`) } func (s *clientSuite) TestAttemptDestroyingNonExistentRelation(c *gc.C) { s.setUpScenario(c) s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak")) endpoints := []string{"riak", "wordpress"} err := s.APIState.Client().DestroyRelation(endpoints...) c.Assert(err, gc.ErrorMatches, "no relations found") } func (s *clientSuite) TestAttemptDestroyingWithOnlyOneEndpoint(c *gc.C) { s.setUpScenario(c) endpoints := []string{"wordpress"} err := s.APIState.Client().DestroyRelation(endpoints...) c.Assert(err, gc.ErrorMatches, "no relations found") } func (s *clientSuite) TestAttemptDestroyingPeerRelation(c *gc.C) { s.setUpScenario(c) s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak")) endpoints := []string{"riak:ring"} err := s.APIState.Client().DestroyRelation(endpoints...) c.Assert(err, gc.ErrorMatches, `cannot destroy relation "riak:ring": is a peer relation`) } func (s *clientSuite) TestAttemptDestroyingAlreadyDestroyedRelation(c *gc.C) { s.setUpScenario(c) // Add a relation between wordpress and mysql. eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) endpoints := []string{"wordpress", "mysql"} err = s.APIState.Client().DestroyRelation(endpoints...) // Show that the relation was removed. c.Assert(rel.Refresh(), jc.Satisfies, errors.IsNotFoundError) // And try to destroy it again. err = s.APIState.Client().DestroyRelation(endpoints...) c.Assert(err, gc.ErrorMatches, `relation "wordpress:db mysql:server" not found`) } func (s *clientSuite) TestClientWatchAll(c *gc.C) { // A very simple end-to-end test, because // all the logic is tested elsewhere. m, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) err = m.SetProvisioned("i-0", state.BootstrapNonce, nil) c.Assert(err, gc.IsNil) watcher, err := s.APIState.Client().WatchAll() c.Assert(err, gc.IsNil) defer func() { err := watcher.Stop() c.Assert(err, gc.IsNil) }() deltas, err := watcher.Next() c.Assert(err, gc.IsNil) if !c.Check(deltas, gc.DeepEquals, []params.Delta{{ Entity: &params.MachineInfo{ Id: m.Id(), InstanceId: "i-0", Status: params.StatusPending, Life: params.Alive, Series: "quantal", Jobs: []params.MachineJob{state.JobManageEnviron.ToParams()}, Addresses: []instance.Address{}, HardwareCharacteristics: &instance.HardwareCharacteristics{}, }, }}) { c.Logf("got:") for _, d := range deltas { c.Logf("%#v\n", d.Entity) } } } func (s *clientSuite) TestClientSetServiceConstraints(c *gc.C) { service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) // Update constraints for the service. cons, err := constraints.Parse("mem=4096", "cpu-cores=2") c.Assert(err, gc.IsNil) err = s.APIState.Client().SetServiceConstraints("dummy", cons) c.Assert(err, gc.IsNil) // Ensure the constraints have been correctly updated. obtained, err := service.Constraints() c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, cons) } func (s *clientSuite) TestClientGetServiceConstraints(c *gc.C) { service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy")) // Set constraints for the service. cons, err := constraints.Parse("mem=4096", "cpu-cores=2") c.Assert(err, gc.IsNil) err = service.SetConstraints(cons) c.Assert(err, gc.IsNil) // Check we can get the constraints. obtained, err := s.APIState.Client().GetServiceConstraints("dummy") c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, cons) } func (s *clientSuite) TestClientSetEnvironmentConstraints(c *gc.C) { // Set constraints for the environment. cons, err := constraints.Parse("mem=4096", "cpu-cores=2") c.Assert(err, gc.IsNil) err = s.APIState.Client().SetEnvironmentConstraints(cons) c.Assert(err, gc.IsNil) // Ensure the constraints have been correctly updated. obtained, err := s.State.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, cons) } func (s *clientSuite) TestClientGetEnvironmentConstraints(c *gc.C) { // Set constraints for the environment. cons, err := constraints.Parse("mem=4096", "cpu-cores=2") c.Assert(err, gc.IsNil) err = s.State.SetEnvironConstraints(cons) c.Assert(err, gc.IsNil) // Check we can get the constraints. obtained, err := s.APIState.Client().GetEnvironmentConstraints() c.Assert(err, gc.IsNil) c.Assert(obtained, gc.DeepEquals, cons) } func (s *clientSuite) TestClientServiceCharmRelations(c *gc.C) { s.setUpScenario(c) _, err := s.APIState.Client().ServiceCharmRelations("blah") c.Assert(err, gc.ErrorMatches, `service "blah" not found`) relations, err := s.APIState.Client().ServiceCharmRelations("wordpress") c.Assert(err, gc.IsNil) c.Assert(relations, gc.DeepEquals, []string{ "cache", "db", "juju-info", "logging-dir", "monitoring-port", "url", }) } func (s *clientSuite) TestClientPublicAddressErrors(c *gc.C) { s.setUpScenario(c) _, err := s.APIState.Client().PublicAddress("wordpress") c.Assert(err, gc.ErrorMatches, `unknown unit or machine "wordpress"`) _, err = s.APIState.Client().PublicAddress("0") c.Assert(err, gc.ErrorMatches, `machine "0" has no public address`) _, err = s.APIState.Client().PublicAddress("wordpress/0") c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" has no public address`) } func (s *clientSuite) TestClientPublicAddressMachine(c *gc.C) { s.setUpScenario(c) // Internally, instance.SelectPublicAddress is used; the "most public" // address is returned. m1, err := s.State.Machine("1") c.Assert(err, gc.IsNil) cloudLocalAddress := instance.NewAddress("cloudlocal") cloudLocalAddress.NetworkScope = instance.NetworkCloudLocal publicAddress := instance.NewAddress("public") publicAddress.NetworkScope = instance.NetworkPublic err = m1.SetAddresses([]instance.Address{cloudLocalAddress}) c.Assert(err, gc.IsNil) addr, err := s.APIState.Client().PublicAddress("1") c.Assert(err, gc.IsNil) c.Assert(addr, gc.Equals, "cloudlocal") err = m1.SetAddresses([]instance.Address{cloudLocalAddress, publicAddress}) addr, err = s.APIState.Client().PublicAddress("1") c.Assert(err, gc.IsNil) c.Assert(addr, gc.Equals, "public") } func (s *clientSuite) TestClientPublicAddressUnitWithMachine(c *gc.C) { s.setUpScenario(c) // Public address of unit is taken from its machine // (if its machine has addresses). m1, err := s.State.Machine("1") publicAddress := instance.NewAddress("public") publicAddress.NetworkScope = instance.NetworkPublic err = m1.SetAddresses([]instance.Address{publicAddress}) c.Assert(err, gc.IsNil) addr, err := s.APIState.Client().PublicAddress("wordpress/0") c.Assert(err, gc.IsNil) c.Assert(addr, gc.Equals, "public") } func (s *clientSuite) TestClientPublicAddressUnitWithoutMachine(c *gc.C) { s.setUpScenario(c) // If the unit's machine has no addresses, the public address // comes from the unit's document. u, err := s.State.Unit("wordpress/1") c.Assert(err, gc.IsNil) err = u.SetPublicAddress("127.0.0.1") c.Assert(err, gc.IsNil) addr, err := s.APIState.Client().PublicAddress("wordpress/1") c.Assert(err, gc.IsNil) c.Assert(addr, gc.Equals, "127.0.0.1") } func (s *clientSuite) TestClientPrivateAddressErrors(c *gc.C) { s.setUpScenario(c) _, err := s.APIState.Client().PrivateAddress("wordpress") c.Assert(err, gc.ErrorMatches, `unknown unit or machine "wordpress"`) _, err = s.APIState.Client().PrivateAddress("0") c.Assert(err, gc.ErrorMatches, `machine "0" has no internal address`) _, err = s.APIState.Client().PrivateAddress("wordpress/0") c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" has no internal address`) } func (s *clientSuite) TestClientPrivateAddressMachine(c *gc.C) { s.setUpScenario(c) // Internally, instance.SelectInternalAddress is used; the public // address if no cloud-local one is available. m1, err := s.State.Machine("1") c.Assert(err, gc.IsNil) cloudLocalAddress := instance.NewAddress("cloudlocal") cloudLocalAddress.NetworkScope = instance.NetworkCloudLocal publicAddress := instance.NewAddress("public") publicAddress.NetworkScope = instance.NetworkCloudLocal err = m1.SetAddresses([]instance.Address{publicAddress}) c.Assert(err, gc.IsNil) addr, err := s.APIState.Client().PrivateAddress("1") c.Assert(err, gc.IsNil) c.Assert(addr, gc.Equals, "public") err = m1.SetAddresses([]instance.Address{cloudLocalAddress, publicAddress}) addr, err = s.APIState.Client().PrivateAddress("1") c.Assert(err, gc.IsNil) c.Assert(addr, gc.Equals, "cloudlocal") } func (s *clientSuite) TestClientPrivateAddressUnitWithMachine(c *gc.C) { s.setUpScenario(c) // Private address of unit is taken from its machine // (if its machine has addresses). m1, err := s.State.Machine("1") publicAddress := instance.NewAddress("public") publicAddress.NetworkScope = instance.NetworkCloudLocal err = m1.SetAddresses([]instance.Address{publicAddress}) c.Assert(err, gc.IsNil) addr, err := s.APIState.Client().PrivateAddress("wordpress/0") c.Assert(err, gc.IsNil) c.Assert(addr, gc.Equals, "public") } func (s *clientSuite) TestClientPrivateAddressUnitWithoutMachine(c *gc.C) { s.setUpScenario(c) // If the unit's machine has no addresses, the public address // comes from the unit's document. u, err := s.State.Unit("wordpress/1") c.Assert(err, gc.IsNil) err = u.SetPrivateAddress("127.0.0.1") c.Assert(err, gc.IsNil) addr, err := s.APIState.Client().PrivateAddress("wordpress/1") c.Assert(err, gc.IsNil) c.Assert(addr, gc.Equals, "127.0.0.1") } func (s *clientSuite) TestClientEnvironmentGet(c *gc.C) { envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) attrs, err := s.APIState.Client().EnvironmentGet() c.Assert(err, gc.IsNil) allAttrs := envConfig.AllAttrs() // We cannot simply use DeepEquals, because after the // map[string]interface{} result of EnvironmentGet is // serialized to JSON, integers are converted to floats. for key, apiValue := range attrs { envValue, found := allAttrs[key] c.Check(found, jc.IsTrue) switch apiValue.(type) { case float64, float32: c.Check(fmt.Sprintf("%v", envValue), gc.Equals, fmt.Sprintf("%v", apiValue)) default: c.Check(envValue, gc.Equals, apiValue) } } } func (s *clientSuite) TestClientEnvironmentSet(c *gc.C) { envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) _, found := envConfig.AllAttrs()["some-key"] c.Assert(found, jc.IsFalse) args := map[string]interface{}{"some-key": "value"} err = s.APIState.Client().EnvironmentSet(args) c.Assert(err, gc.IsNil) envConfig, err = s.State.EnvironConfig() c.Assert(err, gc.IsNil) value, found := envConfig.AllAttrs()["some-key"] c.Assert(found, jc.IsTrue) c.Assert(value, gc.Equals, "value") } func (s *clientSuite) TestClientSetEnvironAgentVersion(c *gc.C) { err := s.APIState.Client().SetEnvironAgentVersion(version.MustParse("9.8.7")) c.Assert(err, gc.IsNil) envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) agentVersion, found := envConfig.AllAttrs()["agent-version"] c.Assert(found, jc.IsTrue) c.Assert(agentVersion, gc.Equals, "9.8.7") } func (s *clientSuite) TestClientEnvironmentSetCannotChangeAgentVersion(c *gc.C) { args := map[string]interface{}{"agent-version": "9.9.9"} err := s.APIState.Client().EnvironmentSet(args) c.Assert(err, gc.ErrorMatches, "agent-version cannot be changed") // It's okay to pass env back with the same agent-version. cfg, err := s.APIState.Client().EnvironmentGet() c.Assert(err, gc.IsNil) c.Assert(cfg["agent-version"], gc.NotNil) err = s.APIState.Client().EnvironmentSet(cfg) c.Assert(err, gc.IsNil) } func (s *clientSuite) TestClientEnvironmentUnset(c *gc.C) { err := s.State.UpdateEnvironConfig(map[string]interface{}{"abc": 123}, nil, nil) c.Assert(err, gc.IsNil) envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) _, found := envConfig.AllAttrs()["abc"] c.Assert(found, jc.IsTrue) err = s.APIState.Client().EnvironmentUnset("abc") c.Assert(err, gc.IsNil) envConfig, err = s.State.EnvironConfig() c.Assert(err, gc.IsNil) _, found = envConfig.AllAttrs()["abc"] c.Assert(found, jc.IsFalse) } func (s *clientSuite) TestClientEnvironmentUnsetMissing(c *gc.C) { // It's okay to unset a non-existent attribute. err := s.APIState.Client().EnvironmentUnset("not_there") c.Assert(err, gc.IsNil) } func (s *clientSuite) TestClientEnvironmentUnsetError(c *gc.C) { err := s.State.UpdateEnvironConfig(map[string]interface{}{"abc": 123}, nil, nil) c.Assert(err, gc.IsNil) envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) _, found := envConfig.AllAttrs()["abc"] c.Assert(found, jc.IsTrue) // "type" may not be removed, and this will cause an error. // If any one attribute's removal causes an error, there // should be no change. err = s.APIState.Client().EnvironmentUnset("abc", "type") c.Assert(err, gc.ErrorMatches, "type: expected string, got nothing") envConfig, err = s.State.EnvironConfig() c.Assert(err, gc.IsNil) _, found = envConfig.AllAttrs()["abc"] c.Assert(found, jc.IsTrue) } func (s *clientSuite) TestClientFindTools(c *gc.C) { result, err := s.APIState.Client().FindTools(2, -1, "", "") c.Assert(err, gc.IsNil) c.Assert(result.Error, jc.Satisfies, params.IsCodeNotFound) ttesting.UploadToStorage(c, s.Conn.Environ.Storage(), version.MustParseBinary("2.12.0-precise-amd64")) result, err = s.APIState.Client().FindTools(2, 12, "precise", "amd64") c.Assert(err, gc.IsNil) c.Assert(result.Error, gc.IsNil) c.Assert(result.List, gc.HasLen, 1) c.Assert(result.List[0].Version, gc.Equals, version.MustParseBinary("2.12.0-precise-amd64")) } func (s *clientSuite) checkMachine(c *gc.C, id, series, cons string) { // Ensure the machine was actually created. machine, err := s.BackingState.Machine(id) c.Assert(err, gc.IsNil) c.Assert(machine.Series(), gc.Equals, series) c.Assert(machine.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobHostUnits}) machineConstraints, err := machine.Constraints() c.Assert(err, gc.IsNil) c.Assert(machineConstraints.String(), gc.Equals, cons) } func (s *clientSuite) TestClientAddMachinesDefaultSeries(c *gc.C) { apiParams := make([]params.AddMachineParams, 3) for i := 0; i < 3; i++ { apiParams[i] = params.AddMachineParams{ Jobs: []params.MachineJob{params.JobHostUnits}, } } machines, err := s.APIState.Client().AddMachines(apiParams) c.Assert(err, gc.IsNil) c.Assert(len(machines), gc.Equals, 3) for i, machineResult := range machines { c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) s.checkMachine(c, machineResult.Machine, coretesting.FakeDefaultSeries, apiParams[i].Constraints.String()) } } func (s *clientSuite) TestClientAddMachinesWithSeries(c *gc.C) { apiParams := make([]params.AddMachineParams, 3) for i := 0; i < 3; i++ { apiParams[i] = params.AddMachineParams{ Series: "quantal", Jobs: []params.MachineJob{params.JobHostUnits}, } } machines, err := s.APIState.Client().AddMachines(apiParams) c.Assert(err, gc.IsNil) c.Assert(len(machines), gc.Equals, 3) for i, machineResult := range machines { c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) s.checkMachine(c, machineResult.Machine, "quantal", apiParams[i].Constraints.String()) } } func (s *clientSuite) TestClientAddMachineInsideMachine(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{{ Jobs: []params.MachineJob{params.JobHostUnits}, ParentId: "0", ContainerType: instance.LXC, Series: "quantal", }}) c.Assert(err, gc.IsNil) c.Assert(machines, gc.HasLen, 1) c.Assert(machines[0].Machine, gc.Equals, "0/lxc/0") } func (s *clientSuite) TestClientAddMachinesWithConstraints(c *gc.C) { apiParams := make([]params.AddMachineParams, 3) for i := 0; i < 3; i++ { apiParams[i] = params.AddMachineParams{ Jobs: []params.MachineJob{params.JobHostUnits}, } } // The last machine has some constraints. apiParams[2].Constraints = constraints.MustParse("mem=4G") machines, err := s.APIState.Client().AddMachines(apiParams) c.Assert(err, gc.IsNil) c.Assert(len(machines), gc.Equals, 3) for i, machineResult := range machines { c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) s.checkMachine(c, machineResult.Machine, coretesting.FakeDefaultSeries, apiParams[i].Constraints.String()) } } func (s *clientSuite) TestClientAddMachinesSomeErrors(c *gc.C) { // Here we check that adding a number of containers correctly handles the // case that some adds succeed and others fail and report the errors // accordingly. // We will set up params to the AddMachines API to attempt to create 4 machines. // Machines 0 and 1 will be added successfully. // Mchines 2 and 3 will fail due to different reasons. // Create a machine to host the requested containers. host, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // The host only supports lxc containers. err = host.SetSupportedContainers([]instance.ContainerType{instance.LXC}) c.Assert(err, gc.IsNil) // Set up params for adding 4 containers. apiParams := make([]params.AddMachineParams, 4) for i := 0; i < 4; i++ { apiParams[i] = params.AddMachineParams{ Jobs: []params.MachineJob{params.JobHostUnits}, } } // Make it so that machines 2 and 3 will fail to be added. // This will cause a machine add to fail because of an invalid parent. apiParams[2].ParentId = "123" // This will cause a machine add to fail due to an unsupported container. apiParams[3].ParentId = host.Id() apiParams[3].ContainerType = instance.KVM machines, err := s.APIState.Client().AddMachines(apiParams) c.Assert(err, gc.IsNil) c.Assert(len(machines), gc.Equals, 4) // Check the results - machines 2 and 3 will have errors. c.Check(machines[0].Machine, gc.Equals, "1") c.Check(machines[0].Error, gc.IsNil) c.Check(machines[1].Machine, gc.Equals, "2") c.Check(machines[1].Error, gc.IsNil) c.Assert(machines[2].Error, gc.NotNil) c.Check(machines[2].Error, gc.ErrorMatches, "parent machine specified without container type") c.Assert(machines[2].Error, gc.NotNil) c.Check(machines[3].Error, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host kvm containers") } func (s *clientSuite) TestClientAddMachinesWithInstanceIdSomeErrors(c *gc.C) { apiParams := make([]params.AddMachineParams, 3) addrs := []instance.Address{instance.NewAddress("1.2.3.4")} hc := instance.MustParseHardware("mem=4G") for i := 0; i < 3; i++ { apiParams[i] = params.AddMachineParams{ Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: instance.Id(fmt.Sprintf("1234-%d", i)), Nonce: "foo", HardwareCharacteristics: hc, Addrs: addrs, } } // This will cause the last machine add to fail. apiParams[2].Nonce = "" machines, err := s.APIState.Client().AddMachines(apiParams) c.Assert(err, gc.IsNil) c.Assert(len(machines), gc.Equals, 3) for i, machineResult := range machines { if i == 2 { c.Assert(machineResult.Error, gc.NotNil) c.Assert(machineResult.Error, gc.ErrorMatches, "cannot add a new machine: cannot add a machine with an instance id and no nonce") } else { c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) s.checkMachine(c, machineResult.Machine, coretesting.FakeDefaultSeries, apiParams[i].Constraints.String()) instanceId := fmt.Sprintf("1234-%d", i) s.checkInstance(c, machineResult.Machine, instanceId, "foo", hc, addrs) } } } func (s *clientSuite) checkInstance(c *gc.C, id, instanceId, nonce string, hc instance.HardwareCharacteristics, addr []instance.Address) { machine, err := s.BackingState.Machine(id) c.Assert(err, gc.IsNil) machineInstanceId, err := machine.InstanceId() c.Assert(err, gc.IsNil) c.Assert(machine.CheckProvisioned(nonce), jc.IsTrue) c.Assert(machineInstanceId, gc.Equals, instance.Id(instanceId)) machineHardware, err := machine.HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Assert(machineHardware.String(), gc.Equals, hc.String()) c.Assert(machine.Addresses(), gc.DeepEquals, addr) } func (s *clientSuite) TestInjectMachinesStillExists(c *gc.C) { results := new(params.AddMachinesResults) // We need to use Call directly because the client interface // no longer refers to InjectMachine. args := params.AddMachines{ MachineParams: []params.AddMachineParams{{ Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: "i-foo", Nonce: "nonce", }}, } err := s.APIState.Call("Client", "", "AddMachines", args, &results) c.Assert(err, gc.IsNil) c.Assert(results.Machines, gc.HasLen, 1) } func (s *clientSuite) TestProvisioningScript(c *gc.C) { // Inject a machine and then call the ProvisioningScript API. // The result should be the same as when calling MachineConfig, // converting it to a cloudinit.MachineConfig, and disabling // apt_upgrade. apiParams := params.AddMachineParams{ Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: instance.Id("1234"), Nonce: "foo", HardwareCharacteristics: instance.MustParseHardware("arch=amd64"), } machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) c.Assert(err, gc.IsNil) c.Assert(len(machines), gc.Equals, 1) machineId := machines[0].Machine // Call ProvisioningScript. Normally ProvisioningScript and // MachineConfig are mutually exclusive; both of them will // allocate a state/api password for the machine agent. script, err := s.APIState.Client().ProvisioningScript(params.ProvisioningScriptParams{ MachineId: machineId, Nonce: apiParams.Nonce, }) c.Assert(err, gc.IsNil) mcfg, err := statecmd.MachineConfig(s.State, machineId, apiParams.Nonce, "") c.Assert(err, gc.IsNil) sshinitScript, err := manual.ProvisioningScript(mcfg) c.Assert(err, gc.IsNil) // ProvisioningScript internally calls MachineConfig, // which allocates a new, random password. Everything // about the scripts should be the same other than // the line containing "oldpassword" from agent.conf. scriptLines := strings.Split(script, "\n") sshinitScriptLines := strings.Split(sshinitScript, "\n") c.Assert(scriptLines, gc.HasLen, len(sshinitScriptLines)) for i, line := range scriptLines { if strings.Contains(line, "oldpassword") { continue } c.Assert(line, gc.Equals, sshinitScriptLines[i]) } } func (s *clientSuite) TestProvisioningScriptDisablePackageCommands(c *gc.C) { apiParams := params.AddMachineParams{ Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: instance.Id("1234"), Nonce: "foo", HardwareCharacteristics: instance.MustParseHardware("arch=amd64"), } machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) c.Assert(err, gc.IsNil) c.Assert(len(machines), gc.Equals, 1) machineId := machines[0].Machine for _, disable := range []bool{false, true} { script, err := s.APIState.Client().ProvisioningScript(params.ProvisioningScriptParams{ MachineId: machineId, Nonce: apiParams.Nonce, DisablePackageCommands: disable, }) c.Assert(err, gc.IsNil) var checker gc.Checker = jc.Contains if disable { // We disabled package commands: there should be no "apt" commands in the script. checker = gc.Not(checker) } c.Assert(script, checker, "apt-get") } } func (s *clientSuite) TestClientSpecializeStoreOnDeployServiceSetCharmAndAddCharm(c *gc.C) { store, restore := makeMockCharmStore() defer restore() attrs := map[string]interface{}{"charm-store-auth": "token=value", "test-mode": true} err := s.State.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) curl, _ := addCharm(c, store, "dummy") err = s.APIState.Client().ServiceDeploy( curl.String(), "service", 3, "", constraints.Value{}, "", ) c.Assert(err, gc.IsNil) // check that the store's auth attributes were set c.Assert(store.AuthAttrs, gc.Equals, "token=value") c.Assert(store.TestMode, gc.Equals, true) store.AuthAttrs = "" curl, _ = addCharm(c, store, "wordpress") err = s.APIState.Client().ServiceSetCharm( "service", curl.String(), false, ) // check that the store's auth attributes were set c.Assert(store.AuthAttrs, gc.Equals, "token=value") curl, _ = addCharm(c, store, "riak") err = s.APIState.Client().AddCharm(curl) // check that the store's auth attributes were set c.Assert(store.AuthAttrs, gc.Equals, "token=value") } func (s *clientSuite) TestAddCharm(c *gc.C) { store, restore := makeMockCharmStore() defer restore() client := s.APIState.Client() // First test the sanity checks. err := client.AddCharm(&charm.URL{Reference: charm.Reference{Name: "nonsense"}}) c.Assert(err, gc.ErrorMatches, `charm URL has invalid schema: ":nonsense-0"`) err = client.AddCharm(charm.MustParseURL("local:precise/dummy")) c.Assert(err, gc.ErrorMatches, "only charm store charm URLs are supported, with cs: schema") err = client.AddCharm(charm.MustParseURL("cs:precise/wordpress")) c.Assert(err, gc.ErrorMatches, "charm URL must include revision") // Add a charm, without uploading it to storage, to // check that AddCharm does not try to do it. charmDir := coretesting.Charms.Dir("dummy") ident := fmt.Sprintf("%s-%d", charmDir.Meta().Name, charmDir.Revision()) curl := charm.MustParseURL("cs:quantal/" + ident) bundleURL, err := url.Parse("http://bundles.testing.invalid/" + ident) c.Assert(err, gc.IsNil) sch, err := s.State.AddCharm(charmDir, curl, bundleURL, ident+"-sha256") c.Assert(err, gc.IsNil) name := charm.Quote(sch.URL().String()) storage := s.Conn.Environ.Storage() _, err = storage.Get(name) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // AddCharm should see the charm in state and not upload it. err = client.AddCharm(sch.URL()) c.Assert(err, gc.IsNil) _, err = storage.Get(name) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // Now try adding another charm completely. curl, _ = addCharm(c, store, "wordpress") err = client.AddCharm(curl) c.Assert(err, gc.IsNil) // Verify it's in state and it got uploaded. sch, err = s.State.Charm(curl) c.Assert(err, gc.IsNil) s.assertUploaded(c, storage, sch.BundleURL(), sch.BundleSha256()) } var resolveCharmCases = []struct { schema, defaultSeries, charmName string parseErr string resolveErr string }{ {"cs", "precise", "wordpress", "", ""}, {"cs", "trusty", "wordpress", "", ""}, {"cs", "", "wordpress", "", `missing default series, cannot resolve charm url: "cs:wordpress"`}, {"cs", "trusty", "", `charm URL has invalid charm name: "cs:"`, ""}, {"local", "trusty", "wordpress", "", `only charm store charm references are supported, with cs: schema`}, {"cs", "precise", "hl3", "", ""}, {"cs", "trusty", "hl3", "", ""}, {"cs", "", "hl3", "", `missing default series, cannot resolve charm url: \"cs:hl3\"`}, } func (s *clientSuite) TestResolveCharm(c *gc.C) { store, restore := makeMockCharmStore() defer restore() for i, test := range resolveCharmCases { c.Logf("test %d: %#v", i, test) // Mock charm store will use this to resolve a charm reference. store.DefaultSeries = test.defaultSeries client := s.APIState.Client() ref, series, err := charm.ParseReference(fmt.Sprintf("%s:%s", test.schema, test.charmName)) if test.parseErr == "" { if !c.Check(err, gc.IsNil) { continue } } else { c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, test.parseErr) continue } c.Check(series, gc.Equals, "") c.Check(ref.String(), gc.Equals, fmt.Sprintf("%s:%s", test.schema, test.charmName)) curl, err := client.ResolveCharm(ref) if err == nil { c.Assert(curl, gc.NotNil) // Only cs: schema should make it through here c.Check(curl.String(), gc.Equals, fmt.Sprintf("cs:%s/%s", test.defaultSeries, test.charmName)) c.Check(test.resolveErr, gc.Equals, "") } else { c.Check(curl, gc.IsNil) c.Check(err, gc.ErrorMatches, test.resolveErr) } } } func (s *clientSuite) TestAddCharmConcurrently(c *gc.C) { store, restore := makeMockCharmStore() defer restore() client := s.APIState.Client() curl, _ := addCharm(c, store, "wordpress") // Expect storage Put() to be called once for each goroutine // below. ops := make(chan dummy.Operation, 500) dummy.Listen(ops) go s.assertPutCalled(c, ops, 10) // Try adding the same charm concurrently from multiple goroutines // to test no "duplicate key errors" are reported (see lp bug // #1067979) and also at the end only one charm document is // created. var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func(index int) { defer wg.Done() c.Assert(client.AddCharm(curl), gc.IsNil, gc.Commentf("goroutine %d", index)) sch, err := s.State.Charm(curl) c.Assert(err, gc.IsNil, gc.Commentf("goroutine %d", index)) c.Assert(sch.URL(), jc.DeepEquals, curl, gc.Commentf("goroutine %d", index)) expectedName := fmt.Sprintf("%s-%d-[0-9a-f-]+", curl.Name, curl.Revision) c.Assert(getArchiveName(sch.BundleURL()), gc.Matches, expectedName) }(i) } wg.Wait() close(ops) // Verify there is only a single uploaded charm remains and it // contains the correct data. sch, err := s.State.Charm(curl) c.Assert(err, gc.IsNil) storage, err := environs.GetStorage(s.State) c.Assert(err, gc.IsNil) uploads, err := storage.List(fmt.Sprintf("%s-%d-", curl.Name, curl.Revision)) c.Assert(err, gc.IsNil) c.Assert(uploads, gc.HasLen, 1) c.Assert(getArchiveName(sch.BundleURL()), gc.Equals, uploads[0]) s.assertUploaded(c, storage, sch.BundleURL(), sch.BundleSha256()) } func (s *clientSuite) TestAddCharmOverwritesPlaceholders(c *gc.C) { store, restore := makeMockCharmStore() defer restore() client := s.APIState.Client() curl, _ := addCharm(c, store, "wordpress") // Add a placeholder with the same charm URL. err := s.State.AddStoreCharmPlaceholder(curl) c.Assert(err, gc.IsNil) _, err = s.State.Charm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // Now try to add the charm, which will convert the placeholder to // a pending charm. err = client.AddCharm(curl) c.Assert(err, gc.IsNil) // Make sure the document's flags were reset as expected. sch, err := s.State.Charm(curl) c.Assert(err, gc.IsNil) c.Assert(sch.URL(), jc.DeepEquals, curl) c.Assert(sch.IsPlaceholder(), jc.IsFalse) c.Assert(sch.IsUploaded(), jc.IsTrue) } func (s *clientSuite) TestCharmArchiveName(c *gc.C) { for rev, name := range []string{"Foo", "bar", "wordpress", "mysql"} { archiveFormat := fmt.Sprintf("%s-%d-[0-9a-f-]+", name, rev) archiveName, err := client.CharmArchiveName(name, rev) c.Check(err, gc.IsNil) c.Check(archiveName, gc.Matches, archiveFormat) } } func (s *clientSuite) assertPutCalled(c *gc.C, ops chan dummy.Operation, numCalls int) { calls := 0 select { case op, ok := <-ops: if !ok { return } if op, ok := op.(dummy.OpPutFile); ok { calls++ if calls > numCalls { c.Fatalf("storage Put() called %d times, expected %d times", calls, numCalls) return } nameFormat := "[0-9a-z-]+-[0-9]+-[0-9a-f-]+" c.Assert(op.FileName, gc.Matches, nameFormat) } case <-time.After(coretesting.LongWait): c.Fatalf("timed out while waiting for a storage Put() calls") return } } func (s *clientSuite) assertUploaded(c *gc.C, storage envstorage.Storage, bundleURL *url.URL, expectedSHA256 string) { archiveName := getArchiveName(bundleURL) reader, err := storage.Get(archiveName) c.Assert(err, gc.IsNil) defer reader.Close() downloadedSHA256, _, err := utils.ReadSHA256(reader) c.Assert(err, gc.IsNil) c.Assert(downloadedSHA256, gc.Equals, expectedSHA256) } func getArchiveName(bundleURL *url.URL) string { return strings.TrimPrefix(bundleURL.RequestURI(), "/dummyenv/private/") } func (s *clientSuite) TestRetryProvisioning(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine.SetStatus(params.StatusError, "error", nil) c.Assert(err, gc.IsNil) _, err = s.APIState.Client().RetryProvisioning(machine.Tag()) c.Assert(err, gc.IsNil) status, info, data, err := machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "error") c.Assert(data["transient"], gc.Equals, true) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/export_test.go������������������0000644�0000153�0000161�00000000404�12321735642�030170� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client var ParseSettingsCompatible = parseSettingsCompatible var RemoteParamsForMachine = remoteParamsForMachine var GetAllUnitNames = getAllUnitNames ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/perm_test.go��������������������0000644�0000153�0000161�00000031547�12321735642�027626� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client_test import ( "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/version" ) type permSuite struct { baseSuite } var _ = gc.Suite(&permSuite{}) // Most (if not all) of the permission tests below aim to test // end-to-end operations execution through the API, but do not care // about the results. They only test that a call is succeeds or fails // (usually due to "permission denied"). There are separate test cases // testing each individual API call data flow later on. var operationPermTests = []struct { about string // op performs the operation to be tested using the given state // connection. It returns a function that should be used to // undo any changes made by the operation. op func(c *gc.C, st *api.State, mst *state.State) (reset func(), err error) allow []string deny []string }{{ about: "Client.Status", op: opClientStatus, allow: []string{"user-admin", "user-other"}, }, { about: "Client.ServiceSet", op: opClientServiceSet, allow: []string{"user-admin", "user-other"}, }, { about: "Client.ServiceSetYAML", op: opClientServiceSetYAML, allow: []string{"user-admin", "user-other"}, }, { about: "Client.ServiceGet", op: opClientServiceGet, allow: []string{"user-admin", "user-other"}, }, { about: "Client.Resolved", op: opClientResolved, allow: []string{"user-admin", "user-other"}, }, { about: "Client.ServiceExpose", op: opClientServiceExpose, allow: []string{"user-admin", "user-other"}, }, { about: "Client.ServiceUnexpose", op: opClientServiceUnexpose, allow: []string{"user-admin", "user-other"}, }, { about: "Client.ServiceDeploy", op: opClientServiceDeploy, allow: []string{"user-admin", "user-other"}, }, { about: "Client.ServiceDeployWithNetworks", op: opClientServiceDeployWithNetworks, allow: []string{"user-admin", "user-other"}, }, { about: "Client.ServiceUpdate", op: opClientServiceUpdate, allow: []string{"user-admin", "user-other"}, }, { about: "Client.ServiceSetCharm", op: opClientServiceSetCharm, allow: []string{"user-admin", "user-other"}, }, { about: "Client.GetAnnotations", op: opClientGetAnnotations, allow: []string{"user-admin", "user-other"}, }, { about: "Client.SetAnnotations", op: opClientSetAnnotations, allow: []string{"user-admin", "user-other"}, }, { about: "Client.AddServiceUnits", op: opClientAddServiceUnits, allow: []string{"user-admin", "user-other"}, }, { about: "Client.DestroyServiceUnits", op: opClientDestroyServiceUnits, allow: []string{"user-admin", "user-other"}, }, { about: "Client.ServiceDestroy", op: opClientServiceDestroy, allow: []string{"user-admin", "user-other"}, }, { about: "Client.GetServiceConstraints", op: opClientGetServiceConstraints, allow: []string{"user-admin", "user-other"}, }, { about: "Client.SetServiceConstraints", op: opClientSetServiceConstraints, allow: []string{"user-admin", "user-other"}, }, { about: "Client.SetEnvironmentConstraints", op: opClientSetEnvironmentConstraints, allow: []string{"user-admin", "user-other"}, }, { about: "Client.EnvironmentGet", op: opClientEnvironmentGet, allow: []string{"user-admin", "user-other"}, }, { about: "Client.EnvironmentSet", op: opClientEnvironmentSet, allow: []string{"user-admin", "user-other"}, }, { about: "Client.SetEnvironAgentVersion", op: opClientSetEnvironAgentVersion, allow: []string{"user-admin", "user-other"}, }, { about: "Client.WatchAll", op: opClientWatchAll, allow: []string{"user-admin", "user-other"}, }, { about: "Client.CharmInfo", op: opClientCharmInfo, allow: []string{"user-admin", "user-other"}, }, { about: "Client.AddRelation", op: opClientAddRelation, allow: []string{"user-admin", "user-other"}, }, { about: "Client.DestroyRelation", op: opClientDestroyRelation, allow: []string{"user-admin", "user-other"}, }} // allowed returns the set of allowed entities given an allow list and a // deny list. If an allow list is specified, only those entities are // allowed; otherwise those in deny are disallowed. func allowed(all, allow, deny []string) map[string]bool { p := make(map[string]bool) if allow != nil { for _, e := range allow { p[e] = true } return p } loop: for _, e0 := range all { for _, e1 := range deny { if e1 == e0 { continue loop } } p[e0] = true } return p } func (s *permSuite) TestOperationPerm(c *gc.C) { entities := s.setUpScenario(c) for i, t := range operationPermTests { allow := allowed(entities, t.allow, t.deny) for _, e := range entities { c.Logf("test %d; %s; entity %q", i, t.about, e) st := s.openAs(c, e) reset, err := t.op(c, st, s.State) if allow[e] { c.Check(err, gc.IsNil) } else { c.Check(err, gc.ErrorMatches, "permission denied") c.Check(err, jc.Satisfies, params.IsCodeUnauthorized) } reset() st.Close() } } } func opClientCharmInfo(c *gc.C, st *api.State, mst *state.State) (func(), error) { info, err := st.Client().CharmInfo("local:quantal/wordpress-3") if err != nil { c.Check(info, gc.IsNil) return func() {}, err } c.Assert(info.URL, gc.Equals, "local:quantal/wordpress-3") c.Assert(info.Meta.Name, gc.Equals, "wordpress") c.Assert(info.Revision, gc.Equals, 3) return func() {}, nil } func opClientAddRelation(c *gc.C, st *api.State, mst *state.State) (func(), error) { _, err := st.Client().AddRelation("nosuch1", "nosuch2") if params.IsCodeNotFound(err) { err = nil } return func() {}, err } func opClientDestroyRelation(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().DestroyRelation("nosuch1", "nosuch2") if params.IsCodeNotFound(err) { err = nil } return func() {}, err } func opClientStatus(c *gc.C, st *api.State, mst *state.State) (func(), error) { status, err := st.Client().Status(nil) if err != nil { c.Check(status, gc.IsNil) return func() {}, err } c.Assert(status, jc.DeepEquals, scenarioStatus) return func() {}, nil } func resetBlogTitle(c *gc.C, st *api.State) func() { return func() { err := st.Client().ServiceSet("wordpress", map[string]string{ "blog-title": "", }) c.Assert(err, gc.IsNil) } } func opClientServiceSet(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceSet("wordpress", map[string]string{ "blog-title": "foo", }) if err != nil { return func() {}, err } return resetBlogTitle(c, st), nil } func opClientServiceSetYAML(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceSetYAML("wordpress", `"wordpress": {"blog-title": "foo"}`) if err != nil { return func() {}, err } return resetBlogTitle(c, st), nil } func opClientServiceGet(c *gc.C, st *api.State, mst *state.State) (func(), error) { _, err := st.Client().ServiceGet("wordpress") if err != nil { return func() {}, err } return func() {}, nil } func opClientServiceExpose(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceExpose("wordpress") if err != nil { return func() {}, err } return func() { svc, err := mst.Service("wordpress") c.Assert(err, gc.IsNil) svc.ClearExposed() }, nil } func opClientServiceUnexpose(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceUnexpose("wordpress") if err != nil { return func() {}, err } return func() {}, nil } func opClientResolved(c *gc.C, st *api.State, _ *state.State) (func(), error) { err := st.Client().Resolved("wordpress/0", false) // There are several scenarios in which this test is called, one is // that the user is not authorized. In that case we want to exit now, // letting the error percolate out so the caller knows that the // permission error was correctly generated. if err != nil && params.IsCodeUnauthorized(err) { return func() {}, err } // Otherwise, the user was authorized, but we expect an error anyway // because the unit is not in an error state when we tried to resolve // the error. Therefore, since it is complaining it means that the // call to Resolved worked, so we're happy. c.Assert(err, gc.NotNil) c.Assert(err.Error(), gc.Equals, `unit "wordpress/0" is not in an error state`) return func() {}, nil } func opClientGetAnnotations(c *gc.C, st *api.State, mst *state.State) (func(), error) { ann, err := st.Client().GetAnnotations("service-wordpress") if err != nil { return func() {}, err } c.Assert(ann, gc.DeepEquals, make(map[string]string)) return func() {}, nil } func opClientSetAnnotations(c *gc.C, st *api.State, mst *state.State) (func(), error) { pairs := map[string]string{"key1": "value1", "key2": "value2"} err := st.Client().SetAnnotations("service-wordpress", pairs) if err != nil { return func() {}, err } return func() { pairs := map[string]string{"key1": "", "key2": ""} st.Client().SetAnnotations("service-wordpress", pairs) }, nil } func opClientServiceDeploy(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceDeploy("mad:bad/url-1", "x", 1, "", constraints.Value{}, "") if err.Error() == `charm URL has invalid schema: "mad:bad/url-1"` { err = nil } return func() {}, err } func opClientServiceDeployWithNetworks(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceDeployWithNetworks("mad:bad/url-1", "x", 1, "", constraints.Value{}, "", nil, nil) if err.Error() == `charm URL has invalid schema: "mad:bad/url-1"` { err = nil } return func() {}, err } func opClientServiceUpdate(c *gc.C, st *api.State, mst *state.State) (func(), error) { args := params.ServiceUpdate{ ServiceName: "no-such-charm", CharmUrl: "cs:quantal/wordpress-42", ForceCharmUrl: true, SettingsStrings: map[string]string{"blog-title": "foo"}, SettingsYAML: `"wordpress": {"blog-title": "foo"}`, } err := st.Client().ServiceUpdate(args) if params.IsCodeNotFound(err) { err = nil } return func() {}, err } func opClientServiceSetCharm(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceSetCharm("nosuch", "local:quantal/wordpress", false) if params.IsCodeNotFound(err) { err = nil } return func() {}, err } func opClientAddServiceUnits(c *gc.C, st *api.State, mst *state.State) (func(), error) { _, err := st.Client().AddServiceUnits("nosuch", 1, "") if params.IsCodeNotFound(err) { err = nil } return func() {}, err } func opClientDestroyServiceUnits(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().DestroyServiceUnits("wordpress/99") if err != nil && strings.HasPrefix(err.Error(), "no units were destroyed") { err = nil } return func() {}, err } func opClientServiceDestroy(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceDestroy("non-existent") if params.IsCodeNotFound(err) { err = nil } return func() {}, err } func opClientGetServiceConstraints(c *gc.C, st *api.State, mst *state.State) (func(), error) { _, err := st.Client().GetServiceConstraints("wordpress") return func() {}, err } func opClientSetServiceConstraints(c *gc.C, st *api.State, mst *state.State) (func(), error) { nullConstraints := constraints.Value{} err := st.Client().SetServiceConstraints("wordpress", nullConstraints) if err != nil { return func() {}, err } return func() {}, nil } func opClientSetEnvironmentConstraints(c *gc.C, st *api.State, mst *state.State) (func(), error) { nullConstraints := constraints.Value{} err := st.Client().SetEnvironmentConstraints(nullConstraints) if err != nil { return func() {}, err } return func() {}, nil } func opClientEnvironmentGet(c *gc.C, st *api.State, mst *state.State) (func(), error) { _, err := st.Client().EnvironmentGet() if err != nil { return func() {}, err } return func() {}, nil } func opClientEnvironmentSet(c *gc.C, st *api.State, mst *state.State) (func(), error) { args := map[string]interface{}{"some-key": "some-value"} err := st.Client().EnvironmentSet(args) if err != nil { return func() {}, err } return func() { args["some-key"] = nil st.Client().EnvironmentSet(args) }, nil } func opClientSetEnvironAgentVersion(c *gc.C, st *api.State, mst *state.State) (func(), error) { attrs, err := st.Client().EnvironmentGet() if err != nil { return func() {}, err } err = st.Client().SetEnvironAgentVersion(version.Current.Number) if err != nil { return func() {}, err } return func() { oldAgentVersion, found := attrs["agent-version"] if found { versionString := oldAgentVersion.(string) st.Client().SetEnvironAgentVersion(version.MustParse(versionString)) } }, nil } func opClientWatchAll(c *gc.C, st *api.State, mst *state.State) (func(), error) { watcher, err := st.Client().WatchAll() if err == nil { watcher.Stop() } return func() {}, err } ���������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/run_test.go���������������������0000644�0000153�0000161�00000020434�12321735776�027470� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client_test import ( "fmt" "io/ioutil" "path/filepath" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/client" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils/exec" "launchpad.net/juju-core/utils/ssh" ) type runSuite struct { baseSuite } var _ = gc.Suite(&runSuite{}) func (s *runSuite) addMachine(c *gc.C) *state.Machine { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) return machine } func (s *runSuite) addMachineWithAddress(c *gc.C, address string) *state.Machine { machine := s.addMachine(c) machine.SetAddresses([]instance.Address{instance.NewAddress(address)}) return machine } func (s *runSuite) TestRemoteParamsForMachinePopulates(c *gc.C) { machine := s.addMachine(c) result := client.RemoteParamsForMachine(machine, "command", time.Minute) c.Assert(result.Command, gc.Equals, "command") c.Assert(result.Timeout, gc.Equals, time.Minute) c.Assert(result.MachineId, gc.Equals, machine.Id()) // Now an empty host isn't particularly useful, but the machine doesn't // have an address to use. c.Assert(machine.Addresses(), gc.HasLen, 0) c.Assert(result.Host, gc.Equals, "") } func (s *runSuite) TestRemoteParamsForMachinePopulatesWithAddress(c *gc.C) { machine := s.addMachineWithAddress(c, "10.3.2.1") result := client.RemoteParamsForMachine(machine, "command", time.Minute) c.Assert(result.Command, gc.Equals, "command") c.Assert(result.Timeout, gc.Equals, time.Minute) c.Assert(result.MachineId, gc.Equals, machine.Id()) c.Assert(result.Host, gc.Equals, "ubuntu@10.3.2.1") } func (s *runSuite) addUnit(c *gc.C, service *state.Service) *state.Unit { unit, err := service.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToNewMachine() c.Assert(err, gc.IsNil) mId, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) machine, err := s.State.Machine(mId) c.Assert(err, gc.IsNil) machine.SetAddresses([]instance.Address{instance.NewAddress("10.3.2.1")}) return unit } func (s *runSuite) TestGetAllUnitNames(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") magic, err := s.State.AddService("magic", "user-admin", charm, nil, nil) s.addUnit(c, magic) s.addUnit(c, magic) notAssigned, err := s.State.AddService("not-assigned", "user-admin", charm, nil, nil) c.Assert(err, gc.IsNil) _, err = notAssigned.AddUnit() c.Assert(err, gc.IsNil) _, err = s.State.AddService("no-units", "user-admin", charm, nil, nil) c.Assert(err, gc.IsNil) for i, test := range []struct { message string expected []string units []string services []string error string }{{ message: "no units, expected nil slice", }, { message: "asking for a unit that isn't there", units: []string{"foo/0"}, error: `unit "foo/0" not found`, }, { message: "asking for a service that isn't there", services: []string{"foo"}, error: `service "foo" not found`, }, { message: "service with no units is not really an error", services: []string{"no-units"}, }, { message: "A service with units not assigned is an error", services: []string{"not-assigned"}, error: `unit "not-assigned/0" is not assigned to a machine`, }, { message: "A service with units", services: []string{"magic"}, expected: []string{"magic/0", "magic/1"}, }, { message: "Asking for just a unit", units: []string{"magic/0"}, expected: []string{"magic/0"}, }, { message: "Asking for a unit, and the service", services: []string{"magic"}, units: []string{"magic/0"}, expected: []string{"magic/0", "magic/1"}, }} { c.Logf("%v: %s", i, test.message) result, err := client.GetAllUnitNames(s.State, test.units, test.services) if test.error == "" { c.Check(err, gc.IsNil) var units []string for _, unit := range result { units = append(units, unit.Name()) } c.Check(units, jc.SameContents, test.expected) } else { c.Check(err, gc.ErrorMatches, test.error) } } } func (s *runSuite) mockSSH(c *gc.C, cmd string) { testbin := c.MkDir() fakessh := filepath.Join(testbin, "ssh") s.PatchEnvPathPrepend(testbin) err := ioutil.WriteFile(fakessh, []byte(cmd), 0755) c.Assert(err, gc.IsNil) } func (s *runSuite) TestParallelExecuteErrorsOnBlankHost(c *gc.C) { s.mockSSH(c, echoInputShowArgs) params := []*client.RemoteExec{ &client.RemoteExec{ ExecParams: ssh.ExecParams{ Command: "foo", Timeout: testing.LongWait, }, }, } runResults := client.ParallelExecute("/some/dir", params) c.Assert(runResults.Results, gc.HasLen, 1) result := runResults.Results[0] c.Assert(result.Error, gc.Equals, "missing host address") } func (s *runSuite) TestParallelExecuteAddsIdentity(c *gc.C) { s.mockSSH(c, echoInputShowArgs) params := []*client.RemoteExec{ &client.RemoteExec{ ExecParams: ssh.ExecParams{ Host: "localhost", Command: "foo", Timeout: testing.LongWait, }, }, } runResults := client.ParallelExecute("/some/dir", params) c.Assert(runResults.Results, gc.HasLen, 1) result := runResults.Results[0] c.Assert(result.Error, gc.Equals, "") c.Assert(string(result.Stderr), jc.Contains, "-i /some/dir/system-identity") } func (s *runSuite) TestParallelExecuteCopiesAcrossMachineAndUnit(c *gc.C) { s.mockSSH(c, echoInputShowArgs) params := []*client.RemoteExec{ &client.RemoteExec{ ExecParams: ssh.ExecParams{ Host: "localhost", Command: "foo", Timeout: testing.LongWait, }, MachineId: "machine-id", UnitId: "unit-id", }, } runResults := client.ParallelExecute("/some/dir", params) c.Assert(runResults.Results, gc.HasLen, 1) result := runResults.Results[0] c.Assert(result.Error, gc.Equals, "") c.Assert(result.MachineId, gc.Equals, "machine-id") c.Assert(result.UnitId, gc.Equals, "unit-id") } func (s *runSuite) TestRunOnAllMachines(c *gc.C) { // Make three machines. s.addMachineWithAddress(c, "10.3.2.1") s.addMachineWithAddress(c, "10.3.2.2") s.addMachineWithAddress(c, "10.3.2.3") s.mockSSH(c, echoInput) // hmm... this seems to be going through the api client, and from there // through to the apiserver implementation. Not ideal, but it is how the // other client tests are written. client := s.APIState.Client() results, err := client.RunOnAllMachines("hostname", testing.LongWait) c.Assert(err, gc.IsNil) c.Assert(results, gc.HasLen, 3) var expectedResults []params.RunResult for i := 0; i < 3; i++ { expectedResults = append(expectedResults, params.RunResult{ ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run --no-context 'hostname'\n")}, MachineId: fmt.Sprint(i), }) } c.Assert(results, jc.DeepEquals, expectedResults) } func (s *runSuite) TestRunMachineAndService(c *gc.C) { // Make three machines. s.addMachineWithAddress(c, "10.3.2.1") charm := s.AddTestingCharm(c, "dummy") magic, err := s.State.AddService("magic", "user-admin", charm, nil, nil) s.addUnit(c, magic) s.addUnit(c, magic) s.mockSSH(c, echoInput) // hmm... this seems to be going through the api client, and from there // through to the apiserver implementation. Not ideal, but it is how the // other client tests are written. client := s.APIState.Client() results, err := client.Run( params.RunParams{ Commands: "hostname", Timeout: testing.LongWait, Machines: []string{"0"}, Services: []string{"magic"}, }) c.Assert(err, gc.IsNil) c.Assert(results, gc.HasLen, 3) expectedResults := []params.RunResult{ params.RunResult{ ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run --no-context 'hostname'\n")}, MachineId: "0", }, params.RunResult{ ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run magic/0 'hostname'\n")}, MachineId: "1", UnitId: "magic/0", }, params.RunResult{ ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run magic/1 'hostname'\n")}, MachineId: "2", UnitId: "magic/1", }, } c.Assert(results, jc.DeepEquals, expectedResults) } var echoInputShowArgs = `#!/bin/bash # Write the args to stderr echo "$*" >&2 # And echo stdin to stdout while read line do echo $line done <&0 ` var echoInput = `#!/bin/bash # And echo stdin to stdout while read line do echo $line done <&0 ` ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/tools.go������������������������0000644�0000153�0000161�00000001172�12321735642�026753� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client import ( "fmt" "launchpad.net/juju-core/environs" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/tools" ) func findInstanceTools(env environs.Environ, series, arch string) (*tools.Tools, error) { agentVersion, ok := env.Config().AgentVersion() if !ok { return nil, fmt.Errorf("no agent version set in environment configuration") } possibleTools, err := envtools.FindInstanceTools(env, agentVersion, series, &arch) if err != nil { return nil, err } return possibleTools[0], nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/get.go��������������������������0000644�0000153�0000161�00000003671�12321735642�026400� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client import ( "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/state/api/params" ) // ServiceGet returns the configuration for a service. func (c *Client) ServiceGet(args params.ServiceGet) (params.ServiceGetResults, error) { service, err := c.api.state.Service(args.ServiceName) if err != nil { return params.ServiceGetResults{}, err } settings, err := service.ConfigSettings() if err != nil { return params.ServiceGetResults{}, err } charm, _, err := service.Charm() if err != nil { return params.ServiceGetResults{}, err } configInfo := describe(settings, charm.Config()) var constraints constraints.Value if service.IsPrincipal() { constraints, err = service.Constraints() if err != nil { return params.ServiceGetResults{}, err } } return params.ServiceGetResults{ Service: args.ServiceName, Charm: charm.Meta().Name, Config: configInfo, Constraints: constraints, }, nil } func describe(settings charm.Settings, config *charm.Config) map[string]interface{} { results := make(map[string]interface{}) for name, option := range config.Options { info := map[string]interface{}{ "description": option.Description, "type": option.Type, } if value := settings[name]; value != nil { info["value"] = value } else { if option.Default != nil { info["value"] = option.Default } info["default"] = true } results[name] = info } return results } // ServiceGetCharmURL returns the charm URL the given service is // running at present. func (c *Client) ServiceGetCharmURL(args params.ServiceGet) (params.StringResult, error) { service, err := c.api.state.Service(args.ServiceName) if err != nil { return params.StringResult{}, err } charmURL, _ := service.CharmURL() return params.StringResult{Result: charmURL.String()}, nil } �����������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/destroy.go����������������������0000644�0000153�0000161�00000010257�12321735642�027310� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client import ( "fmt" "strings" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" ) // DestroyEnvironment destroys all services and non-manager machine // instances in the environment. func (c *Client) DestroyEnvironment() error { // TODO(axw) 2013-08-30 bug 1218688 // // There's a race here: a client might add a manual machine // after another client checks. Destroy-environment will // still fail, but the environment will be in a state where // entities can only be destroyed. In the future we should // introduce a method of preventing Environment.Destroy() // from succeeding if machines have been added. // First, check for manual machines. We bail out if there are any, // to stop the user from prematurely hobbling the environment. machines, err := c.api.state.AllMachines() if err != nil { return err } if err := checkManualMachines(machines); err != nil { return err } // Set the environment to Dying, to lock out new machines and services. // Environment.Destroy() also schedules a cleanup for existing services. // Afterwards, refresh the machines in case any were added between the // first check and the Environment.Destroy(). env, err := c.api.state.Environment() if err != nil { return err } if err = env.Destroy(); err != nil { return err } machines, err = c.api.state.AllMachines() if err != nil { return err } // We must destroy instances server-side to support hosted Juju, // as there's no CLI to fall back on. In that case, we only ever // destroy non-state machines; we leave destroying state servers // in non-hosted environments to the CLI, as otherwise the API // server may get cut off. if err := destroyInstances(c.api.state, machines); err != nil { return err } // Make sure once again that there are no manually provisioned // non-manager machines. This caters for the race between the // first check and the Environment.Destroy(). if err := checkManualMachines(machines); err != nil { return err } // Return to the caller. If it's the CLI, it will finish up // by calling the provider's Destroy method, which will // destroy the state servers, any straggler instances, and // other provider-specific resources. return nil } // destroyInstances directly destroys all non-manager, // non-manual machine instances. func destroyInstances(st *state.State, machines []*state.Machine) error { var ids []instance.Id for _, m := range machines { if m.IsManager() { continue } manual, err := m.IsManual() if manual { continue } else if err != nil { return err } id, err := m.InstanceId() if err != nil { continue } ids = append(ids, id) } if len(ids) == 0 { return nil } envcfg, err := st.EnvironConfig() if err != nil { return err } env, err := environs.New(envcfg) if err != nil { return err } // TODO(axw) 2013-12-12 #1260171 // Modify InstanceBroker.StopInstances to take // a slice of IDs rather than Instances. instances, err := env.Instances(ids) switch err { case nil: default: return err case environs.ErrNoInstances: return nil case environs.ErrPartialInstances: var nonNilInstances []instance.Instance for i, inst := range instances { if inst == nil { logger.Warningf("unknown instance ID: %v", ids[i]) continue } nonNilInstances = append(nonNilInstances, inst) } instances = nonNilInstances } return env.StopInstances(instances) } // checkManualMachines checks if any of the machines in the slice were // manually provisioned, and are non-manager machines. These machines // must (currently) be manually destroyed via destroy-machine before // destroy-environment can successfully complete. func checkManualMachines(machines []*state.Machine) error { var ids []string for _, m := range machines { if m.IsManager() { continue } manual, err := m.IsManual() if err != nil { return err } if manual { ids = append(ids, m.Id()) } } if len(ids) > 0 { return fmt.Errorf("manually provisioned machines must first be destroyed with `juju destroy-machine %s`", strings.Join(ids, " ")) } return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/api_test.go���������������������0000644�0000153�0000161�00000022462�12321735776�027440� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client_test import ( "fmt" stdtesting "testing" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type baseSuite struct { testing.JujuConnSuite } var _ = gc.Suite(&baseSuite{}) func chanReadEmpty(c *gc.C, ch <-chan struct{}, what string) bool { select { case _, ok := <-ch: return ok case <-time.After(10 * time.Second): c.Fatalf("timed out reading from %s", what) } panic("unreachable") } func chanReadStrings(c *gc.C, ch <-chan []string, what string) ([]string, bool) { select { case changes, ok := <-ch: return changes, ok case <-time.After(10 * time.Second): c.Fatalf("timed out reading from %s", what) } panic("unreachable") } func chanReadConfig(c *gc.C, ch <-chan *config.Config, what string) (*config.Config, bool) { select { case envConfig, ok := <-ch: return envConfig, ok case <-time.After(10 * time.Second): c.Fatalf("timed out reading from %s", what) } panic("unreachable") } func removeServiceAndUnits(c *gc.C, service *state.Service) { // Destroy all units for the service. units, err := service.AllUnits() c.Assert(err, gc.IsNil) for _, unit := range units { err = unit.EnsureDead() c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.IsNil) } err = service.Destroy() c.Assert(err, gc.IsNil) err = service.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } // apiAuthenticator represents a simple authenticator object with only the // SetPassword and Tag methods. This will fit types from both the state // and api packages, as those in the api package do not have PasswordValid(). type apiAuthenticator interface { state.Entity SetPassword(string) error } func setDefaultPassword(c *gc.C, e apiAuthenticator) { err := e.SetPassword(defaultPassword(e)) c.Assert(err, gc.IsNil) } func defaultPassword(e apiAuthenticator) string { return e.Tag() + " password-1234567890" } type setStatuser interface { SetStatus(status params.Status, info string, data params.StatusData) error } func setDefaultStatus(c *gc.C, entity setStatuser) { err := entity.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) } func (s *baseSuite) tryOpenState(c *gc.C, e apiAuthenticator, password string) error { stateInfo := s.StateInfo(c) stateInfo.Tag = e.Tag() stateInfo.Password = password st, err := state.Open(stateInfo, state.DialOpts{ Timeout: 25 * time.Millisecond, }, environs.NewStatePolicy()) if err == nil { st.Close() } return err } // openAs connects to the API state as the given entity // with the default password for that entity. func (s *baseSuite) openAs(c *gc.C, tag string) *api.State { _, info, err := s.APIConn.Environ.StateInfo() c.Assert(err, gc.IsNil) info.Tag = tag // Must match defaultPassword() info.Password = fmt.Sprintf("%s password-1234567890", tag) // Set this always, so that the login attempts as a machine will // not fail with ErrNotProvisioned; it's not used otherwise. info.Nonce = "fake_nonce" c.Logf("opening state; entity %q; password %q", info.Tag, info.Password) st, err := api.Open(info, api.DialOpts{}) c.Assert(err, gc.IsNil) c.Assert(st, gc.NotNil) return st } // scenarioStatus describes the expected state // of the juju environment set up by setUpScenario. // // NOTE: AgentState: "down", AgentStateInfo: "(started)" here is due // to the scenario not calling SetAgentAlive on the respective entities, // but this behavior is already tested in cmd/juju/status_test.go and // also tested live and it works. var scenarioStatus = &api.Status{ EnvironmentName: "dummyenv", Machines: map[string]api.MachineStatus{ "0": { Id: "0", InstanceId: instance.Id("i-machine-0"), AgentState: "down", AgentStateInfo: "(started)", Series: "quantal", Containers: map[string]api.MachineStatus{}, }, "1": { Id: "1", InstanceId: instance.Id("i-machine-1"), AgentState: "down", AgentStateInfo: "(started)", Series: "quantal", Containers: map[string]api.MachineStatus{}, }, "2": { Id: "2", InstanceId: instance.Id("i-machine-2"), AgentState: "down", AgentStateInfo: "(started)", Series: "quantal", Containers: map[string]api.MachineStatus{}, }, }, Services: map[string]api.ServiceStatus{ "logging": api.ServiceStatus{ Charm: "local:quantal/logging-1", Relations: map[string][]string{ "logging-directory": []string{"wordpress"}, }, SubordinateTo: []string{"wordpress"}, }, "mysql": api.ServiceStatus{ Charm: "local:quantal/mysql-1", Relations: map[string][]string{}, SubordinateTo: []string{}, Units: map[string]api.UnitStatus{}, }, "wordpress": api.ServiceStatus{ Charm: "local:quantal/wordpress-3", Relations: map[string][]string{ "logging-dir": []string{"logging"}, }, SubordinateTo: []string{}, Units: map[string]api.UnitStatus{ "wordpress/0": api.UnitStatus{ AgentState: "pending", Machine: "1", Subordinates: map[string]api.UnitStatus{ "logging/0": api.UnitStatus{ AgentState: "pending", }, }, }, "wordpress/1": api.UnitStatus{ AgentState: "pending", Machine: "2", Subordinates: map[string]api.UnitStatus{ "logging/1": api.UnitStatus{ AgentState: "pending", }, }, }, }, }, }, } // setUpScenario makes an environment scenario suitable for // testing most kinds of access scenario. It returns // a list of all the entities in the scenario. // // When the scenario is initialized, we have: // user-admin // user-other // machine-0 // instance-id="i-machine-0" // nonce="fake_nonce" // jobs=manage-environ // status=started, info="" // machine-1 // instance-id="i-machine-1" // nonce="fake_nonce" // jobs=host-units // status=started, info="" // constraints=mem=1G // machine-2 // instance-id="i-machine-2" // nonce="fake_nonce" // jobs=host-units // status=started, info="" // service-wordpress // service-logging // unit-wordpress-0 // deployer-name=machine-1 // unit-logging-0 // deployer-name=unit-wordpress-0 // unit-wordpress-1 // deployer-name=machine-2 // unit-logging-1 // deployer-name=unit-wordpress-1 // // The passwords for all returned entities are // set to the entity name with a " password" suffix. // // Note that there is nothing special about machine-0 // here - it's the environment manager in this scenario // just because machine 0 has traditionally been the // environment manager (bootstrap machine), so is // hopefully easier to remember as such. func (s *baseSuite) setUpScenario(c *gc.C) (entities []string) { add := func(e state.Entity) { entities = append(entities, e.Tag()) } u, err := s.State.User(state.AdminUser) c.Assert(err, gc.IsNil) setDefaultPassword(c, u) add(u) u, err = s.State.AddUser("other", "") c.Assert(err, gc.IsNil) setDefaultPassword(c, u) add(u) m, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) c.Assert(m.Tag(), gc.Equals, "machine-0") err = m.SetProvisioned(instance.Id("i-"+m.Tag()), "fake_nonce", nil) c.Assert(err, gc.IsNil) setDefaultPassword(c, m) setDefaultStatus(c, m) add(m) s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"logging", "wordpress"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) for i := 0; i < 2; i++ { wu, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) c.Assert(wu.Tag(), gc.Equals, fmt.Sprintf("unit-wordpress-%d", i)) setDefaultPassword(c, wu) add(wu) m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(m.Tag(), gc.Equals, fmt.Sprintf("machine-%d", i+1)) if i == 1 { err = m.SetConstraints(constraints.MustParse("mem=1G")) c.Assert(err, gc.IsNil) } err = m.SetProvisioned(instance.Id("i-"+m.Tag()), "fake_nonce", nil) c.Assert(err, gc.IsNil) setDefaultPassword(c, m) setDefaultStatus(c, m) add(m) err = wu.AssignToMachine(m) c.Assert(err, gc.IsNil) deployer, ok := wu.DeployerTag() c.Assert(ok, gc.Equals, true) c.Assert(deployer, gc.Equals, fmt.Sprintf("machine-%d", i+1)) wru, err := rel.Unit(wu) c.Assert(err, gc.IsNil) // Create the subordinate unit as a side-effect of entering // scope in the principal's relation-unit. err = wru.EnterScope(nil) c.Assert(err, gc.IsNil) lu, err := s.State.Unit(fmt.Sprintf("logging/%d", i)) c.Assert(err, gc.IsNil) c.Assert(lu.IsPrincipal(), gc.Equals, false) deployer, ok = lu.DeployerTag() c.Assert(ok, gc.Equals, true) c.Assert(deployer, gc.Equals, fmt.Sprintf("unit-wordpress-%d", i)) setDefaultPassword(c, lu) add(lu) } return } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/run.go��������������������������0000644�0000153�0000161�00000013444�12321735642�026424� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client import ( "fmt" "path/filepath" "sort" "sync" "time" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/utils/ssh" ) // remoteParamsForMachine returns a filled in RemoteExec instance // based on the machine, command and timeout params. If the machine // does not have an internal address, the Host is empty. This is caught // by the function that actually tries to execute the command. func remoteParamsForMachine(machine *state.Machine, command string, timeout time.Duration) *RemoteExec { // magic boolean parameters are bad :-( address := instance.SelectInternalAddress(machine.Addresses(), false) execParams := &RemoteExec{ ExecParams: ssh.ExecParams{ Command: command, Timeout: timeout, }, MachineId: machine.Id(), } if address != "" { execParams.Host = fmt.Sprintf("ubuntu@%s", address) } return execParams } // getAllUnitNames returns a sequence of valid Unit objects from state. If any // of the service names or unit names are not found, an error is returned. func getAllUnitNames(st *state.State, units, services []string) (result []*state.Unit, err error) { unitsSet := set.NewStrings(units...) for _, name := range services { service, err := st.Service(name) if err != nil { return nil, err } units, err := service.AllUnits() if err != nil { return nil, err } for _, unit := range units { unitsSet.Add(unit.Name()) } } for _, unitName := range unitsSet.Values() { unit, err := st.Unit(unitName) if err != nil { return nil, err } // We only operate on principal units, and only thise that have an // assigned machines. if unit.IsPrincipal() { if _, err := unit.AssignedMachineId(); err != nil { return nil, err } } else { return nil, fmt.Errorf("%s is not a principal unit", unit) } result = append(result, unit) } return result, nil } // Run the commands specified on the machines identified through the // list of machines, units and services. func (c *Client) Run(run params.RunParams) (results params.RunResults, err error) { units, err := getAllUnitNames(c.api.state, run.Units, run.Services) if err != nil { return results, err } // We want to create a RemoteExec for each unit and each machine. // If we have both a unit and a machine request, we run it twice, // once for the unit inside the exec context using juju-run, and // the other outside the context just using bash. var params []*RemoteExec var quotedCommands = utils.ShQuote(run.Commands) for _, unit := range units { // We know that the unit is both a principal unit, and that it has an // assigned machine. machineId, _ := unit.AssignedMachineId() machine, err := c.api.state.Machine(machineId) if err != nil { return results, err } command := fmt.Sprintf("juju-run %s %s", unit.Name(), quotedCommands) execParam := remoteParamsForMachine(machine, command, run.Timeout) execParam.UnitId = unit.Name() params = append(params, execParam) } for _, machineId := range run.Machines { machine, err := c.api.state.Machine(machineId) if err != nil { return results, err } command := fmt.Sprintf("juju-run --no-context %s", quotedCommands) execParam := remoteParamsForMachine(machine, command, run.Timeout) params = append(params, execParam) } return ParallelExecute(c.api.dataDir, params), nil } // RunOnAllMachines attempts to run the specified command on all the machines. func (c *Client) RunOnAllMachines(run params.RunParams) (params.RunResults, error) { machines, err := c.api.state.AllMachines() if err != nil { return params.RunResults{}, err } var params []*RemoteExec quotedCommands := utils.ShQuote(run.Commands) command := fmt.Sprintf("juju-run --no-context %s", quotedCommands) for _, machine := range machines { params = append(params, remoteParamsForMachine(machine, command, run.Timeout)) } return ParallelExecute(c.api.dataDir, params), nil } // RemoteExec extends the standard ssh.ExecParams by providing the machine and // perhaps the unit ids. These are then returned in the params.RunResult return // values. type RemoteExec struct { ssh.ExecParams MachineId string UnitId string } // ParallelExecute executes all of the requests defined in the params, // using the system identity stored in the dataDir. func ParallelExecute(dataDir string, runParams []*RemoteExec) params.RunResults { logger.Debugf("exec %#v", runParams) var outstanding sync.WaitGroup var lock sync.Mutex var result []params.RunResult identity := filepath.Join(dataDir, cloudinit.SystemIdentity) for _, param := range runParams { outstanding.Add(1) logger.Debugf("exec on %s: %#v", param.MachineId, *param) param.IdentityFile = identity go func(param *RemoteExec) { response, err := ssh.ExecuteCommandOnMachine(param.ExecParams) logger.Debugf("reponse from %s: %v (err:%v)", param.MachineId, response, err) execResponse := params.RunResult{ ExecResponse: response, MachineId: param.MachineId, UnitId: param.UnitId, } if err != nil { execResponse.Error = fmt.Sprint(err) } lock.Lock() defer lock.Unlock() result = append(result, execResponse) outstanding.Done() }(param) } outstanding.Wait() sort.Sort(MachineOrder(result)) return params.RunResults{result} } // MachineOrder is used to provide the api to sort the results by the machine // id. type MachineOrder []params.RunResult func (a MachineOrder) Len() int { return len(a) } func (a MachineOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a MachineOrder) Less(i, j int) bool { return a[i].MachineId < a[j].MachineId } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/client.go�����������������������0000644�0000153�0000161�00000075016�12321735776�027111� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client import ( "fmt" "net/url" "os" "strings" "github.com/errgo/errgo" "github.com/juju/loggo" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/manual" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/statecmd" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" ) var logger = loggo.GetLogger("juju.state.apiserver.client") type API struct { state *state.State auth common.Authorizer resources *common.Resources client *Client dataDir string // statusSetter provides common methods for updating an entity's provisioning status. statusSetter *common.StatusSetter } // Client serves client-specific API methods. type Client struct { api *API } // NewAPI creates a new instance of the Client API. func NewAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer, datadir string) *API { r := &API{ state: st, auth: authorizer, resources: resources, dataDir: datadir, statusSetter: common.NewStatusSetter(st, common.AuthAlways(true)), } r.client = &Client{ api: r, } return r } // Client returns an object that provides access // to methods accessible to non-agent clients. func (r *API) Client(id string) (*Client, error) { if !r.auth.AuthClient() { return nil, common.ErrPerm } if id != "" { // Safeguard id for possible future use. return nil, common.ErrBadId } return r.client, nil } func (c *Client) WatchAll() (params.AllWatcherId, error) { w := c.api.state.Watch() return params.AllWatcherId{ AllWatcherId: c.api.resources.Register(w), }, nil } // ServiceSet implements the server side of Client.ServiceSet. Values set to an // empty string will be unset. // // (Deprecated) Use NewServiceSetForClientAPI instead, to preserve values set to // an empty string, and use ServiceUnset to unset values. func (c *Client) ServiceSet(p params.ServiceSet) error { svc, err := c.api.state.Service(p.ServiceName) if err != nil { return err } return serviceSetSettingsStrings(svc, p.Options) } // NewServiceSetForClientAPI implements the server side of // Client.NewServiceSetForClientAPI. This is exactly like ServiceSet except that // it does not unset values that are set to an empty string. ServiceUnset // should be used for that. // // TODO(Nate): rename this to ServiceSet (and remove the deprecated ServiceSet) // when the GUI handles the new behavior. func (c *Client) NewServiceSetForClientAPI(p params.ServiceSet) error { svc, err := c.api.state.Service(p.ServiceName) if err != nil { return err } return newServiceSetSettingsStringsForClientAPI(svc, p.Options) } // ServiceUnset implements the server side of Client.ServiceUnset. func (c *Client) ServiceUnset(p params.ServiceUnset) error { svc, err := c.api.state.Service(p.ServiceName) if err != nil { return err } settings := make(charm.Settings) for _, option := range p.Options { settings[option] = nil } return svc.UpdateConfigSettings(settings) } // ServiceSetYAML implements the server side of Client.ServerSetYAML. func (c *Client) ServiceSetYAML(p params.ServiceSetYAML) error { svc, err := c.api.state.Service(p.ServiceName) if err != nil { return err } return serviceSetSettingsYAML(svc, p.Config) } // ServiceCharmRelations implements the server side of Client.ServiceCharmRelations. func (c *Client) ServiceCharmRelations(p params.ServiceCharmRelations) (params.ServiceCharmRelationsResults, error) { var results params.ServiceCharmRelationsResults service, err := c.api.state.Service(p.ServiceName) if err != nil { return results, err } endpoints, err := service.Endpoints() if err != nil { return results, err } results.CharmRelations = make([]string, len(endpoints)) for i, endpoint := range endpoints { results.CharmRelations[i] = endpoint.Relation.Name } return results, nil } // Resolved implements the server side of Client.Resolved. func (c *Client) Resolved(p params.Resolved) error { unit, err := c.api.state.Unit(p.UnitName) if err != nil { return err } return unit.Resolve(p.Retry) } // PublicAddress implements the server side of Client.PublicAddress. func (c *Client) PublicAddress(p params.PublicAddress) (results params.PublicAddressResults, err error) { switch { case names.IsMachine(p.Target): machine, err := c.api.state.Machine(p.Target) if err != nil { return results, err } addr := instance.SelectPublicAddress(machine.Addresses()) if addr == "" { return results, fmt.Errorf("machine %q has no public address", machine) } return params.PublicAddressResults{PublicAddress: addr}, nil case names.IsUnit(p.Target): unit, err := c.api.state.Unit(p.Target) if err != nil { return results, err } addr, ok := unit.PublicAddress() if !ok { return results, fmt.Errorf("unit %q has no public address", unit) } return params.PublicAddressResults{PublicAddress: addr}, nil } return results, fmt.Errorf("unknown unit or machine %q", p.Target) } // PrivateAddress implements the server side of Client.PrivateAddress. func (c *Client) PrivateAddress(p params.PrivateAddress) (results params.PrivateAddressResults, err error) { switch { case names.IsMachine(p.Target): machine, err := c.api.state.Machine(p.Target) if err != nil { return results, err } addr := instance.SelectInternalAddress(machine.Addresses(), false) if addr == "" { return results, fmt.Errorf("machine %q has no internal address", machine) } return params.PrivateAddressResults{PrivateAddress: addr}, nil case names.IsUnit(p.Target): unit, err := c.api.state.Unit(p.Target) if err != nil { return results, err } addr, ok := unit.PrivateAddress() if !ok { return results, fmt.Errorf("unit %q has no internal address", unit) } return params.PrivateAddressResults{PrivateAddress: addr}, nil } return results, fmt.Errorf("unknown unit or machine %q", p.Target) } // ServiceExpose changes the juju-managed firewall to expose any ports that // were also explicitly marked by units as open. func (c *Client) ServiceExpose(args params.ServiceExpose) error { svc, err := c.api.state.Service(args.ServiceName) if err != nil { return err } return svc.SetExposed() } // ServiceUnexpose changes the juju-managed firewall to unexpose any ports that // were also explicitly marked by units as open. func (c *Client) ServiceUnexpose(args params.ServiceUnexpose) error { svc, err := c.api.state.Service(args.ServiceName) if err != nil { return err } return svc.ClearExposed() } var CharmStore charm.Repository = charm.Store // ServiceDeploy fetches the charm from the charm store and deploys it. // AddCharm or AddLocalCharm should be called to add the charm // before calling ServiceDeploy, although for backward compatibility // this is not necessary until 1.16 support is removed. func (c *Client) ServiceDeploy(args params.ServiceDeploy) error { curl, err := charm.ParseURL(args.CharmUrl) if err != nil { return err } if curl.Revision < 0 { return fmt.Errorf("charm url must include revision") } // Try to find the charm URL in state first. ch, err := c.api.state.Charm(curl) if errors.IsNotFoundError(err) { // Remove this whole if block when 1.16 compatibility is dropped. if curl.Schema != "cs" { return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema) } err = c.AddCharm(params.CharmURL{args.CharmUrl}) if err != nil { return err } ch, err = c.api.state.Charm(curl) if err != nil { return err } } else if err != nil { return err } var settings charm.Settings if len(args.ConfigYAML) > 0 { settings, err = ch.Config().ParseSettingsYAML([]byte(args.ConfigYAML), args.ServiceName) } else if len(args.Config) > 0 { // Parse config in a compatile way (see function comment). settings, err = parseSettingsCompatible(ch, args.Config) } if err != nil { return err } _, err = juju.DeployService(c.api.state, juju.DeployServiceParams{ ServiceName: args.ServiceName, Charm: ch, NumUnits: args.NumUnits, ConfigSettings: settings, Constraints: args.Constraints, ToMachineSpec: args.ToMachineSpec, IncludeNetworks: args.IncludeNetworks, ExcludeNetworks: args.ExcludeNetworks, }) return err } // ServiceDeployWithNetworks works exactly like ServiceDeploy, but // allows specifying networks to include or exclude on the machine // where the charm gets deployed. func (c *Client) ServiceDeployWithNetworks(args params.ServiceDeploy) error { return c.ServiceDeploy(args) } // ServiceUpdate updates the service attributes, including charm URL, // minimum number of units, settings and constraints. // All parameters in params.ServiceUpdate except the service name are optional. func (c *Client) ServiceUpdate(args params.ServiceUpdate) error { service, err := c.api.state.Service(args.ServiceName) if err != nil { return err } // Set the charm for the given service. if args.CharmUrl != "" { if err = c.serviceSetCharm(service, args.CharmUrl, args.ForceCharmUrl); err != nil { return err } } // Set the minimum number of units for the given service. if args.MinUnits != nil { if err = service.SetMinUnits(*args.MinUnits); err != nil { return err } } // Set up service's settings. if args.SettingsYAML != "" { if err = serviceSetSettingsYAML(service, args.SettingsYAML); err != nil { return err } } else if len(args.SettingsStrings) > 0 { if err = serviceSetSettingsStrings(service, args.SettingsStrings); err != nil { return err } } // Update service's constraints. if args.Constraints != nil { return service.SetConstraints(*args.Constraints) } return nil } // serviceSetCharm sets the charm for the given service. func (c *Client) serviceSetCharm(service *state.Service, url string, force bool) error { curl, err := charm.ParseURL(url) if err != nil { return err } sch, err := c.api.state.Charm(curl) if errors.IsNotFoundError(err) { // Charms should be added before trying to use them, with // AddCharm or AddLocalCharm API calls. When they're not, // we're reverting to 1.16 compatibility mode. return c.serviceSetCharm1dot16(service, curl, force) } if err != nil { return err } return service.SetCharm(sch, force) } // serviceSetCharm1dot16 sets the charm for the given service in 1.16 // compatibility mode. Remove this when support for 1.16 is dropped. func (c *Client) serviceSetCharm1dot16(service *state.Service, curl *charm.URL, force bool) error { if curl.Schema != "cs" { return fmt.Errorf(`charm url has unsupported schema %q`, curl.Schema) } if curl.Revision < 0 { return fmt.Errorf("charm url must include revision") } err := c.AddCharm(params.CharmURL{curl.String()}) if err != nil { return err } ch, err := c.api.state.Charm(curl) if err != nil { return err } return service.SetCharm(ch, force) } // serviceSetSettingsYAML updates the settings for the given service, // taking the configuration from a YAML string. func serviceSetSettingsYAML(service *state.Service, settings string) error { ch, _, err := service.Charm() if err != nil { return err } changes, err := ch.Config().ParseSettingsYAML([]byte(settings), service.Name()) if err != nil { return err } return service.UpdateConfigSettings(changes) } // serviceSetSettingsStrings updates the settings for the given service, // taking the configuration from a map of strings. func serviceSetSettingsStrings(service *state.Service, settings map[string]string) error { ch, _, err := service.Charm() if err != nil { return err } // Parse config in a compatible way (see function comment). changes, err := parseSettingsCompatible(ch, settings) if err != nil { return err } return service.UpdateConfigSettings(changes) } // newServiceSetSettingsStringsForClientAPI updates the settings for the given // service, taking the configuration from a map of strings. // // TODO(Nate): replace serviceSetSettingsStrings with this onces the GUI no // longer expects to be able to unset values by sending an empty string. func newServiceSetSettingsStringsForClientAPI(service *state.Service, settings map[string]string) error { ch, _, err := service.Charm() if err != nil { return err } // Validate the settings. changes, err := ch.Config().ParseSettingsStrings(settings) if err != nil { return err } return service.UpdateConfigSettings(changes) } // ServiceSetCharm sets the charm for a given service. func (c *Client) ServiceSetCharm(args params.ServiceSetCharm) error { service, err := c.api.state.Service(args.ServiceName) if err != nil { return err } return c.serviceSetCharm(service, args.CharmUrl, args.Force) } // addServiceUnits adds a given number of units to a service. func addServiceUnits(state *state.State, args params.AddServiceUnits) ([]*state.Unit, error) { service, err := state.Service(args.ServiceName) if err != nil { return nil, err } if args.NumUnits < 1 { return nil, fmt.Errorf("must add at least one unit") } if args.NumUnits > 1 && args.ToMachineSpec != "" { return nil, fmt.Errorf("cannot use NumUnits with ToMachineSpec") } return juju.AddUnits(state, service, args.NumUnits, args.ToMachineSpec) } // AddServiceUnits adds a given number of units to a service. func (c *Client) AddServiceUnits(args params.AddServiceUnits) (params.AddServiceUnitsResults, error) { units, err := addServiceUnits(c.api.state, args) if err != nil { return params.AddServiceUnitsResults{}, err } unitNames := make([]string, len(units)) for i, unit := range units { unitNames[i] = unit.String() } return params.AddServiceUnitsResults{Units: unitNames}, nil } // DestroyServiceUnits removes a given set of service units. func (c *Client) DestroyServiceUnits(args params.DestroyServiceUnits) error { var errs []string for _, name := range args.UnitNames { unit, err := c.api.state.Unit(name) switch { case errors.IsNotFoundError(err): err = fmt.Errorf("unit %q does not exist", name) case err != nil: case unit.Life() != state.Alive: continue case unit.IsPrincipal(): err = unit.Destroy() default: err = fmt.Errorf("unit %q is a subordinate", name) } if err != nil { errs = append(errs, err.Error()) } } return destroyErr("units", args.UnitNames, errs) } // ServiceDestroy destroys a given service. func (c *Client) ServiceDestroy(args params.ServiceDestroy) error { svc, err := c.api.state.Service(args.ServiceName) if err != nil { return err } return svc.Destroy() } // GetServiceConstraints returns the constraints for a given service. func (c *Client) GetServiceConstraints(args params.GetServiceConstraints) (params.GetConstraintsResults, error) { svc, err := c.api.state.Service(args.ServiceName) if err != nil { return params.GetConstraintsResults{}, err } cons, err := svc.Constraints() return params.GetConstraintsResults{cons}, err } // GetEnvironmentConstraints returns the constraints for the environment. func (c *Client) GetEnvironmentConstraints() (params.GetConstraintsResults, error) { cons, err := c.api.state.EnvironConstraints() if err != nil { return params.GetConstraintsResults{}, err } return params.GetConstraintsResults{cons}, nil } // SetServiceConstraints sets the constraints for a given service. func (c *Client) SetServiceConstraints(args params.SetConstraints) error { svc, err := c.api.state.Service(args.ServiceName) if err != nil { return err } return svc.SetConstraints(args.Constraints) } // SetEnvironmentConstraints sets the constraints for the environment. func (c *Client) SetEnvironmentConstraints(args params.SetConstraints) error { return c.api.state.SetEnvironConstraints(args.Constraints) } // AddRelation adds a relation between the specified endpoints and returns the relation info. func (c *Client) AddRelation(args params.AddRelation) (params.AddRelationResults, error) { inEps, err := c.api.state.InferEndpoints(args.Endpoints) if err != nil { return params.AddRelationResults{}, err } rel, err := c.api.state.AddRelation(inEps...) if err != nil { return params.AddRelationResults{}, err } outEps := make(map[string]charm.Relation) for _, inEp := range inEps { outEp, err := rel.Endpoint(inEp.ServiceName) if err != nil { return params.AddRelationResults{}, err } outEps[inEp.ServiceName] = outEp.Relation } return params.AddRelationResults{Endpoints: outEps}, nil } // DestroyRelation removes the relation between the specified endpoints. func (c *Client) DestroyRelation(args params.DestroyRelation) error { eps, err := c.api.state.InferEndpoints(args.Endpoints) if err != nil { return err } rel, err := c.api.state.EndpointsRelation(eps...) if err != nil { return err } return rel.Destroy() } // AddMachines adds new machines with the supplied parameters. func (c *Client) AddMachines(args params.AddMachines) (params.AddMachinesResults, error) { results := params.AddMachinesResults{ Machines: make([]params.AddMachinesResult, len(args.MachineParams)), } for i, p := range args.MachineParams { m, err := c.addOneMachine(p) results.Machines[i].Error = common.ServerError(err) if err == nil { results.Machines[i].Machine = m.Id() } } return results, nil } // InjectMachines injects a machine into state with provisioned status. func (c *Client) InjectMachines(args params.AddMachines) (params.AddMachinesResults, error) { return c.AddMachines(args) } func (c *Client) addOneMachine(p params.AddMachineParams) (*state.Machine, error) { if p.Series == "" { conf, err := c.api.state.EnvironConfig() if err != nil { return nil, err } p.Series = config.PreferredSeries(conf) } if p.ContainerType != "" { // Guard against dubious client by making sure that // the following attributes can only be set when we're // not making a new container. p.InstanceId = "" p.Nonce = "" p.HardwareCharacteristics = instance.HardwareCharacteristics{} p.Addrs = nil } else if p.ParentId != "" { return nil, fmt.Errorf("parent machine specified without container type") } jobs, err := stateJobs(p.Jobs) if err != nil { return nil, err } template := state.MachineTemplate{ Series: p.Series, Constraints: p.Constraints, InstanceId: p.InstanceId, Jobs: jobs, Nonce: p.Nonce, HardwareCharacteristics: p.HardwareCharacteristics, Addresses: p.Addrs, } if p.ContainerType == "" { return c.api.state.AddOneMachine(template) } if p.ParentId != "" { return c.api.state.AddMachineInsideMachine(template, p.ParentId, p.ContainerType) } return c.api.state.AddMachineInsideNewMachine(template, template, p.ContainerType) } func stateJobs(jobs []params.MachineJob) ([]state.MachineJob, error) { newJobs := make([]state.MachineJob, len(jobs)) for i, job := range jobs { newJob, err := state.MachineJobFromParams(job) if err != nil { return nil, err } newJobs[i] = newJob } return newJobs, nil } // ProvisioningScript returns a shell script that, when run, // provisions a machine agent on the machine executing the script. func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (params.ProvisioningScriptResult, error) { var result params.ProvisioningScriptResult mcfg, err := statecmd.MachineConfig(c.api.state, args.MachineId, args.Nonce, args.DataDir) if err != nil { return result, err } mcfg.DisablePackageCommands = args.DisablePackageCommands result.Script, err = manual.ProvisioningScript(mcfg) return result, err } // DestroyMachines removes a given set of machines. func (c *Client) DestroyMachines(args params.DestroyMachines) error { var errs []string for _, id := range args.MachineNames { machine, err := c.api.state.Machine(id) switch { case errors.IsNotFoundError(err): err = fmt.Errorf("machine %s does not exist", id) case err != nil: case args.Force: err = machine.ForceDestroy() case machine.Life() != state.Alive: continue default: err = machine.Destroy() } if err != nil { errs = append(errs, err.Error()) } } return destroyErr("machines", args.MachineNames, errs) } // CharmInfo returns information about the requested charm. func (c *Client) CharmInfo(args params.CharmInfo) (api.CharmInfo, error) { curl, err := charm.ParseURL(args.CharmURL) if err != nil { return api.CharmInfo{}, err } charm, err := c.api.state.Charm(curl) if err != nil { return api.CharmInfo{}, err } info := api.CharmInfo{ Revision: charm.Revision(), URL: curl.String(), Config: charm.Config(), Meta: charm.Meta(), } return info, nil } // EnvironmentInfo returns information about the current environment (default // series and type). func (c *Client) EnvironmentInfo() (api.EnvironmentInfo, error) { state := c.api.state conf, err := state.EnvironConfig() if err != nil { return api.EnvironmentInfo{}, err } env, err := state.Environment() if err != nil { return api.EnvironmentInfo{}, err } info := api.EnvironmentInfo{ DefaultSeries: config.PreferredSeries(conf), ProviderType: conf.Type(), Name: conf.Name(), UUID: env.UUID(), } return info, nil } // GetAnnotations returns annotations about a given entity. func (c *Client) GetAnnotations(args params.GetAnnotations) (params.GetAnnotationsResults, error) { nothing := params.GetAnnotationsResults{} entity, err := c.findEntity(args.Tag) if err != nil { return nothing, err } ann, err := entity.Annotations() if err != nil { return nothing, err } return params.GetAnnotationsResults{Annotations: ann}, nil } func (c *Client) findEntity(tag string) (state.Annotator, error) { entity0, err := c.api.state.FindEntity(tag) if err != nil { return nil, err } entity, ok := entity0.(state.Annotator) if !ok { return nil, common.NotSupportedError(tag, "annotations") } return entity, nil } // SetAnnotations stores annotations about a given entity. func (c *Client) SetAnnotations(args params.SetAnnotations) error { entity, err := c.findEntity(args.Tag) if err != nil { return err } return entity.SetAnnotations(args.Pairs) } // parseSettingsCompatible parses setting strings in a way that is // compatible with the behavior before this CL based on the issue // http://pad.lv/1194945. Until then setting an option to an empty // string caused it to reset to the default value. We now allow // empty strings as actual values, but we want to preserve the API // behavior. func parseSettingsCompatible(ch *state.Charm, settings map[string]string) (charm.Settings, error) { setSettings := map[string]string{} unsetSettings := charm.Settings{} // Split settings into those which set and those which unset a value. for name, value := range settings { if value == "" { unsetSettings[name] = nil continue } setSettings[name] = value } // Validate the settings. changes, err := ch.Config().ParseSettingsStrings(setSettings) if err != nil { return nil, err } // Validate the unsettings and merge them into the changes. unsetSettings, err = ch.Config().ValidateSettings(unsetSettings) if err != nil { return nil, err } for name := range unsetSettings { changes[name] = nil } return changes, nil } // EnvironmentGet implements the server-side part of the // get-environment CLI command. func (c *Client) EnvironmentGet() (params.EnvironmentGetResults, error) { result := params.EnvironmentGetResults{} // Get the existing environment config from the state. config, err := c.api.state.EnvironConfig() if err != nil { return result, err } result.Config = config.AllAttrs() return result, nil } // EnvironmentSet implements the server-side part of the // set-environment CLI command. func (c *Client) EnvironmentSet(args params.EnvironmentSet) error { // Make sure we don't allow changing agent-version. checkAgentVersion := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error { if v, found := updateAttrs["agent-version"]; found { oldVersion, _ := oldConfig.AgentVersion() if v != oldVersion.String() { return fmt.Errorf("agent-version cannot be changed") } } return nil } // TODO(waigani) 2014-3-11 #1167616 // Add a txn retry loop to ensure that the settings on disk have not // changed underneath us. return c.api.state.UpdateEnvironConfig(args.Config, nil, checkAgentVersion) } // EnvironmentUnset implements the server-side part of the // set-environment CLI command. func (c *Client) EnvironmentUnset(args params.EnvironmentUnset) error { // TODO(waigani) 2014-3-11 #1167616 // Add a txn retry loop to ensure that the settings on disk have not // changed underneath us. return c.api.state.UpdateEnvironConfig(nil, args.Keys, nil) } // SetEnvironAgentVersion sets the environment agent version. func (c *Client) SetEnvironAgentVersion(args params.SetEnvironAgentVersion) error { return c.api.state.SetEnvironAgentVersion(args.Version) } // FindTools returns a List containing all tools matching the given parameters. func (c *Client) FindTools(args params.FindToolsParams) (params.FindToolsResults, error) { result := params.FindToolsResults{} // Get the existing environment config from the state. envConfig, err := c.api.state.EnvironConfig() if err != nil { return result, err } env, err := environs.New(envConfig) if err != nil { return result, err } filter := coretools.Filter{ Arch: args.Arch, Series: args.Series, } result.List, err = envtools.FindTools(env, args.MajorVersion, args.MinorVersion, filter, envtools.DoNotAllowRetry) result.Error = common.ServerError(err) return result, nil } func destroyErr(desc string, ids, errs []string) error { if len(errs) == 0 { return nil } msg := "some %s were not destroyed" if len(errs) == len(ids) { msg = "no %s were destroyed" } msg = fmt.Sprintf(msg, desc) return fmt.Errorf("%s: %s", msg, strings.Join(errs, "; ")) } // AddCharm adds the given charm URL (which must include revision) to // the environment, if it does not exist yet. Local charms are not // supported, only charm store URLs. See also AddLocalCharm(). func (c *Client) AddCharm(args params.CharmURL) error { charmURL, err := charm.ParseURL(args.URL) if err != nil { return err } if charmURL.Schema != "cs" { return fmt.Errorf("only charm store charm URLs are supported, with cs: schema") } if charmURL.Revision < 0 { return fmt.Errorf("charm URL must include revision") } // First, check if a pending or a real charm exists in state. stateCharm, err := c.api.state.PrepareStoreCharmUpload(charmURL) if err == nil && stateCharm.IsUploaded() { // Charm already in state (it was uploaded already). return nil } else if err != nil { return err } // Get the charm and its information from the store. envConfig, err := c.api.state.EnvironConfig() if err != nil { return err } store := config.SpecializeCharmRepo(CharmStore, envConfig) downloadedCharm, err := store.Get(charmURL) if err != nil { return errgo.Annotatef(err, "cannot download charm %q", charmURL.String()) } // Open it and calculate the SHA256 hash. downloadedBundle, ok := downloadedCharm.(*charm.Bundle) if !ok { return errgo.New("expected a charm archive, got %T", downloadedCharm) } archive, err := os.Open(downloadedBundle.Path) if err != nil { return errgo.Annotate(err, "cannot read downloaded charm") } defer archive.Close() bundleSHA256, size, err := utils.ReadSHA256(archive) if err != nil { return errgo.Annotate(err, "cannot calculate SHA256 hash of charm") } if _, err := archive.Seek(0, 0); err != nil { return errgo.Annotate(err, "cannot rewind charm archive") } // Get the environment storage and upload the charm. env, err := environs.New(envConfig) if err != nil { return errgo.Annotate(err, "cannot access environment") } storage := env.Storage() archiveName, err := CharmArchiveName(charmURL.Name, charmURL.Revision) if err != nil { return errgo.Annotate(err, "cannot generate charm archive name") } if err := storage.Put(archiveName, archive, size); err != nil { return errgo.Annotate(err, "cannot upload charm to provider storage") } storageURL, err := storage.URL(archiveName) if err != nil { return errgo.Annotate(err, "cannot get storage URL for charm") } bundleURL, err := url.Parse(storageURL) if err != nil { return errgo.Annotate(err, "cannot parse storage URL") } // Finally, update the charm data in state and mark it as no longer pending. _, err = c.api.state.UpdateUploadedCharm(downloadedCharm, charmURL, bundleURL, bundleSHA256) if err == state.ErrCharmRevisionAlreadyModified || state.IsCharmAlreadyUploadedError(err) { // This is not an error, it just signifies somebody else // managed to upload and update the charm in state before // us. This means we have to delete what we just uploaded // to storage. if err := storage.Remove(archiveName); err != nil { errgo.Annotate(err, "cannot remove duplicated charm from storage") } return nil } return err } func (c *Client) ResolveCharms(args params.ResolveCharms) (params.ResolveCharmResults, error) { var results params.ResolveCharmResults envConfig, err := c.api.state.EnvironConfig() if err != nil { return params.ResolveCharmResults{}, err } repo := config.SpecializeCharmRepo(CharmStore, envConfig) for _, ref := range args.References { result := params.ResolveCharmResult{} curl, err := c.resolveCharm(ref, repo) if err != nil { result.Error = err.Error() } else { result.URL = curl } results.URLs = append(results.URLs, result) } return results, nil } func (c *Client) resolveCharm(ref charm.Reference, repo charm.Repository) (*charm.URL, error) { if ref.Schema != "cs" { return nil, fmt.Errorf("only charm store charm references are supported, with cs: schema") } // Resolve the charm location with the repository. return repo.Resolve(ref) } // CharmArchiveName returns a string that is suitable as a file name // in a storage URL. It is constructed from the charm name, revision // and a random UUID string. func CharmArchiveName(name string, revision int) (string, error) { uuid, err := utils.NewUUID() if err != nil { return "", err } return charm.Quote(fmt.Sprintf("%s-%d-%s", name, revision, uuid)), nil } // RetryProvisioning marks a provisioning error as transient on the machines. func (c *Client) RetryProvisioning(p params.Entities) (params.ErrorResults, error) { entityStatus := make([]params.EntityStatus, len(p.Entities)) for i, entity := range p.Entities { entityStatus[i] = params.EntityStatus{Tag: entity.Tag, Data: params.StatusData{"transient": true}} } return c.api.statusSetter.UpdateStatus(params.SetStatus{ Entities: entityStatus, }) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/destroy_test.go�����������������0000644�0000153�0000161�00000010040�12321735642�030335� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" coreerrors "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" ) type destroyEnvironmentSuite struct { baseSuite } var _ = gc.Suite(&destroyEnvironmentSuite{}) // setUpManual adds "manually provisioned" machines to state: // one manager machine, and one non-manager. func (s *destroyEnvironmentSuite) setUpManual(c *gc.C) (m0, m1 *state.Machine) { m0, err := s.State.AddMachine("precise", state.JobManageEnviron) c.Assert(err, gc.IsNil) err = m0.SetProvisioned(instance.Id("manual:0"), "manual:0:fake_nonce", nil) c.Assert(err, gc.IsNil) m1, err = s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, gc.IsNil) err = m1.SetProvisioned(instance.Id("manual:1"), "manual:1:fake_nonce", nil) c.Assert(err, gc.IsNil) return m0, m1 } // setUpInstances adds machines to state backed by instances: // one manager machine, and one non-manager. func (s *destroyEnvironmentSuite) setUpInstances(c *gc.C) (m0, m1 *state.Machine) { m0, err := s.State.AddMachine("precise", state.JobManageEnviron) c.Assert(err, gc.IsNil) inst, _ := testing.AssertStartInstance(c, s.APIConn.Environ, m0.Id()) err = m0.SetProvisioned(inst.Id(), "fake_nonce", nil) c.Assert(err, gc.IsNil) m1, err = s.State.AddMachine("precise", state.JobHostUnits) c.Assert(err, gc.IsNil) inst, _ = testing.AssertStartInstance(c, s.APIConn.Environ, m1.Id()) err = m1.SetProvisioned(inst.Id(), "fake_nonce", nil) c.Assert(err, gc.IsNil) return m0, m1 } func (s *destroyEnvironmentSuite) TestDestroyEnvironmentManual(c *gc.C) { _, nonManager := s.setUpManual(c) // If there are any non-manager manual machines in state, DestroyEnvironment will // error. It will not set the Dying flag on the environment. err := s.APIState.Client().DestroyEnvironment() c.Assert(err, gc.ErrorMatches, fmt.Sprintf("manually provisioned machines must first be destroyed with `juju destroy-machine %s`", nonManager.Id())) env, err := s.State.Environment() c.Assert(err, gc.IsNil) c.Assert(env.Life(), gc.Equals, state.Alive) // If we remove the non-manager machine, it should pass. // Manager machines will remain. err = nonManager.EnsureDead() c.Assert(err, gc.IsNil) err = nonManager.Remove() c.Assert(err, gc.IsNil) err = s.APIState.Client().DestroyEnvironment() c.Assert(err, gc.IsNil) err = env.Refresh() c.Assert(err, gc.IsNil) c.Assert(env.Life(), gc.Equals, state.Dying) } func (s *destroyEnvironmentSuite) TestDestroyEnvironment(c *gc.C) { manager, nonManager := s.setUpInstances(c) managerId, _ := manager.InstanceId() nonManagerId, _ := nonManager.InstanceId() instances, err := s.APIConn.Environ.Instances([]instance.Id{managerId, nonManagerId}) c.Assert(err, gc.IsNil) for _, inst := range instances { c.Assert(inst, gc.NotNil) } services, err := s.State.AllServices() c.Assert(err, gc.IsNil) err = s.APIState.Client().DestroyEnvironment() c.Assert(err, gc.IsNil) // After DestroyEnvironment returns, we should have: // - all non-manager instances stopped instances, err = s.APIConn.Environ.Instances([]instance.Id{managerId, nonManagerId}) c.Assert(err, gc.Equals, environs.ErrPartialInstances) c.Assert(instances[0], gc.NotNil) c.Assert(instances[1], gc.IsNil) // - all services in state are Dying or Dead (or removed altogether), // after running the state Cleanups. needsCleanup, err := s.State.NeedsCleanup() c.Assert(err, gc.IsNil) c.Assert(needsCleanup, jc.IsTrue) err = s.State.Cleanup() c.Assert(err, gc.IsNil) for _, s := range services { err = s.Refresh() if err != nil { c.Assert(err, jc.Satisfies, coreerrors.IsNotFoundError) } else { c.Assert(s.Life(), gc.Not(gc.Equals), state.Alive) } } // - environment is Dying env, err := s.State.Environment() c.Assert(err, gc.IsNil) c.Assert(env.Life(), gc.Equals, state.Dying) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/status.go�����������������������0000644�0000153�0000161�00000002153�12321735642�027136� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client import ( "launchpad.net/juju-core/juju" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/statecmd" ) // FullStatus gives the information needed for juju status over the api func (c *Client) FullStatus(args params.StatusParams) (api.Status, error) { conn, err := juju.NewConnFromState(c.api.state) if err != nil { return api.Status{}, err } status, err := statecmd.Status(conn, args.Patterns) return *status, err } // Status is a stub version of FullStatus that was introduced in 1.16 func (c *Client) Status() (api.LegacyStatus, error) { var legacyStatus api.LegacyStatus status, err := c.FullStatus(params.StatusParams{}) if err != nil { return legacyStatus, err } legacyStatus.Machines = make(map[string]api.LegacyMachineStatus) for machineName, machineStatus := range status.Machines { legacyStatus.Machines[machineName] = api.LegacyMachineStatus{ InstanceId: string(machineStatus.InstanceId), } } return legacyStatus, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/status_test.go������������������0000644�0000153�0000161�00000003173�12321735642�030200� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" ) type statusSuite struct { baseSuite } var _ = gc.Suite(&statusSuite{}) func (s *statusSuite) addMachine(c *gc.C) *state.Machine { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) return machine } // Complete testing of status functionality happens elsewhere in the codebase, // these tests just sanity-check the api itself. func (s *statusSuite) TestFullStatus(c *gc.C) { machine := s.addMachine(c) client := s.APIState.Client() status, err := client.Status(nil) c.Assert(err, gc.IsNil) c.Check(status.EnvironmentName, gc.Equals, "dummyenv") c.Check(status.Services, gc.HasLen, 0) c.Check(status.Machines, gc.HasLen, 1) resultMachine, ok := status.Machines[machine.Id()] if !ok { c.Fatalf("Missing machine with id %q", machine.Id()) } c.Check(resultMachine.Id, gc.Equals, machine.Id()) c.Check(resultMachine.Series, gc.Equals, machine.Series()) } func (s *statusSuite) TestLegacyStatus(c *gc.C) { machine := s.addMachine(c) instanceId := "i-fakeinstance" err := machine.SetProvisioned(instance.Id(instanceId), "fakenonce", nil) c.Assert(err, gc.IsNil) client := s.APIState.Client() status, err := client.LegacyStatus() c.Assert(err, gc.IsNil) c.Check(status.Machines, gc.HasLen, 1) resultMachine, ok := status.Machines[machine.Id()] if !ok { c.Fatalf("Missing machine with id %q", machine.Id()) } c.Check(resultMachine.InstanceId, gc.Equals, instanceId) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/client/get_test.go���������������������0000644�0000153�0000161�00000013037�12321735642�027434� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package client_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/state/api/params" ) type getSuite struct { baseSuite } var _ = gc.Suite(&getSuite{}) func (s *getSuite) TestClientServiceGetSmoketest(c *gc.C) { s.setUpScenario(c) results, err := s.APIState.Client().ServiceGet("wordpress") c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, &params.ServiceGetResults{ Service: "wordpress", Charm: "wordpress", Config: map[string]interface{}{ "blog-title": map[string]interface{}{ "type": "string", "value": "My Title", "description": "A descriptive title used for the blog.", "default": true, }, }, }) } func (s *getSuite) TestServiceGetUnknownService(c *gc.C) { apiclient := s.APIState.Client() _, err := apiclient.ServiceGet("unknown") c.Assert(err, gc.ErrorMatches, `service "unknown" not found`) } var getTests = []struct { about string charm string constraints string config charm.Settings expect params.ServiceGetResults }{{ about: "deployed service", charm: "dummy", constraints: "mem=2G cpu-power=400", config: charm.Settings{ // Different from default. "title": "Look To Windward", // Same as default. "username": "admin001", // Use default (but there's no charm default) "skill-level": nil, // Outlook is left unset. }, expect: params.ServiceGetResults{ Config: map[string]interface{}{ "title": map[string]interface{}{ "description": "A descriptive title used for the service.", "type": "string", "value": "Look To Windward", }, "outlook": map[string]interface{}{ "description": "No default outlook.", "type": "string", "default": true, }, "username": map[string]interface{}{ "description": "The name of the initial account (given admin permissions).", "type": "string", "value": "admin001", }, "skill-level": map[string]interface{}{ "description": "A number indicating skill.", "type": "int", "default": true, }, }, }, }, { about: "deployed service #2", charm: "dummy", config: charm.Settings{ // Set title to default. "title": nil, // Value when there's a default. "username": "foobie", // Numeric value. "skill-level": 0, // String value. "outlook": "phlegmatic", }, expect: params.ServiceGetResults{ Config: map[string]interface{}{ "title": map[string]interface{}{ "description": "A descriptive title used for the service.", "type": "string", "value": "My Title", "default": true, }, "outlook": map[string]interface{}{ "description": "No default outlook.", "type": "string", "value": "phlegmatic", }, "username": map[string]interface{}{ "description": "The name of the initial account (given admin permissions).", "type": "string", "value": "foobie", }, "skill-level": map[string]interface{}{ "description": "A number indicating skill.", "type": "int", // TODO(jam): 2013-08-28 bug #1217742 // we have to use float64() here, because the // API does not preserve int types. This used // to be int64() but we end up with a type // mismatch when comparing the content "value": float64(0), }, }, }, }, { about: "subordinate service", charm: "logging", expect: params.ServiceGetResults{ Config: map[string]interface{}{}, }, }} func (s *getSuite) TestServiceGet(c *gc.C) { for i, t := range getTests { c.Logf("test %d. %s", i, t.about) ch := s.AddTestingCharm(c, t.charm) svc := s.AddTestingService(c, fmt.Sprintf("test%d", i), ch) var constraintsv constraints.Value if t.constraints != "" { constraintsv = constraints.MustParse(t.constraints) err := svc.SetConstraints(constraintsv) c.Assert(err, gc.IsNil) } if t.config != nil { err := svc.UpdateConfigSettings(t.config) c.Assert(err, gc.IsNil) } expect := t.expect expect.Constraints = constraintsv expect.Service = svc.Name() expect.Charm = ch.Meta().Name apiclient := s.APIState.Client() got, err := apiclient.ServiceGet(svc.Name()) c.Assert(err, gc.IsNil) c.Assert(*got, gc.DeepEquals, expect) } } func (s *getSuite) TestServiceGetMaxResolutionInt(c *gc.C) { // See the bug http://pad.lv/1217742 // ServiceGet ends up pushing a map[string]interface{} which containts // an int64 through a JSON Marshal & Unmarshal which ends up changing // the int64 into a float64. We will fix it if we find it is actually a // problem. const nonFloatInt = (int64(1) << 54) + 1 const asFloat = float64(nonFloatInt) c.Assert(int64(asFloat), gc.Not(gc.Equals), nonFloatInt) c.Assert(int64(asFloat)+1, gc.Equals, nonFloatInt) ch := s.AddTestingCharm(c, "dummy") svc := s.AddTestingService(c, "test-service", ch) err := svc.UpdateConfigSettings(map[string]interface{}{"skill-level": nonFloatInt}) c.Assert(err, gc.IsNil) got, err := s.APIState.Client().ServiceGet(svc.Name()) c.Assert(err, gc.IsNil) c.Assert(got.Config["skill-level"], gc.DeepEquals, map[string]interface{}{ "description": "A number indicating skill.", "type": "int", "value": asFloat, }) } func (s *getSuite) TestServiceGetCharmURL(c *gc.C) { s.setUpScenario(c) charmURL, err := s.APIState.Client().ServiceGetCharmURL("wordpress") c.Assert(err, gc.IsNil) c.Assert(charmURL.String(), gc.Equals, "local:quantal/wordpress-3") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/watcher.go�����������������������������0000644�0000153�0000161�00000005667�12321735642�026007� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver import ( "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/multiwatcher" ) type srvClientAllWatcher struct { watcher *multiwatcher.Watcher id string resources *common.Resources } func (aw *srvClientAllWatcher) Next() (params.AllWatcherNextResults, error) { deltas, err := aw.watcher.Next() return params.AllWatcherNextResults{ Deltas: deltas, }, err } func (w *srvClientAllWatcher) Stop() error { return w.resources.Stop(w.id) } type srvNotifyWatcher struct { watcher state.NotifyWatcher id string resources *common.Resources } // Next returns when a change has occurred to the // entity being watched since the most recent call to Next // or the Watch call that created the NotifyWatcher. func (w *srvNotifyWatcher) Next() error { if _, ok := <-w.watcher.Changes(); ok { return nil } err := w.watcher.Err() if err == nil { err = common.ErrStoppedWatcher } return err } // Stop stops the watcher. func (w *srvNotifyWatcher) Stop() error { return w.resources.Stop(w.id) } // srvStringsWatcher notifies about changes for all entities of a // given kind, sending the changes as a list of strings. type srvStringsWatcher struct { watcher state.StringsWatcher id string resources *common.Resources } // Next returns when a change has occured to an entity of the // collection being watched since the most recent call to Next // or the Watch call that created the srvStringsWatcher. func (w *srvStringsWatcher) Next() (params.StringsWatchResult, error) { if changes, ok := <-w.watcher.Changes(); ok { return params.StringsWatchResult{ Changes: changes, }, nil } err := w.watcher.Err() if err == nil { err = common.ErrStoppedWatcher } return params.StringsWatchResult{}, err } // Stop stops the watcher. func (w *srvStringsWatcher) Stop() error { return w.resources.Stop(w.id) } // srvRelationUnitsWatcher notifies about units entering and leaving // the scope of a RelationUnit, and changes to the settings of those // units known to have entered. type srvRelationUnitsWatcher struct { watcher state.RelationUnitsWatcher id string resources *common.Resources } // Next returns when a change has occured to an entity of the // collection being watched since the most recent call to Next // or the Watch call that created the srvRelationUnitsWatcher. func (w *srvRelationUnitsWatcher) Next() (params.RelationUnitsWatchResult, error) { if changes, ok := <-w.watcher.Changes(); ok { return params.RelationUnitsWatchResult{ Changes: changes, }, nil } err := w.watcher.Err() if err == nil { err = common.ErrStoppedWatcher } return params.RelationUnitsWatchResult{}, err } // Stop stops the watcher. func (w *srvRelationUnitsWatcher) Stop() error { return w.resources.Stop(w.id) } �������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/environment/���������������������������0000755�0000153�0000161�00000000000�12321735642�026351� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/environment/environment.go�������������0000644�0000153�0000161�00000001466�12321735642�031253� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environment import ( "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/apiserver/common" ) // EnvironmentAPI implements the API used by the machine environment worker. type EnvironmentAPI struct { *common.EnvironWatcher } // NewEnvironmentAPI creates a new instance of the Environment API. func NewEnvironmentAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*EnvironmentAPI, error) { // Can always watch for environ changes. getCanWatch := common.AuthAlways(true) // Does not get the secrets. getCanReadSecrets := common.AuthAlways(false) return &EnvironmentAPI{ EnvironWatcher: common.NewEnvironWatcher(st, resources, getCanWatch, getCanReadSecrets), }, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/environment/package_test.go������������0000644�0000153�0000161�00000000373�12321735642�031335� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environment_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/environment/environment_test.go��������0000644�0000153�0000161�00000002560�12321735642�032306� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environment_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/apiserver/common" commontesting "launchpad.net/juju-core/state/apiserver/common/testing" "launchpad.net/juju-core/state/apiserver/environment" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" ) type environmentSuite struct { testing.JujuConnSuite *commontesting.EnvironWatcherTest authorizer apiservertesting.FakeAuthorizer resources *common.Resources machine0 *state.Machine api *environment.EnvironmentAPI } var _ = gc.Suite(&environmentSuite{}) func (s *environmentSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) var err error s.machine0, err = s.State.AddMachine("quantal", state.JobHostUnits, state.JobManageEnviron) c.Assert(err, gc.IsNil) s.authorizer = apiservertesting.FakeAuthorizer{ Tag: s.machine0.Tag(), LoggedIn: true, MachineAgent: true, Entity: s.machine0, } s.resources = common.NewResources() s.api, err = environment.NewEnvironmentAPI( s.State, s.resources, s.authorizer, ) c.Assert(err, gc.IsNil) s.EnvironWatcherTest = commontesting.NewEnvironWatcherTest( s.api, s.State, s.resources, commontesting.NoSecrets) } ������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/keymanager/����������������������������0000755�0000153�0000161�00000000000�12321736000�026115� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/keymanager/keymanager_test.go����������0000644�0000153�0000161�00000022715�12321735642�031650� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keymanager_test import ( "fmt" "strings" gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/apiserver/keymanager" keymanagertesting "launchpad.net/juju-core/state/apiserver/keymanager/testing" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" "launchpad.net/juju-core/utils/ssh" sshtesting "launchpad.net/juju-core/utils/ssh/testing" ) type keyManagerSuite struct { jujutesting.JujuConnSuite keymanager *keymanager.KeyManagerAPI resources *common.Resources authoriser apiservertesting.FakeAuthorizer } var _ = gc.Suite(&keyManagerSuite{}) func (s *keyManagerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.resources = common.NewResources() s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) s.authoriser = apiservertesting.FakeAuthorizer{ Tag: "user-admin", LoggedIn: true, Client: true, } var err error s.keymanager, err = keymanager.NewKeyManagerAPI(s.State, s.resources, s.authoriser) c.Assert(err, gc.IsNil) } func (s *keyManagerSuite) TestNewKeyManagerAPIAcceptsClient(c *gc.C) { endPoint, err := keymanager.NewKeyManagerAPI(s.State, s.resources, s.authoriser) c.Assert(err, gc.IsNil) c.Assert(endPoint, gc.NotNil) } func (s *keyManagerSuite) TestNewKeyManagerAPIAcceptsEnvironManager(c *gc.C) { anAuthoriser := s.authoriser anAuthoriser.EnvironManager = true endPoint, err := keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser) c.Assert(err, gc.IsNil) c.Assert(endPoint, gc.NotNil) } func (s *keyManagerSuite) TestNewKeyManagerAPIRefusesNonClient(c *gc.C) { anAuthoriser := s.authoriser anAuthoriser.Client = false endPoint, err := keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser) c.Assert(endPoint, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *keyManagerSuite) TestNewKeyManagerAPIRefusesNonEnvironManager(c *gc.C) { anAuthoriser := s.authoriser anAuthoriser.Client = false anAuthoriser.MachineAgent = true endPoint, err := keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser) c.Assert(endPoint, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *keyManagerSuite) setAuthorisedKeys(c *gc.C, keys string) { err := s.State.UpdateEnvironConfig(map[string]interface{}{"authorized-keys": keys}, nil, nil) c.Assert(err, gc.IsNil) envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(envConfig.AuthorizedKeys(), gc.Equals, keys) } func (s *keyManagerSuite) TestListKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key s.setAuthorisedKeys(c, strings.Join([]string{key1, key2, "bad key"}, "\n")) args := params.ListSSHKeys{ Entities: params.Entities{[]params.Entity{ {Tag: state.AdminUser}, {Tag: "invalid"}, }}, Mode: ssh.FullKeys, } results, err := s.keymanager.ListKeys(args) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.StringsResults{ Results: []params.StringsResult{ {Result: []string{key1, key2, "Invalid key: bad key"}}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *keyManagerSuite) assertEnvironKeys(c *gc.C, expected []string) { envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) keys := envConfig.AuthorizedKeys() c.Assert(keys, gc.Equals, strings.Join(expected, "\n")) } func (s *keyManagerSuite) TestAddKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key initialKeys := []string{key1, key2, "bad key"} s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n")) newKey := sshtesting.ValidKeyThree.Key + " newuser@host" args := params.ModifyUserSSHKeys{ User: state.AdminUser, Keys: []string{key2, newKey, "invalid-key"}, } results, err := s.keymanager.AddKeys(args) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {Error: apiservertesting.ServerError(fmt.Sprintf("duplicate ssh key: %s", key2))}, {Error: nil}, {Error: apiservertesting.ServerError("invalid ssh key: invalid-key")}, }, }) s.assertEnvironKeys(c, append(initialKeys, newKey)) } func (s *keyManagerSuite) TestAddJujuSystemKey(c *gc.C) { anAuthoriser := s.authoriser anAuthoriser.Client = false anAuthoriser.EnvironManager = true anAuthoriser.Tag = "machine-0" var err error s.keymanager, err = keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser) c.Assert(err, gc.IsNil) key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key initialKeys := []string{key1, key2} s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n")) newKey := sshtesting.ValidKeyThree.Key + " juju-system-key" args := params.ModifyUserSSHKeys{ User: "juju-system-key", Keys: []string{newKey}, } results, err := s.keymanager.AddKeys(args) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {Error: nil}, }, }) s.assertEnvironKeys(c, append(initialKeys, newKey)) } func (s *keyManagerSuite) TestAddJujuSystemKeyNotMachine(c *gc.C) { anAuthoriser := s.authoriser anAuthoriser.Client = false anAuthoriser.EnvironManager = true anAuthoriser.Tag = "unit-wordpress-0" var err error s.keymanager, err = keymanager.NewKeyManagerAPI(s.State, s.resources, anAuthoriser) c.Assert(err, gc.IsNil) key1 := sshtesting.ValidKeyOne.Key s.setAuthorisedKeys(c, key1) newKey := sshtesting.ValidKeyThree.Key + " juju-system-key" args := params.ModifyUserSSHKeys{ User: "juju-system-key", Keys: []string{newKey}, } _, err = s.keymanager.AddKeys(args) c.Assert(err, gc.ErrorMatches, "permission denied") s.assertEnvironKeys(c, []string{key1}) } func (s *keyManagerSuite) TestDeleteKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key initialKeys := []string{key1, key2, "bad key"} s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n")) args := params.ModifyUserSSHKeys{ User: state.AdminUser, Keys: []string{sshtesting.ValidKeyTwo.Fingerprint, sshtesting.ValidKeyThree.Fingerprint, "invalid-key"}, } results, err := s.keymanager.DeleteKeys(args) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {Error: nil}, {Error: apiservertesting.ServerError("invalid ssh key: " + sshtesting.ValidKeyThree.Fingerprint)}, {Error: apiservertesting.ServerError("invalid ssh key: invalid-key")}, }, }) s.assertEnvironKeys(c, []string{"bad key", key1}) } func (s *keyManagerSuite) TestCannotDeleteAllKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key initialKeys := []string{key1, key2} s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n")) args := params.ModifyUserSSHKeys{ User: state.AdminUser, Keys: []string{sshtesting.ValidKeyTwo.Fingerprint, "user@host"}, } _, err := s.keymanager.DeleteKeys(args) c.Assert(err, gc.ErrorMatches, "cannot delete all keys") s.assertEnvironKeys(c, initialKeys) } func (s *keyManagerSuite) assertInvalidUserOperation(c *gc.C, runTestLogic func(args params.ModifyUserSSHKeys) error) { initialKey := sshtesting.ValidKeyOne.Key + " user@host" s.setAuthorisedKeys(c, initialKey) // Set up the params. newKey := sshtesting.ValidKeyThree.Key + " newuser@host" args := params.ModifyUserSSHKeys{ User: "invalid", Keys: []string{newKey}, } // Run the required test code and check the error. err := runTestLogic(args) c.Assert(err, gc.DeepEquals, apiservertesting.ErrUnauthorized) // No environ changes. s.assertEnvironKeys(c, []string{initialKey}) } func (s *keyManagerSuite) TestAddKeysInvalidUser(c *gc.C) { s.assertInvalidUserOperation(c, func(args params.ModifyUserSSHKeys) error { _, err := s.keymanager.AddKeys(args) return err }) } func (s *keyManagerSuite) TestDeleteKeysInvalidUser(c *gc.C) { s.assertInvalidUserOperation(c, func(args params.ModifyUserSSHKeys) error { _, err := s.keymanager.DeleteKeys(args) return err }) } func (s *keyManagerSuite) TestImportKeys(c *gc.C) { s.PatchValue(&keymanager.RunSSHImportId, keymanagertesting.FakeImport) key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key key3 := sshtesting.ValidKeyThree.Key initialKeys := []string{key1, key2, "bad key"} s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n")) args := params.ModifyUserSSHKeys{ User: state.AdminUser, Keys: []string{"lp:existing", "lp:validuser", "invalid-key"}, } results, err := s.keymanager.ImportKeys(args) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {Error: apiservertesting.ServerError(fmt.Sprintf("duplicate ssh key: %s", key2))}, {Error: nil}, {Error: apiservertesting.ServerError("invalid ssh key id: invalid-key")}, }, }) s.assertEnvironKeys(c, append(initialKeys, key3)) } func (s *keyManagerSuite) TestCallSSHImportId(c *gc.C) { c.Skip("the landing bot does not run ssh-import-id successfully") output, err := keymanager.RunSSHImportId("lp:wallyworld") c.Assert(err, gc.IsNil) lines := strings.Split(output, "\n") var key string for _, line := range lines { if !strings.HasPrefix(line, "ssh-") { continue } _, _, err := ssh.KeyFingerprint(line) if err == nil { key = line } } c.Assert(key, gc.Not(gc.Equals), "") } ���������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/keymanager/keymanager.go���������������0000644�0000153�0000161�00000027461�12321735776�030624� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keymanager import ( stderrors "errors" "fmt" "strings" "github.com/juju/loggo" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/set" "launchpad.net/juju-core/utils/ssh" ) var logger = loggo.GetLogger("juju.state.apiserver.keymanager") // KeyManager defines the methods on the keymanager API end point. type KeyManager interface { ListKeys(arg params.ListSSHKeys) (params.StringsResults, error) AddKeys(arg params.ModifyUserSSHKeys) (params.ErrorResults, error) DeleteKeys(arg params.ModifyUserSSHKeys) (params.ErrorResults, error) ImportKeys(arg params.ModifyUserSSHKeys) (params.ErrorResults, error) } // KeyUpdaterAPI implements the KeyUpdater interface and is the concrete // implementation of the api end point. type KeyManagerAPI struct { state *state.State resources *common.Resources authorizer common.Authorizer getCanRead common.GetAuthFunc getCanWrite common.GetAuthFunc } var _ KeyManager = (*KeyManagerAPI)(nil) // NewKeyUpdaterAPI creates a new server-side keyupdater API end point. func NewKeyManagerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*KeyManagerAPI, error) { // Only clients and environment managers can access the key manager service. if !authorizer.AuthClient() && !authorizer.AuthEnvironManager() { return nil, common.ErrPerm } // TODO(wallyworld) - replace stub with real canRead function // For now, only admins can read authorised ssh keys. getCanRead := func() (common.AuthFunc, error) { return func(tag string) bool { return authorizer.GetAuthTag() == "user-admin" }, nil } // TODO(wallyworld) - replace stub with real canWrite function // For now, only admins can write authorised ssh keys for users. // Machine agents can write the juju-system-key. getCanWrite := func() (common.AuthFunc, error) { return func(tag string) bool { // Are we a machine agent writing the Juju system key. if tag == config.JujuSystemKey { _, _, err := names.ParseTag(authorizer.GetAuthTag(), names.MachineTagKind) return err == nil } // Are we writing the auth key for a user. if _, err := st.User(tag); err != nil { return false } return authorizer.GetAuthTag() == "user-admin" }, nil } return &KeyManagerAPI{ state: st, resources: resources, authorizer: authorizer, getCanRead: getCanRead, getCanWrite: getCanWrite}, nil } // ListKeys returns the authorised ssh keys for the specified users. func (api *KeyManagerAPI) ListKeys(arg params.ListSSHKeys) (params.StringsResults, error) { if len(arg.Entities.Entities) == 0 { return params.StringsResults{}, nil } results := make([]params.StringsResult, len(arg.Entities.Entities)) // For now, authorised keys are global, common to all users. var keyInfo []string cfg, configErr := api.state.EnvironConfig() if configErr == nil { keys := ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys()) keyInfo = parseKeys(keys, arg.Mode) } canRead, err := api.getCanRead() if err != nil { return params.StringsResults{}, err } for i, entity := range arg.Entities.Entities { if !canRead(entity.Tag) { results[i].Error = common.ServerError(common.ErrPerm) continue } if _, err := api.state.User(entity.Tag); err != nil { if errors.IsNotFoundError(err) { results[i].Error = common.ServerError(common.ErrPerm) } else { results[i].Error = common.ServerError(err) } continue } var err error if configErr == nil { results[i].Result = keyInfo } else { err = configErr } results[i].Error = common.ServerError(err) } return params.StringsResults{results}, nil } func parseKeys(keys []string, mode ssh.ListMode) (keyInfo []string) { for _, key := range keys { fingerprint, comment, err := ssh.KeyFingerprint(key) if err != nil { keyInfo = append(keyInfo, fmt.Sprintf("Invalid key: %v", key)) } else { if mode == ssh.FullKeys { keyInfo = append(keyInfo, key) } else { shortKey := fingerprint if comment != "" { shortKey += fmt.Sprintf(" (%s)", comment) } keyInfo = append(keyInfo, shortKey) } } } return keyInfo } func (api *KeyManagerAPI) writeSSHKeys(sshKeys []string) error { // Write out the new keys. keyStr := strings.Join(sshKeys, "\n") attrs := map[string]interface{}{config.AuthKeysConfig: keyStr} // TODO(waigani) 2014-03-17 bug #1293324 // Pass in validation to ensure SSH keys // have not changed underfoot err := api.state.UpdateEnvironConfig(attrs, nil, nil) if err != nil { return fmt.Errorf("writing environ config: %v", err) } return nil } // currentKeyDataForAdd gathers data used when adding ssh keys. func (api *KeyManagerAPI) currentKeyDataForAdd() (keys []string, fingerprints *set.Strings, err error) { fp := set.NewStrings() fingerprints = &fp cfg, err := api.state.EnvironConfig() if err != nil { return nil, nil, fmt.Errorf("reading current key data: %v", err) } keys = ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys()) for _, key := range keys { fingerprint, _, err := ssh.KeyFingerprint(key) if err != nil { logger.Warningf("ignoring invalid ssh key %q: %v", key, err) } fingerprints.Add(fingerprint) } return keys, fingerprints, nil } // AddKeys adds new authorised ssh keys for the specified user. func (api *KeyManagerAPI) AddKeys(arg params.ModifyUserSSHKeys) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(arg.Keys)), } if len(arg.Keys) == 0 { return result, nil } canWrite, err := api.getCanWrite() if err != nil { return params.ErrorResults{}, common.ServerError(err) } if !canWrite(arg.User) { return params.ErrorResults{}, common.ServerError(common.ErrPerm) } // For now, authorised keys are global, common to all users. sshKeys, currentFingerprints, err := api.currentKeyDataForAdd() if err != nil { return params.ErrorResults{}, common.ServerError(fmt.Errorf("reading current key data: %v", err)) } // Ensure we are not going to add invalid or duplicate keys. result.Results = make([]params.ErrorResult, len(arg.Keys)) for i, key := range arg.Keys { fingerprint, _, err := ssh.KeyFingerprint(key) if err != nil { result.Results[i].Error = common.ServerError(fmt.Errorf("invalid ssh key: %s", key)) continue } if currentFingerprints.Contains(fingerprint) { result.Results[i].Error = common.ServerError(fmt.Errorf("duplicate ssh key: %s", key)) continue } sshKeys = append(sshKeys, key) } err = api.writeSSHKeys(sshKeys) if err != nil { return params.ErrorResults{}, common.ServerError(err) } return result, nil } type importedSSHKey struct { key string fingerprint string err error } // Override for testing var RunSSHImportId = runSSHImportId func runSSHImportId(keyId string) (string, error) { return utils.RunCommand("ssh-import-id", "-o", "-", keyId) } // runSSHKeyImport uses ssh-import-id to find the ssh keys for the specified key ids. func runSSHKeyImport(keyIds []string) []importedSSHKey { keyInfo := make([]importedSSHKey, len(keyIds)) for i, keyId := range keyIds { output, err := RunSSHImportId(keyId) if err != nil { keyInfo[i].err = err continue } lines := strings.Split(output, "\n") for _, line := range lines { if !strings.HasPrefix(line, "ssh-") { continue } keyInfo[i].fingerprint, _, keyInfo[i].err = ssh.KeyFingerprint(line) if err == nil { keyInfo[i].key = line } } if keyInfo[i].key == "" { keyInfo[i].err = fmt.Errorf("invalid ssh key id: %s", keyId) } } return keyInfo } // ImportKeys imports new authorised ssh keys from the specified key ids for the specified user. func (api *KeyManagerAPI) ImportKeys(arg params.ModifyUserSSHKeys) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(arg.Keys)), } if len(arg.Keys) == 0 { return result, nil } canWrite, err := api.getCanWrite() if err != nil { return params.ErrorResults{}, common.ServerError(err) } if !canWrite(arg.User) { return params.ErrorResults{}, common.ServerError(common.ErrPerm) } // For now, authorised keys are global, common to all users. sshKeys, currentFingerprints, err := api.currentKeyDataForAdd() if err != nil { return params.ErrorResults{}, common.ServerError(fmt.Errorf("reading current key data: %v", err)) } importedKeyInfo := runSSHKeyImport(arg.Keys) // Ensure we are not going to add invalid or duplicate keys. result.Results = make([]params.ErrorResult, len(importedKeyInfo)) for i, keyInfo := range importedKeyInfo { if keyInfo.err != nil { result.Results[i].Error = common.ServerError(keyInfo.err) continue } if currentFingerprints.Contains(keyInfo.fingerprint) { result.Results[i].Error = common.ServerError(fmt.Errorf("duplicate ssh key: %s", keyInfo.key)) continue } sshKeys = append(sshKeys, keyInfo.key) } err = api.writeSSHKeys(sshKeys) if err != nil { return params.ErrorResults{}, common.ServerError(err) } return result, nil } // currentKeyDataForAdd gathers data used when deleting ssh keys. func (api *KeyManagerAPI) currentKeyDataForDelete() ( keys map[string]string, invalidKeys []string, comments map[string]string, err error) { cfg, err := api.state.EnvironConfig() if err != nil { return nil, nil, nil, fmt.Errorf("reading current key data: %v", err) } // For now, authorised keys are global, common to all users. existingSSHKeys := ssh.SplitAuthorisedKeys(cfg.AuthorizedKeys()) // Build up a map of keys indexed by fingerprint, and fingerprints indexed by comment // so we can easily get the key represented by each keyId, which may be either a fingerprint // or comment. keys = make(map[string]string) comments = make(map[string]string) for _, key := range existingSSHKeys { fingerprint, comment, err := ssh.KeyFingerprint(key) if err != nil { logger.Debugf("keeping unrecognised existing ssh key %q: %v", key, err) invalidKeys = append(invalidKeys, key) continue } keys[fingerprint] = key if comment != "" { comments[comment] = fingerprint } } return keys, invalidKeys, comments, nil } // DeleteKeys deletes the authorised ssh keys for the specified user. func (api *KeyManagerAPI) DeleteKeys(arg params.ModifyUserSSHKeys) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(arg.Keys)), } if len(arg.Keys) == 0 { return result, nil } canWrite, err := api.getCanWrite() if err != nil { return params.ErrorResults{}, common.ServerError(err) } if !canWrite(arg.User) { return params.ErrorResults{}, common.ServerError(common.ErrPerm) } sshKeys, invalidKeys, keyComments, err := api.currentKeyDataForDelete() if err != nil { return params.ErrorResults{}, common.ServerError(fmt.Errorf("reading current key data: %v", err)) } // We keep all existing invalid keys. keysToWrite := invalidKeys // Find the keys corresponding to the specified key fingerprints or comments. for i, keyId := range arg.Keys { // assume keyId may be a fingerprint fingerprint := keyId _, ok := sshKeys[keyId] if !ok { // keyId is a comment fingerprint, ok = keyComments[keyId] } if !ok { result.Results[i].Error = common.ServerError(fmt.Errorf("invalid ssh key: %s", keyId)) } // We found the key to delete so remove it from those we wish to keep. delete(sshKeys, fingerprint) } for _, key := range sshKeys { keysToWrite = append(keysToWrite, key) } if len(keysToWrite) == 0 { return params.ErrorResults{}, common.ServerError(stderrors.New("cannot delete all keys")) } err = api.writeSSHKeys(keysToWrite) if err != nil { return params.ErrorResults{}, common.ServerError(err) } return result, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/keymanager/package_test.go�������������0000644�0000153�0000161�00000000372�12321735642�031113� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keymanager_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/keymanager/testing/��������������������0000755�0000153�0000161�00000000000�12321735642�027605� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/keymanager/testing/fakesshimport.go����0000644�0000153�0000161�00000001040�12321735642�033006� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "strings" sshtesting "launchpad.net/juju-core/utils/ssh/testing" ) var importResponses = map[string]string{ "lp:validuser": sshtesting.ValidKeyThree.Key, "lp:existing": sshtesting.ValidKeyTwo.Key, } var FakeImport = func(keyId string) (string, error) { response, ok := importResponses[keyId] if ok { return strings.Join([]string{"INFO: line1", response, "INFO: line3"}, "\n"), nil } return "INFO: line", nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/httphandler.go�������������������������0000644�0000153�0000161�00000003646�12321735776�026672� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver import ( "encoding/base64" "fmt" "net/http" "strings" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" ) // errorSender implementations send errors back to the caller. type errorSender interface { sendError(w http.ResponseWriter, statusCode int, message string) error } // httpHandler handles http requests through HTTPS in the API server. type httpHandler struct { // Structs which embed httpHandler provide their own errorSender implementation. errorSender state *state.State } // authenticate parses HTTP basic authentication and authorizes the // request by looking up the provided tag and password against state. func (h *httpHandler) authenticate(r *http.Request) error { parts := strings.Fields(r.Header.Get("Authorization")) if len(parts) != 2 || parts[0] != "Basic" { // Invalid header format or no header provided. return fmt.Errorf("invalid request format") } // Challenge is a base64-encoded "tag:pass" string. // See RFC 2617, Section 2. challenge, err := base64.StdEncoding.DecodeString(parts[1]) if err != nil { return fmt.Errorf("invalid request format") } tagPass := strings.SplitN(string(challenge), ":", 2) if len(tagPass) != 2 { return fmt.Errorf("invalid request format") } // Only allow users, not agents. _, _, err = names.ParseTag(tagPass[0], names.UserTagKind) if err != nil { return common.ErrBadCreds } // Ensure the credentials are correct. _, err = checkCreds(h.state, params.Creds{ AuthTag: tagPass[0], Password: tagPass[1], }) return err } // authError sends an unauthorized error. func (h *httpHandler) authError(w http.ResponseWriter) { w.Header().Set("WWW-Authenticate", `Basic realm="juju"`) h.sendError(w, http.StatusUnauthorized, "unauthorized") } ������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/agent/���������������������������������0000755�0000153�0000161�00000000000�12321736000�025070� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/agent/agent.go�������������������������0000644�0000153�0000161�00000004466�12321735776�026552� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The machine package implements the API interfaces // used by the machine agent. package agent import ( "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" ) // API implements the API provided to an agent. type API struct { *common.PasswordChanger st *state.State auth common.Authorizer } // NewAPI returns an object implementing an agent API // with the given authorizer representing the currently logged in client. func NewAPI(st *state.State, auth common.Authorizer) (*API, error) { // Agents are defined to be any user that's not a client user. if !auth.AuthMachineAgent() && !auth.AuthUnitAgent() { return nil, common.ErrPerm } getCanChange := func() (common.AuthFunc, error) { return auth.AuthOwner, nil } return &API{ PasswordChanger: common.NewPasswordChanger(st, getCanChange), st: st, auth: auth, }, nil } func (api *API) GetEntities(args params.Entities) params.AgentGetEntitiesResults { results := params.AgentGetEntitiesResults{ Entities: make([]params.AgentGetEntitiesResult, len(args.Entities)), } for i, entity := range args.Entities { result, err := api.getEntity(entity.Tag) result.Error = common.ServerError(err) results.Entities[i] = result } return results } func (api *API) getEntity(tag string) (result params.AgentGetEntitiesResult, err error) { // Allow only for the owner agent. // Note: having a bulk API call for this is utter madness, given that // this check means we can only ever return a single object. if !api.auth.AuthOwner(tag) { err = common.ErrPerm return } entity0, err := api.st.FindEntity(tag) if err != nil { return } entity, ok := entity0.(state.Lifer) if !ok { err = common.NotSupportedError(tag, "life cycles") return } result.Life = params.Life(entity.Life().String()) if machine, ok := entity.(*state.Machine); ok { result.Jobs = stateJobsToAPIParamsJobs(machine.Jobs()) result.ContainerType = machine.ContainerType() } return } func stateJobsToAPIParamsJobs(jobs []state.MachineJob) []params.MachineJob { pjobs := make([]params.MachineJob, len(jobs)) for i, job := range jobs { pjobs[i] = params.MachineJob(job.String()) } return pjobs } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/agent/agent_test.go��������������������0000644�0000153�0000161�00000012251�12321735642�027570� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package agent_test import ( stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/agent" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" coretesting "launchpad.net/juju-core/testing" ) func TestPackage(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type agentSuite struct { jujutesting.JujuConnSuite authorizer apiservertesting.FakeAuthorizer machine0 *state.Machine machine1 *state.Machine container *state.Machine agent *agent.API } var _ = gc.Suite(&agentSuite{}) func (s *agentSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) var err error s.machine0, err = s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) s.machine1, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } s.container, err = s.State.AddMachineInsideMachine(template, s.machine1.Id(), instance.LXC) c.Assert(err, gc.IsNil) // Create a FakeAuthorizer so we can check permissions, // set up assuming machine 1 has logged in. s.authorizer = apiservertesting.FakeAuthorizer{ Tag: s.machine1.Tag(), LoggedIn: true, MachineAgent: true, } // Create a machiner API for machine 1. s.agent, err = agent.NewAPI(s.State, s.authorizer) c.Assert(err, gc.IsNil) } func (s *agentSuite) TestAgentFailsWithNonAgent(c *gc.C) { auth := s.authorizer auth.MachineAgent = false auth.UnitAgent = false api, err := agent.NewAPI(s.State, auth) c.Assert(err, gc.NotNil) c.Assert(api, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *agentSuite) TestAgentSucceedsWithUnitAgent(c *gc.C) { auth := s.authorizer auth.MachineAgent = false auth.UnitAgent = true _, err := agent.NewAPI(s.State, auth) c.Assert(err, gc.IsNil) } func (s *agentSuite) TestGetEntities(c *gc.C) { err := s.container.Destroy() c.Assert(err, gc.IsNil) args := params.Entities{ Entities: []params.Entity{ {Tag: "machine-1"}, {Tag: "machine-0"}, {Tag: "machine-1-lxc-0"}, {Tag: "machine-42"}, }, } results := s.agent.GetEntities(args) c.Assert(results, gc.DeepEquals, params.AgentGetEntitiesResults{ Entities: []params.AgentGetEntitiesResult{ { Life: "alive", Jobs: []params.MachineJob{params.JobHostUnits}, }, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Now login as the machine agent of the container and try again. auth := s.authorizer auth.MachineAgent = true auth.UnitAgent = false auth.Tag = s.container.Tag() containerAgent, err := agent.NewAPI(s.State, auth) c.Assert(err, gc.IsNil) results = containerAgent.GetEntities(args) c.Assert(results, gc.DeepEquals, params.AgentGetEntitiesResults{ Entities: []params.AgentGetEntitiesResult{ {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, { Life: "dying", Jobs: []params.MachineJob{params.JobHostUnits}, ContainerType: instance.LXC, }, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *agentSuite) TestGetNotFoundEntity(c *gc.C) { // Destroy the container first, so we can destroy its parent. err := s.container.Destroy() c.Assert(err, gc.IsNil) err = s.container.EnsureDead() c.Assert(err, gc.IsNil) err = s.container.Remove() c.Assert(err, gc.IsNil) err = s.machine1.Destroy() c.Assert(err, gc.IsNil) err = s.machine1.EnsureDead() c.Assert(err, gc.IsNil) err = s.machine1.Remove() c.Assert(err, gc.IsNil) results := s.agent.GetEntities(params.Entities{ Entities: []params.Entity{{Tag: "machine-1"}}, }) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.AgentGetEntitiesResults{ Entities: []params.AgentGetEntitiesResult{{ Error: &params.Error{ Code: params.CodeNotFound, Message: "machine 1 not found", }, }}, }) } func (s *agentSuite) TestSetPasswords(c *gc.C) { results, err := s.agent.SetPasswords(params.EntityPasswords{ Changes: []params.EntityPassword{ {Tag: "machine-0", Password: "xxx-12345678901234567890"}, {Tag: "machine-1", Password: "yyy-12345678901234567890"}, {Tag: "machine-42", Password: "zzz-12345678901234567890"}, }, }) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) err = s.machine1.Refresh() c.Assert(err, gc.IsNil) changed := s.machine1.PasswordValid("yyy-12345678901234567890") c.Assert(changed, gc.Equals, true) } func (s *agentSuite) TestShortSetPasswords(c *gc.C) { results, err := s.agent.SetPasswords(params.EntityPasswords{ Changes: []params.EntityPassword{ {Tag: "machine-1", Password: "yyy"}, }, }) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.ErrorMatches, "password is only 3 bytes long, and is not a valid Agent password") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/deployer/������������������������������0000755�0000153�0000161�00000000000�12321736000�025615� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/deployer/deployer.go�������������������0000644�0000153�0000161�00000005533�12321735642�030010� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer import ( "fmt" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" ) // DeployerAPI provides access to the Deployer API facade. type DeployerAPI struct { *common.Remover *common.PasswordChanger *common.LifeGetter *common.StateAddresser *common.APIAddresser *common.UnitsWatcher st *state.State resources *common.Resources authorizer common.Authorizer } // NewDeployerAPI creates a new server-side DeployerAPI facade. func NewDeployerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*DeployerAPI, error) { if !authorizer.AuthMachineAgent() { return nil, common.ErrPerm } getAuthFunc := func() (common.AuthFunc, error) { // Get all units of the machine and cache them. thisMachineTag := authorizer.GetAuthTag() units, err := getAllUnits(st, thisMachineTag) if err != nil { return nil, err } // Then we just check if the unit is already known. return func(tag string) bool { for _, unit := range units { if names.UnitTag(unit) == tag { return true } } return false }, nil } getCanWatch := func() (common.AuthFunc, error) { return authorizer.AuthOwner, nil } return &DeployerAPI{ Remover: common.NewRemover(st, true, getAuthFunc), PasswordChanger: common.NewPasswordChanger(st, getAuthFunc), LifeGetter: common.NewLifeGetter(st, getAuthFunc), StateAddresser: common.NewStateAddresser(st), APIAddresser: common.NewAPIAddresser(st, resources), UnitsWatcher: common.NewUnitsWatcher(st, resources, getCanWatch), st: st, resources: resources, authorizer: authorizer, }, nil } // ConnectionInfo returns all the address information that the // deployer task needs in one call. func (d *DeployerAPI) ConnectionInfo() (result params.DeployerConnectionValues, err error) { info, err := d.st.DeployerConnectionInfo() if info != nil { result = params.DeployerConnectionValues{ StateAddresses: info.StateAddresses, APIAddresses: info.APIAddresses, } } return result, err } // getAllUnits returns a list of all principal and subordinate units // assigned to the given machine. func getAllUnits(st *state.State, machineTag string) ([]string, error) { _, id, err := names.ParseTag(machineTag, names.MachineTagKind) if err != nil { return nil, err } machine, err := st.Machine(id) if err != nil { return nil, err } // Start a watcher on machine's units, read the initial event and stop it. watch := machine.WatchUnits() defer watch.Stop() if units, ok := <-watch.Changes(); ok { return units, nil } return nil, fmt.Errorf("cannot obtain units of machine %q: %v", machineTag, watch.Err()) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/deployer/deployer_test.go��������������0000644�0000153�0000161�00000023423�12321735776�031055� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer_test import ( "sort" stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/apiserver/deployer" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" ) func Test(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type deployerSuite struct { testing.JujuConnSuite authorizer apiservertesting.FakeAuthorizer service0 *state.Service service1 *state.Service machine0 *state.Machine machine1 *state.Machine principal0 *state.Unit principal1 *state.Unit subordinate0 *state.Unit resources *common.Resources deployer *deployer.DeployerAPI } var _ = gc.Suite(&deployerSuite{}) func (s *deployerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) // The two known machines now contain the following units: // machine 0 (not authorized): mysql/1 (principal1) // machine 1 (authorized): mysql/0 (principal0), logging/0 (subordinate0) var err error s.machine0, err = s.State.AddMachine("quantal", state.JobManageEnviron, state.JobHostUnits) c.Assert(err, gc.IsNil) s.machine1, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) s.service0 = s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) s.service1 = s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"mysql", "logging"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) s.principal0, err = s.service0.AddUnit() c.Assert(err, gc.IsNil) err = s.principal0.AssignToMachine(s.machine1) c.Assert(err, gc.IsNil) s.principal1, err = s.service0.AddUnit() c.Assert(err, gc.IsNil) err = s.principal1.AssignToMachine(s.machine0) c.Assert(err, gc.IsNil) relUnit0, err := rel.Unit(s.principal0) c.Assert(err, gc.IsNil) err = relUnit0.EnterScope(nil) c.Assert(err, gc.IsNil) s.subordinate0, err = s.service1.Unit("logging/0") c.Assert(err, gc.IsNil) // Create a FakeAuthorizer so we can check permissions, // set up assuming machine 1 has logged in. s.authorizer = apiservertesting.FakeAuthorizer{ Tag: names.MachineTag(s.machine1.Id()), LoggedIn: true, MachineAgent: true, } // Create the resource registry separately to track invocations to // Register. s.resources = common.NewResources() // Create a deployer API for machine 1. deployer, err := deployer.NewDeployerAPI( s.State, s.resources, s.authorizer, ) c.Assert(err, gc.IsNil) s.deployer = deployer } func (s *deployerSuite) TestDeployerFailsWithNonMachineAgentUser(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.MachineAgent = false aDeployer, err := deployer.NewDeployerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.NotNil) c.Assert(aDeployer, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *deployerSuite) TestWatchUnits(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) args := params.Entities{Entities: []params.Entity{ {Tag: "machine-1"}, {Tag: "machine-0"}, {Tag: "machine-42"}, }} result, err := s.deployer.WatchUnits(args) c.Assert(err, gc.IsNil) sort.Strings(result.Results[0].Changes) c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ Results: []params.StringsWatchResult{ {Changes: []string{"logging/0", "mysql/0"}, StringsWatcherId: "1"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Verify the resource was registered and stop when done c.Assert(s.resources.Count(), gc.Equals, 1) c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1") resource := s.resources.Get("1") defer statetesting.AssertStop(c, resource) // Check that the Watch has consumed the initial event ("returned" in // the Watch call) wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) wc.AssertNoChange() } func (s *deployerSuite) TestSetPasswords(c *gc.C) { args := params.EntityPasswords{ Changes: []params.EntityPassword{ {Tag: "unit-mysql-0", Password: "xxx-12345678901234567890"}, {Tag: "unit-mysql-1", Password: "yyy-12345678901234567890"}, {Tag: "unit-logging-0", Password: "zzz-12345678901234567890"}, {Tag: "unit-fake-42", Password: "abc-12345678901234567890"}, }, } results, err := s.deployer.SetPasswords(args) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {nil}, {apiservertesting.ErrUnauthorized}, {nil}, {apiservertesting.ErrUnauthorized}, }, }) err = s.principal0.Refresh() c.Assert(err, gc.IsNil) changed := s.principal0.PasswordValid("xxx-12345678901234567890") c.Assert(changed, gc.Equals, true) err = s.subordinate0.Refresh() c.Assert(err, gc.IsNil) changed = s.subordinate0.PasswordValid("zzz-12345678901234567890") c.Assert(changed, gc.Equals, true) // Remove the subordinate and make sure it's detected. err = s.subordinate0.EnsureDead() c.Assert(err, gc.IsNil) err = s.subordinate0.Remove() c.Assert(err, gc.IsNil) err = s.subordinate0.Refresh() c.Assert(errors.IsNotFoundError(err), gc.Equals, true) results, err = s.deployer.SetPasswords(params.EntityPasswords{ Changes: []params.EntityPassword{ {Tag: "unit-logging-0", Password: "blah-12345678901234567890"}, }, }) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {apiservertesting.ErrUnauthorized}, }, }) } func (s *deployerSuite) TestLife(c *gc.C) { err := s.subordinate0.EnsureDead() c.Assert(err, gc.IsNil) err = s.subordinate0.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.subordinate0.Life(), gc.Equals, state.Dead) err = s.principal0.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.principal0.Life(), gc.Equals, state.Alive) args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-mysql-1"}, {Tag: "unit-logging-0"}, {Tag: "unit-fake-42"}, }} result, err := s.deployer.Life(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.LifeResults{ Results: []params.LifeResult{ {Life: "alive"}, {Error: apiservertesting.ErrUnauthorized}, {Life: "dead"}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Remove the subordinate and make sure it's detected. err = s.subordinate0.EnsureDead() c.Assert(err, gc.IsNil) err = s.subordinate0.Remove() c.Assert(err, gc.IsNil) err = s.subordinate0.Refresh() c.Assert(errors.IsNotFoundError(err), gc.Equals, true) result, err = s.deployer.Life(params.Entities{ Entities: []params.Entity{ {Tag: "unit-logging-0"}, }, }) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.LifeResults{ Results: []params.LifeResult{ {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *deployerSuite) TestRemove(c *gc.C) { c.Assert(s.principal0.Life(), gc.Equals, state.Alive) c.Assert(s.subordinate0.Life(), gc.Equals, state.Alive) // Try removing alive units - should fail. args := params.Entities{Entities: []params.Entity{ {Tag: "unit-mysql-0"}, {Tag: "unit-mysql-1"}, {Tag: "unit-logging-0"}, {Tag: "unit-fake-42"}, }} result, err := s.deployer.Remove(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {&params.Error{Message: `cannot remove entity "unit-mysql-0": still alive`}}, {apiservertesting.ErrUnauthorized}, {&params.Error{Message: `cannot remove entity "unit-logging-0": still alive`}}, {apiservertesting.ErrUnauthorized}, }, }) err = s.principal0.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.principal0.Life(), gc.Equals, state.Alive) err = s.subordinate0.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.subordinate0.Life(), gc.Equals, state.Alive) // Now make the subordinate dead and try again. err = s.subordinate0.EnsureDead() c.Assert(err, gc.IsNil) err = s.subordinate0.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.subordinate0.Life(), gc.Equals, state.Dead) args = params.Entities{ Entities: []params.Entity{{Tag: "unit-logging-0"}}, } result, err = s.deployer.Remove(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{{nil}}, }) err = s.subordinate0.Refresh() c.Assert(errors.IsNotFoundError(err), gc.Equals, true) // Make sure the subordinate is detected as removed. result, err = s.deployer.Remove(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{{apiservertesting.ErrUnauthorized}}, }) } func (s *deployerSuite) TestStateAddresses(c *gc.C) { err := s.machine0.SetAddresses([]instance.Address{ instance.NewAddress("0.1.2.3"), }) c.Assert(err, gc.IsNil) addresses, err := s.State.Addresses() c.Assert(err, gc.IsNil) result, err := s.deployer.StateAddresses() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringsResult{ Result: addresses, }) } func (s *deployerSuite) TestAPIAddresses(c *gc.C) { err := s.machine0.SetAddresses([]instance.Address{ instance.NewAddress("0.1.2.3"), }) c.Assert(err, gc.IsNil) apiAddresses, err := s.State.APIAddressesFromMachines() c.Assert(err, gc.IsNil) result, err := s.deployer.APIAddresses() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringsResult{ Result: apiAddresses, }) } func (s *deployerSuite) TestCACert(c *gc.C) { result := s.deployer.CACert() c.Assert(result, gc.DeepEquals, params.BytesResult{ Result: s.State.CACert(), }) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/testing/�������������������������������0000755�0000153�0000161�00000000000�12321736000�025447� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/testing/fakeauthorizer.go��������������0000644�0000153�0000161�00000001635�12321735642�031041� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "launchpad.net/juju-core/state" ) // FakeAuthorizer implements the common.Authorizer interface. type FakeAuthorizer struct { Tag string LoggedIn bool EnvironManager bool MachineAgent bool UnitAgent bool Client bool Entity state.Entity } func (fa FakeAuthorizer) AuthOwner(tag string) bool { return fa.Tag == tag } func (fa FakeAuthorizer) AuthEnvironManager() bool { return fa.EnvironManager } func (fa FakeAuthorizer) AuthMachineAgent() bool { return fa.MachineAgent } func (fa FakeAuthorizer) AuthUnitAgent() bool { return fa.UnitAgent } func (fa FakeAuthorizer) AuthClient() bool { return fa.Client } func (fa FakeAuthorizer) GetAuthTag() string { return fa.Tag } func (fa FakeAuthorizer) GetAuthEntity() state.Entity { return fa.Entity } ���������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/testing/errors.go����������������������0000644�0000153�0000161�00000001671�12321735776�027342� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "fmt" "launchpad.net/juju-core/state/api/params" ) var ErrUnauthorized = &params.Error{ Message: "permission denied", Code: params.CodeUnauthorized, } func NotFoundError(prefixMessage string) *params.Error { return &params.Error{ Message: fmt.Sprintf("%s not found", prefixMessage), Code: params.CodeNotFound, } } func NotProvisionedError(machineId string) *params.Error { return &params.Error{ Message: fmt.Sprintf("machine %s is not provisioned", machineId), Code: params.CodeNotProvisioned, } } func NotAssignedError(unitName string) *params.Error { return &params.Error{ Message: fmt.Sprintf("unit %q is not assigned to a machine", unitName), Code: params.CodeNotAssigned, } } func ServerError(message string) *params.Error { return &params.Error{ Message: message, Code: "", } } �����������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/root.go��������������������������������0000644�0000153�0000161�00000033223�12321735776�025332� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver import ( "errors" "time" "launchpad.net/tomb" "launchpad.net/juju-core/names" "launchpad.net/juju-core/rpc" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/apiserver/agent" "launchpad.net/juju-core/state/apiserver/charmrevisionupdater" "launchpad.net/juju-core/state/apiserver/client" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/apiserver/deployer" "launchpad.net/juju-core/state/apiserver/environment" "launchpad.net/juju-core/state/apiserver/firewaller" "launchpad.net/juju-core/state/apiserver/keymanager" "launchpad.net/juju-core/state/apiserver/keyupdater" loggerapi "launchpad.net/juju-core/state/apiserver/logger" "launchpad.net/juju-core/state/apiserver/machine" "launchpad.net/juju-core/state/apiserver/provisioner" "launchpad.net/juju-core/state/apiserver/rsyslog" "launchpad.net/juju-core/state/apiserver/uniter" "launchpad.net/juju-core/state/apiserver/upgrader" "launchpad.net/juju-core/state/apiserver/usermanager" "launchpad.net/juju-core/state/multiwatcher" ) type clientAPI struct{ *client.API } type taggedAuthenticator interface { state.Entity state.Authenticator } // maxPingInterval defines the timeframe until the ping // timeout closes the monitored connection. // TODO(mue): Idea by Roger: Move to API (e.g. params) so // that the pinging there may depend on the interval. var maxPingInterval = 3 * time.Minute // srvRoot represents a single client's connection to the state // after it has logged in. type srvRoot struct { clientAPI srv *Server rpcConn *rpc.Conn resources *common.Resources pingTimeout *pingTimeout entity taggedAuthenticator } // newSrvRoot creates the client's connection representation // and starts a ping timeout for the monitoring of this // connection. func newSrvRoot(root *initialRoot, entity taggedAuthenticator) *srvRoot { r := &srvRoot{ srv: root.srv, rpcConn: root.rpcConn, resources: common.NewResources(), entity: entity, } r.clientAPI.API = client.NewAPI(r.srv.state, r.resources, r, r.srv.dataDir) return r } // Kill implements rpc.Killer. It cleans up any resources that need // cleaning up to ensure that all outstanding requests return. func (r *srvRoot) Kill() { r.resources.StopAll() if r.pingTimeout != nil { r.pingTimeout.stop() } } // requireAgent checks whether the current client is an agent and hence // may access the agent APIs. We filter out non-agents when calling one // of the accessor functions (Machine, Unit, etc) which avoids us making // the check in every single request method. func (r *srvRoot) requireAgent() error { if !isAgent(r.entity) { return common.ErrPerm } return nil } // requireClient returns an error unless the current // client is a juju client user. func (r *srvRoot) requireClient() error { if isAgent(r.entity) { return common.ErrPerm } return nil } // KeyManager returns an object that provides access to the KeyManager API // facade. The id argument is reserved for future use and currently // needs to be empty. func (r *srvRoot) KeyManager(id string) (*keymanager.KeyManagerAPI, error) { if id != "" { return nil, common.ErrBadId } return keymanager.NewKeyManagerAPI(r.srv.state, r.resources, r) } // UserManager returns an object that provides access to the UserManager API // facade. The id argument is reserved for future use and currently // needs to be empty func (r *srvRoot) UserManager(id string) (*usermanager.UserManagerAPI, error) { if id != "" { return nil, common.ErrBadId } return usermanager.NewUserManagerAPI(r.srv.state, r) } // Machiner returns an object that provides access to the Machiner API // facade. The id argument is reserved for future use and currently // needs to be empty. func (r *srvRoot) Machiner(id string) (*machine.MachinerAPI, error) { if id != "" { // Safeguard id for possible future use. return nil, common.ErrBadId } return machine.NewMachinerAPI(r.srv.state, r.resources, r) } // Provisioner returns an object that provides access to the // Provisioner API facade. The id argument is reserved for future use // and currently needs to be empty. func (r *srvRoot) Provisioner(id string) (*provisioner.ProvisionerAPI, error) { if id != "" { // Safeguard id for possible future use. return nil, common.ErrBadId } return provisioner.NewProvisionerAPI(r.srv.state, r.resources, r) } // Uniter returns an object that provides access to the Uniter API // facade. The id argument is reserved for future use and currently // needs to be empty. func (r *srvRoot) Uniter(id string) (*uniter.UniterAPI, error) { if id != "" { // Safeguard id for possible future use. return nil, common.ErrBadId } return uniter.NewUniterAPI(r.srv.state, r.resources, r) } // Firewaller returns an object that provides access to the Firewaller // API facade. The id argument is reserved for future use and // currently needs to be empty. func (r *srvRoot) Firewaller(id string) (*firewaller.FirewallerAPI, error) { if id != "" { // Safeguard id for possible future use. return nil, common.ErrBadId } return firewaller.NewFirewallerAPI(r.srv.state, r.resources, r) } // Agent returns an object that provides access to the // agent API. The id argument is reserved for future use and must currently // be empty. func (r *srvRoot) Agent(id string) (*agent.API, error) { if id != "" { return nil, common.ErrBadId } return agent.NewAPI(r.srv.state, r) } // Deployer returns an object that provides access to the Deployer API facade. // The id argument is reserved for future use and must be empty. func (r *srvRoot) Deployer(id string) (*deployer.DeployerAPI, error) { if id != "" { // TODO(dimitern): There is no direct test for this return nil, common.ErrBadId } return deployer.NewDeployerAPI(r.srv.state, r.resources, r) } // Environment returns an object that provides access to the Environment API // facade. The id argument is reserved for future use and currently needs to // be empty. func (r *srvRoot) Environment(id string) (*environment.EnvironmentAPI, error) { if id != "" { // Safeguard id for possible future use. return nil, common.ErrBadId } return environment.NewEnvironmentAPI(r.srv.state, r.resources, r) } // Rsyslog returns an object that provides access to the Rsyslog API // facade. The id argument is reserved for future use and currently needs to // be empty. func (r *srvRoot) Rsyslog(id string) (*rsyslog.RsyslogAPI, error) { if id != "" { // Safeguard id for possible future use. return nil, common.ErrBadId } return rsyslog.NewRsyslogAPI(r.srv.state, r.resources, r) } // Logger returns an object that provides access to the Logger API facade. // The id argument is reserved for future use and must be empty. func (r *srvRoot) Logger(id string) (*loggerapi.LoggerAPI, error) { if id != "" { // TODO: There is no direct test for this return nil, common.ErrBadId } return loggerapi.NewLoggerAPI(r.srv.state, r.resources, r) } // Upgrader returns an object that provides access to the Upgrader API facade. // The id argument is reserved for future use and must be empty. func (r *srvRoot) Upgrader(id string) (upgrader.Upgrader, error) { if id != "" { // TODO: There is no direct test for this return nil, common.ErrBadId } // The type of upgrader we return depends on who is asking. // Machines get an UpgraderAPI, units get a UnitUpgraderAPI. // This is tested in the state/api/upgrader package since there // are currently no direct srvRoot tests. tagKind, _, err := names.ParseTag(r.GetAuthTag(), "") if err != nil { return nil, common.ErrPerm } switch tagKind { case names.MachineTagKind: return upgrader.NewUpgraderAPI(r.srv.state, r.resources, r) case names.UnitTagKind: return upgrader.NewUnitUpgraderAPI(r.srv.state, r.resources, r, r.srv.dataDir) } // Not a machine or unit. return nil, common.ErrPerm } // KeyUpdater returns an object that provides access to the KeyUpdater API facade. // The id argument is reserved for future use and must be empty. func (r *srvRoot) KeyUpdater(id string) (*keyupdater.KeyUpdaterAPI, error) { if id != "" { // TODO: There is no direct test for this return nil, common.ErrBadId } return keyupdater.NewKeyUpdaterAPI(r.srv.state, r.resources, r) } // CharmRevisionUpdater returns an object that provides access to the CharmRevisionUpdater API facade. // The id argument is reserved for future use and must be empty. func (r *srvRoot) CharmRevisionUpdater(id string) (*charmrevisionupdater.CharmRevisionUpdaterAPI, error) { if id != "" { // TODO: There is no direct test for this return nil, common.ErrBadId } return charmrevisionupdater.NewCharmRevisionUpdaterAPI(r.srv.state, r.resources, r) } // NotifyWatcher returns an object that provides // API access to methods on a state.NotifyWatcher. // Each client has its own current set of watchers, stored // in r.resources. func (r *srvRoot) NotifyWatcher(id string) (*srvNotifyWatcher, error) { if err := r.requireAgent(); err != nil { return nil, err } watcher, ok := r.resources.Get(id).(state.NotifyWatcher) if !ok { return nil, common.ErrUnknownWatcher } return &srvNotifyWatcher{ watcher: watcher, id: id, resources: r.resources, }, nil } // StringsWatcher returns an object that provides API access to // methods on a state.StringsWatcher. Each client has its own // current set of watchers, stored in r.resources. func (r *srvRoot) StringsWatcher(id string) (*srvStringsWatcher, error) { if err := r.requireAgent(); err != nil { return nil, err } watcher, ok := r.resources.Get(id).(state.StringsWatcher) if !ok { return nil, common.ErrUnknownWatcher } return &srvStringsWatcher{ watcher: watcher, id: id, resources: r.resources, }, nil } // RelationUnitsWatcher returns an object that provides API access to // methods on a state.RelationUnitsWatcher. Each client has its own // current set of watchers, stored in r.resources. func (r *srvRoot) RelationUnitsWatcher(id string) (*srvRelationUnitsWatcher, error) { if err := r.requireAgent(); err != nil { return nil, err } watcher, ok := r.resources.Get(id).(state.RelationUnitsWatcher) if !ok { return nil, common.ErrUnknownWatcher } return &srvRelationUnitsWatcher{ watcher: watcher, id: id, resources: r.resources, }, nil } // AllWatcher returns an object that provides API access to methods on // a state/multiwatcher.Watcher, which watches any changes to the // state. Each client has its own current set of watchers, stored in // r.resources. func (r *srvRoot) AllWatcher(id string) (*srvClientAllWatcher, error) { if err := r.requireClient(); err != nil { return nil, err } watcher, ok := r.resources.Get(id).(*multiwatcher.Watcher) if !ok { return nil, common.ErrUnknownWatcher } return &srvClientAllWatcher{ watcher: watcher, id: id, resources: r.resources, }, nil } // Pinger returns an object that can be pinged // by calling its Ping method. If this method // is not called frequently enough, the connection // will be dropped. func (r *srvRoot) Pinger(id string) (pinger, error) { if r.pingTimeout == nil { return nullPinger{}, nil } return r.pingTimeout, nil } type nullPinger struct{} func (nullPinger) Ping() {} // AuthMachineAgent returns whether the current client is a machine agent. func (r *srvRoot) AuthMachineAgent() bool { _, ok := r.entity.(*state.Machine) return ok } // AuthUnitAgent returns whether the current client is a unit agent. func (r *srvRoot) AuthUnitAgent() bool { _, ok := r.entity.(*state.Unit) return ok } // AuthOwner returns whether the authenticated user's tag matches the // given entity tag. func (r *srvRoot) AuthOwner(tag string) bool { return r.entity.Tag() == tag } // AuthEnvironManager returns whether the authenticated user is a // machine with running the ManageEnviron job. func (r *srvRoot) AuthEnvironManager() bool { return isMachineWithJob(r.entity, state.JobManageEnviron) } // AuthClient returns whether the authenticated entity is a client // user. func (r *srvRoot) AuthClient() bool { return !isAgent(r.entity) } // GetAuthTag returns the tag of the authenticated entity. func (r *srvRoot) GetAuthTag() string { return r.entity.Tag() } // GetAuthEntity returns the authenticated entity. func (r *srvRoot) GetAuthEntity() state.Entity { return r.entity } // pinger describes a type that can be pinged. type pinger interface { Ping() } // pingTimeout listens for pings and will call the // passed action in case of a timeout. This way broken // or inactive connections can be closed. type pingTimeout struct { tomb tomb.Tomb action func() timeout time.Duration reset chan struct{} } // newPingTimeout returns a new pingTimeout instance // that invokes the given action asynchronously if there // is more than the given timeout interval between calls // to its Ping method. func newPingTimeout(action func(), timeout time.Duration) *pingTimeout { pt := &pingTimeout{ action: action, timeout: timeout, reset: make(chan struct{}), } go func() { defer pt.tomb.Done() pt.tomb.Kill(pt.loop()) }() return pt } // Ping is used by the client heartbeat monitor and resets // the killer. func (pt *pingTimeout) Ping() { select { case <-pt.tomb.Dying(): case pt.reset <- struct{}{}: } } // stop terminates the ping timeout. func (pt *pingTimeout) stop() error { pt.tomb.Kill(nil) return pt.tomb.Wait() } // loop waits for a reset signal, otherwise it performs // the initially passed action. func (pt *pingTimeout) loop() error { timer := time.NewTimer(pt.timeout) defer timer.Stop() for { select { case <-pt.tomb.Dying(): return nil case <-timer.C: go pt.action() return errors.New("ping timeout") case <-pt.reset: timer.Reset(pt.timeout) } } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/server_test.go�������������������������0000644�0000153�0000161�00000013373�12321735776�026720� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver_test import ( "io" stdtesting "testing" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/rpc" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } var fastDialOpts = api.DialOpts{} type serverSuite struct { jujutesting.JujuConnSuite } var _ = gc.Suite(&serverSuite{}) func (s *serverSuite) TestStop(c *gc.C) { // Start our own instance of the server so we have // a handle on it to stop it. srv, err := apiserver.NewServer(s.State, "localhost:0", []byte(coretesting.ServerCert), []byte(coretesting.ServerKey), "") c.Assert(err, gc.IsNil) defer srv.Stop() stm, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = stm.SetProvisioned("foo", "fake_nonce", nil) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = stm.SetPassword(password) c.Assert(err, gc.IsNil) // Note we can't use openAs because we're not connecting to // s.APIConn. apiInfo := &api.Info{ Tag: stm.Tag(), Password: password, Nonce: "fake_nonce", Addrs: []string{srv.Addr()}, CACert: []byte(coretesting.CACert), } st, err := api.Open(apiInfo, fastDialOpts) c.Assert(err, gc.IsNil) defer st.Close() _, err = st.Machiner().Machine(stm.Tag()) c.Assert(err, gc.IsNil) err = srv.Stop() c.Assert(err, gc.IsNil) _, err = st.Machiner().Machine(stm.Tag()) // The client has not necessarily seen the server shutdown yet, // so there are two possible errors. if err != rpc.ErrShutdown && err != io.ErrUnexpectedEOF { c.Fatalf("unexpected error from request: %v", err) } // Check it can be stopped twice. err = srv.Stop() c.Assert(err, gc.IsNil) } func (s *serverSuite) TestOpenAsMachineErrors(c *gc.C) { assertNotProvisioned := func(err error) { c.Assert(err, gc.NotNil) c.Assert(err, jc.Satisfies, params.IsCodeNotProvisioned) c.Assert(err, gc.ErrorMatches, `machine \d+ is not provisioned`) } stm, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = stm.SetProvisioned("foo", "fake_nonce", nil) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = stm.SetPassword(password) c.Assert(err, gc.IsNil) // This does almost exactly the same as OpenAPIAsMachine but checks // for failures instead. _, info, err := s.APIConn.Environ.StateInfo() info.Tag = stm.Tag() info.Password = password info.Nonce = "invalid-nonce" st, err := api.Open(info, fastDialOpts) assertNotProvisioned(err) c.Assert(st, gc.IsNil) // Try with empty nonce as well. info.Nonce = "" st, err = api.Open(info, fastDialOpts) assertNotProvisioned(err) c.Assert(st, gc.IsNil) // Finally, with the correct one succeeds. info.Nonce = "fake_nonce" st, err = api.Open(info, fastDialOpts) c.Assert(err, gc.IsNil) c.Assert(st, gc.NotNil) st.Close() // Now add another machine, intentionally unprovisioned. stm1, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = stm1.SetPassword(password) c.Assert(err, gc.IsNil) // Try connecting, it will fail. info.Tag = stm1.Tag() info.Nonce = "" st, err = api.Open(info, fastDialOpts) assertNotProvisioned(err) c.Assert(st, gc.IsNil) } func (s *serverSuite) TestMachineLoginStartsPinger(c *gc.C) { // This is the same steps as OpenAPIAsNewMachine but we need to assert // the agent is not alive before we actually open the API. // Create a new machine to verify "agent alive" behavior. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("foo", "fake_nonce", nil) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = machine.SetPassword(password) c.Assert(err, gc.IsNil) // Not alive yet. s.assertAlive(c, machine, false) // Login as the machine agent of the created machine. st := s.OpenAPIAsMachine(c, machine.Tag(), password, "fake_nonce") // Make sure the pinger has started. s.assertAlive(c, machine, true) // Now make sure it stops when connection is closed. c.Assert(st.Close(), gc.IsNil) // Sync, then wait for a bit to make sure the state is updated. s.State.StartSync() <-time.After(coretesting.ShortWait) s.State.StartSync() s.assertAlive(c, machine, false) } func (s *serverSuite) TestUnitLoginStartsPinger(c *gc.C) { // Create a new service and unit to verify "agent alive" behavior. service := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) unit, err := service.AddUnit() c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = unit.SetPassword(password) c.Assert(err, gc.IsNil) // Not alive yet. s.assertAlive(c, unit, false) // Login as the unit agent of the created unit. st := s.OpenAPIAs(c, unit.Tag(), password) // Make sure the pinger has started. s.assertAlive(c, unit, true) // Now make sure it stops when connection is closed. c.Assert(st.Close(), gc.IsNil) // Sync, then wait for a bit to make sure the state is updated. s.State.StartSync() <-time.After(coretesting.ShortWait) s.State.StartSync() s.assertAlive(c, unit, false) } type agentAliver interface { AgentAlive() (bool, error) } func (s *serverSuite) assertAlive(c *gc.C, entity agentAliver, isAlive bool) { s.State.StartSync() alive, err := entity.AgentAlive() c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, isAlive) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/login_test.go��������������������������0000644�0000153�0000161�00000011774�12321735776�026525� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package apiserver_test import ( "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) type loginSuite struct { jujutesting.JujuConnSuite } var _ = gc.Suite(&loginSuite{}) var badLoginTests = []struct { tag string password string err string code string }{{ tag: "user-admin", password: "wrong password", err: "invalid entity name or password", code: params.CodeUnauthorized, }, { tag: "user-foo", password: "password", err: "invalid entity name or password", code: params.CodeUnauthorized, }, { tag: "bar", password: "password", err: `"bar" is not a valid tag`, }} func (s *loginSuite) setupServer(c *gc.C) (*api.Info, func()) { srv, err := apiserver.NewServer( s.State, "localhost:0", []byte(coretesting.ServerCert), []byte(coretesting.ServerKey), "", ) c.Assert(err, gc.IsNil) info := &api.Info{ Tag: "", Password: "", Addrs: []string{srv.Addr()}, CACert: []byte(coretesting.CACert), } return info, func() { err := srv.Stop() c.Assert(err, gc.IsNil) } } func (s *loginSuite) TestBadLogin(c *gc.C) { // Start our own server so we can control when the first login // happens. Otherwise in JujuConnSuite.SetUpTest api.Open is // called with user-admin permissions automatically. info, cleanup := s.setupServer(c) defer cleanup() for i, t := range badLoginTests { c.Logf("test %d; entity %q; password %q", i, t.tag, t.password) // Note that Open does not log in if the tag and password // are empty. This allows us to test operations on the connection // before calling Login, which we could not do if Open // always logged in. info.Tag = "" info.Password = "" func() { st, err := api.Open(info, fastDialOpts) c.Assert(err, gc.IsNil) defer st.Close() _, err = st.Machiner().Machine("0") c.Assert(err, gc.ErrorMatches, `unknown object type "Machiner"`) // Since these are user login tests, the nonce is empty. err = st.Login(t.tag, t.password, "") c.Assert(err, gc.ErrorMatches, t.err) c.Assert(params.ErrCode(err), gc.Equals, t.code) _, err = st.Machiner().Machine("0") c.Assert(err, gc.ErrorMatches, `unknown object type "Machiner"`) }() } } func (s *loginSuite) TestLoginAsDeactivatedUser(c *gc.C) { info, cleanup := s.setupServer(c) defer cleanup() info.Tag = "" info.Password = "" st, err := api.Open(info, fastDialOpts) c.Assert(err, gc.IsNil) defer st.Close() u, err := s.State.AddUser("inactive", "password") c.Assert(err, gc.IsNil) err = u.Deactivate() c.Assert(err, gc.IsNil) _, err = st.Client().Status([]string{}) c.Assert(err, gc.ErrorMatches, `unknown object type "Client"`) // Since these are user login tests, the nonce is empty. err = st.Login("user-inactive", "password", "") c.Assert(err, gc.ErrorMatches, "invalid entity name or password") _, err = st.Client().Status([]string{}) c.Assert(err, gc.ErrorMatches, `unknown object type "Client"`) } func (s *loginSuite) TestLoginSetsLogIdentifier(c *gc.C) { info, cleanup := s.setupServer(c) defer cleanup() machineInState, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machineInState.SetProvisioned("foo", "fake_nonce", nil) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = machineInState.SetPassword(password) c.Assert(err, gc.IsNil) c.Assert(machineInState.Tag(), gc.Equals, "machine-0") tw := &loggo.TestWriter{} c.Assert(loggo.RegisterWriter("login-tester", tw, loggo.DEBUG), gc.IsNil) defer loggo.RemoveWriter("login-tester") info.Tag = machineInState.Tag() info.Password = password info.Nonce = "fake_nonce" apiConn, err := api.Open(info, fastDialOpts) c.Assert(err, gc.IsNil) apiMachine, err := apiConn.Machiner().Machine(machineInState.Tag()) c.Assert(err, gc.IsNil) c.Assert(apiMachine.Tag(), gc.Equals, machineInState.Tag()) apiConn.Close() c.Assert(tw.Log, jc.LogMatches, []string{ `<- \[[0-9A-F]+\] <unknown> {"RequestId":1,"Type":"Admin","Request":"Login","Params":` + `{"AuthTag":"machine-0","Password":"[^"]*","Nonce":"fake_nonce"}` + `}`, // Now that we are logged in, we see the entity's tag // [0-9.umns] is to handle timestamps that are ns, us, ms, or s // long, though we expect it to be in the 'ms' range. `-> \[[0-9A-F]+\] machine-0 [0-9.]+[umn]?s {"RequestId":1,"Response":{}} Admin\[""\].Login`, `<- \[[0-9A-F]+\] machine-0 {"RequestId":2,"Type":"Machiner","Request":"Life","Params":{"Entities":\[{"Tag":"machine-0"}\]}}`, `-> \[[0-9A-F]+\] machine-0 [0-9.umns]+ {"RequestId":2,"Response":{"Results":\[{"Life":"alive","Error":null}\]}} Machiner\[""\]\.Life`, }) } ����juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/firewaller/����������������������������0000755�0000153�0000161�00000000000�12321736000�026126� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/firewaller/firewaller.go���������������0000644�0000153�0000161�00000014030�12321735642�030622� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller import ( "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" ) // FirewallerAPI provides access to the Firewaller API facade. type FirewallerAPI struct { *common.LifeGetter *common.EnvironWatcher *common.AgentEntityWatcher *common.UnitsWatcher *common.EnvironMachinesWatcher *common.InstanceIdGetter st *state.State resources *common.Resources authorizer common.Authorizer accessUnit common.GetAuthFunc accessService common.GetAuthFunc } // NewFirewallerAPI creates a new server-side FirewallerAPI facade. func NewFirewallerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*FirewallerAPI, error) { if !authorizer.AuthEnvironManager() { // Firewaller must run as environment manager. return nil, common.ErrPerm } // Set up the various authorization checkers. accessUnit := getAuthFuncForTagKind(names.UnitTagKind) accessService := getAuthFuncForTagKind(names.ServiceTagKind) accessMachine := getAuthFuncForTagKind(names.MachineTagKind) accessEnviron := getAuthFuncForTagKind("") accessUnitOrService := common.AuthEither(accessUnit, accessService) accessUnitServiceOrMachine := common.AuthEither(accessUnitOrService, accessMachine) // Life() is supported for units, services or machines. lifeGetter := common.NewLifeGetter( st, accessUnitServiceOrMachine, ) // EnvironConfig() and WatchForEnvironConfigChanges() are allowed // with unrestriced access. environWatcher := common.NewEnvironWatcher( st, resources, accessEnviron, accessEnviron, ) // Watch() is supported for units or services. entityWatcher := common.NewAgentEntityWatcher( st, resources, accessUnitOrService, ) // WatchUnits() is supported for machines. unitsWatcher := common.NewUnitsWatcher(st, resources, accessMachine, ) // WatchEnvironMachines() is allowed with unrestricted access. machinesWatcher := common.NewEnvironMachinesWatcher( st, resources, accessEnviron, ) // InstanceId() is supported for machines. instanceIdGetter := common.NewInstanceIdGetter( st, accessMachine, ) return &FirewallerAPI{ LifeGetter: lifeGetter, EnvironWatcher: environWatcher, AgentEntityWatcher: entityWatcher, UnitsWatcher: unitsWatcher, EnvironMachinesWatcher: machinesWatcher, InstanceIdGetter: instanceIdGetter, st: st, resources: resources, authorizer: authorizer, accessUnit: accessUnit, accessService: accessService, }, nil } // OpenedPorts returns the list of opened ports for each given unit. func (f *FirewallerAPI) OpenedPorts(args params.Entities) (params.PortsResults, error) { result := params.PortsResults{ Results: make([]params.PortsResult, len(args.Entities)), } canAccess, err := f.accessUnit() if err != nil { return params.PortsResults{}, err } for i, entity := range args.Entities { var unit *state.Unit unit, err = f.getUnit(canAccess, entity.Tag) if err == nil { result.Results[i].Ports = unit.OpenedPorts() } result.Results[i].Error = common.ServerError(err) } return result, nil } // GetExposed returns the exposed flag value for each given service. func (f *FirewallerAPI) GetExposed(args params.Entities) (params.BoolResults, error) { result := params.BoolResults{ Results: make([]params.BoolResult, len(args.Entities)), } canAccess, err := f.accessService() if err != nil { return params.BoolResults{}, err } for i, entity := range args.Entities { var service *state.Service service, err = f.getService(canAccess, entity.Tag) if err == nil { result.Results[i].Result = service.IsExposed() } result.Results[i].Error = common.ServerError(err) } return result, nil } // GetAssignedMachine returns the assigned machine tag (if any) for // each given unit. func (f *FirewallerAPI) GetAssignedMachine(args params.Entities) (params.StringResults, error) { result := params.StringResults{ Results: make([]params.StringResult, len(args.Entities)), } canAccess, err := f.accessUnit() if err != nil { return params.StringResults{}, err } for i, entity := range args.Entities { var unit *state.Unit unit, err = f.getUnit(canAccess, entity.Tag) if err == nil { var machineId string machineId, err = unit.AssignedMachineId() if err == nil { result.Results[i].Result = names.MachineTag(machineId) } } result.Results[i].Error = common.ServerError(err) } return result, nil } func (f *FirewallerAPI) getEntity(canAccess common.AuthFunc, tag string) (state.Entity, error) { if !canAccess(tag) { return nil, common.ErrPerm } return f.st.FindEntity(tag) } func (f *FirewallerAPI) getUnit(canAccess common.AuthFunc, tag string) (*state.Unit, error) { entity, err := f.getEntity(canAccess, tag) if err != nil { return nil, err } // The authorization function guarantees that the tag represents a // unit. return entity.(*state.Unit), nil } func (f *FirewallerAPI) getService(canAccess common.AuthFunc, tag string) (*state.Service, error) { entity, err := f.getEntity(canAccess, tag) if err != nil { return nil, err } // The authorization function guarantees that the tag represents a // service. return entity.(*state.Service), nil } // getAuthFuncForTagKind returns a GetAuthFunc which creates an // AuthFunc allowing only the given tag kind and denies all // others. In the special case where a single empty string is given, // it's assumed only environment tags are allowed, but not specified // (for now). func getAuthFuncForTagKind(kind string) common.GetAuthFunc { return func() (common.AuthFunc, error) { return func(tag string) bool { if tag == "" { // Assume an empty tag means a missing environment tag. return kind == "" } // Allow only the given tag kind. _, _, err := names.ParseTag(tag, kind) if err != nil { return false } return true }, nil } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/firewaller/firewaller_test.go����������0000644�0000153�0000161�00000032725�12321735776�031704� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller_test import ( stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" commontesting "launchpad.net/juju-core/state/apiserver/common/testing" "launchpad.net/juju-core/state/apiserver/firewaller" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" ) func Test(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type firewallerSuite struct { testing.JujuConnSuite *commontesting.EnvironWatcherTest machines []*state.Machine service *state.Service charm *state.Charm units []*state.Unit authorizer apiservertesting.FakeAuthorizer resources *common.Resources firewaller *firewaller.FirewallerAPI } var _ = gc.Suite(&firewallerSuite{}) func (s *firewallerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) // Reset previous machines and units (if any) and create 3 // machines for the tests. s.machines = nil s.units = nil // Note that the specific machine ids allocated are assumed // to be numerically consecutive from zero. for i := 0; i <= 2; i++ { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Check(err, gc.IsNil) s.machines = append(s.machines, machine) } // Create a service and three units for these machines. s.charm = s.AddTestingCharm(c, "wordpress") s.service = s.AddTestingService(c, "wordpress", s.charm) // Add the rest of the units and assign them. for i := 0; i <= 2; i++ { unit, err := s.service.AddUnit() c.Check(err, gc.IsNil) err = unit.AssignToMachine(s.machines[i]) c.Check(err, gc.IsNil) s.units = append(s.units, unit) } // Create a FakeAuthorizer so we can check permissions, // set up assuming we logged in as the environment manager. s.authorizer = apiservertesting.FakeAuthorizer{ LoggedIn: true, EnvironManager: true, } // Create the resource registry separately to track invocations to // Register. s.resources = common.NewResources() // Create a firewaller API for the machine. firewallerAPI, err := firewaller.NewFirewallerAPI( s.State, s.resources, s.authorizer, ) c.Assert(err, gc.IsNil) s.firewaller = firewallerAPI s.EnvironWatcherTest = commontesting.NewEnvironWatcherTest(s.firewaller, s.State, s.resources, commontesting.HasSecrets) } func (s *firewallerSuite) TestFirewallerFailsWithNonEnvironManagerUser(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.MachineAgent = true anAuthorizer.EnvironManager = false aFirewaller, err := firewaller.NewFirewallerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.NotNil) c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(aFirewaller, gc.IsNil) } func (s *firewallerSuite) TestLife(c *gc.C) { // Unassign unit 1 from its machine, so we can change its life cycle. err := s.units[1].UnassignFromMachine() c.Assert(err, gc.IsNil) err = s.machines[1].EnsureDead() c.Assert(err, gc.IsNil) s.assertLife(c, 0, state.Alive) s.assertLife(c, 1, state.Dead) s.assertLife(c, 2, state.Alive) args := addFakeEntities(params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: s.machines[1].Tag()}, {Tag: s.machines[2].Tag()}, }}) result, err := s.firewaller.Life(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.LifeResults{ Results: []params.LifeResult{ {Life: "alive"}, {Life: "dead"}, {Life: "alive"}, {Error: apiservertesting.NotFoundError("machine 42")}, {Error: apiservertesting.NotFoundError(`unit "foo/0"`)}, {Error: apiservertesting.NotFoundError(`service "bar"`)}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Remove a machine and make sure it's detected. err = s.machines[1].Remove() c.Assert(err, gc.IsNil) err = s.machines[1].Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) args = params.Entities{ Entities: []params.Entity{ {Tag: s.machines[1].Tag()}, }, } result, err = s.firewaller.Life(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.LifeResults{ Results: []params.LifeResult{ {Error: apiservertesting.NotFoundError("machine 1")}, }, }) } func (s *firewallerSuite) TestInstanceId(c *gc.C) { // Provision 2 machines first. err := s.machines[0].SetProvisioned("i-am", "fake_nonce", nil) c.Assert(err, gc.IsNil) hwChars := instance.MustParseHardware("arch=i386", "mem=4G") err = s.machines[1].SetProvisioned("i-am-not", "fake_nonce", &hwChars) c.Assert(err, gc.IsNil) args := addFakeEntities(params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: s.machines[1].Tag()}, {Tag: s.machines[2].Tag()}, {Tag: s.service.Tag()}, {Tag: s.units[2].Tag()}, }}) result, err := s.firewaller.InstanceId(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Result: "i-am"}, {Result: "i-am-not"}, {Error: apiservertesting.NotProvisionedError("2")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.NotFoundError("machine 42")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *firewallerSuite) TestWatchEnvironMachines(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) result, err := s.firewaller.WatchEnvironMachines() c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.StringsWatchResult{ StringsWatcherId: "1", Changes: []string{"0", "1", "2"}, }) // Verify the resources were registered and stop them when done. c.Assert(s.resources.Count(), gc.Equals, 1) resource := s.resources.Get("1") defer statetesting.AssertStop(c, resource) // Check that the Watch has consumed the initial event ("returned" // in the Watch call) wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) wc.AssertNoChange() } func (s *firewallerSuite) TestWatch(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) args := addFakeEntities(params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: s.service.Tag()}, {Tag: s.units[0].Tag()}, }}) result, err := s.firewaller.Watch(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.NotifyWatchResults{ Results: []params.NotifyWatchResult{ {Error: apiservertesting.ErrUnauthorized}, {NotifyWatcherId: "1"}, {NotifyWatcherId: "2"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.NotFoundError(`unit "foo/0"`)}, {Error: apiservertesting.NotFoundError(`service "bar"`)}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Verify the resources were registered and stop when done. c.Assert(s.resources.Count(), gc.Equals, 2) c.Assert(result.Results[1].NotifyWatcherId, gc.Equals, "1") watcher1 := s.resources.Get("1") defer statetesting.AssertStop(c, watcher1) c.Assert(result.Results[2].NotifyWatcherId, gc.Equals, "2") watcher2 := s.resources.Get("2") defer statetesting.AssertStop(c, watcher2) // Check that the Watch has consumed the initial event ("returned" in // the Watch call) wc1 := statetesting.NewNotifyWatcherC(c, s.State, watcher1.(state.NotifyWatcher)) wc1.AssertNoChange() wc2 := statetesting.NewNotifyWatcherC(c, s.State, watcher2.(state.NotifyWatcher)) wc2.AssertNoChange() } func (s *firewallerSuite) TestWatchUnits(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) args := addFakeEntities(params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: s.service.Tag()}, {Tag: s.units[0].Tag()}, }}) result, err := s.firewaller.WatchUnits(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.StringsWatchResults{ Results: []params.StringsWatchResult{ {Changes: []string{"wordpress/0"}, StringsWatcherId: "1"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.NotFoundError("machine 42")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Verify the resource was registered and stop when done c.Assert(s.resources.Count(), gc.Equals, 1) c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1") resource := s.resources.Get("1") defer statetesting.AssertStop(c, resource) // Check that the Watch has consumed the initial event ("returned" in // the Watch call) wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) wc.AssertNoChange() } func (s *firewallerSuite) TestGetExposed(c *gc.C) { // Set the service to exposed first. err := s.service.SetExposed() c.Assert(err, gc.IsNil) args := addFakeEntities(params.Entities{Entities: []params.Entity{ {Tag: s.service.Tag()}, }}) result, err := s.firewaller.GetExposed(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.BoolResults{ Results: []params.BoolResult{ {Result: true}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.NotFoundError(`service "bar"`)}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Now reset the exposed flag for the service and check again. err = s.service.ClearExposed() c.Assert(err, gc.IsNil) args = params.Entities{Entities: []params.Entity{ {Tag: s.service.Tag()}, }} result, err = s.firewaller.GetExposed(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.BoolResults{ Results: []params.BoolResult{ {Result: false}, }, }) } func (s *firewallerSuite) TestOpenedPorts(c *gc.C) { // Open some ports on two of the units. err := s.units[0].OpenPort("foo", 1234) c.Assert(err, gc.IsNil) err = s.units[0].OpenPort("bar", 4321) c.Assert(err, gc.IsNil) err = s.units[2].OpenPort("baz", 1111) c.Assert(err, gc.IsNil) args := addFakeEntities(params.Entities{Entities: []params.Entity{ {Tag: s.units[0].Tag()}, {Tag: s.units[1].Tag()}, {Tag: s.units[2].Tag()}, }}) result, err := s.firewaller.OpenedPorts(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.PortsResults{ Results: []params.PortsResult{ {Ports: []instance.Port{{"bar", 4321}, {"foo", 1234}}}, {Ports: []instance.Port{}}, {Ports: []instance.Port{{"baz", 1111}}}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.NotFoundError(`unit "foo/0"`)}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Now close unit 2's port and check again. err = s.units[2].ClosePort("baz", 1111) c.Assert(err, gc.IsNil) args = params.Entities{Entities: []params.Entity{ {Tag: s.units[2].Tag()}, }} result, err = s.firewaller.OpenedPorts(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.PortsResults{ Results: []params.PortsResult{ {Ports: []instance.Port{}}, }, }) } func (s *firewallerSuite) TestGetAssignedMachine(c *gc.C) { // Unassign a unit first. err := s.units[2].UnassignFromMachine() c.Assert(err, gc.IsNil) args := addFakeEntities(params.Entities{Entities: []params.Entity{ {Tag: s.units[0].Tag()}, {Tag: s.units[1].Tag()}, {Tag: s.units[2].Tag()}, }}) result, err := s.firewaller.GetAssignedMachine(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Result: s.machines[0].Tag()}, {Result: s.machines[1].Tag()}, {Error: apiservertesting.NotAssignedError("wordpress/2")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.NotFoundError(`unit "foo/0"`)}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Now reset assign unit 2 again and check. err = s.units[2].AssignToMachine(s.machines[0]) c.Assert(err, gc.IsNil) args = params.Entities{Entities: []params.Entity{ {Tag: s.units[2].Tag()}, }} result, err = s.firewaller.GetAssignedMachine(args) c.Assert(err, gc.IsNil) c.Assert(result, jc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Result: s.machines[0].Tag()}, }, }) } func (s *firewallerSuite) assertLife(c *gc.C, index int, expectLife state.Life) { err := s.machines[index].Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machines[index].Life(), gc.Equals, expectLife) } var commonFakeEntities = []params.Entity{ {Tag: "machine-42"}, {Tag: "unit-foo-0"}, {Tag: "service-bar"}, {Tag: "user-foo"}, {Tag: "foo-bar"}, {Tag: ""}, } func addFakeEntities(actual params.Entities) params.Entities { for _, entity := range commonFakeEntities { actual.Entities = append(actual.Entities, entity) } return actual } �������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/provisioner/���������������������������0000755�0000153�0000161�00000000000�12321736000�026351� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/provisioner/provisioner.go�������������0000644�0000153�0000161�00000025742�12321735776�031314� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner import ( "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" "launchpad.net/juju-core/state/watcher" ) // ProvisionerAPI provides access to the Provisioner API facade. type ProvisionerAPI struct { *common.Remover *common.StatusSetter *common.DeadEnsurer *common.PasswordChanger *common.LifeGetter *common.StateAddresser *common.APIAddresser *common.ToolsGetter *common.EnvironWatcher *common.EnvironMachinesWatcher *common.InstanceIdGetter st *state.State resources *common.Resources authorizer common.Authorizer getAuthFunc common.GetAuthFunc getCanWatchMachines common.GetAuthFunc } // NewProvisionerAPI creates a new server-side ProvisionerAPI facade. func NewProvisionerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*ProvisionerAPI, error) { if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() { return nil, common.ErrPerm } getAuthFunc := func() (common.AuthFunc, error) { isEnvironManager := authorizer.AuthEnvironManager() isMachineAgent := authorizer.AuthMachineAgent() authEntityTag := authorizer.GetAuthTag() return func(tag string) bool { if isMachineAgent && tag == authEntityTag { // A machine agent can always access its own machine. return true } _, id, err := names.ParseTag(tag, names.MachineTagKind) if err != nil { return false } parentId := state.ParentId(id) if parentId == "" { // All top-level machines are accessible by the // environment manager. return isEnvironManager } // All containers with the authenticated machine as a // parent are accessible by it. return isMachineAgent && names.MachineTag(parentId) == authEntityTag }, nil } // Both provisioner types can watch the environment. getCanWatch := common.AuthAlways(true) // Only the environment provisioner can read secrets. getCanReadSecrets := common.AuthAlways(authorizer.AuthEnvironManager()) return &ProvisionerAPI{ Remover: common.NewRemover(st, false, getAuthFunc), StatusSetter: common.NewStatusSetter(st, getAuthFunc), DeadEnsurer: common.NewDeadEnsurer(st, getAuthFunc), PasswordChanger: common.NewPasswordChanger(st, getAuthFunc), LifeGetter: common.NewLifeGetter(st, getAuthFunc), StateAddresser: common.NewStateAddresser(st), APIAddresser: common.NewAPIAddresser(st, resources), ToolsGetter: common.NewToolsGetter(st, getAuthFunc), EnvironWatcher: common.NewEnvironWatcher(st, resources, getCanWatch, getCanReadSecrets), EnvironMachinesWatcher: common.NewEnvironMachinesWatcher(st, resources, getCanReadSecrets), InstanceIdGetter: common.NewInstanceIdGetter(st, getAuthFunc), st: st, resources: resources, authorizer: authorizer, getAuthFunc: getAuthFunc, getCanWatchMachines: getCanReadSecrets, }, nil } func (p *ProvisionerAPI) getMachine(canAccess common.AuthFunc, tag string) (*state.Machine, error) { if !canAccess(tag) { return nil, common.ErrPerm } entity, err := p.st.FindEntity(tag) if err != nil { return nil, err } // The authorization function guarantees that the tag represents a // machine. return entity.(*state.Machine), nil } func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) { nothing := params.StringsWatchResult{} canAccess, err := p.getAuthFunc() if err != nil { return nothing, err } if !canAccess(arg.MachineTag) { return nothing, common.ErrPerm } _, id, err := names.ParseTag(arg.MachineTag, names.MachineTagKind) if err != nil { return nothing, err } machine, err := p.st.Machine(id) if err != nil { return nothing, err } var watch state.StringsWatcher if arg.ContainerType != "" { watch = machine.WatchContainers(instance.ContainerType(arg.ContainerType)) } else { watch = machine.WatchAllContainers() } // Consume the initial event and forward it to the result. if changes, ok := <-watch.Changes(); ok { return params.StringsWatchResult{ StringsWatcherId: p.resources.Register(watch), Changes: changes, }, nil } return nothing, watcher.MustErr(watch) } // WatchContainers starts a StringsWatcher to watch containers deployed to // any machine passed in args. func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) { result := params.StringsWatchResults{ Results: make([]params.StringsWatchResult, len(args.Params)), } for i, arg := range args.Params { watcherResult, err := p.watchOneMachineContainers(arg) result.Results[i] = watcherResult result.Results[i].Error = common.ServerError(err) } return result, nil } // WatchAllContainers starts a StringsWatcher to watch all containers deployed to // any machine passed in args. func (p *ProvisionerAPI) WatchAllContainers(args params.WatchContainers) (params.StringsWatchResults, error) { return p.WatchContainers(args) } // SetSupportedContainers updates the list of containers supported by the machines passed in args. func (p *ProvisionerAPI) SetSupportedContainers( args params.MachineContainersParams) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Params)), } for i, arg := range args.Params { canAccess, err := p.getAuthFunc() if err != nil { return result, err } machine, err := p.getMachine(canAccess, arg.MachineTag) if err != nil { result.Results[i].Error = common.ServerError(err) continue } if len(arg.ContainerTypes) == 0 { err = machine.SupportsNoContainers() } else { err = machine.SetSupportedContainers(arg.ContainerTypes) } if err != nil { result.Results[i].Error = common.ServerError(err) } } return result, nil } // ContainerConfig returns information from the environment config that are // needed for container cloud-init. func (p *ProvisionerAPI) ContainerConfig() (params.ContainerConfig, error) { result := params.ContainerConfig{} config, err := p.st.EnvironConfig() if err != nil { return result, err } result.ProviderType = config.Type() result.AuthorizedKeys = config.AuthorizedKeys() result.SSLHostnameVerification = config.SSLHostnameVerification() result.Proxy = config.ProxySettings() result.AptProxy = config.AptProxySettings() return result, nil } // Status returns the status of each given machine entity. func (p *ProvisionerAPI) Status(args params.Entities) (params.StatusResults, error) { result := params.StatusResults{ Results: make([]params.StatusResult, len(args.Entities)), } canAccess, err := p.getAuthFunc() if err != nil { return result, err } for i, entity := range args.Entities { machine, err := p.getMachine(canAccess, entity.Tag) if err == nil { r := &result.Results[i] r.Status, r.Info, r.Data, err = machine.Status() } result.Results[i].Error = common.ServerError(err) } return result, nil } // MachinesWithTransientErrors returns status data for machines with provisioning // errors which are transient. func (p *ProvisionerAPI) MachinesWithTransientErrors() (params.StatusResults, error) { results := params.StatusResults{} canAccessFunc, err := p.getAuthFunc() if err != nil { return results, err } // TODO (wallyworld) - add state.State API for more efficient machines query machines, err := p.st.AllMachines() if err != nil { return results, err } for _, machine := range machines { if !canAccessFunc(machine.Tag()) { continue } if _, provisionedErr := machine.InstanceId(); provisionedErr == nil { // Machine may have been provisioned but machiner hasn't set the // status to Started yet. continue } result := params.StatusResult{} if result.Status, result.Info, result.Data, err = machine.Status(); err != nil { continue } if result.Status != params.StatusError { continue } // Transient errors are marked as such in the status data. if transient, ok := result.Data["transient"].(bool); !ok || !transient { continue } result.Id = machine.Id() result.Life = params.Life(machine.Life().String()) results.Results = append(results.Results, result) } return results, nil } // Series returns the deployed series for each given machine entity. func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) { result := params.StringResults{ Results: make([]params.StringResult, len(args.Entities)), } canAccess, err := p.getAuthFunc() if err != nil { return result, err } for i, entity := range args.Entities { machine, err := p.getMachine(canAccess, entity.Tag) if err == nil { result.Results[i].Result = machine.Series() } result.Results[i].Error = common.ServerError(err) } return result, nil } // Constraints returns the constraints for each given machine entity. func (p *ProvisionerAPI) Constraints(args params.Entities) (params.ConstraintsResults, error) { result := params.ConstraintsResults{ Results: make([]params.ConstraintsResult, len(args.Entities)), } canAccess, err := p.getAuthFunc() if err != nil { return result, err } for i, entity := range args.Entities { machine, err := p.getMachine(canAccess, entity.Tag) if err == nil { var cons constraints.Value cons, err = machine.Constraints() if err == nil { result.Results[i].Constraints = cons } } result.Results[i].Error = common.ServerError(err) } return result, nil } // SetProvisioned sets the provider specific machine id, nonce and // metadata for each given machine. Once set, the instance id cannot // be changed. func (p *ProvisionerAPI) SetProvisioned(args params.SetProvisioned) (params.ErrorResults, error) { result := params.ErrorResults{ Results: make([]params.ErrorResult, len(args.Machines)), } canAccess, err := p.getAuthFunc() if err != nil { return result, err } for i, arg := range args.Machines { machine, err := p.getMachine(canAccess, arg.Tag) if err == nil { err = machine.SetProvisioned(arg.InstanceId, arg.Nonce, arg.Characteristics) } result.Results[i].Error = common.ServerError(err) } return result, nil } // WatchMachineErrorRetry returns a NotifyWatcher that notifies when // the provisioner should retry provisioning machines with transient errors. func (p *ProvisionerAPI) WatchMachineErrorRetry() (params.NotifyWatchResult, error) { result := params.NotifyWatchResult{} canWatch, err := p.getCanWatchMachines() if err != nil { return params.NotifyWatchResult{}, err } if !canWatch("") { return result, common.ErrPerm } watch := newWatchMachineErrorRetry() // Consume any initial event and forward it to the result. if _, ok := <-watch.Changes(); ok { result.NotifyWatcherId = p.resources.Register(watch) } else { return result, watcher.MustErr(watch) } return result, nil } ������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/provisioner/provisioner_test.go��������0000644�0000153�0000161�00000077522�12321735776�032356� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner_test import ( "fmt" stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/apiserver/common" commontesting "launchpad.net/juju-core/state/apiserver/common/testing" "launchpad.net/juju-core/state/apiserver/provisioner" apiservertesting "launchpad.net/juju-core/state/apiserver/testing" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/version" ) func Test(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type provisionerSuite struct { testing.JujuConnSuite machines []*state.Machine authorizer apiservertesting.FakeAuthorizer resources *common.Resources provisioner *provisioner.ProvisionerAPI } var _ = gc.Suite(&provisionerSuite{}) func (s *provisionerSuite) SetUpTest(c *gc.C) { s.setUpTest(c, false) } func (s *provisionerSuite) setUpTest(c *gc.C, withStateServer bool) { s.JujuConnSuite.SetUpTest(c) // Reset previous machines (if any) and create 3 machines // for the tests, plus an optional state server machine. s.machines = nil // Note that the specific machine ids allocated are assumed // to be numerically consecutive from zero. if withStateServer { s.machines = append(s.machines, testing.AddStateServerMachine(c, s.State)) } for i := 0; i < 5; i++ { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Check(err, gc.IsNil) s.machines = append(s.machines, machine) } // Create a FakeAuthorizer so we can check permissions, // set up assuming we logged in as the environment manager. s.authorizer = apiservertesting.FakeAuthorizer{ LoggedIn: true, EnvironManager: true, } // Create the resource registry separately to track invocations to // Register. s.resources = common.NewResources() // Create a provisioner API for the machine. provisionerAPI, err := provisioner.NewProvisionerAPI( s.State, s.resources, s.authorizer, ) c.Assert(err, gc.IsNil) s.provisioner = provisionerAPI } type withoutStateServerSuite struct { provisionerSuite *commontesting.EnvironWatcherTest } var _ = gc.Suite(&withoutStateServerSuite{}) func (s *withoutStateServerSuite) SetUpTest(c *gc.C) { s.setUpTest(c, false) s.EnvironWatcherTest = commontesting.NewEnvironWatcherTest(s.provisioner, s.State, s.resources, commontesting.HasSecrets) } func (s *withoutStateServerSuite) TestProvisionerFailsWithNonMachineAgentNonManagerUser(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.MachineAgent = false anAuthorizer.EnvironManager = true // Works with an environment manager, which is not a machine agent. aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.IsNil) c.Assert(aProvisioner, gc.NotNil) // But fails with neither a machine agent or an environment manager. anAuthorizer.EnvironManager = false aProvisioner, err = provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.NotNil) c.Assert(aProvisioner, gc.IsNil) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *withoutStateServerSuite) TestSetPasswords(c *gc.C) { args := params.EntityPasswords{ Changes: []params.EntityPassword{ {Tag: s.machines[0].Tag(), Password: "xxx0-1234567890123457890"}, {Tag: s.machines[1].Tag(), Password: "xxx1-1234567890123457890"}, {Tag: s.machines[2].Tag(), Password: "xxx2-1234567890123457890"}, {Tag: s.machines[3].Tag(), Password: "xxx3-1234567890123457890"}, {Tag: s.machines[4].Tag(), Password: "xxx4-1234567890123457890"}, {Tag: "machine-42", Password: "foo"}, {Tag: "unit-foo-0", Password: "zzz"}, {Tag: "service-bar", Password: "abc"}, }, } results, err := s.provisioner.SetPasswords(args) c.Assert(err, gc.IsNil) c.Assert(results, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {nil}, {nil}, {nil}, {nil}, {nil}, {apiservertesting.NotFoundError("machine 42")}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) // Verify the changes to both machines succeeded. for i, machine := range s.machines { c.Logf("trying %q password", machine.Tag()) err = machine.Refresh() c.Assert(err, gc.IsNil) changed := machine.PasswordValid(fmt.Sprintf("xxx%d-1234567890123457890", i)) c.Assert(changed, jc.IsTrue) } } func (s *withoutStateServerSuite) TestShortSetPasswords(c *gc.C) { args := params.EntityPasswords{ Changes: []params.EntityPassword{ {Tag: s.machines[1].Tag(), Password: "xxx1"}, }, } results, err := s.provisioner.SetPasswords(args) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.ErrorMatches, "password is only 4 bytes long, and is not a valid Agent password") } func (s *withoutStateServerSuite) TestLifeAsMachineAgent(c *gc.C) { // NOTE: This and the next call serve to test the two // different authorization schemes: // 1. Machine agents can access their own machine and // any container that has their own machine as parent; // 2. Environment managers can access any machine without // a parent. // There's no need to repeat this test for each method, // because the authorization logic is common. // Login as a machine agent for machine 0. anAuthorizer := s.authorizer anAuthorizer.MachineAgent = true anAuthorizer.EnvironManager = false anAuthorizer.Tag = s.machines[0].Tag() aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.IsNil) c.Assert(aProvisioner, gc.NotNil) // Make the machine dead before trying to add containers. err = s.machines[0].EnsureDead() c.Assert(err, gc.IsNil) // Create some containers to work on. template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } var containers []*state.Machine for i := 0; i < 3; i++ { container, err := s.State.AddMachineInsideMachine(template, s.machines[0].Id(), instance.LXC) c.Check(err, gc.IsNil) containers = append(containers, container) } // Make one container dead. err = containers[1].EnsureDead() c.Assert(err, gc.IsNil) args := params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: s.machines[1].Tag()}, {Tag: containers[0].Tag()}, {Tag: containers[1].Tag()}, {Tag: containers[2].Tag()}, {Tag: "machine-42"}, {Tag: "unit-foo-0"}, {Tag: "service-bar"}, }} result, err := aProvisioner.Life(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.LifeResults{ Results: []params.LifeResult{ {Life: "dead"}, {Error: apiservertesting.ErrUnauthorized}, {Life: "alive"}, {Life: "dead"}, {Life: "alive"}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *withoutStateServerSuite) TestLifeAsEnvironManager(c *gc.C) { err := s.machines[1].EnsureDead() c.Assert(err, gc.IsNil) err = s.machines[1].Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machines[0].Life(), gc.Equals, state.Alive) c.Assert(s.machines[1].Life(), gc.Equals, state.Dead) c.Assert(s.machines[2].Life(), gc.Equals, state.Alive) args := params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: s.machines[1].Tag()}, {Tag: s.machines[2].Tag()}, {Tag: "machine-42"}, {Tag: "unit-foo-0"}, {Tag: "service-bar"}, }} result, err := s.provisioner.Life(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.LifeResults{ Results: []params.LifeResult{ {Life: "alive"}, {Life: "dead"}, {Life: "alive"}, {Error: apiservertesting.NotFoundError("machine 42")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Remove the subordinate and make sure it's detected. err = s.machines[1].Remove() c.Assert(err, gc.IsNil) err = s.machines[1].Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) result, err = s.provisioner.Life(params.Entities{ Entities: []params.Entity{ {Tag: s.machines[1].Tag()}, }, }) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.LifeResults{ Results: []params.LifeResult{ {Error: apiservertesting.NotFoundError("machine 1")}, }, }) } func (s *withoutStateServerSuite) TestRemove(c *gc.C) { err := s.machines[1].EnsureDead() c.Assert(err, gc.IsNil) s.assertLife(c, 0, state.Alive) s.assertLife(c, 1, state.Dead) s.assertLife(c, 2, state.Alive) args := params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: s.machines[1].Tag()}, {Tag: s.machines[2].Tag()}, {Tag: "machine-42"}, {Tag: "unit-foo-0"}, {Tag: "service-bar"}, }} result, err := s.provisioner.Remove(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {&params.Error{Message: `cannot remove entity "machine-0": still alive`}}, {nil}, {&params.Error{Message: `cannot remove entity "machine-2": still alive`}}, {apiservertesting.NotFoundError("machine 42")}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) // Verify the changes. s.assertLife(c, 0, state.Alive) err = s.machines[1].Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) s.assertLife(c, 2, state.Alive) } func (s *withoutStateServerSuite) TestSetStatus(c *gc.C) { err := s.machines[0].SetStatus(params.StatusStarted, "blah", nil) c.Assert(err, gc.IsNil) err = s.machines[1].SetStatus(params.StatusStopped, "foo", nil) c.Assert(err, gc.IsNil) err = s.machines[2].SetStatus(params.StatusError, "not really", nil) c.Assert(err, gc.IsNil) args := params.SetStatus{ Entities: []params.EntityStatus{ {Tag: s.machines[0].Tag(), Status: params.StatusError, Info: "not really", Data: params.StatusData{"foo": "bar"}}, {Tag: s.machines[1].Tag(), Status: params.StatusStopped, Info: "foobar"}, {Tag: s.machines[2].Tag(), Status: params.StatusStarted, Info: "again"}, {Tag: "machine-42", Status: params.StatusStarted, Info: "blah"}, {Tag: "unit-foo-0", Status: params.StatusStopped, Info: "foobar"}, {Tag: "service-bar", Status: params.StatusStopped, Info: "foobar"}, }} result, err := s.provisioner.SetStatus(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {nil}, {nil}, {nil}, {apiservertesting.NotFoundError("machine 42")}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) // Verify the changes. s.assertStatus(c, 0, params.StatusError, "not really", params.StatusData{"foo": "bar"}) s.assertStatus(c, 1, params.StatusStopped, "foobar", params.StatusData{}) s.assertStatus(c, 2, params.StatusStarted, "again", params.StatusData{}) } func (s *withoutStateServerSuite) TestMachinesWithTransientErrors(c *gc.C) { err := s.machines[0].SetStatus(params.StatusStarted, "blah", nil) c.Assert(err, gc.IsNil) err = s.machines[1].SetStatus(params.StatusError, "transient error", params.StatusData{"transient": true, "foo": "bar"}) c.Assert(err, gc.IsNil) err = s.machines[2].SetStatus(params.StatusError, "error", params.StatusData{"transient": false}) c.Assert(err, gc.IsNil) err = s.machines[3].SetStatus(params.StatusError, "error", nil) c.Assert(err, gc.IsNil) // Machine 4 is provisioned but error not reset yet. err = s.machines[4].SetStatus(params.StatusError, "transient error", params.StatusData{"transient": true, "foo": "bar"}) c.Assert(err, gc.IsNil) hwChars := instance.MustParseHardware("arch=i386", "mem=4G") err = s.machines[4].SetProvisioned("i-am", "fake_nonce", &hwChars) c.Assert(err, gc.IsNil) result, err := s.provisioner.MachinesWithTransientErrors() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StatusResults{ Results: []params.StatusResult{ {Id: "1", Life: "alive", Status: "error", Info: "transient error", Data: params.StatusData{"transient": true, "foo": "bar"}}, }, }) } func (s *withoutStateServerSuite) TestMachinesWithTransientErrorsPermission(c *gc.C) { // Machines where there's permission issues are omitted. anAuthorizer := s.authorizer anAuthorizer.MachineAgent = true anAuthorizer.EnvironManager = false anAuthorizer.Tag = "machine-1" aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) err = s.machines[0].SetStatus(params.StatusStarted, "blah", nil) c.Assert(err, gc.IsNil) err = s.machines[1].SetStatus(params.StatusError, "transient error", params.StatusData{"transient": true, "foo": "bar"}) c.Assert(err, gc.IsNil) err = s.machines[2].SetStatus(params.StatusError, "error", params.StatusData{"transient": false}) c.Assert(err, gc.IsNil) err = s.machines[3].SetStatus(params.StatusError, "error", nil) c.Assert(err, gc.IsNil) result, err := aProvisioner.MachinesWithTransientErrors() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StatusResults{ Results: []params.StatusResult{ {Id: "1", Life: "alive", Status: "error", Info: "transient error", Data: params.StatusData{"transient": true, "foo": "bar"}}, }, }) } func (s *withoutStateServerSuite) TestEnsureDead(c *gc.C) { err := s.machines[1].EnsureDead() c.Assert(err, gc.IsNil) s.assertLife(c, 0, state.Alive) s.assertLife(c, 1, state.Dead) s.assertLife(c, 2, state.Alive) args := params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: s.machines[1].Tag()}, {Tag: s.machines[2].Tag()}, {Tag: "machine-42"}, {Tag: "unit-foo-0"}, {Tag: "service-bar"}, }} result, err := s.provisioner.EnsureDead(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {nil}, {nil}, {nil}, {apiservertesting.NotFoundError("machine 42")}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) // Verify the changes. s.assertLife(c, 0, state.Dead) s.assertLife(c, 1, state.Dead) s.assertLife(c, 2, state.Dead) } func (s *withoutStateServerSuite) assertLife(c *gc.C, index int, expectLife state.Life) { err := s.machines[index].Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machines[index].Life(), gc.Equals, expectLife) } func (s *withoutStateServerSuite) assertStatus(c *gc.C, index int, expectStatus params.Status, expectInfo string, expectData params.StatusData) { status, info, data, err := s.machines[index].Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, expectStatus) c.Assert(info, gc.Equals, expectInfo) c.Assert(data, gc.DeepEquals, expectData) } func (s *withoutStateServerSuite) TestWatchContainers(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) args := params.WatchContainers{Params: []params.WatchContainer{ {MachineTag: s.machines[0].Tag(), ContainerType: string(instance.LXC)}, {MachineTag: s.machines[1].Tag(), ContainerType: string(instance.KVM)}, {MachineTag: "machine-42", ContainerType: ""}, {MachineTag: "unit-foo-0", ContainerType: ""}, {MachineTag: "service-bar", ContainerType: ""}, }} result, err := s.provisioner.WatchContainers(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ Results: []params.StringsWatchResult{ {StringsWatcherId: "1", Changes: []string{}}, {StringsWatcherId: "2", Changes: []string{}}, {Error: apiservertesting.NotFoundError("machine 42")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Verify the resources were registered and stop them when done. c.Assert(s.resources.Count(), gc.Equals, 2) m0Watcher := s.resources.Get("1") defer statetesting.AssertStop(c, m0Watcher) m1Watcher := s.resources.Get("2") defer statetesting.AssertStop(c, m1Watcher) // Check that the Watch has consumed the initial event ("returned" // in the Watch call) wc0 := statetesting.NewStringsWatcherC(c, s.State, m0Watcher.(state.StringsWatcher)) wc0.AssertNoChange() wc1 := statetesting.NewStringsWatcherC(c, s.State, m1Watcher.(state.StringsWatcher)) wc1.AssertNoChange() } func (s *withoutStateServerSuite) TestWatchAllContainers(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) args := params.WatchContainers{Params: []params.WatchContainer{ {MachineTag: s.machines[0].Tag()}, {MachineTag: s.machines[1].Tag()}, {MachineTag: "machine-42"}, {MachineTag: "unit-foo-0"}, {MachineTag: "service-bar"}, }} result, err := s.provisioner.WatchAllContainers(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringsWatchResults{ Results: []params.StringsWatchResult{ {StringsWatcherId: "1", Changes: []string{}}, {StringsWatcherId: "2", Changes: []string{}}, {Error: apiservertesting.NotFoundError("machine 42")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) // Verify the resources were registered and stop them when done. c.Assert(s.resources.Count(), gc.Equals, 2) m0Watcher := s.resources.Get("1") defer statetesting.AssertStop(c, m0Watcher) m1Watcher := s.resources.Get("2") defer statetesting.AssertStop(c, m1Watcher) // Check that the Watch has consumed the initial event ("returned" // in the Watch call) wc0 := statetesting.NewStringsWatcherC(c, s.State, m0Watcher.(state.StringsWatcher)) wc0.AssertNoChange() wc1 := statetesting.NewStringsWatcherC(c, s.State, m1Watcher.(state.StringsWatcher)) wc1.AssertNoChange() } func (s *withoutStateServerSuite) TestEnvironConfigNonManager(c *gc.C) { // Now test it with a non-environment manager and make sure // the secret attributes are masked. anAuthorizer := s.authorizer anAuthorizer.MachineAgent = true anAuthorizer.EnvironManager = false aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.IsNil) s.AssertEnvironConfig(c, aProvisioner, commontesting.NoSecrets) } func (s *withoutStateServerSuite) TestStatus(c *gc.C) { err := s.machines[0].SetStatus(params.StatusStarted, "blah", nil) c.Assert(err, gc.IsNil) err = s.machines[1].SetStatus(params.StatusStopped, "foo", nil) c.Assert(err, gc.IsNil) err = s.machines[2].SetStatus(params.StatusError, "not really", params.StatusData{"foo": "bar"}) c.Assert(err, gc.IsNil) args := params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: s.machines[1].Tag()}, {Tag: s.machines[2].Tag()}, {Tag: "machine-42"}, {Tag: "unit-foo-0"}, {Tag: "service-bar"}, }} result, err := s.provisioner.Status(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StatusResults{ Results: []params.StatusResult{ {Status: params.StatusStarted, Info: "blah", Data: params.StatusData{}}, {Status: params.StatusStopped, Info: "foo", Data: params.StatusData{}}, {Status: params.StatusError, Info: "not really", Data: params.StatusData{"foo": "bar"}}, {Error: apiservertesting.NotFoundError("machine 42")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *withoutStateServerSuite) TestSeries(c *gc.C) { // Add a machine with different series. foobarMachine, err := s.State.AddMachine("foobar", state.JobHostUnits) c.Assert(err, gc.IsNil) args := params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: foobarMachine.Tag()}, {Tag: s.machines[2].Tag()}, {Tag: "machine-42"}, {Tag: "unit-foo-0"}, {Tag: "service-bar"}, }} result, err := s.provisioner.Series(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Result: s.machines[0].Series()}, {Result: foobarMachine.Series()}, {Result: s.machines[2].Series()}, {Error: apiservertesting.NotFoundError("machine 42")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *withoutStateServerSuite) TestConstraints(c *gc.C) { // Add a machine with some constraints. template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, Constraints: constraints.MustParse("cpu-cores=123", "mem=8G"), } consMachine, err := s.State.AddOneMachine(template) c.Assert(err, gc.IsNil) machine0Constraints, err := s.machines[0].Constraints() c.Assert(err, gc.IsNil) args := params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: consMachine.Tag()}, {Tag: "machine-42"}, {Tag: "unit-foo-0"}, {Tag: "service-bar"}, }} result, err := s.provisioner.Constraints(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ConstraintsResults{ Results: []params.ConstraintsResult{ {Constraints: machine0Constraints}, {Constraints: template.Constraints}, {Error: apiservertesting.NotFoundError("machine 42")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *withoutStateServerSuite) TestSetProvisioned(c *gc.C) { // Provision machine 0 first. hwChars := instance.MustParseHardware("arch=i386", "mem=4G") err := s.machines[0].SetProvisioned("i-am", "fake_nonce", &hwChars) c.Assert(err, gc.IsNil) args := params.SetProvisioned{Machines: []params.MachineSetProvisioned{ {Tag: s.machines[0].Tag(), InstanceId: "i-was", Nonce: "fake_nonce", Characteristics: nil}, {Tag: s.machines[1].Tag(), InstanceId: "i-will", Nonce: "fake_nonce", Characteristics: &hwChars}, {Tag: s.machines[2].Tag(), InstanceId: "i-am-too", Nonce: "fake", Characteristics: nil}, {Tag: "machine-42", InstanceId: "", Nonce: "", Characteristics: nil}, {Tag: "unit-foo-0", InstanceId: "", Nonce: "", Characteristics: nil}, {Tag: "service-bar", InstanceId: "", Nonce: "", Characteristics: nil}, }} result, err := s.provisioner.SetProvisioned(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {&params.Error{ Message: `cannot set instance data for machine "0": already set`, }}, {nil}, {nil}, {apiservertesting.NotFoundError("machine 42")}, {apiservertesting.ErrUnauthorized}, {apiservertesting.ErrUnauthorized}, }, }) // Verify machine 1 and 2 were provisioned. c.Assert(s.machines[1].Refresh(), gc.IsNil) c.Assert(s.machines[2].Refresh(), gc.IsNil) instanceId, err := s.machines[1].InstanceId() c.Assert(err, gc.IsNil) c.Check(instanceId, gc.Equals, instance.Id("i-will")) instanceId, err = s.machines[2].InstanceId() c.Assert(err, gc.IsNil) c.Check(instanceId, gc.Equals, instance.Id("i-am-too")) c.Check(s.machines[1].CheckProvisioned("fake_nonce"), jc.IsTrue) c.Check(s.machines[2].CheckProvisioned("fake"), jc.IsTrue) gotHardware, err := s.machines[1].HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Check(gotHardware, gc.DeepEquals, &hwChars) } func (s *withoutStateServerSuite) TestInstanceId(c *gc.C) { // Provision 2 machines first. err := s.machines[0].SetProvisioned("i-am", "fake_nonce", nil) c.Assert(err, gc.IsNil) hwChars := instance.MustParseHardware("arch=i386", "mem=4G") err = s.machines[1].SetProvisioned("i-am-not", "fake_nonce", &hwChars) c.Assert(err, gc.IsNil) args := params.Entities{Entities: []params.Entity{ {Tag: s.machines[0].Tag()}, {Tag: s.machines[1].Tag()}, {Tag: s.machines[2].Tag()}, {Tag: "machine-42"}, {Tag: "unit-foo-0"}, {Tag: "service-bar"}, }} result, err := s.provisioner.InstanceId(args) c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringResults{ Results: []params.StringResult{ {Result: "i-am"}, {Result: "i-am-not"}, {Error: apiservertesting.NotProvisionedError("2")}, {Error: apiservertesting.NotFoundError("machine 42")}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *withoutStateServerSuite) TestWatchEnvironMachines(c *gc.C) { c.Assert(s.resources.Count(), gc.Equals, 0) result, err := s.provisioner.WatchEnvironMachines() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringsWatchResult{ StringsWatcherId: "1", Changes: []string{"0", "1", "2", "3", "4"}, }) // Verify the resources were registered and stop them when done. c.Assert(s.resources.Count(), gc.Equals, 1) resource := s.resources.Get("1") defer statetesting.AssertStop(c, resource) // Check that the Watch has consumed the initial event ("returned" // in the Watch call) wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher)) wc.AssertNoChange() // Make sure WatchEnvironMachines fails with a machine agent login. anAuthorizer := s.authorizer anAuthorizer.MachineAgent = true anAuthorizer.EnvironManager = false aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.IsNil) result, err = aProvisioner.WatchEnvironMachines() c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(result, gc.DeepEquals, params.StringsWatchResult{}) } func (s *withoutStateServerSuite) TestToolsNothing(c *gc.C) { // Not an error to watch nothing results, err := s.provisioner.Tools(params.Entities{}) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 0) } func (s *withoutStateServerSuite) TestContainerConfig(c *gc.C) { attrs := map[string]interface{}{ "http-proxy": "http://proxy.example.com:9000", } err := s.State.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) expectedProxy := osenv.ProxySettings{ Http: "http://proxy.example.com:9000", } results, err := s.provisioner.ContainerConfig() c.Check(err, gc.IsNil) c.Check(results.ProviderType, gc.Equals, "dummy") c.Check(results.AuthorizedKeys, gc.Equals, coretesting.FakeAuthKeys) c.Check(results.SSLHostnameVerification, jc.IsTrue) c.Check(results.Proxy, gc.DeepEquals, expectedProxy) c.Check(results.AptProxy, gc.DeepEquals, expectedProxy) } func (s *withoutStateServerSuite) TestToolsRefusesWrongAgent(c *gc.C) { anAuthorizer := s.authorizer anAuthorizer.Tag = "machine-12354" anAuthorizer.EnvironManager = false anAuthorizer.MachineAgent = true aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) c.Check(err, gc.IsNil) args := params.Entities{ Entities: []params.Entity{{Tag: s.machines[0].Tag()}}, } results, err := aProvisioner.Tools(args) // It is not an error to make the request, but the specific item is rejected c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) toolResult := results.Results[0] c.Assert(toolResult.Error, gc.DeepEquals, apiservertesting.ErrUnauthorized) } func (s *withoutStateServerSuite) TestToolsForAgent(c *gc.C) { cur := version.Current agent := params.Entity{Tag: s.machines[0].Tag()} // The machine must have its existing tools set before we query for the // next tools. This is so that we can grab Arch and Series without // having to pass it in again err := s.machines[0].SetAgentVersion(version.Current) c.Assert(err, gc.IsNil) args := params.Entities{Entities: []params.Entity{agent}} results, err := s.provisioner.Tools(args) c.Assert(err, gc.IsNil) c.Check(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) agentTools := results.Results[0].Tools c.Check(agentTools.URL, gc.Not(gc.Equals), "") c.Check(agentTools.Version, gc.DeepEquals, cur) } func (s *withoutStateServerSuite) TestSetSupportedContainers(c *gc.C) { args := params.MachineContainersParams{ Params: []params.MachineContainers{ { MachineTag: "machine-0", ContainerTypes: []instance.ContainerType{instance.LXC}, }, { MachineTag: "machine-1", ContainerTypes: []instance.ContainerType{instance.LXC, instance.KVM}, }, }, } results, err := s.provisioner.SetSupportedContainers(args) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 2) for _, result := range results.Results { c.Assert(result.Error, gc.IsNil) } m0, err := s.State.Machine("0") c.Assert(err, gc.IsNil) containers, ok := m0.SupportedContainers() c.Assert(ok, jc.IsTrue) c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXC}) m1, err := s.State.Machine("1") c.Assert(err, gc.IsNil) containers, ok = m1.SupportedContainers() c.Assert(ok, jc.IsTrue) c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXC, instance.KVM}) } func (s *withoutStateServerSuite) TestSetSupportedContainersPermissions(c *gc.C) { // Login as a machine agent for machine 0. anAuthorizer := s.authorizer anAuthorizer.MachineAgent = true anAuthorizer.EnvironManager = false anAuthorizer.Tag = s.machines[0].Tag() aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.IsNil) c.Assert(aProvisioner, gc.NotNil) args := params.MachineContainersParams{ Params: []params.MachineContainers{{ MachineTag: "machine-0", ContainerTypes: []instance.ContainerType{instance.LXC}, }, { MachineTag: "machine-1", ContainerTypes: []instance.ContainerType{instance.LXC}, }, { MachineTag: "machine-42", ContainerTypes: []instance.ContainerType{instance.LXC}, }, }, } // Only machine 0 can have it's containers updated. results, err := aProvisioner.SetSupportedContainers(args) c.Assert(results, gc.DeepEquals, params.ErrorResults{ Results: []params.ErrorResult{ {Error: nil}, {Error: apiservertesting.ErrUnauthorized}, {Error: apiservertesting.ErrUnauthorized}, }, }) } func (s *withoutStateServerSuite) TestSupportsNoContainers(c *gc.C) { args := params.MachineContainersParams{ Params: []params.MachineContainers{ { MachineTag: "machine-0", }, }, } results, err := s.provisioner.SetSupportedContainers(args) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 1) c.Assert(results.Results[0].Error, gc.IsNil) m0, err := s.State.Machine("0") c.Assert(err, gc.IsNil) containers, ok := m0.SupportedContainers() c.Assert(ok, jc.IsTrue) c.Assert(containers, gc.DeepEquals, []instance.ContainerType{}) } var _ = gc.Suite(&withStateServerSuite{}) type withStateServerSuite struct { provisionerSuite } func (s *withStateServerSuite) SetUpTest(c *gc.C) { s.provisionerSuite.setUpTest(c, true) } func (s *withStateServerSuite) TestAPIAddresses(c *gc.C) { addrs, err := s.State.APIAddressesFromMachines() c.Assert(err, gc.IsNil) result, err := s.provisioner.APIAddresses() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringsResult{ Result: addrs, }) } func (s *withStateServerSuite) TestStateAddresses(c *gc.C) { addresses, err := s.State.Addresses() c.Assert(err, gc.IsNil) result, err := s.provisioner.StateAddresses() c.Assert(err, gc.IsNil) c.Assert(result, gc.DeepEquals, params.StringsResult{ Result: addresses, }) } func (s *withStateServerSuite) TestCACert(c *gc.C) { result := s.provisioner.CACert() c.Assert(result, gc.DeepEquals, params.BytesResult{ Result: s.State.CACert(), }) } func (s *withoutStateServerSuite) TestWatchMachineErrorRetry(c *gc.C) { s.PatchValue(&provisioner.ErrorRetryWaitDelay, 2*coretesting.ShortWait) c.Assert(s.resources.Count(), gc.Equals, 0) _, err := s.provisioner.WatchMachineErrorRetry() c.Assert(err, gc.IsNil) // Verify the resources were registered and stop them when done. c.Assert(s.resources.Count(), gc.Equals, 1) resource := s.resources.Get("1") defer statetesting.AssertStop(c, resource) // Check that the Watch has consumed the initial event ("returned" // in the Watch call) wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher)) wc.AssertNoChange() // We should now get a time triggered change. wc.AssertOneChange() // Make sure WatchMachineErrorRetry fails with a machine agent login. anAuthorizer := s.authorizer anAuthorizer.MachineAgent = true anAuthorizer.EnvironManager = false aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer) c.Assert(err, gc.IsNil) result, err := aProvisioner.WatchMachineErrorRetry() c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(result, gc.DeepEquals, params.NotifyWatchResult{}) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/apiserver/provisioner/machineerror.go������������0000644�0000153�0000161�00000003527�12321735642�031400� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner import ( "time" "launchpad.net/tomb" "launchpad.net/juju-core/state" ) // machineErrorRetry is a notify watcher that fires when it is // appropriate to retry provisioning machines with transient errors. type machineErrorRetry struct { tomb tomb.Tomb out chan struct{} } func newWatchMachineErrorRetry() state.NotifyWatcher { w := &machineErrorRetry{ out: make(chan struct{}), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop()) }() return w } // Stop stops the watcher, and returns any error encountered while running // or shutting down. func (w *machineErrorRetry) Stop() error { w.Kill() return w.Wait() } // Kill kills the watcher without waiting for it to shut down. func (w *machineErrorRetry) Kill() { w.tomb.Kill(nil) } // Wait waits for the watcher to die and returns any // error encountered when it was running. func (w *machineErrorRetry) Wait() error { return w.tomb.Wait() } // Err returns any error encountered while running or shutting down, or // tomb.ErrStillAlive if the watcher is still running. func (w *machineErrorRetry) Err() error { return w.tomb.Err() } // Changes returns the event channel for the machineErrorRetry watcher. func (w *machineErrorRetry) Changes() <-chan struct{} { return w.out } // ErrorRetryWaitDelay is the poll time currently used to trigger the watcher. var ErrorRetryWaitDelay = 1 * time.Minute // The initial implementation of this watcher simply acts as a poller, // triggering every ErrorRetryWaitDelay minutes. func (w *machineErrorRetry) loop() error { out := w.out for { select { case <-w.tomb.Dying(): return tomb.ErrDying case <-time.After(ErrorRetryWaitDelay): out = w.out case out <- struct{}{}: out = nil } } return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/user.go������������������������������������������0000644�0000153�0000161�00000011752�12321735642�023320� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package state import ( "fmt" "regexp" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/names" "launchpad.net/juju-core/utils" ) var validUser = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9]*$") func (st *State) checkUserExists(name string) (bool, error) { var count int var err error if count, err = st.users.FindId(name).Count(); err != nil { return false, err } return count > 0, nil } // AddUser adds a user to the state. func (st *State) AddUser(name, password string) (*User, error) { if !validUser.MatchString(name) { return nil, fmt.Errorf("invalid user name %q", name) } salt, err := utils.RandomSalt() if err != nil { return nil, err } u := &User{ st: st, doc: userDoc{ Name: name, PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, }, } ops := []txn.Op{{ C: st.users.Name, Id: name, Assert: txn.DocMissing, Insert: &u.doc, }} err = st.runTransaction(ops) if err == txn.ErrAborted { err = fmt.Errorf("user already exists") } if err != nil { return nil, err } return u, nil } // getUser fetches information about the user with the // given name into the provided userDoc. func (st *State) getUser(name string, udoc *userDoc) error { err := st.users.Find(bson.D{{"_id", name}}).One(udoc) if err == mgo.ErrNotFound { err = errors.NotFoundf("user %q", name) } return err } // User returns the state user for the given name, func (st *State) User(name string) (*User, error) { u := &User{st: st} if err := st.getUser(name, &u.doc); err != nil { return nil, err } return u, nil } // User represents a juju client user. type User struct { st *State doc userDoc } type userDoc struct { Name string `bson:"_id_"` Deactivated bool // Removing users means they still exist, but are marked deactivated PasswordHash string PasswordSalt string } // Name returns the user name, func (u *User) Name() string { return u.doc.Name } // Tag returns the Tag for // the user ("user-$username") func (u *User) Tag() string { return names.UserTag(u.doc.Name) } // SetPassword sets the password associated with the user. func (u *User) SetPassword(password string) error { salt, err := utils.RandomSalt() if err != nil { return err } return u.SetPasswordHash(utils.UserPasswordHash(password, salt), salt) } // SetPasswordHash sets the password to the // inverse of pwHash = utils.UserPasswordHash(pw, pwSalt). // It can be used when we know only the hash // of the password, but not the clear text. func (u *User) SetPasswordHash(pwHash string, pwSalt string) error { ops := []txn.Op{{ C: u.st.users.Name, Id: u.Name(), Update: bson.D{{"$set", bson.D{{"passwordhash", pwHash}, {"passwordsalt", pwSalt}}}}, }} if err := u.st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set password of user %q: %v", u.Name(), err) } u.doc.PasswordHash = pwHash u.doc.PasswordSalt = pwSalt return nil } // PasswordValid returns whether the given password // is valid for the user. func (u *User) PasswordValid(password string) bool { // If the user is deactivated, no point in carrying on if u.IsDeactivated() { return false } // Since these are potentially set by a User, we intentionally use the // slower pbkdf2 style hashing. Also, we don't expect to have thousands // of Users trying to log in at the same time (which we *do* expect of // Unit and Machine agents.) if u.doc.PasswordSalt != "" { return utils.UserPasswordHash(password, u.doc.PasswordSalt) == u.doc.PasswordHash } // In Juju 1.16 and older, we did not set a Salt for the user password, // so check if the password hash matches using CompatSalt. if it // does, then set the password again so that we get a proper salt if utils.UserPasswordHash(password, utils.CompatSalt) == u.doc.PasswordHash { // This will set a new Salt for the password. We ignore if it // fails because we will try again at the next request logger.Debugf("User %s logged in with CompatSalt resetting password for new salt", u.Name()) err := u.SetPassword(password) if err != nil { logger.Errorf("Cannot set resalted password for user %q", u.Name()) } return true } return false } // Refresh refreshes information about the user // from the state. func (u *User) Refresh() error { var udoc userDoc if err := u.st.getUser(u.Name(), &udoc); err != nil { return err } u.doc = udoc return nil } func (u *User) Deactivate() error { if u.doc.Name == AdminUser { return errors.Unauthorizedf("Can't deactivate admin user") } ops := []txn.Op{{ C: u.st.users.Name, Id: u.Name(), Update: bson.D{{"$set", bson.D{{"deactivated", true}}}}, Assert: txn.DocExists, }} if err := u.st.runTransaction(ops); err != nil { if err == txn.ErrAborted { err = fmt.Errorf("user no longer exists") } return fmt.Errorf("cannot deactivate user %q: %v", u.Name(), err) } u.doc.Deactivated = true return nil } func (u *User) IsDeactivated() bool { return u.doc.Deactivated } ����������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/minimumunits_test.go�����������������������������0000644�0000153�0000161�00000027172�12321735642�026142� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "labix.org/v2/mgo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" ) type MinUnitsSuite struct { ConnSuite service *state.Service } var _ = gc.Suite(&MinUnitsSuite{}) func (s *MinUnitsSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) s.service = s.AddTestingService(c, "dummy-service", s.AddTestingCharm(c, "dummy")) } func (s *MinUnitsSuite) assertRevno(c *gc.C, expectedRevno int, expectedErr error) { revno, err := state.MinUnitsRevno(s.State, s.service.Name()) c.Assert(err, gc.Equals, expectedErr) c.Assert(revno, gc.Equals, expectedRevno) } func (s *MinUnitsSuite) addUnits(c *gc.C, count int) { for i := 0; i < count; i++ { _, err := s.service.AddUnit() c.Assert(err, gc.IsNil) } } func (s *MinUnitsSuite) TestSetMinUnits(c *gc.C) { service := s.service for i, t := range []struct { about string initial int changes []int revno int err error }{{ // Revno is set to zero on creation. about: "setting minimum units", changes: []int{42}, }, { // Revno is increased by the update operation. about: "updating minimum units", initial: 1, changes: []int{42}, revno: 1, }, { // Revno does not change. about: "updating minimum units with the same value", initial: 42, changes: []int{42}, }, { // Revno is increased by each update. about: "increasing minimum units multiple times", initial: 1, changes: []int{2, 3, 4}, revno: 3, }, { // Revno does not change. about: "decreasing minimum units multiple times", initial: 5, changes: []int{3, 2, 1}, }, { // No-op. about: "removing not existent minimum units", changes: []int{0}, err: mgo.ErrNotFound, }, { // The document is deleted. about: "removing existing minimum units", initial: 42, changes: []int{0}, err: mgo.ErrNotFound, }} { c.Logf("test %d. %s", i, t.about) // Set up initial minimum units if required. if t.initial > 0 { err := service.SetMinUnits(t.initial) c.Assert(err, gc.IsNil) } // Insert/update minimum units. for _, input := range t.changes { err := service.SetMinUnits(input) c.Assert(err, gc.IsNil) c.Assert(service.MinUnits(), gc.Equals, input) c.Assert(service.Refresh(), gc.IsNil) c.Assert(service.MinUnits(), gc.Equals, input) } // Check the document existence and revno. s.assertRevno(c, t.revno, t.err) // Clean up, if required, the minUnits document. err := service.SetMinUnits(0) c.Assert(err, gc.IsNil) } } func (s *MinUnitsSuite) TestInvalidMinUnits(c *gc.C) { err := s.service.SetMinUnits(-1) c.Assert(err, gc.ErrorMatches, `cannot set minimum units for service "dummy-service": cannot set a negative minimum number of units`) } func (s *MinUnitsSuite) TestMinUnitsInsertRetry(c *gc.C) { defer state.SetRetryHooks(c, s.State, func() { err := s.service.SetMinUnits(41) c.Assert(err, gc.IsNil) s.assertRevno(c, 0, nil) }, func() { s.assertRevno(c, 1, nil) }).Check() err := s.service.SetMinUnits(42) c.Assert(err, gc.IsNil) c.Assert(s.service.MinUnits(), gc.Equals, 42) } func (s *MinUnitsSuite) TestMinUnitsUpdateRetry(c *gc.C) { err := s.service.SetMinUnits(41) c.Assert(err, gc.IsNil) defer state.SetRetryHooks(c, s.State, func() { err := s.service.SetMinUnits(0) c.Assert(err, gc.IsNil) s.assertRevno(c, 0, mgo.ErrNotFound) }, func() { s.assertRevno(c, 0, nil) }).Check() err = s.service.SetMinUnits(42) c.Assert(err, gc.IsNil) c.Assert(s.service.MinUnits(), gc.Equals, 42) } func (s *MinUnitsSuite) TestMinUnitsRemoveBefore(c *gc.C) { err := s.service.SetMinUnits(41) c.Assert(err, gc.IsNil) defer state.SetBeforeHooks(c, s.State, func() { err := s.service.SetMinUnits(0) c.Assert(err, gc.IsNil) s.assertRevno(c, 0, mgo.ErrNotFound) }).Check() err = s.service.SetMinUnits(0) c.Assert(err, gc.IsNil) c.Assert(s.service.MinUnits(), gc.Equals, 0) } func (s *MinUnitsSuite) testDestroyOrRemoveServiceBefore(c *gc.C, initial, input int, preventRemoval bool) { err := s.service.SetMinUnits(initial) c.Assert(err, gc.IsNil) expectedErr := `cannot set minimum units for service "dummy-service": service "dummy-service" not found` if preventRemoval { expectedErr = `cannot set minimum units for service "dummy-service": service is no longer alive` s.addUnits(c, 1) } defer state.SetBeforeHooks(c, s.State, func() { err := s.service.Destroy() c.Assert(err, gc.IsNil) }).Check() err = s.service.SetMinUnits(input) c.Assert(err, gc.ErrorMatches, expectedErr) s.assertRevno(c, 0, mgo.ErrNotFound) } func (s *MinUnitsSuite) TestMinUnitsInsertDestroyServiceBefore(c *gc.C) { s.testDestroyOrRemoveServiceBefore(c, 0, 42, true) } func (s *MinUnitsSuite) TestMinUnitsUpdateDestroyServiceBefore(c *gc.C) { s.testDestroyOrRemoveServiceBefore(c, 1, 42, true) } func (s *MinUnitsSuite) TestMinUnitsRemoveDestroyServiceBefore(c *gc.C) { s.testDestroyOrRemoveServiceBefore(c, 1, 0, true) } func (s *MinUnitsSuite) TestMinUnitsInsertRemoveServiceBefore(c *gc.C) { s.testDestroyOrRemoveServiceBefore(c, 0, 42, false) } func (s *MinUnitsSuite) TestMinUnitsUpdateRemoveServiceBefore(c *gc.C) { s.testDestroyOrRemoveServiceBefore(c, 1, 42, false) } func (s *MinUnitsSuite) TestMinUnitsRemoveRemoveServiceBefore(c *gc.C) { s.testDestroyOrRemoveServiceBefore(c, 1, 0, false) } func (s *MinUnitsSuite) TestMinUnitsSetDestroyEntities(c *gc.C) { err := s.service.SetMinUnits(1) c.Assert(err, gc.IsNil) s.assertRevno(c, 0, nil) // Add two units to the service for later use. unit1, err := s.service.AddUnit() c.Assert(err, gc.IsNil) unit2, err := s.service.AddUnit() c.Assert(err, gc.IsNil) // Destroy a unit and ensure the revno has been increased. preventUnitDestroyRemove(c, unit1) err = unit1.Destroy() c.Assert(err, gc.IsNil) s.assertRevno(c, 1, nil) // Remove a unit and ensure the revno has been increased.. err = unit2.Destroy() c.Assert(err, gc.IsNil) s.assertRevno(c, 2, nil) // Destroy the service and ensure the minUnits document has been removed. err = s.service.Destroy() c.Assert(err, gc.IsNil) s.assertRevno(c, 0, mgo.ErrNotFound) } func (s *MinUnitsSuite) TestMinUnitsNotSetDestroyEntities(c *gc.C) { // Add two units to the service for later use. unit1, err := s.service.AddUnit() c.Assert(err, gc.IsNil) unit2, err := s.service.AddUnit() c.Assert(err, gc.IsNil) // Destroy a unit and ensure the minUnits document has not been created. preventUnitDestroyRemove(c, unit1) err = unit1.Destroy() c.Assert(err, gc.IsNil) s.assertRevno(c, 0, mgo.ErrNotFound) // Remove a unit and ensure the minUnits document has not been created. err = unit2.Destroy() c.Assert(err, gc.IsNil) s.assertRevno(c, 0, mgo.ErrNotFound) // Destroy the service and ensure the minUnits document is still missing. err = s.service.Destroy() c.Assert(err, gc.IsNil) s.assertRevno(c, 0, mgo.ErrNotFound) } func assertAllUnits(c *gc.C, service *state.Service, expected int) { units, err := service.AllUnits() c.Assert(err, gc.IsNil) c.Assert(units, gc.HasLen, expected) } func (s *MinUnitsSuite) TestEnsureMinUnits(c *gc.C) { service := s.service for i, t := range []struct { about string // Test description. initial int // Initial number of units. minimum int // Minimum number of units for the service. destroy int // Number of units to be destroyed before calling EnsureMinUnits. expected int // Expected number of units after calling EnsureMinUnits. }{{ about: "no minimum units set", }, { about: "initial units > minimum units", initial: 2, minimum: 1, expected: 2, }, { about: "initial units == minimum units", initial: 2, minimum: 2, expected: 2, }, { about: "initial units < minimum units", initial: 1, minimum: 2, expected: 2, }, { about: "alive units < minimum units", initial: 2, minimum: 2, destroy: 1, expected: 3, }, { about: "add multiple units", initial: 6, minimum: 5, destroy: 4, expected: 9, }} { c.Logf("test %d. %s", i, t.about) // Set up initial units if required. s.addUnits(c, t.initial) // Set up minimum units if required. err := service.SetMinUnits(t.minimum) c.Assert(err, gc.IsNil) // Destroy units if required. allUnits, err := service.AllUnits() c.Assert(err, gc.IsNil) for i := 0; i < t.destroy; i++ { preventUnitDestroyRemove(c, allUnits[i]) err = allUnits[i].Destroy() c.Assert(err, gc.IsNil) } // Ensure the minimum number of units is correctly restored. c.Assert(service.Refresh(), gc.IsNil) err = service.EnsureMinUnits() c.Assert(err, gc.IsNil) assertAllUnits(c, service, t.expected) // Clean up the minUnits document and the units. err = service.SetMinUnits(0) c.Assert(err, gc.IsNil) removeAllUnits(c, service) } } func (s *MinUnitsSuite) TestEnsureMinUnitsServiceNotAlive(c *gc.C) { err := s.service.SetMinUnits(2) c.Assert(err, gc.IsNil) s.addUnits(c, 1) err = s.service.Destroy() c.Assert(err, gc.IsNil) expectedErr := `cannot ensure minimum units for service "dummy-service": service is not alive` // An error is returned if the service is not alive. c.Assert(s.service.EnsureMinUnits(), gc.ErrorMatches, expectedErr) // An error is returned if the service was removed. err = s.State.Cleanup() c.Assert(err, gc.IsNil) c.Assert(s.service.EnsureMinUnits(), gc.ErrorMatches, expectedErr) } func (s *MinUnitsSuite) TestEnsureMinUnitsUpdateMinUnitsRetry(c *gc.C) { s.addUnits(c, 1) err := s.service.SetMinUnits(4) c.Assert(err, gc.IsNil) defer state.SetRetryHooks(c, s.State, func() { err := s.service.SetMinUnits(2) c.Assert(err, gc.IsNil) }, func() { assertAllUnits(c, s.service, 2) }).Check() err = s.service.EnsureMinUnits() c.Assert(err, gc.IsNil) } func (s *MinUnitsSuite) TestEnsureMinUnitsAddUnitsRetry(c *gc.C) { err := s.service.SetMinUnits(3) c.Assert(err, gc.IsNil) defer state.SetRetryHooks(c, s.State, func() { s.addUnits(c, 2) }, func() { assertAllUnits(c, s.service, 3) }).Check() err = s.service.EnsureMinUnits() c.Assert(err, gc.IsNil) } func (s *MinUnitsSuite) testEnsureMinUnitsBefore(c *gc.C, f func(), minUnits, expectedUnits int) { service := s.service err := service.SetMinUnits(minUnits) c.Assert(err, gc.IsNil) defer state.SetBeforeHooks(c, s.State, f).Check() err = service.EnsureMinUnits() c.Assert(err, gc.IsNil) assertAllUnits(c, service, expectedUnits) } func (s *MinUnitsSuite) TestEnsureMinUnitsDecreaseMinUnitsBefore(c *gc.C) { f := func() { err := s.service.SetMinUnits(3) c.Assert(err, gc.IsNil) } s.testEnsureMinUnitsBefore(c, f, 42, 3) } func (s *MinUnitsSuite) TestEnsureMinUnitsRemoveMinUnitsBefore(c *gc.C) { f := func() { err := s.service.SetMinUnits(0) c.Assert(err, gc.IsNil) } s.testEnsureMinUnitsBefore(c, f, 2, 0) } func (s *MinUnitsSuite) TestEnsureMinUnitsAddUnitsBefore(c *gc.C) { f := func() { s.addUnits(c, 2) } s.testEnsureMinUnitsBefore(c, f, 2, 2) } func (s *MinUnitsSuite) TestEnsureMinUnitsDestroyServiceBefore(c *gc.C) { s.addUnits(c, 1) err := s.service.SetMinUnits(42) c.Assert(err, gc.IsNil) defer state.SetBeforeHooks(c, s.State, func() { err := s.service.Destroy() c.Assert(err, gc.IsNil) }).Check() c.Assert(s.service.EnsureMinUnits(), gc.ErrorMatches, `cannot ensure minimum units for service "dummy-service": service is not alive`) } func (s *MinUnitsSuite) TestEnsureMinUnitsDecreaseMinUnitsAfter(c *gc.C) { s.addUnits(c, 2) service := s.service err := service.SetMinUnits(5) c.Assert(err, gc.IsNil) defer state.SetAfterHooks(c, s.State, func() { err := service.SetMinUnits(3) c.Assert(err, gc.IsNil) }).Check() c.Assert(service.Refresh(), gc.IsNil) err = service.EnsureMinUnits() c.Assert(err, gc.IsNil) assertAllUnits(c, service, 3) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/user_test.go�������������������������������������0000644�0000153�0000161�00000012651�12321735776�024366� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" "launchpad.net/juju-core/utils" ) type UserSuite struct { ConnSuite } var _ = gc.Suite(&UserSuite{}) func (s *UserSuite) TestAddUserInvalidNames(c *gc.C) { for _, name := range []string{ "foo-bar", "", "0foo", } { u, err := s.State.AddUser(name, "password") c.Assert(err, gc.ErrorMatches, `invalid user name "`+name+`"`) c.Assert(u, gc.IsNil) } } func (s *UserSuite) TestAddUser(c *gc.C) { u, err := s.State.AddUser("a", "b") c.Check(u, gc.NotNil) c.Assert(err, gc.IsNil) c.Assert(u.Name(), gc.Equals, "a") c.Assert(u.PasswordValid("b"), jc.IsTrue) u1, err := s.State.User("a") c.Check(u1, gc.NotNil) c.Assert(err, gc.IsNil) c.Assert(u1.Name(), gc.Equals, "a") c.Assert(u1.PasswordValid("b"), jc.IsTrue) } func (s *UserSuite) TestCheckUserExists(c *gc.C) { u, err := s.State.AddUser("a", "b") c.Check(u, gc.NotNil) c.Assert(err, gc.IsNil) e, err := state.CheckUserExists(s.State, "a") c.Assert(err, gc.IsNil) c.Assert(e, gc.Equals, true) e, err = state.CheckUserExists(s.State, "notAUser") c.Assert(err, gc.IsNil) c.Assert(e, gc.Equals, false) } func (s *UserSuite) TestSetPassword(c *gc.C) { u, err := s.State.AddUser("someuser", "") c.Assert(err, gc.IsNil) testSetPassword(c, func() (state.Authenticator, error) { return s.State.User(u.Name()) }) } func (s *UserSuite) TestAddUserSetsSalt(c *gc.C) { u, err := s.State.AddUser("someuser", "a-password") c.Assert(err, gc.IsNil) salt, hash := state.GetUserPasswordSaltAndHash(u) c.Check(hash, gc.Not(gc.Equals), "") c.Check(salt, gc.Not(gc.Equals), "") c.Check(utils.UserPasswordHash("a-password", salt), gc.Equals, hash) c.Check(u.PasswordValid("a-password"), jc.IsTrue) } func (s *UserSuite) TestSetPasswordChangesSalt(c *gc.C) { u, err := s.State.AddUser("someuser", "a-password") c.Assert(err, gc.IsNil) origSalt, origHash := state.GetUserPasswordSaltAndHash(u) c.Check(origSalt, gc.Not(gc.Equals), "") // Even though the password is the same, we take this opportunity to // update the salt u.SetPassword("a-password") newSalt, newHash := state.GetUserPasswordSaltAndHash(u) c.Check(newSalt, gc.Not(gc.Equals), "") c.Check(newSalt, gc.Not(gc.Equals), origSalt) c.Check(newHash, gc.Not(gc.Equals), origHash) c.Check(u.PasswordValid("a-password"), jc.IsTrue) } func (s *UserSuite) TestSetPasswordHash(c *gc.C) { u, err := s.State.AddUser("someuser", "") c.Assert(err, gc.IsNil) err = u.SetPasswordHash(utils.UserPasswordHash("foo", utils.CompatSalt), utils.CompatSalt) c.Assert(err, gc.IsNil) c.Assert(u.PasswordValid("foo"), jc.IsTrue) c.Assert(u.PasswordValid("bar"), jc.IsFalse) // User passwords should *not* use the fast PasswordHash function hash := utils.AgentPasswordHash("foo-12345678901234567890") c.Assert(err, gc.IsNil) err = u.SetPasswordHash(hash, "") c.Assert(err, gc.IsNil) c.Assert(u.PasswordValid("foo-12345678901234567890"), jc.IsFalse) } func (s *UserSuite) TestSetPasswordHashWithSalt(c *gc.C) { u, err := s.State.AddUser("someuser", "") c.Assert(err, gc.IsNil) err = u.SetPasswordHash(utils.UserPasswordHash("foo", "salted"), "salted") c.Assert(err, gc.IsNil) c.Assert(u.PasswordValid("foo"), jc.IsTrue) salt, hash := state.GetUserPasswordSaltAndHash(u) c.Assert(salt, gc.Equals, "salted") c.Assert(hash, gc.Not(gc.Equals), utils.UserPasswordHash("foo", utils.CompatSalt)) } func (s *UserSuite) TestPasswordValidUpdatesSalt(c *gc.C) { u, err := s.State.AddUser("someuser", "") c.Assert(err, gc.IsNil) compatHash := utils.UserPasswordHash("foo", utils.CompatSalt) err = u.SetPasswordHash(compatHash, "") c.Assert(err, gc.IsNil) beforeSalt, beforeHash := state.GetUserPasswordSaltAndHash(u) c.Assert(beforeSalt, gc.Equals, "") c.Assert(beforeHash, gc.Equals, compatHash) c.Assert(u.PasswordValid("bar"), jc.IsFalse) // A bad password doesn't trigger a rewrite afterBadSalt, afterBadHash := state.GetUserPasswordSaltAndHash(u) c.Assert(afterBadSalt, gc.Equals, "") c.Assert(afterBadHash, gc.Equals, compatHash) // When we get a valid check, we then add a salt and rewrite the hash c.Assert(u.PasswordValid("foo"), jc.IsTrue) afterSalt, afterHash := state.GetUserPasswordSaltAndHash(u) c.Assert(afterSalt, gc.Not(gc.Equals), "") c.Assert(afterHash, gc.Not(gc.Equals), compatHash) c.Assert(afterHash, gc.Equals, utils.UserPasswordHash("foo", afterSalt)) // running PasswordValid again doesn't trigger another rewrite c.Assert(u.PasswordValid("foo"), jc.IsTrue) lastSalt, lastHash := state.GetUserPasswordSaltAndHash(u) c.Assert(lastSalt, gc.Equals, afterSalt) c.Assert(lastHash, gc.Equals, afterHash) } func (s *UserSuite) TestName(c *gc.C) { u, err := s.State.AddUser("someuser", "") c.Assert(err, gc.IsNil) c.Assert(u.Name(), gc.Equals, "someuser") c.Assert(u.Tag(), gc.Equals, "user-someuser") } func (s *UserSuite) TestDeactivate(c *gc.C) { u, err := s.State.AddUser("someuser", "") c.Assert(err, gc.IsNil) c.Assert(u.IsDeactivated(), gc.Equals, false) err = u.Deactivate() c.Assert(err, gc.IsNil) c.Assert(u.IsDeactivated(), gc.Equals, true) c.Assert(u.PasswordValid(""), gc.Equals, false) } func (s *UserSuite) TestCantDeactivateAdminUser(c *gc.C) { u, err := s.State.User(state.AdminUser) c.Assert(err, gc.IsNil) err = u.Deactivate() c.Assert(err, gc.ErrorMatches, "Can't deactivate admin user") } ���������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/endpoint.go��������������������������������������0000644�0000153�0000161�00000003346�12321735642�024162� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "launchpad.net/juju-core/charm" ) // counterpartRole returns the RelationRole that this RelationRole // can relate to. // This should remain an internal method because the relation // model does not guarantee that for every role there will // necessarily exist a single counterpart role that is sensible // for basing algorithms upon. func counterpartRole(r charm.RelationRole) charm.RelationRole { switch r { case charm.RoleProvider: return charm.RoleRequirer case charm.RoleRequirer: return charm.RoleProvider case charm.RolePeer: return charm.RolePeer } panic(fmt.Errorf("unknown relation role %q", r)) } // Endpoint represents one endpoint of a relation. type Endpoint struct { ServiceName string charm.Relation } // String returns the unique identifier of the relation endpoint. func (ep Endpoint) String() string { return ep.ServiceName + ":" + ep.Name } // CanRelateTo returns whether a relation may be established between e and other. func (ep Endpoint) CanRelateTo(other Endpoint) bool { return ep.ServiceName != other.ServiceName && ep.Interface == other.Interface && ep.Role != charm.RolePeer && counterpartRole(ep.Role) == other.Role } type epSlice []Endpoint var roleOrder = map[charm.RelationRole]int{ charm.RoleRequirer: 0, charm.RoleProvider: 1, charm.RolePeer: 2, } func (eps epSlice) Len() int { return len(eps) } func (eps epSlice) Swap(i, j int) { eps[i], eps[j] = eps[j], eps[i] } func (eps epSlice) Less(i, j int) bool { ep1 := eps[i] ep2 := eps[j] if ep1.Role != ep2.Role { return roleOrder[ep1.Role] < roleOrder[ep2.Role] } return ep1.String() < ep2.String() } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/life_test.go�������������������������������������0000644�0000153�0000161�00000010345�12321735642�024315� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "labix.org/v2/mgo/bson" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" ) type LifeSuite struct { ConnSuite charm *state.Charm svc *state.Service } func (s *LifeSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) s.charm = s.AddTestingCharm(c, "dummy") s.svc = s.AddTestingService(c, "dummysvc", s.charm) } var _ = gc.Suite(&LifeSuite{}) var stateChanges = []struct { cached, desired state.Life dbinitial, dbfinal state.Life }{ { state.Alive, state.Dying, state.Alive, state.Dying, }, { state.Alive, state.Dying, state.Dying, state.Dying, }, { state.Alive, state.Dying, state.Dead, state.Dead, }, { state.Alive, state.Dead, state.Alive, state.Dead, }, { state.Alive, state.Dead, state.Dying, state.Dead, }, { state.Alive, state.Dead, state.Dead, state.Dead, }, { state.Dying, state.Dying, state.Dying, state.Dying, }, { state.Dying, state.Dying, state.Dead, state.Dead, }, { state.Dying, state.Dead, state.Dying, state.Dead, }, { state.Dying, state.Dead, state.Dead, state.Dead, }, { state.Dead, state.Dying, state.Dead, state.Dead, }, { state.Dead, state.Dead, state.Dead, state.Dead, }, } type lifeFixture interface { id() (coll string, id interface{}) setup(s *LifeSuite, c *gc.C) state.AgentLiving } type unitLife struct { unit *state.Unit } func (l *unitLife) id() (coll string, id interface{}) { return "units", l.unit.Name() } func (l *unitLife) setup(s *LifeSuite, c *gc.C) state.AgentLiving { unit, err := s.svc.AddUnit() c.Assert(err, gc.IsNil) preventUnitDestroyRemove(c, unit) l.unit = unit return l.unit } type machineLife struct { machine *state.Machine } func (l *machineLife) id() (coll string, id interface{}) { return "machines", l.machine.Id() } func (l *machineLife) setup(s *LifeSuite, c *gc.C) state.AgentLiving { var err error l.machine, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) return l.machine } func (s *LifeSuite) prepareFixture(living state.Living, lfix lifeFixture, cached, dbinitial state.Life, c *gc.C) { collName, id := lfix.id() coll := s.MgoSuite.Session.DB("juju").C(collName) err := coll.UpdateId(id, bson.D{{"$set", bson.D{ {"life", cached}, }}}) c.Assert(err, gc.IsNil) err = living.Refresh() c.Assert(err, gc.IsNil) err = coll.UpdateId(id, bson.D{{"$set", bson.D{ {"life", dbinitial}, }}}) c.Assert(err, gc.IsNil) } func (s *LifeSuite) TestLifecycleStateChanges(c *gc.C) { for i, lfix := range []lifeFixture{&unitLife{}, &machineLife{}} { c.Logf("fixture %d", i) for j, v := range stateChanges { c.Logf("sequence %d", j) living := lfix.setup(s, c) s.prepareFixture(living, lfix, v.cached, v.dbinitial, c) switch v.desired { case state.Dying: err := living.Destroy() c.Assert(err, gc.IsNil) case state.Dead: err := living.EnsureDead() c.Assert(err, gc.IsNil) default: panic("desired lifecycle can only be dying or dead") } err := living.Refresh() c.Assert(err, gc.IsNil) c.Assert(living.Life(), gc.Equals, v.dbfinal) err = living.EnsureDead() c.Assert(err, gc.IsNil) err = living.Remove() c.Assert(err, gc.IsNil) } } } const ( notAliveErr = ".*: not found or not alive" deadErr = ".*: not found or dead" noErr = "" ) type lifer interface { EnsureDead() error Destroy() error Life() state.Life } func runLifeChecks(c *gc.C, obj lifer, expectErr string, checks []func() error) { for i, check := range checks { c.Logf("check %d when %v", i, obj.Life()) err := check() if expectErr == noErr { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, expectErr) } } } // testWhenDying sets obj to Dying and Dead in turn, and asserts // that the errors from the given checks match aliveErr, dyingErr and deadErr // in each respective life state. func testWhenDying(c *gc.C, obj lifer, dyingErr, deadErr string, checks ...func() error) { c.Logf("checking life of %v (%T)", obj, obj) err := obj.Destroy() c.Assert(err, gc.IsNil) runLifeChecks(c, obj, dyingErr, checks) err = obj.EnsureDead() c.Assert(err, gc.IsNil) runLifeChecks(c, obj, deadErr, checks) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/addmachine.go������������������������������������0000644�0000153�0000161�00000042671�12321735776�024433� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "strconv" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/replicaset" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/utils" ) // MachineTemplate holds attributes that are to be associated // with a newly created machine. type MachineTemplate struct { // Series is the series to be associated with the new machine. Series string // Constraints are the constraints to be used when finding // an instance for the machine. Constraints constraints.Value // Jobs holds the jobs to run on the machine's instance. // A machine must have at least one job to do. // JobManageEnviron can only be part of the jobs // when the first (bootstrap) machine is added. Jobs []MachineJob // NoVote holds whether a machine running // a state server should abstain from peer voting. // It is ignored if Jobs does not contain JobManageEnviron. NoVote bool // Addresses holds the addresses to be associated with the // new machine. Addresses []instance.Address // InstanceId holds the instance id to associate with the machine. // If this is empty, the provisioner will try to provision the machine. // If this is non-empty, the HardwareCharacteristics and Nonce // fields must be set appropriately. InstanceId instance.Id // HardwareCharacteristics holds the h/w characteristics to // be associated with the machine. HardwareCharacteristics instance.HardwareCharacteristics // IncludeNetworks holds a list of networks the machine should be // part of. IncludeNetworks []string // ExcludeNetworks holds a list of network the machine should not // be part of. ExcludeNetworks []string // Nonce holds a unique value that can be used to check // if a new instance was really started for this machine. // See Machine.SetProvisioned. This must be set if InstanceId is set. Nonce string // Dirty signifies whether the new machine will be treated // as unclean for unit-assignment purposes. Dirty bool // principals holds the principal units that will // associated with the machine. principals []string } // AddMachineInsideNewMachine creates a new machine within a container // of the given type inside another new machine. The two given templates // specify the form of the child and parent respectively. func (st *State) AddMachineInsideNewMachine(template, parentTemplate MachineTemplate, containerType instance.ContainerType) (*Machine, error) { mdoc, ops, err := st.addMachineInsideNewMachineOps(template, parentTemplate, containerType) if err != nil { return nil, fmt.Errorf("cannot add a new machine: %v", err) } return st.addMachine(mdoc, ops) } // AddMachineInsideMachine adds a machine inside a container of the // given type on the existing machine with id=parentId. func (st *State) AddMachineInsideMachine(template MachineTemplate, parentId string, containerType instance.ContainerType) (*Machine, error) { mdoc, ops, err := st.addMachineInsideMachineOps(template, parentId, containerType) if err != nil { return nil, fmt.Errorf("cannot add a new machine: %v", err) } return st.addMachine(mdoc, ops) } // AddMachine adds a machine with the given series and jobs. // It is deprecated and around for testing purposes only. func (st *State) AddMachine(series string, jobs ...MachineJob) (*Machine, error) { ms, err := st.AddMachines(MachineTemplate{ Series: series, Jobs: jobs, }) if err != nil { return nil, err } return ms[0], nil } // AddOne machine adds a new machine configured according to the // given template. func (st *State) AddOneMachine(template MachineTemplate) (*Machine, error) { ms, err := st.AddMachines(template) if err != nil { return nil, err } return ms[0], nil } // AddMachines adds new machines configured according to the // given templates. func (st *State) AddMachines(templates ...MachineTemplate) (_ []*Machine, err error) { defer utils.ErrorContextf(&err, "cannot add a new machine") var ms []*Machine env, err := st.Environment() if err != nil { return nil, err } else if env.Life() != Alive { return nil, fmt.Errorf("environment is no longer alive") } var ops []txn.Op var mdocs []*machineDoc for _, template := range templates { mdoc, addOps, err := st.addMachineOps(template) if err != nil { return nil, err } mdocs = append(mdocs, mdoc) ms = append(ms, newMachine(st, mdoc)) ops = append(ops, addOps...) } ssOps, err := st.maintainStateServersOps(mdocs, nil) if err != nil { return nil, err } ops = append(ops, ssOps...) ops = append(ops, env.assertAliveOp()) if err := st.runTransaction(ops); err != nil { return nil, onAbort(err, fmt.Errorf("environment is no longer alive")) } return ms, nil } func (st *State) addMachine(mdoc *machineDoc, ops []txn.Op) (*Machine, error) { env, err := st.Environment() if err != nil { return nil, err } else if env.Life() != Alive { return nil, fmt.Errorf("environment is no longer alive") } ops = append([]txn.Op{env.assertAliveOp()}, ops...) if err := st.runTransaction(ops); err != nil { enverr := env.Refresh() if (enverr == nil && env.Life() != Alive) || errors.IsNotFoundError(enverr) { return nil, fmt.Errorf("environment is no longer alive") } else if enverr != nil { err = enverr } return nil, err } return newMachine(st, mdoc), nil } // effectiveMachineTemplate verifies that the given template is // valid and combines it with values from the state // to produce a resulting template that more accurately // represents the data that will be inserted into the state. func (st *State) effectiveMachineTemplate(p MachineTemplate, allowStateServer bool) (MachineTemplate, error) { if p.Series == "" { return MachineTemplate{}, fmt.Errorf("no series specified") } cons, err := st.EnvironConstraints() if err != nil { return MachineTemplate{}, err } p.Constraints = p.Constraints.WithFallbacks(cons) // Machine constraints do not use a container constraint value. // Both provisioning and deployment constraints use the same // constraints.Value struct so here we clear the container // value. Provisioning ignores the container value but clearing // it avoids potential confusion. p.Constraints.Container = nil if len(p.Jobs) == 0 { return MachineTemplate{}, fmt.Errorf("no jobs specified") } jset := make(map[MachineJob]bool) for _, j := range p.Jobs { if jset[j] { return MachineTemplate{}, fmt.Errorf("duplicate job: %s", j) } jset[j] = true } if jset[JobManageEnviron] { if !allowStateServer { return MachineTemplate{}, errStateServerNotAllowed } } if p.InstanceId != "" { if p.Nonce == "" { return MachineTemplate{}, fmt.Errorf("cannot add a machine with an instance id and no nonce") } } else if p.Nonce != "" { return MachineTemplate{}, fmt.Errorf("cannot specify a nonce without an instance id") } return p, nil } // addMachineOps returns operations to add a new top level machine // based on the given template. It also returns the machine document // that will be inserted. func (st *State) addMachineOps(template MachineTemplate) (*machineDoc, []txn.Op, error) { template, err := st.effectiveMachineTemplate(template, true) if err != nil { return nil, nil, err } if template.InstanceId == "" { if err := st.precheckInstance(template.Series, template.Constraints); err != nil { return nil, nil, err } } seq, err := st.sequence("machine") if err != nil { return nil, nil, err } mdoc := machineDocForTemplate(template, strconv.Itoa(seq)) var ops []txn.Op ops = append(ops, st.insertNewMachineOps(mdoc, template)...) ops = append(ops, st.insertNewContainerRefOp(mdoc.Id)) if template.InstanceId != "" { ops = append(ops, txn.Op{ C: st.instanceData.Name, Id: mdoc.Id, Assert: txn.DocMissing, Insert: &instanceData{ Id: mdoc.Id, InstanceId: template.InstanceId, Arch: template.HardwareCharacteristics.Arch, Mem: template.HardwareCharacteristics.Mem, RootDisk: template.HardwareCharacteristics.RootDisk, CpuCores: template.HardwareCharacteristics.CpuCores, CpuPower: template.HardwareCharacteristics.CpuPower, Tags: template.HardwareCharacteristics.Tags, }, }) } return mdoc, ops, nil } // supportsContainerType reports whether the machine supports the given // container type. If the machine's supportedContainers attribute is // set, this decision can be made right here, otherwise we assume that // everything will be ok and later on put the container into an error // state if necessary. func (m *Machine) supportsContainerType(ctype instance.ContainerType) bool { supportedContainers, ok := m.SupportedContainers() if !ok { // We don't know yet, so we report that we support the container. return true } for _, ct := range supportedContainers { if ct == ctype { return true } } return false } // addMachineInsideMachineOps returns operations to add a machine inside // a container of the given type on an existing machine. func (st *State) addMachineInsideMachineOps(template MachineTemplate, parentId string, containerType instance.ContainerType) (*machineDoc, []txn.Op, error) { if template.InstanceId != "" { return nil, nil, fmt.Errorf("cannot specify instance id for a new container") } template, err := st.effectiveMachineTemplate(template, false) if err != nil { return nil, nil, err } if containerType == "" { return nil, nil, fmt.Errorf("no container type specified") } // If a parent machine is specified, make sure it exists // and can support the requested container type. parent, err := st.Machine(parentId) if err != nil { return nil, nil, err } if !parent.supportsContainerType(containerType) { return nil, nil, fmt.Errorf("machine %s cannot host %s containers", parentId, containerType) } newId, err := st.newContainerId(parentId, containerType) if err != nil { return nil, nil, err } mdoc := machineDocForTemplate(template, newId) mdoc.ContainerType = string(containerType) var ops []txn.Op ops = append(ops, st.insertNewMachineOps(mdoc, template)...) ops = append(ops, // Update containers record for host machine. st.addChildToContainerRefOp(parentId, mdoc.Id), // Create a containers reference document for the container itself. st.insertNewContainerRefOp(mdoc.Id), ) return mdoc, ops, nil } // newContainerId returns a new id for a machine within the machine // with id parentId and the given container type. func (st *State) newContainerId(parentId string, containerType instance.ContainerType) (string, error) { seq, err := st.sequence(fmt.Sprintf("machine%s%sContainer", parentId, containerType)) if err != nil { return "", err } return fmt.Sprintf("%s/%s/%d", parentId, containerType, seq), nil } // addMachineInsideNewMachineOps returns operations to create a new // machine within a container of the given type inside another // new machine. The two given templates specify the form // of the child and parent respectively. func (st *State) addMachineInsideNewMachineOps(template, parentTemplate MachineTemplate, containerType instance.ContainerType) (*machineDoc, []txn.Op, error) { if template.InstanceId != "" || parentTemplate.InstanceId != "" { return nil, nil, fmt.Errorf("cannot specify instance id for a new container") } seq, err := st.sequence("machine") if err != nil { return nil, nil, err } parentTemplate, err = st.effectiveMachineTemplate(parentTemplate, false) if err != nil { return nil, nil, err } if containerType == "" { return nil, nil, fmt.Errorf("no container type specified") } if parentTemplate.InstanceId == "" { if err := st.precheckInstance(parentTemplate.Series, parentTemplate.Constraints); err != nil { return nil, nil, err } } parentDoc := machineDocForTemplate(parentTemplate, strconv.Itoa(seq)) newId, err := st.newContainerId(parentDoc.Id, containerType) if err != nil { return nil, nil, err } template, err = st.effectiveMachineTemplate(template, false) if err != nil { return nil, nil, err } mdoc := machineDocForTemplate(template, newId) mdoc.ContainerType = string(containerType) var ops []txn.Op ops = append(ops, st.insertNewMachineOps(parentDoc, parentTemplate)...) ops = append(ops, st.insertNewMachineOps(mdoc, template)...) ops = append(ops, // The host machine doesn't exist yet, create a new containers record. st.insertNewContainerRefOp(mdoc.Id), // Create a containers reference document for the container itself. st.insertNewContainerRefOp(parentDoc.Id, mdoc.Id), ) return mdoc, ops, nil } func machineDocForTemplate(template MachineTemplate, id string) *machineDoc { return &machineDoc{ Id: id, Series: template.Series, Jobs: template.Jobs, Clean: !template.Dirty, Principals: template.principals, Life: Alive, InstanceId: template.InstanceId, Nonce: template.Nonce, Addresses: instanceAddressesToAddresses(template.Addresses), NoVote: template.NoVote, } } // insertNewMachineOps returns operations to insert the given machine // document into the database, based on the given template. Only the // constraints and networks are used from the template. func (st *State) insertNewMachineOps(mdoc *machineDoc, template MachineTemplate) []txn.Op { return []txn.Op{ { C: st.machines.Name, Id: mdoc.Id, Assert: txn.DocMissing, Insert: mdoc, }, createConstraintsOp(st, machineGlobalKey(mdoc.Id), template.Constraints), createStatusOp(st, machineGlobalKey(mdoc.Id), statusDoc{ Status: params.StatusPending, }), createNetworksOp(st, machineGlobalKey(mdoc.Id), template.IncludeNetworks, template.ExcludeNetworks, ), } } func hasJob(jobs []MachineJob, job MachineJob) bool { for _, j := range jobs { if j == job { return true } } return false } var errStateServerNotAllowed = fmt.Errorf("state server jobs specified without calling EnsureAvailability") // maintainStateServersOps returns a set of operations that will maintain // the state server information when the given machine documents // are added to the machines collection. If currentInfo is nil, // there can be only one machine document and it must have // id 0 (this is a special case to allow adding the bootstrap machine) func (st *State) maintainStateServersOps(mdocs []*machineDoc, currentInfo *StateServerInfo) ([]txn.Op, error) { var newIds, newVotingIds []string for _, doc := range mdocs { if !hasJob(doc.Jobs, JobManageEnviron) { continue } newIds = append(newIds, doc.Id) if !doc.NoVote { newVotingIds = append(newVotingIds, doc.Id) } } if len(newIds) == 0 { return nil, nil } if currentInfo == nil { // Allow bootstrap machine only. if len(mdocs) != 1 || mdocs[0].Id != "0" { return nil, errStateServerNotAllowed } var err error currentInfo, err = st.StateServerInfo() if err != nil { return nil, fmt.Errorf("cannot get state server info: %v", err) } if len(currentInfo.MachineIds) > 0 || len(currentInfo.VotingMachineIds) > 0 { return nil, fmt.Errorf("state servers already exist") } } ops := []txn.Op{{ C: st.stateServers.Name, Id: environGlobalKey, Assert: bson.D{{ "$and", []bson.D{ {{"machineids", bson.D{{"$size", len(currentInfo.MachineIds)}}}}, {{"votingmachineids", bson.D{{"$size", len(currentInfo.VotingMachineIds)}}}}, }, }}, Update: bson.D{ {"$addToSet", bson.D{{"machineids", bson.D{{"$each", newIds}}}}}, {"$addToSet", bson.D{{"votingmachineids", bson.D{{"$each", newVotingIds}}}}}, }, }} return ops, nil } // EnsureAvailability adds state server machines as necessary to make // the number of live state servers equal to numStateServers. The given // constraints and series will be attached to any new machines. // // TODO(rog): // If any current state servers are down, they will be // removed from the current set of voting replica set // peers (although the machines themselves will remain // and they will still remain part of the replica set). // Once a machine's voting status has been removed, // the machine itself may be removed. func (st *State) EnsureAvailability(numStateServers int, cons constraints.Value, series string) error { if numStateServers%2 != 1 || numStateServers <= 0 { return fmt.Errorf("number of state servers must be odd and greater than zero") } if numStateServers > replicaset.MaxPeers { return fmt.Errorf("state server count is too large (allowed %d)", replicaset.MaxPeers) } info, err := st.StateServerInfo() if err != nil { return err } if len(info.VotingMachineIds) == numStateServers { // TODO(rog) #1271504 2014-01-22 // Find machines which are down, set // their NoVote flag and add new machines to // replace them. return nil } if len(info.VotingMachineIds) > numStateServers { return fmt.Errorf("cannot reduce state server count") } mdocs := make([]*machineDoc, 0, numStateServers-len(info.MachineIds)) var ops []txn.Op for i := len(info.MachineIds); i < numStateServers; i++ { template := MachineTemplate{ Series: series, Jobs: []MachineJob{ JobHostUnits, JobManageEnviron, }, Constraints: cons, } mdoc, addOps, err := st.addMachineOps(template) if err != nil { return err } mdocs = append(mdocs, mdoc) ops = append(ops, addOps...) } ssOps, err := st.maintainStateServersOps(mdocs, info) if err != nil { return fmt.Errorf("cannot prepare machine add operations: %v", err) } ops = append(ops, ssOps...) err = st.runTransaction(ops) if err != nil { return fmt.Errorf("failed to create new state server machines: %v", err) } return nil } �����������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/compat_test.go�����������������������������������0000644�0000153�0000161�00000006022�12321735776�024666� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) // compatSuite contains backwards compatibility tests, // for ensuring state operations behave correctly across // schema changes. type compatSuite struct { testbase.LoggingSuite testing.MgoSuite state *State env *Environment } var _ = gc.Suite(&compatSuite{}) func (s *compatSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *compatSuite) TearDownSuite(c *gc.C) { s.MgoSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *compatSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) s.state = TestingInitialize(c, nil, Policy(nil)) env, err := s.state.Environment() c.Assert(err, gc.IsNil) s.env = env } func (s *compatSuite) TearDownTest(c *gc.C) { s.state.Close() s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *compatSuite) TestEnvironAssertAlive(c *gc.C) { // 1.17+ has a "Life" field in environment documents. // We remove it here, to test 1.16 compatibility. ops := []txn.Op{{ C: s.state.environments.Name, Id: s.env.doc.UUID, Update: bson.D{{"$unset", bson.D{{"life", nil}}}}, }} err := s.state.runTransaction(ops) c.Assert(err, gc.IsNil) // Now check the assertAliveOp and Destroy work as if // the environment is Alive. err = s.state.runTransaction([]txn.Op{s.env.assertAliveOp()}) c.Assert(err, gc.IsNil) err = s.env.Destroy() c.Assert(err, gc.IsNil) } func (s *compatSuite) TestGetServiceWithoutNetworksIsOK(c *gc.C) { _, err := s.state.AddUser(AdminUser, "pass") c.Assert(err, gc.IsNil) charm := addCharm(c, s.state, "quantal", testing.Charms.Dir("mysql")) service, err := s.state.AddService("mysql", "user-admin", charm, nil, nil) c.Assert(err, gc.IsNil) // In 1.17.7+ all services have associated document in the // networks collection. We remove it here to test backwards // compatibility. ops := []txn.Op{removeNetworksOp(s.state, service.globalKey())} err = s.state.runTransaction(ops) c.Assert(err, gc.IsNil) // Now check the trying to fetch service's networks is OK. include, exclude, err := service.Networks() c.Assert(err, gc.IsNil) c.Assert(include, gc.HasLen, 0) c.Assert(exclude, gc.HasLen, 0) } func (s *compatSuite) TestGetMachineWithoutNetworksIsOK(c *gc.C) { machine, err := s.state.AddMachine("quantal", JobHostUnits) c.Assert(err, gc.IsNil) // In 1.17.7+ all machines have associated document in the // networks collection. We remove it here to test backwards // compatibility. ops := []txn.Op{removeNetworksOp(s.state, machine.globalKey())} err = s.state.runTransaction(ops) c.Assert(err, gc.IsNil) // Now check the trying to fetch machine's networks is OK. include, exclude, err := machine.Networks() c.Assert(err, gc.IsNil) c.Assert(include, gc.HasLen, 0) c.Assert(exclude, gc.HasLen, 0) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/annotator.go�������������������������������������0000644�0000153�0000161�00000011050�12321735642�024336� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "strings" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/utils" ) // annotatorDoc represents the internal state of annotations for an Entity in // MongoDB. Note that the annotations map is not maintained in local storage // due to the fact that it is not accessed directly, but through // Annotations/Annotation below. // Note also the correspondence with AnnotationInfo in state/api/params. type annotatorDoc struct { GlobalKey string `bson:"_id"` Tag string Annotations map[string]string } // annotator implements annotation-related methods // for any entity that wishes to use it. type annotator struct { globalKey string tag string st *State } // SetAnnotations adds key/value pairs to annotations in MongoDB. func (a *annotator) SetAnnotations(pairs map[string]string) (err error) { defer utils.ErrorContextf(&err, "cannot update annotations on %s", a.tag) if len(pairs) == 0 { return nil } // Collect in separate maps pairs to be inserted/updated or removed. toRemove := make(map[string]bool) toInsert := make(map[string]string) toUpdate := make(map[string]string) for key, value := range pairs { if strings.Contains(key, ".") { return fmt.Errorf("invalid key %q", key) } if value == "" { toRemove["annotations."+key] = true } else { toInsert[key] = value toUpdate["annotations."+key] = value } } // Two attempts should be enough to update annotations even with racing // clients - if the document does not already exist, one of the clients // will create it and the others will fail, then all the rest of the // clients should succeed on their second attempt. If the referred-to // entity has disappeared, and removed its annotations in the meantime, // we consider that worthy of an error (will be fixed when new entities // can never share names with old ones). for i := 0; i < 2; i++ { var ops []txn.Op if count, err := a.st.annotations.FindId(a.globalKey).Count(); err != nil { return err } else if count == 0 { // Check that the annotator entity was not previously destroyed. if i != 0 { return fmt.Errorf("%s no longer exists", a.tag) } ops, err = a.insertOps(toInsert) if err != nil { return err } } else { ops = a.updateOps(toUpdate, toRemove) } if err := a.st.runTransaction(ops); err == nil { return nil } else if err != txn.ErrAborted { return err } } return ErrExcessiveContention } // insertOps returns the operations required to insert annotations in MongoDB. func (a *annotator) insertOps(toInsert map[string]string) ([]txn.Op, error) { tag := a.tag ops := []txn.Op{{ C: a.st.annotations.Name, Id: a.globalKey, Assert: txn.DocMissing, Insert: &annotatorDoc{a.globalKey, tag, toInsert}, }} if strings.HasPrefix(tag, "environment-") { return ops, nil } // If the entity is not the environment, add a DocExists check on the // entity document, in order to avoid possible races between entity // removal and annotation creation. coll, id, err := a.st.parseTag(tag) if err != nil { return nil, err } return append(ops, txn.Op{ C: coll, Id: id, Assert: txn.DocExists, }), nil } // updateOps returns the operations required to update or remove annotations in MongoDB. func (a *annotator) updateOps(toUpdate map[string]string, toRemove map[string]bool) []txn.Op { return []txn.Op{{ C: a.st.annotations.Name, Id: a.globalKey, Assert: txn.DocExists, Update: bson.D{{"$set", toUpdate}, {"$unset", toRemove}}, }} } // Annotations returns all the annotations corresponding to an entity. func (a *annotator) Annotations() (map[string]string, error) { doc := new(annotatorDoc) err := a.st.annotations.FindId(a.globalKey).One(doc) if err == mgo.ErrNotFound { // Returning an empty map if there are no annotations. return make(map[string]string), nil } if err != nil { return nil, err } return doc.Annotations, nil } // Annotation returns the annotation value corresponding to the given key. // If the requested annotation is not found, an empty string is returned. func (a *annotator) Annotation(key string) (string, error) { ann, err := a.Annotations() if err != nil { return "", err } return ann[key], nil } // annotationRemoveOp returns an operation to remove a given annotation // document from MongoDB. func annotationRemoveOp(st *State, id string) txn.Op { return txn.Op{ C: st.annotations.Name, Id: id, Remove: true, } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/initialize_test.go�������������������������������0000644�0000153�0000161�00000011223�12321735642�025533� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/state" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type InitializeSuite struct { testing.MgoSuite testbase.LoggingSuite State *state.State } var _ = gc.Suite(&InitializeSuite{}) func (s *InitializeSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *InitializeSuite) TearDownSuite(c *gc.C) { s.MgoSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *InitializeSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) } func (s *InitializeSuite) openState(c *gc.C) { var err error s.State, err = state.Open(state.TestingStateInfo(), state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) } func (s *InitializeSuite) TearDownTest(c *gc.C) { s.State.Close() s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *InitializeSuite) TestInitialize(c *gc.C) { cfg := testing.EnvironConfig(c) initial := cfg.AllAttrs() st, err := state.Initialize(state.TestingStateInfo(), cfg, state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) c.Assert(st, gc.NotNil) err = st.Close() c.Assert(err, gc.IsNil) s.openState(c) cfg, err = s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.AllAttrs(), gc.DeepEquals, initial) env, err := s.State.Environment() c.Assert(err, gc.IsNil) entity, err := s.State.FindEntity("environment-" + env.UUID()) c.Assert(err, gc.IsNil) annotator := entity.(state.Annotator) annotations, err := annotator.Annotations() c.Assert(err, gc.IsNil) c.Assert(annotations, gc.HasLen, 0) cons, err := s.State.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(&cons, jc.Satisfies, constraints.IsEmpty) addrs, err := s.State.APIHostPorts() c.Assert(err, gc.IsNil) c.Assert(addrs, gc.HasLen, 0) info, err := s.State.StateServerInfo() c.Assert(err, gc.IsNil) c.Assert(info, jc.DeepEquals, &state.StateServerInfo{}) } func (s *InitializeSuite) TestDoubleInitializeConfig(c *gc.C) { cfg := testing.EnvironConfig(c) initial := cfg.AllAttrs() st := state.TestingInitialize(c, cfg, state.Policy(nil)) st.Close() // A second initialize returns an open *State, but ignores its params. // TODO(fwereade) I think this is crazy, but it's what we were testing // for originally... cfg, err := cfg.Apply(map[string]interface{}{"authorized-keys": "something-else"}) c.Assert(err, gc.IsNil) st, err = state.Initialize(state.TestingStateInfo(), cfg, state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) c.Assert(st, gc.NotNil) st.Close() s.openState(c) cfg, err = s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.AllAttrs(), gc.DeepEquals, initial) } func (s *InitializeSuite) TestEnvironConfigWithAdminSecret(c *gc.C) { // admin-secret blocks Initialize. good := testing.EnvironConfig(c) badUpdateAttrs := map[string]interface{}{"admin-secret": "foo"} bad, err := good.Apply(badUpdateAttrs) _, err = state.Initialize(state.TestingStateInfo(), bad, state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.ErrorMatches, "admin-secret should never be written to the state") // admin-secret blocks UpdateEnvironConfig. st := state.TestingInitialize(c, good, state.Policy(nil)) st.Close() s.openState(c) err = s.State.UpdateEnvironConfig(badUpdateAttrs, nil, nil) c.Assert(err, gc.ErrorMatches, "admin-secret should never be written to the state") // EnvironConfig remains inviolate. cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.AllAttrs(), gc.DeepEquals, good.AllAttrs()) } func (s *InitializeSuite) TestEnvironConfigWithoutAgentVersion(c *gc.C) { // admin-secret blocks Initialize. good := testing.EnvironConfig(c) attrs := good.AllAttrs() delete(attrs, "agent-version") bad, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) _, err = state.Initialize(state.TestingStateInfo(), bad, state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.ErrorMatches, "agent-version must always be set in state") st := state.TestingInitialize(c, good, state.Policy(nil)) st.Close() s.openState(c) err = s.State.UpdateEnvironConfig(map[string]interface{}{}, []string{"agent-version"}, nil) c.Assert(err, gc.ErrorMatches, "agent-version must always be set in state") // EnvironConfig remains inviolate. cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(cfg.AllAttrs(), gc.DeepEquals, good.AllAttrs()) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/prechecker_test.go�������������������������������0000644�0000153�0000161�00000010546�12321735642�025514� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" ) type PrecheckerSuite struct { ConnSuite prechecker mockPrechecker } var _ = gc.Suite(&PrecheckerSuite{}) type mockPrechecker struct { precheckInstanceError error precheckInstanceSeries string precheckInstanceConstraints constraints.Value } func (p *mockPrechecker) PrecheckInstance(series string, cons constraints.Value) error { p.precheckInstanceSeries = series p.precheckInstanceConstraints = cons return p.precheckInstanceError } func (s *PrecheckerSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) s.prechecker = mockPrechecker{} s.policy.getPrechecker = func(*config.Config) (state.Prechecker, error) { return &s.prechecker, nil } } func (s *PrecheckerSuite) TestPrecheckInstance(c *gc.C) { // PrecheckInstance should be called with the specified // series, and the specified constraints merged with the // environment constraints, when attempting to create an // instance. envCons := constraints.MustParse("mem=4G") template, err := s.addOneMachine(c, envCons) c.Assert(err, gc.IsNil) c.Assert(s.prechecker.precheckInstanceSeries, gc.Equals, template.Series) cons := template.Constraints.WithFallbacks(envCons) c.Assert(s.prechecker.precheckInstanceConstraints, gc.DeepEquals, cons) } func (s *PrecheckerSuite) TestPrecheckErrors(c *gc.C) { // Ensure that AddOneMachine fails when PrecheckInstance returns an error. s.prechecker.precheckInstanceError = fmt.Errorf("no instance for you") _, err := s.addOneMachine(c, constraints.Value{}) c.Assert(err, gc.ErrorMatches, ".*no instance for you") // If the policy's Prechecker method fails, that will be returned first. s.policy.getPrechecker = func(*config.Config) (state.Prechecker, error) { return nil, fmt.Errorf("no prechecker for you") } _, err = s.addOneMachine(c, constraints.Value{}) c.Assert(err, gc.ErrorMatches, ".*no prechecker for you") } func (s *PrecheckerSuite) TestPrecheckPrecheckerUnimplemented(c *gc.C) { var precheckerErr error s.policy.getPrechecker = func(*config.Config) (state.Prechecker, error) { return nil, precheckerErr } _, err := s.addOneMachine(c, constraints.Value{}) c.Assert(err, gc.ErrorMatches, "cannot add a new machine: policy returned nil prechecker without an error") precheckerErr = errors.NewNotImplementedError("Prechecker") _, err = s.addOneMachine(c, constraints.Value{}) c.Assert(err, gc.IsNil) } func (s *PrecheckerSuite) TestPrecheckNoPolicy(c *gc.C) { s.policy.getPrechecker = func(*config.Config) (state.Prechecker, error) { c.Errorf("should not have been invoked") return nil, nil } state.SetPolicy(s.State, nil) _, err := s.addOneMachine(c, constraints.Value{}) c.Assert(err, gc.IsNil) } func (s *PrecheckerSuite) addOneMachine(c *gc.C, envCons constraints.Value) (state.MachineTemplate, error) { err := s.State.SetEnvironConstraints(envCons) c.Assert(err, gc.IsNil) oneJob := []state.MachineJob{state.JobHostUnits} extraCons := constraints.MustParse("cpu-cores=4") template := state.MachineTemplate{ Series: "precise", Constraints: extraCons, Jobs: oneJob, } _, err = s.State.AddOneMachine(template) return template, err } func (s *PrecheckerSuite) TestPrecheckInstanceInjectMachine(c *gc.C) { template := state.MachineTemplate{ InstanceId: instance.Id("bootstrap"), Series: "precise", Nonce: state.BootstrapNonce, Jobs: []state.MachineJob{state.JobManageEnviron}, } _, err := s.State.AddOneMachine(template) c.Assert(err, gc.IsNil) // PrecheckInstance should not have been called, as we've // injected a machine with an existing instance. c.Assert(s.prechecker.precheckInstanceSeries, gc.Equals, "") } func (s *PrecheckerSuite) TestPrecheckContainerNewMachine(c *gc.C) { // Attempting to add a container to a new machine should cause // PrecheckInstance to be called. template := state.MachineTemplate{ Series: "precise", Jobs: []state.MachineJob{state.JobHostUnits}, } _, err := s.State.AddMachineInsideNewMachine(template, template, instance.LXC) c.Assert(err, gc.IsNil) c.Assert(s.prechecker.precheckInstanceSeries, gc.Equals, template.Series) } ����������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/settings.go��������������������������������������0000644�0000153�0000161�00000021771�12321735642�024204� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "sort" "strings" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/errors" ) // See: http://docs.mongodb.org/manual/faq/developers/#faq-dollar-sign-escaping // for why we're using those replacements. const ( fullWidthDot = "\uff0e" fullWidthDollar = "\uff04" ) var ( escapeReplacer = strings.NewReplacer(".", fullWidthDot, "$", fullWidthDollar) unescapeReplacer = strings.NewReplacer(fullWidthDot, ".", fullWidthDollar, "$") ) const ( ItemAdded = iota ItemModified ItemDeleted ) // ItemChange represents the change of an item in a settings. type ItemChange struct { Type int Key string OldValue interface{} NewValue interface{} } // String returns the item change in a readable format. func (ic *ItemChange) String() string { switch ic.Type { case ItemAdded: return fmt.Sprintf("setting added: %v = %v", ic.Key, ic.NewValue) case ItemModified: return fmt.Sprintf("setting modified: %v = %v (was %v)", ic.Key, ic.NewValue, ic.OldValue) case ItemDeleted: return fmt.Sprintf("setting deleted: %v (was %v)", ic.Key, ic.OldValue) } return fmt.Sprintf("unknown setting change type %d: %v = %v (was %v)", ic.Type, ic.Key, ic.NewValue, ic.OldValue) } // itemChangeSlice contains a slice of item changes in a config node. // It implements the sort interface to sort the items changes by key. type itemChangeSlice []ItemChange func (ics itemChangeSlice) Len() int { return len(ics) } func (ics itemChangeSlice) Less(i, j int) bool { return ics[i].Key < ics[j].Key } func (ics itemChangeSlice) Swap(i, j int) { ics[i], ics[j] = ics[j], ics[i] } // A Settings manages changes to settings as a delta in memory and merges // them back in the database when explicitly requested. type Settings struct { st *State key string // disk holds the values in the config node before // any keys have been changed. It is reset on Read and Write // operations. disk map[string]interface{} // cache holds the current values in the config node. // The difference between disk and core // determines the delta to be applied when Settings.Write // is called. core map[string]interface{} txnRevno int64 } // Keys returns the current keys in alphabetical order. func (c *Settings) Keys() []string { keys := []string{} for key := range c.core { keys = append(keys, key) } sort.Strings(keys) return keys } // Get returns the value of key and whether it was found. func (c *Settings) Get(key string) (value interface{}, found bool) { value, found = c.core[key] return } // Map returns all keys and values of the node. func (c *Settings) Map() map[string]interface{} { return copyMap(c.core, nil) } // Set sets key to value func (c *Settings) Set(key string, value interface{}) { c.core[key] = value } // Update sets multiple key/value pairs. func (c *Settings) Update(kv map[string]interface{}) { for key, value := range kv { c.core[key] = value } } // Delete removes key. func (c *Settings) Delete(key string) { delete(c.core, key) } // cacheKeys returns the keys of all caches as a key=>true map. func cacheKeys(caches ...map[string]interface{}) map[string]bool { keys := make(map[string]bool) for _, cache := range caches { for key := range cache { keys[key] = true } } return keys } // Write writes changes made to c back onto its node. Changes are written // as a delta applied on top of the latest version of the node, to prevent // overwriting unrelated changes made to the node since it was last read. func (c *Settings) Write() ([]ItemChange, error) { changes := []ItemChange{} updates := map[string]interface{}{} deletions := map[string]int{} for key := range cacheKeys(c.disk, c.core) { old, ondisk := c.disk[key] new, incore := c.core[key] if new == old { continue } var change ItemChange escapedKey := escapeReplacer.Replace(key) switch { case incore && ondisk: change = ItemChange{ItemModified, key, old, new} updates[escapedKey] = new case incore && !ondisk: change = ItemChange{ItemAdded, key, nil, new} updates[escapedKey] = new case ondisk && !incore: change = ItemChange{ItemDeleted, key, old, nil} deletions[escapedKey] = 1 default: panic("unreachable") } changes = append(changes, change) } if len(changes) == 0 { return []ItemChange{}, nil } sort.Sort(itemChangeSlice(changes)) ops := []txn.Op{{ C: c.st.settings.Name, Id: c.key, Assert: txn.DocExists, Update: bson.D{ {"$set", updates}, {"$unset", deletions}, }, }} err := c.st.runTransaction(ops) if err == txn.ErrAborted { return nil, errors.NotFoundf("settings") } if err != nil { return nil, fmt.Errorf("cannot write settings: %v", err) } c.disk = copyMap(c.core, nil) return changes, nil } func newSettings(st *State, key string) *Settings { return &Settings{ st: st, key: key, core: make(map[string]interface{}), } } // cleanSettingsMap cleans the map of version and _id fields and also unescapes // keys coming out of MongoDB. func cleanSettingsMap(in map[string]interface{}) { delete(in, "_id") delete(in, "txn-revno") delete(in, "txn-queue") replaceKeys(in, unescapeReplacer.Replace) } // replaceKeys will modify the provided map in place by replacing keys with // their replacement if they have been modified. func replaceKeys(m map[string]interface{}, replace func(string) string) { for key, value := range m { if newKey := replace(key); newKey != key { delete(m, key) m[newKey] = value } } return } // copyMap copies the keys and values of one map into a new one. If replace // is non-nil, for each old key k, the new key will be replace(k). func copyMap(in map[string]interface{}, replace func(string) string) (out map[string]interface{}) { out = make(map[string]interface{}) for key, value := range in { if replace != nil { key = replace(key) } out[key] = value } return } // Read (re)reads the node data into c. func (c *Settings) Read() error { config, txnRevno, err := readSettingsDoc(c.st, c.key) if err == mgo.ErrNotFound { c.disk = nil c.core = make(map[string]interface{}) return errors.NotFoundf("settings") } if err != nil { return fmt.Errorf("cannot read settings: %v", err) } c.txnRevno = txnRevno c.disk = config c.core = copyMap(config, nil) return nil } // readSettingsDoc reads the settings with the given // key. It returns the settings and the current rxnRevno. func readSettingsDoc(st *State, key string) (map[string]interface{}, int64, error) { config := map[string]interface{}{} err := st.settings.FindId(key).One(config) if err != nil { return nil, 0, err } txnRevno := config["txn-revno"].(int64) cleanSettingsMap(config) return config, txnRevno, nil } // readSettings returns the Settings for key. func readSettings(st *State, key string) (*Settings, error) { s := newSettings(st, key) if err := s.Read(); err != nil { return nil, err } return s, nil } var errSettingsExist = fmt.Errorf("cannot overwrite existing settings") func createSettingsOp(st *State, key string, values map[string]interface{}) txn.Op { newValues := copyMap(values, escapeReplacer.Replace) return txn.Op{ C: st.settings.Name, Id: key, Assert: txn.DocMissing, Insert: newValues, } } // createSettings writes an initial config node. func createSettings(st *State, key string, values map[string]interface{}) (*Settings, error) { s := newSettings(st, key) s.core = copyMap(values, nil) ops := []txn.Op{createSettingsOp(st, key, values)} err := s.st.runTransaction(ops) if err == txn.ErrAborted { return nil, errSettingsExist } if err != nil { return nil, fmt.Errorf("cannot create settings: %v", err) } return s, nil } // removeSettings removes the Settings for key. func removeSettings(st *State, key string) error { err := st.settings.RemoveId(key) if err == mgo.ErrNotFound { return errors.NotFoundf("settings") } return nil } // replaceSettingsOp returns a txn.Op that deletes the document's contents and // replaces it with the supplied values, and a function that should be called on // txn failure to determine whether this operation failed (due to a concurrent // settings change). func replaceSettingsOp(st *State, key string, values map[string]interface{}) (txn.Op, func() (bool, error), error) { s, err := readSettings(st, key) if err != nil { return txn.Op{}, nil, err } deletes := map[string]int{} for k := range s.disk { if _, found := values[k]; !found { deletes[escapeReplacer.Replace(k)] = 1 } } newValues := copyMap(values, escapeReplacer.Replace) op := s.assertUnchangedOp() op.Update = bson.D{ {"$set", newValues}, {"$unset", deletes}, } assertFailed := func() (bool, error) { latest, err := readSettings(st, key) if err != nil { return false, err } return latest.txnRevno != s.txnRevno, nil } return op, assertFailed, nil } func (s *Settings) assertUnchangedOp() txn.Op { return txn.Op{ C: s.st.settings.Name, Id: s.key, Assert: bson.D{{"txn-revno", s.txnRevno}}, } } �������juju-core_1.18.1/src/launchpad.net/juju-core/state/service.go���������������������������������������0000644�0000153�0000161�00000066271�12321735776�024020� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( stderrors "errors" "fmt" "sort" "strconv" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/utils" ) // Service represents the state of a service. type Service struct { st *State doc serviceDoc annotator } // serviceDoc represents the internal state of a service in MongoDB. // Note the correspondence with ServiceInfo in state/api/params. type serviceDoc struct { Name string `bson:"_id"` Series string Subordinate bool CharmURL *charm.URL ForceCharm bool Life Life UnitSeq int UnitCount int RelationCount int Exposed bool MinUnits int OwnerTag string TxnRevno int64 `bson:"txn-revno"` } func newService(st *State, doc *serviceDoc) *Service { svc := &Service{ st: st, doc: *doc, } svc.annotator = annotator{ globalKey: svc.globalKey(), tag: svc.Tag(), st: st, } return svc } // Name returns the service name. func (s *Service) Name() string { return s.doc.Name } // Tag returns a name identifying the service that is safe to use // as a file name. The returned name will be different from other // Tag values returned by any other entities from the same state. func (s *Service) Tag() string { return names.ServiceTag(s.Name()) } // serviceGlobalKey returns the global database key for the service // with the given name. func serviceGlobalKey(svcName string) string { return "s#" + svcName } // globalKey returns the global database key for the service. func (s *Service) globalKey() string { return serviceGlobalKey(s.doc.Name) } func serviceSettingsKey(serviceName string, curl *charm.URL) string { return fmt.Sprintf("s#%s#%s", serviceName, curl) } // settingsKey returns the charm-version-specific settings collection // key for the service. func (s *Service) settingsKey() string { return serviceSettingsKey(s.doc.Name, s.doc.CharmURL) } // Life returns whether the service is Alive, Dying or Dead. func (s *Service) Life() Life { return s.doc.Life } var errRefresh = stderrors.New("state seems inconsistent, refresh and try again") // Destroy ensures that the service and all its relations will be removed at // some point; if the service has no units, and no relation involving the // service has any units in scope, they are all removed immediately. func (s *Service) Destroy() (err error) { defer utils.ErrorContextf(&err, "cannot destroy service %q", s) defer func() { if err == nil { // This is a white lie; the document might actually be removed. s.doc.Life = Dying } }() svc := &Service{st: s.st, doc: s.doc} for i := 0; i < 5; i++ { switch ops, err := svc.destroyOps(); err { case errRefresh: case errAlreadyDying: return nil case nil: if err := svc.st.runTransaction(ops); err != txn.ErrAborted { return err } default: return err } if err := svc.Refresh(); errors.IsNotFoundError(err) { return nil } else if err != nil { return err } } return ErrExcessiveContention } // destroyOps returns the operations required to destroy the service. If it // returns errRefresh, the service should be refreshed and the destruction // operations recalculated. func (s *Service) destroyOps() ([]txn.Op, error) { if s.doc.Life == Dying { return nil, errAlreadyDying } rels, err := s.Relations() if err != nil { return nil, err } if len(rels) != s.doc.RelationCount { // This is just an early bail out. The relations obtained may still // be wrong, but that situation will be caught by a combination of // asserts on relationcount and on each known relation, below. return nil, errRefresh } ops := []txn.Op{minUnitsRemoveOp(s.st, s.doc.Name)} removeCount := 0 for _, rel := range rels { relOps, isRemove, err := rel.destroyOps(s.doc.Name) if err == errAlreadyDying { relOps = []txn.Op{{ C: s.st.relations.Name, Id: rel.doc.Key, Assert: bson.D{{"life", Dying}}, }} } else if err != nil { return nil, err } if isRemove { removeCount++ } ops = append(ops, relOps...) } // If the service has no units, and all its known relations will be // removed, the service can also be removed. if s.doc.UnitCount == 0 && s.doc.RelationCount == removeCount { hasLastRefs := bson.D{{"life", Alive}, {"unitcount", 0}, {"relationcount", removeCount}} return append(ops, s.removeOps(hasLastRefs)...), nil } // In all other cases, service removal will be handled as a consequence // of the removal of the last unit or relation referencing it. If any // relations have been removed, they'll be caught by the operations // collected above; but if any has been added, we need to abort and add // a destroy op for that relation too. In combination, it's enough to // check for count equality: an add/remove will not touch the count, but // will be caught by virtue of being a remove. notLastRefs := bson.D{ {"life", Alive}, {"relationcount", s.doc.RelationCount}, } // With respect to unit count, a changing value doesn't matter, so long // as the count's equality with zero does not change, because all we care // about is that *some* unit is, or is not, keeping the service from // being removed: the difference between 1 unit and 1000 is irrelevant. if s.doc.UnitCount > 0 { ops = append(ops, s.st.newCleanupOp("units", s.doc.Name+"/")) notLastRefs = append(notLastRefs, bson.D{{"unitcount", bson.D{{"$gt", 0}}}}...) } else { notLastRefs = append(notLastRefs, bson.D{{"unitcount", 0}}...) } update := bson.D{{"$set", bson.D{{"life", Dying}}}} if removeCount != 0 { decref := bson.D{{"$inc", bson.D{{"relationcount", -removeCount}}}} update = append(update, decref...) } return append(ops, txn.Op{ C: s.st.services.Name, Id: s.doc.Name, Assert: notLastRefs, Update: update, }), nil } // removeOps returns the operations required to remove the service. Supplied // asserts will be included in the operation on the service document. func (s *Service) removeOps(asserts bson.D) []txn.Op { ops := []txn.Op{{ C: s.st.services.Name, Id: s.doc.Name, Assert: asserts, Remove: true, }, { C: s.st.settingsrefs.Name, Id: s.settingsKey(), Remove: true, }, { C: s.st.settings.Name, Id: s.settingsKey(), Remove: true, }} ops = append(ops, removeNetworksOp(s.st, s.globalKey())) ops = append(ops, removeConstraintsOp(s.st, s.globalKey())) return append(ops, annotationRemoveOp(s.st, s.globalKey())) } // IsExposed returns whether this service is exposed. The explicitly open // ports (with open-port) for exposed services may be accessed from machines // outside of the local deployment network. See SetExposed and ClearExposed. func (s *Service) IsExposed() bool { return s.doc.Exposed } // SetExposed marks the service as exposed. // See ClearExposed and IsExposed. func (s *Service) SetExposed() error { return s.setExposed(true) } // ClearExposed removes the exposed flag from the service. // See SetExposed and IsExposed. func (s *Service) ClearExposed() error { return s.setExposed(false) } func (s *Service) setExposed(exposed bool) (err error) { ops := []txn.Op{{ C: s.st.services.Name, Id: s.doc.Name, Assert: isAliveDoc, Update: bson.D{{"$set", bson.D{{"exposed", exposed}}}}, }} if err := s.st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set exposed flag for service %q to %v: %v", s, exposed, onAbort(err, errNotAlive)) } s.doc.Exposed = exposed return nil } // Charm returns the service's charm and whether units should upgrade to that // charm even if they are in an error state. func (s *Service) Charm() (ch *Charm, force bool, err error) { ch, err = s.st.Charm(s.doc.CharmURL) if err != nil { return nil, false, err } return ch, s.doc.ForceCharm, nil } // IsPrincipal returns whether units of the service can // have subordinate units. func (s *Service) IsPrincipal() bool { return !s.doc.Subordinate } // CharmURL returns the service's charm URL, and whether units should upgrade // to the charm with that URL even if they are in an error state. func (s *Service) CharmURL() (curl *charm.URL, force bool) { return s.doc.CharmURL, s.doc.ForceCharm } // Endpoints returns the service's currently available relation endpoints. func (s *Service) Endpoints() (eps []Endpoint, err error) { ch, _, err := s.Charm() if err != nil { return nil, err } collect := func(role charm.RelationRole, rels map[string]charm.Relation) { for _, rel := range rels { eps = append(eps, Endpoint{ ServiceName: s.doc.Name, Relation: rel, }) } } meta := ch.Meta() collect(charm.RolePeer, meta.Peers) collect(charm.RoleProvider, meta.Provides) collect(charm.RoleRequirer, meta.Requires) collect(charm.RoleProvider, map[string]charm.Relation{ "juju-info": { Name: "juju-info", Role: charm.RoleProvider, Interface: "juju-info", Scope: charm.ScopeGlobal, }, }) sort.Sort(epSlice(eps)) return eps, nil } // Endpoint returns the relation endpoint with the supplied name, if it exists. func (s *Service) Endpoint(relationName string) (Endpoint, error) { eps, err := s.Endpoints() if err != nil { return Endpoint{}, err } for _, ep := range eps { if ep.Name == relationName { return ep, nil } } return Endpoint{}, fmt.Errorf("service %q has no %q relation", s, relationName) } // extraPeerRelations returns only the peer relations in newMeta not // present in the service's current charm meta data. func (s *Service) extraPeerRelations(newMeta *charm.Meta) map[string]charm.Relation { if newMeta == nil { // This should never happen, since we're checking the charm in SetCharm already. panic("newMeta is nil") } ch, _, err := s.Charm() if err != nil { return nil } newPeers := newMeta.Peers oldPeers := ch.Meta().Peers extraPeers := make(map[string]charm.Relation) for relName, rel := range newPeers { if _, ok := oldPeers[relName]; !ok { extraPeers[relName] = rel } } return extraPeers } func (s *Service) checkRelationsOps(ch *Charm, relations []*Relation) ([]txn.Op, error) { asserts := make([]txn.Op, 0, len(relations)) // All relations must still exist and their endpoints are implemented by the charm. for _, rel := range relations { if ep, err := rel.Endpoint(s.doc.Name); err != nil { return nil, err } else if !ep.ImplementedBy(ch) { return nil, fmt.Errorf("cannot upgrade service %q to charm %q: would break relation %q", s, ch, rel) } asserts = append(asserts, txn.Op{ C: s.st.relations.Name, Id: rel.doc.Key, Assert: txn.DocExists, }) } return asserts, nil } // changeCharmOps returns the operations necessary to set a service's // charm URL to a new value. func (s *Service) changeCharmOps(ch *Charm, force bool) ([]txn.Op, error) { // Build the new service config from what can be used of the old one. oldSettings, err := readSettings(s.st, s.settingsKey()) if err != nil { return nil, err } newSettings := ch.Config().FilterSettings(oldSettings.Map()) // Create or replace service settings. var settingsOp txn.Op newKey := serviceSettingsKey(s.doc.Name, ch.URL()) if count, err := s.st.settings.FindId(newKey).Count(); err != nil { return nil, err } else if count == 0 { // No settings for this key yet, create it. settingsOp = createSettingsOp(s.st, newKey, newSettings) } else { // Settings exist, just replace them with the new ones. var err error settingsOp, _, err = replaceSettingsOp(s.st, newKey, newSettings) if err != nil { return nil, err } } // Add or create a reference to the new settings doc. incOp, err := settingsIncRefOp(s.st, s.doc.Name, ch.URL(), true) if err != nil { return nil, err } // Drop the reference to the old settings doc. decOps, err := settingsDecRefOps(s.st, s.doc.Name, s.doc.CharmURL) // current charm if err != nil { return nil, err } // Build the transaction. differentCharm := bson.D{{"charmurl", bson.D{{"$ne", ch.URL()}}}} ops := []txn.Op{ // Old settings shouldn't change oldSettings.assertUnchangedOp(), // Create/replace with new settings. settingsOp, // Increment the ref count. incOp, // Update the charm URL and force flag (if relevant). { C: s.st.services.Name, Id: s.doc.Name, Assert: append(isAliveDoc, differentCharm...), Update: bson.D{{"$set", bson.D{{"charmurl", ch.URL()}, {"forcecharm", force}}}}, }, } // Add any extra peer relations that need creation. newPeers := s.extraPeerRelations(ch.Meta()) peerOps, err := s.st.addPeerRelationsOps(s.doc.Name, newPeers) if err != nil { return nil, err } // Get all relations - we need to check them later. relations, err := s.Relations() if err != nil { return nil, err } // Make sure the relation count does not change. sameRelCount := bson.D{{"relationcount", len(relations)}} ops = append(ops, peerOps...) // Update the relation count as well. ops = append(ops, txn.Op{ C: s.st.services.Name, Id: s.doc.Name, Assert: append(isAliveDoc, sameRelCount...), Update: bson.D{{"$inc", bson.D{{"relationcount", len(newPeers)}}}}, }) // Check relations to ensure no active relations are removed. relOps, err := s.checkRelationsOps(ch, relations) if err != nil { return nil, err } ops = append(ops, relOps...) // And finally, decrement the old settings. return append(ops, decOps...), nil } // SetCharm changes the charm for the service. New units will be started with // this charm, and existing units will be upgraded to use it. If force is true, // units will be upgraded even if they are in an error state. func (s *Service) SetCharm(ch *Charm, force bool) (err error) { if ch.Meta().Subordinate != s.doc.Subordinate { return fmt.Errorf("cannot change a service's subordinacy") } if ch.URL().Series != s.doc.Series { return fmt.Errorf("cannot change a service's series") } for i := 0; i < 5; i++ { var ops []txn.Op // Make sure the service doesn't have this charm already. sel := bson.D{{"_id", s.doc.Name}, {"charmurl", ch.URL()}} if count, err := s.st.services.Find(sel).Count(); err != nil { return err } else if count == 1 { // Charm URL already set; just update the force flag. sameCharm := bson.D{{"charmurl", ch.URL()}} ops = []txn.Op{{ C: s.st.services.Name, Id: s.doc.Name, Assert: append(isAliveDoc, sameCharm...), Update: bson.D{{"$set", bson.D{{"forcecharm", force}}}}, }} } else { // Change the charm URL. ops, err = s.changeCharmOps(ch, force) if err != nil { return err } } if err := s.st.runTransaction(ops); err == nil { s.doc.CharmURL = ch.URL() s.doc.ForceCharm = force return nil } else if err != txn.ErrAborted { return err } // If the service is not alive, fail out immediately; otherwise, // data changed underneath us, so retry. if alive, err := isAlive(s.st.services, s.doc.Name); err != nil { return err } else if !alive { return fmt.Errorf("service %q is not alive", s.doc.Name) } } return ErrExcessiveContention } // String returns the service name. func (s *Service) String() string { return s.doc.Name } // Refresh refreshes the contents of the Service from the underlying // state. It returns an error that satisfies IsNotFound if the service has // been removed. func (s *Service) Refresh() error { err := s.st.services.FindId(s.doc.Name).One(&s.doc) if err == mgo.ErrNotFound { return errors.NotFoundf("service %q", s) } if err != nil { return fmt.Errorf("cannot refresh service %q: %v", s, err) } return nil } // newUnitName returns the next unit name. func (s *Service) newUnitName() (string, error) { change := mgo.Change{Update: bson.D{{"$inc", bson.D{{"unitseq", 1}}}}} result := serviceDoc{} if _, err := s.st.services.Find(bson.D{{"_id", s.doc.Name}}).Apply(change, &result); err == mgo.ErrNotFound { return "", errors.NotFoundf("service %q", s) } else if err != nil { return "", fmt.Errorf("cannot increment unit sequence: %v", err) } name := s.doc.Name + "/" + strconv.Itoa(result.UnitSeq) return name, nil } // addUnitOps returns a unique name for a new unit, and a list of txn operations // necessary to create that unit. The principalName param must be non-empty if // and only if s is a subordinate service. Only one subordinate of a given // service will be assigned to a given principal. The asserts param can be used // to include additional assertions for the service document. func (s *Service) addUnitOps(principalName string, asserts bson.D) (string, []txn.Op, error) { if s.doc.Subordinate && principalName == "" { return "", nil, fmt.Errorf("service is a subordinate") } else if !s.doc.Subordinate && principalName != "" { return "", nil, fmt.Errorf("service is not a subordinate") } name, err := s.newUnitName() if err != nil { return "", nil, err } globalKey := unitGlobalKey(name) udoc := &unitDoc{ Name: name, Service: s.doc.Name, Series: s.doc.Series, Life: Alive, Principal: principalName, } sdoc := statusDoc{ Status: params.StatusPending, } ops := []txn.Op{ { C: s.st.units.Name, Id: name, Assert: txn.DocMissing, Insert: udoc, }, createStatusOp(s.st, globalKey, sdoc), { C: s.st.services.Name, Id: s.doc.Name, Assert: append(isAliveDoc, asserts...), Update: bson.D{{"$inc", bson.D{{"unitcount", 1}}}}, }} if s.doc.Subordinate { ops = append(ops, txn.Op{ C: s.st.units.Name, Id: principalName, Assert: append(isAliveDoc, bson.DocElem{ "subordinates", bson.D{{"$not", bson.RegEx{Pattern: "^" + s.doc.Name + "/"}}}, }), Update: bson.D{{"$addToSet", bson.D{{"subordinates", name}}}}, }) } else { scons, err := s.Constraints() if err != nil { return "", nil, err } econs, err := s.st.EnvironConstraints() if err != nil { return "", nil, err } cons := scons.WithFallbacks(econs) ops = append(ops, createConstraintsOp(s.st, globalKey, cons)) } return name, ops, nil } // GetOwnerTag returns the owner of this service // SCHEMACHANGE // TODO(mattyw) remove when schema upgrades are possible func (s *serviceDoc) GetOwnerTag() string { if s.OwnerTag != "" { return s.OwnerTag } return "user-admin" } // SCHEMACHANGE // TODO(mattyw) remove when schema upgrades are possible func (s *Service) GetOwnerTag() string { return s.doc.GetOwnerTag() } // AddUnit adds a new principal unit to the service. func (s *Service) AddUnit() (unit *Unit, err error) { defer utils.ErrorContextf(&err, "cannot add unit to service %q", s) name, ops, err := s.addUnitOps("", nil) if err != nil { return nil, err } if err := s.st.runTransaction(ops); err == txn.ErrAborted { if alive, err := isAlive(s.st.services, s.doc.Name); err != nil { return nil, err } else if !alive { return nil, fmt.Errorf("service is not alive") } return nil, fmt.Errorf("inconsistent state") } else if err != nil { return nil, err } return s.Unit(name) } var ErrExcessiveContention = stderrors.New("state changing too quickly; try again soon") // removeUnitOps returns the operations necessary to remove the supplied unit, // assuming the supplied asserts apply to the unit document. func (s *Service) removeUnitOps(u *Unit, asserts bson.D) ([]txn.Op, error) { var ops []txn.Op if s.doc.Subordinate { ops = append(ops, txn.Op{ C: s.st.units.Name, Id: u.doc.Principal, Assert: txn.DocExists, Update: bson.D{{"$pull", bson.D{{"subordinates", u.doc.Name}}}}, }) } else if u.doc.MachineId != "" { ops = append(ops, txn.Op{ C: s.st.machines.Name, Id: u.doc.MachineId, Assert: txn.DocExists, Update: bson.D{{"$pull", bson.D{{"principals", u.doc.Name}}}}, }) } observedFieldsMatch := bson.D{ {"charmurl", u.doc.CharmURL}, {"machineid", u.doc.MachineId}, } ops = append(ops, txn.Op{ C: s.st.units.Name, Id: u.doc.Name, Assert: append(observedFieldsMatch, asserts...), Remove: true, }, removeConstraintsOp(s.st, u.globalKey()), removeStatusOp(s.st, u.globalKey()), annotationRemoveOp(s.st, u.globalKey()), ) if u.doc.CharmURL != nil { decOps, err := settingsDecRefOps(s.st, s.doc.Name, u.doc.CharmURL) if errors.IsNotFoundError(err) { return nil, errRefresh } else if err != nil { return nil, err } ops = append(ops, decOps...) } if s.doc.Life == Dying && s.doc.RelationCount == 0 && s.doc.UnitCount == 1 { hasLastRef := bson.D{{"life", Dying}, {"relationcount", 0}, {"unitcount", 1}} return append(ops, s.removeOps(hasLastRef)...), nil } svcOp := txn.Op{ C: s.st.services.Name, Id: s.doc.Name, Update: bson.D{{"$inc", bson.D{{"unitcount", -1}}}}, } if s.doc.Life == Alive { svcOp.Assert = bson.D{{"life", Alive}, {"unitcount", bson.D{{"$gt", 0}}}} } else { svcOp.Assert = bson.D{ {"life", Dying}, {"$or", []bson.D{ {{"unitcount", bson.D{{"$gt", 1}}}}, {{"relationcount", bson.D{{"$gt", 0}}}}, }}, } } return append(ops, svcOp), nil } // Unit returns the service's unit with name. func (s *Service) Unit(name string) (*Unit, error) { if !names.IsUnit(name) { return nil, fmt.Errorf("%q is not a valid unit name", name) } udoc := &unitDoc{} sel := bson.D{{"_id", name}, {"service", s.doc.Name}} if err := s.st.units.Find(sel).One(udoc); err != nil { return nil, fmt.Errorf("cannot get unit %q from service %q: %v", name, s.doc.Name, err) } return newUnit(s.st, udoc), nil } // AllUnits returns all units of the service. func (s *Service) AllUnits() (units []*Unit, err error) { docs := []unitDoc{} err = s.st.units.Find(bson.D{{"service", s.doc.Name}}).All(&docs) if err != nil { return nil, fmt.Errorf("cannot get all units from service %q: %v", s, err) } for i := range docs { units = append(units, newUnit(s.st, &docs[i])) } return units, nil } // Relations returns a Relation for every relation the service is in. func (s *Service) Relations() (relations []*Relation, err error) { return serviceRelations(s.st, s.doc.Name) } func serviceRelations(st *State, name string) (relations []*Relation, err error) { defer utils.ErrorContextf(&err, "can't get relations for service %q", name) docs := []relationDoc{} err = st.relations.Find(bson.D{{"endpoints.servicename", name}}).All(&docs) if err != nil { return nil, err } for _, v := range docs { relations = append(relations, newRelation(st, &v)) } return relations, nil } // ConfigSettings returns the raw user configuration for the service's charm. // Unset values are omitted. func (s *Service) ConfigSettings() (charm.Settings, error) { settings, err := readSettings(s.st, s.settingsKey()) if err != nil { return nil, err } return settings.Map(), nil } // UpdateConfigSettings changes a service's charm config settings. Values set // to nil will be deleted; unknown and invalid values will return an error. func (s *Service) UpdateConfigSettings(changes charm.Settings) error { charm, _, err := s.Charm() if err != nil { return err } changes, err = charm.Config().ValidateSettings(changes) if err != nil { return err } // TODO(fwereade) state.Settings is itself really problematic in just // about every use case. This needs to be resolved some time; but at // least the settings docs are keyed by charm url as well as service // name, so the actual impact of a race is non-threatening. node, err := readSettings(s.st, s.settingsKey()) if err != nil { return err } for name, value := range changes { if value == nil { node.Delete(name) } else { node.Set(name, value) } } _, err = node.Write() return err } var ErrSubordinateConstraints = stderrors.New("constraints do not apply to subordinate services") // Constraints returns the current service constraints. func (s *Service) Constraints() (constraints.Value, error) { if s.doc.Subordinate { return constraints.Value{}, ErrSubordinateConstraints } return readConstraints(s.st, s.globalKey()) } // SetConstraints replaces the current service constraints. func (s *Service) SetConstraints(cons constraints.Value) (err error) { if s.doc.Subordinate { return ErrSubordinateConstraints } defer utils.ErrorContextf(&err, "cannot set constraints") if s.doc.Life != Alive { return errNotAlive } ops := []txn.Op{ { C: s.st.services.Name, Id: s.doc.Name, Assert: isAliveDoc, }, setConstraintsOp(s.st, s.globalKey(), cons), } return onAbort(s.st.runTransaction(ops), errNotAlive) } // Networks returns the networks a service is associated with. func (s *Service) Networks() (includeNetworks, excludeNetworks []string, err error) { return readNetworks(s.st, s.globalKey()) } // settingsIncRefOp returns an operation that increments the ref count // of the service settings identified by serviceName and curl. If // canCreate is false, a missing document will be treated as an error; // otherwise, it will be created with a ref count of 1. func settingsIncRefOp(st *State, serviceName string, curl *charm.URL, canCreate bool) (txn.Op, error) { key := serviceSettingsKey(serviceName, curl) if count, err := st.settingsrefs.FindId(key).Count(); err != nil { return txn.Op{}, err } else if count == 0 { if !canCreate { return txn.Op{}, errors.NotFoundf("service settings") } return txn.Op{ C: st.settingsrefs.Name, Id: key, Assert: txn.DocMissing, Insert: settingsRefsDoc{1}, }, nil } return txn.Op{ C: st.settingsrefs.Name, Id: key, Assert: txn.DocExists, Update: bson.D{{"$inc", bson.D{{"refcount", 1}}}}, }, nil } // settingsDecRefOps returns a list of operations that decrement the // ref count of the service settings identified by serviceName and // curl. If the ref count is set to zero, the appropriate setting and // ref count documents will both be deleted. func settingsDecRefOps(st *State, serviceName string, curl *charm.URL) ([]txn.Op, error) { key := serviceSettingsKey(serviceName, curl) var doc settingsRefsDoc if err := st.settingsrefs.FindId(key).One(&doc); err == mgo.ErrNotFound { return nil, errors.NotFoundf("service %q settings for charm %q", serviceName, curl) } else if err != nil { return nil, err } if doc.RefCount == 1 { return []txn.Op{{ C: st.settingsrefs.Name, Id: key, Assert: bson.D{{"refcount", 1}}, Remove: true, }, { C: st.settings.Name, Id: key, Remove: true, }}, nil } return []txn.Op{{ C: st.settingsrefs.Name, Id: key, Assert: bson.D{{"refcount", bson.D{{"$gt", 1}}}}, Update: bson.D{{"$inc", bson.D{{"refcount", -1}}}}, }}, nil } // settingsRefsDoc holds the number of units and services using the // settings document identified by the document's id. Every time a // service upgrades its charm the settings doc ref count for the new // charm url is incremented, and the old settings is ref count is // decremented. When a unit upgrades to the new charm, the old service // settings ref count is decremented and the ref count of the new // charm settings is incremented. The last unit upgrading to the new // charm is responsible for deleting the old charm's settings doc. // // Note: We're not using the settingsDoc for this because changing // just the ref count is not considered a change worth reporting // to watchers and firing config-changed hooks. // // There is and implicit _id field here, which mongo creates, which is // always the same as the settingsDoc's id. type settingsRefsDoc struct { RefCount int } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/conn_test.go�������������������������������������0000644�0000153�0000161�00000007606�12321735776�024351� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( stdtesting "testing" "labix.org/v2/mgo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) // TestPackage integrates the tests into gotest. func TestPackage(t *stdtesting.T) { testing.MgoTestPackage(t) } // ConnSuite provides the infrastructure for all other // test suites (StateSuite, CharmSuite, MachineSuite, etc). type ConnSuite struct { testing.MgoSuite testbase.LoggingSuite annotations *mgo.Collection charms *mgo.Collection machines *mgo.Collection relations *mgo.Collection services *mgo.Collection units *mgo.Collection stateServers *mgo.Collection State *state.State policy mockPolicy } func (cs *ConnSuite) SetUpSuite(c *gc.C) { cs.LoggingSuite.SetUpSuite(c) cs.MgoSuite.SetUpSuite(c) } func (cs *ConnSuite) TearDownSuite(c *gc.C) { cs.MgoSuite.TearDownSuite(c) cs.LoggingSuite.TearDownSuite(c) } func (cs *ConnSuite) SetUpTest(c *gc.C) { cs.LoggingSuite.SetUpTest(c) cs.MgoSuite.SetUpTest(c) cs.policy = mockPolicy{} cs.State = state.TestingInitialize(c, nil, &cs.policy) cs.annotations = cs.MgoSuite.Session.DB("juju").C("annotations") cs.charms = cs.MgoSuite.Session.DB("juju").C("charms") cs.machines = cs.MgoSuite.Session.DB("juju").C("machines") cs.relations = cs.MgoSuite.Session.DB("juju").C("relations") cs.services = cs.MgoSuite.Session.DB("juju").C("services") cs.units = cs.MgoSuite.Session.DB("juju").C("units") cs.stateServers = cs.MgoSuite.Session.DB("juju").C("stateServers") cs.State.AddUser(state.AdminUser, "pass") } func (cs *ConnSuite) TearDownTest(c *gc.C) { if cs.State != nil { // If setup fails, we don't have a State yet cs.State.Close() } cs.MgoSuite.TearDownTest(c) cs.LoggingSuite.TearDownTest(c) } func (s *ConnSuite) AddTestingCharm(c *gc.C, name string) *state.Charm { return state.AddTestingCharm(c, s.State, name) } func (s *ConnSuite) AddTestingService(c *gc.C, name string, ch *state.Charm) *state.Service { return state.AddTestingService(c, s.State, name, ch) } func (s *ConnSuite) AddTestingServiceWithNetworks(c *gc.C, name string, ch *state.Charm, includeNetworks, excludeNetworks []string) *state.Service { return state.AddTestingServiceWithNetworks(c, s.State, name, ch, includeNetworks, excludeNetworks) } func (s *ConnSuite) AddSeriesCharm(c *gc.C, name, series string) *state.Charm { return state.AddCustomCharm(c, s.State, name, "", "", series, -1) } // AddConfigCharm clones a testing charm, replaces its config with // the given YAML string and adds it to the state, using the given // revision. func (s *ConnSuite) AddConfigCharm(c *gc.C, name, configYaml string, revision int) *state.Charm { return state.AddCustomCharm(c, s.State, name, "config.yaml", configYaml, "quantal", revision) } // AddMetaCharm clones a testing charm, replaces its metadata with the // given YAML string and adds it to the state, using the given revision. func (s *ConnSuite) AddMetaCharm(c *gc.C, name, metaYaml string, revsion int) *state.Charm { return state.AddCustomCharm(c, s.State, name, "metadata.yaml", metaYaml, "quantal", revsion) } type mockPolicy struct { getPrechecker func(*config.Config) (state.Prechecker, error) getConfigValidator func(string) (state.ConfigValidator, error) } func (p *mockPolicy) Prechecker(cfg *config.Config) (state.Prechecker, error) { if p.getPrechecker != nil { return p.getPrechecker(cfg) } return nil, errors.NewNotImplementedError("Prechecker") } func (p *mockPolicy) ConfigValidator(providerType string) (state.ConfigValidator, error) { if p.getConfigValidator != nil { return p.getConfigValidator(providerType) } return nil, errors.NewNotImplementedError("ConfigValidator") } ��������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/minimumunits.go����������������������������������0000644�0000153�0000161�00000014223�12321735642�025074� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "errors" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/utils" ) // minUnitsDoc keeps track of relevant changes on the service's MinUnits field // and on the number of alive units for the service. // A new document is created when MinUnits is set to a non zero value. // A document is deleted when either the associated service is destroyed // or MinUnits is restored to zero. The Revno is increased when either MinUnits // for a service is increased or a unit is destroyed. // TODO(frankban): the MinUnitsWatcher reacts to changes by sending events, // each one describing one or more services. A worker reacts to those events // ensuring the number of units for the service is never less than the actual // alive units: new units are added if required. type minUnitsDoc struct { // ServiceName is safe to be used here in place of its globalKey, since // the referred entity type is always the Service. ServiceName string `bson:"_id"` Revno int } // SetMinUnits changes the number of minimum units required by the service. func (s *Service) SetMinUnits(minUnits int) (err error) { defer utils.ErrorContextf(&err, "cannot set minimum units for service %q", s) defer func() { if err == nil { s.doc.MinUnits = minUnits } }() if minUnits < 0 { return errors.New("cannot set a negative minimum number of units") } service := &Service{st: s.st, doc: s.doc} // Removing the document never fails. Racing clients trying to create the // document generate one failure, but the second attempt should succeed. // If one client tries to update the document, and a racing client removes // it, the former should be able to re-create the document in the second // attempt. If the referred-to service advanced its life cycle to a not // alive state, an error is returned after the first failing attempt. for i := 0; i < 2; i++ { if service.doc.Life != Alive { return errors.New("service is no longer alive") } if minUnits == service.doc.MinUnits { return nil } ops := setMinUnitsOps(service, minUnits) if err := s.st.runTransaction(ops); err != txn.ErrAborted { return err } if err := service.Refresh(); err != nil { return err } } return ErrExcessiveContention } // setMinUnitsOps returns the operations required to set MinUnits on the // service and to create/update/remove the minUnits document in MongoDB. func setMinUnitsOps(service *Service, minUnits int) []txn.Op { state := service.st serviceName := service.Name() ops := []txn.Op{{ C: state.services.Name, Id: serviceName, Assert: isAliveDoc, Update: bson.D{{"$set", bson.D{{"minunits", minUnits}}}}, }} if service.doc.MinUnits == 0 { return append(ops, txn.Op{ C: state.minUnits.Name, Id: serviceName, Assert: txn.DocMissing, Insert: &minUnitsDoc{ServiceName: serviceName}, }) } if minUnits == 0 { return append(ops, minUnitsRemoveOp(state, serviceName)) } if minUnits > service.doc.MinUnits { op := minUnitsTriggerOp(state, serviceName) op.Assert = txn.DocExists return append(ops, op) } return ops } // minUnitsTriggerOp returns the operation required to increase the minimum // units revno for the service in MongoDB, ignoring the case of document not // existing. This is included in the operations performed when a unit is // destroyed: if the document exists, then we need to update the Revno. // If the service does not require a minimum number of units, then the // operation is a noop. func minUnitsTriggerOp(st *State, serviceName string) txn.Op { return txn.Op{ C: st.minUnits.Name, Id: serviceName, Update: bson.D{{"$inc", bson.D{{"revno", 1}}}}, } } // minUnitsRemoveOp returns the operation required to remove the minimum // units document from MongoDB. func minUnitsRemoveOp(st *State, serviceName string) txn.Op { return txn.Op{ C: st.minUnits.Name, Id: serviceName, Remove: true, } } // MinUnits returns the minimum units count for the service. func (s *Service) MinUnits() int { return s.doc.MinUnits } // EnsureMinUnits adds new units if the service's MinUnits value is greater // than the number of alive units. func (s *Service) EnsureMinUnits() (err error) { defer utils.ErrorContextf(&err, "cannot ensure minimum units for service %q", s) service := &Service{st: s.st, doc: s.doc} for { // Ensure the service is alive. if service.doc.Life != Alive { return errors.New("service is not alive") } // Exit without errors if the MinUnits for the service is not set. if service.doc.MinUnits == 0 { return nil } // Retrieve the number of alive units for the service. aliveUnits, err := aliveUnitsCount(service) if err != nil { return err } // Calculate the number of required units to be added. missing := service.doc.MinUnits - aliveUnits if missing <= 0 { return nil } name, ops, err := ensureMinUnitsOps(service) if err != nil { return err } // Add missing unit. switch err := s.st.runTransaction(ops); err { case nil: // Assign the new unit. unit, err := service.Unit(name) if err != nil { return err } if err := service.st.AssignUnit(unit, AssignNew); err != nil { return err } // No need to proceed and refresh the service if this was the // last/only missing unit. if missing == 1 { return nil } case txn.ErrAborted: // Refresh the service and restart the loop. default: return err } if err := service.Refresh(); err != nil { return err } } } // aliveUnitsCount returns the number a alive units for the service. func aliveUnitsCount(service *Service) (int, error) { query := bson.D{{"service", service.doc.Name}, {"life", Alive}} return service.st.units.Find(query).Count() } // ensureMinUnitsOps returns the operations required to add a unit for the // service in MongoDB and the name for the new unit. The resulting transaction // will be aborted if the service document changes when running the operations. func ensureMinUnitsOps(service *Service) (string, []txn.Op, error) { asserts := bson.D{{"txn-revno", service.doc.TxnRevno}} return service.addUnitOps("", asserts) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/watcher.go���������������������������������������0000644�0000153�0000161�00000111700�12321735776�024001� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "regexp" "strings" "time" "github.com/juju/loggo" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "launchpad.net/tomb" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/utils/set" ) var watchLogger = loggo.GetLogger("juju.state.watch") // Watcher is implemented by all watchers; the actual // changes channel is returned by a watcher-specific // Changes method. type Watcher interface { // Kill asks the watcher to stop without waiting for it do so. Kill() // Wait waits for the watcher to die and returns any // error encountered when it was running. Wait() error // Stop kills the watcher, then waits for it to die. Stop() error // Err returns any error encountered while the watcher // has been running. Err() error } // NotifyWatcher generates signals when something changes, but it does not // return any content for those changes type NotifyWatcher interface { Watcher Changes() <-chan struct{} } // StringsWatcher generates signals when something changes, returning // the changes as a list of strings. type StringsWatcher interface { Watcher Changes() <-chan []string } // RelationUnitsWatcher generates signals when units enter or leave // the scope of a RelationUnit, and changes to the settings of those // units known to have entered. type RelationUnitsWatcher interface { Watcher Changes() <-chan params.RelationUnitsChange } // commonWatcher is part of all client watchers. type commonWatcher struct { st *State tomb tomb.Tomb } // Stop stops the watcher, and returns any error encountered while running // or shutting down. func (w *commonWatcher) Stop() error { w.Kill() return w.Wait() } // Kill kills the watcher without waiting for it to shut down. func (w *commonWatcher) Kill() { w.tomb.Kill(nil) } // Wait waits for the watcher to die and returns any // error encountered when it was running. func (w *commonWatcher) Wait() error { return w.tomb.Wait() } // Err returns any error encountered while running or shutting down, or // tomb.ErrStillAlive if the watcher is still running. func (w *commonWatcher) Err() error { return w.tomb.Err() } // collect combines the effects of the one change, and any further changes read // from more in the next 10ms. The result map describes the existence, or not, // of every id observed to have changed. If a value is read from the supplied // stop chan, collect returns false immediately. func collect(one watcher.Change, more <-chan watcher.Change, stop <-chan struct{}) (map[interface{}]bool, bool) { var count int result := map[interface{}]bool{} handle := func(ch watcher.Change) { count++ result[ch.Id] = ch.Revno != -1 } handle(one) timeout := time.After(10 * time.Millisecond) for done := false; !done; { select { case <-stop: return nil, false case another := <-more: handle(another) case <-timeout: done = true } } watchLogger.Tracef("read %d events for %d documents", count, len(result)) return result, true } func hasString(changes []string, name string) bool { for _, v := range changes { if v == name { return true } } return false } var _ Watcher = (*lifecycleWatcher)(nil) // lifecycleWatcher notifies about lifecycle changes for a set of entities of // the same kind. The first event emitted will contain the ids of all non-Dead // entities; subsequent events are emitted whenever one or more entities are // added, or change their lifecycle state. After an entity is found to be // Dead, no further event will include it. type lifecycleWatcher struct { commonWatcher out chan []string // coll is the collection holding all interesting entities. coll *mgo.Collection // members is used to select the initial set of interesting entities. members bson.D // filter is used to exclude events not affecting interesting entities. filter func(interface{}) bool // life holds the most recent known life states of interesting entities. life map[string]Life } // WatchServices returns a StringsWatcher that notifies of changes to // the lifecycles of the services in the environment. func (st *State) WatchServices() StringsWatcher { return newLifecycleWatcher(st, st.services, nil, nil) } // WatchUnits returns a StringsWatcher that notifies of changes to the // lifecycles of units of s. func (s *Service) WatchUnits() StringsWatcher { members := bson.D{{"service", s.doc.Name}} prefix := s.doc.Name + "/" filter := func(id interface{}) bool { return strings.HasPrefix(id.(string), prefix) } return newLifecycleWatcher(s.st, s.st.units, members, filter) } // WatchRelations returns a StringsWatcher that notifies of changes to the // lifecycles of relations involving s. func (s *Service) WatchRelations() StringsWatcher { members := bson.D{{"endpoints.servicename", s.doc.Name}} prefix := s.doc.Name + ":" infix := " " + prefix filter := func(key interface{}) bool { k := key.(string) return strings.HasPrefix(k, prefix) || strings.Contains(k, infix) } return newLifecycleWatcher(s.st, s.st.relations, members, filter) } // WatchEnvironMachines returns a StringsWatcher that notifies of changes to // the lifecycles of the machines (but not containers) in the environment. func (st *State) WatchEnvironMachines() StringsWatcher { members := bson.D{{"$or", []bson.D{ {{"containertype", ""}}, {{"containertype", bson.D{{"$exists", false}}}}, }}} filter := func(id interface{}) bool { return !strings.Contains(id.(string), "/") } return newLifecycleWatcher(st, st.machines, members, filter) } // WatchContainers returns a StringsWatcher that notifies of changes to the // lifecycles of containers of the specified type on a machine. func (m *Machine) WatchContainers(ctype instance.ContainerType) StringsWatcher { isChild := fmt.Sprintf("^%s/%s/%s$", m.doc.Id, ctype, names.NumberSnippet) return m.containersWatcher(isChild) } // WatchAllContainers returns a StringsWatcher that notifies of changes to the // lifecycles of all containers on a machine. func (m *Machine) WatchAllContainers() StringsWatcher { isChild := fmt.Sprintf("^%s/%s/%s$", m.doc.Id, names.ContainerTypeSnippet, names.NumberSnippet) return m.containersWatcher(isChild) } func (m *Machine) containersWatcher(isChildRegexp string) StringsWatcher { members := bson.D{{"_id", bson.D{{"$regex", isChildRegexp}}}} compiled := regexp.MustCompile(isChildRegexp) filter := func(key interface{}) bool { return compiled.MatchString(key.(string)) } return newLifecycleWatcher(m.st, m.st.machines, members, filter) } func newLifecycleWatcher(st *State, coll *mgo.Collection, members bson.D, filter func(key interface{}) bool) StringsWatcher { w := &lifecycleWatcher{ commonWatcher: commonWatcher{st: st}, coll: coll, members: members, filter: filter, life: make(map[string]Life), out: make(chan []string), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop()) }() return w } type lifeDoc struct { Id string `bson:"_id"` Life Life } var lifeFields = bson.D{{"_id", 1}, {"life", 1}} // Changes returns the event channel for the LifecycleWatcher. func (w *lifecycleWatcher) Changes() <-chan []string { return w.out } func (w *lifecycleWatcher) initial() (ids *set.Strings, err error) { ids = &set.Strings{} var doc lifeDoc iter := w.coll.Find(w.members).Select(lifeFields).Iter() for iter.Next(&doc) { ids.Add(doc.Id) if doc.Life != Dead { w.life[doc.Id] = doc.Life } } if err := iter.Err(); err != nil { return nil, err } return ids, nil } func (w *lifecycleWatcher) merge(ids *set.Strings, updates map[interface{}]bool) error { // Separate ids into those thought to exist and those known to be removed. changed := []string{} latest := map[string]Life{} for id, exists := range updates { id := id.(string) if exists { changed = append(changed, id) } else { latest[id] = Dead } } // Collect life states from ids thought to exist. Any that don't actually // exist are ignored (we'll hear about them in the next set of updates -- // all that's actually happened in that situation is that the watcher // events have lagged a little behind reality). iter := w.coll.Find(bson.D{{"_id", bson.D{{"$in", changed}}}}).Select(lifeFields).Iter() var doc lifeDoc for iter.Next(&doc) { latest[doc.Id] = doc.Life } if err := iter.Err(); err != nil { return err } // Add to ids any whose life state is known to have changed. for id, newLife := range latest { gone := newLife == Dead oldLife, known := w.life[id] switch { case known && gone: delete(w.life, id) case !known && !gone: w.life[id] = newLife case known && newLife != oldLife: w.life[id] = newLife default: continue } ids.Add(id) } return nil } // ErrStateClosed is returned from watchers if their underlying // state connection has been closed. var ErrStateClosed = fmt.Errorf("state has been closed") // stateWatcherDeadError processes the error received when the watcher // inside a state connection dies. If the State has been closed, the // watcher will have been stopped and error will be nil, so we ensure // that higher level watchers return a non-nil error in that case, as // watchers are not expected to die unexpectedly without an error. func stateWatcherDeadError(err error) error { if err != nil { return err } return ErrStateClosed } func (w *lifecycleWatcher) loop() error { in := make(chan watcher.Change) w.st.watcher.WatchCollectionWithFilter(w.coll.Name, in, w.filter) defer w.st.watcher.UnwatchCollection(w.coll.Name, in) ids, err := w.initial() if err != nil { return err } out := w.out for { select { case <-w.tomb.Dying(): return tomb.ErrDying case <-w.st.watcher.Dead(): return stateWatcherDeadError(w.st.watcher.Err()) case ch := <-in: updates, ok := collect(ch, in, w.tomb.Dying()) if !ok { return tomb.ErrDying } if err := w.merge(ids, updates); err != nil { return err } if !ids.IsEmpty() { out = w.out } case out <- ids.Values(): ids = &set.Strings{} out = nil } } return nil } // minUnitsWatcher notifies about MinUnits changes of the services requiring // a minimum number of units to be alive. The first event returned by the // watcher is the set of service names requiring a minimum number of units. // Subsequent events are generated when a service increases MinUnits, or when // one or more units belonging to a service are destroyed. type minUnitsWatcher struct { commonWatcher known map[string]int out chan []string } var _ Watcher = (*minUnitsWatcher)(nil) func newMinUnitsWatcher(st *State) StringsWatcher { w := &minUnitsWatcher{ commonWatcher: commonWatcher{st: st}, known: make(map[string]int), out: make(chan []string), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop()) }() return w } func (st *State) WatchMinUnits() StringsWatcher { return newMinUnitsWatcher(st) } func (w *minUnitsWatcher) initial() (*set.Strings, error) { serviceNames := new(set.Strings) doc := &minUnitsDoc{} iter := w.st.minUnits.Find(nil).Iter() for iter.Next(doc) { w.known[doc.ServiceName] = doc.Revno serviceNames.Add(doc.ServiceName) } return serviceNames, iter.Err() } func (w *minUnitsWatcher) merge(serviceNames *set.Strings, change watcher.Change) error { serviceName := change.Id.(string) if change.Revno == -1 { delete(w.known, serviceName) serviceNames.Remove(serviceName) return nil } doc := minUnitsDoc{} if err := w.st.minUnits.FindId(serviceName).One(&doc); err != nil { return err } revno, known := w.known[serviceName] w.known[serviceName] = doc.Revno if !known || doc.Revno > revno { serviceNames.Add(serviceName) } return nil } func (w *minUnitsWatcher) loop() (err error) { ch := make(chan watcher.Change) w.st.watcher.WatchCollection(w.st.minUnits.Name, ch) defer w.st.watcher.UnwatchCollection(w.st.minUnits.Name, ch) serviceNames, err := w.initial() if err != nil { return err } out := w.out for { select { case <-w.tomb.Dying(): return tomb.ErrDying case <-w.st.watcher.Dead(): return stateWatcherDeadError(w.st.watcher.Err()) case change := <-ch: if err = w.merge(serviceNames, change); err != nil { return err } if !serviceNames.IsEmpty() { out = w.out } case out <- serviceNames.Values(): out = nil serviceNames = new(set.Strings) } } return nil } func (w *minUnitsWatcher) Changes() <-chan []string { return w.out } // RelationScopeWatcher observes changes to the set of units // in a particular relation scope. type RelationScopeWatcher struct { commonWatcher prefix string ignore string knownUnits set.Strings out chan *RelationScopeChange } var _ Watcher = (*RelationScopeWatcher)(nil) // RelationScopeChange contains information about units that have // entered or left a particular scope. type RelationScopeChange struct { Entered []string Left []string } func newRelationScopeWatcher(st *State, scope, ignore string) *RelationScopeWatcher { w := &RelationScopeWatcher{ commonWatcher: commonWatcher{st: st}, prefix: scope + "#", ignore: ignore, out: make(chan *RelationScopeChange), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop()) }() return w } // Changes returns a channel that will receive changes when units enter and // leave a relation scope. The Entered field in the first event on the channel // holds the initial state. func (w *RelationScopeWatcher) Changes() <-chan *RelationScopeChange { return w.out } func (changes *RelationScopeChange) isEmpty() bool { return len(changes.Entered)+len(changes.Left) == 0 } func (w *RelationScopeWatcher) mergeChange(changes *RelationScopeChange, ch watcher.Change) (err error) { doc := &relationScopeDoc{ch.Id.(string)} if !strings.HasPrefix(doc.Key, w.prefix) { return nil } name := doc.unitName() if name == w.ignore { return nil } if ch.Revno == -1 { if w.knownUnits.Contains(name) { changes.Left = append(changes.Left, name) w.knownUnits.Remove(name) } return nil } if !w.knownUnits.Contains(name) { changes.Entered = append(changes.Entered, name) w.knownUnits.Add(name) } return nil } func (w *RelationScopeWatcher) getInitialEvent() (initial *RelationScopeChange, err error) { changes := &RelationScopeChange{} docs := []relationScopeDoc{} sel := bson.D{{"_id", bson.D{{"$regex", "^" + w.prefix}}}} err = w.st.relationScopes.Find(sel).All(&docs) if err != nil { return nil, err } for _, doc := range docs { if name := doc.unitName(); name != w.ignore { changes.Entered = append(changes.Entered, name) w.knownUnits.Add(name) } } return changes, nil } func (w *RelationScopeWatcher) loop() error { ch := make(chan watcher.Change) w.st.watcher.WatchCollection(w.st.relationScopes.Name, ch) defer w.st.watcher.UnwatchCollection(w.st.relationScopes.Name, ch) changes, err := w.getInitialEvent() if err != nil { return err } out := w.out for { select { case <-w.st.watcher.Dead(): return stateWatcherDeadError(w.st.watcher.Err()) case <-w.tomb.Dying(): return tomb.ErrDying case c := <-ch: if err := w.mergeChange(changes, c); err != nil { return err } if !changes.isEmpty() { out = w.out } case out <- changes: changes = &RelationScopeChange{} out = nil } } return nil } // relationUnitsWatcher sends notifications of units entering and leaving the // scope of a RelationUnit, and changes to the settings of those units known // to have entered. type relationUnitsWatcher struct { commonWatcher sw *RelationScopeWatcher watching set.Strings updates chan watcher.Change out chan params.RelationUnitsChange } var _ Watcher = (*relationUnitsWatcher)(nil) // Watch returns a watcher that notifies of changes to conterpart units in // the relation. func (ru *RelationUnit) Watch() RelationUnitsWatcher { return newRelationUnitsWatcher(ru) } func newRelationUnitsWatcher(ru *RelationUnit) RelationUnitsWatcher { w := &relationUnitsWatcher{ commonWatcher: commonWatcher{st: ru.st}, sw: ru.WatchScope(), updates: make(chan watcher.Change), out: make(chan params.RelationUnitsChange), } go func() { defer w.finish() w.tomb.Kill(w.loop()) }() return w } // Changes returns a channel that will receive the changes to // counterpart units in a relation. The first event on the // channel holds the initial state of the relation in its // Changed field. func (w *relationUnitsWatcher) Changes() <-chan params.RelationUnitsChange { return w.out } func emptyRelationUnitsChanges(changes *params.RelationUnitsChange) bool { return len(changes.Changed)+len(changes.Departed) == 0 } // mergeSettings reads the relation settings node for the unit with the // supplied id, and sets a value in the Changed field keyed on the unit's // name. It returns the mgo/txn revision number of the settings node. func (w *relationUnitsWatcher) mergeSettings(changes *params.RelationUnitsChange, key string) (int64, error) { node, err := readSettings(w.st, key) if err != nil { return -1, err } name := (&relationScopeDoc{key}).unitName() settings := params.UnitSettings{Version: node.txnRevno} if changes.Changed == nil { changes.Changed = map[string]params.UnitSettings{name: settings} } else { changes.Changed[name] = settings } return node.txnRevno, nil } // mergeScope starts and stops settings watches on the units entering and // leaving the scope in the supplied RelationScopeChange event, and applies // the expressed changes to the supplied RelationUnitsChange event. func (w *relationUnitsWatcher) mergeScope(changes *params.RelationUnitsChange, c *RelationScopeChange) error { for _, name := range c.Entered { key := w.sw.prefix + name revno, err := w.mergeSettings(changes, key) if err != nil { return err } changes.Departed = remove(changes.Departed, name) w.st.watcher.Watch(w.st.settings.Name, key, revno, w.updates) w.watching.Add(key) } for _, name := range c.Left { key := w.sw.prefix + name changes.Departed = append(changes.Departed, name) if changes.Changed != nil { delete(changes.Changed, name) } w.st.watcher.Unwatch(w.st.settings.Name, key, w.updates) w.watching.Remove(key) } return nil } // remove removes s from strs and returns the modified slice. func remove(strs []string, s string) []string { for i, v := range strs { if s == v { strs[i] = strs[len(strs)-1] return strs[:len(strs)-1] } } return strs } func (w *relationUnitsWatcher) finish() { watcher.Stop(w.sw, &w.tomb) for _, watchedValue := range w.watching.Values() { w.st.watcher.Unwatch(w.st.settings.Name, watchedValue, w.updates) } close(w.updates) close(w.out) w.tomb.Done() } func (w *relationUnitsWatcher) loop() (err error) { sentInitial := false changes := params.RelationUnitsChange{} out := w.out out = nil for { select { case <-w.st.watcher.Dead(): return stateWatcherDeadError(w.st.watcher.Err()) case <-w.tomb.Dying(): return tomb.ErrDying case c, ok := <-w.sw.Changes(): if !ok { return watcher.MustErr(w.sw) } if err = w.mergeScope(&changes, c); err != nil { return err } if !sentInitial || !emptyRelationUnitsChanges(&changes) { out = w.out } else { out = nil } case c := <-w.updates: if _, err = w.mergeSettings(&changes, c.Id.(string)); err != nil { return err } out = w.out case out <- changes: sentInitial = true changes = params.RelationUnitsChange{} out = nil } } } // unitsWatcher notifies of changes to a set of units. Notifications will be // sent when units enter or leave the set, and when units in the set change // their lifecycle status. The initial event contains all units in the set, // regardless of lifecycle status; once a unit observed to be Dead or removed // has been reported, it will not be reported again. type unitsWatcher struct { commonWatcher tag string getUnits func() ([]string, error) life map[string]Life in chan watcher.Change out chan []string } var _ Watcher = (*unitsWatcher)(nil) // WatchSubordinateUnits returns a StringsWatcher tracking the unit's subordinate units. func (u *Unit) WatchSubordinateUnits() StringsWatcher { u = &Unit{st: u.st, doc: u.doc} coll := u.st.units.Name getUnits := func() ([]string, error) { if err := u.Refresh(); err != nil { return nil, err } return u.doc.Subordinates, nil } return newUnitsWatcher(u.st, u.Tag(), getUnits, coll, u.doc.Name) } // WatchPrincipalUnits returns a StringsWatcher tracking the machine's principal // units. func (m *Machine) WatchPrincipalUnits() StringsWatcher { m = &Machine{st: m.st, doc: m.doc} coll := m.st.machines.Name getUnits := func() ([]string, error) { if err := m.Refresh(); err != nil { return nil, err } return m.doc.Principals, nil } return newUnitsWatcher(m.st, m.Tag(), getUnits, coll, m.doc.Id) } func newUnitsWatcher(st *State, tag string, getUnits func() ([]string, error), coll, id string) StringsWatcher { w := &unitsWatcher{ commonWatcher: commonWatcher{st: st}, tag: tag, getUnits: getUnits, life: map[string]Life{}, in: make(chan watcher.Change), out: make(chan []string), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop(coll, id)) }() return w } // Tag returns the tag of the entity whose units are being watched. func (w *unitsWatcher) Tag() string { return w.tag } // Changes returns the UnitsWatcher's output channel. func (w *unitsWatcher) Changes() <-chan []string { return w.out } // lifeWatchDoc holds the fields used in starting and maintaining a watch // on a entity's lifecycle. type lifeWatchDoc struct { Id string `bson:"_id"` Life Life TxnRevno int64 `bson:"txn-revno"` } // lifeWatchFields specifies the fields of a lifeWatchDoc. var lifeWatchFields = bson.D{{"_id", 1}, {"life", 1}, {"txn-revno", 1}} // initial returns every member of the tracked set. func (w *unitsWatcher) initial() ([]string, error) { initial, err := w.getUnits() if err != nil { return nil, err } docs := []lifeWatchDoc{} query := bson.D{{"_id", bson.D{{"$in", initial}}}} if err := w.st.units.Find(query).Select(lifeWatchFields).All(&docs); err != nil { return nil, err } changes := []string{} for _, doc := range docs { changes = append(changes, doc.Id) if doc.Life != Dead { w.life[doc.Id] = doc.Life w.st.watcher.Watch(w.st.units.Name, doc.Id, doc.TxnRevno, w.in) } } return changes, nil } // update adds to and returns changes, such that it contains the names of any // non-Dead units to have entered or left the tracked set. func (w *unitsWatcher) update(changes []string) ([]string, error) { latest, err := w.getUnits() if err != nil { return nil, err } for _, name := range latest { if _, known := w.life[name]; !known { changes, err = w.merge(changes, name) if err != nil { return nil, err } } } for name := range w.life { if hasString(latest, name) { continue } if !hasString(changes, name) { changes = append(changes, name) } delete(w.life, name) w.st.watcher.Unwatch(w.st.units.Name, name, w.in) } return changes, nil } // merge adds to and returns changes, such that it contains the supplied unit // name if that unit is unknown and non-Dead, or has changed lifecycle status. func (w *unitsWatcher) merge(changes []string, name string) ([]string, error) { doc := lifeWatchDoc{} err := w.st.units.FindId(name).Select(lifeWatchFields).One(&doc) gone := false if err == mgo.ErrNotFound { gone = true } else if err != nil { return nil, err } else if doc.Life == Dead { gone = true } life, known := w.life[name] switch { case known && gone: delete(w.life, name) w.st.watcher.Unwatch(w.st.units.Name, name, w.in) case !known && !gone: w.st.watcher.Watch(w.st.units.Name, name, doc.TxnRevno, w.in) w.life[name] = doc.Life case known && life != doc.Life: w.life[name] = doc.Life default: return changes, nil } if !hasString(changes, name) { changes = append(changes, name) } return changes, nil } func (w *unitsWatcher) loop(coll, id string) error { revno, err := getTxnRevno(w.st.db.C(coll), id) if err != nil { return err } w.st.watcher.Watch(coll, id, revno, w.in) defer func() { w.st.watcher.Unwatch(coll, id, w.in) for name := range w.life { w.st.watcher.Unwatch(w.st.units.Name, name, w.in) } }() changes, err := w.initial() if err != nil { return err } out := w.out for { select { case <-w.st.watcher.Dead(): return stateWatcherDeadError(w.st.watcher.Err()) case <-w.tomb.Dying(): return tomb.ErrDying case c := <-w.in: name := c.Id.(string) if name == id { changes, err = w.update(changes) } else { changes, err = w.merge(changes, name) } if err != nil { return err } if len(changes) > 0 { out = w.out } case out <- changes: out = nil changes = nil } } return nil } // EnvironConfigWatcher observes changes to the // environment configuration. type EnvironConfigWatcher struct { commonWatcher out chan *config.Config } var _ Watcher = (*EnvironConfigWatcher)(nil) // WatchEnvironConfig returns a watcher for observing changes // to the environment configuration. func (s *State) WatchEnvironConfig() *EnvironConfigWatcher { return newEnvironConfigWatcher(s) } func newEnvironConfigWatcher(s *State) *EnvironConfigWatcher { w := &EnvironConfigWatcher{ commonWatcher: commonWatcher{st: s}, out: make(chan *config.Config), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop()) }() return w } // Changes returns a channel that will receive the new environment // configuration when a change is detected. Note that multiple changes may // be observed as a single event in the channel. func (w *EnvironConfigWatcher) Changes() <-chan *config.Config { return w.out } func (w *EnvironConfigWatcher) loop() (err error) { sw := w.st.watchSettings(environGlobalKey) defer sw.Stop() out := w.out out = nil cfg := &config.Config{} for { select { case <-w.st.watcher.Dead(): return stateWatcherDeadError(w.st.watcher.Err()) case <-w.tomb.Dying(): return tomb.ErrDying case settings, ok := <-sw.Changes(): if !ok { return watcher.MustErr(sw) } cfg, err = config.New(config.NoDefaults, settings.Map()) if err == nil { out = w.out } else { out = nil } case out <- cfg: out = nil } } return nil } type settingsWatcher struct { commonWatcher out chan *Settings } var _ Watcher = (*settingsWatcher)(nil) // watchSettings creates a watcher for observing changes to settings. func (s *State) watchSettings(key string) *settingsWatcher { return newSettingsWatcher(s, key) } func newSettingsWatcher(s *State, key string) *settingsWatcher { w := &settingsWatcher{ commonWatcher: commonWatcher{st: s}, out: make(chan *Settings), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop(key)) }() return w } // Changes returns a channel that will receive the new settings. // Multiple changes may be observed as a single event in the channel. func (w *settingsWatcher) Changes() <-chan *Settings { return w.out } func (w *settingsWatcher) loop(key string) (err error) { ch := make(chan watcher.Change) revno := int64(-1) settings, err := readSettings(w.st, key) if err == nil { revno = settings.txnRevno } else if !errors.IsNotFoundError(err) { return err } w.st.watcher.Watch(w.st.settings.Name, key, revno, ch) defer w.st.watcher.Unwatch(w.st.settings.Name, key, ch) out := w.out if revno == -1 { out = nil } for { select { case <-w.st.watcher.Dead(): return stateWatcherDeadError(w.st.watcher.Err()) case <-w.tomb.Dying(): return tomb.ErrDying case <-ch: settings, err = readSettings(w.st, key) if err != nil { return err } out = w.out case out <- settings: out = nil } } return nil } // entityWatcher generates an event when a document in the db changes type entityWatcher struct { commonWatcher out chan struct{} } var _ Watcher = (*entityWatcher)(nil) // WatchHardwareCharacteristics returns a watcher for observing changes to a machine's hardware characteristics. func (m *Machine) WatchHardwareCharacteristics() NotifyWatcher { return newEntityWatcher(m.st, m.st.instanceData, m.doc.Id) } func (st *State) WatchStateServerInfo() NotifyWatcher { return newEntityWatcher(st, st.stateServers, environGlobalKey) } // Watch returns a watcher for observing changes to a machine. func (m *Machine) Watch() NotifyWatcher { return newEntityWatcher(m.st, m.st.machines, m.doc.Id) } // Watch returns a watcher for observing changes to a service. func (s *Service) Watch() NotifyWatcher { return newEntityWatcher(s.st, s.st.services, s.doc.Name) } // Watch returns a watcher for observing changes to a unit. func (u *Unit) Watch() NotifyWatcher { return newEntityWatcher(u.st, u.st.units, u.doc.Name) } // Watch returns a watcher for observing changes to an environment. func (e *Environment) Watch() NotifyWatcher { return newEntityWatcher(e.st, e.st.environments, e.doc.UUID) } // WatchForEnvironConfigChanges returns a NotifyWatcher waiting for the Environ // Config to change. This differs from WatchEnvironConfig in that the watcher // is a NotifyWatcher that does not give content during Changes() func (st *State) WatchForEnvironConfigChanges() NotifyWatcher { return newEntityWatcher(st, st.settings, environGlobalKey) } // WatchAPIHostPorts returns a NotifyWatcher that notifies // when the set of API addresses changes. func (st *State) WatchAPIHostPorts() NotifyWatcher { return newEntityWatcher(st, st.stateServers, apiHostPortsKey) } // WatchConfigSettings returns a watcher for observing changes to the // unit's service configuration settings. The unit must have a charm URL // set before this method is called, and the returned watcher will be // valid only while the unit's charm URL is not changed. // TODO(fwereade): this could be much smarter; if it were, uniter.Filter // could be somewhat simpler. func (u *Unit) WatchConfigSettings() (NotifyWatcher, error) { if u.doc.CharmURL == nil { return nil, fmt.Errorf("unit charm not set") } settingsKey := serviceSettingsKey(u.doc.Service, u.doc.CharmURL) return newEntityWatcher(u.st, u.st.settings, settingsKey), nil } func newEntityWatcher(st *State, coll *mgo.Collection, key string) NotifyWatcher { w := &entityWatcher{ commonWatcher: commonWatcher{st: st}, out: make(chan struct{}), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop(coll, key)) }() return w } // Changes returns the event channel for the entityWatcher. func (w *entityWatcher) Changes() <-chan struct{} { return w.out } // getTxnRevo returns the transaction revision number of the // given key in the given collection. It is useful to enable // a watcher.Watcher to be primed with the correct revision // id. func getTxnRevno(coll *mgo.Collection, key string) (int64, error) { doc := &struct { TxnRevno int64 `bson:"txn-revno"` }{} fields := bson.D{{"txn-revno", 1}} if err := coll.FindId(key).Select(fields).One(doc); err == mgo.ErrNotFound { return -1, nil } else if err != nil { return 0, err } return doc.TxnRevno, nil } func (w *entityWatcher) loop(coll *mgo.Collection, key string) error { txnRevno, err := getTxnRevno(coll, key) if err != nil { return err } in := make(chan watcher.Change) w.st.watcher.Watch(coll.Name, key, txnRevno, in) defer w.st.watcher.Unwatch(coll.Name, key, in) out := w.out for { select { case <-w.tomb.Dying(): return tomb.ErrDying case <-w.st.watcher.Dead(): return stateWatcherDeadError(w.st.watcher.Err()) case ch := <-in: if _, ok := collect(ch, in, w.tomb.Dying()); !ok { return tomb.ErrDying } out = w.out case out <- struct{}{}: out = nil } } return nil } // machineUnitsWatcher notifies about assignments and lifecycle changes // for all units of a machine. // // The first event emitted contains the unit names of all units currently // assigned to the machine, irrespective of their life state. From then on, // a new event is emitted whenever a unit is assigned to or unassigned from // the machine, or the lifecycle of a unit that is currently assigned to // the machine changes. // // After a unit is found to be Dead, no further event will include it. type machineUnitsWatcher struct { commonWatcher machine *Machine out chan []string in chan watcher.Change known map[string]Life } var _ Watcher = (*machineUnitsWatcher)(nil) // WatchUnits returns a new StringsWatcher watching m's units. func (m *Machine) WatchUnits() StringsWatcher { return newMachineUnitsWatcher(m) } func newMachineUnitsWatcher(m *Machine) StringsWatcher { w := &machineUnitsWatcher{ commonWatcher: commonWatcher{st: m.st}, out: make(chan []string), in: make(chan watcher.Change), known: make(map[string]Life), machine: &Machine{st: m.st, doc: m.doc}, // Copy so it may be freely refreshed } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop()) }() return w } // Changes returns the event channel for w. func (w *machineUnitsWatcher) Changes() <-chan []string { return w.out } func (w *machineUnitsWatcher) updateMachine(pending []string) (new []string, err error) { err = w.machine.Refresh() if err != nil { return nil, err } for _, unit := range w.machine.doc.Principals { if _, ok := w.known[unit]; !ok { pending, err = w.merge(pending, unit) if err != nil { return nil, err } } } return pending, nil } func (w *machineUnitsWatcher) merge(pending []string, unit string) (new []string, err error) { doc := unitDoc{} err = w.st.units.FindId(unit).One(&doc) if err != nil && err != mgo.ErrNotFound { return nil, err } life, known := w.known[unit] if err == mgo.ErrNotFound || doc.Principal == "" && (doc.MachineId == "" || doc.MachineId != w.machine.doc.Id) { // Unit was removed or unassigned from w.machine. if known { delete(w.known, unit) w.st.watcher.Unwatch(w.st.units.Name, unit, w.in) if life != Dead && !hasString(pending, unit) { pending = append(pending, unit) } for _, subunit := range doc.Subordinates { if sublife, subknown := w.known[subunit]; subknown { delete(w.known, subunit) w.st.watcher.Unwatch(w.st.units.Name, subunit, w.in) if sublife != Dead && !hasString(pending, subunit) { pending = append(pending, subunit) } } } } return pending, nil } if !known { w.st.watcher.Watch(w.st.units.Name, unit, doc.TxnRevno, w.in) pending = append(pending, unit) } else if life != doc.Life && !hasString(pending, unit) { pending = append(pending, unit) } w.known[unit] = doc.Life for _, subunit := range doc.Subordinates { if _, ok := w.known[subunit]; !ok { pending, err = w.merge(pending, subunit) if err != nil { return nil, err } } } return pending, nil } func (w *machineUnitsWatcher) loop() error { defer func() { for unit := range w.known { w.st.watcher.Unwatch(w.st.units.Name, unit, w.in) } }() revno, err := getTxnRevno(w.st.machines, w.machine.doc.Id) if err != nil { return err } machineCh := make(chan watcher.Change) w.st.watcher.Watch(w.st.machines.Name, w.machine.doc.Id, revno, machineCh) defer w.st.watcher.Unwatch(w.st.machines.Name, w.machine.doc.Id, machineCh) changes, err := w.updateMachine([]string(nil)) if err != nil { return err } out := w.out for { select { case <-w.st.watcher.Dead(): return stateWatcherDeadError(w.st.watcher.Err()) case <-w.tomb.Dying(): return tomb.ErrDying case <-machineCh: changes, err = w.updateMachine(changes) if err != nil { return err } if len(changes) > 0 { out = w.out } case c := <-w.in: changes, err = w.merge(changes, c.Id.(string)) if err != nil { return err } if len(changes) > 0 { out = w.out } case out <- changes: out = nil changes = nil } } } // cleanupWatcher notifies of changes in the cleanups collection. type cleanupWatcher struct { commonWatcher out chan struct{} } var _ Watcher = (*cleanupWatcher)(nil) // WatchCleanups starts and returns a CleanupWatcher. func (st *State) WatchCleanups() NotifyWatcher { return newCleanupWatcher(st) } func newCleanupWatcher(st *State) NotifyWatcher { w := &cleanupWatcher{ commonWatcher: commonWatcher{st: st}, out: make(chan struct{}), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop()) }() return w } // Changes returns the event channel for w. func (w *cleanupWatcher) Changes() <-chan struct{} { return w.out } func (w *cleanupWatcher) loop() (err error) { in := make(chan watcher.Change) w.st.watcher.WatchCollection(w.st.cleanups.Name, in) defer w.st.watcher.UnwatchCollection(w.st.cleanups.Name, in) out := w.out for { select { case <-w.tomb.Dying(): return tomb.ErrDying case <-w.st.watcher.Dead(): return stateWatcherDeadError(w.st.watcher.Err()) case ch := <-in: if _, ok := collect(ch, in, w.tomb.Dying()); !ok { return tomb.ErrDying } out = w.out case out <- struct{}{}: out = nil } } } ����������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/environ.go���������������������������������������0000644�0000153�0000161�00000010153�12321735642�024014� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/names" ) // environGlobalKey is the key for the environment, its // settings and constraints. const environGlobalKey = "e" // Environment represents the state of an environment. type Environment struct { st *State doc environmentDoc annotator } // environmentDoc represents the internal state of the environment in MongoDB. type environmentDoc struct { UUID string `bson:"_id"` Name string Life Life } // Environment returns the environment entity. func (st *State) Environment() (*Environment, error) { env := &Environment{st: st} if err := env.refresh(st.environments.Find(nil)); err != nil { return nil, err } env.annotator = annotator{ globalKey: env.globalKey(), tag: env.Tag(), st: st, } return env, nil } // Tag returns a name identifying the environment. // The returned name will be different from other Tag values returned // by any other entities from the same state. func (e *Environment) Tag() string { return names.EnvironTag(e.doc.UUID) } // UUID returns the universally unique identifier of the environment. func (e *Environment) UUID() string { return e.doc.UUID } // Name returns the human friendly name of the environment. func (e *Environment) Name() string { return e.doc.Name } // Life returns whether the environment is Alive, Dying or Dead. func (e *Environment) Life() Life { return e.doc.Life } // globalKey returns the global database key for the environment. func (e *Environment) globalKey() string { return environGlobalKey } func (e *Environment) Refresh() error { return e.refresh(e.st.environments.FindId(e.UUID())) } func (e *Environment) refresh(query *mgo.Query) error { err := query.One(&e.doc) if err == mgo.ErrNotFound { return errors.NotFoundf("environment") } return err } // Destroy sets the environment's lifecycle to Dying, preventing // addition of services or machines to state. func (e *Environment) Destroy() error { if e.Life() != Alive { return nil } // TODO(axw) 2013-12-11 #1218688 // Resolve the race between checking for manual machines and // destroying the environment. We can set Environment to Dying // to resolve the race, but that puts the environment into an // unusable state. // // We add a cleanup for services, but not for machines; // machines are destroyed via the provider interface. The // exception to this rule is manual machines; the API prevents // destroy-environment from succeeding if any non-manager // manual machines exist. ops := []txn.Op{{ C: e.st.environments.Name, Id: e.doc.UUID, Update: bson.D{{"$set", bson.D{{"life", Dying}}}}, Assert: isEnvAliveDoc, }, e.st.newCleanupOp("services", "")} err := e.st.runTransaction(ops) switch err { case nil, txn.ErrAborted: // If the transaction aborted, the environment is either // Dying or Dead; neither case is an error. If it's Dead, // reporting it as Dying is not incorrect; the user thought // it was Alive, so we've progressed towards Dead. If the // user then calls Refresh they'll get the true value. e.doc.Life = Dying } return err } // createEnvironmentOp returns the operation needed to create // an environment document with the given name and UUID. func createEnvironmentOp(st *State, name, uuid string) txn.Op { doc := &environmentDoc{uuid, name, Alive} return txn.Op{ C: st.environments.Name, Id: uuid, Assert: txn.DocMissing, Insert: doc, } } // assertAliveOp returns a txn.Op that asserts the environment is alive. func (e *Environment) assertAliveOp() txn.Op { return txn.Op{ C: e.st.environments.Name, Id: e.UUID(), Assert: isEnvAliveDoc, } } // isEnvAlive is an Environment-specific versio nof isAliveDoc. // // Environment documents from versions of Juju prior to 1.17 // do not have the life field; if it does not exist, it should // be considered to have the value Alive. var isEnvAliveDoc = bson.D{ {"life", bson.D{{"$in", []interface{}{Alive, nil}}}}, } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/annotator_test.go��������������������������������0000644�0000153�0000161�00000005653�12321735642�025411� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" ) var annotatorTests = []struct { about string initial map[string]string input map[string]string expected map[string]string err string }{ { about: "test setting an annotation", input: map[string]string{"mykey": "myvalue"}, expected: map[string]string{"mykey": "myvalue"}, }, { about: "test setting multiple annotations", input: map[string]string{"key1": "value1", "key2": "value2"}, expected: map[string]string{"key1": "value1", "key2": "value2"}, }, { about: "test overriding annotations", initial: map[string]string{"mykey": "myvalue"}, input: map[string]string{"mykey": "another-value"}, expected: map[string]string{"mykey": "another-value"}, }, { about: "test setting an invalid annotation", input: map[string]string{"invalid.key": "myvalue"}, err: `cannot update annotations on .*: invalid key "invalid.key"`, }, { about: "test returning a non existent annotation", expected: map[string]string{}, }, { about: "test removing an annotation", initial: map[string]string{"mykey": "myvalue"}, input: map[string]string{"mykey": ""}, expected: map[string]string{}, }, { about: "test removing multiple annotations", initial: map[string]string{"key1": "v1", "key2": "v2", "key3": "v3"}, input: map[string]string{"key1": "", "key3": ""}, expected: map[string]string{"key2": "v2"}, }, { about: "test removing/adding annotations in the same transaction", initial: map[string]string{"key1": "value1"}, input: map[string]string{"key1": "", "key2": "value2"}, expected: map[string]string{"key2": "value2"}, }, { about: "test removing a non existent annotation", input: map[string]string{"mykey": ""}, expected: map[string]string{}, }, { about: "test passing an empty map", input: map[string]string{}, expected: map[string]string{}, }, } func testAnnotator(c *gc.C, getEntity func() (state.Annotator, error)) { for i, t := range annotatorTests { c.Logf("test %d. %s", i, t.about) entity, err := getEntity() c.Assert(err, gc.IsNil) err = entity.SetAnnotations(t.initial) c.Assert(err, gc.IsNil) err = entity.SetAnnotations(t.input) if t.err != "" { c.Assert(err, gc.ErrorMatches, t.err) continue } // Retrieving single values works as expected. for key, value := range t.input { v, err := entity.Annotation(key) c.Assert(err, gc.IsNil) c.Assert(v, gc.Equals, value) } // The value stored in MongoDB changed. ann, err := entity.Annotations() c.Assert(err, gc.IsNil) c.Assert(ann, gc.DeepEquals, t.expected) // Clean up existing annotations. cleanup := make(map[string]string) for key := range t.expected { cleanup[key] = "" } err = entity.SetAnnotations(cleanup) c.Assert(err, gc.IsNil) } } �������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/charm.go�����������������������������������������0000644�0000153�0000161�00000003434�12321735642�023432� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "net/url" "launchpad.net/juju-core/charm" ) // charmDoc represents the internal state of a charm in MongoDB. type charmDoc struct { URL *charm.URL `bson:"_id"` Meta *charm.Meta Config *charm.Config BundleURL *url.URL BundleSha256 string PendingUpload bool Placeholder bool } // Charm represents the state of a charm in the environment. type Charm struct { st *State doc charmDoc } func newCharm(st *State, cdoc *charmDoc) (*Charm, error) { return &Charm{st: st, doc: *cdoc}, nil } func (c *Charm) String() string { return c.doc.URL.String() } // URL returns the URL that identifies the charm. func (c *Charm) URL() *charm.URL { clone := *c.doc.URL return &clone } // Revision returns the monotonically increasing charm // revision number. func (c *Charm) Revision() int { return c.doc.URL.Revision } // Meta returns the metadata of the charm. func (c *Charm) Meta() *charm.Meta { return c.doc.Meta } // Config returns the configuration of the charm. func (c *Charm) Config() *charm.Config { return c.doc.Config } // BundleURL returns the url to the charm bundle in // the provider storage. func (c *Charm) BundleURL() *url.URL { return c.doc.BundleURL } // BundleSha256 returns the SHA256 digest of the charm bundle bytes. func (c *Charm) BundleSha256() string { return c.doc.BundleSha256 } // IsUploaded returns whether the charm has been uploaded to the // provider storage. func (c *Charm) IsUploaded() bool { return !c.doc.PendingUpload } // IsPlaceholder returns whether the charm record is just a placeholder // rather than representing a deployed charm. func (c *Charm) IsPlaceholder() bool { return c.doc.Placeholder } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/open.go������������������������������������������0000644�0000153�0000161�00000030277�12321735776�023316� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "crypto/tls" "crypto/x509" stderrors "errors" "fmt" "net" "time" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/cert" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/presence" "launchpad.net/juju-core/state/watcher" "launchpad.net/juju-core/utils" ) // mongoSocketTimeout should be long enough that // even a slow mongo server will respond in that // length of time. Since mongo servers ping themselves // every 10 seconds, that seems like a reasonable // default. const mongoSocketTimeout = 10 * time.Second // Info encapsulates information about cluster of // servers holding juju state and can be used to make a // connection to that cluster. type Info struct { // Addrs gives the addresses of the MongoDB servers for the state. // Each address should be in the form address:port. Addrs []string // CACert holds the CA certificate that will be used // to validate the state server's certificate, in PEM format. CACert []byte // Tag holds the name of the entity that is connecting. // It should be empty when connecting as an administrator. Tag string // Password holds the password for the connecting entity. Password string } // DialOpts holds configuration parameters that control the // Dialing behavior when connecting to a state server. type DialOpts struct { // Timeout is the amount of time to wait contacting // a state server. Timeout time.Duration } // DefaultDialOpts returns a DialOpts representing the default // parameters for contacting a state server. func DefaultDialOpts() DialOpts { return DialOpts{ Timeout: 10 * time.Minute, } } // Open connects to the server described by the given // info, waits for it to be initialized, and returns a new State // representing the environment connected to. // // A policy may be provided, which will be used to validate and // modify behaviour of certain operations in state. A nil policy // may be provided. // // Open returns unauthorizedError if access is unauthorized. func Open(info *Info, opts DialOpts, policy Policy) (*State, error) { logger.Infof("opening state; mongo addresses: %q; entity %q", info.Addrs, info.Tag) if len(info.Addrs) == 0 { return nil, stderrors.New("no mongo addresses") } if len(info.CACert) == 0 { return nil, stderrors.New("missing CA certificate") } xcert, err := cert.ParseCert(info.CACert) if err != nil { return nil, fmt.Errorf("cannot parse CA certificate: %v", err) } pool := x509.NewCertPool() pool.AddCert(xcert) tlsConfig := &tls.Config{ RootCAs: pool, ServerName: "anything", } dial := func(addr net.Addr) (net.Conn, error) { c, err := net.Dial("tcp", addr.String()) if err != nil { logger.Debugf("connection failed, will retry: %v", err) return nil, err } cc := tls.Client(c, tlsConfig) if err := cc.Handshake(); err != nil { logger.Errorf("TLS handshake failed: %v", err) return nil, err } return cc, nil } session, err := mgo.DialWithInfo(&mgo.DialInfo{ Addrs: info.Addrs, Timeout: opts.Timeout, Dial: dial, }) if err != nil { return nil, err } logger.Infof("connection established") st, err := newState(session, info, policy) if err != nil { session.Close() return nil, err } session.SetSocketTimeout(mongoSocketTimeout) return st, nil } // Initialize sets up an initial empty state and returns it. // This needs to be performed only once for a given environment. // It returns unauthorizedError if access is unauthorized. func Initialize(info *Info, cfg *config.Config, opts DialOpts, policy Policy) (rst *State, err error) { st, err := Open(info, opts, policy) if err != nil { return nil, err } defer func() { if err != nil { st.Close() } }() // A valid environment is used as a signal that the // state has already been initalized. If this is the case // do nothing. if _, err := st.Environment(); err == nil { return st, nil } else if !errors.IsNotFoundError(err) { return nil, err } logger.Infof("initializing environment") if err := checkEnvironConfig(cfg); err != nil { return nil, err } uuid, err := utils.NewUUID() if err != nil { return nil, fmt.Errorf("environment UUID cannot be created: %v", err) } ops := []txn.Op{ createConstraintsOp(st, environGlobalKey, constraints.Value{}), createSettingsOp(st, environGlobalKey, cfg.AllAttrs()), createEnvironmentOp(st, cfg.Name(), uuid.String()), { C: st.stateServers.Name, Id: environGlobalKey, Insert: &stateServersDoc{}, }, { C: st.stateServers.Name, Id: apiHostPortsKey, Insert: &apiHostPortsDoc{}, }, } if err := st.runTransaction(ops); err == txn.ErrAborted { // The config was created in the meantime. return st, nil } else if err != nil { return nil, err } return st, nil } var indexes = []struct { collection string key []string }{ // After the first public release, do not remove entries from here // without adding them to a list of indexes to drop, to ensure // old databases are modified to have the correct indexes. {"relations", []string{"endpoints.relationname"}}, {"relations", []string{"endpoints.servicename"}}, {"units", []string{"service"}}, {"units", []string{"principal"}}, {"units", []string{"machineid"}}, {"users", []string{"name"}}, } // The capped collection used for transaction logs defaults to 10MB. // It's tweaked in export_test.go to 1MB to avoid the overhead of // creating and deleting the large file repeatedly in tests. var ( logSize = 10000000 logSizeTests = 1000000 ) func maybeUnauthorized(err error, msg string) error { if err == nil { return nil } if isUnauthorized(err) { return errors.Unauthorizedf("%s: unauthorized mongo access: %v", msg, err) } return fmt.Errorf("%s: %v", msg, err) } func isUnauthorized(err error) bool { if err == nil { return false } // Some unauthorized access errors have no error code, // just a simple error string. if err.Error() == "auth fails" { return true } if err, ok := err.(*mgo.QueryError); ok { return err.Code == 10057 || err.Message == "need to login" || err.Message == "unauthorized" } return false } func newState(session *mgo.Session, info *Info, policy Policy) (*State, error) { db := session.DB("juju") pdb := session.DB("presence") if info.Tag != "" { if err := db.Login(info.Tag, info.Password); err != nil { return nil, maybeUnauthorized(err, fmt.Sprintf("cannot log in to juju database as %q", info.Tag)) } if err := pdb.Login(info.Tag, info.Password); err != nil { return nil, maybeUnauthorized(err, fmt.Sprintf("cannot log in to presence database as %q", info.Tag)) } } else if info.Password != "" { admin := session.DB(AdminUser) if err := admin.Login(AdminUser, info.Password); err != nil { return nil, maybeUnauthorized(err, "cannot log in to admin database") } } st := &State{ info: info, policy: policy, db: db, environments: db.C("environments"), charms: db.C("charms"), machines: db.C("machines"), containerRefs: db.C("containerRefs"), instanceData: db.C("instanceData"), relations: db.C("relations"), relationScopes: db.C("relationscopes"), services: db.C("services"), networks: db.C("linkednetworks"), minUnits: db.C("minunits"), settings: db.C("settings"), settingsrefs: db.C("settingsrefs"), constraints: db.C("constraints"), units: db.C("units"), users: db.C("users"), presence: pdb.C("presence"), cleanups: db.C("cleanups"), annotations: db.C("annotations"), statuses: db.C("statuses"), stateServers: db.C("stateServers"), } log := db.C("txns.log") logInfo := mgo.CollectionInfo{Capped: true, MaxBytes: logSize} // The lack of error code for this error was reported upstream: // https://jira.klmongodb.org/browse/SERVER-6992 err := log.Create(&logInfo) if err != nil && err.Error() != "collection already exists" { return nil, maybeUnauthorized(err, "cannot create log collection") } st.runner = txn.NewRunner(db.C("txns")) st.runner.ChangeLog(db.C("txns.log")) st.watcher = watcher.New(db.C("txns.log")) st.pwatcher = presence.NewWatcher(pdb.C("presence")) for _, item := range indexes { index := mgo.Index{Key: item.key} if err := db.C(item.collection).EnsureIndex(index); err != nil { return nil, fmt.Errorf("cannot create database index: %v", err) } } st.transactionHooks = make(chan ([]transactionHook), 1) st.transactionHooks <- nil // TODO(rog) delete this when we can assume there are no // pre-1.18 environments running. if err := st.createStateServersDoc(); err != nil { return nil, fmt.Errorf("cannot create state servers document: %v", err) } if err := st.createAPIAddressesDoc(); err != nil { return nil, fmt.Errorf("cannot create API addresses document: %v", err) } if err := st.createStateServingInfoDoc(); err != nil { return nil, fmt.Errorf("cannot create state serving info document: %v", err) } return st, nil } // createStateServersDoc creates the state servers document // if it does not already exist. This is necessary to cope with // legacy environments that have not created the document // at initialization time. func (st *State) createStateServersDoc() error { // Quick check to see if we need to do anything so // that we can avoid transaction overhead in most cases. // We don't care what the error is - if it's something // unexpected, it'll be picked up again below. if info, err := st.StateServerInfo(); err == nil { if len(info.MachineIds) > 0 && len(info.VotingMachineIds) > 0 { return nil } } logger.Infof("adding state server info to legacy environment") // Find all current state servers and add the state servers // record containing them. We don't need to worry about // this being concurrent-safe, because in the juju versions // we're concerned about, there is only ever one state connection // (from the single bootstrap machine). var machineDocs []machineDoc err := st.machines.Find(bson.D{{"jobs", JobManageEnviron}}).All(&machineDocs) if err != nil { return err } var doc stateServersDoc for _, m := range machineDocs { doc.MachineIds = append(doc.MachineIds, m.Id) } doc.VotingMachineIds = doc.MachineIds logger.Infof("found existing state servers %v", doc.MachineIds) // We update the document before inserting it because // an earlier version of this code did not insert voting machine // ids or maintain the ids correctly. If that was the case, // the insert will be a no-op. ops := []txn.Op{{ C: st.stateServers.Name, Id: environGlobalKey, Update: bson.D{{"$set", bson.D{ {"machineids", doc.MachineIds}, {"votingmachineids", doc.VotingMachineIds}, }}}, }, { C: st.stateServers.Name, Id: environGlobalKey, Insert: &doc, }} return st.runTransaction(ops) } // createAPIAddressesDoc creates the API addresses document // if it does not already exist. This is necessary to cope with // legacy environments that have not created the document // at initialization time. func (st *State) createAPIAddressesDoc() error { var doc apiHostPortsDoc ops := []txn.Op{{ C: st.stateServers.Name, Id: apiHostPortsKey, Assert: txn.DocMissing, Insert: &doc, }} return onAbort(st.runTransaction(ops), nil) } // createStateServingInfoDoc creates the state serving info document // if it does not already exist func (st *State) createStateServingInfoDoc() error { var info params.StateServingInfo ops := []txn.Op{{ C: st.stateServers.Name, Id: stateServingInfoKey, Assert: txn.DocMissing, Insert: &info, }} return onAbort(st.runTransaction(ops), nil) } // CACert returns the certificate used to validate the state connection. func (st *State) CACert() (cert []byte) { return append(cert, st.info.CACert...) } func (st *State) Close() error { err1 := st.watcher.Stop() err2 := st.pwatcher.Stop() st.mu.Lock() var err3 error if st.allManager != nil { err3 = st.allManager.Stop() } st.mu.Unlock() st.db.Session.Close() for _, err := range []error{err1, err2, err3} { if err != nil { return err } } return nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/charm_test.go������������������������������������0000644�0000153�0000161�00000007706�12321735642�024477� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "bytes" "net/url" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" "launchpad.net/juju-core/testing" ) type CharmSuite struct { ConnSuite curl *charm.URL } var _ = gc.Suite(&CharmSuite{}) func (s *CharmSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) added := s.AddTestingCharm(c, "dummy") s.curl = added.URL() } func (s *CharmSuite) TestCharm(c *gc.C) { dummy, err := s.State.Charm(s.curl) c.Assert(err, gc.IsNil) c.Assert(dummy.URL().String(), gc.Equals, s.curl.String()) c.Assert(dummy.Revision(), gc.Equals, 1) bundleURL, err := url.Parse("http://bundles.testing.invalid/quantal-dummy-1") c.Assert(err, gc.IsNil) c.Assert(dummy.BundleURL(), gc.DeepEquals, bundleURL) c.Assert(dummy.BundleSha256(), gc.Equals, "quantal-dummy-1-sha256") c.Assert(dummy.IsUploaded(), jc.IsTrue) meta := dummy.Meta() c.Assert(meta.Name, gc.Equals, "dummy") config := dummy.Config() c.Assert(config.Options["title"], gc.Equals, charm.Option{ Default: "My Title", Description: "A descriptive title used for the service.", Type: "string", }, ) } func (s *CharmSuite) TestCharmNotFound(c *gc.C) { curl := charm.MustParseURL("local:anotherseries/dummy-1") _, err := s.State.Charm(curl) c.Assert(err, gc.ErrorMatches, `charm "local:anotherseries/dummy-1" not found`) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } type CharmTestHelperSuite struct { ConnSuite } var _ = gc.Suite(&CharmTestHelperSuite{}) func assertCustomCharm(c *gc.C, ch *state.Charm, series string, meta *charm.Meta, config *charm.Config, revision int) { // Check Charm interface method results. c.Assert(ch.Meta(), gc.DeepEquals, meta) c.Assert(ch.Config(), gc.DeepEquals, config) c.Assert(ch.Revision(), gc.DeepEquals, revision) // Test URL matches charm and expected series. url := ch.URL() c.Assert(url.Series, gc.Equals, series) c.Assert(url.Revision, gc.Equals, ch.Revision()) // Ignore the BundleURL and BundleSHA256 methods, they're irrelevant. } func assertStandardCharm(c *gc.C, ch *state.Charm, series string) { chd := testing.Charms.Dir(ch.Meta().Name) assertCustomCharm(c, ch, series, chd.Meta(), chd.Config(), chd.Revision()) } func forEachStandardCharm(c *gc.C, f func(name string)) { for _, name := range []string{ "logging", "mysql", "riak", "wordpress", } { c.Logf("checking %s", name) f(name) } } func (s *CharmTestHelperSuite) TestSimple(c *gc.C) { forEachStandardCharm(c, func(name string) { chd := testing.Charms.Dir(name) meta := chd.Meta() config := chd.Config() revision := chd.Revision() ch := s.AddTestingCharm(c, name) assertCustomCharm(c, ch, "quantal", meta, config, revision) ch = s.AddSeriesCharm(c, name, "anotherseries") assertCustomCharm(c, ch, "anotherseries", meta, config, revision) }) } var configYaml = ` options: working: description: when set to false, prevents service from functioning correctly default: true type: boolean ` func (s *CharmTestHelperSuite) TestConfigCharm(c *gc.C) { config, err := charm.ReadConfig(bytes.NewBuffer([]byte(configYaml))) c.Assert(err, gc.IsNil) forEachStandardCharm(c, func(name string) { chd := testing.Charms.Dir(name) meta := chd.Meta() ch := s.AddConfigCharm(c, name, configYaml, 123) assertCustomCharm(c, ch, "quantal", meta, config, 123) }) } var metaYamlSnippet = ` summary: blah description: blah blah ` func (s *CharmTestHelperSuite) TestMetaCharm(c *gc.C) { forEachStandardCharm(c, func(name string) { chd := testing.Charms.Dir(name) config := chd.Config() metaYaml := "name: " + name + metaYamlSnippet meta, err := charm.ReadMeta(bytes.NewBuffer([]byte(metaYaml))) c.Assert(err, gc.IsNil) ch := s.AddMetaCharm(c, name, metaYaml, 123) assertCustomCharm(c, ch, "quantal", meta, config, 123) }) } ����������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/presence/����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023576� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/presence/export_test.go��������������������������0000644�0000153�0000161�00000000655�12321735642�026526� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package presence func FakeTimeSlot(offset int) { fakeTimeSlot(offset) } func RealTimeSlot() { realTimeSlot() } func FakePeriod(seconds int64) { period = seconds } var realPeriod = period func RealPeriod() { period = realPeriod } func FindAllBeings(w *Watcher) (map[int64]beingInfo, error) { return w.findAllBeings() } �����������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/presence/presence_test.go������������������������0000644�0000153�0000161�00000025022�12321735642�027004� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package presence_test import ( "strconv" stdtesting "testing" "time" "labix.org/v2/mgo" gc "launchpad.net/gocheck" "launchpad.net/tomb" "launchpad.net/juju-core/state/presence" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) func TestPackage(t *stdtesting.T) { testing.MgoTestPackage(t) } type PresenceSuite struct { testing.MgoSuite testbase.LoggingSuite presence *mgo.Collection pings *mgo.Collection } var _ = gc.Suite(&PresenceSuite{}) func (s *PresenceSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *PresenceSuite) TearDownSuite(c *gc.C) { s.MgoSuite.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *PresenceSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) db := s.MgoSuite.Session.DB("presence") s.presence = db.C("presence") s.pings = db.C("presence.pings") presence.FakeTimeSlot(0) } func (s *PresenceSuite) TearDownTest(c *gc.C) { s.MgoSuite.TearDownTest(c) s.LoggingSuite.TearDownTest(c) presence.RealTimeSlot() presence.RealPeriod() } func assertChange(c *gc.C, watch <-chan presence.Change, want presence.Change) { select { case got := <-watch: if got != want { c.Fatalf("watch reported %v, want %v", got, want) } case <-time.After(testing.LongWait): c.Fatalf("watch reported nothing, want %v", want) } } func assertNoChange(c *gc.C, watch <-chan presence.Change) { select { case got := <-watch: c.Fatalf("watch reported %v, want nothing", got) case <-time.After(testing.ShortWait): } } func assertAlive(c *gc.C, w *presence.Watcher, key string, alive bool) { alive, err := w.Alive("a") c.Assert(err, gc.IsNil) c.Assert(alive, gc.Equals, alive) } func (s *PresenceSuite) TestErrAndDead(c *gc.C) { w := presence.NewWatcher(s.presence) defer w.Stop() c.Assert(w.Err(), gc.Equals, tomb.ErrStillAlive) select { case <-w.Dead(): c.Fatalf("Dead channel fired unexpectedly") default: } c.Assert(w.Stop(), gc.IsNil) c.Assert(w.Err(), gc.IsNil) select { case <-w.Dead(): default: c.Fatalf("Dead channel should have fired") } } func (s *PresenceSuite) TestAliveError(c *gc.C) { w := presence.NewWatcher(s.presence) c.Assert(w.Stop(), gc.IsNil) alive, err := w.Alive("a") c.Assert(err, gc.ErrorMatches, ".*: watcher is dying") c.Assert(alive, gc.Equals, false) } func (s *PresenceSuite) TestWorkflow(c *gc.C) { w := presence.NewWatcher(s.presence) pa := presence.NewPinger(s.presence, "a") pb := presence.NewPinger(s.presence, "b") defer w.Stop() defer pa.Stop() defer pb.Stop() assertAlive(c, w, "a", false) assertAlive(c, w, "b", false) // Buffer one entry to avoid blocking the watcher here. cha := make(chan presence.Change, 1) chb := make(chan presence.Change, 1) w.Watch("a", cha) w.Watch("b", chb) // Initial events with current status. assertChange(c, cha, presence.Change{"a", false}) assertChange(c, chb, presence.Change{"b", false}) w.StartSync() assertNoChange(c, cha) assertNoChange(c, chb) c.Assert(pa.Start(), gc.IsNil) w.StartSync() assertChange(c, cha, presence.Change{"a", true}) assertNoChange(c, cha) assertNoChange(c, chb) assertAlive(c, w, "a", true) assertAlive(c, w, "b", false) // Changes while the channel is out are not observed. w.Unwatch("a", cha) assertNoChange(c, cha) pa.Kill() w.Sync() pa = presence.NewPinger(s.presence, "a") pa.Start() w.StartSync() assertNoChange(c, cha) // We can still query it manually, though. assertAlive(c, w, "a", true) assertAlive(c, w, "b", false) // Initial positive event. No refresh needed. w.Watch("a", cha) assertChange(c, cha, presence.Change{"a", true}) c.Assert(pb.Start(), gc.IsNil) w.StartSync() assertChange(c, chb, presence.Change{"b", true}) assertNoChange(c, cha) assertNoChange(c, chb) c.Assert(pa.Stop(), gc.IsNil) w.StartSync() assertNoChange(c, cha) assertNoChange(c, chb) // pb is running, pa isn't. c.Assert(pa.Kill(), gc.IsNil) c.Assert(pb.Kill(), gc.IsNil) w.StartSync() assertChange(c, cha, presence.Change{"a", false}) assertChange(c, chb, presence.Change{"b", false}) c.Assert(w.Stop(), gc.IsNil) } func (s *PresenceSuite) TestScale(c *gc.C) { const N = 1000 var ps []*presence.Pinger defer func() { for _, p := range ps { p.Stop() } }() c.Logf("Starting %d pingers...", N) for i := 0; i < N; i++ { p := presence.NewPinger(s.presence, strconv.Itoa(i)) c.Assert(p.Start(), gc.IsNil) ps = append(ps, p) } c.Logf("Killing odd ones...") for i := 1; i < N; i += 2 { c.Assert(ps[i].Kill(), gc.IsNil) } c.Logf("Checking who's still alive...") w := presence.NewWatcher(s.presence) defer w.Stop() w.Sync() ch := make(chan presence.Change) for i := 0; i < N; i++ { k := strconv.Itoa(i) w.Watch(k, ch) if i%2 == 0 { assertChange(c, ch, presence.Change{k, true}) } else { assertChange(c, ch, presence.Change{k, false}) } } } func (s *PresenceSuite) TestExpiry(c *gc.C) { w := presence.NewWatcher(s.presence) p := presence.NewPinger(s.presence, "a") defer w.Stop() defer p.Stop() ch := make(chan presence.Change) w.Watch("a", ch) assertChange(c, ch, presence.Change{"a", false}) c.Assert(p.Start(), gc.IsNil) w.StartSync() assertChange(c, ch, presence.Change{"a", true}) // Still alive in previous slot. presence.FakeTimeSlot(1) w.StartSync() assertNoChange(c, ch) // Two last slots are empty. presence.FakeTimeSlot(2) w.StartSync() assertChange(c, ch, presence.Change{"a", false}) // Already dead so killing isn't noticed. p.Kill() w.StartSync() assertNoChange(c, ch) } func (s *PresenceSuite) TestWatchPeriod(c *gc.C) { presence.FakePeriod(1) presence.RealTimeSlot() w := presence.NewWatcher(s.presence) p := presence.NewPinger(s.presence, "a") defer w.Stop() defer p.Stop() ch := make(chan presence.Change) w.Watch("a", ch) assertChange(c, ch, presence.Change{"a", false}) // A single ping. c.Assert(p.Start(), gc.IsNil) c.Assert(p.Stop(), gc.IsNil) // Wait for next periodic refresh. time.Sleep(1 * time.Second) assertChange(c, ch, presence.Change{"a", true}) } func (s *PresenceSuite) TestWatchUnwatchOnQueue(c *gc.C) { w := presence.NewWatcher(s.presence) ch := make(chan presence.Change) for i := 0; i < 100; i++ { key := strconv.Itoa(i) c.Logf("Adding %q", key) w.Watch(key, ch) } for i := 1; i < 100; i += 2 { key := strconv.Itoa(i) c.Logf("Removing %q", key) w.Unwatch(key, ch) } alive := make(map[string]bool) for i := 0; i < 50; i++ { change := <-ch c.Logf("Got change for %q: %v", change.Key, change.Alive) alive[change.Key] = change.Alive } for i := 0; i < 100; i += 2 { key := strconv.Itoa(i) c.Logf("Checking %q...", key) c.Assert(alive[key], gc.Equals, false) } } func (s *PresenceSuite) TestRestartWithoutGaps(c *gc.C) { p := presence.NewPinger(s.presence, "a") c.Assert(p.Start(), gc.IsNil) defer p.Stop() done := make(chan bool) go func() { stop := false for !stop { if !c.Check(p.Stop(), gc.IsNil) { break } if !c.Check(p.Start(), gc.IsNil) { break } select { case stop = <-done: default: } } }() go func() { stop := false for !stop { w := presence.NewWatcher(s.presence) w.Sync() alive, err := w.Alive("a") c.Check(w.Stop(), gc.IsNil) if !c.Check(err, gc.IsNil) || !c.Check(alive, gc.Equals, true) { break } select { case stop = <-done: default: } } }() // TODO(jam): This forceful delay of 500ms sounds like a bad test, // since we always sleep for the full timeout time.Sleep(500 * time.Millisecond) done <- true done <- true } func (s *PresenceSuite) TestPingerPeriodAndResilience(c *gc.C) { // This test verifies both the periodic pinging, // and also a great property of the design: deaths // also expire, which means erroneous scenarios are // automatically recovered from. const period = 1 presence.FakePeriod(period) presence.RealTimeSlot() w := presence.NewWatcher(s.presence) p1 := presence.NewPinger(s.presence, "a") p2 := presence.NewPinger(s.presence, "a") defer w.Stop() defer p1.Stop() defer p2.Stop() // Start p1 and let it go on. c.Assert(p1.Start(), gc.IsNil) w.Sync() assertAlive(c, w, "a", true) // Start and kill p2, which will temporarily // invalidate p1 and set the key as dead. c.Assert(p2.Start(), gc.IsNil) c.Assert(p2.Kill(), gc.IsNil) w.Sync() assertAlive(c, w, "a", false) // Wait for two periods, and check again. Since // p1 is still alive, p2's death will expire and // the key will come back. time.Sleep(period * 2 * time.Second) w.Sync() assertAlive(c, w, "a", true) } func (s *PresenceSuite) TestStartSync(c *gc.C) { w := presence.NewWatcher(s.presence) p := presence.NewPinger(s.presence, "a") defer w.Stop() defer p.Stop() ch := make(chan presence.Change) w.Watch("a", ch) assertChange(c, ch, presence.Change{"a", false}) c.Assert(p.Start(), gc.IsNil) done := make(chan bool) go func() { w.StartSync() w.StartSync() w.StartSync() done <- true }() select { case <-done: case <-time.After(testing.LongWait): c.Fatalf("StartSync failed to return") } assertChange(c, ch, presence.Change{"a", true}) } func (s *PresenceSuite) TestSync(c *gc.C) { w := presence.NewWatcher(s.presence) p := presence.NewPinger(s.presence, "a") defer w.Stop() defer p.Stop() ch := make(chan presence.Change) w.Watch("a", ch) assertChange(c, ch, presence.Change{"a", false}) // Nothing to do here. w.Sync() c.Assert(p.Start(), gc.IsNil) done := make(chan bool) go func() { w.Sync() done <- true }() select { case <-done: c.Fatalf("Sync returned too early") // Note(jam): This used to wait 200ms to ensure that // Sync was actually blocked waiting for a presence // change. Is ShortWait long enough for this assurance? case <-time.After(testing.ShortWait): } assertChange(c, ch, presence.Change{"a", true}) select { case <-done: case <-time.After(testing.LongWait): c.Fatalf("Sync failed to returned") } } func (s *PresenceSuite) TestFindAllBeings(c *gc.C) { w := presence.NewWatcher(s.presence) p := presence.NewPinger(s.presence, "a") defer w.Stop() defer p.Stop() ch := make(chan presence.Change) w.Watch("a", ch) assertChange(c, ch, presence.Change{"a", false}) c.Assert(p.Start(), gc.IsNil) done := make(chan bool) go func() { w.Sync() done <- true }() assertChange(c, ch, presence.Change{"a", true}) results, err := presence.FindAllBeings(w) c.Assert(err, gc.IsNil) c.Assert(results, gc.HasLen, 1) select { case <-done: case <-time.After(testing.LongWait): c.Fatalf("Sync failed to returned") } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/presence/presence.go�����������������������������0000644�0000153�0000161�00000047164�12321735776�025770� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The presence package implements an interface for observing liveness // of arbitrary keys (agents, processes, etc) on top of MongoDB. // The design works by periodically updating the database so that // watchers can tell an arbitrary key is alive. package presence import ( "fmt" "strconv" "sync" "time" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "launchpad.net/tomb" "launchpad.net/juju-core/log" ) // Debug specifies whether the package will log debug // messages. // TODO(rog) allow debug level setting in the log package. var Debug = false // The implementation works by assigning a unique sequence number to each // pinger that is alive, and the pinger is then responsible for // periodically updating the current time slot document with its // sequence number so that watchers can tell it is alive. // // The internal implementation of the time slot document is as follows: // // { // "_id": <time slot>, // "alive": { hex(<pinger seq> / 63) : (1 << (<pinger seq> % 63) | <others>) }, // "dead": { hex(<pinger seq> / 63) : (1 << (<pinger seq> % 63) | <others>) }, // } // // All pingers that have their sequence number under "alive" and not // under "dead" are currently alive. This design enables implementing // a ping with a single update operation, a kill with another operation, // and obtaining liveness data with a single query that returns two // documents (the last two time slots). // // A new pinger sequence is obtained every time a pinger starts by // atomically incrementing a counter in a globally used document in a // helper collection. That sequence number is then inserted into the // beings collection to establish the mapping between pinger sequence // and key. // BUG(gn): The pings and beings collection currently grow without bound. // A Watcher can watch any number of pinger keys for liveness changes. type Watcher struct { tomb tomb.Tomb base *mgo.Collection pings *mgo.Collection beings *mgo.Collection // delta is an approximate clock skew between the local system // clock and the database clock. delta time.Duration // beingKey and beingSeq are the pinger seq <=> key mappings. // Entries in these maps are considered alive. beingKey map[int64]string beingSeq map[string]int64 // watches has the per-key observer channels from Watch/Unwatch. watches map[string][]chan<- Change // pending contains all the events to be dispatched to the watcher // channels. They're queued during processing and flushed at the // end to simplify the algorithm. pending []event // request is used to deliver requests from the public API into // the the gorotuine loop. request chan interface{} // syncDone contains pending done channels from sync requests. syncDone []chan bool // next will dispatch when it's time to sync the database // knowledge. It's maintained here so that ForceRefresh // can manipulate it to force a sync sooner. next <-chan time.Time } type event struct { ch chan<- Change key string alive bool } // Change holds a liveness change notification. type Change struct { Key string Alive bool } // NewWatcher returns a new Watcher. func NewWatcher(base *mgo.Collection) *Watcher { w := &Watcher{ base: base, pings: pingsC(base), beings: beingsC(base), beingKey: make(map[int64]string), beingSeq: make(map[string]int64), watches: make(map[string][]chan<- Change), request: make(chan interface{}), } go func() { w.tomb.Kill(w.loop()) w.tomb.Done() }() return w } // Stop stops all the watcher activities. func (w *Watcher) Stop() error { w.tomb.Kill(nil) return w.tomb.Wait() } // Dead returns a channel that is closed when the watcher has stopped. func (w *Watcher) Dead() <-chan struct{} { return w.tomb.Dead() } // Err returns the error with which the watcher stopped. // It returns nil if the watcher stopped cleanly, tomb.ErrStillAlive // if the watcher is still running properly, or the respective error // if the watcher is terminating or has terminated with an error. func (w *Watcher) Err() error { return w.tomb.Err() } type reqWatch struct { key string ch chan<- Change } type reqUnwatch struct { key string ch chan<- Change } type reqSync struct { done chan bool } type reqAlive struct { key string result chan bool } func (w *Watcher) sendReq(req interface{}) { select { case w.request <- req: case <-w.tomb.Dying(): } } // Watch starts watching the liveness of key. An event will // be sent onto ch to report the initial status for the key, and // from then on a new event will be sent whenever a change is // detected. Change values sent to the channel must be consumed, // or the whole watcher will blocked. func (w *Watcher) Watch(key string, ch chan<- Change) { w.sendReq(reqWatch{key, ch}) } // Unwatch stops watching the liveness of key via ch. func (w *Watcher) Unwatch(key string, ch chan<- Change) { w.sendReq(reqUnwatch{key, ch}) } // StartSync forces the watcher to load new events from the database. func (w *Watcher) StartSync() { w.sendReq(reqSync{nil}) } // Sync forces the watcher to load new events from the database and blocks // until all events have been dispatched. func (w *Watcher) Sync() { done := make(chan bool) w.sendReq(reqSync{done}) select { case <-done: case <-w.tomb.Dying(): } } // Alive returns whether the key is currently considered alive by w, // or an error in case the watcher is dying. func (w *Watcher) Alive(key string) (bool, error) { result := make(chan bool, 1) w.sendReq(reqAlive{key, result}) var alive bool select { case alive = <-result: case <-w.tomb.Dying(): return false, fmt.Errorf("cannot check liveness: watcher is dying") } return alive, nil } // period is the length of each time slot in seconds. // It's not a time.Duration because the code is more convenient like // this and also because sub-second timings don't work as the slot // identifier is an int64 in seconds. var period int64 = 30 // loop implements the main watcher loop. func (w *Watcher) loop() error { var err error if w.delta, err = clockDelta(w.base); err != nil { return err } w.next = time.After(0) for { select { case <-w.tomb.Dying(): return tomb.ErrDying case <-w.next: w.next = time.After(time.Duration(period) * time.Second) syncDone := w.syncDone w.syncDone = nil if err := w.sync(); err != nil { return err } w.flush() for _, done := range syncDone { close(done) } case req := <-w.request: w.handle(req) w.flush() } } return nil } // flush sends all pending events to their respective channels. func (w *Watcher) flush() { // w.pending may get new requests as we handle other requests. for i := 0; i < len(w.pending); i++ { e := &w.pending[i] for e.ch != nil { select { case <-w.tomb.Dying(): return case req := <-w.request: w.handle(req) continue case e.ch <- Change{e.key, e.alive}: } break } } w.pending = w.pending[:0] } // handle deals with requests delivered by the public API // onto the background watcher goroutine. func (w *Watcher) handle(req interface{}) { debugf("state/presence: got request: %#v", req) switch r := req.(type) { case reqSync: w.next = time.After(0) if r.done != nil { w.syncDone = append(w.syncDone, r.done) } case reqWatch: for _, ch := range w.watches[r.key] { if ch == r.ch { panic("adding channel twice for same key") } } w.watches[r.key] = append(w.watches[r.key], r.ch) _, alive := w.beingSeq[r.key] w.pending = append(w.pending, event{r.ch, r.key, alive}) case reqUnwatch: watches := w.watches[r.key] for i, ch := range watches { if ch == r.ch { watches[i] = watches[len(watches)-1] w.watches[r.key] = watches[:len(watches)-1] break } } for i := range w.pending { e := &w.pending[i] if e.key == r.key && e.ch == r.ch { e.ch = nil } } case reqAlive: _, alive := w.beingSeq[r.key] r.result <- alive default: panic(fmt.Errorf("unknown request: %T", req)) } } type beingInfo struct { Seq int64 "_id,omitempty" Key string "key,omitempty" } type pingInfo struct { Slot int64 "_id" Alive map[string]int64 ",omitempty" Dead map[string]int64 ",omitempty" } func (w *Watcher) findAllBeings() (map[int64]beingInfo, error) { beings := make([]beingInfo, 0) err := w.beings.Find(bson.D{{}}).All(&beings) if err != nil { return nil, err } beingInfos := make(map[int64]beingInfo, len(beings)) for _, being := range beings { beingInfos[being.Seq] = being } return beingInfos, nil } // sync updates the watcher knowledge from the database, and // queues events to observing channels. It fetches the last two time // slots and compares the union of both to the in-memory state. func (w *Watcher) sync() error { var allBeings map[int64]beingInfo if len(w.beingKey) == 0 { // The very first time we sync, we grab all ever-known beings, // so we don't have to look them up one-by-one var err error if allBeings, err = w.findAllBeings(); err != nil { return err } } slot := timeSlot(time.Now(), w.delta) var ping []pingInfo err := w.pings.Find(bson.D{{"$or", []pingInfo{{Slot: slot}, {Slot: slot - period}}}}).All(&ping) if err != nil && err == mgo.ErrNotFound { return err } // Learn about all enforced deaths. dead := make(map[int64]bool) for i := range ping { for key, value := range ping[i].Dead { k, err := strconv.ParseInt(key, 16, 64) if err != nil { panic(fmt.Errorf("presence cannot parse dead key: %q", key)) } k *= 63 for i := int64(0); i < 63 && value > 0; i++ { on := value&1 == 1 value >>= 1 if !on { continue } seq := k + i dead[seq] = true debugf("state/presence: found seq=%d dead", seq) } } } // Learn about all the pingers that reported and queue // events for those that weren't known to be alive and // are not reportedly dead either. alive := make(map[int64]bool) being := beingInfo{} for i := range ping { for key, value := range ping[i].Alive { k, err := strconv.ParseInt(key, 16, 64) if err != nil { panic(fmt.Errorf("presence cannot parse alive key: %q", key)) } k *= 63 for i := int64(0); i < 63 && value > 0; i++ { on := value&1 == 1 value >>= 1 if !on { continue } seq := k + i alive[seq] = true if _, ok := w.beingKey[seq]; ok { continue } // Check if the being exists in the 'all' map, // otherwise do a single lookup in mongo var ok bool if being, ok = allBeings[seq]; !ok { err := w.beings.Find(bson.D{{"_id", seq}}).One(&being) if err == mgo.ErrNotFound { debugf("state/presence: found seq=%d unowned", seq) continue } else if err != nil { return err } } cur := w.beingSeq[being.Key] if cur < seq { delete(w.beingKey, cur) } else { // Current sequence is more recent. continue } w.beingKey[seq] = being.Key w.beingSeq[being.Key] = seq if cur > 0 || dead[seq] { continue } debugf("state/presence: found seq=%d alive with key %q", seq, being.Key) for _, ch := range w.watches[being.Key] { w.pending = append(w.pending, event{ch, being.Key, true}) } } } } // Pingers that were known to be alive and haven't reported // in the last two slots are now considered dead. Dispatch // the respective events and forget their sequences. for seq, key := range w.beingKey { if dead[seq] || !alive[seq] { delete(w.beingKey, seq) delete(w.beingSeq, key) for _, ch := range w.watches[key] { w.pending = append(w.pending, event{ch, key, false}) } } } return nil } // Pinger periodically reports that a specific key is alive, so that // watchers interested on that fact can react appropriately. type Pinger struct { mu sync.Mutex tomb tomb.Tomb base *mgo.Collection pings *mgo.Collection started bool beingKey string beingSeq int64 fieldKey string // hex(beingKey / 63) fieldBit uint64 // 1 << (beingKey%63) lastSlot int64 delta time.Duration } // NewPinger returns a new Pinger to report that key is alive. // It starts reporting after Start is called. func NewPinger(base *mgo.Collection, key string) *Pinger { return &Pinger{base: base, pings: pingsC(base), beingKey: key} } // Start starts periodically reporting that p's key is alive. func (p *Pinger) Start() error { p.mu.Lock() defer p.mu.Unlock() if p.started { return fmt.Errorf("pinger already started") } p.tomb = tomb.Tomb{} if err := p.prepare(); err != nil { return err } debugf("state/presence: starting pinger for %q with seq=%d", p.beingKey, p.beingSeq) if err := p.ping(); err != nil { return err } p.started = true go func() { p.tomb.Kill(p.loop()) p.tomb.Done() }() return nil } // Stop stops p's periodical ping. // Watchers will not notice p has stopped pinging until the // previous ping times out. func (p *Pinger) Stop() error { p.mu.Lock() defer p.mu.Unlock() if p.started { debugf("state/presence: stopping pinger for %q with seq=%d", p.beingKey, p.beingSeq) } p.tomb.Kill(nil) err := p.tomb.Wait() // TODO ping one more time to guarantee a late timeout. p.started = false return err } // Stop stops p's periodical ping and immediately report that it is dead. func (p *Pinger) Kill() error { p.mu.Lock() defer p.mu.Unlock() if p.started { debugf("state/presence: killing pinger for %q (was started)", p.beingKey) return p.killStarted() } debugf("state/presence: killing pinger for %q (was stopped)", p.beingKey) return p.killStopped() } // killStarted kills the pinger while it is running, by first // stopping it and then recording in the last pinged slot that // the pinger was killed. func (p *Pinger) killStarted() error { p.tomb.Kill(nil) killErr := p.tomb.Wait() p.started = false slot := p.lastSlot udoc := bson.D{{"$inc", bson.D{{"dead." + p.fieldKey, p.fieldBit}}}} if _, err := p.pings.UpsertId(slot, udoc); err != nil { return err } return killErr } // killStopped kills the pinger while it is not running, by // first allocating a new sequence, and then atomically recording // the new sequence both as alive and dead at once. func (p *Pinger) killStopped() error { if err := p.prepare(); err != nil { return err } slot := timeSlot(time.Now(), p.delta) udoc := bson.D{{"$inc", bson.D{ {"dead." + p.fieldKey, p.fieldBit}, {"alive." + p.fieldKey, p.fieldBit}, }}} _, err := p.pings.UpsertId(slot, udoc) return err } // loop is the main pinger loop that runs while it is // in started state. func (p *Pinger) loop() error { for { select { case <-p.tomb.Dying(): return tomb.ErrDying case <-time.After(time.Duration(float64(period+1)*0.75) * time.Second): if err := p.ping(); err != nil { return err } } } } // prepare allocates a new unique sequence for the // pinger key and prepares the pinger to use it. func (p *Pinger) prepare() error { change := mgo.Change{ Update: bson.D{{"$inc", bson.D{{"seq", int64(1)}}}}, Upsert: true, ReturnNew: true, } seqs := seqsC(p.base) var seq struct{ Seq int64 } if _, err := seqs.FindId("beings").Apply(change, &seq); err != nil { return err } p.beingSeq = seq.Seq p.fieldKey = fmt.Sprintf("%x", p.beingSeq/63) p.fieldBit = 1 << uint64(p.beingSeq%63) p.lastSlot = 0 beings := beingsC(p.base) return beings.Insert(beingInfo{p.beingSeq, p.beingKey}) } // ping records updates the current time slot with the // sequence in use by the pinger. func (p *Pinger) ping() error { debugf("state/presence: pinging %q with seq=%d", p.beingKey, p.beingSeq) if p.delta == 0 { delta, err := clockDelta(p.base) if err != nil { return err } p.delta = delta } slot := timeSlot(time.Now(), p.delta) if slot == p.lastSlot { // Never, ever, ping the same slot twice. // The increment below would corrupt the slot. return nil } p.lastSlot = slot if _, err := p.pings.UpsertId(slot, bson.D{{"$inc", bson.D{{"alive." + p.fieldKey, p.fieldBit}}}}); err != nil { return err } return nil } // clockDelta returns the approximate skew between // the local clock and the database clock. func clockDelta(c *mgo.Collection) (time.Duration, error) { var server struct { time.Time "retval" } var isMaster struct { LocalTime time.Time "localTime" } var after time.Time var before time.Time var serverDelay time.Duration supportsMasterLocalTime := true for i := 0; i < 10; i++ { if supportsMasterLocalTime { // Try isMaster.localTime, which is present since MongoDB 2.2 // and does not require admin privileges. before = time.Now() err := c.Database.Run("isMaster", &isMaster) after = time.Now() if err != nil { return 0, err } if isMaster.LocalTime.IsZero() { supportsMasterLocalTime = false continue } else { serverDelay = isMaster.LocalTime.Sub(before) } } else { // If MongoDB doesn't have localTime as part of // isMaster result, it means that the server is likely // a MongoDB older than 2.2. // // Fallback to 'eval' works fine on versions older than // 2.4 where it does not require admin privileges. // // NOTE: 'eval' takes a global write lock unless you // specify 'nolock' (which we are not doing below, for // no apparent reason), so it is quite likely that the // eval could take a relatively long time to acquire // the lock and thus cause a retry on the callDelay // check below on a busy server. before = time.Now() err := c.Database.Run(bson.D{{"$eval", "function() { return new Date(); }"}}, &server) after = time.Now() if err != nil { return 0, err } serverDelay = server.Sub(before) } // If the call to the server takes longer than a few seconds we // retry it a couple more times before giving up. It is unclear // why the retry would help at all here. // // If the server takes longer than the specified amount of time // on every single try, then we simply give up. callDelay := after.Sub(before) if callDelay > 5*time.Second { continue } return serverDelay, nil } return 0, fmt.Errorf("cannot synchronize clock with database server") } // timeSlot returns the current time slot, in seconds since the // epoch, for the provided now time. The delta skew is applied // to the now time to improve the synchronization with a // centrally agreed time. // // The result of this method may be manipulated for test purposes // by fakeTimeSlot and realTimeSlot. func timeSlot(now time.Time, delta time.Duration) int64 { fakeMutex.Lock() fake := !fakeNow.IsZero() if fake { now = fakeNow } slot := now.Add(delta).Unix() slot -= slot % period if fake { slot += int64(fakeOffset) * period } fakeMutex.Unlock() return slot } var ( fakeMutex sync.Mutex // protects fakeOffset, fakeNow fakeNow time.Time fakeOffset int ) // fakeTimeSlot hardcodes the slot time returned by the timeSlot // function for testing purposes. The offset parameter is the slot // position to return: offsets +1 and -1 are +period and -period // seconds from slot 0, respectively. func fakeTimeSlot(offset int) { fakeMutex.Lock() if fakeNow.IsZero() { fakeNow = time.Now() } fakeOffset = offset fakeMutex.Unlock() log.Infof("state/presence: Faking presence to time slot %d", offset) } // realTimeSlot disables the hardcoding introduced by fakeTimeSlot. func realTimeSlot() { fakeMutex.Lock() fakeNow = time.Time{} fakeOffset = 0 fakeMutex.Unlock() log.Infof("state/presence: Not faking presence time. Real time slot in use.") } func seqsC(base *mgo.Collection) *mgo.Collection { return base.Database.C(base.Name + ".seqs") } func beingsC(base *mgo.Collection) *mgo.Collection { return base.Database.C(base.Name + ".beings") } func pingsC(base *mgo.Collection) *mgo.Collection { return base.Database.C(base.Name + ".pings") } func debugf(f string, a ...interface{}) { if Debug { log.Debugf(f, a...) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/status.go����������������������������������������0000644�0000153�0000161�00000005353�12321735642�023665� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state/api/params" ) // statusDoc represents a entity status in Mongodb. The implicit // _id field is explicitly set to the global key of the associated // entity in the document's creation transaction, but omitted to allow // direct use of the document in both create and update transactions. type statusDoc struct { Status params.Status StatusInfo string StatusData params.StatusData } // validateSet returns an error if the statusDoc does not represent a sane // SetStatus operation. func (doc statusDoc) validateSet(allowPending bool) error { if !doc.Status.Valid() { return fmt.Errorf("cannot set invalid status %q", doc.Status) } switch doc.Status { case params.StatusPending: if !allowPending { return fmt.Errorf("cannot set status %q", doc.Status) } case params.StatusDown: return fmt.Errorf("cannot set status %q", doc.Status) case params.StatusError: if doc.StatusInfo == "" { return fmt.Errorf("cannot set status %q without info", doc.Status) } } if doc.StatusData != nil && doc.Status != params.StatusError { return fmt.Errorf("cannot set status data when status is %q", doc.Status) } return nil } // getStatus retrieves the status document associated with the given // globalKey and copies it to outStatusDoc, which needs to be created // by the caller before. func getStatus(st *State, globalKey string) (statusDoc, error) { var doc statusDoc err := st.statuses.FindId(globalKey).One(&doc) if err == mgo.ErrNotFound { return statusDoc{}, errors.NotFoundf("status") } if err != nil { return statusDoc{}, fmt.Errorf("cannot get status %q: %v", globalKey, err) } return doc, nil } // createStatusOp returns the operation needed to create the given // status document associated with the given globalKey. func createStatusOp(st *State, globalKey string, doc statusDoc) txn.Op { return txn.Op{ C: st.statuses.Name, Id: globalKey, Assert: txn.DocMissing, Insert: doc, } } // updateStatusOp returns the operations needed to update the given // status document associated with the given globalKey. func updateStatusOp(st *State, globalKey string, doc statusDoc) txn.Op { return txn.Op{ C: st.statuses.Name, Id: globalKey, Assert: txn.DocExists, Update: bson.D{{"$set", doc}}, } } // removeStatusOp returns the operation needed to remove the status // document associated with the given globalKey. func removeStatusOp(st *State, globalKey string) txn.Op { return txn.Op{ C: st.statuses.Name, Id: globalKey, Remove: true, } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/relation_test.go���������������������������������0000644�0000153�0000161�00000024670�12321735642�025221� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state" ) type RelationSuite struct { ConnSuite } var _ = gc.Suite(&RelationSuite{}) func (s *RelationSuite) TestAddRelationErrors(c *gc.C) { wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) wordpressEP, err := wordpress.Endpoint("db") c.Assert(err, gc.IsNil) mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) mysqlEP, err := mysql.Endpoint("server") c.Assert(err, gc.IsNil) riak := s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak")) riakEP, err := riak.Endpoint("ring") c.Assert(err, gc.IsNil) // Check we can't add a relation with services that don't exist. yoursqlEP := mysqlEP yoursqlEP.ServiceName = "yoursql" _, err = s.State.AddRelation(yoursqlEP, wordpressEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db yoursql:server": service "yoursql" does not exist`) assertNoRelations(c, wordpress) assertNoRelations(c, mysql) // Check that interfaces have to match. msep3 := mysqlEP msep3.Interface = "roflcopter" _, err = s.State.AddRelation(msep3, wordpressEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": endpoints do not relate`) assertNoRelations(c, wordpress) assertNoRelations(c, mysql) // Check a variety of surprising endpoint combinations. _, err = s.State.AddRelation(wordpressEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db": relation must have two endpoints`) assertNoRelations(c, wordpress) _, err = s.State.AddRelation(riakEP, wordpressEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db riak:ring": endpoints do not relate`) assertOneRelation(c, riak, 0, riakEP) assertNoRelations(c, wordpress) _, err = s.State.AddRelation(riakEP, riakEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "riak:ring riak:ring": endpoints do not relate`) assertOneRelation(c, riak, 0, riakEP) _, err = s.State.AddRelation() c.Assert(err, gc.ErrorMatches, `cannot add relation "": relation must have two endpoints`) _, err = s.State.AddRelation(mysqlEP, wordpressEP, riakEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server riak:ring": relation must have two endpoints`) assertOneRelation(c, riak, 0, riakEP) assertNoRelations(c, wordpress) assertNoRelations(c, mysql) // Check that a relation can't be added to a Dying service. _, err = wordpress.AddUnit() c.Assert(err, gc.IsNil) err = wordpress.Destroy() c.Assert(err, gc.IsNil) _, err = s.State.AddRelation(mysqlEP, wordpressEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": service "wordpress" is not alive`) assertNoRelations(c, wordpress) assertNoRelations(c, mysql) } func (s *RelationSuite) TestRetrieveSuccess(c *gc.C) { wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) wordpressEP, err := wordpress.Endpoint("db") c.Assert(err, gc.IsNil) mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) mysqlEP, err := mysql.Endpoint("server") c.Assert(err, gc.IsNil) expect, err := s.State.AddRelation(wordpressEP, mysqlEP) c.Assert(err, gc.IsNil) rel, err := s.State.EndpointsRelation(wordpressEP, mysqlEP) check := func() { c.Assert(err, gc.IsNil) c.Assert(rel.Id(), gc.Equals, expect.Id()) c.Assert(rel.String(), gc.Equals, expect.String()) } check() rel, err = s.State.EndpointsRelation(mysqlEP, wordpressEP) check() rel, err = s.State.Relation(expect.Id()) check() } func (s *RelationSuite) TestRetrieveNotFound(c *gc.C) { subway := state.Endpoint{ ServiceName: "subway", Relation: charm.Relation{ Name: "db", Interface: "mongodb", Role: charm.RoleRequirer, Scope: charm.ScopeGlobal, }, } mongo := state.Endpoint{ ServiceName: "mongo", Relation: charm.Relation{ Name: "server", Interface: "mongodb", Role: charm.RoleProvider, Scope: charm.ScopeGlobal, }, } _, err := s.State.EndpointsRelation(subway, mongo) c.Assert(err, gc.ErrorMatches, `relation "subway:db mongo:server" not found`) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) _, err = s.State.Relation(999) c.Assert(err, gc.ErrorMatches, `relation 999 not found`) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *RelationSuite) TestAddRelation(c *gc.C) { // Add a relation. wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) wordpressEP, err := wordpress.Endpoint("db") c.Assert(err, gc.IsNil) mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) mysqlEP, err := mysql.Endpoint("server") c.Assert(err, gc.IsNil) _, err = s.State.AddRelation(wordpressEP, mysqlEP) c.Assert(err, gc.IsNil) assertOneRelation(c, mysql, 0, mysqlEP, wordpressEP) assertOneRelation(c, wordpress, 0, wordpressEP, mysqlEP) // Check we cannot re-add the same relation, regardless of endpoint ordering. _, err = s.State.AddRelation(mysqlEP, wordpressEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": relation already exists`) _, err = s.State.AddRelation(wordpressEP, mysqlEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": relation already exists`) assertOneRelation(c, mysql, 0, mysqlEP, wordpressEP) assertOneRelation(c, wordpress, 0, wordpressEP, mysqlEP) } func (s *RelationSuite) TestAddRelationSeriesNeedNotMatch(c *gc.C) { wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) wordpressEP, err := wordpress.Endpoint("db") c.Assert(err, gc.IsNil) mysql := s.AddTestingService(c, "mysql", s.AddSeriesCharm(c, "mysql", "otherseries")) mysqlEP, err := mysql.Endpoint("server") c.Assert(err, gc.IsNil) _, err = s.State.AddRelation(wordpressEP, mysqlEP) c.Assert(err, gc.IsNil) assertOneRelation(c, mysql, 0, mysqlEP, wordpressEP) assertOneRelation(c, wordpress, 0, wordpressEP, mysqlEP) } func (s *RelationSuite) TestAddContainerRelation(c *gc.C) { // Add a relation. wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) wordpressEP, err := wordpress.Endpoint("juju-info") c.Assert(err, gc.IsNil) logging := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) loggingEP, err := logging.Endpoint("info") c.Assert(err, gc.IsNil) _, err = s.State.AddRelation(wordpressEP, loggingEP) c.Assert(err, gc.IsNil) // Check that the endpoints both have container scope. wordpressEP.Scope = charm.ScopeContainer assertOneRelation(c, logging, 0, loggingEP, wordpressEP) assertOneRelation(c, wordpress, 0, wordpressEP, loggingEP) // Check we cannot re-add the same relation, regardless of endpoint ordering. _, err = s.State.AddRelation(loggingEP, wordpressEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "logging:info wordpress:juju-info": relation already exists`) _, err = s.State.AddRelation(wordpressEP, loggingEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "logging:info wordpress:juju-info": relation already exists`) assertOneRelation(c, logging, 0, loggingEP, wordpressEP) assertOneRelation(c, wordpress, 0, wordpressEP, loggingEP) } func (s *RelationSuite) TestAddContainerRelationSeriesMustMatch(c *gc.C) { wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) wordpressEP, err := wordpress.Endpoint("juju-info") c.Assert(err, gc.IsNil) logging := s.AddTestingService(c, "logging", s.AddSeriesCharm(c, "logging", "otherseries")) loggingEP, err := logging.Endpoint("info") c.Assert(err, gc.IsNil) _, err = s.State.AddRelation(wordpressEP, loggingEP) c.Assert(err, gc.ErrorMatches, `cannot add relation "logging:info wordpress:juju-info": principal and subordinate services' series must match`) } func (s *RelationSuite) TestDestroyRelation(c *gc.C) { wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) // Test that the relation can be destroyed. err = rel.Destroy() c.Assert(err, gc.IsNil) err = rel.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) assertNoRelations(c, wordpress) assertNoRelations(c, mysql) // Check that a second destroy is a no-op. err = rel.Destroy() c.Assert(err, gc.IsNil) // Create a new relation and check that refreshing the old does not find // the new. _, err = s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) err = rel.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *RelationSuite) TestDestroyPeerRelation(c *gc.C) { // Check that a peer relation cannot be destroyed directly. riakch := s.AddTestingCharm(c, "riak") riak := s.AddTestingService(c, "riak", riakch) riakEP, err := riak.Endpoint("ring") c.Assert(err, gc.IsNil) rel := assertOneRelation(c, riak, 0, riakEP) err = rel.Destroy() c.Assert(err, gc.ErrorMatches, `cannot destroy relation "riak:ring": is a peer relation`) assertOneRelation(c, riak, 0, riakEP) // Check that it is destroyed when the service is destroyed. err = riak.Destroy() c.Assert(err, gc.IsNil) assertNoRelations(c, riak) err = rel.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // Create a new service (and hence a new relation in the background); check // that refreshing the old one does not accidentally get the new one. newriak := s.AddTestingService(c, "riak", riakch) assertOneRelation(c, newriak, 1, riakEP) err = rel.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func assertNoRelations(c *gc.C, srv *state.Service) { rels, err := srv.Relations() c.Assert(err, gc.IsNil) c.Assert(rels, gc.HasLen, 0) } func assertOneRelation(c *gc.C, srv *state.Service, relId int, endpoints ...state.Endpoint) *state.Relation { rels, err := srv.Relations() c.Assert(err, gc.IsNil) c.Assert(rels, gc.HasLen, 1) rel := rels[0] c.Assert(rel.Id(), gc.Equals, relId) name := srv.Name() expectEp := endpoints[0] ep, err := rel.Endpoint(name) c.Assert(err, gc.IsNil) c.Assert(ep, gc.DeepEquals, expectEp) if len(endpoints) == 2 { expectEp = endpoints[1] } eps, err := rel.RelatedEndpoints(name) c.Assert(err, gc.IsNil) c.Assert(eps, gc.DeepEquals, []state.Endpoint{expectEp}) return rel } ������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/state_test.go������������������������������������0000644�0000153�0000161�00000274562�12321735776�024543� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( "fmt" "net/url" "sort" "strconv" "time" jc "github.com/juju/testing/checkers" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/replicaset" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" statetesting "launchpad.net/juju-core/state/testing" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) var goodPassword = "foo-12345678901234567890" var alternatePassword = "bar-12345678901234567890" // preventUnitDestroyRemove sets a non-pending status on the unit, and hence // prevents it from being unceremoniously removed from state on Destroy. This // is useful because several tests go through a unit's lifecycle step by step, // asserting the behaviour of a given method in each state, and the unit quick- // remove change caused many of these to fail. func preventUnitDestroyRemove(c *gc.C, u *state.Unit) { err := u.SetStatus(params.StatusStarted, "", nil) c.Assert(err, gc.IsNil) } type StateSuite struct { ConnSuite } var _ = gc.Suite(&StateSuite{}) func (s *StateSuite) TestDialAgain(c *gc.C) { // Ensure idempotent operations on Dial are working fine. for i := 0; i < 2; i++ { st, err := state.Open(state.TestingStateInfo(), state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) c.Assert(st.Close(), gc.IsNil) } } func (s *StateSuite) TestMongoSession(c *gc.C) { session := s.State.MongoSession() c.Assert(session.Ping(), gc.IsNil) } func (s *StateSuite) TestAddresses(c *gc.C) { var err error machines := make([]*state.Machine, 4) machines[0], err = s.State.AddMachine("quantal", state.JobManageEnviron, state.JobHostUnits) c.Assert(err, gc.IsNil) machines[1], err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = s.State.EnsureAvailability(3, constraints.Value{}, "quantal") c.Assert(err, gc.IsNil) machines[2], err = s.State.Machine("2") c.Assert(err, gc.IsNil) machines[3], err = s.State.Machine("3") c.Assert(err, gc.IsNil) for i, m := range machines { err := m.SetAddresses([]instance.Address{{ Type: instance.Ipv4Address, NetworkScope: instance.NetworkCloudLocal, Value: fmt.Sprintf("10.0.0.%d", i), }, { Type: instance.Ipv6Address, NetworkScope: instance.NetworkCloudLocal, Value: "::1", }, { Type: instance.Ipv4Address, NetworkScope: instance.NetworkMachineLocal, Value: "127.0.0.1", }, { Type: instance.Ipv4Address, NetworkScope: instance.NetworkPublic, Value: "5.4.3.2", }}) c.Assert(err, gc.IsNil) } envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) addrs, err := s.State.Addresses() c.Assert(err, gc.IsNil) c.Assert(addrs, gc.HasLen, 3) c.Assert(addrs, jc.SameContents, []string{ fmt.Sprintf("10.0.0.0:%d", envConfig.StatePort()), fmt.Sprintf("10.0.0.2:%d", envConfig.StatePort()), fmt.Sprintf("10.0.0.3:%d", envConfig.StatePort()), }) addrs, err = s.State.APIAddressesFromMachines() c.Assert(err, gc.IsNil) c.Assert(addrs, gc.HasLen, 3) c.Assert(addrs, jc.SameContents, []string{ fmt.Sprintf("10.0.0.0:%d", envConfig.APIPort()), fmt.Sprintf("10.0.0.2:%d", envConfig.APIPort()), fmt.Sprintf("10.0.0.3:%d", envConfig.APIPort()), }) } func (s *StateSuite) TestPing(c *gc.C) { c.Assert(s.State.Ping(), gc.IsNil) testing.MgoServer.Restart() c.Assert(s.State.Ping(), gc.NotNil) } func (s *StateSuite) TestIsNotFound(c *gc.C) { err1 := fmt.Errorf("unrelated error") err2 := errors.NotFoundf("foo") c.Assert(err1, gc.Not(jc.Satisfies), errors.IsNotFoundError) c.Assert(err2, jc.Satisfies, errors.IsNotFoundError) } func (s *StateSuite) dummyCharm(c *gc.C, curlOverride string) (ch charm.Charm, curl *charm.URL, bundleURL *url.URL, bundleSHA256 string) { var err error ch = testing.Charms.Dir("dummy") if curlOverride != "" { curl = charm.MustParseURL(curlOverride) } else { curl = charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", ch.Meta().Name, ch.Revision()), ) } bundleURL, err = url.Parse("http://bundles.testing.invalid/dummy-1") c.Assert(err, gc.IsNil) bundleSHA256 = "dummy-1-sha256" return ch, curl, bundleURL, bundleSHA256 } func (s *StateSuite) TestAddCharm(c *gc.C) { // Check that adding charms from scratch works correctly. ch, curl, bundleURL, bundleSHA256 := s.dummyCharm(c, "") dummy, err := s.State.AddCharm(ch, curl, bundleURL, bundleSHA256) c.Assert(err, gc.IsNil) c.Assert(dummy.URL().String(), gc.Equals, curl.String()) doc := state.CharmDoc{} err = s.charms.FindId(curl).One(&doc) c.Assert(err, gc.IsNil) c.Logf("%#v", doc) c.Assert(doc.URL, gc.DeepEquals, curl) } func (s *StateSuite) TestAddCharmUpdatesPlaceholder(c *gc.C) { // Check that adding charms updates any existing placeholder charm // with the same URL. ch := testing.Charms.Dir("dummy") // Add a placeholder charm. curl := charm.MustParseURL("cs:quantal/dummy-1") err := s.State.AddStoreCharmPlaceholder(curl) c.Assert(err, gc.IsNil) // Add a deployed charm. bundleURL, err := url.Parse("http://bundles.testing.invalid/dummy-1") c.Assert(err, gc.IsNil) bundleSHA256 := "dummy-1-sha256" dummy, err := s.State.AddCharm(ch, curl, bundleURL, bundleSHA256) c.Assert(err, gc.IsNil) c.Assert(dummy.URL().String(), gc.Equals, curl.String()) // Charm doc has been updated. var docs []state.CharmDoc err = s.charms.FindId(curl).All(&docs) c.Assert(err, gc.IsNil) c.Assert(docs, gc.HasLen, 1) c.Assert(docs[0].URL, gc.DeepEquals, curl) c.Assert(docs[0].BundleURL, gc.DeepEquals, bundleURL) // No more placeholder charm. _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *StateSuite) assertPendingCharmExists(c *gc.C, curl *charm.URL) { // Find charm directly and verify only the charm URL and // PendingUpload are set. doc := state.CharmDoc{} err := s.charms.FindId(curl).One(&doc) c.Assert(err, gc.IsNil) c.Logf("%#v", doc) c.Assert(doc.URL, gc.DeepEquals, curl) c.Assert(doc.PendingUpload, jc.IsTrue) c.Assert(doc.Placeholder, jc.IsFalse) c.Assert(doc.Meta, gc.IsNil) c.Assert(doc.Config, gc.IsNil) c.Assert(doc.BundleURL, gc.IsNil) c.Assert(doc.BundleSha256, gc.Equals, "") // Make sure we can't find it with st.Charm(). _, err = s.State.Charm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *StateSuite) TestPrepareLocalCharmUpload(c *gc.C) { // First test the sanity checks. curl, err := s.State.PrepareLocalCharmUpload(charm.MustParseURL("local:quantal/dummy")) c.Assert(err, gc.ErrorMatches, "expected charm URL with revision, got .*") c.Assert(curl, gc.IsNil) curl, err = s.State.PrepareLocalCharmUpload(charm.MustParseURL("cs:quantal/dummy")) c.Assert(err, gc.ErrorMatches, "expected charm URL with local schema, got .*") c.Assert(curl, gc.IsNil) // No charm in state, so the call should respect given revision. testCurl := charm.MustParseURL("local:quantal/missing-123") curl, err = s.State.PrepareLocalCharmUpload(testCurl) c.Assert(err, gc.IsNil) c.Assert(curl, gc.DeepEquals, testCurl) s.assertPendingCharmExists(c, curl) // Try adding it again with the same revision and ensure it gets bumped. curl, err = s.State.PrepareLocalCharmUpload(curl) c.Assert(err, gc.IsNil) c.Assert(curl.Revision, gc.Equals, 124) // Also ensure the revision cannot decrease. curl, err = s.State.PrepareLocalCharmUpload(curl.WithRevision(42)) c.Assert(err, gc.IsNil) c.Assert(curl.Revision, gc.Equals, 125) // Check the given revision is respected. curl, err = s.State.PrepareLocalCharmUpload(curl.WithRevision(1234)) c.Assert(err, gc.IsNil) c.Assert(curl.Revision, gc.Equals, 1234) } func (s *StateSuite) TestPrepareStoreCharmUpload(c *gc.C) { // First test the sanity checks. sch, err := s.State.PrepareStoreCharmUpload(charm.MustParseURL("cs:quantal/dummy")) c.Assert(err, gc.ErrorMatches, "expected charm URL with revision, got .*") c.Assert(sch, gc.IsNil) sch, err = s.State.PrepareStoreCharmUpload(charm.MustParseURL("local:quantal/dummy")) c.Assert(err, gc.ErrorMatches, "expected charm URL with cs schema, got .*") c.Assert(sch, gc.IsNil) // No charm in state, so the call should respect given revision. testCurl := charm.MustParseURL("cs:quantal/missing-123") sch, err = s.State.PrepareStoreCharmUpload(testCurl) c.Assert(err, gc.IsNil) c.Assert(sch.URL(), gc.DeepEquals, testCurl) c.Assert(sch.IsUploaded(), jc.IsFalse) s.assertPendingCharmExists(c, sch.URL()) // Try adding it again with the same revision and ensure we get the same document. schCopy, err := s.State.PrepareStoreCharmUpload(testCurl) c.Assert(err, gc.IsNil) c.Assert(sch, jc.DeepEquals, schCopy) // Now add a charm and try again - we should get the same result // as with AddCharm. ch, curl, bundleURL, bundleSHA256 := s.dummyCharm(c, "cs:precise/dummy-2") sch, err = s.State.AddCharm(ch, curl, bundleURL, bundleSHA256) c.Assert(err, gc.IsNil) schCopy, err = s.State.PrepareStoreCharmUpload(curl) c.Assert(err, gc.IsNil) c.Assert(sch, jc.DeepEquals, schCopy) // Finally, try poking around the state with a placeholder and // bundlesha256 to make sure we do the right thing. curl = curl.WithRevision(999) first := state.TransactionHook{ Before: func() { err := s.State.AddStoreCharmPlaceholder(curl) c.Assert(err, gc.IsNil) }, After: func() { err := s.charms.RemoveId(curl) c.Assert(err, gc.IsNil) }, } second := state.TransactionHook{ Before: func() { err := s.State.AddStoreCharmPlaceholder(curl) c.Assert(err, gc.IsNil) }, After: func() { err := s.charms.UpdateId(curl, bson.D{{"$set", bson.D{ {"bundlesha256", "fake"}}, }}) c.Assert(err, gc.IsNil) }, } defer state.SetTransactionHooks( c, s.State, first, second, first, ).Check() _, err = s.State.PrepareStoreCharmUpload(curl) c.Assert(err, gc.Equals, state.ErrExcessiveContention) } func (s *StateSuite) TestUpdateUploadedCharm(c *gc.C) { ch, curl, bundleURL, bundleSHA256 := s.dummyCharm(c, "") _, err := s.State.AddCharm(ch, curl, bundleURL, bundleSHA256) c.Assert(err, gc.IsNil) // Test with already uploaded and a missing charms. sch, err := s.State.UpdateUploadedCharm(ch, curl, bundleURL, bundleSHA256) c.Assert(err, gc.ErrorMatches, fmt.Sprintf("charm %q already uploaded", curl)) c.Assert(sch, gc.IsNil) missingCurl := charm.MustParseURL("local:quantal/missing-1") sch, err = s.State.UpdateUploadedCharm(ch, missingCurl, bundleURL, "missing") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) c.Assert(sch, gc.IsNil) // Test with with an uploaded local charm. _, err = s.State.PrepareLocalCharmUpload(missingCurl) c.Assert(err, gc.IsNil) sch, err = s.State.UpdateUploadedCharm(ch, missingCurl, bundleURL, "missing") c.Assert(err, gc.IsNil) c.Assert(sch.URL(), gc.DeepEquals, missingCurl) c.Assert(sch.Revision(), gc.Equals, missingCurl.Revision) c.Assert(sch.IsUploaded(), jc.IsTrue) c.Assert(sch.IsPlaceholder(), jc.IsFalse) c.Assert(sch.Meta(), gc.DeepEquals, ch.Meta()) c.Assert(sch.Config(), gc.DeepEquals, ch.Config()) c.Assert(sch.BundleURL(), gc.DeepEquals, bundleURL) c.Assert(sch.BundleSha256(), gc.Equals, "missing") } func (s *StateSuite) assertPlaceholderCharmExists(c *gc.C, curl *charm.URL) { // Find charm directly and verify only the charm URL and // Placeholder are set. doc := state.CharmDoc{} err := s.charms.FindId(curl).One(&doc) c.Assert(err, gc.IsNil) c.Assert(doc.URL, gc.DeepEquals, curl) c.Assert(doc.PendingUpload, jc.IsFalse) c.Assert(doc.Placeholder, jc.IsTrue) c.Assert(doc.Meta, gc.IsNil) c.Assert(doc.Config, gc.IsNil) c.Assert(doc.BundleURL, gc.IsNil) c.Assert(doc.BundleSha256, gc.Equals, "") // Make sure we can't find it with st.Charm(). _, err = s.State.Charm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *StateSuite) TestLatestPlaceholderCharm(c *gc.C) { // Add a deployed charm ch, curl, bundleURL, bundleSHA256 := s.dummyCharm(c, "cs:quantal/dummy-1") _, err := s.State.AddCharm(ch, curl, bundleURL, bundleSHA256) c.Assert(err, gc.IsNil) // Deployed charm not found. _, err = s.State.LatestPlaceholderCharm(curl) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) // Add a charm reference curl2 := charm.MustParseURL("cs:quantal/dummy-2") err = s.State.AddStoreCharmPlaceholder(curl2) c.Assert(err, gc.IsNil) s.assertPlaceholderCharmExists(c, curl2) // Use a URL with an arbitrary rev to search. curl = charm.MustParseURL("cs:quantal/dummy-23") pending, err := s.State.LatestPlaceholderCharm(curl) c.Assert(err, gc.IsNil) c.Assert(pending.URL(), gc.DeepEquals, curl2) c.Assert(pending.IsPlaceholder(), jc.IsTrue) c.Assert(pending.Meta(), gc.IsNil) c.Assert(pending.Config(), gc.IsNil) c.Assert(pending.BundleURL(), gc.IsNil) c.Assert(pending.BundleSha256(), gc.Equals, "") } func (s *StateSuite) TestAddStoreCharmPlaceholderErrors(c *gc.C) { ch := testing.Charms.Dir("dummy") curl := charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", ch.Meta().Name, ch.Revision()), ) err := s.State.AddStoreCharmPlaceholder(curl) c.Assert(err, gc.ErrorMatches, "expected charm URL with cs schema, got .*") curl = charm.MustParseURL("cs:quantal/dummy") err = s.State.AddStoreCharmPlaceholder(curl) c.Assert(err, gc.ErrorMatches, "expected charm URL with revision, got .*") } func (s *StateSuite) TestAddStoreCharmPlaceholder(c *gc.C) { curl := charm.MustParseURL("cs:quantal/dummy-1") err := s.State.AddStoreCharmPlaceholder(curl) c.Assert(err, gc.IsNil) s.assertPlaceholderCharmExists(c, curl) // Add the same one again, should be a no-op err = s.State.AddStoreCharmPlaceholder(curl) c.Assert(err, gc.IsNil) s.assertPlaceholderCharmExists(c, curl) } func (s *StateSuite) assertAddStoreCharmPlaceholder(c *gc.C) (*charm.URL, *charm.URL, *state.Charm) { // Add a deployed charm ch, curl, bundleURL, bundleSHA256 := s.dummyCharm(c, "cs:quantal/dummy-1") dummy, err := s.State.AddCharm(ch, curl, bundleURL, bundleSHA256) c.Assert(err, gc.IsNil) // Add a charm placeholder curl2 := charm.MustParseURL("cs:quantal/dummy-2") err = s.State.AddStoreCharmPlaceholder(curl2) c.Assert(err, gc.IsNil) s.assertPlaceholderCharmExists(c, curl2) // Deployed charm is still there. existing, err := s.State.Charm(curl) c.Assert(err, gc.IsNil) c.Assert(existing, jc.DeepEquals, dummy) return curl, curl2, dummy } func (s *StateSuite) TestAddStoreCharmPlaceholderLeavesDeployedCharmsAlone(c *gc.C) { s.assertAddStoreCharmPlaceholder(c) } func (s *StateSuite) TestAddStoreCharmPlaceholderDeletesOlder(c *gc.C) { curl, curlOldRef, dummy := s.assertAddStoreCharmPlaceholder(c) // Add a new charm placeholder curl3 := charm.MustParseURL("cs:quantal/dummy-3") err := s.State.AddStoreCharmPlaceholder(curl3) c.Assert(err, gc.IsNil) s.assertPlaceholderCharmExists(c, curl3) // Deployed charm is still there. existing, err := s.State.Charm(curl) c.Assert(err, gc.IsNil) c.Assert(existing, jc.DeepEquals, dummy) // Older charm placeholder is gone. doc := state.CharmDoc{} err = s.charms.FindId(curlOldRef).One(&doc) c.Assert(err, gc.Equals, mgo.ErrNotFound) } func (s *StateSuite) AssertMachineCount(c *gc.C, expect int) { ms, err := s.State.AllMachines() c.Assert(err, gc.IsNil) c.Assert(len(ms), gc.Equals, expect) } var jobStringTests = []struct { job state.MachineJob s string }{ {state.JobHostUnits, "JobHostUnits"}, {state.JobManageEnviron, "JobManageEnviron"}, {state.JobManageStateDeprecated, "JobManageState"}, {0, "<unknown job 0>"}, {5, "<unknown job 5>"}, } func (s *StateSuite) TestJobString(c *gc.C) { for _, t := range jobStringTests { c.Check(t.job.String(), gc.Equals, t.s) } } func (s *StateSuite) TestAddMachineErrors(c *gc.C) { _, err := s.State.AddMachine("") c.Assert(err, gc.ErrorMatches, "cannot add a new machine: no series specified") _, err = s.State.AddMachine("quantal") c.Assert(err, gc.ErrorMatches, "cannot add a new machine: no jobs specified") _, err = s.State.AddMachine("quantal", state.JobHostUnits, state.JobHostUnits) c.Assert(err, gc.ErrorMatches, "cannot add a new machine: duplicate job: .*") } func (s *StateSuite) TestAddMachine(c *gc.C) { allJobs := []state.MachineJob{ state.JobHostUnits, state.JobManageEnviron, } m0, err := s.State.AddMachine("quantal", allJobs...) c.Assert(err, gc.IsNil) check := func(m *state.Machine, id, series string, jobs []state.MachineJob) { c.Assert(m.Id(), gc.Equals, id) c.Assert(m.Series(), gc.Equals, series) c.Assert(m.Jobs(), gc.DeepEquals, jobs) s.assertMachineContainers(c, m, nil) } check(m0, "0", "quantal", allJobs) m0, err = s.State.Machine("0") c.Assert(err, gc.IsNil) check(m0, "0", "quantal", allJobs) oneJob := []state.MachineJob{state.JobHostUnits} m1, err := s.State.AddMachine("blahblah", oneJob...) c.Assert(err, gc.IsNil) check(m1, "1", "blahblah", oneJob) m1, err = s.State.Machine("1") c.Assert(err, gc.IsNil) check(m1, "1", "blahblah", oneJob) m, err := s.State.AllMachines() c.Assert(err, gc.IsNil) c.Assert(m, gc.HasLen, 2) check(m[0], "0", "quantal", allJobs) check(m[1], "1", "blahblah", oneJob) } func (s *StateSuite) TestAddMachines(c *gc.C) { oneJob := []state.MachineJob{state.JobHostUnits} cons := constraints.MustParse("mem=4G") hc := instance.MustParseHardware("mem=2G") machineTemplate := state.MachineTemplate{ Series: "precise", Constraints: cons, HardwareCharacteristics: hc, InstanceId: "inst-id", Nonce: "nonce", Jobs: oneJob, } machines, err := s.State.AddMachines(machineTemplate) c.Assert(err, gc.IsNil) c.Assert(machines, gc.HasLen, 1) m, err := s.State.Machine(machines[0].Id()) c.Assert(err, gc.IsNil) instId, err := m.InstanceId() c.Assert(err, gc.IsNil) c.Assert(string(instId), gc.Equals, "inst-id") c.Assert(m.CheckProvisioned("nonce"), jc.IsTrue) c.Assert(m.Series(), gc.Equals, "precise") mcons, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(mcons, gc.DeepEquals, cons) mhc, err := m.HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Assert(*mhc, gc.DeepEquals, hc) // Clear the deprecated machineDoc InstanceId attribute and do it again. // still works as expected with the new data model. state.SetMachineInstanceId(m, "") instId, err = m.InstanceId() c.Assert(err, gc.IsNil) c.Assert(string(instId), gc.Equals, "inst-id") } func (s *StateSuite) TestAddMachinesEnvironmentDying(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) env, err := s.State.Environment() c.Assert(err, gc.IsNil) err = env.Destroy() c.Assert(err, gc.IsNil) // Check that machines cannot be added if the environment is initially Dying. _, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.ErrorMatches, "cannot add a new machine: environment is no longer alive") } func (s *StateSuite) TestAddMachinesEnvironmentDyingAfterInitial(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) env, err := s.State.Environment() c.Assert(err, gc.IsNil) // Check that machines cannot be added if the environment is initially // Alive but set to Dying immediately before the transaction is run. defer state.SetBeforeHooks(c, s.State, func() { c.Assert(env.Life(), gc.Equals, state.Alive) c.Assert(env.Destroy(), gc.IsNil) }).Check() _, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.ErrorMatches, "cannot add a new machine: environment is no longer alive") } func (s *StateSuite) TestAddMachineExtraConstraints(c *gc.C) { err := s.State.SetEnvironConstraints(constraints.MustParse("mem=4G")) c.Assert(err, gc.IsNil) oneJob := []state.MachineJob{state.JobHostUnits} extraCons := constraints.MustParse("cpu-cores=4") m, err := s.State.AddOneMachine(state.MachineTemplate{ Series: "quantal", Constraints: extraCons, Jobs: oneJob, }) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, "0") c.Assert(m.Series(), gc.Equals, "quantal") c.Assert(m.Jobs(), gc.DeepEquals, oneJob) expectedCons := constraints.MustParse("cpu-cores=4 mem=4G") mcons, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(mcons, gc.DeepEquals, expectedCons) } func (s *StateSuite) assertMachineContainers(c *gc.C, m *state.Machine, containers []string) { mc, err := m.Containers() c.Assert(err, gc.IsNil) c.Assert(mc, gc.DeepEquals, containers) } func (s *StateSuite) TestAddContainerToNewMachine(c *gc.C) { oneJob := []state.MachineJob{state.JobHostUnits} template := state.MachineTemplate{ Series: "quantal", Jobs: oneJob, } parentTemplate := state.MachineTemplate{ Series: "raring", Jobs: oneJob, } m, err := s.State.AddMachineInsideNewMachine(template, parentTemplate, instance.LXC) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, "0/lxc/0") c.Assert(m.Series(), gc.Equals, "quantal") c.Assert(m.ContainerType(), gc.Equals, instance.LXC) mcons, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(&mcons, jc.Satisfies, constraints.IsEmpty) c.Assert(m.Jobs(), gc.DeepEquals, oneJob) m, err = s.State.Machine("0") c.Assert(err, gc.IsNil) s.assertMachineContainers(c, m, []string{"0/lxc/0"}) c.Assert(m.Series(), gc.Equals, "raring") m, err = s.State.Machine("0/lxc/0") c.Assert(err, gc.IsNil) s.assertMachineContainers(c, m, nil) c.Assert(m.Jobs(), gc.DeepEquals, oneJob) } func (s *StateSuite) TestAddContainerToExistingMachine(c *gc.C) { oneJob := []state.MachineJob{state.JobHostUnits} m0, err := s.State.AddMachine("quantal", oneJob...) c.Assert(err, gc.IsNil) m1, err := s.State.AddMachine("quantal", oneJob...) c.Assert(err, gc.IsNil) // Add first container. m, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, "1", instance.LXC) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, "1/lxc/0") c.Assert(m.Series(), gc.Equals, "quantal") c.Assert(m.ContainerType(), gc.Equals, instance.LXC) mcons, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(&mcons, jc.Satisfies, constraints.IsEmpty) c.Assert(m.Jobs(), gc.DeepEquals, oneJob) s.assertMachineContainers(c, m1, []string{"1/lxc/0"}) s.assertMachineContainers(c, m0, nil) s.assertMachineContainers(c, m1, []string{"1/lxc/0"}) m, err = s.State.Machine("1/lxc/0") c.Assert(err, gc.IsNil) s.assertMachineContainers(c, m, nil) // Add second container. m, err = s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, "1", instance.LXC) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, "1/lxc/1") c.Assert(m.Series(), gc.Equals, "quantal") c.Assert(m.ContainerType(), gc.Equals, instance.LXC) c.Assert(m.Jobs(), gc.DeepEquals, oneJob) s.assertMachineContainers(c, m1, []string{"1/lxc/0", "1/lxc/1"}) } func (s *StateSuite) TestAddContainerToMachineWithKnownSupportedContainers(c *gc.C) { oneJob := []state.MachineJob{state.JobHostUnits} host, err := s.State.AddMachine("quantal", oneJob...) c.Assert(err, gc.IsNil) err = host.SetSupportedContainers([]instance.ContainerType{instance.KVM}) c.Assert(err, gc.IsNil) m, err := s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, "0", instance.KVM) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, "0/kvm/0") s.assertMachineContainers(c, host, []string{"0/kvm/0"}) } func (s *StateSuite) TestAddInvalidContainerToMachineWithKnownSupportedContainers(c *gc.C) { oneJob := []state.MachineJob{state.JobHostUnits} host, err := s.State.AddMachine("quantal", oneJob...) c.Assert(err, gc.IsNil) err = host.SetSupportedContainers([]instance.ContainerType{instance.KVM}) c.Assert(err, gc.IsNil) _, err = s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, "0", instance.LXC) c.Assert(err, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host lxc containers") s.assertMachineContainers(c, host, nil) } func (s *StateSuite) TestAddContainerToMachineSupportingNoContainers(c *gc.C) { oneJob := []state.MachineJob{state.JobHostUnits} host, err := s.State.AddMachine("quantal", oneJob...) c.Assert(err, gc.IsNil) err = host.SupportsNoContainers() c.Assert(err, gc.IsNil) _, err = s.State.AddMachineInsideMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }, "0", instance.LXC) c.Assert(err, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host lxc containers") s.assertMachineContainers(c, host, nil) } func (s *StateSuite) TestInvalidAddMachineParams(c *gc.C) { instIdTemplate := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, InstanceId: "i-foo", } normalTemplate := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } _, err := s.State.AddMachineInsideMachine(instIdTemplate, "0", instance.LXC) c.Check(err, gc.ErrorMatches, "cannot add a new machine: cannot specify instance id for a new container") _, err = s.State.AddMachineInsideNewMachine(instIdTemplate, normalTemplate, instance.LXC) c.Check(err, gc.ErrorMatches, "cannot add a new machine: cannot specify instance id for a new container") _, err = s.State.AddMachineInsideNewMachine(normalTemplate, instIdTemplate, instance.LXC) c.Check(err, gc.ErrorMatches, "cannot add a new machine: cannot specify instance id for a new container") _, err = s.State.AddOneMachine(instIdTemplate) c.Check(err, gc.ErrorMatches, "cannot add a new machine: cannot add a machine with an instance id and no nonce") _, err = s.State.AddOneMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits, state.JobHostUnits}, InstanceId: "i-foo", }) c.Check(err, gc.ErrorMatches, fmt.Sprintf("cannot add a new machine: duplicate job: %s", state.JobHostUnits)) noSeriesTemplate := state.MachineTemplate{ Jobs: []state.MachineJob{state.JobHostUnits, state.JobHostUnits}, } _, err = s.State.AddOneMachine(noSeriesTemplate) c.Check(err, gc.ErrorMatches, "cannot add a new machine: no series specified") _, err = s.State.AddMachineInsideNewMachine(noSeriesTemplate, normalTemplate, instance.LXC) c.Check(err, gc.ErrorMatches, "cannot add a new machine: no series specified") _, err = s.State.AddMachineInsideNewMachine(normalTemplate, noSeriesTemplate, instance.LXC) c.Check(err, gc.ErrorMatches, "cannot add a new machine: no series specified") _, err = s.State.AddMachineInsideMachine(noSeriesTemplate, "0", instance.LXC) c.Check(err, gc.ErrorMatches, "cannot add a new machine: no series specified") } func (s *StateSuite) TestAddContainerErrors(c *gc.C) { template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } _, err := s.State.AddMachineInsideMachine(template, "10", instance.LXC) c.Assert(err, gc.ErrorMatches, "cannot add a new machine: machine 10 not found") _, err = s.State.AddMachineInsideMachine(template, "10", "") c.Assert(err, gc.ErrorMatches, "cannot add a new machine: no container type specified") } func (s *StateSuite) TestInjectMachineErrors(c *gc.C) { injectMachine := func(series string, instanceId instance.Id, nonce string, jobs ...state.MachineJob) error { _, err := s.State.AddOneMachine(state.MachineTemplate{ Series: series, Jobs: jobs, InstanceId: instanceId, Nonce: nonce, }) return err } err := injectMachine("", "i-minvalid", state.BootstrapNonce, state.JobHostUnits) c.Assert(err, gc.ErrorMatches, "cannot add a new machine: no series specified") err = injectMachine("quantal", "", state.BootstrapNonce, state.JobHostUnits) c.Assert(err, gc.ErrorMatches, "cannot add a new machine: cannot specify a nonce without an instance id") err = injectMachine("quantal", "i-minvalid", "", state.JobHostUnits) c.Assert(err, gc.ErrorMatches, "cannot add a new machine: cannot add a machine with an instance id and no nonce") err = injectMachine("quantal", state.BootstrapNonce, "i-mlazy") c.Assert(err, gc.ErrorMatches, "cannot add a new machine: no jobs specified") } func (s *StateSuite) TestInjectMachine(c *gc.C) { cons := constraints.MustParse("mem=4G") arch := "amd64" mem := uint64(1024) disk := uint64(1024) tags := []string{"foo", "bar"} template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits, state.JobManageEnviron}, Constraints: cons, InstanceId: "i-mindustrious", Nonce: state.BootstrapNonce, HardwareCharacteristics: instance.HardwareCharacteristics{ Arch: &arch, Mem: &mem, RootDisk: &disk, Tags: &tags, }, } m, err := s.State.AddOneMachine(template) c.Assert(err, gc.IsNil) c.Assert(m.Jobs(), gc.DeepEquals, template.Jobs) instanceId, err := m.InstanceId() c.Assert(err, gc.IsNil) c.Assert(instanceId, gc.Equals, template.InstanceId) mcons, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, mcons) characteristics, err := m.HardwareCharacteristics() c.Assert(err, gc.IsNil) c.Assert(*characteristics, gc.DeepEquals, template.HardwareCharacteristics) // Make sure the bootstrap nonce value is set. c.Assert(m.CheckProvisioned(template.Nonce), gc.Equals, true) } func (s *StateSuite) TestAddContainerToInjectedMachine(c *gc.C) { oneJob := []state.MachineJob{state.JobHostUnits} template := state.MachineTemplate{ Series: "quantal", InstanceId: "i-mindustrious", Nonce: state.BootstrapNonce, Jobs: []state.MachineJob{state.JobHostUnits, state.JobManageEnviron}, } m0, err := s.State.AddOneMachine(template) c.Assert(err, gc.IsNil) // Add first container. template = state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } m, err := s.State.AddMachineInsideMachine(template, "0", instance.LXC) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, "0/lxc/0") c.Assert(m.Series(), gc.Equals, "quantal") c.Assert(m.ContainerType(), gc.Equals, instance.LXC) mcons, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(&mcons, jc.Satisfies, constraints.IsEmpty) c.Assert(m.Jobs(), gc.DeepEquals, oneJob) s.assertMachineContainers(c, m0, []string{"0/lxc/0"}) // Add second container. m, err = s.State.AddMachineInsideMachine(template, "0", instance.LXC) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, "0/lxc/1") c.Assert(m.Series(), gc.Equals, "quantal") c.Assert(m.ContainerType(), gc.Equals, instance.LXC) c.Assert(m.Jobs(), gc.DeepEquals, oneJob) s.assertMachineContainers(c, m0, []string{"0/lxc/0", "0/lxc/1"}) } func (s *StateSuite) TestAddMachineCanOnlyAddStateServerForMachine0(c *gc.C) { template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobManageEnviron}, } // Check that we can add the bootstrap machine. m, err := s.State.AddOneMachine(template) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Equals, "0") c.Assert(m.WantsVote(), jc.IsTrue) c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageEnviron}) // Check that the state server information is correct. info, err := s.State.StateServerInfo() c.Assert(err, gc.IsNil) c.Assert(info.MachineIds, gc.DeepEquals, []string{"0"}) c.Assert(info.VotingMachineIds, gc.DeepEquals, []string{"0"}) const errCannotAdd = "cannot add a new machine: state server jobs specified without calling EnsureAvailability" m, err = s.State.AddOneMachine(template) c.Assert(err, gc.ErrorMatches, errCannotAdd) m, err = s.State.AddMachineInsideMachine(template, "0", instance.LXC) c.Assert(err, gc.ErrorMatches, errCannotAdd) m, err = s.State.AddMachineInsideNewMachine(template, template, instance.LXC) c.Assert(err, gc.ErrorMatches, errCannotAdd) } func (s *StateSuite) TestReadMachine(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) expectedId := machine.Id() machine, err = s.State.Machine(expectedId) c.Assert(err, gc.IsNil) c.Assert(machine.Id(), gc.Equals, expectedId) } func (s *StateSuite) TestMachineNotFound(c *gc.C) { _, err := s.State.Machine("0") c.Assert(err, gc.ErrorMatches, "machine 0 not found") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *StateSuite) TestMachineIdLessThan(c *gc.C) { c.Assert(state.MachineIdLessThan("0", "0"), gc.Equals, false) c.Assert(state.MachineIdLessThan("0", "1"), gc.Equals, true) c.Assert(state.MachineIdLessThan("1", "0"), gc.Equals, false) c.Assert(state.MachineIdLessThan("10", "2"), gc.Equals, false) c.Assert(state.MachineIdLessThan("0", "0/lxc/0"), gc.Equals, true) c.Assert(state.MachineIdLessThan("0/lxc/0", "0"), gc.Equals, false) c.Assert(state.MachineIdLessThan("1", "0/lxc/0"), gc.Equals, false) c.Assert(state.MachineIdLessThan("0/lxc/0", "1"), gc.Equals, true) c.Assert(state.MachineIdLessThan("0/lxc/0/lxc/1", "0/lxc/0"), gc.Equals, false) c.Assert(state.MachineIdLessThan("0/kvm/0", "0/lxc/0"), gc.Equals, true) } func (s *StateSuite) TestAllMachines(c *gc.C) { numInserts := 42 for i := 0; i < numInserts; i++ { m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = m.SetProvisioned(instance.Id(fmt.Sprintf("foo-%d", i)), "fake_nonce", nil) c.Assert(err, gc.IsNil) err = m.SetAgentVersion(version.MustParseBinary("7.8.9-foo-bar")) c.Assert(err, gc.IsNil) err = m.Destroy() c.Assert(err, gc.IsNil) } s.AssertMachineCount(c, numInserts) ms, _ := s.State.AllMachines() for i, m := range ms { c.Assert(m.Id(), gc.Equals, strconv.Itoa(i)) instId, err := m.InstanceId() c.Assert(err, gc.IsNil) c.Assert(string(instId), gc.Equals, fmt.Sprintf("foo-%d", i)) tools, err := m.AgentTools() c.Check(err, gc.IsNil) c.Check(tools.Version, gc.DeepEquals, version.MustParseBinary("7.8.9-foo-bar")) c.Assert(m.Life(), gc.Equals, state.Dying) } } func (s *StateSuite) TestAddService(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") _, err := s.State.AddService("haha/borken", "user-admin", charm, nil, nil) c.Assert(err, gc.ErrorMatches, `cannot add service "haha/borken": invalid name`) _, err = s.State.Service("haha/borken") c.Assert(err, gc.ErrorMatches, `"haha/borken" is not a valid service name`) // set that a nil charm is handled correctly _, err = s.State.AddService("umadbro", "user-admin", nil, nil, nil) c.Assert(err, gc.ErrorMatches, `cannot add service "umadbro": charm is nil`) wordpress, err := s.State.AddService("wordpress", "user-admin", charm, nil, nil) c.Assert(err, gc.IsNil) c.Assert(wordpress.Name(), gc.Equals, "wordpress") mysql, err := s.State.AddService("mysql", "user-admin", charm, nil, nil) c.Assert(err, gc.IsNil) c.Assert(mysql.Name(), gc.Equals, "mysql") // Check that retrieving the new created services works correctly. wordpress, err = s.State.Service("wordpress") c.Assert(err, gc.IsNil) c.Assert(wordpress.Name(), gc.Equals, "wordpress") ch, _, err := wordpress.Charm() c.Assert(err, gc.IsNil) c.Assert(ch.URL(), gc.DeepEquals, charm.URL()) mysql, err = s.State.Service("mysql") c.Assert(err, gc.IsNil) c.Assert(mysql.Name(), gc.Equals, "mysql") ch, _, err = mysql.Charm() c.Assert(err, gc.IsNil) c.Assert(ch.URL(), gc.DeepEquals, charm.URL()) } func (s *StateSuite) TestAddServiceEnvironmentDying(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") s.AddTestingService(c, "s0", charm) // Check that services cannot be added if the environment is initially Dying. env, err := s.State.Environment() c.Assert(err, gc.IsNil) err = env.Destroy() c.Assert(err, gc.IsNil) _, err = s.State.AddService("s1", "user-admin", charm, nil, nil) c.Assert(err, gc.ErrorMatches, `cannot add service "s1": environment is no longer alive`) } func (s *StateSuite) TestAddServiceEnvironmentDyingAfterInitial(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") s.AddTestingService(c, "s0", charm) env, err := s.State.Environment() c.Assert(err, gc.IsNil) // Check that services cannot be added if the environment is initially // Alive but set to Dying immediately before the transaction is run. defer state.SetBeforeHooks(c, s.State, func() { c.Assert(env.Life(), gc.Equals, state.Alive) c.Assert(env.Destroy(), gc.IsNil) }).Check() _, err = s.State.AddService("s1", "user-admin", charm, nil, nil) c.Assert(err, gc.ErrorMatches, `cannot add service "s1": environment is no longer alive`) } func (s *StateSuite) TestServiceNotFound(c *gc.C) { _, err := s.State.Service("bummer") c.Assert(err, gc.ErrorMatches, `service "bummer" not found`) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *StateSuite) TestAddServiceNoTag(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") _, err := s.State.AddService("wordpress", state.AdminUser, charm, nil, nil) c.Assert(err, gc.ErrorMatches, "cannot add service \"wordpress\": Invalid ownertag admin") } func (s *StateSuite) TestAddServiceNotUserTag(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") _, err := s.State.AddService("wordpress", "machine-3", charm, nil, nil) c.Assert(err, gc.ErrorMatches, "cannot add service \"wordpress\": Invalid ownertag machine-3") } func (s *StateSuite) TestAddServiceNonExistentUser(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") _, err := s.State.AddService("wordpress", "user-notAuser", charm, nil, nil) c.Assert(err, gc.ErrorMatches, "cannot add service \"wordpress\": user notAuser doesn't exist") } func (s *StateSuite) TestAllServices(c *gc.C) { charm := s.AddTestingCharm(c, "dummy") services, err := s.State.AllServices() c.Assert(err, gc.IsNil) c.Assert(len(services), gc.Equals, 0) // Check that after adding services the result is ok. _, err = s.State.AddService("wordpress", "user-admin", charm, nil, nil) c.Assert(err, gc.IsNil) services, err = s.State.AllServices() c.Assert(err, gc.IsNil) c.Assert(len(services), gc.Equals, 1) _, err = s.State.AddService("mysql", "user-admin", charm, nil, nil) c.Assert(err, gc.IsNil) services, err = s.State.AllServices() c.Assert(err, gc.IsNil) c.Assert(len(services), gc.Equals, 2) // Check the returned service, order is defined by sorted keys. c.Assert(services[0].Name(), gc.Equals, "wordpress") c.Assert(services[1].Name(), gc.Equals, "mysql") } var inferEndpointsTests = []struct { summary string inputs [][]string eps []state.Endpoint err string }{ { summary: "insane args", inputs: [][]string{nil}, err: `cannot relate 0 endpoints`, }, { summary: "insane args", inputs: [][]string{{"blah", "blur", "bleurgh"}}, err: `cannot relate 3 endpoints`, }, { summary: "invalid args", inputs: [][]string{ {"ping:"}, {":pong"}, {":"}, }, err: `invalid endpoint ".*"`, }, { summary: "unknown service", inputs: [][]string{{"wooble"}}, err: `service "wooble" not found`, }, { summary: "invalid relations", inputs: [][]string{ {"lg", "lg"}, {"ms", "ms"}, {"wp", "wp"}, {"rk1", "rk1"}, {"rk1", "rk2"}, }, err: `no relations found`, }, { summary: "valid peer relation", inputs: [][]string{ {"rk1"}, {"rk1:ring"}, }, eps: []state.Endpoint{{ ServiceName: "rk1", Relation: charm.Relation{ Name: "ring", Interface: "riak", Limit: 1, Role: charm.RolePeer, Scope: charm.ScopeGlobal, }, }}, }, { summary: "ambiguous provider/requirer relation", inputs: [][]string{ {"ms", "wp"}, {"ms", "wp:db"}, }, err: `ambiguous relation: ".*" could refer to "wp:db ms:dev"; "wp:db ms:prod"`, }, { summary: "unambiguous provider/requirer relation", inputs: [][]string{ {"ms:dev", "wp"}, {"ms:dev", "wp:db"}, }, eps: []state.Endpoint{{ ServiceName: "ms", Relation: charm.Relation{ Interface: "mysql", Name: "dev", Role: charm.RoleProvider, Scope: charm.ScopeGlobal, Limit: 2, }, }, { ServiceName: "wp", Relation: charm.Relation{ Interface: "mysql", Name: "db", Role: charm.RoleRequirer, Scope: charm.ScopeGlobal, Limit: 1, }, }}, }, { summary: "explicit logging relation is preferred over implicit juju-info", inputs: [][]string{{"lg", "wp"}}, eps: []state.Endpoint{{ ServiceName: "lg", Relation: charm.Relation{ Interface: "logging", Name: "logging-directory", Role: charm.RoleRequirer, Scope: charm.ScopeContainer, Limit: 1, }, }, { ServiceName: "wp", Relation: charm.Relation{ Interface: "logging", Name: "logging-dir", Role: charm.RoleProvider, Scope: charm.ScopeContainer, }, }}, }, { summary: "implict relations can be chosen explicitly", inputs: [][]string{ {"lg:info", "wp"}, {"lg", "wp:juju-info"}, {"lg:info", "wp:juju-info"}, }, eps: []state.Endpoint{{ ServiceName: "lg", Relation: charm.Relation{ Interface: "juju-info", Name: "info", Role: charm.RoleRequirer, Scope: charm.ScopeContainer, Limit: 1, }, }, { ServiceName: "wp", Relation: charm.Relation{ Interface: "juju-info", Name: "juju-info", Role: charm.RoleProvider, Scope: charm.ScopeGlobal, }, }}, }, { summary: "implicit relations will be chosen if there are no other options", inputs: [][]string{{"lg", "ms"}}, eps: []state.Endpoint{{ ServiceName: "lg", Relation: charm.Relation{ Interface: "juju-info", Name: "info", Role: charm.RoleRequirer, Scope: charm.ScopeContainer, Limit: 1, }, }, { ServiceName: "ms", Relation: charm.Relation{ Interface: "juju-info", Name: "juju-info", Role: charm.RoleProvider, Scope: charm.ScopeGlobal, }, }}, }, } func (s *StateSuite) TestInferEndpoints(c *gc.C) { s.AddTestingService(c, "ms", s.AddTestingCharm(c, "mysql-alternative")) s.AddTestingService(c, "wp", s.AddTestingCharm(c, "wordpress")) s.AddTestingService(c, "lg", s.AddTestingCharm(c, "logging")) riak := s.AddTestingCharm(c, "riak") s.AddTestingService(c, "rk1", riak) s.AddTestingService(c, "rk2", riak) for i, t := range inferEndpointsTests { c.Logf("test %d", i) for j, input := range t.inputs { c.Logf(" input %d", j) eps, err := s.State.InferEndpoints(input) if t.err == "" { c.Assert(err, gc.IsNil) c.Assert(eps, gc.DeepEquals, t.eps) } else { c.Assert(err, gc.ErrorMatches, t.err) } } } } func (s *StateSuite) TestEnvironConfig(c *gc.C) { attrs := map[string]interface{}{ "authorized-keys": "different-keys", "arbitrary-key": "shazam!", } cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) err = s.State.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) cfg, err = cfg.Apply(attrs) c.Assert(err, gc.IsNil) oldCfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) c.Assert(oldCfg, gc.DeepEquals, cfg) } func (s *StateSuite) TestEnvironConstraints(c *gc.C) { // Environ constraints start out empty (for now). cons, err := s.State.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(&cons, jc.Satisfies, constraints.IsEmpty) // Environ constraints can be set. cons2 := constraints.Value{Mem: uint64p(1024)} err = s.State.SetEnvironConstraints(cons2) c.Assert(err, gc.IsNil) cons3, err := s.State.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(cons3, gc.DeepEquals, cons2) // Environ constraints are completely overwritten when re-set. cons4 := constraints.Value{CpuPower: uint64p(250)} err = s.State.SetEnvironConstraints(cons4) c.Assert(err, gc.IsNil) cons5, err := s.State.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(cons5, gc.DeepEquals, cons4) } func (s *StateSuite) TestWatchServicesBulkEvents(c *gc.C) { // Alive service... dummyCharm := s.AddTestingCharm(c, "dummy") alive := s.AddTestingService(c, "service0", dummyCharm) // Dying service... dying := s.AddTestingService(c, "service1", dummyCharm) keepDying, err := dying.AddUnit() c.Assert(err, gc.IsNil) err = dying.Destroy() c.Assert(err, gc.IsNil) // Dead service (actually, gone, Dead == removed in this case). gone := s.AddTestingService(c, "service2", dummyCharm) err = gone.Destroy() c.Assert(err, gc.IsNil) // All except gone are reported in initial event. w := s.State.WatchServices() defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.State, w) wc.AssertChange(alive.Name(), dying.Name()) wc.AssertNoChange() // Remove them all; alive/dying changes reported. err = alive.Destroy() c.Assert(err, gc.IsNil) err = keepDying.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange(alive.Name(), dying.Name()) wc.AssertNoChange() } func (s *StateSuite) TestWatchServicesLifecycle(c *gc.C) { // Initial event is empty when no services. w := s.State.WatchServices() defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.State, w) wc.AssertChange() wc.AssertNoChange() // Add a service: reported. service := s.AddTestingService(c, "service", s.AddTestingCharm(c, "dummy")) wc.AssertChange("service") wc.AssertNoChange() // Change the service: not reported. keepDying, err := service.AddUnit() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Make it Dying: reported. err = service.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange("service") wc.AssertNoChange() // Make it Dead(/removed): reported. err = keepDying.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange("service") wc.AssertNoChange() } func (s *StateSuite) TestWatchServicesDiesOnStateClose(c *gc.C) { // This test is testing logic in watcher.lifecycleWatcher, // which is also used by: // Service.WatchUnits // Service.WatchRelations // State.WatchEnviron // Machine.WatchContainers testWatcherDiesWhenStateCloses(c, func(c *gc.C, st *state.State) waiter { w := st.WatchServices() <-w.Changes() return w }) } func (s *StateSuite) TestWatchMachinesBulkEvents(c *gc.C) { // Alive machine... alive, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) // Dying machine... dying, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = dying.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) c.Assert(err, gc.IsNil) err = dying.Destroy() c.Assert(err, gc.IsNil) // Dead machine... dead, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = dead.EnsureDead() c.Assert(err, gc.IsNil) // Gone machine. gone, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = gone.EnsureDead() c.Assert(err, gc.IsNil) err = gone.Remove() c.Assert(err, gc.IsNil) // All except gone machine are reported in initial event. w := s.State.WatchEnvironMachines() defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.State, w) wc.AssertChange(alive.Id(), dying.Id(), dead.Id()) wc.AssertNoChange() // Remove them all; alive/dying changes reported; dead never mentioned again. err = alive.Destroy() c.Assert(err, gc.IsNil) err = dying.EnsureDead() c.Assert(err, gc.IsNil) err = dying.Remove() c.Assert(err, gc.IsNil) err = dead.Remove() c.Assert(err, gc.IsNil) wc.AssertChange(alive.Id(), dying.Id()) wc.AssertNoChange() } func (s *StateSuite) TestWatchMachinesLifecycle(c *gc.C) { // Initial event is empty when no machines. w := s.State.WatchEnvironMachines() defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.State, w) wc.AssertChange() wc.AssertNoChange() // Add a machine: reported. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) wc.AssertChange("0") wc.AssertNoChange() // Change the machine: not reported. err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Make it Dying: reported. err = machine.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange("0") wc.AssertNoChange() // Make it Dead: reported. err = machine.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertChange("0") wc.AssertNoChange() // Remove it: not reported. err = machine.Remove() c.Assert(err, gc.IsNil) wc.AssertNoChange() } func (s *StateSuite) TestWatchMachinesIncludesOldMachines(c *gc.C) { // Older versions of juju do not write the "containertype" field. // This has caused machines to not be detected in the initial event. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = s.machines.Update( bson.D{{"_id", machine.Id()}}, bson.D{{"$unset", bson.D{{"containertype", 1}}}}, ) c.Assert(err, gc.IsNil) w := s.State.WatchEnvironMachines() defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.State, w) wc.AssertChange(machine.Id()) wc.AssertNoChange() } func (s *StateSuite) TestWatchMachinesIgnoresContainers(c *gc.C) { // Initial event is empty when no machines. w := s.State.WatchEnvironMachines() defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.State, w) wc.AssertChange() wc.AssertNoChange() // Add a machine: reported. template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } machines, err := s.State.AddMachines(template) c.Assert(err, gc.IsNil) c.Assert(machines, gc.HasLen, 1) machine := machines[0] wc.AssertChange("0") wc.AssertNoChange() // Add a container: not reported. m, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Make the container Dying: not reported. err = m.Destroy() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Make the container Dead: not reported. err = m.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Remove the container: not reported. err = m.Remove() c.Assert(err, gc.IsNil) wc.AssertNoChange() } func (s *StateSuite) TestWatchContainerLifecycle(c *gc.C) { // Add a host machine. template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } machine, err := s.State.AddOneMachine(template) c.Assert(err, gc.IsNil) otherMachine, err := s.State.AddOneMachine(template) c.Assert(err, gc.IsNil) // Initial event is empty when no containers. w := machine.WatchContainers(instance.LXC) defer statetesting.AssertStop(c, w) wAll := machine.WatchAllContainers() defer statetesting.AssertStop(c, wAll) wc := statetesting.NewStringsWatcherC(c, s.State, w) wc.AssertChange() wc.AssertNoChange() wcAll := statetesting.NewStringsWatcherC(c, s.State, wAll) wcAll.AssertChange() wcAll.AssertNoChange() // Add a container of the required type: reported. m, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) wc.AssertChange("0/lxc/0") wc.AssertNoChange() wcAll.AssertChange("0/lxc/0") wcAll.AssertNoChange() // Add a container of a different type: not reported. m1, err := s.State.AddMachineInsideMachine(template, machine.Id(), instance.KVM) c.Assert(err, gc.IsNil) wc.AssertNoChange() // But reported by the all watcher. wcAll.AssertChange("0/kvm/0") wcAll.AssertNoChange() // Add a nested container of the right type: not reported. mchild, err := s.State.AddMachineInsideMachine(template, m.Id(), instance.LXC) c.Assert(err, gc.IsNil) wc.AssertNoChange() wcAll.AssertNoChange() // Add a container of a different machine: not reported. m2, err := s.State.AddMachineInsideMachine(template, otherMachine.Id(), instance.LXC) c.Assert(err, gc.IsNil) wc.AssertNoChange() statetesting.AssertStop(c, w) wcAll.AssertNoChange() statetesting.AssertStop(c, wAll) w = machine.WatchContainers(instance.LXC) defer statetesting.AssertStop(c, w) wc = statetesting.NewStringsWatcherC(c, s.State, w) wAll = machine.WatchAllContainers() defer statetesting.AssertStop(c, wAll) wcAll = statetesting.NewStringsWatcherC(c, s.State, wAll) wc.AssertChange("0/lxc/0") wc.AssertNoChange() wcAll.AssertChange("0/kvm/0", "0/lxc/0") wcAll.AssertNoChange() // Make the container Dying: cannot because of nested container. err = m.Destroy() c.Assert(err, gc.ErrorMatches, `machine .* is hosting containers ".*"`) err = mchild.EnsureDead() c.Assert(err, gc.IsNil) err = mchild.Remove() c.Assert(err, gc.IsNil) // Make the container Dying: reported. err = m.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange("0/lxc/0") wc.AssertNoChange() wcAll.AssertChange("0/lxc/0") wcAll.AssertNoChange() // Make the other containers Dying: not reported. err = m1.Destroy() c.Assert(err, gc.IsNil) err = m2.Destroy() c.Assert(err, gc.IsNil) wc.AssertNoChange() // But reported by the all watcher. wcAll.AssertChange("0/kvm/0") wcAll.AssertNoChange() // Make the container Dead: reported. err = m.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertChange("0/lxc/0") wc.AssertNoChange() wcAll.AssertChange("0/lxc/0") wcAll.AssertNoChange() // Make the other containers Dead: not reported. err = m1.EnsureDead() c.Assert(err, gc.IsNil) err = m2.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertNoChange() // But reported by the all watcher. wcAll.AssertChange("0/kvm/0") wcAll.AssertNoChange() // Remove the container: not reported. err = m.Remove() c.Assert(err, gc.IsNil) wc.AssertNoChange() wcAll.AssertNoChange() } func (s *StateSuite) TestWatchMachineHardwareCharacteristics(c *gc.C) { // Add a machine: reported. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) w := machine.WatchHardwareCharacteristics() defer statetesting.AssertStop(c, w) // Initial event. wc := statetesting.NewNotifyWatcherC(c, s.State, w) wc.AssertOneChange() // Provision a machine: reported. err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil) c.Assert(err, gc.IsNil) wc.AssertOneChange() // Alter the machine: not reported. vers := version.MustParseBinary("1.2.3-gutsy-ppc") err = machine.SetAgentVersion(vers) c.Assert(err, gc.IsNil) wc.AssertNoChange() } func (s *StateSuite) TestWatchStateServerInfo(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) w := s.State.WatchStateServerInfo() defer statetesting.AssertStop(c, w) // Initial event. wc := statetesting.NewNotifyWatcherC(c, s.State, w) wc.AssertOneChange() info, err := s.State.StateServerInfo() c.Assert(err, gc.IsNil) c.Assert(info, jc.DeepEquals, &state.StateServerInfo{ MachineIds: []string{"0"}, VotingMachineIds: []string{"0"}, }) err = s.State.EnsureAvailability(3, constraints.Value{}, "quantal") c.Assert(err, gc.IsNil) wc.AssertOneChange() info, err = s.State.StateServerInfo() c.Assert(err, gc.IsNil) c.Assert(info, jc.DeepEquals, &state.StateServerInfo{ MachineIds: []string{"0", "1", "2"}, VotingMachineIds: []string{"0", "1", "2"}, }) } func (s *StateSuite) TestAdditionalValidation(c *gc.C) { updateAttrs := map[string]interface{}{"logging-config": "juju=ERROR"} configValidator1 := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error { c.Assert(updateAttrs, gc.DeepEquals, map[string]interface{}{"logging-config": "juju=ERROR"}) if _, found := updateAttrs["logging-config"]; found { return fmt.Errorf("cannot change logging-config") } return nil } removeAttrs := []string{"logging-config"} configValidator2 := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error { c.Assert(removeAttrs, gc.DeepEquals, []string{"logging-config"}) for _, i := range removeAttrs { if i == "logging-config" { return fmt.Errorf("cannot remove logging-config") } } return nil } configValidator3 := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error { return nil } err := s.State.UpdateEnvironConfig(updateAttrs, nil, configValidator1) c.Assert(err, gc.ErrorMatches, "cannot change logging-config") err = s.State.UpdateEnvironConfig(nil, removeAttrs, configValidator2) c.Assert(err, gc.ErrorMatches, "cannot remove logging-config") err = s.State.UpdateEnvironConfig(updateAttrs, nil, configValidator3) c.Assert(err, gc.IsNil) } type attrs map[string]interface{} func (s *StateSuite) TestWatchEnvironConfig(c *gc.C) { w := s.State.WatchEnvironConfig() defer statetesting.AssertStop(c, w) // TODO(fwereade) just use a NotifyWatcher and NotifyWatcherC to test it. assertNoChange := func() { s.State.StartSync() select { case got := <-w.Changes(): c.Fatalf("got unexpected change: %#v", got) case <-time.After(testing.ShortWait): } } assertChange := func(change attrs) { cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) cfg, err = cfg.Apply(change) c.Assert(err, gc.IsNil) if change != nil { err = s.State.UpdateEnvironConfig(change, nil, nil) c.Assert(err, gc.IsNil) } s.State.StartSync() select { case got, ok := <-w.Changes(): c.Assert(ok, gc.Equals, true) c.Assert(got.AllAttrs(), gc.DeepEquals, cfg.AllAttrs()) case <-time.After(testing.LongWait): c.Fatalf("did not get change: %#v", change) } assertNoChange() } assertChange(nil) assertChange(attrs{"default-series": "another-series"}) assertChange(attrs{"fancy-new-key": "arbitrary-value"}) } func (s *StateSuite) TestWatchEnvironConfigDiesOnStateClose(c *gc.C) { testWatcherDiesWhenStateCloses(c, func(c *gc.C, st *state.State) waiter { w := st.WatchEnvironConfig() <-w.Changes() return w }) } func (s *StateSuite) TestWatchForEnvironConfigChanges(c *gc.C) { cur := version.Current.Number err := statetesting.SetAgentVersion(s.State, cur) c.Assert(err, gc.IsNil) w := s.State.WatchForEnvironConfigChanges() defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.State, w) // Initially we get one change notification wc.AssertOneChange() // Multiple changes will only result in a single change notification newVersion := cur newVersion.Minor += 1 err = statetesting.SetAgentVersion(s.State, newVersion) c.Assert(err, gc.IsNil) newerVersion := newVersion newerVersion.Minor += 1 err = statetesting.SetAgentVersion(s.State, newerVersion) c.Assert(err, gc.IsNil) wc.AssertOneChange() // Setting it to the same value does not trigger a change notification err = statetesting.SetAgentVersion(s.State, newerVersion) c.Assert(err, gc.IsNil) wc.AssertNoChange() } func (s *StateSuite) TestWatchEnvironConfigCorruptConfig(c *gc.C) { cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) // Corrupt the environment configuration. settings := s.Session.DB("juju").C("settings") err = settings.UpdateId("e", bson.D{{"$unset", bson.D{{"name", 1}}}}) c.Assert(err, gc.IsNil) s.State.StartSync() // Start watching the configuration. watcher := s.State.WatchEnvironConfig() defer watcher.Stop() done := make(chan *config.Config) go func() { select { case cfg, ok := <-watcher.Changes(): if !ok { c.Errorf("watcher channel closed") } else { done <- cfg } case <-time.After(5 * time.Second): c.Fatalf("no environment configuration observed") } }() s.State.StartSync() // The invalid configuration must not have been generated. select { case <-done: c.Fatalf("configuration returned too soon") case <-time.After(testing.ShortWait): } // Fix the configuration. err = settings.UpdateId("e", bson.D{{"$set", bson.D{{"name", "foo"}}}}) c.Assert(err, gc.IsNil) fixed := cfg.AllAttrs() err = s.State.UpdateEnvironConfig(fixed, nil, nil) c.Assert(err, gc.IsNil) s.State.StartSync() select { case got := <-done: c.Assert(got.AllAttrs(), gc.DeepEquals, fixed) case <-time.After(5 * time.Second): c.Fatalf("no environment configuration observed") } } func (s *StateSuite) TestAddAndGetEquivalence(c *gc.C) { // The equivalence tested here isn't necessarily correct, and // comparing private details is discouraged in the project. // The implementation might choose to cache information, or // to have different logic when adding or removing, and the // comparison might fail despite it being correct. // That said, we've had bugs with txn-revno being incorrect // before, so this testing at least ensures we're conscious // about such changes. m1, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) m2, err := s.State.Machine(m1.Id()) c.Assert(m1, jc.DeepEquals, m2) charm1 := s.AddTestingCharm(c, "wordpress") charm2, err := s.State.Charm(charm1.URL()) c.Assert(err, gc.IsNil) c.Assert(charm1, jc.DeepEquals, charm2) wordpress1 := s.AddTestingService(c, "wordpress", charm1) wordpress2, err := s.State.Service("wordpress") c.Assert(err, gc.IsNil) c.Assert(wordpress1, jc.DeepEquals, wordpress2) unit1, err := wordpress1.AddUnit() c.Assert(err, gc.IsNil) unit2, err := s.State.Unit("wordpress/0") c.Assert(err, gc.IsNil) c.Assert(unit1, jc.DeepEquals, unit2) s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) c.Assert(err, gc.IsNil) eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) c.Assert(err, gc.IsNil) relation1, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) relation2, err := s.State.EndpointsRelation(eps...) c.Assert(relation1, jc.DeepEquals, relation2) relation3, err := s.State.Relation(relation1.Id()) c.Assert(relation1, jc.DeepEquals, relation3) } func tryOpenState(info *state.Info) error { st, err := state.Open(info, state.TestingDialOpts(), state.Policy(nil)) if err == nil { st.Close() } return err } func (s *StateSuite) TestOpenWithoutSetMongoPassword(c *gc.C) { info := state.TestingStateInfo() info.Tag, info.Password = "arble", "bar" err := tryOpenState(info) c.Assert(err, jc.Satisfies, errors.IsUnauthorizedError) info.Tag, info.Password = "arble", "" err = tryOpenState(info) c.Assert(err, jc.Satisfies, errors.IsUnauthorizedError) info.Tag, info.Password = "", "" err = tryOpenState(info) c.Assert(err, gc.IsNil) } func (s *StateSuite) TestOpenBadAddress(c *gc.C) { info := state.TestingStateInfo() info.Addrs = []string{"0.1.2.3:1234"} st, err := state.Open(info, state.DialOpts{ Timeout: 1 * time.Millisecond, }, state.Policy(nil)) if err == nil { st.Close() } c.Assert(err, gc.ErrorMatches, "no reachable servers") } func (s *StateSuite) TestOpenDelaysRetryBadAddress(c *gc.C) { // Default mgo retry delay retryDelay := 500 * time.Millisecond info := state.TestingStateInfo() info.Addrs = []string{"0.1.2.3:1234"} t0 := time.Now() st, err := state.Open(info, state.DialOpts{ Timeout: 1 * time.Millisecond, }, state.Policy(nil)) if err == nil { st.Close() } c.Assert(err, gc.ErrorMatches, "no reachable servers") // tryOpenState should have delayed for at least retryDelay // internally mgo will try three times in a row before returning // to the caller. if t1 := time.Since(t0); t1 < 3*retryDelay { c.Errorf("mgo.Dial only paused for %v, expected at least %v", t1, 3*retryDelay) } } func testSetPassword(c *gc.C, getEntity func() (state.Authenticator, error)) { e, err := getEntity() c.Assert(err, gc.IsNil) c.Assert(e.PasswordValid(goodPassword), gc.Equals, false) err = e.SetPassword(goodPassword) c.Assert(err, gc.IsNil) c.Assert(e.PasswordValid(goodPassword), gc.Equals, true) // Check a newly-fetched entity has the same password. e2, err := getEntity() c.Assert(err, gc.IsNil) c.Assert(e2.PasswordValid(goodPassword), gc.Equals, true) err = e.SetPassword(alternatePassword) c.Assert(err, gc.IsNil) c.Assert(e.PasswordValid(goodPassword), gc.Equals, false) c.Assert(e.PasswordValid(alternatePassword), gc.Equals, true) // Check that refreshing fetches the new password err = e2.Refresh() c.Assert(err, gc.IsNil) c.Assert(e2.PasswordValid(alternatePassword), gc.Equals, true) if le, ok := e.(lifer); ok { testWhenDying(c, le, noErr, deadErr, func() error { return e.SetPassword("arble-farble-dying-yarble") }) } } func testSetAgentCompatPassword(c *gc.C, entity state.Authenticator) { // In Juju versions 1.16 and older we used UserPasswordHash(password,CompatSalt) // for Machine and Unit agents. This was determined to be overkill // (since we know that Unit agents will actually use // utils.RandomPassword() and get 18 bytes of entropy, and thus won't // be brute-forced.) c.Assert(entity.PasswordValid(goodPassword), jc.IsFalse) agentHash := utils.AgentPasswordHash(goodPassword) err := state.SetPasswordHash(entity, agentHash) c.Assert(err, gc.IsNil) c.Assert(entity.PasswordValid(goodPassword), jc.IsTrue) c.Assert(entity.PasswordValid(alternatePassword), jc.IsFalse) c.Assert(state.GetPasswordHash(entity), gc.Equals, agentHash) backwardsCompatibleHash := utils.UserPasswordHash(goodPassword, utils.CompatSalt) c.Assert(backwardsCompatibleHash, gc.Not(gc.Equals), agentHash) err = state.SetPasswordHash(entity, backwardsCompatibleHash) c.Assert(err, gc.IsNil) c.Assert(entity.PasswordValid(alternatePassword), jc.IsFalse) c.Assert(state.GetPasswordHash(entity), gc.Equals, backwardsCompatibleHash) // After succeeding to log in with the old compatible hash, the db // should be updated with the new hash c.Assert(entity.PasswordValid(goodPassword), jc.IsTrue) c.Assert(state.GetPasswordHash(entity), gc.Equals, agentHash) c.Assert(entity.PasswordValid(goodPassword), jc.IsTrue) // Agents are unable to set short passwords err = entity.SetPassword("short") c.Check(err, gc.ErrorMatches, "password is only 5 bytes long, and is not a valid Agent password") // Grandfather clause. Agents that have short passwords are allowed if // it was done in the compatHash form agentHash = utils.AgentPasswordHash("short") backwardsCompatibleHash = utils.UserPasswordHash("short", utils.CompatSalt) err = state.SetPasswordHash(entity, backwardsCompatibleHash) c.Assert(err, gc.IsNil) c.Assert(entity.PasswordValid("short"), jc.IsTrue) // We'll still update the hash, but now it points to the hash of the // shorter password. Agents still can't set the password to it c.Assert(state.GetPasswordHash(entity), gc.Equals, agentHash) // Still valid with the shorter password c.Assert(entity.PasswordValid("short"), jc.IsTrue) } type entity interface { state.Entity state.Lifer state.Authenticator state.MongoPassworder } func testSetMongoPassword(c *gc.C, getEntity func(st *state.State) (entity, error)) { info := state.TestingStateInfo() st, err := state.Open(info, state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st.Close() // Turn on fully-authenticated mode. err = st.SetAdminMongoPassword("admin-secret") c.Assert(err, gc.IsNil) // Set the password for the entity ent, err := getEntity(st) c.Assert(err, gc.IsNil) err = ent.SetMongoPassword("foo") c.Assert(err, gc.IsNil) // Check that we cannot log in with the wrong password. info.Tag = ent.Tag() info.Password = "bar" err = tryOpenState(info) c.Assert(err, jc.Satisfies, errors.IsUnauthorizedError) // Check that we can log in with the correct password. info.Password = "foo" st1, err := state.Open(info, state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st1.Close() // Change the password with an entity derived from the newly // opened and authenticated state. ent, err = getEntity(st) c.Assert(err, gc.IsNil) err = ent.SetMongoPassword("bar") c.Assert(err, gc.IsNil) // Check that we cannot log in with the old password. info.Password = "foo" err = tryOpenState(info) c.Assert(err, jc.Satisfies, errors.IsUnauthorizedError) // Check that we can log in with the correct password. info.Password = "bar" err = tryOpenState(info) c.Assert(err, gc.IsNil) // Check that the administrator can still log in. info.Tag, info.Password = "", "admin-secret" err = tryOpenState(info) c.Assert(err, gc.IsNil) // Remove the admin password so that the test harness can reset the state. err = st.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) } func (s *StateSuite) TestSetAdminMongoPassword(c *gc.C) { // Check that we can SetAdminMongoPassword to nothing when there's // no password currently set. err := s.State.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) err = s.State.SetAdminMongoPassword("foo") c.Assert(err, gc.IsNil) defer s.State.SetAdminMongoPassword("") info := state.TestingStateInfo() err = tryOpenState(info) c.Assert(err, jc.Satisfies, errors.IsUnauthorizedError) info.Password = "foo" err = tryOpenState(info) c.Assert(err, gc.IsNil) err = s.State.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) // Check that removing the password is idempotent. err = s.State.SetAdminMongoPassword("") c.Assert(err, gc.IsNil) info.Password = "" err = tryOpenState(info) c.Assert(err, gc.IsNil) } type findEntityTest struct { tag string err string } var findEntityTests = []findEntityTest{{ tag: "", err: `"" is not a valid tag`, }, { tag: "machine", err: `"machine" is not a valid tag`, }, { tag: "-foo", err: `"-foo" is not a valid tag`, }, { tag: "foo-", err: `"foo-" is not a valid tag`, }, { tag: "---", err: `"---" is not a valid tag`, }, { tag: "machine-bad", err: `"machine-bad" is not a valid machine tag`, }, { tag: "unit-123", err: `"unit-123" is not a valid unit tag`, }, { tag: "relation-blah", err: `"relation-blah" is not a valid relation tag`, }, { tag: "relation-svc1.rel1#svc2.rel2", err: `relation "svc1:rel1 svc2:rel2" not found`, }, { tag: "unit-foo", err: `"unit-foo" is not a valid unit tag`, }, { tag: "service-", err: `"service-" is not a valid service tag`, }, { tag: "service-foo/bar", err: `"service-foo/bar" is not a valid service tag`, }, { tag: "environment-9f484882-2f18-4fd2-967d-db9663db7bea", err: `environment "9f484882-2f18-4fd2-967d-db9663db7bea" not found`, }, { tag: "machine-1234", err: `machine 1234 not found`, }, { tag: "unit-foo-654", err: `unit "foo/654" not found`, }, { tag: "unit-foo-bar-654", err: `unit "foo-bar/654" not found`, }, { tag: "machine-0", }, { tag: "service-ser-vice2", }, { tag: "relation-wordpress.db#ser-vice2.server", }, { tag: "unit-ser-vice2-0", }, { tag: "user-arble", }, { // TODO(axw) 2013-12-04 #1257587 // remove backwards compatibility for environment-tag; see state.go tag: "environment-notauuid", //err: `"environment-notauuid" is not a valid environment tag`, }, { tag: "environment-testenv", //err: `"environment-testenv" is not a valid environment tag`, }} var entityTypes = map[string]interface{}{ names.UserTagKind: (*state.User)(nil), names.EnvironTagKind: (*state.Environment)(nil), names.ServiceTagKind: (*state.Service)(nil), names.UnitTagKind: (*state.Unit)(nil), names.MachineTagKind: (*state.Machine)(nil), names.RelationTagKind: (*state.Relation)(nil), } func (s *StateSuite) TestFindEntity(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) svc := s.AddTestingService(c, "ser-vice2", s.AddTestingCharm(c, "mysql")) _, err = svc.AddUnit() c.Assert(err, gc.IsNil) _, err = s.State.AddUser("arble", "pass") c.Assert(err, gc.IsNil) s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) eps, err := s.State.InferEndpoints([]string{"wordpress", "ser-vice2"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) c.Assert(rel.String(), gc.Equals, "wordpress:db ser-vice2:server") // environment tag is dynamically generated env, err := s.State.Environment() c.Assert(err, gc.IsNil) findEntityTests = append([]findEntityTest{}, findEntityTests...) findEntityTests = append(findEntityTests, findEntityTest{ tag: "environment-" + env.UUID(), }) for i, test := range findEntityTests { c.Logf("test %d: %q", i, test.tag) e, err := s.State.FindEntity(test.tag) if test.err != "" { c.Assert(err, gc.ErrorMatches, test.err) } else { c.Assert(err, gc.IsNil) kind, err := names.TagKind(test.tag) c.Assert(err, gc.IsNil) c.Assert(e, gc.FitsTypeOf, entityTypes[kind]) if kind == "environment" { // TODO(axw) 2013-12-04 #1257587 // We *should* only be able to get the entity with its tag, but // for backwards-compatibility we accept any non-UUID tag. c.Assert(e.Tag(), gc.Equals, env.Tag()) } else { c.Assert(e.Tag(), gc.Equals, test.tag) } } } } func (s *StateSuite) TestParseTag(c *gc.C) { bad := []string{ "", "machine", "-foo", "foo-", "---", "foo-bar", "unit-foo", } for _, name := range bad { c.Logf(name) coll, id, err := state.ParseTag(s.State, name) c.Check(coll, gc.Equals, "") c.Check(id, gc.Equals, "") c.Assert(err, gc.ErrorMatches, `".*" is not a valid( [a-z]+)? tag`) } // Parse a machine entity name. m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) coll, id, err := state.ParseTag(s.State, m.Tag()) c.Assert(coll, gc.Equals, "machines") c.Assert(id, gc.Equals, m.Id()) c.Assert(err, gc.IsNil) // Parse a service entity name. svc := s.AddTestingService(c, "ser-vice2", s.AddTestingCharm(c, "dummy")) coll, id, err = state.ParseTag(s.State, svc.Tag()) c.Assert(coll, gc.Equals, "services") c.Assert(id, gc.Equals, svc.Name()) c.Assert(err, gc.IsNil) // Parse a unit entity name. u, err := svc.AddUnit() c.Assert(err, gc.IsNil) coll, id, err = state.ParseTag(s.State, u.Tag()) c.Assert(coll, gc.Equals, "units") c.Assert(id, gc.Equals, u.Name()) c.Assert(err, gc.IsNil) // Parse a user entity name. user, err := s.State.AddUser("arble", "pass") c.Assert(err, gc.IsNil) coll, id, err = state.ParseTag(s.State, user.Tag()) c.Assert(coll, gc.Equals, "users") c.Assert(id, gc.Equals, user.Name()) c.Assert(err, gc.IsNil) // Parse an environment entity name. env, err := s.State.Environment() c.Assert(err, gc.IsNil) coll, id, err = state.ParseTag(s.State, env.Tag()) c.Assert(coll, gc.Equals, "environments") c.Assert(id, gc.Equals, env.UUID()) c.Assert(err, gc.IsNil) } func (s *StateSuite) TestWatchCleanups(c *gc.C) { // Check initial event. w := s.State.WatchCleanups() defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.State, w) wc.AssertOneChange() // Set up two relations for later use, check no events. s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) c.Assert(err, gc.IsNil) relM, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) s.AddTestingService(c, "varnish", s.AddTestingCharm(c, "varnish")) c.Assert(err, gc.IsNil) eps, err = s.State.InferEndpoints([]string{"wordpress", "varnish"}) c.Assert(err, gc.IsNil) relV, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Destroy one relation, check one change. err = relM.Destroy() c.Assert(err, gc.IsNil) wc.AssertOneChange() // Handle that cleanup doc and create another, check one change. err = s.State.Cleanup() c.Assert(err, gc.IsNil) err = relV.Destroy() c.Assert(err, gc.IsNil) wc.AssertOneChange() // Clean up final doc, check change. err = s.State.Cleanup() c.Assert(err, gc.IsNil) wc.AssertOneChange() // Stop watcher, check closed. statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *StateSuite) TestWatchCleanupsDiesOnStateClose(c *gc.C) { testWatcherDiesWhenStateCloses(c, func(c *gc.C, st *state.State) waiter { w := st.WatchCleanups() <-w.Changes() return w }) } func (s *StateSuite) TestWatchCleanupsBulk(c *gc.C) { // Check initial event. w := s.State.WatchCleanups() defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.State, w) wc.AssertOneChange() // Create two peer relations by creating their services. riak := s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak")) _, err := riak.Endpoint("ring") c.Assert(err, gc.IsNil) allHooks := s.AddTestingService(c, "all-hooks", s.AddTestingCharm(c, "all-hooks")) _, err = allHooks.Endpoint("self") c.Assert(err, gc.IsNil) wc.AssertNoChange() // Destroy them both, check one change. err = riak.Destroy() c.Assert(err, gc.IsNil) err = allHooks.Destroy() c.Assert(err, gc.IsNil) wc.AssertOneChange() // Clean them both up, check one change. err = s.State.Cleanup() c.Assert(err, gc.IsNil) wc.AssertOneChange() } func (s *StateSuite) TestWatchMinUnits(c *gc.C) { // Check initial event. w := s.State.WatchMinUnits() defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.State, w) wc.AssertChange() wc.AssertNoChange() // Set up services for later use. wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) wordpressName := wordpress.Name() // Add service units for later use. wordpress0, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) wordpress1, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) mysql0, err := mysql.AddUnit() c.Assert(err, gc.IsNil) // No events should occur. wc.AssertNoChange() // Add minimum units to a service; a single change should occur. err = wordpress.SetMinUnits(2) c.Assert(err, gc.IsNil) wc.AssertChange(wordpressName) wc.AssertNoChange() // Decrease minimum units for a service; expect no changes. err = wordpress.SetMinUnits(1) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Increase minimum units for two services; a single change should occur. err = mysql.SetMinUnits(1) c.Assert(err, gc.IsNil) err = wordpress.SetMinUnits(3) c.Assert(err, gc.IsNil) wc.AssertChange(mysql.Name(), wordpressName) wc.AssertNoChange() // Remove minimum units for a service; expect no changes. err = mysql.SetMinUnits(0) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Destroy a unit of a service with required minimum units. // Also avoid the unit removal. A single change should occur. preventUnitDestroyRemove(c, wordpress0) err = wordpress0.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange(wordpressName) wc.AssertNoChange() // Two actions: destroy a unit and increase minimum units for a service. // A single change should occur, and the service name should appear only // one time in the change. err = wordpress.SetMinUnits(5) c.Assert(err, gc.IsNil) err = wordpress1.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange(wordpressName) wc.AssertNoChange() // Destroy a unit of a service not requiring minimum units; expect no changes. err = mysql0.Destroy() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Destroy a service with required minimum units; expect no changes. err = wordpress.Destroy() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Destroy a service not requiring minimum units; expect no changes. err = mysql.Destroy() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Stop watcher, check closed. statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *StateSuite) TestWatchMinUnitsDiesOnStateClose(c *gc.C) { testWatcherDiesWhenStateCloses(c, func(c *gc.C, st *state.State) waiter { w := st.WatchMinUnits() <-w.Changes() return w }) } func (s *StateSuite) TestNestingLevel(c *gc.C) { c.Assert(state.NestingLevel("0"), gc.Equals, 0) c.Assert(state.NestingLevel("0/lxc/1"), gc.Equals, 1) c.Assert(state.NestingLevel("0/lxc/1/kvm/0"), gc.Equals, 2) } func (s *StateSuite) TestTopParentId(c *gc.C) { c.Assert(state.TopParentId("0"), gc.Equals, "0") c.Assert(state.TopParentId("0/lxc/1"), gc.Equals, "0") c.Assert(state.TopParentId("0/lxc/1/kvm/2"), gc.Equals, "0") } func (s *StateSuite) TestParentId(c *gc.C) { c.Assert(state.ParentId("0"), gc.Equals, "") c.Assert(state.ParentId("0/lxc/1"), gc.Equals, "0") c.Assert(state.ParentId("0/lxc/1/kvm/0"), gc.Equals, "0/lxc/1") } func (s *StateSuite) TestContainerTypeFromId(c *gc.C) { c.Assert(state.ContainerTypeFromId("0"), gc.Equals, instance.ContainerType("")) c.Assert(state.ContainerTypeFromId("0/lxc/1"), gc.Equals, instance.LXC) c.Assert(state.ContainerTypeFromId("0/lxc/1/kvm/0"), gc.Equals, instance.KVM) } func (s *StateSuite) TestSetEnvironAgentVersionErrors(c *gc.C) { // Get the agent-version set in the environment. envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) agentVersion, ok := envConfig.AgentVersion() c.Assert(ok, jc.IsTrue) stringVersion := agentVersion.String() // Add 4 machines: one with a different version, one with an // empty version, one with the current version, and one with // the new version. machine0, err := s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine0.SetAgentVersion(version.MustParseBinary("9.9.9-series-arch")) c.Assert(err, gc.IsNil) machine1, err := s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, gc.IsNil) machine2, err := s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine2.SetAgentVersion(version.MustParseBinary(stringVersion + "-series-arch")) c.Assert(err, gc.IsNil) machine3, err := s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine3.SetAgentVersion(version.MustParseBinary("4.5.6-series-arch")) c.Assert(err, gc.IsNil) // Verify machine0 and machine1 are reported as error. err = s.State.SetEnvironAgentVersion(version.MustParse("4.5.6")) expectErr := fmt.Sprintf("some agents have not upgraded to the current environment version %s: machine-0, machine-1", stringVersion) c.Assert(err, gc.ErrorMatches, expectErr) c.Assert(err, jc.Satisfies, state.IsVersionInconsistentError) // Add a service and 4 units: one with a different version, one // with an empty version, one with the current version, and one // with the new version. service, err := s.State.AddService("wordpress", "user-admin", s.AddTestingCharm(c, "wordpress"), nil, nil) c.Assert(err, gc.IsNil) unit0, err := service.AddUnit() c.Assert(err, gc.IsNil) err = unit0.SetAgentVersion(version.MustParseBinary("6.6.6-series-arch")) c.Assert(err, gc.IsNil) _, err = service.AddUnit() c.Assert(err, gc.IsNil) unit2, err := service.AddUnit() c.Assert(err, gc.IsNil) err = unit2.SetAgentVersion(version.MustParseBinary(stringVersion + "-series-arch")) c.Assert(err, gc.IsNil) unit3, err := service.AddUnit() c.Assert(err, gc.IsNil) err = unit3.SetAgentVersion(version.MustParseBinary("4.5.6-series-arch")) c.Assert(err, gc.IsNil) // Verify unit0 and unit1 are reported as error, along with the // machines from before. err = s.State.SetEnvironAgentVersion(version.MustParse("4.5.6")) expectErr = fmt.Sprintf("some agents have not upgraded to the current environment version %s: machine-0, machine-1, unit-wordpress-0, unit-wordpress-1", stringVersion) c.Assert(err, gc.ErrorMatches, expectErr) c.Assert(err, jc.Satisfies, state.IsVersionInconsistentError) // Now remove the machines. for _, machine := range []*state.Machine{machine0, machine1, machine2} { err = machine.EnsureDead() c.Assert(err, gc.IsNil) err = machine.Remove() c.Assert(err, gc.IsNil) } // Verify only the units are reported as error. err = s.State.SetEnvironAgentVersion(version.MustParse("4.5.6")) expectErr = fmt.Sprintf("some agents have not upgraded to the current environment version %s: unit-wordpress-0, unit-wordpress-1", stringVersion) c.Assert(err, gc.ErrorMatches, expectErr) c.Assert(err, jc.Satisfies, state.IsVersionInconsistentError) } func (s *StateSuite) prepareAgentVersionTests(c *gc.C) (*config.Config, string) { // Get the agent-version set in the environment. envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) agentVersion, ok := envConfig.AgentVersion() c.Assert(ok, jc.IsTrue) currentVersion := agentVersion.String() // Add a machine and a unit with the current version. machine, err := s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, gc.IsNil) service, err := s.State.AddService("wordpress", "user-admin", s.AddTestingCharm(c, "wordpress"), nil, nil) c.Assert(err, gc.IsNil) unit, err := service.AddUnit() c.Assert(err, gc.IsNil) err = machine.SetAgentVersion(version.MustParseBinary(currentVersion + "-series-arch")) c.Assert(err, gc.IsNil) err = unit.SetAgentVersion(version.MustParseBinary(currentVersion + "-series-arch")) c.Assert(err, gc.IsNil) return envConfig, currentVersion } func (s *StateSuite) changeEnviron(c *gc.C, envConfig *config.Config, name string, value interface{}) { attrs := envConfig.AllAttrs() attrs[name] = value c.Assert(s.State.UpdateEnvironConfig(attrs, nil, nil), gc.IsNil) } func (s *StateSuite) assertAgentVersion(c *gc.C, envConfig *config.Config, vers string) { envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) agentVersion, ok := envConfig.AgentVersion() c.Assert(ok, jc.IsTrue) c.Assert(agentVersion.String(), gc.Equals, vers) } func (s *StateSuite) TestSetEnvironAgentVersionRetriesOnConfigChange(c *gc.C) { envConfig, _ := s.prepareAgentVersionTests(c) // Set up a transaction hook to change something // other than the version, and make sure it retries // and passes. defer state.SetBeforeHooks(c, s.State, func() { s.changeEnviron(c, envConfig, "default-series", "foo") }).Check() // Change the agent-version and ensure it has changed. err := s.State.SetEnvironAgentVersion(version.MustParse("4.5.6")) c.Assert(err, gc.IsNil) s.assertAgentVersion(c, envConfig, "4.5.6") } func (s *StateSuite) TestSetEnvironAgentVersionSucceedsWithSameVersion(c *gc.C) { envConfig, _ := s.prepareAgentVersionTests(c) // Set up a transaction hook to change the version // to the new one, and make sure it retries // and passes. defer state.SetBeforeHooks(c, s.State, func() { s.changeEnviron(c, envConfig, "agent-version", "4.5.6") }).Check() // Change the agent-version and verify. err := s.State.SetEnvironAgentVersion(version.MustParse("4.5.6")) c.Assert(err, gc.IsNil) s.assertAgentVersion(c, envConfig, "4.5.6") } func (s *StateSuite) TestSetEnvironAgentVersionExcessiveContention(c *gc.C) { envConfig, currentVersion := s.prepareAgentVersionTests(c) // Set a hook to change the config 5 times // to test we return ErrExcessiveContention. changeFuncs := []func(){ func() { s.changeEnviron(c, envConfig, "default-series", "1") }, func() { s.changeEnviron(c, envConfig, "default-series", "2") }, func() { s.changeEnviron(c, envConfig, "default-series", "3") }, func() { s.changeEnviron(c, envConfig, "default-series", "4") }, func() { s.changeEnviron(c, envConfig, "default-series", "5") }, } defer state.SetBeforeHooks(c, s.State, changeFuncs...).Check() err := s.State.SetEnvironAgentVersion(version.MustParse("4.5.6")) c.Assert(err, gc.Equals, state.ErrExcessiveContention) // Make sure the version remained the same. s.assertAgentVersion(c, envConfig, currentVersion) } type waiter interface { Wait() error } // testWatcherDiesWhenStateCloses calls the given function to start a watcher, // closes the state and checks that the watcher dies with the expected error. // The watcher should already have consumed the first // event, otherwise the watcher's initialisation logic may // interact with the closed state, causing it to return an // unexpected error (often "Closed explictly"). func testWatcherDiesWhenStateCloses(c *gc.C, startWatcher func(c *gc.C, st *state.State) waiter) { st, err := state.Open(state.TestingStateInfo(), state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) watcher := startWatcher(c, st) err = st.Close() c.Assert(err, gc.IsNil) done := make(chan error) go func() { done <- watcher.Wait() }() select { case err := <-done: c.Assert(err, gc.Equals, state.ErrStateClosed) case <-time.After(testing.LongWait): c.Fatalf("watcher %T did not exit when state closed", watcher) } } func (s *StateSuite) TestStateServerInfo(c *gc.C) { ids, err := s.State.StateServerInfo() c.Assert(err, gc.IsNil) c.Assert(ids.MachineIds, gc.HasLen, 0) c.Assert(ids.VotingMachineIds, gc.HasLen, 0) // TODO(rog) more testing here when we can actually add // state servers. } func (s *StateSuite) TestOpenCreatesStateServersDoc(c *gc.C) { m0, err := s.State.AddMachine("quantal", state.JobHostUnits, state.JobManageEnviron) c.Assert(err, gc.IsNil) // Delete the stateServers collection to pretend this // is an older environment that had not created it // already. err = s.stateServers.DropCollection() c.Assert(err, gc.IsNil) // Sanity check that we have in fact deleted the right info. info, err := s.State.StateServerInfo() c.Assert(err, gc.NotNil) c.Assert(info, gc.IsNil) st, err := state.Open(state.TestingStateInfo(), state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st.Close() expectIds := []string{m0.Id()} expectStateServerInfo := &state.StateServerInfo{ MachineIds: expectIds, VotingMachineIds: expectIds, } info, err = st.StateServerInfo() c.Assert(err, gc.IsNil) c.Assert(info, gc.DeepEquals, expectStateServerInfo) } func (s *StateSuite) TestOpenCreatesAPIHostPortsDoc(c *gc.C) { // Delete the stateServers collection to pretend this // is an older environment that had not created it // already. err := s.stateServers.DropCollection() c.Assert(err, gc.IsNil) // Sanity check that we have in fact deleted the right info. addrs, err := s.State.APIHostPorts() c.Assert(err, gc.NotNil) c.Assert(addrs, gc.IsNil) st, err := state.Open(state.TestingStateInfo(), state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st.Close() addrs, err = s.State.APIHostPorts() c.Assert(err, gc.IsNil) c.Assert(addrs, gc.HasLen, 0) } func (s *StateSuite) TestReopenWithNoMachines(c *gc.C) { info, err := s.State.StateServerInfo() c.Assert(err, gc.IsNil) c.Assert(info, jc.DeepEquals, &state.StateServerInfo{}) st, err := state.Open(state.TestingStateInfo(), state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st.Close() info, err = s.State.StateServerInfo() c.Assert(err, gc.IsNil) c.Assert(info, jc.DeepEquals, &state.StateServerInfo{}) } func (s *StateSuite) TestOpenReplacesOldStateServersDoc(c *gc.C) { m0, err := s.State.AddMachine("quantal", state.JobHostUnits, state.JobManageEnviron) c.Assert(err, gc.IsNil) // Clear the voting machine ids from the stateServers collection // to pretend this is a semi-old environment that had // created the collection but not the voting ids. _, err = s.stateServers.UpdateAll(nil, bson.D{{ "$set", bson.D{ {"votingmachineids", nil}, {"machineids", nil}, }, }}) c.Assert(err, gc.IsNil) // Sanity check that they have actually been removed. info, err := s.State.StateServerInfo() c.Assert(err, gc.IsNil) c.Assert(info.MachineIds, gc.HasLen, 0) c.Assert(info.VotingMachineIds, gc.HasLen, 0) st, err := state.Open(state.TestingStateInfo(), state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st.Close() info, err = s.State.StateServerInfo() c.Assert(err, gc.IsNil) expectIds := []string{m0.Id()} c.Assert(info, gc.DeepEquals, &state.StateServerInfo{ MachineIds: expectIds, VotingMachineIds: expectIds, }) } func (s *StateSuite) TestEnsureAvailabilityFailsWithBadCount(c *gc.C) { for _, n := range []int{-1, 0, 2, 6} { err := s.State.EnsureAvailability(n, constraints.Value{}, "") c.Assert(err, gc.ErrorMatches, "number of state servers must be odd and greater than zero") } err := s.State.EnsureAvailability(replicaset.MaxPeers+2, constraints.Value{}, "") c.Assert(err, gc.ErrorMatches, `state server count is too large \(allowed \d+\)`) } func (s *StateSuite) TestEnsureAvailabilityAddsNewMachines(c *gc.C) { ids := make([]string, 3) m0, err := s.State.AddMachine("quantal", state.JobHostUnits, state.JobManageEnviron) c.Assert(err, gc.IsNil) ids[0] = m0.Id() // Add a non-state-server machine just to make sure. _, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) info, err := s.State.StateServerInfo() c.Assert(err, gc.IsNil) c.Assert(info, gc.DeepEquals, &state.StateServerInfo{ MachineIds: []string{m0.Id()}, VotingMachineIds: []string{m0.Id()}, }) cons := constraints.Value{ Mem: newUint64(100), } err = s.State.EnsureAvailability(3, cons, "quantal") c.Assert(err, gc.IsNil) for i := 1; i < 3; i++ { m, err := s.State.Machine(fmt.Sprint(i + 1)) c.Assert(err, gc.IsNil) c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{ state.JobHostUnits, state.JobManageEnviron, }) gotCons, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(gotCons, gc.DeepEquals, cons) c.Assert(m.WantsVote(), jc.IsTrue) ids[i] = m.Id() } sort.Strings(ids) info, err = s.State.StateServerInfo() c.Assert(err, gc.IsNil) sort.Strings(info.MachineIds) sort.Strings(info.VotingMachineIds) c.Assert(info, gc.DeepEquals, &state.StateServerInfo{ MachineIds: ids, VotingMachineIds: ids, }) } func newUint64(i uint64) *uint64 { return &i } func (s *StateSuite) TestStateServingInfo(c *gc.C) { info, err := s.State.StateServingInfo() c.Assert(info, jc.DeepEquals, params.StateServingInfo{}) // no error because empty doc created by default c.Assert(err, gc.IsNil) data := params.StateServingInfo{ APIPort: 69, StatePort: 80, Cert: "Some cert", PrivateKey: "Some key", SharedSecret: "Some Keyfile", } err = s.State.SetStateServingInfo(data) c.Assert(err, gc.IsNil) info, err = s.State.StateServingInfo() c.Assert(err, gc.IsNil) c.Assert(info, jc.DeepEquals, data) } func (s *StateSuite) TestOpenCreatesStateServingInfoDoc(c *gc.C) { // Delete the stateServers collection to pretend this // is an older environment that had not created it // already. err := s.stateServers.DropCollection() c.Assert(err, gc.IsNil) st, err := state.Open(state.TestingStateInfo(), state.TestingDialOpts(), state.Policy(nil)) c.Assert(err, gc.IsNil) defer st.Close() info, err := st.StateServingInfo() c.Assert(err, gc.IsNil) c.Assert(info, gc.DeepEquals, params.StateServingInfo{}) } func (s *StateSuite) TestSetAPIHostPorts(c *gc.C) { addrs, err := s.State.APIHostPorts() c.Assert(err, gc.IsNil) c.Assert(addrs, gc.HasLen, 0) newHostPorts := [][]instance.HostPort{{{ Address: instance.Address{ Value: "0.2.4.6", Type: instance.Ipv4Address, NetworkName: "net", NetworkScope: instance.NetworkCloudLocal, }, Port: 1, }, { Address: instance.Address{ Value: "0.4.8.16", Type: instance.Ipv4Address, NetworkName: "foo", NetworkScope: instance.NetworkPublic, }, Port: 2, }}, {{ Address: instance.Address{ Value: "0.6.1.2", Type: instance.Ipv4Address, NetworkName: "net", NetworkScope: instance.NetworkCloudLocal, }, Port: 5, }}} err = s.State.SetAPIHostPorts(newHostPorts) c.Assert(err, gc.IsNil) gotHostPorts, err := s.State.APIHostPorts() c.Assert(err, gc.IsNil) c.Assert(gotHostPorts, jc.DeepEquals, newHostPorts) newHostPorts = [][]instance.HostPort{{{ Address: instance.Address{ Value: "0.2.4.6", Type: instance.Ipv6Address, NetworkName: "net", NetworkScope: instance.NetworkCloudLocal, }, Port: 13, }}} err = s.State.SetAPIHostPorts(newHostPorts) c.Assert(err, gc.IsNil) gotHostPorts, err = s.State.APIHostPorts() c.Assert(err, gc.IsNil) c.Assert(gotHostPorts, jc.DeepEquals, newHostPorts) } func (s *StateSuite) TestWatchAPIHostPorts(c *gc.C) { w := s.State.WatchAPIHostPorts() defer statetesting.AssertStop(c, w) // Initial event. wc := statetesting.NewNotifyWatcherC(c, s.State, w) wc.AssertOneChange() err := s.State.SetAPIHostPorts([][]instance.HostPort{{{ Address: instance.NewAddress("0.1.2.3"), Port: 99, }}}) c.Assert(err, gc.IsNil) wc.AssertOneChange() // Stop, check closed. statetesting.AssertStop(c, w) wc.AssertClosed() } ����������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/unit.go������������������������������������������0000644�0000153�0000161�00000123571�12321735776�023334� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( stderrors "errors" "fmt" "time" "github.com/juju/loggo" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/presence" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) var unitLogger = loggo.GetLogger("juju.state.unit") // AssignmentPolicy controls what machine a unit will be assigned to. type AssignmentPolicy string const ( // AssignLocal indicates that all service units should be assigned // to machine 0. AssignLocal AssignmentPolicy = "local" // AssignClean indicates that every service unit should be assigned // to a machine which never previously has hosted any units, and that // new machines should be launched if required. AssignClean AssignmentPolicy = "clean" // AssignCleanEmpty indicates that every service unit should be assigned // to a machine which never previously has hosted any units, and which is not // currently hosting any containers, and that new machines should be launched if required. AssignCleanEmpty AssignmentPolicy = "clean-empty" // AssignNew indicates that every service unit should be assigned to a new // dedicated machine. A new machine will be launched for each new unit. AssignNew AssignmentPolicy = "new" ) // ResolvedMode describes the way state transition errors // are resolved. type ResolvedMode string const ( ResolvedNone ResolvedMode = "" ResolvedRetryHooks ResolvedMode = "retry-hooks" ResolvedNoHooks ResolvedMode = "no-hooks" ) // unitDoc represents the internal state of a unit in MongoDB. // Note the correspondence with UnitInfo in state/api/params. type unitDoc struct { Name string `bson:"_id"` Service string Series string CharmURL *charm.URL Principal string Subordinates []string PublicAddress string PrivateAddress string MachineId string Resolved ResolvedMode Tools *tools.Tools `bson:",omitempty"` Ports []instance.Port Life Life TxnRevno int64 `bson:"txn-revno"` PasswordHash string } // Unit represents the state of a service unit. type Unit struct { st *State doc unitDoc annotator } func newUnit(st *State, udoc *unitDoc) *Unit { unit := &Unit{ st: st, doc: *udoc, } unit.annotator = annotator{ globalKey: unit.globalKey(), tag: unit.Tag(), st: st, } return unit } // Service returns the service. func (u *Unit) Service() (*Service, error) { return u.st.Service(u.doc.Service) } // ConfigSettings returns the complete set of service charm config settings // available to the unit. Unset values will be replaced with the default // value for the associated option, and may thus be nil when no default is // specified. func (u *Unit) ConfigSettings() (charm.Settings, error) { if u.doc.CharmURL == nil { return nil, fmt.Errorf("unit charm not set") } settings, err := readSettings(u.st, serviceSettingsKey(u.doc.Service, u.doc.CharmURL)) if err != nil { return nil, err } chrm, err := u.st.Charm(u.doc.CharmURL) if err != nil { return nil, err } result := chrm.Config().DefaultSettings() for name, value := range settings.Map() { result[name] = value } return result, nil } // ServiceName returns the service name. func (u *Unit) ServiceName() string { return u.doc.Service } // Series returns the deployed charm's series. func (u *Unit) Series() string { return u.doc.Series } // String returns the unit as string. func (u *Unit) String() string { return u.doc.Name } // Name returns the unit name. func (u *Unit) Name() string { return u.doc.Name } // unitGlobalKey returns the global database key for the named unit. func unitGlobalKey(name string) string { return "u#" + name } // globalKey returns the global database key for the unit. func (u *Unit) globalKey() string { return unitGlobalKey(u.doc.Name) } // Life returns whether the unit is Alive, Dying or Dead. func (u *Unit) Life() Life { return u.doc.Life } // AgentTools returns the tools that the agent is currently running. // It an error that satisfies IsNotFound if the tools have not yet been set. func (u *Unit) AgentTools() (*tools.Tools, error) { if u.doc.Tools == nil { return nil, errors.NotFoundf("agent tools for unit %q", u) } tools := *u.doc.Tools return &tools, nil } // SetAgentVersion sets the version of juju that the agent is // currently running. func (u *Unit) SetAgentVersion(v version.Binary) (err error) { defer utils.ErrorContextf(&err, "cannot set agent version for unit %q", u) if err = checkVersionValidity(v); err != nil { return err } tools := &tools.Tools{Version: v} ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: notDeadDoc, Update: bson.D{{"$set", bson.D{{"tools", tools}}}}, }} if err := u.st.runTransaction(ops); err != nil { return onAbort(err, errDead) } u.doc.Tools = tools return nil } // SetMongoPassword sets the password the agent responsible for the unit // should use to communicate with the state servers. Previous passwords // are invalidated. func (u *Unit) SetMongoPassword(password string) error { return u.st.setMongoPassword(u.Tag(), password) } // SetPassword sets the password for the machine's agent. func (u *Unit) SetPassword(password string) error { if len(password) < utils.MinAgentPasswordLength { return fmt.Errorf("password is only %d bytes long, and is not a valid Agent password", len(password)) } return u.setPasswordHash(utils.AgentPasswordHash(password)) } // setPasswordHash sets the underlying password hash in the database directly // to the value supplied. This is split out from SetPassword to allow direct // manipulation in tests (to check for backwards compatibility). func (u *Unit) setPasswordHash(passwordHash string) error { ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: notDeadDoc, Update: bson.D{{"$set", bson.D{{"passwordhash", passwordHash}}}}, }} err := u.st.runTransaction(ops) if err != nil { return fmt.Errorf("cannot set password of unit %q: %v", u, onAbort(err, errDead)) } u.doc.PasswordHash = passwordHash return nil } // Return the underlying PasswordHash stored in the database. Used by the test // suite to check that the PasswordHash gets properly updated to new values // when compatibility mode is detected. func (u *Unit) getPasswordHash() string { return u.doc.PasswordHash } // PasswordValid returns whether the given password is valid // for the given unit. func (u *Unit) PasswordValid(password string) bool { agentHash := utils.AgentPasswordHash(password) if agentHash == u.doc.PasswordHash { return true } // In Juju 1.16 and older we used the slower password hash for unit // agents. So check to see if the supplied password matches the old // path, and if so, update it to the new mechanism. // We ignore any error in setting the password hash, as we'll just try // again next time if utils.UserPasswordHash(password, utils.CompatSalt) == u.doc.PasswordHash { logger.Debugf("%s logged in with old password hash, changing to AgentPasswordHash", u.Tag()) u.setPasswordHash(agentHash) return true } return false } // Destroy, when called on a Alive unit, advances its lifecycle as far as // possible; it otherwise has no effect. In most situations, the unit's // life is just set to Dying; but if a principal unit that is not assigned // to a provisioned machine is Destroyed, it will be removed from state // directly. func (u *Unit) Destroy() (err error) { defer func() { if err == nil { // This is a white lie; the document might actually be removed. u.doc.Life = Dying } }() unit := &Unit{st: u.st, doc: u.doc} for i := 0; i < 5; i++ { switch ops, err := unit.destroyOps(); err { case errRefresh: case errAlreadyDying: return nil case nil: if err := unit.st.runTransaction(ops); err != txn.ErrAborted { return err } default: return err } if err := unit.Refresh(); errors.IsNotFoundError(err) { return nil } else if err != nil { return err } } return ErrExcessiveContention } // destroyOps returns the operations required to destroy the unit. If it // returns errRefresh, the unit should be refreshed and the destruction // operations recalculated. func (u *Unit) destroyOps() ([]txn.Op, error) { if u.doc.Life != Alive { return nil, errAlreadyDying } // Where possible, we'd like to be able to short-circuit unit destruction // such that units can be removed directly rather than waiting for their // agents to start, observe Dying, set Dead, and shut down; this takes a // long time and is vexing to users. This turns out to be possible if and // only if the unit agent has not yet set its status; this implies that the // most the unit could possibly have done is to run its install hook. // // There's no harm in removing a unit that's run its install hook only -- // or, at least, there is no more harm than there is in removing a unit // that's run its stop hook, and that's the usual condition. // // Principals with subordinates are never eligible for this shortcut, // because the unit agent must inevitably have set a status before getting // to the point where it can actually create its subordinate. // // Subordinates should be eligible for the shortcut but are not currently // considered, on the basis that (1) they were created by active principals // and can be expected to be deployed pretty soon afterwards, so we don't // lose much time and (2) by maintaining this restriction, I can reduce // the number of tests that have to change and defer that improvement to // its own CL. minUnitsOp := minUnitsTriggerOp(u.st, u.ServiceName()) setDyingOps := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: isAliveDoc, Update: bson.D{{"$set", bson.D{{"life", Dying}}}}, }, minUnitsOp} if u.doc.Principal != "" { return setDyingOps, nil } else if len(u.doc.Subordinates) != 0 { return setDyingOps, nil } sdocId := u.globalKey() sdoc, err := getStatus(u.st, sdocId) if errors.IsNotFoundError(err) { return nil, errAlreadyDying } else if err != nil { return nil, err } if sdoc.Status != params.StatusPending { return setDyingOps, nil } ops := []txn.Op{{ C: u.st.statuses.Name, Id: sdocId, Assert: bson.D{{"status", params.StatusPending}}, }, minUnitsOp} removeAsserts := append(isAliveDoc, unitHasNoSubordinates...) removeOps, err := u.removeOps(removeAsserts) if err == errAlreadyRemoved { return nil, errAlreadyDying } else if err != nil { return nil, err } return append(ops, removeOps...), nil } var errAlreadyRemoved = stderrors.New("entity has already been removed") // removeOps returns the operations necessary to remove the unit, assuming // the supplied asserts apply to the unit document. func (u *Unit) removeOps(asserts bson.D) ([]txn.Op, error) { svc, err := u.st.Service(u.doc.Service) if errors.IsNotFoundError(err) { // If the service has been removed, the unit must already have been. return nil, errAlreadyRemoved } else if err != nil { return nil, err } return svc.removeUnitOps(u, asserts) } var ErrUnitHasSubordinates = stderrors.New("unit has subordinates") var unitHasNoSubordinates = bson.D{{ "$or", []bson.D{ {{"subordinates", bson.D{{"$size", 0}}}}, {{"subordinates", bson.D{{"$exists", false}}}}, }, }} // EnsureDead sets the unit lifecycle to Dead if it is Alive or Dying. // It does nothing otherwise. If the unit has subordinates, it will // return ErrUnitHasSubordinates. func (u *Unit) EnsureDead() (err error) { if u.doc.Life == Dead { return nil } defer func() { if err == nil { u.doc.Life = Dead } }() ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: append(notDeadDoc, unitHasNoSubordinates...), Update: bson.D{{"$set", bson.D{{"life", Dead}}}}, }} if err := u.st.runTransaction(ops); err != txn.ErrAborted { return err } if notDead, err := isNotDead(u.st.units, u.doc.Name); err != nil { return err } else if !notDead { return nil } return ErrUnitHasSubordinates } // Remove removes the unit from state, and may remove its service as well, if // the service is Dying and no other references to it exist. It will fail if // the unit is not Dead. func (u *Unit) Remove() (err error) { defer utils.ErrorContextf(&err, "cannot remove unit %q", u) if u.doc.Life != Dead { return stderrors.New("unit is not dead") } // Now the unit is Dead, we can be sure that it's impossible for it to // enter relation scopes (once it's Dying, we can be sure of this; but // EnsureDead does not require that it already be Dying, so this is the // only point at which we can safely backstop lp:1233457 and mitigate // the impact of unit agent bugs that leave relation scopes occupied). relations, err := serviceRelations(u.st, u.doc.Service) if err != nil { return err } for _, rel := range relations { ru, err := rel.Unit(u) if err != nil { return err } if err := ru.LeaveScope(); err != nil { return err } } // Now we're sure we haven't left any scopes occupied by this unit, we // can safely remove the document. unit := &Unit{st: u.st, doc: u.doc} for i := 0; i < 5; i++ { switch ops, err := unit.removeOps(isDeadDoc); err { case errRefresh: case errAlreadyRemoved: return nil case nil: if err := u.st.runTransaction(ops); err != txn.ErrAborted { return err } default: return err } if err := unit.Refresh(); errors.IsNotFoundError(err) { return nil } else if err != nil { return err } } return ErrExcessiveContention } // Resolved returns the resolved mode for the unit. func (u *Unit) Resolved() ResolvedMode { return u.doc.Resolved } // IsPrincipal returns whether the unit is deployed in its own container, // and can therefore have subordinate services deployed alongside it. func (u *Unit) IsPrincipal() bool { return u.doc.Principal == "" } // SubordinateNames returns the names of any subordinate units. func (u *Unit) SubordinateNames() []string { names := make([]string, len(u.doc.Subordinates)) copy(names, u.doc.Subordinates) 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) { if u.doc.Principal != "" { return names.UnitTag(u.doc.Principal), true } else if u.doc.MachineId != "" { return names.MachineTag(u.doc.MachineId), true } return "", false } // PrincipalName returns the name of the unit's principal. // If the unit is not a subordinate, false is returned. func (u *Unit) PrincipalName() (string, bool) { return u.doc.Principal, u.doc.Principal != "" } // addressesOfMachine returns Addresses of the related machine if present. func (u *Unit) addressesOfMachine() []instance.Address { if id, err := u.AssignedMachineId(); err != nil { unitLogger.Errorf("unit %v cannot get assigned machine: %v", u, err) return nil } else { m, err := u.st.Machine(id) if err == nil { return m.Addresses() } unitLogger.Errorf("unit %v misses machine id %v", u, id) } return nil } // PublicAddress returns the public address of the unit and whether it is valid. func (u *Unit) PublicAddress() (string, bool) { publicAddress := u.doc.PublicAddress addresses := u.addressesOfMachine() if len(addresses) > 0 { publicAddress = instance.SelectPublicAddress(addresses) } return publicAddress, publicAddress != "" } // PrivateAddress returns the private address of the unit and whether it is valid. func (u *Unit) PrivateAddress() (string, bool) { privateAddress := u.doc.PrivateAddress addresses := u.addressesOfMachine() if len(addresses) > 0 { privateAddress = instance.SelectInternalAddress(addresses, false) } return privateAddress, privateAddress != "" } // Refresh refreshes the contents of the Unit from the underlying // state. It an error that satisfies IsNotFound if the unit has been removed. func (u *Unit) Refresh() error { err := u.st.units.FindId(u.doc.Name).One(&u.doc) if err == mgo.ErrNotFound { return errors.NotFoundf("unit %q", u) } if err != nil { return fmt.Errorf("cannot refresh unit %q: %v", u, err) } return nil } // Status returns the status of the unit. func (u *Unit) Status() (status params.Status, info string, data params.StatusData, err error) { doc, err := getStatus(u.st, u.globalKey()) if err != nil { return "", "", nil, err } status = doc.Status info = doc.StatusInfo data = doc.StatusData return } // SetStatus sets the status of the unit. The optional values // allow to pass additional helpful status data. func (u *Unit) SetStatus(status params.Status, info string, data params.StatusData) error { doc := statusDoc{ Status: status, StatusInfo: info, StatusData: data, } if err := doc.validateSet(false); err != nil { return err } ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: notDeadDoc, }, updateStatusOp(u.st, u.globalKey(), doc), } err := u.st.runTransaction(ops) if err != nil { return fmt.Errorf("cannot set status of unit %q: %v", u, onAbort(err, errDead)) } return nil } // OpenPort sets the policy of the port with protocol and number to be opened. func (u *Unit) OpenPort(protocol string, number int) (err error) { port := instance.Port{Protocol: protocol, Number: number} defer utils.ErrorContextf(&err, "cannot open port %v for unit %q", port, u) ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: notDeadDoc, Update: bson.D{{"$addToSet", bson.D{{"ports", port}}}}, }} err = u.st.runTransaction(ops) if err != nil { return onAbort(err, errDead) } found := false for _, p := range u.doc.Ports { if p == port { break } } if !found { u.doc.Ports = append(u.doc.Ports, port) } return nil } // ClosePort sets the policy of the port with protocol and number to be closed. func (u *Unit) ClosePort(protocol string, number int) (err error) { port := instance.Port{Protocol: protocol, Number: number} defer utils.ErrorContextf(&err, "cannot close port %v for unit %q", port, u) ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: notDeadDoc, Update: bson.D{{"$pull", bson.D{{"ports", port}}}}, }} err = u.st.runTransaction(ops) if err != nil { return onAbort(err, errDead) } newPorts := make([]instance.Port, 0, len(u.doc.Ports)) for _, p := range u.doc.Ports { if p != port { newPorts = append(newPorts, p) } } u.doc.Ports = newPorts return nil } // OpenedPorts returns a slice containing the open ports of the unit. func (u *Unit) OpenedPorts() []instance.Port { ports := append([]instance.Port{}, u.doc.Ports...) instance.SortPorts(ports) return ports } // CharmURL returns the charm URL this unit is currently using. func (u *Unit) CharmURL() (*charm.URL, bool) { if u.doc.CharmURL == nil { return nil, false } return u.doc.CharmURL, true } // SetCharmURL marks the unit as currently using the supplied charm URL. // An error will be returned if the unit is dead, or the charm URL not known. func (u *Unit) SetCharmURL(curl *charm.URL) (err error) { defer func() { if err == nil { u.doc.CharmURL = curl } }() if curl == nil { return fmt.Errorf("cannot set nil charm url") } for i := 0; i < 5; i++ { if notDead, err := isNotDead(u.st.units, u.doc.Name); err != nil { return err } else if !notDead { return fmt.Errorf("unit %q is dead", u) } sel := bson.D{{"_id", u.doc.Name}, {"charmurl", curl}} if count, err := u.st.units.Find(sel).Count(); err != nil { return err } else if count == 1 { // Already set return nil } if count, err := u.st.charms.FindId(curl).Count(); err != nil { return err } else if count < 1 { return fmt.Errorf("unknown charm url %q", curl) } // Add a reference to the service settings for the new charm. incOp, err := settingsIncRefOp(u.st, u.doc.Service, curl, false) if err != nil { return err } // Set the new charm URL. differentCharm := bson.D{{"charmurl", bson.D{{"$ne", curl}}}} ops := []txn.Op{ incOp, { C: u.st.units.Name, Id: u.doc.Name, Assert: append(notDeadDoc, differentCharm...), Update: bson.D{{"$set", bson.D{{"charmurl", curl}}}}, }} if u.doc.CharmURL != nil { // Drop the reference to the old charm. decOps, err := settingsDecRefOps(u.st, u.doc.Service, u.doc.CharmURL) if err != nil { return err } ops = append(ops, decOps...) } if err := u.st.runTransaction(ops); err != txn.ErrAborted { return err } } return ErrExcessiveContention } // AgentAlive returns whether the respective remote agent is alive. func (u *Unit) AgentAlive() (bool, error) { return u.st.pwatcher.Alive(u.globalKey()) } // Tag returns a name identifying the unit that is safe to use // as a file name. The returned name will be different from other // Tag values returned by any other entities from the same state. func (u *Unit) Tag() string { return names.UnitTag(u.Name()) } // WaitAgentAlive blocks until the respective agent is alive. func (u *Unit) WaitAgentAlive(timeout time.Duration) (err error) { defer utils.ErrorContextf(&err, "waiting for agent of unit %q", u) ch := make(chan presence.Change) u.st.pwatcher.Watch(u.globalKey(), ch) defer u.st.pwatcher.Unwatch(u.globalKey(), ch) for i := 0; i < 2; i++ { select { case change := <-ch: if change.Alive { return nil } case <-time.After(timeout): return fmt.Errorf("still not alive after timeout") case <-u.st.pwatcher.Dead(): return u.st.pwatcher.Err() } } panic(fmt.Sprintf("presence reported dead status twice in a row for unit %q", u)) } // SetAgentAlive signals that the agent for unit u is alive. // It returns the started pinger. func (u *Unit) SetAgentAlive() (*presence.Pinger, error) { p := presence.NewPinger(u.st.presence, u.globalKey()) err := p.Start() if err != nil { return nil, err } return p, nil } // NotAssignedError indicates that a unit is not assigned to a machine (and, in // the case of subordinate units, that the unit's principal is not assigned). type NotAssignedError struct{ Unit *Unit } func (e *NotAssignedError) Error() string { return fmt.Sprintf("unit %q is not assigned to a machine", e.Unit) } func IsNotAssigned(err error) bool { _, ok := err.(*NotAssignedError) return ok } // AssignedMachineId returns the id of the assigned machine. func (u *Unit) AssignedMachineId() (id string, err error) { if u.IsPrincipal() { if u.doc.MachineId == "" { return "", &NotAssignedError{u} } return u.doc.MachineId, nil } pudoc := unitDoc{} err = u.st.units.Find(bson.D{{"_id", u.doc.Principal}}).One(&pudoc) if err == mgo.ErrNotFound { return "", errors.NotFoundf("principal unit %q of %q", u.doc.Principal, u) } else if err != nil { return "", err } if pudoc.MachineId == "" { return "", &NotAssignedError{u} } return pudoc.MachineId, nil } var ( machineNotAliveErr = stderrors.New("machine is not alive") machineNotCleanErr = stderrors.New("machine is dirty") unitNotAliveErr = stderrors.New("unit is not alive") alreadyAssignedErr = stderrors.New("unit is already assigned to a machine") inUseErr = stderrors.New("machine is not unused") ) // assignToMachine is the internal version of AssignToMachine, // also used by AssignToUnusedMachine. It returns specific errors // in some cases: // - machineNotAliveErr when the machine is not alive. // - unitNotAliveErr when the unit is not alive. // - alreadyAssignedErr when the unit has already been assigned // - inUseErr when the machine already has a unit assigned (if unused is true) func (u *Unit) assignToMachine(m *Machine, unused bool) (err error) { if u.doc.Series != m.doc.Series { return fmt.Errorf("series does not match") } if u.doc.MachineId != "" { if u.doc.MachineId != m.Id() { return alreadyAssignedErr } return nil } if u.doc.Principal != "" { return fmt.Errorf("unit is a subordinate") } canHost := false for _, j := range m.doc.Jobs { if j == JobHostUnits { canHost = true break } } if !canHost { return fmt.Errorf("machine %q cannot host units", m) } assert := append(isAliveDoc, bson.D{ {"$or", []bson.D{ {{"machineid", ""}}, {{"machineid", m.Id()}}, }}, }...) massert := isAliveDoc if unused { massert = append(massert, bson.D{{"clean", bson.D{{"$ne", false}}}}...) } ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: assert, Update: bson.D{{"$set", bson.D{{"machineid", m.doc.Id}}}}, }, { C: u.st.machines.Name, Id: m.doc.Id, Assert: massert, Update: bson.D{{"$addToSet", bson.D{{"principals", u.doc.Name}}}, {"$set", bson.D{{"clean", false}}}}, }} err = u.st.runTransaction(ops) if err == nil { u.doc.MachineId = m.doc.Id m.doc.Clean = false return nil } if err != txn.ErrAborted { return err } u0, err := u.st.Unit(u.Name()) if err != nil { return err } m0, err := u.st.Machine(m.Id()) if err != nil { return err } switch { case u0.Life() != Alive: return unitNotAliveErr case m0.Life() != Alive: return machineNotAliveErr case u0.doc.MachineId != "" || !unused: return alreadyAssignedErr } return inUseErr } func assignContextf(err *error, unit *Unit, target string) { if *err != nil { *err = fmt.Errorf("cannot assign unit %q to %s: %v", unit, target, *err) } } // AssignToMachine assigns this unit to a given machine. func (u *Unit) AssignToMachine(m *Machine) (err error) { defer assignContextf(&err, u, fmt.Sprintf("machine %s", m)) return u.assignToMachine(m, false) } // assignToNewMachine assigns the unit to a machine created according to // the supplied params, with the supplied constraints. func (u *Unit) assignToNewMachine(template MachineTemplate, parentId string, containerType instance.ContainerType) error { template.principals = []string{u.doc.Name} template.Dirty = true var ( mdoc *machineDoc ops []txn.Op err error ) switch { case parentId == "" && containerType == "": mdoc, ops, err = u.st.addMachineOps(template) case parentId == "": if containerType == "" { return fmt.Errorf("assignToNewMachine called without container type (should never happen)") } // The new parent machine is clean and only hosts units, // regardless of its child. parentParams := template parentParams.Jobs = []MachineJob{JobHostUnits} mdoc, ops, err = u.st.addMachineInsideNewMachineOps(template, parentParams, containerType) default: // Container type is specified but no parent id. mdoc, ops, err = u.st.addMachineInsideMachineOps(template, parentId, containerType) } if err != nil { return err } // Ensure the host machine is really clean. if parentId != "" { ops = append(ops, txn.Op{ C: u.st.machines.Name, Id: parentId, Assert: bson.D{{"clean", true}}, }, txn.Op{ C: u.st.containerRefs.Name, Id: parentId, Assert: bson.D{hasNoContainersTerm}, }) } isUnassigned := bson.D{{"machineid", ""}} asserts := append(isAliveDoc, isUnassigned...) ops = append(ops, txn.Op{ C: u.st.units.Name, Id: u.doc.Name, Assert: asserts, Update: bson.D{{"$set", bson.D{{"machineid", mdoc.Id}}}}, }) err = u.st.runTransaction(ops) if err == nil { u.doc.MachineId = mdoc.Id return nil } else if err != txn.ErrAborted { return err } // If we assume that the machine ops will never give us an // operation that would fail (because the machine id(s) that it // chooses are unique), then the only reasons that the // transaction could have been aborted are: // * the unit is no longer alive // * the unit has been assigned to a different machine // * the parent machine we want to create a container on was // clean but became dirty unit, err := u.st.Unit(u.Name()) if err != nil { return err } switch { case unit.Life() != Alive: return unitNotAliveErr case unit.doc.MachineId != "": return alreadyAssignedErr } if parentId == "" { return fmt.Errorf("cannot add top level machine: transaction aborted for unknown reason") } m, err := u.st.Machine(parentId) if err != nil { return err } if !m.Clean() { return machineNotCleanErr } containers, err := m.Containers() if err != nil { return err } if len(containers) > 0 { return machineNotCleanErr } return fmt.Errorf("cannot add container within machine: transaction aborted for unknown reason") } // constraints is a helper function to return a unit's deployment constraints. func (u *Unit) constraints() (*constraints.Value, error) { cons, err := readConstraints(u.st, u.globalKey()) if errors.IsNotFoundError(err) { // Lack of constraints indicates lack of unit. return nil, errors.NotFoundf("unit") } else if err != nil { return nil, err } return &cons, nil } // AssignToNewMachineOrContainer assigns the unit to a new machine, // with constraints determined according to the service and // environment constraints at the time of unit creation. If a // container is required, a clean, empty machine instance is required // on which to create the container. An existing clean, empty instance // is first searched for, and if not found, a new one is created. func (u *Unit) AssignToNewMachineOrContainer() (err error) { defer assignContextf(&err, u, "new machine or container") if u.doc.Principal != "" { return fmt.Errorf("unit is a subordinate") } cons, err := u.constraints() if err != nil { return err } if !cons.HasContainer() { return u.AssignToNewMachine() } // Find a clean, empty machine on which to create a container. var host machineDoc hostCons := *cons noContainer := instance.NONE hostCons.Container = &noContainer query, err := u.findCleanMachineQuery(true, &hostCons) if err != nil { return err } err = query.One(&host) if err == mgo.ErrNotFound { // No existing clean, empty machine so create a new one. // The container constraint will be used by AssignToNewMachine to create the required container. return u.AssignToNewMachine() } else if err != nil { return err } svc, err := u.Service() if err != nil { return err } includeNetworks, excludeNetworks, err := svc.Networks() if err != nil { return err } template := MachineTemplate{ Series: u.doc.Series, Constraints: *cons, Jobs: []MachineJob{JobHostUnits}, IncludeNetworks: includeNetworks, ExcludeNetworks: excludeNetworks, } err = u.assignToNewMachine(template, host.Id, *cons.Container) if err == machineNotCleanErr { // The clean machine was used before we got a chance to use it so just // stick the unit on a new machine. return u.AssignToNewMachine() } return err } // AssignToNewMachine assigns the unit to a new machine, with constraints // determined according to the service and environment constraints at the // time of unit creation. func (u *Unit) AssignToNewMachine() (err error) { defer assignContextf(&err, u, "new machine") if u.doc.Principal != "" { return fmt.Errorf("unit is a subordinate") } // Get the ops necessary to create a new machine, and the machine doc that // will be added with those operations (which includes the machine id). cons, err := u.constraints() if err != nil { return err } var containerType instance.ContainerType // Configure to create a new container if required. if cons.HasContainer() { containerType = *cons.Container } svc, err := u.Service() if err != nil { return err } includeNetworks, excludeNetworks, err := svc.Networks() if err != nil { return err } template := MachineTemplate{ Series: u.doc.Series, Constraints: *cons, Jobs: []MachineJob{JobHostUnits}, IncludeNetworks: includeNetworks, ExcludeNetworks: excludeNetworks, } return u.assignToNewMachine(template, "", containerType) } var noCleanMachines = stderrors.New("all eligible machines in use") // AssignToCleanMachine assigns u to a machine which is marked as clean. A machine // is clean if it has never had any principal units assigned to it. // If there are no clean machines besides any machine(s) running JobHostEnviron, // an error is returned. // This method does not take constraints into consideration when choosing a // machine (lp:1161919). func (u *Unit) AssignToCleanMachine() (m *Machine, err error) { return u.assignToCleanMaybeEmptyMachine(false) } // AssignToCleanMachine assigns u to a machine which is marked as clean and is also // not hosting any containers. A machine is clean if it has never had any principal units // assigned to it. If there are no clean machines besides any machine(s) running JobHostEnviron, // an error is returned. // This method does not take constraints into consideration when choosing a // machine (lp:1161919). func (u *Unit) AssignToCleanEmptyMachine() (m *Machine, err error) { return u.assignToCleanMaybeEmptyMachine(true) } var hasContainerTerm = bson.DocElem{ "$and", []bson.D{ {{"children", bson.D{{"$not", bson.D{{"$size", 0}}}}}}, {{"children", bson.D{{"$exists", true}}}}, }} var hasNoContainersTerm = bson.DocElem{ "$or", []bson.D{ {{"children", bson.D{{"$size", 0}}}}, {{"children", bson.D{{"$exists", false}}}}, }} // findCleanMachineQuery returns a Mongo query to find clean (and possibly empty) machines with // characteristics matching the specified constraints. func (u *Unit) findCleanMachineQuery(requireEmpty bool, cons *constraints.Value) (*mgo.Query, error) { // Select all machines that can accept principal units and are clean. var containerRefs []machineContainers // If we need empty machines, first build up a list of machine ids which have containers // so we can exclude those. if requireEmpty { err := u.st.containerRefs.Find(bson.D{hasContainerTerm}).All(&containerRefs) if err != nil { return nil, err } } var machinesWithContainers = make([]string, len(containerRefs)) for i, cref := range containerRefs { machinesWithContainers[i] = cref.Id } terms := bson.D{ {"life", Alive}, {"series", u.doc.Series}, {"jobs", []MachineJob{JobHostUnits}}, {"clean", true}, {"_id", bson.D{{"$nin", machinesWithContainers}}}, } // Add the container filter term if necessary. var containerType instance.ContainerType if cons.Container != nil { containerType = *cons.Container } if containerType == instance.NONE { terms = append(terms, bson.DocElem{"containertype", ""}) } else if containerType != "" { terms = append(terms, bson.DocElem{"containertype", string(containerType)}) } // Find the ids of machines which satisfy any required hardware // constraints. If there is no instanceData for a machine, that // machine is not considered as suitable for deploying the unit. // This can happen if the machine is not yet provisioned. It may // be that when the machine is provisioned it will be found to // be suitable, but we don't know that right now and it's best // to err on the side of caution and exclude such machines. var suitableInstanceData []instanceData var suitableTerms bson.D if cons.Arch != nil && *cons.Arch != "" { suitableTerms = append(suitableTerms, bson.DocElem{"arch", *cons.Arch}) } if cons.Mem != nil && *cons.Mem > 0 { suitableTerms = append(suitableTerms, bson.DocElem{"mem", bson.D{{"$gte", *cons.Mem}}}) } if cons.RootDisk != nil && *cons.RootDisk > 0 { suitableTerms = append(suitableTerms, bson.DocElem{"rootdisk", bson.D{{"$gte", *cons.RootDisk}}}) } if cons.CpuCores != nil && *cons.CpuCores > 0 { suitableTerms = append(suitableTerms, bson.DocElem{"cpucores", bson.D{{"$gte", *cons.CpuCores}}}) } if cons.CpuPower != nil && *cons.CpuPower > 0 { suitableTerms = append(suitableTerms, bson.DocElem{"cpupower", bson.D{{"$gte", *cons.CpuPower}}}) } if cons.Tags != nil && len(*cons.Tags) > 0 { suitableTerms = append(suitableTerms, bson.DocElem{"tags", bson.D{{"$all", *cons.Tags}}}) } if len(suitableTerms) > 0 { err := u.st.instanceData.Find(suitableTerms).Select(bson.M{"_id": 1}).All(&suitableInstanceData) if err != nil { return nil, err } var suitableIds = make([]string, len(suitableInstanceData)) for i, m := range suitableInstanceData { suitableIds[i] = m.Id } terms = append(terms, bson.DocElem{"_id", bson.D{{"$in", suitableIds}}}) } return u.st.machines.Find(terms), nil } // assignToCleanMaybeEmptyMachine implements AssignToCleanMachine and AssignToCleanEmptyMachine. // A 'machine' may be a machine instance or container depending on the service constraints. func (u *Unit) assignToCleanMaybeEmptyMachine(requireEmpty bool) (m *Machine, err error) { context := "clean" if requireEmpty { context += ", empty" } context += " machine" if u.doc.Principal != "" { err = fmt.Errorf("unit is a subordinate") assignContextf(&err, u, context) return nil, err } // Get the unit constraints to see what deployment requirements we have to adhere to. cons, err := u.constraints() if err != nil { assignContextf(&err, u, context) return nil, err } query, err := u.findCleanMachineQuery(requireEmpty, cons) if err != nil { assignContextf(&err, u, context) return nil, err } // TODO(rog) Fix so this is more efficient when there are concurrent uses. // Possible solution: pick the highest and the smallest id of all // unused machines, and try to assign to the first one >= a random id in the // middle. iter := query.Batch(1).Prefetch(0).Iter() var mdoc machineDoc for iter.Next(&mdoc) { m := newMachine(u.st, &mdoc) err := u.assignToMachine(m, true) if err == nil { return m, nil } if err != inUseErr && err != machineNotAliveErr { assignContextf(&err, u, context) return nil, err } } if err := iter.Err(); err != nil { assignContextf(&err, u, context) return nil, err } return nil, noCleanMachines } // UnassignFromMachine removes the assignment between this unit and the // machine it's assigned to. func (u *Unit) UnassignFromMachine() (err error) { // TODO check local machine id and add an assert that the // machine id is as expected. ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: txn.DocExists, Update: bson.D{{"$set", bson.D{{"machineid", ""}}}}, }} if u.doc.MachineId != "" { ops = append(ops, txn.Op{ C: u.st.machines.Name, Id: u.doc.MachineId, Assert: txn.DocExists, Update: bson.D{{"$pull", bson.D{{"principals", u.doc.Name}}}}, }) } err = u.st.runTransaction(ops) if err != nil { return fmt.Errorf("cannot unassign unit %q from machine: %v", u, onAbort(err, errors.NotFoundf("machine"))) } u.doc.MachineId = "" return nil } // SetPublicAddress sets the public address of the unit. func (u *Unit) SetPublicAddress(address string) (err error) { ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: txn.DocExists, Update: bson.D{{"$set", bson.D{{"publicaddress", address}}}}, }} if err := u.st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set public address of unit %q: %v", u, onAbort(err, errors.NotFoundf("unit"))) } u.doc.PublicAddress = address return nil } // SetPrivateAddress sets the private address of the unit. func (u *Unit) SetPrivateAddress(address string) error { ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: notDeadDoc, Update: bson.D{{"$set", bson.D{{"privateaddress", address}}}}, }} err := u.st.runTransaction(ops) if err != nil { return fmt.Errorf("cannot set private address of unit %q: %v", u, onAbort(err, errors.NotFoundf("unit"))) } u.doc.PrivateAddress = address return nil } // Resolve marks the unit as having had any previous state transition // problems resolved, and informs the unit that it may attempt to // reestablish normal workflow. The retryHooks parameter informs // whether to attempt to reexecute previous failed hooks or to continue // as if they had succeeded before. func (u *Unit) Resolve(retryHooks bool) error { status, _, _, err := u.Status() if err != nil { return err } if status != params.StatusError { return fmt.Errorf("unit %q is not in an error state", u) } mode := ResolvedNoHooks if retryHooks { mode = ResolvedRetryHooks } return u.SetResolved(mode) } // SetResolved marks the unit as having had any previous state transition // problems resolved, and informs the unit that it may attempt to // reestablish normal workflow. The resolved mode parameter informs // whether to attempt to reexecute previous failed hooks or to continue // as if they had succeeded before. func (u *Unit) SetResolved(mode ResolvedMode) (err error) { defer utils.ErrorContextf(&err, "cannot set resolved mode for unit %q", u) switch mode { case ResolvedRetryHooks, ResolvedNoHooks: default: return fmt.Errorf("invalid error resolution mode: %q", mode) } // TODO(fwereade): assert unit has error status. resolvedNotSet := bson.D{{"resolved", ResolvedNone}} ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: append(notDeadDoc, resolvedNotSet...), Update: bson.D{{"$set", bson.D{{"resolved", mode}}}}, }} if err := u.st.runTransaction(ops); err == nil { u.doc.Resolved = mode return nil } else if err != txn.ErrAborted { return err } if ok, err := isNotDead(u.st.units, u.doc.Name); err != nil { return err } else if !ok { return errDead } // For now, the only remaining assert is that resolved was unset. return fmt.Errorf("already resolved") } // ClearResolved removes any resolved setting on the unit. func (u *Unit) ClearResolved() error { ops := []txn.Op{{ C: u.st.units.Name, Id: u.doc.Name, Assert: txn.DocExists, Update: bson.D{{"$set", bson.D{{"resolved", ResolvedNone}}}}, }} err := u.st.runTransaction(ops) if err != nil { return fmt.Errorf("cannot clear resolved mode for unit %q: %v", u, errors.NotFoundf("unit")) } u.doc.Resolved = ResolvedNone return nil } ���������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/megawatcher.go�����������������������������������0000644�0000153�0000161�00000037724�12321735642�024640� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "reflect" "strings" "labix.org/v2/mgo" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/multiwatcher" "launchpad.net/juju-core/state/watcher" ) // allWatcherStateBacking implements allWatcherBacking by // fetching entities from the State. type allWatcherStateBacking struct { st *State // collections collectionByName map[string]allWatcherStateCollection } type backingMachine machineDoc func (m *backingMachine) updated(st *State, store *multiwatcher.Store, id interface{}) error { info := &params.MachineInfo{ Id: m.Id, Life: params.Life(m.Life.String()), Series: m.Series, Jobs: paramsJobsFromJobs(m.Jobs), Addresses: mergedAddresses(m.MachineAddresses, m.Addresses), SupportedContainers: m.SupportedContainers, SupportedContainersKnown: m.SupportedContainersKnown, } oldInfo := store.Get(info.EntityId()) if oldInfo == nil { // We're adding the entry for the first time, // so fetch the associated machine status. sdoc, err := getStatus(st, machineGlobalKey(m.Id)) if err != nil { return err } info.Status = sdoc.Status info.StatusInfo = sdoc.StatusInfo } else { // The entry already exists, so preserve the current status and // instance data. oldInfo := oldInfo.(*params.MachineInfo) info.Status = oldInfo.Status info.StatusInfo = oldInfo.StatusInfo info.InstanceId = oldInfo.InstanceId info.HardwareCharacteristics = oldInfo.HardwareCharacteristics } // If the machine is been provisioned, fetch the instance id as required, // and set instance id and hardware characteristics. if m.Nonce != "" && info.InstanceId == "" { instanceData, err := getInstanceData(st, m.Id) if err == nil { info.InstanceId = string(instanceData.InstanceId) info.HardwareCharacteristics = hardwareCharacteristics(instanceData) } else if !errors.IsNotFoundError(err) { return err } } store.Update(info) return nil } func (svc *backingMachine) removed(st *State, store *multiwatcher.Store, id interface{}) error { store.Remove(params.EntityId{ Kind: "machine", Id: id, }) return nil } func (m *backingMachine) mongoId() interface{} { return m.Id } type backingUnit unitDoc func (u *backingUnit) updated(st *State, store *multiwatcher.Store, id interface{}) error { info := &params.UnitInfo{ Name: u.Name, Service: u.Service, Series: u.Series, MachineId: u.MachineId, Ports: u.Ports, } if u.CharmURL != nil { info.CharmURL = u.CharmURL.String() } oldInfo := store.Get(info.EntityId()) if oldInfo == nil { // We're adding the entry for the first time, // so fetch the associated unit status. sdoc, err := getStatus(st, unitGlobalKey(u.Name)) if err != nil { return err } info.Status = sdoc.Status info.StatusInfo = sdoc.StatusInfo } else { // The entry already exists, so preserve the current status. oldInfo := oldInfo.(*params.UnitInfo) info.Status = oldInfo.Status info.StatusInfo = oldInfo.StatusInfo } publicAddress, privateAddress, err := getUnitAddresses(st, u.Name) if err != nil { return err } info.PublicAddress = publicAddress info.PrivateAddress = privateAddress store.Update(info) return nil } // getUnitAddresses returns the public and private addresses on a given unit. // As of 1.18, the addresses are stored on the assigned machine but we retain // this approach for backwards compatibility. func getUnitAddresses(st *State, unitName string) (publicAddress, privateAddress string, err error) { u, err := st.Unit(unitName) if err != nil { return "", "", err } publicAddress, _ = u.PublicAddress() privateAddress, _ = u.PrivateAddress() return publicAddress, privateAddress, nil } func (svc *backingUnit) removed(st *State, store *multiwatcher.Store, id interface{}) error { store.Remove(params.EntityId{ Kind: "unit", Id: id, }) return nil } func (m *backingUnit) mongoId() interface{} { return m.Name } type backingService serviceDoc func (svc *backingService) updated(st *State, store *multiwatcher.Store, id interface{}) error { info := &params.ServiceInfo{ Name: svc.Name, Exposed: svc.Exposed, CharmURL: svc.CharmURL.String(), OwnerTag: svc.fixOwnerTag(), Life: params.Life(svc.Life.String()), MinUnits: svc.MinUnits, } oldInfo := store.Get(info.EntityId()) needConfig := false if oldInfo == nil { // We're adding the entry for the first time, // so fetch the associated child documents. c, err := readConstraints(st, serviceGlobalKey(svc.Name)) if err != nil { return err } info.Constraints = c needConfig = true } else { // The entry already exists, so preserve the current status. oldInfo := oldInfo.(*params.ServiceInfo) info.Constraints = oldInfo.Constraints if info.CharmURL == oldInfo.CharmURL { // The charm URL remains the same - we can continue to // use the same config settings. info.Config = oldInfo.Config } else { // The charm URL has changed - we need to fetch the // settings from the new charm's settings doc. needConfig = true } } if needConfig { var err error info.Config, _, err = readSettingsDoc(st, serviceSettingsKey(svc.Name, svc.CharmURL)) if err != nil { return err } } store.Update(info) return nil } func (svc *backingService) removed(st *State, store *multiwatcher.Store, id interface{}) error { store.Remove(params.EntityId{ Kind: "service", Id: id, }) return nil } // SCHEMACHANGE // TODO(mattyw) remove when schema upgrades are possible func (svc *backingService) fixOwnerTag() string { if svc.OwnerTag != "" { return svc.OwnerTag } return "user-admin" } func (m *backingService) mongoId() interface{} { return m.Name } type backingRelation relationDoc func (r *backingRelation) updated(st *State, store *multiwatcher.Store, id interface{}) error { eps := make([]params.Endpoint, len(r.Endpoints)) for i, ep := range r.Endpoints { eps[i] = params.Endpoint{ ServiceName: ep.ServiceName, Relation: ep.Relation, } } info := &params.RelationInfo{ Key: r.Key, Id: r.Id, Endpoints: eps, } store.Update(info) return nil } func (svc *backingRelation) removed(st *State, store *multiwatcher.Store, id interface{}) error { store.Remove(params.EntityId{ Kind: "relation", Id: id, }) return nil } func (m *backingRelation) mongoId() interface{} { return m.Key } type backingAnnotation annotatorDoc func (a *backingAnnotation) updated(st *State, store *multiwatcher.Store, id interface{}) error { info := &params.AnnotationInfo{ Tag: a.Tag, Annotations: a.Annotations, } store.Update(info) return nil } func (svc *backingAnnotation) removed(st *State, store *multiwatcher.Store, id interface{}) error { tag, ok := tagForGlobalKey(id.(string)) if !ok { panic(fmt.Errorf("unknown global key %q in state", id)) } store.Remove(params.EntityId{ Kind: "annotation", Id: tag, }) return nil } func (a *backingAnnotation) mongoId() interface{} { return a.GlobalKey } type backingStatus statusDoc func (s *backingStatus) updated(st *State, store *multiwatcher.Store, id interface{}) error { parentId, ok := backingEntityIdForGlobalKey(id.(string)) if !ok { return nil } info0 := store.Get(parentId) switch info := info0.(type) { case nil: // The parent info doesn't exist. Ignore the status until it does. return nil case *params.UnitInfo: newInfo := *info newInfo.Status = s.Status newInfo.StatusInfo = s.StatusInfo newInfo.StatusData = s.StatusData info0 = &newInfo case *params.MachineInfo: newInfo := *info newInfo.Status = s.Status newInfo.StatusInfo = s.StatusInfo newInfo.StatusData = s.StatusData info0 = &newInfo default: panic(fmt.Errorf("status for unexpected entity with id %q; type %T", id, info)) } store.Update(info0) return nil } func (s *backingStatus) removed(st *State, store *multiwatcher.Store, id interface{}) error { // If the status is removed, the parent will follow not long after, // so do nothing. return nil } func (a *backingStatus) mongoId() interface{} { panic("cannot find mongo id from status document") } type backingConstraints constraintsDoc func (s *backingConstraints) updated(st *State, store *multiwatcher.Store, id interface{}) error { parentId, ok := backingEntityIdForGlobalKey(id.(string)) if !ok { return nil } info0 := store.Get(parentId) switch info := info0.(type) { case nil: // The parent info doesn't exist. Ignore the status until it does. return nil case *params.UnitInfo, *params.MachineInfo: // We don't (yet) publish unit or machine constraints. return nil case *params.ServiceInfo: newInfo := *info newInfo.Constraints = constraintsDoc(*s).value() info0 = &newInfo default: panic(fmt.Errorf("status for unexpected entity with id %q; type %T", id, info)) } store.Update(info0) return nil } func (s *backingConstraints) removed(st *State, store *multiwatcher.Store, id interface{}) error { return nil } func (a *backingConstraints) mongoId() interface{} { panic("cannot find mongo id from constraints document") } type backingSettings map[string]interface{} func (s *backingSettings) updated(st *State, store *multiwatcher.Store, id interface{}) error { parentId, url, ok := backingEntityIdForSettingsKey(id.(string)) if !ok { return nil } info0 := store.Get(parentId) switch info := info0.(type) { case nil: // The parent info doesn't exist. Ignore the status until it does. return nil case *params.ServiceInfo: // If we're seeing settings for the service with a different // charm URL, we ignore them - we will fetch // them again when the service charm changes. // By doing this we make sure that the settings in the // ServiceInfo are always consistent with the charm URL. if info.CharmURL != url { break } newInfo := *info cleanSettingsMap(*s) newInfo.Config = *s info0 = &newInfo default: return nil } store.Update(info0) return nil } func (s *backingSettings) removed(st *State, store *multiwatcher.Store, id interface{}) error { return nil } func (a *backingSettings) mongoId() interface{} { panic("cannot find mongo id from settings document") } // backingEntityIdForSettingsKey returns the entity id for the given // settings key. Any extra information in the key is returned in // extra. func backingEntityIdForSettingsKey(key string) (eid params.EntityId, extra string, ok bool) { if !strings.HasPrefix(key, "s#") { eid, ok = backingEntityIdForGlobalKey(key) return } key = key[2:] i := strings.Index(key, "#") if i == -1 { return params.EntityId{}, "", false } eid = (&params.ServiceInfo{Name: key[0:i]}).EntityId() extra = key[i+1:] ok = true return } // backingEntityIdForGlobalKey returns the entity id for the given global key. // It returns false if the key is not recognized. func backingEntityIdForGlobalKey(key string) (params.EntityId, bool) { if len(key) < 3 || key[1] != '#' { return params.EntityId{}, false } id := key[2:] switch key[0] { case 'm': return (&params.MachineInfo{Id: id}).EntityId(), true case 'u': return (&params.UnitInfo{Name: id}).EntityId(), true case 's': return (&params.ServiceInfo{Name: id}).EntityId(), true } return params.EntityId{}, false } // backingEntityDoc is implemented by the documents in // collections that the allWatcherStateBacking watches. type backingEntityDoc interface { // updated is called when the document has changed. // The mongo _id value of the document is provided in id. updated(st *State, store *multiwatcher.Store, id interface{}) error // removed is called when the document has changed. // The receiving instance will not contain any data. // The mongo _id value of the document is provided in id. removed(st *State, store *multiwatcher.Store, id interface{}) error // mongoId returns the mongo _id field of the document. // It is currently never called for subsidiary documents. mongoId() interface{} } var ( _ backingEntityDoc = (*backingMachine)(nil) _ backingEntityDoc = (*backingUnit)(nil) _ backingEntityDoc = (*backingService)(nil) _ backingEntityDoc = (*backingRelation)(nil) _ backingEntityDoc = (*backingAnnotation)(nil) _ backingEntityDoc = (*backingStatus)(nil) _ backingEntityDoc = (*backingConstraints)(nil) _ backingEntityDoc = (*backingSettings)(nil) ) // allWatcherStateCollection holds information about a // collection watched by an allWatcher and the // type of value we use to store entity information // for that collection. type allWatcherStateCollection struct { *mgo.Collection // infoType stores the type of the info type // that we use for this collection. infoType reflect.Type // subsidiary is true if the collection is used only // to modify a primary entity. subsidiary bool } func newAllWatcherStateBacking(st *State) multiwatcher.Backing { collectionByType := make(map[reflect.Type]allWatcherStateCollection) b := &allWatcherStateBacking{ st: st, collectionByName: make(map[string]allWatcherStateCollection), } collections := []allWatcherStateCollection{{ Collection: st.machines, infoType: reflect.TypeOf(backingMachine{}), }, { Collection: st.units, infoType: reflect.TypeOf(backingUnit{}), }, { Collection: st.services, infoType: reflect.TypeOf(backingService{}), }, { Collection: st.relations, infoType: reflect.TypeOf(backingRelation{}), }, { Collection: st.annotations, infoType: reflect.TypeOf(backingAnnotation{}), }, { Collection: st.statuses, infoType: reflect.TypeOf(backingStatus{}), subsidiary: true, }, { Collection: st.constraints, infoType: reflect.TypeOf(backingConstraints{}), subsidiary: true, }, { Collection: st.settings, infoType: reflect.TypeOf(backingSettings{}), subsidiary: true, }} // Populate the collection maps from the above set of collections. for _, c := range collections { docType := c.infoType if _, ok := collectionByType[docType]; ok { panic(fmt.Errorf("duplicate collection type %s", docType)) } collectionByType[docType] = c if _, ok := b.collectionByName[c.Name]; ok { panic(fmt.Errorf("duplicate collection name %q", c.Name)) } b.collectionByName[c.Name] = c } return b } // Watch watches all the collections. func (b *allWatcherStateBacking) Watch(in chan<- watcher.Change) { for _, c := range b.collectionByName { b.st.watcher.WatchCollection(c.Name, in) } } // Unwatch unwatches all the collections. func (b *allWatcherStateBacking) Unwatch(in chan<- watcher.Change) { for _, c := range b.collectionByName { b.st.watcher.UnwatchCollection(c.Name, in) } } // GetAll fetches all items that we want to watch from the state. func (b *allWatcherStateBacking) GetAll(all *multiwatcher.Store) error { // TODO(rog) fetch collections concurrently? for _, c := range b.collectionByName { if c.subsidiary { continue } infoSlicePtr := reflect.New(reflect.SliceOf(c.infoType)) if err := c.Find(nil).All(infoSlicePtr.Interface()); err != nil { return fmt.Errorf("cannot get all %s: %v", c.Name, err) } infos := infoSlicePtr.Elem() for i := 0; i < infos.Len(); i++ { info := infos.Index(i).Addr().Interface().(backingEntityDoc) info.updated(b.st, all, info.mongoId()) } } return nil } // Changed updates the allWatcher's idea of the current state // in response to the given change. func (b *allWatcherStateBacking) Changed(all *multiwatcher.Store, change watcher.Change) error { c, ok := b.collectionByName[change.C] if !ok { panic(fmt.Errorf("unknown collection %q in fetch request", change.C)) } doc := reflect.New(c.infoType).Interface().(backingEntityDoc) // TODO(rog) investigate ways that this can be made more efficient // than simply fetching each entity in turn. // TODO(rog) avoid fetching documents that we have no interest // in, such as settings changes to entities we don't care about. err := c.FindId(change.Id).One(doc) if err == mgo.ErrNotFound { return doc.removed(b.st, all, change.Id) } if err != nil { return err } return doc.updated(b.st, all, change.Id) } ��������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/cleanup.go���������������������������������������0000644�0000153�0000161�00000017505�12321735776�024003� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package state import ( "fmt" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/errors" ) // cleanupDoc represents a potentially large set of documents that should be // removed. type cleanupDoc struct { Id bson.ObjectId `bson:"_id"` Kind string Prefix string } // newCleanupOp returns a txn.Op that creates a cleanup document with a unique // id and the supplied kind and prefix. func (st *State) newCleanupOp(kind, prefix string) txn.Op { doc := &cleanupDoc{ Id: bson.NewObjectId(), Kind: kind, Prefix: prefix, } return txn.Op{ C: st.cleanups.Name, Id: doc.Id, Insert: doc, } } // NeedsCleanup returns true if documents previously marked for removal exist. func (st *State) NeedsCleanup() (bool, error) { count, err := st.cleanups.Count() if err != nil { return false, err } return count > 0, nil } // Cleanup removes all documents that were previously marked for removal, if // any such exist. It should be called periodically by at least one element // of the system. func (st *State) Cleanup() error { doc := cleanupDoc{} iter := st.cleanups.Find(nil).Iter() for iter.Next(&doc) { var err error logger.Debugf("running %q cleanup: %q", doc.Kind, doc.Prefix) switch doc.Kind { case "settings": err = st.cleanupSettings(doc.Prefix) case "units": err = st.cleanupUnits(doc.Prefix) case "services": err = st.cleanupServices() case "machine": err = st.cleanupMachine(doc.Prefix) default: err = fmt.Errorf("unknown cleanup kind %q", doc.Kind) } if err != nil { logger.Warningf("cleanup failed: %v", err) continue } ops := []txn.Op{{ C: st.cleanups.Name, Id: doc.Id, Remove: true, }} if err := st.runTransaction(ops); err != nil { logger.Warningf("cannot remove empty cleanup document: %v", err) } } if err := iter.Err(); err != nil { return fmt.Errorf("cannot read cleanup document: %v", err) } return nil } func (st *State) cleanupSettings(prefix string) error { // Documents marked for cleanup are not otherwise referenced in the // system, and will not be under watch, and are therefore safe to // delete directly. sel := bson.D{{"_id", bson.D{{"$regex", "^" + prefix}}}} if count, err := st.settings.Find(sel).Count(); err != nil { return fmt.Errorf("cannot detect cleanup targets: %v", err) } else if count != 0 { if _, err := st.settings.RemoveAll(sel); err != nil { return fmt.Errorf("cannot remove documents marked for cleanup: %v", err) } } return nil } // cleanupServices sets all services to Dying, if they are not already Dying // or Dead. It's expected to be used when an environment is destroyed. func (st *State) cleanupServices() error { // This won't miss services, because a Dying environment cannot have // services added to it. But we do have to remove the services themselves // via individual transactions, because they could be in any state at all. service := &Service{st: st} sel := bson.D{{"life", Alive}} iter := st.services.Find(sel).Iter() for iter.Next(&service.doc) { if err := service.Destroy(); err != nil { return err } } if err := iter.Err(); err != nil { return fmt.Errorf("cannot read service document: %v", err) } return nil } // cleanupUnits sets all units with the given prefix to Dying, if they are not // already Dying or Dead. It's expected to be used when a service is destroyed. func (st *State) cleanupUnits(prefix string) error { // This won't miss units, because a Dying service cannot have units added // to it. But we do have to remove the units themselves via individual // transactions, because they could be in any state at all. unit := &Unit{st: st} sel := bson.D{{"_id", bson.D{{"$regex", "^" + prefix}}}, {"life", Alive}} iter := st.units.Find(sel).Iter() for iter.Next(&unit.doc) { if err := unit.Destroy(); err != nil { return err } } if err := iter.Err(); err != nil { return fmt.Errorf("cannot read unit document: %v", err) } return nil } // cleanupMachine systematically destroys and removes all entities that // depend upon the supplied machine, and removes the machine from state. It's // expected to be used in response to destroy-machine --force. func (st *State) cleanupMachine(machineId string) error { machine, err := st.Machine(machineId) if errors.IsNotFoundError(err) { return nil } else if err != nil { return err } // In an ideal world, we'd call machine.Destroy() here, and thus prevent // new dependencies being added while we clean up the ones we know about. // But machine destruction is unsophisticated, and doesn't allow for // destruction while dependencies exist; so we just have to deal with that // possibility below. if err := st.cleanupContainers(machine); err != nil { return err } if err := st.cleanupNetworks(machineGlobalKey(machineId)); err != nil { return err } for _, unitName := range machine.doc.Principals { if err := st.obliterateUnit(unitName); err != nil { return err } } // We need to refresh the machine at this point, because the local copy // of the document will not reflect changes caused by the unit cleanups // above, and may thus fail immediately. if err := machine.Refresh(); errors.IsNotFoundError(err) { return nil } else if err != nil { return err } // TODO(fwereade): 2013-11-11 bug 1250104 // If this fails, it's *probably* due to a race in which new dependencies // were added while we cleaned up the old ones. If the cleanup doesn't run // again -- which it *probably* will anyway -- the issue can be resolved by // force-destroying the machine again; that's better than adding layer // upon layer of complication here. return machine.EnsureDead() // Note that we do *not* remove the machine entirely: we leave it for the // provisioner to clean up, so that we don't end up with an unreferenced // instance that would otherwise be ignored when in provisioner-safe-mode. } // cleanupContainers recursively calls cleanupMachine on the supplied // machine's containers, and removes them from state entirely. func (st *State) cleanupContainers(machine *Machine) error { containerIds, err := machine.Containers() if errors.IsNotFoundError(err) { return nil } else if err != nil { return err } for _, containerId := range containerIds { if err := st.cleanupMachine(containerId); err != nil { return err } container, err := st.Machine(containerId) if errors.IsNotFoundError(err) { return nil } else if err != nil { return err } if err := container.Remove(); err != nil { return err } } return nil } // cleanupNetworks removes associated networks for a machine or // service, given by its global key. func (st *State) cleanupNetworks(globalKey string) error { op := removeNetworksOp(st, globalKey) if err := st.runTransaction([]txn.Op{op}); err != nil { logger.Warningf("cannot remove networks document for %q: %v", globalKey, err) } return nil } // obliterateUnit removes a unit from state completely. It is not safe or // sane to obliterate any unit in isolation; its only reasonable use is in // the context of machine obliteration, in which we can be sure that unclean // shutdown of units is not going to leave a machine in a difficult state. func (st *State) obliterateUnit(unitName string) error { unit, err := st.Unit(unitName) if errors.IsNotFoundError(err) { return nil } else if err != nil { return err } // Unlike the machine, we *can* always destroy the unit, and (at least) // prevent further dependencies being added. If we're really lucky, the // unit will be removed immediately. if err := unit.Destroy(); err != nil { return err } if err := unit.Refresh(); errors.IsNotFoundError(err) { return nil } else if err != nil { return err } for _, subName := range unit.SubordinateNames() { if err := st.obliterateUnit(subName); err != nil { return err } } if err := unit.EnsureDead(); err != nil { return err } return unit.Remove() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/testing/�����������������������������������������0000755�0000153�0000161�00000000000�12321735643�023463� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/testing/watcher.go�������������������������������0000644�0000153�0000161�00000015433�12321735642�025454� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "sort" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" ) type Stopper interface { Stop() error } func AssertStop(c *gc.C, stopper Stopper) { c.Assert(stopper.Stop(), gc.IsNil) } // AssertCanStopWhenSending ensures even when there are changes // pending to be delivered by the watcher it can still stop // cleanly. This is necessary to check for deadlocks in case the // watcher's inner loop is blocked trying to send and its tomb is // already dying. func AssertCanStopWhenSending(c *gc.C, stopper Stopper) { // Leave some time for the event to be delivered and the watcher // to block on sending it. <-time.After(testing.ShortWait) stopped := make(chan bool) // Stop() blocks, so we need to call it in a separate goroutine. go func() { c.Check(stopper.Stop(), gc.IsNil) stopped <- true }() select { case <-time.After(testing.LongWait): // NOTE: If this test fails here it means we have a deadlock // in the client-side watcher implementation. c.Fatalf("watcher did not stop as expected") case <-stopped: } } type NotifyWatcher interface { Changes() <-chan struct{} } // NotifyWatcherC embeds a gocheck.C and adds methods to help verify // the behaviour of any watcher that uses a <-chan struct{}. type NotifyWatcherC struct { *gc.C State *state.State Watcher NotifyWatcher } // NewNotifyWatcherC returns a NotifyWatcherC that checks for aggressive // event coalescence. func NewNotifyWatcherC(c *gc.C, st *state.State, w NotifyWatcher) NotifyWatcherC { return NotifyWatcherC{ C: c, State: st, Watcher: w, } } func (c NotifyWatcherC) AssertNoChange() { c.State.StartSync() select { case _, ok := <-c.Watcher.Changes(): c.Fatalf("watcher sent unexpected change: (_, %v)", ok) case <-time.After(testing.ShortWait): } } func (c NotifyWatcherC) AssertOneChange() { c.State.StartSync() select { case _, ok := <-c.Watcher.Changes(): c.Assert(ok, jc.IsTrue) case <-time.After(testing.LongWait): c.Fatalf("watcher did not send change") } c.AssertNoChange() } func (c NotifyWatcherC) AssertClosed() { select { case _, ok := <-c.Watcher.Changes(): c.Assert(ok, jc.IsFalse) default: c.Fatalf("watcher not closed") } } // StringsWatcherC embeds a gocheck.C and adds methods to help verify // the behaviour of any watcher that uses a <-chan []string. type StringsWatcherC struct { *gc.C State *state.State Watcher StringsWatcher } // NewStringsWatcherC returns a StringsWatcherC that checks for aggressive // event coalescence. func NewStringsWatcherC(c *gc.C, st *state.State, w StringsWatcher) StringsWatcherC { return StringsWatcherC{ C: c, State: st, Watcher: w, } } type StringsWatcher interface { Stop() error Changes() <-chan []string } func (c StringsWatcherC) AssertNoChange() { c.State.StartSync() select { case actual, ok := <-c.Watcher.Changes(): c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok) case <-time.After(testing.ShortWait): } } func (c StringsWatcherC) AssertChange(expect ...string) { c.assertChange(false, expect...) } func (c StringsWatcherC) AssertChangeInSingleEvent(expect ...string) { c.assertChange(true, expect...) } // AssertChange asserts the given list of changes was reported by // the watcher, but does not assume there are no following changes. func (c StringsWatcherC) assertChange(single bool, expect ...string) { c.State.StartSync() timeout := time.After(testing.LongWait) var actual []string loop: for { select { case changes, ok := <-c.Watcher.Changes(): c.Assert(ok, jc.IsTrue) actual = append(actual, changes...) if single || len(actual) >= len(expect) { break loop } case <-timeout: c.Fatalf("watcher did not send change") } } if len(expect) == 0 { c.Assert(actual, gc.HasLen, 0) } else { sort.Strings(expect) sort.Strings(actual) c.Assert(actual, gc.DeepEquals, expect) } } func (c StringsWatcherC) AssertClosed() { select { case _, ok := <-c.Watcher.Changes(): c.Assert(ok, jc.IsFalse) default: c.Fatalf("watcher not closed") } } // RelationUnitsWatcherC embeds a gocheck.C and adds methods to help // verify the behaviour of any watcher that uses a <-chan // params.RelationUnitsChange. type RelationUnitsWatcherC struct { *gc.C State *state.State Watcher RelationUnitsWatcher // settingsVersions keeps track of the settings version of each // changed unit since the last received changes to ensure version // always increases. settingsVersions map[string]int64 } // NewRelationUnitsWatcherC returns a RelationUnitsWatcherC that // checks for aggressive event coalescence. func NewRelationUnitsWatcherC(c *gc.C, st *state.State, w RelationUnitsWatcher) RelationUnitsWatcherC { return RelationUnitsWatcherC{ C: c, State: st, Watcher: w, settingsVersions: make(map[string]int64), } } type RelationUnitsWatcher interface { Stop() error Changes() <-chan params.RelationUnitsChange } func (c RelationUnitsWatcherC) AssertNoChange() { c.State.StartSync() select { case actual, ok := <-c.Watcher.Changes(): c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok) case <-time.After(testing.ShortWait): } } // AssertChange asserts the given changes was reported by the watcher, // but does not assume there are no following changes. func (c RelationUnitsWatcherC) AssertChange(changed []string, departed []string) { // Get all items in changed in a map for easy lookup. changedNames := make(map[string]bool) for _, name := range changed { changedNames[name] = true } c.State.StartSync() timeout := time.After(testing.LongWait) select { case actual, ok := <-c.Watcher.Changes(): c.Assert(ok, jc.IsTrue) c.Assert(actual.Changed, gc.HasLen, len(changed)) // Because the versions can change, we only need to make sure // the keys match, not the contents (UnitSettings == txnRevno). for k, settings := range actual.Changed { _, ok := changedNames[k] c.Assert(ok, jc.IsTrue) oldVer, ok := c.settingsVersions[k] if !ok { // This is the first time we see this unit, so // save the settings version for later. c.settingsVersions[k] = settings.Version } else { // Already seen; make sure the version increased. if settings.Version <= oldVer { c.Fatalf("expected unit settings version > %d (got %d)", oldVer, settings.Version) } } } c.Assert(actual.Departed, jc.SameContents, departed) case <-timeout: c.Fatalf("watcher did not send change") } } func (c RelationUnitsWatcherC) AssertClosed() { select { case _, ok := <-c.Watcher.Changes(): c.Assert(ok, jc.IsFalse) default: c.Fatalf("watcher not closed") } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/testing/agent.go���������������������������������0000644�0000153�0000161�00000001131�12321735642�025103� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "launchpad.net/juju-core/state" "launchpad.net/juju-core/version" ) // 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) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/���������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022543� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/charmrevisionupdater/������������������������0000755�0000153�0000161�00000000000�12321735642�027014� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/charmrevisionupdater/updater.go��������������0000644�0000153�0000161�00000001552�12321735642�031012� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charmrevisionupdater import ( "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/params" ) // State provides access to a worker's view of the state. type State struct { caller base.Caller } // NewState returns a version of the state that provides functionality required by the worker. func NewState(caller base.Caller) *State { return &State{caller} } // UpdateLatestRevisions retrieves charm revision info from a repository // and updates the revision info in state. func (st *State) UpdateLatestRevisions() error { result := new(params.ErrorResult) err := st.caller.Call("CharmRevisionUpdater", "", "UpdateLatestRevisions", nil, result) if err != nil { return err } if result.Error != nil { return result.Error } return nil } ������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/charmrevisionupdater/updater_test.go���������0000644�0000153�0000161�00000002642�12321735642�032052� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charmrevisionupdater_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/charmrevisionupdater" "launchpad.net/juju-core/state/apiserver/charmrevisionupdater/testing" "launchpad.net/juju-core/utils" ) type versionUpdaterSuite struct { testing.CharmSuite updater *charmrevisionupdater.State } var _ = gc.Suite(&versionUpdaterSuite{}) func (s *versionUpdaterSuite) SetUpTest(c *gc.C) { s.CharmSuite.SetUpTest(c) machine, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = machine.SetPassword(password) c.Assert(err, gc.IsNil) err = machine.SetProvisioned("i-manager", "fake_nonce", nil) c.Assert(err, gc.IsNil) st := s.OpenAPIAsMachine(c, machine.Tag(), password, "fake_nonce") c.Assert(st, gc.NotNil) s.updater = charmrevisionupdater.NewState(st) c.Assert(s.updater, gc.NotNil) } func (s *versionUpdaterSuite) TestUpdateRevisions(c *gc.C) { s.SetupScenario(c) err := s.updater.UpdateLatestRevisions() c.Assert(err, gc.IsNil) curl := charm.MustParseURL("cs:quantal/mysql") pending, err := s.State.LatestPlaceholderCharm(curl) c.Assert(err, gc.IsNil) c.Assert(pending.String(), gc.Equals, "cs:quantal/mysql-23") } ����������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/charmrevisionupdater/package_test.go���������0000644�0000153�0000161�00000000404�12321735642�031773� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package charmrevisionupdater_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/��������������������������������������0000755�0000153�0000161�00000000000�12321736000�024051� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/service_test.go�����������������������0000644�0000153�0000161�00000006320�12321735642�027113� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/uniter" statetesting "launchpad.net/juju-core/state/testing" ) type serviceSuite struct { uniterSuite apiService *uniter.Service } var _ = gc.Suite(&serviceSuite{}) func (s *serviceSuite) SetUpTest(c *gc.C) { s.uniterSuite.SetUpTest(c) var err error s.apiService, err = s.uniter.Service(s.wordpressService.Tag()) c.Assert(err, gc.IsNil) } func (s *serviceSuite) TearDownTest(c *gc.C) { s.uniterSuite.TearDownTest(c) } func (s *serviceSuite) TestNameAndString(c *gc.C) { c.Assert(s.apiService.Name(), gc.Equals, s.wordpressService.Name()) c.Assert(s.apiService.String(), gc.Equals, s.wordpressService.String()) } func (s *serviceSuite) TestWatch(c *gc.C) { c.Assert(s.apiService.Life(), gc.Equals, params.Alive) w, err := s.apiService.Watch() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w) // Initial event. wc.AssertOneChange() // Change something and check it's detected. err = s.wordpressService.SetExposed() c.Assert(err, gc.IsNil) wc.AssertOneChange() // Destroy the service and check it's detected. err = s.wordpressService.Destroy() c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *serviceSuite) TestWatchRelations(c *gc.C) { w, err := s.apiService.WatchRelations() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.BackingState, w) // Initial event. wc.AssertChange() wc.AssertNoChange() // Change something other than the lifecycle and make sure it's // not detected. err = s.wordpressService.SetExposed() c.Assert(err, gc.IsNil) wc.AssertNoChange() // Add another service and relate it to wordpress, // check it's detected. s.addMachineServiceCharmAndUnit(c, "mysql") rel := s.addRelation(c, "wordpress", "mysql") wc.AssertChange(rel.String()) // Destroy the relation and check it's detected. err = rel.Destroy() c.Assert(err, gc.IsNil) wc.AssertChange(rel.String()) wc.AssertNoChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *serviceSuite) TestRefresh(c *gc.C) { c.Assert(s.apiService.Life(), gc.Equals, params.Alive) err := s.wordpressService.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.apiService.Life(), gc.Equals, params.Alive) err = s.apiService.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.apiService.Life(), gc.Equals, params.Dying) } func (s *serviceSuite) TestCharmURL(c *gc.C) { // Get the charm URL through state calls. curl, force := s.wordpressService.CharmURL() c.Assert(curl, gc.DeepEquals, s.wordpressCharm.URL()) c.Assert(force, jc.IsFalse) // Now check the same through the API. curl, force, err := s.apiService.CharmURL() c.Assert(err, gc.IsNil) c.Assert(curl, gc.DeepEquals, s.wordpressCharm.URL()) c.Assert(force, jc.IsFalse) } func (s *serviceSuite) TestGetOwnerTag(c *gc.C) { tag, err := s.apiService.GetOwnerTag() c.Assert(err, gc.IsNil) c.Assert(tag, gc.Equals, "user-admin") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/unit_test.go��������������������������0000644�0000153�0000161�00000025123�12321735776�026444� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( "sort" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/uniter" statetesting "launchpad.net/juju-core/state/testing" ) type unitSuite struct { uniterSuite apiUnit *uniter.Unit } var _ = gc.Suite(&unitSuite{}) func (s *unitSuite) SetUpTest(c *gc.C) { s.uniterSuite.SetUpTest(c) var err error s.apiUnit, err = s.uniter.Unit(s.wordpressUnit.Tag()) c.Assert(err, gc.IsNil) } func (s *unitSuite) TearDownTest(c *gc.C) { s.uniterSuite.TearDownTest(c) } func (s *unitSuite) TestUnitAndUnitTag(c *gc.C) { apiUnitFoo, err := s.uniter.Unit("unit-foo-42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(apiUnitFoo, gc.IsNil) c.Assert(s.apiUnit.Tag(), gc.Equals, "unit-wordpress-0") } func (s *unitSuite) TestSetStatus(c *gc.C) { status, info, data, err := s.wordpressUnit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusPending) c.Assert(info, gc.Equals, "") c.Assert(data, gc.HasLen, 0) err = s.apiUnit.SetStatus(params.StatusStarted, "blah", nil) c.Assert(err, gc.IsNil) status, info, data, err = s.wordpressUnit.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStarted) c.Assert(info, gc.Equals, "blah") c.Assert(data, gc.HasLen, 0) } func (s *unitSuite) TestEnsureDead(c *gc.C) { c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) err := s.apiUnit.EnsureDead() c.Assert(err, gc.IsNil) err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) err = s.apiUnit.EnsureDead() c.Assert(err, gc.IsNil) err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) err = s.wordpressUnit.Remove() c.Assert(err, gc.IsNil) err = s.wordpressUnit.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = s.apiUnit.EnsureDead() c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" not found`) c.Assert(err, jc.Satisfies, params.IsCodeNotFound) } func (s *unitSuite) TestDestroy(c *gc.C) { c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) err := s.apiUnit.Destroy() c.Assert(err, gc.IsNil) err = s.wordpressUnit.Refresh() c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" not found`) } func (s *unitSuite) TestDestroyAllSubordinates(c *gc.C) { c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) // Call without subordinates - no change. err := s.apiUnit.DestroyAllSubordinates() c.Assert(err, gc.IsNil) // Add a couple of subordinates and try again. _, _, loggingSub := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) _, _, monitoringSub := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit) c.Assert(loggingSub.Life(), gc.Equals, state.Alive) c.Assert(monitoringSub.Life(), gc.Equals, state.Alive) err = s.apiUnit.DestroyAllSubordinates() c.Assert(err, gc.IsNil) // Verify they got destroyed. err = loggingSub.Refresh() c.Assert(err, gc.IsNil) c.Assert(loggingSub.Life(), gc.Equals, state.Dying) err = monitoringSub.Refresh() c.Assert(err, gc.IsNil) c.Assert(monitoringSub.Life(), gc.Equals, state.Dying) } func (s *unitSuite) TestRefresh(c *gc.C) { c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive) err := s.apiUnit.EnsureDead() c.Assert(err, gc.IsNil) c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive) err = s.apiUnit.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.apiUnit.Life(), gc.Equals, params.Dead) } func (s *unitSuite) TestWatch(c *gc.C) { c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive) w, err := s.apiUnit.Watch() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w) // Initial event. wc.AssertOneChange() // Change something other than the lifecycle and make sure it's // not detected. err = s.apiUnit.SetStatus(params.StatusStarted, "not really", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Make the unit dead and check it's detected. err = s.apiUnit.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *unitSuite) TestResolve(c *gc.C) { err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks) c.Assert(err, gc.IsNil) mode, err := s.apiUnit.Resolved() c.Assert(err, gc.IsNil) c.Assert(mode, gc.Equals, params.ResolvedRetryHooks) err = s.apiUnit.ClearResolved() c.Assert(err, gc.IsNil) mode, err = s.apiUnit.Resolved() c.Assert(err, gc.IsNil) c.Assert(mode, gc.Equals, params.ResolvedNone) } func (s *unitSuite) TestIsPrincipal(c *gc.C) { ok, err := s.apiUnit.IsPrincipal() c.Assert(err, gc.IsNil) c.Assert(ok, jc.IsTrue) } func (s *unitSuite) TestHasSubordinates(c *gc.C) { found, err := s.apiUnit.HasSubordinates() c.Assert(err, gc.IsNil) c.Assert(found, jc.IsFalse) // Add a couple of subordinates and try again. s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit) found, err = s.apiUnit.HasSubordinates() c.Assert(err, gc.IsNil) c.Assert(found, jc.IsTrue) } func (s *unitSuite) TestGetSetPublicAddress(c *gc.C) { address, err := s.apiUnit.PublicAddress() c.Assert(err, gc.ErrorMatches, `"unit-wordpress-0" has no public address set`) err = s.apiUnit.SetPublicAddress("1.2.3.4") c.Assert(err, gc.IsNil) address, err = s.apiUnit.PublicAddress() c.Assert(err, gc.IsNil) c.Assert(address, gc.Equals, "1.2.3.4") } func (s *unitSuite) TestGetSetPrivateAddress(c *gc.C) { address, err := s.apiUnit.PrivateAddress() c.Assert(err, gc.ErrorMatches, `"unit-wordpress-0" has no private address set`) err = s.apiUnit.SetPrivateAddress("1.2.3.4") c.Assert(err, gc.IsNil) address, err = s.apiUnit.PrivateAddress() c.Assert(err, gc.IsNil) c.Assert(address, gc.Equals, "1.2.3.4") } func (s *unitSuite) TestOpenClosePort(c *gc.C) { ports := s.wordpressUnit.OpenedPorts() c.Assert(ports, gc.HasLen, 0) err := s.apiUnit.OpenPort("foo", 1234) c.Assert(err, gc.IsNil) err = s.apiUnit.OpenPort("bar", 4321) c.Assert(err, gc.IsNil) err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) ports = s.wordpressUnit.OpenedPorts() // OpenedPorts returns a sorted slice. c.Assert(ports, gc.DeepEquals, []instance.Port{ {Protocol: "bar", Number: 4321}, {Protocol: "foo", Number: 1234}, }) err = s.apiUnit.ClosePort("bar", 4321) c.Assert(err, gc.IsNil) err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) ports = s.wordpressUnit.OpenedPorts() // OpenedPorts returns a sorted slice. c.Assert(ports, gc.DeepEquals, []instance.Port{ {Protocol: "foo", Number: 1234}, }) err = s.apiUnit.ClosePort("foo", 1234) c.Assert(err, gc.IsNil) err = s.wordpressUnit.Refresh() c.Assert(err, gc.IsNil) ports = s.wordpressUnit.OpenedPorts() c.Assert(ports, gc.HasLen, 0) } func (s *unitSuite) TestGetSetCharmURL(c *gc.C) { // No charm URL set yet. curl, ok := s.wordpressUnit.CharmURL() c.Assert(curl, gc.IsNil) c.Assert(ok, jc.IsFalse) // Now check the same through the API. _, err := s.apiUnit.CharmURL() c.Assert(err, gc.Equals, uniter.ErrNoCharmURLSet) err = s.apiUnit.SetCharmURL(s.wordpressCharm.URL()) c.Assert(err, gc.IsNil) curl, err = s.apiUnit.CharmURL() c.Assert(err, gc.IsNil) c.Assert(curl, gc.NotNil) c.Assert(curl.String(), gc.Equals, s.wordpressCharm.String()) } func (s *unitSuite) TestConfigSettings(c *gc.C) { // Make sure ConfigSettings returns an error when // no charm URL is set, as its state counterpart does. settings, err := s.apiUnit.ConfigSettings() c.Assert(err, gc.ErrorMatches, "unit charm not set") // Now set the charm and try again. err = s.apiUnit.SetCharmURL(s.wordpressCharm.URL()) c.Assert(err, gc.IsNil) settings, err = s.apiUnit.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{ "blog-title": "My Title", }) // Update the config and check we get the changes on the next call. err = s.wordpressService.UpdateConfigSettings(charm.Settings{ "blog-title": "superhero paparazzi", }) c.Assert(err, gc.IsNil) settings, err = s.apiUnit.ConfigSettings() c.Assert(err, gc.IsNil) c.Assert(settings, gc.DeepEquals, charm.Settings{ "blog-title": "superhero paparazzi", }) } func (s *unitSuite) TestWatchConfigSettings(c *gc.C) { // Make sure WatchConfigSettings returns an error when // no charm URL is set, as its state counterpart does. w, err := s.apiUnit.WatchConfigSettings() c.Assert(err, gc.ErrorMatches, "unit charm not set") // Now set the charm and try again. err = s.apiUnit.SetCharmURL(s.wordpressCharm.URL()) c.Assert(err, gc.IsNil) w, err = s.apiUnit.WatchConfigSettings() defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w) // Initial event. wc.AssertOneChange() // Update config a couple of times, check a single event. err = s.wordpressService.UpdateConfigSettings(charm.Settings{ "blog-title": "superhero paparazzi", }) c.Assert(err, gc.IsNil) err = s.wordpressService.UpdateConfigSettings(charm.Settings{ "blog-title": "sauceror central", }) c.Assert(err, gc.IsNil) wc.AssertOneChange() // Non-change is not reported. err = s.wordpressService.UpdateConfigSettings(charm.Settings{ "blog-title": "sauceror central", }) c.Assert(err, gc.IsNil) wc.AssertNoChange() // NOTE: This test is not as exhaustive as the one in state, // because the watcher is already tested there. Here we just // ensure we get the events when we expect them and don't get // them when they're not expected. statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *unitSuite) TestServiceNameAndTag(c *gc.C) { 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()}) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/relation.go���������������������������0000644�0000153�0000161�00000004577�12321735642�026245� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "fmt" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" ) // This module implements a subset of the interface provided by // state.Relation, as needed by the uniter API. // Relation represents a relation between one or two service // endpoints. type Relation struct { st *State tag string id int life params.Life } // String returns the relation as a string. func (r *Relation) String() string { _, relId, err := names.ParseTag(r.tag, names.RelationTagKind) if err != nil { panic(fmt.Sprintf("%q is not a valid relation tag", r.tag)) } return relId } // Id returns the integer internal relation key. This is exposed // because the unit agent needs to expose a value derived from this // (as JUJU_RELATION_ID) to allow relation hooks to differentiate // between relations with different services. func (r *Relation) Id() int { return r.id } // Life returns the relation's current life state. func (r *Relation) Life() params.Life { return r.life } // Refresh refreshes the contents of the relation from the underlying // state. It returns an error that satisfies IsNotFound if the relation has been // removed. func (r *Relation) Refresh() error { result, err := r.st.relation(r.tag, r.st.unitTag) if err != nil { return err } // NOTE: The life cycle information is the only // thing that can change - id, tag and endpoint // information are static. r.life = result.Life return nil } // Endpoint returns the endpoint of the relation for the service the // uniter's managed unit belongs to. func (r *Relation) Endpoint() (*Endpoint, error) { // NOTE: This differs from state.Relation.Endpoint(), because when // talking to the API, there's already an authenticated entity - the // unit, and we can find out its service name. result, err := r.st.relation(r.tag, r.st.unitTag) if err != nil { return nil, err } return &Endpoint{result.Endpoint.Relation}, nil } // Unit returns a RelationUnit for the supplied unit. func (r *Relation) Unit(u *Unit) (*RelationUnit, error) { if u == nil { return nil, fmt.Errorf("unit is nil") } result, err := r.st.relation(r.tag, u.tag) if err != nil { return nil, err } return &RelationUnit{ relation: r, unit: u, endpoint: Endpoint{result.Endpoint.Relation}, st: r.st, }, nil } ���������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/settings_test.go����������������������0000644�0000153�0000161�00000006644�12321735642�027324� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/uniter" ) type settingsSuite struct { uniterSuite commonRelationSuiteMixin } var _ = gc.Suite(&settingsSuite{}) func (s *settingsSuite) SetUpTest(c *gc.C) { s.uniterSuite.SetUpTest(c) s.commonRelationSuiteMixin.SetUpTest(c, s.uniterSuite) } func (s *settingsSuite) TearDownTest(c *gc.C) { s.uniterSuite.TearDownTest(c) } func (s *settingsSuite) TestNewSettingsAndMap(c *gc.C) { // Make sure newSettings accepts nil settings. settings := uniter.NewSettings(s.uniter, "blah", "foo", nil) theMap := settings.Map() c.Assert(theMap, gc.NotNil) c.Assert(theMap, gc.HasLen, 0) // And also accepts a populated map, and returns a converted map. rawSettings := params.RelationSettings{ "some": "settings", "other": "stuff", } settings = uniter.NewSettings(s.uniter, "blah", "foo", rawSettings) theMap = settings.Map() c.Assert(theMap, gc.DeepEquals, rawSettings) } func (s *settingsSuite) TestSet(c *gc.C) { settings := uniter.NewSettings(s.uniter, "blah", "foo", nil) settings.Set("foo", "bar") c.Assert(settings.Map(), gc.DeepEquals, params.RelationSettings{ "foo": "bar", }) settings.Set("foo", "qaz") c.Assert(settings.Map(), gc.DeepEquals, params.RelationSettings{ "foo": "qaz", }) settings.Set("bar", "Cheers") c.Assert(settings.Map(), gc.DeepEquals, params.RelationSettings{ "foo": "qaz", "bar": "Cheers", }) } func (s *settingsSuite) TestDelete(c *gc.C) { settings := uniter.NewSettings(s.uniter, "blah", "foo", nil) settings.Set("foo", "qaz") settings.Set("abc", "tink") settings.Set("bar", "tonk") c.Assert(settings.Map(), gc.DeepEquals, params.RelationSettings{ "foo": "qaz", "abc": "tink", "bar": "tonk", }) settings.Delete("abc") c.Assert(settings.Map(), gc.DeepEquals, params.RelationSettings{ "foo": "qaz", "bar": "tonk", }) settings.Delete("bar") c.Assert(settings.Map(), gc.DeepEquals, params.RelationSettings{ "foo": "qaz", }) settings.Set("abc", "123") c.Assert(settings.Map(), gc.DeepEquals, params.RelationSettings{ "foo": "qaz", "abc": "123", }) settings.Delete("missing") c.Assert(settings.Map(), gc.DeepEquals, params.RelationSettings{ "foo": "qaz", "abc": "123", }) } func (s *settingsSuite) TestWrite(c *gc.C) { wpRelUnit, err := s.stateRelation.Unit(s.wordpressUnit) c.Assert(err, gc.IsNil) rawSettings := map[string]interface{}{ "some": "stuff", "other": "things", } err = wpRelUnit.EnterScope(rawSettings) c.Assert(err, gc.IsNil) s.assertInScope(c, wpRelUnit, true) apiUnit, err := s.uniter.Unit(s.wordpressUnit.Tag()) c.Assert(err, gc.IsNil) apiRelation, err := s.uniter.Relation(s.stateRelation.Tag()) c.Assert(err, gc.IsNil) apiRelUnit, err := apiRelation.Unit(apiUnit) c.Assert(err, gc.IsNil) settings, err := apiRelUnit.Settings() c.Assert(err, gc.IsNil) c.Assert(settings.Map(), gc.DeepEquals, params.RelationSettings{ "some": "stuff", "other": "things", }) settings.Set("some", "bar") settings.Delete("foo") settings.Delete("some") settings.Set("foo", "qaz") settings.Set("other", "days") err = settings.Write() c.Assert(err, gc.IsNil) settings, err = apiRelUnit.Settings() c.Assert(err, gc.IsNil) c.Assert(settings.Map(), gc.DeepEquals, params.RelationSettings{ "foo": "qaz", "other": "days", }) } ��������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/export_test.go������������������������0000644�0000153�0000161�00000000213�12321735642�026767� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter var NewSettings = newSettings �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/relationunit_test.go������������������0000644�0000153�0000161�00000020756�12321735776�030211� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/uniter" statetesting "launchpad.net/juju-core/state/testing" ) // commonRelationSuiteMixin contains fields used by both relationSuite // and relationUnitSuite. We're not just embeddnig relationUnitSuite // into relationSuite to avoid running the former's tests twice. type commonRelationSuiteMixin struct { mysqlMachine *state.Machine mysqlService *state.Service mysqlCharm *state.Charm mysqlUnit *state.Unit stateRelation *state.Relation } type relationUnitSuite struct { uniterSuite commonRelationSuiteMixin } var _ = gc.Suite(&relationUnitSuite{}) func (m *commonRelationSuiteMixin) SetUpTest(c *gc.C, s uniterSuite) { // Create another machine, service and unit, so we can // test relations and relation units. m.mysqlMachine, m.mysqlService, m.mysqlCharm, m.mysqlUnit = s.addMachineServiceCharmAndUnit(c, "mysql") // Add a relation, used by both this suite and relationSuite. m.stateRelation = s.addRelation(c, "wordpress", "mysql") } func (s *relationUnitSuite) SetUpTest(c *gc.C) { s.uniterSuite.SetUpTest(c) s.commonRelationSuiteMixin.SetUpTest(c, s.uniterSuite) } func (s *relationUnitSuite) TearDownTest(c *gc.C) { s.uniterSuite.TearDownTest(c) } func (s *relationUnitSuite) getRelationUnits(c *gc.C) (*state.RelationUnit, *uniter.RelationUnit) { wpRelUnit, err := s.stateRelation.Unit(s.wordpressUnit) c.Assert(err, gc.IsNil) apiRelation, err := s.uniter.Relation(s.stateRelation.Tag()) c.Assert(err, gc.IsNil) apiUnit, err := s.uniter.Unit(s.wordpressUnit.Tag()) c.Assert(err, gc.IsNil) apiRelUnit, err := apiRelation.Unit(apiUnit) c.Assert(err, gc.IsNil) return wpRelUnit, apiRelUnit } func (s *relationUnitSuite) TestRelation(c *gc.C) { _, apiRelUnit := s.getRelationUnits(c) apiRel := apiRelUnit.Relation() c.Assert(apiRel, gc.NotNil) c.Assert(apiRel.String(), gc.Equals, "wordpress:db mysql:server") } func (s *relationUnitSuite) TestEndpoint(c *gc.C) { _, apiRelUnit := s.getRelationUnits(c) apiEndpoint := apiRelUnit.Endpoint() c.Assert(apiEndpoint, gc.DeepEquals, uniter.Endpoint{ charm.Relation{ Name: "db", Role: "requirer", Interface: "mysql", Optional: false, Limit: 1, Scope: "global", }, }) } func (s *relationUnitSuite) TestPrivateAddress(c *gc.C) { _, apiRelUnit := s.getRelationUnits(c) // Try getting it first without an address set. address, err := apiRelUnit.PrivateAddress() c.Assert(err, gc.ErrorMatches, `"unit-wordpress-0" has no private address set`) // Set an address and try again. err = s.wordpressUnit.SetPrivateAddress("1.2.3.4") c.Assert(err, gc.IsNil) address, err = apiRelUnit.PrivateAddress() c.Assert(err, gc.IsNil) c.Assert(address, gc.Equals, "1.2.3.4") } func (s *relationUnitSuite) TestEnterScopeSuccessfully(c *gc.C) { // NOTE: This test is not as exhaustive as the ones in state. // Here, we just check the success case, while the two error // cases are tested separately. wpRelUnit, apiRelUnit := s.getRelationUnits(c) s.assertInScope(c, wpRelUnit, false) err := apiRelUnit.EnterScope() c.Assert(err, gc.IsNil) s.assertInScope(c, wpRelUnit, true) } func (s *relationUnitSuite) TestEnterScopeErrCannotEnterScope(c *gc.C) { // Test the ErrCannotEnterScope gets forwarded correctly. // We need to enter the scope wit the other unit first. myRelUnit, err := s.stateRelation.Unit(s.mysqlUnit) c.Assert(err, gc.IsNil) err = myRelUnit.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertInScope(c, myRelUnit, true) // Now we destroy mysqlService, so the relation is be set to // dying. err = s.mysqlService.Destroy() c.Assert(err, gc.IsNil) err = s.stateRelation.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.stateRelation.Life(), gc.Equals, state.Dying) // Enter the scope with wordpressUnit. wpRelUnit, apiRelUnit := s.getRelationUnits(c) s.assertInScope(c, wpRelUnit, false) err = apiRelUnit.EnterScope() c.Assert(err, gc.NotNil) c.Check(err, jc.Satisfies, params.IsCodeCannotEnterScope) c.Check(err, gc.ErrorMatches, "cannot enter scope: unit or relation is not alive") } func (s *relationUnitSuite) TestEnterScopeErrCannotEnterScopeYet(c *gc.C) { // Test the ErrCannotEnterScopeYet gets forwarded correctly. // First we need to destroy the stateRelation. err := s.stateRelation.Destroy() c.Assert(err, gc.IsNil) // Now we create a subordinate of wordpressUnit and enter scope. subRel, _, loggingSub := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) wpRelUnit, err := subRel.Unit(s.wordpressUnit) c.Assert(err, gc.IsNil) s.assertInScope(c, wpRelUnit, true) // Leave scope, destroy the subordinate and try entering again. err = wpRelUnit.LeaveScope() c.Assert(err, gc.IsNil) s.assertInScope(c, wpRelUnit, false) err = loggingSub.Destroy() c.Assert(err, gc.IsNil) apiUnit, err := s.uniter.Unit(s.wordpressUnit.Tag()) c.Assert(err, gc.IsNil) apiRel, err := s.uniter.Relation(subRel.Tag()) c.Assert(err, gc.IsNil) apiRelUnit, err := apiRel.Unit(apiUnit) c.Assert(err, gc.IsNil) err = apiRelUnit.EnterScope() c.Assert(err, gc.NotNil) c.Check(err, jc.Satisfies, params.IsCodeCannotEnterScopeYet) c.Check(err, gc.ErrorMatches, "cannot enter scope yet: non-alive subordinate unit has not been removed") } func (s *relationUnitSuite) TestLeaveScope(c *gc.C) { wpRelUnit, apiRelUnit := s.getRelationUnits(c) s.assertInScope(c, wpRelUnit, false) err := wpRelUnit.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertInScope(c, wpRelUnit, true) err = apiRelUnit.LeaveScope() c.Assert(err, gc.IsNil) s.assertInScope(c, wpRelUnit, false) } func (s *relationUnitSuite) TestSettings(c *gc.C) { wpRelUnit, apiRelUnit := s.getRelationUnits(c) settings := map[string]interface{}{ "some": "settings", "other": "things", } err := wpRelUnit.EnterScope(settings) c.Assert(err, gc.IsNil) s.assertInScope(c, wpRelUnit, true) gotSettings, err := apiRelUnit.Settings() c.Assert(err, gc.IsNil) c.Assert(gotSettings.Map(), gc.DeepEquals, params.RelationSettings{ "some": "settings", "other": "things", }) } func (s *relationUnitSuite) TestReadSettings(c *gc.C) { // First try to read the settings which are not set. myRelUnit, err := s.stateRelation.Unit(s.mysqlUnit) c.Assert(err, gc.IsNil) err = myRelUnit.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertInScope(c, myRelUnit, true) // Try reading - should be ok. wpRelUnit, apiRelUnit := s.getRelationUnits(c) s.assertInScope(c, wpRelUnit, false) gotSettings, err := apiRelUnit.ReadSettings("mysql/0") c.Assert(err, gc.IsNil) c.Assert(gotSettings, gc.HasLen, 0) // Now leave and re-enter scope with some settings. settings := map[string]interface{}{ "some": "settings", "other": "things", } err = myRelUnit.LeaveScope() c.Assert(err, gc.IsNil) s.assertInScope(c, myRelUnit, false) err = myRelUnit.EnterScope(settings) c.Assert(err, gc.IsNil) s.assertInScope(c, myRelUnit, true) gotSettings, err = apiRelUnit.ReadSettings("mysql/0") c.Assert(err, gc.IsNil) c.Assert(gotSettings, gc.DeepEquals, params.RelationSettings{ "some": "settings", "other": "things", }) } func (s *relationUnitSuite) TestWatchRelationUnits(c *gc.C) { // Enter scope with mysqlUnit. myRelUnit, err := s.stateRelation.Unit(s.mysqlUnit) c.Assert(err, gc.IsNil) err = myRelUnit.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertInScope(c, myRelUnit, true) apiRel, err := s.uniter.Relation(s.stateRelation.Tag()) c.Assert(err, gc.IsNil) apiUnit, err := s.uniter.Unit("unit-wordpress-0") c.Assert(err, gc.IsNil) apiRelUnit, err := apiRel.Unit(apiUnit) c.Assert(err, gc.IsNil) w, err := apiRelUnit.Watch() defer statetesting.AssertStop(c, w) wc := statetesting.NewRelationUnitsWatcherC(c, s.BackingState, w) // Initial event. wc.AssertChange([]string{"mysql/0"}, nil) // Leave scope with mysqlUnit, check it's detected. err = myRelUnit.LeaveScope() c.Assert(err, gc.IsNil) s.assertInScope(c, myRelUnit, false) wc.AssertChange(nil, []string{"mysql/0"}) // Non-change is not reported. err = myRelUnit.LeaveScope() c.Assert(err, gc.IsNil) wc.AssertNoChange() // NOTE: This test is not as exhaustive as the one in state, // because the watcher is already tested there. Here we just // ensure we get the events when we expect them and don't get // them when they're not expected. statetesting.AssertStop(c, w) wc.AssertClosed() } ������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/relationunit.go�����������������������0000644�0000153�0000161�00000013367�12321735642�027142� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "fmt" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // This module implements a subset of the interface provided by // state.RelationUnit, as needed by the uniter API. // Most of this is pretty much a verbatim copy of the code in // state/relationunit.go, except for a few API-specific changes. // RelationUnit holds information about a single unit in a relation, // and allows clients to conveniently access unit-specific // functionality. type RelationUnit struct { st *State relation *Relation unit *Unit endpoint Endpoint scope string } // Relation returns the relation associated with the unit. func (ru *RelationUnit) Relation() *Relation { return ru.relation } // Endpoint returns the relation endpoint that defines the unit's // participation in the relation. func (ru *RelationUnit) Endpoint() Endpoint { return ru.endpoint } // PrivateAddress returns the private address of the unit and whether // it is valid. // // NOTE: This differs from state.RelationUnit.PrivateAddress() by // returning an error instead of a bool, because it needs to make an // API call. func (ru *RelationUnit) PrivateAddress() (string, error) { return ru.unit.PrivateAddress() } // EnterScope ensures that the unit has entered its scope in the relation. // When the unit has already entered its relation scope, EnterScope will report // success but make no changes to state. // // Otherwise, assuming both the relation and the unit are alive, it will enter // scope. // // If the unit is a principal and the relation has container scope, EnterScope // will also create the required subordinate unit, if it does not already exist; // this is because there's no point having a principal in scope if there is no // corresponding subordinate to join it. // // Once a unit has entered a scope, it stays in scope without further // intervention; the relation will not be able to become Dead until all units // have departed its scopes. // // NOTE: Unlike state.RelatioUnit.EnterScope(), this method does not take // settings, because uniter only uses this to supply the unit's private // address, but this is not done at the server-side by the API. func (ru *RelationUnit) EnterScope() error { var result params.ErrorResults args := params.RelationUnits{ RelationUnits: []params.RelationUnit{{ Relation: ru.relation.tag, Unit: ru.unit.tag, }}, } err := ru.st.call("EnterScope", args, &result) if err != nil { return err } return result.OneError() } // LeaveScope signals that the unit has left its scope in the relation. // After the unit has left its relation scope, it is no longer a member // of the relation; if the relation is dying when its last member unit // leaves, it is removed immediately. It is not an error to leave a scope // that the unit is not, or never was, a member of. func (ru *RelationUnit) LeaveScope() error { var result params.ErrorResults args := params.RelationUnits{ RelationUnits: []params.RelationUnit{{ Relation: ru.relation.tag, Unit: ru.unit.tag, }}, } err := ru.st.call("LeaveScope", args, &result) if err != nil { return err } return result.OneError() } // Settings returns a Settings which allows access to the unit's settings // within the relation. func (ru *RelationUnit) Settings() (*Settings, error) { var results params.RelationSettingsResults args := params.RelationUnits{ RelationUnits: []params.RelationUnit{{ Relation: ru.relation.tag, Unit: ru.unit.tag, }}, } err := ru.st.call("ReadSettings", 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 newSettings(ru.st, ru.relation.tag, ru.unit.tag, result.Settings), nil } // ReadSettings returns a map holding the settings of the unit with the // supplied name within this relation. An error will be returned if the // relation no longer exists, or if the unit's service is not part of the // relation, or the settings are invalid; but mere non-existence of the // unit is not grounds for an error, because the unit settings are // guaranteed to persist for the lifetime of the relation, regardless // of the lifetime of the unit. func (ru *RelationUnit) ReadSettings(uname string) (params.RelationSettings, error) { tag := names.UnitTag(uname) var results params.RelationSettingsResults args := params.RelationUnitPairs{ RelationUnitPairs: []params.RelationUnitPair{{ Relation: ru.relation.tag, LocalUnit: ru.unit.tag, RemoteUnit: tag, }}, } err := ru.st.call("ReadRemoteSettings", 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.Settings, nil } // Watch returns a watcher that notifies of changes to counterpart // units in the relation. func (ru *RelationUnit) Watch() (watcher.RelationUnitsWatcher, error) { var results params.RelationUnitsWatchResults args := params.RelationUnits{ RelationUnits: []params.RelationUnit{{ Relation: ru.relation.tag, Unit: ru.unit.tag, }}, } err := ru.st.call("WatchRelationUnits", 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 } w := watcher.NewRelationUnitsWatcher(ru.st.caller, result) return w, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/endpoint.go���������������������������0000644�0000153�0000161�00000000612�12321735642�026232� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "launchpad.net/juju-core/charm" ) // Endpoint represents one endpoint of a relation. It is just a wrapper // around charm.Relation. No API calls to the server-side are needed to // support the interface needed by the uniter worker. type Endpoint struct { charm.Relation } ����������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/uniter_test.go������������������������0000644�0000153�0000161�00000006151�12321735642�026763� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/uniter" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) // NOTE: This suite is intended for embedding into other suites, // so common code can be reused. Do not add test cases to it, // otherwise they'll be run by each other suite that embeds it. type uniterSuite struct { testing.JujuConnSuite st *api.State stateServerMachine *state.Machine wordpressMachine *state.Machine wordpressService *state.Service wordpressCharm *state.Charm wordpressUnit *state.Unit uniter *uniter.State } var _ = gc.Suite(&uniterSuite{}) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } func (s *uniterSuite) SetUpTest(c *gc.C) { s.setUpTest(c, true) } func (s *uniterSuite) setUpTest(c *gc.C, addStateServer bool) { s.JujuConnSuite.SetUpTest(c) if addStateServer { s.stateServerMachine = testing.AddStateServerMachine(c, s.State) } // Create a machine, a service and add a unit so we can log in as // its agent. s.wordpressMachine, s.wordpressService, s.wordpressCharm, s.wordpressUnit = s.addMachineServiceCharmAndUnit(c, "wordpress") password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = s.wordpressUnit.SetPassword(password) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAs(c, s.wordpressUnit.Tag(), password) // Create the uniter API facade. s.uniter = s.st.Uniter() c.Assert(s.uniter, gc.NotNil) } func (s *uniterSuite) addMachineServiceCharmAndUnit(c *gc.C, serviceName string) (*state.Machine, *state.Service, *state.Charm, *state.Unit) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) charm := s.AddTestingCharm(c, serviceName) service := s.AddTestingService(c, serviceName, charm) unit, err := service.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToMachine(machine) c.Assert(err, gc.IsNil) return machine, service, charm, unit } func (s *uniterSuite) addRelation(c *gc.C, first, second string) *state.Relation { eps, err := s.State.InferEndpoints([]string{first, second}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) return rel } func (s *uniterSuite) addRelatedService(c *gc.C, firstSvc, relatedSvc string, unit *state.Unit) (*state.Relation, *state.Service, *state.Unit) { relatedService := s.AddTestingService(c, relatedSvc, s.AddTestingCharm(c, relatedSvc)) rel := s.addRelation(c, firstSvc, relatedSvc) relUnit, err := rel.Unit(unit) c.Assert(err, gc.IsNil) err = relUnit.EnterScope(nil) c.Assert(err, gc.IsNil) relatedUnit, err := relatedService.Unit(relatedSvc + "/0") c.Assert(err, gc.IsNil) return rel, relatedService, relatedUnit } func (s *uniterSuite) assertInScope(c *gc.C, relUnit *state.RelationUnit, inScope bool) { ok, err := relUnit.InScope() c.Assert(err, gc.IsNil) c.Assert(ok, gc.Equals, inScope) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/settings.go���������������������������0000644�0000153�0000161�00000005244�12321735642�026260� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "launchpad.net/juju-core/state/api/params" ) // This module implements a subset of the interface provided by // state.Settings, as needed by the uniter API. // Settings manages changes to unit settings in a relation. type Settings struct { st *State relationTag string unitTag string settings params.RelationSettings } func newSettings(st *State, relationTag, unitTag string, settings params.RelationSettings) *Settings { if settings == nil { settings = make(params.RelationSettings) } return &Settings{ st: st, relationTag: relationTag, unitTag: unitTag, settings: settings, } } // Map returns all keys and values of the node. // // TODO(dimitern): This differes from state.Settings.Map() - it does // not return map[string]interface{}, but since all values are // expected to be strings anyway, we need to fix the uniter code // accordingly when migrating to the API. func (s *Settings) Map() params.RelationSettings { settingsCopy := make(params.RelationSettings) for k, v := range s.settings { if v != "" { // Skip deleted keys. settingsCopy[k] = v } } return settingsCopy } // Set sets key to value. // // TODO(dimitern): value must be a string. Change the code that uses // this accordingly. func (s *Settings) Set(key, value string) { s.settings[key] = value } // Delete removes key. func (s *Settings) Delete(key string) { // Keys are only marked as deleted, because we need to report them // back to the server for deletion on Write(). s.settings[key] = "" } // Write writes changes made to s back onto its node. Keys set to // empty values will be deleted, others will be updated to the new // value. // // TODO(dimitern): 2013-09-06 bug 1221798 // Once the machine addressability changes lands, we may need to // revise the logic here to take into account that the // "private-address" setting for a unit can be changed outside of the // uniter's control. So we may need to send diffs of what has changed // to make sure we update the address (and other settings) correctly, // without overwritting. func (s *Settings) Write() error { // First make a copy of the map, including deleted keys. settingsCopy := make(params.RelationSettings) for k, v := range s.settings { settingsCopy[k] = v } var result params.ErrorResults args := params.RelationUnitsSettings{ RelationUnits: []params.RelationUnitSettings{{ Relation: s.relationTag, Unit: s.unitTag, Settings: settingsCopy, }}, } err := s.st.call("UpdateSettings", args, &result) if err != nil { return err } return result.OneError() } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/service.go����������������������������0000644�0000153�0000161�00000007311�12321735642�026055� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "fmt" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // This module implements a subset of the interface provided by // state.Service, as needed by the uniter API. // Service represents the state of a service. type Service struct { st *State tag string life params.Life } // Name returns the service name. func (s *Service) Name() string { _, serviceName, err := names.ParseTag(s.tag, names.ServiceTagKind) if err != nil { panic(fmt.Sprintf("%q is not a valid service tag", s.tag)) } return serviceName } // String returns the service as a string. func (s *Service) String() string { return s.Name() } // Watch returns a watcher for observing changes to a service. func (s *Service) Watch() (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: s.tag}}, } err := s.st.call("Watch", 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 } w := watcher.NewNotifyWatcher(s.st.caller, result) return w, nil } // WatchRelations returns a StringsWatcher that notifies of changes to // the lifecycles of relations involving s. func (s *Service) WatchRelations() (watcher.StringsWatcher, error) { var results params.StringsWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: s.tag}}, } err := s.st.call("WatchServiceRelations", 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 } w := watcher.NewStringsWatcher(s.st.caller, result) return w, nil } // Life returns the service's current life state. func (s *Service) Life() params.Life { return s.life } // Refresh refreshes the contents of the Service from the underlying // state. func (s *Service) Refresh() error { life, err := s.st.life(s.tag) if err != nil { return err } s.life = life return nil } // CharmURL returns the service's charm URL, and whether units should // upgrade to the charm with that URL even if they are in an error // state (force flag). // // NOTE: This differs from state.Service.CharmURL() by returning // an error instead as well, because it needs to make an API call. func (s *Service) CharmURL() (*charm.URL, bool, error) { var results params.StringBoolResults args := params.Entities{ Entities: []params.Entity{{Tag: s.tag}}, } err := s.st.call("CharmURL", args, &results) if err != nil { return nil, false, err } if len(results.Results) != 1 { return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return nil, false, result.Error } if result.Result != "" { curl, err := charm.ParseURL(result.Result) if err != nil { return nil, false, err } return curl, result.Ok, nil } return nil, false, fmt.Errorf("%q has no charm url set", s.tag) } // TODO(dimitern) bug #1270795 2014-01-20 // Add a doc comment here. func (s *Service) GetOwnerTag() (string, error) { var result params.StringResult args := params.Entities{ Entities: []params.Entity{{Tag: s.tag}}, } err := s.st.call("GetOwnerTag", args, &result) if err != nil { return "", err } if result.Error != nil { return "", result.Error } return result.Result, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/environ.go����������������������������0000644�0000153�0000161�00000001077�12321735642�026100� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter // This module implements a subset of the interface provided by // state.Environment, as needed by the uniter API. // Environment represents the state of an environment. type Environment struct { name string uuid string } // UUID returns the universally unique identifier of the environment. func (e Environment) UUID() string { return e.uuid } // Name returns the human friendly name of the environment. func (e Environment) Name() string { return e.name } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/charm.go������������������������������0000644�0000153�0000161�00000005570�12321735642�025514� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "fmt" "net/url" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/utils" ) // This module implements a subset of the interface provided by // state.Charm, as needed by the uniter API. // Charm represents the state of a charm in the environment. type Charm struct { st *State url string } // Strings returns the charm URL as a string. func (c *Charm) String() string { return c.url } // URL returns the URL that identifies the charm. func (c *Charm) URL() *charm.URL { return charm.MustParseURL(c.url) } func (c *Charm) getArchiveInfo(apiCall string) (string, error) { var results params.StringResults args := params.CharmURLs{ URLs: []params.CharmURL{{URL: c.url}}, } err := c.st.call(apiCall, args, &results) if err != nil { return "", err } if len(results.Results) != 1 { return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return "", result.Error } return result.Result, nil } // ArchiveURL returns the url to the charm archive (bundle) in the // provider storage, and DisableSSLHostnameVerification flag. // // NOTE: This differs from state.Charm.BundleURL() by returning an // error as well, because it needs to make an API call. It's also // renamed to avoid confusion with juju deployment bundles. // // TODO(dimitern): 2013-09-06 bug 1221834 // Cache the result after getting it once for the same charm URL, // because it's immutable. func (c *Charm) ArchiveURL() (*url.URL, utils.SSLHostnameVerification, error) { var results params.CharmArchiveURLResults args := params.CharmURLs{ URLs: []params.CharmURL{{URL: c.url}}, } err := c.st.call("CharmArchiveURL", args, &results) if err != nil { return nil, false, err } if len(results.Results) != 1 { return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return nil, false, result.Error } archiveURL, err := url.Parse(result.Result) if err != nil { return nil, false, err } hostnameVerification := utils.VerifySSLHostnames if result.DisableSSLHostnameVerification { hostnameVerification = utils.NoVerifySSLHostnames } return archiveURL, hostnameVerification, nil } // ArchiveSha256 returns the SHA256 digest of the charm archive // (bundle) bytes. // // NOTE: This differs from state.Charm.BundleSha256() by returning an // error as well, because it needs to make an API call. It's also // renamed to avoid confusion with juju deployment bundles. // // TODO(dimitern): 2013-09-06 bug 1221834 // Cache the result after getting it once for the same charm URL, // because it's immutable. func (c *Charm) ArchiveSha256() (string, error) { return c.getArchiveInfo("CharmArchiveSha256") } ����������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/charm_test.go�������������������������0000644�0000153�0000161�00000003414�12321735642�026546� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( gc "launchpad.net/gocheck" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/state/api/uniter" "launchpad.net/juju-core/utils" ) type charmSuite struct { uniterSuite apiCharm *uniter.Charm } var _ = gc.Suite(&charmSuite{}) func (s *charmSuite) SetUpTest(c *gc.C) { s.uniterSuite.SetUpTest(c) var err error s.apiCharm, err = s.uniter.Charm(s.wordpressCharm.URL()) c.Assert(err, gc.IsNil) c.Assert(s.apiCharm, gc.NotNil) } func (s *charmSuite) TearDownTest(c *gc.C) { s.uniterSuite.TearDownTest(c) } func (s *charmSuite) TestCharmWithNilFails(c *gc.C) { _, err := s.uniter.Charm(nil) c.Assert(err, gc.ErrorMatches, "charm url cannot be nil") } func (s *charmSuite) TestString(c *gc.C) { c.Assert(s.apiCharm.String(), gc.Equals, s.wordpressCharm.String()) } func (s *charmSuite) TestURL(c *gc.C) { c.Assert(s.apiCharm.URL(), gc.DeepEquals, s.wordpressCharm.URL()) } func (s *charmSuite) TestArchiveURL(c *gc.C) { archiveURL, hostnameVerification, err := s.apiCharm.ArchiveURL() c.Assert(err, gc.IsNil) c.Assert(archiveURL, gc.DeepEquals, s.wordpressCharm.BundleURL()) c.Assert(hostnameVerification, gc.Equals, utils.VerifySSLHostnames) envtesting.SetSSLHostnameVerification(c, s.State, false) archiveURL, hostnameVerification, err = s.apiCharm.ArchiveURL() c.Assert(err, gc.IsNil) c.Assert(archiveURL, gc.DeepEquals, s.wordpressCharm.BundleURL()) c.Assert(hostnameVerification, gc.Equals, utils.NoVerifySSLHostnames) } func (s *charmSuite) TestArchiveSha256(c *gc.C) { archiveSha256, err := s.apiCharm.ArchiveSha256() c.Assert(err, gc.IsNil) c.Assert(archiveSha256, gc.Equals, s.wordpressCharm.BundleSha256()) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/uniter.go�����������������������������0000644�0000153�0000161�00000011466�12321735642�025731� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "fmt" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/common" "launchpad.net/juju-core/state/api/params" ) const uniterFacade = "Uniter" // State provides access to the Uniter API facade. type State struct { *common.EnvironWatcher *common.APIAddresser caller base.Caller // unitTag contains the authenticated unit's tag. unitTag string } // NewState creates a new client-side Uniter facade. func NewState(caller base.Caller, authTag string) *State { return &State{ EnvironWatcher: common.NewEnvironWatcher(uniterFacade, caller), APIAddresser: common.NewAPIAddresser(uniterFacade, caller), caller: caller, unitTag: authTag, } } func (st *State) call(method string, params, results interface{}) error { return st.caller.Call(uniterFacade, "", method, params, results) } // life requests the lifecycle of the given entity from the server. func (st *State) life(tag string) (params.Life, error) { return common.Life(st.caller, uniterFacade, tag) } // relation requests relation information from the server. func (st *State) relation(relationTag, unitTag string) (params.RelationResult, error) { nothing := params.RelationResult{} var result params.RelationResults args := params.RelationUnits{ RelationUnits: []params.RelationUnit{ {Relation: relationTag, Unit: unitTag}, }, } err := st.call("Relation", args, &result) if err != nil { return nothing, err } if len(result.Results) != 1 { return nothing, fmt.Errorf("expected 1 result, got %d", len(result.Results)) } if err := result.Results[0].Error; err != nil { return nothing, err } return result.Results[0], nil } // Unit provides access to methods of a state.Unit through the facade. func (st *State) Unit(tag string) (*Unit, error) { life, err := st.life(tag) if err != nil { return nil, err } return &Unit{ tag: tag, life: life, st: st, }, nil } // Service returns a service state by tag. func (st *State) Service(tag string) (*Service, error) { life, err := st.life(tag) if err != nil { return nil, err } return &Service{ tag: tag, life: life, st: st, }, nil } // ProviderType returns a provider type used by the current juju // environment. // // TODO(dimitern): We might be able to drop this, once we have machine // addresses implemented fully. See also LP bug 1221798. func (st *State) ProviderType() (string, error) { var result params.StringResult err := st.call("ProviderType", nil, &result) if err != nil { return "", err } if err := result.Error; err != nil { return "", err } return result.Result, nil } // Charm returns the charm with the given URL. func (st *State) Charm(curl *charm.URL) (*Charm, error) { if curl == nil { return nil, fmt.Errorf("charm url cannot be nil") } return &Charm{ st: st, url: curl.String(), }, nil } // Relation returns the existing relation with the given tag. func (st *State) Relation(tag string) (*Relation, error) { result, err := st.relation(tag, st.unitTag) if err != nil { return nil, err } return &Relation{ id: result.Id, tag: tag, life: result.Life, st: st, }, nil } // RelationById returns the existing relation with the given id. func (st *State) RelationById(id int) (*Relation, error) { var results params.RelationResults args := params.RelationIds{ RelationIds: []int{id}, } err := st.call("RelationById", 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 err := result.Error; err != nil { return nil, err } relationTag := names.RelationTag(result.Key) return &Relation{ id: result.Id, tag: relationTag, life: result.Life, st: st, }, nil } // Environment returns the environment entity. func (st *State) Environment() (*Environment, error) { var result params.EnvironmentResult err := st.call("CurrentEnvironment", nil, &result) if params.IsCodeNotImplemented(err) { // Fall back to using the 1.16 API. return st.environment1dot16() } if err != nil { return nil, err } if err := result.Error; err != nil { return nil, err } return &Environment{ name: result.Name, uuid: result.UUID, }, nil } // environment1dot16 requests just the UUID of the current environment, when // using an older API server that does not support CurrentEnvironment API call. func (st *State) environment1dot16() (*Environment, error) { var result params.StringResult err := st.call("CurrentEnvironUUID", nil, &result) if err != nil { return nil, err } if err := result.Error; err != nil { return nil, err } return &Environment{ uuid: result.Result, }, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/relation_test.go����������������������0000644�0000153�0000161�00000006301�12321735642�027267� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/uniter" ) type relationSuite struct { uniterSuite commonRelationSuiteMixin apiRelation *uniter.Relation } var _ = gc.Suite(&relationSuite{}) func (s *relationSuite) SetUpTest(c *gc.C) { s.uniterSuite.SetUpTest(c) s.commonRelationSuiteMixin.SetUpTest(c, s.uniterSuite) var err error s.apiRelation, err = s.uniter.Relation(s.stateRelation.Tag()) c.Assert(err, gc.IsNil) } func (s *relationSuite) TearDownTest(c *gc.C) { s.uniterSuite.TearDownTest(c) } func (s *relationSuite) TestString(c *gc.C) { c.Assert(s.apiRelation.String(), gc.Equals, "wordpress:db mysql:server") } func (s *relationSuite) TestId(c *gc.C) { c.Assert(s.apiRelation.Id(), gc.Equals, s.stateRelation.Id()) } func (s *relationSuite) TestRefresh(c *gc.C) { c.Assert(s.apiRelation.Life(), gc.Equals, params.Alive) // EnterScope with mysqlUnit, so the relation will be set to dying // when destroyed later. myRelUnit, err := s.stateRelation.Unit(s.mysqlUnit) c.Assert(err, gc.IsNil) err = myRelUnit.EnterScope(nil) c.Assert(err, gc.IsNil) s.assertInScope(c, myRelUnit, true) // Destroy it - should set it to dying. err = s.stateRelation.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.apiRelation.Life(), gc.Equals, params.Alive) err = s.apiRelation.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.apiRelation.Life(), gc.Equals, params.Dying) // Leave scope with mysqlUnit, so the relation will be removed. err = myRelUnit.LeaveScope() c.Assert(err, gc.IsNil) c.Assert(s.apiRelation.Life(), gc.Equals, params.Dying) err = s.apiRelation.Refresh() c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) } func (s *relationSuite) TestEndpoint(c *gc.C) { apiEndpoint, err := s.apiRelation.Endpoint() c.Assert(err, gc.IsNil) c.Assert(apiEndpoint, gc.DeepEquals, &uniter.Endpoint{ charm.Relation{ Name: "db", Role: "requirer", Interface: "mysql", Optional: false, Limit: 1, Scope: "global", }, }) } func (s *relationSuite) TestUnit(c *gc.C) { _, err := s.apiRelation.Unit(nil) c.Assert(err, gc.ErrorMatches, "unit is nil") apiUnit, err := s.uniter.Unit("unit-wordpress-0") c.Assert(err, gc.IsNil) apiRelUnit, err := s.apiRelation.Unit(apiUnit) c.Assert(err, gc.IsNil) c.Assert(apiRelUnit, gc.NotNil) // We just ensure we get the correct type, more tests // are done in relationunit_test.go. c.Assert(apiRelUnit, gc.FitsTypeOf, (*uniter.RelationUnit)(nil)) } func (s *relationSuite) TestRelationById(c *gc.C) { apiRel, err := s.uniter.RelationById(s.stateRelation.Id()) c.Assert(err, gc.IsNil) c.Assert(apiRel, gc.DeepEquals, s.apiRelation) // Add a relation to mysql service, which cannot be retrived. otherRel, _, _ := s.addRelatedService(c, "mysql", "logging", s.mysqlUnit) // Test some invalid cases. for _, relId := range []int{-1, 42, otherRel.Id()} { apiRel, err = s.uniter.RelationById(relId) c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(apiRel, gc.IsNil) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/state_test.go�������������������������0000644�0000153�0000161�00000001517�12321735642�026576� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( gc "launchpad.net/gocheck" apitesting "launchpad.net/juju-core/state/api/testing" ) type stateSuite struct { uniterSuite *apitesting.APIAddresserTests *apitesting.EnvironWatcherTests } var _ = gc.Suite(&stateSuite{}) func (s *stateSuite) SetUpTest(c *gc.C) { s.uniterSuite.SetUpTest(c) s.APIAddresserTests = apitesting.NewAPIAddresserTests(s.uniter, s.BackingState) s.EnvironWatcherTests = apitesting.NewEnvironWatcherTests(s.uniter, s.BackingState, apitesting.NoSecrets) } func (s *stateSuite) TestProviderType(c *gc.C) { cfg, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) providerType, err := s.uniter.ProviderType() c.Assert(err, gc.IsNil) c.Assert(providerType, gc.DeepEquals, cfg.Type()) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/unit.go�������������������������������0000644�0000153�0000161�00000032155�12321735776�025410� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter import ( "errors" "fmt" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // Unit represents a juju unit as seen by a uniter worker. type Unit struct { st *State tag string life params.Life } // Tag returns the unit's tag. func (u *Unit) Tag() string { return u.tag } // Name returns the name of the unit. func (u *Unit) Name() string { _, unitName, err := names.ParseTag(u.tag, names.UnitTagKind) if err != nil { panic(fmt.Sprintf("%q is not a valid unit tag", u.tag)) } return unitName } // String returns the unit as a string. func (u *Unit) String() string { return u.Name() } // Life returns the unit's lifecycle value. func (u *Unit) Life() params.Life { return u.life } // Refresh updates the cached local copy of the unit's data. func (u *Unit) Refresh() error { life, err := u.st.life(u.tag) if err != nil { return err } u.life = life return nil } // SetStatus sets the status of the unit. func (u *Unit) SetStatus(status params.Status, info string, data params.StatusData) error { var result params.ErrorResults args := params.SetStatus{ Entities: []params.EntityStatus{ {Tag: u.tag, Status: status, Info: info, Data: data}, }, } err := u.st.call("SetStatus", args, &result) if err != nil { return err } return result.OneError() } // EnsureDead sets the unit lifecycle to Dead if it is Alive or // Dying. It does nothing otherwise. func (u *Unit) EnsureDead() error { var result params.ErrorResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("EnsureDead", args, &result) if err != nil { return err } return result.OneError() } // Watch returns a watcher for observing changes to the unit. func (u *Unit) Watch() (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("Watch", 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 } w := watcher.NewNotifyWatcher(u.st.caller, result) return w, nil } // Service returns the service. func (u *Unit) Service() (*Service, error) { serviceTag := names.ServiceTag(u.ServiceName()) service := &Service{ st: u.st, tag: serviceTag, } // Call Refresh() immediately to get the up-to-date // life and other needed locally cached fields. err := service.Refresh() if err != nil { return nil, err } return service, nil } // ConfigSettings returns the complete set of service charm config settings // available to the unit. Unset values will be replaced with the default // value for the associated option, and may thus be nil when no default is // specified. func (u *Unit) ConfigSettings() (charm.Settings, error) { var results params.ConfigSettingsResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("ConfigSettings", 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 charm.Settings(result.Settings), nil } // ServiceName returns the service name. func (u *Unit) ServiceName() string { return names.UnitService(u.Name()) } // ServiceTag returns the service tag. func (u *Unit) ServiceTag() string { return names.ServiceTag(u.ServiceName()) } // Destroy, when called on a Alive unit, advances its lifecycle as far as // possible; it otherwise has no effect. In most situations, the unit's // life is just set to Dying; but if a principal unit that is not assigned // to a provisioned machine is Destroyed, it will be removed from state // directly. func (u *Unit) Destroy() error { var result params.ErrorResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("Destroy", args, &result) if err != nil { return err } return result.OneError() } // DestroyAllSubordinates destroys all subordinates of the unit. func (u *Unit) DestroyAllSubordinates() error { var result params.ErrorResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("DestroyAllSubordinates", args, &result) if err != nil { return err } return result.OneError() } // Resolved returns the resolved mode for the unit. // // NOTE: This differs from state.Unit.Resolved() by returning an // error as well, because it needs to make an API call func (u *Unit) Resolved() (params.ResolvedMode, error) { var results params.ResolvedModeResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("Resolved", args, &results) if err != nil { return "", err } if len(results.Results) != 1 { return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return "", result.Error } return result.Mode, nil } // IsPrincipal returns whether the unit is deployed in its own container, // and can therefore have subordinate services deployed alongside it. // // NOTE: This differs from state.Unit.IsPrincipal() by returning an // error as well, because it needs to make an API call. func (u *Unit) IsPrincipal() (bool, error) { var results params.StringBoolResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("GetPrincipal", args, &results) if err != nil { return false, err } if len(results.Results) != 1 { return false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return false, result.Error } // GetPrincipal returns false when the unit is subordinate. return !result.Ok, nil } // HasSubordinates returns the tags of any subordinate units. func (u *Unit) HasSubordinates() (bool, error) { var results params.BoolResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("HasSubordinates", args, &results) if err != nil { return false, err } if len(results.Results) != 1 { return false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return false, result.Error } return result.Result, nil } // PublicAddress returns the public address of the unit and whether it // is valid. // // NOTE: This differs from state.Unit.PublicAddres() by returning // an error instead of a bool, because it needs to make an API call. // // TODO(dimitern): We might be able to drop this, once we have machine // addresses implemented fully. See also LP bug 1221798. func (u *Unit) PublicAddress() (string, error) { var results params.StringResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("PublicAddress", args, &results) if err != nil { return "", err } if len(results.Results) != 1 { return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return "", result.Error } return result.Result, nil } // SetPublicAddress sets the public address of the unit. // // TODO(dimitern): We might be able to drop this, once we have machine // addresses implemented fully. See also LP bug 1221798. func (u *Unit) SetPublicAddress(address string) error { var result params.ErrorResults args := params.SetEntityAddresses{ Entities: []params.SetEntityAddress{ {Tag: u.tag, Address: address}, }, } err := u.st.call("SetPublicAddress", args, &result) if err != nil { return err } return result.OneError() } // PrivateAddress returns the private address of the unit and whether // it is valid. // // NOTE: This differs from state.Unit.PrivateAddress() by returning // an error instead of a bool, because it needs to make an API call. // // TODO(dimitern): We might be able to drop this, once we have machine // addresses implemented fully. See also LP bug 1221798. func (u *Unit) PrivateAddress() (string, error) { var results params.StringResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("PrivateAddress", args, &results) if err != nil { return "", err } if len(results.Results) != 1 { return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return "", result.Error } return result.Result, nil } // SetPrivateAddress sets the private address of the unit. // // TODO(dimitern): We might be able to drop this, once we have machine // addresses implemented fully. See also LP bug 1221798. func (u *Unit) SetPrivateAddress(address string) error { var result params.ErrorResults args := params.SetEntityAddresses{ Entities: []params.SetEntityAddress{ {Tag: u.tag, Address: address}, }, } err := u.st.call("SetPrivateAddress", args, &result) if err != nil { return err } return result.OneError() } // OpenPort sets the policy of the port with protocol and number to be // opened. // // TODO: We should really be opening and closing ports on machines, // rather than units. func (u *Unit) OpenPort(protocol string, number int) error { var result params.ErrorResults args := params.EntitiesPorts{ Entities: []params.EntityPort{ {Tag: u.tag, Protocol: protocol, Port: number}, }, } err := u.st.call("OpenPort", args, &result) if err != nil { return err } return result.OneError() } // ClosePort sets the policy of the port with protocol and number to // be closed. // // TODO: We should really be opening and closing ports on machines, // rather than units. func (u *Unit) ClosePort(protocol string, number int) error { var result params.ErrorResults args := params.EntitiesPorts{ Entities: []params.EntityPort{ {Tag: u.tag, Protocol: protocol, Port: number}, }, } err := u.st.call("ClosePort", args, &result) if err != nil { return err } return result.OneError() } var ErrNoCharmURLSet = errors.New("unit has no charm url set") // CharmURL returns the charm URL this unit is currently using. // // NOTE: This differs from state.Unit.CharmURL() by returning // an error instead of a bool, because it needs to make an API call. func (u *Unit) CharmURL() (*charm.URL, error) { var results params.StringBoolResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("CharmURL", 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 } if result.Result != "" { curl, err := charm.ParseURL(result.Result) if err != nil { return nil, err } return curl, nil } return nil, ErrNoCharmURLSet } // SetCharmURL marks the unit as currently using the supplied charm URL. // An error will be returned if the unit is dead, or the charm URL not known. func (u *Unit) SetCharmURL(curl *charm.URL) error { if curl == nil { return fmt.Errorf("charm URL cannot be nil") } var result params.ErrorResults args := params.EntitiesCharmURL{ Entities: []params.EntityCharmURL{ {Tag: u.tag, CharmURL: curl.String()}, }, } err := u.st.call("SetCharmURL", args, &result) if err != nil { return err } return result.OneError() } // ClearResolved removes any resolved setting on the unit. func (u *Unit) ClearResolved() error { var result params.ErrorResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("ClearResolved", args, &result) if err != nil { return err } return result.OneError() } // WatchConfigSettings returns a watcher for observing changes to the // unit's service configuration settings. The unit must have a charm URL // set before this method is called, and the returned watcher will be // valid only while the unit's charm URL is not changed. func (u *Unit) WatchConfigSettings() (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("WatchConfigSettings", 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 } 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 } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/uniter/environ_test.go�����������������������0000644�0000153�0000161�00000001615�12321735642�027135� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package uniter_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/uniter" ) type environSuite struct { uniterSuite apiEnviron *uniter.Environment stateEnviron *state.Environment } var _ = gc.Suite(&environSuite{}) func (s *environSuite) SetUpTest(c *gc.C) { s.uniterSuite.SetUpTest(c) var err error s.apiEnviron, err = s.uniter.Environment() c.Assert(err, gc.IsNil) s.stateEnviron, err = s.State.Environment() c.Assert(err, gc.IsNil) } func (s *environSuite) TearDownTest(c *gc.C) { s.uniterSuite.TearDownTest(c) } func (s *environSuite) TestUUID(c *gc.C) { c.Assert(s.apiEnviron.UUID(), gc.Equals, s.stateEnviron.UUID()) } func (s *environSuite) TestName(c *gc.C) { c.Assert(s.apiEnviron.Name(), gc.Equals, s.stateEnviron.Name()) } �������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/machiner/������������������������������������0000755�0000153�0000161�00000000000�12321736000�024331� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/machiner/machiner_test.go��������������������0000644�0000153�0000161�00000011177�12321735776�027537� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package machiner_test import ( stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/machiner" "launchpad.net/juju-core/state/api/params" apitesting "launchpad.net/juju-core/state/api/testing" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type machinerSuite struct { testing.JujuConnSuite *apitesting.APIAddresserTests st *api.State machine *state.Machine machiner *machiner.State } var _ = gc.Suite(&machinerSuite{}) func (s *machinerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) m, err := s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) err = m.SetAddresses(instance.NewAddresses([]string{"10.0.0.1"})) c.Assert(err, gc.IsNil) s.st, s.machine = s.OpenAPIAsNewMachine(c) // Create the machiner API facade. s.machiner = s.st.Machiner() c.Assert(s.machiner, gc.NotNil) s.APIAddresserTests = apitesting.NewAPIAddresserTests(s.machiner, s.BackingState) } func (s *machinerSuite) TestMachineAndMachineTag(c *gc.C) { machine, err := s.machiner.Machine("machine-42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(machine, gc.IsNil) machine, err = s.machiner.Machine("machine-1") c.Assert(err, gc.IsNil) c.Assert(machine.Tag(), gc.Equals, "machine-1") } func (s *machinerSuite) TestSetStatus(c *gc.C) { machine, err := s.machiner.Machine("machine-1") c.Assert(err, gc.IsNil) status, info, data, err := s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusPending) c.Assert(info, gc.Equals, "") c.Assert(data, gc.HasLen, 0) err = machine.SetStatus(params.StatusStarted, "blah", nil) c.Assert(err, gc.IsNil) status, info, data, err = s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStarted) c.Assert(info, gc.Equals, "blah") c.Assert(data, gc.HasLen, 0) } func (s *machinerSuite) TestEnsureDead(c *gc.C) { c.Assert(s.machine.Life(), gc.Equals, state.Alive) machine, err := s.machiner.Machine("machine-1") c.Assert(err, gc.IsNil) err = machine.EnsureDead() c.Assert(err, gc.IsNil) err = s.machine.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine.Life(), gc.Equals, state.Dead) err = machine.EnsureDead() c.Assert(err, gc.IsNil) err = s.machine.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine.Life(), gc.Equals, state.Dead) err = s.machine.Remove() c.Assert(err, gc.IsNil) err = s.machine.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = machine.EnsureDead() c.Assert(err, gc.ErrorMatches, "machine 1 not found") c.Assert(err, jc.Satisfies, params.IsCodeNotFound) } func (s *machinerSuite) TestRefresh(c *gc.C) { machine, err := s.machiner.Machine("machine-1") c.Assert(err, gc.IsNil) c.Assert(machine.Life(), gc.Equals, params.Alive) err = machine.EnsureDead() c.Assert(err, gc.IsNil) c.Assert(machine.Life(), gc.Equals, params.Alive) err = machine.Refresh() c.Assert(err, gc.IsNil) c.Assert(machine.Life(), gc.Equals, params.Dead) } func (s *machinerSuite) TestSetMachineAddresses(c *gc.C) { machine, err := s.machiner.Machine("machine-1") c.Assert(err, gc.IsNil) addr := s.machine.Addresses() c.Assert(addr, gc.HasLen, 0) addresses := []instance.Address{ instance.NewAddress("10.0.0.1"), instance.NewAddress("8.8.8.8"), } err = machine.SetMachineAddresses(addresses) c.Assert(err, gc.IsNil) err = s.machine.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine.MachineAddresses(), gc.DeepEquals, addresses) } func (s *machinerSuite) TestWatch(c *gc.C) { machine, err := s.machiner.Machine("machine-1") c.Assert(err, gc.IsNil) c.Assert(machine.Life(), gc.Equals, params.Alive) w, err := machine.Watch() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w) // Initial event. wc.AssertOneChange() // Change something other than the lifecycle and make sure it's // not detected. err = machine.SetStatus(params.StatusStarted, "not really", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Make the machine dead and check it's detected. err = machine.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/machiner/machiner.go�������������������������0000644�0000153�0000161�00000002321�12321735642�026457� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package machiner import ( "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/common" "launchpad.net/juju-core/state/api/params" ) const machinerFacade = "Machiner" // State provides access to the Machiner API facade. type State struct { caller base.Caller *common.APIAddresser } func (st *State) call(method string, params, result interface{}) error { return st.caller.Call(machinerFacade, "", method, params, result) } // NewState creates a new client-side Machiner facade. func NewState(caller base.Caller) *State { return &State{ caller: caller, APIAddresser: common.NewAPIAddresser(machinerFacade, caller), } } // machineLife requests the lifecycle of the given machine from the server. func (st *State) machineLife(tag string) (params.Life, error) { return common.Life(st.caller, machinerFacade, tag) } // Machine provides access to methods of a state.Machine through the facade. func (st *State) Machine(tag string) (*Machine, error) { life, err := st.machineLife(tag) if err != nil { return nil, err } return &Machine{ tag: tag, life: life, st: st, }, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/machiner/machine.go��������������������������0000644�0000153�0000161�00000005012�12321735642�026275� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package machiner import ( "fmt" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // Machine represents a juju machine as seen by a machiner worker. type Machine struct { tag string life params.Life st *State } // Tag returns the machine's tag. func (m *Machine) Tag() string { return m.tag } // Life returns the machine's lifecycle value. func (m *Machine) Life() params.Life { return m.life } // Refresh updates the cached local copy of the machine's data. func (m *Machine) Refresh() error { life, err := m.st.machineLife(m.tag) if err != nil { return err } m.life = life return nil } // SetStatus sets the status of the machine. func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error { var result params.ErrorResults args := params.SetStatus{ Entities: []params.EntityStatus{ {Tag: m.tag, Status: status, Info: info, Data: data}, }, } err := m.st.call("SetStatus", args, &result) if err != nil { return err } return result.OneError() } // SetMachineAddresses sets the machine determined addresses of the machine. func (m *Machine) SetMachineAddresses(addresses []instance.Address) error { var result params.ErrorResults args := params.SetMachinesAddresses{ MachineAddresses: []params.MachineAddresses{ {Tag: m.Tag(), Addresses: addresses}, }, } err := m.st.call("SetMachineAddresses", args, &result) if err != nil { return err } return result.OneError() } // EnsureDead sets the machine lifecycle to Dead if it is Alive or // Dying. It does nothing otherwise. func (m *Machine) EnsureDead() error { var result params.ErrorResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("EnsureDead", args, &result) if err != nil { return err } return result.OneError() } // Watch returns a watcher for observing changes to the machine. func (m *Machine) Watch() (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("Watch", 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 } w := watcher.NewNotifyWatcher(m.st.caller, result) return w, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/params/��������������������������������������0000755�0000153�0000161�00000000000�12321736000�024026� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/params/constants.go��������������������������0000644�0000153�0000161�00000004017�12321735642�026406� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package params // Life describes the lifecycle state of an entity ("alive", "dying" // or "dead"). type Life string const ( Alive Life = "alive" Dying Life = "dying" Dead Life = "dead" ) // MachineJob values define responsibilities that machines may be // expected to fulfil. type MachineJob string const ( JobHostUnits MachineJob = "JobHostUnits" JobManageEnviron MachineJob = "JobManageEnviron" // Deprecated in 1.18 JobManageStateDeprecated MachineJob = "JobManageState" ) // NeedsState returns true if the job requires a state connection. func (job MachineJob) NeedsState() bool { return job == JobManageEnviron } // ResolvedMode describes the way state transition errors // are resolved. type ResolvedMode string const ( ResolvedNone ResolvedMode = "" ResolvedRetryHooks ResolvedMode = "retry-hooks" ResolvedNoHooks ResolvedMode = "no-hooks" ) // Status represents the status of an entity. // It could be a unit, machine or its agent. type Status string const ( // The entity is not yet participating in the environment. StatusPending Status = "pending" // The unit has performed initial setup and is adapting itself to // the environment. Not applicable to machines. StatusInstalled Status = "installed" // The entity is actively participating in the environment. StatusStarted Status = "started" // The entity's agent will perform no further action, other than // to set the unit to Dead at a suitable moment. StatusStopped Status = "stopped" // The entity requires human intervention in order to operate // correctly. StatusError Status = "error" // The entity ought to be signalling activity, but it cannot be // detected. StatusDown Status = "down" ) // Valid returns true if status has a known value. func (status Status) Valid() bool { switch status { case StatusPending, StatusInstalled, StatusStarted, StatusStopped, StatusError, StatusDown: default: return false } return true } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/params/internal.go���������������������������0000644�0000153�0000161�00000033051�12321735776�026216� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package params import ( "time" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils/exec" "launchpad.net/juju-core/version" ) // MachineContainersParams holds the arguments for making a SetSupportedContainers // API call. type MachineContainersParams struct { Params []MachineContainers } // MachineContainers holds the arguments for making an SetSupportedContainers call // on a given machine. type MachineContainers struct { MachineTag string ContainerTypes []instance.ContainerType } // WatchContainer identifies a single container type within a machine. type WatchContainer struct { MachineTag string ContainerType string } // WatchContainers holds the arguments for making a WatchContainers // API call. type WatchContainers struct { Params []WatchContainer } // CharmURL identifies a single charm URL. type CharmURL struct { URL string } // CharmURLs identifies multiple charm URLs. type CharmURLs struct { URLs []CharmURL } // StringsResult holds the result of an API call that returns a slice // of strings or an error. type StringsResult struct { Error *Error Result []string } // PortsResults holds the bulk operation result of an API call // that returns a slice of instance.Port. type PortsResults struct { Results []PortsResult } // PortsResult holds the result of an API call that returns a slice // of instance.Port or an error. type PortsResult struct { Error *Error Ports []instance.Port } // StringsResults holds the bulk operation result of an API call // that returns a slice of strings or an error. type StringsResults struct { Results []StringsResult } // StringResult holds a string or an error. type StringResult struct { Error *Error Result string } // StringResults holds the bulk operation result of an API call // that returns a string or an error. type StringResults struct { Results []StringResult } // CharmArchiveURLResult holds a charm archive (bundle) URL, a // DisableSSLHostnameVerification flag or an error. type CharmArchiveURLResult struct { Error *Error Result string DisableSSLHostnameVerification bool } // CharmArchiveURLResults holds the bulk operation result of an API // call that returns a charm archive (bundle) URL, a // DisableSSLHostnameVerification flag or an error. type CharmArchiveURLResults struct { Results []CharmArchiveURLResult } // EnvironmentResult holds the result of an API call returning a name and UUID // for an environment. type EnvironmentResult struct { Error *Error Name string UUID string } // ResolvedModeResult holds a resolved mode or an error. type ResolvedModeResult struct { Error *Error Mode ResolvedMode } // ResolvedModeResults holds the bulk operation result of an API call // that returns a resolved mode or an error. type ResolvedModeResults struct { Results []ResolvedModeResult } // StringBoolResult holds the result of an API call that returns a // string and a boolean. type StringBoolResult struct { Error *Error Result string Ok bool } // StringBoolResults holds multiple results with a string and a bool // each. type StringBoolResults struct { Results []StringBoolResult } // BoolResult holds the result of an API call that returns a // a boolean or an error. type BoolResult struct { Error *Error Result bool } // BoolResults holds multiple results with BoolResult each. type BoolResults struct { Results []BoolResult } // RelationSettings holds relation settings names and values. type RelationSettings map[string]string // RelationSettingsResult holds a relation settings map or an error. type RelationSettingsResult struct { Error *Error Settings RelationSettings } // RelationSettingsResults holds the result of an API calls that // returns settings for multiple relations. type RelationSettingsResults struct { Results []RelationSettingsResult } // ConfigSettings holds unit, service or cham configuration settings // with string keys and arbitrary values. type ConfigSettings map[string]interface{} // ConfigSettingsResult holds a configuration map or an error. type ConfigSettingsResult struct { Error *Error Settings ConfigSettings } // ConfigSettingsResults holds multiple configuration maps or errors. type ConfigSettingsResults struct { Results []ConfigSettingsResult } // EnvironConfig holds an environment configuration. type EnvironConfig map[string]interface{} // EnvironConfigResult holds environment configuration or an error. type EnvironConfigResult struct { Config EnvironConfig } // RelationUnit holds a relation and a unit tag. type RelationUnit struct { Relation string Unit string } // RelationUnits holds the parameters for API calls expecting a pair // of relation and unit tags. type RelationUnits struct { RelationUnits []RelationUnit } // RelationIds holds multiple relation ids. type RelationIds struct { RelationIds []int } // RelationUnitPair holds a relation tag, a local and remote unit tags. type RelationUnitPair struct { Relation string LocalUnit string RemoteUnit string } // RelationUnitPairs holds the parameters for API calls expecting // multiple sets of a relation tag, a local and remote unit tags. type RelationUnitPairs struct { RelationUnitPairs []RelationUnitPair } // RelationUnitSettings holds a relation tag, a unit tag and local // unit settings. type RelationUnitSettings struct { Relation string Unit string Settings RelationSettings } // RelationUnitsSettings holds the arguments for making a EnterScope // or WriteSettings API calls. type RelationUnitsSettings struct { RelationUnits []RelationUnitSettings } // RelationResult returns information about a single relation, // or an error. type RelationResult struct { Error *Error Life Life Id int Key string Endpoint Endpoint } // RelationResults holds the result of an API call that returns // information about multiple relations. type RelationResults struct { Results []RelationResult } // EntityPort holds an entity's tag, a protocol and a port. type EntityPort struct { Tag string Protocol string Port int } // EntitiesPorts holds the parameters for making an OpenPort or // ClosePort on some entities. type EntitiesPorts struct { Entities []EntityPort } // EntityCharmURL holds an entity's tag and a charm URL. type EntityCharmURL struct { Tag string CharmURL string } // EntitiesCharmURL holds the parameters for making a SetCharmURL API // call. type EntitiesCharmURL struct { Entities []EntityCharmURL } // BytesResult holds the result of an API call that returns a slice // of bytes. type BytesResult struct { Result []byte } // LifeResult holds the life status of a single entity, or an error // indicating why it is not available. type LifeResult struct { Life Life Error *Error } // LifeResults holds the life or error status of multiple entities. type LifeResults struct { Results []LifeResult } // SetEntityAddress holds an entity tag and an address. type SetEntityAddress struct { Tag string Address string } // SetEntityAddresses holds the parameters for making a Set*Address // call, where the address can be a public or a private one. type SetEntityAddresses struct { Entities []SetEntityAddress } // MachineSetProvisioned holds a machine tag, provider-specific instance id, // a nonce, or an error. type MachineSetProvisioned struct { Tag string InstanceId instance.Id Nonce string Characteristics *instance.HardwareCharacteristics } // SetProvisioned holds the parameters for making a SetProvisioned // call for a machine. type SetProvisioned struct { Machines []MachineSetProvisioned } // EntityStatus holds an entity tag, status and extra info. type EntityStatus struct { Tag string Status Status Info string Data StatusData } // SetStatus holds the parameters for making a SetStatus/UpdateStatus call. type SetStatus struct { Entities []EntityStatus } // StatusResult holds an entity status, extra information, or an // error. type StatusResult struct { Error *Error Id string Life Life Status Status Info string Data StatusData } // StatusResults holds multiple status results. type StatusResults struct { Results []StatusResult } // MachineAddresses holds an machine tag and addresses. type MachineAddresses struct { Tag string Addresses []instance.Address } // SetMachinesAddresses holds the parameters for making a SetMachineAddresses call. type SetMachinesAddresses struct { MachineAddresses []MachineAddresses } // ConstraintsResult holds machine constraints or an error. type ConstraintsResult struct { Error *Error Constraints constraints.Value } // ConstraintsResults holds multiple constraints results. type ConstraintsResults struct { Results []ConstraintsResult } // AgentGetEntitiesResults holds the results of a // agent.API.GetEntities call. type AgentGetEntitiesResults struct { Entities []AgentGetEntitiesResult } // AgentGetEntitiesResult holds the results of a // machineagent.API.GetEntities call for a single entity. type AgentGetEntitiesResult struct { Life Life Jobs []MachineJob ContainerType instance.ContainerType Error *Error } // VersionResult holds the version and possibly error for a given // DesiredVersion() API call. type VersionResult struct { Version *version.Number Error *Error } // VersionResults is a list of versions for the requested entities. type VersionResults struct { Results []VersionResult } // ToolsResult holds the tools and possibly error for a given // Tools() API call. type ToolsResult struct { Tools *tools.Tools DisableSSLHostnameVerification bool Error *Error } // ToolsResults is a list of tools for various requested agents. type ToolsResults struct { Results []ToolsResult } // FindToolsParams defines parameters for the FindTools method. type FindToolsParams struct { MajorVersion int MinorVersion int Arch string Series string } // FindToolsResults holds a list of tools from FindTools and any error. type FindToolsResults struct { List tools.List Error *Error } // Version holds a specific binary version. type Version struct { Version version.Binary } // EntityVersion specifies the tools version to be set for an entity // with the given tag. // version.Binary directly. type EntityVersion struct { Tag string Tools *Version } // EntitiesVersion specifies what tools are being run for // multiple entities. type EntitiesVersion struct { AgentTools []EntityVersion } // NotifyWatchResult holds a NotifyWatcher id and an error (if any). type NotifyWatchResult struct { NotifyWatcherId string Error *Error } // NotifyWatchResults holds the results for any API call which ends up // returning a list of NotifyWatchers type NotifyWatchResults struct { Results []NotifyWatchResult } // StringsWatchResult holds a StringsWatcher id, changes and an error // (if any). type StringsWatchResult struct { StringsWatcherId string Changes []string Error *Error } // StringsWatchResults holds the results for any API call which ends up // returning a list of StringsWatchers. type StringsWatchResults struct { Results []StringsWatchResult } // UnitSettings holds information about a service unit's settings // within a relation. type UnitSettings struct { Version int64 } // RelationUnitsChange holds notifications of units entering and leaving the // scope of a RelationUnit, and changes to the settings of those units known // to have entered. // // When remote units first enter scope and then when their settings // change, the changes will be noted in the Changed field, which holds // the unit settings for every such unit, indexed by the unit id. // // When remote units leave scope, their ids will be noted in the // Departed field, and no further events will be sent for those units. type RelationUnitsChange struct { Changed map[string]UnitSettings Departed []string } // RelationUnitsWatchResult holds a RelationUnitsWatcher id, changes // and an error (if any). type RelationUnitsWatchResult struct { RelationUnitsWatcherId string Changes RelationUnitsChange Error *Error } // RelationUnitsWatchResults holds the results for any API call which ends up // returning a list of RelationUnitsWatchers. type RelationUnitsWatchResults struct { Results []RelationUnitsWatchResult } // CharmsResponse is the server response to charm upload or GET requests. type CharmsResponse struct { Error string `json:",omitempty"` CharmURL string `json:",omitempty"` Files []string `json:",omitempty"` } // RunParams is used to provide the parameters to the Run method. // Commands and Timeout are expected to have values, and one or more // values should be in the Machines, Services, or Units slices. type RunParams struct { Commands string Timeout time.Duration Machines []string Services []string Units []string } // RunResult contains the result from an individual run call on a machine. // UnitId is populated if the command was run inside the unit context. type RunResult struct { exec.ExecResponse MachineId string UnitId string Error string } // RunResults is used to return the slice of results. API server side calls // need to return single structure values. type RunResults struct { Results []RunResult } // APIHostPortsResult holds the result of an APIHostPorts // call. Each element in the top level slice holds // the addresses for one API server. type APIHostPortsResult struct { Servers [][]instance.HostPort } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/params/params_test.go������������������������0000644�0000153�0000161�00000013416�12321735642�026717� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package params_test import ( "encoding/json" "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/params" ) // TestPackage integrates the tests into gotest. func TestPackage(t *testing.T) { gc.TestingT(t) } type MarshalSuite struct{} var _ = gc.Suite(&MarshalSuite{}) var marshalTestCases = []struct { about string // Value holds a real Go struct. value params.Delta // JSON document. json string }{{ about: "MachineInfo Delta", value: params.Delta{ Entity: &params.MachineInfo{ Id: "Benji", InstanceId: "Shazam", Status: "error", StatusInfo: "foo", Life: params.Alive, Series: "trusty", SupportedContainers: []instance.ContainerType{instance.LXC}, Jobs: []params.MachineJob{state.JobManageEnviron.ToParams()}, Addresses: []instance.Address{}, HardwareCharacteristics: &instance.HardwareCharacteristics{}, }, }, json: `["machine","change",{"Id":"Benji","InstanceId":"Shazam","Status":"error","StatusInfo":"foo","StatusData":null,"Life":"alive","Series":"trusty","SupportedContainers":["lxc"],"SupportedContainersKnown":false,"Jobs":["JobManageEnviron"],"Addresses":[],"HardwareCharacteristics":{}}]`, }, { about: "ServiceInfo Delta", value: params.Delta{ Entity: &params.ServiceInfo{ Name: "Benji", Exposed: true, CharmURL: "cs:quantal/name", Life: params.Dying, OwnerTag: "test-owner", MinUnits: 42, Constraints: constraints.MustParse("arch=armhf mem=1024M"), Config: charm.Settings{ "hello": "goodbye", "foo": false, }, }, }, json: `["service","change",{"CharmURL": "cs:quantal/name","Name":"Benji","Exposed":true,"Life":"dying","OwnerTag":"test-owner","MinUnits":42,"Constraints":{"arch":"armhf", "mem": 1024},"Config": {"hello":"goodbye","foo":false}}]`, }, { about: "UnitInfo Delta", value: params.Delta{ Entity: &params.UnitInfo{ Name: "Benji", Service: "Shazam", Series: "precise", CharmURL: "cs:~user/precise/wordpress-42", Ports: []instance.Port{ { Protocol: "http", Number: 80}, }, PublicAddress: "testing.invalid", PrivateAddress: "10.0.0.1", MachineId: "1", Status: "error", StatusInfo: "foo", }, }, json: `["unit", "change", {"CharmURL": "cs:~user/precise/wordpress-42", "MachineId": "1", "Series": "precise", "Name": "Benji", "PublicAddress": "testing.invalid", "Service": "Shazam", "PrivateAddress": "10.0.0.1", "Ports": [{"Protocol": "http", "Number": 80}], "Status": "error", "StatusInfo": "foo","StatusData":null}]`, }, { about: "RelationInfo Delta", value: params.Delta{ Entity: &params.RelationInfo{ Key: "Benji", Id: 4711, Endpoints: []params.Endpoint{ {ServiceName: "logging", Relation: charm.Relation{Name: "logging-directory", Role: "requirer", Interface: "logging", Optional: false, Limit: 1, Scope: "container"}}, {ServiceName: "wordpress", Relation: charm.Relation{Name: "logging-dir", Role: "provider", Interface: "logging", Optional: false, Limit: 0, Scope: "container"}}}, }, }, json: `["relation","change",{"Key":"Benji", "Id": 4711, "Endpoints": [{"ServiceName":"logging", "Relation":{"Name":"logging-directory", "Role":"requirer", "Interface":"logging", "Optional":false, "Limit":1, "Scope":"container"}}, {"ServiceName":"wordpress", "Relation":{"Name":"logging-dir", "Role":"provider", "Interface":"logging", "Optional":false, "Limit":0, "Scope":"container"}}]}]`, }, { about: "AnnotationInfo Delta", value: params.Delta{ Entity: &params.AnnotationInfo{ Tag: "machine-0", Annotations: map[string]string{ "foo": "bar", "arble": "2 4", }, }, }, json: `["annotation","change",{"Tag":"machine-0","Annotations":{"foo":"bar","arble":"2 4"}}]`, }, { about: "Delta Removed True", value: params.Delta{ Removed: true, Entity: &params.RelationInfo{ Key: "Benji", }, }, json: `["relation","remove",{"Key":"Benji", "Id": 0, "Endpoints": null}]`, }} func (s *MarshalSuite) TestDeltaMarshalJSON(c *gc.C) { for _, t := range marshalTestCases { c.Log(t.about) output, err := t.value.MarshalJSON() c.Check(err, gc.IsNil) // We check unmarshalled output both to reduce the fragility of the // tests (because ordering in the maps can change) and to verify that // the output is well-formed. var unmarshalledOutput interface{} err = json.Unmarshal(output, &unmarshalledOutput) c.Check(err, gc.IsNil) var expected interface{} err = json.Unmarshal([]byte(t.json), &expected) c.Check(err, gc.IsNil) c.Check(unmarshalledOutput, gc.DeepEquals, expected) } } func (s *MarshalSuite) TestDeltaUnmarshalJSON(c *gc.C) { for i, t := range marshalTestCases { c.Logf("test %d. %s", i, t.about) var unmarshalled params.Delta err := json.Unmarshal([]byte(t.json), &unmarshalled) c.Check(err, gc.IsNil) c.Check(unmarshalled, gc.DeepEquals, t.value) } } func (s *MarshalSuite) TestDeltaMarshalJSONCardinality(c *gc.C) { err := json.Unmarshal([]byte(`[1,2]`), new(params.Delta)) c.Check(err, gc.ErrorMatches, "Expected 3 elements in top-level of JSON but got 2") } func (s *MarshalSuite) TestDeltaMarshalJSONUnknownOperation(c *gc.C) { err := json.Unmarshal([]byte(`["relation","masticate",{}]`), new(params.Delta)) c.Check(err, gc.ErrorMatches, `Unexpected operation "masticate"`) } func (s *MarshalSuite) TestDeltaMarshalJSONUnknownEntity(c *gc.C) { err := json.Unmarshal([]byte(`["qwan","change",{}]`), new(params.Delta)) c.Check(err, gc.ErrorMatches, `Unexpected entity name "qwan"`) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/params/apierror.go���������������������������0000644�0000153�0000161�00000006677�12321735776�026243� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package params import ( "fmt" "launchpad.net/juju-core/rpc" ) // Error is the type of error returned by any call to the state API type Error struct { Message string Code string } func (e *Error) Error() string { return e.Message } func (e *Error) ErrorCode() string { return e.Code } var _ rpc.ErrorCoder = (*Error)(nil) // GoString implements fmt.GoStringer. It means that a *Error shows its // contents correctly when printed with %#v. func (e Error) GoString() string { return fmt.Sprintf("&params.Error{%q, %q}", e.Code, e.Message) } // The Code constants hold error codes for some kinds of error. const ( CodeNotFound = "not found" CodeUnauthorized = "unauthorized access" CodeCannotEnterScope = "cannot enter scope" CodeCannotEnterScopeYet = "cannot enter scope yet" CodeExcessiveContention = "excessive contention" CodeUnitHasSubordinates = "unit has subordinates" CodeNotAssigned = "not assigned" CodeStopped = "stopped" CodeHasAssignedUnits = "machine has assigned units" CodeNotProvisioned = "not provisioned" CodeNoAddressSet = "no address set" CodeNotImplemented = rpc.CodeNotImplemented ) // ErrCode returns the error code associated with // the given error, or the empty string if there // is none. func ErrCode(err error) string { if err, _ := err.(rpc.ErrorCoder); err != nil { return err.ErrorCode() } return "" } // clientError maps errors returned from an RPC call into local errors with // appropriate values. func ClientError(err error) error { rerr, ok := err.(*rpc.RequestError) if !ok { return err } // We use our own error type rather than rpc.ServerError // because we don't want the code or the "server error" prefix // within the error message. Also, it's best not to make clients // know that we're using the rpc package. return &Error{ Message: rerr.Message, Code: rerr.Code, } } func IsCodeNotFound(err error) bool { return ErrCode(err) == CodeNotFound } func IsCodeUnauthorized(err error) bool { return ErrCode(err) == CodeUnauthorized } // IsCodeNotFoundOrCodeUnauthorized is used in API clients which, pre-API, used // IsNotFoundErr; this is because an API client is not necessarily privileged to // know about the existence or otherwise of a particular entity, and the server // may hence convert NotFound to Unauthorized at its discretion. func IsCodeNotFoundOrCodeUnauthorized(err error) bool { return IsCodeNotFound(err) || IsCodeUnauthorized(err) } func IsCodeCannotEnterScope(err error) bool { return ErrCode(err) == CodeCannotEnterScope } func IsCodeCannotEnterScopeYet(err error) bool { return ErrCode(err) == CodeCannotEnterScopeYet } func IsCodeExcessiveContention(err error) bool { return ErrCode(err) == CodeExcessiveContention } func IsCodeUnitHasSubordinates(err error) bool { return ErrCode(err) == CodeUnitHasSubordinates } func IsCodeNotAssigned(err error) bool { return ErrCode(err) == CodeNotAssigned } func IsCodeStopped(err error) bool { return ErrCode(err) == CodeStopped } func IsCodeHasAssignedUnits(err error) bool { return ErrCode(err) == CodeHasAssignedUnits } func IsCodeNotProvisioned(err error) bool { return ErrCode(err) == CodeNotProvisioned } func IsCodeNoAddressSet(err error) bool { return ErrCode(err) == CodeNoAddressSet } func IsCodeNotImplemented(err error) bool { return ErrCode(err) == CodeNotImplemented } �����������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/params/params.go�����������������������������0000644�0000153�0000161�00000040663�12321735776�025674� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package params import ( "bytes" "encoding/json" "fmt" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/utils/ssh" "launchpad.net/juju-core/version" ) // Entity identifies a single entity. type Entity struct { Tag string } // Entities identifies multiple entities. type Entities struct { Entities []Entity } // EntityPasswords holds the parameters for making a SetPasswords call. type EntityPasswords struct { Changes []EntityPassword } // EntityPassword specifies a password change for the entity // with the given tag. type EntityPassword struct { Tag string Password string } // ErrorResults holds the results of calling a bulk operation which // returns no data, only an error result. The order and // number of elements matches the operations specified in the request. type ErrorResults struct { // Results contains the error results from each operation. Results []ErrorResult } // OneError returns the error from the result // of a bulk operation on a single value. func (result ErrorResults) OneError() error { if n := len(result.Results); n != 1 { return fmt.Errorf("expected 1 result, got %d", n) } if err := result.Results[0].Error; err != nil { return err } return nil } // ErrorResult holds the error status of a single operation. type ErrorResult struct { Error *Error } // StatusData contains additional information for a status. type StatusData map[string]interface{} // AddRelation holds the parameters for making the AddRelation call. // The endpoints specified are unordered. type AddRelation struct { Endpoints []string } // AddRelationResults holds the results of a AddRelation call. The Endpoints // field maps service names to the involved endpoints. type AddRelationResults struct { Endpoints map[string]charm.Relation } // DestroyRelation holds the parameters for making the DestroyRelation call. // The endpoints specified are unordered. type DestroyRelation struct { Endpoints []string } // AddMachineParams encapsulates the parameters used to create a new machine. type AddMachineParams struct { // The following fields hold attributes that will be given to the // new machine when it is created. Series string Constraints constraints.Value Jobs []MachineJob // If ParentId is non-empty, it specifies the id of the // parent machine within which the new machine will // be created. In that case, ContainerType must also be // set. ParentId string // ContainerType optionally gives the container type of the // new machine. If it is non-empty, the new machine // will be implemented by a container. If it is specified // but ParentId is empty, a new top level machine will // be created to hold the container with given series, // constraints and jobs. ContainerType instance.ContainerType // If InstanceId is non-empty, it will be associated with // the new machine along with the given nonce, // hardware characteristics and addresses. // All the following fields will be ignored if ContainerType // is set. InstanceId instance.Id Nonce string HardwareCharacteristics instance.HardwareCharacteristics Addrs []instance.Address } // AddMachines holds the parameters for making the AddMachines call. type AddMachines struct { MachineParams []AddMachineParams } // AddMachinesResults holds the results of an AddMachines call. type AddMachinesResults struct { Machines []AddMachinesResult } // AddMachinesResults holds the name of a machine added by the // state.api.client.AddMachine call for a single machine. type AddMachinesResult struct { Machine string Error *Error } // DestroyMachines holds parameters for the DestroyMachines call. type DestroyMachines struct { MachineNames []string Force bool } // ServiceDeploy holds the parameters for making the ServiceDeploy call. type ServiceDeploy struct { ServiceName string CharmUrl string NumUnits int Config map[string]string ConfigYAML string // Takes precedence over config if both are present. Constraints constraints.Value ToMachineSpec string // The following fields are supported from 1.17.7 onwards and // ignored before that. IncludeNetworks []string ExcludeNetworks []string } // ServiceUpdate holds the parameters for making the ServiceUpdate call. type ServiceUpdate struct { ServiceName string CharmUrl string ForceCharmUrl bool MinUnits *int SettingsStrings map[string]string SettingsYAML string // Takes precedence over SettingsStrings if both are present. Constraints *constraints.Value } // ServiceSetCharm sets the charm for a given service. type ServiceSetCharm struct { ServiceName string CharmUrl string Force bool } // ServiceExpose holds the parameters for making the ServiceExpose call. type ServiceExpose struct { ServiceName string } // ServiceSet holds the parameters for a ServiceSet // command. Options contains the configuration data. type ServiceSet struct { ServiceName string Options map[string]string } // ServiceSetYAML holds the parameters for // a ServiceSetYAML command. Config contains the // configuration data in YAML format. type ServiceSetYAML struct { ServiceName string Config string } // ServiceUnset holds the parameters for a ServiceUnset // command. Options contains the option attribute names // to unset. type ServiceUnset struct { ServiceName string Options []string } // ServiceGet holds parameters for making the ServiceGet or // ServiceGetCharmURL calls. type ServiceGet struct { ServiceName string } // ServiceGetResults holds results of the ServiceGet call. type ServiceGetResults struct { Service string Charm string Config map[string]interface{} Constraints constraints.Value } // ServiceCharmRelations holds parameters for making the ServiceCharmRelations call. type ServiceCharmRelations struct { ServiceName string } // ServiceCharmRelationsResults holds the results of the ServiceCharmRelations call. type ServiceCharmRelationsResults struct { CharmRelations []string } // ServiceUnexpose holds parameters for the ServiceUnexpose call. type ServiceUnexpose struct { ServiceName string } // PublicAddress holds parameters for the PublicAddress call. type PublicAddress struct { Target string } // PublicAddressResults holds results of the PublicAddress call. type PublicAddressResults struct { PublicAddress string } // PrivateAddress holds parameters for the PrivateAddress call. type PrivateAddress struct { Target string } // PrivateAddressResults holds results of the PrivateAddress call. type PrivateAddressResults struct { PrivateAddress string } // Resolved holds parameters for the Resolved call. type Resolved struct { UnitName string Retry bool } // ResolvedResults holds results of the Resolved call. type ResolvedResults struct { Service string Charm string Settings map[string]interface{} } // AddServiceUnitsResults holds the names of the units added by the // AddServiceUnits call. type AddServiceUnitsResults struct { Units []string } // AddServiceUnits holds parameters for the AddUnits call. type AddServiceUnits struct { ServiceName string NumUnits int ToMachineSpec string } // DestroyServiceUnits holds parameters for the DestroyUnits call. type DestroyServiceUnits struct { UnitNames []string } // ServiceDestroy holds the parameters for making the ServiceDestroy call. type ServiceDestroy struct { ServiceName string } // Creds holds credentials for identifying an entity. type Creds struct { AuthTag string Password string Nonce string } // GetAnnotationsResults holds annotations associated with an entity. type GetAnnotationsResults struct { Annotations map[string]string } // GetAnnotations stores parameters for making the GetAnnotations call. type GetAnnotations struct { Tag string } // SetAnnotations stores parameters for making the SetAnnotations call. type SetAnnotations struct { Tag string Pairs map[string]string } // GetServiceConstraints stores parameters for making the GetServiceConstraints call. type GetServiceConstraints struct { ServiceName string } // GetConstraintsResults holds results of the GetConstraints call. type GetConstraintsResults struct { Constraints constraints.Value } // SetConstraints stores parameters for making the SetConstraints call. type SetConstraints struct { ServiceName string //optional, if empty, environment constraints are set. Constraints constraints.Value } // CharmInfo stores parameters for a CharmInfo call. type CharmInfo struct { CharmURL string } // ResolveCharms stores charm references for a ResolveCharms call. type ResolveCharms struct { References []charm.Reference } // ResolveCharmResult holds the result of resolving a charm reference to a URL, or any error that occurred. type ResolveCharmResult struct { URL *charm.URL `json:",omitempty"` Error string `json:",omitempty"` } // ResolveCharmResults holds results of the ResolveCharms call. type ResolveCharmResults struct { URLs []ResolveCharmResult } // AllWatcherId holds the id of an AllWatcher. type AllWatcherId struct { AllWatcherId string } // AllWatcherNextResults holds deltas returned from calling AllWatcher.Next(). type AllWatcherNextResults struct { Deltas []Delta } // Delta holds details of a change to the environment. type Delta struct { // If Removed is true, the entity has been removed; // otherwise it has been created or changed. Removed bool // Entity holds data about the entity that has changed. Entity EntityInfo } // ListSSHKeys stores parameters used for a KeyManager.ListKeys call. type ListSSHKeys struct { Entities Mode ssh.ListMode } // ModifySSHKeys stores parameters used for a KeyManager.Add|Delete|Import call for a user. type ModifyUserSSHKeys struct { User string Keys []string } // MarshalJSON implements json.Marshaler. func (d *Delta) MarshalJSON() ([]byte, error) { b, err := json.Marshal(d.Entity) if err != nil { return nil, err } var buf bytes.Buffer buf.WriteByte('[') c := "change" if d.Removed { c = "remove" } fmt.Fprintf(&buf, "%q,%q,", d.Entity.EntityId().Kind, c) buf.Write(b) buf.WriteByte(']') return buf.Bytes(), nil } // UnmarshalJSON implements json.Unmarshaler. func (d *Delta) UnmarshalJSON(data []byte) error { var elements []json.RawMessage if err := json.Unmarshal(data, &elements); err != nil { return err } if len(elements) != 3 { return fmt.Errorf( "Expected 3 elements in top-level of JSON but got %d", len(elements)) } var entityKind, operation string if err := json.Unmarshal(elements[0], &entityKind); err != nil { return err } if err := json.Unmarshal(elements[1], &operation); err != nil { return err } if operation == "remove" { d.Removed = true } else if operation != "change" { return fmt.Errorf("Unexpected operation %q", operation) } switch entityKind { case "machine": d.Entity = new(MachineInfo) case "service": d.Entity = new(ServiceInfo) case "unit": d.Entity = new(UnitInfo) case "relation": d.Entity = new(RelationInfo) case "annotation": d.Entity = new(AnnotationInfo) default: return fmt.Errorf("Unexpected entity name %q", entityKind) } if err := json.Unmarshal(elements[2], &d.Entity); err != nil { return err } return nil } // EntityInfo is implemented by all entity Info types. type EntityInfo interface { // EntityId returns an identifier that will uniquely // identify the entity within its kind EntityId() EntityId } // IMPORTANT NOTE: the types below are direct subsets of the entity docs // held in mongo, as defined in the state package (serviceDoc, // machineDoc etc). // In particular, the document marshalled into mongo // must unmarshal correctly into these documents. // If the format of a field in a document is changed in mongo, or // a field is removed and it coincides with one of the // fields below, a similar change must be made here. // // MachineInfo corresponds with state.machineDoc. // ServiceInfo corresponds with state.serviceDoc. // UnitInfo corresponds with state.unitDoc. // RelationInfo corresponds with state.relationDoc. // AnnotationInfo corresponds with state.annotatorDoc. var ( _ EntityInfo = (*MachineInfo)(nil) _ EntityInfo = (*ServiceInfo)(nil) _ EntityInfo = (*UnitInfo)(nil) _ EntityInfo = (*RelationInfo)(nil) _ EntityInfo = (*AnnotationInfo)(nil) ) type EntityId struct { Kind string Id interface{} } // StateServingInfo holds information needed by a state // server. type StateServingInfo struct { APIPort int StatePort int Cert string PrivateKey string // this will be passed as the KeyFile argument to MongoDB SharedSecret string } // MachineInfo holds the information about a Machine // that is watched by StateWatcher. type MachineInfo struct { Id string `bson:"_id"` InstanceId string Status Status StatusInfo string StatusData StatusData Life Life Series string SupportedContainers []instance.ContainerType SupportedContainersKnown bool HardwareCharacteristics *instance.HardwareCharacteristics `json:",omitempty"` Jobs []MachineJob Addresses []instance.Address } func (i *MachineInfo) EntityId() EntityId { return EntityId{ Kind: "machine", Id: i.Id, } } type ServiceInfo struct { Name string `bson:"_id"` Exposed bool CharmURL string OwnerTag string Life Life MinUnits int Constraints constraints.Value Config map[string]interface{} } func (i *ServiceInfo) EntityId() EntityId { return EntityId{ Kind: "service", Id: i.Name, } } type UnitInfo struct { Name string `bson:"_id"` Service string Series string CharmURL string PublicAddress string PrivateAddress string MachineId string Ports []instance.Port Status Status StatusInfo string StatusData StatusData } func (i *UnitInfo) EntityId() EntityId { return EntityId{ Kind: "unit", Id: i.Name, } } type Endpoint struct { ServiceName string Relation charm.Relation } type RelationInfo struct { Key string `bson:"_id"` Id int Endpoints []Endpoint } func (i *RelationInfo) EntityId() EntityId { return EntityId{ Kind: "relation", Id: i.Key, } } type AnnotationInfo struct { Tag string Annotations map[string]string } func (i *AnnotationInfo) EntityId() EntityId { return EntityId{ Kind: "annotation", Id: i.Tag, } } // ContainerConfig contains information from the environment config that is // needed for container cloud-init. type ContainerConfig struct { ProviderType string AuthorizedKeys string SSLHostnameVerification bool Proxy osenv.ProxySettings AptProxy osenv.ProxySettings } // ProvisioningScriptParams contains the parameters for the // ProvisioningScript client API call. type ProvisioningScriptParams struct { MachineId string Nonce string // DataDir may be "", in which case the default will be used. DataDir string // DisablePackageCommands may be set to disable all package-related // commands. It is then the responsibility of the provisioner to // ensure that all the packages required by Juju are available. DisablePackageCommands bool } // ProvisioningScriptResult contains the result of the // ProvisioningScript client API call. type ProvisioningScriptResult struct { Script string } // EnvironmentGetResults contains the result of EnvironmentGet client // API call. type EnvironmentGetResults struct { Config map[string]interface{} } // EnvironmentSet contains the arguments for EnvironmentSet client API // call. type EnvironmentSet struct { Config map[string]interface{} } // EnvironmentUnset contains the arguments for EnvironmentUnset client API // call. type EnvironmentUnset struct { Keys []string } // SetEnvironAgentVersion contains the arguments for // SetEnvironAgentVersion client API call. type SetEnvironAgentVersion struct { Version version.Number } // DeployerConnectionValues containers the result of deployer.ConnectionInfo // API call. type DeployerConnectionValues struct { StateAddresses []string APIAddresses []string } // StatusParams holds parameters for the Status call. type StatusParams struct { Patterns []string } // SetRsyslogCertParams holds parameters for the SetRsyslogCert call. type SetRsyslogCertParams struct { CACert []byte } �����������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/client_test.go�������������������������������0000644�0000153�0000161�00000005010�12321735776�025426� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package api_test import ( "fmt" "net/http" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/charm" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" ) type clientSuite struct { jujutesting.JujuConnSuite } var _ = gc.Suite(&clientSuite{}) // TODO(jam) 2013-08-27 http://pad.lv/1217282 // Right now most of the direct tests for api.Client behavior are in // state/apiserver/client/*_test.go func (s *clientSuite) TestCloseMultipleOk(c *gc.C) { client := s.APIState.Client() c.Assert(client.Close(), gc.IsNil) c.Assert(client.Close(), gc.IsNil) c.Assert(client.Close(), gc.IsNil) } func (s *clientSuite) TestAddLocalCharm(c *gc.C) { charmArchive := testing.Charms.Bundle(c.MkDir(), "dummy") curl := charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", charmArchive.Meta().Name, charmArchive.Revision()), ) client := s.APIState.Client() // Test the sanity checks first. _, err := client.AddLocalCharm(charm.MustParseURL("cs:quantal/wordpress-1"), nil) c.Assert(err, gc.ErrorMatches, `expected charm URL with local: schema, got "cs:quantal/wordpress-1"`) // Upload an archive with its original revision. savedURL, err := client.AddLocalCharm(curl, charmArchive) c.Assert(err, gc.IsNil) c.Assert(savedURL.String(), gc.Equals, curl.String()) // Upload a charm directory with changed revision. charmDir := testing.Charms.ClonedDir(c.MkDir(), "dummy") charmDir.SetDiskRevision(42) savedURL, err = client.AddLocalCharm(curl, charmDir) c.Assert(err, gc.IsNil) c.Assert(savedURL.Revision, gc.Equals, 42) // Upload a charm directory again, revision should be bumped. savedURL, err = client.AddLocalCharm(curl, charmDir) c.Assert(err, gc.IsNil) c.Assert(savedURL.String(), gc.Equals, curl.WithRevision(43).String()) // Finally, try the NotImplementedError by mocking the server // address to a handler that returns 405 Method Not Allowed for // POST. http.HandleFunc("/charms", func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) } }) go func() { err = http.ListenAndServe(":8900", nil) c.Assert(err, gc.IsNil) }() api.SetServerRoot(client, "http://localhost:8900") _, err = client.AddLocalCharm(curl, charmArchive) c.Assert(err, jc.Satisfies, params.IsCodeNotImplemented) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/export_test.go�������������������������������0000644�0000153�0000161�00000000475�12321735776�025503� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package api // SetServerRoot allows changing the URL to the internal API server // that AddLocalCharm uses in order to test NotImplementedError. func SetServerRoot(c *Client, root string) { c.st.serverRoot = root } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/logger/��������������������������������������0000755�0000153�0000161�00000000000�12321735642�024035� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/logger/logger.go�����������������������������0000644�0000153�0000161�00000004075�12321735642�025651� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package logger import ( "fmt" "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // State provides access to an logger worker's view of the state. type State struct { caller base.Caller } func (st *State) call(method string, params, result interface{}) error { return st.caller.Call("Logger", "", method, params, result) } // NewState returns a version of the state that provides functionality // required by the logger worker. func NewState(caller base.Caller) *State { return &State{caller} } // LoggingConfig returns the loggo configuration string for the agent // specified by agentTag. func (st *State) LoggingConfig(agentTag string) (string, error) { var results params.StringResults args := params.Entities{ Entities: []params.Entity{{Tag: agentTag}}, } err := st.call("LoggingConfig", args, &results) if err != nil { // TODO: Not directly tested return "", err } if len(results.Results) != 1 { // TODO: Not directly tested return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { return "", err } return result.Result, nil } // WatchLoggingConfig returns a notify watcher that looks for changes in the // logging-config for the agent specifed by agentTag. func (st *State) WatchLoggingConfig(agentTag string) (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: agentTag}}, } err := st.call("WatchLoggingConfig", args, &results) if err != nil { // TODO: Not directly tested return nil, err } if len(results.Results) != 1 { // TODO: Not directly tested return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { // TODO: Not directly tested return nil, result.Error } w := watcher.NewNotifyWatcher(st.caller, result) return w, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/logger/package_test.go�����������������������0000644�0000153�0000161�00000000366�12321735642�027023� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package logger_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/logger/logger_test.go������������������������0000644�0000153�0000161�00000004330�12321735642�026702� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package logger_test import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/logger" "launchpad.net/juju-core/state/testing" ) type loggerSuite struct { jujutesting.JujuConnSuite // These are raw State objects. Use them for setup and assertions, but // should never be touched by the API calls themselves rawMachine *state.Machine rawCharm *state.Charm rawService *state.Service rawUnit *state.Unit logger *logger.State } var _ = gc.Suite(&loggerSuite{}) func (s *loggerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) var stateAPI *api.State stateAPI, s.rawMachine = s.OpenAPIAsNewMachine(c) // Create the logger facade. s.logger = stateAPI.Logger() c.Assert(s.logger, gc.NotNil) } func (s *loggerSuite) TestLoggingConfigWrongMachine(c *gc.C) { config, err := s.logger.LoggingConfig("42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(config, gc.Equals, "") } func (s *loggerSuite) TestLoggingConfig(c *gc.C) { config, err := s.logger.LoggingConfig(s.rawMachine.Tag()) c.Assert(err, gc.IsNil) c.Assert(config, gc.Not(gc.Equals), "") } func (s *loggerSuite) setLoggingConfig(c *gc.C, loggingConfig string) { err := s.BackingState.UpdateEnvironConfig(map[string]interface{}{"logging-config": loggingConfig}, nil, nil) c.Assert(err, gc.IsNil) } func (s *loggerSuite) TestWatchLoggingConfig(c *gc.C) { watcher, err := s.logger.WatchLoggingConfig(s.rawMachine.Tag()) c.Assert(err, gc.IsNil) defer testing.AssertStop(c, watcher) wc := testing.NewNotifyWatcherC(c, s.BackingState, watcher) // Initial event wc.AssertOneChange() loggingConfig := "<root>=WARN;juju.log.test=DEBUG" s.setLoggingConfig(c, loggingConfig) // One change noticing the new version wc.AssertOneChange() // Setting the version to the same value doesn't trigger a change s.setLoggingConfig(c, loggingConfig) wc.AssertNoChange() loggingConfig = loggingConfig + ";wibble=DEBUG" s.setLoggingConfig(c, loggingConfig) wc.AssertOneChange() testing.AssertStop(c, watcher) wc.AssertClosed() } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/watcher/�������������������������������������0000755�0000153�0000161�00000000000�12321735642�024213� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/watcher/interfaces.go������������������������0000644�0000153�0000161�00000001457�12321735642�026674� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package watcher import ( "launchpad.net/juju-core/state/api/params" ) // NotifyWatcher will send events when something changes. // It does not send content for those changes. type NotifyWatcher interface { Changes() <-chan struct{} Stop() error Err() error } // StringsWatcher will send events when something changes. // The content for the changes is a list of strings. type StringsWatcher interface { Changes() <-chan []string Stop() error Err() error } // RelationUnitsWatcher will send events when something changes. // The content for the changes is a params.RelationUnitsChange struct. type RelationUnitsWatcher interface { Changes() <-chan params.RelationUnitsChange Stop() error Err() error } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/watcher/watcher_test.go����������������������0000644�0000153�0000161�00000014313�12321735642�027240� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package watcher_test import ( stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type watcherSuite struct { testing.JujuConnSuite stateAPI *api.State // These are raw State objects. Use them for setup and assertions, but // should never be touched by the API calls themselves rawMachine *state.Machine } var _ = gc.Suite(&watcherSuite{}) func (s *watcherSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.stateAPI, s.rawMachine = s.OpenAPIAsNewMachine(c) } func (s *watcherSuite) TestWatchInitialEventConsumed(c *gc.C) { // Machiner.Watch should send the initial event as part of the Watch // call (for NotifyWatchers there is no state to be transmitted). So a // call to Next() should not have anything to return. var results params.NotifyWatchResults args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} err := s.stateAPI.Call("Machiner", "", "Watch", args, &results) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 1) result := results.Results[0] c.Assert(result.Error, gc.IsNil) // We expect the Call() to "Next" to block, so run it in a goroutine. done := make(chan error) go func() { ignored := struct{}{} done <- s.stateAPI.Call("NotifyWatcher", result.NotifyWatcherId, "Next", nil, &ignored) }() select { case err := <-done: c.Errorf("Call(Next) did not block immediately after Watch(): err %v", err) case <-time.After(coretesting.ShortWait): } } func (s *watcherSuite) TestWatchMachine(c *gc.C) { var results params.NotifyWatchResults args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} err := s.stateAPI.Call("Machiner", "", "Watch", args, &results) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 1) result := results.Results[0] c.Assert(result.Error, gc.IsNil) // params.NotifyWatcher conforms to the state.NotifyWatcher interface w := watcher.NewNotifyWatcher(s.stateAPI, result) wc := statetesting.NewNotifyWatcherC(c, s.State, w) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *watcherSuite) TestNotifyWatcherStopsWithPendingSend(c *gc.C) { var results params.NotifyWatchResults args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} err := s.stateAPI.Call("Machiner", "", "Watch", args, &results) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 1) result := results.Results[0] c.Assert(result.Error, gc.IsNil) // params.NotifyWatcher conforms to the state.NotifyWatcher interface w := watcher.NewNotifyWatcher(s.stateAPI, result) wc := statetesting.NewNotifyWatcherC(c, s.State, w) // Now, without reading any changes try stopping the watcher. statetesting.AssertCanStopWhenSending(c, w) wc.AssertClosed() } func (s *watcherSuite) TestWatchUnitsKeepsEvents(c *gc.C) { // Create two services, relate them, and add one unit to each - a // principal and a subordinate. mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) logging := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"mysql", "logging"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) principal, err := mysql.AddUnit() c.Assert(err, gc.IsNil) err = principal.AssignToMachine(s.rawMachine) c.Assert(err, gc.IsNil) relUnit, err := rel.Unit(principal) c.Assert(err, gc.IsNil) err = relUnit.EnterScope(nil) c.Assert(err, gc.IsNil) subordinate, err := logging.Unit("logging/0") c.Assert(err, gc.IsNil) // Call the Deployer facade's WatchUnits for machine-0. var results params.StringsWatchResults args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} err = s.stateAPI.Call("Deployer", "", "WatchUnits", args, &results) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 1) result := results.Results[0] c.Assert(result.Error, gc.IsNil) // Start a StringsWatcher and check the initial event. w := watcher.NewStringsWatcher(s.stateAPI, result) wc := statetesting.NewStringsWatcherC(c, s.State, w) wc.AssertChange("mysql/0", "logging/0") wc.AssertNoChange() // Now, without reading any changes advance the lifecycle of both // units, inducing an update server-side after each two changes to // ensure they're reported as separate events over the API. err = subordinate.EnsureDead() c.Assert(err, gc.IsNil) s.BackingState.StartSync() err = subordinate.Remove() c.Assert(err, gc.IsNil) err = principal.EnsureDead() c.Assert(err, gc.IsNil) s.BackingState.StartSync() // Expect these changes as 2 separate events, so that // nothing gets lost. wc.AssertChange("logging/0") wc.AssertChange("mysql/0") wc.AssertNoChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *watcherSuite) TestStringsWatcherStopsWithPendingSend(c *gc.C) { // Call the Deployer facade's WatchUnits for machine-0. var results params.StringsWatchResults args := params.Entities{Entities: []params.Entity{{Tag: s.rawMachine.Tag()}}} err := s.stateAPI.Call("Deployer", "", "WatchUnits", args, &results) c.Assert(err, gc.IsNil) c.Assert(results.Results, gc.HasLen, 1) result := results.Results[0] c.Assert(result.Error, gc.IsNil) // Start a StringsWatcher and check the initial event. w := watcher.NewStringsWatcher(s.stateAPI, result) wc := statetesting.NewStringsWatcherC(c, s.State, w) // Create a service, deploy a unit of it on the machine. mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) principal, err := mysql.AddUnit() c.Assert(err, gc.IsNil) err = principal.AssignToMachine(s.rawMachine) c.Assert(err, gc.IsNil) // Ensure the initial event is delivered. Then test the watcher // can be stopped cleanly without reading the pending change. s.BackingState.StartSync() statetesting.AssertCanStopWhenSending(c, w) wc.AssertClosed() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/watcher/watcher.go���������������������������0000644�0000153�0000161�00000017601�12321735642�026204� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package watcher import ( "sync" "launchpad.net/tomb" "launchpad.net/juju-core/log" "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/params" ) // commonWatcher implements common watcher logic in one place to // reduce code duplication, but it's not in fact a complete watcher; // it's intended for embedding. type commonWatcher struct { tomb tomb.Tomb wg sync.WaitGroup in chan interface{} // These fields must be set by the embedding watcher, before // calling init(). // newResult must return a pointer to a value of the type returned // by the watcher's Next call. newResult func() interface{} // call should invoke the given API method, placing the call's // returned value in result (if any). call func(method string, result interface{}) error } // init must be called to initialize an embedded commonWatcher's // fields. Make sure newResult and call fields are set beforehand. func (w *commonWatcher) init() { w.in = make(chan interface{}) if w.newResult == nil { panic("newResult must me set") } if w.call == nil { panic("call must be set") } } // commonLoop implements the loop structure common to the client // watchers. It should be started in a separate goroutine by any // watcher that embeds commonWatcher. It kills the commonWatcher's // tomb when an error occurs. func (w *commonWatcher) commonLoop() { defer close(w.in) w.wg.Add(1) go func() { // When the watcher has been stopped, we send a Stop request // to the server, which will remove the watcher and return a // CodeStopped error to any currently outstanding call to // Next. If a call to Next happens just after the watcher has // been stopped, we'll get a CodeNotFound error; Either way // we'll return, wait for the stop request to complete, and // the watcher will die with all resources cleaned up. defer w.wg.Done() <-w.tomb.Dying() if err := w.call("Stop", nil); err != nil { log.Errorf("state/api: error trying to stop watcher %v", err) } }() w.wg.Add(1) go func() { // Because Next blocks until there are changes, we need to // call it in a separate goroutine, so the watcher can be // stopped normally. defer w.wg.Done() for { result := w.newResult() err := w.call("Next", &result) if err != nil { if params.IsCodeStopped(err) || params.IsCodeNotFound(err) { if w.tomb.Err() != tomb.ErrStillAlive { // The watcher has been stopped at the client end, so we're // expecting one of the above two kinds of error. // We might see the same errors if the server itself // has been shut down, in which case we leave them // untouched. err = tomb.ErrDying } } // Something went wrong, just report the error and bail out. w.tomb.Kill(err) return } select { case <-w.tomb.Dying(): return case w.in <- result: // Report back the result we just got. } } }() w.wg.Wait() } func (w *commonWatcher) Stop() error { w.tomb.Kill(nil) return w.tomb.Wait() } func (w *commonWatcher) Err() error { return w.tomb.Err() } // notifyWatcher will send events when something changes. // It does not send content for those changes. type notifyWatcher struct { commonWatcher caller base.Caller notifyWatcherId string out chan struct{} } // If an API call returns a NotifyWatchResult, you can use this to turn it into // a local Watcher. func NewNotifyWatcher(caller base.Caller, result params.NotifyWatchResult) NotifyWatcher { w := &notifyWatcher{ caller: caller, notifyWatcherId: result.NotifyWatcherId, out: make(chan struct{}), } go func() { defer w.tomb.Done() defer close(w.out) defer w.wg.Wait() // Wait for watcher to be stopped. w.tomb.Kill(w.loop()) }() return w } func (w *notifyWatcher) loop() error { // No results for this watcher type. w.newResult = func() interface{} { return nil } w.call = func(request string, result interface{}) error { return w.caller.Call("NotifyWatcher", w.notifyWatcherId, request, nil, &result) } w.commonWatcher.init() go w.commonLoop() for { select { // Since for a notifyWatcher there are no changes to send, we // just set the event (initial first, then after each change). case w.out <- struct{}{}: case <-w.tomb.Dying(): return nil } if _, ok := <-w.in; !ok { // The tomb is already killed with the correct // error at this point, so just return. return nil } } return nil } // Changes returns a channel that receives a value when a given entity // changes in some way. func (w *notifyWatcher) Changes() <-chan struct{} { return w.out } // stringsWatcher will send events when something changes. // The content of the changes is a list of strings. type stringsWatcher struct { commonWatcher caller base.Caller stringsWatcherId string out chan []string } func NewStringsWatcher(caller base.Caller, result params.StringsWatchResult) StringsWatcher { w := &stringsWatcher{ caller: caller, stringsWatcherId: result.StringsWatcherId, out: make(chan []string), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop(result.Changes)) }() return w } func (w *stringsWatcher) loop(initialChanges []string) error { changes := initialChanges w.newResult = func() interface{} { return new(params.StringsWatchResult) } w.call = func(request string, result interface{}) error { return w.caller.Call("StringsWatcher", w.stringsWatcherId, request, nil, &result) } w.commonWatcher.init() go w.commonLoop() for { select { // Send the initial event or subsequent change. case w.out <- changes: case <-w.tomb.Dying(): return nil } // Read the next change. data, ok := <-w.in if !ok { // The tomb is already killed with the correct error // at this point, so just return. return nil } changes = data.(*params.StringsWatchResult).Changes } return nil } // Changes returns a channel that receives a list of strings of watched // entites with changes. func (w *stringsWatcher) Changes() <-chan []string { return w.out } // relationUnitsWatcher will sends notifications of units entering and // leaving the scope of a RelationUnit, and changes to the settings of // those units known to have entered. type relationUnitsWatcher struct { commonWatcher caller base.Caller relationUnitsWatcherId string out chan params.RelationUnitsChange } func NewRelationUnitsWatcher(caller base.Caller, result params.RelationUnitsWatchResult) RelationUnitsWatcher { w := &relationUnitsWatcher{ caller: caller, relationUnitsWatcherId: result.RelationUnitsWatcherId, out: make(chan params.RelationUnitsChange), } go func() { defer w.tomb.Done() defer close(w.out) w.tomb.Kill(w.loop(result.Changes)) }() return w } func (w *relationUnitsWatcher) loop(initialChanges params.RelationUnitsChange) error { changes := initialChanges w.newResult = func() interface{} { return new(params.RelationUnitsWatchResult) } w.call = func(request string, result interface{}) error { return w.caller.Call("RelationUnitsWatcher", w.relationUnitsWatcherId, request, nil, &result) } w.commonWatcher.init() go w.commonLoop() for { select { // Send the initial event or subsequent change. case w.out <- changes: case <-w.tomb.Dying(): return nil } // Read the next change. data, ok := <-w.in if !ok { // The tomb is already killed with the correct error // at this point, so just return. return nil } changes = data.(*params.RelationUnitsWatchResult).Changes } return nil } // Changes returns a channel that will receive the changes to // counterpart units in a relation. The first event on the channel // holds the initial state of the relation in its Changed field. func (w *relationUnitsWatcher) Changes() <-chan params.RelationUnitsChange { return w.out } �������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/rsyslog/�������������������������������������0000755�0000153�0000161�00000000000�12321735642�024260� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/rsyslog/rsyslog_test.go����������������������0000644�0000153�0000161�00000001361�12321735642�027351� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rsyslog_test import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" apitesting "launchpad.net/juju-core/state/api/testing" ) type rsyslogSuite struct { jujutesting.JujuConnSuite *apitesting.EnvironWatcherTests } var _ = gc.Suite(&rsyslogSuite{}) func (s *rsyslogSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) stateAPI, _ := s.OpenAPIAsNewMachine(c) rsyslogAPI := stateAPI.Rsyslog() c.Assert(rsyslogAPI, gc.NotNil) s.EnvironWatcherTests = apitesting.NewEnvironWatcherTests( rsyslogAPI, s.BackingState, apitesting.NoSecrets, ) } // SetRsyslogCACert is tested in state/apiserver/rsyslog �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/rsyslog/rsyslog.go���������������������������0000644�0000153�0000161�00000002055�12321735642�026313� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rsyslog import ( "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/common" "launchpad.net/juju-core/state/api/params" ) const rsyslogAPI = "Rsyslog" // State provides access to the Rsyslog API facade. type State struct { *common.EnvironWatcher caller base.Caller } // NewState creates a new client-side Rsyslog facade. func NewState(caller base.Caller) *State { return &State{ EnvironWatcher: common.NewEnvironWatcher(rsyslogAPI, caller), caller: caller, } } // SetRsyslogCert sets the rsyslog CA certificate, // which is used by clients to verify the server's // identity and establish a TLS session. func (st *State) SetRsyslogCert(caCert []byte) error { var result params.ErrorResult args := params.SetRsyslogCertParams{ CACert: caCert, } err := st.caller.Call(rsyslogAPI, "", "SetRsyslogCert", args, &result) if err != nil { return err } if result.Error != nil { return result.Error } return nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/rsyslog/package_test.go����������������������0000644�0000153�0000161�00000000367�12321735642�027247� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package rsyslog_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/usermanager/���������������������������������0000755�0000153�0000161�00000000000�12321735643�025070� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/usermanager/client_test.go�������������������0000644�0000153�0000161�00000003122�12321735642�027731� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package usermanager_test import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/usermanager" ) type usermanagerSuite struct { jujutesting.JujuConnSuite usermanager *usermanager.Client } var _ = gc.Suite(&usermanagerSuite{}) func (s *usermanagerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.usermanager = usermanager.NewClient(s.APIState) c.Assert(s.usermanager, gc.NotNil) } func (s *usermanagerSuite) TestAddUser(c *gc.C) { err := s.usermanager.AddUser("foobar", "password") c.Assert(err, gc.IsNil) _, err = s.State.User("foobar") c.Assert(err, gc.IsNil) } func (s *usermanagerSuite) TestRemoveUser(c *gc.C) { err := s.usermanager.AddUser("foobar", "password") c.Assert(err, gc.IsNil) _, err = s.State.User("foobar") c.Assert(err, gc.IsNil) err = s.usermanager.RemoveUser("foobar") c.Assert(err, gc.IsNil) user, err := s.State.User("foobar") c.Assert(user.IsDeactivated(), gc.Equals, true) } func (s *usermanagerSuite) TestAddExistingUser(c *gc.C) { err := s.usermanager.AddUser("foobar", "password") c.Assert(err, gc.IsNil) // Try adding again err = s.usermanager.AddUser("foobar", "password") c.Assert(err, gc.ErrorMatches, "Failed to create user: user already exists") } func (s *usermanagerSuite) TestCantRemoveAdminUser(c *gc.C) { err := s.usermanager.RemoveUser(state.AdminUser) c.Assert(err, gc.ErrorMatches, "Failed to remove user: Can't deactivate admin user") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/usermanager/package_test.go������������������0000644�0000153�0000161�00000000373�12321735642�030053� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package usermanager_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/usermanager/client.go������������������������0000644�0000153�0000161�00000002211�12321735642�026670� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package usermanager import ( "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" ) // TODO(mattyw) 2014-03-07 bug #1288750 // Need a SetPassword method. type Client struct { st *api.State } func (c *Client) call(method string, params, result interface{}) error { return c.st.Call("UserManager", "", method, params, result) } func NewClient(st *api.State) *Client { return &Client{st} } func (c *Client) Close() error { return c.st.Close() } func (c *Client) AddUser(tag, password string) error { u := params.EntityPassword{Tag: tag, Password: password} p := params.EntityPasswords{Changes: []params.EntityPassword{u}} results := new(params.ErrorResults) err := c.call("AddUser", p, results) if err != nil { return err } return results.OneError() } func (c *Client) RemoveUser(tag string) error { u := params.Entity{Tag: tag} p := params.Entities{Entities: []params.Entity{u}} results := new(params.ErrorResults) err := c.call("RemoveUser", p, results) if err != nil { return err } return results.OneError() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/state.go�������������������������������������0000644�0000153�0000161�00000006304�12321735776�024240� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package api import ( "launchpad.net/juju-core/state/api/agent" "launchpad.net/juju-core/state/api/charmrevisionupdater" "launchpad.net/juju-core/state/api/deployer" "launchpad.net/juju-core/state/api/environment" "launchpad.net/juju-core/state/api/firewaller" "launchpad.net/juju-core/state/api/keyupdater" "launchpad.net/juju-core/state/api/logger" "launchpad.net/juju-core/state/api/machiner" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/provisioner" "launchpad.net/juju-core/state/api/rsyslog" "launchpad.net/juju-core/state/api/uniter" "launchpad.net/juju-core/state/api/upgrader" ) // Login authenticates as the entity with the given name and password. // Subsequent requests on the state will act as that entity. This // method is usually called automatically by Open. The machine nonce // should be empty unless logging in as a machine agent. func (st *State) Login(tag, password, nonce string) error { err := st.Call("Admin", "", "Login", &params.Creds{ AuthTag: tag, Password: password, Nonce: nonce, }, nil) if err == nil { st.authTag = tag } return err } // Client returns an object that can be used // to access client-specific functionality. func (st *State) Client() *Client { return &Client{st} } // Machiner returns a version of the state that provides functionality // required by the machiner worker. func (st *State) Machiner() *machiner.State { return machiner.NewState(st) } // Provisioner returns a version of the state that provides functionality // required by the provisioner worker. func (st *State) Provisioner() *provisioner.State { return provisioner.NewState(st) } // Uniter returns a version of the state that provides functionality // required by the uniter worker. func (st *State) Uniter() *uniter.State { return uniter.NewState(st, st.authTag) } // Firewaller returns a version of the state that provides functionality // required by the firewaller worker. func (st *State) Firewaller() *firewaller.State { return firewaller.NewState(st) } // Agent returns a version of the state that provides // functionality required by the agent code. func (st *State) Agent() *agent.State { return agent.NewState(st) } // Upgrader returns access to the Upgrader API func (st *State) Upgrader() *upgrader.State { return upgrader.NewState(st) } // Deployer returns access to the Deployer API func (st *State) Deployer() *deployer.State { return deployer.NewState(st) } // Environment returns access to the Environment API func (st *State) Environment() *environment.Facade { return environment.NewFacade(st) } // Logger returns access to the Logger API func (st *State) Logger() *logger.State { return logger.NewState(st) } // KeyUpdater returns access to the KeyUpdater API func (st *State) KeyUpdater() *keyupdater.State { return keyupdater.NewState(st) } // CharmRevisionUpdater returns access to the CharmRevisionUpdater API func (st *State) CharmRevisionUpdater() *charmrevisionupdater.State { return charmrevisionupdater.NewState(st) } // Rsyslog returns access to the Rsyslog API func (st *State) Rsyslog() *rsyslog.State { return rsyslog.NewState(st) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/common/��������������������������������������0000755�0000153�0000161�00000000000�12321735642�024046� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/common/environwatcher.go���������������������0000644�0000153�0000161�00000003015�12321735642�027432� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // EnvironWatcher provides common client-side API functions // to call into apiserver.common.EnvironWatcher. type EnvironWatcher struct { facadeName string caller base.Caller } // NewEnvironWatcher creates a EnvironWatcher on the specified facade, // and uses this name when calling through the caller. func NewEnvironWatcher(facadeName string, caller base.Caller) *EnvironWatcher { return &EnvironWatcher{facadeName, caller} } // WatchForEnvironConfigChanges return a NotifyWatcher waiting for the // environment configuration to change. func (e *EnvironWatcher) WatchForEnvironConfigChanges() (watcher.NotifyWatcher, error) { var result params.NotifyWatchResult err := e.caller.Call(e.facadeName, "", "WatchForEnvironConfigChanges", nil, &result) if err != nil { return nil, err } return watcher.NewNotifyWatcher(e.caller, result), nil } // EnvironConfig returns the current environment configuration. func (e *EnvironWatcher) EnvironConfig() (*config.Config, error) { var result params.EnvironConfigResult err := e.caller.Call(e.facadeName, "", "EnvironConfig", nil, &result) if err != nil { return nil, err } conf, err := config.New(config.NoDefaults, result.Config) if err != nil { return nil, err } return conf, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/common/life.go�������������������������������0000644�0000153�0000161�00000001472�12321735642�025320� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "fmt" "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/params" ) // Life requests the life cycle of the given entity from the given // server-side API facade via the given caller. func Life(caller base.Caller, facadeName, tag string) (params.Life, error) { var result params.LifeResults args := params.Entities{ Entities: []params.Entity{{Tag: tag}}, } err := caller.Call(facadeName, "", "Life", args, &result) if err != nil { return "", err } if len(result.Results) != 1 { return "", fmt.Errorf("expected 1 result, got %d", len(result.Results)) } if err := result.Results[0].Error; err != nil { return "", err } return result.Results[0].Life, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/common/apiaddresser.go�����������������������0000644�0000153�0000161�00000004013�12321735642�027041� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // APIAddresser provides common client-side API // functions to call into apiserver.common.APIAddresser type APIAddresser struct { facadeName string caller base.Caller } // NewAPIAddresser returns a new APIAddresser that makes API calls // using caller and the specified facade name. func NewAPIAddresser(facadeName string, caller base.Caller) *APIAddresser { return &APIAddresser{ facadeName: facadeName, caller: caller, } } // APIAddresses returns the list of addresses used to connect to the API. func (a *APIAddresser) APIAddresses() ([]string, error) { var result params.StringsResult err := a.caller.Call(a.facadeName, "", "APIAddresses", nil, &result) if err != nil { return nil, err } if err := result.Error; err != nil { return nil, err } return result.Result, nil } // CACert returns the certificate used to validate the API and state connections. func (a *APIAddresser) CACert() ([]byte, error) { var result params.BytesResult err := a.caller.Call(a.facadeName, "", "CACert", nil, &result) if err != nil { return nil, err } return result.Result, nil } // APIHostPorts returns the host/port addresses of the API servers. func (a *APIAddresser) APIHostPorts() ([][]instance.HostPort, error) { var result params.APIHostPortsResult err := a.caller.Call(a.facadeName, "", "APIHostPorts", nil, &result) if err != nil { return nil, err } return result.Servers, nil } // APIHostPorts watches the host/port addresses of the API servers. func (a *APIAddresser) WatchAPIHostPorts() (watcher.NotifyWatcher, error) { var result params.NotifyWatchResult err := a.caller.Call(a.facadeName, "", "WatchAPIHostPorts", nil, &result) if err != nil { return nil, err } return watcher.NewNotifyWatcher(a.caller, result), nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/keyupdater/����������������������������������0000755�0000153�0000161�00000000000�12321735642�024733� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/keyupdater/package_test.go�������������������0000644�0000153�0000161�00000000372�12321735642�027716� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keyupdater_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/keyupdater/authorisedkeys.go�����������������0000644�0000153�0000161�00000004113�12321735642�030324� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keyupdater import ( "fmt" "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // State provides access to a worker's view of the state. type State struct { caller base.Caller } func (st *State) call(method string, params, result interface{}) error { return st.caller.Call("KeyUpdater", "", method, params, result) } // NewState returns a version of the state that provides functionality required by the worker. func NewState(caller base.Caller) *State { return &State{caller} } // AuthorisedKeys returns the authorised ssh keys for the machine specified by machineTag. func (st *State) AuthorisedKeys(machineTag string) ([]string, error) { var results params.StringsResults args := params.Entities{ Entities: []params.Entity{{Tag: machineTag}}, } err := st.call("AuthorisedKeys", args, &results) if err != nil { // TODO: Not directly tested return nil, err } if len(results.Results) != 1 { // TODO: Not directly tested return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { return nil, err } return result.Result, nil } // WatchAuthorisedKeys returns a notify watcher that looks for changes in the // authorised ssh keys for the machine specified by machineTag. func (st *State) WatchAuthorisedKeys(machineTag string) (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: machineTag}}, } err := st.call("WatchAuthorisedKeys", args, &results) if err != nil { // TODO: Not directly tested return nil, err } if len(results.Results) != 1 { // TODO: Not directly tested return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { // TODO: Not directly tested return nil, result.Error } w := watcher.NewNotifyWatcher(st.caller, result) return w, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/keyupdater/authorisedkeys_test.go������������0000644�0000153�0000161�00000004604�12321735642�031370� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keyupdater_test import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/keyupdater" "launchpad.net/juju-core/state/testing" ) type keyupdaterSuite struct { jujutesting.JujuConnSuite // These are raw State objects. Use them for setup and assertions, but // should never be touched by the API calls themselves rawMachine *state.Machine keyupdater *keyupdater.State } var _ = gc.Suite(&keyupdaterSuite{}) func (s *keyupdaterSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) var stateAPI *api.State stateAPI, s.rawMachine = s.OpenAPIAsNewMachine(c) c.Assert(stateAPI, gc.NotNil) s.keyupdater = stateAPI.KeyUpdater() c.Assert(s.keyupdater, gc.NotNil) } func (s *keyupdaterSuite) TestAuthorisedKeysNoSuchMachine(c *gc.C) { _, err := s.keyupdater.AuthorisedKeys("machine-42") c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *keyupdaterSuite) TestAuthorisedKeysForbiddenMachine(c *gc.C) { m, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) _, err = s.keyupdater.AuthorisedKeys(m.Tag()) c.Assert(err, gc.ErrorMatches, "permission denied") } func (s *keyupdaterSuite) TestAuthorisedKeys(c *gc.C) { s.setAuthorisedKeys(c, "key1\nkey2") keys, err := s.keyupdater.AuthorisedKeys(s.rawMachine.Tag()) c.Assert(err, gc.IsNil) c.Assert(keys, gc.DeepEquals, []string{"key1", "key2"}) } func (s *keyupdaterSuite) setAuthorisedKeys(c *gc.C, keys string) { err := s.BackingState.UpdateEnvironConfig(map[string]interface{}{"authorized-keys": keys}, nil, nil) c.Assert(err, gc.IsNil) } func (s *keyupdaterSuite) TestWatchAuthorisedKeys(c *gc.C) { watcher, err := s.keyupdater.WatchAuthorisedKeys(s.rawMachine.Tag()) c.Assert(err, gc.IsNil) defer testing.AssertStop(c, watcher) wc := testing.NewNotifyWatcherC(c, s.BackingState, watcher) // Initial event wc.AssertOneChange() s.setAuthorisedKeys(c, "key1\nkey2") // One change noticing the new version wc.AssertOneChange() // Setting the version to the same value doesn't trigger a change s.setAuthorisedKeys(c, "key1\nkey2") wc.AssertNoChange() s.setAuthorisedKeys(c, "key1\nkey2\nkey3") wc.AssertOneChange() testing.AssertStop(c, watcher) wc.AssertClosed() } ����������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/upgrader/������������������������������������0000755�0000153�0000161�00000000000�12321735643�024370� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/upgrader/upgrader.go�������������������������0000644�0000153�0000161�00000007024�12321735642�026532� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader import ( "fmt" "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // State provides access to an upgrader worker's view of the state. type State struct { caller base.Caller } func (st *State) call(method string, params, result interface{}) error { return st.caller.Call("Upgrader", "", method, params, result) } // NewState returns a version of the state that provides functionality // required by the upgrader worker. func NewState(caller base.Caller) *State { return &State{caller} } // SetVersion sets the tools version associated with the entity with // the given tag, which must be the tag of the entity that the // upgrader is running on behalf of. func (st *State) SetVersion(tag string, v version.Binary) error { var results params.ErrorResults args := params.EntitiesVersion{ AgentTools: []params.EntityVersion{{ Tag: tag, Tools: &params.Version{v}, }}, } err := st.call("SetTools", args, &results) if err != nil { // TODO: Not directly tested return err } return results.OneError() } func (st *State) DesiredVersion(tag string) (version.Number, error) { var results params.VersionResults args := params.Entities{ Entities: []params.Entity{{Tag: tag}}, } err := st.call("DesiredVersion", args, &results) if err != nil { // TODO: Not directly tested return version.Number{}, err } if len(results.Results) != 1 { // TODO: Not directly tested return version.Number{}, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { return version.Number{}, err } if result.Version == nil { // TODO: Not directly tested return version.Number{}, fmt.Errorf("received no error, but got a nil Version") } return *result.Version, nil } // Tools returns the agent tools that should run on the given entity, // along with a flag whether to disable SSL hostname verification. func (st *State) Tools(tag string) (*tools.Tools, utils.SSLHostnameVerification, error) { var results params.ToolsResults args := params.Entities{ Entities: []params.Entity{{Tag: tag}}, } err := st.call("Tools", args, &results) if err != nil { // TODO: Not directly tested return nil, false, err } if len(results.Results) != 1 { // TODO: Not directly tested return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { return nil, false, err } hostnameVerification := utils.VerifySSLHostnames if result.DisableSSLHostnameVerification { hostnameVerification = utils.NoVerifySSLHostnames } return result.Tools, hostnameVerification, nil } func (st *State) WatchAPIVersion(agentTag string) (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: agentTag}}, } err := st.call("WatchAPIVersion", args, &results) if err != nil { // TODO: Not directly tested return nil, err } if len(results.Results) != 1 { // TODO: Not directly tested return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { // TODO: Not directly tested return nil, result.Error } w := watcher.NewNotifyWatcher(st.caller, result) return w, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/upgrader/unitupgrader_test.go����������������0000644�0000153�0000161�00000013173�12321735642�030473� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/errors" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/upgrader" statetesting "launchpad.net/juju-core/state/testing" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) type unitUpgraderSuite struct { jujutesting.JujuConnSuite stateAPI *api.State // These are raw State objects. Use them for setup and assertions, but // should never be touched by the API calls themselves rawMachine *state.Machine rawUnit *state.Unit st *upgrader.State } var _ = gc.Suite(&unitUpgraderSuite{}) func (s *unitUpgraderSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.rawMachine, _, _, s.rawUnit = s.addMachineServiceCharmAndUnit(c, "wordpress") password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = s.rawUnit.SetPassword(password) c.Assert(err, gc.IsNil) s.stateAPI = s.OpenAPIAs(c, s.rawUnit.Tag(), password) // Create the upgrader facade. s.st = s.stateAPI.Upgrader() c.Assert(s.st, gc.NotNil) } func (s *unitUpgraderSuite) addMachineServiceCharmAndUnit(c *gc.C, serviceName string) (*state.Machine, *state.Service, *state.Charm, *state.Unit) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) charm := s.AddTestingCharm(c, serviceName) service := s.AddTestingService(c, serviceName, charm) unit, err := service.AddUnit() c.Assert(err, gc.IsNil) err = unit.AssignToMachine(machine) c.Assert(err, gc.IsNil) return machine, service, charm, unit } func (s *unitUpgraderSuite) TestSetVersionWrongUnit(c *gc.C) { err := s.st.SetVersion("unit-wordpress-42", version.Current) c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) } func (s *unitUpgraderSuite) TestSetVersionNotUnit(c *gc.C) { err := s.st.SetVersion("foo-42", version.Current) c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) } func (s *unitUpgraderSuite) TestSetVersion(c *gc.C) { cur := version.Current agentTools, err := s.rawUnit.AgentTools() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) c.Assert(agentTools, gc.IsNil) err = s.st.SetVersion(s.rawUnit.Tag(), cur) c.Assert(err, gc.IsNil) s.rawUnit.Refresh() agentTools, err = s.rawUnit.AgentTools() c.Assert(err, gc.IsNil) c.Check(agentTools.Version, gc.Equals, cur) } func (s *unitUpgraderSuite) TestToolsWrongUnit(c *gc.C) { tools, _, err := s.st.Tools("unit-wordpress-42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(tools, gc.IsNil) } func (s *unitUpgraderSuite) TestToolsNotUnit(c *gc.C) { tools, _, err := s.st.Tools("foo-42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(tools, gc.IsNil) } func (s *unitUpgraderSuite) TestTools(c *gc.C) { cur := version.Current curTools := &tools.Tools{Version: cur, URL: ""} curTools.Version.Minor++ s.rawMachine.SetAgentVersion(cur) // UnitUpgrader.Tools returns the *desired* set of tools, not the currently // running set. We want to be upgraded to cur.Version stateTools, _, err := s.st.Tools(s.rawUnit.Tag()) c.Assert(err, gc.IsNil) c.Check(stateTools.Version.Number, gc.DeepEquals, version.Current.Number) c.Assert(stateTools.URL, gc.NotNil) } func (s *unitUpgraderSuite) TestWatchAPIVersion(c *gc.C) { w, err := s.st.WatchAPIVersion(s.rawUnit.Tag()) c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w) // Initial event wc.AssertOneChange() vers := version.MustParseBinary("10.20.34-quantal-amd64") err = s.rawMachine.SetAgentVersion(vers) c.Assert(err, gc.IsNil) // One change noticing the new version wc.AssertOneChange() vers = version.MustParseBinary("10.20.35-quantal-amd64") err = s.rawMachine.SetAgentVersion(vers) c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *unitUpgraderSuite) TestWatchAPIVersionWrongUnit(c *gc.C) { _, err := s.st.WatchAPIVersion("unit-wordpress-42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) } func (s *unitUpgraderSuite) TestWatchAPIVersionNotUnit(c *gc.C) { _, err := s.st.WatchAPIVersion("foo-42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) } func (s *unitUpgraderSuite) TestDesiredVersion(c *gc.C) { cur := version.Current curTools := &tools.Tools{Version: cur, URL: ""} curTools.Version.Minor++ s.rawMachine.SetAgentVersion(cur) // UnitUpgrader.DesiredVersion returns the *desired* set of tools, not the // currently running set. We want to be upgraded to cur.Version stateVersion, err := s.st.DesiredVersion(s.rawUnit.Tag()) c.Assert(err, gc.IsNil) c.Assert(stateVersion, gc.Equals, cur.Number) } func (s *unitUpgraderSuite) TestDesiredVersionWrongUnit(c *gc.C) { _, err := s.st.DesiredVersion("unit-wordpress-42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) } func (s *unitUpgraderSuite) TestDesiredVersionNotUnit(c *gc.C) { _, err := s.st.DesiredVersion("foo-42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/upgrader/upgrader_test.go��������������������0000644�0000153�0000161�00000012037�12321735642�027571� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package upgrader_test import ( stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/upgrader" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type machineUpgraderSuite struct { testing.JujuConnSuite stateAPI *api.State // These are raw State objects. Use them for setup and assertions, but // should never be touched by the API calls themselves rawMachine *state.Machine st *upgrader.State } var _ = gc.Suite(&machineUpgraderSuite{}) func (s *machineUpgraderSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.stateAPI, s.rawMachine = s.OpenAPIAsNewMachine(c) // Create the upgrader facade. s.st = s.stateAPI.Upgrader() c.Assert(s.st, gc.NotNil) } // Note: This is really meant as a unit-test, this isn't a test that should // need all of the setup we have for this test suite func (s *machineUpgraderSuite) TestNew(c *gc.C) { upgrader := upgrader.NewState(s.stateAPI) c.Assert(upgrader, gc.NotNil) } func (s *machineUpgraderSuite) TestSetVersionWrongMachine(c *gc.C) { err := s.st.SetVersion("machine-42", version.Current) c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) } func (s *machineUpgraderSuite) TestSetVersionNotMachine(c *gc.C) { err := s.st.SetVersion("foo-42", version.Current) c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) } func (s *machineUpgraderSuite) TestSetVersion(c *gc.C) { cur := version.Current agentTools, err := s.rawMachine.AgentTools() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) c.Assert(agentTools, gc.IsNil) err = s.st.SetVersion(s.rawMachine.Tag(), cur) c.Assert(err, gc.IsNil) s.rawMachine.Refresh() agentTools, err = s.rawMachine.AgentTools() c.Assert(err, gc.IsNil) c.Check(agentTools.Version, gc.Equals, cur) } func (s *machineUpgraderSuite) TestToolsWrongMachine(c *gc.C) { tools, _, err := s.st.Tools("machine-42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(tools, gc.IsNil) } func (s *machineUpgraderSuite) TestToolsNotMachine(c *gc.C) { tools, _, err := s.st.Tools("foo-42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(tools, gc.IsNil) } func (s *machineUpgraderSuite) TestTools(c *gc.C) { cur := version.Current curTools := &tools.Tools{Version: cur, URL: ""} curTools.Version.Minor++ s.rawMachine.SetAgentVersion(cur) // Upgrader.Tools returns the *desired* set of tools, not the currently // running set. We want to be upgraded to cur.Version stateTools, hostnameVerification, err := s.st.Tools(s.rawMachine.Tag()) c.Assert(err, gc.IsNil) c.Assert(stateTools.Version, gc.Equals, cur) c.Assert(stateTools.URL, gc.Not(gc.Equals), "") c.Assert(hostnameVerification, gc.Equals, utils.VerifySSLHostnames) envtesting.SetSSLHostnameVerification(c, s.State, false) stateTools, hostnameVerification, err = s.st.Tools(s.rawMachine.Tag()) c.Assert(err, gc.IsNil) c.Assert(stateTools.Version, gc.Equals, cur) c.Assert(stateTools.URL, gc.Not(gc.Equals), "") c.Assert(hostnameVerification, gc.Equals, utils.NoVerifySSLHostnames) } func (s *machineUpgraderSuite) TestWatchAPIVersion(c *gc.C) { w, err := s.st.WatchAPIVersion(s.rawMachine.Tag()) c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w) // Initial event wc.AssertOneChange() vers := version.MustParse("10.20.34") err = statetesting.SetAgentVersion(s.BackingState, vers) c.Assert(err, gc.IsNil) // One change noticing the new version wc.AssertOneChange() // Setting the version to the same value doesn't trigger a change err = statetesting.SetAgentVersion(s.BackingState, vers) c.Assert(err, gc.IsNil) wc.AssertNoChange() vers = version.MustParse("10.20.35") err = statetesting.SetAgentVersion(s.BackingState, vers) c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *machineUpgraderSuite) TestDesiredVersion(c *gc.C) { cur := version.Current curTools := &tools.Tools{Version: cur, URL: ""} curTools.Version.Minor++ s.rawMachine.SetAgentVersion(cur) // Upgrader.DesiredVersion returns the *desired* set of tools, not the // currently running set. We want to be upgraded to cur.Version stateVersion, err := s.st.DesiredVersion(s.rawMachine.Tag()) c.Assert(err, gc.IsNil) c.Assert(stateVersion, gc.Equals, cur.Number) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/client.go������������������������������������0000644�0000153�0000161�00000055453�12321735776�024407� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package api import ( "encoding/json" "fmt" "io/ioutil" "net/http" "os" "strings" "time" "launchpad.net/juju-core/charm" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) // Client represents the client-accessible part of the state. type Client struct { st *State } // NetworksSpecification holds the enabled and disabled networks for a // service. type NetworksSpecification struct { Enabled []string Disabled []string } func (c *Client) call(method string, params, result interface{}) error { return c.st.Call("Client", "", method, params, result) } // MachineStatus holds status info about a machine. type MachineStatus struct { Err error AgentState params.Status AgentStateInfo string AgentVersion string DNSName string InstanceId instance.Id InstanceState string Life string Series string Id string Containers map[string]MachineStatus Hardware string } // ServiceStatus holds status info about a service. type ServiceStatus struct { Err error Charm string Exposed bool Life string Relations map[string][]string Networks NetworksSpecification CanUpgradeTo string SubordinateTo []string Units map[string]UnitStatus } // UnitStatus holds status info about a unit. type UnitStatus struct { Err error AgentState params.Status AgentStateInfo string AgentVersion string Life string Machine string OpenedPorts []string PublicAddress string Charm string Subordinates map[string]UnitStatus } // Status holds information about the status of a juju environment. type Status struct { EnvironmentName string Machines map[string]MachineStatus Services map[string]ServiceStatus } // Status returns the status of the juju environment. func (c *Client) Status(patterns []string) (*Status, error) { var result Status p := params.StatusParams{Patterns: patterns} if err := c.call("FullStatus", p, &result); err != nil { return nil, err } return &result, nil } // LegacyMachineStatus holds just the instance-id of a machine. type LegacyMachineStatus struct { InstanceId string // Not type instance.Id just to match original api. } // LegacyStatus holds minimal information on the status of a juju environment. type LegacyStatus struct { Machines map[string]LegacyMachineStatus } // LegacyStatus is a stub version of Status that 1.16 introduced. Should be // removed along with structs when api versioning makes it safe to do so. func (c *Client) LegacyStatus() (*LegacyStatus, error) { var result LegacyStatus if err := c.call("Status", nil, &result); err != nil { return nil, err } return &result, nil } // ServiceSet sets configuration options on a service. func (c *Client) ServiceSet(service string, options map[string]string) error { p := params.ServiceSet{ ServiceName: service, Options: options, } // TODO(Nate): Put this back to ServiceSet when the GUI stops expecting // ServiceSet to unset values set to an empty string. return c.call("NewServiceSetForClientAPI", p, nil) } // ServiceUnset resets configuration options on a service. func (c *Client) ServiceUnset(service string, options []string) error { p := params.ServiceUnset{ ServiceName: service, Options: options, } return c.call("ServiceUnset", p, nil) } // Resolved clears errors on a unit. func (c *Client) Resolved(unit string, retry bool) error { p := params.Resolved{ UnitName: unit, Retry: retry, } return c.call("Resolved", p, nil) } // RetryProvisioning updates the provisioning status of a machine allowing the // provisioner to retry. func (c *Client) RetryProvisioning(machines ...string) ([]params.ErrorResult, error) { p := params.Entities{} p.Entities = make([]params.Entity, len(machines)) for i, machine := range machines { p.Entities[i] = params.Entity{Tag: machine} } var results params.ErrorResults err := c.st.Call("Client", "", "RetryProvisioning", p, &results) return results.Results, err } // PublicAddress returns the public address of the specified // machine or unit. func (c *Client) PublicAddress(target string) (string, error) { var results params.PublicAddressResults p := params.PublicAddress{Target: target} err := c.call("PublicAddress", p, &results) return results.PublicAddress, err } // PrivateAddress returns the private address of the specified // machine or unit. func (c *Client) PrivateAddress(target string) (string, error) { var results params.PrivateAddressResults p := params.PrivateAddress{Target: target} err := c.call("PrivateAddress", p, &results) return results.PrivateAddress, err } // ServiceSetYAML sets configuration options on a service // given options in YAML format. func (c *Client) ServiceSetYAML(service string, yaml string) error { p := params.ServiceSetYAML{ ServiceName: service, Config: yaml, } return c.call("ServiceSetYAML", p, nil) } // ServiceGet returns the configuration for the named service. func (c *Client) ServiceGet(service string) (*params.ServiceGetResults, error) { var results params.ServiceGetResults params := params.ServiceGet{ServiceName: service} err := c.call("ServiceGet", params, &results) return &results, err } // AddRelation adds a relation between the specified endpoints and returns the relation info. func (c *Client) AddRelation(endpoints ...string) (*params.AddRelationResults, error) { var addRelRes params.AddRelationResults params := params.AddRelation{Endpoints: endpoints} err := c.call("AddRelation", params, &addRelRes) return &addRelRes, err } // DestroyRelation removes the relation between the specified endpoints. func (c *Client) DestroyRelation(endpoints ...string) error { params := params.DestroyRelation{Endpoints: endpoints} return c.call("DestroyRelation", params, nil) } // ServiceCharmRelations returns the service's charms relation names. func (c *Client) ServiceCharmRelations(service string) ([]string, error) { var results params.ServiceCharmRelationsResults params := params.ServiceCharmRelations{ServiceName: service} err := c.call("ServiceCharmRelations", params, &results) return results.CharmRelations, err } // AddMachines adds new machines with the supplied parameters. func (c *Client) AddMachines(machineParams []params.AddMachineParams) ([]params.AddMachinesResult, error) { args := params.AddMachines{ MachineParams: machineParams, } results := new(params.AddMachinesResults) err := c.call("AddMachines", args, results) return results.Machines, err } // ProvisioningScript returns a shell script that, when run, // provisions a machine agent on the machine executing the script. func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (script string, err error) { var result params.ProvisioningScriptResult if err = c.call("ProvisioningScript", args, &result); err != nil { return "", err } return result.Script, nil } // DestroyMachines removes a given set of machines. func (c *Client) DestroyMachines(machines ...string) error { params := params.DestroyMachines{MachineNames: machines} return c.call("DestroyMachines", params, nil) } // ForceDestroyMachines removes a given set of machines and all associated units. func (c *Client) ForceDestroyMachines(machines ...string) error { params := params.DestroyMachines{Force: true, MachineNames: machines} return c.call("DestroyMachines", params, nil) } // ServiceExpose changes the juju-managed firewall to expose any ports that // were also explicitly marked by units as open. func (c *Client) ServiceExpose(service string) error { params := params.ServiceExpose{ServiceName: service} return c.call("ServiceExpose", params, nil) } // ServiceUnexpose changes the juju-managed firewall to unexpose any ports that // were also explicitly marked by units as open. func (c *Client) ServiceUnexpose(service string) error { params := params.ServiceUnexpose{ServiceName: service} return c.call("ServiceUnexpose", params, nil) } // ServiceDeployWithNetworks works exactly like ServiceDeploy, but // allows specifying networks to either include or exclude on the // machine where the charm is deployed. func (c *Client) ServiceDeployWithNetworks(charmURL string, serviceName string, numUnits int, configYAML string, cons constraints.Value, toMachineSpec string, includeNetworks, excludeNetworks []string) error { params := params.ServiceDeploy{ ServiceName: serviceName, CharmUrl: charmURL, NumUnits: numUnits, ConfigYAML: configYAML, Constraints: cons, ToMachineSpec: toMachineSpec, IncludeNetworks: includeNetworks, ExcludeNetworks: excludeNetworks, } return c.st.Call("Client", "", "ServiceDeployWithNetworks", params, nil) } // ServiceDeploy obtains the charm, either locally or from the charm store, // and deploys it. func (c *Client) ServiceDeploy(charmURL string, serviceName string, numUnits int, configYAML string, cons constraints.Value, toMachineSpec string) error { params := params.ServiceDeploy{ ServiceName: serviceName, CharmUrl: charmURL, NumUnits: numUnits, ConfigYAML: configYAML, Constraints: cons, ToMachineSpec: toMachineSpec, } return c.call("ServiceDeploy", params, nil) } // ServiceUpdate updates the service attributes, including charm URL, // minimum number of units, settings and constraints. // TODO(frankban) deprecate redundant API calls that this supercedes. func (c *Client) ServiceUpdate(args params.ServiceUpdate) error { return c.call("ServiceUpdate", args, nil) } // ServiceSetCharm sets the charm for a given service. func (c *Client) ServiceSetCharm(serviceName string, charmUrl string, force bool) error { args := params.ServiceSetCharm{ ServiceName: serviceName, CharmUrl: charmUrl, Force: force, } return c.call("ServiceSetCharm", args, nil) } // ServiceGetCharmURL returns the charm URL the given service is // running at present. func (c *Client) ServiceGetCharmURL(serviceName string) (*charm.URL, error) { result := new(params.StringResult) args := params.ServiceGet{ServiceName: serviceName} err := c.call("ServiceGetCharmURL", args, &result) if err != nil { return nil, err } return charm.ParseURL(result.Result) } // AddServiceUnits adds a given number of units to a service. func (c *Client) AddServiceUnits(service string, numUnits int, machineSpec string) ([]string, error) { args := params.AddServiceUnits{ ServiceName: service, NumUnits: numUnits, ToMachineSpec: machineSpec, } results := new(params.AddServiceUnitsResults) err := c.call("AddServiceUnits", args, results) return results.Units, err } // DestroyServiceUnits decreases the number of units dedicated to a service. func (c *Client) DestroyServiceUnits(unitNames ...string) error { params := params.DestroyServiceUnits{unitNames} return c.call("DestroyServiceUnits", params, nil) } // ServiceDestroy destroys a given service. func (c *Client) ServiceDestroy(service string) error { params := params.ServiceDestroy{ ServiceName: service, } return c.call("ServiceDestroy", params, nil) } // GetServiceConstraints returns the constraints for the given service. func (c *Client) GetServiceConstraints(service string) (constraints.Value, error) { results := new(params.GetConstraintsResults) err := c.call("GetServiceConstraints", params.GetServiceConstraints{service}, results) return results.Constraints, err } // GetEnvironmentConstraints returns the constraints for the environment. func (c *Client) GetEnvironmentConstraints() (constraints.Value, error) { results := new(params.GetConstraintsResults) err := c.call("GetEnvironmentConstraints", nil, results) return results.Constraints, err } // SetServiceConstraints specifies the constraints for the given service. func (c *Client) SetServiceConstraints(service string, constraints constraints.Value) error { params := params.SetConstraints{ ServiceName: service, Constraints: constraints, } return c.call("SetServiceConstraints", params, nil) } // SetEnvironmentConstraints specifies the constraints for the environment. func (c *Client) SetEnvironmentConstraints(constraints constraints.Value) error { params := params.SetConstraints{ Constraints: constraints, } return c.call("SetEnvironmentConstraints", params, nil) } // CharmInfo holds information about a charm. type CharmInfo struct { Revision int URL string Config *charm.Config Meta *charm.Meta } // CharmInfo returns information about the requested charm. func (c *Client) CharmInfo(charmURL string) (*CharmInfo, error) { args := params.CharmInfo{CharmURL: charmURL} info := new(CharmInfo) if err := c.call("CharmInfo", args, info); err != nil { return nil, err } return info, nil } // EnvironmentInfo holds information about the Juju environment. type EnvironmentInfo struct { DefaultSeries string ProviderType string Name string UUID string } // EnvironmentInfo returns details about the Juju environment. func (c *Client) EnvironmentInfo() (*EnvironmentInfo, error) { info := new(EnvironmentInfo) err := c.call("EnvironmentInfo", nil, info) return info, err } // WatchAll holds the id of the newly-created AllWatcher. type WatchAll struct { AllWatcherId string } // WatchAll returns an AllWatcher, from which you can request the Next // collection of Deltas. func (c *Client) WatchAll() (*AllWatcher, error) { info := new(WatchAll) if err := c.call("WatchAll", nil, info); err != nil { return nil, err } return newAllWatcher(c, &info.AllWatcherId), nil } // GetAnnotations returns annotations that have been set on the given entity. func (c *Client) GetAnnotations(tag string) (map[string]string, error) { args := params.GetAnnotations{tag} ann := new(params.GetAnnotationsResults) err := c.call("GetAnnotations", args, ann) return ann.Annotations, err } // SetAnnotations sets the annotation pairs on the given entity. // Currently annotations are supported on machines, services, // units and the environment itself. func (c *Client) SetAnnotations(tag string, pairs map[string]string) error { args := params.SetAnnotations{tag, pairs} return c.call("SetAnnotations", args, nil) } // Close closes the Client's underlying State connection // Client is unique among the api.State facades in closing its own State // connection, but it is conventional to use a Client object without any access // to its underlying state connection. func (c *Client) Close() error { return c.st.Close() } // EnvironmentGet returns all environment settings. func (c *Client) EnvironmentGet() (map[string]interface{}, error) { result := params.EnvironmentGetResults{} err := c.call("EnvironmentGet", nil, &result) return result.Config, err } // EnvironmentSet sets the given key-value pairs in the environment. func (c *Client) EnvironmentSet(config map[string]interface{}) error { args := params.EnvironmentSet{Config: config} return c.call("EnvironmentSet", args, nil) } // EnvironmentUnset sets the given key-value pairs in the environment. func (c *Client) EnvironmentUnset(keys ...string) error { args := params.EnvironmentUnset{Keys: keys} return c.call("EnvironmentUnset", args, nil) } // SetEnvironAgentVersion sets the environment agent-version setting // to the given value. func (c *Client) SetEnvironAgentVersion(version version.Number) error { args := params.SetEnvironAgentVersion{Version: version} return c.call("SetEnvironAgentVersion", args, nil) } // FindTools returns a List containing all tools matching the specified parameters. func (c *Client) FindTools(majorVersion, minorVersion int, series, arch string) (result params.FindToolsResults, err error) { args := params.FindToolsParams{ MajorVersion: majorVersion, MinorVersion: minorVersion, Arch: arch, Series: series, } err = c.call("FindTools", args, &result) return result, err } // RunOnAllMachines runs the command on all the machines with the specified // timeout. func (c *Client) RunOnAllMachines(commands string, timeout time.Duration) ([]params.RunResult, error) { var results params.RunResults args := params.RunParams{Commands: commands, Timeout: timeout} err := c.call("RunOnAllMachines", args, &results) return results.Results, err } // Run the Commands specified on the machines identified through the ids // provided in the machines, services and units slices. func (c *Client) Run(run params.RunParams) ([]params.RunResult, error) { var results params.RunResults err := c.call("Run", run, &results) return results.Results, err } // DestroyEnvironment puts the environment into a "dying" state, // and removes all non-manager machine instances. DestroyEnvironment // will fail if there are any manually-provisioned non-manager machines // in state. func (c *Client) DestroyEnvironment() error { return c.call("DestroyEnvironment", nil, nil) } // AddLocalCharm prepares the given charm with a local: schema in its // URL, and uploads it via the API server, returning the assigned // charm URL. If the API server does not support charm uploads, an // error satisfying params.IsCodeNotImplemented() is returned. func (c *Client) AddLocalCharm(curl *charm.URL, ch charm.Charm) (*charm.URL, error) { if curl.Schema != "local" { return nil, fmt.Errorf("expected charm URL with local: schema, got %q", curl.String()) } // Package the charm for uploading. var archive *os.File switch ch := ch.(type) { case *charm.Dir: var err error if archive, err = ioutil.TempFile("", "charm"); err != nil { return nil, fmt.Errorf("cannot create temp file: %v", err) } defer os.Remove(archive.Name()) defer archive.Close() if err := ch.BundleTo(archive); err != nil { return nil, fmt.Errorf("cannot repackage charm: %v", err) } if _, err := archive.Seek(0, 0); err != nil { return nil, fmt.Errorf("cannot rewind packaged charm: %v", err) } case *charm.Bundle: var err error if archive, err = os.Open(ch.Path); err != nil { return nil, fmt.Errorf("cannot read charm archive: %v", err) } defer archive.Close() default: return nil, fmt.Errorf("unknown charm type %T", ch) } // Prepare the upload request. url := fmt.Sprintf("%s/charms?series=%s", c.st.serverRoot, curl.Series) req, err := http.NewRequest("POST", url, archive) if err != nil { return nil, fmt.Errorf("cannot create upload request: %v", err) } req.SetBasicAuth(c.st.tag, c.st.password) req.Header.Set("Content-Type", "application/zip") // Send the request. // BUG(dimitern) 2013-12-17 bug #1261780 // Due to issues with go 1.1.2, fixed later, we cannot use a // regular TLS client with the CACert here, because we get "x509: // cannot validate certificate for 127.0.0.1 because it doesn't // contain any IP SANs". Once we use a later go version, this // should be changed to connect to the API server with a regular // HTTP+TLS enabled client, using the CACert (possily cached, like // the tag and password) passed in api.Open()'s info argument. resp, err := utils.GetNonValidatingHTTPClient().Do(req) if err != nil { return nil, fmt.Errorf("cannot upload charm: %v", err) } if resp.StatusCode == http.StatusMethodNotAllowed { // API server is 1.16 or older, so charm upload // is not supported; notify the client. return nil, &params.Error{ Message: "charm upload is not supported by the API server", Code: params.CodeNotImplemented, } } // Now parse the response & return. body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("cannot read charm upload response: %v", err) } defer resp.Body.Close() var jsonResponse params.CharmsResponse if err := json.Unmarshal(body, &jsonResponse); err != nil { return nil, fmt.Errorf("cannot unmarshal upload response: %v", err) } if jsonResponse.Error != "" { return nil, fmt.Errorf("error uploading charm: %v", jsonResponse.Error) } return charm.MustParseURL(jsonResponse.CharmURL), nil } // AddCharm adds the given charm URL (which must include revision) to // the environment, if it does not exist yet. Local charms are not // supported, only charm store URLs. See also AddLocalCharm() in the // client-side API. func (c *Client) AddCharm(curl *charm.URL) error { args := params.CharmURL{URL: curl.String()} return c.call("AddCharm", args, nil) } // ResolveCharms resolves the best available charm URLs with series, for charm // locations without a series specified. func (c *Client) ResolveCharm(ref charm.Reference) (*charm.URL, error) { args := params.ResolveCharms{References: []charm.Reference{ref}} result := new(params.ResolveCharmResults) if err := c.st.Call("Client", "", "ResolveCharms", args, result); err != nil { return nil, err } if len(result.URLs) == 0 { return nil, fmt.Errorf("unexpected empty response") } urlInfo := result.URLs[0] if urlInfo.Error != "" { return nil, fmt.Errorf("%v", urlInfo.Error) } return urlInfo.URL, nil } func (c *Client) UploadTools( toolsFilename string, vers version.Binary, fakeSeries ...string, ) ( tools *tools.Tools, err error, ) { toolsTarball, err := os.Open(toolsFilename) if err != nil { return nil, err } defer toolsTarball.Close() // Prepare the upload request. url := fmt.Sprintf("%s/tools?binaryVersion=%s&series=%s", c.st.serverRoot, vers, strings.Join(fakeSeries, ",")) req, err := http.NewRequest("POST", url, toolsTarball) if err != nil { return nil, fmt.Errorf("cannot create upload request: %v", err) } req.SetBasicAuth(c.st.tag, c.st.password) req.Header.Set("Content-Type", "application/x-tar-gz") // Send the request. // BUG(dimitern) 2013-12-17 bug #1261780 // Due to issues with go 1.1.2, fixed later, we cannot use a // regular TLS client with the CACert here, because we get "x509: // cannot validate certificate for 127.0.0.1 because it doesn't // contain any IP SANs". Once we use a later go version, this // should be changed to connect to the API server with a regular // HTTP+TLS enabled client, using the CACert (possily cached, like // the tag and password) passed in api.Open()'s info argument. resp, err := utils.GetNonValidatingHTTPClient().Do(req) if err != nil { return nil, fmt.Errorf("cannot upload charm: %v", err) } if resp.StatusCode == http.StatusMethodNotAllowed { // API server is older than 1.17.5, so tools upload // is not supported; notify the client. return nil, &params.Error{ Message: "tools upload is not supported by the API server", Code: params.CodeNotImplemented, } } // Now parse the response & return. body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("cannot read tools upload response: %v", err) } defer resp.Body.Close() var jsonResponse params.ToolsResult if err := json.Unmarshal(body, &jsonResponse); err != nil { return nil, fmt.Errorf("cannot unmarshal upload response: %v", err) } if err := jsonResponse.Error; err != nil { return nil, fmt.Errorf("error uploading tools: %v", err) } return jsonResponse.Tools, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/environment/���������������������������������0000755�0000153�0000161�00000000000�12321735642�025122� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/environment/environment.go�������������������0000644�0000153�0000161�00000001052�12321735642�030013� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environment import ( "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/common" ) const apiName = "Environment" // Facade provides access to a machine environment worker's view of the world. type Facade struct { *common.EnvironWatcher } // NewFacade returns a new api client facade instance. func NewFacade(caller base.Caller) *Facade { return &Facade{ EnvironWatcher: common.NewEnvironWatcher(apiName, caller), } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/environment/package_test.go������������������0000644�0000153�0000161�00000000373�12321735642�030106� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environment_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/environment/environment_test.go��������������0000644�0000153�0000161�00000001321�12321735642�031051� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package environment_test import ( gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" apitesting "launchpad.net/juju-core/state/api/testing" ) type environmentSuite struct { jujutesting.JujuConnSuite *apitesting.EnvironWatcherTests } var _ = gc.Suite(&environmentSuite{}) func (s *environmentSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) stateAPI, _ := s.OpenAPIAsNewMachine(c) environmentAPI := stateAPI.Environment() c.Assert(environmentAPI, gc.NotNil) s.EnvironWatcherTests = apitesting.NewEnvironWatcherTests( environmentAPI, s.BackingState, apitesting.NoSecrets) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/keymanager/����������������������������������0000755�0000153�0000161�00000000000�12321735642�024701� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/keymanager/client_test.go��������������������0000644�0000153�0000161�00000013632�12321735642�027552� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keymanager_test import ( "strings" gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/keymanager" "launchpad.net/juju-core/state/api/params" keymanagerserver "launchpad.net/juju-core/state/apiserver/keymanager" keymanagertesting "launchpad.net/juju-core/state/apiserver/keymanager/testing" "launchpad.net/juju-core/utils/ssh" sshtesting "launchpad.net/juju-core/utils/ssh/testing" ) type keymanagerSuite struct { jujutesting.JujuConnSuite keymanager *keymanager.Client } var _ = gc.Suite(&keymanagerSuite{}) func (s *keymanagerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.keymanager = keymanager.NewClient(s.APIState) c.Assert(s.keymanager, gc.NotNil) } func (s *keymanagerSuite) setAuthorisedKeys(c *gc.C, keys string) { err := s.BackingState.UpdateEnvironConfig(map[string]interface{}{"authorized-keys": keys}, nil, nil) c.Assert(err, gc.IsNil) } func (s *keymanagerSuite) TestListKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key s.setAuthorisedKeys(c, strings.Join([]string{key1, key2}, "\n")) keyResults, err := s.keymanager.ListKeys(ssh.Fingerprints, state.AdminUser) c.Assert(err, gc.IsNil) c.Assert(len(keyResults), gc.Equals, 1) result := keyResults[0] c.Assert(result.Error, gc.IsNil) c.Assert(result.Result, gc.DeepEquals, []string{sshtesting.ValidKeyOne.Fingerprint + " (user@host)", sshtesting.ValidKeyTwo.Fingerprint}) } func (s *keymanagerSuite) TestListKeysErrors(c *gc.C) { keyResults, err := s.keymanager.ListKeys(ssh.Fingerprints, "invalid") c.Assert(err, gc.IsNil) c.Assert(len(keyResults), gc.Equals, 1) result := keyResults[0] c.Assert(result.Error, gc.ErrorMatches, `permission denied`) } func clientError(message string) *params.Error { return &params.Error{ Message: message, Code: "", } } func (s *keymanagerSuite) assertEnvironKeys(c *gc.C, expected []string) { envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) keys := envConfig.AuthorizedKeys() c.Assert(keys, gc.Equals, strings.Join(expected, "\n")) } func (s *keymanagerSuite) TestAddKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" s.setAuthorisedKeys(c, key1) newKeys := []string{sshtesting.ValidKeyTwo.Key, sshtesting.ValidKeyThree.Key, "invalid"} errResults, err := s.keymanager.AddKeys(state.AdminUser, newKeys...) c.Assert(err, gc.IsNil) c.Assert(errResults, gc.DeepEquals, []params.ErrorResult{ {Error: nil}, {Error: nil}, {Error: clientError("invalid ssh key: invalid")}, }) s.assertEnvironKeys(c, append([]string{key1}, newKeys[:2]...)) } func (s *keymanagerSuite) TestAddSystemKey(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" s.setAuthorisedKeys(c, key1) apiState, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) keyManager := keymanager.NewClient(apiState) newKey := sshtesting.ValidKeyTwo.Key errResults, err := keyManager.AddKeys("juju-system-key", newKey) c.Assert(err, gc.IsNil) c.Assert(errResults, gc.DeepEquals, []params.ErrorResult{ {Error: nil}, }) s.assertEnvironKeys(c, []string{key1, newKey}) } func (s *keymanagerSuite) TestAddSystemKeyWrongUser(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" s.setAuthorisedKeys(c, key1) apiState, _ := s.OpenAPIAsNewMachine(c, state.JobManageEnviron) keyManager := keymanager.NewClient(apiState) newKey := sshtesting.ValidKeyTwo.Key _, err := keyManager.AddKeys("some-user", newKey) c.Assert(err, gc.ErrorMatches, "permission denied") s.assertEnvironKeys(c, []string{key1}) } func (s *keymanagerSuite) TestDeleteKeys(c *gc.C) { key1 := sshtesting.ValidKeyOne.Key + " user@host" key2 := sshtesting.ValidKeyTwo.Key key3 := sshtesting.ValidKeyThree.Key initialKeys := []string{key1, key2, key3, "invalid"} s.setAuthorisedKeys(c, strings.Join(initialKeys, "\n")) errResults, err := s.keymanager.DeleteKeys(state.AdminUser, sshtesting.ValidKeyTwo.Fingerprint, "user@host", "missing") c.Assert(err, gc.IsNil) c.Assert(errResults, gc.DeepEquals, []params.ErrorResult{ {Error: nil}, {Error: nil}, {Error: clientError("invalid ssh key: missing")}, }) s.assertEnvironKeys(c, []string{"invalid", key3}) } func (s *keymanagerSuite) TestImportKeys(c *gc.C) { s.PatchValue(&keymanagerserver.RunSSHImportId, keymanagertesting.FakeImport) key1 := sshtesting.ValidKeyOne.Key + " user@host" s.setAuthorisedKeys(c, key1) keyIds := []string{"lp:validuser", "invalid-key"} errResults, err := s.keymanager.ImportKeys(state.AdminUser, keyIds...) c.Assert(err, gc.IsNil) c.Assert(errResults, gc.DeepEquals, []params.ErrorResult{ {Error: nil}, {Error: clientError("invalid ssh key id: invalid-key")}, }) s.assertEnvironKeys(c, []string{key1, sshtesting.ValidKeyThree.Key}) } func (s *keymanagerSuite) assertInvalidUserOperation(c *gc.C, test func(user string, keys []string) error) { key1 := sshtesting.ValidKeyOne.Key + " user@host" s.setAuthorisedKeys(c, key1) // Run the required test code and check the error. keys := []string{sshtesting.ValidKeyTwo.Key, sshtesting.ValidKeyThree.Key} err := test("invalid", keys) c.Assert(err, gc.ErrorMatches, `permission denied`) // No environ changes. s.assertEnvironKeys(c, []string{key1}) } func (s *keymanagerSuite) TestAddKeysInvalidUser(c *gc.C) { s.assertInvalidUserOperation(c, func(user string, keys []string) error { _, err := s.keymanager.AddKeys(user, keys...) return err }) } func (s *keymanagerSuite) TestDeleteKeysInvalidUser(c *gc.C) { s.assertInvalidUserOperation(c, func(user string, keys []string) error { _, err := s.keymanager.DeleteKeys(user, keys...) return err }) } func (s *keymanagerSuite) TestImportKeysInvalidUser(c *gc.C) { s.assertInvalidUserOperation(c, func(user string, keys []string) error { _, err := s.keymanager.ImportKeys(user, keys...) return err }) } ������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/keymanager/package_test.go�������������������0000644�0000153�0000161�00000000372�12321735642�027664� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keymanager_test import ( stdtesting "testing" "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { testing.MgoTestPackage(t) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/keymanager/client.go�������������������������0000644�0000153�0000161�00000004200�12321735642�026502� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package keymanager import ( "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/utils/ssh" ) // Client provides access to the keymanager, used to add/delete/list authorised ssh keys. type Client struct { st *api.State } func (c *Client) call(method string, params, result interface{}) error { return c.st.Call("KeyManager", "", method, params, result) } // NewClient returns a new keymanager client. func NewClient(st *api.State) *Client { return &Client{st} } // Close closes the underlying State connection. func (c *Client) Close() error { return c.st.Close() } // ListKeys returns the authorised ssh keys for the specified users. func (c *Client) ListKeys(mode ssh.ListMode, users ...string) ([]params.StringsResult, error) { p := params.ListSSHKeys{Mode: mode} p.Entities.Entities = make([]params.Entity, len(users)) for i, userName := range users { p.Entities.Entities[i] = params.Entity{Tag: userName} } results := new(params.StringsResults) err := c.call("ListKeys", p, results) return results.Results, err } // AddKeys adds the authorised ssh keys for the specified user. func (c *Client) AddKeys(user string, keys ...string) ([]params.ErrorResult, error) { p := params.ModifyUserSSHKeys{User: user, Keys: keys} results := new(params.ErrorResults) err := c.call("AddKeys", p, results) return results.Results, err } // DeleteKeys deletes the authorised ssh keys for the specified user. func (c *Client) DeleteKeys(user string, keys ...string) ([]params.ErrorResult, error) { p := params.ModifyUserSSHKeys{User: user, Keys: keys} results := new(params.ErrorResults) err := c.call("DeleteKeys", p, results) return results.Results, err } // ImportKeys imports the authorised ssh keys with the specified key ids for the specified user. func (c *Client) ImportKeys(user string, keyIds ...string) ([]params.ErrorResult, error) { p := params.ModifyUserSSHKeys{User: user, Keys: keyIds} results := new(params.ErrorResults) err := c.call("ImportKeys", p, results) return results.Results, err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/base/����������������������������������������0000755�0000153�0000161�00000000000�12321735642�023470� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/base/caller.go�������������������������������0000644�0000153�0000161�00000000703�12321735642�025261� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package base // Caller is implemented by the client-facing State object. type Caller interface { // Call makes a call to the API server with the given object type, // id, request and parameters. The response is filled in with the // call's result if the call is successful. Call(objType, id, request string, params, response interface{}) error } �������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/allwatcher.go��������������������������������0000644�0000153�0000161�00000001362�12321735642�025235� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package api import ( "launchpad.net/juju-core/state/api/params" ) // AllWatcher holds information allowing us to get Deltas describing changes // to the entire environment. type AllWatcher struct { client *Client id *string } func newAllWatcher(client *Client, id *string) *AllWatcher { return &AllWatcher{client, id} } func (watcher *AllWatcher) Next() ([]params.Delta, error) { info := new(params.AllWatcherNextResults) err := watcher.client.st.Call("AllWatcher", *watcher.id, "Next", nil, info) return info.Deltas, err } func (watcher *AllWatcher) Stop() error { return watcher.client.st.Call("AllWatcher", *watcher.id, "Stop", nil, nil) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/agent/���������������������������������������0000755�0000153�0000161�00000000000�12321736000�023641� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/agent/unit_test.go���������������������������0000644�0000153�0000161�00000003105�12321735642�026220� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/utils" ) var _ = gc.Suite(&unitSuite{}) type unitSuite struct { testing.JujuConnSuite unit *state.Unit st *api.State } func (s *unitSuite) SetUpTest(c *gc.C) { var err error s.JujuConnSuite.SetUpTest(c) svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) s.unit, err = svc.AddUnit() c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = s.unit.SetPassword(password) s.st = s.OpenAPIAs(c, s.unit.Tag(), password) } func (s *unitSuite) TestUnitEntity(c *gc.C) { m, err := s.st.Agent().Entity("wordpress/1") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(m, gc.IsNil) m, err = s.st.Agent().Entity(s.unit.Tag()) c.Assert(err, gc.IsNil) c.Assert(m.Tag(), gc.Equals, s.unit.Tag()) c.Assert(m.Life(), gc.Equals, params.Alive) c.Assert(m.Jobs(), gc.HasLen, 0) err = s.unit.EnsureDead() c.Assert(err, gc.IsNil) err = s.unit.Remove() c.Assert(err, gc.IsNil) m, err = s.st.Agent().Entity(s.unit.Tag()) c.Assert(err, gc.ErrorMatches, fmt.Sprintf("unit %q not found", s.unit.Name())) c.Assert(err, jc.Satisfies, params.IsCodeNotFound) c.Assert(m, gc.IsNil) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/agent/machine_test.go������������������������0000644�0000153�0000161�00000005126�12321735776�026662� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent_test import ( "fmt" stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type machineSuite struct { testing.JujuConnSuite machine *state.Machine st *api.State } var _ = gc.Suite(&machineSuite{}) func (s *machineSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.st, s.machine = s.OpenAPIAsNewMachine(c) } func (s *machineSuite) TestMachineEntity(c *gc.C) { m, err := s.st.Agent().Entity("42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(m, gc.IsNil) m, err = s.st.Agent().Entity(s.machine.Tag()) c.Assert(err, gc.IsNil) c.Assert(m.Tag(), gc.Equals, s.machine.Tag()) c.Assert(m.Life(), gc.Equals, params.Alive) c.Assert(m.Jobs(), gc.DeepEquals, []params.MachineJob{params.JobHostUnits}) err = s.machine.EnsureDead() c.Assert(err, gc.IsNil) err = s.machine.Remove() c.Assert(err, gc.IsNil) m, err = s.st.Agent().Entity(s.machine.Tag()) c.Assert(err, gc.ErrorMatches, fmt.Sprintf("machine %s not found", s.machine.Id())) c.Assert(err, jc.Satisfies, params.IsCodeNotFound) c.Assert(m, gc.IsNil) } func (s *machineSuite) TestEntitySetPassword(c *gc.C) { entity, err := s.st.Agent().Entity(s.machine.Tag()) c.Assert(err, gc.IsNil) err = entity.SetPassword("foo") c.Assert(err, gc.ErrorMatches, "password is only 3 bytes long, and is not a valid Agent password") err = entity.SetPassword("foo-12345678901234567890") c.Assert(err, gc.IsNil) err = s.machine.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.machine.PasswordValid("bar"), gc.Equals, false) c.Assert(s.machine.PasswordValid("foo-12345678901234567890"), gc.Equals, true) // Check that we cannot log in to mongo with the correct password. // This is because there's no mongo password set for s.machine, // which has JobHostUnits info := s.StateInfo(c) info.Tag = entity.Tag() info.Password = "foo-12345678901234567890" err = tryOpenState(info) c.Assert(err, jc.Satisfies, errors.IsUnauthorizedError) } func tryOpenState(info *state.Info) error { st, err := state.Open(info, state.DialOpts{}, environs.NewStatePolicy()) if err == nil { st.Close() } return err } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/agent/state.go�������������������������������0000644�0000153�0000161�00000004520�12321735776�025334� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package agent import ( "fmt" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/params" ) // State provides access to an agent's view of the state. type State struct { caller base.Caller } // NewState returns a version of the state that provides functionality // required by agent code. func NewState(caller base.Caller) *State { return &State{caller} } func (st *State) getEntity(tag string) (*params.AgentGetEntitiesResult, error) { var results params.AgentGetEntitiesResults args := params.Entities{ Entities: []params.Entity{{Tag: tag}}, } err := st.caller.Call("Agent", "", "GetEntities", args, &results) if err != nil { return nil, err } if len(results.Entities) != 1 { return nil, fmt.Errorf("expected 1 result, got %d", len(results.Entities)) } if err := results.Entities[0].Error; err != nil { return nil, err } return &results.Entities[0], nil } type Entity struct { st *State tag string doc params.AgentGetEntitiesResult } func (st *State) Entity(tag string) (*Entity, error) { doc, err := st.getEntity(tag) if err != nil { return nil, err } return &Entity{ st: st, tag: tag, doc: *doc, }, nil } // Tag returns the entity's tag. func (m *Entity) Tag() string { return m.tag } // Life returns the current life cycle state of the entity. func (m *Entity) Life() params.Life { return m.doc.Life } // Jobs returns the set of configured jobs // if the API is running on behalf of a machine agent. // When running for other agents, it will return // the empty list. func (m *Entity) Jobs() []params.MachineJob { return m.doc.Jobs } // ContainerType returns the type of container hosting this entity. // If the entity is not a machine, it returns an empty string. func (m *Entity) ContainerType() instance.ContainerType { return m.doc.ContainerType } // SetPassword sets the password associated with the agent's entity. func (m *Entity) SetPassword(password string) error { var results params.ErrorResults args := params.EntityPasswords{ Changes: []params.EntityPassword{{ Tag: m.tag, Password: password, }}, } err := m.st.caller.Call("Agent", "", "SetPasswords", args, &results) if err != nil { return err } return results.OneError() } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/deployer/������������������������������������0000755�0000153�0000161�00000000000�12321736000�024366� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/deployer/deployer.go�������������������������0000644�0000153�0000161�00000003577�12321735642�026567� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer import ( "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/common" "launchpad.net/juju-core/state/api/params" ) const deployerFacade = "Deployer" // State provides access to the deployer worker's idea of the state. type State struct { caller base.Caller *common.APIAddresser } // NewState creates a new State instance that makes API calls // through the given caller. func NewState(caller base.Caller) *State { return &State{ APIAddresser: common.NewAPIAddresser(deployerFacade, caller), caller: caller, } } func (st *State) call(method string, params, result interface{}) error { return st.caller.Call(deployerFacade, "", method, params, result) } // unitLife returns the lifecycle state of the given unit. func (st *State) unitLife(tag string) (params.Life, error) { return common.Life(st.caller, deployerFacade, tag) } // Unit returns the unit with the given tag. func (st *State) Unit(tag string) (*Unit, error) { life, err := st.unitLife(tag) if err != nil { return nil, err } return &Unit{ tag: tag, life: life, st: st, }, nil } // Machine returns the machine with the given tag. func (st *State) Machine(tag string) (*Machine, error) { return &Machine{ tag: tag, st: st, }, nil } // StateAddresses returns the list of addresses used to connect to the state. func (st *State) StateAddresses() ([]string, error) { var result params.StringsResult err := st.call("StateAddresses", nil, &result) if err != nil { return nil, err } return result.Result, nil } // ConnectionInfo returns all the address information that the deployer task // needs in one call. func (st *State) ConnectionInfo() (result params.DeployerConnectionValues, err error) { err = st.call("ConnectionInfo", nil, &result) return result, err } ���������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/deployer/deployer_test.go��������������������0000644�0000153�0000161�00000017152�12321735776�027630� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer_test import ( stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/deployer" "launchpad.net/juju-core/state/api/params" apitesting "launchpad.net/juju-core/state/api/testing" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type deployerSuite struct { testing.JujuConnSuite *apitesting.APIAddresserTests stateAPI *api.State // These are raw State objects. Use them for setup and assertions, but // should never be touched by the API calls themselves machine *state.Machine service0 *state.Service service1 *state.Service principal *state.Unit subordinate *state.Unit st *deployer.State } var _ = gc.Suite(&deployerSuite{}) func (s *deployerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.stateAPI, s.machine = s.OpenAPIAsNewMachine(c, state.JobManageEnviron, state.JobHostUnits) err := s.machine.SetAddresses(instance.NewAddresses([]string{"0.1.2.3"})) c.Assert(err, gc.IsNil) // Create the needed services and relate them. s.service0 = s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) s.service1 = s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) eps, err := s.State.InferEndpoints([]string{"mysql", "logging"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) // Create principal and subordinate units and assign them. s.principal, err = s.service0.AddUnit() c.Assert(err, gc.IsNil) err = s.principal.AssignToMachine(s.machine) c.Assert(err, gc.IsNil) relUnit, err := rel.Unit(s.principal) c.Assert(err, gc.IsNil) err = relUnit.EnterScope(nil) c.Assert(err, gc.IsNil) s.subordinate, err = s.service1.Unit("logging/0") c.Assert(err, gc.IsNil) // Create the deployer facade. s.st = s.stateAPI.Deployer() c.Assert(s.st, gc.NotNil) s.APIAddresserTests = apitesting.NewAPIAddresserTests(s.st, s.BackingState) } // Note: This is really meant as a unit-test, this isn't a test that // should need all of the setup we have for this test suite func (s *deployerSuite) TestNew(c *gc.C) { deployer := deployer.NewState(s.stateAPI) c.Assert(deployer, gc.NotNil) } func (s *deployerSuite) assertUnauthorized(c *gc.C, err error) { c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) } func (s *deployerSuite) TestWatchUnitsWrongMachine(c *gc.C) { // Try with a non-existent machine tag. machine, err := s.st.Machine("machine-42") c.Assert(err, gc.IsNil) w, err := machine.WatchUnits() s.assertUnauthorized(c, err) c.Assert(w, gc.IsNil) // Try it with an invalid tag format. machine, err = s.st.Machine("foo") c.Assert(err, gc.IsNil) w, err = machine.WatchUnits() s.assertUnauthorized(c, err) c.Assert(w, gc.IsNil) } func (s *deployerSuite) TestWatchUnits(c *gc.C) { machine, err := s.st.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) w, err := machine.WatchUnits() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.BackingState, w) // Initial event. wc.AssertChange("mysql/0", "logging/0") wc.AssertNoChange() // Change something other than the lifecycle and make sure it's // not detected. err = s.subordinate.SetPassword("foo") c.Assert(err, gc.ErrorMatches, "password is only 3 bytes long, and is not a valid Agent password") wc.AssertNoChange() err = s.subordinate.SetPassword("foo-12345678901234567890") c.Assert(err, gc.IsNil) wc.AssertNoChange() // Make the subordinate dead and check it's detected. err = s.subordinate.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertChange("logging/0") wc.AssertNoChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *deployerSuite) TestUnit(c *gc.C) { // Try getting a missing unit and an invalid tag. unit, err := s.st.Unit("unit-foo-42") s.assertUnauthorized(c, err) c.Assert(unit, gc.IsNil) unit, err = s.st.Unit("42") s.assertUnauthorized(c, err) c.Assert(unit, gc.IsNil) // Try getting a unit we're not responsible for. // First create a new machine and deploy another unit there. machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) principal1, err := s.service0.AddUnit() c.Assert(err, gc.IsNil) err = principal1.AssignToMachine(machine) c.Assert(err, gc.IsNil) unit, err = s.st.Unit(principal1.Tag()) s.assertUnauthorized(c, err) c.Assert(unit, gc.IsNil) // Get the principal and subordinate we're responsible for. unit, err = s.st.Unit(s.principal.Tag()) c.Assert(err, gc.IsNil) c.Assert(unit.Name(), gc.Equals, "mysql/0") unit, err = s.st.Unit(s.subordinate.Tag()) c.Assert(err, gc.IsNil) c.Assert(unit.Name(), gc.Equals, "logging/0") } func (s *deployerSuite) TestUnitLifeRefresh(c *gc.C) { unit, err := s.st.Unit(s.subordinate.Tag()) c.Assert(err, gc.IsNil) c.Assert(unit.Life(), gc.Equals, params.Alive) // Now make it dead and check again, then refresh and check. err = s.subordinate.EnsureDead() c.Assert(err, gc.IsNil) err = s.subordinate.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.subordinate.Life(), gc.Equals, state.Dead) c.Assert(unit.Life(), gc.Equals, params.Alive) err = unit.Refresh() c.Assert(err, gc.IsNil) c.Assert(unit.Life(), gc.Equals, params.Dead) } func (s *deployerSuite) TestUnitRemove(c *gc.C) { unit, err := s.st.Unit(s.principal.Tag()) c.Assert(err, gc.IsNil) // It fails because the entity is still alive. // And EnsureDead will fail because there is a subordinate. err = unit.Remove() c.Assert(err, gc.ErrorMatches, `cannot remove entity "unit-mysql-0": still alive`) c.Assert(params.ErrCode(err), gc.Equals, "") // With the subordinate it also fails due to it being alive. unit, err = s.st.Unit(s.subordinate.Tag()) c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.ErrorMatches, `cannot remove entity "unit-logging-0": still alive`) c.Assert(params.ErrCode(err), gc.Equals, "") // Make it dead first and try again. err = s.subordinate.EnsureDead() c.Assert(err, gc.IsNil) err = unit.Remove() c.Assert(err, gc.IsNil) // Verify it's gone. err = unit.Refresh() s.assertUnauthorized(c, err) unit, err = s.st.Unit(s.subordinate.Tag()) s.assertUnauthorized(c, err) c.Assert(unit, gc.IsNil) } func (s *deployerSuite) TestUnitSetPassword(c *gc.C) { unit, err := s.st.Unit(s.principal.Tag()) c.Assert(err, gc.IsNil) // Change the principal's password and verify. err = unit.SetPassword("foobar-12345678901234567890") c.Assert(err, gc.IsNil) err = s.principal.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.principal.PasswordValid("foobar-12345678901234567890"), gc.Equals, true) // Then the subordinate. unit, err = s.st.Unit(s.subordinate.Tag()) c.Assert(err, gc.IsNil) err = unit.SetPassword("phony-12345678901234567890") c.Assert(err, gc.IsNil) err = s.subordinate.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.subordinate.PasswordValid("phony-12345678901234567890"), gc.Equals, true) } func (s *deployerSuite) TestStateAddresses(c *gc.C) { addrs := []instance.Address{ instance.NewAddress("0.1.2.3"), } err := s.machine.SetAddresses(addrs) c.Assert(err, gc.IsNil) stateAddresses, err := s.State.Addresses() c.Assert(err, gc.IsNil) c.Assert(len(stateAddresses), gc.Equals, 1) addresses, err := s.st.StateAddresses() c.Assert(err, gc.IsNil) c.Assert(addresses, gc.DeepEquals, stateAddresses) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/deployer/unit.go�����������������������������0000644�0000153�0000161�00000003063�12321735642�025711� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer import ( "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" ) // Unit represents a juju unit as seen by the deployer worker. type Unit struct { tag string life params.Life st *State } // Tag returns the unit's tag. func (u *Unit) Tag() string { return u.tag } // Name returns the unit's name. func (u *Unit) Name() string { _, name, err := names.ParseTag(u.tag, names.UnitTagKind) if err != nil { panic(err) } return name } // Life returns the unit's lifecycle value. func (u *Unit) Life() params.Life { return u.life } // Refresh updates the cached local copy of the unit's data. func (u *Unit) Refresh() error { life, err := u.st.unitLife(u.tag) if err != nil { return err } u.life = life return nil } // Remove removes the unit from state, calling EnsureDead first, then Remove. // It will fail if the unit is not present. func (u *Unit) Remove() error { var result params.ErrorResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("Remove", args, &result) if err != nil { return err } return result.OneError() } // SetPassword sets the unit's password. func (u *Unit) SetPassword(password string) error { var result params.ErrorResults args := params.EntityPasswords{ Changes: []params.EntityPassword{ {Tag: u.tag, Password: password}, }, } err := u.st.call("SetPasswords", args, &result) if err != nil { return err } return result.OneError() } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/deployer/machine.go��������������������������0000644�0000153�0000161�00000002000�12321735642�026324� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package deployer import ( "fmt" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // Machine represents a juju machine as seen by the deployer worker. type Machine struct { tag string st *State } // WatchUnits starts a StringsWatcher to watch all units deployed to // the machine, in order to track which ones should be deployed or // recalled. func (m *Machine) WatchUnits() (watcher.StringsWatcher, error) { var results params.StringsWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("WatchUnits", 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 } w := watcher.NewStringsWatcher(m.st.caller, result) return w, nil } juju-core_1.18.1/src/launchpad.net/juju-core/state/api/state_test.go��������������������������������0000644�0000153�0000161�00000001133�12321735642�025262� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package api_test import ( stdtesting "testing" gc "launchpad.net/gocheck" jujutesting "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type stateSuite struct { jujutesting.JujuConnSuite } var _ = gc.Suite(&stateSuite{}) func (s *stateSuite) TestCloseMultipleOk(c *gc.C) { c.Assert(s.APIState.Close(), gc.IsNil) c.Assert(s.APIState.Close(), gc.IsNil) c.Assert(s.APIState.Close(), gc.IsNil) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/apiclient.go���������������������������������0000644�0000153�0000161�00000012231�12321735776�025064� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package api import ( "crypto/tls" "crypto/x509" "time" "code.google.com/p/go.net/websocket" "launchpad.net/juju-core/cert" "launchpad.net/juju-core/log" "launchpad.net/juju-core/rpc" "launchpad.net/juju-core/rpc/jsoncodec" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/utils" ) // PingPeriod defines how often the internal connection health check // will run. It's a variable so it can be changed in tests. var PingPeriod = 1 * time.Minute type State struct { client *rpc.Conn conn *websocket.Conn addr string // authTag holds the authenticated entity's tag after login. authTag string // broken is a channel that gets closed when the connection is // broken. broken chan struct{} // tag and password hold the cached login credentials. tag string password string // serverRoot holds the cached API server address and port we used // to login, with a https:// prefix. serverRoot string } // Info encapsulates information about a server holding juju state and // can be used to make a connection to it. type Info struct { // Addrs holds the addresses of the state servers. Addrs []string // CACert holds the CA certificate that will be used // to validate the state server's certificate, in PEM format. CACert []byte // Tag holds the name of the entity that is connecting. // If this and the password are empty, no login attempt will be made // (this is to allow tests to access the API to check that operations // fail when not logged in). Tag string // Password holds the password for the administrator or connecting entity. Password string // Nonce holds the nonce used when provisioning the machine. Used // only by the machine agent. Nonce string `yaml:",omitempty"` } // DialOpts holds configuration parameters that control the // Dialing behavior when connecting to a state server. type DialOpts struct { // Timeout is the amount of time to wait contacting // a state server. Timeout time.Duration // RetryDelay is the amount of time to wait between // unsucssful connection attempts. RetryDelay time.Duration } // DefaultDialOpts returns a DialOpts representing the default // parameters for contacting a state server. func DefaultDialOpts() DialOpts { return DialOpts{ Timeout: 10 * time.Minute, RetryDelay: 2 * time.Second, } } func Open(info *Info, opts DialOpts) (*State, error) { // TODO Select a random address from info.Addrs // and only fail when we've tried all the addresses. // TODO what does "origin" really mean, and is localhost always ok? cfg, err := websocket.NewConfig("wss://"+info.Addrs[0]+"/", "http://localhost/") if err != nil { return nil, err } pool := x509.NewCertPool() xcert, err := cert.ParseCert(info.CACert) if err != nil { return nil, err } pool.AddCert(xcert) cfg.TlsConfig = &tls.Config{ RootCAs: pool, ServerName: "anything", } var conn *websocket.Conn openAttempt := utils.AttemptStrategy{ Total: opts.Timeout, Delay: opts.RetryDelay, } for a := openAttempt.Start(); a.Next(); { log.Infof("state/api: dialing %q", cfg.Location) conn, err = websocket.DialConfig(cfg) if err == nil { break } log.Errorf("state/api: %v", err) } if err != nil { return nil, err } log.Infof("state/api: connection established") client := rpc.NewConn(jsoncodec.NewWebsocket(conn), nil) client.Start() st := &State{ client: client, conn: conn, addr: cfg.Location.Host, serverRoot: "https://" + cfg.Location.Host, tag: info.Tag, password: info.Password, } if info.Tag != "" || info.Password != "" { if err := st.Login(info.Tag, info.Password, info.Nonce); err != nil { conn.Close() return nil, err } } st.broken = make(chan struct{}) go st.heartbeatMonitor() return st, nil } func (s *State) heartbeatMonitor() { for { if err := s.Ping(); err != nil { close(s.broken) return } time.Sleep(PingPeriod) } } func (s *State) Ping() error { return s.Call("Pinger", "", "Ping", nil, nil) } // Call invokes a low-level RPC method of the given objType, id, and // request, passing the given parameters and filling in the response // results. This should not be used directly by clients. // TODO (dimitern) Add tests for all client-facing objects to verify // we return the correct error when invoking Call("Object", // "non-empty-id",...) func (s *State) Call(objType, id, request string, args, response interface{}) error { err := s.client.Call(rpc.Request{ Type: objType, Id: id, Action: request, }, args, response) return params.ClientError(err) } func (s *State) Close() error { return s.client.Close() } // Broken returns a channel that's closed when the connection is broken. func (s *State) Broken() <-chan struct{} { return s.broken } // RPCClient returns the RPC client for the state, so that testing // functions can tickle parts of the API that the conventional entry // points don't reach. This is exported for testing purposes only. func (s *State) RPCClient() *rpc.Conn { return s.client } // Addr returns the address used to connect to the RPC server. func (s *State) Addr() string { return s.addr } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/testing/�������������������������������������0000755�0000153�0000161�00000000000�12321736000�024220� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/testing/environwatcher.go��������������������0000644�0000153�0000161�00000005742�12321735642�027630� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/watcher" statetesting "launchpad.net/juju-core/state/testing" ) const ( HasSecrets = true NoSecrets = false ) type EnvironWatcherFacade interface { WatchForEnvironConfigChanges() (watcher.NotifyWatcher, error) EnvironConfig() (*config.Config, error) } type EnvironWatcherTests struct { facade EnvironWatcherFacade state *state.State hasSecrets bool } func NewEnvironWatcherTests( facade EnvironWatcherFacade, st *state.State, hasSecrets bool) *EnvironWatcherTests { return &EnvironWatcherTests{ facade: facade, state: st, hasSecrets: hasSecrets, } } func (s *EnvironWatcherTests) TestEnvironConfig(c *gc.C) { envConfig, err := s.state.EnvironConfig() c.Assert(err, gc.IsNil) conf, err := s.facade.EnvironConfig() c.Assert(err, gc.IsNil) // If the facade doesn't have secrets, we need to replace the config // values in our environment to compare against with the secrets replaced. if !s.hasSecrets { env, err := environs.New(envConfig) c.Assert(err, gc.IsNil) secretAttrs, err := env.Provider().SecretAttrs(envConfig) c.Assert(err, gc.IsNil) secrets := make(map[string]interface{}) for key := range secretAttrs { secrets[key] = "not available" } envConfig, err = envConfig.Apply(secrets) c.Assert(err, gc.IsNil) } c.Assert(conf, jc.DeepEquals, envConfig) } func (s *EnvironWatcherTests) TestWatchForEnvironConfigChanges(c *gc.C) { envConfig, err := s.state.EnvironConfig() c.Assert(err, gc.IsNil) w, err := s.facade.WatchForEnvironConfigChanges() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.state, w) // Initial event. wc.AssertOneChange() // Change the environment configuration by updating an existing attribute, check it's detected. newAttrs := map[string]interface{}{"logging-config": "juju=ERROR"} err = s.state.UpdateEnvironConfig(newAttrs, nil, nil) c.Assert(err, gc.IsNil) wc.AssertOneChange() // Change the environment configuration by adding a new attribute, check it's detected. newAttrs = map[string]interface{}{"foo": "bar"} err = s.state.UpdateEnvironConfig(newAttrs, nil, nil) c.Assert(err, gc.IsNil) wc.AssertOneChange() // Change the environment configuration by removing an attribute, check it's detected. err = s.state.UpdateEnvironConfig(map[string]interface{}{}, []string{"foo"}, nil) c.Assert(err, gc.IsNil) wc.AssertOneChange() // Change it back to the original config. oldAttrs := map[string]interface{}{"logging-config": envConfig.AllAttrs()["logging-config"]} err = s.state.UpdateEnvironConfig(oldAttrs, nil, nil) c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } ������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/testing/apiaddresser.go����������������������0000644�0000153�0000161�00000004760�12321735776�027247� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/watcher" statetesting "launchpad.net/juju-core/state/testing" ) type APIAddresserTests struct { state *state.State facade APIAddresserFacade } func NewAPIAddresserTests(facade APIAddresserFacade, st *state.State) *APIAddresserTests { return &APIAddresserTests{ state: st, facade: facade, } } type APIAddresserFacade interface { APIAddresses() ([]string, error) CACert() ([]byte, error) APIHostPorts() ([][]instance.HostPort, error) WatchAPIHostPorts() (watcher.NotifyWatcher, error) } func (s *APIAddresserTests) TestAPIAddresses(c *gc.C) { apiAddresses, err := s.state.APIAddressesFromMachines() c.Assert(err, gc.IsNil) addresses, err := s.facade.APIAddresses() c.Assert(err, gc.IsNil) c.Assert(addresses, gc.DeepEquals, apiAddresses) } func (s *APIAddresserTests) TestAPIHostPorts(c *gc.C) { expectServerAddrs := [][]instance.HostPort{{{ Address: instance.NewAddress("0.1.2.24"), Port: 999, }, { Address: instance.NewAddress("example.com"), Port: 1234, }}, {{ Address: instance.Address{ Value: "2001:DB8::1", Type: instance.Ipv6Address, NetworkName: "someNetwork", NetworkScope: instance.NetworkCloudLocal, }, Port: 999, }}} err := s.state.SetAPIHostPorts(expectServerAddrs) c.Assert(err, gc.IsNil) serverAddrs, err := s.facade.APIHostPorts() c.Assert(err, gc.IsNil) c.Assert(serverAddrs, gc.DeepEquals, expectServerAddrs) } func (s *APIAddresserTests) TestCACert(c *gc.C) { caCert, err := s.facade.CACert() c.Assert(err, gc.IsNil) c.Assert(caCert, gc.DeepEquals, s.state.CACert()) } func (s *APIAddresserTests) TestWatchAPIHostPorts(c *gc.C) { expectServerAddrs := [][]instance.HostPort{{{ Address: instance.NewAddress("0.1.2.3"), Port: 1234, }}} err := s.state.SetAPIHostPorts(expectServerAddrs) c.Assert(err, gc.IsNil) w, err := s.facade.WatchAPIHostPorts() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.state, w) // Initial event. wc.AssertOneChange() // Change the state addresses and check that we get a notification expectServerAddrs[0][0].Value = "0.1.99.99" err = s.state.SetAPIHostPorts(expectServerAddrs) c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } ����������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/firewaller/����������������������������������0000755�0000153�0000161�00000000000�12321735643�024713� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/firewaller/service_test.go�������������������0000644�0000153�0000161�00000004155�12321735642�027745� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/state/api/firewaller" "launchpad.net/juju-core/state/api/params" statetesting "launchpad.net/juju-core/state/testing" ) type serviceSuite struct { firewallerSuite apiService *firewaller.Service } var _ = gc.Suite(&serviceSuite{}) func (s *serviceSuite) SetUpTest(c *gc.C) { s.firewallerSuite.SetUpTest(c) var err error apiUnit, err := s.firewaller.Unit(s.units[0].Tag()) s.apiService, err = apiUnit.Service() c.Assert(err, gc.IsNil) } func (s *serviceSuite) TearDownTest(c *gc.C) { s.firewallerSuite.TearDownTest(c) } func (s *serviceSuite) TestName(c *gc.C) { c.Assert(s.apiService.Name(), gc.Equals, s.service.Name()) } func (s *serviceSuite) TestWatch(c *gc.C) { c.Assert(s.apiService.Life(), gc.Equals, params.Alive) w, err := s.apiService.Watch() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w) // Initial event. wc.AssertOneChange() // Change something and check it's detected. err = s.service.SetExposed() c.Assert(err, gc.IsNil) wc.AssertOneChange() // Destroy the service and check it's detected. err = s.service.Destroy() c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *serviceSuite) TestRefresh(c *gc.C) { c.Assert(s.apiService.Life(), gc.Equals, params.Alive) err := s.service.Destroy() c.Assert(err, gc.IsNil) c.Assert(s.apiService.Life(), gc.Equals, params.Alive) err = s.apiService.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.apiService.Life(), gc.Equals, params.Dying) } func (s *serviceSuite) TestIsExposed(c *gc.C) { err := s.service.SetExposed() c.Assert(err, gc.IsNil) isExposed, err := s.apiService.IsExposed() c.Assert(err, gc.IsNil) c.Assert(isExposed, jc.IsTrue) err = s.service.ClearExposed() c.Assert(err, gc.IsNil) isExposed, err = s.apiService.IsExposed() c.Assert(err, gc.IsNil) c.Assert(isExposed, jc.IsFalse) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/firewaller/unit_test.go����������������������0000644�0000153�0000161�00000006421�12321735642�027262� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state/api/firewaller" "launchpad.net/juju-core/state/api/params" statetesting "launchpad.net/juju-core/state/testing" ) type unitSuite struct { firewallerSuite apiUnit *firewaller.Unit } var _ = gc.Suite(&unitSuite{}) func (s *unitSuite) SetUpTest(c *gc.C) { s.firewallerSuite.SetUpTest(c) var err error s.apiUnit, err = s.firewaller.Unit(s.units[0].Tag()) c.Assert(err, gc.IsNil) } func (s *unitSuite) TearDownTest(c *gc.C) { s.firewallerSuite.TearDownTest(c) } func (s *unitSuite) TestUnit(c *gc.C) { apiUnitFoo, err := s.firewaller.Unit("unit-foo-42") c.Assert(err, gc.ErrorMatches, `unit "foo/42" not found`) c.Assert(err, jc.Satisfies, params.IsCodeNotFound) c.Assert(apiUnitFoo, gc.IsNil) apiUnit0, err := s.firewaller.Unit(s.units[0].Tag()) c.Assert(err, gc.IsNil) c.Assert(apiUnit0, gc.NotNil) c.Assert(apiUnit0.Name(), gc.Equals, s.units[0].Name()) } func (s *unitSuite) TestRefresh(c *gc.C) { c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive) err := s.units[0].EnsureDead() c.Assert(err, gc.IsNil) c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive) err = s.apiUnit.Refresh() c.Assert(err, gc.IsNil) c.Assert(s.apiUnit.Life(), gc.Equals, params.Dead) } func (s *unitSuite) TestWatch(c *gc.C) { c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive) w, err := s.apiUnit.Watch() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w) // Initial event. wc.AssertOneChange() // Change something other than the life cycle and make sure it's // not detected. err = s.units[0].SetStatus(params.StatusStarted, "not really", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Make the unit dead and check it's detected. err = s.units[0].EnsureDead() c.Assert(err, gc.IsNil) wc.AssertOneChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *unitSuite) TestAssignedMachine(c *gc.C) { machineTag, err := s.apiUnit.AssignedMachine() c.Assert(err, gc.IsNil) c.Assert(machineTag, gc.Equals, s.machines[0].Tag()) // Unassign now and check CodeNotAssigned is reported. err = s.units[0].UnassignFromMachine() c.Assert(err, gc.IsNil) _, err = s.apiUnit.AssignedMachine() c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is not assigned to a machine`) c.Assert(err, jc.Satisfies, params.IsCodeNotAssigned) } func (s *unitSuite) TestOpenedPorts(c *gc.C) { ports, err := s.apiUnit.OpenedPorts() c.Assert(err, gc.IsNil) c.Assert(ports, jc.DeepEquals, []instance.Port{}) // Open some ports and check again. err = s.units[0].OpenPort("foo", 1234) c.Assert(err, gc.IsNil) err = s.units[0].OpenPort("bar", 4321) c.Assert(err, gc.IsNil) ports, err = s.apiUnit.OpenedPorts() c.Assert(err, gc.IsNil) c.Assert(ports, jc.DeepEquals, []instance.Port{{"bar", 4321}, {"foo", 1234}}) } func (s *unitSuite) TestService(c *gc.C) { service, err := s.apiUnit.Service() c.Assert(err, gc.IsNil) c.Assert(service.Name(), gc.Equals, s.service.Name()) } func (s *unitSuite) TestName(c *gc.C) { c.Assert(s.apiUnit.Name(), gc.Equals, s.units[0].Name()) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/firewaller/machine_test.go�������������������0000644�0000153�0000161�00000005113�12321735642�027704� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api/firewaller" "launchpad.net/juju-core/state/api/params" statetesting "launchpad.net/juju-core/state/testing" ) type machineSuite struct { firewallerSuite apiMachine *firewaller.Machine } var _ = gc.Suite(&machineSuite{}) func (s *machineSuite) SetUpTest(c *gc.C) { s.firewallerSuite.SetUpTest(c) var err error s.apiMachine, err = s.firewaller.Machine(s.machines[0].Tag()) c.Assert(err, gc.IsNil) } func (s *machineSuite) TearDownTest(c *gc.C) { s.firewallerSuite.TearDownTest(c) } func (s *machineSuite) TestMachine(c *gc.C) { apiMachine42, err := s.firewaller.Machine("machine-42") c.Assert(err, gc.ErrorMatches, "machine 42 not found") c.Assert(err, jc.Satisfies, params.IsCodeNotFound) c.Assert(apiMachine42, gc.IsNil) apiMachine0, err := s.firewaller.Machine(s.machines[0].Tag()) c.Assert(err, gc.IsNil) c.Assert(apiMachine0, gc.NotNil) } func (s *machineSuite) TestInstanceId(c *gc.C) { // Add another, not provisioned machine to test // CodeNotProvisioned. newMachine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) apiNewMachine, err := s.firewaller.Machine(newMachine.Tag()) c.Assert(err, gc.IsNil) _, err = apiNewMachine.InstanceId() c.Assert(err, gc.ErrorMatches, "machine 3 is not provisioned") c.Assert(err, jc.Satisfies, params.IsCodeNotProvisioned) instanceId, err := s.apiMachine.InstanceId() c.Assert(err, gc.IsNil) c.Assert(instanceId, gc.Equals, instance.Id("i-manager")) } func (s *machineSuite) TestWatchUnits(c *gc.C) { w, err := s.apiMachine.WatchUnits() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.BackingState, w) // Initial event. wc.AssertChange("wordpress/0") wc.AssertNoChange() // Change something other than the life cycle and make sure it's // not detected. err = s.machines[0].SetPassword("foo") c.Assert(err, gc.ErrorMatches, "password is only 3 bytes long, and is not a valid Agent password") wc.AssertNoChange() err = s.machines[0].SetPassword("foo-12345678901234567890") c.Assert(err, gc.IsNil) wc.AssertNoChange() // Unassign unit 0 from the machine and check it's detected. err = s.units[0].UnassignFromMachine() c.Assert(err, gc.IsNil) wc.AssertChange("wordpress/0") wc.AssertNoChange() statetesting.AssertStop(c, w) wc.AssertClosed() } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/firewaller/firewaller.go���������������������0000644�0000153�0000161�00000003747�12321735642�027410� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller import ( "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/common" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) const firewallerFacade = "Firewaller" // State provides access to the Firewaller API facade. type State struct { caller base.Caller *common.EnvironWatcher } func (st *State) call(method string, params, result interface{}) error { return st.caller.Call(firewallerFacade, "", method, params, result) } // NewState creates a new client-side Firewaller facade. func NewState(caller base.Caller) *State { return &State{ caller: caller, EnvironWatcher: common.NewEnvironWatcher(firewallerFacade, caller), } } // life requests the life cycle of the given entity from the server. func (st *State) life(tag string) (params.Life, error) { return common.Life(st.caller, firewallerFacade, tag) } // Unit provides access to methods of a state.Unit through the facade. func (st *State) Unit(tag string) (*Unit, error) { life, err := st.life(tag) if err != nil { return nil, err } return &Unit{ tag: tag, life: life, st: st, }, nil } // Machine provides access to methods of a state.Machine through the // facade. func (st *State) Machine(tag string) (*Machine, error) { life, err := st.life(tag) if err != nil { return nil, err } return &Machine{ tag: tag, life: life, st: st, }, nil } // WatchEnvironMachines returns a StringsWatcher that notifies of // changes to the life cycles of the top level machines in the current // environment. func (st *State) WatchEnvironMachines() (watcher.StringsWatcher, error) { var result params.StringsWatchResult err := st.call("WatchEnvironMachines", nil, &result) if err != nil { return nil, err } if err := result.Error; err != nil { return nil, result.Error } w := watcher.NewStringsWatcher(st.caller, result) return w, nil } �������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/firewaller/service.go������������������������0000644�0000153�0000161�00000004373�12321735642�026710� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller import ( "fmt" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // Service represents the state of a service. type Service struct { st *State tag string life params.Life } // Name returns the service name. func (s *Service) Name() string { _, serviceName, err := names.ParseTag(s.tag, names.ServiceTagKind) if err != nil { panic(fmt.Sprintf("%q is not a valid service tag", s.tag)) } return serviceName } // Watch returns a watcher for observing changes to a service. func (s *Service) Watch() (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: s.tag}}, } err := s.st.call("Watch", 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 } w := watcher.NewNotifyWatcher(s.st.caller, result) return w, nil } // Life returns the service's current life state. func (s *Service) Life() params.Life { return s.life } // Refresh refreshes the contents of the Service from the underlying // state. func (s *Service) Refresh() error { life, err := s.st.life(s.tag) if err != nil { return err } s.life = life return nil } // IsExposed returns whether this service is exposed. The explicitly // open ports (with open-port) for exposed services may be accessed // from machines outside of the local deployment network. // // NOTE: This differs from state.Service.IsExposed() by returning // an error as well, because it needs to make an API call. func (s *Service) IsExposed() (bool, error) { var results params.BoolResults args := params.Entities{ Entities: []params.Entity{{Tag: s.tag}}, } err := s.st.call("GetExposed", args, &results) if err != nil { return false, err } if len(results.Results) != 1 { return false, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return false, result.Error } return result.Result, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/firewaller/firewaller_test.go����������������0000644�0000153�0000161�00000004521�12321735642�030436� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller_test import ( stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/firewaller" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) // NOTE: This suite is intended for embedding into other suites, // so common code can be reused. Do not add test cases to it, // otherwise they'll be run by each other suite that embeds it. type firewallerSuite struct { testing.JujuConnSuite st *api.State machines []*state.Machine service *state.Service charm *state.Charm units []*state.Unit firewaller *firewaller.State } var _ = gc.Suite(&firewallerSuite{}) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } func (s *firewallerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) // Reset previous machines and units (if any) and create 3 // machines for the tests. The first one is a manager node. s.machines = make([]*state.Machine, 3) s.units = make([]*state.Unit, 3) var err error s.machines[0], err = s.State.AddMachine("quantal", state.JobManageEnviron, state.JobHostUnits) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = s.machines[0].SetPassword(password) c.Assert(err, gc.IsNil) err = s.machines[0].SetProvisioned("i-manager", "fake_nonce", nil) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAsMachine(c, s.machines[0].Tag(), password, "fake_nonce") c.Assert(s.st, gc.NotNil) // Note that the specific machine ids allocated are assumed // to be numerically consecutive from zero. for i := 1; i <= 2; i++ { s.machines[i], err = s.State.AddMachine("quantal", state.JobHostUnits) c.Check(err, gc.IsNil) } // Create a service and three units for these machines. s.charm = s.AddTestingCharm(c, "wordpress") s.service = s.AddTestingService(c, "wordpress", s.charm) // Add the rest of the units and assign them. for i := 0; i <= 2; i++ { s.units[i], err = s.service.AddUnit() c.Check(err, gc.IsNil) err = s.units[i].AssignToMachine(s.machines[i]) c.Check(err, gc.IsNil) } // Create the firewaller API facade. s.firewaller = s.st.Firewaller() c.Assert(s.firewaller, gc.NotNil) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/firewaller/state_test.go���������������������0000644�0000153�0000161�00000003247�12321735642�027426� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state" apitesting "launchpad.net/juju-core/state/api/testing" statetesting "launchpad.net/juju-core/state/testing" ) type stateSuite struct { firewallerSuite *apitesting.EnvironWatcherTests } var _ = gc.Suite(&stateSuite{}) func (s *stateSuite) SetUpTest(c *gc.C) { s.firewallerSuite.SetUpTest(c) s.EnvironWatcherTests = apitesting.NewEnvironWatcherTests(s.firewaller, s.BackingState, true) } func (s *stateSuite) TearDownTest(c *gc.C) { s.firewallerSuite.TearDownTest(c) } func (s *stateSuite) TestWatchEnvironMachines(c *gc.C) { w, err := s.firewaller.WatchEnvironMachines() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.BackingState, w) // Initial event. wc.AssertChange(s.machines[0].Id(), s.machines[1].Id(), s.machines[2].Id()) // Add another machine make sure they are detected. otherMachine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) wc.AssertChange(otherMachine.Id()) // Change the life cycle of last machine. err = otherMachine.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertChange(otherMachine.Id()) // Add a container and make sure it's not detected. template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } _, err = s.State.AddMachineInsideMachine(template, s.machines[0].Id(), instance.LXC) c.Assert(err, gc.IsNil) wc.AssertNoChange() statetesting.AssertStop(c, w) wc.AssertClosed() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/firewaller/unit.go���������������������������0000644�0000153�0000161�00000006111�12321735642�026217� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package firewaller import ( "fmt" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // Unit represents a juju unit as seen by a firewaller worker. type Unit struct { st *State tag string life params.Life } // Name returns the name of the unit. func (u *Unit) Name() string { _, unitName, err := names.ParseTag(u.tag, names.UnitTagKind) if err != nil { panic(fmt.Sprintf("%q is not a valid unit tag", u.tag)) } return unitName } // Life returns the unit's life cycle value. func (u *Unit) Life() params.Life { return u.life } // Refresh updates the cached local copy of the unit's data. func (u *Unit) Refresh() error { life, err := u.st.life(u.tag) if err != nil { return err } u.life = life return nil } // Watch returns a watcher for observing changes to the unit. func (u *Unit) Watch() (watcher.NotifyWatcher, error) { var results params.NotifyWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("Watch", 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 } w := watcher.NewNotifyWatcher(u.st.caller, result) return w, nil } // Service returns the service. func (u *Unit) Service() (*Service, error) { serviceTag := names.ServiceTag(names.UnitService(u.Name())) service := &Service{ st: u.st, tag: serviceTag, } // Call Refresh() immediately to get the up-to-date // life and other needed locally cached fields. err := service.Refresh() if err != nil { return nil, err } return service, nil } // OpenedPorts returns the list of opened ports for this unit. // // NOTE: This differs from state.Unit.OpenedPorts() by returning // an error as well, because it needs to make an API call. func (u *Unit) OpenedPorts() ([]instance.Port, error) { var results params.PortsResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("OpenedPorts", 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.Ports, nil } // AssignedMachine returns the tag of this unit's assigned machine (if // any), or a CodeNotAssigned error. func (u *Unit) AssignedMachine() (string, error) { var results params.StringResults args := params.Entities{ Entities: []params.Entity{{Tag: u.tag}}, } err := u.st.call("GetAssignedMachine", args, &results) if err != nil { return "", err } if len(results.Results) != 1 { return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return "", result.Error } return result.Result, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/firewaller/machine.go������������������������0000644�0000153�0000161�00000003231�12321735642�026644� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Licensed under the AGPLv3, see LICENCE file for details. package firewaller import ( "fmt" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // Machine represents a juju machine as seen by the firewaller worker. type Machine struct { st *State tag string life params.Life } // WatchUnits starts a StringsWatcher to watch all units assigned to // the machine. func (m *Machine) WatchUnits() (watcher.StringsWatcher, error) { var results params.StringsWatchResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("WatchUnits", 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 } w := watcher.NewStringsWatcher(m.st.caller, result) return w, nil } // InstanceId returns the provider specific instance id for this // machine, or a CodeNotProvisioned error, if not set. func (m *Machine) InstanceId() (instance.Id, error) { var results params.StringResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("InstanceId", args, &results) if err != nil { return "", err } if len(results.Results) != 1 { return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return "", result.Error } return instance.Id(result.Result), nil } // Life returns the machine's life cycle value. func (m *Machine) Life() params.Life { return m.life } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/provisioner/���������������������������������0000755�0000153�0000161�00000000000�12321736000�025122� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/provisioner/provisioner.go�������������������0000644�0000153�0000161�00000010021�12321735642�030035� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner import ( "fmt" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/base" "launchpad.net/juju-core/state/api/common" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" "launchpad.net/juju-core/tools" ) // State provides access to the Machiner API facade. type State struct { *common.EnvironWatcher *common.APIAddresser caller base.Caller } const provisionerFacade = "Provisioner" // NewState creates a new client-side Machiner facade. func NewState(caller base.Caller) *State { return &State{ EnvironWatcher: common.NewEnvironWatcher(provisionerFacade, caller), APIAddresser: common.NewAPIAddresser(provisionerFacade, caller), caller: caller} } func (st *State) call(method string, params, result interface{}) error { return st.caller.Call(provisionerFacade, "", method, params, result) } // machineLife requests the lifecycle of the given machine from the server. func (st *State) machineLife(tag string) (params.Life, error) { return common.Life(st.caller, provisionerFacade, tag) } // Machine provides access to methods of a state.Machine through the facade. func (st *State) Machine(tag string) (*Machine, error) { life, err := st.machineLife(tag) if err != nil { return nil, err } return &Machine{ tag: tag, life: life, st: st, }, nil } // WatchEnvironMachines returns a StringsWatcher that notifies of // changes to the lifecycles of the machines (but not containers) in // the current environment. func (st *State) WatchEnvironMachines() (watcher.StringsWatcher, error) { var result params.StringsWatchResult err := st.call("WatchEnvironMachines", nil, &result) if err != nil { return nil, err } if err := result.Error; err != nil { return nil, result.Error } w := watcher.NewStringsWatcher(st.caller, result) return w, nil } func (st *State) WatchMachineErrorRetry() (watcher.NotifyWatcher, error) { var result params.NotifyWatchResult err := st.call("WatchMachineErrorRetry", nil, &result) if err != nil { return nil, err } if err := result.Error; err != nil { return nil, result.Error } w := watcher.NewNotifyWatcher(st.caller, result) return w, nil } // StateAddresses returns the list of addresses used to connect to the state. func (st *State) StateAddresses() ([]string, error) { var result params.StringsResult err := st.call("StateAddresses", nil, &result) if err != nil { return nil, err } return result.Result, nil } // Tools returns the agent tools for the given entity. func (st *State) Tools(tag string) (*tools.Tools, error) { var results params.ToolsResults args := params.Entities{ Entities: []params.Entity{{Tag: tag}}, } err := st.call("Tools", args, &results) if err != nil { // TODO: Not directly tested return nil, err } if len(results.Results) != 1 { // TODO: Not directly tested return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if err := result.Error; err != nil { return nil, err } return result.Tools, nil } // ContainerConfig returns information from the environment config that are // needed for container cloud-init. func (st *State) ContainerConfig() (result params.ContainerConfig, err error) { err = st.call("ContainerConfig", nil, &result) return result, err } // MachinesWithTransientErrors returns a slice of machines and corresponding status information // for those machines which have transient provisioning errors. func (st *State) MachinesWithTransientErrors() ([]*Machine, []params.StatusResult, error) { var results params.StatusResults err := st.call("MachinesWithTransientErrors", nil, &results) if err != nil { return nil, nil, err } machines := make([]*Machine, len(results.Results)) for i, status := range results.Results { if status.Error != nil { continue } machines[i] = &Machine{ tag: names.MachineTag(status.Id), life: status.Life, st: st, } } return machines, results.Results, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/provisioner/provisioner_test.go��������������0000644�0000153�0000161�00000033621�12321735776�031117� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner_test import ( stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/provisioner" apitesting "launchpad.net/juju-core/state/api/testing" statetesting "launchpad.net/juju-core/state/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) func TestAll(t *stdtesting.T) { coretesting.MgoTestPackage(t) } type provisionerSuite struct { testing.JujuConnSuite *apitesting.EnvironWatcherTests *apitesting.APIAddresserTests st *api.State machine *state.Machine provisioner *provisioner.State } var _ = gc.Suite(&provisionerSuite{}) func (s *provisionerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) var err error s.machine, err = s.State.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) err = s.machine.SetPassword(password) c.Assert(err, gc.IsNil) err = s.machine.SetProvisioned("i-manager", "fake_nonce", nil) c.Assert(err, gc.IsNil) s.st = s.OpenAPIAsMachine(c, s.machine.Tag(), password, "fake_nonce") c.Assert(s.st, gc.NotNil) err = s.machine.SetAddresses(instance.NewAddresses([]string{"0.1.2.3"})) c.Assert(err, gc.IsNil) // Create the provisioner API facade. s.provisioner = s.st.Provisioner() c.Assert(s.provisioner, gc.NotNil) s.EnvironWatcherTests = apitesting.NewEnvironWatcherTests(s.provisioner, s.BackingState, apitesting.HasSecrets) s.APIAddresserTests = apitesting.NewAPIAddresserTests(s.provisioner, s.BackingState) } func (s *provisionerSuite) TestMachineTagAndId(c *gc.C) { apiMachine, err := s.provisioner.Machine("machine-42") c.Assert(err, gc.ErrorMatches, "machine 42 not found") c.Assert(err, jc.Satisfies, params.IsCodeNotFound) c.Assert(apiMachine, gc.IsNil) apiMachine, err = s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) c.Assert(apiMachine.Tag(), gc.Equals, s.machine.Tag()) c.Assert(apiMachine.Id(), gc.Equals, s.machine.Id()) } func (s *provisionerSuite) TestGetSetStatus(c *gc.C) { apiMachine, err := s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) status, info, err := apiMachine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusPending) c.Assert(info, gc.Equals, "") err = apiMachine.SetStatus(params.StatusStarted, "blah", nil) c.Assert(err, gc.IsNil) status, info, err = apiMachine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusStarted) c.Assert(info, gc.Equals, "blah") _, _, data, err := s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(data, gc.HasLen, 0) } func (s *provisionerSuite) TestGetSetStatusWithData(c *gc.C) { apiMachine, err := s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) err = apiMachine.SetStatus(params.StatusError, "blah", params.StatusData{"foo": "bar"}) c.Assert(err, gc.IsNil) status, info, err := apiMachine.Status() c.Assert(err, gc.IsNil) c.Assert(status, gc.Equals, params.StatusError) c.Assert(info, gc.Equals, "blah") _, _, data, err := s.machine.Status() c.Assert(err, gc.IsNil) c.Assert(data, gc.DeepEquals, params.StatusData{"foo": "bar"}) } func (s *provisionerSuite) TestMachinesWithTransientErrors(c *gc.C) { machine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) err = machine.SetStatus(params.StatusError, "blah", params.StatusData{"transient": true}) c.Assert(err, gc.IsNil) machines, info, err := s.provisioner.MachinesWithTransientErrors() c.Assert(err, gc.IsNil) c.Assert(machines, gc.HasLen, 1) c.Assert(machines[0].Id(), gc.Equals, "1") c.Assert(info, gc.HasLen, 1) c.Assert(info[0], gc.DeepEquals, params.StatusResult{ Id: "1", Life: "alive", Status: "error", Info: "blah", Data: params.StatusData{"transient": true}, }) } func (s *provisionerSuite) TestEnsureDeadAndRemove(c *gc.C) { // Create a fresh machine to test the complete scenario. otherMachine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(otherMachine.Life(), gc.Equals, state.Alive) apiMachine, err := s.provisioner.Machine(otherMachine.Tag()) c.Assert(err, gc.IsNil) err = apiMachine.Remove() c.Assert(err, gc.ErrorMatches, `cannot remove entity "machine-1": still alive`) err = apiMachine.EnsureDead() c.Assert(err, gc.IsNil) err = otherMachine.Refresh() c.Assert(err, gc.IsNil) c.Assert(otherMachine.Life(), gc.Equals, state.Dead) err = apiMachine.EnsureDead() c.Assert(err, gc.IsNil) err = otherMachine.Refresh() c.Assert(err, gc.IsNil) c.Assert(otherMachine.Life(), gc.Equals, state.Dead) err = apiMachine.Remove() c.Assert(err, gc.IsNil) err = otherMachine.Refresh() c.Assert(err, jc.Satisfies, errors.IsNotFoundError) err = apiMachine.EnsureDead() c.Assert(err, gc.ErrorMatches, "machine 1 not found") c.Assert(err, jc.Satisfies, params.IsCodeNotFound) // Now try to EnsureDead machine 0 - should fail. apiMachine, err = s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) err = apiMachine.EnsureDead() c.Assert(err, gc.ErrorMatches, "machine 0 is required by the environment") } func (s *provisionerSuite) TestRefreshAndLife(c *gc.C) { // Create a fresh machine to test the complete scenario. otherMachine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) c.Assert(otherMachine.Life(), gc.Equals, state.Alive) apiMachine, err := s.provisioner.Machine(otherMachine.Tag()) c.Assert(err, gc.IsNil) c.Assert(apiMachine.Life(), gc.Equals, params.Alive) err = apiMachine.EnsureDead() c.Assert(err, gc.IsNil) c.Assert(apiMachine.Life(), gc.Equals, params.Alive) err = apiMachine.Refresh() c.Assert(err, gc.IsNil) c.Assert(apiMachine.Life(), gc.Equals, params.Dead) } func (s *provisionerSuite) TestSetProvisionedAndInstanceId(c *gc.C) { // Create a fresh machine, since machine 0 is already provisioned. notProvisionedMachine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) apiMachine, err := s.provisioner.Machine(notProvisionedMachine.Tag()) c.Assert(err, gc.IsNil) instanceId, err := apiMachine.InstanceId() c.Assert(err, jc.Satisfies, params.IsCodeNotProvisioned) c.Assert(err, gc.ErrorMatches, "machine 1 is not provisioned") c.Assert(instanceId, gc.Equals, instance.Id("")) hwChars := instance.MustParseHardware("cpu-cores=123", "mem=4G") err = apiMachine.SetProvisioned("i-will", "fake_nonce", &hwChars) c.Assert(err, gc.IsNil) instanceId, err = apiMachine.InstanceId() c.Assert(err, gc.IsNil) c.Assert(instanceId, gc.Equals, instance.Id("i-will")) // Try it again - should fail. err = apiMachine.SetProvisioned("i-wont", "fake", nil) c.Assert(err, gc.ErrorMatches, `cannot set instance data for machine "1": already set`) // Now try to get machine 0's instance id. apiMachine, err = s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) instanceId, err = apiMachine.InstanceId() c.Assert(err, gc.IsNil) c.Assert(instanceId, gc.Equals, instance.Id("i-manager")) } func (s *provisionerSuite) TestSeries(c *gc.C) { // Create a fresh machine with different series. foobarMachine, err := s.State.AddMachine("foobar", state.JobHostUnits) c.Assert(err, gc.IsNil) apiMachine, err := s.provisioner.Machine(foobarMachine.Tag()) c.Assert(err, gc.IsNil) series, err := apiMachine.Series() c.Assert(err, gc.IsNil) c.Assert(series, gc.Equals, "foobar") // Now try machine 0. apiMachine, err = s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) series, err = apiMachine.Series() c.Assert(err, gc.IsNil) c.Assert(series, gc.Equals, "quantal") } func (s *provisionerSuite) TestConstraints(c *gc.C) { // Create a fresh machine with some constraints. template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, Constraints: constraints.MustParse("cpu-cores=12", "mem=8G"), } consMachine, err := s.State.AddOneMachine(template) c.Assert(err, gc.IsNil) apiMachine, err := s.provisioner.Machine(consMachine.Tag()) c.Assert(err, gc.IsNil) cons, err := apiMachine.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, template.Constraints) // Now try machine 0. apiMachine, err = s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) cons, err = apiMachine.Constraints() c.Assert(err, gc.IsNil) c.Assert(cons, gc.DeepEquals, constraints.Value{}) } func (s *provisionerSuite) TestWatchContainers(c *gc.C) { apiMachine, err := s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) // Add one LXC container. template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } container, err := s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) w, err := apiMachine.WatchContainers(instance.LXC) c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.BackingState, w) // Initial event. wc.AssertChange(container.Id()) // Change something other than the containers and make sure it's // not detected. err = apiMachine.SetStatus(params.StatusStarted, "not really", nil) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Add a KVM container and make sure it's not detected. container, err = s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.KVM) c.Assert(err, gc.IsNil) wc.AssertNoChange() // Add another LXC container and make sure it's detected. container, err = s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) wc.AssertChange(container.Id()) statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *provisionerSuite) TestWatchContainersAcceptsSupportedContainers(c *gc.C) { apiMachine, err := s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) for _, ctype := range instance.ContainerTypes { w, err := apiMachine.WatchContainers(ctype) c.Assert(w, gc.NotNil) c.Assert(err, gc.IsNil) } } func (s *provisionerSuite) TestWatchContainersErrors(c *gc.C) { apiMachine, err := s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) _, err = apiMachine.WatchContainers(instance.NONE) c.Assert(err, gc.ErrorMatches, `unsupported container type "none"`) _, err = apiMachine.WatchContainers("") c.Assert(err, gc.ErrorMatches, "container type must be specified") } func (s *provisionerSuite) TestWatchEnvironMachines(c *gc.C) { w, err := s.provisioner.WatchEnvironMachines() c.Assert(err, gc.IsNil) defer statetesting.AssertStop(c, w) wc := statetesting.NewStringsWatcherC(c, s.BackingState, w) // Initial event. wc.AssertChange(s.machine.Id()) // Add another 2 machines make sure they are detected. otherMachine, err := s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) otherMachine, err = s.State.AddMachine("quantal", state.JobHostUnits) c.Assert(err, gc.IsNil) wc.AssertChange("1", "2") // Change the lifecycle of last machine. err = otherMachine.EnsureDead() c.Assert(err, gc.IsNil) wc.AssertChange("2") // Add a container and make sure it's not detected. template := state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, } _, err = s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.LXC) c.Assert(err, gc.IsNil) wc.AssertNoChange() statetesting.AssertStop(c, w) wc.AssertClosed() } func (s *provisionerSuite) TestStateAddresses(c *gc.C) { err := s.machine.SetAddresses([]instance.Address{ instance.NewAddress("0.1.2.3"), }) c.Assert(err, gc.IsNil) stateAddresses, err := s.State.Addresses() c.Assert(err, gc.IsNil) addresses, err := s.provisioner.StateAddresses() c.Assert(err, gc.IsNil) c.Assert(addresses, gc.DeepEquals, stateAddresses) } func (s *provisionerSuite) TestContainerConfig(c *gc.C) { result, err := s.provisioner.ContainerConfig() c.Assert(err, gc.IsNil) c.Assert(result.ProviderType, gc.Equals, "dummy") c.Assert(result.AuthorizedKeys, gc.Equals, coretesting.FakeAuthKeys) c.Assert(result.SSLHostnameVerification, jc.IsTrue) } func (s *provisionerSuite) TestToolsWrongMachine(c *gc.C) { tools, err := s.provisioner.Tools("42") c.Assert(err, gc.ErrorMatches, "permission denied") c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) c.Assert(tools, gc.IsNil) } func (s *provisionerSuite) TestTools(c *gc.C) { cur := version.Current curTools := &tools.Tools{Version: cur, URL: ""} curTools.Version.Minor++ s.machine.SetAgentVersion(cur) // Provisioner.Tools returns the *desired* set of tools, not the // currently running set. We want to be upgraded to cur.Version stateTools, err := s.provisioner.Tools(s.machine.Tag()) c.Assert(err, gc.IsNil) c.Assert(stateTools.Version, gc.Equals, cur) c.Assert(stateTools.URL, gc.Not(gc.Equals), "") } func (s *provisionerSuite) TestSetSupportedContainers(c *gc.C) { apiMachine, err := s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) err = apiMachine.SetSupportedContainers(instance.LXC, instance.KVM) c.Assert(err, gc.IsNil) err = s.machine.Refresh() c.Assert(err, gc.IsNil) containers, ok := s.machine.SupportedContainers() c.Assert(ok, jc.IsTrue) c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXC, instance.KVM}) } func (s *provisionerSuite) TestSupportsNoContainers(c *gc.C) { apiMachine, err := s.provisioner.Machine(s.machine.Tag()) c.Assert(err, gc.IsNil) err = apiMachine.SupportsNoContainers() c.Assert(err, gc.IsNil) err = s.machine.Refresh() c.Assert(err, gc.IsNil) containers, ok := s.machine.SupportedContainers() c.Assert(ok, jc.IsTrue) c.Assert(containers, gc.DeepEquals, []instance.ContainerType{}) } ���������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/api/provisioner/machine.go�����������������������0000644�0000153�0000161�00000020174�12321735776�027104� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provisioner import ( "errors" "fmt" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/state/api/watcher" ) // Machine represents a juju machine as seen by the provisioner worker. type Machine struct { tag string life params.Life st *State } // Tag returns the machine's tag. func (m *Machine) Tag() string { return m.tag } // Id returns the machine id. func (m *Machine) Id() string { _, machineId, err := names.ParseTag(m.tag, names.MachineTagKind) if err != nil { panic(fmt.Sprintf("%q is not a valid machine tag", m.tag)) } return machineId } // String returns the machine as a string. func (m *Machine) String() string { return m.Id() } // Life returns the machine's lifecycle value. func (m *Machine) Life() params.Life { return m.life } // Refresh updates the cached local copy of the machine's data. func (m *Machine) Refresh() error { life, err := m.st.machineLife(m.tag) if err != nil { return err } m.life = life return nil } // SetStatus sets the status of the machine. func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error { var result params.ErrorResults args := params.SetStatus{ Entities: []params.EntityStatus{ {Tag: m.tag, Status: status, Info: info, Data: data}, }, } err := m.st.call("SetStatus", args, &result) if err != nil { return err } return result.OneError() } // Status returns the status of the machine. func (m *Machine) Status() (params.Status, string, error) { var results params.StatusResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("Status", args, &results) if err != nil { return "", "", err } if len(results.Results) != 1 { return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return "", "", result.Error } return result.Status, result.Info, nil } // Constraints returns the exact constraints that should apply when provisioning // an instance for the machine. func (m *Machine) Constraints() (constraints.Value, error) { nothing := constraints.Value{} var results params.ConstraintsResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("Constraints", args, &results) if err != nil { return nothing, err } if len(results.Results) != 1 { return nothing, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return nothing, result.Error } return result.Constraints, nil } // EnsureDead sets the machine lifecycle to Dead if it is Alive or // Dying. It does nothing otherwise. func (m *Machine) EnsureDead() error { var result params.ErrorResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("EnsureDead", args, &result) if err != nil { return err } return result.OneError() } // Remove removes the machine from state. It will fail if the machine // is not Dead. func (m *Machine) Remove() error { var result params.ErrorResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("Remove", args, &result) if err != nil { return err } return result.OneError() } // Series returns the operating system series running on the machine. // // NOTE: Unlike state.Machine.Series(), this method returns an error // as well, because it needs to do an API call. func (m *Machine) Series() (string, error) { var results params.StringResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("Series", args, &results) if err != nil { return "", err } if len(results.Results) != 1 { return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return "", result.Error } return result.Result, nil } // SetProvisioned sets the provider specific machine id, nonce and also metadata for // this machine. Once set, the instance id cannot be changed. func (m *Machine) SetProvisioned(id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics) error { var result params.ErrorResults args := params.SetProvisioned{ Machines: []params.MachineSetProvisioned{{ Tag: m.tag, InstanceId: id, Nonce: nonce, Characteristics: characteristics, }}, } err := m.st.call("SetProvisioned", args, &result) if err != nil { return err } return result.OneError() } // InstanceId returns the provider specific instance id for the // machine or an CodeNotProvisioned error, if not set. func (m *Machine) InstanceId() (instance.Id, error) { var results params.StringResults args := params.Entities{ Entities: []params.Entity{{Tag: m.tag}}, } err := m.st.call("InstanceId", args, &results) if err != nil { return "", err } if len(results.Results) != 1 { return "", fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return "", result.Error } return instance.Id(result.Result), nil } // SetPassword sets the machine's password. func (m *Machine) SetPassword(password string) error { var result params.ErrorResults args := params.EntityPasswords{ Changes: []params.EntityPassword{ {Tag: m.tag, Password: password}, }, } err := m.st.call("SetPasswords", args, &result) if err != nil { return err } return result.OneError() } // WatchContainers returns a StringsWatcher that notifies of changes // to the lifecycles of containers of the specified type on the machine. func (m *Machine) WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error) { if string(ctype) == "" { return nil, errors.New("container type must be specified") } supported := false for _, c := range instance.ContainerTypes { if ctype == c { supported = true break } } if !supported { return nil, fmt.Errorf("unsupported container type %q", ctype) } var results params.StringsWatchResults args := params.WatchContainers{ Params: []params.WatchContainer{ {MachineTag: m.tag, ContainerType: string(ctype)}, }, } err := m.st.call("WatchContainers", 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 } w := watcher.NewStringsWatcher(m.st.caller, result) return w, nil } // WatchAllContainers returns a StringsWatcher that notifies of changes // to the lifecycles of all containers on the machine. func (m *Machine) WatchAllContainers() (watcher.StringsWatcher, error) { var results params.StringsWatchResults args := params.WatchContainers{ Params: []params.WatchContainer{ {MachineTag: m.tag}, }, } err := m.st.call("WatchContainers", 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 } w := watcher.NewStringsWatcher(m.st.caller, result) return w, nil } // SetSupportedContainers updates the list of containers supported by this machine. func (m *Machine) SetSupportedContainers(containerTypes ...instance.ContainerType) error { var results params.ErrorResults args := params.MachineContainersParams{ Params: []params.MachineContainers{ {MachineTag: m.tag, ContainerTypes: containerTypes}, }, } err := m.st.call("SetSupportedContainers", args, &results) if err != nil { return err } if len(results.Results) != 1 { return fmt.Errorf("expected 1 result, got %d", len(results.Results)) } apiError := results.Results[0].Error if apiError != nil { return apiError } return nil } // SupportsNoContainers records the fact that this machine doesn't support any containers. func (m *Machine) SupportsNoContainers() error { return m.SetSupportedContainers([]instance.ContainerType{}...) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/life.go������������������������������������������0000644�0000153�0000161�00000002440�12321735642�023253� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "launchpad.net/juju-core/state/api/params" ) // Life represents the lifecycle state of the entities // Relation, Unit, Service and Machine. type Life int8 const ( Alive Life = iota Dying Dead nLife ) var lifeStrings = [nLife]params.Life{ Alive: params.Alive, Dying: params.Dying, Dead: params.Dead, } func (l Life) String() string { return string(lifeStrings[l]) } var isAliveDoc = bson.D{{"life", Alive}} var isDeadDoc = bson.D{{"life", Dead}} var notDeadDoc = bson.D{{"life", bson.D{{"$ne", Dead}}}} // Living describes state entities with a lifecycle. type Living interface { Life() Life Destroy() error Refresh() error } // AgentLiving describes state entities with a lifecycle and an agent that // manages it. type AgentLiving interface { Living EnsureDead() error Remove() error } func isAlive(coll *mgo.Collection, id interface{}) (bool, error) { n, err := coll.Find(bson.D{{"_id", id}, {"life", Alive}}).Count() return n == 1, err } func isNotDead(coll *mgo.Collection, id interface{}) (bool, error) { n, err := coll.Find(bson.D{{"_id", id}, {"life", bson.D{{"$ne", Dead}}}}).Count() return n == 1, err } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/machine.go���������������������������������������0000644�0000153�0000161�00000103623�12321735776�023755� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state import ( "fmt" "strings" "time" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" "labix.org/v2/mgo/txn" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state/api/params" "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" ) // Machine represents the state of a machine. type Machine struct { st *State doc machineDoc annotator } // MachineJob values define responsibilities that machines may be // expected to fulfil. type MachineJob int const ( _ MachineJob = iota JobHostUnits JobManageEnviron // Deprecated in 1.18. JobManageStateDeprecated ) var jobNames = map[MachineJob]params.MachineJob{ JobHostUnits: params.JobHostUnits, JobManageEnviron: params.JobManageEnviron, // Deprecated in 1.18. JobManageStateDeprecated: params.JobManageStateDeprecated, } // AllJobs returns all supported machine jobs. func AllJobs() []MachineJob { return []MachineJob{JobHostUnits, JobManageEnviron} } // ToParams returns the job as params.MachineJob. func (job MachineJob) ToParams() params.MachineJob { if paramsJob, ok := jobNames[job]; ok { return paramsJob } return params.MachineJob(fmt.Sprintf("<unknown job %d>", int(job))) } // MachineJobFromParams returns the job corresponding to params.MachineJob. func MachineJobFromParams(job params.MachineJob) (MachineJob, error) { for machineJob, paramJob := range jobNames { if paramJob == job { return machineJob, nil } } return -1, fmt.Errorf("invalid machine job %q", job) } // paramsJobsFromJobs converts state jobs to params jobs. func paramsJobsFromJobs(jobs []MachineJob) []params.MachineJob { paramsJobs := make([]params.MachineJob, len(jobs)) for i, machineJob := range jobs { paramsJobs[i] = machineJob.ToParams() } return paramsJobs } func (job MachineJob) String() string { return string(job.ToParams()) } // machineDoc represents the internal state of a machine in MongoDB. // Note the correspondence with MachineInfo in state/api/params. type machineDoc struct { Id string `bson:"_id"` Nonce string Series string ContainerType string Principals []string Life Life Tools *tools.Tools `bson:",omitempty"` Jobs []MachineJob NoVote bool HasVote bool PasswordHash string Clean bool // We store 2 different sets of addresses for the machine, obtained // from different sources. // Addresses is the set of addresses obtained by asking the provider. Addresses []address // MachineAddresses is the set of addresses obtained from the machine itself. MachineAddresses []address // The SupportedContainers attributes are used to advertise what containers this // machine is capable of hosting. SupportedContainersKnown bool SupportedContainers []instance.ContainerType `bson:",omitempty"` // Deprecated. InstanceId, now lives on instanceData. // This attribute is retained so that data from existing machines can be read. // SCHEMACHANGE // TODO(wallyworld): remove this attribute when schema upgrades are possible. InstanceId instance.Id } func newMachine(st *State, doc *machineDoc) *Machine { machine := &Machine{ st: st, doc: *doc, } machine.annotator = annotator{ globalKey: machine.globalKey(), tag: machine.Tag(), st: st, } return machine } // Id returns the machine id. func (m *Machine) Id() string { return m.doc.Id } // Series returns the operating system series running on the machine. func (m *Machine) Series() string { return m.doc.Series } // ContainerType returns the type of container hosting this machine. func (m *Machine) ContainerType() instance.ContainerType { return instance.ContainerType(m.doc.ContainerType) } // machineGlobalKey returns the global database key for the identified machine. func machineGlobalKey(id string) string { return "m#" + id } // globalKey returns the global database key for the machine. func (m *Machine) globalKey() string { return machineGlobalKey(m.doc.Id) } // instanceData holds attributes relevant to a provisioned machine. type instanceData struct { Id string `bson:"_id"` InstanceId instance.Id `bson:"instanceid"` Status string `bson:"status,omitempty"` Arch *string `bson:"arch,omitempty"` Mem *uint64 `bson:"mem,omitempty"` RootDisk *uint64 `bson:"rootdisk,omitempty"` CpuCores *uint64 `bson:"cpucores,omitempty"` CpuPower *uint64 `bson:"cpupower,omitempty"` Tags *[]string `bson:"tags,omitempty"` } func hardwareCharacteristics(instData instanceData) *instance.HardwareCharacteristics { return &instance.HardwareCharacteristics{ Arch: instData.Arch, Mem: instData.Mem, RootDisk: instData.RootDisk, CpuCores: instData.CpuCores, CpuPower: instData.CpuPower, Tags: instData.Tags, } } // TODO(wallyworld): move this method to a service. func (m *Machine) HardwareCharacteristics() (*instance.HardwareCharacteristics, error) { instData, err := getInstanceData(m.st, m.Id()) if err != nil { return nil, err } return hardwareCharacteristics(instData), nil } func getInstanceData(st *State, id string) (instanceData, error) { var instData instanceData err := st.instanceData.FindId(id).One(&instData) if err == mgo.ErrNotFound { return instanceData{}, errors.NotFoundf("instance data for machine %v", id) } if err != nil { return instanceData{}, fmt.Errorf("cannot get instance data for machine %v: %v", id, err) } return instData, nil } // Tag returns a name identifying the machine that is safe to use // as a file name. The returned name will be different from other // Tag values returned by any other entities from the same state. func (m *Machine) Tag() string { return names.MachineTag(m.Id()) } // Life returns whether the machine is Alive, Dying or Dead. func (m *Machine) Life() Life { return m.doc.Life } // Jobs returns the responsibilities that must be fulfilled by m's agent. func (m *Machine) Jobs() []MachineJob { return m.doc.Jobs } // WantsVote reports whether the machine is a state server // that wants to take part in peer voting. func (m *Machine) WantsVote() bool { return hasJob(m.doc.Jobs, JobManageEnviron) && !m.doc.NoVote } // HasVote reports whether that machine is currently a voting // member of the replica set. func (m *Machine) HasVote() bool { return m.doc.HasVote } // SetHasVote sets whether the machine is currently a voting // member of the replica set. It should only be called // from the worker that maintains the replica set. func (m *Machine) SetHasVote(hasVote bool) error { ops := []txn.Op{{ C: m.st.machines.Name, Id: m.doc.Id, Assert: notDeadDoc, Update: bson.D{{"$set", bson.D{{"hasvote", hasVote}}}}, }} if err := m.st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set HasVote of machine %v: %v", m, onAbort(err, errDead)) } m.doc.HasVote = hasVote return nil } // IsManager returns true if the machine has JobManageEnviron. func (m *Machine) IsManager() bool { return hasJob(m.doc.Jobs, JobManageEnviron) } // IsManual returns true if the machine was manually provisioned. func (m *Machine) IsManual() (bool, error) { // Apart from the bootstrap machine, manually provisioned // machines have a nonce prefixed with "manual:". This is // unique to manual provisioning. if strings.HasPrefix(m.doc.Nonce, "manual:") { return true, nil } // The bootstrap machine uses BootstrapNonce, so in that // case we need to check if its provider type is "manual". // We also check for "null", which is an alias for manual. if m.doc.Id == "0" { cfg, err := m.st.EnvironConfig() if err != nil { return false, err } t := cfg.Type() return t == "null" || t == "manual", nil } return false, nil } // AgentTools returns the tools that the agent is currently running. // It returns an error that satisfies IsNotFound if the tools have not yet been set. func (m *Machine) AgentTools() (*tools.Tools, error) { if m.doc.Tools == nil { return nil, errors.NotFoundf("agent tools for machine %v", m) } tools := *m.doc.Tools return &tools, nil } // checkVersionValidity checks whether the given version is suitable // for passing to SetAgentVersion. func checkVersionValidity(v version.Binary) error { if v.Series == "" || v.Arch == "" { return fmt.Errorf("empty series or arch") } return nil } // SetAgentVersion sets the version of juju that the agent is // currently running. func (m *Machine) SetAgentVersion(v version.Binary) (err error) { defer utils.ErrorContextf(&err, "cannot set agent version for machine %v", m) if err = checkVersionValidity(v); err != nil { return err } tools := &tools.Tools{Version: v} ops := []txn.Op{{ C: m.st.machines.Name, Id: m.doc.Id, Assert: notDeadDoc, Update: bson.D{{"$set", bson.D{{"tools", tools}}}}, }} if err := m.st.runTransaction(ops); err != nil { return onAbort(err, errDead) } m.doc.Tools = tools return nil } // SetMongoPassword sets the password the agent responsible for the machine // should use to communicate with the state servers. Previous passwords // are invalidated. func (m *Machine) SetMongoPassword(password string) error { return m.st.setMongoPassword(m.Tag(), password) } // SetPassword sets the password for the machine's agent. func (m *Machine) SetPassword(password string) error { if len(password) < utils.MinAgentPasswordLength { return fmt.Errorf("password is only %d bytes long, and is not a valid Agent password", len(password)) } return m.setPasswordHash(utils.AgentPasswordHash(password)) } // setPasswordHash sets the underlying password hash in the database directly // to the value supplied. This is split out from SetPassword to allow direct // manipulation in tests (to check for backwards compatibility). func (m *Machine) setPasswordHash(passwordHash string) error { ops := []txn.Op{{ C: m.st.machines.Name, Id: m.doc.Id, Assert: notDeadDoc, Update: bson.D{{"$set", bson.D{{"passwordhash", passwordHash}}}}, }} if err := m.st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set password of machine %v: %v", m, onAbort(err, errDead)) } m.doc.PasswordHash = passwordHash return nil } // Return the underlying PasswordHash stored in the database. Used by the test // suite to check that the PasswordHash gets properly updated to new values // when compatibility mode is detected. func (m *Machine) getPasswordHash() string { return m.doc.PasswordHash } // PasswordValid returns whether the given password is valid // for the given machine. func (m *Machine) PasswordValid(password string) bool { agentHash := utils.AgentPasswordHash(password) if agentHash == m.doc.PasswordHash { return true } // In Juju 1.16 and older we used the slower password hash for unit // agents. So check to see if the supplied password matches the old // path, and if so, update it to the new mechanism. // We ignore any error in setting the password, as we'll just try again // next time if utils.UserPasswordHash(password, utils.CompatSalt) == m.doc.PasswordHash { logger.Debugf("%s logged in with old password hash, changing to AgentPasswordHash", m.Tag()) m.setPasswordHash(agentHash) return true } return false } // Destroy sets the machine lifecycle to Dying if it is Alive. It does // nothing otherwise. Destroy will fail if the machine has principal // units assigned, or if the machine has JobManageEnviron. // If the machine has assigned units, Destroy will return // a HasAssignedUnitsError. func (m *Machine) Destroy() error { return m.advanceLifecycle(Dying) } // ForceDestroy queues the machine for complete removal, including the // destruction of all units and containers on the machine. func (m *Machine) ForceDestroy() error { if !m.IsManager() { ops := []txn.Op{{ C: m.st.machines.Name, Id: m.doc.Id, Assert: bson.D{{"jobs", bson.D{{"$nin", []MachineJob{JobManageEnviron}}}}}, }, m.st.newCleanupOp("machine", m.doc.Id)} if err := m.st.runTransaction(ops); err != txn.ErrAborted { return err } } return fmt.Errorf("machine %s is required by the environment", m.doc.Id) } // EnsureDead sets the machine lifecycle to Dead if it is Alive or Dying. // It does nothing otherwise. EnsureDead will fail if the machine has // principal units assigned, or if the machine has JobManageEnviron. // If the machine has assigned units, EnsureDead will return // a HasAssignedUnitsError. func (m *Machine) EnsureDead() error { return m.advanceLifecycle(Dead) } type HasAssignedUnitsError struct { MachineId string UnitNames []string } func (e *HasAssignedUnitsError) Error() string { return fmt.Sprintf("machine %s has unit %q assigned", e.MachineId, e.UnitNames[0]) } func IsHasAssignedUnitsError(err error) bool { _, ok := err.(*HasAssignedUnitsError) return ok } // Containers returns the container ids belonging to a parent machine. // TODO(wallyworld): move this method to a service func (m *Machine) Containers() ([]string, error) { var mc machineContainers err := m.st.containerRefs.FindId(m.Id()).One(&mc) if err == nil { return mc.Children, nil } if err == mgo.ErrNotFound { return nil, errors.NotFoundf("container info for machine %v", m.Id()) } return nil, err } // ParentId returns the Id of the host machine if this machine is a container. func (m *Machine) ParentId() (string, bool) { parentId := ParentId(m.Id()) return parentId, parentId != "" } type HasContainersError struct { MachineId string ContainerIds []string } func (e *HasContainersError) Error() string { return fmt.Sprintf("machine %s is hosting containers %q", e.MachineId, strings.Join(e.ContainerIds, ",")) } func IsHasContainersError(err error) bool { _, ok := err.(*HasContainersError) return ok } // advanceLifecycle ensures that the machine's lifecycle is no earlier // than the supplied value. If the machine already has that lifecycle // value, or a later one, no changes will be made to remote state. If // the machine has any responsibilities that preclude a valid change in // lifecycle, it will return an error. func (original *Machine) advanceLifecycle(life Life) (err error) { containers, err := original.Containers() if err != nil { return err } if len(containers) > 0 { return &HasContainersError{ MachineId: original.doc.Id, ContainerIds: containers, } } m := original defer func() { if err == nil { // The machine's lifecycle is known to have advanced; it may be // known to have already advanced further than requested, in // which case we set the latest known valid value. if m == nil { life = Dead } else if m.doc.Life > life { life = m.doc.Life } original.doc.Life = life } }() // op and op := txn.Op{ C: m.st.machines.Name, Id: m.doc.Id, Update: bson.D{{"$set", bson.D{{"life", life}}}}, } advanceAsserts := bson.D{ {"jobs", bson.D{{"$nin", []MachineJob{JobManageEnviron}}}}, {"$or", []bson.D{ {{"principals", bson.D{{"$size", 0}}}}, {{"principals", bson.D{{"$exists", false}}}}, }}, {"hasvote", bson.D{{"$ne", true}}}, } // 3 attempts: one with original data, one with refreshed data, and a final // one intended to determine the cause of failure of the preceding attempt. for i := 0; i < 3; i++ { // If the transaction was aborted, grab a fresh copy of the machine data. // We don't write to original, because the expectation is that state- // changing methods only set the requested change on the receiver; a case // could perhaps be made that this is not a helpful convention in the // context of the new state API, but we maintain consistency in the // face of uncertainty. if i != 0 { if m, err = m.st.Machine(m.doc.Id); errors.IsNotFoundError(err) { return nil } else if err != nil { return err } } // Check that the life change is sane, and collect the assertions // necessary to determine that it remains so. switch life { case Dying: if m.doc.Life != Alive { return nil } op.Assert = append(advanceAsserts, isAliveDoc...) case Dead: if m.doc.Life == Dead { return nil } op.Assert = append(advanceAsserts, notDeadDoc...) default: panic(fmt.Errorf("cannot advance lifecycle to %v", life)) } // Check that the machine does not have any responsibilities that // prevent a lifecycle change. if hasJob(m.doc.Jobs, JobManageEnviron) { // (NOTE: When we enable multiple JobManageEnviron machines, // this restriction will be lifted, but we will assert that the // machine is not voting) return fmt.Errorf("machine %s is required by the environment", m.doc.Id) } if m.doc.HasVote { return fmt.Errorf("machine %s is a voting replica set member", m.doc.Id) } if len(m.doc.Principals) != 0 { return &HasAssignedUnitsError{ MachineId: m.doc.Id, UnitNames: m.doc.Principals, } } // Run the transaction... if err := m.st.runTransaction([]txn.Op{op}); err != txn.ErrAborted { return err } // ...and retry on abort. } // In very rare circumstances, the final iteration above will have determined // no cause of failure, and attempted a final transaction: if this also failed, // we can be sure that the machine document is changing very fast, in a somewhat // surprising fashion, and that it is sensible to back off for now. return fmt.Errorf("machine %s cannot advance lifecycle: %v", m, ErrExcessiveContention) } // Remove removes the machine from state. It will fail if the machine is not // Dead. func (m *Machine) Remove() (err error) { defer utils.ErrorContextf(&err, "cannot remove machine %s", m.doc.Id) if m.doc.Life != Dead { return fmt.Errorf("machine is not dead") } ops := []txn.Op{ { C: m.st.machines.Name, Id: m.doc.Id, Assert: txn.DocExists, Remove: true, }, { C: m.st.instanceData.Name, Id: m.doc.Id, Remove: true, }, removeStatusOp(m.st, m.globalKey()), removeConstraintsOp(m.st, m.globalKey()), removeNetworksOp(m.st, m.globalKey()), annotationRemoveOp(m.st, m.globalKey()), } ops = append(ops, removeContainerRefOps(m.st, m.Id())...) // The only abort conditions in play indicate that the machine has already // been removed. return onAbort(m.st.runTransaction(ops), nil) } // Refresh refreshes the contents of the machine from the underlying // state. It returns an error that satisfies IsNotFound if the machine has // been removed. func (m *Machine) Refresh() error { doc := machineDoc{} err := m.st.machines.FindId(m.doc.Id).One(&doc) if err == mgo.ErrNotFound { return errors.NotFoundf("machine %v", m) } if err != nil { return fmt.Errorf("cannot refresh machine %v: %v", m, err) } m.doc = doc return nil } // AgentAlive returns whether the respective remote agent is alive. func (m *Machine) AgentAlive() (bool, error) { return m.st.pwatcher.Alive(m.globalKey()) } // WaitAgentAlive blocks until the respective agent is alive. func (m *Machine) WaitAgentAlive(timeout time.Duration) (err error) { defer utils.ErrorContextf(&err, "waiting for agent of machine %v", m) ch := make(chan presence.Change) m.st.pwatcher.Watch(m.globalKey(), ch) defer m.st.pwatcher.Unwatch(m.globalKey(), ch) for i := 0; i < 2; i++ { select { case change := <-ch: if change.Alive { return nil } case <-time.After(timeout): return fmt.Errorf("still not alive after timeout") case <-m.st.pwatcher.Dead(): return m.st.pwatcher.Err() } } panic(fmt.Sprintf("presence reported dead status twice in a row for machine %v", m)) } // SetAgentAlive signals that the agent for machine m is alive. // It returns the started pinger. func (m *Machine) SetAgentAlive() (*presence.Pinger, error) { p := presence.NewPinger(m.st.presence, m.globalKey()) err := p.Start() if err != nil { return nil, err } return p, nil } // InstanceId returns the provider specific instance id for this // machine, or a NotProvisionedError, if not set. func (m *Machine) InstanceId() (instance.Id, error) { // SCHEMACHANGE // TODO(wallyworld) - remove this backward compatibility code when schema upgrades are possible // (we first check for InstanceId stored on the machineDoc) if m.doc.InstanceId != "" { return m.doc.InstanceId, nil } instData, err := getInstanceData(m.st, m.Id()) if (err == nil && instData.InstanceId == "") || errors.IsNotFoundError(err) { err = NotProvisionedError(m.Id()) } if err != nil { return "", err } return instData.InstanceId, nil } // InstanceStatus returns the provider specific instance status for this machine, // or a NotProvisionedError if instance is not yet provisioned. func (m *Machine) InstanceStatus() (string, error) { // SCHEMACHANGE // InstanceId may not be stored in the instanceData doc, so we // get it using an API on machine which knows to look in the old // place if necessary. instId, err := m.InstanceId() if err != nil { return "", err } instData, err := getInstanceData(m.st, m.Id()) if (err == nil && instId == "") || errors.IsNotFoundError(err) { err = NotProvisionedError(m.Id()) } if err != nil { return "", err } return instData.Status, nil } // SetInstanceStatus sets the provider specific instance status for a machine. func (m *Machine) SetInstanceStatus(status string) (err error) { defer utils.ErrorContextf(&err, "cannot set instance status for machine %q", m) // SCHEMACHANGE - we can't do this yet until the schema is updated // so just do a txn.DocExists for now. // provisioned := bson.D{{"instanceid", bson.D{{"$ne", ""}}}} ops := []txn.Op{ { C: m.st.instanceData.Name, Id: m.doc.Id, Assert: txn.DocExists, Update: bson.D{{"$set", bson.D{{"status", status}}}}, }, } if err = m.st.runTransaction(ops); err == nil { return nil } else if err != txn.ErrAborted { return err } return NotProvisionedError(m.Id()) } // Units returns all the units that have been assigned to the machine. func (m *Machine) Units() (units []*Unit, err error) { defer utils.ErrorContextf(&err, "cannot get units assigned to machine %v", m) pudocs := []unitDoc{} err = m.st.units.Find(bson.D{{"machineid", m.doc.Id}}).All(&pudocs) if err != nil { return nil, err } for _, pudoc := range pudocs { units = append(units, newUnit(m.st, &pudoc)) docs := []unitDoc{} err = m.st.units.Find(bson.D{{"principal", pudoc.Name}}).All(&docs) if err != nil { return nil, err } for _, doc := range docs { units = append(units, newUnit(m.st, &doc)) } } return units, nil } // SetProvisioned sets the provider specific machine id, nonce and also metadata for // this machine. Once set, the instance id cannot be changed. // // When provisioning an instance, a nonce should be created and passed // when starting it, before adding the machine to the state. This means // that if the provisioner crashes (or its connection to the state is // lost) after starting the instance, we can be sure that only a single // instance will be able to act for that machine. func (m *Machine) SetProvisioned(id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics) (err error) { defer utils.ErrorContextf(&err, "cannot set instance data for machine %q", m) if id == "" || nonce == "" { return fmt.Errorf("instance id and nonce cannot be empty") } if characteristics == nil { characteristics = &instance.HardwareCharacteristics{} } instData := &instanceData{ Id: m.doc.Id, InstanceId: id, Arch: characteristics.Arch, Mem: characteristics.Mem, RootDisk: characteristics.RootDisk, CpuCores: characteristics.CpuCores, CpuPower: characteristics.CpuPower, Tags: characteristics.Tags, } // SCHEMACHANGE // TODO(wallyworld) - do not check instanceId on machineDoc after schema is upgraded notSetYet := bson.D{{"instanceid", ""}, {"nonce", ""}} ops := []txn.Op{ { C: m.st.machines.Name, Id: m.doc.Id, Assert: append(isAliveDoc, notSetYet...), Update: bson.D{{"$set", bson.D{{"instanceid", id}, {"nonce", nonce}}}}, }, { C: m.st.instanceData.Name, Id: m.doc.Id, Assert: txn.DocMissing, Insert: instData, }, } if err = m.st.runTransaction(ops); err == nil { m.doc.Nonce = nonce // SCHEMACHANGE // TODO(wallyworld) - remove this backward compatibility code when schema upgrades are possible // (InstanceId is stored on the instanceData document but we duplicate the value on the machineDoc. m.doc.InstanceId = id return nil } else if err != txn.ErrAborted { return err } else if alive, err := isAlive(m.st.machines, m.doc.Id); err != nil { return err } else if !alive { return errNotAlive } return fmt.Errorf("already set") } // notProvisionedError records an error when a machine is not provisioned. type notProvisionedError struct { machineId string } func NotProvisionedError(machineId string) error { return &notProvisionedError{machineId} } func (e *notProvisionedError) Error() string { return fmt.Sprintf("machine %v is not provisioned", e.machineId) } // IsNotProvisionedError returns true if err is a notProvisionedError. func IsNotProvisionedError(err error) bool { _, ok := err.(*notProvisionedError) return ok } func mergedAddresses(machineAddresses, providerAddresses []address) []instance.Address { 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() } for _, address := range machineAddresses { if !providerValues.Contains(address.Value) { merged = append(merged, address.InstanceAddress()) } } 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. Provider-reported // addresses always come before machine-reported addresses. func (m *Machine) Addresses() (addresses []instance.Address) { return mergedAddresses(m.doc.MachineAddresses, m.doc.Addresses) } // SetAddresses records any addresses related to the machine, sourced // by asking the provider. func (m *Machine) SetAddresses(addresses []instance.Address) (err error) { stateAddresses := instanceAddressesToAddresses(addresses) ops := []txn.Op{ { C: m.st.machines.Name, Id: m.doc.Id, Assert: notDeadDoc, Update: bson.D{{"$set", bson.D{{"addresses", stateAddresses}}}}, }, } if err = m.st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set addresses of machine %v: %v", m, onAbort(err, errDead)) } m.doc.Addresses = stateAddresses return nil } // MachineAddresses returns any hostnames and ips associated with a machine, // determined by asking the machine itself. func (m *Machine) MachineAddresses() (addresses []instance.Address) { for _, address := range m.doc.MachineAddresses { addresses = append(addresses, address.InstanceAddress()) } return } // SetMachineAddresses records any addresses related to the machine, sourced // by asking the machine. func (m *Machine) SetMachineAddresses(addresses ...instance.Address) (err error) { stateAddresses := instanceAddressesToAddresses(addresses) ops := []txn.Op{ { C: m.st.machines.Name, Id: m.doc.Id, Assert: notDeadDoc, Update: bson.D{{"$set", bson.D{{"machineaddresses", stateAddresses}}}}, }, } if err = m.st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set machine addresses of machine %v: %v", m, onAbort(err, errDead)) } m.doc.MachineAddresses = stateAddresses return nil } // Networks returns the list of networks the machine should be on // (includeNetworks) or not (excludeNetworks). func (m *Machine) Networks() (includeNetworks, excludeNetworks []string, err error) { return readNetworks(m.st, m.globalKey()) } // CheckProvisioned returns true if the machine was provisioned with the given nonce. func (m *Machine) CheckProvisioned(nonce string) bool { return nonce == m.doc.Nonce && nonce != "" } // String returns a unique description of this machine. func (m *Machine) String() string { return m.doc.Id } // Constraints returns the exact constraints that should apply when provisioning // an instance for the machine. func (m *Machine) Constraints() (constraints.Value, error) { return readConstraints(m.st, m.globalKey()) } // SetConstraints sets the exact constraints to apply when provisioning an // instance for the machine. It will fail if the machine is Dead, or if it // is already provisioned. func (m *Machine) SetConstraints(cons constraints.Value) (err error) { defer utils.ErrorContextf(&err, "cannot set constraints") notSetYet := bson.D{{"nonce", ""}} ops := []txn.Op{ { C: m.st.machines.Name, Id: m.doc.Id, Assert: append(isAliveDoc, notSetYet...), }, setConstraintsOp(m.st, m.globalKey(), cons), } // 3 attempts is enough to push the ErrExcessiveContention case out of the // realm of plausibility: it implies local state indicating unprovisioned, // and remote state indicating provisioned (reasonable); but which changes // back to unprovisioned and then to provisioned again with *very* specific // timing in the course of this loop. for i := 0; i < 3; i++ { if m.doc.Life != Alive { return errNotAlive } if _, err := m.InstanceId(); err == nil { return fmt.Errorf("machine is already provisioned") } else if !IsNotProvisionedError(err) { return err } if err := m.st.runTransaction(ops); err != txn.ErrAborted { return err } if m, err = m.st.Machine(m.doc.Id); err != nil { return err } } return ErrExcessiveContention } // Status returns the status of the machine. func (m *Machine) Status() (status params.Status, info string, data params.StatusData, err error) { doc, err := getStatus(m.st, m.globalKey()) if err != nil { return "", "", nil, err } status = doc.Status info = doc.StatusInfo data = doc.StatusData return } // SetStatus sets the status of the machine. func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error { doc := statusDoc{ Status: status, StatusInfo: info, StatusData: data, } // If a machine is not yet provisioned, we allow its status // to be set back to pending (when a retry is to occur). _, err := m.InstanceId() allowPending := IsNotProvisionedError(err) if err := doc.validateSet(allowPending); err != nil { return err } ops := []txn.Op{{ C: m.st.machines.Name, Id: m.doc.Id, Assert: notDeadDoc, }, updateStatusOp(m.st, m.globalKey(), doc), } if err := m.st.runTransaction(ops); err != nil { return fmt.Errorf("cannot set status of machine %q: %v", m, onAbort(err, errNotAlive)) } return nil } // Clean returns true if the machine does not have any deployed units or containers. func (m *Machine) Clean() bool { return m.doc.Clean } // SupportedContainers returns any containers this machine is capable of hosting, and a bool // indicating if the supported containers have been determined or not. func (m *Machine) SupportedContainers() ([]instance.ContainerType, bool) { return m.doc.SupportedContainers, m.doc.SupportedContainersKnown } // SupportsNoContainers records the fact that this machine doesn't support any containers. func (m *Machine) SupportsNoContainers() (err error) { if err = m.updateSupportedContainers([]instance.ContainerType{}); err != nil { return err } return m.markInvalidContainers() } // SetSupportedContainers sets the list of containers supported by this machine. func (m *Machine) SetSupportedContainers(containers []instance.ContainerType) (err error) { if len(containers) == 0 { return fmt.Errorf("at least one valid container type is required") } for _, container := range containers { if container == instance.NONE { return fmt.Errorf("%q is not a valid container type", container) } } if err = m.updateSupportedContainers(containers); err != nil { return err } return m.markInvalidContainers() } func isSupportedContainer(container instance.ContainerType, supportedContainers []instance.ContainerType) bool { for _, supportedContainer := range supportedContainers { if supportedContainer == container { return true } } return false } // updateSupportedContainers sets the supported containers on this host machine. func (m *Machine) updateSupportedContainers(supportedContainers []instance.ContainerType) (err error) { ops := []txn.Op{ { C: m.st.machines.Name, Id: m.doc.Id, Assert: notDeadDoc, Update: bson.D{ {"$set", bson.D{ {"supportedcontainers", supportedContainers}, {"supportedcontainersknown", true}, }}}, }, } if err = m.st.runTransaction(ops); err != nil { return fmt.Errorf("cannot update supported containers of machine %v: %v", m, onAbort(err, errDead)) } m.doc.SupportedContainers = supportedContainers m.doc.SupportedContainersKnown = true return nil } // markInvalidContainers sets the status of any container belonging to this machine // as being in error if the container type is not supported. func (m *Machine) markInvalidContainers() error { currentContainers, err := m.Containers() if err != nil { return err } for _, containerId := range currentContainers { if !isSupportedContainer(ContainerTypeFromId(containerId), m.doc.SupportedContainers) { container, err := m.st.Machine(containerId) if err != nil { logger.Errorf("loading container %v to mark as invalid: %v", containerId, err) continue } // There should never be a circumstance where an unsupported container is started. // Nonetheless, we check and log an error if such a situation arises. status, _, _, err := container.Status() if err != nil { logger.Errorf("finding status of container %v to mark as invalid: %v", containerId, err) continue } if status == params.StatusPending { containerType := ContainerTypeFromId(containerId) container.SetStatus( params.StatusError, "unsupported container", params.StatusData{"type": containerType}) } else { logger.Errorf("unsupported container %v has unexpected status %v", containerId, status) } } } return nil } �������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/state/environ_test.go����������������������������������0000644�0000153�0000161�00000002305�12321735642�025053� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package state_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/state" ) type EnvironSuite struct { ConnSuite env *state.Environment } var _ = gc.Suite(&EnvironSuite{}) func (s *EnvironSuite) SetUpTest(c *gc.C) { s.ConnSuite.SetUpTest(c) env, err := s.State.Environment() c.Assert(err, gc.IsNil) s.env = env } func (s *EnvironSuite) TestTag(c *gc.C) { expected := "environment-" + s.env.UUID() c.Assert(s.env.Tag(), gc.Equals, expected) } func (s *EnvironSuite) TestName(c *gc.C) { c.Assert(s.env.Name(), gc.Equals, "testenv") } func (s *EnvironSuite) TestUUID(c *gc.C) { uuidA := s.env.UUID() c.Assert(uuidA, gc.HasLen, 36) // Check that two environments have different UUIDs. s.State.Close() s.MgoSuite.TearDownTest(c) s.MgoSuite.SetUpTest(c) s.State = state.TestingInitialize(c, nil, state.Policy(nil)) env, err := s.State.Environment() c.Assert(err, gc.IsNil) uuidB := env.UUID() c.Assert(uuidA, gc.Not(gc.Equals), uuidB) } func (s *EnvironSuite) TestAnnotatorForEnvironment(c *gc.C) { testAnnotator(c, func() (state.Annotator, error) { return s.State.Environment() }) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/�����������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022327� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/locking_test.go��������������������������������0000644�0000153�0000161�00000001731�12321735642�025360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "errors" "sync" gc "launchpad.net/gocheck" ) type LockingSuite struct{} var _ = gc.Suite(&LockingSuite{}) func (LockingSuite) TestTestLockingFunctionPassesCorrectLock(c *gc.C) { lock := sync.Mutex{} function := func() { lock.Lock() lock.Unlock() } // TestLockingFunction succeeds. TestLockingFunction(&lock, function) } func (LockingSuite) TestTestLockingFunctionDetectsDisobeyedLock(c *gc.C) { lock := sync.Mutex{} function := func() {} c.Check( func() { TestLockingFunction(&lock, function) }, gc.Panics, errors.New("function did not obey lock")) } func (LockingSuite) TestTestLockingFunctionDetectsFailureToReleaseLock(c *gc.C) { lock := sync.Mutex{} defer lock.Unlock() function := func() { lock.Lock() } c.Check( func() { TestLockingFunction(&lock, function) }, gc.Panics, errors.New("function did not release lock")) } ���������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/locking.go�������������������������������������0000644�0000153�0000161�00000004707�12321735642�024327� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "errors" "sync" "time" ) // TestLockingFunction verifies that a function obeys a given lock. // // Use this as a building block in your own tests for proper locking. // Parameters are a lock that you expect your function to block on, and // the function that you want to test for proper locking on that lock. // // This helper attempts to verify that the function both obtains and releases // the lock. It will panic if the function fails to do either. // TODO: Support generic sync.Locker instead of just Mutex. // TODO: This could be a gocheck checker. // TODO(rog): make this work reliably even for functions that take longer // than a few µs to execute. func TestLockingFunction(lock *sync.Mutex, function func()) { // We record two events that must happen in the right order. // Buffer the channel so that we don't get hung up during attempts // to push the events in. events := make(chan string, 2) // Synchronization channel, to make sure that the function starts // trying to run at the point where we're going to make it block. proceed := make(chan bool, 1) goroutine := func() { proceed <- true function() events <- "complete function" } lock.Lock() go goroutine() // Make the goroutine start now. It should block inside "function()." // (It's fine, technically even better, if the goroutine started right // away, and this channel is buffered specifically so that it can.) <-proceed // Give a misbehaved function plenty of rope to hang itself. We don't // want it to block for a microsecond, hand control back to here so we // think it's stuck on the lock, and then later continue on its merry // lockless journey to finish last, as expected but for the wrong // reason. for counter := 0; counter < 10; counter++ { // TODO: In Go 1.1, use runtime.GoSched instead. time.Sleep(0) } // Unblock the goroutine. events <- "release lock" lock.Unlock() // Now that we've released the lock, the function is unblocked. Read // the 2 events. (This will wait until the function has completed.) firstEvent := <-events secondEvent := <-events if firstEvent != "release lock" || secondEvent != "complete function" { panic(errors.New("function did not obey lock")) } // Also, the function must have released the lock. blankLock := sync.Mutex{} if *lock != blankLock { panic(errors.New("function did not release lock")) } } ���������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/mgo_test.go������������������������������������0000644�0000153�0000161�00000003535�12321735642�024520� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing_test import ( stdtesting "testing" "labix.org/v2/mgo/bson" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type mgoSuite struct { testbase.LoggingSuite testing.MgoSuite } var _ = gc.Suite(&mgoSuite{}) func TestMgoSuite(t *stdtesting.T) { testing.MgoTestPackage(t) } func (s *mgoSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.MgoSuite.SetUpSuite(c) } func (s *mgoSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) s.MgoSuite.TearDownSuite(c) } func (s *mgoSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.MgoSuite.SetUpTest(c) } func (s *mgoSuite) TearDownTest(c *gc.C) { s.LoggingSuite.TearDownTest(c) s.MgoSuite.TearDownTest(c) } func (s *mgoSuite) TestResetWhenUnauthorized(c *gc.C) { session := testing.MgoServer.MustDial() defer session.Close() err := session.DB("admin").AddUser("admin", "foo", false) if err != nil && err.Error() != "need to login" { c.Assert(err, gc.IsNil) } // The test will fail if the reset does not succeed } func (s *mgoSuite) TestStartAndClean(c *gc.C) { c.Assert(testing.MgoServer.Addr(), gc.Not(gc.Equals), "") session := testing.MgoServer.MustDial() defer session.Close() menu := session.DB("food").C("menu") err := menu.Insert( bson.D{{"spam", "lots"}}, bson.D{{"eggs", "fried"}}, ) c.Assert(err, gc.IsNil) food := make([]map[string]string, 0) err = menu.Find(nil).All(&food) c.Assert(err, gc.IsNil) c.Assert(food, gc.HasLen, 2) c.Assert(food[0]["spam"], gc.Equals, "lots") c.Assert(food[1]["eggs"], gc.Equals, "fried") testing.MgoServer.Reset() morefood := make([]map[string]string, 0) err = menu.Find(nil).All(&morefood) c.Assert(err, gc.IsNil) c.Assert(morefood, gc.HasLen, 0) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/testbase/��������������������������������������0000755�0000153�0000161�00000000000�12321735642�024154� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/testbase/log.go��������������������������������0000644�0000153�0000161�00000001773�12321735642�025274� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testbase import ( "flag" "github.com/juju/loggo" "github.com/juju/testing/logging" gc "launchpad.net/gocheck" ) // LoggingSuite redirects the juju logger to the test logger // when embedded in a gocheck suite type. type LoggingSuite struct { logging.LoggingSuite } func (t *LoggingSuite) SetUpSuite(c *gc.C) { t.LoggingSuite.SetUpSuite(c) t.setUp(c) } func (t *LoggingSuite) SetUpTest(c *gc.C) { t.LoggingSuite.SetUpTest(c) t.PatchEnvironment("JUJU_LOGGING_CONFIG", "") t.setUp(c) } var logConfig = flag.String("juju.log", "DEBUG", "logging configuration (see http://godoc.org/github.com/juju/loggo#ConfigureLoggers; also accepts a bare log level to configure the log level of the root module") func (t *LoggingSuite) setUp(c *gc.C) { if _, ok := loggo.ParseLevel(*logConfig); ok { *logConfig = "<root>=" + *logConfig } err := loggo.ConfigureLoggers(*logConfig) c.Assert(err, gc.IsNil) } �����juju-core_1.18.1/src/launchpad.net/juju-core/testing/testbase/log_test.go���������������������������0000644�0000153�0000161�00000002416�12321735642�026326� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testbase_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/log" "launchpad.net/juju-core/testing/testbase" ) var _ = gc.Suite(&logSuite{}) type logSuite struct { testbase.LoggingSuite } func (s *logSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) log.Infof("testing-SetUpSuite") c.Assert(c.GetTestLog(), gc.Matches, ".*INFO juju testing-SetUpSuite\n") } func (s *logSuite) TearDownSuite(c *gc.C) { // Unfortunately there's no way of testing that the // log output is printed, as the logger is printing // a previously set up *gc.C. We print a message // anyway so that we can manually verify it. log.Infof("testing-TearDownSuite") } func (s *logSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) log.Infof("testing-SetUpTest") c.Assert(c.GetTestLog(), gc.Matches, ".*INFO juju testing-SetUpTest\n") } func (s *logSuite) TearDownTest(c *gc.C) { // The same applies here as to TearDownSuite. log.Infof("testing-TearDownTest") s.LoggingSuite.TearDownTest(c) } func (s *logSuite) TestLog(c *gc.C) { log.Infof("testing-Test") c.Assert(c.GetTestLog(), gc.Matches, ".*INFO juju testing-SetUpTest\n"+ ".*INFO juju testing-Test\n", ) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/testbase/imports.go����������������������������0000644�0000153�0000161�00000002566�12321735642�026211� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testbase import ( "go/build" "path/filepath" "sort" "strings" gc "launchpad.net/gocheck" ) const jujuPkgPrefix = "launchpad.net/juju-core/" // FindJujuCoreImports returns a sorted list of juju-core packages that are // imported by the packageName parameter. The resulting list removes the // common prefix "launchpad.net/juju-core/" leaving just the short names. func FindJujuCoreImports(c *gc.C, packageName string) []string { var result []string allpkgs := make(map[string]bool) findJujuCoreImports(c, packageName, allpkgs) for name := range allpkgs { result = append(result, name[len(jujuPkgPrefix):]) } sort.Strings(result) return result } // findJujuCoreImports recursively adds all imported packages of given // package (packageName) to allpkgs map. func findJujuCoreImports(c *gc.C, packageName string, allpkgs map[string]bool) { var imports []string for _, root := range build.Default.SrcDirs() { fullpath := filepath.Join(root, packageName) pkg, err := build.ImportDir(fullpath, 0) if err == nil { imports = pkg.Imports break } } if imports == nil { c.Fatalf(packageName + " not found") } for _, name := range imports { if strings.HasPrefix(name, jujuPkgPrefix) { allpkgs[name] = true findJujuCoreImports(c, name, allpkgs) } } } ������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/testbase/package_test.go�����������������������0000644�0000153�0000161�00000001065�12321735642�027137� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testbase_test import ( "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) func Test(t *testing.T) { gc.TestingT(t) } type DependencySuite struct{} var _ = gc.Suite(&DependencySuite{}) func (*DependencySuite) TestPackageDependencies(c *gc.C) { // This test is to ensure we don't bring in any juju-core dependencies. c.Assert(testbase.FindJujuCoreImports(c, "launchpad.net/juju-core/testing/testbase"), gc.HasLen, 0) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/������������������������������������������0000755�0000153�0000161�00000000000�12321735641�023306� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/����������������������������������0000755�0000153�0000161�00000000000�12321735641�024753� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/varnish/��������������������������0000755�0000153�0000161�00000000000�12321735642�026426� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/varnish/revision������������������0000644�0000153�0000161�00000000001�12321735642�030176� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/varnish/metadata.yaml�������������0000644�0000153�0000161�00000000157�12321735642�031075� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: varnish summary: "Database engine" description: "Another popular database" provides: webcache: varnish �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/upgrade1/�������������������������0000755�0000153�0000161�00000000000�12321735642�026464� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/upgrade1/revision�����������������0000644�0000153�0000161�00000000002�12321735642�030235� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/upgrade1/metadata.yaml������������0000644�0000153�0000161�00000000222�12321735642�031124� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: upgrade summary: "Sample charm to test version changes" description: | Sample charm to test version changes. This is the old charm. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/����������������������������0000755�0000153�0000161�00000000000�12321735642�026107� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/revision��������������������0000644�0000153�0000161�00000000001�12321735642�027657� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/build/����������������������0000755�0000153�0000161�00000000000�12321735642�027206� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/build/ignored���������������0000644�0000153�0000161�00000000000�12321735642�030546� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/.ignored��������������������0000644�0000153�0000161�00000000001�12321735642�027526� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/empty/����������������������0000755�0000153�0000161�00000000000�12321735641�027244� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/hooks/����������������������0000755�0000153�0000161�00000000000�12321735642�027232� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/hooks/install���������������0000755�0000153�0000161�00000000031�12321735642�030620� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash echo "Done!" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/config.yaml�����������������0000644�0000153�0000161�00000000543�12321735642�030242� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������options: title: {default: My Title, description: A descriptive title used for the service., type: string} outlook: {description: No default outlook., type: string} username: {default: admin001, description: The name of the initial account (given admin permissions)., type: string} skill-level: {description: A number indicating skill., type: int} �������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/metadata.yaml���������������0000644�0000153�0000161�00000000214�12321735642�030550� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: dummy summary: "That's a dummy charm." description: | This is a longer description which potentially contains multiple lines. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/.dir/�����������������������0000755�0000153�0000161�00000000000�12321735642�026743� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/.dir/ignored����������������0000644�0000153�0000161�00000000000�12321735642�030303� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/src/������������������������0000755�0000153�0000161�00000000000�12321735642�026676� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/dummy/src/hello.c�����������������0000644�0000153�0000161�00000000114�12321735642�030141� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include <stdio.h> main() { printf ("Hello World!\n"); return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/wordpress/������������������������0000755�0000153�0000161�00000000000�12321735642�027004� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/wordpress/revision����������������0000644�0000153�0000161�00000000001�12321735642�030554� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������3�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/wordpress/hooks/������������������0000755�0000153�0000161�00000000000�12321735641�030126� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/wordpress/config.yaml�������������0000644�0000153�0000161�00000000157�12321735642�031140� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������options: blog-title: {default: My Title, description: A descriptive title used for the blog., type: string} �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/wordpress/metadata.yaml�����������0000644�0000153�0000161�00000000632�12321735642�031451� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: wordpress summary: "Blog engine" description: "A pretty popular blog engine" provides: url: interface: http limit: optional: false logging-dir: interface: logging scope: container monitoring-port: interface: monitoring scope: container requires: db: interface: mysql limit: 1 optional: false cache: interface: varnish limit: 2 optional: true ������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/monitoring/�����������������������0000755�0000153�0000161�00000000000�12321735642�027141� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/monitoring/hooks/�����������������0000755�0000153�0000161�00000000000�12321735641�030263� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/monitoring/metadata.yaml����������0000644�0000153�0000161�00000000577�12321735642�031616� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: monitoring summary: "Subordinate monitoring test charm" description: | This is a longer description which potentially contains multiple lines. subordinate: true provides: monitoring-client: interface: monitoring requires: monitoring-port: interface: monitoring scope: container info: interface: juju-info scope: container ���������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/mysql/����������������������������0000755�0000153�0000161�00000000000�12321735642�026121� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/mysql/revision��������������������0000644�0000153�0000161�00000000001�12321735642�027671� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/mysql/metadata.yaml���������������0000644�0000153�0000161�00000000152�12321735642�030563� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: mysql summary: "Database engine" description: "A pretty popular database" provides: server: mysql ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/category/�������������������������0000755�0000153�0000161�00000000000�12321735642�026571� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/category/.ignored�����������������0000644�0000153�0000161�00000000001�12321735642�030210� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/category/metadata.yaml������������0000644�0000153�0000161�00000000224�12321735642�031233� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: categories summary: "Sample charm with a category" description: | That's a boring charm that has a category. categories: ["database"] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/category/.dir/��������������������0000755�0000153�0000161�00000000000�12321735642�027425� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/category/.dir/ignored�������������0000644�0000153�0000161�00000000000�12321735642�030765� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/format2/��������������������������0000755�0000153�0000161�00000000000�12321735642�026326� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/format2/.ignored������������������0000644�0000153�0000161�00000000001�12321735642�027745� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/format2/metadata.yaml�������������0000644�0000153�0000161�00000000243�12321735642�030771� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: format2 format: 2 summary: "Sample charm described in format 2" description: | That's a boring charm that is described in terms of format 2. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/format2/.dir/���������������������0000755�0000153�0000161�00000000000�12321735642�027162� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/format2/.dir/ignored��������������0000644�0000153�0000161�00000000000�12321735642�030522� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/logging/��������������������������0000755�0000153�0000161�00000000000�12321735642�026402� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/logging/revision������������������0000644�0000153�0000161�00000000002�12321735642�030153� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/logging/hooks/��������������������0000755�0000153�0000161�00000000000�12321735641�027524� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/logging/metadata.yaml�������������0000644�0000153�0000161�00000000562�12321735642�031051� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: logging summary: "Subordinate logging test charm" description: | This is a longer description which potentially contains multiple lines. subordinate: true provides: logging-client: interface: logging requires: logging-directory: interface: logging scope: container info: interface: juju-info scope: container ����������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/mysql-alternative/����������������0000755�0000153�0000161�00000000000�12321735642�030435� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/mysql-alternative/revision��������0000644�0000153�0000161�00000000001�12321735642�032205� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/mysql-alternative/metadata.yaml���0000644�0000153�0000161�00000000254�12321735642�033102� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: mysql-alternative summary: "Database engine" description: "A pretty popular database" provides: prod: interface: mysql dev: interface: mysql limit: 2 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/������������������������0000755�0000153�0000161�00000000000�12321735642�026645� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/revision����������������0000644�0000153�0000161�00000000002�12321735642�030416� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/������������������0000755�0000153�0000161�00000000000�12321735642�027770� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000146�00000000000�011566� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/foo-relation-joined���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/foo-relation-joine0000644�0000153�0000161�00000000022�12321735642�033405� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000147�00000000000�011567� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/self-relation-broken��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/self-relation-brok0000644�0000153�0000161�00000000022�12321735642�033404� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000151�00000000000�011562� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/self-relation-departed������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/self-relation-depa0000644�0000153�0000161�00000000022�12321735642�033360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/install�����������0000644�0000153�0000161�00000000022�12321735642�031353� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000147�00000000000�011567� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/self-relation-joined��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/self-relation-join0000644�0000153�0000161�00000000022�12321735642�033406� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000150�00000000000�011561� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/self-relation-changed�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/self-relation-chan0000644�0000153�0000161�00000000022�12321735642�033360� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/config-changed����0000644�0000153�0000161�00000000022�12321735642�032541� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/stop��������������0000644�0000153�0000161�00000000022�12321735642�030672� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000150�00000000000�011561� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/foo-relation-departed�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/foo-relation-depar0000644�0000153�0000161�00000000022�12321735642�033374� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000147�00000000000�011567� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/bar-relation-changed��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/bar-relation-chang0000644�0000153�0000161�00000000022�12321735642�033342� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/upgrade-charm�����0000644�0000153�0000161�00000000022�12321735642�032424� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/otherdata���������0000644�0000153�0000161�00000000012�12321735642�031657� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������some text ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000146�00000000000�011566� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/bar-relation-joined���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/bar-relation-joine0000644�0000153�0000161�00000000022�12321735642�033366� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000146�00000000000�011566� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/bar-relation-broken���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/bar-relation-broke0000644�0000153�0000161�00000000022�12321735642�033364� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000146�00000000000�011566� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/foo-relation-broken���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/foo-relation-broke0000644�0000153�0000161�00000000022�12321735642�033403� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/subdir/�����������0000755�0000153�0000161�00000000000�12321735642�031260� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/subdir/stuff������0000644�0000153�0000161�00000000027�12321735642�032331� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������non hook related stuff ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000150�00000000000�011561� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/bar-relation-departed�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/bar-relation-depar0000644�0000153�0000161�00000000022�12321735642�033355� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000147�00000000000�011567� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/foo-relation-changed��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/foo-relation-chang0000644�0000153�0000161�00000000022�12321735642�033361� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/hooks/start�������������0000644�0000153�0000161�00000000022�12321735642�031042� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh echo $0 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/all-hooks/metadata.yaml�����������0000644�0000153�0000161�00000000365�12321735642�031315� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: all-hooks summary: "That's a dummy charm with hook scrips for all types of hooks." description: "This is a longer description." provides: foo: interface: phony requires: bar: interface: fake peers: self: interface: dummy ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/upgrade2/�������������������������0000755�0000153�0000161�00000000000�12321735642�026465� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/upgrade2/revision�����������������0000644�0000153�0000161�00000000002�12321735642�030236� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������2 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/upgrade2/metadata.yaml������������0000644�0000153�0000161�00000000222�12321735642�031125� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: upgrade summary: "Sample charm to test version changes" description: | Sample charm to test version changes. This is the new charm. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/varnish-alternative/��������������0000755�0000153�0000161�00000000000�12321735642�030742� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/varnish-alternative/revision������0000644�0000153�0000161�00000000001�12321735642�032512� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/varnish-alternative/hooks/��������0000755�0000153�0000161�00000000000�12321735642�032065� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/varnish-alternative/hooks/install�0000755�0000153�0000161�00000000035�12321735642�033457� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash echo hello world���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/varnish-alternative/metadata.yaml�0000644�0000153�0000161�00000000173�12321735642�033407� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: varnish-alternative summary: "Database engine" description: "Another popular database" provides: webcache: varnish �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/riak/�����������������������������0000755�0000153�0000161�00000000000�12321735642�025702� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/riak/revision���������������������0000644�0000153�0000161�00000000001�12321735642�027452� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������7�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/riak/metadata.yaml����������������0000644�0000153�0000161�00000000317�12321735642�030347� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: riak summary: "K/V storage engine" description: "Scalable K/V Store in Erlang with Clocks :-)" provides: endpoint: interface: http admin: interface: http peers: ring: interface: riak �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/terracotta/�����������������������0000755�0000153�0000161�00000000000�12321735642�027124� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/terracotta/revision���������������0000644�0000153�0000161�00000000002�12321735642�030675� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������3 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/repo/quantal/terracotta/metadata.yaml����������0000644�0000153�0000161�00000000751�12321735642�031573� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������name: terracotta summary: Distributed HA caching/storage platform for Java maintainer: Robert Ayres <robert.ayres@ubuntu.com> description: | Distributed HA caching/storage platform for Java. . Terracotta provides out of the box clustering for a number of well known Java frameworks, including EHCache, Hibernate and Quartz as well as clustering for J2EE containers. provides: dso: interface: terracotta optional: true peers: server-array: terracotta-server �����������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/git.go�����������������������������������������0000644�0000153�0000161�00000002064�12321735642�023456� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "os" gc "launchpad.net/gocheck" ) type GitSuite struct { oldValues map[string]string } // We ensure that Git is told about the user name and email if the setup under which the // tests are run does not already provide that information. These git env variables are used for // that purpose. var gitEnvVars = []string{ "GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL", "GIT_COMMITTER_NAME", "GIT_COMMITTER_EMAIL", } func (t *GitSuite) SetUpTest(c *gc.C) { t.oldValues = make(map[string]string) for _, v := range gitEnvVars { t.oldValues[v] = os.Getenv(v) } if t.oldValues["GIT_AUTHOR_NAME"] == "" { os.Setenv("GIT_AUTHOR_NAME", "Foo Bar") } if t.oldValues["GIT_AUTHOR_EMAIL"] == "" { os.Setenv("GIT_AUTHOR_EMAIL", "foo@example.org") } os.Setenv("GIT_COMMITTER_NAME", "$GIT_AUTHOR_NAME") os.Setenv("GIT_COMMITTER_EMAIL", "$GIT_AUTHOR_EMAIL") } func (t *GitSuite) TearDownTest(c *gc.C) { for k, v := range t.oldValues { os.Setenv(k, v) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/cmd.go�����������������������������������������0000644�0000153�0000161�00000005706�12321735642�023444� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "bytes" "io/ioutil" "launchpad.net/gnuflag" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" ) // NewFlagSet creates a new flag set using the standard options, particularly // the option to stop the gnuflag methods from writing to StdErr or StdOut. func NewFlagSet() *gnuflag.FlagSet { fs := gnuflag.NewFlagSet("", gnuflag.ContinueOnError) fs.SetOutput(ioutil.Discard) return fs } // InitCommand will create a new flag set, and call the Command's SetFlags and // Init methods with the appropriate args. func InitCommand(c cmd.Command, args []string) error { f := NewFlagSet() c.SetFlags(f) if err := f.Parse(c.AllowInterspersedFlags(), args); err != nil { return err } return c.Init(f.Args()) } // Context creates a simple command execution context with the current // dir set to a newly created directory within the test directory. func Context(c *gc.C) *cmd.Context { return &cmd.Context{ Dir: c.MkDir(), Stdin: &bytes.Buffer{}, Stdout: &bytes.Buffer{}, Stderr: &bytes.Buffer{}, } } // ContextForDir creates a simple command execution context with the current // dir set to the specified directory. func ContextForDir(c *gc.C, dir string) *cmd.Context { return &cmd.Context{ Dir: dir, Stdin: &bytes.Buffer{}, Stdout: &bytes.Buffer{}, Stderr: &bytes.Buffer{}, } } // Stdout takes a command Context that we assume has been created in this // package, and gets the content of the Stdout buffer as a string. func Stdout(ctx *cmd.Context) string { return ctx.Stdout.(*bytes.Buffer).String() } // Stderr takes a command Context that we assume has been created in this // package, and gets the content of the Stderr buffer as a string. func Stderr(ctx *cmd.Context) string { return ctx.Stderr.(*bytes.Buffer).String() } // RunCommand runs a command with the specified args. The returned error // may come from either the parsing of the args, the command initialisation, or // the actual running of the command. Access to the resulting output streams // is provided through the returned context instance. func RunCommand(c *gc.C, com cmd.Command, args []string) (*cmd.Context, error) { if err := InitCommand(com, args); err != nil { return nil, err } var context = Context(c) return context, com.Run(context) } // RunCommandInDir works like RunCommand, but runs with a context that uses dir. func RunCommandInDir(c *gc.C, com cmd.Command, args []string, dir string) (*cmd.Context, error) { if err := InitCommand(com, args); err != nil { return nil, err } var context = ContextForDir(c, dir) return context, com.Run(context) } // TestInit checks that a command initialises correctly with the given set of // arguments. func TestInit(c *gc.C, com cmd.Command, args []string, errPat string) { err := InitCommand(com, args) if errPat != "" { c.Assert(err, gc.ErrorMatches, errPat) } else { c.Assert(err, gc.IsNil) } } ����������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/constants.go�����������������������������������0000644�0000153�0000161�00000001715�12321735642�024711� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "time" "launchpad.net/juju-core/utils" ) // ShortWait is a reasonable amount of time to block waiting for something that // shouldn't actually happen. (as in, the test suite will *actually* wait this // long before continuing) const ShortWait = 50 * time.Millisecond // LongWait is used when something should have already happened, or happens // quickly, but we want to make sure we just haven't missed it. As in, the test // suite should proceed without sleeping at all, but just in case. It is long // so that we don't have spurious failures without actually slowing down the // test suite const LongWait = 10 * time.Second var LongAttempt = &utils.AttemptStrategy{ Total: LongWait, Delay: ShortWait, } // SupportedSeries lists the series known to Juju. var SupportedSeries = []string{"precise", "quantal", "raring", "saucy", "trusty"} ���������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/http.go����������������������������������������0000644�0000153�0000161�00000010647�12321735642�023660� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "bytes" "fmt" "io/ioutil" "net" "net/http" "os" "time" gc "launchpad.net/gocheck" ) type HTTPSuite struct{} var Server = NewHTTPServer(5 * time.Second) func (s *HTTPSuite) SetUpSuite(c *gc.C) { Server.Start() } func (s *HTTPSuite) TearDownSuite(c *gc.C) {} func (s *HTTPSuite) SetUpTest(c *gc.C) {} func (s *HTTPSuite) TearDownTest(c *gc.C) { Server.Flush() } func (s *HTTPSuite) URL(path string) string { return Server.URL + path } type HTTPServer struct { URL string Timeout time.Duration started bool request chan *http.Request response chan ResponseFunc } func NewHTTPServer(timeout time.Duration) *HTTPServer { return &HTTPServer{Timeout: timeout} } type Response struct { Status int Headers map[string]string Body []byte } type ResponseFunc func(path string) Response func (s *HTTPServer) Start() { if s.started { return } s.started = true s.request = make(chan *http.Request, 64) s.response = make(chan ResponseFunc, 64) l, err := net.Listen("tcp", "localhost:0") if err != nil { panic(err) } port := l.Addr().(*net.TCPAddr).Port s.URL = fmt.Sprintf("http://localhost:%d", port) go http.Serve(l, s) s.Response(203, nil, nil) for { // Wait for it to be up. resp, err := http.Get(s.URL) if err == nil && resp.StatusCode == 203 { break } time.Sleep(1e8) } s.WaitRequest() // Consume dummy request. } // Flush discards all pending requests and responses. func (s *HTTPServer) Flush() { for { select { case <-s.request: case <-s.response: default: return } } } func body(req *http.Request) string { data, err := ioutil.ReadAll(req.Body) if err != nil { panic(err) } return string(data) } func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { req.ParseMultipartForm(1e6) data, err := ioutil.ReadAll(req.Body) if err != nil { panic(err) } req.Body = ioutil.NopCloser(bytes.NewBuffer(data)) s.request <- req var resp Response select { case respFunc := <-s.response: resp = respFunc(req.URL.Path) case <-time.After(s.Timeout): const msg = "ERROR: Timeout waiting for test to prepare a response\n" fmt.Fprintf(os.Stderr, msg) resp = Response{500, nil, []byte(msg)} } if resp.Headers != nil { h := w.Header() for k, v := range resp.Headers { h.Set(k, v) } } if resp.Status != 0 { w.WriteHeader(resp.Status) } w.Write(resp.Body) } // WaitRequests returns the next n requests made to the http server from // the queue. If not enough requests were previously made, it waits until // the timeout value for them to be made. func (s *HTTPServer) WaitRequests(n int) []*http.Request { reqs := make([]*http.Request, 0, n) for i := 0; i < n; i++ { select { case req := <-s.request: reqs = append(reqs, req) case <-time.After(s.Timeout): panic("Timeout waiting for request") } } return reqs } // WaitRequest returns the next request made to the http server from // the queue. If no requests were previously made, it waits until the // timeout value for one to be made. func (s *HTTPServer) WaitRequest() *http.Request { return s.WaitRequests(1)[0] } // ResponseFunc prepares the test server to respond the following n // requests using f to build each response. func (s *HTTPServer) ResponseFunc(n int, f ResponseFunc) { for i := 0; i < n; i++ { s.response <- f } } // ResponseMap maps request paths to responses. type ResponseMap map[string]Response // ResponseMap prepares the test server to respond the following n // requests using the m to obtain the responses. func (s *HTTPServer) ResponseMap(n int, m ResponseMap) { f := func(path string) Response { for rpath, resp := range m { if rpath == path { return resp } } body := []byte("Path not found in response map: " + path) return Response{Status: 500, Body: body} } s.ResponseFunc(n, f) } // Responses prepares the test server to respond the following n requests // using the provided response parameters. func (s *HTTPServer) Responses(n int, status int, headers map[string]string, body []byte) { f := func(path string) Response { return Response{status, headers, body} } s.ResponseFunc(n, f) } // Response prepares the test server to respond the following request // using the provided response parameters. func (s *HTTPServer) Response(status int, headers map[string]string, body []byte) { s.Responses(1, status, headers, body) } �����������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/environ.go�������������������������������������0000644�0000153�0000161�00000016435�12321735776�024372� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "io/ioutil" "os" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/ssh" ) // FakeAuthKeys holds the authorized key used for testing // purposes in FakeConfig. It is valid for parsing with the utils/ssh // authorized-key utilities. const FakeAuthKeys = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAYQDP8fPSAMFm2PQGoVUks/FENVUMww1QTK6m++Y2qX9NGHm43kwEzxfoWR77wo6fhBhgFHsQ6ogE/cYLx77hOvjTchMEP74EVxSce0qtDjI7SwYbOpAButRId3g/Ef4STz8= joe@0.1.2.4` func init() { _, err := ssh.ParseAuthorisedKey(FakeAuthKeys) if err != nil { panic("FakeAuthKeys does not hold a valid authorized key: " + err.Error()) } } const FakeDefaultSeries = "precise" // FakeConfig() returns an environment configuration for a // fake provider with all required attributes set. func FakeConfig() Attrs { return Attrs{ "type": "someprovider", "name": "testenv", "authorized-keys": FakeAuthKeys, "firewall-mode": config.FwInstance, "admin-secret": "fish", "ca-cert": CACert, "ca-private-key": CAKey, "ssl-hostname-verification": true, "development": false, "state-port": 19034, "api-port": 17777, "default-series": FakeDefaultSeries, } } // EnvironConfig returns a default environment configuration suitable for // setting in the state. func EnvironConfig(c *gc.C) *config.Config { return CustomEnvironConfig(c, Attrs{}) } // CustomEnvironConfig returns an environment configuration with // additional specified keys added. func CustomEnvironConfig(c *gc.C, extra Attrs) *config.Config { attrs := FakeConfig().Merge(Attrs{ "agent-version": "1.2.3", }).Merge(extra).Delete("admin-secret", "ca-private-key") cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) return cfg } const ( SampleEnvName = "erewhemos" EnvDefault = "default:\n " + SampleEnvName + "\n" ) const DefaultMongoPassword = "conn-from-name-secret" // Environment names below are explicit as it makes them more readable. const SingleEnvConfigNoDefault = ` environments: erewhemos: type: dummy state-server: true authorized-keys: i-am-a-key admin-secret: ` + DefaultMongoPassword + ` ` const SingleEnvConfig = EnvDefault + SingleEnvConfigNoDefault const MultipleEnvConfigNoDefault = ` environments: erewhemos: type: dummy state-server: true authorized-keys: i-am-a-key admin-secret: ` + DefaultMongoPassword + ` erewhemos-2: type: dummy state-server: true authorized-keys: i-am-a-key admin-secret: ` + DefaultMongoPassword + ` ` const MultipleEnvConfig = EnvDefault + MultipleEnvConfigNoDefault const SampleCertName = "erewhemos" type TestFile struct { Name, Data string } type FakeHome struct { oldHomeEnv string oldEnvironment map[string]string oldJujuHome string files []TestFile } // MakeFakeHomeNoEnvironments creates a new temporary directory through the // test checker, and overrides the HOME environment variable to point to this // new temporary directory. // // No ~/.juju/environments.yaml exists, but CAKeys are written for each of the // 'certNames' specified, and the id_rsa.pub file is written to to the .ssh // dir. func MakeFakeHomeNoEnvironments(c *gc.C, certNames ...string) *FakeHome { fake := MakeEmptyFakeHome(c) for _, name := range certNames { err := ioutil.WriteFile(osenv.JujuHomePath(name+"-cert.pem"), []byte(CACert), 0600) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(osenv.JujuHomePath(name+"-private-key.pem"), []byte(CAKey), 0600) c.Assert(err, gc.IsNil) } err := os.Mkdir(HomePath(".ssh"), 0777) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(HomePath(".ssh", "id_rsa.pub"), []byte("auth key\n"), 0666) c.Assert(err, gc.IsNil) return fake } // MakeFakeHome creates a new temporary directory through the test checker, // and overrides the HOME environment variable to point to this new temporary // directory. // // A new ~/.juju/environments.yaml file is created with the content of the // `envConfig` parameter, and CAKeys are written for each of the 'certNames' // specified. func MakeFakeHome(c *gc.C, envConfig string, certNames ...string) *FakeHome { fake := MakeFakeHomeNoEnvironments(c, certNames...) envs := osenv.JujuHomePath("environments.yaml") err := ioutil.WriteFile(envs, []byte(envConfig), 0644) c.Assert(err, gc.IsNil) return fake } func MakeEmptyFakeHome(c *gc.C) *FakeHome { fake := MakeEmptyFakeHomeWithoutJuju(c) err := os.Mkdir(osenv.JujuHome(), 0700) c.Assert(err, gc.IsNil) return fake } func MakeEmptyFakeHomeWithoutJuju(c *gc.C) *FakeHome { oldHomeEnv := osenv.Home() oldEnvironment := make(map[string]string) for _, name := range []string{ osenv.JujuHomeEnvKey, osenv.JujuEnvEnvKey, osenv.JujuLoggingConfigEnvKey, } { oldEnvironment[name] = os.Getenv(name) } fakeHome := c.MkDir() osenv.SetHome(fakeHome) os.Setenv(osenv.JujuHomeEnvKey, "") os.Setenv(osenv.JujuEnvEnvKey, "") os.Setenv(osenv.JujuLoggingConfigEnvKey, "") jujuHome := filepath.Join(fakeHome, ".juju") oldJujuHome := osenv.SetJujuHome(jujuHome) return &FakeHome{ oldHomeEnv: oldHomeEnv, oldEnvironment: oldEnvironment, oldJujuHome: oldJujuHome, files: []TestFile{}, } } func HomePath(names ...string) string { all := append([]string{osenv.Home()}, names...) return filepath.Join(all...) } func (h *FakeHome) Restore() { osenv.SetJujuHome(h.oldJujuHome) for name, value := range h.oldEnvironment { os.Setenv(name, value) } osenv.SetHome(h.oldHomeEnv) } func (h *FakeHome) AddFiles(c *gc.C, files []TestFile) { for _, f := range files { path := HomePath(f.Name) err := os.MkdirAll(filepath.Dir(path), 0700) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(path, []byte(f.Data), 0666) c.Assert(err, gc.IsNil) h.files = append(h.files, f) } } // FileContents returns the test file contents for the // given specified path (which may be relative, so // we compare with the base filename only). func (h *FakeHome) FileContents(c *gc.C, path string) string { for _, f := range h.files { if filepath.Base(f.Name) == filepath.Base(path) { return f.Data } } c.Fatalf("path attribute holds unknown test file: %q", path) panic("unreachable") } // FileExists returns if the given relative file path exists // in the fake home. func (h *FakeHome) FileExists(path string) bool { for _, f := range h.files { if f.Name == path { return true } } return false } func MakeFakeHomeWithFiles(c *gc.C, files []TestFile) *FakeHome { fake := MakeEmptyFakeHome(c) fake.AddFiles(c, files) return fake } func MakeSampleHome(c *gc.C) *FakeHome { return MakeFakeHome(c, SingleEnvConfig, SampleCertName) } func MakeMultipleEnvHome(c *gc.C) *FakeHome { return MakeFakeHome(c, MultipleEnvConfig, SampleCertName, SampleCertName+"-2") } type FakeHomeSuite struct { testbase.LoggingSuite Home *FakeHome } func (s *FakeHomeSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.Home = MakeSampleHome(c) s.AddCleanup(func(*gc.C) { s.Home.Restore() }) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/charm.go���������������������������������������0000644�0000153�0000161�00000015240�12321735642�023765� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "fmt" "go/build" "os" "os/exec" "path/filepath" "sync" "launchpad.net/juju-core/charm" ) func check(err error) { if err != nil { panic(err) } } // Repo represents a charm repository used for testing. type Repo struct { once sync.Once path string } func (r *Repo) Path() string { r.once.Do(r.init) return r.path } // init is called once when r.Path() is called for the first time, and // it initializes r.path to the location of the local testing // repository. func (r *Repo) init() { p, err := build.Import("launchpad.net/juju-core/testing", "", build.FindOnly) check(err) r.path = filepath.Join(p.Dir, "repo") } // Charms represents the specific charm repository stored in this package and // used by the Juju unit tests. The series name is "quantal". var Charms = &Repo{} func clone(dst, src string) string { check(exec.Command("cp", "-r", src, dst).Run()) return filepath.Join(dst, filepath.Base(src)) } // DirPath returns the path to a charm directory with the given name in the // default series func (r *Repo) DirPath(name string) string { return filepath.Join(r.Path(), "quantal", name) } // Dir returns the actual charm.Dir named name. func (r *Repo) Dir(name string) *charm.Dir { ch, err := charm.ReadDir(r.DirPath(name)) check(err) return ch } // ClonedDirPath returns the path to a new copy of the default charm directory // named name. func (r *Repo) ClonedDirPath(dst, name string) string { return clone(dst, r.DirPath(name)) } // RenamedClonedDirPath returns the path to a new copy of the default // charm directory named name, but renames it to newName. func (r *Repo) RenamedClonedDirPath(dst, name, newName string) string { newDst := clone(dst, r.DirPath(name)) renamedDst := filepath.Join(filepath.Dir(newDst), newName) check(os.Rename(newDst, renamedDst)) return renamedDst } // ClonedDir returns an actual charm.Dir based on a new copy of the charm directory // named name, in the directory dst. func (r *Repo) ClonedDir(dst, name string) *charm.Dir { ch, err := charm.ReadDir(r.ClonedDirPath(dst, name)) check(err) return ch } // ClonedURL makes a copy of the charm directory. It will create a directory // with the series name if it does not exist, and then clone the charm named // name into that directory. The return value is a URL pointing at the local // charm. func (r *Repo) ClonedURL(dst, series, name string) *charm.URL { dst = filepath.Join(dst, series) if err := os.MkdirAll(dst, os.FileMode(0777)); err != nil { panic(fmt.Errorf("cannot make destination directory: %v", err)) } clone(dst, r.DirPath(name)) return &charm.URL{ Reference: charm.Reference{ Schema: "local", Name: name, Revision: -1, }, Series: series, } } // BundlePath returns the path to a new charm bundle file created from the // charm directory named name, in the directory dst. func (r *Repo) BundlePath(dst, name string) string { dir := r.Dir(name) path := filepath.Join(dst, "bundle.charm") file, err := os.Create(path) check(err) defer file.Close() check(dir.BundleTo(file)) return path } // Bundle returns an actual charm.Bundle created from a new charm bundle file // created from the charm directory named name, in the directory dst. func (r *Repo) Bundle(dst, name string) *charm.Bundle { ch, err := charm.ReadBundle(r.BundlePath(dst, name)) check(err) return ch } // MockCharmStore implements charm.Repository and is used to isolate tests // that would otherwise need to hit the real charm store. type MockCharmStore struct { charms map[string]map[int]*charm.Bundle AuthAttrs string TestMode bool DefaultSeries string } func NewMockCharmStore() *MockCharmStore { return &MockCharmStore{charms: map[string]map[int]*charm.Bundle{}} } func (s *MockCharmStore) WithAuthAttrs(auth string) charm.Repository { s.AuthAttrs = auth return s } func (s *MockCharmStore) WithTestMode(testMode bool) charm.Repository { s.TestMode = testMode return s } func (s *MockCharmStore) WithDefaultSeries(series string) charm.Repository { s.DefaultSeries = series return s } func (s *MockCharmStore) Resolve(ref charm.Reference) (*charm.URL, error) { if s.DefaultSeries == "" { return nil, fmt.Errorf("missing default series, cannot resolve charm url: %q", ref) } return &charm.URL{Reference: ref, Series: s.DefaultSeries}, nil } // SetCharm adds and removes charms in s. The affected charm is identified by // charmURL, which must be revisioned. If bundle is nil, the charm will be // removed; otherwise, it will be stored. It is an error to store a bundle // under a charmURL that does not share its name and revision. func (s *MockCharmStore) SetCharm(charmURL *charm.URL, bundle *charm.Bundle) error { base := charmURL.WithRevision(-1).String() if charmURL.Revision < 0 { return fmt.Errorf("bad charm url revision") } if bundle == nil { delete(s.charms[base], charmURL.Revision) return nil } bundleRev := bundle.Revision() bundleName := bundle.Meta().Name if bundleName != charmURL.Name || bundleRev != charmURL.Revision { return fmt.Errorf("charm url %s mismatch with bundle %s-%d", charmURL, bundleName, bundleRev) } if _, found := s.charms[base]; !found { s.charms[base] = map[int]*charm.Bundle{} } s.charms[base][charmURL.Revision] = bundle return nil } // interpret extracts from charmURL information relevant to both Latest and // Get. The returned "base" is always the string representation of the // unrevisioned part of charmURL; the "rev" wil be taken from the charmURL if // available, and will otherwise be the revision of the latest charm in the // store with the same "base". func (s *MockCharmStore) interpret(charmURL *charm.URL) (base string, rev int) { base, rev = charmURL.WithRevision(-1).String(), charmURL.Revision if rev == -1 { for candidate := range s.charms[base] { if candidate > rev { rev = candidate } } } return } // Get implements charm.Repository.Get. func (s *MockCharmStore) Get(charmURL *charm.URL) (charm.Charm, error) { base, rev := s.interpret(charmURL) charm, found := s.charms[base][rev] if !found { return nil, fmt.Errorf("charm not found in mock store: %s", charmURL) } return charm, nil } // Latest implements charm.Repository.Latest. func (s *MockCharmStore) Latest(charmURLs ...*charm.URL) ([]charm.CharmRevision, error) { result := make([]charm.CharmRevision, len(charmURLs)) for i, curl := range charmURLs { charmURL := curl.WithRevision(-1) base, rev := s.interpret(charmURL) if _, found := s.charms[base][rev]; !found { result[i].Err = fmt.Errorf("charm not found in mock store: %s", charmURL) } else { result[i].Revision = rev } } return result, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/targz.go���������������������������������������0000644�0000153�0000161�00000003277�12321735642�024031� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "archive/tar" "bytes" "compress/gzip" "crypto/sha256" "fmt" "io" "os" ) // TarFile represents a file to be archived. type TarFile struct { Header tar.Header Contents string } var modes = map[os.FileMode]byte{ os.ModeDir: tar.TypeDir, os.ModeSymlink: tar.TypeSymlink, 0: tar.TypeReg, } // NewTarFile returns a new TarFile instance with the given file // mode and contents. func NewTarFile(name string, mode os.FileMode, contents string) *TarFile { ftype := modes[mode&os.ModeType] if ftype == 0 { panic(fmt.Errorf("unexpected mode %v", mode)) } // NOTE: Do not set attributes (e.g. times) dynamically, as various // tests expect the contents of fake tools archives to be unchanging. return &TarFile{ Header: tar.Header{ Typeflag: ftype, Name: name, Size: int64(len(contents)), Mode: int64(mode & 0777), Uname: "ubuntu", Gname: "ubuntu", }, Contents: contents, } } // TarGz returns the given files in gzipped tar-archive format, along with the sha256 checksum. func TarGz(files ...*TarFile) ([]byte, string) { var buf bytes.Buffer sha256hash := sha256.New() gzw := gzip.NewWriter(io.MultiWriter(&buf, sha256hash)) tarw := tar.NewWriter(gzw) for _, f := range files { err := tarw.WriteHeader(&f.Header) if err != nil { panic(err) } _, err = tarw.Write([]byte(f.Contents)) if err != nil { panic(err) } } err := tarw.Close() if err != nil { panic(err) } err = gzw.Close() if err != nil { panic(err) } checksum := fmt.Sprintf("%x", sha256hash.Sum(nil)) return buf.Bytes(), checksum } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/filetesting/�����������������������������������0000755�0000153�0000161�00000000000�12321736000�024644� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/filetesting/filetesting.go���������������������0000644�0000153�0000161�00000011510�12321735776�027531� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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 } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/filetesting/package_test.go��������������������0000644�0000153�0000161�00000000336�12321735776�027652� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/filetesting/filetesting_test.go����������������0000644�0000153�0000161�00000021661�12321735776�030600� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// 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) } �������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/attrs.go���������������������������������������0000644�0000153�0000161�00000000770�12321735642�024032� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package testing // Attrs is a convenience type for messing // around with configuration attributes. type Attrs map[string]interface{} func (a Attrs) Merge(with Attrs) Attrs { new := make(Attrs) for attr, val := range a { new[attr] = val } for attr, val := range with { new[attr] = val } return new } func (a Attrs) Delete(attrNames ...string) Attrs { new := make(Attrs) for attr, val := range a { new[attr] = val } for _, attr := range attrNames { delete(new, attr) } return new } ��������juju-core_1.18.1/src/launchpad.net/juju-core/testing/sshdata/���������������������������������������0000755�0000153�0000161�00000000000�12321735642�023771� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/sshdata/authorized_keys������������������������0000644�0000153�0000161�00000000705�12321735642�027127� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������no-pty,no-X11-forwarding,no-agent-forwarding,command="sleep 20" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUKcDY9VUJ2jIQrHuWL04G3Ek7sMc7M8xBHFSprg61fsh+DIoUOqyr48G7W2mSh0W48GPscdS/dAz6PPyLTShp9HtX65P7dBDxE4vnFyAeaOIE+rQv2sgdqm8kyhTvHG2XyYQ4uaswvnkRca1xBjdTx93RSUpVDGod4+5GDXIMtj8pYMCiA1VNmg5YB9ZKQfyam53XERrhym//fOefJZBdQq/1ZYTyUpZLfSbQe3dIXQhdKOzkz92dRQMMCLKR5W0x/VjvBk1iLyH34fC2DvU2XK0xfrZ2QpawtEiNzHatFDfxKIRHoXnWI4tgl1OeF2bc8MU8je1lZsD8fc5Qj6Dz comment �����������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/sshdata/id_rsa.pub�����������������������������0000644�0000153�0000161�00000000605�12321735642�025743� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUKcDY9VUJ2jIQrHuWL04G3Ek7sMc7M8xBHFSprg61fsh+DIoUOqyr48G7W2mSh0W48GPscdS/dAz6PPyLTShp9HtX65P7dBDxE4vnFyAeaOIE+rQv2sgdqm8kyhTvHG2XyYQ4uaswvnkRca1xBjdTx93RSUpVDGod4+5GDXIMtj8pYMCiA1VNmg5YB9ZKQfyam53XERrhym//fOefJZBdQq/1ZYTyUpZLfSbQe3dIXQhdKOzkz92dRQMMCLKR5W0x/VjvBk1iLyH34fC2DvU2XK0xfrZ2QpawtEiNzHatFDfxKIRHoXnWI4tgl1OeF2bc8MU8je1lZsD8fc5Qj6Dz comment ���������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/sshdata/sshd_config����������������������������0000644�0000153�0000161�00000000074�12321735642�026203� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PasswordAuthentication no PermitRootLogin no StrictModes no ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/sshdata/id_rsa���������������������������������0000644�0000153�0000161�00000003217�12321735642�025160� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEA1CnA2PVVCdoyEKx7li9OBtxJO7DHOzPMQRxUqa4OtX7IfgyK FDqsq+PBu1tpkodFuPBj7HHUv3QM+jz8i00oafR7V+uT+3QQ8ROL5xcgHmjiBPq0 L9rIHapvJMoU7xxtl8mEOLmrML55EXGtcQY3U8fd0UlKVQxqHePuRg1yDLY/KWDA ogNVTZoOWAfWSkH8mpud1xEa4cpv/3znnyWQXUKv9WWE8lKWS30m0Ht3SF0IXSjs 5M/dnUUDDAiykeVtMf1Y7wZNYi8h9+Hwtg71NlytMX62dkKWsLRIjcx2rRQ38SiE R6F51iOLYJdTnhdm3PDFPI3tZWbA/H3OUI+g8wIDAQABAoIBAQC4eKFkQ1QwX9a+ JqrCd9kZH/pQ+zEPqccEKfT3IA9MAbOj3ZVFZ4rxXfP5ys6tiA64cdQAJy17VhVe kx5mpKzqC5gfZwbhURbV9k/jm3ji2AEFoVKAdXMgUCuZSlgPW6Fyo9XiWJWfqU2t PDE5H9WFdDfLHDWUY1n1KVTo9hHfOBWqqWDAs5JhH8mgk1u/3naU1Gk4JN0vLvHR 66fi6CIRmdPxXRFJ+9MTSO7sGPol5z4F8bLjevQWj88gydSlg/JE95RC92TWOct+ Ee6ycV4KNv5LsyTWZBQ7Jt5SgiGuPAjcsrfeuLLTZhhgGKeLArT3TTV5pb9xrm/Q OKZLn+S5AoGBAO6E11VleablLULG4VBgYbZSE6EgIYxbcPmMPXDWnwuKv1d6Hoz7 cxBbMLYLG9JCkXVWNMZC8Z89/jwfiBq4u8cf8MDgIN8AzrV5HMJk0ld68TyAZhPk vHzwytkC5cl7nrwBZV38j3LanWi2x0Od7NjcNeE9Df6ezPAFW1Y4JTLdAoGBAOO2 asFYXQLlgZ1ne2LhJ7DdGt3xR8acHGLfR08TUVFlp3RWz5259TjXWfCTTxMuUidt 6BpDca2dZUq9+RT1070BZVtrZ3ugTRyM8T/j/Hk8gkzzRjbWuSWXTZdEx28y2Aqp g48pScqDGd2TZQrLlq1sgGA8dVkKgtNZFFwx/t4PAoGAFm4Rnes8BHDaz8PKHnfh 2HZheC6AWSWkOyNwB7N3I2u50K0gQFvJF0msDAKBbEVsTl97+QUEmdgHl3nTn8et iO09A7ZQAQifZ7tq8/DtQuLB2tGsR8Sj4Q/3026NU2Q3qjBrauo8Ry29zcIafQZV 5pLW0kaekUo3tX6bd0EyZlUCgYEA2l6VAdU7XQu6wLf8G1bQOUvP+AbqTgxnLGny eGKOGQDjFS4q65bpExlxpsOdkUdagXVkn8sNuPC3snV76B3Wb+pmyMS2+FdBm5+N uzdnZtwgGKszKmPAKcxvHCFZH8oiZBcrkp6FpS8UHui6fR+/34+tjJxt7HWoAAXt AHzxbQECgYEAnoz4R1MlDWgw8TGtLouSik3j5pxz8uyMfzOcPC6ashqj8wr5CVAI 3ecQzdopW4QMgyt4+8EwGeCcr6KL8BTCxtIu1oBtn1NK2jo/Zl9BvzSeQKLH9Qht PjtJLo1YzX7AOJg3KnrTRPJyLoLp/fy+yNHzzQWJbYfosPoDF/2O2Cc= -----END RSA PRIVATE KEY----- ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/channel.go�������������������������������������0000644�0000153�0000161�00000007366�12321735642�024315� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "reflect" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" ) // NotifyAsserterC gives helper functions for making assertions about how a // channel operates (whether we get a receive event or not, whether it is // closed, etc.) type NotifyAsserterC struct { // C is a gocheck C structure for doing assertions C *gc.C // Chan is the channel we want to receive on Chan <-chan struct{} // Precond will be called before waiting on the channel, can be nil Precond func() } // AssertReceive will ensure that we get an event on the channel and the // channel is not closed. func (a *NotifyAsserterC) AssertReceive() { if a.Precond != nil { a.Precond() } select { case _, ok := <-a.Chan: a.C.Assert(ok, jc.IsTrue) case <-time.After(LongWait): a.C.Fatalf("timed out waiting for channel message") } } // AssertOneReceive checks that we have exactly one message, and no more func (a *NotifyAsserterC) AssertOneReceive() { a.AssertReceive() a.AssertNoReceive() } // AssertClosed ensures that we get a closed event on the channel func (a *NotifyAsserterC) AssertClosed() { if a.Precond != nil { a.Precond() } select { case _, ok := <-a.Chan: a.C.Assert(ok, jc.IsFalse) case <-time.After(LongWait): a.C.Fatalf("timed out waiting for channel to close") } } // Assert that we fail to receive on the channel after a short wait. func (a *NotifyAsserterC) AssertNoReceive() { select { case <-a.Chan: a.C.Fatalf("unexpected receive") case <-time.After(ShortWait): } } // ContentAsserterC is like NotifyAsserterC in that it checks the behavior of a // channel. The difference is that we expect actual content on the channel, so // callers need to put that into and out of an 'interface{}' type ContentAsserterC struct { // C is a gocheck C structure for doing assertions C *gc.C // Chan is the channel we want to receive on Chan interface{} // Precond will be called before waiting on the channel, can be nil Precond func() } // recv waits to receive a value on the channe for the given // time. It returns the value received, if any, whether it // was received ok (the channel was not closed) and // whether the receive timed out. func (a *ContentAsserterC) recv(timeout time.Duration) (val interface{}, ok, timedOut bool) { if a.Precond != nil { a.Precond() } which, v, ok := reflect.Select([]reflect.SelectCase{{ Dir: reflect.SelectRecv, Chan: reflect.ValueOf(a.Chan), }, { Dir: reflect.SelectRecv, Chan: reflect.ValueOf(time.After(timeout)), }}) switch which { case 0: a.C.Assert(ok, jc.IsTrue) return v.Interface(), ok, false case 1: return nil, false, true } panic("unreachable") } // AssertReceive will ensure that we get an event on the channel and the // channel is not closed. It will return the content received func (a *ContentAsserterC) AssertReceive() interface{} { v, ok, timedOut := a.recv(LongWait) if timedOut { a.C.Fatalf("timed out waiting for channel message") } a.C.Assert(ok, jc.IsTrue) return v } // AssertOneReceive checks that we have exactly one message, and no more func (a *ContentAsserterC) AssertOneReceive() interface{} { res := a.AssertReceive() a.AssertNoReceive() return res } // AssertClosed ensures that we get a closed event on the channel func (a *ContentAsserterC) AssertClosed() { _, ok, timedOut := a.recv(LongWait) if timedOut { a.C.Fatalf("timed out waiting for channel to close") } a.C.Assert(ok, jc.IsFalse) } // Assert that we fail to receive on the channel after a short wait. func (a *ContentAsserterC) AssertNoReceive() { content, _, timedOut := a.recv(ShortWait) if timedOut { return } a.C.Fatalf("unexpected receive: %#v", content) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/mgo.go�����������������������������������������0000644�0000153�0000161�00000026276�12321735776�023500� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "bufio" "crypto/tls" "crypto/x509" "fmt" "io" "io/ioutil" "net" "os" "os/exec" "path/filepath" "strconv" "strings" stdtesting "testing" "time" "github.com/juju/loggo" "labix.org/v2/mgo" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cert" "launchpad.net/juju-core/log" "launchpad.net/juju-core/utils" ) var ( // MgoServer is a shared mongo server used by tests. MgoServer = &MgoInstance{ssl: true} logger = loggo.GetLogger("juju.testing") ) type MgoInstance struct { // addr holds the address of the MongoDB server addr string // MgoPort holds the port of the MongoDB server. port int // server holds the running MongoDB command. server *exec.Cmd // exited receives a value when the mongodb server exits. exited <-chan struct{} // dir holds the directory that MongoDB is running in. dir string // ssl determines whether the MongoDB server will use TLS ssl bool // Params is a list of additional parameters that will be passed to // the mongod application Params []string } // Addr returns the address of the MongoDB server. func (m *MgoInstance) Addr() string { return m.addr } // Port returns the port of the MongoDB server. func (m *MgoInstance) Port() int { return m.port } // We specify a timeout to mgo.Dial, to prevent // mongod failures hanging the tests. const mgoDialTimeout = 15 * time.Second // MgoSuite is a suite that deletes all content from the shared MongoDB // server at the end of every test and supplies a connection to the shared // MongoDB server. type MgoSuite struct { Session *mgo.Session } // Start starts a MongoDB server in a temporary directory. func (inst *MgoInstance) Start(ssl bool) error { dbdir, err := ioutil.TempDir("", "test-mgo") if err != nil { return err } logger.Debugf("starting mongo in %s", dbdir) // give them all the same keyfile so they can talk appropriately keyFilePath := filepath.Join(dbdir, "keyfile") err = ioutil.WriteFile(keyFilePath, []byte("not very secret"), 0600) if err != nil { return fmt.Errorf("cannot write key file: %v", err) } pemPath := filepath.Join(dbdir, "server.pem") err = ioutil.WriteFile(pemPath, []byte(ServerCert+ServerKey), 0600) if err != nil { return fmt.Errorf("cannot write cert/key PEM: %v", err) } inst.port = FindTCPPort() inst.addr = fmt.Sprintf("localhost:%d", inst.port) inst.dir = dbdir inst.ssl = ssl if err := inst.run(); err != nil { inst.addr = "" inst.port = 0 os.RemoveAll(inst.dir) inst.dir = "" } logger.Debugf("started mongod pid %d in %s on port %d", inst.server.Process.Pid, dbdir, inst.port) return err } // run runs the MongoDB server at the // address and directory already configured. func (inst *MgoInstance) run() error { if inst.server != nil { panic("mongo server is already running") } mgoport := strconv.Itoa(inst.port) mgoargs := []string{ "--auth", "--dbpath", inst.dir, "--port", mgoport, "--nssize", "1", "--noprealloc", "--smallfiles", "--nojournal", "--nounixsocket", "--oplogSize", "10", "--keyFile", filepath.Join(inst.dir, "keyfile"), } if inst.ssl { mgoargs = append(mgoargs, "--sslOnNormalPorts", "--sslPEMKeyFile", filepath.Join(inst.dir, "server.pem"), "--sslPEMKeyPassword", "ignored") } if inst.Params != nil { mgoargs = append(mgoargs, inst.Params...) } server := exec.Command("mongod", mgoargs...) out, err := server.StdoutPipe() if err != nil { return err } server.Stderr = server.Stdout exited := make(chan struct{}) started := make(chan struct{}) go func() { <-started lines := readLines(fmt.Sprintf("mongod:%v", mgoport), out, 20) err := server.Wait() exitErr, _ := err.(*exec.ExitError) if err == nil || exitErr != nil && exitErr.Exited() { // mongodb has exited without being killed, so print the // last few lines of its log output. log.Errorf("mongodb has exited without being killed") for _, line := range lines { log.Errorf("mongod: %s", line) } } close(exited) }() inst.exited = exited err = server.Start() close(started) if err != nil { return err } inst.server = server return nil } func (inst *MgoInstance) kill() { inst.server.Process.Kill() <-inst.exited inst.server = nil inst.exited = nil } func (inst *MgoInstance) Destroy() { if inst.server != nil { logger.Debugf("killing mongod pid %d in %s on port %d", inst.server.Process.Pid, inst.dir, inst.port) inst.kill() os.RemoveAll(inst.dir) inst.addr, inst.dir = "", "" } } // Restart restarts the mongo server, useful for // testing what happens when a state server goes down. func (inst *MgoInstance) Restart() { logger.Debugf("restarting mongod pid %d in %s on port %d", inst.server.Process.Pid, inst.dir, inst.port) inst.kill() if err := inst.Start(inst.ssl); err != nil { panic(err) } } // MgoTestPackage should be called to register the tests for any package that // requires a MongoDB server. func MgoTestPackage(t *stdtesting.T) { MgoTestPackageSsl(t, true) } func MgoTestPackageSsl(t *stdtesting.T, ssl bool) { if err := MgoServer.Start(ssl); err != nil { t.Fatal(err) } defer MgoServer.Destroy() gc.TestingT(t) } func (s *MgoSuite) SetUpSuite(c *gc.C) { if MgoServer.addr == "" { panic("MgoSuite tests must be run with MgoTestPackage") } mgo.SetStats(true) // Make tests that use password authentication faster. utils.FastInsecureHash = true } // readLines reads lines from the given reader and returns // the last n non-empty lines, ignoring empty lines. func readLines(prefix string, r io.Reader, n int) []string { br := bufio.NewReader(r) lines := make([]string, n) i := 0 for { line, err := br.ReadString('\n') if line = strings.TrimRight(line, "\n"); line != "" { logger.Tracef("%s: %s", prefix, line) lines[i%n] = line i++ } if err != nil { break } } final := make([]string, 0, n+1) if i > n { final = append(final, fmt.Sprintf("[%d lines omitted]", i-n)) } for j := 0; j < n; j++ { if line := lines[(j+i)%n]; line != "" { final = append(final, line) } } return final } func (s *MgoSuite) TearDownSuite(c *gc.C) { utils.FastInsecureHash = false } // MustDial returns a new connection to the MongoDB server, and panics on // errors. func (inst *MgoInstance) MustDial() *mgo.Session { s, err := mgo.DialWithInfo(inst.DialInfo()) if err != nil { panic(err) } return s } // Dial returns a new connection to the MongoDB server. func (inst *MgoInstance) Dial() (*mgo.Session, error) { return mgo.DialWithInfo(inst.DialInfo()) } // DialInfo returns information suitable for dialling the // receiving MongoDB instance. func (inst *MgoInstance) DialInfo() *mgo.DialInfo { return MgoDialInfo(inst.addr) } // DialDirect returns a new direct connection to the shared MongoDB server. This // must be used if you're connecting to a replicaset that hasn't been initiated // yet. func (inst *MgoInstance) DialDirect() (*mgo.Session, error) { info := inst.DialInfo() info.Direct = true return mgo.DialWithInfo(info) } // MustDialDirect works like DialDirect, but panics on errors. func (inst *MgoInstance) MustDialDirect() *mgo.Session { session, err := inst.DialDirect() if err != nil { panic(err) } return session } // MgoDialInfo returns a DialInfo suitable // for dialling an MgoInstance at any of the // given addresses. func MgoDialInfo(addrs ...string) *mgo.DialInfo { pool := x509.NewCertPool() xcert, err := cert.ParseCert([]byte(CACert)) if err != nil { panic(err) } pool.AddCert(xcert) tlsConfig := &tls.Config{ RootCAs: pool, ServerName: "anything", } return &mgo.DialInfo{ Addrs: addrs, Dial: func(addr net.Addr) (net.Conn, error) { conn, err := tls.Dial("tcp", addr.String(), tlsConfig) if err != nil { logger.Debugf("tls.Dial(%s) failed with %v", addr, err) return nil, err } return conn, nil }, Timeout: mgoDialTimeout, } } func (s *MgoSuite) SetUpTest(c *gc.C) { mgo.ResetStats() s.Session = MgoServer.MustDial() } // Reset deletes all content from the MongoDB server and panics if it encounters // errors. func (inst *MgoInstance) Reset() { session := inst.MustDial() defer session.Close() dbnames, ok := resetAdminPasswordAndFetchDBNames(session) if ok { log.Infof("Reset successfully reset admin password") } else { // We restart it to regain access. This should only // happen when tests fail. log.Noticef("testing: restarting MongoDB server after unauthorized access") inst.Destroy() if err := inst.Start(inst.ssl); err != nil { panic(err) } return } for _, name := range dbnames { switch name { case "admin", "local", "config": default: if err := session.DB(name).DropDatabase(); err != nil { panic(fmt.Errorf("Cannot drop MongoDB database %v: %v", name, err)) } } } } // resetAdminPasswordAndFetchDBNames logs into the database with a // plausible password and returns all the database's db names. We need // to try several passwords because we don't know what state the mongo // server is in when Reset is called. If the test has set a custom // password, we're out of luck, but if they are using // DefaultStatePassword, we can succeed. func resetAdminPasswordAndFetchDBNames(session *mgo.Session) ([]string, bool) { // First try with no password dbnames, err := session.DatabaseNames() if err == nil { return dbnames, true } if !isUnauthorized(err) { panic(err) } // Then try the two most likely passwords in turn. for _, password := range []string{ DefaultMongoPassword, utils.UserPasswordHash(DefaultMongoPassword, utils.CompatSalt), } { admin := session.DB("admin") if err := admin.Login("admin", password); err != nil { log.Infof("failed to log in with password %q", password) continue } dbnames, err := session.DatabaseNames() if err == nil { if err := admin.RemoveUser("admin"); err != nil { panic(err) } return dbnames, true } if !isUnauthorized(err) { panic(err) } log.Infof("unauthorized access when getting database names; password %q", password) } return nil, false } // isUnauthorized is a copy of the same function in state/open.go. func isUnauthorized(err error) bool { if err == nil { return false } // Some unauthorized access errors have no error code, // just a simple error string. if err.Error() == "auth fails" { return true } if err, ok := err.(*mgo.QueryError); ok { return err.Code == 10057 || err.Message == "need to login" || err.Message == "unauthorized" } return false } func (s *MgoSuite) TearDownTest(c *gc.C) { MgoServer.Reset() s.Session.Close() for i := 0; ; i++ { stats := mgo.GetStats() if stats.SocketsInUse == 0 && stats.SocketsAlive == 0 { break } if i == 20 { c.Fatal("Test left sockets in a dirty state") } c.Logf("Waiting for sockets to die: %d in use, %d alive", stats.SocketsInUse, stats.SocketsAlive) time.Sleep(500 * time.Millisecond) } } // FindTCPPort finds an unused TCP port and returns it. // Use of this function has an inherent race condition - another // process may claim the port before we try to use it. // We hope that the probability is small enough during // testing to be negligible. func FindTCPPort() int { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { panic(err) } l.Close() return l.Addr().(*net.TCPAddr).Port } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/cert.go����������������������������������������0000644�0000153�0000161�00000003442�12321735642�023631� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing import ( "crypto/rsa" "crypto/tls" "crypto/x509" "fmt" "time" "launchpad.net/juju-core/cert" ) func init() { if err := verifyCertificates(); err != nil { panic(err) } } // CACert and CAKey make up a CA key pair. // CACertX509 and CAKeyRSA hold their parsed equivalents. // ServerCert and ServerKey hold a CA-signed server cert/key. var ( CACert, CAKey = mustNewCA() CACertX509, CAKeyRSA = mustParseCertAndKey([]byte(CACert), []byte(CAKey)) ServerCert, ServerKey = mustNewServer() ) func verifyCertificates() error { _, err := tls.X509KeyPair([]byte(CACert), []byte(CAKey)) if err != nil { return fmt.Errorf("bad CA cert key pair: %v", err) } _, err = tls.X509KeyPair([]byte(ServerCert), []byte(ServerKey)) if err != nil { return fmt.Errorf("bad server cert key pair: %v", err) } return cert.Verify([]byte(ServerCert), []byte(CACert), time.Now()) } func mustNewCA() (string, string) { cert.KeyBits = 512 caCert, caKey, err := cert.NewCA("juju testing", time.Now().AddDate(10, 0, 0)) if err != nil { panic(err) } return string(caCert), string(caKey) } func mustNewServer() (string, string) { cert.KeyBits = 512 var hostnames []string srvCert, srvKey, err := cert.NewServer([]byte(CACert), []byte(CAKey), time.Now().AddDate(10, 0, 0), hostnames) if err != nil { panic(err) } return string(srvCert), string(srvKey) } func mustParseCert(pemData string) *x509.Certificate { cert, err := cert.ParseCert([]byte(pemData)) if err != nil { panic(err) } return cert } func mustParseCertAndKey(certPEM, keyPEM []byte) (*x509.Certificate, *rsa.PrivateKey) { cert, key, err := cert.ParseCertAndKey(certPEM, keyPEM) if err != nil { panic(err) } return cert, key } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/testing/environ_test.go��������������������������������0000644�0000153�0000161�00000002732�12321735642�025414� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing_test import ( "os" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/testing" ) type TestingEnvironSuite struct { home string jujuHome string } var _ = gc.Suite(&TestingEnvironSuite{}) func (s *TestingEnvironSuite) SetUpTest(c *gc.C) { s.home = osenv.Home() s.jujuHome = os.Getenv("JUJU_HOME") osenv.SetHome("/home/eric") os.Setenv("JUJU_HOME", "/home/eric/juju") osenv.SetJujuHome("/home/eric/juju") } func (s *TestingEnvironSuite) TearDownTest(c *gc.C) { osenv.SetHome(s.home) os.Setenv("JUJU_HOME", s.jujuHome) } func (s *TestingEnvironSuite) TestFakeHomeReplacesEnvironment(c *gc.C) { _ = testing.MakeEmptyFakeHome(c) c.Assert(osenv.Home(), gc.Not(gc.Equals), "/home/eric") c.Assert(os.Getenv("JUJU_HOME"), gc.Equals, "") c.Assert(osenv.JujuHome(), gc.Not(gc.Equals), "/home/eric/juju") } func (s *TestingEnvironSuite) TestFakeHomeRestoresEnvironment(c *gc.C) { fake := testing.MakeEmptyFakeHome(c) fake.Restore() c.Assert(osenv.Home(), gc.Equals, "/home/eric") c.Assert(os.Getenv("JUJU_HOME"), gc.Equals, "/home/eric/juju") c.Assert(osenv.JujuHome(), gc.Equals, "/home/eric/juju") } func (s *TestingEnvironSuite) TestFakeHomeSetsConfigJujuHome(c *gc.C) { _ = testing.MakeEmptyFakeHome(c) expected := filepath.Join(osenv.Home(), ".juju") c.Assert(osenv.JujuHome(), gc.Equals, expected) } ��������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/���������������������������������������������0000755�0000153�0000161�00000000000�12321735643�022660� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/options.go�����������������������������������0000644�0000153�0000161�00000025467�12321735642�024717� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cloudinit import ( "fmt" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" ) // CloudToolsPrefsPath defines the default location of // apt_preferences(5) file for the cloud-tools pocket. const CloudToolsPrefsPath = "/etc/apt/preferences.d/50-cloud-tools" // SetAttr sets an arbitrary attribute in the cloudinit config. // If value is nil the attribute will be deleted; otherwise // the value will be marshalled according to the rules // of the goyaml Marshal function. func (cfg *Config) SetAttr(name string, value interface{}) { cfg.set(name, value != nil, value) } // SetUser sets the user name that will be used for some other options. // The user will be assumed to already exist in the machine image. // The default user is "ubuntu". func (cfg *Config) SetUser(user string) { cfg.set("user", user != "", user) } // SetAptUpgrade sets whether cloud-init runs "apt-get upgrade" // on first boot. func (cfg *Config) SetAptUpgrade(yes bool) { cfg.set("apt_upgrade", yes, yes) } // AptUpgrade returns the value set by SetAptUpgrade, or // false if no call to SetAptUpgrade has been made. func (cfg *Config) AptUpgrade() bool { update, _ := cfg.attrs["apt_upgrade"].(bool) return update } // SetUpdate sets whether cloud-init runs "apt-get update" // on first boot. func (cfg *Config) SetAptUpdate(yes bool) { cfg.set("apt_update", yes, yes) } // AptUpdate returns the value set by SetAptUpdate, or // false if no call to SetAptUpdate has been made. func (cfg *Config) AptUpdate() bool { update, _ := cfg.attrs["apt_update"].(bool) return update } // SetAptProxy sets the URL to be used as the apt // proxy. func (cfg *Config) SetAptProxy(url string) { cfg.set("apt_proxy", url != "", url) } // SetAptMirror sets the URL to be used as the apt // mirror site. If not set, the URL is selected based // on cloud metadata in EC2 - <region>.archive.ubuntu.com func (cfg *Config) SetAptMirror(url string) { cfg.set("apt_mirror", url != "", url) } // SetAptPreserveSourcesList sets whether /etc/apt/sources.list // is overwritten by the mirror. If true, SetAptMirror above // will have no effect. func (cfg *Config) SetAptPreserveSourcesList(yes bool) { cfg.set("apt_mirror", yes, yes) } // AddAptSource adds an apt source. The key holds the // public key of the source, in the form expected by apt-key(8). func (cfg *Config) AddAptSource(name, key string, prefs *AptPreferences) { src, _ := cfg.attrs["apt_sources"].([]*AptSource) cfg.attrs["apt_sources"] = append(src, &AptSource{ Source: name, Key: key, Prefs: prefs, }, ) if prefs != nil { // Create the apt preferences file. cfg.AddFile(prefs.Path, prefs.FileContents(), 0644) } } // AptSources returns the apt sources added with AddAptSource. func (cfg *Config) AptSources() []*AptSource { srcs, _ := cfg.attrs["apt_sources"].([]*AptSource) return srcs } // SetDebconfSelections provides preseeded debconf answers // for the boot process. The given answers will be used as input // to debconf-set-selections(1). func (cfg *Config) SetDebconfSelections(answers string) { cfg.set("debconf_selections", answers != "", answers) } // AddPackage adds a package to be installed on first boot. // If any packages are specified, "apt-get update" // will be called. func (cfg *Config) AddPackage(name string) { cfg.attrs["packages"] = append(cfg.Packages(), name) } // AddPackageFromTargetRelease adds a package to be installed using // the given release, passed to apt-get with the --target-release // argument. func (cfg *Config) AddPackageFromTargetRelease(packageName, targetRelease string) { cfg.AddPackage(fmt.Sprintf("--target-release '%s' '%s'", targetRelease, packageName)) } // Packages returns a list of packages that will be // installed on first boot. func (cfg *Config) Packages() []string { pkgs, _ := cfg.attrs["packages"].([]string) return pkgs } func (cfg *Config) addCmd(kind string, c *command) { cfg.attrs[kind] = append(cfg.getCmds(kind), c) } func (cfg *Config) getCmds(kind string) []*command { cmds, _ := cfg.attrs[kind].([]*command) return cmds } // getCmdStrings returns a slice of interface{}, where // each interface's dynamic value is either a string // or slice of strings. func (cfg *Config) getCmdStrings(kind string) []interface{} { cmds := cfg.getCmds(kind) result := make([]interface{}, len(cmds)) for i, cmd := range cmds { if cmd.args != nil { result[i] = append([]string{}, cmd.args...) } else { result[i] = cmd.literal } } return result } // BootCmds returns a list of commands added with // AddBootCmd*. // // Each element in the resultant slice is either a // string or []string, corresponding to how the command // was added. func (cfg *Config) BootCmds() []interface{} { return cfg.getCmdStrings("bootcmd") } // RunCmds returns a list of commands that will be // run at first boot. // // Each element in the resultant slice is either a // string or []string, corresponding to how the command // was added. func (cfg *Config) RunCmds() []interface{} { return cfg.getCmdStrings("runcmd") } // AddRunCmd adds a command to be executed // at first boot. The command will be run // by the shell with any metacharacters retaining // their special meaning (that is, no quoting takes place). func (cfg *Config) AddRunCmd(cmd string) { cfg.addCmd("runcmd", &command{literal: cmd}) } // AddRunCmdArgs is like AddRunCmd except that the command // will be executed with the given arguments properly quoted. func (cfg *Config) AddRunCmdArgs(args ...string) { cfg.addCmd("runcmd", &command{args: args}) } // AddBootCmd is like AddRunCmd except that the // command will run very early in the boot process, // and it will run on every boot, not just the first time. func (cfg *Config) AddBootCmd(cmd string) { cfg.addCmd("bootcmd", &command{literal: cmd}) } // AddBootCmdArgs is like AddBootCmd except that the command // will be executed with the given arguments properly quoted. func (cfg *Config) AddBootCmdArgs(args ...string) { cfg.addCmd("bootcmd", &command{args: args}) } // SetDisableEC2Metadata sets whether access to the // EC2 metadata service is disabled early in boot // via a null route ( route del -host 169.254.169.254 reject). func (cfg *Config) SetDisableEC2Metadata(yes bool) { cfg.set("disable_ec2_metadata", yes, yes) } // SetFinalMessage sets to message that will be written // when the system has finished booting for the first time. // By default, the message is: // "cloud-init boot finished at $TIMESTAMP. Up $UPTIME seconds". func (cfg *Config) SetFinalMessage(msg string) { cfg.set("final_message", msg != "", msg) } // SetLocale sets the locale; it defaults to en_US.UTF-8. func (cfg *Config) SetLocale(locale string) { cfg.set("locale", locale != "", locale) } // AddMount adds a mount point. The given // arguments will be used as a line in /etc/fstab. func (cfg *Config) AddMount(args ...string) { mounts, _ := cfg.attrs["mounts"].([][]string) cfg.attrs["mounts"] = append(mounts, args) } // OutputKind represents a destination for command output. type OutputKind string const ( OutInit OutputKind = "init" OutConfig OutputKind = "config" OutFinal OutputKind = "final" OutAll OutputKind = "all" ) // SetOutput specifies destination for command output. // Valid values for the kind "init", "config", "final" and "all". // Each of stdout and stderr can take one of the following forms: // >>file // appends to file // >file // overwrites file // |command // pipes to the given command. func (cfg *Config) SetOutput(kind OutputKind, stdout, stderr string) { out, _ := cfg.attrs["output"].(map[string]interface{}) if out == nil { out = make(map[string]interface{}) } if stderr == "" { out[string(kind)] = stdout } else { out[string(kind)] = []string{stdout, stderr} } cfg.attrs["output"] = out } // Output returns the output destination passed to SetOutput for // the specified output kind. func (cfg *Config) Output(kind OutputKind) (stdout, stderr string) { if out, ok := cfg.attrs["output"].(map[string]interface{}); ok { switch out := out[string(kind)].(type) { case string: stdout = out case []string: stdout, stderr = out[0], out[1] } } return stdout, stderr } // AddSSHKey adds a pre-generated ssh key to the // server keyring. Keys that are added like this will be // written to /etc/ssh and new random keys will not // be generated. func (cfg *Config) AddSSHKey(keyType SSHKeyType, keyData string) { keys, _ := cfg.attrs["ssh_keys"].(map[SSHKeyType]string) if keys == nil { keys = make(map[SSHKeyType]string) cfg.attrs["ssh_keys"] = keys } keys[keyType] = keyData } // SetDisableRoot sets whether ssh login is disabled to the root account // via the ssh authorized key associated with the instance metadata. // It is true by default. func (cfg *Config) SetDisableRoot(disable bool) { // note that disable_root defaults to true, so we include // the option only if disable is false. cfg.set("disable_root", !disable, disable) } // AddSSHAuthorizedKey adds a set of keys in // ssh authorized_keys format (see ssh(8) for details) // that will be added to ~/.ssh/authorized_keys for the // configured user (see SetUser). func (cfg *Config) AddSSHAuthorizedKeys(keyData string) { akeys, _ := cfg.attrs["ssh_authorized_keys"].([]string) keys := ssh.SplitAuthorisedKeys(keyData) for _, key := range keys { // Ensure the key has a comment prepended with "Juju:" so we // can distinguish between Juju managed keys and those added // externally. jujuKey := ssh.EnsureJujuComment(key) akeys = append(akeys, jujuKey) } cfg.attrs["ssh_authorized_keys"] = akeys } // AddScripts is a simple shorthand for calling AddRunCmd multiple times. func (cfg *Config) AddScripts(scripts ...string) { for _, s := range scripts { cfg.AddRunCmd(s) } } // AddFile will add multiple run_cmd entries to safely set the contents of a // specific file to the requested contents. func (cfg *Config) AddFile(filename, data string, mode uint) { // Note: recent versions of cloud-init have the "write_files" // module, which can write arbitrary files. We currently support // 12.04 LTS, which uses an older version of cloud-init without // this module. p := shquote(filename) // Don't use the shell's echo builtin here; the interpretation // of escape sequences differs between shells, namely bash and // dash. Instead, we use printf (or we could use /bin/echo). cfg.AddScripts( fmt.Sprintf("install -D -m %o /dev/null %s", mode, p), fmt.Sprintf(`printf '%%s\n' %s > %s`, shquote(data), p), ) } func shquote(p string) string { return utils.ShQuote(p) } // TODO // byobu // grub_dpkg // mcollective // phone_home // puppet // resizefs // rightscale_userdata // scripts_per_boot // scripts_per_instance // scripts_per_once // scripts_user // set_hostname // set_passwords // ssh_import_id // timezone // update_etc_hosts // update_hostname ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/cloudinit.go���������������������������������0000644�0000153�0000161�00000004646�12321735642�025212� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The cloudinit package implements a way of creating // a cloud-init configuration file. // See https://help.ubuntu.com/community/CloudInit. package cloudinit import ( "bytes" "text/template" yaml "launchpad.net/goyaml" ) // Config represents a set of cloud-init configuration options. type Config struct { attrs map[string]interface{} } // New returns a new Config with no options set. func New() *Config { return &Config{make(map[string]interface{})} } // Render returns the cloud-init configuration as a YAML file. func (cfg *Config) Render() ([]byte, error) { data, err := yaml.Marshal(cfg.attrs) if err != nil { return nil, err } return append([]byte("#cloud-config\n"), data...), nil } func (cfg *Config) set(opt string, yes bool, value interface{}) { if yes { cfg.attrs[opt] = value } else { delete(cfg.attrs, opt) } } // AptSource is an apt(8) source, comprising a source location, // with an optional Key, and optional apt_preferences(5). type AptSource struct { Source string `yaml:"source"` Key string `yaml:"key,omitempty"` Prefs *AptPreferences `yaml:"-"` } // AptPreferences is a set of apt_preferences(5) compatible // preferences for an apt source. It can be used to override the // default priority for the source. Path where the file will be // created (usually in /etc/apt/preferences.d/). type AptPreferences struct { Path string Explanation string Package string Pin string PinPriority int } // FileContents generates an apt_preferences(5) file from the fields // in prefs. func (prefs *AptPreferences) FileContents() string { const prefTemplate = ` Explanation: {{.Explanation}} Package: {{.Package}} Pin: {{.Pin}} Pin-Priority: {{.PinPriority}} ` var buf bytes.Buffer t := template.Must(template.New("").Parse(prefTemplate[1:])) err := t.Execute(&buf, prefs) if err != nil { panic(err) } return buf.String() } // command represents a shell command. type command struct { literal string args []string } // GetYAML implements yaml.Getter func (t *command) GetYAML() (tag string, value interface{}) { if t.args != nil { return "", t.args } return "", t.literal } type SSHKeyType string const ( RSAPrivate SSHKeyType = "rsa_private" RSAPublic SSHKeyType = "rsa_public" DSAPrivate SSHKeyType = "dsa_private" DSAPublic SSHKeyType = "dsa_public" ) ������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/progress_test.go�����������������������������0000644�0000153�0000161�00000001346�12321735642�026115� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cloudinit_test import ( "regexp" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cloudinit" ) type progressSuite struct{} var _ = gc.Suite(&progressSuite{}) func (*progressSuite) TestProgressCmds(c *gc.C) { initCmd := cloudinit.InitProgressCmd() re := regexp.MustCompile(`test -e /proc/self/fd/([0-9]+) \|\| exec ([0-9]+)>&2`) submatch := re.FindStringSubmatch(initCmd) c.Assert(submatch, gc.HasLen, 3) c.Assert(submatch[0], gc.Equals, initCmd) c.Assert(submatch[1], gc.Equals, submatch[2]) logCmd := cloudinit.LogProgressCmd("he'llo\"!") c.Assert(logCmd, gc.Equals, `echo 'he'"'"'llo"!' >&`+submatch[1]) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/progress.go����������������������������������0000644�0000153�0000161�00000003033�12321735642�025051� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cloudinit import ( "fmt" "launchpad.net/juju-core/utils" ) // progressFd is the file descriptor to which progress is logged. // This is necessary so that we can redirect all non-progress // related stderr away. // // Note, from the Bash manual: // "Redirections using file descriptors greater than 9 should be // used with care, as they may conflict with file descriptors the // shell uses internally." const progressFd = 9 // InitProgressCmd will return a command to initialise progress // reporting, sending messages to stderr. If LogProgressCmd is // used in a script, InitProgressCmd MUST be executed beforehand. // // The returned command is idempotent; this is important, to // allow a script to be embedded in another with stderr redirected, // in which case InitProgressCmd must precede the redirection. func InitProgressCmd() string { return fmt.Sprintf("test -e /proc/self/fd/%d || exec %d>&2", progressFd, progressFd) } // LogProgressCmd will return a command to log the specified progress // message to stderr; the resultant command should be added to the // configuration as a runcmd or bootcmd as appropriate. // // If there are any uses of LogProgressCmd in a configuration, the // configuration MUST precede the command with the result of // InitProgressCmd. func LogProgressCmd(format string, args ...interface{}) string { msg := utils.ShQuote(fmt.Sprintf(format, args...)) return fmt.Sprintf("echo %s >&%d", msg, progressFd) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/sshinit/�������������������������������������0000755�0000153�0000161�00000000000�12321736000�024325� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/sshinit/configure.go�������������������������0000644�0000153�0000161�00000014213�12321735642�026651� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshinit import ( "fmt" "io" "strings" "github.com/juju/loggo" "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" ) var logger = loggo.GetLogger("juju.cloudinit.sshinit") type ConfigureParams struct { // Host is the host to configure, in the format [user@]hostname. Host string // Client is the SSH client to connect with. // If Client is nil, ssh.DefaultClient will be used. Client ssh.Client // Config is the cloudinit config to carry out. Config *cloudinit.Config // ProgressWriter is an io.Writer to which progress will be written, // for realtime feedback. ProgressWriter io.Writer } // Configure connects to the specified host over SSH, // and executes a script that carries out cloud-config. func Configure(params ConfigureParams) error { logger.Infof("Provisioning machine agent on %s", params.Host) script, err := ConfigureScript(params.Config) if err != nil { return err } return RunConfigureScript(script, params) } // RunConfigureScript connects to the specified host over // SSH, and executes the provided script which is expected // to have been returned by ConfigureScript. func RunConfigureScript(script string, params ConfigureParams) error { logger.Debugf("Running script on %s: %s", params.Host, script) client := params.Client if client == nil { client = ssh.DefaultClient } cmd := ssh.Command(params.Host, []string{"sudo", "/bin/bash"}, nil) cmd.Stdin = strings.NewReader(script) cmd.Stderr = params.ProgressWriter return cmd.Run() } // ConfigureScript generates the bash script that applies // the specified cloud-config. func ConfigureScript(cloudcfg *cloudinit.Config) (string, error) { // TODO(axw): 2013-08-23 bug 1215777 // Carry out configuration for ssh-keys-per-user, // machine-updates-authkeys, using cloud-init config. // // We should work with smoser to get a supported // command in (or next to) cloud-init for manually // invoking cloud-config. This would address the // above comment by removing the need to generate a // script "by hand". // Bootcmds must be run before anything else, // as they may affect package installation. bootcmds, err := cmdlist(cloudcfg.BootCmds()) if err != nil { return "", err } // Add package sources and packages. pkgcmds, err := addPackageCommands(cloudcfg) if err != nil { return "", err } // Runcmds come last. runcmds, err := cmdlist(cloudcfg.RunCmds()) if err != nil { return "", err } // We prepend "set -xe". This is already in runcmds, // but added here to avoid relying on that to be // invariant. script := []string{"#!/bin/bash", "set -e"} // We must initialise progress reporting before entering // the subshell and redirecting stderr. script = append(script, cloudinit.InitProgressCmd()) stdout, stderr := cloudcfg.Output(cloudinit.OutAll) script = append(script, "(") if stderr != "" { script = append(script, "(") } script = append(script, bootcmds...) script = append(script, pkgcmds...) script = append(script, runcmds...) if stderr != "" { script = append(script, ") "+stdout) script = append(script, ") "+stderr) } else { script = append(script, ") "+stdout+" 2>&1") } return strings.Join(script, "\n"), nil } // The options specified are to prevent any kind of prompting. // * --assume-yes answers yes to any yes/no question in apt-get; // * the --force-confold option is passed to dpkg, and tells dpkg // to always keep old configuration files in the face of change. const aptget = "apt-get --option Dpkg::Options::=--force-confold --assume-yes " // addPackageCommands returns a slice of commands that, when run, // will add the required apt repositories and packages. func addPackageCommands(cfg *cloudinit.Config) ([]string, error) { var cmds []string if len(cfg.AptSources()) > 0 { // Ensure add-apt-repository is available. cmds = append(cmds, cloudinit.LogProgressCmd("Installing add-apt-repository")) cmds = append(cmds, aptget+"install python-software-properties") } for _, src := range cfg.AptSources() { // PPA keys are obtained by add-apt-repository, from launchpad. if !strings.HasPrefix(src.Source, "ppa:") { if src.Key != "" { key := utils.ShQuote(src.Key) cmd := fmt.Sprintf("printf '%%s\\n' %s | apt-key add -", key) cmds = append(cmds, cmd) } } cmds = append(cmds, cloudinit.LogProgressCmd("Adding apt repository: %s", src.Source)) cmds = append(cmds, "add-apt-repository -y "+utils.ShQuote(src.Source)) if src.Prefs != nil { path := utils.ShQuote(src.Prefs.Path) contents := utils.ShQuote(src.Prefs.FileContents()) cmds = append(cmds, "install -D -m 644 /dev/null "+path) cmds = append(cmds, `printf '%s\n' `+contents+` > `+path) } } if len(cfg.AptSources()) > 0 || cfg.AptUpdate() { cmds = append(cmds, cloudinit.LogProgressCmd("Running apt-get update")) cmds = append(cmds, aptget+"update") } if cfg.AptUpgrade() { cmds = append(cmds, cloudinit.LogProgressCmd("Running apt-get upgrade")) cmds = append(cmds, aptget+"upgrade") } for _, pkg := range cfg.Packages() { cmds = append(cmds, cloudinit.LogProgressCmd("Installing package: %s", pkg)) if !strings.Contains(pkg, "--target-release") { // We only need to shquote the package name if it does not // contain additional arguments. pkg = utils.ShQuote(pkg) } cmd := fmt.Sprintf(aptget+"install %s", pkg) cmds = append(cmds, cmd) } if len(cmds) > 0 { // setting DEBIAN_FRONTEND=noninteractive prevents debconf // from prompting, always taking default values instead. cmds = append([]string{"export DEBIAN_FRONTEND=noninteractive"}, cmds...) } return cmds, nil } func cmdlist(cmds []interface{}) ([]string, error) { result := make([]string, 0, len(cmds)) for _, cmd := range cmds { switch cmd := cmd.(type) { case []string: // Quote args, so shell meta-characters are not interpreted. for i, arg := range cmd[1:] { cmd[i] = utils.ShQuote(arg) } result = append(result, strings.Join(cmd, " ")) case string: result = append(result, cmd) default: return nil, fmt.Errorf("unexpected command type: %T", cmd) } } return result, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/sshinit/export_test.go�����������������������0000644�0000153�0000161�00000000205�12321735642�027244� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshinit const Aptget = aptget �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/sshinit/suite_test.go������������������������0000644�0000153�0000161�00000000342�12321735642�027056� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshinit_test import ( stdtesting "testing" gc "launchpad.net/gocheck" ) func Test(t *stdtesting.T) { gc.TestingT(t) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/sshinit/configure_test.go��������������������0000644�0000153�0000161�00000012105�12321735776�027716� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package sshinit_test import ( "regexp" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/cloudinit/sshinit" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" envcloudinit "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/version" ) type configureSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&configureSuite{}) type testProvider struct { environs.EnvironProvider } func (p *testProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { return map[string]string{}, nil } func init() { environs.RegisterProvider("sshinit_test", &testProvider{}) } func testConfig(c *gc.C, stateServer bool, vers version.Binary) *config.Config { testConfig, err := config.New(config.UseDefaults, coretesting.FakeConfig()) c.Assert(err, gc.IsNil) testConfig, err = testConfig.Apply(map[string]interface{}{ "type": "sshinit_test", "default-series": vers.Series, "agent-version": vers.Number.String(), }) c.Assert(err, gc.IsNil) return testConfig } func (s *configureSuite) getCloudConfig(c *gc.C, stateServer bool, vers version.Binary) *cloudinit.Config { var mcfg *envcloudinit.MachineConfig if stateServer { mcfg = environs.NewBootstrapMachineConfig("private-key") mcfg.InstanceId = "instance-id" mcfg.Jobs = []params.MachineJob{params.JobManageEnviron, params.JobHostUnits} } else { mcfg = environs.NewMachineConfig("0", "ya", nil, nil) mcfg.Jobs = []params.MachineJob{params.JobHostUnits} } mcfg.Tools = &tools.Tools{ Version: vers, URL: "file:///var/lib/juju/storage/" + envtools.StorageName(vers), } environConfig := testConfig(c, stateServer, vers) err := environs.FinishMachineConfig(mcfg, environConfig, constraints.Value{}) c.Assert(err, gc.IsNil) cloudcfg := cloudinit.New() err = envcloudinit.Configure(mcfg, cloudcfg) c.Assert(err, gc.IsNil) return cloudcfg } var allSeries = [...]string{"precise", "quantal", "raring", "saucy"} func checkIff(checker gc.Checker, condition bool) gc.Checker { if condition { return checker } return gc.Not(checker) } var aptgetRegexp = "(.|\n)*" + regexp.QuoteMeta(sshinit.Aptget) func (s *configureSuite) TestAptSources(c *gc.C) { for _, series := range allSeries { vers := version.MustParseBinary("1.16.0-" + series + "-amd64") script, err := sshinit.ConfigureScript(s.getCloudConfig(c, true, vers)) c.Assert(err, gc.IsNil) // Only Precise requires the cloud-tools pocket. // // The only source we add that requires an explicitly // specified key is cloud-tools. needsCloudTools := series == "precise" c.Assert( script, checkIff(gc.Matches, needsCloudTools), "(.|\n)*apt-key add.*(.|\n)*", ) c.Assert( script, checkIff(gc.Matches, needsCloudTools), "(.|\n)*add-apt-repository.*cloud-tools(.|\n)*", ) c.Assert( script, checkIff(gc.Matches, needsCloudTools), "(.|\n)*Pin: release n=precise-updates/cloud-tools\nPin-Priority: 400(.|\n)*", ) c.Assert( script, checkIff(gc.Matches, needsCloudTools), "(.|\n)*install -D -m 644 /dev/null '/etc/apt/preferences.d/50-cloud-tools'(.|\n)*", ) // Only Quantal requires the PPA (for mongo). needsJujuPPA := series == "quantal" c.Assert( script, checkIff(gc.Matches, needsJujuPPA), "(.|\n)*add-apt-repository.*ppa:juju/stable(.|\n)*", ) // Only install python-software-properties (add-apt-repository) // if we need to. c.Assert( script, checkIff(gc.Matches, needsCloudTools || needsJujuPPA), aptgetRegexp+"install.*python-software-properties(.|\n)*", ) } } func assertScriptMatches(c *gc.C, cfg *cloudinit.Config, pattern string, match bool) { script, err := sshinit.ConfigureScript(cfg) c.Assert(err, gc.IsNil) checker := gc.Matches if !match { checker = gc.Not(checker) } c.Assert(script, checker, pattern) } func (s *configureSuite) TestAptUpdate(c *gc.C) { // apt-get update is run if either AptUpdate is set, // or apt sources are defined. aptGetUpdatePattern := aptgetRegexp + "update(.|\n)*" cfg := cloudinit.New() c.Assert(cfg.AptUpdate(), gc.Equals, false) c.Assert(cfg.AptSources(), gc.HasLen, 0) assertScriptMatches(c, cfg, aptGetUpdatePattern, false) cfg.SetAptUpdate(true) assertScriptMatches(c, cfg, aptGetUpdatePattern, true) cfg.SetAptUpdate(false) cfg.AddAptSource("source", "key", nil) assertScriptMatches(c, cfg, aptGetUpdatePattern, true) } func (s *configureSuite) TestAptUpgrade(c *gc.C) { // apt-get upgrade is only run if AptUpgrade is set. aptGetUpgradePattern := aptgetRegexp + "upgrade(.|\n)*" cfg := cloudinit.New() cfg.SetAptUpdate(true) cfg.AddAptSource("source", "key", nil) assertScriptMatches(c, cfg, aptGetUpgradePattern, false) cfg.SetAptUpgrade(true) assertScriptMatches(c, cfg, aptGetUpgradePattern, true) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/cloudinit/cloudinit_test.go����������������������������0000644�0000153�0000161�00000017647�12321735642�026256� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package cloudinit_test import ( "fmt" "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/testing/testbase" sshtesting "launchpad.net/juju-core/utils/ssh/testing" ) // TODO integration tests, but how? type S struct { testbase.LoggingSuite } var _ = gc.Suite(S{}) func Test1(t *testing.T) { gc.TestingT(t) } var ctests = []struct { name string expect string setOption func(cfg *cloudinit.Config) }{ { "User", "user: me\n", func(cfg *cloudinit.Config) { cfg.SetUser("me") }, }, { "AptUpgrade", "apt_upgrade: true\n", func(cfg *cloudinit.Config) { cfg.SetAptUpgrade(true) }, }, { "AptUpdate", "apt_update: true\n", func(cfg *cloudinit.Config) { cfg.SetAptUpdate(true) }, }, { "AptProxy", "apt_proxy: http://foo.com\n", func(cfg *cloudinit.Config) { cfg.SetAptProxy("http://foo.com") }, }, { "AptMirror", "apt_mirror: http://foo.com\n", func(cfg *cloudinit.Config) { cfg.SetAptMirror("http://foo.com") }, }, { "AptPreserveSourcesList", "apt_mirror: true\n", func(cfg *cloudinit.Config) { cfg.SetAptPreserveSourcesList(true) }, }, { "DebconfSelections", "debconf_selections: '# Force debconf priority to critical.\n\n debconf debconf/priority select critical\n\n'\n", func(cfg *cloudinit.Config) { cfg.SetDebconfSelections("# Force debconf priority to critical.\ndebconf debconf/priority select critical\n") }, }, { "DisableEC2Metadata", "disable_ec2_metadata: true\n", func(cfg *cloudinit.Config) { cfg.SetDisableEC2Metadata(true) }, }, { "FinalMessage", "final_message: goodbye\n", func(cfg *cloudinit.Config) { cfg.SetFinalMessage("goodbye") }, }, { "Locale", "locale: en_us\n", func(cfg *cloudinit.Config) { cfg.SetLocale("en_us") }, }, { "DisableRoot", "disable_root: false\n", func(cfg *cloudinit.Config) { cfg.SetDisableRoot(false) }, }, { "SSHAuthorizedKeys", fmt.Sprintf( "ssh_authorized_keys:\n- %s\n Juju:user@host\n- %s\n Juju:another@host\n", sshtesting.ValidKeyOne.Key, sshtesting.ValidKeyTwo.Key), func(cfg *cloudinit.Config) { cfg.AddSSHAuthorizedKeys(sshtesting.ValidKeyOne.Key + " Juju:user@host") cfg.AddSSHAuthorizedKeys(sshtesting.ValidKeyTwo.Key + " another@host") }, }, { "SSHAuthorizedKeys", fmt.Sprintf( "ssh_authorized_keys:\n- %s\n Juju:sshkey\n- %s\n Juju:user@host\n- %s\n Juju:another@host\n", sshtesting.ValidKeyOne.Key, sshtesting.ValidKeyTwo.Key, sshtesting.ValidKeyThree.Key), func(cfg *cloudinit.Config) { cfg.AddSSHAuthorizedKeys("#command\n" + sshtesting.ValidKeyOne.Key) cfg.AddSSHAuthorizedKeys( sshtesting.ValidKeyTwo.Key + " user@host\n# comment\n\n" + sshtesting.ValidKeyThree.Key + " another@host") cfg.AddSSHAuthorizedKeys("") }, }, { "SSHKeys RSAPrivate", "ssh_keys:\n rsa_private: key1data\n", func(cfg *cloudinit.Config) { cfg.AddSSHKey(cloudinit.RSAPrivate, "key1data") }, }, { "SSHKeys RSAPublic", "ssh_keys:\n rsa_public: key2data\n", func(cfg *cloudinit.Config) { cfg.AddSSHKey(cloudinit.RSAPublic, "key2data") }, }, { "SSHKeys DSAPublic", "ssh_keys:\n dsa_public: key1data\n", func(cfg *cloudinit.Config) { cfg.AddSSHKey(cloudinit.DSAPublic, "key1data") }, }, { "SSHKeys DSAPrivate", "ssh_keys:\n dsa_private: key2data\n", func(cfg *cloudinit.Config) { cfg.AddSSHKey(cloudinit.DSAPrivate, "key2data") }, }, { "Output", "output:\n all:\n - '>foo'\n - '|bar'\n", func(cfg *cloudinit.Config) { cfg.SetOutput("all", ">foo", "|bar") }, }, { "Output", "output:\n all: '>foo'\n", func(cfg *cloudinit.Config) { cfg.SetOutput(cloudinit.OutAll, ">foo", "") }, }, { "AptSources", "apt_sources:\n- source: keyName\n key: someKey\n", func(cfg *cloudinit.Config) { cfg.AddAptSource("keyName", "someKey", nil) }, }, { "AptSources with preferences", `apt_sources: - source: keyName key: someKey runcmd: - install -D -m 644 /dev/null '/some/path' - 'printf ''%s\n'' ''Explanation: test Package: * Pin: release n=series Pin-Priority: 123 '' > ''/some/path''' `, func(cfg *cloudinit.Config) { prefs := &cloudinit.AptPreferences{ Path: "/some/path", Explanation: "test", Package: "*", Pin: "release n=series", PinPriority: 123, } cfg.AddAptSource("keyName", "someKey", prefs) }, }, { "Packages", "packages:\n- juju\n- ubuntu\n", func(cfg *cloudinit.Config) { cfg.AddPackage("juju") cfg.AddPackage("ubuntu") }, }, { "Packages with --target-release", "packages:\n- --target-release 'precise-updates/cloud-tools' 'mongodb-server'\n", func(cfg *cloudinit.Config) { cfg.AddPackageFromTargetRelease("mongodb-server", "precise-updates/cloud-tools") }, }, { "BootCmd", "bootcmd:\n- ls > /dev\n- - ls\n - '>with space'\n", func(cfg *cloudinit.Config) { cfg.AddBootCmd("ls > /dev") cfg.AddBootCmdArgs("ls", ">with space") }, }, { "Mounts", "mounts:\n- - x\n - \"y\"\n- - z\n - w\n", func(cfg *cloudinit.Config) { cfg.AddMount("x", "y") cfg.AddMount("z", "w") }, }, { "Attr", "arbitraryAttr: someValue\n", func(cfg *cloudinit.Config) { cfg.SetAttr("arbitraryAttr", "someValue") }, }, { "RunCmd", "runcmd:\n- ifconfig\n", func(cfg *cloudinit.Config) { cfg.AddRunCmd("ifconfig") }, }, { "AddScripts", "runcmd:\n- echo 'Hello World'\n- ifconfig\n", func(cfg *cloudinit.Config) { cfg.AddScripts( "echo 'Hello World'", "ifconfig", ) }, }, { "AddFile", addFileExpected, func(cfg *cloudinit.Config) { cfg.AddFile( "/etc/apt/apt.conf.d/99proxy", `"Acquire::http::Proxy "http://10.0.3.1:3142";`, 0644, ) }, }, } const ( header = "#cloud-config\n" addFileExpected = `runcmd: - install -D -m 644 /dev/null '/etc/apt/apt.conf.d/99proxy' - printf '%s\n' '"Acquire::http::Proxy "http://10.0.3.1:3142";' > '/etc/apt/apt.conf.d/99proxy' ` ) func (S) TestOutput(c *gc.C) { for _, t := range ctests { cfg := cloudinit.New() t.setOption(cfg) data, err := cfg.Render() c.Assert(err, gc.IsNil) c.Assert(data, gc.NotNil) c.Assert(string(data), gc.Equals, header+t.expect, gc.Commentf("test %q output differs", t.name)) } } func (S) TestRunCmds(c *gc.C) { cfg := cloudinit.New() c.Assert(cfg.RunCmds(), gc.HasLen, 0) cfg.AddScripts("a", "b") cfg.AddRunCmdArgs("c", "d") cfg.AddRunCmd("e") c.Assert(cfg.RunCmds(), gc.DeepEquals, []interface{}{ "a", "b", []string{"c", "d"}, "e", }) } func (S) TestPackages(c *gc.C) { cfg := cloudinit.New() c.Assert(cfg.Packages(), gc.HasLen, 0) cfg.AddPackage("a b c") cfg.AddPackage("d!") expectedPackages := []string{"a b c", "d!"} c.Assert(cfg.Packages(), gc.DeepEquals, expectedPackages) cfg.AddPackageFromTargetRelease("package", "series") expectedPackages = append(expectedPackages, "--target-release 'series' 'package'") c.Assert(cfg.Packages(), gc.DeepEquals, expectedPackages) } func (S) TestSetOutput(c *gc.C) { type test struct { kind cloudinit.OutputKind stdout string stderr string } tests := []test{{ cloudinit.OutAll, "a", "", }, { cloudinit.OutAll, "", "b", }, { cloudinit.OutInit, "a", "b", }, { cloudinit.OutAll, "a", "b", }, { cloudinit.OutAll, "", "", }} cfg := cloudinit.New() stdout, stderr := cfg.Output(cloudinit.OutAll) c.Assert(stdout, gc.Equals, "") c.Assert(stderr, gc.Equals, "") for i, t := range tests { c.Logf("test %d: %+v", i, t) cfg.SetOutput(t.kind, t.stdout, t.stderr) stdout, stderr = cfg.Output(t.kind) c.Assert(stdout, gc.Equals, t.stdout) c.Assert(stderr, gc.Equals, t.stderr) } } //#cloud-config //packages: //- juju //- ubuntu func ExampleConfig() { cfg := cloudinit.New() cfg.AddPackage("juju") cfg.AddPackage("ubuntu") data, err := cfg.Render() if err != nil { fmt.Printf("render error: %v", err) return } fmt.Printf("%s", data) } �����������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/�����������������������������������������������0000755�0000153�0000161�00000000000�12321735642�022354� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/stresstest/������������������������������������0000755�0000153�0000161�00000000000�12321735642�024577� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/stresstest/run.sh������������������������������0000755�0000153�0000161�00000000310�12321735642�025734� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # basic stress test set -e while true; do go get -u -v launchpad.net/juju-core/utils export GOMAXPROCS=$[ 1 + $[ RANDOM % 128 ]] go test launchpad.net/juju-core/... 2>&1 done ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/build-release-tarball/�������������������������0000755�0000153�0000161�00000000000�12321735642�026510� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������././@LongLink���������������������������������������������������������������������������������������0000000�0000000�0000000�00000000146�00000000000�011566� L����������������������������������������������������������������������������������������������������ustar �root����������������������������root�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/build-release-tarball/build-release-tarball.bash���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/build-release-tarball/build-release-tarball.bas0000644�0000153�0000161�00000002077�12321735642�033341� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env bash -e # if someone invokes this with bash set -e unset GOPATH # build release tarball from a bzr branch usage() { echo usage: $0 TAG exit 2 } test $# -eq 1 || usage TAG=$1 TMP_DIR=$(mktemp -d) mkdir $TMP_DIR/RELEASE WORK=$TMP_DIR/RELEASE echo "Getting juju-core and all its dependencies." GOPATH=$WORK go get -v -d launchpad.net/juju-core/... echo "Setting juju-core tree to $TAG." (cd "${WORK}/src/launchpad.net/juju-core/" && bzr revert -r $TAG) echo "Updating juju-core dependencies to the required versions." GOPATH=$WORK godeps -u "${WORK}/src/launchpad.net/juju-core/dependencies.tsv" # Smoke test GOPATH=$WORK go build -v launchpad.net/juju-core/... # Change the generic release to the proper juju-core version. VERSION=$(sed -n 's/^const version = "\(.*\)"/\1/p' \ $WORK/src/launchpad.net/juju-core/version/version.go) mv $WORK $TMP_DIR/juju-core_${VERSION}/ # Tar it up. TARFILE=`pwd`/juju-core_${VERSION}.tar.gz cd $TMP_DIR tar cfz $TARFILE --exclude .hg --exclude .git --exclude .bzr juju-core_${VERSION} echo "release tarball: ${TARFILE}" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/generate-docs.py�������������������������������0000755�0000153�0000161�00000003014�12321735642�025447� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # Copyright 2013 Canonical Ltd. import os import sys from optparse import OptionParser from jujuman import JujuMan GENERATORS = { 'man': JujuMan } # Insert the directory that this module is in into the python path. sys.path.insert(0, (os.path.dirname(__file__))) def main(argv): parser = OptionParser(usage="""%prog [options] OUTPUT_FORMAT Available OUTPUT_FORMAT: man man page And that is all for now.""") parser.add_option("-s", "--show-filename", action="store_true", dest="show_filename", default=False, help="print default filename on stdout") parser.add_option("-o", "--output", dest="filename", metavar="FILE", help="write output to FILE") (options, args) = parser.parse_args(argv) if len(args) != 2: parser.print_help() sys.exit(1) try: doc_generator = GENERATORS[args[1]]() except KeyError as e: sys.stderr.write("Unknown documentation generator %r\n" % e.message) sys.exit(1) if options.filename: outfilename = options.filename else: outfilename = doc_generator.get_filename(options) if outfilename == "-": outfile = sys.stdout else: outfile = open(outfilename, "w") if options.show_filename and (outfilename != "-"): sys.stdout.write(outfilename) sys.stdout.write('\n') doc_generator.write_documentation(options, outfile) if __name__ == "__main__": main(sys.argv) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/checktesting.bash������������������������������0000755�0000153�0000161�00000000761�12321735642�025675� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash exitstatus=0 for i in $(go list -f '{{.Dir}}' launchpad.net/juju-core/...) do src=$i/*_test.go # The -s flag is needed to suppress errors when # the above pattern does not match any files. if grep -s -q -l 'launchpad.net/gocheck' $src && ! egrep -l -q 'gc\.TestingT|testing\.(\w*)MgoTestPackage' $src then # There are _test.go files that use gocheck but # don't call gocheck.TestingT. echo $i uses gocheck but never calls TestingT exitstatus=1 fi done exit $exitstatus ���������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/win-installer/���������������������������������0000755�0000153�0000161�00000000000�12321736000�025131� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/win-installer/README.txt�����������������������0000644�0000153�0000161�00000016110�12321735642�026641� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Introduction to Juju This tutorial will show you how to get started with Juju, including installing, configuring and bootstrapping a new Juju environment. Before you start you will need: * An Ubuntu, Windows or OSX machine to install the client on. * An environment which can provide a new server with an Ubuntu cloud operating system image on-demand. This includes services such as Microsoft Azure, Amazon EC2, HP Cloud, or an OpenStack installation. * An SSH key-pair. Juju expects to find ssh keys called id_rsa and id_rsa.pub in a .ssh folder in your home directory. Configuring Now the Juju software is installed, it needs to be configured to use your particular cloud provider. This is done by generating and editing a file, "environments.yaml", which will live in your %LOCALAPPDATA%\Juju directory. You can generate the environments file manually, but Juju also includes a boilerplate configuration option that will flesh out most of the file for you and minimise the amount of work (and potential errors). To generate an initial config file, you simply need to run: > juju generate-config This causes the file to be written to your %LOCALAPPDATA%\Juju directory if an environments.yaml file does not already exist. It will also create the %LOCALAPPDATA%\Juju directory if that does not exist. This file will contain sample profiles for different types of cloud services, but you will need to edit the files to provide specific information for your cloud provider. Sections are created for Amazon (AWS) services, HPCloud and a generic OpenStack instance. For more specifics on what needs to be changed, see https://juju.ubuntu.com/docs/getting-started.html Testing your setup Once you have installed and configured Juju, it is probably a good idea to take it for a bit of a test drive and check that everything is working as expected. Because Juju makes it really easy to deploy services, this is actually quick and straightforward. The first thing to do is set up a bootstrap environment. This is an instance in the cloud that Juju will use to deploy and control other services with. It will be created according to the configuration you have provided, and your SSH key will automatically be uploaded so that Juju can communicate securely with the bootstrap instance. > juju bootstrap Note: If you have multiple environments configured, you can choose which one to address with a particular command by adding the -e switch followed by the environment name, E.g. "-e hpcloud". You may have to wait a few moments for this command to return, as it needs to perform various tasks and contact your cloud provider. Assuming it returns successfully (otherwise see common error messages and what to do about them - https://juju.ubuntu.com/docs/getting-started.html#errors), we can now deploy some services and explore the basic operations of Juju. To start with, we will deploy Wordpress, by running this command: > juju deploy wordpress Now juju will fetch the Wordpress charm and use it, through the bootstrap instance to request and deploy whatever resources it needs to set up this service. Wordpress needs a database though, so we will also deploy one of those: > juju deploy mysql Once again, juju will do whatever is necessary to deploy this service for you, and it may take some time for the command to return. Note: If you want to get more information on what is actually happening, or to help resolve problems, you can add the -v switch to the juju command to get verbose output. Although we have deployed Wordpress and a MySQL database, they are not linked together in any way yet. To do this we should run: > juju add-relation wordpress mysql This command uses information provided by the relevant charms to associate these services with each other in whatever way makes sense. There is much more to be said about linking services together which is covered in the juju command documentation, but for the moment, we just need to know that it will link these services together. Juju command documentation: https://juju.ubuntu.com/docs/getting-started.html#add-relation In order to make our Wordpress public, we now need to expose this service: > juju expose wordpress This service will now be configured to respond to web requests, so visitors can see it. But where exactly is it? If we run the juju status command, we will be able to see what services are running, and where they are located. > juju status The output from this command should look something like this: > juju status machines: "0": agent-state: started agent-version: 1.10.0 dns-name: ec2-50-16-167-135.compute-1.amazonaws.com instance-id: i-781bf614 series: precise "1": agent-state: started agent-version: 1.10.0 dns-name: ec2-23-22-225-54.compute-1.amazonaws.com instance-id: i-9e8927f6 series: precise "2": agent-state: started agent-version: 1.10.0 dns-name: ec2-54-224-220-210.compute-1.amazonaws.com instance-id: i-5c440436 series: precise services: mysql: charm: cs:precise/mysql-18 exposed: false relations: db: - wordpress units: mysql/0: agent-state: started agent-version: 1.10.0 machine: "1" public-address: ec2-23-22-225-54.compute-1.amazonaws.com wordpress: charm: cs:precise/wordpress-12 exposed: true relations: db: - mysql loadbalancer: - wordpress units: wordpress/0: agent-state: started agent-version: 1.10.0 machine: "2" public-address: ec2-54-224-220-210.compute-1.amazonaws.com There is quite a lot of information here. the first section, titled machines:, details all the instances which are currently running. For each you will see the version of Juju they are running, their hostname, instance id and the series or version of Ubuntu they are running. After that, the sections list the services which are currently deployed. The information here differs slightly according to the service and how it is configured. It will however, always list the charm that was used to deploy the service, whether it is exposed or not, its address and whatever relationships exist. From this status readout, we can see that wordpress is exposed and ready. If we simply copy the address into a web browser, we should be able to see it running. Congratulations, you have just deployed a service with Juju! Now you are ready to deploy whatever service you really want from the 100s available at the Juju Charm Store: http://jujucharms.com/ To remove all current deployments and clear up everything in your cloud, you can run the command: > juju destroy-environment This will remove everything, including the bootstrap node. To learn more about charms, including configuring options and managing running systems, you should continue to read the charm documentation here: https://juju.ubuntu.com/docs/charms.html��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/win-installer/LICENCE.txt����������������������0000644�0000153�0000161�00000103330�12321735642�026747� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see <http://www.gnu.org/licenses/>. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/win-installer/juju.ico�������������������������0000644�0000153�0000161�00001322626�12321735642�026631� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �( �f���€€��� �(�Ž �@@��� �(B��¶(�00��� �¨%��Þj� ��� �¨��†���� �h��.¡�(���������� �������������������������ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ!HÝHHÝ_HÝlHÝyH݆HÝ’HÝŸHݬHݹHÝÆHÝÓHÝßHÝìHÝùHÝùHÝìHÝßHÝÓHÝÆHݹHݬHÝŸHÝ’H݆HÝyHÝlHÝ_HÝHHÝ!HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ6HÝ]HÝ„HÝ«HÝÒHÝ÷HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ÷HÝÒHÝ«HÝ„HÝ]HÝ6HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ8HÝrHÝšHÝÁHÝèHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝèHÝÁHÝšHÝrHÝ8HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ1HÝsHݵHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHݵHÝsHÝ1HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ*HÝlHÝ­HÝíHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝíHÝ­HÝlHÝ*ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝeHݦHÝçHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝçHݦHÝeHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HÝZHݸHÝüHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝüHݸHÝZHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝAHÝŸHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝŸHÝAHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ'HÝ…HÝáHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝáHÝ…HÝ'ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝkHÝÉHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÉHÝkHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ#HÝ›HÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝ›HÝ#ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ+HÝ¥HÝüHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝüHÝ¥HÝ+ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ4HݯHÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHݯHÝ4ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ>HݹHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݹHÝ>ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHHÝÃHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÃHÝHÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝBHÝÍHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÍHÝBÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ¥HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ¥HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝmHÝñHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝñHÝmHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ7HÝÎHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÎHÝ7ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ™HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝ™HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝaHÝëHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝëHÝaHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ-HÝÄHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÄHÝ-ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝlHÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝlÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ­HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ­HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ5HÝÞHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÞHÝ5ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝqHÝúHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝúHÝqÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHݱHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݱHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ9HÝáHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝáHÝ9ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝuHÝûHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝûHÝuHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ­HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ­HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÀHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÀHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÐHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÐHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ$HÝÞHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÞHÝ$ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ2HÝéHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝéHÝ2ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝBHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝBÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝTHÝøHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝøHÝTÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝiHÝýHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝýHÝiÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝzHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝzÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝiHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝiÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝTHÝýHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝýHÝTÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝBHÝøHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝøHÝBÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ2HÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝ2ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ$HÝéHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝéHÝ$ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÞHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÞHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÐHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÐHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÀHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÀHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ­HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ­HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝuHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ[çÿ³ñÿÊÖ÷ÿºÉõÿ‹¥îÿ1^áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝuÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ9HÝûHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ$Tßÿ¯Áóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóöýÿr‘ëÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝûHÝ9ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝáHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿºÉõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝáHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݱHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿj‹éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéîüÿ QßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݱÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝqHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿµÆôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿU{æÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝqÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ5HÝúHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿãéûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„ŸíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝúHÝ5ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÞHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÞåúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ~›ìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÞHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ­HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ®ÀóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿPwæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ­ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝlHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿU{æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝäúÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝlÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ-HÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¥¹òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿIqåÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝ-ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÄHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿ§ïÿûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáçûÿPwæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÄHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝaHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ;fãÿx–ìÿ¦¹òÿ•¬ðÿfˆéÿPÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝaÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝëHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝëHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ™HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ™ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ7HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿêîüÿêîüÿêîüÿêîüÿêîüÿêîüÿêîüÿêîüÿêîüÿêîüÿêîüÿ’ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝ7ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÎHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÎHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝmHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝmÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝñHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ6câÿv”ëÿ—®ðÿ¨¼òÿ»ÊõÿÍØøÿßæúÿíñüÿÞåúÿÌØøÿºÉõÿ¨¼òÿ–­ðÿu“ëÿ5bâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝñHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ¥HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿ_‚èÿ µñÿáçûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáçûÿ¡¶ñÿ`„èÿPÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ¥ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝBHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿv”ëÿèíüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìðüÿ|™ìÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝBÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÍHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿiŠéÿàçúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæìûÿs’ëÿLÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÍÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOÞÿ·ÇõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÓ÷ÿ&VàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝHÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÃHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ*YàÿÏÚøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝäúÿ6câÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÃÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ>HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ:eâÿâéûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíñüÿJråÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ>ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݹHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ Qßÿßæúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîòýÿ.\áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݹÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ4HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ©¼óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅÒ÷ÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ4ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݯHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿh‰éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ… íÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݯÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ+HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ&VàÿóöýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòõýÿÌØøÿ¦¹òÿ›ìÿ`ƒèÿz˜ìÿ µñÿÇÓ÷ÿíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûüþÿ:eâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝ+ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ¥HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ„Ÿíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿ±Âôÿ=hãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ0^áÿ›±ñÿùûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤¸òÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ¥ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ#HÝüHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿæìûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíñüÿNuåÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ5bâÿÚâúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøùþÿ(XàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝüHÝ#ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ›HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿg‰éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãéûÿ5bâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÉÕ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿФîÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ›ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÆÓ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýÿÿJråÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ+ZàÿíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêîüÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝkHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMÞÿùúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ_‚èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:eâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝkÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÉHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIqåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøùþÿ&VàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÖßùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÉÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ'HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ›ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ºòÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿs’ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨¼òÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ'ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ…HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ´ÅôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTzæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ"SßÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàçúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ…ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝáHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÚâúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿJÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝáHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝAHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿåëûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ©¼óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ&VàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝAÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝŸHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿöøþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÄôÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6câÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝŸÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥¹òÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿqêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGoäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝZHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ*Yàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–­ðÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿc†èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿV|çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝZÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݸHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆ¢îÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿUzæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݸÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝüHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝüHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝeHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝeÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݦHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݦÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝçHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝçÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ*HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ*ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝlHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝlÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ­HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ­ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝíHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝíHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ1HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ1ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝsHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝsÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݵHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݵÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ8HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ8ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝrHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝrÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝšHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝšÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÁHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÁÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝèHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝèÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿ�ÿÿÿ�HÝ6HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ6ÿÿÿ�ÿÿÿ�HÝ]HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ]ÿÿÿ�ÿÿÿ�HÝ„HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ„ÿÿÿ�ÿÿÿ�HÝ«HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ«ÿÿÿ�ÿÿÿ�HÝÒHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÒÿÿÿ�HÝHÝ÷HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ÷HÝHÝ!HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ!HÝHHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝHHÝ_HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ_HÝlHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝlHÝyHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ(XàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿQßÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ@jãÿ"SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝyH݆HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿH݆HÝ’HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ#Sßÿ#Sßÿ#Sßÿ#Sßÿ#Sßÿ#Sßÿ#Sßÿ#Sßÿ#Sßÿ#Sßÿ#SßÿMÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ&VàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ’HÝŸHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ=hãÿ»Êõÿíñüÿÿÿÿÿðôýÿ¿ÍöÿEnäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝŸHݬHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿqêÿùúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûüþÿ}šìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݬHݹHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿBläÿúûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿNuåÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݹHÝÆHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÊÕ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙâúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÆHÝÓHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿùûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ&VàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÓHÝßHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ;fãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿPwæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝßHÝìHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ!Rßÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2`áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝìHÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÜäúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìðüÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥¹òÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ6câÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿfˆéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿu“ëÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝìHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿùûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÃôÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿDmäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|™ìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ§ºòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´ÅôÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝìHÝßHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿèíüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÎöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿRxæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿk‹êÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿr‘ëÿòõýÿÿÿÿÿÿÿÿÿÿÿÿÿöøþÿ|™ìÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝßHÝÓHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÖßùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÚøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ`„èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿY~çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿJÝÿ=hãÿ`„èÿ?jãÿLÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÓHÝÆHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÄÑöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòõýÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ… íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHpäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÆHݹHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ³Ãôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:eâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ·Çõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ5bâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿФîÿФîÿФîÿФîÿФîÿФîÿФîÿФîÿФîÿФîÿФîÿ9eâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݹHݬHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ”¬ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿêîüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúûþÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݬHÝŸHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ_‚èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÊõÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLtåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿË×øÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝŸHÝ’HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ)Yàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿ4aâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ±Âôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”¬ðÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ%Ußÿ=hãÿPwæÿc†èÿw•ëÿФîÿ’ªïÿ€œíÿlêÿY~çÿFoäÿ2`áÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ’H݆HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿàçúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢·òÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ8dâÿûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿU{æÿ–­ðÿÖßùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïóýÿ³Ãôÿt“ëÿ5bâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿH݆HÝyHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ§»òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿV|çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿÑÛøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýÿÿ&VàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ=hãÿ´Åôÿùúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáçûÿ{˜ìÿMÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝyHÝlHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMtåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéîüÿ7câÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ¢·òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²ÃôÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ2`áÿª½óÿýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäêûÿpêÿKÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝlHÝ_HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÓÝùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîòýÿNuåÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿPÞÿ·ÇõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHpäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ¦îÿùûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝäúÿDmäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ_HÝHHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿmŽêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿ«¾óÿ6câÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿw•ëÿçìüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÖ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMÞÿ°Áôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöøþÿ\€çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝHHÝ!HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿêîüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿâéûÿ¼Ëõÿ•¬ðÿnŽêÿJråÿ_‚èÿ… íÿ«¾óÿÒÜøÿ÷ùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`ƒèÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ%UßÿÇÓ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿx–ìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ!HÝHÝ÷HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ[çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÃôÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ}šìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ÷HÝÿÿÿ�HÝÓHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ›±ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäêûÿ$TßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿŸ´ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúûþÿDmäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÓÿÿÿ�ÿÿÿ�HݬHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÖßùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýÿÿLtåÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ^èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞåúÿQßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݬÿÿÿ�ÿÿÿ�HÝ…HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ5bâÿáçûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýÿÿr‘ëÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ+Zàÿîòýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ÷þÿÎÙøÿºÉõÿßæúÿýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ºòÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ…ÿÿÿ�ÿÿÿ�HÝ^HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ(XàÿÌØøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôöýÿY~çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ•¬ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèíüÿˆ¢îÿ[çÿ2`áÿIÝÿHÝÿHÝÿHÝÿOÞÿCmäÿk‹êÿ§»òÿûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿ8dâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ^ÿÿÿ�ÿÿÿ�HÝ7HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿ´ÅôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèíüÿBläÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿQßÿñôýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûüþÿ… íÿOÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ5bâÿºÉõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡¶ñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ7ÿÿÿ�ÿÿÿ�HÝHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿoêÿåëûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúûþÿ³ñÿ)YàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿx–ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòõýÿUzæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ’ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøùþÿ'WàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿ�ÿÿÿ�ÿÿÿ�HÝéHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿ~›ìÿíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýÿÿ§»òÿ1_áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿÝäúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿPwæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ™¯ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆ¢îÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝéÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÂHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ!Rßÿr‘ëÿ²ÃôÿïóýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùûþÿÄÑöÿ… íÿ8dâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ9eâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦îÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMÞÿÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÙøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÂÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ›HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿFoäÿ… íÿª½óÿ½ËõÿÏÚøÿáçûÿóöýÿÿÿÿÿ÷ùþÿäêûÿÓÝùÿÀÎöÿ¯Áóÿ“«ïÿV|çÿOÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿnŽêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéîüÿLÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿTzæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûüþÿPÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ›ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝsHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿJÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¤¸òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ¥îÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿÛãúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQwæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝsÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ:HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÙâúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.\áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ~›ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‡¢îÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ:ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàçúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿEnäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝóHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݶHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ.\áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­¿óÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿùúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÖ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݶÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝtHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ>iãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ}šìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÎÙøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜäúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝtÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ2HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNuåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿh‰éÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¹ÈõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíñüÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ2ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝîHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ_‚èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿY~çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿª½óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝîHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ®HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlŒêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKsåÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿœ²ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ®ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝlHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝlÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ*HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ*ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝèHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝèÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݧHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݧÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝeHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝeÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝüHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝüHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݸHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݸÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝZHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝZÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝŸHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝŸÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝAHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝAÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝáHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝáHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ…HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ…ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ'HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ'ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÉHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÉÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝkHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝkÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ›HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ›ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ#HÝüHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝüHÝ#ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ¥HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ¥ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ+HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝ+ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݯHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݯÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ4HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ4ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݹHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݹÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ>HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ>ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÃHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÃÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝHÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÍHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÍÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝBHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿFoäÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ-[áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿZçÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿ™¯ðÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝBÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ¥HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ¥ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝñHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝñHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝmHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNuåÿ¯Áóÿ¯Áóÿ¯Áóÿ¯Áóÿ¯Áóÿ¯Áóÿ¯Áóÿ¯Áóÿ¯Áóÿ¯Áóÿ¯Áóÿ1^áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝmÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÎHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÎHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ7HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝ7ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ™HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ™ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝëHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝëHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝaHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝaÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÄHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÄHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ-HÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝ-ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝlHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_‚èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿmŽêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝlÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ­HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMtåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ{˜ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ>iãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ­ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÞHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ?jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|™ìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‹¥îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ1^áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÞHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ5HÝúHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ0^áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ¥îÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ›±ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!RßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝúHÝ5ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝqHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿQßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦¹òÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿµÆôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûüþÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝqÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݱHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿúûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖßùÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿæìûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìðüÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݱÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝáHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿçìüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿQßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ,[àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØáùÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝáHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ9HÝûHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ·ÇõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿY~çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿiŠéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ºòÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝûHÝ9ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝuHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ‚íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸ÈõÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÊÕ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿoêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝuÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ­HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿKsåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿ1^áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿCmäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8dâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ­HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÀHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿùûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½ËõÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿKÞÿÍØøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìðüÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÀHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÐHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¸Èõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ}šìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¦îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡¶ñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÐHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÞHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿQwæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿ€œíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ¦îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿ9eâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÞHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ$HÝéHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ®Àóÿ1_áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ9eâÿ¸Èõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ»ÊõÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝéHÝ$ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ2HÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿmŽêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùûþÿ§»òÿx–ëÿQwæÿ+ZàÿHÝÿHÝÿIÝÿ0^áÿV|çÿ}šìÿ²ÃôÿýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQwæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝ2ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝBHÝøHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿÔÝùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðôýÿÖßùÿôöýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼ËõÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝøHÝBÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝTHÝýHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ:eâÿöøþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèíüÿ'WàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝýHÝTÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝiHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿr‘ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿQwæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝiÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝzHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ­¿óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆ¢îÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝzÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝiHÝýHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿJÝÿœ²ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿ|™ìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝýHÝiÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝTHÝøHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ}šìÿýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ùþÿ_‚èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝøHÝTÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝBHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ]èÿæìûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×àùÿFoäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝBÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ2HÝéHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿ›ìÿíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝäúÿg‰éÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝéHÝ2ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ$HÝÞHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ!RßÿФîÿÝäúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿÌØøÿmŽêÿJÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÞHÝ$ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÐHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ2`áÿs’ëÿ´Åôÿñôýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãéûÿ£·òÿb…èÿ$TßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÐHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÀHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ#Sßÿ5bâÿGoäÿY~çÿlŒêÿv”ëÿfˆéÿTzæÿBläÿ0^áÿPÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÀHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ­HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ­HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝuHÝûHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝûHÝuHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ9HÝáHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝáHÝ9ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHݱHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݱHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝqHÝúHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝúHÝqÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ5HÝÞHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÞHÝ5ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ­HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ­HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝlHÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝlÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ-HÝÄHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÄHÝ-ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝaHÝëHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝëHÝaHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ™HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝ™HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ7HÝÎHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÎHÝ7ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝmHÝñHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝñHÝmHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ¥HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ¥HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝBHÝÍHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÍHÝBÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHHÝÃHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÃHÝHÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ>HݹHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݹHÝ>ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ4HݯHÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHݯHÝ4ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ+HÝ¥HÝüHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝüHÝ¥HÝ+ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ#HÝ›HÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝ›HÝ#ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝkHÝÉHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÉHÝkHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ'HÝ…HÝáHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝáHÝ…HÝ'ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝAHÝŸHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝŸHÝAHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HÝZHݸHÝüHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝüHݸHÝZHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝeHݦHÝçHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝçHݦHÝeHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ*HÝlHÝ­HÝíHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝíHÝ­HÝlHÝ*ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ1HÝsHݵHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHݵHÝsHÝ1HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ8HÝrHÝšHÝÁHÝèHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝèHÝÁHÝšHÝrHÝ8HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ6HÝ]HÝ„HÝ«HÝÒHÝ÷HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ÷HÝÒHÝ«HÝ„HÝ]HÝ6HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ!HÝHHÝ_HÝlHÝyH݆HÝ’HÝŸHݬHݹHÝÆHÝÓHÝßHÝìHÝùHÝùHÝìHÝßHÝÓHÝÆHݹHݬHÝŸHÝ’H݆HÝyHÝlHÝ_HÝHHÝ!HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿÿÿÿÿÿÿÿÿÿÿÿü��?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ��������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà��������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ��������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø��������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà��������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€��������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü����������������?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿð����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿà����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿü������������������?ÿÿÿÿÿÿÿÿÿÿÿÿø������������������ÿÿÿÿÿÿÿÿÿÿÿÿð������������������ÿÿÿÿÿÿÿÿÿÿÿÿÀ������������������ÿÿÿÿÿÿÿÿÿÿÿÿ€������������������ÿÿÿÿÿÿÿÿÿÿÿÿ��������������������ÿÿÿÿÿÿÿÿÿÿÿü��������������������?ÿÿÿÿÿÿÿÿÿÿø��������������������ÿÿÿÿÿÿÿÿÿÿð��������������������ÿÿÿÿÿÿÿÿÿÿà��������������������ÿÿÿÿÿÿÿÿÿÿÀ��������������������ÿÿÿÿÿÿÿÿÿÿ€��������������������ÿÿÿÿÿÿÿÿÿÿ����������������������ÿÿÿÿÿÿÿÿÿþ����������������������ÿÿÿÿÿÿÿÿü����������������������?ÿÿÿÿÿÿÿÿø����������������������ÿÿÿÿÿÿÿÿð����������������������ÿÿÿÿÿÿÿÿà����������������������ÿÿÿÿÿÿÿÿÀ����������������������ÿÿÿÿÿÿÿÿ€����������������������ÿÿÿÿÿÿÿÿ������������������������ÿÿÿÿÿÿÿþ������������������������ÿÿÿÿÿÿü������������������������?ÿÿÿÿÿÿø������������������������ÿÿÿÿÿÿø������������������������ÿÿÿÿÿÿð������������������������ÿÿÿÿÿÿà������������������������ÿÿÿÿÿÿÀ������������������������ÿÿÿÿÿÿÀ������������������������ÿÿÿÿÿÿ€������������������������ÿÿÿÿÿÿ��������������������������ÿÿÿÿÿþ��������������������������ÿÿÿÿþ��������������������������ÿÿÿÿü��������������������������?ÿÿÿÿø��������������������������ÿÿÿÿø��������������������������ÿÿÿÿð��������������������������ÿÿÿÿà��������������������������ÿÿÿÿà��������������������������ÿÿÿÿÀ��������������������������ÿÿÿÿÀ��������������������������ÿÿÿÿ€��������������������������ÿÿÿÿ����������������������������ÿÿÿÿ����������������������������ÿÿÿþ����������������������������ÿÿþ����������������������������ÿÿü����������������������������?ÿÿü����������������������������?ÿÿø����������������������������ÿÿø����������������������������ÿÿð����������������������������ÿÿð����������������������������ÿÿà����������������������������ÿÿà����������������������������ÿÿÀ����������������������������ÿÿÀ����������������������������ÿÿÀ����������������������������ÿÿ€����������������������������ÿÿ€����������������������������ÿÿ������������������������������ÿÿ������������������������������ÿÿ������������������������������ÿþ������������������������������þ������������������������������þ������������������������������ü������������������������������?ü������������������������������?ü������������������������������?ø������������������������������ø������������������������������ø������������������������������ø������������������������������ð������������������������������ð������������������������������ð������������������������������ð������������������������������à������������������������������à������������������������������à������������������������������à������������������������������À������������������������������À������������������������������À������������������������������À������������������������������À������������������������������À������������������������������€������������������������������€������������������������������€������������������������������€������������������������������€������������������������������€������������������������������€������������������������������€������������������������������€����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������€������������������������������€������������������������������€������������������������������€������������������������������€������������������������������€������������������������������€������������������������������€������������������������������€������������������������������À������������������������������À������������������������������À������������������������������À������������������������������À������������������������������À������������������������������à������������������������������à������������������������������à������������������������������à������������������������������ð������������������������������ð������������������������������ð������������������������������ð������������������������������ø������������������������������ø������������������������������ø������������������������������ø������������������������������ü������������������������������?ü������������������������������?ü������������������������������?þ������������������������������þ������������������������������þ������������������������������ÿ������������������������������ÿÿ������������������������������ÿÿ������������������������������ÿÿ€����������������������������ÿÿ€����������������������������ÿÿÀ����������������������������ÿÿÀ����������������������������ÿÿÀ����������������������������ÿÿà����������������������������ÿÿà����������������������������ÿÿð����������������������������ÿÿð����������������������������ÿÿø����������������������������ÿÿø����������������������������ÿÿü����������������������������?ÿÿü����������������������������?ÿÿþ����������������������������ÿÿþ����������������������������ÿÿÿ����������������������������ÿÿÿÿ����������������������������ÿÿÿÿ€��������������������������ÿÿÿÿÀ��������������������������ÿÿÿÿÀ��������������������������ÿÿÿÿà��������������������������ÿÿÿÿà��������������������������ÿÿÿÿð��������������������������ÿÿÿÿø��������������������������ÿÿÿÿø��������������������������ÿÿÿÿü��������������������������?ÿÿÿÿþ��������������������������ÿÿÿÿþ��������������������������ÿÿÿÿÿ��������������������������ÿÿÿÿÿÿ€������������������������ÿÿÿÿÿÿÀ������������������������ÿÿÿÿÿÿÀ������������������������ÿÿÿÿÿÿà������������������������ÿÿÿÿÿÿð������������������������ÿÿÿÿÿÿø������������������������ÿÿÿÿÿÿø������������������������ÿÿÿÿÿÿü������������������������?ÿÿÿÿÿÿþ������������������������ÿÿÿÿÿÿÿ������������������������ÿÿÿÿÿÿÿÿ€����������������������ÿÿÿÿÿÿÿÿÀ����������������������ÿÿÿÿÿÿÿÿà����������������������ÿÿÿÿÿÿÿÿð����������������������ÿÿÿÿÿÿÿÿø����������������������ÿÿÿÿÿÿÿÿü����������������������?ÿÿÿÿÿÿÿÿþ����������������������ÿÿÿÿÿÿÿÿÿ����������������������ÿÿÿÿÿÿÿÿÿÿ€��������������������ÿÿÿÿÿÿÿÿÿÿÀ��������������������ÿÿÿÿÿÿÿÿÿÿà��������������������ÿÿÿÿÿÿÿÿÿÿð��������������������ÿÿÿÿÿÿÿÿÿÿø��������������������ÿÿÿÿÿÿÿÿÿÿü��������������������?ÿÿÿÿÿÿÿÿÿÿÿ��������������������ÿÿÿÿÿÿÿÿÿÿÿÿ€������������������ÿÿÿÿÿÿÿÿÿÿÿÿÀ������������������ÿÿÿÿÿÿÿÿÿÿÿÿð������������������ÿÿÿÿÿÿÿÿÿÿÿÿø������������������ÿÿÿÿÿÿÿÿÿÿÿÿü������������������?ÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿà����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿð����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿü����������������?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ����������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€��������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà��������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø��������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ��������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà��������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ��������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø����ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü��?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(���€������� �������������������������ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ4HÝ[HÝ‚HÝ¥HݵHÝÃHÝÐHÝÞHÝëHÝùHÝùHÝëHÝÞHÝÐHÝÃHݵHÝ¥HÝ‚HÝ[HÝ4HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HÝGH݉HÝ¿HÝæHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝæHÝ¿H݉HÝGHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝBHÝ„HÝÅHÝúHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝúHÝÅH݃HÝBHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ8HÝ•HÝíHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝíHÝ•HÝ8ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ|HÝÙHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÙHÝ|HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ4HÝ®HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝ®HÝ4ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ=HݸHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݸHÝ=ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝCHÝÂHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÂHÝCÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ«HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ«HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝsHÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝóHÝsHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ<HÝÓHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÓHÝ<ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝH݆HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþH݆HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÃHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÃHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝJHÝìHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝìHÝJÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝH݈HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþH݈HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHݧHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݧHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HݺHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݺHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝËHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝËHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÙHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÙHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÙHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÙHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HÝËHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝËHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHݺHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݺHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHݧHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݧHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�H݈HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿJråÿ:eâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿH݈ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝJHÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ4aâÿÖßùÿÿÿÿÿÿÿÿÿÂÐöÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝJÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝìHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ™¯ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝìHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÃHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿàçúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÄôÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÃHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�H݆HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ“«ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿg‰éÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿH݆ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ<HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ0^áÿÇÓ÷ÿÿÿÿÿüýÿÿ³ÄôÿLÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝ<ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÓHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ=hãÿ.\áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÓHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝsHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿƒžíÿƒžíÿƒžíÿƒžíÿƒžíÿj‹éÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝsÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝóHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ«HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ*Yàÿlêÿ¬¾óÿÐÚøÿâèûÿòõýÿëïüÿØáùÿÂÐöÿŒ¥îÿMtåÿJÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ«ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝCHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ'Wàÿ›±ðÿúûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛãúÿa„èÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝCÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÂHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿJråÿíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ÃôÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÂÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ=HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿùúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊÕ÷ÿJÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ=ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݸHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿJÝÿ×àùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿs’ëÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݸÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ4HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿu“ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöøþÿ–­ðÿg‰éÿ>iãÿOvæÿx–ëÿ¾Íöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìðüÿ"SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ4ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ®HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈÔ÷ÿ3`âÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿiŠéÿòõýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”¬ðÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ®ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿu“ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóöýÿ'WàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿz˜ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøùþÿJÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ|HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ™¯ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ… íÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿàçúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3`âÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ|ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÙHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ»Êõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,[àÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿŒ¥îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿV|çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÙÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ8HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÜäúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøùþÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿmŽêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿz˜ìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ8ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ•HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿJÝÿúûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ–ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝíHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÎöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ4aâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝíHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝBHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝBÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ„HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ„ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÅHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÅÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HÝúHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝúHÝ ÿÿÿ�ÿÿÿ�HÝGHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝGÿÿÿ�ÿÿÿ�H݉HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿH݉ÿÿÿ�ÿÿÿ�HÝ¿HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ¿ÿÿÿ�ÿÿÿ�HÝæHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝæÿÿÿ�HÝHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝHÝ4HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ4HÝ[HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ[HÝ‚HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ‚HÝ¥HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ¥HݵHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿ µñÿ µñÿ µñÿ µñÿ µñÿx–ìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ&Vàÿ µñÿ µñÿ µñÿ µñÿ µñÿoêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݵHÝÃHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿNÞÿNÞÿNÞÿNÞÿLÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÃHÝÐHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ(Xàÿ’ªïÿíñüÿÈÔ÷ÿ`„èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÐHÝÞHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿœ²ñÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿ1^áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÞHÝëHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿóöýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦îÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝëHÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÙâúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿk‹êÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙâúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ/]áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÉõÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿr‘ëÿÿÿÿÿÿÿÿÿÿÿÿÿèíüÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝëHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿáçûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùúþÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMtåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿž´ñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ;fãÿ–­ðÿoêÿOÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝëHÝÞHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¾Íöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ/]áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿlŒêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|™ìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿOvæÿOvæÿOvæÿOvæÿOvæÿ;fãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÞHÝÐHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿœ²ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ™¯ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZçÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÐHÝÃHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿz˜ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÛøÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ.\áÿ÷ùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8dâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿEnäÿ†¡îÿ¦¹òÿ¸ÈõÿÇÓ÷ÿ¼Ëõÿ©¼óÿ‘ªïÿUzæÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÃHݵHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿCmäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿoêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ®ÀóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçìüÿLÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMÞÿ|™ìÿêîüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ùþÿœ²ñÿ*YàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݵHÝ¥HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ®Àóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿž´ñÿQßÿHÝÿHÝÿHÝÿHÝÿ6câÿÉÕ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿiŠéÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ<gãÿàçúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöøþÿiŠéÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ¥HÝ‚HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ0^áÿ÷ùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøùþÿÔÝùÿ®Àóÿ¹ÈõÿßæúÿýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿË×øÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHpäÿñôýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ‚HÝ[HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¦îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿFoäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿJÝÿ×àùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýÿÿ<gãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ[HÝ4HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿµÆôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýÿÿw•ëÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿu“ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýÿÿ¹Èõÿ‘ªïÿoêÿ… íÿ«¾óÿëïüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÍöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ4HÝHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿާïÿùúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåëûÿY~çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛãúÿFoäÿHÝÿHÝÿHÝÿHÝÿHÝÿ&Vàÿ®ÀóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿY~çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿ�HÝæHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ/]áÿ³ñÿçìüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿÐÚøÿy—ìÿMÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ„ íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñôýÿ&VàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ³ÄôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÙøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝæÿÿÿ�ÿÿÿ�HÝ¿HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ?jãÿg‰éÿy—ìÿ‹¥îÿ„ íÿr‘ëÿ^èÿ'WàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¯Áóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚íÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ5bâÿúûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøùþÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ¿ÿÿÿ�ÿÿÿ�H݉HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÑÛøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüýÿÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ³Äôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ2`áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿH݉ÿÿÿ�ÿÿÿ�HÝGHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿóöýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâèûÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ©ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿU{æÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝGÿÿÿ�ÿÿÿ�HÝ HÝúHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ)YàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÐöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿr‘ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿy—ìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝúHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÅHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ?jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§ºòÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿU{æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©ïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÅÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ„HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ„ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝBHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝBÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝíHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝíHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ•HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ–ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ8HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ8ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÙHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÙÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ|HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ|ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ®HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ®ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ4HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ4ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HݸHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݸÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ=HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ=ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝÂHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿOvæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ªïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÂÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝCHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ6câÿÌØøÿÌØøÿÌØøÿÌØøÿÌØøÿíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿBläÿÌØøÿÌØøÿÌØøÿÌØøÿÌØøÿv”ëÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝCÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ«HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ«ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ8dâÿÔÝùÿÔÝùÿÔÝùÿÔÝùÿÔÝùÿ… íÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝóHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝsHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝsÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÓHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÓHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ<HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÁóÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ µñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝ<ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�H݆HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1^áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHpäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸ´ñÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿH݆ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÃHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMÞÿýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÝùÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿe‡éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†¡îÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÃHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝìHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿâéûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòõýÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ„Ÿíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿb…èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝìHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝJHÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÀÎöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ+ZàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¨¼òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?jãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝJÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�H݈HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿŸ´ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜¯ðÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1^áÿùúþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿOÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿH݈ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHݧHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿs’ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúûþÿ6câÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ®ÀóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݧHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHݺHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMÞÿâèûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëïüÿ_‚èÿHÝÿHÝÿHÝÿHÝÿHÝÿ'Wàÿ³Ãôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿd†éÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݺHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HÝËHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿb…èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøÿŸ´ñÿx–ìÿ¦îÿµÆôÿîòýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÕ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝËHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÙHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÇÓ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿDmäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÙHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÙHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ:eâÿçìüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿФîÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÙHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝËHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ.\áÿÍØøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ÷þÿmŽêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝËHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HݺHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿh‰éÿÛãúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõ÷þÿœ²ñÿ)YàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݺHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHݧHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ5bâÿu“ëÿ–­ðÿ¨¼òÿ¸Èõÿ®Àóÿœ²ñÿ… íÿMtåÿLÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݧHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝH݈HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþH݈HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝJHÝìHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝìHÝJÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝÃHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÃHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝH݆HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþH݆HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ<HÝÓHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÓHÝ<ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝsHÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝóHÝsHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ«HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ«HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝCHÝÂHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÂHÝCÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ=HݸHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݸHÝ=ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ4HÝ®HÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝ®HÝ4ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ|HÝÙHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÙHÝ|HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ8HÝ•HÝíHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝíHÝ•HÝ8ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝBHÝ„HÝÅHÝúHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝúHÝÅH݃HÝBHÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ HÝGH݉HÝ¿HÝæHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝæHÝ¿H݉HÝGHÝ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝHÝ4HÝ[HÝ‚HÝ¥HݵHÝÃHÝÐHÝÞHÝëHÝùHÝùHÝëHÝÞHÝÐHÝÃHݵHÝ¥HÝ‚HÝ[HÝ4HÝÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿÿÿÿÿ��ÿÿÿÿÿÿÿÿÿÿÿÿÿÀ��ÿÿÿÿÿÿÿÿÿÿÿü����?ÿÿÿÿÿÿÿÿÿÿà����ÿÿÿÿÿÿÿÿÿÿ€����ÿÿÿÿÿÿÿÿÿü������?ÿÿÿÿÿÿÿÿð������ÿÿÿÿÿÿÿÿÀ������ÿÿÿÿÿÿÿÿ��������ÿÿÿÿÿÿÿþ��������ÿÿÿÿÿÿø��������ÿÿÿÿÿÿà��������ÿÿÿÿÿÿÀ��������ÿÿÿÿÿÿ€��������ÿÿÿÿÿþ����������ÿÿÿÿü����������?ÿÿÿÿø����������ÿÿÿÿð����������ÿÿÿÿà����������ÿÿÿÿÀ����������ÿÿÿÿ€����������ÿÿÿÿ������������ÿÿÿþ������������ÿÿü������������?ÿÿü������������?ÿÿø������������ÿÿð������������ÿÿà������������ÿÿà������������ÿÿÀ������������ÿÿÀ������������ÿÿ€������������ÿÿ��������������ÿÿ��������������ÿþ��������������þ��������������ü��������������?ü��������������?ø��������������ø��������������ø��������������ð��������������ð��������������à��������������à��������������à��������������À��������������À��������������À��������������À��������������€��������������€��������������€��������������€��������������€��������������€������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������€��������������€��������������€��������������€��������������€��������������€��������������À��������������À��������������À��������������À��������������à��������������à��������������à��������������ð��������������ð��������������ø��������������ø��������������ø��������������ü��������������?ü��������������?þ��������������þ��������������ÿ��������������ÿÿ��������������ÿÿ€������������ÿÿÀ������������ÿÿÀ������������ÿÿà������������ÿÿà������������ÿÿð������������ÿÿø������������ÿÿü������������?ÿÿü������������?ÿÿþ������������ÿÿÿ������������ÿÿÿÿ€����������ÿÿÿÿÀ����������ÿÿÿÿà����������ÿÿÿÿð����������ÿÿÿÿø����������ÿÿÿÿü����������?ÿÿÿÿþ����������ÿÿÿÿÿ€��������ÿÿÿÿÿÿÀ��������ÿÿÿÿÿÿà��������ÿÿÿÿÿÿø��������ÿÿÿÿÿÿþ��������ÿÿÿÿÿÿÿ��������ÿÿÿÿÿÿÿÿÀ������ÿÿÿÿÿÿÿÿð������ÿÿÿÿÿÿÿÿü������?ÿÿÿÿÿÿÿÿÿ€����ÿÿÿÿÿÿÿÿÿÿà����ÿÿÿÿÿÿÿÿÿÿü����?ÿÿÿÿÿÿÿÿÿÿÿÀ��ÿÿÿÿÿÿÿÿÿÿÿÿÿ��ÿÿÿÿÿÿÿ(���@���€���� �������������������������ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�@Õ HÞNIÝpHÞŠHݤHݾHÝØHÝòHÝòHÝØHݾHݤHÞŠIÝpHÞN@Õ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�LÙGÝhGݶHÝøHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝøGݶGÝhLÙÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�Mæ IÞtHÝËHÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝËIÞtMæ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿGÞdHÜåHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÜåGÞd�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�FÝLHÝÕHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÕFÝLÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JÞHÜÀHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÜÀJÞÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ<HÝêHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝêHÝ<ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÝbHÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùIÝbÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿHÝŽHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝŽ�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿHÞ°HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÞ°�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝŽHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝŽÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÝbHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ.\áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝbÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ<HÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ«¾óÿÿÿÿÿ”¬ðÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝ<ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JÞHÝêHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿßæúÿÿÿÿÿÉÕ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝêJÞÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÜÀHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIqåÿœ²ñÿ=hãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÜÀÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�FÝLHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¾Ìöÿ¾Ìöÿ­¿óÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿFÝLÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿHÝÕHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ,[àÿš°ðÿÆÓ÷ÿïóýÿåëûÿ¼Ëõÿ… íÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÕ�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�GÞdHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ]èÿïóýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚâúÿ=hãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÞdÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�Mæ HÜåHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ+Zàÿôöýÿÿÿÿÿÿÿÿÿâéûÿ¨¼òÿ³ÃôÿðôýÿÿÿÿÿÿÿÿÿÕÞùÿJÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÜåMæ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÞtHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ µñÿÿÿÿÿÿÿÿÿ»ÊõÿNÞÿHÝÿHÝÿ)YàÿÚâúÿÿÿÿÿÿÿÿÿmŽêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÞtÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝËHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÕÞùÿÿÿÿÿÿÿÿÿ9eâÿHÝÿHÝÿHÝÿHÝÿe‡éÿÿÿÿÿÿÿÿÿ¤¸òÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝËÿÿÿ�ÿÿÿ�ÿÿÿ�LÙHÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿöøþÿÿÿÿÿ÷ùþÿIÝÿHÝÿHÝÿHÝÿHÝÿ8dâÿÿÿÿÿÿÿÿÿÊÕ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþLÙÿÿÿ�ÿÿÿ�GÝhHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿÿÿÿÿâéûÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÝhÿÿÿ�ÿÿÿ�GݶHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿÿÿÿÿâéûÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGݶÿÿÿ�@Õ HÝøHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿÿÿÿÿâéûÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝø@Õ HÞNHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿÿÿÿÿâéûÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÞNIÝpHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿÿÿÿÿâéûÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝpHÞŠHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿÿÿÿÿâéûÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÞŠHݤHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿÿÿÿÿâéûÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݤHݾHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿV|çÿV|çÿNuåÿHÝÿHÝÿHÝÿHÝÿHÝÿKÞÿV|çÿV|çÿKsåÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݾHÝØHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿÿÿÿÿâéûÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ–­ðÿíñüÿj‹éÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝØHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿÿÿÿÿêîüÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿÿÿÿÿâéûÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿòõýÿÿÿÿÿ»ÊõÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿøùþÿÿÿÿÿ÷ùþÿIÝÿHÝÿHÝÿHÝÿHÝÿ)YàÿÿÿÿÿÿÿÿÿÙâúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿpêÿÁÏöÿNuåÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝØHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ×àùÿÿÿÿÿÿÿÿÿ.\áÿHÝÿHÝÿHÝÿHÝÿKsåÿÿÿÿÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿHÝÿOÞÿ§»òÿ§»òÿ©ïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝØHݾHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ°Áôÿÿÿÿÿÿÿÿÿ—®ðÿHÝÿHÝÿHÝÿHÝÿ³ÄôÿÿÿÿÿÿÿÿÿŒ¥îÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ&Vàÿ†¡îÿ¯ÁóÿÕÞùÿÈÔ÷ÿ µñÿiŠéÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݾHݤHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿAkäÿýþÿÿÿÿÿÿÿÿÿÿ­¿óÿh‰éÿmŽêÿ½Ëõÿÿÿÿÿÿÿÿÿñôýÿ(XàÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿZçÿëïüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÎöÿ,[àÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݤHÞŠHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿާïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûüþÿlêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ/]áÿ÷ùþÿÿÿÿÿÿÿÿÿíñüÿ½ËõÿÊÖ÷ÿúûþÿÿÿÿÿÿÿÿÿ¼ËõÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÞŠIÝpHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿZçÿÙâúÿüýÿÿÿÿÿÿÿÿÿÿùûþÿÊÖ÷ÿBläÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ§ºòÿÿÿÿÿÿÿÿÿ¹Èõÿ QßÿHÝÿHÝÿ=hãÿìðüÿÿÿÿÿÿÿÿÿUzæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝpHÞNHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿMÞÿ>iãÿ7câÿJÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿáçûÿÿÿÿÿÿÿÿÿ3`âÿHÝÿHÝÿHÝÿHÝÿ~›ìÿÿÿÿÿÿÿÿÿ¨ïÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÞN@Õ HÝøHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿüýÿÿÿÿÿÿíñüÿHÝÿHÝÿHÝÿHÝÿHÝÿKsåÿÿÿÿÿÿÿÿÿ²ÃôÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝø@Õ ÿÿÿ�GݶHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿ)YàÿÿÿÿÿÿÿÿÿÕÞùÿHÝÿHÝÿHÝÿHÝÿHÝÿ2`áÿÿÿÿÿÿÿÿÿÄÑöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGݶÿÿÿ�ÿÿÿ�GÝhHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿ*YàÿÿÿÿÿÿÿÿÿÔÝùÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÅÒ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÝhÿÿÿ�ÿÿÿ�LÙHÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿ*YàÿÿÿÿÿÿÿÿÿÔÝùÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÅÒ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþLÙÿÿÿ�ÿÿÿ�ÿÿÿ�HÝËHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿ*YàÿÿÿÿÿÿÿÿÿÔÝùÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÅÒ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝËÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÞtHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿ*YàÿÿÿÿÿÿÿÿÿÔÝùÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÅÒ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÞtÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�Mæ HÜåHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿ*YàÿÿÿÿÿÿÿÿÿÔÝùÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÅÒ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÜåMæ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�GÞdHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿ*YàÿÿÿÿÿÿÿÿÿÔÝùÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÿÿÿÿÿÿÿÿÅÒ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÞdÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿHÝÕHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿt“ëÿt“ëÿa„èÿHÝÿHÝÿHÝÿHÝÿHÝÿ Qßÿt“ëÿt“ëÿ\€çÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÕ�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�FÝLHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿ'WàÿêîüÿêîüÿÁÏöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿFÝLÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÜÀHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#SßÿÿÿÿÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿHÝÿ*YàÿÿÿÿÿÿÿÿÿÔÝùÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÜÀÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JÞHÝêHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿÿÿÿÿåëûÿHÝÿHÝÿHÝÿHÝÿHÝÿ4aâÿÿÿÿÿÿÿÿÿÌØøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝêJÞÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ<HÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿéîüÿÿÿÿÿýþÿÿNÞÿHÝÿHÝÿHÝÿHÝÿTzæÿÿÿÿÿÿÿÿÿª½óÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùHÝ<ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÝbHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÄÑöÿÿÿÿÿÿÿÿÿt“ëÿHÝÿHÝÿHÝÿHÝÿ³ÄôÿÿÿÿÿÿÿÿÿƒžíÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝbÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝŽHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ]èÿÿÿÿÿÿÿÿÿùúþÿ§ïÿOvæÿ[çÿ²Ãôÿÿÿÿÿÿÿÿÿðôýÿ'WàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝŽÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿHÞ°HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¶ÆôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿÿpêÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÞ°�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿHÝŽHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ{˜ìÿóöýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚâúÿKsåÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝŽ�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÝbHÝùHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ4aâÿ]èÿQwæÿ'WàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝùIÝbÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�HÝ<HÝêHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝêHÝ<ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JÞHÜÀHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÜÀJÞÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�FÝLHÝÕHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÕFÝLÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿGÞdHÜåHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÜåGÞd�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�Mæ IÞtHÝËHÝþHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝþHÝËIÞtMæ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�LÙGÝhGݶHÝøHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝøGݶGÝhLÙÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�@Õ HÞNIÝpHÞŠHݤHݾHÝØHÝòHÝòHÝØHݾHݤHÞŠIÝpHÞN@Õ ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿàÿÿÿÿÿþ��ÿÿÿÿð��ÿÿÿÿÀ��ÿÿÿÿ����ÿÿÿü����?ÿÿø����ÿÿð����ÿÿÀ����ÿÿ€����ÿÿ������ÿÿ������ÿþ������ü������?ø������ø������ð������ð������à������à������À������À������À������€������€������€������€��������������������������������������������������������������������������������������€������€������€������€������À������À������À������à������à������ð������ð������ø������ø������ü������?þ������ÿ������ÿÿ������ÿÿ€����ÿÿÀ����ÿÿð����ÿÿø����ÿÿü����?ÿÿÿ����ÿÿÿÿÀ��ÿÿÿÿð��ÿÿÿÿþ��ÿÿÿÿÿàÿÿÿ(���0���`���� �������������������������ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�Uã FÝLG݈HÝ£HݽHÝØHÝòHÝòHÝØHݽHÝ£G݈FÝLUã ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ� C×IÝbHÞ°HÝõHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝõHÞ°IÝb C×ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�Fè IÝ~HÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝóIÝ~Fè ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿIÜfHÝçHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝçIÜf�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�EâHÝÊHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÊEâÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�GÞ6HÝæHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝæGÞ6ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÝ[HÝ÷HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ÷IÝ[ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÝ[HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝ[ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�GÞ6HÝ÷HÝÿHÝÿHÝÿHÝÿ"SßÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ÷GÞ6ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�EâHÝæHÝÿHÝÿHÝÿHÝÿ\€çÿýþÿÿ›ìÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝæEâÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿHÝÊHÝÿHÝÿHÝÿHÝÿHÝÿKsåÿçìüÿiŠéÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÊ�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÜfHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿSyæÿ‘ªïÿj‹éÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÜfÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�Fè HÝçHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿHÝÿMÞÿ‹¥îÿÐÚøÿôöýÿÝäúÿ®Àóÿ2`áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝçFè ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÝ~HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿJÝÿÎÙøÿÿÿÿÿÿÿÿÿúûþÿÿÿÿÿÿÿÿÿôöýÿ=hãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝ~ÿÿÿ�ÿÿÿ�ÿÿÿ� C×HÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿlêÿÿÿÿÿöøþÿTzæÿIÝÿ0^áÿË×øÿÿÿÿÿ½ËõÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝó C×ÿÿÿ�ÿÿÿ�IÝbHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿ§»òÿÿÿÿÿž´ñÿHÝÿHÝÿHÝÿMtåÿÿÿÿÿ÷ùþÿIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÝaÿÿÿ�ÿÿÿ�HÞ°HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿÈÔ÷ÿÿÿÿÿz˜ìÿHÝÿHÝÿHÝÿ)Yàÿÿÿÿÿÿÿÿÿ-[áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÞ°ÿÿÿ�Uã HÝõHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿÌØøÿÿÿÿÿt“ëÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝõUã FÝLHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿÌØøÿÿÿÿÿt“ëÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿFÝLG݈HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿÌØøÿÿÿÿÿt“ëÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿG݈HÝ£HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿÌØøÿÿÿÿÿt“ëÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ£HݽHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿÆÓ÷ÿùúþÿpêÿHÝÿHÝÿHÝÿ"Sßÿùúþÿùúþÿ1^áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݽHÝØHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿpêÿФîÿDmäÿHÝÿHÝÿHÝÿHÝÿUzæÿ_‚èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝØHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿФîÿÿÿÿÿ¶ÆôÿHÝÿHÝÿHÝÿHÝÿÌØøÿÿÿÿÿt“ëÿHÝÿHÝÿHÝÿHÝÿíñüÿûüþÿNÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ€œíÿÿÿÿÿÃÐöÿHÝÿHÝÿHÝÿHÝÿÙâúÿÿÿÿÿh‰éÿHÝÿHÝÿHÝÿHÝÿg‰éÿs’ëÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòHÝØHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ_‚èÿÿÿÿÿíñüÿPÞÿHÝÿHÝÿ)YàÿùúþÿÿÿÿÿDmäÿHÝÿHÝÿHÝÿ"Sßÿùúþÿùúþÿ1^áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ7câÿ]èÿd†éÿ?jãÿKÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝØHݽHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ!Rßÿëïüÿÿÿÿÿ¾ÍöÿX}çÿ^èÿÍØøÿÿÿÿÿ×àùÿJÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿHÝÿ$Tßÿ±ÂôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÛøÿ<gãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݽHÝ£HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ]èÿóöýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéîüÿIqåÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿHÝÿ¦¹òÿÿÿÿÿ÷ùþÿ¤¸òÿ™¯ðÿåëûÿÿÿÿÿÜäúÿLÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ£G݈HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿíÿ¦¹òÿ¡¶ñÿz˜ìÿ%UßÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿ'WàÿýþÿÿÿÿÿÿSyæÿHÝÿHÝÿ'Wàÿðôýÿÿÿÿÿ`„èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿG݈FÝLHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿJråÿÿÿÿÿðôýÿHÝÿHÝÿHÝÿHÝÿµÆôÿÿÿÿÿ… íÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿFÝLUã HÝõHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿ]èÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿ µñÿÿÿÿÿ˜¯ðÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝõUã ÿÿÿ�HÞ°HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿ^èÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿ µñÿÿÿÿÿ™¯ðÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÞ°ÿÿÿ�ÿÿÿ�IÝbHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿ^èÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿ µñÿÿÿÿÿ™¯ðÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÝaÿÿÿ�ÿÿÿ� C×HÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿ^èÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿ µñÿÿÿÿÿ™¯ðÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝó C×ÿÿÿ�ÿÿÿ�ÿÿÿ�IÝ~HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿ^èÿÿÿÿÿÛãúÿHÝÿHÝÿHÝÿHÝÿ µñÿÿÿÿÿ™¯ðÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝ~ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�Fè HÝçHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿ;fãÿ‘ªïÿ}šìÿHÝÿHÝÿHÝÿHÝÿ^èÿ‘ªïÿZçÿHÝÿHÝÿHÝÿHÝÿHÝçFè ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÜfHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ#Sßÿÿÿÿÿÿÿÿÿ1_áÿHÝÿHÝÿHÝÿY~çÿñôýÿÎÙøÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÜfÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿHÝÊHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ"Sßÿÿÿÿÿÿÿÿÿ3`âÿHÝÿHÝÿHÝÿ`„èÿÿÿÿÿÚâúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÊ�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�EâHÝæHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿôöýÿÿÿÿÿLtåÿHÝÿHÝÿHÝÿ}šìÿÿÿÿÿÄÑöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝæEâÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�GÞ6HÝ÷HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÇÓ÷ÿÿÿÿÿ¯ÁóÿIÝÿHÝÿQßÿØáùÿÿÿÿÿ›±ðÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ÷GÞ6ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÝ[HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿQwæÿÿÿÿÿÿÿÿÿÜäúÿ¹Èõÿèíüÿÿÿÿÿöøþÿ.\áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝ[ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�IÝ[HÝ÷HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ_‚èÿêîüÿÿÿÿÿÿÿÿÿÿÿÿÿÕÞùÿDmäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝ÷IÝ[ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�GÞ6HÝæHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ1_áÿKsåÿ(XàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝæGÞ6ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�EâHÝÊHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÊEâÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ��UÿIÜfHÝçHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝçIÜf�Uÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�Fè IÝ~HÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝóIÝ~Fè ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ� C×IÝbHÞ°HÝõHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝõHÞ°IÝb C×ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�Uã FÝLG݈HÝ£HݽHÝØHÝòHÝòHÝØHݽHÝ£G݈FÝLUã ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿàÿÿ��ÿÿ��ÿÿ��ÿü��?ÿ��ÿð��ÿ��ÿÀ��ÿ��ÿ€��ÿ��ÿ����ÿ��þ������ü����?��ø������ð������ð������à������à������À������À������€������€������€��������������������������������������������������������������������������������������€������€������€������À������À������à������à������ð������ð������ø������ü����?��þ������ÿ����ÿ��ÿ€��ÿ��ÿÀ��ÿ��ÿð��ÿ��ÿü��?ÿ��ÿÿ��ÿÿ��ÿÿàÿÿ��(��� ���@���� �������������������������ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�$IÛIÜIHݘGÝÁIÝÚHÝóHÝóIÝÚGÝÁHݘIÜI$IÛÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�GàIÝ—HÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòIÝ—Gàÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ� @ßGÜ„HÝõHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝõGÜ„ @ßÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JßGÝÈHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÝÈJßÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JÝ-HÝãHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝãJÝ-ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JßHÝãHÝÿHÝÿLÞÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝãJßÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ� @ßGÝÈHÝÿHÝÿHÝÿâéûÿa„èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÝÈ @ßÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�GÜ„HÝÿHÝÿHÝÿHÝÿr’ëÿ8dãÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÜ„ÿÿÿ�ÿÿÿ�ÿÿÿ�GàHÝõHÝÿHÝÿHÝÿHÝÿÿÿÿÿ{˜ìÿHÝÿHÝÿHÝÿg‰éÿÙâúÿöøþÿÒÜøÿTzæÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝõGàÿÿÿ�ÿÿÿ�IÝ—HÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿ{˜ìÿHÝÿHÝÿ=hãÿýþÿÿ¬¾óÿ_‚èÿ¼Ëõÿöøþÿ,[àÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝ—ÿÿÿ�$IÛHÝòHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿ{˜ìÿHÝÿHÝÿz˜ìÿýþÿÿNÞÿHÝÿ4aâÿÿÿÿÿd†éÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝò$IÛIÜIHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿ{˜ìÿHÝÿHÝÿФîÿñôýÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÜIHݘHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿ{˜ìÿHÝÿHÝÿФîÿñôýÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݘGÝÁHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿ{˜ìÿHÝÿHÝÿФîÿñôýÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÝÁIÝÚHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿ{˜ìÿHÝÿHÝÿSyæÿ‰£îÿHÝÿHÝÿKÞÿ‘ªïÿGoäÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÚHÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÿÿÿÿ{˜ìÿHÝÿHÝÿ{˜ìÿÖßùÿHÝÿHÝÿHÝÿ¹Èõÿ:eâÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝóHÝóHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿòõýÿŒ¥îÿHÝÿHÝÿ›±ðÿãéûÿHÝÿHÝÿIÝÿc†èÿ'WàÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝóIÝÚHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ¹ÈõÿåëûÿPwæÿU{æÿíñüÿª½óÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿHÝÿ_‚èÿÌØøÿæìûÿÁÏöÿEnäÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÚGÝÁHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ1_áÿÌØøÿÿÿÿÿþþÿÿÂÐöÿ+ZàÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿ?jãÿýþÿÿ±ÂôÿmŽêÿÊÖ÷ÿïóýÿ&VàÿHÝÿHÝÿHÝÿHÝÿGÝÁHݘHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ QßÿOÞÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿ„ íÿùûþÿLÞÿHÝÿ=hãÿÿÿÿÿ\€çÿHÝÿHÝÿHÝÿHÝÿHݘIÜIHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿ˜¯ðÿêîüÿHÝÿHÝÿ#SßÿÿÿÿÿlŒêÿHÝÿHÝÿHÝÿHÝÿIÜI$IÛHÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿ™¯ðÿêîüÿHÝÿHÝÿ#SßÿÿÿÿÿlêÿHÝÿHÝÿHÝÿHÝò$IÛÿÿÿ�IÝ—HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿ™¯ðÿêîüÿHÝÿHÝÿ#SßÿÿÿÿÿlêÿHÝÿHÝÿHÝÿIÝ—ÿÿÿ�ÿÿÿ�GàHÝõHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿ_‚èÿŒ¥îÿHÝÿHÝÿNÞÿ™¯ðÿFoäÿHÝÿHÝÿHÝõGàÿÿÿ�ÿÿÿ�ÿÿÿ�GÜ„HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿÿÿÿÿt“ëÿHÝÿHÝÿ„ŸíÿÈÔ÷ÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÜ„ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ� @ßGÝÈHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿúûþÿ„ŸíÿHÝÿHÝÿ¥¹òÿÝäúÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿGÝÈ @ßÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JßHÝãHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿÊÕ÷ÿÞåúÿ@jãÿJråÿíñüÿ§ºòÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝãJßÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JÝ-HÝãHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿ=hãÿÞåúÿÿÿÿÿÿÿÿÿÍØøÿ-[áÿHÝÿHÝÿHÝÿHÝÿHÝãJÝ-ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JßGÝÈHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝÿ0^áÿ)YàÿHÝÿHÝÿHÝÿHÝÿHÝÿGÝÈJßÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ� @ßGÜ„HÝõHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝõGÜ„ @ßÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�GàIÝ—HÝòHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝòIÝ—Gàÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�$IÛIÜIHݘGÝÁIÝÚHÝóHÝóIÝÚGÝÁHݘIÜI$IÛÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿðÿÿ€ÿþ��ü��?ø��ð��à��À��À��€��€��€����������������������������������€��€��€��À��À��à��ð��ø��ü��?þ��ÿ€ÿÿðÿ(������ ���� �������������������������ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JÝ&HÝ€IݳHÝæHÝæIݳHÝ€JÝ&ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ���ÿIÝpHÝöHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝöIÝp��ÿÿÿÿ�ÿÿÿ�ÿÿÿ���ÿHݵIÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHݵ��ÿÿÿÿ�ÿÿÿ�IÝpHÝÿ_‚èÿ/^áÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIÝpÿÿÿ�JÝ&HÝöHÝÿФîÿHpäÿNÞÿ·Çõÿ·Çõÿ`„èÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝöJÝ&HÝ€HÝÿHÝÿФîÿHpäÿIråÿˆ¢îÿPÞÿ¸ÈõÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝIݳHÝÿHÝÿФîÿHpäÿOvæÿƒžíÿNÞÿ¾ÌöÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿIݳHÝæHÝÿHÝÿФîÿHpäÿ;fãÿZ~çÿIÝÿg‰éÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝÿHÝæHÝæHÝÿHÝÿu“ëÿt“ëÿ|™ìÿlêÿKÞÿ~›ìÿHÝÿ'Wàÿx–ìÿOvæÿHÝÿHÝÿHÝæIݳHÝÿHÝÿNÞÿƒžíÿ~›ìÿLÞÿNÞÿ¾ÌöÿHÝÿ­¿óÿRxæÿ¾Ìöÿ(XàÿHÝÿIݳHÝ€HÝÿHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿ¾ÌöÿHÝÿ¾ÌöÿHÝÿ‘ªïÿ?jãÿHÝÿHÝJÝ&HÝöHÝÿHÝÿHÝÿHÝÿHÝÿNÞÿ¾ÌöÿHÝÿ˜¯ðÿHÝÿv”ëÿ6câÿHÝöJÝ&ÿÿÿ�IÝpHÝÿHÝÿHÝÿHÝÿHÝÿLÞÿ¾ÍöÿHÝÿ¯ÁóÿHÝÿHÝÿHÝÿIÝpÿÿÿ�ÿÿÿ���ÿHݵHÝÿHÝÿHÝÿHÝÿHÝÿ«¾óÿ£·òÿ£·òÿHÝÿHÝÿHݵ��ÿÿÿÿ�ÿÿÿ�ÿÿÿ���ÿIÝpHÝöHÝÿHÝÿHÝÿHÝÿPÞÿHÝÿHÝöIÝp��ÿÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�JÝ&HÝ€IݳHÝæHÝæIݳHÝ€JÝ&ÿÿÿ�ÿÿÿ�ÿÿÿ�ÿÿÿ�ø��ð��À��À��€������������������������€��À��À��ð��ø������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/win-installer/modpath.iss����������������������0000644�0000153�0000161�00000015446�12321735642�027332� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// ---------------------------------------------------------------------------- // // Inno Setup Ver: 5.4.2 // Script Version: 1.4.2 // Author: Jared Breland <jbreland@legroom.net> // Homepage: http://www.legroom.net/software // License: GNU Lesser General Public License (LGPL), version 3 // http://www.gnu.org/licenses/lgpl.html // // Script Function: // Allow modification of environmental path directly from Inno Setup installers // // Instructions: // Copy modpath.iss to the same directory as your setup script // // Add this statement to your [Setup] section // ChangesEnvironment=true // // Add this statement to your [Tasks] section // You can change the Description or Flags // You can change the Name, but it must match the ModPathName setting below // Name: modifypath; Description: &Add application directory to your environmental path; Flags: unchecked // // Add the following to the end of your [Code] section // ModPathName defines the name of the task defined above // ModPathType defines whether the 'user' or 'system' path will be modified; // this will default to user if anything other than system is set // setArrayLength must specify the total number of dirs to be added // Result[0] contains first directory, Result[1] contains second, etc. // const // ModPathName = 'modifypath'; // ModPathType = 'user'; // // function ModPathDir(): TArrayOfString; // begin // setArrayLength(Result, 1); // Result[0] := ExpandConstant('{app}'); // end; // #include "modpath.iss" // ---------------------------------------------------------------------------- procedure ModPath(); var oldpath: String; newpath: String; updatepath: Boolean; pathArr: TArrayOfString; aExecFile: String; aExecArr: TArrayOfString; i, d: Integer; pathdir: TArrayOfString; regroot: Integer; regpath: String; begin // Get constants from main script and adjust behavior accordingly // ModPathType MUST be 'system' or 'user'; force 'user' if invalid if ModPathType = 'system' then begin regroot := HKEY_LOCAL_MACHINE; regpath := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; end else begin regroot := HKEY_CURRENT_USER; regpath := 'Environment'; end; // Get array of new directories and act on each individually pathdir := ModPathDir(); for d := 0 to GetArrayLength(pathdir)-1 do begin updatepath := true; // Modify WinNT path if UsingWinNT() = true then begin // Get current path, split into an array RegQueryStringValue(regroot, regpath, 'Path', oldpath); oldpath := oldpath + ';'; i := 0; while (Pos(';', oldpath) > 0) do begin SetArrayLength(pathArr, i+1); pathArr[i] := Copy(oldpath, 0, Pos(';', oldpath)-1); oldpath := Copy(oldpath, Pos(';', oldpath)+1, Length(oldpath)); i := i + 1; // Check if current directory matches app dir if pathdir[d] = pathArr[i-1] then begin // if uninstalling, remove dir from path if IsUninstaller() = true then begin continue; // if installing, flag that dir already exists in path end else begin updatepath := false; end; end; // Add current directory to new path if i = 1 then begin newpath := pathArr[i-1]; end else begin newpath := newpath + ';' + pathArr[i-1]; end; end; // Append app dir to path if not already included if (IsUninstaller() = false) AND (updatepath = true) then newpath := newpath + ';' + pathdir[d]; // Write new path RegWriteStringValue(regroot, regpath, 'Path', newpath); // Modify Win9x path end else begin // Convert to shortened dirname pathdir[d] := GetShortName(pathdir[d]); // If autoexec.bat exists, check if app dir already exists in path aExecFile := 'C:\AUTOEXEC.BAT'; if FileExists(aExecFile) then begin LoadStringsFromFile(aExecFile, aExecArr); for i := 0 to GetArrayLength(aExecArr)-1 do begin if IsUninstaller() = false then begin // If app dir already exists while installing, skip add if (Pos(pathdir[d], aExecArr[i]) > 0) then updatepath := false; break; end else begin // If app dir exists and = what we originally set, then delete at uninstall if aExecArr[i] = 'SET PATH=%PATH%;' + pathdir[d] then aExecArr[i] := ''; end; end; end; // If app dir not found, or autoexec.bat didn't exist, then (create and) append to current path if (IsUninstaller() = false) AND (updatepath = true) then begin SaveStringToFile(aExecFile, #13#10 + 'SET PATH=%PATH%;' + pathdir[d], True); // If uninstalling, write the full autoexec out end else begin SaveStringsToFile(aExecFile, aExecArr, False); end; end; end; end; // Split a string into an array using passed delimeter procedure MPExplode(var Dest: TArrayOfString; Text: String; Separator: String); var i: Integer; begin i := 0; repeat SetArrayLength(Dest, i+1); if Pos(Separator,Text) > 0 then begin Dest[i] := Copy(Text, 1, Pos(Separator, Text)-1); Text := Copy(Text, Pos(Separator,Text) + Length(Separator), Length(Text)); i := i + 1; end else begin Dest[i] := Text; Text := ''; end; until Length(Text)=0; end; procedure CurStepChanged(CurStep: TSetupStep); var taskname: String; begin taskname := ModPathName; if CurStep = ssPostInstall then if IsTaskSelected(taskname) then ModPath(); end; procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); var aSelectedTasks: TArrayOfString; i: Integer; taskname: String; regpath: String; regstring: String; appid: String; begin // only run during actual uninstall if CurUninstallStep = usUninstall then begin // get list of selected tasks saved in registry at install time appid := '{#emit SetupSetting("AppId")}'; if appid = '' then appid := '{#emit SetupSetting("AppName")}'; regpath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\'+appid+'_is1'); RegQueryStringValue(HKLM, regpath, 'Inno Setup: Selected Tasks', regstring); if regstring = '' then RegQueryStringValue(HKCU, regpath, 'Inno Setup: Selected Tasks', regstring); // check each task; if matches modpath taskname, trigger patch removal if regstring <> '' then begin taskname := ModPathName; MPExplode(aSelectedTasks, regstring, ','); if GetArrayLength(aSelectedTasks) > 0 then begin for i := 0 to GetArrayLength(aSelectedTasks)-1 do begin if comparetext(aSelectedTasks[i], taskname) = 0 then ModPath(); end; end; end; end; end; function NeedRestart(): Boolean; var taskname: String; begin taskname := ModPathName; if IsTaskSelected(taskname) and not UsingWinNT() then begin Result := True; end else begin Result := False; end; end; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/win-installer/setup.iss������������������������0000644�0000153�0000161�00000003640�12321735776�027037� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Juju" #define MyAppVersion "1.18.1" #define MyAppPublisher "Canonical, Ltd" #define MyAppURL "http://juju.ubuntu.com/" #define MyAppExeName "juju.exe" [Setup] ; NOTE: The value of AppId uniquely identifies this application. ; Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) AppId={{B2781001-AA89-4E70-AC2B-17D004DFBAE2} AppName={#MyAppName} AppVersion={#MyAppVersion} ;AppVerName={#MyAppName} {#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} ChangesEnvironment=yes DefaultDirName={pf}\{#MyAppName} DisableDirPage=auto DefaultGroupName={#MyAppName} DisableProgramGroupPage=yes OutputBaseFilename=juju-setup-{#MyAppVersion} SetupIconFile=juju.ico Compression=lzma SolidCompression=yes UninstallDisplayIcon={app}\juju.ico UninstallDisplayName=Juju WizardImageStretch=no WizardImageFile=juju-wizard-side.bmp WizardImageBackColor=clWhite WizardSmallImageFile=juju-55.bmp LicenseFile=LICENCE.txt [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Tasks] Name: modifypath; Description: Add application directory to your environment path [Files] Source: "juju.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "juju.ico"; DestDir: "{app}"; Flags: ignoreversion Source: "README.txt"; DestDir: "{app}"; Flags: ignoreversion Source: "LICENCE.txt"; DestDir: "{app}"; Flags: ignoreversion [Run] Filename: "{app}\README.txt"; Description: "View the README file"; Flags: postinstall shellexec skipifsilent [Code] const ModPathName= 'modifypath'; ModPathType = 'system'; function ModPathDir(): TArrayOfString; begin setArrayLength(Result, 1) Result[0] := ExpandConstant('{app}'); end; #include "modpath.iss" ������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/win-installer/juju-wizard-side.bmp�������������0000644�0000153�0000161�00000241426�12321735642�031052� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BMC�����6���(���¤���¨���������àB�Ä��Ä����������øøøÁÁÁƒƒƒMMM222%%%888MMM‚‚‚ÃÃÃúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõººº{{{HHH///''':::PPPˆˆˆÊÊÊüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������������������������ PPPÊÊÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøø������������������������������������[[[ÕÕÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ888���������������������������������������������IIIËËËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������������������������������WWWÙÙÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ```������������������������������������������������šššÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿFFF������������������������������������������������ °°°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰‰‰������������������������������������������������������rrrýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnnn���������������������������������������������������‡‡‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²²²���������������������������������������������������������{{{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–––���������������������������������������������������������“““ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÛÛ���������������������������������������������������������¿¿¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½½½���������������������������������������������������������ÑÑÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüü���������������������������������������������������������íííÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿååå������������������������������������������������������������)))öööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBBBKKK›››²²²­­­€€€RRR$$$���������������������������������€€€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)))RRR„„„žžžµµµ¨¨¨zzzMMM ���������������������������������˜˜˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛÛÛGGG������������������������������úúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÐÐ999������������������������������,,,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjjj������������������������������¨¨¨ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüTTT������������������������������ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ<<<���������������������������HHHÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúú'''���������������������������```ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬¬¬���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘‘‘���������������������������(((ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüü���������������������������ÚÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿððð ������������������������ñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†††���������������������������¥¥¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������½½½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌ���������������������������tttÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬¬¬���������������������������ŒŒŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæææ���������������������������aaaÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÆÆ���������������������������yyyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý������������������������RRRÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿààà���������������������������jjjÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������������CCCÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýÇÇÇ‘‘‘}}}iiiVVVBBB///---555>>>FFFNNNWWWbbb}}}›››¹¹¹×××õõõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùù������������������������[[[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûÁÁÁ{{{gggTTT@@@---...666???GGGOOOXXXcccžžž¼¼¼ÚÚÚ÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ555������������������������444ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêêꢢ¢YYY���������������������������������������������������������III}}}°°°ãããÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������������LLLÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäää›››RRR���������������������������������������������������������NNN‚‚‚µµµèèèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������(((ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóóyyy���������������������������������������������������������������������������������JJJ~~~±±±ûûûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������@@@ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíííkkk���������������������������������������������������������������������������������OOO‚‚‚¶¶¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþŸŸŸ���������������������������������������������������������������������������������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûû‘‘‘���������������������������������������������������������������������������������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöööHHH���������������������������������������������������������������������������������������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïï777���������������������������������������������������������������������������������������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõCCC������������������������������������������������������������������������������������������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿííí222������������������������������������������������������������������������������������������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóóó???���������������������������������������������������������������������������������������������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëëë///���������������������������������������������������������������������������������������������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[[[������������������������������������������������������������������������������������������������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿCCC������������������������������������������������������������������������������������������������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¼¼���������������������������������������������555JJJ___ccc[[[TTTFFF///���������������������������������������èèèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤¤¤���������������������������������������������777LLLaaabbb[[[SSSCCC,,,���������������������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøø&&&������������������������������������@@@”””ÚÚÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûûûßßßÁÁÁ”””666���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïïï������������������������������������KKKœœœâââÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùùùÜÜܽ½½+++���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„„„���������������������������������***¶¶¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlll���������������������������������555ÃÃÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýý������������������������������EEE÷÷÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõ ������������������������������XXXüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾¾¾������������������������������@@@öööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦¦¦������������������������������SSSüüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿccc���������������������������àààÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿKKK���������������������������ïïïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøøø���������������������������sssÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëëë���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉÉÉ���������������������������êêêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±±±���������������������������úúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœœœ���������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„„„���������������������������___ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnnn���������������������������ŽŽŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿVVV���������������������������®®®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@@@���������������������������ÔÔÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(((���������������������������óóóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������ñññÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúúú������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������������ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëëë���������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõõõ��������������������������� ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝÝÝ���������������������������===ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿççç���������������������������888ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏ���������������������������TTTÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙÙÙ���������������������������PPPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁÁÁ���������������������������kkkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌÌÌ���������������������������hhhÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´´´���������������������������‚‚‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������€€€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������™™™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������———ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������¯¯¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGGG������������������������'''ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÇÇ���������������������������ŸŸŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���������������������������çççÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ'''������������������������???ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯¯¯���������������������������···ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿggg���������������������������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^^^BBBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÎΫ««ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêêêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBBBWWWÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹¹¹ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿzzzÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïóý×àù¿Íö§»ò¨ï¦î¥¹ò½ËõÕÞùíñüþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïóý©¼óh‰éKså3`âNÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝLÞ1^áIqåb…è µñèíüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâéûš°ðQwæKÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝIÝHpä©ïÚâúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×àùФîAkäHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ8dâíÌØøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝäúb…èHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝSyæÒÜøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêîüv”ëKÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝIÝg‰éáçûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôöý‰£îQßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝLÞ{˜ìíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿœ²ñ(XàHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ"Sߨïúûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóöý[çHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝLtåìðüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàçú<gãHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ1^áÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃÐö&VàHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝPÞ´Åôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿž´ñKÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝIÝŒ¥îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúûþt“ëHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝb…èöøþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿk‹êHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝW|çüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“«ïHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ}šìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿºÉõIÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHݦ¹òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØáù!RßHÝHÝHÝHÝHÝHÝHÝHÝ6c⣷ò’ªï(XàHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝLÞÉÕ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíñü3`âHÝHÝHÝHÝHÝHÝHÝHÝHÝÀÎöÿÿÿÿÿÿ›±ñHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ(XàãéûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûüþOvæHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝÛãúÿÿÿÿÿÿ¶ÆôHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ?jãõ÷þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„ŸíHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ]èãéûÕÞùEnäHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝmŽêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäêûNÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝIÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝIÝÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmŽêHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝLÞÌØøÌØøÌØø¯ÁóHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ"SßNÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝW|çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖßùIÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ$Tß©ï¾ÍöæìûÿÿÿüýÿÝäúµÆôx–ìJÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝÃÐöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZçHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝY~ççìüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÙø<gãHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝEnäþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÓ÷HÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝa„èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùúþ5bâHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHݰÁôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHpäHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝNÞâèûÿÿÿÿÿÿÿÿÿòõý’ªïg‰émŽê¡¶ñûüþÿÿÿÿÿÿÿÿÿµÆôHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ5bâúûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ÇõHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝ}šìÿÿÿÿÿÿÿÿÿãéû.\áHÝHÝHÝHÝFoäùûþÿÿÿÿÿÿÿÿÿJråHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ¡¶ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿd†éHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHݵÆôÿÿÿÿÿÿÿÿÿg‰éHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿÿÿÿÿÿÿ„ŸíHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNuåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùûþ!RßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝØáùÿÿÿÿÿÿÿÿÿ QßHÝHÝHÝHÝHÝHÝRxæÿÿÿÿÿÿÿÿÿ©¼óHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJÝíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÌöHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝIÝùúþÿÿÿÿÿÿîòýHÝHÝHÝHÝHÝHÝHÝ1_áÿÿÿÿÿÿÿÿÿÍØøHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHݧ»òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿt“ëHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ^èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿ,[àHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝOÞ÷ùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍØøHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ·Çõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„ŸíHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝmŽêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=hãHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ'WàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿNÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝòõýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðôýHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝÚâúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØáùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝÂÐöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ÍöHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ©¼óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§»òHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝIÝV|çV|çV|çKsåHÝHÝHÝHÝHÝHÝHÝIÝV|çV|çV|çKsåHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ‘ªïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨ïHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝIÝ1_á1_á1_á-[áHÝHÝHÝHÝHÝHÝHÝHÝJÝ`ƒèKsåHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝy—ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿx–ëHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHݨ¼òÿÿÿÿÿÿu“ëHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝa„èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`ƒèHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝíñüÿÿÿÿÿÿ»ÊõHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝIråÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿV|çHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝLÞÿÿÿÿÿÿÿÿÿãéûHÝHÝHÝHÝHÝHÝHÝ#SßÿÿÿÿÿÿÿÿÿØáùHÝHÝHÝHÝHÝHÝHÝHݘ¯ðÿÿÿýþÿfˆéHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ@jãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿmŽêHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝéîüÿÿÿÿÿÿüýÿLÞHÝHÝHÝHÝHÝHÝ>iãÿÿÿÿÿÿÿÿÿ½ËõHÝHÝHÝHÝHÝHÝHÝHÝHÝDmä3`âHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝW|çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ… íHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝÆÓ÷ÿÿÿÿÿÿÿÿÿ?jãHÝHÝHÝHÝHÝHÝe‡éÿÿÿÿÿÿÿÿÿš°ðHÝHÝHÝHÝHÝHÝHÝKÞ µñ µñ µñФîHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝoêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ñHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ¡¶ñÿÿÿÿÿÿÿÿÿ°ÁôHÝHÝHÝHÝHÝJÝÖßùÿÿÿÿÿÿÿÿÿu“ëHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJÝfˆé”¬ð¼Ëõâéû×àù°Áôˆ¢îLtåHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝH݇¢îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿµÆôHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ>iãýþÿÿÿÿÿÿÿÿÿÿž´ñ$TßHÝHÝ2`á¼Ëõÿÿÿÿÿÿÿÿÿìðü"SßHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ9eâÍØøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤¸ò!RßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝŸ´ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍØøHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ¢·òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäêûëïüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿqêHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝNuåøùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâèû#SßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ·ÇõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåëûHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ&VàÆÓ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤¸òIÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝIÝÕÞùÿÿÿÿÿÿÿÿÿýþÿ¼Ëõ‘ªï›±ñÏÚøÿÿÿÿÿÿÿÿÿÿÿÿ™¯ðHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝÏÚøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûüþIÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝIÝz˜ìöøþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæìûW|çHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝpêÿÿÿÿÿÿÿÿÿîòýJråHÝHÝHÝHÝt“ëÿÿÿÿÿÿÿÿÿûüþ9eâHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝçìüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ)YàHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞAkäiŠé©ï‡¢î`ƒè8dâKÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHݵÆôÿÿÿÿÿÿÿÿÿ›ìHÝHÝHÝHÝHÝHݶÆôÿÿÿÿÿÿÿÿÿ{˜ìHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJÝüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿa„èHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝÖßùÿÿÿÿÿÿÿÿÿ&VàHÝHÝHÝHÝHÝHÝZçÿÿÿÿÿÿÿÿÿž´ñHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝKsåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿª½óHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝõ÷þÿÿÿÿÿÿðôýHÝHÝHÝHÝHÝHÝHÝ=hãÿÿÿÿÿÿÿÿÿÀÎöHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ”¬ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïóýKÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝMÞÿÿÿÿÿÿÿÿÿÖßùHÝHÝHÝHÝHÝHÝHÝ%UßÿÿÿÿÿÿÿÿÿÓÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝÞåúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRxæHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝ#SßÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ<gãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›±ñHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝ#SßÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ… íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäêûHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝ#SßÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝÎÙøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBläHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝ#SßÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ-[áþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ¥îHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝ#SßÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝv”ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëïüQßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝ#SßÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝKÞÜäúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿФîHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝ#SßÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝt“ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôöý)YàHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝ#SßÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝPÞéîüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³ñHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝJ݃žíƒžíƒžímŽêHÝHÝHÝHÝHÝHÝHÝMÞƒžíƒžíƒžímŽêHÝHÝHÝHÝHÝHÝHÝHÝH݇¢îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûüþ6câHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ'Wàóöýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÂôHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝ"SßñôýñôýñôýÈÔ÷HÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ›±ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿFoäHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞÿÿÿÿÿÿÿÿÿÛãúHÝHÝHÝHÝHÝHÝHÝ#SßÿÿÿÿÿÿÿÿÿÔÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ3`âúûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÝùOÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝMÞÿÿÿÿÿÿÿÿÿßæúHÝHÝHÝHÝHÝHÝHÝ&VàÿÿÿÿÿÿÿÿÿÓÝùHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝKÞÂÐöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²ÃôIÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝïóýÿÿÿÿÿÿøùþIÝHÝHÝHÝHÝHÝHÝ@jãÿÿÿÿÿÿÿÿÿ¼ËõHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝœ²ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿФîHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝÌØøÿÿÿÿÿÿÿÿÿ-[áHÝHÝHÝHÝHÝHÝ`„èÿÿÿÿÿÿÿÿÿ™¯ðHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝt“ëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿa„èHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ©¼óÿÿÿÿÿÿÿÿÿ•¬ðHÝHÝHÝHÝHÝHÝÄÑöÿÿÿÿÿÿÿÿÿv”ëHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝOvæúûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöøþAkäHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝU{æÿÿÿÿÿÿÿÿÿùûþpêHÝHÝHÝMÞ’ªïÿÿÿÿÿÿÿÿÿõ÷þ,[àHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ3`âíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäêû*YàHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ»Êõÿÿÿÿÿÿÿÿÿÿÿÿáçû¶Æô¿Íöìðüÿÿÿÿÿÿÿÿÿÿÿÿ‡¢îHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ QߨáùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍØø&VàHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ7câæìûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌØøNÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝPÞ¾Ìöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàçú<gãHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ#Sß©¼óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøùþíJÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ1^áÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóöý[çHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝAkäoê—®ð¾Ìö³Ãô‹¥îc†è/]áHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝLtåìðüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþÿƒžíHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝqêúûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬¾óNÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝKÞ›±ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎÙø3`âHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ+ZàÀÎöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùûþš°ð'WàHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ Qߦîöøþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóöý†¡îPÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝLÞx–ììðüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéîüs’ëKÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝIÝd†éßæúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜäúj‹é!RßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝNÞ^èÏÚøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùûþ¾Íöu“ë-[áHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ&VàlŒêµÆôöøþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÎÙø„ í;fãHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ2`á{˜ìÅÒ÷üýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåëûÉÕ÷±Âô™¯ðíiŠéQwæ9eâ!RßPÞ6câNuåfˆé~›ì–­ð®ÀóÆÓ÷àçúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/win-installer/juju-55.bmp����������������������0000644�0000153�0000161�00000023106�12321735642�027052� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BMF&������6���(���7���:���������&��Ä��Ä����������ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøùþºÉõŒ¥ît“ë\€çDmä,[àLÞ,[àDmä\€çt“댥îºÉõøùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíñü§»ò`ƒèPÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝPÞ`ƒè§»òíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙâú^èJÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJÝ^èÙâúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèíüqêJÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJÝqêèíüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùûþ†¡îOÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝOÞ†¡îùûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëïüJråHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJråëïüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÞù1^áHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ1^áÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÆôQßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝQß¶Æôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÆôIÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝIݶÆôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÞùQßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝQßÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëïü1^áHÝHÝHÝHÝHÝx–ìÄÑö4aâHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ1^áëïüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿùûþJråHÝHÝHÝHÝHÝHÝÝäúÿÿÿv”ëHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJråùûþÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿ†¡îHÝHÝHÝHÝHÝHÝHÝKs剣î!RßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝH݆¡îÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿèíüOÞHÝHÝHÝHÝHÝHÝHÝÜäúâéû‚íHÝHÝHÝHÝHÝHÝHÝHÝMÞ@jã1_áIÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝOÞèíüÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿqêHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝHÝHÝ<gãÉÕ÷üýÿÿÿÿÿÿÿóöý›±ðPÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝqêÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÙâúJÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝHÝCmäùúþÿÿÿÿÿÿþþÿÿÿÿÿÿÿÿÿÿÓÝùKÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJÝÙâúÿÿÿÿÿÿ���ÿÿÿÿÿÿ^èHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝHÝÈÔ÷ÿÿÿôöý^èLÞ$Tßš°ðÿÿÿÿÿÿw•ëHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ^èÿÿÿÿÿÿ���ÿÿÿíñüJÝHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝ"SßÿÿÿÿÿÿާïHÝHÝHÝIÝÜäúÿÿÿ¾ÍöHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJÝíñüÿÿÿ���ÿÿÿ§»òHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝBläÿÿÿÿÿÿY~çHÝHÝHÝHݪ½óÿÿÿâéûHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHݧ»òÿÿÿ���ÿÿÿ`ƒèHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝOvæÿÿÿÿÿÿHpäHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ`ƒèÿÿÿ���øùþPÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝOvæÿÿÿÿÿÿHpäHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝPÞøùþ���ºÉõHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝOvæÿÿÿÿÿÿHpäHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHݺÉõ���Œ¥îHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝOvæÿÿÿÿÿÿHpäHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝŒ¥î���t“ëHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝOvæÿÿÿÿÿÿHpäHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝt“ë���\€çHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝOvæÿÿÿÿÿÿHpäHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ\€ç���DmäHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝ+Záj‹éj‹é(XàHÝHÝHÝHÝ-[áY}ç@jãHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝDmä���,[àHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝùúþÿÿÿ‘ªïHÝHÝHÝHÝOvæÿÿÿÿÿÿHpäHÝHÝHÝHÝb…èÿÿÿ»ÊõHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ,[à���LÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ÷ùþÿÿÿ”¬ðHÝHÝHÝHÝRxæÿÿÿÿÿÿFoäHÝHÝHÝHÝb…èÿÿÿºÉõHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝLÞ���,[àHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝÝäúÿÿÿ®ÀóHÝHÝHÝHÝlŒêÿÿÿÿÿÿ,[àHÝHÝHÝHÝ=hãpê[çHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ,[à���DmäHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHݸÈõÿÿÿîòý#SßHÝHÝHݼËõÿÿÿòõýHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝHÝHÝ(Xàs’ëš°ð©¼ó„ íNuåHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝDmä���\€çHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝTzæÿÿÿÿÿÿÙâúx–ëmŽêºÉõÿÿÿÿÿÿ§ïHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝHÝW|çìðüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§»òNÞHÝHÝHÝHÝHÝHÝHÝHÝHÝ\€ç���t“ëHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝš°ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇÓ÷ QßHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝ)Yàóöýÿÿÿùûþ«¾ó—®ðÖßùÿÿÿÿÿÿŒ¥îHÝHÝHÝHÝHÝHÝHÝHÝHÝt“ë���Œ¥îHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝSy椸òÊÖ÷ÒÜø«¾ónŽêIÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝ“«ïÿÿÿÿÿÿQwæHÝHÝKÞÄÑöÿÿÿ÷ùþPÞHÝHÝHÝHÝHÝHÝHÝHÝŒ¥î���ºÉõHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHݼËõÿÿÿÍØøHÝHÝHÝHÝ_‚èÿÿÿÿÿÿCmäHÝHÝHÝHÝHÝHÝHÝHݺÉõ���øùþPÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝ×àùÿÿÿ®ÀóHÝHÝHÝHÝ?jãÿÿÿÿÿÿa„èHÝHÝHÝHÝHÝHÝHÝPÞøùþ���ÿÿÿ`ƒèHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝÛãúÿÿÿ§»òHÝHÝHÝHÝ9eâÿÿÿÿÿÿe‡éHÝHÝHÝHÝHÝHÝHÝ`ƒèÿÿÿ���ÿÿÿ§»òHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝÛãúÿÿÿ§»òHÝHÝHÝHÝ9eâÿÿÿÿÿÿe‡éHÝHÝHÝHÝHÝHÝHݧ»òÿÿÿ���ÿÿÿíñüJÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝÛãúÿÿÿ§»òHÝHÝHÝHÝ9eâÿÿÿÿÿÿe‡éHÝHÝHÝHÝHÝHÝJÝíñüÿÿÿ���ÿÿÿÿÿÿ^èHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝÛãúÿÿÿ§»òHÝHÝHÝHÝ9eâÿÿÿÿÿÿe‡éHÝHÝHÝHÝHÝHÝ^èÿÿÿÿÿÿ���ÿÿÿÿÿÿÙâúJÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝÛãúÿÿÿ§»òHÝHÝHÝHÝ9eâÿÿÿÿÿÿe‡éHÝHÝHÝHÝHÝJÝÙâúÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿqêHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝ£·ò¾Ìö~›ìHÝHÝHÝHÝ.\á¾Ìö¾ÌöNuåHÝHÝHÝHÝHÝqêÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿèíüOÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝœ²ñ¶Æôy—ìHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝOÞèíüÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿ†¡îHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ™¯ðÿÿÿñôýHÝHÝHÝHÝHÝÛãúÿÿÿ§»òHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝH݆¡îÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿùûþJråHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHݨïÿÿÿúûþJÝHÝHÝHÝHÝçìüÿÿÿŸ´ñHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJråùûþÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëïü1^áHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝmŽêÿÿÿÿÿÿ;fãHÝHÝHÝ&Vàþþÿÿÿÿ€œíHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ1^áëïüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÞùQßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ7câüýÿÿÿÿ½ËõQßHÝNÞ­¿óÿÿÿÿÿÿIråHÝHÝHÝHÝHÝHÝHÝHÝHÝQßÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÆôIÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ¢·òÿÿÿÿÿÿñôýÏÚøîòýÿÿÿÿÿÿ³ÄôHÝHÝHÝHÝHÝHÝHÝHÝHÝIݶÆôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶ÆôQßHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝMÞ•¬ðýþÿÿÿÿÿÿÿÿÿÿÿÿÿŸ´ñPÞHÝHÝHÝHÝHÝHÝHÝHÝQß¶Æôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÕÞù1^áHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝ*YàTzæqêUzæ-[áHÝHÝHÝHÝHÝHÝHÝHÝHÝ1^áÕÞùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëïüJråHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJråëïüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùûþ†¡îOÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝOÞ†¡îùûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèíüqêJÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJÝqêèíüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙâú^èJÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝJÝ^èÙâúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿíñü§»ò`ƒèPÞHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝHÝPÞ`ƒè§»òíñüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøùþºÉõŒ¥ît“ë\€çDmä,[àLÞ,[àDmä\€çt“댥îºÉõøùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ���ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/jujuman.py�������������������������������������0000644�0000153�0000161�00000010412�12321735642�024375� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������"""Functions for generating the manpage using the juju command.""" import subprocess import textwrap import time class JujuMan(object): def __init__(self): self.version = self._version() def get_filename(self, options): """Provides name of manpage""" return 'juju.1' def run_juju(self, *args): cmd = ['juju'] + list(args) return subprocess.check_output(cmd).strip() def _version(self): juju_version = self.run_juju('version') return juju_version.split('-')[0] def commands(self): commands = self.run_juju('help', 'commands') result = [] for line in commands.split('\n'): name, short_help = line.split(' ', 1) if 'alias for' in short_help: continue result.append((name, short_help.strip())) return result def write_documentation(self, options, outfile): """Assembles a man page""" t = time.time() tt = time.gmtime(t) params = { "cmd": "juju", "datestamp": time.strftime("%Y-%m-%d",tt), "timestamp": time.strftime("%Y-%m-%d %H:%M:%S +0000",tt), "version": self.version, } outfile.write(man_preamble % params) outfile.write(man_escape(man_head % params)) outfile.write(man_escape(self.getcommand_list(params))) outfile.write("".join(environment_variables())) outfile.write(man_escape(man_foot % params)) def getcommand_list(self, params): """Builds summary help for command names in manpage format""" output = '.SH "COMMAND OVERVIEW"\n' for cmd_name, short_help in self.commands(): tmp = '.TP\n.B "%s %s"\n%s\n' % (params['cmd'], cmd_name, short_help) output = output + tmp return output ENVIRONMENT = ( ('JUJU_ENV', textwrap.dedent("""\ Provides a way for the shell environment to specify the current Juju environment to use. If the environment is specified explicitly using -e ENV, this takes precedence. """)), ('JUJU_HOME', textwrap.dedent("""\ Overrides the default Juju configuration directory of ~/.juju. """)), ('AWS_ACCESS_KEY_ID', textwrap.dedent("""\ The access-key for your AWS account. """)), ('AWS_SECRET_ACCESS_KEY', textwrap.dedent("""\ The secret-key for your AWS account. """)), ('OS_USERNAME', textwrap.dedent("""\ Your openstack username. """)), ('OS_PASSWORD', textwrap.dedent("""\ Your openstack password. """)), ('OS_TENANT_NAME', textwrap.dedent("""\ Your openstack tenant name. """)), ('OS_REGION_NAME', textwrap.dedent("""\ Your openstack region name. """)), ) def man_escape(string): """Escapes strings for man page compatibility""" result = string.replace("\\","\\\\") result = result.replace("`","\\'") result = result.replace("'","\\*(Aq") result = result.replace("-","\\-") return result def environment_variables(): yield ".SH \"ENVIRONMENT\"\n" for k, desc in ENVIRONMENT: yield ".TP\n" yield ".I \"%s\"\n" % k yield man_escape(desc) + "\n" man_preamble = """\ .\\\"Man page for Juju (%(cmd)s) .\\\" .\\\" Large parts of this file are autogenerated from the output of .\\\" \"%(cmd)s help commands\" .\\\" \"%(cmd)s help <cmd>\" .\\\" .\\\" Generation time: %(timestamp)s .\\\" .ie \\n(.g .ds Aq \\(aq .el .ds Aq ' """ man_head = """\ .TH %(cmd)s 1 "%(datestamp)s" "%(version)s" "Juju" .SH "NAME" %(cmd)s - Juju -- devops distilled .SH "SYNOPSIS" .B "%(cmd)s" .I "command" [ .I "command_options" ] .br .B "%(cmd)s" .B "help" .br .B "%(cmd)s" .B "help" .I "command" .SH "DESCRIPTION" Juju provides easy, intelligent service orchestration on top of environments such as OpenStack, Amazon AWS, or bare metal. """ man_foot = """\ .SH "FILES" .TP .I "~/.juju/environments.yaml" This is the Juju config file, which you can use to specify multiple environments in which to deploy. A config file can be created using .B juju init which you can then edit to provide the secret keys, or use environment variables to provide the secret values. .SH "SEE ALSO" .UR https://juju.ubuntu.com/ .BR https://juju.ubuntu.com/ """ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/release-public-tools/��������������������������0000755�0000153�0000161�00000000000�12321735643�026407� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/scripts/release-public-tools/release-public-tools.sh���0000644�0000153�0000161�00000016340�12321735642�033000� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env bash # Release public tools. # # Retrieve the published juju-core debs for a specific release. # Extract the jujud from the packages. # Generate the streams data. # Publish to Canonistack, HP, AWS, and Azure. # # This script requires that the user has credentials to upload the tools # to Canonistack, HP Cloud, AWS, and Azure set -e usage() { echo usage: $0 RELEASE destination-directory exit 1 } check_deps() { has_deps=1 which lftp || has_deps=0 which swift || has_deps=0 which s3cmd || has_deps=0 test -f ~/.juju/canonistacktoolsrc || has_deps=0 test -f ~/.juju/hptoolsrc || has_deps=0 test -f ~/.juju/awstoolsrc || has_deps=0 test -f ~/.juju/azuretoolsrc || has_deps=0 if [[ $has_deps == 0 ]]; then echo "Install lftp, python-swiftclient, and s3cmd" echo "Your ~/.juju dir must contain rc files to publish:" echo " canonistacktoolsrc, hptoolsrc, awstoolsrc, azuretoolsrc" exit 2 fi } build_tool_tree() { if [[ ! -d $DEST_DEBS ]]; then mkdir $DEST_DEBS fi if [[ ! -d $DEST_TOOLS ]]; then mkdir -p $DEST_TOOLS fi if [[ ! -d $DEST_DIST ]]; then mkdir $DEST_DIST fi } retrieve_released_tools() { # Retrieve previously released tools to ensure the metadata continues # to work for historic releases. source ~/.juju/awstoolsrc s3cmd sync s3://juju-dist/tools/releases/ $DEST_TOOLS } retrieve_packages() { # Retrieve the $RELEASE packages that contain jujud. cd $DEST_DEBS for archive in $UBUNTU_ARCH $STABLE_ARCH $DEVEL_ARCH; do echo "checking $archive for $RELEASE." lftp -c mirror -I "juju-core_${RELEASE}*.deb" $archive; done mv juju-core/*deb ./ rm -r juju-core } get_version() { # Defines $version. $version can be different than $RELEASE used to # match the packages in the archives. control_version=$1 version=$(echo "$control_version" | sed -n 's/^\([0-9]\+\).\([0-9]\+\).\([0-9]\+\)-[0-9].*/\1.\2.\3/p') if [ "${version}" == "" ] ; then echo "Invalid version: $control_version" exit 3 fi } get_series() { # Defines $series. control_version=$1 pkg_series=$(echo "$control_version" | sed -e 's/~juju.//;' \ -e 's/^.*~\(ubuntu[0-9][0-9]\.[0-9][0-9]\|[a-z]\+\).*/\1/') series=${version_names["$pkg_series"]} case "${series}" in "precise" | "quantal" | "raring" | "saucy" ) ;; *) echo "Invalid series: $control_version" exit 3 ;; esac } get_arch() { # Defines $arch. control_file=$1 arch=$(sed -n 's/^Architecture: \([a-z]\+\)/\1/p' $control_file) case "${arch}" in "amd64" | "i386" | "armel" | "armhf" | "arm64" | "ppc64el" ) ;; *) echo "Invalid arch: $arch" exit 3 ;; esac } archive_tools() { # Builds the jujud tgz for each series and arch. cd $DESTINATION WORK=$(mktemp -d) mkdir ${WORK}/juju packages=$(ls ${DEST_DEBS}/*.deb) for package in $packages; do echo "Extracting jujud from ${package}." dpkg-deb -e $package ${WORK}/juju control_file="${WORK}/juju/control" control_version=$(sed -n 's/^Version: \(.*\)/\1/p' $control_file) get_version $control_version get_series $control_version get_arch $control_file tool="${DEST_TOOLS}/juju-${version}-${series}-${arch}.tgz" echo "Creating $tool." dpkg-deb -x $package ${WORK}/juju bin_dir="${WORK}/juju/usr/bin" lib_dir="${WORK}/juju/usr/lib/juju-${version}/bin" if [[ -f "${bin_dir}/jujud" ]]; then change_dir=$bin_dir elif [[ -f "${lib_dir}/jujud" ]]; then change_dir=$lib_dir else echo "jujud is not in /usr/bin or /usr/lib" exit 4 fi tar cvfz $tool -C $change_dir jujud echo "Created ${tool}." rm -r ${WORK}/juju/* done } generate_streams() { # Create the streams metadata and organised the tree for later publication. cd $DESTINATION ${GOPATH}/bin/juju sync-tools --all --dev \ --source=${DESTINATION} --destination=${DEST_DIST} # Support old tools location so that deployments can upgrade to new tools. cp ${DEST_DIST}/tools/releases/*tgz ${DEST_DIST}/tools echo "The tools are in ${DEST_DIST}." } publish_to_canonistack() { echo "Phase 6.1: Publish to canonistack." cd $DESTINATION source ~/.juju/canonistacktoolsrc ${GOPATH}/bin/juju --show-log \ sync-tools -e public-tools-canonistack --dev --source=${DEST_DIST} # This needed to allow old deployments upgrade. cd ${DEST_DIST} swift upload juju-dist tools/*.tgz } publish_to_hp() { echo "Phase 6.2: Publish to HP Cloud." cd $DESTINATION source ~/.juju/hptoolsrc ${GOPATH}/bin/juju --show-log \ sync-tools -e public-tools-hp --dev --source=${DEST_DIST} # Support old tools location so that deployments can upgrade to new tools. cd ${DEST_DIST} swift upload juju-dist tools/*.tgz } publish_to_aws() { echo "Phase 6.3: Publish to AWS." cd $DESTINATION source ~/.juju/awstoolsrc s3cmd sync ${DEST_DIST}/tools s3://juju-dist/ } publish_to_azure() { # This command sets the tool name from the local path! The local path for # each public file MUST match the destination path :(. echo "Phase 6.4: Publish to Azure." cd $DESTINATION source ~/.juju/azuretoolsrc cd ${DEST_DIST} public_files=$(find tools -name *.tgz -o -name *.json) for public_file in $public_files; do echo "Uploading $public_file to Azure West US." go run $GOPATH/src/launchpad.net/gwacl/example/storage/run.go \ -account=${AZURE_ACCOUNT} -container=juju-tools \ -location="West US" \ -key=${AZURE_JUJU_TOOLS_KEY} \ -filename=$public_file \ addblock done } # These are the archives that are search for matching releases. UBUNTU_ARCH="http://archive.ubuntu.com/ubuntu/pool/universe/j/juju-core/" STABLE_ARCH="http://ppa.launchpad.net/juju/stable/ubuntu/pool/main/j/juju-core/" DEVEL_ARCH="http://ppa.launchpad.net/juju/devel/ubuntu/pool/main/j/juju-core/" # Series names found in package versions need to be normalised. declare -A version_names version_names+=(["ubuntu12.04"]="precise") version_names+=(["ubuntu12.10"]="quantal") version_names+=(["ubuntu13.04"]="raring") version_names+=(["ubuntu13.10"]="saucy") version_names+=(["precise"]="precise") version_names+=(["quantal"]="quantal") version_names+=(["raring"]="raring") version_names+=(["saucy"]="saucy") test $# -eq 2 || usage RELEASE=$1 DESTINATION=$(cd $2; pwd) DEST_DEBS="${DESTINATION}/debs" DEST_TOOLS="${DESTINATION}/tools/releases" DEST_DIST="${DESTINATION}/juju-dist" echo "Phase 0: Checking requirements." check_deps echo "Phase 1: Building collection and republication tree." build_tool_tree echo "Phase 2: Retrieving released tools." retrieve_released_tools echo "Phase 3: Retrieving juju-core packages from archives" retrieve_packages echo "Phase 4: Extracting jujud from packages and archiving tools." archive_tools echo "Phase 5: Generating streams data." generate_streams echo "Phase 6: Publishing tools." publish_to_canonistack publish_to_hp publish_to_aws publish_to_azure ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/schema/������������������������������������������������0000755�0000153�0000161�00000000000�12321735642�022125� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/schema/schema_test.go����������������������������������0000644�0000153�0000161�00000030741�12321735642�024760� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package schema_test import ( "math" "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/schema" ) func Test(t *testing.T) { gc.TestingT(t) } type S struct{} var _ = gc.Suite(&S{}) type Dummy struct{} func (d *Dummy) Coerce(value interface{}, path []string) (coerced interface{}, err error) { return "i-am-dummy", nil } var aPath = []string{"<pa", "th>"} func (s *S) TestConst(c *gc.C) { sch := schema.Const("foo") out, err := sch.Coerce("foo", aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, "foo") out, err = sch.Coerce(42, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected "foo", got int\(42\)`) out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected "foo", got nothing`) } func (s *S) TestAny(c *gc.C) { sch := schema.Any() out, err := sch.Coerce("foo", aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, "foo") out, err = sch.Coerce(nil, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, nil) } func (s *S) TestOneOf(c *gc.C) { sch := schema.OneOf(schema.Const("foo"), schema.Const(42)) out, err := sch.Coerce("foo", aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, "foo") out, err = sch.Coerce(42, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, 42) out, err = sch.Coerce("bar", aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: unexpected value "bar"`) } func (s *S) TestBool(c *gc.C) { sch := schema.Bool() for _, trueValue := range []interface{}{true, "1", "true", "True", "TRUE"} { out, err := sch.Coerce(trueValue, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, true) } for _, falseValue := range []interface{}{false, "0", "false", "False", "FALSE"} { out, err := sch.Coerce(falseValue, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, false) } out, err := sch.Coerce(42, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected bool, got int\(42\)`) out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected bool, got nothing") } func (s *S) TestInt(c *gc.C) { sch := schema.Int() out, err := sch.Coerce(42, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, int64(42)) out, err = sch.Coerce(int8(42), aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, int64(42)) out, err = sch.Coerce("42", aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, int64(42)) out, err = sch.Coerce(true, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected int, got bool\(true\)`) out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected int, got nothing") } func (s *S) TestForceInt(c *gc.C) { sch := schema.ForceInt() out, err := sch.Coerce(42, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, int(42)) out, err = sch.Coerce("42", aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, int(42)) out, err = sch.Coerce("42.66", aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, int(42)) out, err = sch.Coerce(int8(42), aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, int(42)) out, err = sch.Coerce(float32(42), aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, int(42)) out, err = sch.Coerce(float64(42), aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, int(42)) out, err = sch.Coerce(42.66, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, int(42)) // If an out of range value is provided, that value is truncated, // generating unexpected results, but no error is raised. out, err = sch.Coerce(float64(math.MaxInt64+1), aPath) c.Assert(err, gc.IsNil) out, err = sch.Coerce(true, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected number, got bool\(true\)`) out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected number, got nothing") } func (s *S) TestFloat(c *gc.C) { sch := schema.Float() out, err := sch.Coerce(float32(1.0), aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, float64(1.0)) out, err = sch.Coerce(float64(1.0), aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, float64(1.0)) out, err = sch.Coerce(true, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected float, got bool\(true\)`) out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected float, got nothing") } func (s *S) TestString(c *gc.C) { sch := schema.String() out, err := sch.Coerce("foo", aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, "foo") out, err = sch.Coerce(true, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected string, got bool\(true\)`) out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected string, got nothing") } func (s *S) TestSimpleRegexp(c *gc.C) { sch := schema.SimpleRegexp() out, err := sch.Coerce("[0-9]+", aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.Equals, "[0-9]+") out, err = sch.Coerce(1, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected regexp string, got int\(1\)`) out, err = sch.Coerce("[", aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected valid regexp, got string\("\["\)`) out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected regexp string, got nothing`) } func (s *S) TestList(c *gc.C) { sch := schema.List(schema.Int()) out, err := sch.Coerce([]int8{1, 2}, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, []interface{}{int64(1), int64(2)}) out, err = sch.Coerce(42, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected list, got int\\(42\\)") out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected list, got nothing") out, err = sch.Coerce([]interface{}{1, true}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>\[1\]: expected int, got bool\(true\)`) } func (s *S) TestMap(c *gc.C) { sch := schema.Map(schema.String(), schema.Int()) out, err := sch.Coerce(map[string]interface{}{"a": 1, "b": int8(2)}, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, map[interface{}]interface{}{"a": int64(1), "b": int64(2)}) out, err = sch.Coerce(42, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected map, got int\\(42\\)") out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected map, got nothing") out, err = sch.Coerce(map[int]int{1: 1}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected string, got int\\(1\\)") out, err = sch.Coerce(map[string]bool{"a": true}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>\.a: expected int, got bool\(true\)`) // First path entry shouldn't have dots in an error message. out, err = sch.Coerce(map[string]bool{"a": true}, nil) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `a: expected int, got bool\(true\)`) } func (s *S) TestStringMap(c *gc.C) { sch := schema.StringMap(schema.Int()) out, err := sch.Coerce(map[string]interface{}{"a": 1, "b": int8(2)}, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, map[string]interface{}{"a": int64(1), "b": int64(2)}) out, err = sch.Coerce(42, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected map, got int\\(42\\)") out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected map, got nothing") out, err = sch.Coerce(map[int]int{1: 1}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected string, got int\\(1\\)") out, err = sch.Coerce(map[string]bool{"a": true}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>\.a: expected int, got bool\(true\)`) // First path entry shouldn't have dots in an error message. out, err = sch.Coerce(map[string]bool{"a": true}, nil) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `a: expected int, got bool\(true\)`) } func assertFieldMap(c *gc.C, sch schema.Checker) { out, err := sch.Coerce(map[string]interface{}{"a": "A", "b": "B"}, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, map[string]interface{}{"a": "A", "b": "B", "c": "C"}) out, err = sch.Coerce(42, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected map, got int\\(42\\)") out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, "<path>: expected map, got nothing") out, err = sch.Coerce(map[string]interface{}{"a": "A", "b": "C"}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>\.b: expected "B", got string\("C"\)`) out, err = sch.Coerce(map[string]interface{}{"b": "B"}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>\.a: expected "A", got nothing`) // b is optional out, err = sch.Coerce(map[string]interface{}{"a": "A"}, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, map[string]interface{}{"a": "A", "c": "C"}) // First path entry shouldn't have dots in an error message. out, err = sch.Coerce(map[string]bool{"a": true}, nil) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `a: expected "A", got bool\(true\)`) } func (s *S) TestFieldMap(c *gc.C) { fields := schema.Fields{ "a": schema.Const("A"), "b": schema.Const("B"), "c": schema.Const("C"), } defaults := schema.Defaults{ "b": schema.Omit, "c": "C", } sch := schema.FieldMap(fields, defaults) assertFieldMap(c, sch) out, err := sch.Coerce(map[string]interface{}{"a": "A", "b": "B", "d": "D"}, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, map[string]interface{}{"a": "A", "b": "B", "c": "C"}) out, err = sch.Coerce(map[string]interface{}{"a": "A", "d": "D"}, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, map[string]interface{}{"a": "A", "c": "C"}) } func (s *S) TestFieldMapDefaultInvalid(c *gc.C) { fields := schema.Fields{ "a": schema.Const("A"), } defaults := schema.Defaults{ "a": "B", } sch := schema.FieldMap(fields, defaults) _, err := sch.Coerce(map[string]interface{}{}, aPath) c.Assert(err, gc.ErrorMatches, `<path>.a: expected "A", got string\("B"\)`) } func (s *S) TestStrictFieldMap(c *gc.C) { fields := schema.Fields{ "a": schema.Const("A"), "b": schema.Const("B"), "c": schema.Const("C"), } defaults := schema.Defaults{ "b": schema.Omit, "c": "C", } sch := schema.StrictFieldMap(fields, defaults) assertFieldMap(c, sch) out, err := sch.Coerce(map[string]interface{}{"a": "A", "b": "B", "d": "D"}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: unknown key "d" \(value "D"\)`) } func (s *S) TestSchemaMap(c *gc.C) { fields1 := schema.FieldMap(schema.Fields{ "type": schema.Const(1), "a": schema.Const(2), }, nil) fields2 := schema.FieldMap(schema.Fields{ "type": schema.Const(3), "b": schema.Const(4), }, nil) sch := schema.FieldMapSet("type", []schema.Checker{fields1, fields2}) out, err := sch.Coerce(map[string]int{"type": 1, "a": 2}, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, map[string]interface{}{"type": 1, "a": 2}) out, err = sch.Coerce(map[string]int{"type": 3, "b": 4}, aPath) c.Assert(err, gc.IsNil) c.Assert(out, gc.DeepEquals, map[string]interface{}{"type": 3, "b": 4}) out, err = sch.Coerce(map[string]int{}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>\.type: expected supported selector, got nothing`) out, err = sch.Coerce(map[string]int{"type": 2}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>\.type: expected supported selector, got int\(2\)`) out, err = sch.Coerce(map[string]int{"type": 3, "b": 5}, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>\.b: expected 4, got int\(5\)`) out, err = sch.Coerce(42, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected map, got int\(42\)`) out, err = sch.Coerce(nil, aPath) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `<path>: expected map, got nothing`) // First path entry shouldn't have dots in an error message. out, err = sch.Coerce(map[string]int{"a": 1}, nil) c.Assert(out, gc.IsNil) c.Assert(err, gc.ErrorMatches, `type: expected supported selector, got nothing`) } �������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/schema/schema.go���������������������������������������0000644�0000153�0000161�00000032155�12321735642�023722� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package schema import ( "fmt" "reflect" "regexp" "strconv" "strings" ) // The Coerce method of the Checker interface is called recursively when // v is being validated. If err is nil, newv is used as the new value // at the recursion point. If err is non-nil, v is taken as invalid and // may be either ignored or error out depending on where in the schema // checking process the error happened. Checkers like OneOf may continue // with an alternative, for instance. type Checker interface { Coerce(v interface{}, path []string) (newv interface{}, err error) } type error_ struct { want string got interface{} path []string } // pathAsString returns a string consisting of the path elements. If path // starts with a ".", the dot is omitted. func pathAsString(path []string) string { if path[0] == "." { return strings.Join(path[1:], "") } else { return strings.Join(path, "") } } func (e error_) Error() string { path := pathAsString(e.path) if e.want == "" { return fmt.Sprintf("%s: unexpected value %#v", path, e.got) } if e.got == nil { return fmt.Sprintf("%s: expected %s, got nothing", path, e.want) } return fmt.Sprintf("%s: expected %s, got %T(%#v)", path, e.want, e.got, e.got) } // Any returns a Checker that succeeds with any input value and // results in the value itself unprocessed. func Any() Checker { return anyC{} } type anyC struct{} func (c anyC) Coerce(v interface{}, path []string) (interface{}, error) { return v, nil } // Const returns a Checker that only succeeds if the input matches // value exactly. The value is compared with reflect.DeepEqual. func Const(value interface{}) Checker { return constC{value} } type constC struct { value interface{} } func (c constC) Coerce(v interface{}, path []string) (interface{}, error) { if reflect.DeepEqual(v, c.value) { return v, nil } return nil, error_{fmt.Sprintf("%#v", c.value), v, path} } // OneOf returns a Checker that attempts to Coerce the value with each // of the provided checkers. The value returned by the first checker // that succeeds will be returned by the OneOf checker itself. If no // checker succeeds, OneOf will return an error on coercion. func OneOf(options ...Checker) Checker { return oneOfC{options} } type oneOfC struct { options []Checker } func (c oneOfC) Coerce(v interface{}, path []string) (interface{}, error) { for _, o := range c.options { newv, err := o.Coerce(v, path) if err == nil { return newv, nil } } return nil, error_{"", v, path} } // Bool returns a Checker that accepts boolean values only. func Bool() Checker { return boolC{} } type boolC struct{} func (c boolC) Coerce(v interface{}, path []string) (interface{}, error) { if v != nil { switch reflect.TypeOf(v).Kind() { case reflect.Bool: return v, nil case reflect.String: val, err := strconv.ParseBool(reflect.ValueOf(v).String()) if err == nil { return val, nil } } } return nil, error_{"bool", v, path} } // Int returns a Checker that accepts any integer value, and returns // the same value consistently typed as an int64. func Int() Checker { return intC{} } type intC struct{} func (c intC) Coerce(v interface{}, path []string) (interface{}, error) { if v == nil { return nil, error_{"int", v, path} } switch reflect.TypeOf(v).Kind() { case reflect.Int: case reflect.Int8: case reflect.Int16: case reflect.Int32: case reflect.Int64: case reflect.String: val, err := strconv.ParseInt(reflect.ValueOf(v).String(), 0, 64) if err == nil { return val, nil } else { return nil, error_{"int", v, path} } default: return nil, error_{"int", v, path} } return reflect.ValueOf(v).Int(), nil } // ForceInt returns a Checker that accepts any integer or float value, and // returns the same value consistently typed as an int. This is required // in order to handle the interface{}/float64 type conversion performed by // the JSON serializer used as part of the API infrastructure. func ForceInt() Checker { return forceIntC{} } type forceIntC struct{} func (c forceIntC) Coerce(v interface{}, path []string) (interface{}, error) { if v != nil { switch vv := reflect.TypeOf(v); vv.Kind() { case reflect.String: vstr := reflect.ValueOf(v).String() intValue, err := strconv.ParseInt(vstr, 0, 64) if err == nil { return int(intValue), nil } floatValue, err := strconv.ParseFloat(vstr, 64) if err == nil { return int(floatValue), nil } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return int(reflect.ValueOf(v).Int()), nil case reflect.Float32, reflect.Float64: return int(reflect.ValueOf(v).Float()), nil } } return nil, error_{"number", v, path} } // Float returns a Checker that accepts any float value, and returns // the same value consistently typed as a float64. func Float() Checker { return floatC{} } type floatC struct{} func (c floatC) Coerce(v interface{}, path []string) (interface{}, error) { if v == nil { return nil, error_{"float", v, path} } switch reflect.TypeOf(v).Kind() { case reflect.Float32: case reflect.Float64: default: return nil, error_{"float", v, path} } return reflect.ValueOf(v).Float(), nil } // String returns a Checker that accepts a string value only and returns // it unprocessed. func String() Checker { return stringC{} } type stringC struct{} func (c stringC) Coerce(v interface{}, path []string) (interface{}, error) { if v != nil && reflect.TypeOf(v).Kind() == reflect.String { return reflect.ValueOf(v).String(), nil } return nil, error_{"string", v, path} } func SimpleRegexp() Checker { return sregexpC{} } type sregexpC struct{} func (c sregexpC) Coerce(v interface{}, path []string) (interface{}, error) { // XXX The regexp package happens to be extremely simple right now. // Once exp/regexp goes mainstream, we'll have to update this // logic to use a more widely accepted regexp subset. if v != nil && reflect.TypeOf(v).Kind() == reflect.String { s := reflect.ValueOf(v).String() _, err := regexp.Compile(s) if err != nil { return nil, error_{"valid regexp", s, path} } return v, nil } return nil, error_{"regexp string", v, path} } // List returns a Checker that accepts a slice value with values // that are processed with the elem checker. If any element of the // provided slice value fails to be processed, processing will stop // and return with the obtained error. // // The coerced output value has type []interface{}. func List(elem Checker) Checker { return listC{elem} } type listC struct { elem Checker } func (c listC) Coerce(v interface{}, path []string) (interface{}, error) { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Slice { return nil, error_{"list", v, path} } path = append(path, "[", "?", "]") l := rv.Len() out := make([]interface{}, 0, l) for i := 0; i != l; i++ { path[len(path)-2] = strconv.Itoa(i) elem, err := c.elem.Coerce(rv.Index(i).Interface(), path) if err != nil { return nil, err } out = append(out, elem) } return out, nil } // Map returns a Checker that accepts a map value. Every key and value // in the map are processed with the respective checker, and if any // value fails to be coerced, processing stops and returns with the // underlying error. // // The coerced output value has type map[interface{}]interface{}. func Map(key Checker, value Checker) Checker { return mapC{key, value} } type mapC struct { key Checker value Checker } func (c mapC) Coerce(v interface{}, path []string) (interface{}, error) { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Map { return nil, error_{"map", v, path} } vpath := append(path, ".", "?") l := rv.Len() out := make(map[interface{}]interface{}, l) keys := rv.MapKeys() for i := 0; i != l; i++ { k := keys[i] newk, err := c.key.Coerce(k.Interface(), path) if err != nil { return nil, err } vpath[len(vpath)-1] = fmt.Sprint(k.Interface()) newv, err := c.value.Coerce(rv.MapIndex(k).Interface(), vpath) if err != nil { return nil, err } out[newk] = newv } return out, nil } // StringMap returns a Checker that accepts a map value. Every key in // the map must be a string, and every value in the map are processed // with the provided checker. If any value fails to be coerced, // processing stops and returns with the underlying error. // // The coerced output value has type map[string]interface{}. func StringMap(value Checker) Checker { return stringMapC{value} } type stringMapC struct { value Checker } func (c stringMapC) Coerce(v interface{}, path []string) (interface{}, error) { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Map { return nil, error_{"map", v, path} } vpath := append(path, ".", "?") key := String() l := rv.Len() out := make(map[string]interface{}, l) keys := rv.MapKeys() for i := 0; i != l; i++ { k := keys[i] newk, err := key.Coerce(k.Interface(), path) if err != nil { return nil, err } vpath[len(vpath)-1] = fmt.Sprint(k.Interface()) newv, err := c.value.Coerce(rv.MapIndex(k).Interface(), vpath) if err != nil { return nil, err } out[newk.(string)] = newv } return out, nil } // Omit is a marker for FieldMap and StructFieldMap defaults parameter. // If a field is not present in the map and defaults to Omit, the missing // field will be ommitted from the coerced map as well. var Omit omit type omit struct{} type Fields map[string]Checker type Defaults map[string]interface{} // FieldMap returns a Checker that accepts a map value with defined // string keys. Every key has an independent checker associated, // and processing will only succeed if all the values succeed // individually. If a field fails to be processed, processing stops // and returns with the underlying error. // // Fields in defaults will be set to the provided value if not present // in the coerced map. If the default value is schema.Omit, the // missing field will be omitted from the coerced map. // // The coerced output value has type map[string]interface{}. func FieldMap(fields Fields, defaults Defaults) Checker { return fieldMapC{fields, defaults, false} } // StrictFieldMap returns a Checker that acts as the one returned by FieldMap, // but the Checker returns an error if it encounters an unknown key. func StrictFieldMap(fields Fields, defaults Defaults) Checker { return fieldMapC{fields, defaults, true} } type fieldMapC struct { fields Fields defaults Defaults strict bool } func (c fieldMapC) Coerce(v interface{}, path []string) (interface{}, error) { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Map { return nil, error_{"map", v, path} } if c.strict { for _, k := range rv.MapKeys() { ks := k.String() if _, found := c.fields[ks]; !found { value := interface{}("invalid") valuev := rv.MapIndex(k) if valuev.IsValid() { value = valuev.Interface() } return nil, fmt.Errorf("%v: unknown key %q (value %q)", pathAsString(path), ks, value) } } } vpath := append(path, ".", "?") l := rv.Len() out := make(map[string]interface{}, l) for k, checker := range c.fields { valuev := rv.MapIndex(reflect.ValueOf(k)) var value interface{} if valuev.IsValid() { value = valuev.Interface() } else if dflt, ok := c.defaults[k]; ok { if dflt == Omit { continue } value = dflt } vpath[len(vpath)-1] = k newv, err := checker.Coerce(value, vpath) if err != nil { return nil, err } out[k] = newv } for k, v := range c.defaults { if v == Omit { continue } if _, ok := out[k]; !ok { checker, ok := c.fields[k] if !ok { return nil, fmt.Errorf("got default value for unknown field %q", k) } vpath[len(vpath)-1] = k newv, err := checker.Coerce(v, vpath) if err != nil { return nil, err } out[k] = newv } } return out, nil } // FieldMapSet returns a Checker that accepts a map value checked // against one of several FieldMap checkers. The actual checker // used is the first one whose checker associated with the selector // field processes the map correctly. If no checker processes // the selector value correctly, an error is returned. // // The coerced output value has type map[string]interface{}. func FieldMapSet(selector string, maps []Checker) Checker { fmaps := make([]fieldMapC, len(maps)) for i, m := range maps { if fmap, ok := m.(fieldMapC); ok { if checker, _ := fmap.fields[selector]; checker == nil { panic("FieldMapSet has a FieldMap with a missing selector") } fmaps[i] = fmap } else { panic("FieldMapSet got a non-FieldMap checker") } } return mapSetC{selector, fmaps} } type mapSetC struct { selector string fmaps []fieldMapC } func (c mapSetC) Coerce(v interface{}, path []string) (interface{}, error) { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Map { return nil, error_{"map", v, path} } var selector interface{} selectorv := rv.MapIndex(reflect.ValueOf(c.selector)) if selectorv.IsValid() { selector = selectorv.Interface() for _, fmap := range c.fmaps { _, err := fmap.fields[c.selector].Coerce(selector, path) if err != nil { continue } return fmap.Coerce(v, path) } } return nil, error_{"supported selector", selector, append(path, ".", c.selector)} } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/����������������������������������������������0000755�0000153�0000161�00000000000�12321735642�022517� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/provider.go�����������������������������������0000644�0000153�0000161�00000001065�12321735642�024702� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The provider package holds constants identifying known provider types. // They have hitherto only been used for nefarious purposes; no new code // should use them, and when old code is updated to no longer use them // they must be deleted. package provider const ( Local = "local" ) // IsManual returns true iff the specified provider // type refers to the manual provider. func IsManual(provider string) bool { return provider == "null" || provider == "manual" } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/������������������������������������������0000755�0000153�0000161�00000000000�12321736000�023155� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/instancetype.go���������������������������0000644�0000153�0000161�00000015746�12321735642�026242� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2 import ( "launchpad.net/goamz/aws" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/juju/arch" ) // Type of virtualisation used. var ( paravirtual = "pv" hvm = "hvm" ) // all instance types can run amd64 images, and some can also run i386 ones. var ( amd64 = []string{arch.AMD64} both = []string{arch.AMD64, arch.I386} ) // allRegions is defined here to allow tests to override the content. var allRegions = aws.Regions // allInstanceTypes holds the relevant attributes of every known // instance type. // Note that while the EC2 root disk default is 8G, constraints on disk // for amazon will simply cause the root disk to grow to match the constraint var allInstanceTypes = []instances.InstanceType{ { // First generation. Name: "m1.small", Arches: both, CpuCores: 1, CpuPower: instances.CpuPower(100), Mem: 1740, VirtType: &paravirtual, }, { Name: "m1.medium", Arches: both, CpuCores: 1, CpuPower: instances.CpuPower(200), Mem: 3840, VirtType: &paravirtual, }, { Name: "m1.large", Arches: amd64, CpuCores: 2, CpuPower: instances.CpuPower(400), Mem: 7680, VirtType: &paravirtual, }, { Name: "m1.xlarge", Arches: amd64, CpuCores: 4, CpuPower: instances.CpuPower(800), Mem: 15360, VirtType: &paravirtual, }, { // Second generation. Name: "m3.medium", Arches: amd64, CpuCores: 1, CpuPower: instances.CpuPower(300), Mem: 3840, VirtType: &paravirtual, }, { Name: "m3.large", Arches: amd64, CpuCores: 2, CpuPower: instances.CpuPower(6500), Mem: 7680, VirtType: &paravirtual, }, { Name: "m3.xlarge", Arches: amd64, CpuCores: 4, CpuPower: instances.CpuPower(1300), Mem: 15360, VirtType: &paravirtual, }, { Name: "m3.2xlarge", Arches: amd64, CpuCores: 8, CpuPower: instances.CpuPower(2600), Mem: 30720, VirtType: &paravirtual, }, { // Micro. Name: "t1.micro", Arches: both, CpuCores: 1, CpuPower: instances.CpuPower(20), Mem: 613, VirtType: &paravirtual, }, { // High-Memory. Name: "m2.xlarge", Arches: amd64, CpuCores: 2, CpuPower: instances.CpuPower(650), Mem: 17408, VirtType: &paravirtual, }, { Name: "m2.2xlarge", Arches: amd64, CpuCores: 4, CpuPower: instances.CpuPower(1300), Mem: 34816, VirtType: &paravirtual, }, { Name: "m2.4xlarge", Arches: amd64, CpuCores: 8, CpuPower: instances.CpuPower(2600), Mem: 69632, VirtType: &paravirtual, }, { // High-CPU. Name: "c1.medium", Arches: both, CpuCores: 2, CpuPower: instances.CpuPower(500), Mem: 1740, VirtType: &paravirtual, }, { Name: "c1.xlarge", Arches: amd64, CpuCores: 8, CpuPower: instances.CpuPower(2000), Mem: 7168, VirtType: &paravirtual, }, { // Cluster compute. Name: "cc1.4xlarge", Arches: amd64, CpuCores: 8, CpuPower: instances.CpuPower(3350), Mem: 23552, VirtType: &hvm, }, { Name: "cc2.8xlarge", Arches: amd64, CpuCores: 16, CpuPower: instances.CpuPower(8800), Mem: 61952, VirtType: &hvm, }, { // High Memory cluster. Name: "cr1.8xlarge", Arches: amd64, CpuCores: 16, CpuPower: instances.CpuPower(8800), Mem: 249856, VirtType: &hvm, }, { // Cluster GPU. Name: "cg1.4xlarge", Arches: amd64, CpuCores: 8, CpuPower: instances.CpuPower(3350), Mem: 22528, VirtType: &hvm, }, { // High I/O. Name: "hi1.4xlarge", Arches: amd64, CpuCores: 16, CpuPower: instances.CpuPower(3500), Mem: 61952, VirtType: &paravirtual, }, { // High storage. Name: "hs1.8xlarge", Arches: amd64, CpuCores: 16, CpuPower: instances.CpuPower(3500), Mem: 119808, VirtType: &paravirtual, }, } type instanceTypeCost map[string]uint64 type regionCosts map[string]instanceTypeCost // allRegionCosts holds the cost in USDe-3/hour for each available instance // type in each region. var allRegionCosts = regionCosts{ "ap-northeast-1": { // Tokyo. "m1.small": 88, "m1.medium": 175, "m1.large": 350, "m1.xlarge": 700, "m3.medium": 171, "m3.large": 342, "m3.xlarge": 684, "m3.2xlarge": 1368, "t1.micro": 27, "m2.xlarge": 505, "m2.2xlarge": 1010, "m2.4xlarge": 2020, "c1.medium": 185, "c1.xlarge": 740, }, "ap-southeast-1": { // Singapore. "m1.small": 80, "m1.medium": 160, "m1.large": 320, "m1.xlarge": 640, "m3.medium": 158, "m3.large": 315, "m3.xlarge": 730, "m3.2xlarge": 1260, "t1.micro": 20, "m2.xlarge": 495, "m2.2xlarge": 990, "m2.4xlarge": 1980, "c1.medium": 183, "c1.xlarge": 730, }, "ap-southeast-2": { // Sydney. "m1.small": 80, "m1.medium": 160, "m1.large": 320, "m1.xlarge": 640, "m3.medium": 158, "m3.large": 315, "m3.xlarge": 730, "m3.2xlarge": 1260, "t1.micro": 20, "m2.xlarge": 495, "m2.2xlarge": 990, "m2.4xlarge": 1980, "c1.medium": 183, "c1.xlarge": 730, }, "eu-west-1": { // Ireland. "m1.small": 65, "m1.medium": 130, "m1.large": 260, "m1.xlarge": 520, "m3.medium": 124, "m3.large": 248, "m3.xlarge": 495, "m3.2xlarge": 990, "t1.micro": 20, "m2.xlarge": 460, "m2.2xlarge": 920, "m2.4xlarge": 1840, "c1.medium": 165, "c1.xlarge": 660, "cc2.8xlarge": 2700, "cg1.4xlarge": 2360, "hi1.4xlarge": 3410, }, "sa-east-1": { // Sao Paulo. "m1.small": 80, "m1.medium": 160, "m1.large": 320, "m1.xlarge": 640, "t1.micro": 27, "m3.medium": 153, "m3.large": 306, "m3.xlarge": 612, "m3.2xlarge": 1224, "m2.xlarge": 540, "m2.2xlarge": 1080, "m2.4xlarge": 2160, "c1.medium": 200, "c1.xlarge": 800, }, "us-east-1": { // Northern Virginia. "m1.small": 60, "m1.medium": 120, "m1.large": 240, "m1.xlarge": 480, "m3.medium": 113, "m3.large": 225, "m3.xlarge": 450, "m3.2xlarge": 900, "t1.micro": 20, "m2.xlarge": 410, "m2.2xlarge": 820, "m2.4xlarge": 1640, "c1.medium": 145, "c1.xlarge": 580, "cc1.4xlarge": 1300, "cc2.8xlarge": 2400, "cr1.8xlarge": 3500, "cg1.4xlarge": 2100, "hi1.4xlarge": 3100, "hs1.8xlarge": 4600, }, "us-west-1": { // Northern California. "m1.small": 65, "m1.medium": 130, "m1.large": 260, "m1.xlarge": 520, "m3.medium": 124, "m3.large": 248, "m3.xlarge": 495, "m3.2xlarge": 990, "t1.micro": 25, "m2.xlarge": 460, "m2.2xlarge": 920, "m2.4xlarge": 1840, "c1.medium": 165, "c1.xlarge": 660, }, "us-west-2": { // Oregon. "m1.small": 60, "m1.medium": 120, "m1.large": 240, "m1.xlarge": 480, "m3.medium": 113, "m3.large": 225, "m3.xlarge": 450, "m3.2xlarge": 900, "t1.micro": 20, "m2.xlarge": 410, "m2.2xlarge": 820, "m2.4xlarge": 1640, "c1.medium": 145, "c1.xlarge": 580, "cc2.8xlarge": 2400, "cr1.8xlarge": 3500, "hi1.4xlarge": 3100, }, } ��������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/export_test.go����������������������������0000644�0000153�0000161�00000021036�12321735642�026101� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2 import ( "io" "launchpad.net/goamz/aws" "launchpad.net/goamz/ec2" "launchpad.net/goamz/s3" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/instance" ) func ControlBucketName(e environs.Environ) string { return e.(*environ).ecfg().controlBucket() } func JujuGroupName(e environs.Environ) string { return e.(*environ).jujuGroupName() } func MachineGroupName(e environs.Environ, machineId string) string { return e.(*environ).machineGroupName(machineId) } func EnvironEC2(e environs.Environ) *ec2.EC2 { return e.(*environ).ec2() } func EnvironS3(e environs.Environ) *s3.S3 { return e.(*environ).s3() } func InstanceEC2(inst instance.Instance) *ec2.Instance { return inst.(*ec2Instance).Instance } // BucketStorage returns a storage instance addressing // an arbitrary s3 bucket. func BucketStorage(b *s3.Bucket) storage.Storage { return &ec2storage{ bucket: b, } } // DeleteBucket deletes the s3 bucket used by the storage instance. func DeleteBucket(s storage.Storage) error { return deleteBucket(s.(*ec2storage)) } var testRoundTripper = &jujutest.ProxyRoundTripper{} func init() { // Prepare mock http transport for overriding metadata and images output in tests. testRoundTripper.RegisterForScheme("test") } // TODO: Apart from overriding different hardcoded hosts, these two test helpers are identical. Let's share. var origImagesUrl = imagemetadata.DefaultBaseURL // UseTestImageData causes the given content to be served // when the ec2 client asks for image data. func UseTestImageData(files map[string]string) { if files != nil { testRoundTripper.Sub = jujutest.NewCannedRoundTripper(files, nil) imagemetadata.DefaultBaseURL = "test:" signedImageDataOnly = false } else { signedImageDataOnly = true testRoundTripper.Sub = nil imagemetadata.DefaultBaseURL = origImagesUrl } } func UseTestRegionData(content map[string]aws.Region) { if content != nil { allRegions = content } else { allRegions = aws.Regions } } // UseTestInstanceTypeData causes the given instance type // cost data to be served for the "test" region. func UseTestInstanceTypeData(content instanceTypeCost) { if content != nil { allRegionCosts["test"] = content } else { delete(allRegionCosts, "test") } } var ( ShortAttempt = &shortAttempt StorageAttempt = &storageAttempt ) func EC2ErrCode(err error) string { return ec2ErrCode(err) } // FabricateInstance creates a new fictitious instance // given an existing instance and a new id. func FabricateInstance(inst instance.Instance, newId string) instance.Instance { oldi := inst.(*ec2Instance) newi := &ec2Instance{ e: oldi.e, Instance: &ec2.Instance{}, } *newi.Instance = *oldi.Instance newi.InstanceId = newId return newi } // Access non exported methods on ec2.storage type Storage interface { Put(file string, r io.Reader, length int64) error ResetMadeBucket() } func (s *ec2storage) ResetMadeBucket() { s.Lock() defer s.Unlock() s.madeBucket = false } var TestImagesData = map[string]string{ "/streams/v1/index.json": ` { "index": { "com.ubuntu.cloud:released": { "updated": "Wed, 01 May 2013 13:31:26 +0000", "clouds": [ { "region": "test", "endpoint": "https://ec2.endpoint.com" } ], "cloudname": "aws", "datatype": "image-ids", "format": "products:1.0", "products": [ "com.ubuntu.cloud:server:12.04:amd64", "com.ubuntu.cloud:server:12.04:i386", "com.ubuntu.cloud:server:12.04:amd64", "com.ubuntu.cloud:server:12.10:amd64", "com.ubuntu.cloud:server:13.04:i386" ], "path": "streams/v1/com.ubuntu.cloud:released:aws.js" } }, "updated": "Wed, 01 May 2013 13:31:26 +0000", "format": "index:1.0" } `, "/streams/v1/com.ubuntu.cloud:released:aws.js": ` { "content_id": "com.ubuntu.cloud:released:aws", "products": { "com.ubuntu.cloud:server:12.04:amd64": { "release": "precise", "version": "12.04", "arch": "amd64", "versions": { "20121218": { "items": { "usee1pi": { "root_store": "instance", "virt": "pv", "region": "us-east-1", "id": "ami-00000011" }, "usww1pe": { "root_store": "ebs", "virt": "pv", "region": "eu-west-1", "id": "ami-00000016" }, "apne1pe": { "root_store": "ebs", "virt": "pv", "region": "ap-northeast-1", "id": "ami-00000026" }, "apne1he": { "root_store": "ebs", "virt": "hvm", "region": "ap-northeast-1", "id": "ami-00000087" }, "test1pe": { "root_store": "ebs", "virt": "pv", "region": "test", "id": "ami-00000033" }, "test1he": { "root_store": "ebs", "virt": "hvm", "region": "test", "id": "ami-00000035" } }, "pubname": "ubuntu-precise-12.04-amd64-server-20121218", "label": "release" } } }, "com.ubuntu.cloud:server:12.04:i386": { "release": "precise", "version": "12.04", "arch": "i386", "versions": { "20121218": { "items": { "test1pe": { "root_store": "ebs", "virt": "pv", "region": "test", "id": "ami-00000034" }, "apne1pe": { "root_store": "ebs", "virt": "pv", "region": "ap-northeast-1", "id": "ami-00000023" } }, "pubname": "ubuntu-precise-12.04-i386-server-20121218", "label": "release" } } }, "com.ubuntu.cloud:server:12.10:amd64": { "release": "quantal", "version": "12.10", "arch": "amd64", "versions": { "20121218": { "items": { "usee1pi": { "root_store": "instance", "virt": "pv", "region": "us-east-1", "id": "ami-00000011" }, "usww1pe": { "root_store": "ebs", "virt": "pv", "region": "eu-west-1", "id": "ami-01000016" }, "apne1pe": { "root_store": "ebs", "virt": "pv", "region": "ap-northeast-1", "id": "ami-01000026" }, "apne1he": { "root_store": "ebs", "virt": "hvm", "region": "ap-northeast-1", "id": "ami-01000087" }, "test1he": { "root_store": "ebs", "virt": "hvm", "region": "test", "id": "ami-01000035" } }, "pubname": "ubuntu-quantal-12.10-amd64-server-20121218", "label": "release" } } }, "com.ubuntu.cloud:server:12.10:i386": { "release": "quantal", "version": "12.10", "arch": "i386", "versions": { "20121218": { "items": { "test1pe": { "root_store": "ebs", "virt": "pv", "region": "test", "id": "ami-01000034" }, "apne1pe": { "root_store": "ebs", "virt": "pv", "region": "ap-northeast-1", "id": "ami-01000023" } }, "pubname": "ubuntu-quantal-12.10-i386-server-20121218", "label": "release" } } }, "com.ubuntu.cloud:server:13.04:i386": { "release": "raring", "version": "13.04", "arch": "i386", "versions": { "20121218": { "items": { "test1pe": { "root_store": "ebs", "virt": "pv", "region": "test", "id": "ami-02000034" } }, "pubname": "ubuntu-raring-13.04-i386-server-20121218", "label": "release" } } } }, "format": "products:1.0" } `, } var TestInstanceTypeCosts = instanceTypeCost{ "m1.small": 60, "m1.medium": 120, "m1.large": 240, "m1.xlarge": 480, "t1.micro": 20, "c1.medium": 145, "c1.xlarge": 580, "cc1.4xlarge": 1300, "cc2.8xlarge": 2400, } var TestRegions = map[string]aws.Region{ "test": { Name: "test", EC2Endpoint: "https://ec2.endpoint.com", }, } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/config_test.go����������������������������0000644�0000153�0000161�00000017504�12321735642�026032� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2 // TODO: Clean this up so it matches environs/openstack/config_test.go. import ( "io/ioutil" "os" "path/filepath" "strings" "launchpad.net/goamz/aws" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) // Use local suite since this file lives in the ec2 package // for testing internals. type ConfigSuite struct { testbase.LoggingSuite savedHome, savedAccessKey, savedSecretKey string } var _ = gc.Suite(&ConfigSuite{}) var configTestRegion = aws.Region{ Name: "configtest", EC2Endpoint: "testregion.nowhere:1234", } var testAuth = aws.Auth{"gopher", "long teeth"} // configTest specifies a config parsing test, checking that env when // parsed as the ec2 section of a config file matches baseConfigResult // when mutated by the mutate function, or that the parse matches the // given error. type configTest struct { config map[string]interface{} change map[string]interface{} expect map[string]interface{} region string cbucket string pbucket string pbucketRegion string accessKey string secretKey string firewallMode string err string } type attrs map[string]interface{} func (t configTest) check(c *gc.C) { attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "ec2", "control-bucket": "x", }).Merge(t.config) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) e, err := environs.New(cfg) if t.change != nil { c.Assert(err, gc.IsNil) // Testing a change in configuration. var old, changed, valid *config.Config ec2env := e.(*environ) old = ec2env.ecfg().Config changed, err = old.Apply(t.change) c.Assert(err, gc.IsNil) // Keep err for validation below. valid, err = providerInstance.Validate(changed, old) if err == nil { err = ec2env.SetConfig(valid) } } if t.err != "" { c.Check(err, gc.ErrorMatches, t.err) return } c.Assert(err, gc.IsNil) ecfg := e.(*environ).ecfg() c.Assert(ecfg.Name(), gc.Equals, "testenv") c.Assert(ecfg.controlBucket(), gc.Equals, "x") if t.region != "" { c.Assert(ecfg.region(), gc.Equals, t.region) } if t.accessKey != "" { c.Assert(ecfg.accessKey(), gc.Equals, t.accessKey) c.Assert(ecfg.secretKey(), gc.Equals, t.secretKey) expected := map[string]string{ "access-key": t.accessKey, "secret-key": t.secretKey, } c.Assert(err, gc.IsNil) actual, err := e.Provider().SecretAttrs(ecfg.Config) c.Assert(err, gc.IsNil) c.Assert(expected, gc.DeepEquals, actual) } else { c.Assert(ecfg.accessKey(), gc.DeepEquals, testAuth.AccessKey) c.Assert(ecfg.secretKey(), gc.DeepEquals, testAuth.SecretKey) } if t.firewallMode != "" { c.Assert(ecfg.FirewallMode(), gc.Equals, t.firewallMode) } for name, expect := range t.expect { actual, found := ecfg.UnknownAttrs()[name] c.Check(found, gc.Equals, true) c.Check(actual, gc.Equals, expect) } // check storage bucket is configured correctly env := e.(*environ) c.Assert(env.Storage().(*ec2storage).bucket.Region.Name, gc.Equals, ecfg.region()) } var configTests = []configTest{ { config: attrs{}, }, { // check that region defaults to us-east-1 config: attrs{}, region: "us-east-1", }, { config: attrs{ "region": "eu-west-1", }, region: "eu-west-1", }, { config: attrs{ "region": "unknown", }, err: ".*invalid region name.*", }, { config: attrs{ "region": "configtest", }, region: "configtest", }, { config: attrs{ "region": "configtest", }, change: attrs{ "region": "us-east-1", }, err: `cannot change region from "configtest" to "us-east-1"`, }, { config: attrs{ "region": 666, }, err: `.*expected string, got int\(666\)`, }, { config: attrs{ "access-key": 666, }, err: `.*expected string, got int\(666\)`, }, { config: attrs{ "secret-key": 666, }, err: `.*expected string, got int\(666\)`, }, { config: attrs{ "control-bucket": 666, }, err: `.*expected string, got int\(666\)`, }, { change: attrs{ "control-bucket": "new-x", }, err: `cannot change control-bucket from "x" to "new-x"`, }, { config: attrs{ "access-key": "jujuer", "secret-key": "open sesame", }, accessKey: "jujuer", secretKey: "open sesame", }, { config: attrs{ "access-key": "jujuer", }, err: ".*environment has no access-key or secret-key", }, { config: attrs{ "secret-key": "badness", }, err: ".*environment has no access-key or secret-key", }, { config: attrs{ "admin-secret": "Futumpsh", }, }, { config: attrs{}, firewallMode: config.FwInstance, }, { config: attrs{ "firewall-mode": "instance", }, firewallMode: config.FwInstance, }, { config: attrs{ "firewall-mode": "global", }, firewallMode: config.FwGlobal, }, { config: attrs{ "ssl-hostname-verification": false, }, err: "disabling ssh-hostname-verification is not supported", }, { config: attrs{ "future": "hammerstein", }, expect: attrs{ "future": "hammerstein", }, }, { change: attrs{ "future": "hammerstein", }, expect: attrs{ "future": "hammerstein", }, }, } func indent(s string, with string) string { var r string lines := strings.Split(s, "\n") for _, l := range lines { r += with + l + "\n" } return r } func (s *ConfigSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.savedHome = osenv.Home() s.savedAccessKey = os.Getenv("AWS_ACCESS_KEY_ID") s.savedSecretKey = os.Getenv("AWS_SECRET_ACCESS_KEY") home := c.MkDir() sshDir := filepath.Join(home, ".ssh") err := os.Mkdir(sshDir, 0777) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(filepath.Join(sshDir, "id_rsa.pub"), []byte("sshkey\n"), 0666) c.Assert(err, gc.IsNil) osenv.SetHome(home) os.Setenv("AWS_ACCESS_KEY_ID", testAuth.AccessKey) os.Setenv("AWS_SECRET_ACCESS_KEY", testAuth.SecretKey) aws.Regions["configtest"] = configTestRegion } func (s *ConfigSuite) TearDownTest(c *gc.C) { osenv.SetHome(s.savedHome) os.Setenv("AWS_ACCESS_KEY_ID", s.savedAccessKey) os.Setenv("AWS_SECRET_ACCESS_KEY", s.savedSecretKey) delete(aws.Regions, "configtest") s.LoggingSuite.TearDownTest(c) } func (s *ConfigSuite) TestConfig(c *gc.C) { for i, t := range configTests { c.Logf("test %d: %v", i, t.config) t.check(c) } } func (s *ConfigSuite) TestMissingAuth(c *gc.C) { os.Setenv("AWS_ACCESS_KEY_ID", "") os.Setenv("AWS_SECRET_ACCESS_KEY", "") // Since r37 goamz uses these as fallbacks, so unset them too. os.Setenv("EC2_ACCESS_KEY", "") os.Setenv("EC2_SECRET_KEY", "") test := configTests[0] test.err = "environment has no access-key or secret-key" test.check(c) } func (s *ConfigSuite) TestPrepareInsertsUniqueControlBucket(c *gc.C) { attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "ec2", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) ctx := testing.Context(c) env0, err := providerInstance.Prepare(ctx, cfg) c.Assert(err, gc.IsNil) bucket0 := env0.(*environ).ecfg().controlBucket() c.Assert(bucket0, gc.Matches, "[a-f0-9]{32}") env1, err := providerInstance.Prepare(ctx, cfg) c.Assert(err, gc.IsNil) bucket1 := env1.(*environ).ecfg().controlBucket() c.Assert(bucket1, gc.Matches, "[a-f0-9]{32}") c.Assert(bucket1, gc.Not(gc.Equals), bucket0) } func (s *ConfigSuite) TestPrepareDoesNotTouchExistingControlBucket(c *gc.C) { attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "ec2", "control-bucket": "burblefoo", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := providerInstance.Prepare(testing.Context(c), cfg) c.Assert(err, gc.IsNil) bucket := env.(*environ).ecfg().controlBucket() c.Assert(bucket, gc.Equals, "burblefoo") } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/config.go���������������������������������0000644�0000153�0000161�00000005135�12321735642�024770� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2 import ( "fmt" "launchpad.net/goamz/aws" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/schema" ) var configFields = schema.Fields{ "access-key": schema.String(), "secret-key": schema.String(), "region": schema.String(), "control-bucket": schema.String(), } var configDefaults = schema.Defaults{ "access-key": "", "secret-key": "", "region": "us-east-1", } type environConfig struct { *config.Config attrs map[string]interface{} } func (c *environConfig) region() string { return c.attrs["region"].(string) } func (c *environConfig) controlBucket() string { return c.attrs["control-bucket"].(string) } func (c *environConfig) accessKey() string { return c.attrs["access-key"].(string) } func (c *environConfig) secretKey() string { return c.attrs["secret-key"].(string) } func (p environProvider) newConfig(cfg *config.Config) (*environConfig, error) { valid, err := p.Validate(cfg, nil) if err != nil { return nil, err } return &environConfig{valid, valid.UnknownAttrs()}, nil } func (p environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { // Check for valid changes for the base config values. if err := config.Validate(cfg, old); err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } ecfg := &environConfig{cfg, validated} if ecfg.accessKey() == "" || ecfg.secretKey() == "" { auth, err := aws.EnvAuth() if err != nil || ecfg.accessKey() != "" || ecfg.secretKey() != "" { return nil, fmt.Errorf("environment has no access-key or secret-key") } ecfg.attrs["access-key"] = auth.AccessKey ecfg.attrs["secret-key"] = auth.SecretKey } if _, ok := aws.Regions[ecfg.region()]; !ok { return nil, fmt.Errorf("invalid region name %q", ecfg.region()) } if old != nil { attrs := old.UnknownAttrs() if region, _ := attrs["region"].(string); ecfg.region() != region { return nil, fmt.Errorf("cannot change region from %q to %q", region, ecfg.region()) } if bucket, _ := attrs["control-bucket"].(string); ecfg.controlBucket() != bucket { return nil, fmt.Errorf("cannot change control-bucket from %q to %q", bucket, ecfg.controlBucket()) } } // ssl-hostname-verification cannot be disabled if !ecfg.SSLHostnameVerification() { return nil, fmt.Errorf("disabling ssh-hostname-verification is not supported") } // Apply the coerced unknown values back into the config. return cfg.Apply(ecfg.attrs) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/ec2_test.go�������������������������������0000644�0000153�0000161�00000002363�12321735642�025233� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2 import ( amzec2 "launchpad.net/goamz/ec2" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" ) type Suite struct{} var _ = gc.Suite(&Suite{}) type RootDiskTest struct { name string constraint *uint64 disksize uint64 device amzec2.BlockDeviceMapping } var rootDiskTests = []RootDiskTest{ { "nil constraint", nil, 8192, amzec2.BlockDeviceMapping{VolumeSize: 8, DeviceName: "/dev/sda1"}, }, { "too small constraint", pInt(4000), 8192, amzec2.BlockDeviceMapping{VolumeSize: 8, DeviceName: "/dev/sda1"}, }, { "big constraint", pInt(20 * 1024), 20 * 1024, amzec2.BlockDeviceMapping{VolumeSize: 20, DeviceName: "/dev/sda1"}, }, { "round up constraint", pInt(20*1024 + 1), 21 * 1024, amzec2.BlockDeviceMapping{VolumeSize: 21, DeviceName: "/dev/sda1"}, }, } func (*Suite) TestRootDisk(c *gc.C) { for _, t := range rootDiskTests { c.Logf("Test %s", t.name) cons := constraints.Value{RootDisk: t.constraint} device, size := getDiskSize(cons) c.Check(size, gc.Equals, t.disksize) c.Check(device, gc.DeepEquals, t.device) } } func pInt(i uint64) *uint64 { return &i } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/storage.go��������������������������������0000644�0000153�0000161�00000012363�12321735642�025170� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2 import ( "fmt" "io" "net" "sync" "time" "launchpad.net/goamz/s3" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" ) func init() { // We will decide when to retry and under what circumstances, not s3. // Sometimes it is expected a file may not exist and we don't want s3 // to hold things up by unilaterally deciding to retry for no good reason. s3.RetryAttempts(false) } func NewStorage(bucket *s3.Bucket) storage.Storage { return &ec2storage{bucket: bucket} } // ec2storage implements storage.Storage on // an ec2.bucket. type ec2storage struct { sync.Mutex madeBucket bool bucket *s3.Bucket } // makeBucket makes the environent's control bucket, the // place where bootstrap information and deployed charms // are stored. To avoid two round trips on every PUT operation, // we do this only once for each environ. func (s *ec2storage) makeBucket() error { s.Lock() defer s.Unlock() if s.madeBucket { return nil } // PutBucket always return a 200 if we recreate an existing bucket for the // original s3.amazonaws.com endpoint. For all other endpoints PutBucket // returns 409 with a known subcode. if err := s.bucket.PutBucket(s3.Private); err != nil && s3ErrCode(err) != "BucketAlreadyOwnedByYou" { return err } s.madeBucket = true return nil } func (s *ec2storage) Put(file string, r io.Reader, length int64) error { if err := s.makeBucket(); err != nil { return fmt.Errorf("cannot make S3 control bucket: %v", err) } err := s.bucket.PutReader(file, r, length, "binary/octet-stream", s3.Private) if err != nil { return fmt.Errorf("cannot write file %q to control bucket: %v", file, err) } return nil } func (s *ec2storage) Get(file string) (r io.ReadCloser, err error) { r, err = s.bucket.GetReader(file) return r, maybeNotFound(err) } func (s *ec2storage) URL(name string) (string, error) { // 10 years should be good enough. return s.bucket.SignedURL(name, time.Now().AddDate(10, 0, 0)), nil } var storageAttempt = utils.AttemptStrategy{ Total: 5 * time.Second, Delay: 200 * time.Millisecond, } // ConsistencyStrategy is specified in the StorageReader interface. func (s *ec2storage) DefaultConsistencyStrategy() utils.AttemptStrategy { return storageAttempt } // ShouldRetry is specified in the StorageReader interface. func (s *ec2storage) ShouldRetry(err error) bool { if err == nil { return false } switch err { case io.ErrUnexpectedEOF, io.EOF: return true } if s3ErrorStatusCode(err) == 404 { return true } switch e := err.(type) { case *net.DNSError: return true case *net.OpError: switch e.Op { case "read", "write": return true } case *s3.Error: switch e.Code { case "InternalError": return true } } return false } // s3ErrorStatusCode returns the HTTP status of the S3 request error, // if it is an error from an S3 operation, or 0 if it was not. func s3ErrorStatusCode(err error) int { if err, _ := err.(*s3.Error); err != nil { return err.StatusCode } return 0 } // s3ErrCode returns the text status code of the S3 error code. func s3ErrCode(err error) string { if err, ok := err.(*s3.Error); ok { return err.Code } return "" } func (s *ec2storage) Remove(file string) error { err := s.bucket.Del(file) // If we can't delete the object because the bucket doesn't // exist, then we don't care. if s3ErrorStatusCode(err) == 404 { return nil } return err } func (s *ec2storage) List(prefix string) ([]string, error) { // TODO cope with more than 1000 objects in the bucket. resp, err := s.bucket.List(prefix, "", "", 0) if err != nil { // If the bucket is not found, it's not an error // because it's only created when the first // file is put. if s3ErrorStatusCode(err) == 404 { return nil, nil } return nil, err } var names []string for _, key := range resp.Contents { names = append(names, key.Key) } return names, nil } func (s *ec2storage) RemoveAll() error { names, err := storage.List(s, "") if err != nil { return err } // Remove all the objects in parallel to minimize round-trips. // If we're in danger of having hundreds of objects, // we'll want to change this to limit the number // of concurrent operations. var wg sync.WaitGroup wg.Add(len(names)) errc := make(chan error, len(names)) for _, name := range names { name := name go func() { if err := s.Remove(name); err != nil { errc <- err } wg.Done() }() } wg.Wait() select { case err := <-errc: return fmt.Errorf("cannot delete all provider state: %v", err) default: } s.Lock() defer s.Unlock() // Even DelBucket fails, it won't harm if we try again - the operation // might have succeeded even if we get an error. s.madeBucket = false err = deleteBucket(s) err = s.bucket.DelBucket() if s3ErrorStatusCode(err) == 404 { return nil } return err } func deleteBucket(s *ec2storage) (err error) { for a := s.DefaultConsistencyStrategy().Start(); a.Next(); { err = s.bucket.DelBucket() if err == nil || !s.ShouldRetry(err) { break } } return err } func maybeNotFound(err error) error { if err != nil && s3ErrorStatusCode(err) == 404 { return errors.NewNotFoundError(err, "") } return err } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/suite_test.go�����������������������������0000644�0000153�0000161�00000000575�12321735642�025716� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2_test import ( "flag" "testing" gc "launchpad.net/gocheck" ) var amazon = flag.Bool("amazon", false, "Also run some tests on live Amazon servers") func TestEC2(t *testing.T) { if *amazon { registerAmazonTests() } registerLocalTests() gc.TestingT(t) } �����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/local_test.go�����������������������������0000644�0000153�0000161�00000037026�12321735776�025670� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2_test import ( "fmt" "regexp" "sort" "strings" jc "github.com/juju/testing/checkers" "launchpad.net/goamz/aws" amzec2 "launchpad.net/goamz/ec2" "launchpad.net/goamz/ec2/ec2test" "launchpad.net/goamz/s3" "launchpad.net/goamz/s3/s3test" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/simplestreams" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/ec2" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" "launchpad.net/juju-core/version" ) type ProviderSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&ProviderSuite{}) func (t *ProviderSuite) assertGetImageMetadataSources(c *gc.C, stream, officialSourcePath string) { // Make an env configured with the stream. envAttrs := localConfigAttrs if stream != "" { envAttrs = envAttrs.Merge(coretesting.Attrs{ "image-stream": stream, }) } cfg, err := config.New(config.NoDefaults, envAttrs) c.Assert(err, gc.IsNil) env, err := environs.Prepare(cfg, coretesting.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) c.Assert(env, gc.NotNil) sources, err := imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) c.Assert(len(sources), gc.Equals, 2) var urls = make([]string, len(sources)) for i, source := range sources { url, err := source.URL("") c.Assert(err, gc.IsNil) urls[i] = url } // The control bucket URL contains the bucket name. c.Check(strings.Contains(urls[0], ec2.ControlBucketName(env)+"/images"), jc.IsTrue) c.Assert(urls[1], gc.Equals, fmt.Sprintf("http://cloud-images.ubuntu.com/%s/", officialSourcePath)) } func (t *ProviderSuite) TestGetImageMetadataSources(c *gc.C) { t.assertGetImageMetadataSources(c, "", "releases") t.assertGetImageMetadataSources(c, "released", "releases") t.assertGetImageMetadataSources(c, "daily", "daily") } var localConfigAttrs = coretesting.FakeConfig().Merge(coretesting.Attrs{ "name": "sample", "type": "ec2", "region": "test", "control-bucket": "test-bucket", "access-key": "x", "secret-key": "x", "agent-version": version.Current.Number.String(), }) func registerLocalTests() { // N.B. Make sure the region we use here // has entries in the images/query txt files. aws.Regions["test"] = aws.Region{ Name: "test", } gc.Suite(&localServerSuite{}) gc.Suite(&localLiveSuite{}) gc.Suite(&localNonUSEastSuite{}) } // localLiveSuite runs tests from LiveTests using a fake // EC2 server that runs within the test process itself. type localLiveSuite struct { LiveTests srv localServer restoreEC2Patching func() } func (t *localLiveSuite) SetUpSuite(c *gc.C) { t.TestConfig = localConfigAttrs t.restoreEC2Patching = patchEC2ForTesting() t.srv.startServer(c) t.LiveTests.SetUpSuite(c) } func (t *localLiveSuite) TearDownSuite(c *gc.C) { t.LiveTests.TearDownSuite(c) t.srv.stopServer(c) t.restoreEC2Patching() } func (t *LiveTests) TestStartInstanceOnUnknownPlatform(c *gc.C) { c.Skip("broken under ec2 - see https://bugs.launchpad.net/juju-core/+bug/1233278") } // localServer represents a fake EC2 server running within // the test process itself. type localServer struct { ec2srv *ec2test.Server s3srv *s3test.Server config *s3test.Config } func (srv *localServer) startServer(c *gc.C) { var err error srv.ec2srv, err = ec2test.NewServer() if err != nil { c.Fatalf("cannot start ec2 test server: %v", err) } srv.s3srv, err = s3test.NewServer(srv.config) if err != nil { c.Fatalf("cannot start s3 test server: %v", err) } aws.Regions["test"] = aws.Region{ Name: "test", EC2Endpoint: srv.ec2srv.URL(), S3Endpoint: srv.s3srv.URL(), S3LocationConstraint: true, } s3inst := s3.New(aws.Auth{}, aws.Regions["test"]) storage := ec2.BucketStorage(s3inst.Bucket("juju-dist")) envtesting.UploadFakeTools(c, storage) srv.addSpice(c) } // addSpice adds some "spice" to the local server // by adding state that may cause tests to fail. func (srv *localServer) addSpice(c *gc.C) { states := []amzec2.InstanceState{ ec2test.ShuttingDown, ec2test.Terminated, ec2test.Stopped, } for _, state := range states { srv.ec2srv.NewInstances(1, "m1.small", "ami-a7f539ce", state, nil) } } func (srv *localServer) stopServer(c *gc.C) { srv.ec2srv.Quit() srv.s3srv.Quit() // Clear out the region because the server address is // no longer valid. delete(aws.Regions, "test") } // localServerSuite contains tests that run against a fake EC2 server // running within the test process itself. These tests can test things that // would be unreasonably slow or expensive to test on a live Amazon server. // It starts a new local ec2test server for each test. The server is // accessed by using the "test" region, which is changed to point to the // network address of the local server. type localServerSuite struct { jujutest.Tests srv localServer restoreEC2Patching func() } func (t *localServerSuite) SetUpSuite(c *gc.C) { t.TestConfig = localConfigAttrs t.restoreEC2Patching = patchEC2ForTesting() t.Tests.SetUpSuite(c) } func (t *localServerSuite) TearDownSuite(c *gc.C) { t.Tests.TearDownSuite(c) t.restoreEC2Patching() } func (t *localServerSuite) SetUpTest(c *gc.C) { t.srv.startServer(c) t.Tests.SetUpTest(c) t.PatchValue(&version.Current, version.Binary{ Number: version.Current.Number, Series: coretesting.FakeDefaultSeries, Arch: arch.AMD64, }) } func (t *localServerSuite) TearDownTest(c *gc.C) { t.Tests.TearDownTest(c) t.srv.stopServer(c) } func (t *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) { env := t.Prepare(c) envtesting.UploadFakeTools(c, env.Storage()) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) // check that the state holds the id of the bootstrap machine. bootstrapState, err := bootstrap.LoadState(env.Storage()) c.Assert(err, gc.IsNil) c.Assert(bootstrapState.StateInstances, gc.HasLen, 1) expectedHardware := instance.MustParseHardware("arch=amd64 cpu-cores=1 cpu-power=100 mem=1740M root-disk=8192M") insts, err := env.AllInstances() c.Assert(err, gc.IsNil) c.Assert(insts, gc.HasLen, 1) c.Check(insts[0].Id(), gc.Equals, bootstrapState.StateInstances[0]) c.Check(expectedHardware, gc.DeepEquals, bootstrapState.Characteristics[0]) // check that the user data is configured to start zookeeper // and the machine and provisioning agents. // check that the user data is configured to only configure // authorized SSH keys and set the log output; everything // else happens after the machine is brought up. inst := t.srv.ec2srv.Instance(string(insts[0].Id())) c.Assert(inst, gc.NotNil) bootstrapDNS, err := insts[0].DNSName() c.Assert(err, gc.IsNil) c.Assert(bootstrapDNS, gc.Not(gc.Equals), "") userData, err := utils.Gunzip(inst.UserData) c.Assert(err, gc.IsNil) c.Logf("first instance: UserData: %q", userData) var userDataMap map[interface{}]interface{} err = goyaml.Unmarshal(userData, &userDataMap) c.Assert(err, gc.IsNil) c.Assert(userDataMap, jc.DeepEquals, map[interface{}]interface{}{ "output": map[interface{}]interface{}{ "all": "| tee -a /var/log/cloud-init-output.log", }, "ssh_authorized_keys": splitAuthKeys(env.Config().AuthorizedKeys()), "runcmd": []interface{}{ "set -xe", "install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'", "printf '%s\\n' 'user-admin:bootstrap' > '/var/lib/juju/nonce.txt'", }, }) // check that a new instance will be started with a machine agent inst1, hc := testing.AssertStartInstance(c, env, "1") c.Check(*hc.Arch, gc.Equals, "amd64") c.Check(*hc.Mem, gc.Equals, uint64(1740)) c.Check(*hc.CpuCores, gc.Equals, uint64(1)) c.Assert(*hc.CpuPower, gc.Equals, uint64(100)) inst = t.srv.ec2srv.Instance(string(inst1.Id())) c.Assert(inst, gc.NotNil) userData, err = utils.Gunzip(inst.UserData) c.Assert(err, gc.IsNil) c.Logf("second instance: UserData: %q", userData) userDataMap = nil err = goyaml.Unmarshal(userData, &userDataMap) c.Assert(err, gc.IsNil) CheckPackage(c, userDataMap, "git", true) CheckPackage(c, userDataMap, "mongodb-server", false) CheckScripts(c, userDataMap, "jujud bootstrap-state", false) CheckScripts(c, userDataMap, "/var/lib/juju/agents/machine-1/agent.conf", true) // TODO check for provisioning agent err = env.Destroy() c.Assert(err, gc.IsNil) _, err = bootstrap.LoadState(env.Storage()) c.Assert(err, gc.NotNil) } // splitAuthKeys splits the given authorized keys // into the form expected to be found in the // user data. func splitAuthKeys(keys string) []interface{} { slines := strings.FieldsFunc(keys, func(r rune) bool { return r == '\n' }) var lines []interface{} for _, line := range slines { lines = append(lines, ssh.EnsureJujuComment(strings.TrimSpace(line))) } return lines } func (t *localServerSuite) TestInstanceStatus(c *gc.C) { env := t.Prepare(c) envtesting.UploadFakeTools(c, env.Storage()) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) t.srv.ec2srv.SetInitialInstanceState(ec2test.Terminated) inst, _ := testing.AssertStartInstance(c, env, "1") c.Assert(err, gc.IsNil) c.Assert(inst.Status(), gc.Equals, "terminated") } func (t *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) { env := t.Prepare(c) envtesting.UploadFakeTools(c, env.Storage()) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) _, hc := testing.AssertStartInstance(c, env, "1") c.Check(*hc.Arch, gc.Equals, "amd64") c.Check(*hc.Mem, gc.Equals, uint64(1740)) c.Check(*hc.CpuCores, gc.Equals, uint64(1)) c.Assert(*hc.CpuPower, gc.Equals, uint64(100)) } func (t *localServerSuite) TestAddresses(c *gc.C) { env := t.Prepare(c) envtesting.UploadFakeTools(c, env.Storage()) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) inst, _ := testing.AssertStartInstance(c, env, "1") c.Assert(err, gc.IsNil) addrs, err := inst.Addresses() c.Assert(err, gc.IsNil) // Expected values use Address type but really contain a regexp for // the value rather than a valid ip or hostname. expected := []instance.Address{{ Value: "*.testing.invalid", Type: instance.HostName, NetworkScope: instance.NetworkPublic, }, { Value: "*.internal.invalid", Type: instance.HostName, NetworkScope: instance.NetworkCloudLocal, }, { Value: "8.0.0.*", Type: instance.Ipv4Address, NetworkScope: instance.NetworkPublic, }, { Value: "127.0.0.*", Type: instance.Ipv4Address, NetworkScope: instance.NetworkCloudLocal, }} c.Assert(addrs, gc.HasLen, len(expected)) for i, addr := range addrs { c.Check(addr.Value, gc.Matches, expected[i].Value) c.Check(addr.Type, gc.Equals, expected[i].Type) c.Check(addr.NetworkScope, gc.Equals, expected[i].NetworkScope) } } func (t *localServerSuite) TestValidateImageMetadata(c *gc.C) { env := t.Prepare(c) params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("test") c.Assert(err, gc.IsNil) params.Series = "precise" params.Endpoint = "https://ec2.endpoint.com" params.Sources, err = imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) image_ids, _, err := imagemetadata.ValidateImageMetadata(params) c.Assert(err, gc.IsNil) sort.Strings(image_ids) c.Assert(image_ids, gc.DeepEquals, []string{"ami-00000033", "ami-00000034", "ami-00000035"}) } func (t *localServerSuite) TestGetToolsMetadataSources(c *gc.C) { env := t.Prepare(c) sources, err := tools.GetMetadataSources(env) c.Assert(err, gc.IsNil) c.Assert(len(sources), gc.Equals, 1) url, err := sources[0].URL("") // The control bucket URL contains the bucket name. c.Assert(strings.Contains(url, ec2.ControlBucketName(env)+"/tools"), jc.IsTrue) } func (t *localServerSuite) TestSupportedArchitectures(c *gc.C) { env := t.Prepare(c) a, err := env.SupportedArchitectures() c.Assert(err, gc.IsNil) c.Assert(a, jc.SameContents, []string{"amd64", "i386"}) } // localNonUSEastSuite is similar to localServerSuite but the S3 mock server // behaves as if it is not in the us-east region. type localNonUSEastSuite struct { testbase.LoggingSuite restoreEC2Patching func() srv localServer env environs.Environ } func (t *localNonUSEastSuite) SetUpSuite(c *gc.C) { t.LoggingSuite.SetUpSuite(c) t.restoreEC2Patching = patchEC2ForTesting() } func (t *localNonUSEastSuite) TearDownSuite(c *gc.C) { t.restoreEC2Patching() t.LoggingSuite.TearDownSuite(c) } func (t *localNonUSEastSuite) SetUpTest(c *gc.C) { t.LoggingSuite.SetUpTest(c) t.srv.config = &s3test.Config{ Send409Conflict: true, } t.srv.startServer(c) cfg, err := config.New(config.NoDefaults, localConfigAttrs) c.Assert(err, gc.IsNil) env, err := environs.Prepare(cfg, coretesting.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) t.env = env } func (t *localNonUSEastSuite) TearDownTest(c *gc.C) { t.srv.stopServer(c) t.LoggingSuite.TearDownTest(c) } func patchEC2ForTesting() func() { ec2.UseTestImageData(ec2.TestImagesData) ec2.UseTestInstanceTypeData(ec2.TestInstanceTypeCosts) ec2.UseTestRegionData(ec2.TestRegions) restoreTimeouts := envtesting.PatchAttemptStrategies(ec2.ShortAttempt, ec2.StorageAttempt) restoreFinishBootstrap := envtesting.DisableFinishBootstrap() return func() { restoreFinishBootstrap() restoreTimeouts() ec2.UseTestImageData(nil) ec2.UseTestInstanceTypeData(nil) ec2.UseTestRegionData(nil) } } // If match is true, CheckScripts checks that at least one script started // by the cloudinit data matches the given regexp pattern, otherwise it // checks that no script matches. It's exported so it can be used by tests // defined in ec2_test. func CheckScripts(c *gc.C, userDataMap map[interface{}]interface{}, pattern string, match bool) { scripts0 := userDataMap["runcmd"] if scripts0 == nil { c.Errorf("cloudinit has no entry for runcmd") return } scripts := scripts0.([]interface{}) re := regexp.MustCompile(pattern) found := false for _, s0 := range scripts { s := s0.(string) if re.MatchString(s) { found = true } } switch { case match && !found: c.Errorf("script %q not found in %q", pattern, scripts) case !match && found: c.Errorf("script %q found but not expected in %q", pattern, scripts) } } // CheckPackage checks that the cloudinit will or won't install the given // package, depending on the value of match. It's exported so it can be // used by tests defined outside the ec2 package. func CheckPackage(c *gc.C, userDataMap map[interface{}]interface{}, pkg string, match bool) { pkgs0 := userDataMap["packages"] if pkgs0 == nil { if match { c.Errorf("cloudinit has no entry for packages") } return } pkgs := pkgs0.([]interface{}) found := false for _, p0 := range pkgs { p := p0.(string) if p == pkg { found = true } } switch { case match && !found: c.Errorf("package %q not found in %v", pkg, pkgs) case !match && found: c.Errorf("%q found but not expected in %v", pkg, pkgs) } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/live_test.go������������������������������0000644�0000153�0000161�00000031704�12321735642�025522� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2_test import ( "crypto/rand" "fmt" "io" "strings" jc "github.com/juju/testing/checkers" amzec2 "launchpad.net/goamz/ec2" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/storage" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/ec2" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) // uniqueName is generated afresh for every test run, so that // we are not polluted by previous test state. var uniqueName = randomName() func randomName() string { buf := make([]byte, 8) _, err := io.ReadFull(rand.Reader, buf) if err != nil { panic(fmt.Sprintf("error from crypto rand: %v", err)) } return fmt.Sprintf("%x", buf) } func registerAmazonTests() { // The following attributes hold the environment configuration // for running the amazon EC2 integration tests. // // This is missing keys for security reasons; set the following // environment variables to make the Amazon testing work: // access-key: $AWS_ACCESS_KEY_ID // secret-key: $AWS_SECRET_ACCESS_KEY attrs := coretesting.FakeConfig().Merge(map[string]interface{}{ "name": "sample-" + uniqueName, "type": "ec2", "control-bucket": "juju-test-" + uniqueName, "admin-secret": "for real", "firewall-mode": config.FwInstance, "agent-version": version.Current.Number.String(), }) gc.Suite(&LiveTests{ LiveTests: jujutest.LiveTests{ TestConfig: attrs, Attempt: *ec2.ShortAttempt, CanOpenState: true, HasProvisioner: true, }, }) } // LiveTests contains tests that can be run against the Amazon servers. // Each test runs using the same ec2 connection. type LiveTests struct { testbase.LoggingSuite jujutest.LiveTests } func (t *LiveTests) SetUpSuite(c *gc.C) { t.LoggingSuite.SetUpSuite(c) // TODO: Share code from jujutest.LiveTests for creating environment e, err := environs.NewFromAttrs(t.TestConfig) c.Assert(err, gc.IsNil) // Put some fake tools in place so that tests that are simply // starting instances without any need to check if those instances // are running will find them in the public bucket. envtesting.UploadFakeTools(c, e.Storage()) t.LiveTests.SetUpSuite(c) } func (t *LiveTests) TearDownSuite(c *gc.C) { if t.Env == nil { // This can happen if SetUpSuite fails. return } t.LiveTests.TearDownSuite(c) t.LoggingSuite.TearDownSuite(c) } func (t *LiveTests) SetUpTest(c *gc.C) { t.LoggingSuite.SetUpTest(c) t.LiveTests.SetUpTest(c) t.PatchValue(&version.Current, version.Binary{ Number: version.Current.Number, Series: coretesting.FakeDefaultSeries, Arch: arch.AMD64, }) } func (t *LiveTests) TearDownTest(c *gc.C) { t.LiveTests.TearDownTest(c) t.LoggingSuite.TearDownTest(c) } // TODO(niemeyer): Looks like many of those tests should be moved to jujutest.LiveTests. func (t *LiveTests) TestInstanceAttributes(c *gc.C) { inst, hc := testing.AssertStartInstance(c, t.Env, "30") defer t.Env.StopInstances([]instance.Instance{inst}) // Sanity check for hardware characteristics. c.Assert(hc.Arch, gc.NotNil) c.Assert(hc.Mem, gc.NotNil) c.Assert(hc.RootDisk, gc.NotNil) c.Assert(hc.CpuCores, gc.NotNil) c.Assert(hc.CpuPower, gc.NotNil) dns, err := inst.WaitDNSName() // TODO(niemeyer): This assert sometimes fails with "no instances found" c.Assert(err, gc.IsNil) c.Assert(dns, gc.Not(gc.Equals), "") insts, err := t.Env.Instances([]instance.Id{inst.Id()}) c.Assert(err, gc.IsNil) c.Assert(len(insts), gc.Equals, 1) ec2inst := ec2.InstanceEC2(insts[0]) c.Assert(ec2inst.DNSName, gc.Equals, dns) c.Assert(ec2inst.InstanceType, gc.Equals, "m1.small") } func (t *LiveTests) TestStartInstanceConstraints(c *gc.C) { cons := constraints.MustParse("mem=2G") inst, hc := testing.AssertStartInstanceWithConstraints(c, t.Env, "30", cons) defer t.Env.StopInstances([]instance.Instance{inst}) ec2inst := ec2.InstanceEC2(inst) c.Assert(ec2inst.InstanceType, gc.Equals, "m1.medium") c.Assert(*hc.Arch, gc.Equals, "amd64") c.Assert(*hc.Mem, gc.Equals, uint64(3840)) c.Assert(*hc.RootDisk, gc.Equals, uint64(8192)) c.Assert(*hc.CpuCores, gc.Equals, uint64(1)) c.Assert(*hc.CpuPower, gc.Equals, uint64(200)) } func (t *LiveTests) TestInstanceGroups(c *gc.C) { t.PrepareOnce(c) ec2conn := ec2.EnvironEC2(t.Env) groups := amzec2.SecurityGroupNames( ec2.JujuGroupName(t.Env), ec2.MachineGroupName(t.Env, "98"), ec2.MachineGroupName(t.Env, "99"), ) info := make([]amzec2.SecurityGroupInfo, len(groups)) // Create a group with the same name as the juju group // but with different permissions, to check that it's deleted // and recreated correctly. oldJujuGroup := createGroup(c, ec2conn, groups[0].Name, "old juju group") // Add two permissions: one is required and should be left alone; // the other is not and should be deleted. // N.B. this is unfortunately sensitive to the actual set of permissions used. _, err := ec2conn.AuthorizeSecurityGroup(oldJujuGroup, []amzec2.IPPerm{ { Protocol: "tcp", FromPort: 22, ToPort: 22, SourceIPs: []string{"0.0.0.0/0"}, }, { Protocol: "udp", FromPort: 4321, ToPort: 4322, SourceIPs: []string{"3.4.5.6/32"}, }, }) c.Assert(err, gc.IsNil) inst0, _ := testing.AssertStartInstance(c, t.Env, "98") defer t.Env.StopInstances([]instance.Instance{inst0}) // Create a same-named group for the second instance // before starting it, to check that it's reused correctly. oldMachineGroup := createGroup(c, ec2conn, groups[2].Name, "old machine group") inst1, _ := testing.AssertStartInstance(c, t.Env, "99") defer t.Env.StopInstances([]instance.Instance{inst1}) groupsResp, err := ec2conn.SecurityGroups(groups, nil) c.Assert(err, gc.IsNil) c.Assert(groupsResp.Groups, gc.HasLen, len(groups)) // For each group, check that it exists and record its id. for i, group := range groups { found := false for _, g := range groupsResp.Groups { if g.Name == group.Name { groups[i].Id = g.Id info[i] = g found = true break } } if !found { c.Fatalf("group %q not found", group.Name) } } // The old juju group should have been reused. c.Check(groups[0].Id, gc.Equals, oldJujuGroup.Id) // Check that it authorizes the correct ports and there // are no extra permissions (in particular we are checking // that the unneeded permission that we added earlier // has been deleted). perms := info[0].IPPerms c.Assert(perms, gc.HasLen, 6) checkPortAllowed(c, perms, 22) // SSH checkPortAllowed(c, perms, coretesting.FakeConfig()["state-port"].(int)) checkPortAllowed(c, perms, coretesting.FakeConfig()["api-port"].(int)) checkSecurityGroupAllowed(c, perms, groups[0]) // The old machine group should have been reused also. c.Check(groups[2].Id, gc.Equals, oldMachineGroup.Id) // Check that each instance is part of the correct groups. resp, err := ec2conn.Instances([]string{string(inst0.Id()), string(inst1.Id())}, nil) c.Assert(err, gc.IsNil) c.Assert(resp.Reservations, gc.HasLen, 2) for _, r := range resp.Reservations { c.Assert(r.Instances, gc.HasLen, 1) // each instance must be part of the general juju group. inst := r.Instances[0] msg := gc.Commentf("instance %#v", inst) c.Assert(hasSecurityGroup(inst, groups[0]), gc.Equals, true, msg) switch instance.Id(inst.InstanceId) { case inst0.Id(): c.Assert(hasSecurityGroup(inst, groups[1]), gc.Equals, true, msg) c.Assert(hasSecurityGroup(inst, groups[2]), gc.Equals, false, msg) case inst1.Id(): c.Assert(hasSecurityGroup(inst, groups[2]), gc.Equals, true, msg) c.Assert(hasSecurityGroup(inst, groups[1]), gc.Equals, false, msg) default: c.Errorf("unknown instance found: %v", inst) } } // Check that listing those instances finds them using the groups instIds := []instance.Id{inst0.Id(), inst1.Id()} idsFromInsts := func(insts []instance.Instance) (ids []instance.Id) { for _, inst := range insts { ids = append(ids, inst.Id()) } return ids } insts, err := t.Env.Instances(instIds) c.Assert(err, gc.IsNil) c.Assert(instIds, jc.SameContents, idsFromInsts(insts)) allInsts, err := t.Env.AllInstances() c.Assert(err, gc.IsNil) c.Assert(instIds, jc.SameContents, idsFromInsts(allInsts)) } func (t *LiveTests) TestDestroy(c *gc.C) { s := t.Env.Storage() err := s.Put("foo", strings.NewReader("foo"), 3) c.Assert(err, gc.IsNil) err = s.Put("bar", strings.NewReader("bar"), 3) c.Assert(err, gc.IsNil) // Check that the bucket exists, so we can be sure // we have checked correctly that it's been destroyed. names, err := storage.List(s, "") c.Assert(err, gc.IsNil) c.Assert(len(names) >= 2, gc.Equals, true) t.Destroy(c) for a := ec2.ShortAttempt.Start(); a.Next(); { names, err = storage.List(s, "") if len(names) == 0 { break } } c.Assert(names, gc.HasLen, 0) } func checkPortAllowed(c *gc.C, perms []amzec2.IPPerm, port int) { for _, perm := range perms { if perm.FromPort == port { c.Check(perm.Protocol, gc.Equals, "tcp") c.Check(perm.ToPort, gc.Equals, port) c.Check(perm.SourceIPs, gc.DeepEquals, []string{"0.0.0.0/0"}) c.Check(perm.SourceGroups, gc.HasLen, 0) return } } c.Errorf("ip port permission not found for %d in %#v", port, perms) } func checkSecurityGroupAllowed(c *gc.C, perms []amzec2.IPPerm, g amzec2.SecurityGroup) { protos := map[string]struct { fromPort int toPort int }{ "tcp": {0, 65535}, "udp": {0, 65535}, "icmp": {-1, -1}, } for _, perm := range perms { if len(perm.SourceGroups) > 0 { c.Check(perm.SourceGroups, gc.HasLen, 1) c.Check(perm.SourceGroups[0].Id, gc.Equals, g.Id) ports, ok := protos[perm.Protocol] if !ok { c.Errorf("unexpected protocol in security group: %q", perm.Protocol) continue } delete(protos, perm.Protocol) c.Check(perm.FromPort, gc.Equals, ports.fromPort) c.Check(perm.ToPort, gc.Equals, ports.toPort) } } if len(protos) > 0 { c.Errorf("%d security group permission not found for %#v in %#v", len(protos), g, perms) } } func (t *LiveTests) TestStopInstances(c *gc.C) { // It would be nice if this test was in jujutest, but // there's no way for jujutest to fabricate a valid-looking // instance id. inst0, _ := testing.AssertStartInstance(c, t.Env, "40") inst1 := ec2.FabricateInstance(inst0, "i-aaaaaaaa") inst2, _ := testing.AssertStartInstance(c, t.Env, "41") err := t.Env.StopInstances([]instance.Instance{inst0, inst1, inst2}) c.Check(err, gc.IsNil) var insts []instance.Instance // We need the retry logic here because we are waiting // for Instances to return an error, and it will not retry // if it succeeds. gone := false for a := ec2.ShortAttempt.Start(); a.Next(); { insts, err = t.Env.Instances([]instance.Id{inst0.Id(), inst2.Id()}) if err == environs.ErrPartialInstances { // instances not gone yet. continue } if err == environs.ErrNoInstances { gone = true break } c.Fatalf("error getting instances: %v", err) } if !gone { c.Errorf("after termination, instances remaining: %v", insts) } } func (t *LiveTests) TestPutBucketOnlyOnce(c *gc.C) { s3inst := ec2.EnvironS3(t.Env) b := s3inst.Bucket("test-once-" + uniqueName) s := ec2.BucketStorage(b) // Check that we don't do a PutBucket every time by // getting it to create the bucket, destroying the bucket behind // the scenes, and trying to put another object, // which should fail because it doesn't try to do // the PutBucket again. err := s.Put("test-object", strings.NewReader("test"), 4) c.Assert(err, gc.IsNil) err = s.Remove("test-object") c.Assert(err, gc.IsNil) err = ec2.DeleteBucket(s) c.Assert(err, gc.IsNil) err = s.Put("test-object", strings.NewReader("test"), 4) c.Assert(err, gc.ErrorMatches, ".*The specified bucket does not exist") } // createGroup creates a new EC2 group and returns it. If it already exists, // it revokes all its permissions and returns the existing group. func createGroup(c *gc.C, ec2conn *amzec2.EC2, name, descr string) amzec2.SecurityGroup { resp, err := ec2conn.CreateSecurityGroup(name, descr) if err == nil { return resp.SecurityGroup } if err.(*amzec2.Error).Code != "InvalidGroup.Duplicate" { c.Fatalf("cannot make group %q: %v", name, err) } // Found duplicate group, so revoke its permissions and return it. gresp, err := ec2conn.SecurityGroups(amzec2.SecurityGroupNames(name), nil) c.Assert(err, gc.IsNil) gi := gresp.Groups[0] if len(gi.IPPerms) > 0 { _, err = ec2conn.RevokeSecurityGroup(gi.SecurityGroup, gi.IPPerms) c.Assert(err, gc.IsNil) } return gi.SecurityGroup } func hasSecurityGroup(inst amzec2.Instance, group amzec2.SecurityGroup) bool { for _, instGroup := range inst.SecurityGroups { if instGroup.Id == group.Id { return true } } return false } ������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/image_test.go�����������������������������0000644�0000153�0000161�00000012171�12321735642�025642� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2 import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type imageSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&imageSuite{}) func (s *imageSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) UseTestImageData(TestImagesData) } func (s *imageSuite) TearDownSuite(c *gc.C) { UseTestImageData(nil) s.LoggingSuite.TearDownTest(c) } type specSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&specSuite{}) func (s *specSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) UseTestImageData(TestImagesData) UseTestInstanceTypeData(TestInstanceTypeCosts) UseTestRegionData(TestRegions) } func (s *specSuite) TearDownSuite(c *gc.C) { UseTestInstanceTypeData(nil) UseTestImageData(nil) UseTestRegionData(nil) s.LoggingSuite.TearDownSuite(c) } var findInstanceSpecTests = []struct { series string arches []string cons string itype string image string }{ { series: "precise", arches: both, itype: "m1.small", image: "ami-00000033", }, { series: "quantal", arches: both, itype: "m1.small", image: "ami-01000034", }, { series: "precise", arches: both, cons: "cpu-cores=4", itype: "m1.xlarge", image: "ami-00000033", }, { series: "precise", arches: both, cons: "cpu-cores=2 arch=i386", itype: "c1.medium", image: "ami-00000034", }, { series: "precise", arches: both, cons: "mem=10G", itype: "m1.xlarge", image: "ami-00000033", }, { series: "precise", arches: both, cons: "mem=", itype: "m1.small", image: "ami-00000033", }, { series: "precise", arches: both, cons: "cpu-power=", itype: "m1.small", image: "ami-00000033", }, { series: "precise", arches: both, cons: "cpu-power=800", itype: "m1.xlarge", image: "ami-00000033", }, { series: "precise", arches: both, cons: "cpu-power=500 arch=i386", itype: "c1.medium", image: "ami-00000034", }, { series: "precise", arches: []string{"i386"}, cons: "cpu-power=400", itype: "c1.medium", image: "ami-00000034", }, { series: "quantal", arches: both, cons: "arch=amd64", itype: "cc1.4xlarge", image: "ami-01000035", }, } func (s *specSuite) TestFindInstanceSpec(c *gc.C) { for i, t := range findInstanceSpecTests { c.Logf("test %d", i) stor := ebsStorage spec, err := findInstanceSpec( []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames)}, "released", &instances.InstanceConstraint{ Region: "test", Series: t.series, Arches: t.arches, Constraints: constraints.MustParse(t.cons), Storage: &stor, }) c.Assert(err, gc.IsNil) c.Check(spec.InstanceType.Name, gc.Equals, t.itype) c.Check(spec.Image.Id, gc.Equals, t.image) } } var findInstanceSpecErrorTests = []struct { series string arches []string cons string err string }{ { series: "bad", arches: both, err: `invalid series "bad"`, }, { series: "precise", arches: []string{"arm"}, err: `no "precise" images in test with arches \[arm\]`, }, { series: "raring", arches: both, cons: "mem=4G", err: `no "raring" images in test matching instance types \[m1.large m1.xlarge c1.xlarge cc1.4xlarge cc2.8xlarge\]`, }, } func (s *specSuite) TestFindInstanceSpecErrors(c *gc.C) { for i, t := range findInstanceSpecErrorTests { c.Logf("test %d", i) _, err := findInstanceSpec( []simplestreams.DataSource{ simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames)}, "released", &instances.InstanceConstraint{ Region: "test", Series: t.series, Arches: t.arches, Constraints: constraints.MustParse(t.cons), }) c.Check(err, gc.ErrorMatches, t.err) } } func (*specSuite) TestFilterImagesAcceptsNil(c *gc.C) { c.Check(filterImages(nil), gc.HasLen, 0) } func (*specSuite) TestFilterImagesAcceptsImageWithEBSStorage(c *gc.C) { input := []*imagemetadata.ImageMetadata{{Id: "yay", Storage: "ebs"}} c.Check(filterImages(input), gc.DeepEquals, input) } func (*specSuite) TestFilterImagesRejectsImageWithoutEBSStorage(c *gc.C) { input := []*imagemetadata.ImageMetadata{{Id: "boo", Storage: "ftp"}} c.Check(filterImages(input), gc.HasLen, 0) } func (*specSuite) TestFilterImagesReturnsSelectively(c *gc.C) { good := imagemetadata.ImageMetadata{Id: "good", Storage: "ebs"} bad := imagemetadata.ImageMetadata{Id: "bad", Storage: "ftp"} input := []*imagemetadata.ImageMetadata{&good, &bad} expectation := []*imagemetadata.ImageMetadata{&good} c.Check(filterImages(input), gc.DeepEquals, expectation) } func (*specSuite) TestFilterImagesMaintainsOrdering(c *gc.C) { input := []*imagemetadata.ImageMetadata{ {Id: "one", Storage: "ebs"}, {Id: "two", Storage: "ebs"}, {Id: "three", Storage: "ebs"}, } c.Check(filterImages(input), gc.DeepEquals, input) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/image.go����������������������������������0000644�0000153�0000161�00000005426�12321735642�024610� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2 import ( "fmt" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/simplestreams" ) // signedImageDataOnly is defined here to allow tests to override the content. // If true, only inline PGP signed image metadata will be used. var signedImageDataOnly = true // defaultCpuPower is larger the smallest instance's cpuPower, and no larger than // any other instance type's cpuPower. It is used when no explicit CpuPower // constraint exists, preventing the smallest instance from being chosen unless // the user has clearly indicated that they are willing to accept poor performance. const defaultCpuPower = 100 // filterImages returns only that subset of the input (in the same order) that // this provider finds suitable. func filterImages(images []*imagemetadata.ImageMetadata) []*imagemetadata.ImageMetadata { result := make([]*imagemetadata.ImageMetadata, 0, len(images)) for _, image := range images { // For now, we only want images with "ebs" storage. if image.Storage == ebsStorage { result = append(result, image) } } return result } // findInstanceSpec returns an InstanceSpec satisfying the supplied instanceConstraint. func findInstanceSpec( sources []simplestreams.DataSource, stream string, ic *instances.InstanceConstraint) (*instances.InstanceSpec, error) { if ic.Constraints.CpuPower == nil { ic.Constraints.CpuPower = instances.CpuPower(defaultCpuPower) } ec2Region := allRegions[ic.Region] imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ic.Region, ec2Region.EC2Endpoint}, Series: []string{ic.Series}, Arches: ic.Arches, Stream: stream, }) matchingImages, _, err := imagemetadata.Fetch( sources, simplestreams.DefaultIndexPath, imageConstraint, signedImageDataOnly) if err != nil { return nil, err } if len(matchingImages) == 0 { logger.Warningf("no matching image meta data for constraints: %v", ic) } suitableImages := filterImages(matchingImages) images := instances.ImageMetadataToImages(suitableImages) // Make a copy of the known EC2 instance types, filling in the cost for the specified region. regionCosts := allRegionCosts[ic.Region] if len(regionCosts) == 0 && len(allRegionCosts) > 0 { return nil, fmt.Errorf("no instance types found in %s", ic.Region) } var itypesWithCosts []instances.InstanceType for _, itype := range allInstanceTypes { cost, ok := regionCosts[itype.Name] if !ok { continue } itWithCost := itype itWithCost.Cost = cost itypesWithCosts = append(itypesWithCosts, itWithCost) } return instances.FindInstanceSpec(images, ic, itypesWithCosts) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/ec2/ec2.go������������������������������������0000644�0000153�0000161�00000071620�12321735776�024206� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ec2 import ( "fmt" "sync" "time" "github.com/juju/loggo" "launchpad.net/goamz/aws" "launchpad.net/goamz/ec2" "launchpad.net/goamz/s3" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" ) var logger = loggo.GetLogger("juju.provider.ec2") // Use shortAttempt to poll for short-term events. var shortAttempt = utils.AttemptStrategy{ Total: 5 * time.Second, Delay: 200 * time.Millisecond, } func init() { environs.RegisterProvider("ec2", environProvider{}) } type environProvider struct{} var providerInstance environProvider type environ struct { name string // archMutex gates access to supportedArchitectures archMutex sync.Mutex // supportedArchitectures caches the architectures // for which images can be instantiated. supportedArchitectures []string // ecfgMutex protects the *Unlocked fields below. ecfgMutex sync.Mutex ecfgUnlocked *environConfig ec2Unlocked *ec2.EC2 s3Unlocked *s3.S3 storageUnlocked storage.Storage } var _ environs.Environ = (*environ)(nil) var _ simplestreams.HasRegion = (*environ)(nil) var _ imagemetadata.SupportsCustomSources = (*environ)(nil) var _ envtools.SupportsCustomSources = (*environ)(nil) type ec2Instance struct { e *environ mu sync.Mutex *ec2.Instance } func (inst *ec2Instance) String() string { return string(inst.Id()) } var _ instance.Instance = (*ec2Instance)(nil) func (inst *ec2Instance) getInstance() *ec2.Instance { inst.mu.Lock() defer inst.mu.Unlock() return inst.Instance } func (inst *ec2Instance) Id() instance.Id { return instance.Id(inst.getInstance().InstanceId) } func (inst *ec2Instance) Status() string { return inst.getInstance().State.Name } // Refresh implements instance.Refresh(), requerying the // Instance details over the ec2 api func (inst *ec2Instance) Refresh() error { _, err := inst.refresh() return err } // refresh requeries Instance details over the ec2 api. func (inst *ec2Instance) refresh() (*ec2.Instance, error) { id := inst.Id() insts, err := inst.e.Instances([]instance.Id{id}) if err != nil { return nil, err } inst.mu.Lock() defer inst.mu.Unlock() inst.Instance = insts[0].(*ec2Instance).Instance return inst.Instance, nil } // Addresses implements instance.Addresses() returning generic address // details for the instance, and requerying the ec2 api if required. func (inst *ec2Instance) Addresses() ([]instance.Address, error) { // TODO(gz): Stop relying on this requerying logic, maybe remove error instInstance := inst.getInstance() if instInstance.DNSName == "" { // Fetch the instance information again, in case // the DNS information has become available. var err error instInstance, err = inst.refresh() if err != nil { return nil, err } } var addresses []instance.Address possibleAddresses := []instance.Address{ { Value: instInstance.DNSName, Type: instance.HostName, NetworkScope: instance.NetworkPublic, }, { Value: instInstance.PrivateDNSName, Type: instance.HostName, NetworkScope: instance.NetworkCloudLocal, }, { Value: instInstance.IPAddress, Type: instance.Ipv4Address, NetworkScope: instance.NetworkPublic, }, { Value: instInstance.PrivateIPAddress, Type: instance.Ipv4Address, NetworkScope: instance.NetworkCloudLocal, }, } for _, address := range possibleAddresses { if address.Value != "" { addresses = append(addresses, address) } } return addresses, nil } func (inst *ec2Instance) DNSName() (string, error) { addresses, err := inst.Addresses() if err != nil { return "", err } addr := instance.SelectPublicAddress(addresses) if addr == "" { return "", instance.ErrNoDNSName } return addr, nil } func (inst *ec2Instance) WaitDNSName() (string, error) { return common.WaitDNSName(inst) } func (p environProvider) BoilerplateConfig() string { return ` # https://juju.ubuntu.com/docs/config-aws.html amazon: type: ec2 # region specifies the EC2 region. It defaults to us-east-1. # # region: us-east-1 # access-key holds the EC2 access key. It defaults to the # environment variable AWS_ACCESS_KEY_ID. # # access-key: <secret> # secret-key holds the EC2 secret key. It defaults to the # environment variable AWS_SECRET_ACCESS_KEY. # # secret-key: <secret> # image-stream chooses a simplestreams stream to select OS images # from, for example daily or released images (or any other stream # available on simplestreams). # # image-stream: "released" `[1:] } func (p environProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Infof("opening environment %q", cfg.Name()) e := new(environ) e.name = cfg.Name() err := e.SetConfig(cfg) if err != nil { return nil, err } return e, nil } func (p environProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { attrs := cfg.UnknownAttrs() if _, ok := attrs["control-bucket"]; !ok { uuid, err := utils.NewUUID() if err != nil { return nil, err } attrs["control-bucket"] = fmt.Sprintf("%x", uuid.Raw()) } cfg, err := cfg.Apply(attrs) if err != nil { return nil, err } return p.Open(cfg) } // MetadataLookupParams returns parameters which are used to query image metadata to // find matching image information. func (p environProvider) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) { if region == "" { fmt.Errorf("region must be specified") } ec2Region, ok := allRegions[region] if !ok { return nil, fmt.Errorf("unknown region %q", region) } return &simplestreams.MetadataLookupParams{ Region: region, Endpoint: ec2Region.EC2Endpoint, Architectures: arch.AllSupportedArches, }, nil } func (environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { m := make(map[string]string) ecfg, err := providerInstance.newConfig(cfg) if err != nil { return nil, err } m["access-key"] = ecfg.accessKey() m["secret-key"] = ecfg.secretKey() return m, nil } func (e *environ) Config() *config.Config { return e.ecfg().Config } func (e *environ) SetConfig(cfg *config.Config) error { ecfg, err := providerInstance.newConfig(cfg) if err != nil { return err } e.ecfgMutex.Lock() defer e.ecfgMutex.Unlock() e.ecfgUnlocked = ecfg auth := aws.Auth{ecfg.accessKey(), ecfg.secretKey()} region := aws.Regions[ecfg.region()] e.ec2Unlocked = ec2.New(auth, region) e.s3Unlocked = s3.New(auth, region) // create new storage instances, existing instances continue // to reference their existing configuration. e.storageUnlocked = &ec2storage{ bucket: e.s3Unlocked.Bucket(ecfg.controlBucket()), } return nil } func (e *environ) ecfg() *environConfig { e.ecfgMutex.Lock() ecfg := e.ecfgUnlocked e.ecfgMutex.Unlock() return ecfg } func (e *environ) ec2() *ec2.EC2 { e.ecfgMutex.Lock() ec2 := e.ec2Unlocked e.ecfgMutex.Unlock() return ec2 } func (e *environ) s3() *s3.S3 { e.ecfgMutex.Lock() s3 := e.s3Unlocked e.ecfgMutex.Unlock() return s3 } func (e *environ) Name() string { return e.name } func (e *environ) Storage() storage.Storage { e.ecfgMutex.Lock() stor := e.storageUnlocked e.ecfgMutex.Unlock() return stor } func (e *environ) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { return common.Bootstrap(ctx, e, cons) } func (e *environ) StateInfo() (*state.Info, *api.Info, error) { return common.StateInfo(e) } // SupportedArchitectures is specified on the EnvironCapability interface. func (e *environ) SupportedArchitectures() ([]string, error) { e.archMutex.Lock() defer e.archMutex.Unlock() if e.supportedArchitectures != nil { return e.supportedArchitectures, nil } // Create a filter to get all images from our region and for the correct stream. cloudSpec, err := e.Region() if err != nil { return nil, err } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: cloudSpec, Stream: e.Config().ImageStream(), }) e.supportedArchitectures, err = common.SupportedArchitectures(e, imageConstraint) return e.supportedArchitectures, err } // MetadataLookupParams returns parameters which are used to query simplestreams metadata. func (e *environ) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) { if region == "" { region = e.ecfg().region() } cloudSpec, err := e.cloudSpec(region) if err != nil { return nil, err } return &simplestreams.MetadataLookupParams{ Series: config.PreferredSeries(e.ecfg()), Region: cloudSpec.Region, Endpoint: cloudSpec.Endpoint, Architectures: arch.AllSupportedArches, }, nil } // Region is specified in the HasRegion interface. func (e *environ) Region() (simplestreams.CloudSpec, error) { return e.cloudSpec(e.ecfg().region()) } func (e *environ) cloudSpec(region string) (simplestreams.CloudSpec, error) { ec2Region, ok := allRegions[region] if !ok { return simplestreams.CloudSpec{}, fmt.Errorf("unknown region %q", region) } return simplestreams.CloudSpec{ Region: region, Endpoint: ec2Region.EC2Endpoint, }, nil } const ebsStorage = "ebs" // StartInstance is specified in the InstanceBroker interface. func (e *environ) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { arches := args.Tools.Arches() stor := ebsStorage sources, err := imagemetadata.GetMetadataSources(e) if err != nil { return nil, nil, err } series := args.Tools.OneSeries() spec, err := findInstanceSpec(sources, e.Config().ImageStream(), &instances.InstanceConstraint{ Region: e.ecfg().region(), Series: series, Arches: arches, Constraints: args.Constraints, Storage: &stor, }) if err != nil { return nil, nil, err } tools, err := args.Tools.Match(tools.Filter{Arch: spec.Image.Arch}) if err != nil { return nil, nil, fmt.Errorf("chosen architecture %v not present in %v", spec.Image.Arch, arches) } args.MachineConfig.Tools = tools[0] if err := environs.FinishMachineConfig(args.MachineConfig, e.Config(), args.Constraints); err != nil { return nil, nil, err } userData, err := environs.ComposeUserData(args.MachineConfig, nil) if err != nil { return nil, nil, fmt.Errorf("cannot make user data: %v", err) } logger.Debugf("ec2 user data; %d bytes", len(userData)) cfg := e.Config() groups, err := e.setUpGroups(args.MachineConfig.MachineId, cfg.StatePort(), cfg.APIPort()) if err != nil { return nil, nil, fmt.Errorf("cannot set up groups: %v", err) } var instResp *ec2.RunInstancesResp device, diskSize := getDiskSize(args.Constraints) for a := shortAttempt.Start(); a.Next(); { instResp, err = e.ec2().RunInstances(&ec2.RunInstances{ ImageId: spec.Image.Id, MinCount: 1, MaxCount: 1, UserData: userData, InstanceType: spec.InstanceType.Name, SecurityGroups: groups, BlockDeviceMappings: []ec2.BlockDeviceMapping{device}, }) if err == nil || ec2ErrCode(err) != "InvalidGroup.NotFound" { break } } if err != nil { return nil, nil, fmt.Errorf("cannot run instances: %v", err) } if len(instResp.Instances) != 1 { return nil, nil, fmt.Errorf("expected 1 started instance, got %d", len(instResp.Instances)) } inst := &ec2Instance{ e: e, Instance: &instResp.Instances[0], } logger.Infof("started instance %q", inst.Id()) hc := instance.HardwareCharacteristics{ Arch: &spec.Image.Arch, Mem: &spec.InstanceType.Mem, CpuCores: &spec.InstanceType.CpuCores, CpuPower: spec.InstanceType.CpuPower, RootDisk: &diskSize, // Tags currently not supported by EC2 } return inst, &hc, nil } func (e *environ) StopInstances(insts []instance.Instance) error { ids := make([]instance.Id, len(insts)) for i, inst := range insts { ids[i] = inst.(*ec2Instance).Id() } return e.terminateInstances(ids) } // minDiskSize is the minimum/default size (in megabytes) for ec2 root disks. const minDiskSize uint64 = 8 * 1024 // getDiskSize translates a RootDisk constraint (or lackthereof) into a // BlockDeviceMapping request for EC2. megs is the size in megabytes of // the disk that was requested. func getDiskSize(cons constraints.Value) (dvc ec2.BlockDeviceMapping, megs uint64) { diskSize := minDiskSize if cons.RootDisk != nil { if *cons.RootDisk >= minDiskSize { diskSize = *cons.RootDisk } else { logger.Infof("Ignoring root-disk constraint of %dM because it is smaller than the EC2 image size of %dM", *cons.RootDisk, minDiskSize) } } // AWS's volume size is in gigabytes, root-disk is in megabytes, // so round up to the nearest gigabyte. volsize := int64((diskSize + 1023) / 1024) return ec2.BlockDeviceMapping{ DeviceName: "/dev/sda1", VolumeSize: volsize, }, uint64(volsize * 1024) } // groupInfoByName returns information on the security group // with the given name including rules and other details. func (e *environ) groupInfoByName(groupName string) (ec2.SecurityGroupInfo, error) { // Non-default VPC does not support name-based group lookups, can // use a filter by group name instead when support is needed. limitToGroups := []ec2.SecurityGroup{{Name: groupName}} resp, err := e.ec2().SecurityGroups(limitToGroups, nil) if err != nil { return ec2.SecurityGroupInfo{}, err } if len(resp.Groups) != 1 { return ec2.SecurityGroupInfo{}, fmt.Errorf("expected one security group named %q, got %v", groupName, resp.Groups) } return resp.Groups[0], nil } // groupByName returns the security group with the given name. func (e *environ) groupByName(groupName string) (ec2.SecurityGroup, error) { groupInfo, err := e.groupInfoByName(groupName) return groupInfo.SecurityGroup, err } // addGroupFilter sets a limit an instance filter so only those machines // with the juju environment wide security group associated will be listed. // // An EC2 API call is required to resolve the group name to an id, as VPC // enabled accounts do not support name based filtering. // TODO: Detect classic accounts and just filter by name for those. // // Callers must handle InvalidGroup.NotFound errors to mean the same as no // matching instances. func (e *environ) addGroupFilter(filter *ec2.Filter) error { groupName := e.jujuGroupName() group, err := e.groupByName(groupName) if err != nil { return err } // EC2 should support filtering with and without the 'instance.' // prefix, but only the form with seems to work with default VPC. filter.Add("instance.group-id", group.Id) return nil } // gatherInstances tries to get information on each instance // id whose corresponding insts slot is nil. // It returns environs.ErrPartialInstances if the insts // slice has not been completely filled. func (e *environ) gatherInstances(ids []instance.Id, insts []instance.Instance) error { var need []string for i, inst := range insts { if inst == nil { need = append(need, string(ids[i])) } } if len(need) == 0 { return nil } filter := ec2.NewFilter() filter.Add("instance-state-name", "pending", "running") err := e.addGroupFilter(filter) if err != nil { if ec2ErrCode(err) == "InvalidGroup.NotFound" { return environs.ErrPartialInstances } return err } filter.Add("instance-id", need...) resp, err := e.ec2().Instances(nil, filter) if err != nil { return err } n := 0 // For each requested id, add it to the returned instances // if we find it in the response. for i, id := range ids { if insts[i] != nil { continue } for j := range resp.Reservations { r := &resp.Reservations[j] for k := range r.Instances { if r.Instances[k].InstanceId == string(id) { inst := r.Instances[k] // TODO(wallyworld): lookup the details to fill in the instance type data insts[i] = &ec2Instance{e: e, Instance: &inst} n++ } } } } if n < len(ids) { return environs.ErrPartialInstances } return nil } func (e *environ) Instances(ids []instance.Id) ([]instance.Instance, error) { if len(ids) == 0 { return nil, nil } insts := make([]instance.Instance, len(ids)) // Make a series of requests to cope with eventual consistency. // Each request will attempt to add more instances to the requested // set. var err error for a := shortAttempt.Start(); a.Next(); { err = e.gatherInstances(ids, insts) if err == nil || err != environs.ErrPartialInstances { break } } if err == environs.ErrPartialInstances { for _, inst := range insts { if inst != nil { return insts, environs.ErrPartialInstances } } return nil, environs.ErrNoInstances } if err != nil { return nil, err } return insts, nil } func (e *environ) AllInstances() ([]instance.Instance, error) { filter := ec2.NewFilter() filter.Add("instance-state-name", "pending", "running") err := e.addGroupFilter(filter) if err != nil { if ec2ErrCode(err) == "InvalidGroup.NotFound" { return nil, nil } return nil, err } resp, err := e.ec2().Instances(nil, filter) if err != nil { return nil, err } var insts []instance.Instance for _, r := range resp.Reservations { for i := range r.Instances { inst := r.Instances[i] // TODO(wallyworld): lookup the details to fill in the instance type data insts = append(insts, &ec2Instance{e: e, Instance: &inst}) } } return insts, nil } func (e *environ) Destroy() error { return common.Destroy(e) } func portsToIPPerms(ports []instance.Port) []ec2.IPPerm { ipPerms := make([]ec2.IPPerm, len(ports)) for i, p := range ports { ipPerms[i] = ec2.IPPerm{ Protocol: p.Protocol, FromPort: p.Number, ToPort: p.Number, SourceIPs: []string{"0.0.0.0/0"}, } } return ipPerms } func (e *environ) openPortsInGroup(name string, ports []instance.Port) error { if len(ports) == 0 { return nil } // Give permissions for anyone to access the given ports. g, err := e.groupByName(name) if err != nil { return err } ipPerms := portsToIPPerms(ports) _, err = e.ec2().AuthorizeSecurityGroup(g, ipPerms) if err != nil && ec2ErrCode(err) == "InvalidPermission.Duplicate" { if len(ports) == 1 { return nil } // If there's more than one port and we get a duplicate error, // then we go through authorizing each port individually, // otherwise the ports that were *not* duplicates will have // been ignored for i := range ipPerms { _, err := e.ec2().AuthorizeSecurityGroup(g, ipPerms[i:i+1]) if err != nil && ec2ErrCode(err) != "InvalidPermission.Duplicate" { return fmt.Errorf("cannot open port %v: %v", ipPerms[i], err) } } return nil } if err != nil { return fmt.Errorf("cannot open ports: %v", err) } return nil } func (e *environ) closePortsInGroup(name string, ports []instance.Port) error { if len(ports) == 0 { return nil } // Revoke permissions for anyone to access the given ports. // Note that ec2 allows the revocation of permissions that aren't // granted, so this is naturally idempotent. g, err := e.groupByName(name) if err != nil { return err } _, err = e.ec2().RevokeSecurityGroup(g, portsToIPPerms(ports)) if err != nil { return fmt.Errorf("cannot close ports: %v", err) } return nil } func (e *environ) portsInGroup(name string) (ports []instance.Port, err error) { group, err := e.groupInfoByName(name) if err != nil { return nil, err } for _, p := range group.IPPerms { if len(p.SourceIPs) != 1 { logger.Warningf("unexpected IP permission found: %v", p) continue } for i := p.FromPort; i <= p.ToPort; i++ { ports = append(ports, instance.Port{ Protocol: p.Protocol, Number: i, }) } } instance.SortPorts(ports) return ports, nil } func (e *environ) OpenPorts(ports []instance.Port) error { if e.Config().FirewallMode() != config.FwGlobal { return fmt.Errorf("invalid firewall mode %q for opening ports on environment", e.Config().FirewallMode()) } if err := e.openPortsInGroup(e.globalGroupName(), ports); err != nil { return err } logger.Infof("opened ports in global group: %v", ports) return nil } func (e *environ) ClosePorts(ports []instance.Port) error { if e.Config().FirewallMode() != config.FwGlobal { return fmt.Errorf("invalid firewall mode %q for closing ports on environment", e.Config().FirewallMode()) } if err := e.closePortsInGroup(e.globalGroupName(), ports); err != nil { return err } logger.Infof("closed ports in global group: %v", ports) return nil } func (e *environ) Ports() ([]instance.Port, error) { if e.Config().FirewallMode() != config.FwGlobal { return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from environment", e.Config().FirewallMode()) } return e.portsInGroup(e.globalGroupName()) } func (*environ) Provider() environs.EnvironProvider { return &providerInstance } func (e *environ) terminateInstances(ids []instance.Id) error { if len(ids) == 0 { return nil } var err error ec2inst := e.ec2() strs := make([]string, len(ids)) for i, id := range ids { strs[i] = string(id) } for a := shortAttempt.Start(); a.Next(); { _, err = ec2inst.TerminateInstances(strs) if err == nil || ec2ErrCode(err) != "InvalidInstanceID.NotFound" { return err } } if len(ids) == 1 { return err } // If we get a NotFound error, it means that no instances have been // terminated even if some exist, so try them one by one, ignoring // NotFound errors. var firstErr error for _, id := range ids { _, err = ec2inst.TerminateInstances([]string{string(id)}) if ec2ErrCode(err) == "InvalidInstanceID.NotFound" { err = nil } if err != nil && firstErr == nil { firstErr = err } } return firstErr } func (e *environ) globalGroupName() string { return fmt.Sprintf("%s-global", e.jujuGroupName()) } func (e *environ) machineGroupName(machineId string) string { return fmt.Sprintf("%s-%s", e.jujuGroupName(), machineId) } func (e *environ) jujuGroupName() string { return "juju-" + e.name } func (inst *ec2Instance) OpenPorts(machineId string, ports []instance.Port) error { if inst.e.Config().FirewallMode() != config.FwInstance { return fmt.Errorf("invalid firewall mode %q for opening ports on instance", inst.e.Config().FirewallMode()) } name := inst.e.machineGroupName(machineId) if err := inst.e.openPortsInGroup(name, ports); err != nil { return err } logger.Infof("opened ports in security group %s: %v", name, ports) return nil } func (inst *ec2Instance) ClosePorts(machineId string, ports []instance.Port) error { if inst.e.Config().FirewallMode() != config.FwInstance { return fmt.Errorf("invalid firewall mode %q for closing ports on instance", inst.e.Config().FirewallMode()) } name := inst.e.machineGroupName(machineId) if err := inst.e.closePortsInGroup(name, ports); err != nil { return err } logger.Infof("closed ports in security group %s: %v", name, ports) return nil } func (inst *ec2Instance) Ports(machineId string) ([]instance.Port, error) { if inst.e.Config().FirewallMode() != config.FwInstance { return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from instance", inst.e.Config().FirewallMode()) } name := inst.e.machineGroupName(machineId) return inst.e.portsInGroup(name) } // setUpGroups creates the security groups for the new machine, and // returns them. // // Instances are tagged with a group so they can be distinguished from // other instances that might be running on the same EC2 account. In // addition, a specific machine security group is created for each // machine, so that its firewall rules can be configured per machine. func (e *environ) setUpGroups(machineId string, statePort, apiPort int) ([]ec2.SecurityGroup, error) { jujuGroup, err := e.ensureGroup(e.jujuGroupName(), []ec2.IPPerm{ { Protocol: "tcp", FromPort: 22, ToPort: 22, SourceIPs: []string{"0.0.0.0/0"}, }, { Protocol: "tcp", FromPort: statePort, ToPort: statePort, SourceIPs: []string{"0.0.0.0/0"}, }, { Protocol: "tcp", FromPort: apiPort, ToPort: apiPort, SourceIPs: []string{"0.0.0.0/0"}, }, { Protocol: "tcp", FromPort: 0, ToPort: 65535, }, { Protocol: "udp", FromPort: 0, ToPort: 65535, }, { Protocol: "icmp", FromPort: -1, ToPort: -1, }, }) if err != nil { return nil, err } var machineGroup ec2.SecurityGroup switch e.Config().FirewallMode() { case config.FwInstance: machineGroup, err = e.ensureGroup(e.machineGroupName(machineId), nil) case config.FwGlobal: machineGroup, err = e.ensureGroup(e.globalGroupName(), nil) } if err != nil { return nil, err } return []ec2.SecurityGroup{jujuGroup, machineGroup}, nil } // zeroGroup holds the zero security group. var zeroGroup ec2.SecurityGroup // ensureGroup returns the security group with name and perms. // If a group with name does not exist, one will be created. // If it exists, its permissions are set to perms. // Any entries in perms without SourceIPs will be granted for // the named group only. func (e *environ) ensureGroup(name string, perms []ec2.IPPerm) (g ec2.SecurityGroup, err error) { ec2inst := e.ec2() resp, err := ec2inst.CreateSecurityGroup(name, "juju group") if err != nil && ec2ErrCode(err) != "InvalidGroup.Duplicate" { return zeroGroup, err } var have permSet if err == nil { g = resp.SecurityGroup } else { resp, err := ec2inst.SecurityGroups(ec2.SecurityGroupNames(name), nil) if err != nil { return zeroGroup, err } info := resp.Groups[0] // It's possible that the old group has the wrong // description here, but if it does it's probably due // to something deliberately playing games with juju, // so we ignore it. g = info.SecurityGroup have = newPermSetForGroup(info.IPPerms, g) } want := newPermSetForGroup(perms, g) revoke := make(permSet) for p := range have { if !want[p] { revoke[p] = true } } if len(revoke) > 0 { _, err := ec2inst.RevokeSecurityGroup(g, revoke.ipPerms()) if err != nil { return zeroGroup, fmt.Errorf("cannot revoke security group: %v", err) } } add := make(permSet) for p := range want { if !have[p] { add[p] = true } } if len(add) > 0 { _, err := ec2inst.AuthorizeSecurityGroup(g, add.ipPerms()) if err != nil { return zeroGroup, fmt.Errorf("cannot authorize securityGroup: %v", err) } } return g, nil } // permKey represents a permission for a group or an ip address range // to access the given range of ports. Only one of groupName or ipAddr // should be non-empty. type permKey struct { protocol string fromPort int toPort int groupId string ipAddr string } type permSet map[permKey]bool // newPermSetForGroup returns a set of all the permissions in the // given slice of IPPerms. It ignores the name and owner // id in source groups, and any entry with no source ips will // be granted for the given group only. func newPermSetForGroup(ps []ec2.IPPerm, group ec2.SecurityGroup) permSet { m := make(permSet) for _, p := range ps { k := permKey{ protocol: p.Protocol, fromPort: p.FromPort, toPort: p.ToPort, } if len(p.SourceIPs) > 0 { for _, ip := range p.SourceIPs { k.ipAddr = ip m[k] = true } } else { k.groupId = group.Id m[k] = true } } return m } // ipPerms returns m as a slice of permissions usable // with the ec2 package. func (m permSet) ipPerms() (ps []ec2.IPPerm) { // We could compact the permissions, but it // hardly seems worth it. for p := range m { ipp := ec2.IPPerm{ Protocol: p.protocol, FromPort: p.fromPort, ToPort: p.toPort, } if p.ipAddr != "" { ipp.SourceIPs = []string{p.ipAddr} } else { ipp.SourceGroups = []ec2.UserSecurityGroup{{Id: p.groupId}} } ps = append(ps, ipp) } return } // If the err is of type *ec2.Error, ec2ErrCode returns // its code, otherwise it returns the empty string. func ec2ErrCode(err error) string { ec2err, _ := err.(*ec2.Error) if ec2err == nil { return "" } return ec2err.Code } // GetImageSources returns a list of sources which are used to search for simplestreams image metadata. func (e *environ) GetImageSources() ([]simplestreams.DataSource, error) { // Add the simplestreams source off the control bucket. sources := []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseImagesPath)} return sources, nil } // GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata. func (e *environ) GetToolsSources() ([]simplestreams.DataSource, error) { // Add the simplestreams source off the control bucket. sources := []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)} return sources, nil } ����������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/dummy/����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023637� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/dummy/config_test.go��������������������������0000644�0000153�0000161�00000004726�12321735642�026516� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package dummy_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) var _ = gc.Suite(&ConfigSuite{}) type ConfigSuite struct { testbase.LoggingSuite } func (s *ConfigSuite) TearDownTest(c *gc.C) { s.LoggingSuite.TearDownTest(c) dummy.Reset() } func (*ConfigSuite) TestSecretAttrs(c *gc.C) { attrs := dummy.SampleConfig().Delete("secret") cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) ctx := testing.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) c.Assert(err, gc.IsNil) defer env.Destroy() expected := map[string]string{ "secret": "pork", } actual, err := env.Provider().SecretAttrs(cfg) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, expected) } var firewallModeTests = []struct { configFirewallMode string firewallMode string errorMsg string }{ { // Empty value leads to default value. firewallMode: config.FwInstance, }, { // Explicit default value. configFirewallMode: "", firewallMode: config.FwInstance, }, { // Instance mode. configFirewallMode: "instance", firewallMode: config.FwInstance, }, { // Global mode. configFirewallMode: "global", firewallMode: config.FwGlobal, }, { // Invalid mode. configFirewallMode: "invalid", errorMsg: `invalid firewall mode in environment configuration: "invalid"`, }, } func (s *ConfigSuite) TestFirewallMode(c *gc.C) { for i, test := range firewallModeTests { c.Logf("test %d: %s", i, test.configFirewallMode) attrs := dummy.SampleConfig() if test.configFirewallMode != "" { attrs = attrs.Merge(testing.Attrs{ "firewall-mode": test.configFirewallMode, }) } cfg, err := config.New(config.NoDefaults, attrs) if err != nil { c.Assert(err, gc.ErrorMatches, test.errorMsg) continue } ctx := testing.Context(c) env, err := environs.Prepare(cfg, ctx, configstore.NewMem()) if test.errorMsg != "" { c.Assert(err, gc.ErrorMatches, test.errorMsg) continue } c.Assert(err, gc.IsNil) defer env.Destroy() firewallMode := env.Config().FirewallMode() c.Assert(firewallMode, gc.Equals, test.firewallMode) s.TearDownTest(c) } } ������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/dummy/environs_test.go������������������������0000644�0000153�0000161�00000001323�12321735642�027102� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package dummy_test import ( stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/provider/dummy" "launchpad.net/juju-core/testing" ) func TestPackage(t *stdtesting.T) { testing.MgoTestPackage(t) } func init() { gc.Suite(&jujutest.LiveTests{ TestConfig: dummy.SampleConfig(), CanOpenState: true, HasProvisioner: false, }) gc.Suite(&suite{ Tests: jujutest.Tests{ TestConfig: dummy.SampleConfig(), }, }) } type suite struct { jujutest.Tests } func (s *suite) TearDownTest(c *gc.C) { s.Tests.TearDownTest(c) dummy.Reset() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/dummy/storage.go������������������������������0000644�0000153�0000161�00000013423�12321735642�025650� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package dummy import ( "bytes" "fmt" "io" "io/ioutil" "net/http" "sort" "strings" "time" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" ) // IsSameStorage returns whether the storage instances are the same. // Both storages must have been created through the dummy provider. func IsSameStorage(s1, s2 storage.Storage) bool { localS1, localS2 := s1.(*dummyStorage), s2.(*dummyStorage) return localS1.env.name == localS2.env.name } func (e *environ) Storage() storage.Storage { return &dummyStorage{env: e} } // storageServer holds the storage for an environState. type storageServer struct { path string // path prefix in http space. state *environState files map[string][]byte poisoned map[string]error } func newStorageServer(state *environState, path string) *storageServer { return &storageServer{ state: state, files: make(map[string][]byte), path: path, poisoned: make(map[string]error), } } // Poison causes all fetches of the given path to // return the given error. func Poison(ss storage.Storage, path string, poisonErr error) { s := ss.(*dummyStorage) srv, err := s.server() if err != nil { panic("cannot poison destroyed storage") } srv.state.mu.Lock() srv.poisoned[path] = poisonErr srv.state.mu.Unlock() } func (s *storageServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { if req.Method != "GET" { http.Error(w, "only GET is supported", http.StatusMethodNotAllowed) return } data, err := s.dataWithDelay(req.URL.Path) if err != nil { http.Error(w, "404 "+err.Error(), http.StatusNotFound) return } w.Header().Set("Content-Type", "application/octet-stream") // If the write fails, the rest of the tests should pick up the problem. // It's more likely because the client has legitimately dropped the // connection. w.Write(data) } // dataWithDelay returns the data for the given path, // waiting for the configured amount of time before // accessing it. func (s *storageServer) dataWithDelay(path string) (data []byte, err error) { s.state.mu.Lock() delay := s.state.storageDelay s.state.mu.Unlock() time.Sleep(delay) s.state.mu.Lock() defer s.state.mu.Unlock() if err := s.poisoned[path]; err != nil { return nil, err } data, ok := s.files[path] if !ok { return nil, errors.NotFoundf("file %q not found", path) } return data, nil } func (s *storageServer) Put(name string, r io.Reader, length int64) error { // Allow Put to be poisoned as well. if err := s.poisoned[name]; err != nil { return err } // We only log Put requests on private storage. if strings.HasSuffix(s.path, "/private") { s.state.ops <- OpPutFile{s.state.name, name} } var buf bytes.Buffer _, err := io.Copy(&buf, r) if err != nil { return err } s.state.mu.Lock() s.files[name] = buf.Bytes() s.state.mu.Unlock() return nil } func (s *storageServer) Get(name string) (io.ReadCloser, error) { data, err := s.dataWithDelay(name) if err != nil { return nil, err } return ioutil.NopCloser(bytes.NewBuffer(data)), nil } func (s *storageServer) URL(name string) (string, error) { // Mimic the MAAS behaviour so we are testing with the // lowest common denominator. if name != "" { if _, ok := s.files[name]; !ok { found := false for file, _ := range s.files { found = strings.HasPrefix(file, name+"/") if found { break } } if !found { return "", errors.NotFoundf(name) } } } return fmt.Sprintf("http://%v%s/%s", s.state.httpListener.Addr(), s.path, name), nil } func (s *storageServer) Remove(name string) error { s.state.mu.Lock() delete(s.files, name) s.state.mu.Unlock() return nil } func (s *storageServer) DefaultConsistencyStrategy() utils.AttemptStrategy { return utils.AttemptStrategy{} } // ShouldRetry is specified in the StorageReader interface. func (s *storageServer) ShouldRetry(err error) bool { return false } func (s *storageServer) RemoveAll() error { s.state.mu.Lock() s.files = make(map[string][]byte) s.state.mu.Unlock() return nil } func (s *storageServer) List(prefix string) ([]string, error) { s.state.mu.Lock() defer s.state.mu.Unlock() var names []string for name := range s.files { if strings.HasPrefix(name, prefix) { names = append(names, name) } } sort.Strings(names) return names, nil } // dummyStorage implements the client side of the Storage interface. type dummyStorage struct { env *environ } // server returns the server side of the given storage. func (s *dummyStorage) server() (*storageServer, error) { st, err := s.env.state() if err != nil { return nil, err } return st.storage, nil } func (s *dummyStorage) Get(name string) (io.ReadCloser, error) { srv, err := s.server() if err != nil { return nil, err } return srv.Get(name) } func (s *dummyStorage) URL(name string) (string, error) { srv, err := s.server() if err != nil { return "", err } return srv.URL(name) } func (s *dummyStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { return utils.AttemptStrategy{} } // ShouldRetry is specified in the StorageReader interface. func (s *dummyStorage) ShouldRetry(err error) bool { return false } func (s *dummyStorage) Put(name string, r io.Reader, length int64) error { srv, err := s.server() if err != nil { return err } return srv.Put(name, r, length) } func (s *dummyStorage) Remove(name string) error { srv, err := s.server() if err != nil { return err } return srv.Remove(name) } func (s *dummyStorage) RemoveAll() error { srv, err := s.server() if err != nil { return err } return srv.RemoveAll() } func (s *dummyStorage) List(prefix string) ([]string, error) { srv, err := s.server() if err != nil { return nil, err } return srv.List(prefix) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/dummy/environs.go�����������������������������0000644�0000153�0000161�00000064525�12321735776�026070� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The dummy provider implements an environment provider for testing // purposes, registered with environs under the name "dummy". // // The configuration YAML for the testing environment // must specify a "state-server" property with a boolean // value. If this is true, a state server will be started // the first time StateInfo is called on a newly reset environment. // // The configuration data also accepts a "broken" property // of type boolean. If this is non-empty, any operation // after the environment has been opened will return // the error "broken environment", and will also log that. // // The DNS name of instances is the same as the Id, // with ".dns" appended. // // To avoid enumerating all possible series and architectures, // any series or architecture with the prefix "unknown" is // treated as bad when starting a new instance. package dummy import ( "errors" "fmt" "net" "net/http" "os" "strconv" "strings" "sync" "time" "github.com/juju/loggo" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/names" "launchpad.net/juju-core/provider" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/schema" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/apiserver" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) var logger = loggo.GetLogger("juju.provider.dummy") // SampleConfig() returns an environment configuration with all required // attributes set. func SampleConfig() testing.Attrs { return testing.Attrs{ "type": "dummy", "name": "only", "authorized-keys": testing.FakeAuthKeys, "firewall-mode": config.FwInstance, "admin-secret": testing.DefaultMongoPassword, "ca-cert": testing.CACert, "ca-private-key": testing.CAKey, "ssl-hostname-verification": true, "development": false, "state-port": 1234, "api-port": 4321, "syslog-port": 2345, "default-series": "precise", "secret": "pork", "state-server": true, } } // stateInfo returns a *state.Info which allows clients to connect to the // shared dummy state, if it exists. func stateInfo() *state.Info { if testing.MgoServer.Addr() == "" { panic("dummy environ state tests must be run with MgoTestPackage") } return &state.Info{ Addrs: []string{testing.MgoServer.Addr()}, CACert: []byte(testing.CACert), } } // Operation represents an action on the dummy provider. type Operation interface{} type OpBootstrap struct { Context environs.BootstrapContext Env string Constraints constraints.Value } type OpDestroy struct { Env string Error error } type OpStartInstance struct { Env string MachineId string MachineNonce string Instance instance.Instance Constraints constraints.Value Info *state.Info APIInfo *api.Info Secret string } type OpStopInstances struct { Env string Instances []instance.Instance } type OpOpenPorts struct { Env string MachineId string InstanceId instance.Id Ports []instance.Port } type OpClosePorts struct { Env string MachineId string InstanceId instance.Id Ports []instance.Port } type OpPutFile struct { Env string FileName string } // environProvider represents the dummy provider. There is only ever one // instance of this type (providerInstance) type environProvider struct { mu sync.Mutex ops chan<- Operation statePolicy state.Policy // We have one state for each environment name state map[int]*environState maxStateId int } var providerInstance environProvider const noStateId = 0 // environState represents the state of an environment. // It can be shared between several environ values, // so that a given environment can be opened several times. type environState struct { id int name string ops chan<- Operation statePolicy state.Policy mu sync.Mutex maxId int // maximum instance id allocated so far. insts map[instance.Id]*dummyInstance globalPorts map[instance.Port]bool bootstrapped bool storageDelay time.Duration storage *storageServer httpListener net.Listener apiServer *apiserver.Server apiState *state.State } // environ represents a client's connection to a given environment's // state. type environ struct { name string ecfgMutex sync.Mutex ecfgUnlocked *environConfig } var _ imagemetadata.SupportsCustomSources = (*environ)(nil) var _ tools.SupportsCustomSources = (*environ)(nil) var _ environs.Environ = (*environ)(nil) // discardOperations discards all Operations written to it. var discardOperations chan<- Operation func init() { environs.RegisterProvider("dummy", &providerInstance) // Prime the first ops channel, so that naive clients can use // the testing environment by simply importing it. c := make(chan Operation) go func() { for _ = range c { } }() discardOperations = c Reset() // parse errors are ignored providerDelay, _ = time.ParseDuration(os.Getenv("JUJU_DUMMY_DELAY")) } // Reset resets the entire dummy environment and forgets any registered // operation listener. All opened environments after Reset will share // the same underlying state. func Reset() { logger.Infof("reset environment") p := &providerInstance p.mu.Lock() defer p.mu.Unlock() providerInstance.ops = discardOperations for _, s := range p.state { s.httpListener.Close() s.destroy() } providerInstance.state = make(map[int]*environState) if testing.MgoServer.Addr() != "" { testing.MgoServer.Reset() } providerInstance.statePolicy = environs.NewStatePolicy() } func (state *environState) destroy() { state.storage.files = make(map[string][]byte) if !state.bootstrapped { return } if state.apiServer != nil { if err := state.apiServer.Stop(); err != nil { panic(err) } state.apiServer = nil if err := state.apiState.Close(); err != nil { panic(err) } state.apiState = nil } if testing.MgoServer.Addr() != "" { testing.MgoServer.Reset() } state.bootstrapped = false } // GetStateInAPIServer returns the state connection used by the API server // This is so code in the test suite can trigger Syncs, etc that the API server // will see, which will then trigger API watchers, etc. func (e *environ) GetStateInAPIServer() *state.State { st, err := e.state() if err != nil { panic(err) } return st.apiState } // newState creates the state for a new environment with the // given name and starts an http server listening for // storage requests. func newState(name string, ops chan<- Operation, policy state.Policy) *environState { s := &environState{ name: name, ops: ops, statePolicy: policy, insts: make(map[instance.Id]*dummyInstance), globalPorts: make(map[instance.Port]bool), } s.storage = newStorageServer(s, "/"+name+"/private") s.listen() return s } // listen starts a network listener listening for http // requests to retrieve files in the state's storage. func (s *environState) listen() { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { panic(fmt.Errorf("cannot start listener: %v", err)) } s.httpListener = l mux := http.NewServeMux() mux.Handle(s.storage.path+"/", http.StripPrefix(s.storage.path+"/", s.storage)) go http.Serve(l, mux) } // SetStatePolicy sets the state.Policy to use when a // state server is initialised by dummy. func SetStatePolicy(policy state.Policy) { p := &providerInstance p.mu.Lock() defer p.mu.Unlock() p.statePolicy = policy } // Listen closes the previously registered listener (if any). // Subsequent operations on any dummy environment can be received on c // (if not nil). func Listen(c chan<- Operation) { p := &providerInstance p.mu.Lock() defer p.mu.Unlock() if c == nil { c = discardOperations } if p.ops != discardOperations { close(p.ops) } p.ops = c for _, st := range p.state { st.mu.Lock() st.ops = c st.mu.Unlock() } } // SetStorageDelay causes any storage download operation in any current // environment to be delayed for the given duration. func SetStorageDelay(d time.Duration) { p := &providerInstance p.mu.Lock() defer p.mu.Unlock() for _, st := range p.state { st.mu.Lock() st.storageDelay = d st.mu.Unlock() } } var configFields = schema.Fields{ "state-server": schema.Bool(), "broken": schema.String(), "secret": schema.String(), "state-id": schema.String(), } var configDefaults = schema.Defaults{ "broken": "", "secret": "pork", "state-id": schema.Omit, } type environConfig struct { *config.Config attrs map[string]interface{} } func (c *environConfig) stateServer() bool { return c.attrs["state-server"].(bool) } func (c *environConfig) broken() string { return c.attrs["broken"].(string) } func (c *environConfig) secret() string { return c.attrs["secret"].(string) } func (c *environConfig) stateId() int { idStr, ok := c.attrs["state-id"].(string) if !ok { return noStateId } id, err := strconv.Atoi(idStr) if err != nil { panic(fmt.Errorf("unexpected state-id %q (should have pre-checked)", idStr)) } return id } func (p *environProvider) newConfig(cfg *config.Config) (*environConfig, error) { valid, err := p.Validate(cfg, nil) if err != nil { return nil, err } return &environConfig{valid, valid.UnknownAttrs()}, nil } func (p *environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { // Check for valid changes for the base config values. if err := config.Validate(cfg, old); err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } if idStr, ok := validated["state-id"].(string); ok { if _, err := strconv.Atoi(idStr); err != nil { return nil, fmt.Errorf("invalid state-id %q", idStr) } } // Apply the coerced unknown values back into the config. return cfg.Apply(validated) } func (e *environ) state() (*environState, error) { stateId := e.ecfg().stateId() if stateId == noStateId { return nil, provider.ErrNotPrepared } p := &providerInstance p.mu.Lock() defer p.mu.Unlock() if state := p.state[stateId]; state != nil { return state, nil } return nil, provider.ErrDestroyed } func (p *environProvider) Open(cfg *config.Config) (environs.Environ, error) { p.mu.Lock() defer p.mu.Unlock() ecfg, err := p.newConfig(cfg) if err != nil { return nil, err } if ecfg.stateId() == noStateId { return nil, provider.ErrNotPrepared } env := &environ{ name: ecfg.Name(), ecfgUnlocked: ecfg, } if err := env.checkBroken("Open"); err != nil { return nil, err } return env, nil } func (p *environProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { cfg, err := p.prepare(cfg) if err != nil { return nil, err } return p.Open(cfg) } // prepare is the internal version of Prepare - it prepares the // environment but does not open it. func (p *environProvider) prepare(cfg *config.Config) (*config.Config, error) { ecfg, err := p.newConfig(cfg) if err != nil { return nil, err } p.mu.Lock() defer p.mu.Unlock() name := cfg.Name() if ecfg.stateId() != noStateId { return cfg, nil } // The environment has not been prepared, // so create it and set its state identifier accordingly. if ecfg.stateServer() && len(p.state) != 0 { for _, old := range p.state { panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q", old.name, name)) } } state := newState(name, p.ops, p.statePolicy) p.maxStateId++ state.id = p.maxStateId p.state[state.id] = state // Add the state id to the configuration we use to // in the returned environment. return cfg.Apply(map[string]interface{}{ "state-id": fmt.Sprint(state.id), }) } func (*environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { ecfg, err := providerInstance.newConfig(cfg) if err != nil { return nil, err } return map[string]string{ "secret": ecfg.secret(), }, nil } func (*environProvider) BoilerplateConfig() string { return ` # Fake configuration for dummy provider. dummy: type: dummy `[1:] } var errBroken = errors.New("broken environment") // Override for testing - the data directory with which the state api server is initialised. var DataDir = "" func (e *environ) ecfg() *environConfig { e.ecfgMutex.Lock() ecfg := e.ecfgUnlocked e.ecfgMutex.Unlock() return ecfg } func (e *environ) checkBroken(method string) error { for _, m := range strings.Fields(e.ecfg().broken()) { if m == method { return fmt.Errorf("dummy.%s is broken", method) } } return nil } func (e *environ) Name() string { return e.name } // SupportedArchitectures is specified on the EnvironCapability interface. func (*environ) SupportedArchitectures() ([]string, error) { return []string{arch.AMD64, arch.PPC64}, nil } // GetImageSources returns a list of sources which are used to search for simplestreams image metadata. func (e *environ) GetImageSources() ([]simplestreams.DataSource, error) { return []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseImagesPath)}, nil } // GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata. func (e *environ) GetToolsSources() ([]simplestreams.DataSource, error) { return []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}, nil } func (e *environ) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { selectedTools, err := common.EnsureBootstrapTools(ctx, e, config.PreferredSeries(e.Config()), cons.Arch) if err != nil { return err } defer delay() if err := e.checkBroken("Bootstrap"); err != nil { return err } password := e.Config().AdminSecret() if password == "" { return fmt.Errorf("admin-secret is required for bootstrap") } if _, ok := e.Config().CACert(); !ok { return fmt.Errorf("no CA certificate in environment configuration") } logger.Infof("would pick tools from %s", selectedTools) cfg, err := environs.BootstrapConfig(e.Config()) if err != nil { return fmt.Errorf("cannot make bootstrap config: %v", err) } estate, err := e.state() if err != nil { return err } estate.mu.Lock() defer estate.mu.Unlock() if estate.bootstrapped { return fmt.Errorf("environment is already bootstrapped") } // Write the bootstrap file just like a normal provider. However // we need to release the mutex for the save state to work, so regain // it after the call. estate.mu.Unlock() if err := bootstrap.SaveState(e.Storage(), &bootstrap.BootstrapState{StateInstances: []instance.Id{"localhost"}}); err != nil { logger.Errorf("failed to save state instances: %v", err) estate.mu.Lock() // otherwise defered unlock will fail return err } estate.mu.Lock() // back at it if e.ecfg().stateServer() { // TODO(rog) factor out relevant code from cmd/jujud/bootstrap.go // so that we can call it here. info := stateInfo() st, err := state.Initialize(info, cfg, state.DefaultDialOpts(), estate.statePolicy) if err != nil { panic(err) } if err := st.SetEnvironConstraints(cons); err != nil { panic(err) } if err := st.SetAdminMongoPassword(utils.UserPasswordHash(password, utils.CompatSalt)); err != nil { panic(err) } _, err = st.AddUser("admin", password) if err != nil { panic(err) } estate.apiServer, err = apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert), []byte(testing.ServerKey), DataDir) if err != nil { panic(err) } estate.apiState = st } estate.bootstrapped = true estate.ops <- OpBootstrap{Context: ctx, Env: e.name, Constraints: cons} return nil } func (e *environ) StateInfo() (*state.Info, *api.Info, error) { estate, err := e.state() if err != nil { return nil, nil, err } estate.mu.Lock() defer estate.mu.Unlock() if err := e.checkBroken("StateInfo"); err != nil { return nil, nil, err } if !e.ecfg().stateServer() { return nil, nil, errors.New("dummy environment has no state configured") } if !estate.bootstrapped { return nil, nil, environs.ErrNotBootstrapped } return stateInfo(), &api.Info{ Addrs: []string{estate.apiServer.Addr()}, CACert: []byte(testing.CACert), }, nil } func (e *environ) Config() *config.Config { return e.ecfg().Config } func (e *environ) SetConfig(cfg *config.Config) error { if err := e.checkBroken("SetConfig"); err != nil { return err } ecfg, err := providerInstance.newConfig(cfg) if err != nil { return err } e.ecfgMutex.Lock() e.ecfgUnlocked = ecfg e.ecfgMutex.Unlock() return nil } func (e *environ) Destroy() (res error) { defer delay() estate, err := e.state() if err != nil { if err == provider.ErrDestroyed { return nil } return err } defer func() { estate.ops <- OpDestroy{Env: estate.name, Error: res} }() if err := e.checkBroken("Destroy"); err != nil { return err } p := &providerInstance p.mu.Lock() delete(p.state, estate.id) p.mu.Unlock() estate.mu.Lock() defer estate.mu.Unlock() estate.destroy() return nil } // StartInstance is specified in the InstanceBroker interface. func (e *environ) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { defer delay() machineId := args.MachineConfig.MachineId logger.Infof("dummy startinstance, machine %s", machineId) if err := e.checkBroken("StartInstance"); err != nil { return nil, nil, err } estate, err := e.state() if err != nil { return nil, nil, err } estate.mu.Lock() defer estate.mu.Unlock() if args.MachineConfig.MachineNonce == "" { return nil, nil, fmt.Errorf("cannot start instance: missing machine nonce") } if _, ok := e.Config().CACert(); !ok { return nil, nil, fmt.Errorf("no CA certificate in environment configuration") } if args.MachineConfig.StateInfo.Tag != names.MachineTag(machineId) { return nil, nil, fmt.Errorf("entity tag must match started machine") } if args.MachineConfig.APIInfo.Tag != names.MachineTag(machineId) { return nil, nil, fmt.Errorf("entity tag must match started machine") } logger.Infof("would pick tools from %s", args.Tools) series := args.Tools.OneSeries() i := &dummyInstance{ id: instance.Id(fmt.Sprintf("%s-%d", e.name, estate.maxId)), ports: make(map[instance.Port]bool), machineId: machineId, series: series, firewallMode: e.Config().FirewallMode(), state: estate, } var hc *instance.HardwareCharacteristics // To match current system capability, only provide hardware characteristics for // environ machines, not containers. if state.ParentId(machineId) == "" { // We will just assume the instance hardware characteristics exactly matches // the supplied constraints (if specified). hc = &instance.HardwareCharacteristics{ Arch: args.Constraints.Arch, Mem: args.Constraints.Mem, RootDisk: args.Constraints.RootDisk, CpuCores: args.Constraints.CpuCores, CpuPower: args.Constraints.CpuPower, Tags: args.Constraints.Tags, } // Fill in some expected instance hardware characteristics if constraints not specified. if hc.Arch == nil { arch := "amd64" hc.Arch = &arch } if hc.Mem == nil { mem := uint64(1024) hc.Mem = &mem } if hc.RootDisk == nil { disk := uint64(8192) hc.RootDisk = &disk } if hc.CpuCores == nil { cores := uint64(1) hc.CpuCores = &cores } } estate.insts[i.id] = i estate.maxId++ estate.ops <- OpStartInstance{ Env: e.name, MachineId: machineId, MachineNonce: args.MachineConfig.MachineNonce, Constraints: args.Constraints, Instance: i, Info: args.MachineConfig.StateInfo, APIInfo: args.MachineConfig.APIInfo, Secret: e.ecfg().secret(), } return i, hc, nil } func (e *environ) StopInstances(is []instance.Instance) error { defer delay() if err := e.checkBroken("StopInstance"); err != nil { return err } estate, err := e.state() if err != nil { return err } estate.mu.Lock() defer estate.mu.Unlock() for _, i := range is { delete(estate.insts, i.(*dummyInstance).id) } estate.ops <- OpStopInstances{ Env: e.name, Instances: is, } return nil } func (e *environ) Instances(ids []instance.Id) (insts []instance.Instance, err error) { defer delay() if err := e.checkBroken("Instances"); err != nil { return nil, err } if len(ids) == 0 { return nil, nil } estate, err := e.state() if err != nil { return nil, err } estate.mu.Lock() defer estate.mu.Unlock() notFound := 0 for _, id := range ids { inst := estate.insts[id] if inst == nil { err = environs.ErrPartialInstances notFound++ insts = append(insts, nil) } else { insts = append(insts, inst) } } if notFound == len(ids) { return nil, environs.ErrNoInstances } return } func (e *environ) AllInstances() ([]instance.Instance, error) { defer delay() if err := e.checkBroken("AllInstances"); err != nil { return nil, err } var insts []instance.Instance estate, err := e.state() if err != nil { return nil, err } estate.mu.Lock() defer estate.mu.Unlock() for _, v := range estate.insts { insts = append(insts, v) } return insts, nil } func (e *environ) OpenPorts(ports []instance.Port) error { if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { return fmt.Errorf("invalid firewall mode %q for opening ports on environment", mode) } estate, err := e.state() if err != nil { return err } estate.mu.Lock() defer estate.mu.Unlock() for _, p := range ports { estate.globalPorts[p] = true } return nil } func (e *environ) ClosePorts(ports []instance.Port) error { if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { return fmt.Errorf("invalid firewall mode %q for closing ports on environment", mode) } estate, err := e.state() if err != nil { return err } estate.mu.Lock() defer estate.mu.Unlock() for _, p := range ports { delete(estate.globalPorts, p) } return nil } func (e *environ) Ports() (ports []instance.Port, err error) { if mode := e.ecfg().FirewallMode(); mode != config.FwGlobal { return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from environment", mode) } estate, err := e.state() if err != nil { return nil, err } estate.mu.Lock() defer estate.mu.Unlock() for p := range estate.globalPorts { ports = append(ports, p) } instance.SortPorts(ports) return } func (*environ) Provider() environs.EnvironProvider { return &providerInstance } type dummyInstance struct { state *environState ports map[instance.Port]bool id instance.Id status string machineId string series string firewallMode string mu sync.Mutex addresses []instance.Address } func (inst *dummyInstance) Id() instance.Id { return inst.id } func (inst *dummyInstance) Status() string { return inst.status } // SetInstanceAddresses sets the addresses associated with the given // dummy instance. func SetInstanceAddresses(inst instance.Instance, addrs []instance.Address) { inst0 := inst.(*dummyInstance) inst0.mu.Lock() inst0.addresses = append(inst0.addresses[:0], addrs...) inst0.mu.Unlock() } // SetInstanceStatus sets the status associated with the given // dummy instance. func SetInstanceStatus(inst instance.Instance, status string) { inst0 := inst.(*dummyInstance) inst0.mu.Lock() inst0.status = status inst0.mu.Unlock() } func (inst *dummyInstance) DNSName() (string, error) { defer delay() return string(inst.id) + ".dns", nil } func (*dummyInstance) Refresh() error { return nil } func (inst *dummyInstance) Addresses() ([]instance.Address, error) { inst.mu.Lock() defer inst.mu.Unlock() return append([]instance.Address{}, inst.addresses...), nil } func (inst *dummyInstance) WaitDNSName() (string, error) { return common.WaitDNSName(inst) } func (inst *dummyInstance) OpenPorts(machineId string, ports []instance.Port) error { defer delay() logger.Infof("openPorts %s, %#v", machineId, ports) if inst.firewallMode != config.FwInstance { return fmt.Errorf("invalid firewall mode %q for opening ports on instance", inst.firewallMode) } if inst.machineId != machineId { panic(fmt.Errorf("OpenPorts with mismatched machine id, expected %q got %q", inst.machineId, machineId)) } inst.state.mu.Lock() defer inst.state.mu.Unlock() inst.state.ops <- OpOpenPorts{ Env: inst.state.name, MachineId: machineId, InstanceId: inst.Id(), Ports: ports, } for _, p := range ports { inst.ports[p] = true } return nil } func (inst *dummyInstance) ClosePorts(machineId string, ports []instance.Port) error { defer delay() if inst.firewallMode != config.FwInstance { return fmt.Errorf("invalid firewall mode %q for closing ports on instance", inst.firewallMode) } if inst.machineId != machineId { panic(fmt.Errorf("ClosePorts with mismatched machine id, expected %s got %s", inst.machineId, machineId)) } inst.state.mu.Lock() defer inst.state.mu.Unlock() inst.state.ops <- OpClosePorts{ Env: inst.state.name, MachineId: machineId, InstanceId: inst.Id(), Ports: ports, } for _, p := range ports { delete(inst.ports, p) } return nil } func (inst *dummyInstance) Ports(machineId string) (ports []instance.Port, err error) { defer delay() if inst.firewallMode != config.FwInstance { return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from instance", inst.firewallMode) } if inst.machineId != machineId { panic(fmt.Errorf("Ports with mismatched machine id, expected %q got %q", inst.machineId, machineId)) } inst.state.mu.Lock() defer inst.state.mu.Unlock() for p := range inst.ports { ports = append(ports, p) } instance.SortPorts(ports) return } // providerDelay controls the delay before dummy responds. // non empty values in JUJU_DUMMY_DELAY will be parsed as // time.Durations into this value. var providerDelay time.Duration // pause execution to simulate the latency of a real provider func delay() { if providerDelay > 0 { logger.Infof("pausing for %v", providerDelay) <-time.After(providerDelay) } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023632� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/instancetype.go�������������������������0000644�0000153�0000161�00000016142�12321735642�026706� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "fmt" "sort" "launchpad.net/gwacl" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/errors" ) // preferredTypes is a list of machine types, in order of preference so that // the first type that matches a set of hardware constraints is also the best // (cheapest) fit for those constraints. Or if your constraint is a maximum // price you're willing to pay, your best match is the last type that falls // within your threshold price. type preferredTypes []*gwacl.RoleSize // preferredTypes implements sort.Interface. var _ sort.Interface = (*preferredTypes)(nil) // newPreferredTypes creates a preferredTypes based on the given slice of // RoleSize objects. It will hold pointers to the elements of that slice. func newPreferredTypes(availableTypes []gwacl.RoleSize) preferredTypes { types := make(preferredTypes, len(availableTypes)) for index := range availableTypes { types[index] = &availableTypes[index] } sort.Sort(&types) return types } // Len is specified in sort.Interface. func (types *preferredTypes) Len() int { return len(*types) } // Less is specified in sort.Interface. func (types *preferredTypes) Less(i, j int) bool { // All we care about for now is cost. If at some point Azure offers // different tradeoffs for the same price, we may need a tie-breaker. return (*types)[i].Cost < (*types)[j].Cost } // Swap is specified in sort.Interface. func (types *preferredTypes) Swap(i, j int) { firstPtr := &(*types)[i] secondPtr := &(*types)[j] *secondPtr, *firstPtr = *firstPtr, *secondPtr } // suffices returns whether the given value is high enough to meet the // required value, if any. If "required" is nil, there is no requirement // and so any given value will do. // // This is a method only to limit namespace pollution. It ignores the receiver. func (*preferredTypes) suffices(given uint64, required *uint64) bool { return required == nil || given >= *required } // satisfies returns whether the given machine type is enough to satisfy // the given constraints. (It doesn't matter if it's overkill; all that // matters here is whether the machine type is good enough.) // // This is a method only to limit namespace pollution. It ignores the receiver. func (types *preferredTypes) satisfies(machineType *gwacl.RoleSize, constraint constraints.Value) bool { // gwacl does not model CPU power yet, although Azure does have the // option of a shared core (for ExtraSmall instances). For now we // just pretend that's a full-fledged core. return types.suffices(machineType.CpuCores, constraint.CpuCores) && types.suffices(machineType.Mem, constraint.Mem) } const defaultMem = 1 * gwacl.GB // If you specify no constraints at all, you're going to get the smallest // instance type available. In practice that one's a bit small. So unless // the constraints are deliberately set lower, this gives you a set of // baseline constraints that are just slightly more ambitious than that. func defaultToBaselineSpec(constraint constraints.Value) constraints.Value { result := constraint if result.Mem == nil { var value uint64 = defaultMem result.Mem = &value } return result } // selectMachineType returns the Azure machine type that best matches the // supplied instanceConstraint. func selectMachineType(availableTypes []gwacl.RoleSize, constraint constraints.Value) (*gwacl.RoleSize, error) { types := newPreferredTypes(availableTypes) for _, machineType := range types { if types.satisfies(machineType, constraint) { return machineType, nil } } return nil, fmt.Errorf("no machine type matches constraints %v", constraint) } // getEndpoint returns the simplestreams endpoint to use for the given Azure // location (e.g. West Europe or China North). func getEndpoint(location string) string { // Simplestreams uses the management-API endpoint for the image, not // the base managent API URL. return gwacl.GetEndpoint(location).ManagementAPI() } // As long as this code only supports the default simplestreams // database, which is always signed, there is no point in accepting // unsigned metadata. // // For tests, however, unsigned data is more convenient. They can override // this setting. var signedImageDataOnly = true // findMatchingImages queries simplestreams for OS images that match the given // requirements. // // If it finds no matching images, that's an error. func findMatchingImages(e *azureEnviron, location, series string, arches []string) ([]*imagemetadata.ImageMetadata, error) { endpoint := getEndpoint(location) constraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{location, endpoint}, Series: []string{series}, Arches: arches, Stream: e.Config().ImageStream(), }) sources, err := imagemetadata.GetMetadataSources(e) if err != nil { return nil, err } indexPath := simplestreams.DefaultIndexPath images, _, err := imagemetadata.Fetch(sources, indexPath, constraint, signedImageDataOnly) if len(images) == 0 || errors.IsNotFoundError(err) { return nil, fmt.Errorf("no OS images found for location %q, series %q, architectures %q (and endpoint: %q)", location, series, arches, endpoint) } else if err != nil { return nil, err } return images, nil } // newInstanceType creates an InstanceType based on a gwacl.RoleSize. func newInstanceType(roleSize gwacl.RoleSize) instances.InstanceType { vtype := "Hyper-V" // Actually Azure has shared and dedicated CPUs, but gwacl doesn't // model that distinction yet. var cpuPower uint64 = 100 return instances.InstanceType{ Id: roleSize.Name, Name: roleSize.Name, CpuCores: roleSize.CpuCores, Mem: roleSize.Mem, RootDisk: roleSize.OSDiskSpaceVirt, Cost: roleSize.Cost, VirtType: &vtype, CpuPower: &cpuPower, // tags are not currently supported by azure } } // listInstanceTypes describes the available instance types based on a // description in gwacl's terms. func listInstanceTypes(env *azureEnviron, roleSizes []gwacl.RoleSize) ([]instances.InstanceType, error) { arches, err := env.SupportedArchitectures() if err != nil { return nil, err } types := make([]instances.InstanceType, len(roleSizes)) for index, roleSize := range roleSizes { types[index] = newInstanceType(roleSize) types[index].Arches = arches } return types, nil } // findInstanceSpec returns the InstanceSpec that best satisfies the supplied // InstanceConstraint. func findInstanceSpec(env *azureEnviron, constraint *instances.InstanceConstraint) (*instances.InstanceSpec, error) { constraint.Constraints = defaultToBaselineSpec(constraint.Constraints) imageData, err := findMatchingImages(env, constraint.Region, constraint.Series, constraint.Arches) if err != nil { return nil, err } images := instances.ImageMetadataToImages(imageData) instanceTypes, err := listInstanceTypes(env, gwacl.RoleSizes) if err != nil { return nil, err } return instances.FindInstanceSpec(images, constraint, instanceTypes) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/config_test.go��������������������������0000644�0000153�0000161�00000016032�12321735776�026512� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "io/ioutil" "strings" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type configSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&configSuite{}) // makeConfigMap creates a minimal map of standard configuration items, // adds the given extra items to that and returns it. func makeConfigMap(extra map[string]interface{}) map[string]interface{} { return testing.FakeConfig().Merge(testing.Attrs{ "name": "testenv", "type": "azure", }).Merge(extra) } var testCert = ` -----BEGIN PRIVATE KEY----- MIIBCgIBADANBgkqhkiG9w0BAQEFAASB9TCB8gIBAAIxAKQGQxP1i0VfCWn4KmMP taUFn8sMBKjP/9vHnUYdZRvvmoJCA1C6arBUDp8s2DNX+QIDAQABAjBLRqhwN4dU LfqHDKJ/Vg1aD8u3Buv4gYRBxdFR5PveyqHSt5eJ4g/x/4ndsvr2OqUCGQDNfNlD zxHCiEAwZZAPaAkn8jDkFupTljcCGQDMWCujiVZ1NNuBD/N32Yt8P9JDiNzZa08C GBW7VXLxbExpgnhb1V97vjQmTfthXQjYAwIYSTEjoFXm4+Bk5xuBh2IidgSeGZaC FFY9AhkAsteo31cyQw2xJ80SWrmsIw+ps7Cvt5W9 -----END PRIVATE KEY----- -----BEGIN CERTIFICATE----- MIIBDzCByqADAgECAgkAgIBb3+lSwzEwDQYJKoZIhvcNAQEFBQAwFTETMBEGA1UE AxQKQEhvc3ROYW1lQDAeFw0xMzA3MTkxNjA1NTRaFw0yMzA3MTcxNjA1NTRaMBUx EzARBgNVBAMUCkBIb3N0TmFtZUAwTDANBgkqhkiG9w0BAQEFAAM7ADA4AjEApAZD E/WLRV8JafgqYw+1pQWfywwEqM//28edRh1lG++agkIDULpqsFQOnyzYM1f5AgMB AAGjDTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADMQABKfn08tKfzzqMMD2w PI2fs3bw5bRH8tmGjrsJeEdp9crCBS8I3hKcxCkTTRTowdY= -----END CERTIFICATE----- ` func makeAzureConfigMap(c *gc.C) map[string]interface{} { azureConfig := map[string]interface{}{ "location": "location", "management-subscription-id": "subscription-id", "management-certificate": testCert, "storage-account-name": "account-name", } return makeConfigMap(azureConfig) } // createTempFile creates a temporary file. The file will be cleaned // up at the end of the test calling this method. func createTempFile(c *gc.C, content []byte) string { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) filename := file.Name() err = ioutil.WriteFile(filename, content, 0644) c.Assert(err, gc.IsNil) return filename } func (*configSuite) TestValidateAcceptsNilOldConfig(c *gc.C) { attrs := makeAzureConfigMap(c) provider := azureEnvironProvider{} config, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) result, err := provider.Validate(config, nil) c.Assert(err, gc.IsNil) c.Check(result.Name(), gc.Equals, attrs["name"]) } func (*configSuite) TestValidateAcceptsUnchangedConfig(c *gc.C) { attrs := makeAzureConfigMap(c) provider := azureEnvironProvider{} oldConfig, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) newConfig, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) result, err := provider.Validate(newConfig, oldConfig) c.Assert(err, gc.IsNil) c.Check(result.Name(), gc.Equals, attrs["name"]) } func (*configSuite) TestValidateChecksConfigChanges(c *gc.C) { provider := azureEnvironProvider{} oldConfig, err := config.New(config.NoDefaults, makeConfigMap(nil)) c.Assert(err, gc.IsNil) newAttrs := makeConfigMap(map[string]interface{}{ "name": "different-name", }) newConfig, err := config.New(config.NoDefaults, newAttrs) c.Assert(err, gc.IsNil) _, err = provider.Validate(newConfig, oldConfig) c.Check(err, gc.NotNil) } func (*configSuite) TestValidateParsesAzureConfig(c *gc.C) { location := "location" managementSubscriptionId := "subscription-id" certificate := "certificate content" storageAccountName := "account-name" forceImageName := "force-image-name" unknownFutureSetting := "preserved" azureConfig := map[string]interface{}{ "location": location, "management-subscription-id": managementSubscriptionId, "management-certificate": certificate, "storage-account-name": storageAccountName, "force-image-name": forceImageName, "unknown-future-setting": unknownFutureSetting, } attrs := makeConfigMap(azureConfig) provider := azureEnvironProvider{} config, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) azConfig, err := provider.newConfig(config) c.Assert(err, gc.IsNil) c.Check(azConfig.Name(), gc.Equals, attrs["name"]) c.Check(azConfig.location(), gc.Equals, location) c.Check(azConfig.managementSubscriptionId(), gc.Equals, managementSubscriptionId) c.Check(azConfig.managementCertificate(), gc.Equals, certificate) c.Check(azConfig.storageAccountName(), gc.Equals, storageAccountName) c.Check(azConfig.forceImageName(), gc.Equals, forceImageName) c.Check(azConfig.UnknownAttrs()["unknown-future-setting"], gc.Equals, unknownFutureSetting) } func (*configSuite) TestValidateReadsCertFile(c *gc.C) { certificate := "test certificate" certFile := createTempFile(c, []byte(certificate)) attrs := makeAzureConfigMap(c) delete(attrs, "management-certificate") attrs["management-certificate-path"] = certFile provider := azureEnvironProvider{} newConfig, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) azConfig, err := provider.newConfig(newConfig) c.Assert(err, gc.IsNil) c.Check(azConfig.managementCertificate(), gc.Equals, certificate) } func (*configSuite) TestChecksExistingCertFile(c *gc.C) { nonExistingCertPath := "non-existing-cert-file" attrs := makeAzureConfigMap(c) delete(attrs, "management-certificate") attrs["management-certificate-path"] = nonExistingCertPath provider := azureEnvironProvider{} newConfig, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) _, err = provider.Validate(newConfig, nil) c.Check(err, gc.ErrorMatches, ".*"+nonExistingCertPath+": no such file or directory.*") } func (*configSuite) TestChecksLocationIsRequired(c *gc.C) { attrs := makeAzureConfigMap(c) attrs["location"] = "" provider := azureEnvironProvider{} newConfig, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) _, err = provider.Validate(newConfig, nil) c.Check(err, gc.ErrorMatches, ".*environment has no location.*") } func (*configSuite) TestBoilerplateConfigReturnsAzureConfig(c *gc.C) { provider := azureEnvironProvider{} boilerPlateConfig := provider.BoilerplateConfig() c.Assert(strings.Contains(boilerPlateConfig, "type: azure"), gc.Equals, true) } func (*configSuite) TestSecretAttrsReturnsSensitiveAttributes(c *gc.C) { attrs := makeAzureConfigMap(c) certificate := "certificate" attrs["management-certificate"] = certificate config, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) provider := azureEnvironProvider{} secretAttrs, err := provider.SecretAttrs(config) c.Assert(err, gc.IsNil) expectedAttrs := map[string]string{ "management-certificate": certificate, } c.Check(secretAttrs, gc.DeepEquals, expectedAttrs) } func (*configSuite) TestEmptyImageStream1dot16Compat(c *gc.C) { attrs := makeAzureConfigMap(c) attrs["image-stream"] = "" provider := azureEnvironProvider{} cfg, err := config.New(config.UseDefaults, attrs) c.Assert(err, gc.IsNil) _, err = provider.Validate(cfg, nil) c.Assert(err, gc.IsNil) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/customdata.go���������������������������0000644�0000153�0000161�00000001374�12321735642�026345� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "encoding/base64" "fmt" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/cloudinit" ) // makeCustomData produces custom data for Azure. This is a base64-encoded // zipfile of cloudinit userdata. func makeCustomData(cfg *cloudinit.MachineConfig) (string, error) { zipData, err := environs.ComposeUserData(cfg, nil) if err != nil { return "", fmt.Errorf("failure while generating custom data: %v", err) } logger.Debugf("user data; %d bytes", len(zipData)) encodedData := base64.StdEncoding.EncodeToString(zipData) logger.Debugf("base64-encoded custom data: %d bytes", len(encodedData)) return encodedData, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/config.go�������������������������������0000644�0000153�0000161�00000010424�12321735776�025452� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "fmt" "io/ioutil" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/schema" ) var configFields = schema.Fields{ "location": schema.String(), "management-subscription-id": schema.String(), "management-certificate-path": schema.String(), "management-certificate": schema.String(), "storage-account-name": schema.String(), "force-image-name": schema.String(), } var configDefaults = schema.Defaults{ "location": "", "management-certificate": "", "management-certificate-path": "", "force-image-name": "", } type azureEnvironConfig struct { *config.Config attrs map[string]interface{} } func (cfg *azureEnvironConfig) location() string { return cfg.attrs["location"].(string) } func (cfg *azureEnvironConfig) managementSubscriptionId() string { return cfg.attrs["management-subscription-id"].(string) } func (cfg *azureEnvironConfig) managementCertificate() string { return cfg.attrs["management-certificate"].(string) } func (cfg *azureEnvironConfig) storageAccountName() string { return cfg.attrs["storage-account-name"].(string) } func (cfg *azureEnvironConfig) forceImageName() string { return cfg.attrs["force-image-name"].(string) } func (prov azureEnvironProvider) newConfig(cfg *config.Config) (*azureEnvironConfig, error) { validCfg, err := prov.Validate(cfg, nil) if err != nil { return nil, err } result := new(azureEnvironConfig) result.Config = validCfg result.attrs = validCfg.UnknownAttrs() return result, nil } // Validate ensures that config is a valid configuration for this // provider like specified in the EnvironProvider interface. func (prov azureEnvironProvider) Validate(cfg, oldCfg *config.Config) (*config.Config, error) { // Validate base configuration change before validating Azure specifics. err := config.Validate(cfg, oldCfg) if err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } envCfg := new(azureEnvironConfig) envCfg.Config = cfg envCfg.attrs = validated cert := envCfg.managementCertificate() if cert == "" { certPath := envCfg.attrs["management-certificate-path"].(string) pemData, err := ioutil.ReadFile(certPath) if err != nil { return nil, fmt.Errorf("invalid management-certificate-path: %s", err) } envCfg.attrs["management-certificate"] = string(pemData) } delete(envCfg.attrs, "management-certificate-path") if envCfg.location() == "" { return nil, fmt.Errorf("environment has no location; you need to set one. E.g. 'West US'") } return cfg.Apply(envCfg.attrs) } var boilerplateYAML = ` # https://juju.ubuntu.com/docs/config-azure.html azure: type: azure # location specifies the place where instances will be started, # for example: West US, North Europe. # location: West US # The following attributes specify Windows Azure Management # information. See: # http://msdn.microsoft.com/en-us/library/windowsazure # for details. # management-subscription-id: <00000000-0000-0000-0000-000000000000> management-certificate-path: /home/me/azure.pem # storage-account-name holds Windows Azure Storage info. # storage-account-name: abcdefghijkl # force-image-name overrides the OS image selection to use a fixed # image for all deployments. Most useful for developers. # # force-image-name: b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-DEVELOPMENT-20130713-Juju_ALPHA-en-us-30GB # image-stream chooses a simplestreams stream to select OS images # from, for example daily or released images (or any other stream # available on simplestreams). # # image-stream: "released" `[1:] func (prov azureEnvironProvider) BoilerplateConfig() string { return boilerplateYAML } // SecretAttrs is specified in the EnvironProvider interface. func (prov azureEnvironProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { secretAttrs := make(map[string]string) azureCfg, err := prov.newConfig(cfg) if err != nil { return nil, err } secretAttrs["management-certificate"] = azureCfg.managementCertificate() return secretAttrs, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/instance.go�����������������������������0000644�0000153�0000161�00000021327�12321735776�026015� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "fmt" "strings" "launchpad.net/gwacl" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/worker/firewaller" ) type azureInstance struct { // An instance contains an Azure Service (instance==service). gwacl.HostedServiceDescriptor environ *azureEnviron } // azureInstance implements Instance. var _ instance.Instance = (*azureInstance)(nil) // Id is specified in the Instance interface. func (azInstance *azureInstance) Id() instance.Id { return instance.Id(azInstance.ServiceName) } // Status is specified in the Instance interface. func (azInstance *azureInstance) Status() string { return azInstance.HostedServiceDescriptor.Status } var AZURE_DOMAIN_NAME = "cloudapp.net" // Refresh is specified in the Instance interface. func (azInstance *azureInstance) Refresh() error { // TODO(axw) 2013-12-16 #1261324 // Cache Addresses/netInfo, refresh here. return nil } // Addresses is specified in the Instance interface. func (azInstance *azureInstance) Addresses() ([]instance.Address, error) { addrs := []instance.Address{} ip, netname, err := azInstance.netInfo() if err != nil { return nil, err } if ip != "" { addrs = append(addrs, instance.Address{ ip, instance.Ipv4Address, netname, instance.NetworkCloudLocal}) } name, err := azInstance.DNSName() if err != nil { return nil, err } host := instance.Address{name, instance.HostName, "", instance.NetworkPublic} addrs = append(addrs, host) return addrs, nil } func (azInstance *azureInstance) netInfo() (ip, netname string, err error) { err = azInstance.apiCall(false, func(c *azureManagementContext) error { d, err := c.GetDeployment(&gwacl.GetDeploymentRequest{ ServiceName: azInstance.ServiceName, DeploymentName: azInstance.ServiceName, }) if err != nil { return err } switch len(d.RoleInstanceList) { case 0: // nothing to do, this can happen if the instances aren't finished deploying return nil case 1: // success ip = d.RoleInstanceList[0].IPAddress netname = d.VirtualNetworkName return nil default: return fmt.Errorf("Too many instances, expected one, got %d", len(d.RoleInstanceList)) } }) if err != nil { return "", "", err } return ip, netname, nil } // DNSName is specified in the Instance interface. func (azInstance *azureInstance) DNSName() (string, error) { // For deployments in the Production slot, the instance's DNS name // is its service name, in the cloudapp.net domain. // (For Staging deployments it's all much weirder: they get random // names assigned, which somehow don't seem to resolve from the // outside.) name := fmt.Sprintf("%s.%s", azInstance.ServiceName, AZURE_DOMAIN_NAME) return name, nil } // WaitDNSName is specified in the Instance interface. func (azInstance *azureInstance) WaitDNSName() (string, error) { return common.WaitDNSName(azInstance) } // OpenPorts is specified in the Instance interface. func (azInstance *azureInstance) OpenPorts(machineId string, ports []instance.Port) error { return azInstance.apiCall(true, func(context *azureManagementContext) error { return azInstance.openEndpoints(context, ports) }) } // apiCall wraps a call to the azure API to ensure it is properly disposed, optionally locking // the environment func (azInstance *azureInstance) apiCall(lock bool, f func(*azureManagementContext) error) error { env := azInstance.environ context, err := env.getManagementAPI() if err != nil { return err } defer env.releaseManagementAPI(context) if lock { env.Lock() defer env.Unlock() } return f(context) } // openEndpoints opens the endpoints in the Azure deployment. The caller is // responsible for locking and unlocking the environ and releasing the // management context. func (azInstance *azureInstance) openEndpoints(context *azureManagementContext, ports []instance.Port) error { deployments, err := context.ListAllDeployments(&gwacl.ListAllDeploymentsRequest{ ServiceName: azInstance.ServiceName, }) if err != nil { return err } for _, deployment := range deployments { for _, role := range deployment.RoleList { request := &gwacl.AddRoleEndpointsRequest{ ServiceName: azInstance.ServiceName, DeploymentName: deployment.Name, RoleName: role.RoleName, } for _, port := range ports { request.InputEndpoints = append( request.InputEndpoints, gwacl.InputEndpoint{ LocalPort: port.Number, Name: fmt.Sprintf("%s%d", port.Protocol, port.Number), Port: port.Number, Protocol: port.Protocol, }) } err := context.AddRoleEndpoints(request) if err != nil { return err } } } return nil } // ClosePorts is specified in the Instance interface. func (azInstance *azureInstance) ClosePorts(machineId string, ports []instance.Port) error { return azInstance.apiCall(true, func(context *azureManagementContext) error { return azInstance.closeEndpoints(context, ports) }) } // closeEndpoints closes the endpoints in the Azure deployment. The caller is // responsible for locking and unlocking the environ and releasing the // management context. func (azInstance *azureInstance) closeEndpoints(context *azureManagementContext, ports []instance.Port) error { deployments, err := context.ListAllDeployments(&gwacl.ListAllDeploymentsRequest{ ServiceName: azInstance.ServiceName, }) if err != nil { return err } for _, deployment := range deployments { for _, role := range deployment.RoleList { request := &gwacl.RemoveRoleEndpointsRequest{ ServiceName: azInstance.ServiceName, DeploymentName: deployment.Name, RoleName: role.RoleName, } for _, port := range ports { request.InputEndpoints = append( request.InputEndpoints, gwacl.InputEndpoint{ LocalPort: port.Number, Name: fmt.Sprintf("%s%d", port.Protocol, port.Number), Port: port.Number, Protocol: port.Protocol, }) } err := context.RemoveRoleEndpoints(request) if err != nil { return err } } } return nil } // convertAndFilterEndpoints converts a slice of gwacl.InputEndpoint into a slice of instance.Port. func convertEndpointsToPorts(endpoints []gwacl.InputEndpoint) []instance.Port { ports := []instance.Port{} for _, endpoint := range endpoints { ports = append(ports, instance.Port{ Protocol: strings.ToLower(endpoint.Protocol), Number: endpoint.Port, }) } return ports } // convertAndFilterEndpoints converts a slice of gwacl.InputEndpoint into a slice of instance.Port // and filters out the initial endpoints that every instance should have opened (ssh port, etc.). func convertAndFilterEndpoints(endpoints []gwacl.InputEndpoint, env *azureEnviron) []instance.Port { return firewaller.Diff( convertEndpointsToPorts(endpoints), convertEndpointsToPorts(env.getInitialEndpoints())) } // Ports is specified in the Instance interface. func (azInstance *azureInstance) Ports(machineId string) (ports []instance.Port, err error) { err = azInstance.apiCall(false, func(context *azureManagementContext) error { ports, err = azInstance.listPorts(context) return err }) if ports != nil { instance.SortPorts(ports) } return ports, err } // listPorts returns the slice of ports (instance.Port) that this machine // has opened. The returned list does not contain the "initial ports" // (i.e. the ports every instance shoud have opened). The caller is // responsible for locking and unlocking the environ and releasing the // management context. func (azInstance *azureInstance) listPorts(context *azureManagementContext) ([]instance.Port, error) { deployments, err := context.ListAllDeployments(&gwacl.ListAllDeploymentsRequest{ ServiceName: azInstance.ServiceName, }) if err != nil { return nil, err } env := azInstance.environ switch { // Only zero or one deployment is a valid state (instance==service). case len(deployments) > 1: return nil, fmt.Errorf("more than one Azure deployment inside the service named %q", azInstance.ServiceName) case len(deployments) == 1: deployment := deployments[0] switch { // Only zero or one role is a valid state (instance==service). case len(deployment.RoleList) > 1: return nil, fmt.Errorf("more than one Azure role inside the deployment named %q", deployment.Name) case len(deployment.RoleList) == 1: role := deployment.RoleList[0] endpoints, err := context.ListRoleEndpoints(&gwacl.ListRoleEndpointsRequest{ ServiceName: azInstance.ServiceName, DeploymentName: deployment.Name, RoleName: role.RoleName, }) if err != nil { return nil, err } ports := convertAndFilterEndpoints(endpoints, env) return ports, nil } return nil, nil } return nil, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/instance_test.go������������������������0000644�0000153�0000161�00000040114�12321735776�027047� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "encoding/base64" "fmt" "net/http" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/gwacl" "launchpad.net/juju-core/instance" ) type instanceSuite struct{} var _ = gc.Suite(&instanceSuite{}) // makeHostedServiceDescriptor creates a HostedServiceDescriptor with the // given service name. func makeHostedServiceDescriptor(name string) *gwacl.HostedServiceDescriptor { labelBase64 := base64.StdEncoding.EncodeToString([]byte("label")) return &gwacl.HostedServiceDescriptor{ServiceName: name, Label: labelBase64} } func (*instanceSuite) TestId(c *gc.C) { serviceName := "test-name" testService := makeHostedServiceDescriptor(serviceName) azInstance := azureInstance{*testService, nil} c.Check(azInstance.Id(), gc.Equals, instance.Id(serviceName)) } func (*instanceSuite) TestStatus(c *gc.C) { serviceName := "test-name" testService := makeHostedServiceDescriptor(serviceName) testService.Status = "something" azInstance := azureInstance{*testService, nil} c.Check(azInstance.Status(), gc.Equals, testService.Status) } func (*instanceSuite) TestDNSName(c *gc.C) { // An instance's DNS name is computed from its hosted-service name. host := "hostname" testService := makeHostedServiceDescriptor(host) azInstance := azureInstance{*testService, nil} dnsName, err := azInstance.DNSName() c.Assert(err, gc.IsNil) c.Check(dnsName, gc.Equals, host+"."+AZURE_DOMAIN_NAME) } func (*instanceSuite) TestWaitDNSName(c *gc.C) { // An Azure instance gets its DNS name immediately, so there's no // waiting involved. host := "hostname" testService := makeHostedServiceDescriptor(host) azInstance := azureInstance{*testService, nil} dnsName, err := azInstance.WaitDNSName() c.Assert(err, gc.IsNil) c.Check(dnsName, gc.Equals, host+"."+AZURE_DOMAIN_NAME) } func makeRole(name string, endpoints ...gwacl.InputEndpoint) gwacl.Role { return gwacl.Role{ RoleName: name, ConfigurationSets: []gwacl.ConfigurationSet{ { ConfigurationSetType: gwacl.CONFIG_SET_NETWORK, InputEndpoints: &endpoints, }, }, } } func makeDeployment(name string, roles ...gwacl.Role) gwacl.Deployment { return gwacl.Deployment{ Name: name, RoleList: roles, } } func makeInputEndpoint(port int, protocol string) gwacl.InputEndpoint { return gwacl.InputEndpoint{ LocalPort: port, Name: fmt.Sprintf("%s%d", protocol, port), Port: port, Protocol: protocol, } } func serialize(c *gc.C, object gwacl.AzureObject) []byte { xml, err := object.Serialize() c.Assert(err, gc.IsNil) return []byte(xml) } func prepareDeploymentInfoResponse( c *gc.C, dep gwacl.Deployment) []gwacl.DispatcherResponse { return []gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse( serialize(c, &dep), http.StatusOK, nil), } } func preparePortChangeConversation( c *gc.C, service *gwacl.HostedServiceDescriptor, deployments ...gwacl.Deployment) []gwacl.DispatcherResponse { // Construct the series of responses to expected requests. responses := []gwacl.DispatcherResponse{ // First, GetHostedServiceProperties gwacl.NewDispatcherResponse( serialize(c, &gwacl.HostedService{ Deployments: deployments, HostedServiceDescriptor: *service, XMLNS: gwacl.XMLNS, }), http.StatusOK, nil), } for _, deployment := range deployments { for _, role := range deployment.RoleList { // GetRole returns a PersistentVMRole. persistentRole := &gwacl.PersistentVMRole{ XMLNS: gwacl.XMLNS, RoleName: role.RoleName, ConfigurationSets: role.ConfigurationSets, } responses = append(responses, gwacl.NewDispatcherResponse( serialize(c, persistentRole), http.StatusOK, nil)) // UpdateRole expects a 200 response, that's all. responses = append(responses, gwacl.NewDispatcherResponse(nil, http.StatusOK, nil)) } } return responses } // point is 1-indexed; it represents which request should fail. func failPortChangeConversationAt(point int, responses []gwacl.DispatcherResponse) { responses[point-1] = gwacl.NewDispatcherResponse( nil, http.StatusInternalServerError, nil) } type expectedRequest struct { method string urlpattern string } func assertPortChangeConversation(c *gc.C, record []*gwacl.X509Request, expected []expectedRequest) { c.Assert(record, gc.HasLen, len(expected)) for index, request := range record { c.Check(request.Method, gc.Equals, expected[index].method) c.Check(request.URL, gc.Matches, expected[index].urlpattern) } } func (*instanceSuite) TestAddresses(c *gc.C) { name := "service-name" vnn := "Virt Net Name" service := makeHostedServiceDescriptor(name) responses := prepareDeploymentInfoResponse(c, gwacl.Deployment{ RoleInstanceList: []gwacl.RoleInstance{ gwacl.RoleInstance{IPAddress: "1.2.3.4"}, }, VirtualNetworkName: vnn, }) gwacl.PatchManagementAPIResponses(responses) inst := azureInstance{*service, makeEnviron(c)} expected := []instance.Address{ instance.Address{ "1.2.3.4", instance.Ipv4Address, vnn, instance.NetworkCloudLocal, }, instance.Address{ name + "." + AZURE_DOMAIN_NAME, instance.HostName, "", instance.NetworkPublic, }, } addrs, err := inst.Addresses() c.Check(err, gc.IsNil) c.Check(addrs, jc.SameContents, expected) } func (*instanceSuite) TestOpenPorts(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := preparePortChangeConversation(c, service, makeDeployment("deployment-one", makeRole("role-one"), makeRole("role-two")), makeDeployment("deployment-two", makeRole("role-three"))) record := gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} err := azInstance.OpenPorts("machine-id", []instance.Port{ {"tcp", 79}, {"tcp", 587}, {"udp", 9}, }) c.Assert(err, gc.IsNil) assertPortChangeConversation(c, *record, []expectedRequest{ {"GET", ".*/services/hostedservices/service-name[?].*"}, // GetHostedServiceProperties {"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole {"PUT", ".*/deployments/deployment-one/roles/role-one"}, // UpdateRole {"GET", ".*/deployments/deployment-one/roles/role-two"}, // GetRole {"PUT", ".*/deployments/deployment-one/roles/role-two"}, // UpdateRole {"GET", ".*/deployments/deployment-two/roles/role-three"}, // GetRole {"PUT", ".*/deployments/deployment-two/roles/role-three"}, // UpdateRole }) // A representative UpdateRole payload includes configuration for the // ports requested. role := &gwacl.PersistentVMRole{} err = role.Deserialize((*record)[2].Payload) c.Assert(err, gc.IsNil) c.Check( *(role.ConfigurationSets[0].InputEndpoints), gc.DeepEquals, []gwacl.InputEndpoint{ makeInputEndpoint(79, "tcp"), makeInputEndpoint(587, "tcp"), makeInputEndpoint(9, "udp"), }) } func (*instanceSuite) TestOpenPortsFailsWhenUnableToGetServiceProperties(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := []gwacl.DispatcherResponse{ // GetHostedServiceProperties breaks. gwacl.NewDispatcherResponse(nil, http.StatusInternalServerError, nil), } record := gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} err := azInstance.OpenPorts("machine-id", []instance.Port{ {"tcp", 79}, {"tcp", 587}, {"udp", 9}, }) c.Check(err, gc.ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") c.Check(*record, gc.HasLen, 1) } func (*instanceSuite) TestOpenPortsFailsWhenUnableToGetRole(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := preparePortChangeConversation(c, service, makeDeployment("deployment-one", makeRole("role-one"))) failPortChangeConversationAt(2, responses) // 2nd request, GetRole record := gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} err := azInstance.OpenPorts("machine-id", []instance.Port{ {"tcp", 79}, {"tcp", 587}, {"udp", 9}, }) c.Check(err, gc.ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") c.Check(*record, gc.HasLen, 2) } func (*instanceSuite) TestOpenPortsFailsWhenUnableToUpdateRole(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := preparePortChangeConversation(c, service, makeDeployment("deployment-one", makeRole("role-one"))) failPortChangeConversationAt(3, responses) // 3rd request, UpdateRole record := gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} err := azInstance.OpenPorts("machine-id", []instance.Port{ {"tcp", 79}, {"tcp", 587}, {"udp", 9}, }) c.Check(err, gc.ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]") c.Check(*record, gc.HasLen, 3) } func (*instanceSuite) TestClosePorts(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := preparePortChangeConversation(c, service, makeDeployment("deployment-one", makeRole("role-one", makeInputEndpoint(587, "tcp"), ), makeRole("role-two", makeInputEndpoint(79, "tcp"), makeInputEndpoint(9, "udp"), )), makeDeployment("deployment-two", makeRole("role-three", makeInputEndpoint(9, "tcp"), makeInputEndpoint(9, "udp"), ))) record := gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} err := azInstance.ClosePorts("machine-id", []instance.Port{{"tcp", 587}, {"udp", 9}}) c.Assert(err, gc.IsNil) assertPortChangeConversation(c, *record, []expectedRequest{ {"GET", ".*/services/hostedservices/service-name[?].*"}, // GetHostedServiceProperties {"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole {"PUT", ".*/deployments/deployment-one/roles/role-one"}, // UpdateRole {"GET", ".*/deployments/deployment-one/roles/role-two"}, // GetRole {"PUT", ".*/deployments/deployment-one/roles/role-two"}, // UpdateRole {"GET", ".*/deployments/deployment-two/roles/role-three"}, // GetRole {"PUT", ".*/deployments/deployment-two/roles/role-three"}, // UpdateRole }) // The first UpdateRole removes all endpoints from the role's // configuration. roleOne := &gwacl.PersistentVMRole{} err = roleOne.Deserialize((*record)[2].Payload) c.Assert(err, gc.IsNil) c.Check(roleOne.ConfigurationSets[0].InputEndpoints, gc.IsNil) // The second UpdateRole removes all but 79/TCP. roleTwo := &gwacl.PersistentVMRole{} err = roleTwo.Deserialize((*record)[4].Payload) c.Assert(err, gc.IsNil) c.Check( roleTwo.ConfigurationSets[0].InputEndpoints, gc.DeepEquals, &[]gwacl.InputEndpoint{makeInputEndpoint(79, "tcp")}) // The third UpdateRole removes all but 9/TCP. roleThree := &gwacl.PersistentVMRole{} err = roleThree.Deserialize((*record)[6].Payload) c.Assert(err, gc.IsNil) c.Check( roleThree.ConfigurationSets[0].InputEndpoints, gc.DeepEquals, &[]gwacl.InputEndpoint{makeInputEndpoint(9, "tcp")}) } func (*instanceSuite) TestClosePortsFailsWhenUnableToGetServiceProperties(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := []gwacl.DispatcherResponse{ // GetHostedServiceProperties breaks. gwacl.NewDispatcherResponse(nil, http.StatusInternalServerError, nil), } record := gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} err := azInstance.ClosePorts("machine-id", []instance.Port{ {"tcp", 79}, {"tcp", 587}, {"udp", 9}, }) c.Check(err, gc.ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") c.Check(*record, gc.HasLen, 1) } func (*instanceSuite) TestClosePortsFailsWhenUnableToGetRole(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := preparePortChangeConversation(c, service, makeDeployment("deployment-one", makeRole("role-one"))) failPortChangeConversationAt(2, responses) // 2nd request, GetRole record := gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} err := azInstance.ClosePorts("machine-id", []instance.Port{ {"tcp", 79}, {"tcp", 587}, {"udp", 9}, }) c.Check(err, gc.ErrorMatches, "GET request failed [(]500: Internal Server Error[)]") c.Check(*record, gc.HasLen, 2) } func (*instanceSuite) TestClosePortsFailsWhenUnableToUpdateRole(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := preparePortChangeConversation(c, service, makeDeployment("deployment-one", makeRole("role-one"))) failPortChangeConversationAt(3, responses) // 3rd request, UpdateRole record := gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} err := azInstance.ClosePorts("machine-id", []instance.Port{ {"tcp", 79}, {"tcp", 587}, {"udp", 9}, }) c.Check(err, gc.ErrorMatches, "PUT request failed [(]500: Internal Server Error[)]") c.Check(*record, gc.HasLen, 3) } func (*instanceSuite) TestConvertAndFilterEndpoints(c *gc.C) { env := makeEnviron(c) endpoints := []gwacl.InputEndpoint{ { LocalPort: 123, Protocol: "udp", Name: "test123", Port: 1123, }, { LocalPort: 456, Protocol: "tcp", Name: "test456", Port: 44, }} endpoints = append(endpoints, env.getInitialEndpoints()...) expectedPorts := []instance.Port{ { Number: 1123, Protocol: "udp", }, { Number: 44, Protocol: "tcp", }} c.Check(convertAndFilterEndpoints(endpoints, env), gc.DeepEquals, expectedPorts) } func (*instanceSuite) TestConvertAndFilterEndpointsEmptySlice(c *gc.C) { env := makeEnviron(c) ports := convertAndFilterEndpoints([]gwacl.InputEndpoint{}, env) c.Check(ports, gc.HasLen, 0) } func (*instanceSuite) TestPorts(c *gc.C) { service := makeHostedServiceDescriptor("service-name") endpoints := []gwacl.InputEndpoint{ { LocalPort: 223, Protocol: "udp", Name: "test223", Port: 2123, }, { LocalPort: 123, Protocol: "udp", Name: "test123", Port: 1123, }, { LocalPort: 456, Protocol: "tcp", Name: "test456", Port: 4456, }} responses := preparePortChangeConversation(c, service, makeDeployment("deployment-one", makeRole("role-one", endpoints...))) record := gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} ports, err := azInstance.Ports("machine-id") c.Assert(err, gc.IsNil) assertPortChangeConversation(c, *record, []expectedRequest{ {"GET", ".*/services/hostedservices/service-name[?].*"}, // GetHostedServiceProperties {"GET", ".*/deployments/deployment-one/roles/role-one"}, // GetRole }) c.Check( ports, gc.DeepEquals, // The result is sorted using instance.SortPorts() (i.e. first by protocol, // then by number). []instance.Port{ {Number: 4456, Protocol: "tcp"}, {Number: 1123, Protocol: "udp"}, {Number: 2123, Protocol: "udp"}, }) } func (*instanceSuite) TestPortsErrorsIfMoreThanOneRole(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := preparePortChangeConversation(c, service, makeDeployment("deployment-one", makeRole("role-one"), makeRole("role-two"))) gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} _, err := azInstance.Ports("machine-id") c.Check(err, gc.ErrorMatches, ".*more than one Azure role inside the deployment.*") } func (*instanceSuite) TestPortsErrorsIfMoreThanOneDeployment(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := preparePortChangeConversation(c, service, makeDeployment("deployment-one", makeRole("role-one")), makeDeployment("deployment-two", makeRole("role-two"))) gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} _, err := azInstance.Ports("machine-id") c.Check(err, gc.ErrorMatches, ".*more than one Azure deployment inside the service.*") } func (*instanceSuite) TestPortsReturnsEmptySliceIfNoDeployment(c *gc.C) { service := makeHostedServiceDescriptor("service-name") responses := preparePortChangeConversation(c, service) gwacl.PatchManagementAPIResponses(responses) azInstance := azureInstance{*service, makeEnviron(c)} ports, err := azInstance.Ports("machine-id") c.Assert(err, gc.IsNil) c.Check(ports, gc.HasLen, 0) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/customdata_test.go����������������������0000644�0000153�0000161�00000004602�12321735642�027401� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "encoding/base64" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/names" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/tools" ) type customDataSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&customDataSuite{}) // makeMachineConfig produces a valid cloudinit machine config. func makeMachineConfig(c *gc.C) *cloudinit.MachineConfig { machineID := "0" return &cloudinit.MachineConfig{ MachineId: machineID, MachineNonce: "gxshasqlnng", DataDir: environs.DataDir, LogDir: agent.DefaultLogDir, Jobs: []params.MachineJob{params.JobManageEnviron, params.JobHostUnits}, CloudInitOutputLog: environs.CloudInitOutputLog, Tools: &tools.Tools{URL: "file://" + c.MkDir()}, StateInfo: &state.Info{ CACert: []byte(testing.CACert), Addrs: []string{"127.0.0.1:123"}, Tag: names.MachineTag(machineID), Password: "password", }, APIInfo: &api.Info{ CACert: []byte(testing.CACert), Addrs: []string{"127.0.0.1:123"}, Tag: names.MachineTag(machineID), }, MachineAgentServiceName: "jujud-machine-0", } } // makeBadMachineConfig produces a cloudinit machine config that cloudinit // will reject as invalid. func makeBadMachineConfig() *cloudinit.MachineConfig { // As it happens, a default-initialized config is invalid. return &cloudinit.MachineConfig{} } func (*customDataSuite) TestMakeCustomDataPropagatesError(c *gc.C) { _, err := makeCustomData(makeBadMachineConfig()) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, "failure while generating custom data: invalid machine configuration: invalid machine id") } func (*customDataSuite) TestMakeCustomDataEncodesUserData(c *gc.C) { cfg := makeMachineConfig(c) encodedData, err := makeCustomData(cfg) c.Assert(err, gc.IsNil) data, err := base64.StdEncoding.DecodeString(encodedData) c.Assert(err, gc.IsNil) reference, err := environs.ComposeUserData(cfg, nil) c.Assert(err, gc.IsNil) c.Check(data, gc.DeepEquals, reference) } ������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/azure_test.go���������������������������0000644�0000153�0000161�00000001646�12321735642�026370� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( stdtesting "testing" gc "launchpad.net/gocheck" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/testing/testbase" ) func TestAzureProvider(t *stdtesting.T) { gc.TestingT(t) } type providerSuite struct { testbase.LoggingSuite envtesting.ToolsFixture restoreTimeouts func() } var _ = gc.Suite(&providerSuite{}) func (s *providerSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.restoreTimeouts = envtesting.PatchAttemptStrategies() } func (s *providerSuite) TearDownSuite(c *gc.C) { s.restoreTimeouts() s.LoggingSuite.TearDownSuite(c) } func (s *providerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) } func (s *providerSuite) TearDownTest(c *gc.C) { s.ToolsFixture.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } ������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/storage.go������������������������������0000644�0000153�0000161�00000012667�12321735642�025654� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "io" "net/http" "sync" "time" "launchpad.net/gwacl" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" ) type azureStorage struct { mutex sync.Mutex createdContainer bool storageContext } // storageContext is an abstraction that is there only to accommodate the need // for using an azureStorage independently from an environ object in tests. type storageContext interface { getContainer() string getStorageContext() (*gwacl.StorageContext, error) } // environStorageContext is a storageContext which gets its information from // an azureEnviron object. type environStorageContext struct { environ *azureEnviron } var _ storageContext = (*environStorageContext)(nil) func (context *environStorageContext) getContainer() string { return context.environ.getContainerName() } func (context *environStorageContext) getStorageContext() (*gwacl.StorageContext, error) { return context.environ.getStorageContext() } // azureStorage implements Storage. var _ storage.Storage = (*azureStorage)(nil) // Get is specified in the StorageReader interface. func (storage *azureStorage) Get(name string) (io.ReadCloser, error) { context, err := storage.getStorageContext() if err != nil { return nil, err } reader, err := context.GetBlob(storage.getContainer(), name) if gwacl.IsNotFoundError(err) { return nil, errors.NotFoundf("file %q not found", name) } return reader, err } // List is specified in the StorageReader interface. func (storage *azureStorage) List(prefix string) ([]string, error) { context, err := storage.getStorageContext() if err != nil { return nil, err } request := &gwacl.ListBlobsRequest{Container: storage.getContainer(), Prefix: prefix, Marker: ""} blobList, err := context.ListAllBlobs(request) httpErr, isHTTPErr := err.(gwacl.HTTPError) if isHTTPErr && httpErr.StatusCode() == http.StatusNotFound { // A 404 means the container doesn't exist. There are no files so // just return nothing. return nil, nil } if err != nil { return nil, err } names := make([]string, len(blobList.Blobs)) for index, blob := range blobList.Blobs { names[index] = blob.Name } return names, nil } // URL is specified in the StorageReader interface. func (storage *azureStorage) URL(name string) (string, error) { context, err := storage.getStorageContext() if err != nil { return "", err } if context.Key != "" { // 10 years should be good enough. expires := time.Now().AddDate(10, 0, 0) return context.GetAnonymousFileURL(storage.getContainer(), name, expires) } return context.GetFileURL(storage.getContainer(), name), nil } // ConsistencyStrategy is specified in the StorageReader interface. func (storage *azureStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { // This storage backend has immediate consistency, so there's no // need to wait. One attempt should do. return utils.AttemptStrategy{} } // ShouldRetry is specified in the StorageReader interface. func (storage *azureStorage) ShouldRetry(err error) bool { return false } // Put is specified in the StorageWriter interface. func (storage *azureStorage) Put(name string, r io.Reader, length int64) error { err := storage.createContainer(storage.getContainer()) if err != nil { return err } limitedReader := io.LimitReader(r, length) context, err := storage.getStorageContext() if err != nil { return err } return context.UploadBlockBlob(storage.getContainer(), name, limitedReader) } // Remove is specified in the StorageWriter interface. func (storage *azureStorage) Remove(name string) error { context, err := storage.getStorageContext() if err != nil { return err } return context.DeleteBlob(storage.getContainer(), name) } // RemoveAll is specified in the StorageWriter interface. func (storage *azureStorage) RemoveAll() error { context, err := storage.getStorageContext() if err != nil { return err } return context.DeleteContainer(storage.getContainer()) } // createContainer makes a private container in the storage account. // It can be called when the container already exists and returns with no error // if it does. To avoid unnecessary HTTP requests, we do this only once for // every PUT operation by using a mutex lock and boolean flag. func (storage *azureStorage) createContainer(name string) error { // We must get our storage context before entering our critical // section, because this may lock the environment. context, err := storage.getStorageContext() if err != nil { return err } storage.mutex.Lock() defer storage.mutex.Unlock() if storage.createdContainer { return nil } _, err = context.GetContainerProperties(name) if err == nil { // No error means it's already there, just return now. return nil } httpErr, isHTTPErr := err.(gwacl.HTTPError) if !isHTTPErr || httpErr.StatusCode() != http.StatusNotFound { // We were hoping for a 404: Not Found. That means we can go // ahead and create the container. But we got some other // error. return err } err = context.CreateContainer(name) if err != nil { return err } storage.createdContainer = true return nil } // deleteContainer deletes the named comtainer from the storage account. func (storage *azureStorage) deleteContainer(name string) error { context, err := storage.getStorageContext() if err != nil { return err } return context.DeleteContainer(name) } �������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/instancetype_test.go��������������������0000644�0000153�0000161�00000030111�12321735642�027735� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( gc "launchpad.net/gocheck" "launchpad.net/gwacl" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/testing" ) type instanceTypeSuite struct { providerSuite } var _ = gc.Suite(&instanceTypeSuite{}) func (s *instanceTypeSuite) SetUpTest(c *gc.C) { s.providerSuite.SetUpTest(c) s.PatchValue(&imagemetadata.DefaultBaseURL, "") s.PatchValue(&signedImageDataOnly, false) } // setDummyStorage injects the local provider's fake storage implementation // into the given environment, so that tests can manipulate storage as if it // were real. func (s *instanceTypeSuite) setDummyStorage(c *gc.C, env *azureEnviron) { closer, storage, _ := testing.CreateLocalTestStorage(c) env.storage = storage s.AddCleanup(func(c *gc.C) { closer.Close() }) } func (*instanceTypeSuite) TestNewPreferredTypesAcceptsNil(c *gc.C) { types := newPreferredTypes(nil) c.Check(types, gc.HasLen, 0) c.Check(types.Len(), gc.Equals, 0) } func (*instanceTypeSuite) TestNewPreferredTypesRepresentsInput(c *gc.C) { availableTypes := []gwacl.RoleSize{{Name: "Humongous", Cost: 123}} types := newPreferredTypes(availableTypes) c.Assert(types, gc.HasLen, len(availableTypes)) c.Check(types[0], gc.Equals, &availableTypes[0]) c.Check(types.Len(), gc.Equals, len(availableTypes)) } func (*instanceTypeSuite) TestNewPreferredTypesSortsByCost(c *gc.C) { availableTypes := []gwacl.RoleSize{ {Name: "Excessive", Cost: 12}, {Name: "Ridiculous", Cost: 99}, {Name: "Modest", Cost: 3}, } types := newPreferredTypes(availableTypes) c.Assert(types, gc.HasLen, len(availableTypes)) // We end up with machine types sorted by ascending cost. c.Check(types[0].Name, gc.Equals, "Modest") c.Check(types[1].Name, gc.Equals, "Excessive") c.Check(types[2].Name, gc.Equals, "Ridiculous") } func (*instanceTypeSuite) TestLessComparesCost(c *gc.C) { types := preferredTypes{ {Name: "Cheap", Cost: 1}, {Name: "Posh", Cost: 200}, } c.Check(types.Less(0, 1), gc.Equals, true) c.Check(types.Less(1, 0), gc.Equals, false) } func (*instanceTypeSuite) TestSwapSwitchesEntries(c *gc.C) { types := preferredTypes{ {Name: "First"}, {Name: "Last"}, } types.Swap(0, 1) c.Check(types[0].Name, gc.Equals, "Last") c.Check(types[1].Name, gc.Equals, "First") } func (*instanceTypeSuite) TestSwapIsCommutative(c *gc.C) { types := preferredTypes{ {Name: "First"}, {Name: "Last"}, } types.Swap(1, 0) c.Check(types[0].Name, gc.Equals, "Last") c.Check(types[1].Name, gc.Equals, "First") } func (*instanceTypeSuite) TestSwapLeavesOtherEntriesIntact(c *gc.C) { types := preferredTypes{ {Name: "A"}, {Name: "B"}, {Name: "C"}, {Name: "D"}, } types.Swap(1, 2) c.Check(types[0].Name, gc.Equals, "A") c.Check(types[1].Name, gc.Equals, "C") c.Check(types[2].Name, gc.Equals, "B") c.Check(types[3].Name, gc.Equals, "D") } func (*instanceTypeSuite) TestSufficesAcceptsNilRequirement(c *gc.C) { types := preferredTypes{} c.Check(types.suffices(0, nil), gc.Equals, true) } func (*instanceTypeSuite) TestSufficesAcceptsMetRequirement(c *gc.C) { types := preferredTypes{} var expectation uint64 = 100 c.Check(types.suffices(expectation+1, &expectation), gc.Equals, true) } func (*instanceTypeSuite) TestSufficesAcceptsExactRequirement(c *gc.C) { types := preferredTypes{} var expectation uint64 = 100 c.Check(types.suffices(expectation+1, &expectation), gc.Equals, true) } func (*instanceTypeSuite) TestSufficesRejectsUnmetRequirement(c *gc.C) { types := preferredTypes{} var expectation uint64 = 100 c.Check(types.suffices(expectation-1, &expectation), gc.Equals, false) } func (*instanceTypeSuite) TestSatisfiesComparesCPUCores(c *gc.C) { types := preferredTypes{} var desiredCores uint64 = 5 constraint := constraints.Value{CpuCores: &desiredCores} // A machine with fewer cores than required does not satisfy... machine := gwacl.RoleSize{CpuCores: desiredCores - 1} c.Check(types.satisfies(&machine, constraint), gc.Equals, false) // ...Even if it would, given more cores. machine.CpuCores = desiredCores c.Check(types.satisfies(&machine, constraint), gc.Equals, true) } func (*instanceTypeSuite) TestSatisfiesComparesMem(c *gc.C) { types := preferredTypes{} var desiredMem uint64 = 37 constraint := constraints.Value{Mem: &desiredMem} // A machine with less memory than required does not satisfy... machine := gwacl.RoleSize{Mem: desiredMem - 1} c.Check(types.satisfies(&machine, constraint), gc.Equals, false) // ...Even if it would, given more memory. machine.Mem = desiredMem c.Check(types.satisfies(&machine, constraint), gc.Equals, true) } func (*instanceTypeSuite) TestDefaultToBaselineSpecSetsMimimumMem(c *gc.C) { c.Check( *defaultToBaselineSpec(constraints.Value{}).Mem, gc.Equals, uint64(defaultMem)) } func (*instanceTypeSuite) TestDefaultToBaselineSpecLeavesOriginalIntact(c *gc.C) { original := constraints.Value{} defaultToBaselineSpec(original) c.Check(original.Mem, gc.IsNil) } func (*instanceTypeSuite) TestDefaultToBaselineSpecLeavesLowerMemIntact(c *gc.C) { const low = 100 * gwacl.MB var value uint64 = low c.Check( defaultToBaselineSpec(constraints.Value{Mem: &value}).Mem, gc.Equals, &value) c.Check(value, gc.Equals, uint64(low)) } func (*instanceTypeSuite) TestDefaultToBaselineSpecLeavesHigherMemIntact(c *gc.C) { const high = 100 * gwacl.MB var value uint64 = high c.Check( defaultToBaselineSpec(constraints.Value{Mem: &value}).Mem, gc.Equals, &value) c.Check(value, gc.Equals, uint64(high)) } func (*instanceTypeSuite) TestSelectMachineTypeReturnsErrorIfNoMatch(c *gc.C) { var lots uint64 = 1000000000000 _, err := selectMachineType(nil, constraints.Value{Mem: &lots}) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, "no machine type matches constraints mem=100000*[MGT]") } func (*instanceTypeSuite) TestSelectMachineTypeReturnsCheapestMatch(c *gc.C) { var desiredCores uint64 = 50 availableTypes := []gwacl.RoleSize{ // Cheap, but not up to our requirements. {Name: "Panda", CpuCores: desiredCores / 2, Cost: 10}, // Exactly what we need, but not the cheapest match. {Name: "LFA", CpuCores: desiredCores, Cost: 200}, // Much more power than we need, but actually cheaper. {Name: "Lambo", CpuCores: 2 * desiredCores, Cost: 100}, // Way out of our league. {Name: "Veyron", CpuCores: 10 * desiredCores, Cost: 500}, } choice, err := selectMachineType(availableTypes, constraints.Value{CpuCores: &desiredCores}) c.Assert(err, gc.IsNil) // Out of these options, selectMachineType picks not the first; not // the cheapest; not the biggest; not the last; but the cheapest type // of machine that meets requirements. c.Check(choice.Name, gc.Equals, "Lambo") } func (s *instanceTypeSuite) setupEnvWithDummyMetadata(c *gc.C) *azureEnviron { envAttrs := makeAzureConfigMap(c) envAttrs["location"] = "West US" env := makeEnvironWithConfig(c, envAttrs) s.setDummyStorage(c, env) images := []*imagemetadata.ImageMetadata{ { Id: "image-id", VirtType: "Hyper-V", Arch: "amd64", RegionName: "West US", Endpoint: "https://management.core.windows.net/", }, } makeTestMetadata(c, env, "precise", "West US", images) return env } func (s *instanceTypeSuite) TestFindMatchingImagesReturnsErrorIfNoneFound(c *gc.C) { env := s.setupEnvWithDummyMetadata(c) _, err := findMatchingImages(env, "West US", "saucy", []string{"amd64"}) c.Assert(err, gc.NotNil) c.Assert(err, gc.ErrorMatches, "no OS images found for location .*") } func (s *instanceTypeSuite) TestFindMatchingImagesReturnsReleasedImages(c *gc.C) { env := s.setupEnvWithDummyMetadata(c) images, err := findMatchingImages(env, "West US", "precise", []string{"amd64"}) c.Assert(err, gc.IsNil) c.Assert(images, gc.HasLen, 1) c.Check(images[0].Id, gc.Equals, "image-id") } func (s *instanceTypeSuite) TestFindMatchingImagesReturnsDailyImages(c *gc.C) { envAttrs := makeAzureConfigMap(c) envAttrs["image-stream"] = "daily" envAttrs["location"] = "West US" env := makeEnvironWithConfig(c, envAttrs) s.setDummyStorage(c, env) images := []*imagemetadata.ImageMetadata{ { Id: "image-id", VirtType: "Hyper-V", Arch: "amd64", RegionName: "West US", Endpoint: "https://management.core.windows.net/", Stream: "daily", }, } makeTestMetadata(c, env, "precise", "West US", images) images, err := findMatchingImages(env, "West US", "precise", []string{"amd64"}) c.Assert(err, gc.IsNil) c.Assert(images, gc.HasLen, 1) c.Assert(images[0].Id, gc.Equals, "image-id") } func (*instanceTypeSuite) TestNewInstanceTypeConvertsRoleSize(c *gc.C) { roleSize := gwacl.RoleSize{ Name: "Outrageous", CpuCores: 128, Mem: 4 * gwacl.TB, OSDiskSpaceCloud: 48 * gwacl.TB, OSDiskSpaceVirt: 50 * gwacl.TB, MaxDataDisks: 20, Cost: 999999500, } vtype := "Hyper-V" var cpupower uint64 = 100 expectation := instances.InstanceType{ Id: roleSize.Name, Name: roleSize.Name, CpuCores: roleSize.CpuCores, Mem: roleSize.Mem, RootDisk: roleSize.OSDiskSpaceVirt, Cost: roleSize.Cost, VirtType: &vtype, CpuPower: &cpupower, } c.Assert(newInstanceType(roleSize), gc.DeepEquals, expectation) } func (s *instanceTypeSuite) TestListInstanceTypesAcceptsNil(c *gc.C) { env := s.setupEnvWithDummyMetadata(c) types, err := listInstanceTypes(env, nil) c.Assert(err, gc.IsNil) c.Check(types, gc.HasLen, 0) } func (s *instanceTypeSuite) TestListInstanceTypesMaintainsOrder(c *gc.C) { roleSizes := []gwacl.RoleSize{ {Name: "Biggish"}, {Name: "Tiny"}, {Name: "Huge"}, {Name: "Miniscule"}, } expectation := make([]instances.InstanceType, len(roleSizes)) for index, roleSize := range roleSizes { expectation[index] = newInstanceType(roleSize) expectation[index].Arches = []string{"amd64"} } env := s.setupEnvWithDummyMetadata(c) types, err := listInstanceTypes(env, roleSizes) c.Assert(err, gc.IsNil) c.Assert(types, gc.DeepEquals, expectation) } func (*instanceTypeSuite) TestFindInstanceSpecFailsImpossibleRequest(c *gc.C) { impossibleConstraint := &instances.InstanceConstraint{ Series: "precise", Arches: []string{"axp"}, } env := makeEnviron(c) _, err := findInstanceSpec(env, impossibleConstraint) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, "no OS images found for .*") } func makeTestMetadata(c *gc.C, env environs.Environ, series, location string, im []*imagemetadata.ImageMetadata) { cloudSpec := simplestreams.CloudSpec{ Region: location, Endpoint: "https://management.core.windows.net/", } err := imagemetadata.MergeAndWriteMetadata(series, im, &cloudSpec, env.Storage()) c.Assert(err, gc.IsNil) } func (s *instanceTypeSuite) TestFindInstanceSpecFindsMatch(c *gc.C) { env := s.setupEnvWithDummyMetadata(c) // We'll tailor our constraints to describe one particular Azure // instance type: aim := gwacl.RoleNameMap["Large"] constraints := &instances.InstanceConstraint{ Region: "West US", Series: "precise", Arches: []string{"amd64"}, Constraints: constraints.Value{ CpuCores: &aim.CpuCores, Mem: &aim.Mem, }, } // Find a matching instance type and image. spec, err := findInstanceSpec(env, constraints) c.Assert(err, gc.IsNil) // We got the instance type we described in our constraints, and // the image returned by (the fake) simplestreams. c.Check(spec.InstanceType.Name, gc.Equals, aim.Name) c.Check(spec.Image.Id, gc.Equals, "image-id") } func (s *instanceTypeSuite) TestFindInstanceSpecSetsBaseline(c *gc.C) { env := s.setupEnvWithDummyMetadata(c) // findInstanceSpec sets baseline constraints, so that it won't pick // ExtraSmall (which is too small for routine tasks) if you fail to // set sufficient hardware constraints. anyInstanceType := &instances.InstanceConstraint{ Region: "West US", Series: "precise", Arches: []string{"amd64"}, } spec, err := findInstanceSpec(env, anyInstanceType) c.Assert(err, gc.IsNil) c.Check(spec.InstanceType.Name, gc.Equals, "Small") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/environprovider.go����������������������0000644�0000153�0000161�00000002545�12321735776�027445� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "github.com/juju/loggo" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" ) // Register the Azure provider with Juju. func init() { environs.RegisterProvider("azure", azureEnvironProvider{}) } // Logger for the Azure provider. var logger = loggo.GetLogger("juju.provider.azure") type azureEnvironProvider struct{} // azureEnvironProvider implements EnvironProvider. var _ environs.EnvironProvider = (*azureEnvironProvider)(nil) // Open is specified in the EnvironProvider interface. func (prov azureEnvironProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Debugf("opening environment %q.", cfg.Name()) // We can't return NewEnviron(cfg) directly here because otherwise, // when err is not nil, we end up with a non-nil returned environ and // this breaks the loop in cmd/jujud/upgrade.go:run() (see // http://golang.org/doc/faq#nil_error for the gory details). environ, err := NewEnviron(cfg) if err != nil { return nil, err } return environ, nil } // Prepare is specified in the EnvironProvider interface. func (prov azureEnvironProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { // TODO prepare environment as necessary return prov.Open(cfg) } �����������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/storage_test.go�������������������������0000644�0000153�0000161�00000032256�12321735642�026707� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "encoding/base64" "fmt" "io/ioutil" "net/http" "net/url" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/gwacl" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" ) type storageSuite struct { providerSuite } var _ = gc.Suite(&storageSuite{}) func makeResponse(content string, status int) *http.Response { return &http.Response{ Status: fmt.Sprintf("%d", status), StatusCode: status, Body: ioutil.NopCloser(strings.NewReader(content)), } } // MockingTransportExchange is a recording of a request and a response over // HTTP. type MockingTransportExchange struct { Request *http.Request Response *http.Response Error error } // MockingTransport is used as an http.Client.Transport for testing. It // records the sequence of requests, and returns a predetermined sequence of // Responses and errors. type MockingTransport struct { Exchanges []*MockingTransportExchange ExchangeCount int } // MockingTransport implements the http.RoundTripper interface. var _ http.RoundTripper = &MockingTransport{} func (t *MockingTransport) AddExchange(response *http.Response, err error) { exchange := MockingTransportExchange{Response: response, Error: err} t.Exchanges = append(t.Exchanges, &exchange) } func (t *MockingTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { exchange := t.Exchanges[t.ExchangeCount] t.ExchangeCount++ exchange.Request = req return exchange.Response, exchange.Error } // testStorageContext is a struct implementing the storageContext interface // used in test. It will return, via getContainer() and getStorageContext() // the objects used at creation time. type testStorageContext struct { container string storageContext *gwacl.StorageContext } func (context *testStorageContext) getContainer() string { return context.container } func (context *testStorageContext) getStorageContext() (*gwacl.StorageContext, error) { return context.storageContext, nil } // makeFakeStorage creates a test azureStorage object that will talk to a // fake HTTP server set up to always return preconfigured http.Response objects. // The MockingTransport object can be used to check that the expected query has // been issued to the test server. func makeFakeStorage(container, account, key string) (*azureStorage, *MockingTransport) { transport := &MockingTransport{} client := &http.Client{Transport: transport} storageContext := gwacl.NewTestStorageContext(client) storageContext.Account = account storageContext.Key = key context := &testStorageContext{container: container, storageContext: storageContext} azStorage := &azureStorage{storageContext: context} return azStorage, transport } // setStorageEndpoint sets a given Azure API endpoint on a given azureStorage. func setStorageEndpoint(azStorage *azureStorage, endpoint gwacl.APIEndpoint) { // Ugly, because of the confusingly similar layers of nesting. testContext := azStorage.storageContext.(*testStorageContext) var gwaclContext *gwacl.StorageContext = testContext.storageContext gwaclContext.AzureEndpoint = endpoint } var blobListResponse = ` <?xml version="1.0" encoding="utf-8"?> <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer"> <Prefix>prefix</Prefix> <Marker>marker</Marker> <MaxResults>maxresults</MaxResults> <Delimiter>delimiter</Delimiter> <Blobs> <Blob> <Name>prefix-1</Name> <Url>blob-url1</Url> </Blob> <Blob> <Name>prefix-2</Name> <Url>blob-url2</Url> </Blob> </Blobs> <NextMarker /> </EnumerationResults>` func (*storageSuite) TestList(c *gc.C) { container := "container" response := makeResponse(blobListResponse, http.StatusOK) azStorage, transport := makeFakeStorage(container, "account", "") transport.AddExchange(response, nil) prefix := "prefix" names, err := storage.List(azStorage, prefix) c.Assert(err, gc.IsNil) c.Assert(transport.ExchangeCount, gc.Equals, 1) // The prefix has been passed down as a query parameter. c.Check(transport.Exchanges[0].Request.URL.Query()["prefix"], gc.DeepEquals, []string{prefix}) // The container name is used in the requested URL. c.Check(transport.Exchanges[0].Request.URL.String(), gc.Matches, ".*"+container+".*") c.Check(names, gc.DeepEquals, []string{"prefix-1", "prefix-2"}) } func (*storageSuite) TestListWithNonexistentContainerReturnsNoFiles(c *gc.C) { // If Azure returns a 404 it means the container doesn't exist. In this // case the provider should interpret this as "no files" and return nil. container := "container" response := makeResponse("", http.StatusNotFound) azStorage, transport := makeFakeStorage(container, "account", "") transport.AddExchange(response, nil) names, err := storage.List(azStorage, "prefix") c.Assert(err, gc.IsNil) c.Assert(names, gc.IsNil) } func (*storageSuite) TestGet(c *gc.C) { blobContent := "test blob" container := "container" filename := "blobname" response := makeResponse(blobContent, http.StatusOK) azStorage, transport := makeFakeStorage(container, "account", "") transport.AddExchange(response, nil) reader, err := storage.Get(azStorage, filename) c.Assert(err, gc.IsNil) c.Assert(reader, gc.NotNil) defer reader.Close() context, err := azStorage.getStorageContext() c.Assert(err, gc.IsNil) c.Assert(transport.ExchangeCount, gc.Equals, 1) c.Check(transport.Exchanges[0].Request.URL.String(), gc.Matches, context.GetFileURL(container, filename)+"?.*") data, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(string(data), gc.Equals, blobContent) } func (*storageSuite) TestGetReturnsNotFoundIf404(c *gc.C) { container := "container" filename := "blobname" response := makeResponse("not found", http.StatusNotFound) azStorage, transport := makeFakeStorage(container, "account", "") transport.AddExchange(response, nil) _, err := storage.Get(azStorage, filename) c.Assert(err, gc.NotNil) c.Check(err, jc.Satisfies, errors.IsNotFoundError) } func (*storageSuite) TestPut(c *gc.C) { blobContent := "test blob" container := "container" filename := "blobname" azStorage, transport := makeFakeStorage(container, "account", "") // The create container call makes two exchanges. transport.AddExchange(makeResponse("", http.StatusNotFound), nil) transport.AddExchange(makeResponse("", http.StatusCreated), nil) putResponse := makeResponse("", http.StatusCreated) transport.AddExchange(putResponse, nil) transport.AddExchange(putResponse, nil) err := azStorage.Put(filename, strings.NewReader(blobContent), int64(len(blobContent))) c.Assert(err, gc.IsNil) context, err := azStorage.getStorageContext() c.Assert(err, gc.IsNil) c.Assert(transport.ExchangeCount, gc.Equals, 4) c.Check(transport.Exchanges[2].Request.URL.String(), gc.Matches, context.GetFileURL(container, filename)+"?.*") } func (*storageSuite) TestRemove(c *gc.C) { container := "container" filename := "blobname" response := makeResponse("", http.StatusAccepted) azStorage, transport := makeFakeStorage(container, "account", "") transport.AddExchange(response, nil) err := azStorage.Remove(filename) c.Assert(err, gc.IsNil) context, err := azStorage.getStorageContext() c.Assert(err, gc.IsNil) c.Assert(transport.ExchangeCount, gc.Equals, 1) c.Check(transport.Exchanges[0].Request.URL.String(), gc.Matches, context.GetFileURL(container, filename)+"?.*") c.Check(transport.Exchanges[0].Request.Method, gc.Equals, "DELETE") } func (*storageSuite) TestRemoveErrors(c *gc.C) { container := "container" filename := "blobname" response := makeResponse("", http.StatusForbidden) azStorage, transport := makeFakeStorage(container, "account", "") transport.AddExchange(response, nil) err := azStorage.Remove(filename) c.Assert(err, gc.NotNil) } func (*storageSuite) TestRemoveAll(c *gc.C) { // When we ask gwacl to remove all blobs, it calls DeleteContainer. response := makeResponse("", http.StatusAccepted) storage, transport := makeFakeStorage("cntnr", "account", "") transport.AddExchange(response, nil) err := storage.RemoveAll() c.Assert(err, gc.IsNil) _, err = storage.getStorageContext() c.Assert(err, gc.IsNil) // Without going too far into gwacl's innards, this is roughly what // it needs to do in order to delete a container. c.Assert(transport.ExchangeCount, gc.Equals, 1) c.Check(transport.Exchanges[0].Request.URL.String(), gc.Matches, "http.*/cntnr?.*restype=container.*") c.Check(transport.Exchanges[0].Request.Method, gc.Equals, "DELETE") } func (*storageSuite) TestRemoveNonExistentBlobSucceeds(c *gc.C) { container := "container" filename := "blobname" response := makeResponse("", http.StatusNotFound) azStorage, transport := makeFakeStorage(container, "account", "") transport.AddExchange(response, nil) err := azStorage.Remove(filename) c.Assert(err, gc.IsNil) } func (*storageSuite) TestURL(c *gc.C) { container := "container" filename := "blobname" account := "account" key := "bWFkZXlvdWxvb2sK" azStorage, _ := makeFakeStorage(container, account, key) // Use a realistic service endpoint for this test, so that we can see // that we're really getting the expected kind of URL. setStorageEndpoint(azStorage, gwacl.GetEndpoint("West US")) URL, err := azStorage.URL(filename) c.Assert(err, gc.IsNil) parsedURL, err := url.Parse(URL) c.Assert(err, gc.IsNil) c.Check(parsedURL.Host, gc.Matches, fmt.Sprintf("%s.blob.core.windows.net", account)) c.Check(parsedURL.Path, gc.Matches, fmt.Sprintf("/%s/%s", container, filename)) values, err := url.ParseQuery(parsedURL.RawQuery) c.Assert(err, gc.IsNil) signature := values.Get("sig") // The query string contains a non-empty signature. c.Check(signature, gc.Not(gc.HasLen), 0) // The signature is base64-encoded. _, err = base64.StdEncoding.DecodeString(signature) c.Assert(err, gc.IsNil) // If Key is empty, query string does not contain a signature. key = "" azStorage, _ = makeFakeStorage(container, account, key) URL, err = azStorage.URL(filename) c.Assert(err, gc.IsNil) parsedURL, err = url.Parse(URL) c.Assert(err, gc.IsNil) values, err = url.ParseQuery(parsedURL.RawQuery) c.Assert(err, gc.IsNil) c.Check(values.Get("sig"), gc.HasLen, 0) } func (*storageSuite) TestCreateContainerCreatesContainerIfDoesNotExist(c *gc.C) { azStorage, transport := makeFakeStorage("", "account", "") transport.AddExchange(makeResponse("", http.StatusNotFound), nil) transport.AddExchange(makeResponse("", http.StatusCreated), nil) err := azStorage.createContainer("cntnr") c.Assert(err, gc.IsNil) c.Assert(transport.ExchangeCount, gc.Equals, 2) // Without going too far into gwacl's innards, this is roughly what // it needs to do in order to call GetContainerProperties. c.Check(transport.Exchanges[0].Request.URL.String(), gc.Matches, "http.*/cntnr?.*restype=container.*") c.Check(transport.Exchanges[0].Request.Method, gc.Equals, "GET") // ... and for CreateContainer. c.Check(transport.Exchanges[1].Request.URL.String(), gc.Matches, "http.*/cntnr?.*restype=container.*") c.Check(transport.Exchanges[1].Request.Method, gc.Equals, "PUT") } func (*storageSuite) TestCreateContainerIsDoneIfContainerAlreadyExists(c *gc.C) { container := "" azStorage, transport := makeFakeStorage(container, "account", "") header := make(http.Header) header.Add("Last-Modified", "last-modified") header.Add("ETag", "etag") header.Add("X-Ms-Lease-Status", "status") header.Add("X-Ms-Lease-State", "state") header.Add("X-Ms-Lease-Duration", "duration") response := makeResponse("", http.StatusOK) response.Header = header transport.AddExchange(response, nil) err := azStorage.createContainer("cntnr") c.Assert(err, gc.IsNil) c.Assert(transport.ExchangeCount, gc.Equals, 1) // Without going too far into gwacl's innards, this is roughly what // it needs to do in order to call GetContainerProperties. c.Check(transport.Exchanges[0].Request.URL.String(), gc.Matches, "http.*/cntnr?.*restype=container.*") c.Check(transport.Exchanges[0].Request.Method, gc.Equals, "GET") } func (*storageSuite) TestCreateContainerFailsIfContainerInaccessible(c *gc.C) { azStorage, transport := makeFakeStorage("", "account", "") transport.AddExchange(makeResponse("", http.StatusInternalServerError), nil) err := azStorage.createContainer("cntnr") c.Assert(err, gc.NotNil) // createContainer got an error when trying to query for an existing // container of the right name. But it does not mistake that error for // "this container does not exist yet so go ahead and create it." // The proper response to the situation is to report the failure. c.Assert(err, gc.ErrorMatches, ".*Internal Server Error.*") } func (*storageSuite) TestDeleteContainer(c *gc.C) { azStorage, transport := makeFakeStorage("", "account", "") transport.AddExchange(makeResponse("", http.StatusAccepted), nil) err := azStorage.deleteContainer("cntnr") c.Assert(err, gc.IsNil) c.Assert(transport.ExchangeCount, gc.Equals, 1) // Without going too far into gwacl's innards, this is roughly what // it needs to do in order to call GetContainerProperties. c.Check(transport.Exchanges[0].Request.URL.String(), gc.Matches, "http.*/cntnr?.*restype=container.*") c.Check(transport.Exchanges[0].Request.Method, gc.Equals, "DELETE") } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/environprovider_test.go�����������������0000644�0000153�0000161�00000002233�12321735642�030466� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" ) type environProviderSuite struct { providerSuite } var _ = gc.Suite(&environProviderSuite{}) func (*environProviderSuite) TestOpen(c *gc.C) { prov := azureEnvironProvider{} attrs := makeAzureConfigMap(c) attrs["name"] = "my-shiny-new-env" cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := prov.Open(cfg) c.Assert(err, gc.IsNil) c.Check(env.Name(), gc.Equals, attrs["name"]) } func (environProviderSuite) TestOpenReturnsNilInterfaceUponFailure(c *gc.C) { prov := azureEnvironProvider{} attrs := makeAzureConfigMap(c) // Make the config invalid. attrs["location"] = "" cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := prov.Open(cfg) // When Open() fails (i.e. returns a non-nil error), it returns an // environs.Environ interface object with a nil value and a nil // type. c.Check(env, gc.Equals, nil) c.Check(err, gc.ErrorMatches, ".*environment has no location; you need to set one.*") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/environ.go������������������������������0000644�0000153�0000161�00000074064�12321735776�025677� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "fmt" "net/http" "sync" "time" "launchpad.net/gwacl" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/parallel" ) const ( // In our initial implementation, each instance gets its own hosted // service, deployment and role in Azure. The role always gets this // hostname (instance==service). roleHostname = "default" // deploymentSlot says in which slot to deploy instances. Azure // supports 'Production' or 'Staging'. // This provider always deploys to Production. Think twice about // changing that: DNS names in the staging slot work differently from // those in the production slot. In Staging, Azure assigns an // arbitrary hostname that we can then extract from the deployment's // URL. In Production, the hostname in the deployment URL does not // actually seem to resolve; instead, the service name is used as the // DNS name, with ".cloudapp.net" appended. deploymentSlot = "Production" // Address space of the virtual network used by the nodes in this // environement, in CIDR notation. This is the network used for // machine-to-machine communication. networkDefinition = "10.0.0.0/8" ) type azureEnviron struct { // Except where indicated otherwise, all fields in this object should // only be accessed using a lock or a snapshot. sync.Mutex // name is immutable; it does not need locking. name string // archMutex gates access to supportedArchitectures archMutex sync.Mutex // supportedArchitectures caches the architectures // for which images can be instantiated. supportedArchitectures []string // ecfg is the environment's Azure-specific configuration. ecfg *azureEnvironConfig // storage is this environ's own private storage. storage storage.Storage // storageAccountKey holds an access key to this environment's // private storage. This is automatically queried from Azure on // startup. storageAccountKey string } // azureEnviron implements Environ and HasRegion. var _ environs.Environ = (*azureEnviron)(nil) var _ simplestreams.HasRegion = (*azureEnviron)(nil) var _ imagemetadata.SupportsCustomSources = (*azureEnviron)(nil) var _ envtools.SupportsCustomSources = (*azureEnviron)(nil) // NewEnviron creates a new azureEnviron. func NewEnviron(cfg *config.Config) (*azureEnviron, error) { env := azureEnviron{name: cfg.Name()} err := env.SetConfig(cfg) if err != nil { return nil, err } // Set up storage. env.storage = &azureStorage{ storageContext: &environStorageContext{environ: &env}, } return &env, nil } // extractStorageKey returns the primary account key from a gwacl // StorageAccountKeys struct, or if there is none, the secondary one. func extractStorageKey(keys *gwacl.StorageAccountKeys) string { if keys.Primary != "" { return keys.Primary } return keys.Secondary } // queryStorageAccountKey retrieves the storage account's key from Azure. func (env *azureEnviron) queryStorageAccountKey() (string, error) { azure, err := env.getManagementAPI() if err != nil { return "", err } defer env.releaseManagementAPI(azure) accountName := env.getSnapshot().ecfg.storageAccountName() keys, err := azure.GetStorageAccountKeys(accountName) if err != nil { return "", fmt.Errorf("cannot obtain storage account keys: %v", err) } key := extractStorageKey(keys) if key == "" { return "", fmt.Errorf("no keys available for storage account") } return key, nil } // Name is specified in the Environ interface. func (env *azureEnviron) Name() string { return env.name } // getSnapshot produces an atomic shallow copy of the environment object. // Whenever you need to access the environment object's fields without // modifying them, get a snapshot and read its fields instead. You will // get a consistent view of the fields without any further locking. // If you do need to modify the environment's fields, do not get a snapshot // but lock the object throughout the critical section. func (env *azureEnviron) getSnapshot() *azureEnviron { env.Lock() defer env.Unlock() // Copy the environment. (Not the pointer, the environment itself.) // This is a shallow copy. snap := *env // Reset the snapshot's mutex, because we just copied it while we // were holding it. The snapshot will have a "clean," unlocked mutex. snap.Mutex = sync.Mutex{} return &snap } // getAffinityGroupName returns the name of the affinity group used by all // the Services in this environment. func (env *azureEnviron) getAffinityGroupName() string { return env.getEnvPrefix() + "ag" } func (env *azureEnviron) createAffinityGroup() error { affinityGroupName := env.getAffinityGroupName() azure, err := env.getManagementAPI() if err != nil { return err } defer env.releaseManagementAPI(azure) snap := env.getSnapshot() location := snap.ecfg.location() cag := gwacl.NewCreateAffinityGroup(affinityGroupName, affinityGroupName, affinityGroupName, location) return azure.CreateAffinityGroup(&gwacl.CreateAffinityGroupRequest{ CreateAffinityGroup: cag}) } func (env *azureEnviron) deleteAffinityGroup() error { affinityGroupName := env.getAffinityGroupName() azure, err := env.getManagementAPI() if err != nil { return err } defer env.releaseManagementAPI(azure) return azure.DeleteAffinityGroup(&gwacl.DeleteAffinityGroupRequest{ Name: affinityGroupName}) } // getVirtualNetworkName returns the name of the virtual network used by all // the VMs in this environment. func (env *azureEnviron) getVirtualNetworkName() string { return env.getEnvPrefix() + "vnet" } func (env *azureEnviron) createVirtualNetwork() error { vnetName := env.getVirtualNetworkName() affinityGroupName := env.getAffinityGroupName() azure, err := env.getManagementAPI() if err != nil { return err } defer env.releaseManagementAPI(azure) virtualNetwork := gwacl.VirtualNetworkSite{ Name: vnetName, AffinityGroup: affinityGroupName, AddressSpacePrefixes: []string{ networkDefinition, }, } return azure.AddVirtualNetworkSite(&virtualNetwork) } func (env *azureEnviron) deleteVirtualNetwork() error { azure, err := env.getManagementAPI() if err != nil { return err } defer env.releaseManagementAPI(azure) vnetName := env.getVirtualNetworkName() return azure.RemoveVirtualNetworkSite(vnetName) } // getContainerName returns the name of the private storage account container // that this environment is using. func (env *azureEnviron) getContainerName() string { return env.getEnvPrefix() + "private" } // Bootstrap is specified in the Environ interface. func (env *azureEnviron) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) (err error) { // The creation of the affinity group and the virtual network is specific to the Azure provider. err = env.createAffinityGroup() if err != nil { return err } // If we fail after this point, clean up the affinity group. defer func() { if err != nil { env.deleteAffinityGroup() } }() err = env.createVirtualNetwork() if err != nil { return err } // If we fail after this point, clean up the virtual network. defer func() { if err != nil { env.deleteVirtualNetwork() } }() err = common.Bootstrap(ctx, env, cons) return err } // StateInfo is specified in the Environ interface. func (env *azureEnviron) StateInfo() (*state.Info, *api.Info, error) { return common.StateInfo(env) } // Config is specified in the Environ interface. func (env *azureEnviron) Config() *config.Config { snap := env.getSnapshot() return snap.ecfg.Config } // SetConfig is specified in the Environ interface. func (env *azureEnviron) SetConfig(cfg *config.Config) error { ecfg, err := azureEnvironProvider{}.newConfig(cfg) if err != nil { return err } env.Lock() defer env.Unlock() if env.ecfg != nil { _, err = azureEnvironProvider{}.Validate(cfg, env.ecfg.Config) if err != nil { return err } } env.ecfg = ecfg // Reset storage account key. Even if we had one before, it may not // be appropriate for the new config. env.storageAccountKey = "" return nil } // attemptCreateService tries to create a new hosted service on Azure, with a // name it chooses (based on the given prefix), but recognizes that the name // may not be available. If the name is not available, it does not treat that // as an error but just returns nil. func attemptCreateService(azure *gwacl.ManagementAPI, prefix string, affinityGroupName string, location string) (*gwacl.CreateHostedService, error) { var err error name := gwacl.MakeRandomHostedServiceName(prefix) err = azure.CheckHostedServiceNameAvailability(name) if err != nil { // The calling function should retry. return nil, nil } req := gwacl.NewCreateHostedServiceWithLocation(name, name, location) req.AffinityGroup = affinityGroupName err = azure.AddHostedService(req) if err != nil { return nil, err } return req, nil } // newHostedService creates a hosted service. It will make up a unique name, // starting with the given prefix. func newHostedService(azure *gwacl.ManagementAPI, prefix string, affinityGroupName string, location string) (*gwacl.CreateHostedService, error) { var err error var svc *gwacl.CreateHostedService for tries := 10; tries > 0 && err == nil && svc == nil; tries-- { svc, err = attemptCreateService(azure, prefix, affinityGroupName, location) } if err != nil { return nil, fmt.Errorf("could not create hosted service: %v", err) } if svc == nil { return nil, fmt.Errorf("could not come up with a unique hosted service name - is your randomizer initialized?") } return svc, nil } // SupportedArchitectures is specified on the EnvironCapability interface. func (env *azureEnviron) SupportedArchitectures() ([]string, error) { env.archMutex.Lock() defer env.archMutex.Unlock() if env.supportedArchitectures != nil { return env.supportedArchitectures, nil } // Create a filter to get all images from our region and for the correct stream. ecfg := env.getSnapshot().ecfg region := ecfg.location() cloudSpec := simplestreams.CloudSpec{ Region: region, Endpoint: getEndpoint(region), } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: cloudSpec, Stream: ecfg.ImageStream(), }) var err error env.supportedArchitectures, err = common.SupportedArchitectures(env, imageConstraint) return env.supportedArchitectures, err } // selectInstanceTypeAndImage returns the appropriate instance-type name and // the OS image name for launching a virtual machine with the given parameters. func (env *azureEnviron) selectInstanceTypeAndImage(constraint *instances.InstanceConstraint) (string, string, error) { ecfg := env.getSnapshot().ecfg sourceImageName := ecfg.forceImageName() if sourceImageName != "" { // Configuration forces us to use a specific image. There may // not be a suitable image in the simplestreams database. // This means we can't use Juju's normal selection mechanism, // because it combines instance-type and image selection: if // there are no images we can use, it won't offer us an // instance type either. // // Select the instance type using simple, Azure-specific code. machineType, err := selectMachineType(gwacl.RoleSizes, defaultToBaselineSpec(constraint.Constraints)) if err != nil { return "", "", err } return machineType.Name, sourceImageName, nil } // Choose the most suitable instance type and OS image, based on simplestreams information. spec, err := findInstanceSpec(env, constraint) if err != nil { return "", "", err } return spec.InstanceType.Id, spec.Image.Id, nil } // StartInstance is specified in the InstanceBroker interface. func (env *azureEnviron) StartInstance(args environs.StartInstanceParams) (_ instance.Instance, _ *instance.HardwareCharacteristics, err error) { // Declaring "err" in the function signature so that we can "defer" // any cleanup that needs to run during error returns. err = environs.FinishMachineConfig(args.MachineConfig, env.Config(), args.Constraints) if err != nil { return nil, nil, err } // Pick envtools. Needed for the custom data (which is what we normally // call userdata). args.MachineConfig.Tools = args.Tools[0] logger.Infof("picked tools %q", args.MachineConfig.Tools) // Compose userdata. userData, err := makeCustomData(args.MachineConfig) if err != nil { return nil, nil, fmt.Errorf("custom data: %v", err) } azure, err := env.getManagementAPI() if err != nil { return nil, nil, err } defer env.releaseManagementAPI(azure) snap := env.getSnapshot() location := snap.ecfg.location() service, err := newHostedService(azure.ManagementAPI, env.getEnvPrefix(), env.getAffinityGroupName(), location) if err != nil { return nil, nil, err } serviceName := service.ServiceName // If we fail after this point, clean up the hosted service. defer func() { if err != nil { azure.DestroyHostedService( &gwacl.DestroyHostedServiceRequest{ ServiceName: serviceName, }) } }() instanceType, sourceImageName, err := env.selectInstanceTypeAndImage(&instances.InstanceConstraint{ Region: location, Series: args.Tools.OneSeries(), Arches: args.Tools.Arches(), Constraints: args.Constraints, }) if err != nil { return nil, nil, err } // virtualNetworkName is the virtual network to which all the // deployments in this environment belong. virtualNetworkName := env.getVirtualNetworkName() // 1. Create an OS Disk. vhd := env.newOSDisk(sourceImageName) // 2. Create a Role for a Linux machine. role := env.newRole(instanceType, vhd, userData, roleHostname) // 3. Create the Deployment object. deployment := env.newDeployment(role, serviceName, serviceName, virtualNetworkName) err = azure.AddDeployment(deployment, serviceName) if err != nil { return nil, nil, err } var inst instance.Instance // From here on, remember to shut down the instance before returning // any error. defer func() { if err != nil && inst != nil { err2 := env.StopInstances([]instance.Instance{inst}) if err2 != nil { // Failure upon failure. Log it, but return // the original error. logger.Errorf("error releasing failed instance: %v", err) } } }() // Assign the returned instance to 'inst' so that the deferred method // above can perform its check. inst, err = env.getInstance(serviceName) if err != nil { return nil, nil, err } // TODO(bug 1193998) - return instance hardware characteristics as well return inst, &instance.HardwareCharacteristics{}, nil } // getInstance returns an up-to-date version of the instance with the given // name. func (env *azureEnviron) getInstance(instanceName string) (instance.Instance, error) { context, err := env.getManagementAPI() if err != nil { return nil, err } defer env.releaseManagementAPI(context) service, err := context.GetHostedServiceProperties(instanceName, false) if err != nil { return nil, fmt.Errorf("could not get instance %q: %v", instanceName, err) } instance := &azureInstance{service.HostedServiceDescriptor, env} return instance, nil } // newOSDisk creates a gwacl.OSVirtualHardDisk object suitable for an // Azure Virtual Machine. func (env *azureEnviron) newOSDisk(sourceImageName string) *gwacl.OSVirtualHardDisk { vhdName := gwacl.MakeRandomDiskName("juju") vhdPath := fmt.Sprintf("vhds/%s", vhdName) snap := env.getSnapshot() storageAccount := snap.ecfg.storageAccountName() mediaLink := gwacl.CreateVirtualHardDiskMediaLink(storageAccount, vhdPath) // The disk label is optional and the disk name can be omitted if // mediaLink is provided. return gwacl.NewOSVirtualHardDisk("", "", "", mediaLink, sourceImageName, "Linux") } // getInitialEndpoints returns a slice of the endpoints every instance should have open // (ssh port, etc). func (env *azureEnviron) getInitialEndpoints() []gwacl.InputEndpoint { cfg := env.Config() return []gwacl.InputEndpoint{ { LocalPort: 22, Name: "sshport", Port: 22, Protocol: "tcp", }, // TODO: Ought to have this only for state servers. { LocalPort: cfg.StatePort(), Name: "stateport", Port: cfg.StatePort(), Protocol: "tcp", }, // TODO: Ought to have this only for API servers. { LocalPort: cfg.APIPort(), Name: "apiport", Port: cfg.APIPort(), Protocol: "tcp", }} } // newRole creates a gwacl.Role object (an Azure Virtual Machine) which uses // the given Virtual Hard Drive. // // The VM will have: // - an 'ubuntu' user defined with an unguessable (randomly generated) password // - its ssh port (TCP 22) open // - its state port (TCP mongoDB) port open // - its API port (TCP) open // // roleSize is the name of one of Azure's machine types, e.g. ExtraSmall, // Large, A6 etc. func (env *azureEnviron) newRole(roleSize string, vhd *gwacl.OSVirtualHardDisk, userData string, roleHostname string) *gwacl.Role { // Create a Linux Configuration with the username and the password // empty and disable SSH with password authentication. hostname := roleHostname username := "ubuntu" password := gwacl.MakeRandomPassword() linuxConfigurationSet := gwacl.NewLinuxProvisioningConfigurationSet(hostname, username, password, userData, "true") // Generate a Network Configuration with the initially required ports // open. networkConfigurationSet := gwacl.NewNetworkConfigurationSet(env.getInitialEndpoints(), nil) roleName := gwacl.MakeRandomRoleName("juju") // The ordering of these configuration sets is significant for the tests. return gwacl.NewRole( roleSize, roleName, []gwacl.ConfigurationSet{*linuxConfigurationSet, *networkConfigurationSet}, []gwacl.OSVirtualHardDisk{*vhd}) } // newDeployment creates and returns a gwacl Deployment object. func (env *azureEnviron) newDeployment(role *gwacl.Role, deploymentName string, deploymentLabel string, virtualNetworkName string) *gwacl.Deployment { // Use the service name as the label for the deployment. return gwacl.NewDeploymentForCreateVMDeployment(deploymentName, deploymentSlot, deploymentLabel, []gwacl.Role{*role}, virtualNetworkName) } // Spawn this many goroutines to issue requests for destroying services. // TODO: this is currently set to 1 because of a problem in Azure: // removing Services in the same affinity group concurrently causes a conflict. // This conflict is wrongly reported by Azure as a BadRequest (400). // This has been reported to Windows Azure. var maxConcurrentDeletes = 1 // StartInstance is specified in the InstanceBroker interface. func (env *azureEnviron) StopInstances(instances []instance.Instance) error { // Each Juju instance is an Azure Service (instance==service), destroy // all the Azure services. // Acquire management API object. context, err := env.getManagementAPI() if err != nil { return err } defer env.releaseManagementAPI(context) // Destroy all the services in parallel. run := parallel.NewRun(maxConcurrentDeletes) for _, instance := range instances { serviceName := string(instance.Id()) run.Do(func() error { request := &gwacl.DestroyHostedServiceRequest{ServiceName: serviceName} return context.DestroyHostedService(request) }) } return run.Wait() } // Instances is specified in the Environ interface. func (env *azureEnviron) Instances(ids []instance.Id) ([]instance.Instance, error) { // The instance list is built using the list of all the relevant // Azure Services (instance==service). // Acquire management API object. context, err := env.getManagementAPI() if err != nil { return nil, err } defer env.releaseManagementAPI(context) // Prepare gwacl request object. serviceNames := make([]string, len(ids)) for i, id := range ids { serviceNames[i] = string(id) } request := &gwacl.ListSpecificHostedServicesRequest{ServiceNames: serviceNames} // Issue 'ListSpecificHostedServices' request with gwacl. services, err := context.ListSpecificHostedServices(request) if err != nil { return nil, err } // If no instances were found, return ErrNoInstances. if len(services) == 0 { return nil, environs.ErrNoInstances } instances := convertToInstances(services, env) // Check if we got a partial result. if len(ids) != len(instances) { return instances, environs.ErrPartialInstances } return instances, nil } // AllInstances is specified in the InstanceBroker interface. func (env *azureEnviron) AllInstances() ([]instance.Instance, error) { // The instance list is built using the list of all the Azure // Services (instance==service). // Acquire management API object. context, err := env.getManagementAPI() if err != nil { return nil, err } defer env.releaseManagementAPI(context) request := &gwacl.ListPrefixedHostedServicesRequest{ServiceNamePrefix: env.getEnvPrefix()} services, err := context.ListPrefixedHostedServices(request) if err != nil { return nil, err } return convertToInstances(services, env), nil } // getEnvPrefix returns the prefix used to name the objects specific to this // environment. func (env *azureEnviron) getEnvPrefix() string { return fmt.Sprintf("juju-%s-", env.Name()) } // convertToInstances converts a slice of gwacl.HostedServiceDescriptor objects // into a slice of instance.Instance objects. func convertToInstances(services []gwacl.HostedServiceDescriptor, env *azureEnviron) []instance.Instance { instances := make([]instance.Instance, len(services)) for i, service := range services { instances[i] = &azureInstance{service, env} } return instances } // Storage is specified in the Environ interface. func (env *azureEnviron) Storage() storage.Storage { return env.getSnapshot().storage } // Destroy is specified in the Environ interface. func (env *azureEnviron) Destroy() error { logger.Debugf("destroying environment %q", env.name) // Stop all instances. insts, err := env.AllInstances() if err != nil { return fmt.Errorf("cannot get instances: %v", err) } err = env.StopInstances(insts) if err != nil { return fmt.Errorf("cannot stop instances: %v", err) } // Delete vnet and affinity group. err = env.deleteVirtualNetwork() if err != nil { return fmt.Errorf("cannot delete the environment's virtual network: %v", err) } err = env.deleteAffinityGroup() if err != nil { return fmt.Errorf("cannot delete the environment's affinity group: %v", err) } // Delete storage. // Deleting the storage is done last so that if something fails // half way through the Destroy() method, the storage won't be cleaned // up and thus an attempt to re-boostrap the environment will lead to // a "error: environment is already bootstrapped" error. err = env.Storage().RemoveAll() if err != nil { return fmt.Errorf("cannot clean up storage: %v", err) } return nil } // OpenPorts is specified in the Environ interface. However, Azure does not // support the global firewall mode. func (env *azureEnviron) OpenPorts(ports []instance.Port) error { return nil } // ClosePorts is specified in the Environ interface. However, Azure does not // support the global firewall mode. func (env *azureEnviron) ClosePorts(ports []instance.Port) error { return nil } // Ports is specified in the Environ interface. func (env *azureEnviron) Ports() ([]instance.Port, error) { // TODO: implement this. return []instance.Port{}, nil } // Provider is specified in the Environ interface. func (env *azureEnviron) Provider() environs.EnvironProvider { return azureEnvironProvider{} } // azureManagementContext wraps two things: a gwacl.ManagementAPI (effectively // a session on the Azure management API) and a tempCertFile, which keeps track // of the temporary certificate file that needs to be deleted once we're done // with this particular session. // Since it embeds *gwacl.ManagementAPI, you can use it much as if it were a // pointer to a ManagementAPI object. Just don't forget to release it after // use. type azureManagementContext struct { *gwacl.ManagementAPI certFile *tempCertFile } var ( retryPolicy = gwacl.RetryPolicy{ NbRetries: 6, HttpStatusCodes: []int{ http.StatusConflict, http.StatusRequestTimeout, http.StatusInternalServerError, http.StatusServiceUnavailable, }, Delay: 10 * time.Second} ) // getManagementAPI obtains a context object for interfacing with Azure's // management API. // For now, each invocation just returns a separate object. This is probably // wasteful (each context gets its own SSL connection) and may need optimizing // later. func (env *azureEnviron) getManagementAPI() (*azureManagementContext, error) { snap := env.getSnapshot() subscription := snap.ecfg.managementSubscriptionId() certData := snap.ecfg.managementCertificate() certFile, err := newTempCertFile([]byte(certData)) if err != nil { return nil, err } // After this point, if we need to leave prematurely, we should clean // up that certificate file. location := snap.ecfg.location() mgtAPI, err := gwacl.NewManagementAPIWithRetryPolicy(subscription, certFile.Path(), location, retryPolicy) if err != nil { certFile.Delete() return nil, err } context := azureManagementContext{ ManagementAPI: mgtAPI, certFile: certFile, } return &context, nil } // releaseManagementAPI frees up a context object obtained through // getManagementAPI. func (env *azureEnviron) releaseManagementAPI(context *azureManagementContext) { // Be tolerant to incomplete context objects, in case we ever get // called during cleanup of a failed attempt to create one. if context == nil || context.certFile == nil { return } // For now, all that needs doing is to delete the temporary certificate // file. We may do cleverer things later, such as connection pooling // where this method returns a context to the pool. context.certFile.Delete() } // updateStorageAccountKey queries the storage account key, and updates the // version cached in env.storageAccountKey. // // It takes a snapshot in order to preserve transactional integrity relative // to the snapshot's starting state, without having to lock the environment // for the duration. If there is a conflicting change to env relative to the // state recorded in the snapshot, this function will fail. func (env *azureEnviron) updateStorageAccountKey(snapshot *azureEnviron) (string, error) { // This method follows an RCU pattern, an optimistic technique to // implement atomic read-update transactions: get a consistent snapshot // of state; process data; enter critical section; check for conflicts; // write back changes. The advantage is that there are no long-held // locks, in particular while waiting for the request to Azure to // complete. // "Get a consistent snapshot of state" is the caller's responsibility. // The caller can use env.getSnapshot(). // Process data: get a current account key from Azure. key, err := env.queryStorageAccountKey() if err != nil { return "", err } // Enter critical section. env.Lock() defer env.Unlock() // Check for conflicts: is the config still what it was? if env.ecfg != snapshot.ecfg { // The environment has been reconfigured while we were // working on this, so the key we just get may not be // appropriate any longer. So fail. // Whatever we were doing isn't likely to be right any more // anyway. Otherwise, it might be worth returning the key // just in case it still works, and proceed without updating // env.storageAccountKey. return "", fmt.Errorf("environment was reconfigured") } // Write back changes. env.storageAccountKey = key return key, nil } // getStorageContext obtains a context object for interfacing with Azure's // storage API. // For now, each invocation just returns a separate object. This is probably // wasteful (each context gets its own SSL connection) and may need optimizing // later. func (env *azureEnviron) getStorageContext() (*gwacl.StorageContext, error) { snap := env.getSnapshot() key := snap.storageAccountKey if key == "" { // We don't know the storage-account key yet. Request it. var err error key, err = env.updateStorageAccountKey(snap) if err != nil { return nil, err } } context := gwacl.StorageContext{ Account: snap.ecfg.storageAccountName(), Key: key, AzureEndpoint: gwacl.GetEndpoint(snap.ecfg.location()), RetryPolicy: retryPolicy, } return &context, nil } // baseURLs specifies an Azure specific location where we look for simplestreams information. // It contains the central databases for the released and daily streams, but this may // become more configurable. This variable is here as a placeholder, but also // as an injection point for tests. var baseURLs = []string{} // GetImageSources returns a list of sources which are used to search for simplestreams image metadata. func (env *azureEnviron) GetImageSources() ([]simplestreams.DataSource, error) { sources := make([]simplestreams.DataSource, 1+len(baseURLs)) sources[0] = storage.NewStorageSimpleStreamsDataSource("cloud storage", env.Storage(), storage.BaseImagesPath) for i, url := range baseURLs { sources[i+1] = simplestreams.NewURLDataSource("Azure base URL", url, utils.VerifySSLHostnames) } return sources, nil } // GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata. func (env *azureEnviron) GetToolsSources() ([]simplestreams.DataSource, error) { // Add the simplestreams source off the control bucket. sources := []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", env.Storage(), storage.BaseToolsPath)} return sources, nil } // getImageMetadataSigningRequired returns whether this environment requires // image metadata from Simplestreams to be signed. func (env *azureEnviron) getImageMetadataSigningRequired() bool { // Hard-coded to true for now. Once we support custom base URLs, // this may have to change. return true } // Region is specified in the HasRegion interface. func (env *azureEnviron) Region() (simplestreams.CloudSpec, error) { ecfg := env.getSnapshot().ecfg return simplestreams.CloudSpec{ Region: ecfg.location(), Endpoint: string(gwacl.GetEndpoint(ecfg.location())), }, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/certfile_test.go������������������������0000644�0000153�0000161�00000003507�12321735642�027035� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "io/ioutil" "os" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" ) type certFileSuite struct{} var _ = gc.Suite(&certFileSuite{}) func (*certFileSuite) TestPathReturnsFullPath(c *gc.C) { certFile := tempCertFile{tempDir: "/tmp/dir", filename: "file"} c.Check(certFile.Path(), gc.Equals, "/tmp/dir/file") } func (*certFileSuite) TestNewTempCertFileCreatesFile(c *gc.C) { certData := []byte("content") certFile, err := newTempCertFile(certData) c.Assert(err, gc.IsNil) defer certFile.Delete() storedData, err := ioutil.ReadFile(certFile.Path()) c.Assert(err, gc.IsNil) c.Check(storedData, gc.DeepEquals, certData) } func (*certFileSuite) TestNewTempCertFileRestrictsAccessToFile(c *gc.C) { certFile, err := newTempCertFile([]byte("content")) c.Assert(err, gc.IsNil) defer certFile.Delete() info, err := os.Stat(certFile.Path()) c.Assert(err, gc.IsNil) c.Check(info.Mode().Perm(), gc.Equals, os.FileMode(0600)) } func (*certFileSuite) TestNewTempCertFileRestrictsAccessToDir(c *gc.C) { certFile, err := newTempCertFile([]byte("content")) c.Assert(err, gc.IsNil) defer certFile.Delete() info, err := os.Stat(certFile.tempDir) c.Assert(err, gc.IsNil) c.Check(info.Mode().Perm(), gc.Equals, os.FileMode(0700)) } func (*certFileSuite) TestDeleteRemovesFile(c *gc.C) { certFile, err := newTempCertFile([]byte("content")) c.Assert(err, gc.IsNil) certFile.Delete() _, err = os.Open(certFile.Path()) c.Assert(err, jc.Satisfies, os.IsNotExist) } func (*certFileSuite) TestDeleteIsIdempotent(c *gc.C) { certFile, err := newTempCertFile([]byte("content")) c.Assert(err, gc.IsNil) certFile.Delete() certFile.Delete() _, err = os.Open(certFile.Path()) c.Assert(err, jc.Satisfies, os.IsNotExist) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/certfile.go�����������������������������0000644�0000153�0000161�00000005712�12321735642�025776� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package azure import ( "fmt" "io/ioutil" "math/rand" "os" "path" "launchpad.net/juju-core/utils" ) // tempCertFile is a temporary file containing an x509 certificate. // It's possible to pass a certificate to libcurl in-memory, but much more // complicated. We went with this hack for now. Call newTempCertFile to // store a certificate in a temporary file, and once you're done with the // file, invoke its Delete method to clean it up. type tempCertFile struct { tempDir string filename string } // Path returns the full absolute path for the temporary certificate file. func (certFile *tempCertFile) Path() string { return path.Join(certFile.tempDir, certFile.filename) } // Delete cleans up a tempCertFile. You must call this after use, or you'll // leave not just garbage but security-sensitive garbage. // This method is idempotent. If called after it's already been run, it // does nothing. func (certFile *tempCertFile) Delete() { if certFile.tempDir == "" { // Either it wasn't constructed, or it's been deleted already. return } err := os.RemoveAll(certFile.tempDir) if err != nil { panic(err) } // We no longer own a file that needs cleaning up. certFile.filename = "" certFile.tempDir = "" } // newTempCertFile stores the given x509 certificate in a temporary file, // which only the current user will be allowed to access. // You *must* clean up the file after use, by calling its Delete method. func newTempCertFile(data []byte) (certFile *tempCertFile, err error) { // Add context to any error we may return. defer utils.ErrorContextf(&err, "failed while writing temporary certificate file") // Access permissions for these temporary files: const ( // Owner can read/write temporary files. Not backed up. fileMode = 0600 | os.ModeTemporary | os.ModeExclusive // Temporary dirs are like files, but owner also has "x" // permission. dirMode = fileMode | 0100 ) certFile = &tempCertFile{} // We'll randomize the file's name, so that even someone with access // to the temporary directory (perhaps a group member sneaking in // just before we close access to the directory) won't be able to // guess its name and inject their own file. certFile.filename = fmt.Sprintf("x509-%d.cert", rand.Int31()) // To guarantee that nobody else will be able to access the file, even // by predicting or guessing its name, we create the file in its own // private directory. certFile.tempDir, err = ioutil.TempDir("", "juju-azure") if err != nil { return nil, err } err = os.Chmod(certFile.tempDir, dirMode) if err != nil { return nil, err } // Now, at last, write the file. WriteFile could have done most of // the work on its own, but it doesn't guarantee that nobody creates // a file of the same name first. When that happens, you get a file // but not with the requested permissions. err = ioutil.WriteFile(certFile.Path(), data, fileMode) if err != nil { os.RemoveAll(certFile.tempDir) return nil, err } return certFile, nil } ������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/azure/environ_test.go�������������������������0000644�0000153�0000161�00000132457�12321735776�026737� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package azure import ( "bytes" "encoding/base64" "encoding/xml" "fmt" "io/ioutil" "net/http" "net/url" "path" "regexp" "strings" "sync" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/gwacl" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/testing" ) type environSuite struct { providerSuite } var _ = gc.Suite(&environSuite{}) // makeEnviron creates a fake azureEnviron with arbitrary configuration. func makeEnviron(c *gc.C) *azureEnviron { attrs := makeAzureConfigMap(c) return makeEnvironWithConfig(c, attrs) } // makeEnviron creates a fake azureEnviron with the specified configuration. func makeEnvironWithConfig(c *gc.C, attrs map[string]interface{}) *azureEnviron { cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := NewEnviron(cfg) c.Assert(err, gc.IsNil) // Prevent the test from trying to query for a storage-account key. env.storageAccountKey = "fake-storage-account-key" return env } // setDummyStorage injects the local provider's fake storage implementation // into the given environment, so that tests can manipulate storage as if it // were real. func (s *environSuite) setDummyStorage(c *gc.C, env *azureEnviron) { closer, storage, _ := envtesting.CreateLocalTestStorage(c) env.storage = storage s.AddCleanup(func(c *gc.C) { closer.Close() }) } func (*environSuite) TestGetEndpoint(c *gc.C) { c.Check( getEndpoint("West US"), gc.Equals, "https://management.core.windows.net/") c.Check( getEndpoint("China East"), gc.Equals, "https://management.core.chinacloudapi.cn/") } func (*environSuite) TestGetSnapshot(c *gc.C) { original := azureEnviron{name: "this-env", ecfg: new(azureEnvironConfig)} snapshot := original.getSnapshot() // The snapshot is identical to the original. c.Check(*snapshot, gc.DeepEquals, original) // However, they are distinct objects. c.Check(snapshot, gc.Not(gc.Equals), &original) // It's a shallow copy; they still share pointers. c.Check(snapshot.ecfg, gc.Equals, original.ecfg) // Neither object is locked at the end of the copy. c.Check(original.Mutex, gc.Equals, sync.Mutex{}) c.Check(snapshot.Mutex, gc.Equals, sync.Mutex{}) } func (*environSuite) TestGetSnapshotLocksEnviron(c *gc.C) { original := azureEnviron{} testing.TestLockingFunction(&original.Mutex, func() { original.getSnapshot() }) } func (*environSuite) TestName(c *gc.C) { env := azureEnviron{name: "foo"} c.Check(env.Name(), gc.Equals, env.name) } func (*environSuite) TestConfigReturnsConfig(c *gc.C) { cfg := new(config.Config) ecfg := azureEnvironConfig{Config: cfg} env := azureEnviron{ecfg: &ecfg} c.Check(env.Config(), gc.Equals, cfg) } func (*environSuite) TestConfigLocksEnviron(c *gc.C) { env := azureEnviron{name: "env", ecfg: new(azureEnvironConfig)} testing.TestLockingFunction(&env.Mutex, func() { env.Config() }) } func (*environSuite) TestGetManagementAPI(c *gc.C) { env := makeEnviron(c) context, err := env.getManagementAPI() c.Assert(err, gc.IsNil) defer env.releaseManagementAPI(context) c.Check(context, gc.NotNil) c.Check(context.ManagementAPI, gc.NotNil) c.Check(context.certFile, gc.NotNil) c.Check(context.GetRetryPolicy(), gc.DeepEquals, retryPolicy) } func (*environSuite) TestReleaseManagementAPIAcceptsNil(c *gc.C) { env := makeEnviron(c) env.releaseManagementAPI(nil) // The real test is that this does not panic. } func (*environSuite) TestReleaseManagementAPIAcceptsIncompleteContext(c *gc.C) { env := makeEnviron(c) context := azureManagementContext{ ManagementAPI: nil, certFile: nil, } env.releaseManagementAPI(&context) // The real test is that this does not panic. } func getAzureServiceListResponse(c *gc.C, services []gwacl.HostedServiceDescriptor) []gwacl.DispatcherResponse { list := gwacl.HostedServiceDescriptorList{HostedServices: services} listXML, err := list.Serialize() c.Assert(err, gc.IsNil) responses := []gwacl.DispatcherResponse{gwacl.NewDispatcherResponse( []byte(listXML), http.StatusOK, nil, )} return responses } // getAzureServiceResponses returns the slice of responses // (gwacl.DispatcherResponse) which correspond to the API requests used to // get the properties of a Service. func getAzureServiceResponses(c *gc.C, service gwacl.HostedService) []gwacl.DispatcherResponse { serviceXML, err := service.Serialize() c.Assert(err, gc.IsNil) responses := []gwacl.DispatcherResponse{gwacl.NewDispatcherResponse( []byte(serviceXML), http.StatusOK, nil, )} return responses } func patchWithServiceListResponse(c *gc.C, services []gwacl.HostedServiceDescriptor) *[]*gwacl.X509Request { responses := getAzureServiceListResponse(c, services) return gwacl.PatchManagementAPIResponses(responses) } func (s *environSuite) TestSupportedArchitectures(c *gc.C) { env := s.setupEnvWithDummyMetadata(c) a, err := env.SupportedArchitectures() c.Assert(err, gc.IsNil) c.Assert(a, gc.DeepEquals, []string{"ppc64"}) } func (suite *environSuite) TestGetEnvPrefixContainsEnvName(c *gc.C) { env := makeEnviron(c) c.Check(strings.Contains(env.getEnvPrefix(), env.Name()), jc.IsTrue) } func (*environSuite) TestGetContainerName(c *gc.C) { env := makeEnviron(c) expected := env.getEnvPrefix() + "private" c.Check(env.getContainerName(), gc.Equals, expected) } func (suite *environSuite) TestAllInstances(c *gc.C) { env := makeEnviron(c) prefix := env.getEnvPrefix() services := []gwacl.HostedServiceDescriptor{{ServiceName: "deployment-in-another-env"}, {ServiceName: prefix + "deployment-1"}, {ServiceName: prefix + "deployment-2"}} requests := patchWithServiceListResponse(c, services) instances, err := env.AllInstances() c.Assert(err, gc.IsNil) c.Check(len(instances), gc.Equals, 2) c.Check(instances[0].Id(), gc.Equals, instance.Id(prefix+"deployment-1")) c.Check(instances[1].Id(), gc.Equals, instance.Id(prefix+"deployment-2")) c.Check(len(*requests), gc.Equals, 1) } func (suite *environSuite) TestInstancesReturnsFilteredList(c *gc.C) { services := []gwacl.HostedServiceDescriptor{{ServiceName: "deployment-1"}, {ServiceName: "deployment-2"}} requests := patchWithServiceListResponse(c, services) env := makeEnviron(c) instances, err := env.Instances([]instance.Id{"deployment-1"}) c.Assert(err, gc.IsNil) c.Check(len(instances), gc.Equals, 1) c.Check(instances[0].Id(), gc.Equals, instance.Id("deployment-1")) c.Check(len(*requests), gc.Equals, 1) } func (suite *environSuite) TestInstancesReturnsErrNoInstancesIfNoInstancesRequested(c *gc.C) { services := []gwacl.HostedServiceDescriptor{{ServiceName: "deployment-1"}, {ServiceName: "deployment-2"}} patchWithServiceListResponse(c, services) env := makeEnviron(c) instances, err := env.Instances([]instance.Id{}) c.Check(err, gc.Equals, environs.ErrNoInstances) c.Check(instances, gc.IsNil) } func (suite *environSuite) TestInstancesReturnsErrNoInstancesIfNoInstanceFound(c *gc.C) { services := []gwacl.HostedServiceDescriptor{} patchWithServiceListResponse(c, services) env := makeEnviron(c) instances, err := env.Instances([]instance.Id{"deploy-id"}) c.Check(err, gc.Equals, environs.ErrNoInstances) c.Check(instances, gc.IsNil) } func (suite *environSuite) TestInstancesReturnsPartialInstancesIfSomeInstancesAreNotFound(c *gc.C) { services := []gwacl.HostedServiceDescriptor{{ServiceName: "deployment-1"}, {ServiceName: "deployment-2"}} requests := patchWithServiceListResponse(c, services) env := makeEnviron(c) instances, err := env.Instances([]instance.Id{"deployment-1", "unknown-deployment"}) c.Assert(err, gc.Equals, environs.ErrPartialInstances) c.Check(len(instances), gc.Equals, 1) c.Check(instances[0].Id(), gc.Equals, instance.Id("deployment-1")) c.Check(len(*requests), gc.Equals, 1) } func (*environSuite) TestStorage(c *gc.C) { env := makeEnviron(c) baseStorage := env.Storage() storage, ok := baseStorage.(*azureStorage) c.Check(ok, gc.Equals, true) c.Assert(storage, gc.NotNil) c.Check(storage.storageContext.getContainer(), gc.Equals, env.getContainerName()) context, err := storage.getStorageContext() c.Assert(err, gc.IsNil) c.Check(context.Account, gc.Equals, env.ecfg.storageAccountName()) c.Check(context.RetryPolicy, gc.DeepEquals, retryPolicy) } func (*environSuite) TestQueryStorageAccountKeyGetsKey(c *gc.C) { env := makeEnviron(c) keysInAzure := gwacl.StorageAccountKeys{Primary: "a-key"} azureResponse, err := xml.Marshal(keysInAzure) c.Assert(err, gc.IsNil) requests := gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(azureResponse, http.StatusOK, nil), }) returnedKey, err := env.queryStorageAccountKey() c.Assert(err, gc.IsNil) c.Check(returnedKey, gc.Equals, keysInAzure.Primary) c.Assert(*requests, gc.HasLen, 1) c.Check((*requests)[0].Method, gc.Equals, "GET") } func (*environSuite) TestGetStorageContextCreatesStorageContext(c *gc.C) { env := makeEnviron(c) stor, err := env.getStorageContext() c.Assert(err, gc.IsNil) c.Assert(stor, gc.NotNil) c.Check(stor.Account, gc.Equals, env.ecfg.storageAccountName()) c.Check(stor.AzureEndpoint, gc.Equals, gwacl.GetEndpoint(env.ecfg.location())) } func (*environSuite) TestGetStorageContextUsesKnownStorageAccountKey(c *gc.C) { env := makeEnviron(c) env.storageAccountKey = "my-key" stor, err := env.getStorageContext() c.Assert(err, gc.IsNil) c.Check(stor.Key, gc.Equals, "my-key") } func (*environSuite) TestGetStorageContextQueriesStorageAccountKeyIfNeeded(c *gc.C) { env := makeEnviron(c) env.storageAccountKey = "" keysInAzure := gwacl.StorageAccountKeys{Primary: "my-key"} azureResponse, err := xml.Marshal(keysInAzure) c.Assert(err, gc.IsNil) gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(azureResponse, http.StatusOK, nil), }) stor, err := env.getStorageContext() c.Assert(err, gc.IsNil) c.Check(stor.Key, gc.Equals, keysInAzure.Primary) c.Check(env.storageAccountKey, gc.Equals, keysInAzure.Primary) } func (*environSuite) TestGetStorageContextFailsIfNoKeyAvailable(c *gc.C) { env := makeEnviron(c) env.storageAccountKey = "" azureResponse, err := xml.Marshal(gwacl.StorageAccountKeys{}) c.Assert(err, gc.IsNil) gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(azureResponse, http.StatusOK, nil), }) _, err = env.getStorageContext() c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, "no keys available for storage account") } func (*environSuite) TestUpdateStorageAccountKeyGetsFreshKey(c *gc.C) { env := makeEnviron(c) keysInAzure := gwacl.StorageAccountKeys{Primary: "my-key"} azureResponse, err := xml.Marshal(keysInAzure) c.Assert(err, gc.IsNil) gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(azureResponse, http.StatusOK, nil), }) key, err := env.updateStorageAccountKey(env.getSnapshot()) c.Assert(err, gc.IsNil) c.Check(key, gc.Equals, keysInAzure.Primary) c.Check(env.storageAccountKey, gc.Equals, keysInAzure.Primary) } func (*environSuite) TestUpdateStorageAccountKeyReturnsError(c *gc.C) { env := makeEnviron(c) env.storageAccountKey = "" gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(nil, http.StatusInternalServerError, nil), }) _, err := env.updateStorageAccountKey(env.getSnapshot()) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, "cannot obtain storage account keys: GET request failed.*Internal Server Error.*") c.Check(env.storageAccountKey, gc.Equals, "") } func (*environSuite) TestUpdateStorageAccountKeyDetectsConcurrentUpdate(c *gc.C) { env := makeEnviron(c) env.storageAccountKey = "" keysInAzure := gwacl.StorageAccountKeys{Primary: "my-key"} azureResponse, err := xml.Marshal(keysInAzure) c.Assert(err, gc.IsNil) gwacl.PatchManagementAPIResponses([]gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(azureResponse, http.StatusOK, nil), }) // Here we use a snapshot that's different from the environment, to // simulate a concurrent change to the environment. _, err = env.updateStorageAccountKey(makeEnviron(c)) c.Assert(err, gc.NotNil) // updateStorageAccountKey detects the change, and refuses to write its // outdated information into env. c.Check(err, gc.ErrorMatches, "environment was reconfigured") c.Check(env.storageAccountKey, gc.Equals, "") } func (*environSuite) TestSetConfigValidates(c *gc.C) { env := makeEnviron(c) originalCfg := env.ecfg attrs := makeAzureConfigMap(c) // This config is not valid. It lacks essential information. delete(attrs, "management-subscription-id") badCfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) err = env.SetConfig(badCfg) // Since the config was not valid, SetConfig returns an error. It // does not update the environment's config either. c.Check(err, gc.NotNil) c.Check( err, gc.ErrorMatches, "management-subscription-id: expected string, got nothing") c.Check(env.ecfg, gc.Equals, originalCfg) } func (*environSuite) TestSetConfigUpdatesConfig(c *gc.C) { env := makeEnviron(c) // We're going to set a new config. It can be recognized by its // unusual default Ubuntu release series: 7.04 Feisty Fawn. attrs := makeAzureConfigMap(c) attrs["default-series"] = "feisty" cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) err = env.SetConfig(cfg) c.Assert(err, gc.IsNil) c.Check(config.PreferredSeries(env.ecfg.Config), gc.Equals, "feisty") } func (*environSuite) TestSetConfigLocksEnviron(c *gc.C) { env := makeEnviron(c) cfg, err := config.New(config.NoDefaults, makeAzureConfigMap(c)) c.Assert(err, gc.IsNil) testing.TestLockingFunction(&env.Mutex, func() { env.SetConfig(cfg) }) } func (*environSuite) TestSetConfigWillNotUpdateName(c *gc.C) { // Once the environment's name has been set, it cannot be updated. // Global validation rejects such a change. // This matters because the attribute is not protected by a lock. env := makeEnviron(c) originalName := env.Name() attrs := makeAzureConfigMap(c) attrs["name"] = "new-name" cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) err = env.SetConfig(cfg) c.Assert(err, gc.NotNil) c.Check( err, gc.ErrorMatches, `cannot change name from ".*" to "new-name"`) c.Check(env.Name(), gc.Equals, originalName) } func (*environSuite) TestSetConfigClearsStorageAccountKey(c *gc.C) { env := makeEnviron(c) env.storageAccountKey = "key-for-previous-config" attrs := makeAzureConfigMap(c) attrs["default-series"] = "other" cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) err = env.SetConfig(cfg) c.Assert(err, gc.IsNil) c.Check(env.storageAccountKey, gc.Equals, "") } func (s *environSuite) TestStateInfoFailsIfNoStateInstances(c *gc.C) { env := makeEnviron(c) s.setDummyStorage(c, env) _, _, err := env.StateInfo() c.Check(err, gc.Equals, environs.ErrNotBootstrapped) } func (s *environSuite) TestStateInfo(c *gc.C) { instanceID := "my-instance" patchWithServiceListResponse(c, []gwacl.HostedServiceDescriptor{{ ServiceName: instanceID, }}) env := makeEnviron(c) s.setDummyStorage(c, env) err := bootstrap.SaveState( env.Storage(), &bootstrap.BootstrapState{StateInstances: []instance.Id{instance.Id(instanceID)}}) c.Assert(err, gc.IsNil) stateInfo, apiInfo, err := env.StateInfo() c.Assert(err, gc.IsNil) config := env.Config() dnsName := "my-instance." + AZURE_DOMAIN_NAME stateServerAddr := fmt.Sprintf("%s:%d", dnsName, config.StatePort()) apiServerAddr := fmt.Sprintf("%s:%d", dnsName, config.APIPort()) c.Check(stateInfo.Addrs, gc.DeepEquals, []string{stateServerAddr}) c.Check(apiInfo.Addrs, gc.DeepEquals, []string{apiServerAddr}) } // parseCreateServiceRequest reconstructs the original CreateHostedService // request object passed to gwacl's AddHostedService method, based on the // X509Request which the method issues. func parseCreateServiceRequest(c *gc.C, request *gwacl.X509Request) *gwacl.CreateHostedService { body := gwacl.CreateHostedService{} err := xml.Unmarshal(request.Payload, &body) c.Assert(err, gc.IsNil) return &body } // makeNonAvailabilityResponse simulates a reply to the // CheckHostedServiceNameAvailability call saying that a name is not available. func makeNonAvailabilityResponse(c *gc.C) []byte { errorBody, err := xml.Marshal(gwacl.AvailabilityResponse{ Result: "false", Reason: "he's a very naughty boy"}) c.Assert(err, gc.IsNil) return errorBody } // makeAvailabilityResponse simulates a reply to the // CheckHostedServiceNameAvailability call saying that a name is available. func makeAvailabilityResponse(c *gc.C) []byte { errorBody, err := xml.Marshal(gwacl.AvailabilityResponse{ Result: "true"}) c.Assert(err, gc.IsNil) return errorBody } func (*environSuite) TestAttemptCreateServiceCreatesService(c *gc.C) { prefix := "myservice" affinityGroup := "affinity-group" location := "location" responses := []gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(makeAvailabilityResponse(c), http.StatusOK, nil), gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), } requests := gwacl.PatchManagementAPIResponses(responses) azure, err := gwacl.NewManagementAPI("subscription", "", "West US") c.Assert(err, gc.IsNil) service, err := attemptCreateService(azure, prefix, affinityGroup, location) c.Assert(err, gc.IsNil) c.Assert(*requests, gc.HasLen, 2) body := parseCreateServiceRequest(c, (*requests)[1]) c.Check(body.ServiceName, gc.Equals, service.ServiceName) c.Check(body.AffinityGroup, gc.Equals, affinityGroup) c.Check(service.ServiceName, gc.Matches, prefix+".*") c.Check(service.Location, gc.Equals, location) label, err := base64.StdEncoding.DecodeString(service.Label) c.Assert(err, gc.IsNil) c.Check(string(label), gc.Equals, service.ServiceName) } func (*environSuite) TestAttemptCreateServiceReturnsNilIfNameNotUnique(c *gc.C) { responses := []gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(makeNonAvailabilityResponse(c), http.StatusOK, nil), } gwacl.PatchManagementAPIResponses(responses) azure, err := gwacl.NewManagementAPI("subscription", "", "West US") c.Assert(err, gc.IsNil) service, err := attemptCreateService(azure, "service", "affinity-group", "location") c.Check(err, gc.IsNil) c.Check(service, gc.IsNil) } func (*environSuite) TestAttemptCreateServicePropagatesOtherFailure(c *gc.C) { responses := []gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(makeAvailabilityResponse(c), http.StatusOK, nil), gwacl.NewDispatcherResponse(nil, http.StatusNotFound, nil), } gwacl.PatchManagementAPIResponses(responses) azure, err := gwacl.NewManagementAPI("subscription", "", "West US") c.Assert(err, gc.IsNil) _, err = attemptCreateService(azure, "service", "affinity-group", "location") c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, ".*Not Found.*") } func (*environSuite) TestNewHostedServiceCreatesService(c *gc.C) { prefix := "myservice" affinityGroup := "affinity-group" location := "location" responses := []gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(makeAvailabilityResponse(c), http.StatusOK, nil), gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), } requests := gwacl.PatchManagementAPIResponses(responses) azure, err := gwacl.NewManagementAPI("subscription", "", "West US") c.Assert(err, gc.IsNil) service, err := newHostedService(azure, prefix, affinityGroup, location) c.Assert(err, gc.IsNil) c.Assert(*requests, gc.HasLen, 2) body := parseCreateServiceRequest(c, (*requests)[1]) c.Check(body.ServiceName, gc.Equals, service.ServiceName) c.Check(body.AffinityGroup, gc.Equals, affinityGroup) c.Check(service.ServiceName, gc.Matches, prefix+".*") c.Check(service.Location, gc.Equals, location) } func (*environSuite) TestNewHostedServiceRetriesIfNotUnique(c *gc.C) { errorBody := makeNonAvailabilityResponse(c) okBody := makeAvailabilityResponse(c) // In this scenario, the first two names that we try are already // taken. The third one is unique though, so we succeed. responses := []gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(errorBody, http.StatusOK, nil), gwacl.NewDispatcherResponse(errorBody, http.StatusOK, nil), gwacl.NewDispatcherResponse(okBody, http.StatusOK, nil), gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), } requests := gwacl.PatchManagementAPIResponses(responses) azure, err := gwacl.NewManagementAPI("subscription", "", "West US") c.Assert(err, gc.IsNil) service, err := newHostedService(azure, "service", "affinity-group", "location") c.Check(err, gc.IsNil) c.Assert(*requests, gc.HasLen, 4) // How many names have been attempted, and how often? // There is a minute chance that this tries the same name twice, and // then this test will fail. If that happens, try seeding the // randomizer with some fixed seed that doens't produce the problem. attemptedNames := make(map[string]int) for _, request := range *requests { // Exit the loop if we hit the request to create the service, it comes // after the check calls. if request.Method == "POST" { break } // Name is the last part of the URL from the GET requests that check // availability. _, name := path.Split(strings.TrimRight(request.URL, "/")) attemptedNames[name] += 1 } // The three attempts we just made all had different service names. c.Check(attemptedNames, gc.HasLen, 3) // Once newHostedService succeeds, we get a hosted service with the // last requested name. c.Check( service.ServiceName, gc.Equals, parseCreateServiceRequest(c, (*requests)[3]).ServiceName) } func (*environSuite) TestNewHostedServiceFailsIfUnableToFindUniqueName(c *gc.C) { errorBody := makeNonAvailabilityResponse(c) responses := []gwacl.DispatcherResponse{} for counter := 0; counter < 100; counter++ { responses = append(responses, gwacl.NewDispatcherResponse(errorBody, http.StatusOK, nil)) } gwacl.PatchManagementAPIResponses(responses) azure, err := gwacl.NewManagementAPI("subscription", "", "West US") c.Assert(err, gc.IsNil) _, err = newHostedService(azure, "service", "affinity-group", "location") c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, "could not come up with a unique hosted service name.*") } // buildDestroyAzureServiceResponses returns a slice containing the responses that a fake Azure server // can use to simulate the deletion of the given list of services. func buildDestroyAzureServiceResponses(c *gc.C, services []*gwacl.HostedService) []gwacl.DispatcherResponse { responses := []gwacl.DispatcherResponse{} for _, service := range services { // When destroying a hosted service, gwacl first issues a Get request // to fetch the properties of the services. Then it destroys all the // deployments found in this service (none in this case, we make sure // the service does not contain deployments to keep the testing simple) // And it finally deletes the service itself. if len(service.Deployments) != 0 { panic("buildDestroyAzureServiceResponses does not support services with deployments!") } serviceXML, err := service.Serialize() c.Assert(err, gc.IsNil) serviceGetResponse := gwacl.NewDispatcherResponse( []byte(serviceXML), http.StatusOK, nil, ) responses = append(responses, serviceGetResponse) serviceDeleteResponse := gwacl.NewDispatcherResponse( nil, http.StatusOK, nil, ) responses = append(responses, serviceDeleteResponse) } return responses } func makeAzureService(name string) (*gwacl.HostedService, *gwacl.HostedServiceDescriptor) { service1Desc := &gwacl.HostedServiceDescriptor{ServiceName: name} service1 := &gwacl.HostedService{HostedServiceDescriptor: *service1Desc} return service1, service1Desc } func (s *environSuite) setServiceDeletionConcurrency(nbGoroutines int) { s.PatchValue(&maxConcurrentDeletes, nbGoroutines) } func (s *environSuite) TestStopInstancesDestroysMachines(c *gc.C) { s.setServiceDeletionConcurrency(3) service1Name := "service1" service1, service1Desc := makeAzureService(service1Name) service2Name := "service2" service2, service2Desc := makeAzureService(service2Name) services := []*gwacl.HostedService{service1, service2} responses := buildDestroyAzureServiceResponses(c, services) requests := gwacl.PatchManagementAPIResponses(responses) env := makeEnviron(c) instances := convertToInstances( []gwacl.HostedServiceDescriptor{*service1Desc, *service2Desc}, env) err := env.StopInstances(instances) c.Check(err, gc.IsNil) // It takes 2 API calls to delete each service: // - one GET request to fetch the service's properties; // - one DELETE request to delete the service. c.Check(len(*requests), gc.Equals, len(services)*2) assertOneRequestMatches(c, *requests, "GET", ".*"+service1Name+".*") assertOneRequestMatches(c, *requests, "DELETE", ".*"+service1Name+".*") assertOneRequestMatches(c, *requests, "GET", ".*"+service2Name+".") assertOneRequestMatches(c, *requests, "DELETE", ".*"+service2Name+".*") } func (s *environSuite) TestStopInstancesWhenStoppingMachinesFails(c *gc.C) { s.setServiceDeletionConcurrency(3) responses := []gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(nil, http.StatusConflict, nil), } service1Name := "service1" _, service1Desc := makeAzureService(service1Name) service2Name := "service2" service2, service2Desc := makeAzureService(service2Name) services := []*gwacl.HostedService{service2} destroyResponses := buildDestroyAzureServiceResponses(c, services) responses = append(responses, destroyResponses...) requests := gwacl.PatchManagementAPIResponses(responses) env := makeEnviron(c) instances := convertToInstances( []gwacl.HostedServiceDescriptor{*service1Desc, *service2Desc}, env) err := env.StopInstances(instances) c.Check(err, gc.ErrorMatches, ".*Conflict.*") c.Check(len(*requests), gc.Equals, 3) assertOneRequestMatches(c, *requests, "GET", ".*"+service1Name+".") assertOneRequestMatches(c, *requests, "GET", ".*"+service2Name+".") // Only one of the services was deleted. assertOneRequestMatches(c, *requests, "DELETE", ".*") } func (s *environSuite) TestStopInstancesWithLimitedConcurrency(c *gc.C) { s.setServiceDeletionConcurrency(3) services := []*gwacl.HostedService{} serviceDescs := []gwacl.HostedServiceDescriptor{} for i := 0; i < 10; i++ { serviceName := fmt.Sprintf("service%d", i) service, serviceDesc := makeAzureService(serviceName) services = append(services, service) serviceDescs = append(serviceDescs, *serviceDesc) } responses := buildDestroyAzureServiceResponses(c, services) requests := gwacl.PatchManagementAPIResponses(responses) env := makeEnviron(c) instances := convertToInstances(serviceDescs, env) err := env.StopInstances(instances) c.Check(err, gc.IsNil) c.Check(len(*requests), gc.Equals, len(services)*2) } func (s *environSuite) TestStopInstancesWithZeroInstance(c *gc.C) { s.setServiceDeletionConcurrency(3) env := makeEnviron(c) instances := []instance.Instance{} err := env.StopInstances(instances) c.Check(err, gc.IsNil) } // getVnetAndAffinityGroupCleanupResponses returns the responses // (gwacl.DispatcherResponse) that a fake http server should return // when gwacl's RemoveVirtualNetworkSite() and DeleteAffinityGroup() // are called. func getVnetAndAffinityGroupCleanupResponses(c *gc.C) []gwacl.DispatcherResponse { existingConfig := &gwacl.NetworkConfiguration{ XMLNS: gwacl.XMLNS_NC, VirtualNetworkSites: nil, } body, err := existingConfig.Serialize() c.Assert(err, gc.IsNil) cleanupResponses := []gwacl.DispatcherResponse{ // Return empty net configuration. gwacl.NewDispatcherResponse([]byte(body), http.StatusOK, nil), // Accept deletion of affinity group. gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), } return cleanupResponses } func (s *environSuite) TestDestroyDoesNotCleanStorageIfError(c *gc.C) { env := makeEnviron(c) s.setDummyStorage(c, env) // Populate storage. err := bootstrap.SaveState( env.Storage(), &bootstrap.BootstrapState{StateInstances: []instance.Id{instance.Id("test-id")}}) c.Assert(err, gc.IsNil) responses := []gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(nil, http.StatusBadRequest, nil), } gwacl.PatchManagementAPIResponses(responses) err = env.Destroy() c.Check(err, gc.NotNil) files, err := storage.List(env.Storage(), "") c.Assert(err, gc.IsNil) c.Check(files, gc.HasLen, 1) } func (s *environSuite) TestDestroyCleansUpStorage(c *gc.C) { env := makeEnviron(c) s.setDummyStorage(c, env) // Populate storage. err := bootstrap.SaveState( env.Storage(), &bootstrap.BootstrapState{StateInstances: []instance.Id{instance.Id("test-id")}}) c.Assert(err, gc.IsNil) services := []gwacl.HostedServiceDescriptor{} responses := getAzureServiceListResponse(c, services) cleanupResponses := getVnetAndAffinityGroupCleanupResponses(c) responses = append(responses, cleanupResponses...) gwacl.PatchManagementAPIResponses(responses) err = env.Destroy() c.Check(err, gc.IsNil) files, err := storage.List(env.Storage(), "") c.Assert(err, gc.IsNil) c.Check(files, gc.HasLen, 0) } func (s *environSuite) TestDestroyDeletesVirtualNetworkAndAffinityGroup(c *gc.C) { env := makeEnviron(c) s.setDummyStorage(c, env) services := []gwacl.HostedServiceDescriptor{} responses := getAzureServiceListResponse(c, services) // Prepare a configuration with a single virtual network. existingConfig := &gwacl.NetworkConfiguration{ XMLNS: gwacl.XMLNS_NC, VirtualNetworkSites: &[]gwacl.VirtualNetworkSite{ {Name: env.getVirtualNetworkName()}, }, } body, err := existingConfig.Serialize() c.Assert(err, gc.IsNil) cleanupResponses := []gwacl.DispatcherResponse{ // Return existing configuration. gwacl.NewDispatcherResponse([]byte(body), http.StatusOK, nil), // Accept upload of new configuration. gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), // Accept deletion of affinity group. gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), } responses = append(responses, cleanupResponses...) requests := gwacl.PatchManagementAPIResponses(responses) err = env.Destroy() c.Check(err, gc.IsNil) c.Assert(*requests, gc.HasLen, 4) // One request to get the network configuration. getRequest := (*requests)[1] c.Check(getRequest.Method, gc.Equals, "GET") c.Check(strings.HasSuffix(getRequest.URL, "services/networking/media"), gc.Equals, true) // One request to upload the new version of the network configuration. putRequest := (*requests)[2] c.Check(putRequest.Method, gc.Equals, "PUT") c.Check(strings.HasSuffix(putRequest.URL, "services/networking/media"), gc.Equals, true) // One request to delete the Affinity Group. agRequest := (*requests)[3] c.Check(strings.Contains(agRequest.URL, env.getAffinityGroupName()), jc.IsTrue) c.Check(agRequest.Method, gc.Equals, "DELETE") } var emptyListResponse = ` <?xml version="1.0" encoding="utf-8"?> <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer"> <Prefix>prefix</Prefix> <Marker>marker</Marker> <MaxResults>maxresults</MaxResults> <Delimiter>delimiter</Delimiter> <Blobs></Blobs> <NextMarker /> </EnumerationResults>` // assertOneRequestMatches asserts that at least one request in the given slice // contains a request with the given method and whose URL matches the given regexp. func assertOneRequestMatches(c *gc.C, requests []*gwacl.X509Request, method string, urlPattern string) { for _, request := range requests { matched, err := regexp.MatchString(urlPattern, request.URL) if err == nil && request.Method == method && matched { return } } c.Error(fmt.Sprintf("none of the requests matches: Method=%v, URL pattern=%v", method, urlPattern)) } func (s *environSuite) TestDestroyStopsAllInstances(c *gc.C) { s.setServiceDeletionConcurrency(3) env := makeEnviron(c) s.setDummyStorage(c, env) // Simulate 2 instances corresponding to two Azure services. prefix := env.getEnvPrefix() service1Name := prefix + "service1" service1, service1Desc := makeAzureService(service1Name) services := []*gwacl.HostedService{service1} // The call to AllInstances() will return only one service (service1). listInstancesResponses := getAzureServiceListResponse(c, []gwacl.HostedServiceDescriptor{*service1Desc}) destroyResponses := buildDestroyAzureServiceResponses(c, services) responses := append(listInstancesResponses, destroyResponses...) cleanupResponses := getVnetAndAffinityGroupCleanupResponses(c) responses = append(responses, cleanupResponses...) requests := gwacl.PatchManagementAPIResponses(responses) err := env.Destroy() c.Check(err, gc.IsNil) // One request to get the list of all the environment's instances. // Then two requests per destroyed machine (one to fetch the // service's information, one to delete it) and two requests to delete // the Virtual Network and the Affinity Group. c.Check((*requests), gc.HasLen, 1+len(services)*2+2) c.Check((*requests)[0].Method, gc.Equals, "GET") assertOneRequestMatches(c, *requests, "GET", ".*"+service1Name+".*") assertOneRequestMatches(c, *requests, "DELETE", ".*"+service1Name+".*") } func (*environSuite) TestGetInstance(c *gc.C) { env := makeEnviron(c) prefix := env.getEnvPrefix() serviceName := prefix + "instance-name" serviceDesc := gwacl.HostedServiceDescriptor{ServiceName: serviceName} service := gwacl.HostedService{HostedServiceDescriptor: serviceDesc} responses := getAzureServiceResponses(c, service) gwacl.PatchManagementAPIResponses(responses) instance, err := env.getInstance("serviceName") c.Check(err, gc.IsNil) c.Check(string(instance.Id()), gc.Equals, serviceName) c.Check(instance, gc.FitsTypeOf, &azureInstance{}) azInstance := instance.(*azureInstance) c.Check(azInstance.environ, gc.Equals, env) } func (*environSuite) TestNewOSVirtualDisk(c *gc.C) { env := makeEnviron(c) sourceImageName := "source-image-name" vhd := env.newOSDisk(sourceImageName) mediaLinkUrl, err := url.Parse(vhd.MediaLink) c.Check(err, gc.IsNil) storageAccount := env.ecfg.storageAccountName() c.Check(mediaLinkUrl.Host, gc.Equals, fmt.Sprintf("%s.blob.core.windows.net", storageAccount)) c.Check(vhd.SourceImageName, gc.Equals, sourceImageName) } // mapInputEndpointsByPort takes a slice of input endpoints, and returns them // as a map keyed by their (external) ports. This makes it easier to query // individual endpoints from an array whose ordering you don't know. // Multiple input endpoints for the same port are treated as an error. func mapInputEndpointsByPort(c *gc.C, endpoints []gwacl.InputEndpoint) map[int]gwacl.InputEndpoint { mapping := make(map[int]gwacl.InputEndpoint) for _, endpoint := range endpoints { _, have := mapping[endpoint.Port] c.Assert(have, gc.Equals, false) mapping[endpoint.Port] = endpoint } return mapping } func (*environSuite) TestNewRole(c *gc.C) { env := makeEnviron(c) size := "Large" vhd := env.newOSDisk("source-image-name") userData := "example-user-data" hostname := "hostname" role := env.newRole(size, vhd, userData, hostname) configs := role.ConfigurationSets linuxConfig := configs[0] networkConfig := configs[1] c.Check(linuxConfig.CustomData, gc.Equals, userData) c.Check(linuxConfig.Hostname, gc.Equals, hostname) c.Check(linuxConfig.Username, gc.Not(gc.Equals), "") c.Check(linuxConfig.Password, gc.Not(gc.Equals), "") c.Check(linuxConfig.DisableSSHPasswordAuthentication, gc.Equals, "true") c.Check(role.RoleSize, gc.Equals, size) c.Check(role.OSVirtualHardDisk[0], gc.Equals, *vhd) endpoints := mapInputEndpointsByPort(c, *networkConfig.InputEndpoints) // The network config contains an endpoint for ssh communication. sshEndpoint, ok := endpoints[22] c.Assert(ok, gc.Equals, true) c.Check(sshEndpoint.LocalPort, gc.Equals, 22) c.Check(sshEndpoint.Protocol, gc.Equals, "tcp") // There's also an endpoint for the state (mongodb) port. // TODO: Ought to have this only for state servers. stateEndpoint, ok := endpoints[env.Config().StatePort()] c.Assert(ok, gc.Equals, true) c.Check(stateEndpoint.LocalPort, gc.Equals, env.Config().StatePort()) c.Check(stateEndpoint.Protocol, gc.Equals, "tcp") // And one for the API port. // TODO: Ought to have this only for API servers. apiEndpoint, ok := endpoints[env.Config().APIPort()] c.Assert(ok, gc.Equals, true) c.Check(apiEndpoint.LocalPort, gc.Equals, env.Config().APIPort()) c.Check(apiEndpoint.Protocol, gc.Equals, "tcp") } func (*environSuite) TestNewDeployment(c *gc.C) { env := makeEnviron(c) deploymentName := "deployment-name" deploymentLabel := "deployment-label" virtualNetworkName := "virtual-network-name" vhd := env.newOSDisk("source-image-name") role := env.newRole("Small", vhd, "user-data", "hostname") deployment := env.newDeployment(role, deploymentName, deploymentLabel, virtualNetworkName) base64Label := base64.StdEncoding.EncodeToString([]byte(deploymentLabel)) c.Check(deployment.Label, gc.Equals, base64Label) c.Check(deployment.Name, gc.Equals, deploymentName) c.Check(deployment.RoleList, gc.HasLen, 1) } func (*environSuite) TestProviderReturnsAzureEnvironProvider(c *gc.C) { prov := makeEnviron(c).Provider() c.Assert(prov, gc.NotNil) azprov, ok := prov.(azureEnvironProvider) c.Assert(ok, gc.Equals, true) c.Check(azprov, gc.NotNil) } func (*environSuite) TestCreateVirtualNetwork(c *gc.C) { env := makeEnviron(c) responses := []gwacl.DispatcherResponse{ // No existing configuration found. gwacl.NewDispatcherResponse(nil, http.StatusNotFound, nil), // Accept upload of new configuration. gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), } requests := gwacl.PatchManagementAPIResponses(responses) env.createVirtualNetwork() c.Assert(*requests, gc.HasLen, 2) request := (*requests)[1] body := gwacl.NetworkConfiguration{} err := xml.Unmarshal(request.Payload, &body) c.Assert(err, gc.IsNil) networkConf := (*body.VirtualNetworkSites)[0] c.Check(networkConf.Name, gc.Equals, env.getVirtualNetworkName()) c.Check(networkConf.AffinityGroup, gc.Equals, env.getAffinityGroupName()) } func (*environSuite) TestDestroyVirtualNetwork(c *gc.C) { env := makeEnviron(c) // Prepare a configuration with a single virtual network. existingConfig := &gwacl.NetworkConfiguration{ XMLNS: gwacl.XMLNS_NC, VirtualNetworkSites: &[]gwacl.VirtualNetworkSite{ {Name: env.getVirtualNetworkName()}, }, } body, err := existingConfig.Serialize() c.Assert(err, gc.IsNil) responses := []gwacl.DispatcherResponse{ // Return existing configuration. gwacl.NewDispatcherResponse([]byte(body), http.StatusOK, nil), // Accept upload of new configuration. gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), } requests := gwacl.PatchManagementAPIResponses(responses) env.deleteVirtualNetwork() c.Assert(*requests, gc.HasLen, 2) // One request to get the existing network configuration. getRequest := (*requests)[0] c.Check(getRequest.Method, gc.Equals, "GET") // One request to update the network configuration. putRequest := (*requests)[1] c.Check(putRequest.Method, gc.Equals, "PUT") newConfig := gwacl.NetworkConfiguration{} err = xml.Unmarshal(putRequest.Payload, &newConfig) c.Assert(err, gc.IsNil) // The new configuration has no VirtualNetworkSites. c.Check(newConfig.VirtualNetworkSites, gc.IsNil) } func (*environSuite) TestGetVirtualNetworkNameContainsEnvName(c *gc.C) { env := makeEnviron(c) c.Check(strings.Contains(env.getVirtualNetworkName(), env.Name()), jc.IsTrue) } func (*environSuite) TestGetVirtualNetworkNameIsConstant(c *gc.C) { env := makeEnviron(c) c.Check(env.getVirtualNetworkName(), gc.Equals, env.getVirtualNetworkName()) } func (*environSuite) TestCreateAffinityGroup(c *gc.C) { env := makeEnviron(c) responses := []gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(nil, http.StatusCreated, nil), } requests := gwacl.PatchManagementAPIResponses(responses) env.createAffinityGroup() c.Assert(*requests, gc.HasLen, 1) request := (*requests)[0] body := gwacl.CreateAffinityGroup{} err := xml.Unmarshal(request.Payload, &body) c.Assert(err, gc.IsNil) c.Check(body.Name, gc.Equals, env.getAffinityGroupName()) // This is a testing antipattern, the expected data comes from // config defaults. Fix it sometime. c.Check(body.Location, gc.Equals, "location") } func (*environSuite) TestDestroyAffinityGroup(c *gc.C) { env := makeEnviron(c) responses := []gwacl.DispatcherResponse{ gwacl.NewDispatcherResponse(nil, http.StatusOK, nil), } requests := gwacl.PatchManagementAPIResponses(responses) env.deleteAffinityGroup() c.Assert(*requests, gc.HasLen, 1) request := (*requests)[0] c.Check(strings.Contains(request.URL, env.getAffinityGroupName()), jc.IsTrue) c.Check(request.Method, gc.Equals, "DELETE") } func (*environSuite) TestGetAffinityGroupName(c *gc.C) { env := makeEnviron(c) c.Check(strings.Contains(env.getAffinityGroupName(), env.Name()), jc.IsTrue) } func (*environSuite) TestGetAffinityGroupNameIsConstant(c *gc.C) { env := makeEnviron(c) c.Check(env.getAffinityGroupName(), gc.Equals, env.getAffinityGroupName()) } func (*environSuite) TestGetImageMetadataSigningRequiredDefaultsToTrue(c *gc.C) { env := makeEnviron(c) // Hard-coded to true for now. Once we support other base URLs, this // may have to become configurable. c.Check(env.getImageMetadataSigningRequired(), gc.Equals, true) } func (*environSuite) TestSelectInstanceTypeAndImageUsesForcedImage(c *gc.C) { env := makeEnviron(c) forcedImage := "my-image" env.ecfg.attrs["force-image-name"] = forcedImage aim := gwacl.RoleNameMap["ExtraLarge"] cons := constraints.Value{ CpuCores: &aim.CpuCores, Mem: &aim.Mem, } instanceType, image, err := env.selectInstanceTypeAndImage(&instances.InstanceConstraint{ Region: "West US", Series: "precise", Constraints: cons, }) c.Assert(err, gc.IsNil) c.Check(instanceType, gc.Equals, aim.Name) c.Check(image, gc.Equals, forcedImage) } func (s *environSuite) setupEnvWithDummyMetadata(c *gc.C) *azureEnviron { envAttrs := makeAzureConfigMap(c) envAttrs["location"] = "North Europe" env := makeEnvironWithConfig(c, envAttrs) s.setDummyStorage(c, env) s.PatchValue(&imagemetadata.DefaultBaseURL, "") s.PatchValue(&signedImageDataOnly, false) images := []*imagemetadata.ImageMetadata{ { Id: "image-id", VirtType: "Hyper-V", Arch: "ppc64", RegionName: "North Europe", Endpoint: "https://management.core.windows.net/", }, } makeTestMetadata(c, env, "precise", "North Europe", images) return env } func (s *environSuite) TestSelectInstanceTypeAndImageUsesSimplestreamsByDefault(c *gc.C) { env := s.setupEnvWithDummyMetadata(c) // We'll tailor our constraints so as to get a specific instance type. aim := gwacl.RoleNameMap["ExtraSmall"] cons := constraints.Value{ CpuCores: &aim.CpuCores, Mem: &aim.Mem, } instanceType, image, err := env.selectInstanceTypeAndImage(&instances.InstanceConstraint{ Region: "North Europe", Series: "precise", Constraints: cons, }) c.Assert(err, gc.IsNil) c.Assert(instanceType, gc.Equals, aim.Name) c.Assert(image, gc.Equals, "image-id") } func (*environSuite) TestConvertToInstances(c *gc.C) { services := []gwacl.HostedServiceDescriptor{ {ServiceName: "foo"}, {ServiceName: "bar"}, } env := makeEnviron(c) instances := convertToInstances(services, env) c.Check(instances, gc.DeepEquals, []instance.Instance{ &azureInstance{services[0], env}, &azureInstance{services[1], env}, }) } func (*environSuite) TestExtractStorageKeyPicksPrimaryKeyIfSet(c *gc.C) { keys := gwacl.StorageAccountKeys{ Primary: "mainkey", Secondary: "otherkey", } c.Check(extractStorageKey(&keys), gc.Equals, "mainkey") } func (*environSuite) TestExtractStorageKeyFallsBackToSecondaryKey(c *gc.C) { keys := gwacl.StorageAccountKeys{ Secondary: "sparekey", } c.Check(extractStorageKey(&keys), gc.Equals, "sparekey") } func (*environSuite) TestExtractStorageKeyReturnsBlankIfNoneSet(c *gc.C) { c.Check(extractStorageKey(&gwacl.StorageAccountKeys{}), gc.Equals, "") } func assertSourceContents(c *gc.C, source simplestreams.DataSource, filename string, content []byte) { rc, _, err := source.Fetch(filename) c.Assert(err, gc.IsNil) defer rc.Close() retrieved, err := ioutil.ReadAll(rc) c.Assert(err, gc.IsNil) c.Assert(retrieved, gc.DeepEquals, content) } func (s *environSuite) assertGetImageMetadataSources(c *gc.C, stream, officialSourcePath string) { envAttrs := makeAzureConfigMap(c) if stream != "" { envAttrs["image-stream"] = stream } env := makeEnvironWithConfig(c, envAttrs) s.setDummyStorage(c, env) data := []byte{1, 2, 3, 4} env.Storage().Put("images/filename", bytes.NewReader(data), int64(len(data))) sources, err := imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) c.Assert(len(sources), gc.Equals, 2) assertSourceContents(c, sources[0], "filename", data) url, err := sources[1].URL("") c.Assert(err, gc.IsNil) c.Assert(url, gc.Equals, fmt.Sprintf("http://cloud-images.ubuntu.com/%s/", officialSourcePath)) } func (s *environSuite) TestGetImageMetadataSources(c *gc.C) { s.assertGetImageMetadataSources(c, "", "releases") s.assertGetImageMetadataSources(c, "released", "releases") s.assertGetImageMetadataSources(c, "daily", "daily") } func (s *environSuite) TestGetToolsMetadataSources(c *gc.C) { env := makeEnviron(c) s.setDummyStorage(c, env) data := []byte{1, 2, 3, 4} env.Storage().Put("tools/filename", bytes.NewReader(data), int64(len(data))) sources, err := tools.GetMetadataSources(env) c.Assert(err, gc.IsNil) c.Assert(len(sources), gc.Equals, 1) assertSourceContents(c, sources[0], "filename", data) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023576� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/export_test.go��������������������������0000644�0000153�0000161�00000003124�12321735642�026520� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local import ( "github.com/juju/testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" ) var ( CheckIfRoot = &checkIfRoot CheckLocalPort = &checkLocalPort DetectAptProxies = &detectAptProxies FinishBootstrap = &finishBootstrap Provider = providerInstance ReleaseVersion = &releaseVersion UseFastLXC = useFastLXC UserCurrent = &userCurrent ) // ConfigNamespace returns the result of the namespace call on the // localConfig. func ConfigNamespace(cfg *config.Config) string { env, _ := providerInstance.Open(cfg) return env.(*localEnviron).config.namespace() } // CreateDirs calls createDirs on the localEnviron. func CreateDirs(c *gc.C, cfg *config.Config) error { env, err := providerInstance.Open(cfg) c.Assert(err, gc.IsNil) return env.(*localEnviron).config.createDirs() } // CheckDirs returns the list of directories to check for permissions in the test. func CheckDirs(c *gc.C, cfg *config.Config) []string { localConfig, err := providerInstance.newConfig(cfg) c.Assert(err, gc.IsNil) return []string{ localConfig.rootDir(), localConfig.storageDir(), localConfig.mongoDir(), } } // MockAddressForInterface replaces the getAddressForInterface with a function // that returns a constant localhost ip address. func MockAddressForInterface() func() { return testing.PatchValue(&getAddressForInterface, func(name string) (string, error) { logger.Debugf("getAddressForInterface called for %s", name) return "127.0.0.1", nil }) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/config_test.go��������������������������0000644�0000153�0000161�00000006572�12321735642�026456� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local_test import ( "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/provider" "launchpad.net/juju-core/provider/local" "launchpad.net/juju-core/testing" ) type configSuite struct { baseProviderSuite } var _ = gc.Suite(&configSuite{}) func minimalConfigValues() map[string]interface{} { return testing.FakeConfig().Merge(testing.Attrs{ "name": "test", "type": provider.Local, }) } func minimalConfig(c *gc.C) *config.Config { minimal := minimalConfigValues() testConfig, err := config.New(config.NoDefaults, minimal) c.Assert(err, gc.IsNil) valid, err := local.Provider.Validate(testConfig, nil) c.Assert(err, gc.IsNil) return valid } func localConfig(c *gc.C, extra map[string]interface{}) *config.Config { values := minimalConfigValues() for key, value := range extra { values[key] = value } testConfig, err := config.New(config.NoDefaults, values) c.Assert(err, gc.IsNil) valid, err := local.Provider.Validate(testConfig, nil) c.Assert(err, gc.IsNil) return valid } func (s *configSuite) TestDefaultNetworkBridge(c *gc.C) { config := minimalConfig(c) unknownAttrs := config.UnknownAttrs() c.Assert(unknownAttrs["network-bridge"], gc.Equals, "lxcbr0") } func (s *configSuite) TestSetNetworkBridge(c *gc.C) { config := localConfig(c, map[string]interface{}{ "network-bridge": "br0", }) unknownAttrs := config.UnknownAttrs() c.Assert(unknownAttrs["network-bridge"], gc.Equals, "br0") } func (s *configSuite) TestValidateConfig(c *gc.C) { valid := minimalConfig(c) expectedRootDir := filepath.Join(osenv.Home(), ".juju", "test") unknownAttrs := valid.UnknownAttrs() c.Assert(unknownAttrs["root-dir"], gc.Equals, expectedRootDir) } func (s *configSuite) TestValidateConfigWithRootDir(c *gc.C) { root := c.MkDir() valid := localConfig(c, map[string]interface{}{ "root-dir": root, }) unknownAttrs := valid.UnknownAttrs() c.Assert(unknownAttrs["root-dir"], gc.Equals, root) } func (s *configSuite) TestValidateConfigWithTildeInRootDir(c *gc.C) { valid := localConfig(c, map[string]interface{}{ "root-dir": "~/.juju/foo", }) expectedRootDir := filepath.Join(osenv.Home(), ".juju", "foo") unknownAttrs := valid.UnknownAttrs() c.Assert(unknownAttrs["root-dir"], gc.Equals, expectedRootDir) } func (s *configSuite) TestValidateConfigWithFloatPort(c *gc.C) { // When the config values get serialized through JSON, the integers // get coerced to float64 values. The parsing needs to handle this. valid := localConfig(c, map[string]interface{}{ "storage-port": float64(8040), }) unknownAttrs := valid.UnknownAttrs() c.Assert(unknownAttrs["storage-port"], gc.Equals, int(8040)) } func (s *configSuite) TestNamespace(c *gc.C) { testConfig := minimalConfig(c) s.PatchEnvironment("USER", "tester") c.Assert(local.ConfigNamespace(testConfig), gc.Equals, "tester-test") } func (s *configSuite) TestBootstrapAsRoot(c *gc.C) { s.PatchValue(local.CheckIfRoot, func() bool { return true }) env, err := local.Provider.Prepare(testing.Context(c), minimalConfig(c)) c.Assert(err, gc.IsNil) err = env.Bootstrap(testing.Context(c), constraints.Value{}) c.Assert(err, gc.ErrorMatches, "bootstrapping a local environment must not be done as root") } ��������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/config.go�������������������������������0000644�0000153�0000161�00000006477�12321735642�025423� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local import ( "fmt" "os" "path/filepath" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/schema" ) var checkIfRoot = func() bool { return os.Getuid() == 0 } var ( configFields = schema.Fields{ "root-dir": schema.String(), "bootstrap-ip": schema.String(), "network-bridge": schema.String(), "container": schema.String(), "storage-port": schema.ForceInt(), "namespace": schema.String(), "lxc-clone": schema.Bool(), "lxc-clone-aufs": schema.Bool(), } // The port defaults below are not entirely arbitrary. Local user web // frameworks often use 8000 or 8080, so I didn't want to use either of // these, but did want the familiarity of using something in the 8000 // range. configDefaults = schema.Defaults{ "root-dir": "", "network-bridge": "lxcbr0", "container": string(instance.LXC), "bootstrap-ip": schema.Omit, "storage-port": 8040, "namespace": "", "lxc-clone": schema.Omit, "lxc-clone-aufs": schema.Omit, } ) type environConfig struct { *config.Config attrs map[string]interface{} } func newEnvironConfig(config *config.Config, attrs map[string]interface{}) *environConfig { return &environConfig{ Config: config, attrs: attrs, } } // Since it is technically possible for two different users on one machine to // have the same local provider name, we need to have a simple way to // namespace the file locations, but more importantly the containers. func (c *environConfig) namespace() string { return c.attrs["namespace"].(string) } func (c *environConfig) rootDir() string { return c.attrs["root-dir"].(string) } func (c *environConfig) container() instance.ContainerType { return instance.ContainerType(c.attrs["container"].(string)) } func (c *environConfig) networkBridge() string { return c.attrs["network-bridge"].(string) } func (c *environConfig) storageDir() string { return filepath.Join(c.rootDir(), "storage") } func (c *environConfig) mongoDir() string { return filepath.Join(c.rootDir(), "db") } func (c *environConfig) logDir() string { return fmt.Sprintf("%s-%s", agent.DefaultLogDir, c.namespace()) } // bootstrapIPAddress returns the IP address of the bootstrap machine. // As of 1.18 this is only set inside the environment, and not in the // .jenv file. func (c *environConfig) bootstrapIPAddress() string { addr, _ := c.attrs["bootstrap-ip"].(string) return addr } func (c *environConfig) storagePort() int { return c.attrs["storage-port"].(int) } func (c *environConfig) storageAddr() string { return fmt.Sprintf("%s:%d", c.bootstrapIPAddress(), c.storagePort()) } func (c *environConfig) configFile(filename string) string { return filepath.Join(c.rootDir(), filename) } func (c *environConfig) lxcClone() bool { value, _ := c.attrs["lxc-clone"].(bool) return value } func (c *environConfig) lxcCloneAUFS() bool { value, _ := c.attrs["lxc-clone-aufs"].(bool) return value } func (c *environConfig) createDirs() error { for _, dirname := range []string{ c.storageDir(), c.mongoDir(), } { logger.Tracef("creating directory %s", dirname) if err := os.MkdirAll(dirname, 0755); err != nil { return err } } return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/prereqs_test.go�������������������������0000644�0000153�0000161�00000014111�12321735776�026666� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local import ( "fmt" "io/ioutil" "os" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/agent/mongo" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) type prereqsSuite struct { testbase.LoggingSuite tmpdir string testMongodPath string } var _ = gc.Suite(&prereqsSuite{}) const lsbrelease = `#!/bin/sh echo $JUJUTEST_LSB_RELEASE_ID ` func init() { // Set the paths to mongod and lxc-ls to // something we know exists. This allows // all of the non-prereqs tests to pass // even when mongodb and lxc-ls can't be // found. lxclsPath = "/bin/true" // Allow non-prereq tests to pass by default. isPackageInstalled = func(packageName string) bool { return true } } func (s *prereqsSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.tmpdir = c.MkDir() s.testMongodPath = filepath.Join(s.tmpdir, "mongod") s.PatchEnvironment("PATH", s.tmpdir) s.PatchValue(&mongo.JujuMongodPath, "/somewhere/that/wont/exist") s.setMongoVersion(c, lowestMongoVersion.Major, lowestMongoVersion.Minor, lowestMongoVersion.Patch) os.Setenv("JUJUTEST_LSB_RELEASE_ID", "Ubuntu") err := ioutil.WriteFile(filepath.Join(s.tmpdir, "lsb_release"), []byte(lsbrelease), 0777) c.Assert(err, gc.IsNil) // symlink $temp/dpkg-query to /bin/true, to // simulate package installation query responses. err = os.Symlink("/bin/true", filepath.Join(s.tmpdir, "dpkg-query")) c.Assert(err, gc.IsNil) s.PatchValue(&isPackageInstalled, utils.IsPackageInstalled) } func (*prereqsSuite) TestSupportedOS(c *gc.C) { defer func(old string) { goos = old }(goos) goos = "windows" err := VerifyPrerequisites(instance.LXC) c.Assert(err, gc.ErrorMatches, "Unsupported operating system: windows(.|\n)*") } const fakeMongoFmt = `#!/bin/sh echo db version v%d.%d.%d echo Thu Feb 13 15:53:58.210 git version: b9925db5eac369d77a3a5f5d98a145eaaacd9673 ` func (s *prereqsSuite) setMongoVersion(c *gc.C, major, minor, patch int) { script := fmt.Sprintf(fakeMongoFmt, major, minor, patch) err := ioutil.WriteFile(s.testMongodPath, []byte(script), 0777) c.Assert(err, gc.IsNil) } func (s *prereqsSuite) TestParseMongoVersion(c *gc.C) { s.setMongoVersion(c, 2, 2, 2) ver, err := mongodVersion(s.testMongodPath) c.Assert(err, gc.IsNil) c.Assert(ver, gc.Equals, version.Number{2, 2, 2, 0}) } func (s *prereqsSuite) TestVerifyMongod(c *gc.C) { lowver := version.Number{2, 2, 2, 0} s.PatchValue(&lowestMongoVersion, lowver) s.setMongoVersion(c, 3, 0, 0) c.Assert(verifyMongod(), gc.IsNil) s.setMongoVersion(c, 2, 3, 0) c.Assert(verifyMongod(), gc.IsNil) s.setMongoVersion(c, 2, 2, 3) c.Assert(verifyMongod(), gc.IsNil) s.setMongoVersion(c, 2, 2, 2) c.Assert(verifyMongod(), gc.IsNil) expected := fmt.Sprintf("installed version of mongod .* is not supported by Juju. "+ "Juju requires version %v or greater.", lowver) s.setMongoVersion(c, 2, 2, 1) c.Assert(verifyMongod(), gc.ErrorMatches, expected) s.setMongoVersion(c, 2, 1, 3) c.Assert(verifyMongod(), gc.ErrorMatches, expected) s.setMongoVersion(c, 1, 3, 3) c.Assert(verifyMongod(), gc.ErrorMatches, expected) } func (s *prereqsSuite) TestParseVersion(c *gc.C) { data := ` db version v3.2.1 Thu Feb 13 15:53:58.210 git version: b9925db5eac369d77a3a5f5d98a145eaaacd9673 `[1:] v, err := parseVersion(data) c.Assert(err, gc.IsNil) c.Assert(v, gc.Equals, version.Number{3, 2, 1, 0}) data = "this is total garbage" v, err = parseVersion(data) c.Assert(err, gc.ErrorMatches, "could not parse mongod version") c.Assert(v, gc.Equals, version.Zero) } func (s *prereqsSuite) TestMongoPrereq(c *gc.C) { err := os.Remove(s.testMongodPath) c.Assert(err, gc.IsNil) err = VerifyPrerequisites(instance.LXC) c.Assert(err, gc.ErrorMatches, "(.|\n)*MongoDB server must be installed(.|\n)*") c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install mongodb-server(.|\n)*") os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu") err = VerifyPrerequisites(instance.LXC) c.Assert(err, gc.ErrorMatches, "(.|\n)*MongoDB server must be installed(.|\n)*") c.Assert(err, gc.Not(gc.ErrorMatches), "(.|\n)*apt-get install(.|\n)*") s.PatchValue(&lowestMongoVersion, version.Number{2, 2, 2, 0}) s.setMongoVersion(c, 3, 0, 0) err = VerifyPrerequisites(instance.LXC) c.Assert(err, gc.IsNil) } func (s *prereqsSuite) TestLxcPrereq(c *gc.C) { s.PatchValue(&lxclsPath, filepath.Join(s.tmpdir, "non-existent")) err := VerifyPrerequisites(instance.LXC) c.Assert(err, gc.ErrorMatches, "(.|\n)*Linux Containers \\(LXC\\) userspace tools must be\ninstalled(.|\n)*") c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install lxc(.|\n)*") os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu") err = VerifyPrerequisites(instance.LXC) c.Assert(err, gc.ErrorMatches, "(.|\n)*Linux Containers \\(LXC\\) userspace tools must be installed(.|\n)*") c.Assert(err, gc.Not(gc.ErrorMatches), "(.|\n)*apt-get install(.|\n)*") err = ioutil.WriteFile(lxclsPath, nil, 0777) c.Assert(err, gc.IsNil) err = VerifyPrerequisites(instance.LXC) c.Assert(err, gc.IsNil) } func (s *prereqsSuite) TestRsyslogGnutlsPrereq(c *gc.C) { err := os.Remove(filepath.Join(s.tmpdir, "dpkg-query")) c.Assert(err, gc.IsNil) err = os.Symlink("/bin/false", filepath.Join(s.tmpdir, "dpkg-query")) c.Assert(err, gc.IsNil) err = VerifyPrerequisites(instance.LXC) c.Assert(err, gc.ErrorMatches, "(.|\n)*rsyslog-gnutls must be installed to enable the local provider(.|\n)*") c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install rsyslog-gnutls(.|\n)*") s.PatchValue(&defaultRsyslogGnutlsPath, filepath.Join(s.tmpdir, "non-existent")) os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu") err = VerifyPrerequisites(instance.LXC) c.Assert(err, gc.ErrorMatches, "(.|\n)*non-existent: no such file or directory(.|\n)*") c.Assert(err, gc.Not(gc.ErrorMatches), "(.|\n)*apt-get install rsyslog-gnutls(.|\n)*") err = ioutil.WriteFile(defaultRsyslogGnutlsPath, nil, 0644) c.Assert(err, gc.IsNil) err = VerifyPrerequisites(instance.LXC) c.Assert(err, gc.IsNil) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/instance.go�����������������������������0000644�0000153�0000161�00000004253�12321735642�025750� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local import ( "fmt" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" ) type localInstance struct { id instance.Id env *localEnviron } var _ instance.Instance = (*localInstance)(nil) // Id implements instance.Instance.Id. func (inst *localInstance) Id() instance.Id { return inst.id } // Status implements instance.Instance.Status. func (inst *localInstance) Status() string { return "" } func (*localInstance) Refresh() error { return nil } func (inst *localInstance) Addresses() ([]instance.Address, error) { if inst.id == bootstrapInstanceId { addrs := []instance.Address{{ NetworkScope: instance.NetworkPublic, Type: instance.HostName, Value: "localhost", }, { NetworkScope: instance.NetworkCloudLocal, Type: instance.Ipv4Address, Value: inst.env.bridgeAddress, }} return addrs, nil } return nil, errors.NewNotImplementedError("localInstance.Addresses") } // DNSName implements instance.Instance.DNSName. func (inst *localInstance) DNSName() (string, error) { if inst.id == bootstrapInstanceId { return inst.env.bridgeAddress, nil } // Get the IPv4 address from eth0 return getAddressForInterface("eth0") } // WaitDNSName implements instance.Instance.WaitDNSName. func (inst *localInstance) WaitDNSName() (string, error) { return common.WaitDNSName(inst) } // OpenPorts implements instance.Instance.OpenPorts. func (inst *localInstance) OpenPorts(machineId string, ports []instance.Port) error { logger.Infof("OpenPorts called for %s:%v", machineId, ports) return nil } // ClosePorts implements instance.Instance.ClosePorts. func (inst *localInstance) ClosePorts(machineId string, ports []instance.Port) error { logger.Infof("ClosePorts called for %s:%v", machineId, ports) return nil } // Ports implements instance.Instance.Ports. func (inst *localInstance) Ports(machineId string) ([]instance.Port, error) { return nil, nil } // Add a string representation of the id. func (inst *localInstance) String() string { return fmt.Sprintf("inst:%v", inst.id) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/lxc.go����������������������������������0000644�0000153�0000161�00000001224�12321735642�024725� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local import ( "strconv" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/version" ) // releaseVersion is a function that returns a string representing the // DISTRIB_RELEASE from the /etc/lsb-release file. var releaseVersion = version.ReleaseVersion func useFastLXC(containerType instance.ContainerType) bool { if containerType != instance.LXC { return false } release := releaseVersion() if release == "" { return false } value, err := strconv.ParseFloat(release, 64) if err != nil { return false } return value >= 14.04 } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/lxc_test.go�����������������������������0000644�0000153�0000161�00000002416�12321735642�025770� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/local" "launchpad.net/juju-core/testing/testbase" ) type lxcTest struct { testbase.LoggingSuite } var _ = gc.Suite(&lxcTest{}) func (*lxcTest) TestUseFastLXCForContainer(c *gc.C) { c.Assert(local.UseFastLXC(instance.ContainerType("")), jc.IsFalse) c.Assert(local.UseFastLXC(instance.KVM), jc.IsFalse) } func (t *lxcTest) TestUseFastLXC(c *gc.C) { for i, test := range []struct { message string releaseVersion string expected bool }{{ message: "missing release file", }, { message: "precise release", releaseVersion: "12.04", }, { message: "trusty release", releaseVersion: "14.04", expected: true, }, { message: "unstable unicorn", releaseVersion: "14.10", expected: true, }, { message: "jaunty", releaseVersion: "9.10", }} { c.Logf("%v: %v", i, test.message) t.PatchValue(local.ReleaseVersion, func() string { return test.releaseVersion }) value := local.UseFastLXC(instance.LXC) c.Assert(value, gc.Equals, test.expected) } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/local_test.go���������������������������0000644�0000153�0000161�00000002176�12321735776�026307� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local_test import ( "fmt" "net" stdtesting "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/provider" "launchpad.net/juju-core/provider/local" "launchpad.net/juju-core/testing/testbase" ) func TestLocal(t *stdtesting.T) { gc.TestingT(t) } type localSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&localSuite{}) func (*localSuite) TestProviderRegistered(c *gc.C) { provider, error := environs.Provider(provider.Local) c.Assert(error, gc.IsNil) c.Assert(provider, gc.DeepEquals, local.Provider) } func (*localSuite) TestCheckLocalPort(c *gc.C) { // Listen on a random port. ln, err := net.Listen("tcp", ":0") c.Assert(err, gc.IsNil) defer ln.Close() port := ln.Addr().(*net.TCPAddr).Port checkLocalPort := *local.CheckLocalPort err = checkLocalPort(port, "test port") c.Assert(err, gc.ErrorMatches, fmt.Sprintf("cannot use %d as test port, already in use", port)) ln.Close() err = checkLocalPort(port, "test port, no longer in use") c.Assert(err, gc.IsNil) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/environprovider.go����������������������0000644�0000153�0000161�00000020423�12321735776�027404� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local import ( "fmt" "net" "os" "os/user" "syscall" "github.com/juju/loggo" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/provider" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) var logger = loggo.GetLogger("juju.provider.local") var _ environs.EnvironProvider = (*environProvider)(nil) type environProvider struct{} var providerInstance = &environProvider{} func init() { environs.RegisterProvider(provider.Local, providerInstance) } var userCurrent = user.Current // Open implements environs.EnvironProvider.Open. func (environProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Infof("opening environment %q", cfg.Name()) if _, ok := cfg.AgentVersion(); !ok { newCfg, err := cfg.Apply(map[string]interface{}{ "agent-version": version.Current.Number.String(), }) if err != nil { return nil, err } cfg = newCfg } // Set the "namespace" attribute. We do this here, and not in Prepare, // for backwards compatibility: older versions did not store the namespace // in config. if namespace, _ := cfg.UnknownAttrs()["namespace"].(string); namespace == "" { username := os.Getenv("USER") if username == "" { u, err := userCurrent() if err != nil { return nil, fmt.Errorf("failed to determine username for namespace: %v", err) } username = u.Username } var err error namespace = fmt.Sprintf("%s-%s", username, cfg.Name()) cfg, err = cfg.Apply(map[string]interface{}{"namespace": namespace}) if err != nil { return nil, fmt.Errorf("failed to create namespace: %v", err) } } // Do the initial validation on the config. localConfig, err := providerInstance.newConfig(cfg) if err != nil { return nil, err } if err := VerifyPrerequisites(localConfig.container()); err != nil { logger.Errorf("failed verification of local provider prerequisites: %v", err) return nil, err } environ := &localEnviron{name: cfg.Name()} if err := environ.SetConfig(cfg); err != nil { logger.Errorf("failure setting config: %v", err) return nil, err } return environ, nil } var detectAptProxies = utils.DetectAptProxies // Prepare implements environs.EnvironProvider.Prepare. func (p environProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { // The user must not set bootstrap-ip; this is determined by the provider, // and its presence used to determine whether the environment has yet been // bootstrapped. if _, ok := cfg.UnknownAttrs()["bootstrap-ip"]; ok { return nil, fmt.Errorf("bootstrap-ip must not be specified") } err := checkLocalPort(cfg.StatePort(), "state port") if err != nil { return nil, err } err = checkLocalPort(cfg.APIPort(), "API port") if err != nil { return nil, err } // If the user has specified no values for any of the three normal // proxies, then look in the environment and set them. attrs := make(map[string]interface{}) setIfNotBlank := func(key, value string) { if value != "" { attrs[key] = value } } logger.Tracef("Look for proxies?") if cfg.HttpProxy() == "" && cfg.HttpsProxy() == "" && cfg.FtpProxy() == "" && cfg.NoProxy() == "" { proxy := osenv.DetectProxies() logger.Tracef("Proxies detected %#v", proxy) setIfNotBlank("http-proxy", proxy.Http) setIfNotBlank("https-proxy", proxy.Https) setIfNotBlank("ftp-proxy", proxy.Ftp) setIfNotBlank("no-proxy", proxy.NoProxy) } if cfg.AptHttpProxy() == "" && cfg.AptHttpsProxy() == "" && cfg.AptFtpProxy() == "" { proxy, err := detectAptProxies() if err != nil { return nil, err } setIfNotBlank("apt-http-proxy", proxy.Http) setIfNotBlank("apt-https-proxy", proxy.Https) setIfNotBlank("apt-ftp-proxy", proxy.Ftp) } if len(attrs) > 0 { cfg, err = cfg.Apply(attrs) if err != nil { return nil, err } } return p.Open(cfg) } // checkLocalPort checks that the passed port is not used so far. var checkLocalPort = func(port int, description string) error { logger.Infof("checking %s", description) // Try to connect the port on localhost. address := fmt.Sprintf("localhost:%d", port) // TODO(mue) Add a timeout? conn, err := net.Dial("tcp", address) if err != nil { if nerr, ok := err.(*net.OpError); ok { if nerr.Err == syscall.ECONNREFUSED { // No connection, so everything is fine. return nil } } return err } // Connected, so port is in use. err = conn.Close() if err != nil { return err } return fmt.Errorf("cannot use %d as %s, already in use", port, description) } // Validate implements environs.EnvironProvider.Validate. func (provider environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { // Check for valid changes for the base config values. if err := config.Validate(cfg, old); err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { logger.Errorf("failed to validate unknown attrs: %v", err) return nil, err } localConfig := newEnvironConfig(cfg, validated) // Before potentially creating directories, make sure that the // root directory has not changed. containerType := localConfig.container() if old != nil { oldLocalConfig, err := provider.newConfig(old) if err != nil { return nil, fmt.Errorf("old config is not a valid local config: %v", old) } if containerType != oldLocalConfig.container() { return nil, fmt.Errorf("cannot change container from %q to %q", oldLocalConfig.container(), containerType) } if localConfig.rootDir() != oldLocalConfig.rootDir() { return nil, fmt.Errorf("cannot change root-dir from %q to %q", oldLocalConfig.rootDir(), localConfig.rootDir()) } if localConfig.networkBridge() != oldLocalConfig.networkBridge() { return nil, fmt.Errorf("cannot change network-bridge from %q to %q", oldLocalConfig.rootDir(), localConfig.rootDir()) } if localConfig.storagePort() != oldLocalConfig.storagePort() { return nil, fmt.Errorf("cannot change storage-port from %v to %v", oldLocalConfig.storagePort(), localConfig.storagePort()) } } // Currently only supported containers are "lxc" and "kvm". if containerType != instance.LXC && containerType != instance.KVM { return nil, fmt.Errorf("unsupported container type: %q", containerType) } dir, err := utils.NormalizePath(localConfig.rootDir()) if err != nil { return nil, err } if dir == "." { dir = osenv.JujuHomePath(cfg.Name()) } // Always assign the normalized path. localConfig.attrs["root-dir"] = dir if containerType != instance.KVM { fastOptionAvailable := useFastLXC(containerType) if _, found := localConfig.attrs["lxc-clone"]; !found { localConfig.attrs["lxc-clone"] = fastOptionAvailable } } // Apply the coerced unknown values back into the config. return cfg.Apply(localConfig.attrs) } // BoilerplateConfig implements environs.EnvironProvider.BoilerplateConfig. func (environProvider) BoilerplateConfig() string { return ` # https://juju.ubuntu.com/docs/config-local.html local: type: local # root-dir holds the directory that is used for the storage files and # database. The default location is $JUJU_HOME/<env-name>. # $JUJU_HOME defaults to ~/.juju. Override if needed. # # root-dir: ~/.juju/local # storage-port holds the port where the local provider starts the # HTTP file server. Override the value if you have multiple local # providers, or if the default port is used by another program. # # storage-port: 8040 # network-bridge holds the name of the LXC network bridge to use. # Override if the default LXC network bridge is different. # # # network-bridge: lxcbr0 # The default series to deploy the state-server and charms on. # # default-series: precise `[1:] } // SecretAttrs implements environs.EnvironProvider.SecretAttrs. func (environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { // don't have any secret attrs return nil, nil } func (p environProvider) newConfig(cfg *config.Config) (*environConfig, error) { valid, err := p.Validate(cfg, nil) if err != nil { return nil, err } return newEnvironConfig(valid, valid.UnknownAttrs()), nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/environprovider_test.go�����������������0000644�0000153�0000161�00000024750�12321735776�030452� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local_test import ( "errors" "os/user" "github.com/juju/loggo" "github.com/juju/testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/container/kvm" lxctesting "launchpad.net/juju-core/container/lxc/testing" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/provider" "launchpad.net/juju-core/provider/local" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) type baseProviderSuite struct { lxctesting.TestSuite home *coretesting.FakeHome restore func() } func (s *baseProviderSuite) SetUpTest(c *gc.C) { s.TestSuite.SetUpTest(c) s.home = coretesting.MakeFakeHomeNoEnvironments(c, "test") loggo.GetLogger("juju.provider.local").SetLogLevel(loggo.TRACE) s.restore = local.MockAddressForInterface() } func (s *baseProviderSuite) TearDownTest(c *gc.C) { s.restore() s.home.Restore() s.TestSuite.TearDownTest(c) } type prepareSuite struct { coretesting.FakeHomeSuite } var _ = gc.Suite(&prepareSuite{}) func (s *prepareSuite) SetUpTest(c *gc.C) { s.FakeHomeSuite.SetUpTest(c) loggo.GetLogger("juju.provider.local").SetLogLevel(loggo.TRACE) s.PatchEnvironment("http_proxy", "") s.PatchEnvironment("HTTP_PROXY", "") s.PatchEnvironment("https_proxy", "") s.PatchEnvironment("HTTPS_PROXY", "") s.PatchEnvironment("ftp_proxy", "") s.PatchEnvironment("FTP_PROXY", "") s.PatchEnvironment("no_proxy", "") s.PatchEnvironment("NO_PROXY", "") s.HookCommandOutput(&utils.AptCommandOutput, nil, nil) s.PatchValue(local.CheckLocalPort, func(port int, desc string) error { return nil }) restore := local.MockAddressForInterface() s.AddCleanup(func(*gc.C) { restore() }) } func (s *prepareSuite) TestPrepareCapturesEnvironment(c *gc.C) { baseConfig, err := config.New(config.UseDefaults, map[string]interface{}{ "type": provider.Local, "name": "test", }) c.Assert(err, gc.IsNil) provider, err := environs.Provider(provider.Local) c.Assert(err, gc.IsNil) for i, test := range []struct { message string extraConfig map[string]interface{} env map[string]string aptOutput string expectedProxy osenv.ProxySettings expectedAptProxy osenv.ProxySettings }{{ message: "nothing set", }, { message: "grabs proxy from environment", env: map[string]string{ "http_proxy": "http://user@10.0.0.1", "HTTPS_PROXY": "https://user@10.0.0.1", "ftp_proxy": "ftp://user@10.0.0.1", "no_proxy": "localhost,10.0.3.1", }, expectedProxy: osenv.ProxySettings{ Http: "http://user@10.0.0.1", Https: "https://user@10.0.0.1", Ftp: "ftp://user@10.0.0.1", NoProxy: "localhost,10.0.3.1", }, expectedAptProxy: osenv.ProxySettings{ Http: "http://user@10.0.0.1", Https: "https://user@10.0.0.1", Ftp: "ftp://user@10.0.0.1", }, }, { message: "skips proxy from environment if http-proxy set", extraConfig: map[string]interface{}{ "http-proxy": "http://user@10.0.0.42", }, env: map[string]string{ "http_proxy": "http://user@10.0.0.1", "HTTPS_PROXY": "https://user@10.0.0.1", "ftp_proxy": "ftp://user@10.0.0.1", }, expectedProxy: osenv.ProxySettings{ Http: "http://user@10.0.0.42", }, expectedAptProxy: osenv.ProxySettings{ Http: "http://user@10.0.0.42", }, }, { message: "skips proxy from environment if https-proxy set", extraConfig: map[string]interface{}{ "https-proxy": "https://user@10.0.0.42", }, env: map[string]string{ "http_proxy": "http://user@10.0.0.1", "HTTPS_PROXY": "https://user@10.0.0.1", "ftp_proxy": "ftp://user@10.0.0.1", }, expectedProxy: osenv.ProxySettings{ Https: "https://user@10.0.0.42", }, expectedAptProxy: osenv.ProxySettings{ Https: "https://user@10.0.0.42", }, }, { message: "skips proxy from environment if ftp-proxy set", extraConfig: map[string]interface{}{ "ftp-proxy": "ftp://user@10.0.0.42", }, env: map[string]string{ "http_proxy": "http://user@10.0.0.1", "HTTPS_PROXY": "https://user@10.0.0.1", "ftp_proxy": "ftp://user@10.0.0.1", }, expectedProxy: osenv.ProxySettings{ Ftp: "ftp://user@10.0.0.42", }, expectedAptProxy: osenv.ProxySettings{ Ftp: "ftp://user@10.0.0.42", }, }, { message: "skips proxy from environment if no-proxy set", extraConfig: map[string]interface{}{ "no-proxy": "localhost,10.0.3.1", }, env: map[string]string{ "http_proxy": "http://user@10.0.0.1", "HTTPS_PROXY": "https://user@10.0.0.1", "ftp_proxy": "ftp://user@10.0.0.1", }, expectedProxy: osenv.ProxySettings{ NoProxy: "localhost,10.0.3.1", }, }, { message: "apt-proxies detected", aptOutput: `CommandLine::AsString "apt-config dump"; Acquire::http::Proxy "10.0.3.1:3142"; Acquire::https::Proxy "false"; Acquire::ftp::Proxy "none"; Acquire::magic::Proxy "none"; `, expectedAptProxy: osenv.ProxySettings{ Http: "10.0.3.1:3142", Https: "false", Ftp: "none", }, }, { message: "apt-proxies not used if apt-http-proxy set", extraConfig: map[string]interface{}{ "apt-http-proxy": "value-set", }, aptOutput: `CommandLine::AsString "apt-config dump"; Acquire::http::Proxy "10.0.3.1:3142"; Acquire::https::Proxy "false"; Acquire::ftp::Proxy "none"; Acquire::magic::Proxy "none"; `, expectedAptProxy: osenv.ProxySettings{ Http: "value-set", }, }, { message: "apt-proxies not used if apt-https-proxy set", extraConfig: map[string]interface{}{ "apt-https-proxy": "value-set", }, aptOutput: `CommandLine::AsString "apt-config dump"; Acquire::http::Proxy "10.0.3.1:3142"; Acquire::https::Proxy "false"; Acquire::ftp::Proxy "none"; Acquire::magic::Proxy "none"; `, expectedAptProxy: osenv.ProxySettings{ Https: "value-set", }, }, { message: "apt-proxies not used if apt-ftp-proxy set", extraConfig: map[string]interface{}{ "apt-ftp-proxy": "value-set", }, aptOutput: `CommandLine::AsString "apt-config dump"; Acquire::http::Proxy "10.0.3.1:3142"; Acquire::https::Proxy "false"; Acquire::ftp::Proxy "none"; Acquire::magic::Proxy "none"; `, expectedAptProxy: osenv.ProxySettings{ Ftp: "value-set", }, }} { c.Logf("\n%v: %s", i, test.message) cleanup := []func(){} for key, value := range test.env { restore := testing.PatchEnvironment(key, value) cleanup = append(cleanup, restore) } _, restore := testing.HookCommandOutput(&utils.AptCommandOutput, []byte(test.aptOutput), nil) cleanup = append(cleanup, restore) testConfig := baseConfig if test.extraConfig != nil { testConfig, err = baseConfig.Apply(test.extraConfig) c.Assert(err, gc.IsNil) } env, err := provider.Prepare(coretesting.Context(c), testConfig) c.Assert(err, gc.IsNil) envConfig := env.Config() c.Assert(envConfig.HttpProxy(), gc.Equals, test.expectedProxy.Http) c.Assert(envConfig.HttpsProxy(), gc.Equals, test.expectedProxy.Https) c.Assert(envConfig.FtpProxy(), gc.Equals, test.expectedProxy.Ftp) c.Assert(envConfig.NoProxy(), gc.Equals, test.expectedProxy.NoProxy) c.Assert(envConfig.AptHttpProxy(), gc.Equals, test.expectedAptProxy.Http) c.Assert(envConfig.AptHttpsProxy(), gc.Equals, test.expectedAptProxy.Https) c.Assert(envConfig.AptFtpProxy(), gc.Equals, test.expectedAptProxy.Ftp) for _, clean := range cleanup { clean() } } } func (s *prepareSuite) TestPrepareNamespace(c *gc.C) { s.PatchValue(local.DetectAptProxies, func() (osenv.ProxySettings, error) { return osenv.ProxySettings{}, nil }) basecfg, err := config.New(config.UseDefaults, map[string]interface{}{ "type": "local", "name": "test", }) provider, err := environs.Provider("local") c.Assert(err, gc.IsNil) type test struct { userEnv string userOS string userOSErr error namespace string err string } tests := []test{{ userEnv: "someone", userOS: "other", namespace: "someone-test", }, { userOS: "other", namespace: "other-test", }, { userOSErr: errors.New("oh noes"), err: "failed to determine username for namespace: oh noes", }} for i, test := range tests { c.Logf("test %d: %v", i, test) s.PatchEnvironment("USER", test.userEnv) s.PatchValue(local.UserCurrent, func() (*user.User, error) { return &user.User{Username: test.userOS}, test.userOSErr }) env, err := provider.Prepare(coretesting.Context(c), basecfg) if test.err == "" { c.Assert(err, gc.IsNil) cfg := env.Config() c.Assert(cfg.UnknownAttrs()["namespace"], gc.Equals, test.namespace) } else { c.Assert(err, gc.ErrorMatches, test.err) } } } func (s *prepareSuite) TestFastLXCClone(c *gc.C) { s.PatchValue(local.DetectAptProxies, func() (osenv.ProxySettings, error) { return osenv.ProxySettings{}, nil }) s.PatchValue(&kvm.IsKVMSupported, func() (bool, error) { return true, nil }) s.PatchValue(&local.VerifyPrerequisites, func(containerType instance.ContainerType) error { return nil }) basecfg, err := config.New(config.UseDefaults, map[string]interface{}{ "type": "local", "name": "test", }) provider, err := environs.Provider("local") c.Assert(err, gc.IsNil) type test struct { systemDefault bool extraConfig map[string]interface{} expectClone bool expectAUFS bool } tests := []test{{ extraConfig: map[string]interface{}{ "container": "lxc", }, }, { extraConfig: map[string]interface{}{ "container": "lxc", "lxc-clone": "true", }, expectClone: true, }, { systemDefault: true, extraConfig: map[string]interface{}{ "container": "lxc", }, expectClone: true, }, { systemDefault: true, extraConfig: map[string]interface{}{ "container": "kvm", }, }, { systemDefault: true, extraConfig: map[string]interface{}{ "container": "lxc", "lxc-clone": false, }, }, { systemDefault: true, extraConfig: map[string]interface{}{ "container": "lxc", "lxc-clone-aufs": true, }, expectClone: true, expectAUFS: true, }} for i, test := range tests { c.Logf("test %d: %v", i, test) releaseVersion := "12.04" if test.systemDefault { releaseVersion = "14.04" } s.PatchValue(local.ReleaseVersion, func() string { return releaseVersion }) testConfig, err := basecfg.Apply(test.extraConfig) c.Assert(err, gc.IsNil) env, err := provider.Open(testConfig) c.Assert(err, gc.IsNil) localAttributes := env.Config().UnknownAttrs() value, _ := localAttributes["lxc-clone"].(bool) c.Assert(value, gc.Equals, test.expectClone) value, _ = localAttributes["lxc-clone-aufs"].(bool) c.Assert(value, gc.Equals, test.expectAUFS) } } ������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/environ.go������������������������������0000644�0000153�0000161�00000036477�12321735776�025651� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013, 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local import ( "fmt" "net" "os" "os/exec" "path/filepath" "regexp" "strconv" "strings" "sync" "syscall" "github.com/errgo/errgo" "launchpad.net/juju-core/agent" coreCloudinit "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/cloudinit/sshinit" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/factory" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/filestorage" "launchpad.net/juju-core/environs/httpstorage" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/state/api/params" "launchpad.net/juju-core/upstart" "launchpad.net/juju-core/utils/shell" "launchpad.net/juju-core/version" "launchpad.net/juju-core/worker/terminationworker" ) // boostrapInstanceId is just the name we give to the bootstrap machine. // Using "localhost" because it is, and it makes sense. const bootstrapInstanceId instance.Id = "localhost" // localEnviron implements Environ. var _ environs.Environ = (*localEnviron)(nil) // localEnviron implements SupportsCustomSources. var _ envtools.SupportsCustomSources = (*localEnviron)(nil) type localEnviron struct { localMutex sync.Mutex config *environConfig name string bridgeAddress string localStorage storage.Storage storageListener net.Listener containerManager container.Manager } // GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata. func (e *localEnviron) GetToolsSources() ([]simplestreams.DataSource, error) { // Add the simplestreams source off the control bucket. return []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}, nil } // SupportedArchitectures is specified on the EnvironCapability interface. func (*localEnviron) SupportedArchitectures() ([]string, error) { localArch := arch.HostArch() return []string{localArch}, nil } // Name is specified in the Environ interface. func (env *localEnviron) Name() string { return env.name } func (env *localEnviron) mongoServiceName() string { return "juju-db-" + env.config.namespace() } func (env *localEnviron) machineAgentServiceName() string { return "juju-agent-" + env.config.namespace() } func ensureNotRoot() error { if checkIfRoot() { return fmt.Errorf("bootstrapping a local environment must not be done as root") } return nil } // Bootstrap is specified in the Environ interface. func (env *localEnviron) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { if err := ensureNotRoot(); err != nil { return err } privateKey, err := common.GenerateSystemSSHKey(env) if err != nil { return err } // Before we write the agent config file, we need to make sure the // instance is saved in the StateInfo. if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{ StateInstances: []instance.Id{bootstrapInstanceId}, }); err != nil { logger.Errorf("failed to save state instances: %v", err) return err } vers := version.Current selectedTools, err := common.EnsureBootstrapTools(ctx, env, vers.Series, &vers.Arch) if err != nil { return err } // Record the bootstrap IP, so the containers know where to go for storage. cfg, err := env.Config().Apply(map[string]interface{}{ "bootstrap-ip": env.bridgeAddress, }) if err == nil { err = env.SetConfig(cfg) } if err != nil { logger.Errorf("failed to apply bootstrap-ip to config: %v", err) return err } mcfg := environs.NewBootstrapMachineConfig(privateKey) mcfg.InstanceId = bootstrapInstanceId mcfg.Tools = selectedTools[0] mcfg.DataDir = env.config.rootDir() mcfg.LogDir = fmt.Sprintf("/var/log/juju-%s", env.config.namespace()) mcfg.Jobs = []params.MachineJob{params.JobManageEnviron} mcfg.CloudInitOutputLog = filepath.Join(mcfg.DataDir, "cloud-init-output.log") mcfg.DisablePackageCommands = true mcfg.MachineAgentServiceName = env.machineAgentServiceName() mcfg.MongoServiceName = env.mongoServiceName() mcfg.AgentEnvironment = map[string]string{ agent.Namespace: env.config.namespace(), agent.StorageDir: env.config.storageDir(), agent.StorageAddr: env.config.storageAddr(), } if err := environs.FinishMachineConfig(mcfg, cfg, cons); err != nil { return err } // don't write proxy settings for local machine mcfg.AptProxySettings = osenv.ProxySettings{} mcfg.ProxySettings = osenv.ProxySettings{} cloudcfg := coreCloudinit.New() // Since rsyslogd is restricted by apparmor to only write to /var/log/** // we now provide a symlink to the written file in the local log dir. // Also, we leave the old all-machines.log file in // /var/log/juju-{{namespace}} until we start the environment again. So // potentially remove it at the start of the cloud-init. localLogDir := filepath.Join(mcfg.DataDir, "log") if err := os.RemoveAll(localLogDir); err != nil { return err } if err := os.Symlink(mcfg.LogDir, localLogDir); err != nil { return err } if err := os.Remove(mcfg.CloudInitOutputLog); err != nil && !os.IsNotExist(err) { return err } cloudcfg.AddScripts( fmt.Sprintf("rm -fr %s", mcfg.LogDir), fmt.Sprintf("rm -f /var/spool/rsyslog/machine-0-%s", env.config.namespace()), ) if err := cloudinit.ConfigureJuju(mcfg, cloudcfg); err != nil { return err } return finishBootstrap(mcfg, cloudcfg, ctx) } // finishBootstrap converts the machine config to cloud-config, // converts that to a script, and then executes it locally. // // mcfg is supplied for testing purposes. var finishBootstrap = func(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config, ctx environs.BootstrapContext) error { configScript, err := sshinit.ConfigureScript(cloudcfg) if err != nil { return nil } script := shell.DumpFileOnErrorScript(mcfg.CloudInitOutputLog) + configScript cmd := exec.Command("sudo", "/bin/bash", "-s") cmd.Stdin = strings.NewReader(script) cmd.Stdout = ctx.GetStdout() cmd.Stderr = ctx.GetStderr() return cmd.Run() } // StateInfo is specified in the Environ interface. func (env *localEnviron) StateInfo() (*state.Info, *api.Info, error) { return common.StateInfo(env) } // Config is specified in the Environ interface. func (env *localEnviron) Config() *config.Config { env.localMutex.Lock() defer env.localMutex.Unlock() return env.config.Config } // SetConfig is specified in the Environ interface. func (env *localEnviron) SetConfig(cfg *config.Config) error { ecfg, err := providerInstance.newConfig(cfg) if err != nil { logger.Errorf("failed to create new environ config: %v", err) return err } env.localMutex.Lock() defer env.localMutex.Unlock() env.config = ecfg env.name = ecfg.Name() containerType := ecfg.container() managerConfig := container.ManagerConfig{ container.ConfigName: env.config.namespace(), container.ConfigLogDir: env.config.logDir(), } if containerType == instance.LXC { managerConfig["use-clone"] = strconv.FormatBool(env.config.lxcClone()) managerConfig["use-aufs"] = strconv.FormatBool(env.config.lxcCloneAUFS()) } env.containerManager, err = factory.NewContainerManager( containerType, managerConfig) if err != nil { return err } // When the localEnviron value is created on the client // side, the bootstrap-ip attribute will not exist, // because it is only set *within* the running // environment, not in the configuration created by // Prepare. // // When bootstrapIPAddress returns a non-empty string, // we know we are running server-side and thus must use // httpstorage. if addr := ecfg.bootstrapIPAddress(); addr != "" { env.bridgeAddress = addr return nil } // If we get to here, it is because we haven't yet bootstrapped an // environment, and saved the config in it, or we are running a command // from the command line, so it is ok to work on the assumption that we // have direct access to the directories. if err := env.config.createDirs(); err != nil { return err } // Record the network bridge address and create a filestorage. if err := env.resolveBridgeAddress(cfg); err != nil { return err } return env.setLocalStorage() } // resolveBridgeAddress finishes up the setup of the environment in // situations where there is no machine agent running yet. func (env *localEnviron) resolveBridgeAddress(cfg *config.Config) error { // We need the provider config to get the network bridge. config, err := providerInstance.newConfig(cfg) if err != nil { logger.Errorf("failed to create new environ config: %v", err) return err } networkBridge := config.networkBridge() bridgeAddress, err := getAddressForInterface(networkBridge) if err != nil { logger.Infof("configure a different bridge using 'network-bridge' in the config file") return fmt.Errorf("cannot find address of network-bridge: %q: %v", networkBridge, err) } logger.Debugf("found %q as address for %q", bridgeAddress, networkBridge) env.bridgeAddress = bridgeAddress return nil } // setLocalStorage creates a filestorage so tools can // be synced and so forth without having a machine agent // running. func (env *localEnviron) setLocalStorage() error { storage, err := filestorage.NewFileStorageWriter(env.config.storageDir()) if err != nil { return err } env.localStorage = storage return nil } // StartInstance is specified in the InstanceBroker interface. func (env *localEnviron) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { series := args.Tools.OneSeries() logger.Debugf("StartInstance: %q, %s", args.MachineConfig.MachineId, series) args.MachineConfig.Tools = args.Tools[0] args.MachineConfig.MachineContainerType = env.config.container() logger.Debugf("tools: %#v", args.MachineConfig.Tools) network := container.BridgeNetworkConfig(env.config.networkBridge()) if err := environs.FinishMachineConfig(args.MachineConfig, env.config.Config, args.Constraints); err != nil { return nil, nil, err } // TODO: evaluate the impact of setting the contstraints on the // machineConfig for all machines rather than just state server nodes. // This limiation is why the constraints are assigned directly here. args.MachineConfig.Constraints = args.Constraints args.MachineConfig.AgentEnvironment[agent.Namespace] = env.config.namespace() inst, hardware, err := env.containerManager.CreateContainer(args.MachineConfig, series, network) if err != nil { return nil, nil, err } return inst, hardware, nil } // StartInstance is specified in the InstanceBroker interface. func (env *localEnviron) StopInstances(instances []instance.Instance) error { for _, inst := range instances { if inst.Id() == bootstrapInstanceId { return fmt.Errorf("cannot stop the bootstrap instance") } if err := env.containerManager.DestroyContainer(inst); err != nil { return err } } return nil } // Instances is specified in the Environ interface. func (env *localEnviron) Instances(ids []instance.Id) ([]instance.Instance, error) { if len(ids) == 0 { return nil, nil } insts, err := env.AllInstances() if err != nil { return nil, err } allInstances := make(map[instance.Id]instance.Instance) for _, inst := range insts { allInstances[inst.Id()] = inst } var found int insts = make([]instance.Instance, len(ids)) for i, id := range ids { if inst, ok := allInstances[id]; ok { insts[i] = inst found++ } } if found == 0 { insts, err = nil, environs.ErrNoInstances } else if found < len(ids) { err = environs.ErrPartialInstances } else { err = nil } return insts, err } // AllInstances is specified in the InstanceBroker interface. func (env *localEnviron) AllInstances() (instances []instance.Instance, err error) { instances = append(instances, &localInstance{bootstrapInstanceId, env}) // Add in all the containers as well. lxcInstances, err := env.containerManager.ListContainers() if err != nil { return nil, err } for _, inst := range lxcInstances { instances = append(instances, &localInstance{inst.Id(), env}) } return instances, nil } // Storage is specified in the Environ interface. func (env *localEnviron) Storage() storage.Storage { // localStorage is non-nil if we're running from the CLI if env.localStorage != nil { return env.localStorage } return httpstorage.Client(env.config.storageAddr()) } // Destroy is specified in the Environ interface. func (env *localEnviron) Destroy() error { // If bootstrap failed, for example because the user // lacks sudo rights, then the agents won't have been // installed. If that's the case, we can just remove // the data-dir and exit. agentsDir := filepath.Join(env.config.rootDir(), "agents") if _, err := os.Stat(agentsDir); os.IsNotExist(err) { // If we can't remove the root dir, then continue // and attempt as root anyway. if os.RemoveAll(env.config.rootDir()) == nil { return nil } } if !checkIfRoot() { juju, err := exec.LookPath(os.Args[0]) if err != nil { return err } args := []string{ "env", osenv.JujuHomeEnvKey + "=" + osenv.JujuHome(), juju, "destroy-environment", "-y", "--force", env.Name(), } cmd := exec.Command("sudo", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() } // Kill all running instances. This must be done as // root, or listing/stopping containers will fail. containers, err := env.containerManager.ListContainers() if err != nil { return err } for _, inst := range containers { if err := env.containerManager.DestroyContainer(inst); err != nil { return err } } cmd := exec.Command( "pkill", fmt.Sprintf("-%d", terminationworker.TerminationSignal), "-f", filepath.Join(regexp.QuoteMeta(env.config.rootDir()), ".*", "jujud"), ) if err := cmd.Run(); err != nil { if err, ok := err.(*exec.ExitError); ok { // Exit status 1 means no processes were matched: // we don't consider this an error here. if err.ProcessState.Sys().(syscall.WaitStatus).ExitStatus() != 1 { return errgo.Annotate(err, "failed to kill jujud") } } } // Stop the mongo database and machine agent. It's possible that the // service doesn't exist or is not running, so don't check the error. upstart.NewService(env.mongoServiceName()).StopAndRemove() upstart.NewService(env.machineAgentServiceName()).StopAndRemove() // Finally, remove the data-dir. if err := os.RemoveAll(env.config.rootDir()); err != nil && !os.IsNotExist(err) { return err } return nil } // OpenPorts is specified in the Environ interface. func (env *localEnviron) OpenPorts(ports []instance.Port) error { return fmt.Errorf("open ports not implemented") } // ClosePorts is specified in the Environ interface. func (env *localEnviron) ClosePorts(ports []instance.Port) error { return fmt.Errorf("close ports not implemented") } // Ports is specified in the Environ interface. func (env *localEnviron) Ports() ([]instance.Port, error) { return nil, nil } // Provider is specified in the Environ interface. func (env *localEnviron) Provider() environs.EnvironProvider { return providerInstance } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/net.go����������������������������������0000644�0000153�0000161�00000000470�12321735642�024727� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local import ( "launchpad.net/juju-core/utils" ) // getAddressForInterface is a variable so we can change the implementation // for testing purposes. var getAddressForInterface = utils.GetAddressForInterface ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/prereqs.go������������������������������0000644�0000153�0000161�00000012461�12321735776�025635� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local import ( "errors" "fmt" "os" "os/exec" "regexp" "runtime" "launchpad.net/juju-core/agent/mongo" "launchpad.net/juju-core/container/kvm" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) var notLinuxError = errors.New("The local provider is currently only available for Linux") const installMongodUbuntu = "MongoDB server must be installed to enable the local provider:" const aptAddRepositoryJujuStable = ` sudo apt-add-repository ppa:juju/stable # required for MongoDB SSL support sudo apt-get update` const aptGetInstallMongodbServer = ` sudo apt-get install mongodb-server` const installMongodGeneric = ` MongoDB server must be installed to enable the local provider. Please consult your operating system distribution's documentation for instructions on installing the MongoDB server. Juju requires a MongoDB server built with SSL support. ` const installLxcUbuntu = ` Linux Containers (LXC) userspace tools must be installed to enable the local provider: sudo apt-get install lxc` const installRsyslogGnutlsUbuntu = ` rsyslog-gnutls must be installed to enable the local provider: sudo apt-get install rsyslog-gnutls` const installRsyslogGnutlsGeneric = ` rsyslog-gnutls must be installed to enable the local provider. Please consult your operating system distribution's documentation for instructions on installing this package.` const installLxcGeneric = ` Linux Containers (LXC) userspace tools must be installed to enable the local provider. Please consult your operating system distribution's documentation for instructions on installing the LXC userspace tools.` const errUnsupportedOS = `Unsupported operating system: %s The local provider is currently only available for Linux` // lowestMongoVersion is the lowest version of mongo that juju supports. var lowestMongoVersion = version.Number{Major: 2, Minor: 2, Patch: 4} // lxclsPath is the path to "lxc-ls", an LXC userspace tool // we check the presence of to determine whether the // tools are installed. This is a variable only to support // unit testing. var lxclsPath = "lxc-ls" // isPackageInstalled is a variable to support testing. var isPackageInstalled = utils.IsPackageInstalled // defaultRsyslogGnutlsPath is the default path to the // rsyslog GnuTLS module. This is a variable only to // support unit testing. var defaultRsyslogGnutlsPath = "/usr/lib/rsyslog/lmnsd_gtls.so" // The operating system the process is running in. // This is a variable only to support unit testing. var goos = runtime.GOOS // This is the regex for processing the results of mongod --verison var mongoVerRegex = regexp.MustCompile(`db version v(\d+\.\d+\.\d+)`) // VerifyPrerequisites verifies the prerequisites of // the local machine (machine 0) for running the local // provider. var VerifyPrerequisites = func(containerType instance.ContainerType) error { if goos != "linux" { return fmt.Errorf(errUnsupportedOS, goos) } if err := verifyMongod(); err != nil { return err } if err := verifyRsyslogGnutls(); err != nil { return err } switch containerType { case instance.LXC: return verifyLxc() case instance.KVM: return kvm.VerifyKVMEnabled() } return fmt.Errorf("Unknown container type specified in the config.") } func verifyMongod() error { path, err := mongo.MongodPath() if err != nil { return wrapMongodNotExist(err) } ver, err := mongodVersion(path) if err != nil { return err } if ver.Compare(lowestMongoVersion) < 0 { return fmt.Errorf("installed version of mongod (%v) is not supported by Juju. "+ "Juju requires version %v or greater.", ver, lowestMongoVersion) } return nil } func mongodVersion(path string) (version.Number, error) { data, err := utils.RunCommand(path, "--version") if err != nil { return version.Zero, wrapMongodNotExist(err) } return parseVersion(data) } func parseVersion(data string) (version.Number, error) { matches := mongoVerRegex.FindStringSubmatch(data) if len(matches) < 2 { return version.Zero, errors.New("could not parse mongod version") } return version.Parse(matches[1]) } func verifyLxc() error { _, err := exec.LookPath(lxclsPath) if err != nil { return wrapLxcNotFound(err) } return nil } func verifyRsyslogGnutls() error { if isPackageInstalled("rsyslog-gnutls") { return nil } if utils.IsUbuntu() { return errors.New(installRsyslogGnutlsUbuntu) } // Not all Linuxes will distribute the module // in the same way. Check if it's in the default // location too. _, err := os.Stat(defaultRsyslogGnutlsPath) if err == nil { return nil } return fmt.Errorf("%v\n%s", err, installRsyslogGnutlsGeneric) } func wrapMongodNotExist(err error) error { if utils.IsUbuntu() { series := version.Current.Series args := []interface{}{err, installMongodUbuntu} format := "%v\n%s\n%s" if series == "precise" || series == "quantal" { format += "%s" args = append(args, aptAddRepositoryJujuStable) } args = append(args, aptGetInstallMongodbServer) return fmt.Errorf(format, args...) } return fmt.Errorf("%v\n%s", err, installMongodGeneric) } func wrapLxcNotFound(err error) error { if utils.IsUbuntu() { return fmt.Errorf("%v\n%s", err, installLxcUbuntu) } return fmt.Errorf("%v\n%s", err, installLxcGeneric) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/local/environ_test.go�������������������������0000644�0000153�0000161�00000022266�12321735776�026677� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package local_test import ( "fmt" "io/ioutil" "os" "path/filepath" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" coreCloudinit "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/container" "launchpad.net/juju-core/container/lxc" containertesting "launchpad.net/juju-core/container/testing" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/jujutest" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/provider/local" "launchpad.net/juju-core/state/api/params" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/upstart" ) const echoCommandScript = "#!/bin/sh\necho $0 \"$@\" >> $0.args" type environSuite struct { baseProviderSuite envtesting.ToolsFixture } var _ = gc.Suite(&environSuite{}) func (s *environSuite) SetUpTest(c *gc.C) { s.baseProviderSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) } func (s *environSuite) TearDownTest(c *gc.C) { s.ToolsFixture.TearDownTest(c) s.baseProviderSuite.TearDownTest(c) } func (*environSuite) TestOpenFailsWithProtectedDirectories(c *gc.C) { testConfig := minimalConfig(c) testConfig, err := testConfig.Apply(map[string]interface{}{ "root-dir": "/usr/lib/juju", }) c.Assert(err, gc.IsNil) environ, err := local.Provider.Open(testConfig) c.Assert(err, gc.ErrorMatches, "mkdir .* permission denied") c.Assert(environ, gc.IsNil) } func (s *environSuite) TestNameAndStorage(c *gc.C) { testConfig := minimalConfig(c) environ, err := local.Provider.Open(testConfig) c.Assert(err, gc.IsNil) c.Assert(environ.Name(), gc.Equals, "test") c.Assert(environ.Storage(), gc.NotNil) } func (s *environSuite) TestGetToolsMetadataSources(c *gc.C) { testConfig := minimalConfig(c) environ, err := local.Provider.Open(testConfig) c.Assert(err, gc.IsNil) sources, err := tools.GetMetadataSources(environ) c.Assert(err, gc.IsNil) c.Assert(len(sources), gc.Equals, 1) url, err := sources[0].URL("") c.Assert(err, gc.IsNil) c.Assert(strings.Contains(url, "/tools"), jc.IsTrue) } func (*environSuite) TestSupportedArchitectures(c *gc.C) { testConfig := minimalConfig(c) environ, err := local.Provider.Open(testConfig) c.Assert(err, gc.IsNil) c.Assert(err, gc.IsNil) arches, err := environ.SupportedArchitectures() c.Assert(err, gc.IsNil) for _, a := range arches { c.Assert(arch.IsSupportedArch(a), jc.IsTrue) } } type localJujuTestSuite struct { baseProviderSuite jujutest.Tests oldUpstartLocation string testPath string dbServiceName string fakesudo string } func (s *localJujuTestSuite) SetUpTest(c *gc.C) { s.baseProviderSuite.SetUpTest(c) // Construct the directories first. err := local.CreateDirs(c, minimalConfig(c)) c.Assert(err, gc.IsNil) s.testPath = c.MkDir() s.fakesudo = filepath.Join(s.testPath, "sudo") s.PatchEnvPathPrepend(s.testPath) // Write a fake "sudo" which records its args to sudo.args. err = ioutil.WriteFile(s.fakesudo, []byte(echoCommandScript), 0755) c.Assert(err, gc.IsNil) // Add in an admin secret s.Tests.TestConfig["admin-secret"] = "sekrit" s.PatchValue(local.CheckIfRoot, func() bool { return false }) s.Tests.SetUpTest(c) cfg, err := config.New(config.NoDefaults, s.TestConfig) c.Assert(err, gc.IsNil) s.dbServiceName = "juju-db-" + local.ConfigNamespace(cfg) s.PatchValue(local.FinishBootstrap, func(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config, ctx environs.BootstrapContext) error { return nil }) } func (s *localJujuTestSuite) TearDownTest(c *gc.C) { s.Tests.TearDownTest(c) s.baseProviderSuite.TearDownTest(c) } func (s *localJujuTestSuite) MakeTool(c *gc.C, name, script string) { path := filepath.Join(s.testPath, name) script = "#!/bin/bash\n" + script err := ioutil.WriteFile(path, []byte(script), 0755) c.Assert(err, gc.IsNil) } func (s *localJujuTestSuite) StoppedStatus(c *gc.C) { s.MakeTool(c, "status", `echo "some-service stop/waiting"`) } func (s *localJujuTestSuite) RunningStatus(c *gc.C) { s.MakeTool(c, "status", `echo "some-service start/running, process 123"`) } var _ = gc.Suite(&localJujuTestSuite{ Tests: jujutest.Tests{ TestConfig: minimalConfigValues(), }, }) func (s *localJujuTestSuite) TestStartStop(c *gc.C) { c.Skip("StartInstance not implemented yet.") } func (s *localJujuTestSuite) testBootstrap(c *gc.C, cfg *config.Config) (env environs.Environ) { ctx := coretesting.Context(c) environ, err := local.Provider.Prepare(ctx, cfg) c.Assert(err, gc.IsNil) envtesting.UploadFakeTools(c, environ.Storage()) defer environ.Storage().RemoveAll() err = environ.Bootstrap(ctx, constraints.Value{}) c.Assert(err, gc.IsNil) return environ } func (s *localJujuTestSuite) TestBootstrap(c *gc.C) { s.PatchValue(local.FinishBootstrap, func(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config, ctx environs.BootstrapContext) error { c.Assert(cloudcfg.AptUpdate(), jc.IsFalse) c.Assert(cloudcfg.AptUpgrade(), jc.IsFalse) c.Assert(cloudcfg.Packages(), gc.HasLen, 0) c.Assert(mcfg.AgentEnvironment, gc.Not(gc.IsNil)) // local does not allow machine-0 to host units c.Assert(mcfg.Jobs, gc.DeepEquals, []params.MachineJob{params.JobManageEnviron}) return nil }) s.testBootstrap(c, minimalConfig(c)) } func (s *localJujuTestSuite) TestDestroy(c *gc.C) { env := s.testBootstrap(c, minimalConfig(c)) err := env.Destroy() // Succeeds because there's no "agents" directory, // so destroy will just return without attempting // sudo or anything. c.Assert(err, gc.IsNil) c.Assert(s.fakesudo+".args", jc.DoesNotExist) } func (s *localJujuTestSuite) makeAgentsDir(c *gc.C, env environs.Environ) { rootDir := env.Config().AllAttrs()["root-dir"].(string) agentsDir := filepath.Join(rootDir, "agents") err := os.Mkdir(agentsDir, 0755) c.Assert(err, gc.IsNil) } func (s *localJujuTestSuite) TestDestroyCallSudo(c *gc.C) { env := s.testBootstrap(c, minimalConfig(c)) s.makeAgentsDir(c, env) err := env.Destroy() c.Assert(err, gc.IsNil) data, err := ioutil.ReadFile(s.fakesudo + ".args") c.Assert(err, gc.IsNil) expected := []string{ s.fakesudo, "env", "JUJU_HOME=" + osenv.JujuHome(), os.Args[0], "destroy-environment", "-y", "--force", env.Config().Name(), } c.Assert(string(data), gc.Equals, strings.Join(expected, " ")+"\n") } func (s *localJujuTestSuite) makeFakeUpstartScripts(c *gc.C, env environs.Environ, ) (mongo *upstart.Service, machineAgent *upstart.Service) { upstartDir := c.MkDir() s.PatchValue(&upstart.InitDir, upstartDir) s.MakeTool(c, "start", `echo "some-service start/running, process 123"`) namespace := env.Config().AllAttrs()["namespace"].(string) mongo = upstart.NewService(fmt.Sprintf("juju-db-%s", namespace)) mongoConf := upstart.Conf{ Service: *mongo, Desc: "fake mongo", Cmd: "echo FAKE", } err := mongoConf.Install() c.Assert(err, gc.IsNil) c.Assert(mongo.Installed(), jc.IsTrue) machineAgent = upstart.NewService(fmt.Sprintf("juju-agent-%s", namespace)) agentConf := upstart.Conf{ Service: *machineAgent, Desc: "fake agent", Cmd: "echo FAKE", } err = agentConf.Install() c.Assert(err, gc.IsNil) c.Assert(machineAgent.Installed(), jc.IsTrue) return mongo, machineAgent } func (s *localJujuTestSuite) TestDestroyRemovesUpstartServices(c *gc.C) { env := s.testBootstrap(c, minimalConfig(c)) s.makeAgentsDir(c, env) mongo, machineAgent := s.makeFakeUpstartScripts(c, env) s.PatchValue(local.CheckIfRoot, func() bool { return true }) err := env.Destroy() c.Assert(err, gc.IsNil) c.Assert(mongo.Installed(), jc.IsFalse) c.Assert(machineAgent.Installed(), jc.IsFalse) } func (s *localJujuTestSuite) TestDestroyRemovesContainers(c *gc.C) { env := s.testBootstrap(c, minimalConfig(c)) s.makeAgentsDir(c, env) s.PatchValue(local.CheckIfRoot, func() bool { return true }) namespace := env.Config().AllAttrs()["namespace"].(string) manager, err := lxc.NewContainerManager(container.ManagerConfig{ container.ConfigName: namespace, container.ConfigLogDir: "logdir", }) c.Assert(err, gc.IsNil) machine1 := containertesting.CreateContainer(c, manager, "1") err = env.Destroy() c.Assert(err, gc.IsNil) container := s.Factory.New(string(machine1.Id())) c.Assert(container.IsConstructed(), jc.IsFalse) } func (s *localJujuTestSuite) TestBootstrapRemoveLeftovers(c *gc.C) { cfg := minimalConfig(c) rootDir := cfg.AllAttrs()["root-dir"].(string) // Create a dir inside local/log that should be removed by Bootstrap. logThings := filepath.Join(rootDir, "log", "things") err := os.MkdirAll(logThings, 0755) c.Assert(err, gc.IsNil) // Create a cloud-init-output.log in root-dir that should be // removed/truncated by Bootstrap. cloudInitOutputLog := filepath.Join(rootDir, "cloud-init-output.log") err = ioutil.WriteFile(cloudInitOutputLog, []byte("ohai"), 0644) c.Assert(err, gc.IsNil) s.testBootstrap(c, cfg) c.Assert(logThings, jc.DoesNotExist) c.Assert(cloudInitOutputLog, jc.DoesNotExist) c.Assert(filepath.Join(rootDir, "log"), jc.IsSymlink) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/���������������������������������������0000755�0000153�0000161�00000000000�12321736000�023774� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/export_test.go�������������������������0000644�0000153�0000161�00000000436�12321735642�026721� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common var ( GetDNSNames = getDNSNames GetStateInfo = getStateInfo ComposeAddresses = composeAddresses ConnectSSH = &connectSSH WaitSSH = waitSSH ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/mock_test.go���������������������������0000644�0000153�0000161�00000006331�12321735776�026341� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "io" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/tools" ) type allInstancesFunc func() ([]instance.Instance, error) type startInstanceFunc func(constraints.Value, environs.Networks, tools.List, *cloudinit.MachineConfig) (instance.Instance, *instance.HardwareCharacteristics, error) type stopInstancesFunc func([]instance.Instance) error type getToolsSourcesFunc func() ([]simplestreams.DataSource, error) type configFunc func() *config.Config type setConfigFunc func(*config.Config) error type mockEnviron struct { storage storage.Storage allInstances allInstancesFunc startInstance startInstanceFunc stopInstances stopInstancesFunc getToolsSources getToolsSourcesFunc config configFunc setConfig setConfigFunc environs.Environ // stub out other methods with panics } func (*mockEnviron) Name() string { return "mock environment" } func (*mockEnviron) SupportedArchitectures() ([]string, error) { return []string{"amd64", "arm64"}, nil } func (env *mockEnviron) Storage() storage.Storage { return env.storage } func (env *mockEnviron) AllInstances() ([]instance.Instance, error) { return env.allInstances() } func (env *mockEnviron) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { return env.startInstance(args.Constraints, args.Networks, args.Tools, args.MachineConfig) } func (env *mockEnviron) StopInstances(instances []instance.Instance) error { return env.stopInstances(instances) } func (env *mockEnviron) Config() *config.Config { return env.config() } func (env *mockEnviron) SetConfig(cfg *config.Config) error { if env.setConfig != nil { return env.setConfig(cfg) } return nil } func (env *mockEnviron) GetToolsSources() ([]simplestreams.DataSource, error) { if env.getToolsSources != nil { return env.getToolsSources() } datasource := storage.NewStorageSimpleStreamsDataSource("test cloud storage", env.Storage(), storage.BaseToolsPath) return []simplestreams.DataSource{datasource}, nil } func (env *mockEnviron) GetImageSources() ([]simplestreams.DataSource, error) { datasource := storage.NewStorageSimpleStreamsDataSource("test cloud storage", env.Storage(), storage.BaseImagesPath) return []simplestreams.DataSource{datasource}, nil } type mockInstance struct { id string instance.Instance // stub out other methods with panics } func (inst *mockInstance) Id() instance.Id { return instance.Id(inst.id) } type mockStorage struct { storage.Storage putErr error removeAllErr error } func (stor *mockStorage) Put(name string, reader io.Reader, size int64) error { if stor.putErr != nil { return stor.putErr } return stor.Storage.Put(name, reader, size) } func (stor *mockStorage) RemoveAll() error { if stor.removeAllErr != nil { return stor.removeAllErr } return stor.Storage.RemoveAll() } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/bootstrap_test.go����������������������0000644�0000153�0000161�00000025157�12321735776�027434� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" "os" "time" "github.com/juju/loggo" "github.com/juju/testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/storage" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils/ssh" ) type BootstrapSuite struct { testbase.LoggingSuite envtesting.ToolsFixture } var _ = gc.Suite(&BootstrapSuite{}) type cleaner interface { AddCleanup(testing.CleanupFunc) } func (s *BootstrapSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) s.PatchValue(common.ConnectSSH, func(_ ssh.Client, host, checkHostScript string) error { return fmt.Errorf("mock connection failure to %s", host) }) } func (s *BootstrapSuite) TearDownTest(c *gc.C) { s.ToolsFixture.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func newStorage(suite cleaner, c *gc.C) storage.Storage { closer, stor, _ := envtesting.CreateLocalTestStorage(c) suite.AddCleanup(func(*gc.C) { closer.Close() }) envtesting.UploadFakeTools(c, stor) return stor } func minimalConfig(c *gc.C) *config.Config { attrs := map[string]interface{}{ "name": "whatever", "type": "anything, really", "ca-cert": coretesting.CACert, "ca-private-key": coretesting.CAKey, } cfg, err := config.New(config.UseDefaults, attrs) c.Assert(err, gc.IsNil) return cfg } func configGetter(c *gc.C) configFunc { cfg := minimalConfig(c) return func() *config.Config { return cfg } } func (s *BootstrapSuite) TestCannotStartInstance(c *gc.C) { checkCons := constraints.MustParse("mem=8G") startInstance := func( cons constraints.Value, _ environs.Networks, possibleTools tools.List, mcfg *cloudinit.MachineConfig, ) ( instance.Instance, *instance.HardwareCharacteristics, error, ) { c.Assert(cons, gc.DeepEquals, checkCons) c.Assert(mcfg, gc.DeepEquals, environs.NewBootstrapMachineConfig(mcfg.SystemPrivateSSHKey)) return nil, nil, fmt.Errorf("meh, not started") } env := &mockEnviron{ storage: newStorage(s, c), startInstance: startInstance, config: configGetter(c), } ctx := coretesting.Context(c) err := common.Bootstrap(ctx, env, checkCons) c.Assert(err, gc.ErrorMatches, "cannot start bootstrap instance: meh, not started") } func (s *BootstrapSuite) TestCannotRecordStartedInstance(c *gc.C) { innerStorage := newStorage(s, c) stor := &mockStorage{Storage: innerStorage} startInstance := func( _ constraints.Value, _ environs.Networks, _ tools.List, _ *cloudinit.MachineConfig, ) ( instance.Instance, *instance.HardwareCharacteristics, error, ) { stor.putErr = fmt.Errorf("suddenly a wild blah") return &mockInstance{id: "i-blah"}, nil, nil } var stopped []instance.Instance stopInstances := func(instances []instance.Instance) error { stopped = append(stopped, instances...) return nil } env := &mockEnviron{ storage: stor, startInstance: startInstance, stopInstances: stopInstances, config: configGetter(c), } ctx := coretesting.Context(c) err := common.Bootstrap(ctx, env, constraints.Value{}) c.Assert(err, gc.ErrorMatches, "cannot save state: suddenly a wild blah") c.Assert(stopped, gc.HasLen, 1) c.Assert(stopped[0].Id(), gc.Equals, instance.Id("i-blah")) } func (s *BootstrapSuite) TestCannotRecordThenCannotStop(c *gc.C) { innerStorage := newStorage(s, c) stor := &mockStorage{Storage: innerStorage} startInstance := func( _ constraints.Value, _ environs.Networks, _ tools.List, _ *cloudinit.MachineConfig, ) ( instance.Instance, *instance.HardwareCharacteristics, error, ) { stor.putErr = fmt.Errorf("suddenly a wild blah") return &mockInstance{id: "i-blah"}, nil, nil } var stopped []instance.Instance stopInstances := func(instances []instance.Instance) error { stopped = append(stopped, instances...) return fmt.Errorf("bork bork borken") } tw := &loggo.TestWriter{} c.Assert(loggo.RegisterWriter("bootstrap-tester", tw, loggo.DEBUG), gc.IsNil) defer loggo.RemoveWriter("bootstrap-tester") env := &mockEnviron{ storage: stor, startInstance: startInstance, stopInstances: stopInstances, config: configGetter(c), } ctx := coretesting.Context(c) err := common.Bootstrap(ctx, env, constraints.Value{}) c.Assert(err, gc.ErrorMatches, "cannot save state: suddenly a wild blah") c.Assert(stopped, gc.HasLen, 1) c.Assert(stopped[0].Id(), gc.Equals, instance.Id("i-blah")) c.Assert(tw.Log, jc.LogMatches, []jc.SimpleMessage{{ loggo.ERROR, `cannot stop failed bootstrap instance "i-blah": bork bork borken`, }}) } func (s *BootstrapSuite) TestSuccess(c *gc.C) { stor := newStorage(s, c) checkInstanceId := "i-success" checkHardware := instance.MustParseHardware("mem=2T") startInstance := func( _ constraints.Value, _ environs.Networks, _ tools.List, mcfg *cloudinit.MachineConfig, ) ( instance.Instance, *instance.HardwareCharacteristics, error, ) { return &mockInstance{id: checkInstanceId}, &checkHardware, nil } var mocksConfig = minimalConfig(c) var getConfigCalled int getConfig := func() *config.Config { getConfigCalled++ return mocksConfig } setConfig := func(c *config.Config) error { mocksConfig = c return nil } restore := envtesting.DisableFinishBootstrap() defer restore() env := &mockEnviron{ storage: stor, startInstance: startInstance, config: getConfig, setConfig: setConfig, } originalAuthKeys := env.Config().AuthorizedKeys() ctx := coretesting.Context(c) err := common.Bootstrap(ctx, env, constraints.Value{}) c.Assert(err, gc.IsNil) authKeys := env.Config().AuthorizedKeys() c.Assert(authKeys, gc.Not(gc.Equals), originalAuthKeys) c.Assert(authKeys, jc.HasSuffix, "juju-system-key\n") } type neverRefreshes struct { } func (neverRefreshes) Refresh() error { return nil } type neverAddresses struct { neverRefreshes } func (neverAddresses) Addresses() ([]instance.Address, error) { return nil, nil } var testSSHTimeout = config.SSHTimeoutOpts{ Timeout: coretesting.ShortWait, RetryDelay: 1 * time.Millisecond, AddressesDelay: 1 * time.Millisecond, } func (s *BootstrapSuite) TestWaitSSHTimesOutWaitingForAddresses(c *gc.C) { ctx := coretesting.Context(c) _, err := common.WaitSSH(ctx, nil, ssh.DefaultClient, "/bin/true", neverAddresses{}, testSSHTimeout) c.Check(err, gc.ErrorMatches, `waited for `+testSSHTimeout.Timeout.String()+` without getting any addresses`) c.Check(coretesting.Stderr(ctx), gc.Matches, "Waiting for address\n") } func (s *BootstrapSuite) TestWaitSSHKilledWaitingForAddresses(c *gc.C) { ctx := coretesting.Context(c) interrupted := make(chan os.Signal, 1) go func() { <-time.After(2 * time.Millisecond) interrupted <- os.Interrupt }() _, err := common.WaitSSH(ctx, interrupted, ssh.DefaultClient, "/bin/true", neverAddresses{}, testSSHTimeout) c.Check(err, gc.ErrorMatches, "interrupted") c.Check(coretesting.Stderr(ctx), gc.Matches, "Waiting for address\n") } type brokenAddresses struct { neverRefreshes } func (brokenAddresses) Addresses() ([]instance.Address, error) { return nil, fmt.Errorf("Addresses will never work") } func (s *BootstrapSuite) TestWaitSSHStopsOnBadError(c *gc.C) { ctx := coretesting.Context(c) _, err := common.WaitSSH(ctx, nil, ssh.DefaultClient, "/bin/true", brokenAddresses{}, testSSHTimeout) c.Check(err, gc.ErrorMatches, "getting addresses: Addresses will never work") c.Check(coretesting.Stderr(ctx), gc.Equals, "Waiting for address\n") } type neverOpensPort struct { neverRefreshes addr string } func (n *neverOpensPort) Addresses() ([]instance.Address, error) { return []instance.Address{instance.NewAddress(n.addr)}, nil } func (s *BootstrapSuite) TestWaitSSHTimesOutWaitingForDial(c *gc.C) { ctx := coretesting.Context(c) // 0.x.y.z addresses are always invalid _, err := common.WaitSSH(ctx, nil, ssh.DefaultClient, "/bin/true", &neverOpensPort{addr: "0.1.2.3"}, testSSHTimeout) c.Check(err, gc.ErrorMatches, `waited for `+testSSHTimeout.Timeout.String()+` without being able to connect: mock connection failure to 0.1.2.3`) c.Check(coretesting.Stderr(ctx), gc.Matches, "Waiting for address\n"+ "(Attempting to connect to 0.1.2.3:22\n)+") } type interruptOnDial struct { neverRefreshes name string interrupted chan os.Signal returned bool } func (i *interruptOnDial) Addresses() ([]instance.Address, error) { // kill the tomb the second time Addresses is called if !i.returned { i.returned = true } else { i.interrupted <- os.Interrupt } return []instance.Address{instance.NewAddress(i.name)}, nil } func (s *BootstrapSuite) TestWaitSSHKilledWaitingForDial(c *gc.C) { ctx := coretesting.Context(c) timeout := testSSHTimeout timeout.Timeout = 1 * time.Minute interrupted := make(chan os.Signal, 1) _, err := common.WaitSSH(ctx, interrupted, ssh.DefaultClient, "", &interruptOnDial{name: "0.1.2.3", interrupted: interrupted}, timeout) c.Check(err, gc.ErrorMatches, "interrupted") // Exact timing is imprecise but it should have tried a few times before being killed c.Check(coretesting.Stderr(ctx), gc.Matches, "Waiting for address\n"+ "(Attempting to connect to 0.1.2.3:22\n)+") } type addressesChange struct { addrs [][]string } func (ac *addressesChange) Refresh() error { if len(ac.addrs) > 1 { ac.addrs = ac.addrs[1:] } return nil } func (ac *addressesChange) Addresses() ([]instance.Address, error) { var addrs []instance.Address for _, addr := range ac.addrs[0] { addrs = append(addrs, instance.NewAddress(addr)) } return addrs, nil } func (s *BootstrapSuite) TestWaitSSHRefreshAddresses(c *gc.C) { ctx := coretesting.Context(c) _, err := common.WaitSSH(ctx, nil, ssh.DefaultClient, "", &addressesChange{addrs: [][]string{ nil, nil, []string{"0.1.2.3"}, []string{"0.1.2.3"}, nil, []string{"0.1.2.4"}, }}, testSSHTimeout) // Not necessarily the last one in the list, due to scheduling. c.Check(err, gc.ErrorMatches, `waited for `+testSSHTimeout.Timeout.String()+` without being able to connect: mock connection failure to 0.1.2.[34]`) stderr := coretesting.Stderr(ctx) c.Check(stderr, gc.Matches, "Waiting for address\n"+ "(.|\n)*(Attempting to connect to 0.1.2.3:22\n)+(.|\n)*") c.Check(stderr, gc.Matches, "Waiting for address\n"+ "(.|\n)*(Attempting to connect to 0.1.2.4:22\n)+(.|\n)*") } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/state.go�������������������������������0000644�0000153�0000161�00000005512�12321735642�025461� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "errors" "fmt" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/log" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" ) // getDNSNames queries and returns the DNS names for the given instances, // ignoring nil instances or ones without DNS names. func getDNSNames(instances []instance.Instance) []string { names := make([]string, 0) for _, inst := range instances { if inst != nil { name, err := inst.DNSName() // If that fails, just keep looking. if err == nil && name != "" { names = append(names, name) } } } return names } // composeAddresses suffixes each of a slice of hostnames with a given port // number. func composeAddresses(hostnames []string, port int) []string { addresses := make([]string, len(hostnames)) for index, hostname := range hostnames { addresses[index] = fmt.Sprintf("%s:%d", hostname, port) } return addresses } // composeStateInfo puts together the state.Info and api.Info for the given // config, with the given state-server host names. // The given config absolutely must have a CACert. func getStateInfo(config *config.Config, hostnames []string) (*state.Info, *api.Info) { cert, hasCert := config.CACert() if !hasCert { panic(errors.New("getStateInfo: config has no CACert")) } return &state.Info{ Addrs: composeAddresses(hostnames, config.StatePort()), CACert: cert, }, &api.Info{ Addrs: composeAddresses(hostnames, config.APIPort()), CACert: cert, } } // StateInfo is a reusable implementation of Environ.StateInfo, available to // providers that also use the other functionality from this file. func StateInfo(env environs.Environ) (*state.Info, *api.Info, error) { st, err := bootstrap.LoadState(env.Storage()) if err != nil { return nil, nil, err } config := env.Config() if _, hasCert := config.CACert(); !hasCert { return nil, nil, fmt.Errorf("no CA certificate in environment configuration") } // Wait for the DNS names of any of the instances // to become available. log.Debugf("waiting for DNS name(s) of state server instances %v", st.StateInstances) var hostnames []string for a := LongAttempt.Start(); len(hostnames) == 0 && a.Next(); { insts, err := env.Instances(st.StateInstances) if err != nil && err != environs.ErrPartialInstances { log.Debugf("error getting state instances: %v", err.Error()) return nil, nil, err } hostnames = getDNSNames(insts) } if len(hostnames) == 0 { return nil, nil, fmt.Errorf("timed out waiting for mgo address from %v", st.StateInstances) } stateInfo, apiInfo := getStateInfo(config, hostnames) return stateInfo, apiInfo, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/polling.go�����������������������������0000644�0000153�0000161�00000002456�12321735642�026011� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "fmt" "time" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/utils" ) // Use ShortAttempt to poll for short-term events. // TODO: This may need tuning for different providers (or even environments). var ShortAttempt = utils.AttemptStrategy{ Total: 5 * time.Second, Delay: 200 * time.Millisecond, } // A request may fail to due "eventual consistency" semantics, which // should resolve fairly quickly. These delays are specific to the provider // and best tuned there. // Other requests fail due to a slow state transition (e.g. an instance taking // a while to release a security group after termination). If you need to // poll for the latter kind, use LongAttempt. var LongAttempt = utils.AttemptStrategy{ Total: 3 * time.Minute, Delay: 1 * time.Second, } // WaitDNSName is an implementation that the providers can use. It builds on // the provider's implementation of Instance.DNSName. func WaitDNSName(inst instance.Instance) (string, error) { for a := LongAttempt.Start(); a.Next(); { name, err := inst.DNSName() if err == nil || err != instance.ErrNoDNSName { return name, err } } return "", fmt.Errorf("timed out trying to get DNS address for %v", inst.Id()) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/bootstrap.go���������������������������0000644�0000153�0000161�00000032023�12321735776�026363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "fmt" "io" "os" "path" "strings" "time" "github.com/juju/loggo" coreCloudinit "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/cloudinit/sshinit" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/cloudinit" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" coretools "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/parallel" "launchpad.net/juju-core/utils/shell" "launchpad.net/juju-core/utils/ssh" ) var logger = loggo.GetLogger("juju.provider.common") // Bootstrap is a common implementation of the Bootstrap method defined on // environs.Environ; we strongly recommend that this implementation be used // when writing a new provider. func Bootstrap(ctx environs.BootstrapContext, env environs.Environ, cons constraints.Value) (err error) { // TODO make safe in the case of racing Bootstraps // If two Bootstraps are called concurrently, there's // no way to make sure that only one succeeds. var inst instance.Instance defer func() { handleBootstrapError(err, ctx, inst, env) }() // First thing, ensure we have tools otherwise there's no point. selectedTools, err := EnsureBootstrapTools(ctx, env, config.PreferredSeries(env.Config()), cons.Arch) if err != nil { return err } // Get the bootstrap SSH client. Do this early, so we know // not to bother with any of the below if we can't finish the job. client := ssh.DefaultClient if client == nil { // This should never happen: if we don't have OpenSSH, then // go.crypto/ssh should be used with an auto-generated key. return fmt.Errorf("no SSH client available") } privateKey, err := GenerateSystemSSHKey(env) if err != nil { return err } machineConfig := environs.NewBootstrapMachineConfig(privateKey) fmt.Fprintln(ctx.GetStderr(), "Launching instance") inst, hw, err := env.StartInstance(environs.StartInstanceParams{ Constraints: cons, Tools: selectedTools, MachineConfig: machineConfig, }) if err != nil { return fmt.Errorf("cannot start bootstrap instance: %v", err) } fmt.Fprintf(ctx.GetStderr(), " - %s\n", inst.Id()) machineConfig.InstanceId = inst.Id() machineConfig.HardwareCharacteristics = hw var characteristics []instance.HardwareCharacteristics if hw != nil { characteristics = []instance.HardwareCharacteristics{*hw} } err = bootstrap.SaveState( env.Storage(), &bootstrap.BootstrapState{ StateInstances: []instance.Id{inst.Id()}, Characteristics: characteristics, }) if err != nil { return fmt.Errorf("cannot save state: %v", err) } return FinishBootstrap(ctx, client, inst, machineConfig) } // GenerateSystemSSHKey creates a new key for the system identity. The // authorized_keys in the environment config is updated to include the public // key for the generated key. func GenerateSystemSSHKey(env environs.Environ) (privateKey string, err error) { logger.Debugf("generate a system ssh key") // Create a new system ssh key and add that to the authorized keys. privateKey, publicKey, err := ssh.GenerateKey(config.JujuSystemKey) if err != nil { return "", fmt.Errorf("failed to create system key: %v", err) } authorized_keys := config.ConcatAuthKeys(env.Config().AuthorizedKeys(), publicKey) newConfig, err := env.Config().Apply(map[string]interface{}{ config.AuthKeysConfig: authorized_keys, }) if err != nil { return "", fmt.Errorf("failed to create new config: %v", err) } if err = env.SetConfig(newConfig); err != nil { return "", fmt.Errorf("failed to set new config: %v", err) } return privateKey, nil } // handelBootstrapError cleans up after a failed bootstrap. func handleBootstrapError(err error, ctx environs.BootstrapContext, inst instance.Instance, env environs.Environ) { if err == nil { return } logger.Errorf("bootstrap failed: %v", err) ch := make(chan os.Signal, 1) ctx.InterruptNotify(ch) defer ctx.StopInterruptNotify(ch) defer close(ch) go func() { for _ = range ch { fmt.Fprintln(ctx.GetStderr(), "Cleaning up failed bootstrap") } }() if inst != nil { fmt.Fprintln(ctx.GetStderr(), "Stopping instance...") if stoperr := env.StopInstances([]instance.Instance{inst}); stoperr != nil { logger.Errorf("cannot stop failed bootstrap instance %q: %v", inst.Id(), stoperr) } else { // set to nil so we know we can safely delete the state file inst = nil } } // We only delete the bootstrap state file if either we didn't // start an instance, or we managed to cleanly stop it. if inst == nil { if rmerr := bootstrap.DeleteStateFile(env.Storage()); rmerr != nil { logger.Errorf("cannot delete bootstrap state file: %v", rmerr) } } } // FinishBootstrap completes the bootstrap process by connecting // to the instance via SSH and carrying out the cloud-config. // // Note: FinishBootstrap is exposed so it can be replaced for testing. var FinishBootstrap = func(ctx environs.BootstrapContext, client ssh.Client, inst instance.Instance, machineConfig *cloudinit.MachineConfig) error { interrupted := make(chan os.Signal, 1) ctx.InterruptNotify(interrupted) defer ctx.StopInterruptNotify(interrupted) // Each attempt to connect to an address must verify the machine is the // bootstrap machine by checking its nonce file exists and contains the // nonce in the MachineConfig. This also blocks sshinit from proceeding // until cloud-init has completed, which is necessary to ensure apt // invocations don't trample each other. nonceFile := utils.ShQuote(path.Join(machineConfig.DataDir, cloudinit.NonceFile)) checkNonceCommand := fmt.Sprintf(` noncefile=%s if [ ! -e "$noncefile" ]; then echo "$noncefile does not exist" >&2 exit 1 fi content=$(cat $noncefile) if [ "$content" != %s ]; then echo "$noncefile contents do not match machine nonce" >&2 exit 1 fi `, nonceFile, utils.ShQuote(machineConfig.MachineNonce)) addr, err := waitSSH( ctx, interrupted, client, checkNonceCommand, inst, machineConfig.Config.BootstrapSSHOpts(), ) if err != nil { return err } // Bootstrap is synchronous, and will spawn a subprocess // to complete the procedure. If the user hits Ctrl-C, // SIGINT is sent to the foreground process attached to // the terminal, which will be the ssh subprocess at this // point. For that reason, we do not call StopInterruptNotify // until this function completes. cloudcfg := coreCloudinit.New() if err := cloudinit.ConfigureJuju(machineConfig, cloudcfg); err != nil { return err } configScript, err := sshinit.ConfigureScript(cloudcfg) if err != nil { return err } script := shell.DumpFileOnErrorScript(machineConfig.CloudInitOutputLog) + configScript return sshinit.RunConfigureScript(script, sshinit.ConfigureParams{ Host: "ubuntu@" + addr, Client: client, Config: cloudcfg, ProgressWriter: ctx.GetStderr(), }) } type addresser interface { // Refresh refreshes the addresses for the instance. Refresh() error // Addresses returns the addresses for the instance. // To ensure that the results are up to date, call // Refresh first. Addresses() ([]instance.Address, error) } type hostChecker struct { addr instance.Address client ssh.Client // checkDelay is the amount of time to wait between retries. checkDelay time.Duration // checkHostScript is executed on the host via SSH. // hostChecker.loop will return once the script // runs without error. checkHostScript string // closed is closed to indicate that the host checker should // return, without waiting for the result of any ongoing // attempts. closed <-chan struct{} } // Close implements io.Closer, as required by parallel.Try. func (*hostChecker) Close() error { return nil } func (hc *hostChecker) loop(dying <-chan struct{}) (io.Closer, error) { // The value of connectSSH is taken outside the goroutine that may outlive // hostChecker.loop, or we evoke the wrath of the race detector. connectSSH := connectSSH done := make(chan error, 1) var lastErr error for { go func() { done <- connectSSH(hc.client, hc.addr.Value, hc.checkHostScript) }() select { case <-hc.closed: return hc, lastErr case <-dying: return hc, lastErr case lastErr = <-done: if lastErr == nil { return hc, nil } } select { case <-hc.closed: case <-dying: case <-time.After(hc.checkDelay): } } } type parallelHostChecker struct { *parallel.Try client ssh.Client stderr io.Writer // active is a map of adresses to channels for addresses actively // being tested. The goroutine testing the address will continue // to attempt connecting to the address until it succeeds, the Try // is killed, or the corresponding channel in this map is closed. active map[instance.Address]chan struct{} // checkDelay is how long each hostChecker waits between attempts. checkDelay time.Duration // checkHostScript is the script to run on each host to check that // it is the host we expect. checkHostScript string } func (p *parallelHostChecker) UpdateAddresses(addrs []instance.Address) { for _, addr := range addrs { if _, ok := p.active[addr]; ok { continue } fmt.Fprintf(p.stderr, "Attempting to connect to %s:22\n", addr.Value) closed := make(chan struct{}) hc := &hostChecker{ addr: addr, client: p.client, checkDelay: p.checkDelay, checkHostScript: p.checkHostScript, closed: closed, } p.active[addr] = closed p.Start(hc.loop) } } // Close prevents additional functions from being added to // the Try, and tells each active hostChecker to exit. func (p *parallelHostChecker) Close() error { // We signal each checker to stop and wait for them // each to complete; this allows us to get the error, // as opposed to when using try.Kill which does not // wait for the functions to complete. p.Try.Close() for _, ch := range p.active { close(ch) } return nil } // connectSSH is called to connect to the specified host and // execute the "checkHostScript" bash script on it. var connectSSH = func(client ssh.Client, host, checkHostScript string) error { cmd := client.Command("ubuntu@"+host, []string{"/bin/bash"}, nil) cmd.Stdin = strings.NewReader(checkHostScript) output, err := cmd.CombinedOutput() if err != nil && len(output) > 0 { err = fmt.Errorf("%s", strings.TrimSpace(string(output))) } return err } // waitSSH waits for the instance to be assigned a routable // address, then waits until we can connect to it via SSH. // // waitSSH attempts on all addresses returned by the instance // in parallel; the first succeeding one wins. We ensure that // private addresses are for the correct machine by checking // the presence of a file on the machine that contains the // machine's nonce. The "checkHostScript" is a bash script // that performs this file check. func waitSSH(ctx environs.BootstrapContext, interrupted <-chan os.Signal, client ssh.Client, checkHostScript string, inst addresser, timeout config.SSHTimeoutOpts) (addr string, err error) { globalTimeout := time.After(timeout.Timeout) pollAddresses := time.NewTimer(0) // checker checks each address in a loop, in parallel, // until one succeeds, the global timeout is reached, // or the tomb is killed. checker := parallelHostChecker{ Try: parallel.NewTry(0, nil), client: client, stderr: ctx.GetStderr(), active: make(map[instance.Address]chan struct{}), checkDelay: timeout.RetryDelay, checkHostScript: checkHostScript, } defer checker.Kill() fmt.Fprintln(ctx.GetStderr(), "Waiting for address") for { select { case <-pollAddresses.C: pollAddresses.Reset(timeout.AddressesDelay) if err := inst.Refresh(); err != nil { return "", fmt.Errorf("refreshing addresses: %v", err) } addresses, err := inst.Addresses() if err != nil { return "", fmt.Errorf("getting addresses: %v", err) } checker.UpdateAddresses(addresses) case <-globalTimeout: checker.Close() lastErr := checker.Wait() format := "waited for %v " args := []interface{}{timeout.Timeout} if len(checker.active) == 0 { format += "without getting any addresses" } else { format += "without being able to connect" } if lastErr != nil && lastErr != parallel.ErrStopped { format += ": %v" args = append(args, lastErr) } return "", fmt.Errorf(format, args...) case <-interrupted: return "", fmt.Errorf("interrupted") case <-checker.Dead(): result, err := checker.Result() if err != nil { return "", err } return result.(*hostChecker).addr.Value, nil } } } // EnsureBootstrapTools finds tools, syncing with an external tools source as // necessary; it then selects the newest tools to bootstrap with, and sets // agent-version. func EnsureBootstrapTools(ctx environs.BootstrapContext, env environs.Environ, series string, arch *string) (coretools.List, error) { possibleTools, err := bootstrap.EnsureToolsAvailability(ctx, env, series, arch) if err != nil { return nil, err } return bootstrap.SetBootstrapTools(env, possibleTools) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/destroy.go�����������������������������0000644�0000153�0000161�00000001234�12321735642�026027� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/environs" ) // Destroy is a common implementation of the Destroy method defined on // environs.Environ; we strongly recommend that this implementation be // used when writing a new provider. func Destroy(env environs.Environ) error { logger.Infof("destroying environment %q", env.Name()) instances, err := env.AllInstances() switch err { case nil: if err := env.StopInstances(instances); err != nil { return err } fallthrough case environs.ErrNoInstances: return env.Storage().RemoveAll() } return err } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/destroy_test.go������������������������0000644�0000153�0000161�00000006452�12321735642�027075� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "fmt" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/testing/testbase" ) type DestroySuite struct { testbase.LoggingSuite } var _ = gc.Suite(&DestroySuite{}) func (s *DestroySuite) TestCannotGetInstances(c *gc.C) { env := &mockEnviron{ allInstances: func() ([]instance.Instance, error) { return nil, fmt.Errorf("nope") }, } err := common.Destroy(env) c.Assert(err, gc.ErrorMatches, "nope") } func (s *DestroySuite) TestCannotStopInstances(c *gc.C) { env := &mockEnviron{ allInstances: func() ([]instance.Instance, error) { return []instance.Instance{ &mockInstance{id: "one"}, &mockInstance{id: "another"}, }, nil }, stopInstances: func(instances []instance.Instance) error { c.Assert(instances, gc.HasLen, 2) c.Assert(instances[0].Id(), gc.Equals, instance.Id("one")) c.Assert(instances[1].Id(), gc.Equals, instance.Id("another")) return fmt.Errorf("nah") }, } err := common.Destroy(env) c.Assert(err, gc.ErrorMatches, "nah") } func (s *DestroySuite) TestCannotTrashStorage(c *gc.C) { env := &mockEnviron{ storage: &mockStorage{removeAllErr: fmt.Errorf("noes!")}, allInstances: func() ([]instance.Instance, error) { return []instance.Instance{ &mockInstance{id: "one"}, &mockInstance{id: "another"}, }, nil }, stopInstances: func(instances []instance.Instance) error { c.Assert(instances, gc.HasLen, 2) c.Assert(instances[0].Id(), gc.Equals, instance.Id("one")) c.Assert(instances[1].Id(), gc.Equals, instance.Id("another")) return nil }, } err := common.Destroy(env) c.Assert(err, gc.ErrorMatches, "noes!") } func (s *DestroySuite) TestSuccess(c *gc.C) { stor := newStorage(s, c) err := stor.Put("somewhere", strings.NewReader("stuff"), 5) c.Assert(err, gc.IsNil) env := &mockEnviron{ storage: stor, allInstances: func() ([]instance.Instance, error) { return []instance.Instance{ &mockInstance{id: "one"}, }, nil }, stopInstances: func(instances []instance.Instance) error { c.Assert(instances, gc.HasLen, 1) c.Assert(instances[0].Id(), gc.Equals, instance.Id("one")) return nil }, } err = common.Destroy(env) c.Assert(err, gc.IsNil) _, err = stor.Get("somewhere") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *DestroySuite) TestCannotTrashStorageWhenNoInstances(c *gc.C) { env := &mockEnviron{ storage: &mockStorage{removeAllErr: fmt.Errorf("noes!")}, allInstances: func() ([]instance.Instance, error) { return nil, environs.ErrNoInstances }, } err := common.Destroy(env) c.Assert(err, gc.ErrorMatches, "noes!") } func (s *DestroySuite) TestSuccessWhenNoInstances(c *gc.C) { stor := newStorage(s, c) err := stor.Put("elsewhere", strings.NewReader("stuff"), 5) c.Assert(err, gc.IsNil) env := &mockEnviron{ storage: stor, allInstances: func() ([]instance.Instance, error) { return nil, environs.ErrNoInstances }, } err = common.Destroy(env) c.Assert(err, gc.IsNil) _, err = stor.Get("elsewhere") c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/supportedarchitectures.go��������������0000644�0000153�0000161�00000001554�12321735642�031156� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common import ( "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/utils/set" ) // SupportedArchitectures returns all the image architectures for env matching the constraints. func SupportedArchitectures(env environs.Environ, imageConstraint *imagemetadata.ImageConstraint) ([]string, error) { sources, err := imagemetadata.GetMetadataSources(env) if err != nil { return nil, err } matchingImages, _, err := imagemetadata.Fetch(sources, simplestreams.DefaultIndexPath, imageConstraint, false) if err != nil { return nil, err } var arches = set.NewStrings() for _, im := range matchingImages { arches.Add(im.Arch) } return arches.Values(), nil } ����������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/polling_test.go������������������������0000644�0000153�0000161�00000005235�12321735642�027046� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( "errors" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/utils" ) type pollingSuite struct { originalLongAttempt utils.AttemptStrategy } var _ = gc.Suite(&pollingSuite{}) func (s *pollingSuite) SetUpSuite(c *gc.C) { s.originalLongAttempt = common.LongAttempt // The implementation of AttemptStrategy does not yield at all for a // delay that's already expired. So while this setting must be short // to avoid blocking tests, it must also allow enough time to convince // AttemptStrategy to sleep. Otherwise a polling loop would just run // uninterrupted and a concurrent goroutine that it was waiting for // might never actually get to do its work. common.LongAttempt = utils.AttemptStrategy{ Total: 10 * time.Millisecond, Delay: 1 * time.Millisecond, } } func (s *pollingSuite) TearDownSuite(c *gc.C) { common.LongAttempt = s.originalLongAttempt } // dnsNameFakeInstance is a fake environs.Instance implementation where // DNSName returns whatever you tell it to, and WaitDNSName delegates to the // shared WaitDNSName implementation. All the other methods are empty stubs. type dnsNameFakeInstance struct { // embed a nil Instance to panic on unimplemented method instance.Instance name string err error } var _ instance.Instance = (*dnsNameFakeInstance)(nil) func (inst *dnsNameFakeInstance) DNSName() (string, error) { return inst.name, inst.err } func (inst *dnsNameFakeInstance) WaitDNSName() (string, error) { return common.WaitDNSName(inst) } func (*dnsNameFakeInstance) Id() instance.Id { return "" } func (pollingSuite) TestWaitDNSNameReturnsDNSNameIfAvailable(c *gc.C) { inst := dnsNameFakeInstance{name: "anansi"} name, err := common.WaitDNSName(&inst) c.Assert(err, gc.IsNil) c.Check(name, gc.Equals, "anansi") } func (pollingSuite) TestWaitDNSNamePollsOnErrNoDNSName(c *gc.C) { inst := dnsNameFakeInstance{err: instance.ErrNoDNSName} _, err := common.WaitDNSName(&inst) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, ".*timed out trying to get DNS address.*") } func (pollingSuite) TestWaitDNSNamePropagatesFailure(c *gc.C) { failure := errors.New("deliberate failure") inst := dnsNameFakeInstance{err: failure} _, err := common.WaitDNSName(&inst) c.Assert(err, gc.NotNil) c.Check(err, gc.Equals, failure) } func (pollingSuite) TestInstanceWaitDNSDelegatesToSharedWaitDNS(c *gc.C) { inst := dnsNameFakeInstance{name: "anansi"} name, err := inst.WaitDNSName() c.Assert(err, gc.IsNil) c.Check(name, gc.Equals, "anansi") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/state_test.go��������������������������0000644�0000153�0000161�00000004630�12321735642�026520� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type StateSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&StateSuite{}) func (suite *StateSuite) TestGetDNSNamesAcceptsNil(c *gc.C) { result := common.GetDNSNames(nil) c.Check(result, gc.DeepEquals, []string{}) } func (suite *StateSuite) TestGetDNSNamesReturnsNames(c *gc.C) { instances := []instance.Instance{ &dnsNameFakeInstance{name: "foo"}, &dnsNameFakeInstance{name: "bar"}, } c.Check(common.GetDNSNames(instances), gc.DeepEquals, []string{"foo", "bar"}) } func (suite *StateSuite) TestGetDNSNamesIgnoresNils(c *gc.C) { c.Check(common.GetDNSNames([]instance.Instance{nil, nil}), gc.DeepEquals, []string{}) } func (suite *StateSuite) TestGetDNSNamesIgnoresInstancesWithoutNames(c *gc.C) { instances := []instance.Instance{&dnsNameFakeInstance{err: instance.ErrNoDNSName}} c.Check(common.GetDNSNames(instances), gc.DeepEquals, []string{}) } func (suite *StateSuite) TestGetDNSNamesIgnoresInstancesWithBlankNames(c *gc.C) { instances := []instance.Instance{&dnsNameFakeInstance{name: ""}} c.Check(common.GetDNSNames(instances), gc.DeepEquals, []string{}) } func (suite *StateSuite) TestComposeAddressesAcceptsNil(c *gc.C) { c.Check(common.ComposeAddresses(nil, 1433), gc.DeepEquals, []string{}) } func (suite *StateSuite) TestComposeAddressesSuffixesAddresses(c *gc.C) { c.Check( common.ComposeAddresses([]string{"onehost", "otherhost"}, 1957), gc.DeepEquals, []string{"onehost:1957", "otherhost:1957"}) } func (suite *StateSuite) TestGetStateInfo(c *gc.C) { cert := testing.CACert attrs := testing.FakeConfig().Merge(testing.Attrs{ "ca-cert": cert, "state-port": 123, "api-port": 456, }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) hostnames := []string{"onehost", "otherhost"} stateInfo, apiInfo := common.GetStateInfo(cfg, hostnames) c.Check(stateInfo.Addrs, gc.DeepEquals, []string{"onehost:123", "otherhost:123"}) c.Check(string(stateInfo.CACert), gc.Equals, cert) c.Check(apiInfo.Addrs, gc.DeepEquals, []string{"onehost:456", "otherhost:456"}) c.Check(string(apiInfo.CACert), gc.Equals, cert) } ��������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/supportedarchitectures_test.go���������0000644�0000153�0000161�00000005004�12321735776�032217� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package common_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/testing/testbase" ) type archSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&archSuite{}) func (s *archSuite) setupMetadata(c *gc.C, arches []string) (environs.Environ, simplestreams.CloudSpec) { s.PatchValue(&imagemetadata.DefaultBaseURL, "") stor := newStorage(s, c) env := &mockEnviron{ storage: stor, config: configGetter(c), } var images []*imagemetadata.ImageMetadata for _, arch := range arches { images = append(images, &imagemetadata.ImageMetadata{ Id: "image-id", Arch: arch, RegionName: "Region", Endpoint: "https://endpoint/", }) } // Append an image from another region with some other arch to ensure it is ignored. images = append(images, &imagemetadata.ImageMetadata{ Id: "image-id", Arch: "arch", RegionName: "Region-Two", Endpoint: "https://endpoint/", }) cloudSpec := simplestreams.CloudSpec{ Region: "Region", Endpoint: "https://endpoint/", } err := imagemetadata.MergeAndWriteMetadata("precise", images, &cloudSpec, env.Storage()) c.Assert(err, gc.IsNil) return env, cloudSpec } func (s *archSuite) TestSupportedArchitecturesNone(c *gc.C) { env, cloudSpec := s.setupMetadata(c, nil) imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: cloudSpec, }) arches, err := common.SupportedArchitectures(env, imageConstraint) c.Assert(err, gc.IsNil) c.Assert(arches, gc.HasLen, 0) } func (s *archSuite) TestSupportedArchitecturesOne(c *gc.C) { env, cloudSpec := s.setupMetadata(c, []string{"ppc64"}) imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: cloudSpec, }) arches, err := common.SupportedArchitectures(env, imageConstraint) c.Assert(err, gc.IsNil) c.Assert(arches, gc.DeepEquals, []string{"ppc64"}) } func (s *archSuite) TestSupportedArchitecturesMany(c *gc.C) { env, cloudSpec := s.setupMetadata(c, []string{"ppc64", "amd64"}) imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: cloudSpec, }) arches, err := common.SupportedArchitectures(env, imageConstraint) c.Assert(err, gc.IsNil) c.Assert(arches, gc.DeepEquals, []string{"amd64", "ppc64"}) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/common/provider_test.go�����������������������0000644�0000153�0000161�00000000174�12321735642�027231� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package common_test import ( "testing" gc "launchpad.net/gocheck" ) func TestPackage(t *testing.T) { gc.TestingT(t) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/error.go��������������������������������������0000644�0000153�0000161�00000000576�12321735642�024207� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package provider import "errors" var ( // ErrNotPrepared should be returned by providers when // an operation is attempted on an unprepared environment. ErrNotPrepared = errors.New("environment is not prepared") ErrDestroyed = errors.New("environment has been destroyed") ) ����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/all/������������������������������������������0000755�0000153�0000161�00000000000�12321735642�023267� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/all/all.go������������������������������������0000644�0000153�0000161�00000000726�12321735642�024373� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package all // Register all the available providers. import ( _ "launchpad.net/juju-core/provider/azure" _ "launchpad.net/juju-core/provider/ec2" _ "launchpad.net/juju-core/provider/joyent" _ "launchpad.net/juju-core/provider/local" _ "launchpad.net/juju-core/provider/maas" _ "launchpad.net/juju-core/provider/manual" _ "launchpad.net/juju-core/provider/openstack" ) ������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/�����������������������������������������0000755�0000153�0000161�00000000000�12321736000�023425� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/util_test.go�����������������������������0000644�0000153�0000161�00000003510�12321735642�026002� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/instance" ) type utilSuite struct{} var _ = gc.Suite(&utilSuite{}) func (*utilSuite) TestExtractSystemId(c *gc.C) { instanceId := instance.Id("/MAAS/api/1.0/nodes/system_id/") systemId := extractSystemId(instanceId) c.Check(systemId, gc.Equals, "system_id") } func (*utilSuite) TestGetSystemIdValues(c *gc.C) { instanceId1 := instance.Id("/MAAS/api/1.0/nodes/system_id1/") instanceId2 := instance.Id("/MAAS/api/1.0/nodes/system_id2/") instanceIds := []instance.Id{instanceId1, instanceId2} values := getSystemIdValues(instanceIds) c.Check(values["id"], gc.DeepEquals, []string{"system_id1", "system_id2"}) } func (*utilSuite) TestMachineInfoCloudinitRunCmd(c *gc.C) { hostname := "hostname" filename := "path/to/file" old_MAASInstanceFilename := _MAASInstanceFilename _MAASInstanceFilename = filename defer func() { _MAASInstanceFilename = old_MAASInstanceFilename }() info := machineInfo{hostname} script, err := info.cloudinitRunCmd() c.Assert(err, gc.IsNil) yaml, err := goyaml.Marshal(info) c.Assert(err, gc.IsNil) expected := fmt.Sprintf("mkdir -p '%s'; echo -n '%s' > '%s'", environs.DataDir, yaml, filename) c.Check(script, gc.Equals, expected) } func (*utilSuite) TestMachineInfoLoad(c *gc.C) { hostname := "hostname" yaml := fmt.Sprintf("hostname: %s\n", hostname) filename := createTempFile(c, []byte(yaml)) old_MAASInstanceFilename := _MAASInstanceFilename _MAASInstanceFilename = filename defer func() { _MAASInstanceFilename = old_MAASInstanceFilename }() info := machineInfo{} err := info.load() c.Assert(err, gc.IsNil) c.Check(info.Hostname, gc.Equals, hostname) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/export_test.go���������������������������0000644�0000153�0000161�00000001012�12321735642�026341� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "launchpad.net/gomaasapi" "launchpad.net/juju-core/environs" ) var ( ShortAttempt = &shortAttempt APIVersion = apiVersion NewCloudinitConfig = newCloudinitConfig ) func MAASAgentName(env environs.Environ) string { return env.(*maasEnviron).ecfg().maasAgentName() } func GetMAASClient(env environs.Environ) *gomaasapi.MAASObject { return env.(*maasEnviron).getMAASClient() } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/config_test.go���������������������������0000644�0000153�0000161�00000007555�12321735642�026307� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type configSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&configSuite{}) // copyAttrs copies values from src into dest. If src contains a key that was // already in dest, its value in dest will still be updated to the one from // src. func copyAttrs(src, dest map[string]interface{}) { for k, v := range src { dest[k] = v } } // newConfig creates a MAAS environment config from attributes. func newConfig(values map[string]interface{}) (*maasEnvironConfig, error) { attrs := testing.FakeConfig().Merge(testing.Attrs{ "name": "testenv", "type": "maas", }).Merge(values) env, err := environs.NewFromAttrs(attrs) if err != nil { return nil, err } return env.(*maasEnviron).ecfg(), nil } func (*configSuite) TestParsesMAASSettings(c *gc.C) { server := "http://maas.testing.invalid/maas/" oauth := "consumer-key:resource-token:resource-secret" future := "futurama" uuid, err := utils.NewUUID() c.Assert(err, gc.IsNil) ecfg, err := newConfig(map[string]interface{}{ "maas-server": server, "maas-oauth": oauth, "maas-agent-name": uuid.String(), "future-key": future, }) c.Assert(err, gc.IsNil) c.Check(ecfg.maasServer(), gc.Equals, server) c.Check(ecfg.maasOAuth(), gc.DeepEquals, oauth) c.Check(ecfg.maasAgentName(), gc.Equals, uuid.String()) c.Check(ecfg.UnknownAttrs()["future-key"], gc.DeepEquals, future) } func (*configSuite) TestMaasAgentNameDefault(c *gc.C) { ecfg, err := newConfig(map[string]interface{}{ "maas-server": "http://maas.testing.invalid/maas/", "maas-oauth": "consumer-key:resource-token:resource-secret", }) c.Assert(err, gc.IsNil) c.Check(ecfg.maasAgentName(), gc.Equals, "") } func (*configSuite) TestChecksWellFormedMaasServer(c *gc.C) { _, err := newConfig(map[string]interface{}{ "maas-server": "This should have been a URL.", "maas-oauth": "consumer-key:resource-token:resource-secret", }) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, ".*malformed maas-server.*") } func (*configSuite) TestChecksWellFormedMaasOAuth(c *gc.C) { _, err := newConfig(map[string]interface{}{ "maas-server": "http://maas.testing.invalid/maas/", "maas-oauth": "This should have been a 3-part token.", }) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, ".*malformed maas-oauth.*") } func (*configSuite) TestValidateUpcallsEnvironsConfigValidate(c *gc.C) { // The base Validate() function will not allow an environment to // change its name. Trigger that error so as to prove that the // environment provider's Validate() calls the base Validate(). baseAttrs := map[string]interface{}{ "maas-server": "http://maas.testing.invalid/maas/", "maas-oauth": "consumer-key:resource-token:resource-secret", } oldCfg, err := newConfig(baseAttrs) c.Assert(err, gc.IsNil) newName := oldCfg.Name() + "-but-different" newCfg, err := oldCfg.Apply(map[string]interface{}{"name": newName}) c.Assert(err, gc.IsNil) _, err = maasEnvironProvider{}.Validate(newCfg, oldCfg.Config) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, ".*cannot change name.*") } func (*configSuite) TestValidateCannotChangeAgentName(c *gc.C) { baseAttrs := map[string]interface{}{ "maas-server": "http://maas.testing.invalid/maas/", "maas-oauth": "consumer-key:resource-token:resource-secret", "maas-agent-name": "1234-5678", } oldCfg, err := newConfig(baseAttrs) c.Assert(err, gc.IsNil) newCfg, err := oldCfg.Apply(map[string]interface{}{ "maas-agent-name": "9876-5432", }) c.Assert(err, gc.IsNil) _, err = maasEnvironProvider{}.Validate(newCfg, oldCfg.Config) c.Assert(err, gc.ErrorMatches, "cannot change maas-agent-name") } ���������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/config.go��������������������������������0000644�0000153�0000161�00000005573�12321735642�025246� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "errors" "fmt" "net/url" "strings" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/schema" ) var configFields = schema.Fields{ "maas-server": schema.String(), // maas-oauth is a colon-separated triplet of: // consumer-key:resource-token:resource-secret "maas-oauth": schema.String(), // maas-agent-name is an optional UUID to group the instances // acquired from MAAS, to support multiple environments per MAAS user. "maas-agent-name": schema.String(), } var configDefaults = schema.Defaults{ // For backward-compatibility, maas-agent-name is the empty string // by default. However, new environments should all use a UUID. "maas-agent-name": "", } type maasEnvironConfig struct { *config.Config attrs map[string]interface{} } func (cfg *maasEnvironConfig) maasServer() string { return cfg.attrs["maas-server"].(string) } func (cfg *maasEnvironConfig) maasOAuth() string { return cfg.attrs["maas-oauth"].(string) } func (cfg *maasEnvironConfig) maasAgentName() string { if uuid, ok := cfg.attrs["maas-agent-name"].(string); ok { return uuid } return "" } func (prov maasEnvironProvider) newConfig(cfg *config.Config) (*maasEnvironConfig, error) { validCfg, err := prov.Validate(cfg, nil) if err != nil { return nil, err } result := new(maasEnvironConfig) result.Config = validCfg result.attrs = validCfg.UnknownAttrs() return result, nil } var errMalformedMaasOAuth = errors.New("malformed maas-oauth (3 items separated by colons)") func (prov maasEnvironProvider) Validate(cfg, oldCfg *config.Config) (*config.Config, error) { // Validate base configuration change before validating MAAS specifics. err := config.Validate(cfg, oldCfg) if err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } if oldCfg != nil { oldAttrs := oldCfg.UnknownAttrs() validMaasAgentName := false if oldName, ok := oldAttrs["maas-agent-name"]; !ok || oldName == nil { // If maas-agent-name was nil (because the config was // generated pre-1.16.2 the only correct value for it is "" // See bug #1256179 validMaasAgentName = (validated["maas-agent-name"] == "") } else { validMaasAgentName = (validated["maas-agent-name"] == oldName) } if !validMaasAgentName { return nil, fmt.Errorf("cannot change maas-agent-name") } } envCfg := new(maasEnvironConfig) envCfg.Config = cfg envCfg.attrs = validated server := envCfg.maasServer() serverURL, err := url.Parse(server) if err != nil || serverURL.Scheme == "" || serverURL.Host == "" { return nil, fmt.Errorf("malformed maas-server URL '%v': %s", server, err) } oauth := envCfg.maasOAuth() if strings.Count(oauth, ":") != 2 { return nil, errMalformedMaasOAuth } return cfg.Apply(envCfg.attrs) } �������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/instance.go������������������������������0000644�0000153�0000161�00000006543�12321735776�025613� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "fmt" "sync" "launchpad.net/gomaasapi" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" ) type maasInstance struct { environ *maasEnviron mu sync.Mutex maasObject *gomaasapi.MAASObject } var _ instance.Instance = (*maasInstance)(nil) func (mi *maasInstance) String() string { hostname, err := mi.DNSName() if err != nil { // This is meant to be impossible, but be paranoid. hostname = fmt.Sprintf("<DNSName failed: %q>", err) } return fmt.Sprintf("%s:%s", hostname, mi.Id()) } func (mi *maasInstance) Id() instance.Id { return maasObjectId(mi.getMaasObject()) } func maasObjectId(maasObject *gomaasapi.MAASObject) instance.Id { // Use the node's 'resource_uri' value. return instance.Id(maasObject.URI().String()) } func (mi *maasInstance) Status() string { // MAAS does not track node status once they're allocated. // Since any instance that juju knows about will be an // allocated one, it doesn't make sense to report any // state unless we obtain it through some means other than // through the MAAS API. return "" } // Refresh refreshes the instance with the most up-to-date information // from the MAAS server. func (mi *maasInstance) Refresh() error { mi.mu.Lock() defer mi.mu.Unlock() insts, err := mi.environ.Instances([]instance.Id{maasObjectId(mi.maasObject)}) if err != nil { return err } mi.maasObject = insts[0].(*maasInstance).maasObject return nil } func (mi *maasInstance) getMaasObject() *gomaasapi.MAASObject { mi.mu.Lock() defer mi.mu.Unlock() return mi.maasObject } func (mi *maasInstance) Addresses() ([]instance.Address, error) { name, err := mi.DNSName() if err != nil { return nil, err } host := instance.Address{name, instance.HostName, "", instance.NetworkPublic} addrs := []instance.Address{host} ips, err := mi.ipAddresses() if err != nil { return nil, err } for _, ip := range ips { a := instance.NewAddress(ip) addrs = append(addrs, a) } return addrs, nil } func (mi *maasInstance) ipAddresses() ([]string, error) { // we have to do this the hard way, since maasObject doesn't have this built-in yet addressArray := mi.getMaasObject().GetMap()["ip_addresses"] if addressArray.IsNil() { // Older MAAS versions do not return ip_addresses. return nil, nil } objs, err := addressArray.GetArray() if err != nil { return nil, err } ips := make([]string, len(objs)) for i, obj := range objs { s, err := obj.GetString() if err != nil { return nil, err } ips[i] = s } return ips, nil } func (mi *maasInstance) DNSName() (string, error) { // A MAAS instance has its DNS name immediately. return mi.getMaasObject().GetField("hostname") } func (mi *maasInstance) WaitDNSName() (string, error) { return common.WaitDNSName(mi) } // MAAS does not do firewalling so these port methods do nothing. func (mi *maasInstance) OpenPorts(machineId string, ports []instance.Port) error { logger.Debugf("unimplemented OpenPorts() called") return nil } func (mi *maasInstance) ClosePorts(machineId string, ports []instance.Port) error { logger.Debugf("unimplemented ClosePorts() called") return nil } func (mi *maasInstance) Ports(machineId string) ([]instance.Port, error) { logger.Debugf("unimplemented Ports() called") return []instance.Port{}, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/instance_test.go�������������������������0000644�0000153�0000161�00000010555�12321735776�026650� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "fmt" gc "launchpad.net/gocheck" "launchpad.net/juju-core/instance" ) type instanceTest struct { providerSuite } var _ = gc.Suite(&instanceTest{}) func (s *instanceTest) TestId(c *gc.C) { jsonValue := `{"system_id": "system_id", "test": "test"}` obj := s.testMAASObject.TestServer.NewNode(jsonValue) resourceURI, _ := obj.GetField("resource_uri") instance := maasInstance{maasObject: &obj, environ: s.makeEnviron()} c.Check(string(instance.Id()), gc.Equals, resourceURI) } func (s *instanceTest) TestString(c *gc.C) { jsonValue := `{"hostname": "thethingintheplace", "system_id": "system_id", "test": "test"}` obj := s.testMAASObject.TestServer.NewNode(jsonValue) instance := &maasInstance{maasObject: &obj, environ: s.makeEnviron()} hostname, err := instance.DNSName() c.Assert(err, gc.IsNil) expected := hostname + ":" + string(instance.Id()) c.Assert(fmt.Sprint(instance), gc.Equals, expected) } func (s *instanceTest) TestStringWithoutHostname(c *gc.C) { // For good measure, test what happens if we don't have a hostname. jsonValue := `{"system_id": "system_id", "test": "test"}` obj := s.testMAASObject.TestServer.NewNode(jsonValue) instance := &maasInstance{maasObject: &obj, environ: s.makeEnviron()} _, err := instance.DNSName() c.Assert(err, gc.NotNil) expected := fmt.Sprintf("<DNSName failed: %q>", err) + ":" + string(instance.Id()) c.Assert(fmt.Sprint(instance), gc.Equals, expected) } func (s *instanceTest) TestRefreshInstance(c *gc.C) { jsonValue := `{"system_id": "system_id", "test": "test"}` obj := s.testMAASObject.TestServer.NewNode(jsonValue) s.testMAASObject.TestServer.ChangeNode("system_id", "test2", "test2") instance := maasInstance{maasObject: &obj, environ: s.makeEnviron()} err := instance.Refresh() c.Check(err, gc.IsNil) testField, err := (*instance.maasObject).GetField("test2") c.Check(err, gc.IsNil) c.Check(testField, gc.Equals, "test2") } func (s *instanceTest) TestDNSName(c *gc.C) { jsonValue := `{"hostname": "DNS name", "system_id": "system_id"}` obj := s.testMAASObject.TestServer.NewNode(jsonValue) instance := maasInstance{maasObject: &obj, environ: s.makeEnviron()} dnsName, err := instance.DNSName() c.Check(err, gc.IsNil) c.Check(dnsName, gc.Equals, "DNS name") // WaitDNSName() currently simply calls DNSName(). dnsName, err = instance.WaitDNSName() c.Check(err, gc.IsNil) c.Check(dnsName, gc.Equals, "DNS name") } func (s *instanceTest) TestAddresses(c *gc.C) { jsonValue := `{ "hostname": "testing.invalid", "system_id": "system_id", "ip_addresses": [ "1.2.3.4", "fe80::d806:dbff:fe23:1199" ] }` obj := s.testMAASObject.TestServer.NewNode(jsonValue) inst := maasInstance{maasObject: &obj, environ: s.makeEnviron()} expected := []instance.Address{ {Value: "testing.invalid", Type: instance.HostName, NetworkScope: instance.NetworkPublic}, instance.NewAddress("1.2.3.4"), instance.NewAddress("fe80::d806:dbff:fe23:1199"), } addr, err := inst.Addresses() c.Assert(err, gc.IsNil) c.Check(addr, gc.DeepEquals, expected) } func (s *instanceTest) TestAddressesMissing(c *gc.C) { // Older MAAS versions do not have ip_addresses returned, for these // just the DNS name should be returned without error. jsonValue := `{ "hostname": "testing.invalid", "system_id": "system_id" }` obj := s.testMAASObject.TestServer.NewNode(jsonValue) inst := maasInstance{maasObject: &obj, environ: s.makeEnviron()} addr, err := inst.Addresses() c.Assert(err, gc.IsNil) c.Check(addr, gc.DeepEquals, []instance.Address{ {Value: "testing.invalid", Type: instance.HostName, NetworkScope: instance.NetworkPublic}, }) } func (s *instanceTest) TestAddressesInvalid(c *gc.C) { jsonValue := `{ "hostname": "testing.invalid", "system_id": "system_id", "ip_addresses": "incompatible" }` obj := s.testMAASObject.TestServer.NewNode(jsonValue) inst := maasInstance{maasObject: &obj, environ: s.makeEnviron()} _, err := inst.Addresses() c.Assert(err, gc.NotNil) } func (s *instanceTest) TestAddressesInvalidContents(c *gc.C) { jsonValue := `{ "hostname": "testing.invalid", "system_id": "system_id", "ip_addresses": [42] }` obj := s.testMAASObject.TestServer.NewNode(jsonValue) inst := maasInstance{maasObject: &obj, environ: s.makeEnviron()} _, err := inst.Addresses() c.Assert(err, gc.NotNil) } ���������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/environ_whitebox_test.go�����������������0000644�0000153�0000161�00000043512�12321735776�030434� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "bytes" "encoding/base64" "fmt" "io/ioutil" "net/url" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/goyaml" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtesting "launchpad.net/juju-core/environs/testing" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) type environSuite struct { providerSuite } const ( allocatedNode = `{"system_id": "test-allocated"}` ) var _ = gc.Suite(&environSuite{}) // getTestConfig creates a customized sample MAAS provider configuration. func getTestConfig(name, server, oauth, secret string) *config.Config { ecfg, err := newConfig(map[string]interface{}{ "name": name, "maas-server": server, "maas-oauth": oauth, "admin-secret": secret, "authorized-keys": "I-am-not-a-real-key", }) if err != nil { panic(err) } return ecfg.Config } func (suite *environSuite) setupFakeProviderStateFile(c *gc.C) { suite.testMAASObject.TestServer.NewFile(bootstrap.StateFile, []byte("test file content")) } func (suite *environSuite) setupFakeTools(c *gc.C) { stor := NewStorage(suite.makeEnviron()) envtesting.UploadFakeTools(c, stor) } func (suite *environSuite) addNode(jsonText string) instance.Id { node := suite.testMAASObject.TestServer.NewNode(jsonText) resourceURI, _ := node.GetField("resource_uri") return instance.Id(resourceURI) } func (suite *environSuite) TestInstancesReturnsInstances(c *gc.C) { id := suite.addNode(allocatedNode) instances, err := suite.makeEnviron().Instances([]instance.Id{id}) c.Check(err, gc.IsNil) c.Assert(instances, gc.HasLen, 1) c.Assert(instances[0].Id(), gc.Equals, id) } func (suite *environSuite) TestInstancesReturnsErrNoInstancesIfEmptyParameter(c *gc.C) { suite.addNode(allocatedNode) instances, err := suite.makeEnviron().Instances([]instance.Id{}) c.Check(err, gc.Equals, environs.ErrNoInstances) c.Check(instances, gc.IsNil) } func (suite *environSuite) TestInstancesReturnsErrNoInstancesIfNilParameter(c *gc.C) { suite.addNode(allocatedNode) instances, err := suite.makeEnviron().Instances(nil) c.Check(err, gc.Equals, environs.ErrNoInstances) c.Check(instances, gc.IsNil) } func (suite *environSuite) TestInstancesReturnsErrNoInstancesIfNoneFound(c *gc.C) { instances, err := suite.makeEnviron().Instances([]instance.Id{"unknown"}) c.Check(err, gc.Equals, environs.ErrNoInstances) c.Check(instances, gc.IsNil) } func (suite *environSuite) TestAllInstances(c *gc.C) { id := suite.addNode(allocatedNode) instances, err := suite.makeEnviron().AllInstances() c.Check(err, gc.IsNil) c.Assert(instances, gc.HasLen, 1) c.Assert(instances[0].Id(), gc.Equals, id) } func (suite *environSuite) TestAllInstancesReturnsEmptySliceIfNoInstance(c *gc.C) { instances, err := suite.makeEnviron().AllInstances() c.Check(err, gc.IsNil) c.Check(instances, gc.HasLen, 0) } func (suite *environSuite) TestInstancesReturnsErrorIfPartialInstances(c *gc.C) { known := suite.addNode(allocatedNode) suite.addNode(`{"system_id": "test2"}`) unknown := instance.Id("unknown systemID") instances, err := suite.makeEnviron().Instances([]instance.Id{known, unknown}) c.Check(err, gc.Equals, environs.ErrPartialInstances) c.Assert(instances, gc.HasLen, 2) c.Check(instances[0].Id(), gc.Equals, known) c.Check(instances[1], gc.IsNil) } func (suite *environSuite) TestStorageReturnsStorage(c *gc.C) { env := suite.makeEnviron() stor := env.Storage() c.Check(stor, gc.NotNil) // The Storage object is really a maasStorage. specificStorage := stor.(*maasStorage) // Its environment pointer refers back to its environment. c.Check(specificStorage.environUnlocked, gc.Equals, env) } func decodeUserData(userData string) ([]byte, error) { data, err := base64.StdEncoding.DecodeString(userData) if err != nil { return []byte(""), err } return utils.Gunzip(data) } func (suite *environSuite) TestStartInstanceStartsInstance(c *gc.C) { suite.setupFakeTools(c) env := suite.makeEnviron() // Create node 0: it will be used as the bootstrap node. suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0"}`) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) // The bootstrap node has been acquired and started. operations := suite.testMAASObject.TestServer.NodeOperations() actions, found := operations["node0"] c.Check(found, gc.Equals, true) c.Check(actions, gc.DeepEquals, []string{"acquire", "start"}) // Test the instance id is correctly recorded for the bootstrap node. // Check that the state holds the id of the bootstrap machine. stateData, err := bootstrap.LoadState(env.Storage()) c.Assert(err, gc.IsNil) c.Assert(stateData.StateInstances, gc.HasLen, 1) insts, err := env.AllInstances() c.Assert(err, gc.IsNil) c.Assert(insts, gc.HasLen, 1) c.Check(insts[0].Id(), gc.Equals, stateData.StateInstances[0]) // Create node 1: it will be used as instance number 1. suite.testMAASObject.TestServer.NewNode(`{"system_id": "node1", "hostname": "host1"}`) // TODO(wallyworld) - test instance metadata instance, _ := testing.AssertStartInstance(c, env, "1") c.Assert(err, gc.IsNil) c.Check(instance, gc.NotNil) // The instance number 1 has been acquired and started. actions, found = operations["node1"] c.Assert(found, gc.Equals, true) c.Check(actions, gc.DeepEquals, []string{"acquire", "start"}) // The value of the "user data" parameter used when starting the node // contains the run cmd used to write the machine information onto // the node's filesystem. requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() nodeRequestValues, found := requestValues["node1"] c.Assert(found, gc.Equals, true) c.Assert(len(nodeRequestValues), gc.Equals, 2) userData := nodeRequestValues[1].Get("user_data") decodedUserData, err := decodeUserData(userData) c.Assert(err, gc.IsNil) info := machineInfo{"host1"} cloudinitRunCmd, err := info.cloudinitRunCmd() c.Assert(err, gc.IsNil) data, err := goyaml.Marshal(cloudinitRunCmd) c.Assert(err, gc.IsNil) c.Check(string(decodedUserData), gc.Matches, "(.|\n)*"+string(data)+"(\n|.)*") // Trash the tools and try to start another instance. envtesting.RemoveTools(c, env.Storage()) instance, _, err = testing.StartInstance(env, "2") c.Check(instance, gc.IsNil) c.Check(err, jc.Satisfies, errors.IsNotFoundError) } func uint64p(val uint64) *uint64 { return &val } func stringp(val string) *string { return &val } func (suite *environSuite) TestAcquireNode(c *gc.C) { stor := NewStorage(suite.makeEnviron()) fakeTools := envtesting.MustUploadFakeToolsVersions(stor, version.Current)[0] env := suite.makeEnviron() suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0"}`) _, _, err := env.acquireNode(constraints.Value{}, environs.Networks{}, tools.List{fakeTools}) c.Check(err, gc.IsNil) operations := suite.testMAASObject.TestServer.NodeOperations() actions, found := operations["node0"] c.Assert(found, gc.Equals, true) c.Check(actions, gc.DeepEquals, []string{"acquire"}) } func (suite *environSuite) TestAcquireNodeTakesConstraintsIntoAccount(c *gc.C) { stor := NewStorage(suite.makeEnviron()) fakeTools := envtesting.MustUploadFakeToolsVersions(stor, version.Current)[0] env := suite.makeEnviron() suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0"}`) constraints := constraints.Value{Arch: stringp("arm"), Mem: uint64p(1024)} _, _, err := env.acquireNode(constraints, environs.Networks{}, tools.List{fakeTools}) c.Check(err, gc.IsNil) requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() nodeRequestValues, found := requestValues["node0"] c.Assert(found, gc.Equals, true) c.Assert(nodeRequestValues[0].Get("arch"), gc.Equals, "arm") c.Assert(nodeRequestValues[0].Get("mem"), gc.Equals, "1024") } func (suite *environSuite) TestAcquireNodePassedAgentName(c *gc.C) { stor := NewStorage(suite.makeEnviron()) fakeTools := envtesting.MustUploadFakeToolsVersions(stor, version.Current)[0] env := suite.makeEnviron() suite.testMAASObject.TestServer.NewNode(`{"system_id": "node0", "hostname": "host0"}`) _, _, err := env.acquireNode(constraints.Value{}, environs.Networks{}, tools.List{fakeTools}) c.Check(err, gc.IsNil) requestValues := suite.testMAASObject.TestServer.NodeOperationRequestValues() nodeRequestValues, found := requestValues["node0"] c.Assert(found, gc.Equals, true) c.Assert(nodeRequestValues[0].Get("agent_name"), gc.Equals, exampleAgentName) } var testValues = []struct { constraints constraints.Value expectedResult url.Values }{ {constraints.Value{Arch: stringp("arm")}, url.Values{"arch": {"arm"}}}, {constraints.Value{CpuCores: uint64p(4)}, url.Values{"cpu_count": {"4"}}}, {constraints.Value{Mem: uint64p(1024)}, url.Values{"mem": {"1024"}}}, // CpuPower is ignored. {constraints.Value{CpuPower: uint64p(1024)}, url.Values{}}, // RootDisk is ignored. {constraints.Value{RootDisk: uint64p(8192)}, url.Values{}}, {constraints.Value{Tags: &[]string{"foo", "bar"}}, url.Values{"tags": {"foo,bar"}}}, {constraints.Value{Arch: stringp("arm"), CpuCores: uint64p(4), Mem: uint64p(1024), CpuPower: uint64p(1024), RootDisk: uint64p(8192), Tags: &[]string{"foo", "bar"}}, url.Values{"arch": {"arm"}, "cpu_count": {"4"}, "mem": {"1024"}, "tags": {"foo,bar"}}}, } func (*environSuite) TestConvertConstraints(c *gc.C) { for _, test := range testValues { c.Check(convertConstraints(test.constraints), gc.DeepEquals, test.expectedResult) } } var testNetworkValues = []struct { networks environs.Networks expectedResult url.Values }{ { environs.Networks{}, url.Values{}, }, { environs.Networks{ IncludeNetworks: []string{"included_net_1"}, }, url.Values{"networks": {"included_net_1"}}, }, { environs.Networks{ ExcludeNetworks: []string{"excluded_net_1"}, }, url.Values{"not_networks": {"excluded_net_1"}}, }, { environs.Networks{ IncludeNetworks: []string{"included_net_1", "included_net_2"}, ExcludeNetworks: []string{"excluded_net_1", "excluded_net_2"}, }, url.Values{ "networks": {"included_net_1", "included_net_2"}, "not_networks": {"excluded_net_1", "excluded_net_2"}, }, }, } func (*environSuite) TestConvertNetworks(c *gc.C) { for _, test := range testNetworkValues { var vals = url.Values{} addNetworks(vals, test.networks) c.Check(vals, gc.DeepEquals, test.expectedResult) } } func (suite *environSuite) getInstance(systemId string) *maasInstance { input := `{"system_id": "` + systemId + `"}` node := suite.testMAASObject.TestServer.NewNode(input) return &maasInstance{maasObject: &node, environ: suite.makeEnviron()} } func (suite *environSuite) TestStopInstancesReturnsIfParameterEmpty(c *gc.C) { suite.getInstance("test1") err := suite.makeEnviron().StopInstances([]instance.Instance{}) c.Check(err, gc.IsNil) operations := suite.testMAASObject.TestServer.NodeOperations() c.Check(operations, gc.DeepEquals, map[string][]string{}) } func (suite *environSuite) TestStopInstancesStopsAndReleasesInstances(c *gc.C) { instance1 := suite.getInstance("test1") instance2 := suite.getInstance("test2") suite.getInstance("test3") instances := []instance.Instance{instance1, instance2} err := suite.makeEnviron().StopInstances(instances) c.Check(err, gc.IsNil) operations := suite.testMAASObject.TestServer.NodeOperations() expectedOperations := map[string][]string{"test1": {"release"}, "test2": {"release"}} c.Check(operations, gc.DeepEquals, expectedOperations) } func (suite *environSuite) TestStateInfo(c *gc.C) { env := suite.makeEnviron() hostname := "test" input := `{"system_id": "system_id", "hostname": "` + hostname + `"}` node := suite.testMAASObject.TestServer.NewNode(input) testInstance := &maasInstance{maasObject: &node, environ: suite.makeEnviron()} err := bootstrap.SaveState( env.Storage(), &bootstrap.BootstrapState{StateInstances: []instance.Id{testInstance.Id()}}) c.Assert(err, gc.IsNil) stateInfo, apiInfo, err := env.StateInfo() c.Assert(err, gc.IsNil) cfg := env.Config() statePortSuffix := fmt.Sprintf(":%d", cfg.StatePort()) apiPortSuffix := fmt.Sprintf(":%d", cfg.APIPort()) c.Assert(stateInfo.Addrs, gc.DeepEquals, []string{hostname + statePortSuffix}) c.Assert(apiInfo.Addrs, gc.DeepEquals, []string{hostname + apiPortSuffix}) } func (suite *environSuite) TestStateInfoFailsIfNoStateInstances(c *gc.C) { env := suite.makeEnviron() _, _, err := env.StateInfo() c.Check(err, gc.Equals, environs.ErrNotBootstrapped) } func (suite *environSuite) TestDestroy(c *gc.C) { env := suite.makeEnviron() suite.getInstance("test1") data := makeRandomBytes(10) suite.testMAASObject.TestServer.NewFile("filename", data) stor := env.Storage() err := env.Destroy() c.Check(err, gc.IsNil) // Instances have been stopped. operations := suite.testMAASObject.TestServer.NodeOperations() expectedOperations := map[string][]string{"test1": {"release"}} c.Check(operations, gc.DeepEquals, expectedOperations) // Files have been cleaned up. listing, err := storage.List(stor, "") c.Assert(err, gc.IsNil) c.Check(listing, gc.DeepEquals, []string{}) } // It would be nice if we could unit-test Bootstrap() in more detail, but // at the time of writing that would require more support from gomaasapi's // testing service than we have. func (suite *environSuite) TestBootstrapSucceeds(c *gc.C) { suite.setupFakeTools(c) env := suite.makeEnviron() suite.testMAASObject.TestServer.NewNode(`{"system_id": "thenode", "hostname": "host"}`) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) } func (suite *environSuite) TestBootstrapFailsIfNoTools(c *gc.C) { suite.setupFakeTools(c) env := suite.makeEnviron() // Can't RemoveAllTools, no public storage. envtesting.RemoveTools(c, env.Storage()) // Disable auto-uploading by setting the agent version. cfg, err := env.Config().Apply(map[string]interface{}{ "agent-version": version.Current.Number.String(), }) c.Assert(err, gc.IsNil) err = env.SetConfig(cfg) c.Assert(err, gc.IsNil) err = bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) stripped := strings.Replace(err.Error(), "\n", "", -1) c.Check(stripped, gc.Matches, "cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment.*") } func (suite *environSuite) TestBootstrapFailsIfNoNodes(c *gc.C) { suite.setupFakeTools(c) env := suite.makeEnviron() err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) // Since there are no nodes, the attempt to allocate one returns a // 409: Conflict. c.Check(err, gc.ErrorMatches, ".*409.*") } func (suite *environSuite) TestBootstrapIntegratesWithEnvirons(c *gc.C) { suite.setupFakeTools(c) env := suite.makeEnviron() suite.testMAASObject.TestServer.NewNode(`{"system_id": "bootstrapnode", "hostname": "host"}`) // bootstrap.Bootstrap calls Environ.Bootstrap. This works. err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) } func assertSourceContents(c *gc.C, source simplestreams.DataSource, filename string, content []byte) { rc, _, err := source.Fetch(filename) c.Assert(err, gc.IsNil) defer rc.Close() retrieved, err := ioutil.ReadAll(rc) c.Assert(err, gc.IsNil) c.Assert(retrieved, gc.DeepEquals, content) } func (suite *environSuite) assertGetImageMetadataSources(c *gc.C, stream, officialSourcePath string) { // Make an env configured with the stream. testAttrs := maasEnvAttrs testAttrs = testAttrs.Merge(coretesting.Attrs{ "maas-server": suite.testMAASObject.TestServer.URL, }) if stream != "" { testAttrs = testAttrs.Merge(coretesting.Attrs{ "image-stream": stream, }) } attrs := coretesting.FakeConfig().Merge(testAttrs) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := NewEnviron(cfg) c.Assert(err, gc.IsNil) // Add a dummy file to storage so we can use that to check the // obtained source later. data := makeRandomBytes(10) stor := NewStorage(env) err = stor.Put("images/filename", bytes.NewBuffer([]byte(data)), int64(len(data))) c.Assert(err, gc.IsNil) sources, err := imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) c.Assert(len(sources), gc.Equals, 2) assertSourceContents(c, sources[0], "filename", data) url, err := sources[1].URL("") c.Assert(err, gc.IsNil) c.Assert(url, gc.Equals, fmt.Sprintf("http://cloud-images.ubuntu.com/%s/", officialSourcePath)) } func (suite *environSuite) TestGetImageMetadataSources(c *gc.C) { suite.assertGetImageMetadataSources(c, "", "releases") suite.assertGetImageMetadataSources(c, "released", "releases") suite.assertGetImageMetadataSources(c, "daily", "daily") } func (suite *environSuite) TestGetToolsMetadataSources(c *gc.C) { env := suite.makeEnviron() // Add a dummy file to storage so we can use that to check the // obtained source later. data := makeRandomBytes(10) stor := NewStorage(env) err := stor.Put("tools/filename", bytes.NewBuffer([]byte(data)), int64(len(data))) c.Assert(err, gc.IsNil) sources, err := envtools.GetMetadataSources(env) c.Assert(err, gc.IsNil) c.Assert(len(sources), gc.Equals, 1) assertSourceContents(c, sources[0], "filename", data) } func (suite *environSuite) TestSupportedArchitectures(c *gc.C) { env := suite.makeEnviron() a, err := env.SupportedArchitectures() c.Assert(err, gc.IsNil) c.Assert(a, gc.DeepEquals, []string{"amd64"}) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/storage.go�������������������������������0000644�0000153�0000161�00000016463�12321735642�025445� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "bytes" "encoding/base64" "fmt" "io" "io/ioutil" "net/url" "sort" "strings" "sync" "launchpad.net/gomaasapi" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" ) type maasStorage struct { // Mutex protects the "*Unlocked" fields. sync.Mutex // The Environ that this Storage is for. environUnlocked *maasEnviron // Reference to the URL on the API where files are stored. maasClientUnlocked gomaasapi.MAASObject } var _ storage.Storage = (*maasStorage)(nil) func NewStorage(env *maasEnviron) storage.Storage { stor := new(maasStorage) stor.environUnlocked = env stor.maasClientUnlocked = env.getMAASClient().GetSubObject("files") return stor } // getSnapshot returns a consistent copy of a maasStorage. Use this if you // need a consistent view of the object's entire state, without having to // lock the object the whole time. // // An easy mistake to make with "defer" is to keep holding a lock without // realizing it, while you go on to block on http requests or other slow // things that don't actually require the lock. In most cases you can just // create a snapshot first (releasing the lock immediately) and then do the // rest of the work with the snapshot. func (stor *maasStorage) getSnapshot() *maasStorage { stor.Lock() defer stor.Unlock() return &maasStorage{ environUnlocked: stor.environUnlocked, maasClientUnlocked: stor.maasClientUnlocked, } } // addressFileObject creates a MAASObject pointing to a given file. // Takes out a lock on the storage object to get a consistent view. func (stor *maasStorage) addressFileObject(name string) gomaasapi.MAASObject { stor.Lock() defer stor.Unlock() return stor.maasClientUnlocked.GetSubObject(name) } // retrieveFileObject retrieves the information of the named file, including // its download URL and its contents, as a MAASObject. // // This may return many different errors, but specifically, it returns // an error that satisfies errors.IsNotFoundError if the file did not exist. // // The function takes out a lock on the storage object. func (stor *maasStorage) retrieveFileObject(name string) (gomaasapi.MAASObject, error) { obj, err := stor.addressFileObject(name).Get() if err != nil { noObj := gomaasapi.MAASObject{} serverErr, ok := err.(gomaasapi.ServerError) if ok && serverErr.StatusCode == 404 { return noObj, errors.NotFoundf("file '%s' not found", name) } msg := fmt.Errorf("could not access file '%s': %v", name, err) return noObj, msg } return obj, nil } // All filenames need to be namespaced so they are private to this environment. // This prevents different environments from interfering with each other. // We're using the agent name UUID here. func (stor *maasStorage) prefixWithPrivateNamespace(name string) string { env := stor.getSnapshot().environUnlocked prefix := env.ecfg().maasAgentName() if prefix != "" { return prefix + "-" + name } return name } // Get is specified in the StorageReader interface. func (stor *maasStorage) Get(name string) (io.ReadCloser, error) { name = stor.prefixWithPrivateNamespace(name) fileObj, err := stor.retrieveFileObject(name) if err != nil { return nil, err } data, err := fileObj.GetField("content") if err != nil { return nil, fmt.Errorf("could not extract file content for %s: %v", name, err) } buf, err := base64.StdEncoding.DecodeString(data) if err != nil { return nil, fmt.Errorf("bad data in file '%s': %v", name, err) } return ioutil.NopCloser(bytes.NewReader(buf)), nil } // extractFilenames returns the filenames from a "list" operation on the // MAAS API, sorted by name. func (stor *maasStorage) extractFilenames(listResult gomaasapi.JSONObject) ([]string, error) { privatePrefix := stor.prefixWithPrivateNamespace("") list, err := listResult.GetArray() if err != nil { return nil, err } result := make([]string, len(list)) for index, entry := range list { file, err := entry.GetMap() if err != nil { return nil, err } filename, err := file["filename"].GetString() if err != nil { return nil, err } // When listing files we need to return them without our special prefix. result[index] = strings.TrimPrefix(filename, privatePrefix) } sort.Strings(result) return result, nil } // List is specified in the StorageReader interface. func (stor *maasStorage) List(prefix string) ([]string, error) { prefix = stor.prefixWithPrivateNamespace(prefix) params := make(url.Values) params.Add("prefix", prefix) snapshot := stor.getSnapshot() obj, err := snapshot.maasClientUnlocked.CallGet("list", params) if err != nil { return nil, err } return snapshot.extractFilenames(obj) } // URL is specified in the StorageReader interface. func (stor *maasStorage) URL(name string) (string, error) { name = stor.prefixWithPrivateNamespace(name) fileObj, err := stor.retrieveFileObject(name) if err != nil { return "", err } uri, err := fileObj.GetField("anon_resource_uri") if err != nil { msg := fmt.Errorf("could not get file's download URL (may be an outdated MAAS): %s", err) return "", msg } partialURL, err := url.Parse(uri) if err != nil { return "", err } fullURL := fileObj.URL().ResolveReference(partialURL) return fullURL.String(), nil } // ConsistencyStrategy is specified in the StorageReader interface. func (stor *maasStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { // This storage backend has immediate consistency, so there's no // need to wait. One attempt should do. return utils.AttemptStrategy{} } // ShouldRetry is specified in the StorageReader interface. func (stor *maasStorage) ShouldRetry(err error) bool { return false } // Put is specified in the StorageWriter interface. func (stor *maasStorage) Put(name string, r io.Reader, length int64) error { name = stor.prefixWithPrivateNamespace(name) data, err := ioutil.ReadAll(io.LimitReader(r, length)) if err != nil { return err } params := url.Values{"filename": {name}} files := map[string][]byte{"file": data} snapshot := stor.getSnapshot() _, err = snapshot.maasClientUnlocked.CallPostFiles("add", params, files) return err } // Remove is specified in the StorageWriter interface. func (stor *maasStorage) Remove(name string) error { name = stor.prefixWithPrivateNamespace(name) // The only thing that can go wrong here, really, is that the file // does not exist. But deletion is idempotent: deleting a file that // is no longer there anyway is success, not failure. stor.getSnapshot().maasClientUnlocked.GetSubObject(name).Delete() return nil } // RemoveAll is specified in the StorageWriter interface. func (stor *maasStorage) RemoveAll() error { names, err := storage.List(stor, "") if err != nil { return err } // Remove all the objects in parallel so that we incur fewer round-trips. // If we're in danger of having hundreds of objects, // we'll want to change this to limit the number // of concurrent operations. var wg sync.WaitGroup wg.Add(len(names)) errc := make(chan error, len(names)) for _, name := range names { name := name go func() { defer wg.Done() if err := stor.Remove(name); err != nil { errc <- err } }() } wg.Wait() select { case err := <-errc: return fmt.Errorf("cannot delete all provider state: %v", err) default: } return nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/util.go����������������������������������0000644�0000153�0000161�00000004351�12321735642�024747� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "fmt" "net/url" "strings" "launchpad.net/goyaml" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/utils" ) // extractSystemId extracts the 'system_id' part from an InstanceId. // "/MAAS/api/1.0/nodes/system_id/" => "system_id" func extractSystemId(instanceId instance.Id) string { trimmed := strings.TrimRight(string(instanceId), "/") split := strings.Split(trimmed, "/") return split[len(split)-1] } // getSystemIdValues returns a url.Values object with all the 'system_ids' // from the given instanceIds stored under the key 'id'. This is used // to filter out instances when listing the nodes objects. func getSystemIdValues(instanceIds []instance.Id) url.Values { values := url.Values{} for _, instanceId := range instanceIds { values.Add("id", extractSystemId(instanceId)) } return values } // machineInfo is the structure used to pass information between the provider // and the agent running on a node. // When a node is started, the provider code creates a machineInfo object // containing information about the node being started and configures // cloudinit to get a YAML representation of that object written on the node's // filesystem during its first startup. That file is then read by the juju // agent running on the node and converted back into a machineInfo object. type machineInfo struct { Hostname string `yaml:,omitempty` } var _MAASInstanceFilename = environs.DataDir + "/MAASmachine.txt" // cloudinitRunCmd returns the shell command that, when run, will create the // "machine info" file containing the hostname of a machine. // That command is destined to be used by cloudinit. func (info *machineInfo) cloudinitRunCmd() (string, error) { yaml, err := goyaml.Marshal(info) if err != nil { return "", err } script := fmt.Sprintf(`mkdir -p %s; echo -n %s > %s`, utils.ShQuote(environs.DataDir), utils.ShQuote(string(yaml)), utils.ShQuote(_MAASInstanceFilename)) return script, nil } // load loads the "machine info" file and parse the content into the info // object. func (info *machineInfo) load() error { return utils.ReadYaml(_MAASInstanceFilename, info) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/environprovider.go�����������������������0000644�0000153�0000161�00000004402�12321735642�027222� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "errors" "github.com/juju/loggo" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/utils" ) // Logger for the MAAS provider. var logger = loggo.GetLogger("juju.provider.maas") type maasEnvironProvider struct{} var _ environs.EnvironProvider = (*maasEnvironProvider)(nil) var providerInstance maasEnvironProvider func init() { environs.RegisterProvider("maas", maasEnvironProvider{}) } func (maasEnvironProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Debugf("opening environment %q.", cfg.Name()) env, err := NewEnviron(cfg) if err != nil { return nil, err } return env, nil } var errAgentNameAlreadySet = errors.New( "maas-agent-name is already set; this should not be set by hand") func (p maasEnvironProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { attrs := cfg.UnknownAttrs() oldName, found := attrs["maas-agent-name"] if found && oldName != "" { return nil, errAgentNameAlreadySet } uuid, err := utils.NewUUID() if err != nil { return nil, err } attrs["maas-agent-name"] = uuid.String() cfg, err = cfg.Apply(attrs) if err != nil { return nil, err } return p.Open(cfg) } // Boilerplate config YAML. Don't mess with the indentation or add newlines! var boilerplateYAML = ` # https://juju.ubuntu.com/docs/config-maas.html maas: type: maas # maas-server specifies the location of the MAAS server. It must # specify the base path. # maas-server: 'http://192.168.1.1/MAAS/' # maas-oauth holds the OAuth credentials from MAAS. # maas-oauth: '<add your OAuth credentials from MAAS here>' `[1:] // BoilerplateConfig is specified in the EnvironProvider interface. func (maasEnvironProvider) BoilerplateConfig() string { return boilerplateYAML } // SecretAttrs is specified in the EnvironProvider interface. func (prov maasEnvironProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { secretAttrs := make(map[string]string) maasCfg, err := prov.newConfig(cfg) if err != nil { return nil, err } secretAttrs["maas-oauth"] = maasCfg.maasOAuth() return secretAttrs, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/storage_test.go��������������������������0000644�0000153�0000161�00000030730�12321735642�026475� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "bytes" "encoding/base64" "io/ioutil" "math/rand" "net/http" "net/url" "sync" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/gomaasapi" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/errors" ) type storageSuite struct { providerSuite } var _ = gc.Suite(&storageSuite{}) // makeStorage creates a MAAS storage object for the running test. func (s *storageSuite) makeStorage(name string) *maasStorage { maasobj := s.testMAASObject.MAASObject env := s.makeEnviron() env.name = name env.maasClientUnlocked = &maasobj return NewStorage(env).(*maasStorage) } // makeRandomBytes returns an array of arbitrary byte values. func makeRandomBytes(length int) []byte { data := make([]byte, length) for index := range data { data[index] = byte(rand.Intn(256)) } return data } // fakeStoredFile creates a file directly in the (simulated) MAAS file store. // It will contain an arbitrary amount of random data. The contents are also // returned. // // If you want properly random data here, initialize the randomizer first. // Or don't, if you want consistent (and debuggable) results. func (s *storageSuite) fakeStoredFile(stor storage.Storage, name string) gomaasapi.MAASObject { data := makeRandomBytes(rand.Intn(10)) // The filename must be prefixed with the private namespace as we're // bypassing the Put() method that would normally do that. prefixFilename := stor.(*maasStorage).prefixWithPrivateNamespace("") + name return s.testMAASObject.TestServer.NewFile(prefixFilename, data) } func (s *storageSuite) TestGetSnapshotCreatesClone(c *gc.C) { original := s.makeStorage("storage-name") snapshot := original.getSnapshot() c.Check(snapshot.environUnlocked, gc.Equals, original.environUnlocked) c.Check(snapshot.maasClientUnlocked.URL().String(), gc.Equals, original.maasClientUnlocked.URL().String()) // Snapshotting locks the original internally, but does not leave // either the original or the snapshot locked. unlockedMutexValue := sync.Mutex{} c.Check(original.Mutex, gc.Equals, unlockedMutexValue) c.Check(snapshot.Mutex, gc.Equals, unlockedMutexValue) } func (s *storageSuite) TestGetRetrievesFile(c *gc.C) { const filename = "stored-data" stor := s.makeStorage("get-retrieves-file") file := s.fakeStoredFile(stor, filename) base64Content, err := file.GetField("content") c.Assert(err, gc.IsNil) content, err := base64.StdEncoding.DecodeString(base64Content) c.Assert(err, gc.IsNil) reader, err := storage.Get(stor, filename) c.Assert(err, gc.IsNil) defer reader.Close() buf, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(len(buf), gc.Equals, len(content)) c.Check(buf, gc.DeepEquals, content) } func (s *storageSuite) TestRetrieveFileObjectReturnsFileObject(c *gc.C) { const filename = "myfile" stor := s.makeStorage("rfo-test") file := s.fakeStoredFile(stor, filename) fileURI, err := file.GetField("anon_resource_uri") c.Assert(err, gc.IsNil) fileContent, err := file.GetField("content") c.Assert(err, gc.IsNil) prefixFilename := stor.prefixWithPrivateNamespace(filename) obj, err := stor.retrieveFileObject(prefixFilename) c.Assert(err, gc.IsNil) uri, err := obj.GetField("anon_resource_uri") c.Assert(err, gc.IsNil) c.Check(uri, gc.Equals, fileURI) content, err := obj.GetField("content") c.Check(content, gc.Equals, fileContent) } func (s *storageSuite) TestRetrieveFileObjectReturnsNotFoundForMissingFile(c *gc.C) { stor := s.makeStorage("rfo-test") _, err := stor.retrieveFileObject("nonexistent-file") c.Assert(err, gc.NotNil) c.Check(err, jc.Satisfies, errors.IsNotFoundError) } func (s *storageSuite) TestRetrieveFileObjectEscapesName(c *gc.C) { const filename = "#a?b c&d%e!" data := []byte("File contents here") stor := s.makeStorage("rfo-test") err := stor.Put(filename, bytes.NewReader(data), int64(len(data))) c.Assert(err, gc.IsNil) prefixFilename := stor.prefixWithPrivateNamespace(filename) obj, err := stor.retrieveFileObject(prefixFilename) c.Assert(err, gc.IsNil) base64Content, err := obj.GetField("content") c.Assert(err, gc.IsNil) content, err := base64.StdEncoding.DecodeString(base64Content) c.Assert(err, gc.IsNil) c.Check(content, gc.DeepEquals, data) } func (s *storageSuite) TestFileContentsAreBinary(c *gc.C) { const filename = "myfile.bin" data := []byte{0, 1, 255, 2, 254, 3} stor := s.makeStorage("binary-test") err := stor.Put(filename, bytes.NewReader(data), int64(len(data))) c.Assert(err, gc.IsNil) file, err := storage.Get(stor, filename) c.Assert(err, gc.IsNil) content, err := ioutil.ReadAll(file) c.Assert(err, gc.IsNil) c.Check(content, gc.DeepEquals, data) } func (s *storageSuite) TestGetReturnsNotFoundErrorIfNotFound(c *gc.C) { const filename = "lost-data" stor := NewStorage(s.makeEnviron()) _, err := storage.Get(stor, filename) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) } func (s *storageSuite) TestListReturnsEmptyIfNoFilesStored(c *gc.C) { stor := NewStorage(s.makeEnviron()) listing, err := storage.List(stor, "") c.Assert(err, gc.IsNil) c.Check(listing, gc.DeepEquals, []string{}) } func (s *storageSuite) TestListReturnsAllFilesIfPrefixEmpty(c *gc.C) { stor := NewStorage(s.makeEnviron()) files := []string{"1a", "2b", "3c"} for _, name := range files { s.fakeStoredFile(stor, name) } listing, err := storage.List(stor, "") c.Assert(err, gc.IsNil) c.Check(listing, gc.DeepEquals, files) } func (s *storageSuite) TestListSortsResults(c *gc.C) { stor := NewStorage(s.makeEnviron()) files := []string{"4d", "1a", "3c", "2b"} for _, name := range files { s.fakeStoredFile(stor, name) } listing, err := storage.List(stor, "") c.Assert(err, gc.IsNil) c.Check(listing, gc.DeepEquals, []string{"1a", "2b", "3c", "4d"}) } func (s *storageSuite) TestListReturnsNoFilesIfNoFilesMatchPrefix(c *gc.C) { stor := NewStorage(s.makeEnviron()) s.fakeStoredFile(stor, "foo") listing, err := storage.List(stor, "bar") c.Assert(err, gc.IsNil) c.Check(listing, gc.DeepEquals, []string{}) } func (s *storageSuite) TestListReturnsOnlyFilesWithMatchingPrefix(c *gc.C) { stor := NewStorage(s.makeEnviron()) s.fakeStoredFile(stor, "abc") s.fakeStoredFile(stor, "xyz") listing, err := storage.List(stor, "x") c.Assert(err, gc.IsNil) c.Check(listing, gc.DeepEquals, []string{"xyz"}) } func (s *storageSuite) TestListMatchesPrefixOnly(c *gc.C) { stor := NewStorage(s.makeEnviron()) s.fakeStoredFile(stor, "abc") s.fakeStoredFile(stor, "xabc") listing, err := storage.List(stor, "a") c.Assert(err, gc.IsNil) c.Check(listing, gc.DeepEquals, []string{"abc"}) } func (s *storageSuite) TestListOperatesOnFlatNamespace(c *gc.C) { stor := NewStorage(s.makeEnviron()) s.fakeStoredFile(stor, "a/b/c/d") listing, err := storage.List(stor, "a/b") c.Assert(err, gc.IsNil) c.Check(listing, gc.DeepEquals, []string{"a/b/c/d"}) } // getFileAtURL requests, and returns, the file at the given URL. func getFileAtURL(fileURL string) ([]byte, error) { response, err := http.Get(fileURL) if err != nil { return nil, err } body, err := ioutil.ReadAll(response.Body) if err != nil { return nil, err } return body, nil } func (s *storageSuite) TestURLReturnsURLCorrespondingToFile(c *gc.C) { const filename = "my-file.txt" stor := NewStorage(s.makeEnviron()).(*maasStorage) file := s.fakeStoredFile(stor, filename) // The file contains an anon_resource_uri, which lacks a network part // (but will probably contain a query part). anonURL will be the // file's full URL. anonURI, err := file.GetField("anon_resource_uri") c.Assert(err, gc.IsNil) parsedURI, err := url.Parse(anonURI) c.Assert(err, gc.IsNil) anonURL := stor.maasClientUnlocked.URL().ResolveReference(parsedURI) c.Assert(err, gc.IsNil) fileURL, err := stor.URL(filename) c.Assert(err, gc.IsNil) c.Check(fileURL, gc.NotNil) c.Check(fileURL, gc.Equals, anonURL.String()) } func (s *storageSuite) TestPutStoresRetrievableFile(c *gc.C) { const filename = "broken-toaster.jpg" contents := []byte("Contents here") length := int64(len(contents)) stor := NewStorage(s.makeEnviron()) err := stor.Put(filename, bytes.NewReader(contents), length) reader, err := storage.Get(stor, filename) c.Assert(err, gc.IsNil) defer reader.Close() buf, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(buf, gc.DeepEquals, contents) } func (s *storageSuite) TestPutOverwritesFile(c *gc.C) { const filename = "foo.bar" stor := NewStorage(s.makeEnviron()) s.fakeStoredFile(stor, filename) newContents := []byte("Overwritten") err := stor.Put(filename, bytes.NewReader(newContents), int64(len(newContents))) c.Assert(err, gc.IsNil) reader, err := storage.Get(stor, filename) c.Assert(err, gc.IsNil) defer reader.Close() buf, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(len(buf), gc.Equals, len(newContents)) c.Check(buf, gc.DeepEquals, newContents) } func (s *storageSuite) TestPutStopsAtGivenLength(c *gc.C) { const filename = "xyzzyz.2.xls" const length = 5 contents := []byte("abcdefghijklmnopqrstuvwxyz") stor := NewStorage(s.makeEnviron()) err := stor.Put(filename, bytes.NewReader(contents), length) c.Assert(err, gc.IsNil) reader, err := storage.Get(stor, filename) c.Assert(err, gc.IsNil) defer reader.Close() buf, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(len(buf), gc.Equals, length) } func (s *storageSuite) TestPutToExistingFileTruncatesAtGivenLength(c *gc.C) { const filename = "a-file-which-is-mine" oldContents := []byte("abcdefghijklmnopqrstuvwxyz") newContents := []byte("xyz") stor := NewStorage(s.makeEnviron()) err := stor.Put(filename, bytes.NewReader(oldContents), int64(len(oldContents))) c.Assert(err, gc.IsNil) err = stor.Put(filename, bytes.NewReader(newContents), int64(len(newContents))) c.Assert(err, gc.IsNil) reader, err := storage.Get(stor, filename) c.Assert(err, gc.IsNil) defer reader.Close() buf, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(len(buf), gc.Equals, len(newContents)) c.Check(buf, gc.DeepEquals, newContents) } func (s *storageSuite) TestRemoveDeletesFile(c *gc.C) { const filename = "doomed.txt" stor := NewStorage(s.makeEnviron()) s.fakeStoredFile(stor, filename) err := stor.Remove(filename) c.Assert(err, gc.IsNil) _, err = storage.Get(stor, filename) c.Assert(err, jc.Satisfies, errors.IsNotFoundError) listing, err := storage.List(stor, filename) c.Assert(err, gc.IsNil) c.Assert(listing, gc.DeepEquals, []string{}) } func (s *storageSuite) TestRemoveIsIdempotent(c *gc.C) { const filename = "half-a-file" stor := NewStorage(s.makeEnviron()) s.fakeStoredFile(stor, filename) err := stor.Remove(filename) c.Assert(err, gc.IsNil) err = stor.Remove(filename) c.Assert(err, gc.IsNil) } func (s *storageSuite) TestNamesMayHaveSlashes(c *gc.C) { const filename = "name/with/slashes" content := []byte("File contents") stor := NewStorage(s.makeEnviron()) err := stor.Put(filename, bytes.NewReader(content), int64(len(content))) c.Assert(err, gc.IsNil) // There's not much we can say about the anonymous URL, except that // we get one. anonURL, err := stor.URL(filename) c.Assert(err, gc.IsNil) c.Check(anonURL, gc.Matches, "http[s]*://.*") reader, err := storage.Get(stor, filename) c.Assert(err, gc.IsNil) defer reader.Close() data, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(data, gc.DeepEquals, content) } func (s *storageSuite) TestRemoveAllDeletesAllFiles(c *gc.C) { stor := s.makeStorage("get-retrieves-file") const filename1 = "stored-data1" s.fakeStoredFile(stor, filename1) const filename2 = "stored-data2" s.fakeStoredFile(stor, filename2) err := stor.RemoveAll() c.Assert(err, gc.IsNil) listing, err := storage.List(stor, "") c.Assert(err, gc.IsNil) c.Assert(listing, gc.DeepEquals, []string{}) } func (s *storageSuite) TestprefixWithPrivateNamespacePrefixesWithAgentName(c *gc.C) { sstor := NewStorage(s.makeEnviron()) stor := sstor.(*maasStorage) agentName := stor.environUnlocked.ecfg().maasAgentName() c.Assert(agentName, gc.Not(gc.Equals), "") expectedPrefix := agentName + "-" const name = "myname" expectedResult := expectedPrefix + name c.Assert(stor.prefixWithPrivateNamespace(name), gc.Equals, expectedResult) } func (s *storageSuite) TesttprefixWithPrivateNamespaceIgnoresAgentName(c *gc.C) { sstor := NewStorage(s.makeEnviron()) stor := sstor.(*maasStorage) ecfg := stor.environUnlocked.ecfg() ecfg.attrs["maas-agent-name"] = "" const name = "myname" c.Assert(stor.prefixWithPrivateNamespace(name), gc.Equals, name) } ����������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/environprovider_test.go������������������0000644�0000153�0000161�00000006735�12321735642�030274� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "io/ioutil" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) type EnvironProviderSuite struct { providerSuite } var _ = gc.Suite(&EnvironProviderSuite{}) func (suite *EnvironProviderSuite) TestSecretAttrsReturnsSensitiveMAASAttributes(c *gc.C) { testJujuHome := c.MkDir() defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome)) const oauth = "aa:bb:cc" attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "maas", "maas-oauth": oauth, "maas-server": "http://maas.testing.invalid/maas/", }) config, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) secretAttrs, err := suite.makeEnviron().Provider().SecretAttrs(config) c.Assert(err, gc.IsNil) expectedAttrs := map[string]string{"maas-oauth": oauth} c.Check(secretAttrs, gc.DeepEquals, expectedAttrs) } func (suite *EnvironProviderSuite) TestUnknownAttrsContainAgentName(c *gc.C) { testJujuHome := c.MkDir() defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome)) attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "maas", "maas-oauth": "aa:bb:cc", "maas-server": "http://maas.testing.invalid/maas/", }) config, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) ctx := testing.Context(c) environ, err := suite.makeEnviron().Provider().Prepare(ctx, config) c.Assert(err, gc.IsNil) preparedConfig := environ.Config() unknownAttrs := preparedConfig.UnknownAttrs() uuid, ok := unknownAttrs["maas-agent-name"] c.Assert(ok, jc.IsTrue) c.Assert(uuid, jc.Satisfies, utils.IsValidUUIDString) } func (suite *EnvironProviderSuite) TestAgentNameShouldNotBeSetByHand(c *gc.C) { testJujuHome := c.MkDir() defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome)) attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "maas", "maas-oauth": "aa:bb:cc", "maas-server": "http://maas.testing.invalid/maas/", "maas-agent-name": "foobar", }) config, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) ctx := testing.Context(c) _, err = suite.makeEnviron().Provider().Prepare(ctx, config) c.Assert(err, gc.Equals, errAgentNameAlreadySet) } // create a temporary file with the given content. The file will be cleaned // up at the end of the test calling this method. func createTempFile(c *gc.C, content []byte) string { file, err := ioutil.TempFile(c.MkDir(), "") c.Assert(err, gc.IsNil) filename := file.Name() err = ioutil.WriteFile(filename, content, 0644) c.Assert(err, gc.IsNil) return filename } func (suite *EnvironProviderSuite) TestOpenReturnsNilInterfaceUponFailure(c *gc.C) { testJujuHome := c.MkDir() defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome)) const oauth = "wrongly-formatted-oauth-string" attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "maas", "maas-oauth": oauth, "maas-server": "http://maas.testing.invalid/maas/", }) config, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := suite.makeEnviron().Provider().Open(config) // When Open() fails (i.e. returns a non-nil error), it returns an // environs.Environ interface object with a nil value and a nil // type. c.Check(env, gc.Equals, nil) c.Check(err, gc.ErrorMatches, ".*malformed maas-oauth.*") } �����������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/maas_test.go�����������������������������0000644�0000153�0000161�00000003634�12321735642�025755� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( gc "launchpad.net/gocheck" "launchpad.net/gomaasapi" "launchpad.net/juju-core/environs/config" envtesting "launchpad.net/juju-core/environs/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type providerSuite struct { testbase.LoggingSuite envtesting.ToolsFixture testMAASObject *gomaasapi.TestMAASObject restoreTimeouts func() } var _ = gc.Suite(&providerSuite{}) func (s *providerSuite) SetUpSuite(c *gc.C) { s.restoreTimeouts = envtesting.PatchAttemptStrategies(&shortAttempt) s.LoggingSuite.SetUpSuite(c) TestMAASObject := gomaasapi.NewTestMAAS("1.0") s.testMAASObject = TestMAASObject restoreFinishBootstrap := envtesting.DisableFinishBootstrap() s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) } func (s *providerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) } func (s *providerSuite) TearDownTest(c *gc.C) { s.testMAASObject.TestServer.Clear() s.ToolsFixture.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *providerSuite) TearDownSuite(c *gc.C) { s.testMAASObject.Close() s.restoreTimeouts() s.LoggingSuite.TearDownSuite(c) } const exampleAgentName = "dfb69555-0bc4-4d1f-85f2-4ee390974984" var maasEnvAttrs = coretesting.Attrs{ "name": "test env", "type": "maas", "maas-oauth": "a:b:c", "maas-agent-name": exampleAgentName, } // makeEnviron creates a functional maasEnviron for a test. func (suite *providerSuite) makeEnviron() *maasEnviron { testAttrs := maasEnvAttrs testAttrs["maas-server"] = suite.testMAASObject.TestServer.URL attrs := coretesting.FakeConfig().Merge(maasEnvAttrs) cfg, err := config.New(config.NoDefaults, attrs) if err != nil { panic(err) } env, err := NewEnviron(cfg) if err != nil { panic(err) } return env } ����������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/environ.go�������������������������������0000644�0000153�0000161�00000035203�12321735776�025462� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas import ( "encoding/base64" "fmt" "net/url" "strings" "sync" "time" "launchpad.net/gomaasapi" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/cloudinit" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" ) const ( // We're using v1.0 of the MAAS API. apiVersion = "1.0" ) // A request may fail to due "eventual consistency" semantics, which // should resolve fairly quickly. A request may also fail due to a slow // state transition (for instance an instance taking a while to release // a security group after termination). The former failure mode is // dealt with by shortAttempt, the latter by LongAttempt. var shortAttempt = utils.AttemptStrategy{ Total: 5 * time.Second, Delay: 200 * time.Millisecond, } type maasEnviron struct { name string // archMutex gates access to supportedArchitectures archMutex sync.Mutex // supportedArchitectures caches the architectures // for which images can be instantiated. supportedArchitectures []string // ecfgMutex protects the *Unlocked fields below. ecfgMutex sync.Mutex ecfgUnlocked *maasEnvironConfig maasClientUnlocked *gomaasapi.MAASObject storageUnlocked storage.Storage } var _ environs.Environ = (*maasEnviron)(nil) var _ imagemetadata.SupportsCustomSources = (*maasEnviron)(nil) var _ envtools.SupportsCustomSources = (*maasEnviron)(nil) func NewEnviron(cfg *config.Config) (*maasEnviron, error) { env := new(maasEnviron) err := env.SetConfig(cfg) if err != nil { return nil, err } env.name = cfg.Name() env.storageUnlocked = NewStorage(env) return env, nil } // Name is specified in the Environ interface. func (env *maasEnviron) Name() string { return env.name } // Bootstrap is specified in the Environ interface. func (env *maasEnviron) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { return common.Bootstrap(ctx, env, cons) } // StateInfo is specified in the Environ interface. func (env *maasEnviron) StateInfo() (*state.Info, *api.Info, error) { return common.StateInfo(env) } // ecfg returns the environment's maasEnvironConfig, and protects it with a // mutex. func (env *maasEnviron) ecfg() *maasEnvironConfig { env.ecfgMutex.Lock() defer env.ecfgMutex.Unlock() return env.ecfgUnlocked } // Config is specified in the Environ interface. func (env *maasEnviron) Config() *config.Config { return env.ecfg().Config } // SetConfig is specified in the Environ interface. func (env *maasEnviron) SetConfig(cfg *config.Config) error { env.ecfgMutex.Lock() defer env.ecfgMutex.Unlock() // The new config has already been validated by itself, but now we // validate the transition from the old config to the new. var oldCfg *config.Config if env.ecfgUnlocked != nil { oldCfg = env.ecfgUnlocked.Config } cfg, err := env.Provider().Validate(cfg, oldCfg) if err != nil { return err } ecfg, err := providerInstance.newConfig(cfg) if err != nil { return err } env.ecfgUnlocked = ecfg authClient, err := gomaasapi.NewAuthenticatedClient(ecfg.maasServer(), ecfg.maasOAuth(), apiVersion) if err != nil { return err } env.maasClientUnlocked = gomaasapi.NewMAAS(*authClient) return nil } // SupportedArchitectures is specified on the EnvironCapability interface. func (env *maasEnviron) SupportedArchitectures() ([]string, error) { env.archMutex.Lock() defer env.archMutex.Unlock() if env.supportedArchitectures != nil { return env.supportedArchitectures, nil } // Create a filter to get all images from our region and for the correct stream. imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ Stream: env.Config().ImageStream(), }) var err error env.supportedArchitectures, err = common.SupportedArchitectures(env, imageConstraint) return env.supportedArchitectures, err } // getMAASClient returns a MAAS client object to use for a request, in a // lock-protected fashion. func (env *maasEnviron) getMAASClient() *gomaasapi.MAASObject { env.ecfgMutex.Lock() defer env.ecfgMutex.Unlock() return env.maasClientUnlocked } // convertConstraints converts the given constraints into an url.Values // object suitable to pass to MAAS when acquiring a node. // CpuPower is ignored because it cannot translated into something // meaningful for MAAS right now. func convertConstraints(cons constraints.Value) url.Values { params := url.Values{} if cons.Arch != nil { params.Add("arch", *cons.Arch) } if cons.CpuCores != nil { params.Add("cpu_count", fmt.Sprintf("%d", *cons.CpuCores)) } if cons.Mem != nil { params.Add("mem", fmt.Sprintf("%d", *cons.Mem)) } if cons.Tags != nil && len(*cons.Tags) > 0 { params.Add("tags", strings.Join(*cons.Tags, ",")) } // TODO(bug 1212689): ignore root-disk constraint for now. if cons.RootDisk != nil { logger.Warningf("ignoring unsupported constraint 'root-disk'") } if cons.CpuPower != nil { logger.Warningf("ignoring unsupported constraint 'cpu-power'") } return params } // addNetworks converts networks include/exclude information into // url.Values object suitable to pass to MAAS when acquiring a node. func addNetworks(params url.Values, nets environs.Networks) { // Network Inclusion/Exclusion setup if nets.IncludeNetworks != nil { for _, network_name := range nets.IncludeNetworks { params.Add("networks", network_name) } } if nets.ExcludeNetworks != nil { for _, not_network_name := range nets.ExcludeNetworks { params.Add("not_networks", not_network_name) } } } // acquireNode allocates a node from the MAAS. func (environ *maasEnviron) acquireNode(cons constraints.Value, nets environs.Networks, possibleTools tools.List) (gomaasapi.MAASObject, *tools.Tools, error) { acquireParams := convertConstraints(cons) addNetworks(acquireParams, nets) acquireParams.Add("agent_name", environ.ecfg().maasAgentName()) var result gomaasapi.JSONObject var err error for a := shortAttempt.Start(); a.Next(); { client := environ.getMAASClient().GetSubObject("nodes/") result, err = client.CallPost("acquire", acquireParams) if err == nil { break } } if err != nil { return gomaasapi.MAASObject{}, nil, err } node, err := result.GetMAASObject() if err != nil { msg := fmt.Errorf("unexpected result from 'acquire' on MAAS API: %v", err) return gomaasapi.MAASObject{}, nil, msg } tools := possibleTools[0] logger.Warningf("picked arbitrary tools %q", tools) return node, tools, nil } // startNode installs and boots a node. func (environ *maasEnviron) startNode(node gomaasapi.MAASObject, series string, userdata []byte) error { userDataParam := base64.StdEncoding.EncodeToString(userdata) params := url.Values{ "distro_series": {series}, "user_data": {userDataParam}, } // Initialize err to a non-nil value as a sentinel for the following // loop. err := fmt.Errorf("(no error)") for a := shortAttempt.Start(); a.Next() && err != nil; { _, err = node.CallPost("start", params) } return err } // createBridgeNetwork returns a string representing the upstart command to // create a bridged eth0. func createBridgeNetwork() string { return `cat > /etc/network/eth0.config << EOF iface eth0 inet manual auto br0 iface br0 inet dhcp bridge_ports eth0 EOF ` } // linkBridgeInInterfaces adds the file created by createBridgeNetwork to the // interfaces file. func linkBridgeInInterfaces() string { return `sed -i "s/iface eth0 inet dhcp/source \/etc\/network\/eth0.config/" /etc/network/interfaces` } // StartInstance is specified in the InstanceBroker interface. func (environ *maasEnviron) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { var inst *maasInstance var err error if node, tools, err := environ.acquireNode(args.Constraints, args.Networks, args.Tools); err != nil { return nil, nil, fmt.Errorf("cannot run instances: %v", err) } else { inst = &maasInstance{maasObject: &node, environ: environ} args.MachineConfig.Tools = tools } defer func() { if err != nil { if err := environ.releaseInstance(inst); err != nil { logger.Errorf("error releasing failed instance: %v", err) } } }() hostname, err := inst.DNSName() if err != nil { return nil, nil, err } if err := environs.FinishMachineConfig(args.MachineConfig, environ.Config(), args.Constraints); err != nil { return nil, nil, err } // TODO(thumper): 2013-08-28 bug 1217614 // The machine envronment config values are being moved to the agent config. // Explicitly specify that the lxc containers use the network bridge defined above. args.MachineConfig.AgentEnvironment[agent.LxcBridge] = "br0" cloudcfg, err := newCloudinitConfig(hostname) if err != nil { return nil, nil, err } userdata, err := environs.ComposeUserData(args.MachineConfig, cloudcfg) if err != nil { msg := fmt.Errorf("could not compose userdata for bootstrap node: %v", err) return nil, nil, msg } logger.Debugf("maas user data; %d bytes", len(userdata)) series := args.Tools.OneSeries() if err := environ.startNode(*inst.maasObject, series, userdata); err != nil { return nil, nil, err } logger.Debugf("started instance %q", inst.Id()) // TODO(bug 1193998) - return instance hardware characteristics as well return inst, nil, nil } // newCloudinitConfig creates a cloudinit.Config structure // suitable as a base for initialising a MAAS node. func newCloudinitConfig(hostname string) (*cloudinit.Config, error) { info := machineInfo{hostname} runCmd, err := info.cloudinitRunCmd() if err != nil { return nil, err } cloudcfg := cloudinit.New() cloudcfg.SetAptUpdate(true) cloudcfg.AddPackage("bridge-utils") cloudcfg.AddScripts( "set -xe", runCmd, "ifdown eth0", createBridgeNetwork(), linkBridgeInInterfaces(), "ifup br0", ) return cloudcfg, nil } // StartInstance is specified in the InstanceBroker interface. func (environ *maasEnviron) StopInstances(instances []instance.Instance) error { // Shortcut to exit quickly if 'instances' is an empty slice or nil. if len(instances) == 0 { return nil } // Tell MAAS to release each of the instances. If there are errors, // return only the first one (but release all instances regardless). // Note that releasing instances also turns them off. var firstErr error for _, instance := range instances { err := environ.releaseInstance(instance) if firstErr == nil { firstErr = err } } return firstErr } // releaseInstance releases a single instance. func (environ *maasEnviron) releaseInstance(inst instance.Instance) error { maasInst := inst.(*maasInstance) maasObj := maasInst.maasObject _, err := maasObj.CallPost("release", nil) if err != nil { logger.Debugf("error releasing instance %v", maasInst) } return err } // instances calls the MAAS API to list nodes. The "ids" slice is a filter for // specific instance IDs. Due to how this works in the HTTP API, an empty // "ids" matches all instances (not none as you might expect). func (environ *maasEnviron) instances(ids []instance.Id) ([]instance.Instance, error) { nodeListing := environ.getMAASClient().GetSubObject("nodes") filter := getSystemIdValues(ids) filter.Add("agent_name", environ.ecfg().maasAgentName()) listNodeObjects, err := nodeListing.CallGet("list", filter) if err != nil { return nil, err } listNodes, err := listNodeObjects.GetArray() if err != nil { return nil, err } instances := make([]instance.Instance, len(listNodes)) for index, nodeObj := range listNodes { node, err := nodeObj.GetMAASObject() if err != nil { return nil, err } instances[index] = &maasInstance{ maasObject: &node, environ: environ, } } return instances, nil } // Instances returns the instance.Instance objects corresponding to the given // slice of instance.Id. The error is ErrNoInstances if no instances // were found. func (environ *maasEnviron) Instances(ids []instance.Id) ([]instance.Instance, error) { if len(ids) == 0 { // This would be treated as "return all instances" below, so // treat it as a special case. // The interface requires us to return this particular error // if no instances were found. return nil, environs.ErrNoInstances } instances, err := environ.instances(ids) if err != nil { return nil, err } if len(instances) == 0 { return nil, environs.ErrNoInstances } idMap := make(map[instance.Id]instance.Instance) for _, instance := range instances { idMap[instance.Id()] = instance } result := make([]instance.Instance, len(ids)) for index, id := range ids { result[index] = idMap[id] } if len(instances) < len(ids) { return result, environs.ErrPartialInstances } return result, nil } // AllInstances returns all the instance.Instance in this provider. func (environ *maasEnviron) AllInstances() ([]instance.Instance, error) { return environ.instances(nil) } // Storage is defined by the Environ interface. func (env *maasEnviron) Storage() storage.Storage { env.ecfgMutex.Lock() defer env.ecfgMutex.Unlock() return env.storageUnlocked } func (environ *maasEnviron) Destroy() error { return common.Destroy(environ) } // MAAS does not do firewalling so these port methods do nothing. func (*maasEnviron) OpenPorts([]instance.Port) error { logger.Debugf("unimplemented OpenPorts() called") return nil } func (*maasEnviron) ClosePorts([]instance.Port) error { logger.Debugf("unimplemented ClosePorts() called") return nil } func (*maasEnviron) Ports() ([]instance.Port, error) { logger.Debugf("unimplemented Ports() called") return []instance.Port{}, nil } func (*maasEnviron) Provider() environs.EnvironProvider { return &providerInstance } // GetImageSources returns a list of sources which are used to search for simplestreams image metadata. func (e *maasEnviron) GetImageSources() ([]simplestreams.DataSource, error) { // Add the simplestreams source off the control bucket. return []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseImagesPath)}, nil } // GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata. func (e *maasEnviron) GetToolsSources() ([]simplestreams.DataSource, error) { // Add the simplestreams source off the control bucket. return []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath)}, nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/maas/environ_test.go��������������������������0000644�0000153�0000161�00000016471�12321735642�026517� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package maas_test import ( stdtesting "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/gomaasapi" "launchpad.net/juju-core/environs/config" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/provider/maas" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type environSuite struct { testbase.LoggingSuite envtesting.ToolsFixture testMAASObject *gomaasapi.TestMAASObject restoreTimeouts func() } var _ = gc.Suite(&environSuite{}) func TestMAAS(t *stdtesting.T) { gc.TestingT(t) } // TDOO: jam 2013-12-06 This is copied from the providerSuite which is in a // whitebox package maas. Either move that into a whitebox test so it can be // shared, or into a 'testing' package so we can use it here. func (s *environSuite) SetUpSuite(c *gc.C) { s.restoreTimeouts = envtesting.PatchAttemptStrategies(maas.ShortAttempt) s.LoggingSuite.SetUpSuite(c) TestMAASObject := gomaasapi.NewTestMAAS("1.0") s.testMAASObject = TestMAASObject } func (s *environSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) } func (s *environSuite) TearDownTest(c *gc.C) { s.testMAASObject.TestServer.Clear() s.ToolsFixture.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func (s *environSuite) TearDownSuite(c *gc.C) { s.testMAASObject.Close() s.restoreTimeouts() s.LoggingSuite.TearDownSuite(c) } func getSimpleTestConfig(c *gc.C, extraAttrs coretesting.Attrs) *config.Config { attrs := coretesting.FakeConfig() attrs["type"] = "maas" attrs["maas-server"] = "http://maas.testing.invalid" attrs["maas-oauth"] = "a:b:c" for k, v := range extraAttrs { attrs[k] = v } cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) return cfg } func (*environSuite) TestSetConfigValidatesFirst(c *gc.C) { // SetConfig() validates the config change and disallows, for example, // changes in the environment name. oldCfg := getSimpleTestConfig(c, coretesting.Attrs{"name": "old-name"}) newCfg := getSimpleTestConfig(c, coretesting.Attrs{"name": "new-name"}) env, err := maas.NewEnviron(oldCfg) c.Assert(err, gc.IsNil) // SetConfig() fails, even though both the old and the new config are // individually valid. err = env.SetConfig(newCfg) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, ".*cannot change name.*") // The old config is still in place. The new config never took effect. c.Check(env.Name(), gc.Equals, "old-name") } func (*environSuite) TestSetConfigRefusesChangingAgentName(c *gc.C) { oldCfg := getSimpleTestConfig(c, coretesting.Attrs{"maas-agent-name": "agent-one"}) newCfgTwo := getSimpleTestConfig(c, coretesting.Attrs{"maas-agent-name": "agent-two"}) env, err := maas.NewEnviron(oldCfg) c.Assert(err, gc.IsNil) // SetConfig() fails, even though both the old and the new config are // individually valid. err = env.SetConfig(newCfgTwo) c.Assert(err, gc.NotNil) c.Check(err, gc.ErrorMatches, ".*cannot change maas-agent-name.*") // The old config is still in place. The new config never took effect. c.Check(maas.MAASAgentName(env), gc.Equals, "agent-one") // It also refuses to set it to the empty string: err = env.SetConfig(getSimpleTestConfig(c, coretesting.Attrs{"maas-agent-name": ""})) c.Check(err, gc.ErrorMatches, ".*cannot change maas-agent-name.*") // And to nil err = env.SetConfig(getSimpleTestConfig(c, nil)) c.Check(err, gc.ErrorMatches, ".*cannot change maas-agent-name.*") } func (*environSuite) TestSetConfigAllowsEmptyFromNilAgentName(c *gc.C) { // bug #1256179 is that when using an older version of Juju (<1.16.2) // we didn't include maas-agent-name in the database, so it was 'nil' // in the OldConfig. However, when setting an environment, we would set // it to "" (because maasEnvironConfig.Validate ensures it is a 'valid' // string). We can't create that from NewEnviron or newConfig because // both of them Validate the contents. 'cmd/juju/environment // SetEnvironmentCommand' instead uses conn.State.EnvironConfig() which // just reads the content of the database into a map, so we just create // the map ourselves. // Even though we use 'nil' here, it actually stores it as "" because // 1.16.2 already validates the value baseCfg := getSimpleTestConfig(c, coretesting.Attrs{"maas-agent-name": ""}) c.Check(baseCfg.UnknownAttrs()["maas-agent-name"], gc.Equals, "") env, err := maas.NewEnviron(baseCfg) c.Assert(err, gc.IsNil) provider := env.Provider() attrs := coretesting.FakeConfig() // These are attrs we need to make it a valid Config, but would usually // be set by other infrastructure attrs["type"] = "maas" nilCfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) validatedConfig, err := provider.Validate(baseCfg, nilCfg) c.Assert(err, gc.IsNil) c.Check(validatedConfig.UnknownAttrs()["maas-agent-name"], gc.Equals, "") // However, you can't set it to an actual value if you haven't been using a value valueCfg := getSimpleTestConfig(c, coretesting.Attrs{"maas-agent-name": "agent-name"}) _, err = provider.Validate(valueCfg, nilCfg) c.Check(err, gc.ErrorMatches, ".*cannot change maas-agent-name.*") } func (*environSuite) TestSetConfigAllowsChangingNilAgentNameToEmptyString(c *gc.C) { oldCfg := getSimpleTestConfig(c, nil) newCfgTwo := getSimpleTestConfig(c, coretesting.Attrs{"maas-agent-name": ""}) env, err := maas.NewEnviron(oldCfg) c.Assert(err, gc.IsNil) err = env.SetConfig(newCfgTwo) c.Assert(err, gc.IsNil) c.Check(maas.MAASAgentName(env), gc.Equals, "") } func (*environSuite) TestSetConfigUpdatesConfig(c *gc.C) { origAttrs := coretesting.Attrs{ "server-name": "http://maas2.testing.invalid", "maas-oauth": "a:b:c", "admin-secret": "secret", } cfg := getSimpleTestConfig(c, origAttrs) env, err := maas.NewEnviron(cfg) c.Check(err, gc.IsNil) c.Check(env.Name(), gc.Equals, "testenv") anotherServer := "http://maas.testing.invalid" anotherOauth := "c:d:e" anotherSecret := "secret2" newAttrs := coretesting.Attrs{ "server-name": anotherServer, "maas-oauth": anotherOauth, "admin-secret": anotherSecret, } cfg2 := getSimpleTestConfig(c, newAttrs) errSetConfig := env.SetConfig(cfg2) c.Check(errSetConfig, gc.IsNil) c.Check(env.Name(), gc.Equals, "testenv") authClient, _ := gomaasapi.NewAuthenticatedClient(anotherServer, anotherOauth, maas.APIVersion) maasClient := gomaasapi.NewMAAS(*authClient) MAASServer := maas.GetMAASClient(env) c.Check(MAASServer, gc.DeepEquals, maasClient) } func (*environSuite) TestNewEnvironSetsConfig(c *gc.C) { cfg := getSimpleTestConfig(c, nil) env, err := maas.NewEnviron(cfg) c.Check(err, gc.IsNil) c.Check(env.Name(), gc.Equals, "testenv") } func (*environSuite) TestNewCloudinitConfig(c *gc.C) { cloudcfg, err := maas.NewCloudinitConfig("testing.invalid") c.Assert(err, gc.IsNil) c.Assert(cloudcfg.AptUpdate(), jc.IsTrue) c.Assert(cloudcfg.RunCmds(), gc.DeepEquals, []interface{}{ "set -xe", "mkdir -p '/var/lib/juju'; echo -n 'hostname: testing.invalid\n' > '/var/lib/juju/MAASmachine.txt'", "ifdown eth0", "cat > /etc/network/eth0.config << EOF\niface eth0 inet manual\n\nauto br0\niface br0 inet dhcp\n bridge_ports eth0\nEOF\n", `sed -i "s/iface eth0 inet dhcp/source \/etc\/network\/eth0.config/" /etc/network/interfaces`, "ifup br0", }) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/openstack/������������������������������������0000755�0000153�0000161�00000000000�12321736000�024473� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/openstack/provider.go�������������������������0000644�0000153�0000161�00000112763�12321735776�026711� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // Stub provider for OpenStack, using goose will be implemented here package openstack import ( "errors" "fmt" "io/ioutil" "net/http" "regexp" "sync" "time" "github.com/juju/loggo" "launchpad.net/goose/client" gooseerrors "launchpad.net/goose/errors" "launchpad.net/goose/identity" "launchpad.net/goose/nova" "launchpad.net/goose/swift" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/names" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" ) var logger = loggo.GetLogger("juju.provider.openstack") type environProvider struct{} var _ environs.EnvironProvider = (*environProvider)(nil) var providerInstance environProvider // Use shortAttempt to poll for short-term events. // TODO: This was kept to a long timeout because Nova needs more time than EC2. // For example, HP Cloud takes around 9.1 seconds (10 samples) to return a // BUILD(spawning) status. But storage delays are handled separately now, and // perhaps other polling attempts can time out faster. var shortAttempt = utils.AttemptStrategy{ Total: 15 * time.Second, Delay: 200 * time.Millisecond, } func init() { environs.RegisterProvider("openstack", environProvider{}) } func (p environProvider) BoilerplateConfig() string { return ` # https://juju.ubuntu.com/docs/config-openstack.html openstack: type: openstack # use-floating-ip specifies whether a floating IP address is # required to give the nodes a public IP address. Some # installations assign public IP addresses by default without # requiring a floating IP address. # # use-floating-ip: false # use-default-secgroup specifies whether new machine instances # should have the "default" Openstack security group assigned. # # use-default-secgroup: false # network specifies the network label or uuid to bring machines up # on, in the case where multiple networks exist. It may be omitted # otherwise. # # network: <your network label or uuid> # tools-metadata-url specifies the location of the Juju tools and # metadata. It defaults to the global public tools metadata # location https://streams.canonical.com/tools. # # tools-metadata-url: https://your-tools-metadata-url # image-metadata-url specifies the location of Ubuntu cloud image # metadata. It defaults to the global public image metadata # location https://cloud-images.ubuntu.com/releases. # # image-metadata-url: https://your-image-metadata-url # image-stream chooses a simplestreams stream to select OS images # from, for example daily or released images (or any other stream # available on simplestreams). # # image-stream: "released" # auth-url defaults to the value of the environment variable # OS_AUTH_URL, but can be specified here. # # auth-url: https://yourkeystoneurl:443/v2.0/ # tenant-name holds the openstack tenant name. It defaults to the # environment variable OS_TENANT_NAME. # # tenant-name: <your tenant name> # region holds the openstack region. It defaults to the # environment variable OS_REGION_NAME. # # region: <your region> # The auth-mode, username and password attributes are used for # userpass authentication (the default). # # auth-mode holds the authentication mode. For user-password # authentication, auth-mode should be "userpass" and username and # password should be set appropriately; they default to the # environment variables OS_USERNAME and OS_PASSWORD respectively. # # auth-mode: userpass # username: <your username> # password: <secret> # For key-pair authentication, auth-mode should be "keypair" and # access-key and secret-key should be set appropriately; they # default to the environment variables OS_ACCESS_KEY and # OS_SECRET_KEY respectively. # # auth-mode: keypair # access-key: <secret> # secret-key: <secret> # https://juju.ubuntu.com/docs/config-hpcloud.html hpcloud: type: openstack # use-floating-ip specifies whether a floating IP address is # required to give the nodes a public IP address. Some # installations assign public IP addresses by default without # requiring a floating IP address. # # use-floating-ip: false # use-default-secgroup specifies whether new machine instances # should have the "default" Openstack security group assigned. # # use-default-secgroup: false # tenant-name holds the openstack tenant name. In HPCloud, this is # synonymous with the project-name It defaults to the environment # variable OS_TENANT_NAME. # # tenant-name: <your tenant name> # image-stream chooses a simplestreams stream to select OS images # from, for example daily or released images (or any other stream # available on simplestreams). # # image-stream: "released" # auth-url holds the keystone url for authentication. It defaults # to the value of the environment variable OS_AUTH_URL. # # auth-url: https://region-a.geo-1.identity.hpcloudsvc.com:35357/v2.0/ # region holds the HP Cloud region (e.g. az-1.region-a.geo-1). It # defaults to the environment variable OS_REGION_NAME. # # region: <your region> # auth-mode holds the authentication mode. For user-password # authentication, auth-mode should be "userpass" and username and # password should be set appropriately; they default to the # environment variables OS_USERNAME and OS_PASSWORD respectively. # # auth-mode: userpass # username: <your_username> # password: <your_password> # For key-pair authentication, auth-mode should be "keypair" and # access-key and secret-key should be set appropriately; they # default to the environment variables OS_ACCESS_KEY and # OS_SECRET_KEY respectively. # # auth-mode: keypair # access-key: <secret> # secret-key: <secret> `[1:] } func (p environProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Infof("opening environment %q", cfg.Name()) e := new(environ) err := e.SetConfig(cfg) if err != nil { return nil, err } e.name = cfg.Name() return e, nil } func (p environProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { attrs := cfg.UnknownAttrs() if _, ok := attrs["control-bucket"]; !ok { uuid, err := utils.NewUUID() if err != nil { return nil, err } attrs["control-bucket"] = fmt.Sprintf("%x", uuid.Raw()) } cfg, err := cfg.Apply(attrs) if err != nil { return nil, err } return p.Open(cfg) } // MetadataLookupParams returns parameters which are used to query image metadata to // find matching image information. func (p environProvider) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) { if region == "" { return nil, fmt.Errorf("region must be specified") } return &simplestreams.MetadataLookupParams{ Region: region, Architectures: arch.AllSupportedArches, }, nil } func (p environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { m := make(map[string]string) ecfg, err := providerInstance.newConfig(cfg) if err != nil { return nil, err } m["username"] = ecfg.username() m["password"] = ecfg.password() m["tenant-name"] = ecfg.tenantName() return m, nil } func retryGet(uri string) (data []byte, err error) { for a := shortAttempt.Start(); a.Next(); { var resp *http.Response resp, err = http.Get(uri) if err != nil { continue } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { err = fmt.Errorf("bad http response %v", resp.Status) continue } var data []byte data, err = ioutil.ReadAll(resp.Body) if err != nil { continue } return data, nil } if err != nil { return nil, fmt.Errorf("cannot get %q: %v", uri, err) } return } type environ struct { name string // archMutex gates access to supportedArchitectures archMutex sync.Mutex // supportedArchitectures caches the architectures // for which images can be instantiated. supportedArchitectures []string ecfgMutex sync.Mutex imageBaseMutex sync.Mutex toolsBaseMutex sync.Mutex ecfgUnlocked *environConfig client client.AuthenticatingClient novaUnlocked *nova.Client storageUnlocked storage.Storage // An ordered list of sources in which to find the simplestreams index files used to // look up image ids. imageSources []simplestreams.DataSource // An ordered list of paths in which to find the simplestreams index files used to // look up tools ids. toolsSources []simplestreams.DataSource } var _ environs.Environ = (*environ)(nil) var _ imagemetadata.SupportsCustomSources = (*environ)(nil) var _ envtools.SupportsCustomSources = (*environ)(nil) var _ simplestreams.HasRegion = (*environ)(nil) type openstackInstance struct { e *environ instType *instances.InstanceType arch *string mu sync.Mutex serverDetail *nova.ServerDetail } func (inst *openstackInstance) String() string { return string(inst.Id()) } var _ instance.Instance = (*openstackInstance)(nil) func (inst *openstackInstance) Refresh() error { inst.mu.Lock() defer inst.mu.Unlock() server, err := inst.e.nova().GetServer(inst.serverDetail.Id) if err != nil { return err } inst.serverDetail = server return nil } func (inst *openstackInstance) getServerDetail() *nova.ServerDetail { inst.mu.Lock() defer inst.mu.Unlock() return inst.serverDetail } func (inst *openstackInstance) Id() instance.Id { return instance.Id(inst.getServerDetail().Id) } func (inst *openstackInstance) Status() string { return inst.getServerDetail().Status } func (inst *openstackInstance) hardwareCharacteristics() *instance.HardwareCharacteristics { hc := &instance.HardwareCharacteristics{Arch: inst.arch} if inst.instType != nil { hc.Mem = &inst.instType.Mem // openstack is special in that a 0-size root disk means that // the root disk will result in an instance with a root disk // the same size as the image that created it, so we just set // the HardwareCharacteristics to nil to signal that we don't // know what the correct size is. if inst.instType.RootDisk == 0 { hc.RootDisk = nil } else { hc.RootDisk = &inst.instType.RootDisk } hc.CpuCores = &inst.instType.CpuCores hc.CpuPower = inst.instType.CpuPower // tags not currently supported on openstack } return hc } // getAddress returns the existing server information on addresses, // but fetches the details over the api again if no addresses exist. func (inst *openstackInstance) getAddresses() (map[string][]nova.IPAddress, error) { addrs := inst.getServerDetail().Addresses if len(addrs) == 0 { server, err := inst.e.nova().GetServer(string(inst.Id())) if err != nil { return nil, err } addrs = server.Addresses } return addrs, nil } // Addresses implements instance.Addresses() returning generic address // details for the instances, and calling the openstack api if needed. func (inst *openstackInstance) Addresses() ([]instance.Address, error) { addresses, err := inst.getAddresses() if err != nil { return nil, err } return convertNovaAddresses(addresses), nil } // convertNovaAddresses returns nova addresses in generic format func convertNovaAddresses(addresses map[string][]nova.IPAddress) []instance.Address { // TODO(gz) Network ordering may be significant but is not preserved by // the map, see lp:1188126 for example. That could potentially be fixed // in goose, or left to be derived by other means. var machineAddresses []instance.Address for network, ips := range addresses { networkscope := instance.NetworkUnknown // For canonistack and hpcloud, public floating addresses may // be put in networks named something other than public. Rely // on address sanity logic to catch and mark them corectly. if network == "public" { networkscope = instance.NetworkPublic } for _, address := range ips { // Assume ipv4 unless specified otherwise addrtype := instance.Ipv4Address if address.Version == 6 { addrtype = instance.Ipv6Address } 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) } } return machineAddresses } func (inst *openstackInstance) DNSName() (string, error) { addresses, err := inst.Addresses() if err != nil { return "", err } addr := instance.SelectPublicAddress(addresses) if addr == "" { return "", instance.ErrNoDNSName } return addr, nil } func (inst *openstackInstance) WaitDNSName() (string, error) { return common.WaitDNSName(inst) } // TODO: following 30 lines nearly verbatim from environs/ec2 func (inst *openstackInstance) OpenPorts(machineId string, ports []instance.Port) error { if inst.e.Config().FirewallMode() != config.FwInstance { return fmt.Errorf("invalid firewall mode %q for opening ports on instance", inst.e.Config().FirewallMode()) } name := inst.e.machineGroupName(machineId) if err := inst.e.openPortsInGroup(name, ports); err != nil { return err } logger.Infof("opened ports in security group %s: %v", name, ports) return nil } func (inst *openstackInstance) ClosePorts(machineId string, ports []instance.Port) error { if inst.e.Config().FirewallMode() != config.FwInstance { return fmt.Errorf("invalid firewall mode %q for closing ports on instance", inst.e.Config().FirewallMode()) } name := inst.e.machineGroupName(machineId) if err := inst.e.closePortsInGroup(name, ports); err != nil { return err } logger.Infof("closed ports in security group %s: %v", name, ports) return nil } func (inst *openstackInstance) Ports(machineId string) ([]instance.Port, error) { if inst.e.Config().FirewallMode() != config.FwInstance { return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from instance", inst.e.Config().FirewallMode()) } name := inst.e.machineGroupName(machineId) return inst.e.portsInGroup(name) } func (e *environ) ecfg() *environConfig { e.ecfgMutex.Lock() ecfg := e.ecfgUnlocked e.ecfgMutex.Unlock() return ecfg } func (e *environ) nova() *nova.Client { e.ecfgMutex.Lock() nova := e.novaUnlocked e.ecfgMutex.Unlock() return nova } func (e *environ) Name() string { return e.name } // SupportedArchitectures is specified on the EnvironCapability interface. func (e *environ) SupportedArchitectures() ([]string, error) { e.archMutex.Lock() defer e.archMutex.Unlock() if e.supportedArchitectures != nil { return e.supportedArchitectures, nil } // Create a filter to get all images from our region and for the correct stream. cloudSpec, err := e.Region() if err != nil { return nil, err } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: cloudSpec, Stream: e.Config().ImageStream(), }) e.supportedArchitectures, err = common.SupportedArchitectures(e, imageConstraint) return e.supportedArchitectures, err } func (e *environ) Storage() storage.Storage { e.ecfgMutex.Lock() stor := e.storageUnlocked e.ecfgMutex.Unlock() return stor } func (e *environ) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { // The client's authentication may have been reset when finding tools if the agent-version // attribute was updated so we need to re-authenticate. This will be a no-op if already authenticated. // An authenticated client is needed for the URL() call below. err := e.client.Authenticate() if err != nil { return err } return common.Bootstrap(ctx, e, cons) } func (e *environ) StateInfo() (*state.Info, *api.Info, error) { return common.StateInfo(e) } func (e *environ) Config() *config.Config { return e.ecfg().Config } func (e *environ) authClient(ecfg *environConfig, authModeCfg AuthMode) client.AuthenticatingClient { cred := &identity.Credentials{ User: ecfg.username(), Secrets: ecfg.password(), Region: ecfg.region(), TenantName: ecfg.tenantName(), URL: ecfg.authURL(), } // authModeCfg has already been validated so we know it's one of the values below. var authMode identity.AuthMode switch authModeCfg { case AuthLegacy: authMode = identity.AuthLegacy case AuthUserPass: authMode = identity.AuthUserPass case AuthKeyPair: authMode = identity.AuthKeyPair cred.User = ecfg.accessKey() cred.Secrets = ecfg.secretKey() } newClient := client.NewClient if !ecfg.SSLHostnameVerification() { newClient = client.NewNonValidatingClient } return newClient(cred, authMode, nil) } func (e *environ) SetConfig(cfg *config.Config) error { ecfg, err := providerInstance.newConfig(cfg) if err != nil { return err } // At this point, the authentication method config value has been validated so we extract it's value here // to avoid having to validate again each time when creating the OpenStack client. var authModeCfg AuthMode e.ecfgMutex.Lock() defer e.ecfgMutex.Unlock() authModeCfg = AuthMode(ecfg.authMode()) e.ecfgUnlocked = ecfg e.client = e.authClient(ecfg, authModeCfg) e.novaUnlocked = nova.New(e.client) // create new control storage instance, existing instances continue // to reference their existing configuration. // public storage instance creation is deferred until needed since authenticated // access to the identity service is required so that any juju-tools endpoint can be used. e.storageUnlocked = &openstackstorage{ containerName: ecfg.controlBucket(), // this is possibly just a hack - if the ACL is swift.Private, // the machine won't be able to get the tools (401 error) containerACL: swift.PublicRead, swift: swift.New(e.client)} return nil } // GetImageSources returns a list of sources which are used to search for simplestreams image metadata. func (e *environ) GetImageSources() ([]simplestreams.DataSource, error) { e.imageBaseMutex.Lock() defer e.imageBaseMutex.Unlock() if e.imageSources != nil { return e.imageSources, nil } if !e.client.IsAuthenticated() { err := e.client.Authenticate() if err != nil { return nil, err } } // Add the simplestreams source off the control bucket. e.imageSources = append(e.imageSources, storage.NewStorageSimpleStreamsDataSource( "cloud storage", e.Storage(), storage.BaseImagesPath)) // Add the simplestreams base URL from keystone if it is defined. productStreamsURL, err := e.client.MakeServiceURL("product-streams", nil) if err == nil { verify := utils.VerifySSLHostnames if !e.Config().SSLHostnameVerification() { verify = utils.NoVerifySSLHostnames } source := simplestreams.NewURLDataSource("keystone catalog", productStreamsURL, verify) e.imageSources = append(e.imageSources, source) } return e.imageSources, nil } // GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata. func (e *environ) GetToolsSources() ([]simplestreams.DataSource, error) { e.toolsBaseMutex.Lock() defer e.toolsBaseMutex.Unlock() if e.toolsSources != nil { return e.toolsSources, nil } if !e.client.IsAuthenticated() { err := e.client.Authenticate() if err != nil { return nil, err } } verify := utils.VerifySSLHostnames if !e.Config().SSLHostnameVerification() { verify = utils.NoVerifySSLHostnames } // Add the simplestreams source off the control bucket. e.toolsSources = append(e.toolsSources, storage.NewStorageSimpleStreamsDataSource( "cloud storage", e.Storage(), storage.BaseToolsPath)) // Add the simplestreams base URL from keystone if it is defined. toolsURL, err := e.client.MakeServiceURL("juju-tools", nil) if err == nil { source := simplestreams.NewURLDataSource("keystone catalog", toolsURL, verify) e.toolsSources = append(e.toolsSources, source) } return e.toolsSources, nil } // TODO(gz): Move this somewhere more reusable const uuidPattern = "^([a-fA-F0-9]{8})-([a-fA-f0-9]{4})-([1-5][a-fA-f0-9]{3})-([a-fA-f0-9]{4})-([a-fA-f0-9]{12})$" var uuidRegexp = regexp.MustCompile(uuidPattern) // resolveNetwork takes either a network id or label and returns a network id func (e *environ) resolveNetwork(networkName string) (string, error) { if uuidRegexp.MatchString(networkName) { // Network id supplied, assume valid as boot will fail if not return networkName, nil } // Network label supplied, resolve to a network id networks, err := e.nova().ListNetworks() if err != nil { return "", err } var networkIds = []string{} for _, network := range networks { if network.Label == networkName { networkIds = append(networkIds, network.Id) } } switch len(networkIds) { case 1: return networkIds[0], nil case 0: return "", fmt.Errorf("No networks exist with label %q", networkName) } return "", fmt.Errorf("Multiple networks with label %q: %v", networkName, networkIds) } // allocatePublicIP tries to find an available floating IP address, or // allocates a new one, returning it, or an error func (e *environ) allocatePublicIP() (*nova.FloatingIP, error) { fips, err := e.nova().ListFloatingIPs() if err != nil { return nil, err } var newfip *nova.FloatingIP for _, fip := range fips { newfip = &fip if fip.InstanceId != nil && *fip.InstanceId != "" { // unavailable, skip newfip = nil continue } else { // unassigned, we can use it return newfip, nil } } if newfip == nil { // allocate a new IP and use it newfip, err = e.nova().AllocateFloatingIP() if err != nil { return nil, err } } return newfip, nil } // assignPublicIP tries to assign the given floating IP address to the // specified server, or returns an error. func (e *environ) assignPublicIP(fip *nova.FloatingIP, serverId string) (err error) { if fip == nil { return fmt.Errorf("cannot assign a nil public IP to %q", serverId) } if fip.InstanceId != nil && *fip.InstanceId == serverId { // IP already assigned, nothing to do return nil } // At startup nw_info is not yet cached so this may fail // temporarily while the server is being built for a := common.LongAttempt.Start(); a.Next(); { err = e.nova().AddServerFloatingIP(serverId, fip.IP) if err == nil { return nil } } return err } // StartInstance is specified in the InstanceBroker interface. func (e *environ) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { series := args.Tools.OneSeries() arches := args.Tools.Arches() spec, err := findInstanceSpec(e, &instances.InstanceConstraint{ Region: e.ecfg().region(), Series: series, Arches: arches, Constraints: args.Constraints, }) if err != nil { return nil, nil, err } tools, err := args.Tools.Match(tools.Filter{Arch: spec.Image.Arch}) if err != nil { return nil, nil, fmt.Errorf("chosen architecture %v not present in %v", spec.Image.Arch, arches) } args.MachineConfig.Tools = tools[0] if err := environs.FinishMachineConfig(args.MachineConfig, e.Config(), args.Constraints); err != nil { return nil, nil, err } userData, err := environs.ComposeUserData(args.MachineConfig, nil) if err != nil { return nil, nil, fmt.Errorf("cannot make user data: %v", err) } logger.Debugf("openstack user data; %d bytes", len(userData)) var networks = []nova.ServerNetworks{} usingNetwork := e.ecfg().network() if usingNetwork != "" { networkId, err := e.resolveNetwork(usingNetwork) if err != nil { return nil, nil, err } logger.Debugf("using network id %q", networkId) networks = append(networks, nova.ServerNetworks{NetworkId: networkId}) } withPublicIP := e.ecfg().useFloatingIP() var publicIP *nova.FloatingIP if withPublicIP { if fip, err := e.allocatePublicIP(); err != nil { return nil, nil, fmt.Errorf("cannot allocate a public IP as needed: %v", err) } else { publicIP = fip logger.Infof("allocated public IP %s", publicIP.IP) } } cfg := e.Config() groups, err := e.setUpGroups(args.MachineConfig.MachineId, cfg.StatePort(), cfg.APIPort()) if err != nil { return nil, nil, fmt.Errorf("cannot set up groups: %v", err) } var groupNames = make([]nova.SecurityGroupName, len(groups)) for i, g := range groups { groupNames[i] = nova.SecurityGroupName{g.Name} } var opts = nova.RunServerOpts{ Name: e.machineFullName(args.MachineConfig.MachineId), FlavorId: spec.InstanceType.Id, ImageId: spec.Image.Id, UserData: userData, SecurityGroupNames: groupNames, Networks: networks, } var server *nova.Entity for a := shortAttempt.Start(); a.Next(); { server, err = e.nova().RunServer(opts) if err == nil || !gooseerrors.IsNotFound(err) { break } } if err != nil { return nil, nil, fmt.Errorf("cannot run instance: %v", err) } detail, err := e.nova().GetServer(server.Id) if err != nil { return nil, nil, fmt.Errorf("cannot get started instance: %v", err) } inst := &openstackInstance{ e: e, serverDetail: detail, arch: &spec.Image.Arch, instType: &spec.InstanceType, } logger.Infof("started instance %q", inst.Id()) if withPublicIP { if err := e.assignPublicIP(publicIP, string(inst.Id())); err != nil { if err := e.terminateInstances([]instance.Id{inst.Id()}); err != nil { // ignore the failure at this stage, just log it logger.Debugf("failed to terminate instance %q: %v", inst.Id(), err) } return nil, nil, fmt.Errorf("cannot assign public address %s to instance %q: %v", publicIP.IP, inst.Id(), err) } logger.Infof("assigned public IP %s to %q", publicIP.IP, inst.Id()) } return inst, inst.hardwareCharacteristics(), nil } func (e *environ) StopInstances(insts []instance.Instance) error { ids := make([]instance.Id, len(insts)) for i, inst := range insts { instanceValue, ok := inst.(*openstackInstance) if !ok { return errors.New("Incompatible instance.Instance supplied") } ids[i] = instanceValue.Id() } logger.Debugf("terminating instances %v", ids) return e.terminateInstances(ids) } // collectInstances tries to get information on each instance id in ids. // It fills the slots in the given map for known servers with status // either ACTIVE or BUILD. Returns a list of missing ids. func (e *environ) collectInstances(ids []instance.Id, out map[instance.Id]instance.Instance) []instance.Id { var err error serversById := make(map[string]nova.ServerDetail) if len(ids) == 1 { // most common case - single instance var server *nova.ServerDetail server, err = e.nova().GetServer(string(ids[0])) if server != nil { serversById[server.Id] = *server } } else { var servers []nova.ServerDetail servers, err = e.nova().ListServersDetail(e.machinesFilter()) for _, server := range servers { serversById[server.Id] = server } } if err != nil { return ids } var missing []instance.Id for _, id := range ids { if server, found := serversById[string(id)]; found { // HPCloud uses "BUILD(spawning)" as an intermediate BUILD states once networking is available. switch server.Status { case nova.StatusActive, nova.StatusBuild, nova.StatusBuildSpawning: // TODO(wallyworld): lookup the flavor details to fill in the instance type data out[id] = &openstackInstance{e: e, serverDetail: &server} continue } } missing = append(missing, id) } return missing } func (e *environ) Instances(ids []instance.Id) ([]instance.Instance, error) { if len(ids) == 0 { return nil, nil } missing := ids found := make(map[instance.Id]instance.Instance) // Make a series of requests to cope with eventual consistency. // Each request will attempt to add more instances to the requested // set. for a := shortAttempt.Start(); a.Next(); { if missing = e.collectInstances(missing, found); len(missing) == 0 { break } } if len(found) == 0 { return nil, environs.ErrNoInstances } insts := make([]instance.Instance, len(ids)) var err error for i, id := range ids { if inst := found[id]; inst != nil { insts[i] = inst } else { err = environs.ErrPartialInstances } } return insts, err } func (e *environ) AllInstances() (insts []instance.Instance, err error) { servers, err := e.nova().ListServersDetail(e.machinesFilter()) if err != nil { return nil, err } for _, server := range servers { if server.Status == nova.StatusActive || server.Status == nova.StatusBuild { var s = server // TODO(wallyworld): lookup the flavor details to fill in the instance type data insts = append(insts, &openstackInstance{ e: e, serverDetail: &s, }) } } return insts, err } func (e *environ) Destroy() error { return common.Destroy(e) } func (e *environ) globalGroupName() string { return fmt.Sprintf("%s-global", e.jujuGroupName()) } func (e *environ) machineGroupName(machineId string) string { return fmt.Sprintf("%s-%s", e.jujuGroupName(), machineId) } func (e *environ) jujuGroupName() string { return fmt.Sprintf("juju-%s", e.name) } func (e *environ) machineFullName(machineId string) string { return fmt.Sprintf("juju-%s-%s", e.Name(), names.MachineTag(machineId)) } // machinesFilter returns a nova.Filter matching all machines in the environment. func (e *environ) machinesFilter() *nova.Filter { filter := nova.NewFilter() filter.Set(nova.FilterServer, fmt.Sprintf("juju-%s-machine-\\d*", e.Name())) return filter } func (e *environ) openPortsInGroup(name string, ports []instance.Port) error { novaclient := e.nova() group, err := novaclient.SecurityGroupByName(name) if err != nil { return err } for _, port := range ports { _, err := novaclient.CreateSecurityGroupRule(nova.RuleInfo{ ParentGroupId: group.Id, FromPort: port.Number, ToPort: port.Number, IPProtocol: port.Protocol, Cidr: "0.0.0.0/0", }) if err != nil { // TODO: if err is not rule already exists, raise? logger.Debugf("error creating security group rule: %v", err.Error()) } } return nil } func (e *environ) closePortsInGroup(name string, ports []instance.Port) error { if len(ports) == 0 { return nil } novaclient := e.nova() group, err := novaclient.SecurityGroupByName(name) if err != nil { return err } // TODO: Hey look ma, it's quadratic for _, port := range ports { for _, p := range (*group).Rules { if p.IPProtocol == nil || *p.IPProtocol != port.Protocol || p.FromPort == nil || *p.FromPort != port.Number || p.ToPort == nil || *p.ToPort != port.Number { continue } err := novaclient.DeleteSecurityGroupRule(p.Id) if err != nil { return err } break } } return nil } func (e *environ) portsInGroup(name string) (ports []instance.Port, err error) { group, err := e.nova().SecurityGroupByName(name) if err != nil { return nil, err } for _, p := range (*group).Rules { for i := *p.FromPort; i <= *p.ToPort; i++ { ports = append(ports, instance.Port{ Protocol: *p.IPProtocol, Number: i, }) } } instance.SortPorts(ports) return ports, nil } // TODO: following 30 lines nearly verbatim from environs/ec2 func (e *environ) OpenPorts(ports []instance.Port) error { if e.Config().FirewallMode() != config.FwGlobal { return fmt.Errorf("invalid firewall mode %q for opening ports on environment", e.Config().FirewallMode()) } if err := e.openPortsInGroup(e.globalGroupName(), ports); err != nil { return err } logger.Infof("opened ports in global group: %v", ports) return nil } func (e *environ) ClosePorts(ports []instance.Port) error { if e.Config().FirewallMode() != config.FwGlobal { return fmt.Errorf("invalid firewall mode %q for closing ports on environment", e.Config().FirewallMode()) } if err := e.closePortsInGroup(e.globalGroupName(), ports); err != nil { return err } logger.Infof("closed ports in global group: %v", ports) return nil } func (e *environ) Ports() ([]instance.Port, error) { if e.Config().FirewallMode() != config.FwGlobal { return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from environment", e.Config().FirewallMode()) } return e.portsInGroup(e.globalGroupName()) } func (e *environ) Provider() environs.EnvironProvider { return &providerInstance } func (e *environ) setUpGlobalGroup(groupName string, statePort, apiPort int) (nova.SecurityGroup, error) { return e.ensureGroup(groupName, []nova.RuleInfo{ { IPProtocol: "tcp", FromPort: 22, ToPort: 22, Cidr: "0.0.0.0/0", }, { IPProtocol: "tcp", FromPort: statePort, ToPort: statePort, Cidr: "0.0.0.0/0", }, { IPProtocol: "tcp", FromPort: apiPort, ToPort: apiPort, Cidr: "0.0.0.0/0", }, { IPProtocol: "tcp", FromPort: 1, ToPort: 65535, }, { IPProtocol: "udp", FromPort: 1, ToPort: 65535, }, { IPProtocol: "icmp", FromPort: -1, ToPort: -1, }, }) } // setUpGroups creates the security groups for the new machine, and // returns them. // // Instances are tagged with a group so they can be distinguished from // other instances that might be running on the same OpenStack account. // In addition, a specific machine security group is created for each // machine, so that its firewall rules can be configured per machine. // // Note: ideally we'd have a better way to determine group membership so that 2 // people that happen to share an openstack account and name their environment // "openstack" don't end up destroying each other's machines. func (e *environ) setUpGroups(machineId string, statePort, apiPort int) ([]nova.SecurityGroup, error) { jujuGroup, err := e.setUpGlobalGroup(e.jujuGroupName(), statePort, apiPort) if err != nil { return nil, err } var machineGroup nova.SecurityGroup switch e.Config().FirewallMode() { case config.FwInstance: machineGroup, err = e.ensureGroup(e.machineGroupName(machineId), nil) case config.FwGlobal: machineGroup, err = e.ensureGroup(e.globalGroupName(), nil) } if err != nil { return nil, err } groups := []nova.SecurityGroup{jujuGroup, machineGroup} if e.ecfg().useDefaultSecurityGroup() { defaultGroup, err := e.nova().SecurityGroupByName("default") if err != nil { return nil, fmt.Errorf("loading default security group: %v", err) } groups = append(groups, *defaultGroup) } return groups, nil } // zeroGroup holds the zero security group. var zeroGroup nova.SecurityGroup // ensureGroup returns the security group with name and perms. // If a group with name does not exist, one will be created. // If it exists, its permissions are set to perms. func (e *environ) ensureGroup(name string, rules []nova.RuleInfo) (nova.SecurityGroup, error) { novaClient := e.nova() // First attempt to look up an existing group by name. group, err := novaClient.SecurityGroupByName(name) if err == nil { // Group exists, so assume it is correctly set up and return it. // TODO(jam): 2013-09-18 http://pad.lv/121795 // We really should verify the group is set up correctly, // because deleting and re-creating environments can get us bad // groups (especially if they were set up under Python) return *group, nil } // Doesn't exist, so try and create it. group, err = novaClient.CreateSecurityGroup(name, "juju group") if err != nil { if !gooseerrors.IsDuplicateValue(err) { return zeroGroup, err } else { // We just tried to create a duplicate group, so load the existing group. group, err = novaClient.SecurityGroupByName(name) if err != nil { return zeroGroup, err } return *group, nil } } // The new group is created so now add the rules. group.Rules = make([]nova.SecurityGroupRule, len(rules)) for i, rule := range rules { rule.ParentGroupId = group.Id if rule.Cidr == "" { // http://pad.lv/1226996 Rules that don't have a CIDR // are meant to apply only to this group. If you don't // supply CIDR or GroupId then openstack assumes you // mean CIDR=0.0.0.0/0 rule.GroupId = &group.Id } groupRule, err := novaClient.CreateSecurityGroupRule(rule) if err != nil && !gooseerrors.IsDuplicateValue(err) { return zeroGroup, err } group.Rules[i] = *groupRule } return *group, nil } func (e *environ) terminateInstances(ids []instance.Id) error { if len(ids) == 0 { return nil } var firstErr error novaClient := e.nova() for _, id := range ids { err := novaClient.DeleteServer(string(id)) if gooseerrors.IsNotFound(err) { err = nil } if err != nil && firstErr == nil { logger.Debugf("error terminating instance %q: %v", id, err) firstErr = err } } return firstErr } // MetadataLookupParams returns parameters which are used to query simplestreams metadata. func (e *environ) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) { if region == "" { region = e.ecfg().region() } cloudSpec, err := e.cloudSpec(region) if err != nil { return nil, err } return &simplestreams.MetadataLookupParams{ Series: config.PreferredSeries(e.ecfg()), Region: cloudSpec.Region, Endpoint: cloudSpec.Endpoint, Architectures: arch.AllSupportedArches, }, nil } // Region is specified in the HasRegion interface. func (e *environ) Region() (simplestreams.CloudSpec, error) { return e.cloudSpec(e.ecfg().region()) } func (e *environ) cloudSpec(region string) (simplestreams.CloudSpec, error) { return simplestreams.CloudSpec{ Region: region, Endpoint: e.ecfg().authURL(), }, nil } �������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/openstack/export_test.go����������������������0000644�0000153�0000161�00000020700�12321735642�027414� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package openstack import ( "bytes" "fmt" "strings" "text/template" "launchpad.net/goose/errors" "launchpad.net/goose/identity" "launchpad.net/goose/nova" "launchpad.net/goose/swift" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/instance" ) // This provides the content for code accessing test:///... URLs. This allows // us to set the responses for things like the Metadata server, by pointing // metadata requests at test:///... rather than http://169.254.169.254 var testRoundTripper = &jujutest.ProxyRoundTripper{} func init() { testRoundTripper.RegisterForScheme("test") } var ( ShortAttempt = &shortAttempt StorageAttempt = &storageAttempt ) // MetadataStorage returns a Storage instance which is used to store simplestreams metadata for tests. func MetadataStorage(e environs.Environ) storage.Storage { ecfg := e.(*environ).ecfg() authModeCfg := AuthMode(ecfg.authMode()) container := "juju-dist-test" metadataStorage := &openstackstorage{ containerName: container, swift: swift.New(e.(*environ).authClient(ecfg, authModeCfg)), } // Ensure the container exists. err := metadataStorage.makeContainer(container, swift.PublicRead) if err != nil { panic(fmt.Errorf("cannot create %s container: %v", container, err)) } return metadataStorage } func InstanceAddress(addresses map[string][]nova.IPAddress) string { return instance.SelectPublicAddress(convertNovaAddresses(addresses)) } var indexData = ` { "index": { "com.ubuntu.cloud:released:openstack": { "updated": "Wed, 01 May 2013 13:31:26 +0000", "clouds": [ { "region": "{{.Region}}", "endpoint": "{{.URL}}" } ], "cloudname": "test", "datatype": "image-ids", "format": "products:1.0", "products": [ "com.ubuntu.cloud:server:12.04:amd64", "com.ubuntu.cloud:server:12.10:amd64", "com.ubuntu.cloud:server:13.04:amd64" ], "path": "image-metadata/products.json" } }, "updated": "Wed, 01 May 2013 13:31:26 +0000", "format": "index:1.0" } ` var imagesData = ` { "content_id": "com.ubuntu.cloud:released:openstack", "products": { "com.ubuntu.cloud:server:12.04:amd64": { "release": "precise", "version": "12.04", "arch": "amd64", "versions": { "20121218": { "items": { "inst1": { "root_store": "ebs", "virt": "pv", "region": "some-region", "id": "1" }, "inst2": { "root_store": "ebs", "virt": "pv", "region": "another-region", "id": "2" } }, "pubname": "ubuntu-precise-12.04-amd64-server-20121218", "label": "release" }, "20121111": { "items": { "inst3": { "root_store": "ebs", "virt": "pv", "region": "some-region", "id": "3" } }, "pubname": "ubuntu-precise-12.04-amd64-server-20121111", "label": "release" } } }, "com.ubuntu.cloud:server:12.04:ppc64": { "release": "precise", "version": "12.04", "arch": "ppc64", "versions": { "20121111": { "items": { "inst33": { "root_store": "ebs", "virt": "pv", "region": "some-region", "id": "33" } }, "pubname": "ubuntu-precise-12.04-ppc64-server-20121111", "label": "release" } } }, "com.ubuntu.cloud:server:12.10:amd64": { "release": "quantal", "version": "12.10", "arch": "amd64", "versions": { "20121218": { "items": { "inst3": { "root_store": "ebs", "virt": "pv", "region": "region-1", "id": "id-1" }, "inst4": { "root_store": "ebs", "virt": "pv", "region": "region-2", "id": "id-2" } }, "pubname": "ubuntu-quantal-12.14-amd64-server-20121218", "label": "release" } } }, "com.ubuntu.cloud:server:13.04:amd64": { "release": "raring", "version": "13.04", "arch": "amd64", "versions": { "20121218": { "items": { "inst5": { "root_store": "ebs", "virt": "pv", "region": "some-region", "id": "id-y" }, "inst6": { "root_store": "ebs", "virt": "pv", "region": "another-region", "id": "id-z" } }, "pubname": "ubuntu-raring-13.04-amd64-server-20121218", "label": "release" } } } }, "format": "products:1.0" } ` const productMetadatafile = "image-metadata/products.json" func UseTestImageData(stor storage.Storage, cred *identity.Credentials) { // Put some image metadata files into the public storage. t := template.Must(template.New("").Parse(indexData)) var metadata bytes.Buffer if err := t.Execute(&metadata, cred); err != nil { panic(fmt.Errorf("cannot generate index metdata: %v", err)) } data := metadata.Bytes() stor.Put(simplestreams.DefaultIndexPath+".json", bytes.NewReader(data), int64(len(data))) stor.Put( productMetadatafile, strings.NewReader(imagesData), int64(len(imagesData))) } func RemoveTestImageData(stor storage.Storage) { stor.Remove(simplestreams.DefaultIndexPath + ".json") stor.Remove(productMetadatafile) } // DiscardSecurityGroup cleans up a security group, it is not an error to // delete something that doesn't exist. func DiscardSecurityGroup(e environs.Environ, name string) error { env := e.(*environ) novaClient := env.nova() group, err := novaClient.SecurityGroupByName(name) if err != nil { if errors.IsNotFound(err) { // Group already deleted, done return nil } } err = novaClient.DeleteSecurityGroup(group.Id) if err != nil { return err } return nil } func FindInstanceSpec(e environs.Environ, series, arch, cons string) (spec *instances.InstanceSpec, err error) { env := e.(*environ) spec, err = findInstanceSpec(env, &instances.InstanceConstraint{ Series: series, Arches: []string{arch}, Region: env.ecfg().region(), Constraints: constraints.MustParse(cons), }) return } func ControlBucketName(e environs.Environ) string { env := e.(*environ) return env.ecfg().controlBucket() } func GetSwiftURL(e environs.Environ) (string, error) { return e.(*environ).client.MakeServiceURL("object-store", nil) } func SetUseFloatingIP(e environs.Environ, val bool) { env := e.(*environ) env.ecfg().attrs["use-floating-ip"] = val } func SetUpGlobalGroup(e environs.Environ, name string, statePort, apiPort int) (nova.SecurityGroup, error) { return e.(*environ).setUpGlobalGroup(name, statePort, apiPort) } func EnsureGroup(e environs.Environ, name string, rules []nova.RuleInfo) (nova.SecurityGroup, error) { return e.(*environ).ensureGroup(name, rules) } func CollectInstances(e environs.Environ, ids []instance.Id, out map[instance.Id]instance.Instance) []instance.Id { return e.(*environ).collectInstances(ids, out) } // ImageMetadataStorage returns a Storage object pointing where the goose // infrastructure sets up its keystone entry for image metadata func ImageMetadataStorage(e environs.Environ) storage.Storage { env := e.(*environ) return &openstackstorage{ containerName: "imagemetadata", swift: swift.New(env.client), } } // CreateCustomStorage creates a swift container and returns the Storage object // so you can put data into it. func CreateCustomStorage(e environs.Environ, containerName string) storage.Storage { env := e.(*environ) swiftClient := swift.New(env.client) if err := swiftClient.CreateContainer(containerName, swift.PublicRead); err != nil { panic(err) } return &openstackstorage{ containerName: containerName, swift: swiftClient, } } func GetNovaClient(e environs.Environ) *nova.Client { return e.(*environ).nova() } // ResolveNetwork exposes environ helper function resolveNetwork for testing func ResolveNetwork(e environs.Environ, networkName string) (string, error) { return e.(*environ).resolveNetwork(networkName) } ����������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/openstack/config_test.go����������������������0000644�0000153�0000161�00000034403�12321735642�027345� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package openstack import ( "os" "time" "github.com/juju/loggo" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type ConfigSuite struct { testbase.LoggingSuite savedVars map[string]string oldJujuHome *testing.FakeHome } // Ensure any environment variables a user may have set locally are reset. var envVars = map[string]string{ "AWS_SECRET_ACCESS_KEY": "", "EC2_SECRET_KEYS": "", "NOVA_API_KEY": "", "NOVA_PASSWORD": "", "NOVA_PROJECT_ID": "", "NOVA_REGION": "", "NOVA_USERNAME": "", "OS_ACCESS_KEY": "", "OS_AUTH_URL": "", "OS_PASSWORD": "", "OS_REGION_NAME": "", "OS_SECRET_KEY": "", "OS_TENANT_NAME": "", "OS_USERNAME": "", } var _ = gc.Suite(&ConfigSuite{}) // configTest specifies a config parsing test, checking that env when // parsed as the openstack section of a config file matches // baseConfigResult when mutated by the mutate function, or that the // parse matches the given error. type configTest struct { summary string config map[string]interface{} change map[string]interface{} expect map[string]interface{} envVars map[string]string region string controlBucket string useFloatingIP bool useDefaultSecurityGroup bool network string username string password string tenantName string authMode string authURL string accessKey string secretKey string firewallMode string err string sslHostnameVerification bool sslHostnameSet bool } type attrs map[string]interface{} func restoreEnvVars(envVars map[string]string) { for k, v := range envVars { os.Setenv(k, v) } } func (t configTest) check(c *gc.C) { attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "openstack", "control-bucket": "x", }).Merge(t.config) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) // Set environment variables if any. savedVars := make(map[string]string) if t.envVars != nil { for k, v := range t.envVars { savedVars[k] = os.Getenv(k) os.Setenv(k, v) } } defer restoreEnvVars(savedVars) e, err := environs.New(cfg) if t.change != nil { c.Assert(err, gc.IsNil) // Testing a change in configuration. var old, changed, valid *config.Config osenv := e.(*environ) old = osenv.ecfg().Config changed, err = old.Apply(t.change) c.Assert(err, gc.IsNil) // Keep err for validation below. valid, err = providerInstance.Validate(changed, old) if err == nil { err = osenv.SetConfig(valid) } } if t.err != "" { c.Check(err, gc.ErrorMatches, t.err) return } c.Assert(err, gc.IsNil) ecfg := e.(*environ).ecfg() c.Assert(ecfg.Name(), gc.Equals, "testenv") c.Assert(ecfg.controlBucket(), gc.Equals, "x") if t.region != "" { c.Assert(ecfg.region(), gc.Equals, t.region) } if t.authMode != "" { c.Assert(ecfg.authMode(), gc.Equals, t.authMode) } if t.accessKey != "" { c.Assert(ecfg.accessKey(), gc.Equals, t.accessKey) } if t.secretKey != "" { c.Assert(ecfg.secretKey(), gc.Equals, t.secretKey) } if t.username != "" { c.Assert(ecfg.username(), gc.Equals, t.username) c.Assert(ecfg.password(), gc.Equals, t.password) c.Assert(ecfg.tenantName(), gc.Equals, t.tenantName) c.Assert(ecfg.authURL(), gc.Equals, t.authURL) expected := map[string]string{ "username": t.username, "password": t.password, "tenant-name": t.tenantName, } c.Assert(err, gc.IsNil) actual, err := e.Provider().SecretAttrs(ecfg.Config) c.Assert(err, gc.IsNil) c.Assert(expected, gc.DeepEquals, actual) } if t.firewallMode != "" { c.Assert(ecfg.FirewallMode(), gc.Equals, t.firewallMode) } c.Assert(ecfg.useFloatingIP(), gc.Equals, t.useFloatingIP) c.Assert(ecfg.useDefaultSecurityGroup(), gc.Equals, t.useDefaultSecurityGroup) c.Assert(ecfg.network(), gc.Equals, t.network) // Default should be true expectedHostnameVerification := true if t.sslHostnameSet { expectedHostnameVerification = t.sslHostnameVerification } c.Assert(ecfg.SSLHostnameVerification(), gc.Equals, expectedHostnameVerification) for name, expect := range t.expect { actual, found := ecfg.UnknownAttrs()[name] c.Check(found, gc.Equals, true) c.Check(actual, gc.Equals, expect) } } func (s *ConfigSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.oldJujuHome = testing.MakeEmptyFakeHome(c) s.savedVars = make(map[string]string) for v, val := range envVars { s.savedVars[v] = os.Getenv(v) os.Setenv(v, val) } } func (s *ConfigSuite) TearDownTest(c *gc.C) { for k, v := range s.savedVars { os.Setenv(k, v) } s.oldJujuHome.Restore() s.LoggingSuite.TearDownTest(c) } var configTests = []configTest{ { summary: "setting region", config: attrs{ "region": "testreg", }, region: "testreg", }, { summary: "setting region (2)", config: attrs{ "region": "configtest", }, region: "configtest", }, { summary: "changing region", config: attrs{ "region": "configtest", }, change: attrs{ "region": "somereg", }, err: `cannot change region from "configtest" to "somereg"`, }, { summary: "invalid region", config: attrs{ "region": 666, }, err: `.*expected string, got int\(666\)`, }, { summary: "missing region in environment", envVars: map[string]string{ "OS_REGION_NAME": "", "NOVA_REGION": "", }, err: "required environment variable not set for credentials attribute: Region", }, { summary: "invalid username", config: attrs{ "username": 666, }, err: `.*expected string, got int\(666\)`, }, { summary: "missing username in environment", err: "required environment variable not set for credentials attribute: User", envVars: map[string]string{ "OS_USERNAME": "", "NOVA_USERNAME": "", }, }, { summary: "invalid password", config: attrs{ "password": 666, }, err: `.*expected string, got int\(666\)`, }, { summary: "missing password in environment", err: "required environment variable not set for credentials attribute: Secrets", envVars: map[string]string{ "OS_PASSWORD": "", "NOVA_PASSWORD": "", }, }, { summary: "invalid tenant-name", config: attrs{ "tenant-name": 666, }, err: `.*expected string, got int\(666\)`, }, { summary: "missing tenant in environment", err: "required environment variable not set for credentials attribute: TenantName", envVars: map[string]string{ "OS_TENANT_NAME": "", "NOVA_PROJECT_ID": "", }, }, { summary: "invalid auth-url type", config: attrs{ "auth-url": 666, }, err: `.*expected string, got int\(666\)`, }, { summary: "missing auth-url in environment", err: "required environment variable not set for credentials attribute: URL", envVars: map[string]string{ "OS_AUTH_URL": "", }, }, { summary: "invalid authorization mode", config: attrs{ "auth-mode": "invalid-mode", }, err: ".*invalid authorization mode.*", }, { summary: "keypair authorization mode", config: attrs{ "auth-mode": "keypair", "access-key": "MyAccessKey", "secret-key": "MySecretKey", }, authMode: "keypair", accessKey: "MyAccessKey", secretKey: "MySecretKey", }, { summary: "keypair authorization mode without access key", config: attrs{ "auth-mode": "keypair", "secret-key": "MySecretKey", }, envVars: map[string]string{ "OS_USERNAME": "", }, err: "required environment variable not set for credentials attribute: User", }, { summary: "keypair authorization mode without secret key", config: attrs{ "auth-mode": "keypair", "access-key": "MyAccessKey", }, envVars: map[string]string{ "OS_PASSWORD": "", }, err: "required environment variable not set for credentials attribute: Secrets", }, { summary: "invalid auth-url format", config: attrs{ "auth-url": "invalid", }, err: `invalid auth-url value "invalid"`, }, { summary: "invalid control-bucket", config: attrs{ "control-bucket": 666, }, err: `.*expected string, got int\(666\)`, }, { summary: "changing control-bucket", change: attrs{ "control-bucket": "new-x", }, err: `cannot change control-bucket from "x" to "new-x"`, }, { summary: "valid auth args", config: attrs{ "username": "jujuer", "password": "open sesame", "tenant-name": "juju tenant", "auth-mode": "legacy", "auth-url": "http://some/url", }, username: "jujuer", password: "open sesame", tenantName: "juju tenant", authURL: "http://some/url", authMode: string(AuthLegacy), }, { summary: "valid auth args in environment", envVars: map[string]string{ "OS_USERNAME": "jujuer", "OS_PASSWORD": "open sesame", "OS_AUTH_URL": "http://some/url", "OS_TENANT_NAME": "juju tenant", "OS_REGION_NAME": "region", }, username: "jujuer", password: "open sesame", tenantName: "juju tenant", authURL: "http://some/url", region: "region", }, { summary: "default auth mode based on environment", authMode: string(AuthUserPass), }, { summary: "default use floating ip", // Do not use floating IP's by default. useFloatingIP: false, }, { summary: "use floating ip", config: attrs{ "use-floating-ip": true, }, useFloatingIP: true, }, { summary: "default use default security group", // Do not use default security group by default. useDefaultSecurityGroup: false, }, { summary: "use default security group", config: attrs{ "use-default-secgroup": true, }, useDefaultSecurityGroup: true, }, { summary: "admin-secret given", config: attrs{ "admin-secret": "Futumpsh", }, }, { summary: "default firewall-mode", config: attrs{}, firewallMode: config.FwInstance, }, { summary: "instance firewall-mode", config: attrs{ "firewall-mode": "instance", }, firewallMode: config.FwInstance, }, { summary: "global firewall-mode", config: attrs{ "firewall-mode": "global", }, firewallMode: config.FwGlobal, }, { config: attrs{ "future": "hammerstein", }, expect: attrs{ "future": "hammerstein", }, }, { change: attrs{ "future": "hammerstein", }, expect: attrs{ "future": "hammerstein", }, }, { change: attrs{ "ssl-hostname-verification": false, }, sslHostnameVerification: false, sslHostnameSet: true, }, { change: attrs{ "ssl-hostname-verification": true, }, sslHostnameVerification: true, sslHostnameSet: true, }, { summary: "default network", network: "", }, { summary: "network", config: attrs{ "network": "a-network-label", }, network: "a-network-label", }, } func (s *ConfigSuite) TestConfig(c *gc.C) { s.setupEnvCredentials() for i, t := range configTests { c.Logf("test %d: %s (%v)", i, t.summary, t.config) t.check(c) } } func (s *ConfigSuite) TestDeprecatedAttributesRemoved(c *gc.C) { s.setupEnvCredentials() attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "openstack", "control-bucket": "x", "default-image-id": "id-1234", "default-instance-type": "big", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) // Keep err for validation below. valid, err := providerInstance.Validate(cfg, nil) c.Assert(err, gc.IsNil) // Check deprecated attributes removed. allAttrs := valid.AllAttrs() for _, attr := range []string{"default-image-id", "default-instance-type"} { _, ok := allAttrs[attr] c.Assert(ok, jc.IsFalse) } } func (s *ConfigSuite) TestPrepareInsertsUniqueControlBucket(c *gc.C) { s.setupEnvCredentials() attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "openstack", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) ctx := testing.Context(c) env0, err := providerInstance.Prepare(ctx, cfg) c.Assert(err, gc.IsNil) bucket0 := env0.(*environ).ecfg().controlBucket() c.Assert(bucket0, gc.Matches, "[a-f0-9]{32}") env1, err := providerInstance.Prepare(ctx, cfg) c.Assert(err, gc.IsNil) bucket1 := env1.(*environ).ecfg().controlBucket() c.Assert(bucket1, gc.Matches, "[a-f0-9]{32}") c.Assert(bucket1, gc.Not(gc.Equals), bucket0) } func (s *ConfigSuite) TestPrepareDoesNotTouchExistingControlBucket(c *gc.C) { s.setupEnvCredentials() attrs := testing.FakeConfig().Merge(testing.Attrs{ "type": "openstack", "control-bucket": "burblefoo", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := providerInstance.Prepare(testing.Context(c), cfg) c.Assert(err, gc.IsNil) bucket := env.(*environ).ecfg().controlBucket() c.Assert(bucket, gc.Equals, "burblefoo") } func (s *ConfigSuite) setupEnvCredentials() { os.Setenv("OS_USERNAME", "user") os.Setenv("OS_PASSWORD", "secret") os.Setenv("OS_AUTH_URL", "http://auth") os.Setenv("OS_TENANT_NAME", "sometenant") os.Setenv("OS_REGION_NAME", "region") } type ConfigDeprecationSuite struct { ConfigSuite writer *testWriter } var _ = gc.Suite(&ConfigDeprecationSuite{}) func (s *ConfigDeprecationSuite) SetUpTest(c *gc.C) { s.ConfigSuite.SetUpTest(c) } func (s *ConfigDeprecationSuite) TearDownTest(c *gc.C) { s.ConfigSuite.TearDownTest(c) } func (s *ConfigDeprecationSuite) setupLogger(c *gc.C) { var err error s.writer = &testWriter{} err = loggo.RegisterWriter("test", s.writer, loggo.WARNING) c.Assert(err, gc.IsNil) } func (s *ConfigDeprecationSuite) resetLogger(c *gc.C) { _, _, err := loggo.RemoveWriter("test") c.Assert(err, gc.IsNil) } type testWriter struct { messages []string } func (t *testWriter) Write(level loggo.Level, module, filename string, line int, timestamp time.Time, message string) { t.messages = append(t.messages, message) } func (s *ConfigDeprecationSuite) setupEnv(c *gc.C, deprecatedKey, value string) { s.setupEnvCredentials() attrs := testing.FakeConfig().Merge(testing.Attrs{ "name": "testenv", "type": "openstack", "control-bucket": "x", deprecatedKey: value, }) _, err := environs.NewFromAttrs(attrs) c.Assert(err, gc.IsNil) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/openstack/config.go���������������������������0000644�0000153�0000161�00000015110�12321735642�026300� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package openstack import ( "fmt" "net/url" "launchpad.net/goose/identity" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/schema" ) var configFields = schema.Fields{ "username": schema.String(), "password": schema.String(), "tenant-name": schema.String(), "auth-url": schema.String(), "auth-mode": schema.String(), "access-key": schema.String(), "secret-key": schema.String(), "region": schema.String(), "control-bucket": schema.String(), "use-floating-ip": schema.Bool(), "use-default-secgroup": schema.Bool(), "network": schema.String(), } var configDefaults = schema.Defaults{ "username": "", "password": "", "tenant-name": "", "auth-url": "", "auth-mode": string(AuthUserPass), "access-key": "", "secret-key": "", "region": "", "control-bucket": "", "use-floating-ip": false, "use-default-secgroup": false, "network": "", } type environConfig struct { *config.Config attrs map[string]interface{} } func (c *environConfig) region() string { return c.attrs["region"].(string) } func (c *environConfig) username() string { return c.attrs["username"].(string) } func (c *environConfig) password() string { return c.attrs["password"].(string) } func (c *environConfig) tenantName() string { return c.attrs["tenant-name"].(string) } func (c *environConfig) authURL() string { return c.attrs["auth-url"].(string) } func (c *environConfig) authMode() string { return c.attrs["auth-mode"].(string) } func (c *environConfig) accessKey() string { return c.attrs["access-key"].(string) } func (c *environConfig) secretKey() string { return c.attrs["secret-key"].(string) } func (c *environConfig) controlBucket() string { return c.attrs["control-bucket"].(string) } func (c *environConfig) useFloatingIP() bool { return c.attrs["use-floating-ip"].(bool) } func (c *environConfig) useDefaultSecurityGroup() bool { return c.attrs["use-default-secgroup"].(bool) } func (c *environConfig) network() string { return c.attrs["network"].(string) } func (p environProvider) newConfig(cfg *config.Config) (*environConfig, error) { valid, err := p.Validate(cfg, nil) if err != nil { return nil, err } return &environConfig{valid, valid.UnknownAttrs()}, nil } type AuthMode string const ( AuthKeyPair AuthMode = "keypair" AuthLegacy AuthMode = "legacy" AuthUserPass AuthMode = "userpass" ) func (p environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { // Check for valid changes for the base config values. if err := config.Validate(cfg, old); err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } ecfg := &environConfig{cfg, validated} authMode := AuthMode(ecfg.authMode()) switch authMode { case AuthKeyPair: case AuthLegacy: case AuthUserPass: default: return nil, fmt.Errorf("invalid authorization mode: %q", authMode) } if ecfg.authURL() != "" { parts, err := url.Parse(ecfg.authURL()) if err != nil || parts.Host == "" || parts.Scheme == "" { return nil, fmt.Errorf("invalid auth-url value %q", ecfg.authURL()) } } cred := identity.CredentialsFromEnv() format := "required environment variable not set for credentials attribute: %s" if authMode == AuthUserPass || authMode == AuthLegacy { if ecfg.username() == "" { if cred.User == "" { return nil, fmt.Errorf(format, "User") } ecfg.attrs["username"] = cred.User } if ecfg.password() == "" { if cred.Secrets == "" { return nil, fmt.Errorf(format, "Secrets") } ecfg.attrs["password"] = cred.Secrets } } else if authMode == AuthKeyPair { if ecfg.accessKey() == "" { if cred.User == "" { return nil, fmt.Errorf(format, "User") } ecfg.attrs["access-key"] = cred.User } if ecfg.secretKey() == "" { if cred.Secrets == "" { return nil, fmt.Errorf(format, "Secrets") } ecfg.attrs["secret-key"] = cred.Secrets } } if ecfg.authURL() == "" { if cred.URL == "" { return nil, fmt.Errorf(format, "URL") } ecfg.attrs["auth-url"] = cred.URL } if ecfg.tenantName() == "" { if cred.TenantName == "" { return nil, fmt.Errorf(format, "TenantName") } ecfg.attrs["tenant-name"] = cred.TenantName } if ecfg.region() == "" { if cred.Region == "" { return nil, fmt.Errorf(format, "Region") } ecfg.attrs["region"] = cred.Region } if old != nil { attrs := old.UnknownAttrs() if region, _ := attrs["region"].(string); ecfg.region() != region { return nil, fmt.Errorf("cannot change region from %q to %q", region, ecfg.region()) } if controlBucket, _ := attrs["control-bucket"].(string); ecfg.controlBucket() != controlBucket { return nil, fmt.Errorf("cannot change control-bucket from %q to %q", controlBucket, ecfg.controlBucket()) } } // Check for deprecated fields and log a warning. We also print to stderr to ensure the user sees the message // even if they are not running with --debug. cfgAttrs := cfg.AllAttrs() if defaultImageId := cfgAttrs["default-image-id"]; defaultImageId != nil && defaultImageId.(string) != "" { msg := fmt.Sprintf( "Config attribute %q (%v) is deprecated and ignored.\n"+ "Your cloud provider should have set up image metadata to provide the correct image id\n"+ "for your chosen series and archietcure. If this is a private Openstack deployment without\n"+ "existing image metadata, please run 'juju-metadata help' to see how suitable image"+ "metadata can be generated.", "default-image-id", defaultImageId) logger.Warningf(msg) } if defaultInstanceType := cfgAttrs["default-instance-type"]; defaultInstanceType != nil && defaultInstanceType.(string) != "" { msg := fmt.Sprintf( "Config attribute %q (%v) is deprecated and ignored.\n"+ "The correct instance flavor is determined using constraints, globally specified\n"+ "when an environment is bootstrapped, or individually when a charm is deployed.\n"+ "See 'juju help bootstrap' or 'juju help deploy'.", "default-instance-type", defaultInstanceType) logger.Warningf(msg) } // Construct a new config with the deprecated attributes removed. for _, attr := range []string{"default-image-id", "default-instance-type"} { delete(cfgAttrs, attr) delete(ecfg.attrs, attr) } for k, v := range ecfg.attrs { cfgAttrs[k] = v } return config.New(config.NoDefaults, cfgAttrs) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/openstack/storage.go��������������������������0000644�0000153�0000161�00000011703�12321735642�026503� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package openstack import ( "fmt" "io" "sync" "time" gooseerrors "launchpad.net/goose/errors" "launchpad.net/goose/swift" "launchpad.net/juju-core/environs/storage" coreerrors "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" ) // openstackstorage implements storage.Storage on an OpenStack container. type openstackstorage struct { sync.Mutex madeContainer bool containerName string containerACL swift.ACL swift *swift.Client } // makeContainer makes the environment's control container, the // place where bootstrap information and deployed charms // are stored. To avoid two round trips on every PUT operation, // we do this only once for each environ. func (s *openstackstorage) makeContainer(containerName string, containerACL swift.ACL) error { s.Lock() defer s.Unlock() if s.madeContainer { return nil } // try to make the container - CreateContainer will succeed if the container already exists. err := s.swift.CreateContainer(containerName, containerACL) if err == nil { s.madeContainer = true } return err } func (s *openstackstorage) Put(file string, r io.Reader, length int64) error { if err := s.makeContainer(s.containerName, s.containerACL); err != nil { return fmt.Errorf("cannot make Swift control container: %v", err) } err := s.swift.PutReader(s.containerName, file, r, length) if err != nil { return fmt.Errorf("cannot write file %q to control container %q: %v", file, s.containerName, err) } return nil } func (s *openstackstorage) Get(file string) (r io.ReadCloser, err error) { r, err = s.swift.GetReader(s.containerName, file) err, _ = maybeNotFound(err) if err != nil { return nil, err } return r, nil } func (s *openstackstorage) URL(name string) (string, error) { // 10 years should be good enough. expires := time.Now().AddDate(10, 0, 0) return s.swift.SignedURL(s.containerName, name, expires) } var storageAttempt = utils.AttemptStrategy{ // It seems Nova needs more time than EC2. Total: 10 * time.Second, Delay: 200 * time.Millisecond, } // ConsistencyStrategy is specified in the StorageReader interface. func (s *openstackstorage) DefaultConsistencyStrategy() utils.AttemptStrategy { return storageAttempt } // ShouldRetry is specified in the StorageReader interface. func (s *openstackstorage) ShouldRetry(err error) bool { _, retry := maybeNotFound(err) return retry } func (s *openstackstorage) Remove(file string) error { err := s.swift.DeleteObject(s.containerName, file) // If we can't delete the object because the bucket doesn't // exist, then we don't care. if err, ok := maybeNotFound(err); !ok { return err } return nil } func (s *openstackstorage) List(prefix string) ([]string, error) { contents, err := s.swift.List(s.containerName, prefix, "", "", 0) if err != nil { // If the container is not found, it's not an error // because it's only created when the first // file is put. if err, ok := maybeNotFound(err); !ok { return nil, err } return nil, nil } var names []string for _, item := range contents { names = append(names, item.Name) } return names, nil } // Spawn this many goroutines to issue requests for deleting items from the // server. If only Openstack had a delete many request. const maxConcurrentDeletes = 8 // RemoveAll is specified in the StorageWriter interface. func (s *openstackstorage) RemoveAll() error { names, err := storage.List(s, "") if err != nil { return err } // Remove all the objects in parallel so as to minimize round-trips. // Start with a goroutine feeding all the names that need to be // deleted. toDelete := make(chan string) go func() { for _, name := range names { toDelete <- name } close(toDelete) }() // Now spawn up to N routines to actually issue the requests. maxRoutines := len(names) if maxConcurrentDeletes < maxRoutines { maxRoutines = maxConcurrentDeletes } var wg sync.WaitGroup wg.Add(maxRoutines) // Make a channel long enough to buffer all possible errors. errc := make(chan error, len(names)) for i := 0; i < maxRoutines; i++ { go func() { for name := range toDelete { if err := s.Remove(name); err != nil { errc <- err } } wg.Done() }() } wg.Wait() select { case err := <-errc: return fmt.Errorf("cannot delete all provider state: %v", err) default: } s.Lock() defer s.Unlock() // Even DeleteContainer fails, it won't harm if we try again - the // operation might have succeeded even if we get an error. s.madeContainer = false err = s.swift.DeleteContainer(s.containerName) err, ok := maybeNotFound(err) if ok { return nil } return err } // maybeNotFound returns a errors.NotFoundError if the root cause of the specified error is due to a file or // container not being found. func maybeNotFound(err error) (error, bool) { if err != nil && gooseerrors.IsNotFound(err) { return coreerrors.NewNotFoundError(err, ""), true } return err, false } �������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/openstack/local_test.go�����������������������0000644�0000153�0000161�00000100314�12321735776�027175� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package openstack_test import ( "bytes" "fmt" "io/ioutil" "net/http" "net/http/httptest" "net/url" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/goose/client" "launchpad.net/goose/identity" "launchpad.net/goose/nova" "launchpad.net/goose/testservices/hook" "launchpad.net/goose/testservices/openstackservice" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/openstack" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) type ProviderSuite struct { restoreTimeouts func() } var _ = gc.Suite(&ProviderSuite{}) var _ = gc.Suite(&localHTTPSServerSuite{}) func (s *ProviderSuite) SetUpTest(c *gc.C) { s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt) } func (s *ProviderSuite) TearDownTest(c *gc.C) { s.restoreTimeouts() } // Register tests to run against a test Openstack instance (service doubles). func registerLocalTests() { cred := &identity.Credentials{ User: "fred", Secrets: "secret", Region: "some-region", TenantName: "some tenant", } config := makeTestConfig(cred) config["agent-version"] = version.Current.Number.String() config["authorized-keys"] = "fakekey" gc.Suite(&localLiveSuite{ LiveTests: LiveTests{ cred: cred, LiveTests: jujutest.LiveTests{ TestConfig: config, }, }, }) gc.Suite(&localServerSuite{ cred: cred, Tests: jujutest.Tests{ TestConfig: config, }, }) } // localServer is used to spin up a local Openstack service double. type localServer struct { Server *httptest.Server Mux *http.ServeMux oldHandler http.Handler Service *openstackservice.Openstack restoreTimeouts func() UseTLS bool } func (s *localServer) start(c *gc.C, cred *identity.Credentials) { // Set up the HTTP server. if s.UseTLS { s.Server = httptest.NewTLSServer(nil) } else { s.Server = httptest.NewServer(nil) } c.Assert(s.Server, gc.NotNil) s.oldHandler = s.Server.Config.Handler s.Mux = http.NewServeMux() s.Server.Config.Handler = s.Mux cred.URL = s.Server.URL c.Logf("Started service at: %v", s.Server.URL) s.Service = openstackservice.New(cred, identity.AuthUserPass) s.Service.SetupHTTP(s.Mux) s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt) } func (s *localServer) stop() { s.Mux = nil s.Server.Config.Handler = s.oldHandler s.Server.Close() s.restoreTimeouts() } // localLiveSuite runs tests from LiveTests using an Openstack service double. type localLiveSuite struct { testbase.LoggingSuite LiveTests srv localServer } func (s *localLiveSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) c.Logf("Running live tests using openstack service test double") s.srv.start(c, s.cred) s.LiveTests.SetUpSuite(c) openstack.UseTestImageData(openstack.ImageMetadataStorage(s.Env), s.cred) restoreFinishBootstrap := envtesting.DisableFinishBootstrap() s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) } func (s *localLiveSuite) TearDownSuite(c *gc.C) { openstack.RemoveTestImageData(openstack.ImageMetadataStorage(s.Env)) s.LiveTests.TearDownSuite(c) s.srv.stop() s.LoggingSuite.TearDownSuite(c) } func (s *localLiveSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.LiveTests.SetUpTest(c) s.PatchValue(&imagemetadata.DefaultBaseURL, "") } func (s *localLiveSuite) TearDownTest(c *gc.C) { s.LiveTests.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } // localServerSuite contains tests that run against an Openstack service double. // These tests can test things that would be unreasonably slow or expensive // to test on a live Openstack server. The service double is started and stopped for // each test. type localServerSuite struct { testbase.LoggingSuite jujutest.Tests cred *identity.Credentials srv localServer toolsMetadataStorage storage.Storage imageMetadataStorage storage.Storage } func (s *localServerSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.Tests.SetUpSuite(c) restoreFinishBootstrap := envtesting.DisableFinishBootstrap() s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) c.Logf("Running local tests") } func (s *localServerSuite) TearDownSuite(c *gc.C) { s.Tests.TearDownSuite(c) s.LoggingSuite.TearDownSuite(c) } func (s *localServerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.srv.start(c, s.cred) cl := client.NewClient(s.cred, identity.AuthUserPass, nil) err := cl.Authenticate() c.Assert(err, gc.IsNil) containerURL, err := cl.MakeServiceURL("object-store", nil) c.Assert(err, gc.IsNil) s.TestConfig = s.TestConfig.Merge(coretesting.Attrs{ "tools-metadata-url": containerURL + "/juju-dist-test/tools", "image-metadata-url": containerURL + "/juju-dist-test", "auth-url": s.cred.URL, }) s.Tests.SetUpTest(c) // For testing, we create a storage instance to which is uploaded tools and image metadata. env := s.Prepare(c) s.toolsMetadataStorage = openstack.MetadataStorage(env) // Put some fake metadata in place so that tests that are simply // starting instances without any need to check if those instances // are running can find the metadata. envtesting.UploadFakeTools(c, s.toolsMetadataStorage) s.imageMetadataStorage = openstack.ImageMetadataStorage(env) openstack.UseTestImageData(s.imageMetadataStorage, s.cred) } func (s *localServerSuite) TearDownTest(c *gc.C) { if s.imageMetadataStorage != nil { openstack.RemoveTestImageData(s.imageMetadataStorage) } if s.toolsMetadataStorage != nil { envtesting.RemoveFakeToolsMetadata(c, s.toolsMetadataStorage) } s.Tests.TearDownTest(c) s.srv.stop() s.LoggingSuite.TearDownTest(c) } // If the bootstrap node is configured to require a public IP address, // bootstrapping fails if an address cannot be allocated. func (s *localServerSuite) TestBootstrapFailsWhenPublicIPError(c *gc.C) { cleanup := s.srv.Service.Nova.RegisterControlPoint( "addFloatingIP", func(sc hook.ServiceControl, args ...interface{}) error { return fmt.Errorf("failed on purpose") }, ) defer cleanup() // Create a config that matches s.TestConfig but with use-floating-ip set to true cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ "use-floating-ip": true, })) c.Assert(err, gc.IsNil) env, err := environs.New(cfg) c.Assert(err, gc.IsNil) err = bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.ErrorMatches, "(.|\n)*cannot allocate a public IP as needed(.|\n)*") } // If the environment is configured not to require a public IP address for nodes, // bootstrapping and starting an instance should occur without any attempt to // allocate a public address. func (s *localServerSuite) TestStartInstanceWithoutPublicIP(c *gc.C) { cleanup := s.srv.Service.Nova.RegisterControlPoint( "addFloatingIP", func(sc hook.ServiceControl, args ...interface{}) error { return fmt.Errorf("add floating IP should not have been called") }, ) defer cleanup() cleanup = s.srv.Service.Nova.RegisterControlPoint( "addServerFloatingIP", func(sc hook.ServiceControl, args ...interface{}) error { return fmt.Errorf("add server floating IP should not have been called") }, ) defer cleanup() cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ "use-floating-ip": false, })) c.Assert(err, gc.IsNil) env, err := environs.Prepare(cfg, coretesting.Context(c), s.ConfigStore) c.Assert(err, gc.IsNil) err = bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) inst, _ := testing.AssertStartInstance(c, env, "100") err = env.StopInstances([]instance.Instance{inst}) c.Assert(err, gc.IsNil) } func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) { env := s.Prepare(c) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) _, hc := testing.AssertStartInstanceWithConstraints(c, env, "100", constraints.MustParse("mem=1024")) c.Check(*hc.Arch, gc.Equals, "amd64") c.Check(*hc.Mem, gc.Equals, uint64(2048)) c.Check(*hc.CpuCores, gc.Equals, uint64(1)) c.Assert(hc.CpuPower, gc.IsNil) } func (s *localServerSuite) TestStartInstanceNetwork(c *gc.C) { cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ // A label that corresponds to a nova test service network "network": "net", })) c.Assert(err, gc.IsNil) env, err := environs.New(cfg) c.Assert(err, gc.IsNil) inst, _ := testing.AssertStartInstance(c, env, "100") err = env.StopInstances([]instance.Instance{inst}) c.Assert(err, gc.IsNil) } func (s *localServerSuite) TestStartInstanceNetworkUnknownLabel(c *gc.C) { cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ // A label that has no related network in the nova test service "network": "no-network-with-this-label", })) c.Assert(err, gc.IsNil) env, err := environs.New(cfg) c.Assert(err, gc.IsNil) inst, _, err := testing.StartInstance(env, "100") c.Check(inst, gc.IsNil) c.Assert(err, gc.ErrorMatches, "No networks exist with label .*") } func (s *localServerSuite) TestStartInstanceNetworkUnknownId(c *gc.C) { cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ // A valid UUID but no related network in the nova test service "network": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6", })) c.Assert(err, gc.IsNil) env, err := environs.New(cfg) c.Assert(err, gc.IsNil) inst, _, err := testing.StartInstance(env, "100") c.Check(inst, gc.IsNil) c.Assert(err, gc.ErrorMatches, "cannot run instance: (\\n|.)*"+ "caused by: "+ "request \\(.*/servers\\) returned unexpected status: "+ "404; error info: .*itemNotFound.*") } var instanceGathering = []struct { ids []instance.Id err error }{ {ids: []instance.Id{"id0"}}, {ids: []instance.Id{"id0", "id0"}}, {ids: []instance.Id{"id0", "id1"}}, {ids: []instance.Id{"id1", "id0"}}, {ids: []instance.Id{"id1", "id0", "id1"}}, { ids: []instance.Id{""}, err: environs.ErrNoInstances, }, { ids: []instance.Id{"", ""}, err: environs.ErrNoInstances, }, { ids: []instance.Id{"", "", ""}, err: environs.ErrNoInstances, }, { ids: []instance.Id{"id0", ""}, err: environs.ErrPartialInstances, }, { ids: []instance.Id{"", "id1"}, err: environs.ErrPartialInstances, }, { ids: []instance.Id{"id0", "id1", ""}, err: environs.ErrPartialInstances, }, { ids: []instance.Id{"id0", "", "id0"}, err: environs.ErrPartialInstances, }, { ids: []instance.Id{"id0", "id0", ""}, err: environs.ErrPartialInstances, }, { ids: []instance.Id{"", "id0", "id1"}, err: environs.ErrPartialInstances, }, } func (s *localServerSuite) TestInstanceStatus(c *gc.C) { env := s.Prepare(c) // goose's test service always returns ACTIVE state. inst, _ := testing.AssertStartInstance(c, env, "100") c.Assert(inst.Status(), gc.Equals, nova.StatusActive) err := env.StopInstances([]instance.Instance{inst}) c.Assert(err, gc.IsNil) } func (s *localServerSuite) TestInstancesGathering(c *gc.C) { env := s.Prepare(c) inst0, _ := testing.AssertStartInstance(c, env, "100") id0 := inst0.Id() inst1, _ := testing.AssertStartInstance(c, env, "101") id1 := inst1.Id() defer func() { err := env.StopInstances([]instance.Instance{inst0, inst1}) c.Assert(err, gc.IsNil) }() for i, test := range instanceGathering { c.Logf("test %d: find %v -> expect len %d, err: %v", i, test.ids, len(test.ids), test.err) ids := make([]instance.Id, len(test.ids)) for j, id := range test.ids { switch id { case "id0": ids[j] = id0 case "id1": ids[j] = id1 } } insts, err := env.Instances(ids) c.Assert(err, gc.Equals, test.err) if err == environs.ErrNoInstances { c.Assert(insts, gc.HasLen, 0) } else { c.Assert(insts, gc.HasLen, len(test.ids)) } for j, inst := range insts { if ids[j] != "" { c.Assert(inst.Id(), gc.Equals, ids[j]) } else { c.Assert(inst, gc.IsNil) } } } } func (s *localServerSuite) TestCollectInstances(c *gc.C) { env := s.Prepare(c) cleanup := s.srv.Service.Nova.RegisterControlPoint( "addServer", func(sc hook.ServiceControl, args ...interface{}) error { details := args[0].(*nova.ServerDetail) details.Status = "BUILD(networking)" return nil }, ) defer cleanup() stateInst, _ := testing.AssertStartInstance(c, env, "100") defer func() { err := env.StopInstances([]instance.Instance{stateInst}) c.Assert(err, gc.IsNil) }() found := make(map[instance.Id]instance.Instance) missing := []instance.Id{stateInst.Id()} resultMissing := openstack.CollectInstances(env, missing, found) c.Assert(resultMissing, gc.DeepEquals, missing) } func (s *localServerSuite) TestInstancesBuildSpawning(c *gc.C) { env := s.Prepare(c) // HP servers are available once they are BUILD(spawning). cleanup := s.srv.Service.Nova.RegisterControlPoint( "addServer", func(sc hook.ServiceControl, args ...interface{}) error { details := args[0].(*nova.ServerDetail) details.Status = nova.StatusBuildSpawning return nil }, ) defer cleanup() stateInst, _ := testing.AssertStartInstance(c, env, "100") defer func() { err := env.StopInstances([]instance.Instance{stateInst}) c.Assert(err, gc.IsNil) }() instances, err := env.Instances([]instance.Id{stateInst.Id()}) c.Assert(err, gc.IsNil) c.Assert(instances, gc.HasLen, 1) c.Assert(instances[0].Status(), gc.Equals, nova.StatusBuildSpawning) } // TODO (wallyworld) - this test was copied from the ec2 provider. // It should be moved to environs.jujutests.Tests. func (s *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) { env := s.Prepare(c) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) // check that the state holds the id of the bootstrap machine. stateData, err := bootstrap.LoadState(env.Storage()) c.Assert(err, gc.IsNil) c.Assert(stateData.StateInstances, gc.HasLen, 1) expectedHardware := instance.MustParseHardware("arch=amd64 cpu-cores=1 mem=2G") insts, err := env.AllInstances() c.Assert(err, gc.IsNil) c.Assert(insts, gc.HasLen, 1) c.Check(insts[0].Id(), gc.Equals, stateData.StateInstances[0]) c.Check(expectedHardware, gc.DeepEquals, stateData.Characteristics[0]) bootstrapDNS, err := insts[0].DNSName() c.Assert(err, gc.IsNil) c.Assert(bootstrapDNS, gc.Not(gc.Equals), "") // TODO(wallyworld) - 2013-03-01 bug=1137005 // The nova test double needs to be updated to support retrieving instance userData. // Until then, we can't check the cloud init script was generated correctly. // When we can, we should also check cloudinit for a non-manager node (as in the // ec2 tests). } func (s *localServerSuite) assertGetImageMetadataSources(c *gc.C, stream, officialSourcePath string) { // Create a config that matches s.TestConfig but with the specified stream. envAttrs := s.TestConfig if stream != "" { envAttrs = envAttrs.Merge(coretesting.Attrs{"image-stream": stream}) } cfg, err := config.New(config.NoDefaults, envAttrs) c.Assert(err, gc.IsNil) env, err := environs.New(cfg) c.Assert(err, gc.IsNil) sources, err := imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) c.Assert(sources, gc.HasLen, 4) var urls = make([]string, len(sources)) for i, source := range sources { url, err := source.URL("") c.Assert(err, gc.IsNil) urls[i] = url } // The image-metadata-url ends with "/juju-dist-test/". c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/"), jc.IsTrue) // The control bucket URL contains the bucket name. c.Check(strings.Contains(urls[1], openstack.ControlBucketName(env)+"/images"), jc.IsTrue) // The product-streams URL ends with "/imagemetadata". c.Check(strings.HasSuffix(urls[2], "/imagemetadata/"), jc.IsTrue) c.Assert(urls[3], gc.Equals, fmt.Sprintf("http://cloud-images.ubuntu.com/%s/", officialSourcePath)) } func (s *localServerSuite) TestGetImageMetadataSources(c *gc.C) { s.assertGetImageMetadataSources(c, "", "releases") s.assertGetImageMetadataSources(c, "released", "releases") s.assertGetImageMetadataSources(c, "daily", "daily") } func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) { env := s.Open(c) sources, err := tools.GetMetadataSources(env) c.Assert(err, gc.IsNil) c.Assert(sources, gc.HasLen, 3) var urls = make([]string, len(sources)) for i, source := range sources { url, err := source.URL("") c.Assert(err, gc.IsNil) urls[i] = url } // The tools-metadata-url ends with "/juju-dist-test/tools/". c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/tools/"), jc.IsTrue) // The control bucket URL contains the bucket name. c.Check(strings.Contains(urls[1], openstack.ControlBucketName(env)+"/tools"), jc.IsTrue) // Check that the URL from keystone parses. _, err = url.Parse(urls[2]) c.Assert(err, gc.IsNil) } func (s *localServerSuite) TestSupportedArchitectures(c *gc.C) { env := s.Open(c) a, err := env.SupportedArchitectures() c.Assert(err, gc.IsNil) c.Assert(a, gc.DeepEquals, []string{"amd64", "ppc64"}) } func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) { // Prevent falling over to the public datasource. s.PatchValue(&imagemetadata.DefaultBaseURL, "") env := s.Open(c) // An error occurs if no suitable image is found. _, err := openstack.FindInstanceSpec(env, "saucy", "amd64", "mem=1G") c.Assert(err, gc.ErrorMatches, `no "saucy" images in some-region with arches \[amd64\]`) } func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) { env := s.Open(c) params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("some-region") c.Assert(err, gc.IsNil) params.Sources, err = imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) params.Series = "raring" image_ids, _, err := imagemetadata.ValidateImageMetadata(params) c.Assert(err, gc.IsNil) c.Assert(image_ids, gc.DeepEquals, []string{"id-y"}) } func (s *localServerSuite) TestRemoveAll(c *gc.C) { env := s.Prepare(c) stor := env.Storage() for _, a := range []byte("abcdefghijklmnopqrstuvwxyz") { content := []byte{a} name := string(content) err := stor.Put(name, bytes.NewBuffer(content), int64(len(content))) c.Assert(err, gc.IsNil) } reader, err := storage.Get(stor, "a") c.Assert(err, gc.IsNil) allContent, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Assert(string(allContent), gc.Equals, "a") err = stor.RemoveAll() c.Assert(err, gc.IsNil) _, err = storage.Get(stor, "a") c.Assert(err, gc.NotNil) } func (s *localServerSuite) TestDeleteMoreThan100(c *gc.C) { env := s.Prepare(c) stor := env.Storage() // 6*26 = 156 items for _, a := range []byte("abcdef") { for _, b := range []byte("abcdefghijklmnopqrstuvwxyz") { content := []byte{a, b} name := string(content) err := stor.Put(name, bytes.NewBuffer(content), int64(len(content))) c.Assert(err, gc.IsNil) } } reader, err := storage.Get(stor, "ab") c.Assert(err, gc.IsNil) allContent, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Assert(string(allContent), gc.Equals, "ab") err = stor.RemoveAll() c.Assert(err, gc.IsNil) _, err = storage.Get(stor, "ab") c.Assert(err, gc.NotNil) } // TestEnsureGroup checks that when creating a duplicate security group, the existing group is // returned and the existing rules have been left as is. func (s *localServerSuite) TestEnsureGroup(c *gc.C) { env := s.Prepare(c) rule := []nova.RuleInfo{ { IPProtocol: "tcp", FromPort: 22, ToPort: 22, }, } assertRule := func(group nova.SecurityGroup) { c.Check(len(group.Rules), gc.Equals, 1) c.Check(*group.Rules[0].IPProtocol, gc.Equals, "tcp") c.Check(*group.Rules[0].FromPort, gc.Equals, 22) c.Check(*group.Rules[0].ToPort, gc.Equals, 22) } group, err := openstack.EnsureGroup(env, "test group", rule) c.Assert(err, gc.IsNil) c.Assert(group.Name, gc.Equals, "test group") assertRule(group) id := group.Id // Do it again and check that the existing group is returned. anotherRule := []nova.RuleInfo{ { IPProtocol: "tcp", FromPort: 1, ToPort: 65535, }, } group, err = openstack.EnsureGroup(env, "test group", anotherRule) c.Assert(err, gc.IsNil) c.Check(group.Id, gc.Equals, id) c.Assert(group.Name, gc.Equals, "test group") assertRule(group) } // localHTTPSServerSuite contains tests that run against an Openstack service // double connected on an HTTPS port with a self-signed certificate. This // service is set up and torn down for every test. This should only test // things that depend on the HTTPS connection, all other functional tests on a // local connection should be in localServerSuite type localHTTPSServerSuite struct { testbase.LoggingSuite attrs map[string]interface{} cred *identity.Credentials srv localServer env environs.Environ } func (s *localHTTPSServerSuite) createConfigAttrs(c *gc.C) map[string]interface{} { attrs := makeTestConfig(s.cred) attrs["agent-version"] = version.Current.Number.String() attrs["authorized-keys"] = "fakekey" // In order to set up and tear down the environment properly, we must // disable hostname verification attrs["ssl-hostname-verification"] = false attrs["auth-url"] = s.cred.URL // Now connect and set up test-local tools and image-metadata URLs cl := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil) err := cl.Authenticate() c.Assert(err, gc.IsNil) containerURL, err := cl.MakeServiceURL("object-store", nil) c.Assert(err, gc.IsNil) c.Check(containerURL[:8], gc.Equals, "https://") attrs["tools-metadata-url"] = containerURL + "/juju-dist-test/tools" c.Logf("Set tools-metadata-url=%q", attrs["tools-metadata-url"]) attrs["image-metadata-url"] = containerURL + "/juju-dist-test" c.Logf("Set image-metadata-url=%q", attrs["image-metadata-url"]) return attrs } func (s *localHTTPSServerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.srv.UseTLS = true cred := &identity.Credentials{ User: "fred", Secrets: "secret", Region: "some-region", TenantName: "some tenant", } // Note: start() will change cred.URL to point to s.srv.Server.URL s.srv.start(c, cred) s.cred = cred attrs := s.createConfigAttrs(c) c.Assert(attrs["auth-url"].(string)[:8], gc.Equals, "https://") cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) s.env, err = environs.Prepare(cfg, coretesting.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) s.attrs = s.env.Config().AllAttrs() } func (s *localHTTPSServerSuite) TearDownTest(c *gc.C) { if s.env != nil { err := s.env.Destroy() c.Check(err, gc.IsNil) s.env = nil } s.srv.stop() s.LoggingSuite.TearDownTest(c) } func (s *localHTTPSServerSuite) TestCanUploadTools(c *gc.C) { envtesting.UploadFakeTools(c, s.env.Storage()) } func (s *localHTTPSServerSuite) TestMustDisableSSLVerify(c *gc.C) { // If you don't have ssl-hostname-verification set to false, then we // fail to connect to the environment. Copy the attrs used by SetUp and // force hostname verification. newattrs := make(map[string]interface{}, len(s.attrs)) for k, v := range s.attrs { newattrs[k] = v } newattrs["ssl-hostname-verification"] = true env, err := environs.NewFromAttrs(newattrs) c.Assert(err, gc.IsNil) err = env.Storage().Put("test-name", strings.NewReader("content"), 7) c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority") // However, it works just fine if you use the one with the credentials set err = s.env.Storage().Put("test-name", strings.NewReader("content"), 7) c.Assert(err, gc.IsNil) _, err = env.Storage().Get("test-name") c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority") reader, err := s.env.Storage().Get("test-name") c.Assert(err, gc.IsNil) contents, err := ioutil.ReadAll(reader) c.Assert(string(contents), gc.Equals, "content") } func (s *localHTTPSServerSuite) TestCanBootstrap(c *gc.C) { restoreFinishBootstrap := envtesting.DisableFinishBootstrap() defer restoreFinishBootstrap() // For testing, we create a storage instance to which is uploaded tools and image metadata. metadataStorage := openstack.MetadataStorage(s.env) url, err := metadataStorage.URL("") c.Assert(err, gc.IsNil) c.Logf("Generating fake tools for: %v", url) envtesting.UploadFakeTools(c, metadataStorage) defer envtesting.RemoveFakeTools(c, metadataStorage) openstack.UseTestImageData(metadataStorage, s.cred) defer openstack.RemoveTestImageData(metadataStorage) err = bootstrap.Bootstrap(coretesting.Context(c), s.env, constraints.Value{}) c.Assert(err, gc.IsNil) } func (s *localHTTPSServerSuite) TestFetchFromImageMetadataSources(c *gc.C) { // Setup a custom URL for image metadata customStorage := openstack.CreateCustomStorage(s.env, "custom-metadata") customURL, err := customStorage.URL("") c.Assert(err, gc.IsNil) c.Check(customURL[:8], gc.Equals, "https://") config, err := s.env.Config().Apply( map[string]interface{}{"image-metadata-url": customURL}, ) c.Assert(err, gc.IsNil) err = s.env.SetConfig(config) c.Assert(err, gc.IsNil) sources, err := imagemetadata.GetMetadataSources(s.env) c.Assert(err, gc.IsNil) c.Assert(sources, gc.HasLen, 4) // Make sure there is something to download from each location private := "private-content" err = s.env.Storage().Put("images/"+private, bytes.NewBufferString(private), int64(len(private))) c.Assert(err, gc.IsNil) metadata := "metadata-content" metadataStorage := openstack.ImageMetadataStorage(s.env) err = metadataStorage.Put(metadata, bytes.NewBufferString(metadata), int64(len(metadata))) c.Assert(err, gc.IsNil) custom := "custom-content" err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom))) c.Assert(err, gc.IsNil) // Read from the Config entry's image-metadata-url contentReader, url, err := sources[0].Fetch(custom) c.Assert(err, gc.IsNil) defer contentReader.Close() content, err := ioutil.ReadAll(contentReader) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, custom) c.Check(url[:8], gc.Equals, "https://") // Read from the private bucket contentReader, url, err = sources[1].Fetch(private) c.Assert(err, gc.IsNil) defer contentReader.Close() content, err = ioutil.ReadAll(contentReader) c.Assert(err, gc.IsNil) c.Check(string(content), gc.Equals, private) c.Check(url[:8], gc.Equals, "https://") // Check the entry we got from keystone contentReader, url, err = sources[2].Fetch(metadata) c.Assert(err, gc.IsNil) defer contentReader.Close() content, err = ioutil.ReadAll(contentReader) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, metadata) c.Check(url[:8], gc.Equals, "https://") // Verify that we are pointing at exactly where metadataStorage thinks we are metaURL, err := metadataStorage.URL(metadata) c.Assert(err, gc.IsNil) c.Check(url, gc.Equals, metaURL) } func (s *localHTTPSServerSuite) TestFetchFromToolsMetadataSources(c *gc.C) { // Setup a custom URL for image metadata customStorage := openstack.CreateCustomStorage(s.env, "custom-tools-metadata") customURL, err := customStorage.URL("") c.Assert(err, gc.IsNil) c.Check(customURL[:8], gc.Equals, "https://") config, err := s.env.Config().Apply( map[string]interface{}{"tools-metadata-url": customURL}, ) c.Assert(err, gc.IsNil) err = s.env.SetConfig(config) c.Assert(err, gc.IsNil) sources, err := tools.GetMetadataSources(s.env) c.Assert(err, gc.IsNil) c.Assert(sources, gc.HasLen, 4) // Make sure there is something to download from each location private := "private-tools-content" // The Private data storage always tacks on "tools/" to the URL stream, // so add it in here err = s.env.Storage().Put("tools/"+private, bytes.NewBufferString(private), int64(len(private))) c.Assert(err, gc.IsNil) keystone := "keystone-tools-content" // The keystone entry just points at the root of the Swift storage, and // we have to create a container to upload any data. So we just point // into a subdirectory for the data we are downloading keystoneContainer := "tools-test" keystoneStorage := openstack.CreateCustomStorage(s.env, "tools-test") err = keystoneStorage.Put(keystone, bytes.NewBufferString(keystone), int64(len(keystone))) c.Assert(err, gc.IsNil) custom := "custom-tools-content" err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom))) c.Assert(err, gc.IsNil) // Read from the Config entry's tools-metadata-url contentReader, url, err := sources[0].Fetch(custom) c.Assert(err, gc.IsNil) defer contentReader.Close() content, err := ioutil.ReadAll(contentReader) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, custom) c.Check(url[:8], gc.Equals, "https://") // Read from the private bucket contentReader, url, err = sources[1].Fetch(private) c.Assert(err, gc.IsNil) defer contentReader.Close() content, err = ioutil.ReadAll(contentReader) c.Assert(err, gc.IsNil) c.Check(string(content), gc.Equals, private) //c.Check(url[:8], gc.Equals, "https://") c.Check(strings.HasSuffix(url, "tools/"+private), jc.IsTrue) // Check the entry we got from keystone // Now fetch the data, and verify the contents. contentReader, url, err = sources[2].Fetch(keystoneContainer + "/" + keystone) c.Assert(err, gc.IsNil) defer contentReader.Close() content, err = ioutil.ReadAll(contentReader) c.Assert(err, gc.IsNil) c.Assert(string(content), gc.Equals, keystone) c.Check(url[:8], gc.Equals, "https://") keystoneURL, err := keystoneStorage.URL(keystone) c.Assert(err, gc.IsNil) c.Check(url, gc.Equals, keystoneURL) // We *don't* test Fetch for sources[3] because it points to // streams.canonical.com } func (s *localServerSuite) TestAllInstancesIgnoresOtherMachines(c *gc.C) { env := s.Prepare(c) err := bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) // Check that we see 1 instance in the environment insts, err := env.AllInstances() c.Assert(err, gc.IsNil) c.Check(insts, gc.HasLen, 1) // Now start a machine 'manually' in the same account, with a similar // but not matching name, and ensure it isn't seen by AllInstances // See bug #1257481, for how similar names were causing them to get // listed (and thus destroyed) at the wrong time existingEnvName := s.TestConfig["name"] newMachineName := fmt.Sprintf("juju-%s-2-machine-0", existingEnvName) // We grab the Nova client directly from the env, just to save time // looking all the stuff up novaClient := openstack.GetNovaClient(env) entity, err := novaClient.RunServer(nova.RunServerOpts{ Name: newMachineName, FlavorId: "1", // test service has 1,2,3 for flavor ids ImageId: "1", // UseTestImageData sets up images 1 and 2 }) c.Assert(err, gc.IsNil) c.Assert(entity, gc.NotNil) // List all servers with no filter, we should see both instances servers, err := novaClient.ListServersDetail(nova.NewFilter()) c.Assert(err, gc.IsNil) c.Assert(servers, gc.HasLen, 2) insts, err = env.AllInstances() c.Assert(err, gc.IsNil) c.Check(insts, gc.HasLen, 1) } func (s *localServerSuite) TestResolveNetworkUUID(c *gc.C) { env := s.Prepare(c) var sampleUUID = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" networkId, err := openstack.ResolveNetwork(env, sampleUUID) c.Assert(err, gc.IsNil) c.Assert(networkId, gc.Equals, sampleUUID) } func (s *localServerSuite) TestResolveNetworkLabel(c *gc.C) { env := s.Prepare(c) // For now this test has to cheat and use knowledge of goose internals var networkLabel = "net" var expectNetworkId = "1" networkId, err := openstack.ResolveNetwork(env, networkLabel) c.Assert(err, gc.IsNil) c.Assert(networkId, gc.Equals, expectNetworkId) } func (s *localServerSuite) TestResolveNetworkNotPresent(c *gc.C) { env := s.Prepare(c) var notPresentNetwork = "no-network-with-this-label" networkId, err := openstack.ResolveNetwork(env, notPresentNetwork) c.Check(networkId, gc.Equals, "") c.Assert(err, gc.ErrorMatches, `No networks exist with label "no-network-with-this-label"`) } // TODO(gz): TestResolveNetworkMultipleMatching when can inject new networks ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/openstack/live_test.go������������������������0000644�0000153�0000161�00000020673�12321735642�027043� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package openstack_test import ( "crypto/rand" "fmt" "io" "sort" gc "launchpad.net/gocheck" "launchpad.net/goose/client" "launchpad.net/goose/identity" "launchpad.net/goose/nova" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/storage" envtesting "launchpad.net/juju-core/environs/testing" jujutesting "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/openstack" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) // generate a different bucket name for each config instance, so that // we are not polluted by previous test state. func randomName() string { buf := make([]byte, 8) _, err := io.ReadFull(rand.Reader, buf) if err != nil { panic(fmt.Sprintf("error from crypto rand: %v", err)) } return fmt.Sprintf("%x", buf) } func makeTestConfig(cred *identity.Credentials) map[string]interface{} { // The following attributes hold the environment configuration // for running the OpenStack integration tests. // // This is missing keys for security reasons; set the following // environment variables to make the OpenStack testing work: // access-key: $OS_USERNAME // secret-key: $OS_PASSWORD // attrs := coretesting.FakeConfig().Merge(coretesting.Attrs{ "name": "sample-" + randomName(), "type": "openstack", "auth-mode": "userpass", "control-bucket": "juju-test-" + randomName(), "username": cred.User, "password": cred.Secrets, "region": cred.Region, "auth-url": cred.URL, "tenant-name": cred.TenantName, }) return attrs } // Register tests to run against a real Openstack instance. func registerLiveTests(cred *identity.Credentials) { config := makeTestConfig(cred) gc.Suite(&LiveTests{ cred: cred, LiveTests: jujutest.LiveTests{ TestConfig: config, Attempt: *openstack.ShortAttempt, CanOpenState: true, HasProvisioner: true, }, }) } // LiveTests contains tests that can be run against OpenStack deployments. // The deployment can be a real live instance or service doubles. // Each test runs using the same connection. type LiveTests struct { testbase.LoggingSuite jujutest.LiveTests cred *identity.Credentials metadataStorage storage.Storage } func (t *LiveTests) SetUpSuite(c *gc.C) { t.LoggingSuite.SetUpSuite(c) // Update some Config items now that we have services running. // This is setting the simplestreams urls and auth-url because that // information is set during startup of the localLiveSuite cl := client.NewClient(t.cred, identity.AuthUserPass, nil) err := cl.Authenticate() c.Assert(err, gc.IsNil) containerURL, err := cl.MakeServiceURL("object-store", nil) c.Assert(err, gc.IsNil) t.TestConfig = t.TestConfig.Merge(coretesting.Attrs{ "tools-metadata-url": containerURL + "/juju-dist-test/tools", "image-metadata-url": containerURL + "/juju-dist-test", "auth-url": t.cred.URL, }) t.LiveTests.SetUpSuite(c) // For testing, we create a storage instance to which is uploaded tools and image metadata. t.PrepareOnce(c) t.metadataStorage = openstack.MetadataStorage(t.Env) // Put some fake tools metadata in place so that tests that are simply // starting instances without any need to check if those instances // are running can find the metadata. envtesting.UploadFakeTools(c, t.metadataStorage) } func (t *LiveTests) TearDownSuite(c *gc.C) { if t.Env == nil { // This can happen if SetUpSuite fails. return } if t.metadataStorage != nil { envtesting.RemoveFakeToolsMetadata(c, t.metadataStorage) } t.LiveTests.TearDownSuite(c) t.LoggingSuite.TearDownSuite(c) } func (t *LiveTests) SetUpTest(c *gc.C) { t.LoggingSuite.SetUpTest(c) t.LiveTests.SetUpTest(c) } func (t *LiveTests) TearDownTest(c *gc.C) { t.LiveTests.TearDownTest(c) t.LoggingSuite.TearDownTest(c) } func (t *LiveTests) TestEnsureGroupSetsGroupId(c *gc.C) { t.PrepareOnce(c) rules := []nova.RuleInfo{ { // First group explicitly asks for all services IPProtocol: "tcp", FromPort: 22, ToPort: 22, Cidr: "0.0.0.0/0", }, { // Second group should only allow access from within the group IPProtocol: "tcp", FromPort: 1, ToPort: 65535, }, } groupName := "juju-test-group-" + randomName() // Make sure things are clean before we start, and clean when we are done cleanup := func() { c.Check(openstack.DiscardSecurityGroup(t.Env, groupName), gc.IsNil) } cleanup() defer cleanup() group, err := openstack.EnsureGroup(t.Env, groupName, rules) c.Assert(err, gc.IsNil) c.Check(group.Rules, gc.HasLen, 2) c.Check(*group.Rules[0].IPProtocol, gc.Equals, "tcp") c.Check(*group.Rules[0].FromPort, gc.Equals, 22) c.Check(*group.Rules[0].ToPort, gc.Equals, 22) c.Check(group.Rules[0].IPRange["cidr"], gc.Equals, "0.0.0.0/0") c.Check(group.Rules[0].Group.Name, gc.Equals, "") c.Check(group.Rules[0].Group.TenantId, gc.Equals, "") c.Check(*group.Rules[1].IPProtocol, gc.Equals, "tcp") c.Check(*group.Rules[1].FromPort, gc.Equals, 1) c.Check(*group.Rules[1].ToPort, gc.Equals, 65535) c.Check(group.Rules[1].IPRange, gc.HasLen, 0) c.Check(group.Rules[1].Group.Name, gc.Equals, groupName) c.Check(group.Rules[1].Group.TenantId, gc.Equals, group.TenantId) } func (t *LiveTests) TestSetupGlobalGroupExposesCorrectPorts(c *gc.C) { t.PrepareOnce(c) groupName := "juju-test-group-" + randomName() // Make sure things are clean before we start, and will be clean when we finish cleanup := func() { c.Check(openstack.DiscardSecurityGroup(t.Env, groupName), gc.IsNil) } cleanup() defer cleanup() statePort := 12345 // Default 37017 apiPort := 34567 // Default 17070 group, err := openstack.SetUpGlobalGroup(t.Env, groupName, statePort, apiPort) c.Assert(err, gc.IsNil) c.Assert(err, gc.IsNil) // We default to exporting 22, statePort, apiPort, and icmp/udp/tcp on // all ports to other machines inside the same group // TODO(jam): 2013-09-18 http://pad.lv/1227142 // We shouldn't be exposing the API and State ports on all the machines // that *aren't* hosting the state server. (And once we finish // client-via-API we can disable the State port as well.) stringRules := make([]string, 0, len(group.Rules)) for _, rule := range group.Rules { ruleStr := fmt.Sprintf("%s %d %d %q %q", *rule.IPProtocol, *rule.FromPort, *rule.ToPort, rule.IPRange["cidr"], rule.Group.Name, ) stringRules = append(stringRules, ruleStr) } // We don't care about the ordering, so we sort the result, and compare it. expectedRules := []string{ `tcp 22 22 "0.0.0.0/0" ""`, fmt.Sprintf(`tcp %d %d "0.0.0.0/0" ""`, statePort, statePort), fmt.Sprintf(`tcp %d %d "0.0.0.0/0" ""`, apiPort, apiPort), fmt.Sprintf(`tcp 1 65535 "" "%s"`, groupName), fmt.Sprintf(`udp 1 65535 "" "%s"`, groupName), fmt.Sprintf(`icmp -1 -1 "" "%s"`, groupName), } sort.Strings(stringRules) sort.Strings(expectedRules) c.Check(stringRules, gc.DeepEquals, expectedRules) } func (s *LiveTests) assertStartInstanceDefaultSecurityGroup(c *gc.C, useDefault bool) { attrs := s.TestConfig.Merge(coretesting.Attrs{ "name": "sample-" + randomName(), "control-bucket": "juju-test-" + randomName(), "use-default-secgroup": useDefault, }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) // Set up a test environment. env, err := environs.New(cfg) c.Assert(err, gc.IsNil) c.Assert(env, gc.NotNil) defer env.Destroy() // Bootstrap and start an instance. err = bootstrap.Bootstrap(coretesting.Context(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) inst, _ := jujutesting.AssertStartInstance(c, env, "100") // Check whether the instance has the default security group assigned. novaClient := openstack.GetNovaClient(env) groups, err := novaClient.GetServerSecurityGroups(string(inst.Id())) c.Assert(err, gc.IsNil) defaultGroupFound := false for _, group := range groups { if group.Name == "default" { defaultGroupFound = true break } } c.Assert(defaultGroupFound, gc.Equals, useDefault) } func (s *LiveTests) TestStartInstanceWithDefaultSecurityGroup(c *gc.C) { s.assertStartInstanceDefaultSecurityGroup(c, true) } func (s *LiveTests) TestStartInstanceWithoutDefaultSecurityGroup(c *gc.C) { s.assertStartInstanceDefaultSecurityGroup(c, false) } ���������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/openstack/image.go����������������������������0000644�0000153�0000161�00000003601�12321735642�026117� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package openstack import ( "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/simplestreams" ) // findInstanceSpec returns an image and instance type satisfying the constraint. // The instance type comes from querying the flavors supported by the deployment. func findInstanceSpec(e *environ, ic *instances.InstanceConstraint) (*instances.InstanceSpec, error) { // first construct all available instance types from the supported flavors. nova := e.nova() flavors, err := nova.ListFlavorsDetail() if err != nil { return nil, err } allInstanceTypes := []instances.InstanceType{} for _, flavor := range flavors { instanceType := instances.InstanceType{ Id: flavor.Id, Name: flavor.Name, Arches: ic.Arches, Mem: uint64(flavor.RAM), CpuCores: uint64(flavor.VCPUs), RootDisk: uint64(flavor.Disk * 1024), // tags not currently supported on openstack } allInstanceTypes = append(allInstanceTypes, instanceType) } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ic.Region, e.ecfg().authURL()}, Series: []string{ic.Series}, Arches: ic.Arches, Stream: e.Config().ImageStream(), }) sources, err := imagemetadata.GetMetadataSources(e) if err != nil { return nil, err } // TODO (wallyworld): use an env parameter (default true) to mandate use of only signed image metadata. matchingImages, _, err := imagemetadata.Fetch(sources, simplestreams.DefaultIndexPath, imageConstraint, false) if err != nil { return nil, err } images := instances.ImageMetadataToImages(matchingImages) spec, err := instances.FindInstanceSpec(images, ic, allInstanceTypes) if err != nil { return nil, err } return spec, nil } �������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/openstack/provider_test.go��������������������0000644�0000153�0000161�00000006011�12321735642�027724� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package openstack_test import ( "flag" "testing" gc "launchpad.net/gocheck" "launchpad.net/goose/identity" "launchpad.net/goose/nova" "launchpad.net/juju-core/provider/openstack" ) var live = flag.Bool("live", false, "Include live OpenStack tests") func Test(t *testing.T) { if *live { cred, err := identity.CompleteCredentialsFromEnv() if err != nil { t.Fatalf("Error setting up test suite: %s", err.Error()) } registerLiveTests(cred) } registerLocalTests() gc.TestingT(t) } // localTests contains tests which do not require a live service or test double to run. type localTests struct{} var _ = gc.Suite(&localTests{}) // ported from lp:juju/juju/providers/openstack/tests/test_machine.py var addressTests = []struct { summary string private []nova.IPAddress public []nova.IPAddress networks []string expected string failure error }{ { summary: "missing", expected: "", }, { summary: "empty", private: []nova.IPAddress{}, networks: []string{"private"}, expected: "", }, { summary: "private only", private: []nova.IPAddress{{4, "192.168.0.1"}}, networks: []string{"private"}, expected: "192.168.0.1", }, { summary: "private plus (HP cloud)", private: []nova.IPAddress{{4, "10.0.0.1"}, {4, "8.8.4.4"}}, networks: []string{"private"}, expected: "8.8.4.4", }, { summary: "public only", public: []nova.IPAddress{{4, "8.8.8.8"}}, networks: []string{"", "public"}, expected: "8.8.8.8", }, { summary: "public and private", 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, "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, "192.168.0.1"}}, networks: []string{"special"}, expected: "192.168.0.1", }, { summary: "custom and public", 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", }, { summary: "non-IPv4", private: []nova.IPAddress{{6, "::dead:beef:f00d"}}, networks: []string{"private"}, expected: "", }, } func (t *localTests) TestGetServerAddresses(c *gc.C) { for i, t := range addressTests { c.Logf("#%d. %s -> %s (%v)", i, t.summary, t.expected, t.failure) addresses := make(map[string][]nova.IPAddress) if t.private != nil { if len(t.networks) < 1 { addresses["private"] = t.private } else { addresses[t.networks[0]] = t.private } } if t.public != nil { if len(t.networks) < 2 { addresses["public"] = t.public } else { addresses[t.networks[1]] = t.public } } addr := openstack.InstanceAddress(addresses) c.Assert(addr, gc.Equals, t.expected) } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/���������������������������������������0000755�0000153�0000161�00000000000�12321736000�024014� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/provider.go����������������������������0000644�0000153�0000161�00000007216�12321735642�026216� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent import ( "errors" "fmt" "github.com/juju/loggo" "github.com/joyent/gosign/auth" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" envtools "launchpad.net/juju-core/environs/tools" ) var logger = loggo.GetLogger("juju.provider.joyent") type joyentProvider struct{} var providerInstance = joyentProvider{} var _ environs.EnvironProvider = providerInstance var _ simplestreams.HasRegion = (*joyentEnviron)(nil) var _ imagemetadata.SupportsCustomSources = (*joyentEnviron)(nil) var _ envtools.SupportsCustomSources = (*joyentEnviron)(nil) func init() { environs.RegisterProvider("joyent", providerInstance) } var errNotImplemented = errors.New("not implemented in Joyent provider") func (joyentProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { preparedCfg, err := prepareConfig(cfg) if err != nil { return nil, err } return providerInstance.Open(preparedCfg) } func credentials(cfg *environConfig) (*auth.Credentials, error) { if cfg.privateKey() == "" { return nil, errors.New("cannot create credentials without a private key") } authentication := auth.Auth{User: cfg.mantaUser(), PrivateKey: cfg.privateKey(), Algorithm: cfg.algorithm()} return &auth.Credentials{ UserAuthentication: authentication, MantaKeyId: cfg.mantaKeyId(), MantaEndpoint: auth.Endpoint{URL: cfg.mantaUrl()}, SdcKeyId: cfg.sdcKeyId(), SdcEndpoint: auth.Endpoint{URL: cfg.sdcUrl()}, }, nil } func (joyentProvider) Open(cfg *config.Config) (environs.Environ, error) { env, err := newEnviron(cfg) if err != nil { return nil, err } return env, nil } func (joyentProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { newEcfg, err := validateConfig(cfg, old) if err != nil { return nil, fmt.Errorf("invalid Joyent provider config: %v", err) } return cfg.Apply(newEcfg.attrs) } func (joyentProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { // If you keep configSecretFields up to date, this method should Just Work. ecfg, err := validateConfig(cfg, nil) if err != nil { return nil, err } secretAttrs := map[string]string{} for _, field := range configSecretFields { if value, ok := ecfg.attrs[field]; ok { if stringValue, ok := value.(string); ok { secretAttrs[field] = stringValue } else { // All your secret attributes must be strings at the moment. Sorry. // It's an expedient and hopefully temporary measure that helps us // plug a security hole in the API. return nil, fmt.Errorf( "secret %q field must have a string value; got %v", field, value, ) } } } return secretAttrs, nil } func (joyentProvider) BoilerplateConfig() string { return boilerplateConfig } func GetProviderInstance() environs.EnvironProvider { return providerInstance } // MetadataLookupParams returns parameters which are used to query image metadata to // find matching image information. func (p joyentProvider) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) { if region == "" { return nil, fmt.Errorf("region must be specified") } return &simplestreams.MetadataLookupParams{ Region: region, Architectures: []string{"amd64", "armhf"}, }, nil } func (p joyentProvider) newConfig(cfg *config.Config) (*environConfig, error) { valid, err := p.Validate(cfg, nil) if err != nil { return nil, err } return &environConfig{valid, valid.UnknownAttrs()}, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/joyent_test.go�������������������������0000644�0000153�0000161�00000007331�12321735642�026731� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent_test import ( "fmt" "io/ioutil" "os" gc "launchpad.net/gocheck" envtesting "launchpad.net/juju-core/environs/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/version" ) const ( testUser = "test" testKeyFileName = "provider_id_rsa" testPrivateKey = `-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAza+KvczCrcpQGRq9e347VHx9oEvuhseJt0ydR+UMAveyQprU 4JHvzwUUhGnG147GJQYyfQ4nzaSG62az/YThoZJzw8gtxGkVHv0wlAlRkYhxbKbq 8WQIh73xDQkHLw2lXLvf7Tt0Mhow0qGEmkOjTb5fPsj2evphrV3jJ15QlhL4cv33 t8jVadIrL0iIpwdqWiPqUKpsrSfKJghkoXS6quPy78P820TnuoBG+/Ppr8Kkvn6m A7j4xnOQ12QE6jPK4zkikj5ZczSC4fTG0d3BwwX4VYu+4y/T/BX0L9VNUmQU22Y+ /MRXAUZxsa8VhNB+xXF5XSubyb2n6loMWWaYGwIDAQABAoIBAQDCJt9JxYxGS+BL sigF9+O9Hj3fH42p/5QJR/J2uMgbzP+hS1GCIX9B5MO3MbmWI5j5vd3OmZwMyy7n 6Wwg9FufDgTkW4KIEcD0HX7LXfh27VpTe0PuU8SRjUOKUGlNiw36eQUog6Rs3rgT Oo9Wpl3xtq9lLoErGEk3QpZ2xNpArTfsN9N3pdmD4sv7wmJq0PZQyej482g9R0g/ 5k2ni6JpcEifzBQ6Bzx3EV2l9UipEIqbqDpMOtYFCpnLQhEaDfUribqXINGIsjiq VyFa3Mbg/eayqG3UX3rVTCif2NnW2ojl4mMgWCyxgWfb4Jg1trc3v7X4SXfbgPWD WcfrOhOhAoGBAP7ZC8KHAnjujwvXf3PxVNm6CTs5evbByQVyxNciGxRuOomJIR4D euqepQ4PuNAabnrbMyQWXpibIKpmLnBVoj1q0IMXYvi2MZF5e2tH/Gx01UvxamHh bKhHmp9ImHhVl6kObXOdNvLVTt/BI5FZBblvm7qOoiVwImPbqqVHP7Q5AoGBAM6d mNsrW0iV/nP1m7d8mcFw74PI0FNlNdfUoePUgokO0t5OU0Ri/lPBDCRGlvVF3pj1 HnmwroNtdWr9oPVB6km8193fb2zIWe53tj+6yRFQpz5elrSPfeZaZXlJZAGCCCdN gBggWQFPeQiT54aPywPpcTZHIs72XBqQ6QsIPrbzAoGAdW2hg5MeSobyFuzHZ69N /70/P7DuvgDxFbeah97JR5K7GmC7h87mtnE/cMlByXJEcgvK9tfv4rWoSZwnzc9H oLE1PxJpolyhXnzxp69V2svC9OlasZtjq+7Cip6y0s/twBJL0Lgid6ZeX6/pKbIx dw68XSwX/tQ6pHS1ns7DxdECgYBJbBWapNCefbbbjEcWsC+PX0uuABmP2SKGHSie ZrEwdVUX7KuIXMlWB/8BkRgp9vdAUbLPuap6R9Z2+8RMA213YKUxUiotdREIPgBE q2KyRX/5GPHjHi62Qh9XN25TXtr45ICFklEutwgitTSMS+Lv8+/oQuUquL9ILYCz C+4FYwKBgQDE9yZTUpJjG2424z6bl/MHzwl5RB4pMronp0BbeVqPwhCBfj0W5I42 1ZL4+8eniHfUs4GXzf5tb9YwVt3EltIF2JybaBvFsv2o356yJUQmqQ+jyYRoEpT5 2SwilFo/XCotCXxi5n8sm9V94a0oix4ehZrohTA/FZLsggwFCPmXfw== -----END RSA PRIVATE KEY-----` testKeyFingerprint = "66:ca:1c:09:75:99:35:69:be:91:08:25:03:c0:17:c0" ) type providerSuite struct { coretesting.FakeHomeSuite envtesting.ToolsFixture restoreTimeouts func() } var _ = gc.Suite(&providerSuite{}) func (s *providerSuite) SetUpSuite(c *gc.C) { s.restoreTimeouts = envtesting.PatchAttemptStrategies() s.FakeHomeSuite.SetUpSuite(c) s.AddSuiteCleanup(CreateTestKey(c)) } func (s *providerSuite) TearDownSuite(c *gc.C) { s.restoreTimeouts() s.FakeHomeSuite.TearDownSuite(c) } func (s *providerSuite) SetUpTest(c *gc.C) { s.FakeHomeSuite.SetUpTest(c) s.ToolsFixture.SetUpTest(c) s.AddCleanup(CreateTestKey(c)) } func (s *providerSuite) TearDownTest(c *gc.C) { s.ToolsFixture.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } func GetFakeConfig(sdcUrl, mantaUrl string) coretesting.Attrs { return coretesting.FakeConfig().Merge(coretesting.Attrs{ "name": "joyent test environment", "type": "joyent", "sdc-user": testUser, "sdc-key-id": testKeyFingerprint, "sdc-url": sdcUrl, "manta-user": testUser, "manta-key-id": testKeyFingerprint, "manta-url": mantaUrl, "private-key-path": fmt.Sprintf("~/.ssh/%s", testKeyFileName), "algorithm": "rsa-sha256", "control-dir": "juju-test", "agent-version": version.Current.Number.String(), }) } func CreateTestKey(c *gc.C) func(*gc.C) { keyFile := fmt.Sprintf("~/.ssh/%s", testKeyFileName) keyFilePath, err := utils.NormalizePath(keyFile) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(keyFilePath, []byte(testPrivateKey), 400) c.Assert(err, gc.IsNil) return func(c *gc.C) { os.Remove(keyFilePath) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/export_test.go�������������������������0000644�0000153�0000161�00000012722�12321735642�026742� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent import ( "bytes" "fmt" "text/template" "github.com/joyent/gosign/auth" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/configstore" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/testing" ) var Provider environs.EnvironProvider = GetProviderInstance() var EnvironmentVariables = environmentVariables var indexData = ` { "index": { "com.ubuntu.cloud:released:joyent": { "updated": "Fri, 14 Feb 2014 13:39:35 +0000", "clouds": [ { "region": "{{.Region}}", "endpoint": "{{.SdcEndpoint.URL}}" } ], "cloudname": "joyent", "datatype": "image-ids", "format": "products:1.0", "products": [ "com.ubuntu.cloud:server:12.04:amd64", "com.ubuntu.cloud:server:12.10:amd64", "com.ubuntu.cloud:server:13.04:amd64" ], "path": "streams/v1/com.ubuntu.cloud:released:joyent.json" } }, "updated": "Fri, 14 Feb 2014 13:39:35 +0000", "format": "index:1.0" } ` var imagesData = ` { "content_id": "com.ubuntu.cloud:released:joyent", "format": "products:1.0", "updated": "Fri, 14 Feb 2014 13:39:35 +0000", "datatype": "image-ids", "products": { "com.ubuntu.cloud:server:12.04:amd64": { "release": "precise", "version": "12.04", "arch": "amd64", "versions": { "20140214": { "items": { "11223344-0a0a-ff99-11bb-0a1b2c3d4e5f": { "region": "some-region", "id": "11223344-0a0a-ff99-11bb-0a1b2c3d4e5f", "virt": "kvm" } }, "pubname": "ubuntu-precise-12.04-amd64-server-20140214", "label": "release" } } }, "com.ubuntu.cloud:server:12.10:amd64": { "release": "quantal", "version": "12.10", "arch": "amd64", "versions": { "20140214": { "items": { "11223344-0a0a-ee88-22ab-00aa11bb22cc": { "region": "some-region", "id": "11223344-0a0a-ee88-22ab-00aa11bb22cc", "virt": "kvm" } }, "pubname": "ubuntu-quantal-12.10-amd64-server-20140214", "label": "release" } } }, "com.ubuntu.cloud:server:13.04:amd64": { "release": "raring", "version": "13.04", "arch": "amd64", "versions": { "20140214": { "items": { "11223344-0a0a-dd77-33cd-abcd1234e5f6": { "region": "some-region", "id": "11223344-0a0a-dd77-33cd-abcd1234e5f6", "virt": "kvm" } }, "pubname": "ubuntu-raring-13.04-amd64-server-20140214", "label": "release" } } } } } ` func parseIndexData(creds *auth.Credentials) bytes.Buffer { var metadata bytes.Buffer t := template.Must(template.New("").Parse(indexData)) if err := t.Execute(&metadata, creds); err != nil { panic(fmt.Errorf("cannot generate index metdata: %v", err)) } return metadata } // This provides the content for code accessing test://host/... URLs. This allows // us to set the responses for things like the Metadata server, by pointing // metadata requests at test://host/... var testRoundTripper = &jujutest.ProxyRoundTripper{} func init() { testRoundTripper.RegisterForScheme("test") } var origImagesUrl = imagemetadata.DefaultBaseURL // Set Metadata requests to be served by the filecontent supplied. func UseExternalTestImageMetadata(creds *auth.Credentials) { metadata := parseIndexData(creds) files := map[string]string{ "/streams/v1/index.json": metadata.String(), "/streams/v1/com.ubuntu.cloud:released:joyent.json": imagesData, } testRoundTripper.Sub = jujutest.NewCannedRoundTripper(files, nil) imagemetadata.DefaultBaseURL = "test://host" } func UnregisterExternalTestImageMetadata() { testRoundTripper.Sub = nil } func FindInstanceSpec(e environs.Environ, series, arch, cons string) (spec *instances.InstanceSpec, err error) { env := e.(*joyentEnviron) spec, err = env.FindInstanceSpec(&instances.InstanceConstraint{ Series: series, Arches: []string{arch}, Region: env.Ecfg().Region(), Constraints: constraints.MustParse(cons), }) return } func ControlBucketName(e environs.Environ) string { env := e.(*joyentEnviron) return env.Storage().(*JoyentStorage).GetContainerName() } func CreateContainer(s *JoyentStorage) error { return s.createContainer() } // MakeConfig creates a functional environConfig for a test. func MakeConfig(c *gc.C, attrs testing.Attrs) *environConfig { cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) env, err := environs.Prepare(cfg, testing.Context(c), configstore.NewMem()) c.Assert(err, gc.IsNil) return env.(*joyentEnviron).Ecfg() } // MakeCredentials creates credentials for a test. func MakeCredentials(c *gc.C, attrs testing.Attrs) *auth.Credentials { creds, err := credentials(MakeConfig(c, attrs)) c.Assert(err, gc.IsNil) return creds } // MakeStorage creates an env storage for a test. func MakeStorage(c *gc.C, attrs testing.Attrs) storage.Storage { stor, err := newStorage(MakeConfig(c, attrs), "") c.Assert(err, gc.IsNil) return stor } ����������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/config_test.go�������������������������0000644�0000153�0000161�00000031101�12321735642�026656� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent_test import ( "fmt" "io/ioutil" "os" "github.com/juju/testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" jp "launchpad.net/juju-core/provider/joyent" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils" ) func newConfig(c *gc.C, attrs coretesting.Attrs) *config.Config { attrs = coretesting.FakeConfig().Merge(attrs) cfg, err := config.New(config.UseDefaults, attrs) c.Assert(err, gc.IsNil) return cfg } func validAttrs() coretesting.Attrs { return coretesting.FakeConfig().Merge(coretesting.Attrs{ "type": "joyent", "sdc-user": "juju-test", "sdc-key-id": "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff", "sdc-url": "https://test.api.joyentcloud.com", "manta-user": "juju-test", "manta-key-id": "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff", "manta-url": "https://test.manta.joyent.com", "private-key-path": "~/.ssh/provider_id_rsa", "algorithm": "rsa-sha256", "control-dir": "juju-test", "private-key": "key", }) } type ConfigSuite struct { coretesting.FakeHomeSuite originalValues map[string]testing.Restorer } var _ = gc.Suite(&ConfigSuite{}) func (s *ConfigSuite) SetUpSuite(c *gc.C) { s.FakeHomeSuite.SetUpSuite(c) restoreSdcAccount := testing.PatchEnvironment(jp.SdcAccount, "tester") s.AddSuiteCleanup(func(*gc.C) { restoreSdcAccount() }) restoreSdcKeyId := testing.PatchEnvironment(jp.SdcKeyId, "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00") s.AddSuiteCleanup(func(*gc.C) { restoreSdcKeyId() }) restoreMantaUser := testing.PatchEnvironment(jp.MantaUser, "tester") s.AddSuiteCleanup(func(*gc.C) { restoreMantaUser() }) restoreMantaKeyId := testing.PatchEnvironment(jp.MantaKeyId, "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00") s.AddSuiteCleanup(func(*gc.C) { restoreMantaKeyId() }) } func (s *ConfigSuite) SetUpTest(c *gc.C) { s.FakeHomeSuite.SetUpTest(c) s.AddCleanup(CreateTestKey(c)) for _, envVar := range jp.EnvironmentVariables { s.PatchEnvironment(envVar, "") } } var newConfigTests = []struct { info string insert coretesting.Attrs remove []string envVars map[string]string expect coretesting.Attrs err string }{{ info: "sdc-user is required", remove: []string{"sdc-user"}, err: ".* cannot get sdc-user value from environment variable .*", }, { info: "sdc-user cannot be empty", insert: coretesting.Attrs{"sdc-user": ""}, err: ".* cannot get sdc-user value from environment variable .*", }, { info: "can get sdc-user from env variable", insert: coretesting.Attrs{"sdc-user": ""}, expect: coretesting.Attrs{"sdc-user": "tester"}, envVars: map[string]string{ "SDC_ACCOUNT": "tester", }, }, { info: "can get sdc-user from env variable, missing from config", remove: []string{"sdc-user"}, expect: coretesting.Attrs{"sdc-user": "tester"}, envVars: map[string]string{ "SDC_ACCOUNT": "tester", }, }, { info: "sdc-key-id is required", remove: []string{"sdc-key-id"}, err: ".* cannot get sdc-key-id value from environment variable .*", }, { info: "sdc-key-id cannot be empty", insert: coretesting.Attrs{"sdc-key-id": ""}, err: ".* cannot get sdc-key-id value from environment variable .*", }, { info: "can get sdc-key-id from env variable", insert: coretesting.Attrs{"sdc-key-id": ""}, expect: coretesting.Attrs{"sdc-key-id": "key"}, envVars: map[string]string{ "SDC_KEY_ID": "key", }, }, { info: "can get sdc-key-id from env variable, missing from config", remove: []string{"sdc-key-id"}, expect: coretesting.Attrs{"sdc-key-id": "key"}, envVars: map[string]string{ "SDC_KEY_ID": "key", }, }, { info: "sdc-url is inserted if missing", expect: coretesting.Attrs{"sdc-url": "https://test.api.joyentcloud.com"}, }, { info: "sdc-url cannot be empty", insert: coretesting.Attrs{"sdc-url": ""}, err: ".* cannot get sdc-url value from environment variable .*", }, { info: "sdc-url is untouched if present", insert: coretesting.Attrs{"sdc-url": "https://test.api.joyentcloud.com"}, expect: coretesting.Attrs{"sdc-url": "https://test.api.joyentcloud.com"}, }, { info: "manta-user is required", remove: []string{"manta-user"}, err: ".* cannot get manta-user value from environment variable .*", }, { info: "manta-user cannot be empty", insert: coretesting.Attrs{"manta-user": ""}, err: ".* cannot get manta-user value from environment variable .*", }, { info: "can get manta-user from env variable", insert: coretesting.Attrs{"manta-user": ""}, expect: coretesting.Attrs{"manta-user": "tester"}, envVars: map[string]string{ "MANTA_USER": "tester", }, }, { info: "can get manta-user from env variable, missing from config", remove: []string{"manta-user"}, expect: coretesting.Attrs{"manta-user": "tester"}, envVars: map[string]string{ "MANTA_USER": "tester", }, }, { info: "manta-key-id is required", remove: []string{"manta-key-id"}, err: ".* cannot get manta-key-id value from environment variable .*", }, { info: "manta-key-id cannot be empty", insert: coretesting.Attrs{"manta-key-id": ""}, err: ".* cannot get manta-key-id value from environment variable .*", }, { info: "can get manta-key-id from env variable", insert: coretesting.Attrs{"manta-key-id": ""}, expect: coretesting.Attrs{"manta-key-id": "key"}, envVars: map[string]string{ "MANTA_KEY_ID": "key", }, }, { info: "can get manta-key-id from env variable, missing from config", remove: []string{"manta-key-id"}, expect: coretesting.Attrs{"manta-key-id": "key"}, envVars: map[string]string{ "MANTA_KEY_ID": "key", }, }, { info: "manta-url is inserted if missing", expect: coretesting.Attrs{"manta-url": "https://test.manta.joyent.com"}, }, { info: "manta-url cannot be empty", insert: coretesting.Attrs{"manta-url": ""}, err: ".* cannot get manta-url value from environment variable .*", }, { info: "manta-url is untouched if present", insert: coretesting.Attrs{"manta-url": "https://test.manta.joyent.com"}, expect: coretesting.Attrs{"manta-url": "https://test.manta.joyent.com"}, }, { info: "private-key-path is inserted if missing", remove: []string{"private-key-path"}, expect: coretesting.Attrs{"private-key-path": "~/.ssh/id_rsa"}, }, { info: "can get private-key-path from env variable", insert: coretesting.Attrs{"private-key-path": ""}, expect: coretesting.Attrs{"private-key-path": "some-file"}, envVars: map[string]string{ "MANTA_PRIVATE_KEY_FILE": "some-file", }, }, { info: "can get private-key-path from env variable, missing from config", remove: []string{"private-key-path"}, expect: coretesting.Attrs{"private-key-path": "some-file"}, envVars: map[string]string{ "MANTA_PRIVATE_KEY_FILE": "some-file", }, }, { info: "algorithm is inserted if missing", expect: coretesting.Attrs{"algorithm": "rsa-sha256"}, }, { info: "algorithm cannot be empty", insert: coretesting.Attrs{"algorithm": ""}, err: ".* algorithm: must not be empty", }, { info: "unknown field is not touched", insert: coretesting.Attrs{"unknown-field": 12345}, expect: coretesting.Attrs{"unknown-field": 12345}, }} func (*ConfigSuite) TestNewEnvironConfig(c *gc.C) { for i, test := range newConfigTests { c.Logf("test %d: %s", i, test.info) for k, v := range test.envVars { os.Setenv(k, v) } attrs := validAttrs().Merge(test.insert).Delete(test.remove...) testConfig := newConfig(c, attrs) environ, err := environs.New(testConfig) if test.err == "" { c.Check(err, gc.IsNil) attrs := environ.Config().AllAttrs() for field, value := range test.expect { c.Check(attrs[field], gc.Equals, value) } } else { c.Check(environ, gc.IsNil) c.Check(err, gc.ErrorMatches, test.err) } } } var changeConfigTests = []struct { info string insert coretesting.Attrs remove []string expect coretesting.Attrs err string }{{ info: "no change, no error", expect: validAttrs(), }, { info: "can change sdc-user", insert: coretesting.Attrs{"sdc-user": "joyent_user"}, expect: coretesting.Attrs{"sdc-user": "joyent_user"}, }, { info: "can change sdc-key-id", insert: coretesting.Attrs{"sdc-key-id": "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00"}, expect: coretesting.Attrs{"sdc-key-id": "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00"}, }, { info: "can change sdc-url", insert: coretesting.Attrs{"sdc-url": "https://test.api.joyentcloud.com"}, expect: coretesting.Attrs{"sdc-url": "https://test.api.joyentcloud.com"}, }, { info: "can change manta-user", insert: coretesting.Attrs{"manta-user": "manta_user"}, expect: coretesting.Attrs{"manta-user": "manta_user"}, }, { info: "can change manta-key-id", insert: coretesting.Attrs{"manta-key-id": "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00"}, expect: coretesting.Attrs{"manta-key-id": "ff:ee:dd:cc:bb:aa:99:88:77:66:55:44:33:22:11:00"}, }, { info: "can change manta-url", insert: coretesting.Attrs{"manta-url": "https://test.manta.joyent.com"}, expect: coretesting.Attrs{"manta-url": "https://test.manta.joyent.com"}, }, { info: "can insert unknown field", insert: coretesting.Attrs{"unknown": "ignoti"}, expect: coretesting.Attrs{"unknown": "ignoti"}, }} func (s *ConfigSuite) TestValidateChange(c *gc.C) { baseConfig := newConfig(c, validAttrs()) for i, test := range changeConfigTests { c.Logf("test %d: %s", i, test.info) attrs := validAttrs().Merge(test.insert).Delete(test.remove...) testConfig := newConfig(c, attrs) validatedConfig, err := jp.Provider.Validate(testConfig, baseConfig) if test.err == "" { c.Check(err, gc.IsNil) attrs := validatedConfig.AllAttrs() for field, value := range test.expect { c.Check(attrs[field], gc.Equals, value) } } else { c.Check(validatedConfig, gc.IsNil) c.Check(err, gc.ErrorMatches, "invalid config change: "+test.err) } } } func (s *ConfigSuite) TestSetConfig(c *gc.C) { baseConfig := newConfig(c, validAttrs()) for i, test := range changeConfigTests { c.Logf("test %d: %s", i, test.info) environ, err := environs.New(baseConfig) c.Assert(err, gc.IsNil) attrs := validAttrs().Merge(test.insert).Delete(test.remove...) testConfig := newConfig(c, attrs) err = environ.SetConfig(testConfig) newAttrs := environ.Config().AllAttrs() if test.err == "" { c.Check(err, gc.IsNil) for field, value := range test.expect { c.Check(newAttrs[field], gc.Equals, value) } } else { c.Check(err, gc.ErrorMatches, test.err) for field, value := range baseConfig.UnknownAttrs() { c.Check(newAttrs[field], gc.Equals, value) } } } } func validPrepareAttrs() coretesting.Attrs { return validAttrs().Delete("private-key") } var prepareConfigTests = []struct { info string insert coretesting.Attrs remove []string expect coretesting.Attrs err string }{{ info: "All value provided, nothig to do", expect: validPrepareAttrs(), }, { info: "private key is loaded from key file", insert: coretesting.Attrs{"private-key-path": fmt.Sprintf("~/.ssh/%s", testKeyFileName)}, expect: coretesting.Attrs{"private-key": testPrivateKey}, }, { info: "bad private-key-path errors, not panics", insert: coretesting.Attrs{"private-key-path": "~/.ssh/no-such-file"}, err: "invalid Joyent provider config: open .*: no such file or directory", }} func (s *ConfigSuite) TestPrepare(c *gc.C) { ctx := coretesting.Context(c) for i, test := range prepareConfigTests { c.Logf("test %d: %s", i, test.info) attrs := validPrepareAttrs().Merge(test.insert).Delete(test.remove...) testConfig := newConfig(c, attrs) preparedConfig, err := jp.Provider.Prepare(ctx, testConfig) if test.err == "" { c.Check(err, gc.IsNil) attrs := preparedConfig.Config().AllAttrs() for field, value := range test.expect { c.Check(attrs[field], gc.Equals, value) } } else { c.Check(preparedConfig, gc.IsNil) c.Check(err, gc.ErrorMatches, test.err) } } } func (s *ConfigSuite) TestPrepareWithDefaultKeyFile(c *gc.C) { ctx := coretesting.Context(c) // By default "private-key-path isn't set until after validateConfig has been called. attrs := validAttrs().Delete("private-key-path", "private-key") keyFilePath, err := utils.NormalizePath(jp.DefaultPrivateKey) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(keyFilePath, []byte(testPrivateKey), 400) c.Assert(err, gc.IsNil) defer os.Remove(keyFilePath) testConfig := newConfig(c, attrs) preparedConfig, err := jp.Provider.Prepare(ctx, testConfig) c.Assert(err, gc.IsNil) attrs = preparedConfig.Config().AllAttrs() c.Check(attrs["private-key-path"], gc.Equals, jp.DefaultPrivateKey) c.Check(attrs["private-key"], gc.Equals, testPrivateKey) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/config.go������������������������������0000644�0000153�0000161�00000016277�12321735642�025640� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent import ( "fmt" "io/ioutil" "net/url" "os" "strings" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/schema" "launchpad.net/juju-core/utils" ) // boilerplateConfig will be shown in help output, so please keep it up to // date when you change environment configuration below. const boilerplateConfig = `joyent: type: joyent # SDC config # Can be set via env variables, or specified here # sdc-user: <secret> # Can be set via env variables, or specified here # sdc-key-id: <secret> # url defaults to us-west-1 DC, override if required # sdc-url: https://us-west-1.api.joyentcloud.com # Manta config # Can be set via env variables, or specified here # manta-user: <secret> # Can be set via env variables, or specified here # manta-key-id: <secret> # url defaults to us-east DC, override if required # manta-url: https://us-east.manta.joyent.com # Auth config # private-key-path is the private key used to sign Joyent requests. # Defaults to ~/.ssh/id_rsa, override if a different ssh key is used. # Alternatively, you can supply "private-key" with the content of the private # key instead supplying the path to a file. # private-key-path: ~/.ssh/id_rsa # algorithm defaults to rsa-sha256, override if required # algorithm: rsa-sha256 ` const ( SdcAccount = "SDC_ACCOUNT" SdcKeyId = "SDC_KEY_ID" SdcUrl = "SDC_URL" MantaUser = "MANTA_USER" MantaKeyId = "MANTA_KEY_ID" MantaUrl = "MANTA_URL" MantaPrivateKeyFile = "MANTA_PRIVATE_KEY_FILE" DefaultPrivateKey = "~/.ssh/id_rsa" ) var environmentVariables = map[string]string{ "sdc-user": SdcAccount, "sdc-key-id": SdcKeyId, "sdc-url": SdcUrl, "manta-user": MantaUser, "manta-key-id": MantaKeyId, "manta-url": MantaUrl, "private-key-path": MantaPrivateKeyFile, } var configFields = schema.Fields{ "sdc-user": schema.String(), "sdc-key-id": schema.String(), "sdc-url": schema.String(), "manta-user": schema.String(), "manta-key-id": schema.String(), "manta-url": schema.String(), "private-key-path": schema.String(), "algorithm": schema.String(), "control-dir": schema.String(), "private-key": schema.String(), } var configDefaults = schema.Defaults{ "sdc-url": "https://us-west-1.api.joyentcloud.com", "manta-url": "https://us-east.manta.joyent.com", "algorithm": "rsa-sha256", "private-key-path": schema.Omit, "sdc-user": schema.Omit, "sdc-key-id": schema.Omit, "manta-user": schema.Omit, "manta-key-id": schema.Omit, "private-key": schema.Omit, } var configSecretFields = []string{ "sdc-user", "sdc-key-id", "manta-user", "manta-key-id", "private-key", } var configImmutableFields = []string{ "sdc-url", "manta-url", "private-key-path", "private-key", "algorithm", } func prepareConfig(cfg *config.Config) (*config.Config, error) { // Turn an incomplete config into a valid one, if possible. attrs := cfg.UnknownAttrs() if _, ok := attrs["control-dir"]; !ok { uuid, err := utils.NewUUID() if err != nil { return nil, err } attrs["control-dir"] = fmt.Sprintf("%x", uuid.Raw()) } return cfg.Apply(attrs) } func validateConfig(cfg, old *config.Config) (*environConfig, error) { // Check for valid changes for the base config values. if err := config.Validate(cfg, old); err != nil { return nil, err } newAttrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } envConfig := &environConfig{cfg, newAttrs} // If an old config was supplied, check any immutable fields have not changed. if old != nil { oldEnvConfig, err := validateConfig(old, nil) if err != nil { return nil, err } for _, field := range configImmutableFields { if oldEnvConfig.attrs[field] != envConfig.attrs[field] { return nil, fmt.Errorf( "%s: cannot change from %v to %v", field, oldEnvConfig.attrs[field], envConfig.attrs[field], ) } } } // Read env variables to fill in any missing fields. for field, envVar := range environmentVariables { // If field is not set, get it from env variables if fieldValue, ok := envConfig.attrs[field]; !ok || fieldValue == "" { localEnvVariable := os.Getenv(envVar) if localEnvVariable != "" { envConfig.attrs[field] = localEnvVariable } else { if field != "private-key-path" { return nil, fmt.Errorf("cannot get %s value from environment variable %s", field, envVar) } } } } // Ensure private-key-path is set - if it's not in config or an env var, use a default value. if v, ok := envConfig.attrs["private-key-path"]; !ok || v == "" { v = os.Getenv(environmentVariables["private-key-path"]) if v == "" { v = DefaultPrivateKey } envConfig.attrs["private-key-path"] = v } // Now that we've ensured private-key-path is properly set, we go back and set // up the private key - this is used to sign requests. if fieldValue, ok := envConfig.attrs["private-key"]; !ok || fieldValue == "" { keyFile, err := utils.NormalizePath(envConfig.attrs["private-key-path"].(string)) if err != nil { return nil, err } privateKey, err := ioutil.ReadFile(keyFile) if err != nil { return nil, err } envConfig.attrs["private-key"] = string(privateKey) } // Check for missing fields. for field := range configFields { if envConfig.attrs[field] == "" { return nil, fmt.Errorf("%s: must not be empty", field) } } return envConfig, nil } type environConfig struct { *config.Config attrs map[string]interface{} } func (ecfg *environConfig) GetAttrs() map[string]interface{} { return ecfg.attrs } func (ecfg *environConfig) sdcUrl() string { return ecfg.attrs["sdc-url"].(string) } func (ecfg *environConfig) sdcUser() string { return ecfg.attrs["sdc-user"].(string) } func (ecfg *environConfig) sdcKeyId() string { return ecfg.attrs["sdc-key-id"].(string) } func (ecfg *environConfig) mantaUrl() string { return ecfg.attrs["manta-url"].(string) } func (ecfg *environConfig) mantaUser() string { return ecfg.attrs["manta-user"].(string) } func (ecfg *environConfig) mantaKeyId() string { return ecfg.attrs["manta-key-id"].(string) } func (ecfg *environConfig) privateKey() string { if v, ok := ecfg.attrs["private-key"]; ok { return v.(string) } return "" } func (ecfg *environConfig) algorithm() string { return ecfg.attrs["algorithm"].(string) } func (c *environConfig) controlDir() string { return c.attrs["control-dir"].(string) } func (c *environConfig) ControlDir() string { return c.controlDir() } func (ecfg *environConfig) SdcUrl() string { return ecfg.sdcUrl() } func (ecfg *environConfig) Region() string { sdcUrl := ecfg.sdcUrl() // Check if running against local services if isLocalhost(sdcUrl) { return "some-region" } return sdcUrl[strings.LastIndex(sdcUrl, "/")+1 : strings.Index(sdcUrl, ".")] } func isLocalhost(u string) bool { parsedUrl, err := url.Parse(u) if err != nil { return false } if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.") { return true } return false } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/instance.go����������������������������0000644�0000153�0000161�00000004512�12321735776�026174� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent import ( "fmt" "strings" "time" "github.com/joyent/gosdc/cloudapi" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/provider/common" ) type joyentInstance struct { machine *cloudapi.Machine env *joyentEnviron } var _ instance.Instance = (*joyentInstance)(nil) func (inst *joyentInstance) Id() instance.Id { return instance.Id(inst.machine.Id) } func (inst *joyentInstance) Status() string { return inst.machine.State } func (inst *joyentInstance) Refresh() error { return nil } func (inst *joyentInstance) Addresses() ([]instance.Address, error) { addresses := make([]instance.Address, len(inst.machine.IPs)) for _, ip := range inst.machine.IPs { address := instance.NewAddress(ip) if ip == inst.machine.PrimaryIP { address.NetworkScope = instance.NetworkPublic } else { address.NetworkScope = instance.NetworkCloudLocal } addresses = append(addresses, address) } return addresses, nil } func (inst *joyentInstance) DNSName() (string, error) { addresses, err := inst.Addresses() if err != nil { return "", err } addr := instance.SelectPublicAddress(addresses) if addr == "" { return "", instance.ErrNoDNSName } return addr, nil } func (inst *joyentInstance) WaitDNSName() (string, error) { return common.WaitDNSName(inst) } // Stop will stop and delete the machine // Stopped machines are still billed for in the Joyent Public Cloud func (inst *joyentInstance) Stop() error { id := string(inst.Id()) // wait for machine to be running // if machine is still provisioning stop will fail for !inst.pollMachineState(id, "running") { time.Sleep(1 * time.Second) } err := inst.env.compute.cloudapi.StopMachine(id) if err != nil { return fmt.Errorf("cannot stop instance %s: %v", id, err) } // wait for machine to be stopped for !inst.pollMachineState(id, "stopped") { time.Sleep(1 * time.Second) } err = inst.env.compute.cloudapi.DeleteMachine(id) if err != nil { return fmt.Errorf("cannot delete instance %s: %v", id, err) } return nil } func (inst *joyentInstance) pollMachineState(machineId, state string) bool { machineConfig, err := inst.env.compute.cloudapi.GetMachine(machineId) if err != nil { return false } return strings.EqualFold(machineConfig.State, state) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/storage.go�����������������������������0000644�0000153�0000161�00000016410�12321735642�026024� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent import ( "bytes" "fmt" "io" "io/ioutil" "path" "strings" "sync" "time" "launchpad.net/juju-core/environs/storage" coreerrors "launchpad.net/juju-core/errors" "launchpad.net/juju-core/utils" "github.com/joyent/gocommon/client" je "github.com/joyent/gocommon/errors" "github.com/joyent/gomanta/manta" ) type JoyentStorage struct { sync.Mutex ecfg *environConfig madeContainer bool containerName string manta *manta.Client } type byteCloser struct { io.Reader } func (byteCloser) Close() error { return nil } var _ storage.Storage = (*JoyentStorage)(nil) func newStorage(cfg *environConfig, name string) (storage.Storage, error) { creds, err := credentials(cfg) if err != nil { return nil, err } client := client.NewClient(cfg.mantaUrl(), "", creds, &logger) if name == "" { name = cfg.controlDir() } return &JoyentStorage{ ecfg: cfg, containerName: name, manta: manta.New(client)}, nil } func (s *JoyentStorage) GetContainerName() string { return s.containerName } func (s *JoyentStorage) GetMantaUrl() string { return s.ecfg.mantaUrl() } func (s *JoyentStorage) GetMantaUser() string { return s.ecfg.mantaUser() } // createContainer makes the environment's control container, the // place where bootstrap information and deployed charms // are stored. To avoid two round trips on every PUT operation, // we do this only once for each environ. func (s *JoyentStorage) createContainer() error { s.Lock() defer s.Unlock() if s.madeContainer { return nil } // try to make the container err := s.manta.PutDirectory(s.containerName) if err == nil { s.madeContainer = true } return err } // deleteContainer deletes the named container from the storage account. func (s *JoyentStorage) DeleteContainer(containerName string) error { err := s.manta.DeleteDirectory(containerName) if err == nil && strings.EqualFold(s.containerName, containerName) { s.madeContainer = false } if je.IsResourceNotFound(err) { return coreerrors.NewNotFoundError(err, fmt.Sprintf("cannot delete %s, not found", containerName)) } return err } func (s *JoyentStorage) List(prefix string) ([]string, error) { content, err := list(s, s.containerName) if err != nil { return nil, err } var names []string for _, item := range content { name := strings.TrimPrefix(item, s.containerName+"/") if prefix != "" { if strings.HasPrefix(name, prefix) { names = append(names, name) } } else { names = append(names, name) } } return names, nil } func list(s *JoyentStorage, path string) ([]string, error) { // TODO - we don't want to create the container here, but instead handle // any 404 and return as if no files exist. if err := s.createContainer(); err != nil { return nil, fmt.Errorf("cannot make Manta control container: %v", err) } // use empty opts, i.e. default values // -- might be added in the provider config? contents, err := s.manta.ListDirectory(path, manta.ListDirectoryOpts{}) if err != nil { return nil, err } var names []string for _, item := range contents { if strings.EqualFold(item.Type, "directory") { items, err := list(s, path+"/"+item.Name) if err != nil { return nil, err } names = append(names, items...) } else { names = append(names, path+"/"+item.Name) } } return names, nil } //return something that a random wget can retrieve the object at, without any credentials func (s *JoyentStorage) URL(name string) (string, error) { path := fmt.Sprintf("/%s/stor/%s/%s", s.ecfg.mantaUser(), s.containerName, name) return s.manta.SignURL(path, time.Now().AddDate(10, 0, 0)) } func (s *JoyentStorage) Get(name string) (io.ReadCloser, error) { b, err := s.manta.GetObject(s.containerName, name) if err != nil { return nil, coreerrors.NewNotFoundError(err, fmt.Sprintf("cannot find %s", name)) } r := byteCloser{bytes.NewReader(b)} return r, nil } func (s *JoyentStorage) Put(name string, r io.Reader, length int64) error { if err := s.createContainer(); err != nil { return fmt.Errorf("cannot make Manta control container: %v", err) } if strings.Contains(name, "/") { var parents []string dirs := strings.Split(name, "/") for i, _ := range dirs { if i < (len(dirs) - 1) { parents = append(parents, strings.Join(dirs[:(i+1)], "/")) } } for _, dir := range parents { err := s.manta.PutDirectory(path.Join(s.containerName, dir)) if err != nil { return fmt.Errorf("cannot create parent directory %q in control container %q: %v", dir, s.containerName, err) } } } object, err := ioutil.ReadAll(r) if err != nil { return fmt.Errorf("failed to read object %q: %v", name, err) } err = s.manta.PutObject(s.containerName, name, object) if err != nil { return fmt.Errorf("cannot write file %q to control container %q: %v", name, s.containerName, err) } return nil } func (s *JoyentStorage) Remove(name string) error { err := s.manta.DeleteObject(s.containerName, name) if err != nil { if je.IsResourceNotFound(err) { // gojoyent returns an error if file doesn't exist // just log a warning logger.Warningf("cannot delete %s from %s, already deleted", name, s.containerName) } else { return err } } if strings.Contains(name, "/") { var parents []string dirs := strings.Split(name, "/") for i := (len(dirs) - 1); i >= 0; i-- { if i < (len(dirs) - 1) { parents = append(parents, strings.Join(dirs[:(i+1)], "/")) } } for _, dir := range parents { err := s.manta.DeleteDirectory(path.Join(s.containerName, dir)) if err != nil { if je.IsBadRequest(err) { // check if delete request returned a bad request error, i.e. directory is not empty // just log a warning logger.Warningf("cannot delete %s, not empty", dir) } else if je.IsResourceNotFound(err) { // check if delete request returned a resource not found error, i.e. directory was already deleted // just log a warning logger.Warningf("cannot delete %s, already deleted", dir) } else { return fmt.Errorf("cannot delete parent directory %q in control container %q: %v", dir, s.containerName, err) } } } } return nil } func (s *JoyentStorage) RemoveAll() error { names, err := storage.List(s, "") if err != nil { return err } // Remove all the objects in parallel so that we incur less round-trips. // If we're in danger of having hundreds of objects, // we'll want to change this to limit the number // of concurrent operations. var wg sync.WaitGroup wg.Add(len(names)) errc := make(chan error, len(names)) for _, name := range names { name := name go func() { defer wg.Done() if err := s.Remove(name); err != nil { errc <- err } }() } wg.Wait() select { case err := <-errc: return fmt.Errorf("cannot delete all provider state: %v", err) default: } s.Lock() defer s.Unlock() // Even DeleteContainer fails, it won't harm if we try again - the // operation might have succeeded even if we get an error. s.madeContainer = false if err = s.manta.DeleteDirectory(s.containerName); err != nil { return err } return nil } func (s *JoyentStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { return utils.AttemptStrategy{} } func (s *JoyentStorage) ShouldRetry(err error) bool { return false } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/instance_firewall.go�������������������0000644�0000153�0000161�00000006151�12321735642�030052� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent import ( "fmt" "strings" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "github.com/joyent/gosdc/cloudapi" ) const ( firewallRuleVm = "FROM tag %s TO vm %s ALLOW %s PORT %d" ) // Helper method to create a firewall rule string for the given machine Id and port func createFirewallRuleVm(env *joyentEnviron, machineId string, port instance.Port) string { return fmt.Sprintf(firewallRuleVm, env.Name(), machineId, strings.ToLower(port.Protocol), port.Number) } func (inst *joyentInstance) OpenPorts(machineId string, ports []instance.Port) error { if inst.env.Config().FirewallMode() != config.FwInstance { return fmt.Errorf("invalid firewall mode %q for opening ports on instance", inst.env.Config().FirewallMode()) } fwRules, err := inst.env.compute.cloudapi.ListFirewallRules() if err != nil { return fmt.Errorf("cannot get firewall rules: %v", err) } machineId = string(inst.Id()) for _, p := range ports { rule := createFirewallRuleVm(inst.env, machineId, p) if e, id := ruleExists(fwRules, rule); e { _, err := inst.env.compute.cloudapi.EnableFirewallRule(id) if err != nil { return fmt.Errorf("couldn't enable rule %s: %v", rule, err) } } else { _, err := inst.env.compute.cloudapi.CreateFirewallRule(cloudapi.CreateFwRuleOpts{ Enabled: true, Rule: rule, }) if err != nil { return fmt.Errorf("couldn't create rule %s: %v", rule, err) } } } logger.Infof("ports %v opened for instance %q", ports, machineId) return nil } func (inst *joyentInstance) ClosePorts(machineId string, ports []instance.Port) error { if inst.env.Config().FirewallMode() != config.FwInstance { return fmt.Errorf("invalid firewall mode %q for closing ports on instance", inst.env.Config().FirewallMode()) } fwRules, err := inst.env.compute.cloudapi.ListFirewallRules() if err != nil { return fmt.Errorf("cannot get firewall rules: %v", err) } machineId = string(inst.Id()) for _, p := range ports { rule := createFirewallRuleVm(inst.env, machineId, p) if e, id := ruleExists(fwRules, rule); e { _, err := inst.env.compute.cloudapi.DisableFirewallRule(id) if err != nil { return fmt.Errorf("couldn't disable rule %s: %v", rule, err) } } else { _, err := inst.env.compute.cloudapi.CreateFirewallRule(cloudapi.CreateFwRuleOpts{ Enabled: false, Rule: rule, }) if err != nil { return fmt.Errorf("couldn't create rule %s: %v", rule, err) } } } logger.Infof("ports %v closed for instance %q", ports, machineId) return nil } func (inst *joyentInstance) Ports(machineId string) ([]instance.Port, error) { if inst.env.Config().FirewallMode() != config.FwInstance { return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from instance", inst.env.Config().FirewallMode()) } machineId = string(inst.Id()) fwRules, err := inst.env.compute.cloudapi.ListMachineFirewallRules(machineId) if err != nil { return nil, fmt.Errorf("cannot get firewall rules: %v", err) } return getPorts(inst.env, fwRules), nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/local_test.go��������������������������0000644�0000153�0000161�00000027575�12321735642�026527� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent_test import ( "bytes" "io/ioutil" "net/http" "net/http/httptest" "strings" lm "github.com/joyent/gomanta/localservices/manta" lc "github.com/joyent/gosdc/localservices/cloudapi" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/bootstrap" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/jujutest" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" envtesting "launchpad.net/juju-core/environs/testing" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/testing" "launchpad.net/juju-core/provider/joyent" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type ProviderSuite struct{} var _ = gc.Suite(&ProviderSuite{}) func registerLocalTests() { gc.Suite(&localServerSuite{}) gc.Suite(&localLiveSuite{}) } type localCloudAPIServer struct { Server *httptest.Server Mux *http.ServeMux oldHandler http.Handler cloudapi *lc.CloudAPI } func (ca *localCloudAPIServer) setupServer(c *gc.C) { // Set up the HTTP server. ca.Server = httptest.NewServer(nil) c.Assert(ca.Server, gc.NotNil) ca.oldHandler = ca.Server.Config.Handler ca.Mux = http.NewServeMux() ca.Server.Config.Handler = ca.Mux ca.cloudapi = lc.New(ca.Server.URL, testUser) ca.cloudapi.SetupHTTP(ca.Mux) c.Logf("Started local CloudAPI service at: %v", ca.Server.URL) } func (c *localCloudAPIServer) destroyServer() { c.Mux = nil c.Server.Config.Handler = c.oldHandler c.Server.Close() } type localMantaServer struct { Server *httptest.Server Mux *http.ServeMux oldHandler http.Handler manta *lm.Manta } func (m *localMantaServer) setupServer(c *gc.C) { // Set up the HTTP server. m.Server = httptest.NewServer(nil) c.Assert(m.Server, gc.NotNil) m.oldHandler = m.Server.Config.Handler m.Mux = http.NewServeMux() m.Server.Config.Handler = m.Mux m.manta = lm.New(m.Server.URL, testUser) m.manta.SetupHTTP(m.Mux) c.Logf("Started local Manta service at: %v", m.Server.URL) } func (m *localMantaServer) destroyServer() { m.Mux = nil m.Server.Config.Handler = m.oldHandler m.Server.Close() } type localLiveSuite struct { testbase.LoggingSuite LiveTests cSrv *localCloudAPIServer mSrv *localMantaServer } func (s *localLiveSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.AddSuiteCleanup(CreateTestKey(c)) s.cSrv = &localCloudAPIServer{} s.mSrv = &localMantaServer{} s.cSrv.setupServer(c) s.mSrv.setupServer(c) s.TestConfig = GetFakeConfig(s.cSrv.Server.URL, s.mSrv.Server.URL) s.TestConfig = s.TestConfig.Merge(coretesting.Attrs{ "image-metadata-url": "test://host", }) s.LiveTests.SetUpSuite(c) creds := joyent.MakeCredentials(c, s.TestConfig) joyent.UseExternalTestImageMetadata(creds) restoreFinishBootstrap := envtesting.DisableFinishBootstrap() s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) } func (s *localLiveSuite) TearDownSuite(c *gc.C) { joyent.UnregisterExternalTestImageMetadata() s.LiveTests.TearDownSuite(c) s.cSrv.destroyServer() s.mSrv.destroyServer() s.LoggingSuite.TearDownSuite(c) } func (s *localLiveSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.LiveTests.SetUpTest(c) } func (s *localLiveSuite) TearDownTest(c *gc.C) { s.LiveTests.TearDownTest(c) s.LoggingSuite.TearDownTest(c) } // localServerSuite contains tests that run against an Joyent service double. // These tests can test things that would be unreasonably slow or expensive // to test on a live Joyent server. The service double is started and stopped for // each test. type localServerSuite struct { jujutest.Tests cSrv *localCloudAPIServer mSrv *localMantaServer } func (s *localServerSuite) SetUpSuite(c *gc.C) { s.Tests.SetUpSuite(c) restoreFinishBootstrap := envtesting.DisableFinishBootstrap() s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) } func (s *localServerSuite) TearDownSuite(c *gc.C) { s.Tests.TearDownSuite(c) } func (s *localServerSuite) SetUpTest(c *gc.C) { s.AddSuiteCleanup(CreateTestKey(c)) s.cSrv = &localCloudAPIServer{} s.mSrv = &localMantaServer{} s.cSrv.setupServer(c) s.mSrv.setupServer(c) s.Tests.SetUpTest(c) s.TestConfig = GetFakeConfig(s.cSrv.Server.URL, s.mSrv.Server.URL) // Put some fake image metadata in place. creds := joyent.MakeCredentials(c, s.TestConfig) joyent.UseExternalTestImageMetadata(creds) } func (s *localServerSuite) TearDownTest(c *gc.C) { joyent.UnregisterExternalTestImageMetadata() s.Tests.TearDownTest(c) s.cSrv.destroyServer() s.mSrv.destroyServer() } func bootstrapContext(c *gc.C) environs.BootstrapContext { return coretesting.Context(c) } // If the environment is configured not to require a public IP address for nodes, // bootstrapping and starting an instance should occur without any attempt to // allocate a public address. func (s *localServerSuite) TestStartInstance(c *gc.C) { env := s.Prepare(c) envtesting.UploadFakeTools(c, env.Storage()) err := bootstrap.Bootstrap(bootstrapContext(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) inst, _ := testing.AssertStartInstance(c, env, "100") err = env.StopInstances([]instance.Instance{inst}) c.Assert(err, gc.IsNil) } func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) { env := s.Prepare(c) envtesting.UploadFakeTools(c, env.Storage()) err := bootstrap.Bootstrap(bootstrapContext(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) _, hc := testing.AssertStartInstanceWithConstraints(c, env, "100", constraints.MustParse("mem=1024")) c.Check(*hc.Arch, gc.Equals, "amd64") c.Check(*hc.Mem, gc.Equals, uint64(1024)) c.Check(*hc.CpuCores, gc.Equals, uint64(1)) c.Assert(hc.CpuPower, gc.IsNil) } var instanceGathering = []struct { ids []instance.Id err error }{ {ids: []instance.Id{"id0"}}, {ids: []instance.Id{"id0", "id0"}}, {ids: []instance.Id{"id0", "id1"}}, {ids: []instance.Id{"id1", "id0"}}, {ids: []instance.Id{"id1", "id0", "id1"}}, { ids: []instance.Id{""}, err: environs.ErrNoInstances, }, { ids: []instance.Id{"", ""}, err: environs.ErrNoInstances, }, { ids: []instance.Id{"", "", ""}, err: environs.ErrNoInstances, }, { ids: []instance.Id{"id0", ""}, err: environs.ErrPartialInstances, }, { ids: []instance.Id{"", "id1"}, err: environs.ErrPartialInstances, }, { ids: []instance.Id{"id0", "id1", ""}, err: environs.ErrPartialInstances, }, { ids: []instance.Id{"id0", "", "id0"}, err: environs.ErrPartialInstances, }, { ids: []instance.Id{"id0", "id0", ""}, err: environs.ErrPartialInstances, }, { ids: []instance.Id{"", "id0", "id1"}, err: environs.ErrPartialInstances, }, } func (s *localServerSuite) TestInstanceStatus(c *gc.C) { env := s.Prepare(c) envtesting.UploadFakeTools(c, env.Storage()) inst, _ := testing.AssertStartInstance(c, env, "100") c.Assert(inst.Status(), gc.Equals, "running") err := env.StopInstances([]instance.Instance{inst}) c.Assert(err, gc.IsNil) } func (s *localServerSuite) TestInstancesGathering(c *gc.C) { env := s.Prepare(c) envtesting.UploadFakeTools(c, env.Storage()) inst0, _ := testing.AssertStartInstance(c, env, "100") id0 := inst0.Id() inst1, _ := testing.AssertStartInstance(c, env, "101") id1 := inst1.Id() c.Logf("id0: %s, id1: %s", id0, id1) defer func() { err := env.StopInstances([]instance.Instance{inst0, inst1}) c.Assert(err, gc.IsNil) }() for i, test := range instanceGathering { c.Logf("test %d: find %v -> expect len %d, err: %v", i, test.ids, len(test.ids), test.err) ids := make([]instance.Id, len(test.ids)) for j, id := range test.ids { switch id { case "id0": ids[j] = id0 case "id1": ids[j] = id1 } } insts, err := env.Instances(ids) c.Assert(err, gc.Equals, test.err) if err == environs.ErrNoInstances { c.Assert(insts, gc.HasLen, 0) } else { c.Assert(insts, gc.HasLen, len(test.ids)) } for j, inst := range insts { if ids[j] != "" { c.Assert(inst.Id(), gc.Equals, ids[j]) } else { c.Assert(inst, gc.IsNil) } } } } // It should be moved to environs.jujutests.Tests. func (s *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) { env := s.Prepare(c) envtesting.UploadFakeTools(c, env.Storage()) err := bootstrap.Bootstrap(bootstrapContext(c), env, constraints.Value{}) c.Assert(err, gc.IsNil) // check that the state holds the id of the bootstrap machine. stateData, err := bootstrap.LoadState(env.Storage()) c.Assert(err, gc.IsNil) c.Assert(stateData.StateInstances, gc.HasLen, 1) insts, err := env.AllInstances() c.Assert(err, gc.IsNil) c.Assert(insts, gc.HasLen, 1) c.Check(stateData.StateInstances[0], gc.Equals, insts[0].Id()) bootstrapDNS, err := insts[0].DNSName() c.Assert(err, gc.IsNil) c.Assert(bootstrapDNS, gc.Not(gc.Equals), "") } func (s *localServerSuite) TestGetImageMetadataSources(c *gc.C) { env := s.Prepare(c) sources, err := imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) c.Assert(len(sources), gc.Equals, 2) var urls = make([]string, len(sources)) for i, source := range sources { url, err := source.URL("") c.Assert(err, gc.IsNil) urls[i] = url } // The control bucket URL contains the bucket name. c.Assert(strings.Contains(urls[0], joyent.ControlBucketName(env)+"/images"), jc.IsTrue) } func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) { env := s.Prepare(c) sources, err := tools.GetMetadataSources(env) c.Assert(err, gc.IsNil) c.Assert(len(sources), gc.Equals, 1) var urls = make([]string, len(sources)) for i, source := range sources { url, err := source.URL("") c.Assert(err, gc.IsNil) urls[i] = url } // The control bucket URL contains the bucket name. c.Assert(strings.Contains(urls[0], joyent.ControlBucketName(env)+"/tools"), jc.IsTrue) } func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) { env := s.Prepare(c) // An error occurs if no suitable image is found. _, err := joyent.FindInstanceSpec(env, "saucy", "amd64", "mem=4G") c.Assert(err, gc.ErrorMatches, `no "saucy" images in some-region with arches \[amd64\]`) } func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) { env := s.Prepare(c) params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("some-region") c.Assert(err, gc.IsNil) params.Sources, err = imagemetadata.GetMetadataSources(env) c.Assert(err, gc.IsNil) params.Series = "raring" image_ids, _, err := imagemetadata.ValidateImageMetadata(params) c.Assert(err, gc.IsNil) c.Assert(image_ids, gc.DeepEquals, []string{"11223344-0a0a-dd77-33cd-abcd1234e5f6"}) } func (s *localServerSuite) TestRemoveAll(c *gc.C) { env := s.Prepare(c) stor := env.Storage() for _, a := range []byte("abcdefghijklmnopqrstuvwxyz") { content := []byte{a} name := string(content) err := stor.Put(name, bytes.NewBuffer(content), int64(len(content))) c.Assert(err, gc.IsNil) } reader, err := storage.Get(stor, "a") c.Assert(err, gc.IsNil) allContent, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Assert(string(allContent), gc.Equals, "a") err = stor.RemoveAll() c.Assert(err, gc.IsNil) _, err = storage.Get(stor, "a") c.Assert(err, gc.NotNil) } func (s *localServerSuite) TestDeleteMoreThan100(c *gc.C) { env := s.Prepare(c) stor := env.Storage() // 6*26 = 156 items for _, a := range []byte("abcdef") { for _, b := range []byte("abcdefghijklmnopqrstuvwxyz") { content := []byte{a, b} name := string(content) err := stor.Put(name, bytes.NewBuffer(content), int64(len(content))) c.Assert(err, gc.IsNil) } } reader, err := storage.Get(stor, "ab") c.Assert(err, gc.IsNil) allContent, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Assert(string(allContent), gc.Equals, "ab") err = stor.RemoveAll() c.Assert(err, gc.IsNil) _, err = storage.Get(stor, "ab") c.Assert(err, gc.NotNil) } �����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/live_test.go���������������������������0000644�0000153�0000161�00000004716�12321735642�026364� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent_test import ( "crypto/rand" "fmt" "io" "os" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/jujutest" envtesting "launchpad.net/juju-core/environs/testing" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/version" ) // uniqueName is generated afresh for every test run, so that // we are not polluted by previous test state. var uniqueName = randomName() func randomName() string { buf := make([]byte, 8) _, err := io.ReadFull(rand.Reader, buf) if err != nil { panic(fmt.Sprintf("error from crypto rand: %v", err)) } return fmt.Sprintf("%x", buf) } func registerLiveTests() { attrs := coretesting.FakeConfig().Merge(map[string]interface{}{ "name": "sample-" + uniqueName, "type": "joyent", "sdc-user": os.Getenv("SDC_ACCOUNT"), "sdc-key-id": os.Getenv("SDC_KEY_ID"), "manta-user": os.Getenv("MANTA_USER"), "manta-key-id": os.Getenv("MANTA_KEY_ID"), "control-dir": "juju-test-" + uniqueName, "admin-secret": "for real", "firewall-mode": config.FwInstance, "agent-version": version.Current.Number.String(), }) gc.Suite(&LiveTests{ LiveTests: jujutest.LiveTests{ TestConfig: attrs, CanOpenState: true, HasProvisioner: true, }, }) } // LiveTests contains tests that can be run against the Joyent Public Cloud. type LiveTests struct { testbase.LoggingSuite jujutest.LiveTests } func (t *LiveTests) SetUpSuite(c *gc.C) { t.LoggingSuite.SetUpSuite(c) t.LiveTests.SetUpSuite(c) // For testing, we create a storage instance to which is uploaded tools and image metadata. t.PrepareOnce(c) c.Assert(t.Env.Storage(), gc.NotNil) // Put some fake tools metadata in place so that tests that are simply // starting instances without any need to check if those instances // are running can find the metadata. envtesting.UploadFakeTools(c, t.Env.Storage()) } func (t *LiveTests) TearDownSuite(c *gc.C) { if t.Env == nil { // This can happen if SetUpSuite fails. return } t.LiveTests.TearDownSuite(c) t.LoggingSuite.TearDownSuite(c) } func (t *LiveTests) SetUpTest(c *gc.C) { t.LoggingSuite.SetUpTest(c) t.LiveTests.SetUpTest(c) c.Assert(t.Env.Storage(), gc.NotNil) } func (t *LiveTests) TearDownTest(c *gc.C) { t.LiveTests.TearDownTest(c) t.LoggingSuite.TearDownTest(c) } ��������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/storage_test.go������������������������0000644�0000153�0000161�00000012542�12321735642�027065� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent_test import ( "fmt" "io/ioutil" "math/rand" "net/http" "net/url" "strings" "github.com/joyent/gocommon/errors" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" coreerrors "launchpad.net/juju-core/errors" "launchpad.net/juju-core/provider/joyent" jp "launchpad.net/juju-core/provider/joyent" ) type storageSuite struct { providerSuite localMantaServer } const ( storageName = "testStorage" fileName = "testFile" fileBlobContent = "Juju Joyent Provider Storage - Test" ) var _ = gc.Suite(&storageSuite{}) func (s *storageSuite) SetUpSuite(c *gc.C) { s.providerSuite.SetUpSuite(c) s.localMantaServer.setupServer(c) } func (s *storageSuite) TearDownSuite(c *gc.C) { s.localMantaServer.destroyServer() s.providerSuite.TearDownSuite(c) } // makeStorage creates a Manta storage object for the running test. func (s *storageSuite) makeStorage(name string, c *gc.C) *jp.JoyentStorage { stor := joyent.MakeStorage(c, GetFakeConfig("localhost", s.localMantaServer.Server.URL)) return stor.(*jp.JoyentStorage) } func (s *storageSuite) assertContainer(storage *jp.JoyentStorage, c *gc.C) { err := jp.CreateContainer(storage) c.Assert(err, gc.IsNil) } func (s *storageSuite) assertFile(storage *jp.JoyentStorage, c *gc.C) { err := storage.Put(fileName, strings.NewReader(fileBlobContent), int64(len(fileBlobContent))) c.Assert(err, gc.IsNil) } // makeRandomBytes returns an array of arbitrary byte values. func makeRandomBytes(length int) []byte { data := make([]byte, length) for index := range data { data[index] = byte(rand.Intn(256)) } return data } func makeResponse(content string, status int) *http.Response { return &http.Response{ Status: fmt.Sprintf("%d", status), StatusCode: status, Body: ioutil.NopCloser(strings.NewReader(content)), } } func (s *storageSuite) TestList(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) s.assertContainer(mantaStorage, c) s.assertFile(mantaStorage, c) names, err := mantaStorage.List("") c.Assert(err, gc.IsNil) c.Check(names, gc.DeepEquals, []string{fileName}) } func (s *storageSuite) TestListWithPrefix(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) s.assertContainer(mantaStorage, c) s.assertFile(mantaStorage, c) err := mantaStorage.Put("pr/fileName", strings.NewReader(fileBlobContent), int64(len(fileBlobContent))) c.Assert(err, gc.IsNil) names, err := mantaStorage.List("p") c.Assert(err, gc.IsNil) c.Check(names, gc.DeepEquals, []string{"pr/fileName"}) } func (s *storageSuite) TestGet(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) s.assertFile(mantaStorage, c) reader, err := mantaStorage.Get(fileName) c.Assert(err, gc.IsNil) c.Assert(reader, gc.NotNil) defer reader.Close() data, err := ioutil.ReadAll(reader) c.Assert(err, gc.IsNil) c.Check(string(data), gc.Equals, fileBlobContent) } func (s *storageSuite) TestGetFileNotExists(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) _, err := mantaStorage.Get("noFile") c.Assert(err, gc.NotNil) c.Assert(err, jc.Satisfies, coreerrors.IsNotFoundError) } func (s *storageSuite) TestPut(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) s.assertFile(mantaStorage, c) } func (s *storageSuite) TestRemove(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) s.assertFile(mantaStorage, c) err := mantaStorage.Remove(fileName) c.Assert(err, gc.IsNil) } func (s *storageSuite) TestRemoveFileNotExists(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) err := mantaStorage.Remove("nofile") c.Assert(err, gc.IsNil) } func (s *storageSuite) TestRemoveAll(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) err := mantaStorage.RemoveAll() c.Assert(err, gc.IsNil) } func (s *storageSuite) TestURL(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) URL, err := mantaStorage.URL(fileName) c.Assert(err, gc.IsNil) parsedURL, err := url.Parse(URL) c.Assert(err, gc.IsNil) c.Check(parsedURL.Host, gc.Matches, mantaStorage.GetMantaUrl()[strings.LastIndex(mantaStorage.GetMantaUrl(), "/")+1:]) c.Check(parsedURL.Path, gc.Matches, fmt.Sprintf("/%s/stor/%s/%s", mantaStorage.GetMantaUser(), mantaStorage.GetContainerName(), fileName)) } func (s *storageSuite) TestCreateContainer(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) s.assertContainer(mantaStorage, c) } func (s *storageSuite) TestCreateContainerAlreadyExists(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) s.assertContainer(mantaStorage, c) s.assertContainer(mantaStorage, c) } func (s *storageSuite) TestDeleteContainer(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) s.assertContainer(mantaStorage, c) err := mantaStorage.DeleteContainer(mantaStorage.GetContainerName()) c.Assert(err, gc.IsNil) } func (s *storageSuite) TestDeleteContainerNotEmpty(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) s.assertContainer(mantaStorage, c) s.assertFile(mantaStorage, c) err := mantaStorage.DeleteContainer(mantaStorage.GetContainerName()) c.Assert(err, gc.NotNil) c.Assert(err, jc.Satisfies, errors.IsBadRequest) } func (s *storageSuite) TestDeleteContainerNotExists(c *gc.C) { mantaStorage := s.makeStorage(storageName, c) err := mantaStorage.DeleteContainer("noContainer") c.Assert(err, gc.NotNil) c.Assert(err, jc.Satisfies, coreerrors.IsNotFoundError) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/environ_instance.go��������������������0000644�0000153�0000161�00000015413�12321735776�027736� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent import ( "fmt" "strings" "sync" "time" "github.com/joyent/gocommon/client" "github.com/joyent/gosdc/cloudapi" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/instances" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/names" "launchpad.net/juju-core/tools" "launchpad.net/juju-core/utils" ) var ( vTypeSmartmachine = "smartmachine" vTypeVirtualmachine = "kvm" signedImageDataOnly = false ) type joyentCompute struct { sync.Mutex ecfg *environConfig cloudapi *cloudapi.Client } func newCompute(cfg *environConfig) (*joyentCompute, error) { creds, err := credentials(cfg) if err != nil { return nil, err } client := client.NewClient(cfg.sdcUrl(), cloudapi.DefaultAPIVersion, creds, &logger) return &joyentCompute{ ecfg: cfg, cloudapi: cloudapi.New(client)}, nil } func (env *joyentEnviron) machineFullName(machineId string) string { return fmt.Sprintf("juju-%s-%s", env.Name(), names.MachineTag(machineId)) } func (env *joyentEnviron) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { series := args.Tools.OneSeries() arches := args.Tools.Arches() spec, err := env.FindInstanceSpec(&instances.InstanceConstraint{ Region: env.Ecfg().Region(), Series: series, Arches: arches, Constraints: args.Constraints, }) if err != nil { return nil, nil, err } tools, err := args.Tools.Match(tools.Filter{Arch: spec.Image.Arch}) if err != nil { return nil, nil, fmt.Errorf("chosen architecture %v not present in %v", spec.Image.Arch, arches) } args.MachineConfig.Tools = tools[0] if err := environs.FinishMachineConfig(args.MachineConfig, env.Config(), args.Constraints); err != nil { return nil, nil, err } userData, err := environs.ComposeUserData(args.MachineConfig, nil) if err != nil { return nil, nil, fmt.Errorf("cannot make user data: %v", err) } // Unzipping as Joyent API expects it as string userData, err = utils.Gunzip(userData) if err != nil { return nil, nil, fmt.Errorf("cannot make user data: %v", err) } logger.Debugf("joyent user data: %d bytes", len(userData)) var machine *cloudapi.Machine machine, err = env.compute.cloudapi.CreateMachine(cloudapi.CreateMachineOpts{ //Name: env.machineFullName(machineConf.MachineId), Package: spec.InstanceType.Name, Image: spec.Image.Id, Metadata: map[string]string{"metadata.cloud-init:user-data": string(userData)}, Tags: map[string]string{"tag.group": "juju", "tag.env": env.Name()}, }) if err != nil { return nil, nil, fmt.Errorf("cannot create instances: %v", err) } machineId := machine.Id logger.Infof("provisioning instance %q", machineId) machine, err = env.compute.cloudapi.GetMachine(machineId) if err != nil { return nil, nil, fmt.Errorf("cannot start instances: %v", err) } // wait for machine to start for !strings.EqualFold(machine.State, "running") { time.Sleep(1 * time.Second) machine, err = env.compute.cloudapi.GetMachine(machineId) if err != nil { return nil, nil, fmt.Errorf("cannot start instances: %v", err) } } logger.Infof("started instance %q", machineId) inst := &joyentInstance{ machine: machine, env: env, } disk64 := uint64(machine.Disk) hc := instance.HardwareCharacteristics{ Arch: &spec.Image.Arch, Mem: &spec.InstanceType.Mem, CpuCores: &spec.InstanceType.CpuCores, CpuPower: spec.InstanceType.CpuPower, RootDisk: &disk64, } return inst, &hc, nil } func (env *joyentEnviron) AllInstances() ([]instance.Instance, error) { instances := []instance.Instance{} filter := cloudapi.NewFilter() filter.Set("tag.group", "juju") filter.Set("tag.env", env.Name()) machines, err := env.compute.cloudapi.ListMachines(filter) if err != nil { return nil, fmt.Errorf("cannot retrieve instances: %v", err) } for _, m := range machines { if strings.EqualFold(m.State, "provisioning") || strings.EqualFold(m.State, "running") { copy := m instances = append(instances, &joyentInstance{machine: &copy, env: env}) } } return instances, nil } func (env *joyentEnviron) Instances(ids []instance.Id) ([]instance.Instance, error) { if len(ids) == 0 { return nil, nil } logger.Debugf("Looking for instances %q", ids) instances := make([]instance.Instance, len(ids)) found := 0 allInstances, err := env.AllInstances() if err != nil { return nil, err } for i, id := range ids { for _, instance := range allInstances { if instance.Id() == id { instances[i] = instance found++ } } } logger.Debugf("Found %d instances %q", found, instances) if found == 0 { return nil, environs.ErrNoInstances } else if found < len(ids) { return instances, environs.ErrPartialInstances } return instances, nil } func (env *joyentEnviron) StopInstances(instances []instance.Instance) error { // Remove all the instances in parallel so that we incur less round-trips. var wg sync.WaitGroup //var err error wg.Add(len(instances)) errc := make(chan error, len(instances)) for _, inst := range instances { inst := inst.(*joyentInstance) go func() { defer wg.Done() if err := inst.Stop(); err != nil { errc <- err } }() } wg.Wait() select { case err := <-errc: return fmt.Errorf("cannot stop all instances: %v", err) default: } return nil } // findInstanceSpec returns an InstanceSpec satisfying the supplied instanceConstraint. func (env *joyentEnviron) FindInstanceSpec(ic *instances.InstanceConstraint) (*instances.InstanceSpec, error) { packages, err := env.compute.cloudapi.ListPackages(nil) if err != nil { return nil, err } allInstanceTypes := []instances.InstanceType{} for _, pkg := range packages { instanceType := instances.InstanceType{ Id: pkg.Id, Name: pkg.Name, Arches: ic.Arches, Mem: uint64(pkg.Memory), CpuCores: uint64(pkg.VCPUs), RootDisk: uint64(pkg.Disk * 1024), VirtType: &vTypeVirtualmachine, } allInstanceTypes = append(allInstanceTypes, instanceType) } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: simplestreams.CloudSpec{ic.Region, env.Ecfg().SdcUrl()}, Series: []string{ic.Series}, Arches: ic.Arches, }) sources, err := imagemetadata.GetMetadataSources(env) if err != nil { return nil, err } matchingImages, _, err := imagemetadata.Fetch(sources, simplestreams.DefaultIndexPath, imageConstraint, signedImageDataOnly) if err != nil { return nil, err } images := instances.ImageMetadataToImages(matchingImages) spec, err := instances.FindInstanceSpec(images, ic, allInstanceTypes) if err != nil { return nil, err } return spec, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/environ.go�����������������������������0000644�0000153�0000161�00000012127�12321735776�026051� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent import ( "sync" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/imagemetadata" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" ) // This file contains the core of the Joyent Environ implementation. type joyentEnviron struct { name string // supportedArchitectures caches the architectures // for which images can be instantiated. archLock sync.Mutex supportedArchitectures []string // All mutating operations should lock the mutex. Non-mutating operations // should read all fields (other than name, which is immutable) from a // shallow copy taken with getSnapshot(). // This advice is predicated on the goroutine-safety of the values of the // affected fields. lock sync.Mutex ecfg *environConfig storage storage.Storage compute *joyentCompute } var _ environs.Environ = (*joyentEnviron)(nil) // newEnviron create a new Joyent environ instance from config. func newEnviron(cfg *config.Config) (*joyentEnviron, error) { env := new(joyentEnviron) if err := env.SetConfig(cfg); err != nil { return nil, err } env.name = cfg.Name() var err error env.storage, err = newStorage(env.ecfg, "") if err != nil { return nil, err } env.compute, err = newCompute(env.ecfg) if err != nil { return nil, err } return env, nil } func (env *joyentEnviron) SetName(envName string) { env.name = envName } func (env *joyentEnviron) Name() string { return env.name } func (*joyentEnviron) Provider() environs.EnvironProvider { return providerInstance } // SupportedArchitectures is specified on the EnvironCapability interface. func (env *joyentEnviron) SupportedArchitectures() ([]string, error) { env.archLock.Lock() defer env.archLock.Unlock() if env.supportedArchitectures != nil { return env.supportedArchitectures, nil } cfg := env.Ecfg() // Create a filter to get all images from our region and for the correct stream. cloudSpec := simplestreams.CloudSpec{ Region: cfg.Region(), Endpoint: cfg.SdcUrl(), } imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ CloudSpec: cloudSpec, Stream: cfg.ImageStream(), }) var err error env.supportedArchitectures, err = common.SupportedArchitectures(env, imageConstraint) return env.supportedArchitectures, err } func (env *joyentEnviron) SetConfig(cfg *config.Config) error { env.lock.Lock() defer env.lock.Unlock() ecfg, err := providerInstance.newConfig(cfg) if err != nil { return err } env.ecfg = ecfg return nil } func (env *joyentEnviron) getSnapshot() *joyentEnviron { env.lock.Lock() clone := *env env.lock.Unlock() clone.lock = sync.Mutex{} return &clone } func (env *joyentEnviron) Config() *config.Config { return env.getSnapshot().ecfg.Config } func (env *joyentEnviron) Storage() storage.Storage { return env.getSnapshot().storage } func (env *joyentEnviron) PublicStorage() storage.StorageReader { return environs.EmptyStorage } func (env *joyentEnviron) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { return common.Bootstrap(ctx, env, cons) } func (env *joyentEnviron) StateInfo() (*state.Info, *api.Info, error) { return common.StateInfo(env) } func (env *joyentEnviron) Destroy() error { return common.Destroy(env) } func (env *joyentEnviron) Ecfg() *environConfig { return env.getSnapshot().ecfg } // MetadataLookupParams returns parameters which are used to query simplestreams metadata. func (env *joyentEnviron) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) { if region == "" { region = env.Ecfg().Region() } return &simplestreams.MetadataLookupParams{ Series: config.PreferredSeries(env.Ecfg()), Region: region, Endpoint: env.Ecfg().sdcUrl(), Architectures: []string{"amd64", "armhf"}, }, nil } // Region is specified in the HasRegion interface. func (env *joyentEnviron) Region() (simplestreams.CloudSpec, error) { return simplestreams.CloudSpec{ Region: env.Ecfg().Region(), Endpoint: env.Ecfg().sdcUrl(), }, nil } // GetImageSources returns a list of sources which are used to search for simplestreams image metadata. func (env *joyentEnviron) GetImageSources() ([]simplestreams.DataSource, error) { // Add the simplestreams source off the control bucket. sources := []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", env.Storage(), storage.BaseImagesPath)} return sources, nil } // GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata. func (env *joyentEnviron) GetToolsSources() ([]simplestreams.DataSource, error) { // Add the simplestreams source off the control bucket. sources := []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", env.Storage(), storage.BaseToolsPath)} return sources, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/environ_firewall.go��������������������0000644�0000153�0000161�00000007175�12321735642�027735� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent import ( "fmt" "strconv" "strings" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/instance" "github.com/joyent/gosdc/cloudapi" ) const ( firewallRuleAll = "FROM tag %s TO tag juju ALLOW %s PORT %d" ) // Helper method to create a firewall rule string for the given port func createFirewallRuleAll(env *joyentEnviron, port instance.Port) string { return fmt.Sprintf(firewallRuleAll, env.Name(), strings.ToLower(port.Protocol), port.Number) } // Helper method to check if a firewall rule string already exist func ruleExists(rules []cloudapi.FirewallRule, rule string) (bool, string) { for _, r := range rules { if strings.EqualFold(r.Rule, rule) { return true, r.Id } } return false, "" } // Helper method to get port from the given firewall rules func getPorts(env *joyentEnviron, rules []cloudapi.FirewallRule) []instance.Port { ports := []instance.Port{} for _, r := range rules { rule := r.Rule if r.Enabled && strings.HasPrefix(rule, "FROM tag "+env.Name()) && strings.Contains(rule, "PORT") { p := rule[strings.Index(rule, "ALLOW")+6 : strings.Index(rule, "PORT")-1] n, _ := strconv.Atoi(rule[strings.LastIndex(rule, " ")+1:]) port := instance.Port{Protocol: p, Number: n} ports = append(ports, port) } } instance.SortPorts(ports) return ports } func (env *joyentEnviron) OpenPorts(ports []instance.Port) error { if env.Config().FirewallMode() != config.FwGlobal { return fmt.Errorf("invalid firewall mode %q for opening ports on environment", env.Config().FirewallMode()) } fwRules, err := env.compute.cloudapi.ListFirewallRules() if err != nil { return fmt.Errorf("cannot get firewall rules: %v", err) } for _, p := range ports { rule := createFirewallRuleAll(env, p) if e, id := ruleExists(fwRules, rule); e { _, err := env.compute.cloudapi.EnableFirewallRule(id) if err != nil { return fmt.Errorf("couldn't enable rule %s: %v", rule, err) } } else { _, err := env.compute.cloudapi.CreateFirewallRule(cloudapi.CreateFwRuleOpts{ Enabled: true, Rule: rule, }) if err != nil { return fmt.Errorf("couldn't create rule %s: %v", rule, err) } } } logger.Infof("ports %v opened in environment", ports) return nil } func (env *joyentEnviron) ClosePorts(ports []instance.Port) error { if env.Config().FirewallMode() != config.FwGlobal { return fmt.Errorf("invalid firewall mode %q for closing ports on environment", env.Config().FirewallMode()) } fwRules, err := env.compute.cloudapi.ListFirewallRules() if err != nil { return fmt.Errorf("cannot get firewall rules: %v", err) } for _, p := range ports { rule := createFirewallRuleAll(env, p) if e, id := ruleExists(fwRules, rule); e { _, err := env.compute.cloudapi.DisableFirewallRule(id) if err != nil { return fmt.Errorf("couldn't disable rule %s: %v", rule, err) } } else { _, err := env.compute.cloudapi.CreateFirewallRule(cloudapi.CreateFwRuleOpts{ Enabled: false, Rule: rule, }) if err != nil { return fmt.Errorf("couldn't create rule %s: %v", rule, err) } } } logger.Infof("ports %v closed in environment", ports) return nil } func (env *joyentEnviron) Ports() ([]instance.Port, error) { if env.Config().FirewallMode() != config.FwGlobal { return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from environment", env.Config().FirewallMode()) } fwRules, err := env.compute.cloudapi.ListFirewallRules() if err != nil { return nil, fmt.Errorf("cannot get firewall rules: %v", err) } return getPorts(env, fwRules), nil } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/joyent/provider_test.go�����������������������0000644�0000153�0000161�00000000554�12321735642�027253� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Joyent Inc. // Licensed under the AGPLv3, see LICENCE file for details. package joyent_test import ( "flag" "testing" gc "launchpad.net/gocheck" ) var live = flag.Bool("live", false, "Also run tests on live Joyent Public Cloud") func TestJoyent(t *testing.T) { if *live { registerLiveTests() } registerLocalTests() gc.TestingT(t) } ����������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/manual/���������������������������������������0000755�0000153�0000161�00000000000�12321736000�023761� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/manual/provider.go����������������������������0000644�0000153�0000161�00000011502�12321735642�026154� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual import ( "errors" "fmt" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/manual" "launchpad.net/juju-core/utils" ) type manualProvider struct{} func init() { p := manualProvider{} environs.RegisterProvider("manual", p, "null") } var errNoBootstrapHost = errors.New("bootstrap-host must be specified") var initUbuntuUser = manual.InitUbuntuUser func ensureBootstrapUbuntuUser(ctx environs.BootstrapContext, cfg *environConfig) error { err := initUbuntuUser(cfg.bootstrapHost(), cfg.bootstrapUser(), cfg.AuthorizedKeys(), ctx.GetStdin(), ctx.GetStdout()) if err != nil { logger.Errorf("initializing ubuntu user: %v", err) return err } logger.Infof("initialized ubuntu user") return nil } func (p manualProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { if _, ok := cfg.UnknownAttrs()["storage-auth-key"]; !ok { uuid, err := utils.NewUUID() if err != nil { return nil, err } cfg, err = cfg.Apply(map[string]interface{}{ "storage-auth-key": uuid.String(), }) if err != nil { return nil, err } } if use, ok := cfg.UnknownAttrs()["use-sshstorage"].(bool); ok && !use { return nil, fmt.Errorf("use-sshstorage must not be specified") } envConfig, err := p.validate(cfg, nil) if err != nil { return nil, err } if err := ensureBootstrapUbuntuUser(ctx, envConfig); err != nil { return nil, err } return p.open(envConfig) } func (p manualProvider) Open(cfg *config.Config) (environs.Environ, error) { envConfig, err := p.validate(cfg, nil) if err != nil { return nil, err } return p.open(envConfig) } func (p manualProvider) open(cfg *environConfig) (environs.Environ, error) { env := &manualEnviron{cfg: cfg} // Need to call SetConfig to initialise storage. if err := env.SetConfig(cfg.Config); err != nil { return nil, err } return env, nil } func checkImmutableString(cfg, old *environConfig, key string) error { if old.attrs[key] != cfg.attrs[key] { return fmt.Errorf("cannot change %s from %q to %q", key, old.attrs[key], cfg.attrs[key]) } return nil } func (p manualProvider) validate(cfg, old *config.Config) (*environConfig, error) { // Check for valid changes for the base config values. if err := config.Validate(cfg, old); err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } envConfig := newEnvironConfig(cfg, validated) if envConfig.bootstrapHost() == "" { return nil, errNoBootstrapHost } // Check various immutable attributes. if old != nil { oldEnvConfig, err := p.validate(old, nil) if err != nil { return nil, err } for _, key := range [...]string{ "bootstrap-user", "bootstrap-host", "storage-listen-ip", } { if err = checkImmutableString(envConfig, oldEnvConfig, key); err != nil { return nil, err } } oldPort, newPort := oldEnvConfig.storagePort(), envConfig.storagePort() if oldPort != newPort { return nil, fmt.Errorf("cannot change storage-port from %q to %q", oldPort, newPort) } oldUseSSHStorage, newUseSSHStorage := oldEnvConfig.useSSHStorage(), envConfig.useSSHStorage() if oldUseSSHStorage != newUseSSHStorage && newUseSSHStorage == true { return nil, fmt.Errorf("cannot change use-sshstorage from %v to %v", oldUseSSHStorage, newUseSSHStorage) } } return envConfig, nil } func (p manualProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { envConfig, err := p.validate(cfg, old) if err != nil { return nil, err } return cfg.Apply(envConfig.attrs) } func (_ manualProvider) BoilerplateConfig() string { return ` manual: type: manual # bootstrap-host holds the host name of the machine where the # bootstrap machine agent will be started. bootstrap-host: somehost.example.com # bootstrap-user specifies the user to authenticate as when # connecting to the bootstrap machine. If defaults to # the current user. # bootstrap-user: joebloggs # storage-listen-ip specifies the IP address that the # bootstrap machine's Juju storage server will listen # on. By default, storage will be served on all # network interfaces. # storage-listen-ip: # storage-port specifes the TCP port that the # bootstrap machine's Juju storage server will listen # on. It defaults to ` + fmt.Sprint(defaultStoragePort) + ` # storage-port: ` + fmt.Sprint(defaultStoragePort) + ` `[1:] } func (p manualProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { envConfig, err := p.validate(cfg, nil) if err != nil { return nil, err } attrs := make(map[string]string) attrs["storage-auth-key"] = envConfig.storageAuthKey() return attrs, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/manual/export_test.go�������������������������0000644�0000153�0000161�00000000342�12321735642�026702� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual var ( ProviderInstance = manualProvider{} NewSSHStorage = &newSSHStorage InitUbuntuUser = &initUbuntuUser ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/manual/config_test.go�������������������������0000644�0000153�0000161�00000011266�12321735776�026645� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual import ( "fmt" "regexp" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/config" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" ) type configSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&configSuite{}) func MinimalConfigValues() map[string]interface{} { return map[string]interface{}{ "name": "test", "type": "manual", "bootstrap-host": "hostname", "storage-auth-key": "whatever", // Not strictly necessary, but simplifies testing by disabling // ssh storage by default. "use-sshstorage": false, // While the ca-cert bits aren't entirely minimal, they avoid the need // to set up a fake home. "ca-cert": coretesting.CACert, "ca-private-key": coretesting.CAKey, } } func MinimalConfig(c *gc.C) *config.Config { minimal := MinimalConfigValues() testConfig, err := config.New(config.UseDefaults, minimal) c.Assert(err, gc.IsNil) return testConfig } func getEnvironConfig(c *gc.C, attrs map[string]interface{}) *environConfig { testConfig, err := config.New(config.UseDefaults, attrs) c.Assert(err, gc.IsNil) envConfig, err := manualProvider{}.validate(testConfig, nil) c.Assert(err, gc.IsNil) return envConfig } func (s *configSuite) TestValidateConfig(c *gc.C) { testConfig := MinimalConfig(c) testConfig, err := testConfig.Apply(map[string]interface{}{"bootstrap-host": ""}) c.Assert(err, gc.IsNil) _, err = manualProvider{}.Validate(testConfig, nil) c.Assert(err, gc.ErrorMatches, "bootstrap-host must be specified") testConfig, err = testConfig.Apply(map[string]interface{}{"storage-auth-key": nil}) c.Assert(err, gc.IsNil) _, err = manualProvider{}.Validate(testConfig, nil) c.Assert(err, gc.ErrorMatches, "storage-auth-key: expected string, got nothing") testConfig = MinimalConfig(c) valid, err := manualProvider{}.Validate(testConfig, nil) c.Assert(err, gc.IsNil) unknownAttrs := valid.UnknownAttrs() c.Assert(unknownAttrs["bootstrap-host"], gc.Equals, "hostname") c.Assert(unknownAttrs["bootstrap-user"], gc.Equals, "") c.Assert(unknownAttrs["storage-listen-ip"], gc.Equals, "") c.Assert(unknownAttrs["storage-port"], gc.Equals, int64(8040)) } func (s *configSuite) TestConfigMutability(c *gc.C) { testConfig := MinimalConfig(c) valid, err := manualProvider{}.Validate(testConfig, nil) c.Assert(err, gc.IsNil) unknownAttrs := valid.UnknownAttrs() // Make sure the immutable values can't be changed. It'd be nice to be // able to change these, but that would involve somehow updating the // machine agent's config/upstart config. oldConfig := testConfig for k, v := range map[string]interface{}{ "bootstrap-host": "new-hostname", "bootstrap-user": "new-username", "storage-listen-ip": "10.0.0.123", "storage-port": int64(1234), } { testConfig = MinimalConfig(c) testConfig, err = testConfig.Apply(map[string]interface{}{k: v}) c.Assert(err, gc.IsNil) _, err := manualProvider{}.Validate(testConfig, oldConfig) oldv := unknownAttrs[k] errmsg := fmt.Sprintf("cannot change %s from %q to %q", k, oldv, v) c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta(errmsg)) } } func (s *configSuite) TestBootstrapHostUser(c *gc.C) { values := MinimalConfigValues() testConfig := getEnvironConfig(c, values) c.Assert(testConfig.bootstrapHost(), gc.Equals, "hostname") c.Assert(testConfig.bootstrapUser(), gc.Equals, "") values["bootstrap-host"] = "127.0.0.1" values["bootstrap-user"] = "ubuntu" testConfig = getEnvironConfig(c, values) c.Assert(testConfig.bootstrapHost(), gc.Equals, "127.0.0.1") c.Assert(testConfig.bootstrapUser(), gc.Equals, "ubuntu") } func (s *configSuite) TestStorageParams(c *gc.C) { values := MinimalConfigValues() testConfig := getEnvironConfig(c, values) c.Assert(testConfig.storageAddr(), gc.Equals, "hostname:8040") c.Assert(testConfig.storageListenAddr(), gc.Equals, ":8040") values["storage-listen-ip"] = "10.0.0.123" values["storage-port"] = int64(1234) testConfig = getEnvironConfig(c, values) c.Assert(testConfig.storageAddr(), gc.Equals, "hostname:1234") c.Assert(testConfig.storageListenAddr(), gc.Equals, "10.0.0.123:1234") } func (s *configSuite) TestStorageCompat(c *gc.C) { // Older environment configurations will not have the // use-sshstorage attribute. We treat them as if they // have use-sshstorage=false. values := MinimalConfigValues() delete(values, "use-sshstorage") cfg, err := config.New(config.UseDefaults, values) c.Assert(err, gc.IsNil) envConfig := newEnvironConfig(cfg, values) c.Assert(err, gc.IsNil) c.Assert(envConfig.useSSHStorage(), jc.IsFalse) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/manual/config.go������������������������������0000644�0000153�0000161�00000004043�12321735776�025601� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual import ( "fmt" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/schema" ) const defaultStoragePort = 8040 var ( configFields = schema.Fields{ "bootstrap-host": schema.String(), "bootstrap-user": schema.String(), "storage-listen-ip": schema.String(), "storage-port": schema.Int(), "storage-auth-key": schema.String(), "use-sshstorage": schema.Bool(), } configDefaults = schema.Defaults{ "bootstrap-user": "", "storage-listen-ip": "", "storage-port": defaultStoragePort, "use-sshstorage": true, } ) type environConfig struct { *config.Config attrs map[string]interface{} } func newEnvironConfig(config *config.Config, attrs map[string]interface{}) *environConfig { return &environConfig{Config: config, attrs: attrs} } func (c *environConfig) useSSHStorage() bool { // Prior to 1.17.3, the use-sshstorage attribute // did not exist. We take non-existence to be // equivalent to false. useSSHStorage, _ := c.attrs["use-sshstorage"].(bool) return useSSHStorage } func (c *environConfig) bootstrapHost() string { return c.attrs["bootstrap-host"].(string) } func (c *environConfig) bootstrapUser() string { return c.attrs["bootstrap-user"].(string) } func (c *environConfig) storageListenIPAddress() string { return c.attrs["storage-listen-ip"].(string) } func (c *environConfig) storagePort() int { return int(c.attrs["storage-port"].(int64)) } func (c *environConfig) storageAuthKey() string { return c.attrs["storage-auth-key"].(string) } // storageAddr returns an address for connecting to the // bootstrap machine's localstorage. func (c *environConfig) storageAddr() string { return fmt.Sprintf("%s:%d", c.bootstrapHost(), c.storagePort()) } // storageListenAddr returns an address for the bootstrap // machine to listen on for its localstorage. func (c *environConfig) storageListenAddr() string { return fmt.Sprintf("%s:%d", c.storageListenIPAddress(), c.storagePort()) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/manual/instance.go����������������������������0000644�0000153�0000161�00000002212�12321735776�026134� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual import ( "launchpad.net/juju-core/environs/manual" "launchpad.net/juju-core/instance" ) type manualBootstrapInstance struct { host string } func (manualBootstrapInstance) Id() instance.Id { // The only way to bootrap is via manual bootstrap. return manual.BootstrapInstanceId } func (manualBootstrapInstance) Status() string { return "" } func (manualBootstrapInstance) Refresh() error { return nil } func (inst manualBootstrapInstance) Addresses() (addresses []instance.Address, err error) { return manual.HostAddresses(inst.host) } func (inst manualBootstrapInstance) DNSName() (string, error) { return inst.host, nil } func (i manualBootstrapInstance) WaitDNSName() (string, error) { return i.DNSName() } func (manualBootstrapInstance) OpenPorts(machineId string, ports []instance.Port) error { return nil } func (manualBootstrapInstance) ClosePorts(machineId string, ports []instance.Port) error { return nil } func (manualBootstrapInstance) Ports(machineId string) ([]instance.Port, error) { return []instance.Port{}, nil } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/manual/suite_test.go��������������������������0000644�0000153�0000161�00000000714�12321735642�026515� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual_test import ( "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/provider/manual" ) func Test(t *testing.T) { // Prevent any use of ssh for storage. *manual.NewSSHStorage = func(sshHost, storageDir, storageTmpdir string) (storage.Storage, error) { return nil, nil } gc.TestingT(t) } ����������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/manual/environ.go�����������������������������0000644�0000153�0000161�00000021130�12321735776�026010� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual import ( "bytes" "errors" "fmt" "net" "path" "strings" "sync" "github.com/juju/loggo" "launchpad.net/juju-core/agent" "launchpad.net/juju-core/constraints" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/httpstorage" "launchpad.net/juju-core/environs/manual" "launchpad.net/juju-core/environs/simplestreams" "launchpad.net/juju-core/environs/sshstorage" "launchpad.net/juju-core/environs/storage" envtools "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/provider/common" "launchpad.net/juju-core/state" "launchpad.net/juju-core/state/api" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" "launchpad.net/juju-core/worker/localstorage" "launchpad.net/juju-core/worker/terminationworker" ) const ( // storageSubdir is the subdirectory of // dataDir in which storage will be located. storageSubdir = "storage" // storageTmpSubdir is the subdirectory of // dataDir in which temporary storage will // be located. storageTmpSubdir = "storage-tmp" ) var logger = loggo.GetLogger("juju.provider.manual") type manualEnviron struct { cfg *environConfig cfgmutex sync.Mutex storage storage.Storage ubuntuUserInited bool ubuntuUserInitMutex sync.Mutex } var _ envtools.SupportsCustomSources = (*manualEnviron)(nil) var errNoStartInstance = errors.New("manual provider cannot start instances") var errNoStopInstance = errors.New("manual provider cannot stop instances") func (*manualEnviron) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { return nil, nil, errNoStartInstance } func (*manualEnviron) StopInstances([]instance.Instance) error { return errNoStopInstance } func (e *manualEnviron) AllInstances() ([]instance.Instance, error) { return e.Instances([]instance.Id{manual.BootstrapInstanceId}) } func (e *manualEnviron) envConfig() (cfg *environConfig) { e.cfgmutex.Lock() cfg = e.cfg e.cfgmutex.Unlock() return cfg } func (e *manualEnviron) Config() *config.Config { return e.envConfig().Config } func (e *manualEnviron) Name() string { return e.envConfig().Name() } // SupportedArchitectures is specified on the EnvironCapability interface. func (e *manualEnviron) SupportedArchitectures() ([]string, error) { return arch.AllSupportedArches, nil } func (e *manualEnviron) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { // Set "use-sshstorage" to false, so agents know not to use sshstorage. cfg, err := e.Config().Apply(map[string]interface{}{"use-sshstorage": false}) if err != nil { return err } if err := e.SetConfig(cfg); err != nil { return err } envConfig := e.envConfig() host := envConfig.bootstrapHost() hc, series, err := manual.DetectSeriesAndHardwareCharacteristics(host) if err != nil { return err } selectedTools, err := common.EnsureBootstrapTools(ctx, e, series, hc.Arch) if err != nil { return err } return manual.Bootstrap(manual.BootstrapArgs{ Context: ctx, Host: host, DataDir: agent.DefaultDataDir, Environ: e, PossibleTools: selectedTools, Series: series, HardwareCharacteristics: &hc, }) } func (e *manualEnviron) StateInfo() (*state.Info, *api.Info, error) { return common.StateInfo(e) } func (e *manualEnviron) SetConfig(cfg *config.Config) error { e.cfgmutex.Lock() defer e.cfgmutex.Unlock() envConfig, err := manualProvider{}.validate(cfg, e.cfg.Config) if err != nil { return err } // Set storage. If "use-sshstorage" is true then use the SSH storage. // Otherwise, use HTTP storage. // // We don't change storage once it's been set. Storage parameters // are fixed at bootstrap time, and it is not possible to change // them. if e.storage == nil { var stor storage.Storage if envConfig.useSSHStorage() { storageDir := e.StorageDir() storageTmpdir := path.Join(agent.DefaultDataDir, storageTmpSubdir) stor, err = newSSHStorage("ubuntu@"+e.cfg.bootstrapHost(), storageDir, storageTmpdir) if err != nil { return fmt.Errorf("initialising SSH storage failed: %v", err) } } else { caCertPEM, ok := envConfig.CACert() if !ok { // should not be possible to validate base config return fmt.Errorf("ca-cert not set") } authkey := envConfig.storageAuthKey() stor, err = httpstorage.ClientTLS(envConfig.storageAddr(), caCertPEM, authkey) if err != nil { return fmt.Errorf("initialising HTTPS storage failed: %v", err) } } e.storage = stor } e.cfg = envConfig return nil } // Implements environs.Environ. // // This method will only ever return an Instance for the Id // environ/manual.BootstrapInstanceId. If any others are // specified, then ErrPartialInstances or ErrNoInstances // will result. func (e *manualEnviron) Instances(ids []instance.Id) (instances []instance.Instance, err error) { instances = make([]instance.Instance, len(ids)) var found bool for i, id := range ids { if id == manual.BootstrapInstanceId { instances[i] = manualBootstrapInstance{e.envConfig().bootstrapHost()} found = true } else { err = environs.ErrPartialInstances } } if !found { err = environs.ErrNoInstances } return instances, err } var newSSHStorage = func(sshHost, storageDir, storageTmpdir string) (storage.Storage, error) { logger.Debugf("using ssh storage at host %q dir %q", sshHost, storageDir) return sshstorage.NewSSHStorage(sshstorage.NewSSHStorageParams{ Host: sshHost, StorageDir: storageDir, TmpDir: storageTmpdir, }) } // GetToolsSources returns a list of sources which are // used to search for simplestreams tools metadata. func (e *manualEnviron) GetToolsSources() ([]simplestreams.DataSource, error) { // Add the simplestreams source off private storage. return []simplestreams.DataSource{ storage.NewStorageSimpleStreamsDataSource("cloud storage", e.Storage(), storage.BaseToolsPath), }, nil } func (e *manualEnviron) Storage() storage.Storage { e.cfgmutex.Lock() defer e.cfgmutex.Unlock() return e.storage } var runSSHCommand = func(host string, command []string, stdin string) (stderr string, err error) { cmd := ssh.Command(host, command, nil) var stderrBuf bytes.Buffer cmd.Stdin = strings.NewReader(stdin) cmd.Stderr = &stderrBuf err = cmd.Run() return stderrBuf.String(), err } func (e *manualEnviron) Destroy() error { script := ` set -x pkill -%d jujud && exit stop juju-db rm -f /etc/init/juju* rm -f /etc/rsyslog.d/*juju* rm -fr %s %s exit 0 ` script = fmt.Sprintf( script, terminationworker.TerminationSignal, utils.ShQuote(agent.DefaultDataDir), utils.ShQuote(agent.DefaultLogDir), ) stderr, err := runSSHCommand( "ubuntu@"+e.envConfig().bootstrapHost(), []string{"sudo", "/bin/bash"}, script, ) if err != nil { if stderr := strings.TrimSpace(stderr); len(stderr) > 0 { err = fmt.Errorf("%v (%v)", err, stderr) } } return err } func (*manualEnviron) PrecheckInstance(series string, cons constraints.Value) error { return errors.New(`use "juju add-machine ssh:[user@]<host>" to provision machines`) } func (e *manualEnviron) OpenPorts(ports []instance.Port) error { return nil } func (e *manualEnviron) ClosePorts(ports []instance.Port) error { return nil } func (e *manualEnviron) Ports() ([]instance.Port, error) { return []instance.Port{}, nil } func (*manualEnviron) Provider() environs.EnvironProvider { return manualProvider{} } func (e *manualEnviron) StorageAddr() string { return e.envConfig().storageListenAddr() } func (e *manualEnviron) StorageDir() string { return path.Join(agent.DefaultDataDir, storageSubdir) } func (e *manualEnviron) SharedStorageAddr() string { return "" } func (e *manualEnviron) SharedStorageDir() string { return "" } func (e *manualEnviron) StorageCACert() []byte { if bytes, ok := e.envConfig().CACert(); ok { return bytes } return nil } func (e *manualEnviron) StorageCAKey() []byte { if bytes, ok := e.envConfig().CAPrivateKey(); ok { return bytes } return nil } func (e *manualEnviron) StorageHostnames() []string { cfg := e.envConfig() hostnames := []string{cfg.bootstrapHost()} if ip := net.ParseIP(cfg.storageListenIPAddress()); ip != nil { if !ip.IsUnspecified() { hostnames = append(hostnames, ip.String()) } } return hostnames } func (e *manualEnviron) StorageAuthKey() string { return e.envConfig().storageAuthKey() } var _ localstorage.LocalTLSStorageConfig = (*manualEnviron)(nil) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/manual/environ_test.go������������������������0000644�0000153�0000161�00000010023�12321735776�027046� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual import ( "errors" "strings" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/manual" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/environs/tools" "launchpad.net/juju-core/instance" "launchpad.net/juju-core/juju/arch" "launchpad.net/juju-core/testing/testbase" ) type environSuite struct { testbase.LoggingSuite env *manualEnviron } type dummyStorage struct { storage.Storage } var _ = gc.Suite(&environSuite{}) func (s *environSuite) SetUpTest(c *gc.C) { env, err := manualProvider{}.Open(MinimalConfig(c)) c.Assert(err, gc.IsNil) s.env = env.(*manualEnviron) } func (s *environSuite) TestSetConfig(c *gc.C) { err := s.env.SetConfig(MinimalConfig(c)) c.Assert(err, gc.IsNil) testConfig := MinimalConfig(c) testConfig, err = testConfig.Apply(map[string]interface{}{"bootstrap-host": ""}) c.Assert(err, gc.IsNil) err = s.env.SetConfig(testConfig) c.Assert(err, gc.ErrorMatches, "bootstrap-host must be specified") } func (s *environSuite) TestInstances(c *gc.C) { var ids []instance.Id instances, err := s.env.Instances(ids) c.Assert(err, gc.Equals, environs.ErrNoInstances) c.Assert(instances, gc.HasLen, 0) ids = append(ids, manual.BootstrapInstanceId) instances, err = s.env.Instances(ids) c.Assert(err, gc.IsNil) c.Assert(instances, gc.HasLen, 1) c.Assert(instances[0], gc.NotNil) ids = append(ids, manual.BootstrapInstanceId) instances, err = s.env.Instances(ids) c.Assert(err, gc.IsNil) c.Assert(instances, gc.HasLen, 2) c.Assert(instances[0], gc.NotNil) c.Assert(instances[1], gc.NotNil) ids = append(ids, instance.Id("invalid")) instances, err = s.env.Instances(ids) c.Assert(err, gc.Equals, environs.ErrPartialInstances) c.Assert(instances, gc.HasLen, 3) c.Assert(instances[0], gc.NotNil) c.Assert(instances[1], gc.NotNil) c.Assert(instances[2], gc.IsNil) ids = []instance.Id{instance.Id("invalid")} instances, err = s.env.Instances(ids) c.Assert(err, gc.Equals, environs.ErrNoInstances) c.Assert(instances, gc.HasLen, 1) c.Assert(instances[0], gc.IsNil) } func (s *environSuite) TestDestroy(c *gc.C) { var resultStderr string var resultErr error runSSHCommandTesting := func(host string, command []string, stdin string) (string, error) { c.Assert(host, gc.Equals, "ubuntu@hostname") c.Assert(command, gc.DeepEquals, []string{"sudo", "/bin/bash"}) c.Assert(stdin, gc.DeepEquals, ` set -x pkill -6 jujud && exit stop juju-db rm -f /etc/init/juju* rm -f /etc/rsyslog.d/*juju* rm -fr '/var/lib/juju' '/var/log/juju' exit 0 `) return resultStderr, resultErr } s.PatchValue(&runSSHCommand, runSSHCommandTesting) type test struct { stderr string err error match string } tests := []test{ {"", nil, ""}, {"abc", nil, ""}, {"", errors.New("oh noes"), "oh noes"}, {"123", errors.New("abc"), "abc \\(123\\)"}, } for i, t := range tests { c.Logf("test %d: %v", i, t) resultStderr, resultErr = t.stderr, t.err err := s.env.Destroy() if t.match == "" { c.Assert(err, gc.IsNil) } else { c.Assert(err, gc.ErrorMatches, t.match) } } } func (s *environSuite) TestLocalStorageConfig(c *gc.C) { c.Assert(s.env.StorageDir(), gc.Equals, "/var/lib/juju/storage") c.Assert(s.env.cfg.storageListenAddr(), gc.Equals, ":8040") c.Assert(s.env.StorageAddr(), gc.Equals, s.env.cfg.storageListenAddr()) c.Assert(s.env.SharedStorageAddr(), gc.Equals, "") c.Assert(s.env.SharedStorageDir(), gc.Equals, "") } func (s *environSuite) TestEnvironSupportsCustomSources(c *gc.C) { sources, err := tools.GetMetadataSources(s.env) c.Assert(err, gc.IsNil) c.Assert(len(sources), gc.Equals, 2) url, err := sources[0].URL("") c.Assert(err, gc.IsNil) c.Assert(strings.Contains(url, "/tools"), jc.IsTrue) } func (s *environSuite) TestSupportedArchitectures(c *gc.C) { arches, err := s.env.SupportedArchitectures() c.Assert(err, gc.IsNil) c.Assert(arches, gc.DeepEquals, arch.AllSupportedArches) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/provider/manual/provider_test.go�����������������������0000644�0000153�0000161�00000004430�12321735776�027225� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package manual_test import ( "fmt" "io" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/environs" "launchpad.net/juju-core/environs/config" "launchpad.net/juju-core/environs/storage" "launchpad.net/juju-core/provider/manual" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type providerSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&providerSuite{}) func (s *providerSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.PatchValue(manual.InitUbuntuUser, func(host, user, keys string, stdin io.Reader, stdout io.Writer) error { return nil }) } func (s *providerSuite) TestPrepare(c *gc.C) { minimal := manual.MinimalConfigValues() minimal["use-sshstorage"] = true delete(minimal, "storage-auth-key") testConfig, err := config.New(config.UseDefaults, minimal) c.Assert(err, gc.IsNil) env, err := manual.ProviderInstance.Prepare(coretesting.Context(c), testConfig) c.Assert(err, gc.IsNil) cfg := env.Config() key, _ := cfg.UnknownAttrs()["storage-auth-key"].(string) c.Assert(key, jc.Satisfies, utils.IsValidUUIDString) } func (s *providerSuite) TestPrepareUseSSHStorage(c *gc.C) { minimal := manual.MinimalConfigValues() minimal["use-sshstorage"] = false testConfig, err := config.New(config.UseDefaults, minimal) c.Assert(err, gc.IsNil) _, err = manual.ProviderInstance.Prepare(coretesting.Context(c), testConfig) c.Assert(err, gc.ErrorMatches, "use-sshstorage must not be specified") s.PatchValue(manual.NewSSHStorage, func(sshHost, storageDir, storageTmpdir string) (storage.Storage, error) { return nil, fmt.Errorf("newSSHStorage failed") }) minimal["use-sshstorage"] = true testConfig, err = config.New(config.UseDefaults, minimal) c.Assert(err, gc.IsNil) _, err = manual.ProviderInstance.Prepare(coretesting.Context(c), testConfig) c.Assert(err, gc.ErrorMatches, "initialising SSH storage failed: newSSHStorage failed") } func (s *providerSuite) TestNullAlias(c *gc.C) { p, err := environs.Provider("manual") c.Assert(p, gc.NotNil) c.Assert(err, gc.IsNil) p, err = environs.Provider("null") c.Assert(p, gc.NotNil) c.Assert(err, gc.IsNil) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/dependencies.tsv���������������������������������������0000644�0000153�0000161�00000003065�12321735776�024065� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������code.google.com/p/go.crypto hg 6478cc9340cbbe6c04511280c5007722269108e9 184 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 github.com/joyent/gosdc git 10bbe92c5d98c8b38a0b7f62ee042c7252150efc github.com/joyent/gosign git 476720af5427223da5420afbbadf620bfb760345 github.com/juju/loggo git fa3acf9ab9ed09aea29030558528e24a254d27af github.com/juju/ratelimit git 0025ab75db6c6eaa4ffff0240c2c9e617ad1a0eb github.com/juju/testing git 9c0e0686136637876ae659e9056897575236e11f labix.org/v2/mgo bzr gustavo@niemeyer.net-20131118213720-aralgr4ienh0gdyq 248 launchpad.net/gnuflag bzr roger.peppe@canonical.com-20121003093437-zcyyw0lpvj2nifpk 12 launchpad.net/goamz bzr roger.peppe@canonical.com-20131218155244-hbnkvlkkzy3vmlh9 44 launchpad.net/gocheck bzr gustavo@niemeyer.net-20130302024745-6ikofwq2c03h7giu 85 launchpad.net/golxc bzr tim.penhey@canonical.com-20140311005930-b14361bwnocu3krh 8 launchpad.net/gomaasapi bzr ian.booth@canonical.com-20131017011445-m1hmr0ap14osd7li 47 launchpad.net/goose bzr tarmac-20140124165235-h9rloooc531udms5 116 launchpad.net/goyaml bzr gustavo@niemeyer.net-20131114120802-abe042syx64z2m7s 50 launchpad.net/gwacl bzr tarmac-20140205045433-81h182mhz24fzp5e 231 launchpad.net/lpad bzr gustavo@niemeyer.net-20120626194701-536yx0g9jdq2ik3h 64 launchpad.net/tomb bzr gustavo@niemeyer.net-20130531003818-70ikdgklbxopn8x4 17 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/�������������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022012� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/apt_test.go��������������������������������������0000644�0000153�0000161�00000015267�12321735642�024212� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils_test import ( "fmt" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type AptSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&AptSuite{}) func (s *AptSuite) TestOnePackage(c *gc.C) { cmdChan := s.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil) err := utils.AptGetInstall("test-package") c.Assert(err, gc.IsNil) cmd := <-cmdChan c.Assert(cmd.Args, gc.DeepEquals, []string{ "apt-get", "--option=Dpkg::Options::=--force-confold", "--option=Dpkg::options::=--force-unsafe-io", "--assume-yes", "--quiet", "install", "test-package", }) c.Assert(cmd.Env[len(cmd.Env)-1], gc.Equals, "DEBIAN_FRONTEND=noninteractive") } func (s *AptSuite) TestAptGetPreparePackages(c *gc.C) { packagesList := utils.AptGetPreparePackages([]string{"lxc", "bridge-utils", "git", "mongodb"}, "precise") c.Assert(packagesList[0], gc.DeepEquals, []string{"--target-release", "precise-updates/cloud-tools", "lxc", "mongodb"}) c.Assert(packagesList[1], gc.DeepEquals, []string{"bridge-utils", "git"}) } func (s *AptSuite) TestAptGetError(c *gc.C) { const expected = `E: frobnicator failure detected` cmdError := fmt.Errorf("error") cmdExpectedError := fmt.Errorf("apt-get failed: error") cmdChan := s.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), cmdError) err := utils.AptGetInstall("foo") c.Assert(err, gc.DeepEquals, cmdExpectedError) cmd := <-cmdChan c.Assert(cmd.Args, gc.DeepEquals, []string{ "apt-get", "--option=Dpkg::Options::=--force-confold", "--option=Dpkg::options::=--force-unsafe-io", "--assume-yes", "--quiet", "install", "foo", }) } func (s *AptSuite) TestConfigProxyEmpty(c *gc.C) { cmdChan := s.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil) out, err := utils.AptConfigProxy() c.Assert(err, gc.IsNil) cmd := <-cmdChan c.Assert(cmd.Args, gc.DeepEquals, []string{ "apt-config", "dump", "Acquire::http::Proxy", "Acquire::https::Proxy", "Acquire::ftp::Proxy", }) c.Assert(out, gc.Equals, "") } func (s *AptSuite) TestConfigProxyConfigured(c *gc.C) { const expected = `Acquire::http::Proxy "10.0.3.1:3142"; Acquire::https::Proxy "false";` cmdChan := s.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), nil) out, err := utils.AptConfigProxy() c.Assert(err, gc.IsNil) cmd := <-cmdChan c.Assert(cmd.Args, gc.DeepEquals, []string{ "apt-config", "dump", "Acquire::http::Proxy", "Acquire::https::Proxy", "Acquire::ftp::Proxy", }) c.Assert(out, gc.Equals, expected) } func (s *AptSuite) TestDetectAptProxy(c *gc.C) { const output = `CommandLine::AsString "apt-config dump"; Acquire::http::Proxy "10.0.3.1:3142"; Acquire::https::Proxy "false"; Acquire::ftp::Proxy "none"; Acquire::magic::Proxy "none"; ` _ = s.HookCommandOutput(&utils.AptCommandOutput, []byte(output), nil) proxy, err := utils.DetectAptProxies() c.Assert(err, gc.IsNil) c.Assert(proxy, gc.DeepEquals, osenv.ProxySettings{ Http: "10.0.3.1:3142", Https: "false", Ftp: "none", }) } func (s *AptSuite) TestDetectAptProxyNone(c *gc.C) { _ = s.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil) proxy, err := utils.DetectAptProxies() c.Assert(err, gc.IsNil) c.Assert(proxy, gc.DeepEquals, osenv.ProxySettings{}) } func (s *AptSuite) TestDetectAptProxyPartial(c *gc.C) { const output = `CommandLine::AsString "apt-config dump"; Acquire::http::Proxy "10.0.3.1:3142"; Acquire::ftp::Proxy "here-it-is"; Acquire::magic::Proxy "none"; ` _ = s.HookCommandOutput(&utils.AptCommandOutput, []byte(output), nil) proxy, err := utils.DetectAptProxies() c.Assert(err, gc.IsNil) c.Assert(proxy, gc.DeepEquals, osenv.ProxySettings{ Http: "10.0.3.1:3142", Ftp: "here-it-is", }) } func (s *AptSuite) TestAptProxyContentEmpty(c *gc.C) { output := utils.AptProxyContent(osenv.ProxySettings{}) c.Assert(output, gc.Equals, "") } func (s *AptSuite) TestAptProxyContentPartial(c *gc.C) { proxy := osenv.ProxySettings{ Http: "user@10.0.0.1", } output := utils.AptProxyContent(proxy) expected := `Acquire::http::Proxy "user@10.0.0.1";` c.Assert(output, gc.Equals, expected) } func (s *AptSuite) TestAptProxyContentRoundtrip(c *gc.C) { proxy := osenv.ProxySettings{ Http: "http://user@10.0.0.1", Https: "https://user@10.0.0.1", Ftp: "ftp://user@10.0.0.1", } output := utils.AptProxyContent(proxy) s.HookCommandOutput(&utils.AptCommandOutput, []byte(output), nil) detected, err := utils.DetectAptProxies() c.Assert(err, gc.IsNil) c.Assert(detected, gc.DeepEquals, proxy) } func (s *AptSuite) TestConfigProxyConfiguredFilterOutput(c *gc.C) { const ( output = `CommandLine::AsString "apt-config dump"; Acquire::http::Proxy "10.0.3.1:3142"; Acquire::https::Proxy "false";` expected = `Acquire::http::Proxy "10.0.3.1:3142"; Acquire::https::Proxy "false";` ) cmdChan := s.HookCommandOutput(&utils.AptCommandOutput, []byte(output), nil) out, err := utils.AptConfigProxy() c.Assert(err, gc.IsNil) cmd := <-cmdChan c.Assert(cmd.Args, gc.DeepEquals, []string{ "apt-config", "dump", "Acquire::http::Proxy", "Acquire::https::Proxy", "Acquire::ftp::Proxy", }) c.Assert(out, gc.Equals, expected) } func (s *AptSuite) TestConfigProxyError(c *gc.C) { const expected = `E: frobnicator failure detected` cmdError := fmt.Errorf("error") cmdExpectedError := fmt.Errorf("apt-config failed: error") cmdChan := s.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), cmdError) out, err := utils.AptConfigProxy() c.Assert(err, gc.DeepEquals, cmdExpectedError) cmd := <-cmdChan c.Assert(cmd.Args, gc.DeepEquals, []string{ "apt-config", "dump", "Acquire::http::Proxy", "Acquire::https::Proxy", "Acquire::ftp::Proxy", }) c.Assert(out, gc.Equals, "") } func (s *AptSuite) patchLsbRelease(c *gc.C, name string) { content := fmt.Sprintf("#!/bin/bash --norc\necho %s", name) patchExecutable(s, c.MkDir(), "lsb_release", content) } func (s *AptSuite) TestIsUbuntu(c *gc.C) { s.patchLsbRelease(c, "Ubuntu") c.Assert(utils.IsUbuntu(), jc.IsTrue) } func (s *AptSuite) TestIsNotUbuntu(c *gc.C) { s.patchLsbRelease(c, "Windows NT") c.Assert(utils.IsUbuntu(), jc.IsFalse) } func (s *AptSuite) patchDpkgQuery(c *gc.C, installed bool) { rc := 0 if !installed { rc = 1 } content := fmt.Sprintf("#!/bin/bash --norc\nexit %v", rc) patchExecutable(s, c.MkDir(), "dpkg-query", content) } func (s *AptSuite) TestIsPackageInstalled(c *gc.C) { s.patchDpkgQuery(c, true) c.Assert(utils.IsPackageInstalled("foo-bar"), jc.IsTrue) } func (s *AptSuite) TestIsPackageNotInstalled(c *gc.C) { s.patchDpkgQuery(c, false) c.Assert(utils.IsPackageInstalled("foo-bar"), jc.IsFalse) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/uuid_test.go�������������������������������������0000644�0000153�0000161�00000002335�12321735642�024364� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/utils" ) type uuidSuite struct{} var _ = gc.Suite(uuidSuite{}) func (uuidSuite) TestUUID(c *gc.C) { uuid, err := utils.NewUUID() c.Assert(err, gc.IsNil) uuidCopy := uuid.Copy() uuidRaw := uuid.Raw() uuidStr := uuid.String() c.Assert(uuidRaw, gc.HasLen, 16) c.Assert(uuidStr, jc.Satisfies, utils.IsValidUUIDString) uuid[0] = 0x00 uuidCopy[0] = 0xFF c.Assert(uuid, gc.Not(gc.DeepEquals), uuidCopy) uuidRaw[0] = 0xFF c.Assert(uuid, gc.Not(gc.DeepEquals), uuidRaw) nextUUID, err := utils.NewUUID() c.Assert(err, gc.IsNil) c.Assert(uuid, gc.Not(gc.DeepEquals), nextUUID) } func (uuidSuite) TestIsValidUUIDFailsWhenNotValid(c *gc.C) { c.Assert(utils.IsValidUUIDString("blah"), gc.Equals, false) } func (uuidSuite) TestUUIDFromString(c *gc.C) { _, err := utils.UUIDFromString("blah") c.Assert(err, gc.ErrorMatches, `invalid UUID: "blah"`) validUUID := "9f484882-2f18-4fd2-967d-db9663db7bea" uuid, err := utils.UUIDFromString(validUUID) c.Assert(err, gc.IsNil) c.Assert(uuid.String(), gc.Equals, validUUID) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/fslock/������������������������������������������0000755�0000153�0000161�00000000000�12321735642�023306� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/fslock/fslock.go���������������������������������0000644�0000153�0000161�00000015277�12321735642�025132� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // On-disk mutex protecting a resource // // A lock is represented on disk by a directory of a particular name, // containing an information file. Taking a lock is done by renaming a // temporary directory into place. We use temporary directories because for // all filesystems we believe that exactly one attempt to claim the lock will // succeed and the others will fail. package fslock import ( "bytes" "errors" "fmt" "io/ioutil" "os" "path" "regexp" "time" "github.com/juju/loggo" "launchpad.net/juju-core/utils" ) const ( // NameRegexp specifies the regular expression used to identify valid lock names. NameRegexp = "^[a-z]+[a-z0-9.-]*$" heldFilename = "held" messageFilename = "message" ) var ( logger = loggo.GetLogger("juju.utils.fslock") ErrLockNotHeld = errors.New("lock not held") ErrTimeout = errors.New("lock timeout exceeded") validName = regexp.MustCompile(NameRegexp) LockWaitDelay = 1 * time.Second ) type Lock struct { name string parent string nonce []byte } // NewLock returns a new lock with the given name within the given lock // directory, without acquiring it. The lock name must match the regular // expression defined by NameRegexp. func NewLock(lockDir, name string) (*Lock, error) { if !validName.MatchString(name) { return nil, fmt.Errorf("Invalid lock name %q. Names must match %q", name, NameRegexp) } nonce, err := utils.NewUUID() if err != nil { return nil, err } lock := &Lock{ name: name, parent: lockDir, nonce: nonce[:], } // Ensure the parent exists. if err := os.MkdirAll(lock.parent, 0755); err != nil { return nil, err } return lock, nil } func (lock *Lock) lockDir() string { return path.Join(lock.parent, lock.name) } func (lock *Lock) heldFile() string { return path.Join(lock.lockDir(), "held") } func (lock *Lock) messageFile() string { return path.Join(lock.lockDir(), "message") } // If message is set, it will write the message to the lock directory as the // lock is taken. func (lock *Lock) acquire(message string) (bool, error) { // If the lockDir exists, then the lock is held by someone else. _, err := os.Stat(lock.lockDir()) if err == nil { return false, nil } if !os.IsNotExist(err) { return false, err } // Create a temporary directory (in the parent dir), and then move it to // the right name. Using the same directory to make sure the directories // are on the same filesystem. Use a directory name starting with "." as // it isn't a valid lock name. tempLockName := fmt.Sprintf(".%x", lock.nonce) tempDirName, err := ioutil.TempDir(lock.parent, tempLockName) if err != nil { return false, err // this shouldn't really fail... } // write nonce into the temp dir err = ioutil.WriteFile(path.Join(tempDirName, heldFilename), lock.nonce, 0755) if err != nil { return false, err } if message != "" { err = ioutil.WriteFile(path.Join(tempDirName, messageFilename), []byte(message), 0755) if err != nil { return false, err } } // Now move the temp directory to the lock directory. err = utils.ReplaceFile(tempDirName, lock.lockDir()) if err != nil { // Any error on rename means we failed. // Beaten to it, clean up temporary directory. os.RemoveAll(tempDirName) return false, nil } // We now have the lock. return true, nil } // lockLoop tries to acquire the lock. If the acquisition fails, the // continueFunc is run to see if the function should continue waiting. func (lock *Lock) lockLoop(message string, continueFunc func() error) error { var heldMessage = "" for { acquired, err := lock.acquire(message) if err != nil { return err } if acquired { return nil } if err = continueFunc(); err != nil { return err } currMessage := lock.Message() if currMessage != heldMessage { logger.Infof("attempted lock failed %q, %s, currently held: %s", lock.name, message, currMessage) heldMessage = currMessage } time.Sleep(LockWaitDelay) } } // Lock blocks until it is able to acquire the lock. Since we are dealing // with sharing and locking using the filesystem, it is good behaviour to // provide a message that is saved with the lock. This is output in debugging // information, and can be queried by any other Lock dealing with the same // lock name and lock directory. func (lock *Lock) Lock(message string) error { // The continueFunc is effectively a no-op, causing continual looping // until the lock is acquired. continueFunc := func() error { return nil } return lock.lockLoop(message, continueFunc) } // LockWithTimeout tries to acquire the lock. If it cannot acquire the lock // within the given duration, it returns ErrTimeout. See `Lock` for // information about the message. func (lock *Lock) LockWithTimeout(duration time.Duration, message string) error { deadline := time.Now().Add(duration) continueFunc := func() error { if time.Now().After(deadline) { return ErrTimeout } return nil } return lock.lockLoop(message, continueFunc) } // Lock blocks until it is able to acquire the lock. If the lock is failed to // be acquired, the continueFunc is called prior to the sleeping. If the // continueFunc returns an error, that error is returned from LockWithFunc. func (lock *Lock) LockWithFunc(message string, continueFunc func() error) error { return lock.lockLoop(message, continueFunc) } // IsHeld returns whether the lock is currently held by the receiver. func (lock *Lock) IsLockHeld() bool { heldNonce, err := ioutil.ReadFile(lock.heldFile()) if err != nil { return false } return bytes.Equal(heldNonce, lock.nonce) } // Unlock releases a held lock. If the lock is not held ErrLockNotHeld is // returned. func (lock *Lock) Unlock() error { if !lock.IsLockHeld() { return ErrLockNotHeld } // To ensure reasonable unlocking, we should rename to a temp name, and delete that. tempLockName := fmt.Sprintf(".%s.%x", lock.name, lock.nonce) tempDirName := path.Join(lock.parent, tempLockName) // Now move the lock directory to the temp directory to release the lock. if err := utils.ReplaceFile(lock.lockDir(), tempDirName); err != nil { return err } // And now cleanup. return os.RemoveAll(tempDirName) } // IsLocked returns true if the lock is currently held by anyone. func (lock *Lock) IsLocked() bool { _, err := os.Stat(lock.heldFile()) return err == nil } // BreakLock forcably breaks the lock that is currently being held. func (lock *Lock) BreakLock() error { return os.RemoveAll(lock.lockDir()) } // Message returns the saved message, or the empty string if there is no // saved message. func (lock *Lock) Message() string { message, err := ioutil.ReadFile(lock.messageFile()) if err != nil { return "" } return string(message) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/fslock/package_test.go���������������������������0000644�0000153�0000161�00000001137�12321735642�026271� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package fslock_test import ( "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) func Test(t *testing.T) { gc.TestingT(t) } type Dependencies struct{} var _ = gc.Suite(&Dependencies{}) func (*Dependencies) TestPackageDependencies(c *gc.C) { // This test is to ensure we don't bring in dependencies without thinking. c.Assert(testbase.FindJujuCoreImports(c, "launchpad.net/juju-core/utils/fslock"), gc.DeepEquals, []string{"juju/osenv", "thirdparty/pbkdf2", "utils"}) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/fslock/fslock_test.go����������������������������0000644�0000153�0000161�00000020161�12321735642�026155� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package fslock_test import ( "fmt" "io/ioutil" "os" "path" "runtime" "sync/atomic" "time" gc "launchpad.net/gocheck" "launchpad.net/tomb" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/fslock" ) type fslockSuite struct { testbase.LoggingSuite lockDelay time.Duration } var _ = gc.Suite(&fslockSuite{}) func (s *fslockSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) s.PatchValue(&fslock.LockWaitDelay, 1*time.Millisecond) } // This test also happens to test that locks can get created when the parent // lock directory doesn't exist. func (s *fslockSuite) TestValidNamesLockDir(c *gc.C) { for _, name := range []string{ "a", "longer", "longer-with.special-characters", } { dir := c.MkDir() _, err := fslock.NewLock(dir, name) c.Assert(err, gc.IsNil) } } func (s *fslockSuite) TestInvalidNames(c *gc.C) { for _, name := range []string{ ".start", "-start", "NoCapitals", "no+plus", "no/slash", "no\\backslash", "no$dollar", "no:colon", } { dir := c.MkDir() _, err := fslock.NewLock(dir, name) c.Assert(err, gc.ErrorMatches, "Invalid lock name .*") } } func (s *fslockSuite) TestNewLockWithExistingDir(c *gc.C) { dir := c.MkDir() err := os.MkdirAll(dir, 0755) c.Assert(err, gc.IsNil) _, err = fslock.NewLock(dir, "special") c.Assert(err, gc.IsNil) } func (s *fslockSuite) TestNewLockWithExistingFileInPlace(c *gc.C) { dir := c.MkDir() err := os.MkdirAll(dir, 0755) c.Assert(err, gc.IsNil) path := path.Join(dir, "locks") err = ioutil.WriteFile(path, []byte("foo"), 0644) c.Assert(err, gc.IsNil) _, err = fslock.NewLock(path, "special") c.Assert(err, gc.ErrorMatches, `.* not a directory`) } func (s *fslockSuite) TestIsLockHeldBasics(c *gc.C) { dir := c.MkDir() lock, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) c.Assert(lock.IsLockHeld(), gc.Equals, false) err = lock.Lock("") c.Assert(err, gc.IsNil) c.Assert(lock.IsLockHeld(), gc.Equals, true) err = lock.Unlock() c.Assert(err, gc.IsNil) c.Assert(lock.IsLockHeld(), gc.Equals, false) } func (s *fslockSuite) TestIsLockHeldTwoLocks(c *gc.C) { dir := c.MkDir() lock1, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) lock2, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) err = lock1.Lock("") c.Assert(err, gc.IsNil) c.Assert(lock2.IsLockHeld(), gc.Equals, false) } func (s *fslockSuite) TestLockBlocks(c *gc.C) { dir := c.MkDir() lock1, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) lock2, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) acquired := make(chan struct{}) err = lock1.Lock("") c.Assert(err, gc.IsNil) go func() { lock2.Lock("") acquired <- struct{}{} close(acquired) }() // Waiting for something not to happen is inherently hard... select { case <-acquired: c.Fatalf("Unexpected lock acquisition") case <-time.After(coretesting.ShortWait): // all good } err = lock1.Unlock() c.Assert(err, gc.IsNil) select { case <-acquired: // all good case <-time.After(coretesting.LongWait): c.Fatalf("Expected lock acquisition") } c.Assert(lock2.IsLockHeld(), gc.Equals, true) } func (s *fslockSuite) TestLockWithTimeoutUnlocked(c *gc.C) { dir := c.MkDir() lock, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) err = lock.LockWithTimeout(10*time.Millisecond, "") c.Assert(err, gc.IsNil) } func (s *fslockSuite) TestLockWithTimeoutLocked(c *gc.C) { dir := c.MkDir() lock1, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) lock2, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) err = lock1.Lock("") c.Assert(err, gc.IsNil) err = lock2.LockWithTimeout(10*time.Millisecond, "") c.Assert(err, gc.Equals, fslock.ErrTimeout) } func (s *fslockSuite) TestUnlock(c *gc.C) { dir := c.MkDir() lock, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) err = lock.Unlock() c.Assert(err, gc.Equals, fslock.ErrLockNotHeld) } func (s *fslockSuite) TestIsLocked(c *gc.C) { dir := c.MkDir() lock1, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) lock2, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) err = lock1.Lock("") c.Assert(err, gc.IsNil) c.Assert(lock1.IsLocked(), gc.Equals, true) c.Assert(lock2.IsLocked(), gc.Equals, true) } func (s *fslockSuite) TestBreakLock(c *gc.C) { dir := c.MkDir() lock1, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) lock2, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) err = lock1.Lock("") c.Assert(err, gc.IsNil) err = lock2.BreakLock() c.Assert(err, gc.IsNil) c.Assert(lock2.IsLocked(), gc.Equals, false) // Normally locks are broken due to client crashes, not duration. err = lock1.Unlock() c.Assert(err, gc.Equals, fslock.ErrLockNotHeld) // Breaking a non-existant isn't an error err = lock2.BreakLock() c.Assert(err, gc.IsNil) } func (s *fslockSuite) TestMessage(c *gc.C) { dir := c.MkDir() lock, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) c.Assert(lock.Message(), gc.Equals, "") err = lock.Lock("my message") c.Assert(err, gc.IsNil) c.Assert(lock.Message(), gc.Equals, "my message") // Unlocking removes the message. err = lock.Unlock() c.Assert(err, gc.IsNil) c.Assert(lock.Message(), gc.Equals, "") } func (s *fslockSuite) TestMessageAcrossLocks(c *gc.C) { dir := c.MkDir() lock1, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) lock2, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) err = lock1.Lock("very busy") c.Assert(err, gc.IsNil) c.Assert(lock2.Message(), gc.Equals, "very busy") } func (s *fslockSuite) TestInitialMessageWhenLocking(c *gc.C) { dir := c.MkDir() lock, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) err = lock.Lock("initial message") c.Assert(err, gc.IsNil) c.Assert(lock.Message(), gc.Equals, "initial message") err = lock.Unlock() c.Assert(err, gc.IsNil) err = lock.LockWithTimeout(10*time.Millisecond, "initial timeout message") c.Assert(err, gc.IsNil) c.Assert(lock.Message(), gc.Equals, "initial timeout message") } func (s *fslockSuite) TestStress(c *gc.C) { const lockAttempts = 200 const concurrentLocks = 10 var counter = new(int64) // Use atomics to update lockState to make sure the lock isn't held by // someone else. A value of 1 means locked, 0 means unlocked. var lockState = new(int32) var done = make(chan struct{}) defer close(done) dir := c.MkDir() var stress = func(name string) { defer func() { done <- struct{}{} }() lock, err := fslock.NewLock(dir, "testing") if err != nil { c.Errorf("Failed to create a new lock") return } for i := 0; i < lockAttempts; i++ { err = lock.Lock(name) c.Assert(err, gc.IsNil) state := atomic.AddInt32(lockState, 1) c.Assert(state, gc.Equals, int32(1)) // Tell the go routine scheduler to give a slice to someone else // while we have this locked. runtime.Gosched() // need to decrement prior to unlock to avoid the race of someone // else grabbing the lock before we decrement the state. atomic.AddInt32(lockState, -1) err = lock.Unlock() c.Assert(err, gc.IsNil) // increment the general counter atomic.AddInt64(counter, 1) } } for i := 0; i < concurrentLocks; i++ { go stress(fmt.Sprintf("Lock %d", i)) } for i := 0; i < concurrentLocks; i++ { <-done } c.Assert(*counter, gc.Equals, int64(lockAttempts*concurrentLocks)) } func (s *fslockSuite) TestTomb(c *gc.C) { const timeToDie = 200 * time.Millisecond die := tomb.Tomb{} dir := c.MkDir() lock, err := fslock.NewLock(dir, "testing") c.Assert(err, gc.IsNil) // Just use one lock, and try to lock it twice. err = lock.Lock("very busy") c.Assert(err, gc.IsNil) checkTomb := func() error { select { case <-die.Dying(): return tomb.ErrDying default: // no-op to fall through to return. } return nil } go func() { time.Sleep(timeToDie) die.Killf("time to die") }() err = lock.LockWithFunc("won't happen", checkTomb) c.Assert(err, gc.Equals, tomb.ErrDying) c.Assert(lock.Message(), gc.Equals, "very busy") } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/tailer/������������������������������������������0000755�0000153�0000161�00000000000�12321736000�023272� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/tailer/export_test.go����������������������������0000644�0000153�0000161�00000000214�12321735642�026211� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tailer var NewTestTailer = newTailer ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/tailer/tailer_test.go����������������������������0000644�0000153�0000161�00000030361�12321735776�026166� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tailer_test import ( "bufio" "bytes" "fmt" "io" "sync" stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils/tailer" ) func Test(t *stdtesting.T) { gc.TestingT(t) } type tailerSuite struct{} var _ = gc.Suite(tailerSuite{}) var alphabetData = []string{ "alpha alpha\n", "bravo bravo\n", "charlie charlie\n", "delta delta\n", "echo echo\n", "foxtrott foxtrott\n", "golf golf\n", "hotel hotel\n", "india india\n", "juliet juliet\n", "kilo kilo\n", "lima lima\n", "mike mike\n", "november november\n", "oscar oscar\n", "papa papa\n", "quebec quebec\n", "romeo romeo\n", "sierra sierra\n", "tango tango\n", "uniform uniform\n", "victor victor\n", "whiskey whiskey\n", "x-ray x-ray\n", "yankee yankee\n", "zulu zulu\n", } var tests = []struct { description string data []string initialLinesWritten int initialLinesRequested int bufferSize int filter tailer.TailerFilterFunc injector func(*tailer.Tailer, *readSeeker) func([]string) initialCollectedData []string appendedCollectedData []string err string }{{ description: "lines are longer than buffer size", data: []string{ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n", "0123456789012345678901234567890123456789012345678901\n", }, initialLinesWritten: 1, initialLinesRequested: 1, bufferSize: 5, initialCollectedData: []string{ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n", }, appendedCollectedData: []string{ "0123456789012345678901234567890123456789012345678901\n", }, }, { description: "lines are longer than buffer size, missing termination of last line", data: []string{ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n", "0123456789012345678901234567890123456789012345678901\n", "the quick brown fox ", }, initialLinesWritten: 1, initialLinesRequested: 1, bufferSize: 5, initialCollectedData: []string{ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n", }, appendedCollectedData: []string{ "0123456789012345678901234567890123456789012345678901\n", }, }, { description: "lines are longer than buffer size, last line is terminated later", data: []string{ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n", "0123456789012345678901234567890123456789012345678901\n", "the quick brown fox ", "jumps over the lazy dog\n", }, initialLinesWritten: 1, initialLinesRequested: 1, bufferSize: 5, initialCollectedData: []string{ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n", }, appendedCollectedData: []string{ "0123456789012345678901234567890123456789012345678901\n", "the quick brown fox jumps over the lazy dog\n", }, }, { description: "missing termination of last line", data: []string{ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n", "0123456789012345678901234567890123456789012345678901\n", "the quick brown fox ", }, initialLinesWritten: 1, initialLinesRequested: 1, initialCollectedData: []string{ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n", }, appendedCollectedData: []string{ "0123456789012345678901234567890123456789012345678901\n", }, }, { description: "last line is terminated later", data: []string{ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n", "0123456789012345678901234567890123456789012345678901\n", "the quick brown fox ", "jumps over the lazy dog\n", }, initialLinesWritten: 1, initialLinesRequested: 1, initialCollectedData: []string{ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n", }, appendedCollectedData: []string{ "0123456789012345678901234567890123456789012345678901\n", "the quick brown fox jumps over the lazy dog\n", }, }, { description: "more lines already written than initially requested", data: alphabetData, initialLinesWritten: 5, initialLinesRequested: 3, initialCollectedData: []string{ "charlie charlie\n", "delta delta\n", "echo echo\n", }, appendedCollectedData: alphabetData[5:], }, { description: "less lines already written than initially requested", data: alphabetData, initialLinesWritten: 3, initialLinesRequested: 5, initialCollectedData: []string{ "alpha alpha\n", "bravo bravo\n", "charlie charlie\n", }, appendedCollectedData: alphabetData[3:], }, { description: "lines are longer than buffer size, more lines already written than initially requested", data: alphabetData, initialLinesWritten: 5, initialLinesRequested: 3, bufferSize: 5, initialCollectedData: []string{ "charlie charlie\n", "delta delta\n", "echo echo\n", }, appendedCollectedData: alphabetData[5:], }, { description: "lines are longer than buffer size, less lines already written than initially requested", data: alphabetData, initialLinesWritten: 3, initialLinesRequested: 5, bufferSize: 5, initialCollectedData: []string{ "alpha alpha\n", "bravo bravo\n", "charlie charlie\n", }, appendedCollectedData: alphabetData[3:], }, { description: "filter lines which contain the char 'e'", data: alphabetData, initialLinesWritten: 10, initialLinesRequested: 3, filter: func(line []byte) bool { return bytes.Contains(line, []byte{'e'}) }, initialCollectedData: []string{ "echo echo\n", "hotel hotel\n", "juliet juliet\n", }, appendedCollectedData: []string{ "mike mike\n", "november november\n", "quebec quebec\n", "romeo romeo\n", "sierra sierra\n", "whiskey whiskey\n", "yankee yankee\n", }, }, { description: "stop tailing after 10 collected lines", data: alphabetData, initialLinesWritten: 5, initialLinesRequested: 3, injector: func(t *tailer.Tailer, rs *readSeeker) func([]string) { return func(lines []string) { if len(lines) == 10 { t.Stop() } } }, initialCollectedData: []string{ "charlie charlie\n", "delta delta\n", "echo echo\n", }, appendedCollectedData: alphabetData[5:], }, { description: "generate an error after 10 collected lines", data: alphabetData, initialLinesWritten: 5, initialLinesRequested: 3, injector: func(t *tailer.Tailer, rs *readSeeker) func([]string) { return func(lines []string) { if len(lines) == 10 { rs.setError(fmt.Errorf("ouch after 10 lines")) } } }, initialCollectedData: []string{ "charlie charlie\n", "delta delta\n", "echo echo\n", }, appendedCollectedData: alphabetData[5:], err: "ouch after 10 lines", }, { description: "more lines already written than initially requested, some empty, unfiltered", data: []string{ "one one\n", "two two\n", "\n", "\n", "three three\n", "four four\n", "\n", "\n", "five five\n", "six six\n", }, initialLinesWritten: 3, initialLinesRequested: 2, initialCollectedData: []string{ "two two\n", "\n", }, appendedCollectedData: []string{ "\n", "three three\n", "four four\n", "\n", "\n", "five five\n", "six six\n", }, }, { description: "more lines already written than initially requested, some empty, those filtered", data: []string{ "one one\n", "two two\n", "\n", "\n", "three three\n", "four four\n", "\n", "\n", "five five\n", "six six\n", }, initialLinesWritten: 3, initialLinesRequested: 2, filter: func(line []byte) bool { return len(bytes.TrimSpace(line)) > 0 }, initialCollectedData: []string{ "one one\n", "two two\n", }, appendedCollectedData: []string{ "three three\n", "four four\n", "five five\n", "six six\n", }, }} func (tailerSuite) TestTailer(c *gc.C) { for i, test := range tests { c.Logf("Test #%d) %s", i, test.description) bufferSize := test.bufferSize if bufferSize == 0 { // Default value. bufferSize = 4096 } reader, writer := io.Pipe() sigc := make(chan struct{}, 1) rs := startReadSeeker(c, test.data, test.initialLinesWritten, sigc) tailer := tailer.NewTestTailer(rs, writer, test.initialLinesRequested, test.filter, bufferSize, 2*time.Millisecond) linec := startReading(c, tailer, reader, writer) // Collect initial data. assertCollected(c, linec, test.initialCollectedData, nil) sigc <- struct{}{} // Collect remaining data, possibly with injection to stop // earlier or generate an error. var injection func([]string) if test.injector != nil { injection = test.injector(tailer, rs) } assertCollected(c, linec, test.appendedCollectedData, injection) if test.err == "" { c.Assert(tailer.Stop(), gc.IsNil) } else { c.Assert(tailer.Err(), gc.ErrorMatches, test.err) } } } // startReading starts a goroutine receiving the lines out of the reader // in the background and passing them to a created string channel. This // will used in the assertions. func startReading(c *gc.C, tailer *tailer.Tailer, reader *io.PipeReader, writer *io.PipeWriter) chan string { linec := make(chan string) // Start goroutine for reading. go func() { defer close(linec) reader := bufio.NewReader(reader) for { line, err := reader.ReadString('\n') switch err { case nil: linec <- line case io.EOF: return default: c.Fail() } } }() // Close writer when tailer is stopped or has an error. Tailer using // components can do it the same way. go func() { tailer.Wait() writer.Close() }() return linec } // assertCollected reads lines from the string channel linec. It compares if // those are the one passed with compare until a timeout. If the timeout is // reached earlier than all lines are collected the assertion fails. The // injection function allows to interrupt the processing with a function // generating an error or a regular stopping during the tailing. In case the // linec is closed due to stopping or an error only the values so far care // compared. Checking the reason for termination is done in the test. func assertCollected(c *gc.C, linec chan string, compare []string, injection func([]string)) { timeout := time.After(testing.LongWait) lines := []string{} for { select { case line, ok := <-linec: if ok { lines = append(lines, line) if injection != nil { injection(lines) } if len(lines) == len(compare) { // All data received. c.Assert(lines, gc.DeepEquals, compare) return } } else { // linec closed after stopping or error. c.Assert(lines, gc.DeepEquals, compare[:len(lines)]) return } case <-timeout: if injection == nil { c.Fatalf("timeout during tailer collection") } return } } } // startReadSeeker returns a ReadSeeker for the Tailer. It simulates // reading and seeking inside a file and also simulating an error. // The goroutine waits for a signal that it can start writing the // appended lines. func startReadSeeker(c *gc.C, data []string, initialLeg int, sigc chan struct{}) *readSeeker { // Write initial lines into the buffer. var rs readSeeker var i int for i = 0; i < initialLeg; i++ { rs.write(data[i]) } go func() { <-sigc for ; i < len(data); i++ { time.Sleep(5 * time.Millisecond) rs.write(data[i]) } }() return &rs } type readSeeker struct { mux sync.Mutex buffer []byte pos int err error } func (r *readSeeker) write(s string) { r.mux.Lock() defer r.mux.Unlock() r.buffer = append(r.buffer, []byte(s)...) } func (r *readSeeker) setError(err error) { r.mux.Lock() defer r.mux.Unlock() r.err = err } func (r *readSeeker) Read(p []byte) (n int, err error) { r.mux.Lock() defer r.mux.Unlock() if r.err != nil { return 0, r.err } if r.pos >= len(r.buffer) { return 0, io.EOF } n = copy(p, r.buffer[r.pos:]) r.pos += n return n, nil } func (r *readSeeker) Seek(offset int64, whence int) (ret int64, err error) { r.mux.Lock() defer r.mux.Unlock() var newPos int64 switch whence { case 0: newPos = offset case 1: newPos = int64(r.pos) + offset case 2: newPos = int64(len(r.buffer)) + offset default: return 0, fmt.Errorf("invalid whence: %d", whence) } if newPos < 0 { return 0, fmt.Errorf("negative position: %d", newPos) } if newPos >= 1<<31 { return 0, fmt.Errorf("position out of range: %d", newPos) } r.pos = int(newPos) return newPos, nil } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/tailer/tailer.go���������������������������������0000644�0000153�0000161�00000014251�12321735776�025127� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package tailer import ( "bufio" "bytes" "io" "os" "time" "launchpad.net/tomb" ) const ( bufferSize = 4096 polltime = time.Second delimiter = '\n' ) var ( delimiters = []byte{delimiter} ) // TailerFilterFunc decides if a line shall be tailed (func is nil or // returns true) of shall be omitted (func returns false). type TailerFilterFunc func(line []byte) bool // Tailer reads an input line by line an tails them into the passed Writer. // The lines have to be terminated with a newline. type Tailer struct { tomb tomb.Tomb readSeeker io.ReadSeeker reader *bufio.Reader writeCloser io.WriteCloser writer *bufio.Writer lines int filter TailerFilterFunc bufferSize int polltime time.Duration } // NewTailer starts a Tailer which reads strings from the passed // ReadSeeker line by line. If a filter function is specified the read // lines are filtered. The matching lines are written to the passed // Writer. The reading begins the specified number of matching lines // from the end. func NewTailer(readSeeker io.ReadSeeker, writer io.Writer, lines int, filter TailerFilterFunc) *Tailer { return newTailer(readSeeker, writer, lines, filter, bufferSize, polltime) } // newTailer starts a Tailer like NewTailer but allows the setting of // the read buffer size and the time between pollings for testing. func newTailer(readSeeker io.ReadSeeker, writer io.Writer, lines int, filter TailerFilterFunc, bufferSize int, polltime time.Duration) *Tailer { t := &Tailer{ readSeeker: readSeeker, reader: bufio.NewReaderSize(readSeeker, bufferSize), writer: bufio.NewWriter(writer), lines: lines, filter: filter, bufferSize: bufferSize, polltime: polltime, } go func() { defer t.tomb.Done() t.tomb.Kill(t.loop()) }() return t } // Stop tells the tailer to stop working. func (t *Tailer) Stop() error { t.tomb.Kill(nil) return t.tomb.Wait() } // Wait waits until the tailer is stopped due to command // or an error. In case of an error it returns the reason. func (t *Tailer) Wait() error { return t.tomb.Wait() } // Dead returns the channel that can be used to wait until // the tailer is stopped. func (t *Tailer) Dead() <-chan struct{} { return t.tomb.Dead() } // Err returns a possible error. func (t *Tailer) Err() error { return t.tomb.Err() } // loop writes the last lines based on the buffer size to the // writer and then polls for more data to write it to the // writer too. func (t *Tailer) loop() error { // Position the readSeeker. if err := t.seekLastLines(); err != nil { return err } // Start polling. // TODO(mue) 2013-12-06 // Handling of read-seeker/files being truncated during // tailing is currently missing! timer := time.NewTimer(0) for { select { case <-t.tomb.Dying(): return nil case <-timer.C: for { line, readErr := t.readLine() _, writeErr := t.writer.Write(line) if writeErr != nil { return writeErr } if readErr != nil { if readErr != io.EOF { return readErr } break } } if writeErr := t.writer.Flush(); writeErr != nil { return writeErr } timer.Reset(t.polltime) } } } // seekLastLines sets the read position of the ReadSeeker to the // wanted number of filtered lines before the end. func (t *Tailer) seekLastLines() error { offset, err := t.readSeeker.Seek(0, os.SEEK_END) if err != nil { return err } seekPos := int64(0) found := 0 buffer := make([]byte, t.bufferSize) SeekLoop: for offset > 0 { // buffer contains the data left over from the // previous iteration. space := cap(buffer) - len(buffer) if space < t.bufferSize { // Grow buffer. newBuffer := make([]byte, len(buffer), cap(buffer)*2) copy(newBuffer, buffer) buffer = newBuffer space = cap(buffer) - len(buffer) } if int64(space) > offset { // Use exactly the right amount of space if there's // only a small amount remaining. space = int(offset) } // Copy data remaining from last time to the end of the buffer, // so we can read into the right place. copy(buffer[space:cap(buffer)], buffer) buffer = buffer[0 : len(buffer)+space] offset -= int64(space) _, err := t.readSeeker.Seek(offset, os.SEEK_SET) if err != nil { return err } _, err = io.ReadFull(t.readSeeker, buffer[0:space]) if err != nil { return err } // Find the end of the last line in the buffer. // This will discard any unterminated line at the end // of the file. end := bytes.LastIndex(buffer, delimiters) if end == -1 { // No end of line found - discard incomplete // line and continue looking. If this happens // at the beginning of the file, we don't care // because we're going to stop anyway. buffer = buffer[:0] continue } end++ for { start := bytes.LastIndex(buffer[0:end-1], delimiters) if start == -1 && offset >= 0 { break } start++ if t.isValid(buffer[start:end]) { found++ if found >= t.lines { seekPos = offset + int64(start) break SeekLoop } } end = start } // Leave the last line in buffer, as we don't know whether // it's complete or not. buffer = buffer[0:end] } // Final positioning. t.readSeeker.Seek(seekPos, os.SEEK_SET) return nil } // readLine reads the next valid line from the reader, even if it is // larger than the reader buffer. func (t *Tailer) readLine() ([]byte, error) { for { slice, err := t.reader.ReadSlice(delimiter) if err == nil { if t.isValid(slice) { return slice, nil } continue } line := append([]byte(nil), slice...) for err == bufio.ErrBufferFull { slice, err = t.reader.ReadSlice(delimiter) line = append(line, slice...) } switch err { case nil: if t.isValid(line) { return line, nil } case io.EOF: // EOF without delimiter, step back. t.readSeeker.Seek(-int64(len(line)), os.SEEK_CUR) return nil, err default: return nil, err } } } // isValid checks if the passed line is valid by checking if the // line has content, the filter function is nil or it returns true. func (t *Tailer) isValid(line []byte) bool { if t.filter == nil { return true } return t.filter(line) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/parallel/����������������������������������������0000755�0000153�0000161�00000000000�12321735643�023622� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/parallel/try.go����������������������������������0000644�0000153�0000161�00000011412�12321735642�024765� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package parallel import ( "errors" "io" "sync" "launchpad.net/tomb" ) var ( ErrStopped = errors.New("try was stopped") ErrClosed = errors.New("try was closed") ) // Try represents an attempt made concurrently // by a number of goroutines. type Try struct { tomb tomb.Tomb closeMutex sync.Mutex close chan struct{} limiter chan struct{} start chan func() result chan result combineErrors func(err0, err1 error) error maxParallel int endResult io.Closer } // NewTry returns an object that runs functions concurrently until one // succeeds. The result of the first function that returns without an // error is available from the Result method. If maxParallel is // positive, it limits the number of concurrently running functions. // // The function combineErrors(oldErr, newErr) is called to determine // the error return (see the Result method). The first time it is called, // oldErr will be nil; subsequently oldErr will be the error previously // returned by combineErrors. If combineErrors is nil, the last // encountered error is chosen. func NewTry(maxParallel int, combineErrors func(err0, err1 error) error) *Try { if combineErrors == nil { combineErrors = chooseLastError } t := &Try{ combineErrors: combineErrors, maxParallel: maxParallel, close: make(chan struct{}, 1), result: make(chan result), start: make(chan func()), } if t.maxParallel > 0 { t.limiter = make(chan struct{}, t.maxParallel) for i := 0; i < t.maxParallel; i++ { t.limiter <- struct{}{} } } go func() { defer t.tomb.Done() val, err := t.loop() t.endResult = val t.tomb.Kill(err) }() return t } func chooseLastError(err0, err1 error) error { return err1 } type result struct { val io.Closer err error } func (t *Try) loop() (io.Closer, error) { var err error close := t.close nrunning := 0 for { select { case f := <-t.start: nrunning++ go f() case r := <-t.result: if r.err == nil { return r.val, r.err } err = t.combineErrors(err, r.err) nrunning-- if close == nil && nrunning == 0 { return nil, err } case <-t.tomb.Dying(): if err == nil { return nil, ErrStopped } return nil, err case <-close: close = nil if nrunning == 0 { return nil, err } } } } // Start requests the given function to be started, waiting until there // are less than maxParallel functions running if necessary. It returns // an error if the function has not been started (ErrClosed if the Try // has been closed, and ErrStopped if the try is finishing). // // The function should listen on the stop channel and return if it // receives a value, though this is advisory only - the Try does not // wait for all started functions to return before completing. // // If the function returns a nil error but some earlier try was // successful (that is, the returned value is being discarded), // its returned value will be closed by calling its Close method. func (t *Try) Start(try func(stop <-chan struct{}) (io.Closer, error)) error { if t.limiter != nil { // Wait for availability slot. select { case <-t.limiter: case <-t.tomb.Dying(): return ErrStopped case <-t.close: return ErrClosed } } dying := t.tomb.Dying() f := func() { val, err := try(dying) if t.limiter != nil { // Signal availability slot is now free. t.limiter <- struct{}{} } // Deliver result. select { case t.result <- result{val, err}: case <-dying: if err == nil { val.Close() } } } select { case t.start <- f: return nil case <-dying: return ErrStopped case <-t.close: return ErrClosed } } // Close closes the Try. No more functions will be started // if Start is called, and the Try will terminate when all // outstanding functions have completed (or earlier // if one succeeds) func (t *Try) Close() { t.closeMutex.Lock() defer t.closeMutex.Unlock() select { case <-t.close: default: close(t.close) } } // Dead returns a channel that is closed when the // Try completes. func (t *Try) Dead() <-chan struct{} { return t.tomb.Dead() } // Wait waits for the Try to complete and returns the same // error returned by Result. func (t *Try) Wait() error { return t.tomb.Wait() } // Result waits for the Try to complete and returns the result of the // first successful function started by Start. // // If no function succeeded, the last error returned by // combineErrors is returned. If there were no errors or // combineErrors returned nil, ErrStopped is returned. func (t *Try) Result() (io.Closer, error) { err := t.tomb.Wait() return t.endResult, err } // Kill stops the try and all its currently executing functions. func (t *Try) Kill() { t.tomb.Kill(nil) } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/parallel/parallel.go�����������������������������0000644�0000153�0000161�00000004101�12321735642�025740� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // The parallel package provides a way of running functions concurrently while // limiting the maximum number running at once. package parallel import ( "fmt" "sync" ) // Run represents a number of functions running concurrently. type Run struct { limiter chan struct{} done chan error err chan error wg sync.WaitGroup } // Errors holds any errors encountered during the parallel run. type Errors []error func (errs Errors) Error() string { switch len(errs) { case 0: return "no error" case 1: return errs[0].Error() } return fmt.Sprintf("%s (and %d more)", errs[0].Error(), len(errs)-1) } // NewRun returns a new parallel instance. It will run up to maxParallel // functions concurrently. func NewRun(maxParallel int) *Run { if maxParallel < 1 { panic("parameter maxParallel must be >= 1") } parallelRun := &Run{ limiter: make(chan struct{}, maxParallel), done: make(chan error), err: make(chan error), } go func() { var errs Errors for e := range parallelRun.done { errs = append(errs, e) } // TODO(rog) sort errors by original order of Do request? if len(errs) > 0 { parallelRun.err <- errs } else { parallelRun.err <- nil } }() return parallelRun } // Do requests that r run f concurrently. If there are already the maximum // number of functions running concurrently, it will block until one of them // has completed. Do may itself be called concurrently. func (parallelRun *Run) Do(f func() error) { parallelRun.limiter <- struct{}{} parallelRun.wg.Add(1) go func() { defer func() { parallelRun.wg.Done() <-parallelRun.limiter }() if err := f(); err != nil { parallelRun.done <- err } }() } // Wait marks the parallel instance as complete and waits for all the functions // to complete. If any errors were encountered, it returns an Errors value // describing all the errors in arbitrary order. func (parallelRun *Run) Wait() error { parallelRun.wg.Wait() close(parallelRun.done) return <-parallelRun.err } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/parallel/parallel_test.go������������������������0000644�0000153�0000161�00000004225�12321735642�027006� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package parallel_test import ( "sort" "sync" stdtesting "testing" "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/utils/parallel" ) func Test(t *stdtesting.T) { gc.TestingT(t) } type parallelSuite struct{} var _ = gc.Suite(&parallelSuite{}) func (*parallelSuite) TestParallelMaxPar(c *gc.C) { const ( totalDo = 10 maxConcurrentRunnersPar = 3 ) var mu sync.Mutex maxConcurrentRunners := 0 nbRunners := 0 nbRuns := 0 parallelRunner := parallel.NewRun(maxConcurrentRunnersPar) for i := 0; i < totalDo; i++ { parallelRunner.Do(func() error { mu.Lock() nbRuns++ nbRunners++ if nbRunners > maxConcurrentRunners { maxConcurrentRunners = nbRunners } mu.Unlock() time.Sleep(time.Second / 10) mu.Lock() nbRunners-- mu.Unlock() return nil }) } err := parallelRunner.Wait() if nbRunners != 0 { c.Errorf("%d functions still running", nbRunners) } if nbRuns != totalDo { c.Errorf("all functions not executed; want %d got %d", totalDo, nbRuns) } c.Check(err, gc.IsNil) if maxConcurrentRunners != maxConcurrentRunnersPar { c.Errorf("wrong number of do's ran at once; want %d got %d", maxConcurrentRunnersPar, maxConcurrentRunners) } } type intError int func (intError) Error() string { return "error" } func (*parallelSuite) TestParallelError(c *gc.C) { const ( totalDo = 10 errDo = 5 ) parallelRun := parallel.NewRun(6) for i := 0; i < totalDo; i++ { i := i if i >= errDo { parallelRun.Do(func() error { return intError(i) }) } else { parallelRun.Do(func() error { return nil }) } } err := parallelRun.Wait() c.Check(err, gc.NotNil) errs := err.(parallel.Errors) c.Check(len(errs), gc.Equals, totalDo-errDo) ints := make([]int, len(errs)) for i, err := range errs { ints[i] = int(err.(intError)) } sort.Ints(ints) for i, n := range ints { c.Check(n, gc.Equals, i+errDo) } } func (*parallelSuite) TestZeroWorkerPanics(c *gc.C) { defer func() { r := recover() c.Check(r, gc.Matches, "parameter maxParallel must be >= 1") }() parallel.NewRun(0) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/parallel/try_test.go�����������������������������0000644�0000153�0000161�00000020505�12321735642�026027� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package parallel_test import ( "errors" "fmt" "io" "sort" "sync" "time" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/utils/parallel" ) type result string func (r result) Close() error { return nil } type trySuite struct{} var _ = gc.Suite(&trySuite{}) func tryFunc(delay time.Duration, val io.Closer, err error) func(<-chan struct{}) (io.Closer, error) { return func(<-chan struct{}) (io.Closer, error) { time.Sleep(delay) return val, err } } func (*trySuite) TestOneSuccess(c *gc.C) { try := parallel.NewTry(0, nil) try.Start(tryFunc(0, result("hello"), nil)) val, err := try.Result() c.Assert(err, gc.IsNil) c.Assert(val, gc.Equals, result("hello")) } func (*trySuite) TestOneFailure(c *gc.C) { try := parallel.NewTry(0, nil) expectErr := errors.New("foo") err := try.Start(tryFunc(0, nil, expectErr)) c.Assert(err, gc.IsNil) select { case <-try.Dead(): c.Fatalf("try died before it should") case <-time.After(testing.ShortWait): } try.Close() select { case <-try.Dead(): case <-time.After(testing.LongWait): c.Fatalf("timed out waiting for Try to complete") } val, err := try.Result() c.Assert(val, gc.IsNil) c.Assert(err, gc.Equals, expectErr) } func (*trySuite) TestStartReturnsErrorAfterClose(c *gc.C) { try := parallel.NewTry(0, nil) expectErr := errors.New("foo") err := try.Start(tryFunc(0, nil, expectErr)) c.Assert(err, gc.IsNil) try.Close() err = try.Start(tryFunc(0, result("goodbye"), nil)) c.Assert(err, gc.Equals, parallel.ErrClosed) // Wait for the first try to deliver its result time.Sleep(testing.ShortWait) try.Kill() err = try.Wait() c.Assert(err, gc.Equals, expectErr) } func (*trySuite) TestOutOfOrderResults(c *gc.C) { try := parallel.NewTry(0, nil) try.Start(tryFunc(50*time.Millisecond, result("first"), nil)) try.Start(tryFunc(10*time.Millisecond, result("second"), nil)) r, err := try.Result() c.Assert(err, gc.IsNil) c.Assert(r, gc.Equals, result("second")) } func (*trySuite) TestMaxParallel(c *gc.C) { try := parallel.NewTry(3, nil) var ( mu sync.Mutex count int max int ) for i := 0; i < 10; i++ { try.Start(func(<-chan struct{}) (io.Closer, error) { mu.Lock() if count++; count > max { max = count } c.Check(count, gc.Not(jc.GreaterThan), 3) mu.Unlock() time.Sleep(20 * time.Millisecond) mu.Lock() count-- mu.Unlock() return result("hello"), nil }) } r, err := try.Result() c.Assert(err, gc.IsNil) c.Assert(r, gc.Equals, result("hello")) mu.Lock() defer mu.Unlock() c.Assert(max, gc.Equals, 3) } func (*trySuite) TestStartBlocksForMaxParallel(c *gc.C) { try := parallel.NewTry(3, nil) started := make(chan struct{}) begin := make(chan struct{}) go func() { for i := 0; i < 6; i++ { err := try.Start(func(<-chan struct{}) (io.Closer, error) { <-begin return nil, fmt.Errorf("an error") }) started <- struct{}{} if i < 5 { c.Check(err, gc.IsNil) } else { c.Check(err, gc.Equals, parallel.ErrClosed) } } close(started) }() // Check we can start the first three. timeout := time.After(testing.LongWait) for i := 0; i < 3; i++ { select { case <-started: case <-timeout: c.Fatalf("timed out") } } // Check we block when going above maxParallel. timeout = time.After(testing.ShortWait) select { case <-started: c.Fatalf("Start did not block") case <-timeout: } // Unblock two attempts. begin <- struct{}{} begin <- struct{}{} // Check we can start another two. timeout = time.After(testing.LongWait) for i := 0; i < 2; i++ { select { case <-started: case <-timeout: c.Fatalf("timed out") } } // Check we block again when going above maxParallel. timeout = time.After(testing.ShortWait) select { case <-started: c.Fatalf("Start did not block") case <-timeout: } // Close the Try - the last request should be discarded, // unblocking last remaining Start request. try.Close() timeout = time.After(testing.LongWait) select { case <-started: case <-timeout: c.Fatalf("Start did not unblock after Close") } // Ensure all checks are completed select { case _, ok := <-started: c.Assert(ok, gc.Equals, false) case <-timeout: c.Fatalf("Start goroutine did not finish") } } func (*trySuite) TestAllConcurrent(c *gc.C) { try := parallel.NewTry(0, nil) started := make(chan chan struct{}) for i := 0; i < 10; i++ { try.Start(func(<-chan struct{}) (io.Closer, error) { reply := make(chan struct{}) started <- reply <-reply return result("hello"), nil }) } timeout := time.After(testing.LongWait) for i := 0; i < 10; i++ { select { case reply := <-started: reply <- struct{}{} case <-timeout: c.Fatalf("timed out") } } } type gradedError int func (e gradedError) Error() string { return fmt.Sprintf("error with importance %d", e) } func gradedErrorCombine(err0, err1 error) error { if err0 == nil || err0.(gradedError) < err1.(gradedError) { return err1 } return err0 } type multiError struct { errs []int } func (e *multiError) Error() string { return fmt.Sprintf("%v", e.errs) } func (*trySuite) TestErrorCombine(c *gc.C) { // Use maxParallel=1 to guarantee that all errors are processed sequentially. try := parallel.NewTry(1, func(err0, err1 error) error { if err0 == nil { err0 = &multiError{} } err0.(*multiError).errs = append(err0.(*multiError).errs, int(err1.(gradedError))) return err0 }) errors := []gradedError{3, 2, 4, 0, 5, 5, 3} for _, err := range errors { err := err try.Start(func(<-chan struct{}) (io.Closer, error) { return nil, err }) } try.Close() val, err := try.Result() c.Assert(val, gc.IsNil) grades := err.(*multiError).errs sort.Ints(grades) c.Assert(grades, gc.DeepEquals, []int{0, 2, 3, 3, 4, 5, 5}) } func (*trySuite) TestTriesAreStopped(c *gc.C) { try := parallel.NewTry(0, nil) stopped := make(chan struct{}) try.Start(func(stop <-chan struct{}) (io.Closer, error) { <-stop stopped <- struct{}{} return nil, parallel.ErrStopped }) try.Start(tryFunc(0, result("hello"), nil)) val, err := try.Result() c.Assert(err, gc.IsNil) c.Assert(val, gc.Equals, result("hello")) select { case <-stopped: case <-time.After(testing.LongWait): c.Fatalf("timed out waiting for stop") } } func (*trySuite) TestCloseTwice(c *gc.C) { try := parallel.NewTry(0, nil) try.Close() try.Close() val, err := try.Result() c.Assert(val, gc.IsNil) c.Assert(err, gc.IsNil) } type closeResult struct { closed chan struct{} } func (r *closeResult) Close() error { close(r.closed) return nil } func (*trySuite) TestExtraResultsAreClosed(c *gc.C) { try := parallel.NewTry(0, nil) begin := make([]chan struct{}, 4) results := make([]*closeResult, len(begin)) for i := range begin { begin[i] = make(chan struct{}) results[i] = &closeResult{make(chan struct{})} i := i try.Start(func(<-chan struct{}) (io.Closer, error) { <-begin[i] return results[i], nil }) } begin[0] <- struct{}{} val, err := try.Result() c.Assert(err, gc.IsNil) c.Assert(val, gc.Equals, results[0]) timeout := time.After(testing.ShortWait) for i, r := range results[1:] { begin[i+1] <- struct{}{} select { case <-r.closed: case <-timeout: c.Fatalf("timed out waiting for close") } } select { case <-results[0].closed: c.Fatalf("result was inappropriately closed") case <-time.After(testing.ShortWait): } } func (*trySuite) TestEverything(c *gc.C) { try := parallel.NewTry(5, gradedErrorCombine) tries := []struct { startAt time.Duration wait time.Duration val result err error }{{ wait: 30 * time.Millisecond, err: gradedError(3), }, { startAt: 10 * time.Millisecond, wait: 20 * time.Millisecond, val: result("result 1"), }, { startAt: 20 * time.Millisecond, wait: 10 * time.Millisecond, val: result("result 2"), }, { startAt: 20 * time.Millisecond, wait: 5 * time.Second, val: "delayed result", }, { startAt: 5 * time.Millisecond, err: gradedError(4), }} for _, t := range tries { t := t go func() { time.Sleep(t.startAt) try.Start(tryFunc(t.wait, t.val, t.err)) }() } val, err := try.Result() if val != result("result 1") && val != result("result 2") { c.Errorf(`expected "result 1" or "result 2" got %#v`, val) } c.Assert(err, gc.IsNil) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/export_test.go�����������������������������������0000644�0000153�0000161�00000000245�12321735642�024735� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils var ( GOMAXPROCS = &gomaxprocs NumCPU = &numCPU ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/gomaxprocs_test.go�������������������������������0000644�0000153�0000161�00000002321�12321735642�025573� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils_test import ( "os" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type gomaxprocsSuite struct { testbase.LoggingSuite setmaxprocs chan int numCPUResponse int setMaxProcs int } var _ = gc.Suite(&gomaxprocsSuite{}) func (s *gomaxprocsSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) // always stub out GOMAXPROCS so we don't actually change anything s.numCPUResponse = 2 s.setMaxProcs = -1 maxProcsFunc := func(n int) int { s.setMaxProcs = n return 1 } numCPUFunc := func() int { return s.numCPUResponse } s.PatchValue(utils.GOMAXPROCS, maxProcsFunc) s.PatchValue(utils.NumCPU, numCPUFunc) s.PatchEnvironment("GOMAXPROCS", "") } func (s *gomaxprocsSuite) TestUseMultipleCPUsDoesNothingWhenGOMAXPROCSSet(c *gc.C) { os.Setenv("GOMAXPROCS", "1") utils.UseMultipleCPUs() c.Check(s.setMaxProcs, gc.Equals, 0) } func (s *gomaxprocsSuite) TestUseMultipleCPUsWhenEnabled(c *gc.C) { utils.UseMultipleCPUs() c.Check(s.setMaxProcs, gc.Equals, 2) s.numCPUResponse = 4 utils.UseMultipleCPUs() c.Check(s.setMaxProcs, gc.Equals, 4) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/file_test.go�������������������������������������0000644�0000153�0000161�00000011771�12321735642�024341� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils_test import ( "fmt" "io/ioutil" "os" "os/user" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/juju/osenv" "launchpad.net/juju-core/utils" ) type fileSuite struct { oldHome string } var _ = gc.Suite(&fileSuite{}) func (s *fileSuite) SetUpTest(c *gc.C) { s.oldHome = osenv.Home() err := osenv.SetHome("/home/test-user") c.Assert(err, gc.IsNil) } func (s *fileSuite) TearDownTest(c *gc.C) { err := osenv.SetHome(s.oldHome) c.Assert(err, gc.IsNil) } func (*fileSuite) TestNormalizePath(c *gc.C) { currentUser, err := user.Current() c.Assert(err, gc.IsNil) for _, test := range []struct { path string expected string err string }{{ path: "/var/lib/juju", expected: "/var/lib/juju", }, { path: "~/foo", expected: "/home/test-user/foo", }, { path: "~/foo//../bar", expected: "/home/test-user/bar", }, { path: "~", expected: "/home/test-user", }, { path: "~" + currentUser.Username, expected: currentUser.HomeDir, }, { path: "~" + currentUser.Username + "/foo", expected: currentUser.HomeDir + "/foo", }, { path: "~" + currentUser.Username + "/foo//../bar", expected: currentUser.HomeDir + "/bar", }, { path: "~foobar/path", err: "user: unknown user foobar", }} { actual, err := utils.NormalizePath(test.path) if test.err != "" { c.Check(err, gc.ErrorMatches, test.err) } else { c.Check(err, gc.IsNil) c.Check(actual, gc.Equals, test.expected) } } } func (*fileSuite) TestCopyFile(c *gc.C) { dir := c.MkDir() f, err := ioutil.TempFile(dir, "source") c.Assert(err, gc.IsNil) defer f.Close() _, err = f.Write([]byte("hello world")) c.Assert(err, gc.IsNil) dest := filepath.Join(dir, "dest") err = utils.CopyFile(dest, f.Name()) c.Assert(err, gc.IsNil) data, err := ioutil.ReadFile(dest) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "hello world") } var atomicWriteFileTests = []struct { summary string change func(filename string, contents []byte) error check func(c *gc.C, fileInfo os.FileInfo) expectErr string }{{ summary: "atomic file write and chmod 0644", change: func(filename string, contents []byte) error { return utils.AtomicWriteFile(filename, contents, 0765) }, check: func(c *gc.C, fi os.FileInfo) { c.Assert(fi.Mode(), gc.Equals, 0765) }, }, { summary: "atomic file write and change", change: func(filename string, contents []byte) error { chmodChange := func(f *os.File) error { return f.Chmod(0700) } return utils.AtomicWriteFileAndChange(filename, contents, chmodChange) }, check: func(c *gc.C, fi os.FileInfo) { c.Assert(fi.Mode(), gc.Equals, 0700) }, }, { summary: "atomic file write empty contents", change: func(filename string, contents []byte) error { nopChange := func(*os.File) error { return nil } return utils.AtomicWriteFileAndChange(filename, contents, nopChange) }, }, { summary: "atomic file write and failing change func", change: func(filename string, contents []byte) error { errChange := func(*os.File) error { return fmt.Errorf("pow!") } return utils.AtomicWriteFileAndChange(filename, contents, errChange) }, expectErr: "pow!", }} func (*fileSuite) TestAtomicWriteFile(c *gc.C) { dir := c.MkDir() name := "test.file" path := filepath.Join(dir, name) assertDirContents := func(names ...string) { fis, err := ioutil.ReadDir(dir) c.Assert(err, gc.IsNil) c.Assert(fis, gc.HasLen, len(names)) for i, name := range names { c.Assert(fis[i].Name(), gc.Equals, name) } } assertNotExist := func(path string) { _, err := os.Lstat(path) c.Assert(err, jc.Satisfies, os.IsNotExist) } for i, test := range atomicWriteFileTests { c.Logf("test %d: %s", i, test.summary) // First - test with file not already there. assertDirContents() assertNotExist(path) contents := []byte("some\ncontents") err := test.change(path, contents) if test.expectErr == "" { c.Assert(err, gc.IsNil) data, err := ioutil.ReadFile(path) c.Assert(err, gc.IsNil) c.Assert(data, jc.DeepEquals, contents) assertDirContents(name) } else { c.Assert(err, gc.ErrorMatches, test.expectErr) assertDirContents() continue } // Second - test with a file already there. contents = []byte("new\ncontents") err = test.change(path, contents) c.Assert(err, gc.IsNil) data, err = ioutil.ReadFile(path) c.Assert(err, gc.IsNil) c.Assert(data, jc.DeepEquals, contents) assertDirContents(name) // Remove the file to reset scenario. 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) } �������juju-core_1.18.1/src/launchpad.net/juju-core/utils/attempt.go���������������������������������������0000644�0000153�0000161�00000003627�12321735642�024042� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011, 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "time" ) // The Attempt and AttemptStrategy types are copied from those in launchpad.net/goamz/aws. // AttemptStrategy represents a strategy for waiting for an action // to complete successfully. type AttemptStrategy struct { Total time.Duration // total duration of attempt. Delay time.Duration // interval between each try in the burst. Min int // minimum number of retries; overrides Total } type Attempt struct { strategy AttemptStrategy last time.Time end time.Time force bool count int } // Start begins a new sequence of attempts for the given strategy. func (s AttemptStrategy) Start() *Attempt { now := time.Now() return &Attempt{ strategy: s, last: now, end: now.Add(s.Total), force: true, } } // Next waits until it is time to perform the next attempt or returns // false if it is time to stop trying. // It always returns true the first time it is called - we are guaranteed to // make at least one attempt. func (a *Attempt) Next() bool { now := time.Now() sleep := a.nextSleep(now) if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count { return false } a.force = false if sleep > 0 && a.count > 0 { time.Sleep(sleep) now = time.Now() } a.count++ a.last = now return true } func (a *Attempt) nextSleep(now time.Time) time.Duration { sleep := a.strategy.Delay - now.Sub(a.last) if sleep < 0 { return 0 } return sleep } // HasNext returns whether another attempt will be made if the current // one fails. If it returns true, the following call to Next is // guaranteed to return true. func (a *Attempt) HasNext() bool { if a.force || a.strategy.Min > a.count { return true } now := time.Now() if now.Add(a.nextSleep(now)).Before(a.end) { a.force = true return true } return false } ���������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/exec/��������������������������������������������0000755�0000153�0000161�00000000000�12321735642�022751� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/exec/exec.go�������������������������������������0000644�0000153�0000161�00000003452�12321735642�024230� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package exec import ( "bytes" "os/exec" "syscall" "github.com/juju/loggo" ) var logger = loggo.GetLogger("juju.util.exec") // Parameters for RunCommands. Commands contains one or more commands to be // executed using '/bin/bash -s'. If WorkingDir is set, this is passed // through to bash. Similarly if the Environment is specified, this is used // for executing the command. type RunParams struct { Commands string WorkingDir string Environment []string } // ExecResponse contains the return code and output generated by executing a // command. type ExecResponse struct { Code int Stdout []byte Stderr []byte } // RunCommands executes the Commands specified in the RunParams using // '/bin/bash -s', passing the commands through as stdin, and collecting // stdout and stderr. If a non-zero return code is returned, this is // collected as the code for the response and this does not classify as an // error. func RunCommands(run RunParams) (*ExecResponse, error) { ps := exec.Command("/bin/bash", "-s") if run.Environment != nil { ps.Env = run.Environment } if run.WorkingDir != "" { ps.Dir = run.WorkingDir } ps.Stdin = bytes.NewBufferString(run.Commands) stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} ps.Stdout = stdout ps.Stderr = stderr err := ps.Start() if err == nil { err = ps.Wait() } result := &ExecResponse{ Stdout: stdout.Bytes(), Stderr: stderr.Bytes(), } if ee, ok := err.(*exec.ExitError); ok && err != nil { status := ee.ProcessState.Sys().(syscall.WaitStatus) if status.Exited() { // A non-zero return code isn't considered an error here. result.Code = status.ExitStatus() err = nil } logger.Infof("run result: %v", ee) } return result, err } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/exec/exec_test.go��������������������������������0000644�0000153�0000161�00000003714�12321735642�025270� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package exec_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/exec" ) type execSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&execSuite{}) func (*execSuite) TestRunCommands(c *gc.C) { newDir := c.MkDir() for i, test := range []struct { message string commands string workingDir string environment []string stdout string stderr string code int }{ { message: "test stdout capture", commands: "echo testing stdout", stdout: "testing stdout\n", }, { message: "test stderr capture", commands: "echo testing stderr >&2", stderr: "testing stderr\n", }, { message: "test return code", commands: "exit 42", code: 42, }, { message: "test working dir", commands: "pwd", workingDir: newDir, stdout: newDir + "\n", }, { message: "test environment", commands: "echo $OMG_IT_WORKS", environment: []string{"OMG_IT_WORKS=like magic"}, stdout: "like magic\n", }, } { c.Logf("%v: %s", i, test.message) result, err := exec.RunCommands( exec.RunParams{ Commands: test.commands, WorkingDir: test.workingDir, Environment: test.environment, }) c.Assert(err, gc.IsNil) c.Assert(string(result.Stdout), gc.Equals, test.stdout) c.Assert(string(result.Stderr), gc.Equals, test.stderr) c.Assert(result.Code, gc.Equals, test.code) } } func (*execSuite) TestExecUnknownCommand(c *gc.C) { result, err := exec.RunCommands( exec.RunParams{ Commands: "unknown-command", }, ) c.Assert(err, gc.IsNil) c.Assert(result.Stdout, gc.HasLen, 0) c.Assert(string(result.Stderr), jc.Contains, "unknown-command: command not found") // 127 is a special bash return code meaning command not found. c.Assert(result.Code, gc.Equals, 127) } ����������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/exec/package_test.go�����������������������������0000644�0000153�0000161�00000001044�12321735642�025731� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package exec_test import ( "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) func Test(t *testing.T) { gc.TestingT(t) } type Dependencies struct{} var _ = gc.Suite(&Dependencies{}) func (*Dependencies) TestPackageDependencies(c *gc.C) { // This test is to ensure we don't bring in dependencies without thinking. c.Assert(testbase.FindJujuCoreImports(c, "launchpad.net/juju-core/utils/exec"), gc.HasLen, 0) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/timeit.go����������������������������������������0000644�0000153�0000161�00000002575�12321735642�023660� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "fmt" "os" "time" ) type timer struct { action string start time.Time depth int duration time.Duration subActions []*timer } func (t *timer) String() string { this := fmt.Sprintf("%.3fs %*s%s\n", t.duration.Seconds(), t.depth, "", t.action) for _, sub := range t.subActions { this += sub.String() } return this } var stack []*timer // Start a timer, used for tracking time spent. // Generally used with either defer, as in: // defer utils.Timeit("my func")() // Which will track how much time is spent in your function. Or // if you want to track the time spent in a function you are calling // then you would use: // toc := utils.Timeit("anotherFunc()") // anotherFunc() // toc() // This tracks nested calls by indenting the output, and will print out the // full stack of timing when we reach the top of the stack. func Timeit(action string) func() { cur := &timer{action: action, start: time.Now(), depth: len(stack)} if len(stack) != 0 { tip := stack[len(stack)-1] tip.subActions = append(tip.subActions, cur) } stack = append(stack, cur) return func() { cur.duration = time.Since(cur.start) if len(stack) == 0 || cur == stack[0] { fmt.Fprint(os.Stderr, cur) stack = nil } else { stack = stack[0 : len(stack)-1] } } } �����������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/attempt_test.go����������������������������������0000644�0000153�0000161�00000004433�12321735642�025075� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package utils_test import ( "time" gc "launchpad.net/gocheck" "launchpad.net/juju-core/utils" ) func doSomething() (int, error) { return 0, nil } func shouldRetry(error) bool { return false } func doSomethingWith(int) {} func ExampleAttempt_HasNext() { // This example shows how Attempt.HasNext can be used to help // structure an attempt loop. If the godoc example code allowed // us to make the example return an error, we would uncomment // the commented return statements. attempts := utils.AttemptStrategy{ Total: 1 * time.Second, Delay: 250 * time.Millisecond, } for attempt := attempts.Start(); attempt.Next(); { x, err := doSomething() if shouldRetry(err) && attempt.HasNext() { continue } if err != nil { // return err return } doSomethingWith(x) } // return ErrTimedOut return } func (utilsSuite) TestAttemptTiming(c *gc.C) { testAttempt := utils.AttemptStrategy{ Total: 0.25e9, Delay: 0.1e9, } want := []time.Duration{0, 0.1e9, 0.2e9, 0.2e9} got := make([]time.Duration, 0, len(want)) // avoid allocation when testing timing t0 := time.Now() for a := testAttempt.Start(); a.Next(); { got = append(got, time.Now().Sub(t0)) } got = append(got, time.Now().Sub(t0)) c.Assert(got, gc.HasLen, len(want)) const margin = 0.01e9 for i, got := range want { lo := want[i] - margin hi := want[i] + margin if got < lo || got > hi { c.Errorf("attempt %d want %g got %g", i, want[i].Seconds(), got.Seconds()) } } } func (utilsSuite) TestAttemptNextHasNext(c *gc.C) { a := utils.AttemptStrategy{}.Start() c.Assert(a.Next(), gc.Equals, true) c.Assert(a.Next(), gc.Equals, false) a = utils.AttemptStrategy{}.Start() c.Assert(a.Next(), gc.Equals, true) c.Assert(a.HasNext(), gc.Equals, false) c.Assert(a.Next(), gc.Equals, false) a = utils.AttemptStrategy{Total: 2e8}.Start() c.Assert(a.Next(), gc.Equals, true) c.Assert(a.HasNext(), gc.Equals, true) time.Sleep(2e8) c.Assert(a.HasNext(), gc.Equals, true) c.Assert(a.Next(), gc.Equals, true) c.Assert(a.Next(), gc.Equals, false) a = utils.AttemptStrategy{Total: 1e8, Min: 2}.Start() time.Sleep(1e8) c.Assert(a.Next(), gc.Equals, true) c.Assert(a.HasNext(), gc.Equals, true) c.Assert(a.Next(), gc.Equals, true) c.Assert(a.HasNext(), gc.Equals, false) c.Assert(a.Next(), gc.Equals, false) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/http.go������������������������������������������0000644�0000153�0000161�00000005514�12321735776�023350� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "crypto/tls" "net/http" "sync" ) var insecureClient = (*http.Client)(nil) var insecureClientMutex = sync.Mutex{} func init() { // See https://code.google.com/p/go/issues/detail?id=4677 // We need to force the connection to close each time so that we don't // hit the above Go bug. defaultTransport := http.DefaultTransport.(*http.Transport) defaultTransport.DisableKeepAlives = true registerFileProtocol(defaultTransport) } // registerFileProtocol registers support for file:// URLs on the given transport. func registerFileProtocol(transport *http.Transport) { transport.RegisterProtocol("file", http.NewFileTransport(http.Dir("/"))) } // SSLHostnameVerification is used as a switch for when a given provider might // use self-signed credentials and we should not try to verify the hostname on // the TLS/SSL certificates type SSLHostnameVerification bool const ( // VerifySSLHostnames ensures we verify the hostname on the certificate // matches the host we are connecting and is signed VerifySSLHostnames = SSLHostnameVerification(true) // NoVerifySSLHostnames informs us to skip verifying the hostname // matches a valid certificate NoVerifySSLHostnames = SSLHostnameVerification(false) ) // GetHTTPClient returns either a standard http client or // non validating client depending on the value of verify. func GetHTTPClient(verify SSLHostnameVerification) *http.Client { if verify == VerifySSLHostnames { return GetValidatingHTTPClient() } return GetNonValidatingHTTPClient() } // GetValidatingHTTPClient returns a new http.Client that // verifies the server's certificate chain and hostname. func GetValidatingHTTPClient() *http.Client { logger.Infof("hostname SSL verification enabled") return http.DefaultClient } // GetNonValidatingHTTPClient returns a new http.Client that // does not verify the server's certificate chain and hostname. func GetNonValidatingHTTPClient() *http.Client { logger.Infof("hostname SSL verification disabled") insecureClientMutex.Lock() defer insecureClientMutex.Unlock() if insecureClient == nil { insecureConfig := &tls.Config{InsecureSkipVerify: true} insecureTransport := NewHttpTLSTransport(insecureConfig) insecureClient = &http.Client{Transport: insecureTransport} } return insecureClient } // NewHttpTLSTransport returns a new http.Transport constructed with the TLS config // and the necessary parameters for Juju. func NewHttpTLSTransport(tlsConfig *tls.Config) *http.Transport { // See https://code.google.com/p/go/issues/detail?id=4677 // We need to force the connection to close each time so that we don't // hit the above Go bug. transport := &http.Transport{ TLSClientConfig: tlsConfig, DisableKeepAlives: true, } registerFileProtocol(transport) return transport } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/password.go��������������������������������������0000644�0000153�0000161�00000006135�12321735642�024223� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "crypto/rand" "crypto/sha512" "encoding/base64" "fmt" "io" "launchpad.net/juju-core/thirdparty/pbkdf2" ) // CompatSalt is because Juju 1.16 and older used a hard-coded salt to compute // the password hash for all users and agents var CompatSalt = string([]byte{0x75, 0x82, 0x81, 0xca}) const randomPasswordBytes = 18 // MinAgentPasswordLength describes how long agent passwords should be. We // require this length because we assume enough entropy in the Agent password // that it is safe to not do extra rounds of iterated hashing. var MinAgentPasswordLength = base64.StdEncoding.EncodedLen(randomPasswordBytes) // RandomBytes returns n random bytes. func RandomBytes(n int) ([]byte, error) { buf := make([]byte, n) _, err := io.ReadFull(rand.Reader, buf) if err != nil { return nil, fmt.Errorf("cannot read random bytes: %v", err) } return buf, nil } // RandomPassword generates a random base64-encoded password. func RandomPassword() (string, error) { b, err := RandomBytes(randomPasswordBytes) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(b), nil } // RandomSalt generates a random base64 data suitable for using as a password // salt The pbkdf2 guideline is to use 8 bytes of salt, so we do 12 raw bytes // into 16 base64 bytes. (The alternative is 6 raw into 8 base64). func RandomSalt() (string, error) { b, err := RandomBytes(12) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(b), nil } // FastInsecureHash specifies whether a fast, insecure version of the hash // algorithm will be used. Changing this will cause PasswordHash to // produce incompatible passwords. It should only be changed for // testing purposes - to make tests run faster. var FastInsecureHash = false // UserPasswordHash returns base64-encoded one-way hash password that is // computationally hard to crack by iterating through possible passwords. func UserPasswordHash(password string, salt string) string { if salt == "" { panic("salt is not allowed to be empty") } iter := 8192 if FastInsecureHash { iter = 1 } // Generate 18 byte passwords because we know that MongoDB // uses the MD5 sum of the password anyway, so there's // no point in using more bytes. (18 so we don't get base 64 // padding characters). h := pbkdf2.Key([]byte(password), []byte(salt), iter, 18, sha512.New) return base64.StdEncoding.EncodeToString(h) } // AgentPasswordHash returns base64-encoded one-way hash of password. This is // not suitable for User passwords because those will have limited entropy (see // UserPasswordHash). However, since we generate long random passwords for // agents, we can trust that there is sufficient entropy to prevent brute force // search. And using a faster hash allows us to restart the state machines and // have 1000s of agents log in in a reasonable amount of time. func AgentPasswordHash(password string) string { sum := sha512.New() sum.Write([]byte(password)) h := sum.Sum(nil) return base64.StdEncoding.EncodeToString(h[:18]) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/network.go���������������������������������������0000644�0000153�0000161�00000002242�12321735642�024045� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "fmt" "net" "github.com/juju/loggo" ) var logger = loggo.GetLogger("juju.utils") // GetIPv4Address iterates through the addresses expecting the format from // func (ifi *net.Interface) Addrs() ([]net.Addr, error) func GetIPv4Address(addresses []net.Addr) (string, error) { for _, addr := range addresses { ip, _, err := net.ParseCIDR(addr.String()) if err != nil { return "", err } ipv4 := ip.To4() if ipv4 == nil { continue } return ipv4.String(), nil } return "", fmt.Errorf("no addresses match") } // GetAddressForInterface looks for the network interface // and returns the IPv4 address from the possible addresses. func GetAddressForInterface(interfaceName string) (string, error) { iface, err := net.InterfaceByName(interfaceName) if err != nil { logger.Errorf("cannot find network interface %q: %v", interfaceName, err) return "", err } addrs, err := iface.Addrs() if err != nil { logger.Errorf("cannot get addresses for network interface %q: %v", interfaceName, err) return "", err } return GetIPv4Address(addrs) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/trivial_test.go����������������������������������0000644�0000153�0000161�00000006147�12321735642�025075� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils_test import ( "bytes" "fmt" "io/ioutil" "path/filepath" "strings" "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/utils" ) func Test(t *testing.T) { gc.TestingT(t) } type utilsSuite struct{} var _ = gc.Suite(utilsSuite{}) var ( data = []byte(strings.Repeat("some data to be compressed\n", 100)) // compressedData was produced by the gzip command. compressedData = []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x33, 0xb5, 0xf6, 0x50, 0x00, 0x03, 0xed, 0xc9, 0xb1, 0x0d, 0x00, 0x20, 0x08, 0x45, 0xc1, 0xde, 0x29, 0x58, 0x0d, 0xe5, 0x97, 0x04, 0x23, 0xee, 0x1f, 0xa7, 0xb0, 0x7b, 0xd7, 0x5e, 0x57, 0xca, 0xc2, 0xaf, 0xdb, 0x2d, 0x9b, 0xb2, 0x55, 0xb9, 0x8f, 0xba, 0x15, 0xa3, 0x29, 0x8a, 0xa2, 0x28, 0x8a, 0xa2, 0x28, 0xea, 0x67, 0x3d, 0x71, 0x71, 0x6e, 0xbf, 0x8c, 0x0a, 0x00, 0x00, } ) func (*utilsSuite) TestCompression(c *gc.C) { cdata := utils.Gzip(data) c.Assert(len(cdata) < len(data), gc.Equals, true) data1, err := utils.Gunzip(cdata) c.Assert(err, gc.IsNil) c.Assert(data1, gc.DeepEquals, data) data1, err = utils.Gunzip(compressedData) c.Assert(err, gc.IsNil) c.Assert(data1, gc.DeepEquals, data) } func (*utilsSuite) TestCommandString(c *gc.C) { type test struct { args []string expected string } tests := []test{ {nil, ""}, {[]string{"a"}, "a"}, {[]string{"a$"}, `"a\$"`}, {[]string{""}, ""}, {[]string{"\\"}, `"\\"`}, {[]string{"a", "'b'"}, "a 'b'"}, {[]string{"a b"}, `"a b"`}, {[]string{"a", `"b"`}, `a "\"b\""`}, {[]string{"a", `"b\"`}, `a "\"b\\\""`}, {[]string{"a\n"}, "\"a\n\""}, } for i, test := range tests { c.Logf("test %d: %q", i, test.args) result := utils.CommandString(test.args...) c.Assert(result, gc.Equals, test.expected) } } func (*utilsSuite) TestReadSHA256AndReadFileSHA256(c *gc.C) { sha256Tests := []struct { content string sha256 string }{{ content: "", sha256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", }, { content: "some content", sha256: "290f493c44f5d63d06b374d0a5abd292fae38b92cab2fae5efefe1b0e9347f56", }, { content: "foo", sha256: "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", }, { content: "Foo", sha256: "1cbec737f863e4922cee63cc2ebbfaafcd1cff8b790d8cfd2e6a5d550b648afa", }, { content: "multi\nline\ntext\nhere", sha256: "c384f11c0294280792a44d9d6abb81f9fd991904cb7eb851a88311b04114231e", }} tempDir := c.MkDir() for i, test := range sha256Tests { c.Logf("test %d: %q -> %q", i, test.content, test.sha256) buf := bytes.NewBufferString(test.content) hash, size, err := utils.ReadSHA256(buf) c.Check(err, gc.IsNil) c.Check(hash, gc.Equals, test.sha256) c.Check(int(size), gc.Equals, len(test.content)) tempFileName := filepath.Join(tempDir, fmt.Sprintf("sha256-%d", i)) err = ioutil.WriteFile(tempFileName, []byte(test.content), 0644) c.Check(err, gc.IsNil) fileHash, fileSize, err := utils.ReadFileSHA256(tempFileName) c.Check(err, gc.IsNil) c.Check(fileHash, gc.Equals, hash) c.Check(fileSize, gc.Equals, size) } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/apt.go�������������������������������������������0000644�0000153�0000161�00000014656�12321735776�023164� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "bytes" "fmt" "os" "os/exec" "regexp" "strings" "github.com/juju/loggo" "launchpad.net/juju-core/juju/osenv" ) var ( aptLogger = loggo.GetLogger("juju.utils.apt") aptProxyRE = regexp.MustCompile(`(?im)^\s*Acquire::(?P<protocol>[a-z]+)::Proxy\s+"(?P<proxy>[^"]+)";\s*$`) // AptConfFile is the full file path for the proxy settings that are // written by cloud-init and the machine environ worker. AptConfFile = "/etc/apt/apt.conf.d/42-juju-proxy-settings" ) // Some helpful functions for running apt in a sane way // AptCommandOutput calls cmd.Output, this is used as an overloading point so we // can test what *would* be run without actually executing another program var AptCommandOutput = (*exec.Cmd).CombinedOutput // This is the default apt-get command used in cloud-init, the various settings // mean that apt won't actually block waiting for a prompt from the user. var aptGetCommand = []string{ "apt-get", "--option=Dpkg::Options::=--force-confold", "--option=Dpkg::options::=--force-unsafe-io", "--assume-yes", "--quiet", } // aptEnvOptions are options we need to pass to apt-get to not have it prompt // the user var aptGetEnvOptions = []string{"DEBIAN_FRONTEND=noninteractive"} // cloudArchivePackages maintaines a list of packages that AptGetPreparePackages // should reference when determining the --target-release for a given series. // http://reqorts.qa.ubuntu.com/reports/ubuntu-server/cloud-archive/cloud-tools_versions.html var cloudArchivePackages = map[string]bool{ "cloud-utils": true, "curtin": true, "djorm-ext-pgarray": true, "golang": true, "iproute2": true, "isc-dhcp": true, "juju-core": true, "libseccomp": true, "libv8-3.14": true, "lxc": true, "maas": true, "mongodb": true, "python-django": true, "python-django-piston": true, "python-jujuclient": true, "python-tx-tftp": true, "python-websocket-client": true, "raphael 2.1.0-1ubuntu1": true, "simplestreams": true, "txlongpoll": true, "uvtool": true, "yui3": true, } // targetRelease returns a string base on the current series // that is suitable for use with the apt-get --target-release option func targetRelease(series string) string { switch series { case "precise": return "precise-updates/cloud-tools" default: return "" } } // AptGetPreparePackages returns a slice of installCommands. Each item // in the slice is suitable for passing directly to AptGetInstall. // // AptGetPreparePackages will inspect the series passed to it // and properly generate an installCommand entry with a --target-release // should the series be an LTS release with cloud archive packages. func AptGetPreparePackages(packages []string, series string) [][]string { var installCommands [][]string if target := targetRelease(series); target == "" { return append(installCommands, packages) } else { var pkgs []string pkgs_with_target := []string{"--target-release", target} for _, pkg := range packages { if cloudArchivePackages[pkg] { pkgs_with_target = append(pkgs_with_target, pkg) } else { pkgs = append(pkgs, pkg) } } // We check for >2 here so that we only append pkgs_with_target // if there was an actual package in the slice. if len(pkgs_with_target) > 2 { installCommands = append(installCommands, pkgs_with_target) } // Sometimes we may end up with all cloudArchivePackages // in that case we do not want to append an empty slice of pkgs if len(pkgs) > 0 { installCommands = append(installCommands, pkgs) } return installCommands } } // AptGetInstall runs 'apt-get install packages' for the packages listed here func AptGetInstall(packages ...string) error { cmdArgs := append([]string(nil), aptGetCommand...) cmdArgs = append(cmdArgs, "install") cmdArgs = append(cmdArgs, packages...) aptLogger.Infof("Running: %s", cmdArgs) cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) cmd.Env = append(os.Environ(), aptGetEnvOptions...) out, err := AptCommandOutput(cmd) if err != nil { aptLogger.Errorf("apt-get command failed: %v\nargs: %#v\n%s", err, cmdArgs, string(out)) return fmt.Errorf("apt-get failed: %v", err) } return nil } // AptConfigProxy will consult apt-config about the configured proxy // settings. If there are no proxy settings configured, an empty string is // returned. func AptConfigProxy() (string, error) { cmdArgs := []string{ "apt-config", "dump", "Acquire::http::Proxy", "Acquire::https::Proxy", "Acquire::ftp::Proxy", } cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) out, err := AptCommandOutput(cmd) if err != nil { aptLogger.Errorf("apt-config command failed: %v\nargs: %#v\n%s", err, cmdArgs, string(out)) return "", fmt.Errorf("apt-config failed: %v", err) } return string(bytes.Join(aptProxyRE.FindAll(out, -1), []byte("\n"))), nil } // DetectAptProxies will parse the results of AptConfigProxy to return a // ProxySettings instance. func DetectAptProxies() (result osenv.ProxySettings, err error) { output, err := AptConfigProxy() if err != nil { return result, err } for _, match := range aptProxyRE.FindAllStringSubmatch(output, -1) { switch match[1] { case "http": result.Http = match[2] case "https": result.Https = match[2] case "ftp": result.Ftp = match[2] } } return result, nil } // AptProxyContent produces the format expected by the apt config files // from the ProxySettings struct. func AptProxyContent(proxy osenv.ProxySettings) string { lines := []string{} addLine := func(proxy, value string) { if value != "" { lines = append(lines, fmt.Sprintf( "Acquire::%s::Proxy %q;", proxy, value)) } } addLine("http", proxy.Http) addLine("https", proxy.Https) addLine("ftp", proxy.Ftp) return strings.Join(lines, "\n") } // IsUbuntu executes lxb_release to see if the host OS is Ubuntu. func IsUbuntu() bool { out, err := RunCommand("lsb_release", "-i", "-s") if err != nil { return false } return strings.TrimSpace(out) == "Ubuntu" } // IsPackageInstalled uses dpkg-query to determine if the `packageName` // package is installed. func IsPackageInstalled(packageName string) bool { _, err := RunCommand("dpkg-query", "--status", packageName) return err == nil } ����������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/network_test.go����������������������������������0000644�0000153�0000161�00000002642�12321735642�025110� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils_test import ( "net" gc "launchpad.net/gocheck" "launchpad.net/juju-core/utils" ) type networkSuite struct { } var _ = gc.Suite(&networkSuite{}) type fakeAddress struct { address string } func (fake fakeAddress) Network() string { return "ignored" } func (fake fakeAddress) String() string { return fake.address } func makeAddresses(values ...string) (result []net.Addr) { for _, v := range values { result = append(result, &fakeAddress{v}) } return } func (*networkSuite) TestGetIPv4Address(c *gc.C) { for _, test := range []struct { addresses []net.Addr expected string errorString string }{{ addresses: makeAddresses( "complete", "nonsense"), errorString: "invalid CIDR address: complete", }, { addresses: makeAddresses( "fe80::90cf:9dff:fe6e:ece/64", ), errorString: "no addresses match", }, { addresses: makeAddresses( "fe80::90cf:9dff:fe6e:ece/64", "10.0.3.1/24", ), expected: "10.0.3.1", }, { addresses: makeAddresses( "10.0.3.1/24", "fe80::90cf:9dff:fe6e:ece/64", ), expected: "10.0.3.1", }} { ip, err := utils.GetIPv4Address(test.addresses) if test.errorString == "" { c.Assert(err, gc.IsNil) c.Assert(ip, gc.Equals, test.expected) } else { c.Assert(err, gc.ErrorMatches, test.errorString) c.Assert(ip, gc.Equals, "") } } } ����������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/file_unix.go�������������������������������������0000644�0000153�0000161�00000002132�12321735642�024334� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // +build !windows package utils import ( "os" "syscall" ) // ReplaceFile atomically replaces the destination file or directory // with the source. The errors that are returned are identical to // those returned by os.Rename. 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 } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/voyeur/������������������������������������������0000755�0000153�0000161�00000000000�12321735643�023357� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/voyeur/value_test.go�����������������������������0000644�0000153�0000161�00000010657�12321735642�026071� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package voyeur import ( "fmt" "testing" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) type suite struct { testbase.LoggingSuite } var _ = gc.Suite(&suite{}) func Test(t *testing.T) { gc.TestingT(t) } func ExampleWatcher_Next() { v := NewValue(nil) // The channel is not necessary for normal use of the watcher. // It just makes the test output predictable. ch := make(chan bool) go func() { for x := 0; x < 3; x++ { v.Set(fmt.Sprintf("value%d", x)) ch <- true } v.Close() }() w := v.Watch() for w.Next() { fmt.Println(w.Value()) <-ch } // output: // value0 // value1 // value2 } func (s *suite) TestValueGetSet(c *gc.C) { v := NewValue(nil) expected := "12345" v.Set(expected) got := v.Get() c.Assert(got, gc.Equals, expected) c.Assert(v.Closed(), jc.IsFalse) } func (s *suite) TestValueInitial(c *gc.C) { expected := "12345" v := NewValue(expected) got := v.Get() c.Assert(got, gc.Equals, expected) c.Assert(v.Closed(), jc.IsFalse) } func (s *suite) TestValueClose(c *gc.C) { expected := "12345" v := NewValue(expected) c.Assert(v.Close(), gc.IsNil) isClosed := v.Closed() c.Assert(isClosed, jc.IsTrue) got := v.Get() c.Assert(got, gc.Equals, expected) // test that we can close multiple times without a problem c.Assert(v.Close(), gc.IsNil) } func (s *suite) TestWatcher(c *gc.C) { vals := []string{"one", "two", "three"} // blocking on the channel forces the scheduler to let the other goroutine // run for a bit, so we get predictable results. This is not necessary for // normal use of the watcher. ch := make(chan bool) v := NewValue(nil) go func() { for _, s := range vals { v.Set(s) ch <- true } v.Close() }() w := v.Watch() c.Assert(w.Next(), jc.IsTrue) c.Assert(w.Value(), gc.Equals, vals[0]) // test that we can get the same value multiple times c.Assert(w.Value(), gc.Equals, vals[0]) <-ch // now try skipping a value by calling next without getting the value c.Assert(w.Next(), jc.IsTrue) <-ch c.Assert(w.Next(), jc.IsTrue) c.Assert(w.Value(), gc.Equals, vals[2]) <-ch c.Assert(w.Next(), jc.IsFalse) } func (s *suite) TestDoubleSet(c *gc.C) { vals := []string{"one", "two", "three"} // blocking on the channel forces the scheduler to let the other goroutine // run for a bit, so we get predictable results. This is not necessary for // normal use of the watcher. ch := make(chan bool) v := NewValue(nil) go func() { v.Set(vals[0]) ch <- true v.Set(vals[1]) v.Set(vals[2]) ch <- true v.Close() ch <- true }() w := v.Watch() c.Assert(w.Next(), jc.IsTrue) c.Assert(w.Value(), gc.Equals, vals[0]) <-ch // since we did two sets before sending on the channel, // we should just get vals[2] here and not get vals[1] c.Assert(w.Next(), jc.IsTrue) c.Assert(w.Value(), gc.Equals, vals[2]) } func (s *suite) TestTwoReceivers(c *gc.C) { vals := []string{"one", "two", "three"} // blocking on the channel forces the scheduler to let the other goroutine // run for a bit, so we get predictable results. This is not necessary for // normal use of the watcher. ch := make(chan bool) v := NewValue(nil) watcher := func() { w := v.Watch() x := 0 for w.Next() { c.Assert(w.Value(), gc.Equals, vals[x]) x++ <-ch } c.Assert(x, gc.Equals, len(vals)) <-ch } go watcher() go watcher() for _, val := range vals { v.Set(val) ch <- true ch <- true } v.Close() ch <- true ch <- true } func (s *suite) TestCloseWatcher(c *gc.C) { vals := []string{"one", "two", "three"} // blocking on the channel forces the scheduler to let the other goroutine // run for a bit, so we get predictable results. This is not necessary for // normal use of the watcher. ch := make(chan bool) v := NewValue(nil) w := v.Watch() go func() { x := 0 for w.Next() { c.Assert(w.Value(), gc.Equals, vals[x]) x++ <-ch } // the value will only get set once before the watcher is closed c.Assert(x, gc.Equals, 1) <-ch }() v.Set(vals[0]) ch <- true w.Close() ch <- true // prove the value is not closed, even though the watcher is c.Assert(v.Closed(), jc.IsFalse) } func (s *suite) TestWatchZeroValue(c *gc.C) { var v Value ch := make(chan bool) go func() { w := v.Watch() ch <- true ch <- w.Next() }() <-ch v.Set(struct{}{}) c.Assert(<-ch, jc.IsTrue) } ���������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/voyeur/value.go����������������������������������0000644�0000153�0000161�00000006440�12321735642�025025� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // Package voyeur implements a concurrency-safe value that can be watched for // changes. package voyeur import ( "sync" ) // Value represents a shared value that can be watched for changes. Methods on // a Value may be called concurrently. The zero Value is // ok to use, and is equivalent to a NewValue result // with a nil initial value. type Value struct { val interface{} version int mu sync.RWMutex wait sync.Cond closed bool } // NewValue creates a new Value holding the given initial value. If initial is // nil, any watchers will wait until a value is set. func NewValue(initial interface{}) *Value { v := new(Value) v.init() if initial != nil { v.val = initial v.version++ } return v } func (v *Value) needsInit() bool { return v.wait.L == nil } func (v *Value) init() { if v.needsInit() { v.wait.L = v.mu.RLocker() } } // Set sets the shared value to val. func (v *Value) Set(val interface{}) { v.mu.Lock() v.init() v.val = val v.version++ v.mu.Unlock() v.wait.Broadcast() } // Close closes the Value, unblocking any outstanding watchers. Close always // returns nil. func (v *Value) Close() error { v.mu.Lock() v.init() v.closed = true v.mu.Unlock() v.wait.Broadcast() return nil } // Closed reports whether the value has been closed. func (v *Value) Closed() bool { v.mu.RLock() defer v.mu.RUnlock() return v.closed } // Get returns the current value. func (v *Value) Get() interface{} { v.mu.RLock() defer v.mu.RUnlock() return v.val } // Watch returns a Watcher that can be used to watch for changes to the value. func (v *Value) Watch() *Watcher { return &Watcher{value: v} } // Watcher represents a single watcher of a shared value. type Watcher struct { value *Value version int current interface{} closed bool } // Next blocks until there is a new value to be retrieved from the value that is // being watched. It also unblocks when the value or the Watcher itself is // closed. Next returns false if the value or the Watcher itself have been // closed. func (w *Watcher) Next() bool { val := w.value val.mu.RLock() defer val.mu.RUnlock() if val.needsInit() { val.mu.RUnlock() val.mu.Lock() val.init() val.mu.Unlock() val.mu.RLock() } // We can go around this loop a maximum of two times, // because the only thing that can cause a Wait to // return is for the condition to be triggered, // which can only happen if the value is set (causing // the version to increment) or it is closed // causing the closed flag to be set. // Both these cases will cause Next to return. for { if w.version != val.version { w.version = val.version w.current = val.val return true } if val.closed || w.closed { return false } // Wait releases the lock until triggered and then reacquires the lock, // thus avoiding a deadlock. val.wait.Wait() } } // Close closes the Watcher without closing the underlying // value. It may be called concurrently with Next. func (w *Watcher) Close() { w.value.mu.Lock() w.value.init() w.closed = true w.value.mu.Unlock() w.value.wait.Broadcast() } // Value returns the last value that was retrieved from the watched Value by // Next. func (w *Watcher) Value() interface{} { return w.current } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/���������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022607� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_openssh.go�������������������������������0000644�0000153�0000161�00000011373�12321735642�025512� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh import ( "bytes" "fmt" "io" "os" "os/exec" "strings" "launchpad.net/juju-core/utils" ) var opensshCommonOptions = []string{"-o", "StrictHostKeyChecking no"} // default identities will not be attempted if // -i is specified and they are not explcitly // included. var defaultIdentities = []string{ "~/.ssh/identity", "~/.ssh/id_rsa", "~/.ssh/id_dsa", "~/.ssh/id_ecdsa", } type opensshCommandKind int const ( sshKind opensshCommandKind = iota scpKind ) // sshpassWrap wraps the command/args with sshpass if it is found in $PATH // and the SSHPASS environment variable is set. Otherwise, the original // command/args are returned. func sshpassWrap(cmd string, args []string) (string, []string) { if os.Getenv("SSHPASS") != "" { if path, err := exec.LookPath("sshpass"); err == nil { return path, append([]string{"-e", cmd}, args...) } } return cmd, args } // OpenSSHClient is an implementation of Client that // uses the ssh and scp executables found in $PATH. type OpenSSHClient struct{} // NewOpenSSHClient creates a new OpenSSHClient. // If the ssh and scp programs cannot be found // in $PATH, then an error is returned. func NewOpenSSHClient() (*OpenSSHClient, error) { var c OpenSSHClient if _, err := exec.LookPath("ssh"); err != nil { return nil, err } if _, err := exec.LookPath("scp"); err != nil { return nil, err } return &c, nil } func opensshOptions(options *Options, commandKind opensshCommandKind) []string { args := append([]string{}, opensshCommonOptions...) if options == nil { options = &Options{} } if len(options.proxyCommand) > 0 { args = append(args, "-o", "ProxyCommand "+utils.CommandString(options.proxyCommand...)) } if !options.passwordAuthAllowed { args = append(args, "-o", "PasswordAuthentication no") } if options.allocatePTY { args = append(args, "-t", "-t") // twice to force } identities := append([]string{}, options.identities...) if pk := PrivateKeyFiles(); len(pk) > 0 { // Add client keys as implicit identities identities = append(identities, pk...) } // If any identities are specified, the // default ones must be explicitly specified. if len(identities) > 0 { for _, identity := range defaultIdentities { path, err := utils.NormalizePath(identity) if err != nil { logger.Warningf("failed to normalize path %q: %v", identity, err) continue } if _, err := os.Stat(path); err == nil { identities = append(identities, path) } } } for _, identity := range identities { args = append(args, "-i", identity) } if options.port != 0 { port := fmt.Sprint(options.port) if commandKind == scpKind { // scp uses -P instead of -p (-p means preserve). args = append(args, "-P", port) } else { args = append(args, "-p", port) } } return args } // Command implements Client.Command. func (c *OpenSSHClient) Command(host string, command []string, options *Options) *Cmd { args := opensshOptions(options, sshKind) args = append(args, host) if len(command) > 0 { args = append(args, command...) } bin, args := sshpassWrap("ssh", args) logger.Debugf("running: %s %s", bin, utils.CommandString(args...)) return &Cmd{impl: &opensshCmd{exec.Command(bin, args...)}} } // Copy implements Client.Copy. 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 } 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...)) if err := cmd.Run(); err != nil { stderr := strings.TrimSpace(stderr.String()) if len(stderr) > 0 { err = fmt.Errorf("%v (%v)", err, stderr) } return err } return nil } type opensshCmd struct { *exec.Cmd } func (c *opensshCmd) SetStdio(stdin io.Reader, stdout, stderr io.Writer) { c.Stdin, c.Stdout, c.Stderr = stdin, stdout, stderr } func (c *opensshCmd) StdinPipe() (io.WriteCloser, io.Reader, error) { wc, err := c.Cmd.StdinPipe() if err != nil { return nil, nil, err } return wc, c.Stdin, nil } func (c *opensshCmd) StdoutPipe() (io.ReadCloser, io.Writer, error) { rc, err := c.Cmd.StdoutPipe() if err != nil { return nil, nil, err } return rc, c.Stdout, nil } func (c *opensshCmd) StderrPipe() (io.ReadCloser, io.Writer, error) { rc, err := c.Cmd.StderrPipe() if err != nil { return nil, nil, err } return rc, c.Stderr, nil } func (c *opensshCmd) Kill() error { if c.Process == nil { return fmt.Errorf("process has not been started") } return c.Process.Kill() } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/export_test.go�������������������������������0000644�0000153�0000161�00000000473�12321735642�025535� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh var ( ReadAuthorisedKeys = readAuthorisedKeys WriteAuthorisedKeys = writeAuthorisedKeys InitDefaultClient = initDefaultClient DefaultIdentities = &defaultIdentities SSHDial = &sshDial ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_gocrypto.go������������������������������0000644�0000153�0000161�00000011620�12321735776�025704� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh import ( "fmt" "io" "io/ioutil" "os/user" "strings" "code.google.com/p/go.crypto/ssh" "launchpad.net/juju-core/utils" ) const sshDefaultPort = 22 // GoCryptoClient is an implementation of Client that // uses the embedded go.crypto/ssh SSH client. // // GoCryptoClient is intentionally limited in the // functionality that it enables, as it is currently // intended to be used only for non-interactive command // execution. type GoCryptoClient struct { signers []ssh.Signer } // NewGoCryptoClient creates a new GoCryptoClient. // // If no signers are specified, NewGoCryptoClient will // use the private key generated by LoadClientKeys. func NewGoCryptoClient(signers ...ssh.Signer) (*GoCryptoClient, error) { return &GoCryptoClient{signers: signers}, nil } // Command implements Client.Command. func (c *GoCryptoClient) Command(host string, command []string, options *Options) *Cmd { shellCommand := utils.CommandString(command...) signers := c.signers if len(signers) == 0 { signers = privateKeys() } user, host := splitUserHost(host) port := sshDefaultPort if options != nil { if options.port != 0 { port = options.port } } logger.Debugf(`running (equivalent of): ssh "%s@%s" -p %d '%s'`, user, host, port, shellCommand) return &Cmd{impl: &goCryptoCommand{ signers: signers, user: user, addr: fmt.Sprintf("%s:%d", host, port), command: shellCommand, }} } // Copy implements Client.Copy. // // Copy is currently unimplemented, and will always return an error. func (c *GoCryptoClient) Copy(args []string, options *Options) error { return fmt.Errorf("scp command is not implemented (OpenSSH scp not available in PATH)") } type goCryptoCommand struct { signers []ssh.Signer user string addr string command string stdin io.Reader stdout io.Writer stderr io.Writer conn *ssh.ClientConn sess *ssh.Session } var sshDial = ssh.Dial func (c *goCryptoCommand) ensureSession() (*ssh.Session, error) { if c.sess != nil { return c.sess, nil } if len(c.signers) == 0 { return nil, fmt.Errorf("no private keys available") } if c.user == "" { currentUser, err := user.Current() if err != nil { return nil, fmt.Errorf("getting current user: %v", err) } c.user = currentUser.Username } config := &ssh.ClientConfig{ User: c.user, Auth: []ssh.ClientAuth{ ssh.ClientAuthKeyring(keyring{c.signers}), }, } conn, err := sshDial("tcp", c.addr, config) if err != nil { return nil, err } sess, err := conn.NewSession() if err != nil { conn.Close() return nil, err } c.conn = conn c.sess = sess c.sess.Stdin = c.stdin c.sess.Stdout = c.stdout c.sess.Stderr = c.stderr return sess, nil } func (c *goCryptoCommand) Start() error { sess, err := c.ensureSession() if err != nil { return err } if c.command == "" { return sess.Shell() } return sess.Start(c.command) } func (c *goCryptoCommand) Close() error { if c.sess == nil { return nil } err0 := c.sess.Close() err1 := c.conn.Close() if err0 == nil { err0 = err1 } c.sess = nil c.conn = nil return err0 } func (c *goCryptoCommand) Wait() error { if c.sess == nil { return fmt.Errorf("Command has not been started") } err := c.sess.Wait() c.Close() return err } func (c *goCryptoCommand) Kill() error { if c.sess == nil { return fmt.Errorf("Command has not been started") } return c.sess.Signal(ssh.SIGKILL) } func (c *goCryptoCommand) SetStdio(stdin io.Reader, stdout, stderr io.Writer) { c.stdin = stdin c.stdout = stdout c.stderr = stderr } func (c *goCryptoCommand) StdinPipe() (io.WriteCloser, io.Reader, error) { sess, err := c.ensureSession() if err != nil { return nil, nil, err } wc, err := sess.StdinPipe() return wc, sess.Stdin, err } func (c *goCryptoCommand) StdoutPipe() (io.ReadCloser, io.Writer, error) { sess, err := c.ensureSession() if err != nil { return nil, nil, err } wc, err := sess.StdoutPipe() return ioutil.NopCloser(wc), sess.Stdout, err } func (c *goCryptoCommand) StderrPipe() (io.ReadCloser, io.Writer, error) { sess, err := c.ensureSession() if err != nil { return nil, nil, err } wc, err := sess.StderrPipe() return ioutil.NopCloser(wc), sess.Stderr, err } // keyring implements ssh.ClientKeyring type keyring struct { signers []ssh.Signer } func (k keyring) Key(i int) (ssh.PublicKey, error) { if i < 0 || i >= len(k.signers) { // nil key marks the end of the keyring; must not return an error. return nil, nil } return k.signers[i].PublicKey(), nil } func (k keyring) Sign(i int, rand io.Reader, data []byte) ([]byte, error) { if i < 0 || i >= len(k.signers) { return nil, fmt.Errorf("no key at position %d", i) } return k.signers[i].Sign(rand, data) } func splitUserHost(s string) (user, host string) { userHost := strings.SplitN(s, "@", 2) if len(userHost) == 2 { return userHost[0], userHost[1] } return "", userHost[0] } ����������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/generate.go����������������������������������0000644�0000153�0000161�00000002552�12321735642�024747� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh import ( "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" "fmt" "strings" "code.google.com/p/go.crypto/ssh" ) // KeyBits is used to determine the number of bits to use for the RSA keys // created using the GenerateKey function. var KeyBits = 2048 // GenerateKey makes a 2048 bit RSA no-passphrase SSH capable key. The bit // size is actually controlled by the KeyBits var. The private key returned is // encoded to ASCII using the PKCS1 encoding. The public key is suitable to // be added into an authorized_keys file, and has the comment passed in as the // comment part of the key. func GenerateKey(comment string) (private, public string, err error) { key, err := rsa.GenerateKey(rand.Reader, KeyBits) if err != nil { return "", "", err } identity := pem.EncodeToMemory( &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), }) signer, err := ssh.ParsePrivateKey(identity) if err != nil { return "", "", fmt.Errorf("failed to load key: %v", err) } auth_key := string(ssh.MarshalAuthorizedKey(signer.PublicKey())) // Strip off the trailing new line so we can add a comment. auth_key = strings.TrimSpace(auth_key) public = fmt.Sprintf("%s %s\n", auth_key, comment) return string(identity), public, nil } ������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/clientkeys.go��������������������������������0000644�0000153�0000161�00000011320�12321735642�025320� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh import ( "fmt" "io/ioutil" "os" "path/filepath" "strings" "sync" "code.google.com/p/go.crypto/ssh" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/set" ) const clientKeyName = "juju_id_rsa" // PublicKeySuffix is the file extension for public key files. const PublicKeySuffix = ".pub" var ( clientKeysMutex sync.Mutex // clientKeys is a cached map of private key filenames // to ssh.Signers. The private keys are those loaded // from the client key directory, passed to LoadClientKeys. clientKeys map[string]ssh.Signer ) // LoadClientKeys loads the client SSH keys from the // specified directory, and caches them as a process-wide // global. If the directory does not exist, it is created; // if the directory did not exist, or contains no keys, it // is populated with a new key pair. // // If the directory exists, then all pairs of files where one // has the same name as the other + ".pub" will be loaded as // private/public key pairs. // // Calls to LoadClientKeys will clear the previously loaded // keys, and recompute the keys. func LoadClientKeys(dir string) error { clientKeysMutex.Lock() defer clientKeysMutex.Unlock() dir, err := utils.NormalizePath(dir) if err != nil { return err } if _, err := os.Stat(dir); err == nil { keys, err := loadClientKeys(dir) if err != nil { return err } else if len(keys) > 0 { clientKeys = keys return nil } // Directory exists but contains no keys; // fall through and create one. } if err := os.MkdirAll(dir, 0700); err != nil { return err } keyfile, key, err := generateClientKey(dir) if err != nil { os.RemoveAll(dir) return err } clientKeys = map[string]ssh.Signer{keyfile: key} return nil } // ClearClientKeys clears the client keys cached in memory. func ClearClientKeys() { clientKeysMutex.Lock() defer clientKeysMutex.Unlock() clientKeys = nil } func generateClientKey(dir string) (keyfile string, key ssh.Signer, err error) { private, public, err := GenerateKey("juju-client-key") if err != nil { return "", nil, err } clientPrivateKey, err := ssh.ParsePrivateKey([]byte(private)) if err != nil { return "", nil, err } privkeyFilename := filepath.Join(dir, clientKeyName) if err = ioutil.WriteFile(privkeyFilename, []byte(private), 0600); err != nil { return "", nil, err } if err := ioutil.WriteFile(privkeyFilename+PublicKeySuffix, []byte(public), 0600); err != nil { os.Remove(privkeyFilename) return "", nil, err } return privkeyFilename, clientPrivateKey, nil } func loadClientKeys(dir string) (map[string]ssh.Signer, error) { publicKeyFiles, err := publicKeyFiles(dir) if err != nil { return nil, err } keys := make(map[string]ssh.Signer, len(publicKeyFiles)) for _, filename := range publicKeyFiles { filename = filename[:len(filename)-len(PublicKeySuffix)] data, err := ioutil.ReadFile(filename) if err != nil { return nil, err } keys[filename], err = ssh.ParsePrivateKey(data) if err != nil { return nil, fmt.Errorf("parsing key file %q: %v", filename, err) } } return keys, nil } // privateKeys returns the private keys loaded by LoadClientKeys. func privateKeys() (signers []ssh.Signer) { clientKeysMutex.Lock() defer clientKeysMutex.Unlock() for _, key := range clientKeys { signers = append(signers, key) } return signers } // PrivateKeyFiles returns the filenames of private SSH keys loaded by // LoadClientKeys. func PrivateKeyFiles() []string { clientKeysMutex.Lock() defer clientKeysMutex.Unlock() keyfiles := make([]string, 0, len(clientKeys)) for f := range clientKeys { keyfiles = append(keyfiles, f) } return keyfiles } // PublicKeyFiles returns the filenames of public SSH keys loaded by // LoadClientKeys. func PublicKeyFiles() []string { privkeys := PrivateKeyFiles() pubkeys := make([]string, len(privkeys)) for i, priv := range privkeys { pubkeys[i] = priv + PublicKeySuffix } return pubkeys } // publicKeyFiles returns the filenames of public SSH keys // in the specified directory (all the files ending with .pub). func publicKeyFiles(clientKeysDir string) ([]string, error) { if clientKeysDir == "" { return nil, nil } var keys []string dir, err := os.Open(clientKeysDir) if err != nil { return nil, err } names, err := dir.Readdirnames(-1) dir.Close() if err != nil { return nil, err } candidates := set.NewStrings(names...) for _, name := range names { if !strings.HasSuffix(name, PublicKeySuffix) { continue } // If the private key filename also exists, add the file. priv := name[:len(name)-len(PublicKeySuffix)] if candidates.Contains(priv) { keys = append(keys, filepath.Join(dir.Name(), name)) } } return keys, nil } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/run_test.go����������������������������������0000644�0000153�0000161�00000006013�12321735642�025014� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh_test import ( "io/ioutil" "path/filepath" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/ssh" ) type ExecuteSSHCommandSuite struct { testbase.LoggingSuite testbin string fakessh string } var _ = gc.Suite(&ExecuteSSHCommandSuite{}) func (s *ExecuteSSHCommandSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.testbin = c.MkDir() s.fakessh = filepath.Join(s.testbin, "ssh") s.PatchEnvPathPrepend(s.testbin) } func (s *ExecuteSSHCommandSuite) fakeSSH(c *gc.C, cmd string) { err := ioutil.WriteFile(s.fakessh, []byte(cmd), 0755) c.Assert(err, gc.IsNil) } func (s *ExecuteSSHCommandSuite) TestCaptureOutput(c *gc.C) { s.fakeSSH(c, echoSSH) response, err := ssh.ExecuteCommandOnMachine(ssh.ExecParams{ Host: "hostname", Command: "sudo apt-get update\nsudo apt-get upgrade", Timeout: testing.ShortWait, }) c.Assert(err, gc.IsNil) c.Assert(response.Code, gc.Equals, 0) c.Assert(string(response.Stdout), gc.Equals, "sudo apt-get update\nsudo apt-get upgrade\n") c.Assert(string(response.Stderr), gc.Equals, "-o StrictHostKeyChecking no -o PasswordAuthentication no hostname /bin/bash -s\n") } func (s *ExecuteSSHCommandSuite) TestIdentityFile(c *gc.C) { s.fakeSSH(c, echoSSH) response, err := ssh.ExecuteCommandOnMachine(ssh.ExecParams{ IdentityFile: "identity-file", Host: "hostname", Timeout: testing.ShortWait, }) c.Assert(err, gc.IsNil) c.Assert(string(response.Stderr), jc.Contains, " -i identity-file ") } func (s *ExecuteSSHCommandSuite) TestTimoutCaptureOutput(c *gc.C) { s.fakeSSH(c, slowSSH) response, err := ssh.ExecuteCommandOnMachine(ssh.ExecParams{ IdentityFile: "identity-file", Host: "hostname", Command: "ignored", Timeout: testing.ShortWait, }) c.Check(err, gc.ErrorMatches, "command timed out") c.Assert(response.Code, gc.Equals, 0) c.Assert(string(response.Stdout), gc.Equals, "stdout\n") c.Assert(string(response.Stderr), gc.Equals, "stderr\n") } func (s *ExecuteSSHCommandSuite) TestCapturesReturnCode(c *gc.C) { s.fakeSSH(c, passthroughSSH) response, err := ssh.ExecuteCommandOnMachine(ssh.ExecParams{ IdentityFile: "identity-file", Host: "hostname", Command: "echo stdout; exit 42", Timeout: testing.ShortWait, }) c.Check(err, gc.IsNil) c.Assert(response.Code, gc.Equals, 42) c.Assert(string(response.Stdout), gc.Equals, "stdout\n") c.Assert(string(response.Stderr), gc.Equals, "") } // echoSSH outputs the command args to stderr, and copies stdin to stdout var echoSSH = `#!/bin/bash # Write the args to stderr echo "$*" >&2 cat /dev/stdin ` // slowSSH sleeps for a while after outputting some text to stdout and stderr var slowSSH = `#!/bin/bash echo "stderr" >&2 echo "stdout" sleep 5s ` // passthroughSSH creates an ssh that executes stdin. var passthroughSSH = `#!/bin/bash -s` ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/fingerprint_test.go��������������������������0000644�0000153�0000161�00000001637�12321735642�026546� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh_test import ( gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/ssh" sshtesting "launchpad.net/juju-core/utils/ssh/testing" ) type FingerprintSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&FingerprintSuite{}) func (s *FingerprintSuite) TestKeyFingerprint(c *gc.C) { keys := []sshtesting.SSHKey{ sshtesting.ValidKeyOne, sshtesting.ValidKeyTwo, sshtesting.ValidKeyThree, } for _, k := range keys { fingerprint, _, err := ssh.KeyFingerprint(k.Key) c.Assert(err, gc.IsNil) c.Assert(fingerprint, gc.Equals, k.Fingerprint) } } func (s *FingerprintSuite) TestKeyFingerprintError(c *gc.C) { _, _, err := ssh.KeyFingerprint("invalid key") c.Assert(err, gc.ErrorMatches, `generating key fingerprint: invalid authorized_key "invalid key"`) } �������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/generate_test.go�����������������������������0000644�0000153�0000161�00000001324�12321735642�026002� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/ssh" ) type GenerateSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&GenerateSuite{}) func (s *GenerateSuite) TestGenerate(c *gc.C) { private, public, err := ssh.GenerateKey("some-comment") c.Check(err, gc.IsNil) c.Check(private, jc.HasPrefix, "-----BEGIN RSA PRIVATE KEY-----\n") c.Check(private, jc.HasSuffix, "-----END RSA PRIVATE KEY-----\n") c.Check(public, jc.HasPrefix, "ssh-rsa ") c.Check(public, jc.HasSuffix, " some-comment\n") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/run.go���������������������������������������0000644�0000153�0000161�00000004425�12321735642�023762� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh import ( "bytes" "fmt" "os/exec" "strings" "syscall" "time" utilexec "launchpad.net/juju-core/utils/exec" ) // ExecParams are used for the parameters for ExecuteCommandOnMachine. type ExecParams struct { IdentityFile string Host string Command string Timeout time.Duration } // ExecuteCommandOnMachine will execute the command passed through on // the host specified. This is done using ssh, and passing the commands // through /bin/bash. If the command is not finished within the timeout // specified, an error is returned. Any output captured during that time // is also returned in the remote response. func ExecuteCommandOnMachine(params ExecParams) (result utilexec.ExecResponse, err error) { // execute bash accepting commands on stdin if params.Host == "" { return result, fmt.Errorf("missing host address") } logger.Debugf("execute on %s", params.Host) var options Options if params.IdentityFile != "" { options.SetIdentities(params.IdentityFile) } command := Command(params.Host, []string{"/bin/bash", "-s"}, &options) // start a go routine to do the actual execution var stdout, stderr bytes.Buffer command.Stdout = &stdout command.Stderr = &stderr command.Stdin = strings.NewReader(params.Command + "\n") if err = command.Start(); err != nil { return result, err } commandDone := make(chan error) go func() { defer close(commandDone) err := command.Wait() logger.Debugf("command.Wait finished: %v", err) commandDone <- err }() select { case err = <-commandDone: logger.Debugf("select from commandDone channel: %v", err) // command finished and returned us the results if ee, ok := err.(*exec.ExitError); ok && err != nil { status := ee.ProcessState.Sys().(syscall.WaitStatus) if status.Exited() { // A non-zero return code isn't considered an error here. result.Code = status.ExitStatus() err = nil } } case <-time.After(params.Timeout): logger.Infof("killing the command due to timeout") err = fmt.Errorf("command timed out") command.Kill() } // In either case, gather as much as we have from stdout and stderr result.Stderr = stderr.Bytes() result.Stdout = stdout.Bytes() return result, err } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh.go���������������������������������������0000644�0000153�0000161�00000015161�12321735642�023752� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. // Package ssh contains utilities for dealing with SSH connections, // key management, and so on. All SSH-based command executions in // Juju should use the Command/ScpCommand functions in this package. // package ssh import ( "bytes" "errors" "io" "os/exec" "syscall" "launchpad.net/juju-core/cmd" ) // Options is a client-implementation independent SSH options set. type Options struct { // proxyCommand specifies the command to // execute to proxy SSH traffic through. proxyCommand []string // ssh server port; zero means use the default (22) port int // no PTY forced by default allocatePTY bool // password authentication is disallowed by default passwordAuthAllowed bool // identities is a sequence of paths to private key/identity files // to use when attempting to login. A client implementaton may attempt // with additional identities, but must give preference to these identities []string } // SetProxyCommand sets a command to execute to proxy traffic through. func (o *Options) SetProxyCommand(command ...string) { o.proxyCommand = append([]string{}, command...) } // SetPort sets the SSH server port to connect to. func (o *Options) SetPort(port int) { o.port = port } // EnablePTY forces the allocation of a pseudo-TTY. // // Forcing a pseudo-TTY is required, for example, for sudo // prompts on the target host. func (o *Options) EnablePTY() { o.allocatePTY = true } // AllowPasswordAuthentication allows the SSH // client to prompt the user for a password. // // Password authentication is disallowed by default. func (o *Options) AllowPasswordAuthentication() { o.passwordAuthAllowed = true } // SetIdentities sets a sequence of paths to private key/identity files // to use when attempting login. Client implementations may attempt to // use additional identities, but must give preference to the ones // specified here. func (o *Options) SetIdentities(identityFiles ...string) { o.identities = append([]string{}, identityFiles...) } // Client is an interface for SSH clients to implement type Client interface { // Command returns a Command for executing a command // on the specified host. Each Command is executed // within its own SSH session. // // Host is specified in the format [user@]host. Command(host string, command []string, options *Options) *Cmd // Copy copies file(s) between local and remote host(s). // Paths are specified in the scp format, [[user@]host:]path. If // any extra arguments are specified in extraArgs, they are passed // verbatim. Copy(args []string, options *Options) error } // Cmd represents a command to be (or being) executed // on a remote host. type Cmd struct { Stdin io.Reader Stdout io.Writer Stderr io.Writer impl command } // CombinedOutput runs the command, and returns the // combined stdout/stderr output and result of // executing the command. func (c *Cmd) CombinedOutput() ([]byte, error) { if c.Stdout != nil { return nil, errors.New("ssh: Stdout already set") } if c.Stderr != nil { return nil, errors.New("ssh: Stderr already set") } var b bytes.Buffer c.Stdout = &b c.Stderr = &b err := c.Run() return b.Bytes(), err } // Output runs the command, and returns the stdout // output and result of executing the command. func (c *Cmd) Output() ([]byte, error) { if c.Stdout != nil { return nil, errors.New("ssh: Stdout already set") } var b bytes.Buffer c.Stdout = &b err := c.Run() return b.Bytes(), err } // Run runs the command, and returns the result as an error. func (c *Cmd) Run() error { if err := c.Start(); err != nil { return err } err := c.Wait() if exitError, ok := err.(*exec.ExitError); ok && exitError != nil { status := exitError.ProcessState.Sys().(syscall.WaitStatus) if status.Exited() { return cmd.NewRcPassthroughError(status.ExitStatus()) } } return err } // Start starts the command running, but does not wait for // it to complete. If the command could not be started, an // error is returned. func (c *Cmd) Start() error { c.impl.SetStdio(c.Stdin, c.Stdout, c.Stderr) return c.impl.Start() } // Wait waits for the started command to complete, // and returns the result as an error. func (c *Cmd) Wait() error { return c.impl.Wait() } // Kill kills the started command. func (c *Cmd) Kill() error { return c.impl.Kill() } // StdinPipe creates a pipe and connects it to // the command's stdin. The read end of the pipe // is assigned to c.Stdin. func (c *Cmd) StdinPipe() (io.WriteCloser, error) { wc, r, err := c.impl.StdinPipe() if err != nil { return nil, err } c.Stdin = r return wc, nil } // StdoutPipe creates a pipe and connects it to // the command's stdout. The write end of the pipe // is assigned to c.Stdout. func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { rc, w, err := c.impl.StdoutPipe() if err != nil { return nil, err } c.Stdout = w return rc, nil } // StderrPipe creates a pipe and connects it to // the command's stderr. The write end of the pipe // is assigned to c.Stderr. func (c *Cmd) StderrPipe() (io.ReadCloser, error) { rc, w, err := c.impl.StderrPipe() if err != nil { return nil, err } c.Stderr = w return rc, nil } // command is an implementation-specific representation of a // command prepared to execute against a specific host. type command interface { Start() error Wait() error Kill() error SetStdio(stdin io.Reader, stdout, stderr io.Writer) StdinPipe() (io.WriteCloser, io.Reader, error) StdoutPipe() (io.ReadCloser, io.Writer, error) StderrPipe() (io.ReadCloser, io.Writer, error) } // DefaultClient is the default SSH client for the process. // // If the OpenSSH client is found in $PATH, then it will be // used for DefaultClient; otherwise, DefaultClient will use // an embedded client based on go.crypto/ssh. var DefaultClient Client // chosenClient holds the type of SSH client created for // DefaultClient, so that we can log it in Command or Copy. var chosenClient string func init() { initDefaultClient() } func initDefaultClient() { if client, err := NewOpenSSHClient(); err == nil { DefaultClient = client chosenClient = "OpenSSH" } else if client, err := NewGoCryptoClient(); err == nil { DefaultClient = client chosenClient = "go.crypto (embedded)" } } // Command is a short-cut for DefaultClient.Command. func Command(host string, command []string, options *Options) *Cmd { logger.Debugf("using %s ssh client", chosenClient) return DefaultClient.Command(host, command, options) } // Copy is a short-cut for DefaultClient.Copy. func Copy(args []string, options *Options) error { logger.Debugf("using %s ssh client", chosenClient) return DefaultClient.Copy(args, options) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/fingerprint.go�������������������������������0000644�0000153�0000161�00000001443�12321735642�025502� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh import ( "bytes" "crypto/md5" "fmt" ) // KeyFingerprint returns the fingerprint and comment for the specified key // in authorized_key format. Fingerprints are generated according to RFC4716. // See ttp://www.ietf.org/rfc/rfc4716.txt, section 4. func KeyFingerprint(key string) (fingerprint, comment string, err error) { ak, err := ParseAuthorisedKey(key) if err != nil { return "", "", fmt.Errorf("generating key fingerprint: %v", err) } hash := md5.New() hash.Write(ak.Key) sum := hash.Sum(nil) var buf bytes.Buffer for i := 0; i < hash.Size(); i++ { if i > 0 { buf.WriteByte(':') } buf.WriteString(fmt.Sprintf("%02x", sum[i])) } return buf.String(), ak.Comment, nil } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_gocrypto_test.go�������������������������0000644�0000153�0000161�00000010411�12321735776�026740� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh_test import ( "encoding/binary" "errors" "net" "sync" cryptossh "code.google.com/p/go.crypto/ssh" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/ssh" ) var ( testCommand = []string{"echo", "$abc"} testCommandFlat = `echo "\$abc"` ) type sshServer struct { cfg *cryptossh.ServerConfig *cryptossh.Listener } func newServer(c *gc.C) *sshServer { private, _, err := ssh.GenerateKey("test-server") c.Assert(err, gc.IsNil) key, err := cryptossh.ParsePrivateKey([]byte(private)) c.Assert(err, gc.IsNil) server := &sshServer{ cfg: &cryptossh.ServerConfig{}, } server.cfg.AddHostKey(key) server.Listener, err = cryptossh.Listen("tcp", "127.0.0.1:0", server.cfg) c.Assert(err, gc.IsNil) return server } func (s *sshServer) run(c *gc.C) { conn, err := s.Accept() c.Assert(err, gc.IsNil) defer func() { err = conn.Close() c.Assert(err, gc.IsNil) }() err = conn.Handshake() c.Assert(err, gc.IsNil) var wg sync.WaitGroup defer wg.Wait() for { channel, err := conn.Accept() c.Assert(err, gc.IsNil) c.Assert(channel.ChannelType(), gc.Equals, "session") channel.Accept() wg.Add(1) go func() { defer wg.Done() defer channel.Close() _, err := channel.Read(nil) c.Assert(err, gc.FitsTypeOf, cryptossh.ChannelRequest{}) req := err.(cryptossh.ChannelRequest) c.Assert(req.Request, gc.Equals, "exec") c.Assert(req.WantReply, jc.IsTrue) n := binary.BigEndian.Uint32(req.Payload[:4]) command := string(req.Payload[4 : n+4]) c.Assert(command, gc.Equals, testCommandFlat) // TODO(axw) when gosshnew is ready, send reply to client. }() } } type SSHGoCryptoCommandSuite struct { testbase.LoggingSuite client ssh.Client } var _ = gc.Suite(&SSHGoCryptoCommandSuite{}) func (s *SSHGoCryptoCommandSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) client, err := ssh.NewGoCryptoClient() c.Assert(err, gc.IsNil) s.client = client } func (s *SSHGoCryptoCommandSuite) TestNewGoCryptoClient(c *gc.C) { _, err := ssh.NewGoCryptoClient() c.Assert(err, gc.IsNil) private, _, err := ssh.GenerateKey("test-client") c.Assert(err, gc.IsNil) key, err := cryptossh.ParsePrivateKey([]byte(private)) c.Assert(err, gc.IsNil) _, err = ssh.NewGoCryptoClient(key) c.Assert(err, gc.IsNil) } func (s *SSHGoCryptoCommandSuite) TestClientNoKeys(c *gc.C) { client, err := ssh.NewGoCryptoClient() c.Assert(err, gc.IsNil) cmd := client.Command("0.1.2.3", []string{"echo", "123"}, nil) _, err = cmd.Output() c.Assert(err, gc.ErrorMatches, "no private keys available") defer ssh.ClearClientKeys() err = ssh.LoadClientKeys(c.MkDir()) c.Assert(err, gc.IsNil) s.PatchValue(ssh.SSHDial, func(network, address string, cfg *cryptossh.ClientConfig) (*cryptossh.ClientConn, error) { return nil, errors.New("ssh.Dial failed") }) cmd = client.Command("0.1.2.3", []string{"echo", "123"}, nil) _, err = cmd.Output() // error message differs based on whether using cgo or not c.Assert(err, gc.ErrorMatches, "ssh.Dial failed") } func (s *SSHGoCryptoCommandSuite) TestCommand(c *gc.C) { private, _, err := ssh.GenerateKey("test-server") c.Assert(err, gc.IsNil) key, err := cryptossh.ParsePrivateKey([]byte(private)) client, err := ssh.NewGoCryptoClient(key) c.Assert(err, gc.IsNil) server := newServer(c) var opts ssh.Options opts.SetPort(server.Addr().(*net.TCPAddr).Port) cmd := client.Command("127.0.0.1", testCommand, &opts) checkedKey := false server.cfg.PublicKeyCallback = func(conn *cryptossh.ServerConn, user, algo string, pubkey []byte) bool { c.Check(pubkey, gc.DeepEquals, cryptossh.MarshalPublicKey(key.PublicKey())) checkedKey = true return true } go server.run(c) out, err := cmd.Output() c.Assert(err, gc.ErrorMatches, "ssh: could not execute command.*") // TODO(axw) when gosshnew is ready, expect reply from server. c.Assert(out, gc.IsNil) c.Assert(checkedKey, jc.IsTrue) } 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) c.Assert(err, gc.ErrorMatches, `scp command is not implemented \(OpenSSH scp not available in PATH\)`) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/ssh_test.go����������������������������������0000644�0000153�0000161�00000015257�12321735642�025017� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh_test import ( "io/ioutil" "os" "path/filepath" "strings" gc "launchpad.net/gocheck" "launchpad.net/juju-core/cmd" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/ssh" ) type SSHCommandSuite struct { testbase.LoggingSuite testbin string fakessh string fakescp string client ssh.Client } var _ = gc.Suite(&SSHCommandSuite{}) const echoCommandScript = "#!/bin/sh\necho $0 \"$@\" | tee $0.args" func (s *SSHCommandSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) s.testbin = c.MkDir() s.fakessh = filepath.Join(s.testbin, "ssh") s.fakescp = filepath.Join(s.testbin, "scp") err := ioutil.WriteFile(s.fakessh, []byte(echoCommandScript), 0755) c.Assert(err, gc.IsNil) err = ioutil.WriteFile(s.fakescp, []byte(echoCommandScript), 0755) c.Assert(err, gc.IsNil) s.PatchEnvPathPrepend(s.testbin) s.client, err = ssh.NewOpenSSHClient() c.Assert(err, gc.IsNil) s.PatchValue(ssh.DefaultIdentities, nil) } func (s *SSHCommandSuite) command(args ...string) *ssh.Cmd { return s.commandOptions(args, nil) } func (s *SSHCommandSuite) commandOptions(args []string, opts *ssh.Options) *ssh.Cmd { return s.client.Command("localhost", args, opts) } func (s *SSHCommandSuite) assertCommandArgs(c *gc.C, cmd *ssh.Cmd, expected string) { out, err := cmd.Output() c.Assert(err, gc.IsNil) c.Assert(strings.TrimSpace(string(out)), gc.Equals, expected) } func (s *SSHCommandSuite) TestDefaultClient(c *gc.C) { ssh.InitDefaultClient() c.Assert(ssh.DefaultClient, gc.FitsTypeOf, &ssh.OpenSSHClient{}) s.PatchEnvironment("PATH", "") ssh.InitDefaultClient() c.Assert(ssh.DefaultClient, gc.FitsTypeOf, &ssh.GoCryptoClient{}) } func (s *SSHCommandSuite) TestCommandSSHPass(c *gc.C) { // First create a fake sshpass, but don't set $SSHPASS fakesshpass := filepath.Join(s.testbin, "sshpass") err := ioutil.WriteFile(fakesshpass, []byte(echoCommandScript), 0755) s.assertCommandArgs(c, s.command("echo", "123"), s.fakessh+" -o StrictHostKeyChecking no -o PasswordAuthentication no localhost echo 123", ) // Now set $SSHPASS. s.PatchEnvironment("SSHPASS", "anyoldthing") s.assertCommandArgs(c, s.command("echo", "123"), fakesshpass+" -e ssh -o StrictHostKeyChecking no -o PasswordAuthentication no localhost echo 123", ) // Finally, remove sshpass from $PATH. err = os.Remove(fakesshpass) c.Assert(err, gc.IsNil) s.assertCommandArgs(c, s.command("echo", "123"), s.fakessh+" -o StrictHostKeyChecking no -o PasswordAuthentication no localhost echo 123", ) } func (s *SSHCommandSuite) TestCommand(c *gc.C) { s.assertCommandArgs(c, s.command("echo", "123"), s.fakessh+" -o StrictHostKeyChecking no -o PasswordAuthentication no localhost echo 123", ) } func (s *SSHCommandSuite) TestCommandEnablePTY(c *gc.C) { var opts ssh.Options opts.EnablePTY() s.assertCommandArgs(c, s.commandOptions([]string{"echo", "123"}, &opts), s.fakessh+" -o StrictHostKeyChecking no -o PasswordAuthentication no -t -t localhost echo 123", ) } func (s *SSHCommandSuite) TestCommandAllowPasswordAuthentication(c *gc.C) { var opts ssh.Options opts.AllowPasswordAuthentication() s.assertCommandArgs(c, s.commandOptions([]string{"echo", "123"}, &opts), s.fakessh+" -o StrictHostKeyChecking no localhost echo 123", ) } func (s *SSHCommandSuite) TestCommandIdentities(c *gc.C) { var opts ssh.Options opts.SetIdentities("x", "y") s.assertCommandArgs(c, s.commandOptions([]string{"echo", "123"}, &opts), s.fakessh+" -o StrictHostKeyChecking no -o PasswordAuthentication no -i x -i y localhost echo 123", ) } func (s *SSHCommandSuite) TestCommandPort(c *gc.C) { var opts ssh.Options opts.SetPort(2022) s.assertCommandArgs(c, s.commandOptions([]string{"echo", "123"}, &opts), s.fakessh+" -o StrictHostKeyChecking no -o PasswordAuthentication no -p 2022 localhost echo 123", ) } func (s *SSHCommandSuite) TestCopy(c *gc.C) { var opts ssh.Options opts.EnablePTY() opts.AllowPasswordAuthentication() opts.SetIdentities("x", "y") opts.SetPort(2022) 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) // EnablePTY has no effect for Copy 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", "-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 /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) { clientKeysDir := c.MkDir() defer ssh.ClearClientKeys() err := ssh.LoadClientKeys(clientKeysDir) c.Assert(err, gc.IsNil) ck := filepath.Join(clientKeysDir, "juju_id_rsa") var opts ssh.Options opts.SetIdentities("x", "y") s.assertCommandArgs(c, s.commandOptions([]string{"echo", "123"}, &opts), s.fakessh+" -o StrictHostKeyChecking no -o PasswordAuthentication no -i x -i y -i "+ck+" localhost echo 123", ) } func (s *SSHCommandSuite) TestCommandError(c *gc.C) { var opts ssh.Options err := ioutil.WriteFile(s.fakessh, []byte("#!/bin/sh\nexit 42"), 0755) c.Assert(err, gc.IsNil) command := s.client.Command("ignored", []string{"echo", "foo"}, &opts) err = command.Run() c.Assert(cmd.IsRcPassthroughError(err), gc.Equals, true) } func (s *SSHCommandSuite) TestCommandDefaultIdentities(c *gc.C) { var opts ssh.Options tempdir := c.MkDir() def1 := filepath.Join(tempdir, "def1") def2 := filepath.Join(tempdir, "def2") s.PatchValue(ssh.DefaultIdentities, []string{def1, def2}) // If no identities are specified, then the defaults aren't added. s.assertCommandArgs(c, s.commandOptions([]string{"echo", "123"}, &opts), s.fakessh+" -o StrictHostKeyChecking no -o PasswordAuthentication no localhost echo 123", ) // If identities are specified, then the defaults are must added. // Only the defaults that exist on disk will be added. err := ioutil.WriteFile(def2, nil, 0644) c.Assert(err, gc.IsNil) opts.SetIdentities("x", "y") s.assertCommandArgs(c, s.commandOptions([]string{"echo", "123"}, &opts), s.fakessh+" -o StrictHostKeyChecking no -o PasswordAuthentication no -i x -i y -i "+def2+" localhost echo 123", ) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/authorisedkeys.go����������������������������0000644�0000153�0000161�00000021205�12321735642�026214� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh import ( "fmt" "io/ioutil" "os" "os/user" "path/filepath" "runtime" "strconv" "strings" "sync" "code.google.com/p/go.crypto/ssh" "github.com/juju/loggo" "launchpad.net/juju-core/utils" ) var logger = loggo.GetLogger("juju.utils.ssh") type ListMode bool var ( FullKeys ListMode = true Fingerprints ListMode = false ) const ( authKeysDir = "~%s/.ssh" authKeysFile = "authorized_keys" ) type AuthorisedKey struct { Key []byte Comment string } // ParseAuthorisedKey parses a non-comment line from an // authorized_keys file and returns the constituent parts. // Based on description in "man sshd". func ParseAuthorisedKey(line string) (*AuthorisedKey, error) { key, comment, _, _, ok := ssh.ParseAuthorizedKey([]byte(line)) if !ok { return nil, fmt.Errorf("invalid authorized_key %q", line) } keyBytes := ssh.MarshalPublicKey(key) return &AuthorisedKey{ Key: keyBytes, Comment: comment, }, nil } // SplitAuthorisedKeys extracts a key slice from the specified key data, // by splitting the key data into lines and ignoring comments and blank lines. func SplitAuthorisedKeys(keyData string) []string { var keys []string for _, key := range strings.Split(string(keyData), "\n") { key = strings.Trim(key, " \r") if len(key) == 0 { continue } if key[0] == '#' { continue } keys = append(keys, key) } return keys } func readAuthorisedKeys(username string) ([]string, error) { keyDir := fmt.Sprintf(authKeysDir, username) sshKeyFile, err := utils.NormalizePath(filepath.Join(keyDir, authKeysFile)) if err != nil { return nil, err } logger.Debugf("reading authorised keys file %s", sshKeyFile) keyData, err := ioutil.ReadFile(sshKeyFile) if os.IsNotExist(err) { return []string{}, nil } if err != nil { return nil, fmt.Errorf("reading ssh authorised keys file: %v", err) } var keys []string for _, key := range strings.Split(string(keyData), "\n") { if len(strings.Trim(key, " \r")) == 0 { continue } keys = append(keys, key) } return keys, nil } func writeAuthorisedKeys(username string, keys []string) error { keyDir := fmt.Sprintf(authKeysDir, username) keyDir, err := utils.NormalizePath(keyDir) if err != nil { return err } err = os.MkdirAll(keyDir, os.FileMode(0755)) if err != nil { return fmt.Errorf("cannot create ssh key directory: %v", err) } keyData := strings.Join(keys, "\n") + "\n" // Get perms to use on auth keys file sshKeyFile := filepath.Join(keyDir, authKeysFile) perms := os.FileMode(0644) info, err := os.Stat(sshKeyFile) if err == nil { perms = info.Mode().Perm() } logger.Debugf("writing authorised keys file %s", sshKeyFile) err = utils.AtomicWriteFile(sshKeyFile, []byte(keyData), perms) if err != nil { return err } // TODO (wallyworld) - what to do on windows (if anything) // TODO(dimitern) - no need to use user.Current() if username // is "" - it will use the current user anyway. if runtime.GOOS != "windows" { // Ensure the resulting authorised keys file has its ownership // set to the specified username. var u *user.User if username == "" { u, err = user.Current() } else { u, err = user.Lookup(username) } if err != nil { return err } // chown requires ints but user.User has strings for windows. uid, err := strconv.Atoi(u.Uid) if err != nil { return err } gid, err := strconv.Atoi(u.Gid) if err != nil { return err } err = os.Chown(sshKeyFile, uid, gid) if err != nil { return err } } return nil } // We need a mutex because updates to the authorised keys file are done by // reading the contents, updating, and writing back out. So only one caller // at a time can use either Add, Delete, List. var mutex sync.Mutex // AddKeys adds the specified ssh keys to the authorized_keys file for user. // Returns an error if there is an issue with *any* of the supplied keys. func AddKeys(user string, newKeys ...string) error { mutex.Lock() defer mutex.Unlock() existingKeys, err := readAuthorisedKeys(user) if err != nil { return err } for _, newKey := range newKeys { fingerprint, comment, err := KeyFingerprint(newKey) if err != nil { return err } if comment == "" { return fmt.Errorf("cannot add ssh key without comment") } for _, key := range existingKeys { existingFingerprint, existingComment, err := KeyFingerprint(key) if err != nil { // Only log a warning if the unrecognised key line is not a comment. if key[0] != '#' { logger.Warningf("invalid existing ssh key %q: %v", key, err) } continue } if existingFingerprint == fingerprint { return fmt.Errorf("cannot add duplicate ssh key: %v", fingerprint) } if existingComment == comment { return fmt.Errorf("cannot add ssh key with duplicate comment: %v", comment) } } } sshKeys := append(existingKeys, newKeys...) return writeAuthorisedKeys(user, sshKeys) } // DeleteKeys removes the specified ssh keys from the authorized ssh keys file for user. // keyIds may be either key comments or fingerprints. // Returns an error if there is an issue with *any* of the keys to delete. func DeleteKeys(user string, keyIds ...string) error { mutex.Lock() defer mutex.Unlock() existingKeyData, err := readAuthorisedKeys(user) if err != nil { return err } // Build up a map of keys indexed by fingerprint, and fingerprints indexed by comment // so we can easily get the key represented by each keyId, which may be either a fingerprint // or comment. var keysToWrite []string var sshKeys = make(map[string]string) var keyComments = make(map[string]string) for _, key := range existingKeyData { fingerprint, comment, err := KeyFingerprint(key) if err != nil { logger.Debugf("keeping unrecognised existing ssh key %q: %v", key, err) keysToWrite = append(keysToWrite, key) continue } sshKeys[fingerprint] = key if comment != "" { keyComments[comment] = fingerprint } } for _, keyId := range keyIds { // assume keyId may be a fingerprint fingerprint := keyId _, ok := sshKeys[keyId] if !ok { // keyId is a comment fingerprint, ok = keyComments[keyId] } if !ok { return fmt.Errorf("cannot delete non existent key: %v", keyId) } delete(sshKeys, fingerprint) } for _, key := range sshKeys { keysToWrite = append(keysToWrite, key) } if len(keysToWrite) == 0 { return fmt.Errorf("cannot delete all keys") } return writeAuthorisedKeys(user, keysToWrite) } // ReplaceKeys writes the specified ssh keys to the authorized_keys file for user, // replacing any that are already there. // Returns an error if there is an issue with *any* of the supplied keys. func ReplaceKeys(user string, newKeys ...string) error { mutex.Lock() defer mutex.Unlock() existingKeyData, err := readAuthorisedKeys(user) if err != nil { return err } var existingNonKeyLines []string for _, line := range existingKeyData { _, _, err := KeyFingerprint(line) if err != nil { existingNonKeyLines = append(existingNonKeyLines, line) } } return writeAuthorisedKeys(user, append(existingNonKeyLines, newKeys...)) } // ListKeys returns either the full keys or key comments from the authorized ssh keys file for user. func ListKeys(user string, mode ListMode) ([]string, error) { mutex.Lock() defer mutex.Unlock() keyData, err := readAuthorisedKeys(user) if err != nil { return nil, err } var keys []string for _, key := range keyData { fingerprint, comment, err := KeyFingerprint(key) if err != nil { // Only log a warning if the unrecognised key line is not a comment. if key[0] != '#' { logger.Warningf("ignoring invalid ssh key %q: %v", key, err) } continue } if mode == FullKeys { keys = append(keys, key) } else { shortKey := fingerprint if comment != "" { shortKey += fmt.Sprintf(" (%s)", comment) } keys = append(keys, shortKey) } } return keys, nil } // Any ssh key added to the authorised keys list by Juju will have this prefix. // This allows Juju to know which keys have been added externally and any such keys // will always be retained by Juju when updating the authorised keys file. const JujuCommentPrefix = "Juju:" func EnsureJujuComment(key string) string { ak, err := ParseAuthorisedKey(key) // Just return an invalid key as is. if err != nil { logger.Warningf("invalid Juju ssh key %s: %v", key, err) return key } if ak.Comment == "" { return key + " " + JujuCommentPrefix + "sshkey" } else { // Add the Juju prefix to the comment if necessary. if !strings.HasPrefix(ak.Comment, JujuCommentPrefix) { commentIndex := strings.LastIndex(key, ak.Comment) return key[:commentIndex] + JujuCommentPrefix + ak.Comment } } return key } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/testing/�������������������������������������0000755�0000153�0000161�00000000000�12321735642�024277� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/testing/keys.go������������������������������0000644�0000153�0000161�00000003276�12321735642�025611� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package testing type SSHKey struct { Key string Fingerprint string } var ( ValidKeyOne = SSHKey{ `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEX/dPu4PmtvgK3La9zioCEDrJ` + `yUr6xEIK7Pr+rLgydcqWTU/kt7w7gKjOw4vvzgHfjKl09CWyvgb+y5dCiTk` + `9MxI+erGNhs3pwaoS+EavAbawB7iEqYyTep3YaJK+4RJ4OX7ZlXMAIMrTL+` + `UVrK89t56hCkFYaAgo3VY+z6rb/b3bDBYtE1Y2tS7C3au73aDgeb9psIrSV` + `86ucKBTl5X62FnYiyGd++xCnLB6uLximM5OKXfLzJQNS/QyZyk12g3D8y69` + `Xw1GzCSKX1u1+MQboyf0HJcG2ryUCLHdcDVppApyHx2OLq53hlkQ/yxdflD` + `qCqAE4j+doagSsIfC1T2T`, "86:ed:1b:cd:26:a0:a3:4c:27:35:49:60:95:b7:0f:68", } ValidKeyTwo = SSHKey{ `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNC6zK8UMazlVgp8en8N7m7H/Y6` + `DoMWbmPFjXYRXu6iQJJ18hCtsfMe63E5/PBaOjDT8am0Sx3Eqn4ZzpWMj+z` + `knTcSd8xnMHYYxH2HStRWC1akTe4tTno2u2mqzjKd8f62URPtIocYCNRBls` + `9yjnq9SogI5EXgcx6taQcrIFcIK0SlthxxcMVSlLpnbReujW65JHtiMqoYA` + `OIALyO+Rkmtvb/ObmViDnwCKCN1up/xWt6J10MrAUtpI5b4prqG7FOqVMM/` + `zdgrVg6rUghnzdYeQ8QMyEv4mVSLzX0XIPcxorkl9q06s5mZmAzysEbKZCO` + `aXcLeNlXx/nkmuWslYCJ`, "2f:fb:b0:65:68:c8:4e:a6:1b:a6:4b:8d:14:0b:40:79", } ValidKeyThree = SSHKey{ `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpGj1JMjGjAFt5wjARbIORyjQ/c` + `ZAiDyDHe/w8qmLKUG2KTs6586QqqM6DKPZiYesrzXqvZsWYV4B6OjLM1sxq` + `WjeDIl56PSnJ0+KP8pUV9KTkkKtRXxAoNg/II4l69e05qGffj9AcQ/7JPxx` + `eL14Ulvh/a69r3uVkw1UGVk9Bwm4eCOSCqKalYLA1k5da6crEAXn9hiXLGs` + `S9dOn3Lsqj5tK31aaUncue+a3iKb7R5LRFflDizzNS+h8tPuANQflOjOhR0` + `Vas0BsurgISseZZ0NIMISyWhZpr0eOBWA/YruN9r++kYPOnDy0eMaOVGLO7` + `SQwJ/6QHvf73yksJTncz`, "1d:cf:ab:66:8a:f6:77:fb:4c:b2:59:6f:12:cf:cb:2f", } ) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/clientkeys_test.go���������������������������0000644�0000153�0000161�00000007130�12321735642�026363� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh_test import ( "io/ioutil" "os" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" "launchpad.net/juju-core/utils/ssh" ) type ClientKeysSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&ClientKeysSuite{}) func (s *ClientKeysSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) fakeHome := testing.MakeEmptyFakeHome(c) s.AddCleanup(func(*gc.C) { fakeHome.Restore() }) s.AddCleanup(func(*gc.C) { ssh.ClearClientKeys() }) } func checkFiles(c *gc.C, obtained, expected []string) { var err error for i, e := range expected { expected[i], err = utils.NormalizePath(e) c.Assert(err, gc.IsNil) } c.Assert(obtained, jc.SameContents, expected) } func checkPublicKeyFiles(c *gc.C, expected ...string) { keys := ssh.PublicKeyFiles() checkFiles(c, keys, expected) } func checkPrivateKeyFiles(c *gc.C, expected ...string) { keys := ssh.PrivateKeyFiles() checkFiles(c, keys, expected) } func (s *ClientKeysSuite) TestPublicKeyFiles(c *gc.C) { // LoadClientKeys will create the specified directory // and populate it with a key pair. err := ssh.LoadClientKeys("~/.juju/ssh") c.Assert(err, gc.IsNil) checkPublicKeyFiles(c, "~/.juju/ssh/juju_id_rsa.pub") // All files ending with .pub in the client key dir get picked up. priv, pub, err := ssh.GenerateKey("whatever") c.Assert(err, gc.IsNil) err = ioutil.WriteFile(testing.HomePath(".juju", "ssh", "whatever.pub"), []byte(pub), 0600) c.Assert(err, gc.IsNil) err = ssh.LoadClientKeys("~/.juju/ssh") c.Assert(err, gc.IsNil) // The new public key won't be observed until the // corresponding private key exists. checkPublicKeyFiles(c, "~/.juju/ssh/juju_id_rsa.pub") err = ioutil.WriteFile(testing.HomePath(".juju", "ssh", "whatever"), []byte(priv), 0600) c.Assert(err, gc.IsNil) err = ssh.LoadClientKeys("~/.juju/ssh") c.Assert(err, gc.IsNil) checkPublicKeyFiles(c, "~/.juju/ssh/juju_id_rsa.pub", "~/.juju/ssh/whatever.pub") } func (s *ClientKeysSuite) TestPrivateKeyFiles(c *gc.C) { // Create/load client keys. They will be cached in memory: // any files added to the directory will not be considered // unless LoadClientKeys is called again. err := ssh.LoadClientKeys("~/.juju/ssh") c.Assert(err, gc.IsNil) checkPrivateKeyFiles(c, "~/.juju/ssh/juju_id_rsa") priv, pub, err := ssh.GenerateKey("whatever") c.Assert(err, gc.IsNil) err = ioutil.WriteFile(testing.HomePath(".juju", "ssh", "whatever"), []byte(priv), 0600) c.Assert(err, gc.IsNil) err = ssh.LoadClientKeys("~/.juju/ssh") c.Assert(err, gc.IsNil) // The new private key won't be observed until the // corresponding public key exists. checkPrivateKeyFiles(c, "~/.juju/ssh/juju_id_rsa") err = ioutil.WriteFile(testing.HomePath(".juju", "ssh", "whatever.pub"), []byte(pub), 0600) c.Assert(err, gc.IsNil) // new keys won't be reported until we call LoadClientKeys again checkPublicKeyFiles(c, "~/.juju/ssh/juju_id_rsa.pub") checkPrivateKeyFiles(c, "~/.juju/ssh/juju_id_rsa") err = ssh.LoadClientKeys("~/.juju/ssh") c.Assert(err, gc.IsNil) checkPublicKeyFiles(c, "~/.juju/ssh/juju_id_rsa.pub", "~/.juju/ssh/whatever.pub") checkPrivateKeyFiles(c, "~/.juju/ssh/juju_id_rsa", "~/.juju/ssh/whatever") } func (s *ClientKeysSuite) TestLoadClientKeysDirExists(c *gc.C) { err := os.MkdirAll(testing.HomePath(".juju", "ssh"), 0755) c.Assert(err, gc.IsNil) err = ssh.LoadClientKeys("~/.juju/ssh") c.Assert(err, gc.IsNil) checkPrivateKeyFiles(c, "~/.juju/ssh/juju_id_rsa") } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/ssh/authorisedkeys_test.go�����������������������0000644�0000153�0000161�00000022647�12321735642�027266� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package ssh_test import ( "encoding/base64" "strings" stdtesting "testing" gc "launchpad.net/gocheck" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/ssh" sshtesting "launchpad.net/juju-core/utils/ssh/testing" ) func Test(t *stdtesting.T) { gc.TestingT(t) } type AuthorisedKeysKeysSuite struct { testbase.LoggingSuite } const ( // We'll use the current user for ssh tests. testSSHUser = "" ) var _ = gc.Suite(&AuthorisedKeysKeysSuite{}) func (s *AuthorisedKeysKeysSuite) SetUpTest(c *gc.C) { s.LoggingSuite.SetUpTest(c) fakeHome := coretesting.MakeEmptyFakeHomeWithoutJuju(c) s.AddCleanup(func(*gc.C) { fakeHome.Restore() }) } func writeAuthKeysFile(c *gc.C, keys []string) { err := ssh.WriteAuthorisedKeys(testSSHUser, keys) c.Assert(err, gc.IsNil) } func (s *AuthorisedKeysKeysSuite) TestListKeys(c *gc.C) { keys := []string{ sshtesting.ValidKeyOne.Key + " user@host", sshtesting.ValidKeyTwo.Key, } writeAuthKeysFile(c, keys) keys, err := ssh.ListKeys(testSSHUser, ssh.Fingerprints) c.Assert(err, gc.IsNil) c.Assert( keys, gc.DeepEquals, []string{sshtesting.ValidKeyOne.Fingerprint + " (user@host)", sshtesting.ValidKeyTwo.Fingerprint}) } func (s *AuthorisedKeysKeysSuite) TestListKeysFull(c *gc.C) { keys := []string{ sshtesting.ValidKeyOne.Key + " user@host", sshtesting.ValidKeyTwo.Key + " anotheruser@host", } writeAuthKeysFile(c, keys) actual, err := ssh.ListKeys(testSSHUser, ssh.FullKeys) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, keys) } func (s *AuthorisedKeysKeysSuite) TestAddNewKey(c *gc.C) { key := sshtesting.ValidKeyOne.Key + " user@host" err := ssh.AddKeys(testSSHUser, key) c.Assert(err, gc.IsNil) actual, err := ssh.ListKeys(testSSHUser, ssh.FullKeys) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, []string{key}) } func (s *AuthorisedKeysKeysSuite) TestAddMoreKeys(c *gc.C) { firstKey := sshtesting.ValidKeyOne.Key + " user@host" writeAuthKeysFile(c, []string{firstKey}) moreKeys := []string{ sshtesting.ValidKeyTwo.Key + " anotheruser@host", sshtesting.ValidKeyThree.Key + " yetanotheruser@host", } err := ssh.AddKeys(testSSHUser, moreKeys...) c.Assert(err, gc.IsNil) actual, err := ssh.ListKeys(testSSHUser, ssh.FullKeys) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, append([]string{firstKey}, moreKeys...)) } func (s *AuthorisedKeysKeysSuite) TestAddDuplicateKey(c *gc.C) { key := sshtesting.ValidKeyOne.Key + " user@host" err := ssh.AddKeys(testSSHUser, key) c.Assert(err, gc.IsNil) moreKeys := []string{ sshtesting.ValidKeyOne.Key + " user@host", sshtesting.ValidKeyTwo.Key + " yetanotheruser@host", } err = ssh.AddKeys(testSSHUser, moreKeys...) c.Assert(err, gc.ErrorMatches, "cannot add duplicate ssh key: "+sshtesting.ValidKeyOne.Fingerprint) } func (s *AuthorisedKeysKeysSuite) TestAddDuplicateComment(c *gc.C) { key := sshtesting.ValidKeyOne.Key + " user@host" err := ssh.AddKeys(testSSHUser, key) c.Assert(err, gc.IsNil) moreKeys := []string{ sshtesting.ValidKeyTwo.Key + " user@host", sshtesting.ValidKeyThree.Key + " yetanotheruser@host", } err = ssh.AddKeys(testSSHUser, moreKeys...) c.Assert(err, gc.ErrorMatches, "cannot add ssh key with duplicate comment: user@host") } func (s *AuthorisedKeysKeysSuite) TestAddKeyWithoutComment(c *gc.C) { keys := []string{ sshtesting.ValidKeyOne.Key + " user@host", sshtesting.ValidKeyTwo.Key, } err := ssh.AddKeys(testSSHUser, keys...) c.Assert(err, gc.ErrorMatches, "cannot add ssh key without comment") } func (s *AuthorisedKeysKeysSuite) TestAddKeepsUnrecognised(c *gc.C) { writeAuthKeysFile(c, []string{sshtesting.ValidKeyOne.Key, "invalid-key"}) anotherKey := sshtesting.ValidKeyTwo.Key + " anotheruser@host" err := ssh.AddKeys(testSSHUser, anotherKey) c.Assert(err, gc.IsNil) actual, err := ssh.ReadAuthorisedKeys(testSSHUser) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, []string{sshtesting.ValidKeyOne.Key, "invalid-key", anotherKey}) } func (s *AuthorisedKeysKeysSuite) TestDeleteKeys(c *gc.C) { firstKey := sshtesting.ValidKeyOne.Key + " user@host" anotherKey := sshtesting.ValidKeyTwo.Key thirdKey := sshtesting.ValidKeyThree.Key + " anotheruser@host" writeAuthKeysFile(c, []string{firstKey, anotherKey, thirdKey}) err := ssh.DeleteKeys(testSSHUser, "user@host", sshtesting.ValidKeyTwo.Fingerprint) c.Assert(err, gc.IsNil) actual, err := ssh.ListKeys(testSSHUser, ssh.FullKeys) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, []string{thirdKey}) } func (s *AuthorisedKeysKeysSuite) TestDeleteKeysKeepsUnrecognised(c *gc.C) { firstKey := sshtesting.ValidKeyOne.Key + " user@host" writeAuthKeysFile(c, []string{firstKey, sshtesting.ValidKeyTwo.Key, "invalid-key"}) err := ssh.DeleteKeys(testSSHUser, "user@host") c.Assert(err, gc.IsNil) actual, err := ssh.ReadAuthorisedKeys(testSSHUser) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, []string{"invalid-key", sshtesting.ValidKeyTwo.Key}) } func (s *AuthorisedKeysKeysSuite) TestDeleteNonExistentComment(c *gc.C) { firstKey := sshtesting.ValidKeyOne.Key + " user@host" writeAuthKeysFile(c, []string{firstKey}) err := ssh.DeleteKeys(testSSHUser, "someone@host") c.Assert(err, gc.ErrorMatches, "cannot delete non existent key: someone@host") } func (s *AuthorisedKeysKeysSuite) TestDeleteNonExistentFingerprint(c *gc.C) { firstKey := sshtesting.ValidKeyOne.Key + " user@host" writeAuthKeysFile(c, []string{firstKey}) err := ssh.DeleteKeys(testSSHUser, sshtesting.ValidKeyTwo.Fingerprint) c.Assert(err, gc.ErrorMatches, "cannot delete non existent key: "+sshtesting.ValidKeyTwo.Fingerprint) } func (s *AuthorisedKeysKeysSuite) TestDeleteLastKeyForbidden(c *gc.C) { keys := []string{ sshtesting.ValidKeyOne.Key + " user@host", sshtesting.ValidKeyTwo.Key + " yetanotheruser@host", } writeAuthKeysFile(c, keys) err := ssh.DeleteKeys(testSSHUser, "user@host", sshtesting.ValidKeyTwo.Fingerprint) c.Assert(err, gc.ErrorMatches, "cannot delete all keys") } func (s *AuthorisedKeysKeysSuite) TestReplaceKeys(c *gc.C) { firstKey := sshtesting.ValidKeyOne.Key + " user@host" anotherKey := sshtesting.ValidKeyTwo.Key writeAuthKeysFile(c, []string{firstKey, anotherKey}) // replaceKey is created without a comment so test that // ReplaceKeys handles keys without comments. This is // because existing keys may not have a comment and // ReplaceKeys is used to rewrite the entire authorized_keys // file when adding new keys. replaceKey := sshtesting.ValidKeyThree.Key err := ssh.ReplaceKeys(testSSHUser, replaceKey) c.Assert(err, gc.IsNil) actual, err := ssh.ListKeys(testSSHUser, ssh.FullKeys) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, []string{replaceKey}) } func (s *AuthorisedKeysKeysSuite) TestReplaceKeepsUnrecognised(c *gc.C) { writeAuthKeysFile(c, []string{sshtesting.ValidKeyOne.Key, "invalid-key"}) anotherKey := sshtesting.ValidKeyTwo.Key + " anotheruser@host" err := ssh.ReplaceKeys(testSSHUser, anotherKey) c.Assert(err, gc.IsNil) actual, err := ssh.ReadAuthorisedKeys(testSSHUser) c.Assert(err, gc.IsNil) c.Assert(actual, gc.DeepEquals, []string{"invalid-key", anotherKey}) } func (s *AuthorisedKeysKeysSuite) TestEnsureJujuComment(c *gc.C) { sshKey := sshtesting.ValidKeyOne.Key for _, test := range []struct { key string expected string }{ {"invalid-key", "invalid-key"}, {sshKey, sshKey + " Juju:sshkey"}, {sshKey + " user@host", sshKey + " Juju:user@host"}, {sshKey + " Juju:user@host", sshKey + " Juju:user@host"}, {sshKey + " " + sshKey[3:5], sshKey + " Juju:" + sshKey[3:5]}, } { actual := ssh.EnsureJujuComment(test.key) c.Assert(actual, gc.Equals, test.expected) } } func (s *AuthorisedKeysKeysSuite) TestSplitAuthorisedKeys(c *gc.C) { sshKey := sshtesting.ValidKeyOne.Key for _, test := range []struct { keyData string expected []string }{ {"", nil}, {sshKey, []string{sshKey}}, {sshKey + "\n", []string{sshKey}}, {sshKey + "\n\n", []string{sshKey}}, {sshKey + "\n#comment\n", []string{sshKey}}, {sshKey + "\n #comment\n", []string{sshKey}}, {sshKey + "\ninvalid\n", []string{sshKey, "invalid"}}, } { actual := ssh.SplitAuthorisedKeys(test.keyData) c.Assert(actual, gc.DeepEquals, test.expected) } } func b64decode(c *gc.C, s string) []byte { b, err := base64.StdEncoding.DecodeString(s) c.Assert(err, gc.IsNil) return b } func (s *AuthorisedKeysKeysSuite) TestParseAuthorisedKey(c *gc.C) { for i, test := range []struct { line string key []byte comment string err string }{{ line: sshtesting.ValidKeyOne.Key, key: b64decode(c, strings.Fields(sshtesting.ValidKeyOne.Key)[1]), }, { line: sshtesting.ValidKeyOne.Key + " a b c", key: b64decode(c, strings.Fields(sshtesting.ValidKeyOne.Key)[1]), comment: "a b c", }, { line: "ssh-xsa blah", err: "invalid authorized_key \"ssh-xsa blah\"", }, { // options should be skipped line: `no-pty,principals="\"",command="\!" ` + sshtesting.ValidKeyOne.Key, key: b64decode(c, strings.Fields(sshtesting.ValidKeyOne.Key)[1]), }, { line: "ssh-rsa", err: "invalid authorized_key \"ssh-rsa\"", }} { c.Logf("test %d: %s", i, test.line) ak, err := ssh.ParseAuthorisedKey(test.line) if test.err != "" { c.Assert(err, gc.ErrorMatches, test.err) } else { c.Assert(err, gc.IsNil) c.Assert(ak, gc.Not(gc.IsNil)) c.Assert(ak.Key, gc.DeepEquals, test.key) c.Assert(ak.Comment, gc.Equals, test.comment) } } } �����������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/gomaxprocs.go������������������������������������0000644�0000153�0000161�00000001211�12321735642�024531� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "os" "runtime" ) var gomaxprocs = runtime.GOMAXPROCS var numCPU = runtime.NumCPU // UseMultipleCPUs sets GOMAXPROCS to the number of CPU cores unless it has // already been overridden by the GOMAXPROCS environment variable. func UseMultipleCPUs() { if envGOMAXPROCS := os.Getenv("GOMAXPROCS"); envGOMAXPROCS != "" { n := gomaxprocs(0) logger.Debugf("GOMAXPROCS already set in environment to %q, %d internally", envGOMAXPROCS, n) return } n := numCPU() logger.Debugf("setting GOMAXPROCS to %d", n) gomaxprocs(n) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/uuid.go������������������������������������������0000644�0000153�0000161�00000003235�12321735642�023325� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "crypto/rand" "encoding/hex" "fmt" "io" "regexp" "strings" ) // UUID represent a universal identifier with 16 octets. type UUID [16]byte var validUUID = regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[8,9,a,b][0-9a-f]{3}-[0-9a-f]{12}") func UUIDFromString(s string) (UUID, error) { if !IsValidUUIDString(s) { return UUID{}, fmt.Errorf("invalid UUID: %q", s) } s = strings.Replace(s, "-", "", 4) raw, err := hex.DecodeString(s) if err != nil { return UUID{}, err } var uuid UUID copy(uuid[:], raw) return uuid, nil } // IsValidUUIDString returns true, if the given string matches a valid UUID (version 4, variant 2). func IsValidUUIDString(s string) bool { return validUUID.MatchString(s) } // NewUUID generates a new version 4 UUID relying only on random numbers. func NewUUID() (UUID, error) { uuid := UUID{} if _, err := io.ReadFull(rand.Reader, []byte(uuid[0:16])); err != nil { return UUID{}, err } // Set version (4) and variant (2) according to RfC 4122. var version byte = 4 << 4 var variant byte = 8 << 4 uuid[6] = version | (uuid[6] & 15) uuid[8] = variant | (uuid[8] & 15) return uuid, nil } // Copy returns a copy of the UUID. func (uuid UUID) Copy() UUID { uuidCopy := uuid return uuidCopy } // Raw returns a copy of the UUID bytes. func (uuid UUID) Raw() [16]byte { return [16]byte(uuid) } // String returns a hexadecimal string representation with // standardized separators. func (uuid UUID) String() string { return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:16]) } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/zip/���������������������������������������������0000755�0000153�0000161�00000000000�12321736000�022614� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/zip/package_test.go������������������������������0000644�0000153�0000161�00000005105�12321735776�025621� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package zip_test import ( "archive/zip" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" ) func TestPackage(t *testing.T) { gc.TestingT(t) } type BaseSuite struct { testbase.LoggingSuite } func (s *BaseSuite) makeZip(c *gc.C, creators ...creator) *zip.Reader { basePath := c.MkDir() for _, creator := range creators { creator.create(c, basePath) } defer os.RemoveAll(basePath) outPath := join(c.MkDir(), "test.zip") cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("cd %q; zip --fifo --symlinks -r %q .", basePath, outPath)) output, err := cmd.CombinedOutput() c.Assert(err, gc.IsNil, gc.Commentf("Command output: %s", output)) file, err := os.Open(outPath) c.Assert(err, gc.IsNil) s.AddCleanup(func(c *gc.C) { err := file.Close() c.Assert(err, gc.IsNil) }) fileInfo, err := file.Stat() c.Assert(err, gc.IsNil) reader, err := zip.NewReader(file, fileInfo.Size()) c.Assert(err, gc.IsNil) return reader } type creator interface { create(c *gc.C, basePath string) check(c *gc.C, basePath string) } func join(basePath, path string) string { return filepath.Join(basePath, filepath.FromSlash(path)) } type dir struct { path string perm os.FileMode } func (d dir) create(c *gc.C, basePath string) { err := os.MkdirAll(join(basePath, d.path), d.perm) c.Assert(err, gc.IsNil) } func (d dir) check(c *gc.C, basePath string) { fileInfo, err := os.Lstat(join(basePath, d.path)) c.Check(err, gc.IsNil) c.Check(fileInfo.Mode()&os.ModePerm, gc.Equals, d.perm) } type file struct { path string data string perm os.FileMode } func (f file) create(c *gc.C, basePath string) { err := ioutil.WriteFile(join(basePath, f.path), []byte(f.data), f.perm) c.Assert(err, gc.IsNil) } func (f file) check(c *gc.C, basePath string) { path := join(basePath, f.path) fileInfo, err := os.Lstat(path) if !c.Check(err, gc.IsNil) { return } 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) } type symlink struct { path string data string } func (s symlink) create(c *gc.C, basePath string) { err := os.Symlink(s.data, join(basePath, s.path)) c.Assert(err, gc.IsNil) } func (s symlink) check(c *gc.C, basePath string) { data, err := os.Readlink(join(basePath, s.path)) c.Check(err, gc.IsNil) c.Check(data, gc.Equals, s.data) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/zip/zip_test.go����������������������������������0000644�0000153�0000161�00000020776�12321735776�025043� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package zip_test import ( "bytes" "io/ioutil" "os/exec" "path/filepath" "sort" jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/utils/zip" ) type ZipSuite struct { BaseSuite } var _ = gc.Suite(&ZipSuite{}) func (s *ZipSuite) TestFind(c *gc.C) { reader := s.makeZip(c, file{"some-file", "", 0644}, file{"another-file", "", 0644}, symlink{"some-symlink", "some-file"}, dir{"some-dir", 0755}, dir{"some-dir/another-dir", 0755}, file{"some-dir/another-file", "", 0644}, ) for i, test := range []struct { pattern string expect []string }{{ "", nil, }, { "no-matches", nil, }, { "some-file", []string{ "some-file"}, }, { "another-file", []string{ "another-file", "some-dir/another-file"}, }, { "some-*", []string{ "some-file", "some-symlink", "some-dir"}, }, { "another-*", []string{ "another-file", "some-dir/another-dir", "some-dir/another-file"}, }, { "*", []string{ "some-file", "another-file", "some-symlink", "some-dir", "some-dir/another-dir", "some-dir/another-file"}, }} { c.Logf("test %d: %q", i, test.pattern) actual, err := zip.Find(reader, test.pattern) c.Assert(err, gc.IsNil) sort.Strings(test.expect) sort.Strings(actual) c.Check(actual, jc.DeepEquals, test.expect) } c.Logf("test $spanish-inquisition: FindAll") expect, err := zip.Find(reader, "*") c.Assert(err, gc.IsNil) actual, err := zip.FindAll(reader) c.Assert(err, gc.IsNil) sort.Strings(expect) sort.Strings(actual) c.Check(actual, jc.DeepEquals, expect) } func (s *ZipSuite) TestFindError(c *gc.C) { reader := s.makeZip(c, file{"some-file", "", 0644}) _, err := zip.Find(reader, "[]") c.Assert(err, gc.ErrorMatches, "syntax error in pattern") } func (s *ZipSuite) TestExtractAll(c *gc.C) { creators := []creator{ file{"some-file", "content 1", 0644}, file{"another-file", "content 2", 0640}, symlink{"some-symlink", "some-file"}, dir{"some-dir", 0750}, file{"some-dir/another-file", "content 3", 0644}, dir{"some-dir/another-dir", 0755}, symlink{"some-dir/another-dir/another-symlink", "../../another-file"}, } reader := s.makeZip(c, creators...) targetPath := c.MkDir() err := zip.ExtractAll(reader, targetPath) c.Assert(err, gc.IsNil) for i, creator := range creators { c.Logf("test %d: %#v", i, creator) creator.check(c, targetPath) } } func (s *ZipSuite) TestExtractAllOverwriteFiles(c *gc.C) { name := "some-file" for i, test := range []creator{ file{name, "content", 0644}, dir{name, 0751}, symlink{name, "wherever"}, } { c.Logf("test %d: %#v", i, test) targetPath := c.MkDir() file{name, "original", 0}.create(c, targetPath) reader := s.makeZip(c, test) err := zip.ExtractAll(reader, targetPath) c.Check(err, gc.IsNil) test.check(c, targetPath) } } func (s *ZipSuite) TestExtractAllOverwriteSymlinks(c *gc.C) { name := "some-symlink" for i, test := range []creator{ file{name, "content", 0644}, dir{name, 0751}, symlink{name, "wherever"}, } { c.Logf("test %d: %#v", i, test) targetPath := c.MkDir() original := file{"original", "content", 0644} original.create(c, targetPath) symlink{name, "original"}.create(c, targetPath) reader := s.makeZip(c, test) err := zip.ExtractAll(reader, targetPath) c.Check(err, gc.IsNil) test.check(c, targetPath) original.check(c, targetPath) } } func (s *ZipSuite) TestExtractAllOverwriteDirs(c *gc.C) { name := "some-dir" for i, test := range []creator{ file{name, "content", 0644}, dir{name, 0751}, symlink{name, "wherever"}, } { c.Logf("test %d: %#v", i, test) targetPath := c.MkDir() dir{name, 0}.create(c, targetPath) reader := s.makeZip(c, test) err := zip.ExtractAll(reader, targetPath) c.Check(err, gc.IsNil) test.check(c, targetPath) } } func (s *ZipSuite) TestExtractAllMergeDirs(c *gc.C) { targetPath := c.MkDir() dir{"dir", 0755}.create(c, targetPath) originals := []creator{ dir{"dir/original-dir", 0751}, file{"dir/original-file", "content 1", 0600}, symlink{"dir/original-symlink", "original-file"}, } for _, creator := range originals { creator.create(c, targetPath) } merges := []creator{ dir{"dir", 0751}, dir{"dir/merge-dir", 0750}, file{"dir/merge-file", "content 2", 0640}, symlink{"dir/merge-symlink", "merge-file"}, } reader := s.makeZip(c, merges...) err := zip.ExtractAll(reader, targetPath) c.Assert(err, gc.IsNil) for i, test := range append(originals, merges...) { c.Logf("test %d: %#v", i, test) test.check(c, targetPath) } } func (s *ZipSuite) TestExtractAllSymlinkErrors(c *gc.C) { for i, test := range []struct { content []creator error string }{{ content: []creator{ symlink{"symlink", "/blah"}, }, error: `cannot extract "symlink": symlink "/blah" is absolute`, }, { content: []creator{ symlink{"symlink", "../blah"}, }, error: `cannot extract "symlink": symlink "../blah" leads out of scope`, }, { content: []creator{ dir{"dir", 0755}, symlink{"dir/symlink", "../../blah"}, }, error: `cannot extract "dir/symlink": symlink "../../blah" leads out of scope`, }} { c.Logf("test %d: %s", i, test.error) targetPath := c.MkDir() reader := s.makeZip(c, test.content...) err := zip.ExtractAll(reader, targetPath) c.Check(err, gc.ErrorMatches, test.error) } } func (s *ZipSuite) TestExtractDir(c *gc.C) { reader := s.makeZip(c, file{"bad-file", "xxx", 0644}, dir{"bad-dir", 0755}, symlink{"bad-symlink", "bad-file"}, dir{"some-dir", 0751}, file{"some-dir-bad-lol", "xxx", 0644}, file{"some-dir/some-file", "content 1", 0644}, file{"some-dir/another-file", "content 2", 0600}, dir{"some-dir/another-dir", 0750}, symlink{"some-dir/another-dir/some-symlink", "../some-file"}, ) targetParent := c.MkDir() targetPath := filepath.Join(targetParent, "random-dir") err := zip.Extract(reader, targetPath, "some-dir") c.Assert(err, gc.IsNil) for i, test := range []creator{ dir{"random-dir", 0751}, file{"random-dir/some-file", "content 1", 0644}, file{"random-dir/another-file", "content 2", 0600}, dir{"random-dir/another-dir", 0750}, symlink{"random-dir/another-dir/some-symlink", "../some-file"}, } { c.Logf("test %d: %#v", i, test) test.check(c, targetParent) } fileInfos, err := ioutil.ReadDir(targetParent) c.Check(err, gc.IsNil) c.Check(fileInfos, gc.HasLen, 1) fileInfos, err = ioutil.ReadDir(targetPath) c.Check(err, gc.IsNil) c.Check(fileInfos, gc.HasLen, 3) } func (s *ZipSuite) TestExtractSingleFile(c *gc.C) { reader := s.makeZip(c, dir{"dir", 0755}, dir{"dir/dir", 0755}, file{"dir/dir/some-file", "content 1", 0644}, file{"dir/dir/some-file-wtf", "content 2", 0644}, ) targetParent := c.MkDir() targetPath := filepath.Join(targetParent, "just-the-one-file") err := zip.Extract(reader, targetPath, "dir/dir/some-file") c.Assert(err, gc.IsNil) fileInfos, err := ioutil.ReadDir(targetParent) c.Check(err, gc.IsNil) c.Check(fileInfos, gc.HasLen, 1) file{"just-the-one-file", "content 1", 0644}.check(c, targetParent) } func (s *ZipSuite) TestClosesFile(c *gc.C) { reader := s.makeZip(c, file{"f", "echo hullo!", 0755}) targetPath := c.MkDir() err := zip.ExtractAll(reader, targetPath) c.Assert(err, gc.IsNil) cmd := exec.Command("/bin/sh", "-c", filepath.Join(targetPath, "f")) var buffer bytes.Buffer cmd.Stdout = &buffer err = cmd.Run() c.Assert(err, gc.IsNil) c.Assert(buffer.String(), gc.Equals, "hullo!\n") } func (s *ZipSuite) TestExtractSymlinkErrors(c *gc.C) { for i, test := range []struct { content []creator source string error string }{{ content: []creator{ dir{"dir", 0755}, symlink{"dir/symlink", "/blah"}, }, source: "dir", error: `cannot extract "dir/symlink": symlink "/blah" is absolute`, }, { content: []creator{ dir{"dir", 0755}, symlink{"dir/symlink", "../blah"}, }, source: "dir", error: `cannot extract "dir/symlink": symlink "../blah" leads out of scope`, }, { content: []creator{ symlink{"symlink", "blah"}, }, source: "symlink", error: `cannot extract "symlink": symlink "blah" leads out of scope`, }} { c.Logf("test %d: %s", i, test.error) targetPath := c.MkDir() reader := s.makeZip(c, test.content...) err := zip.Extract(reader, targetPath, test.source) c.Check(err, gc.ErrorMatches, test.error) } } func (s *ZipSuite) TestExtractSourceError(c *gc.C) { reader := s.makeZip(c, dir{"dir", 0755}) err := zip.Extract(reader, c.MkDir(), "../lol") c.Assert(err, gc.ErrorMatches, `cannot extract files rooted at "../lol"`) } ��juju-core_1.18.1/src/launchpad.net/juju-core/utils/zip/zip.go���������������������������������������0000644�0000153�0000161�00000012627�12321735642�023770� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2011-2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package zip import ( "archive/zip" "bytes" "fmt" "io" "os" "path" "path/filepath" "strings" ) // FindAll returns the cleaned path of every file in the supplied zip reader. func FindAll(reader *zip.Reader) ([]string, error) { return Find(reader, "*") } // Find returns the cleaned path of every file in the supplied zip reader whose // base name matches the supplied pattern, which is interpreted as in path.Match. func Find(reader *zip.Reader, pattern string) ([]string, error) { // path.Match will only return an error if the pattern is not // valid (*and* the supplied name is not empty, hence "check"). if _, err := path.Match(pattern, "check"); err != nil { return nil, err } var matches []string for _, zipFile := range reader.File { cleanPath := path.Clean(zipFile.Name) baseName := path.Base(cleanPath) if match, _ := path.Match(pattern, baseName); match { matches = append(matches, cleanPath) } } return matches, nil } // ExtractAll extracts the supplied zip reader to the target path, overwriting // existing files and directories only where necessary. func ExtractAll(reader *zip.Reader, targetRoot string) error { return Extract(reader, targetRoot, "") } // Extract extracts files from the supplied zip reader, from the (internal, slash- // separated) source path into the (external, OS-specific) target path. If the // source path does not reference a directory, the referenced file will be written // directly to the target path. func Extract(reader *zip.Reader, targetRoot, sourceRoot string) error { sourceRoot = path.Clean(sourceRoot) if sourceRoot == "." { sourceRoot = "" } if !isSanePath(sourceRoot) { return fmt.Errorf("cannot extract files rooted at %q", sourceRoot) } extractor := extractor{targetRoot, sourceRoot} for _, zipFile := range reader.File { if err := extractor.extract(zipFile); err != nil { cleanName := path.Clean(zipFile.Name) return fmt.Errorf("cannot extract %q: %v", cleanName, err) } } return nil } type extractor struct { targetRoot string sourceRoot string } // targetPath returns the target path for a given zip file and whether // it should be extracted. func (x extractor) targetPath(zipFile *zip.File) (string, bool) { cleanPath := path.Clean(zipFile.Name) if cleanPath == x.sourceRoot { return x.targetRoot, true } if x.sourceRoot != "" { mustPrefix := x.sourceRoot + "/" if !strings.HasPrefix(cleanPath, mustPrefix) { return "", false } cleanPath = cleanPath[len(mustPrefix):] } return filepath.Join(x.targetRoot, filepath.FromSlash(cleanPath)), true } func (x extractor) extract(zipFile *zip.File) error { targetPath, ok := x.targetPath(zipFile) if !ok { return nil } parentPath := filepath.Dir(targetPath) if err := os.MkdirAll(parentPath, 0777); err != nil { return err } mode := zipFile.Mode() modePerm := mode & os.ModePerm modeType := mode & os.ModeType switch modeType { case os.ModeDir: return x.writeDir(targetPath, modePerm) case os.ModeSymlink: return x.writeSymlink(targetPath, zipFile) case 0: return x.writeFile(targetPath, zipFile, modePerm) } return fmt.Errorf("unknown file type %d", modeType) } func (x extractor) writeDir(targetPath string, modePerm os.FileMode) error { fileInfo, err := os.Lstat(targetPath) switch { case err == nil: mode := fileInfo.Mode() if mode.IsDir() { if mode&os.ModePerm != modePerm { return os.Chmod(targetPath, modePerm) } return nil } fallthrough case !os.IsNotExist(err): if err := os.RemoveAll(targetPath); err != nil { return err } } return os.MkdirAll(targetPath, modePerm) } func (x extractor) writeFile(targetPath string, zipFile *zip.File, modePerm os.FileMode) error { if _, err := os.Lstat(targetPath); !os.IsNotExist(err) { if err := os.RemoveAll(targetPath); err != nil { return err } } writer, err := os.OpenFile(targetPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, modePerm) if err != nil { return err } defer writer.Close() return copyTo(writer, zipFile) } func (x extractor) writeSymlink(targetPath string, zipFile *zip.File) error { symlinkTarget, err := x.checkSymlink(targetPath, zipFile) if err != nil { return err } if _, err := os.Lstat(targetPath); !os.IsNotExist(err) { if err := os.RemoveAll(targetPath); err != nil { return err } } return os.Symlink(symlinkTarget, targetPath) } func (x extractor) checkSymlink(targetPath string, zipFile *zip.File) (string, error) { var buffer bytes.Buffer if err := copyTo(&buffer, zipFile); err != nil { return "", err } symlinkTarget := buffer.String() if filepath.IsAbs(symlinkTarget) { return "", fmt.Errorf("symlink %q is absolute", symlinkTarget) } finalPath := filepath.Join(filepath.Dir(targetPath), symlinkTarget) relativePath, err := filepath.Rel(x.targetRoot, finalPath) if err != nil { // Not tested, because I don't know how to trigger this condition. return "", fmt.Errorf("symlink %q not comprehensible", symlinkTarget) } if !isSanePath(relativePath) { return "", fmt.Errorf("symlink %q leads out of scope", symlinkTarget) } return symlinkTarget, nil } func copyTo(writer io.Writer, zipFile *zip.File) error { reader, err := zipFile.Open() if err != nil { return err } _, err = io.Copy(writer, reader) reader.Close() return err } func isSanePath(path string) bool { if path == ".." || strings.HasPrefix(path, "../") { return false } return true } ���������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/file.go������������������������������������������0000644�0000153�0000161�00000010052�12321735642�023271� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "fmt" "io" "io/ioutil" "os" "os/user" "path" "path/filepath" "regexp" "runtime" "launchpad.net/juju-core/juju/osenv" ) // UserHomeDir returns the home directory for the specified user, or the // home directory for the current user if the specified user is empty. func UserHomeDir(userName string) (homeDir string, err error) { var u *user.User if userName == "" { // TODO (wallyworld) - fix tests on Windows // Ordinarily, we'd always use user.Current() to get the current user // and then get the HomeDir from that. But our tests rely on poking // a value into $HOME in order to override the normal home dir for the // current user. So on *nix, we're forced to use osenv.Home() to make // the tests pass. All of our tests currently construct paths with the // default user in mind eg "~/foo". if runtime.GOOS == "windows" { u, err = user.Current() } else { return osenv.Home(), nil } } else { u, err = user.Lookup(userName) if err != nil { return "", err } } return u.HomeDir, nil } var userHomePathRegexp = regexp.MustCompile("(~(?P<user>[^/]*))(?P<path>.*)") // NormalizePath expands a path containing ~ to its absolute form, // and removes any .. or . path elements. func NormalizePath(dir string) (string, error) { if userHomePathRegexp.MatchString(dir) { user := userHomePathRegexp.ReplaceAllString(dir, "$user") userHomeDir, err := UserHomeDir(user) if err != nil { return "", err } dir = userHomePathRegexp.ReplaceAllString(dir, fmt.Sprintf("%s$path", userHomeDir)) } return filepath.Clean(dir), nil } // JoinServerPath joins any number of path elements into a single path, adding // a path separator (based on the current juju server OS) if necessary. The // result is Cleaned; in particular, all empty strings are ignored. func JoinServerPath(elem ...string) string { return path.Join(elem...) } // UniqueDirectory returns "path/name" if that directory doesn't exist. If it // does, the method starts appending .1, .2, etc until a unique name is found. func UniqueDirectory(path, name string) (string, error) { dir := filepath.Join(path, name) _, err := os.Stat(dir) if os.IsNotExist(err) { return dir, nil } for i := 1; ; i++ { dir := filepath.Join(path, fmt.Sprintf("%s.%d", name, i)) _, err := os.Stat(dir) if os.IsNotExist(err) { return dir, nil } else if err != nil { return "", err } } } // CopyFile writes the contents of the given source file to dest. func CopyFile(dest, source string) error { df, err := os.Create(dest) if err != nil { return err } f, err := os.Open(source) if err != nil { return err } defer f.Close() _, err = io.Copy(df, f) return err } // AtomicWriteFileAndChange atomically writes the filename with the // given contents and calls the given function after the contents were // written, but before the file is renamed. func AtomicWriteFileAndChange(filename string, contents []byte, change func(*os.File) error) (err error) { dir, file := filepath.Split(filename) f, err := ioutil.TempFile(dir, file) if err != nil { return fmt.Errorf("cannot create temp file: %v", err) } defer f.Close() defer func() { if err != nil { // Don't leave the temp file lying around on error. os.Remove(f.Name()) } }() if _, err := f.Write(contents); err != nil { return fmt.Errorf("cannot write %q contents: %v", filename, err) } if err := change(f); err != nil { return err } if err := ReplaceFile(f.Name(), filename); err != nil { return fmt.Errorf("cannot replace %q with %q: %v", f.Name(), filename, err) } return nil } // AtomicWriteFile atomically writes the filename with the given // contents and permissions, replacing any existing file at the same // path. func AtomicWriteFile(filename string, contents []byte, perms os.FileMode) (err error) { return AtomicWriteFileAndChange(filename, contents, func(f *os.File) error { if err := f.Chmod(perms); err != nil { return fmt.Errorf("cannot set permissions: %v", err) } return nil }) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/trivial.go���������������������������������������0000644�0000153�0000161�00000007127�12321735642�024035� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "bytes" "compress/gzip" "crypto/sha256" "encoding/hex" "errors" "fmt" "io" "io/ioutil" "os" "strings" "unicode" "launchpad.net/goyaml" ) // WriteYaml marshals obj as yaml and then writes it to a file, atomically, // by first writing a sibling with the suffix ".preparing" and then moving // the sibling to the real path. func WriteYaml(path string, obj interface{}) error { data, err := goyaml.Marshal(obj) if err != nil { return err } prep := path + ".preparing" f, err := os.OpenFile(prep, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644) if err != nil { return err } defer f.Close() if _, err = f.Write(data); err != nil { return err } return ReplaceFile(prep, path) } // ReadYaml unmarshals the yaml contained in the file at path into obj. See // goyaml.Unmarshal. func ReadYaml(path string, obj interface{}) error { data, err := ioutil.ReadFile(path) if err != nil { return err } return goyaml.Unmarshal(data, obj) } // ErrorContextf prefixes any error stored in err with text formatted // according to the format specifier. If err does not contain an error, // ErrorContextf does nothing. func ErrorContextf(err *error, format string, args ...interface{}) { if *err != nil { *err = errors.New(fmt.Sprintf(format, args...) + ": " + (*err).Error()) } } // ShQuote quotes s so that when read by bash, no metacharacters // within s will be interpreted as such. func ShQuote(s string) string { // single-quote becomes single-quote, double-quote, single-quote, double-quote, single-quote return `'` + strings.Replace(s, `'`, `'"'"'`, -1) + `'` } // CommandString flattens a sequence of command arguments into a // string suitable for executing in a shell, escaping slashes, // variables and quotes as necessary; each argument is double-quoted // if and only if necessary. func CommandString(args ...string) string { var buf bytes.Buffer for i, arg := range args { needsQuotes := false var argBuf bytes.Buffer for _, r := range arg { if unicode.IsSpace(r) { needsQuotes = true } else if r == '"' || r == '$' || r == '\\' { needsQuotes = true argBuf.WriteByte('\\') } argBuf.WriteRune(r) } if i > 0 { buf.WriteByte(' ') } if needsQuotes { buf.WriteByte('"') argBuf.WriteTo(&buf) buf.WriteByte('"') } else { argBuf.WriteTo(&buf) } } return buf.String() } // Gzip compresses the given data. func Gzip(data []byte) []byte { var buf bytes.Buffer w := gzip.NewWriter(&buf) if _, err := w.Write(data); err != nil { // Compression should never fail unless it fails // to write to the underlying writer, which is a bytes.Buffer // that never fails. panic(err) } if err := w.Close(); err != nil { panic(err) } return buf.Bytes() } // Gunzip uncompresses the given data. func Gunzip(data []byte) ([]byte, error) { r, err := gzip.NewReader(bytes.NewReader(data)) if err != nil { return nil, err } return ioutil.ReadAll(r) } // ReadSHA256 returns the SHA256 hash of the contents read from source // (hex encoded) and the size of the source in bytes. func ReadSHA256(source io.Reader) (string, int64, error) { hash := sha256.New() size, err := io.Copy(hash, source) if err != nil { return "", 0, err } digest := hex.EncodeToString(hash.Sum(nil)) return digest, size, nil } // ReadFileSHA256 is like ReadSHA256 but reads the contents of the // given file. func ReadFileSHA256(filename string) (string, int64, error) { f, err := os.Open(filename) if err != nil { return "", 0, err } defer f.Close() return ReadSHA256(f) } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/shell/�������������������������������������������0000755�0000153�0000161�00000000000�12321735642�023134� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/shell/script_test.go�����������������������������0000644�0000153�0000161�00000003134�12321735642�026027� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package shell_test import ( "bytes" "io/ioutil" "os" "os/exec" "path/filepath" "strings" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils/shell" ) type scriptSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&scriptSuite{}) func (*scriptSuite) TestDumpFileOnErrorScriptOutput(c *gc.C) { script := shell.DumpFileOnErrorScript("a b c") c.Assert(script, gc.Equals, ` dump_file() { code=$? if [ $code -ne 0 -a -e 'a b c' ]; then cat 'a b c' >&2 fi exit $code } trap dump_file EXIT `[1:]) } func (*scriptSuite) TestDumpFileOnErrorScript(c *gc.C) { tempdir := c.MkDir() filename := filepath.Join(tempdir, "log.txt") err := ioutil.WriteFile(filename, []byte("abc"), 0644) c.Assert(err, gc.IsNil) dumpScript := shell.DumpFileOnErrorScript(filename) c.Logf("%s", dumpScript) run := func(command string) (stdout, stderr string) { var stdoutBuf, stderrBuf bytes.Buffer cmd := exec.Command("/bin/bash", "-s") cmd.Stdin = strings.NewReader(dumpScript + command) cmd.Stdout = &stdoutBuf cmd.Stderr = &stderrBuf cmd.Run() return stdoutBuf.String(), stderrBuf.String() } stdout, stderr := run("exit 0") c.Assert(stdout, gc.Equals, "") c.Assert(stderr, gc.Equals, "") stdout, stderr = run("exit 1") c.Assert(stdout, gc.Equals, "") c.Assert(stderr, gc.Equals, "abc") err = os.Remove(filename) c.Assert(err, gc.IsNil) stdout, stderr = run("exit 1") c.Assert(stdout, gc.Equals, "") c.Assert(stderr, gc.Equals, "") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/shell/package_test.go����������������������������0000644�0000153�0000161�00000000322�12321735642�026112� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package shell_test import ( "testing" gc "launchpad.net/gocheck" ) func Test(t *testing.T) { gc.TestingT(t) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/shell/script.go����������������������������������0000644�0000153�0000161�00000001136�12321735642�024770� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2014 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package shell import ( "fmt" "launchpad.net/juju-core/utils" ) // DumpFileOnErrorScript returns a bash script that // may be used to dump the contents of the specified // file to stderr when the shell exits with an error. func DumpFileOnErrorScript(filename string) string { script := ` dump_file() { code=$? if [ $code -ne 0 -a -e %s ]; then cat %s >&2 fi exit $code } trap dump_file EXIT `[1:] filename = utils.ShQuote(filename) return fmt.Sprintf(script, filename, filename) } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/http_test.go�������������������������������������0000644�0000153�0000161�00000004044�12321735776�024404� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils_test import ( "fmt" "io/ioutil" "net/http" "net/http/httptest" gc "launchpad.net/gocheck" "launchpad.net/juju-core/utils" ) type httpSuite struct { Server *httptest.Server } var _ = gc.Suite(&httpSuite{}) type trivialResponseHandler struct{} func (t *trivialResponseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Greetings!\n") } func (s *httpSuite) SetUpSuite(c *gc.C) { } func (s *httpSuite) TearDownSuite(c *gc.C) { } func (s *httpSuite) SetUpTest(c *gc.C) { s.Server = httptest.NewTLSServer(&trivialResponseHandler{}) } func (s *httpSuite) TearDownTest(c *gc.C) { if s.Server != nil { s.Server.Close() } } func (s *httpSuite) TestDefaultClientFails(c *gc.C) { _, err := http.Get(s.Server.URL) c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority") } func (s *httpSuite) TestValidatingClientGetter(c *gc.C) { client1 := utils.GetValidatingHTTPClient() client2 := utils.GetHTTPClient(utils.VerifySSLHostnames) c.Check(client1, gc.Equals, client2) } func (s *httpSuite) TestNonValidatingClientGetter(c *gc.C) { client1 := utils.GetNonValidatingHTTPClient() client2 := utils.GetHTTPClient(utils.NoVerifySSLHostnames) c.Check(client1, gc.Equals, client2) } func (s *httpSuite) TestValidatingClientFails(c *gc.C) { client := utils.GetValidatingHTTPClient() _, err := client.Get(s.Server.URL) c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority") } func (s *httpSuite) TestInsecureClientSucceeds(c *gc.C) { response, err := utils.GetNonValidatingHTTPClient().Get(s.Server.URL) c.Assert(err, gc.IsNil) defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) c.Assert(err, gc.IsNil) c.Check(string(body), gc.Equals, "Greetings!\n") } func (s *httpSuite) TestInsecureClientCached(c *gc.C) { client1 := utils.GetNonValidatingHTTPClient() client2 := utils.GetNonValidatingHTTPClient() c.Check(client1, gc.Equals, client2) } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/command.go���������������������������������������0000644�0000153�0000161�00000000662�12321735642�023776� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "os/exec" ) // RunCommand executes the command and return the combined output. func RunCommand(command string, args ...string) (output string, err error) { cmd := exec.Command(command, args...) out, err := cmd.CombinedOutput() output = string(out) if err != nil { return output, err } return output, nil } ������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/set/���������������������������������������������0000755�0000153�0000161�00000000000�12321735642�022620� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/set/strings_test.go������������������������������0000644�0000153�0000161�00000010426�12321735642�025702� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package set_test import ( "sort" "testing" gc "launchpad.net/gocheck" "launchpad.net/juju-core/utils/set" ) func Test(t *testing.T) { gc.TestingT(t) } type stringSetSuite struct{} var _ = gc.Suite(stringSetSuite{}) // Helper methods for the tests. func AssertValues(c *gc.C, s set.Strings, expected ...string) { values := s.Values() // Expect an empty slice, not a nil slice for values. if expected == nil { expected = []string{} } sort.Strings(expected) sort.Strings(values) c.Assert(values, gc.DeepEquals, expected) c.Assert(s.Size(), gc.Equals, len(expected)) // Check the sorted values too. sorted := s.SortedValues() c.Assert(sorted, gc.DeepEquals, expected) } // Actual tests start here. func (stringSetSuite) TestEmpty(c *gc.C) { s := set.NewStrings() AssertValues(c, s) } func (stringSetSuite) TestInitialValues(c *gc.C) { values := []string{"foo", "bar", "baz"} s := set.NewStrings(values...) AssertValues(c, s, values...) } func (stringSetSuite) TestSize(c *gc.C) { // Empty sets are empty. s := set.NewStrings() c.Assert(s.Size(), gc.Equals, 0) // Size returns number of unique values. s = set.NewStrings("foo", "foo", "bar") c.Assert(s.Size(), gc.Equals, 2) } func (stringSetSuite) TestIsEmpty(c *gc.C) { // Empty sets are empty. s := set.NewStrings() c.Assert(s.IsEmpty(), gc.Equals, true) // Non-empty sets are not empty. s = set.NewStrings("foo") c.Assert(s.IsEmpty(), gc.Equals, false) // Newly empty sets work too. s.Remove("foo") c.Assert(s.IsEmpty(), gc.Equals, true) } func (stringSetSuite) TestAdd(c *gc.C) { s := set.NewStrings() s.Add("foo") s.Add("foo") s.Add("bar") AssertValues(c, s, "foo", "bar") } func (stringSetSuite) TestRemove(c *gc.C) { s := set.NewStrings("foo", "bar") s.Remove("foo") AssertValues(c, s, "bar") } func (stringSetSuite) TestContains(c *gc.C) { s := set.NewStrings("foo", "bar") c.Assert(s.Contains("foo"), gc.Equals, true) c.Assert(s.Contains("bar"), gc.Equals, true) c.Assert(s.Contains("baz"), gc.Equals, false) } func (stringSetSuite) TestRemoveNonExistent(c *gc.C) { s := set.NewStrings() s.Remove("foo") AssertValues(c, s) } func (stringSetSuite) TestUnion(c *gc.C) { s1 := set.NewStrings("foo", "bar") s2 := set.NewStrings("foo", "baz", "bang") union1 := s1.Union(s2) union2 := s2.Union(s1) AssertValues(c, union1, "foo", "bar", "baz", "bang") AssertValues(c, union2, "foo", "bar", "baz", "bang") } func (stringSetSuite) TestIntersection(c *gc.C) { s1 := set.NewStrings("foo", "bar") s2 := set.NewStrings("foo", "baz", "bang") int1 := s1.Intersection(s2) int2 := s2.Intersection(s1) AssertValues(c, int1, "foo") AssertValues(c, int2, "foo") } func (stringSetSuite) TestDifference(c *gc.C) { s1 := set.NewStrings("foo", "bar") s2 := set.NewStrings("foo", "baz", "bang") diff1 := s1.Difference(s2) diff2 := s2.Difference(s1) AssertValues(c, diff1, "bar") AssertValues(c, diff2, "baz", "bang") } func (stringSetSuite) TestUninitialized(c *gc.C) { var uninitialized set.Strings c.Assert(uninitialized.Size(), gc.Equals, 0) c.Assert(uninitialized.IsEmpty(), gc.Equals, true) // You can get values and sorted values from an unitialized set. AssertValues(c, uninitialized) // All contains checks are false c.Assert(uninitialized.Contains("foo"), gc.Equals, false) // Remove works on an uninitialized Strings uninitialized.Remove("foo") var other set.Strings // Union returns a new set that is empty but initialized. c.Assert(uninitialized.Union(other), gc.DeepEquals, set.NewStrings()) c.Assert(uninitialized.Intersection(other), gc.DeepEquals, set.NewStrings()) c.Assert(uninitialized.Difference(other), gc.DeepEquals, set.NewStrings()) other = set.NewStrings("foo", "bar") c.Assert(uninitialized.Union(other), gc.DeepEquals, other) c.Assert(uninitialized.Intersection(other), gc.DeepEquals, set.NewStrings()) c.Assert(uninitialized.Difference(other), gc.DeepEquals, set.NewStrings()) c.Assert(other.Union(uninitialized), gc.DeepEquals, other) c.Assert(other.Intersection(uninitialized), gc.DeepEquals, set.NewStrings()) c.Assert(other.Difference(uninitialized), gc.DeepEquals, other) // Once something is added, the set becomes initialized. uninitialized.Add("foo") AssertValues(c, uninitialized, "foo") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/set/strings.go�����������������������������������0000644�0000153�0000161�00000005700�12321735642�024642� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package set import ( "sort" ) // Strings represents the classic "set" data structure, and contains // strings. type Strings struct { values map[string]bool } // NewStrings creates and initializes a Strings and populates it with // initial values as specified in the parameters. func NewStrings(initial ...string) Strings { result := Strings{values: make(map[string]bool)} for _, value := range initial { result.Add(value) } return result } // Size returns the number of elements in the set. func (s Strings) Size() int { return len(s.values) } // IsEmpty is true for empty or uninitialized sets. func (s Strings) IsEmpty() bool { return len(s.values) == 0 } // Add puts a value into the set. func (s *Strings) Add(value string) { if s.values == nil { s.values = make(map[string]bool) } s.values[value] = true } // Remove takes a value out of the set. If value wasn't in the set to start // with, this method silently succeeds. func (s *Strings) Remove(value string) { delete(s.values, value) } // Contains returns true if the value is in the set, and false otherwise. func (s Strings) Contains(value string) bool { _, exists := s.values[value] return exists } // Values returns an unordered slice containing all the values in the set. func (s Strings) Values() []string { result := make([]string, len(s.values)) i := 0 for key := range s.values { result[i] = key i++ } return result } // SortedValues returns an ordered slice containing all the values in the set. func (s Strings) SortedValues() []string { values := s.Values() sort.Strings(values) return values } // Union returns a new Strings representing a union of the elments in the // method target and the parameter. func (s Strings) Union(other Strings) Strings { result := NewStrings() // Use the internal map rather than going through the friendlier functions // to avoid extra allocation of slices. for value := range s.values { result.values[value] = true } for value := range other.values { result.values[value] = true } return result } // Intersection returns a new Strings representing a intersection of the elments in the // method target and the parameter. func (s Strings) Intersection(other Strings) Strings { result := NewStrings() // Use the internal map rather than going through the friendlier functions // to avoid extra allocation of slices. for value := range s.values { if other.Contains(value) { result.values[value] = true } } return result } // Difference returns a new Strings representing all the values in the // target that are not in the parameter. func (s Strings) Difference(other Strings) Strings { result := NewStrings() // Use the internal map rather than going through the friendlier functions // to avoid extra allocation of slices. for value := range s.values { if !other.Contains(value) { result.values[value] = true } } return result } ����������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/password_test.go���������������������������������0000644�0000153�0000161�00000005010�12321735642�025251� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils_test import ( jc "github.com/juju/testing/checkers" gc "launchpad.net/gocheck" "launchpad.net/juju-core/utils" ) type passwordSuite struct{} var _ = gc.Suite(passwordSuite{}) // Base64 *can* include a tail of '=' characters, but all the tests here // explicitly *don't* want those because it is wasteful. var base64Chars = "^[A-Za-z0-9+/]+$" func (passwordSuite) TestRandomBytes(c *gc.C) { b, err := utils.RandomBytes(16) c.Assert(err, gc.IsNil) c.Assert(b, gc.HasLen, 16) x0 := b[0] for _, x := range b { if x != x0 { return } } c.Errorf("all same bytes in result of RandomBytes") } func (passwordSuite) TestRandomPassword(c *gc.C) { p, err := utils.RandomPassword() c.Assert(err, gc.IsNil) if len(p) < 18 { c.Errorf("password too short: %q", p) } c.Assert(p, gc.Matches, base64Chars) } func (passwordSuite) TestRandomSalt(c *gc.C) { salt, err := utils.RandomSalt() c.Assert(err, gc.IsNil) if len(salt) < 12 { c.Errorf("salt too short: %q", salt) } // check we're not adding base64 padding. c.Assert(salt, gc.Matches, base64Chars) } var testPasswords = []string{"", "a", "a longer password than i would usually bother with"} var testSalts = []string{"abcd", "abcdefgh", "abcdefghijklmnop", utils.CompatSalt} func (passwordSuite) TestUserPasswordHash(c *gc.C) { seenHashes := make(map[string]bool) for i, password := range testPasswords { for j, salt := range testSalts { c.Logf("test %d, %d %s %s", i, j, password, salt) hashed := utils.UserPasswordHash(password, salt) c.Logf("hash %q", hashed) c.Assert(len(hashed), gc.Equals, 24) c.Assert(seenHashes[hashed], gc.Equals, false) // check we're not adding base64 padding. c.Assert(hashed, gc.Matches, base64Chars) seenHashes[hashed] = true // check it's deterministic altHashed := utils.UserPasswordHash(password, salt) c.Assert(altHashed, gc.Equals, hashed) } } } func (passwordSuite) TestAgentPasswordHash(c *gc.C) { seenValues := make(map[string]bool) for i := 0; i < 1000; i++ { password, err := utils.RandomPassword() c.Assert(err, gc.IsNil) c.Assert(seenValues[password], jc.IsFalse) seenValues[password] = true hashed := utils.AgentPasswordHash(password) c.Assert(hashed, gc.Not(gc.Equals), password) c.Assert(seenValues[hashed], jc.IsFalse) seenValues[hashed] = true c.Assert(len(hashed), gc.Equals, 24) // check we're not adding base64 padding. c.Assert(hashed, gc.Matches, base64Chars) } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/command_test.go����������������������������������0000644�0000153�0000161�00000002361�12321735642�025033� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2012, 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils_test import ( "io/ioutil" "path/filepath" gc "launchpad.net/gocheck" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) type EnvironmentPatcher interface { PatchEnvironment(name, value string) } func patchExecutable(patcher EnvironmentPatcher, dir, execName, script string) { patcher.PatchEnvironment("PATH", dir) filename := filepath.Join(dir, execName) ioutil.WriteFile(filename, []byte(script), 0755) } type commandSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&commandSuite{}) func (s *commandSuite) TestRunCommandCombinesOutput(c *gc.C) { content := `#!/bin/bash --norc echo stdout echo stderr 1>&2 ` patchExecutable(s, c.MkDir(), "test-output", content) output, err := utils.RunCommand("test-output") c.Assert(err, gc.IsNil) c.Assert(output, gc.Equals, "stdout\nstderr\n") } func (s *commandSuite) TestRunCommandNonZeroExit(c *gc.C) { content := `#!/bin/bash --norc echo stdout exit 42 ` patchExecutable(s, c.MkDir(), "test-output", content) output, err := utils.RunCommand("test-output") c.Assert(err, gc.ErrorMatches, `exit status 42`) c.Assert(output, gc.Equals, "stdout\n") } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/file_windows.go����������������������������������0000644�0000153�0000161�00000002335�12321735642�025050� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Copyright 2013 Canonical Ltd. // Licensed under the AGPLv3, see LICENCE file for details. package utils import ( "os" "syscall" ) const ( movefile_replace_existing = 0x1 movefile_write_through = 0x8 ) //sys moveFileEx(lpExistingFileName *uint16, lpNewFileName *uint16, dwFlags uint32) (err error) = MoveFileExW // ReplaceFile atomically replaces the destination file or directory with the source. // The errors that are returned are identical to those returned by os.Rename. func ReplaceFile(source, destination string) error { src, err := syscall.UTF16PtrFromString(source) if err != nil { return &os.LinkError{"replace", source, destination, err} } dest, err := syscall.UTF16PtrFromString(destination) if err != nil { return &os.LinkError{"replace", source, destination, err} } // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx if err := moveFileEx(src, dest, movefile_replace_existing|movefile_write_through); err != nil { return &os.LinkError{"replace", source, destination, err} } 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) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/utils/zfile_windows.go���������������������������������0000644�0000153�0000161�00000001155�12321735642�025241� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// mksyscall_windows.pl -l32 file_windows.go // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT package utils import "unsafe" import "syscall" var ( modkernel32 = syscall.NewLazyDLL("kernel32.dll") procMoveFileExW = modkernel32.NewProc("MoveFileExW") ) func moveFileEx(lpExistingFileName *uint16, lpNewFileName *uint16, dwFlags uint32) (err error) { r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, uintptr(unsafe.Pointer(lpExistingFileName)), uintptr(unsafe.Pointer(lpNewFileName)), uintptr(dwFlags)) if r1 == 0 { if e1 != 0 { err = error(e1) } else { err = syscall.EINVAL } } return } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/replicaset/��������������������������������������������0000755�0000153�0000161�00000000000�12321736000�023005� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/replicaset/replicaset.go�������������������������������0000644�0000153�0000161�00000025074�12321735776�025522� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package replicaset import ( "fmt" "io" "strings" "time" "github.com/juju/loggo" "labix.org/v2/mgo" "labix.org/v2/mgo/bson" ) // MaxPeers defines the maximum number of peers that mongo supports. const MaxPeers = 7 var logger = loggo.GetLogger("juju.replicaset") // Initiate sets up a replica set with the given replica set name with the // single given member. It need be called only once for a given mongo replica // set. // // Note that you must set DialWithInfo and set Direct = true when dialing into a // specific non-initiated mongo server. The session will be set to Monotonic // mode. // // See http://docs.mongodb.org/manual/reference/method/rs.initiate/ for more // details. func Initiate(session *mgo.Session, address, name string) error { session.SetMode(mgo.Monotonic, true) cfg := Config{ Name: name, Version: 1, Members: []Member{{Id: 1, Address: address}}, } return session.Run(bson.D{{"replSetInitiate", cfg}}, nil) } // Member holds configuration information for a replica set member. // // See http://docs.mongodb.org/manual/reference/replica-configuration/ // for more details type Member struct { // Id is a unique id for a member in a set. Id int `bson:"_id"` // Address holds the network address of the member, // in the form hostname:port. Address string `bson:"host"` // Arbiter holds whether the member is an arbiter only. // This value is optional; it defaults to false. Arbiter *bool `bson:"arbiterOnly,omitempty"` // BuildIndexes determines whether the mongod builds indexes on this member. // This value is optional; it defaults to true. BuildIndexes *bool `bson:"buildIndexes,omitempty"` // Hidden determines whether the replica set hides this member from // the output of IsMaster. // This value is optional; it defaults to false. Hidden *bool `bson:"hidden,omitempty"` // Priority determines eligibility of a member to become primary. // This value is optional; it defaults to 1. Priority *float64 `bson:"priority,omitempty"` // Tags store additional information about a replica member, often used for // customizing read preferences and write concern. Tags map[string]string `bson:"tags,omitempty"` // SlaveDelay describes the number of seconds behind the master that this // replica set member should lag rounded up to the nearest second. // This value is optional; it defaults to 0. SlaveDelay *time.Duration `bson:"slaveDelay,omitempty"` // Votes controls the number of votes a server has in a replica set election. // This value is optional; it defaults to 1. Votes *int `bson:"votes,omitempty"` } func fmtConfigForLog(config *Config) string { memberInfo := make([]string, len(config.Members)) for i, member := range config.Members { memberInfo[i] = fmt.Sprintf("Member{%d %q %v}", member.Id, member.Address, member.Tags) } return fmt.Sprintf("{Name: %s, Version: %d, Members: {%s}}", config.Name, config.Version, strings.Join(memberInfo, ", ")) } // applyRelSetConfig applies the new config to the mongo session. It also logs // what the changes are. It checks if the replica set changes cause the DB // connection to be dropped. If so, it Refreshes the session and tries to Ping // again. func applyRelSetConfig(cmd string, session *mgo.Session, oldconfig, newconfig *Config) error { logger.Debugf("%s() changing replica set\nfrom %s\n to %s", cmd, fmtConfigForLog(oldconfig), fmtConfigForLog(newconfig)) err := session.Run(bson.D{{"replSetReconfig", newconfig}}, nil) // We will only try to Ping 2 times for i := 0; i < 2; i++ { if err == io.EOF { // If the primary changes due to replSetReconfig, then all // current connections are dropped. // Refreshing should fix us up. logger.Debugf("got EOF while running %s(), calling session.Refresh()", cmd) session.Refresh() } else if err != nil { // For all errors that aren't EOF, return immediately return err } // err is either nil or EOF and we called Refresh, so Ping to // make sure we're actually connected err = session.Ping() // Change the command because it is the new command we ran cmd = "Ping" } return err } // Add adds the given members to the session's replica set. Duplicates of // existing replicas will be ignored. // // Members will have their Ids set automatically if they are not already > 0 func Add(session *mgo.Session, members ...Member) error { config, err := CurrentConfig(session) if err != nil { return err } oldconfig := *config config.Version++ max := 0 for _, member := range config.Members { if member.Id > max { max = member.Id } } outerLoop: for _, newMember := range members { for _, member := range config.Members { if member.Address == newMember.Address { // already exists, skip it continue outerLoop } } // let the caller specify an id if they want, treat zero as unspecified if newMember.Id < 1 { max++ newMember.Id = max } config.Members = append(config.Members, newMember) } return applyRelSetConfig("Add", session, &oldconfig, config) } // Remove removes members with the given addresses from the replica set. It is // not an error to remove addresses of non-existent replica set members. func Remove(session *mgo.Session, addrs ...string) error { config, err := CurrentConfig(session) if err != nil { return err } oldconfig := *config config.Version++ for _, rem := range addrs { for n, repl := range config.Members { if repl.Address == rem { config.Members = append(config.Members[:n], config.Members[n+1:]...) break } } } return applyRelSetConfig("Remove", session, &oldconfig, config) } // Set changes the current set of replica set members. Members will have their // ids set automatically if their ids are not already > 0. func Set(session *mgo.Session, members []Member) error { config, err := CurrentConfig(session) if err != nil { return err } // Copy the current configuration for logging oldconfig := *config config.Version++ // Assign ids to members that did not previously exist, starting above the // value of the highest id that already existed ids := map[string]int{} max := 0 for _, m := range config.Members { ids[m.Address] = m.Id if m.Id > max { max = m.Id } } for x, m := range members { if id, ok := ids[m.Address]; ok { m.Id = id } else if m.Id < 1 { max++ m.Id = max } members[x] = m } config.Members = members return applyRelSetConfig("Set", session, &oldconfig, config) } // Config reports information about the configuration of a given mongo node type IsMasterResults struct { // The following fields hold information about the specific mongodb node. IsMaster bool `bson:"ismaster"` Secondary bool `bson:"secondary"` Arbiter bool `bson:"arbiterOnly"` Address string `bson:"me"` LocalTime time.Time `bson:"localTime"` // The following fields hold information about the replica set. ReplicaSetName string `bson:"setName"` Addresses []string `bson:"hosts"` Arbiters []string `bson:"arbiters"` PrimaryAddress string `bson:"primary"` } // IsMaster returns information about the configuration of the node that // the given session is connected to. func IsMaster(session *mgo.Session) (*IsMasterResults, error) { results := &IsMasterResults{} err := session.Run("isMaster", results) if err != nil { return nil, err } return results, nil } // CurrentMembers returns the current members of the replica set. func CurrentMembers(session *mgo.Session) ([]Member, error) { cfg, err := CurrentConfig(session) if err != nil { return nil, err } return cfg.Members, nil } // CurrentConfig returns the Config for the given session's replica set. func CurrentConfig(session *mgo.Session) (*Config, error) { cfg := &Config{} err := session.DB("local").C("system.replset").Find(nil).One(cfg) if err != nil { return nil, fmt.Errorf("Error getting replset config : %s", err.Error()) } return cfg, nil } // Config is the document stored in mongodb that defines the servers in the // replica set type Config struct { Name string `bson:"_id"` Version int `bson:"version"` Members []Member `bson:"members"` } // CurrentStatus returns the status of the replica set for the given session. func CurrentStatus(session *mgo.Session) (*Status, error) { status := &Status{} err := session.Run("replSetGetStatus", status) if err != nil { return nil, fmt.Errorf("Error from replSetGetStatus: %v", err) } return status, nil } // Status holds data about the status of members of the replica set returned // from replSetGetStatus // // See http://docs.mongodb.org/manual/reference/command/replSetGetStatus/#dbcmd.replSetGetStatus type Status struct { Name string `bson:"set"` Members []MemberStatus `bson:"members"` } // Status holds the status of a replica set member returned from // replSetGetStatus. type MemberStatus struct { // Id holds the replica set id of the member that the status is describing. Id int `bson:"_id"` // Address holds address of the member that the status is describing. Address string `bson:"name"` // Self holds whether this is the status for the member that // the session is connected to. Self bool `bson:"self"` // ErrMsg holds the most recent error or status message received // from the member. ErrMsg string `bson:"errmsg"` // Healthy reports whether the member is up. It is true for the // member that the request was made to. Healthy bool `bson:"health"` // State describes the current state of the member. State MemberState `bson:"state"` // Uptime describes how long the member has been online. Uptime time.Duration `bson:"uptime"` // Ping describes the length of time a round-trip packet takes to travel // between the remote member and the local instance. It is zero for the // member that the session is connected to. Ping time.Duration `bson:"pingMS"` } // MemberState represents the state of a replica set member. // See http://docs.mongodb.org/manual/reference/replica-states/ type MemberState int const ( StartupState = iota PrimaryState SecondaryState RecoveringState FatalState Startup2State UnknownState ArbiterState DownState RollbackState ShunnedState ) var memberStateStrings = []string{ StartupState: "STARTUP", PrimaryState: "PRIMARY", SecondaryState: "SECONDARY", RecoveringState: "RECOVERING", FatalState: "FATAL", Startup2State: "STARTUP2", UnknownState: "UNKNOWN", ArbiterState: "ARBITER", DownState: "DOWN", RollbackState: "ROLLBACK", ShunnedState: "SHUNNED", } // String returns a string describing the state. func (state MemberState) String() string { if state < 0 || int(state) >= len(memberStateStrings) { return "INVALID_MEMBER_STATE" } return memberStateStrings[state] } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/juju-core/replicaset/replicaset_test.go��������������������������0000644�0000153�0000161�00000025445�12321735776�026563� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package replicaset import ( "fmt" "testing" "time" "labix.org/v2/mgo" gc "launchpad.net/gocheck" coretesting "launchpad.net/juju-core/testing" "launchpad.net/juju-core/testing/testbase" "launchpad.net/juju-core/utils" ) var ( name = "juju" root *coretesting.MgoInstance ) func TestPackage(t *testing.T) { gc.TestingT(t) } func newServer() (*coretesting.MgoInstance, error) { inst := &coretesting.MgoInstance{Params: []string{"--replSet", name}} err := inst.Start(true) if err != nil { return nil, fmt.Errorf("Error starting mongo server: %s", err.Error()) } // by dialing right now, we'll wait until it's running strategy := utils.AttemptStrategy{Total: time.Second * 5, Delay: time.Millisecond * 100} attempt := strategy.Start() for attempt.Next() { var session *mgo.Session session, err = inst.DialDirect() if err != nil { err = fmt.Errorf("Error dialing mongo server %q: %s", inst.Addr(), err.Error()) } else { session.SetMode(mgo.Monotonic, true) err = session.Ping() if err != nil { err = fmt.Errorf("Error pinging mongo server %q: %s", inst.Addr(), err.Error()) } session.Close() } if err == nil || !attempt.HasNext() { break } } return inst, err } type MongoSuite struct { testbase.LoggingSuite } var _ = gc.Suite(&MongoSuite{}) func (s *MongoSuite) SetUpSuite(c *gc.C) { s.LoggingSuite.SetUpSuite(c) var err error // do all this stuff here, since we don't want to have to redo it for each test root, err = newServer() if err != nil { c.Fatalf("Got error from Start of root server: %s", err.Error()) } // note, this is an actual test around Initiate, but again, I don't want to // have to redo it, so I just do it once. dialAndTestInitiate(c) } func (s *MongoSuite) TearDownTest(c *gc.C) { s.LoggingSuite.TearDownTest(c) // remove all secondaries from the replicaset on test teardown session, err := root.DialDirect() if err != nil { c.Logf("Failed to dial root during test cleanup: %v", err) return } defer session.Close() mems, err := CurrentMembers(session) if err != nil { c.Logf("Failed to get list of memners during test cleanup: %v", err) return } addrs := []string{} for _, m := range mems { if root.Addr() != m.Address { addrs = append(addrs, m.Address) } } if err = Remove(session, addrs...); err != nil { c.Logf("Error removing secondaries: %v", err) } } func dialAndTestInitiate(c *gc.C) { session := root.MustDialDirect() defer session.Close() err := Initiate(session, root.Addr(), name) c.Assert(err, gc.IsNil) // Ids start at 1 for us, so we can differentiate between set and unset expectedMembers := []Member{Member{Id: 1, Address: root.Addr()}} // need to set mode to strong so that we wait for the write to succeed // before reading and thus ensure that we're getting consistent reads. session.SetMode(mgo.Strong, false) mems, err := CurrentMembers(session) c.Assert(err, gc.IsNil) c.Assert(mems, gc.DeepEquals, expectedMembers) // now add some data so we get a more real-life test loadData(session, c) } func loadData(session *mgo.Session, c *gc.C) { type foo struct { Name string Address string Count int } for col := 0; col < 10; col++ { foos := make([]foo, 10000) for n := range foos { foos[n] = foo{ Name: fmt.Sprintf("name_%d_%d", col, n), Address: fmt.Sprintf("address_%d_%d", col, n), Count: n * (col + 1), } } err := session.DB("testing").C(fmt.Sprintf("data%d", col)).Insert(foos) c.Assert(err, gc.IsNil) } } func (s *MongoSuite) TearDownSuite(c *gc.C) { s.LoggingSuite.TearDownSuite(c) root.Destroy() } func (s *MongoSuite) TestAddRemoveSet(c *gc.C) { session := root.MustDial() defer session.Close() members := make([]Member, 0, 5) // Add should be idempotent, so re-adding root here shouldn't result in // two copies of root in the replica set members = append(members, Member{Address: root.Addr()}) instances := make([]*coretesting.MgoInstance, 0, 5) instances = append(instances, root) for x := 0; x < 4; x++ { inst, err := newServer() c.Assert(err, gc.IsNil) instances = append(instances, inst) defer inst.Destroy() defer Remove(session, inst.Addr()) key := fmt.Sprintf("key%d", x) val := fmt.Sprintf("val%d", x) tags := map[string]string{key: val} members = append(members, Member{Address: inst.Addr(), Tags: tags}) } var err error // We use a delay of 31s. Our Mongo Dial timeout is 15s, so this gives // us 2 attempts before we give up. strategy := utils.AttemptStrategy{Total: time.Second * 31, Delay: time.Millisecond * 100} start := time.Now() attemptCount := 0 attempt := strategy.Start() for attempt.Next() { attemptCount += 1 err = Add(session, members...) if err == nil || !attempt.HasNext() { break } c.Logf("attempting to Add got error: %v", err) } c.Logf("Add() %d attempts in %s", attemptCount, time.Since(start)) c.Assert(err, gc.IsNil) expectedMembers := make([]Member, len(members)) for x, m := range members { // Ids should start at 1 (for the root) and go up m.Id = x + 1 expectedMembers[x] = m } var cfg *Config start = time.Now() attemptCount = 0 attempt = strategy.Start() for attempt.Next() { attemptCount += 1 cfg, err = CurrentConfig(session) if err == nil || !attempt.HasNext() { break } c.Logf("attempting CurrentConfig got error: %v", err) } c.Logf("CurrentConfig() %d attempts in %s", attemptCount, time.Since(start)) c.Assert(err, gc.IsNil) c.Assert(cfg.Name, gc.Equals, name) // 2 since we already changed it once c.Assert(cfg.Version, gc.Equals, 2) mems := cfg.Members c.Assert(mems, gc.DeepEquals, expectedMembers) // Now remove the last two Members start = time.Now() attemptCount = 0 attempt = strategy.Start() for attempt.Next() { attemptCount += 1 err = Remove(session, members[3].Address, members[4].Address) if err == nil || !attempt.HasNext() { break } c.Logf("attempting Remove got error: %v", err) } c.Logf("Remove() %d attempts in %s", attemptCount, time.Since(start)) c.Assert(err, gc.IsNil) expectedMembers = expectedMembers[0:3] start = time.Now() attemptCount = 0 attempt = strategy.Start() for attempt.Next() { attemptCount += 1 mems, err = CurrentMembers(session) if err == nil || !attempt.HasNext() { break } c.Logf("attempting CurrentMembers got error: %v", err) } c.Logf("CurrentMembers() %d attempts in %s", attemptCount, time.Since(start)) c.Assert(err, gc.IsNil) c.Assert(mems, gc.DeepEquals, expectedMembers) // now let's mix it up and set the new members to a mix of the previous // plus the new arbiter mems = []Member{members[3], mems[2], mems[0], members[4]} start = time.Now() attemptCount = 0 attempt = strategy.Start() for attempt.Next() { attemptCount += 1 err = Set(session, mems) if err == nil || !attempt.HasNext() { break } c.Logf("attempting Set got error: %v", err) } c.Logf("Set() %d attempts in %s", attemptCount, time.Since(start)) c.Assert(err, gc.IsNil) start = time.Now() attemptCount = 0 attempt = strategy.Start() for attempt.Next() { attemptCount += 1 // can dial whichever replica address here, mongo will figure it out session = instances[0].MustDialDirect() err = session.Ping() if err == nil || !attempt.HasNext() { break } c.Logf("attempting session.Ping() got error: %v after %s", err, time.Since(start)) } c.Logf("session.Ping() %d attempts in %s", attemptCount, time.Since(start)) c.Assert(err, gc.IsNil) expectedMembers = []Member{members[3], expectedMembers[2], expectedMembers[0], members[4]} // any new members will get an id of max(other_ids...)+1 expectedMembers[0].Id = 4 expectedMembers[3].Id = 5 start = time.Now() attemptCount = 0 attempt = strategy.Start() for attempt.Next() { attemptCount += 1 mems, err = CurrentMembers(session) if err == nil || !attempt.HasNext() { break } c.Logf("attempting CurrentMembers() got error: %v", err) } c.Assert(err, gc.IsNil) c.Logf("CurrentMembers() %d attempts in %s", attemptCount, time.Since(start)) c.Assert(mems, gc.DeepEquals, expectedMembers) } func (s *MongoSuite) TestIsMaster(c *gc.C) { session := root.MustDial() defer session.Close() expected := IsMasterResults{ // The following fields hold information about the specific mongodb node. IsMaster: true, Secondary: false, Arbiter: false, Address: root.Addr(), LocalTime: time.Time{}, // The following fields hold information about the replica set. ReplicaSetName: name, Addresses: []string{root.Addr()}, Arbiters: nil, PrimaryAddress: root.Addr(), } res, err := IsMaster(session) c.Assert(err, gc.IsNil) c.Check(closeEnough(res.LocalTime, time.Now()), gc.Equals, true) res.LocalTime = time.Time{} c.Check(*res, gc.DeepEquals, expected) } func (s *MongoSuite) TestCurrentStatus(c *gc.C) { session := root.MustDial() defer session.Close() inst1, err := newServer() c.Assert(err, gc.IsNil) defer inst1.Destroy() defer Remove(session, inst1.Addr()) inst2, err := newServer() c.Assert(err, gc.IsNil) defer inst2.Destroy() defer Remove(session, inst2.Addr()) strategy := utils.AttemptStrategy{Total: time.Second * 31, Delay: time.Millisecond * 100} attempt := strategy.Start() for attempt.Next() { err = Add(session, Member{Address: inst1.Addr()}, Member{Address: inst2.Addr()}) if err == nil || !attempt.HasNext() { break } } c.Assert(err, gc.IsNil) expected := &Status{ Name: name, Members: []MemberStatus{{ Id: 1, Address: root.Addr(), Self: true, ErrMsg: "", Healthy: true, State: PrimaryState, }, { Id: 2, Address: inst1.Addr(), Self: false, ErrMsg: "", Healthy: true, State: SecondaryState, }, { Id: 3, Address: inst2.Addr(), Self: false, ErrMsg: "", Healthy: true, State: SecondaryState, }}, } strategy.Total = time.Second * 90 attempt = strategy.Start() var res *Status for attempt.Next() { var err error res, err = CurrentStatus(session) if err != nil { if !attempt.HasNext() { c.Errorf("Couldn't get status before timeout, got err: %v", err) return } else { // try again continue } } if res.Members[0].State == PrimaryState && res.Members[1].State == SecondaryState && res.Members[2].State == SecondaryState { break } if !attempt.HasNext() { c.Errorf("Servers did not get into final state before timeout. Status: %#v", res) return } } for x, _ := range res.Members { // non-empty uptime and ping c.Check(res.Members[x].Uptime, gc.Not(gc.Equals), 0) // ping is always going to be zero since we're on localhost // so we can't really test it right now // now overwrite Uptime so it won't throw off DeepEquals res.Members[x].Uptime = 0 } c.Check(res, gc.DeepEquals, expected) } func closeEnough(expected, obtained time.Time) bool { t := obtained.Sub(expected) return (-500*time.Millisecond) < t && t < (500*time.Millisecond) } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/����������������������������������������������������������0000755�0000153�0000161�00000000000�12321736015�020245� 5����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/emitterc.go�����������������������������������������������0000644�0000153�0000161�00000130311�12321735675�022422� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml import ( "bytes" ) // Flush the buffer if needed. func flush(emitter *yaml_emitter_t) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) { return yaml_emitter_flush(emitter) } return true } // Put a character to the output buffer. func put(emitter *yaml_emitter_t, value byte) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { return false } emitter.buffer[emitter.buffer_pos] = value emitter.buffer_pos++ emitter.column++ return true } // Put a line break to the output buffer. func put_break(emitter *yaml_emitter_t) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { return false } switch emitter.line_break { case yaml_CR_BREAK: emitter.buffer[emitter.buffer_pos] = '\r' emitter.buffer_pos += 1 case yaml_LN_BREAK: emitter.buffer[emitter.buffer_pos] = '\n' emitter.buffer_pos += 1 case yaml_CRLN_BREAK: emitter.buffer[emitter.buffer_pos+0] = '\r' emitter.buffer[emitter.buffer_pos+1] = '\n' emitter.buffer_pos += 2 default: panic("unknown line break setting") } emitter.column = 0 emitter.line++ return true } // Copy a character from a string into buffer. func write(emitter *yaml_emitter_t, s []byte, i *int) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { return false } p := emitter.buffer_pos w := width(s[*i]) switch w { case 4: emitter.buffer[p+3] = s[*i+3] fallthrough case 3: emitter.buffer[p+2] = s[*i+2] fallthrough case 2: emitter.buffer[p+1] = s[*i+1] fallthrough case 1: emitter.buffer[p+0] = s[*i+0] default: panic("unknown character width") } emitter.column++ emitter.buffer_pos += w *i += w return true } // Write a whole string into buffer. func write_all(emitter *yaml_emitter_t, s []byte) bool { for i := 0; i < len(s); { if !write(emitter, s, &i) { return false } } return true } // Copy a line break character from a string into buffer. func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { if s[*i] == '\n' { if !put_break(emitter) { return false } *i++ } else { if !write(emitter, s, i) { return false } emitter.column = 0 emitter.line++ } return true } // Set an emitter error and return false. func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool { emitter.error = yaml_EMITTER_ERROR emitter.problem = problem return false } // Emit an event. func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { emitter.events = append(emitter.events, *event) for !yaml_emitter_need_more_events(emitter) { event := &emitter.events[emitter.events_head] if !yaml_emitter_analyze_event(emitter, event) { return false } if !yaml_emitter_state_machine(emitter, event) { return false } yaml_event_delete(event) emitter.events_head++ } return true } // Check if we need to accumulate more events before emitting. // // We accumulate extra // - 1 event for DOCUMENT-START // - 2 events for SEQUENCE-START // - 3 events for MAPPING-START // func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { if emitter.events_head == len(emitter.events) { return true } var accumulate int switch emitter.events[emitter.events_head].typ { case yaml_DOCUMENT_START_EVENT: accumulate = 1 break case yaml_SEQUENCE_START_EVENT: accumulate = 2 break case yaml_MAPPING_START_EVENT: accumulate = 3 break default: return false } if len(emitter.events)-emitter.events_head > accumulate { return false } var level int for i := emitter.events_head; i < len(emitter.events); i++ { switch emitter.events[i].typ { case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT: level++ case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT: level-- } if level == 0 { return false } } return true } // Append a directive to the directives stack. func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool { for i := 0; i < len(emitter.tag_directives); i++ { if bytes.Equal(value.handle, emitter.tag_directives[i].handle) { if allow_duplicates { return true } return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive") } } // [Go] Do we actually need to copy this given garbage collection // and the lack of deallocating destructors? tag_copy := yaml_tag_directive_t{ handle: make([]byte, len(value.handle)), prefix: make([]byte, len(value.prefix)), } copy(tag_copy.handle, value.handle) copy(tag_copy.prefix, value.prefix) emitter.tag_directives = append(emitter.tag_directives, tag_copy) return true } // Increase the indentation level. func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { emitter.indents = append(emitter.indents, emitter.indent) if emitter.indent < 0 { if flow { emitter.indent = emitter.best_indent } else { emitter.indent = 0 } } else if !indentless { emitter.indent += emitter.best_indent } return true } // State dispatcher. func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool { switch emitter.state { default: case yaml_EMIT_STREAM_START_STATE: return yaml_emitter_emit_stream_start(emitter, event) case yaml_EMIT_FIRST_DOCUMENT_START_STATE: return yaml_emitter_emit_document_start(emitter, event, true) case yaml_EMIT_DOCUMENT_START_STATE: return yaml_emitter_emit_document_start(emitter, event, false) case yaml_EMIT_DOCUMENT_CONTENT_STATE: return yaml_emitter_emit_document_content(emitter, event) case yaml_EMIT_DOCUMENT_END_STATE: return yaml_emitter_emit_document_end(emitter, event) case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: return yaml_emitter_emit_flow_sequence_item(emitter, event, true) case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: return yaml_emitter_emit_flow_sequence_item(emitter, event, false) case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: return yaml_emitter_emit_flow_mapping_key(emitter, event, true) case yaml_EMIT_FLOW_MAPPING_KEY_STATE: return yaml_emitter_emit_flow_mapping_key(emitter, event, false) case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: return yaml_emitter_emit_flow_mapping_value(emitter, event, true) case yaml_EMIT_FLOW_MAPPING_VALUE_STATE: return yaml_emitter_emit_flow_mapping_value(emitter, event, false) case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: return yaml_emitter_emit_block_sequence_item(emitter, event, true) case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE: return yaml_emitter_emit_block_sequence_item(emitter, event, false) case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: return yaml_emitter_emit_block_mapping_key(emitter, event, true) case yaml_EMIT_BLOCK_MAPPING_KEY_STATE: return yaml_emitter_emit_block_mapping_key(emitter, event, false) case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: return yaml_emitter_emit_block_mapping_value(emitter, event, true) case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE: return yaml_emitter_emit_block_mapping_value(emitter, event, false) case yaml_EMIT_END_STATE: return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END") } panic("invalid emitter state") } // Expect STREAM-START. func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { if event.typ != yaml_STREAM_START_EVENT { return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START") } if emitter.encoding == yaml_ANY_ENCODING { emitter.encoding = event.encoding if emitter.encoding == yaml_ANY_ENCODING { emitter.encoding = yaml_UTF8_ENCODING } } if emitter.best_indent < 2 || emitter.best_indent > 9 { emitter.best_indent = 2 } if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 { emitter.best_width = 80 } if emitter.best_width < 0 { emitter.best_width = 1<<31 - 1 } if emitter.line_break == yaml_ANY_BREAK { emitter.line_break = yaml_LN_BREAK } emitter.indent = -1 emitter.line = 0 emitter.column = 0 emitter.whitespace = true emitter.indention = true if emitter.encoding != yaml_UTF8_ENCODING { if !yaml_emitter_write_bom(emitter) { return false } } emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE return true } // Expect DOCUMENT-START or STREAM-END. func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if event.typ == yaml_DOCUMENT_START_EVENT { if event.version_directive != nil { if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) { return false } } for i := 0; i < len(event.tag_directives); i++ { tag_directive := &event.tag_directives[i] if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) { return false } if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) { return false } } for i := 0; i < len(default_tag_directives); i++ { tag_directive := &default_tag_directives[i] if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) { return false } } implicit := event.implicit if !first || emitter.canonical { implicit = false } if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) { if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if event.version_directive != nil { implicit = false if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) { return false } if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if len(event.tag_directives) > 0 { implicit = false for i := 0; i < len(event.tag_directives); i++ { tag_directive := &event.tag_directives[i] if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) { return false } if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) { return false } if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) { return false } if !yaml_emitter_write_indent(emitter) { return false } } } if yaml_emitter_check_empty_document(emitter) { implicit = false } if !implicit { if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { return false } if emitter.canonical { if !yaml_emitter_write_indent(emitter) { return false } } } emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE return true } if event.typ == yaml_STREAM_END_EVENT { if emitter.open_ended { if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_flush(emitter) { return false } emitter.state = yaml_EMIT_END_STATE return true } return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") } // Expect the root node. func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) return yaml_emitter_emit_node(emitter, event, true, false, false, false) } // Expect DOCUMENT-END. func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool { if event.typ != yaml_DOCUMENT_END_EVENT { return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") } if !yaml_emitter_write_indent(emitter) { return false } if !event.implicit { // [Go] Allocate the slice elsewhere. if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_flush(emitter) { return false } emitter.state = yaml_EMIT_DOCUMENT_START_STATE emitter.tag_directives = emitter.tag_directives[:0] return true } // Expect a flow item node. func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { return false } if !yaml_emitter_increase_indent(emitter, true, false) { return false } emitter.flow_level++ } if event.typ == yaml_SEQUENCE_END_EVENT { emitter.flow_level-- emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] if emitter.canonical && !first { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { return false } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !first { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } } emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) return yaml_emitter_emit_node(emitter, event, false, true, false, false) } // Expect a flow key node. func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { return false } if !yaml_emitter_increase_indent(emitter, true, false) { return false } emitter.flow_level++ } if event.typ == yaml_MAPPING_END_EVENT { emitter.flow_level-- emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] if emitter.canonical && !first { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { return false } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !first { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } } if !emitter.canonical && yaml_emitter_check_simple_key(emitter) { emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, true) } if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) { return false } emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, false) } // Expect a flow value node. func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { if simple { if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { return false } } else { if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) { return false } } emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, false) } // Expect a block item node. func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) { return false } } if event.typ == yaml_SEQUENCE_END_EVENT { emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) { return false } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) return yaml_emitter_emit_node(emitter, event, false, true, false, false) } // Expect a block key node. func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { if !yaml_emitter_increase_indent(emitter, false, false) { return false } } if event.typ == yaml_MAPPING_END_EVENT { emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !yaml_emitter_write_indent(emitter) { return false } if yaml_emitter_check_simple_key(emitter) { emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, true) } if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) { return false } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, false) } // Expect a block value node. func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { if simple { if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { return false } } else { if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) { return false } } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, false) } // Expect a node. func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, root bool, sequence bool, mapping bool, simple_key bool) bool { emitter.root_context = root emitter.sequence_context = sequence emitter.mapping_context = mapping emitter.simple_key_context = simple_key switch event.typ { case yaml_ALIAS_EVENT: return yaml_emitter_emit_alias(emitter, event) case yaml_SCALAR_EVENT: return yaml_emitter_emit_scalar(emitter, event) case yaml_SEQUENCE_START_EVENT: return yaml_emitter_emit_sequence_start(emitter, event) case yaml_MAPPING_START_EVENT: return yaml_emitter_emit_mapping_start(emitter, event) default: return yaml_emitter_set_emitter_error(emitter, "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS") } return false } // Expect ALIAS. func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_process_anchor(emitter) { return false } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } // Expect SCALAR. func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_select_scalar_style(emitter, event) { return false } if !yaml_emitter_process_anchor(emitter) { return false } if !yaml_emitter_process_tag(emitter) { return false } if !yaml_emitter_increase_indent(emitter, true, false) { return false } if !yaml_emitter_process_scalar(emitter) { return false } emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } // Expect SEQUENCE-START. func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_process_anchor(emitter) { return false } if !yaml_emitter_process_tag(emitter) { return false } if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || yaml_emitter_check_empty_sequence(emitter) { emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE } else { emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE } return true } // Expect MAPPING-START. func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_process_anchor(emitter) { return false } if !yaml_emitter_process_tag(emitter) { return false } if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE || yaml_emitter_check_empty_mapping(emitter) { emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE } else { emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE } return true } // Check if the document content is an empty scalar. func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool { return false // [Go] Huh? } // Check if the next events represent an empty sequence. func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool { if len(emitter.events)-emitter.events_head < 2 { return false } return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT && emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT } // Check if the next events represent an empty mapping. func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool { if len(emitter.events)-emitter.events_head < 2 { return false } return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT && emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT } // Check if the next node can be expressed as a simple key. func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool { length := 0 switch emitter.events[emitter.events_head].typ { case yaml_ALIAS_EVENT: length += len(emitter.anchor_data.anchor) case yaml_SCALAR_EVENT: if emitter.scalar_data.multiline { return false } length += len(emitter.anchor_data.anchor) + len(emitter.tag_data.handle) + len(emitter.tag_data.suffix) + len(emitter.scalar_data.value) case yaml_SEQUENCE_START_EVENT: if !yaml_emitter_check_empty_sequence(emitter) { return false } length += len(emitter.anchor_data.anchor) + len(emitter.tag_data.handle) + len(emitter.tag_data.suffix) case yaml_MAPPING_START_EVENT: if !yaml_emitter_check_empty_mapping(emitter) { return false } length += len(emitter.anchor_data.anchor) + len(emitter.tag_data.handle) + len(emitter.tag_data.suffix) default: return false } return length <= 128 } // Determine an acceptable scalar style. func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool { no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 if no_tag && !event.implicit && !event.quoted_implicit { return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified") } style := event.scalar_style() if style == yaml_ANY_SCALAR_STYLE { style = yaml_PLAIN_SCALAR_STYLE } if emitter.canonical { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } if emitter.simple_key_context && emitter.scalar_data.multiline { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } if style == yaml_PLAIN_SCALAR_STYLE { if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed || emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed { style = yaml_SINGLE_QUOTED_SCALAR_STYLE } if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) { style = yaml_SINGLE_QUOTED_SCALAR_STYLE } if no_tag && !event.implicit { style = yaml_SINGLE_QUOTED_SCALAR_STYLE } } if style == yaml_SINGLE_QUOTED_SCALAR_STYLE { if !emitter.scalar_data.single_quoted_allowed { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } } if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE { if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } } if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE { emitter.tag_data.handle = []byte{'!'} } emitter.scalar_data.style = style return true } // Write an achor. func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool { if emitter.anchor_data.anchor == nil { return true } c := []byte{'&'} if emitter.anchor_data.alias { c[0] = '*' } if !yaml_emitter_write_indicator(emitter, c, true, false, false) { return false } return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor) } // Write a tag. func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool { if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 { return true } if len(emitter.tag_data.handle) > 0 { if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) { return false } if len(emitter.tag_data.suffix) > 0 { if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { return false } } } else { // [Go] Allocate these slices elsewhere. if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) { return false } if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { return false } if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) { return false } } return true } // Write a scalar. func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { switch emitter.scalar_data.style { case yaml_PLAIN_SCALAR_STYLE: return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) case yaml_SINGLE_QUOTED_SCALAR_STYLE: return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) case yaml_DOUBLE_QUOTED_SCALAR_STYLE: return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) case yaml_LITERAL_SCALAR_STYLE: return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value) case yaml_FOLDED_SCALAR_STYLE: return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value) } panic("unknown scalar style") } // Check if a %YAML directive is valid. func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { if version_directive.major != 1 || version_directive.minor != 1 { return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive") } return true } // Check if a %TAG directive is valid. func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool { handle := tag_directive.handle prefix := tag_directive.prefix if len(handle) == 0 { return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty") } if handle[0] != '!' { return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'") } if handle[len(handle)-1] != '!' { return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'") } for i := 1; i < len(handle)-1; i += width(handle[i]) { if !is_alpha(handle, i) { return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only") } } if len(prefix) == 0 { return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty") } return true } // Check if an anchor is valid. func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool { if len(anchor) == 0 { problem := "anchor value must not be empty" if alias { problem = "alias value must not be empty" } return yaml_emitter_set_emitter_error(emitter, problem) } for i := 0; i < len(anchor); i += width(anchor[i]) { if !is_alpha(anchor, i) { problem := "anchor value must contain alphanumerical characters only" if alias { problem = "alias value must contain alphanumerical characters only" } return yaml_emitter_set_emitter_error(emitter, problem) } } emitter.anchor_data.anchor = anchor emitter.anchor_data.alias = alias return true } // Check if a tag is valid. func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { if len(tag) == 0 { return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty") } for i := 0; i < len(emitter.tag_directives); i++ { tag_directive := &emitter.tag_directives[i] if bytes.HasPrefix(tag, tag_directive.prefix) { emitter.tag_data.handle = tag_directive.handle emitter.tag_data.suffix = tag[len(tag_directive.prefix):] } return true } emitter.tag_data.suffix = tag return true } // Check if a scalar is valid. func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { var ( block_indicators = false flow_indicators = false line_breaks = false special_characters = false leading_space = false leading_break = false trailing_space = false trailing_break = false break_space = false space_break = false preceeded_by_whitespace = false followed_by_whitespace = false previous_space = false previous_break = false ) emitter.scalar_data.value = value if len(value) == 0 { emitter.scalar_data.multiline = false emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = true emitter.scalar_data.single_quoted_allowed = true emitter.scalar_data.block_allowed = false return true } if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) { block_indicators = true flow_indicators = true } preceeded_by_whitespace = true for i, w := 0, 0; i < len(value); i += w { w = width(value[0]) followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) if i == 0 { switch value[i] { case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`': flow_indicators = true block_indicators = true case '?', ':': flow_indicators = true if followed_by_whitespace { block_indicators = true } case '-': if followed_by_whitespace { flow_indicators = true block_indicators = true } } } else { switch value[i] { case ',', '?', '[', ']', '{', '}': flow_indicators = true case ':': flow_indicators = true if followed_by_whitespace { block_indicators = true } case '#': if preceeded_by_whitespace { flow_indicators = true block_indicators = true } } } if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { special_characters = true } if is_space(value, i) { if i == 0 { leading_space = true } if i+width(value[i]) == len(value) { trailing_space = true } if previous_break { break_space = true } previous_space = true previous_break = false } else if is_break(value, i) { line_breaks = true if i == 0 { leading_break = true } if i+width(value[i]) == len(value) { trailing_break = true } if previous_space { space_break = true } previous_space = false previous_break = true } else { previous_space = false previous_break = false } // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. preceeded_by_whitespace = is_blankz(value, i) } emitter.scalar_data.multiline = line_breaks emitter.scalar_data.flow_plain_allowed = true emitter.scalar_data.block_plain_allowed = true emitter.scalar_data.single_quoted_allowed = true emitter.scalar_data.block_allowed = true if leading_space || leading_break || trailing_space || trailing_break { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false } if trailing_space { emitter.scalar_data.block_allowed = false } if break_space { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false emitter.scalar_data.single_quoted_allowed = false } if space_break || special_characters { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false emitter.scalar_data.single_quoted_allowed = false emitter.scalar_data.block_allowed = false } if line_breaks { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false } if flow_indicators { emitter.scalar_data.flow_plain_allowed = false } if block_indicators { emitter.scalar_data.block_plain_allowed = false } return true } // Check if the event data is valid. func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { emitter.anchor_data.anchor = nil emitter.tag_data.handle = nil emitter.tag_data.suffix = nil emitter.scalar_data.value = nil switch event.typ { case yaml_ALIAS_EVENT: if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { return false } case yaml_SCALAR_EVENT: if len(event.anchor) > 0 { if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { return false } } if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) { if !yaml_emitter_analyze_tag(emitter, event.tag) { return false } } if !yaml_emitter_analyze_scalar(emitter, event.value) { return false } case yaml_SEQUENCE_START_EVENT: if len(event.anchor) > 0 { if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { return false } } if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { if !yaml_emitter_analyze_tag(emitter, event.tag) { return false } } case yaml_MAPPING_START_EVENT: if len(event.anchor) > 0 { if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { return false } } if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { if !yaml_emitter_analyze_tag(emitter, event.tag) { return false } } } return true } // Write the BOM character. func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool { if !flush(emitter) { return false } pos := emitter.buffer_pos emitter.buffer[pos+0] = '\xEF' emitter.buffer[pos+1] = '\xBB' emitter.buffer[pos+2] = '\xBF' emitter.buffer_pos += 3 return true } func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { indent := emitter.indent if indent < 0 { indent = 0 } if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) { if !put_break(emitter) { return false } } for emitter.column < indent { if !put(emitter, ' ') { return false } } emitter.whitespace = true emitter.indention = true return true } func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool { if need_whitespace && !emitter.whitespace { if !put(emitter, ' ') { return false } } if !write_all(emitter, indicator) { return false } emitter.whitespace = is_whitespace emitter.indention = (emitter.indention && is_indention) emitter.open_ended = false return true } func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool { if !write_all(emitter, value) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool { if !emitter.whitespace { if !put(emitter, ' ') { return false } } if !write_all(emitter, value) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool { if need_whitespace && !emitter.whitespace { if !put(emitter, ' ') { return false } } for i := 0; i < len(value); { var must_write bool switch value[i] { case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']': must_write = true default: must_write = is_alpha(value, i) } if must_write { if !write(emitter, value, &i) { return false } } else { w := width(value[i]) for k := 0; k < w; k++ { octet := value[i] i++ c := octet >> 4 if c < 10 { c += '0' } else { c += 'A' - 10 } if !put(emitter, c) { return false } c = octet & 0x0f if c < 10 { c += '0' } else { c += 'A' - 10 } if !put(emitter, c) { return false } } } } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { if !emitter.whitespace { if !put(emitter, ' ') { return false } } spaces := false breaks := false for i := 0; i < len(value); { if is_space(value, i) { if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) { if !yaml_emitter_write_indent(emitter) { return false } i += width(value[i]) } else { if !write(emitter, value, &i) { return false } } spaces = true } else if is_break(value, i) { if !breaks && value[i] == '\n' { if !put_break(emitter) { return false } } if !write_break(emitter, value, &i) { return false } emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } } if !write(emitter, value, &i) { return false } emitter.indention = false spaces = false breaks = false } } emitter.whitespace = false emitter.indention = false if emitter.root_context { emitter.open_ended = true } return true } func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) { return false } spaces := false breaks := false for i := 0; i < len(value); { if is_space(value, i) { if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) { if !yaml_emitter_write_indent(emitter) { return false } i += width(value[i]) } else { if !write(emitter, value, &i) { return false } } spaces = true } else if is_break(value, i) { if !breaks && value[i] == '\n' { if !put_break(emitter) { return false } } if !write_break(emitter, value, &i) { return false } emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } } if value[i] == '\'' { if !put(emitter, '\'') { return false } } if !write(emitter, value, &i) { return false } emitter.indention = false spaces = false breaks = false } } if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { spaces := false if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) { return false } for i := 0; i < len(value); { if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) || is_bom(value, i) || is_break(value, i) || value[i] == '"' || value[i] == '\\' { octet := value[i] var w int var v rune switch { case octet&0x80 == 0x00: w, v = 1, rune(octet&0x7F) case octet&0xE0 == 0xC0: w, v = 2, rune(octet&0x1F) case octet&0xF0 == 0xE0: w, v = 3, rune(octet&0x0F) case octet&0xF8 == 0xF0: w, v = 4, rune(octet&0x07) } for k := 1; k < w; k++ { octet = value[i+k] v = (v << 6) + (rune(octet) & 0x3F) } i += w if !put(emitter, '\\') { return false } var ok bool switch v { case 0x00: ok = put(emitter, '0') case 0x07: ok = put(emitter, 'a') case 0x08: ok = put(emitter, 'b') case 0x09: ok = put(emitter, 't') case 0x0A: ok = put(emitter, 'n') case 0x0b: ok = put(emitter, 'v') case 0x0c: ok = put(emitter, 'f') case 0x0d: ok = put(emitter, 'r') case 0x1b: ok = put(emitter, 'e') case 0x22: ok = put(emitter, '"') case 0x5c: ok = put(emitter, '\\') case 0x85: ok = put(emitter, 'N') case 0xA0: ok = put(emitter, '_') case 0x2028: ok = put(emitter, 'L') case 0x2029: ok = put(emitter, 'P') default: if v <= 0xFF { ok = put(emitter, 'x') w = 2 } else if v <= 0xFFFF { ok = put(emitter, 'u') w = 4 } else { ok = put(emitter, 'U') w = 8 } for k := (w - 1) * 4; ok && k >= 0; k -= 4 { digit := byte((v >> uint(k)) & 0x0F) if digit < 10 { ok = put(emitter, digit+'0') } else { ok = put(emitter, digit+'A'-10) } } } if !ok { return false } spaces = false } else if is_space(value, i) { if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 { if !yaml_emitter_write_indent(emitter) { return false } if is_space(value, i+1) { if !put(emitter, '\\') { return false } } i += width(value[i]) } else if !write(emitter, value, &i) { return false } spaces = true } else { if !write(emitter, value, &i) { return false } spaces = false } } if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool { if is_space(value, 0) || is_break(value, 0) { indent_hint := []byte{'0' + byte(emitter.best_indent)} if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) { return false } } emitter.open_ended = false var chomp_hint [1]byte if len(value) == 0 { chomp_hint[0] = '-' } else { i := len(value) - 1 for value[i]&0xC0 == 0x80 { i-- } if !is_break(value, i) { chomp_hint[0] = '-' } else if i == 0 { chomp_hint[0] = '+' emitter.open_ended = true } else { i-- for value[i]&0xC0 == 0x80 { i-- } if is_break(value, i) { chomp_hint[0] = '+' emitter.open_ended = true } } } if chomp_hint[0] != 0 { if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) { return false } } return true } func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool { if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) { return false } if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } if !put_break(emitter) { return false } emitter.indention = true emitter.whitespace = true breaks := true for i := 0; i < len(value); { if is_break(value, i) { if !write_break(emitter, value, &i) { return false } emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } } if !write(emitter, value, &i) { return false } emitter.indention = false breaks = false } } return true } func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool { if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) { return false } if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } if !put_break(emitter) { return false } emitter.indention = true emitter.whitespace = true breaks := true leading_spaces := true for i := 0; i < len(value); { if is_break(value, i) { if !breaks && !leading_spaces && value[i] == '\n' { k := 0 for is_break(value, k) { k += width(value[k]) } if !is_blankz(value, k) { if !put_break(emitter) { return false } } } if !write_break(emitter, value, &i) { return false } emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } leading_spaces = is_blank(value, i) } if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } i += width(value[i]) } else { if !write(emitter, value, &i) { return false } } emitter.indention = false breaks = false } } return true } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/LICENSE���������������������������������������������������0000644�0000153�0000161�00000021053�12321735675�021266� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This software is licensed under the LGPLv3, included below. As a special exception to the GNU Lesser General Public License version 3 ("LGPL3"), the copyright holders of this Library give you permission to convey to a third party a Combined Work that links statically or dynamically to this Library without providing any Minimal Corresponding Source or Minimal Application Code as set out in 4d or providing the installation information set out in section 4e, provided that you comply with the other provisions of LGPL3 and provided that you meet, for the Application the terms and conditions of the license(s) which apply to the Application. Except as stated in this special exception, the provisions of LGPL3 will continue to comply in full to this Library. If you modify this Library, you may apply this exception to your version of this Library, but you are not obliged to do so. If you do not wish to do so, delete this exception statement from your version. This exception does not (and cannot) modify any license terms which apply to the Application, with which you must still comply. GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/readerc.go������������������������������������������������0000644�0000153�0000161�00000027167�12321735675�022231� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml import ( "io" ) // Set the reader error and return 0. func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { parser.error = yaml_READER_ERROR parser.problem = problem parser.problem_offset = offset parser.problem_value = value return false } // Byte order marks. const ( bom_UTF8 = "\xef\xbb\xbf" bom_UTF16LE = "\xff\xfe" bom_UTF16BE = "\xfe\xff" ) // Determine the input stream encoding by checking the BOM symbol. If no BOM is // found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { // Ensure that we had enough bytes in the raw buffer. for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { if !yaml_parser_update_raw_buffer(parser) { return false } } // Determine the encoding. buf := parser.raw_buffer pos := parser.raw_buffer_pos avail := len(buf) - pos if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { parser.encoding = yaml_UTF16LE_ENCODING parser.raw_buffer_pos += 2 parser.offset += 2 } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { parser.encoding = yaml_UTF16BE_ENCODING parser.raw_buffer_pos += 2 parser.offset += 2 } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { parser.encoding = yaml_UTF8_ENCODING parser.raw_buffer_pos += 3 parser.offset += 3 } else { parser.encoding = yaml_UTF8_ENCODING } return true } // Update the raw buffer. func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { size_read := 0 // Return if the raw buffer is full. if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { return true } // Return on EOF. if parser.eof { return true } // Move the remaining bytes in the raw buffer to the beginning. if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) } parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] parser.raw_buffer_pos = 0 // Call the read handler to fill the buffer. size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] if err == io.EOF { parser.eof = true } else if err != nil { return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) } return true } // Ensure that the buffer contains at least `length` characters. // Return true on success, false on failure. // // The length is supposed to be significantly less that the buffer size. func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { if parser.read_handler == nil { panic("read handler must be set") } // If the EOF flag is set and the raw buffer is empty, do nothing. if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { return true } // Return if the buffer contains enough characters. if parser.unread >= length { return true } // Determine the input encoding if it is not known yet. if parser.encoding == yaml_ANY_ENCODING { if !yaml_parser_determine_encoding(parser) { return false } } // Move the unread characters to the beginning of the buffer. buffer_len := len(parser.buffer) if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { copy(parser.buffer, parser.buffer[parser.buffer_pos:]) buffer_len -= parser.buffer_pos parser.buffer_pos = 0 } else if parser.buffer_pos == buffer_len { buffer_len = 0 parser.buffer_pos = 0 } // Open the whole buffer for writing, and cut it before returning. parser.buffer = parser.buffer[:cap(parser.buffer)] // Fill the buffer until it has enough characters. first := true for parser.unread < length { // Fill the raw buffer if necessary. if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { if !yaml_parser_update_raw_buffer(parser) { parser.buffer = parser.buffer[:buffer_len] return false } } first = false // Decode the raw buffer. inner: for parser.raw_buffer_pos != len(parser.raw_buffer) { var value rune var width int raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos // Decode the next character. switch parser.encoding { case yaml_UTF8_ENCODING: // Decode a UTF-8 character. Check RFC 3629 // (http://www.ietf.org/rfc/rfc3629.txt) for more details. // // The following table (taken from the RFC) is used for // decoding. // // Char. number range | UTF-8 octet sequence // (hexadecimal) | (binary) // --------------------+------------------------------------ // 0000 0000-0000 007F | 0xxxxxxx // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx // // Additionally, the characters in the range 0xD800-0xDFFF // are prohibited as they are reserved for use with UTF-16 // surrogate pairs. // Determine the length of the UTF-8 sequence. octet := parser.raw_buffer[parser.raw_buffer_pos] switch { case octet&0x80 == 0x00: width = 1 case octet&0xE0 == 0xC0: width = 2 case octet&0xF0 == 0xE0: width = 3 case octet&0xF8 == 0xF0: width = 4 default: // The leading octet is invalid. return yaml_parser_set_reader_error(parser, "invalid leading UTF-8 octet", parser.offset, int(octet)) } // Check if the raw buffer contains an incomplete character. if width > raw_unread { if parser.eof { return yaml_parser_set_reader_error(parser, "incomplete UTF-8 octet sequence", parser.offset, -1) } break inner } // Decode the leading octet. switch { case octet&0x80 == 0x00: value = rune(octet & 0x7F) case octet&0xE0 == 0xC0: value = rune(octet & 0x1F) case octet&0xF0 == 0xE0: value = rune(octet & 0x0F) case octet&0xF8 == 0xF0: value = rune(octet & 0x07) default: value = 0 } // Check and decode the trailing octets. for k := 1; k < width; k++ { octet = parser.raw_buffer[parser.raw_buffer_pos+k] // Check if the octet is valid. if (octet & 0xC0) != 0x80 { return yaml_parser_set_reader_error(parser, "invalid trailing UTF-8 octet", parser.offset+k, int(octet)) } // Decode the octet. value = (value << 6) + rune(octet&0x3F) } // Check the length of the sequence against the value. switch { case width == 1: case width == 2 && value >= 0x80: case width == 3 && value >= 0x800: case width == 4 && value >= 0x10000: default: return yaml_parser_set_reader_error(parser, "invalid length of a UTF-8 sequence", parser.offset, -1) } // Check the range of the value. if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { return yaml_parser_set_reader_error(parser, "invalid Unicode character", parser.offset, int(value)) } case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: var low, high int if parser.encoding == yaml_UTF16LE_ENCODING { low, high = 0, 1 } else { high, low = 1, 0 } // The UTF-16 encoding is not as simple as one might // naively think. Check RFC 2781 // (http://www.ietf.org/rfc/rfc2781.txt). // // Normally, two subsequent bytes describe a Unicode // character. However a special technique (called a // surrogate pair) is used for specifying character // values larger than 0xFFFF. // // A surrogate pair consists of two pseudo-characters: // high surrogate area (0xD800-0xDBFF) // low surrogate area (0xDC00-0xDFFF) // // The following formulas are used for decoding // and encoding characters using surrogate pairs: // // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) // W1 = 110110yyyyyyyyyy // W2 = 110111xxxxxxxxxx // // where U is the character value, W1 is the high surrogate // area, W2 is the low surrogate area. // Check for incomplete UTF-16 character. if raw_unread < 2 { if parser.eof { return yaml_parser_set_reader_error(parser, "incomplete UTF-16 character", parser.offset, -1) } break inner } // Get the character. value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) // Check for unexpected low surrogate area. if value&0xFC00 == 0xDC00 { return yaml_parser_set_reader_error(parser, "unexpected low surrogate area", parser.offset, int(value)) } // Check for a high surrogate area. if value&0xFC00 == 0xD800 { width = 4 // Check for incomplete surrogate pair. if raw_unread < 4 { if parser.eof { return yaml_parser_set_reader_error(parser, "incomplete UTF-16 surrogate pair", parser.offset, -1) } break inner } // Get the next character. value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) // Check for a low surrogate area. if value2&0xFC00 != 0xDC00 { return yaml_parser_set_reader_error(parser, "expected low surrogate area", parser.offset+2, int(value2)) } // Generate the value of the surrogate pair. value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) } else { width = 2 } default: panic("impossible") } // Check if the character is in the allowed range: // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) // | [#x10000-#x10FFFF] (32 bit) switch { case value == 0x09: case value == 0x0A: case value == 0x0D: case value >= 0x20 && value <= 0x7E: case value == 0x85: case value >= 0xA0 && value <= 0xD7FF: case value >= 0xE000 && value <= 0xFFFD: case value >= 0x10000 && value <= 0x10FFFF: default: return yaml_parser_set_reader_error(parser, "control characters are not allowed", parser.offset, int(value)) } // Move the raw pointers. parser.raw_buffer_pos += width parser.offset += width // Finally put the character into the buffer. if value <= 0x7F { // 0000 0000-0000 007F . 0xxxxxxx parser.buffer[buffer_len+0] = byte(value) } else if value <= 0x7FF { // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) } else if value <= 0xFFFF { // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) } else { // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) } buffer_len += width parser.unread++ } // On EOF, put NUL into the buffer and return. if parser.eof { parser.buffer[buffer_len] = 0 buffer_len++ parser.unread++ break } } parser.buffer = parser.buffer[:buffer_len] return true } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/sorter.go�������������������������������������������������0000644�0000153�0000161�00000004656�12321735675�022140� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml import ( "reflect" "unicode" ) type keyList []reflect.Value func (l keyList) Len() int { return len(l) } func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l keyList) Less(i, j int) bool { a := l[i] b := l[j] ak := a.Kind() bk := b.Kind() for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { a = a.Elem() ak = a.Kind() } for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { b = b.Elem() bk = b.Kind() } af, aok := keyFloat(a) bf, bok := keyFloat(b) if aok && bok { if af != bf { return af < bf } if ak != bk { return ak < bk } return numLess(a, b) } if ak != reflect.String || bk != reflect.String { return ak < bk } ar, br := []rune(a.String()), []rune(b.String()) for i := 0; i < len(ar) && i < len(br); i++ { if ar[i] == br[i] { continue } al := unicode.IsLetter(ar[i]) bl := unicode.IsLetter(br[i]) if al && bl { return ar[i] < br[i] } if al || bl { return bl } var ai, bi int var an, bn int64 for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { an = an*10 + int64(ar[ai]-'0') } for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { bn = bn*10 + int64(br[bi]-'0') } if an != bn { return an < bn } if ai != bi { return ai < bi } return ar[i] < br[i] } return len(ar) < len(br) } // keyFloat returns a float value for v if it is a number/bool // and whether it is a number/bool or not. func keyFloat(v reflect.Value) (f float64, ok bool) { switch v.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return float64(v.Int()), true case reflect.Float32, reflect.Float64: return v.Float(), true case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return float64(v.Uint()), true case reflect.Bool: if v.Bool() { return 1, true } return 0, true } return 0, false } // numLess returns whether a < b. // a and b must necessarily have the same kind. func numLess(a, b reflect.Value) bool { switch a.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return a.Int() < b.Int() case reflect.Float32, reflect.Float64: return a.Float() < b.Float() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return a.Uint() < b.Uint() case reflect.Bool: return !a.Bool() && b.Bool() } panic("not a number") } ����������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/LICENSE.libyaml�������������������������������������������0000644�0000153�0000161�00000002042�12321735675�022713� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Copyright (c) 2006 Kirill Simonov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/writerc.go������������������������������������������������0000644�0000153�0000161�00000004605�12321735675�022273� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml // Set the writer error and return false. func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { emitter.error = yaml_WRITER_ERROR emitter.problem = problem return false } // Flush the output buffer. func yaml_emitter_flush(emitter *yaml_emitter_t) bool { if emitter.write_handler == nil { panic("write handler not set") } // Check if the buffer is empty. if emitter.buffer_pos == 0 { return true } // If the output encoding is UTF-8, we don't need to recode the buffer. if emitter.encoding == yaml_UTF8_ENCODING { if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) } emitter.buffer_pos = 0 return true } // Recode the buffer into the raw buffer. var low, high int if emitter.encoding == yaml_UTF16LE_ENCODING { low, high = 0, 1 } else { high, low = 1, 0 } pos := 0 for pos < emitter.buffer_pos { // See the "reader.c" code for more details on UTF-8 encoding. Note // that we assume that the buffer contains a valid UTF-8 sequence. // Read the next UTF-8 character. octet := emitter.buffer[pos] var w int var value rune switch { case octet&0x80 == 0x00: w, value = 1, rune(octet&0x7F) case octet&0xE0 == 0xC0: w, value = 2, rune(octet&0x1F) case octet&0xF0 == 0xE0: w, value = 3, rune(octet&0x0F) case octet&0xF8 == 0xF0: w, value = 4, rune(octet&0x07) } for k := 1; k < w; k++ { octet = emitter.buffer[pos+k] value = (value << 6) + (rune(octet) & 0x3F) } pos += w // Write the character. if value < 0x10000 { var b [2]byte b[high] = byte(value >> 8) b[low] = byte(value & 0xFF) emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1]) } else { // Write the character using a surrogate pair (check "reader.c"). var b [4]byte value -= 0x10000 b[high] = byte(0xD8 + (value >> 18)) b[low] = byte((value >> 10) & 0xFF) b[high+2] = byte(0xDC + ((value >> 8) & 0xFF)) b[low+2] = byte(value & 0xFF) emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3]) } } // Write the raw buffer. if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil { return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) } emitter.buffer_pos = 0 emitter.raw_buffer = emitter.raw_buffer[:0] return true } ���������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/parserc.go������������������������������������������������0000644�0000153�0000161�00000103731�12321735675�022253� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml import ( "bytes" ) // The parser implements the following grammar: // // stream ::= STREAM-START implicit_document? explicit_document* STREAM-END // implicit_document ::= block_node DOCUMENT-END* // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // block_node_or_indentless_sequence ::= // ALIAS // | properties (block_content | indentless_block_sequence)? // | block_content // | indentless_block_sequence // block_node ::= ALIAS // | properties block_content? // | block_content // flow_node ::= ALIAS // | properties flow_content? // | flow_content // properties ::= TAG ANCHOR? | ANCHOR TAG? // block_content ::= block_collection | flow_collection | SCALAR // flow_content ::= flow_collection | SCALAR // block_collection ::= block_sequence | block_mapping // flow_collection ::= flow_sequence | flow_mapping // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END // indentless_sequence ::= (BLOCK-ENTRY block_node?)+ // block_mapping ::= BLOCK-MAPPING_START // ((KEY block_node_or_indentless_sequence?)? // (VALUE block_node_or_indentless_sequence?)?)* // BLOCK-END // flow_sequence ::= FLOW-SEQUENCE-START // (flow_sequence_entry FLOW-ENTRY)* // flow_sequence_entry? // FLOW-SEQUENCE-END // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // flow_mapping ::= FLOW-MAPPING-START // (flow_mapping_entry FLOW-ENTRY)* // flow_mapping_entry? // FLOW-MAPPING-END // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // Peek the next token in the token queue. func peek_token(parser *yaml_parser_t) *yaml_token_t { if parser.token_available || yaml_parser_fetch_more_tokens(parser) { return &parser.tokens[parser.tokens_head] } return nil } // Remove the next token from the queue (must be called after peek_token). func skip_token(parser *yaml_parser_t) { parser.token_available = false parser.tokens_parsed++ parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN parser.tokens_head++ } // Get the next event. func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool { // Erase the event object. *event = yaml_event_t{} // No events after the end of the stream or error. if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE { return true } // Generate the next event. return yaml_parser_state_machine(parser, event) } // Set parser error. func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool { parser.error = yaml_PARSER_ERROR parser.problem = problem parser.problem_mark = problem_mark return false } func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool { parser.error = yaml_PARSER_ERROR parser.context = context parser.context_mark = context_mark parser.problem = problem parser.problem_mark = problem_mark return false } // State dispatcher. func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool { //trace("yaml_parser_state_machine", "state:", parser.state.String()) switch parser.state { case yaml_PARSE_STREAM_START_STATE: return yaml_parser_parse_stream_start(parser, event) case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: return yaml_parser_parse_document_start(parser, event, true) case yaml_PARSE_DOCUMENT_START_STATE: return yaml_parser_parse_document_start(parser, event, false) case yaml_PARSE_DOCUMENT_CONTENT_STATE: return yaml_parser_parse_document_content(parser, event) case yaml_PARSE_DOCUMENT_END_STATE: return yaml_parser_parse_document_end(parser, event) case yaml_PARSE_BLOCK_NODE_STATE: return yaml_parser_parse_node(parser, event, true, false) case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: return yaml_parser_parse_node(parser, event, true, true) case yaml_PARSE_FLOW_NODE_STATE: return yaml_parser_parse_node(parser, event, false, false) case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: return yaml_parser_parse_block_sequence_entry(parser, event, true) case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_block_sequence_entry(parser, event, false) case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_indentless_sequence_entry(parser, event) case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: return yaml_parser_parse_block_mapping_key(parser, event, true) case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: return yaml_parser_parse_block_mapping_key(parser, event, false) case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: return yaml_parser_parse_block_mapping_value(parser, event) case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: return yaml_parser_parse_flow_sequence_entry(parser, event, true) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_flow_sequence_entry(parser, event, false) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event) case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: return yaml_parser_parse_flow_mapping_key(parser, event, true) case yaml_PARSE_FLOW_MAPPING_KEY_STATE: return yaml_parser_parse_flow_mapping_key(parser, event, false) case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: return yaml_parser_parse_flow_mapping_value(parser, event, false) case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: return yaml_parser_parse_flow_mapping_value(parser, event, true) default: panic("invalid parser state") } return false } // Parse the production: // stream ::= STREAM-START implicit_document? explicit_document* STREAM-END // ************ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ != yaml_STREAM_START_TOKEN { return yaml_parser_set_parser_error(parser, "did not find expected <stream-start>", token.start_mark) } parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE *event = yaml_event_t{ typ: yaml_STREAM_START_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, encoding: token.encoding, } skip_token(parser) return true } // Parse the productions: // implicit_document ::= block_node DOCUMENT-END* // * // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // ************************* func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { token := peek_token(parser) if token == nil { return false } // Parse extra document end indicators. if !implicit { for token.typ == yaml_DOCUMENT_END_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } } } if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN && token.typ != yaml_TAG_DIRECTIVE_TOKEN && token.typ != yaml_DOCUMENT_START_TOKEN && token.typ != yaml_STREAM_END_TOKEN { // Parse an implicit document. if !yaml_parser_process_directives(parser, nil, nil) { return false } parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) parser.state = yaml_PARSE_BLOCK_NODE_STATE *event = yaml_event_t{ typ: yaml_DOCUMENT_START_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } } else if token.typ != yaml_STREAM_END_TOKEN { // Parse an explicit document. var version_directive *yaml_version_directive_t var tag_directives []yaml_tag_directive_t start_mark := token.start_mark if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) { return false } token = peek_token(parser) if token == nil { return false } if token.typ != yaml_DOCUMENT_START_TOKEN { yaml_parser_set_parser_error(parser, "did not find expected <document start>", token.start_mark) return false } parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE end_mark := token.end_mark *event = yaml_event_t{ typ: yaml_DOCUMENT_START_EVENT, start_mark: start_mark, end_mark: end_mark, version_directive: version_directive, tag_directives: tag_directives, implicit: false, } skip_token(parser) } else { // Parse the stream end. parser.state = yaml_PARSE_END_STATE *event = yaml_event_t{ typ: yaml_STREAM_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) } return true } // Parse the productions: // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // *********** // func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN || token.typ == yaml_DOCUMENT_START_TOKEN || token.typ == yaml_DOCUMENT_END_TOKEN || token.typ == yaml_STREAM_END_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } return yaml_parser_parse_node(parser, event, true, false) } // Parse the productions: // implicit_document ::= block_node DOCUMENT-END* // ************* // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } start_mark := token.start_mark end_mark := token.start_mark implicit := true if token.typ == yaml_DOCUMENT_END_TOKEN { end_mark = token.end_mark skip_token(parser) implicit = false } parser.tag_directives = parser.tag_directives[:0] parser.state = yaml_PARSE_DOCUMENT_START_STATE *event = yaml_event_t{ typ: yaml_DOCUMENT_END_EVENT, start_mark: start_mark, end_mark: end_mark, implicit: implicit, } return true } // Parse the productions: // block_node_or_indentless_sequence ::= // ALIAS // ***** // | properties (block_content | indentless_block_sequence)? // ********** * // | block_content | indentless_block_sequence // * // block_node ::= ALIAS // ***** // | properties block_content? // ********** * // | block_content // * // flow_node ::= ALIAS // ***** // | properties flow_content? // ********** * // | flow_content // * // properties ::= TAG ANCHOR? | ANCHOR TAG? // ************************* // block_content ::= block_collection | flow_collection | SCALAR // ****** // flow_content ::= flow_collection | SCALAR // ****** func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() token := peek_token(parser) if token == nil { return false } if token.typ == yaml_ALIAS_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_ALIAS_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, anchor: token.value, } skip_token(parser) return true } start_mark := token.start_mark end_mark := token.start_mark var tag_token bool var tag_handle, tag_suffix, anchor []byte var tag_mark yaml_mark_t if token.typ == yaml_ANCHOR_TOKEN { anchor = token.value start_mark = token.start_mark end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ == yaml_TAG_TOKEN { tag_token = true tag_handle = token.value tag_suffix = token.suffix tag_mark = token.start_mark end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } } } else if token.typ == yaml_TAG_TOKEN { tag_token = true tag_handle = token.value tag_suffix = token.suffix start_mark = token.start_mark tag_mark = token.start_mark end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ == yaml_ANCHOR_TOKEN { anchor = token.value end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } } } var tag []byte if tag_token { if len(tag_handle) == 0 { tag = tag_suffix tag_suffix = nil } else { for i := range parser.tag_directives { if bytes.Equal(parser.tag_directives[i].handle, tag_handle) { tag = append([]byte(nil), parser.tag_directives[i].prefix...) tag = append(tag, tag_suffix...) break } } if len(tag) == 0 { yaml_parser_set_parser_error_context(parser, "while parsing a node", start_mark, "found undefined tag handle", tag_mark) return false } } } implicit := len(tag) == 0 if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), } return true } if token.typ == yaml_SCALAR_TOKEN { var plain_implicit, quoted_implicit bool end_mark = token.end_mark if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') { plain_implicit = true } else if len(tag) == 0 { quoted_implicit = true } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, value: token.value, implicit: plain_implicit, quoted_implicit: quoted_implicit, style: yaml_style_t(token.style), } skip_token(parser) return true } if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN { // [Go] Some of the events below can be merged as they differ only on style. end_mark = token.end_mark parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), } return true } if token.typ == yaml_FLOW_MAPPING_START_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), } return true } if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), } return true } if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), } return true } if len(anchor) > 0 || len(tag) > 0 { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, quoted_implicit: false, style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), } return true } context := "while parsing a flow node" if block { context = "while parsing a block node" } yaml_parser_set_parser_error_context(parser, context, start_mark, "did not find expected node content", token.start_mark) return false } // Parse the productions: // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END // ******************** *********** * ********* // func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ == yaml_BLOCK_ENTRY_TOKEN { mark := token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE) return yaml_parser_parse_node(parser, event, true, false) } else { parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } } if token.typ == yaml_BLOCK_END_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) return true } context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a block collection", context_mark, "did not find expected '-' indicator", token.start_mark) } // Parse the productions: // indentless_sequence ::= (BLOCK-ENTRY block_node?)+ // *********** * func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_BLOCK_ENTRY_TOKEN { mark := token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_KEY_TOKEN && token.typ != yaml_VALUE_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE) return yaml_parser_parse_node(parser, event, true, false) } parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, start_mark: token.start_mark, end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark? } return true } // Parse the productions: // block_mapping ::= BLOCK-MAPPING_START // ******************* // ((KEY block_node_or_indentless_sequence?)? // *** * // (VALUE block_node_or_indentless_sequence?)?)* // // BLOCK-END // ********* // func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ == yaml_KEY_TOKEN { mark := token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_KEY_TOKEN && token.typ != yaml_VALUE_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE) return yaml_parser_parse_node(parser, event, true, true) } else { parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } } else if token.typ == yaml_BLOCK_END_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) return true } context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a block mapping", context_mark, "did not find expected key", token.start_mark) } // Parse the productions: // block_mapping ::= BLOCK-MAPPING_START // // ((KEY block_node_or_indentless_sequence?)? // // (VALUE block_node_or_indentless_sequence?)?)* // ***** * // BLOCK-END // // func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_VALUE_TOKEN { mark := token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_KEY_TOKEN && token.typ != yaml_VALUE_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE) return yaml_parser_parse_node(parser, event, true, true) } parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } // Parse the productions: // flow_sequence ::= FLOW-SEQUENCE-START // ******************* // (flow_sequence_entry FLOW-ENTRY)* // * ********** // flow_sequence_entry? // * // FLOW-SEQUENCE-END // ***************** // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * // func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { if !first { if token.typ == yaml_FLOW_ENTRY_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } } else { context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a flow sequence", context_mark, "did not find expected ',' or ']'", token.start_mark) } } if token.typ == yaml_KEY_TOKEN { parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, implicit: true, style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), } skip_token(parser) return true } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) return true } // // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // *** * // func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ != yaml_VALUE_TOKEN && token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE) return yaml_parser_parse_node(parser, event, false, false) } mark := token.end_mark skip_token(parser) parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // ***** * // func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_VALUE_TOKEN { skip_token(parser) token := peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * // func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, start_mark: token.start_mark, end_mark: token.start_mark, // [Go] Shouldn't this be end_mark? } return true } // Parse the productions: // flow_mapping ::= FLOW-MAPPING-START // ****************** // (flow_mapping_entry FLOW-ENTRY)* // * ********** // flow_mapping_entry? // ****************** // FLOW-MAPPING-END // **************** // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * *** * // func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_MAPPING_END_TOKEN { if !first { if token.typ == yaml_FLOW_ENTRY_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } } else { context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a flow mapping", context_mark, "did not find expected ',' or '}'", token.start_mark) } } if token.typ == yaml_KEY_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_VALUE_TOKEN && token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE) return yaml_parser_parse_node(parser, event, false, false) } else { parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) return true } // Parse the productions: // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * ***** * // func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { token := peek_token(parser) if token == nil { return false } if empty { parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } if token.typ == yaml_VALUE_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } // Generate an empty scalar event. func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool { *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, start_mark: mark, end_mark: mark, value: nil, // Empty implicit: true, style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), } return true } var default_tag_directives = []yaml_tag_directive_t{ {[]byte("!"), []byte("!")}, {[]byte("!!"), []byte("tag:yaml.org,2002:")}, } // Parse directives. func yaml_parser_process_directives(parser *yaml_parser_t, version_directive_ref **yaml_version_directive_t, tag_directives_ref *[]yaml_tag_directive_t) bool { var version_directive *yaml_version_directive_t var tag_directives []yaml_tag_directive_t token := peek_token(parser) if token == nil { return false } for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN { if token.typ == yaml_VERSION_DIRECTIVE_TOKEN { if version_directive != nil { yaml_parser_set_parser_error(parser, "found duplicate %YAML directive", token.start_mark) return false } if token.major != 1 || token.minor != 1 { yaml_parser_set_parser_error(parser, "found incompatible YAML document", token.start_mark) return false } version_directive = &yaml_version_directive_t{ major: token.major, minor: token.minor, } } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN { value := yaml_tag_directive_t{ handle: token.value, prefix: token.prefix, } if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) { return false } tag_directives = append(tag_directives, value) } skip_token(parser) token = peek_token(parser) if token == nil { return false } } for i := range default_tag_directives { if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) { return false } } if version_directive_ref != nil { *version_directive_ref = version_directive } if tag_directives_ref != nil { *tag_directives_ref = tag_directives } return true } // Append a tag directive to the directives stack. func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool { for i := range parser.tag_directives { if bytes.Equal(value.handle, parser.tag_directives[i].handle) { if allow_duplicates { return true } return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark) } } // [Go] I suspect the copy is unnecessary. This was likely done // because there was no way to track ownership of the data. value_copy := yaml_tag_directive_t{ handle: make([]byte, len(value.handle)), prefix: make([]byte, len(value.prefix)), } copy(value_copy.handle, value.handle) copy(value_copy.prefix, value.prefix) parser.tag_directives = append(parser.tag_directives, value_copy) return true } ���������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/suite_test.go���������������������������������������������0000644�0000153�0000161�00000000225�12321735675�022776� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml_test import ( . "launchpad.net/gocheck" "testing" ) func Test(t *testing.T) { TestingT(t) } type S struct{} var _ = Suite(&S{}) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/.lbox.check�����������������������������������������������0000755�0000153�0000161�00000000630�12321735675�022303� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh set -e BADFMT=`find * -name '*.go' | xargs gofmt -l` if [ -n "$BADFMT" ]; then BADFMT=`echo "$BADFMT" | sed "s/^/ /"` echo -e "gofmt is sad:\n\n$BADFMT" exit 1 fi VERSION=`go version | awk '{print $3}'` if [ $VERSION == 'devel' ]; then go tool vet \ -methods \ -printf \ -rangeloops \ -printfuncs 'ErrorContextf:1,notFoundf:0,badReqErrorf:0,Commitf:0,Snapshotf:0,Debugf:0' \ . fi ��������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/goyaml.go�������������������������������������������������0000644�0000153�0000161�00000021002�12321736015�022057� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������// Package goyaml implements YAML support for the Go language. package goyaml import ( "errors" "fmt" "reflect" "runtime" "strings" "sync" ) func handleErr(err *error) { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { panic(r) } else if _, ok := r.(*reflect.ValueError); ok { panic(r) } else if _, ok := r.(externalPanic); ok { panic(r) } else if s, ok := r.(string); ok { *err = errors.New("YAML error: " + s) } else if e, ok := r.(error); ok { *err = e } else { panic(r) } } } // Objects implementing the goyaml.Setter interface will receive the YAML // tag and value via the SetYAML method during unmarshaling, rather than // being implicitly assigned by the goyaml machinery. If setting the value // works, the method should return true. If it returns false, the given // value will be omitted from maps and slices. type Setter interface { SetYAML(tag string, value interface{}) bool } // Objects implementing the goyaml.Getter interface will get the GetYAML() // method called when goyaml is requested to marshal the given value, and // the result of this method will be marshaled in place of the actual object. type Getter interface { GetYAML() (tag string, value interface{}) } // Unmarshal decodes the first document found within the in byte slice // and assigns decoded values into the object pointed by out. // // Maps, pointers to structs and ints, etc, may all be used as out values. // If an internal pointer within a struct is not initialized, goyaml // will initialize it if necessary for unmarshalling the provided data, // but the struct provided as out must not be a nil pointer. // // The type of the decoded values and the type of out will be considered, // and Unmarshal() will do the best possible job to unmarshal values // appropriately. It is NOT considered an error, though, to skip values // because they are not available in the decoded YAML, or if they are not // compatible with the out value. To ensure something was properly // unmarshaled use a map or compare against the previous value for the // field (usually the zero value). // // Struct fields are only unmarshalled if they are exported (have an // upper case first letter), and will be unmarshalled using the field // name lowercased by default. When custom field names are desired, the // tag value may be used to tweak the name. Everything before the first // comma in the field tag will be used as the name. The values following // the comma are used to tweak the marshalling process (see Marshal). // Conflicting names result in a runtime error. // // For example: // // type T struct { // F int `yaml:"a,omitempty"` // B int // } // var T t // goyaml.Unmarshal([]byte("a: 1\nb: 2"), &t) // // See the documentation of Marshal for the format of tags and a list of // supported tag options. // func Unmarshal(in []byte, out interface{}) (err error) { defer handleErr(&err) d := newDecoder() p := newParser(in) defer p.destroy() node := p.parse() if node != nil { d.unmarshal(node, reflect.ValueOf(out)) } return nil } // Marshal serializes the value provided into a YAML document. The structure // of the generated document will reflect the structure of the value itself. // Maps, pointers to structs and ints, etc, may all be used as the in value. // // In the case of struct values, only exported fields will be serialized. // The lowercased field name is used as the key for each exported field, // but this behavior may be changed using the respective field tag. // The tag may also contain flags to tweak the marshalling behavior for // the field. Conflicting names result in a runtime error. The tag format // accepted is: // // `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)` // // The following flags are currently supported: // // omitempty Only include the field if it's not set to the zero // value for the type or to empty slices or maps. // Does not apply to zero valued structs. // // flow Marshal using a flow style (useful for structs, // sequences and maps. // // inline Inline the struct it's applied to, so its fields // are processed as if they were part of the outer // struct. // // In addition, if the key is "-", the field is ignored. // // For example: // // type T struct { // F int "a,omitempty" // B int // } // goyaml.Marshal(&T{B: 2}) // Returns "b: 2\n" // goyaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" // func Marshal(in interface{}) (out []byte, err error) { defer handleErr(&err) e := newEncoder() defer e.destroy() e.marshal("", reflect.ValueOf(in)) e.finish() out = e.out return } // -------------------------------------------------------------------------- // Maintain a mapping of keys to structure field indexes // The code in this section was copied from gobson. // structInfo holds details for the serialization of fields of // a given struct. type structInfo struct { FieldsMap map[string]fieldInfo FieldsList []fieldInfo // InlineMap is the number of the field in the struct that // contains an ,inline map, or -1 if there's none. InlineMap int } type fieldInfo struct { Key string Num int OmitEmpty bool Flow bool // Inline holds the field index if the field is part of an inlined struct. Inline []int } var structMap = make(map[reflect.Type]*structInfo) var fieldMapMutex sync.RWMutex type externalPanic string func (e externalPanic) String() string { return string(e) } func getStructInfo(st reflect.Type) (*structInfo, error) { fieldMapMutex.RLock() sinfo, found := structMap[st] fieldMapMutex.RUnlock() if found { return sinfo, nil } n := st.NumField() fieldsMap := make(map[string]fieldInfo) fieldsList := make([]fieldInfo, 0, n) inlineMap := -1 for i := 0; i != n; i++ { field := st.Field(i) if field.PkgPath != "" { continue // Private field } info := fieldInfo{Num: i} tag := field.Tag.Get("yaml") if tag == "" && strings.Index(string(field.Tag), ":") < 0 { tag = string(field.Tag) } if tag == "-" { continue } inline := false fields := strings.Split(tag, ",") if len(fields) > 1 { for _, flag := range fields[1:] { switch flag { case "omitempty": info.OmitEmpty = true case "flow": info.Flow = true case "inline": inline = true default: msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st) panic(externalPanic(msg)) } } tag = fields[0] } if inline { switch field.Type.Kind() { //case reflect.Map: // if inlineMap >= 0 { // return nil, errors.New("Multiple ,inline maps in struct " + st.String()) // } // if field.Type.Key() != reflect.TypeOf("") { // return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) // } // inlineMap = info.Num case reflect.Struct: sinfo, err := getStructInfo(field.Type) if err != nil { return nil, err } for _, finfo := range sinfo.FieldsList { if _, found := fieldsMap[finfo.Key]; found { msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() return nil, errors.New(msg) } if finfo.Inline == nil { finfo.Inline = []int{i, finfo.Num} } else { finfo.Inline = append([]int{i}, finfo.Inline...) } fieldsMap[finfo.Key] = finfo fieldsList = append(fieldsList, finfo) } default: //panic("Option ,inline needs a struct value or map field") panic("Option ,inline needs a struct value field") } continue } if tag != "" { info.Key = tag } else { info.Key = strings.ToLower(field.Name) } if _, found = fieldsMap[info.Key]; found { msg := "Duplicated key '" + info.Key + "' in struct " + st.String() return nil, errors.New(msg) } fieldsList = append(fieldsList, info) fieldsMap[info.Key] = info } sinfo = &structInfo{fieldsMap, fieldsList, inlineMap} fieldMapMutex.Lock() structMap[st] = sinfo fieldMapMutex.Unlock() return sinfo, nil } func isZero(v reflect.Value) bool { switch v.Kind() { case reflect.String: return len(v.String()) == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() case reflect.Slice: return v.Len() == 0 case reflect.Map: return v.Len() == 0 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Bool: return !v.Bool() } return false } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/encode.go�������������������������������������������������0000644�0000153�0000161�00000011713�12321735675�022047� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml import ( "reflect" "sort" "strconv" ) type encoder struct { emitter yaml_emitter_t event yaml_event_t out []byte flow bool } func newEncoder() (e *encoder) { e = &encoder{} e.must(yaml_emitter_initialize(&e.emitter)) yaml_emitter_set_output_string(&e.emitter, &e.out) e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)) e.emit() e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true)) e.emit() return e } func (e *encoder) finish() { e.must(yaml_document_end_event_initialize(&e.event, true)) e.emit() e.emitter.open_ended = false e.must(yaml_stream_end_event_initialize(&e.event)) e.emit() } func (e *encoder) destroy() { yaml_emitter_delete(&e.emitter) } func (e *encoder) emit() { // This will internally delete the e.event value. if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT { e.must(false) } } func (e *encoder) must(ok bool) { if !ok { msg := e.emitter.problem if msg == "" { msg = "Unknown problem generating YAML content" } panic(msg) } } func (e *encoder) marshal(tag string, in reflect.Value) { var value interface{} if getter, ok := in.Interface().(Getter); ok { tag, value = getter.GetYAML() if value == nil { e.nilv() return } in = reflect.ValueOf(value) } switch in.Kind() { case reflect.Interface: if in.IsNil() { e.nilv() } else { e.marshal(tag, in.Elem()) } case reflect.Map: e.mapv(tag, in) case reflect.Ptr: if in.IsNil() { e.nilv() } else { e.marshal(tag, in.Elem()) } case reflect.Struct: e.structv(tag, in) case reflect.Slice: e.slicev(tag, in) case reflect.String: e.stringv(tag, in) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: e.intv(tag, in) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: e.uintv(tag, in) case reflect.Float32, reflect.Float64: e.floatv(tag, in) case reflect.Bool: e.boolv(tag, in) default: panic("Can't marshal type yet: " + in.Type().String()) } } func (e *encoder) mapv(tag string, in reflect.Value) { e.mappingv(tag, func() { keys := keyList(in.MapKeys()) sort.Sort(keys) for _, k := range keys { e.marshal("", k) e.marshal("", in.MapIndex(k)) } }) } func (e *encoder) structv(tag string, in reflect.Value) { sinfo, err := getStructInfo(in.Type()) if err != nil { panic(err) } e.mappingv(tag, func() { for _, info := range sinfo.FieldsList { var value reflect.Value if info.Inline == nil { value = in.Field(info.Num) } else { value = in.FieldByIndex(info.Inline) } if info.OmitEmpty && isZero(value) { continue } e.marshal("", reflect.ValueOf(info.Key)) e.flow = info.Flow e.marshal("", value) } }) } func (e *encoder) mappingv(tag string, f func()) { implicit := tag == "" style := yaml_BLOCK_MAPPING_STYLE if e.flow { e.flow = false style = yaml_FLOW_MAPPING_STYLE } e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) e.emit() f() e.must(yaml_mapping_end_event_initialize(&e.event)) e.emit() } func (e *encoder) slicev(tag string, in reflect.Value) { implicit := tag == "" style := yaml_BLOCK_SEQUENCE_STYLE if e.flow { e.flow = false style = yaml_FLOW_SEQUENCE_STYLE } e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) e.emit() n := in.Len() for i := 0; i < n; i++ { e.marshal("", in.Index(i)) } e.must(yaml_sequence_end_event_initialize(&e.event)) e.emit() } func (e *encoder) stringv(tag string, in reflect.Value) { var style yaml_scalar_style_t s := in.String() if rtag, _ := resolve("", s); rtag != "!!str" { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } else { style = yaml_PLAIN_SCALAR_STYLE } e.emitScalar(s, "", tag, style) } func (e *encoder) boolv(tag string, in reflect.Value) { var s string if in.Bool() { s = "true" } else { s = "false" } e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) } func (e *encoder) intv(tag string, in reflect.Value) { s := strconv.FormatInt(in.Int(), 10) e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) } func (e *encoder) uintv(tag string, in reflect.Value) { s := strconv.FormatUint(in.Uint(), 10) e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) } func (e *encoder) floatv(tag string, in reflect.Value) { // FIXME: Handle 64 bits here. s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32) switch s { case "+Inf": s = ".inf" case "-Inf": s = "-.inf" case "NaN": s = ".nan" } e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) } func (e *encoder) nilv() { e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) } func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { implicit := tag == "" if !implicit { style = yaml_PLAIN_SCALAR_STYLE } e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) e.emit() } �����������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/yamlprivateh.go�������������������������������������������0000644�0000153�0000161�00000011543�12321735675�023320� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml const ( // The size of the input raw buffer. input_raw_buffer_size = 512 // The size of the input buffer. // It should be possible to decode the whole raw buffer. input_buffer_size = input_raw_buffer_size * 3 // The size of the output buffer. output_buffer_size = 128 // The size of the output raw buffer. // It should be possible to encode the whole output buffer. output_raw_buffer_size = (output_buffer_size*2 + 2) // The size of other stacks and queues. initial_stack_size = 16 initial_queue_size = 16 initial_string_size = 16 ) // Check if the character at the specified position is an alphabetical // character, a digit, '_', or '-'. func is_alpha(b []byte, i int) bool { return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' } // Check if the character at the specified position is a digit. func is_digit(b []byte, i int) bool { return b[i] >= '0' && b[i] <= '9' } // Get the value of a digit. func as_digit(b []byte, i int) int { return int(b[i]) - '0' } // Check if the character at the specified position is a hex-digit. func is_hex(b []byte, i int) bool { return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' } // Get the value of a hex-digit. func as_hex(b []byte, i int) int { bi := b[i] if bi >= 'A' && bi <= 'F' { return int(bi) - 'A' + 10 } if bi >= 'a' && bi <= 'f' { return int(bi) - 'a' + 10 } return int(bi) - '0' } // Check if the character is ASCII. func is_ascii(b []byte, i int) bool { return b[i] <= 0x7F } // Check if the character at the start of the buffer can be printed unescaped. func is_printable(b []byte, i int) bool { return ((b[i] == 0x0A) || // . == #x0A (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF (b[i] > 0xC2 && b[i] < 0xED) || (b[i] == 0xED && b[i+1] < 0xA0) || (b[i] == 0xEE) || (b[i] == 0xEF && // #xE000 <= . <= #xFFFD !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) } // Check if the character at the specified position is NUL. func is_z(b []byte, i int) bool { return b[i] == 0x00 } // Check if the beginning of the buffer is a BOM. func is_bom(b []byte, i int) bool { return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF } // Check if the character at the specified position is space. func is_space(b []byte, i int) bool { return b[i] == ' ' } // Check if the character at the specified position is tab. func is_tab(b []byte, i int) bool { return b[i] == '\t' } // Check if the character at the specified position is blank (space or tab). func is_blank(b []byte, i int) bool { //return is_space(b, i) || is_tab(b, i) return b[i] == ' ' || b[i] == '\t' } // Check if the character at the specified position is a line break. func is_break(b []byte, i int) bool { return (b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) } func is_crlf(b []byte, i int) bool { return b[i] == '\r' && b[i+1] == '\n' } // Check if the character is a line break or NUL. func is_breakz(b []byte, i int) bool { //return is_break(b, i) || is_z(b, i) return ( // is_break: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) // is_z: b[i] == 0) } // Check if the character is a line break, space, or NUL. func is_spacez(b []byte, i int) bool { //return is_space(b, i) || is_breakz(b, i) return ( // is_space: b[i] == ' ' || // is_breakz: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) b[i] == 0) } // Check if the character is a line break, space, tab, or NUL. func is_blankz(b []byte, i int) bool { //return is_blank(b, i) || is_breakz(b, i) return ( // is_blank: b[i] == ' ' || b[i] == '\t' || // is_breakz: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) b[i] == 0) } // Determine the width of the character. func width(b byte) int { // Don't replace these by a switch without first // confirming that it is being inlined. if b&0x80 == 0x00 { return 1 } if b&0xE0 == 0xC0 { return 2 } if b&0xF0 == 0xE0 { return 3 } if b&0xF8 == 0xF0 { return 4 } return 0 } �������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/.bzrignore������������������������������������������������0000644�0000153�0000161�00000000260�12321735675�022260� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������[568].out _* *.cgo*.* yaml-*/stamp-h1 yaml-*/Makefile yaml-*/*/Makefile yaml-*/libtool yaml-*/config* yaml-*/*/*.lo yaml-*/*/*.la yaml-*/*/.libs yaml-*/*/.deps yaml-*/tests/* ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/.lbox�����������������������������������������������������0000644�0000153�0000161�00000000033�12321735675�021221� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������propose -cr -for=lp:goyaml �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/resolve.go������������������������������������������������0000644�0000153�0000161�00000006567�12321735675�022304� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml import ( "math" "strconv" "strings" ) // TODO: merge, timestamps, base 60 floats, omap. type resolveMapItem struct { value interface{} tag string } var resolveTable = make([]byte, 256) var resolveMap = make(map[string]resolveMapItem) func init() { t := resolveTable t[int('+')] = 'S' // Sign t[int('-')] = 'S' for _, c := range "0123456789" { t[int(c)] = 'D' // Digit } for _, c := range "yYnNtTfFoO~" { t[int(c)] = 'M' // In map } t[int('.')] = '.' // Float (potentially in map) t[int('<')] = '<' // Merge var resolveMapList = []struct { v interface{} tag string l []string }{ {true, "!!bool", []string{"y", "Y", "yes", "Yes", "YES"}}, {true, "!!bool", []string{"true", "True", "TRUE"}}, {true, "!!bool", []string{"on", "On", "ON"}}, {false, "!!bool", []string{"n", "N", "no", "No", "NO"}}, {false, "!!bool", []string{"false", "False", "FALSE"}}, {false, "!!bool", []string{"off", "Off", "OFF"}}, {nil, "!!null", []string{"~", "null", "Null", "NULL"}}, {math.NaN(), "!!float", []string{".nan", ".NaN", ".NAN"}}, {math.Inf(+1), "!!float", []string{".inf", ".Inf", ".INF"}}, {math.Inf(+1), "!!float", []string{"+.inf", "+.Inf", "+.INF"}}, {math.Inf(-1), "!!float", []string{"-.inf", "-.Inf", "-.INF"}}, } m := resolveMap for _, item := range resolveMapList { for _, s := range item.l { m[s] = resolveMapItem{item.v, item.tag} } } } const longTagPrefix = "tag:yaml.org,2002:" func shortTag(tag string) string { if strings.HasPrefix(tag, longTagPrefix) { return "!!" + tag[len(longTagPrefix):] } return tag } func resolvableTag(tag string) bool { switch tag { case "", "!!str", "!!bool", "!!int", "!!float", "!!null": return true } return false } func resolve(tag string, in string) (rtag string, out interface{}) { tag = shortTag(tag) if !resolvableTag(tag) { return tag, in } defer func() { if tag != "" && tag != rtag { panic("Can't decode " + rtag + " '" + in + "' as a " + tag) } }() if in == "" { return "!!null", nil } c := resolveTable[in[0]] if c == 0 { // It's a string for sure. Nothing to do. return "!!str", in } // Handle things we can lookup in a map. if item, ok := resolveMap[in]; ok { return item.tag, item.value } switch c { case 'M': // We've already checked the map above. case '.': // Not in the map, so maybe a normal float. floatv, err := strconv.ParseFloat(in, 64) if err == nil { return "!!float", floatv } // XXX Handle base 60 floats here (WTF!) case 'D', 'S': // Int, float, or timestamp. for i := 0; i != len(in); i++ { if in[i] == '_' { in = strings.Replace(in, "_", "", -1) break } } intv, err := strconv.ParseInt(in, 0, 64) if err == nil { if intv == int64(int(intv)) { return "!!int", int(intv) } else { return "!!int", intv } } floatv, err := strconv.ParseFloat(in, 64) if err == nil { return "!!float", floatv } if strings.HasPrefix(in, "0b") { intv, err := strconv.ParseInt(in[2:], 2, 64) if err == nil { return "!!int", int(intv) } } else if strings.HasPrefix(in, "-0b") { intv, err := strconv.ParseInt(in[3:], 2, 64) if err == nil { return "!!int", -int(intv) } } // XXX Handle timestamps here. case '<': // XXX Handle merge (<<) here. default: panic("resolveTable item not yet handled: " + string([]byte{c}) + " (with " + in + ")") } return "!!str", in } �����������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/scannerc.go�����������������������������������������������0000644�0000153�0000161�00000226721�12321735675�022415� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml import ( "bytes" "fmt" ) // Introduction // ************ // // The following notes assume that you are familiar with the YAML specification // (http://yaml.org/spec/cvs/current.html). We mostly follow it, although in // some cases we are less restrictive that it requires. // // The process of transforming a YAML stream into a sequence of events is // divided on two steps: Scanning and Parsing. // // The Scanner transforms the input stream into a sequence of tokens, while the // parser transform the sequence of tokens produced by the Scanner into a // sequence of parsing events. // // The Scanner is rather clever and complicated. The Parser, on the contrary, // is a straightforward implementation of a recursive-descendant parser (or, // LL(1) parser, as it is usually called). // // Actually there are two issues of Scanning that might be called "clever", the // rest is quite straightforward. The issues are "block collection start" and // "simple keys". Both issues are explained below in details. // // Here the Scanning step is explained and implemented. We start with the list // of all the tokens produced by the Scanner together with short descriptions. // // Now, tokens: // // STREAM-START(encoding) # The stream start. // STREAM-END # The stream end. // VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. // TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. // DOCUMENT-START # '---' // DOCUMENT-END # '...' // BLOCK-SEQUENCE-START # Indentation increase denoting a block // BLOCK-MAPPING-START # sequence or a block mapping. // BLOCK-END # Indentation decrease. // FLOW-SEQUENCE-START # '[' // FLOW-SEQUENCE-END # ']' // BLOCK-SEQUENCE-START # '{' // BLOCK-SEQUENCE-END # '}' // BLOCK-ENTRY # '-' // FLOW-ENTRY # ',' // KEY # '?' or nothing (simple keys). // VALUE # ':' // ALIAS(anchor) # '*anchor' // ANCHOR(anchor) # '&anchor' // TAG(handle,suffix) # '!handle!suffix' // SCALAR(value,style) # A scalar. // // The following two tokens are "virtual" tokens denoting the beginning and the // end of the stream: // // STREAM-START(encoding) // STREAM-END // // We pass the information about the input stream encoding with the // STREAM-START token. // // The next two tokens are responsible for tags: // // VERSION-DIRECTIVE(major,minor) // TAG-DIRECTIVE(handle,prefix) // // Example: // // %YAML 1.1 // %TAG ! !foo // %TAG !yaml! tag:yaml.org,2002: // --- // // The correspoding sequence of tokens: // // STREAM-START(utf-8) // VERSION-DIRECTIVE(1,1) // TAG-DIRECTIVE("!","!foo") // TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") // DOCUMENT-START // STREAM-END // // Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole // line. // // The document start and end indicators are represented by: // // DOCUMENT-START // DOCUMENT-END // // Note that if a YAML stream contains an implicit document (without '---' // and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be // produced. // // In the following examples, we present whole documents together with the // produced tokens. // // 1. An implicit document: // // 'a scalar' // // Tokens: // // STREAM-START(utf-8) // SCALAR("a scalar",single-quoted) // STREAM-END // // 2. An explicit document: // // --- // 'a scalar' // ... // // Tokens: // // STREAM-START(utf-8) // DOCUMENT-START // SCALAR("a scalar",single-quoted) // DOCUMENT-END // STREAM-END // // 3. Several documents in a stream: // // 'a scalar' // --- // 'another scalar' // --- // 'yet another scalar' // // Tokens: // // STREAM-START(utf-8) // SCALAR("a scalar",single-quoted) // DOCUMENT-START // SCALAR("another scalar",single-quoted) // DOCUMENT-START // SCALAR("yet another scalar",single-quoted) // STREAM-END // // We have already introduced the SCALAR token above. The following tokens are // used to describe aliases, anchors, tag, and scalars: // // ALIAS(anchor) // ANCHOR(anchor) // TAG(handle,suffix) // SCALAR(value,style) // // The following series of examples illustrate the usage of these tokens: // // 1. A recursive sequence: // // &A [ *A ] // // Tokens: // // STREAM-START(utf-8) // ANCHOR("A") // FLOW-SEQUENCE-START // ALIAS("A") // FLOW-SEQUENCE-END // STREAM-END // // 2. A tagged scalar: // // !!float "3.14" # A good approximation. // // Tokens: // // STREAM-START(utf-8) // TAG("!!","float") // SCALAR("3.14",double-quoted) // STREAM-END // // 3. Various scalar styles: // // --- # Implicit empty plain scalars do not produce tokens. // --- a plain scalar // --- 'a single-quoted scalar' // --- "a double-quoted scalar" // --- |- // a literal scalar // --- >- // a folded // scalar // // Tokens: // // STREAM-START(utf-8) // DOCUMENT-START // DOCUMENT-START // SCALAR("a plain scalar",plain) // DOCUMENT-START // SCALAR("a single-quoted scalar",single-quoted) // DOCUMENT-START // SCALAR("a double-quoted scalar",double-quoted) // DOCUMENT-START // SCALAR("a literal scalar",literal) // DOCUMENT-START // SCALAR("a folded scalar",folded) // STREAM-END // // Now it's time to review collection-related tokens. We will start with // flow collections: // // FLOW-SEQUENCE-START // FLOW-SEQUENCE-END // FLOW-MAPPING-START // FLOW-MAPPING-END // FLOW-ENTRY // KEY // VALUE // // The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and // FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' // correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the // indicators '?' and ':', which are used for denoting mapping keys and values, // are represented by the KEY and VALUE tokens. // // The following examples show flow collections: // // 1. A flow sequence: // // [item 1, item 2, item 3] // // Tokens: // // STREAM-START(utf-8) // FLOW-SEQUENCE-START // SCALAR("item 1",plain) // FLOW-ENTRY // SCALAR("item 2",plain) // FLOW-ENTRY // SCALAR("item 3",plain) // FLOW-SEQUENCE-END // STREAM-END // // 2. A flow mapping: // // { // a simple key: a value, # Note that the KEY token is produced. // ? a complex key: another value, // } // // Tokens: // // STREAM-START(utf-8) // FLOW-MAPPING-START // KEY // SCALAR("a simple key",plain) // VALUE // SCALAR("a value",plain) // FLOW-ENTRY // KEY // SCALAR("a complex key",plain) // VALUE // SCALAR("another value",plain) // FLOW-ENTRY // FLOW-MAPPING-END // STREAM-END // // A simple key is a key which is not denoted by the '?' indicator. Note that // the Scanner still produce the KEY token whenever it encounters a simple key. // // For scanning block collections, the following tokens are used (note that we // repeat KEY and VALUE here): // // BLOCK-SEQUENCE-START // BLOCK-MAPPING-START // BLOCK-END // BLOCK-ENTRY // KEY // VALUE // // The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation // increase that precedes a block collection (cf. the INDENT token in Python). // The token BLOCK-END denote indentation decrease that ends a block collection // (cf. the DEDENT token in Python). However YAML has some syntax pecularities // that makes detections of these tokens more complex. // // The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators // '-', '?', and ':' correspondingly. // // The following examples show how the tokens BLOCK-SEQUENCE-START, // BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: // // 1. Block sequences: // // - item 1 // - item 2 // - // - item 3.1 // - item 3.2 // - // key 1: value 1 // key 2: value 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-ENTRY // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 3.1",plain) // BLOCK-ENTRY // SCALAR("item 3.2",plain) // BLOCK-END // BLOCK-ENTRY // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // BLOCK-END // STREAM-END // // 2. Block mappings: // // a simple key: a value # The KEY token is produced here. // ? a complex key // : another value // a mapping: // key 1: value 1 // key 2: value 2 // a sequence: // - item 1 // - item 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-MAPPING-START // KEY // SCALAR("a simple key",plain) // VALUE // SCALAR("a value",plain) // KEY // SCALAR("a complex key",plain) // VALUE // SCALAR("another value",plain) // KEY // SCALAR("a mapping",plain) // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // KEY // SCALAR("a sequence",plain) // VALUE // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // BLOCK-END // STREAM-END // // YAML does not always require to start a new block collection from a new // line. If the current line contains only '-', '?', and ':' indicators, a new // block collection may start at the current line. The following examples // illustrate this case: // // 1. Collections in a sequence: // // - - item 1 // - item 2 // - key 1: value 1 // key 2: value 2 // - ? complex key // : complex value // // Tokens: // // STREAM-START(utf-8) // BLOCK-SEQUENCE-START // BLOCK-ENTRY // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // BLOCK-ENTRY // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // BLOCK-ENTRY // BLOCK-MAPPING-START // KEY // SCALAR("complex key") // VALUE // SCALAR("complex value") // BLOCK-END // BLOCK-END // STREAM-END // // 2. Collections in a mapping: // // ? a sequence // : - item 1 // - item 2 // ? a mapping // : key 1: value 1 // key 2: value 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-MAPPING-START // KEY // SCALAR("a sequence",plain) // VALUE // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // KEY // SCALAR("a mapping",plain) // VALUE // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // BLOCK-END // STREAM-END // // YAML also permits non-indented sequences if they are included into a block // mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: // // key: // - item 1 # BLOCK-SEQUENCE-START is NOT produced here. // - item 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-MAPPING-START // KEY // SCALAR("key",plain) // VALUE // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // // Ensure that the buffer contains the required number of characters. // Return true on success, false on failure (reader error or memory error). func cache(parser *yaml_parser_t, length int) bool { // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B) return parser.unread >= length || yaml_parser_update_buffer(parser, length) } // Advance the buffer pointer. func skip(parser *yaml_parser_t) { parser.mark.index++ parser.mark.column++ parser.unread-- parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) } func skip_line(parser *yaml_parser_t) { if is_crlf(parser.buffer, parser.buffer_pos) { parser.mark.index += 2 parser.mark.column = 0 parser.mark.line++ parser.unread -= 2 parser.buffer_pos += 2 } else if is_break(parser.buffer, parser.buffer_pos) { parser.mark.index++ parser.mark.column = 0 parser.mark.line++ parser.unread-- parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) } } // Copy a character to a string buffer and advance pointers. func read(parser *yaml_parser_t, s []byte) []byte { w := width(parser.buffer[parser.buffer_pos]) if w == 0 { panic("invalid character sequence") } if len(s) == 0 { s = make([]byte, 0, 32) } if w == 1 && len(s)+w <= cap(s) { s = s[:len(s)+1] s[len(s)-1] = parser.buffer[parser.buffer_pos] parser.buffer_pos++ } else { s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...) parser.buffer_pos += w } parser.mark.index++ parser.mark.column++ parser.unread-- return s } // Copy a line break character to a string buffer and advance pointers. func read_line(parser *yaml_parser_t, s []byte) []byte { buf := parser.buffer pos := parser.buffer_pos switch { case buf[pos] == '\r' && buf[pos+1] == '\n': // CR LF . LF s = append(s, '\n') parser.buffer_pos += 2 parser.mark.index++ parser.unread-- case buf[pos] == '\r' || buf[pos] == '\n': // CR|LF . LF s = append(s, '\n') parser.buffer_pos += 1 case buf[pos] == '\xC2' && buf[pos+1] == '\x85': // NEL . LF s = append(s, '\n') parser.buffer_pos += 2 case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'): // LS|PS . LS|PS s = append(s, buf[parser.buffer_pos:pos+3]...) parser.buffer_pos += 3 default: return s } parser.mark.index++ parser.mark.column = 0 parser.mark.line++ parser.unread-- return s } // Get the next token. func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool { // Erase the token object. *token = yaml_token_t{} // [Go] Is this necessary? // No tokens after STREAM-END or error. if parser.stream_end_produced || parser.error != yaml_NO_ERROR { return true } // Ensure that the tokens queue contains enough tokens. if !parser.token_available { if !yaml_parser_fetch_more_tokens(parser) { return false } } // Fetch the next token from the queue. *token = parser.tokens[parser.tokens_head] parser.tokens_head++ parser.tokens_parsed++ parser.token_available = false if token.typ == yaml_STREAM_END_TOKEN { parser.stream_end_produced = true } return true } // Set the scanner error and return false. func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool { parser.error = yaml_SCANNER_ERROR parser.context = context parser.context_mark = context_mark parser.problem = problem parser.problem_mark = parser.mark return false } func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool { context := "while parsing a tag" if directive { context = "while parsing a %TAG directive" } return yaml_parser_set_scanner_error(parser, context, context_mark, "did not find URI escaped octet") } func trace(args ...interface{}) func() { pargs := append([]interface{}{"+++"}, args...) fmt.Println(pargs...) pargs = append([]interface{}{"---"}, args...) return func() { fmt.Println(pargs...) } } // Ensure that the tokens queue contains at least one token which can be // returned to the Parser. func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { // While we need more tokens to fetch, do it. for { // Check if we really need to fetch more tokens. need_more_tokens := false if parser.tokens_head == len(parser.tokens) { // Queue is empty. need_more_tokens = true } else { // Check if any potential simple key may occupy the head position. if !yaml_parser_stale_simple_keys(parser) { return false } for i := range parser.simple_keys { simple_key := &parser.simple_keys[i] if simple_key.possible && simple_key.token_number == parser.tokens_parsed { need_more_tokens = true break } } } // We are finished. if !need_more_tokens { break } // Fetch the next token. if !yaml_parser_fetch_next_token(parser) { return false } } parser.token_available = true return true } // The dispatcher for token fetchers. func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { // Ensure that the buffer is initialized. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } // Check if we just started scanning. Fetch STREAM-START then. if !parser.stream_start_produced { return yaml_parser_fetch_stream_start(parser) } // Eat whitespaces and comments until we reach the next token. if !yaml_parser_scan_to_next_token(parser) { return false } // Remove obsolete potential simple keys. if !yaml_parser_stale_simple_keys(parser) { return false } // Check the indentation level against the current column. if !yaml_parser_unroll_indent(parser, parser.mark.column) { return false } // Ensure that the buffer contains at least 4 characters. 4 is the length // of the longest indicators ('--- ' and '... '). if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { return false } // Is it the end of the stream? if is_z(parser.buffer, parser.buffer_pos) { return yaml_parser_fetch_stream_end(parser) } // Is it a directive? if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' { return yaml_parser_fetch_directive(parser) } buf := parser.buffer pos := parser.buffer_pos // Is it the document start indicator? if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) { return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN) } // Is it the document end indicator? if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) { return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) } // Is it the flow sequence start indicator? if buf[pos] == '[' { return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) } // Is it the flow mapping start indicator? if parser.buffer[parser.buffer_pos] == '{' { return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN) } // Is it the flow sequence end indicator? if parser.buffer[parser.buffer_pos] == ']' { return yaml_parser_fetch_flow_collection_end(parser, yaml_FLOW_SEQUENCE_END_TOKEN) } // Is it the flow mapping end indicator? if parser.buffer[parser.buffer_pos] == '}' { return yaml_parser_fetch_flow_collection_end(parser, yaml_FLOW_MAPPING_END_TOKEN) } // Is it the flow entry indicator? if parser.buffer[parser.buffer_pos] == ',' { return yaml_parser_fetch_flow_entry(parser) } // Is it the block entry indicator? if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) { return yaml_parser_fetch_block_entry(parser) } // Is it the key indicator? if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { return yaml_parser_fetch_key(parser) } // Is it the value indicator? if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { return yaml_parser_fetch_value(parser) } // Is it an alias? if parser.buffer[parser.buffer_pos] == '*' { return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN) } // Is it an anchor? if parser.buffer[parser.buffer_pos] == '&' { return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN) } // Is it a tag? if parser.buffer[parser.buffer_pos] == '!' { return yaml_parser_fetch_tag(parser) } // Is it a literal scalar? if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 { return yaml_parser_fetch_block_scalar(parser, true) } // Is it a folded scalar? if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 { return yaml_parser_fetch_block_scalar(parser, false) } // Is it a single-quoted scalar? if parser.buffer[parser.buffer_pos] == '\'' { return yaml_parser_fetch_flow_scalar(parser, true) } // Is it a double-quoted scalar? if parser.buffer[parser.buffer_pos] == '"' { return yaml_parser_fetch_flow_scalar(parser, false) } // Is it a plain scalar? // // A plain scalar may start with any non-blank characters except // // '-', '?', ':', ',', '[', ']', '{', '}', // '#', '&', '*', '!', '|', '>', '\'', '\"', // '%', '@', '`'. // // In the block context (and, for the '-' indicator, in the flow context // too), it may also start with the characters // // '-', '?', ':' // // if it is followed by a non-space character. // // The last rule is more restrictive than the specification requires. // [Go] Make this logic more reasonable. //switch parser.buffer[parser.buffer_pos] { //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': //} if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' || parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' || parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' || parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') || (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) || (parser.flow_level == 0 && (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') && !is_blankz(parser.buffer, parser.buffer_pos+1)) { return yaml_parser_fetch_plain_scalar(parser) } // If we don't determine the token type so far, it is an error. return yaml_parser_set_scanner_error(parser, "while scanning for the next token", parser.mark, "found character that cannot start any token") } // Check the list of potential simple keys and remove the positions that // cannot contain simple keys anymore. func yaml_parser_stale_simple_keys(parser *yaml_parser_t) bool { // Check for a potential simple key for each flow level. for i := range parser.simple_keys { simple_key := &parser.simple_keys[i] // The specification requires that a simple key // // - is limited to a single line, // - is shorter than 1024 characters. if simple_key.possible && (simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index) { // Check if the potential simple key to be removed is required. if simple_key.required { return yaml_parser_set_scanner_error(parser, "while scanning a simple key", simple_key.mark, "could not find expected ':'") } simple_key.possible = false } } return true } // Check if a simple key may start at the current position and add it if // needed. func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { // A simple key is required at the current position if the scanner is in // the block context and the current column coincides with the indentation // level. required := parser.flow_level == 0 && parser.indent == parser.mark.column // A simple key is required only when it is the first token in the current // line. Therefore it is always allowed. But we add a check anyway. if required && !parser.simple_key_allowed { panic("should not happen") } // // If the current position may start a simple key, save it. // if parser.simple_key_allowed { simple_key := yaml_simple_key_t{ possible: true, required: required, token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), } simple_key.mark = parser.mark if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_keys[len(parser.simple_keys)-1] = simple_key } return true } // Remove a potential simple key at the current flow level. func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { i := len(parser.simple_keys) - 1 if parser.simple_keys[i].possible { // If the key is required, it is an error. if parser.simple_keys[i].required { return yaml_parser_set_scanner_error(parser, "while scanning a simple key", parser.simple_keys[i].mark, "could not find expected ':'") } } // Remove the key from the stack. parser.simple_keys[i].possible = false return true } // Increase the flow level and resize the simple key list if needed. func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { // Reset the simple key on the next level. parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) // Increase the flow level. parser.flow_level++ return true } // Decrease the flow level. func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { if parser.flow_level > 0 { parser.flow_level-- parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1] } return true } // Push the current indentation level to the stack and set the new level // the current column is greater than the indentation level. In this case, // append or insert the specified token into the token queue. func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool { // In the flow context, do nothing. if parser.flow_level > 0 { return true } if parser.indent < column { // Push the current indentation level to the stack and set the new // indentation level. parser.indents = append(parser.indents, parser.indent) parser.indent = column // Create a token and insert it into the queue. token := yaml_token_t{ typ: typ, start_mark: mark, end_mark: mark, } if number > -1 { number -= parser.tokens_parsed } yaml_insert_token(parser, number, &token) } return true } // Pop indentation levels from the indents stack until the current level // becomes less or equal to the column. For each intendation level, append // the BLOCK-END token. func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool { // In the flow context, do nothing. if parser.flow_level > 0 { return true } // Loop through the intendation levels in the stack. for parser.indent > column { // Create a token and append it to the queue. token := yaml_token_t{ typ: yaml_BLOCK_END_TOKEN, start_mark: parser.mark, end_mark: parser.mark, } yaml_insert_token(parser, -1, &token) // Pop the indentation level. parser.indent = parser.indents[len(parser.indents)-1] parser.indents = parser.indents[:len(parser.indents)-1] } return true } // Initialize the scanner and produce the STREAM-START token. func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { // Set the initial indentation. parser.indent = -1 // Initialize the simple key stack. parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) // A simple key is allowed at the beginning of the stream. parser.simple_key_allowed = true // We have started. parser.stream_start_produced = true // Create the STREAM-START token and append it to the queue. token := yaml_token_t{ typ: yaml_STREAM_START_TOKEN, start_mark: parser.mark, end_mark: parser.mark, encoding: parser.encoding, } yaml_insert_token(parser, -1, &token) return true } // Produce the STREAM-END token and shut down the scanner. func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { // Force new line. if parser.mark.column != 0 { parser.mark.column = 0 parser.mark.line++ } // Reset the indentation level. if !yaml_parser_unroll_indent(parser, -1) { return false } // Reset simple keys. if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_key_allowed = false // Create the STREAM-END token and append it to the queue. token := yaml_token_t{ typ: yaml_STREAM_END_TOKEN, start_mark: parser.mark, end_mark: parser.mark, } yaml_insert_token(parser, -1, &token) return true } // Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { // Reset the indentation level. if !yaml_parser_unroll_indent(parser, -1) { return false } // Reset simple keys. if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_key_allowed = false // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. token := yaml_token_t{} if !yaml_parser_scan_directive(parser, &token) { return false } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the DOCUMENT-START or DOCUMENT-END token. func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { // Reset the indentation level. if !yaml_parser_unroll_indent(parser, -1) { return false } // Reset simple keys. if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_key_allowed = false // Consume the token. start_mark := parser.mark skip(parser) skip(parser) skip(parser) end_mark := parser.mark // Create the DOCUMENT-START or DOCUMENT-END token. token := yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { // The indicators '[' and '{' may start a simple key. if !yaml_parser_save_simple_key(parser) { return false } // Increase the flow level. if !yaml_parser_increase_flow_level(parser) { return false } // A simple key may follow the indicators '[' and '{'. parser.simple_key_allowed = true // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. token := yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool { // Reset any potential simple key on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Decrease the flow level. if !yaml_parser_decrease_flow_level(parser) { return false } // No simple keys after the indicators ']' and '}'. parser.simple_key_allowed = false // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. token := yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the FLOW-ENTRY token. func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool { // Reset any potential simple keys on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Simple keys are allowed after ','. parser.simple_key_allowed = true // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the FLOW-ENTRY token and append it to the queue. token := yaml_token_t{ typ: yaml_FLOW_ENTRY_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the BLOCK-ENTRY token. func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool { // Check if the scanner is in the block context. if parser.flow_level == 0 { // Check if we are allowed to start a new entry. if !parser.simple_key_allowed { return yaml_parser_set_scanner_error(parser, "", parser.mark, "block sequence entries are not allowed in this context") } // Add the BLOCK-SEQUENCE-START token if needed. if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) { return false } } else { // It is an error for the '-' indicator to occur in the flow context, // but we let the Parser detect and report about it because the Parser // is able to point to the context. } // Reset any potential simple keys on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Simple keys are allowed after '-'. parser.simple_key_allowed = true // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the BLOCK-ENTRY token and append it to the queue. token := yaml_token_t{ typ: yaml_BLOCK_ENTRY_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the KEY token. func yaml_parser_fetch_key(parser *yaml_parser_t) bool { // In the block context, additional checks are required. if parser.flow_level == 0 { // Check if we are allowed to start a new key (not nessesary simple). if !parser.simple_key_allowed { return yaml_parser_set_scanner_error(parser, "", parser.mark, "mapping keys are not allowed in this context") } // Add the BLOCK-MAPPING-START token if needed. if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { return false } } // Reset any potential simple keys on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Simple keys are allowed after '?' in the block context. parser.simple_key_allowed = parser.flow_level == 0 // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the KEY token and append it to the queue. token := yaml_token_t{ typ: yaml_KEY_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the VALUE token. func yaml_parser_fetch_value(parser *yaml_parser_t) bool { simple_key := &parser.simple_keys[len(parser.simple_keys)-1] // Have we found a simple key? if simple_key.possible { // Create the KEY token and insert it into the queue. token := yaml_token_t{ typ: yaml_KEY_TOKEN, start_mark: simple_key.mark, end_mark: simple_key.mark, } yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token) // In the block context, we may need to add the BLOCK-MAPPING-START token. if !yaml_parser_roll_indent(parser, simple_key.mark.column, simple_key.token_number, yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) { return false } // Remove the simple key. simple_key.possible = false // A simple key cannot follow another simple key. parser.simple_key_allowed = false } else { // The ':' indicator follows a complex key. // In the block context, extra checks are required. if parser.flow_level == 0 { // Check if we are allowed to start a complex value. if !parser.simple_key_allowed { return yaml_parser_set_scanner_error(parser, "", parser.mark, "mapping values are not allowed in this context") } // Add the BLOCK-MAPPING-START token if needed. if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { return false } } // Simple keys after ':' are allowed in the block context. parser.simple_key_allowed = parser.flow_level == 0 } // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the VALUE token and append it to the queue. token := yaml_token_t{ typ: yaml_VALUE_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the ALIAS or ANCHOR token. func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool { // An anchor or an alias could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow an anchor or an alias. parser.simple_key_allowed = false // Create the ALIAS or ANCHOR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_anchor(parser, &token, typ) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the TAG token. func yaml_parser_fetch_tag(parser *yaml_parser_t) bool { // A tag could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow a tag. parser.simple_key_allowed = false // Create the TAG token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_tag(parser, &token) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool { // Remove any potential simple keys. if !yaml_parser_remove_simple_key(parser) { return false } // A simple key may follow a block scalar. parser.simple_key_allowed = true // Create the SCALAR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_block_scalar(parser, &token, literal) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool { // A plain scalar could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow a flow scalar. parser.simple_key_allowed = false // Create the SCALAR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_flow_scalar(parser, &token, single) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the SCALAR(...,plain) token. func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { // A plain scalar could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow a flow scalar. parser.simple_key_allowed = false // Create the SCALAR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_plain_scalar(parser, &token) { return false } yaml_insert_token(parser, -1, &token) return true } // Eat whitespaces and comments until the next token is found. func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { // Until the next token is not found. for { // Allow the BOM mark to start a line. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) { skip(parser) } // Eat whitespaces. // Tabs are allowed: // - in the flow context // - in the block context, but not at the beginning of the line or // after '-', '?', or ':' (complex value). if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Eat a comment until a line break. if parser.buffer[parser.buffer_pos] == '#' { for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } } // If it is a line break, eat it. if is_break(parser.buffer, parser.buffer_pos) { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) // In the block context, a new line may start a simple key. if parser.flow_level == 0 { parser.simple_key_allowed = true } } else { break // We have found a token. } } return true } // Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. // // Scope: // %YAML 1.1 # a comment \n // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // %TAG !yaml! tag:yaml.org,2002: \n // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { // Eat '%'. start_mark := parser.mark skip(parser) // Scan the directive name. var name []byte if !yaml_parser_scan_directive_name(parser, start_mark, &name) { return false } // Is it a YAML directive? if bytes.Equal(name, []byte("YAML")) { // Scan the VERSION directive value. var major, minor int8 if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) { return false } end_mark := parser.mark // Create a VERSION-DIRECTIVE token. *token = yaml_token_t{ typ: yaml_VERSION_DIRECTIVE_TOKEN, start_mark: start_mark, end_mark: end_mark, major: major, minor: minor, } // Is it a TAG directive? } else if bytes.Equal(name, []byte("TAG")) { // Scan the TAG directive value. var handle, prefix []byte if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) { return false } end_mark := parser.mark // Create a TAG-DIRECTIVE token. *token = yaml_token_t{ typ: yaml_TAG_DIRECTIVE_TOKEN, start_mark: start_mark, end_mark: end_mark, value: handle, prefix: prefix, } // Unknown directive. } else { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "found uknown directive name") return false } // Eat the rest of the line including any comments. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } if parser.buffer[parser.buffer_pos] == '#' { for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } } // Check if we are at the end of the line. if !is_breakz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "did not find expected comment or line break") return false } // Eat a line break. if is_break(parser.buffer, parser.buffer_pos) { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) } return true } // Scan the directive name. // // Scope: // %YAML 1.1 # a comment \n // ^^^^ // %TAG !yaml! tag:yaml.org,2002: \n // ^^^ // func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { // Consume the directive name. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } var s []byte for is_alpha(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the name is empty. if len(s) == 0 { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "could not find expected directive name") return false } // Check for an blank character after the name. if !is_blankz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "found unexpected non-alphabetical character") return false } *name = s return true } // Scan the value of VERSION-DIRECTIVE. // // Scope: // %YAML 1.1 # a comment \n // ^^^^^^ func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { // Eat whitespaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Consume the major version number. if !yaml_parser_scan_version_directive_number(parser, start_mark, major) { return false } // Eat '.'. if parser.buffer[parser.buffer_pos] != '.' { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "did not find expected digit or '.' character") } skip(parser) // Consume the minor version number. if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) { return false } return true } const max_number_length = 2 // Scan the version number of VERSION-DIRECTIVE. // // Scope: // %YAML 1.1 # a comment \n // ^ // %YAML 1.1 # a comment \n // ^ func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { // Repeat while the next character is digit. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } var value, length int8 for is_digit(parser.buffer, parser.buffer_pos) { // Check if the number is too long. length++ if length > max_number_length { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "found extremely long version number") } value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos)) skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the number was present. if length == 0 { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "did not find expected version number") } *number = value return true } // Scan the value of a TAG-DIRECTIVE token. // // Scope: // %TAG !yaml! tag:yaml.org,2002: \n // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { var handle_value, prefix_value []byte // Eat whitespaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Scan a handle. if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) { return false } // Expect a whitespace. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if !is_blank(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", start_mark, "did not find expected whitespace") return false } // Eat whitespaces. for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Scan a prefix. if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) { return false } // Expect a whitespace or line break. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if !is_blankz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", start_mark, "did not find expected whitespace or line break") return false } *handle = handle_value *prefix = prefix_value return true } func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool { var s []byte // Eat the indicator character. start_mark := parser.mark skip(parser) // Consume the value. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_alpha(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } end_mark := parser.mark /* * Check if length of the anchor is greater than 0 and it is followed by * a whitespace character or one of the indicators: * * '?', ':', ',', ']', '}', '%', '@', '`'. */ if len(s) == 0 || !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') { context := "while scanning an alias" if typ == yaml_ANCHOR_TOKEN { context = "while scanning an anchor" } yaml_parser_set_scanner_error(parser, context, start_mark, "did not find expected alphabetic or numeric character") return false } // Create a token. *token = yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, value: s, } return true } /* * Scan a TAG token. */ func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool { var handle, suffix []byte start_mark := parser.mark // Check if the tag is in the canonical form. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } if parser.buffer[parser.buffer_pos+1] == '<' { // Keep the handle as '' // Eat '!<' skip(parser) skip(parser) // Consume the tag value. if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { return false } // Check for '>' and eat it. if parser.buffer[parser.buffer_pos] != '>' { yaml_parser_set_scanner_error(parser, "while scanning a tag", start_mark, "did not find the expected '>'") return false } skip(parser) } else { // The tag has either the '!suffix' or the '!handle!suffix' form. // First, try to scan a handle. if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) { return false } // Check if it is, indeed, handle. if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' { // Scan the suffix now. if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { return false } } else { // It wasn't a handle after all. Scan the rest of the tag. if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) { return false } // Set the handle to '!'. handle = []byte{'!'} // A special case: the '!' tag. Set the handle to '' and the // suffix to '!'. if len(suffix) == 0 { handle, suffix = suffix, handle } } } // Check the character which ends the tag. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if !is_blankz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a tag", start_mark, "did not find expected whitespace or line break") return false } end_mark := parser.mark // Create a token. *token = yaml_token_t{ typ: yaml_TAG_TOKEN, start_mark: start_mark, end_mark: end_mark, value: handle, suffix: suffix, } return true } // Scan a tag handle. func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool { // Check the initial '!' character. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if parser.buffer[parser.buffer_pos] != '!' { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected '!'") return false } var s []byte // Copy the '!' character. s = read(parser, s) // Copy all subsequent alphabetical and numerical characters. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_alpha(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the trailing character is '!' and copy it. if parser.buffer[parser.buffer_pos] == '!' { s = read(parser, s) } else { // It's either the '!' tag or not really a tag handle. If it's a %TAG // directive, it's an error. If it's a tag token, it must be a part of URI. if directive && !(s[0] == '!' && s[1] == 0) { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected '!'") return false } } *handle = s return true } // Scan a tag. func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { //size_t length = head ? strlen((char *)head) : 0 var s []byte // Copy the head if needed. // // Note that we don't copy the leading '!' character. if len(head) > 1 { s = append(s, head[1:]...) } // Scan the tag. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } // The set of characters that may appear in URI is as follows: // // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', // '%'. // [Go] Convert this into more reasonable logic. for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' || parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' || parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' || parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' || parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' || parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' || parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '%' { // Check if it is a URI-escape sequence. if parser.buffer[parser.buffer_pos] == '%' { if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) { return false } } else { s = read(parser, s) } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the tag is non-empty. if len(s) == 0 { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected tag URI") return false } *uri = s return true } // Decode an URI-escape sequence corresponding to a single UTF-8 character. func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool { // Decode the required number of characters. w := 1024 for w > 0 { // Check for a URI-escaped octet. if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { return false } if !(parser.buffer[parser.buffer_pos] == '%' && is_hex(parser.buffer, parser.buffer_pos+1) && is_hex(parser.buffer, parser.buffer_pos+2)) { return yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find URI escaped octet") } // Get the octet. octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2)) // If it is the leading octet, determine the length of the UTF-8 sequence. if w == 1024 { w = width(octet) if w == 0 { return yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "found an incorrect leading UTF-8 octet") } } else { // Check if the trailing octet is correct. if octet&0xC0 != 0x80 { return yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "found an incorrect trailing UTF-8 octet") } } // Copy the octet and move the pointers. *s = append(*s, octet) skip(parser) skip(parser) skip(parser) w-- } return true } // Scan a block scalar. func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool { // Eat the indicator '|' or '>'. start_mark := parser.mark skip(parser) // Scan the additional block scalar indicators. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } // Check for a chomping indicator. var chomping, increment int if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { // Set the chomping method and eat the indicator. if parser.buffer[parser.buffer_pos] == '+' { chomping = +1 } else { chomping = -1 } skip(parser) // Check for an indentation indicator. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if is_digit(parser.buffer, parser.buffer_pos) { // Check that the intendation is greater than 0. if parser.buffer[parser.buffer_pos] == '0' { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found an intendation indicator equal to 0") return false } // Get the intendation level and eat the indicator. increment = as_digit(parser.buffer, parser.buffer_pos) skip(parser) } } else if is_digit(parser.buffer, parser.buffer_pos) { // Do the same as above, but in the opposite order. if parser.buffer[parser.buffer_pos] == '0' { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found an intendation indicator equal to 0") return false } increment = as_digit(parser.buffer, parser.buffer_pos) skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { if parser.buffer[parser.buffer_pos] == '+' { chomping = +1 } else { chomping = -1 } skip(parser) } } // Eat whitespaces and comments to the end of the line. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } if parser.buffer[parser.buffer_pos] == '#' { for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } } // Check if we are at the end of the line. if !is_breakz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "did not find expected comment or line break") return false } // Eat a line break. if is_break(parser.buffer, parser.buffer_pos) { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) } end_mark := parser.mark // Set the intendation level if it was specified. var indent int if increment > 0 { if parser.indent >= 0 { indent = parser.indent + increment } else { indent = increment } } // Scan the leading line breaks and determine the indentation level if needed. var s, leading_break, trailing_breaks []byte if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { return false } // Scan the block scalar content. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } var leading_blank, trailing_blank bool for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { // We are at the beginning of a non-empty line. // Is it a trailing whitespace? trailing_blank = is_blank(parser.buffer, parser.buffer_pos) // Check if we need to fold the leading line break. if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' { // Do we need to join the lines by space? if len(trailing_breaks) == 0 { s = append(s, ' ') } } else { s = append(s, leading_break...) } leading_break = leading_break[:0] // Append the remaining line breaks. s = append(s, trailing_breaks...) trailing_breaks = trailing_breaks[:0] // Is it a leading whitespace? leading_blank = is_blank(parser.buffer, parser.buffer_pos) // Consume the current line. for !is_breakz(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Consume the line break. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } leading_break = read_line(parser, leading_break) // Eat the following intendation spaces and line breaks. if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { return false } } // Chomp the tail. if chomping != -1 { s = append(s, leading_break...) } if chomping == 1 { s = append(s, trailing_breaks...) } // Create a token. *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, end_mark: end_mark, value: s, style: yaml_LITERAL_SCALAR_STYLE, } if !literal { token.style = yaml_FOLDED_SCALAR_STYLE } return true } // Scan intendation spaces and line breaks for a block scalar. Determine the // intendation level if needed. func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { *end_mark = parser.mark // Eat the intendation spaces and line breaks. max_indent := 0 for { // Eat the intendation spaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } if parser.mark.column > max_indent { max_indent = parser.mark.column } // Check for a tab character messing the intendation. if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found a tab character where an intendation space is expected") } // Have we found a non-empty line? if !is_break(parser.buffer, parser.buffer_pos) { break } // Consume the line break. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } // [Go] Should really be returning breaks instead. *breaks = read_line(parser, *breaks) *end_mark = parser.mark } // Determine the indentation level if needed. if *indent == 0 { *indent = max_indent if *indent < parser.indent+1 { *indent = parser.indent + 1 } if *indent < 1 { *indent = 1 } } return true } // Scan a quoted scalar. func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool { // Eat the left quote. start_mark := parser.mark skip(parser) // Consume the content of the quoted scalar. var s, leading_break, trailing_breaks, whitespaces []byte for { // Check that there are no document indicators at the beginning of the line. if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { return false } if parser.mark.column == 0 && ((parser.buffer[parser.buffer_pos+0] == '-' && parser.buffer[parser.buffer_pos+1] == '-' && parser.buffer[parser.buffer_pos+2] == '-') || (parser.buffer[parser.buffer_pos+0] == '.' && parser.buffer[parser.buffer_pos+1] == '.' && parser.buffer[parser.buffer_pos+2] == '.')) && is_blankz(parser.buffer, parser.buffer_pos+3) { yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", start_mark, "found unexpected document indicator") return false } // Check for EOF. if is_z(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", start_mark, "found unexpected end of stream") return false } // Consume non-blank characters. leading_blanks := false for !is_blankz(parser.buffer, parser.buffer_pos) { if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' { // Is is an escaped single quote. s = append(s, '\'') skip(parser) skip(parser) } else if single && parser.buffer[parser.buffer_pos] == '\'' { // It is a right single quote. break } else if !single && parser.buffer[parser.buffer_pos] == '"' { // It is a right double quote. break } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) { // It is an escaped line break. if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { return false } skip(parser) skip_line(parser) leading_blanks = true break } else if !single && parser.buffer[parser.buffer_pos] == '\\' { // It is an escape sequence. code_length := 0 // Check the escape character. switch parser.buffer[parser.buffer_pos+1] { case '0': s = append(s, 0) case 'a': s = append(s, '\x07') case 'b': s = append(s, '\x08') case 't', '\t': s = append(s, '\x09') case 'n': s = append(s, '\x0A') case 'v': s = append(s, '\x0B') case 'f': s = append(s, '\x0C') case 'r': s = append(s, '\x0D') case 'e': s = append(s, '\x1B') case ' ': s = append(s, '\x20') case '"': s = append(s, '"') case '\'': s = append(s, '\'') case '\\': s = append(s, '\\') case 'N': // NEL (#x85) s = append(s, '\xC2') s = append(s, '\x85') case '_': // #xA0 s = append(s, '\xC2') s = append(s, '\xA0') case 'L': // LS (#x2028) s = append(s, '\xE2') s = append(s, '\x80') s = append(s, '\xA8') case 'P': // PS (#x2029) s = append(s, '\xE2') s = append(s, '\x80') s = append(s, '\xA9') case 'x': code_length = 2 case 'u': code_length = 4 case 'U': code_length = 8 default: yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "found unknown escape character") return false } skip(parser) skip(parser) // Consume an arbitrary escape code. if code_length > 0 { var value int // Scan the character value. if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) { return false } for k := 0; k < code_length; k++ { if !is_hex(parser.buffer, parser.buffer_pos+k) { yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "did not find expected hexdecimal number") return false } value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k) } // Check the value and write the character. if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF { yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "found invalid Unicode character escape code") return false } if value <= 0x7F { s = append(s, byte(value)) } else if value <= 0x7FF { s = append(s, byte(0xC0+(value>>6))) s = append(s, byte(0x80+(value&0x3F))) } else if value <= 0xFFFF { s = append(s, byte(0xE0+(value>>12))) s = append(s, byte(0x80+((value>>6)&0x3F))) s = append(s, byte(0x80+(value&0x3F))) } else { s = append(s, byte(0xF0+(value>>18))) s = append(s, byte(0x80+((value>>12)&0x3F))) s = append(s, byte(0x80+((value>>6)&0x3F))) s = append(s, byte(0x80+(value&0x3F))) } // Advance the pointer. for k := 0; k < code_length; k++ { skip(parser) } } } else { // It is a non-escaped non-blank character. s = read(parser, s) } if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } } // Check if we are at the end of the scalar. if single { if parser.buffer[parser.buffer_pos] == '\'' { break } } else { if parser.buffer[parser.buffer_pos] == '"' { break } } // Consume blank characters. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { if is_blank(parser.buffer, parser.buffer_pos) { // Consume a space or a tab character. if !leading_blanks { whitespaces = read(parser, whitespaces) } else { skip(parser) } } else { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } // Check if it is a first line break. if !leading_blanks { whitespaces = whitespaces[:0] leading_break = read_line(parser, leading_break) leading_blanks = true } else { trailing_breaks = read_line(parser, trailing_breaks) } } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Join the whitespaces or fold line breaks. if leading_blanks { // Do we need to fold line breaks? if len(leading_break) > 0 && leading_break[0] == '\n' { if len(trailing_breaks) == 0 { s = append(s, ' ') } else { s = append(s, trailing_breaks...) } } else { s = append(s, leading_break...) s = append(s, trailing_breaks...) } trailing_breaks = trailing_breaks[:0] leading_break = leading_break[:0] } else { s = append(s, whitespaces...) whitespaces = whitespaces[:0] } } // Eat the right quote. skip(parser) end_mark := parser.mark // Create a token. *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, end_mark: end_mark, value: s, style: yaml_SINGLE_QUOTED_SCALAR_STYLE, } if !single { token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } return true } // Scan a plain scalar. func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool { var s, leading_break, trailing_breaks, whitespaces []byte var leading_blanks bool var indent = parser.indent + 1 start_mark := parser.mark end_mark := parser.mark // Consume the content of the plain scalar. for { // Check for a document indicator. if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { return false } if parser.mark.column == 0 && ((parser.buffer[parser.buffer_pos+0] == '-' && parser.buffer[parser.buffer_pos+1] == '-' && parser.buffer[parser.buffer_pos+2] == '-') || (parser.buffer[parser.buffer_pos+0] == '.' && parser.buffer[parser.buffer_pos+1] == '.' && parser.buffer[parser.buffer_pos+2] == '.')) && is_blankz(parser.buffer, parser.buffer_pos+3) { break } // Check for a comment. if parser.buffer[parser.buffer_pos] == '#' { break } // Consume non-blank characters. for !is_blankz(parser.buffer, parser.buffer_pos) { // Check for 'x:x' in the flow context. TODO: Fix the test "spec-08-13". if parser.flow_level > 0 && parser.buffer[parser.buffer_pos] == ':' && !is_blankz(parser.buffer, parser.buffer_pos+1) { yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", start_mark, "found unexpected ':'") return false } // Check for indicators that may end a plain scalar. if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) || (parser.flow_level > 0 && (parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || parser.buffer[parser.buffer_pos] == '}')) { break } // Check if we need to join whitespaces and breaks. if leading_blanks || len(whitespaces) > 0 { if leading_blanks { // Do we need to fold line breaks? if leading_break[0] == '\n' { if len(trailing_breaks) == 0 { s = append(s, ' ') } else { s = append(s, trailing_breaks...) } } else { s = append(s, leading_break...) s = append(s, trailing_breaks...) } trailing_breaks = trailing_breaks[:0] leading_break = leading_break[:0] leading_blanks = false } else { s = append(s, whitespaces...) whitespaces = whitespaces[:0] } } // Copy the character. s = read(parser, s) end_mark = parser.mark if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } } // Is it the end? if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) { break } // Consume blank characters. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { if is_blank(parser.buffer, parser.buffer_pos) { // Check for tab character that abuse intendation. if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", start_mark, "found a tab character that violate intendation") return false } // Consume a space or a tab character. if !leading_blanks { whitespaces = read(parser, whitespaces) } else { skip(parser) } } else { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } // Check if it is a first line break. if !leading_blanks { whitespaces = whitespaces[:0] leading_break = read_line(parser, leading_break) leading_blanks = true } else { trailing_breaks = read_line(parser, trailing_breaks) } } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check intendation level. if parser.flow_level == 0 && parser.mark.column < indent { break } } // Create a token. *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, end_mark: end_mark, value: s, style: yaml_PLAIN_SCALAR_STYLE, } // Note that we change the 'simple_key_allowed' flag. if leading_blanks { parser.simple_key_allowed = true } return true } �����������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/Makefile��������������������������������������������������0000644�0000153�0000161�00000001124�12321735675�021716� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������include $(GOROOT)/src/Make.inc YAML=yaml-0.1.3 LIBYAML=$(PWD)/$(YAML)/src/.libs/libyaml.a TARG=launchpad.net/goyaml GOFILES=\ goyaml.go\ resolve.go\ CGOFILES=\ decode.go\ encode.go\ CGO_OFILES+=\ helpers.o\ api.o\ scanner.o\ reader.o\ parser.o\ writer.o\ emitter.o\ GOFMT=gofmt BADFMT:=$(shell $(GOFMT) -l $(GOFILES) $(CGOFILES) $(wildcard *_test.go)) all: package gofmt: $(BADFMT) @for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done include $(GOROOT)/src/Make.pkg ifneq ($(BADFMT),) ifneq ($(MAKECMDGOALS),gofmt) $(warning WARNING: make gofmt: $(BADFMT)) endif endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/decode.go�������������������������������������������������0000644�0000153�0000161�00000024440�12321735675�022036� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml import ( "reflect" "strconv" ) const ( documentNode = 1 << iota mappingNode sequenceNode scalarNode aliasNode ) type node struct { kind int line, column int tag string value string implicit bool children []*node anchors map[string]*node } // ---------------------------------------------------------------------------- // Parser, produces a node tree out of a libyaml event stream. type parser struct { parser yaml_parser_t event yaml_event_t doc *node } func newParser(b []byte) *parser { p := parser{} if !yaml_parser_initialize(&p.parser) { panic("Failed to initialize YAML emitter") } if len(b) == 0 { b = []byte{'\n'} } yaml_parser_set_input_string(&p.parser, b) p.skip() if p.event.typ != yaml_STREAM_START_EVENT { panic("Expected stream start event, got " + strconv.Itoa(int(p.event.typ))) } p.skip() return &p } func (p *parser) destroy() { if p.event.typ != yaml_NO_EVENT { yaml_event_delete(&p.event) } yaml_parser_delete(&p.parser) } func (p *parser) skip() { if p.event.typ != yaml_NO_EVENT { if p.event.typ == yaml_STREAM_END_EVENT { panic("Attempted to go past the end of stream. Corrupted value?") } yaml_event_delete(&p.event) } if !yaml_parser_parse(&p.parser, &p.event) { p.fail() } } func (p *parser) fail() { var where string var line int if p.parser.problem_mark.line != 0 { line = p.parser.problem_mark.line } else if p.parser.context_mark.line != 0 { line = p.parser.context_mark.line } if line != 0 { where = "line " + strconv.Itoa(line) + ": " } var msg string if len(p.parser.problem) > 0 { msg = p.parser.problem } else { msg = "Unknown problem parsing YAML content" } panic(where + msg) } func (p *parser) anchor(n *node, anchor []byte) { if anchor != nil { p.doc.anchors[string(anchor)] = n } } func (p *parser) parse() *node { switch p.event.typ { case yaml_SCALAR_EVENT: return p.scalar() case yaml_ALIAS_EVENT: return p.alias() case yaml_MAPPING_START_EVENT: return p.mapping() case yaml_SEQUENCE_START_EVENT: return p.sequence() case yaml_DOCUMENT_START_EVENT: return p.document() case yaml_STREAM_END_EVENT: // Happens when attempting to decode an empty buffer. return nil default: panic("Attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ))) } panic("Unreachable") } func (p *parser) node(kind int) *node { return &node{ kind: kind, line: p.event.start_mark.line, column: p.event.start_mark.column, } } func (p *parser) document() *node { n := p.node(documentNode) n.anchors = make(map[string]*node) p.doc = n p.skip() n.children = append(n.children, p.parse()) if p.event.typ != yaml_DOCUMENT_END_EVENT { panic("Expected end of document event but got " + strconv.Itoa(int(p.event.typ))) } p.skip() return n } func (p *parser) alias() *node { n := p.node(aliasNode) n.value = string(p.event.anchor) p.skip() return n } func (p *parser) scalar() *node { n := p.node(scalarNode) n.value = string(p.event.value) n.tag = string(p.event.tag) n.implicit = p.event.implicit p.anchor(n, p.event.anchor) p.skip() return n } func (p *parser) sequence() *node { n := p.node(sequenceNode) p.anchor(n, p.event.anchor) p.skip() for p.event.typ != yaml_SEQUENCE_END_EVENT { n.children = append(n.children, p.parse()) } p.skip() return n } func (p *parser) mapping() *node { n := p.node(mappingNode) p.anchor(n, p.event.anchor) p.skip() for p.event.typ != yaml_MAPPING_END_EVENT { n.children = append(n.children, p.parse(), p.parse()) } p.skip() return n } // ---------------------------------------------------------------------------- // Decoder, unmarshals a node into a provided value. type decoder struct { doc *node aliases map[string]bool } func newDecoder() *decoder { d := &decoder{} d.aliases = make(map[string]bool) return d } // d.setter deals with setters and pointer dereferencing and initialization. // // It's a slightly convoluted case to handle properly: // // - nil pointers should be initialized, unless being set to nil // - we don't know at this point yet what's the value to SetYAML() with. // - we can't separate pointer deref/init and setter checking, because // a setter may be found while going down a pointer chain. // // Thus, here is how it takes care of it: // // - out is provided as a pointer, so that it can be replaced. // - when looking at a non-setter ptr, *out=ptr.Elem(), unless tag=!!null // - when a setter is found, *out=interface{}, and a set() function is // returned to call SetYAML() with the value of *out once it's defined. // func (d *decoder) setter(tag string, out *reflect.Value, good *bool) (set func()) { again := true for again { again = false setter, _ := (*out).Interface().(Setter) if tag != "!!null" || setter != nil { if pv := (*out); pv.Kind() == reflect.Ptr { if pv.IsNil() { *out = reflect.New(pv.Type().Elem()).Elem() pv.Set((*out).Addr()) } else { *out = pv.Elem() } setter, _ = pv.Interface().(Setter) again = true } } if setter != nil { var arg interface{} *out = reflect.ValueOf(&arg).Elem() return func() { *good = setter.SetYAML(tag, arg) } } } return nil } func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { switch n.kind { case documentNode: good = d.document(n, out) case scalarNode: good = d.scalar(n, out) case aliasNode: good = d.alias(n, out) case mappingNode: good = d.mapping(n, out) case sequenceNode: good = d.sequence(n, out) default: panic("Internal error: unknown node kind: " + strconv.Itoa(n.kind)) } return } func (d *decoder) document(n *node, out reflect.Value) (good bool) { if len(n.children) == 1 { d.doc = n d.unmarshal(n.children[0], out) return true } return false } func (d *decoder) alias(n *node, out reflect.Value) (good bool) { an, ok := d.doc.anchors[n.value] if !ok { panic("Unknown anchor '" + n.value + "' referenced") } if d.aliases[n.value] { panic("Anchor '" + n.value + "' value contains itself") } d.aliases[n.value] = true good = d.unmarshal(an, out) delete(d.aliases, n.value) return good } func (d *decoder) scalar(n *node, out reflect.Value) (good bool) { var tag string var resolved interface{} if n.tag == "" && !n.implicit { resolved = n.value } else { tag, resolved = resolve(n.tag, n.value) if set := d.setter(tag, &out, &good); set != nil { defer set() } } switch out.Kind() { case reflect.String: if resolved != nil { out.SetString(n.value) good = true } case reflect.Interface: if resolved == nil { out.Set(reflect.Zero(out.Type())) } else { out.Set(reflect.ValueOf(resolved)) } good = true case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: switch resolved := resolved.(type) { case int: if !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) good = true } case int64: if !out.OverflowInt(resolved) { out.SetInt(resolved) good = true } case float64: if resolved < 1<<63-1 && !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) good = true } } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: switch resolved := resolved.(type) { case int: if resolved >= 0 { out.SetUint(uint64(resolved)) good = true } case int64: if resolved >= 0 { out.SetUint(uint64(resolved)) good = true } case float64: if resolved < 1<<64-1 && !out.OverflowUint(uint64(resolved)) { out.SetUint(uint64(resolved)) good = true } } case reflect.Bool: switch resolved := resolved.(type) { case bool: out.SetBool(resolved) good = true } case reflect.Float32, reflect.Float64: switch resolved := resolved.(type) { case int: out.SetFloat(float64(resolved)) good = true case int64: out.SetFloat(float64(resolved)) good = true case float64: out.SetFloat(resolved) good = true } case reflect.Ptr: switch resolved.(type) { case nil: out.Set(reflect.Zero(out.Type())) good = true default: if out.Type().Elem() == reflect.TypeOf(resolved) { elem := reflect.New(out.Type().Elem()) elem.Elem().Set(reflect.ValueOf(resolved)) out.Set(elem) good = true } } } return good } func settableValueOf(i interface{}) reflect.Value { v := reflect.ValueOf(i) sv := reflect.New(v.Type()).Elem() sv.Set(v) return sv } func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { if set := d.setter("!!seq", &out, &good); set != nil { defer set() } var iface reflect.Value if out.Kind() == reflect.Interface { // No type hints. Will have to use a generic sequence. iface = out out = settableValueOf(make([]interface{}, 0)) } if out.Kind() != reflect.Slice { return false } et := out.Type().Elem() l := len(n.children) for i := 0; i < l; i++ { e := reflect.New(et).Elem() if ok := d.unmarshal(n.children[i], e); ok { out.Set(reflect.Append(out, e)) } } if iface.IsValid() { iface.Set(out) } return true } func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { if set := d.setter("!!map", &out, &good); set != nil { defer set() } if out.Kind() == reflect.Struct { return d.mappingStruct(n, out) } if out.Kind() == reflect.Interface { // No type hints. Will have to use a generic map. iface := out out = settableValueOf(make(map[interface{}]interface{})) iface.Set(out) } if out.Kind() != reflect.Map { return false } outt := out.Type() kt := outt.Key() et := outt.Elem() if out.IsNil() { out.Set(reflect.MakeMap(outt)) } l := len(n.children) for i := 0; i < l; i += 2 { k := reflect.New(kt).Elem() if d.unmarshal(n.children[i], k) { e := reflect.New(et).Elem() if d.unmarshal(n.children[i+1], e) { out.SetMapIndex(k, e) } } } return true } func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { sinfo, err := getStructInfo(out.Type()) if err != nil { panic(err) } name := settableValueOf("") l := len(n.children) for i := 0; i < l; i += 2 { if !d.unmarshal(n.children[i], name) { continue } if info, ok := sinfo.FieldsMap[name.String()]; ok { var field reflect.Value if info.Inline == nil { field = out.Field(info.Num) } else { field = out.FieldByIndex(info.Inline) } d.unmarshal(n.children[i+1], field) } } return true } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/decode_test.go��������������������������������������������0000644�0000153�0000161�00000025654�12321735675�023105� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml_test import ( . "launchpad.net/gocheck" "launchpad.net/goyaml" "math" "reflect" ) var unmarshalIntTest = 123 var unmarshalTests = []struct { data string value interface{} }{ { "", &struct{}{}, }, { "{}", &struct{}{}, }, { "v: hi", map[string]string{"v": "hi"}, }, { "v: hi", map[string]interface{}{"v": "hi"}, }, { "v: true", map[string]string{"v": "true"}, }, { "v: true", map[string]interface{}{"v": true}, }, { "v: 10", map[string]interface{}{"v": 10}, }, { "v: 0b10", map[string]interface{}{"v": 2}, }, { "v: 0xA", map[string]interface{}{"v": 10}, }, { "v: 4294967296", map[string]int64{"v": 4294967296}, }, { "v: 0.1", map[string]interface{}{"v": 0.1}, }, { "v: .1", map[string]interface{}{"v": 0.1}, }, { "v: .Inf", map[string]interface{}{"v": math.Inf(+1)}, }, { "v: -.Inf", map[string]interface{}{"v": math.Inf(-1)}, }, { "v: -10", map[string]interface{}{"v": -10}, }, { "v: -.1", map[string]interface{}{"v": -0.1}, }, // Simple values. { "123", &unmarshalIntTest, }, // Floats from spec { "canonical: 6.8523e+5", map[string]interface{}{"canonical": 6.8523e+5}, }, { "expo: 685.230_15e+03", map[string]interface{}{"expo": 685.23015e+03}, }, { "fixed: 685_230.15", map[string]interface{}{"fixed": 685230.15}, }, { "neginf: -.inf", map[string]interface{}{"neginf": math.Inf(-1)}, }, { "fixed: 685_230.15", map[string]float64{"fixed": 685230.15}, }, //{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported //{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails. // Bools from spec { "canonical: y", map[string]interface{}{"canonical": true}, }, { "answer: NO", map[string]interface{}{"answer": false}, }, { "logical: True", map[string]interface{}{"logical": true}, }, { "option: on", map[string]interface{}{"option": true}, }, { "option: on", map[string]bool{"option": true}, }, // Ints from spec { "canonical: 685230", map[string]interface{}{"canonical": 685230}, }, { "decimal: +685_230", map[string]interface{}{"decimal": 685230}, }, { "octal: 02472256", map[string]interface{}{"octal": 685230}, }, { "hexa: 0x_0A_74_AE", map[string]interface{}{"hexa": 685230}, }, { "bin: 0b1010_0111_0100_1010_1110", map[string]interface{}{"bin": 685230}, }, { "bin: -0b101010", map[string]interface{}{"bin": -42}, }, { "decimal: +685_230", map[string]int{"decimal": 685230}, }, //{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported // Nulls from spec { "empty:", map[string]interface{}{"empty": nil}, }, { "canonical: ~", map[string]interface{}{"canonical": nil}, }, { "english: null", map[string]interface{}{"english": nil}, }, { "~: null key", map[interface{}]string{nil: "null key"}, }, { "empty:", map[string]*bool{"empty": nil}, }, // Flow sequence { "seq: [A,B]", map[string]interface{}{"seq": []interface{}{"A", "B"}}, }, { "seq: [A,B,C,]", map[string][]string{"seq": []string{"A", "B", "C"}}, }, { "seq: [A,1,C]", map[string][]string{"seq": []string{"A", "1", "C"}}, }, { "seq: [A,1,C]", map[string][]int{"seq": []int{1}}, }, { "seq: [A,1,C]", map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, }, // Block sequence { "seq:\n - A\n - B", map[string]interface{}{"seq": []interface{}{"A", "B"}}, }, { "seq:\n - A\n - B\n - C", map[string][]string{"seq": []string{"A", "B", "C"}}, }, { "seq:\n - A\n - 1\n - C", map[string][]string{"seq": []string{"A", "1", "C"}}, }, { "seq:\n - A\n - 1\n - C", map[string][]int{"seq": []int{1}}, }, { "seq:\n - A\n - 1\n - C", map[string]interface{}{"seq": []interface{}{"A", 1, "C"}}, }, // Literal block scalar { "scalar: | # Comment\n\n literal\n\n \ttext\n\n", map[string]string{"scalar": "\nliteral\n\n\ttext\n"}, }, // Folded block scalar { "scalar: > # Comment\n\n folded\n line\n \n next\n line\n * one\n * two\n\n last\n line\n\n", map[string]string{"scalar": "\nfolded line\nnext line\n * one\n * two\n\nlast line\n"}, }, // Map inside interface with no type hints. { "a: {b: c}", map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, }, // Structs and type conversions. { "hello: world", &struct{ Hello string }{"world"}, }, { "a: {b: c}", &struct{ A struct{ B string } }{struct{ B string }{"c"}}, }, { "a: {b: c}", &struct{ A *struct{ B string } }{&struct{ B string }{"c"}}, }, { "a: {b: c}", &struct{ A map[string]string }{map[string]string{"b": "c"}}, }, { "a: {b: c}", &struct{ A *map[string]string }{&map[string]string{"b": "c"}}, }, { "a:", &struct{ A map[string]string }{}, }, { "a: 1", &struct{ A int }{1}, }, { "a: 1", &struct{ A float64 }{1}, }, { "a: 1.0", &struct{ A int }{1}, }, { "a: 1.0", &struct{ A uint }{1}, }, { "a: [1, 2]", &struct{ A []int }{[]int{1, 2}}, }, { "a: 1", &struct{ B int }{0}, }, { "a: 1", &struct { B int "a" }{1}, }, { "a: y", &struct{ A bool }{true}, }, // Some cross type conversions { "v: 42", map[string]uint{"v": 42}, }, { "v: -42", map[string]uint{}, }, { "v: 4294967296", map[string]uint64{"v": 4294967296}, }, { "v: -4294967296", map[string]uint64{}, }, // Overflow cases. { "v: 4294967297", map[string]int32{}, }, { "v: 128", map[string]int8{}, }, // Quoted values. { "'1': '\"2\"'", map[interface{}]interface{}{"1": "\"2\""}, }, { "v:\n- A\n- 'B\n\n C'\n", map[string][]string{"v": []string{"A", "B\nC"}}, }, // Explicit tags. { "v: !!float '1.1'", map[string]interface{}{"v": 1.1}, }, { "v: !!null ''", map[string]interface{}{"v": nil}, }, { "%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'", map[string]interface{}{"v": 1}, }, // Anchors and aliases. { "a: &x 1\nb: &y 2\nc: *x\nd: *y\n", &struct{ A, B, C, D int }{1, 2, 1, 2}, }, { "a: &a {c: 1}\nb: *a", &struct { A, B struct { C int } }{struct{ C int }{1}, struct{ C int }{1}}, }, { "a: &a [1, 2]\nb: *a", &struct{ B []int }{[]int{1, 2}}, }, // Bug #1133337 { "foo: ''", map[string]*string{"foo": new(string)}, }, { "foo: null", map[string]string{}, }, // Ignored field { "a: 1\nb: 2\n", &struct { A int B int "-" }{1, 0}, }, // Bug #1191981 { "" + "%YAML 1.1\n" + "--- !!str\n" + `"Generic line break (no glyph)\n\` + "\n" + ` Generic line break (glyphed)\n\` + "\n" + ` Line separator\u2028\` + "\n" + ` Paragraph separator\u2029"` + "\n", "" + "Generic line break (no glyph)\n" + "Generic line break (glyphed)\n" + "Line separator\u2028Paragraph separator\u2029", }, // Struct inlining { "a: 1\nb: 2\nc: 3\n", &struct { A int C inlineB `yaml:",inline"` }{1, inlineB{2, inlineC{3}}}, }, } type inlineB struct { B int inlineC `yaml:",inline"` } type inlineC struct { C int } func (s *S) TestUnmarshal(c *C) { for i, item := range unmarshalTests { t := reflect.ValueOf(item.value).Type() var value interface{} switch t.Kind() { case reflect.Map: value = reflect.MakeMap(t).Interface() case reflect.String: t := reflect.ValueOf(item.value).Type() v := reflect.New(t) value = v.Interface() default: pt := reflect.ValueOf(item.value).Type() pv := reflect.New(pt.Elem()) value = pv.Interface() } err := goyaml.Unmarshal([]byte(item.data), value) c.Assert(err, IsNil, Commentf("Item #%d", i)) if t.Kind() == reflect.String { c.Assert(*value.(*string), Equals, item.value, Commentf("Item #%d", i)) } else { c.Assert(value, DeepEquals, item.value, Commentf("Item #%d", i)) } } } func (s *S) TestUnmarshalNaN(c *C) { value := map[string]interface{}{} err := goyaml.Unmarshal([]byte("notanum: .NaN"), &value) c.Assert(err, IsNil) c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true) } var unmarshalErrorTests = []struct { data, error string }{ {"v: !!float 'error'", "YAML error: Can't decode !!str 'error' as a !!float"}, {"v: [A,", "YAML error: line 1: did not find expected node content"}, {"v:\n- [A,", "YAML error: line 2: did not find expected node content"}, {"a: *b\n", "YAML error: Unknown anchor 'b' referenced"}, {"a: &a\n b: *a\n", "YAML error: Anchor 'a' value contains itself"}, {"value: -", "YAML error: block sequence entries are not allowed in this context"}, } func (s *S) TestUnmarshalErrors(c *C) { for _, item := range unmarshalErrorTests { var value interface{} err := goyaml.Unmarshal([]byte(item.data), &value) c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value)) } } var setterTests = []struct { data, tag string value interface{} }{ {"_: {hi: there}", "!!map", map[interface{}]interface{}{"hi": "there"}}, {"_: [1,A]", "!!seq", []interface{}{1, "A"}}, {"_: 10", "!!int", 10}, {"_: null", "!!null", nil}, {"_: !!foo 'BAR!'", "!!foo", "BAR!"}, } var setterResult = map[int]bool{} type typeWithSetter struct { tag string value interface{} } func (o *typeWithSetter) SetYAML(tag string, value interface{}) (ok bool) { o.tag = tag o.value = value if i, ok := value.(int); ok { if result, ok := setterResult[i]; ok { return result } } return true } type typeWithSetterField struct { Field *typeWithSetter "_" } func (s *S) TestUnmarshalWithSetter(c *C) { for _, item := range setterTests { obj := &typeWithSetterField{} err := goyaml.Unmarshal([]byte(item.data), obj) c.Assert(err, IsNil) c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value)) c.Assert(obj.Field.tag, Equals, item.tag) c.Assert(obj.Field.value, DeepEquals, item.value) } } func (s *S) TestUnmarshalWholeDocumentWithSetter(c *C) { obj := &typeWithSetter{} err := goyaml.Unmarshal([]byte(setterTests[0].data), obj) c.Assert(err, IsNil) c.Assert(obj.tag, Equals, setterTests[0].tag) value, ok := obj.value.(map[interface{}]interface{}) c.Assert(ok, Equals, true) c.Assert(value["_"], DeepEquals, setterTests[0].value) } func (s *S) TestUnmarshalWithFalseSetterIgnoresValue(c *C) { setterResult[2] = false setterResult[4] = false defer func() { delete(setterResult, 2) delete(setterResult, 4) }() m := map[string]*typeWithSetter{} data := "{abc: 1, def: 2, ghi: 3, jkl: 4}" err := goyaml.Unmarshal([]byte(data), m) c.Assert(err, IsNil) c.Assert(m["abc"], NotNil) c.Assert(m["def"], IsNil) c.Assert(m["ghi"], NotNil) c.Assert(m["jkl"], IsNil) c.Assert(m["abc"].value, Equals, 1) c.Assert(m["ghi"].value, Equals, 3) } //var data []byte //func init() { // var err error // data, err = ioutil.ReadFile("/tmp/file.yaml") // if err != nil { // panic(err) // } //} // //func (s *S) BenchmarkUnmarshal(c *C) { // var err error // for i := 0; i < c.N; i++ { // var v map[string]interface{} // err = goyaml.Unmarshal(data, &v) // } // if err != nil { // panic(err) // } //} // //func (s *S) BenchmarkMarshal(c *C) { // var v map[string]interface{} // goyaml.Unmarshal(data, &v) // c.ResetTimer() // for i := 0; i < c.N; i++ { // goyaml.Marshal(&v) // } //} ������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/yamlh.go��������������������������������������������������0000644�0000153�0000161�00000061312�12321735675�021724� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml import ( "io" ) // The version directive data. type yaml_version_directive_t struct { major int8 // The major version number. minor int8 // The minor version number. } // The tag directive data. type yaml_tag_directive_t struct { handle []byte // The tag handle. prefix []byte // The tag prefix. } type yaml_encoding_t int // The stream encoding. const ( // Let the parser choose the encoding. yaml_ANY_ENCODING yaml_encoding_t = iota yaml_UTF8_ENCODING // The default UTF-8 encoding. yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. ) type yaml_break_t int // Line break types. const ( // Let the parser choose the break type. yaml_ANY_BREAK yaml_break_t = iota yaml_CR_BREAK // Use CR for line breaks (Mac style). yaml_LN_BREAK // Use LN for line breaks (Unix style). yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). ) type yaml_error_type_t int // Many bad things could happen with the parser and emitter. const ( // No error is produced. yaml_NO_ERROR yaml_error_type_t = iota yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. yaml_READER_ERROR // Cannot read or decode the input stream. yaml_SCANNER_ERROR // Cannot scan the input stream. yaml_PARSER_ERROR // Cannot parse the input stream. yaml_COMPOSER_ERROR // Cannot compose a YAML document. yaml_WRITER_ERROR // Cannot write to the output stream. yaml_EMITTER_ERROR // Cannot emit a YAML stream. ) // The pointer position. type yaml_mark_t struct { index int // The position index. line int // The position line. column int // The position column. } // Node Styles type yaml_style_t int8 type yaml_scalar_style_t yaml_style_t // Scalar styles. const ( // Let the emitter choose the style. yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota yaml_PLAIN_SCALAR_STYLE // The plain scalar style. yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. yaml_LITERAL_SCALAR_STYLE // The literal scalar style. yaml_FOLDED_SCALAR_STYLE // The folded scalar style. ) type yaml_sequence_style_t yaml_style_t // Sequence styles. const ( // Let the emitter choose the style. yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. ) type yaml_mapping_style_t yaml_style_t // Mapping styles. const ( // Let the emitter choose the style. yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota yaml_BLOCK_MAPPING_STYLE // The block mapping style. yaml_FLOW_MAPPING_STYLE // The flow mapping style. ) // Tokens type yaml_token_type_t int // Token types. const ( // An empty token. yaml_NO_TOKEN yaml_token_type_t = iota yaml_STREAM_START_TOKEN // A STREAM-START token. yaml_STREAM_END_TOKEN // A STREAM-END token. yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. yaml_BLOCK_END_TOKEN // A BLOCK-END token. yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. yaml_KEY_TOKEN // A KEY token. yaml_VALUE_TOKEN // A VALUE token. yaml_ALIAS_TOKEN // An ALIAS token. yaml_ANCHOR_TOKEN // An ANCHOR token. yaml_TAG_TOKEN // A TAG token. yaml_SCALAR_TOKEN // A SCALAR token. ) func (tt yaml_token_type_t) String() string { switch tt { case yaml_NO_TOKEN: return "yaml_NO_TOKEN" case yaml_STREAM_START_TOKEN: return "yaml_STREAM_START_TOKEN" case yaml_STREAM_END_TOKEN: return "yaml_STREAM_END_TOKEN" case yaml_VERSION_DIRECTIVE_TOKEN: return "yaml_VERSION_DIRECTIVE_TOKEN" case yaml_TAG_DIRECTIVE_TOKEN: return "yaml_TAG_DIRECTIVE_TOKEN" case yaml_DOCUMENT_START_TOKEN: return "yaml_DOCUMENT_START_TOKEN" case yaml_DOCUMENT_END_TOKEN: return "yaml_DOCUMENT_END_TOKEN" case yaml_BLOCK_SEQUENCE_START_TOKEN: return "yaml_BLOCK_SEQUENCE_START_TOKEN" case yaml_BLOCK_MAPPING_START_TOKEN: return "yaml_BLOCK_MAPPING_START_TOKEN" case yaml_BLOCK_END_TOKEN: return "yaml_BLOCK_END_TOKEN" case yaml_FLOW_SEQUENCE_START_TOKEN: return "yaml_FLOW_SEQUENCE_START_TOKEN" case yaml_FLOW_SEQUENCE_END_TOKEN: return "yaml_FLOW_SEQUENCE_END_TOKEN" case yaml_FLOW_MAPPING_START_TOKEN: return "yaml_FLOW_MAPPING_START_TOKEN" case yaml_FLOW_MAPPING_END_TOKEN: return "yaml_FLOW_MAPPING_END_TOKEN" case yaml_BLOCK_ENTRY_TOKEN: return "yaml_BLOCK_ENTRY_TOKEN" case yaml_FLOW_ENTRY_TOKEN: return "yaml_FLOW_ENTRY_TOKEN" case yaml_KEY_TOKEN: return "yaml_KEY_TOKEN" case yaml_VALUE_TOKEN: return "yaml_VALUE_TOKEN" case yaml_ALIAS_TOKEN: return "yaml_ALIAS_TOKEN" case yaml_ANCHOR_TOKEN: return "yaml_ANCHOR_TOKEN" case yaml_TAG_TOKEN: return "yaml_TAG_TOKEN" case yaml_SCALAR_TOKEN: return "yaml_SCALAR_TOKEN" } return "<unknown token>" } // The token structure. type yaml_token_t struct { // The token type. typ yaml_token_type_t // The start/end of the token. start_mark, end_mark yaml_mark_t // The stream encoding (for yaml_STREAM_START_TOKEN). encoding yaml_encoding_t // The alias/anchor/scalar value or tag/tag directive handle // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). value []byte // The tag suffix (for yaml_TAG_TOKEN). suffix []byte // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). prefix []byte // The scalar style (for yaml_SCALAR_TOKEN). style yaml_scalar_style_t // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). major, minor int8 } // Events type yaml_event_type_t int8 // Event types. const ( // An empty event. yaml_NO_EVENT yaml_event_type_t = iota yaml_STREAM_START_EVENT // A STREAM-START event. yaml_STREAM_END_EVENT // A STREAM-END event. yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. yaml_ALIAS_EVENT // An ALIAS event. yaml_SCALAR_EVENT // A SCALAR event. yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. yaml_MAPPING_START_EVENT // A MAPPING-START event. yaml_MAPPING_END_EVENT // A MAPPING-END event. ) // The event structure. type yaml_event_t struct { // The event type. typ yaml_event_type_t // The start and end of the event. start_mark, end_mark yaml_mark_t // The document encoding (for yaml_STREAM_START_EVENT). encoding yaml_encoding_t // The version directive (for yaml_DOCUMENT_START_EVENT). version_directive *yaml_version_directive_t // The list of tag directives (for yaml_DOCUMENT_START_EVENT). tag_directives []yaml_tag_directive_t // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). anchor []byte // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). tag []byte // The scalar value (for yaml_SCALAR_EVENT). value []byte // Is the document start/end indicator implicit, or the tag optional? // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). implicit bool // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). quoted_implicit bool // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). style yaml_style_t } func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } // Nodes const ( yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. ) type yaml_node_type_t int // Node types. const ( // An empty node. yaml_NO_NODE yaml_node_type_t = iota yaml_SCALAR_NODE // A scalar node. yaml_SEQUENCE_NODE // A sequence node. yaml_MAPPING_NODE // A mapping node. ) // An element of a sequence node. type yaml_node_item_t int // An element of a mapping node. type yaml_node_pair_t struct { key int // The key of the element. value int // The value of the element. } // The node structure. type yaml_node_t struct { typ yaml_node_type_t // The node type. tag []byte // The node tag. // The node data. // The scalar parameters (for yaml_SCALAR_NODE). scalar struct { value []byte // The scalar value. length int // The length of the scalar value. style yaml_scalar_style_t // The scalar style. } // The sequence parameters (for YAML_SEQUENCE_NODE). sequence struct { items_data []yaml_node_item_t // The stack of sequence items. style yaml_sequence_style_t // The sequence style. } // The mapping parameters (for yaml_MAPPING_NODE). mapping struct { pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). pairs_start *yaml_node_pair_t // The beginning of the stack. pairs_end *yaml_node_pair_t // The end of the stack. pairs_top *yaml_node_pair_t // The top of the stack. style yaml_mapping_style_t // The mapping style. } start_mark yaml_mark_t // The beginning of the node. end_mark yaml_mark_t // The end of the node. } // The document structure. type yaml_document_t struct { // The document nodes. nodes []yaml_node_t // The version directive. version_directive *yaml_version_directive_t // The list of tag directives. tag_directives_data []yaml_tag_directive_t tag_directives_start int // The beginning of the tag directives list. tag_directives_end int // The end of the tag directives list. start_implicit int // Is the document start indicator implicit? end_implicit int // Is the document end indicator implicit? // The start/end of the document. start_mark, end_mark yaml_mark_t } // The prototype of a read handler. // // The read handler is called when the parser needs to read more bytes from the // source. The handler should write not more than size bytes to the buffer. // The number of written bytes should be set to the size_read variable. // // [in,out] data A pointer to an application data specified by // yaml_parser_set_input(). // [out] buffer The buffer to write the data from the source. // [in] size The size of the buffer. // [out] size_read The actual number of bytes read from the source. // // On success, the handler should return 1. If the handler failed, // the returned value should be 0. On EOF, the handler should set the // size_read to 0 and return 1. type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) // This structure holds information about a potential simple key. type yaml_simple_key_t struct { possible bool // Is a simple key possible? required bool // Is a simple key required? token_number int // The number of the token. mark yaml_mark_t // The position mark. } // The states of the parser. type yaml_parser_state_t int const ( yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. yaml_PARSE_END_STATE // Expect nothing. ) func (ps yaml_parser_state_t) String() string { switch ps { case yaml_PARSE_STREAM_START_STATE: return "yaml_PARSE_STREAM_START_STATE" case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" case yaml_PARSE_DOCUMENT_START_STATE: return "yaml_PARSE_DOCUMENT_START_STATE" case yaml_PARSE_DOCUMENT_CONTENT_STATE: return "yaml_PARSE_DOCUMENT_CONTENT_STATE" case yaml_PARSE_DOCUMENT_END_STATE: return "yaml_PARSE_DOCUMENT_END_STATE" case yaml_PARSE_BLOCK_NODE_STATE: return "yaml_PARSE_BLOCK_NODE_STATE" case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" case yaml_PARSE_FLOW_NODE_STATE: return "yaml_PARSE_FLOW_NODE_STATE" case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" case yaml_PARSE_FLOW_MAPPING_KEY_STATE: return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" case yaml_PARSE_END_STATE: return "yaml_PARSE_END_STATE" } return "<unknown parser state>" } // This structure holds aliases data. type yaml_alias_data_t struct { anchor []byte // The anchor. index int // The node id. mark yaml_mark_t // The anchor mark. } // The parser structure. // // All members are internal. Manage the structure using the // yaml_parser_ family of functions. type yaml_parser_t struct { // Error handling error yaml_error_type_t // Error type. problem string // Error description. // The byte about which the problem occured. problem_offset int problem_value int problem_mark yaml_mark_t // The error context. context string context_mark yaml_mark_t // Reader stuff read_handler yaml_read_handler_t // Read handler. input_file io.Reader // File input data. input []byte // String input data. input_pos int eof bool // EOF flag buffer []byte // The working buffer. buffer_pos int // The current position of the buffer. unread int // The number of unread characters in the buffer. raw_buffer []byte // The raw buffer. raw_buffer_pos int // The current position of the buffer. encoding yaml_encoding_t // The input encoding. offset int // The offset of the current position (in bytes). mark yaml_mark_t // The mark of the current position. // Scanner stuff stream_start_produced bool // Have we started to scan the input stream? stream_end_produced bool // Have we reached the end of the input stream? flow_level int // The number of unclosed '[' and '{' indicators. tokens []yaml_token_t // The tokens queue. tokens_head int // The head of the tokens queue. tokens_parsed int // The number of tokens fetched from the queue. token_available bool // Does the tokens queue contain a token ready for dequeueing. indent int // The current indentation level. indents []int // The indentation levels stack. simple_key_allowed bool // May a simple key occur at the current position? simple_keys []yaml_simple_key_t // The stack of simple keys. // Parser stuff state yaml_parser_state_t // The current parser state. states []yaml_parser_state_t // The parser states stack. marks []yaml_mark_t // The stack of marks. tag_directives []yaml_tag_directive_t // The list of TAG directives. // Dumper stuff aliases []yaml_alias_data_t // The alias data. document *yaml_document_t // The currently parsed document. } // Emitter Definitions // The prototype of a write handler. // // The write handler is called when the emitter needs to flush the accumulated // characters to the output. The handler should write @a size bytes of the // @a buffer to the output. // // @param[in,out] data A pointer to an application data specified by // yaml_emitter_set_output(). // @param[in] buffer The buffer with bytes to be written. // @param[in] size The size of the buffer. // // @returns On success, the handler should return @c 1. If the handler failed, // the returned value should be @c 0. // type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error type yaml_emitter_state_t int // The emitter states. const ( // Expect STREAM-START. yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. yaml_EMIT_END_STATE // Expect nothing. ) // The emitter structure. // // All members are internal. Manage the structure using the @c yaml_emitter_ // family of functions. type yaml_emitter_t struct { // Error handling error yaml_error_type_t // Error type. problem string // Error description. // Writer stuff write_handler yaml_write_handler_t // Write handler. output_buffer *[]byte // String output data. output_file io.Writer // File output data. buffer []byte // The working buffer. buffer_pos int // The current position of the buffer. raw_buffer []byte // The raw buffer. raw_buffer_pos int // The current position of the buffer. encoding yaml_encoding_t // The stream encoding. // Emitter stuff canonical bool // If the output is in the canonical style? best_indent int // The number of indentation spaces. best_width int // The preferred width of the output lines. unicode bool // Allow unescaped non-ASCII characters? line_break yaml_break_t // The preferred line break. state yaml_emitter_state_t // The current emitter state. states []yaml_emitter_state_t // The stack of states. events []yaml_event_t // The event queue. events_head int // The head of the event queue. indents []int // The stack of indentation levels. tag_directives []yaml_tag_directive_t // The list of tag directives. indent int // The current indentation level. flow_level int // The current flow level. root_context bool // Is it the document root context? sequence_context bool // Is it a sequence context? mapping_context bool // Is it a mapping context? simple_key_context bool // Is it a simple mapping key context? line int // The current line. column int // The current column. whitespace bool // If the last character was a whitespace? indention bool // If the last character was an indentation character (' ', '-', '?', ':')? open_ended bool // If an explicit document end is required? // Anchor analysis. anchor_data struct { anchor []byte // The anchor value. alias bool // Is it an alias? } // Tag analysis. tag_data struct { handle []byte // The tag handle. suffix []byte // The tag suffix. } // Scalar analysis. scalar_data struct { value []byte // The scalar value. multiline bool // Does the scalar contain line breaks? flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? block_plain_allowed bool // Can the scalar be expressed in the block plain style? single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? block_allowed bool // Can the scalar be expressed in the literal or folded styles? style yaml_scalar_style_t // The output style. } // Dumper stuff opened bool // If the stream was already opened? closed bool // If the stream was already closed? // The information associated with the document nodes. anchors *struct { references int // The number of references. anchor int // The anchor id. serialized bool // If the node has been emitted? } last_anchor_id int // The last assigned anchor id. document *yaml_document_t // The currently emitted document. } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/apic.go���������������������������������������������������0000644�0000153�0000161�00000051400�12321735675�021523� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml import ( "io" "os" ) func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) // Check if we can move the queue at the beginning of the buffer. if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { if parser.tokens_head != len(parser.tokens) { copy(parser.tokens, parser.tokens[parser.tokens_head:]) } parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] parser.tokens_head = 0 } parser.tokens = append(parser.tokens, *token) if pos < 0 { return } copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) parser.tokens[parser.tokens_head+pos] = *token } // Create a new parser object. func yaml_parser_initialize(parser *yaml_parser_t) bool { *parser = yaml_parser_t{ raw_buffer: make([]byte, 0, input_raw_buffer_size), buffer: make([]byte, 0, input_buffer_size), } return true } // Destroy a parser object. func yaml_parser_delete(parser *yaml_parser_t) { *parser = yaml_parser_t{} } // String read handler. func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { if parser.input_pos == len(parser.input) { return 0, io.EOF } n = copy(buffer, parser.input[parser.input_pos:]) parser.input_pos += n return n, nil } // File read handler. func yaml_file_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { return parser.input_file.Read(buffer) } // Set a string input. func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { if parser.read_handler != nil { panic("must set the input source only once") } parser.read_handler = yaml_string_read_handler parser.input = input parser.input_pos = 0 } // Set a file input. func yaml_parser_set_input_file(parser *yaml_parser_t, file *os.File) { if parser.read_handler != nil { panic("must set the input source only once") } parser.read_handler = yaml_file_read_handler parser.input_file = file } // Set the source encoding. func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { if parser.encoding != yaml_ANY_ENCODING { panic("must set the encoding only once") } parser.encoding = encoding } // Create a new emitter object. func yaml_emitter_initialize(emitter *yaml_emitter_t) bool { *emitter = yaml_emitter_t{ buffer: make([]byte, output_buffer_size), raw_buffer: make([]byte, 0, output_raw_buffer_size), states: make([]yaml_emitter_state_t, 0, initial_stack_size), events: make([]yaml_event_t, 0, initial_queue_size), } return true } // Destroy an emitter object. func yaml_emitter_delete(emitter *yaml_emitter_t) { *emitter = yaml_emitter_t{} } // String write handler. func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { *emitter.output_buffer = append(*emitter.output_buffer, buffer...) return nil } // File write handler. func yaml_file_write_handler(emitter *yaml_emitter_t, buffer []byte) error { _, err := emitter.output_file.Write(buffer) return err } // Set a string output. func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { if emitter.write_handler != nil { panic("must set the output target only once") } emitter.write_handler = yaml_string_write_handler emitter.output_buffer = output_buffer } // Set a file output. func yaml_emitter_set_output_file(emitter *yaml_emitter_t, file io.Writer) { if emitter.write_handler != nil { panic("must set the output target only once") } emitter.write_handler = yaml_file_write_handler emitter.output_file = file } // Set the output encoding. func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { if emitter.encoding != yaml_ANY_ENCODING { panic("must set the output encoding only once") } emitter.encoding = encoding } // Set the canonical output style. func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { emitter.canonical = canonical } //// Set the indentation increment. func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { if indent < 2 || indent > 9 { indent = 2 } emitter.best_indent = indent } // Set the preferred line width. func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { if width < 0 { width = -1 } emitter.best_width = width } // Set if unescaped non-ASCII characters are allowed. func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { emitter.unicode = unicode } // Set the preferred line break character. func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { emitter.line_break = line_break } ///* // * Destroy a token object. // */ // //YAML_DECLARE(void) //yaml_token_delete(yaml_token_t *token) //{ // assert(token); // Non-NULL token object expected. // // switch (token.type) // { // case YAML_TAG_DIRECTIVE_TOKEN: // yaml_free(token.data.tag_directive.handle); // yaml_free(token.data.tag_directive.prefix); // break; // // case YAML_ALIAS_TOKEN: // yaml_free(token.data.alias.value); // break; // // case YAML_ANCHOR_TOKEN: // yaml_free(token.data.anchor.value); // break; // // case YAML_TAG_TOKEN: // yaml_free(token.data.tag.handle); // yaml_free(token.data.tag.suffix); // break; // // case YAML_SCALAR_TOKEN: // yaml_free(token.data.scalar.value); // break; // // default: // break; // } // // memset(token, 0, sizeof(yaml_token_t)); //} // ///* // * Check if a string is a valid UTF-8 sequence. // * // * Check 'reader.c' for more details on UTF-8 encoding. // */ // //static int //yaml_check_utf8(yaml_char_t *start, size_t length) //{ // yaml_char_t *end = start+length; // yaml_char_t *pointer = start; // // while (pointer < end) { // unsigned char octet; // unsigned int width; // unsigned int value; // size_t k; // // octet = pointer[0]; // width = (octet & 0x80) == 0x00 ? 1 : // (octet & 0xE0) == 0xC0 ? 2 : // (octet & 0xF0) == 0xE0 ? 3 : // (octet & 0xF8) == 0xF0 ? 4 : 0; // value = (octet & 0x80) == 0x00 ? octet & 0x7F : // (octet & 0xE0) == 0xC0 ? octet & 0x1F : // (octet & 0xF0) == 0xE0 ? octet & 0x0F : // (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; // if (!width) return 0; // if (pointer+width > end) return 0; // for (k = 1; k < width; k ++) { // octet = pointer[k]; // if ((octet & 0xC0) != 0x80) return 0; // value = (value << 6) + (octet & 0x3F); // } // if (!((width == 1) || // (width == 2 && value >= 0x80) || // (width == 3 && value >= 0x800) || // (width == 4 && value >= 0x10000))) return 0; // // pointer += width; // } // // return 1; //} // // Create STREAM-START. func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) bool { *event = yaml_event_t{ typ: yaml_STREAM_START_EVENT, encoding: encoding, } return true } // Create STREAM-END. func yaml_stream_end_event_initialize(event *yaml_event_t) bool { *event = yaml_event_t{ typ: yaml_STREAM_END_EVENT, } return true } // Create DOCUMENT-START. func yaml_document_start_event_initialize(event *yaml_event_t, version_directive *yaml_version_directive_t, tag_directives []yaml_tag_directive_t, implicit bool) bool { *event = yaml_event_t{ typ: yaml_DOCUMENT_START_EVENT, version_directive: version_directive, tag_directives: tag_directives, implicit: implicit, } return true } // Create DOCUMENT-END. func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) bool { *event = yaml_event_t{ typ: yaml_DOCUMENT_END_EVENT, implicit: implicit, } return true } ///* // * Create ALIAS. // */ // //YAML_DECLARE(int) //yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t) //{ // mark yaml_mark_t = { 0, 0, 0 } // anchor_copy *yaml_char_t = NULL // // assert(event) // Non-NULL event object is expected. // assert(anchor) // Non-NULL anchor is expected. // // if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0 // // anchor_copy = yaml_strdup(anchor) // if (!anchor_copy) // return 0 // // ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark) // // return 1 //} // Create SCALAR. func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, anchor: anchor, tag: tag, value: value, implicit: plain_implicit, quoted_implicit: quoted_implicit, style: yaml_style_t(style), } return true } // Create SEQUENCE-START. func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(style), } return true } // Create SEQUENCE-END. func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, } return true } // Create MAPPING-START. func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) bool { *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(style), } return true } // Create MAPPING-END. func yaml_mapping_end_event_initialize(event *yaml_event_t) bool { *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, } return true } // Destroy an event object. func yaml_event_delete(event *yaml_event_t) { *event = yaml_event_t{} } ///* // * Create a document object. // */ // //YAML_DECLARE(int) //yaml_document_initialize(document *yaml_document_t, // version_directive *yaml_version_directive_t, // tag_directives_start *yaml_tag_directive_t, // tag_directives_end *yaml_tag_directive_t, // start_implicit int, end_implicit int) //{ // struct { // error yaml_error_type_t // } context // struct { // start *yaml_node_t // end *yaml_node_t // top *yaml_node_t // } nodes = { NULL, NULL, NULL } // version_directive_copy *yaml_version_directive_t = NULL // struct { // start *yaml_tag_directive_t // end *yaml_tag_directive_t // top *yaml_tag_directive_t // } tag_directives_copy = { NULL, NULL, NULL } // value yaml_tag_directive_t = { NULL, NULL } // mark yaml_mark_t = { 0, 0, 0 } // // assert(document) // Non-NULL document object is expected. // assert((tag_directives_start && tag_directives_end) || // (tag_directives_start == tag_directives_end)) // // Valid tag directives are expected. // // if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error // // if (version_directive) { // version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) // if (!version_directive_copy) goto error // version_directive_copy.major = version_directive.major // version_directive_copy.minor = version_directive.minor // } // // if (tag_directives_start != tag_directives_end) { // tag_directive *yaml_tag_directive_t // if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) // goto error // for (tag_directive = tag_directives_start // tag_directive != tag_directives_end; tag_directive ++) { // assert(tag_directive.handle) // assert(tag_directive.prefix) // if (!yaml_check_utf8(tag_directive.handle, // strlen((char *)tag_directive.handle))) // goto error // if (!yaml_check_utf8(tag_directive.prefix, // strlen((char *)tag_directive.prefix))) // goto error // value.handle = yaml_strdup(tag_directive.handle) // value.prefix = yaml_strdup(tag_directive.prefix) // if (!value.handle || !value.prefix) goto error // if (!PUSH(&context, tag_directives_copy, value)) // goto error // value.handle = NULL // value.prefix = NULL // } // } // // DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, // tag_directives_copy.start, tag_directives_copy.top, // start_implicit, end_implicit, mark, mark) // // return 1 // //error: // STACK_DEL(&context, nodes) // yaml_free(version_directive_copy) // while (!STACK_EMPTY(&context, tag_directives_copy)) { // value yaml_tag_directive_t = POP(&context, tag_directives_copy) // yaml_free(value.handle) // yaml_free(value.prefix) // } // STACK_DEL(&context, tag_directives_copy) // yaml_free(value.handle) // yaml_free(value.prefix) // // return 0 //} // ///* // * Destroy a document object. // */ // //YAML_DECLARE(void) //yaml_document_delete(document *yaml_document_t) //{ // struct { // error yaml_error_type_t // } context // tag_directive *yaml_tag_directive_t // // context.error = YAML_NO_ERROR // Eliminate a compliler warning. // // assert(document) // Non-NULL document object is expected. // // while (!STACK_EMPTY(&context, document.nodes)) { // node yaml_node_t = POP(&context, document.nodes) // yaml_free(node.tag) // switch (node.type) { // case YAML_SCALAR_NODE: // yaml_free(node.data.scalar.value) // break // case YAML_SEQUENCE_NODE: // STACK_DEL(&context, node.data.sequence.items) // break // case YAML_MAPPING_NODE: // STACK_DEL(&context, node.data.mapping.pairs) // break // default: // assert(0) // Should not happen. // } // } // STACK_DEL(&context, document.nodes) // // yaml_free(document.version_directive) // for (tag_directive = document.tag_directives.start // tag_directive != document.tag_directives.end // tag_directive++) { // yaml_free(tag_directive.handle) // yaml_free(tag_directive.prefix) // } // yaml_free(document.tag_directives.start) // // memset(document, 0, sizeof(yaml_document_t)) //} // ///** // * Get a document node. // */ // //YAML_DECLARE(yaml_node_t *) //yaml_document_get_node(document *yaml_document_t, index int) //{ // assert(document) // Non-NULL document object is expected. // // if (index > 0 && document.nodes.start + index <= document.nodes.top) { // return document.nodes.start + index - 1 // } // return NULL //} // ///** // * Get the root object. // */ // //YAML_DECLARE(yaml_node_t *) //yaml_document_get_root_node(document *yaml_document_t) //{ // assert(document) // Non-NULL document object is expected. // // if (document.nodes.top != document.nodes.start) { // return document.nodes.start // } // return NULL //} // ///* // * Add a scalar node to a document. // */ // //YAML_DECLARE(int) //yaml_document_add_scalar(document *yaml_document_t, // tag *yaml_char_t, value *yaml_char_t, length int, // style yaml_scalar_style_t) //{ // struct { // error yaml_error_type_t // } context // mark yaml_mark_t = { 0, 0, 0 } // tag_copy *yaml_char_t = NULL // value_copy *yaml_char_t = NULL // node yaml_node_t // // assert(document) // Non-NULL document object is expected. // assert(value) // Non-NULL value is expected. // // if (!tag) { // tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG // } // // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error // tag_copy = yaml_strdup(tag) // if (!tag_copy) goto error // // if (length < 0) { // length = strlen((char *)value) // } // // if (!yaml_check_utf8(value, length)) goto error // value_copy = yaml_malloc(length+1) // if (!value_copy) goto error // memcpy(value_copy, value, length) // value_copy[length] = '\0' // // SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) // if (!PUSH(&context, document.nodes, node)) goto error // // return document.nodes.top - document.nodes.start // //error: // yaml_free(tag_copy) // yaml_free(value_copy) // // return 0 //} // ///* // * Add a sequence node to a document. // */ // //YAML_DECLARE(int) //yaml_document_add_sequence(document *yaml_document_t, // tag *yaml_char_t, style yaml_sequence_style_t) //{ // struct { // error yaml_error_type_t // } context // mark yaml_mark_t = { 0, 0, 0 } // tag_copy *yaml_char_t = NULL // struct { // start *yaml_node_item_t // end *yaml_node_item_t // top *yaml_node_item_t // } items = { NULL, NULL, NULL } // node yaml_node_t // // assert(document) // Non-NULL document object is expected. // // if (!tag) { // tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG // } // // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error // tag_copy = yaml_strdup(tag) // if (!tag_copy) goto error // // if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error // // SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, // style, mark, mark) // if (!PUSH(&context, document.nodes, node)) goto error // // return document.nodes.top - document.nodes.start // //error: // STACK_DEL(&context, items) // yaml_free(tag_copy) // // return 0 //} // ///* // * Add a mapping node to a document. // */ // //YAML_DECLARE(int) //yaml_document_add_mapping(document *yaml_document_t, // tag *yaml_char_t, style yaml_mapping_style_t) //{ // struct { // error yaml_error_type_t // } context // mark yaml_mark_t = { 0, 0, 0 } // tag_copy *yaml_char_t = NULL // struct { // start *yaml_node_pair_t // end *yaml_node_pair_t // top *yaml_node_pair_t // } pairs = { NULL, NULL, NULL } // node yaml_node_t // // assert(document) // Non-NULL document object is expected. // // if (!tag) { // tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG // } // // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error // tag_copy = yaml_strdup(tag) // if (!tag_copy) goto error // // if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error // // MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, // style, mark, mark) // if (!PUSH(&context, document.nodes, node)) goto error // // return document.nodes.top - document.nodes.start // //error: // STACK_DEL(&context, pairs) // yaml_free(tag_copy) // // return 0 //} // ///* // * Append an item to a sequence node. // */ // //YAML_DECLARE(int) //yaml_document_append_sequence_item(document *yaml_document_t, // sequence int, item int) //{ // struct { // error yaml_error_type_t // } context // // assert(document) // Non-NULL document is required. // assert(sequence > 0 // && document.nodes.start + sequence <= document.nodes.top) // // Valid sequence id is required. // assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) // // A sequence node is required. // assert(item > 0 && document.nodes.start + item <= document.nodes.top) // // Valid item id is required. // // if (!PUSH(&context, // document.nodes.start[sequence-1].data.sequence.items, item)) // return 0 // // return 1 //} // ///* // * Append a pair of a key and a value to a mapping node. // */ // //YAML_DECLARE(int) //yaml_document_append_mapping_pair(document *yaml_document_t, // mapping int, key int, value int) //{ // struct { // error yaml_error_type_t // } context // // pair yaml_node_pair_t // // assert(document) // Non-NULL document is required. // assert(mapping > 0 // && document.nodes.start + mapping <= document.nodes.top) // // Valid mapping id is required. // assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) // // A mapping node is required. // assert(key > 0 && document.nodes.start + key <= document.nodes.top) // // Valid key id is required. // assert(value > 0 && document.nodes.start + value <= document.nodes.top) // // Valid value id is required. // // pair.key = key // pair.value = value // // if (!PUSH(&context, // document.nodes.start[mapping-1].data.mapping.pairs, pair)) // return 0 // // return 1 //} // // ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������juju-core_1.18.1/src/launchpad.net/goyaml/encode_test.go��������������������������������������������0000644�0000153�0000161�00000015256�12321735675�023114� 0����������������������������������������������������������������������������������������������������ustar �jenkins�������������������������jenkins����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package goyaml_test import ( "fmt" . "launchpad.net/gocheck" "launchpad.net/goyaml" "math" "strconv" "strings" ) var marshalIntTest = 123 var marshalTests = []struct { value interface{} data string }{ { &struct{}{}, "{}\n", }, { map[string]string{"v": "hi"}, "v: hi\n", }, { map[string]interface{}{"v": "hi"}, "v: hi\n", }, { map[string]string{"v": "true"}, "v: \"true\"\n", }, { map[string]string{"v": "false"}, "v: \"false\"\n", }, { map[string]interface{}{"v": true}, "v: true\n", }, { map[string]interface{}{"v": false}, "v: false\n", }, { map[string]interface{}{"v": 10}, "v: 10\n", }, { map[string]interface{}{"v": -10}, "v: -10\n", }, { map[string]uint{"v": 42}, "v: 42\n", }, { map[string]interface{}{"v": int64(4294967296)}, "v: 4294967296\n", }, { map[string]int64{"v": int64(4294967296)}, "v: 4294967296\n", }, { map[string]uint64{"v": 4294967296}, "v: 4294967296\n", }, { map[string]interface{}{"v": "10"}, "v: \"10\"\n", }, { map[string]interface{}{"v": 0.1}, "v: 0.1\n", }, { map[string]interface{}{"v": float64(0.1)}, "v: 0.1\n", }, { map[string]interface{}{"v": -0.1}, "v: -0.1\n", }, { map[string]interface{}{"v": math.Inf(+1)}, "v: .inf\n", }, { map[string]interface{}{"v": math.Inf(-1)}, "v: -.inf\n", }, { map[string]interface{}{"v": math.NaN()}, "v: .nan\n", }, { map[string]interface{}{"v": nil}, "v: null\n", }, { map[string]interface{}{"v": ""}, "v: \"\"\n", }, { map[string][]string{"v": []string{"A", "B"}}, "v:\n- A\n- B\n", }, { map[string][]string{"v": []string{"A", "B\nC"}}, "v:\n- A\n- 'B\n\n C'\n", }, { map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}}, "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", }, { map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}}, "a:\n b: c\n", }, { map[string]interface{}{"a": "-"}, "a: '-'\n", }, // Simple values. { &marshalIntTest, "123\n", }, // Structures { &struct{ Hello string }{"world"}, "hello: world\n", }, { &struct { A struct { B string } }{struct{ B string }{"c"}}, "a:\n b: c\n", }, { &struct { A *struct { B string } }{&struct{ B string }{"c"}}, "a:\n b: c\n", }, { &struct { A *struct { B string } }{}, "a: null\n", }, { &struct{ A int }{1}, "a: 1\n", }, { &struct{ A []int }{[]int{1, 2}}, "a:\n- 1\n- 2\n", }, { &struct { B int "a" }{1}, "a: 1\n", }, { &struct{ A bool }{true}, "a: true\n", }, // Conditional flag { &struct { A int "a,omitempty" B int "b,omitempty" }{1, 0}, "a: 1\n", }, { &struct { A int "a,omitempty" B int "b,omitempty" }{0, 0}, "{}\n", }, { &struct { A *struct{ X int } "a,omitempty" B int "b,omitempty" }{nil, 0}, "{}\n", }, // Flow flag { &struct { A []int "a,flow" }{[]int{1, 2}}, "a: [1, 2]\n", }, { &struct { A map[string]string "a,flow" }{map[string]string{"b": "c", "d": "e"}}, "a: {b: c, d: e}\n", }, { &struct { A struct { B, D string } "a,flow" }{struct{ B, D string }{"c", "e"}}, "a: {b: c, d: e}\n", }, // Unexported field { &struct { u int A int }{0, 1}, "a: 1\n", }, // Ignored field { &struct { A int B int "-" }{1, 2}, "a: 1\n", }, // Struct inlining { &struct { A int C inlineB `yaml:",inline"` }{1, inlineB{2, inlineC{3}}}, "a: 1\nb: 2\nc: 3\n", }, } func (s *S) TestMarshal(c *C) { for _, item := range marshalTests { data, err := goyaml.Marshal(item.value) c.Assert(err, IsNil) c.Assert(string(data), Equals, item.data) } } var marshalErrorTests = []struct { value interface{} error string }{ { &struct { B int inlineB ",inline" }{1, inlineB{2, inlineC{3}}}, `Duplicated key 'b' in struct struct \{ B int; .*`, }, } func (s *S) TestMarshalErrors(c *C) { for _, item := range marshalErrorTests { _, err := goyaml.Marshal(item.value) c.Assert(err, ErrorMatches, item.error) } } var marshalTaggedIfaceTest interface{} = &struct{ A string }{"B"} var getterTests = []struct { data, tag string value interface{} }{ {"_:\n hi: there\n", "", map[interface{}]interface{}{"hi": "there"}}, {"_:\n- 1\n- A\n", "", []interface{}{1, "A"}}, {"_: 10\n", "", 10}, {"_: null\n", "", nil}, {"_: !foo BAR!\n", "!foo", "BAR!"}, {"_: !foo 1\n", "!foo", "1"}, {"_: !foo '\"1\"'\n", "!foo", "\"1\""}, {"_: !foo 1.1\n", "!foo", 1.1}, {"_: !foo 1\n", "!foo", 1}, {"_: !foo 1\n", "!foo", uint(1)}, {"_: !foo true\n", "!foo", true}, {"_: !foo\n- A\n- B\n", "!foo", []string{"A", "B"}}, {"_: !foo\n A: B\n", "!foo", map[string]string{"A": "B"}}, {"_: !foo\n a: B\n", "!foo", &marshalTaggedIfaceTest}, } func (s *S) TestMarshalTypeCache(c *C) { var data []byte var err error func() { type T struct{ A int } data, err = goyaml.Marshal(&T{}) c.Assert(err, IsNil) }() func() { type T struct{ B int } data, err = goyaml.Marshal(&T{}) c.Assert(err, IsNil) }() c.Assert(string(data), Equals, "b: 0\n") } type typeWithGetter struct { tag string value interface{} } func (o typeWithGetter) GetYAML() (tag string, value interface{}) { return o.tag, o.value } type typeWithGetterField struct { Field typeWithGetter "_" } func (s *S) TestMashalWithGetter(c *C) { for _, item := range getterTests { obj := &typeWithGetterField{} obj.Field.tag = item.tag obj.Field.value = item.value data, err := goyaml.Marshal(obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, string(item.data)) } } func (s *S) TestUnmarshalWholeDocumentWithGetter(c *C) { obj := &typeWithGetter{} obj.tag = "" obj.value = map[string]string{"hello": "world!"} data, err := goyaml.Marshal(obj) c.Assert(err, IsNil) c.Assert(string(data), Equals, "hello: world!\n") } func (s *S) TestSortedOutput(c *C) { order := []interface{}{ false, true, 1, uint(1), 1.0, 1.1, 1.2, 2, uint(2), 2.0, 2.1, "", ".1", ".2", ".a", "1", "2", "a!10", "a/2", "a/10", "a~10", "ab/1", "b/1", "b/01", "b/2", "b/02", "b/3", "b/03", "b1", "b01", "b3", "c2.10", "c10.2", "d1", "d12", "d12a", } m := make(map[interface{}]int) for _, k := range order { m[k] = 1 } data, err := goyaml.Marshal(m) c.Assert(err, IsNil) out := "\n" + string(data) last := 0 for i, k := range order { repr := fmt.Sprint(k) if s, ok := k.(string); ok { if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil { repr = `"` + repr + `"` } } index := strings.Index(out, "\n"+repr+":") if index == -1 { c.Fatalf("%#v is not in the output: %#v", k, out) } if index < last { c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out) } last = index } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������